linux i2c驱动流程

在init中通过内核通知链bus_register_notifier 注册总线通知链

static int __init i2c_dev_init(void)
{
	int res;

	printk(KERN_INFO "i2c /dev entries driver\n");

	res = register_chrdev_region(MKDEV(I2C_MAJOR, 0), I2C_MINORS, "i2c");
	if (res)
		goto out;

	i2c_dev_class = class_create(THIS_MODULE, "i2c-dev");
	if (IS_ERR(i2c_dev_class)) {
		res = PTR_ERR(i2c_dev_class);
		goto out_unreg_chrdev;
	}
	i2c_dev_class->dev_groups = i2c_groups;

	/* Keep track of adapters which will be added or removed later */
	res = bus_register_notifier(&i2c_bus_type, &i2cdev_notifier);
	if (res)
		goto out_unreg_class;

	/* Bind to already existing adapters right away */
	i2c_for_each_dev(NULL, i2cdev_attach_adapter);

	return 0;

out_unreg_class:
	class_destroy(i2c_dev_class);
out_unreg_chrdev:
	unregister_chrdev_region(MKDEV(I2C_MAJOR, 0), I2C_MINORS);
out:
	printk(KERN_ERR "%s: Driver Initialisation failed\n", __FILE__);
	return res;
}

在i2cdev_attach_adapter中注册dev,分配设备号

static int i2cdev_notifier_call(struct notifier_block *nb, unsigned long action,
			 void *data)
{
	struct device *dev = data;

	switch (action) {
	case BUS_NOTIFY_ADD_DEVICE:
		return i2cdev_attach_adapter(dev, NULL);
	case BUS_NOTIFY_DEL_DEVICE:
		return i2cdev_detach_adapter(dev, NULL);
	}

	return 0;
}

static struct notifier_block i2cdev_notifier = {
	.notifier_call = i2cdev_notifier_call,
};

static const struct file_operations i2cdev_fops = {
	.owner		= THIS_MODULE,
	.llseek		= no_llseek,
	.read		= i2cdev_read,
	.write		= i2cdev_write,
	.unlocked_ioctl	= i2cdev_ioctl,
	.open		= i2cdev_open,
	.release	= i2cdev_release,
};

/* ------------------------------------------------------------------------- */

static struct class *i2c_dev_class;

static int i2cdev_attach_adapter(struct device *dev, void *dummy)
{
	struct i2c_adapter *adap;
	struct i2c_dev *i2c_dev;
	int res;

	if (dev->type != &i2c_adapter_type)
		return 0;
	adap = to_i2c_adapter(dev);

	i2c_dev = get_free_i2c_dev(adap);
	if (IS_ERR(i2c_dev))
		return PTR_ERR(i2c_dev);

	cdev_init(&i2c_dev->cdev, &i2cdev_fops);
	i2c_dev->cdev.owner = THIS_MODULE;
	res = cdev_add(&i2c_dev->cdev, MKDEV(I2C_MAJOR, adap->nr), 1);
	if (res)
		goto error_cdev;

	/* register this i2c device with the driver core */
	i2c_dev->dev = device_create(i2c_dev_class, &adap->dev,
				     MKDEV(I2C_MAJOR, adap->nr), NULL,
				     "i2c-%d", adap->nr);
	if (IS_ERR(i2c_dev->dev)) {
		res = PTR_ERR(i2c_dev->dev);
		goto error;
	}

	pr_debug("i2c-dev: adapter [%s] registered as minor %d\n",
		 adap->name, adap->nr);
	return 0;
error:
	cdev_del(&i2c_dev->cdev);
error_cdev:
	put_i2c_dev(i2c_dev);
	return res;
}

最后上层在通过open 和ioctl来调用I2C设备,也可以直接在驱动中调用
调用I2C读写的方法就是去填充struct i2c_msg 这个结构体

struct i2c_msg {
	__u16 addr;	/* slave address			*/
	__u16 flags;
#define I2C_M_RD		0x0001	/* read data, from slave to master */
					/* I2C_M_RD is guaranteed to be 0x0001! */
#define I2C_M_TEN		0x0010	/* this is a ten bit chip address */
#define I2C_M_RECV_LEN		0x0400	/* length will be first received byte */
#define I2C_M_NO_RD_ACK		0x0800	/* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_IGNORE_NAK	0x1000	/* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_REV_DIR_ADDR	0x2000	/* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_NOSTART		0x4000	/* if I2C_FUNC_NOSTART */
#define I2C_M_STOP		0x8000	/* if I2C_FUNC_PROTOCOL_MANGLING */
	__u16 len;		/* msg length				*/
	__u8 *buf;		/* pointer to msg data			*/
};

如果在上层实现调用实际是调用ioctl 中的i2cdev_ioctl_rdwr


static long i2cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
	struct i2c_client *client = file->private_data;
	unsigned long funcs;

	dev_dbg(&client->adapter->dev, "ioctl, cmd=0x%02x, arg=0x%02lx\n",
		cmd, arg);

	switch (cmd) {
	....
		case I2C_RDWR:
		return i2cdev_ioctl_rdwr(client, arg);
	....	

需要在上层传入struct i2c_rdwr_ioctl_data结构体方法如下

struct i2c_rdwr_ioctl_data {
	struct i2c_msg __user *msgs;	/* pointers to i2c_msgs */
	__u32 nmsgs;			/* number of i2c_msgs */
};

read

struct i2c_rdwr_ioctl_data work_queue;
 int fd = open(dev, O_RDWR);
    work_queue.nmsgs = 2;
    work_queue.msgs = (struct i2c_msg *)malloc(work_queue.nmsgs * sizeof(
                            struct i2c_msg));
                            
     (work_queue.msgs[0]).len = 1;
    (work_queue.msgs[0]).flags = 0;
    (work_queue.msgs[0]).addr = slave_address;//从机地址
    (work_queue.msgs[0]).buf = &w_val;//从机寄存器地址

    (work_queue.msgs[1]).len = len;
    (work_queue.msgs[1]).flags = I2C_M_RD;
    (work_queue.msgs[1]).addr = slave_address;//从机寄存器地址
    (work_queue.msgs[1]).buf = buf;//read buf

    ret = ioctl(fd, I2C_RDWR, (unsigned long) &work_queue);
 

write

 struct i2c_rdwr_ioctl_data work_queue;
 unsigned char w_buf[len + 1];
 w_buf[0] = reg_address;//从机寄存器地址
 int fd = open(dev, O_RDWR);
 work_queue.nmsgs = 1;
 work_queue.msgs = (struct i2c_msg *)malloc(work_queue.nmsgs * sizeof(
                            struct  i2c_msg));
                          


    (work_queue.msgs[0]).len = 1 + len;
    (work_queue.msgs[0]).addr = slave_address;//从机地址
    (work_queue.msgs[0]).buf = w_buf;
    (work_queue.msgs[0]).flags = 0;

    memcpy(w_buf + 1, buf, len);//要写入的buf

    ret = ioctl(fd, I2C_RDWR, (unsigned long) &work_queue);

最近遇到一个I2C经常error的问题,打印hal层的的数据都是ok的,但是发现在kernel中从copy_from_user中拿到的flags是随机数,之后再hal对I2C加上bus锁解决了问题
linux i2c驱动流程_第1张图片

你可能感兴趣的:(driver)