linux設備驅動 spi詳解2-通用接口層


SPI通用接口層用於把具體SPI設備的協議驅動和SPI控制器驅動聯接在一起,通用接口層除了為協議驅動和控制器驅動提供一系列的標准接口API,同時還為這些接口API定義了相應的數據結構,這些數據結構一部分是SPI設備、SPI協議驅動和SPI控制器的數據抽象,一部分是為了協助數據傳輸而定義的數據結構。另外,通用接口層還負責SPI系統與Linux設備模型相關的初始化工作。

source定義位於:drivers/spi/spi.c和include\linux\spi\spi.h

1 spi_device

對應某個特定的slave,即spi從機

 1 struct spi_device {
 2     struct device dev;//代表該spi設備的device結構 3 struct spi_master *master;//指向該spi設備所使用的控制器 4  u32 max_speed_hz;//該設備的最大工作時鍾頻率 5  u8 chip_select;//在控制器中的片選引腳 6  u8 mode;//設備的工作模式,包括時鍾格式,片選信號的有效電平等等,針對時鍾相位CPHA(0或1)和時鍾極性CPOL(0或1)的不同組合,將spi分成四種模式,如linux 設備驅動SPI詳解1時序圖所示
/*spi傳輸模式*/
7 #define SPI_CPHA 0x01 /* clock phase */時鍾相位 8 #define SPI_CPOL 0x02 /* clock polarity */時鍾極性 9 #define SPI_MODE_0 (0|0) /* (original MicroWire) */模式0 10 #define SPI_MODE_1 (0|SPI_CPHA) //模式1 11 #define SPI_MODE_2 (SPI_CPOL|0) //模式2 12 #define SPI_MODE_3 (SPI_CPOL|SPI_CPHA) //模式3 13 #define SPI_CS_HIGH 0x04 /* chipselect active high*/片選高電平 14 #define SPI_LSB_FIRST 0x08 /* per-word bits-on-wire */ 15 #define SPI_3WIRE 0x10 /* SI/SO signals shared */ 16 #define SPI_LOOP 0x20 /* loopback mode */ 17 #define SPI_NO_CS 0x40 /* 1 dev/bus, no chipselect */單個設備占用一根SPI總線,所以沒片選  18 #define SPI_READY 0x80 /* slave pulls low to pause */ 19 u8 bits_per_word;//設備每個單位數據所需要的比特數,如果傳輸是以字節為單位的話就設置為8,相應地,如果是以2個字節為單位則設置為16。 20 int irq;//設備使用的irq編號 21 void *controller_state;//控制器狀態 22 void *controller_data;//控制器數據 23 char modalias[SPI_NAME_SIZE];//該設備的名字,用於spi總線和驅動進行配對 24 int cs_gpio; /* chip select gpio */片選信號的gpio編號,通常不用我們自己設置,接口層會根據上面的chip_select字段在spi_master結構中進行查找並賦值 25 26 /* 27 * likely need more hooks for more protocol options affecting how 28 * the controller talks to each chip, like: 29 * - memory packing (12 bit samples into low bits, others zeroed) 30 * - priority 31 * - drop chipselect after each word 32 * - chipselect delays 33 * - ... 34 */ 35 }

 1.1 spi設備的添加spi_new_device

1 struct spi_device *spi_new_device(struct spi_master *master,
 2                   struct spi_board_info *chip) 3 { 4 struct spi_device *proxy; 5 int status; 6 7 /* NOTE: caller did any chip->bus_num checks necessary. 8 * 9 * Also, unless we change the return value convention to use 10 * error-or-pointer (not NULL-or-pointer), troubleshootability 11 * suggests syslogged diagnostics are best here (ugh). 12 */ 13 14 proxy = spi_alloc_device(master);//分配spi_device 15 if (!proxy) 16 return NULL; 17 18 WARN_ON(strlen(chip->modalias) >= sizeof(proxy->modalias)); 19 20 proxy->chip_select = chip->chip_select;//片選 21 proxy->max_speed_hz = chip->max_speed_hz;//最大速率 22 proxy->mode = chip->mode;//模式 23 proxy->irq = chip->irq;//中斷號 24 strlcpy(proxy->modalias, chip->modalias, sizeof(proxy->modalias)); 25 proxy->dev.platform_data = (void *) chip->platform_data; 26 proxy->controller_data = chip->controller_data; 27 proxy->controller_state = NULL; 28 29 status = spi_add_device(proxy);//添加spi_device 30 if (status < 0) { 31  spi_dev_put(proxy);//增加引用計數 32 return NULL; 33  } 34 35 return proxy; 36 }

1.2 首先調用函數spi_alloc_device,分配一個spi_device,並通過device_initialize初始化。

 1 struct spi_device *spi_alloc_device(struct spi_master *master)
 2 { 3 struct spi_device *spi; 4 struct device *dev = master->dev.parent; 5 6 if (!spi_master_get(master))//判斷spi主機是否存在 7 return NULL; 8 9 spi = kzalloc(sizeof *spi, GFP_KERNEL);//分配內存 10 if (!spi) { 11 dev_err(dev, "cannot alloc spi_device\n"); 12  spi_master_put(master);//增加spi主機引用計數 13 return NULL; 14  } 15 16 spi->master = master;//設置spi主機 17 spi->dev.parent = &master->dev;//spi主機設備文件設置為spi設備的父設備 18 spi->dev.bus = &spi_bus_type;//總線類型為spi_bus_type 19 spi->dev.release = spidev_release;//釋放函數 20 spi->cs_gpio = -ENOENT; 21 device_initialize(&spi->dev);//初始化spi設備 22 return spi; 23 }

在spi.c中kernel定義的

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     .pm        = &spi_pm,
7 };

1.3然后調用spi_add_device通過device_add向總線上添加設備,最終在bus下生成相應的設備

 1 int spi_add_device(struct spi_device *spi)
 2 { 3 static DEFINE_MUTEX(spi_add_lock); 4 struct spi_master *master = spi->master; 5 struct device *dev = master->dev.parent; 6 struct device *d; 7 int status; 8 9 /* Chipselects are numbered 0..max; validate. */ 10 if (spi->chip_select >= master->num_chipselect) {//片選信號的判定,即從機個數不能超過主機可掛載個數限制 11 dev_err(dev, "cs%d >= max %d\n", 12 spi->chip_select, 13 master->num_chipselect); 14 return -EINVAL; 15  } 16 17 /* Set the bus ID string */ 18 dev_set_name(&spi->dev, "%s.%u", dev_name(&spi->master->dev), 19 spi->chip_select); 20 21 22 /* We need to make sure there's no other device with this 23 * chipselect **BEFORE** we call setup(), else we'll trash 24 * its configuration. Lock against concurrent add() calls. 25 */ 26 mutex_lock(&spi_add_lock); 27 28 d = bus_find_device_by_name(&spi_bus_type, NULL, dev_name(&spi->dev));//查找總線上的spi設備 29 if (d != NULL) {//判斷是否已經在使用了 30 dev_err(dev, "chipselect %d already in use\n", 31 spi->chip_select); 32  put_device(d); 33 status = -EBUSY; 34 goto done; 35  } 36 37 if (master->cs_gpios) 38 spi->cs_gpio = master->cs_gpios[spi->chip_select]; 39 40 /* Drivers may modify this initial i/o setup, but will 41 * normally rely on the device being setup. Devices 42 * using SPI_CS_HIGH can't coexist well otherwise... 43 */ 44 status = spi_setup(spi);//調用spi主機 setup方法  45 if (status < 0) { 46 dev_err(dev, "can't setup %s, status %d\n", 47 dev_name(&spi->dev), status); 48 goto done; 49  } 50 51 /* Device may be bound to an active driver when this returns */ 52 status = device_add(&spi->dev); 53 if (status < 0) 54 dev_err(dev, "can't add %s, status %d\n", 55 dev_name(&spi->dev), status); 56 else 57 dev_dbg(dev, "registered child %s\n", dev_name(&spi->dev)); 58 59 done: 60 mutex_unlock(&spi_add_lock); 61 return status; 62 }

1.4 spi_setup

 1 int spi_setup(struct spi_device *spi)
 2 {
 3     unsigned    bad_bits;
 4     int        status = 0;
 5 
 6     /* help drivers fail *cleanly* when they need options
 7      * that aren't supported with their current master
 8      */
 9     bad_bits = spi->mode & ~spi->master->mode_bits;//比較spi設備的模式和spi主機支持的模式
10     if (bad_bits) {//存在不支持的模式  
11         dev_err(&spi->dev, "setup: unsupported mode bits %x\n",
12             bad_bits);
13         return -EINVAL;
14     }
15 
16     if (!spi->bits_per_word)//若沒設置設備的每個字含多少位
17         spi->bits_per_word = 8;//則默認設置為8
18 
19     if (spi->master->setup)
20         status = spi->master->setup(spi);//調用spi主機的setup方法,對於2440來說,driver/spi/spi-s3c24xx.c中有定義master->setup
21 
22     dev_dbg(&spi->dev, "setup mode %d, %s%s%s%s"
23                 "%u bits/w, %u Hz max --> %d\n",
24             (int) (spi->mode & (SPI_CPOL | SPI_CPHA)),
25             (spi->mode & SPI_CS_HIGH) ? "cs_high, " : "",
26             (spi->mode & SPI_LSB_FIRST) ? "lsb, " : "",
27             (spi->mode & SPI_3WIRE) ? "3wire, " : "",
28             (spi->mode & SPI_LOOP) ? "loopback, " : "",
29             spi->bits_per_word, spi->max_speed_hz,
30             status);
31 
32     return status;
33 }

2 spi 板機設備

2.1 板級設備結構體

 1 struct spi_board_info {
 2     /* the device name and module name are coupled, like platform_bus;
 3      * "modalias" is normally the driver name.
 4      *
 5      * platform_data goes to spi_device.dev.platform_data,
 6      * controller_data goes to spi_device.controller_data,
 7      * irq is copied too
 8      */
 9     char modalias[SPI_NAME_SIZE];//名字 10 const void *platform_data;//平台數據 11 void *controller_data;//控制器數據 12 int irq;//中斷號 13 14 /* slower signaling on noisy or low voltage boards */ 15  u32 max_speed_hz;//最大速率 16 17 18 /* bus_num is board specific and matches the bus_num of some 19 * spi_master that will probably be registered later. 20 * 21 * chip_select reflects how this chip is wired to that master; 22 * it's less than num_chipselect. 23 */ 24  u16 bus_num;//總線編號 25  u16 chip_select;//片選 26 27 /* mode becomes spi_device.mode, and is essential for chips 28 * where the default of SPI_CS_HIGH = 0 is wrong. 29 */ 30  u8 mode;//模式 31 32 /* ... may need additional spi_device chip config data here. 33 * avoid stuff protocol drivers can set; but include stuff 34 * needed to behave without being bound to a driver: 35 * - quirks like clock rate mattering when not selected 36 */ 37 }

spi_board_info結構大部分字段和spi_device結構相對應,通過spi_board_info結構,我們可以有兩種方式向系統增加spi設備:

2.2 板級設備注冊

第一種方式是在SPI控制器驅動已經被加載后,使用通用接口層的API spi_new_device 來完成:

 1 /**
 2  * spi_register_board_info - register SPI devices for a given board
 3  * @info: array of chip descriptors
 4  * @n: how many descriptors are provided
 5  * Context: can sleep
 6  *
 7  * Board-specific early init code calls this (probably during arch_initcall)
 8  * with segments of the SPI device table.  Any device nodes are created later,
 9  * after the relevant parent SPI controller (bus_num) is defined.  We keep
10  * this table of devices forever, so that reloading a controller driver will
11  * not make Linux forget about these hard-wired devices.
12  *
13  * Other code can also call this, e.g. a particular add-on board might provide
14  * SPI devices through its expansion connector, so code initializing that board
15  * would naturally declare its SPI devices.
16  *
17  * The board info passed can safely be __initdata ... but be careful of
18  * any embedded pointers (platform_data, etc), they're copied as-is.
19  */
20 int spi_register_board_info(struct spi_board_info const *info, unsigned n)
21 { 22 struct boardinfo *bi; 23 int i; 24 25 bi = kzalloc(n * sizeof(*bi), GFP_KERNEL);//分配內存 26 if (!bi) 27 return -ENOMEM; 28 29 for (i = 0; i < n; i++, bi++, info++) { 30 struct spi_master *master; 31 32 memcpy(&bi->board_info, info, sizeof(*info));//設置bi的板級信息 33 mutex_lock(&board_lock); 34 list_add_tail(&bi->list, &board_list);//添加bi->list到全局board_list鏈表 35 list_for_each_entry(master, &spi_master_list, list)遍歷spi主機鏈表和板機設備匹配 36 spi_match_master_to_boardinfo(master, &bi->board_info); 37 mutex_unlock(&board_lock); 38  } 39 40 return 0; 41 }

2.3 spi板級設備與spi主機匹配

最終通過spi_match_master_to_boardinfo 調用函數spi_match_master_to_boardinfo,還是調用1.1中的api spi_new_device

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

spi_register_board_info  會把每個spi_board_info掛在全局鏈表變量board_list上,並且遍歷已經在系統中注冊了的控制器,匹配上相應的控制器並取得它們的spi_master結構指針,最終也會通過spi_new_device函數添加SPI設備。

因為spi_register_board_info可以在板子的初始化代碼中調用,可能這時控制器驅動尚未加載,此刻無法取得相應的spi_master指針。

不過不要擔心,控制器驅動被加載時,一定會調用spi_register_master函數來注冊spi_master結構,而spi_register_master函數會反過來遍歷全局鏈表board_list上的spi_board_info,然后通過spi_new_device函數添加SPI設備。

3 spi設備驅動 spi_driver

既然有spi_device,那就必然也有spi_driver與之對應。

spi_driver就是我們編寫spi驅動是需要編寫的,其他spi_devcie 、spi platform_device和spi platform_driver linux kernel已經幫我們做好。

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);//進入待機 7 int (*resume)(struct spi_device *spi);//退出待機 8 struct device_driver driver;//設備驅動文件  9 }

id_table字段用於指定該驅動可以驅動的設備名稱,總線的匹配函數會把id_table中指定的名字和spi_device結構中的modalias字段進行比較,兩者相符即表示匹配成功,然后出發spi_driver的probe回調函數被調用,從而完成驅動程序的初始化工作。

需要注意的是,這里的spi_driver結構代表的是具體的SPI協議驅動程序。

通用接口層提供如下API來完成spi_driver的注冊:

 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)//若存在probe方法 10 sdrv->driver.probe = spi_drv_probe;//賦值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);//調用driver的注冊函數 16 }

 

4 spi總線

4.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 .pm = &spi_pm, 7 }; 8 static struct class spi_master_class = { 9 .name = "spi_master", 10 .owner = THIS_MODULE, 11 .dev_release = spi_master_release, 12 };

可見,在初始化階段,spi_init函數向系統注冊了一個名為spi的總線類型,同時也為SPI控制器注冊了一個名為spi_master的設備類。這樣,以后在sys目錄下就會出現兩個文件節點。
sys/bus/spi 和sys/class/spi_master 

4.2 spi_match_device
代表spi總線的spi_bus_type結構的match字段指向了spi_match_device函數,該函數用於匹配spi總線上的設備和驅動:

 1 static int spi_match_device(struct device *dev, struct device_driver *drv)
 2 { 3 const struct spi_device *spi = to_spi_device(dev); 4 const struct spi_driver *sdrv = to_spi_driver(drv); 5 6 /* Attempt an OF style match */ 7 if (of_driver_match_device(dev, drv)) 8 return 1; 9 10 /* Then try ACPI */ 11 if (acpi_driver_match_device(dev, drv))//設備文件驅動表的匹配 12 return 1; 13 14 if (sdrv->id_table)//spi設備驅動存在支持id表 15 return !!spi_match_id(sdrv->id_table, spi);//spi設備驅動表的匹配  16 17 return strcmp(spi->modalias, drv->name) == 0;//比較spi設備的名字和spi設備驅動的名字 18 }

4.2.1 of_driver_match_device

1 static inline int of_driver_match_device(const struct device *dev,const struct device_driver *drv)  
2 {  
3     return of_match_device(drv->of_match_table, dev) != NULL;    //調用of_match_device函數  
4 } 

4.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 {  
3     if ((!matches) || (!dev->of_node))   //id表和設備節點都不存在  
4         return NULL;    //則返回  
5     return of_match_node(matches, dev->of_node); //調用of_match_node函數  
6 }  
7 EXPORT_SYMBOL(of_match_device);  

4.2.1.1.1 of_match_node

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

4.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 {  
3     while (id->name[0]) {    //id表的成員的名字域不為空  
4         if (!strcmp(sdev->modalias, id->name))    //則判斷其名字是否與spi設備的名字一樣  
5             return id;  //一樣則返回該id  
6         id++;   //id表指針++,指向下一個id  
7     }  
8     return NULL;  
9 }  

5 spi 設備模型初始化

一般,根據linux設備模型的組織方式,各種設備會掛在合適的總線上,設備驅動和設備通過總線互相進行匹配,使得設備能夠找到正確的驅動程序進行控制和驅動。同時,性質相似的設備可以歸為某一個類的設備,它們具有某些共同的設備屬性,在設備模型上就是所謂的class。SPI設備也不例外,它們也遵循linux的設備模型的規則:

 1 static int __init spi_init(void)
 2 {
 3     int    status;
 4 
 5     buf = kmalloc(SPI_BUFSIZ, GFP_KERNEL);//分配數據收發緩沖區 
 6     if (!buf) {
 7         status = -ENOMEM;
 8         goto err0;
 9     }
10 
11     status = bus_register(&spi_bus_type);//注冊spi總線
12     if (status < 0)
13         goto err1;
14 
15     status = class_register(&spi_master_class);//注冊spi主機類 "/sys/class/spi_master"  
16     if (status < 0)
17         goto err2;
18     return 0;
19 
20 err2:
21     bus_unregister(&spi_bus_type);
22 err1:
23     kfree(buf);
24     buf = NULL;
25 err0:
26     return status;
27 }
28 postcore_initcall(spi_init);

postcore_initcall(spi_init);  指定驅動入口函數。

6 spi_message 和spi_transfer 即spi消息和spi傳輸列表

完成和SPI設備的數據傳輸工作,我們還需要另外兩個數據結構:spi_message和spi_transfer。spi_message包含了一個的spi_transfer結構序列,一旦控制器接收了一個spi_message,其中的spi_transfer應該按順序被發送,並且不能被其它spi_message打斷,所以我們認為spi_message就是一次SPI數據交換的原子操作。下面我們看看這兩個數據結構的定義:

6.1  spi_message 

 1 struct spi_message {
 2     struct list_head    transfers;//用於鏈接掛在本message下的spi_tranfer結構
 3 
 4     struct spi_device    *spi;//所屬spi設備
 5 
 6     unsigned        is_dma_mapped:1;
 7 
 8     /* REVISIT:  we might want a flag affecting the behavior of the
 9      * last transfer ... allowing things like "read 16 bit length L"
10      * immediately followed by "read L bytes".  Basically imposing
11      * a specific message scheduling algorithm.
12      *
13      * Some controller drivers (message-at-a-time queue processing)
14      * could provide that as their default scheduling algorithm.  But
15      * others (with multi-message pipelines) could need a flag to
16      * tell them about such special cases.
17      */
18 
19     /* completion is reported through a callback */
20     void            (*complete)(void *context);
21     void            *context;
22     unsigned        actual_length;
23     int            status;//傳輸狀態 
24 
25     /* for optional use by whatever driver currently owns the
26      * spi_message ...  between calls to spi_async and then later
27      * complete(), that's the spi_master controller driver.
28      */
29     struct list_head    queue;//將該message掛在代表控制器的spi_master結構的queue字段上
30     void            *state;
31 }

鏈表字段queue用於把該結構掛在代表控制器的spi_master結構的queue字段上,控制器上可以同時被加入多個spi_message進行排隊。另一個鏈表字段transfers則用於鏈接掛在本message下的spi_tranfer結構。complete回調函數則會在該message下的所有spi_transfer都被傳輸完成時被調用,以便通知協議驅動處理接收到的數據以及准備下一批需要發送的數據。

6.2 spi_transfer結構:

 1 struct spi_transfer {
 2     /* it's ok if tx_buf == rx_buf (right?)
 3      * for MicroWire, one buffer must be null
 4      * buffers must work with dma_*map_single() calls, unless
 5      *   spi_message.is_dma_mapped reports a pre-existing mapping
 6      */
 7     const void    *tx_buf;//發送緩沖區指針
 8     void        *rx_buf;//接收緩沖區指針
 9     unsigned    len;//消息長度  
10 
11     dma_addr_t    tx_dma;//DMA發送地址
12     dma_addr_t    rx_dma;//DMA接收地址
13 
14     unsigned    cs_change:1;//一個字多少位
15     u8        bits_per_word;//毫秒級延時  
16     u16        delay_usecs;
17     u32        speed_hz;
18 
19     struct list_head transfer_list;//把該transfer掛在一個spi_message結構中
20 }

首先,transfer_list鏈表字段用於把該transfer掛在一個spi_message結構中,tx_buf和rx_buf提供了非dma模式下的數據緩沖區地址,len則是需要傳輸數據的長度,tx_dma和rx_dma則給出了dma模式下的緩沖區地址。原則來講,spi_transfer才是傳輸的最小單位,之所以又引進了spi_message進行打包,我覺得原因是:有時候希望往spi設備的多個不連續的地址(或寄存器)一次性寫入,如果沒有spi_message進行把這樣的多個spi_transfer打包,因為通常真正的數據傳送工作是在另一個內核線程(工作隊列)中完成的,不打包的后果就是會造成更多的進程切換,效率降低,延遲增加,尤其對於多個不連續地址的小規模數據傳送而言就更為明顯。

6.3. 用於初始化spi_message結構,位於spi.h中

1 static inline void spi_message_init(struct spi_message *m)
2 {
3 memset(m, 0, sizeof *m);
4 INIT_LIST_HEAD(&m->transfers);
5 }

6.4 把一個spi_transfer加入到一個spi_message中(注意,只是加入,未啟動傳輸過程),和移除一個spi_transfer:

 1 static inline void
 2 spi_message_add_tail(struct spi_transfer *t, struct spi_message *m)
 3 {
 4     list_add_tail(&t->transfer_list, &m->transfers);
 5 }
 6 
 7 static inline void
 8 spi_transfer_del(struct spi_transfer *t)
 9 {
10     list_del(&t->transfer_list);
11 }

以上兩個API的組合,初始化一個spi_message並添加數個spi_transfer結構:

 1 /**
 2  * spi_message_init_with_transfers - Initialize spi_message and append transfers
 3  * @m: spi_message to be initialized
 4  * @xfers: An array of spi transfers
 5  * @num_xfers: Number of items in the xfer array
 6  *
 7  * This function initializes the given spi_message and adds each spi_transfer in
 8  * the given array to the message.
 9  */
10 static inline void
11 spi_message_init_with_transfers(struct spi_message *m,
12 struct spi_transfer *xfers, unsigned int num_xfers)
13 {
14     unsigned int i;
15 
16     spi_message_init(m);
17     for (i = 0; i < num_xfers; ++i)
18         spi_message_add_tail(&xfers[i], m);
19 }

6.5 分配一個自帶數個spi_transfer機構的spi_message:

 1 /* It's fine to embed message and transaction structures in other data
 2  * structures so long as you don't free them while they're in use.
 3  */
 4 
 5 static inline struct spi_message *spi_message_alloc(unsigned ntrans, gfp_t flags)
 6 {
 7     struct spi_message *m;
 8 
 9     m = kzalloc(sizeof(struct spi_message)
10             + ntrans * sizeof(struct spi_transfer),
11             flags);
12     if (m) {
13         unsigned i;
14         struct spi_transfer *t = (struct spi_transfer *)(m + 1);
15 
16         INIT_LIST_HEAD(&m->transfers);
17         for (i = 0; i < ntrans; i++, t++)
18             spi_message_add_tail(t, m);
19     }
20     return m;
21 }

6.6 spi_master,spi_message,spi_transfer這幾個數據結構的關系可以用下圖來描述

7 spi 子系統API

通用接口層也為一些簡單的數據傳輸提供了獨立的API來完成上述的組合過程:

1 int spi_write(struct spi_device *spi, const void *buf, size_t len);//同步方式發送數據。
2 int spi_read(struct spi_device *spi, void *buf, size_t len); //同步方式接收數據。 3 int spi_sync_transfer(struct spi_device *spi, struct spi_transfer *xfers, unsigned int num_xfers);//同步方式,直接傳送數個spi_transfer,接收和發送。 4 int spi_write_then_read(struct spi_device *spi, const void *txbuf, unsigned n_tx, void *rxbuf, unsigned n_rx); //先寫后讀。 5 ssize_t spi_w8r8(struct spi_device *spi, u8 cmd); //寫8位,然后讀8位。 6 ssize_t spi_w8r16(struct spi_device *spi, u8 cmd); //寫8位,然后讀16位。

7.1 spi讀 spi_read

定義spi.h

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

7.2 spi寫 spi_write

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

spi的讀寫操作都是初始化一個spi_transfer傳輸結構體,並將其添加進spi消息傳輸事務鏈表中。

然后通過spi_sync來同步讀寫操作,接着看下spi_sync的具體代碼

 7.3 同步傳輸spi_sync

1 int spi_sync(struct spi_device *spi, struct spi_message *message)  
2 { 3 return __spi_sync(spi, message, 0); //調用__spi_sync函數 4 } 5 EXPORT_SYMBOL_GPL(spi_sync); 

__spi_sync函數

 1 static int __spi_sync(struct spi_device *spi, struct spi_message *message,int bus_locked)  
 2 { 3 DECLARE_COMPLETION_ONSTACK(done); //聲明一個工作隊列 4 int status; 5 struct spi_master *master = spi->master; //獲取spi主機 6 7 message->complete = spi_complete; //設置spi消息的complete方法為spi_complete 8 message->context = &done; //設置spi消息的context 9 if (!bus_locked) 10 mutex_lock(&master->bus_lock_mutex); //上鎖 11 status = spi_async_locked(spi, message); 12 if (!bus_locked) 13 mutex_unlock(&master->bus_lock_mutex); //解鎖 14 if (status == 0) { 15 wait_for_completion(&done); //等待完成 16 status = message->status; //設置spi消息傳輸狀態 17  } 18 message->context = NULL; 19 return status; 20 } 

spi_async_locked 函數

 1 int spi_async_locked(struct spi_device *spi, struct spi_message *message)  
 2 { 3 struct spi_master *master = spi->master; //獲取spi主機 4 int ret; 5 unsigned long flags; 6 spin_lock_irqsave(&master->bus_lock_spinlock, flags); //上自旋鎖 7 ret = __spi_async(spi, message); //調用了spi異步同步方法 8 spin_unlock_irqrestore(&master->bus_lock_spinlock, flags); //解自旋鎖 9 return ret; 10 } 

7.3 __spi_async函數,異步

 1 static int __spi_async(struct spi_device *spi, struct spi_message *message)  
 2 { 3 struct spi_master *master = spi->master; 4 //主機為半雙工或spi設備為3線設備 5 if ((master->flags & SPI_MASTER_HALF_DUPLEX)|| (spi->mode & SPI_3WIRE)) { 6 struct spi_transfer *xfer; 7 unsigned flags = master->flags; 8 list_for_each_entry(xfer, &message->transfers, transfer_list) { //遍歷spi消息的傳輸事務鏈表 9 if (xfer->rx_buf && xfer->tx_buf) //判斷接收或發送緩沖區是否為空 10 return -EINVAL; 11 if ((flags & SPI_MASTER_NO_TX) && xfer->tx_buf) //檢驗無spi數據發送的情況 12 return -EINVAL; 13 if ((flags & SPI_MASTER_NO_RX) && xfer->rx_buf) //檢驗無spi數據接收的情況 14 return -EINVAL; 15  } 16  } 17 message->spi = spi; //設置spi消息所屬的spi設備 18 message->status = -EINPROGRESS; //設置spi消息的傳輸狀態 19 return master->transfer(spi, message); //調用spi主機的transfer方法,收發spi信息給spi設備 20 } 

spi 主機的transfer函數分析見:linux設備驅動 spi詳解5-應用到驅動的完整流程

7.4 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 { 3 static DEFINE_MUTEX(lock); 4 int status; 5 struct spi_message message; 6 struct spi_transfer x[2]; //聲明兩個spi傳輸結構體 7 u8 *local_buf; 8 9 if ((n_tx + n_rx) > SPI_BUFSIZ) //驗證發送和接收的數據總和是否溢出 10 return -EINVAL; 11 spi_message_init(&message); //spi消息初始化(初始化傳輸事務鏈表頭) 12 memset(x, 0, sizeof x); // 13 if (n_tx) { 14 x[0].len = n_tx; 15 spi_message_add_tail(&x[0], &message); //添加spi傳輸到spi消息傳輸鏈表 16  } 17 if (n_rx) { 18 x[1].len = n_rx; 19 spi_message_add_tail(&x[1], &message); //添加spi傳輸到spi消息傳輸鏈表 20  } 21 if (!mutex_trylock(&lock)) { //嘗試上鎖 失敗 22 local_buf = kmalloc(SPI_BUFSIZ, GFP_KERNEL); //則分配local_buf內存 23 if (!local_buf) 24 return -ENOMEM; 25 } else 26 local_buf = buf; //指向默認分配好內存的緩沖區(spi_init中初始化的) 27 memcpy(local_buf, txbuf, n_tx); //發送緩沖區的內容復制到local_buf中 28 x[0].tx_buf = local_buf; //發送的spi傳輸結構體 29 x[1].rx_buf = local_buf + n_tx; //接收的spi傳輸結構體 30 status = spi_sync(spi, &message); //spi同步傳輸--發送數據 31 if (status == 0) 32 memcpy(rxbuf, x[1].rx_buf, n_rx); //接收返回的數據復制到rxbuf中 33 if (x[0].tx_buf == buf) 34 mutex_unlock(&lock); //解鎖 35 else 36 kfree(local_buf); //釋放內存 37 return status; 38 } 

7.5 spi寫8位后讀8位數據 spi_w8r8 

1 static inline ssize_t spi_w8r8(struct spi_device *spi, u8 cmd)  
2 { 3  ssize_t status; 4  u8 result; 5 status = spi_write_then_read(spi, &cmd, 1, &result, 1); //寫8位讀8位 6 return (status < 0) ? status : result; 7 } 

7.6 spi寫8位數據后讀16位數據 spi_w8r16

1 static inline ssize_t spi_w8r16(struct spi_device *spi, u8 cmd)  
2 { 3  ssize_t status; 4  u16 result; 5 status = spi_write_then_read(spi, &cmd, 1, (u8 *) &result, 2); //寫8位讀16位 6 return (status < 0) ? status : result; 7 } 

以上這些API都是內核空間使用的接口,設備驅動程序調用這些API直接操作spi的讀寫操作,來完成任務

8 總結

總結一下,協議驅動發送數據的流程大致是這樣的:

(1)定義一個spi_message結構;

(2)用spi_message_init函數初始化spi_message;

(3)定義一個或數個spi_transfer結構,初始化並為數據准備緩沖區並賦值給spi_transfer相應的字段(tx_buf,rx_buf等);

(4)通過spi_message_init函數把這些spi_transfer掛在spi_message結構下;

(5)如果使用同步方式,調用spi_sync(),如果使用異步方式,調用spi_async();

參考博文:

https://blog.csdn.net/DroidPhone/java/article/details/23932447

http://dainh.blog.chinaunix.net/uid-26765074-id-3510487.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM