Chinaunix首页 | 论坛 | 博客
  • 博客访问: 27770
  • 博文数量: 13
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 10
  • 用 户 组: 普通用户
  • 注册时间: 2016-03-10 14:13
文章分类
文章存档

2016年(13)

我的朋友
最近访客

分类: LINUX

2016-07-28 14:07:46

linux spi驱动开发学习(一)-----spi子系统架构

一.spi设备(各定义在include/linux/spi.h)
  1. struct spi_device { 
  2.     struct device   dev;    //设备文件 
  3.     struct spi_master   *master;    //spi主机 
  4.     u32 max_speed_hz;   //最大速率 
  5.     u8  chip_select;    //片选 
  6.     u8  mode;   //模式 
  7.     u8  bits_per_word;  //一个字有多少位 
  8.     int irq;    //中断号 
  9.     void    *controller_state;  //控制器状态 
  10.     void    *controller_data;   //控制器数据 
  11.     char    modalias[SPI_NAME_SIZE];//名字 
  12. }; 


2.spi传输模式:

  1. #define SPI_CPHA        0x01            //时钟相位 
  2. #define SPI_CPOL        0x02            //时钟继续 
  3. #define SPI_MODE_0  (0|0)           //模式0 
  4. #define SPI_MODE_1  (0|SPI_CPHA)    //模式1 
  5. #define SPI_MODE_2  (SPI_CPOL|0)    //模式2 
  6. #define SPI_MODE_3  (SPI_CPOL|SPI_CPHA) //模式3 
  7. #define SPI_CS_HIGH 0x04            //片选高电平 
  8. #define SPI_LSB_FIRST   0x08            //LSB 
  9. #define SPI_3WIRE       0x10            //3线模式 SI和SO同一根线 
  10. #define SPI_LOOP        0x20            //回送模式 
  11. #define SPI_NO_CS       0x40            //单个设备占用一根SPI总线,所以没片选 
  12. #define SPI_READY       0x80            //从机拉低电平停止数据传输 


3.spi设备的添加spi_new_device

  1. struct spi_device *spi_new_device(struct spi_master *master,struct spi_board_info *chip) 
  2.     struct spi_device   *proxy; 
  3.     int status; 
  4.      
  5.     proxy = spi_alloc_device(master);   //3.1 spi设备初始化 
  6.     if (!proxy) 
  7.         return NULL; 
  8.     WARN_ON(strlen(chip->modalias) >= sizeof(proxy->modalias)); 
  9.     proxy->chip_select = chip->chip_select;   //片选 
  10.     proxy->max_speed_hz = chip->max_speed_hz; //最大速率 
  11.     proxy->mode = chip->mode; //模式 
  12.     proxy->irq = chip->irq;   //中断号 
  13.     strlcpy(proxy->modalias, chip->modalias, sizeof(proxy->modalias)); 
  14.     proxy->dev.platform_data = (void *) chip->platform_data; 
  15.     proxy->controller_data = chip->controller_data; 
  16.     proxy->controller_state = NULL; 
  17.     status = spi_add_device(proxy); //3.2 添加spi设备 
  18.     if (status < 0) { 
  19.         spi_dev_put(proxy); //增加spi设备引用计数 
  20.         return NULL; 
  21.     } 
  22.     return proxy; 
  23. EXPORT_SYMBOL_GPL(spi_new_device); 


3.1.分配spi设备

  1. struct spi_device *spi_alloc_device(struct spi_master *master) 
  2.     struct spi_device   *spi; 
  3.     struct device       *dev = master->dev.parent; 
  4.  
  5.     if (!spi_master_get(master))    //判断spi主机是否存在 
  6.         return NULL; 
  7.     spi = kzalloc(sizeof *spi, GFP_KERNEL); //分配内存 
  8.     if (!spi) { 
  9.         dev_err(dev, "cannot alloc spi_device\n"); 
  10.         spi_master_put(master); //增加主机引用计数 
  11.         return NULL; 
  12.     } 
  13.     spi->master = master;    //设置spi主机 
  14.     spi->dev.parent = dev;   //spi设备文件的父设备为spi主机设备文件的父设备 
  15.     spi->dev.bus = &spi_bus_type;    //总线类型 
  16.     spi->dev.release = spidev_release;   //释放方法 
  17.     device_initialize(&spi->dev);    //设备初始化 
  18.     return spi; 
  19. EXPORT_SYMBOL_GPL(spi_alloc_device); 


3.2 添加spi设备

  1. int spi_add_device(struct spi_device *spi) 
  2.     static DEFINE_MUTEX(spi_add_lock); 
  3.     struct device *dev = spi->master->dev.parent; 
  4.     struct device *d; 
  5.     int status; 
  6.  
  7.     if (spi->chip_select >= spi->master->num_chipselect) { 
  8.         dev_err(dev, "cs%d >= max %d\n",spi->chip_select,spi->master->num_chipselect); 
  9.         return -EINVAL; 
  10.     } 
  11.     dev_set_name(&spi->dev, "%s.%u", dev_name(&spi->master->dev),spi->chip_select); 
  12.     mutex_lock(&spi_add_lock); 
  13.     d = bus_find_device_by_name(&spi_bus_type, NULL, dev_name(&spi->dev));   //查找总线上的spi设备 
  14.     if (d != NULL) {    //判断是否已经在使用了 
  15.         dev_err(dev, "chipselect %d already in use\n",spi->chip_select); 
  16.         put_device(d); 
  17.         status = -EBUSY; 
  18.         goto done; 
  19.     } 
  20.     status = spi_setup(spi);    //调用spi主机 setup方法 
  21.     if (status < 0) { 
  22.         dev_err(dev, "can't setup %s, status %d\n",dev_name(&spi->dev), status); 
  23.         goto done; 
  24.     } 
  25.     status = device_add(&spi->dev);  //添加设备 
  26.     if (status < 0) 
  27.         dev_err(dev, "can't add %s, status %d\n",dev_name(&spi->dev), status); 
  28.     else 
  29.         dev_dbg(dev, "registered child %s\n", dev_name(&spi->dev)); 
  30. done: 
  31.     mutex_unlock(&spi_add_lock); 
  32.     return status; 
  33. EXPORT_SYMBOL_GPL(spi_add_device); 


3.2.1 spi setup方法

  1. int spi_setup(struct spi_device *spi) 
  2.     unsigned    bad_bits; 
  3.     int status; 
  4.     bad_bits = spi->mode & ~spi->master->mode_bits;    //比较spi设备的模式和spi主机支持的模式 
  5.     if (bad_bits) { //存在不支持的模式 
  6.         dev_err(&spi->dev, "setup: unsupported mode bits %x\n",bad_bits); 
  7.         return -EINVAL; 
  8.     } 
  9.     if (!spi->bits_per_word) //若没设置设备的每个字含多少位 
  10.         spi->bits_per_word = 8;  //则默认设置为8 
  11.  
  12.     status = spi->master->setup(spi); //调用spi主机的setup方法 
  13.  
  14.     dev_dbg(&spi->dev, "setup mode %d, %s%s%s%s""%u bits/w, %u Hz max --> %d\n"
  15.             (int) (spi->mode & (SPI_CPOL | SPI_CPHA)),(spi->mode & SPI_CS_HIGH) ? "cs_high, " : ""
  16.             (spi->mode & SPI_LSB_FIRST) ? "lsb, " : "",(spi->mode & SPI_3WIRE) ? "3wire, " : ""
  17.             (spi->mode & SPI_LOOP) ? "loopback, " : "",spi->bits_per_word, spi->max_speed_hz,status); 
  18.     return status; 
  19. EXPORT_SYMBOL_GPL(spi_setup); 


二.spi板级设备

1.板级设备结构体

  1. struct spi_board_info { 
  2.     char    modalias[SPI_NAME_SIZE];    //名字 
  3.     const void  *platform_data; //平台数据 
  4.     void    *controller_data;   //控制器数据 
  5.     int irq;            //中断号 
  6.     u32 max_speed_hz;   //最大速率 
  7.     u16 bus_num;        //spi总线编号 
  8.     u16 chip_select;    //片选 
  9.     u8  mode;           //模式 
  10. }; 


2.板级设备注册(静态注册,一般在板级初始化函数中调用)

  1. int __init spi_register_board_info(struct spi_board_info const *info, unsigned n) 
  2.     struct boardinfo *bi; 
  3.     int i; 
  4.  
  5.     bi = kzalloc(n * sizeof(*bi), GFP_KERNEL);  //分配内存 
  6.     if (!bi) 
  7.         return -ENOMEM; 
  8.     for (i = 0; i < n; i++, bi++, info++) { 
  9.         struct spi_master *master; 
  10.         memcpy(&bi->board_info, info, sizeof(*info));    //设置bi的板级信息 
  11.         mutex_lock(&board_lock); 
  12.         list_add_tail(&bi->list, &board_list);   //添加bi->list到全局board_list链表 
  13.         list_for_each_entry(master, &spi_master_list, list) //遍历spi主机链表 
  14.             spi_match_master_to_boardinfo(master, &bi->board_info); 
  15.         mutex_unlock(&board_lock); 
  16.     } 
  17.     return 0; 


2.1.spi板级设备与spi主机匹配

  1. static void spi_match_master_to_boardinfo(struct spi_master *master,struct spi_board_info *bi) 
  2.     struct spi_device *dev; 
  3.     if (master->bus_num != bi->bus_num) 
  4.         return
  5.     dev = spi_new_device(master, bi); 
  6.     if (!dev) 
  7.         dev_err(master->dev.parent, "can't create new device for %s\n",bi->modalias); 


三.spi设备驱动

1.spi设备驱动结构体

  1. struct spi_driver { 
  2.     const struct spi_device_id *id_table;   //spi设备id表 
  3.     int     (*probe)(struct spi_device *spi);   //probe方法(探测到设备) 
  4.     int     (*remove)(struct spi_device *spi);  //remove方法(设备移除) 
  5.     void    (*shutdown)(struct spi_device *spi);    //shutdown方法(关闭设备) 
  6.     int     (*suspend)(struct spi_device *spi, pm_message_t mesg);  //suspend方法(挂起设备) 
  7.     int     (*resume)(struct spi_device *spi);  //resume方法(唤醒设备) 
  8.     struct device_driver    driver; //设备驱动文件 
  9. }; 


2.spi设备驱动注册

  1. int spi_register_driver(struct spi_driver *sdrv) 
  2.     sdrv->driver.bus = &spi_bus_type;    //总线类型 
  3.     if (sdrv->probe) //若存在probe方法 
  4.         sdrv->driver.probe = spi_drv_probe;  //设置其设备驱动文件的probe方法为spi_drv_probe 
  5.     if (sdrv->remove)    //若存在remove方法 
  6.         sdrv->driver.remove = spi_drv_remove;    //设置其设备驱动文件的remove方法为spi_drv_remove 
  7.     if (sdrv->shutdown)  ////若存在shutdown方法 
  8.         sdrv->driver.shutdown = spi_drv_shutdown;    //设置其设备驱动文件的shutdown方法为spi_drv_shutdown 
  9.     return driver_register(&sdrv->driver);   //注册设备驱动 
  10. EXPORT_SYMBOL_GPL(spi_register_driver); 

这里的probe方法会在设备与驱动匹配的时候给调用
参看really_probe函数的部分代码

  1. if (dev->bus->probe) {        //若总线有probe方法(spi子系统的没有) 
  2.     ret = dev->bus->probe(dev);   //则调用总线的probe方法 
  3.     if (ret) 
  4.         goto probe_failed; 
  5. }  
  6. else if (drv->probe) {   //若存在设备驱动的probe方法 
  7.     ret = drv->probe(dev);       //则调用设备驱动的probe方法 
  8.     if (ret) 
  9.         goto probe_failed; 


2.1 spi_drv_probe

  1. static int spi_drv_probe(struct device *dev) 
  2.     const struct spi_driver *sdrv = to_spi_driver(dev->driver);  //根据设备文件的设备驱动找到spi设备驱动 
  3.     return sdrv->probe(to_spi_device(dev));  //调用spi设备驱动的probe方法 


3.spi设备驱动注销

  1. static inline void spi_unregister_driver(struct spi_driver *sdrv) 
  2.     if (sdrv) 
  3.         driver_unregister(&sdrv->driver);    //注销设备驱动 


四.spi主机

1.spi主机结构体

  1. struct spi_master { 
  2.     struct device   dev;    //spi主机设备文件 
  3.     struct list_head list; 
  4.     s16 bus_num;    //spi总线号 
  5.     u16 num_chipselect; //片选号 
  6.     u16 dma_alignment;  //dma算法 
  7.     u16 mode_bits;  //模式位 
  8.     u16 flags;  //传输类型标志 
  9.     spinlock_t  bus_lock_spinlock;  //spi总线自旋锁 
  10.     struct mutex    bus_lock_mutex; //spi总线互斥锁 
  11.     bool    bus_lock_flag;  //上锁标志 
  12.     int (*setup)(struct spi_device *spi);   //setup方法 
  13.     int (*transfer)(struct spi_device *spi,struct spi_message *mesg);   //传输方法 
  14.     void    (*cleanup)(struct spi_device *spi); //cleanup方法 
  15. }; 


1.2.flags标志

  1. #define SPI_MASTER_HALF_DUPLEX  BIT(0)  //半双工 
  2. #define SPI_MASTER_NO_RX    BIT(1)      //不读 
  3. #define SPI_MASTER_NO_TX    BIT(2)      //不写 


2.spi主机初始化spi_alloc_master

  1. struct spi_master *spi_alloc_master(struct device *dev, unsigned size) 
  2.     struct spi_master   *master; 
  3.     if (!dev) 
  4.         return NULL; 
  5.     master = kzalloc(size + sizeof *master, GFP_KERNEL);    //分配内存 
  6.     if (!master) 
  7.         return NULL; 
  8.     device_initialize(&master->dev); //初始化主机设备文件 
  9.     master->dev.class = &spi_master_class;   //指定设备类spi_master_class 
  10.     master->dev.parent = get_device(dev);    //设置spi主机设备的父设备 
  11.     spi_master_set_devdata(master, &master[1]); //设置设备数据 
  12.     return master; 
  13. EXPORT_SYMBOL_GPL(spi_alloc_master); 


3.注册spi主机

  1. int spi_register_master(struct spi_master *master) 
  2.     static atomic_t dyn_bus_id = ATOMIC_INIT((1<<15) - 1); 
  3.     struct device   *dev = master->dev.parent;   //获得spi主机设备的父设备 
  4.     struct boardinfo    *bi; 
  5.     int status = -ENODEV; 
  6.     int dynamic = 0; 
  7.  
  8.     if (!dev) 
  9.         return -ENODEV; 
  10.     if (master->num_chipselect == 0) //判断片选个数 
  11.         return -EINVAL; 
  12.     if (master->bus_num < 0) {    //验证spi总线编号 
  13.         master->bus_num = atomic_dec_return(&dyn_bus_id); 
  14.         dynamic = 1; 
  15.     } 
  16.     spin_lock_init(&master->bus_lock_spinlock); 
  17.     mutex_init(&master->bus_lock_mutex); 
  18.      
  19.     master->bus_lock_flag = 0; 
  20.     dev_set_name(&master->dev, "spi%u", master->bus_num); //设置spi主机设备名 
  21.     status = device_add(&master->dev);   //添加spi主机设备 
  22.     if (status < 0) 
  23.         goto done; 
  24.     dev_dbg(dev, "registered master %s%s\n", dev_name(&master->dev),dynamic ? " (dynamic)" : ""); 
  25.     mutex_lock(&board_lock); 
  26.     list_add_tail(&master->list, &spi_master_list);  //spi主机list链表添加进全局spi_master_list链表 
  27.     list_for_each_entry(bi, &board_list, list)      //遍历全局board_list查找bi结构体 
  28.         spi_match_master_to_boardinfo(master, &bi->board_info);  //找到匹配的板级spi设备 
  29.          
  30.     mutex_unlock(&board_lock); 
  31.     status = 0; 
  32.     of_register_spi_devices(master); 
  33. done: 
  34.     return status; 
  35. EXPORT_SYMBOL_GPL(spi_register_master); 


3.1 匹配spi主机和板级spi设备

  1. static void spi_match_master_to_boardinfo(struct spi_master *master,struct spi_board_info *bi) 
  2.     struct spi_device *dev; 
  3.     if (master->bus_num != bi->bus_num)   //判断是否所属的spi总线 
  4.         return
  5.     dev = spi_new_device(master, bi);   //添加新的spi设备 
  6.     if (!dev) 
  7.         dev_err(master->dev.parent, "can't create new device for %s\n",bi->modalias); 

在注册板级设备或主机设备的时候都会添加
spi板级设备添加进board_list链表,spi主机设备添加进spi_master_list链表
不管是先注册spi板级设备还是先注册spi主机设备
都会调用list_for_each_entry遍历对应的要匹配的设备的链表,查找是否有匹配的例子
若找到都会调用spi_match_master_to_boardinfo函数添加spi设备

4.注销spi主机

  1. void spi_unregister_master(struct spi_master *master) 
  2.     int dummy; 
  3.     mutex_lock(&board_lock); 
  4.     list_del(&master->list); //删除链表 
  5.     mutex_unlock(&board_lock); 
  6.     dummy = device_for_each_child(&master->dev, NULL, __unregister); //调用__unregister函数注销子设备 
  7.     device_unregister(&master->dev); //注销设备 
  8. EXPORT_SYMBOL_GPL(spi_unregister_master); 


4.1 注销挂载该总线上的spi子设备

  1. static int __unregister(struct device *dev, void *null) 
  2.     spi_unregister_device(to_spi_device(dev)); 
  3.     return 0; 


5.spi主机设备类

  1. static struct class spi_master_class = { 
  2.     .name   = "spi_master"
  3.     .owner  = THIS_MODULE, 
  4.     .dev_release    = spi_master_release, 
  5. }; 


五.spi总线

1.spi总线结构体

  1. struct bus_type spi_bus_type = { 
  2.     .name       = "spi"
  3.     .dev_attrs  = spi_dev_attrs, 
  4.     .match      = spi_match_device, //匹配方法 
  5.     .uevent     = spi_uevent, 
  6.     .suspend    = spi_suspend, 
  7.     .resume     = spi_resume, 
  8. }; 
  9. EXPORT_SYMBOL_GPL(spi_bus_type); 


2.设备匹配方法spi_match_device

前面的匹配方法是spi板级设备与spi主机设备的匹配方法,匹配的结果是添加新spi设备spi_new_device
这里的匹配是spi设备和spi驱动的匹配,匹配的结果是会调用spi驱动的设备驱动文件probe方法,既spi_drv_probe

  1. static int spi_match_device(struct device *dev, struct device_driver *drv) 
  2.     const struct spi_device *spi = to_spi_device(dev); 
  3.     const struct spi_driver *sdrv = to_spi_driver(drv); 
  4.  
  5.     if (of_driver_match_device(dev, drv))   //设备文件驱动表的匹配 
  6.         return 1; 
  7.     if (sdrv->id_table)  //spi设备驱动存在支持id表 
  8.         return !!spi_match_id(sdrv->id_table, spi);  //spi设备驱动表的匹配 
  9.     return strcmp(spi->modalias, drv->name) == 0; //比较spi设备的名字和spi设备驱动的名字 


2.1 of_driver_match_device

  1. static inline int of_driver_match_device(const struct device *dev,const struct device_driver *drv) 
  2.     return of_match_device(drv->of_match_table, dev) != NULL;    //调用of_match_device函数 


2.1.1 of_match_device

  1. const struct of_device_id *of_match_device(const struct of_device_id *matches,const struct device *dev) 
  2.     if ((!matches) || (!dev->of_node))   //id表和设备节点都不存在 
  3.         return NULL;    //则返回 
  4.     return of_match_node(matches, dev->of_node); //调用of_match_node函数 
  5. EXPORT_SYMBOL(of_match_device); 


2.1.1.1 of_match_node //drv->of_match_table,dev->of_node

  1. const struct of_device_id *of_match_node(const struct of_device_id *matches,const struct device_node *node) 
  2.     while (matches->name[0] || matches->type[0] || matches->compatible[0]) {   //名字,类型,兼容方法有一个存在 
  3.         int match = 1; 
  4.         if (matches->name[0])    //判断名字 
  5.             match &= node->name && !strcmp(matches->name, node->name); 
  6.         if (matches->type[0])    //判断类型 
  7.             match &= node->type && !strcmp(matches->type, node->type); 
  8.         if (matches->compatible[0])  //兼容方法 
  9.             match &= of_device_is_compatible(node,matches->compatible); 
  10.         if (match)  //匹配 
  11.             return matches; //返回匹配的id 
  12.         matches++;  //matches指针++,指向下一个id 
  13.     } 
  14.     return NULL; 
  15. EXPORT_SYMBOL(of_match_node); 


2.2 spi_match_id

  1. static const struct spi_device_id *spi_match_id(const struct spi_device_id *id,const struct spi_device *sdev) 
  2.     while (id->name[0]) {    //id表的成员的名字域不为空 
  3.         if (!strcmp(sdev->modalias, id->name))    //则判断其名字是否与spi设备的名字一样 
  4.             return id;  //一样则返回该id 
  5.         id++;   //id表指针++,指向下一个id 
  6.     } 
  7.     return NULL; 


六 spi消息和spi传输

1.spi消息结构体

  1. struct spi_message { 
  2.     struct list_head    transfers;  //spi传输事务链表头 
  3.     struct spi_device   *spi;   //所属spi设备 
  4.     unsigned    is_dma_mapped:1; 
  5.     void    (*complete)(void *context);  
  6.     void    *context; 
  7.     unsigned    actual_length; 
  8.     int     status; //传输状态 
  9.     struct list_head    queue; 
  10.     void    *state; 
  11. }; 


2.初始化spi消息

  1. static inline void spi_message_init(struct spi_message *m) 
  2.     memset(m, 0, sizeof *m); 
  3.     INIT_LIST_HEAD(&m->transfers);   //初始化spi消息的事务链表头 


3.添加传输事务到spi传输链表

  1. static inline void spi_message_add_tail(struct spi_transfer *t, struct spi_message *m) 
  2.     list_add_tail(&t->transfer_list, &m->transfers); 


4.spi传输结构体

  1. struct spi_transfer { 
  2.     const void  *tx_buf;    //发送缓冲区指针 
  3.     void        *rx_buf;    //接收缓冲区指针 
  4.     unsigned    len;    //消息长度 
  5.     dma_addr_t  tx_dma; //DMA发送地址 
  6.     dma_addr_t  rx_dma; //DMA接收地址 
  7.     unsigned    cs_change:1;     
  8.     u8      bits_per_word;  //一个字多少位 
  9.     u16     delay_usecs;    //毫秒级延时 
  10.     u32     speed_hz;   //速率 
  11.     struct list_head transfer_list; //传输链表头 
  12. }; 


七.spi子系统的初始化spi_init

  1. static int __init spi_init(void
  2.     int status; 
  3.     buf = kmalloc(SPI_BUFSIZ, GFP_KERNEL);  //分配数据收发缓冲区 
  4.     if (!buf) { 
  5.         status = -ENOMEM; 
  6.         goto err0; 
  7.     } 
  8.     status = bus_register(&spi_bus_type);   //注册spi总线 
  9.     if (status < 0) 
  10.         goto err1;   
  11.     status = class_register(&spi_master_class); //注册spi主机类 "/sys/class/spi_master" 
  12.     if (status < 0) 
  13.         goto err2; 
  14.     return 0; 
  15. err2: 
  16.     bus_unregister(&spi_bus_type); 
  17. err1: 
  18.     kfree(buf); 
  19.     buf = NULL; 
  20. err0: 
  21.     return status; 
  22. postcore_initcall(spi_init);    //入口声明 #define postcore_initcall(fn)    __define_initcall("2",fn,2)  


八.spi子系统的API

1.spi读 spi_read

  1. static inline int spi_read(struct spi_device *spi, u8 *buf, size_t len) 
  2.     struct spi_transfer t = { 
  3.             .rx_buf     = buf, 
  4.             .len        = len, 
  5.         }; 
  6.     struct spi_message  m; 
  7.     spi_message_init(&m);   //spi消息初始化(初始化传输事务链表头) 
  8.     spi_message_add_tail(&t, &m);   //添加spi传输到spi消息传输链表 
  9.     return spi_sync(spi, &m);   //spi同步传输 


2.spi写 spi_write

  1. static inline int spi_write(struct spi_device *spi, const u8 *buf, size_t len) 
  2.     struct spi_transfer t = { 
  3.             .tx_buf     = buf, 
  4.             .len        = len, 
  5.         }; 
  6.     struct spi_message  m; 
  7.     spi_message_init(&m);   //spi消息初始化(初始化传输事务链表头) 
  8.     spi_message_add_tail(&t, &m);   //添加spi传输到spi消息传输链表 
  9.     return spi_sync(spi, &m);   //spi同步传输 

spi的读写操作都是初始化一个spi_transfer传输结构体,并将其添加进spi消息传输事务链表中
然后通过spi_sync来同步读写操作,接着看下spi_sync的具体代码


2.1 spi_sync

  1. int spi_sync(struct spi_device *spi, struct spi_message *message) 
  2.     return __spi_sync(spi, message, 0); //调用__spi_sync函数 
  3. EXPORT_SYMBOL_GPL(spi_sync); 


2.1.1 __spi_sync函数

  1. static int __spi_sync(struct spi_device *spi, struct spi_message *message,int bus_locked) 
  2.     DECLARE_COMPLETION_ONSTACK(done);   //声明一个工作队列 
  3.     int status; 
  4.     struct spi_master *master = spi->master; //获取spi主机 
  5.  
  6.     message->complete = spi_complete;    //设置spi消息的complete方法为spi_complete 
  7.     message->context = &done;    //设置spi消息的context 
  8.     if (!bus_locked)     
  9.         mutex_lock(&master->bus_lock_mutex); //上锁 
  10.     status = spi_async_locked(spi, message); 
  11.     if (!bus_locked) 
  12.         mutex_unlock(&master->bus_lock_mutex);   //解锁 
  13.     if (status == 0) { 
  14.         wait_for_completion(&done); //等待完成 
  15.         status = message->status;    //设置spi消息传输状态 
  16.     } 
  17.     message->context = NULL; 
  18.     return status; 


2.1.1.1 spi_async_locked 函数

  1. int spi_async_locked(struct spi_device *spi, struct spi_message *message) 
  2.     struct spi_master *master = spi->master; //获取spi主机 
  3.     int ret; 
  4.     unsigned long flags; 
  5.     spin_lock_irqsave(&master->bus_lock_spinlock, flags);    //上自旋锁 
  6.     ret = __spi_async(spi, message);    //调用了spi异步同步方法 
  7.     spin_unlock_irqrestore(&master->bus_lock_spinlock, flags);   //解自旋锁 
  8.     return ret; 
  9. EXPORT_SYMBOL_GPL(spi_async_locked); 


2.1.1.1.1 __spi_async函数(异步)

  1. static int __spi_async(struct spi_device *spi, struct spi_message *message) 
  2.     struct spi_master *master = spi->master; 
  3.     //主机为半双工或spi设备为3线设备 
  4.     if ((master->flags & SPI_MASTER_HALF_DUPLEX)|| (spi->mode & SPI_3WIRE)) { 
  5.         struct spi_transfer *xfer; 
  6.         unsigned flags = master->flags;   
  7.         list_for_each_entry(xfer, &message->transfers, transfer_list) {  //遍历spi消息的传输事务链表 
  8.             if (xfer->rx_buf && xfer->tx_buf) //判断接收或发送缓冲区是否为空 
  9.                 return -EINVAL; 
  10.             if ((flags & SPI_MASTER_NO_TX) && xfer->tx_buf)  //检验无spi数据发送的情况 
  11.                 return -EINVAL; 
  12.             if ((flags & SPI_MASTER_NO_RX) && xfer->rx_buf)  //检验无spi数据接收的情况 
  13.                 return -EINVAL; 
  14.         } 
  15.     } 
  16.     message->spi = spi;  //设置spi消息所属的spi设备 
  17.     message->status = -EINPROGRESS;  //设置spi消息的传输状态 
  18.     return master->transfer(spi, message);   //调用spi主机的transfer方法,收发spi信息给spi设备 


3.spi先写后读 spi_write_then_read

  1. int spi_write_then_read(struct spi_device *spi,const u8 *txbuf, unsigned n_tx,u8 *rxbuf, unsigned n_rx) 
  2.     static DEFINE_MUTEX(lock); 
  3.     int status; 
  4.     struct spi_message  message; 
  5.     struct spi_transfer x[2];   //声明两个spi传输结构体 
  6.     u8  *local_buf; 
  7.      
  8.     if ((n_tx + n_rx) > SPI_BUFSIZ)  //验证发送和接收的数据总和是否溢出 
  9.         return -EINVAL; 
  10.     spi_message_init(&message);     //spi消息初始化(初始化传输事务链表头) 
  11.     memset(x, 0, sizeof x); // 
  12.     if (n_tx) { 
  13.         x[0].len = n_tx; 
  14.         spi_message_add_tail(&x[0], &message);  //添加spi传输到spi消息传输链表 
  15.     } 
  16.     if (n_rx) { 
  17.         x[1].len = n_rx; 
  18.         spi_message_add_tail(&x[1], &message);  //添加spi传输到spi消息传输链表 
  19.     } 
  20.     if (!mutex_trylock(&lock)) {    //尝试上锁 失败 
  21.         local_buf = kmalloc(SPI_BUFSIZ, GFP_KERNEL);    //则分配local_buf内存 
  22.         if (!local_buf) 
  23.             return -ENOMEM; 
  24.     } else 
  25.         local_buf = buf;    //指向默认分配好内存的缓冲区(spi_init中初始化的) 
  26.     memcpy(local_buf, txbuf, n_tx); //发送缓冲区的内容复制到local_buf中 
  27.     x[0].tx_buf = local_buf;        //发送的spi传输结构体 
  28.     x[1].rx_buf = local_buf + n_tx; //接收的spi传输结构体 
  29.     status = spi_sync(spi, &message);   //spi同步传输--发送数据 
  30.     if (status == 0) 
  31.         memcpy(rxbuf, x[1].rx_buf, n_rx);   //接收返回的数据复制到rxbuf中 
  32.     if (x[0].tx_buf == buf) 
  33.         mutex_unlock(&lock);    //解锁 
  34.     else 
  35.         kfree(local_buf);   //释放内存 
  36.     return status; 
  37. EXPORT_SYMBOL_GPL(spi_write_then_read); 


4.spi写8位后读8位数据 spi_w8r8

  1. static inline ssize_t spi_w8r8(struct spi_device *spi, u8 cmd) 
  2.     ssize_t status; 
  3.     u8  result; 
  4.     status = spi_write_then_read(spi, &cmd, 1, &result, 1); //写8位读8位 
  5.     return (status < 0) ? status : result; 


5.spi写8位数据后读16位数据 spi_w8r16

  1. static inline ssize_t spi_w8r16(struct spi_device *spi, u8 cmd) 
  2.     ssize_t status; 
  3.     u16 result; 
  4.     status = spi_write_then_read(spi, &cmd, 1, (u8 *) &result, 2);  //写8位读16位 
  5.     return (status < 0) ? status : result; 

这里的API是内核空间使用的接口,设备驱动程序调用这些API直接操作spi的读写操作,来完成任务

阅读(759) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~