第6章 设备驱动程序(6)

目录

6.7 总线系统

6.7.2 PCI总线

6.7.3 USB

6.8 小结


本专栏文章将有70篇左右,欢迎+关注,查看后续文章。

6.7 总线系统

6.7.2 PCI总线

PCI由Intel开发,用于替代ISA。

PCI已过时,目前采用PCIe。

PCI特点:

        高带宽传输。

        易配置。

        与平台无关。

1. PCI系统的布局

设备标识

PCI总线上的每个设备,有3个编号:

        1. 总线编号(bus number):

                设备所在总线的编号。

                系统最多255个总线。

        2. 插槽编号(slot number):

                一个总线内部编号。不同总线设备插槽编号可能相同。

                一个总线最多有32个设备插槽。

        3. 功能编号(function number):

                区分一个扩展卡上多个不同设备。

                最大为8。

每个设备有一个16位编号标识:

        8位总线编号 + 5位插槽编号 + 3位功能编号         最大值255/32/8

地址空间

PCI设备有三个地址空间:

        IO空间:32bit,最大4G空间。

        数据空间:32bit或64bit 根据CPU位数决定。

        配置空间:包含vendor,设备类型等信息。

三个地址空间可被映射到系统的虚拟内存中。

配置信息

PCI总线无跳线,支持可通过软件配置。即使用配置空间。

PCI设备的配置空间:

        256字节。

                前64字节是标准化的,但都是非强制,而某些项是可选的。

                剩余空间自由使用。

前64字节如上图,深色部分是强制要求的。

配置空间介绍:

        Vendor ID:厂商ID,由PCI组织分配。

        Device ID:由厂商自己内部分配。

        Rev ID:版本ID,可用于选择驱动版本。

        Class Code:用于将设备分为不同功能组,共24bit。

                前8bit为基类,后16bit是子类。如:

                        基类:存储(PCI_BASE_CLASS_STORAGE)

                                子类:

                                        PCI_CLASS_STORAGE_SCSI

                                        PCI_CLASS_STORAGE_SATA

                                        PCI_CLASS_STORAGE_IDE

                                        PCI_CLASS_STORAGE_SAS

                基类:网络(PCI_BASE_CLASS_NETWORK)

                        子类:

                                PCI_CLASS_NETWORK_ETHERNET

                                PCI_CLASS_NETWORK_TOKEN_RING

                                PCI_CLASS_NETWORK_FDDI

                                PCI_CLASS_NETWORK_ATM

        基类:系统组件(PCI_BASE_CLASS_SYSTEM)

                子类:

                        PCI_CLASS_SYSTEM_DMA

                        PCI_CLASS_SYSTEM_TIMER

                        PCI_CLASS_SYSTEM_RTC

Base Address 0-5: 32bit

        指定了设备内存或I/O端口在PCI总线地址空间中的位置(可映射到内存)。

        64位设备中可两两合并3个base address。

IRQ Line:

        值范围:0-255

        用于指定设备使用的中断。

其余字段由硬件使用。

2. 内核中的实现

数据结构

struct pci_bus:

        一个PCI总线。

struct pci_dev:

        一个PCI设备。

struct pci_driver:

        一个PCI驱动。

struct list_head         pci_root_buses:

        作用:连接系统所有PCI总线,用于遍历。

总线的表示

struct         pci_bus {

        struct list_head         node;         //用于连接到全局总线链表

        struct pci_bus         *parent;       //父总线

        struct list_head        children;     //子总线链表

        struct list_head        devices;         //该总线上的所有设备

        struct pci_dev         *self;

                //对于桥设备,指向该桥设备的struct pci_dev实例。

        struct list_head         slots;

                //连接该总线的所有插槽。

        struct resource         *resource[PCI_BRIDGE_RESOURCE_NUM];

                //该总线的系统资源,如IO端口,IO内存。

        struct pci_ops         *ops;

                //用于访问配置空间的函数,如:

                总线扫描、设备枚举、资源分配与释放、电源管理等。

        void         *sysdata;

        struct proc_dir_entry         *procdir;         //向/proc/bus/pci导出总线信息

        unsigned char                    number;      //总线编号

        unsigned char                    primary;

                //桥设备创建的子总线,指示该总线是否为主桥。

        char                                   name[48];         //如:"PCI Bus #01"

        struct device                      dev;

};

除总线0外,其余总线可只通过一个PCI桥接器寻址。

        桥接器:用于连接PCI总线,也算一个PCI设备。

设备管理

pci_dev:

        描述一个PCI设备。

struct         pci_dev {

        struct list_head         bus_list;         //连接同一PCI总线下的PCI设备。

        struct pci_bus           *bus;             //所属总线。

        struct pci_bus           *subordinate;

                //若为PCI桥,用于连接该PCI桥下总线上的设备。

    

        struct proc_dir_entry         *procent;

                //proc文件系统中表示。

        struct pci_slot                    *slot;

                //该设备所在插槽。

        unsigned short         vendor;

        unsigned short         device;

                //该设备的vendor ID和device ID。

        struct pci_driver   *driver;         //该设备的驱动。

        struct device         dev;             //内核通用设备模型。

        unsigned int         irq;                //该设备的中断号。

        struct resource         resource[DEVICE_COUNT_RESOURCE];

                //IO端口,IO内存等资源。

};

驱动程序

pci_driver:表示一个PCI驱动程序。

struct         pci_driver {

        struct list_head         node;         //连接到全局PCI驱动列表。方便遍历查找PCI驱动

        const char                 *name;

        struct pci_device_id         *id_table;

                //定义该驱动可管理的设备列表,每个条目包含Vendor ID、Device ID等标识符。

    

        int         (*probe)(struct pci_dev *dev, const struct pci_device_id *id);

                //热插入一个新PCI设备,且与id_table中一个设备匹配时,调用此回调函数。

        void         (*remove)(struct pci_dev *dev);

                //热拔出一个一个新PCI设备时,调用该函数。

        int         (*suspend)(struct pci_dev *dev, pm_message_t state);

        int         (*resume)(struct pci_dev *dev);

        void         (*shutdown)(struct pci_dev *dev);

                //电源管理。

        struct device_driver         driver;         //通用驱动模型。

};

注册驱动程序

int    pci_register_driver(struct pci_driver   *drv,    struct module    *owner,     const char   *mod_name)

{

        drv->driver.name     =    drv->name;

        drv->driver.bus    =    &pci_bus_type;

        drv->driver.owner    =    owner;

        drv->driver.mod_name     =     mod_name;

        return    driver_register(&drv->driver);

}

一个驱动程序中需填写一个struct pci_device_id数组,并赋值给struct pci_device_id *id_table; 表示该驱动程序可管理的设备列表。

举例:

int __init         my_pci_driver_init(void)

{

        return pci_register_driver(&my_pci_driver);

}

static struct pci_driver         my_pci_driver = {

        .name    =     "my-pci",

        .id_table    =     my_pci_ids,

        .probe     =     my_pci_probe,

        .remove    =     my_pci_remove,

};

static struct pci_device_id         my_pci_ids[] = {

        { PCI_DEVICE(0x10ec, 0x8168) },

        { 0, }

};

其中

#define PCI_DEVICE(vend,dev) \

        .vendor = (vend), .device = (dev), \

        .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID

PCI_ANY_ID: 可以与任何PCI设备ID匹配。

pci_match_id()函数:

        判断指定设备ID是否包含在id_table表中。

6.7.3 USB

USB:即通用串行总线。一种外部总线。

1. 特性和运作模式

USB拓扑如下:

USB驱动设计的核心:

        热插拔。

        驱动透明安装。

设备不直接连接到宿主机控制器,而是连接到集线器。

USB设备分3个层次:

        1. 设备:

                连接到USB总线的设备。

                一个设备可包含几种功能部件,分别由不同驱动控制。

        2. 配置:

                每个设备由一或多个配置组成。

        3. 接口:

                每个配置由一或多个接口组成

        4. 端点end point:

                每个接口由一或多个端点组成。

drivers/usb/下目录将驱动程序分为:

        image:图形/视频设备,如照相机,扫描仪。

        input:输入输出设备。如键盘,鼠标,触摸屏。

        media:多媒体设备。

        net:网卡。

        storage:存储设备。如硬盘。

        core:适配器。

USB有4种传输模式:

        1. 控制传输:

                作用:传输控制信息,配置设备。

                占用带宽少。

                如标准命令:GET_STATUS,SET_INTERFACE。

        2. 块传输bulk transfer:

                作用:传输数据。

                占用全部带宽。

                如存储设备。

        3. 中断传输:

                即周期重复的块传输。

                如网卡。

        4. 同步传输:

                使用预定义带宽,可容忍偶尔数据丢失

                如网络摄像头。

2. 驱动程序管理

USB总线系统包括:

        1. 宿主机适配器的驱动:自身被连接到另一系统总线。

                适配器类型有OHCI,EHCI,UHCI。

        2. 驱动与USB设备通信。

USB有子系统4个任务:

        1. 注册和管理driver

        2. 为USB设备查找driver,及初始化与配置。

        3. 在内存中表示USB设备树。

        4. 设备通信。

struct         usb_driver {

        const char         *name;         //驱动名称。

        int         (*probe) (struct usb_interface *intf, const struct usb_device_id *id);

                //宿主机检测到新设备(id_table中定义的)时调用。

        void      (*disconnect) (struct usb_interface *intf);

        int         (*unlocked_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);

        int         (*reset_resume)(struct usb_interface *intf);

        struct usb_device_id         *id_table;

        struct usbdrv_wrap         drvwrap;

};

struct    usbdrv_wrap {

        struct device_driver         driver; //通用驱动模型。

        int         for_devices;

                //若为0,则为接口驱动。 若为1,则为设备驱动。

};

probe函数和id_tables:用法和PCI一样。

struct         usb_device_id {

        __u16         match_flags;         //检测设备时,指定检查哪些ID。

        __u16         idVendor;

        __u16         idProduct;

        __u16         bcdDevice_lo;

        __u16         bcdDevice_hi;

        __u8         bDeviceClass;

        __u8         bDeviceSubClass;

        __u8         bDeviceProtocol;

        __u8         bInterfaceClass;

        __u8         bInterfaceSubClass;

        __u8         bInterfaceProtocol;

        __u8 bInterfaceNumber;

}

int usb_register(struct usb_driver *driver)

插入设备时,检测到和id_tables中匹配后,调用usb_driver的probe函数,以初始化。

拔出设备时,调用usb_driver的 disconnect函数,来清除资源并卸载设备。

3. 设备树的表示

struct     usb_device {

        int         devnum;         //USB树中全局编号

        char      devpath[16]; //从USB树根节点到设备的经过的集线器端口号。

        u32        route;

        enum usb_device_state         state;

                //已连接/已配置。

        enum usb_device_speed       speed;

                //值为:

                        USB_SPEED_LOW

                        USB_SPEED_HIGH

                        USB_SPEED_SUPER

        struct usb_device         *parent;

                //指向连接的集线器(USB hub)

        struct usb_bus                         *bus;

        struct usb_host_endpoint         ep0;

        struct device                              dev;

        struct usb_device_descriptor     descriptor;

                //厂商ID 产品ID 设备class。

        struct usb_host_config                *config;         //设备可能的配置。

        struct usb_host_config                *actconfig;     //设备当前的配置。

        char                 *product;                   //产品名称。

        char                 *manufacturer;         //生产商。

        char                 *serial;                     //序列号。

        int                    maxchild;                 //如果是集线器,集线器的端口数目。

};

系统可有多个USB树,一颗树就是一根USB总线,而usb_bus_list可连接所有USB总线。

struct         usb_bus {

        struct device         *controller;         //代表总线的硬件

        struct device         *sysdev;

        int                         busnum;             //总线编号,按顺序分配

        const char             *bus_name;

        struct list_head      bus_list;            //用于连接到系统所有总线

        struct usb_devmap         devmap;        //位图,可跟踪已分配的usb编号

        struct usb_device           *root_hub;     //总线设备树根节点,即根集线器。

        int                         bandwidth_allocated;

};

URB:即USB Request Block,底层通过URB和设备交换数据。

6.8 小结

  • 23
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

山下小童

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值