- 论坛徽章:
- 0
|
2009.9.15
PCI device 和device model的交互
![]()
PCI BUS register
struct bus_type pci_bus_type = {
.name = "pci",
.match = pci_bus_match,
.hotplug = pci_hotplug,
.suspend = pci_device_suspend,
.resume = pci_device_resume,
.dev_attrs = pci_dev_attrs,
};
bus_register 之后,创建 /sys/bus/pci 目录(sub system(kset+sem)),和其下的两个目录(两个kset): device和 driver.
PCI DRIVER 注册
注册PCI driver的时候, PCI core做如下初始化
/* initialize common driver fields */
drv->driver.name = drv->name;
drv->driver.bus = &pci_bus_type; /*PCI driver ops*/
drv->driver.probe = pci_device_probe;
drv->driver.remove = pci_device_remove;
drv->driver.kobj.ktype = &pci_driver_kobj_type; /*release, attr,show,store*/
/* register with core */
error = driver_register(&drv->driver); /*设置drv 的obj的parent(通过加入bus下的kset:driver)*/
PCI device 注册
PCI 自动搜索并注册设备
struct pci_dev {
/* ... */
unsigned int devfn;
unsigned short vendor;
unsigned short device;
unsigned short subsystem_vendor;
unsigned short subsystem_device;
unsigned int class;
/* ... */
struct pci_driver *driver;
/* ... */
struct device dev; /*系统设备,包括一个kobj,对应driver指针,设备parent(和kobj体系有所重叠),bus_type等*/
/* ... */
};
PCI device和/sys 的交互
pci_scan_single_device (搜索设置pci特定信息, dev.parent设置为所在bus的bridge设备, kset设置成/sys/device)
pci_bus_add_device-> device_register(&dev->dev); /*以下是2.6.27的代码*/
1. pci_dev:dev.kobj.kset= /sys/device 其kset作为"系统级别"的分类 (device_initialize)
2. 注册kobjec,触发hotplug事件, 设备出现在/sys/device的某个目录 (kset和parent不同)
4. 如果有class, 在这个设备目录下创建符号连接"subsystem"到对应class (这个..名字重复了)
有class的情况下,在设备目录下创建符号连接到对应的class
4. 在bus的device kset下创建符号连接到这个设备(pci_bus_type:->bus_type_private)
5. 在这个设备的目录下创建符号连接到PCI subsystem ,(奇怪的是名字也叫"subsystem",这个才是正确的)
PCI device和 PCI BUS 的交互
1. 添加device的kobject, 触发一个hotplug事件, 发送uevent
2. 设备添加到bus的时候(bus_attach_device),触发 match 过程, (drv->probe 匹配返回1, 不匹配返回0, 出错返回负值(继续寻找))
Remove a Device
PCI也可以支持hotplug, 比如cardbus. 如果不支持,需要测试,可以用fakephe. PCI移除设备主要个工作由pci_remove_bus_device完成 :
1. PCI 资源清理
2. device_unregister
1) 删除sysfs文件
2) 从各种链表摘除
3) 调用kobject_del, 产生hotplug事件,通知usr space
3. kobject del 会递减dev的引用计数,如果是最后一个引用,则
1)调用设备的release
2) 设备的release函数最后调用pci_release_dev (仅仅释放pci dev占用的内存)
Add driver
1. pci_register_driver -> driver_register
2. -> bus_add_driver
1) 看看对应bus是否注册,如果没有就立即返回了
2) 创建driver的对应sysfs目录文件
3) 遍历设备, 认领对应设备
Remove a Driver
1. driver 调用 pci_unregister_driver
2. -> driver_unregister
3. sysfs unregister, 然后调用个个device的release函数
4.最后使用下面代码等待所有引用消失
down(&drv->unload_sem);
up(&drv->unload_sem);
Hotplug
Dynamic Devices
USB,
cardbus, PCMCIA, 1394, PCI hotplug controller. 对于PCI,CardBus,or
PCMCIA, 在调用设备的remove之前, 设备就已经被拔掉了. 这是所有读取PCI的操作,返回全f, 要求能正确处理返回值0xFF才成.
类似:
result = readl(ptr);
if (result = = ~(u32)0) /* card removed */
return -ENODEV;
对USB, 来讲, 所有请求的返回 -ENODEV错误.
The /sbin/hotplug Utility
这个程序是钦定的uevent处理函数, 一般简单处理后传递给/etc/hotplug.d/中的对应程序. 比如像下面这样:
DIR="/etc/hotplug.d"
for I in "${DIR}/$1/"*.hotplug "${DIR}/"default/*.hotplug ; do
if [ -f $I ]; then
test -x $I && $I $1 ;
fi
done
exit 1
参考 : hotplug(8) manpage.
内核传递的参数只有一个, 其他的用环境变量来传递. 参数是对应的kset的name函数产生的, 如果没有那个函数,则名字是kset本身. 一些环境变量总是被设置的:
ACTION : add or remove
DEVPATH: 代表被创建/删除的kobject的目录
SEQNUM : 64bit的序列码, 可以用于保持事件顺序
SUBSYSTEM : 和命令行参数相同
各个bus一般都添加自己的环境变量(kset_hotplug_ops)
IEEE1394 (FireWire)
SUBSYSTEM => ieee1394.
VENDOR_ID 24-bit vendor ID for the IEEE1394 device
MODEL_ID The 24-bit model ID for the IEEE1394 device
GUID The 64-bit GUID for the device
SPECIFIER_ID The 24-bit value specifying the owner of the protocol spec for this device
VERSION
Networking
SUBSYSTEM => net
INTERFACE interface name
PCI
SUBSYSTEM => pci
PCI_CLASS The PCI class number for the device, in hex.
PCI_ID The PCI vendor and device IDs for the device,in hex,combined in the format vendor:device.
PCI_SUBSYS_ID subsys_vendor:subsys_device.
PCI_SLOT_NAME domain:bus:slot:function. An example might be 0000:00:0d.0.
Input
SUBSYSTEM => input
PRODUCT bustype:vendor:product:version.
NAME
PHYS input子系统给定的设备物理地址
input 特定环境变量: EV KEY REL ABS MSC LED SND FF
USB
SUBSYSTEM => usb.
PRODUCT idVendor/idProduct/bcdDevice
TYPE bDeviceClass/bDeviceSubClass/bDeviceProtocol
bDeviceClass =0 时
INTERFACE bInterfaceClass/bInterfaceSubClass/bInterfaceProtocol
配置了:CONFIG_USB_DEVICEFS,
DEVICE : /proc/bus/usb/USB_BUS_NUMBER/USB_DEVICE_NUMBER
3 位bus号, 3 位设备好
SCSI
SUBSYSTEM => scsi
没有其他的, 考其他途径获取信息
Laptop docking stations
SUBSYSTEM => dock
hotplug scripts 和udev 是两个典想的user space空间程序.
Linux hotplug scripts
这
组脚本是最早应用/sbin/hotplug的程序了, 它解析参数,
在/lib/module/KERNEL_VERSION/modules.*map (模块MODULE_DEVICE_TABLE,
depmod生成.) 中寻找合适模块(匹配的都加载,以便寻找最好的), 支持PCI, USB, IEEE1394, INPUT,
ISAPNP, and CCW subsystems.
modporbe也支持这个modules.*map了, 这个脚本可以用modprobe来替换相当部分的代码.
udev
sysfs为了替代devfs而生, devfs的弱点:
1. 需要每个driver都得修改,并且自己在/dev下寻找合适的位置
2. 动态maojor,minor编号支持不好
3. 设备命名规则是在kernel的
4. 无法将一个设备的设备文件名称固定下来(比如重启会变..)
udev 和/sys /sbin/hotplug配合完成这些工作.
*udev 查找/class/下对应目录一个叫dev的文件来确定major,minor
*class_simple_ 借口可以方便的完成这个工作(如果需要自己创建class的话)
Dealing with Firmware
不要用code内嵌代码和从内核打开文件(文件名称被指定了, kernel最好不要有policy). 正确的是:
#include
int request_firmware(const struct firmware **fw, char *name, struct device *device); //name应该是和vendor提供的一样
struct firmware {
size_t size;
u8 *data;
};
void release_firmware(struct firmware *fw);
非睡眠版本:
int request_firmware_nowait(struct module *module,char *name, struct device *device, void *context,
void (*cont)(const struct firmware *fw, void *context));
1. request_firmware调用时, 在/sys/class/firmware下用你的设备名字创建一个目录, 并包含三个属性:
loading :加载数据是置为1, 完成后置为0, 取消写-1
data : 写入数据的二进制属性
device : 符号连接到 /sys/devices 内的设备文件
2. 产生 uevent, 传递FIRMWARE 变量 (名字)
3. 如果10秒钟没有加载就放弃, 这个超时时间/sys/class/firmware/timeout可以设置
3.
本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u2/79526/showart_2124794.html |
|