深入浅出spi驱动之设备驱动(三)

本文深入剖析SPI设备驱动,从spidev_probe函数的调用来讲解设备驱动的初始化,接着分析file_operations中的open、read、write、ioctl函数,揭示数据如何在用户空间与SPI设备间流动。SPI子系统通过spi_bus_type实现设备与驱动的匹配,并依赖中断模式进行数据传输。
摘要由CSDN通过智能技术生成

Allein.Cao原创作品,转载请注明出处:

http://blog.csdn.net/alleincao/article/details/7525977

内核版本:2.6.32.2

硬件:S3C2440

设备驱动是在core之上的模块,向上给应用程序提供file_operations接口,应用程序可以通过设备节点访问驱动程序,向下通过core向控制器模块发送数据,控制器模块将数据发送到物理总线上。

spidev.c是一个典型的设备驱动程序,前面提到在linux中,一般都会采用设备驱动和控制器驱动分离的思想,两者通过一个core进行关联,目的是最大程度保证代码的可移植性,我们以应用程序调用为主线,详细分析spi驱动的数据流流向。

首先来看

static struct spi_driver spidev_spi = {			//spi_driver
	.driver = {
		.name =		"spidev",		//spi_bus_type上spi_despi_devie与spi_driver匹配依赖于此名字
		.owner =	THIS_MODULE,
	},
	.probe =	spidev_probe,					//probe函数
	.remove =	__devexit_p(spidev_remove),//编译为模块or编译到内核?内核则为NULL,模块为spidev_remove
};
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.
	 */
	//设备驱动与主设备号一一对应,所以当用户打开主设备号为SPIDEV_MAJOR的设备节点时会调用spidev_fops相关函数BUILD_BUG_ON(N_SPI_MINORS > 256);
	status = register_chrdev(SPIDEV_MAJOR, "spi", &spidev_fops);	
	if (status < 0)
		return status;

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

	status = spi_register_driver(&spidev_spi);	//注册spi_driver
	if (status < 0) {
		class_destroy(spidev_class);
		unregister_chrdev(SPIDEV_MAJOR, spidev_spi.driver.name);
	}
	return status;
}

spidev_probe函数的调用在spi_bus_type的match函数即spi_match_device函数调用之后进行,它的实现相对简单,主要是分配并初始化spidev_data结构体,将其添加到device_list链表,执行完此函数,所有spi_bus_type上spi_devie和spi_driver匹配完毕:

static int spidev_probe(struct spi_device *spi)	//其入口参数是spi_bus_type上的spi_device
{
	struct	*spidev;
	int			status;
	unsigned long		minor;

	/* Allocate driver data */
	spidev = kzalloc(sizeof(*spidev), GFP_KERNEL);	//每个spi_device对应一个spidev_data
	if (!spidev)
		return -ENOMEM;

	/* Initialize the driver data */
	spidev->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);	//找到第一个0位,作为次设备号,见下面分析
	if (minor < N_SPI_MINORS) {
		struct device *dev;

		spidev->devt = MKDEV(SPIDEV_MAJOR, minor);
		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);		//设置相应位
		list_add(&spidev->device_entry, &device_list);	//添加到device_list链表
	}
	mutex_unlock(&device_list_lock);

	if (status == 0)
		spi_set_drvdata(spi, spidev);		//设置私有数据spi_device–>dev->p->driver_data
	else
		kfree(spidev);

	return status;
}

接下来,我们分析该驱动的调用流程,即file_operations中的成员函数:

static const struct file_operations spidev_fops = {
	.owner =	THIS_MODULE,
	/* REVISIT switch to aio primitives, so that userspace
	 * gets more complete API coverage.  It'll simplify things
	 * too, except for the locking.
	 */
	.write =	spidev_write,
	.read =		spidev_read,
	.unlocked_ioctl = spidev_ioctl,
	.open =		spidev_open,
	.release =	spidev_release,
};

首先,我们来开open函数spidev_open,其主要目的就是根据用户打开的设备节点的设备号找到对应的spidev_data,也就自然找到对应的spi_device(通过spidev_data->spi,其在probe函数中赋值),这样就很自然地实现用户空间访问spi设备:

static int spidev_open(struct inode *inode, struct file *filp)
{
	struct spidev_data	*spidev;
	int			status = -ENXIO;

	lock_kernel();
	mutex_lock(&device_list_lock);

//根据设备号找到对应的spidev,很自然可以找到对应的spi_device
	list_for_each_entry(spidev, &device_list, device_entry) {
		if (spidev->devt == inode->i_rdev) {	
			status = 0;
			break;
		}
	}
	if (status == 0) {
		if (!spidev->buffer) {		//buffer没分配则分配buffer
			spidev->buffer = kmalloc(bufsiz, GFP_KERNEL);	
			if (!spidev->buffer) {
				dev_dbg(&spidev->spi->dev, "open/ENOMEM\n");
				status = -ENOMEM;
			}
		}
		if (status == 0) {
			spidev-&
  • 1
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值