字符驱动程序与循环缓冲区

时间:2015-05-09 17:03:18

标签: c linux-kernel kernel linux-device-driver

我正在学习设备驱动程序编程,我创建了一个简单的字符驱动程序,我从用户空间应用程序传递数据并将其存储在内核空间循环缓冲区中,然后从另一个用户空间应用程序再次读取此循环缓冲区。 / p>

我正在使用linux内核版本3.3.6

我的驱动程序代码是:

/* A simple character driver program to pass some data from user space and storing that on kernel circular buffer and reading back it on user space */

#include<linux/kernel.h>
#include<linux/init.h>
#include<linux/module.h>
#include<linux/vmalloc.h>
#include<linux/fs.h>
#include<linux/major.h>
#include<linux/cdev.h>
#include<asm/uaccess.h>
#include<linux/slab.h>
#include<linux/device.h>
#include<linux/types.h>
#include<linux/kdev_t.h>
#include<linux/wait.h>
#include<linux/sched.h>
#include<linux/circ_buf.h>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("C-DAC");
#define MAX_DEVICE 2
#define KERN_BUFF_SIZE 1
#define DRIVER_NAME "ass3q1" 

int my_open(struct inode *inode, struct file *filp);
ssize_t my_write(struct file *filp, const char __user *usr_buff, size_t count, loff_t *ppos);
ssize_t my_read(struct file *filp, char __user *usr_buf, size_t count, loff_t *ppos);
int my_release(struct inode *inode, struct file *filp);
long my_ioctl(struct file *filp, unsigned int cmd,unsigned long arg);


/* DEFINE A DEVICE SPECIFIC DATA */
typedef struct privatedata1
{
    int minor_num;
    struct cdev mycdev;
    struct circ_buf KERN_BUFF;
    int KERN_SIZE;
//  char KERN_BUFF[KERN_BUFF_SIZE];
}my_privatedata;

my_privatedata devices[MAX_DEVICE];

/* DECLARE THE REQUIRED VARIABLES */
int major;
int minor=1;
dev_t device_num;

struct class *my_class;
struct device *my_device;

/* DEFINE THE FILE OPERATIONS STRUCTURE OF THE CHAR DRIVER */
struct file_operations my_fops={
    .owner      = THIS_MODULE,
    .open       = my_open,
    .write      = my_write,
    .read       = my_read,
    .release    = my_release,
    .unlocked_ioctl = my_ioctl,
};


/* INITIALISATION FUNCTION */
static int __init my_init(void)
{
    int i;
    int res;
    printk("\nI am in Init Function");
    /* DYNAMICALLY DEVICE NUMBER */
    res = alloc_chrdev_region(&device_num,minor,MAX_DEVICE,DRIVER_NAME);
    if(res<0)
    {
        printk("\nRegister Device Num generation failed");
        return -1;
    }
    major = MAJOR(device_num);

    my_class = class_create(THIS_MODULE,DRIVER_NAME);
    if(my_class == NULL)
    {
        printk("\nClass creation failed");
        return -1;
    }

    for(i=0; i<MAX_DEVICE; i++)
    {
        device_num = MKDEV(major, minor + i);
        cdev_init(&devices[i].mycdev,&my_fops);     //registration of device
        cdev_add(&devices[i].mycdev,device_num,1);  //attachment of device

        /* CREATE DEVICE NODES IN /dev/ DIRECTORY */
        my_device = device_create(my_class,NULL,device_num,NULL,"sample_cdev%d",i);     
        if(my_device == NULL)
        {
            class_destroy(my_class);
            printk("\nDevice creation failed");
            return -1;
        }

        devices[i].minor_num = minor+i;
    }
    return 0;
}

static void __exit my_exit(void)
{
    int i;

    printk("\nI am in Exit Function");
    /* REMOVE DEVICES AND NODES */
    for(i=0; i<MAX_DEVICE; i++)
    {
        device_num = MKDEV(major, minor + i);
        cdev_del(&devices[i].mycdev);
        device_destroy(my_class, device_num);
    }

    /* DESTROY CLASS */
    class_destroy(my_class);

    /* UNREGISTER DEVICE WITH KERNEL */
    device_num = MKDEV(major, minor);
    unregister_chrdev_region(device_num, MAX_DEVICE);
}

/* DEVICE OPEN METHOD */
int my_open(struct inode *inode, struct file *filp)
{
    my_privatedata *dev = container_of(inode->i_cdev, my_privatedata, mycdev);
    filp->private_data = dev;
    dev->KERN_SIZE = 4096;
    printk("\nIn character driver open function device node %d", dev->minor_num);
    return 0;
}

/* DEVICE WRITE METHOD */
ssize_t my_write(struct file *filp, const char __user *usr_buff, size_t count, loff_t *ppos)
{
    my_privatedata *dev = filp->private_data;

    if(CIRC_SPACE(dev->KERN_BUFF.head, dev->KERN_BUFF.tail, dev->KERN_SIZE) >= 1)
    {
        int i;
        char ch;
        for(i=0; i<count; i++)
        {
            get_user(ch, &usr_buff[i]);
            dev->KERN_BUFF.buf[dev->KERN_BUFF.head] = ch;
            printk("\nIn character driver write function and value of KERN_BUFF is: %s", dev->KERN_BUFF.buf);
            dev->KERN_BUFF.head = (dev->KERN_BUFF.head + 1) & (dev->KERN_SIZE-1);
        }
    }   
    else
    {
        printk("\nCopy from user to kernel space failed");
        return -EFAULT;
    }
    return 0;
}

/* DEVICE READ METHOD */
ssize_t my_read(struct file *filp, char __user *usr_buf, size_t count, loff_t *ppos)
{
    my_privatedata *dev = filp->private_data;
    int res;
    printk("\nI am in character driver read function");
    dev->KERN_BUFF.buf = "Bye Bye";
//  usr_buf = dev->KERN_BUFF.buf;

    res = copy_to_user((char *)usr_buf, (char *)dev->KERN_BUFF.buf, strlen(dev->KERN_BUFF.buf)+1);
    printk("\nData '%s' from kernel buffer to user buffer copied successfully with bytes: %d",dev->KERN_BUFF.buf,strlen(dev->KERN_BUFF.buf));

/*  if(res == 0)
    {
        printk("\nData '%s' from kernel buffer to user buffer copied successfully with bytes: %d",dev->KERN_BUFF.buf,strlen(dev->KERN_BUFF.buf));
        memset(dev->KERN_BUFF.buf, 0, strlen(dev->KERN_BUFF.buf));
        return strlen(dev->KERN_BUFF.buf);
    }
    else
    {
        printk("\nCopy from kernel to user failed");
        return -EFAULT;
    }
*/
    return 0;
}

/* DEVICE RELEASE METHOD */
int my_release(struct inode *inode, struct file *filp)
{
    my_privatedata *dev = filp->private_data;
    printk("\nI am in release function and minor number is %d", dev->minor_num);
    return 0;
}

/* DRIVER IOCTL METHOD */
long my_ioctl(struct file *filp, unsigned int cmd,unsigned long arg)
{
    printk("\nIn Driver ioctl function");
    my_privatedata *dev = filp->private_data;

    switch (cmd)
    {
        case 1:
            {
                int count;
                count = CIRC_CNT(dev->KERN_BUFF.head, dev->KERN_BUFF.tail,dev->KERN_SIZE);
                printk("\nSize of buffer count is %d",count);
                return count;
                break;
            }
        case 2:
            {
                int space;
                space = CIRC_SPACE(dev->KERN_BUFF.head, dev->KERN_BUFF.tail,dev->KERN_SIZE);
                printk("\nSize of buffer count is %d",space);
                return space;
                break;
            }
        default:
            {
                printk("\nNothing to show");
                break;
            }
    }
}

module_init(my_init);
module_exit(my_exit);

我的用户空间编写器应用

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<fcntl.h>
#include<string.h>

int main()
{
    int fd;
    char Ubuff[50], Kbuff[50];

    fd = open("/dev/sample_cdev1", O_RDWR);
    if(fd < 0) {
        perror("Unable to open the device file\n");
        return -1;
    }

    printf("\nPlease enter a string");
    gets(Ubuff);

    /* Write the data into the device */
    write(fd , Ubuff , strlen(Ubuff) + 1);

    close(fd);  
    return 0;
}

我的用户空间阅读器应用

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<fcntl.h>
#include<string.h>

int main()
{
    int fd;
    char Ubuff[50], Kbuff[50];

    fd = open("/dev/sample_cdev1", O_RDWR);
    if(fd < 0) {
        perror("Unable to open the device file\n");
        return -1;
    }

    /* Read the data back from the device */
    memset(Kbuff , 0 ,sizeof(Kbuff));
    read(fd , Kbuff , sizeof(Kbuff));
    printf("Data from kernel : %s\n", Kbuff);

    close(fd);
    return 0;
}

在my_write函数中,我在

处得到一个内核空指针解除引用错误
            dev->KERN_BUFF.buf[dev->KERN_BUFF.head] = ch;
            printk("\nIn character driver write function and value of KERN_BUFF is: %s", dev->KERN_BUFF.buf);
            dev->KERN_BUFF.head = (dev->KERN_BUFF.head + 1) & (dev->KERN_SIZE-1);

my_read函数似乎不正确,但由于我无法向内核循环缓冲区写入内容,因此无法测试my_read函数。

我做错了什么?

1 个答案:

答案 0 :(得分:0)

我认为问题是你没有在循环缓冲区中分配任何空间...... dev->KERN_BUFF.buf = kmalloc(...) 不要忘记自由。 你也注册了2个my_private_data,这意味着每个fd都会获得自己的my_private_data。我想你希望生产者和消费者能够处理相同的数据。也许你需要一个spin_lock ......

还请阅读:CIRCULAR BUFFERS