linux I2C驱动解析

I2C的设备ID,包含一个设备名字,和私有数据结构(目前没用到),属于i2c_driver;

include/linux/mod_devicetable.h
#define I2C_NAME_SIZE   20
#define I2C_MODULE_PREFIX "i2c:"
struct i2c_device_id {
        char name[I2C_NAME_SIZE];
        kernel_ulong_t driver_data      /* Data private to the driver */
                        __attribute__((aligned(sizeof(kernel_ulong_t))));
};

内核关于I2C_driver是这样注释的。为了完成设备自动检测,需要实现detectaddress_dataclass(这三项在i2c_detect中需要,不过我这板子上没有I2C设备,通过调试信息可以发现没有设置,直接return 0)driver.owner, driver.name(i2c_register_driver中需要)的设置。否则,将只创建强制使用模块参数的设备。

在完成detect函数的时候,最少需要填充i2c_board_info的name域。i2c_board_info结构体在成功detect后将被移交,可能还有flag域。

就算没有实现detect函数,驱动依然可以枚举I2C设备,但是不会支持设备。//红色是文章中出现并设置的变量

i2c_client结构体,是交给detect的回调信息,并不是真的i2c_client.完成足够的初始化,这样就可以调用i2c_smbus_read_byte_data。

include/linux/i2c.h
struct i2c_driver {
        unsigned int class; //I2C设备实例的种类(for detect)
        
        /* 标准驱动模型接口 */
        int (*probe)(struct i2c_client *, const struct i2c_device_id *);//设备和驱动match之后的回调函数
        int (*remove)(struct i2c_client *);//设备和驱动解绑的回调函数
        
        struct device_driver driver;//设备模型驱动,见设备驱动第十四章设备模型
        const struct i2c_device_id *id_table;//I2C设备列表support

        /* 设备检测回调,用于自动创建设备 */
        int (*detect)(struct i2c_client *, struct i2c_board_info *);//设备探测回调函数
        const unsigned short *address_list;//The I2C addresses to probe (for detect)
        struct list_head clients;//List of detected clients we created (for i2c-core use only)

        /* 与枚举设备无关的驱动模型接口  */
        void (*shutdown)(struct i2c_client *);//设备关机回调函数
        int (*suspend)(struct i2c_client *, pm_message_t mesg);//设备挂起回调
        int (*resume)(struct i2c_client *);//设备恢复回调
        
        int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);//类似于ioctl
        
        
};

 i2c_driver的实现,完成了标准模型接口函数probe、remove。配置了i2c_device_id、和device_driver的name配置。

arch/arm/mach-davinci/board-da850-sdi.c
typedef struct cdce_reg {
         u8      addr;
         u8      val;
} cdce_reg; //创建地址->数值键值对,服务probe
  
static int cdce_probe(struct i2c_client *client,
                        const struct i2c_device_id *id)
{
        int ret = 0;
        int i;
        struct cdce_reg regData[] = {
            { 0x03, 0x09 },
            { 0x14, 0x6d },
            { 0x18, 0xc0 },
            { 0x19, 0x04 },
            { 0x1A, 0x82 },
            { 0x1B, 0x07 },
        };
        for (i=0; i < sizeof(regData) / (sizeof(cdce_reg)); i++) {
            ret = i2c_smbus_write_byte_data(client,
                                            regData[i].addr | 0x80, regData[i].val);
            if (ret) { 
                printk (KERN_WARNING "Failed to write to CDCE13 register 0x%02x.", regData[i].addr);
                return ret;
            }
        }
        return ret;
 }//match完成,配置cdce913
 
static int __devexit cdce_remove(struct i2c_client *client)
{
        return 0;
}
 
static const struct i2c_device_id cdce_id[] = {
        { "cdce913", 0 },//设置i2c的名字,和私有结构数据
        { }
};

static struct i2c_driver cdce_driver = {
        .driver = {
                .name   = "cdce913",  //设置了注释需要的driver.name
        },
        .probe          = cdce_probe,
        .remove         = cdce_remove,
        .id_table       = cdce_id,
};

static __init void da850_evm_init(void)
{  
        i2c_add_driver(&cdce_driver);
}

 i2c_add_driver,这样就把I2C驱动注册进系统,完成I2C驱动的初始化。下面看深层次的i2c_add_drvier做了什么工作

include/linux/i2c.h
/* use a define to avoid include chaining to get THIS_MODULE */
#define i2c_add_driver(driver) \
        i2c_register_driver(THIS_MODULE, driver)
drivers/i2c/i2c-core.c
struct bus_type i2c_bus_type = {
         .name           = "i2c",
         .match          = i2c_device_match,
         .probe          = i2c_device_probe,
         .remove         = i2c_device_remove,
         .shutdown       = i2c_device_shutdown,
         .pm             = &i2c_device_pm_ops,
 };
drivers/i2c/i2c-core.c
int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
{
        /*设备模型注册见设备驱动程序第十四章,设备模型*/
        /*i2c_driver 与 1个或多个i2c_client节点用于访问i2c slave芯片*/
        int res;
        driver->driver.owner = owner; //在这里设置了i2c_driver 注释里面要求设置drvier的变量
        driver->driver.bus = &i2c_bus_type;
        res = driver_register(&driver->driver);
        if (res)
                return res;
        INIT_LIST_HEAD(&driver->clients);

        /* 遍历已存在的适配器 */
        i2c_for_each_dev(driver, __process_new_driver);
        return 0;
}
EXPORT_SYMBOL(i2c_register_driver);
drivers/i2c/i2c-core.c
int i2c_for_each_dev(void *data, int (*fn)(struct device *, void *))
{
        int res;
        mutex_lock(&core_lock);
        res = bus_for_each_dev(&i2c_bus_type, NULL, data, fn);
        mutex_unlock(&core_lock);
        return res;
}
EXPORT_SYMBOL_GPL(i2c_for_each_dev);
drivers/base/bus.c

int bus_for_each_dev(struct bus_type *bus, struct device *start,
                     void *data, int (*fn)(struct device *, void *))
{
        struct klist_iter i;
        struct device *dev;
        int error = 0;
        /*构建了一个以i为迭代器,klist_devices内核设备链表为头,插入start节点的klist*/
        klist_iter_init_node(&bus->p->klist_devices, &i,
                             (start ? &start->p->knode_bus : NULL));
        /*data是i2c_driver, fn是__process_new_driver
         *遍历与总线关联的设备列表,关于klist_devices可见文章最后结构体*/
        while ((dev = next_device(&i)) && !error) 
                error = fn(dev, data);
        klist_iter_exit(&i);
        return error;
}
EXPORT_SYMBOL_GPL(bus_for_each_dev);
drivers/i2c/i2c-core.c
static int __process_new_driver(struct device *dev, void *data)
{
        /*i2c添加适配器, data是驱动,通过设备dev找到适配器adapter*/
        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);
        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;

        /* Stop here if the classes do not match */
        if (!(adapter->class & driver->class))
                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;

        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 (unlikely(err))
                        break;
        }

        kfree(temp_client);
        return err;
}

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;

        /* 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
}
include/linux/device.h 
/**
 * struct bus_type - The bus type of the device
 *
 * @name:       The name of the bus.
 * @dev_name:   Used for subsystems to enumerate devices like ("foo%u", dev->id).
 * @dev_root:   Default device to use as the parent.
 * @bus_attrs:  Default attributes of the bus.
 * @dev_attrs:  Default attributes of the devices on the bus.
 * @drv_attrs:  Default attributes of the device drivers on the bus.
 * @match:      Called, perhaps multiple times, whenever a new device or driver
 *              is added for this bus. It should return a nonzero value if the
 *              given device can be handled by the given driver.
 * @uevent:     Called when a device is added, removed, or a few other things
 *              that generate uevents to add the environment variables.
 * @probe:      Called when a new device or driver add to this bus, and callback
 *              the specific driver's probe to initial the matched device.
 * @remove:     Called when a device removed from this bus.
 * @shutdown:   Called at shut-down time to quiesce the device.
 * @suspend:    Called when a device on this bus wants to go to sleep mode.
 * @resume:     Called to bring a device on this bus out of sleep mode.
 * @pm:         Power management operations of this bus, callback the specific
 *              device driver's pm-ops.
 * @iommu_ops:  IOMMU specific operations for this bus, used to attach IOMMU
 *              driver implementations to a bus and allow the driver to do
 *              bus-specific setup
 * @p:          The private data of the driver core, only the driver core can
 *              touch this.
 *
 * A bus is a channel between the processor and one or more devices. For the
 * purposes of the device model, all devices are connected via a bus, even if
 * it is an internal, virtual, "platform" bus. Buses can plug into each other.
 * A USB controller is usually a PCI device, for example. The device model
 * represents the actual connections between buses and the devices they control.
 * A bus is represented by the bus_type structure. It contains the name, the
 * default attributes, the bus' methods, PM operations, and the driver core's
 * private data.
 */
struct bus_type {
        const char              *name;
        const char              *dev_name;
        struct device           *dev_root;
        struct bus_attribute    *bus_attrs;
        struct device_attribute *dev_attrs;
        struct driver_attribute *drv_attrs;

        int (*match)(struct device *dev, struct device_driver *drv);
        int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
        int (*probe)(struct device *dev);
        int (*remove)(struct device *dev);
        void (*shutdown)(struct device *dev);

        int (*suspend)(struct device *dev, pm_message_t state);
        int (*resume)(struct device *dev);

        const struct dev_pm_ops *pm;

        struct iommu_ops *iommu_ops;

        struct subsys_private *p;
};

drivers/base/base.h 
/**
 * struct subsys_private - structure to hold the private to the driver core portions of the bus_type/class structure.
 *
 * @subsys - the struct kset that defines this subsystem
 * @devices_kset - the subsystem's 'devices' directory
 * @interfaces - list of subsystem interfaces associated
 * @mutex - protect the devices, and interfaces lists.
 *
 * @drivers_kset - the list of drivers associated
 * @klist_devices - the klist to iterate over the @devices_kset
 * @klist_drivers - the klist to iterate over the @drivers_kset
 * @bus_notifier - the bus notifier list for anything that cares about things
 *                 on this bus.
 * @bus - pointer back to the struct bus_type that this structure is associated
 *        with.
 *
 * @glue_dirs - "glue" directory to put in-between the parent device to
 *              avoid namespace conflicts
 * @class - pointer back to the struct class that this structure is associated
 *          with.
 *
 * This structure is the one that is the actual kobject allowing struct
 * bus_type/class to be statically allocated safely.  Nothing outside of the
 * driver core should ever touch these fields.
 */
struct subsys_private {
        struct kset subsys;
        struct kset *devices_kset;
        struct list_head interfaces;
        struct mutex mutex;

        struct kset *drivers_kset;
        struct klist klist_devices;
        struct klist klist_drivers;
        struct blocking_notifier_head bus_notifier;
        unsigned int drivers_autoprobe:1;
        struct bus_type *bus;

        struct kset glue_dirs;
        struct class *class;
};
201 include/linux/i2c.h
/**
202  * struct i2c_client - represent an I2C slave device
203  * @flags: I2C_CLIENT_TEN indicates the device uses a ten bit chip address;
204  *      I2C_CLIENT_PEC indicates it uses SMBus Packet Error Checking
205  * @addr: Address used on the I2C bus connected to the parent adapter.
206  * @name: Indicates the type of the device, usually a chip name that's
207  *      generic enough to hide second-sourcing and compatible revisions.
208  * @adapter: manages the bus segment hosting this I2C device
209  * @driver: device's driver, hence pointer to access routines
210  * @dev: Driver model device node for the slave.
211  * @irq: indicates the IRQ generated by this device (if any)
212  * @detected: member of an i2c_driver.clients list or i2c-core's
213  *      userspace_devices list
214  *
215  * An i2c_client identifies a single device (i.e. chip) connected to an
216  * i2c bus. The behaviour exposed to Linux is defined by the driver
217  * managing the device.
218  */
219 struct i2c_client {
220         unsigned short flags;           /* div., see below              */
221         unsigned short addr;            /* chip address - NOTE: 7bit    */
222                                         /* addresses are stored in the  */
223                                         /* _LOWER_ 7 bits               */
224         char name[I2C_NAME_SIZE];
225         struct i2c_adapter *adapter;    /* the adapter we sit on        */
226         struct i2c_driver *driver;      /* and our access routines      */
227         struct device dev;              /* the device structure         */
228         int irq;                        /* irq issued by device         */
229         struct list_head detected;
230 };

/*
 * i2c_adapter is the structure used to identify a physical i2c bus along
 * with the access algorithms necessary to access it.
 */
struct i2c_adapter {
        struct module *owner;
        unsigned int class;               /* classes to allow probing for */
        const struct i2c_algorithm *algo; /* the algorithm to access the bus */
        void *algo_data;

        /* data fields that are valid for all devices   */
        struct rt_mutex bus_lock;

        int timeout;                    /* in jiffies */
        int retries;
        struct device dev;              /* the adapter device */

        int nr;
        char name[48];
        struct completion dev_released;

        struct mutex userspace_clients_lock;
        struct list_head userspace_clients;
};


drivers/base/driver.c
214 /**
215  * driver_register - register driver with bus
216  * @drv: driver to register
217  *
218  * We pass off most of the work to the bus_add_driver() call,
219  * since most of the things we have to do deal with the bus
220  * structures.
221  */
222 int driver_register(struct device_driver *drv)
223 {
224         int ret;
225         struct device_driver *other;
226 
235         other = driver_find(drv->name, drv->bus);
236         if (other) {
237                 put_driver(other);
238                 printk(KERN_ERR "Error: Driver '%s' is already registered, "
239                         "aborting...\n", drv->name);
240                 return -EBUSY;
241         }
242 
243         ret = bus_add_driver(drv);
244         if (ret)
245                 return ret;
246         ret = driver_add_groups(drv, drv->groups);
247         if (ret)
248                 bus_remove_driver(drv);
249         return ret;
250 }
251 EXPORT_SYMBOL_GPL(driver_register);

 

 

 

 

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值