Linux驱动分析之SPI设备

简介: 前面我们对SPI控制器驱动进行了分析,接下来来分析SPI设备驱动。我们以DS1302驱动作为分析对象。DS1302是一款RTC芯片,估计很多人在学单片机时用到过。RTC芯片算是比较简单的,也方便分析理解。

 前言

前面我们对SPI控制器驱动进行了分析,接下来来分析SPI设备驱动。我们以DS1302驱动作为分析对象。DS1302是一款RTC芯片,估计很多人在学单片机时用到过。RTC芯片算是比较简单的,也方便分析理解。


SPI设备驱动分析

内核:4.20

芯片:DS1302  RTC

下面的代码分析主要都在注释中,会按照驱动中函数的执行顺序分析。我们不需要去关心RTC的具体内容,因为它主要是一些读写寄存器的过程。应主要关注SPI的通信。


(1) 装载和卸载函数

//dts匹配表staticconststructof_device_idds1302_dt_ids[] = {
  { .compatible="maxim,ds1302", },
  { /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, ds1302_dt_ids);
staticstructspi_driverds1302_driver= {
  .driver.name="rtc-ds1302",
  .driver.of_match_table=of_match_ptr(ds1302_dt_ids),
  .probe=ds1302_probe,
  .remove=ds1302_remove,
};
//封装了spi_register_driver和spi_unregister_drivermodule_spi_driver(ds1302_driver);

image.gif

module_spi_driver宏定义在 include/linux/spi/spi.h, 具体看一下源码

#define module_spi_driver(__spi_driver) \module_driver(__spi_driver, spi_register_driver, \spi_unregister_driver)#define module_driver(__driver, __register, __unregister, ...) \static int __init __driver##_init(void) \{ \return __register(&(__driver) , ##__VA_ARGS__); \} \module_init(__driver##_init); \static void __exit __driver##_exit(void) \{ \__unregister(&(__driver) , ##__VA_ARGS__); \} \module_exit(__driver##_exit);

image.gif

所以只是对 spi_register_driverspi_unregister_driver 做了封装。


(2) probe()函数

staticintds1302_probe(structspi_device*spi)
{
structrtc_device*rtc;
u8addr;
u8buf[4];
u8*bp;
intstatus;
//检查是不是8bit传输if (spi->bits_per_word&& (spi->bits_per_word!=8)) {
dev_err(&spi->dev, "bad word length\n");
return-EINVAL;
  } elseif (spi->max_speed_hz>2000000) {//检查最大速率dev_err(&spi->dev, "speed is too high\n");
return-EINVAL;
  } elseif (spi->mode&SPI_CPHA) { 
dev_err(&spi->dev, "bad mode\n");
return-EINVAL;
  }
//使用spi读写一下寄存器,检查是否可以写(DS1302有个寄存器是设置写保护的)addr=RTC_ADDR_CTRL<<1|RTC_CMD_READ;
status=spi_write_then_read(spi, &addr, sizeof(addr), buf, 1);
    ......
spi_set_drvdata(spi, spi);
//注册rtcrtc=devm_rtc_device_register(&spi->dev, "ds1302",
&ds1302_rtc_ops, THIS_MODULE);
return0;
}

image.gif

(3) RTC设置和读取函数

//读取时间staticintds1302_rtc_get_time(structdevice*dev, structrtc_time*time)
{
structspi_device*spi=dev_get_drvdata(dev);
u8addr=RTC_CLCK_BURST<<1|RTC_CMD_READ;
u8buf[RTC_CLCK_LEN-1];
intstatus;
//spi读取时间status=spi_write_then_read(spi, &addr, sizeof(addr),
buf, sizeof(buf));
if (status<0)
returnstatus;
/* Decode the registers */time->tm_sec=bcd2bin(buf[RTC_ADDR_SEC]);
time->tm_min=bcd2bin(buf[RTC_ADDR_MIN]);
time->tm_hour=bcd2bin(buf[RTC_ADDR_HOUR]);
time->tm_wday=buf[RTC_ADDR_DAY] -1;
time->tm_mday=bcd2bin(buf[RTC_ADDR_DATE]);
time->tm_mon=bcd2bin(buf[RTC_ADDR_MON]) -1;
time->tm_year=bcd2bin(buf[RTC_ADDR_YEAR]) +100;
return0;
}
//设置时间staticintds1302_rtc_set_time(structdevice*dev, structrtc_time*time)
{
structspi_device*spi=dev_get_drvdata(dev);
u8buf[1+RTC_CLCK_LEN];
u8*bp;
intstatus;
/* Enable writing */bp=buf;
*bp++=RTC_ADDR_CTRL<<1|RTC_CMD_WRITE;
*bp++=RTC_CMD_WRITE_ENABLE;
//关闭写保护status=spi_write_then_read(spi, buf, 2,
NULL, 0);
if (status)
returnstatus;
/* Write registers starting at the first time/date address. */bp=buf;
*bp++=RTC_CLCK_BURST<<1|RTC_CMD_WRITE;
*bp++=bin2bcd(time->tm_sec);
*bp++=bin2bcd(time->tm_min);
*bp++=bin2bcd(time->tm_hour);
*bp++=bin2bcd(time->tm_mday);
*bp++=bin2bcd(time->tm_mon+1);
*bp++=time->tm_wday+1;
*bp++=bin2bcd(time->tm_year%100);
*bp++=RTC_CMD_WRITE_DISABLE;
//只有写,没有读returnspi_write_then_read(spi, buf, sizeof(buf),
NULL, 0);
}
staticconststructrtc_class_opsds1302_rtc_ops= {
  .read_time=ds1302_rtc_get_time,
  .set_time=ds1302_rtc_set_time,
};

image.gif

上面读取和设置都是调用spi_write_then_read来进行Spi通信,这个是Linux帮我们封装好的接口函数。看一下具体实现:

intspi_write_then_read(structspi_device*spi,
constvoid*txbuf, unsignedn_tx,
void*rxbuf, unsignedn_rx)
{
staticDEFINE_MUTEX(lock);
intstatus;
structspi_messagemessage;
structspi_transferx[2];
u8*local_buf;
if ((n_tx+n_rx) >SPI_BUFSIZ||!mutex_trylock(&lock)) {
local_buf=kmalloc(max((unsigned)SPI_BUFSIZ, n_tx+n_rx),
GFP_KERNEL|GFP_DMA);
if (!local_buf)
return-ENOMEM;
  } else {
local_buf=buf;
  }
//初始化spi_messagespi_message_init(&message);
//将要传的数据放到spi_transfer,然后追加到spi_messagememset(x, 0, sizeof(x));
if (n_tx) {
x[0].len=n_tx;
spi_message_add_tail(&x[0], &message);
  }
if (n_rx) {
x[1].len=n_rx;
spi_message_add_tail(&x[1], &message);
  }
memcpy(local_buf, txbuf, n_tx);
x[0].tx_buf=local_buf;
x[1].rx_buf=local_buf+n_tx;
//进行SPI发送status=spi_sync(spi, &message);
if (status==0)
memcpy(rxbuf, x[1].rx_buf, n_rx);
if (x[0].tx_buf==buf)
mutex_unlock(&lock);
elsekfree(local_buf);
returnstatus;
}

image.gif

spi_sync最终会调用spi_master->transfer();传递给spi_sync函数的参数中有spi_device, 而spi_device中又包含spi_master的指针。所以就能找到了对应的spi控制器进行数据发送。


总结

大部分的SPI设备驱动框架都差不多,大家可以配合下面两篇文章一起看。这样更能理解。我们会发现,SPI设备驱动内容其实就是使用SPI控制器(spi_master)去对具体芯片设备进行读写。


相关文章
|
1月前
|
安全 Linux iOS开发
Binary Ninja 5.1.8104 (macOS, Linux, Windows) - 反编译器、反汇编器、调试器和二进制分析平台
Binary Ninja 5.1.8104 (macOS, Linux, Windows) - 反编译器、反汇编器、调试器和二进制分析平台
315 53
Binary Ninja 5.1.8104 (macOS, Linux, Windows) - 反编译器、反汇编器、调试器和二进制分析平台
|
1月前
|
Linux API iOS开发
Binary Ninja 4.2.6455 (macOS, Linux, Windows) - 反编译器、反汇编器、调试器和二进制分析平台
Binary Ninja 4.2.6455 (macOS, Linux, Windows) - 反编译器、反汇编器、调试器和二进制分析平台
181 14
Binary Ninja 4.2.6455 (macOS, Linux, Windows) - 反编译器、反汇编器、调试器和二进制分析平台
|
2月前
|
安全 Linux 网络安全
Nipper 3.9.0 for Windows & Linux - 网络设备漏洞评估
Nipper 3.9.0 for Windows & Linux - 网络设备漏洞评估
97 0
Nipper 3.9.0 for Windows & Linux - 网络设备漏洞评估
|
2月前
|
数据管理 Linux iOS开发
Splunk Enterprise 9.4.5 (macOS, Linux, Windows) - 机器数据管理和分析
Splunk Enterprise 9.4.5 (macOS, Linux, Windows) - 机器数据管理和分析
120 0
|
3月前
|
数据采集 编解码 运维
一文讲完说懂 WowKey -- WowKey 是一款 Linux 类设备的命令行(CLT)运维工具
WowKey 是一款面向 Linux 类设备的命令行运维工具,支持自动登录、批量执行及标准化维护,适用于企业、团队或个人管理多台设备,显著提升运维效率与质量。
|
Unix Linux iOS开发
Splunk Enterprise 10.0.0 (macOS, Linux, Windows) - 搜索、分析和可视化,数据全面洞察平台
Splunk Enterprise 10.0.0 (macOS, Linux, Windows) - 搜索、分析和可视化,数据全面洞察平台
109 0
|
4月前
|
监控 Linux 开发者
理解Linux操作系统内核中物理设备驱动(phy driver)的功能。
综合来看,物理设备驱动在Linux系统中的作用是至关重要的,它通过与硬件设备的紧密配合,为上层应用提供稳定可靠的通信基础设施。开发一款优秀的物理设备驱动需要开发者具备深厚的硬件知识、熟练的编程技能以及对Linux内核架构的深入理解,以确保驱动程序能在不同的硬件平台和网络条件下都能提供最优的性能。
270 0
|
6月前
|
安全 Ubuntu Linux
Nipper 3.8.0 for Windows & Linux - 网络设备漏洞评估
Nipper 3.8.0 for Windows & Linux - 网络设备漏洞评估
214 0
Nipper 3.8.0 for Windows & Linux - 网络设备漏洞评估
|
7月前
|
运维 安全 Linux
试试Linux设备命令行运维工具——Wowkey
WowKey 是一款专为 Linux 设备设计的命令行运维工具,提供自动化、批量化、标准化、简单化的运维解决方案。它简单易用、高效集成且无依赖,仅需 WIS 指令剧本文件、APT 账号密码文件和 wowkey 命令即可操作。通过分离鉴权内容与执行内容,WowKey 让运维人员专注于决策,摆脱繁琐的交互与执行细节工作,大幅提升运维效率与质量。无论是健康检查、数据采集还是配置更新,WowKey 都能助您轻松应对大规模设备运维挑战。立即从官方资源了解更多信息:https://atsight.top/training。
|
7月前
|
数据采集 运维 安全
Linux设备命令行运维工具WowKey问答
WowKey 是一款用于 Linux 设备运维的工具,可通过命令行手动或自动执行指令剧本,实现批量、标准化操作,如健康检查、数据采集、配置更新等。它简单易用,只需编写 WIS 指令剧本和 APT 帐号密码表文件,学习成本极低。支持不同流派的 Linux 系统,如 RHEL、Debian、SUSE 等,只要使用通用 Shell 命令即可通吃Linux设备。