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文件系统中那个属性文件的操作了。