i2c子系统 深入理解(一)

关于i2c子系统从以下几个问题开始

设备是如何通过vfs的接口调用到驱动的file_operation的

一句话简单来讲就是,统一设备模型是基于kobject和ktype的。而统一设备模型最重要的就是设备和驱动,struct device和struct device_driver driver;

struct device {  
...
struct kobject kobj;
...
}

其中设备的结构体中就包含了kobject
函数调用流程为:
vfs---->sysfs---->kobject---->attibute---->kobj_type---->sysfs_ops---->xxx_attribute,

参考蜗窝科技写的文章,写的非常好
http://www.wowotech.net/device_model/device_and_driver.html

struct i2c_driver {    
    int id;                  
    unsigned int class;      
                                      
    /* 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 *);    
      
    /* 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 *);    
    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;              
    struct list_head list;                                            
}; 
struct i2c_client {                                                     
    unsigned short flags;       /* div., see below      */           
    unsigned short addr;        /* chip address - NOTE: 7bit    */                              
                    /* addresses are stored in the  */
                    /* _LOWER_ 7 bits       */                     
    char name[I2C_NAME_SIZE];                                     
    struct i2c_adapter *adapter;    /* the adapter we sit on    */  
    struct i2c_driver *driver;  /* and our access routines  */
    int usage_count;        /* How many accesses currently  */       
                    /* to the client        */                       
    struct device dev;      /* the device structure     */     
    int irq;            /* irq issued by device (or -1) */                 
    char driver_name[KOBJ_NAME_LEN];                                           
    struct list_head list;                                                
    struct completion released;                                          
}; 

可以看到i2c子系统最重要的,struct i2c_driver和struct i2c_client就是对struct device和struct device_driver driver进行了封装,增加了和i2c有关的信息。

i2c子系统和字符设备有什么关系

分析一下代码可以看出,i2c子系统的核心还是调用register_chrdev,字符设备的接口,也就是说,各种子系统都是建立在字符设备之上的,如果足够强的话,可以绕过子系统,自己直接调用字符设备的接口实现i2c的框架,不过没啥必要。

static struct i2c_driver i2cdev_driver = {                                                      
    .driver = {                                                                                 
        .name   = "dev_driver",                                                                 
    },                                                                                          
    .id     = I2C_DRIVERID_I2CDEV,                                                              
    .attach_adapter = i2cdev_attach_adapter,                                                    
    .detach_adapter = i2cdev_detach_adapter,                                                    
    .detach_client  = i2cdev_detach_client,                                                     
};                                                                                              
                                                                                                
static int __init i2c_dev_init(void)                                                            
{                                                                                               
    int res;                                                                                    
                                                                                                
    printk(KERN_INFO "i2c /dev entries driver\n");                                              
                                                                                                
    res = register_chrdev(I2C_MAJOR, "i2c", &i2cdev_fops);                                      
    if (res)                                                                                    
        goto out;                                                                               
                                                                                                
    i2c_dev_class = class_create(THIS_MODULE, "i2c-dev");                                       
    if (IS_ERR(i2c_dev_class))                                                                  
        goto out_unreg_chrdev;                                                                  
                                                                                                
    res = i2c_add_driver(&i2cdev_driver);                                                       
    if (res)                                                                                    
        goto out_unreg_class;                                                                   
                                                                                                
    return 0;                                                                                   
                                                                                                
out_unreg_class:                                                                                
    class_destroy(i2c_dev_class);                                                               
out_unreg_chrdev:                                                                               
    unregister_chrdev(I2C_MAJOR, "i2c");                                                        
out:                                                                                            
    printk(KERN_ERR "%s: Driver Initialisation failed\n", __FILE__);                            
    return res;                                                                                 
} 

i2c 子系统和platform有什么区别

platform和i2c子系统最大的实现区别就在于,没有struct i2c_adapter *adapter; 因为它是一种虚拟总线,没有特定总线的时序算法,所以就简单很多。所以驱动直接调用probe函数,而不用进行探测。

struct platform_device {                                                                        
    const char  * name;                                                                         
    u32     id;                                                                                 
    struct device   dev;                                                                        
    u32     num_resources;                                                                      
    struct resource * resource;                                                                 
}; 
struct platform_driver {                                                                        
    int (*probe)(struct platform_device *);                                                     
    int (*remove)(struct platform_device *);                                                    
    void (*shutdown)(struct platform_device *);                                                 
    int (*suspend)(struct platform_device *, pm_message_t state);                               
    int (*suspend_late)(struct platform_device *, pm_message_t state);                          
    int (*resume_early)(struct platform_device *);                                              
    int (*resume)(struct platform_device *);                                                    
    struct device_driver driver;                                                                
};

i2c 子系统的算法是怎样实现的

以at24c的驱动作为讲解:

  1. 很明显,这最重要就是,at24cxx_attach这个函数,这个就是i2c子系统,最先调用的函数
static struct file_operations at24cxx_fops = {    
   .owner = THIS_MODULE,    
   .read  = at24cxx_read,                                   
   .write = at24cxx_write,                                                          
};                                                                                                                             
static int at24cxx_detect(struct i2c_adapter *adapter, int address, int kind)    
{                                                                            
   printk("at24cxx_detect\n");                           
                                           
   /* 构构一个i2c_client结构体: 以后收改数据时会用到它 */    
   at24cxx_client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);                                at24cxx_client->addr    = address;    
   at24cxx_client->adapter = adapter;    
   at24cxx_client->driver  = &at24cxx_driver;    
   strcpy(at24cxx_client->name, "at24cxx");              
   i2c_attach_client(at24cxx_client);    
       
   major = register_chrdev(0, "at24cxx", &at24cxx_fops);    
   
   cls = class_create(THIS_MODULE, "at24cxx");    
   class_device_create(cls, NULL, MKDEV(major, 0), NULL, "at24cxx"); /* /dev/at24cxx */    
                            
   return 0;                         
}    
static int at24cxx_attach(struct i2c_adapter *adapter)    
{    
   return i2c_probe(adapter, &addr_data, at24cxx_detect);    
} 
static struct i2c_driver at24cxx_driver = {               
   .driver = {    
       .name   = "at24cxx",    
   },                                                
   .attach_adapter = at24cxx_attach,                   
   .detach_client  = at24cxx_detach,                          
};
module_init(at24cxx_init);
  1. 整理代码发现最重要的就是调用i2c_probe_address
int i2c_probe(struct i2c_adapter *adapter,
         struct i2c_client_address_data *address_data,
         int (*found_proc) (struct i2c_adapter *, int, int))
{
...
       err = i2c_probe_address(adapter, address_data->normal_i2c[i],
                   -1, found_proc);

...
}

根据名字可以判断,是需要地址的,以我的经验估计就是,读取i2c设备的地址,查看是否和写入的地址吻合,判断是否有设备。那么地址肯定要传递进去,回头查看传入参数

static struct i2c_client_address_data addr_data = {    
   .normal_i2c = normal_addr,  /* 要发出S信号和设备地址并得到ACK信号,才能确定存在这个设备 */       .probe      = ignore,
   .ignore     = ignore,    
   //.forces     = forces, /* 强制认为存在这个设备 */
}; 
static struct i2c_client_address_data addr_data = {    
   .normal_i2c = normal_addr,  /* 要发出S信号和设备地址并得到ACK信号,才能确定存在这个设备 */       .probe      = ignore,
   .ignore     = ignore,    
   //.forces     = forces, /* 强制认为存在这个设备 */
}; 

刚好发现i2c_probe(adapter, &addr_data, at24cxx_detect); 通过这里将地址传递进去啦。
接下来就看i2c_probe_address是如何实现的。

  1. 代码比较清晰,主要是判断改地址是否已经被别的设备使用,没有使用的话才会通过i2c_smbus_xfer去验证地址。如果验证通过,则会调用err = found_proc(adapter, addr, kind); 那么这个found_proc是哪里来的呢
static int i2c_probe_address(struct i2c_adapter *adapter, int addr, int kind,                   
                 int (*found_proc) (struct i2c_adapter *, int, int))                            
{                                                                                               
    int err;                                                                                    
                                                                                                
    /* Make sure the address is valid */                                                        
    if (addr < 0x03 || addr > 0x77) {                                                           
        dev_warn(&adapter->dev, "Invalid probe address 0x%02x\n",                               
             addr);                                                                             
        return -EINVAL;                                                                         
    }                                                                                           
                                                                                                
    /* Skip if already in use */                                                                
    if (i2c_check_addr(adapter, addr))                                                          
        return 0;                                                                               
                                                                                                
    /* Make sure there is something at this address, unless forced */                           
    if (kind < 0) {                                                                             
        if (i2c_smbus_xfer(adapter, addr, 0, 0, 0,                                              
                   I2C_SMBUS_QUICK, NULL) < 0)                                                  
            return 0;                                                                           
                                                                                                
        /* prevent 24RF08 corruption */                                                         
        if ((addr & ~0x0f) == 0x50)                                                             
            i2c_smbus_xfer(adapter, addr, 0, 0, 0,                                              
                       I2C_SMBUS_QUICK, NULL);                                                  
    }                                                                                           
                                                                                                
    /* Finally call the custom detection function */                                            
    err = found_proc(adapter, addr, kind);                                                      
    /* -ENODEV can be returned if there is a chip at the given address                          
       but it isn't supported by this chip driver. We catch it here as                          
       this isn't an error. */                                                                  
    if (err == -ENODEV)                                                                                 err = 0;                                                                                
                                                                                                    if (err)                                                                                    
        dev_warn(&adapter->dev, "Client creation failed at 0x%x (%d)\n",                        
             addr, err);                                                                        
    return err;                                                                                 
}
继续回头看i2c_probe(adapter, &addr_data, at24cxx_detect); 发现就是at24cxx_detect,因此只有在检测到设备时,才会调用at24cxx_detect。那么我们继续看i2c_smbus_xfer的实现。
  
  1. 到这里发现,就是调用smbus_xfer,或者是i2c_smbus_xfer_emulated
s32 i2c_smbus_xfer(struct i2c_adapter * adapter, u16 addr, unsigned short flags,                
                   char read_write, u8 command, int size,                                       
                   union i2c_smbus_data * data)                                                 
{                                                                                                   s32 res;                                                                                    
                                                                                                    flags &= I2C_M_TEN | I2C_CLIENT_PEC;                                                        
                                                                                                
    if (adapter->algo->smbus_xfer) {                                                            
        mutex_lock(&adapter->bus_lock);                                                         
        res = adapter->algo->smbus_xfer(adapter,addr,flags,read_write,                          
                                        command,size,data);                                     
        mutex_unlock(&adapter->bus_lock);                                                       
    } else                                                                                      
        res = i2c_smbus_xfer_emulated(adapter,addr,flags,read_write,
                                          command,size,data);
               
    return res;                           
}

那么在这里我们首先必须要搜索,smbus_xfer是在哪里复制的。查找和struct i2c_adapter 相关赋值的内容发现都在busses目录下,以i2c-at91.c为例。

static struct i2c_algorithm at91_algorithm = {
    .master_xfer    = at91_xfer,
    .functionality  = at91_func,
};

static int __devinit at91_i2c_probe(struct platform_device *pdev)                               
{                                                                                               
    struct i2c_adapter *adapter;                                                                
    struct resource *res;                                                                       
    int rc;                                                                                     
                                                                                                
    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);                                       
    if (!res)                                                                                   
        return -ENXIO;                                                                          
                                                                                                
    if (!request_mem_region(res->start, res->end - res->start + 1, "at91_i2c"))                 
        return -EBUSY;                                                                          
                                                                                                
    twi_base = ioremap(res->start, res->end - res->start + 1);                                  
    if (!twi_base) {                                                                            
        rc = -ENOMEM;                                                                           
        goto fail0;                                                                             
    }                                                                                           
                                                                                                
    twi_clk = clk_get(NULL, "twi_clk");                                                         
    if (IS_ERR(twi_clk)) {                                                                      
        dev_err(&pdev->dev, "no clock defined\n");                                              
        rc = -ENODEV;                                                                           
        goto fail1;                                                                             
    }                                                                                           
                                                                                                
    adapter = kzalloc(sizeof(struct i2c_adapter), GFP_KERNEL);                                  
    if (adapter == NULL) {                                                                      
        dev_err(&pdev->dev, "can't allocate inteface!\n");                                      
        rc = -ENOMEM;                                                                           
        goto fail2;                                                                             
    }                                                                                           
    sprintf(adapter->name, "AT91");                                                             
    adapter->algo = &at91_algorithm;                                                            
    adapter->class = I2C_CLASS_HWMON;                                                           
    adapter->dev.parent = &pdev->dev;                                                           
    /* adapter->id == 0 ... only one TWI controller for now */                                  
                                                                                                
    platform_set_drvdata(pdev, adapter);                                                        
                                                                                                
    clk_enable(twi_clk);        /* enable peripheral clock */                                   
    at91_twi_hwinit();      /* initialize TWI controller */                                     
                                                                                                
    rc = i2c_add_numbered_adapter(adapter);                                                     
    if (rc) {                                                                                   
        dev_err(&pdev->dev, "Adapter %s registration failed\n",                                 
                adapter->name);                                                                 
        goto fail3;                                                                             
    }                                                                                           
                                                                                                
    dev_info(&pdev->dev, "AT91 i2c bus driver.\n");                                             
    return 0;                                                                                   
                                                                                                
fail3:                                                                                          
    platform_set_drvdata(pdev, NULL);                                                           
    kfree(adapter);                                                                             
    clk_disable(twi_clk);                                                                       
fail2:                                                                                          
    clk_put(twi_clk);                                                                           
fail1:                                                                                          
    iounmap(twi_base);                                                                          
fail0:                                                                                          
    release_mem_region(res->start, res->end - res->start + 1);                                  
                                                                                                
    return rc;                                                                                  
} 
static struct platform_driver at91_i2c_driver = {                                               
    .probe      = at91_i2c_probe,                                                               
    .remove     = __devexit_p(at91_i2c_remove),                                                 
    .suspend    = at91_i2c_suspend,                                                             
    .resume     = at91_i2c_resume,                                                              
    .driver     = {                                                                             
        .name   = "at91_i2c",                                                                   
        .owner  = THIS_MODULE,                                                                  
    },                                                                                          
}; 
static int __init at91_i2c_init(void)                                                           
{                                                                                               
    return platform_driver_register(&at91_i2c_driver);                                          
}                                                                                               
    
module_init(at91_i2c_init); 

只看其中的probe函数发现,就是通过adapter->algo = &at91_algorithm;和rc = i2c_add_numbered_adapter(adapter);将i2c外设的时序算法注册到i2c子系统中。但是这个demo很明显没有smbus_xfer函数所以应该是调用,i2c_smbus_xfer_emulated。那我们接下来解析i2c_smbus_xfer_emulated这个函数

  1. 经过简化,最终调用的是i2c_transfer
static s32 i2c_smbus_xfer_emulated(struct i2c_adapter * adapter, u16 addr,                      
                                   unsigned short flags,                                        
                                   char read_write, u8 command, int size,                       
                                   union i2c_smbus_data * data)                                 
{
···
    if (i2c_transfer(adapter, msg, num) < 0)                                                    
        return -1; 
···
}
  1. 这里就非常清晰啦,就是调用算法里的master_xfer函数去读地址的
int i2c_transfer(struct i2c_adapter * adap, struct i2c_msg *msgs, int num)                      
{                                                                                               
    int ret;                                                                                    
                                                                                                
    if (adap->algo->master_xfer) {                                                              
#ifdef DEBUG                                                                                    
        for (ret = 0; ret < num; ret++) {                                                       
            dev_dbg(&adap->dev, "master_xfer[%d] %c, addr=0x%02x, "                             
                "len=%d%s\n", ret, (msgs[ret].flags & I2C_M_RD)                                 
                ? 'R' : 'W', msgs[ret].addr, msgs[ret].len,                                     
                (msgs[ret].flags & I2C_M_RECV_LEN) ? "+" : "");                                 
        }                                                                                       
#endif                                                                                          
                                                                                                
        mutex_lock_nested(&adap->bus_lock, adap->level);                                        
        ret = adap->algo->master_xfer(adap,msgs,num);                                           
        mutex_unlock(&adap->bus_lock);                                                          
                                                                                                
        return ret;                                                                             
    } else {                                                                                    
        dev_dbg(&adap->dev, "I2C level transfers not supported\n");                             
        return -ENOSYS;                                                                         
    }                                                                                           
} 

而master_xfer就是在busses里面的众多算法里面定义的,回头看就是

static struct i2c_algorithm at91_algorithm = {
    .master_xfer    = at91_xfer,
    .functionality  = at91_func,
};
``
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值