基于S3C2440的Linux-3.6.6移植——devfs文件系统的IIC应用

来源:互联网 发布:淘宝网鱼的十字绣 编辑:程序博客网 时间:2024/04/29 21:18

Linux-3.6.6提供了两种访问IIC的方法,一种是基于devfs的方法,另一种是基于sysfs的方法,文中介绍第一种方法,下一篇文章介绍第二种方法。

 

在arch/arm/plat-samsung/devs.c文件已经定义了IIC平台设备及其资源:

static struct resource s3c_i2c0_resource[]= {

       [0]= DEFINE_RES_MEM(S3C_PA_IIC,SZ_4K),

       [1] = DEFINE_RES_IRQ(IRQ_IIC),

};

 

struct platform_device s3c_device_i2c0 = {

       .name             = "s3c2410-i2c",

#ifdef CONFIG_S3C_DEV_I2C1

       .id          = 0,

#else

       .id          = -1,

#endif

       .num_resources      = ARRAY_SIZE(s3c_i2c0_resource),

       .resource = s3c_i2c0_resource,

};

 

并且通过arch/arm/mach-s3c24xx/mach-zhaocj2440.c文件中的zhaocj2440_init函数把IIC平台设备添加到了总线上。

 

在zhaocj2440_init函数中还有一条关于IIC的语句:

s3c_i2c0_set_platdata(NULL);

 

该语句在devs.c中被定义,作用是设置IIC的平台设备数据:

void __init s3c_i2c0_set_platdata(structs3c2410_platform_i2c *pd)

{

       structs3c2410_platform_i2c *npd;

 

       if(!pd) {

              pd= &default_i2c_data;

              pd->bus_num= 0;

       }

 

       //设置IIC平台设备的数据

       npd= s3c_set_platdata(pd,sizeof(struct s3c2410_platform_i2c),

                            &s3c_device_i2c0);

 

       //定义IIC的引脚——GPE14和GPE15

       if(!npd->cfg_gpio)

              npd->cfg_gpio= s3c_i2c0_cfg_gpio;

}

 

其中default_i2c_data定义为:

struct s3c2410_platform_i2c default_i2c_data__initdata = {

       .flags             =0,                      //标识

       .slave_addr     = 0x10,                 //从模式地址

       .frequency      = 100*1000,          //频率

       .sda_delay      = 100,                   //数据传输延时

};

 

我们再来看看IIC的平台驱动。在drivers/i2c/busses/i2c-s3c2410.c文件中,IIC子系统的初始化为:

staticint __init i2c_adap_s3c_init(void)

{

       return platform_driver_register(&s3c24xx_i2c_driver);

}

 

s3c24xx_i2c_driver为IIC的平台驱动:

staticstruct platform_driver s3c24xx_i2c_driver = {

       .probe            = s3c24xx_i2c_probe,

       .remove          = s3c24xx_i2c_remove,

       .id_table  = s3c24xx_driver_ids,

       .driver            = {

              .owner    = THIS_MODULE,

              .name      = "s3c-i2c",

              .pm  = S3C24XX_DEV_PM_OPS,

              .of_match_table= of_match_ptr(s3c24xx_i2c_match),

       },

};

 

在驱动列表s3c24xx_driver_ids中,可以找到"s3c2410-i2c",因此IIC的设备和驱动匹配上了:

staticstruct platform_device_id s3c24xx_driver_ids[]= {

       {

              .name             = "s3c2410-i2c",

              .driver_data    = 0,

       }, {

              .name             = "s3c2440-i2c",

              .driver_data    = QUIRK_S3C2440,

       }, {

              .name             = "s3c2440-hdmiphy-i2c",

              .driver_data    = QUIRK_S3C2440 | QUIRK_HDMIPHY | QUIRK_NO_GPIO,

       },{ },

};

 

按惯例,我们再来介绍s3c24xx_i2c_probe函数:

static int s3c24xx_i2c_probe(structplatform_device *pdev)

{

       structs3c24xx_i2c *i2c;

       structs3c2410_platform_i2c *pdata = NULL;

       structresource *res;

       intret;

 

       if(!pdev->dev.of_node) {

              //获得平台设备数据

              pdata =pdev->dev.platform_data;

              if(!pdata) {

                     dev_err(&pdev->dev,"no platform data\n");

                     return-EINVAL;

              }

       }

 

       //申请内存空间

       i2c = devm_kzalloc(&pdev->dev,sizeof(struct s3c24xx_i2c), GFP_KERNEL);

       if(!i2c) {

              dev_err(&pdev->dev,"no memory for state\n");

              return-ENOMEM;

       }

 

       i2c->pdata =devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);

       if(!i2c->pdata) {

              ret= -ENOMEM;

              gotoerr_noclk;

       }

 

       i2c->quirks = s3c24xx_get_device_quirks(pdev);

       if (pdata)

              memcpy(i2c->pdata, pdata, sizeof(*pdata));

       else

              s3c24xx_i2c_parse_dt(pdev->dev.of_node,i2c);

 

       //为i2c结构赋值

       strlcpy(i2c->adap.name, "s3c2410-i2c",sizeof(i2c->adap.name));

       i2c->adap.owner   = THIS_MODULE;

       i2c->adap.algo    = &s3c24xx_i2c_algorithm;         //IIC算法

       i2c->adap.retries = 2;

       i2c->adap.class   = I2C_CLASS_HWMON| I2C_CLASS_SPD;

       i2c->tx_setup     = 50;

 

       spin_lock_init(&i2c->lock);

       init_waitqueue_head(&i2c->wait);

 

       /*find the clock and enable it */

 

       i2c->dev = &pdev->dev;

       i2c->clk = clk_get(&pdev->dev,"i2c");                     //IIC时钟

       if(IS_ERR(i2c->clk)) {

              dev_err(&pdev->dev,"cannot get clock\n");

              ret= -ENOENT;

              gotoerr_noclk;

       }

 

       dev_dbg(&pdev->dev,"clock source %p\n", i2c->clk);

       //IIC时钟使能

       clk_enable(i2c->clk);

 

       /*map the registers */

       //获得内存资源,这里指的是IIC的寄存器

       res= platform_get_resource(pdev, IORESOURCE_MEM, 0);

       if(res == NULL) {

              dev_err(&pdev->dev,"cannot find IO resource\n");

              ret= -ENOENT;

              gotoerr_clk;

       }

       //申请内存区域

       i2c->ioarea =request_mem_region(res->start, resource_size(res),

                                    pdev->name);

 

       if(i2c->ioarea == NULL) {

              dev_err(&pdev->dev,"cannot request IO\n");

              ret= -ENXIO;

              gotoerr_clk;

       }

       //内存重镜像

       i2c->regs = ioremap(res->start,resource_size(res));

 

       if(i2c->regs == NULL) {

              dev_err(&pdev->dev,"cannot map IO\n");

              ret = -ENXIO;

              goto err_ioarea;

       }

 

       dev_dbg(&pdev->dev,"registers %p (%p, %p)\n",

              i2c->regs, i2c->ioarea, res);

 

       /*setup info block for the i2c core*/

 

       i2c->adap.algo_data= i2c;

       i2c->adap.dev.parent =&pdev->dev;

 

       /*initialise the i2ccontroller */

       //初始化IIC

       ret= s3c24xx_i2c_init(i2c);

       if(ret != 0)

              gotoerr_iomap;

 

       /*find the IRQ for this unit (note, this relies on the init call to

        * ensure no current IRQs pending

        */

       //得到IIC的中断

       i2c->irq = ret = platform_get_irq(pdev,0);

       if(ret <= 0) {

              dev_err(&pdev->dev,"cannot find IRQ\n");

              gotoerr_iomap;

       }

       //申请中断

       ret= request_irq(i2c->irq,s3c24xx_i2c_irq, 0,

                       dev_name(&pdev->dev), i2c);

 

       if(ret != 0) {

              dev_err(&pdev->dev,"cannot claim IRQ %d\n", i2c->irq);

              gotoerr_iomap;

       }

 

       ret= s3c24xx_i2c_register_cpufreq(i2c);

       if(ret < 0) {

              dev_err(&pdev->dev,"failed to register cpufreq notifier\n");

              gotoerr_irq;

       }

 

       /*Note, previous versions of the driver used i2c_add_adapter()

        * to add the bus at any number. We now passthe bus number via

        * the platform data, so if unset it will nowdefault to always

        * being bus 0.

        */

 

       i2c->adap.nr = i2c->pdata->bus_num;

       i2c->adap.dev.of_node =pdev->dev.of_node;

       //使用静态总线号来声明IIC适配器

       ret= i2c_add_numbered_adapter(&i2c->adap);

       if(ret < 0) {

              dev_err(&pdev->dev,"failed to add bus to i2ccore\n");

              goto err_cpufreq;

       }

 

       of_i2c_register_devices(&i2c->adap);

       platform_set_drvdata(pdev, i2c);

 

       pm_runtime_enable(&pdev->dev);

       pm_runtime_enable(&i2c->adap.dev);

 

       dev_info(&pdev->dev,"%s: S3C I2C adapter\n", dev_name(&i2c->adap.dev));

       clk_disable(i2c->clk);

       return 0;

 

 err_cpufreq:

       s3c24xx_i2c_deregister_cpufreq(i2c);

 

 err_irq:

       free_irq(i2c->irq, i2c);

 

 err_iomap:

       iounmap(i2c->regs);

 

 err_ioarea:

       release_resource(i2c->ioarea);

       kfree(i2c->ioarea);

 

 err_clk:

       clk_disable(i2c->clk);

       clk_put(i2c->clk);

 

 err_noclk:

       returnret;

}

 

前面提到的IIC算法结构为:

static const struct i2c_algorithm s3c24xx_i2c_algorithm= {

       .master_xfer          = s3c24xx_i2c_xfer,

       .functionality         = s3c24xx_i2c_func,

};

 

在s3c24xx_i2c_xfer函数内,关键用到了s3c24xx_i2c_doxfer函数:

static int s3c24xx_i2c_doxfer(structs3c24xx_i2c *i2c,

                           struct i2c_msg *msgs, int num)

{

       unsignedlong iicstat, timeout;

       intspins = 20;

       intret;

 

       if(i2c->suspended)

              return-EIO;

 

       //设置寄存器,为传输数据做准备

       ret= s3c24xx_i2c_set_master(i2c);

       if(ret != 0) {

              dev_err(i2c->dev, "cannot get bus (error%d)\n", ret);

              ret= -EAGAIN;

              gotoout;

       }

 

       spin_lock_irq(&i2c->lock);

 

       i2c->msg     =msgs;                //数据

       i2c->msg_num = num;                 //字节数

       i2c->msg_ptr = 0;

       i2c->msg_idx = 0;

       i2c->state  = STATE_START;           //开始标识

 

       s3c24xx_i2c_enable_irq(i2c);                     //开启IIC中断

       s3c24xx_i2c_message_start(i2c,msgs);        //开始传输,把数据头字节放在总线上

       spin_unlock_irq(&i2c->lock);

 

       timeout= wait_event_timeout(i2c->wait,i2c->msg_num == 0, HZ * 5);

 

       ret= i2c->msg_idx;

 

       /*having these next two as dev_err() makes life very

        * noisy when doing an i2cdetect */

 

       if(timeout == 0)

              dev_dbg(i2c->dev, "timeout\n");

       elseif (ret != num)

              dev_dbg(i2c->dev, "incomplete xfer(%d)\n", ret);

 

       /*ensure the stop has been through the bus */

 

       dev_dbg(i2c->dev, "waiting for busidle\n");

 

       /*first, try busy waiting briefly */

       //等待传输结束

       do{

              cpu_relax();

              iicstat= readl(i2c->regs +S3C2410_IICSTAT);

       }while ((iicstat & S3C2410_IICSTAT_START)&& --spins);

 

       /*if that timed out sleep */

       if(!spins) {

              msleep(1);

              iicstat= readl(i2c->regs +S3C2410_IICSTAT);

       }

 

       if(iicstat & S3C2410_IICSTAT_START)

              dev_warn(i2c->dev, "timeout waiting for busidle\n");

 

 out:

       returnret;

}

 

从上面的分析可以看出,s3c24xx_i2c_xfer的作用是在开启中断后把数据的头字节放到总线上。当数据真正开始传输的时候,就会触发IIC的中断,因此,实际的数据传输是在中断内完成的。我们来看看IIC的中断处理函数s3c24xx_i2c_irq:

static irqreturn_t s3c24xx_i2c_irq(int irqno, void *dev_id)

{

       structs3c24xx_i2c *i2c = dev_id;

       unsignedlong status;

       unsignedlong tmp;

       //读IICSTAT

       status= readl(i2c->regs +S3C2410_IICSTAT);

       //IIC仲裁失败

       if(status & S3C2410_IICSTAT_ARBITR){

              /*deal with arbitration loss */

              dev_err(i2c->dev, "deal with arbitrationloss\n");

       }

      

       //如果总线处理闲置状况,则退出中断

       if(i2c->state == STATE_IDLE) {

              dev_dbg(i2c->dev, "IRQ: error i2c->state == IDLE\n");

              //并清IIC中断悬挂标识

              tmp =readl(i2c->regs +S3C2410_IICCON);

              tmp&= ~S3C2410_IICCON_IRQPEND;

              writel(tmp,i2c->regs +  S3C2410_IICCON);

              gotoout;

       }

 

       /*pretty much this leaves us with the fact that we've

        * transmitted or received whatever byte welast sent */

 

       i2c_s3c_irq_nextbyte(i2c, status);

 

 out:

       returnIRQ_HANDLED;

}

 

真正的传输函数出现了——i2c_s3c_irq_nextbyte:

static int i2c_s3c_irq_nextbyte(structs3c24xx_i2c *i2c, unsigned long iicstat)

{

       unsignedlong tmp;

       unsignedchar byte;

       intret = 0;

 

       switch(i2c->state) {

 

       caseSTATE_IDLE:               //空闲状态,什么也不做直接退出该函数

              dev_err(i2c->dev, "%s: called inSTATE_IDLE\n", __func__);

              gotoout;

 

       caseSTATE_STOP:              //数据传输结束

              dev_err(i2c->dev, "%s: called inSTATE_STOP\n", __func__);

              s3c24xx_i2c_disable_irq(i2c);              //关闭中断

              gotoout_ack;                //跳到应答

 

       caseSTATE_START:             //传输开始,或者是传输结束为下一次传输做准备

              /*last thing we did was send a start condition on the

               * bus, or started a new i2c message

               */

 

              if(iicstat & S3C2410_IICSTAT_LASTBIT&&                 //传输结束

                  !(i2c->msg->flags& I2C_M_IGNORE_NAK)){              //不忽略应答

                     /*ack was not received... */

 

                     dev_dbg(i2c->dev, "ack was notreceived\n");

                     s3c24xx_i2c_stop(i2c,-ENXIO);                //由于从设备没有响应,而结束传输

                     gotoout_ack;                //跳到应答

              }

 

              //判断此次传输是发送还是接受,即是读还是写

              if(i2c->msg->flags & I2C_M_RD)

                     i2c->state = STATE_READ;

              else

                     i2c->state = STATE_WRITE;

 

              /*terminate the transfer if there is nothing to do

               * as this is used by the i2c probe to find devices. */

              //判断是否传输结束

              if(is_lastmsg(i2c)&& i2c->msg->len== 0) {

                     s3c24xx_i2c_stop(i2c,0);             //结束传输

                     gotoout_ack;

              }

 

              if(i2c->state ==STATE_READ)         //读状态

                     goto prepare_read;

 

              /*fall through to the write state, as we will need to

               * send a byte as well */

 

       caseSTATE_WRITE:                   //写入状态

              /*we are writing data to the device... check for the

               * end of the message, and if so, work out whatto do

               */

              //从设备拒绝写入

              if(!(i2c->msg->flags & I2C_M_IGNORE_NAK)) {

                     if(iicstat & S3C2410_IICSTAT_LASTBIT){

                            dev_dbg(i2c->dev, "WRITE: No Ack\n");

 

                            s3c24xx_i2c_stop(i2c,-ECONNREFUSED);

                            gotoout_ack;

                     }

              }

 

 retry_write:

 

              if(!is_msgend(i2c)) {           //信息传输尚未结束

                     byte= i2c->msg->buf[i2c->msg_ptr++];

                     writeb(byte,i2c->regs + S3C2410_IICDS);               //写入数据

 

                     /*delay after writing the byte to allow the

                      * data setup time on the bus, as writing the

                      * data to the register causes the first bit

                      * to appear on SDA, and SCL will change as

                      * soon as the interrupt is acknowledged */

 

                     ndelay(i2c->tx_setup);                 //等待,延时

 

              }else if (!is_lastmsg(i2c)) {                //不是最后一组信息

                     /*we need to go to the next i2cmessage */

 

                     dev_dbg(i2c->dev, "WRITE: Next Message\n");

                     //为下一组信息传输做准备,即调整指针

                     i2c->msg_ptr = 0;

                     i2c->msg_idx++;

                     i2c->msg++;

 

                     /*check to see if we need to do another message */

                     if (i2c->msg->flags & I2C_M_NOSTART) {

 

                            if (i2c->msg->flags & I2C_M_RD) {

                                   /* cannot do this, the controller

                                    * forces us to send a new START

                                    * when we change direction */

 

                                   s3c24xx_i2c_stop(i2c,-EINVAL);

                            }

 

                            gotoretry_write;

                     }else {

                            /*send the new start */

                            //准备传输下一组信息

                            s3c24xx_i2c_message_start(i2c, i2c->msg);

                            i2c->state = STATE_START;

                     }

 

              }else {                 //所有信息都传输结束

                     /*send stop */

 

                     s3c24xx_i2c_stop(i2c,0);

              }

              break;

 

       caseSTATE_READ:                    //读入状态

              /*we have a byte of data in the data register, do

               * something with it, and then work out wetherwe are

               * going to do any more read/write

               */

              //读入数据

              byte= readb(i2c->regs +S3C2410_IICDS);

              i2c->msg->buf[i2c->msg_ptr++] = byte;

 

 prepare_read:

              if(is_msglast(i2c)) {            //是当前信息的最后一个字节

                     /*last byte of buffer */

 

                     if(is_lastmsg(i2c))               //是最后一组信息,即再没有数据需要读入了

                            s3c24xx_i2c_disable_ack(i2c);             //无需应答

 

              }else if (is_msgend(i2c)) {          //当前信息的尾部

                     /*ok, we've read the entire buffer, see if there

                      * is anything else we need to do */

 

                     if(is_lastmsg(i2c)) {            //最后一组信息

                            /*last message, send stop and complete */

                            dev_dbg(i2c->dev, "READ: Send Stop\n");

 

                            s3c24xx_i2c_stop(i2c,0);

                     }else {

                            /*go to the next transfer */

                            dev_dbg(i2c->dev, "READ: NextTransfer\n");

 

                            i2c->msg_ptr = 0;

                            i2c->msg_idx++;

                            i2c->msg++;

                     }

              }

 

              break;

       }

 

       /*acknowlegde the IRQ and get back on with the work */

 

 out_ack:                     //并清IIC中断悬挂标识

       tmp= readl(i2c->regs +S3C2410_IICCON);

       tmp&= ~S3C2410_IICCON_IRQPEND;

       writel(tmp,i2c->regs + S3C2410_IICCON);

 out:

       returnret;

}

 

s3c24xx_i2c_probe函数中还用到了s3c24xx_i2c_init函数:

static int s3c24xx_i2c_init(structs3c24xx_i2c *i2c)

{

       unsignedlong iicon = S3C2410_IICCON_IRQEN| S3C2410_IICCON_ACKEN;

       structs3c2410_platform_i2c *pdata;

       unsignedint freq;

 

       /*get the plafrom data */

 

       pdata = i2c->pdata;

 

       /* inititalise the gpio */

       //初始化IIC的引脚

       if(pdata->cfg_gpio)

              pdata->cfg_gpio(to_platform_device(i2c->dev));

       else

              if(s3c24xx_i2c_parse_dt_gpio(i2c))

                     return-EINVAL;

 

       /*write slave address */

       //写IICADD寄存器

       writeb(pdata->slave_addr,i2c->regs + S3C2410_IICADD);

 

       dev_info(i2c->dev, "slave address0x%02x\n", pdata->slave_addr);

       //写IICCON,IIC应答使能,IIC中断使能

       writel(iicon,i2c->regs + S3C2410_IICCON);

 

       /*we need to work out the divisors for the clock... */

       //分频

       if(s3c24xx_i2c_clockrate(i2c, &freq) != 0) {

              writel(0,i2c->regs + S3C2410_IICCON);

              dev_err(i2c->dev, "cannot meet bus frequencyrequired\n");

              return-EINVAL;

       }

 

       /*todo - check that the i2clines aren't being dragged anywhere */

 

       dev_info(i2c->dev, "bus frequency set to %dKHz\n", freq);

       dev_dbg(i2c->dev, "S3C2410_IICCON=0x%02lx\n", iicon);

 

       return 0;

}

 

我们最后看看IIC的文件层drivers/i2c/i2c-dev.c:

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)

              gotoout;

       //创建子类

       i2c_dev_class = class_create(THIS_MODULE,"i2c-dev");

       if(IS_ERR(i2c_dev_class)){

              res= PTR_ERR(i2c_dev_class);

              gotoout_unreg_chrdev;

       }

 

       /*Keep track of adapters which will be added or removed later */

       res= bus_register_notifier(&i2c_bus_type,&i2cdev_notifier);

       if(res)

              goto out_unreg_class;

 

       /*Bind to already existing adapters right away */

       //绑定现有的IIC适配器

       i2c_for_each_dev(NULL, i2cdev_attach_adapter);

 

       return0;

 

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__);

       returnres;

}

 

IIC的操作集i2cdev_fops:

static const struct file_operationsi2cdev_fops = {

       .owner           = THIS_MODULE,

       .llseek            = no_llseek,

       .read              = i2cdev_read,

       .write             = i2cdev_write,

       .unlocked_ioctl      = i2cdev_ioctl,

       .open             = i2cdev_open,

       .release    = i2cdev_release,

};

 

我们对i2cdev_read、i2cdev_write和i2cdev_ioctl这三个主要的函数进行分析:

static ssize_t i2cdev_read(struct file*file, char __user *buf, size_t count,

              loff_t*offset)

{

       char*tmp;

       intret;

 

       structi2c_client *client =file->private_data;

 

       if(count > 8192)

              count= 8192;                      //限制字节数

 

       tmp= kmalloc(count, GFP_KERNEL);                     //开辟内存空间

       if(tmp == NULL)

              return-ENOMEM;

 

       pr_debug("i2c-dev: i2c-%d reading %zu bytes.\n",

              iminor(file->f_path.dentry->d_inode),count);

 

       ret= i2c_master_recv(client, tmp,count);                  //以master形式接受数据

       if(ret >= 0)

              ret= copy_to_user(buf, tmp, count) ? -EFAULT : ret;               //复制到用户程序中

       kfree(tmp);

       returnret;

}

 

static ssize_t i2cdev_write(struct file*file, const char __user *buf,

              size_tcount, loff_t *offset)

{

       intret;

       char*tmp;

       structi2c_client *client =file->private_data;

 

       if(count > 8192)

              count= 8192;

 

       tmp= memdup_user(buf, count);

       if(IS_ERR(tmp))

              returnPTR_ERR(tmp);

 

       pr_debug("i2c-dev: i2c-%d writing %zu bytes.\n",

              iminor(file->f_path.dentry->d_inode),count);

 

       ret= i2c_master_send(client, tmp,count);                  //以master形式发送数据

       kfree(tmp);

       returnret;

}

 

i2c_master_recv函数和i2c_master_send函数都在drviers/i2c/I2c-core.c文件内,而且这两个函数最终都是调用的i2c_transfer函数。

 

由于对IIC的读i2cdev_read和写i2cdev_write操作有一些局限性,而且这两个操作完全可以用i2cdev_ioctl来替代:

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

{

       structi2c_client *client =file->private_data;

       unsignedlong funcs;

 

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

              cmd, arg);

 

       switch(cmd) {

       caseI2C_SLAVE:

       caseI2C_SLAVE_FORCE:                  //设置从设备地址

              /*NOTE:  devices set up to work with"new style" drivers

               * can't use I2C_SLAVE, even when the device node is not

               * bound to a driver.  Only I2C_SLAVE_FORCE will work.

               *

               * Setting the PEC flag here won't affectkernel drivers,

               * which will be using the i2c_client node registered with

               * the driver model core.  Likewise, when that client has

               * the PEC flag already set, the i2c-dev driver won't see

               * (or use) this setting.

               */

              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;

              /*REVISIT: address could become busy later */

              client->addr= arg;

              return0;

       caseI2C_TENBIT:               //设置7位地址还是10位地址

              if (arg)                         //非零是十位

                     client->flags |= I2C_M_TEN;

              else

                     client->flags &= ~I2C_M_TEN;          //七位

              return 0;

       caseI2C_PEC:

              if(arg)

                     client->flags |= I2C_CLIENT_PEC;

              else

                     client->flags &= ~I2C_CLIENT_PEC;

              return 0;

       caseI2C_FUNCS:                //返回控制器算法所支持的传输种类

              funcs= i2c_get_functionality(client->adapter);

              returnput_user(funcs, (unsigned long __user *)arg);

 

       caseI2C_RDWR:                 //IIC的读写操作,master方式

              returni2cdev_ioctl_rdrw(client, arg);

 

       caseI2C_SMBUS:                //IIC的SMBus传输方式

              returni2cdev_ioctl_smbus(client, arg);

 

       caseI2C_RETRIES:             //重复次数

              client->adapter->retries = arg;

              break;

       case I2C_TIMEOUT:            //延时

              /*For historical reasons, user-space sets the timeout

               * value in units of 10 ms.

               */

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

              break;

       default:

              /*NOTE:  returning a fault code here couldcause trouble

               * in buggy userspace code.  Some old kernel bugs returned

               * zero in this case, and userspace code mightaccidentally

               * have depended on that bug.

               */

              return-ENOTTY;

       }

       return0;

}

 

最主要的IIC读写操作i2cdev_ioctl_rdrw函数:

static noinline inti2cdev_ioctl_rdrw(struct i2c_client*client,

              unsignedlong arg)

{

       structi2c_rdwr_ioctl_data rdwr_arg;

       structi2c_msg *rdwr_pa;

       u8 __user **data_ptrs;

       int i, res;

 

       //复制用户空间中的数据

       if(copy_from_user(&rdwr_arg,

                        (struct i2c_rdwr_ioctl_data __user *)arg,

                        sizeof(rdwr_arg)))

              return-EFAULT;

 

       /*Put an arbitrary limit on the number of messages that can

        * be sent at once */

       if(rdwr_arg.nmsgs > I2C_RDRW_IOCTL_MAX_MSGS)

              return-EINVAL;

       //从用户空间复制内存

       rdwr_pa= memdup_user(rdwr_arg.msgs,

                           rdwr_arg.nmsgs * sizeof(struct i2c_msg));

       if(IS_ERR(rdwr_pa))

              returnPTR_ERR(rdwr_pa);

       //开辟一段内存空间给data_ptrs

       data_ptrs= kmalloc(rdwr_arg.nmsgs * sizeof(u8 __user *), GFP_KERNEL);

       if(data_ptrs == NULL) {

              kfree(rdwr_pa);

              return -ENOMEM;

       }

 

       res = 0;

       //复制数据

       for (i = 0; i < rdwr_arg.nmsgs; i++) {

              /* Limitthe size of the message to a sane amount */

              if(rdwr_pa[i].len > 8192) {

                     res= -EINVAL;

                     break;

              }

              //复制数据

              data_ptrs[i]= (u8 __user *)rdwr_pa[i].buf;

              rdwr_pa[i].buf= memdup_user(data_ptrs[i], rdwr_pa[i].len);

              if(IS_ERR(rdwr_pa[i].buf)) {

                     res = PTR_ERR(rdwr_pa[i].buf);

                     break;

              }

 

              /*

               * If the message length is received from theslave (similar

               * to SMBus block read), we must ensure thatthe buffer will

               * be large enough to cope with a messagelength of

               * I2C_SMBUS_BLOCK_MAXas this is the maximum underlying bus

               * drivers allow. The first byte in the buffermust be

               * pre-filled with the number of extra bytes,which must be

               * at least one to hold the message length, butcan be

               * greater (for example to account for achecksum byte at

               * the end of the message.)

               */

              if (rdwr_pa[i].flags & I2C_M_RECV_LEN) {

                     if (!(rdwr_pa[i].flags& I2C_M_RD) ||        //不是读命令

                         rdwr_pa[i].buf[0] < 1 ||

                         rdwr_pa[i].len < rdwr_pa[i].buf[0] +

                                        I2C_SMBUS_BLOCK_MAX){

                            res = -EINVAL;

                            break;            //退出for循环

                     }

 

                     rdwr_pa[i].len= rdwr_pa[i].buf[0];

              }

       }

       if(res < 0) {

              int j;

              for (j = 0; j < i; ++j)

                     kfree(rdwr_pa[j].buf);

              kfree(data_ptrs);

              kfree(rdwr_pa);

              returnres;

       }

       //IIC的传输数据

       res= i2c_transfer(client->adapter,rdwr_pa, rdwr_arg.nmsgs);

       //如果是读命令(数据传输方向是从设备向主设备),把数据传递给用户

       while(i-- > 0) {

              //读命令判断

              if (res >= 0 &&(rdwr_pa[i].flags & I2C_M_RD)){

                     if(copy_to_user(data_ptrs[i], rdwr_pa[i].buf,            //传输数据给用户

                                    rdwr_pa[i].len))

                            res = -EFAULT;

              }

              kfree(rdwr_pa[i].buf);

       }

       kfree(data_ptrs);

       kfree(rdwr_pa);

       return res;

}

 

再来看看上面这个函数最关键的一条——i2c_transfer。i2cdev_write和i2cdev_read实际上也主要是调用这个函数,因此正如上文介绍的,完全可以用ioctl来代替read和write:

int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)

{

       intret;

 

       /*REVISIT the fault reporting model here is weak:

        *

        *  -When we get an error after receiving N bytes from a slave,

        *   there is no way to report "N".

        *

        *  -When we get a NAK after transmitting N bytes to a slave,

        *   there is no way to report "N" ... or to let the master

        *    continueexecuting the rest of this combined message, if

        *   that's the appropriate response.

        *

        *  -When for example "num" is two and we successfully complete

        *    thefirst message but get an error part way through the

        *   second, it's unclear whether that should be reported as

        *    one(discarding status on the second message) or errno

        *   (discarding status on the first one).

        */

 

       if(adap->algo->master_xfer) {            //IIC算法为master

#ifdefDEBUG

              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

 

              if(in_atomic() || irqs_disabled()) {

                     ret = i2c_trylock_adapter(adap);

                     if(!ret)

                            /*I2C activity is ongoing. */

                            return-EAGAIN;

              }else {

                     i2c_lock_adapter(adap);

              }

              //IIC传输数据

              ret = __i2c_transfer(adap, msgs, num);

              i2c_unlock_adapter(adap);

 

              returnret;

       }else {

              dev_dbg(&adap->dev,"I2C leveltransfers not supported\n");

              return-EOPNOTSUPP;

       }

}

 

__i2c_transfer函数也在drviers/i2c/I2c-core.c文件内:

int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)

{

       unsignedlong orig_jiffies;

       intret, try;

 

       /*Retry automatically on arbitration loss */

       orig_jiffies= jiffies;

       //根据所设置的重试次数来循环

       for(ret = 0, try = 0; try <= adap->retries; try++) {

//使用master进行传输,即调用s3c24xx_i2c_xfer函数

              ret =adap->algo->master_xfer(adap, msgs, num);            

              if (ret !=-EAGAIN)

                     break;

              if(time_after(jiffies, orig_jiffies + adap->timeout))           //延时

                     break;

       }

 

       returnret;

}

 

在写应用程序的时候,如果我们要用I2C_RDWR这个命令,那么就要用到i2c_rdwr_ioctl_data结构(它是在include/linux/i2c-dev.h中定义的),所以我们给出它的定义:

struct i2c_rdwr_ioctl_data {

       structi2c_msg __user *msgs; /* pointers to i2c_msgs */

       __u32nmsgs;                /* number of i2c_msgs */

};

 

struct i2c_msg {

       __u16addr;    /* slave address                    */

       __u16flags;

#define I2C_M_TEN            0x0010    /* this is a ten bit chip address */

#define I2C_M_RD              0x0001    /* read data, from slave to master */

#define I2C_M_STOP          0x8000    /* if I2C_FUNC_PROTOCOL_MANGLING */

#define I2C_M_NOSTART           0x4000    /* if I2C_FUNC_NOSTART */

#define I2C_M_REV_DIR_ADDR       0x2000    /* if I2C_FUNC_PROTOCOL_MANGLING */

#define I2C_M_IGNORE_NAK   0x1000    /* if I2C_FUNC_PROTOCOL_MANGLING */

#define I2C_M_NO_RD_ACK            0x0800    /* if I2C_FUNC_PROTOCOL_MANGLING */

#define I2C_M_RECV_LEN        0x0400    /* length will be first received byte */

       __u16len;             /* msg length                       */

       __u8*buf;             /* pointer to msg data                  */

};

 

下面我们就来写访问IIC的应用程序。在开发板上,用IIC连接的设备是AT24C02的eeprom。我们首先用write和read来读写eeprom:

#include <stdio.h>

#include <stdlib.h>

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

#include <string.h>

//下面两个头文件是操作IIC的关键

#include <linux/i2c.h>

#include <linux/i2c-dev.h>

 

int main(void){

int fd, i;

char sendadd;

char buf[50];

 

fd = open("/dev/i2c-0", O_RDWR);

if(fd < 0){

printf("####i2ctest device open failed####/n");

return (-1);

}

 

ioctl(fd,I2C_TENBIT,0);              //七位地址

//AT24C02的地址为0xa0,但由于是七位地址,最低一位是读写位,因此0xa0右移一位就变为0x50

if (ioctl(fd, I2C_SLAVE_FORCE, 0x50) < 0) {

printf("####i2cset slave address failed####/n");

return (-1);

}

ioctl(fd,I2C_TIMEOUT,1);          //超时时间

ioctl(fd,I2C_RETRIES,2);           //重复次数

 

sendaddr = 0x28;           //这里设置读的地址为0x28

 

write(fd, &sendadd,1);         //发送地址

read(fd, buf, 8);                   //连续读出8个数据

 

       for(i=0; i<8; i++)

printf("buff[%d]=%x\n", i, buf[i]);

 

close(fd);

return 0;

}

 

上面的程序实现了读取AT24C02中从地址0x28开始的连续8个数据。下面的程序实现了从0x30地址开始,连续写入8个数据:

#include <stdio.h>

#include <stdlib.h>

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

#include <string.h>

#include <linux/i2c.h>

#include <linux/i2c-dev.h>

 

int main(void){

int fd;

charbuf[8]={0xef,0x9f,0x3,0x5a,0x2e,0x67,0x89,0x10};         //要写入的数据

//senddata为传输的数据,它包括第一个字节的地址信息,和后8个字节的写入数据信息

char senddata[9];          

 

fd = open("/dev/i2c-0", O_RDWR);

if(fd < 0){

printf("####i2ctest device open failed####/n");

return (-1);

}

 

ioctl(fd,I2C_TENBIT,0);

if (ioctl(fd, I2C_SLAVE_FORCE, 0x50) < 0) {

printf("####i2cset slave address failed####/n");

return (-1);

}

ioctl(fd,I2C_TIMEOUT,1);

ioctl(fd,I2C_RETRIES,2);

 

memcpy(senddata+1,buf, 8);             

senddata[0]=0x30;                       //要写入的地址

 

write(fd, senddata,9);

 

close(fd);

return 0;

}

 

下面我们再用ioctl的方法来实现读写功能:

/*******************

*    iicr.c        *

******************/

#include <stdio.h>

#include <linux/types.h>

#include <stdlib.h>

#include <fcntl.h>

#include <unistd.h>

#include <sys/types.h>

#include <sys/ioctl.h>

#include <errno.h>

#include<linux/i2c.h>

#include<linux/i2c-dev.h>

 

int main(int argc, char **argv)

{

int fd,ret;

struct i2c_rdwr_ioctl_data e2prom_data;

unsigned intreg_address;  

 

fd=open("/dev/i2c-0",O_RDWR);

if(fd<0){

perror("open error");

exit(1);

}

 

sscanf(argv[1],"%x", &reg_address);         //转换参数类型,第一个参数为目标地址

 

e2prom_data.nmsgs=2;

/*

*因为操作时序中,最多是用到2个开始信号(字节读操作中),所以此将

*e2prom_data.nmsgs配置为2

*/

e2prom_data.msgs=(structi2c_msg*)malloc(e2prom_data.nmsgs*sizeof(structi2c_msg));

if(!e2prom_data.msgs){

perror("malloc error");

exit(1);

}

ioctl(fd,I2C_TIMEOUT,1);

ioctl(fd,I2C_RETRIES,2);

 

/***write datato e2prom**/

e2prom_data.nmsgs=2;

(e2prom_data.msgs[0]).len=1;             //1个 e2prom 写入目标的地址

(e2prom_data.msgs[0]).addr=0x50;      //e2prom 设备地址

(e2prom_data.msgs[0]).flags=0;  //write

(e2prom_data.msgs[0]).buf=(unsignedchar*)malloc(1);

(e2prom_data.msgs[0]).buf[0]=reg_address;        // e2prom 写入目标的地址

 

(e2prom_data.msgs[1]).len=1;             //读出的数据

(e2prom_data.msgs[1]).addr=0x50;      // e2prom 设备地址

(e2prom_data.msgs[1]).flags=I2C_M_RD;          //read

(e2prom_data.msgs[1]).buf=(unsignedchar*)malloc(1);//存放返回值的地址。

(e2prom_data.msgs[1]).buf[0]=0;        //初始化读缓冲

 

ret=ioctl(fd, I2C_RDWR, (unsigned long)&e2prom_data);

if(ret<0){

perror("ioctl error1");

exit(1);

}

 

printf("buf[0]=%x\n",(e2prom_data.msgs[1]).buf[0]);

close(fd);

return 0;

}

上面的程序实现了可以读取AT24C02中的任意一个地址数据的功能。而下面的程序实现了可以写入任意一个地址数据的功能,其中第一个参数内存地址,第二个参数为要写入的数据:

/***************

*    iicw.c    *

***************/

#include <stdio.h>

#include <linux/types.h>

#include <stdlib.h>

#include <fcntl.h>

#include <unistd.h>

#include <sys/types.h>

#include <sys/ioctl.h>

#include <errno.h>

#include<string.h>

#include<linux/i2c.h>

#include<linux/i2c-dev.h>

 

int main(int argc, char *argv[])

{

int fd, ret;

struct i2c_rdwr_ioctl_data e2prom_data;

unsignedint  reg_address,value;  

 

fd=open("/dev/i2c-0",O_RDWR);

if(fd<0){

perror("open error");

}

 

sscanf(argv[1],"%x", &reg_address);          //目标地址

sscanf(argv[2],"%x", &value);                   //要写入的数据

 

e2prom_data.nmsgs=1;

e2prom_data.msgs=(structi2c_msg*)malloc(e2prom_data.nmsgs*sizeof(structi2c_msg));

if(!e2prom_data.msgs){

perror("malloc error");

exit(1);

}

 

ioctl(fd,I2C_TIMEOUT,1);

ioctl(fd,I2C_RETRIES,2);

 

e2prom_data.nmsgs=1;

(e2prom_data.msgs[0]).len=2;    //1个 e2prom 写入目标的地址和1个数据

(e2prom_data.msgs[0]).addr=0x50;      //e2prom 设备地址

(e2prom_data.msgs[0]).flags=0;  //write

(e2prom_data.msgs[0]).buf=(unsignedchar*)malloc(2);

(e2prom_data.msgs[0]).buf[0]=reg_address;        // e2prom 写入目标的地址

(e2prom_data.msgs[0]).buf[1]=value;          //要写入的数据

 

ret=ioctl(fd,I2C_RDWR,(unsigned long)&e2prom_data);

if(ret<0){

perror("ioctl error2");

}

 

close(fd);

return 0;

}

 

正如前面介绍的,用ioctl完全可以实现read和write的功能,但ioctl一次只能读写一个字节,而read和write可以一次读写多个字节。