如測試程序中的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_asyncstatic 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_transferint 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_workstatic 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_irqstatic 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;
}
其實更重要的是學習數據發送過程中所用到的同步,互斥,鏈表,任務隊列等編程技巧。