蕉橘r18change world:驱动模块使用I2C总线范例 - AZURE - CSDN博客

来源:百度文库 编辑:九乡新闻网 时间:2024/05/06 07:55:47

驱动模块使用I2C总线范例 收藏
在嵌入式中,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

下面是正题,附上示范代码,碰到相关场合,直接将其复制过去使用即可。

view plaincopy to clipboardprint?
#define AZURE_I2C_ADDR   0x1a  
#define AZURE_I2C_BUS_NO 0  
 
static struct i2c_client *azure_i2c;  
 
static int azure_i2c_probe(struct i2c_client *i2c,  
    const struct i2c_device_id *id)  
{  
    azure_i2c = i2c;  
    i2c->addr = AZURE_I2C_ADDR;  
      
    return 0;  
}  
 
static int azure_i2c_remove(struct i2c_client *client)  
{  
    return 0;  
}  
 
static const struct i2c_device_id azure_i2c_id[] = {  
    { "azure", 0 },  
    { }  
};  
MODULE_DEVICE_TABLE(i2c, azure_i2c_id);  
 
static struct i2c_driver azure_i2c_driver = {  
    .driver  = {  
        .name  = "AZURE I2C",  
        .owner = THIS_MODULE,  
    },  
    .probe    = azure_i2c_probe,  
    .remove   = azure_i2c_remove,  
    .id_table = azure_i2c_id,  
};  
 
static int azure_add_i2c_device()  
{  
    struct i2c_board_info info;  
    struct i2c_adapter *adapter;  
    struct i2c_client *client;  
    int ret;  
 
    memset(&info, 0, sizeof(struct i2c_board_info));  
    info.addr = AZURE_I2C_ADDR;  
    strlcpy(info.type, "azure", I2C_NAME_SIZE);  
      
    adapter = i2c_get_adapter(AZURE_I2C_BUS_NO);  
    if (!adapter) {  
        printk(KERN_ERR "can't get i2c adapter %d\n", AZURE_I2C_BUS_NO);  
        goto err_driver;  
    }  
      
    client = i2c_new_device(adapter, &info);  
    i2c_put_adapter(adapter);  
      
    if (!client) {  
        printk(KERN_ERR "can't add i2c device at 0x%x\n", (unsigned int)info.addr);  
        goto err_driver;  
    }  
      
    ret = i2c_add_driver(&azure_i2c_driver);  
    if (ret != 0) {  
        printk(KERN_ERR "can't add i2c driver\n");  
        return ret;  
    }  
      
    return 0;  
      
err_driver:  
    i2c_del_driver(&azure_i2c_driver);  
    return -ENODEV;  

#define AZURE_I2C_ADDR   0x1a
#define AZURE_I2C_BUS_NO 0

static struct i2c_client *azure_i2c;

static int azure_i2c_probe(struct i2c_client *i2c,
 const struct i2c_device_id *id)
{
 azure_i2c = i2c;
 i2c->addr = AZURE_I2C_ADDR;
 
 return 0;
}

static int azure_i2c_remove(struct i2c_client *client)
{
 return 0;
}

static const struct i2c_device_id azure_i2c_id[] = {
 { "azure", 0 },
 { }
};
MODULE_DEVICE_TABLE(i2c, azure_i2c_id);

static struct i2c_driver azure_i2c_driver = {
 .driver  = {
  .name  = "AZURE I2C",
  .owner = THIS_MODULE,
 },
 .probe    = azure_i2c_probe,
 .remove   = azure_i2c_remove,
 .id_table = azure_i2c_id,
};

static int azure_add_i2c_device()
{
 struct i2c_board_info info;
 struct i2c_adapter *adapter;
 struct i2c_client *client;
 int ret;

 memset(&info, 0, sizeof(struct i2c_board_info));
 info.addr = AZURE_I2C_ADDR;
 strlcpy(info.type, "azure", I2C_NAME_SIZE);
 
 adapter = i2c_get_adapter(AZURE_I2C_BUS_NO);
 if (!adapter) {
  printk(KERN_ERR "can't get i2c adapter %d\n", AZURE_I2C_BUS_NO);
  goto err_driver;
 }
 
 client = i2c_new_device(adapter, &info);
 i2c_put_adapter(adapter);
 
 if (!client) {
  printk(KERN_ERR "can't add i2c device at 0x%x\n", (unsigned int)info.addr);
  goto err_driver;
 }
 
 ret = i2c_add_driver(&azure_i2c_driver);
 if (ret != 0) {
  printk(KERN_ERR "can't add i2c driver\n");
  return ret;
 }
 
 return 0;
 
err_driver:
 i2c_del_driver(&azure_i2c_driver);
 return -ENODEV;
}

然后在模块初始化的时候调用azure_add_i2c_device()即将一个i2c_driver注册到I2C Core,之后便可调用i2c_master_recv()和i2c_master_send()进行I2C总线读写操作。i2c_master_send()/i2c_master_recv()第一个参数client由azure_i2c传入。

 

--------------------------------------------------------------------------------


以上并不是聪明的做法。如果有N个模块要用到I2C,那么在N个模块就要添加基本一样的代码。更好的解决方法是为内核调用I2C写一个独立的模块,EXPORT_SYMBOL读写接口函数,在其他模块里就只需要调用这些读写接口函数就可以了。如下是模块代码:

view plaincopy to clipboardprint?
#include   
#include   
#include   
#include   
#include   
#include   
#include   
#include   
 
#define I2C_API_FAKE_ADDR 0x7f  
#define I2C_MINORS        256  
 
int  i2c_api_attach(struct i2c_adapter *adapter);  
int  i2c_api_detach(struct i2c_adapter *adapter);  
 
struct i2c_api {  
    struct list_head list;  
    struct i2c_client *client;  
};  
 
static LIST_HEAD(i2c_api_list);  
static DEFINE_SPINLOCK(i2c_api_list_lock);  
 
static const unsigned short normal_addr[] = { I2C_API_FAKE_ADDR, I2C_CLIENT_END };  
static const unsigned short ignore[]      = { I2C_CLIENT_END };  
static struct i2c_client_address_data addr_data =   
{  
    .normal_i2c = normal_addr,  
    .probe      = ignore,  
    .ignore     = ignore,  
    .forces     = NULL,  
};  
 
static const struct i2c_device_id id[] = {  
    {"I2C-API", 0},  
    {}   
};        
MODULE_DEVICE_TABLE(i2c, id);  
 
static struct i2c_driver i2c_api_driver = {  
    .id_table       = id,  
    .attach_adapter = i2c_api_attach,  
    .detach_adapter = i2c_api_detach,  
    .command        = NULL,  
    .driver         = {  
        .name  = "I2C-API",  
        .owner = THIS_MODULE,  
    },  
    .address_data   = &addr_data,  
};  
 
static struct i2c_api *get_i2c_api(int bus_id)  
{  
    struct i2c_api *i2c_api;  
 
    spin_lock(&i2c_api_list_lock);  
    list_for_each_entry(i2c_api, &i2c_api_list, list) {  
        if (i2c_api->client->adapter->nr == bus_id)  
            goto found;  
    }  
    i2c_api = NULL;  
      
found:  
    spin_unlock(&i2c_api_list_lock);  
    return i2c_api;  
}  
 
static struct i2c_api *add_i2c_api(struct i2c_client *client)  
{  
    struct i2c_api *i2c_api;  
 
    if (client->adapter->nr >= I2C_MINORS) {  
        printk(KERN_ERR "i2c_api: Out of device minors (%d)\n",  
            client->adapter->nr);  
        return NULL;  
    }  
 
    i2c_api = kzalloc(sizeof(*i2c_api), GFP_KERNEL);  
    if (!i2c_api)  
        return NULL;  
    i2c_api->client = client;  
 
    spin_lock(&i2c_api_list_lock);  
    list_add_tail(&i2c_api->list, &i2c_api_list);  
    spin_unlock(&i2c_api_list_lock);  
    return i2c_api;  
}  
 
static void del_i2c_api(struct i2c_api *i2c_api)  
{  
    spin_lock(&i2c_api_list_lock);  
    list_del(&i2c_api->list);  
    spin_unlock(&i2c_api_list_lock);  
    kfree(i2c_api);  
}  
 
static int i2c_api_do_xfer(int bus_id, char chip_addr, char sub_addr, int mode,   
    char *buf, unsigned int size)  
{  
/** 
 * you can define more transfer mode here, implement it below. 
 */ 
#define I2C_API_XFER_MODE_SEND            0x0 /* standard send */  
#define I2C_API_XFER_MODE_RECV            0x1 /* standard receive */  
#define I2C_API_XFER_MODE_SEND_NO_SUBADDR 0x2 /* send with no sub address */  
#define I2C_API_XFER_MODE_RECV_NO_SUBADDR 0x3 /* receive with no sub address */  
 
    int ret = 0;  
    char *tmp;  
    struct i2c_api *i2c_api = get_i2c_api(bus_id);  
 
    if (!i2c_api)  
        return -ENODEV;  
      
    i2c_api->client->addr = chip_addr;  
    switch (mode) {  
    case I2C_API_XFER_MODE_SEND:  
        tmp = kmalloc(size + 1,GFP_KERNEL);  
        if (tmp == NULL)  
            return -ENOMEM;  
        tmp[0] = sub_addr;  
        memcpy(&tmp[1], buf, size);  
        ret = i2c_master_send(i2c_api->client, tmp, size + 1);  
        ret = (ret == size + 1) ? size : ret;  
        break;  
          
    case I2C_API_XFER_MODE_RECV:  
        ret = i2c_master_send(i2c_api->client, &sub_addr, 1);  
        if (ret < 0)  
            return ret;  
        ret = i2c_master_recv(i2c_api->client, buf, size);  
        break;  
      
    case I2C_API_XFER_MODE_SEND_NO_SUBADDR:  
        ret = i2c_master_send(i2c_api->client, buf, size);  
        break;  
          
    case I2C_API_XFER_MODE_RECV_NO_SUBADDR:  
        ret = i2c_master_recv(i2c_api->client, buf, size);  
        break;  
              
    default:  
        return -EINVAL;  
    }  
    return ret;  
}  
 
int i2c_api_do_send(int bus_id, char chip_addr, char sub_addr, char *buf, unsigned int size)  
{  
    return i2c_api_do_xfer(bus_id, chip_addr, sub_addr, I2C_API_XFER_MODE_SEND, buf, size);  
}  
 
int i2c_api_do_recv(int bus_id, char chip_addr, char sub_addr, char *buf, unsigned int size)  
{  
    return i2c_api_do_xfer(bus_id, chip_addr, sub_addr, I2C_API_XFER_MODE_RECV, buf, size);  
}  
 
int i2c_api_attach(struct i2c_adapter *adap)  
{  
    struct i2c_board_info info;  
    struct i2c_client *client;  
      
    memset(&info, 0, sizeof(struct i2c_board_info));  
        strlcpy(info.type, "i2c_api", I2C_NAME_SIZE);  
    info.addr = I2C_API_FAKE_ADDR;  
    client = i2c_new_device(adap, &info);  
    if (client)  
        add_i2c_api(client);  
    printk(KERN_INFO "i2c_api_attach adap[%d]\n", adap->nr);  
    return 0;  
}  
 
int i2c_api_detach(struct i2c_adapter *adap)  
{     
    struct i2c_api *i2c_api;  
 
    i2c_api = get_i2c_api(adap->nr);  
    if (i2c_api)  
        del_i2c_api(i2c_api);  
    return 0;  
}  
 
static int __init i2c_api_init(void)  
{     
    int ret = i2c_add_driver(&i2c_api_driver);  
      
    if (ret) {  
        printk(KERN_ERR "[%s] Driver registration failed, module not inserted.\n", __func__);  
        return ret;  
    }  
 
    return 0 ;    
}  
 
static void __exit i2c_api_exit(void)  
{     
    i2c_del_driver(&i2c_api_driver);  
}  
 
MODULE_AUTHOR("Loon, ");  
MODULE_DESCRIPTION("I2C i2c_api Driver");  
MODULE_LICENSE("GPL");  
 
module_init(i2c_api_init);  
module_exit(i2c_api_exit);  
 
EXPORT_SYMBOL_GPL(i2c_api_do_send);  
EXPORT_SYMBOL_GPL(i2c_api_do_recv); 
#include
#include
#include
#include
#include
#include
#include
#include

#define I2C_API_FAKE_ADDR 0x7f
#define I2C_MINORS       256

int  i2c_api_attach(struct i2c_adapter *adapter);
int  i2c_api_detach(struct i2c_adapter *adapter);

struct i2c_api {
 struct list_head list;
 struct i2c_client *client;
};

static LIST_HEAD(i2c_api_list);
static DEFINE_SPINLOCK(i2c_api_list_lock);

static const unsigned short normal_addr[] = { I2C_API_FAKE_ADDR, I2C_CLIENT_END };
static const unsigned short ignore[]      = { I2C_CLIENT_END };
static struct i2c_client_address_data addr_data =
{
 .normal_i2c = normal_addr,
 .probe      = ignore,
 .ignore     = ignore,
 .forces     = NULL,
};

static const struct i2c_device_id id[] = {
 {"I2C-API", 0},
 {}
};     
MODULE_DEVICE_TABLE(i2c, id);

static struct i2c_driver i2c_api_driver = {
 .id_table       = id,
 .attach_adapter = i2c_api_attach,
 .detach_adapter = i2c_api_detach,
  .command        = NULL,
 .driver         = {
  .name  = "I2C-API",
  .owner = THIS_MODULE,
 },
 .address_data   = &addr_data,
};

static struct i2c_api *get_i2c_api(int bus_id)
{
 struct i2c_api *i2c_api;

 spin_lock(&i2c_api_list_lock);
 list_for_each_entry(i2c_api, &i2c_api_list, list) {
  if (i2c_api->client->adapter->nr == bus_id)
   goto found;
 }
 i2c_api = NULL;
 
found:
 spin_unlock(&i2c_api_list_lock);
 return i2c_api;
}

static struct i2c_api *add_i2c_api(struct i2c_client *client)
{
 struct i2c_api *i2c_api;

 if (client->adapter->nr >= I2C_MINORS) {
  printk(KERN_ERR "i2c_api: Out of device minors (%d)\n",
   client->adapter->nr);
  return NULL;
 }

 i2c_api = kzalloc(sizeof(*i2c_api), GFP_KERNEL);
 if (!i2c_api)
  return NULL;
 i2c_api->client = client;

 spin_lock(&i2c_api_list_lock);
 list_add_tail(&i2c_api->list, &i2c_api_list);
 spin_unlock(&i2c_api_list_lock);
 return i2c_api;
}

static void del_i2c_api(struct i2c_api *i2c_api)
{
 spin_lock(&i2c_api_list_lock);
 list_del(&i2c_api->list);
 spin_unlock(&i2c_api_list_lock);
 kfree(i2c_api);
}

static int i2c_api_do_xfer(int bus_id, char chip_addr, char sub_addr, int mode,
 char *buf, unsigned int size)
{
/**
 * you can define more transfer mode here, implement it below.
 */
#define I2C_API_XFER_MODE_SEND            0x0 /* standard send */
#define I2C_API_XFER_MODE_RECV            0x1 /* standard receive */
#define I2C_API_XFER_MODE_SEND_NO_SUBADDR 0x2 /* send with no sub address */
#define I2C_API_XFER_MODE_RECV_NO_SUBADDR 0x3 /* receive with no sub address */

 int ret = 0;
 char *tmp;
 struct i2c_api *i2c_api = get_i2c_api(bus_id);

 if (!i2c_api)
  return -ENODEV;
 
 i2c_api->client->addr = chip_addr;
 switch (mode) {
 case I2C_API_XFER_MODE_SEND:
  tmp = kmalloc(size + 1,GFP_KERNEL);
  if (tmp == NULL)
   return -ENOMEM;
  tmp[0] = sub_addr;
  memcpy(&tmp[1], buf, size);
  ret = i2c_master_send(i2c_api->client, tmp, size + 1);
  ret = (ret == size + 1) ? size : ret;
  break;
  
 case I2C_API_XFER_MODE_RECV:
  ret = i2c_master_send(i2c_api->client, &sub_addr, 1);
  if (ret < 0)
   return ret;
  ret = i2c_master_recv(i2c_api->client, buf, size);
  break;
 
 case I2C_API_XFER_MODE_SEND_NO_SUBADDR:
  ret = i2c_master_send(i2c_api->client, buf, size);
  break;
  
 case I2C_API_XFER_MODE_RECV_NO_SUBADDR:
  ret = i2c_master_recv(i2c_api->client, buf, size);
  break;
   
 default:
  return -EINVAL;
 }
 return ret;
}

int i2c_api_do_send(int bus_id, char chip_addr, char sub_addr, char *buf, unsigned int size)
{
 return i2c_api_do_xfer(bus_id, chip_addr, sub_addr, I2C_API_XFER_MODE_SEND, buf, size);
}

int i2c_api_do_recv(int bus_id, char chip_addr, char sub_addr, char *buf, unsigned int size)
{
 return i2c_api_do_xfer(bus_id, chip_addr, sub_addr, I2C_API_XFER_MODE_RECV, buf, size);
}

int i2c_api_attach(struct i2c_adapter *adap)
{
 struct i2c_board_info info;
 struct i2c_client *client;
 
 memset(&info, 0, sizeof(struct i2c_board_info));
  strlcpy(info.type, "i2c_api", I2C_NAME_SIZE);
 info.addr = I2C_API_FAKE_ADDR;
 client = i2c_new_device(adap, &info);
 if (client)
  add_i2c_api(client);
 printk(KERN_INFO "i2c_api_attach adap[%d]\n", adap->nr);
 return 0;
}

int i2c_api_detach(struct i2c_adapter *adap)

 struct i2c_api *i2c_api;

 i2c_api = get_i2c_api(adap->nr);
 if (i2c_api)
  del_i2c_api(i2c_api);
 return 0;
}

static int __init i2c_api_init(void)

 int ret = i2c_add_driver(&i2c_api_driver);
 
 if (ret) {
  printk(KERN_ERR "[%s] Driver registration failed, module not inserted.\n", __func__);
  return ret;
 }

 return 0 ; 
}

static void __exit i2c_api_exit(void)

 i2c_del_driver(&i2c_api_driver);
}

MODULE_AUTHOR("Loon, ");
MODULE_DESCRIPTION("I2C i2c_api Driver");
MODULE_LICENSE("GPL");

module_init(i2c_api_init);
module_exit(i2c_api_exit);

EXPORT_SYMBOL_GPL(i2c_api_do_send);
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