您的位置:首页 > 运维架构 > Linux

Linux驱动修炼之道-SPI驱动框架源码分析(下-续)

2012-09-22 18:36 666 查看
spi_async在spi.h中定义的:

view
plain

<span style="font-size:18px;">static inline int

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

{

message->spi = spi;

return spi->master->transfer(spi, message);

}

</span>

这里的master->transfer是在spi_bitbang_start中进行赋值的:

bitbang->master->transfer=
spi_bitbang_transfer;

看spi_bitbang_transfer的实现:

view
plain

<span style="font-size:18px;">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;

}

EXPORT_SYMBOL_GPL(spi_bitbang_transfer);

</span>

分析工作队列的处理函数:



view
plain

<span style="font-size:18px;">static void bitbang_work(struct work_struct *work)

{

struct spi_bitbang *bitbang =

container_of(work, struct spi_bitbang, work);

unsigned long flags;

spin_lock_irqsave(&bitbang->lock, flags);

/*设置成忙状态*/

bitbang->busy = 1;

/*对bitqueue中的每一个spi_message进行处理*/

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 = m->spi;

tmp = 0;

cs_change = 1;

status = 0;

setup_transfer = NULL;

/*对spi_message的transfers上的每个spi_transfer进行处理*/

list_for_each_entry (t, &m->transfers, transfer_list) {

。。。。。。。。。。。。。。。。。

if (t->len) {

if (!m->is_dma_mapped)

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

/*调用bitbang->txrx_bufs进行数据的传输,bitbang->txrx_bufs = s3c24xx_spi_txrx;这个在s3c24xx_spi_probe中进行赋值的*/

<span style="color:#ff0000;">status = bitbang->txrx_bufs(spi, t);</span>

}

。。。。。。。。。。。。。。。。

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

}

</span>

这个工作队列的处理函数中调用了spi controller driver中的传输函数:

view
plain

<span style="font-size:18px;">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)

{

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

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

}

</span>

下面来分析中断函数:

view
plain

<span style="font-size:18px;">static irqreturn_t s3c24xx_spi_irq(int irq, void *dev)

{

struct s3c24xx_spi *hw = dev;

/*读取spi的状态寄存器*/

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

/*如果count小于需要发送或接收数据的数目,发送其他数据*/

if (count < hw->len)

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

else

/*唤醒完成量,通知s3c24xx_spi_txrx函数*/

complete(&hw->done);

irq_done:

return IRQ_HANDLED;

}

</span>

至此spi数据传输过程完成,如果不想为自己的SPI设备写驱动,那么可以用Linux自带的spidev.c提供的驱动程序,只要在登记时,把设备名设置成spidev就可以了。spidev.c会在device目录下自动为每一个匹配的SPI设备创建设备节点,节点名"spi%d"。之后,用户程序可以通过字符型设备的通用接口控制SPI设备。需要注意的是,spidev创建的设备在设备模型中属于虚拟设备,他的class是spidev_class,他的父设备是在boardinfo中定义的spi设备。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: