|
[记录]ARM,Linux,搭建一个字符设备驱动程序的框架(慢慢改进)
[复制链接]
本帖最后由 ywlzh 于 2016-7-23 21:41 编辑
在我看来,Linux内核开发,有两步,第一步,不管是字符设备驱动程序,还是块设备驱动程序,还是网络设备驱动程序,都需要一个空的框架,第二步,在这个空的框架下再填充对硬件的控制。然后编译,下载到板子上运行。
这里有一篇讲内核开发入门编写hello world 模块,我觉得很好的文章,值得一看,由简入深。
http://mp.weixin.qq.com/s?__biz= ... 85559127&scene=0#rd
先写一个最简单的字符设备驱动:
- #include <linux/module.h>
- #include <linux/kernel.h>
- #include <linux/fs.h>
- #include <linux/init.h>
- #include <linux/delay.h>
- #include <asm/uaccess.h>
- #include <asm/irq.h>
- #include <asm/io.h>
- #include <linux/cdev.h>
- #include <linux/device.h>
- #include <linux/slab.h>
- #include <asm/uaccess.h>
- #define DEVICE_NAME "kong" /* 加载模式后,执行”cat /proc/devices”命令看到的设备名称 */
- #define kong_MAJOR 250 /* 主设备号 */
- static int kong_open(struct inode *inode, struct file *file)
- {
- printk("open ok\n");
- return 0;
- }
- static kong_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
- {
- printk("write ok\n");
- char val;
- copy_from_user(&val,buf,1);
- printk("write data:%d \n",val);
- return 0;
-
- }
- static struct file_operations kong_fops = {
- .owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
- .open = kong_open,
- .write = kong_write,
- };
- static int __init kong_init(void)
- {
- int ret;
- ret = register_chrdev(kong_MAJOR, DEVICE_NAME, &kong_fops);
- if (ret < 0) {
- printk(" can't register major number\n");
- return ret;
- }
- printk("register sucess /dev/kong OK!\n");
- printk(DEVICE_NAME " initialized\n");
- return 0;
- }
- static void __exit kong_exit(void)
- {
- unregister_chrdev(kong_MAJOR, DEVICE_NAME);
-
- printk("rmmod OK!\n");
- }
- module_init(kong_init);
- module_exit(kong_exit);
- MODULE_LICENSE("GPL");
复制代码 可以看到,这个代码中,入口函数就这个register_chrdev函数,这个函数做了什么?注册了设备号250,这个设备号是通过终端cat /proc/devices 发现250号没用用到,于是就用上了,提交了一个结构体kong_fops,这里只对open,write,做了处理,后续可以根据情况添加。将文件传到虚拟机Linux系统里,make,在板子上insmod
再写一个测试.c文件
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <fcntl.h>
- #include <stdio.h>
- int main(int argc, char **argv)
- {
- int fd;
- char val;
- val = 13;
- fd=open("/dev/xxx",O_RDWR);
- if (fd < 0)
- {
- printf("error, can't open \n");
- return 0;
- }
- write(fd, &val, 1);
- printf("write :%d \n",val);
- return 0;
-
- }
复制代码 如果注意到了,会发现驱动文件是注册的"/dev/kong" ,而测试文件是打开的“/dev/xxx”,这能打开吗,答案是能,为什么?因为......太多的解释,我也不清楚,我就知道内核之所以能找上这个驱动因为主设备号,来看结果,我们为/dev/xxx创建一个设备节点,驱动的主设备号是250,那就创建250号,好让测试文件可以打开。在板子终端输入 mknod /dev/xxx c 250 0
运行测试文件,需要先交叉编译
开始改进:
每次驱动程序写完,都需要自己看下哪个这设备号没有用到,然后在选择没有用到的设备号,需要改进为动态注册设备号,让系统自动分配,写完后还需要手动创建设备节点,需要改进为让系统自己去为这个驱动创建设备节点,这样应用层就更好的测试和开发了
上面的驱动程序不变,刚才需要改进的地方都是可以在入口函数做的
- static int __init kong_init(void)
- {
- int ret;
- ret = register_chrdev(kong_MAJOR, DEVICE_NAME, &kong_fops);
- if (ret < 0) {
- printk(" can't register major number\n");
- return ret;
- }
- printk("register sucess /dev/kong OK!\n");
- kong_class = class_create(THIS_MODULE, "kong");
- if (IS_ERR(kong_class))
- return PTR_ERR(kong_class);
-
- kong_class_devs = device_create(kong_class, NULL, MKDEV(kong_MAJOR, 0), NULL, "kong");
- if (unlikely(IS_ERR(kong_class_devs)))
- return PTR_ERR(kong_class_devs);
-
- printk(DEVICE_NAME " initialized\n");
- return 0;
- }
复制代码 整个函数先不要被if语句给看误导了,就三个函数
register_chrdev
class_create
device_create
既然入口函数变了,出口也要相应的变
- static void __exit kong_exit(void)
- {
- unregister_chrdev(kong_MAJOR, DEVICE_NAME);
-
- device_unregister(kong_class_devs);
-
- class_destroy(kong_class);
-
- printk("rmmod OK!\n");
- }
复制代码 这样,测试函数就不要写open "/dev/xxx",可以直接“dev/kong”,系统可以直接找到这个驱动的
还可以改进
要是注册没有成功怎么办?创建设备节点没有成功怎么办?怎么申请?多个功能一样的硬件比如GPIO一类,串口一类,是不是对应每一个
GPIO都要写一个驱动程序,肯定不是,可以在一个驱动设备程序里写上,怎么区分?用次设备号,次设备号多了需不需要申请内存?失败了怎么办?
这里的改进,就不一一细说了,还是分享网上别人写的挺好的文章。
当中有完整的字符设备驱动程序
http://blog.chinaunix.net/uid-26833883-id-4371047.html
|
|