linux设备驱动
LINUX设备驱动程序(4)

协议简介
对于网络的正式介绍一般都采用 OSI (Open Systems Interconnection)模型, 但是Linux 中网络栈的介绍一般分为四层的 Internet 模型。
协议栈层次对比
OSI七层网络模型 应用层 表示层 会话层 传输层 网络层
数据链路层 物理层
Linux TCP/IP 四层概念模型
网络协议
网络协议层用于实现各种具体的网络协议, 如: TCP、UDP 等。
设备无关接口
设备无关接口将协议与各种网络设备驱动连接在一起。 这一层提供一组通用函数供底层网络设备驱动程序使用,让 它们可以对高层协议栈进行操作。
首先,设备驱动程序可能会通过调用 register_netdevice 或 unregister_netdevice 在内核中 进行注册或注销。调用者首先填写 net_device 结构,然后 传递这个结构进行注册。内核调用它的 init 函数(如果定义 了这种函数),然后执行一组健全性检查,并将新设备添加 到设备列表中(内核中的活动设备链表)。
驱动程序
网络栈底部是负责 管理物理网络设备 的设备驱动程序。
第二节 网卡驱动程序设计
设备注册
设备描述:
每个网络接口都由一个 net_device结构来描述
注册: 网络接口驱动的注册方式与字符驱动不同之处在于 它没有主次设备号,并使用如下函数注册。
int register_netdev(struct net_device *dev)
Linux网络子系统架构
Linux协议架构
Linux 网络子系统的顶部是系统调用接口。它为用 户空间的应用程序提供了一种访问内核网络子系统 的方法。位于其下面的是一个协议无关层,它提供 了一种通用方法来使用传输层协议。然后是具体协 议的实现,在 Linux 中包括内嵌的协议 TCP、 UDP,当然还有 IP。然后是设备无关层,它提供了 协议与设备驱动通信的通用接口,最下面是设备驱 动程序。
Linux设备驱动之Ioctl控制

Linux设备驱动之Ioctl控制 ⼤部分驱动除了需要具备读写设备的能⼒之外,还需要具备对硬件控制的能⼒。
⼀、在⽤户空间,使⽤ioctl系统调⽤来控制设备,原型如下:int ioctl(int fd,unsigned long cmd,...);/*fd:⽂件描述符cmd:控制命令...:可选参数:插⼊*argp,具体内容依赖于cmd*/ ⽤户程序所作的只是通过命令码告诉驱动程序它想做什么,⾄于怎么解释这些命令和怎么实现这些命令,这都是驱动程序要做的事情。
⼆、驱动ioctl⽅法:int (*ioctl) (struct inode *inode,struct file *filp,unsigned int cmd,unsigned long arg);/*inode与filp两个指针对应于应⽤程序传递的⽂件描述符fd,这和传递open⽅法的参数⼀样。
cmd 由⽤户空间直接不经修改的传递给驱动程序arg 可选。
*/ 在驱动程序中实现的ioctl函数体内,实际上是有⼀个switch {case}结构,每⼀个case对应⼀个命令码,做出⼀些相应的操作。
怎么实现这些操作,这是每⼀个程序员⾃⼰的事情,因为设备都是特定的。
关键在于怎么样组织命令码,因为在ioctl中命令码是唯⼀联系⽤户程序命令和驱动程序⽀持的途径。
在Linux核⼼中是这样定义⼀个命令码的:____________________________________| 设备类型 | 序列号 | ⽅向 | 数据尺⼨ ||----------|--------|------|-------- || 8 bit | 8 bit | 2 bit |8~14 bit||----------|--------|------|-------- | 这样⼀来,⼀个命令就变成了⼀个整数形式的命令码。
但是命令码⾮常的不直观,所以Linux Kernel中提供了⼀些宏,这些宏可根据便于理解的字符串⽣成命令码,或者是从命令码得到⼀些⽤户可以理解的字符串以标明这个命令对应的设备类型、设备序列号、数据传送⽅向和数据传输尺⼨。
Linux下的硬件驱动——USB设备

Linux下的硬件驱动——USB设备什么是USB设备?USB即Universal Serial Bus,翻译过来就是通用串行总线。
它是一种规范化的、快速的、热插拔的串行输入/输出接口。
USB接口常被用于连接鼠标、键盘、打印机、扫描仪、音频设备、存储设备等外围设备。
Linux下的USB驱动在Linux系统中,每个USB设备都需要一个相应的驱动程序来驱动。
从Linux 2.4开始,内核提供了完整的USB设备支持。
对于每个USB设备,内核都会自动加载对应的驱动程序。
Linux下的USB设备驱动程序主要分为以下几个部分:USB核心驱动程序USB核心驱动程序是操作系统内核中处理USB设备的核心模块,负责与各种类型的USB设备进行通信,包括主机控制器、USB总线、USB设备等。
它与驱动程序和应用程序之间起到了桥梁的作用,为驱动程序提供了USB设备的基础支持。
USB设备驱动程序USB设备驱动程序是与特定USB设备相对应的驱动程序,为USB设备提供具体的读写功能和其他控制功能。
USB核心驱动程序和USB设备驱动程序之间的接口USB核心驱动程序和USB设备驱动程序之间的接口是指USB层和应用程序层之间的接口,负责传递各种USB操作的命令和数据。
如何编译一个USB设备驱动编译一个USB设备驱动程序需要按照以下步骤进行:步骤一:安装必要的软件包首先需要安装编译和调试USB设备驱动所需的软件包,包括编译工具链、内核源代码、内核头文件等。
sudo apt-get install build-essential linux-source linux-headers-`una me -r`步骤二:编写代码现在可以编写USB设备驱动程序的代码,此处不做详细介绍。
步骤三:编译代码在终端窗口中进入USB设备驱动程序所在的目录下,输入以下命令进行编译:make此命令将会编译USB设备驱动程序,并生成一个将驱动程序与内核进行连接的模块文件。
如何在Linux系统中安装驱动程序

如何在Linux系统中安装驱动程序Linux系统作为一个开源的操作系统,广泛应用于各种设备和领域。
而安装驱动程序是在Linux系统中使用外部硬件设备的关键步骤之一。
在本文中,我们将学习如何在Linux系统中安装驱动程序的方法和步骤。
1. 检查硬件设备在安装驱动程序之前,首先需要确定硬件设备的型号和制造商。
可以通过查询设备的型号或者查看设备的相关文档来获取这些信息。
这是非常重要的,因为不同的设备可能需要不同的驱动程序来正确地工作。
2. 更新系统在安装驱动程序之前,确保你的Linux系统已经是最新的状态。
可以通过在终端中运行以下命令来更新系统:```sudo apt-get updatesudo apt-get upgrade```更新系统可以确保你拥有最新的软件包和驱动程序,以获得更好的兼容性和性能。
3. 查找合适的驱动程序一般来说,大部分硬件设备的驱动程序都可以在Linux系统的软件仓库中找到。
可以通过使用包管理器(如apt、yum等)来查找并安装合适的驱动程序。
运行以下命令来搜索并安装特定的驱动程序:```sudo apt-cache search 驱动程序名称sudo apt-get install 驱动程序名称```注意替换“驱动程序名称”为具体的驱动程序名称。
安装驱动程序可能需要输入管理员密码和确认安装。
如果你无法在软件仓库中找到合适的驱动程序,可以转向设备的制造商网站或者开源社区来获取。
下载驱动程序后,根据驱动程序提供的文档和说明来安装。
4. 编译和安装驱动程序有些驱动程序可能需要手动编译和安装。
在这种情况下,你需要确保你的系统已经安装了编译工具(如GCC、make等)。
在终端中切换到驱动程序所在的目录,并按照以下步骤进行编译和安装:```./configuremakesudo make install```以上命令将分别进行配置、编译和安装驱动程序。
在进行安装之前,可能需要输入一些配置选项或者确认安装。
Linux_USB_gadget设备驱动

#include <asm/arch/regs-gpio.h> #include <asm/arch/regs-clock.h> #include <asm/plat-s3c24xx/udc.h>
修改 arch/arm/mach-s3c2410/mach-smdk2410.c
/*USB device 上拉电阻处理 */ static void smdk2410_udc_pullup(enum s3c2410_udc_cmd_e cmd) {
{ int rc; udelay(800);
…… }
配置内核支持 U 盘模拟
<*> USB Gadget Support --->
USB Peripheral Controller (S3C2410 USB Device Controller) --->
S3C2410 USB Device Controller
# mkdir /mnt/gadget # mount -t vfat /dev/mtdblock2 /mnt/gadget/ #ls
可以看到 windows 存入 U 盘的文件。
二、usbnet 功能的实现
配置内核支持 usbnet
<*> USB Gadget Support --->
USB Peripheral Controller (S3C2410 USB Device Controller) --->
一、背景知识 1、USB Mass Storage 类规范概述 USB 组织在 universal Serial Bus Mass Storage Class Spaceification 1.1 版
Linux视频设备驱动编程(v4l2编程)

Linux视频设备驱动编程(v4l2编程)一.什么是video4linuxVideo4linux2(简称V4L2),是linux中关于视频设备的内核驱动。
在Linux 中,视频设备是设备文件,可以像访问普通文件一样对其进行读写,摄像头在/dev/video0下。
二、一般操作流程(视频设备):1. 打开设备文件。
int fd=open(”/dev/video0″,O_RDWR);2. 取得设备的capability,看看设备具有什么功能,比如是否具有视频输入,或者音频输入输出等。
VIDIOC_QUERYCAP,struct v4l2_capability3. 选择视频输入,一个视频设备可以有多个视频输入。
VIDIOC_S_INPUT,struct v4l2_input4. 设置视频的制式和帧格式,制式包括PAL,NTSC,帧的格式个包括宽度和高度等。
VIDIOC_S_STD,VIDIOC_S_FMT,struct v4l2_std_id,struct v4l2_format5. 向驱动申请帧缓冲,一般不超过5个。
struct v4l2_requestbuffers6. 将申请到的帧缓冲映射到用户空间,这样就可以直接操作采集到的帧了,而不必去复制。
mmap7. 将申请到的帧缓冲全部入队列,以便存放采集到的数据.VIDIOC_QBUF,struct v4l2_buffer8. 开始视频的采集。
VIDIOC_STREAMON9. 出队列以取得已采集数据的帧缓冲,取得原始采集数据。
VIDIOC_DQBUF10. 将缓冲重新入队列尾,这样可以循环采集。
VIDIOC_QBUF11. 停止视频的采集。
VIDIOC_STREAMOFF12. 关闭视频设备。
close(fd);三、常用的结构体(参见/usr/include/linux/videodev2.h):struct v4l2_requestbuffers reqbufs;//向驱动申请帧缓冲的请求,里面包含申请的个数struct v4l2_capability cap;//这个设备的功能,比如是否是视频输入设备struct v4l2_input input; //视频输入struct v4l2_standard std;//视频的制式,比如PAL,NTSCstruct v4l2_format fmt;//帧的格式,比如宽度,高度等struct v4l2_buffer buf;//代表驱动中的一帧v4l2_std_id stdid;//视频制式,例如:V4L2_STD_PAL_Bstruct v4l2_queryctrl query;//查询的控制struct v4l2_control control;//具体控制的值下面具体说明开发流程(网上找的啦,也在学习么)打开视频设备在V4L2中,视频设备被看做一个文件。
linux驱动开发知识点总结
linux驱动开发知识点总结Linux驱动开发是指在Linux操作系统下开发和编写设备驱动程序的过程。
Linux作为一种开源操作系统,具有广泛的应用领域,因此对于驱动开发的需求也非常重要。
本文将从驱动程序的概念、驱动开发的基本步骤、常用的驱动类型以及驱动开发的注意事项等方面进行总结。
一、驱动程序的概念驱动程序是指控制计算机硬件和软件之间通信和交互的程序。
在Linux系统中,驱动程序负责与硬件设备进行交互,实现对硬件的控制和管理。
二、驱动开发的基本步骤1. 确定驱动的类型:驱动程序可以分为字符设备驱动、块设备驱动和网络设备驱动等。
根据具体的硬件设备类型和需求,选择合适的驱动类型。
2. 编写设备注册函数:设备注册函数用于向系统注册设备,使系统能够识别和管理该设备。
3. 实现设备的打开、关闭和读写操作:根据设备的具体功能和使用方式,编写设备的打开、关闭和读写操作函数。
4. 实现设备的中断处理:如果设备需要进行中断处理,可以编写中断处理函数来处理设备的中断请求。
5. 编写设备的控制函数:根据设备的需求,编写相应的控制函数来实现对设备的控制和配置。
6. 编译和安装驱动程序:将编写好的驱动程序进行编译,并将生成的驱动模块安装到系统中。
三、常用的驱动类型1. 字符设备驱动:用于控制字符设备,如串口、打印机等。
字符设备驱动以字符流的方式进行数据传输。
2. 块设备驱动:用于控制块设备,如硬盘、U盘等。
块设备驱动以块为单位进行数据传输。
3. 网络设备驱动:用于控制网络设备,如网卡。
网络设备驱动实现了数据包的收发和网络协议的处理。
4. 触摸屏驱动:用于控制触摸屏设备,实现触摸操作的识别和处理。
5. 显示驱动:用于控制显示设备,实现图像的显示和刷新。
四、驱动开发的注意事项1. 熟悉硬件设备的规格和寄存器的使用方法,了解硬件设备的工作原理。
2. 确保驱动程序的稳定性和可靠性,避免出现系统崩溃或死机等问题。
3. 对于需要频繁访问的设备,要考虑性能问题,尽量减少对硬件的访问次数。
linux驱动21页PPT
设备文件和设备驱动
设备文件和设备驱动
设备文件是文件系统上的一个 节点,是一种特殊的文件,叫 做设备文件。每个设备文件在 用户空间代表了一个设备。
设备文件一般存在/dev目录下, 用mknod命令创建。
/proc/ioports:查看设备的IO端口。 /proc/interrupts:查看正在使用的中断号。
构造和运行模块
Kernel Module的特点
模块只是预先注册自己以便服务于将来的某个请求,然后就立即 结束。
模块可以是实现驱动程序,文件系统,或者其他功能。 加载模块后,模块运行在内核空间,和内核链接为一体。
#include <linux/module.h>
int init_module(void) {
printk("<1>Hello, world\n"); return 0; } void cleanup_module(void) { printk("<1>Goodbye world\n"); }
简单的内核模块例子(2)
SUMMER TEMPLATE
linux驱动
Linux Kernel 系统架构图
设备驱动程序简介
驱动程序的特点
是应用和硬件设备之间的一个软件层 。
这个软件层一般在内核中实现
设备驱动程序的作用在于提供机制,而不是提供策略, 编写访问硬件的内核代码时不要给用户强加任何策略
○ 机制:驱动程序能实现什么功能。
1version>/modules.dep文件,其中<kernel version>
LINUX设备驱动开发详解
LINUX设备驱动开发详解概述LINUX设备驱动开发是一项非常重要的任务,它使得硬件设备能够与操作系统进行有效地交互。
本文将详细介绍LINUX设备驱动开发的基本概念、流程和常用工具,帮助读者了解设备驱动开发的要点和技巧。
设备驱动的基本概念设备驱动是连接硬件设备和操作系统的桥梁,它负责处理硬件设备的输入和输出,并提供相应的接口供操作系统调用。
设备驱动一般由设备驱动程序和设备配置信息组成。
设备驱动程序是编写解决设备驱动的代码,它负责完成设备初始化、IO操作、中断处理、设备状态管理等任务。
设备驱动程序一般由C语言编写,使用Linux内核提供的API函数进行开发。
设备配置信息是定义硬件设备的相关参数和寄存器配置的文件,它告诉操作系统如何与硬件设备进行交互。
设备配置信息一般以设备树或者直接编码在设备驱动程序中。
设备驱动的开发流程设备驱动的开发流程包括设备初始化、设备注册、设备操作函数编写和设备驱动注册等几个主要步骤。
下面将详细介绍这些步骤。
设备初始化设备初始化是设备驱动开发的第一步,它包括硬件初始化和内存分配两个主要任务。
硬件初始化是对硬件设备进行基本的初始化工作,包括寄存器配置、中断初始化等。
通过操作设备的寄存器,将设备设置为所需的状态。
内存分配是为设备驱动程序分配内存空间以便于执行。
在设备初始化阶段,通常需要为设备驱动程序分配一块连续的物理内存空间。
设备注册设备注册是将设备驱动程序与设备对象进行关联的过程,它使得操作系统能够正确地管理设备。
设备注册包括设备号分配、设备文件创建等操作。
设备号是设备在系统中的唯一标识符,通过设备号可以找到设备对象对应的设备驱动程序。
设备号分配通常由操作系统负责,设备驱动程序通过注册函数来获取设备号。
设备文件是用户通过应用程序访问设备的接口,它是操作系统中的一个特殊文件。
设备文件的创建需要通过设备号和驱动程序的注册函数来完成。
设备操作函数编写设备操作函数是设备驱动程序的核心部分,它包括设备打开、设备关闭、读和写等操作。
Linux网络设备驱动_PCI网卡
⏹ Linux 网络设备驱动结构Linux的加载和卸载设备的注册初始化和注销设备的打开和释放据包的发送和接收络连接状况数设置和统计数据此驱动所支持的网卡系列初始化网络设备注销网络设备设备挂起函数设备恢复函数打开网络设备关闭网络设备读取包的网卡收发包的状态,统计数据用户的ioctl 命令系统调用硬件处理数据包发送ISR 数据包发送和接收⏹ struct pci_driver如果网络设备(包括wireless )是PCI 规范的,则先是向内核注册该PCI 设备(pci_register_driver),然后由pci_driver 数据结构中的probe 函数指针所指向的侦测函数来初始化该PCI 设备,并且同时注册和初始化该网络设备。
如果网络设备(包括wireless )是PCMCIA 规范的,则先是向内核注册该PCMCIA 设备(register_pccard_driver),然后driver_info_t 数据结构中的attach 函数指针所指向的侦测函数来初始化该PCMCIA 设备,并且同时注册和初始化该网络设备。
1. 申明为PCI 设备:static struct pci_driver tg3_driver = {.name = DRV_MODULE_NAME,//此驱动所支持的网卡系列,vendor_id, device_id.id_table = tg3_pci_tbl,//初始化网络设备的回调函数.probe = tg3_init_one,//注销网络设备的回调函数.remove = __devexit_p(tg3_remove_one),//设备挂起函数.suspend = tg3_suspend,//设备恢复函数.resume = tg3_resume};2. 驱动模块的加载和卸载static int __init tg3_init(void){//先注册成PCI设备,并初始化,如果是其他的ESIA,PCMCIA,用其他函数return pci_module_init(&tg3_driver);}static void __exit tg3_cleanup(void){pci_unregister_driver(&tg3_driver);//注销PCI设备}module_init(tg3_init); //驱动模块的加载module_exit(tg3_cleanup); //驱动模块的卸载3. PCI设备探测函数probe,初始化网络设备主要工作:申请并设置pci资源(内存),申请并设置net_device网络设备结构,IO映射,注册网络设备static int __devinit tg3_init_one(struct pci_dev *pdev, const struct pci_device_id *ent){//初始化设备,使I/O,memory可用,唤醒设备pci_enable_device(pdev);//申请内存空间,配置网卡的I/O,memory资源pci_request_regions(pdev, DRV_MODULE_NAME);pci_set_master(pdev);//设置DMA属性pci_set_dma_mask(pdev, (u64) 0xffffffffffffffff);//网卡 I/O,memory资源的启始地址tg3reg_base = pci_resource_start(pdev, 0);//网卡I/O,memory资源的大小tg3reg_len = pci_resource_len(pdev, 0);//分配并设置网络设备dev = alloc_etherdev(sizeof(*tp));//申明为内核设备模块SET_MODULE_OWNER(dev);//初始化私有结构中的各成员值tp = dev->priv;tp->pdev = pdev;tp->dev = dev;……//锁的初始化spin_lock_init(&tp->lock);//映射I/O,memory地址到私有域中的寄存器结构tp->regs = (unsigned long) ioremap(tg3reg_base, tg3reg_len);dev->irq = pdev->irq;//网络设备回调函数赋值dev->open = tg3_open;dev->stop = tg3_close;dev->get_stats = tg3_get_stats;dev->set_multicast_list = tg3_set_rx_mode;dev->set_mac_aDDRess = tg3_set_mac_addr;dev->do_ioctl = tg3_ioctl;dev->tx_timeout = tg3_tx_timeout;dev->hard_start_xmit= tg3_start_xmit;//网卡的MAC地址赋值dev->addrtg3_get_device_address(tp);//注册网络设备register_netdev(dev);//把网络设备指针地址放入PCI设备中的设备指针中pci_set_drvdata(pdev, dev);}4. 注销网络设备主要工作:注销并释放网络设备,取消地址映射,释放PCI资源static void __devexit tg3_remove_one(struct pci_dev *pdev){struct net_device *dev = pci_get_drvdata(pdev);//注销网络设备unregister_netdev(dev);//取消地址映射iounmap((void *) ((struct tg3 *)(dev->priv))->regs);//释放网络设备kfree(dev);//释放PCI资源pci_release_regions(pdev);//停用PCI设备pci_disable_device(pdev);//PCI设备中的设备指针赋空pci_set_drvdata(pdev, NULL);}5. 网络设备挂起主要工作:停用网卡的中断寄存器,停止网卡收发包,停用网卡某些硬件,设置电源状态static int tg3_suspend(struct pci_dev *pdev, u32 state){//停用网卡的中断寄存器tg3_disable_ints(tp);//停止网卡收发包netif_device_detach(dev);//停止网卡某些硬件,fireware的一些功能tg3_halt(tp);//设置网卡的电源状态tg3_set_power_state(tp, state);}6. 网络设备恢复主要工作:恢复网卡电源,允许收发包,初始化收发包的缓冲区,初始化网卡硬件,打开网卡中断寄存器static int tg3_resume(struct pci_dev *pdev){//恢复网卡电源tg3_set_power_state(tp, 0);//允许网卡收发包netif_device_attach(dev);//初始化收发包的缓冲区tg3_init_rings(tp);//初始化网卡硬件tg3_init_hw(tp);//打开网卡中断寄存器tg3_enable_ints(tp);}struct net_device1. 打开网络设备主要工作:分配中断及注册中断处理函数,初始化硬件及收发缓冲区,初始化定时器及注册超时函数,允许网卡开始传送包static int tg3_open(struct net_device *dev){//分配一个中断request_irq(dev->irq, tg3_interrupt, SA_SHIRQ, dev->name, dev);/* int request_irq(unsigned int irq,void (*handler)(int irq, void *dev_id, struct pt_regs *regs),unsigned long irqflags,const char * devname,void *dev_id);irq是要申请的硬件中断号。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
Linux设备驱动操作系统的目的之一就是将系统硬件设备细节从用户视线中隐藏起来。
例如虚拟文件系统对各种类型已安装的文件系统提供了统一的视图而屏蔽了具体底层细节。
本章将描叙Linux核心对系统中物理设备的管理。
CPU并不是系统中唯一的智能设备,每个物理设备都拥有自己的控制器。
键盘、鼠标和串行口由一个高级I/O芯片统一管理,IDE控制器控制IDE硬盘而SCSI控制器控制SCSI硬盘等等。
每个硬件控制器都有各自的控制和状态寄存器(CSR)并且各不相同。
例如Adaptec 2940 SCSI控制器的CSR与NCR 810 SCSI控制器完全不一样。
这些CSR被用来启动和停止,初始化设备及对设备进行诊断。
在Linux中管理硬件设备控制器的代码并没有放置在每个应用程序中而是由内核统一管理。
这些处理和管理硬件控制器的软件就是设备驱动。
Linux 核心设备驱动是一组运行在特权级上的内存驻留底层硬件处理共享库。
正是它们负责管理各个设备。
设备驱动的一个基本特征是设备处理的抽象概念。
所有硬件设备都被看成普通文件;可以通过和操纵普通文件相同的标准系统调用来打开、关闭、读取和写入设备。
系统中每个设备都用一种特殊的设备相关文件来表示(device special file),例如系统中第一个IDE硬盘被表示成/dev/hda。
块(磁盘)设备和字符设备的设备相关文件可以通过mknod命令来创建,并使用主从设备号来描叙此设备。
网络设备也用设备相关文件来表示,但Linux寻找和初始化网络设备时才建立这种文件。
由同一个设备驱动控制的所有设备具有相同的主设备号。
从设备号则被用来区分具有相同主设备号且由相同设备驱动控制的不同设备。
例如主IDE硬盘的每个分区的从设备号都不相同。
如/dev/hda2表示主IDE 硬盘的主设备号为3而从设备号为2。
Linux通过使用主从设备号将包含在系统调用中的(如将一个文件系统mount到一个块设备)设备相关文件映射到设备的设备驱动以及大量系统表格中,如字符设备表,chrdevs。
Linux支持三类硬件设备:字符、块及网络设备。
字符设备指那些无需缓冲直接读写的设备,如系统的串口设备/dev/cua0和/dev/cua1。
块设备则仅能以块为单位读写,典型的块大小为512或1024字节。
块设备的存取是通过buffer cache来进行并且可以进行随机访问,即不管块位于设备中何处都可以对其进行读写。
块设备可以通过其设备相关文件进行访问,但更为平常的访问方法是通过文件系统。
只有块设备才能支持可安装文件系统。
网络设备可以通过BSD套接口访问,我们将在网络一章中讨论网络子系统。
Linux核心中虽存在许多不同的设备驱动但它们具有一些共性:核心代码设备驱动是核心的一部分,象核心中其它代码一样,出错将导致系统的严重损伤。
一个编写奇差的设备驱动甚至能使系统崩溃并导致文件系统的破坏和数据丢失。
核心接口设备驱动必须为Linux核心或者其从属子系统提供一个标准接口。
例如终端驱动为Linux核心提供了一个文件I/O接口而SCSI设备驱动为SCSI子系统提供了一个SCSI设备接口,同时此子系统为核心提供了文件I/O和buffer cache接口。
核心机制与服务设备驱动可以使用标准的核心服务如内存分配、中断发送和等待队列等等。
动态可加载多数Linux设备驱动可以在核心模块发出加载请求时加载,同时在不再使用时卸载。
这样核心能有效地利用系统资源。
可配置Linux设备驱动可以连接到核心中。
当核心被编译时,哪些核心被连入核心是可配置的。
动态性当系统启动及设备驱动初始化时将查找它所控制的硬件设备。
如果某个设备的驱动为一个空过程并不会有什么问题。
此时此设备驱动仅仅是一个冗余的程序,它除了会占用少量系统内存外不会对系统造成什么危害。
8.1轮询与中断设备被执行某个命令时,如"将读取磁头移动到软盘的第42扇区上",设备驱动可以从轮询方式和中断方式中选择一种以判断设备是否已经完成此命令。
轮询方式意味着需要经常读取设备的状态,一直到设备状态表明请求已经完成为止。
如果设备驱动被连接进入核心,这时使用轮询方式将会带来灾难性后果:核心将在此过程中无所事事,直到设备完成此请求。
但是轮询设备驱动可以通过使用系统定时器,使核心周期性调用设备驱动中的某个例程来检查设备状态。
定时器过程可以检查命令状态及Linux软盘驱动的工作情况。
使用定时器是轮询方式中最好的一种,但更有效的方法是使用中断。
基于中断的设备驱动会在它所控制的硬件设备需要服务时引发一个硬件中断。
如以太网设备驱动从网络上接收到一个以太数据报时都将引起中断。
Linux 核心需要将来自硬件设备的中断传递到相应的设备驱动。
这个过程由设备驱动向核心注册其使用的中断来协助完成。
此中断处理例程的地址和中断号都将被记录下来。
在/proc/interrupts文件中你可以看到设备驱动所对应的中断号及类型:0:727432 timer 1:20534 keyboard 2:0 cascade 3:79691+serial 4:28258+serial 5:1 sound blaster 11:20868+aic7xxx 13:1 math error 14:247+ide0 15:170+ide1对中断资源的请求在驱动初始化时就已经完成。
作为IBM PC体系结构的遗产,系统中有些中断已经固定。
例如软盘控制器总是使用中断6。
其它中断,如PCI设备中断,在启动时进行动态分配。
设备驱动必须在取得对此中断的所有权之前找到它所控制设备的中断号(IRQ)。
Linux通过支持标准的PCI BIOS回调函数来确定系统中PCI设备的中断信息,包括其IRQ号。
如何将中断发送给CPU本身取决于体系结构,但是在多数体系结构中,中断以一种特殊模式发送同时还将阻止系统中其它中断的产生。
设备驱动在其中断处理过程中作的越少越好,这样Linux核心将能很快的处理完中断并返回中断前的状态中。
为了在接收中断时完成大量工作,设备驱动必须能够使用核心的底层处理例程或者任务队列来对以后需要调用的那些例程进行排队。
8.2直接内存访问(DMA)数据量比较少时,使用中断驱动设备驱动程序能顺利地在硬件设备和内存之间交换数据。
例如波特率为9600的modem可以每毫秒传输一个字符。
如果硬件设备引起中断和调用设备驱动中断所消耗的中断时延比较大(如2毫秒)则系统的综合数据传输率会很低。
则9600波特率modem的数据传输只能利用0.002%的CPU处理时间。
高速设备如硬盘控制器或者以太网设备数据传输率将更高。
SCSI设备的数据传输率可达到每秒40M字节。
直接内存存取(DMA)是解决此类问题的有效方法。
DMA控制器可以在不受处理器干预的情况下在设备和系统内存之间高速传输数据。
PC机的ISA DMA控制器有8个DMA通道,其中七个可以由设备驱动使用。
每个DMA通道具有一个16位的地址寄存器和一个16位的记数寄存器。
为了初始化数据传输,设备驱动将设置DMA通道地址和记数寄存器以描叙数据传输方向以及读写类型。
然后通知设备可以在任何时候启动DMA操作。
传输结束时设备将中断PC。
在传输过程中CPU可以转去执行其他任务。
设备驱动使用DMA时必须十分小心。
首先DMA控制器没有任何虚拟内存的概念,它只存取系统中的物理内存。
同时用作DMA传输缓冲的内存空间必须是连续物理内存块。
这意味着不能在进程虚拟地址空间内直接使用DMA。
但是你可以将进程的物理页面加锁以防止在DMA操作过程中被交换到交换设备上去。
另外DMA控制器所存取物理内存有限。
DMA通道地址寄存器代表DMA地址的高16位而页面寄存器记录的是其余8位。
所以DMA请求被限制到内存最低16M字节中。
DMA通道是非常珍贵的资源,一共才有7个并且还不能够在设备驱动间共享。
与中断一样,设备驱动必须找到它应该使用那个DMA通道。
有些设备使用固定的DMA通道。
例如软盘设备总使用DMA通道2。
有时设备的DMA通道可以由跳线来设置,许多以太网设备使用这种技术。
设计灵活的设备将告诉系统它将使用哪个DMA通道,此时设备驱动仅需要从DMA通道中选取即可。
Linux通过dma_chan(每个DMA通道一个)数组来跟踪DMA通道的使用情况。
dma_chan结构中包含有两个域,一个是指向此DMA通道拥有者的指针,另一个指示DMA通道是否已经被分配出去。
当敲入cat/proc/dma打印出来的结果就是dma_chan结构数组。
8.3内存设备驱动必须谨慎使用内存。
由于它属于核心,所以不能使用虚拟内存。
系统接收到中断信号时或调度底层任务队列处理过程时,设备驱动将开始运行,而当前进程会发生改变。
设备驱动不能依赖于任何运行的特定进程,即使当前是为该进程工作。
与核心的其它部分一样,设备驱动使用数据结构来描叙它所控制的设备。
这些结构被设备驱动代码以静态方式分配,但会增大核心而引起空间的浪费。
多数设备驱动使用核心中非页面内存来存储数据。
Linux为设备驱动提供了一组核心内存分配与回收过程。
核心内存以2的次幂大小的块来分配。
如512或128字节,此时即使设备驱动的需求小于这个数量也会分配这么多。
所以设备驱动的内存分配请求可得到以块大小为边界的内存。
这样核心进行空闲块组合更加容易。
请求分配核心内存时Linux需要完成许多额外的工作。
如果系统中空闲内存数量较少,则可能需要丢弃些物理页面或将其写入交换设备。
一般情况下Linux将挂起请求者并将此进程放置到等待队列中直到系统中有足够的物理内存为止。
不是所有的设备驱动(或者真正的Linux核心代码)都会经历这个过程,所以如分配核心内存的请求不能立刻得到满足,则此请求可能会失败。
如果设备驱动希望在此内存中进行DMA,那么它必须将此内存设置为DMA使能的。
这也是为什么是Linux核心而不是设备驱动需要了解系统中的DMA使能内存的原因。
8.4设备驱动与核心的接口Linux核心与设备驱动之间必须有一个以标准方式进行互操作的接口。
每一类设备驱动:字符设备、块设备及网络设备都提供了通用接口以便在需要时为核心提供服务。
这种通用接口使得核心可以以相同的方式来对待不同的设备及设备驱动。
如SCSI和IDE硬盘的区别很大但Linux对它们使用相同的接口。
Linux动态性很强。
每次Linux核心启动时如遇到不同的物理设备将需要不同的物理设备驱动。
Linux允许通过配置脚本在核心重建时将设备驱动包含在内。
设备驱动在启动初始化时可能会发现系统中根本没有任何硬件需要控制。
其它设备驱动可以在必要时作为核心模块动态加载到。
为了处理设备驱动的动态属性,设备驱动在初始化时将其注册到核心中去。
Linux维护着已注册设备驱动表作为和设备驱动的接口。