ARM9 ADS8344 SPI驱动移植 (四)

来源:互联网 发布:最新网页制作软件 编辑:程序博客网 时间:2024/06/05 13:29

注:本节大量引用和参考了博客http://blog.csdn.net/yj4231/article/details/7755709的内容


step1:

我们现在就通过ADS8344的ioctl函数可分析一下整个数据是如何传递和接受的

static int ads8344_ioctl(struct inode *inode, struct file *file,    unsigned int cmd, unsigned long arg)
{
    send2user=0X00000000;
    u8 channel,revData[AVERAGETIMES][3];
    u8 control_word;
    int ret = 0, i,k,z;
    int temp[AVERAGETIMES][3]; 
    pr_debug("ioctl cmd %d is issued...\n", cmd);

    if((cmd>7)||(cmd<0))
    return -EINVAL;

    switch (cmd) {
         case 0: channel = 0; break;
         case 1: channel = 4; break;
         case 2: channel = 1; break;
         case 3: channel = 5; break;
         
         case 4: channel = 2; break;
         case 5: channel = 6; break;
         case 6: channel = 3; break;
         case 7: channel = 7; break;
         
        default: channel = 0; break;
    }

    mutex_lock(&ads8344_data.lock);

    for (i = 0; i < AVERAGETIMES; i++) {
            control_word = (1u << 7)|(channel << 4)|(1u << 2)|0x3;


        /*发送命令control_word,同时ADS8344中接受3个字节的数据存入revData[i] */
            ret = spi_write_then_read(ads8344_data.spi, &control_word, 1,revData[i],3); 
            if (ret)
            break;
    }

    mutex_unlock(&ads8344_data.lock);

    if (ret)
    return -EIO;

    for(k=0;k<AVERAGETIMES;k++)
    {
        temp[k][0]=(int)revData[k][0];
        temp[k][1]=(int)revData[k][1];
        temp[k][2]=(int)revData[k][2];
    }

    for(z=0;z<AVERAGETIMES;z++)
    {
        send2user += ((temp[z][0]<<16)|(temp[z][1]<<8)|temp[z][2])>>7;
    }

    send2user= send2user>>2;
    send2user &=0xffff; 
    return ret;
}


step2:

上面代码中最重要的就是 spi_write_then_read(ads8344_data.spi, &control_word, 1,revData[i],3)。它实现对对ADS8344设备的读写。我追踪一下这个函数。

int spi_write_then_read(struct spi_device *spi,
const u8 *txbuf, unsigned n_tx,
u8 *rxbuf, unsigned n_rx)
{
static DEFINE_MUTEX(lock);

int status;
struct spi_messagemessage;
struct spi_transferx[2];
u8 *local_buf;

if ((n_tx + n_rx) > SPI_BUFSIZ)
return -EINVAL;

spi_message_init(&message);    //初始化message
memset(x, 0, sizeof x);
if (n_tx) {
x[0].len = n_tx;
spi_message_add_tail(&x[0], &message);   //将x[0]加入传输的message
}
if (n_rx) {
x[1].len = n_rx;
spi_message_add_tail(&x[1], &message); //将x[1]加入传输的message
}

/* ... unless someone else is using the pre-allocated buffer */
if (!mutex_trylock(&lock)) {
local_buf = kmalloc(SPI_BUFSIZ, GFP_KERNEL);
if (!local_buf)
return -ENOMEM;
} else
local_buf = buf;

memcpy(local_buf, txbuf, n_tx);
x[0].tx_buf = local_buf;                                                    //分配好发送与接送数据的地址和把要发送的数据写入x[0]
x[1].rx_buf = local_buf + n_tx;

/* do the i/o */
status = spi_sync(spi, &message);                             //实现数据的接受与发送
if (status == 0)
memcpy(rxbuf, x[1].rx_buf, n_rx);

if (x[0].tx_buf == buf)
mutex_unlock(&lock);
else
kfree(local_buf);

return status;

}

这个函数主要做了两件事一是分配好struct spi_message message; struct spi_transfer x[2]; 同时将发送的数据填充进去。另一件就是调用 spi_sync(spi, &message)函数实现的数据的收发。现在我们追踪一下spi_sync()函数。


int spi_sync(struct spi_device *spi, struct spi_message *message)
{
DECLARE_COMPLETION_ONSTACK(done);   /*创建completion*/
int status;


message->complete = spi_complete;           /*定义complete方法*/
message->context = &done;                          /*complete方法的参数*/  
status = spi_async(spi, message);                /*实现数据的收发*/
if (status == 0) {
wait_for_completion(&done);              /*在bitbang_work中调用complete方法来唤醒*/
status = message->status;
}
message->context = NULL;
return status;
}

这个函数就中最重要的就是调用了spi_async(spi, message)函数,接下来我们继续追踪一下spi_async()这个函数。


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


上面这个函数调用了spi->master->transfer(spi, message)这个函数。但是spi->master->transfer(spi, message)是哪个函数呢。实际上它指向的是spi_bitbang_transfer函数。那它是如何发生关联的呢?之前我们提到过通过指针的相互赋值使 

  {

        struct s3c24xx_spi *hw;
struct spi_master *master 

       struct spi_bitbangbitbang

  }

   这几个数据结构可以相互查找到对方。


  (1)
       在  s3c24xx_spi_probe(struct platform_device *pdev)函数中有
hw->master = spi_master_get(master);

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

   (2)

        在spi_bitbang_start(struct spi_bitbang *bitbang)

    bitbang->master->transfer = spi_bitbang_transfer

  (3)

    spi_alloc_device(struct spi_master *master)函数中

    spi->master = master;


    整理以上关系可得    hw->bitbang.master  = hw->master =spi->master=master;

   所以 可有   bitbang->master->transfer = spi_bitbang_transfer

   得到spi->master->transfer(spi, message) = spi_bitbang_transfer(spi, message) 


 step3:

  接着我们继续追踪这个spi_bitbang_transfer(spi, message)函数

  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);                  /*获取bitbang结构*/

spin_lock_irqsave(&bitbang->lock, flags);
if (!spi->max_speed_hz)
status = -ENETDOWN;
else {
list_add_tail(&m->queue, &bitbang->queue);                      /*将message添加到bitbang的queue链表中*/
queue_work(bitbang->workqueue, &bitbang->work);     /*提交工作到工作队列*/ 

spin_unlock_irqrestore(&bitbang->lock, flags);

return status;
}


 这个函数的主要是将message添加到bitbang的queue链表中和提交工作队列。工作和工作队列的初始化的工作在

 spi_bitbang_start(struct spi_bitbang *bitbang)函数中完成。代码如下:

                        

               INIT_WORK(&bitbang->work, bitbang_work)

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

   

   所要当内核空闲的时候就会调用bitbang_work函数完成数据的收发。



step4:

    接着我们继续追踪这个 bitbang_work函数做了什么工作。

   static void bitbang_work(struct work_struct *work)
{
struct spi_bitbang*bitbang =
container_of(work, struct spi_bitbang, work);      /*获取spi_bitbang*/

          unsigned  longflags;

spin_lock_irqsave(&bitbang->lock, flags);
bitbang->busy = 1;
while (!list_empty(&bitbang->queue)) {    /*遍历bitbang->queue链表上的spi_message,进行数据收发*/
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,      /*获取spi_message*/
queue);
list_del_init(&m->queue);                   /*将获取spi_message从bitbang->queue链表删去*/
spin_unlock_irqrestore(&bitbang->lock, flags);

nsecs = 100;

spi = m->spi;
tmp = 0;
cs_change = 1;
status = 0;
setup_transfer = NULL;

                       /*遍历,获取所有的spi_transfer*/ 
list_for_each_entry (t, &m->transfers, transfer_list) {


/* override or restore speed and wordsize */
if (t->speed_hz || t->bits_per_word) {                     /*如果这两个参数有任何一个已经设置了,本例中没有定义*/
setup_transfer = bitbang->setup_transfer;      
if (!setup_transfer) {
status = -ENOPROTOOPT;
break;
}
}
if (setup_transfer) {                                           /*本例中为NULL*/ 
status = setup_transfer(spi, t);
if (status < 0)
break;
}


/* set up default clock polarity, and activate chip;
* this implicitly updates clock and spi modes as
* previously recorded for this device via setup().
* (and also deselects any other chip that might be
* selected ...)
*/
if (cs_change) {          /*初值为1*/ 

                /*即调用s3c24xx_spi_chipsel,激活CS信号,写寄存器,设置SPI模式*/  
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;            /*不使用DMA*/
status = bitbang->txrx_bufs(spi, t);      /*即调用s3c24xx_spi_txrx,开始发送数据,status为已发送数据的大小*/
}
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)               /*判断是否需要禁止CS,为1表示要求在两次数据传输之间禁止CS*/
continue;
if (t->transfer_list.next == &m->transfers)    /*当message.queue链表上已没有transfer,表示所有的                                                         transfer已传输完毕*/ 
break;


ndelay(nsecs);
bitbang->chipselect(spi, BITBANG_CS_INACTIVE);     /*禁止CS*/ 
ndelay(nsecs);
}   /*遍历spi_transfer结束*/

m->status = status;
m->complete(m->context);   /*调用complete,一个message处理完毕*/

/* 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);
}


以上函数主要调用了status = bitbang->txrx_bufs(spi, t); 进行了数据的收发。



step5:

bitbang->txrx_bufs(spi, t)指向的是s3c24xx_spi_txrx()函数。继续追踪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;

init_completion(&hw->done);

/* send the first byte */
writeb(hw_txbyte(hw, 0), hw->regs + S3C2410_SPTDAT);  /*发送第一个数据,tx[0]*/  

wait_for_completion(&hw->done);

return hw->count;
}


在s3c24xx_spi_txrx函数中,首先发送了待发送数据中的第一个字节,随后就调用wait_for_completion来等待剩余的数据发送完成。

NOTE:这里的completion是master驱动层的,spi设备驱动也有一个completion,用于IO同步,不要混淆。

当第一个数据发送完成以后,SPI中断产生,开始执行中断服务程序。在中断服务程序中,将判断是否需要读取数据,如果是则从寄存器中读取数据。

NOTE:如果是使用read系统调用,那么在此发送的数据将是0。

随后发送下一个数据,直到数据发送完成。发送完成后调用complete,使在s3c24xx_spi_txrx的wait_for_completion得以返回。接着,s3c24xx_spi_txrx就将返回已发送的字节数。

NOTE:其实该中断服务子程序实现了全双工数据的传输,只不过特定于具体的系统调用,从而分为了半双工读和写。


step6:

这里再将中断函数s3c24xx_spi_irq贴出。同时回答了上一将提出的问题,那就是中断函数何时别调用。好了一个spi驱动大致完成了。


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;
}



注:本节大量引用和参考了博客http://blog.csdn.net/yj4231/article/details/7755709

0 0
原创粉丝点击