目录
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和设备交换数据。