当前位置: 首页 > news >正文

SPI驱动(九) -- SPI_Master驱动程序

文章目录

  • 参考资料:
  • 一、SPI传输概述
  • 二、SPI传输的两种方法
    • 2.1 旧方法
    • 2.2 新方法


参考资料:

参考资料:

  • 参考内核源码: drivers\spi\spi.c

一、SPI传输概述

SPI控制器的作用是发起与它下面挂接的SPI设备之间的数据传输,那么控制器驱动程序的核心就是实现与设备之间的数据传输过程。在内核中,SPI传输的最小单位是spi_transfer,对于一个设备,可以发起多个spi_transfer。这些spi_transfer,会放入一个spi_message里面。每个SPI设备都有一个自己的spi_message,同一个spi_master下的spi_message,放在一个队里。

  • spi_transfer:指定tx_buf、rx_buf、len
	struct spi_transfer {
	const void	*tx_buf;
	void		*rx_buf;
	...
	}
  • 同一个SPI设备的spi_transfer,使用spi_message来管理:
struct spi_message {
	struct list_head	transfers; //管理spi_transfer
	...
}
  • 同一个SPI Master下的spi_message,放在一个队列里:
struct spi_master {
	...
	struct list_head		queue;   //存放每个spi_device的spi_message
	...
}

所以,反过来,SPI传输的流程是:

  • spi_master的队列里取出每一个spi_message
    • spi_message的队里里取出一个spi_transfer
      • 处理spi_transfer

二、SPI传输的两种方法

spi_master结构中,有两个传输函数,函数指针transfer 代表旧方法,函数指针transfer_one代表新方法。

struct spi_master {
...
/* 旧方法 */
int	(*transfer) (struct spi_device *spi, struct spi_message *mesg);
/* 新方法 */
int (*transfer_one)(struct spi_master *master, struct spi_device *spi, struct spi_transfer *transfer);
...
}

2.1 旧方法

在这里插入图片描述
内核传输函数入口spi_sync()

int spi_sync(struct spi_device *spi, struct spi_message *message)
{
	ret = __spi_sync(spi, message);
}

继续调用__spi_sync(),里面设置了传输完成回调函数spi_complete,如果master->transfer == spi_queued_transfer表示使用新方法,新方法使用内核提供的transfer 函数,它会帮我们把spi_message放入queue并处理。else分支spi_async_locked表示的是旧方法,需要我们自己实现transfer函数,自己管理queue,自己触发传输。spi_async_locked是异步传输,触发传输后马上返回,随后wait_for_completion(&done)等待传输结果。

static int __spi_sync(struct spi_device *spi, struct spi_message *message)
{
	DECLARE_COMPLETION_ONSTACK(done);
	int status;
	struct spi_master *master = spi->master;
	unsigned long flags;

	status = __spi_validate(spi, message);
	if (status != 0)
		return status;

	message->complete = spi_complete; //传输完成回调函数
	message->context = &done;
	message->spi = spi;

	SPI_STATISTICS_INCREMENT_FIELD(&master->statistics, spi_sync);
	SPI_STATISTICS_INCREMENT_FIELD(&spi->statistics, spi_sync);

	/* If we're not using the legacy transfer method then we will
	 * try to transfer in the calling context so special case.
	 * This code would be less tricky if we could remove the
	 * support for driver implemented message queues.
	 */
	if (master->transfer == spi_queued_transfer) { //新方法
		spin_lock_irqsave(&master->bus_lock_spinlock, flags);

		trace_spi_message_submit(message);

		status = __spi_queued_transfer(spi, message, false);

		spin_unlock_irqrestore(&master->bus_lock_spinlock, flags);
	} else {
		status = spi_async_locked(spi, message); //老方法,异步传输
	}

	if (status == 0) {
		/* Push out the messages in the calling context if we
		 * can.
		 */
		if (master->transfer == spi_queued_transfer) {
			SPI_STATISTICS_INCREMENT_FIELD(&master->statistics,
						       spi_sync_immediate);
			SPI_STATISTICS_INCREMENT_FIELD(&spi->statistics,
						       spi_sync_immediate);
			__spi_pump_messages(master, false);
		}

		wait_for_completion(&done); //等待传输结果
		status = message->status;
	}
	message->context = NULL;
	return status;
}

继续看spi_async_locked(), 它调用__spi_async(), 继续往下,最终调用master->transfer(spi, message),这个就是自己实现的transfer函数。

int spi_async_locked(struct spi_device *spi, struct spi_message *message)
{
	struct spi_master *master = spi->master;
	int ret;
	unsigned long flags;

	ret = __spi_validate(spi, message);
	if (ret != 0)
		return ret;

	spin_lock_irqsave(&master->bus_lock_spinlock, flags);

	ret = __spi_async(spi, message);

	spin_unlock_irqrestore(&master->bus_lock_spinlock, flags);

	return ret;

}

static int __spi_async(struct spi_device *spi, struct spi_message *message)
{
	struct spi_master *master = spi->master;

	message->spi = spi;

	SPI_STATISTICS_INCREMENT_FIELD(&master->statistics, spi_async);
	SPI_STATISTICS_INCREMENT_FIELD(&spi->statistics, spi_async);

	trace_spi_message_submit(message);

	return master->transfer(spi, message); //需要自己实现transfer
}

2.2 新方法

在这里插入图片描述
新方法第一步会调用__spi_queued_transfer()spi_message放入队列,然后在调用__spi_pump_messages()压出数据进行处理。流程如上图。

相关文章:

  • Netty基础—8.Netty实现私有协议栈一
  • 蓝桥杯练习day1:自除数
  • Linux操作系统应用
  • 【JavaScript】三、数据类型
  • SpringBoot-已添加并下载的依赖,reload和mvn clean 后还是提示找不到jar包问题
  • svn常用命令
  • Spring Data JPA 参数陷阱:从 500 错误到完美解决的奇妙之旅 ✨
  • 【linux】服务器限制客户端ssh访问
  • debian12.9 kde切到x11安装搜狗输入法遇到的问题
  • JUC大揭秘:从ConcurrentHashMap到线程池,玩转Java并发编程!
  • 嵌入式编程优化技巧:do-while(0)、case范围扩展与内建函数
  • 图生视频技术的发展与展望:从技术突破到未来图景
  • Vue.js+Element UI 登录界面开发详解【附源码】
  • Cursor在内网环境配置自定义DeepSeek API
  • opencv中stitch图像融合
  • 学c++的人可以几天速通python?
  • C语言历史
  • ThreadPoolExecutor 源码分析
  • 荣耀手机卸载应用商店、快应用中心等系统自带的
  • Linux 命令:按内存使用大小排序查看 PID 的完全指南
  • 国寿资产获批参与第三批保险资金长期投资改革试点
  • 最高法、证监会:常态化开展证券纠纷代表人诉讼,降低投资者维权成本
  • 曾犯强奸罪教师出狱后办教培机构?柳州鱼峰区教育局:正核实
  • 呼吸医学专家杜晓华博士逝世,终年50岁
  • “80后”北京市东城区副区长王智勇获公示拟任区委常委
  • 将人工智能送上太空,我国太空计算卫星星座成功发射