横刀夺爱的三角关系04:如何编写Linux驱动

来源:百度文库 编辑:偶看新闻 时间:2024/04/19 23:11:29
Linux一直被看成黑客的系统,看似神秘的黑屏字符界面,起初我使用它时,总喜欢这么幻想:“一群人做在电脑前,用飞快的速度,在一些很少人知晓的系统上,输入一些很难记住和搞清楚的代码和命令,这些代码有着自己的特殊的含义,它们无可取代,并有着神奇的魔力,发送的数据在网络以惊人的神速,征服一切机器,而这么多事情,任务完成时,黑客们的茶杯上往往还在冒着热气,挂一丝不屑微笑,一端一切都是那么平静,却在另一端制造无法解决的麻烦,这一切,完全有‘谈笑间,樯橹灰飞烟灭’的气派。”对那个时候我而言,命令就是一切,工作往往是ping一下,开些工具或架设一些服务器。
然而,Linux太开放了,就像一个毫无保留的女神,引诱着人去犯罪,我开始疯狂找寻他的内核代码(usr/src下),阅读起来了,才发现这个女生没那么简单,当时的无论是功力或条件,都在显示,你不可能追得到,对于在她家门口徘徊,久久不肯离去,远远的看着她。使尽浑身解数,终不得其欢心,她不可能看得上现在我的,兄弟你必须努力,低着头,一个人到藏到一个黑暗的地方,往往人在这个时候,才慢慢地了解和承认什么叫差距,什么叫门槛,很不愿意的说:“人是分等级的”。气馁,是解决不了任何问题的,为达成目标人必须努力付出,提高自己。偶然的机会遇到一位身经百战的泡妞高手,在他的提示下,我开始看一些书,其中主要包括老赵的《Linux内核完全剖析》、《Linux设备驱动程序》、《深入理解Linux内核》,说句良心话,老赵的书很帅,但是太花,很细心但链条不是很清晰,有点(“很”字不敢说)碎,个人感觉,看他的书若不时刻把握主体的那条线(main.c),很容易迷失方向。人在作大学问时,往往感觉基础不行,这很正常,我现在还是有这种感觉,嘿嘿!有需要读一些硬件的东西,走到这里人,我想大家都得上梁山,到INTEL官网,肯英文,了解什么叫实模式和虚模式(分页)、保护模式(访问权限),回头发现自己成了好汉,这才有胆去泡啊!总结以往的失败之处,要追任何女生之前,怎能对其豪不了解呢?听说她懂事的时候是0.11,找来看看先吧,有了老赵的帮忙,学得不少,当初想一步登天,真不该啊!想回来,要不是当初的失败,现在可能不会有这么好的心境学习这个旧代码了。人们不是常说什么态度决定什么吗?
恩~~~,不错,这女孩不但长得好看而且性格不错,不枉我在她身上花那么多时间。俗话说女大十八变,26岁(2.6内核)的她和11岁(0.11内核)差距还还真不少哦,多了很多数据结构,要知道这意味着什么?在Linux基本就是一堆数据结构,大多数代码都是通过填充这些结构来实现的,或是变量或是函数(如果硬要说学习内核有捷径,也许就是了解一些常用的结构体(如:file、file_opteration)吧!)。哈哈,丰满、成熟,可谓秀色可餐~~~,女性特征浓得很哪!这个时候,把她彻底弄清楚,显得没什么意义了,掌握方法显得尤为重要,采取分而治之是一个不错的办法。
核心部分的代码kernel有时间看看,由于你了解了0.11,所以这个时候,建议你可以开始写驱动代码了,写驱动可以说是一种很好的入门方式吧。老天自古就怜爱不懈努力的人,我们可以通过逛书店得到这个结论,这个时候,已经有不少的好书出来了,书看过不少,深知资料不是多就好,个人觉得以下是几本很值得一看:《The Linux Kernel Primer A Top-Down Approach For X86 and PowerPC Architectures》(中文版本《Linux内核编程》)、《Linux设备驱动开发详解》《嵌入式Linux应用系统开发实例精讲》,俗话说得好:舍不得孩子套不着狼!想泡到妞,做男人的,就要对自己的钱包狠点。如果能拿出点钱,建议你还是买块板,搞一下嵌入去,这对于你了解软件是如何跑在硬件上是非常有益的。
好吧!现在你知道该怎么做了吧?买下上面的书,好好专研2.6去吧!
Linux里的文件
登录Linux之后,看到的不是文件夹就是文件,看不到win下常看到的A,C,D盘。偷懒网上寻了下它的结构图:

想想,其实一点也不奇怪,美女哪个会没有点个性呢(可能真的有,我未曾有幸蒙面,真不幸~~),对不对?对于一个有品位的有修养的男士,能做什么呢?喜欢她就请不要试图去改变她,种花的人都须知道花儿的习性,学会适应是一种不错的选择。当然,如果有上帝的能力,完全是可以创造一个自己喜欢的出来。菜鸟的我,只能做梦时偷偷想下,好像有人管这叫“幻想伴侣”。
经过一段时间的接触,也许你会说:“这个女孩其实很清纯的。”确实,Linux的文件结构非常简单,就是根目录下那几个,基本不会有什么不变化,分类也很清楚,可谓持家有道,恩~,这种女人各位完全可以娶回家做老婆!真是上天对我们男人的怜悯啊!(您是个女生的话,我就无语了!自己滴明白啦!)对于一个应用程序的人,他只需要知道他调用的函数库在什么地方,如果很冲动想搞驱动的话,那就更简单啦,知道/proc和/dev两个目录就好了。/proc是一个伪文件系统,它只存在于内存中,不占用外存空间,它以文件系统的方式向我们展示系统内核的一些信息,即系统运行时的各种信息,这和MM跑去看病、眼神传情可以说是同个道理。/dev是放设备文件地方,或说是MM们的衣橱。里面的存放着许多二进制文件,我们称它们驱动模块,之所以叫他模块一个很重要的原因就是,他是可以动态加载的,MM(内核)光着身子总是不太好的,但老穿一件衣服的MM,似乎不存在,不说有种说法叫女人的衣橱里的衣服是永远不够穿的吗?为了能经常穿新衣(更换驱动)的,于是出现了动态加载机制。同时为保持衣橱整洁,必须做下分类:
#ls -l /dev/hda /dev/hdb
brw-rw---- 1 root disk 3, 0 May 1 2008 /dev/had
brw-rw---- 1 root disk 3, 64 May 1 2008 /dev/hdb
我们可以看3表示两个都是同种类型的硬盘(常使用同个驱动),0,64表示不是同个硬盘。从大体上分,这些设备文件分别属于字符设备、块设备、网络设备。通过这些文件我们可以和外围的设备,如:键盘、U盘等通信。
一种简单表示它们之间关系方法
用户
/dev设备文件
/proc文件
内核
外围设备
话说回来,说了那么多和我们泡MM有什么关系呢?很明显,我们带MM出去难免要购物,事先了解一下其喜好,那么,逛街的时候就懂得避免经过名牌衣店啦!因为对我来说那每件衣服都可以顶我一个月的工资。那么,遇到这种MM。逛街时要怎么走才不至于太郁闷也不至于太无聊呢? 自己想吧!我也不懂~~~
在/proc中有一个和我们的驱动模块密不可分的文件,/proc/modules,通过查看它,我们了解到目前系统已经动态加载的模块
#cat /proc/modules

#lsmod
好了,目前我们已经知道驱动放在上面地方,如何查看已经加载模块,这些都很重要。要知道泡美女说起来容易,做起来可不简单,没有厚厚的脸皮,百折不挠的斗志,是很难得手的,但有第一点,就是我们绝不提倡死缠烂打,玩持久战。绝对是要在享受过程的同时速战速决。所以,下个文章会单刀直入、直捣黄龙,力求了解如何真正完成一个驱动,呵呵!
转变思维做个当家人
不知道你对花钱,有没有这样的感觉?做孩子的时候,经常伸手要钱;但等到你泡到MM成家之后一切就不同了,这个时候的我们花钱还要不断努力赚钱,同时lp会不断向你伸手,我们成了一个提款机,难怪MM们找对象的时候把金钱排在第一位了。做孩子的时候我们写的是应用程序,调用人家的API做一些自己想做的事情;成家后,我们成了男人,在调用系统API的时候,还要懂得给别人提供API,搞驱动了这时。于是,你领悟到写驱动之前必须先转变自己的思维,懂得承担责任,不然就永远是个小屁孩。唉,人生也许就是这样,阶段不同思想不同。
有句话说:“有付出就会有收获。”讲得一点也没错,成家之后,我们变得很重要了,处事自然小心谨慎起来了,要知道驱动可以是在内核空间运行的,稍不注意,就会对整个系统造成毁灭性的破坏。换个说法:为避免离婚,结婚之后,我们要更爱自己的另一半,观头顾尾,前后上下左右。做一个好的男人(好的驱动),据过来人(高手)说,并不是一件很容易的事情,我们的目标之一就是让我们的LP过得更好(内核稳定、高效地运行),这需要成熟的心态(技术)。爱情可以说是世界上最美好的一种感情,两人若是可以相携人生路,一同生活到老死,是怎样美好的事情。网上找个图说明下先,很喜欢用图,事实说明这样很省口水:

做男人该做的事情
要做一个当家人,有那么几件事情一定要知道怎么做;写一个驱动,有三个结构体也是一定要懂的,可以说整个驱动都在绕着它们三转。
Linux/include/fs.h里有两个:
struct file {
union {
struct list_head  fu_list;
struct rcu_head   fu_rcuhead;
} f_u;
struct dentry     *f_dentry;
struct vfsmount         *f_vfsmnt;
struct file_operations   *f_op;//文件操作的结构指针,内核在做file_operations中的open函数操作时对此指针赋值
atomic_t      f_count;
unsigned int      f_flags;//文件标识,用于对阻塞或非阻塞检查
mode_t        f_mode;//标识文件的读写权限
loff_t        f_pos; //当前读写位置,loff_t为64位数
struct fown_struct   f_owner;
unsigned int      f_uid, f_gid;
struct file_ra_state f_ra;
unsigned long     f_version;
void          *f_security;
/* needed for tty driver, and maybe others */
void          *private_data;
#ifdef CONFIG_EPOLL
/* Used by fs/eventpoll.c to link all the hooks to this file */
struct list_head  f_ep_links;
spinlock_t    f_ep_lock;
#endif /* #ifdef CONFIG_EPOLL */
struct address_space *f_mapping;
};

/*
* NOTE:
* read, write, poll, fsync, readv, writev, unlocked_ioctl and compat_ioctl
* can be called without the big kernel lock held in all filesystems.
*/
struct file_operations {
struct module *owner;
loff_t (*llseek) (struct file *, loff_t, int);
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t (*aio_read) (struct kiocb *, char __user *, size_t, loff_t);
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
ssize_t (*aio_write) (struct kiocb *, const char __user *, size_t, loff_t);
int (*readdir) (struct file *, void *, filldir_t);
unsigned int (*poll) (struct file *, struct poll_table_struct *);
int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
int (*mmap) (struct file *, struct vm_area_struct *);
int (*open) (struct inode *, struct file *);
int (*flush) (struct file *);
int (*release) (struct inode *, struct file *);
int (*fsync) (struct file *, struct dentry *, int datasync);
int (*aio_fsync) (struct kiocb *, int datasync);
int (*fasync) (int, struct file *, int);
int (*lock) (struct file *, int, struct file_lock *);
ssize_t (*readv) (struct file *, const struct iovec *, unsigned long, loff_t *);
ssize_t (*writev) (struct file *, const struct iovec *, unsigned long, loff_t *);
ssize_t (*sendfile) (struct file *, loff_t *, size_t, read_actor_t, void *);
ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
int (*check_flags)(int);
int (*dir_notify)(struct file *filp, unsigned long arg);
int (*flock) (struct file *, int, struct file_lock *);
};
Linux/usb.h(注意这个结构根据实际使用的不同而不同,这是USB设备)
struct usb_driver {
const char *name;
int (*probe) (struct usb_interface *intf,
const struct usb_device_id *id);
void (*disconnect) (struct usb_interface *intf);
int (*ioctl) (struct usb_interface *intf, unsigned int code,
void *buf);
int (*suspend) (struct usb_interface *intf, pm_message_t message);
int (*resume) (struct usb_interface *intf);
const struct usb_device_id *id_table;
struct usb_dynids dynids;
struct device_driver driver;
unsigned int no_dynamic_id:1;
};
我们这里没可能对每个函数的作用做具体介绍,这些内容您完全可以从我一个文里的介绍的书中找到。
对我们需要的函数做填充,可以对去做如下填充:(这两个来自/drivers/usb/media/sn9c102_core.c)
static struct file_operations sn9c102_fops = {
.owner = THIS_MODULE,//标识模块的拥有者
.open =    sn9c102_open,//打开设备文件,它往往是设备文件执行的第一操作
.release = sn9c102_release, //当file结构被释放掉时,会调用这个方法
.ioctl =   sn9c102_ioctl,//ioctl是一个系统调用,提供了一种执行设备特定命令的方法
.read =    sn9c102_read, //读函数,ssize_t表示当前平台上固有的整数类型
.poll =    sn9c102_poll, //这个方法用于查询设备是否可读,可写或处于某种状态。当设备是不可读写时,它们可以被阻塞直至设备变为可读或可写。如果驱动程序中没有定义这个方法则它驱动的设备就会被人认为是可读写的。
.mmap =    sn9c102_mmap, //请求将设备内存映射到进程地址空间,一般用于块设备
.llseek =  no_llseek, //标识当前文件的操作位置
};
static struct usb_driver sn9c102_usb_driver = {
.name =       "sn9c102",//驱动名,通常和驱动模块的名称一样,即/dev/sn9c102
.id_table =   sn9c102_id_table, // ID设备表,包含了一列该驱动程序可以支持的USB设备
.probe =      sn9c102_usb_probe,//探测函数
.disconnect = sn9c102_usb_disconnect,//断开函数
};
其中对file* filp的操作通常出现在file_operations里面的各种函数中:
struct sn9c102_device* cam = video_get_drvdata(video_devdata(filp));
同时,代码常通过引用file的某些参数来决定是不是要做某些事情:
if (filp->f_flags & O_NONBLOCK) {
up(&cam->fileop_sem);
return -EAGAIN;
}
具体函数实现可以参看linux2.6.16.20内核,这里就没必要给出了吧!在我们写驱动的时候,是有套路的,先include 一些下面要用到的头文件,提供一些必要函数
#include  //想搞成模块不能没有这个吧
#include //prinfk()之类重要的函数
#include //下面的三个结构体两个来自这里,Linux下一切设备都是文件
#include "sn9c102.h" //用户自定义通常放最后,用””
做一些必要的仪式性质的工作
/**************************************************/
#define SN9C102_MODULE_NAME "V4L2 driver for SN9C10x PC Camera Controllers"
#define SN9C102_MODULE_AUTHOR  "(C) 2004-2006 Luca Risolia"
#define SN9C102_AUTHOR_EMAIL  ""
#define SN9C102_MODULE_LICENSE  "GPL"
#define SN9C102_MODULE_VERSION  "1:1.26"
#define SN9C102_MODULE_VERSION_CODE  KERNEL_VERSION(1, 0, 26)
/************************************************/
MODULE_DEVICE_TABLE(usb, sn9c102_id_table);
MODULE_AUTHOR(SN9C102_MODULE_AUTHOR " " SN9C102_AUTHOR_EMAIL);
MODULE_DESCRIPTION(SN9C102_MODULE_NAME);
MODULE_VERSION(SN9C102_MODULE_VERSION);
MODULE_LICENSE(SN9C102_MODULE_LICENSE);
再填充file_operations然后填充usb_driver,最后写两个重要的模块函数,用宏定义注册下啊
static int __init sn9c102_module_init(void)
{
int err = 0;
KDBG(2, SN9C102_MODULE_NAME " v" SN9C102_MODULE_VERSION);
KDBG(3, SN9C102_MODULE_AUTHOR);
if ((err = usb_register(&sn9c102_usb_driver)))//设备注册,以获取硬件资源
KDBG(1, "usb_register() failed");
return err;
}
static void __exit sn9c102_module_exit(void)
{
usb_deregister(&sn9c102_usb_driver); //注销设备,释放硬件资源
}
module_init(sn9c102_module_init);//模块初始化时候被用到
module_exit(sn9c102_module_exit); //卸载模块时候被用到
总的来说就是这样的(具体参看/drivers/usb/media/sn9c102_core.c)
包含必要的头文件
填充file_operations
填充usb_driver
usb_register\usb_deregister
module_init\ module_exit
所以其实,写个驱动并不是很难,相反他并没有太多新玩儿,如果你打算写个应用往往要想大把大把的算法去解决实际问题,写驱动则没有太多花样,花样多了反而易出问题。这透露我们一个信息,女朋友和老婆,泡妞和过日子完全是两码事。最后,祝你过得愉快,因为你愉快,我愉快!
做新男人
年纪大的人通常会有一种感觉(实际上我还不算大),那就是年轻一代和自己的思想往往会有一些出入,小弟在这里发表下个人拙见,如有看完不爽者请谅解:现在网上年轻一代不是正流行非主流文化嘛!对于一些老人家,往往喜欢对这些现象品头论足,说起来这时代发展就这样呗,想想我们当年,嘿嘿,就我而言吧!彻底的保守派,在很多年轻人看来,从思想或行动上看,完全是个古董那种。但俺也有自己的苦衷,一方面父母管得严,一方面自己生活的环境接触的人就这样,有什么办法呢?于是我选择认命,幸运的是到目前为止,活得还算潇洒,嘿嘿(有点自恋了)。但啊,这时代究竟是时代啊!某一天又出来个新主流了,有人出生的地方,再新的思想就总会有过时的时候。中国有句话说得响亮:“与时俱进”,个人觉得挺有道理,社会生产力发展着,人的需求也在发生变化,哪个时候,没学会玩电脑就算文盲,是吧?这个女人啊!也一样,需求也是越来越丰富啊!市场经济,有需求就会有提供,做新男人,也成了一种新时尚。话是这么说,但不管如何始终还是个男人,这点还没变。
据说在Linux 2.6.18版本的内核之后,出现了一种新的管理设备文件的方法,叫:udev(userspace device management)取代我们之前看到的那种devfs管理方式。(这个我也是今天晚上有人告诉,我才知道的,悲哀的很,要知道孤陋寡闻的人是很容易被淘汰的~~怕怕,于是上官网look了下,搞了这个文章算是知识补充吧!),udev的官方文档对它做了如下定义:udev是一种用户空间上的应用程序,它动态地为位于/dev下的当前正被使用的设备,提供一个唯一的入口,udev将取代devfs。关于udev的优势,网上大把资料,试问,哪个东西出来的时候没吹上一车呢,自己seesee,不说也罢!
下面是一个是一个完整的usb驱动,有人要问了,你小子怎么老来usb,换个其他的,可以不?嘿嘿,对usb太情有独钟了,一想到驱动,总是到它。
与前面所说的devfs在使用上是有所区别的,结构体、使用的函数和实现步骤都存在差异,所谓差异就是说它们还是有一些相似之处的,新男人也还是男人这点还没变。在\linux-2.6.16.20\include\linux\device.h中做如下定义:
struct class {
const char    * name;
struct module     * owner;
struct subsystem  subsys;
struct list_head  children;
struct list_head  interfaces;
struct semaphore  sem;   /* locks both the children and interfaces lists */
struct class_attribute      * class_attrs;
struct class_device_attribute   * class_dev_attrs;
int (*uevent)(struct class_device *dev, char **envp,
int num_envp, char *buffer, int buffer_size);
void   (*release)(struct class_device *dev);
void   (*class_release)(struct class *class);
};
struct class_device {
struct list_head  node;
struct kobject       kobj;
struct class      * class;   /* required */
dev_t         devt;      /* dev_t, creates the sysfs "dev" */
struct class_device_attribute *devt_attr;
struct class_device_attribute uevent_attr;
struct device     * dev;     /* not necessary, but nice to have */
void          * class_data; /* class-specific data */
struct class_device  *parent;   /* parent of this child device, if there is one */
void   (*release)(struct class_device *dev);
int (*uevent)(struct class_device *dev, char **envp,
int num_envp, char *buffer, int buffer_size);
char   class_id[BUS_ID_SIZE];   /* unique to this class */
};
对特定的设备使用特定的结构,下面两个结构来自\linux-2.6.16.20\include\linux\usb.h
struct usb_interface {
/* array of alternate settings for this interface,
* stored in no particular order */
struct usb_host_interface *altsetting;
struct usb_host_interface *cur_altsetting;    /* the currently
* active alternate setting */
unsigned num_altsetting; /* number of alternate settings */
int minor;        /* minor number this interface is
* bound to */
enum usb_interface_condition condition;       /* state of binding */
struct device dev;       /* interface specific device info */
struct class_device *class_dev;
};
struct usb_class_driver {
char *name;
struct file_operations *fops;
int minor_base;
};
在创建的时候要使用到以下函数,它们在\linux-2.6.16.20\include\linux\device.h中定义如下:
extern struct class *class_create(struct module *owner, char *name);//创建一个类,返回一个class
extern void class_destroy(struct class *cls);//销毁一个类
extern struct class_device *class_device_create(struct class *cls,
struct class_device *parent,
dev_t devt,
struct device *device,
char *fmt, ...)
__attribute__((format(printf,5,6)));//为类创建一个类设备,返回一个class_device
extern void class_device_destroy(struct class *cls, dev_t devt);//销毁一个类设备
使用过程可以是这样的(注意:这里做了省略处理):
static struct class *usb_class;
int usb_major_init(void)
{
usb_class = class_create(THIS_MODULE, "usb");
}
void usb_major_cleanup(void)
{
class_destroy(usb_class);
}
int usb_register_dev(struct usb_interface *intf,//class_device为参
struct usb_class_driver *class_driver)
{
intf->class_dev = class_device_create(usb_class, NULL,
MKDEV(USB_MAJOR, minor), //以主设备号和次设备号为参
&intf->dev, "%s", temp);
}
EXPORT_SYMBOL(usb_register_dev);
void usb_deregister_dev(struct usb_interface *intf,
struct usb_class_driver *class_driver)
{
class_device_destroy(usb_class, MKDEV(USB_MAJOR, intf->minor));
}
EXPORT_SYMBOL(usb_deregister_dev);
于是对于udev我们又可以得到一个省口水的图:
Udev设备管理方式
Devfs设备管理方式
包含必要的头文件
包含必要的头文件
填充file_operations
填充file_operations
创建usb_class类
填充usb_driver
register\deregister
+
class_device_create\ class_device_destroy
usb_register\usb_deregister
EXPORT_SYMBOL
module_init\ module_exit
下面给出一个完整的代码
来自:\linux-2.6.16.20\drivers\usb\core\file.c
#include
#include
#include
#include
#include
#include "usb.h"
#define MAX_USB_MINORS   256
static struct file_operations *usb_minors[MAX_USB_MINORS];
static DEFINE_SPINLOCK(minor_lock);
static int usb_open(struct inode * inode, struct file * file)
{
int minor = iminor(inode);
struct file_operations *c;
int err = -ENODEV;
struct file_operations *old_fops, *new_fops = NULL;
spin_lock (&minor_lock);
c = usb_minors[minor];
if (!c || !(new_fops = fops_get(c))) {
spin_unlock(&minor_lock);
return err;
}
spin_unlock(&minor_lock);
old_fops = file->f_op;
file->f_op = new_fops;
/* Curiouser and curiouser... NULL ->open() as "no device" ? */
if (file->f_op->open)
err = file->f_op->open(inode,file);
if (err) {
fops_put(file->f_op);
file->f_op = fops_get(old_fops);
}
fops_put(old_fops);
return err;
}
static struct file_operations usb_fops = {
.owner =   THIS_MODULE,
.open =       usb_open,
};
static struct class *usb_class;
int usb_major_init(void)
{
int error;
error = register_chrdev(USB_MAJOR, "usb", &usb_fops);
if (error) {
err("unable to get major %d for usb devices", USB_MAJOR);
goto out;
}
usb_class = class_create(THIS_MODULE, "usb");
if (IS_ERR(usb_class)) {
error = PTR_ERR(usb_class);
err("class_create failed for usb devices");
unregister_chrdev(USB_MAJOR, "usb");
goto out;
}
out:
return error;
}
void usb_major_cleanup(void)
{
class_destroy(usb_class);
unregister_chrdev(USB_MAJOR, "usb");
}
int usb_register_dev(struct usb_interface *intf,
struct usb_class_driver *class_driver)
{
int retval = -EINVAL;
int minor_base = class_driver->minor_base;
int minor = 0;
char name[BUS_ID_SIZE];
char *temp;
#ifdef CONFIG_USB_DYNAMIC_MINORS
minor_base = 0;
#endif
intf->minor = -1;
dbg ("looking for a minor, starting at %d", minor_base);
if (class_driver->fops == NULL)
goto exit;
spin_lock (&minor_lock);
for (minor = minor_base; minor < MAX_USB_MINORS; ++minor) {
if (usb_minors[minor])
continue;
usb_minors[minor] = class_driver->fops;
retval = 0;
break;
}
spin_unlock (&minor_lock);
if (retval)
goto exit;
intf->minor = minor;
/* create a usb class device for this usb interface */
snprintf(name, BUS_ID_SIZE, class_driver->name, minor - minor_base);
temp = strrchr(name, '/');
if (temp && (temp[1] != 0x00))
++temp;
else
temp = name;
intf->class_dev = class_device_create(usb_class, NULL,
MKDEV(USB_MAJOR, minor),
&intf->dev, "%s", temp);
if (IS_ERR(intf->class_dev)) {
spin_lock (&minor_lock);
usb_minors[intf->minor] = NULL;
spin_unlock (&minor_lock);
retval = PTR_ERR(intf->class_dev);
}
exit:
return retval;
}
EXPORT_SYMBOL(usb_register_dev);
void usb_deregister_dev(struct usb_interface *intf,
struct usb_class_driver *class_driver)
{
int minor_base = class_driver->minor_base;
char name[BUS_ID_SIZE];
#ifdef CONFIG_USB_DYNAMIC_MINORS
minor_base = 0;
#endif
if (intf->minor == -1)
return;
dbg ("removing %d minor", intf->minor);
spin_lock (&minor_lock);
usb_minors[intf->minor] = NULL;
spin_unlock (&minor_lock);
snprintf(name, BUS_ID_SIZE, class_driver->name, intf->minor - minor_base);
class_device_destroy(usb_class, MKDEV(USB_MAJOR, intf->minor));
intf->class_dev = NULL;
intf->minor = -1;
}
EXPORT_SYMBOL(usb_deregister_dev);
从这篇文我们发现,技术是不断变化的,换句话说,女人是善变的感情动物,请不要误会,是感情动物:
Money很重要前面我们已经对驱动程序的结构有了一个初步的认识,我们知道,不管是devfs还是udev设备管理方式,都有自己的一套使用方法,很好的掌握理解和这些方法是十分有必要的,对于一个男人而言长相、才学、擅长交际很重要,如果再加上钱,那简直就是绝世的了。生活是里不开钱的,所以钱很重要。这个世界就是怎么现实,握了理论还能做出来,常有人说:“老板不看过程,只看结果”。于是这里讨论如何编译和安装模块,还有如何很好的完成一个Makefile。这正是很多人看来过不了的技术门槛,今天就来个鲤鱼跳龙门吧!希望你看完后会说:“草你,这有难度吗?”对于驱动编写过程中的基础技术细节,下个文我们会讨论的,大家先看看书吧!写这个文章的时候,linux最新版本是2.6.25,这里以这个版本做为基础做讨论。
模块:一种可以动态装载到内核的二进制块,他在被装载的时候会检测硬件设备是否正常,并做初始化工作;卸载时会释放被它占有的硬件资源。驱动程序中我,我们用:
#include//构建模块需要一些数据结构和一些函数,这些函数包括下面这两个
module_init(sn9c102_module_init);//模块初始化时候被用到
module_exit(sn9c102_module_exit); //卸载模块时候被用到
这些信息是必要的,它们向内核声明这个程序是一个模~~块~~。在devfs下,我们可以用#insmod命令来装载他们。
功能说明:载入模块。
语  法:insmod [-fkmpsvxX][-o <模块名称>][模块文件][符号名称 = 符号值]
补充说明:Linux有许多功能是通过模块的方式,在需要时才载入kernel。如此可使kernel较为精简,进而提高效率,以及保有较大的弹性。这类可载入的模块,通常是设备驱动程序。
参  数:
-f  不检查目前kernel版本与模块编译时的kernel版本是否一致,强制将模块载入。
-k  将模块设置为自动卸除。
-m  输出模块的载入信息。
-o<模块名称>  指定模块的名称,可使用模块文件的文件名。
-p  测试模块是否能正确地载入kernel。
-s  将所有信息记录在系统记录文件中。
-v  执行时显示详细的信息。
-x  不要汇出模块的外部符号。
-X  汇出模块所有的外部符号,此为预设置。
前面文章中已经给了一个复杂的驱动例子,下面再给出一个空壳驱动程序给大家玩下 . .!~~~ 一个最简单的模块代码(这个代码不知道有没有问题,时间关系,我并没真正安装,如果您按照我的说法安装出现问题,请留言!):
#include /*module_init、module_exit*/
#include /*写模块必须的*/
#include /* printk*/
MODULE_LICENSE("GPL");
static int __init hello_init(void)
{
printk(KERN_ALERT " Hello, Kernel World "n");
return 0;
}
static void __exit hello_exit(void)
{
printk(KERN_ALERT "Goodbye, Kernel World"n");
}
module_init(hello_init);
module_exit(hello_exit);
创建一个叫Makefile的文件(无后缀),写入:
obj-m := hello.o #是***.o
KERNELDIR := /lib/modules/$(shell uname -r)/build
default:      #编译模块
make -C $(KERNELDIR) M=$(shell pwd) modules   #前面输入tab键
install:        #安装模块
insmod hello.ko #是***.ko
uninstall:      #卸载模块
rmmod hello
clean:        #清除留文件,以重新编译
make -C $(KERNELDIR) M=$(shell pwd) clean
rm -f Module.symvers
将Makefile及模块代码放到同个目录下,在命令行下cd到该目录下,以root身份登陆,
在字符界面下,使用命令:
#make  #编译模块
#make install  #安装模块,运行后打印:Hello, Kernel World
#make uninstall #卸载模块,运行后打印:Goodbye, Kernel World
#make clean   #删除KERNELDIR目录下所有编译生成的文件
在图形界面下,输出信息不在终端显示,而是保存在文件/var/log/messages
(按“CTRL + ALT + F5 ”换到控制台模式,可看到输出。
按”ALT + F7”可以切换回图形界面)。
运行情况如下:

若Makefile中的obj-m := hello.o 写成obj-m := hello.ko ,将出现如下错误:

还有一点需要注意:模块在安装之后卸载掉才能再次安装,否则会出错。