Chinaunix首页 | 论坛 | 博客
  • 博客访问: 495168
  • 博文数量: 63
  • 博客积分: 1187
  • 博客等级: 少尉
  • 技术积分: 706
  • 用 户 组: 普通用户
  • 注册时间: 2010-03-05 16:53
个人简介

Must Be

文章分类

全部博文(63)

文章存档

2019年(1)

2017年(4)

2016年(6)

2015年(2)

2014年(1)

2013年(3)

2012年(10)

2011年(36)

我的朋友

分类: LINUX

2011-03-09 16:02:56

在嵌入式中,I2C接口的意义非常重大,许多外围芯片控制接口都采用I2C。因此,了解在驱动模块中如何进行I2C总线通信是很有必要的。我们先看看I2C驱动代码的树形结构:
.
|-- Kconfig
|-- Makefile
|-- algos
|   |-- Kconfig
|   |-- Makefile
|   |-- i2c-algo-bit.c
|   |-- i2c-algo-pca.c
|   |-- i2c-algo-pcf.c
|   `-- i2c-algo-pcf.h
|-- busses
|   |-- Kconfig
|   |-- Makefile
|   |-- i2c-acorn.c
|   |-- ...
|   |-- i2c-gpio.c
|   |-- ...
|   |-- i2c-pxa.c
|   |-- i2c-s3c2410.c
|   `-- ...
|-- chips
|   |-- Kconfig
|   |-- Makefile
|   |-- ds1682.c
|   |-- pca963x.c
|   `-- tsl2550.c
|-- i2c-boardinfo.c
|-- i2c-core.c
|-- i2c-core.h
`-- i2c-dev.c
3 directories, 86 files
其中i2c-core.c是I2C核心部分,提供接口函数,i2c_adapter和i2c_driver在模块初始化时分别调用i2c_add_numbered_adapter()和i2c_add_driver()将其注册到I2C Core中。
i2c-dev.c是I2C设备部分,实现字符设备访问接口,对硬件的具体访问是通过i2c_adapter来实现的。它在初始化时,需要向I2C Core注册一个i2c_driver。我认为这部分仅仅是让用户层访问I2C总线的,如果内核态访问I2C总线的话,则没有必要理会它;但是我们需要参考它来实现我们自己的i2c_driver,并且需要将这个i2c_driver注册到I2C Core。
busses目录下有许多个文件,为各种平台的I2C总线驱动,如s2c2410、pxa、mpc等等,跟硬件spec息息相关。一个总线驱动需要两个模块,用结构体i2c_adapter和i2c_algorithm来描述。
algos目录是对bus中的i2c_algorithm的补充,特别的i2c_algorithm可在这里实现。chips是I2C Slave芯片驱动代码,如I2C控制接口的eeprom、LED段码显示芯片等。
更详细的有关I2C驱动架构介绍的文章见:http://blog168.chinaunix.net/space.php?uid=11134731&do=blog&id=33193
下面是正题,附上示范代码,碰到相关场合,直接将其复制过去使用即可。
然后在模块初始化的时候调用azure_add_i2c_device()即将一个i2c_driver注册到I2C Core,之后便可调用i2c_master_recv()和i2c_master_send()进行I2C总线读写操作。i2c_master_send()/i2c_master_recv()第一个参数client由azure_i2c传入。
  1. #define AZURE_I2C_ADDR 0x1a
  2. #define AZURE_I2C_BUS_NO 0
  3. static struct i2c_client *azure_i2c;
  4. static int azure_i2c_probe(struct i2c_client *i2c,
  5.  const struct i2c_device_id *id)
  6. {
  7.  azure_i2c = i2c;
  8.  i2c->addr = AZURE_I2C_ADDR;

  9.  return 0;
  10. }
  11. static int azure_i2c_remove(struct i2c_client *client)
  12. {
  13.  return 0;
  14. }
  15. static const struct i2c_device_id azure_i2c_id[] = {
  16.  { "azure", 0 },
  17.  { }
  18. };
  19. MODULE_DEVICE_TABLE(i2c, azure_i2c_id);
  20. static struct i2c_driver azure_i2c_driver = {
  21.  .driver = {
  22.   .name = "AZURE I2C",
  23.   .owner = THIS_MODULE,
  24.  },
  25.  .probe = azure_i2c_probe,
  26.  .remove = azure_i2c_remove,
  27.  .id_table = azure_i2c_id,
  28. };
  29. static int azure_add_i2c_device()
  30. {
  31.  struct i2c_board_info info;
  32.  struct i2c_adapter *adapter;
  33.  struct i2c_client *client;
  34.  int ret;
  35.  memset(&info, 0, sizeof(struct i2c_board_info));
  36.  info.addr = AZURE_I2C_ADDR;
  37.  strlcpy(info.type, "azure", I2C_NAME_SIZE);

  38.  adapter = i2c_get_adapter(AZURE_I2C_BUS_NO);
  39.  if (!adapter) {
  40.   printk(KERN_ERR "can't get i2c adapter %d\n", AZURE_I2C_BUS_NO);
  41.   goto err_driver;
  42.  }

  43.  client = i2c_new_device(adapter, &info);
  44.  i2c_put_adapter(adapter);

  45.  if (!client) {
  46.   printk(KERN_ERR "can't add i2c device at 0x%x\n", (unsigned int)info.addr);
  47.   goto err_driver;
  48.  }

  49.  ret = i2c_add_driver(&azure_i2c_driver);
  50.  if (ret != 0) {
  51.   printk(KERN_ERR "can't add i2c driver\n");
  52.   return ret;
  53.  }

  54.  return 0;

  55. err_driver:
  56.  i2c_del_driver(&azure_i2c_driver);
  57.  return -ENODEV;
  58. }
--------------------------------------------------------------------------------

以上并不是聪明的做法。如果有N个模块要用到I2C,那么在N个模块就要添加基本一样的代码。更好的解决方法是为内核调用I2C写一个独立的模块,EXPORT_SYMBOL读写接口函数,在其他模块里就只需要调用这些读写接口函数就可以了。如下是模块代码:
  1. #include <linux/kernel.h>
  2. #include <linux/module.h>
  3. #include <linux/init.h>
  4. #include <linux/errno.h>
  5. #include <linux/init.h>
  6. #include <linux/list.h>
  7. #include <linux/i2c.h>
  8. #include <linux/smp_lock.h>
  9. #define I2C_API_FAKE_ADDR 0x7f
  10. #define I2C_MINORS 256
  11. int i2c_api_attach(struct i2c_adapter *adapter);
  12. int i2c_api_detach(struct i2c_adapter *adapter);
  13. struct i2c_api {
  14.  struct list_head list;
  15.  struct i2c_client *client;
  16. };
  17. static LIST_HEAD(i2c_api_list);
  18. static DEFINE_SPINLOCK(i2c_api_list_lock);
  19. static const unsigned short normal_addr[] = { I2C_API_FAKE_ADDR, I2C_CLIENT_END };
  20. static const unsigned short ignore[] = { I2C_CLIENT_END };
  21. static struct i2c_client_address_data addr_data =
  22. {
  23.  .normal_i2c = normal_addr,
  24.  .probe = ignore,
  25.  .ignore = ignore,
  26.  .forces = NULL,
  27. };
  28. static const struct i2c_device_id id[] = {
  29.  {"I2C-API", 0},
  30.  {}
  31. };
  32. MODULE_DEVICE_TABLE(i2c, id);
  33. static struct i2c_driver i2c_api_driver = {
  34.  .id_table = id,
  35.  .attach_adapter = i2c_api_attach,
  36.  .detach_adapter = i2c_api_detach,
  37.   .command = NULL,
  38.  .driver = {
  39.   .name = "I2C-API",
  40.   .owner = THIS_MODULE,
  41.  },
  42.  .address_data = &addr_data,
  43. };
  44. static struct i2c_api *get_i2c_api(int bus_id)
  45. {
  46.  struct i2c_api *i2c_api;
  47.  spin_lock(&i2c_api_list_lock);
  48.  list_for_each_entry(i2c_api, &i2c_api_list, list) {
  49.   if (i2c_api->client->adapter->nr == bus_id)
  50.    goto found;
  51.  }
  52.  i2c_api = NULL;

  53. found:
  54.  spin_unlock(&i2c_api_list_lock);
  55.  return i2c_api;
  56. }
  57. static struct i2c_api *add_i2c_api(struct i2c_client *client)
  58. {
  59.  struct i2c_api *i2c_api;
  60.  if (client->adapter->nr >= I2C_MINORS) {
  61.   printk(KERN_ERR "i2c_api: Out of device minors (%d)\n",
  62.    client->adapter->nr);
  63.   return NULL;
  64.  }
  65.  i2c_api = kzalloc(sizeof(*i2c_api), GFP_KERNEL);
  66.  if (!i2c_api)
  67.   return NULL;
  68.  i2c_api->client = client;
  69.  spin_lock(&i2c_api_list_lock);
  70.  list_add_tail(&i2c_api->list, &i2c_api_list);
  71.  spin_unlock(&i2c_api_list_lock);
  72.  return i2c_api;
  73. }
  74. static void del_i2c_api(struct i2c_api *i2c_api)
  75. {
  76.  spin_lock(&i2c_api_list_lock);
  77.  list_del(&i2c_api->list);
  78.  spin_unlock(&i2c_api_list_lock);
  79.  kfree(i2c_api);
  80. }
  81. static int i2c_api_do_xfer(int bus_id, char chip_addr, char sub_addr, int mode,
  82.  char *buf, unsigned int size)
  83. {
  84. /**
  85.  * you can define more transfer mode here, implement it below.
  86.  */
  87. #define I2C_API_XFER_MODE_SEND 0x0 /* standard send */
  88. #define I2C_API_XFER_MODE_RECV 0x1 /* standard receive */
  89. #define I2C_API_XFER_MODE_SEND_NO_SUBADDR 0x2 /* send with no sub address */
  90. #define I2C_API_XFER_MODE_RECV_NO_SUBADDR 0x3 /* receive with no sub address */
  91.  int ret = 0;
  92.  char *tmp;
  93.  struct i2c_api *i2c_api = get_i2c_api(bus_id);
  94.  if (!i2c_api)
  95.   return -ENODEV;

  96.  i2c_api->client->addr = chip_addr;
  97.  switch (mode) {
  98.  case I2C_API_XFER_MODE_SEND:
  99.   tmp = kmalloc(size + 1,GFP_KERNEL);
  100.   if (tmp == NULL)
  101.    return -ENOMEM;
  102.   tmp[0] = sub_addr;
  103.   memcpy(&tmp[1], buf, size);
  104.   ret = i2c_master_send(i2c_api->client, tmp, size + 1);
  105.   ret = (ret == size + 1) ? size : ret;
  106.   break;
  107.   
  108.  case I2C_API_XFER_MODE_RECV:
  109.   ret = i2c_master_send(i2c_api->client, &sub_addr, 1);
  110.   if (ret < 0)
  111.    return ret;
  112.   ret = i2c_master_recv(i2c_api->client, buf, size);
  113.   break;

  114.  case I2C_API_XFER_MODE_SEND_NO_SUBADDR:
  115.   ret = i2c_master_send(i2c_api->client, buf, size);
  116.   break;
  117.   
  118.  case I2C_API_XFER_MODE_RECV_NO_SUBADDR:
  119.   ret = i2c_master_recv(i2c_api->client, buf, size);
  120.   break;
  121.    
  122.  default:
  123.   return -EINVAL;
  124.  }
  125.  return ret;
  126. }
  127. int i2c_api_do_send(int bus_id, char chip_addr, char sub_addr, char *buf, unsigned int size)
  128. {
  129.  return i2c_api_do_xfer(bus_id, chip_addr, sub_addr, I2C_API_XFER_MODE_SEND, buf, size);
  130. }
  131. int i2c_api_do_recv(int bus_id, char chip_addr, char sub_addr, char *buf, unsigned int size)
  132. {
  133.  return i2c_api_do_xfer(bus_id, chip_addr, sub_addr, I2C_API_XFER_MODE_RECV, buf, size);
  134. }
  135. int i2c_api_attach(struct i2c_adapter *adap)
  136. {
  137.  struct i2c_board_info info;
  138.  struct i2c_client *client;

  139.  memset(&info, 0, sizeof(struct i2c_board_info));
  140.   strlcpy(info.type, "i2c_api", I2C_NAME_SIZE);
  141.  info.addr = I2C_API_FAKE_ADDR;
  142.  client = i2c_new_device(adap, &info);
  143.  if (client)
  144.   add_i2c_api(client);
  145.  printk(KERN_INFO "i2c_api_attach adap[%d]\n", adap->nr);
  146.  return 0;
  147. }
  148. int i2c_api_detach(struct i2c_adapter *adap)
  149. {
  150.  struct i2c_api *i2c_api;
  151.  i2c_api = get_i2c_api(adap->nr);
  152.  if (i2c_api)
  153.   del_i2c_api(i2c_api);
  154.  return 0;
  155. }
  156. static int __init i2c_api_init(void)
  157. {
  158.  int ret = i2c_add_driver(&i2c_api_driver);

  159.  if (ret) {
  160.   printk(KERN_ERR "[%s] Driver registration failed, module not inserted.\n", __func__);
  161.   return ret;
  162.  }
  163.  return 0 ;
  164. }
  165. static void __exit i2c_api_exit(void)
  166. {
  167.  i2c_del_driver(&i2c_api_driver);
  168. }
  169. MODULE_AUTHOR("Loon, ");
  170. MODULE_DESCRIPTION("I2C i2c_api Driver");
  171. MODULE_LICENSE("GPL");
  172. module_init(i2c_api_init);
  173. module_exit(i2c_api_exit);
  174. EXPORT_SYMBOL_GPL(i2c_api_do_send);
  175. EXPORT_SYMBOL_GPL(i2c_api_do_recv);
内核版本是2.6.32,i2c_driver结构体成员attach_adapter和detach_adapter可能以后有增删,届时需要对模块里的i2c_api_attach()和i2c_api_detach()做调整。
加载该模块后,其他模块即可调用i2c_api_do_recv()/i2c_api_do_send()读写I2C总线。

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/sepnic/archive/2011/02/11/6178657.aspx
阅读(4242) | 评论(0) | 转发(0) |
0

上一篇:编译libcurl

下一篇:gcc编译cpp文件的方法

给主人留下些什么吧!~~