IIC驱动程序之完善篇

 

下面我们来分析一个比较完整的IIC驱动程序:
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/jiffies.h>
#include <linux/i2c.h>
#include <linux/mutex.h>
#include <linux/fs.h>
#include <asm/uaccess.h>

static unsigned short ignore[]      = { I2C_CLIENT_END };
static unsigned short normal_addr[] = { 0x50, I2C_CLIENT_END }; /* 地址值是7位 */
                                        /* 改为0x60的话, 由于不存在设备地址为0x60的设备, 所以at24cxx_detect不被调用 */

static unsigned short force_addr[] = {ANY_I2C_BUS, 0x60, I2C_CLIENT_END};
static unsigned short * forces[] = {force_addr, NULL};
          
static struct i2c_client_address_data addr_data = {
 .normal_i2c = normal_addr,  /* 要发出S信号和设备地址并得到ACK信号,才能确定存在这个设备 */
 .probe  = ignore,
 .ignore  = ignore,
 //.forces     = forces, /* 强制认为存在这个设备 */
};

static struct i2c_driver at24cxx_driver;


static int major;
static struct class *cls;
struct i2c_client *at24cxx_client;

static ssize_t at24cxx_read(struct file *file, char __user *buf, size_t size, loff_t * offset)
{
 unsigned char address;
 unsigned char data;
 struct i2c_msg msg[2];
 int ret;
 
 /* address = buf[0] 
  * data    = buf[1]
  */
 if (size != 1)
  return -EINVAL;
 
 copy_from_user(&address, buf, 1);/ *将内核用户空间的数据(在buf中)拷贝到address里面 */
 /* 数据传输三要素: 源,目的,长度 */

 /* 读AT24CXX时,要先把要读的存储空间的地址发给它 */
 msg[0].addr  = at24cxx_client->addr;  /* 目的 */
 msg[0].buf   = &address;              /* 源 */
 msg[0].len   = 1;                     /* 地址=1 byte */
 msg[0].flags = 0;                     /* 表示写 */

 /* 然后启动读操作 */
 msg[1].addr  = at24cxx_client->addr;  /* 源 */
 msg[1].buf   = &data;                 /* 目的 */
 msg[1].len   = 1;                     /* 数据=1 byte */
 msg[1].flags = I2C_M_RD;                     /* 表示读 */


 ret = i2c_transfer(at24cxx_client->adapter, msg, 2);
 if (ret == 2)
 {
  copy_to_user(buf, &data, 1);
  return 1;
 }
 else
  return -EIO;
}

static ssize_t at24cxx_write(struct file *file, const char __user *buf, size_t size, loff_t *offset)
{
 unsigned char val[2];
 struct i2c_msg msg[1];
 int ret;
 
 /* address = buf[0] 
  * data    = buf[1]
  */
 if (size != 2)
  return -EINVAL;
 
 copy_from_user(val, buf, 2);

 /* 数据传输三要素: 源,目的,长度 */
 msg[0].addr  = at24cxx_client->addr;  /* 目的 */
 msg[0].buf   = val;                   /* 源 */
 msg[0].len   = 2;                     /* 地址+数据=2 byte */
 msg[0].flags = 0;                     /* 表示写 */

 ret = i2c_transfer(at24cxx_client->adapter, msg, 1);
 if (ret == 1)
  return 2;
 else
  return -EIO;
}


static struct file_operations at24cxx_fops = {
 .owner = THIS_MODULE,
 .read  = at24cxx_read,
 .write = at24cxx_write,
};

static int at24cxx_detect(struct i2c_adapter *adapter, int address, int kind)
{ 
 printk("at24cxx_detect\n");

 /* 构构一个i2c_client结构体: 以后收改数据时会用到它 */
 at24cxx_client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
 at24cxx_client->addr    = address;
 at24cxx_client->adapter = adapter;
 at24cxx_client->driver  = &at24cxx_driver;
 strcpy(at24cxx_client->name, "at24cxx");
 i2c_attach_client(at24cxx_client);
 
 major = register_chrdev(0, "at24cxx", &at24cxx_fops);

 cls = class_create(THIS_MODULE, "at24cxx");
 class_device_create(cls, NULL, MKDEV(major, 0), NULL, "at24cxx"); /* /dev/at24cxx */
 
 return 0;
}

static int at24cxx_attach(struct i2c_adapter *adapter)
{
 return i2c_probe(adapter, &addr_data, at24cxx_detect);
}

static int at24cxx_detach(struct i2c_client *client)
{
 printk("at24cxx_detach\n");
 class_device_destroy(cls, MKDEV(major, 0));
 class_destroy(cls);
 unregister_chrdev(major, "at24cxx");

 i2c_detach_client(client);
 kfree(i2c_get_clientdata(client));

 return 0;
}


/* 1. 分配一个i2c_driver结构体 */
/* 2. 设置i2c_driver结构体 */
static struct i2c_driver at24cxx_driver = {
 .driver = {
  .name = "at24cxx",
 },
 .attach_adapter = at24cxx_attach,
 .detach_client  = at24cxx_detach,
};

static int at24cxx_init(void)
{
 i2c_add_driver(&at24cxx_driver);
 return 0;
}

static void at24cxx_exit(void)
{
 i2c_del_driver(&at24cxx_driver);
}

module_init(at24cxx_init);
module_exit(at24cxx_exit);

MODULE_LICENSE("GPL");


 

之前我们分析的IIC驱动程序主要是通过IIC总线机制完成了存储设备的识别,但是我们想对存储设备进行操作怎么办呢?那就要用到字符设备驱动程序的概念了!其实 IIC总线机制就和平台总线机制是一个性质的,要完成具体的操作,还需要字符设备的支持。下面我们来详细分析一下:
首先通过IIC总线机制完成了存储设备的识别,接着注册字符设备以及创建设备节点,同时定义了操作函数,那么我们这里详细要分析的就是操作函数了,我们先来看看iic的消息传输函数:
i2c_transfer(struct i2c_adapter * adap, struct i2c_msg *msgs, int num)
adap:适配器
msgs:消息,其格式如下:

struct i2c_msg { 
__u16 addr;/* 设备地址 */
__u16 flags;/* 读写标志位*/
__u16 len;  /* 消息的长度,字节为单位 */ 
__u8 *buf;  /* 指向消息的指针 */
};




 
我们重点要做的就是来设置这个消息,设置好之后只要用 i2c_transfer发送就可以了。
num:要传输的消息的数目
读函数:
读数据的话,首先把地址写进去,然后把数据读出来
些函数:
些数据的话,首先把地址写进去,然后把数据写进去

测试程序如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>


/* i2c_test r addr
 * i2c_test w addr val
 */

void print_usage(char *file)
{
 printf("%s r addr\n", file);
 printf("%s w addr val\n", file);
}

int main(int argc, char **argv)
{
 int fd;
 unsigned char buf[2];
 
 if ((argc != 3) && (argc != 4))
 {
  print_usage(argv[0]);
  return -1;
 }

 fd = open("/dev/at24cxx", O_RDWR);
 if (fd < 0)
 {
  printf("can't open /dev/at24cxx\n");
  return -1;
 }

 if (strcmp(argv[1], "r") == 0)
 {
  buf[0] = strtoul(argv[2], NULL, 0);
  read(fd, buf, 1);
  printf("data: %c, %d, 0x%2x\n", buf[0], buf[0], buf[0]);
 }
 else if (strcmp(argv[1], "w") == 0)
 {
  buf[0] = strtoul(argv[2], NULL, 0);
  buf[1] = strtoul(argv[3], NULL, 0);
  write(fd, buf, 2);
 }
 else
 {
  print_usage(argv[0]);
  return -1;
 }
 
 return 0;
}


  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值