从零开始之驱动发开、linux驱动(五十四、linux2.6下的IIC总结[8])

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_16777851/article/details/88931182

2.6.35.7内核的iic设备已经分析完毕了。

我们这里做一下总结。

首先linux下注册iic设备的方式是有五种的。

这里我们对每一种的注册和顺序进行简单总结。

这里我们的参考资料如下

Documentation/i2c/instantiating-devices

第一种方式:

在mach-xxxx.c文件里面指定号设备名称,地址。

static struct i2c_board_info h4_i2c_board_info[] __initdata = {
	{
		I2C_BOARD_INFO("isp1301_omap", 0x2d),
		.irq		= OMAP_GPIO_IRQ(125),
	},
	{	/* EEPROM on mainboard */
		I2C_BOARD_INFO("24c01", 0x52),
		.platform_data	= &m24c01,
	},
	{	/* EEPROM on cpu card */
		I2C_BOARD_INFO("24c01", 0x57),
		.platform_data	= &m24c01,
	},
};

并在内核启动初期,注册到单板的设备链表中。

static void __init omap_h4_init(void)
{
	(...)
	i2c_register_board_info(1, h4_i2c_board_info,
			ARRAY_SIZE(h4_i2c_board_info));
	(...)
}

当iic控制器注册(adaptor)的时候,会遍历通过i2c_register_board_info注册的设备所在的链表。

之后通过adapter中总线的match函数,来和通过i2c_add_driver,注册的i2c_driver所代表的设备来名称来匹配。

这里要说的是这个设备的驱动也需要存在的。

这里要注意的是i2c_register_board_info函数的第一个参数,表示adaptor的编号,是要和芯片手册中一致的。

因为设备在注册adaptor的时候,要确定设备所在总选和adaptor标号一致,才会创建设备。

这里的设备创建成功后,会和所有的i2c_bus_type上的驱动进行匹配,名字相同既能匹配上。

第二种方式:

这种当时就比较牛逼了,不用再mach-xxxx.c文件定义设备。

这个属于比较懒的做法,就是使用make meunconfig选择了驱动程序,却没在mach文件中定义设备。

当iic控制器注册(adaptor)的时候,会遍历所有在i2c_bus_type上注册的驱动链表。


static int i2c_register_adapter(struct i2c_adapter *adap)
{
    .....


	/* Notify drivers */
	mutex_lock(&core_lock);
	dummy = bus_for_each_drv(&i2c_bus_type, NULL, adap,
				 __process_new_adapter);
	mutex_unlock(&core_lock);

	return 0;


    ......

	return res;
}


static int __process_new_adapter(struct device_driver *d, void *data)
{
	return i2c_do_add_adapter(to_i2c_driver(d), data);
}


static int i2c_do_add_adapter(struct i2c_driver *driver,
			      struct i2c_adapter *adap)
{
	/* Detect supported devices on that bus, and instantiate them */
	i2c_detect(adap, driver);

	/* Let legacy drivers scan this bus for matching devices */
	if (driver->attach_adapter) {
		/* We ignore the return code; if it fails, too bad */
		driver->attach_adapter(adap);
	}
	return 0;
}

下面就是重点了,让下面这个函数尝试用驱动函数匹配能不能匹配到设备。


static int i2c_detect(struct i2c_adapter *adapter, struct i2c_driver *driver)
{
	const unsigned short *address_list;
	struct i2c_client *temp_client;
	int i, err = 0;
	int adap_id = i2c_adapter_id(adapter);

	address_list = driver->address_list;
	if (!driver->detect || !address_list)
		return 0;

	/* Set up a temporary client to help detect callback */
	temp_client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
	if (!temp_client)
		return -ENOMEM;
	temp_client->adapter = adapter;

	/* Stop here if the classes do not match */
	if (!(adapter->class & driver->class))
		goto exit_free;

	/* Stop here if the bus doesn't support probing */
	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_READ_BYTE)) {
		if (address_list[0] == I2C_CLIENT_END)
			goto exit_free;

		dev_warn(&adapter->dev, "Probing not supported\n");
		err = -EOPNOTSUPP;
		goto exit_free;
	}

	for (i = 0; address_list[i] != I2C_CLIENT_END; i += 1) {
		dev_dbg(&adapter->dev, "found normal entry for adapter %d, "
			"addr 0x%02x\n", adap_id, address_list[i]);
		temp_client->addr = address_list[i];
		err = i2c_detect_address(temp_client, driver);
		if (err)
			goto exit_free;
	}

 exit_free:
	kfree(temp_client);
	return err;
}

可以看到,上面只是定义了一个临时的设备(client),假设有这个设备。

让驱动程序中支持的所有地址和设备进行匹配。

/* Addresses scanned */
static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4c,
					0x4d, 0x4e, I2C_CLIENT_END };

这里是adaptor已经注册。硬件可以用了。所以这里会使用一个比较较真的方式,直接给设备发送消息,当然这里只是发送一个字节的地址信息,如设备会回复ACK,这里就认为设备是存在的。

这里如果检查通过的话,会重新申请一个client,加入驱动支持的设备链表。


static int i2c_detect_address(struct i2c_client *temp_client,
			      struct i2c_driver *driver)
{
	struct i2c_board_info info;
	struct i2c_adapter *adapter = temp_client->adapter;
	int addr = temp_client->addr;
	int err;

	/* Make sure the address is valid */
	err = i2c_check_addr_validity(addr);
	if (err) {
		dev_warn(&adapter->dev, "Invalid probe address 0x%02x\n",
			 addr);
		return err;
	}

	/* Skip if already in use */
	if (i2c_check_addr_busy(adapter, addr))
		return 0;

	/* Make sure there is something at this address */
	if (!i2c_default_probe(adapter, addr))
		return 0;

	/* Finally call the custom detection function */
	memset(&info, 0, sizeof(struct i2c_board_info));
	info.addr = addr;
	err = driver->detect(temp_client, &info);
	if (err) {
		/* -ENODEV is returned if the detection fails. We catch it
		   here as this isn't an error. */
		return err == -ENODEV ? 0 : err;
	}

	/* Consistency check */
	if (info.type[0] == '\0') {
		dev_err(&adapter->dev, "%s detection function provided "
			"no name for 0x%x\n", driver->driver.name,
			addr);
	} else {
		struct i2c_client *client;

		/* Detection succeeded, instantiate the device */
		dev_dbg(&adapter->dev, "Creating %s at 0x%02x\n",
			info.type, info.addr);
		client = i2c_new_device(adapter, &info);
		if (client)
			list_add_tail(&client->detected, &driver->clients);
		else
			dev_err(&adapter->dev, "Failed creating %s at 0x%02x\n",
				info.type, info.addr);
	}
	return 0;
}

这里我们看到,调用了下面这样的一个步骤。

	memset(&info, 0, sizeof(struct i2c_board_info));
	info.addr = addr;
	err = driver->detect(temp_client, &info);

这个就是驱动要做的事情。

也就是如果采用这种搜索的方式,匹配成功的设备,需要进一步的检查了。毕竟地址相同的iic设备,也是有很多的。这个需要具体的设备来进行更详细的检查。比如读取厂家ID,设备ID,进行比较等。

当然这里要说明的是,如果真的匹配到了。这个client,要在detect函数中,要设置一下第二个参数i2c_board_info->type,为这会驱动名字的,这样才能在i2c_new_device函数创建client的时候,创建出来的设备名字和驱动名字一样,才能匹配执行probe函数。


这种情况要满足三个条件,

  • 内核驱动是选择了的。
  • 驱动中支持的地址表,也是尝试匹配有设备响应的。
  • detcet函数的检查通过,给i2c_board_info->type也设置了和driver相同的名字了。

第三种方式:

在一个adaptor创建的时候,在sysfs中创建了可以支持创建和删除的设备(client)

echo eeprom 0x50 > /sys/bus/i2c/devices/i2c-3/new_device

echo 0x50 > /sys/bus/i2c/devices/i2c-3/delete_device

第四种方式:

前面我们已经看到了,创建设备的本质都是调用i2c_new_device函数来创建client。

struct i2c_client *
i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info);

只不过前集中方式都是在确定设备存在(创建之前做了检查,或者mach-xxx.c定义了)的情况下来创建的。

那我们是不是可以直接调用i2c_new_device来创建一个设备!

举例1:


static int wm8971_add_i2c_device(struct platform_device *pdev,
				 const struct wm8971_setup_data *setup)
{
	struct i2c_board_info info;
	struct i2c_adapter *adapter;
	struct i2c_client *client;
	int ret;

	ret = i2c_add_driver(&wm8971_i2c_driver);
	if (ret != 0) {
		dev_err(&pdev->dev, "can't add i2c driver\n");
		return ret;
	}

	memset(&info, 0, sizeof(struct i2c_board_info));
	info.addr = setup->i2c_address;
	strlcpy(info.type, "wm8971", I2C_NAME_SIZE);

	adapter = i2c_get_adapter(setup->i2c_bus);
	if (!adapter) {
		dev_err(&pdev->dev, "can't get i2c adapter %d\n",
			setup->i2c_bus);
		goto err_driver;
	}

	client = i2c_new_device(adapter, &info);
	i2c_put_adapter(adapter);
	if (!client) {
		dev_err(&pdev->dev, "can't add i2c device at 0x%x\n",
			(unsigned int)info.addr);
		goto err_driver;
	}

	return 0;

err_driver:
	i2c_del_driver(&wm8971_i2c_driver);
	return -ENODEV;
}

这种方式的格式通常是这样的,设备和驱动一块注册的(当然也可以只定义设备[client])。

int xxxadd_i2c_device()
{
    struct i2c_board_info info;
	struct i2c_adapter *adapter;
	struct i2c_client *client;


    i2c_add_driver(&xxxx_i2c_driver)           //注册设备

    strcpy(info.type, "device_name");


    adapter = i2c_get_adapter(1);              //获取adaptor,当然这里需要知道是那个adaptor

    /* 注册设备 */
    client = i2c_new_device(adapter, &info);
	i2c_put_adapter(adapter);    //adaptor使用数量+1


}

举例2:

注册一个假设备,以下面这个为例


static int max6875_probe(struct i2c_client *client,
			 const struct i2c_device_id *id)
{
	struct i2c_adapter *adapter = client->adapter;
	struct max6875_data *data;
	int err;

	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WRITE_BYTE_DATA
				     | I2C_FUNC_SMBUS_READ_BYTE))
		return -ENODEV;

	/* Only bind to even addresses */
	if (client->addr & 1)
		return -ENODEV;

	if (!(data = kzalloc(sizeof(struct max6875_data), GFP_KERNEL)))
		return -ENOMEM;

	/* A fake client is created on the odd address */
	data->fake_client = i2c_new_dummy(client->adapter, client->addr + 1);
	if (!data->fake_client) {
		err = -ENOMEM;
		goto exit_kfree;
	}

	/* Init real i2c_client */
	i2c_set_clientdata(client, data);
	mutex_init(&data->update_lock);

	err = sysfs_create_bin_file(&client->dev.kobj, &user_eeprom_attr);
	if (err)
		goto exit_remove_fake;

	return 0;

exit_remove_fake:
	i2c_unregister_device(data->fake_client);
exit_kfree:
	kfree(data);
	return err;
}

假设备的特点也很明显,即名字是dummy。对于假设备的驱动,在i2c-core中也有定义,完全是一个空函数。

struct i2c_client *i2c_new_dummy(struct i2c_adapter *adapter, u16 address)
{
	struct i2c_board_info info = {
		I2C_BOARD_INFO("dummy", address),
	};

	return i2c_new_device(adapter, &info);
}



static const struct i2c_device_id dummy_id[] = {
	{ "dummy", 0 },
	{ },
};

static int dummy_probe(struct i2c_client *client,
		       const struct i2c_device_id *id)
{
	return 0;
}

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

static struct i2c_driver dummy_driver = {
	.driver.name	= "dummy",
	.probe		= dummy_probe,
	.remove		= dummy_remove,
	.id_table	= dummy_id,
};

对于假设备驱动,因为是不需要驱动函数的。

通常都是通过sys文件系统定义一个attributr接口来操作的,比如上面的这个例子。

	err = sysfs_create_bin_file(&client->dev.kobj, &user_eeprom_attr);

举例3:

当然有些情况下,使用者也不知道设备地址是那个。

这种情况和第二种比较相似,但这个是设备这里给出几个地址,来尝试匹配。

如果能匹配到就进行注册。


struct i2c_client *
i2c_new_probed_device(struct i2c_adapter *adap,
		      struct i2c_board_info *info,
		      unsigned short const *addr_list)
{
	int i;

	/* Stop here if the bus doesn't support probing */
	if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_READ_BYTE)) {
		dev_err(&adap->dev, "Probing not supported\n");
		return NULL;
	}

	for (i = 0; addr_list[i] != I2C_CLIENT_END; i++) {
		/* Check address validity */
		if (i2c_check_addr_validity(addr_list[i]) < 0) {
			dev_warn(&adap->dev, "Invalid 7-bit address "
				 "0x%02x\n", addr_list[i]);
			continue;
		}

		/* Check address availability */
		if (i2c_check_addr_busy(adap, addr_list[i])) {
			dev_dbg(&adap->dev, "Address 0x%02x already in "
				"use, not probing\n", addr_list[i]);
			continue;
		}

		/* Test address responsiveness */
		if (i2c_default_probe(adap, addr_list[i]))
			break;
	}

	if (addr_list[i] == I2C_CLIENT_END) {
		dev_dbg(&adap->dev, "Probing failed, no device found\n");
		return NULL;
	}

	info->addr = addr_list[i];
	return i2c_new_device(adap, info);
}

比如下面这个例子


/* Instantiate the I2C IR receiver device, if present */
void __devinit init_bttv_i2c_ir(struct bttv *btv)
{
	if (0 == btv->i2c_rc) {
		struct i2c_board_info info;
		/* The external IR receiver is at i2c address 0x34 (0x35 for
		   reads).  Future Hauppauge cards will have an internal
		   receiver at 0x30 (0x31 for reads).  In theory, both can be
		   fitted, and Hauppauge suggest an external overrides an
		   internal.

		   That's why we probe 0x1a (~0x34) first. CB
		*/
		const unsigned short addr_list[] = {
			0x1a, 0x18, 0x4b, 0x64, 0x30, 0x71,
			I2C_CLIENT_END
		};

		memset(&info, 0, sizeof(struct i2c_board_info));
		strlcpy(info.type, "ir_video", I2C_NAME_SIZE);
		i2c_new_probed_device(&btv->c.i2c_adap, &info, addr_list);
	}
}

第五种方式

直接采用i2cdev这个字符设备创建一个client。

当然这种方式是依赖adaptor的,一个adaptor代表一个次设备。

可以看下面的代码


static int __init i2c_dev_init(void)
{
	int res;

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

	res = register_chrdev(I2C_MAJOR, "i2c", &i2cdev_fops);
	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;
	}

    /* 注册一个驱动 */
	res = i2c_add_driver(&i2cdev_driver);
	if (res)
		goto out_unreg_class;

	return 0;

out_unreg_class:
	class_destroy(i2c_dev_class);
out_unreg_chrdev:
	unregister_chrdev(I2C_MAJOR, "i2c");
out:
	printk(KERN_ERR "%s: Driver Initialisation failed\n", __FILE__);
	return res;
}
module_init(i2c_dev_init);

这个驱动如下,当然这个驱动是匹配不到任何设备的。i2cdev也不是让他匹配设备,而是在和设备遍历的过程中,和adaptor发生作用。


static struct i2c_driver i2cdev_driver = {
	.driver = {
		.name	= "dev_driver",
	},
	.attach_adapter	= i2cdev_attach_adapter,
	.detach_adapter	= i2cdev_detach_adapter,
};


int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
{
	int res;

	/* Can't register until after driver model init */
	if (unlikely(WARN_ON(!i2c_bus_type.p)))
		return -EAGAIN;

	/* add the driver to the list of i2c drivers in the driver core */
	driver->driver.owner = owner;
	driver->driver.bus = &i2c_bus_type;

	/* When registration returns, the driver core
	 * will have called probe() for all matching-but-unbound devices.
	 */
	res = driver_register(&driver->driver);
	if (res)
		return res;

	pr_debug("i2c-core: driver [%s] registered\n", driver->driver.name);

	INIT_LIST_HEAD(&driver->clients);
	/* Walk the adapters that are already present */
	mutex_lock(&core_lock);
	bus_for_each_dev(&i2c_bus_type, NULL, driver, __process_new_driver);
	mutex_unlock(&core_lock);

	return 0;
}

我们看到注册驱动函数的最后,__process_new_driver是遍历设备的

static int __process_new_driver(struct device *dev, void *data)
{
	if (dev->type != &i2c_adapter_type)
		return 0;
	return i2c_do_add_adapter(data, to_i2c_adapter(dev));
}


static int i2c_do_add_adapter(struct i2c_driver *driver,
			      struct i2c_adapter *adap)
{
	/* Detect supported devices on that bus, and instantiate them */
	i2c_detect(adap, driver);

	/* Let legacy drivers scan this bus for matching devices */
	if (driver->attach_adapter) {    /* 可以看到这个过程 */
		/* We ignore the return code; if it fails, too bad */
		driver->attach_adapter(adap);
	}
	return 0;
}

可以看到除了detect之外,attach_adapter就是给i2cdev准备的。

可以看到,就是使用device_create来创建次设备号的。


static int i2cdev_attach_adapter(struct i2c_adapter *adap)
{
	struct i2c_dev *i2c_dev;
	int res;

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

	/* 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;
	}
	res = device_create_file(i2c_dev->dev, &dev_attr_name);
	if (res)
		goto error_destroy;

	pr_debug("i2c-dev: adapter [%s] registered as minor %d\n",
		 adap->name, adap->nr);
	return 0;
error_destroy:
	device_destroy(i2c_dev_class, MKDEV(I2C_MAJOR, adap->nr));
error:
	return_i2c_dev(i2c_dev);
	return res;
}

这里可以看到,一个i2cdev的次设备,就代表一个adaptor。

也就是我们不用指定设备了,直接操作这个adaptor。


static int i2cdev_open(struct inode *inode, struct file *file)
{
	unsigned int minor = iminor(inode);
	struct i2c_client *client;
	struct i2c_adapter *adap;

	adap = i2c_get_adapter(minor);
	if (!adap)
		return -ENODEV;

	/* This creates an anonymous i2c_client, which may later be
	 * pointed to some address using I2C_SLAVE or I2C_SLAVE_FORCE.
	 *
	 * This client is ** NEVER REGISTERED ** with the driver model
	 * or I2C core code!!  It just holds private copies of addressing
	 * information and maybe a PEC flag.
	 */
	client = kzalloc(sizeof(*client), GFP_KERNEL);
	if (!client) {
		i2c_put_adapter(adap);
		return -ENOMEM;
	}
	snprintf(client->name, I2C_NAME_SIZE, "i2c-dev %d", adap->nr);

	client->adapter = adap;
	file->private_data = client;

	return 0;
}

这边可以看到,每次打开这个adaptor时,会创建一个client。

而这个client就和打开的这个文件绑定了。

后面可以直接在userspace使用读写函数来操作了。

当然这个纯粹是用户层创建的,cliert,所以也要通过ioctl设置设备地址。


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_SLAVE:
	case I2C_SLAVE_FORCE:
		/* NOTE:  devices set up to work with "new style" drivers
		 * can't use I2C_SLAVE, even when the device node is not
		 * bound to a driver.  Only I2C_SLAVE_FORCE will work.
		 *
		 * Setting the PEC flag here won't affect kernel drivers,
		 * which will be using the i2c_client node registered with
		 * the driver model core.  Likewise, when that client has
		 * the PEC flag already set, the i2c-dev driver won't see
		 * (or use) this setting.
		 */
		if ((arg > 0x3ff) ||
		    (((client->flags & I2C_M_TEN) == 0) && arg > 0x7f))
			return -EINVAL;
		if (cmd == I2C_SLAVE && i2cdev_check_addr(client->adapter, arg))
			return -EBUSY;
		/* REVISIT: address could become busy later */
		client->addr = arg;
		return 0;
	case I2C_TENBIT:
		if (arg)
			client->flags |= I2C_M_TEN;
		else
			client->flags &= ~I2C_M_TEN;
		return 0;
	case I2C_PEC:
		if (arg)
			client->flags |= I2C_CLIENT_PEC;
		else
			client->flags &= ~I2C_CLIENT_PEC;
		return 0;
	case I2C_FUNCS:
		funcs = i2c_get_functionality(client->adapter);
		return put_user(funcs, (unsigned long __user *)arg);

	case I2C_RDWR:
		return i2cdev_ioctl_rdrw(client, arg);

	case I2C_SMBUS:
		return i2cdev_ioctl_smbus(client, arg);

	case I2C_RETRIES:
		client->adapter->retries = arg;
		break;
	case I2C_TIMEOUT:
		/* For historical reasons, user-space sets the timeout
		 * value in units of 10 ms.
		 */
		client->adapter->timeout = msecs_to_jiffies(arg * 10);
		break;
	default:
		/* NOTE:  returning a fault code here could cause trouble
		 * in buggy userspace code.  Some old kernel bugs returned
		 * zero in this case, and userspace code might accidentally
		 * have depended on that bug.
		 */
		return -ENOTTY;
	}
	return 0;
}

之后就可使用读写函数来操纵了。


static ssize_t i2cdev_read(struct file *file, char __user *buf, size_t count,
		loff_t *offset)
{
	char *tmp;
	int ret;

	struct i2c_client *client = file->private_data;

	if (count > 8192)
		count = 8192;

	tmp = kmalloc(count, GFP_KERNEL);
	if (tmp == NULL)
		return -ENOMEM;

	pr_debug("i2c-dev: i2c-%d reading %zu bytes.\n",
		iminor(file->f_path.dentry->d_inode), count);

	ret = i2c_master_recv(client, tmp, count);
	if (ret >= 0)
		ret = copy_to_user(buf, tmp, count) ? -EFAULT : ret;
	kfree(tmp);
	return ret;
}

static ssize_t i2cdev_write(struct file *file, const char __user *buf,
		size_t count, loff_t *offset)
{
	int ret;
	char *tmp;
	struct i2c_client *client = file->private_data;

	if (count > 8192)
		count = 8192;

	tmp = kmalloc(count, GFP_KERNEL);
	if (tmp == NULL)
		return -ENOMEM;
	if (copy_from_user(tmp, buf, count)) {
		kfree(tmp);
		return -EFAULT;
	}

	pr_debug("i2c-dev: i2c-%d writing %zu bytes.\n",
		iminor(file->f_path.dentry->d_inode), count);

	ret = i2c_master_send(client, tmp, count);
	kfree(tmp);
	return ret;
}

说明:内核建议使用第一种、第三种、第五种,方法来创建设备。比较这三种相对使用者都是查看了硬件手册,知道了地址和挂接的总线的。

驱动程序相对稳定,也比较简单,这里就不再说明。

adaptor主要是一些硬件操作和注册,核心是发生函数和中断处理函数,我们前面已经进行了分析。

猜你喜欢

转载自blog.csdn.net/qq_16777851/article/details/88931182