一、靜態申請字符類設備號
- 字符類設備函數在文件"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);
測試結果:


[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);
測試運行后:


[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);
測試結果:


[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);
然后是應用程序:


#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); }
然后是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)
測試結果:


[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!
?
不知道為什么要設兩個驅動設備,按我的寫法應該,這兩個驅動設備沒什么區別。