Chinaunix首页 | 论坛 | 博客
  • 博客访问: 670673
  • 博文数量: 207
  • 博客积分: 1743
  • 博客等级: 上尉
  • 技术积分: 2044
  • 用 户 组: 普通用户
  • 注册时间: 2012-08-20 14:36
文章分类

全部博文(207)

文章存档

2016年(24)

2015年(10)

2014年(50)

2013年(45)

2012年(78)

分类: LINUX

2012-08-22 17:39:29

所有挂接到SPI接口的SPI设备都是作为字符设备存在于内核,这些字符设备的操作函数集在文件spidev.c中实现。

static 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 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)
  return -ENOTTY;
//检测命令的幻数是否为SPI_IOC_MAGIC("k")_IOC_TYPE(cmd)获取命令幻数

 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;
 spin_lock_irq(&spidev->spi_lock);
 spi = spi_dev_get(spidev->spi);//获取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://读取spi传输模式
  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;

   if (tmp & ~SPI_MODE_MASK) {
    retval = -EINVAL;
    break;
   }

   tmp |= spi->mode & ~SPI_MODE_MASK;
   spi->mode = (u8)tmp;
   retval = spi_setup(spi);//将设定的模式写入控制寄存器中                       (1)
    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);//将设定的模式写入控制寄存器中                    (1)
   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);//将设定的模式写入寄存器中                         (1)
   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);//将设定的模式写入寄存器中                        (1)
   if (retval < 0)
    spi->max_speed_hz = save;
   else
    dev_dbg(&spi->dev, "%d Hz (max)\n", tmp);
  }
  break;

 default:
  /* segmented and/or full-duplex I/O request */
  if (_IOC_NR(cmd) != _IOC_NR(SPI_IOC_MESSAGE(0))//查看是否为数据传输命令
    || _IOC_DIR(cmd) != _IOC_WRITE) {
   retval = -ENOTTY;
   break;
  }
//_IOC_SIZE(cmd)从命令参数中解析出用户数据大小
  tmp = _IOC_SIZE(cmd);
  if ((tmp % sizeof(struct spi_ioc_transfer)) != 0) {
   retval = -EINVAL;
   break;
  }
  n_ioc = tmp / sizeof(struct spi_ioc_transfer); //将要传输的数据分成n个传输数据段
  if (n_ioc == 0)
   break;

  /* copy into scratch area */
  ioc = kmalloc(tmp, GFP_KERNEL);//获取n个数据段的数据管理结构体的内存空间
  if (!ioc) {
   retval = -ENOMEM;
   break;
  }
  if (__copy_from_user(ioc, (void __user *)arg, tmp)) {//从用户空间获取数据管理结构体的初始化值
   kfree(ioc);
   retval = -EFAULT;
   break;
  }

  /* translate to spi_message, execute */
  retval = spidev_message(spidev, ioc, n_ioc);//数据传输                             (2)
  kfree(ioc);
  break;
 }

 mutex_unlock(&spidev->buf_lock);
 spi_dev_put(spi);
 return retval;
}

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

(1)

//通过调用函数spi->master->setup()来设置SPI模式。

static inline int
spi_setup(struct spi_device *spi)
{
 return spi->master->setup(spi);
}

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

master->setup成员被初始化成函数s3c24xx_spi_setup()。这一工作是在函数

s3c24xx_spi_probe()中进行的

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

函数s3c24xx_spi_setup()是在文件linux/drivers/spi/spi_s3c24xx.c中实现的。

static int s3c24xx_spi_setup(struct spi_device *spi)
{
 int ret;

 if (!spi->bits_per_word)
  spi->bits_per_word = 8;     //8位数据传输。

。。。。。。

 ret = s3c24xx_spi_setupxfer(spi, NULL);  @@@@
。。。。。。

 return 0;
}

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

static int s3c24xx_spi_setupxfer(struct spi_device *spi,  struct spi_transfer *t)
{
 struct s3c24xx_spi *hw = to_hw(spi);
 unsigned int bpw;
 unsigned int hz;
 unsigned int div;

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

。。。。。。

 div = clk_get_rate(hw->clk) / hz;

 div /= 2;

 if (div > 0)
  div -= 1;

 if (div > 255)
  div = 255;
 writeb(div, hw->regs + S3C2410_SPPRE);               //设置波特率。

//bitbang包含了数据传输函数,在数据传输时忙碌标识bitbang.busy置1,也就是在数据传输过程中不能改变数据传输模式。
 if (!hw->bitbang.busy) { 
  hw->bitbang.chipselect(spi, BITBANG_CS_INACTIVE);  @@@@
  /* need to ndelay for 0.5 clocktick ? */
 }
 spin_unlock(&hw->bitbang.lock);

 return 0;
}

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

static void s3c24xx_spi_chipsel(struct spi_device *spi, int value)
{
 struct s3c24xx_spi *hw = to_hw(spi);
 unsigned int cspol = spi->mode & SPI_CS_HIGH ? 1 : 0;
 unsigned int spcon;

 switch (value) {
 case BITBANG_CS_INACTIVE: 

//上面对本函数的调用传递的参数是BITBANG_CS_INACTIVE。可以看出上层函数对数据传输模式的设置,

//只能改变spi->mode字段,而不能通过函数spi->master->setup(spi);的调用而将要设置的传输模式写入硬件。

//这或许是linux中的SPI子系统还不够完善吧。但spi->mode的值最终是会被写入SPI的控制寄存器中的,

// hw->bitbang.chipselect(spi, BITBANG_CS_ACTIVE);  的时候。在后面会遇到。
  hw->set_cs(hw->pdata, spi->chip_select, cspol^1);
  break;

 case BITBANG_CS_ACTIVE:
  spcon = readb(hw->regs + S3C2410_SPCON);

  if (spi->mode & SPI_CPHA)
   spcon |= S3C2410_SPCON_CPHA_FMTB;
  else
   spcon &= ~S3C2410_SPCON_CPHA_FMTB;

  if (spi->mode & SPI_CPOL)
   spcon |= S3C2410_SPCON_CPOL_HIGH;
  else
   spcon &= ~S3C2410_SPCON_CPOL_HIGH;

  spcon |= S3C2410_SPCON_ENSCK;

  writeb(spcon, hw->regs + S3C2410_SPCON);
  hw->set_cs(hw->pdata, spi->chip_select, cspol);

  break;
 }
}

(2)

//该函数的功能是将用户空间传来的数据段包装成spi_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);
 k_xfers = kcalloc(n_xfers, sizeof(*k_tmp), GFP_KERNEL);//每个 spi_transfer代表一段要传输的数据
 if (k_xfers == NULL)
  return -ENOMEM;
 buf = spidev->buffer;
 total = 0;
 for (n = n_xfers, k_tmp = k_xfers, u_tmp = u_xfers;
   n;
   n--, k_tmp++, u_tmp++) {
  k_tmp->len = u_tmp->len;
//将要传输的数据分成n个数据段每个数据段用一个spi_transfer管理,u_xfers为用户空间传来的数据段
  total += k_tmp->len;//要传输的数据总量
  if (total > bufsiz) {
   status = -EMSGSIZE;
   goto done;
  }

  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;
  }
  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;//指向下一段内存

  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;//在启动函数setup_transfer中用到

  spi_message_add_tail(k_tmp, &msg);//将用于数据传输的数据段挂在msg上
 }

 status = spidev_sync(spidev, &msg);//调用底层函数进行数据传输                 @@@@
 if (status < 0)
  goto done;

 /* copy any rx data out of bounce buffer */
 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;
}

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

static ssize_t
spidev_sync(struct spidev_data *spidev, struct spi_message *message)
{
 DECLARE_COMPLETION_ONSTACK(done);   //定义并初始化一个完成量
 int status;

//spi_message上挂接的所有数据段传输完毕时该函数调用函数complete(arg);通知数据传输完成。

 message->complete = spidev_complete;    
 message->context = &done;    //在底层的数据传输函数中会调用函数spidev_complete来通知数据传输完成,在此留一标记   

 spin_lock_irq(&spidev->spi_lock);
 if (spidev->spi == NULL)
  status = -ESHUTDOWN;
 else
  status = spi_async(spidev->spi, message);    @@@@
 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;
}

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

//通过调用函数spi->master->transfer()将数据携带结构体message向下层传递。

static inline int
spi_async(struct spi_device *spi, struct spi_message *message)
{
 message->spi = spi;
 return spi->master->transfer(spi, message);
}

函数spi->master->transfer()在函数spi_bitbang_start()中被初始化为函数spi_bitbang_transfer()如下

 if (!bitbang->master->transfer)
  bitbang->master->transfer = spi_bitbang_transfer;

函数spi_bitbang_transfer()又在函数s3c24xx_spi_probe()中被调用。

函数spi_bitbang_transfer()在文件spi_bitbang.c中实现。

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;

 bitbang = spi_master_get_devdata(spi->master);

 spin_lock_irqsave(&bitbang->lock, flags);
 if (!spi->max_speed_hz)
  status = -ENETDOWN;
 else {

//将携带数据的结构体spi_message挂到bitbang->queue上。每一次数据传输都将要传输的数据包装成结构体spi_message传递

//下来。bitbang是完成数据传输的重要结构体。
  list_add_tail(&m->queue, &bitbang->queue); 

 //将该传输任务添加到工作队列头bitbang->workqueue。接下来将调用任务处理函数进一步数据处理。
  queue_work(bitbang->workqueue, &bitbang->work);

 }
 spin_unlock_irqrestore(&bitbang->lock, flags);

 return status;
}

 

在此不得不讨论一下结构体,这是一个完成数据传输的重要结构体。

struct spi_bitbang {
 struct workqueue_struct *workqueue; //工作队列头。
 struct work_struct work; //每一次数据传输都传递下来一个spi_message,都向工作队列头添加一个任务。

。。。。。。

//挂接spi_message,如果上一次的spi_message还没处理完接下来的spi_message就挂接在 queue上等待处理。
 struct list_head queue; 
 u8   busy;  //忙碌标识。

。。。。。。

 struct spi_master *master;

//一下三个函数都是在函数s3c24xx_spi_probe()中被初始化的。

 int (*setup_transfer)(struct spi_device *spi,struct spi_transfer *t);  //设置数据传输波特率

 void (*chipselect)(struct spi_device *spi, int is_on);  //将数据传输模式写入控制寄存器。

//向SPTDAT中写入要传输的第一个数据,数据传输是通过中断方式完成的,只要进行一次中断触发,以后向SPTDAT中写数据

//的工作就在中断处理函数中进行。

 int (*txrx_bufs)(struct spi_device *spi, struct spi_transfer *t); 
。。。。。。
};

 

 

数据传输是SPI接口的任务,结构体master代表了一个接口,当一个spi_message从上层函数传递下来时,

master的成员函数bitbang->master->transfer将该数据传输任务添加到工作队列头。

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

函数bitbang->master->transfer()在上面已经讲解。

工作队列struct workqueue_struct *workqueue;的创建和 struct work_struct work的初始化都是在函数

spi_bitbang_start()中进行的。

INIT_WORK(&bitbang->work, bitbang_work); //bitbang_work是任务处理函数

bitbang->workqueue = create_singlethread_workqueue(dev_name(bitbang->master->dev.parent));

任务处理函数如下

static void bitbang_work(struct work_struct *work)
{
 struct spi_bitbang *bitbang =
  container_of(work, struct spi_bitbang, work);//获取bitbang结构体
 unsigned long  flags;

 spin_lock_irqsave(&bitbang->lock, flags);
 bitbang->busy = 1;
//每一次数据传输都传递下来一个spi_message,如果上一次的spi_message
//还没处理完接下来的spi_message就挂接在 queue上等待处理 。
 while (!list_empty(&bitbang->queue)) {
  struct spi_message *m;
  struct spi_device *spi;
  unsigned  nsecs;
  struct spi_transfer *t = NULL;
  unsigned  tmp;
  unsigned  cs_change;
  int   status;
  int   (*setup_transfer)(struct spi_device *,
      struct spi_transfer *);

  m = container_of(bitbang->queue.next, struct spi_message,
    queue);//获取spi_message结构体
  list_del_init(&m->queue);
  spin_unlock_irqrestore(&bitbang->lock, flags);
  nsecs = 100;//spi传输模式设置和hw->set_cs之后的延时

  spi = m->spi;
  tmp = 0;
  cs_change = 1;//多段数据传输时,数据管理结构体挂于链表上,cs_change = 1表征最后一段要传输的数据
  status = 0;
  setup_transfer = NULL;
//每次数据传输,都将要传输的数据分成多个数据段,这些数据段由数据管理结构体
//spi_transfer来管理。结构体spi_transfer又将挂在m->transfers。也就是说
//每次数据传输都将要传输的数据包装成一个结构体spi_message传输下来。

  list_for_each_entry (t, &m->transfers, transfer_list) {//遍历spi_message上的每一个数据段

   /* override or restore speed and wordsize */
   if (t->speed_hz || t->bits_per_word) {
//bitbang->setup_transfer在函数s3c24xx_spi_probe() 中初始化。
//该函数的工作是设置数据传输波特率。   

    setup_transfer = bitbang->setup_transfer; 
    if (!setup_transfer) {
     status = -ENOPROTOOPT;
     break;
    }
   }
   if (setup_transfer) {
    status = setup_transfer(spi, t);
    if (status < 0)
     break;
   }
   if (cs_change) {
//多端数据传输时只需第一段数据传输时调用以下模式设置,将spi->mode
//写入SPI的模式控制寄存器。并调用函数hw->set_cs,片选设置。

//在函数讲解函数spidev_ioctl()中的模式设置时讲到过。在那里  调用的函数chipselect(spi, BITBANG_CS_INACTIVE);

//传递的传输是BITBANG_CS_INACTIVE是不能将数据传输模式spi->mode写入SPI控制寄存器的,不过在那里设置了

//spi->mode的值。
    bitbang->chipselect(spi, BITBANG_CS_ACTIVE);
    ndelay(nsecs);
   }
   cs_change = t->cs_change;//获取该数据段的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);           @@@@@@@@@@@@@@@@@@          
   }
   if (status > 0)
    m->actual_length += status;//计算已传输数据长度
   if (status != t->len) {
    /* always report some kind of error */
    if (status >= 0)
     status = -EREMOTEIO;
    break;
   }
   status = 0;

   /* protocol tweaks before next transfer */
   if (t->delay_usecs)//延时等待一段数据传输完,因为一段数据要经过多次中断传输
    udelay(t->delay_usecs);

   if (!cs_change)//多段数据传输时t->cs_change为0表示下面还有未传输数据否者判断遍历是否结束
    continue;
   if (t->transfer_list.next == &m->transfers)
    break;
   ndelay(nsecs);
   bitbang->chipselect(spi, BITBANG_CS_INACTIVE);//将spi设为INACTIVE
   ndelay(nsecs);
  }

  m->status = status;
  m->complete(m->context);// 通知一次数据传输完成                                                                                                                  

  /* restore speed and wordsize */
  if (setup_transfer)
   setup_transfer(spi, NULL);
  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);
}


函数hw->bitbang.txrx_bufs()在函数s3c24xx_spi_probe()中被初始化为s3c24xx_spi_txrx()。

hw->bitbang.txrx_bufs      = 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或hw->rx 中。

 hw->tx = t->tx_buf;
 hw->rx = t->rx_buf;
 hw->len = t->len;
 hw->count = 0;

 init_completion(&hw->done);

/*

函数hw_txbyte()的实现如下

static inline unsigned int hw_txbyte(struct s3c24xx_spi *hw, int count)
{
 return hw->tx ? hw->tx[count] : 0;
}

将要传输的数据段的第一个数据写入SPI数据寄存器S3C2410_SPTDAT。即便是数据接收也得向数据寄存器写入数据

才能触发一次数据的传输。只需将该数据段的第一个数据写入数据寄存器就可以触发数据传输结束中断,以后的数据

就在中断处理函数中写入数据寄存器。

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

 wait_for_completion(&hw->done);

 return hw->count;
}

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

数据传输结束中断

static irqreturn_t s3c24xx_spi_irq(int irq, void *dev)
{
 struct s3c24xx_spi *hw = dev;
 unsigned int spsta = readb(hw->regs + S3C2410_SPSTA);
 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);  //接收数据

 count++;

 if (count < hw->len)
  writeb(hw_txbyte(hw, count), hw->regs + S3C2410_SPTDAT); //写入数据。
 else
  complete(&hw->done);

 irq_done:
 return IRQ_HANDLED;
}

阅读(983) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~