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.pswp.cn/news/279645.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/279645.shtml
英文地址,請注明出處:http://en.pswp.cn/news/279645.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

monogdb操作system.*權限

mongodb roles system.roles集合刪不掉 當你自定義了特權(角色): db.createRole({role: "dropSystemViewsAnyDatabase",privileges: [{actions: [ "dropCollection" ],resource: { db: "", collection: "system.roles" }}],roles: []}…

如何發現假庫存照片(并將合適的人歸于屬性)

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;以至于我們有時不得不在自動化用…

C6748_UART(5) - UART寄存器

1、FIFO控制寄存器&#xff08;FCR&#xff09;RXFIFTL&#xff1a;接收FIFO中斷觸發(當FIFO中的數據量剛到達所要求&#xff08;trigger level&#xff09;的時候會產生中斷);DMAMODE1:如果FIFO使能的話此位可以使能DMA模式。TXCLR&#xff1a;發送FIFO清除。RXCLR&#xff1a…

如何在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(嵌套)查詢。 二、普通對象與嵌套對象的索引…

寫給深圳首期Python自動化開發周未班的信

你是否做了正確的決定&#xff1f; 深圳首期周未班的同學們大家好&#xff0c;我是Alex, 老男孩教育的聯合創始人&#xff0c;Python項目的發起人&#xff0c;51CTO學院連續2屆最受學員喜愛的講師&#xff0c;中國最早一批使用Python的程序員&#xff0c;當然還有一堆頭銜&…

網站跳出率的相關要點介紹

今天小峰seo博客和大家一起來探討關于“網站跳出率的相關要點”&#xff0c;這里大體是分為三大要點&#xff1a;首先是進入的流量渠道&#xff0c;然后就是綜合流量速度和內容的質量問題&#xff0c;細的來說就是我們的網站進來的用戶是搜索什么關鍵詞來的是通過百度還是搜狗或…

如何使用PowerShell提升開發效率(以Windows Embedded CE為例)

簡介 本文講述如何使用Powershell通過RAPI來控制Windows Embedded CE和Windows Mobile設備。 緣由 我入行的時候是做AS400 RPG和UNIX C開發的&#xff0c;所有開發環境都是字符界面&#xff0c;因此習慣了vigrepmake的開發模式。后來開始做Windows的開發&#xff0c;開始也不大…

視頻圖像傳輸學習筆記-基礎小知識(一)

攝像頭DVP與MIPI區別 DVP是并口&#xff0c;需要PCLK、VSYNC、HSYNC、D[0&#xff1a;11]——可以是8/10/12bit數據&#xff0c;看ISP或baseband是否支持&#xff1b;總線PCLK極限大約在96M左右&#xff0c;而且走線長度不能過長&#xff0c;所有DVP最大速率最好控制在72M以…

java程序員面試交流項目經驗

粘貼自&#xff1a;https://blog.csdn.net/wangyuxuan_java/article/details/8778211 1&#xff1a;請你介紹一下你自己 這是面試官常問的問題。一般人回答這個問題過于平常&#xff0c;只說姓名、愛好、工作經驗&#xff0c;這些簡歷上都有。其實&#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. 我們一直被告知備份數據是一個…

洛谷 P1736 創意吃魚法(多維DP)

題目描述 回到家中的貓貓把三桶魚全部轉移到了她那長方形大池子中&#xff0c;然后開始思考&#xff1a;到底要以何種方法吃魚呢&#xff08;貓貓就是這么可愛&#xff0c;吃魚也要想好吃法 ^_*&#xff09;。她發現&#xff0c;把大池子視為01矩陣&#xff08;0表示對應位置無…

計算機組裝和維護_如何構建自己的計算機,第二部分:組裝在一起

計算機組裝和維護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…

Python學習-集合的常見用法

st [1,2,3,4,5] ct [2,3,4,5,76] list set(["name", list, try]) list2 set(["name", list, try, but, test]) # 兩個列表去重&#xff0c;利用集合st set(st) #設為集合 ct set(ct) print(st, type(st))sct0 st.union(ct) #并集 sct st | ct …

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…