FL2440开发板spi驱动分析(3)

本文详细解析了SPI接口发送数据的具体过程,包括spi_ioc_transfer结构的构造、ioctl函数的调用、spidev_message函数的工作原理及底层硬件的交互方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文分析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;
}
其实更重要的是学习数据发送过程中所用到的同步,互斥,链表,任务队列等编程技巧。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值