i2c体系结构篇(dev与core层交互)

来源:互联网 发布:电脑加密软件 编辑:程序博客网 时间:2024/05/20 17:24
 [本文导读]:I2C总线作为系统总线,广泛用于e2prom,rtc等设备接口中。本文以mpc8309为架构,linux-2.26.34源码为例分析I2C总线的实现过程。

 1. I2c-dev.c驱动的结构

如上图所示,I2c-dev.c是实现I2c通用的slave端的通用驱动架构。i2cdev_fops是实现client的文件操作结构.下面首先看一下i2c_driver结构如下:

struct i2c_driver {

              unsigned int class;

              int (*attach_adapter)(struct i2c_adapter *);

              int (*detach_adapter)(struct i2c_adapter *);

              int (*probe)(struct i2c_client *, const struct i2c_device_id *);

              int (*remove)(struct i2c_client *);

              void (*shutdown)(struct i2c_client *);

              int (*suspend)(struct i2c_client *, pm_message_t mesg);

              int (*resume)(struct i2c_client *);

              void (*alert)(struct i2c_client *, unsigned int data);

              int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);

              struct device_driver driver;

              const struct i2c_device_id *id_table;

              int (*detect)(struct i2c_client *, struct i2c_board_info *);

              const unsigned short *address_list;

              struct list_head clients;

};

#define to_i2c_driver(d) container_of(d, struct i2c_driver, driver)

    从该驱动的数据结构来看,它是client与adapter之间的联系层.

 2.  设备驱动端的具体实现

     对于设备端,有client结构对其描述:

struct i2c_client {

              unsigned short flags;                                 /* div., see below                                */

              unsigned short addr;                                 /* chip address - NOTE: 7bit       */

              char name[I2C_NAME_SIZE];    /*                         */

              struct i2c_adapter *adapter;         /* the adapter we sit on        */

              struct i2c_driver *driver;                /* and our access routines      */

              struct device dev;                           /* the device structure                  */

              int irq;                                              /* irq issued by device           */

              struct list_head detected;

};

 2.1  open:

static int i2cdev_open(struct inode *inode, struct file *file)

{

              unsigned int minor = iminor(inode);  //获取设备的次设备号

              struct i2c_client *client;

              struct i2c_adapter *adap;

              struct i2c_dev *i2c_dev;

              i2c_dev = i2c_dev_get_by_minor(minor);

              if (!i2c_dev)                         return -ENODEV;

              adap = i2c_get_adapter(i2c_dev->adap->nr);

              if (!adap)                   return -ENODEV;

             client = kzalloc(sizeof(*client), GFP_KERNEL);

              if (!client) {

                             i2c_put_adapter(adap);

                             return -ENOMEM;

              }

              snprintf(client->name, I2C_NAME_SIZE, "i2c-dev %d", adap->nr);

              client->driver = &i2cdev_driver;

              client->adapter = adap;

              file->private_data = client;

              return 0;

}

Open函数通过次设备号(也就是总线的ID)获取adapter,创建client结构(也就是实际的dev设备)与adapter,driver相关联,并设置私有数据到file里。

 2.2 Ioctl:

static long i2cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)

{

              struct i2c_client *client = (struct i2c_client *)file->private_data;

              unsigned long funcs;

 

              dev_dbg(&client->adapter->dev, "ioctl, cmd=0x%02x, arg=0x%02lx\n",

                             cmd, arg);

              switch ( cmd ) {

              case I2C_SLAVE:

              case I2C_SLAVE_FORCE:

                             if ((arg > 0x3ff) ||

                                 (((client->flags & I2C_M_TEN) == 0) && arg > 0x7f))

                                           return -EINVAL;

                             if (cmd == I2C_SLAVE && i2cdev_check_addr(client->adapter, arg))

                                           return -EBUSY;

                             client->addr = arg;

                             return 0;

              case I2C_TENBIT:

                             if (arg)

                                           client->flags |= I2C_M_TEN;

                             else

                                           client->flags &= ~I2C_M_TEN;

                             return 0;

              case I2C_PEC:

                             if (arg)

                                           client->flags |= I2C_CLIENT_PEC;

                             else

                                           client->flags &= ~I2C_CLIENT_PEC;

                             return 0;

              case I2C_FUNCS:

                             funcs = i2c_get_functionality(client->adapter);

                             return put_user(funcs, (unsigned long __user *)arg);

              case I2C_RDWR:

                             return i2cdev_ioctl_rdrw(client, arg);

              case I2C_SMBUS:

                             return i2cdev_ioctl_smbus(client, arg);

              case I2C_RETRIES:

                             client->adapter->retries = arg;

                             break;

              case I2C_TIMEOUT:

                             client->adapter->timeout = msecs_to_jiffies(arg * 10);

                             break;

              default:

                             return -ENOTTY;

              }

              return 0;

}

  i2cdev_ioctl主要设置从设备地址,使能读写,超时和重入次数.这些在用户空间编程中首先要设置然后才能对从设备进行读写工作.好了,关于设备端的操作说到这里就差不多了。

 

3. client对adapter增添反应在用户空间方式

/* ------------------------------------------------------------------------- */

/*

 * The legacy "i2cdev_driver" is used primarily to get notifications when

 * I2C adapters are added or removed, so that each one gets an i2c_dev

 * and is thus made available to userspace driver code.

 */

static struct class *i2c_dev_class;

static int i2cdev_attach_adapter(struct i2c_adapter *adap)

{

              struct i2c_dev *i2c_dev;

              int res;

              i2c_dev = get_free_i2c_dev(adap);

              if (IS_ERR(i2c_dev))

                             return PTR_ERR(i2c_dev);

              /* register this i2c device with the driver core */

              i2c_dev->dev = device_create(i2c_dev_class, &adap->dev,

                                                              MKDEV(I2C_MAJOR, adap->nr), NULL,

                                                              "i2c-%d", adap->nr);

              if (IS_ERR(i2c_dev->dev)) {

                             res = PTR_ERR(i2c_dev->dev);

                             goto error;

              }

              res = device_create_file(i2c_dev->dev, &dev_attr_name);

              if (res)

                             goto error_destroy;

              pr_debug("i2c-dev: adapter [%s] registered as minor %d\n",

                              adap->name, adap->nr);

              return 0;

error_destroy:

              device_destroy(i2c_dev_class, MKDEV(I2C_MAJOR, adap->nr));

error:

              return_i2c_dev(i2c_dev);

              return res;

}

 

static int i2cdev_detach_adapter(struct i2c_adapter *adap)

{

              struct i2c_dev *i2c_dev;

              i2c_dev = i2c_dev_get_by_minor(adap->nr);

              if (!i2c_dev) /* attach_adapter must have failed */

                             return 0;

              device_remove_file(i2c_dev->dev, &dev_attr_name);

              return_i2c_dev(i2c_dev);

              device_destroy(i2c_dev_class, MKDEV(I2C_MAJOR, adap->nr));

              pr_debug("i2c-dev: adapter [%s] unregistered\n", adap->name);

              return 0;

}

    从这里可以看出,这两个函数主要增加设备节点的功能,使用udev文件系统,动态添加设备节点.因为这是dev角度来写的,所以不存在probe等函数,probe是总线驱动所实现的功能.

0 0