嵌入式开发记录-day18 设备节点简介、生成设备节点、字符设备框架

1、以模块的方式生成设备节点,不需要烧写镜像

2、生成的设备节点在ls /dev/下,可以供上层应用程序打开使用。相当于提供一个访问内核模块的一个接口。对于常见的open函数,操作一个节点设备 /dev/led0,可以按照下面这样,通过这样的方式就可以调用到模块中定义的file ops接口。

open("/dev/led0", O_RDWR);  // 有点类似window下的文件路径,但是led0是一个内核模块设备

 0、查看设备号:

  设备分主设备号和次设备号,主设备号用于区分一大类设备,此设备号区分大类设备号下的不同设备。

  cat  /proc/device   查看主设备号;

  cat  /proc/misc   查看杂项设备的此设备号;

1、杂项设备:可以节省主设备节点(主设备节点有限255),驱动编写也相对简单,并且是必须要编译的;

2、头文件:include/linux/miscdevice.h

3、涉及到的结构体:struct miscdevice 注册的是杂项设备

struct miscdevice  {
        int minor;   // 设备号,一般随机取值,系统分配
        const char *name;  // 生成设备节点的名称,随便取
        const struct file_operations *fops;  // 设备结点文件
        struct list_head list;
        struct device *parent;
        struct device *this_device;
        const char *nodename;
        mode_t mode;
};

4、注册函数、卸载函数;杂项设备注册与卸载

extern int misc_register(struct miscdevice * misc);
extern int misc_deregister(struct miscdevice *misc);

5、上面需要注册结构体中所包含的结构体 const struct file_operations *fops; // 设备结点文件,操作该结构体中的函数,进而读写相应的设备

  对外界提供访问该设备的接口;需要自己实现函数功能;

  上层应用对接该结构体的函数接口;

  在头文件include/linux/miscdevice.h

const struct file_operations    *i_fop;
    struct file_operations {
        struct module *owner;              // 模块属于者
        loff_t (*llseek) (struct file *, loff_t, int);   //  定位函数
        ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);  // 读操作
        ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);   // 写操作
        ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
        ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
        int (*readdir) (struct file *, void *, filldir_t);
        unsigned int (*poll) (struct file *, struct poll_table_struct *);
...  // 包含非常多,只列举常用的

6、常用的结构体变量:

.owner = THIS_MODULE   // 使用宏定义
.open  
.release   
.unlocked_ioctl() 对GPIO的操作  应用向节点串参数

7、实验代码devicenode_linux_module.c,生成设备结点 hello_ctl123

  杂项设备与platform总线结合:

  1 // 驱动注册所需头文件 包含结构体,注册和卸载所需的函数
  2 #include <linux/platform_device.h>
  3 #include <linux/module.h>
  4 #include <linux/init.h>
  5 #include <linux/miscdevice.h>  // 注册杂项设备头文件
  6 #include <linux/fs.h>    // 设备结点文件
  7 
  8 #define DRIVER_NAME "hello_ctl"
  9 #define DEVICE_NAME "hello_ctl123"
 10 
 11 
 12 MODULE_LICENSE("Dual BSD/GPL"); 
 13 MODULE_AUTHOR("TOPEET");
 14 
 15 static int hello_open(struct inode *node, struct file *f)
 16 {
 17     printk(KERN_EMERG "\t func hello open\n");
 18     return 0;
 19 }
 20 
 21 static int hello_release(struct inode *node, struct file *f)
 22 {
 23     printk(KERN_EMERG "\t hello release\n");
 24     return 0;
 25 }
 26 
 27 static long hello_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
 28 {
 29     printk(KERN_EMERG "\t hello ioctl\n");
 30     printk("hello_ioctl cmd is %d ...arg is %d\n",cmd,arg);
 31     return 0;
 32 }
 33 
 34 // 文件操作,对应生成的设备结点具体如何操作
 35 static struct file_operations hello_ops = {
 36     .owner = THIS_MODULE,
 37     .open = hello_open,
 38     //   int (*open) (struct inode *, struct file *);
 39     .release = hello_release,
 40     // int (*release) (struct inode *, struct file *);
 41     .unlocked_ioctl = hello_ioctl,
 42     //  long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);long);
 43 
 44 };
 45 
 46 static struct miscdevice  hello_dev = {
 47     .minor = MISC_DYNAMIC_MINOR,  // 自动分配ID
 48     .name = DEVICE_NAME,  // 设备结点名称
 49     .fops = &hello_ops,   // 结构体
 50 };
 51 
 52 // int (*probe)(struct platform_device *);
 53 // 初始化相关动作
 54 static int hello_probe(struct platform_device *p)
 55 {
 56     printk(KERN_EMERG "\t initialized\n");
 57     misc_register(&hello_dev);  //2、 杂项设备注册
 58     return 0;
 59 }
 60 //  int (*remove)(struct platform_device *);
 61 static int hello_remove(struct platform_device *p)
 62 {
 63     printk(KERN_EMERG "\t hello_ct123 removed\n");
 64     misc_deregister(&hello_dev);
 65     return 0;
 66 }
 67 // int (*suspend)(struct platform_device *, pm_message_t state);
 68 static int hello_suspend(struct platform_device *p, pm_message_t state)
 69 {
 70     return 0;    
 71 }
 72 //  int (*resume)(struct platform_device *);
 73 static int hello_resume(struct platform_device *p)
 74 {
 75     return 0;    
 76 }
 77 //  void (*shutdown)(struct platform_device *);
 78 static void hello_shutdown(struct platform_device *p)
 79 {
 80         
 81 }
 82 
 83 // 3、结构体数据初始化
 84 // 注册驱动传入的结构体
 85 struct platform_driver hello_driver = {
 86     .probe = hello_probe,    // 用于初始化模块
 87     .remove = hello_remove,  // 移除模块时,执行的动作
 88     .suspend = hello_suspend,// 模块挂起时,执行的动作
 89     .resume = hello_resume,  // 挂起的模块,恢复运行执行动作
 90     .shutdown = hello_shutdown,
 91     .driver = {
 92         .name = DRIVER_NAME, // 驱动名称
 93         .owner = THIS_MODULE,// 驱动所有者,THIS_MODULE宏定义
 94     },
 95 }; 
 96 // 2、模块初始化相关实现         
 97 // 模块的入口函数 加载模块执行动作     
 98 static int hello_init(void)
 99 {
100     int DriverState;
101     printk(KERN_EMERG "HELLO WORLD enter!\n");
     // 平台驱动注册
102 DriverState = platform_driver_register(&hello_driver); // 注册驱动 转而执行初始化动作 103 printk(KERN_EMERG "\tDriverState is %d\n", DriverState); // 注册驱动执行状态 104 105 return 0; 106 } 107 // 模块退出函数 卸载模块所执行 108 static void hello_exit(void) 109 { 110 printk(KERN_EMERG "HELLO WORLD exit!\n"); 111 platform_driver_unregister(&hello_driver); // 卸载驱动函数 112 } 113 114 //1、 入口函数module_nint 115 module_init(hello_init); 116 module_exit(hello_exit);

上面调用流程

8、测试结果,使用ls dev//  查看生成的设备结点

[root@iTOP-4412]# ./mnt/disk/app_hello.o                                                           
[  234.644663]   func hello open
[  234.646263]   hello ioctl
[  234.648756] hello_ioctl cmd is 1 ...arg is 6
[  234.653025]   hello release
App open /dev/hello_ctl123 success   // 执行顺兴,貌似内核优先级比较高

9、需要注意,设备、驱动注册都是嵌入到内中的,生成设备结点则是面向应用;

10、编写简单的应用,调用设备结点App_hello_ctl123

 

 1 #include <stdio.h>
 2 #include <sys/types.h> 
 3 #include <sys/stat.h> 
 4 #include <fcntl.h>  
 5 #include <unistd.h>  
 6 #include <sys/ioctl.h>
 7 
 8 int main(void)
 9 {
10     int fd;
11     char* hello_node = "/dev/hello_ctl123";
12     fd = open(hello_node,O_RDWR|O_NDELAY); // 非阻塞打开
13     if(fd < 0){
14         printf("func open app %s failed\n",hello_node);
15         return -1;
16     }else{
17         printf("App open %s success\n",hello_node);
18         ioctl(fd,1,6);  // 应用程序向驱动传值
19     }
20     close(fd);
21     return 0;
22 }

11、设备结点、驱动注册、设备注册

  设备结点面向上层应用程序;驱动注册和设备注册需要嵌入到linux内核中;

12、简化上面的生成设备结点代码,devicenode_Samp.c

  杂项设备驱动框架:

  使用结构体描述杂项设备:

struct miscdevice  {
    int minor;
    const char *name;
    const struct file_operations *fops;
    struct list_head list;
    struct device *parent;
    struct device *this_device;
    const struct attribute_group **groups;
    const char *nodename;
    umode_t mode;
};
int misc_register(struct miscdevice *misc);
void misc_deregister(struct miscdevice *misc);
// 通过fops成员提供向外的访问接口

  简化后的实例:

 1 #include <linux/platform_device.h>
 2 #include <linux/module.h>
 3 #include <linux/init.h>
 4 #include <linux/miscdevice.h>  // 注册杂项设备头文件
 5 #include <linux/fs.h>    // 设备结点文件
 6 
 7 #define DEVICE_NAME "hello_ctl123"
 8 
 9 
10 MODULE_LICENSE("Dual BSD/GPL"); 
11 MODULE_AUTHOR("TOPEET");
12 
13 static int hello_open(struct inode *node, struct file *f)
14 {
15     printk(KERN_EMERG "\t func hello open\n");
16     return 0;
17 }
18 
19 static int hello_release(struct inode *node, struct file *f)
20 {
21     printk(KERN_EMERG "\t hello release\n");
22     return 0;
23 }
24 
25 static long hello_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
26 {
27     printk(KERN_EMERG "\t hello ioctl\n");
28     printk("hello_ioctl cmd is %d ...arg is %d\n",cmd,arg);
29     return 0;
30 }
31 
32 // 文件操作,对应生成的设备结点具体如何操作
33 static struct file_operations hello_ops = {
34     .owner = THIS_MODULE,
35     .open = hello_open,
36     //   int (*open) (struct inode *, struct file *);
37     .release = hello_release,
38     // int (*release) (struct inode *, struct file *);
39     .unlocked_ioctl = hello_ioctl,
40     //  long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);long);
41 
42 };
43 
44 static struct miscdevice  hello_dev = {
45     .minor = MISC_DYNAMIC_MINOR,  // 自动分配ID
46     .name = DEVICE_NAME,  // 设备结点名称
47     .fops = &hello_ops,   // 结构体
48 };
49 
50 // 2、模块初始化相关实现         
51 // 模块的入口函数 加载模块执行动作     
52 static int hello_init(void)
53 {
54     int DriverState;
55     printk(KERN_EMERG "HELLO WORLD enter!\n");
56     DriverState = misc_register(&hello_dev);
57     printk(KERN_EMERG "\tDriverState is %d\n", DriverState); // 注册驱动执行状态
58     
59     return 0;
60 }
61 // 模块退出函数  卸载模块所执行
62 static void hello_exit(void)
63 {
64     printk(KERN_EMERG "HELLO WORLD exit!\n");
65     misc_deregister(&hello_dev);
66 }
67 
68 //1、 入口函数module_nint
69 module_init(hello_init);
70 module_exit(hello_exit);
平台驱动注册

二、字符设备驱动框架
  内核中使用struct cdev{};来描述一个字符设备;
#include <linux/cdev.h>
struct cdev {
    struct kobject kobj;
    struct module *owner;    
    const struct file_operations *ops;
    struct list_head list;
    dev_t dev;
    unsigned int count;
};
// 1. 分配一个字符设备
struct cdev *cdev_alloc(void);
// 初始化字符设备,配置习惯的系统调用接口。
void cdev_init(struct cdev *, const struct file_operations *);
// 添加字符设备到内核。
int cdev_add(struct cdev *, dev_t, unsigned);
// 释放时候,删除设备
void cdev_del(struct cdev *);

// 暂时用不到。
void cdev_put(struct cdev *p);
void cdev_set_parent(struct cdev *p, struct kobject *kobj);
int cdev_device_add(struct cdev *cdev, struct device *dev);
void cdev_device_del(struct cdev *cdev, struct device *dev);
void cd_forget(struct inode *);
1、注册字符设备
/**
 * alloc_chrdev_region() - register a range of char device numbers
 * @dev: output parameter for first assigned number
 * @baseminor: first of the requested range of minor numbers
 * @count: the number of minor numbers required
 * @name: the name of the associated device or driver
 *
 * Allocates a range of char device numbers.  The major number will be
 * chosen dynamically, and returned (along with the first minor number)
 * in @dev.  Returns zero or a negative error code.
 */
int alloc_chrdev_region(dev_t * dev, unsigned baseminor, unsigned count, const char * name);
// 内核自动分配设备号

/**
* register_chrdev_region() - register a range of device numbers
* @from: the first in the desired range of device numbers; must include
* the major number.
* @count: the number of consecutive device numbers required
* @name: the name of the device or driver.
*
* Return value is zero on success, a negative error code on failure.
*/
int register_chrdev_region(dev_t from, unsigned count, const char *name)
// 由调用者传入设备号参数

  2、卸载字符设备

/**
 * unregister_chrdev_region() - unregister a range of device numbers
 * @from: the first in the range of numbers to unregister
 * @count: the number of device numbers to unregister
 *
 * This function will unregister a range of @count device numbers,
 * starting with @from.  The caller should normally be the one who
 * allocated those numbers in the first place...
 */
void unregister_chrdev_region(dev_t from, unsigned count)

  3、生成设备节点:

  此时还没有生成设备节点,应用程序还无法操作设备;需要使用命令mknod在指定的路径下创建设备节点;

  mknod demo_dev c 245 0    // 在当前路径创建设备节点demo_dev

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>

static dev_t  devNum=0;
static     struct cdev* mydev;

static int myopen (struct inode *node, struct file *file)
{
    return 0;
}


static struct file_operations  fileops={
    .owner     = THIS_MODULE,
    .open     = myopen,
};


static int __init mdev_init(void)
{
    int ret=0;

    printk("Hello cdev\n");
    ret = alloc_chrdev_region(&devNum, 0, 1, "dev_demo");
    if(ret != 0){

        printk("chr dev region err\n");
        return ret;
    }
    printk("dev major:%d.\n", MAJOR(devNum));
    printk("dev manor:%d.\n", MINOR(devNum));

    mydev = cdev_alloc();
    if(mydev == NULL){
        printk("alloc cdev error\n");
    }
    cdev_init(mydev, &fileops);

    ret = cdev_add(mydev, devNum, 1);
    if(ret < 0 ){
        printk("dev add error\n");
    }
    
    return 0;
}
static void __exit mdev_exit(void)
{
    printk("Good bye\n");
    unregister_chrdev(MAJOR(devNum), "dev_demo");
    cdev_del(mydev);
}

module_init(mdev_init);
module_exit(mdev_exit);
MODULE_LICENSE("GPL");

   4、基于udev的文件系统(应用层的接口)创建设备节点

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/err.h>

#define COUNT 3
#define DEV_NAME "devDemo"

static dev_t  devNum=0;
static     struct cdev* mydev=NULL;
static struct class * dev_cls=NULL;
static struct device* devp=NULL;

static int ma=240;

static int myopen (struct inode *node, struct file *file)
{
    printk("--%s--%s--%d----\n", __FILE__,__func__, __LINE__);

    return 0;
}

static struct file_operations  fileops={
    .owner     = THIS_MODULE,
    .open     = myopen,
};

static int __init mdev_init(void)
{
    int ret=0,i;

    printk("Hello cdev\n");
    //ret = alloc_chrdev_region(&devNum, 0, 1, "dev_demo");
    devNum = MKDEV(ma,0);
    printk("dev major:%d.\n", MAJOR(devNum));
    printk("dev manor:%d.\n", MINOR(devNum));
    
    ret = register_chrdev_region(devNum, COUNT, DEV_NAME);
    if(ret != 0){

        printk("chr dev region err\n");
        goto err0;
    }
    // 1. 为cdev分配空间.
    mydev = cdev_alloc();
    if(mydev == NULL){
        printk(KERN_ERR "alloc cdev error\n");
        goto err1;
    }
    // 2. cdev初始化.
    cdev_init(mydev, &fileops);
    // 3. 添加cdev
    ret = cdev_add(mydev, devNum, COUNT);
    if(ret < 0 ){
        printk(KERN_ERR "dev add error\n");
        goto err1;
    }
    // 4. 在sys/class下创建目录
    dev_cls = class_create(THIS_MODULE, DEV_NAME);
    if(IS_ERR(dev_cls)){
        printk(KERN_ERR "class create failed:%ld\n", PTR_ERR(dev_cls));
        ret = PTR_ERR(dev_cls);
        goto err2;
    }
    // 5、创建设备结点,在dev目录下,创建名称为DEV_NAME+i的设备结点;
    for(i=0;i<3;i++){
        devp = device_create(dev_cls, NULL, MKDEV(ma,i), NULL, "%s%d", DEV_NAME,i);
        if(IS_ERR(devp)){
            printk(KERN_ERR "device create failed: %ld\n", PTR_ERR(devp));
            ret = PTR_ERR(devp);
            goto err3;
        }
    }
    printk(KERN_INFO "--%s--%s--%d----\n", __FILE__,__func__, __LINE__);
    
    return 0;
    
err3:
    for(--i;i>=0;i--){
        device_destroy(dev_cls,MKDEV(ma,i));
    }
    class_destroy(dev_cls);
err2:
    cdev_del(mydev);
    
err1:
    unregister_chrdev_region(devNum, COUNT);

err0:
    return 0;
}
static void __exit mdev_exit(void)
{
    int i=0;
    printk("Good bye\n");
    for(i=0;i<3;i++){
        device_destroy(dev_cls,MKDEV(ma,i));
    }
    class_destroy(dev_cls);
    cdev_del(mydev);
    unregister_chrdev_region(devNum, COUNT);
}

module_init(mdev_init);
module_exit(mdev_exit);
MODULE_LICENSE("GPL");

  1. 默认在/dev下根据device_create的名字创建设备节点;

hadlee@lbx-machine:~/mycode/mdriver$ ls /dev/devDemo* -l
crw------- 1 root root 240, 0  4月 30 15:10 /dev/devDemo0
crw------- 1 root root 240, 1  4月 30 15:10 /dev/devDemo1
crw------- 1 root root 240, 2  4月 30 15:10 /dev/devDemo2

   5、USER和KERNEL空间数据交换cop_to_user、copy_from_user

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/err.h>
#include <asm/uaccess.h>   // 适用于x86平台。
#include <asm-generic/uaccess.h>  // 使用ARM平台。


#define COUNT 3
#define DEV_NAME "devDemo"

static dev_t  devNum=0;
static     struct cdev* mydev=NULL;
static struct class * dev_cls=NULL;
static struct device* devp=NULL;

static char kbuf[128]={'\0'};
static int kbufcount=0;
static int ma=240;

static int myopen (struct inode *node, struct file *filp)
{
    printk(KERN_INFO "--%s--%s--%d----\n", __FILE__,__func__, __LINE__);

    return 0;
}
static ssize_t myread (struct file *filp, char __user *ubuf, size_t size, loff_t *off)
{
    printk(KERN_INFO "--%s--%s--%d----\n", __FILE__,__func__, __LINE__);
    //printk(KERN_INFO "buf addr:0x%x, szie:%d\n", ubuf, size);
    
    if(size > kbufcount)
        size = kbufcount;
    if(copy_to_user(ubuf, kbuf,13)){
        printk(KERN_INFO "copy to user failed\n");
        return -EAGAIN;
    }
    kbufcount =0;
    return 0;
}
static ssize_t mywrite (struct file *filp, const char __user * ubuf, size_t size, loff_t *off)
{
    //printk(KERN_INFO "buf addr:0x%x, szie:%d\n", ubuf, size);
    if(size > 128){
        size = 128;
    }
    if(copy_from_user(kbuf, ubuf, size)){
        printk(KERN_ERR "copy from user failed\n");
        return -EAGAIN;   // 拷贝失败应用层程序再尝试一次;
    }
    kbufcount = size;
    printk(KERN_INFO "write buf:%s\n", kbuf);
    printk(KERN_INFO "--%s--%s--%d----\n", __FILE__,__func__, __LINE__);
    
    return kbufcount;
}

static int myrelaese (struct inode *nod, struct file *file)
{
    printk(KERN_INFO "--%s--%s--%d----\n", __FILE__,__func__, __LINE__);
    return 0;
}

static struct file_operations  fileops={
    .owner     = THIS_MODULE,
    .open     = myopen,
    .release    = myrelaese,
    .read    = myread,
    .write    = mywrite,
    
};

static int __init mdev_init(void)
{
    int ret=0,i;

    printk("Hello cdev\n");
    //ret = alloc_chrdev_region(&devNum, 0, 1, "dev_demo");
    devNum = MKDEV(ma,0);
    printk("dev major:%d.\n", MAJOR(devNum));
    printk("dev manor:%d.\n", MINOR(devNum));
    
    ret = register_chrdev_region(devNum, COUNT, DEV_NAME);
    if(ret != 0){

        printk("chr dev region err\n");
        goto err0;
    }
    // 1. 为cdev分配空间.
    mydev = cdev_alloc();
    if(mydev == NULL){
        printk(KERN_ERR "alloc cdev error\n");
        goto err1;
    }
    // 2. cdev初始化.
    cdev_init(mydev, &fileops);
    // 3. 添加cdev
    ret = cdev_add(mydev, devNum, COUNT);
    if(ret < 0 ){
        printk(KERN_ERR "dev add error\n");
        goto err1;
    }
    // 4. 在sys/class下创建目录
    dev_cls = class_create(THIS_MODULE, DEV_NAME);
    if(IS_ERR(dev_cls)){
        printk(KERN_ERR "class create failed:%ld\n", PTR_ERR(dev_cls));
        ret = PTR_ERR(dev_cls);
        goto err2;
    }
    // 5、创建设备结点,在dev目录下,创建名称为DEV_NAME+i的设备结点;
    for(i=0;i<3;i++){
        devp = device_create(dev_cls, NULL, MKDEV(ma,i), NULL, "%s%d", DEV_NAME,i);
        if(IS_ERR(devp)){
            printk(KERN_ERR "device create failed: %ld\n", PTR_ERR(devp));
            ret = PTR_ERR(devp);
            goto err3;
        }
    }
    printk(KERN_INFO "--%s--%s--%d----\n", __FILE__,__func__, __LINE__);
    return 0;
    
err3:
    for(--i;i>=0;i--){
        device_destroy(dev_cls,MKDEV(ma,i));
    }
    class_destroy(dev_cls);
err2:
    cdev_del(mydev);
err1:
    unregister_chrdev_region(devNum, COUNT);
err0:
    return 0;
}
static void __exit mdev_exit(void)
{
    int i=0;
    printk("Good bye\n");
    for(i=0;i<3;i++){
        device_destroy(dev_cls,MKDEV(ma,i));
    }
    class_destroy(dev_cls);
    cdev_del(mydev);
    unregister_chrdev_region(devNum, COUNT);
}
module_init(mdev_init);
module_exit(mdev_exit);
MODULE_LICENSE("GPL");

  参考应用:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <asm/uaccess.h>


MODULE_LICENSE("GPL");
#define CDD_MAJOR    200//cat /proc/devices找一个尚未使用的
#define CDD_MINOR    0
#define CDD_COUNT    10
dev_t dev = 0;
u32 cdd_major = 0;
u32 cdd_minor = 0;

struct class *dev_class = NULL;
#define BUF_SIZE    100
struct cdd_cdev{
    struct cdev cdev;
    struct device *dev_device;
    u8 led;
    
    char kbuf[BUF_SIZE];
};

struct cdd_cdev *cdd_cdevp = NULL;

int cdd_open(struct inode* inode, struct file *filp)
{
    struct cdd_cdev *pcdevp = NULL;
    printk("enter cdd_open!\n");
    
    pcdevp = container_of(inode->i_cdev, struct cdd_cdev, cdev);
    printk("led = %d\n", pcdevp->led);
    
    filp->private_data = pcdevp;
    
    return 0;
}

int cdd_read(struct file *filp, char __user *buf, size_t count, loff_t *offset)
{
    int ret = 0;
    
    struct cdd_cdev *cdevp = filp->private_data;
    printk("enter cdd_read!\n");
    ret = copy_to_user(buf, cdevp->kbuf, count);
    printk("kernel kbuf content:%s\n", cdevp->kbuf);
    return ret;
}

int cdd_write(struct file *filp, const char __user *buf, size_t count, loff_t *offset)
{
    int ret = 0;
    struct cdd_cdev *cdevp = filp->private_data;    
    printk("enter cdd_write!\n");
    
    ret = copy_from_user(cdevp->kbuf, buf, count);
    
    return ret;
}

int cdd_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long data)
{
    printk("enter cdd_ioctl!\n");
    return 0;
}

int cdd_release(struct inode *inode, struct file *filp)
{
    printk("enter cdd_release!\n");
    return 0;
}

struct file_operations cdd_fops = {
    .owner = THIS_MODULE,
    .open = cdd_open,
    .read = cdd_read,
    .write = cdd_write,
    .ioctl = cdd_ioctl,
    .release = cdd_release,
    };

int __init cdd_init(void)
{
    int ret = 0;
    int i = 0;
    
    if(cdd_major){
        dev = MKDEV(CDD_MAJOR, CDD_MINOR);//生成设备号
        //注册设备号;1、要注册的起始设备号2、连续注册的设备号个数3、名字
        ret = register_chrdev_region(dev, CDD_COUNT, "cdd_demo");
    }else{
        // 动态分配设备号
        ret = alloc_chrdev_region(&dev, cdd_minor, CDD_COUNT, "cdd_demo02");
    }
    
    if(ret < 0){
        printk("register_chrdev_region failed!\n");
        goto failure_register_chrdev;
    }
    //获取主设备号
    cdd_major = MAJOR(dev);
    printk("cdd_major = %d\n", cdd_major);
    
    cdd_cdevp = kzalloc(sizeof(struct cdd_cdev)*CDD_COUNT, GFP_KERNEL);
    if(IS_ERR(cdd_cdevp)){
        printk("kzalloc failed!\n");
        goto failure_kzalloc;
    }
    /*创建设备类*/
    dev_class = class_create(THIS_MODULE, "cdd_class");
    if(IS_ERR(dev_class)){
        printk("class_create failed!\n");
        goto failure_dev_class;
    }
    for(i=0; i<CDD_COUNT; i++){
        /*初始化cdev*/
        cdev_init(&(cdd_cdevp[i].cdev), &cdd_fops);
        /*添加cdev到内核*/
        cdev_add(&(cdd_cdevp[i].cdev), dev+i, 1);
        /* “/dev/xxx” */
        device_create(dev_class, NULL, dev+i, NULL, "cdd%d", i);
        
        cdd_cdevp[i].led = i;
        
    }
    
    return 0;
failure_dev_class:
    kfree(cdd_cdevp);
failure_kzalloc:
    unregister_chrdev_region(dev, CDD_COUNT);
failure_register_chrdev:
    return ret;
}

void __exit cdd_exit(void)
{
/*逆序消除*/
    int i = 0;
    for(; i < CDD_COUNT; i++){
        device_destroy(dev_class, dev+i);
        cdev_del(&(cdd_cdevp[i].cdev));
        //cdev_del(&((cdd_cdevp+i)->cdev));
    }
    class_destroy(dev_class);
    kfree(cdd_cdevp);
    unregister_chrdev_region(dev, CDD_COUNT);
    
}    

module_init(cdd_init);
module_exit(cdd_exit);
View Code

 6、ioctl接口传参数:

  内核中有两个ioctl接口,具体使用那个和内核版本有关系吧,不确定;

long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
long (*compat_ioctl) (struct file *, unsigned int, unsigned long);

  应用层:

int ioctl(int d, int request, ...);

  @fd: 文件描述符;

  @request: 命令码;

  第三个参数一般用于,传递数据结构的地址,根据地址配合copy_from_user将结构中的数据内容拷贝过来;完成数据传输;

// 自定义传输的数据结构
struct KData{
    int id;
    char buf[64];
};
static struct KData myData={0};  // 声明变量
// 一般User通过结构体的形式,将结构体的地址传过来args_addr,然后再copy_form_user将数据拷贝过来;
static long myioctl (struct file *filp, unsigned int req_code, unsigned long args_addr)   
{
    switch(req_code){
        case 11:
            printk(KERN_INFO "----------case11------------\n");
            if(copy_from_user((void*)&myData, (void*)args_addr, sizeof(struct KData))){
                printk(KERN_INFO "copy data failed\n");
                return -EAGAIN;
            }
            printk(KERN_INFO "id:%d..buf:%s\n", myData.id,myData.buf);
            break;
        case 22:
            printk(KERN_INFO "----------case22------------\n");
            if(copy_from_user((void*)&myData, (void*)args_addr, sizeof(struct KData))){
                printk(KERN_INFO "copy data failed\n");
                return -EAGAIN;
            }
            printk(KERN_INFO "id:%d..buf:%s\n", myData.id,myData.buf);
            break;
        default:
            break;
    
    }
    printk(KERN_INFO "--%s--%s--%d----\n", __FILE__,__func__, __LINE__);
    return 0;
}

  在ioctl接口中:命令码cmd一般充分使用所有bits 

  long (*unlocked_ioctl) (struct file *, unsigned int cmd, unsigned long);
  对于cmd中的参数,充分使用所有bit,使用命令码生成参数,内核提供了相应的bit操作:
  _IOC(dir,type,nr,size): 32bits由dir(读写)、type(数据类型)、nr(序号)、size(大小)组成;

  #define _IO(type,nr) _IOC(_IOC_NONE,(type),(nr),0)
  #define _IOR(type,nr,size) _IOC(_IOC_READ,(type),(nr),sizeof(size))
  #define _IOW(type,nr,size) _IOC(_IOC_WRITE,(type),(nr),sizeof(size))
  #define _IOWR(type,nr,size) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),sizeof(size))

  比如:
    cmd1 = _IO('A',1);

  取出响应的bit:
    _IOC_DIR(nr)
    _IOC_TYPE(nr)
    _IOC_NR(nr)
    _IOC_SIZE(nr)

 

 

 







posted @ 2020-07-05 12:20  笑不出花的旦旦  阅读(423)  评论(0)    收藏  举报