嵌入式linux之i2c驱动

添加client

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/i2c.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/regmap.h>
// #include <linux/paltform_device.h>


static struct i2c_adapter *adap;
static struct i2c_client  *client;
#define  I2C_DEVICE_ADDR   0x68

/**指定i2c device的信息
 * downey_i2c 是device中的name元素,当这个模块被加载时,i2c总线将使用这个名称匹配相应的drv。
 * I2C_DEVICE_ADDR  为设备的i2c地址 
 * */
static struct i2c_board_info downey_board = {
    I2C_BOARD_INFO("downey_i2c",I2C_DEVICE_ADDR),
};


int dev_init(void)
{
    /*获取i2c适配器,适配器一般指板上I2C控制器,实现i2c底层协议的字节收发,特殊情况下,用普通gpio模拟I2C也可作为适配器*/
    /*参数2表示第二个适配器*/
    adap = i2c_get_adapter(2);
    if(IS_ERR(adap)){
        printk(KERN_ALERT  "I2c_get_adapter failed!!!\n");
        return -ENODEV;
    }
    /*创建一个I2C device,并注册到i2c bus的device链表中*/
    client = i2c_new_device(adap,&downey_board);
    /*使能相应适配器*/
    i2c_put_adapter(adap);
    if(!client){
    printk(KERN_ALERT  "Get new device failed!!!\n");
    return -ENODEV;
    }
    return 0;
}

void dev_exit(void)
{
    i2c_unregister_device(client);
    return ;
}

MODULE_LICENSE("GPL");
module_init(dev_init);
module_exit(dev_exit);

编写驱动:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/i2c.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/uaccess.h>

/* 结构体数组 结构体第一个参数为名称,第二个参数为private数据*/
static const struct i2c_device_id downey_drv_id_table[] = {
    {"downey_i2c",0},
    {},
};

static int major;
static struct class *i2c_test_cls;
static struct device *i2c_test_dev;
static const char* CLASS_NAME = "I2C_TEST_CLASS";
static const char* DEVICE_NAME = "I2C_TEST_DEVICE";

static struct i2c_client *downey_client;


static int i2c_test_open(struct inode *node, struct file *file)
{
    printk(KERN_ALERT "i2c init \n");
    return 0;
}

static ssize_t i2c_test_read(struct file *file,char *buf, size_t len,loff_t *offset)
{
    int cnt = 0;
    uint8_t reg = 0;
    uint8_t val = 0;
    copy_from_user(&reg,buf,1);
    /*i2c读byte,通过这个函数可以从设备中指定地址读取数据*/
    val = i2c_smbus_read_byte_data(downey_client,reg);
    cnt = copy_to_user(&buf[1],&val,1);
    /*另一种方法*/
    /*u8 buf1[4] = { 0 };
    u8 buf2[4] = { 0 };
    struct i2c_msg msgs[] = {
        {
            .addr   = client->addr, //0x38
            .flags  = 0,    //写
            .len    = 1,    //要写的数据的长度
            .buf    = buf1,
        },
        {
            .addr   = client->addr,
            .flags  = I2C_M_RD,
            .len    = 1,
            .buf    = buf2,
        },
    };
    int ret;
    buf1[0] = addr;
    ret = i2c_transfer(client->adapter, msgs, 2);
    if (ret < 0) {
        pr_err("read reg (0x%02x) error, %d\n", addr, ret);
    } else {
        *pdata = buf2[0];
    }*/

    return 1;
}

static ssize_t i2c_test_write(struct file *file,const char *buf,size_t len,loff_t *offset)
{
    uint8_t recv_msg[255] = {0};
    uint8_t reg = 0;
    int cnt = 0;
    cnt = copy_from_user(recv_msg,buf,len);
    reg = recv_msg[0];
    printk(KERN_INFO "recv data = %x.%x\n",recv_msg[0],recv_msg[1]);
    /*i2c写byte,通过这个函数可以往设备中指定地址写数据*/
    if(i2c_smbus_write_byte_data(downey_client,reg,recv_msg[1]) < 0){
        printk(KERN_ALERT  " write failed!!!\n");
        return -EIO;
    }
    /* 另一种方法  
    u8 buf[2];
    struct i2c_msg msgs[1];
    
    ret = copy_from_user(&buf,buffer,2);
    
    msgs[0].addr    = this_client->addr;    //0x38
    msgs[0].flags   = 0;    //写
    msgs[0].len = 2;    //第一个是要写的寄存器地址,第二个是要写的内容
    msgs[0].buf = buf;

    ret = i2c_transfer(this_client->adapter, msgs, 1);
    if (ret < 0) {
        pr_err("write reg 0x%02x error!\n",buf[0]);
    }
    ret = copy_to_user(buffer,buf,1);*/
    return len;
}

static int i2c_test_release(struct inode *node,struct file *file)
{
    printk(KERN_INFO "Release!!\n");
    return 0;
}

static struct file_operations file_oprts = 
{
    .open = i2c_test_open,
    .read = i2c_test_read,
    .write = i2c_test_write,
    .release = i2c_test_release,
};

/*当i2c bus检测到匹配的device - driver,调用probe()函数,在probe函数中,申请设备号,创建设备节点,绑定相应的file operation结构体。*/
static int downey_drv_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
    /*保存参数client,在i2c读写操作时需要用到这个参数,其中保存了适配器、设备地址等信息*/
    printk(KERN_ALERT "addr = %x\n",client->addr);
    downey_client = client;
    major = register_chrdev(0,DEVICE_NAME,&file_oprts);
    if(major < 0 ){
        printk(KERN_ALERT "Register failed!!\r\n");
        return major;
    }
    printk(KERN_ALERT "Registe success,major number is %d\r\n",major);

    /*以CLASS_NAME创建一个class结构,这个动作将会在/sys/class目录创建一个名为CLASS_NAME的目录*/
    i2c_test_cls = class_create(THIS_MODULE,CLASS_NAME);
    if(IS_ERR(i2c_test_cls))
    {
        unregister_chrdev(major,DEVICE_NAME);
        return PTR_ERR(i2c_test_cls);
    }

    /*以DEVICE_NAME为名,参考/sys/class/CLASS_NAME在/dev目录下创建一个设备:/dev/DEVICE_NAME*/
    i2c_test_dev = device_create(i2c_test_cls,NULL,MKDEV(major,0),NULL,DEVICE_NAME);
    if(IS_ERR(i2c_test_dev))
    {
        class_destroy(i2c_test_cls);
        unregister_chrdev(major,DEVICE_NAME);
        return PTR_ERR(i2c_test_dev);
    }
    printk(KERN_ALERT "i2c_test device init success!!\r\n");
    return 0;
}

/*Remove :当匹配关系不存在时(device或是driver被卸载),调用remove函数,remove函数是probe函数的反操作,将probe函数中申请的资源全部释放。*/
static int downey_drv_remove(struct i2c_client *client)
{
    printk(KERN_ALERT  "remove!!!\n");
    device_destroy(i2c_test_cls,MKDEV(major,0));
    class_unregister(i2c_test_cls);
    class_destroy(i2c_test_cls);
    unregister_chrdev(major,DEVICE_NAME);
    return 0;
}

static struct i2c_driver downey_drv = {
    /*.driver中的name元素仅仅是一个标识,并不作为bus匹配的name识别*/
    .driver = {
        .name = "random",
        .owner = THIS_MODULE,
    },
    .probe = downey_drv_probe,
    .remove = downey_drv_remove,
    /*.id_table中存储driver名称,作为bus匹配时的识别*/
    .id_table = downey_drv_id_table,
    // .address_list = downey_i2c,
};


int drv_init(void)
{
    int ret = 0;
    printk(KERN_ALERT  "init!!!\n");
    ret  = i2c_add_driver(&downey_drv);
    if(ret){
        printk(KERN_ALERT "add driver failed!!!\n");
        return -ENODEV;
    }
    return 0;
}


void drv_exit(void)
{
    i2c_del_driver(&downey_drv);
    return ;
}

MODULE_LICENSE("GPL");
module_init(drv_init);
module_exit(drv_exit);

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值