驱动模块使用I2C总线范例

原文http://blog.csdn.net/azloong/article/details/6178657

源码是在linux2.6下写的,为了移值到更高的linux版本,进行了一定的修改。

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/list.h>
#include <linux/i2c.h>
#include <linux/hardirq.h>
#include <linux/slab.h>  
 
#define I2C_API_FAKE_ADDR   0x7f
#define I2C_MINORS          256
//#define i2c_version_old     1  
 
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 };
#ifdef i2c_version_old
static struct i2c_client_address_data addr_data =  
{
    .normal_i2c = normal_addr,
    .probe      = ignore,
    .ignore     = ignore,
    .forces     = NULL,
};
#endif
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,
 
//#if LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0)    
#if 0
    .detach_adapter    = i2c_api_detach,
#endif    
    .command        = NULL,
    .driver         = {
						.name  = "I2C-API",
						.owner = THIS_MODULE,
    },
#ifdef i2c_version_old    
    .address_data   = &addr_data,
#endif
 
};
 
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 could define more transfer mode here, implement it following. */
#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 without sub-address */
#define I2C_API_XFER_MODE_RECV_NO_SUBADDR 0x3 /* receive without 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, <[email protected]>");
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);


你可能感兴趣的:(驱动模块使用I2C总线范例)