该系列文章将分为四个部分:
	   第一部分,将对spi子系统整体进行描述,同时给出spi的相关数据结构,最后描述spi总线的注册。
	   第二部分,该文将对spi的主控制器(master)驱动进行描述。
	   第三部分,即本篇文章,该文将对spi设备驱动,也称protocol 驱动,进行讲解。
	   第四部分,通过spi设备驱动留给用户层的api,我们将从上到下描述数据是如何通过spi的protocol 驱动,由bitbang中转,最后由master驱动将数据传输出
	                   去。
	本文属于第三部分。
	5. spi设备驱动
	    在主控制器驱动中,spi_device已经注册了,在设备驱动中,首先要做的就是注册spi_driver,并提供用户层相应的api。
	5.1 spi设备驱动的注册
	下列数据结构及函数位于drivers/spi/spidev.c。
	
	
	
		- 
			staticstruct file_operations spidev_fops = {  
		
- 
			    .owner =    this_module,  
		
- 
			     
		
- 
			 
		
- 
			 
		
- 
			  
		
- 
			    .write =    spidev_write,  
		
- 
			    .read =        spidev_read,  
		
- 
			    .unlocked_ioctl = spidev_ioctl,  
		
- 
			    .open =        spidev_open,  
		
- 
			    .release =    spidev_release,  
		
- 
			};  
		
- 
			  
		
- 
			 
		
- 
			 
		
- 
			 
		
- 
			  
		
- 
			  
		
- 
			staticstructclass *spidev_class;  
		
- 
			  
		
- 
			staticstruct spi_driver spidev_spi = {  
		
- 
			    .driver = {  
		
- 
			        .name =        "spidev",  
		
- 
			        .owner =    this_module,  
		
- 
			    },  
		
- 
			    .probe =    spidev_probe,  
		
- 
			    .remove =    __devexit_p(spidev_remove),  
		
- 
			  
		
- 
			     
		
- 
			 
		
- 
			 
		
- 
			 
		
- 
			  
		
- 
			};  
		
- 
			  
		
- 
			staticint __init spidev_init(void)  
		
- 
			{  
		
- 
			    int status;  
		
- 
			  
		
- 
			     
		
- 
			 
		
- 
			 
		
- 
			  
		
- 
			    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");       
		
- 
			    if (is_err(spidev_class)) {  
		
- 
			        unregister_chrdev(spidev_major, spidev_spi.driver.name);  
		
- 
			        return ptr_err(spidev_class);  
		
- 
			    }  
		
- 
			  
		
- 
			    status = spi_register_driver(&spidev_spi);        
		
- 
			    if (status < 0) {  
		
- 
			        class_destroy(spidev_class);  
		
- 
			        unregister_chrdev(spidev_major, spidev_spi.driver.name);  
		
- 
			    }  
		
- 
			    return status;  
		
- 
			}  
		
- 
			module_init(spidev_init);  
		
- 
			  
		
- 
			staticvoid __exit spidev_exit(void)  
		
- 
			{  
		
- 
			    spi_unregister_driver(&spidev_spi);           
		
- 
			    class_destroy(spidev_class);                  
		
- 
			    unregister_chrdev(spidev_major, spidev_spi.driver.name);  
		
- 
			}  
		
- 
			module_exit(spidev_exit);  
		
 
static 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,
};
/* the main reason to have this class is to make mdev/udev create the
 * /dev/spidevb.c character device nodes exposing our userspace api.
 * it also simplifies memory management.
 */
static struct class *spidev_class;
static struct spi_driver spidev_spi = {
    .driver = {
        .name =        "spidev",
        .owner =    this_module,
    },
    .probe =    spidev_probe,
    .remove =    __devexit_p(spidev_remove),
    /* note:  suspend/resume methods are not necessary here.
     * we don't do anything except pass the requests to/from
     * the underlying controller.  the refrigerator handles
     * most issues; the controller driver handles the rest.
     */
};
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);	/*检查次设备号*/
	status = register_chrdev(spidev_major, "spi", &spidev_fops); /*注册字符设备,major=153*/
	if (status < 0)
		return status;
	spidev_class = class_create(this_module, "spidev");		/*创建spidev类*/
	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,并调用probe方法*/
	if (status < 0) {
		class_destroy(spidev_class);
		unregister_chrdev(spidev_major, spidev_spi.driver.name);
	}
	return status;
}
module_init(spidev_init);
static void __exit spidev_exit(void)
{
	spi_unregister_driver(&spidev_spi);			/*注销spi_driver*/
	class_destroy(spidev_class);				/*注销类*/
	unregister_chrdev(spidev_major, spidev_spi.driver.name);/*注销字符设备*/
}
module_exit(spidev_exit);
该函数中,创建了一个字符设备以提供api给用户层,同时创建了一个spidev类,最后注册spi_driver到内核中。
	
	在这里我们看到了spi设备驱动是如何提供api给用户层的,那就是通过再熟悉不过的字符设备。通过字符设备,给用户层提供了5个api:open,release,write,read和ioctl。本文在后面将介绍open和close,剩余3个将在本系列的第四篇文章中介绍。
	接着看下spi_register_driver函数, 该函数位于drivers/spi/spidev.c。
	
	
	
		- 
			 
		
- 
			 
		
- 
			 
		
- 
			 
		
- 
			  
		
- 
			int spi_register_driver(struct spi_driver *sdrv)  
		
- 
			{  
		
- 
			    sdrv->driver.bus = &spi_bus_type;  
		
- 
			    if (sdrv->probe)  
		
- 
			        sdrv->driver.probe = spi_drv_probe;  
		
- 
			    if (sdrv->remove)  
		
- 
			        sdrv->driver.remove = spi_drv_remove;  
		
- 
			    if (sdrv->shutdown)  
		
- 
			        sdrv->driver.shutdown = spi_drv_shutdown;  
		
- 
			    return driver_register(&sdrv->driver);  
		
- 
			}  
		
- 
			export_symbol_gpl(spi_register_driver);  
		
 
/**
 * spi_register_driver - register a spi driver
 * @sdrv: the driver to register
 * context: can sleep
 */
int spi_register_driver(struct spi_driver *sdrv)
{
	sdrv->driver.bus = &spi_bus_type;
	if (sdrv->probe)
		sdrv->driver.probe = spi_drv_probe;
	if (sdrv->remove)
		sdrv->driver.remove = spi_drv_remove;
	if (sdrv->shutdown)
		sdrv->driver.shutdown = spi_drv_shutdown;
	return driver_register(&sdrv->driver);
}
export_symbol_gpl(spi_register_driver);
在调用driver_register的过程中,将用driver.name和spi_device的modalias字段进行比较,两者相等则将该spi_driver和spi_device进行绑定。
	
	当spi_driver注册成功以后,将调用probe方法:spidev_probe函数。
	5.2 probe方法
	我们来看看spidev_probe这个函数,该函数位于drivers/spi/spidev.c。
	
	
	
		- 
			#define spidev_major            153    /* assigned */  
		
- 
			#define n_spi_minors            32    /* ... up to 256 */  
		
- 
			  
		
- 
			static unsigned long    minors[n_spi_minors / bits_per_long];      
		
- 
			  
		
- 
			static list_head(device_list);  
		
- 
			static define_mutex(device_list_lock);  
		
- 
			  
		
- 
			staticint spidev_probe(struct spi_device *spi)  
		
- 
			{  
		
- 
			    struct spidev_data  *spidev;  
		
- 
			    int         status;  
		
- 
			    unsigned long       minor;  
		
- 
			  
		
- 
			      
		
- 
			    spidev = kzalloc(sizeof(*spidev), gfp_kernel);    
		
- 
			    if (!spidev)  
		
- 
			        return -enomem;  
		
- 
			  
		
- 
			      
		
- 
			    spidev->spi = spi;                     
		
- 
			    spin_lock_init(&spidev->spi_lock);     
		
- 
			    mutex_init(&spidev->buf_lock);         
		
- 
			  
		
- 
			    init_list_head(&spidev->device_entry);     
		
- 
			  
		
- 
			     
		
- 
			 
		
- 
			  
		
- 
			    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);     
		
- 
			        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);  
		
- 
			    }  
		
- 
			    mutex_unlock(&device_list_lock);  
		
- 
			  
		
- 
			    if (status == 0)  
		
- 
			        spi_set_drvdata(spi, spidev);  
		
- 
			    else  
		
- 
			        kfree(spidev);  
		
- 
			  
		
- 
			    return status;  
		
- 
			}  
		
 
#define spidev_major            153    /* assigned */
#define n_spi_minors            32    /* ... up to 256 */
static unsigned long    minors[n_spi_minors / bits_per_long];    /**/
static list_head(device_list);
static define_mutex(device_list_lock);
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);	/*以kmalloc分配内存,并清0*/
	if (!spidev)
		return -enomem;
	/* initialize the driver data */
	spidev->spi = spi; 					/*保存spi_device*/
	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);	/*根据主次设备号来获取设备号*/
		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);/*在链表头list后面添加entry*/
	}
	mutex_unlock(&device_list_lock);/*解锁互斥体*/
	if (status == 0)
		spi_set_drvdata(spi, spidev);/*spi->dev.driver_data=spidev*/
	else
		kfree(spidev);
	return status;
}
其中用到的的
struct spidev_data结构如下:
	
	
	
		- 
			struct spidev_data {  
		
- 
			    dev_t            devt;  
		
- 
			    spinlock_t        spi_lock;  
		
- 
			    struct spi_device    *spi;  
		
- 
			    struct list_head    device_entry;  
		
- 
			  
		
- 
			      
		
- 
			    struct mutex        buf_lock;  
		
- 
			    unsigned        users;  
		
- 
			    u8            *buffer;  
		
- 
			};  
		
- 
			"font-size: 12px;">  
		
- 
			  
		
 
struct spidev_data {
    dev_t            devt;
    spinlock_t        spi_lock;
    struct spi_device    *spi;
    struct list_head    device_entry;
    /* buffer is null unless this device is open (users > 0) */
    struct mutex        buf_lock;
    unsigned        users;
    u8            *buffer;
}; 
	   这个函数中,分配了spidev_data和次设备号,随后根据主次设备号创建了设备节点。设备节点的名字是spidev“bus_num””.chip_select",意思就是该设备是在第几个spi接口上的第几个设备。
	   此外,将spidev添加到device_list中,这样做就方便查找该spidev。
	5.3 remove方法
	下列函数位于drivers/spi/spidev.c。
	
	
	
		- 
			staticint spidev_remove(struct spi_device *spi)  
		
- 
			{  
		
- 
			    struct spidev_data  *spidev = spi_get_drvdata(spi);  
		
- 
			  
		
- 
			      
		
- 
			    spin_lock_irq(&spidev->spi_lock);  
		
- 
			    spidev->spi = null;  
		
- 
			    spi_set_drvdata(spi, null);  
		
- 
			    spin_unlock_irq(&spidev->spi_lock);  
		
- 
			  
		
- 
			      
		
- 
			    mutex_lock(&device_list_lock);  
		
- 
			    list_del(&spidev->device_entry);   
		
- 
			    device_destroy(spidev_class, spidev->devt);    
		
- 
			    clear_bit(minor(spidev->devt), minors);  
		
- 
			    if (spidev->users == 0)  
		
- 
			        kfree(spidev);  
		
- 
			    mutex_unlock(&device_list_lock);  
		
- 
			  
		
- 
			    return 0;  
		
- 
			}  
		
 
static int spidev_remove(struct spi_device *spi)
{
	struct spidev_data	*spidev = spi_get_drvdata(spi);
	/* make sure ops on existing fds can abort cleanly */
	spin_lock_irq(&spidev->spi_lock);
	spidev->spi = null;
	spi_set_drvdata(spi, null);
	spin_unlock_irq(&spidev->spi_lock);
	/* prevent new opens */
	mutex_lock(&device_list_lock);
	list_del(&spidev->device_entry);	/*删除entry*/
	device_destroy(spidev_class, spidev->devt);	/*删除设备节点*/
	clear_bit(minor(spidev->devt), minors);/*删除使用的次设备号信息*/
	if (spidev->users == 0)
		kfree(spidev);
	mutex_unlock(&device_list_lock);
	return 0;
}
6. open和release
	
	接着来看下open和release系统调用的api接口,其余3个接口将在本系列的第四篇文章中给出。
	6.1 open方法
	下列函数位于drivers/spi/spidev.c。
	
	
	
		- 
			staticint spidev_open(struct inode *inode, struct file *filp)  
		
- 
			{  
		
- 
			    struct spidev_data  *spidev;  
		
- 
			    int         status = -enxio;  
		
- 
			  
		
- 
			    lock_kernel();            
		
- 
			    mutex_lock(&device_list_lock);    
		
- 
			  
		
- 
			    list_for_each_entry(spidev, &device_list, device_entry) {  
		
- 
			        if (spidev->devt == inode->i_rdev) {    
		
- 
			            status = 0;                           
		
- 
			            break;  
		
- 
			        }  
		
- 
			    }  
		
- 
			    if (status == 0) {  
		
- 
			          
		
- 
			        if (!spidev->buffer) {              
		
- 
			            spidev->buffer = kmalloc(bufsiz, gfp_kernel);  
		
- 
			            if (!spidev->buffer) {  
		
- 
			                dev_dbg(&spidev->spi->dev, "open/enomem\n");  
		
- 
			                status = -enomem;  
		
- 
			            }  
		
- 
			        }  
		
- 
			        if (status == 0) {  
		
- 
			            spidev->users  ;       
		
- 
			            filp->private_data = spidev;   
		
- 
			            nonseekable_open(inode, filp);    
		
- 
			        }  
		
- 
			    } else  
		
- 
			        pr_debug("spidev: nothing for minor %d\n", iminor(inode));  
		
- 
			  
		
- 
			    mutex_unlock(&device_list_lock);  
		
- 
			    unlock_kernel();                  
		
- 
			    return status;  
		
- 
			}  
		
 
static int spidev_open(struct inode *inode, struct file *filp)
{
	struct spidev_data	*spidev;
	int			status = -enxio;
	lock_kernel();			/*加锁大内核锁,可以睡眠,只能在进程上下文使用*/
	mutex_lock(&device_list_lock);	/*加锁互斥体*/
	list_for_each_entry(spidev, &device_list, device_entry) {/*从list开始遍历entry,即遍历所有的spidev*/
		if (spidev->devt == inode->i_rdev) {	/*判断设备号是否相等*/
			status = 0;							/*找到匹配的spi设备*/
			break;
		}
	}
	if (status == 0) {
		/*note:多个程序调用open方法,但他们共享一个buffer,因此对buufer需要进行互斥保护*/
		if (!spidev->buffer) {			/*buffer为空*/ 
			spidev->buffer = kmalloc(bufsiz, gfp_kernel);/*分配buffer缓冲区,默认4kb*/
			if (!spidev->buffer) {
				dev_dbg(&spidev->spi->dev, "open/enomem\n");
				status = -enomem;
			}
		}
		if (status == 0) {
			spidev->users  ;		/*成功open以后,增加用户计数*/
			filp->private_data = spidev;	/*保存spidev指针*/
			nonseekable_open(inode, filp);  /*禁用lseek*/
		}
	} else
		pr_debug("spidev: nothing for minor %d\n", iminor(inode));
	mutex_unlock(&device_list_lock);/*释放互斥体*/
	unlock_kernel();				/*释放大内核锁*/
	return status;
}
在这里,以device_list为链表头,遍历所有的spidev_data结构,通过设备节点的设备号和spidev_data中保存的设备号进行匹配,来找到属于该设备节点的spi设备。随后,分配了spi设备驱动层所使用的缓冲区,最后增加打开计数。
	
	6.2 release方法
	下列函数位于drivers/spi/spidev.c。
	
	
	
		- 
			staticint spidev_release(struct inode *inode, struct file *filp)  
		
- 
			{  
		
- 
			    struct spidev_data  *spidev;  
		
- 
			    int         status = 0;  
		
- 
			  
		
- 
			    mutex_lock(&device_list_lock);    
		
- 
			    spidev = filp->private_data;   
		
- 
			    filp->private_data = null;         
		
- 
			  
		
- 
			      
		
- 
			    spidev->users--;               
		
- 
			    if (!spidev->users) {              
		
- 
			        int     dofree;  
		
- 
			  
		
- 
			        kfree(spidev->buffer);         
		
- 
			        spidev->buffer = null;  
		
- 
			  
		
- 
			          
		
- 
			        spin_lock_irq(&spidev->spi_lock);      
		
- 
			        dofree = (spidev->spi == null);        
		
- 
			        spin_unlock_irq(&spidev->spi_lock);    
		
- 
			  
		
- 
			        if (dofree)  
		
- 
			            kfree(spidev);            
		
- 
			    }  
		
- 
			    mutex_unlock(&device_list_lock);  
		
- 
			  
		
- 
			    return status;  
		
- 
			}  
		
 
static int spidev_release(struct inode *inode, struct file *filp)
{
	struct spidev_data	*spidev;
	int			status = 0;
	mutex_lock(&device_list_lock);	/*加锁互斥体*/
	spidev = filp->private_data;	/*获取spidev*/
	filp->private_data = null;		
	/* last close? */
	spidev->users--;				/*关闭设备文件,减少用户计数*/
	if (!spidev->users) {			/*如果用户数为0*/
		int		dofree;
		kfree(spidev->buffer);		/*释放缓冲区*/
		spidev->buffer = null;
		/* ... after we unbound from the underlying device? */
		spin_lock_irq(&spidev->spi_lock);	/*加锁互斥体*/
		dofree = (spidev->spi == null);		/*????*/
		spin_unlock_irq(&spidev->spi_lock);	/*释放互斥体*/
		if (dofree)
			kfree(spidev);			/*释放spidev,在probe中分配*/
	}
	mutex_unlock(&device_list_lock);/*释放互斥体*/
	return status;
}
至此,对于protocol驱动层的框架进行了简单的分析,在下一篇将对该驱动层很多未分析的函数进行一一讲解。下一篇的内容非常的重要哦。                                    
            
            
              
              阅读(2809) | 评论(0) | 转发(2) |