基于S3C2440的嵌入式Linux驱动——SPI子系统解读(三)

发布者:q13358975046最新更新时间:2017-09-23 来源: eefocus关键字:S3C2440  Linux驱动  SPI  子系统 手机看文章 扫描二维码
随时随地手机看文章

该系列文章将分为四个部分:

   第一部分,将对SPI子系统整体进行描述,同时给出SPI的相关数据结构,最后描述SPI总线的注册。基于S3C2440的嵌入式Linux驱动——SPI子系统解读(一)

   第二部分,该文将对SPI的主控制器(master)驱动进行描述。基于S3C2440的嵌入式Linux驱动——SPI子系统解读(二)

   第三部分,即本篇文章,该文将对SPI设备驱动,也称protocol 驱动,进行讲解。

   第四部分,通过SPI设备驱动留给用户层的API,我们将从上到下描述数据是如何通过SPI的protocol 驱动,由bitbang中转,最后由master驱动将数据传输出

                   去。 基于S3C2440的嵌入式Linux驱动——SPI子系统解读(四)

本文属于第三部分。

5. SPI设备驱动

    在主控制器驱动中,spi_device已经注册了,在设备驱动中,首先要做的就是注册spi_driver,并提供用户层相应的API。

5.1 SPI设备驱动的注册

下列数据结构及函数位于drivers/spi/spidev.c。


  1. static struct file_operations spidev_fops = {  

  2.     .owner =    THIS_MODULE,  

  3.     /* REVISIT switch to aio primitives, so that userspace 

  4.      * gets more complete API coverage.  It'll simplify things 

  5.      * too, except for the locking. 

  6.      */  

  7.     .write =    spidev_write,  

  8.     .read =        spidev_read,  

  9.     .unlocked_ioctl = spidev_ioctl,  

  10.     .open =        spidev_open,  

  11.     .release =    spidev_release,  

  12. };  

  13.   

  14. /* The main reason to have this class is to make mdev/udev create the 

  15.  * /dev/spidevB.C character device nodes exposing our userspace API. 

  16.  * It also simplifies memory management. 

  17.  */  

  18.   

  19. static struct class *spidev_class;  

  20.   

  21. static struct spi_driver spidev_spi = {  

  22.     .driver = {  

  23.         .name =        "spidev",  

  24.         .owner =    THIS_MODULE,  

  25.     },  

  26.     .probe =    spidev_probe,  

  27.     .remove =    __devexit_p(spidev_remove),  

  28.   

  29.     /* NOTE:  suspend/resume methods are not necessary here. 

  30.      * We don't do anything except pass the requests to/from 

  31.      * the underlying controller.  The refrigerator handles 

  32.      * most issues; the controller driver handles the rest. 

  33.      */  

  34. };  

  35.   

  36. static int __init spidev_init(void)  

  37. {  

  38.     int status;  

  39.   

  40.     /* Claim our 256 reserved device numbers.  Then register a class 

  41.      * that will key udev/mdev to add/remove /dev nodes.  Last, register 

  42.      * the driver which manages those device numbers. 

  43.      */  

  44.     BUILD_BUG_ON(N_SPI_MINORS > 256);    /*检查次设备号*/  

  45.     status = register_chrdev(SPIDEV_MAJOR, "spi", &spidev_fops); /*注册字符设备,major=153*/  

  46.     if (status < 0)  

  47.         return status;  

  48.   

  49.     spidev_class = class_create(THIS_MODULE, "spidev");     /*创建spidev类*/  

  50.     if (IS_ERR(spidev_class)) {  

  51.         unregister_chrdev(SPIDEV_MAJOR, spidev_spi.driver.name);  

  52.         return PTR_ERR(spidev_class);  

  53.     }  

  54.   

  55.     status = spi_register_driver(&spidev_spi);      /*注册spi_driver,并调用probe方法*/  

  56.     if (status < 0) {  

  57.         class_destroy(spidev_class);  

  58.         unregister_chrdev(SPIDEV_MAJOR, spidev_spi.driver.name);  

  59.     }  

  60.     return status;  

  61. }  

  62. module_init(spidev_init);  

  63.   

  64. static void __exit spidev_exit(void)  

  65. {  

  66.     spi_unregister_driver(&spidev_spi);         /*注销spi_driver*/  

  67.     class_destroy(spidev_class);                /*注销类*/  

  68.     unregister_chrdev(SPIDEV_MAJOR, spidev_spi.driver.name);/*注销字符设备*/  

  69. }  

  70. 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。


  1. /** 

  2.  * spi_register_driver - register a SPI driver 

  3.  * @sdrv: the driver to register 

  4.  * Context: can sleep 

  5.  */  

  6. int spi_register_driver(struct spi_driver *sdrv)  

  7. {  

  8.     sdrv->driver.bus = &spi_bus_type;  

  9.     if (sdrv->probe)  

  10.         sdrv->driver.probe = spi_drv_probe;  

  11.     if (sdrv->remove)  

  12.         sdrv->driver.remove = spi_drv_remove;  

  13.     if (sdrv->shutdown)  

  14.         sdrv->driver.shutdown = spi_drv_shutdown;  

  15.     return driver_register(&sdrv->driver);  

  16. }  

  17. 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。


  1. #define SPIDEV_MAJOR            153    /* assigned */  

  2. #define N_SPI_MINORS            32    /* ... up to 256 */  

  3.   

  4. static unsigned long    minors[N_SPI_MINORS / BITS_PER_LONG];    /**/  

  5.   

  6. static LIST_HEAD(device_list);  

  7. static DEFINE_MUTEX(device_list_lock);  

  8.   

  9. static int spidev_probe(struct spi_device *spi)  

  10. {  

  11.     struct spidev_data  *spidev;  

  12.     int         status;  

  13.     unsigned long       minor;  

  14.   

  15.     /* Allocate driver data */  

  16.     spidev = kzalloc(sizeof(*spidev), GFP_KERNEL);  /*以kmalloc分配内存,并清0*/  

  17.     if (!spidev)  

  18.         return -ENOMEM;  

  19.   

  20.     /* Initialize the driver data */  

  21.     spidev->spi = spi;                   /*保存spi_device*/  

  22.     spin_lock_init(&spidev->spi_lock);   /*初始化自旋锁*/  

  23.     mutex_init(&spidev->buf_lock);       /*初始化互斥体*/  

  24.   

  25.     INIT_LIST_HEAD(&spidev->device_entry);   /*初始化链表头,链表为双向循环链表*/  

  26.   

  27.     /* If we can allocate a minor number, hook up this device. 

  28.      * Reusing minors is fine so long as udev or mdev is working. 

  29.      */  

  30.     mutex_lock(&device_list_lock);      /*上锁*/  

  31.     minor = find_first_zero_bit(minors, N_SPI_MINORS);  /*分配次设备号*/  

  32.     if (minor < N_SPI_MINORS) {  

  33.         struct device *dev;  

  34.   

  35.         spidev->devt = MKDEV(SPIDEV_MAJOR, minor);   /*根据主次设备号来获取设备号*/  

  36.         dev = device_create(spidev_class, &spi->dev, spidev->devt,    /*创建设备节点*/  

  37.                     spidev, "spidev%d.%d",  

  38.                     spi->master->bus_num, spi->chip_select);  

  39.         status = IS_ERR(dev) ? PTR_ERR(dev) : 0;  

  40.     } else {  

  41.         dev_dbg(&spi->dev, "no minor number available!\n");  

  42.         status = -ENODEV;  

  43.     }  

  44.     if (status == 0) {  

  45.         set_bit(minor, minors);     /*保存已使用的次设备号*/  

  46.         list_add(&spidev->device_entry, &device_list);/*在链表头list后面添加entry*/  

  47.     }  

  48.     mutex_unlock(&device_list_lock);/*解锁互斥体*/  

  49.   

  50.     if (status == 0)  

  51.         spi_set_drvdata(spi, spidev);/*spi->dev.driver_data=spidev*/  

  52.     else  

  53.         kfree(spidev);  

  54.   

  55.     return status;  

  56. }  

其中用到的的struct spidev_data结构如下:


  1. struct spidev_data {  

  2.     dev_t            devt;  

  3.     spinlock_t        spi_lock;  

  4.     struct spi_device    *spi;  

  5.     struct list_head    device_entry;  

  6.   

  7.     /* buffer is NULL unless this device is open (users > 0) */  

  8.     struct mutex        buf_lock;  

  9.     unsigned        users;  

  10.     u8            *buffer;  

  11. };  

  12.   

  13.   

   这个函数中,分配了spidev_data和次设备号,随后根据主次设备号创建了设备节点。设备节点的名字是spidev“bus_num””.chip_select",意思就是该设备是在第几个SPI接口上的第几个设备。

   此外,将spidev添加到device_list中,这样做就方便查找该spidev。

5.3 remove方法

下列函数位于drivers/spi/spidev.c。


  1. static int spidev_remove(struct spi_device *spi)  

  2. {  

  3.     struct spidev_data  *spidev = spi_get_drvdata(spi);  

  4.   

  5.     /* make sure ops on existing fds can abort cleanly */  

  6.     spin_lock_irq(&spidev->spi_lock);  

  7.     spidev->spi = NULL;  

  8.     spi_set_drvdata(spi, NULL);  

  9.     spin_unlock_irq(&spidev->spi_lock);  

  10.   

  11.     /* prevent new opens */  

  12.     mutex_lock(&device_list_lock);  

  13.     list_del(&spidev->device_entry); /*删除entry*/  

  14.     device_destroy(spidev_class, spidev->devt);  /*删除设备节点*/  

  15.     clear_bit(MINOR(spidev->devt), minors);/*删除使用的次设备号信息*/  

  16.     if (spidev->users == 0)  

  17.         kfree(spidev);  

  18.     mutex_unlock(&device_list_lock);  

  19.   

  20.     return 0;  

  21. }  


6. open和release


接着来看下open和release系统调用的API接口,其余3个接口将在本系列的第四篇文章中给出。

6.1 open方法

下列函数位于drivers/spi/spidev.c。


  1. static int spidev_open(struct inode *inode, struct file *filp)  

  2. {  

  3.     struct spidev_data  *spidev;  

  4.     int         status = -ENXIO;  

  5.   

  6.     lock_kernel();          /*加锁大内核锁,可以睡眠,只能在进程上下文使用*/  

  7.     mutex_lock(&device_list_lock);  /*加锁互斥体*/  

  8.   

  9.     list_for_each_entry(spidev, &device_list, device_entry) {/*从list开始遍历entry,即遍历所有的spidev*/  

  10.         if (spidev->devt == inode->i_rdev) {  /*判断设备号是否相等*/  

  11.             status = 0;                         /*找到匹配的spi设备*/  

  12.             break;  

  13.         }  

  14.     }  

  15.     if (status == 0) {  

  16.         /*NOTE:多个程序调用open方法,但他们共享一个buffer,因此对buufer需要进行互斥保护*/  

  17.         if (!spidev->buffer) {           /*buffer为空*/   

  18.             spidev->buffer = kmalloc(bufsiz, GFP_KERNEL);/*分配buffer缓冲区,默认4KB*/  

  19.             if (!spidev->buffer) {  

  20.                 dev_dbg(&spidev->spi->dev, "open/ENOMEM\n");  

  21.                 status = -ENOMEM;  

  22.             }  

  23.         }  

  24.         if (status == 0) {  

  25.             spidev->users++;     /*成功open以后,增加用户计数*/  

  26.             filp->private_data = spidev; /*保存spidev指针*/  

  27.             nonseekable_open(inode, filp);  /*禁用lseek*/  

  28.         }  

  29.     } else  

  30.         pr_debug("spidev: nothing for minor %d\n", iminor(inode));  

  31.   

  32.     mutex_unlock(&device_list_lock);/*释放互斥体*/  

  33.     unlock_kernel();                /*释放大内核锁*/  

  34.     return status;  

  35. }  

在这里,以device_list为链表头,遍历所有的spidev_data结构,通过设备节点的设备号和spidev_data中保存的设备号进行匹配,来找到属于该设备节点的spi设备。随后,分配了spi设备驱动层所使用的缓冲区,最后增加打开计数。


6.2 release方法

下列函数位于drivers/spi/spidev.c。


  1. static int spidev_release(struct inode *inode, struct file *filp)  

  2. {  

  3.     struct spidev_data  *spidev;  

  4.     int         status = 0;  

  5.   

  6.     mutex_lock(&device_list_lock);  /*加锁互斥体*/  

  7.     spidev = filp->private_data; /*获取spidev*/  

  8.     filp->private_data = NULL;         

  9.   

  10.     /* last close? */  

  11.     spidev->users--;             /*关闭设备文件,减少用户计数*/  

  12.     if (!spidev->users) {            /*如果用户数为0*/  

  13.         int     dofree;  

  14.   

  15.         kfree(spidev->buffer);       /*释放缓冲区*/  

  16.         spidev->buffer = NULL;  

  17.   

  18.         /* ... after we unbound from the underlying device? */  

  19.         spin_lock_irq(&spidev->spi_lock);    /*加锁互斥体*/  

  20.         dofree = (spidev->spi == NULL);      /*????*/  

  21.         spin_unlock_irq(&spidev->spi_lock);  /*释放互斥体*/  

  22.   

  23.         if (dofree)  

  24.             kfree(spidev);          /*释放spidev,在probe中分配*/  

  25.     }  

  26.     mutex_unlock(&device_list_lock);/*释放互斥体*/  

  27.   

  28.     return status;  

  29. }  


 至此,对于protocol驱动层的框架进行了简单的分析,在下一篇将对该驱动层很多未分析的函数进行一一讲解。下一篇的内容非常的重要哦。


关键字:S3C2440  Linux驱动  SPI  子系统 引用地址:基于S3C2440的嵌入式Linux驱动——SPI子系统解读(三)

上一篇:基于S3C2440的嵌入式Linux驱动——SPI子系统解读(四)
下一篇:基于S3C2440的嵌入式Linux驱动——SPI子系统解读(二)

推荐阅读最新更新时间:2024-03-16 15:38

S3C2440,S3C2450和S3C6410的区别
作为GPS、PDA、数字电视等手持设备的主要方案处理器提供方韩国Sumsung公司,最近又新推出ARM处理器S3C2450、S3C6410芯片。这些平台又与S3C2440处理器有哪些区别优势。 S3C2440: 主频400MHz; SDRam内存控制器; 支持Nor Flash和Nand Flash(SLC) 支持2种Flash启动; 内部LCD、I2C、Camera等控制器。 S3C2450: 主频533MHz; 双总线架构,一路用于内存总线、一路用于Flash总线; DDRII内存和DDR内存控制器; 支持Nor Flash和Nand Flash两种Flash,其中Nand Flash支持SLC和MLC两
[单片机]
STM32-24位AD7799驱动之手册代码详解,支持模拟SPI和硬件SPI
1.AD7799介绍 AD7799结构图如下所示: 其中REFIN参考电压建议为2.5V, REFIN电压低于0.1V时,则差分输入ad值就无法检测了,如下图所示: 注意: 如果REG_CONFIG的REF_DET开启的话,那么输入AD值电压低于0.5V时,则差分输入ad值就无法检测了,如下图所示: 2.AD7799差分信号的输入模式 如下图所示,差分输入电压有3种模式: 注意: 单端输入电压(AIN-接地,只有正电压)则支持任意范围,比如In-Amp模式下,单端输入如果为10mv的话,也能检测到. 2.1 Unbuffered Mode非缓冲模式 该模式可测的AD值可以在 -30mV ~ (AVDD+30
[单片机]
STM32-24位AD7799<font color='red'>驱动</font>之手册代码详解,支持模拟<font color='red'>SPI</font>和硬件<font color='red'>SPI</font>
linux中LCD设备驱动(4)——基于s3c6410平台
我们这一篇来说与具体的TFT显示器有关的部分,当遇到具体的显示器是我们应该设置什么参数,怎样设置这些参数。 1、在s3cfb_WXCAT43.c (linux2.6.28driversvideosamsung)文件中是有关具体显示器的设置。我们一段一段来看。 #include linux/wait.h #include linux/fb.h #include linux/delay.h #include linux/platform_device.h #include plat/regs-gpio.h #include plat/regs-lcd.h #include s3cfb.h #define S3C
[单片机]
<font color='red'>linux</font>中LCD设备<font color='red'>驱动</font>(4)——基于s3c6410平台
兆易创新推出突破性1.2V超低功耗SPI NOR Flash产品系列
应需而生!兆易创新推出突破性1.2V超低功耗SPI NOR Flash产品系列 中国北京(2022年8月19日) — 业界领先的半导体器件供应商兆易创新GigaDevice宣布,推出突破性的 1.2V超低功耗SPI NOR Flash产品——GD25UF系列 。该系列在数据传输速度、供电电压、读写功耗等关键性能指标上均达到国际领先水平,在针对智能可穿戴设备、健康监测、物联网设备或其它单电池供电的应用中,能显著降低运行功耗,有效延长设备的续航时间。 随着物联网技术的发展,新一代智能可穿戴设备需要拥有更丰富的功能来满足消费者的需求,这种空间敏感型产品对系统功耗提出了更严苛的要求,希望进一步提升产品的续航能力。从系统设计层
[嵌入式]
兆易创新推出突破性1.2V超低功耗<font color='red'>SPI</font> NOR Flash产品系列
基于32位ARM嵌入式微控制器S3C2440来构建太阳跟踪系统
  基于计算机视觉原理,以ARM微控制器为核心构建嵌入式图像处理平台,实现了对太阳的实时跟踪。系统采用CMOS图像传感器采集太阳图像,通过微控制器计算太阳角度,通过串口控制转台,实现对太阳的高精度跟踪。同时,与视日运动规律相结合,保证系统的可靠性。试验表明,该系统在降低系统能耗的同时,能可靠有效地跟踪太阳运动。   随着社会的发展和进步,环保节能已经成为人类可持续发展的必要条件。目前,再生能源的开发和利用越来越受到人们的关注。太阳能由于其普遍、无害、无限、长久等特点,成为最绿色、最理想、最可靠的替代能源。但太阳能同时存在分散,不稳定,效率低等特点,太阳能光伏系统为解决这一问题提供了条件。   就目前的太阳能光伏系统而言,如何最
[单片机]
基于32位ARM嵌入式微控制器<font color='red'>S3C2440</font>来构建太阳跟踪系统
51单片机软件SPI初始化ILI9488-DPI模式程序
大家好,最近的项目用到ILI9488这个芯片的屏幕,用51单片机的IO口模拟SPI信号,发送初始化代码 设置成DPI-18bitRGB模式。并用LT768芯片来显示图片。下面是程序和ILI9488资料分享: 显示图片为18bitR6G6B6信号,由于便于测试,每种颜色的6根脚都接一起了,效果如下图: 单片机驱动ILI9488源程序如下: #define CS P13 #define CLK P14 #define DCX P15 #define SI P16 #define LCD_nRESET P00 void SPId_Init(void) { P1M
[单片机]
51单片机软件<font color='red'>SPI</font>初始化ILI9488-DPI模式程序
STC8单片机硬件SPI通信例程W25Q16
SPI全双工通信使用起来相当方便,最常见的是W25Q16存储颗粒的使用了,当重新上电时,存储再W25Q16中的数据不会丢失,仍然可以读取出来,下面就简单讲一下如何使用W25Q16存储芯片。 本篇讲的是使用硬件SPI让单片机和W25Q16进行通信,模拟SPI通信将会在下一篇讲。使用W25Q16的步骤如下: 1.配置与SPI有关的寄存器 SPI的数据寄存器配置起来很简单,清零即可。 SPI的状态寄存器,SPI的中断标志位SPIF和写冲突标志位WCOL都需要写1清0,即将SPIF,WCOL位都置1即可。 SPI的控制寄存器,设置主机模式时,将SSIG和MSTR位都置1即可,使能SPI将SPEN位置1即可,相位和极性的
[单片机]
STC8单片机硬件<font color='red'>SPI</font>通信例程W25Q16
TI杯模拟电子系统专题邀请赛圆满结束
日前,“2010年全国大学生电子设计竞赛—TI杯模拟电子系统专题邀请赛”在东南大学成功举办。 全国大学生电子设计竞赛—TI杯专题邀请赛是由全国大学生电子设计竞赛组织委员会主办、德州仪器公司协办,每逢双年举办的全国性专题邀请赛,并设立TI杯。组委会将根据电子信息领域技术发展和社会经济热点问题决定每次竞赛的主题。全国大学生电子设计竞赛—2010年TI杯模拟电子系统专题邀请赛由东南大学承办。 2010年TI杯模拟电子系统专题邀请赛分成两个阶段:第一阶段,德州仪器公司与江苏省、湖北省、四川省、陕西省、上海市、浙江省大学生电子设计竞赛组委会合作,各省市自行进行以模拟电子电路设计为主要内容的命题式半封闭竞赛,在规定时间内
[模拟电子]
TI杯模拟电<font color='red'>子系统</font>专题邀请赛圆满结束
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
502 Bad Gateway

502 Bad Gateway


openresty
设计资源 培训 开发板 精华推荐

502 Bad Gateway

502 Bad Gateway


openresty
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

502 Bad Gateway

502 Bad Gateway


openresty
502 Bad Gateway

502 Bad Gateway


openresty
502 Bad Gateway

502 Bad Gateway


openresty
随便看看
    502 Bad Gateway

    502 Bad Gateway


    openresty
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved
502 Bad Gateway

502 Bad Gateway


openresty