Linux platform system
先來(lái)看下platform的框架:platform是Linux內(nèi)的一種虛擬總線,稱為platform總線,相應(yīng)的設(shè)備稱為platform_device,而驅(qū)動(dòng)稱為platform_driver。platform總線、設(shè)備和驅(qū)動(dòng)這3個(gè)實(shí)體,總線將設(shè)備和驅(qū)動(dòng)綁定,在系統(tǒng)每注冊(cè)一個(gè)設(shè)備的時(shí)候,會(huì)尋找與之匹配的驅(qū)動(dòng);相反的,在系統(tǒng)每注冊(cè)一個(gè)驅(qū)動(dòng)的時(shí)候,會(huì)尋找與之匹配的設(shè)備,而匹配由總線完成。
platform框架其實(shí)比較直觀,就總線/設(shè)備/驅(qū)動(dòng)三部分,所以platform的實(shí)現(xiàn)一般就為三個(gè)步驟,先對(duì)platform設(shè)備進(jìn)行注冊(cè),再編寫(xiě)platform驅(qū)動(dòng),最后設(shè)備和驅(qū)動(dòng)進(jìn)行匹配,匹配成功則執(zhí)行probe探測(cè)函數(shù),probe函數(shù)里面對(duì)具體的功能實(shí)現(xiàn),下面對(duì)這三步的內(nèi)容進(jìn)行簡(jiǎn)單分析。
1.platform device
platform設(shè)備對(duì)應(yīng)的結(jié)構(gòu)體paltform_device
,位于linux/platform_device.h
中,如下:
struct platform_device {
const char * name; //設(shè)備的名字,這將代替device->dev_id,用作sys/device下顯示的目錄名
int id; //設(shè)備id,用于給插入給該總線并且具有相同name的設(shè)備編號(hào),如果只有一個(gè)設(shè)備的話填-1。
struct device dev; //結(jié)構(gòu)體中內(nèi)嵌的device結(jié)構(gòu)體。
u32 num_resources; //資源數(shù)。
struct resource * resource; //資源數(shù)。
const struct platform_device_id *id_entry;
/* MFD cell pointer */
struct mfd_cell *mfd_cell;
/* arch specific additions */
struct pdev_archdata archdata;
};
可以看到,platform_device
封裝了指定的名字name、id、內(nèi)嵌device。
platform_device
的注冊(cè)一般在平臺(tái)設(shè)備里面實(shí)現(xiàn),這邊舉例說(shuō)明下:
1.先要為設(shè)備提供platform_device結(jié)構(gòu)體,并賦值,這邊以i2c為例。
static struct resource s3c_i2c_resource[] = {
[0] = {
.start = S3C_PA_IIC,
.end = S3C_PA_IIC + SZ_4K - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = IRQ_IIC,
.end = IRQ_IIC,
.flags = IORESOURCE_IRQ,
},
};
struct platform_device s3c_device_i2c0 = {
.name = "s3c-i2c",
.id = -1,
.num_resources = ARRAY_SIZE(s3c_i2c_resource),
.resource = s3c_i2c_resource,
};
2.有了各設(shè)備的platform_device后,一般會(huì)統(tǒng)一放到用來(lái)注冊(cè)的platform_device,里面會(huì)包含各自platform設(shè)備,如下:
static struct platform_device *s3c_devices[] __initdata =
{
&s3c_device_i2c0,
&s3c_device_spi,
&s3c_device_hsmmc0,
&s3c_device_hsmmc1,
}
3.最后使用platform_add_devices
進(jìn)行將device添加到platform總線上。
platform_add_devices(s3c_devices, ARRAY_SIZE(s3c_devices));
該函數(shù)位于drivers/base/platform.c
中,如下:
int platform_add_devices(struct platform_device **devs, int num)
{
int i, ret = 0;
for (i = 0; i < num; i++) {
ret = platform_device_register(devs[i]);
if (ret) {
while (--i >= 0)
platform_device_unregister(devs[i]);
break;
}
}
return ret;
}
可以看到platform_device
會(huì)掃描s3c_devices
里面的每一個(gè)設(shè)備,并為它們進(jìn)行注冊(cè),這邊的注冊(cè)和注銷(xiāo)使用platform_device_register()
和platform_device_unregister()
函數(shù)。
注冊(cè)后,同樣會(huì)在/sys/device/目錄下創(chuàng)建一個(gè)以name命名的目錄,并且創(chuàng)建軟連接到/sys/bus/platform/device下。
2.platform driver
platform驅(qū)動(dòng)對(duì)應(yīng)的結(jié)構(gòu)體paltform_driver
,位于linux/platform_device.h
中,如下:
struct platform_driver {
int (*probe)(struct platform_device *);
int (*remove)(struct platform_device *);
void (*shutdown)(struct platform_device *);
int (*suspend)(struct platform_device *, pm_message_t state);
int (*resume)(struct platform_device *);
struct device_driver driver;
const struct platform_device_id *id_table;
};
與device類(lèi)似,driver里面嵌入的是device_driver,不過(guò)driver里面有實(shí)現(xiàn)函數(shù)probe、remove等。
platform_driver的實(shí)現(xiàn)也比較簡(jiǎn)單,這邊也以i2c為例,如下:
static struct platform_driver s3c_i2c_driver = {
.probe = s3c_i2c_probe,
.remove = s3c_i2c_remove,
.id_table = s3c_driver_ids,
.driver = {
.owner = THIS_MODULE,
.name = "s3c-i2c",
.pm = S3C_DEV_PM_OPS,
},
};
static int __init i2c_adap_s3c_init(void)
{
return platform_driver_register(&s3c24xx_i2c_driver);
}
static void __exit i2c_adap_s3c_exit(void)
{
platform_driver_unregister(&s3c_i2c_driver);
}
module_init(i2c_adap_s3c_init);
module_exit(i2c_adap_s3c_exit);
platform驅(qū)動(dòng)的注冊(cè)和注銷(xiāo)分別用platform_driver_register()
和platform_driver_unregister()
函數(shù)。
3.platform match
platform driver
編寫(xiě)完成后,就是要執(zhí)行driver的probe函數(shù)。probe函數(shù)能否執(zhí)行的關(guān)鍵在于,device中的name與driver中的name是否相等,相等時(shí)說(shuō)明匹配成功,就可以執(zhí)行probe函數(shù)實(shí)現(xiàn)具體的功能了。
這邊也追蹤下如何執(zhí)行到match函數(shù),驅(qū)動(dòng)會(huì)調(diào)用platform_driver_register
進(jìn)行注冊(cè),該函數(shù)會(huì)將總線指向platform總線,而platform總線的結(jié)構(gòu)體如下:
struct bus_type platform_bus_type = {
.name = "platform",
.dev_attrs = platform_dev_attrs,
.match = platform_match,
.uevent = platform_uevent,
.pm = &platform_dev_pm_ops,
};
可以看到platform_match
為platform_bus
的一個(gè)成員,platform_match
的實(shí)現(xiàn)如下:
static int platform_match(struct device *dev, struct device_driver *drv)
{
struct platform_device *pdev = to_platform_device(dev);
struct platform_driver *pdrv = to_platform_driver(drv);
/* Attempt an OF style match first */
if (of_driver_match_device(dev, drv))
return 1;
/* Then try to match against the id table */
if (pdrv->id_table)
return platform_match_id(pdrv->id_table, pdev) != NULL;
/* fall-back to driver name match */
return (strcmp(pdev->name, drv->name) == 0);
}
platform_match
會(huì)調(diào)用platform_match_id()
函數(shù),匹配函數(shù)platform_match_id()
會(huì)去搜索所有已經(jīng)注冊(cè)到platform總線上的設(shè)備,如果匹配到則返回id,沒(méi)找到則返回NULL如下:
static const struct platform_device_id *platform_match_id(
const struct platform_device_id *id,
struct platform_device *pdev)
{
while (id->name[0]) {
printk("pdev->name:%s\n",pdev->name);
printk("id->name:%s\n",id->name);
if (strcmp(pdev->name, id->name) == 0) {
pdev->id_entry = id;
return id;
}
id++;
}
return NULL;
}
這邊兩句printk調(diào)試信息是我自己加上去的,在調(diào)試的時(shí)候可以很直觀的觀察到兩個(gè)name是否相等,在Linux驅(qū)動(dòng)中很多設(shè)備都是通過(guò)這一原理來(lái)進(jìn)行device和driver的匹配,如下面幾個(gè)模塊:
1.drivers/i2c/i2c-core.c
中的i2c_match_id
static const struct i2c_device_id *i2c_match_id(const struct i2c_device_id *id,
const struct i2c_client *client)
{
while (id->name[0]) {
if (strcmp(client->name, id->name) == 0)
return id;
id++;
}
return NULL;
}
2.drivers/spi/spi.c
中的spi_match_id
static const struct spi_device_id *spi_match_id(const struct spi_device_id *id,
const struct spi_device *sdev)
{
while (id->name[0]) {
if (!strcmp(sdev->modalias, id->name))
return id;
id++;
}
return NULL;
}
3.drivers/pci/pci-driver.c
中的pci_match_id
const struct pci_device_id *pci_match_id(const struct pci_device_id *ids,
struct pci_dev *dev)
{
if (ids) {
while (ids->vendor || ids->subvendor || ids->class_mask) {
if (pci_match_one_device(ids, dev))
return ids;
ids++;
}
}
return NULL;
}
4.drivers/usb/core/driver.c
中的usb_match_id
const struct usb_device_id *usb_match_id(struct usb_interface *interface,
const struct usb_device_id *id)
{
if (id == NULL)
return NULL;
for (; id->idVendor || id->idProduct || id->bDeviceClass ||
id->bInterfaceClass || id->driver_info; id++) {
if (usb_match_one_id(interface, id))
return id;
}
return NULL;
}
5.drivers/hid/hid-core.c
中的hid_match_id
static const struct hid_device_id *hid_match_id(struct hid_device *hdev,
const struct hid_device_id *id)
{
for (; id->bus; id++)
if (hid_match_one_id(hdev, id))
return id;
return NULL;
}
如果發(fā)現(xiàn)打印的name不一樣或有一個(gè)沒(méi)有打印出來(lái),這是候就要去檢測(cè)對(duì)應(yīng)的device和driver,可能就是總線沒(méi)注冊(cè)上或驅(qū)動(dòng)id、name不一致。
Linux platform system的分析就到這邊,有感悟時(shí)會(huì)持續(xù)會(huì)更新。
注:以上內(nèi)容都是本人在學(xué)習(xí)過(guò)程積累的一些心得,難免會(huì)有參考到其他文章的一些知識(shí),如有侵權(quán),請(qǐng)及時(shí)通知我,我將及時(shí)刪除或標(biāo)注內(nèi)容出處,如有錯(cuò)誤之處也請(qǐng)指出,進(jìn)行探討學(xué)習(xí)。文章只是起一個(gè)引導(dǎo)作用,詳細(xì)的數(shù)據(jù)解析內(nèi)容還請(qǐng)查看Linux相關(guān)教程,感謝您的查閱。