1. @Documentation/device_drivers.txt文档译稿
barebox中的设备和驱动
---------------------------------------------------------------------------------------------------
这里,我们遵循相当简化的驱动模型.有一个描述系统中出现的特定设备的device结构,一个表示系统中出现的驱动的driver结构.
两个结构通过成员'type'(int)和成员'name'(char*)找到对方.如果这两个成员都匹配,驱动的probe函数会被调用,并以device
结构作为参数.熟悉Linux platform总线的童鞋将会认出这个行为,并且实际上,很多东西都是从Linux 里面"偷"来的.结构的某些
成员将在文档中给出描述.
目前所有的设备和驱动都用简单的链表处理,当涉及到USB和PCI的时候,可能希望有个树结构,但是这也可能会是一笔不必要的开销.
---------------------------------------------------------------------------------------------------
struct device
-------------
char name[MAX_DRIVER_NAME];
这个成员('type'在下面描述)用于和驱动进行匹配.这是一个描述性的名字,可以是MPC5XXX_ether或者imx_serial.
char id[MAX_DRIVER_NAME];
id用于唯一的表示系统中的一个设备.id会在/dev/目录下作为设备的名称出现.通常这是一些类似eth0或者nor0之类.
void *type_data;
某种特殊类型的设备通常需要存储比device结构所能够容纳的更多的信息.这个entry保存了一个指向特定类型的结构的指针
void *priv;
被设备驱动用来存储私有信息.
void *platform_data;
用于从板级代码到设备驱动携带板子专用数据的信息.
struct param_d *param;
设备的参数,参考Documentation/parameters.txt获取更多信息.
struct driver_d
---------------
char name[MAX_DRIVER_NAME];
unsigned long type;
参考上文.
int (*probe) (struct device_d *);
int (*remove)(struct device_d *);
这两个函数在发现一个设备实例或者设备实例丢失的时候调用
ssize_t (*read) (struct device_d*, void* buf, size_t count, ulong offset, ulong flags);
ssize_t (*write) (struct device_d*, const void* buf, size_t count, ulong offset, ulong flags);
标准的read/write函数.这些作为read/write系统调用的响应来实现.驱动不需要实现这些.
void *type_data;
某种程度上因为device结构中已经有了type数据,这个指针有些多余.目前文件系统的实现使用这个域(driver中的type_data)
而以太网驱动则使用device中相同的域(device中的type). 可能二者之一应该被去掉.
struct device_d {
//! This member (and 'type' described below) is used to match with a driver. This is a descriptive
//name and could be MPC5XXX_ether or imx_serial.
char name[MAX_DRIVER_NAME];
//! The id is used to uniquely identify a device in the system. The id will show up under /dev/ as
// the device's name. Usually this is something like eth0 or nor0.
int id;
struct resource *resource;
int num_resources;
void *platform_data; /*! board specific information about this device */
//! Devices of a particular class normaly need to store more
// information than struct device holds.
void *priv;
void *type_data; //! In case this device is a specific device, this pointer
//points to the type specific device, i.e. eth_device
struct driver_d *driver; /*! The driver for this device */
//设备注册的时候将会通过这个list链接到全局设备链表device_list当中.
struct list_head list; /* The list of all devices */
struct list_head children; /* our children */
struct list_head sibling;
struct list_head active; /* The list of all devices which have a driver */
struct device_d *parent; /* our parent, NULL if not present */
struct bus_type *bus;
//! The parameters for this device. This is used to carry information of board specific data from
// the board code to the device driver.
struct list_head parameters;
struct list_head cdevs;
};
struct driver_d {
/*! The name of this driver. Used to match to
* the corresponding device. */
const char *name;
//驱动注册的时候将通过list链接到全局驱动链表driver_list中
struct list_head list;
/*! Called if an instance of a device is found */
int (*probe) (struct device_d *);
/*! Called if an instance of a device is gone. */
void (*remove)(struct device_d *);
void (*info) (struct device_d *);
void (*shortinfo) (struct device_d *);
struct bus_type *bus;
};
struct bus_type {
char *name;
int (*match)(struct device_d *dev, struct driver_d *drv);
int (*probe)(struct device_d *dev);
void (*remove)(struct device_d *dev);
//总线在注册的时候将通过这个list将自己链接到全局总线链表bus_list中
struct list_head list;
};
2.设备、驱动 和 总线 的注册
//设备的注册
register_device(struct device_d *new_device)
+-- list_add_tail(&new_device->list, &device_list);
+-- match()
+-- dev->bus->match(dev, drv);
+-- dev->bus->probe(dev);
+-- list_add(&dev->active, &active);
int register_device(struct device_d *new_device)
{
struct driver_d *drv;
if (new_device->id == DEVICE_ID_DYNAMIC) {
new_device->id = get_free_deviceid(new_device->name);
} else {
if (get_device_by_name_id(new_device->name, new_device->id)) {
eprintf("register_device: already registered %s\n",
dev_name(new_device));
return -EINVAL;
}
}
debug ("register_device: %s\n", dev_name(new_device));
if (!new_device->bus) {
// dev_err(new_device, "no bus type associated. Needs fixup\n");
new_device->bus = &platform_bus;
}
list_add_tail(&new_device->list, &device_list);
INIT_LIST_HEAD(&new_device->children);
INIT_LIST_HEAD(&new_device->cdevs);
INIT_LIST_HEAD(&new_device->parameters);
INIT_LIST_HEAD(&new_device->active);
for_each_driver(drv) {
if (!match(drv, new_device))
break;
}
return 0;
}
EXPORT_SYMBOL(register_device);
static int match(struct driver_d *drv, struct device_d *dev)
{
if (dev->driver)
return -1;
dev->driver = drv;
if (dev->bus != drv->bus) //设备和驱动必须是相同的总线
goto err_out;
if (dev->bus->match(dev, drv)) //如果总线的match()函数存在,则先调用之
goto err_out;
if (dev->bus->probe(dev)) //如果总线的probe()函数存在,则调用之
goto err_out;
list_add(&dev->active, &active); //将设备加入active链表.
return 0;
err_out:
dev->driver = NULL;
return -1;
}
//驱动的注册
register_driver(struct driver_d *drv)
+-- list_add_tail(&drv->list, &driver_list);
+-- match(drv, dev);
+-- dev->bus->match(dev, drv);
+-- dev->bus->probe(dev);
+-- list_add(&dev->active, &active);
int register_driver(struct driver_d *drv)
{
struct device_d *dev = NULL;
debug("register_driver: %s\n", drv->name);
if (!drv->bus) {
// pr_err("driver %s has no bus type associated. Needs fixup\n", drv->name);
drv->bus = &platform_bus;
}
list_add_tail(&drv->list, &driver_list);
if (!drv->info)
drv->info = noinfo;
if (!drv->shortinfo)
drv->shortinfo = noshortinfo;
for_each_device(dev)
match(drv, dev);
return 0;
}
EXPORT_SYMBOL(register_driver);
//总线的注册
//总线本来是由链表bus_list来维护的,但是现在这部分已经被注释掉了,总线是通过内嵌到device_d结构中,之后,
//在遍历driver_list或者device_list的时候,会通过device找到bus,进而找到bus的match()和probe()
//因此,也就没有必要使用bus_list来维护所有的bus_type实例.
##if 0
#LIST_HEAD(bus_list);
#EXPORT_SYMBOL(bus_list);
#int bus_register(struct bus_type *bus){
# list_add_tail(&bus->list, &bus_list);
# return 0;
#}
##endif
//以USB总线为例:
struct bus_type usb_bus_type = {
.name = "usb",
.match = usb_match,
.probe = usb_probe,
.remove = usb_remove,
};
static struct usb_device *usb_alloc_new_device(void)
{
struct usb_device *usbdev = xzalloc(sizeof (*usbdev));
if (!usbdev)
return NULL;
usbdev->devnum = dev_index + 1;
usbdev->maxchild = 0;
usbdev->dev.bus = &usb_bus_type;
usbdev->setup_packet = dma_alloc(sizeof(*usbdev->setup_packet));
usbdev->descriptor = dma_alloc(sizeof(*usbdev->descriptor));
dev_index++;
return usbdev;
}
//如果new_device->bus已经被初始化,则调用bus本身的match()和probe(). 否则初始化为platform_bus.
register_device(struct device_d *new_device)
+-- if (!new_device->bus) {
dev_err(new_device, "no bus type associated. Needs fixup\n");
new_device->bus = &platform_bus;
}
+-- for_each_driver(drv)
if (!match(drv, new_device)) break;
//+-- dev->driver = drv; //设备在这里拿到驱动的指针
//+-- dev->bus->match(dev, drv);
//+-- dev->bus->probe(dev);
//+-- list_add(&dev->active, &active);
//如果drv->bus已经被初始化,则调用bus本身的match()和probe().否则初始化为platform_bus.
usb_driver_register(struct usb_driver *drv)
+-- drv->driver.name = drv->name;
+-- drv->driver.bus = &usb_bus_type;
+-- register_driver(&drv->driver);
+-- if (!drv->bus) {
//pr_err("driver %s has no bus type associated. Needs fixup\n", drv->name);
drv->bus = &platform_bus;
}
+-- for_each_device(dev)
match(drv, dev);
//+-- dev->driver = drv; //设备在这里拿到驱动的指针
//+-- dev->bus->match(dev, drv);
//+-- dev->bus->probe(dev);
//+-- list_add(&dev->active, &active);
//在具体设备的总线驱动代码的bus->probe(struct device_d dev)的实现中,一般都会首先调用dev->driver->probe().
//如果成功则将设备和驱动关联起来,否则直接返回.
3. 具体的设备、驱动和总线
(1). mtd或flash等块设备
(2). platform设备
(3). cdev