LINUX裝置驅動i2c架構分析(三)

來源:互聯網
上載者:User
六:其它的擴充分析完adapter和i2c driver的註冊之後,好像整個架構也差不多了,其它,擴充的東西還有很多.我們舉一個legacy形式的例子,這個例子是在kernel中隨便搜尋出來的:在linux-2.6.26.3/drivers/hwmon/ad7418.c中,初始化函數為:static int __init ad7418_init(void){    return i2c_add_driver(&ad7418_driver);}i2c_driver ad7418_driver結構如下:static struct i2c_driver ad7418_driver = {    .driver = {        .name   = "ad7418",    },    .attach_adapter = ad7418_attach_adapter,    .detach_client  = ad7418_detach_client,};該結構中沒有probe()函數,可以斷定是一個legacy形式的驅動.這類驅動註冊的時候,會調用driver的attach_adapter函數.在這裡也就是ad7418_attach_adapter.這個函數代碼如下:static int ad7418_attach_adapter(struct i2c_adapter *adapter){    if (!(adapter->class & I2C_CLASS_HWMON))        return 0;    return i2c_probe(adapter, &addr_data, ad7418_detect);}在這裡我們又遇到了一個i2c-core中的函數,i2c_probe().在分析這個函數之前,先來看下addr_data是什麼?#define I2C_CLIENT_MODULE_PARM(var,desc) /  static unsigned short var[I2C_CLIENT_MAX_OPTS] = I2C_CLIENT_DEFAULTS; /  static unsigned int var##_num; /  module_param_array(var, short, &var##_num, 0); /  MODULE_PARM_DESC(var,desc) #define I2C_CLIENT_MODULE_PARM_FORCE(name)              /I2C_CLIENT_MODULE_PARM(force_##name,                    /               "List of adapter,address pairs which are "    /               "unquestionably assumed to contain a `"       /               # name "' chip")  #define I2C_CLIENT_INSMOD_COMMON                    /I2C_CLIENT_MODULE_PARM(probe, "List of adapter,address pairs to scan "  /               "additionally");                  /I2C_CLIENT_MODULE_PARM(ignore, "List of adapter,address pairs not to "  /               "scan");                      /static const struct i2c_client_address_data addr_data = {       /    .normal_i2c = normal_i2c,                   /    .probe      = probe,                    /    .ignore     = ignore,                   /    .forces     = forces,                   /}#define I2C_CLIENT_FORCE_TEXT /    "List of adapter,address pairs to boldly assume to be present"由此可知道,addr_data中的三個成員都是模組參數.在載入模組的時候可以用參數的方式對其賦值.三個模組參數為別為probe,ignore,force.另外需要指出的是normal_i2c不能以模組參數的方式對其賦值,只能在驅動內部靜態指定.從模組參數的模述看來, probe是指"List of adapter,address pairs to scan additionally"Ignore是指"List of adapter,address pairs not to scan "Force是指"List of adapter,address pairs to boldly assume to be present" 事實上,它們裡面的資料都是成對出現的.前面一部份表示所在的匯流排號,ANY_I2C_BUS表示任一匯流排.後一部份表示裝置的地址.現在可以來跟蹤i2c_probe()的代碼了.如下:int i2c_probe(struct i2c_adapter *adapter,          const struct i2c_client_address_data *address_data,          int (*found_proc) (struct i2c_adapter *, int, int)){    int i, err;    int adap_id = i2c_adapter_id(adapter);     /* Force entries are done first, and are not affected by ignore       entries */       //先掃描force裡面的資訊,注意它是一個二級指標.ignore裡的資訊對它是無效的    if (address_data->forces) {        const unsigned short * const *forces = address_data->forces;        int kind;         for (kind = 0; forces[kind]; kind++) {            for (i = 0; forces[kind][i] != I2C_CLIENT_END;                 i += 2) {                if (forces[kind][i] == adap_id                 || forces[kind][i] == ANY_I2C_BUS) {                    dev_dbg(&adapter->dev, "found force "                        "parameter for adapter %d, "                        "addr 0x%02x, kind %d/n",                        adap_id, forces[kind][i + 1],                        kind);                    err = i2c_probe_address(adapter,                        forces[kind][i + 1],                        kind, found_proc);                    if (err)                        return err;                }            }        }    }     /* Stop here if we can't use SMBUS_QUICK *///如果adapter不支援quick.不能夠遍曆這個adapter上面的裝置

    if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_QUICK)) {        if (address_data->probe[0] == I2C_CLIENT_END         && address_data->normal_i2c[0] == I2C_CLIENT_END)            return 0;         dev_warn(&adapter->dev, "SMBus Quick command not supported, "             "can't probe for chips/n");        return -1;    }     /* Probe entries are done second, and are not affected by ignore       entries either */      //遍曆probe上面的資訊.ignore上的資訊也對它是沒有影響的     for (i = 0; address_data->probe[i] != I2C_CLIENT_END; i += 2) {        if (address_data->probe[i] == adap_id         || address_data->probe[i] == ANY_I2C_BUS) {            dev_dbg(&adapter->dev, "found probe parameter for "                "adapter %d, addr 0x%02x/n", adap_id,                address_data->probe[i + 1]);            err = i2c_probe_address(adapter,                        address_data->probe[i + 1],                        -1, found_proc);            if (err)                return err;        }    }     /* Normal entries are done last, unless shadowed by an ignore entry */    //最後遍曆normal_i2c上面的資訊.它上面的資訊不能在ignore中.    for (i = 0; address_data->normal_i2c[i] != I2C_CLIENT_END; i += 1) {        int j, ignore;         ignore = 0;        for (j = 0; address_data->ignore[j] != I2C_CLIENT_END;             j += 2) {            if ((address_data->ignore[j] == adap_id ||                 address_data->ignore[j] == ANY_I2C_BUS)             && address_data->ignore[j + 1]                == address_data->normal_i2c[i]) {                dev_dbg(&adapter->dev, "found ignore "                    "parameter for adapter %d, "                    "addr 0x%02x/n", adap_id,                    address_data->ignore[j + 1]);                ignore = 1;                break;            }        }        if (ignore)            continue;         dev_dbg(&adapter->dev, "found normal entry for adapter %d, "            "addr 0x%02x/n", adap_id,            address_data->normal_i2c[i]);        err = i2c_probe_address(adapter, address_data->normal_i2c[i],                    -1, found_proc);        if (err)            return err;    }     return 0;} 這段代碼很簡單,結合代碼上面添加的注釋應該很好理解.如果匹配成功,則會調用i2c_probe_address ().這個函數代碼如下:static int i2c_probe_address(struct i2c_adapter *adapter, int addr, int kind,                 int (*found_proc) (struct i2c_adapter *, int, int)){    int err;     /* Make sure the address is valid */    //地址小於0x03或者大於0x77都是不合法的    if (addr < 0x03 || addr > 0x77) {        dev_warn(&adapter->dev, "Invalid probe address 0x%02x/n",             addr);        return -EINVAL;    }     /* Skip if already in use */    //adapter上已經有這個裝置了    if (i2c_check_addr(adapter, addr))        return 0;     /* Make sure there is something at this address, unless forced */    //如果kind小於0.檢查adapter上是否有這個裝置    if (kind < 0) {        if (i2c_smbus_xfer(adapter, addr, 0, 0, 0,                   I2C_SMBUS_QUICK, NULL) < 0)            return 0;         /* prevent 24RF08 corruption */        if ((addr & ~0x0f) == 0x50)            i2c_smbus_xfer(adapter, addr, 0, 0, 0,                       I2C_SMBUS_QUICK, NULL);    }     /* Finally call the custom detection function */    //調用回呼函數    err = found_proc(adapter, addr, kind);    /* -ENODEV can be returned if there is a chip at the given address       but it isn't supported by this chip driver. We catch it here as       this isn't an error. */    if (err == -ENODEV)        err = 0;     if (err)        dev_warn(&adapter->dev, "Client creation failed at 0x%x (%d)/n",             addr, err);    return err;}首先,對傳入的參數進行一系列的合法性檢查.另外,如果該adapter上已經有了這個地址的裝置了.也會返回失敗.所有adapter下面的裝置都是以adapter->dev為父結點的.因此只需要遍曆adapter->dev下面的子裝置就可以得到當前地址是不是被佔用了.如果kind < 0.還得要adapter檢查該匯流排是否有這個地址的裝置.方法是向這個地址發送一個Read的Quick請求.如果該地址有應答,則說明這個地址上有這個裝置.另外還有一種情況是在24RF08裝置的特例.如果adapter上確實有這個裝置,就會調用驅動調用時的回呼函數. 在上面涉及到了IIC的傳輸方式,有疑問的可以參考intel ICH5手冊的有關smbus部份.跟蹤i2c_smbus_xfer().代碼如下:s32 i2c_smbus_xfer(struct i2c_adapter * adapter, u16 addr, unsigned short flags,                   char read_write, u8 command, int size,                   union i2c_smbus_data * data){    s32 res;     flags &= I2C_M_TEN | I2C_CLIENT_PEC;     if (adapter->algo->smbus_xfer) {        mutex_lock(&adapter->bus_lock);        res = adapter->algo->smbus_xfer(adapter,addr,flags,read_write,                                        command,size,data);        mutex_unlock(&adapter->bus_lock);    } else        res = i2c_smbus_xfer_emulated(adapter,addr,flags,read_write,                                          command,size,data);     return res;}如果adapter有smbus_xfer()函數,則直接調用它發送,否則,也就是在adapter不支援smbus協議的情況下,調用i2c_smbus_xfer_emulated()繼續處理.跟進i2c_smbus_xfer_emulated().代碼如下:static s32 i2c_smbus_xfer_emulated(struct i2c_adapter * adapter, u16 addr,                                   unsigned short flags,                                   char read_write, u8 command, int size,                                   union i2c_smbus_data * data){    /* So we need to generate a series of msgs. In the case of writing, we      need to use only one message; when reading, we need two. We initialize      most things with sane defaults, to keep the code below somewhat      simpler. */     //寫操作只會進行一次互動,而讀操作,有時會有兩次操作.     //因為有時候讀操作要先寫command,再從匯流排上讀資料     //在這裡為了代碼的簡潔.使用了兩個緩衝區,將兩種情況統一起來.    unsigned char msgbuf0[I2C_SMBUS_BLOCK_MAX+3];    unsigned char msgbuf1[I2C_SMBUS_BLOCK_MAX+2];    //一般來說,讀操作要互動兩次.例外的情況我們在下面會接著分析    int num = read_write == I2C_SMBUS_READ?2:1;    //與裝置互動的資料,一般在msg[0]存放寫入裝置的資訊,在msb[1]裡存放接收到的    //資訊.不過也有例外的    //msg[2]的初始化,預設發送緩衝區佔一個位元組,無接收緩衝    struct i2c_msg msg[2] = { { addr, flags, 1, msgbuf0 },                              { addr, flags | I2C_M_RD, 0, msgbuf1 }                            };    int i;    u8 partial_pec = 0;     //將要發送的資訊copy到發送緩衝區的第一位元組    msgbuf0[0] = command;    switch(size) {        //quick類型的,其它並不傳輸有效資料,只是將地址寫到匯流排上,等待應答即可        //所以將發送緩衝區長度置為0 .再根據讀/寫操作,調整msg[0]的標誌位        //這類傳輸只需要一次匯流排互動    case I2C_SMBUS_QUICK:        msg[0].len = 0;        /* Special case: The read/write field is used as data */        msg[0].flags = flags | (read_write==I2C_SMBUS_READ)?I2C_M_RD:0;        num = 1;        break;    case I2C_SMBUS_BYTE:        //BYTE類型指一次寫和讀只有一個位元組.這種情況下,讀和寫都只會互動一次        //這種類型的讀有例外,它讀取出來的資料不是放在msg[1]中的,而是存放在msg[0]        if (read_write == I2C_SMBUS_READ) {            /* Special case: only a read! */            msg[0].flags = I2C_M_RD | flags;            num = 1;        }        break;    case I2C_SMBUS_BYTE_DATA:        //Byte_Data是指命令+資料的傳輸形式.在這種情況下,寫只需要一次互動,讀卻要兩次        //第一次將command寫到匯流排上,第二次要轉換方向.要將裝置地址和read標誌寫入匯流排.        //應回答之後再進行read操作        //寫操作佔兩位元組,分別是command+data.讀操作的有效資料只有一個位元組        //互動次數用初始化值就可以了        if (read_write == I2C_SMBUS_READ)            msg[1].len = 1;        else {            msg[0].len = 2;            msgbuf0[1] = data->byte;        }        break;    case I2C_SMBUS_WORD_DATA:        //Word_Data是指命令+雙位元組的形式.這種情況跟Byte_Data的情況類似        //兩者相比只是互動的資料大小不同        if (read_write == I2C_SMBUS_READ)            msg[1].len = 2;        else {            msg[0].len=3;            msgbuf0[1] = data->word & 0xff;            msgbuf0[2] = data->word >> 8;        }        break;    case I2C_SMBUS_PROC_CALL:        //Proc_Call的方式與write 的Word_Data相似,只不過寫完Word_Data之後,要等待它的應答        //應該它需要互動兩次,一次寫一次讀        num = 2; /* Special case */        read_write = I2C_SMBUS_READ;        msg[0].len = 3;        msg[1].len = 2;        msgbuf0[1] = data->word & 0xff;        msgbuf0[2] = data->word >> 8;        break;    case I2C_SMBUS_BLOCK_DATA:        //Block_Data:指command+N段資料的情況.        //如果是讀操作,它首先要寫command到匯流排,然後再讀N段資料.要寫的command已經        //放在msg[0]了.現在只需要將msg[1]的標誌置I2C_M_RECV_LEN位,msg[1]有效長度為1位元組.因為        //adapter驅動會處理好的.現在現在還不知道要傳多少段資料.         //對於寫的情況:msg[1]照例不需要.將要寫的資料全部都放到msb[0]中.相應的也要更新        //msg[0]中的緩衝區長度        if (read_write == I2C_SMBUS_READ) {            msg[1].flags |= I2C_M_RECV_LEN;            msg[1].len = 1; /* block length will be added by                       the underlying bus driver */        } else {            //data->block[0]表示後面有多少段資料.總長度要加2是因為command+count+N段資料            msg[0].len = data->block[0] + 2;            if (msg[0].len > I2C_SMBUS_BLOCK_MAX + 2) {                dev_err(&adapter->dev, "smbus_access called with "                       "invalid block write size (%d)/n",                       data->block[0]);                return -1;            }            for (i = 1; i < msg[0].len; i++)                msgbuf0[i] = data->block[i-1];        }        break;    case I2C_SMBUS_BLOCK_PROC_CALL:        //Proc_Call:表示寫完Block_Data之後,要等它的應答訊息它和Block_Data相比,只是多了一部份應答而已        num = 2; /* Another special case */        read_write = I2C_SMBUS_READ;        if (data->block[0] > I2C_SMBUS_BLOCK_MAX) {            dev_err(&adapter->dev, "%s called with invalid "                "block proc call size (%d)/n", __func__,                data->block[0]);            return -1;        }        msg[0].len = data->block[0] + 2;        for (i = 1; i < msg[0].len; i++)            msgbuf0[i] = data->block[i-1];        msg[1].flags |= I2C_M_RECV_LEN;        msg[1].len = 1; /* block length will be added by                   the underlying bus driver */        break;    case I2C_SMBUS_I2C_BLOCK_DATA:        //I2c Block_Data與Block_Data相似,只不過read的時候,資料長度是預先定義好了的.另外        //與Block_Data相比,中間不需要傳輸Count欄位.(Count表示資料區段數目)        if (read_write == I2C_SMBUS_READ) {            msg[1].len = data->block[0];        } else {            msg[0].len = data->block[0] + 1;            if (msg[0].len > I2C_SMBUS_BLOCK_MAX + 1) {                dev_err(&adapter->dev, "i2c_smbus_xfer_emulated called with "                       "invalid block write size (%d)/n",                       data->block[0]);                return -1;            }            for (i = 1; i <= data->block[0]; i++)                msgbuf0[i] = data->block[i];        }        break;    default:        dev_err(&adapter->dev, "smbus_access called with invalid size (%d)/n",               size);        return -1;    }//如果啟用了PEC.Quick和I2c Block_Data是不支援PEC的

    i = ((flags & I2C_CLIENT_PEC) && size != I2C_SMBUS_QUICK                      && size != I2C_SMBUS_I2C_BLOCK_DATA);    if (i) {        /* Compute PEC if first message is a write */        //如果第一個操作是寫操作        if (!(msg[0].flags & I2C_M_RD)) {            //如果只是寫操作            if (num == 1) /* Write only */                //如果只有寫操作,寫緩衝區要擴充一個位元組,用來存放計算出來的PEC                i2c_smbus_add_pec(&msg[0]);            else /* Write followed by read */                //如果後面還有讀操作,先計算前面寫部份的PEC(注意這種情況下不需要                //擴充寫緩衝區,因為不需要發送PEC.只會接收到PEC)                partial_pec = i2c_smbus_msg_pec(0, &msg[0]);        }        /* Ask for PEC if last message is a read */        //如果最後一次是讀訊息.還要接收到來自slave的PEC.所以接收緩衝區要擴充一個位元組        if (msg[num-1].flags & I2C_M_RD)            msg[num-1].len++;    }     if (i2c_transfer(adapter, msg, num) < 0)        return -1;     /* Check PEC if last message is a read */    //操作完了之後,如果最後一個操作是PEC的讀操作.檢驗後面的PEC是否正確    if (i && (msg[num-1].flags & I2C_M_RD)) {        if (i2c_smbus_check_pec(partial_pec, &msg[num-1]) < 0)            return -1;    }     //操作完了,現在可以將資料放到data部份返回了.    if (read_write == I2C_SMBUS_READ)        switch(size) {            case I2C_SMBUS_BYTE:                data->byte = msgbuf0[0];                break;            case I2C_SMBUS_BYTE_DATA:                data->byte = msgbuf1[0];                break;            case I2C_SMBUS_WORD_DATA:            case I2C_SMBUS_PROC_CALL:                data->word = msgbuf1[0] | (msgbuf1[1] << 8);                break;            case I2C_SMBUS_I2C_BLOCK_DATA:                for (i = 0; i < data->block[0]; i++)                    data->block[i+1] = msgbuf1[i];                break;            case I2C_SMBUS_BLOCK_DATA:            case I2C_SMBUS_BLOCK_PROC_CALL:                for (i = 0; i < msgbuf1[0] + 1; i++)                    data->block[i] = msgbuf1[i];                break;        }    return 0;}在這個函數添上了很詳細的注釋,配和intel的datasheet,應該很容易看懂.在上面的互動過程中,調用了子函數i2c_transfer().它的代碼如下所示:int i2c_transfer(struct i2c_adapter * adap, struct i2c_msg *msgs, int num){    int ret;     if (adap->algo->master_xfer) {#ifdef DEBUG        for (ret = 0; ret < num; ret++) {            dev_dbg(&adap->dev, "master_xfer[%d] %c, addr=0x%02x, "                "len=%d%s/n", ret, (msgs[ret].flags & I2C_M_RD)                ? 'R' : 'W', msgs[ret].addr, msgs[ret].len,                (msgs[ret].flags & I2C_M_RECV_LEN) ? "+" : "");        }#endif         if (in_atomic() || irqs_disabled()) {            ret = mutex_trylock(&adap->bus_lock);            if (!ret)                /* I2C activity is ongoing. */                return -EAGAIN;        } else {            mutex_lock_nested(&adap->bus_lock, adap->level);        }         ret = adap->algo->master_xfer(adap,msgs,num);        mutex_unlock(&adap->bus_lock);         return ret;    } else {        dev_dbg(&adap->dev, "I2C level transfers not supported/n");        return -ENOSYS;    }}因為在這裡的同步用的是mutex.首先判斷判斷是否充許睡眠,如果不允許,嘗試獲鎖.如果獲鎖失敗,則返回,這樣的操作是避免進入睡眠,我們在後面也可以看到,實際的傳輸工作交給了adap->algo->master_xfer()完成. 在這裡,我們終於把i2c_probe_address()的執行分析完了,經過這個分析,我們也知道了資料是怎麼樣傳輸的.我們接著i2c_probe()往下看.如果i2c_probe_address()成功.說明匯流排上確實有這樣的裝置.那麼就會調用驅動中的回呼函數.在ad7148的驅動中,如下所示:return i2c_probe(adapter, &addr_data, ad7418_detect);也就是說,要調用的回呼函數是ad7418_detect().這個函數中我們只分析和i2c架構相關的部份.程式碼片段如下所示:static int ad7418_detect(struct i2c_adapter *adapter, int address, int kind){    struct i2c_client *client;    ……    ……client->addr = address;    client->adapter = adapter;    client->driver = &ad7418_driver;     i2c_set_clientdata(client, data);    ……    ……if ((err = i2c_attach_client(client)))        goto exit_free;    ……    ……}結合上面關於new-style形式的驅動分析.發現這裡走的是同一個套路,即初始化了client.然後調用i2c_attach_client().後面的流程就跟上面分析的一樣了.只不過,不相同的是,這裡clinet已經指定了驅動為ad7418_driver.應該在註冊clinet->dev之後,就不會走bus->match和bus->probe的流程了. 
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.