设备驱动层:专门针对连接在芯片外部硬件设备而写的代码,IIC总线上可以连接多个硬件设备,与设备驱动一一对应。在应用程序中打开设备驱动程序,就可以操作相关的硬件设备。所以对硬件设备的操作的功能(算法)在这里实现,比如对AT24C08这个EEPROM设备进行所需要的读写方式实现。
实现I2C设备client的两种方法(我认为第二种比较好,因为client在加载驱动时自动生成,减少了在attach_adapter(probe)函数中的操作):
1、int (*attach_adapter)(struct i2c_adapter *); + int (*detach_adapter)(struct i2c_adapter *);
2、int (*probe)(struct i2c_client *, const struct i2c_device_id *); + int (*remove)(struct i2c_client *);
第一种方法需要实现client的创建,i2c_add_driver(&pca953x_driver);调用attach_adapter,
attach_adapter调用核心层提供的i2c_probe(adapter, &addr_data, pca953x_detect);
i2c_add_driver——>attach_adapter——>i2c_probe对每一个适配器进行设备探测,成功则调用pca953x_detect函数
————>pca953x_detect函数
————————>在这个函数里面实现client创建
实现client创建的过程:
// 判断适配器是否支持某个功能,这个重要
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA| I2C_FUNC_SMBUS_WORD_DATA))//判定适配器能力
goto exit;
//分配内存空间
if (!(chip = kzalloc(sizeof(struct pca953x_chip), GFP_KERNEL))) {
err = -ENOMEM;
goto exit;
}
//开始构建client
/****构建i2c-client****/
chip->client=kzalloc(sizeof(struct i2c_client),GFP_KERNEL);
new_client = chip->client;
i2c_set_clientdata(new_client, chip);
new_client->addr = address;
new_client->adapter = adapter;
new_client->driver = &pca953x_driver;
new_client->flags = 0;
strlcpy(new_client->name, "pca953x", I2C_NAME_SIZE);
if ((err = i2c_attach_client(new_client)))//注册i2c_client
goto exit_kfree;
if (err)
goto exit_detach;
第二种方法中实现方法是使用probe探测函数,属于新的一种方法,在i2c_add_driver被调用后,函数执行设备驱动设备的探测,此时需要设备已经存在,这个就要在硬件初始时就要把设备加入,用到了一下代码:
static struct i2c_board_info __initdata test_i2c_devices[] = {
{
I2C_BOARD_INFO("pca9555", 0x27),//pca9555为芯片名称,0x27为芯片地址
.platform_data = &pca9555_data,
}, {
I2C_BOARD_INFO("mt9v022", 0x48),
.platform_data = &iclink[0], /* With extender */
}, {
I2C_BOARD_INFO("mt9m001", 0x5d),
.platform_data = &iclink[0], /* With extender */
},
};
i2c_register_board_info(0, test_i2c_devices,ARRAY_SIZE(test_i2c_devices)); //注册
i2c_client就是在注册过程中构建的。但有一点需要注意的是i2c_register_board_info并没有EXPORT_SYMBOL给模块使用。
i2c_register_board_info实现了一个设备的添加,同时该设备会被挂载到总线上面,提供给驱动用于匹配。
I2C_BOARD_INFO宏实现了设备名字type和地址addr的赋值,把两个内容归到这个宏里面实现。
platform_data 私有数据,由client传递,可以在client被传递的地方使用,根据私有数据传递进来的数据,在probe中可以对设备进行对应的操作。
采用第二种方法实现I2C设备驱动需要的两个步骤:
1、在板级文件中利用i2c_register_board_info实现一个I2C设备挂载到I2C总线上。
涉及到的结构体有:
/**
* 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 "new style" I2C drivers. 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;
int irq;
};
/**
* 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;
mutex_lock(&__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);
}
mutex_unlock(&__i2c_board_lock);
return status;
}
以上内容实现了一个I2C设备的加载
2、实现设备驱动
由于设备驱动应该对应的是多个I2C设备,不应该只是一个,所以应该使用id_table进行匹配。
i2c_add_driver中利用id_table进行设备驱动和设备的匹配,匹配成功就会调用probe函数,probe被调用就说明驱动driver和client已经联系在一起,在构建client时,client也已经和adapter联系在一起了,这时驱动driver通过client,就可以访问到adapter,进而使用到相应的通信函数。此时probe需要实现匹配成功后的操作,比如字符设备的注册。
i2c_del_driver会调用remove,remove里面操作和probe相反。
i2c_del_driver在模块退出函数被调用。
● 注册i2c_driver
static int __init pca953x_init(void)
{
return i2c_add_driver(&pca953x_driver);
}
module_init(pca953x_init);
● 注销i2c_driver
static void __exit pca953x_exit(void)
{
i2c_del_driver(&pca953x_driver);
}
module_exit(pca953x_exit);
在Probe方式下,注销字符驱动的位置在pca953x_remove中。
static int __devinit pca953x_remove (struct i2c_client *client)
{
……
/****字符设备驱动注销的位置****/
……
return 0;
}
参考:https://blog.csdn.net/zyboy2000/article/details/52136462
结构体:struct i2c_driver :是设备驱动中一个重要的结构体。
struct i2c_driver {
struct module *owner; //HJ_add
char name[32]; //HJ_add
int id;
unsigned int class;
unsigned int flags; //HJ_add
/* Notifies the driver that a new bus has appeared. This routine
* can be used by the driver to test if the bus meets its conditions
* & seek for the presence of the chip(s) it supports. If found, it
* registers the client(s) that are on the bus to the i2c admin. via
* i2c_attach_client. (LEGACY I2C DRIVERS ONLY)
*/
int (*attach_adapter)(struct i2c_adapter *);
int (*detach_adapter)(struct i2c_adapter *);
/* tells the driver that a client is about to be deleted & gives it
* the chance to remove its private data. Also, if the client struct
* has been dynamically allocated by the driver in the function above,
* it must be freed here. (LEGACY I2C DRIVERS ONLY)
*/
int (*detach_client)(struct i2c_client *) __deprecated;
/* Standard driver model interfaces, for "new style" i2c drivers.
* With the driver model, device enumeration is NEVER done by drivers;
* it's done by infrastructure. (NEW STYLE DRIVERS ONLY)
*/
int (*probe)(struct i2c_client *, const struct i2c_device_id *);
int (*remove)(struct i2c_client *);
/* driver model interfaces that don't relate to enumeration */
void (*shutdown)(struct i2c_client *);
int (*suspend)(struct i2c_client *, pm_message_t mesg);
int (*resume)(struct i2c_client *);
/* a ioctl like command that can be used to perform specific functions
* with the device.
*/
int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);
struct device_driver driver;
const struct i2c_device_id *id_table;
/* Device detection callback for automatic device creation */
int (*detect)(struct i2c_client *, int kind, struct i2c_board_info *);
const struct i2c_client_address_data *address_data;
struct list_head clients;
};
在驱动的入口函数中,调用I2C的核心层函数:
static inline int i2c_add_driver(struct i2c_driver *driver)
{
return i2c_register_driver(THIS_MODULE, driver);
}
i2c_register_driver
——driver_register
————bus_add_driver
——————driver_attach
————————bus_for_each_dev
——————————__driver_attach
static int __driver_attach(struct device *dev, void *data)
{
struct device_driver *drv = data;
/*
* Lock device and try to bind to it. We drop the error
* here and always return 0, because we need to keep trying
* to bind to devices and some drivers will return an error
* simply if it didn't support the device.
*
* driver_probe_device() will spit a warning if there
* is an error.
*/
if (!driver_match_device(drv, dev))
return 0;
if (dev->parent) /* Needed for USB */
down(&dev->parent->sem);
down(&dev->sem);
if (!dev->driver)
driver_probe_device(drv, dev);
up(&dev->sem);
if (dev->parent)
up(&dev->parent->sem);
return 0;
}
————————————driver_match_device //match就是driver_match_device
driver_match_device 函数如果匹配到返回真,表示匹配到,或者不存在这个函数。driver_probe_device接着执行
/**
* driver_probe_device - attempt to bind device & driver together
* @drv: driver to bind a device to
* @dev: device to try to bind to the driver
*
* This function returns -ENODEV if the device is not registered,
* 1 if the device is bound sucessfully and 0 otherwise.
*
* This function must be called with @dev->sem held. When called for a
* USB interface, @dev->parent->sem must be held as well.
*/
int driver_probe_device(struct device_driver *drv, struct device *dev)
{
int ret = 0;
if (!device_is_registered(dev))
return -ENODEV;
pr_debug("bus: '%s': %s: matched device %s with driver %s\n",
drv->bus->name, __func__, dev_name(dev), drv->name);
ret = really_probe(dev, drv);
return ret;
}
static inline int driver_match_device(struct device_driver *drv,
struct device *dev)
{
return drv->bus->match ? drv->bus->match(dev, drv) : 1;
}
——————————————drv->bus->match(dev, drv) 就是i2c_device_match函数,属于总线
static int i2c_device_match(struct device *dev, struct device_driver *drv)
{
struct i2c_client *client = to_i2c_client(dev);
struct i2c_driver *driver = to_i2c_driver(drv);
/* make legacy i2c drivers bypass driver model probing entirely;
* such drivers scan each i2c adapter/bus themselves.
*/
if (!is_newstyle_driver(driver))
return 0;
/* match on an id table if there is one */
if (driver->id_table)
return i2c_match_id(driver->id_table, client) != NULL;
return 0;
}
static const struct i2c_device_id *i2c_match_id(const struct i2c_device_id *id,
const struct i2c_client *client)
{
while (id->name[0]) {
if (strcmp(client->name, id->name) == 0)
return id;
id++;
}
return NULL;
}
执行接下来的步骤是在上一步进行设备与驱动匹配成功后的连续,主要执行probe函数,以及对设备的一些初始化过程(因为匹配到驱动(或设备)后,而者之间需要互相包含,才能说明建立了联系,设备的信息给驱动,驱动的信息给设备)
——————————————driver_probe_device(上面一段也涉及到这个的解释)
int driver_probe_device(struct device_driver *drv, struct device *dev)
{
int ret = 0;
if (!device_is_registered(dev))
return -ENODEV;
pr_debug("bus: '%s': %s: matched device %s with driver %s\n",
drv->bus->name, __func__, dev_name(dev), drv->name);
ret = really_probe(dev, drv);
return ret;
}
————————————————really_probe
static int really_probe(struct device *dev, struct device_driver *drv)
{
int ret = 0;
atomic_inc(&probe_count);
pr_debug("bus: '%s': %s: probing driver %s with device %s\n",
drv->bus->name, __func__, drv->name, dev_name(dev));
WARN_ON(!list_empty(&dev->devres_head));
dev->driver = drv;
if (driver_sysfs_add(dev)) {
printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n",
__func__, dev_name(dev));
goto probe_failed;
}
if (dev->bus->probe) {
ret = dev->bus->probe(dev);
if (ret)
goto probe_failed;
} else if (drv->probe) {
ret = drv->probe(dev);
if (ret)
goto probe_failed;
}
driver_bound(dev);
ret = 1;
pr_debug("bus: '%s': %s: bound device %s to driver %s\n",
drv->bus->name, __func__, dev_name(dev), drv->name);
goto done;
probe_failed:
devres_release_all(dev);
driver_sysfs_remove(dev);
dev->driver = NULL;
if (ret != -ENODEV && ret != -ENXIO) {
/* driver matched but the probe failed */
printk(KERN_WARNING
"%s: probe of %s failed with error %d\n",
drv->name, dev_name(dev), ret);
}
/*
* Ignore errors returned by ->probe so that the next driver can try
* its luck.
*/
ret = 0;
done:
atomic_dec(&probe_count);
wake_up(&probe_waitqueue);
return ret;
}
if (dev->bus->probe) {
ret = dev->bus->probe(dev);
if (ret)
goto probe_failed;
} else if (drv->probe) {
ret = drv->probe(dev);
if (ret)
goto probe_failed;
}
——————————————————————dev->bus->probe(dev); 就是i2c_device_probe函数
static int i2c_device_probe(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct i2c_driver *driver = to_i2c_driver(dev->driver);
int status;
if (!driver->probe || !driver->id_table)
return -ENODEV;
client->driver = driver;
if (!device_can_wakeup(&client->dev))
device_init_wakeup(&client->dev,
client->flags & I2C_CLIENT_WAKE);
dev_dbg(dev, "probe\n");
status = driver->probe(client, i2c_match_id(driver->id_table, client));
if (status)
client->driver = NULL;
return status;
}
最终执行了i2c_driver的probe函数status = driver->probe(client, i2c_match_id(driver->id_table, client));
调用结束(如果总线没有probe,就会调用驱动的probe,如果总线有probe,就会调用总线probe,在总线probe里面调用驱动的probe)
来将struct i2c_driver结构体加入链表中。其中i2c_register_driver是真正发挥作用的函数,在这里进行i2c_driver 与i2c_adapter的匹配,struct i2c_device_id *id_table;作为匹配的第一条件,如果没有,则拿名字来进行匹配。匹配成功后,就调用probe函数执行。在这之前的过程是在内核中自动执行到结束的,probe函数中的内容需要写匹配成功后要可以进行的下一步操作。比如生成设备节点。
struct i2c_adapter 、struct i2c_dev 、
核心函数API及其作用:
驱动的整体组成:
总线驱动层:专门唯一芯片上的IIC控制器所写的代码,他和单片机上实现IIC初始化、通讯设置之类的内容一样,就是对控制器实现驱动,发送接收数据。设备驱动层需要与总线驱动层匹配,调用设备驱动层时就会经过IIC的核心层和内核调用总线层函数,驱动IIC工作。
结构体:
核心函数API及其作用:
驱动的整体组成:
在Linux内核中,与设备与硬件无关的核心代码已经实现,我们需要做的就是调用这些接口,把设备、总线驱动挂到内核中。
总线驱动-----一般由芯片生产商提供
设备驱动-----需要根据硬件情况编写程序