第一个字符设备驱动

来源:互联网 发布:sql数据库紧急模式 编辑:程序博客网 时间:2024/05/17 08:03

转载请注明出处:http://blog.csdn.net/ruoyunliufeng/article/details/45054183


         linux驱动分为字符设备、块设备驱动、网络驱动三种,其中以字符驱动最为简单。说起要写驱动自然想到从字符设备驱动写起。看了开发板官方的驱动代码,对新手来说简直是噩梦。新手来说要看懂,实在不容易。其中包含了很多知识和设计思想。所以我想还是尽可能从易到难来写这个系列,相信我,我会努力把我知道的都给大家讲清楚。

一、驱动代码

/*  * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. *   */#include <linux/kernel.h>#include <linux/module.h>#include <linux/miscdevice.h>#include <linux/fs.h>#include <linux/types.h>#include <linux/moduleparam.h>#include <linux/slab.h>#include <linux/ioctl.h>#include <linux/cdev.h>#include <linux/delay.h>#include <linux/device.h>  #define HELLO_CNT   1       //所请求连续设备编号个数static int hello_major = 0; //默认内核自动分配static int hello_minor = 0; //默认此设备号从0开始static struct cdev hello_cdev;static struct class *hello_class;static int hello_open(struct inode *inode, struct file *file){printk("hello open\n");return 0;}static struct file_operations hello_fops = {.owner = THIS_MODULE,.open  = hello_open,};static int hello_char_init(void){dev_t devid;int err,result;/*1.分配主设备号*/if(hello_major){devid = MKDEV(hello_major, hello_minor);result = register_chrdev_region(devid,HELLO_CNT,"hello");}else{result = alloc_chrdev_region(&devid,hello_minor,HELLO_CNT,"hello");hello_major = MAJOR(devid);}if(result < 0){printk(KERN_WARNING"hello:can't get major %d\n", hello_major);return result;}/*2.注册字符设备驱动*/cdev_init(&hello_cdev, &hello_fops);err = cdev_add(&hello_cdev, devid, HELLO_CNT);if(err)printk(KERN_WARNING"Error %d adding hello",err);/*3.创建设备*/hello_class = class_create(THIS_MODULE, "hello");if (IS_ERR(hello_class)) {printk(KERN_WARNING "class_create() failed for hello_class\n");}device_create(hello_class, NULL, MKDEV(hello_major, 0), NULL, "hello0"); /* /dev/hello0 */return 0;}static void hello_char_exit(void){device_destroy(hello_class, MKDEV(hello_major, 0));class_destroy(hello_class);cdev_del(&hello_cdev);unregister_chrdev_region(MKDEV(hello_major, 0), HELLO_CNT);}module_init(hello_char_init);module_exit(hello_char_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR("ruoyunliufeng");


二、测试程序

#include <stdio.h>#include <stdlib.h>#include <string.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>/*  * hello_test /dev/hello0 */void print_usage(char *file){printf("%s <dev>\n", file);}int main(int argc, char **argv){int fd;if (argc != 2){print_usage(argv[0]);return 0;}fd = open(argv[1], O_RDWR);if (fd < 0)printf("can't open %s\n", argv[1]);elseprintf("can open %s\n", argv[1]);return 0;}


三、驱动讲解

1.驱动的入口与出口

          在用C写的应用程序中,我们程序的入口是main()函数。驱动程序也有入口,那就是module_init();括号里面的函数就是入口函数,有了入口自然就有出口module_exit();每当insmod XXX.ko的时候驱动就会进入入口函数。入口函数主要做一些初始化的工作。rmmod  XXX.ko的时候,驱动就会调用出口函数,出口函数组要做一些注销和清理工作。


2.字符驱动初始化

          字符设备初始化的框架大体就是这样,可以直接当做模板来用,分三步:

           1.分配诸设备号

                     主设备号可以自己定义,也可以交给内核帮你分配,但一般都推荐内核分配,所以这里用了一个if语句来分别处理这两种情况。

           2.注册字符驱动

                     你的驱动要让内核知道就必须注册呀。调用cdev_init();cdev_add()两个函数

           3.创建设备结点

                     这里选择自动创建的方式,你也完全可以在驱动中不写,然后自己去手动创建。首先class_create()然后device_create()。这样在/dev/下就能出现你的设备了。这样就可以对你的设备进行一系列的读写等操作了。

3.如何调用驱动

          应用程序调用open函数,通过你在驱动中写的file_operations hello_fops去调用其中的hello_open。这样应用和驱动就建立了联系,其他函数也是一样。其余的实现也是一样,比如需要写入数据,就在hello_fops中加入写函数。驱动提供的是机制,不提供策略。策略是应用程序该干的事。“需要提供什么功能”即机制,“如何使用这些功能”即策略。每个函数都有它的功能,这里要注意不要乱写。否则你的驱动程序看上去就像一坨翔。


注:这里我并没有详细的去分析每个函数的参数,意义。我觉得这些东西完全可以自己去内核中看,我不想把我的文章变成翻译内核注释,文档。我希望看我的文章你能领会大概的框架,具体细节自己去看内核吧。源码之前,了无秘密。


参考:ldd3

0 0
原创粉丝点击