本帖最后由 kidsure 于 2017-8-25 17:54 编辑
瑞芯微的板子是支持设备树的,一般来说我比较喜欢用i2c_register_board_info这类的函数来注册设备,不过还是听人家厂商的建议吧,修改一下dts就好,瑞芯微平台的设备树位置在kernel/arch/arm64/boot/dts/rockchip/rk3399-firefly-mini-edp.dts这里我们只修改一下i2c的设备节点就好。简单修改一下,基本我们就为我们的设备添加一下设备的总线和设备地址就好
这样在设备启动uboot的时候会提前判断是否有该设备在这条总线上,确定是否加载。
然后确定有设备之后会在驱动链表里查询是否有对应的驱动程序。具体的详细内容,可以看一下linux内核情景分析。
说实话写这些东西的时候比较麻烦,我的思路也不太清晰,包括rk3399的wiki里面对于i2c驱动的帮助很是模糊,哈哈 ,还是提议大家去看源代码比较方便。我们按照wiki的思路来吧,毕竟我从头讲起来比较麻烦。附rk3399的wiki地址http://wiki.t-firefly.com/index.php/Firefly-RK3399/I2C。。
1.首先我们要写一个 of_device_id的结构体,用于调用在dts设备树中注册的设备。
- static struct of_device_id aw2013_ts_idss[] = {
- {.compatible = "aw2013_kidsure"},
- {}
- };
复制代码
- static const struct i2c_device_id aw2013_ts_ids[] = {
- {AW_NAME , 0},
- {}
- };
- MODULE_DEVICE_TABLE(i2c, aw2013_ts_ids);
复制代码
- static struct i2c_driver aw_i2c_driver = {
- .probe = aw_i2c_probe,
- //.remov = aw_i2c_remove,
- .driver = {
- .name = AW_NAME,
- .owner = THIS_MODULE,
- .of_match_table = of_match_ptr(aw2013_ts_idss),
- },
- .id_table = aw_i2c_id,
- };
复制代码
我们在驱动的结构体中.driver中的.of_match_table中添加,在驱动和设备匹配的时候找到设备树中的设备,通过 compatible 匹配(注意compatible 必须和设备树dts文件中的compatible 保持一致,否则无法找到该设备)。通过前两段代码,使设备树中设备可以在驱动中被调用。这些与wiki中说的差不多。那么来看咱们自己的东西吧,如何注册驱动和测试点。
- static int __init aw2013_i2C_init(void)
- {
- //int ret = i2c_register_board_info(1, &i2c_devs_aw2013, 1);
- //pr_emerg("init is running!!%dn", ret);
- int ret = i2c_add_driver(&aw_i2c_driver);
- pr_emerg("aw2013 dirver is regist %d", ret);
- proc_create("driver/aw2013_mtk", 0, NULL, &aw2013_fops);
- return 0;
- }
- static void __exit aw2013_i2C_exit(void){
- }
- module_init(aw2013_i2C_init);
- module_exit(aw2013_i2C_exit);
- MODULE_LICENSE("GPL");
复制代码
加上gpl协议和驱动程序的入口和出口。然后开始编写驱动,我比较懒(主要因为3399的编译脚本很是反人类,只要报警告就会停止编译)出口函数的话并没有实现,不过是不影响使用的。我们来看一下入口函数哈,入口函数的主要作用说白了就是注册驱动和注册设备,咱们的设备在设备树中已经注册好了,所以省略了这一步,就是我注释掉的那一部分,我没有试,不过不用设备树应该也是可以实现的。使用 i2c_add_driver这个函数来注册驱动到系统中,被注册的结构体就是我们刚刚实现的和wiki中类似的结构体。然后使用proc_create函数在proc目录下生成一个测试节点,我们创建了一个driver/aw2013_mtk节点。
编译成功后会在/proc/driver/目录下生成咱们的测试点,并且chmod 777 给他足够的读写权限。然后定义一下aw_2013_fop,当读写该节点的时候可以直接调用这些fop函数。这个方法可以让用户通过用户空间直接操作到内核空间的设备,百试不爽的调试方法。
- static ssize_t aw_test_read(struct file *file, char __user *data, size_t len, loff_t *ppos)
- {
- return 0;
- }
- //static int aw_test_write(struct file *file, const char *buffer, size_t count,
- // loff_t *data)
- static ssize_t aw_test_write (struct file *file, const char *buffer, size_t count ,
- loff_t *data){
- int ret , status,id;
- char buf[2];
- // char tmp = 0;
- char r_data,g_data,b_data;
- ret = sscanf(buffer, "%x", &status);
- rgb_state = status;
- pr_emerg("_____sure___%s, %x", buffer,rgb_state);
- r_data = rgb_state>>16&0xff;
- g_data = rgb_state>>8&0xff;
- b_data = rgb_state&0xff;
- buf[0]=0x31;
- buf[1]=0x62;
- breathlight_master_send(0x45,buf,2);
- buf[0]=0x32;
- buf[1]=0x62;
- breathlight_master_send(0x45,buf,2);
- buf[0]=0x33;
- buf[1]=0x62;
- breathlight_master_send(0x45,buf,2);
- buf[0]=0x34;
- buf[1]=b_data;
- breathlight_master_send(0x45,buf,2);
- buf[0]=0x35;
- buf[1]=g_data;
- breathlight_master_send(0x45,buf,2);
- buf[0]=0x36;
- buf[1]=r_data;
- breathlight_master_send(0x45,buf,2);
- for(id = 0; id < 3 ; id ++){
- buf[0]=0x37+id*3;
- buf[1]=0x22;
- breathlight_master_send(0x45,buf,2);
- buf[0]=0x38+id*3;
- buf[1]=0x22;
- breathlight_master_send(0x45,buf,2);
- buf[0]=0x39+id*3;
- buf[1]=0x00;
- breathlight_master_send(0x45,buf,2);
- }
- buf[0]=0x30;
- buf[1]=0x07;
- breathlight_master_send(0x45,buf,2);
-
- return 0;
- }
- static struct file_operations aw2013_fops = {
- .read = aw_test_read,
- .write = aw_test_write,
- };
复制代码
因为我们的i2c设备其实是一个rgb的led灯,可以通过向节点中写数据以改变led灯的颜色。(听起来好low啊)不过不耽误咱们用哈,我现在只是简单的移植过来。
写完了这些之后将c文件放到源码目录的kernel/driver/misc下面(其实哪个目录都无所谓了,哈哈),然后改一下对应目录的Makefile,在其中加一条obj-y = xxxxxxx.o 这个o文件的名字和你的c文件相同就好了。然后重新编译内核并且同步到update.img里面。烧写到板子上面,我这里详细的驱动实现还没有仔细调通,不过测试点基本可用了。下一篇开始我会调通rgb读写测试点操作,其实这是个小测试,还在考虑要不要编写HAL。。。
下一篇帖预告一下,会继续该框架实现内部实现内容,驱动已经编在内核里面写进去了。刚刚也给大家截图crt中已经有了测试点了。因为我做的比较偏底层对图形界面和上层要求不高就使用简单的插件来实现显示了哈哈(省着搭建vnc了,装完逼就跑贼爽)
顺手给喜欢玩安卓的童鞋们安利个插件,挺好用的。哈哈
|