您的当前位置:首页正文

【linux内核分析与应用-陈莉君】动手实践-编写字符设备驱动程序

2023-05-09 来源:爱站旅游
导读【linux内核分析与应用-陈莉君】动手实践-编写字符设备驱动程序

目录

 


1.设备驱动程序的定义方式

两种:
1.全局静态变量;
2.内核提供的API.

2.1本文采用第二种方式中的常规方式来实现一个简单的虚拟设备驱动并且实现它的读写功能;
2.2本文采用第二种方式中的字符设备驱动也可以采用MISC机制来注册,也就是linux将一些不符合预先确定
的字符设备划分为杂项设备,这类设备的主设备号是10,内核中使用MISC_DEVICE这个结构体来描述,如果使
用MISC机制来创建设备,就需要定义DEVICE结构体.





说说misc:
杂项设备(misc device)
杂项设备也是嵌入式系统中用得比较多的一种设备驱动。在 Linux 内核的include/linux目录下有
miscdevice.h文件,要把自己定义的misc device从设备定义在这里。其实是因为这些字符设备不符合预先
确定的字符设备范畴,所有这些设备采用主编号10 ,一起归于misc device,其实misc_register就是用主
标号10调用register_chrdev()的,只不过misc是将一些字符设备存放在misc类中。换句话说,misc设备其
实也就是特殊的字符设备。


 

2.源码

2.1普通字符设备

驱动程序代码:
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/init.h>
#include <linux/cdev.h>

#define DEMO_NAME "my_demo_dev"

static dev_t dev;
static struct cdev *demo_cdev;
static signed count = 1;

static int demodrv_open(struct inode *inode,struct file *file)
{
    int major = MAJOR(inode->i_rdev);
    int minor = MINOR(inode->i_rdev);
    printk("%s:major=%d,minor = %d.\n",__func__,major,minor);
    return 0;
}

static ssize_t demodrv_read(struct file *file,char __user *buf,size_t lbuf,loff_t *ppos)
{
    printk("%s enter\n",__func__);
    return 0;    
}


static ssize_t demodrv_write(struct file *file,const char __user *buf,size_t count,loff_t *f_pos)
{
    printk("%s enter\n",__func__);
    return 0;    
}


static const struct file_operations demodrv_fops={
     .owner = THIS_MODULE,
     .open   = demodrv_open,
     .read   = demodrv_read,
     .write  = demodrv_write
};



static int __init simple_char_init(void)
{
    int ret;
    /* Register a single major with a specified minor range.分配一个主设备号和对应特定的一个范围的次设备号*/
    ret = alloc_chrdev_region(&dev,0,count,DEMO_NAME);
    if(ret)
    {
        printk("failed to allocate char device region\n");
        return ret;
    }
    /*allocate a cdev structure.申请分配空间*/
    demo_cdev = cdev_alloc();
    if(!demo_cdev)
    {
        printk("cdev_alloc failed\n");
        goto unregister_chrdev;
    }
    /*initialize a cdev structure.初始化一个struct cdev对象,对fops进行赋值
 *       @cdev: the structure to initialize
 *             @fops: the file_operations for this device
 *                */
    cdev_init(demo_cdev,&demodrv_fops);
   
   /**
 *   * cdev_add() - add a char device to the system
 *   * @p: the cdev structure for the device
 *   * @dev: the first device number for which this device is responsible
 *   * @count: the number of consecutive minor numbers corresponding to this
 *   *         device
 *   *
 *   * cdev_add() adds the device represented by @p to the system, making it
 *   * live immediately.  A negative error code is returned on failure.
 *                                     */
    ret = cdev_add(demo_cdev,dev,count);
    if(ret)
    {
        printk("cdev_add failed\n");
        goto cdev_fail;
    }
    printk("success register char device:%s\n",DEMO_NAME);
    printk("Major number = %d,minor number = %d.\n",MAJOR(dev),MINOR(dev));
    return 0;
cdev_fail:
   cdev_del(demo_cdev);
unregister_chrdev:
   unregister_chrdev_region(dev,count);
   return ret;

}

static void __exit simple_char_exit(void)
{
    printk("removing device\n");
    if(demo_cdev)
       cdev_del(demo_cdev);
    unregister_chrdev_region(dev,count);
}

module_init(simple_char_init);
module_exit(simple_char_exit);
MODULE_LICENSE("GPL");



用户态测试代码:
nclude <stdio.h>
#include <fcntl.h>
#include <unistd.h>

# define DEMO_DEV_NAME "/dev/demo_drv"

int main()
{
     char buffer[64];
     int fd; 
  
     fd = open(DEMO_DEV_NAME,O_RDONLY);
     if(fd<0)
     {   
        printf("open device %s failed\n",DEMO_DEV_NAME);
        return -1; 
     }   
     read(fd,buffer,64);
     close(fd);
     return 0;
}

2.2杂项字符设备

驱动程序代码:
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/init.h>
#include <linux/cdev.h>
// 加入misc机制
#include <linux/miscdevice.h>
#include <linux/kfifo.h>

DEFINE_KFIFO(mydemo_fifo,char,64);

#define DEMO_NAME "my_demo_dev"

static struct device *mydemodrv_device;


static int demodrv_open(struct inode *inode,struct file *file)
{
    int major = MAJOR(inode->i_rdev);
    int minor = MINOR(inode->i_rdev);
    printk("%s:major=%d,minor = %d.\n",__func__,major,minor);
    return 0;
}

static ssize_t demodrv_read(struct file *file,char __user *buf,size_t count,loff_t *ppos)
{
    int actual_readed=0;
    int ret=0;
    ret = kfifo_to_user(&mydemo_fifo,buf,count,&actual_readed); 
    if(ret)
        return -EIO;
    printk("%s enter\n",__func__);
    printk("%s:actual_readed = %d , ppos = %lld\n",__func__,actual_readed,*ppos);
    return actual_readed;     
}


static ssize_t demodrv_write(struct file *file,const char __user *buf,size_t count,loff_t *ppos)
{
    unsigned int actual_write;
    int ret;
    ret = kfifo_from_user(&mydemo_fifo,buf,count,&actual_write);
    if(ret)
        return -EIO;
    printk("%s enter\n",__func__);
    printk("%s:actual_write = %d , ppos = %lld\n",__func__,actual_write,*ppos);
    return actual_write;    
}


static const struct file_operations demodrv_fops={
     .owner = THIS_MODULE,
     .open   = demodrv_open,
     .read   = demodrv_read,
     .write  = demodrv_write
};



static struct miscdevice mydemodrv_misc_device = {
    .minor = MISC_DYNAMIC_MINOR,
    .name  = DEMO_NAME,
    .fops  = &demodrv_fops
};

static int __init simple_char_init(void)
{
    int ret;
    /*自动创建设备节点,不需要手动创建*/
    ret = misc_register(&mydemodrv_misc_device);
    if(ret)
    {
        printk("failed to allocate misc device\n");
        return ret;
    }
    mydemodrv_device = mydemodrv_misc_device.this_device;
    printk("76 Successed register char device %s.\n",DEMO_NAME);
    return 0;
}

static void __exit simple_char_exit(void)
{
    printk("removing device\n");
    misc_deregister(&mydemodrv_misc_device);
}

module_init(simple_char_init);
module_exit(simple_char_exit);
MODULE_LICENSE("GPL");



用户态测试程序代码:
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <malloc.h>
#include <string.h>

#define DEMO_DEV_NAME "/dev/my_demo_dev"

int main()
{
        char buffer[64];
        int fd; 
        int ret;
        size_t len;
        char message[] = "hello";
        char *read_buffer;
        len = sizeof(message);
        fd = open(DEMO_DEV_NAME,O_RDWR);
        if(fd<0)
        {
                printf("open device %s failed\n",DEMO_DEV_NAME);
                return -1; 
        }
        printf("line = %d ,len = %d.\n",__LINE__,len);
        ret = write(fd,message,len);
        printf("line = %d ,write ret = %d.\n",__LINE__,ret);
    
        if(ret != len)
        {
                printf("cannot write on device %d,ret = %d\n",fd,ret);
                return -1; 
        }
        read_buffer = malloc(2*len);
        memset(read_buffer,0,2*len);

        ret = read(fd,read_buffer,2*len);
        printf("line = %d , read %d bytes\n",__LINE__,ret);
        printf("read buffer = %s\n",read_buffer);

        close(fd);
        return 0;
}

 

3.测试遗留问题以及一些截图

生成的设备需要在dev目录下生成对应的节点,这里需要手动生成,是mknod命令,
mknod /dev/demo_drv c 243 0
mknod /dev/demo_dev c 243 0
为啥我这两个都执行了也没有问题?主设备号和次设备号可以都一样吗?


关机重启后,模块不见了,梳理流程.

 

因篇幅问题不能全部显示,请点此查看更多更全内容