Simple character device not working as expected

824 Views Asked by At

I have been writing code for a simple character device and test script. Everything compiles properly. The character device has a device file, the make command, insmod and rmmod commands, etc. all work fine, and have expected output when checking /var/log/syslog. Additionally, the test script compiles and runs without returning killed or anything- it has expected behavior except for the following issue.

Nothing in assembling the module seems to have any issues. However, when I run my test script to evaluate my device/its functions, nothing is returned. for example, when I run write and type in "hello world", it doesn't return any sort of error. However, if I run seek with the whence and offset set to 0, and then use read, it does not output anything. However, I cannot find any issues in my code that would explain this behavior. Edited (Additionally, if I check the logs, the kern alert for the init and exit functions work, but none of the other kern alerts return anything, leading me to believe that maybe they are never being entered, although i cannot figure out why that would be the case.) Any insight would be appreciated. My character device looks like this:

#include <linux/init.h>
#include <linux/module.h>

#include <linux/fs.h>  //gets the functions for device driver coding
#include <linux/slab.h>
#include <asm/uaccess.h>  //move data b/t userspace and kernel

#define BUFFER_SIZE 1024
#define MAJOR_NUMBER 240
#define DEVICE_NAME "simple_character_device"

char device_buffer[BUFFER_SIZE];
int openNum = 0;
int closeNum = 0;
char* buffer;


ssize_t simple_char_driver_read (struct file *pfile, char __user *buffer, size_t length, loff_t *offset){

    int bytesRead = 0;
    if (*offset >=BUFFER_SIZE){
        bytesRead = 0;
        return bytesRead;
    }
    if (*offset + length > BUFFER_SIZE){
        length = BUFFER_SIZE - *offset;
    }
    printk(KERN_INFO "Reading from device\n");
    if (copy_to_user(buffer, device_buffer + *offset, length) != 0){
        return -EFAULT;
    }
    copy_to_user(buffer, device_buffer + *offset, length);
    *offset += length;
    printk(KERN_ALERT "Read: %s", buffer);
    printk(KERN_ALERT "%d bytes read\n", bytesRead); 
    return 0;
}

ssize_t simple_char_driver_write (struct file *pfile, const char __user *buffer, size_t length, loff_t *offset){
    printk(KERN_INFO "Writing to device\n");
    copy_from_user(device_buffer + *offset, buffer, length);
    *offset = strlen(device_buffer);
    printk(KERN_ALERT "%zu bytes written\n", strlen(buffer));
    printk(KERN_ALERT "%s", device_buffer);
    return length;
}

int simple_char_driver_open (struct inode *pinode, struct file *pfile){
    openNum++;
    printk(KERN_ALERT "Device opened %d times\n", openNum);
    return 0;
}

int simple_char_driver_close (struct inode *pinode, struct file *pfile){
    closeNum++;
    printk(KERN_ALERT "Device closed %d times\n", closeNum);
    return 0;
}

loff_t simple_char_driver_seek (struct file *pfile, loff_t position, int whence){
    printk(KERN_ALERT "!!!!!!!!%llx", position);
    switch (whence) {
        case SEEK_SET: 
            if (position < 0 || position >= BUFFER_SIZE){ 
                return -1;
            }
            pfile->f_pos = position;
            break;
        case SEEK_CUR:  
            pfile->f_pos += position;
            break;
        case SEEK_END: 
            pfile->f_pos = BUFFER_SIZE - position;
            break;
    }
    return 0;
}

struct file_operations simple_char_driver_file_operations = {

    .owner   = THIS_MODULE,
    .read = simple_char_driver_read,
    .write = simple_char_driver_write,
    .open = (*simple_char_driver_open),
    .release = simple_char_driver_close,  
    .llseek = simple_char_driver_seek,
};

static int simple_char_driver_init(void){
    printk(KERN_INFO "initializing/registering device\n");
    buffer = kmalloc(BUFFER_SIZE, GFP_KERNEL);
    register_chrdev(MAJOR_NUMBER, DEVICE_NAME, &simple_char_driver_file_operations);
    return 0;
}

static void simple_char_driver_exit(void){
    printk(KERN_INFO "denitializing/unregistering device\n");
    if (buffer)
        kfree(buffer);
    unregister_chrdev(MAJOR_NUMBER, DEVICE_NAME);
}

module_init(simple_char_driver_init);
module_exit(simple_char_driver_exit);

As shown above, I have initialization and exit methods, a method for reading from the device file, a method for writing to it, and a seek method to seek a specific position in the file.

In the test file below, the read method take a parameter of how many bytes you would like to read. The seek method takes in a whence and an offset. The write method takes in a string you would like to write.

My test file looks like this:

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#define DEVICE "/dev/simple_character_device"
#define BUFFER_SIZE 1024

int main () {
    char command;
    char buffer[BUFFER_SIZE];
    int length, offsetVal, whence;
    int file = open(DEVICE, O_RDWR);
    while (1) {
        printf("\nr) Read from device\nw) Write to device\ns) Seek\ne) Exit device\nAnything else to continue reading and writing\n\nEnter command: ");
        scanf("%c", &command);
        switch (command) {
            case 'w':
            case 'W':
                printf("Enter data you want to write to the device: ");
                scanf("%s", buffer);
                write(file, buffer, BUFFER_SIZE);
                while (getchar() != '\n');
                break;
            case 'r':
            case 'R':
                printf("Enter the number of bytes you want to read: ");
                scanf("%d", &length);
                char* buffer = (char*)malloc(sizeof(char) *length);
                read(file, buffer, length);
                printf("Device output: %s\n", buffer);
                while (getchar() != '\n');
                break;
            case 's':
                printf("Enter an offset value: ");
                scanf("%d", &offsetVal);
                printf("Enter a value for whence (third parameter): ");
                scanf("%d", &whence);
                lseek(file, offsetVal, whence);
                while (getchar() != '\n');
                break;
            case 'e':
            case 'E':
                return 0;
            default:
                while (getchar() != '\n');
        }
    }
    close(file);
    return 0;
}

If you have any insight into why the code does not run properly, please let me know. I am extremely puzzled by this problem. Thank you for you time and assistance!

Edit:

Additionally, if I check the logs, the kern alert for the init and exit functions work, but none of the other kern alerts return anything, leading me to believe that maybe they are never being entered, although I cannot figure out why that would be the case. Also, because of this problem, my issue is not a duplicate of the suggested post linked. That solution has been implemented but my problem remains.

0

There are 0 best solutions below