linux内核I2C子系统学习(三)
具體如下:
●????構(gòu)建i2c_driver
| static?struct?i2c_driver pca953x_driver = { ????????????????.driver = { ????????????????????????????????????.name= "pca953x", //名稱 ????????????????????????????????}, ????????????????.id= ID_PCA9555,//id號(hào) ????????????????.attach_adapter= pca953x_attach_adapter, //調(diào)用適配器連接設(shè)備 ????????????????.detach_client= pca953x_detach_client,//讓設(shè)備脫離適配器 ????????}; |
?
?
●????注冊(cè)i2c_driver
?
| static?int?__init pca953x_init(void) ????????{ ????????????????return?i2c_add_driver(&pca953x_driver); ????????} ????????module_init(pca953x_init); 執(zhí)行i2c_add_driver(&pca953x_driver)后,如果內(nèi)核中已經(jīng)注冊(cè)了i2c適配器,則順序調(diào)用這些適配器來(lái)連接我們的i2c設(shè)備。此過(guò)程是通過(guò)調(diào)用i2c_driver中的attach_adapter方法完成的。具體實(shí)現(xiàn)形式如下: static?int?pca953x_attach_adapter(struct?i2c_adapter *adapter) ????????{ ????????????????return?i2c_probe(adapter, &addr_data, pca953x_detect); ????????????????/* ????????????????adapter:適配器 ????????????????addr_data:地址信息 ????????????????pca953x_detect:探測(cè)到設(shè)備后調(diào)用的函數(shù) ????????????????*/ ????????} 地址信息addr_data是由下面代碼指定的。 ????????/* Addresses to scan */ ????????static?unsigned short?normal_i2c[] = {0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,I2C_CLIENT_END}; ????????I2C_CLIENT_INSMOD; 注意:normal_i2c里的地址必須是你i2c芯片的地址。否則將無(wú)法正確探測(cè)到設(shè)備。而I2C_ CLIENT_INSMOD是一個(gè)宏,它會(huì)利用normal_i2c構(gòu)建addr_data。 |
?
?
●????構(gòu)建i2c_client,并注冊(cè)字符設(shè)備驅(qū)動(dòng)
i2c_probe在探測(cè)到目標(biāo)設(shè)備后,后調(diào)用pca953x_detect,并把當(dāng)時(shí)的探測(cè)地址address作為參數(shù)傳入。
| static?int?pca953x_detect(struct?i2c_adapter *adapter, int?address, int?kind) ????????{ ????????????????struct?i2c_client *new_client; ????????????????struct?pca953x_chip *chip; //設(shè)備結(jié)構(gòu)體 ????????????????int?err = 0,result; ????????????????dev_t pca953x_dev=MKDEV(pca953x_major,0);//構(gòu)建設(shè)備號(hào),根據(jù)具體情況設(shè)定,這里我只考慮了normal_i2c中只有一個(gè)地址匹配的情況。主次設(shè)備號(hào)來(lái)源 ????????????????if?(!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA| I2C_FUNC_SMBUS_WORD_DATA))//判定適配器能力 ????????????????goto?exit; ????????????????if?(!(chip = kzalloc(sizeof(struct?pca953x_chip), GFP_KERNEL))) { ????????????????????????err = -ENOMEM; ????????????????????????goto?exit; ????????????????} ????????????????/****構(gòu)建i2c-client****/ ????????????????chip->client=kzalloc(sizeof(struct?i2c_client),GFP_KERNEL); ????????????????new_client = chip->client; ????????????????i2c_set_clientdata(new_client, chip); ????????????????new_client->addr = address; ????????????????new_client->adapter = adapter; ????????????????new_client->driver = &pca953x_driver; ????????????????new_client->flags = 0; ????????????????strlcpy(new_client->name, "pca953x", I2C_NAME_SIZE); ????????????????if?((err = i2c_attach_client(new_client)))//注冊(cè)i2c_client ????????????????goto?exit_kfree; ????????????????if?(err) ????????????????goto?exit_detach; ????????????????if(pca953x_major) ????????????????{ ????????????????????????result=register_chrdev_region(pca953x_dev,1,"pca953x"); ????????????????} ????????????????else{ ????????????????????????result=alloc_chrdev_region(&pca953x_dev,0,1,"pca953x"); ????????????????????????pca953x_major=MAJOR(pca953x_dev); ????????????????} ????????????????if?(result < 0) { ????????????????????????printk(KERN_NOTICE "Unable to get pca953x region, error %d/n", result); ????????????????????????return?result; ????????????????} ????????????????pca953x_setup_cdev(chip,0); //注冊(cè)字符設(shè)備,此處不詳解 ????????????????return?0; ????????????????exit_detach: ????????????????i2c_detach_client(new_client); ????????exit_kfree: ????????????????kfree(chip); ????????exit: ????????????????return?err; ????????} |
i2c_check_functionality用來(lái)判定設(shè)配器的能力,這一點(diǎn)非常重要。你也可以直接查看對(duì)應(yīng)設(shè)配器的能力,如
| static?const?struct?i2c_algorithm smbus_algorithm = { ????????????????.smbus_xfer= i801_access, ????????????????.functionality= i801_func, ????????}; ????????static?u32 i801_func(struct?i2c_adapter *adapter) ????????{ ????????????????????????return?I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | ???????????????????????????????I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | ???????????????????????????????I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_WRITE_I2C_BLOCK|(isich4 ? I2C_FUNC_SMBUS_HWPEC_CALC : 0); ????????} |
? ? 字符驅(qū)動(dòng)的具體實(shí)現(xiàn)
| struct?file_operations pca953x_fops = { ????????????????.owner = THIS_MODULE, ????????????????.ioctl= pca953x_ioctl, ????????????????.open= pca953x_open, ????????????????.release =pca953x_release, ????????}; |
字符設(shè)備驅(qū)動(dòng)本身沒(méi)有什么好說(shuō)的,這里主要想說(shuō)一下,如何在驅(qū)動(dòng)中調(diào)用i2c設(shè)配器幫我們完成數(shù)據(jù)傳輸。
目前設(shè)配器主要支持兩種傳輸方法:smbus_xfer和master_xfer。一般來(lái)說(shuō),如果設(shè)配器支持了master_xfer那么它也可以模擬支持smbus的傳輸。但如果只實(shí)現(xiàn)smbus_xfer,則不支持一些i2c的傳輸。
int (*master_xfer)(struct i2c_adapter *adap,struct i2c_msg *msgs,int num);
int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,unsigned short flags, char read_write,?u8 command, int size, union i2c_smbus_data * data);
master_xfer中的參數(shù)設(shè)置,和前面的用戶空間編程一致。現(xiàn)在只是要在驅(qū)動(dòng)中構(gòu)建相關(guān)的參數(shù)然后調(diào)用i2c_transfer來(lái)完成傳輸既可。
int i2c_transfer(struct i2c_adapter * adap, struct i2c_msg *msgs, int num)
smbus_xfer中的參數(shù)設(shè)置及調(diào)用方法如下:
| static?int?pca953x_write_reg(struct?pca953x_chip *chip, int?reg, uint16_t val) ????????{ ????????????????int?ret; ????????????????ret = i2c_smbus_write_word_data(chip->client, reg << 1, val); ????????????????if?(ret < 0) { ????????????????????????????????dev_err(&chip->client->dev, "failed writing register/n"); ????????????????????????????????????????return?-EIO; ????????????????????????????????} ????????????????return?0; ????????} |
上面函數(shù)完成向芯片的地址為reg的寄存器寫(xiě)一個(gè)16bit的數(shù)據(jù)。i2c_smbus_write_word_data的實(shí)現(xiàn)如下:
| s32 i2c_smbus_write_word_data(struct?i2c_client *client, u8 command, u16 value) ????????{ ????????????????union?i2c_smbus_data data; ????????????????data.word = value; ????????????????return?i2c_smbus_xfer(client->adapter,client->addr,client->flags, ?????????????????I2C_SMBUS_WRITE,command,I2C_SMBUS_WORD_DATA,&data); ????????} |
從中可以看出smbus傳輸一個(gè)16位數(shù)據(jù)的方法。其它操作如:字符寫(xiě)、字符讀、字讀、塊操作等,可以參考內(nèi)核的i2c-core.c中提供的方法。
注釋:i2c_client 信息通常在BSP的板文件中通過(guò)i2c_board_info 填充,如: 定義一個(gè)I2C設(shè)備ID為“ad7142_joystick”、地址為0x2C、中斷號(hào)為IRQ_PF5的i2c_client ? static struct i2c_board_info __initdata xxx_i2c_board_info[] = { ? ? { ????? ? I2C_BOARD_INFO(“ad7142_joystick”,0x2C), ????? ? .irq = IRQ_PF5,????},
......... }; 然后注冊(cè) i2c_register_board_info(1, i2c_devs1, ARRAY_SIZE(i2c_devs1)); 通過(guò)這個(gè)就完成了i2c_client 的注冊(cè)
●????注銷i2c_driver
| static?void?__exit pca953x_exit(void) ????????{ ????????????????i2c_del_driver(&pca953x_driver); ????????} ????????module_exit(pca953x_exit); |
順序調(diào)用內(nèi)核中注冊(cè)的適配器來(lái)斷開(kāi)我們注冊(cè)過(guò)的i2c設(shè)備。此過(guò)程通過(guò)調(diào)用i2c_driver中的attach_adapter方法完成的。具體實(shí)現(xiàn)形式如下:
?
| static?int?pca953x_detach_client(struct?i2c_client *client) ????????{ ????????????????int?err; ????????????????struct?pca953x_chip *data; ????????????????if?((err = i2c_detach_client(client)))//斷開(kāi)i2c_client ????????????????return?err; ????????????????data=i2c_get_clientdata(client); ????????????????cdev_del(&(data->cdev)); ????????????????unregister_chrdev_region(MKDEV(pca953x_major, 0), 1); ????????????????kfree(data->client); ????????????????kfree(data); ????????????????return?0; ????????} |
其實(shí)主芯片的i2c的驅(qū)動(dòng)基本上都支持啦,哈哈,所以剩下的工作量不是很大,只需完成從芯片的i2c的驅(qū)動(dòng)操作就ok啦,那個(gè)只是分析如何編寫(xiě)的便于深入理解。
另外:
?
幾個(gè)重要的結(jié)構(gòu)體:i2c_msg(設(shè)置設(shè)備地址的)、i2c_client(從機(jī)設(shè)備的地址,一般采用平臺(tái)設(shè)備的形式,用probe函數(shù)探測(cè))、i2c_driver自己構(gòu)建?
幾個(gè)重要的方法:i2c_add_driver添加設(shè)備、i2c_transfer用于進(jìn)行I2C適配器和I2C設(shè)備之間的一組消息的交互 I2C與SCCB協(xié)議區(qū)別:從機(jī)地址因?yàn)镮2C是7位地址,最后一位是讀寫(xiě)位,而SCCB是8位地址,比如ov9650,他是SCCB協(xié)議,他的地址是0x60,那么如果掛接到I2C總線上,他的地址就變成0x30了,這樣算的: SCCB地址::: ?0x60: ? 0 1 1 0_0 0 0?0 ? ? 這個(gè)0還是地址位 I2C地址:::: ? ? ? ? ? ? ? ??0 1 1 0_0 0 0?0最后紅色的0是讀寫(xiě)位,那么地址變成了7 位?+讀寫(xiě)位 即?0 1 1_ 0 0 0 0?+0( 讀寫(xiě)位 ) ?所以從機(jī)地址變成了0x30 ? linux內(nèi)核I2C驅(qū)動(dòng)基本上就這些了!轉(zhuǎn)載于:https://www.cnblogs.com/wanghuaijun/p/7057750.html
總結(jié)
以上是生活随笔為你收集整理的linux内核I2C子系统学习(三)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Linux Shell脚本编程学习笔记和
- 下一篇: 关于模板引擎一