linux spi子系统(框架) .

一.spi设备

  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. };  
struct spi_device {
	struct device	dev;	//设备文件
	struct spi_master	*master;	//spi主机
	u32	max_speed_hz;	//最大速率
	u8	chip_select;	//片选
	u8	mode;	//模式
	u8	bits_per_word;	//一个字有多少位
	int	irq;	//中断号
	void	*controller_state;	//控制器状态
	void	*controller_data;	//控制器数据
	char	modalias[SPI_NAME_SIZE];//名字
};


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            //从机拉低电平停止数据传输  
#define	SPI_CPHA		0x01			//时钟相位
#define	SPI_CPOL		0x02			//时钟继续
#define	SPI_MODE_0	(0|0)			//模式0
#define	SPI_MODE_1	(0|SPI_CPHA)	//模式1
#define	SPI_MODE_2	(SPI_CPOL|0)	//模式2
#define	SPI_MODE_3	(SPI_CPOL|SPI_CPHA)	//模式3
#define	SPI_CS_HIGH	0x04			//片选高电平
#define	SPI_LSB_FIRST	0x08			//LSB
#define	SPI_3WIRE		0x10			//3线模式 SI和SO同一根线
#define	SPI_LOOP		0x20			//回送模式
#define	SPI_NO_CS		0x40			//单个设备占用一根SPI总线,所以没片选
#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. {  
  3.     struct spi_device   *proxy;  
  4.     int status;  
  5.       
  6.     proxy = spi_alloc_device(master);   //3.1 spi设备初始化  
  7.     if (!proxy)  
  8.         return NULL;  
  9.     WARN_ON(strlen(chip->modalias) >= sizeof(proxy->modalias));  
  10.     proxy->chip_select = chip->chip_select;   //片选  
  11.     proxy->max_speed_hz = chip->max_speed_hz; //最大速率  
  12.     proxy->mode = chip->mode; //模式   
  13.     proxy->irq = chip->irq;   //中断号  
  14.     strlcpy(proxy->modalias, chip->modalias, sizeof(proxy->modalias));  
  15.     proxy->dev.platform_data = (void *) chip->platform_data;  
  16.     proxy->controller_data = chip->controller_data;  
  17.     proxy->controller_state = NULL;  
  18.     status = spi_add_device(proxy); //3.2 添加spi设备  
  19.     if (status < 0) {  
  20.         spi_dev_put(proxy); //增加spi设备引用计数   
  21.         return NULL;  
  22.     }  
  23.     return proxy;  
  24. }  
  25. EXPORT_SYMBOL_GPL(spi_new_device);  
struct spi_device *spi_new_device(struct spi_master *master,struct spi_board_info *chip)
{
	struct spi_device	*proxy;
	int	status;
	
	proxy = spi_alloc_device(master);	//3.1 spi设备初始化
	if (!proxy)
		return NULL;
	WARN_ON(strlen(chip->modalias) >= sizeof(proxy->modalias));
	proxy->chip_select = chip->chip_select;	//片选
	proxy->max_speed_hz = chip->max_speed_hz;	//最大速率
	proxy->mode = chip->mode;	//模式
	proxy->irq = chip->irq;	//中断号
	strlcpy(proxy->modalias, chip->modalias, sizeof(proxy->modalias));
	proxy->dev.platform_data = (void *) chip->platform_data;
	proxy->controller_data = chip->controller_data;
	proxy->controller_state = NULL;
	status = spi_add_device(proxy);	//3.2 添加spi设备
	if (status < 0) {
		spi_dev_put(proxy);	//增加spi设备引用计数
		return NULL;
	}
	return proxy;
}
EXPORT_SYMBOL_GPL(spi_new_device);

 

3.1.分配spi设备

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

	if (!spi_master_get(master))	//判断spi主机是否存在
		return NULL;
	spi = kzalloc(sizeof *spi, GFP_KERNEL);	//分配内存
	if (!spi) {
		dev_err(dev, "cannot alloc spi_device\n");
		spi_master_put(master);	//增加主机引用计数
		return NULL;
	}
	spi->master = master;	//设置spi主机
	spi->dev.parent = dev;	//spi设备文件的父设备为spi主机设备文件的父设备
	spi->dev.bus = &spi_bus_type;	//总线类型
	spi->dev.release = spidev_release;	//释放方法
	device_initialize(&spi->dev);	//设备初始化
	return spi;
}
EXPORT_SYMBOL_GPL(spi_alloc_device);


3.2 添加spi设备

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

	if (spi->chip_select >= spi->master->num_chipselect) {
		dev_err(dev, "cs%d >= max %d\n",spi->chip_select,spi->master->num_chipselect);
		return -EINVAL;
	}
	dev_set_name(&spi->dev, "%s.%u", dev_name(&spi->master->dev),spi->chip_select);
	mutex_lock(&spi_add_lock);
	d = bus_find_device_by_name(&spi_bus_type, NULL, dev_name(&spi->dev));	//查找总线上的spi设备
	if (d != NULL) {	//判断是否已经在使用了
		dev_err(dev, "chipselect %d already in use\n",spi->chip_select);
		put_device(d);
		status = -EBUSY;
		goto done;
	}
	status = spi_setup(spi);	//调用spi主机 setup方法
	if (status < 0) {
		dev_err(dev, "can't setup %s, status %d\n",dev_name(&spi->dev), status);
		goto done;
	}
	status = device_add(&spi->dev);	//添加设备
	if (status < 0)
		dev_err(dev, "can't add %s, status %d\n",dev_name(&spi->dev), status);
	else
		dev_dbg(dev, "registered child %s\n", dev_name(&spi->dev));
done:
	mutex_unlock(&spi_add_lock);
	return status;
}
EXPORT_SYMBOL_GPL(spi_add_device);


3.2.1 spi setup方法

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

	status = spi->master->setup(spi);	//调用spi主机的setup方法

	dev_dbg(&spi->dev, "setup mode %d, %s%s%s%s""%u bits/w, %u Hz max --> %d\n",
			(int) (spi->mode & (SPI_CPOL | SPI_CPHA)),(spi->mode & SPI_CS_HIGH) ? "cs_high, " : "",
			(spi->mode & SPI_LSB_FIRST) ? "lsb, " : "",(spi->mode & SPI_3WIRE) ? "3wire, " : "",
			(spi->mode & SPI_LOOP) ? "loopback, " : "",spi->bits_per_word, spi->max_speed_hz,status);
	return status;
}
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. };  
struct spi_board_info {
	char	modalias[SPI_NAME_SIZE];	//名字
	const void	*platform_data;	//平台数据
	void	*controller_data;	//控制器数据
	int	irq;			//中断号
	u32	max_speed_hz;	//最大速率
	u16	bus_num;		//spi总线编号
	u16	chip_select;	//片选
	u8	mode;			//模式
};

 

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

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

	bi = kzalloc(n * sizeof(*bi), GFP_KERNEL);	//分配内存
	if (!bi)
		return -ENOMEM;
	for (i = 0; i < n; i++, bi++, info++) {
		struct spi_master *master;
		memcpy(&bi->board_info, info, sizeof(*info));	//设置bi的板级信息
		mutex_lock(&board_lock);
		list_add_tail(&bi->list, &board_list);	//添加bi->list到全局board_list链表
		list_for_each_entry(master, &spi_master_list, list)	//遍历spi主机链表
			spi_match_master_to_boardinfo(master, &bi->board_info);
		mutex_unlock(&board_lock);
	}
	return 0;
}


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

  1. static void spi_match_master_to_boardinfo(struct spi_master *master,struct spi_board_info *bi)  
  2. {  
  3.     struct spi_device *dev;  
  4.     if (master->bus_num != bi->bus_num)  
  5.         return;  
  6.     dev = spi_new_device(master, bi);  
  7.     if (!dev)  
  8.         dev_err(master->dev.parent, "can't create new device for %s\n",bi->modalias);  
  9. }  
static void spi_match_master_to_boardinfo(struct spi_master *master,struct spi_board_info *bi)
{
	struct spi_device *dev;
	if (master->bus_num != bi->bus_num)
		return;
	dev = spi_new_device(master, bi);
	if (!dev)
		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. };  
struct spi_driver {
	const struct spi_device_id *id_table;	//spi设备id表
	int		(*probe)(struct spi_device *spi);	//probe方法(探测到设备)
	int		(*remove)(struct spi_device *spi);	//remove方法(设备移除)
	void	(*shutdown)(struct spi_device *spi);	//shutdown方法(关闭设备)
	int		(*suspend)(struct spi_device *spi, pm_message_t mesg);	//suspend方法(挂起设备)
	int		(*resume)(struct spi_device *spi);	//resume方法(唤醒设备)
	struct device_driver	driver;	//设备驱动文件
};


2.spi设备驱动注册

  1. int spi_register_driver(struct spi_driver *sdrv)  
  2. {  
  3.     sdrv->driver.bus = &spi_bus_type;    //总线类型  
  4.     if (sdrv->probe) //若存在probe方法  
  5.         sdrv->driver.probe = spi_drv_probe;  //设置其设备驱动文件的probe方法为spi_drv_probe  
  6.     if (sdrv->remove)    //若存在remove方法  
  7.         sdrv->driver.remove = spi_drv_remove;    //设置其设备驱动文件的remove方法为spi_drv_remove  
  8.     if (sdrv->shutdown)  若存在shutdown方法  
  9.         sdrv->driver.shutdown = spi_drv_shutdown;    //设置其设备驱动文件的shutdown方法为spi_drv_shutdown  
  10.     return driver_register(&sdrv->driver);   //注册设备驱动  
  11. }  
  12. EXPORT_SYMBOL_GPL(spi_register_driver);  
int spi_register_driver(struct spi_driver *sdrv)
{
	sdrv->driver.bus = &spi_bus_type;	//总线类型
	if (sdrv->probe)	//若存在probe方法
		sdrv->driver.probe = spi_drv_probe;	//设置其设备驱动文件的probe方法为spi_drv_probe
	if (sdrv->remove)	//若存在remove方法
		sdrv->driver.remove = spi_drv_remove;	//设置其设备驱动文件的remove方法为spi_drv_remove
	if (sdrv->shutdown)	若存在shutdown方法
		sdrv->driver.shutdown = spi_drv_shutdown;	//设置其设备驱动文件的shutdown方法为spi_drv_shutdown
	return driver_register(&sdrv->driver);	//注册设备驱动
}
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;  
  10. }  
if (dev->bus->probe) {		//若总线有probe方法(spi子系统的没有)
	ret = dev->bus->probe(dev);	//则调用总线的probe方法
	if (ret)
		goto probe_failed;
} 
else if (drv->probe) {	//若存在设备驱动的probe方法
	ret = drv->probe(dev);		//则调用设备驱动的probe方法
	if (ret)
		goto probe_failed;
}

 

2.1 spi_drv_probe

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


3.spi设备驱动注销

  1. static inline void spi_unregister_driver(struct spi_driver *sdrv)  
  2. {  
  3.     if (sdrv)  
  4.         driver_unregister(&sdrv->driver);    //注销设备驱动  
  5. }  
static inline void spi_unregister_driver(struct spi_driver *sdrv)
{
	if (sdrv)
		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. };  
struct spi_master {
	struct device	dev;	//spi主机设备文件
	struct list_head list;
	s16	bus_num;	//spi总线号
	u16	num_chipselect;	//片选号
	u16	dma_alignment;	//dma算法
	u16	mode_bits;	//模式位
	u16	flags;	//传输类型标志
	spinlock_t	bus_lock_spinlock;	//spi总线自旋锁
	struct mutex	bus_lock_mutex;	//spi总线互斥锁
	bool	bus_lock_flag;	//上锁标志
	int	(*setup)(struct spi_device *spi);	//setup方法
	int	(*transfer)(struct spi_device *spi,struct spi_message *mesg);	//传输方法
	void 	(*cleanup)(struct spi_device *spi);	//cleanup方法
};


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)      //不写  
#define SPI_MASTER_HALF_DUPLEX	BIT(0)	//半双工
#define SPI_MASTER_NO_RX	BIT(1)		//不读
#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. {  
  3.     struct spi_master   *master;  
  4.     if (!dev)  
  5.         return NULL;  
  6.     master = kzalloc(size + sizeof *master, GFP_KERNEL);    //分配内存  
  7.     if (!master)  
  8.         return NULL;  
  9.     device_initialize(&master->dev); //初始化主机设备文件  
  10.     master->dev.class = &spi_master_class;   //指定设备类spi_master_class  
  11.     master->dev.parent = get_device(dev);    //设置spi主机设备的父设备  
  12.     spi_master_set_devdata(master, &master[1]); //设置设备数据  
  13.     return master;  
  14. }  
  15. EXPORT_SYMBOL_GPL(spi_alloc_master);  
struct spi_master *spi_alloc_master(struct device *dev, unsigned size)
{
	struct spi_master	*master;
	if (!dev)
		return NULL;
	master = kzalloc(size + sizeof *master, GFP_KERNEL);	//分配内存
	if (!master)
		return NULL;
	device_initialize(&master->dev);	//初始化主机设备文件
	master->dev.class = &spi_master_class;	//指定设备类spi_master_class
	master->dev.parent = get_device(dev);	//设置spi主机设备的父设备
	spi_master_set_devdata(master, &master[1]);	//设置设备数据
	return master;
}
EXPORT_SYMBOL_GPL(spi_alloc_master);


3.注册spi主机

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

	if (!dev)
		return -ENODEV;
	if (master->num_chipselect == 0)	//判断片选个数
		return -EINVAL;
	if (master->bus_num < 0) {	//验证spi总线编号
		master->bus_num = atomic_dec_return(&dyn_bus_id);
		dynamic = 1;
	}
	spin_lock_init(&master->bus_lock_spinlock);
	mutex_init(&master->bus_lock_mutex);
	
	master->bus_lock_flag = 0;
	dev_set_name(&master->dev, "spi%u", master->bus_num);	//设置spi主机设备名
	status = device_add(&master->dev);	//添加spi主机设备
	if (status < 0)
		goto done;
	dev_dbg(dev, "registered master %s%s\n", dev_name(&master->dev),dynamic ? " (dynamic)" : "");
	mutex_lock(&board_lock);
	list_add_tail(&master->list, &spi_master_list);	//spi主机list链表添加进全局spi_master_list链表
	list_for_each_entry(bi, &board_list, list)		//遍历全局board_list查找bi结构体
		spi_match_master_to_boardinfo(master, &bi->board_info);	//找到匹配的板级spi设备
		
	mutex_unlock(&board_lock);
	status = 0;
	of_register_spi_devices(master);
done:
	return status;
}
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. {  
  3.     struct spi_device *dev;  
  4.     if (master->bus_num != bi->bus_num)   //判断是否所属的spi总线  
  5.         return;  
  6.     dev = spi_new_device(master, bi);   //添加新的spi设备  
  7.     if (!dev)  
  8.         dev_err(master->dev.parent, "can't create new device for %s\n",bi->modalias);  
  9. }  
static void spi_match_master_to_boardinfo(struct spi_master *master,struct spi_board_info *bi)
{
	struct spi_device *dev;
	if (master->bus_num != bi->bus_num)	//判断是否所属的spi总线
		return;
	dev = spi_new_device(master, bi);	//添加新的spi设备
	if (!dev)
		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. {  
  3.     int dummy;  
  4.     mutex_lock(&board_lock);  
  5.     list_del(&master->list); //删除链表  
  6.     mutex_unlock(&board_lock);  
  7.     dummy = device_for_each_child(&master->dev, NULL, __unregister); //调用__unregister函数注销子设备  
  8.     device_unregister(&master->dev); //注销设备  
  9. }  
  10. EXPORT_SYMBOL_GPL(spi_unregister_master);  
void spi_unregister_master(struct spi_master *master)
{
	int dummy;
	mutex_lock(&board_lock);
	list_del(&master->list);	//删除链表
	mutex_unlock(&board_lock);
	dummy = device_for_each_child(&master->dev, NULL, __unregister);	//调用__unregister函数注销子设备
	device_unregister(&master->dev);	//注销设备
}
EXPORT_SYMBOL_GPL(spi_unregister_master);


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

  1. static int __unregister(struct device *dev, void *null)  
  2. {  
  3.     spi_unregister_device(to_spi_device(dev));  
  4.     return 0;  
  5. }  
static int __unregister(struct device *dev, void *null)
{
	spi_unregister_device(to_spi_device(dev));
	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. };  
static struct class spi_master_class = {
	.name	= "spi_master",
	.owner	= THIS_MODULE,
	.dev_release	= spi_master_release,
};


五.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);  
struct bus_type spi_bus_type = {
	.name		= "spi",
	.dev_attrs	= spi_dev_attrs,
	.match		= spi_match_device,	//匹配方法
	.uevent		= spi_uevent,
	.suspend	= spi_suspend,
	.resume		= spi_resume,
};
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. {  
  3.     const struct spi_device *spi = to_spi_device(dev);  
  4.     const struct spi_driver *sdrv = to_spi_driver(drv);  
  5.   
  6.     if (of_driver_match_device(dev, drv))   //设备文件驱动表的匹配  
  7.         return 1;  
  8.     if (sdrv->id_table)  //spi设备驱动存在支持id表  
  9.         return !!spi_match_id(sdrv->id_table, spi);  //spi设备驱动表的匹配  
  10.     return strcmp(spi->modalias, drv->name) == 0; //比较spi设备的名字和spi设备驱动的名字  
  11. }  
static int spi_match_device(struct device *dev, struct device_driver *drv)
{
	const struct spi_device	*spi = to_spi_device(dev);
	const struct spi_driver	*sdrv = to_spi_driver(drv);

	if (of_driver_match_device(dev, drv))	//设备文件驱动表的匹配
		return 1;
	if (sdrv->id_table)	//spi设备驱动存在支持id表
		return !!spi_match_id(sdrv->id_table, spi);	//spi设备驱动表的匹配
	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. {  
  3.     return of_match_device(drv->of_match_table, dev) != NULL;    //调用of_match_device函数  
  4. }  
static inline int of_driver_match_device(const struct device *dev,const struct device_driver *drv)
{
	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. {  
  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);  
const struct of_device_id *of_match_device(const struct of_device_id *matches,const struct device *dev)
{
	if ((!matches) || (!dev->of_node))	//id表和设备节点都不存在
		return NULL;	//则返回
	return of_match_node(matches, dev->of_node);	//调用of_match_node函数
}
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. {  
  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);  
const struct of_device_id *of_match_node(const struct of_device_id *matches,const struct device_node *node)
{
	while (matches->name[0] || matches->type[0] || matches->compatible[0]) {	//名字,类型,兼容方法有一个存在
		int match = 1;
		if (matches->name[0])	//判断名字
			match &= node->name && !strcmp(matches->name, node->name);
		if (matches->type[0])	//判断类型
			match &= node->type && !strcmp(matches->type, node->type);
		if (matches->compatible[0])	//兼容方法
			match &= of_device_is_compatible(node,matches->compatible);
		if (match)	//匹配
			return matches;	//返回匹配的id
		matches++;	//matches指针++,指向下一个id
	}
	return NULL;
}
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. {  
  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. }  
static const struct spi_device_id *spi_match_id(const struct spi_device_id *id,const struct spi_device *sdev)
{
	while (id->name[0]) {	//id表的成员的名字域不为空
		if (!strcmp(sdev->modalias, id->name))	//则判断其名字是否与spi设备的名字一样
			return id;	//一样则返回该id
		id++;	//id表指针++,指向下一个id
	}
	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. };  
struct spi_message {
	struct list_head	transfers;	//spi传输事务链表头
	struct spi_device	*spi;	//所属spi设备
	unsigned	is_dma_mapped:1;
	void	(*complete)(void *context);	
	void	*context;
	unsigned	actual_length;
	int		status;	//传输状态
	struct list_head	queue;
	void	*state;
};


2.初始化spi消息

  1. static inline void spi_message_init(struct spi_message *m)  
  2. {  
  3.     memset(m, 0, sizeof *m);  
  4.     INIT_LIST_HEAD(&m->transfers);   //初始化spi消息的事务链表头  
  5. }  
static inline void spi_message_init(struct spi_message *m)
{
	memset(m, 0, sizeof *m);
	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. {  
  3.     list_add_tail(&t->transfer_list, &m->transfers);  
  4. }  
static inline void spi_message_add_tail(struct spi_transfer *t, struct spi_message *m)
{
	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. };  
struct spi_transfer {
	const void	*tx_buf;	//发送缓冲区指针
	void		*rx_buf;	//接收缓冲区指针
	unsigned	len;	//消息长度
	dma_addr_t	tx_dma;	//DMA发送地址
	dma_addr_t	rx_dma;	//DMA接收地址
	unsigned	cs_change:1;	
	u8		bits_per_word;	//一个字多少位
	u16		delay_usecs;	//毫秒级延时
	u32		speed_hz;	//速率
	struct list_head transfer_list;	//传输链表头
};

 

七.spi子系统的初始化spi_init

  1. static int __init spi_init(void)  
  2. {  
  3.     int status;  
  4.     buf = kmalloc(SPI_BUFSIZ, GFP_KERNEL);  //分配数据收发缓冲区  
  5.     if (!buf) {  
  6.         status = -ENOMEM;  
  7.         goto err0;  
  8.     }  
  9.     status = bus_register(&spi_bus_type);   //注册spi总线  
  10.     if (status < 0)  
  11.         goto err1;    
  12.     status = class_register(&spi_master_class); //注册spi主机类 "/sys/class/spi_master"  
  13.     if (status < 0)  
  14.         goto err2;  
  15.     return 0;  
  16. err2:  
  17.     bus_unregister(&spi_bus_type);  
  18. err1:  
  19.     kfree(buf);  
  20.     buf = NULL;  
  21. err0:  
  22.     return status;  
  23. }  
  24. postcore_initcall(spi_init);    //入口声明 #define postcore_initcall(fn)    __define_initcall("2",fn,2)   
static int __init spi_init(void)
{
	int	status;
	buf = kmalloc(SPI_BUFSIZ, GFP_KERNEL);	//分配数据收发缓冲区
	if (!buf) {
		status = -ENOMEM;
		goto err0;
	}
	status = bus_register(&spi_bus_type);	//注册spi总线
	if (status < 0)
		goto err1;	
	status = class_register(&spi_master_class);	//注册spi主机类 "/sys/class/spi_master"
	if (status < 0)
		goto err2;
	return 0;
err2:
	bus_unregister(&spi_bus_type);
err1:
	kfree(buf);
	buf = NULL;
err0:
	return status;
}
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. {  
  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. }  
static inline int spi_read(struct spi_device *spi, u8 *buf, size_t len)
{
	struct spi_transfer	t = {
			.rx_buf		= buf,
			.len		= len,
		};
	struct spi_message	m;
	spi_message_init(&m);	//spi消息初始化(初始化传输事务链表头)
	spi_message_add_tail(&t, &m);	//添加spi传输到spi消息传输链表
	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. {  
  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. }  
static inline int spi_write(struct spi_device *spi, const u8 *buf, size_t len)
{
	struct spi_transfer	t = {
			.tx_buf		= buf,
			.len		= len,
		};
	struct spi_message	m;
	spi_message_init(&m);	//spi消息初始化(初始化传输事务链表头)
	spi_message_add_tail(&t, &m);	//添加spi传输到spi消息传输链表
	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. {  
  3.     return __spi_sync(spi, message, 0); //调用__spi_sync函数  
  4. }  
  5. EXPORT_SYMBOL_GPL(spi_sync);  
int spi_sync(struct spi_device *spi, struct spi_message *message)
{
	return __spi_sync(spi, message, 0);	//调用__spi_sync函数
}
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. {  
  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. }  
static int __spi_sync(struct spi_device *spi, struct spi_message *message,int bus_locked)
{
	DECLARE_COMPLETION_ONSTACK(done);	//声明一个工作队列
	int status;
	struct spi_master *master = spi->master;	//获取spi主机

	message->complete = spi_complete;	//设置spi消息的complete方法为spi_complete
	message->context = &done;	//设置spi消息的context
	if (!bus_locked)	
		mutex_lock(&master->bus_lock_mutex);	//上锁
	status = spi_async_locked(spi, message);
	if (!bus_locked)
		mutex_unlock(&master->bus_lock_mutex);	//解锁
	if (status == 0) {
		wait_for_completion(&done);	//等待完成
		status = message->status;	//设置spi消息传输状态
	}
	message->context = NULL;
	return status;
}

 

2.1.1.1 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. }  
  11. EXPORT_SYMBOL_GPL(spi_async_locked);  
int spi_async_locked(struct spi_device *spi, struct spi_message *message)
{
	struct spi_master *master = spi->master;	//获取spi主机
	int ret;
	unsigned long flags;
	spin_lock_irqsave(&master->bus_lock_spinlock, flags);	//上自旋锁
	ret = __spi_async(spi, message);	//调用了spi异步同步方法
	spin_unlock_irqrestore(&master->bus_lock_spinlock, flags);	//解自旋锁
	return ret;
}
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. {  
  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. }  
static int __spi_async(struct spi_device *spi, struct spi_message *message)
{
	struct spi_master *master = spi->master;
	//主机为半双工或spi设备为3线设备
	if ((master->flags & SPI_MASTER_HALF_DUPLEX)|| (spi->mode & SPI_3WIRE)) {
		struct spi_transfer *xfer;
		unsigned flags = master->flags;	
		list_for_each_entry(xfer, &message->transfers, transfer_list) {	//遍历spi消息的传输事务链表
			if (xfer->rx_buf && xfer->tx_buf)	//判断接收或发送缓冲区是否为空
				return -EINVAL;
			if ((flags & SPI_MASTER_NO_TX) && xfer->tx_buf)	//检验无spi数据发送的情况
				return -EINVAL;
			if ((flags & SPI_MASTER_NO_RX) && xfer->rx_buf)	//检验无spi数据接收的情况
				return -EINVAL;
		}
	}
	message->spi = spi;	//设置spi消息所属的spi设备
	message->status = -EINPROGRESS;	//设置spi消息的传输状态
	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. {  
  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. }  
  39. EXPORT_SYMBOL_GPL(spi_write_then_read);  
int spi_write_then_read(struct spi_device *spi,const u8 *txbuf, unsigned n_tx,u8 *rxbuf, unsigned n_rx)
{
	static DEFINE_MUTEX(lock);
	int	status;
	struct spi_message	message;
	struct spi_transfer	x[2];	//声明两个spi传输结构体
	u8	*local_buf;
	
	if ((n_tx + n_rx) > SPI_BUFSIZ)	//验证发送和接收的数据总和是否溢出
		return -EINVAL;
	spi_message_init(&message);		//spi消息初始化(初始化传输事务链表头)
	memset(x, 0, sizeof x);	//
	if (n_tx) {
		x[0].len = n_tx;
		spi_message_add_tail(&x[0], &message);	//添加spi传输到spi消息传输链表
	}
	if (n_rx) {
		x[1].len = n_rx;
		spi_message_add_tail(&x[1], &message);	//添加spi传输到spi消息传输链表
	}
	if (!mutex_trylock(&lock)) {	//尝试上锁 失败
		local_buf = kmalloc(SPI_BUFSIZ, GFP_KERNEL);	//则分配local_buf内存
		if (!local_buf)
			return -ENOMEM;
	} else
		local_buf = buf;	//指向默认分配好内存的缓冲区(spi_init中初始化的)
	memcpy(local_buf, txbuf, n_tx);	//发送缓冲区的内容复制到local_buf中
	x[0].tx_buf = local_buf;		//发送的spi传输结构体
	x[1].rx_buf = local_buf + n_tx;	//接收的spi传输结构体
	status = spi_sync(spi, &message);	//spi同步传输--发送数据
	if (status == 0)
		memcpy(rxbuf, x[1].rx_buf, n_rx);	//接收返回的数据复制到rxbuf中
	if (x[0].tx_buf == buf)
		mutex_unlock(&lock);	//解锁
	else
		kfree(local_buf);	//释放内存
	return status;
}
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. {  
  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. }  
static inline ssize_t spi_w8r8(struct spi_device *spi, u8 cmd)
{
	ssize_t status;
	u8	result;
	status = spi_write_then_read(spi, &cmd, 1, &result, 1);	//写8位读8位
	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. {  
  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. }  
static inline ssize_t spi_w8r16(struct spi_device *spi, u8 cmd)
{
	ssize_t	status;
	u16	result;
	status = spi_write_then_read(spi, &cmd, 1, (u8 *) &result, 2);	//写8位读16位
	return (status < 0) ? status : result;
}

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

spi用户空间的接口由spidev.c中的spi驱动提供,见

-->二 spi 子系统(spidev.c)

一.spidev.c文件

看一个设备驱动的方法:

概览下重要的结构体spidev_data及全局变量device_list,bufsiz,SPIDEV_MAJOR...

module_init标识的入口初始化函数spidev_init,(module_exit标识的出口函数)

设备与设备驱动匹配时候调用的probe方法spidev_probe

设备驱动的操作函数集file_operations--->spidev_fops

先看open方法spidev_open

接着看读写方法spidev_read & spidev_write

再接着看ioctl方法-->spidev_ioctl

再看其他剩余的方法

  1. #include <linux/init.h>   
  2. #include <linux/module.h>   
  3. #include <linux/ioctl.h>   
  4. #include <linux/fs.h>   
  5. #include <linux/device.h>  
  6. #include <linux/err.h>   
  7. #include <linux/list.h>   
  8. #include <linux/errno.h>   
  9. #include <linux/mutex.h>   
  10. #include <linux/slab.h>   
  11. #include <linux/spi/spi.h>  
  12. #include <linux/spi/spidev.h>   
  13. #include <asm/uaccess.h>   
  14.   
  15. #define SPIDEV_MAJOR            153 //spidev主设备号  
  16. #define N_SPI_MINORS            32  /* ... up to 256 */  
  17. static DECLARE_BITMAP(minors, N_SPI_MINORS);    //声明次设备位图  
  18. #define SPI_MODE_MASK (SPI_CPHA|SPI_CPOL|SPI_CS_HIGH|SPI_LSB_FIRST|SPI_3WIRE|SPI_LOOP|SPI_NO_CS|SPI_READY)  
  19.   
  20. struct spidev_data {  
  21.     dev_t   devt;               //设备号  
  22.     spinlock_t  spi_lock;       //自旋锁   
  23.     struct spi_device   *spi;   //spi设备结构体  
  24.     struct list_head    device_entry;  
  25.     struct mutex    buf_lock;   //互斥锁  
  26.     unsigned        users;      //使用者计数   
  27.     u8          *buffer;        //缓冲区  
  28. };  
  29.   
  30. static LIST_HEAD(device_list);  //声明spi设备链表  
  31. static DEFINE_MUTEX(device_list_lock);  //定义互斥锁  
  32. static unsigned bufsiz = 4096;  //最大传输缓冲区大小  
  33. module_param(bufsiz, uint, S_IRUGO);  
  34. MODULE_PARM_DESC(bufsiz, "data bytes in biggest supported SPI message");  
  35.   
  36. static void spidev_complete(void *arg)  
  37. {  
  38.     complete(arg);  //调用complete   
  39. }  
  40.   
  41. static ssize_t spidev_sync(struct spidev_data *spidev, struct spi_message *message)  
  42. {  
  43.     DECLARE_COMPLETION_ONSTACK(done);  
  44.     int status;  
  45.   
  46.     message->complete = spidev_complete; //设置spi消息的complete方法 回调函数  
  47.     message->context = &done;  
  48.   
  49.     spin_lock_irq(&spidev->spi_lock);  
  50.     if (spidev->spi == NULL) //判断是否有指定对应的spi设备  
  51.         status = -ESHUTDOWN;  
  52.     else  
  53.         status = spi_async(spidev->spi, message);    //spi异步同步  
  54.     spin_unlock_irq(&spidev->spi_lock);  
  55.   
  56.     if (status == 0) {  
  57.         wait_for_completion(&done); //等待传输完成  
  58.         status = message->status;    //获取spi消息传输事务状态  
  59.         if (status == 0)  
  60.             status = message->actual_length; //status等于传输的实际长度  
  61.     }  
  62.     return status;  //返回实际传输长度  
  63. }  
  64.   
  65. static inline ssize_t spidev_sync_write(struct spidev_data *spidev, size_t len)  
  66. {  
  67.     struct spi_transfer t = {  
  68.             .tx_buf     = spidev->buffer,    //发送缓冲区  
  69.             .len        = len,  //发送数据长度  
  70.         };  
  71.     struct spi_message  m;  
  72.   
  73.     spi_message_init(&m);   //初始化spi消息(初始化spi传递事务队列)  
  74.     spi_message_add_tail(&t, &m);   //添加spr传递到该队列  
  75.     return spidev_sync(spidev, &m); //同步读写  
  76. }  
  77.   
  78. static inline ssize_t spidev_sync_read(struct spidev_data *spidev, size_t len)  
  79. {  
  80.     struct spi_transfer t = {  
  81.             .rx_buf     = spidev->buffer,    //接收缓冲区  
  82.             .len        = len,  //接收数据长度   
  83.         };  
  84.     struct spi_message  m;  
  85.   
  86.     spi_message_init(&m);   //初始化spi消息(初始化spi传递事务队列)  
  87.     spi_message_add_tail(&t, &m);   //添加spr传递到该队列  
  88.     return spidev_sync(spidev, &m); //同步读写  
  89. }  
  90.   
  91. static ssize_t spidev_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)  
  92. {  
  93.     struct spidev_data  *spidev;  
  94.     ssize_t status = 0;  
  95.   
  96.     if (count > bufsiz)  //传输数据大于缓冲区容量  
  97.         return -EMSGSIZE;  
  98.     spidev = filp->private_data; //从文件私有数据指针获取spidev_data  
  99.     mutex_lock(&spidev->buf_lock);   //上互斥锁  
  100.     status = spidev_sync_read(spidev, count);   //同步读,返回传输数据长度  
  101.     if (status > 0) {  
  102.         unsigned long   missing;    //丢失的数据个数  
  103.         missing = copy_to_user(buf, spidev->buffer, status); //内核空间复制到用户空间  
  104.         if (missing == status)      //丢失的数据个数等于要传输的数据个数  
  105.             status = -EFAULT;  
  106.         else  
  107.             status = status - missing;  //传输成功的数据个数  
  108.     }  
  109.     mutex_unlock(&spidev->buf_lock);//解互斥锁  
  110.     return status;  //返回读取成功的数据个数  
  111. }  
  112.   
  113. static ssize_t spidev_write(struct file *filp, const char __user *buf,size_t count, loff_t *f_pos)  
  114. {  
  115.     struct spidev_data  *spidev;  
  116.     ssize_t         status = 0;  
  117.     unsigned long       missing;  
  118.   
  119.     if (count > bufsiz)  //传输数据大于缓冲区容量  
  120.         return -EMSGSIZE;  
  121.     spidev = filp->private_data; //从文件私有数据指针获取spidev_data  
  122.     mutex_lock(&spidev->buf_lock);   //上互斥锁  
  123.     missing = copy_from_user(spidev->buffer, buf, count);    //用户空间复制到内核空间  
  124.     if (missing == 0) { //传输失败个数为0  
  125.         status = spidev_sync_write(spidev, count);  //同步写,返回传输数据长度  
  126.     }   
  127.     else  
  128.         status = -EFAULT;  
  129.     mutex_unlock(&spidev->buf_lock);//解互斥锁  
  130.     return status;  //返回写数据的实际个数  
  131. }  
  132.   
  133. static int spidev_message(struct spidev_data *spidev,struct spi_ioc_transfer *u_xfers, unsigned n_xfers)  
  134. {  
  135.     struct spi_message  msg;  
  136.     struct spi_transfer *k_xfers;  
  137.     struct spi_transfer *k_tmp;  
  138.     struct spi_ioc_transfer *u_tmp;  
  139.     unsigned    n, total;  
  140.     u8  *buf;  
  141.     int status = -EFAULT;  
  142.   
  143.     spi_message_init(&msg); //初始化spi消息(初始化spi传递事务队列)  
  144.     k_xfers = kcalloc(n_xfers, sizeof(*k_tmp), GFP_KERNEL); //分配spi传输指针内存  
  145.     if (k_xfers == NULL)  
  146.         return -ENOMEM;  
  147.     buf = spidev->buffer;    //获取spidev_data的缓冲区  
  148.     total = 0;  
  149.     //n=xfers为spi_ioc_transfer个数,u_tmp = u_xfers为要处理的spi_ioc_transfer指针  
  150.     for (n = n_xfers, k_tmp = k_xfers, u_tmp = u_xfers;n;n--, k_tmp++, u_tmp++) {  
  151.         k_tmp->len = u_tmp->len;  //设置传输信息的长度  
  152.         total += k_tmp->len; //累加传输信息的总长度   
  153.         if (total > bufsiz) {    //信息量超过bufsiz缓冲区最大容量  
  154.             status = -EMSGSIZE;  
  155.             goto done;  
  156.         }  
  157.         if (u_tmp->rx_buf) { //接收缓冲区指针不为空  
  158.             k_tmp->rx_buf = buf; //缓冲区指向buf  
  159.             if (!access_ok(VERIFY_WRITE, (u8 __user *)(uintptr_t) u_tmp->rx_buf,u_tmp->len))  
  160.                 goto done;  
  161.         }  
  162.         if (u_tmp->tx_buf) { //发送缓冲区指针不为空  
  163.             k_tmp->tx_buf = buf; //缓冲区指针指向buf  
  164.             if (copy_from_user(buf, (const u8 __user *)(uintptr_t) u_tmp->tx_buf,u_tmp->len)) //用户空间复制数据到buf  
  165.                 goto done;  
  166.         }  
  167.         buf += k_tmp->len;   //缓冲区指针移动一个传输信息的长度  
  168.         k_tmp->cs_change = !!u_tmp->cs_change;    //设置cs_change  
  169.         k_tmp->bits_per_word = u_tmp->bits_per_word;  //设置bits_per_word 一个字多少位  
  170.         k_tmp->delay_usecs = u_tmp->delay_usecs;  //设置delay_usecs 毫秒级延时  
  171.         k_tmp->speed_hz = u_tmp->speed_hz;    //设置speed_hz 速率  
  172. #ifdef VERBOSE   
  173.         dev_dbg(&spidev->spi->dev,"  xfer len %zd %s%s%s%dbits %u usec %uHz\n",  
  174.             u_tmp->len,u_tmp->rx_buf ? "rx " : "",u_tmp->tx_buf ? "tx " : "",u_tmp->cs_change ? "cs " : "",  
  175.             u_tmp->bits_per_word ? : spidev->spi->bits_per_word,u_tmp->delay_usecs,u_tmp->speed_hz ? : spidev->spi->max_speed_hz);  
  176. #endif   
  177.         spi_message_add_tail(k_tmp, &msg);  //添加spr传递到该队列  
  178.     }  
  179.     //for循环的作用是将spi_ioc_transfer批量转换为spi传递结构体spi_transfer,然后添加进spi传递事务队列  
  180.     status = spidev_sync(spidev, &msg);     //同步读写  
  181.     if (status < 0)  
  182.         goto done;  
  183.     buf = spidev->buffer;    //获取spidev_data缓冲区指针  
  184.     for (n = n_xfers, u_tmp = u_xfers; n; n--, u_tmp++) {   //批量从内核空间复制spi_ioc_transfer到用户空间  
  185.         if (u_tmp->rx_buf) { //判断是否存在接收缓冲区  
  186.             if (__copy_to_user((u8 __user *)(uintptr_t) u_tmp->rx_buf, buf,u_tmp->len)) {  
  187.                 status = -EFAULT;  
  188.                 goto done;  
  189.             }  
  190.         }  
  191.         buf += u_tmp->len;   //buf指针位置调整指向下一个spi_ioc_transfer  
  192.     }  
  193.     status = total; //status等于实际传输的数据长度  
  194. done:  
  195.     kfree(k_xfers); //释放k_xfers  
  196.     return status;  //返回实际传输的数据长度  
  197. }  
  198.   
  199. static long spidev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)  
  200. {  
  201.     int err = 0;  
  202.     int retval = 0;  
  203.     struct spidev_data  *spidev;  
  204.     struct spi_device   *spi;  
  205.     u32 tmp;  
  206.     unsigned    n_ioc;  
  207.     struct spi_ioc_transfer *ioc;  
  208.   
  209.     if (_IOC_TYPE(cmd) != SPI_IOC_MAGIC)    //判断控制命令的类型  
  210.         return -ENOTTY;  
  211.     if (_IOC_DIR(cmd) & _IOC_READ)  //判断控制命令的方向是否为读read  
  212.         err = !access_ok(VERIFY_WRITE,(void __user *)arg, _IOC_SIZE(cmd));  //判断传输数据大小  
  213.     if (err == 0 && _IOC_DIR(cmd) & _IOC_WRITE) //判断控制命令的方向是否为写write  
  214.         err = !access_ok(VERIFY_READ,(void __user *)arg, _IOC_SIZE(cmd));   //判断传输数据大小  
  215.     if (err)  
  216.         return -EFAULT;  
  217.   
  218.     spidev = filp->private_data; //从文件私有数据中获取spidev_data  
  219.     spin_lock_irq(&spidev->spi_lock);    //上自旋锁  
  220.     spi = spi_dev_get(spidev->spi);          //获取spi设备  
  221.     spin_unlock_irq(&spidev->spi_lock);  //解自旋锁  
  222.     if (spi == NULL)    //获取spi设备失败  
  223.         return -ESHUTDOWN;  //则返回错误  
  224.     mutex_lock(&spidev->buf_lock);   //上互斥锁  
  225.       
  226.     switch (cmd) {  
  227.     case SPI_IOC_RD_MODE:   //设置spi读模式  
  228.         retval = __put_user(spi->mode & SPI_MODE_MASK,(__u8 __user *)arg);  
  229.         break;  
  230.     case SPI_IOC_RD_LSB_FIRST:  //设置spi读最低有效位  
  231.         retval = __put_user((spi->mode & SPI_LSB_FIRST) ?  1 : 0,(__u8 __user *)arg);  
  232.         break;  
  233.     case SPI_IOC_RD_BITS_PER_WORD:  //设置spi读每个字含多个个位  
  234.         retval = __put_user(spi->bits_per_word, (__u8 __user *)arg);  
  235.         break;  
  236.     case SPI_IOC_RD_MAX_SPEED_HZ:   //设置spi读最大速率  
  237.         retval = __put_user(spi->max_speed_hz, (__u32 __user *)arg);  
  238.         break;  
  239.     case SPI_IOC_WR_MODE:   //设置spi写模式  
  240.         retval = __get_user(tmp, (u8 __user *)arg);  
  241.         if (retval == 0) {  
  242.             u8  save = spi->mode;    //获取spi设备模式  
  243.   
  244.             if (tmp & ~SPI_MODE_MASK) {  
  245.                 retval = -EINVAL;  
  246.                 break;  
  247.             }  
  248.   
  249.             tmp |= spi->mode & ~SPI_MODE_MASK;  
  250.             spi->mode = (u8)tmp;  
  251.             retval = spi_setup(spi);    //配置spi设备  
  252.             if (retval < 0)  
  253.                 spi->mode = save;  
  254.             else  
  255.                 dev_dbg(&spi->dev, "spi mode %02x\n", tmp);  
  256.         }  
  257.         break;  
  258.     case SPI_IOC_WR_LSB_FIRST:  //设置spi写最低有效位  
  259.         retval = __get_user(tmp, (__u8 __user *)arg);  
  260.         if (retval == 0) {  
  261.             u8  save = spi->mode;    //获取spi设备模式  
  262.   
  263.             if (tmp)  
  264.                 spi->mode |= SPI_LSB_FIRST;  
  265.             else  
  266.                 spi->mode &= ~SPI_LSB_FIRST;  
  267.             retval = spi_setup(spi);    //配置spi设备  
  268.             if (retval < 0)  
  269.                 spi->mode = save;  
  270.             else  
  271.                 dev_dbg(&spi->dev, "%csb first\n",tmp ? 'l' : 'm');  
  272.         }  
  273.         break;  
  274.     case SPI_IOC_WR_BITS_PER_WORD:  //设置spi写每个字含多个个位  
  275.         retval = __get_user(tmp, (__u8 __user *)arg);   //用户空间获取数据  
  276.         if (retval == 0) {  
  277.             u8  save = spi->bits_per_word;   //获取spi设备 每个字含多少位  
  278.   
  279.             spi->bits_per_word = tmp;    //更新新的spi设备 每个字含多少位  
  280.             retval = spi_setup(spi);    //配置spi设备  
  281.             if (retval < 0)  //配置失败  
  282.                 spi->bits_per_word = save;   //还原spi设备 每个字含多少位  
  283.             else  
  284.                 dev_dbg(&spi->dev, "%d bits per word\n", tmp);  
  285.         }  
  286.         break;  
  287.     case SPI_IOC_WR_MAX_SPEED_HZ:       //设置spi写最大速率  
  288.         retval = __get_user(tmp, (__u32 __user *)arg);  //用户空间获取数据  
  289.         if (retval == 0) {  
  290.             u32 save = spi->max_speed_hz;    //获取spi设备最大速率  
  291.   
  292.             spi->max_speed_hz = tmp; //更新新的spi设备最大速率  
  293.             retval = spi_setup(spi);    //配置spi设备  
  294.             if (retval < 0)  //配置失败  
  295.                 spi->max_speed_hz = save;    //还原spi设备最大速率  
  296.             else  
  297.                 dev_dbg(&spi->dev, "%d Hz (max)\n", tmp);  
  298.         }  
  299.         break;  
  300.   
  301.     default:  
  302.         //命令必须为写方向的命令,且传输数据必须是SPI_IOC_MESSAGE()修饰的命令  
  303.         if (_IOC_NR(cmd) != _IOC_NR(SPI_IOC_MESSAGE(0))|| _IOC_DIR(cmd) != _IOC_WRITE) {  
  304.             retval = -ENOTTY;  
  305.             break;  
  306.         }  
  307.   
  308.         tmp = _IOC_SIZE(cmd);   //计算传输数据大小  
  309.         if ((tmp % sizeof(struct spi_ioc_transfer)) != 0) { //判断是否为spi_ioc_transfer对齐  
  310.             retval = -EINVAL;  
  311.             break;  
  312.         }  
  313.         n_ioc = tmp / sizeof(struct spi_ioc_transfer);  //计算出spi_ioc_transfer数据的个数  
  314.         if (n_ioc == 0)  
  315.             break;  
  316.   
  317.         ioc = kmalloc(tmp, GFP_KERNEL); //分配spi_ioc_transfer指针ioc内存  
  318.         if (!ioc) {  
  319.             retval = -ENOMEM;  
  320.             break;  
  321.         }  
  322.         if (__copy_from_user(ioc, (void __user *)arg, tmp)) {   //从用户空间复制到内核空间  
  323.             kfree(ioc); //复制失败则释放ioc内存  
  324.             retval = -EFAULT;  
  325.             break;  
  326.         }  
  327.   
  328.         retval = spidev_message(spidev, ioc, n_ioc);    //spidev消息处理  
  329.         kfree(ioc); //释放ioc内存   
  330.         break;  
  331.     }  
  332.   
  333.     mutex_unlock(&spidev->buf_lock); //解互斥锁  
  334.     spi_dev_put(spi);   //增加spi设备的引用计数   
  335.     return retval;  
  336. }  
  337.   
  338. static int spidev_open(struct inode *inode, struct file *filp)  
  339. {  
  340.     struct spidev_data  *spidev;  
  341.     int status = -ENXIO;  
  342.   
  343.     mutex_lock(&device_list_lock);  //上互斥锁  
  344.     list_for_each_entry(spidev, &device_list, device_entry) {   //遍历device_list  
  345.         if (spidev->devt == inode->i_rdev) {  //判断设备号找到对应的设备  
  346.             status = 0; //设置状态为0   
  347.             break;  
  348.         }  
  349.     }  
  350.     if (status == 0) {  //找得到对应的设备  
  351.         if (!spidev->buffer) {   //spidev_data缓冲区为空  
  352.             spidev->buffer = kmalloc(bufsiz, GFP_KERNEL);    //则分配内存  
  353.             if (!spidev->buffer) {   //还空  
  354.                 dev_dbg(&spidev->spi->dev, "open/ENOMEM\n");  //调试了  
  355.                 status = -ENOMEM;  
  356.             }  
  357.         }  
  358.         if (status == 0) {  //找得到对应的设备  
  359.             spidev->users++; //spidev_data使用者计数++  
  360.             filp->private_data = spidev; //spidev_data放在文件的私有数据里  
  361.             nonseekable_open(inode, filp);  //设置文件的打开模式(文件读写指针不会跟随读写操作移动)  
  362.         }  
  363.     }   
  364.     else  
  365.         pr_debug("spidev: nothing for minor %d\n", iminor(inode));  
  366.     mutex_unlock(&device_list_lock);    //接互斥锁  
  367.     return status;  
  368. }  
  369.   
  370. static int spidev_release(struct inode *inode, struct file *filp)  
  371. {  
  372.     struct spidev_data  *spidev;  
  373.     int status = 0;  
  374.   
  375.     mutex_lock(&device_list_lock);  
  376.     spidev = filp->private_data; //获取spidev_data  
  377.     filp->private_data = NULL;       //清除文件的私有数据指针  
  378.     spidev->users--;             //使用者个数--  
  379.     if (!spidev->users) {    //如果使用者个数为0  
  380.         int     dofree;  
  381.         kfree(spidev->buffer);   //释放spidev_data的缓冲区内存  
  382.         spidev->buffer = NULL;   //清除spidev_data缓冲区指针  
  383.         spin_lock_irq(&spidev->spi_lock);    //上自旋锁  
  384.         dofree = (spidev->spi == NULL);  //判断spi设备是否与spidev_data解绑了  
  385.         spin_unlock_irq(&spidev->spi_lock);  //解自旋锁  
  386.         if (dofree)         //没有捆绑的spi设备  
  387.             kfree(spidev);  //则是否spidev_data内存  
  388.     }  
  389.     mutex_unlock(&device_list_lock);  
  390.     return status;  
  391. }  
  392.   
  393. static const struct file_operations spidev_fops = {     //文件操作函数集  
  394.     .owner =    THIS_MODULE,  
  395.     .write =    spidev_write,       //写write  
  396.     .read =     spidev_read,        //读read  
  397.     .unlocked_ioctl = spidev_ioctl, //控制ioctl  
  398.     .open =     spidev_open,        //打开open  
  399.     .release =  spidev_release,     //释放release  
  400.     .llseek =   no_llseek,          //文件指针移动 no_llseek表示没有移动  
  401. };  
  402.   
  403. static struct class *spidev_class;  
  404.   
  405. static int __devinit spidev_probe(struct spi_device *spi)  
  406. {  
  407.     struct spidev_data  *spidev;  
  408.     int status;  
  409.     unsigned long   minor;  
  410.   
  411.     spidev = kzalloc(sizeof(*spidev), GFP_KERNEL);  //分配spidev_data内存  
  412.     if (!spidev)  
  413.         return -ENOMEM;  
  414.     spidev->spi = spi;   //设置spidev_data->spi(spi设备)  
  415.     spin_lock_init(&spidev->spi_lock);  
  416.     mutex_init(&spidev->buf_lock);  
  417.     INIT_LIST_HEAD(&spidev->device_entry);   //初始化spidev_data入口链表  
  418.     mutex_lock(&device_list_lock);  
  419.     minor = find_first_zero_bit(minors, N_SPI_MINORS);  //查找次设备位图分配次设备号  
  420.     if (minor < N_SPI_MINORS) {  
  421.         struct device *dev;  
  422.         spidev->devt = MKDEV(SPIDEV_MAJOR, minor);   //计算出设备号  
  423.         //创建设备/dev/spidev%d.%d(spidev总线号.片选号)  
  424.         dev = device_create(spidev_class, &spi->dev, spidev->devt,spidev, "spidev%d.%d",spi->master->bus_num, spi->chip_select);  
  425.         status = IS_ERR(dev) ? PTR_ERR(dev) : 0;  
  426.     }   
  427.     else {  
  428.         dev_dbg(&spi->dev, "no minor number available!\n");  
  429.         status = -ENODEV;  
  430.     }  
  431.     if (status == 0) {  //分配设备号成功  
  432.         set_bit(minor, minors); //更新次设备位图   
  433.         list_add(&spidev->device_entry, &device_list);   //添加进设备链表  
  434.     }  
  435.     mutex_unlock(&device_list_lock);  
  436.   
  437.     if (status == 0)  
  438.         spi_set_drvdata(spi, spidev);   //spi->dev->p->driver_data=spidev   
  439.     else  
  440.         kfree(spidev);  
  441.   
  442.     return status;  
  443. }  
  444.   
  445. static int __devexit spidev_remove(struct spi_device *spi)  
  446. {  
  447.     struct spidev_data  *spidev = spi_get_drvdata(spi);     //根据spi设备获取spidev_data  
  448.     spin_lock_irq(&spidev->spi_lock);            //上自旋锁  
  449.     spidev->spi = NULL;                              //清空spidev_data->spi指针  
  450.     spi_set_drvdata(spi, NULL);                     //spi->dev->p->driver_data=NULL  
  451.     spin_unlock_irq(&spidev->spi_lock);          //解自旋锁  
  452.     mutex_lock(&device_list_lock);              //上互斥锁  
  453.     list_del(&spidev->device_entry);             //删除spidev_data入口链表  
  454.     device_destroy(spidev_class, spidev->devt);      //销毁/dev/spidev%d.%d  
  455.     clear_bit(MINOR(spidev->devt), minors);          //清除次设备位图对应位  
  456.     if (spidev->users == 0)                          //使用者个数为0  
  457.         kfree(spidev);                              //释放spidev_data内存  
  458.     mutex_unlock(&device_list_lock);            //解互斥锁  
  459.     return 0;  
  460. }  
  461.   
  462. static struct spi_driver spidev_spi_driver = {  //spi设备驱动  
  463.     .driver = {  
  464.         .name =     "spidev",  
  465.         .owner =    THIS_MODULE,  
  466.     },  
  467.     .probe =    spidev_probe,   //spidev的probe方法(当注册了modalias域为"spidev"的spi设备或板级设备,则会调用probe方法)  
  468.     .remove =   __devexit_p(spidev_remove), //spidev的remove方法  
  469. };  
  470.   
  471. static int __init spidev_init(void)     //spidev接口初始化  
  472. {  
  473.     int status;  
  474.     BUILD_BUG_ON(N_SPI_MINORS > 256);  
  475.     //注册字符设备,主设备号SPIDEV_MAJOR=153,捆绑的设备操作函数集为spidev_fops  
  476.     status = register_chrdev(SPIDEV_MAJOR, "spi", &spidev_fops);  
  477.     if (status < 0)  
  478.         return status;  
  479.     spidev_class = class_create(THIS_MODULE, "spidev"); //创建设备类spidev_class  
  480.     if (IS_ERR(spidev_class)) {  
  481.         unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);  
  482.         return PTR_ERR(spidev_class);  
  483.     }  
  484.     status = spi_register_driver(&spidev_spi_driver);   //注册spi设备驱动spidev_spi_driver  
  485.     if (status < 0) {  
  486.         class_destroy(spidev_class);  
  487.         unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);  
  488.     }  
  489.     return status;  
  490. }  
  491. module_init(spidev_init);   //声明初始化入口  
  492.   
  493. static void __exit spidev_exit(void)            //spidev接口销毁  
  494. {  
  495.     spi_unregister_driver(&spidev_spi_driver);  //注销spi设备驱动spidev_spi_driver  
  496.     class_destroy(spidev_class);                //注销设备类spidev_class  
  497.     unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name); //注销字符设备  
  498. }  
  499. module_exit(spidev_exit);   //声明初始化出口  
  500.   
  501. MODULE_AUTHOR("Andrea Paterniani, <a.paterniani@swapp-eng.it>");  
  502. MODULE_DESCRIPTION("User mode SPI device interface");  
  503. MODULE_LICENSE("GPL");  
  504. MODULE_ALIAS("spi:spidev");  
#include <linux/init.h>
#include <linux/module.h>
#include <linux/ioctl.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/list.h>
#include <linux/errno.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/spi/spi.h>
#include <linux/spi/spidev.h>
#include <asm/uaccess.h>

#define SPIDEV_MAJOR			153	//spidev主设备号
#define N_SPI_MINORS			32	/* ... up to 256 */
static DECLARE_BITMAP(minors, N_SPI_MINORS);	//声明次设备位图
#define SPI_MODE_MASK (SPI_CPHA|SPI_CPOL|SPI_CS_HIGH|SPI_LSB_FIRST|SPI_3WIRE|SPI_LOOP|SPI_NO_CS|SPI_READY)

struct spidev_data {
	dev_t	devt;				//设备号
	spinlock_t	spi_lock;		//自旋锁
	struct spi_device	*spi;	//spi设备结构体
	struct list_head	device_entry;
	struct mutex	buf_lock;	//互斥锁
	unsigned		users;		//使用者计数
	u8			*buffer;		//缓冲区
};

static LIST_HEAD(device_list);	//声明spi设备链表
static DEFINE_MUTEX(device_list_lock);	//定义互斥锁
static unsigned bufsiz = 4096;	//最大传输缓冲区大小
module_param(bufsiz, uint, S_IRUGO);
MODULE_PARM_DESC(bufsiz, "data bytes in biggest supported SPI message");

static void spidev_complete(void *arg)
{
	complete(arg);	//调用complete
}

static ssize_t spidev_sync(struct spidev_data *spidev, struct spi_message *message)
{
	DECLARE_COMPLETION_ONSTACK(done);
	int status;

	message->complete = spidev_complete;	//设置spi消息的complete方法 回调函数
	message->context = &done;

	spin_lock_irq(&spidev->spi_lock);
	if (spidev->spi == NULL)	//判断是否有指定对应的spi设备
		status = -ESHUTDOWN;
	else
		status = spi_async(spidev->spi, message);	//spi异步同步
	spin_unlock_irq(&spidev->spi_lock);

	if (status == 0) {
		wait_for_completion(&done);	//等待传输完成
		status = message->status;	//获取spi消息传输事务状态
		if (status == 0)
			status = message->actual_length;	//status等于传输的实际长度
	}
	return status;	//返回实际传输长度
}

static inline ssize_t spidev_sync_write(struct spidev_data *spidev, size_t len)
{
	struct spi_transfer	t = {
			.tx_buf		= spidev->buffer,	//发送缓冲区
			.len		= len,	//发送数据长度
		};
	struct spi_message	m;

	spi_message_init(&m);	//初始化spi消息(初始化spi传递事务队列)
	spi_message_add_tail(&t, &m);	//添加spr传递到该队列
	return spidev_sync(spidev, &m);	//同步读写
}

static inline ssize_t spidev_sync_read(struct spidev_data *spidev, size_t len)
{
	struct spi_transfer	t = {
			.rx_buf		= spidev->buffer,	//接收缓冲区
			.len		= len,	//接收数据长度
		};
	struct spi_message	m;

	spi_message_init(&m);	//初始化spi消息(初始化spi传递事务队列)
	spi_message_add_tail(&t, &m);	//添加spr传递到该队列
	return spidev_sync(spidev, &m);	//同步读写
}

static ssize_t spidev_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
	struct spidev_data	*spidev;
	ssize_t	status = 0;

	if (count > bufsiz)	//传输数据大于缓冲区容量
		return -EMSGSIZE;
	spidev = filp->private_data;	//从文件私有数据指针获取spidev_data
	mutex_lock(&spidev->buf_lock);	//上互斥锁
	status = spidev_sync_read(spidev, count);	//同步读,返回传输数据长度
	if (status > 0) {
		unsigned long	missing;	//丢失的数据个数
		missing = copy_to_user(buf, spidev->buffer, status);	//内核空间复制到用户空间
		if (missing == status)		//丢失的数据个数等于要传输的数据个数
			status = -EFAULT;
		else
			status = status - missing;	//传输成功的数据个数
	}
	mutex_unlock(&spidev->buf_lock);//解互斥锁
	return status;	//返回读取成功的数据个数
}

static ssize_t spidev_write(struct file *filp, const char __user *buf,size_t count, loff_t *f_pos)
{
	struct spidev_data	*spidev;
	ssize_t			status = 0;
	unsigned long		missing;

	if (count > bufsiz)	//传输数据大于缓冲区容量
		return -EMSGSIZE;
	spidev = filp->private_data;	//从文件私有数据指针获取spidev_data
	mutex_lock(&spidev->buf_lock);	//上互斥锁
	missing = copy_from_user(spidev->buffer, buf, count);	//用户空间复制到内核空间
	if (missing == 0) {	//传输失败个数为0
		status = spidev_sync_write(spidev, count);	//同步写,返回传输数据长度
	} 
	else
		status = -EFAULT;
	mutex_unlock(&spidev->buf_lock);//解互斥锁
	return status;	//返回写数据的实际个数
}

static int spidev_message(struct spidev_data *spidev,struct spi_ioc_transfer *u_xfers, unsigned n_xfers)
{
	struct spi_message	msg;
	struct spi_transfer	*k_xfers;
	struct spi_transfer	*k_tmp;
	struct spi_ioc_transfer *u_tmp;
	unsigned	n, total;
	u8	*buf;
	int	status = -EFAULT;

	spi_message_init(&msg);	//初始化spi消息(初始化spi传递事务队列)
	k_xfers = kcalloc(n_xfers, sizeof(*k_tmp), GFP_KERNEL);	//分配spi传输指针内存
	if (k_xfers == NULL)
		return -ENOMEM;
	buf = spidev->buffer;	//获取spidev_data的缓冲区
	total = 0;
	//n=xfers为spi_ioc_transfer个数,u_tmp = u_xfers为要处理的spi_ioc_transfer指针
	for (n = n_xfers, k_tmp = k_xfers, u_tmp = u_xfers;n;n--, k_tmp++, u_tmp++) {
		k_tmp->len = u_tmp->len;	//设置传输信息的长度
		total += k_tmp->len;	//累加传输信息的总长度
		if (total > bufsiz) {	//信息量超过bufsiz缓冲区最大容量
			status = -EMSGSIZE;
			goto done;
		}
		if (u_tmp->rx_buf) {	//接收缓冲区指针不为空
			k_tmp->rx_buf = buf;	//缓冲区指向buf
			if (!access_ok(VERIFY_WRITE, (u8 __user *)(uintptr_t) u_tmp->rx_buf,u_tmp->len))
				goto done;
		}
		if (u_tmp->tx_buf) {	//发送缓冲区指针不为空
			k_tmp->tx_buf = buf;	//缓冲区指针指向buf
			if (copy_from_user(buf, (const u8 __user *)(uintptr_t) u_tmp->tx_buf,u_tmp->len))	//用户空间复制数据到buf
				goto done;
		}
		buf += k_tmp->len;	//缓冲区指针移动一个传输信息的长度
		k_tmp->cs_change = !!u_tmp->cs_change;	//设置cs_change
		k_tmp->bits_per_word = u_tmp->bits_per_word;	//设置bits_per_word 一个字多少位
		k_tmp->delay_usecs = u_tmp->delay_usecs;	//设置delay_usecs 毫秒级延时
		k_tmp->speed_hz = u_tmp->speed_hz;	//设置speed_hz 速率
#ifdef VERBOSE
		dev_dbg(&spidev->spi->dev,"  xfer len %zd %s%s%s%dbits %u usec %uHz\n",
			u_tmp->len,u_tmp->rx_buf ? "rx " : "",u_tmp->tx_buf ? "tx " : "",u_tmp->cs_change ? "cs " : "",
			u_tmp->bits_per_word ? : spidev->spi->bits_per_word,u_tmp->delay_usecs,u_tmp->speed_hz ? : spidev->spi->max_speed_hz);
#endif
		spi_message_add_tail(k_tmp, &msg);	//添加spr传递到该队列
	}
	//for循环的作用是将spi_ioc_transfer批量转换为spi传递结构体spi_transfer,然后添加进spi传递事务队列
	status = spidev_sync(spidev, &msg);		//同步读写
	if (status < 0)
		goto done;
	buf = spidev->buffer;	//获取spidev_data缓冲区指针
	for (n = n_xfers, u_tmp = u_xfers; n; n--, u_tmp++) {	//批量从内核空间复制spi_ioc_transfer到用户空间
		if (u_tmp->rx_buf) {	//判断是否存在接收缓冲区
			if (__copy_to_user((u8 __user *)(uintptr_t) u_tmp->rx_buf, buf,u_tmp->len)) {
				status = -EFAULT;
				goto done;
			}
		}
		buf += u_tmp->len;	//buf指针位置调整指向下一个spi_ioc_transfer
	}
	status = total;	//status等于实际传输的数据长度
done:
	kfree(k_xfers);	//释放k_xfers
	return status;	//返回实际传输的数据长度
}

static long spidev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
	int	err = 0;
	int	retval = 0;
	struct spidev_data	*spidev;
	struct spi_device	*spi;
	u32	tmp;
	unsigned	n_ioc;
	struct spi_ioc_transfer	*ioc;

	if (_IOC_TYPE(cmd) != SPI_IOC_MAGIC)	//判断控制命令的类型
		return -ENOTTY;
	if (_IOC_DIR(cmd) & _IOC_READ)	//判断控制命令的方向是否为读read
		err = !access_ok(VERIFY_WRITE,(void __user *)arg, _IOC_SIZE(cmd));	//判断传输数据大小
	if (err == 0 && _IOC_DIR(cmd) & _IOC_WRITE)	//判断控制命令的方向是否为写write
		err = !access_ok(VERIFY_READ,(void __user *)arg, _IOC_SIZE(cmd));	//判断传输数据大小
	if (err)
		return -EFAULT;

	spidev = filp->private_data;	//从文件私有数据中获取spidev_data
	spin_lock_irq(&spidev->spi_lock);	//上自旋锁
	spi = spi_dev_get(spidev->spi);			//获取spi设备
	spin_unlock_irq(&spidev->spi_lock);	//解自旋锁
	if (spi == NULL)	//获取spi设备失败
		return -ESHUTDOWN;	//则返回错误
	mutex_lock(&spidev->buf_lock);	//上互斥锁
	
	switch (cmd) {
	case SPI_IOC_RD_MODE:	//设置spi读模式
		retval = __put_user(spi->mode & SPI_MODE_MASK,(__u8 __user *)arg);
		break;
	case SPI_IOC_RD_LSB_FIRST:	//设置spi读最低有效位
		retval = __put_user((spi->mode & SPI_LSB_FIRST) ?  1 : 0,(__u8 __user *)arg);
		break;
	case SPI_IOC_RD_BITS_PER_WORD:	//设置spi读每个字含多个个位
		retval = __put_user(spi->bits_per_word, (__u8 __user *)arg);
		break;
	case SPI_IOC_RD_MAX_SPEED_HZ:	//设置spi读最大速率
		retval = __put_user(spi->max_speed_hz, (__u32 __user *)arg);
		break;
	case SPI_IOC_WR_MODE:	//设置spi写模式
		retval = __get_user(tmp, (u8 __user *)arg);
		if (retval == 0) {
			u8	save = spi->mode;	//获取spi设备模式

			if (tmp & ~SPI_MODE_MASK) {
				retval = -EINVAL;
				break;
			}

			tmp |= spi->mode & ~SPI_MODE_MASK;
			spi->mode = (u8)tmp;
			retval = spi_setup(spi);	//配置spi设备
			if (retval < 0)
				spi->mode = save;
			else
				dev_dbg(&spi->dev, "spi mode %02x\n", tmp);
		}
		break;
	case SPI_IOC_WR_LSB_FIRST:	//设置spi写最低有效位
		retval = __get_user(tmp, (__u8 __user *)arg);
		if (retval == 0) {
			u8	save = spi->mode;	//获取spi设备模式

			if (tmp)
				spi->mode |= SPI_LSB_FIRST;
			else
				spi->mode &= ~SPI_LSB_FIRST;
			retval = spi_setup(spi);	//配置spi设备
			if (retval < 0)
				spi->mode = save;
			else
				dev_dbg(&spi->dev, "%csb first\n",tmp ? 'l' : 'm');
		}
		break;
	case SPI_IOC_WR_BITS_PER_WORD:	//设置spi写每个字含多个个位
		retval = __get_user(tmp, (__u8 __user *)arg);	//用户空间获取数据
		if (retval == 0) {
			u8	save = spi->bits_per_word;	//获取spi设备 每个字含多少位

			spi->bits_per_word = tmp;	//更新新的spi设备 每个字含多少位
			retval = spi_setup(spi);	//配置spi设备
			if (retval < 0)	//配置失败
				spi->bits_per_word = save;	//还原spi设备 每个字含多少位
			else
				dev_dbg(&spi->dev, "%d bits per word\n", tmp);
		}
		break;
	case SPI_IOC_WR_MAX_SPEED_HZ:		//设置spi写最大速率
		retval = __get_user(tmp, (__u32 __user *)arg);	//用户空间获取数据
		if (retval == 0) {
			u32	save = spi->max_speed_hz;	//获取spi设备最大速率

			spi->max_speed_hz = tmp;	//更新新的spi设备最大速率
			retval = spi_setup(spi);	//配置spi设备
			if (retval < 0)	//配置失败
				spi->max_speed_hz = save;	//还原spi设备最大速率
			else
				dev_dbg(&spi->dev, "%d Hz (max)\n", tmp);
		}
		break;

	default:
		//命令必须为写方向的命令,且传输数据必须是SPI_IOC_MESSAGE()修饰的命令
		if (_IOC_NR(cmd) != _IOC_NR(SPI_IOC_MESSAGE(0))|| _IOC_DIR(cmd) != _IOC_WRITE) {
			retval = -ENOTTY;
			break;
		}

		tmp = _IOC_SIZE(cmd);	//计算传输数据大小
		if ((tmp % sizeof(struct spi_ioc_transfer)) != 0) {	//判断是否为spi_ioc_transfer对齐
			retval = -EINVAL;
			break;
		}
		n_ioc = tmp / sizeof(struct spi_ioc_transfer);	//计算出spi_ioc_transfer数据的个数
		if (n_ioc == 0)
			break;

		ioc = kmalloc(tmp, GFP_KERNEL);	//分配spi_ioc_transfer指针ioc内存
		if (!ioc) {
			retval = -ENOMEM;
			break;
		}
		if (__copy_from_user(ioc, (void __user *)arg, tmp)) {	//从用户空间复制到内核空间
			kfree(ioc);	//复制失败则释放ioc内存
			retval = -EFAULT;
			break;
		}

		retval = spidev_message(spidev, ioc, n_ioc);	//spidev消息处理
		kfree(ioc);	//释放ioc内存
		break;
	}

	mutex_unlock(&spidev->buf_lock);	//解互斥锁
	spi_dev_put(spi);	//增加spi设备的引用计数
	return retval;
}

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

	mutex_lock(&device_list_lock);	//上互斥锁
	list_for_each_entry(spidev, &device_list, device_entry) {	//遍历device_list
		if (spidev->devt == inode->i_rdev) {	//判断设备号找到对应的设备
			status = 0;	//设置状态为0
			break;
		}
	}
	if (status == 0) {	//找得到对应的设备
		if (!spidev->buffer) {	//spidev_data缓冲区为空
			spidev->buffer = kmalloc(bufsiz, GFP_KERNEL);	//则分配内存
			if (!spidev->buffer) {	//还空
				dev_dbg(&spidev->spi->dev, "open/ENOMEM\n");	//调试了
				status = -ENOMEM;
			}
		}
		if (status == 0) {	//找得到对应的设备
			spidev->users++;	//spidev_data使用者计数++
			filp->private_data = spidev;	//spidev_data放在文件的私有数据里
			nonseekable_open(inode, filp);	//设置文件的打开模式(文件读写指针不会跟随读写操作移动)
		}
	} 
	else
		pr_debug("spidev: nothing for minor %d\n", iminor(inode));
	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_data
	filp->private_data = NULL;		//清除文件的私有数据指针
	spidev->users--;				//使用者个数--
	if (!spidev->users) {	//如果使用者个数为0
		int		dofree;
		kfree(spidev->buffer);	//释放spidev_data的缓冲区内存
		spidev->buffer = NULL;	//清除spidev_data缓冲区指针
		spin_lock_irq(&spidev->spi_lock);	//上自旋锁
		dofree = (spidev->spi == NULL);	//判断spi设备是否与spidev_data解绑了
		spin_unlock_irq(&spidev->spi_lock);	//解自旋锁
		if (dofree)			//没有捆绑的spi设备
			kfree(spidev);	//则是否spidev_data内存
	}
	mutex_unlock(&device_list_lock);
	return status;
}

static const struct file_operations spidev_fops = {		//文件操作函数集
	.owner =	THIS_MODULE,
	.write =	spidev_write,		//写write
	.read =		spidev_read,		//读read
	.unlocked_ioctl = spidev_ioctl,	//控制ioctl
	.open =		spidev_open,		//打开open
	.release =	spidev_release,		//释放release
	.llseek =	no_llseek,			//文件指针移动 no_llseek表示没有移动
};

static struct class *spidev_class;

static int __devinit spidev_probe(struct spi_device *spi)
{
	struct spidev_data	*spidev;
	int	status;
	unsigned long	minor;

	spidev = kzalloc(sizeof(*spidev), GFP_KERNEL);	//分配spidev_data内存
	if (!spidev)
		return -ENOMEM;
	spidev->spi = spi;	//设置spidev_data->spi(spi设备)
	spin_lock_init(&spidev->spi_lock);
	mutex_init(&spidev->buf_lock);
	INIT_LIST_HEAD(&spidev->device_entry);	//初始化spidev_data入口链表
	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/spidev%d.%d(spidev总线号.片选号)
		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);	//spi->dev->p->driver_data=spidev 
	else
		kfree(spidev);

	return status;
}

static int __devexit spidev_remove(struct spi_device *spi)
{
	struct spidev_data	*spidev = spi_get_drvdata(spi);		//根据spi设备获取spidev_data
	spin_lock_irq(&spidev->spi_lock);			//上自旋锁
	spidev->spi = NULL;								//清空spidev_data->spi指针
	spi_set_drvdata(spi, NULL);						//spi->dev->p->driver_data=NULL
	spin_unlock_irq(&spidev->spi_lock);			//解自旋锁
	mutex_lock(&device_list_lock);				//上互斥锁
	list_del(&spidev->device_entry);				//删除spidev_data入口链表
	device_destroy(spidev_class, spidev->devt);		//销毁/dev/spidev%d.%d
	clear_bit(MINOR(spidev->devt), minors);			//清除次设备位图对应位
	if (spidev->users == 0)							//使用者个数为0
		kfree(spidev);								//释放spidev_data内存
	mutex_unlock(&device_list_lock);			//解互斥锁
	return 0;
}

static struct spi_driver spidev_spi_driver = {	//spi设备驱动
	.driver = {
		.name =		"spidev",
		.owner =	THIS_MODULE,
	},
	.probe =	spidev_probe,	//spidev的probe方法(当注册了modalias域为"spidev"的spi设备或板级设备,则会调用probe方法)
	.remove =	__devexit_p(spidev_remove),	//spidev的remove方法
};

static int __init spidev_init(void)		//spidev接口初始化
{
	int status;
	BUILD_BUG_ON(N_SPI_MINORS > 256);
	//注册字符设备,主设备号SPIDEV_MAJOR=153,捆绑的设备操作函数集为spidev_fops
	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.driver.name);
		return PTR_ERR(spidev_class);
	}
	status = spi_register_driver(&spidev_spi_driver);	//注册spi设备驱动spidev_spi_driver
	if (status < 0) {
		class_destroy(spidev_class);
		unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);
	}
	return status;
}
module_init(spidev_init);	//声明初始化入口

static void __exit spidev_exit(void)			//spidev接口销毁
{
	spi_unregister_driver(&spidev_spi_driver);	//注销spi设备驱动spidev_spi_driver
	class_destroy(spidev_class);				//注销设备类spidev_class
	unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);	//注销字符设备
}
module_exit(spidev_exit);	//声明初始化出口

MODULE_AUTHOR("Andrea Paterniani, <a.paterniani@swapp-eng.it>");
MODULE_DESCRIPTION("User mode SPI device interface");
MODULE_LICENSE("GPL");
MODULE_ALIAS("spi:spidev");


二.用户空间例子(spidev_test.c)

  1. #include <stdint.h>   
  2. #include <unistd.h>   
  3. #include <stdio.h>   
  4. #include <stdlib.h>   
  5. #include <getopt.h>   
  6. #include <fcntl.h>   
  7. #include <sys/ioctl.h>   
  8. #include <linux/types.h>   
  9. #include <linux/spi/spidev.h>  
  10.   
  11. #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))  
  12.   
  13. static void pabort(const char *s)  
  14. {  
  15.     perror(s);  
  16.     abort();  
  17. }  
  18.   
  19. static const char *device = "/dev/spidev1.1";  
  20. static uint8_t mode;  
  21. static uint8_t bits = 8;  
  22. static uint32_t speed = 500000;  
  23. static uint16_t delay;  
  24.   
  25. static void transfer(int fd)  
  26. {  
  27.     int ret;  
  28.     uint8_t tx[] = {    //要发送的数据数组   
  29.         0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,  
  30.         0x40, 0x00, 0x00, 0x00, 0x00, 0x95,  
  31.         0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,  
  32.         0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,  
  33.         0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,  
  34.         0xDE, 0xAD, 0xBE, 0xEF, 0xBA, 0xAD,  
  35.         0xF0, 0x0D,  
  36.     };  
  37.     uint8_t rx[ARRAY_SIZE(tx)] = {0, }; //接收的数据数据  
  38.     struct spi_ioc_transfer tr = {  //声明并初始化spi_ioc_transfer结构体  
  39.         .tx_buf = (unsigned long)tx,  
  40.         .rx_buf = (unsigned long)rx,  
  41.         .len = ARRAY_SIZE(tx),  
  42.         .delay_usecs = delay,  
  43.         .speed_hz = speed,  
  44.         .bits_per_word = bits,  
  45.     };  
  46.     //SPI_IOC_MESSAGE(1)的1表示spi_ioc_transfer的数量  
  47.     ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);   //ioctl默认操作,传输数据  
  48.     if (ret < 1)  
  49.         pabort("can't send spi message");  
  50.   
  51.     for (ret = 0; ret < ARRAY_SIZE(tx); ret++) { //打印接收缓冲区  
  52.         if (!(ret % 6))     //6个数据为一簇打印  
  53.             puts("");  
  54.         printf("%.2X ", rx[ret]);  
  55.     }  
  56.     puts("");  
  57. }  
  58.   
  59. static void print_usage(const char *prog)   //参数错误则打印帮助信息  
  60. {  
  61.     printf("Usage: %s [-DsbdlHOLC3]\n", prog);  
  62.     puts("  -D --device   device to use (default /dev/spidev1.1)\n"  
  63.          "  -s --speed    max speed (Hz)\n"  
  64.          "  -d --delay    delay (usec)\n"  
  65.          "  -b --bpw      bits per word \n"  
  66.          "  -l --loop     loopback\n"  
  67.          "  -H --cpha     clock phase\n"  
  68.          "  -O --cpol     clock polarity\n"  
  69.          "  -L --lsb      least significant bit first\n"  
  70.          "  -C --cs-high  chip select active high\n"  
  71.          "  -3 --3wire    SI/SO signals shared\n");  
  72.     exit(1);  
  73. }  
  74.   
  75. static void parse_opts(int argc, char *argv[])  
  76. {  
  77.     while (1) {  
  78.         static const struct option lopts[] = {  //参数命令表  
  79.             { "device",  1, 0, 'D' },  
  80.             { "speed",   1, 0, 's' },  
  81.             { "delay",   1, 0, 'd' },  
  82.             { "bpw",     1, 0, 'b' },  
  83.             { "loop",    0, 0, 'l' },  
  84.             { "cpha",    0, 0, 'H' },  
  85.             { "cpol",    0, 0, 'O' },  
  86.             { "lsb",     0, 0, 'L' },  
  87.             { "cs-high", 0, 0, 'C' },  
  88.             { "3wire",   0, 0, '3' },  
  89.             { "no-cs",   0, 0, 'N' },  
  90.             { "ready",   0, 0, 'R' },  
  91.             { NULL, 0, 0, 0 },  
  92.         };  
  93.         int c;  
  94.   
  95.         c = getopt_long(argc, argv, "D:s:d:b:lHOLC3NR", lopts, NULL);  
  96.   
  97.         if (c == -1)  
  98.             break;  
  99.   
  100.         switch (c) {  
  101.         case 'D':   //设备名  
  102.             device = optarg;  
  103.             break;  
  104.         case 's':   //速率  
  105.             speed = atoi(optarg);  
  106.             break;  
  107.         case 'd':   //延时时间  
  108.             delay = atoi(optarg);  
  109.             break;  
  110.         case 'b':   //每字含多少位  
  111.             bits = atoi(optarg);  
  112.             break;  
  113.         case 'l':   //回送模式  
  114.             mode |= SPI_LOOP;  
  115.             break;  
  116.         case 'H':   //时钟相位  
  117.             mode |= SPI_CPHA;  
  118.             break;  
  119.         case 'O':   //时钟极性  
  120.             mode |= SPI_CPOL;  
  121.             break;  
  122.         case 'L':   //lsb 最低有效位  
  123.             mode |= SPI_LSB_FIRST;  
  124.             break;  
  125.         case 'C':   //片选高电平  
  126.             mode |= SPI_CS_HIGH;  
  127.             break;  
  128.         case '3':   //3线传输模式  
  129.             mode |= SPI_3WIRE;  
  130.             break;  
  131.         case 'N':   //没片选  
  132.             mode |= SPI_NO_CS;  
  133.             break;  
  134.         case 'R':   //从机拉低电平停止数据传输  
  135.             mode |= SPI_READY;  
  136.             break;  
  137.         default:    //错误的参数  
  138.             print_usage(argv[0]);  
  139.             break;  
  140.         }  
  141.     }  
  142. }  
  143.   
  144. int main(int argc, char *argv[])  
  145. {  
  146.     int ret = 0;  
  147.     int fd;  
  148.   
  149.     parse_opts(argc, argv); //解析传递进来的参数  
  150.   
  151.     fd = open(device, O_RDWR);  //打开设备文件  
  152.     if (fd < 0)  
  153.         pabort("can't open device");  
  154.   
  155.     /* 
  156.      * spi mode //设置spi设备模式 
  157.      */  
  158.     ret = ioctl(fd, SPI_IOC_WR_MODE, &mode);    //写模式  
  159.     if (ret == -1)  
  160.         pabort("can't set spi mode");  
  161.   
  162.     ret = ioctl(fd, SPI_IOC_RD_MODE, &mode);    //读模式  
  163.     if (ret == -1)  
  164.         pabort("can't get spi mode");  
  165.   
  166.     /* 
  167.      * bits per word    //设置每个字含多少位 
  168.      */  
  169.     ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);   //写 每个字含多少位  
  170.     if (ret == -1)  
  171.         pabort("can't set bits per word");  
  172.   
  173.     ret = ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &bits);   //读 每个字含多少位  
  174.     if (ret == -1)  
  175.         pabort("can't get bits per word");  
  176.   
  177.     /* 
  178.      * max speed hz     //设置速率 
  179.      */  
  180.     ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);   //写速率  
  181.     if (ret == -1)  
  182.         pabort("can't set max speed hz");  
  183.   
  184.     ret = ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed);   //读速率  
  185.     if (ret == -1)  
  186.         pabort("can't get max speed hz");  
  187.     //打印模式,每字多少位和速率信息   
  188.     printf("spi mode: %d\n", mode);  
  189.     printf("bits per word: %d\n", bits);  
  190.     printf("max speed: %d Hz (%d KHz)\n", speed, speed/1000);  
  191.   
  192.     transfer(fd);   //传输测试   
  193.   
  194.     close(fd);  //关闭设备   
  195.   
  196.     return ret;  
  197. }  
#include <stdint.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/types.h>
#include <linux/spi/spidev.h>

#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))

static void pabort(const char *s)
{
	perror(s);
	abort();
}

static const char *device = "/dev/spidev1.1";
static uint8_t mode;
static uint8_t bits = 8;
static uint32_t speed = 500000;
static uint16_t delay;

static void transfer(int fd)
{
	int ret;
	uint8_t tx[] = {	//要发送的数据数组
		0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
		0x40, 0x00, 0x00, 0x00, 0x00, 0x95,
		0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
		0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
		0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
		0xDE, 0xAD, 0xBE, 0xEF, 0xBA, 0xAD,
		0xF0, 0x0D,
	};
	uint8_t rx[ARRAY_SIZE(tx)] = {0, };	//接收的数据数据
	struct spi_ioc_transfer tr = {	//声明并初始化spi_ioc_transfer结构体
		.tx_buf = (unsigned long)tx,
		.rx_buf = (unsigned long)rx,
		.len = ARRAY_SIZE(tx),
		.delay_usecs = delay,
		.speed_hz = speed,
		.bits_per_word = bits,
	};
	//SPI_IOC_MESSAGE(1)的1表示spi_ioc_transfer的数量
	ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);	//ioctl默认操作,传输数据
	if (ret < 1)
		pabort("can't send spi message");

	for (ret = 0; ret < ARRAY_SIZE(tx); ret++) {	//打印接收缓冲区
		if (!(ret % 6))		//6个数据为一簇打印
			puts("");
		printf("%.2X ", rx[ret]);
	}
	puts("");
}

static void print_usage(const char *prog)	//参数错误则打印帮助信息
{
	printf("Usage: %s [-DsbdlHOLC3]\n", prog);
	puts("  -D --device   device to use (default /dev/spidev1.1)\n"
	     "  -s --speed    max speed (Hz)\n"
	     "  -d --delay    delay (usec)\n"
	     "  -b --bpw      bits per word \n"
	     "  -l --loop     loopback\n"
	     "  -H --cpha     clock phase\n"
	     "  -O --cpol     clock polarity\n"
	     "  -L --lsb      least significant bit first\n"
	     "  -C --cs-high  chip select active high\n"
	     "  -3 --3wire    SI/SO signals shared\n");
	exit(1);
}

static void parse_opts(int argc, char *argv[])
{
	while (1) {
		static const struct option lopts[] = {	//参数命令表
			{ "device",  1, 0, 'D' },
			{ "speed",   1, 0, 's' },
			{ "delay",   1, 0, 'd' },
			{ "bpw",     1, 0, 'b' },
			{ "loop",    0, 0, 'l' },
			{ "cpha",    0, 0, 'H' },
			{ "cpol",    0, 0, 'O' },
			{ "lsb",     0, 0, 'L' },
			{ "cs-high", 0, 0, 'C' },
			{ "3wire",   0, 0, '3' },
			{ "no-cs",   0, 0, 'N' },
			{ "ready",   0, 0, 'R' },
			{ NULL, 0, 0, 0 },
		};
		int c;

		c = getopt_long(argc, argv, "D:s:d:b:lHOLC3NR", lopts, NULL);

		if (c == -1)
			break;

		switch (c) {
		case 'D':	//设备名
			device = optarg;
			break;
		case 's':	//速率
			speed = atoi(optarg);
			break;
		case 'd':	//延时时间
			delay = atoi(optarg);
			break;
		case 'b':	//每字含多少位
			bits = atoi(optarg);
			break;
		case 'l':	//回送模式
			mode |= SPI_LOOP;
			break;
		case 'H':	//时钟相位
			mode |= SPI_CPHA;
			break;
		case 'O':	//时钟极性
			mode |= SPI_CPOL;
			break;
		case 'L':	//lsb 最低有效位
			mode |= SPI_LSB_FIRST;
			break;
		case 'C':	//片选高电平
			mode |= SPI_CS_HIGH;
			break;
		case '3':	//3线传输模式
			mode |= SPI_3WIRE;
			break;
		case 'N':	//没片选
			mode |= SPI_NO_CS;
			break;
		case 'R':	//从机拉低电平停止数据传输
			mode |= SPI_READY;
			break;
		default:	//错误的参数
			print_usage(argv[0]);
			break;
		}
	}
}

int main(int argc, char *argv[])
{
	int ret = 0;
	int fd;

	parse_opts(argc, argv);	//解析传递进来的参数

	fd = open(device, O_RDWR);	//打开设备文件
	if (fd < 0)
		pabort("can't open device");

	/*
	 * spi mode	//设置spi设备模式
	 */
	ret = ioctl(fd, SPI_IOC_WR_MODE, &mode);	//写模式
	if (ret == -1)
		pabort("can't set spi mode");

	ret = ioctl(fd, SPI_IOC_RD_MODE, &mode);	//读模式
	if (ret == -1)
		pabort("can't get spi mode");

	/*
	 * bits per word	//设置每个字含多少位
	 */
	ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);	//写 每个字含多少位
	if (ret == -1)
		pabort("can't set bits per word");

	ret = ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &bits);	//读 每个字含多少位
	if (ret == -1)
		pabort("can't get bits per word");

	/*
	 * max speed hz		//设置速率
	 */
	ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);	//写速率
	if (ret == -1)
		pabort("can't set max speed hz");

	ret = ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed);	//读速率
	if (ret == -1)
		pabort("can't get max speed hz");
	//打印模式,每字多少位和速率信息
	printf("spi mode: %d\n", mode);
	printf("bits per word: %d\n", bits);
	printf("max speed: %d Hz (%d KHz)\n", speed, speed/1000);

	transfer(fd);	//传输测试

	close(fd);	//关闭设备

	return ret;
}


这里整理下ioctl的命令:

  1. SPI_IOC_RD_MODE     //读 模式  
  2. SPI_IOC_RD_LSB_FIRST    //读 LSB   
  3. SPI_IOC_RD_BITS_PER_WORD    //读 每字多少位  
  4. SPI_IOC_RD_MAX_SPEED_HZ //读 最大速率   
  5. SPI_IOC_WR_MODE     //写 模式   
  6. SPI_IOC_WR_LSB_FIRST    //写 LSB   
  7. SPI_IOC_WR_BITS_PER_WORD    //写 每字多少位  
  8. SPI_IOC_WR_MAX_SPEED_HZ //写 最大速率   
  9. SPI_IOC_MESSAGE(n)      //传输n个数据包  
SPI_IOC_RD_MODE		//读 模式
SPI_IOC_RD_LSB_FIRST	//读 LSB
SPI_IOC_RD_BITS_PER_WORD	//读 每字多少位
SPI_IOC_RD_MAX_SPEED_HZ	//读 最大速率
SPI_IOC_WR_MODE		//写 模式
SPI_IOC_WR_LSB_FIRST	//写 LSB
SPI_IOC_WR_BITS_PER_WORD	//写 每字多少位
SPI_IOC_WR_MAX_SPEED_HZ	//写 最大速率
SPI_IOC_MESSAGE(n)		//传输n个数据包

<--一 linux spi子系统(框架)


  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
引用\[1\]: pinctrl子系统Linux内核的一个框架,用于统一管理不同系统芯片的引脚控制和配置。在pinctrl子系统,有一个主要的结构体struct pinctrl_dev,用于表示一个引脚控制器设备。一般系统只会有一个struct pinctrl_dev实例。通过调用pinctrl_register_pins函数,可以为每个引脚分配一个独立的struct pin_desc结构体,并进行相应的赋值。struct pin_desc是pinctrl子系统用来管理每个引脚的最小单元。 引用\[2\]: 在pinctrl子系统,引脚控制器并不只有一个,而是可以有多个。在设备树,含有pinctrl-names和pinctrl-0属性的节点会拥有一个struct pinctrl结构体。pinctrl-names和pinctrl-0属性的内容指定了要控制的引脚。有时候会遇到多个pinctrl-names和pinctrl-0属性,可以通过这种方式来配置不同的引脚控制。 引用\[3\]: pinctrl子系统的目的是为了统一各种不同的系统芯片的引脚管理。在ARM的各种SOC芯片,一个引脚可以被复用为不同功能的引脚,例如GPIO、SPI、I2C、UART等。pinctrl子系统的引入正是为了解决这种复用问题,使得不同SOC厂商的引脚管理能够在Linux内核得到统一。 #### 引用[.reference_title] - *1* *3* [Linux pinctrl子系统框架流程详解(基于Kernel 3.16,arm,设备树)](https://blog.csdn.net/ZHONGkunjia/article/details/89873417)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [基于Linux的Pinctrl子系统框架源码分析](https://blog.csdn.net/qq_42017846/article/details/127795402)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值