Skip to content

Commit

Permalink
Assignment 8: First Attempt
Browse files Browse the repository at this point in the history
  • Loading branch information
mukesh2006 committed Jan 9, 2025
1 parent 37fb9ee commit a0a8d93
Show file tree
Hide file tree
Showing 5 changed files with 286 additions and 33 deletions.
82 changes: 78 additions & 4 deletions aesd-char-driver/aesd-circular-buffer.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,41 @@
struct aesd_buffer_entry *aesd_circular_buffer_find_entry_offset_for_fpos(struct aesd_circular_buffer *buffer,
size_t char_offset, size_t *entry_offset_byte_rtn )
{
/**
* TODO: implement per description
*/
uint8_t finish_pos, traverse_ix;

if (buffer->in_offs > buffer->out_offs)
{
finish_pos = buffer->in_offs;
}
else
{
finish_pos = buffer->in_offs + AESDCHAR_MAX_WRITE_OPERATIONS_SUPPORTED - 1;
}

traverse_ix = buffer->out_offs;

#ifdef __DEBUG__
fprintf(stderr,"\n LOG 1: aesd_buffer_entry: buffer full: %d buffer->in_offs: %d, buffer->in_offs: %d, finish_pos: %d, \n", buffer->full, buffer->in_offs, buffer->in_offs, finish_pos);
#endif
while (traverse_ix <= finish_pos)
{
if (buffer->entry[traverse_ix % AESDCHAR_MAX_WRITE_OPERATIONS_SUPPORTED].size > char_offset)
{
/* Char must be in this string entry */
if (entry_offset_byte_rtn != NULL)
{
*entry_offset_byte_rtn = char_offset;
}
// b. Return the content (or partial content) related to the most recent 10 write commands, in the order they were received, on any read attempt.
return &buffer->entry[traverse_ix % AESDCHAR_MAX_WRITE_OPERATIONS_SUPPORTED];
}
else
{
char_offset -= buffer->entry[traverse_ix % AESDCHAR_MAX_WRITE_OPERATIONS_SUPPORTED].size;
traverse_ix++;
}
}

return NULL;
}

Expand All @@ -42,11 +74,53 @@ struct aesd_buffer_entry *aesd_circular_buffer_find_entry_offset_for_fpos(struct
* Any necessary locking must be handled by the caller
* Any memory referenced in @param add_entry must be allocated by and/or must have a lifetime managed by the caller.
*/
void aesd_circular_buffer_add_entry(struct aesd_circular_buffer *buffer, const struct aesd_buffer_entry *add_entry)
const char * aesd_circular_buffer_add_entry(struct aesd_circular_buffer *buffer, const struct aesd_buffer_entry *add_entry)
{
/**
* TODO: implement per description
*
*/
const char *ret_ptr = NULL;
if ((buffer == NULL) || (add_entry == NULL))
{
return ret_ptr;
}

if (buffer->full)
{
ret_ptr = buffer->entry[buffer->in_offs].buffptr;
}

struct aesd_buffer_entry *tempEntry=&buffer->entry[buffer->in_offs];
tempEntry->buffptr = add_entry->buffptr;
tempEntry->size = add_entry->size;

// correct the entry for the in_offs
buffer->in_offs=((buffer->in_offs+1)< AESDCHAR_MAX_WRITE_OPERATIONS_SUPPORTED)?buffer->in_offs+1:0;

#ifdef __DEBUG__
fprintf(stderr,"\n LOG 1: aesd_circular_buffer_add_entry: buffer->in_offs: %d, buffer->out_offs: %d, buffer->full: %d \n", buffer->in_offs, buffer->out_offs, buffer->full);
#endif

// check if this entry will make the buffer full
if (true==buffer->full)
{
#ifdef __DEBUG__
fprintf(stderr,"\n LOG 2: aesd_circular_buffer_add_entry: buffer full: %d \n", buffer->in_offs);
#endif
buffer->out_offs = (buffer->out_offs + 1) % AESDCHAR_MAX_WRITE_OPERATIONS_SUPPORTED;
}
else if (buffer->out_offs == buffer->in_offs)
{
// bufferEntry=&buffer[buffer->out_offs]; //todo: free the memory or let the user take care of it
// buffer->out_offs=(buffer->out_offs+1)%AESDCHAR_MAX_WRITE_OPERATIONS_SUPPORTED;
buffer->full = true;
#ifdef __DEBUG__
fprintf(stderr,"\n LOG 3: aesd_circular_buffer_add_entry: buffer full: %d \n", buffer->full);
#endif
}

return ret_ptr;
}

/**
Expand Down
2 changes: 1 addition & 1 deletion aesd-char-driver/aesd-circular-buffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ struct aesd_circular_buffer
extern struct aesd_buffer_entry *aesd_circular_buffer_find_entry_offset_for_fpos(struct aesd_circular_buffer *buffer,
size_t char_offset, size_t *entry_offset_byte_rtn );

extern void aesd_circular_buffer_add_entry(struct aesd_circular_buffer *buffer, const struct aesd_buffer_entry *add_entry);
extern const char * aesd_circular_buffer_add_entry(struct aesd_circular_buffer *buffer, const struct aesd_buffer_entry *add_entry);

extern void aesd_circular_buffer_init(struct aesd_circular_buffer *buffer);

Expand Down
217 changes: 191 additions & 26 deletions aesd-char-driver/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,52 +18,196 @@
#include <linux/cdev.h>
#include <linux/fs.h> // file_operations
#include "aesdchar.h"

int aesd_major = 0; // use dynamic major
int aesd_minor = 0;

MODULE_AUTHOR("Your Name Here"); /** TODO: fill in your name **/
MODULE_AUTHOR("Mukesh Jha");
MODULE_LICENSE("Dual BSD/GPL");

struct aesd_dev aesd_device;

int aesd_open(struct inode *inode, struct file *filp)
{
PDEBUG("open");
/**
* TODO: handle open
*/
PDEBUG("\naesd_open: open");
struct aesd_dev *tmp_dev;
tmp_dev = container_of(inode->i_cdev, struct aesd_dev, cdev);
PDEBUG("\naesd_open: entry buffer pointer val: %p",tmp_dev->buf_entry);
filp->private_data = tmp_dev;
return 0;
}

int aesd_release(struct inode *inode, struct file *filp)
{
PDEBUG("release");
/**
* TODO: handle release
*/
PDEBUG("\naesd_release: release");
filp->private_data = NULL;
return 0;
}

ssize_t aesd_read(struct file *filp, char __user *buf, size_t count,
loff_t *f_pos)
{
ssize_t retval = 0;
PDEBUG("read %zu bytes with offset %lld",count,*f_pos);
/**
* TODO: handle read
*/
ssize_t retval = ERROR;
struct aesd_dev *dev = filp->private_data;
size_t entry_offset = 0;
size_t remaining_bytes = 0;
PDEBUG("\naesd_read: read %zu bytes with offset %lld", count, *f_pos);


if (filp == NULL || buf == NULL || f_pos == NULL || *f_pos < 0)
{
PDEBUG("\naesd_read: Invalid arguments");
return retval;
}

if (mutex_lock_interruptible(&dev->mutex))
{
PDEBUG("\naesd_read: Failed to lock mutex");
return -ERESTARTSYS;
}
PDEBUG("\naesd_read: Mutex locked");

struct aesd_buffer_entry *temp_entry = aesd_circular_buffer_find_entry_offset_for_fpos(&dev->buffer, *f_pos, &entry_offset);
if (temp_entry == NULL)
{
PDEBUG("Failed to find entry for given file position");
mutex_unlock(&dev->mutex);
PDEBUG("Mutex unlocked due to entry not found");
return retval;
}

remaining_bytes = temp_entry->size - entry_offset;
if (remaining_bytes >= count)
remaining_bytes = count;

unsigned long bytes_not_copied = copy_to_user(buf, temp_entry->buffptr + entry_offset, remaining_bytes);
if (bytes_not_copied != 0)
{
PDEBUG("Failed to copy data to user space");
mutex_unlock(&dev->mutex);
PDEBUG("Mutex unlocked due to copy failure");
return -EFAULT;
}

*f_pos += remaining_bytes;
mutex_unlock(&dev->mutex);
PDEBUG("Mutex unlocked");
retval = remaining_bytes;

return retval;

}

ssize_t aesd_write(struct file *filp, const char __user *buf, size_t count,
loff_t *f_pos)
{
ssize_t retval = -ENOMEM;
PDEBUG("write %zu bytes with offset %lld",count,*f_pos);
/**
* TODO: handle write
*/
return retval;
PDEBUG("\naesd_write: write %zu bytes with offset %lld",count,*f_pos);
ssize_t pos = 0;
int prev_length = 0;
bool rec_complete = false;
struct aesd_dev *dev = NULL;
char *data_ptr;
char *ptr;

// Check for null pointers
if (filp == NULL || buf == NULL || f_pos == NULL)
{
return -EINVAL;
}


PDEBUG("\naesd_write: write %zu bytes with offset %lld", count, *f_pos);

// Check for negative file position
if (*f_pos < 0)
{
return -EINVAL;
}
// a. Allocate memory for each write command as it is received, supporting any length of write request (up to the length of memory which can be allocated through kmalloc), and saving the write command content within allocated memory.
// v. For the purpose of this assignment you can use kmalloc for all allocations regardless of size, and assume writes will be small enough to work with kmalloc.
data_ptr = kmalloc(count, GFP_KERNEL);
if (data_ptr == NULL)
{
PDEBUG("\naesd_write: Memory Allocation Failed\n");
return -ENOMEM;
}

// Copy data to the kernel space
if (copy_from_user(data_ptr, buf, count)) {
PDEBUG("\naesd_write: Data copy failed\n");
kfree(data_ptr);
return -EFAULT;
}

// Search for newline character i. The write command will be terminated with a \n character as was done with the aesdsocket application.
//1. Write operations which do not include a \n character should be saved and appended by future write operations and should not be written to the circular buffer until terminated.
ptr = memchr(data_ptr, '\n', count);
if (ptr == NULL)
{
pos = count;
} else
{
pos = ptr - data_ptr + 1;
rec_complete = true;
}

// Lock mutex
dev = filp->private_data;
if (dev == NULL)
{
return -EINVAL;
}


PDEBUG("\naesd_write: Locking mutex");
if (mutex_lock_interruptible(&dev->mutex))
{
kfree(data_ptr);
PDEBUG("\naesd_write: Failed to lock mutex");
return -ERESTARTSYS;
}
PDEBUG("\naesd_write: Mutex locked");

prev_length = dev->entry.size;
dev->entry.size += pos;

// Reallocate buffer
PDEBUG("Reallocation start");
char *new_data_ptr = krealloc(dev->entry.buffptr, dev->entry.size, GFP_KERNEL);
if (new_data_ptr == NULL)
{
PDEBUG("Memory reallocation Failed\n");
kfree(data_ptr);
mutex_unlock(&dev->mutex);
PDEBUG("Mutex unlocked due to reallocation failure");
return -ENOMEM;
}
PDEBUG("Reallocation complete");

dev->entry.buffptr = new_data_ptr; // Update buffptr to point to the new memory location

// Copy data to buffer
memcpy(dev->entry.buffptr + prev_length, data_ptr, pos);

// Add entry to circular buffer if complete record received
if (rec_complete)
{
const char *ret_ptr = aesd_circular_buffer_add_entry(&dev->buffer, &dev->entry);
if (ret_ptr != NULL)
{
kfree(ret_ptr);
}

dev->entry.size = 0;
dev->entry.buffptr = NULL;
}

// Unlock mutex and clean up
PDEBUG("Unlocking mutex");
mutex_unlock(&dev->mutex);
PDEBUG("Mutex unlocked");
kfree(data_ptr);
return pos;
}
struct file_operations aesd_fops = {
.owner = THIS_MODULE,
Expand Down Expand Up @@ -101,18 +245,16 @@ int aesd_init_module(void)
return result;
}
memset(&aesd_device,0,sizeof(struct aesd_dev));
aesd_circular_buffer_init(&aesd_device.buffer);
mutex_init(&aesd_device.mutex);

/**
* TODO: initialize the AESD specific portion of the device
*/

result = aesd_setup_cdev(&aesd_device);

if( result ) {
unregister_chrdev_region(dev, 1);
}
return result;

}

void aesd_cleanup_module(void)
Expand All @@ -124,11 +266,34 @@ void aesd_cleanup_module(void)
/**
* TODO: cleanup AESD specific poritions here as necessary
*/
int index =0;
struct aesd_buffer_entry *circ_buf_entry = NULL;

// PDEBUG("cleanup: buf entry pointer: %p", aesd_device.buf_entry);

unregister_chrdev_region(devno, 1);
}
// free the private entry struct
kfree(aesd_device.buf_entry);
aesd_device.buf_entry = NULL;

// PDEBUG("cleanup: buf entry pointer after: %p", aesd_device.buf_entry);

// free all entries in circular buffer
AESD_CIRCULAR_BUFFER_FOREACH(circ_buf_entry, &aesd_device.circ_buf, index){


if(circ_buf_entry->buffptr != NULL){

// PDEBUG("cleanup loop: freeing circ buf entry");
circ_buf_entry->size = 0;
kfree(circ_buf_entry->buffptr);
circ_buf_entry->buffptr = NULL;

}

}
mutex_destroy(&aesd_device.mutex);
unregister_chrdev_region(devno, 1);
}

module_init(aesd_init_module);
module_exit(aesd_cleanup_module);
7 changes: 5 additions & 2 deletions server/aesdsocket.c
Original file line number Diff line number Diff line change
Expand Up @@ -301,8 +301,11 @@ void setup_print_time_thread(int seconds)
//set the values of the intervals
memset(&timerEvent, 0, sizeof(timerEvent));
timerEvent.sigev_notify = SIGEV_THREAD;
timerEvent.sigev_notify_function = print_time_thread_fxn;


#if USE_AESD_CHAR_DEVICE != 1
timerEvent.sigev_notify_function = print_time_thread_fxn;
#endif

memset(&timerSpec, 0, sizeof(timerSpec));
timerSpec.it_interval.tv_sec = seconds;
timerSpec.it_interval.tv_nsec = 0;
Expand Down
Loading

0 comments on commit a0a8d93

Please sign in to comment.