Linux设备驱动编程之复杂设备驱动
这里所说的复杂设备驱动涉及到PCI、USB、网络设备、块设备等(严格意义而言,这些设备在概念上并不并列,例如与块设备并列的是字符设备,而PCI、USB设备等都可能属于字符设备),这些设备的驱动中又涉及到一些与特定设备类型相关的较为复杂的数据结构和程序结构。本文将不对这些设备驱动的细节进行过多的介绍,仅仅进行轻描淡写的叙述。
PCI 是The Peripheral Component Interconnect -Bus的缩写,CPU使用PCI桥chipset与PCI设备通信,PCI桥chipset处理了PCI子系统与内存子系统间的所有数据交互,PCI设备完全被从内存子系统分离出来。下图呈现了PCI子系统的原理:
![]() |
每个PCI设备都有一个256字节的设备配置块,其中前64字节作为设备的ID和基本配置信息,Linux中提供了一组函数来处理PCI配置块。在PCI设备能得以使用前,Linux驱动程序需要从PCI设备配置块中的信息决定设备的特定参数,进行相关设置以便能正确操作该PCI设备。
一般的PCI设备初始化函数处理流程为:
(1)检查内核是否支持PCI-Bios;
(2)检查设备是否存在,获得设备的配置信息;
1~2这两步的例子如下:
| int pcidata_read_proc(char *buf, char **start, off_t offset, int len, int *eof,void *data) { int i, pos = 0; int bus, devfn; if (!pcibios_present()) return sprintf(buf, "No PCI bios present\n"); /* dev = pci_find_slot(bus, devfn); /* Ok, we've found a device, copy its cfg space to the buffer*/ |
其中使用的pci_find_slot()函数定义为:
| struct pci_dev *pci_find_slot (unsigned int bus, unsigned int devfn) { struct pci_dev *pptr = kmalloc(sizeof(*pptr), GFP_KERNEL); int index = 0; unsigned short vendor; int ret; if (!pptr) return NULL; |
(3)根据设备的配置信息申请I/O空间及IRQ资源;
USB设备的驱动主要处理probe(探测)、disconnect(断开)函数及usb_device_id(设备信息)数据结构,如:
| static struct usb_device_id sample_id_table[] = { { USB_INTERFACE_INFO(3, 1, 1), driver_info: (unsigned long)"keyboard" } , { USB_INTERFACE_INFO(3, 1, 2), driver_info: (unsigned long)"mouse" } , { 0, /* no more matches */ } }; static struct usb_driver sample_usb_driver = |
当一个USB 设备从系统拔掉后,设备驱动程序的disconnect 函数会自动被调用,在执行了disconnect 函数后,所有为USB 设备分配的数据结构,内存空间都会被释放:
| static void sample_disconnect(struct usb_device *udev, void *clientdata) { /* the clientdata is the sample_device we passed originally */ struct sample_device *sample = clientdata; /* remove the URB, remove the input device, free memory */ /* |
当驱动程序向子系统注册后,插入一个新的USB设备后总是要自动进入probe函数。驱动程序会为这个新加入系统的设备向内部的数据结构建立一个新的实例。通常情况下,probe 函数执行一些功能来检测新加入的USB 设备硬件中的生产厂商和产品定义以及设备所属的类或子类定义是否与驱动程序相符,若相符,再比较接口的数目与本驱动程序支持设备的接口数目是否相符。一般在probe 函数中也会解析USB 设备的说明,从而确认新加入的USB 设备会使用这个驱动程序:
| static void *sample_probe(struct usb_device *udev, unsigned int ifnum, const struct usb_device_id *id) { /* * The probe procedure is pretty standard. Device matching has already * been performed based on the id_table structure (defined later) */ struct usb_interface *iface; struct usb_interface_descriptor *interface; struct usb_endpoint_descriptor *endpoint; struct sample_device *sample; printk(KERN_INFO "usbsample: probe called for %s device\n",(char *)id->driver_info /* "mouse" or "keyboard" */ ); iface = &udev->actconfig->interface[ifnum]; if (interface->bNumEndpoints != 1) return NULL; endpoint = interface->endpoint + 0; usb_set_protocol(udev, interface->bInterfaceNumber, 0); /* allocate and zero a new data structure for the new device */ /* fill the URB data structure using the FILL_INT_URB macro */ if (maxp > 8) maxp = 8; sample->maxp = maxp; /* remember for later */ /* register the URB within the USB subsystem */ /* /* and return the new structure */ |
在网络设备驱动的编写中,我们特别关心的就是数据的收、发及中断。网络设备驱动程序的层次如下:
![]() |
网络设备接收到报文后将其传入上层:
| /* * Receive a packet: retrieve, encapsulate and pass over to upper levels */ void snull_rx(struct net_device *dev, int len, unsigned char *buf) { struct sk_buff *skb; struct snull_priv *priv = (struct snull_priv *) dev->priv; /* /* Write metadata, and then pass to the receive level */ |
在中断到来时接收报文信息:
| void snull_interrupt(int irq, void *dev_id, struct pt_regs *regs) { int statusword; struct snull_priv *priv; /* * As usual, check the "device" pointer for shared handlers. * Then assign "struct device *dev" */ struct net_device *dev = (struct net_device *)dev_id; /* ... and check with hw if it's really ours */ if (!dev /*paranoid*/ ) return; /* Lock the device */ /* retrieve statusword: real netdevices use I/O instructions */ /* Unlock the device and we are done */ |
而发送报文则分为两个层次,一个层次是内核调用,一个层次完成真正的硬件上的发送:
| /* * Transmit a packet (called by the kernel) */ int snull_tx(struct sk_buff *skb, struct net_device *dev) { int len; char *data; struct snull_priv *priv = (struct snull_priv *) dev->priv; #ifndef LINUX_24 len = skb->len < ETH_ZLEN ? ETH_ZLEN : skb->len; /* Remember the skb, so we can free it at interrupt time */ /* actual deliver of data is device-specific, and not shown here */ return 0; /* Our simple device can not fail */ /* /* I am paranoid. Ain't I? */ if (0) { /* enable this conditional to look at the data */ ((u8 *)saddr)[2] ^= 1; /* change the third octet (class C) */ ih->check = 0; /* and rebuild the checksum (ip needs it) */ if (dev == snull_devs) /* priv = (struct snull_priv *) dev->priv; |
块设备也以与字符设备register_chrdev、unregister_ chrdev 函数类似的方法进行设备的注册与释放。但是,register_chrdev使用一个向 file_operations 结构的指针,而register_blkdev 则使用 block_device_operations 结构的指针,其中定义的open、release 和 ioctl 方法和字符设备的对应方法相同,但未定义 read 或者 write 操作。这是因为,所有涉及到块设备的 I/O 通常由系统进行缓冲处理。
| static void handle_mtdblock_request(void) { struct request *req; struct mtdblk_dev *mtdblk; unsigned int res; for (;;) { if (minor(req->rq_dev) >= MAX_MTD_DEVICES) if (!IS_REQ_CMD(req)) if ((req->sector + req->current_nr_sectors) > (mtdblk->mtd->size >> 9)) // Handle the request case READ: end_req: int __init init_mtdblock(void) spin_lock_init(&mtdblks_lock); #ifdef CONFIG_DEVFS_FS devfs_dir_handle = devfs_mk_dir(NULL, DEVICE_NAME, NULL); /* We fill it in at open() time. */ BLK_INIT_QUEUE(BLK_DEFAULT_QUEUE(MAJOR_NR), &mtdblock_request, &mtdblock_lock); kernel_thread (mtdblock_thread, NULL, CLONE_FS|CLONE_FILES|CLONE_SIGHAND); static void __exit cleanup_mtdblock(void) |
Linux设备驱动编程之复杂设备驱动的更多相关文章
- 梦织未来Windows驱动编程 第03课 驱动的编程规范
最近根据梦织未来论坛的驱动教程学习了一下Windows下的驱动编程,做个笔记备忘.这是第03课<驱动的编程规范>. 驱动部分包括基本的驱动卸载函数.驱动打开关闭读取写入操作最简单的分发例程 ...
- 梦织未来Windows驱动编程 第06课 驱动对磁盘文件的操作
代码部分: 实现一个文件C:\\text.txt,并读取写入内容到文件,然后将文件设置为只读,并隐藏文件.代码如下: //MyCreateFile.c //2016.07.22 #include &l ...
- 梦织未来Windows驱动编程 第04课 驱动相关的数据结构
- 嵌入式linux驱动开发之点亮led(驱动编程思想之初体验)
这节我们就开始开始进行实战啦!这里顺便说一下啊,出来做开发的基础很重要啊,基础不好,迟早是要恶补的.个人深刻觉得像这种嵌入式的开发对C语言和微机接口与原理是非常依赖的,必须要有深厚的基础才能hold的 ...
- 驱动编程思想之初体验 --------------- 嵌入式linux驱动开发之点亮LED
这节我们就开始开始进行实战啦!这里顺便说一下啊,出来做开发的基础很重要啊,基础不好,迟早是要恶补的.个人深刻觉得像这种嵌入式的开发对C语言和微机接口与原理是非常依赖的,必须要有深厚的基础才能hold的 ...
- 介绍linux设备驱动编程
目前,Linux软件工程师大致可分为两个层次: (1)Linux应用软件工程师(Application Software Engineer): 主要利用C库函数和Linux API进行应用 ...
- Linux设备驱动编程---miscdevice杂类设备的使用方法
miscdev简称杂类设备杂类设备就是对字符设备驱动做一个封装,方便简单使用杂类设备封装字符设备需要包含的头文件:#include <linux/miscdevice.h>(1)杂类设备的 ...
- Linux符设备驱动编程
加入内核源码树外 ① 建立两个文件scull.c,scull.h,以及Makefile文件 Makefile文件 ② 用make进行编译,生成scull.ko驱动程序模块 ③ 把scull.ko模块加 ...
- linux设备驱动第五篇:驱动中的并发与竟态
综述 在上一篇介绍了linux驱动的调试方法,这一篇介绍一下在驱动编程中会遇到的并发和竟态以及如何处理并发和竞争. 首先什么是并发与竟态呢?并发(concurrency)指的是多个执行单元同时.并行被 ...
随机推荐
- CSS lib
Yahoo的轻型CSS框架Pure来加速web开发 http://purecss.io/buttons/#
- bzoj2260: 商店购物 && 4349: 最小树形图
Description Grant是一个个体户老板,他经营的小店因为其丰富的优惠方案深受附近居民的青睐,生意红火.小店的优惠方案十分简单有趣.Grant规定:在一次消费过程中,如果您在本店购买了精制油 ...
- Ubuntu下安装和配置Apache2
http://www.blogjava.net/duanzhimin528/archive/2010/03/05/314564.html 在Ubuntu中安装apache 安装指令:sudo apt- ...
- 转:几十种编程语言的快速入门教程- learnxinyminutes.com
原文来自于:http://top.jobbole.com/15551/ 这家网站的名称是 Learn X in Y minutes,包括了几十种编程语言的快速学习入门教程.打开几种编程语言来看了一下, ...
- BZOJ 1051 受欢迎的牛
Description 每一头牛的愿望就是变成一头最受欢迎的牛.现在有N头牛,给你M对整数(A,B),表示牛A认为牛B受欢迎. 这种关系是具有传递性的,如果A认为B受欢迎,B认为C受欢迎,那么牛A也认 ...
- Unity NGUI 网络斗地主 -发牌 脚本交互
Unity NGUI 网络斗地主 -发牌 脚本交互 @By 灰太龙 Unity4.2.1f4 NGUI 3.0.4 本篇说的问题是脚本与控件的交互! 现在对界面进行了改进,先看副图! 1.制作发牌效果 ...
- Unity3D Asset Server搭建 .
Unity3D Asset Server搭建 本文转载于 http://blog.csdn.net/amazonzx/article/details/7980117,非常感谢! Asset Se ...
- Android Task 相关
在日常开发过程中,只要涉及到activity,那么对task相关的东西总会或多或少的接触到,不过对task相关的一些配置的作用一直理解的还不是很透彻,官方文档在细节上说的也不够清楚,要透彻理解还是得自 ...
- MPI Maelstrom(Dijkstra)
http://poj.org/problem?id=1502 刷一道模板题稳定一下心情... Dijkstra求单源最短路,就是输入的时候注意下,是按下三角输入的(无向图),输入字符x表示i与j不通. ...
- (转载)mysql 用drop和delete方法删除用户的区别
(转载)http://hi.baidu.com/yymagento/item/56c3f6184bce8347e75e06db 在学习drop方法删除用户时,按照书上讲的一直没操作成功,后来到网上查了 ...

