实例解析linux内核I2C体系结构(2)-安博体育官网入口网址
发布时间:2024-11-09 人浏览
本文摘要:四、在内核里写出i2c设备驱动的两种方式前文讲解了利用/dev/i2c-0在应用层已完成对i2c设备的操作者,但很多时候我们还是习惯为i2c设备在内核层撰写驱动程序。
四、在内核里写出i2c设备驱动的两种方式前文讲解了利用/dev/i2c-0在应用层已完成对i2c设备的操作者,但很多时候我们还是习惯为i2c设备在内核层撰写驱动程序。目前内核反对两种撰写i2c驱动程序的方式。
下面分别讲解这两种方式的构建。这里分别称之为这两种方式为Adapter方式(LEGACY)和Probe方式(newstyle)。(1)Adapter方式(LEGACY)(下面的实例代码是在2.6.27内核的pca953x.c基础上改动的,完整代码使用的是本文即将辩论的第2种方式,即Probe方式)●建构i2c_driverstaticstructi2c_driverpca953x_driver={.driver={.name=pca953x,//名称},.id=ID_PCA9555,//id号.attach_adapter=pca953x_attach_adapter,//调用适配器相连设备.detach_client=pca953x_detach_client,//让设备瓦解适配器};●登记i2c_driverstaticint__initpca953x_init(void){returni2c_add_driver(pca953x_driver);}module_init(pca953x_init);●attach_adapter动作继续执行i2c_add_driver(pca953x_driver)后不会,如果内核中早已登记了i2c适配器,则顺序调用这些适配器来相连我们的i2c设备。
此过程是通过调用i2c_driver中的attach_adapter方法已完成的。明确构建形式如下:staticintpca953x_attach_adapter(structi2c_adapter*adapter){returni2c_probe(adapter,addr_data,pca953x_detect);/*adapter:适配器addr_data:地址信息pca953x_detect:观测到设备后调用的函数*/}地址信息addr_data是由下面代码登录的。
/*Addressestoscan*/staticunsignedshortnormal_i2c[]={0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,I2C_CLIENT_END};I2C_CLIENT_INSMOD;留意:normal_i2c里的地址必需是你i2c芯片的地址。否则将无法准确观测到设备。而I2C_CLIENT_INSMOD是一个宏,它不会利用normal_i2c建构addr_data。●建构i2c_client,并登记字符设备驱动i2c_probe在观测到目标设备后,后调用pca953x_detect,并把当时的观测地址address作为参数起源于。
staticintpca953x_detect(structi2c_adapter*adapter,intaddress,intkind){structi2c_client*new_client;structpca953x_chip*chip;//设备结构体interr=0,result;dev_tpca953x_dev=MKDEV(pca953x_major,0);//建构设备号,根据具体情况原作,这里我只考虑到了normal_i2c中只有一个地址给定的情况。if(!i2c_check_functionality(adapter,I2C_FUNC_SMBUS_BYTE_DATA|I2C_FUNC_SMBUS_WORD_DATA))//判断适配器能力gotoexit;if(!(chip=kzalloc(sizeof(structpca953x_chip),GFP_KERNEL))){err=-ENOMEM;gotoexit;}/****建构i2c-client****/chip-client=kzalloc(sizeof(structi2c_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)))//登记i2c_clientgotoexit_kfree;if(err)gotoexit_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(result0){printk(KERN_NOTICEUnabletogetpca953xregion,error%d\n,result);returnresult;}pca953x_setup_cdev(chip,0);//登记字符设备,此处不编撰return0;exit_detach:i2c_detach_client(new_client);exit_kfree:kfree(chip);exit:returnerr;}i2c_check_functionality用来判断另设配器的能力,这一点十分最重要。你也可以必要查阅对应另设配器的能力,如staticconststructi2c_algorithmsmbus_algorithm={.smbus_xfer=i801_access,.functionality=i801_func,};staticu32i801_func(structi2c_adapter*adapter){returnI2C_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);}●字符驱动的明确构建structfile_operationspca953x_fops={.owner=THIS_MODULE,.ioctl=pca953x_ioctl,.open=pca953x_open,.release=pca953x_release,};字符设备驱动本身没什么好说道的,这里主要想要说道一下,如何在驱动中调用i2c设配器老大我们已完成数据传输。
目前另设配器主要反对两种传输方法:smbus_xfer和master_xfer。一般来说,如果另设配器反对了master_xfer那么它也可以仿真反对smbus的传输。但如果只构建smbus_xfer,则不反对一些i2c的传输。int(*master_xfer)(structi2c_adapter*adap,structi2c_msg*msgs,intnum);int(*smbus_xfer)(structi2c_adapter*adap,u16addr,unsignedshortflags,charread_write,u8command,intsize,unioni2c_smbus_data*data);master_xfer中的参数设置,和前面的用户空间编程完全一致。
现在只是要在驱动中建构涉及的参数然后调用i2c_transfer来已完成传输既可。inti2c_transfer(structi2c_adapter*adap,structi2c_msg*msgs,intnum)smbus_xfer中的参数设置及调用方法如下:staticintpca953x_write_reg(structpca953x_chip*chip,intreg,uint16_tval){intret;ret=i2c_smbus_write_word_data(chip-client,reg1,val);if(ret0){dev_err(chip-client-dev,failedwritingregister\n);return-EIO;}return0;}上面函数已完成向芯片的地址为reg的寄存器写出一个16bit的数据。i2c_smbus_write_word_data的构建如下:s32i2c_smbus_write_word_data(structi2c_client*client,u8command,u16value){unioni2c_smbus_datadata;data.word=value;returni2c_smbus_xfer(client-adapter,client-addr,client-flags,I2C_SMBUS_WRITE,command,I2C_SMBUS_WORD_DATA,data);}借此可以显现出smbus传输一个16位数据的方法。
其它操作者如:字符写出、字符读书、字读书、块操作者等,可以参照内核的i2c-core.c中获取的方法。●吊销i2c_driverstaticvoid__exitpca953x_exit(void){i2c_del_driver(pca953x_driver);}module_exit(pca953x_exit);●detach_client动作顺序调用内核中登记的适配器来插入我们登记过的i2c设备。
此过程通过调用i2c_driver中的attach_adapter方法已完成的。明确构建形式如下:staticintpca953x_detach_client(structi2c_client*client){interr;structpca953x_chip*data;if((err=i2c_detach_client(client)))//插入i2c_clientreturnerr;data=i2c_get_clientdata(client);cdev_del((data-cdev));unregister_chrdev_region(MKDEV(pca953x_major,0),1);kfree(data-client);kfree(data);return0;}(2)Probe方式(newstyle)●建构i2c_driver和LEGACY方式一样,也必须建构i2c_driver,但是内容有所不同。staticstructi2c_driverpca953x_driver={.driver={.name=pca953x,},.probe=pca953x_probe,//当有i2c_client和i2c_driver给定时调用.remove=pca953x_remove,//吊销时调用.id_table=pca953x_id,//给定规则};●登记i2c_driverstaticint__initpca953x_init(void){returni2c_add_driver(pca953x_driver);}module_init(pca953x_init);在登记i2c_driver的过程中,是将driver登记到了i2c_bus_type的总线上。
此总线的给定规则是:staticconststructi2c_device_id*i2c_match_id(conststructi2c_device_id*id,conststructi2c_client*client){while(id-name[0]){if(strcmp(client-name,id-name)==0)returnid;id++;}returnNULL;}可以显现出是利用i2c_client的名称和id_table中的名称做到给定的。本驱动中的id_table为staticconststructi2c_device_idpca953x_id[]={{pca9534,8,},{pca9535,16,},{pca9536,4,},{pca9537,4,},{pca9538,8,},{pca9539,16,},{pca9554,8,},{pca9555,16,},{pca9557,8,},{max7310,8,},{}};看见现在我们应当不会有这样的疑惑,在Adapter模式中,i2c_client是我们自己结构出来的,而现在的i2c_client就是指哪来的呢?想到下面的说明●登记i2c_board_info对于Probe模式,一般来说在平台代码中要已完成i2c_board_info的登记。方法如下:staticstructi2c_board_info__initdatatest_i2c_devices[]={{I2C_BOARD_INFO(pca9555,0x27),//pca9555为芯片名称,0x27为芯片地址.platform_data=pca9555_data,},{I2C_BOARD_INFO(mt9v022,0x48),.platform_data=iclink[0],/*Withextender*/},{I2C_BOARD_INFO(mt9m001,0x5d),.platform_data=iclink[0],/*Withextender*/},};i2c_register_board_info(0,test_i2c_devices,ARRAY_SIZE(test_i2c_devices));//登记i2c_client就是在登记过程中建构的。
但有一点必须留意的是i2c_register_board_info并没EXPORT_SYMBOL给模块用于。●字符驱动登记在Probe方式下,加到字符驱动的方位在pca953x_probe中。staticint__devinitpca953x_probe(structi2c_client*client,conststructi2c_device_id*id){/****字符设备驱动登记方位****/return0;}●吊销i2c_driverstaticvoid__exitpca953x_exit(void){i2c_del_driver(pca953x_driver);}module_exit(pca953x_exit);●吊销字符设备驱动在Probe方式下,吊销字符驱动的方位在pca953x_remove中。
staticint__devinitpca953x_remove(structi2c_client*client){/****字符设备驱动吊销的方位****/return0;}●I2C设备的数据交互方法(即:调用适配器操作者设备的方法)和Adapter方式下完全相同。
本文来源:安博体育网页版下载-www.zjjs188.com