【轉】Linux I2C裝置驅動編寫(二)

來源:互聯網
上載者:User

標籤:

原文網址:http://www.cnblogs.com/biglucky/p/4059582.html

在(一)中簡述了Linux I2C子系統的三個主要成員i2c_adapter、i2c_driver、i2c_client。三者的關係也在上一節進行了描述。應該已經算是對Linux I2C子系統有了初步的瞭解。下面再對他們之間的關係進行代碼層的深入分析,我認為對他們的關係瞭解的越好,越有助於I2C裝置的驅動開發及調試。

帶著問題去分析可能會更有協助吧,通過對(一)的瞭解後,可能會產生以下的幾點疑問:

  • i2c_adapter驅動如何添加?
  • i2c_client與i2c_board_info究竟是什麼關係?
I2C對外API

在解答問題前,不妨先縷順一下Linux核心的I2C子系統對驅動模組的API有哪些。(來自https://www.kernel.org/doc/htmldocs/device-drivers/i2c.html)

// 對外資料結構struct i2c_driver — 代表一個I2C裝置驅動struct i2c_client — 代表一個I2C從裝置struct i2c_board_info — 從裝置建立的模版I2C_BOARD_INFO — 建立I2C裝置的宏,包含名字和地址struct i2c_algorithm — 代表I2C傳輸方法struct i2c_bus_recovery_info — I2C匯流排恢複資訊?核心新加入的結構,不是很清楚。//對外函數操作module_i2c_driver — 註冊I2C裝置驅動的宏定義i2c_register_board_info — 靜態聲明(註冊)I2C裝置,可多個i2c_verify_client — 如果裝置是i2c_client的dev成員則返回其父指標,否則返回NULL。用來校正裝置是否為I2C裝置i2c_lock_adapter — I2C匯流排持鎖操作,會找到最根源的那個i2c_adapter。說明你的模組必須符合GPL協議才可以使用這個介面。後邊以GPL代表。i2c_unlock_adapter — 上一個的反操作,GPLi2c_new_device — 由i2c_board_info資訊聲明一個i2c裝置(client),GPLi2c_unregister_device — 上一個的反操作,GPL。i2c_new_dummy — 聲明一個名為dummy(指定地址)的I2C裝置,GPLi2c_verify_adapter — 驗證是否是i2c_adapteri2c_add_adapter — 聲明I2C適配器,系統動態分配匯流排號。i2c_add_numbered_adapter — 同樣是聲明I2C適配器,但是指定了匯流排號,GPLi2c_del_adapter — 卸載I2C適配器i2c_del_driver — 卸載I2C裝置驅動i2c_use_client — i2c_client引用數+1i2c_release_client — i2c_client引用數-1__i2c_transfer — 沒有自動持鎖(adapter lock)的I2C傳輸介面i2c_transfer — 自動持鎖的I2C傳輸介面i2c_master_send — 單條訊息發送i2c_master_recv — 單條訊息接收i2c_smbus_read_byte — SMBus “receive byte” protocoli2c_smbus_write_byte — SMBus “send byte” protocoli2c_smbus_read_byte_data — SMBus “read byte” protocoli2c_smbus_write_byte_data — SMBus “write byte” protocoli2c_smbus_read_word_data — SMBus “read word” protocoli2c_smbus_write_word_data — SMBus “write word” protocoli2c_smbus_read_block_data — SMBus “block read” protocoli2c_smbus_write_block_data — SMBus “block write” protocoli2c_smbus_xfer — execute SMBus protocol operations

(一)中對幾個基本的結構體和宏定義也有了大概的解釋,相信結合I2C的理論基礎不難理解。對以上一些I2C的API進行分類:

No. Adapter Driver Device(client) Transfer
1 i2c_add_adapter module_i2c_driver i2c_register_board_info __i2c_transfer
2 i2c_add_numbered_adapter i2c_del_driver i2c_new_device i2c_transfer
3 i2c_del_adapter   i2c_new_dummy i2c_master_send
4 i2c_lock_adapter   i2c_verify_client i2c_master_recv
5 i2c_unlock_adapter   i2c_unregister_device i2c_smbus_read_byte
6 i2c_verify_adapter   i2c_use_client i2c_smbus_write_byte
7     i2c_release_client i2c_smbus_read_byte_data
8       i2c_smbus_write_byte_data
9       i2c_smbus_read_word_data
10       i2c_smbus_write_word_data
11       i2c_smbus_read_block_data
12       i2c_smbus_write_block_data
13       i2c_smbus_xfer

經過一個表格的整理,不難發現在Linux I2C子系統中,最重要的要數i2c_client,而最多樣化的就是資料的傳輸。

為了更好的理解和銜接,我想也許倒著分析會更有協助,而這裡先暫且不討論I2C傳輸過程中的細節。下邊的順序是由client到driver,再到adapter。

I2C client的註冊

i2c_client即I2C裝置的註冊介面有三個:

i2c_register_board_infoi2c_new_device i2c_new_dummy

而i2c_new_dummy在內部其實也就是將client的name指定為dummy後依舊執行的是i2c_new_device,所以就只分析前兩個就可以了。首先看這兩個函數的原型:

i2c_register_board_info(int busnum, struct i2c_board_info const *info, unsigned len)

busnum 通過匯流排號指定這個(些)裝置屬於哪個匯流排
info i2c裝置的數組集合 i2c_board_info格式
len 數組個數ARRAY_SIZE(info)

i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)

adap 此裝置所依附的I2C適配器指標
info 此裝置描述,i2c_board_info格式,bus_num成員是被忽略的

i2c_register_board_info具體實現
int __initi2c_register_board_info(int busnum,    struct i2c_board_info const *info, unsigned len){    int status;    down_write(&__i2c_board_lock);  //i2c裝置資訊讀寫鎖,鎖寫操作,其他唯讀    /* dynamic bus numbers will be assigned after the last static one */    if (busnum >= __i2c_first_dynamic_bus_num)  //與動態分配的匯流排號相關,動態分配的匯流排號應該是從已經現有最大匯流排號基礎上+1的,這樣能夠保證動態分配出的匯流排號與板級匯流排號不會產生衝突        __i2c_first_dynamic_bus_num = busnum + 1;    for (status = 0; len; len--, info++) {  //處理info數組中每個成員        struct i2c_devinfo    *devinfo;        devinfo = kzalloc(sizeof(*devinfo), GFP_KERNEL);        if (!devinfo) {            pr_debug("i2c-core: can‘t register boardinfo!\n");            status = -ENOMEM;            break;        }        devinfo->busnum = busnum;  //組裝匯流排號        devinfo->board_info = *info;  //組裝裝置資訊        list_add_tail(&devinfo->list, &__i2c_board_list);  //加入到__i2c_board_list鏈表中(尾部)    }    up_write(&__i2c_board_lock);  //釋放讀鎖,其他可讀可寫    return status;}

看完後相信都會產生個疑問?怎麼將相關資訊放到鏈表中就算完事了嗎?不著急,來看下核心中已經給出的解釋:

 * Systems using the Linux I2C driver stack can declare tables of board info * while they initialize.  This should be done in board-specific init code * near arch_initcall() time, or equivalent, before any I2C adapter driver is * registered.  For example, mainboard init code could define several devices, * as could the init code for each daughtercard in a board stack. *  * The I2C devices will be created later, after the adapter for the relevant * bus has been registered.  After that moment, standard driver model tools * are used to bind "new style" I2C drivers to the devices.  The bus number * for any device declared using this routine is not available for dynamic * allocation.

核心內容就是說關於整合的I2C裝置註冊過程應該在板級代碼初始化期間,也就是arch_initcall前後的時間,或者就在這個時候(board-xxx-yyy.c中),切記切記!!!一定要在I2C適配器驅動註冊前完成!!!為什麼說是靜態註冊,是因為真實的I2C裝置是在適配器成功註冊後才被產生的。如果在I2C適配器註冊完後還想要添加I2C裝置的話,就要通過新方式!(即i2c_new_device)

小弟永遠要擋在老大前邊嘛!老大還沒出來前,小弟們趕緊前排列陣好,老大到了可不等你,你列陣也沒用了。而對於遲到的小弟,自己想辦法追上去吧,在原地自己列陣是白費工夫了。

對於__i2c_board_list鏈表中的資訊是如何變成實際的i2c裝置資訊的過程放在之後adapter註冊過程的分析中。記得,重點是i2c_register_board_info方式一定要趕在I2C適配器的註冊前,這樣就沒有問題。

i2c_new_device
struct i2c_client *i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info){    struct i2c_client    *client;    int            status;    client = kzalloc(sizeof *client, GFP_KERNEL);  //為即將註冊的client申請記憶體    if (!client)        return NULL;    client->adapter = adap;  //綁定指定的adapter適配器    client->dev.platform_data = info->platform_data;  //儲存裝置資料    if (info->archdata)  //代碼上看是DMA相關操作資料        client->dev.archdata = *info->archdata;    client->flags = info->flags;  //類型,(一)中說過,或是10位地址,或是使用SMBus檢錯    client->addr = info->addr;  //裝置從地址    client->irq = info->irq;  //裝置終端    strlcpy(client->name, info->type, sizeof(client->name));  //從裝置名稱            //瞧!(一)中說過i2c_board_info中的資訊是與i2c_client有對應關係的,靈驗了吧!    /* Check for address validity */    status = i2c_check_client_addr_validity(client);  //檢測地址是否有效,10位地址是否大於0x3ff,7位地址是否大於0x7f或為0    if (status) {  //非零(實際上為-22,無效參數Invalid argument        dev_err(&adap->dev, "Invalid %d-bit I2C address 0x%02hx\n",            client->flags & I2C_CLIENT_TEN ? 10 : 7, client->addr);        goto out_err_silent;    }    /* Check for address business */    status = i2c_check_addr_busy(adap, client->addr);  //檢測指定適配器上該地址狀態    if (status)        goto out_err;    client->dev.parent = &client->adapter->dev;  //建立從裝置與適配器的父子關係    client->dev.bus = &i2c_bus_type;    client->dev.type = &i2c_client_type;    client->dev.of_node = info->of_node;    ACPI_HANDLE_SET(&client->dev, info->acpi_node.handle);    /* For 10-bit clients, add an arbitrary offset to avoid collisions */    dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adap),             client->addr | ((client->flags & I2C_CLIENT_TEN)                     ? 0xa000 : 0));  //如果是10位地址裝置,那名字格式與7bit的會有不同    status = device_register(&client->dev);  //註冊了!註冊了!!!    if (status)        goto out_err;    dev_dbg(&adap->dev, "client [%s] registered with bus id %s\n",        client->name, dev_name(&client->dev));    return client;out_err:    dev_err(&adap->dev, "Failed to register i2c client %s at 0x%02x "        "(%d)\n", client->name, client->addr, status);out_err_silent:    kfree(client);    return NULL;}

i2d_new_device沒什麼好多說的,由於有i2c_register_board_info的鋪墊,相信也很好理解了。而i2c_new_device不但印證了i2c_client與i2c_board_info的對應關係,還順便體現了10bit地址裝置與7bit地址裝置的略微不同。通過兩者的對比,可以再總結出幾點區別,從而更好的理解I2C裝置的註冊方法:

  • i2c_register_board_info的形參需要的是匯流排號
  • i2c_new_device的形參需要的直接是適配器的指標

我想這也正好能完美的說明兩者的根本區別,對於板級裝置更在乎適配器的匯流排號,因為這是固定的,沒有異議的。而對於可插拔裝置,因為其適配器可能非板級整合的,所以不能在乎其匯流排號,反而只要尋求其適配器指標進行綁定即可。後邊adapter註冊的分析能更好的證明這一點。

  • i2c_register_board_info可以同時註冊多個I2C裝置
  • i2c_new_device只能一次註冊一個I2C裝置

這也是其根本區別決定的,板級代碼中常包含有許多I2C裝置,所以i2c_register_board_info需要有同時註冊多個I2C裝置的能力也可以說是剛需。而i2c_new_device既然是用來給可插拔裝置用的,想必裝置數量並不多,而常可能只是一個兩個而已,所以一次一個就夠了,需求量並不大。

I2C driver

I2C裝置驅動。Linux核心給出的介面只有兩個,一個是註冊,另一個就是卸載。在(一)也分析過module_i2c_driver這個宏定義,因為有它的存在,I2C裝置驅動的開發可以不用在意你的I2C驅動需要如何註冊以及如何卸載的,全部的精力都放在i2c_driver的完善上就可以了。

通過最開始的表單能明顯察覺到,I2C子系統中I2C driver的開放介面最少,說白了就是需要驅動編寫者完成完了i2c_driver放入module_i2c_driver宏中即可,而正因為如此,也恰恰說明,i2c_driver的靈活性是最高的。通常驅動會首先在意在使用者空間的開啟、關閉、讀寫等介面,但是對於i2c_driver來說,這些工作是I2C子系統已經做好的,關於常用的讀寫最終也是通過adapter實現的i2c_algorithm達到目的。好吧,再次說明了I2C子系統的完善程度,對於I2C裝置及驅動開發來說是極其方便的。那麼I2C驅動要實現什麼呢?

再次回顧一下i2c_driver結構體,不過現在要剔除一些不常用的成員:

struct i2c_driver {    int (*probe)(struct i2c_client *, const struct i2c_device_id *); //現行通用的與對應裝置進行綁定的介面函數    int (*remove)(struct i2c_client *);  //現行通用與對應裝置進行解除綁定的介面函數    void (*shutdown)(struct i2c_client *);  //關閉裝置    int (*suspend)(struct i2c_client *, pm_message_t mesg); //掛起裝置,與電源管理有關,為省電    int (*resume)(struct i2c_client *); //從掛起狀態恢複    struct device_driver driver;  //I2C裝置的驅動模型    const struct i2c_device_id *id_table;  //匹配裝置列表    ...};

如果有可能的話,我還想再精簡一下:

struct i2c_driver {    int (*probe)(struct i2c_client *, const struct i2c_device_id *); //現行通用的與對應裝置進行綁定的介面函數    int (*remove)(struct i2c_client *);  //現行通用與對應裝置進行解除綁定的介面函數    struct device_driver driver;  //I2C裝置的驅動模型    const struct i2c_device_id *id_table;  //匹配裝置列表    ...};

好了,精簡到這種程度,為什麼把電源管理相關也幹掉了呢?實際上沒有,通常實際的I2C驅動喜歡在drivers中完成這個動作(以mpu3050為例):

static UNIVERSAL_DEV_PM_OPS(mpu3050_pm, mpu3050_suspend, mpu3050_resume, NULL);static const struct i2c_device_id mpu3050_ids[] = {    { "mpu3050", 0 },    { }};MODULE_DEVICE_TABLE(i2c, mpu3050_ids);static const struct of_device_id mpu3050_of_match[] = {    { .compatible = "invn,mpu3050", },    { },};MODULE_DEVICE_TABLE(of, mpu3050_of_match);static struct i2c_driver mpu3050_i2c_driver = {    .driver    = {        .name    = "mpu3050",        .owner    = THIS_MODULE,        .pm    = &mpu3050_pm,        .of_match_table = mpu3050_of_match,    },    .probe        = mpu3050_probe,    .remove        = mpu3050_remove,    .id_table    = mpu3050_ids,};module_i2c_driver(mpu3050_i2c_driver);

可以看到,實際驅動中喜歡將電源管理整合在i2c_driver的driver成員中。

UNIVERSAL_DEV_PM_OPS這個名字很犀利,貌似是“宇宙終極驅動電源管理大法”的樣子:

#define UNIVERSAL_DEV_PM_OPS(name, suspend_fn, resume_fn, idle_fn) const struct dev_pm_ops name = {     SET_SYSTEM_SLEEP_PM_OPS(suspend_fn, resume_fn)     SET_RUNTIME_PM_OPS(suspend_fn, resume_fn, idle_fn) }#define SET_SYSTEM_SLEEP_PM_OPS(suspend_fn, resume_fn)     .suspend = suspend_fn,     .resume = resume_fn,     .freeze = suspend_fn,     .thaw = resume_fn,     .poweroff = suspend_fn,     .restore = resume_fn,#define SET_RUNTIME_PM_OPS(suspend_fn, resume_fn, idle_fn)     .runtime_suspend = suspend_fn,     .runtime_resume = resume_fn,     .runtime_idle = idle_fn,

結合MPU3050的驅動將其完整展開可以得到:

static const struct dev_pm_ops mpu3050_pm = {    .suspend = mpu3050_suspend,    .resume = mpu3050_resume,    .freeze = mpu3050_suspend,    .thaw = mpu3050_resume,    .poweroff = mpu3050_suspend,    .restore = mpu3050_resume,    .runtime_suspend = mpu3050_suspend,    .runtime_resume = mpu3050_resume,    .runtime_idle = NULL,}

對電源管理有興趣的可以去查閱pm.h,其中對電源管理有詳盡的說明了,這裡不做分析。可以看到,在電源管理中,有很多成員實際上是一樣的,在現實驅動中這樣的情況也經常出現,所以會有“終極電源管理大法”宏的出現了。

of_match_table是OpenFirmware相關,在3.0(具體版本本人不清楚)kernel後對arm平台引入了Device Tree,可通過dts設定檔代替大量板級代碼,有興趣可自行查閱。

上邊說過,i2c_driver的多樣化最多,從mpu3050的驅動註冊中也可以發現,其注重實現的為probe與電源管理,其中probe最為重要(好像是廢話,哪個驅動這個都是最重要的-。-)。因為主要是從驅動的角度看待I2C子系統,所以這裡不詳盡分析mpu3050的代碼,只以其為例說明I2C驅動大體架構。在mpu3050的probe主要對此感應器進行上電、工作模式初始化、註冊INPUT子系統介面、關聯中斷處理常式(在中斷處理線程中上報三軸參數)等工作。

關於I2C裝置驅動的小總結

I2C裝置驅動通常只是需要掛載在I2C匯流排(即依附於I2C子系統),I2C子系統對於裝置驅動來說只是一個載體、基石。許多裝置的主要核心是建立在其他子系統上,如重力感應器、三軸感應器、觸控螢幕等通常主要工作集中在INPUT子系統中,而相機模組、FM模組、GPS模組大多主要依附於V4L2子系統。這也能通過I2C設計理念證明,I2C的產生正是為了節省外圍電路複雜度,讓CPU使用有限的IO口掛載更多的外部模組。假設CPU的擴充IO口足夠多,我想I2C也沒什麼必要存在了,畢竟直接操作IO口驅動裝置比I2C來的更簡單。

I2C adapter的註冊

如上表所示,對於I2C adapter的註冊有兩種途徑:i2c_add_adapter 或i2c_add_numbered_adapter,兩者的區別是後者在註冊時已經指定了此I2C適配器的匯流排號,而前者的匯流排號將由系統自動分配。

其各自的聲明格式為:

int i2c_add_adapter(struct i2c_adapter *adapter)int i2c_add_numbered_adapter(struct i2c_adapter *adap)

在i2c_add_numberd_adapter使用前必須制定adap->nr,如果給-1,說明還是叫系統去自動產生匯流排號的。

使用情境

之所以區分開兩種I2C adapter的註冊方式,是因為他們的使用情境有所不同。

  • i2c_add_adapter的使用經常是用來註冊那些可插拔裝置,如USB PCI裝置等。主板上的其他模組與其沒有直接聯絡,說白了就是現有模組不在乎新加入的I2C適配器的匯流排號是多少,因為他們不需要。反而這個可插拔裝置上的一些模組會需要其註冊成功的適配器指標。回看一開始就分析的i2c_client,會發現不同情境的裝置與其匹配的適配器有著這樣的對應關係:

      1. i2c_register_board_info需要指定已有的busnum,而i2c_add_numbered_adapter註冊前已經指定匯流排號;  2. i2c_new_device需要指定adapter指標,而i2c_add_adapter註冊成功後恰好這個指標就有了。

    想象這樣一個情境:新裝置插入後,對應的驅動程式通過i2c_add_adapter註冊自己的I2C適配器,然後根據與小弟們的協定將其是適配器指標存放在某處,相當於對小弟們(依附在其上的I2C裝置)說:“看見沒?你們註冊你們自己的裝置的時候就通過這個就能找到我,就能跟我混了!”然後驅動程式繼續,當執行到對自己的I2C裝置註冊時候,小弟們去約定地點找老大留下的記號,發現有效資訊後,一擁而上:“看!老大在那!!!”

  • i2c_add_numbered_adapter用來註冊CPU內建的I2C適配器,或是整合在主板上的I2C適配器。主板上的其他I2C從裝置(client)在註冊時候需要這個匯流排號。

通過簡短的程式碼分析看一看他們的區別究竟如何,以及為什麼靜態註冊的i2c_client必須要在adapter註冊前(此處會精簡部分代碼,只留重要部分):

int i2c_add_adapter(struct i2c_adapter *adapter){    int    id, res = 0;    res = idr_get_new_above(&i2c_adapter_idr, adapter,                __i2c_first_dynamic_bus_num, &id);  //動態擷取匯流排號    adapter->nr = id;    return i2c_register_adapter(adapter);  //註冊adapter}int i2c_add_numbered_adapter(struct i2c_adapter *adap){    int    id;    int    status;    if (adap->nr == -1) /* -1 means dynamically assign bus id */        return i2c_add_adapter(adap);    status = i2c_register_adapter(adap);    return status;}

可見,最終他們都是通過i2c_register_adapter註冊適配器:

static int i2c_register_adapter(struct i2c_adapter *adap){    int res = 0;    /* Can‘t register until after driver model init */  //時序檢查    if (unlikely(WARN_ON(!i2c_bus_type.p))) {        res = -EAGAIN;        goto out_list;    }    /* Sanity checks */    if (unlikely(adap->name[0] == ‘\0‘)) {  //防禦型代碼,檢查適配器名稱        pr_err("i2c-core: Attempt to register an adapter with "               "no name!\n");        return -EINVAL;    }    if (unlikely(!adap->algo)) {  //適配器是否已經完成了通訊方法的實現        pr_err("i2c-core: Attempt to register adapter ‘%s‘ with "               "no algo!\n", adap->name);        return -EINVAL;    }    rt_mutex_init(&adap->bus_lock);    mutex_init(&adap->userspace_clients_lock);    INIT_LIST_HEAD(&adap->userspace_clients);    /* Set default timeout to 1 second if not already set */    if (adap->timeout == 0)        adap->timeout = HZ;    dev_set_name(&adap->dev, "i2c-%d", adap->nr);    adap->dev.bus = &i2c_bus_type;    adap->dev.type = &i2c_adapter_type;    res = device_register(&adap->dev);  //註冊裝置節點    if (res)        goto out_list;    /* create pre-declared device nodes */ //建立預-聲明的I2C裝置節點    if (adap->nr < __i2c_first_dynamic_bus_num)          i2c_scan_static_board_info(adap);        //如果adapter的匯流排號小於動態分配的匯流排號的最小那個,說明是板級adapter。        //因為通過i2c_add_adapter加入的適配器所分配的匯流排號一定是比__i2c_first_dynamic_bus_num大的。    ...}

對於i2c_add_numbered_adapter來說會觸發i2c_scan_static_board_info:

static void i2c_scan_static_board_info(struct i2c_adapter *adapter){    struct i2c_devinfo    *devinfo;    down_read(&__i2c_board_lock);  //持有讀寫鎖的讀,有使用者讀的時候不允許寫入    list_for_each_entry(devinfo, &__i2c_board_list, list) {  //又見__i2c_board_list,這不是通過i2c_register_board_info組建起來的那個鏈表嗎!        if (devinfo->busnum == adapter->nr                && !i2c_new_device(adapter,                        &devinfo->board_info)) //找到匯流排號與剛註冊的這個adapter相同的並通過i2c_new_device進行註冊            dev_err(&adapter->dev,                "Can‘t create device at 0x%02x\n",                devinfo->board_info.addr);    }    up_read(&__i2c_board_lock);  //釋放讀寫鎖}

而i2c_board_info成員與i2c_client的對應動作也是在i2c_new_device中進行的,這一點在上邊已經分析過了。看到這裡,對adapter與client的微妙關係應該瞭解程度就比較深了,為什麼說i2c_register_board_info與i2c_add_numbered_adapter對應而不是i2c_add_adapter也可以說得通。

那麼,最終回答開篇提出的那兩個問題:

  • i2c_adapter驅動如何添加?

板級適配器(CPU內建、主板整合)要通過i2c_add_numbered_adapter註冊,註冊前要指定匯流排號,從0開始。假如板級I2C適配器註冊了3個,那麼第一個動態匯流排號一定是3,也就是說可插拔裝置所帶有的I2C適配器需要通過i2c_add_adapter進行註冊,其匯流排號由系統指定。

  • i2c_client與i2c_board_info究竟是什麼關係?

i2c_client與i2c_board_info的對應關係在i2c_new_device中有完整體現。

i2c_client->dev.platform_data = i2c_board_info->platform_data;i2c_client->dev.archdata = i2c_board_info->archdata;i2c_client->flags = i2c_board_info->flags;i2c_client->addr = i2c_board_info->addr;i2c_client->irq = i2c_board_info->irq;

【轉】Linux I2C裝置驅動編寫(二)

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.