平台总线设备模型
2013-10-25 09:23
141 查看
http://blog.csdn.net/mcgrady_tracy/article/details/7210915
平台总线是内核实现的一条虚拟总线,Linux设备模型包含三个重要的元素,总线、设备和驱动,那看看平台总线又是怎样去实现的。
首先看平台总线的定义:
[cpp] view
plaincopy
946 struct bus_type platform_bus_type = {
947 .name = "platform",
948 .dev_attrs = platform_dev_attrs,
949 .match = platform_match,
950 .uevent = platform_uevent,
951 .pm = &platform_dev_pm_ops,
952 };
我们知道总线匹配设备和驱动是通过它的match函数,那具体看看这个函数是怎样实现的。
[cpp] view
plaincopy
606 static int platform_match(struct device *dev, struct device_driver *drv)
607 {
608 struct platform_device *pdev = to_platform_device(dev);
609 struct platform_driver *pdrv = to_platform_driver(drv);
610
611 /* match against the id table first */
612 if (pdrv->id_table)
613 return platform_match_id(pdrv->id_table, pdev) != NULL;
614
615 /* fall-back to driver name match */
616 return (strcmp(pdev->name, drv->name) == 0);
617 }
我们看,如果平台驱动有一个id_table,那就通过函数platform_match_id去匹配,如果没有就比较平台设备的name字段和平台驱动的name字段是否相同,这也就是平台总线的匹配规则。再来看平台总线的注册。
[cpp] view
plaincopy
955 int __init platform_bus_init(void)
956 {
957 int error;
958
959 early_platform_cleanup();
960
961 error = device_register(&platform_bus);
962 if (error)
963 return error;
964 error = bus_register(&platform_bus_type);
965 if (error)
966 device_unregister(&platform_bus);
967 return error;
968 }
我们看平台总线注册就是采用的bus_register函数,再看在注册平台总线之前,还调用了
device_register去注册了一个设备,因为总线它也是一个设备,也要被注册进内核。那就具体来看这个设备是怎么定义的。
[cpp] view
plaincopy
28 struct device platform_bus = {
29 .init_name = "platform",
30 };
31 EXPORT_SYMBOL_GPL(platform_bus);
我们看就给了一个名字。看完了总线,又来看看平台设备又是怎样定义怎样去注册。
[cpp] view
plaincopy
17 struct platform_device {
18 const char * name;
19 int id;
20 struct device dev;
21 u32 num_resources;
22 struct resource * resource;
23
24 struct platform_device_id *id_entry;
25
26 /* arch specific additions */
27 struct pdev_archdata archdata;
28 };
其中有个重要的元素resource,该元素存入的最重要的设备资源信息,比如I/O基地址,中断号等等。structresource结构定义在include/linux/ioport.h中
[cpp] view
plaincopy
18 struct resource {
19 resource_size_t start;
20 resource_size_t end;
21 const char *name;
22 unsigned long flags;
23 struct resource *parent, *sibling, *child;
24 };
有可能设备的资源不只一个,定义资源时定义成一个数组的形式,那就使用函数去获取,
platform_get_resource就是用来获取设备的资源信息,去看看这个函数
[cpp] view
plaincopy
33 /**
34 * platform_get_resource - get a resource for a device
35 * @dev: platform device
36 * @type: resource type
37 * @num: resource index
38 */
39 struct resource *platform_get_resource(struct platform_device *dev,
40 unsigned int type, unsigned int num)
41 {
42 int i;
43
44 for (i = 0; i < dev->num_resources; i++) {
45 struct resource *r = &dev->resource[i];
46
47 if (type == resource_type(r) && num-- == 0)
48 return r;
49 }
50 return NULL;
51 }
52 EXPORT_SYMBOL_GPL(platform_get_resource);
这个函数的第一个参数为要获取资源的平台设备,第二个参数为资源类型,比如IORESOURCE_MEM,第三个参数为资源在数组中的一个号。如果是获取中断号,还可以使用函数platform_get_irq
[cpp] view
plaincopy
54 /**
55 * platform_get_irq - get an IRQ for a device
56 * @dev: platform device
57 * @num: IRQ number index
58 */
59 int platform_get_irq(struct platform_device *dev, unsigned int num)
60 {
61 struct resource *r = platform_get_resource(dev, IORESOURCE_IRQ, num);
62
63 return r ? r->start : -ENXIO;
64 }
65 EXPORT_SYMBOL_GPL(platform_get_irq);
我们看这个函数也是调用platform_get_resource去获取资源,只是它获取资源的类型为IORESOURCE_IRQ,最后返回中断号。
再来看平台设备的注册,平台设备注册采用platform_device_register函数
[cpp] view
plaincopy
54 /**
55 * platform_get_irq - get an IRQ for a device
56 * @dev: platform device
57 * @num: IRQ number index
58 */
59 int platform_get_irq(struct platform_device *dev, unsigned int num)
60 {
61 struct resource *r = platform_get_resource(dev, IORESOURCE_IRQ, num);
62
63 return r ? r->start : -ENXIO;
64 }
65 EXPORT_SYMBOL_GPL(platform_get_irq);
device_initialize就是device_register那的函数,那就看platform_device_add
[cpp] view
plaincopy
227 /**
228 * platform_device_add - add a platform device to device hierarchy
229 * @pdev: platform device we're adding
230 *
231 * This is part 2 of platform_device_register(), though may be called
232 * separately _iff_ pdev was allocated by platform_device_alloc().
233 */
234 int platform_device_add(struct platform_device *pdev)
235 {
236 int i, ret = 0;
237
238 if (!pdev)
239 return -EINVAL;
240
241 if (!pdev->dev.parent)
242 pdev->dev.parent = &platform_bus;
243
244 pdev->dev.bus = &platform_bus_type;
245
246 if (pdev->id != -1)
247 dev_set_name(&pdev->dev, "%s.%d", pdev->name, pdev->id);
248 else
249 dev_set_name(&pdev->dev, "%s", pdev->name);
250
251 for (i = 0; i < pdev->num_resources; i++) {
252 struct resource *p, *r = &pdev->resource[i];
253
254 if (r->name == NULL)
255 r->name = dev_name(&pdev->dev);
256
257 p = r->parent;
258 if (!p) {
259 if (resource_type(r) == IORESOURCE_MEM)
260 p = &iomem_resource;
261 else if (resource_type(r) == IORESOURCE_IO)
262 p = &ioport_resource;
263 }
264
265 if (p && insert_resource(p, r)) {
266 printk(KERN_ERR
267 "%s: failed to claim resource %d\n",
268 dev_name(&pdev->dev), i);
269 ret = -EBUSY;
270 goto failed;
271 }
272 }
273
274 pr_debug("Registering platform device '%s'. Parent at %s\n",
275 dev_name(&pdev->dev), dev_name(pdev->dev.parent));
276
277 ret = device_add(&pdev->dev);
278 if (ret == 0)
279 return ret;
280
281 failed:
282 while (--i >= 0) {
283 struct resource *r = &pdev->resource[i];
284 unsigned long type = resource_type(r);
285
286 if (type == IORESOURCE_MEM || type == IORESOURCE_IO)
287 release_resource(r);
288 }
289
290 return ret;
291 }
292 EXPORT_SYMBOL_GPL(platform_device_add);
最终调用device_register那的device_add完成平台设备的注册。
我们也可以使用platform_add_devices去注册一组平台设备
[cpp] view
plaincopy
103 /**
104 * platform_add_devices - add a numbers of platform devices
105 * @devs: array of platform devices to add
106 * @num: number of platform devices in array
107 */
108 int platform_add_devices(struct platform_device **devs, int num)
109 {
110 int i, ret = 0;
111
112 for (i = 0; i < num; i++) {
113 ret = platform_device_register(devs[i]);
114 if (ret) {
115 while (--i >= 0)
116 platform_device_unregister(devs[i]);
117 break;
118 }
119 }
120
121 return ret;
122 }
123 EXPORT_SYMBOL_GPL(platform_add_devices);
看完了注册来看注销函数,注销函数就是platform_device_unregister。
[cpp] view
plaincopy
331 /**
332 * platform_device_unregister - unregister a platform-level device
333 * @pdev: platform device we're unregistering
334 *
335 * Unregistration is done in 2 steps. First we release all resources
336 * and remove it from the subsystem, then we drop reference count by
337 * calling platform_device_put().
338 */
339 void platform_device_unregister(struct platform_device *pdev)
340 {
341 platform_device_del(pdev);
342 platform_device_put(pdev);
343 }
344 EXPORT_SYMBOL_GPL(platform_device_unregister);
294 /**
295 * platform_device_del - remove a platform-level device
296 * @pdev: platform device we're removing
297 *
298 * Note that this function will also release all memory- and port-based
299 * resources owned by the device (@dev->resource). This function must
300 * _only_ be externally called in error cases. All other usage is a bug.
301 */
302 void platform_device_del(struct platform_device *pdev)
303 {
304 int i;
305
306 if (pdev) {
307 device_del(&pdev->dev);
308
309 for (i = 0; i < pdev->num_resources; i++) {
310 struct resource *r = &pdev->resource[i];
311 unsigned long type = resource_type(r);
312
313 if (type == IORESOURCE_MEM || type == IORESOURCE_IO)
314 release_resource(r);
315 }
316 }
317 }
318 EXPORT_SYMBOL_GPL(platform_device_del);
device_del就是device_unregister那的函数
再来看驱动,平台设备驱动结构定义
[cpp] view
plaincopy
58 struct platform_driver {
59 int (*probe)(struct platform_device *);
60 int (*remove)(struct platform_device *);
61 void (*shutdown)(struct platform_device *);
62 int (*suspend)(struct platform_device *, pm_message_t state);
63 int (*resume)(struct platform_device *);
64 struct device_driver driver;
65 struct platform_device_id *id_table;
66 };
驱动注册
[cpp] view
plaincopy
474 /**
475 * platform_driver_register
476 * @drv: platform driver structure
477 */
478 int platform_driver_register(struct platform_driver *drv)
479 {
480 drv->driver.bus = &platform_bus_type;
481 if (drv->probe)
482 drv->driver.probe = platform_drv_probe;
483 if (drv->remove)
484 drv->driver.remove = platform_drv_remove;
485 if (drv->shutdown)
486 drv->driver.shutdown = platform_drv_shutdown;
487
488 return driver_register(&drv->driver);
489 }
490 EXPORT_SYMBOL_GPL(platform_driver_register);
平台驱动结构里面有个成员driver,它是device_driver结构类型,它的probe函数指针赋值了这里的platform_drv_probe,也就是平台总线匹配设备和驱动成功后,将调用这里的
platform_drv_probe函数,那就在去看看这个函数。
[cpp] view
plaincopy
445 static int platform_drv_probe(struct device *_dev)
446 {
447 struct platform_driver *drv = to_platform_driver(_dev->driver);
448 struct platform_device *dev = to_platform_device(_dev);
449
450 return drv->probe(dev);
451 }
还有一点的是它的remove函数为这里的platform_drv_remove,不管是设备注销还是驱动注销都是先调用这个函数,然后才调用平台驱动的remove函数。
[cpp] view
plaincopy
458 static int platform_drv_remove(struct device *_dev)
459 {
460 struct platform_driver *drv = to_platform_driver(_dev->driver);
461 struct platform_device *dev = to_platform_device(_dev);
462
463 return drv->remove(dev);
464 }
也就是最后调用platform_driver的probe函数,它的参数dev是platform_device结构类型。注意这里有两个probe,一个是platform_driver的probe,它是要求我们在编写平台设备驱动时自己去定义,另一个是device_driver的probe,它供总线匹配设备和驱动成功后调用,probe为这里的platform_drv_probe,这个函数的功能就是调用platform_driver的probe。
平台设备驱动注册最后调用的就是driver_register,只不过这里的总线是平台总线。
驱动注销
[cpp] view
plaincopy
492 /**
493 * platform_driver_unregister
494 * @drv: platform driver structure
495 */
496 void platform_driver_unregister(struct platform_driver *drv)
497 {
498 driver_unregister(&drv->driver);
499 }
500 EXPORT_SYMBOL_GPL(platform_driver_unregister);
平台总线是内核实现的一条虚拟总线,Linux设备模型包含三个重要的元素,总线、设备和驱动,那看看平台总线又是怎样去实现的。
首先看平台总线的定义:
[cpp] view
plaincopy
946 struct bus_type platform_bus_type = {
947 .name = "platform",
948 .dev_attrs = platform_dev_attrs,
949 .match = platform_match,
950 .uevent = platform_uevent,
951 .pm = &platform_dev_pm_ops,
952 };
我们知道总线匹配设备和驱动是通过它的match函数,那具体看看这个函数是怎样实现的。
[cpp] view
plaincopy
606 static int platform_match(struct device *dev, struct device_driver *drv)
607 {
608 struct platform_device *pdev = to_platform_device(dev);
609 struct platform_driver *pdrv = to_platform_driver(drv);
610
611 /* match against the id table first */
612 if (pdrv->id_table)
613 return platform_match_id(pdrv->id_table, pdev) != NULL;
614
615 /* fall-back to driver name match */
616 return (strcmp(pdev->name, drv->name) == 0);
617 }
我们看,如果平台驱动有一个id_table,那就通过函数platform_match_id去匹配,如果没有就比较平台设备的name字段和平台驱动的name字段是否相同,这也就是平台总线的匹配规则。再来看平台总线的注册。
[cpp] view
plaincopy
955 int __init platform_bus_init(void)
956 {
957 int error;
958
959 early_platform_cleanup();
960
961 error = device_register(&platform_bus);
962 if (error)
963 return error;
964 error = bus_register(&platform_bus_type);
965 if (error)
966 device_unregister(&platform_bus);
967 return error;
968 }
我们看平台总线注册就是采用的bus_register函数,再看在注册平台总线之前,还调用了
device_register去注册了一个设备,因为总线它也是一个设备,也要被注册进内核。那就具体来看这个设备是怎么定义的。
[cpp] view
plaincopy
28 struct device platform_bus = {
29 .init_name = "platform",
30 };
31 EXPORT_SYMBOL_GPL(platform_bus);
我们看就给了一个名字。看完了总线,又来看看平台设备又是怎样定义怎样去注册。
[cpp] view
plaincopy
17 struct platform_device {
18 const char * name;
19 int id;
20 struct device dev;
21 u32 num_resources;
22 struct resource * resource;
23
24 struct platform_device_id *id_entry;
25
26 /* arch specific additions */
27 struct pdev_archdata archdata;
28 };
其中有个重要的元素resource,该元素存入的最重要的设备资源信息,比如I/O基地址,中断号等等。structresource结构定义在include/linux/ioport.h中
[cpp] view
plaincopy
18 struct resource {
19 resource_size_t start;
20 resource_size_t end;
21 const char *name;
22 unsigned long flags;
23 struct resource *parent, *sibling, *child;
24 };
有可能设备的资源不只一个,定义资源时定义成一个数组的形式,那就使用函数去获取,
platform_get_resource就是用来获取设备的资源信息,去看看这个函数
[cpp] view
plaincopy
33 /**
34 * platform_get_resource - get a resource for a device
35 * @dev: platform device
36 * @type: resource type
37 * @num: resource index
38 */
39 struct resource *platform_get_resource(struct platform_device *dev,
40 unsigned int type, unsigned int num)
41 {
42 int i;
43
44 for (i = 0; i < dev->num_resources; i++) {
45 struct resource *r = &dev->resource[i];
46
47 if (type == resource_type(r) && num-- == 0)
48 return r;
49 }
50 return NULL;
51 }
52 EXPORT_SYMBOL_GPL(platform_get_resource);
这个函数的第一个参数为要获取资源的平台设备,第二个参数为资源类型,比如IORESOURCE_MEM,第三个参数为资源在数组中的一个号。如果是获取中断号,还可以使用函数platform_get_irq
[cpp] view
plaincopy
54 /**
55 * platform_get_irq - get an IRQ for a device
56 * @dev: platform device
57 * @num: IRQ number index
58 */
59 int platform_get_irq(struct platform_device *dev, unsigned int num)
60 {
61 struct resource *r = platform_get_resource(dev, IORESOURCE_IRQ, num);
62
63 return r ? r->start : -ENXIO;
64 }
65 EXPORT_SYMBOL_GPL(platform_get_irq);
我们看这个函数也是调用platform_get_resource去获取资源,只是它获取资源的类型为IORESOURCE_IRQ,最后返回中断号。
再来看平台设备的注册,平台设备注册采用platform_device_register函数
[cpp] view
plaincopy
54 /**
55 * platform_get_irq - get an IRQ for a device
56 * @dev: platform device
57 * @num: IRQ number index
58 */
59 int platform_get_irq(struct platform_device *dev, unsigned int num)
60 {
61 struct resource *r = platform_get_resource(dev, IORESOURCE_IRQ, num);
62
63 return r ? r->start : -ENXIO;
64 }
65 EXPORT_SYMBOL_GPL(platform_get_irq);
device_initialize就是device_register那的函数,那就看platform_device_add
[cpp] view
plaincopy
227 /**
228 * platform_device_add - add a platform device to device hierarchy
229 * @pdev: platform device we're adding
230 *
231 * This is part 2 of platform_device_register(), though may be called
232 * separately _iff_ pdev was allocated by platform_device_alloc().
233 */
234 int platform_device_add(struct platform_device *pdev)
235 {
236 int i, ret = 0;
237
238 if (!pdev)
239 return -EINVAL;
240
241 if (!pdev->dev.parent)
242 pdev->dev.parent = &platform_bus;
243
244 pdev->dev.bus = &platform_bus_type;
245
246 if (pdev->id != -1)
247 dev_set_name(&pdev->dev, "%s.%d", pdev->name, pdev->id);
248 else
249 dev_set_name(&pdev->dev, "%s", pdev->name);
250
251 for (i = 0; i < pdev->num_resources; i++) {
252 struct resource *p, *r = &pdev->resource[i];
253
254 if (r->name == NULL)
255 r->name = dev_name(&pdev->dev);
256
257 p = r->parent;
258 if (!p) {
259 if (resource_type(r) == IORESOURCE_MEM)
260 p = &iomem_resource;
261 else if (resource_type(r) == IORESOURCE_IO)
262 p = &ioport_resource;
263 }
264
265 if (p && insert_resource(p, r)) {
266 printk(KERN_ERR
267 "%s: failed to claim resource %d\n",
268 dev_name(&pdev->dev), i);
269 ret = -EBUSY;
270 goto failed;
271 }
272 }
273
274 pr_debug("Registering platform device '%s'. Parent at %s\n",
275 dev_name(&pdev->dev), dev_name(pdev->dev.parent));
276
277 ret = device_add(&pdev->dev);
278 if (ret == 0)
279 return ret;
280
281 failed:
282 while (--i >= 0) {
283 struct resource *r = &pdev->resource[i];
284 unsigned long type = resource_type(r);
285
286 if (type == IORESOURCE_MEM || type == IORESOURCE_IO)
287 release_resource(r);
288 }
289
290 return ret;
291 }
292 EXPORT_SYMBOL_GPL(platform_device_add);
最终调用device_register那的device_add完成平台设备的注册。
我们也可以使用platform_add_devices去注册一组平台设备
[cpp] view
plaincopy
103 /**
104 * platform_add_devices - add a numbers of platform devices
105 * @devs: array of platform devices to add
106 * @num: number of platform devices in array
107 */
108 int platform_add_devices(struct platform_device **devs, int num)
109 {
110 int i, ret = 0;
111
112 for (i = 0; i < num; i++) {
113 ret = platform_device_register(devs[i]);
114 if (ret) {
115 while (--i >= 0)
116 platform_device_unregister(devs[i]);
117 break;
118 }
119 }
120
121 return ret;
122 }
123 EXPORT_SYMBOL_GPL(platform_add_devices);
看完了注册来看注销函数,注销函数就是platform_device_unregister。
[cpp] view
plaincopy
331 /**
332 * platform_device_unregister - unregister a platform-level device
333 * @pdev: platform device we're unregistering
334 *
335 * Unregistration is done in 2 steps. First we release all resources
336 * and remove it from the subsystem, then we drop reference count by
337 * calling platform_device_put().
338 */
339 void platform_device_unregister(struct platform_device *pdev)
340 {
341 platform_device_del(pdev);
342 platform_device_put(pdev);
343 }
344 EXPORT_SYMBOL_GPL(platform_device_unregister);
294 /**
295 * platform_device_del - remove a platform-level device
296 * @pdev: platform device we're removing
297 *
298 * Note that this function will also release all memory- and port-based
299 * resources owned by the device (@dev->resource). This function must
300 * _only_ be externally called in error cases. All other usage is a bug.
301 */
302 void platform_device_del(struct platform_device *pdev)
303 {
304 int i;
305
306 if (pdev) {
307 device_del(&pdev->dev);
308
309 for (i = 0; i < pdev->num_resources; i++) {
310 struct resource *r = &pdev->resource[i];
311 unsigned long type = resource_type(r);
312
313 if (type == IORESOURCE_MEM || type == IORESOURCE_IO)
314 release_resource(r);
315 }
316 }
317 }
318 EXPORT_SYMBOL_GPL(platform_device_del);
device_del就是device_unregister那的函数
再来看驱动,平台设备驱动结构定义
[cpp] view
plaincopy
58 struct platform_driver {
59 int (*probe)(struct platform_device *);
60 int (*remove)(struct platform_device *);
61 void (*shutdown)(struct platform_device *);
62 int (*suspend)(struct platform_device *, pm_message_t state);
63 int (*resume)(struct platform_device *);
64 struct device_driver driver;
65 struct platform_device_id *id_table;
66 };
驱动注册
[cpp] view
plaincopy
474 /**
475 * platform_driver_register
476 * @drv: platform driver structure
477 */
478 int platform_driver_register(struct platform_driver *drv)
479 {
480 drv->driver.bus = &platform_bus_type;
481 if (drv->probe)
482 drv->driver.probe = platform_drv_probe;
483 if (drv->remove)
484 drv->driver.remove = platform_drv_remove;
485 if (drv->shutdown)
486 drv->driver.shutdown = platform_drv_shutdown;
487
488 return driver_register(&drv->driver);
489 }
490 EXPORT_SYMBOL_GPL(platform_driver_register);
平台驱动结构里面有个成员driver,它是device_driver结构类型,它的probe函数指针赋值了这里的platform_drv_probe,也就是平台总线匹配设备和驱动成功后,将调用这里的
platform_drv_probe函数,那就在去看看这个函数。
[cpp] view
plaincopy
445 static int platform_drv_probe(struct device *_dev)
446 {
447 struct platform_driver *drv = to_platform_driver(_dev->driver);
448 struct platform_device *dev = to_platform_device(_dev);
449
450 return drv->probe(dev);
451 }
还有一点的是它的remove函数为这里的platform_drv_remove,不管是设备注销还是驱动注销都是先调用这个函数,然后才调用平台驱动的remove函数。
[cpp] view
plaincopy
458 static int platform_drv_remove(struct device *_dev)
459 {
460 struct platform_driver *drv = to_platform_driver(_dev->driver);
461 struct platform_device *dev = to_platform_device(_dev);
462
463 return drv->remove(dev);
464 }
也就是最后调用platform_driver的probe函数,它的参数dev是platform_device结构类型。注意这里有两个probe,一个是platform_driver的probe,它是要求我们在编写平台设备驱动时自己去定义,另一个是device_driver的probe,它供总线匹配设备和驱动成功后调用,probe为这里的platform_drv_probe,这个函数的功能就是调用platform_driver的probe。
平台设备驱动注册最后调用的就是driver_register,只不过这里的总线是平台总线。
驱动注销
[cpp] view
plaincopy
492 /**
493 * platform_driver_unregister
494 * @drv: platform driver structure
495 */
496 void platform_driver_unregister(struct platform_driver *drv)
497 {
498 driver_unregister(&drv->driver);
499 }
500 EXPORT_SYMBOL_GPL(platform_driver_unregister);
相关文章推荐
- Linux平台总线驱动设备模型
- 嵌入式学习-驱动开发-lesson5-总线设备驱动模型及平台总线驱动
- linux平台总线驱动设备模型之点亮LED
- 设备驱动,字符设备驱动、(总线)设备驱动模型、sysfs文件系统、平台设备驱动
- Linux平台总线驱动设备模型
- Linux平台总线驱动设备模型
- Linux内核平台总线设备驱动模型浅析
- 13.总线设备驱动模型(2)-平台总线设备设计
- 《网蜂A8实战演练》——6.Linux 平台总线驱动设备模型
- 20150226 IMX257 总线设备驱动模型编程之平台总线设备platform
- Linux平台总线驱动设备模型
- 平台总线设备模型
- 平台总线设备模型
- Linux平台总线驱动设备模型
- 平台总线设备驱动模型——基础知识
- 嵌入式linux平台设备驱动(设备驱动模型)开发之linux内核中bus总线
- linux平台总线驱动设备模型之点亮LED
- 平台总线设备驱动模型——代码分析
- linux内核模型---总线,设备,驱动在展讯平台上I2C设备的实例解析
- 设备模型(device-model)之平台总线(bus),驱动(driver),设备(device)