I2C总线adapter驱动及设备client驱动分析总结

设备驱动层:专门针对连接在芯片外部硬件设备而写的代码,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内核中,与设备与硬件无关的核心代码已经实现,我们需要做的就是调用这些接口,把设备、总线驱动挂到内核中。
总线驱动-----一般由芯片生产商提供
设备驱动-----需要根据硬件情况编写程序

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值