Chinaunix首页 | 论坛 | 博客
  • 博客访问: 135212
  • 博文数量: 38
  • 博客积分: 2510
  • 博客等级: 少校
  • 技术积分: 376
  • 用 户 组: 普通用户
  • 注册时间: 2010-05-07 22:44
文章分类
文章存档

2010年(38)

我的朋友

分类: LINUX

2010-06-01 22:41:41

RTL8139too 芯片的主要结构

驱动模块的装载和卸载

当我们正确编译完我们的程序后,我们就需要把生成的目标文件加载到内核中去,我们
会先ifconfig eth0 down 和rmmod 8139too 来卸载正在使用的网卡驱动,然后insmod
8139too.o 把我们的驱动加载进去(其中8139too.o 是我们编译生成的目标文件)。
就像C 程序有主函数main()一样, 模块也有第一个执行 的函数, 即
module_init(rtl8139_init_module);在我们的程序中,rtl8139_init_module()在 insmod 之后首先执行,就像其它的驱动程序一样,驱动程序在使用 insmod 载入时,第一个初呼叫的函数是rtl8139_init_module,在使用 rmmod 移除时,rtl8139_cleanup_module 会被呼叫。

module_init(rtl8139_init_module);
module_exit(rtl8139_cleanup_module);

rtl8139_init_module

static struct pci_driver rtl8139_pci_driver = {
.name = DRV_NAME,
.id_table = rtl8139_pci_tbl,
.probe = rtl8139_init_one,//设备的初始化函数,在侦听设备的时候

调用,也就是初始化函数
.remove = __devexit_p(rtl8139_remove_one),
#ifdef CONFIG_PM
.suspend = rtl8139_suspend,
.resume = rtl8139_resume,
#endif /* CONFIG_PM */
};
// rtl8139_pci_tbl 中是由VerdonID,DeviceID 等组成,表示该设备驱动程序

可以控制的设备,
//每一个PCI 设备在配置空间中固化了自己的基本信息,前面说过内核启动的

时候PCI 总线驱动程序会
//扫描PCI 总线上的设备,并且把这些信息收集起来,并且每一个设备的信息

由一个专门
//的结构保存起来,保存在一个pci_dev 结构中。pci_register_driver 会根据

rtl8139_pci_tbl 中的信息和
//内核中扫描到的对比,如果有匹配的话,就把功能驱动程序和目标设备对应

起来了。
static struct pci_device_id rtl8139_pci_tbl[] = {
// pci_device_id 是内核定义的用来辨别不同PCI 设备的一个结构,

//例如在我们这里0x10ec 代表的是Realtek 公司,我们扫描PCI 设备配置

空间如果发现有
//Realtek 公司制造的设备时,两者就 对上了。当然对上了公司号后还得

看其他的设备号什么的,
//都对上了才说明这个驱动是可以为这个设备服务的。

{0x10ec, 0x8139, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
{0x10ec, 0x8138, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
{0x1113, 0x1211, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
{0x1500, 0x1360, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
{0x4033, 0x1360, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
{0x1186, 0x1300, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
{0x1186, 0x1340, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
{0x13d1, 0xab06, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
{0x1259, 0xa117, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
{0x1259, 0xa11e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
{0x14ea, 0xab06, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
{0x14ea, 0xab07, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
{0x11db, 0x1234, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
{0x1432, 0x9130, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
{0x02ac, 0x1012, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
{0x018a, 0x0106, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
{0x126c, 0x1211, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
{0x1743, 0x8139, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
{0x021b, 0x8139, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
#ifdef CONFIG_SH_SECUREEDGE5410
/* Bogus 8139 silicon reports 8129 without external PROM :-( */
{0x10ec, 0x8129, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
#endif
#ifdef CONFIG_8139TOO_8129
{0x10ec, 0x8129, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8129 },
#endif
/* some crazy cards report invalid vendor ids like
* 0x0001 here. The other ids are valid and constant,
* so we simply don't match on the main vendor id.
*/

{PCI_ANY_ID, 0x8139, 0x10ec, 0x8139, 0, 0, RTL8139 },
{PCI_ANY_ID, 0x8139, 0x1186, 0x1300, 0, 0, RTL8139 },
{PCI_ANY_ID, 0x8139, 0x13d1, 0xab06, 0, 0, RTL8139 },
{0,}
};
MODULE_DEVICE_TABLE (pci, rtl8139_pci_tbl);
//我们注册了一个 PCI 驱动程序

static int __init rtl8139_init_module (void)
{
/* when we're a module, we always print a version message,
* even if no 8139 board is found.
*/

#ifdef MODULE
printk (KERN_INFO RTL8139_DRIVER_NAME "\n");
#endif
return pci_module_init (&rtl8139_pci_driver);
//调用了pci_module_init(),这个函数代码在

Linux/drivers/net/eepro100.c 中,
//并且把 rtl8139_pci_driver

//(这个结构是在我们的驱动代码里定义的,它是驱动程序和PCI 设备联系

的纽带)
//的地址作为参数传给了它。

}

pci_module_init()在驱动代码里没有定义,你一定想到了,它是Linux 内核提供给模块是
一个标准接口,那么这个接口都干了些什么,这个函数。
里面调用了pci_register_driver(),这个函数代码在Linux/drivers/pci/pci.c 中,
pci_register_driver 做了三件事情。
① 是把带过来的参数 rtl8139_pci_driver 在内核中进行了注册,内核中有一个PCI 设备的大
的链表,这里负责把这个PCI 驱动挂到里面去。
② ②是查看总线上所有 PCI 设备(网卡设备属于PCI 设备的一种)的配置空间如果发现标
识信息与rtl8139_pci_driver 中的id_table 相同即rtl8139_pci_tbl,
③是把这个rtl8139_pci_driver 结构挂在这个设备的数据结构(pci_dev)上,表示这个设备,从此就有了自己的驱 动了。而驱动也找到了它服务的对象了。

pci_register_driver(src/include/linux/pci.h)

static inline int __must_check pci_register_driver(struct pci_driver
*driver)
{
return __pci_register_driver(driver, THIS_MODULE, KBUILD_MODNAME);
}

__pci_register_driver(src/drivers/pci/pcidriver.c)

/**
* __pci_register_driver - register a new pci driver
* @drv: the driver structure to register
* @owner: owner module of drv
* @mod_name: module name string
*
* Adds the driver structure to the list of registered drivers.
* Returns a negative value on error, otherwise 0.
* If no error occurred, the driver remains registered even if
* no device was claimed during registration.
*/

int __pci_register_driver(struct pci_driver *drv, struct module *owner,
const char *mod_name)
{
int error;
/* initialize common driver fields */
drv->driver.name = drv->name;
drv->driver.bus = &pci_bus_type;
drv->driver.owner = owner;
drv->driver.mod_name = mod_name;
drv->driver.kobj.ktype = &pci_driver_kobj_type;
spin_lock_init(&drv->dynids.lock);
INIT_LIST_HEAD(&drv->dynids.list);
/* register with core *///是通用设备的注册

error = driver_register(&drv->driver);
if (error)
return error;
error = pci_create_newid_file(drv);// //在sysfs 中建立文件项

if (error)
driver_unregister(&drv->driver);
return error;
}

driver_register(src/drivers/base/driver.c)

/**
* driver_register - register driver with bus
* @drv: driver to register
*
* We pass off most of the work to the bus_add_driver() call,
* since most of the things we have to do deal with the bus
* structures.
*/

int driver_register(struct device_driver * drv)
{
if ((drv->bus->probe && drv->probe) ||
(drv->bus->remove && drv->remove) ||
(drv->bus->shutdown && drv->shutdown)) {
printk(KERN_WARNING "Driver '%s' needs updating - please use
bus_type methods\n"
, drv->name);
}
klist_init(&drv->klist_devices, NULL, NULL);
return bus_add_driver(drv);// //这个函数将驱动加入BUS 链表

}

bus_add_driver(src/drivers/base/bus.c)

/**
* bus_add_driver - Add a driver to the bus.
* @drv: driver.
*
*/

int bus_add_driver(struct device_driver *drv)
{
struct bus_type * bus = get_bus(drv->bus);
int error = 0;
if (!bus)
return -EINVAL;
pr_debug("bus %s: add driver %s\n", bus->name, drv->name);
error = kobject_set_name(&drv->kobj, "%s", drv->name);
if (error)
goto out_put_bus;
drv->kobj.kset = &bus->drivers;
if ((error = kobject_register(&drv->kobj)))
goto out_put_bus;
if (drv->bus->drivers_autoprobe) {
// //将这个驱动与可能的设备捆绑起来

error = driver_attach(drv);
if (error)
goto out_unregister;
}
klist_add_tail(&drv->knode_bus, &bus->klist_drivers);// //加入链表

module_add_driver(drv->owner, drv);
error = driver_add_attrs(bus, drv); //注册属性

if (error) {
/* How the hell do we get out of this pickle? Give up */
printk(KERN_ERR "%s: driver_add_attrs(%s) failed\n",
__FUNCTION__, drv->name);
}
error = add_bind_files(drv);
if (error) {
/* Ditto */
printk(KERN_ERR "%s: add_bind_files(%s) failed\n",
__FUNCTION__, drv->name);
}
return error;
out_unregister:
kobject_unregister(&drv->kobj);
out_put_bus:
put_bus(bus);
return error;
}

driver_attach(src/drivers/base/dd.c)

/** //这是个重要的函数,它会遍历设备链表,试图匹配这个总线上的设备
* driver_attach - try to bind driver to devices.
* @drv: driver.
*
* Walk the list of devices that the bus has on it and try to
* match the driver with each one. If driver_probe_device()
* returns 0 and the @dev->driver is set, we've found a
* compatible pair.
*/

int driver_attach(struct device_driver * drv)
{
return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
}

__driver_attach(src/drivers/base/dd.c)

static int __driver_attach(struct device * dev, void * data)
{
struct device_driver * drv = data;
/*
* Lock device and try to bind to it. We drop the error
* here and always return 0, because we need to keep trying
* to bind to devices and some drivers will return an error
* simply if it didn't support the device.
*
* driver_probe_device() will spit a warning if there
* is an error.
*/

if (dev->parent) /* Needed for USB */
down(&dev->parent->sem);
down(&dev->sem);
if (!dev->driver)// //如果设备没有匹配的驱动才会测试

driver_probe_device(drv, dev);
up(&dev->sem);
if (dev->parent)
up(&dev->parent->sem);
return 0;
}

bus_for_each_dev(src/drivers/base/bus.c)

/**
* bus_for_each_dev - device iterator.
* @bus: bus type.
* @start: device to start iterating from.
* @data: data for the callback.
* @fn: function to be called for each device.
*
* Iterate over @bus's list of devices, and call @fn for each,
* passing it @data. If @start is not NULL, we use that device to
* begin iterating from.
*
* We check the return of @fn each time. If it returns anything
* other than 0, we break out and return that value.
*
* NOTE: The device that returns a non-zero value is not retained
* in any way, nor is its refcount incremented. If the caller needs
* to retain this data, it should do, and increment the reference
* count in the supplied callback.
*/

int bus_for_each_dev(struct bus_type * bus, struct device * start,
void * data, int (*fn)(struct device *, void *))
{
struct klist_iter i;
struct device * dev;
int error = 0;
if (!bus)
return -EINVAL;
klist_iter_init_node(&bus->klist_devices, &i,
(start ? &start->knode_bus : NULL));
while ((dev = next_device(&i)) && !error) //由于error 总是0,所以
会遍历整个链表
error = fn(dev, data);
klist_iter_exit(&i);
return error;
}

driver_probe_device

/**
* driver_probe_device - attempt to bind device & driver together
* @drv: driver to bind a device to
* @dev: device to try to bind to the driver
*
* First, we call the bus's match function, if one present, which should
* compare the device IDs the driver supports with the device IDs of the
* device. Note we don't do this ourselves because we don't know the
* format of the ID structures, nor what is to be considered a match and
* what is not.
*
* This function returns 1 if a match is found, -ENODEV if the device is
* not registered, and 0 otherwise.
*
* This function must be called with @dev->sem held. When called for a
* USB interface, @dev->parent->sem must be held as well.
*/

int driver_probe_device(struct device_driver * drv, struct device * dev)
{
int ret = 0;
if (!device_is_registered(dev))
return -ENODEV;
// //调用match()函数,如果不匹配,则说明这个驱动不合适这个设备,跳过

这个设备。
//由上面的结构可以看到,这个函数是pci_bus_match

if (drv->bus->match && !drv->bus->match(dev, drv))
goto done;
pr_debug("%s: Matched Device %s with Driver %s\n",
drv->bus->name, dev->bus_id, drv->name);
ret = really_probe(dev, drv);
done:
return ret;
}

rtl8139_cleanup_module

static void __exit rtl8139_cleanup_module (void)
{
pci_unregister_driver (&rtl8139_pci_driver);
}


阅读(2445) | 评论(0) | 转发(2) |
给主人留下些什么吧!~~