日期:2014-05-16  浏览次数:20652 次

Linux SPI框架(下)

水平有限,描述不当之处还请之处,转载请注明出处http://blog.csdn.net/vanbreaker/article/details/7737833  

     本节以spidev设备驱动为例,来阐述SPI数据传输的过程。spidev是内核中一个通用的设备驱动,我们注册的从设备都可以使用该驱动,只需在注册时将从设备的modalias字段设置为"spidev",这样才能和spidev驱动匹配成功。我们要传输的数据有时需要分为一段一段的(比如先发送,后读取,就需要两个字段),每个字段都被封装成一个transfer,N个transfer可以被添加到message中,作为一个消息包进行传输。当用户发出传输数据的请求时,message并不会立刻传输到从设备,而是由之前定义的transfer()函数将message放入一个等待队列中,这些message会以FIFO的方式有workqueue调度进行传输,这样能够避免SPI从设备同一时间对主SPI控制器的竞争。和之前一样,还是习惯先画一张图来描述数据传输的主要过程。

 

         在使用spidev设备驱动时,需要先初始化spidev. spidev是以字符设备的形式注册进内核的。

static int __init spidev_init(void)
{
	int status;

	/* Claim our 256 reserved device numbers.  Then register a class
	 * that will key udev/mdev to add/remove /dev nodes.  Last, register
	 * the driver which manages those device numbers.
	 */
	BUILD_BUG_ON(N_SPI_MINORS > 256);
	/*将spidev作为字符设备注册*/
	status = register_chrdev(SPIDEV_MAJOR, "spi", &spidev_fops);
	if (status < 0)
		return status;

	/*创建spidev类*/
	spidev_class = class_create(THIS_MODULE, "spidev");
	if (IS_ERR(spidev_class)) {
		unregister_chrdev(SPIDEV_MAJOR, spidev_spi.driver.name);
		return PTR_ERR(spidev_class);
	}

	/*注册spidev的driver,可与modalias字段为"spidev"的spi_device匹配*/
	status = spi_register_driver(&spidev_spi);
	if (status < 0) {
		class_destroy(spidev_class);
		unregister_chrdev(SPIDEV_MAJOR, spidev_spi.driver.name);
	}
	return status;
}


与相应的从设备匹配成功后,则调用spidev中的probe函数

static int spidev_probe(struct spi_device *spi)
{
	struct spidev_data	*spidev;
	int			status;
	unsigned long		minor;

	/* Allocate driver data */
	spidev = kzalloc(sizeof(*spidev), GFP_KERNEL);
	if (!spidev)
		return -ENOMEM;

	/* Initialize the driver data */
	spidev->spi = spi;//设定spi
	spin_lock_init(&spidev->spi_lock);
	mutex_init(&spidev->buf_lock);

	INIT_LIST_HEAD(&spidev->device_entry);

	/* If we can allocate a minor number, hook up this device.
	 * Reusing minors is fine so long as udev or mdev is working.
	 */
	mutex_lock(&device_list_lock);
	minor = find_first_zero_bit(minors, N_SPI_MINORS);//寻找没被占用的次设备号
	if (minor < N_SPI_MINORS) {
		struct device *dev;
		/*计算设备号*/
		spidev->devt = MKDEV(SPIDEV_MAJOR, minor);
		/*在spidev_class下创建设备*/
		dev = device_create(spidev_class, &spi->dev, spidev->devt,
				    spidev, "spidev%d.%d",
				    spi->master->bus_num, spi->chip_select);
		status = IS_ERR(dev) ? PTR_ERR(dev) : 0;
	} else {
		dev_dbg(&spi->dev, "no minor number available!\n");
		status = -ENODEV;
	}
	if (status == 0) {
		set_bit(minor, minors);//将minors的相应位置位,表示该位对应的次设备号已被占用
		list_add(&spidev->device_entry, &device_list);//将创建的spidev添加到device_list
	}
	mutex_unlock(&device_list_lock);

	if (status == 0)
		spi_set_drvdata(spi, spidev);
	else
		kfree(spidev);

	return status;
}


然后就可以利用spidev模块提供的接口来实现主从设备之间的数据传输了。我们以spidev_write()函数为例来分析数据传输的过程,实际上spidev_read()和其是差不多的,只是前面的一些步骤不一样,可以参照上图。

static ssize_t
spidev_write(struct file *filp, const char __user *buf,
		size_t count, loff_t *f_pos)
{
	struct spidev_data	*spidev;
	ssize_t			status = 0;
	unsigned long		missing;

	/* chipselect only toggles at start or end of operation */
	if (count > bufsiz)
		return -EMSGSIZE;

	spidev = filp->private_data;

	mutex_lock(&spidev->buf_lock);
	//将用户要发送的数据拷贝到spidev->buffer
	missing = copy_from_user(spidev->buffer, buf, count);
	if (missing == 0) {//全部拷贝成功,则调用spidev_sysn_write()
		status = spidev_sync_write(spidev,