首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 操作系统 > UNIXLINUX >

Linux上的SPI总线驱动(二)

2012-11-22 
Linux下的SPI总线驱动(二)版权所有,转载请说明转自 http://my.csdn.net/weiqing1981127四.SPI内核代码分析

Linux下的SPI总线驱动(二)

版权所有,转载请说明转自 http://my.csdn.net/weiqing1981127  

四.SPI内核代码分析

我们已经讲过SPI驱动的移植,SPI控制设备和SPI接口设备的注册,SPI控制设备驱动和SPI接口设备驱动的注册。在移植时候,我们配置的选项告诉我们有些文件已经编入内核,那些就是SPI驱动的比较重要的文件。其中spi.c是SPI初始化和核心代码。spi_gpio.c是IO模拟SPI接口代码。spi_s3c24xx.c是s3c24xx系列芯片的SPI控制器驱动,它向更上层的SPI核心层(spi.c)提供接口用来控制芯片的SPI控制器,是一个被其他驱动使用的驱动。spi_s3c24xx_gpio.c允许用户指定3个gpio口,分别充当spi_clk、spi_mosi和spi_miso接口,模拟标准的spi总线。spidev.c是在核心层基础之上将SPI控制器模拟成一个字符型的驱动,向文件系统提供标准的文件系统接口,用来操作对应的SPI控制器。

通过这些代码的分析,我们可以获得三个知识:其一,更清楚的理解设备和驱动的注册。其二,掌握SPI数据的传输过程。三,更清晰的明白SPI各个文件的功能

 

我们先看spi.c

static int __init spi_init(void)

{

      int    status;

      buf = kmalloc(SPI_BUFSIZ, GFP_KERNEL);  //定义一个缓冲区,供后期数据传输使用

      if (!buf) {

             status = -ENOMEM;

             goto err0;

      }

      status = bus_register(&spi_bus_type);   //SPI总线注册

      if (status < 0)

             goto err1;

      status = class_register(&spi_master_class);  //SPI类注册

      if (status < 0)

             goto err2;

      return 0;

err2:

      bus_unregister(&spi_bus_type);

err1:

      kfree(buf);

      buf = NULL;

err0:

      return status;

}

上面的总线注册让我们联想到前面讲SPI接口设备注册和SPI接口设备驱动注册时候,在那里我们看到将它们都注册到我们现在这里注册的SPI总线上来的。

然后我们在spi.c中定义了几个函数

spi_write_then_read             spi_sync         spi_async              spi_setup

spi_busnum_to_master        spi_unregister_master           spi_register_master

spi_alloc_master           spi_new_device             spi_add_device

spi_alloc_device           spi_register_driver         spi_get_device_id

 

其次我们看看spi_bitbang.c

这个文件里主要定义了几个函数

spi_bitbang_stop          spi_bitbang_start           spi_bitbang_transfer

spi_bitbang_setup         spi_bitbang_cleanup              spi_bitbang_setup_transfer

 

然后看看spi_s3c24xx.c

前面我们已经知道spi_s3c24xx.c中,我们注册了SPI控制器驱动,注册了SPI接口设备。现在,我们好好读读这个文件,看看怎么由这个SPI控制器驱动到注册这个SPI接口设备的。

static struct platform_driver s3c24xx_spi_driver = {

      .remove          = __exit_p(s3c24xx_spi_remove),

      .driver            = {

             .name      = "s3c2410-spi",

             .owner    = THIS_MODULE,

             .pm  = S3C24XX_SPI_PMOPS,

      },

};

static int __init s3c24xx_spi_init(void)

{

       return platform_driver_probe(&s3c24xx_spi_driver, s3c24xx_spi_probe);

}

这个平台设备s3c24xx_spi_driver就是我们说的SPI控制器驱动,现在我们把精力集中在s3c24xx_spi_probe上吧

 

static int __init s3c24xx_spi_probe(struct platform_device *pdev)

{

      struct s3c2410_spi_info *pdata;

      struct s3c24xx_spi *hw;

      struct spi_master *master;

      struct resource *res;

      int err = 0;

      // 分配master+ s3c24xx_spi大小的数据,把s3c24xx_spi设为spi_master的私有数据

      master = spi_alloc_master(&pdev->dev, sizeof(struct s3c24xx_spi));

      if (master == NULL) {

             dev_err(&pdev->dev, "No memory for spi_master\n");

             err = -ENOMEM;

             goto err_nomem;

      }

      hw = spi_master_get_devdata(master);        //从master中获得s3c24xx_spi

      memset(hw, 0, sizeof(struct s3c24xx_spi));    //清空s3c24xx_spi

      hw->master = spi_master_get(master);  

//驱动移植的时候需要实现的重要结构,初始化为&s3c2410_spi0_platdata

hw->pdata = pdata = pdev->dev.platform_data;

      hw->dev = &pdev->dev;

      if (pdata == NULL) {

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

             err = -ENOENT;

             goto err_no_pdata;

      }

      platform_set_drvdata(pdev, hw); //设置平台的私有数据为s3c24xx_spi

      init_completion(&hw->done);

      master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;

      master->num_chipselect = hw->pdata->num_cs;    //该总线上的设备数

      master->bus_num = pdata->bus_num;            //总线号

      hw->bitbang.master         = hw->master;

//spi_bitbang专门负责数据的传输

//此处不定义除s3c24xx_spi_chipsel外其他函数也可以,那样会在spi_bitbang.c中定义

      hw->bitbang.setup_transfer = s3c24xx_spi_setupxfer;

      hw->bitbang.chipselect     = s3c24xx_spi_chipsel;

      hw->bitbang.txrx_bufs      = s3c24xx_spi_txrx;

      hw->master->setup  = s3c24xx_spi_setup;

      hw->master->cleanup = s3c24xx_spi_cleanup;

      dev_dbg(hw->dev, "bitbang at %p\n", &hw->bitbang);

      res = platform_get_resource(pdev, IORESOURCE_MEM, 0);

      if (res == NULL) {

             dev_err(&pdev->dev, "Cannot get IORESOURCE_MEM\n");

             err = -ENOENT;

             goto err_no_iores;

      }

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

                                  pdev->name);

      if (hw->ioarea == NULL) {

             dev_err(&pdev->dev, "Cannot reserve region\n");

             err = -ENXIO;

             goto err_no_iores;

      }

      hw->regs = ioremap(res->start, resource_size(res));   //读取寄存器基址

      if (hw->regs == NULL) {

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

             err = -ENXIO;

             goto err_no_iomap;

      }

      hw->irq = platform_get_irq(pdev, 0);

      if (hw->irq < 0) {

             dev_err(&pdev->dev, "No IRQ specified\n");

             err = -ENOENT;

             goto err_no_irq;

      }

      err = request_irq(hw->irq, s3c24xx_spi_irq, 0, pdev->name, hw);  //申请中断

      if (err) {

             dev_err(&pdev->dev, "Cannot claim IRQ\n");

             goto err_no_irq;

      }

      hw->clk = clk_get(&pdev->dev, "spi");

      if (IS_ERR(hw->clk)) {

             dev_err(&pdev->dev, "No clock for device\n");

             err = PTR_ERR(hw->clk);

             goto err_no_clk;

      }

      if (!pdata->set_cs) {

             if (pdata->pin_cs < 0) {

                    dev_err(&pdev->dev, "No chipselect pin\n");

                    goto err_register;

             }

             err = gpio_request(pdata->pin_cs, dev_name(&pdev->dev));  //申请中断

             if (err) {

                    dev_err(&pdev->dev, "Failed to get gpio for cs\n");

                    goto err_register;

             }

             hw->set_cs = s3c24xx_spi_gpiocs; //如果私有数据没定义set_cs,则调用内核中函数

             gpio_direction_output(pdata->pin_cs, 1);

      } else

             hw->set_cs = pdata->set_cs;  //如果私有数据没定义了set_cs就调用之

      //初始化设置寄存器,包括对SPIMOSI,SPIMISO,SPICLK引脚的设置

      s3c24xx_spi_initialsetup(hw);

      err = spi_bitbang_start(&hw->bitbang);   //注册SPI接口设备

      if (err) {

             dev_err(&pdev->dev, "Failed to register SPI master\n");

             goto err_register;

      }

      return 0;

 err_register:

      if (hw->set_cs == s3c24xx_spi_gpiocs)

             gpio_free(pdata->pin_cs);

      clk_disable(hw->clk);

      clk_put(hw->clk);

 err_no_clk:

      free_irq(hw->irq, hw);

 err_no_irq:

      iounmap(hw->regs);

 err_no_iomap:

      release_resource(hw->ioarea);

      kfree(hw->ioarea);

 err_no_iores:

 err_no_pdata:

      spi_master_put(hw->master);

 err_nomem:

      return err;

}

总结下,在s3c24xx_spi_probe中,我们主要做了如下几个事情:其一,申请了spi_master,将s3c24xx_spi作为其私有数据,同时,s3c24xx_spi也作为了平台设备的私有数据。其二,对spi_master和s3c24xx_spi下的bitbang分别进行了初始化。其三,读取寄存器基址和中断号,申请中断。其四,初始化寄存器,包括对SPIMOSI,SPIMISO,SPICLK引脚的设置。其五,注册SPI接口设备。

 

未来我们主要研究中断函数和如何注册SPI接口设备的函数,这里,我们先看看如何注册SPI接口的函数spi_bitbang_start

int spi_bitbang_start(struct spi_bitbang *bitbang)

{

      int    status;

      if (!bitbang->master || !bitbang->chipselect)

             return -EINVAL;

      //动态创建一个work_struct结构,它的处理函数是bitbang_work

      INIT_WORK(&bitbang->work, bitbang_work);

      spin_lock_init(&bitbang->lock);

      INIT_LIST_HEAD(&bitbang->queue);

      if (!bitbang->master->mode_bits)

             bitbang->master->mode_bits = SPI_CPOL | SPI_CPHA | bitbang->flags;

      if (!bitbang->master->transfer)    // spi的数据传输就是用这个方法

             bitbang->master->transfer = spi_bitbang_transfer;

      if (!bitbang->txrx_bufs) { // 在spi_s3c24xx.c中的probe中已经定义,故跳过

             bitbang->use_dma = 0;

             bitbang->txrx_bufs = spi_bitbang_bufs;

             if (!bitbang->master->setup) {

                    if (!bitbang->setup_transfer)

                           bitbang->setup_transfer =

                                   spi_bitbang_setup_transfer;

                    bitbang->master->setup = spi_bitbang_setup;

                    bitbang->master->cleanup = spi_bitbang_cleanup;

             }

      } else if (!bitbang->master->setup)

             return -EINVAL;

      bitbang->busy = 0;

      //调用create_singlethread_workqueue创建单个工作线程

      bitbang->workqueue = create_singlethread_workqueue(

                    dev_name(bitbang->master->dev.parent));

      if (bitbang->workqueue == NULL) {

             status = -EBUSY;

             goto err1;

      }

      status = spi_register_master(bitbang->master);  //注册SPI接口设备

      if (status < 0)

             goto err2;

      return status;

err2:

      destroy_workqueue(bitbang->workqueue);

err1:

      return status;

}

 

我们跟踪spi_bitbang_start中的spi_register_master函数

int spi_register_master(struct spi_master *master)

{

      static atomic_t              dyn_bus_id = ATOMIC_INIT((1<<15) - 1);

      struct device          *dev = master->dev.parent;

      int                  status = -ENODEV;

      int                  dynamic = 0;

      if (!dev)

             return -ENODEV;

      if (master->num_chipselect == 0)

             return -EINVAL;

      if (master->bus_num < 0) {

             master->bus_num = atomic_dec_return(&dyn_bus_id);

             dynamic = 1;

      }

//将spi添加到内核,这也是sys/class/Spi_master下产生Spi0,Spi1的原因

      dev_set_name(&master->dev, "spi%u", master->bus_num);

      status = device_add(&master->dev);

      if (status < 0)

             goto done;

      dev_dbg(dev, "registered master %s%s\n", dev_name(&master->dev),

                    dynamic ? " (dynamic)" : "");

      scan_boardinfo(master);

      status = 0;

done:

      return status;

}

 

这里主要跟踪spi_register_master中的scan_boardinfo函数

static void scan_boardinfo(struct spi_master *master)

{

      struct boardinfo     *bi;

      mutex_lock(&board_lock);

      //遍历所有挂在board_list上的 boardinfo

      list_for_each_entry(bi, &board_list, list) {

      //遍历每个boardinfo管理的spi_board_info

struct spi_board_info    *chip = bi->board_info;   //控制器

             unsigned         n;

             for (n = bi->n_board_info; n > 0; n--, chip++) {

      //如果设备的总线号与控制器的总线好相等,则创建新设备

                    if (chip->bus_num != master->bus_num)

                           continue;

                    (void) spi_new_device(master, chip); //创建SPI接口设备

             }

      }

      mutex_unlock(&board_lock);

}

所以,在SPI控制器驱动注册的时候不但注册这个主机控制器的驱动,还要遍历这个SPI接口设备链表,比较接口设备总线号是否与控制器总线号一致,将接口设备总线号一致的接口设备spi_device全部注册进内核。

现在看看scan_boardinfo中创建新设备的函数spi_new_device

struct spi_device *spi_new_device(struct spi_master *master,

                             struct spi_board_info *chip)

{

      struct spi_device    *proxy;

      int                  status;

      proxy = spi_alloc_device(master); //分配spi_device

      if (!proxy)

             return NULL;

      WARN_ON(strlen(chip->modalias) >= sizeof(proxy->modalias));

      //初始化spi_device的各个字段

      proxy->chip_select = chip->chip_select;

      proxy->max_speed_hz = chip->max_speed_hz;

      proxy->mode = chip->mode;

      proxy->irq = chip->irq;

      //获得spi_device的名字,移植时在mach-smdk2440.c中的s3c2410_spi0_board中设定的

      strlcpy(proxy->modalias, chip->modalias, sizeof(proxy->modalias));

      proxy->dev.platform_data = (void *) chip->platform_data;

      proxy->controller_data = chip->controller_data;

      proxy->controller_state = NULL;

      status = spi_add_device(proxy);    //主要完成将spi_device添加到内核

      if (status < 0) {

             spi_dev_put(proxy);

             return NULL;

      }

      return proxy;

}

之前我们说过spi_new_device中的分配spi_device函数会调用spi->dev.bus = &spi_bus_type;设置SPI接口设备的总线类型为spi_bus_type,这样便于与SPI接口设备驱动相联系。

好了,现在我们看看spi_new_device中将spi_device添加到内核的函数spi_add_device

int spi_add_device(struct spi_device *spi)

{

      static DEFINE_MUTEX(spi_add_lock);

      struct device *dev = spi->master->dev.parent;

      int status;

      if (spi->chip_select >= spi->master->num_chipselect) {

             dev_err(dev, "cs%d >= max %d\n",

                    spi->chip_select,

                    spi->master->num_chipselect);

             return -EINVAL;

      }

      //设置是spi_device在Linux设备驱动模型中的name,也就是spi0.0   dev_set_name(&spi->dev, "%s.%u", dev_name(&spi->master->dev),

                    spi->chip_select);

      mutex_lock(&spi_add_lock);

      //如果总线上挂的设备已经有这个名字,则设置状态忙碌,并退出

      if (bus_find_device_by_name(&spi_bus_type, NULL, dev_name(&spi->dev))

                    != NULL) {

             dev_err(dev, "chipselect %d already in use\n",

                           spi->chip_select);

             status = -EBUSY;

             goto done;

      }

      status = spi_setup(spi); // 对spi_device的时钟等进行设置

      if (status < 0) {

             dev_err(dev, "can't %s %s, status %d\n",

                           "setup", dev_name(&spi->dev), status);

             goto done;

      }

      status = device_add(&spi->dev);  //添加到内核

      if (status < 0)

             dev_err(dev, "can't %s %s, status %d\n",

                           "add", dev_name(&spi->dev), status);

      else

             dev_dbg(dev, "registered child %s\n", dev_name(&spi->dev));

done:

      mutex_unlock(&spi_add_lock);

      return status;

}

下面跟踪spi_add_device中对spi_device的时钟等进行设置函数spi_setup

status = spi->master->setup(spi);而我们知道master所指向的setup在spi_s3c24xx.c中定义为 s3c24xx_spi_setup,我们跟踪这个函数会看到ret = s3c24xx_spi_update_state(spi, NULL);

接着看看s3c24xx_spi_update_state

static int s3c24xx_spi_update_state(struct spi_device *spi,

                               struct spi_transfer *t)

{

      struct s3c24xx_spi *hw = to_hw(spi);

      struct s3c24xx_spi_devstate *cs = spi->controller_state;

      unsigned int bpw;

      unsigned int hz;

      unsigned int div;

      unsigned long clk;

//设置了每字长的位数和发送速度

      bpw = t ? t->bits_per_word : spi->bits_per_word;

      hz  = t ? t->speed_hz : spi->max_speed_hz;

      if (!bpw)

             bpw = 8;

      if (!hz)

             hz = spi->max_speed_hz;

      if (bpw != 8) {

             dev_err(&spi->dev, "invalid bits-per-word (%d)\n", bpw);

             return -EINVAL;

      }

      if (spi->mode != cs->mode) {

             u8 spcon = SPCON_DEFAULT;

             if (spi->mode & SPI_CPHA)

                    spcon |= S3C2410_SPCON_CPHA_FMTB;

             if (spi->mode & SPI_CPOL)

                    spcon |= S3C2410_SPCON_CPOL_HIGH;

             cs->mode = spi->mode;

             cs->spcon = spcon;

      }

      if (cs->hz != hz) {

             clk = clk_get_rate(hw->clk);  //设置分频值

             div = DIV_ROUND_UP(clk, hz * 2) - 1;

             if (div > 255)

                    div = 255;

             dev_dbg(&spi->dev, "pre-scaler=%d (wanted %d, got %ld)\n",

                    div, hz, clk / (2 * (div + 1)));

             cs->hz = hz;

             cs->sppre = div;

      }

      return 0;

}

好了,到此为止,我们跟着spi_s3c24xx.c中s3c24xx_spi_probe已经走完了,看到了SPI接口设备注册的整个流程。

 

接着看看spidev.c

 我们知道spidev.c里面,我们注册了SPI接口设备驱动,并且添加了字符设备,为用户提供接口。

static const struct file_operations spidev_fops = {

      .owner = THIS_MODULE,

      .write =   spidev_write,

      .read =           spidev_read,

      .unlocked_ioctl = spidev_ioctl,

      .open =          spidev_open,

      .release = spidev_release,

};

我们一个个的看看这些函数,看看怎么实现数据传输的吧。

static int spidev_open(struct inode *inode, struct file *filp)

{

      struct spidev_data  *spidev;

      int                  status = -ENXIO;

      lock_kernel();

      mutex_lock(&device_list_lock);

      list_for_each_entry(spidev, &device_list, device_entry) {//从接口设备链表中获取接口设备

             if (spidev->devt == inode->i_rdev) {

                    status = 0;

                    break;

             }

      }

      if (status == 0) {

             if (!spidev->buffer) { //如果没申请缓冲区就去申请缓冲区

                    spidev->buffer = kmalloc(bufsiz, GFP_KERNEL);

                    if (!spidev->buffer) {

                           dev_dbg(&spidev->spi->dev, "open/ENOMEM\n");

                           status = -ENOMEM;

                    }

             }

             if (status == 0) {  //如果初次使用就把使用次数加一

                    spidev->users++;

                    filp->private_data = spidev;  //把spidev作为文件的私有数据

                    nonseekable_open(inode, filp);

             }

      } else

             pr_debug("spidev: nothing for minor %d\n", iminor(inode));

      mutex_unlock(&device_list_lock);

      unlock_kernel();

      return status;

}

这个open函数很简单,主要就是从接口设备链表中获取spidev设备,并对其初始化。好了,我们重点看看ioctl函数

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

{

      int                  err = 0;

      int                  retval = 0;

      struct spidev_data  *spidev;

      struct spi_device    *spi;

      u32                tmp;

      unsigned         n_ioc;

      struct spi_ioc_transfer   *ioc;

      if (_IOC_TYPE(cmd) != SPI_IOC_MAGIC)  //查看这个命令的幻数字段是否为'k'

             return -ENOTTY;

      //如果方向是用户空间从内核读,即内核向用户空间写,检查用户空间的地址是否有效

      if (_IOC_DIR(cmd) & _IOC_READ)

             err = !access_ok(VERIFY_WRITE,

                           (void __user *)arg, _IOC_SIZE(cmd));

      //如果方向是用户空间向内核写,即内核读用户空间,则检查用户空间的地址是否有效

      if (err == 0 && _IOC_DIR(cmd) & _IOC_WRITE)

             err = !access_ok(VERIFY_READ,

                           (void __user *)arg, _IOC_SIZE(cmd));

      if (err)

             return -EFAULT;

      spidev = filp->private_data;   //从文件的私有数据中获取spidev

      spin_lock_irq(&spidev->spi_lock);

      spi = spi_dev_get(spidev->spi);  //从spidev中获取spi_device

      spin_unlock_irq(&spidev->spi_lock);

      if (spi == NULL)

             return -ESHUTDOWN;

      mutex_lock(&spidev->buf_lock);

      switch (cmd) {  

      case SPI_IOC_RD_MODE:

   //因为已经进行了地址是否有效的检查

//所以这里使用__put_user,__get_user,__copy_from_user可以节省几个时钟周期

             retval = __put_user(spi->mode & SPI_MODE_MASK,

                                  (__u8 __user *)arg);

             break;

      case SPI_IOC_RD_LSB_FIRST:

             retval = __put_user((spi->mode & SPI_LSB_FIRST) ?  1 : 0,

                                  (__u8 __user *)arg);

             break;

      case SPI_IOC_RD_BITS_PER_WORD:

             retval = __put_user(spi->bits_per_word, (__u8 __user *)arg);

             break;

      case SPI_IOC_RD_MAX_SPEED_HZ:

             retval = __put_user(spi->max_speed_hz, (__u32 __user *)arg);

             break;

      case SPI_IOC_WR_MODE:  //设置SPI模式

             retval = __get_user(tmp, (u8 __user *)arg);

             if (retval == 0) {

                    u8    save = spi->mode;  //保存老的SPI模式

                    if (tmp & ~SPI_MODE_MASK) {

                           retval = -EINVAL;

                           break;

                    }

                    tmp |= spi->mode & ~SPI_MODE_MASK;

                    spi->mode = (u8)tmp;

                    retval = spi_setup(spi);  //设置新的SPI模式

                    if (retval < 0)

                           spi->mode = save;

                    else

                           dev_dbg(&spi->dev, "spi mode %02x\n", tmp);

             }

             break;

      case SPI_IOC_WR_LSB_FIRST:

             retval = __get_user(tmp, (__u8 __user *)arg);

             if (retval == 0) {

                    u8    save = spi->mode;

                    if (tmp)

                           spi->mode |= SPI_LSB_FIRST;

                    else

                           spi->mode &= ~SPI_LSB_FIRST;

                    retval = spi_setup(spi);

                    if (retval < 0)

                           spi->mode = save;

                    else

                           dev_dbg(&spi->dev, "%csb first\n",

                                         tmp ? 'l' : 'm');

             }

             break;

      case SPI_IOC_WR_BITS_PER_WORD:  //写入几比特代表一个字

             retval = __get_user(tmp, (__u8 __user *)arg);

             if (retval == 0) {

                    u8    save = spi->bits_per_word;

                    spi->bits_per_word = tmp;

                    retval = spi_setup(spi);

                    if (retval < 0)

                           spi->bits_per_word = save;

                    else

                           dev_dbg(&spi->dev, "%d bits per word\n", tmp);

             }

             break;

      case SPI_IOC_WR_MAX_SPEED_HZ:    //写传输速度

             retval = __get_user(tmp, (__u32 __user *)arg);

             if (retval == 0) {

                    u32  save = spi->max_speed_hz;

                    spi->max_speed_hz = tmp;

                    retval = spi_setup(spi);

                    if (retval < 0)

                           spi->max_speed_hz = save;

                    else

                           dev_dbg(&spi->dev, "%d Hz (max)\n", tmp);

             }

             break;

      default:

             if (_IOC_NR(cmd) != _IOC_NR(SPI_IOC_MESSAGE(0))

                           || _IOC_DIR(cmd) != _IOC_WRITE) {

                    retval = -ENOTTY;

                    break;

             }

             tmp = _IOC_SIZE(cmd);  //得到用户空间数据的大小

       //如果这些数据不能分成spi_ioc_transfer的整数倍,则不能进行传输

//spi_io_transfer是对spi_transfer的映射

             if ((tmp % sizeof(struct spi_ioc_transfer)) != 0) {

                    retval = -EINVAL;

                    break;

             }

             n_ioc = tmp / sizeof(struct spi_ioc_transfer); //计算出能分多少个spi_ioc_transfer

             if (n_ioc == 0)

                    break;

             ioc = kmalloc(tmp, GFP_KERNEL); //在内核中分配装载这些数据的内存空间

             if (!ioc) {

                    retval = -ENOMEM;

                    break;

             }

             if (__copy_from_user(ioc, (void __user *)arg, tmp)) { //把用户空间的数据拷贝过来

                    kfree(ioc);

                    retval = -EFAULT;

                    break;

             }

             retval = spidev_message(spidev, ioc, n_ioc); //进行数据传输

             kfree(ioc);

             break;

      }

      mutex_unlock(&spidev->buf_lock);

      spi_dev_put(spi);

      return retval;

}

在这个ioctl函数中,我们可以设置工作模式,可以设置bit/word,可以设置波特率,当然当其他命令进来会执行数据传输函数spidev_message,那么我们就跟踪看看这个函数如何实现数据传输的吧

static int spidev_message(struct spidev_data *spidev,

             struct spi_ioc_transfer *u_xfers, unsigned n_xfers)

{

      struct spi_message msg;

      struct spi_transfer  *k_xfers;

      struct spi_transfer  *k_tmp;

      struct spi_ioc_transfer *u_tmp;

      unsigned         n, total;

      u8                  *buf;

      int                  status = -EFAULT;

      spi_message_init(&msg); //初始化spi_message的tranfers链表头

      //分配n个spi_transfer的内存空间,一个spi_message由多个数据段spi_transfer组成

      k_xfers = kcalloc(n_xfers, sizeof(*k_tmp), GFP_KERNEL);

      if (k_xfers == NULL)

             return -ENOMEM;

      buf = spidev->buffer;  //获得缓冲区

      total = 0;

      //这个for循环的主要任务是将所有的spi_transfer组装成一个spi_message

      for (n = n_xfers, k_tmp = k_xfers, u_tmp = u_xfers;

                    n;

                    n--, k_tmp++, u_tmp++) {

             k_tmp->len = u_tmp->len;

             total += k_tmp->len;  //统计要传输数据的总量

             if (total > bufsiz) {

                    status = -EMSGSIZE;

                    goto done;

             }

             //spi_transfer是一个读写的buffer对,如果是要接收则把buffer给接收的rx_buf

             if (u_tmp->rx_buf) {

                    k_tmp->rx_buf = buf;

                    if (!access_ok(VERIFY_WRITE, (u8 __user *)

                                         (uintptr_t) u_tmp->rx_buf,

                                         u_tmp->len))

                           goto done;

             }

             //如果要传输,这个buffer给tx_buf使用,从用户空间拷过来要传输的数据

             if (u_tmp->tx_buf) {

                    k_tmp->tx_buf = buf;

                    if (copy_from_user(buf, (const u8 __user *)

                                         (uintptr_t) u_tmp->tx_buf,

                                  u_tmp->len))

                           goto done;

             }

             buf += k_tmp->len;   //指向下一段内存

             //最后一个transfer传输完毕是否会影响片选

             k_tmp->cs_change = !!u_tmp->cs_change;

             //每字长的字节数

             k_tmp->bits_per_word = u_tmp->bits_per_word;

             //一段数据传输完需要一定的时间等待

             k_tmp->delay_usecs = u_tmp->delay_usecs;

             //初始化传输速度

             k_tmp->speed_hz = u_tmp->speed_hz;

             //将spi_transfer通过它的transfer_list字段挂到spi_message的transfer队列上

             spi_message_add_tail(k_tmp, &msg);

      }

      status = spidev_sync(spidev, &msg); //调用底层的传输函数

      if (status < 0)

             goto done;

      buf = spidev->buffer;

      //把传输数据拷贝到用户空间打印出来,可以查看是否传输成功

      for (n = n_xfers, u_tmp = u_xfers; n; n--, u_tmp++) {

             if (u_tmp->rx_buf) {

                    if (__copy_to_user((u8 __user *)

                                  (uintptr_t) u_tmp->rx_buf, buf,

                                  u_tmp->len)) {

                           status = -EFAULT;

                           goto done;

                    }

             }

             buf += u_tmp->len;

      }

      status = total;

done:

      kfree(k_xfers);

      return status;

}

其实在上面这个spidev_message函数中,我们由spi_ioc_transfer映射到spi_transfer,同时初始化spi_message,将所有的spi_transfer组装成一个spi_message,最后调用调用底层的传输函数spidev_sync进行传输spi_message。好了,我们现在有必要看看这个底层传输函数

 

static ssize_t spidev_sync(struct spidev_data *spidev, struct spi_message *message)

{

      DECLARE_COMPLETION_ONSTACK(done); //声明并初始化一个完成量

      int status;

      message->complete = spidev_complete; //指定spi_message使用的唤醒完成量函数

      message->context = &done;

      spin_lock_irq(&spidev->spi_lock);

      if (spidev->spi == NULL)

             status = -ESHUTDOWN;

      else

             status = spi_async(spidev->spi, message); //调用spi核心中的函数进行数据传输

      spin_unlock_irq(&spidev->spi_lock);

      if (status == 0) {

             wait_for_completion(&done); //等待完成量被唤醒

             status = message->status;

             if (status == 0)

                    status = message->actual_length;

      }

      return status;

}

在spidev_sync函数中,我们调用核心层传输函数,并且设置完成量,等待完成量被唤醒。spidev_sync的入口参数是spidev_data和spi_message,该函数中,我们最终调用SPI核心层数据传输函数spi_async,入口参数是spi_device和spi_message,继续看

 

int spi_async(struct spi_device *spi, struct spi_message *message)

{

      struct spi_master *master = spi->master; //由spi_device获取spi_master

      if ((master->flags & SPI_MASTER_HALF_DUPLEX)

                    || (spi->mode & SPI_3WIRE)) {

             struct spi_transfer *xfer;

             unsigned flags = master->flags;

             list_for_each_entry(xfer, &message->transfers, transfer_list) {

                    if (xfer->rx_buf && xfer->tx_buf)

                           return -EINVAL;

                    if ((flags & SPI_MASTER_NO_TX) && xfer->tx_buf)

                           return -EINVAL;

                    if ((flags & SPI_MASTER_NO_RX) && xfer->rx_buf)

                           return -EINVAL;

             }

      }

      message->spi = spi;

      message->status = -EINPROGRESS;

      return master->transfer(spi, message);  //真正的传输函数调用

}

核心层只是个包装,其真正的传输函数还是需要其他层来实现的。spi_async中我们最终调用master->transfer(spi, message);而该函数在spi_bitbang.c的spi_bitbang_start函数中曾经执行bitbang->master->transfer = spi_bitbang_transfer,所以我们看看spi_bitbang_transfer

 

int spi_bitbang_transfer(struct spi_device *spi, struct spi_message *m)

{

      struct spi_bitbang   *bitbang;

      unsigned long         flags;

      int                  status = 0;

      m->actual_length = 0;

      m->status = -EINPROGRESS;

      //在spi_alloc_master函数中调用spi_master_set_devdata把struct s3c24xx_spi存放起来

//struct spi_bitbang正是struct s3c24xx_spi结构所包含的第一个结构

      bitbang = spi_master_get_devdata(spi->master);

      spin_lock_irqsave(&bitbang->lock, flags);

      if (!spi->max_speed_hz)

             status = -ENETDOWN;

      else {

             //把message加入到bitbang的等待队列中

             list_add_tail(&m->queue, &bitbang->queue);

             //把bitbang-work加入bitbang->workqueue中,调度运行

             queue_work(bitbang->workqueue, &bitbang->work);

      }

      spin_unlock_irqrestore(&bitbang->lock, flags);

      return status;

}

根据spi_bitbang_transfer我们知道,最终我们把bitbang-work加入bitbang->workqueue中,等待内核调用,而bitbang-work在spi_bitbang_start函数中定义为bitbang_work,我们现在看看bitbang_work,也就是我们把这个数据传输交给了等待队列,等待内核调用bitbang_work

static void bitbang_work(struct work_struct *work)

{

      struct spi_bitbang   *bitbang =container_of(work, struct spi_bitbang, work);

      unsigned long         flags;

      int                  do_setup = -1;

      int                  (*setup_transfer)(struct spi_device *,

                                  struct spi_transfer *);

      setup_transfer = bitbang->setup_transfer;

      spin_lock_irqsave(&bitbang->lock, flags);

      bitbang->busy = 1;       //设置成忙状态

      while (!list_empty(&bitbang->queue)) { //对bitqueue中的每一个spi_message进行处理

             struct spi_message *m;

             struct spi_device    *spi;

             unsigned         nsecs;

             struct spi_transfer  *t = NULL;

             unsigned         tmp;

             unsigned         cs_change;

             int                  status;

             m = container_of(bitbang->queue.next, struct spi_message,queue);v//从链表中获取

             list_del_init(&m->queue);      //从链表中删除spi_message

             spin_unlock_irqrestore(&bitbang->lock, flags);

             nsecs = 100;

             spi = m->spi;        

             tmp = 0;

             cs_change = 1;

             status = 0;

             list_for_each_entry (t, &m->transfers, transfer_list) { //获取spi_transfer

                    if (t->speed_hz || t->bits_per_word)

                           do_setup = 1;

                    if (do_setup != 0) {

                           if (!setup_transfer) {

                                  status = -ENOPROTOOPT;

                                  break;

                           }

                           status = setup_transfer(spi, t);  //传输前期准备

                           if (status < 0)

                                  break;

                    }

                    if (cs_change) {

                           bitbang->chipselect(spi, BITBANG_CS_ACTIVE);

                           ndelay(nsecs);

                    }

                    cs_change = t->cs_change;

                    if (!t->tx_buf && !t->rx_buf && t->len) {

                           status = -EINVAL;

                           break;

                    }

                    if (t->len) {

                           if (!m->is_dma_mapped)

                                  t->rx_dma = t->tx_dma = 0;

                           status = bitbang->txrx_bufs(spi, t); //调用bitbang->txrx_bufs进行数据传输

                    }

                    if (status > 0)

                           m->actual_length += status;

                    if (status != t->len) {

                           if (status >= 0)

                                  status = -EREMOTEIO;

                           break;

                    }

                    status = 0;

                    if (t->delay_usecs)

                           udelay(t->delay_usecs);

                    if (!cs_change)

                           continue;

                    if (t->transfer_list.next == &m->transfers) //链表遍历完毕,退出循环

                           break;

                    ndelay(nsecs);

                    bitbang->chipselect(spi, BITBANG_CS_INACTIVE);

                    ndelay(nsecs);

             }

             m->status = status;  //所有spi_message传输完毕

             m->complete(m->context);  //传输完成,唤醒刚才的那个完成变量,调用完成函数

             if (do_setup == 1)

                    setup_transfer(spi, NULL);

             do_setup = 0;

             if (!(status == 0 && cs_change)) {

                    ndelay(nsecs);

                    bitbang->chipselect(spi, BITBANG_CS_INACTIVE);

                    ndelay(nsecs);

             }

             //重新获取自旋锁,遍历工作者队列的下一个工作

             spin_lock_irqsave(&bitbang->lock, flags);

      }

      bitbang->busy = 0;  //处理完毕,清除忙标志

      spin_unlock_irqrestore(&bitbang->lock, flags);

}

在bitbang_work中,我们主要知道要调用一个bitbang->txrx_bufs的传输函数,在spi_s3c24xx.c中定义为s3c24xx_spi_txrx。另外我们还需要关注一下传输完毕后调用了m->complete(m->context),也就是调用完成函数spidev_complete,这里面只有一条代码complete(arg);所以当数据传输完毕调用完成函数就可以返回到spidev_sync中的wait_for_completion(&done);好了,我们只剩下跟踪s3c24xx_spi_txrx了

 

static int s3c24xx_spi_txrx(struct spi_device *spi, struct spi_transfer *t)

{

      struct s3c24xx_spi *hw = to_hw(spi);

      dev_dbg(&spi->dev, "txrx: tx %p, rx %p, len %d\n",

             t->tx_buf, t->rx_buf, t->len);

      hw->tx = t->tx_buf;   //发送指针

      hw->rx = t->rx_buf;   //接收指针  

      hw->len = t->len;    //需要发送/接收的数目

      hw->count = 0;     //存放实际spi传输的数据数目

      init_completion(&hw->done); //初始化了完成量

 //只需发送第一个字节(如果发送为空,则发送0xff),中断中就会自动发送完其他字节(并//接受数据) 直到所有数据发送完毕且所有数据接收完毕才返回 

      writeb(hw_txbyte(hw, 0), hw->regs + S3C2410_SPTDAT);

      wait_for_completion(&hw->done); //等待完成量被唤醒

      return hw->count;

}

static inline unsigned int hw_txbyte(struct s3c24xx_spi *hw, int count)

{

//如果还有数据没接收完且要发送的数据经已发送完毕,发送空数据0xFF   

      return hw->tx ? hw->tx[count] : 0;

}

下面来分析下中断函数s3c24xx_spi_irq

static irqreturn_t s3c24xx_spi_irq(int irq, void *dev)

{

      struct s3c24xx_spi *hw = dev;

      unsigned int spsta = readb(hw->regs + S3C2410_SPSTA); //读取spi的状态寄存器

      unsigned int count = hw->count;

      if (spsta & S3C2410_SPSTA_DCOL) { //检测冲突

             dev_dbg(hw->dev, "data-collision\n");

             complete(&hw->done);  //唤醒完成量

             goto irq_done;

      }

      if (!(spsta & S3C2410_SPSTA_READY)) { //检测设备忙

             dev_dbg(hw->dev, "spi not ready for tx?\n");

             complete(&hw->done); //唤醒完成量

             goto irq_done;

      }

      hw->count++;

      if (hw->rx)

             hw->rx[count] = readb(hw->regs + S3C2410_SPRDAT); //接收数据

      if (count < hw->len) //如果count小于需要发送或接收数据的数目,发送其他数据

             writeb(hw_txbyte(hw, count), hw->regs + S3C2410_SPTDAT);

      else

             complete(&hw->done); //发送接收完毕,通知s3c24xx_spi_txrx函数

 irq_done:

      return IRQ_HANDLED;

}

注意SPI总线数据传输时候,写和读数据是分开两个阶段来进行的,写数据的时候不读数据,读数据的时候发送空数据0xff,这样由主控产生时钟信号。

 

热点排行