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

  • 字符类设备函数在文件"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

4412 字符类设备的设备号相关推荐

  1. Linux驱动(14)--字符类设备与驱动

    字符类设备 1. 静态申请字符类设备号 1.1 所需函数与头文件 1.2 源码与注释 1.3 运行结果 2. 动态申请字符类设备号 2.1 所需函数与头文件 2.2 源码与注释 2.3 运行结果 3. ...

  2. 十五、linux 注册字符类设备和生成节点

    一. 注册字符类设备 • 分配内存空间函数kmalloc         – 分配连续的虚拟地址,用于小内存分配.在include/linux/slab.h文件中.         – 参数1:申请的 ...

  3. Linux驱动字符设备(设备号的申请)

    在了解Linux字符设备先了解一下Linux设备的分类. Linux设备分类 Linux设备主要分为字符设备.块设备.网络设备. 字符设备:能够像字节流一样被访问且没有缓冲是按顺序访问的设备,当对字符 ...

  4. Linux:驱动之自动创建字符设备的设备文件(未完)

    自动创建字符设备的设备文件 目前尚不是最终版本,还望有心人自己学习的时候,把自己整合的知识点相关的答案也好问题也好,或者实践过程中的一些操作截图,再或者其他的一些想要分享材料发给笔者邮箱:uestc_ ...

  5. stm32f103gd32的usb虚拟串口,打印类printer组合设备

    stm32f103&gd32的usb虚拟串口+打印类printer组合设备@TOC stm32f103&gd32的usb虚拟串口,打印类printer组合设备 由于gd32和stm32 ...

  6. I/O输入输出——I/O设备(块设备与字符设备)

    无论是块设备还是字符设备,都是属于IO硬件,在整个硬件到软件层之前的了解,有助于我们理解. I/O设备 块设备 是一种具有一定结构的随机存取设备,对这种设备的读写是按块进行的,他使用缓冲区来存放暂时的 ...

  7. Linux设备模型——设备驱动模型和sysfs文件系统解读笔记

    Linux设备模型--设备驱动模型和sysfs文件系统解读笔记 原文:https://blog.csdn.net/yj4231/article/details/7799245 将对Linux系统中的s ...

  8. os I/O设备和设备控制器

    I/O 设备一般是由执行 I/O 操作的机械部分和执行控制 I/O 的电子部件组成.通常将这两部分分开,执行 I/O 操作的机械部分就是一般的 I/O 设备,而执行控制 I/O 的电子部件则称为设备控 ...

  9. 设备、设备节点和设备驱动详解

    Linux下的设备通常分为三类,字符设备,块设备和网络设备. 设备驱动程序也分为对应的三类:字符设备驱动程序.块设备驱动程序和网络设备驱动程序. 常见的字符设备有鼠标.键盘.串口.控制台等. 常见的块 ...

最新文章

  1. 主板电源开关接口图解_电脑主板开机电路检修步骤及思路。
  2. 和jwt_秒懂 JWT
  3. 科目三中模拟灯光使用考试常见的错误 广州学车网光大国际驾校学车
  4. RCC 2017 Qual 1 Mail.Ru, April 2, 2017 Problem B. Painting the Wall
  5. 百度AI快车道深圳专场,揭秘CV目标检测核心技术
  6. laravel5.5 尝试使用laravel安装器安装(失败) 最后还是用的composer。。。
  7. [转]何为C10K问题
  8. 微信小程序电商实战-首页(下)
  9. 太阳影子定位问题研究
  10. 文件监控(二) 代码
  11. 学法语的你伤不起之吐槽各种语言
  12. 安全问题的思考---君子不立于危墙之下
  13. Linux/Centos nethogs 按进程监控网络带宽
  14. 利用HTML+CSS进行页面布局(div的浮动效果)
  15. GetTickCount 得到时间进行比较计算遇到的异常
  16. idea 全局搜索不到,原来是你的原因
  17. vue 实现简单的车牌键盘 包括新能源 移动端 电脑端都可以用哦!(使用了elementui) 复制下来直接可以用v-model接收到输入的值
  18. 2、Go自动化测试入门-testify
  19. 笔记本连接投影仪全屏调试
  20. 一文搞懂SQL中的各种联结——内联结、自然联结、自联结、交叉联结

热门文章

  1. 操作系统以什么方式组织用户使用计算机,操作系统习题
  2. php中改变函数路由,php – 如何修改codeigniter中的路由
  3. [Err] ORA-00979: not a GROUP BY expression
  4. IntelliJ IDEA中无法加载jar包导致出现“cannot resolve symbol...”问题的解决
  5. docker为什么比虚拟机快
  6. matlab序列谱分析,基于MATLAB序列谱分析及FFT实现快速卷积.pdf
  7. 程序语言基础:解释程序基本原理笔记
  8. 收集19个前端开发人员的必备工具
  9. layuiajax提交表单控制层代码_漏洞预警|ThinkPHP 5.0 远程代码执行
  10. 流体式布局与响应式布局_将固定像素设计转换为流体比例布局