实例浅议linux设备驱动程序的编写

来源:互联网 发布:淘宝商品违反广告法 编辑:程序博客网 时间:2024/06/03 18:13

        我是2012年2月份在亚马逊买了《Linux设备驱动程序》一书,期间断断续续的读了好几次,前几章都读烂了,最后终于在去年完整的读完了一遍,期间的感受就是难,主要难在对于一个初学者,不是那么容易去实践,可能也是由于linux kernel更新的太快了的缘故,有些差异还是很大的,另外书中的例子对于初学者也是稍显晦涩和小复杂,在理解上要下点功夫和耐心才行,并不是说不好,相反,一直觉得需要动脑才能理解的东西往往是最有价值的,否则只是在别人的思考中洋洋自得。

        读完全书以后,回想学习的过程,感觉耗时挺漫长的,也在这本经典著作上受益匪浅,一开始觉得晦涩难懂的知识,后来也渐渐的明朗起来,可能学习就是这样,一开始的时候,你没有踏遍这块知识版图,就像是一头扎进了森林,但等你在里面摸爬滚打,吃尽了苦头,心里有了路,以后便越来越好走。

        在这里总结一下走过的路,权当画一个粗糙的地图,希望能给后来人些许方便。

        先介绍下我的环境,用的ubuntu发行版,kernel版本是3.8.0,《Linux设备驱动程序》一书是针对2.6.0的kernel,在这个例子中会看到一些差异。这个例子要实现的目标是,编译装载该module后,通过udev动态的生成设备节点star0,读取设备节点cat /dev/star0可以打印出星号,星号的数量可以由模块参数动态的指定。以下为源代码:

star.h

#ifndef STAR_H_H_H#define STAR_H_H_H#define AUTHOR "Tao Yang"#define DESCRIPTION "A CHAR DEVICE DIRVERS SAMPLE USING UDEV"#define VERSION "0.1"#endif
star.c

//module_init module_exit#include <linux/init.h>#include <linux/module.h>//module_param#include <linux/moduleparam.h>//printk container_of #include <linux/kernel.h>//dev_t MAJOR MINOR MKDEV#include <linux/types.h>//file_operations file register/unregister_chrdev_region alloc_chrdev_region register/unregister_chrdev(old)#include <linux/fs.h>//cdev cdev_init/add/del#include <linux/cdev.h>//copy_from_user copy_to_user#include <asm/uaccess.h>#include <linux/device.h>#include "star.h"//define device number#define STAR_MAJOR 0#define STAR_MINOR 0#define STAR_DEVS 1#define DEVICE_NAME "star0"#define CLASS_NAME "star"static int howmany=5;module_param(howmany,int,S_IRUGO);//module infoMODULE_AUTHOR(AUTHOR);MODULE_DESCRIPTION(DESCRIPTION);MODULE_VERSION(VERSION);MODULE_LICENSE("GPL");//device variablesstatic struct class* star_class=NULL;static struct device* star_sysdevice=NULL;int star_major=STAR_MAJOR;int star_minor=STAR_MINOR;int star_nr_devs=STAR_DEVS;//device structstatic struct star_dev {    char *data;    struct cdev cdev;};static struct star_dev star_device;static ssize_t star_read(struct file * filp,char * buf,size_t count,loff_t *ppos){    int i;    char star_str[10000];    struct star_dev *dev=filp->private_data;        for (i=0;i<howmany;i++){        star_str[i]='*';    }    star_str[howmany]='\n';    int len=strlen(star_str);    if(count<len)        return -EINVAL;    if(*ppos!=0)return 0;    if(copy_to_user(buf,star_str,len))        return -EINVAL;    *ppos=len;    return len;}int star_open(struct inode *inode,struct file *filp){    struct star_dev *dev;    dev=container_of(inode->i_cdev,struct star_dev,cdev);    filp->private_data=dev;    //......    return 0;}//file operationsstatic const struct file_operations star_fops = {    .owner  = THIS_MODULE,    .read   = star_read,    .open   = star_open,};static void star_setup_cdev(struct star_dev *dev,int index){    int err,devno=MKDEV(star_major,star_minor+index);        printk(KERN_ALERT "setup cdev...\n");    cdev_init(&dev->cdev,&star_fops);    dev->cdev.owner=THIS_MODULE;    dev->cdev.ops=&star_fops;    err=cdev_add(&dev->cdev,devno,1);    if(err)    printk(KERN_ALERT "Error %d adding star%d",err,index);   }static int __init star_init(void){    int ret;    dev_t dev;    printk(KERN_ALERT "hello tom!\n");    if(star_major){        dev=MKDEV(star_major,star_minor);        ret=register_chrdev_region(dev,star_nr_devs,DEVICE_NAME);        printk(KERN_ALERT "static!\n");    }else{        ret=alloc_chrdev_region(&dev,star_minor,star_nr_devs,DEVICE_NAME);        star_major=MAJOR(dev);        printk(KERN_ALERT "dynamic!\n");        printk(KERN_ALERT "Device Major is %d!\n",star_major);    }    if(ret<0){printk(KERN_ALERT "star:can't get major %d\n",star_major);        return ret;    }    printk(KERN_ALERT "set up cdev!");    star_setup_cdev(&star_device,0);    star_class=class_create(THIS_MODULE,CLASS_NAME);    if(IS_ERR(star_class)){printk(KERN_ALERT "failed to register device class '%s'\n",CLASS_NAME);    }    //with a class ,the easiest way to instantiate a device is to call device_create()    star_sysdevice=device_create(star_class,NULL,MKDEV(star_major,0),NULL,DEVICE_NAME);    return 0;}static void __exit star_exit(void){    device_destroy(star_class,MKDEV(star_major,star_minor));    class_unregister(star_class);    class_destroy(star_class);    cdev_del(&star_device.cdev);    printk(KERN_ALERT "goodbye...\n");}module_init(star_init);module_exit(star_exit);

Makefile

ifneq ($(KERNELRELEASE),)obj-m:=star.oelseKERNELDIR ?= /lib/modules/$(shell uname -r)/buildPWD :=$(shell pwd)default:$(MAKE) -C $(KERNELDIR) M=$(PWD) modulesendif

编译源代码:

root@ubuntu:~/embedded_linux/ldd3/practice# makemake -C /lib/modules/3.8.0-29-generic/build M=/home/tom/embedded_linux/ldd3/practice modulesmake[1]: Entering directory `/usr/src/linux-headers-3.8.0-29-generic'  CC [M]  /home/tom/embedded_linux/ldd3/practice/star.o/home/tom/embedded_linux/ldd3/practice/star.c:58:1: warning: useless storage class specifier in empty declaration [enabled by default]/home/tom/embedded_linux/ldd3/practice/star.c: In function ‘star_read’:/home/tom/embedded_linux/ldd3/practice/star.c:72:5: warning: ISO C90 forbids mixed declarations and code [-Wdeclaration-after-statement]/home/tom/embedded_linux/ldd3/practice/star.c:66:22: warning: unused variable ‘dev’ [-Wunused-variable]/home/tom/embedded_linux/ldd3/practice/star.c:81:1: warning: the frame size of 10032 bytes is larger than 1024 bytes [-Wframe-larger-than=]  Building modules, stage 2.  MODPOST 1 modules  CC      /home/tom/embedded_linux/ldd3/practice/star.mod.o  LD [M]  /home/tom/embedded_linux/ldd3/practice/star.komake[1]: Leaving directory `/usr/src/linux-headers-3.8.0-29-generic'root@ubuntu:~/embedded_linux/ldd3/practice# 

安装驱动模块:

root@ubuntu:~/embedded_linux/ldd3/practice# insmod star.koroot@ubuntu:~/embedded_linux/ldd3/practice# dmesg -c[ 3946.696548] hello tom![ 3946.699892] dynamic![ 3946.699894] Device Major is 249![ 3946.699895] set up cdev![ 3946.699896] setup cdev...root@ubuntu:~/embedded_linux/ldd3/practice#

这时可以看到/dev/目录下自动生成了设备节点:

root@ubuntu:~/embedded_linux/ldd3/practice# ls -l /dev/star0 crw------- 1 root root 249, 0 Feb  8 14:25 /dev/star0root@ubuntu:~/embedded_linux/ldd3/practice# 

接下来去读取设备节点:

root@ubuntu:~/embedded_linux/ldd3/practice# cat /dev/star0*****

在加载驱动模块时,传递参数,也就是指定打印出星号的个数:

root@ubuntu:~/embedded_linux/ldd3/practice# insmod star.ko howmany=10root@ubuntu:~/embedded_linux/ldd3/practice# cat /dev/star0 **********root@ubuntu:~/embedded_linux/ldd3/practice# 

        最后,以上是我在学习过程中写的一个精简的例子,因此很多技巧及错误处理都没有注意,主要是为了勾勒出linux设备驱动程序的宏观骨架,自己最好能通过读代码总结画出一个流程图,加深印象,代码也是配合着ldd3写的,希望能对正在读这本著作的初学者有所帮助。

       多阅读,多实践,多google!




        

0 0
原创粉丝点击