4412 字符类设备的设备号

一、静态申请字符类设备号

  • 字符类设备函数在文件"include/linux/fs.h"中
  • 内核提供了三个函数来注册一组字符设备编号,这三个函数分别是
    • register_chrdev_region()
    • alloc_chrdev_region()
    • register_chrdev()
  • register_chrdev_region()是提前知道设备的主次设备号,再去申请设备号
  • alloc_chrdev_region()是动态分配主次设备号
  • register_chrdev() 是老版本的设备号注册方式,只分配主设备号。从设备号在mknod的时候指定。

 

  • 宏定义MKDEV的头文件"include/linux/kdev.h"
    • 在kdev_t.h头文件中有一个系列设备号处理的宏命令,用于处理各种设备号相关的数据。本期视频只介绍MKDEV,后面使用了其他宏定义再介绍
  • include/linux/cdev.h
    • cdev类型是字符设备描述的结构
    • 其次的设备号必须用"dev_t"类型来描述,高12位为主设备号,低20位为此设备号

 

编写编译运行

  • 将视频"16_驱动模块传参数"中的文件"module_param.c"改成为"request_cdev_num.c",静态生成设备号
  • 编写,编译
  • 加载运行
    • 使用命令"cat /proc/devices"查看已经被注册的主设备,设备号9没有被注册
    • insmod /mnt/udisk/request_cdev_num.ko numdev_major=9 numdev_minor=0
    • 使用命令"cat /proc/devices"查看,设备号9被注册为scdev
    • rmmod request_cdev_num numdev_major=9 numdev_minor=0
#include <linux/init.h>
#include <linux/module.h>/* define module_param module_param_array header file */
#include <linux/moduleparam.h>
/* define perm's head file*/
#include <linux/stat.h>
/* char device register head file */
#include <linux/fs.h>
/* MKDEV change device ID type */
#include <linux/kdev_t.h>
/* define char device struct */
#include <linux/cdev.h>#define DEVICE_NAME "scdev"
#define DEVICE_MINOR_NUM 2
#define DEV_MAJOR 0
#define DEV_MINOR 0MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("TOPEET");int numdev_major = DEV_MAJOR;
int numdev_minor = DEV_MINOR;/* input major device ID */
module_param(numdev_major, int, S_IRUSR);
/* input minor device ID */
module_param(numdev_minor, int, S_IRUSR);static int hello_init(void)
{int ret;dev_t num_dev;printk(KERN_EMERG "numdev_major is %d!\n", numdev_major);printk(KERN_EMERG "numdev_minor is %d!\n", numdev_minor);if(numdev_major) {num_dev = MKDEV(numdev_major, numdev_minor);ret = register_chrdev_region(num_dev, DEVICE_MINOR_NUM, DEVICE_NAME);} else {printk(KERN_EMERG "numdev_major %d is failed\n", numdev_major);}if(ret < 0) {printk(KERN_EMERG "register_chrdev_region req %d is failed\n", numdev_major);unregister_chrdev_region(num_dev, DEVICE_MINOR_NUM);return ret;}printk(KERN_EMERG "Hello World enter!\n");return 0;
}static void hello_exit(void)
{dev_t num_dev = MKDEV(numdev_major, numdev_minor);unregister_chrdev_region(num_dev, DEVICE_MINOR_NUM);printk(KERN_EMERG "Hello World exit!\n");
}module_init(hello_init);
module_exit(hello_exit);
register_chrdev_region

测试结果:

[root@iTOP-4412]# insmod request_cdev_num.ko numdev_major=9 numdev_minor=0                                 
[  135.652085] numdev_major is 9!
[  135.653710] numdev_minor is 0!
[  135.656810] Hello World enter!
[root@iTOP-4412]# cat /proc/devices                                                                        
Character devices:1 mem4 ttyS5 /dev/tty5 /dev/console5 /dev/ptmx9 scdev10 misc13 input21 sg29 fb81 video4linux89 i2c
108 ppp
116 alsa
128 ptm
136 pts
153 rc522_test
166 ttyACM
180 usb
188 ttyUSB
189 usb_device
204 ttySAC
216 rfcomm
243 ump
244 mali
249 mt3326-gps
250 roccat
251 BaseRemoteCtl
252 media
253 ttyGS
254 rtcBlock devices:1 ramdisk
259 blkext7 loop8 sd65 sd66 sd67 sd68 sd69 sd70 sd71 sd
128 sd
129 sd
130 sd
131 sd
132 sd
133 sd
134 sd
135 sd
179 mmc
254 device-mapper
[root@iTOP-4412]# rmmod request_cdev_num numdev_major=9 numdev_minor=0                                     
[  152.245805] Hello World exit!
[root@iTOP-4412]# cat /proc/devices                                                                        
Character devices:1 mem4 ttyS5 /dev/tty5 /dev/console5 /dev/ptmx10 misc13 input21 sg29 fb81 video4linux89 i2c
108 ppp
116 alsa
128 ptm
136 pts
153 rc522_test
166 ttyACM
180 usb
188 ttyUSB
189 usb_device
204 ttySAC
216 rfcomm
243 ump
244 mali
249 mt3326-gps
250 roccat
251 BaseRemoteCtl
252 media
253 ttyGS
254 rtcBlock devices:1 ramdisk
259 blkext7 loop8 sd65 sd66 sd67 sd68 sd69 sd70 sd71 sd
128 sd
129 sd
130 sd
131 sd
132 sd
133 sd
134 sd
135 sd
179 mmc
254 device-mapper
[root@iTOP-4412]
测试结果

 

二、动态申请字符类设备号

  • 字符设备函数在文件"include/linux/fs.h"中
  • alloc_chrdev_region()是动态分配主次设备号
  • 宏定义MAJOR提取dev_t数据中的主设备号

编写编译运行

  • 将视频"17"中的文件"request_cdev_num.c改写为"request_ascdev_num.c"动态生成字符设备号
  • 编写,编译
  • 加载运行
    • 使用命令"cat /proc/device"查看
    • 动态加载模块之后再查看设备号

 修改后的代码:

#include <linux/init.h>
#include <linux/module.h>/* define module_param module_param_array header file */
#include <linux/moduleparam.h>
/* define perm's head file*/
#include <linux/stat.h>
/* char device register head file */
#include <linux/fs.h>
/* MKDEV change device ID type */
#include <linux/kdev_t.h>
/* define char device struct */
#include <linux/cdev.h>#define DEVICE_NAME "scdev"
#define DEVICE_MINOR_NUM 2
#define DEV_MAJOR 0
#define DEV_MINOR 0MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("TOPEET");int numdev_major = DEV_MAJOR;
int numdev_minor = DEV_MINOR;/* input major device ID */
module_param(numdev_major, int, S_IRUSR);
/* input minor device ID */
module_param(numdev_minor, int, S_IRUSR);static int hello_init(void)
{int ret;dev_t num_dev;printk(KERN_EMERG "numdev_major is %d!\n", numdev_major);printk(KERN_EMERG "numdev_minor is %d!\n", numdev_minor);if(numdev_major) {num_dev = MKDEV(numdev_major, numdev_minor);ret = alloc_chrdev_region(&num_dev, numdev_minor, DEVICE_MINOR_NUM, DEVICE_NAME);} else {ret = alloc_chrdev_region(&num_dev, numdev_minor, DEVICE_MINOR_NUM, DEVICE_NAME);numdev_major = MAJOR(num_dev);printk(KERN_EMERG "register req major number is %d\n", numdev_major);}if(ret < 0) {printk(KERN_EMERG "register_chrdev_region req %d is failed\n", numdev_major);unregister_chrdev_region(num_dev, DEVICE_MINOR_NUM);return ret;}printk(KERN_EMERG "Hello World enter!\n");return 0;
}static void hello_exit(void)
{dev_t num_dev = MKDEV(numdev_major, numdev_minor);unregister_chrdev_region(num_dev, DEVICE_MINOR_NUM);printk(KERN_EMERG "Hello World exit!\n");
}module_init(hello_init);
module_exit(hello_exit);
alloc_chrdev_region代码

测试运行后:

[root@iTOP-4412]# insmod request_ascdev_num.ko                                                             
[ 1335.710205] numdev_major is 0!
[ 1335.711817] numdev_minor is 0!
[ 1335.714861] register req major number is 248
[ 1335.727258] Hello World enter!
[root@iTOP-4412]# cat /proc/                                                                               
1/               670/             bus/             mfc/
11907/           714/             cgroups          misc
12924/           726/             cmdline          modules
12925/           731/             consoles         mounts
12926/           734/             cpu/             net/
16/              744/             cpuinfo          pagetypeinfo
2/               745/             crypto           panic_info_dump
3/               763/             devices          partitions
339/             767/             diskstats        sched_debug
341/             838/             driver/          scsi/
343/             866/             execdomains      self/
354/             892/             fb               softirqs
365/             894/             filesystems      stat
376/             896/             fs/              sys/
429/             898/             interrupts       sysrq-trigger
445/             900/             iomem            sysvipc/
455/             911/             ioports          timer_list
456/             918/             irq/             tty/
5/               919/             kallsyms         uid_stat/
508/             9192/            key-users        uptime
522/             940/             kmsg             version
528/             943/             kpagecount       vmallocinfo
6/               956/             kpageflags       vmstat
6437/            994/             loadavg          wakelocks
657/             asound/          locks            zoneinfo
662/             buddyinfo        meminfo
[root@iTOP-4412]# cat /proc/devices                                                                        
Character devices:1 mem4 ttyS5 /dev/tty5 /dev/console5 /dev/ptmx10 misc13 input21 sg29 fb81 video4linux89 i2c
108 ppp
116 alsa
128 ptm
136 pts
153 rc522_test
166 ttyACM
180 usb
188 ttyUSB
189 usb_device
204 ttySAC
216 rfcomm
243 ump
244 mali
248 scdev
249 mt3326-gps
250 roccat
251 BaseRemoteCtl
252 media
253 ttyGS
254 rtc
测试

 

三、注册字符类设备

  • 分配内存空间函数kmalloc
    • 分配连续的虚拟地址,用于小内存分配。在include/linux/slab.h文件中
    • 参数1:申请的内存大小(最大128K)
    • 参数2:GFP_KERNEL,代表优先权,内存不够可以延迟分配
  • 清空内存空间的数据函数memset
    • 可以清空内存空间,也就是全部写为0
    • 参数1:内存地址
    • 参数2:0
    • 参数3:内存长度
  • 字符设备初始化函数cdev_init
    • 在头文件include/linux/cdev.h中
    • 参数1:cdev字符设备文件结构体
    • 参数2:file_operations结构体
    • 注册设备本质是向linux设备文件中添加数据,这些数据需要初始化
  • 字符设备注册函数cdev_add
    • 在头文件include/linux/cdev.h中
    • 参数1:cdev字符设备文件结构体
    • 参数2:设备号
    • 参数3:设备范围大小
    • 向系统注册设备,也就是向linux系统添加数据
  • 卸载设备函数cdev_del
    • 参数1:cdev结构体
    • 移除字符设备
  • 将"18_动态申请字符类设备号"中的文件"request_ascdev_num.c"改写为"register_cdev.c"
  • 编译
  • 测试
    • 通过加载模块后的打印信息,可以观察到驱动加载的过程以及注册设备的反馈信息

 代码:

#include <linux/init.h>
#include <linux/module.h>/* define module_param module_param_array header file */
#include <linux/moduleparam.h>
/* define perm's head file*/
#include <linux/stat.h>
/* char device register head file */
#include <linux/fs.h>
/* MKDEV change device ID type */
#include <linux/kdev_t.h>
/* define char device struct */
#include <linux/cdev.h>
/* define memroy sapce */
#include <linux/slab.h>#define DEVICE_NAME "scdev"
#define DEVICE_MINOR_NUM 2
#define DEV_MAJOR 0
#define DEV_MINOR 0
#define REGDEV_SIZE 3000MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("TOPEET");int numdev_major = DEV_MAJOR;
int numdev_minor = DEV_MINOR;/* input major device ID */
module_param(numdev_major, int, S_IRUSR);
/* input minor device ID */
module_param(numdev_minor, int, S_IRUSR);struct reg_dev {char *data;unsigned long size;struct cdev cdev;
};
struct reg_dev *my_devices;struct file_operations my_fops = {.owner = THIS_MODULE,
};static void reg_init_cdev(struct reg_dev *dev, int index)
{int err;int devno = MKDEV(numdev_major, numdev_minor+index);cdev_init(&dev->cdev, &my_fops);dev->cdev.owner = THIS_MODULE;dev->cdev.ops = &my_fops;err = cdev_add(&dev->cdev, devno, 1);if(err) {printk(KERN_EMERG "cdev_add %d is fail! %d\n", index, err);} else {printk(KERN_EMERG "cdev_add %d is success!\n", index);}
}static int hello_init(void)
{int ret, i;dev_t num_dev;printk(KERN_EMERG "numdev_major is %d!\n", numdev_major);printk(KERN_EMERG "numdev_minor is %d!\n", numdev_minor);if(numdev_major) {num_dev = MKDEV(numdev_major, numdev_minor);ret = alloc_chrdev_region(&num_dev, numdev_minor, DEVICE_MINOR_NUM, DEVICE_NAME);} else {ret = alloc_chrdev_region(&num_dev, numdev_minor, DEVICE_MINOR_NUM, DEVICE_NAME);numdev_major = MAJOR(num_dev);printk(KERN_EMERG "register req major number is %d\n", numdev_major);}if(ret < 0) {printk(KERN_EMERG "register_chrdev_region req %d is failed\n", numdev_major);unregister_chrdev_region(num_dev, DEVICE_MINOR_NUM);return ret;}my_devices = kmalloc(DEVICE_MINOR_NUM*sizeof(struct reg_dev), GFP_KERNEL);if(!my_devices) {ret = -ENOMEM;printk(KERN_EMERG "kamlloc fialed!\n");goto fail;}memset(my_devices, 0, DEVICE_MINOR_NUM*sizeof(struct reg_dev));for(i=0;i<DEVICE_MINOR_NUM;i++) {my_devices[i].data = kmalloc(REGDEV_SIZE, GFP_KERNEL);memset(my_devices[i].data, 0, REGDEV_SIZE);          /* data address */reg_init_cdev(&my_devices[i], i);}printk(KERN_EMERG "Hello World enter!\n");return 0;fail:unregister_chrdev_region(num_dev, DEVICE_MINOR_NUM);return ret;
}static void hello_exit(void)
{int i;dev_t num_dev = MKDEV(numdev_major, numdev_minor);printk(KERN_EMERG "Hello World exit!\n");for(i=0;i<DEVICE_MINOR_NUM;i++) {cdev_del(&my_devices[i].cdev);}unregister_chrdev_region(num_dev, DEVICE_MINOR_NUM);
}module_init(hello_init);
module_exit(hello_exit);
代码

测试结果:

[root@iTOP-4412]# insmod register_cdev.ko                                                                  
[ 1065.781646] numdev_major is 0!
[ 1065.783265] numdev_minor is 0!
[ 1065.786377] register req major number is 248
[ 1065.790637] cdev_add 0 is success!
[ 1065.793943] cdev_add 1 is success!
[ 1065.797392] Hello World enter!
[root@iTOP-4412]# rmmod register_cdev                                                                      
[ 1082.904729] Hello World exit
测试结果

 

四、生成字符类设备节点

前面介绍设备中的模型:bus,device,driver,都是有比较明显确定的定义。bus代表总线,device代表实际的设备和接口,driver代表驱动。

class是设备类,它是一个抽象的概念,没有对应的实体。它是提供给用户接口相似的一类设备的集合。常见的输入子系统input、usb、串口tty‘块设备block等。

  • 函数class_create创建class类文件
    • 参数1:一般是THIS_MODULE
    • 参数2:设备名称
    • 创建一个设备类,用于设备节点文件的创建
    • 返回一个class结构体变量
  • class结构体变量
    • class是设备驱动模型中通用的设备结构
    • 在头文件include/linux/device.h的280行
  • 老版本:创建设备class函数class_device_create
    • 头文件include/linux/device.h中
    • 参数1:class结构体变量
    • 参数2:父设备NULL
    • 参数3:dev_t设备号
    • 参数4:数据NULL
    • 参数5:设备节点名称
  • 释放设备class函数class_destroy
    • 参数1:myclass
  • 创建设备节点函数device_create
    • 头文件include/linux/device.h中
    • 参数1:设备诶所属于的类
    • 参数2:设备的浮设备,NULL
    • 参数3:设备号
    • 参数4:设备数据,NULL
    • 参数5:设备名称
  • 摧毁设备节点函数device_destroy
    • 参数1:设备所属于的类
    • 参数2:设备号
  • 释放内存函数kfree
    • 参数1:数据指针

 编写编译运行测试

  • 将"19注册字符类设备"中的"register_cdev.c"文件为"create_cnode.c"
  • 编译
  • 加载模块"create_cnode.ko"
    • 使用命令"ls /sys/class/"可以查看到生成的class
    • 使用命令"ls /dev"可以查看到生成的两个设备节点
  • 加载模块的时候还可以使用命令生成设备节点命令,列如
    • mknod dev/test0 c 249 0
    • mknod dev/test1 c 249 1

 运行代码:

#include <linux/init.h>
#include <linux/module.h>/* define module_param module_param_array header file */
#include <linux/moduleparam.h>
/* define perm's head file*/
#include <linux/stat.h>
/* char device register head file */
#include <linux/fs.h>
/* MKDEV change device ID type */
#include <linux/kdev_t.h>
/* define char device struct */
#include <linux/cdev.h>
/* define memroy sapce */
#include <linux/slab.h>/* include device_create class file */
#include <linux/device.h>#define DEVICE_NAME "chardevnode"
#define DEVICE_MINOR_NUM 2
#define DEV_MAJOR 0
#define DEV_MINOR 0
#define REGDEV_SIZE 3000MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("TOPEET");int numdev_major = DEV_MAJOR;
int numdev_minor = DEV_MINOR;/* input major device ID */
module_param(numdev_major, int, S_IRUSR);
/* input minor device ID */
module_param(numdev_minor, int, S_IRUSR);static struct class *my_class;struct reg_dev {char *data;unsigned long size;struct cdev cdev;
};
struct reg_dev *my_devices;struct file_operations my_fops = {.owner = THIS_MODULE,
};static void reg_init_cdev(struct reg_dev *dev, int index)
{int err;int devno = MKDEV(numdev_major, numdev_minor+index);cdev_init(&dev->cdev, &my_fops);dev->cdev.owner = THIS_MODULE;dev->cdev.ops = &my_fops;err = cdev_add(&dev->cdev, devno, 1);if(err) {printk(KERN_EMERG "cdev_add %d is fail! %d\n", index, err);} else {printk(KERN_EMERG "cdev_add %d is success!\n", (numdev_minor+index));}
}static int hello_init(void)
{int ret, i;dev_t num_dev;printk(KERN_EMERG "numdev_major is %d!\n", numdev_major);printk(KERN_EMERG "numdev_minor is %d!\n", numdev_minor);if(numdev_major) {num_dev = MKDEV(numdev_major, numdev_minor);ret = alloc_chrdev_region(&num_dev, numdev_minor, DEVICE_MINOR_NUM, DEVICE_NAME);} else {ret = alloc_chrdev_region(&num_dev, numdev_minor, DEVICE_MINOR_NUM, DEVICE_NAME);numdev_major = MAJOR(num_dev);printk(KERN_EMERG "register req major number is %d\n", numdev_major);}if(ret < 0) {printk(KERN_EMERG "register_chrdev_region req %d is failed\n", numdev_major);unregister_chrdev_region(num_dev, DEVICE_MINOR_NUM);return ret;}my_class = class_create(THIS_MODULE, DEVICE_NAME);my_devices = kmalloc(DEVICE_MINOR_NUM*sizeof(struct reg_dev), GFP_KERNEL);if(!my_devices) {ret = -ENOMEM;printk(KERN_EMERG "kmalloc fialed!\n");goto fail;}memset(my_devices, 0, DEVICE_MINOR_NUM*sizeof(struct reg_dev));for(i=0;i<DEVICE_MINOR_NUM;i++) {my_devices[i].data = kmalloc(REGDEV_SIZE, GFP_KERNEL);memset(my_devices[i].data, 0, REGDEV_SIZE);          /* data address *//* register device to system */reg_init_cdev(&my_devices[i], i);/* create device node */device_create(my_class, NULL, MKDEV(numdev_major, numdev_minor+i), "NULL", DEVICE_NAME"%d", i);}printk(KERN_EMERG "Hello World enter!\n");return 0;fail:unregister_chrdev_region(num_dev, DEVICE_MINOR_NUM);return ret;
}static void hello_exit(void)
{int i;dev_t num_dev = MKDEV(numdev_major, numdev_minor);printk(KERN_EMERG "Hello World exit!\n");for(i=0;i<DEVICE_MINOR_NUM;i++) {cdev_del(&my_devices[i].cdev);/* release memory*/device_destroy(my_class, MKDEV(numdev_major, numdev_minor+i));}/* release my class*/class_destroy(my_class);/* release kfre */kfree(my_devices);unregister_chrdev_region(num_dev, DEVICE_MINOR_NUM);
}module_init(hello_init);
module_exit(hello_exit);
class_create

测试结果:

[root@iTOP-4412]# insmod create_cnode.ko                                                                   
[12828.419006] numdev_major is 0!
[12828.420699] numdev_minor is 0!
[12828.423674] register req major number is 248
[12828.429116] cdev_add 0 is success!
[12828.432882] cdev_add 1 is success!
[12828.436403] Hello World enter![root@iTOP-4412]# cat /proc/devices                                                                        
Character devices:1 mem4 ttyS5 /dev/tty5 /dev/console5 /dev/ptmx10 misc13 input21 sg29 fb81 video4linux89 i2c
108 ppp
116 alsa
128 ptm
136 pts
153 rc522_test
166 ttyACM
180 usb
188 ttyUSB
189 usb_device
204 ttySAC
216 rfcomm
243 ump
244 mali
248 chardevnode
249 mt3326-gps
250 roccat
251 BaseRemoteCtl
252 media
253 ttyGS
254 rtc[root@iTOP-4412]# rmmod create_cnode                                                                       
[12439.491484] Hello World exit!
测试结果

 

五、字符驱动

  • file_operations中的函数比较多,选取用的比较多的函数简单介绍,后面的驱动教程中调用了对应的函数,再详细介绍
  • int (*open)(struct inode *, struct file *)
    • 打开函数
  • int (*release)(struct inode *, struct file *)
    • 释放close函数
  • long (*unlocked_ioctl)(struct file *, unsigned int, unsigned long)
    • io控制函数
  • ssize_t (*read)(struct file *, char __user *, size_t, loff_t *)
    • 读函数
  • ssize_t (*write)(struct file *, const char __user *, size_t, loff_t *)
    • 写函数
  • loff_t (*llseek)(struct file *, loff_t, int)
    • 定位函数
  • 如果需要不同的设备节点有不同的功能,只需要在注册设备的时候添加不同的file_operations结构体即可
  • 编写编译运行测试
  • 将驱动视频教程20中的"create_cnode.c"改为“char_driver.c”
  • 修改编译文件Makefile
  • 将驱动视频教程09中"invoke_hello.c"改为"invoke_char_driver.c",编译应用命令如下
    • arm-none-linux-gnueabi-gcc -o invoke_char_driver invoke_char_driver.c -static

 编写代码:

#include <linux/init.h>
#include <linux/module.h>/* define module_param module_param_array header file */
#include <linux/moduleparam.h>
/* define perm's head file*/
#include <linux/stat.h>
/* char device register head file */
#include <linux/fs.h>
/* MKDEV change device ID type */
#include <linux/kdev_t.h>
/* define char device struct */
#include <linux/cdev.h>
/* define memroy sapce */
#include <linux/slab.h>/* include device_create class file */
#include <linux/device.h>#define DEVICE_NAME "chardevnode"
#define DEVICE_MINOR_NUM 2
#define DEV_MAJOR 0
#define DEV_MINOR 0
#define REGDEV_SIZE 3000MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("TOPEET");int numdev_major = DEV_MAJOR;
int numdev_minor = DEV_MINOR;/* input major device ID */
module_param(numdev_major, int, S_IRUSR);
/* input minor device ID */
module_param(numdev_minor, int, S_IRUSR);static struct class *my_class;struct reg_dev {char *data;unsigned long size;struct cdev cdev;
};
struct reg_dev *my_devices;/* open */
static int chardevnode_open(struct inode *inode, struct file *file)
{printk(KERN_EMERG "chardevnode open is success!\n");return 0;
}/* close */
static int chardevnode_release(struct inode *indoe, struct file *file)
{printk(KERN_EMERG "chardevnode release is success!\n");return 0;
}/* io control */
static long chardevnode_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{printk(KERN_EMERG "chardevnode release is success!cmd is %d,arg is %d\n", cmd, arg);return 0;
}/* read */
static ssize_t chardevnode_read(struct file *file, char __user *buf, size_t size, loff_t *f_ops)
{return 0;
}/* write */
static ssize_t chardevnode_write(struct file *file, const char __user *buf, size_t size, loff_t *ops)
{return 0;
}/* lseek */
static loff_t chardevnode_llseek(struct file *file, loff_t offset, int whence)
{return 0;
}struct file_operations my_fops = {.owner = THIS_MODULE,.open  = chardevnode_open,.release = chardevnode_release,.unlocked_ioctl = chardevnode_ioctl,.read = chardevnode_read,.write = chardevnode_write,.llseek = chardevnode_llseek,
};static void reg_init_cdev(struct reg_dev *dev, int index)
{int err;int devno = MKDEV(numdev_major, numdev_minor+index);cdev_init(&dev->cdev, &my_fops);dev->cdev.owner = THIS_MODULE;dev->cdev.ops = &my_fops;err = cdev_add(&dev->cdev, devno, 1);if(err) {printk(KERN_EMERG "cdev_add %d is fail! %d\n", index, err);} else {printk(KERN_EMERG "cdev_add %d is success!\n", (numdev_minor+index));}
}static int hello_init(void)
{int ret, i;dev_t num_dev;printk(KERN_EMERG "numdev_major is %d!\n", numdev_major);printk(KERN_EMERG "numdev_minor is %d!\n", numdev_minor);if(numdev_major) {num_dev = MKDEV(numdev_major, numdev_minor);ret = alloc_chrdev_region(&num_dev, numdev_minor, DEVICE_MINOR_NUM, DEVICE_NAME);} else {ret = alloc_chrdev_region(&num_dev, numdev_minor, DEVICE_MINOR_NUM, DEVICE_NAME);numdev_major = MAJOR(num_dev);printk(KERN_EMERG "register req major number is %d\n", numdev_major);}if(ret < 0) {printk(KERN_EMERG "register_chrdev_region req %d is failed\n", numdev_major);unregister_chrdev_region(num_dev, DEVICE_MINOR_NUM);return ret;}my_class = class_create(THIS_MODULE, DEVICE_NAME);my_devices = kmalloc(DEVICE_MINOR_NUM*sizeof(struct reg_dev), GFP_KERNEL);if(!my_devices) {ret = -ENOMEM;printk(KERN_EMERG "kmalloc fialed!\n");goto fail;}memset(my_devices, 0, DEVICE_MINOR_NUM*sizeof(struct reg_dev));for(i=0;i<DEVICE_MINOR_NUM;i++) {my_devices[i].data = kmalloc(REGDEV_SIZE, GFP_KERNEL);memset(my_devices[i].data, 0, REGDEV_SIZE);          /* data address *//* register device to system */reg_init_cdev(&my_devices[i], i);/* create device node */device_create(my_class, NULL, MKDEV(numdev_major, numdev_minor+i), "NULL", DEVICE_NAME"%d", i);}printk(KERN_EMERG "Hello World enter!\n");return 0;fail:unregister_chrdev_region(num_dev, DEVICE_MINOR_NUM);return ret;
}static void hello_exit(void)
{int i;dev_t num_dev = MKDEV(numdev_major, numdev_minor);printk(KERN_EMERG "Hello World exit!\n");for(i=0;i<DEVICE_MINOR_NUM;i++) {cdev_del(&my_devices[i].cdev);/* release memory*/device_destroy(my_class, MKDEV(numdev_major, numdev_minor+i));}/* release my class*/class_destroy(my_class);/* release kfre */kfree(my_devices);unregister_chrdev_region(num_dev, DEVICE_MINOR_NUM);
}module_init(hello_init);
module_exit(hello_exit);
代码

测试结果:

[root@iTOP-4412]# insmod char_driver.ko                                                                    
[ 2135.474056] numdev_major is 0!
[ 2135.475744] numdev_minor is 0!
[ 2135.478724] register req major number is 248
[ 2135.484220] cdev_add 0 is success!
[ 2135.487909] cdev_add 1 is success!
[ 2135.500246] Hello World enter!
[root@iTOP-4412]# ./invok_char                                                                             
[ 2174.171640] chardevnode open is success!
[ 2174.174452] chardevnode release is success!cmd is 1,arg is 6
[ 2174.179828] chardevnode open is success!
[ 2174.183737] chardevnode release is success!cmd is 1,arg is 6
[ 2174.189346] chardevnode release is success!
[ 2174.193511] chardevnode release is success!
APP open /dev/chardevnode0 success
APP open /dev/chardevnode0 success
[root@iTOP-4412]# rmmod char_driver                                                                        
[ 2199.758801] Hello World exit!
测试结果

 

六、字符类GPIOS,LED驱动编写

  • 将"21_字符驱动"中的文件“char_driver.c”改为“char_driver_leds.c”,添加gpio的初始化和操作函数,卸载模块的时候释放GPIO,将宏定义部分添加到头文件中,简单修改Makefile文件
  • 将“21_字符驱动”中的文件"incoke_char_driver.c"改为文件"invoke_char_gpios.c",并使用main参数传参数操作gpio
  • 应用编译命令
    • arm-none-linux-gnueabi-gcc -o invoke_char_gpios invoke_char_gpios.c -static
  • 操作命令
    • 类似"./invoke_char_gpios 0 1",参数1为命令,参数2为GPIO

字符驱动程序:

#include <linux/init.h>
#include <linux/module.h>/* define module_param module_param_array header file */
#include <linux/moduleparam.h>
/* define perm's head file*/
#include <linux/stat.h>
/* char device register head file */
#include <linux/fs.h>
/* MKDEV change device ID type */
#include <linux/kdev_t.h>
/* define char device struct */
#include <linux/cdev.h>
/* define memroy sapce */
#include <linux/slab.h>/* include device_create class file */
#include <linux/device.h>#include <linux/gpio.h>
#include <plat/gpio-cfg.h>
#include <mach/gpio.h>
#include <mach/gpio-exynos4.h>#define DEVICE_NAME "chardevnode"
#define DEVICE_MINOR_NUM 2
#define DEV_MAJOR 0
#define DEV_MINOR 0
#define REGDEV_SIZE 3000MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("TOPEET");int numdev_major = DEV_MAJOR;
int numdev_minor = DEV_MINOR;/* input major device ID */
module_param(numdev_major, int, S_IRUSR);
/* input minor device ID */
module_param(numdev_minor, int, S_IRUSR);static struct class *my_class;struct reg_dev {char *data;unsigned long size;struct cdev cdev;
};
struct reg_dev *my_devices;/* open */
static int chardevnode_open(struct inode *inode, struct file *file)
{printk(KERN_EMERG "chardevnode open is success!\n");return 0;
}/* close */
static int chardevnode_release(struct inode *indoe, struct file *file)
{printk(KERN_EMERG "chardevnode release is success!\n");return 0;
}/* io control */
static long chardevnode_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{printk(KERN_EMERG "chardevnode release is success!cmd is %d,arg is %d\n", cmd, arg);if(cmd >1 || arg> 1) {printk(KERN_EMERG "cmd and arg is 0 or 1");return 0;}switch(arg) {case 0:gpio_set_value(EXYNOS4_GPL2(0), cmd);break;case 1:gpio_set_value(EXYNOS4_GPK1(1), cmd);break;default:printk(KERN_EMERG "cmd and arg is 0 or 1");break;}return 0;
}/* read */
static ssize_t chardevnode_read(struct file *file, char __user *buf, size_t size, loff_t *f_ops)
{return 0;
}/* write */
static ssize_t chardevnode_write(struct file *file, const char __user *buf, size_t size, loff_t *ops)
{return 0;
}/* lseek */
static loff_t chardevnode_llseek(struct file *file, loff_t offset, int whence)
{return 0;
}struct file_operations my_fops = {.owner = THIS_MODULE,.open  = chardevnode_open,.release = chardevnode_release,.unlocked_ioctl = chardevnode_ioctl,.read = chardevnode_read,.write = chardevnode_write,.llseek = chardevnode_llseek,
};/* GPL2_0 */
static int led1_init(void)
{int ret;printk(KERN_EMERG "Gpio led 1 init\n");ret = gpio_request(EXYNOS4_GPL2(0), "LEDS");if(ret < 0) {printk(KERN_EMERG "gpio_request EXYNOS4_GPL2(0) failed\n");return ret;}s3c_gpio_cfgpin(EXYNOS4_GPL2(0), S3C_GPIO_OUTPUT);gpio_set_value(EXYNOS4_GPL2(0), 0);return 0; 
}/* GPK1_1 */
static int led2_init(void)
{int ret;printk(KERN_EMERG "GPIO led 2 init\n");ret = gpio_request(EXYNOS4_GPK1(1), "LEDS2");if(ret < 0) {printk(KERN_EMERG "gpio_request EXYNOS4_GPK1(1) fialed\n");return ret;}s3c_gpio_cfgpin(EXYNOS4_GPK1(1), S3C_GPIO_OUTPUT);gpio_set_value(EXYNOS4_GPK1(1), 0);return 0;
}static void reg_init_cdev(struct reg_dev *dev, int index)
{int err;int devno = MKDEV(numdev_major, numdev_minor+index);cdev_init(&dev->cdev, &my_fops);dev->cdev.owner = THIS_MODULE;dev->cdev.ops = &my_fops;err = cdev_add(&dev->cdev, devno, 1);if(err) {printk(KERN_EMERG "cdev_add %d is fail! %d\n", index, err);} else {printk(KERN_EMERG "cdev_add %d is success!\n", (numdev_minor+index));}
}static int hello_init(void)
{int ret, i;dev_t num_dev;printk(KERN_EMERG "numdev_major is %d!\n", numdev_major);printk(KERN_EMERG "numdev_minor is %d!\n", numdev_minor);if(numdev_major) {num_dev = MKDEV(numdev_major, numdev_minor);ret = alloc_chrdev_region(&num_dev, numdev_minor, DEVICE_MINOR_NUM, DEVICE_NAME);} else {ret = alloc_chrdev_region(&num_dev, numdev_minor, DEVICE_MINOR_NUM, DEVICE_NAME);numdev_major = MAJOR(num_dev);printk(KERN_EMERG "register req major number is %d\n", numdev_major);}if(ret < 0) {printk(KERN_EMERG "register_chrdev_region req %d is failed\n", numdev_major);unregister_chrdev_region(num_dev, DEVICE_MINOR_NUM);return ret;}my_class = class_create(THIS_MODULE, DEVICE_NAME);    my_devices = kmalloc(DEVICE_MINOR_NUM*sizeof(struct reg_dev), GFP_KERNEL);if(!my_devices) {ret = -ENOMEM;printk(KERN_EMERG "kmalloc fialed!\n");goto fail;}memset(my_devices, 0, DEVICE_MINOR_NUM*sizeof(struct reg_dev));for(i=0;i<DEVICE_MINOR_NUM;i++) {my_devices[i].data = kmalloc(REGDEV_SIZE, GFP_KERNEL);        memset(my_devices[i].data, 0, REGDEV_SIZE);        /* data address *//* register device to system */reg_init_cdev(&my_devices[i], i);/* create device node */device_create(my_class, NULL, MKDEV(numdev_major, numdev_minor+i), "NULL", DEVICE_NAME"%d", i);}led1_init();led2_init();printk(KERN_EMERG "Hello World enter!\n");return 0;fail:unregister_chrdev_region(num_dev, DEVICE_MINOR_NUM);return ret;
}static void hello_exit(void)
{int i;dev_t num_dev = MKDEV(numdev_major, numdev_minor);printk(KERN_EMERG "Hello World exit!\n");for(i=0;i<DEVICE_MINOR_NUM;i++) {cdev_del(&my_devices[i].cdev);/* release memory*/device_destroy(my_class, MKDEV(numdev_major, numdev_minor+i));}/* release my class*/class_destroy(my_class);/* release kfre */kfree(my_devices);unregister_chrdev_region(num_dev, DEVICE_MINOR_NUM);
}module_init(hello_init);
module_exit(hello_exit);
char_driver_leds.c

然后是应用程序:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <stdlib.h>int main(int argc, char *argv[])
{int fd0, fd1;char *hello_node0 = "/dev/chardevnode0";char *hello_node1 = "/dev/chardevnode1";if(argc > 2) {printf("please input cmd and arg\n");}/* O_RDWR只读打开, O_NDELAY非阻塞方式 */fd0 = open(hello_node0, O_RDWR|O_NDELAY);if(fd0 < 0) {printf("APP open %s failed\n", hello_node0);exit(EXIT_FAILURE);} else {printf("APP open %s success\n", hello_node0);ioctl(fd0, atoi(argv[1]), atoi(argv[2]));}/* O_RDWR只读打开, O_NDELAY非阻塞方式 */
/*fd1 = open(hello_node0, O_RDWR|O_NDELAY);if(fd1 < 0) {printf("APP open %s failed\n", hello_node0);exit(EXIT_FAILURE);} else {printf("APP open %s success\n", hello_node0);ioctl(fd1, 1, 6);}
*/close(fd0);close(fd1);
}
invoke_char_driver.c

然后是makefile:

TARGET_NAME = char_driver_leds
APP_NAME = invoke_char_gpios
obj-m += $(TARGET_NAME).oKDIR := /home/topeet/chen/kernel-3.0/iTop4412_Kernel_3.0PWD ?= $(shell pwd)all:appmake -C $(KDIR) M=$(PWD) modulesapp:$(APP_NAME)arm-none-linux-gnueabi-gcc $(APP_NAME).c -o $(APP_NAME) -staticclean:rm -rf *.o *.ko *.mod.c *.symvers *.order *.cmd .$(TARGET_NAME)* $(APP_NAME)
Makefile

测试结果:

[root@iTOP-4412]# insmod char_driver_leds.ko                                                               
[  420.107938] numdev_major is 0!
[  420.109549] numdev_minor is 0!
[  420.112677] register req major number is 248
[  420.125765] cdev_add 0 is success!
[  420.137424] cdev_add 1 is success!
[  420.148881] Gpio led 1 init
[  420.150342] gpio_request EXYNOS4_GPL2(0) failed
[  420.154743] GPIO led 2 init
[  420.165167] Hello World enter!
[root@iTOP-4412]# ./invoke_char_gpios 1 0                                                                  
please input cmd [  431.050669] chardevnode open is success!
[  431.054691] chardevnode release is success!cmd is 1,arg is 0
[  431.060238] chardevnode release is success!
and arg
APP open /dev/chardevnode0 success
[root@iTOP-4412]# ./invoke_char_gpios 1 1                                                                  
please input cmd [  435.289936] chardevnode open is success!
[  435.294047] chardevnode release is success!cmd is 1,arg is 1
[  435.299498] chardevnode release is success!
and arg
APP open /dev/chardevnode0 success
[root@iTOP-4412]# ./invoke_char_gpios 0 0                                                                  
please input cmd [  440.595232] chardevnode open is success!
[  440.599237] chardevnode release is success!cmd is 0,arg is 0
and arg
APP open /dev/chardevnode0 success
[  440.609648] chardevnode release is success!
[root@iTOP-4412]# ./invoke_char_gpios 0 1                                                                  
please input cmd [  443.313565] chardevnode open is success!
[  443.317679] chardevnode release is success!cmd is 0,arg is 1
[  443.323129] chardevnode release is success!
and arg
APP open /dev/chardevnode0 success[root@iTOP-4412]# rmmod char_driver_leds                                                                   
[  468.722834] Hello World exit!
测试结果

 

不知道为什么要设两个驱动设备,按我的写法应该,这两个驱动设备没什么区别。

转载于:https://www.cnblogs.com/ch122633/p/9459904.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/279645.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何发现假库存照片(并将合适的人归于属性)

Spammers and other unscrupulous advertisers are always looking for new ways to get you click on their pages. One of the latest tactics is to steal popular and useful stock images—like the kind you sometimes see in news articles—and re-upload them elsewhe…

Mysql Hunter

一、简介自动化实施的过程中&#xff0c;我们通常都面临一个棘手的问题&#xff1a;数据的准备和恢复。即在成功执行一个自动化用例时&#xff0c;我们可能需要一定的数据前提&#xff0c;而为了使得整个前提不至于被其他的用例破坏&#xff0c;以至于我们有时不得不在自动化用…

如何在Windows 10上限制Wi​​ndows Update的下载带宽

Windows 10’s Fall Creators Update gives you more control of Windows Update’s downloads and uploads. You can now set a download bandwidth limit, ensuring Windows Update won’t hog your Internet connection with its background downloads. Windows 10的Fall Cr…

Elasticsearch嵌套查询

2019独角兽企业重金招聘Python工程师标准>>> 一、背景 最近在做基于宴会厅档期的商户搜索推荐时&#xff0c;如果用传统平铺式的mapping结构&#xff0c;无法满足需求场景&#xff0c;于是用到了Elasticsearch支持的Nested(嵌套)查询。 二、普通对象与嵌套对象的索引…

如何使用PowerShell提升开发效率(以Windows Embedded CE为例)

简介 本文讲述如何使用Powershell通过RAPI来控制Windows Embedded CE和Windows Mobile设备。 缘由 我入行的时候是做AS400 RPG和UNIX C开发的&#xff0c;所有开发环境都是字符界面&#xff0c;因此习惯了vigrepmake的开发模式。后来开始做Windows的开发&#xff0c;开始也不大…

Windows7旗舰版磁盘分区详解—附分区步骤截图

最近工作中配置使用联想的Thinkpad TL系列本本.当然原装的系统时刚发布的Windows RTM旗舰版.在考虑买之前也参考了戴尔 苹果的等等, 但个人私下也是一直在用Tinkpad系列, 相比其他的品牌本人还是比较钟情于Tinkpad 非常实用的键盘. 以及简洁的外观.买回来一看这个TL系列原装的系…

outlook存档邮件_如何在Outlook 2013中存档电子邮件

outlook存档邮件We’ve always been told that backing up our data is a good idea. Well, that same concept can extend to email as well. You may want to archive your email every so often, such as monthly, quarterly, or even yearly. 我们一直被告知备份数据是一个…

计算机组装和维护_如何构建自己的计算机,第二部分:组装在一起

计算机组装和维护So you’ve selected your parts, double- and triple-checked their compatibility, and waited for economy shipping to bring them all to your door. It’s time to get to the fun part: putting them all together. 因此&#xff0c;您已经选择了零件&a…

Autofac之自动装配

从容器中的可用服务中选择一个构造函数来创造对象&#xff0c;这个过程叫做自动装配。这个过程是通过反射实现的 默认 思考这么一个问题,如果注册类型中存在多个构造函数,那么Autofac会选择哪一个来创建类型的实例 答案是"尽可能最多参数" class ConstructorClass {p…

对Emlog 6.0 Beta的完整代码审计过程

Emlog 6.0 beta版本&#xff0c;这可能是最后一篇关于PHP语言CMS的代码审计文章&#xff0c;此次将详细记录完整的审计过程。 文章基本上完整记录小东的对此CMS审计过程&#xff0c;或许显得繁琐&#xff0c;但代码审计的过程就是这样&#xff0c;发现可能项&#xff0c;然后精…

SINOCES 2011

突然发现又好久没写过日志了 是在是太懒了… 难得休假去看了眼消费电子 感觉实在是一年不如一年 佳能、索尼不见踪影&#xff0c;相机满场没见一家&#xff08;大牌子是真没见到&#xff09; 华硕技嘉微星等主板厂商同样失踪… PC方面&#xff0c;联想貌似是来卖电脑包鼠标的&a…

esim卡与ms卡的区别_什么是eSIM,它与SIM卡有何不同?

esim卡与ms卡的区别With the launch of the Apple Watch 3, the term “eSIM” has been thrown around a lot. And now, Google’s Pixel 2 is the first phone to use this new technology, it’s time we take a closer look at what it is, what it does, and what this me…

机器学习实战之logistic回归分类

利用logistic回归进行分类的主要思想&#xff1a;根据现有数据对分类边界建立回归公式&#xff0c;并以此进行分类。 logistic优缺点&#xff1a; 优点&#xff1a;计算代价不高&#xff0c;易于理解和实现。缺点&#xff1a;容易欠拟合&#xff0c;分类精度可能不高。 .适用数…

HDU 6343.Problem L. Graph Theory Homework-数学 (2018 Multi-University Training Contest 4 1012)

6343.Problem L. Graph Theory Homework 官方题解: 一篇写的很好的博客: HDU 6343 - Problem L. Graph Theory Homework - [(伪装成图论题的)简单数学题] 代码: 1 //1012-6343-数学2 #include<iostream>3 #include<cstdio>4 #include<cstring>5 #include<…

Android GridView LruCache

照片墙这种功能现在应该算是挺常见了&#xff0c;在很多应用中你都可以经常看到照片墙的身影。它的设计思路其实也非常简单&#xff0c;用一个GridView控件当作“墙”&#xff0c;然后随着GridView的滚动将一张张照片贴在“墙”上&#xff0c;这些照片可以是手机本地中存储的&a…

如何在Android TV上自定义推荐行

When you fire up Android TV, the first thing you see is a list of movies and shows the system thinks you’ll like. It’s often full of the latest flicks or hottest news, but sometimes it could just be things relevant to your interests and the apps you have…

steam串流到手机_如何从手机将Steam游戏下载到PC

steam串流到手机Steam allows you to remotely install games from your smartphone, just like you can with a PlayStation 4 or Xbox One. You can download games to your gaming PC from anywhere, ensuring those big downloads are complete and the game is ready to p…

禁用windows10更新_如何在Windows 10中禁用投影

禁用windows10更新The drop shadows on applications in the Windows 10 preview are really big and suspiciously similar to the ones in OS X, and if they aren’t your speed, you can easily remove them. We actually think they look good, but since somebody out th…

如何访问 Service?- 每天5分钟玩转 Docker 容器技术(99)

前面我们已经学习了如何部署 service&#xff0c;也验证了 swarm 的 failover 特性。不过截止到现在&#xff0c;有一个重要问题还没有涉及&#xff1a;如何访问 service&#xff1f;这就是本节要讨论的问题。 为了便于分析&#xff0c;我们重新部署 web_server。 ① docker se…

Linux配置手册(二)配置DHCP服务器

1.检查是否安装DHCP服务器软件 2.挂在RHEL5系统光盘 3.安装DHCP服务软件 4.将模板配置文件复制并覆盖现在的配置文件 5.配置修改dhcpd.conf文件 配置信息 默认租约时间 default-lease-time 最大租约时间 max-lease-time 局域网内所有主机的域名 option domain-name 客户机所使用…