Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1064611
  • 博文数量: 573
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 66
  • 用 户 组: 普通用户
  • 注册时间: 2016-06-28 16:21
文章分类

全部博文(573)

文章存档

2018年(3)

2016年(48)

2015年(522)

分类: LINUX

2015-12-04 17:00:28

驱动代码如下:
iic.c文件

点击(此处)折叠或打开

  1. /*模仿这个程序 : linux/drivers/i2c/busses/i2c-s3c2410.c
  2. *左边的适配器使用内核提供的代码
  3. *本程序实现 右边的 驱动代码
  4. 详细讨论一下linux中IIC的体系结构

  5. */

  6. #include <linux/kernel.h>
  7. #include <linux/init.h>
  8. #include <linux/module.h>
  9. #include <linux/slab.h>
  10. #include <linux/jiffies.h>
  11. #include <linux/i2c.h>
  12. #include <linux/mutex.h>
  13. #include <linux/fs.h>
  14. #include <asm/uaccess.h>
  15. #include <linux/device.h>

  16. struct i2c_client * at24c02_client;
  17. static struct class * at24c02_class;
  18. static struct device * at24c02_device;
  19. static struct i2c_driver at24c02_driver; /*把下面定义的结构体,提前声明一下*/
  20. static int major = 0; /*定义主设备号*/

  21. static unsigned short ignore[] = { I2C_CLIENT_END }; //省略
  22. static unsigned short normal_addr[] = { 0x50, I2C_CLIENT_END };//正常地址,第1个:设备地址,第2个: #define I2C_CLIENT_END        0xfffeU
  23. /* 地址值是7位:1010000 ,所以是0x50,不是0xA0*/ /* 改为0x60的话, 由于不存在设备地址为0x60的设备, 所以at24cxx_detect不被调用 */
  24. /*
  25. static unsigned short force_addr[] = {ANY_I2C_BUS, 0x60, I2C_CLIENT_END}; //#define ANY_I2C_BUS        0xffff 指定这个设备让哪个IIC总线适配器来支持它。
  26. static unsigned short * forces[] = {force_addr, NULL};
  27. */

  28. static struct i2c_client_address_data addr_data = /*设备地址*/
  29. {
  30.         .normal_i2c    = normal_addr, /* 要发出S信号和设备地址并得到ACK信号,才能确定存在这个设备,才会调用 at24c02_detect 函数 */
  31.         .probe        = ignore, /*省略*/
  32.         .ignore        = ignore,
  33.         //.forces = forces, /* 不发送S信号,不得到ACK信号,强制认为存在这个设备(同时存在 normal_i2c 和 forces时,优先使用 forces)*/
  34. };

  35. static int at24c02_release(struct inode *inode, struct file *file)
  36. {
  37.         /*printk("Driver : at24c02_release Called!\n");*/
  38.         return 0;
  39. }

  40. static int at24c02_open(struct inode *inode, struct file *file)
  41. {
  42.         /*printk("Driver : at24c02_open Called!\n");*/
  43.         return 0;
  44. }

  45. /*
  46. *读取函数 : 应用程序从at24c02指定的内部地址中,读取数据。
  47. *读取函数要麻烦一点 : 需要先写 at24c02的内部地址,再读取这个地址上的数据。
  48. * 所以,需要传输2次消息
  49. *用 buf[0] : 来表示 地址 (at24c02的内部地址), 内核读取到的数据后,再用这个字节传递出去就行,所以只需要buf[0]
  50. */
  51. static ssize_t at24c02_read(struct file *file, char __user *buf, size_t size, loff_t * offset)
  52. {
  53.         unsigned char address;
  54.         unsigned char data;
  55.         
  56.         struct i2c_msg msgs[2]; /*i2c传输的消息 : 写1次,读1次*/
  57.         int ret = -1;

  58.         /*printk("Driver : at24c02_read Called!\n");*/
  59.         
  60.         if(size != 1)
  61.         {
  62.                 return -EINVAL; /*这些返回码都定义在一个统一的头文件中*/
  63.         }
  64.         copy_from_user(&address, buf, 1); /*把应用程序的数据,拷贝到内核*/
  65.         /*printk("Driver : address=[%d]\n", address);*/

  66.         /*1,读取之前,要先写at24c02的内部地址*/
  67.         /*数据传输3要素 : 源, 目的, 长度*/
  68.         msgs[0].addr = at24c02_client->addr; /*目的 : at24c02的固化地址*/
  69.         msgs[0].buf = &address;                             /*: at24c02的内部地址值*/
  70.         msgs[0].len = 1;                                             /*长度 : 地址 = 1个字节*/
  71.         msgs[0].flags = 0;                                         /*0:*/
  72.         
  73.         /*2,写地址之后,就可以启动读操作了*/
  74.         /*数据传输3要素 : 源, 目的, 长度*/
  75.         msgs[1].addr = at24c02_client->addr; /*: at24c02的固化地址,从这个设备读取数据*/
  76.         msgs[1].buf = &data;                                     /*目的 : 从at24c02读取到的数据,存放在这里*/
  77.         msgs[1].len = 1;                                             /*长度 : 地址 = 1个字节*/
  78.         msgs[1].flags = I2C_M_RD;                         /*0:写 I2C_M_RD :*/

  79.         ret = i2c_transfer(at24c02_client->adapter, msgs, 2); /*启动i2c传输, 2个消息*/
  80.         /*printk("Driver : ret=[%d]\n", ret);*/
  81.         if(ret == 2) /*成功的消息个数 : 表示2个消息成功*/
  82.         {
  83.                 copy_to_user(buf, &data, 1); /*把内核的数据,拷贝到应用程序*/
  84.                 return 1; /*表示成功读取一个数据*/
  85.         }
  86.         else
  87.         {
  88.                 return -EIO; /*表示IO错误*/
  89.         }
  90.         
  91.         return 0;
  92. }

  93. /*
  94. *写入函数:应用程序将数据,写入at24c02中
  95. *用 buf[0] : 来表示 地址 (at24c02的内部地址) 用 buf[1] : 来表示 数据 (将写入at24c02的数据)
  96. *所以至少需要2个字节来给内核传递数据。
  97. */
  98. static ssize_t at24c02_write(struct file *file, const char __user *buf, size_t size, loff_t *offset)
  99. {
  100.         unsigned char val[2];
  101.         struct i2c_msg msgs[1]; /*i2c传输的消息*/
  102.         int ret = 0;
  103.         
  104.         /*printk("Driver : at24c02_write Called!\n");*/

  105.         if(size != 2) /*应用程序写的字节,一定是2个*/
  106.         {
  107.                 return -EINVAL; /*表示输入的值错误*/
  108.         }
  109.         copy_from_user(val, buf, 2); /*把应用程序的数据,拷贝到内核*/
  110.         /*printk("Driver : addr val[0]=[%d]\n", val[0]);*/
  111.         /*printk("Driver : data val[1]=[%d]\n", val[1]);*/

  112.         /*数据传输3要素 : 源, 目的, 长度*/
  113.         msgs[0].addr = at24c02_client->addr; /*目的 : at24c02的固化地址*/
  114.         msgs[0].buf = val;                                         /*: at24c02的内部地址 + 将要写入的数据*/
  115.         msgs[0].len = 2;                                             /*长度 : 地址+数据 = 2个字节*/
  116.         msgs[0].flags = 0;                                         /*0:*/

  117.         ret = i2c_transfer(at24c02_client->adapter, msgs, 1); /*启动i2c传输 : 最后一个参数表示消息个数*/
  118.         /*printk("Driver : ret=[%d]\n", ret);*/
  119.         if(ret == 1) /*成功的消息个数 : 表示1个消息成功*/
  120.         {
  121.                 return 2; /*表示成功写入2个数据*/
  122.         }
  123.         else
  124.         {
  125.                 return -EIO; /*表示IO错误*/
  126.         }
  127. }

  128. /*定义文件操作结构体*/
  129. static struct file_operations at24c02_fops = {
  130.         .owner = THIS_MODULE,
  131.         .read = at24c02_read,
  132.         .write = at24c02_write,
  133.         .open = at24c02_open,
  134.         .release = at24c02_release,
  135. };

  136. /*
  137. *函数功能 : 探测到硬件设备存在之后,执行的函数,要完成连接client设备的过程。
  138. *detect : 侦查
  139. */
  140. static int at24c02_detect_devices(struct i2c_adapter *adapter, int address, int kind)
  141. {
  142.         printk("at24c02_detect_devices\n");

  143.         /* 构造一个i2c_client结构体: 以后收发数据时就会用到这个结构体了 */
  144.         at24c02_client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
  145.         at24c02_client->addr = address; /*设备地址*/
  146.         at24c02_client->adapter = adapter; /*连接左边的 适配器*/
  147.         at24c02_client->driver = &at24c02_driver; /*连接右边的 驱动*/
  148.         strcpy(at24c02_client->name, "at24c02");
  149.         i2c_attach_client(at24c02_client); /*连接右边的驱动,这样左右两边都联系起来了*/

  150.         /*注册一个字符设备*/
  151.         major = register_chrdev(0, "at24c02", &at24c02_fops);
  152.         /*这个是2.4内核老方法注册:
  153.         次设备号0到255都对应这一个 file_operations 结构体
  154.         2.6内核使用新方法注册:就是把上面1步拆分成4步
  155.         */
  156.         
  157.         printk("major=[%d]\n", major);
  158.         /*自动创建设备节点:即在/dev 目录下,自动创建字符设备文件*/
  159.         /*注意: 2.6.13内核是用的class_device_create(); 2.6.30内核是用的device_create();*/
  160.         at24c02_class = class_create(THIS_MODULE, "at24c02");
  161.         at24c02_device = device_create(at24c02_class, NULL, MKDEV(major, 0), NULL, "at24c02");
  162.         
  163.         return 0;
  164. }

  165. /*
  166. *函数功能 : driver层,尝试连接I2C总线驱动程序的适配器adpater
  167. */
  168. static int at24c02_attach_driver(struct i2c_adapter *adapter)
  169. {
  170.         /*
  171.         *函数功能 : 如果发现 addr_data 描述的设备存在,则调用 at24c02_detect 函数。
  172.         */
  173.         return i2c_probe(adapter, &addr_data, at24c02_detect_devices);
  174. }

  175. /*
  176. *函数功能 : driver层,连上I2C总线驱动程序适配器adpater的client设备,这里断开来。
  177. */
  178. static int at24c02_detach_client(struct i2c_client *client)
  179. {
  180.         printk("at24c02_detach_client\n");
  181.         device_destroy(at24c02_class, MKDEV(major, 0));
  182.         class_destroy(at24c02_class);
  183.         unregister_chrdev(major, "at24c02");

  184.         i2c_detach_client(client);
  185.         kfree(i2c_get_clientdata(client));

  186.         return 0;
  187. }

  188. /*1,分配一个 i2c_driver 结构体 : 是核心结构体,我们这里就不分配了,定义的时候直接填值*/
  189. /*2,设置 i2c_driver 结构体
  190. *attach : 附上
  191. *detach : 分离
  192. */
  193. static struct i2c_driver at24c02_driver =
  194. {
  195.         .driver =
  196.         {
  197.                 .name    = "at24c02",
  198.         },
  199.         .attach_adapter = at24c02_attach_driver, // 它直接调用 i2c_probe(adap, 设备地址, 发现这个设备后要调用的函数);
  200.         .detach_client = at24c02_detach_client, // 卸载这个驱动后,如果之前发现能够支持的设备,则调用它来清理
  201. };

  202. static int __init at24c02_init(void)
  203. {
  204.         /*3,注册一个 i2c_driver 结构体
  205.         *会自动调用at24c02_driver结构里面的成员函数 attach_adapter=at24c02_attach。
  206.         */
  207.         i2c_add_driver(&at24c02_driver);
  208.         return 0;
  209. }

  210. static void __exit at24c02_exit(void)
  211. {
  212.         /*注销 i2c_driver 结构体
  213.         *会自动调用at24c02_driver结构里面的成员函数 detach_client=at24c02_detach
  214.         */
  215.         i2c_del_driver(&at24c02_driver);
  216. }

  217. module_init(at24c02_init);
  218. module_exit(at24c02_exit);
  219. MODULE_AUTHOR("WangXiancai");
  220. MODULE_LICENSE("GPL");

  221. /*
  222. 测试1th:
  223. 1. # insmod iic.ko
  224. i2c-adapter i2c-0: Invalid probe address 0xa0 //无效的地址0xa0,这里地址只有前面7位,所以改成0x50了。
  225.    观察输出信息
  226. 修改:normal_addr里的0x50为0x60
  227.    编译加载,观察输出信息
  228. 2. # insmod iic.ko
  229. s3c2440-i2c s3c2440-i2c: cannot get bus (error -110)
  230. s3c2440-i2c s3c2440-i2c: cannot get bus (error -110)
  231. at24c02_detect

  232. 错误信息在这里:
  233. drivers/i2c/busses/i2c-s3c2410.c#L502
  234. 上面一行调用了s3c24xx_i2c_set_master,这个函数在这:
  235. drivers/i2c/busses/i2c-s3c2410.c#L468
  236. 何以看到S3C2410_IICSTAT这个寄存器的S3C2410_IICSTAT[5]这一位总是显示busy,所以就失败了。

  237. 我没用过s3c2440,你自己看看硬件手册,什么原因会导致S3C2410_IICSTAT仅存其显示一直忙碌,
  238. 是因为没有正确初始化还是其它软件在占用?
  239. 这里找到原因如下 : ov9650一直占用着。解决方法如下 :
  240. 文件\nfs_2.6.30\etc\init.d\rcS 中屏蔽掉下面这行就OK了 :
  241. ### insmod /lib/ov9650.ko //注释掉就行了

  242. 3.# rmmod iic.ko //卸载这个驱动函数
  243. 断开函数 at24c02_detach_client 里面的打印语句printk("at24c02_detach\n"); 不会被调用,为什么?
  244. 探测函数 at24c02_detect_devices 这一句加上之后 i2c_attach_client(new_client); 就调用上面的printk语句了。
  245. 也就是说,总线必须连上 具体的client设备,才会在卸载的时候,调用断开client设备的函数。

  246. 4,加上 class_create device_create 之后,可自动创建设备节点。
  247. # insmod iic.ko
  248. at24c02_detect_devices
  249. major=[251]
  250. # ls -lrt /dev/at24c*
  251. crw-rw---- 1 root root 252, 0 Jan 1 01:58 /dev/at24c02
  252. 卸载之后,设备节点就会去掉了。

  253. 5,加入读写函数之后,再测如下:
  254. 还要创建2个设备文件:他们主设备号相同,次设备号不同
  255. # mknod /dev/at24c02 c 252 0


  256. */
makefile文件

点击(此处)折叠或打开

  1. # File: Makefile
  2. # wangxiancai

  3. MODEXT = ko
  4. # INSTALLDIR=/home/wangxc/linux/rootfs/nfs_2.6.13/wxc/driver/chardriver/ko
  5. INSTALLDIR=/home/wangxc/linux/rootfs/nfs_2.6.30/wxc/driver/chardriver/ko
  6. # CROSS=/home/wangxc/linux/toolchain/crosstools_3.4.1_softfloat/arm-linux/gcc-3.4.1-glibc-2.3.3/bin/arm-linux-
  7. CROSS=/home/wangxc/linux/toolchain/crosstools_4.4.3_softfloat/bin/arm-linux-
  8. # KERNELDIR=/home/wangxc/linux/kernel/kernel-2.6.13
  9. KERNELDIR=/home/wangxc/linux/kernel/kerner-2.6.30

  10. CC= $(CROSS)gcc
  11. LD= $(CROSS)ld

  12. #############################################################################
  13. # Compiler Flags
  14. #############################################################################
  15. EXTRA_CFLAGS += -I$(KERNELDIR)/include
  16. #############################################################################
  17. # Make Targets
  18. #############################################################################
  19. obj-m := iic.o
  20. default:
  21.     $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
  22. # Otherwise we were called directly from the command line; invoke the kernel build system.

  23. install: default
  24.     cp -rf $(PWD)/iic.$(MODEXT) $(INSTALLDIR)

  25. clean:
  26.     rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions *.bak modules.order Module.symvers
应用代码如下:
iictest.c文件

点击(此处)折叠或打开

  1. #include <stdio.h>
  2. #include <unistd.h>
  3. #include <sys/types.h>
  4. #include <fcntl.h>
  5. #include <stdlib.h>
  6. #include <errno.h>

  7. int main(int argc, char * * argv)
  8. {
  9.         int fd = 0;
  10.         unsigned char buf[2];
  11.         int ret = -1;
  12.         
  13.         if((argc != 3) && (argc != 4))
  14.         {
  15.                 printf("%s r|R addr\n", argv[0]);
  16.                 printf("%s w|W addr val\n", argv[0]);
  17.                 return -1;
  18.         }
  19.         
  20.         fd = open("/dev/at24c02", O_RDWR);
  21.         if(fd < 0)
  22.         {
  23.                 perror("open /dev/at24c02");
  24.                 exit(-1);
  25.         }
  26.         
  27.         if(strcmp(argv[1], "r") == 0)
  28.         {
  29.                 buf[0] = strtoul(argv[2], NULL, 0); /*将字符串转换成无符号长整型数*/
  30.                 printf("read address = [%d]\n", buf[0]);
  31.                 ret = read(fd, buf, 1); /*注意 : read调用时,buf[0]表示读取的地址,read返回时,buf[0]存储读取的值*/
  32.                 if(ret == 1) /*表示成功读取1个字节*/
  33.                 {
  34.                         printf("data = [%d]\n", buf[0]);
  35.                 }
  36.                 else
  37.                 {
  38.                         printf("write err ret = [%d]\n", ret);
  39.                 }
  40.         }
  41.         else if(strcmp(argv[1], "w") == 0)
  42.         {
  43.                 buf[0] = strtoul(argv[2], NULL, 0);
  44.                 buf[1] = strtoul(argv[3], NULL, 0);
  45.                 /*printf("APP : buf[0] = [%d]\n", buf[0]);*/
  46.                 /*printf("APP : buf[1] = [%d]\n", buf[1]);*/
  47.                 ret = write(fd, buf, 2);
  48.                 if(ret == 2) /*表示成功写入2个字节*/
  49.                 {
  50.                         printf("write address [%d] with data [%d]\n", buf[0], buf[1]);
  51.                 }
  52.                 else
  53.                 {
  54.                         printf("write err ret = [%d]\n", ret);
  55.                 }
  56.         }
  57.         else
  58.         {
  59.                 printf("%s r|R addr\n", argv[0]);
  60.                 printf("%s w|W addr val\n", argv[0]);
  61.                 return -1;
  62.         }
  63.         
  64.         close(fd);

  65.         return 0;
  66. }

  67. /*
  68. 测试 :
  69. 1, 刚才开始的测试的时候,由于open函数使用的是 RDONLY 只读打开,驱动函数 write不能调用,
  70. 使用 O_RDWR 可读可写方式打开,才能正确
  71. 2,
  72. # ./iictest
  73. ./iictest r|R addr
  74. ./iictest w|W addr val
  75. # ./iictest w 100 56 //向100地址中写入数据56
  76. write address [100] with data [56]
  77. # ./iictest r 100 //从100地址中读取数据
  78. read address = [100]
  79. data = [56]
  80. */
makefile文件

点击(此处)折叠或打开

  1. # CROSS=/home/wangxc/linux/toolchain/crosstools_3.4.1_softfloat/arm-linux/gcc-3.4.1-glibc-2.3.3/bin/arm-linux-gcc
  2. CROSS=/home/wangxc/linux/toolchain/crosstools_4.4.3_softfloat/bin/arm-linux-gcc
  3. all:iictest
  4. iictest:iictest.c
  5.     $(CROSS) -o iictest iictest.c
  6. clean:
  7.     @rm -vf iictest.o iictest

阅读(1007) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~