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设备。
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设备。
相关文章推荐
- Linux驱动修炼之道-SPI驱动框架源码分析(下)
- Linux驱动修炼之道-SPI驱动框架源码分析(下)
- Linux驱动修炼之道-SPI驱动框架源码分析(中)
- Linux驱动修炼之道-SPI驱动框架源码分析(下)
- Linux驱动修炼之道-SPI驱动框架源码分析(上)
- Linux驱动修炼之道-SPI驱动框架源码分析(中)
- Linux驱动修炼之道-SPI驱动框架源码分析(中)
- Linux驱动修炼之道-SPI驱动框架源码分析(中)
- Linux驱动修炼之道-SPI驱动框架源码分析(下)
- Linux驱动修炼之道-SPI驱动框架源码分析(下)
- Linux驱动修炼之道-SPI驱动框架源码分析(上)
- Linux驱动修炼之道-SPI驱动框架源码分析(下)
- Linux驱动修炼之道-SPI驱动框架源码分析
- Linux驱动修炼之道-SPI驱动框架源码分析(中)
- Linux驱动修炼之道-SPI驱动框架源码分析(下)
- Linux驱动修炼之道-SPI驱动框架源码分析(上)
- Linux驱动修炼之道-SPI驱动框架源码分析
- Linux驱动修炼之道-SPI驱动框架源码分析(上)
- Linux驱动修炼之道-SPI驱动框架源码分析(下)
- Linux驱动修炼之道-SPI驱动框架源码分析(中)