本文分析SPI发送数据的过程,首先构造spi_ioc_transfer结构,再调用ioctl函数发送。
spidev_ioctl()中的filp->private_data是在spidev_open()中初始化的,指向上篇文章中的spidev_probe()函数里创建的spidev_data结构.
而spidev_data里的spi指针就是scan_boardinfo()->spi_new_device()函数中根据s3c2410_spi0_board所创建的struct spi_device *proxy。
6.s3c24xx_spi_txrx
如测试程序中的transfer函数。
static void transfer(int fd)
{
int ret;
/*要发送的数据*/
uint8_t tx[] = {
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0x40, 0x00, 0x00, 0x00, 0x00, 0x95,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xDE, 0xAD, 0xBE, 0xEF, 0xBA, 0xAD,
0xF0, 0x0D,
};
/*存放接收到的数据*/
uint8_t rx[ARRAY_SIZE(tx)] = {0, };
/*构造spi_ioc_transfer结构,tx_buf,rx_buf指向上面的发送和接收buf*/
struct spi_ioc_transfer tr = {
.tx_buf = (unsigned long)tx,
.rx_buf = (unsigned long)rx,
.len = ARRAY_SIZE(tx),
.delay_usecs = delay,
.speed_hz = speed,
.bits_per_word = bits,
};
/*调用ioctl*/
ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
if (ret == 1)
pabort("can't send spi message");
/*打印接收到的数据*/
for (ret = 0; ret < ARRAY_SIZE(tx); ret++) {
if (!(ret % 6))
puts("");
printf("%.2X ", rx[ret]);
}
puts("");
}
spidev_ioctl()最终会调用spidev_message()来发送数据。
spidev_ioctl()中的filp->private_data是在spidev_open()中初始化的,指向上篇文章中的spidev_probe()函数里创建的spidev_data结构.
而spidev_data里的spi指针就是scan_boardinfo()->spi_new_device()函数中根据s3c2410_spi0_board所创建的struct spi_device *proxy。
1.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*/
spi_message_init(&msg);
/*创建n_xfers个spi_transfer结构*/
k_xfers = kcalloc(n_xfers, sizeof(*k_tmp), GFP_KERNEL);
if (k_xfers == NULL)
return -ENOMEM;
/* Construct spi_message, copying any tx data to bounce buffer.
* We walk the array of user-provided transfers, using each one
* to initialize a kernel version of the same transfer.
*/
buf = spidev->buffer;
total = 0;
/*循环构造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;
}
/*发送和接收公用一个buf?,从buf中发送一个,
*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;
}
/*将要发送的数据放入所要构造的spi_transfer中*/
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;
#ifdef VERBOSE
dev_dbg(&spi->dev,
" xfer len %zd %s%s%s%dbits %u usec %uHz\n",
u_tmp->len,
u_tmp->rx_buf ? "rx " : "",
u_tmp->tx_buf ? "tx " : "",
u_tmp->cs_change ? "cs " : "",
u_tmp->bits_per_word ? : spi->bits_per_word,
u_tmp->delay_usecs,
u_tmp->speed_hz ? : spi->max_speed_hz);
#endif
/*添加到发送链表中,准备发送*/
spi_message_add_tail(k_tmp, &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;
}
2.spidev_sync
static ssize_t
spidev_sync(struct spidev_data *spidev, struct spi_message *message)
{
DECLARE_COMPLETION_ONSTACK(done);
int status;
/*complete唤醒函数*/
message->complete = spidev_complete;
/*complete唤醒变量
*当完成发送后,控制器master代码就会调用
*message->complete函数唤醒message->context变量
*/
message->context = &done;
printk(KERN_ERR "SPI0\n");
/*调用spi_async发送数据*/
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);
/*等待发送结束
*打印消息“SPI3”会在发送结束后才会打印
*/
if (status == 0) {
printk(KERN_ERR "SPI2\n");
wait_for_completion(&done);
printk(KERN_ERR "SPI3\n");
status = message->status;
if (status == 0)
status = message->actual_length;
}
return status;
}
3.spi_async
static inline int
spi_async(struct spi_device *spi, struct spi_message *message)
{
message->spi = spi
/*spi_bitbang_start()函数中初始化为spi_bitbang_transfer*/
return spi->master->transfer(spi, message);
}
4.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;
/*实际获取的是s3c24xx_spi结构的首地址,
*但spi_bitbang是s3c24xx_spi的首元素,所以也是可以的
*/
bitbang = spi_master_get_devdata(spi->master);
/*将发送任务添加到任务列表中,等待空闲时发送*/
spin_lock_irqsave(&bitbang->lock, flags);
if (!spi->max_speed_hz)
status = -ENETDOWN;
else {
list_add_tail(&m->queue, &bitbang->queue);
queue_work(bitbang->workqueue, &bitbang->work);
}
spin_unlock_irqrestore(&bitbang->lock, flags);
return status;
}
5.bitbang_work
static void bitbang_work(struct work_struct *work)
{
struct spi_bitbang *bitbang =
container_of(work, struct spi_bitbang, work);
unsigned long flags;
printk(KERN_ERR "SPI5\n");
spin_lock_irqsave(&bitbang->lock, flags);
bitbang->busy = 1;
/*列表为空则什么也不做*/
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 *);
/*获取spi_message,要发送的数据在里面*/
m = container_of(bitbang->queue.next, struct spi_message,
queue);
list_del_init(&m->queue);
spin_unlock_irqrestore(&bitbang->lock, flags);
/* FIXME this is made-up ... the correct value is known to
* word-at-a-time bitbang code, and presumably chipselect()
* should enforce these requirements too?
*/
nsecs = 100;
spi = m->spi;
tmp = 0;
cs_change = 1;
status = 0;
setup_transfer = NULL;
/*有多个spi_ioc_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;
}
}
/*发送之前的准备工作,如设置时钟等,硬件相关
*调用的是调用的是s3c24xx_spi_probe()中设置的s3c24xx_spi_setupxfer()*/
if (setup_transfer) {
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 ...)
*/
/*是否需要设置CS脚,调用的是s3c24xx_spi_probe()中设置的s3c24xx_spi_chipsel()*/
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;
}
/* transfer data. the lower level code handles any
* new dma mappings it needs. our caller always gave
* us dma-safe buffers.
*/
/*发送,调用的是s3c24xx_spi_probe()中设置的s3c24xx_spi_txrx*/
if (t->len) {
/* REVISIT dma API still needs a designated
* DMA_ADDR_INVALID; ~0 might be better.
*/
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)
continue;
/*判断是否将列表中的spi_transfer发送完毕*/
if (t->transfer_list.next == &m->transfers)
break;
/* sometimes a short mid-message deselect of the chip
* may be needed to terminate a mode or command
*/
/*配置CS脚*/
ndelay(nsecs);
bitbang->chipselect(spi, BITBANG_CS_INACTIVE);
ndelay(nsecs);
}
/*唤醒complete,使spidev_sync继续运行,打印“SPI3”*/
m->status = status;
m->complete(m->context);
/*恢复时钟等,为了省电?*/
/* restore speed and wordsize */
if (setup_transfer)
setup_transfer(spi, NULL);
/* normally deactivate chipselect ... unless no error and
* cs_change has hinted that the next message will probably
* be for this chip too.
*/
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);
}
6.s3c24xx_spi_txrx
实际的发送函数,需要中断程序的配合。
发送第一个字节数据后,该函数进入等待状态,中断程序会发送其他字节。发送完毕后,中断程序再唤醒该函数。
static int s3c24xx_spi_txrx(struct spi_device *spi, struct spi_transfer *t)
{
struct s3c24xx_spi *hw = to_hw(spi);
printk(KERN_ERR "SPI6\n");
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;
/*初始化completion*/
init_completion(&hw->done);
/*发送第一个字节,等待该字节发送完毕,产生中断,再发送下一个字节。
*全部发送完毕,唤醒该函数,打印“SPI8”*/
/* send the first byte */
writeb(hw_txbyte(hw, 0), hw->regs + S3C2410_SPTDAT);
printk(KERN_ERR "SPI7\n");
wait_for_completion(&hw->done);
printk(KERN_ERR "SPI8\n");
return hw->count;
}
7.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);
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++;
printk(KERN_ERR "SPI9\n");
/*刚才发送一个字节,所以先接收一个字节数据*/
if (hw->rx)
hw->rx[count] = readb(hw->regs + S3C2410_SPRDAT);
/*成功发送的字节数加1*/
count++;
/*是否全部发送完毕,是则唤醒等待程序,不是则继续发送*/
if (count < hw->len)
writeb(hw_txbyte(hw, count), hw->regs + S3C2410_SPTDAT);
else
complete(&hw->done);
printk(KERN_ERR "SPI10\n");
irq_done:
return IRQ_HANDLED;
}
其实更重要的是学习数据发送过程中所用到的同步,互斥,链表,任务队列等编程技巧。