FL2440開發板spi驅動分析(3)

本文分析SPI發送數據的過程,首先構造spi_ioc_transfer結構,再調用ioctl函數發送。

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


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章