从零开始之驱动发开、linux驱动(五十二、linux下的IIC设备驱动[6])

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

I2C设备驱动要使用i2c_driver和i2c_client数据结构并填充i2c_driver中的成员函数。 i2c_client一般被包含在设备的私有信息结构体中, 而i2c_driver则适合被定义为全局变量并初始化。
 

这种驱动通常是相对稳定的驱动程序,通常不需要改动。

下面给出内核中已被初始化的i2c_driver。
 

static struct i2c_driver ddc_driver = {
	.driver = {
		.name = "s5p_ddc",
	},
	.id_table	= ddc_idtable,
	.probe		= ddc_probe,
	.remove		= __devexit_p(ddc_remove),
};

这里我们再说一下,linux中设备驱动程序通常是固定的。

也就是这里的ddc驱动程序通常是不用动的。

而具体的这个芯片是挂载那个iic控制器(adaptor)上的,测试由硬件决定的。

现在我们看一下在2.6内核中设备都是定义在单板所在的那个mach目录下的。


/**
 * struct i2c_board_info - template for device creation
 * @type: chip type, to initialize i2c_client.name
 * @flags: to initialize i2c_client.flags
 * @addr: stored in i2c_client.addr
 * @platform_data: stored in i2c_client.dev.platform_data
 * @archdata: copied into i2c_client.dev.archdata
 * @irq: stored in i2c_client.irq
 *
 * I2C doesn't actually support hardware probing, although controllers and
 * devices may be able to use I2C_SMBUS_QUICK to tell whether or not there's
 * a device at a given address.  Drivers commonly need more information than
 * that, such as chip type, configuration, associated IRQ, and so on.
 *
 * i2c_board_info is used to build tables of information listing I2C devices
 * that are present.  This information is used to grow the driver model tree.
 * For mainboards this is done statically using i2c_register_board_info();
 * bus numbers identify adapters that aren't yet available.  For add-on boards,
 * i2c_new_device() does this dynamically with the adapter already known.
 */
struct i2c_board_info {
	char		type[I2C_NAME_SIZE];
	unsigned short	flags;
	unsigned short	addr;
	void		*platform_data;
	struct dev_archdata	*archdata;
#ifdef CONFIG_OF
	struct device_node *of_node;
#endif
	int		irq;
};


/**
 * I2C_BOARD_INFO - macro used to list an i2c device and its address
 * @dev_type: identifies the device type
 * @dev_addr: the device's address on the bus.
 *
 * This macro initializes essential fields of a struct i2c_board_info,
 * declaring what has been provided on a particular board.  Optional
 * fields (such as associated irq, or device-specific platform_data)
 * are provided using conventional syntax.
 */
#define I2C_BOARD_INFO(dev_type, dev_addr) \
	.type = dev_type, .addr = (dev_addr)



/* I2C1 */
static struct i2c_board_info i2c_devs1[] __initdata = {
#ifdef CONFIG_VIDEO_TV20
	{
		I2C_BOARD_INFO("s5p_ddc", (0x74>>1)),
	},
#endif
#ifdef CONFIG_TOUCHSCREEN_GSLX680
	{
		I2C_BOARD_INFO("gslX680", 0x40),
	},
#endif
};

可以看到对于这个单板,在iic1总线控制器(adaptor1)上,挂了两个设备,这里给出了设备的名字和设备的地址。

这里给出我首里这块单板的iic设备的注册


static void __init smdkc110_machine_init(void)
{
    ....

	i2c_register_board_info(0, i2c_devs0, ARRAY_SIZE(i2c_devs0));
	i2c_register_board_info(1, i2c_devs1, ARRAY_SIZE(i2c_devs1));
	i2c_register_board_info(2, i2c_devs2, ARRAY_SIZE(i2c_devs2));

    ...
}

可以看到,这个是很早期就注册了的。

是比驱动程序更早的注册。


/* These symbols are exported ONLY FOR the i2c core.
 * No other users will be supported.
 */
DECLARE_RWSEM(__i2c_board_lock);
EXPORT_SYMBOL_GPL(__i2c_board_lock);

LIST_HEAD(__i2c_board_list);
EXPORT_SYMBOL_GPL(__i2c_board_list);

int __i2c_first_dynamic_bus_num;
EXPORT_SYMBOL_GPL(__i2c_first_dynamic_bus_num);


/**
 * i2c_register_board_info - statically declare I2C devices
 * @busnum: identifies the bus to which these devices belong
 * @info: vector of i2c device descriptors
 * @len: how many descriptors in the vector; may be zero to reserve
 *	the specified bus number.
 *
 * Systems using the Linux I2C driver stack can declare tables of board info
 * while they initialize.  This should be done in board-specific init code
 * near arch_initcall() time, or equivalent, before any I2C adapter driver is
 * registered.  For example, mainboard init code could define several devices,
 * as could the init code for each daughtercard in a board stack.
 *
 * The I2C devices will be created later, after the adapter for the relevant
 * bus has been registered.  After that moment, standard driver model tools
 * are used to bind "new style" I2C drivers to the devices.  The bus number
 * for any device declared using this routine is not available for dynamic
 * allocation.
 *
 * The board info passed can safely be __initdata, but be careful of embedded
 * pointers (for platform_data, functions, etc) since that won't be copied.
 */
int __init
i2c_register_board_info(int busnum,
	struct i2c_board_info const *info, unsigned len)
{
	int status;

	down_write(&__i2c_board_lock);

	/* dynamic bus numbers will be assigned after the last static one */
	if (busnum >= __i2c_first_dynamic_bus_num)
		__i2c_first_dynamic_bus_num = busnum + 1;

	for (status = 0; len; len--, info++) {
		struct i2c_devinfo	*devinfo;

		devinfo = kzalloc(sizeof(*devinfo), GFP_KERNEL);
		if (!devinfo) {
			pr_debug("i2c-core: can't register boardinfo!\n");
			status = -ENOMEM;
			break;
		}

		devinfo->busnum = busnum;
		devinfo->board_info = *info;
		list_add_tail(&devinfo->list, &__i2c_board_list);
	}

	up_write(&__i2c_board_lock);

	return status;
}

真正的设备的注册也很简单,总线的编号通常就是引荐iic的硬件数量。

另一个是把所有的设备都挂在__i2c_board_list链表上,虽然设备是挂在同一链表上,但是每个设备里面都有自己所在的总线编号。

这iic控制器(adaptor)注册时,只需要和这个链表上编号(busnum)相同的设备进行匹配就可以了。

static void i2c_scan_static_board_info(struct i2c_adapter *adapter)
{
	struct i2c_devinfo	*devinfo;

	down_read(&__i2c_board_lock);
	list_for_each_entry(devinfo, &__i2c_board_list, list) {
		if (devinfo->busnum == adapter->nr    //设备的总线编号和adapter->nr一致,才会创建client
				&& !i2c_new_device(adapter,
						&devinfo->board_info))
			dev_err(&adapter->dev,
				"Can't create device at 0x%02x\n",
				devinfo->board_info.addr);
	}
	up_read(&__i2c_board_lock);
}

ddc设备是接在iic总线的,所以它的注册都是调用的iic驱动的注册函数。


static struct i2c_device_id ddc_idtable[] = {
	{"s5p_ddc", 0},
};

MODULE_DEVICE_TABLE(i2c, ddc_idtable);

static struct i2c_driver ddc_driver = {
	.driver = {
		.name = "s5p_ddc",
	},
	.id_table	= ddc_idtable,
	.probe		= ddc_probe,
	.remove		= __devexit_p(ddc_remove),
};

static int __init ddc_init(void)
{
	return i2c_add_driver(&ddc_driver);
}

static void __exit ddc_exit(void)
{
	i2c_del_driver(&ddc_driver);
}

probe函数就跟简单了,就是把驱动和adaptor配上上自动创建的那个client的指针保留一下来。

struct i2c_client *g_ddc_port;




/* i2c client ftn. */
static int __devinit ddc_probe(struct i2c_client *client,
			const struct i2c_device_id *dev_id)
{
	int ret = 0;

	g_ddc_port = client;

	dev_info(&client->adapter->dev, "attached s5p_ddc "
		"into i2c adapter successfully\n");

	return ret;
}

读写函数就跟简单了。


int ddc_read(u8 subaddr, u8 *data, u16 len)
{
	u8 addr = subaddr;
	int ret = 0;

    /* 读函数,第一个msg是设定要读的地址,第二个msg是要读的字节数 */
	struct i2c_msg msg[] = {
		[0] = {
			.addr = g_ddc_port->addr,
			.flags = 0,
			.len = 1,
			.buf = &addr
		},
		[1] = {
			.addr = g_ddc_port->addr,
			.flags = I2C_M_RD,
			.len = len,
			.buf = data
		}
	};

	if (i2c_transfer(g_ddc_port->adapter, msg, 2) != 2)
		ret = -EIO;

	return ret;
}

int ddc_write(u8 *data, u16 len)
{
	int ret = 0;

    /* 写函数就一个msg,直接写就可了 */
	if (i2c_master_send(g_ddc_port, (const char *) data, len) != len)
		ret = -EIO;

	return ret;
}

用户层读写函数怎么使用?

内核的ddc是借用给别的驱动函数使用了。

我这里给出一个使用方式

首先增加一个属性文件

static struct bin_attribute ddc_attr = {
	.attr = {
		.name = "ddc",
		.mode = S_IRUGO | S_IWUGO,
	},

	.read = ddc_read,
    .write = ddc_write,
};

接着在probe函数中,创建出在sys文件系统中的属性文件。(当然remove函数中也应该做好移除工作)


/* i2c client ftn. */
static int __devinit ddc_probe(struct i2c_client *client,
			const struct i2c_device_id *dev_id)
{
	int ret = 0;

	g_ddc_port = client;

	dev_info(&client->adapter->dev, "attached s5p_ddc "
		"into i2c adapter successfully\n");

    /* create the sysfs eeprom file */
	ret = sysfs_create_bin_file(&client->dev.kobj, &ddc_attr );


	return ret;
}

3.把read和write的函数原型做修改

ssize_t ddc_read(struct file *file, struct kobject *kobj, struct bin_attribute *bin_att,
			char *buf, loff_t off, size_t count);

{
	u8 addr = buf[0];
	int ret = 0;

	struct i2c_msg msg[] = {
		[0] = {
			.addr = g_ddc_port->addr,
			.flags = 0,
			.len = 1,
			.buf = &addr
		},
		[1] = {
			.addr = g_ddc_port->addr,
			.flags = I2C_M_RD,
			.len = count,
			.buf = &data[1]
		}
	};

	if (i2c_transfer(g_ddc_port->adapter, msg, 2) != 2)
		ret = -EIO;

	return ret;
}

ssize_t ddc_write(struct file *file,struct kobject *kobj, struct bin_attribute *bin_attr,
			 char *buf, loff_t off, size_t count);
{
	int ret = 0;

	if (i2c_master_send(g_ddc_port, (const char *) buf, count) != len)
		ret = -EIO;

	return ret;
}

这样对设备的读写都就在sys文件系统中那个属性文件的操作了。

猜你喜欢

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