一、概述

MFRC522 支持 SPI、I2C、UART 接口,我在某宝上购买了一个 SPI 接口的 RC522 模块。此笔记主要要是通过 RC522 模块学习 linux 中的 SPI 驱动,方便今后写其他 SPI 驱动时做参考。有需要的小伙伴可以收藏一下。

二、RC522 介绍

  1. 产品外观

    现在的生活中 IC 卡的生活场景大家都不陌生了,外观如下图所示,其中 PCB 部分是主机,白色和绿色的是 IC 卡

  2. 产品介绍

    MFRC522 是应用于 13.56MHz 非接触式通信中高集成度读写卡系列芯片中的一员。是NXP 公司针对“三表”应用推出的一款低 电压、低成本、体积小的非接触式读写卡芯片,是智能仪表和便携式手持设备研发的较好选择【百度百科】。

    更多信息可以参考芯片手册,对于英文不好的小伙伴,可以参考MFRC522中文手册 https://www.doc88.com/p-4042994624020.html?r=1,MFRC522 的引脚如下图所示:

  3. 卡片的内部储存信息

    一张卡片分成若干个扇区,一个扇区有四个块,每一块存储16字节的信息,以块为存取单位。第0扇区的第0块存储卡片的UID和厂商信息,每个扇区的第3块存储该扇区的密钥和控制字信息(这里的第三块是指 block * 4 + 3),其余均可以用来存储数据。

    每个区的块3作为控制块,块0、1、2作为数据块,其中数据块用作一般的数据存储时,可以对其中的数据进行读写操作;用作数据值,可以进行初始化值、加值、减值、读值操作,我在网上找了一张图片,如下图所示:



    注意:我没见过其他的卡片,是否存在我就不知道了,我手里有一张卡片容量为8K位EEPROM,分为16个扇区,每个扇区为4块,每块16个字节,总共有64块,之前我就错误的以为只有一个卡片容量。

  4. 存取控制

    每个扇区的密码和存取控制都是独立的,存取控制是4个字节,即32位(在块3中)。

    每个块都有存取条件,存取条件是由密码和存取控制共同决定的。

    每个块都有相应的三个控制位,这三个控制位存在于存取控制字节中,相应的控制位决定了该块的访问权限,控制位如图:

    注意: 每个扇区的所有块的存取条件控制位,都放在了该扇区的块3中,如图:

  5. 数据块的存取控制

    对数据块,与就是块0、1、2的存取控制是由对应块的控制位来决定的:



    注意: 要想对数据块进行操作,首先要看该数据块的控制位是否允许对数据块的操作,如果允许操作,再看需要验证什么密码,只有验证密码正确后才可以对该数据块执行相应操作。 一般密码A的初始值都是 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF

  6. 控制块的存取控

    块3(控制块)的存取操作与数据块不同,如图:

  7. 通信流程

注意:具体说明参考 MFRC522 手册

三、SPI 设备驱动

  1. /**
  2. * @brief 向 spi 设备中写入多个寄存器数据
  3. *
  4. * @param spi spi 设备
  5. * @param reg 要写入的寄存器首地址
  6. * @param buf 要写入的数据缓冲区
  7. * @param len 要写入的数据长度
  8. * @return 返回执行结果
  9. */
  10. static s32 spi_write_regs(struct spi_device *spi, u8 reg, u8 *buf, u8 len)
  11. {
  12. int ret = -1;
  13. unsigned char *txdata;
  14. struct spi_message msg;
  15. struct spi_transfer *trf;
  16. /* 申请内存*/
  17. trf = kzalloc(sizeof(struct spi_transfer), GFP_KERNEL);
  18. if(!trf) {
  19. return -ENOMEM;
  20. }
  21. txdata = kzalloc(sizeof(char)+len, GFP_KERNEL);
  22. if(!txdata) {
  23. goto out1;
  24. }
  25. /* 一共发送 len+1 个字节的数据,第一个字节为寄存器首地址,len 为要写入的寄存器的集合,*/
  26. *txdata = reg & ~0x80; /* 写数据的时候首寄存器地址 bit8 要清零 */
  27. memcpy(txdata+1, buf, len); /* 把 len 个数据拷贝到 txdata 里 */
  28. trf->tx_buf = txdata; /* 要发送的数据 */
  29. trf->len = len+1; /* trf->len = 发送的长度+读取的长度 */
  30. spi_message_init(&msg); /* 初始化 spi_message */
  31. spi_message_add_tail(trf, &msg);/*添加到 spi_message 队列 */
  32. ret = spi_sync(spi, &msg); /* 同步发送 */
  33. if(ret) {
  34. goto out2;
  35. }
  36. out2:
  37. kfree(txdata); /* 释放内存 */
  38. out1:
  39. kfree(trf); /* 释放内存 */
  40. return ret;
  41. }
  42. /**
  43. * @brief 读取 spi 的多个寄存器数据
  44. *
  45. * @param spi spi 设备
  46. * @param reg 要读取的寄存器首地址
  47. * @param buf 要读取的数据缓冲区
  48. * @param len 要读取的数据长度
  49. * @return 返回执行结果
  50. */
  51. static int spi_read_regs(struct spi_device *spi, u8 reg, void *buf, int len)
  52. {
  53. int ret = -1;
  54. unsigned char txdata[1];
  55. unsigned char * rxdata;
  56. struct spi_message msg;
  57. struct spi_transfer *trf;
  58. /* 申请内存*/
  59. trf = kzalloc(sizeof(struct spi_transfer), GFP_KERNEL);
  60. if(!trf) {
  61. return -ENOMEM;
  62. }
  63. /* 申请内存 */
  64. rxdata = kzalloc(sizeof(char) * len, GFP_KERNEL);
  65. if(!rxdata) {
  66. goto out1;
  67. }
  68. /* 一共发送 len+1 个字节的数据,第一个字节为寄存器首地址,一共要读取 len 个字节长度的数据,*/
  69. txdata[0] = reg | 0x80; /* 写数据的时候首寄存器地址 bit8 要置 1 */
  70. trf->tx_buf = txdata; /* 要发送的数据 */
  71. trf->rx_buf = rxdata; /* 要读取的数据 */
  72. trf->len = len+1; /* trf->len = 发送的长度+读取的长度 */
  73. spi_message_init(&msg); /* 初始化 spi_message */
  74. spi_message_add_tail(trf, &msg);/* 将 spi_transfer 添加到 spi_message*/
  75. ret = spi_sync(spi, &msg); /* 同步发送 */
  76. if(ret) {
  77. goto out2;
  78. }
  79. memcpy(buf , rxdata+1, len); /* 只需要读取的数据 */
  80. out2:
  81. kfree(rxdata); /* 释放内存 */
  82. out1:
  83. kfree(trf); /* 释放内存 */
  84. return ret;
  85. }
  86. /**
  87. * @brief 打开设备
  88. *
  89. * @param inode 传递给驱动的 inode
  90. * @param filp 设备文件,file 结构体有个叫做 private_data 的成员变量
  91. * 一般在 open 的时候将 private_data 指向设备结构体。
  92. * @return 0 成功;其他 失败
  93. */
  94. static int rc522_open(struct inode *inode, struct file *filp)
  95. {
  96. }
  97. /**
  98. * @brief 从设备读取数据
  99. *
  100. * @param filp 要打开的设备文件(文件描述符)
  101. * @param buf 返回给用户空间的数据缓冲区
  102. * @param cnt 要读取的数据长度
  103. * @param offt 相对于文件首地址的偏移
  104. * @return 0 成功;其他 失败
  105. */
  106. static ssize_t rc522_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
  107. {
  108. }
  109. /**
  110. * @brief 向设备写数据
  111. *
  112. * @param filp 设备文件,表示打开的文件描述符
  113. * @param buf 要写给设备写入的数据
  114. * @param cnt 要写入的数据长度
  115. * @param offt 相对于文件首地址的偏移
  116. * @return 写入的字节数,如果为负值,表示写入失败
  117. */
  118. static ssize_t rc522_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
  119. {
  120. }
  121. /**
  122. * @brief 关闭/释放设备
  123. *
  124. * @param filp 要关闭的设备文件(文件描述符)
  125. * @return 0 成功;其他 失败
  126. */
  127. static int rc522_release(struct inode *inode, struct file *filp)
  128. {
  129. }
  130. /* 设备操作函数结构体 */
  131. static struct file_operations rc522_ops = {
  132. .owner = THIS_MODULE,
  133. .open = rc522_open,
  134. .read = rc522_read,
  135. .write = rc522_write,
  136. .release = rc522_release,
  137. };
  138. /**
  139. * @brief spi 驱动的 probe 函数,当驱动与设备匹配以后此函数就会执行
  140. * @param client spi 设备
  141. * @param id spi 设备 ID
  142. * @return 0,成功;其他负值,失败
  143. */
  144. static int rc522_probe(struct spi_device *spi)
  145. {
  146. int ret = -1; // 保存错误状态码
  147. struct rc522_dev_s *rc522_dev; // 设备数据结构体
  148. /*---------------------注册字符设备驱动-----------------*/
  149. /* 驱动与总线设备匹配成功 */
  150. pr_info("\t %s match successed \r\n", spi->modalias);
  151. // dev_info(&spi->dev, "match successed\n");
  152. /* 申请内存并与 client->dev 进行绑定。*/
  153. /* 在 probe 函数中使用时,当设备驱动被卸载,该内存被自动释放,也可使用 devm_kfree() 函数直接释放 */
  154. rc522_dev = devm_kzalloc(&spi->dev, sizeof(*rc522_dev), GFP_KERNEL);
  155. if(!rc522_dev)
  156. {
  157. pr_err("Failed to request memory \r\n");
  158. return -ENOMEM;
  159. }
  160. /* 1、创建设备号 */
  161. /* 采用动态分配的方式,获取设备编号,次设备号为0 */
  162. /* 设备名称为 SPI_NAME,可通过命令 cat /proc/devices 查看 */
  163. /* RC522_CNT 为1,只申请一个设备编号 */
  164. ret = alloc_chrdev_region(&rc522_dev->devid, 0, RC522_CNT, RC522_NAME);
  165. if (ret < 0)
  166. {
  167. pr_err("%s Couldn't alloc_chrdev_region, ret = %d \r\n", RC522_NAME, ret);
  168. return -ENOMEM;
  169. }
  170. /* 2、初始化 cdev */
  171. /* 关联字符设备结构体 cdev 与文件操作结构体 file_operations */
  172. rc522_dev->cdev.owner = THIS_MODULE;
  173. cdev_init(&rc522_dev->cdev, &rc522_ops);
  174. /* 3、添加一个 cdev */
  175. /* 添加设备至cdev_map散列表中 */
  176. ret = cdev_add(&rc522_dev->cdev, rc522_dev->devid, RC522_CNT);
  177. if (ret < 0)
  178. {
  179. pr_err("fail to add cdev \r\n");
  180. goto del_unregister;
  181. }
  182. /* 4、创建类 */
  183. rc522_dev->class = class_create(THIS_MODULE, RC522_NAME);
  184. if (IS_ERR(rc522_dev->class))
  185. {
  186. pr_err("Failed to create device class \r\n");
  187. goto del_cdev;
  188. }
  189. /* 5、创建设备,设备名是 RC522_NAME */
  190. /*创建设备 RC522_NAME 指定设备名,*/
  191. rc522_dev->device = device_create(rc522_dev->class, NULL, rc522_dev->devid, NULL, RC522_NAME);
  192. if (IS_ERR(rc522_dev->device)) {
  193. goto destroy_class;
  194. }
  195. rc522_dev->spi = spi;
  196. /*初始化 rc522_device */
  197. spi->mode = SPI_MODE_0; /*MODE0,CPOL=0,CPHA=0*/
  198. spi_setup(spi);
  199. /* 保存 rc522_dev 结构体 */
  200. spi_set_drvdata(spi, rc522_dev);
  201. return 0;
  202. destroy_class:
  203. device_destroy(rc522_dev->class, rc522_dev->devid);
  204. del_cdev:
  205. cdev_del(&rc522_dev->cdev);
  206. del_unregister:
  207. unregister_chrdev_region(rc522_dev->devid, RC522_CNT);
  208. return -EIO;
  209. }
  210. /**
  211. * @brief spi 驱动的 remove 函数,移除 spi 驱动的时候此函数会执行
  212. * @param client spi 设备
  213. * @return 0,成功;其他负值,失败
  214. */
  215. static int rc522_remove(struct spi_device *spi)
  216. {
  217. struct rc522_dev_s *rc522_dev = spi_get_drvdata(spi);
  218. /*---------------------注销字符设备驱动-----------------*/
  219. /* 1、删除 cdev */
  220. cdev_del(&rc522_dev->cdev);
  221. /* 2、注销设备号 */
  222. unregister_chrdev_region(rc522_dev->devid, RC522_CNT);
  223. /* 3、注销设备 */
  224. device_destroy(rc522_dev->class, rc522_dev->devid);
  225. /* 4、注销类 */
  226. class_destroy(rc522_dev->class);
  227. return 0;
  228. }
  229. /* 传统匹配方式 ID 列表 */
  230. static const struct spi_device_id gtp_device_id[] = {
  231. {"rfid,rfid_rc522", 0},
  232. {}
  233. };
  234. /* 设备树匹配表 */
  235. static const struct of_device_id rc522_of_match_table[] = {
  236. {.compatible = "rfid,rfid_rc522"},
  237. {/* sentinel */}
  238. };
  239. /* SPI 驱动结构体 */
  240. static struct spi_driver rc522_driver = {
  241. .probe = rc522_probe,
  242. .remove = rc522_remove,
  243. .id_table = gtp_device_id,
  244. .driver = {
  245. .name = "rfid,rfid_rc522",
  246. .owner = THIS_MODULE,
  247. .of_match_table = rc522_of_match_table,
  248. },
  249. };
  250. /**
  251. * @brief 驱动入口函数
  252. * @return 0,成功;其他负值,失败
  253. */
  254. static int __init rc522_driver_init(void)
  255. {
  256. int ret;
  257. pr_info("spi_driver_init\n");
  258. ret = spi_register_driver(&rc522_driver);
  259. return ret;
  260. }
  261. /**
  262. * @brief 驱动出口函数
  263. * @return 0,成功;其他负值,失败
  264. */
  265. static void __exit rc522_driver_exit(void)
  266. {
  267. pr_info("spi_driver_exit\n");
  268. spi_unregister_driver(&rc522_driver);
  269. }
  270. /* 将上面两个函数指定为驱动的入口和出口函数 */
  271. module_init(rc522_driver_init);
  272. module_exit(rc522_driver_exit);
  273. /* LICENSE 和作者信息 */
  274. MODULE_LICENSE("GPL");
  275. MODULE_AUTHOR("JIAOZHU");
  276. MODULE_INFO(intree, "Y");

注意:在不确认是驱动还是SPI从设备的问题时,可以通过下面函数进行简单测试,测试时只需要将发送和接收引脚短接,就可以直接读回发送的数据。

  1. /**
  2. * @brief 向 spi 设备中同时读写多个寄存器数据
  3. *
  4. * @param spi spi 设备
  5. * @param reg 要写入的寄存器首地址
  6. * @param write_buf 要写入的数据缓冲区
  7. * @param read_buf 要读取的数据缓冲区
  8. * @param len 要写入的数据长度
  9. * @return 返回执行结果
  10. */
  11. static s32 spi_read_write_regs(struct spi_device *spi, u8 reg, u8 *write_buf, u8 *read_buf, u8 len)
  12. {
  13. int ret = -1;
  14. unsigned char *txdata;
  15. struct spi_message msg;
  16. struct spi_transfer *trf;
  17. unsigned char * rxdata;
  18. /* 申请内存*/
  19. trf = kzalloc(sizeof(struct spi_transfer), GFP_KERNEL);
  20. if(!trf) {
  21. return -ENOMEM;
  22. }
  23. txdata = kzalloc(sizeof(char)+len, GFP_KERNEL);
  24. if(!txdata) {
  25. goto out1;
  26. }
  27. /* 申请内存 */
  28. rxdata = kzalloc(sizeof(char) * len, GFP_KERNEL);
  29. if(!rxdata) {
  30. goto out1;
  31. }
  32. /* 一共发送 len+1 个字节的数据,第一个字节为寄存器首地址,len 为要写入的寄存器的集合,*/
  33. *txdata = reg & ~0x80;
  34. memcpy(txdata+1, write_buf, len); /* 把 len 个数据拷贝到 txdata 里 */
  35. trf->tx_buf = txdata; /* 要发送的数据 */
  36. trf->rx_buf = rxdata; /* 要读取的数据 */
  37. trf->len = len+1; /* trf->len = 发送的长度+读取的长度 */
  38. spi_message_init(&msg); /* 初始化 spi_message */
  39. spi_message_add_tail(trf, &msg);/*添加到 spi_message 队列 */
  40. ret = spi_sync(spi, &msg); /* 同步发送 */
  41. if(ret) {
  42. goto out2;
  43. }
  44. memcpy(read_buf , rxdata+1, len); /* 只需要读取的数据 */
  45. out2:
  46. kfree(txdata); /* 释放内存 */
  47. kfree(rxdata); /* 释放内存 */
  48. out1:
  49. kfree(trf); /* 释放内存 */
  50. return ret;
  51. }

四、RC522 头文件

  1. /**
  2. * @file rc522_device.h
  3. *
  4. */
  5. #ifndef _RC522_DEVICE_H_
  6. #define _RC522_DEVICE_H_
  7. /*********************
  8. * INCLUDES
  9. *********************/
  10. // #include <stdbool.h>
  11. /*********************
  12. * DEFINES
  13. *********************/
  14. /* MF522 FIFO长度定义 */
  15. #define DEF_FIFO_LENGTH (64) // FIFO size=64byte
  16. /* MF522命令字 */
  17. #define PCD_IDLE (0x00) // 取消当前命令
  18. #define PCD_AUTHENT (0x0E) // 验证密钥
  19. #define PCD_RECEIVE (0x08) // 接收数据
  20. #define PCD_TRANSMIT (0x04) // 发送数据
  21. #define PCD_TRANSCEIVE (0x0C) // 发送并接收数据
  22. #define PCD_RESETPHASE (0x0F) // 复位
  23. #define PCD_CALCCRC (0x03) // CRC计算
  24. /* Mifare_One卡片命令字 */
  25. #define PICC_REQIDL (0x26) // 寻天线区内未进入休眠状态
  26. #define PICC_REQALL (0x52) // 寻天线区内全部卡
  27. #define PICC_ANTICOLL1 (0x93) // 防冲撞
  28. #define PICC_ANTICOLL2 (0x95) // 防冲撞
  29. #define PICC_AUTHENT1A (0x60) // 验证A密钥
  30. #define PICC_AUTHENT1B (0x61) // 验证B密钥
  31. #define PICC_READ (0x30) // 读块
  32. #define PICC_WRITE (0xA0) // 写块
  33. #define PICC_DECREMENT (0xC0) // 扣款
  34. #define PICC_INCREMENT (0xC1) // 充值
  35. #define PICC_RESTORE (0xC2) // 调块数据到缓冲区
  36. #define PICC_TRANSFER (0xB0) // 保存缓冲区中数据
  37. #define PICC_HALT (0x50) // 休眠
  38. /*------------------------------ MF522寄存器定义 ------------------------------*/
  39. /* PAGE 0 */
  40. #define RFU00 (0x00) // 保留
  41. #define CommandReg (0x01) // 启动和停止
  42. #define ComIEnReg (0x02) // 中断请求传递的使能和失能控制位
  43. #define DivlEnReg (0x03) // 中断请求传递的使能和失能控制位
  44. #define ComIrqReg (0x04) // 包含中断请求标志
  45. #define DivIrqReg (0x05) // 包含中断请求标志
  46. #define ErrorReg (0x06) // 错误标志,指示执行的上个命令的错误状态
  47. #define Status1Reg (0x07) // 包含通信的状态标志
  48. #define Status2Reg (0x08) // 包含接收器和发送器的状态标志
  49. #define FIFODataReg (0x09) // 64 字节 FIFO 缓冲区的输入和输出
  50. #define FIFOLevelReg (0x0A) // 指示 FIFO 中存储的字节数
  51. #define WaterLevelReg (0x0B) // 定义 FIFO 下溢和上溢报警的 FIFO 深度
  52. #define ControlReg (0x0C) // 不同的控制寄存器
  53. #define BitFramingReg (0x0D) // 面向位的帧的调节
  54. #define CollReg (0x0E) // RF 接口上检测到的第一位冲突的位的位置
  55. #define RFU0F (0x0F) // 保留
  56. /* PAGE 1 */
  57. #define RFU10 (0x10) // 保留用于未来使用
  58. #define ModeReg (0x11) // 定义发送和接收的常用模式
  59. #define TxModeReg (0x12) // 定义发送过程中的数据传输速率
  60. #define RxModeReg (0x13) // 定义接收过程中的数据传输速率
  61. #define TxControlReg (0x14) // 控制天线驱动管脚 TX1 和 TX2 的逻辑特性
  62. #define TxAutoReg (0x15) // 控制天线驱动器的设置
  63. #define TxSelReg (0x16) // 控制天线驱动器的内部源
  64. #define RxSelReg (0x17) // 选择内部的接收器
  65. #define RxThresholdReg (0x18) // 选择位译码器的阀值
  66. #define DemodReg (0x19) // 定义调节器的设置
  67. #define RFU1A (0x1A) // 保留用于未来使用
  68. #define RFU1B (0x1B) // 保留用于未来使用
  69. #define MifareReg (0x1C) // 控制 ISO 14443/MIFARE 模式中 106kbit/s 的通信
  70. #define RFU1D (0x1D) // 保留用于未来使用
  71. #define RFU1E (0x1E) // 保留用于未来使用
  72. #define SerialSpeedReg (0x1F) // 选择串行 UART 接口的速率
  73. /* PAGE 2 */
  74. #define RFU20 (0x20) // 保留用于未来使用
  75. #define CRCResultRegM (0x21) // 显示 CRC 计算的实际 MSB 值
  76. #define CRCResultRegL (0x22) // 显示 CRC 计算的实际 LSB 值
  77. #define RFU23 (0x23) // 保留用于未来使用
  78. #define ModWidthReg (0x24) // 控制 ModWidth 的设置
  79. #define RFU25 (0x25) // 保留用于未来使用
  80. #define RFCfgReg (0x26) // 配置接收器增益
  81. #define GsNReg (0x27) // 选择天线驱动器管脚 TX1 和 TX2 的调制电导
  82. #define CWGsCfgReg (0x28) // 选择天线驱动器管脚 TX1 和 TX2 的调制电导
  83. #define ModGsCfgReg (0x29) // 选择天线驱动器管脚 TX1 和 TX2 的调制电导
  84. #define TModeReg (0x2A) // 定义内部定时器的设置
  85. #define TPrescalerReg (0x2B) // 定义内部定时器的设置
  86. #define TReloadRegH (0x2C) // 描述 16 位长的定时器重装值
  87. #define TReloadRegL (0x2D) // 描述 16 位长的定时器重装值
  88. #define TCounterValueRegH (0x2E) // 显示 16 位长的实际定时器值
  89. #define TCounterValueRegL (0x2F) // 显示 16 位长的实际定时器值
  90. /* PAGE 3 */
  91. #define RFU30 (0x30) // 保留用于未来使用
  92. #define TestSel1Reg (0x31) // 常用测试信号的配置
  93. #define TestSel2Reg (0x32) // 常用测试信号的配置和 PRBS 控制
  94. #define TestPinEnReg (0x33) // D1-D7 输出驱动器的使能管脚(仅用于串行接口)
  95. #define TestPinValueReg (0x34) // 定义 D1-D7 用作 I/O 总线时的值
  96. #define TestBusReg (0x35) // 显示内部测试总线的状态
  97. #define AutoTestReg (0x36) // 控制数字自测试
  98. #define VersionReg (0x37) // 显示版本
  99. #define AnalogTestReg (0x38) // 控制管脚 AUX1 和 AUX2
  100. #define TestDAC1Reg (0x39) // 定义 TestDAC1 的测试值
  101. #define TestDAC2Reg (0x3A) // 定义 TestDAC2 的测试值
  102. #define TestADCReg (0x3B) // 显示 ADC I 和 Q 通道的实际值
  103. #define RFU3C (0x3C) // 保留用于产品测试
  104. #define RFU3D (0x3D) // 保留用于产品测试
  105. #define RFU3E (0x3E) // 保留用于产品测试
  106. #define RFU3F (0x3F) // 保留用于产品测试
  107. /* 与 RC522 通讯时返回的错误代码 */
  108. #define MI_OK (0) // 正确
  109. #define MI_NOTAGERR (-1) // 未知错误
  110. #define MI_ERR (-2) // 错误
  111. #define MAXRLEN (18)
  112. /* 定义 RC522 驱动的最大数据空间 = 67 * 16 = 1056 */
  113. #define RC522_MAX_OFFSET (0x042F)
  114. /**********************
  115. * TYPEDEFS
  116. **********************/
  117. /**********************
  118. * GLOBAL PROTOTYPES
  119. **********************/
  120. /**********************
  121. * MACROS
  122. **********************/
  123. #endif /* _RC522_DEVICE_H_ */

五、完成程序

为了方便操作,这里的 API 函数都被我修改过的,可以对比其他博客的进行参考,比如这位大佬的博客: https://blog.csdn.net/zhiyuan2021/article/details/128922757

  1. #include <linux/module.h>
  2. #include <linux/cdev.h>
  3. #include <linux/spi/spi.h>
  4. #include <linux/types.h>
  5. #include <linux/ide.h>
  6. #include <linux/errno.h>
  7. #include <linux/device.h>
  8. #include <linux/delay.h>
  9. #include <linux/uaccess.h>
  10. #include "rc522_device.h"
  11. /***************************************************************
  12. 文件名 : spi_rc522_drive.c
  13. 作者 : jiaozhu
  14. 版本 : V1.0
  15. 描述 : RFID-RC522 设备驱动文件
  16. 其他 : 无
  17. 日志 : 初版 V1.0 2023/2/16
  18. ***************************************************************/
  19. /*------------------字符设备内容----------------------*/
  20. #define RC522_NAME "spi_rc522"
  21. #define RC522_CNT (1)
  22. static unsigned char card_type[2]; // 卡片类型
  23. static unsigned char card_id[4]; // 卡片id
  24. static unsigned char card_auth_mode = 0x60; // 密码验证类型
  25. static unsigned char card_cipher[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; // 卡片块密码
  26. struct rc522_dev_s {
  27. struct spi_device *spi; // spi 设备
  28. dev_t devid; // 设备号
  29. struct cdev cdev; // cdev
  30. struct class *class; // 类
  31. struct device *device; // 设备
  32. struct device_node *node; // 设备节点
  33. };
  34. /* 声明 SPI 操作函数 */
  35. static s32 spi_write_regs(struct spi_device *spi, u8 reg, u8 *buf, u8 len);
  36. static int spi_read_regs(struct spi_device *spi, u8 reg, void *buf, int len);
  37. /**
  38. * @brief 向 rc522 设备的寄存器中写入 8 位数据
  39. *
  40. * @param rc522_dev rc522 设备
  41. * @param reg 寄存器地址
  42. * @param val 写入的值
  43. * @return 返回执行的结果
  44. */
  45. static int rc522_write_reg8(struct rc522_dev_s *rc522_dev, u8 reg, u8 value)
  46. {
  47. u8 buf = value;
  48. return spi_write_regs(rc522_dev->spi, (reg << 1) & 0x7E, &buf, 1);
  49. }
  50. /**
  51. * @brief 从 rc522 设备的寄存器中读取 8 位数据
  52. *
  53. * @param rc522_dev rc522 设备
  54. * @param reg 寄存器地址
  55. * @param buf 读取的缓冲区
  56. * @return 返回执行的结果
  57. */
  58. static int rc522_read_reg8(struct rc522_dev_s *rc522_dev, u8 reg, u8 *buf)
  59. {
  60. return spi_read_regs(rc522_dev->spi, (reg << 1) & 0x7E, buf, 1);
  61. }
  62. /**
  63. * @brief 置RC522寄存器位
  64. *
  65. * @param rc522_dev rc522 设备
  66. * @param reg 寄存器地址
  67. * @param mask 置位值
  68. * @return 返回执行的结果
  69. */
  70. static int rc522_set_bit_mask(struct rc522_dev_s *rc522_dev, u8 reg, u8 mask)
  71. {
  72. int res = 0;
  73. u8 tmp = 0x0;
  74. res = rc522_read_reg8(rc522_dev, reg, &tmp);
  75. if (0 != res)
  76. {
  77. return MI_NOTAGERR;
  78. }
  79. rc522_write_reg8(rc522_dev, reg, tmp | mask); // set bit mask
  80. return MI_OK;
  81. }
  82. /**
  83. * @brief 清RC522寄存器位
  84. *
  85. * @param rc522_dev rc522 设备
  86. * @param reg 寄存器地址
  87. * @param mask 清位值
  88. * @return 返回执行的结果
  89. */
  90. static int rc522_clear_bit_mask(struct rc522_dev_s *rc522_dev, u8 reg, u8 mask)
  91. {
  92. int res = 0;
  93. u8 tmp = 0x0;
  94. res = rc522_read_reg8(rc522_dev, reg, &tmp);
  95. if (0 != res)
  96. {
  97. return MI_NOTAGERR;
  98. }
  99. rc522_write_reg8(rc522_dev, reg, tmp & ~mask); // set bit mask
  100. return MI_OK;
  101. }
  102. /**
  103. * @brief 用 RC522 计算 CRC16 函数
  104. *
  105. * @param rc522_dev rc522 设备
  106. * @param pIndata 需要计算的数据
  107. * @param len 数据长度
  108. * @param pOutData CRC 计算结果
  109. * @return 返回执行的结果
  110. */
  111. static int rc522_calulate_crc(struct rc522_dev_s *rc522_dev, u8 *pIndata, u8 len, u8 *pOutData)
  112. {
  113. u8 i,n;
  114. int res = 0;
  115. rc522_clear_bit_mask(rc522_dev, DivIrqReg, 0x04);
  116. rc522_write_reg8(rc522_dev, CommandReg, PCD_IDLE);
  117. rc522_set_bit_mask(rc522_dev, FIFOLevelReg, 0x80);
  118. for (i=0; i<len; i++)
  119. {
  120. rc522_write_reg8(rc522_dev, FIFODataReg, *(pIndata+i));
  121. }
  122. rc522_write_reg8(rc522_dev, CommandReg, PCD_CALCCRC);
  123. i = 0xFF;
  124. do
  125. {
  126. res = rc522_read_reg8(rc522_dev, DivIrqReg, &n);
  127. i--;
  128. }
  129. while ((i != 0) && !( n & 0x04));
  130. res |= rc522_read_reg8(rc522_dev, CRCResultRegL, &pOutData[0]);
  131. res |= rc522_read_reg8(rc522_dev, CRCResultRegM, &pOutData[1]);
  132. return res;
  133. }
  134. /**
  135. * @brief 通过RC522和ISO14443卡通讯
  136. *
  137. * @param rc522_dev rc522 设备
  138. * @param Command RC522 命令字
  139. * @param pInData 通过 RC522 发送到卡片的数据
  140. * @param InLenByte 发送数据的字节长度
  141. * @param pOutData 接收到的卡片返回数据
  142. * @param pOutLenBit 返回数据的位长度
  143. * @return 返回执行的结果
  144. */
  145. static int rc522_com_card(struct rc522_dev_s *rc522_dev, u8 Command, u8 *pInData, u8 InLenByte, u8 *pOutData, u32 *pOutLenBit)
  146. {
  147. int status = MI_ERR;
  148. u8 irqEn = 0x00;
  149. u8 waitFor = 0x00;
  150. u8 lastBits;
  151. u8 n;
  152. u32 i;
  153. switch (Command)
  154. {
  155. case PCD_AUTHENT:
  156. irqEn = 0x12;
  157. waitFor = 0x10;
  158. break;
  159. case PCD_TRANSCEIVE:
  160. irqEn = 0x77;
  161. waitFor = 0x30;
  162. break;
  163. default:
  164. break;
  165. }
  166. rc522_write_reg8(rc522_dev, ComIEnReg, irqEn|0x80);
  167. rc522_clear_bit_mask(rc522_dev, ComIrqReg, 0x80);
  168. rc522_write_reg8(rc522_dev, CommandReg, PCD_IDLE);
  169. rc522_set_bit_mask(rc522_dev, FIFOLevelReg, 0x80);
  170. for (i = 0; i < InLenByte; i++)
  171. {
  172. rc522_write_reg8(rc522_dev, FIFODataReg, pInData[i]);
  173. }
  174. rc522_write_reg8(rc522_dev, CommandReg, Command);
  175. if (Command == PCD_TRANSCEIVE)
  176. {
  177. rc522_set_bit_mask(rc522_dev, BitFramingReg, 0x80);
  178. }
  179. /* 根据时钟频率调整,操作 M1 卡最大等待时间25ms */
  180. i = 2000;
  181. do
  182. {
  183. status = rc522_read_reg8(rc522_dev, ComIrqReg, &n);
  184. i--;
  185. }
  186. while ((i != 0) && !(n & 0x01) && !(n & waitFor));
  187. rc522_clear_bit_mask(rc522_dev, BitFramingReg, 0x80);
  188. if (i !=0 )
  189. {
  190. status = rc522_read_reg8(rc522_dev, ErrorReg, &n);
  191. if(!(n & 0x1B))
  192. {
  193. status = MI_OK;
  194. if (n & irqEn & 0x01)
  195. {
  196. status = MI_NOTAGERR;
  197. }
  198. if (Command == PCD_TRANSCEIVE)
  199. {
  200. status = rc522_read_reg8(rc522_dev, FIFOLevelReg, &n);
  201. status = rc522_read_reg8(rc522_dev, ControlReg, &lastBits);
  202. lastBits = lastBits & 0x07;
  203. if (lastBits)
  204. {
  205. *pOutLenBit = (n-1)*8 + lastBits; }
  206. else
  207. {
  208. *pOutLenBit = n * 8;
  209. }
  210. if (n == 0)
  211. {
  212. n = 1;
  213. }
  214. if (n > MAXRLEN)
  215. {
  216. n = MAXRLEN;
  217. }
  218. for (i=0; i<n; i++)
  219. {
  220. status = rc522_read_reg8(rc522_dev, FIFODataReg, &pOutData[i]);
  221. }
  222. }
  223. }
  224. else
  225. {
  226. status = MI_ERR;
  227. }
  228. }
  229. rc522_set_bit_mask(rc522_dev, ControlReg, 0x80);
  230. rc522_write_reg8(rc522_dev, CommandReg, PCD_IDLE);
  231. return status;
  232. }
  233. /**
  234. * @brief 关闭天线
  235. *
  236. * @param rc522_dev rc522 设备
  237. * @return 返回执行的结果
  238. */
  239. static int rc522_antenna_off(struct rc522_dev_s *rc522_dev)
  240. {
  241. return rc522_clear_bit_mask(rc522_dev, TxControlReg, 0x03);
  242. }
  243. /**
  244. * @brief 开启天线
  245. *
  246. * @param rc522_dev rc522 设备, 每次启动或关闭天险发射之间应至少有1ms的间隔
  247. * @return 返回执行的结果
  248. */
  249. static int rc522_antenna_on(struct rc522_dev_s *rc522_dev)
  250. {
  251. u8 tmp = 0x0;
  252. tmp = rc522_read_reg8(rc522_dev, TxControlReg, &tmp);
  253. if (!(tmp & 0x03))
  254. {
  255. return rc522_set_bit_mask(rc522_dev, TxControlReg, 0x03);
  256. }
  257. return MI_OK;
  258. }
  259. /**
  260. * @brief 设置 RC522 的工作方式
  261. *
  262. * @param rc522_dev rc522 设备
  263. * @param type 工作模式,新增模式时,建议通过枚举
  264. * @return 返回执行的结果
  265. */
  266. static int rc522_config_iso_type(struct rc522_dev_s *rc522_dev, u8 type)
  267. {
  268. int res = MI_OK;
  269. switch (type)
  270. {
  271. case 1:
  272. res = rc522_clear_bit_mask(rc522_dev, Status2Reg, 0x08);
  273. res |= rc522_write_reg8(rc522_dev, ModeReg,0x3D);
  274. res |= rc522_write_reg8(rc522_dev, TxSelReg,0x10);
  275. res |= rc522_write_reg8(rc522_dev, RxSelReg,0x86);
  276. res |= rc522_write_reg8(rc522_dev, RFCfgReg,0x7F);
  277. res |= rc522_write_reg8(rc522_dev, TReloadRegL,30);
  278. res |= rc522_write_reg8(rc522_dev, TReloadRegH,0);
  279. res |= rc522_write_reg8(rc522_dev, TModeReg,0x8D);
  280. res |= rc522_write_reg8(rc522_dev, TPrescalerReg,0x3E);
  281. msleep (1);
  282. res |= rc522_antenna_on(rc522_dev);
  283. break;
  284. default:
  285. res = MI_NOTAGERR;
  286. break;
  287. }
  288. return res;
  289. }
  290. /**
  291. * @brief 复位RC522
  292. *
  293. * @param rc522_dev rc522 设备
  294. * @return 返回执行的结果
  295. */
  296. static int rc522_reset_dev(struct rc522_dev_s *rc522_dev)
  297. {
  298. int ret = MI_OK;
  299. /* RC522 启动并复位 */
  300. ret = rc522_write_reg8(rc522_dev, CommandReg, PCD_RESETPHASE);
  301. ret |= rc522_write_reg8(rc522_dev, ModeReg, 0x3D);
  302. ret |= rc522_write_reg8(rc522_dev, TReloadRegL, 30);
  303. ret |= rc522_write_reg8(rc522_dev, TReloadRegH, 0);
  304. ret |= rc522_write_reg8(rc522_dev, TModeReg, 0x8D);
  305. ret |= rc522_write_reg8(rc522_dev, TPrescalerReg, 0x3E);
  306. ret |= rc522_write_reg8(rc522_dev, TxAutoReg, 0x40);
  307. return MI_OK;
  308. }
  309. /**
  310. * @brief RC522 寻卡
  311. *
  312. * @param rc522_dev rc522 设备
  313. * @param req_code 寻卡方式,
  314. * 0x52 = 寻感应区内所有符合14443A标准的卡
  315. * 0x26 = 寻未进入休眠状态的卡
  316. * @param pTagType 卡片类型代码
  317. * 0x4400 = Mifare_UltraLight
  318. * 0x0400 = Mifare_One(S50)
  319. * 0x0200 = Mifare_One(S70)
  320. * 0x0800 = Mifare_Pro(X)
  321. * 0x4403 = Mifare_DESFire
  322. * @return 返回执行的结果
  323. */
  324. static int rc522_request_card(struct rc522_dev_s *rc522_dev, u8 req_code, u8 *pTagType)
  325. {
  326. int status;
  327. unsigned int unLen;
  328. unsigned char ucComMF522Buf[MAXRLEN];
  329. rc522_clear_bit_mask(rc522_dev, Status2Reg, 0x08);
  330. rc522_write_reg8(rc522_dev, BitFramingReg, 0x07);
  331. rc522_set_bit_mask(rc522_dev, TxControlReg, 0x03);
  332. ucComMF522Buf[0] = req_code;
  333. status = rc522_com_card(rc522_dev, PCD_TRANSCEIVE, ucComMF522Buf, 1, ucComMF522Buf, &unLen);
  334. if ((status == MI_OK) && (unLen == 0x10))
  335. {
  336. *pTagType = ucComMF522Buf[0];
  337. *(pTagType+1) = ucComMF522Buf[1];
  338. }
  339. else
  340. {
  341. status = MI_ERR;
  342. }
  343. return status;
  344. }
  345. /**
  346. * @brief RC522 防冲撞
  347. *
  348. * @param rc522_dev rc522 设备
  349. * @param pSnr 卡片序列号,4字节
  350. * @return 返回执行的结果
  351. */
  352. static int rcc_anticoll_card(struct rc522_dev_s *rc522_dev, u8 *pSnr)
  353. {
  354. int status;
  355. unsigned char i, snr_check=0;
  356. unsigned int unLen;
  357. unsigned char ucComMF522Buf[MAXRLEN];
  358. rc522_clear_bit_mask(rc522_dev, Status2Reg, 0x08);
  359. rc522_write_reg8(rc522_dev, BitFramingReg, 0x00);
  360. rc522_clear_bit_mask(rc522_dev, CollReg, 0x80);
  361. ucComMF522Buf[0] = PICC_ANTICOLL1;
  362. ucComMF522Buf[1] = 0x20;
  363. status = rc522_com_card(rc522_dev, PCD_TRANSCEIVE, ucComMF522Buf, 2, ucComMF522Buf, &unLen);
  364. if (status == MI_OK)
  365. {
  366. for (i=0; i<4; i++)
  367. {
  368. *(pSnr+i) = ucComMF522Buf[i];
  369. snr_check ^= ucComMF522Buf[i];
  370. }
  371. if (snr_check != ucComMF522Buf[i])
  372. { status = MI_ERR; }
  373. }
  374. rc522_set_bit_mask(rc522_dev, CollReg, 0x80);
  375. return status;
  376. }
  377. /**
  378. * @brief RC522 选定卡片
  379. *
  380. * @param rc522_dev rc522 设备
  381. * @param pSnr 卡片序列号,4字节
  382. * @return 返回执行的结果
  383. */
  384. static int rc522_select_card(struct rc522_dev_s *rc522_dev, u8 *pSnr)
  385. {
  386. char status;
  387. unsigned char i;
  388. unsigned int unLen;
  389. unsigned char ucComMF522Buf[MAXRLEN];
  390. ucComMF522Buf[0] = PICC_ANTICOLL1;
  391. ucComMF522Buf[1] = 0x70;
  392. ucComMF522Buf[6] = 0;
  393. for (i=0; i<4; i++)
  394. {
  395. ucComMF522Buf[i+2] = *(pSnr+i);
  396. ucComMF522Buf[6] ^= *(pSnr+i);
  397. }
  398. rc522_calulate_crc(rc522_dev, ucComMF522Buf, 7, &ucComMF522Buf[7]);
  399. rc522_clear_bit_mask(rc522_dev, Status2Reg, 0x08);
  400. status = rc522_com_card(rc522_dev, PCD_TRANSCEIVE, ucComMF522Buf, 9, ucComMF522Buf, &unLen);
  401. if ((status == MI_OK) && (unLen == 0x18))
  402. {
  403. status = MI_OK;
  404. }
  405. else
  406. {
  407. status = MI_ERR;
  408. }
  409. return status;
  410. }
  411. /**
  412. * @brief RC522 验证卡片密码
  413. *
  414. * @param rc522_dev rc522 设备
  415. * @param auth_mode 密码验证模式,0x60 = 验证A密钥,0x61 = 验证B密钥
  416. * @param addr 块地址
  417. * @param pKey 密码
  418. * @param pSnr 卡片序列号,4字节
  419. * @return 返回执行的结果
  420. */
  421. static int rc522_auth_state(struct rc522_dev_s *rc522_dev, u8 auth_mode, u8 addr, u8 *pKey, u8 *pSnr)
  422. {
  423. int status;
  424. unsigned int unLen;
  425. unsigned char i,ucComMF522Buf[MAXRLEN];
  426. u8 temp;
  427. ucComMF522Buf[0] = auth_mode;
  428. ucComMF522Buf[1] = addr;
  429. for (i=0; i<6; i++)
  430. {
  431. ucComMF522Buf[i+2] = *(pKey+i);
  432. }
  433. for (i=0; i<6; i++)
  434. {
  435. ucComMF522Buf[i+8] = *(pSnr+i);
  436. }
  437. status = rc522_com_card(rc522_dev, PCD_AUTHENT, ucComMF522Buf, 12, ucComMF522Buf, &unLen);
  438. rc522_read_reg8(rc522_dev, Status2Reg, &temp);
  439. if ((status != MI_OK) || (!(temp & 0x08)))
  440. {
  441. status = MI_ERR;
  442. }
  443. return status;
  444. }
  445. /**
  446. * @brief 读取 RC522 卡的一块数据
  447. *
  448. * @param rc522_dev rc522 设备
  449. * @param addr 块地址
  450. * @param pData 读出的数据,16字节
  451. * @return 返回执行的结果
  452. */
  453. static int rc522_read_card(struct rc522_dev_s *rc522_dev, u8 addr, u8 *pData)
  454. {
  455. char status;
  456. unsigned int unLen;
  457. unsigned char i, ucComMF522Buf[MAXRLEN];
  458. ucComMF522Buf[0] = PICC_READ;
  459. ucComMF522Buf[1] = addr;
  460. rc522_calulate_crc(rc522_dev, ucComMF522Buf, 2 , &ucComMF522Buf[2]);
  461. status = rc522_com_card(rc522_dev, PCD_TRANSCEIVE, ucComMF522Buf, 4, ucComMF522Buf, &unLen);
  462. if ((status == MI_OK) && (unLen == 0x90))
  463. {
  464. for (i=0; i<16; i++)
  465. {
  466. *(pData+i) = ucComMF522Buf[i];
  467. }
  468. }
  469. else
  470. {
  471. status = MI_ERR;
  472. }
  473. return status;
  474. }
  475. /**
  476. * @brief 写入 RC522 卡的一块数据
  477. *
  478. * @param rc522_dev rc522 设备
  479. * @param addr 块地址
  480. * @param pData 读出的数据,16字节
  481. * @return 返回执行的结果
  482. */
  483. static int rc522_write_card(struct rc522_dev_s *rc522_dev, u8 addr, u8 *pData)
  484. {
  485. char status;
  486. unsigned int unLen;
  487. unsigned char i, ucComMF522Buf[MAXRLEN];
  488. ucComMF522Buf[0] = PICC_WRITE;
  489. ucComMF522Buf[1] = addr;
  490. rc522_calulate_crc(rc522_dev, ucComMF522Buf, 2, &ucComMF522Buf[2]);
  491. status = rc522_com_card(rc522_dev, PCD_TRANSCEIVE, ucComMF522Buf, 4, ucComMF522Buf, &unLen);
  492. if ((status != MI_OK) || (unLen != 4) || ((ucComMF522Buf[0] & 0x0F) != 0x0A))
  493. {
  494. status = MI_ERR;
  495. }
  496. if (status == MI_OK)
  497. {
  498. for (i = 0; i < 16; i++)
  499. {
  500. ucComMF522Buf[i] = *(pData + i);
  501. }
  502. rc522_calulate_crc(rc522_dev, ucComMF522Buf, 16, &ucComMF522Buf[16]);
  503. status = rc522_com_card(rc522_dev, PCD_TRANSCEIVE, ucComMF522Buf, 18, ucComMF522Buf, &unLen);
  504. if ((status != MI_OK) || (unLen != 4) || ((ucComMF522Buf[0] & 0x0F) != 0x0A))
  505. {
  506. status = MI_ERR;
  507. }
  508. }
  509. return status;
  510. }
  511. /**
  512. * @brief RC522 命令卡片进入休眠状态
  513. *
  514. * @param rc522_dev rc522 设备
  515. * @return 返回执行的结果
  516. */
  517. static int rc522_halt_card(struct rc522_dev_s *rc522_dev)
  518. {
  519. char status;
  520. unsigned int unLen;
  521. unsigned char ucComMF522Buf[MAXRLEN];
  522. ucComMF522Buf[0] = PICC_HALT;
  523. ucComMF522Buf[1] = 0;
  524. rc522_calulate_crc(rc522_dev, ucComMF522Buf, 2, &ucComMF522Buf[2]);
  525. status = rc522_com_card(rc522_dev, PCD_TRANSCEIVE, ucComMF522Buf, 4, ucComMF522Buf, &unLen);
  526. if ((status != MI_OK))
  527. {
  528. return MI_ERR;
  529. }
  530. return MI_OK;
  531. }
  532. /**
  533. * @brief 向 spi 设备中写入多个寄存器数据
  534. *
  535. * @param spi spi 设备
  536. * @param reg 要写入的寄存器首地址
  537. * @param buf 要写入的数据缓冲区
  538. * @param len 要写入的数据长度
  539. * @return 返回执行结果
  540. */
  541. static s32 spi_write_regs(struct spi_device *spi, u8 reg, u8 *buf, u8 len)
  542. {
  543. int ret = -1;
  544. unsigned char *txdata;
  545. struct spi_message msg;
  546. struct spi_transfer *trf;
  547. /* 申请内存*/
  548. trf = kzalloc(sizeof(struct spi_transfer), GFP_KERNEL);
  549. if(!trf) {
  550. return -ENOMEM;
  551. }
  552. txdata = kzalloc(sizeof(char)+len, GFP_KERNEL);
  553. if(!txdata) {
  554. goto out1;
  555. }
  556. /* 一共发送 len+1 个字节的数据,第一个字节为寄存器首地址,len 为要写入的寄存器的集合,*/
  557. *txdata = reg & ~0x80; /* 写数据的时候首寄存器地址 bit8 要清零 */
  558. memcpy(txdata+1, buf, len); /* 把 len 个数据拷贝到 txdata 里 */
  559. trf->tx_buf = txdata; /* 要发送的数据 */
  560. trf->len = len+1; /* trf->len = 发送的长度+读取的长度 */
  561. spi_message_init(&msg); /* 初始化 spi_message */
  562. spi_message_add_tail(trf, &msg);/*添加到 spi_message 队列 */
  563. ret = spi_sync(spi, &msg); /* 同步发送 */
  564. if(ret) {
  565. goto out2;
  566. }
  567. out2:
  568. kfree(txdata); /* 释放内存 */
  569. out1:
  570. kfree(trf); /* 释放内存 */
  571. return ret;
  572. }
  573. /**
  574. * @brief 读取 spi 的多个寄存器数据
  575. *
  576. * @param spi spi 设备
  577. * @param reg 要读取的寄存器首地址
  578. * @param buf 要读取的数据缓冲区
  579. * @param len 要读取的数据长度
  580. * @return 返回执行结果
  581. */
  582. static int spi_read_regs(struct spi_device *spi, u8 reg, void *buf, int len)
  583. {
  584. int ret = -1;
  585. unsigned char txdata[1];
  586. unsigned char * rxdata;
  587. struct spi_message msg;
  588. struct spi_transfer *trf;
  589. /* 申请内存*/
  590. trf = kzalloc(sizeof(struct spi_transfer), GFP_KERNEL);
  591. if(!trf) {
  592. return -ENOMEM;
  593. }
  594. /* 申请内存 */
  595. rxdata = kzalloc(sizeof(char) * len, GFP_KERNEL);
  596. if(!rxdata) {
  597. goto out1;
  598. }
  599. /* 一共发送 len+1 个字节的数据,第一个字节为寄存器首地址,一共要读取 len 个字节长度的数据,*/
  600. txdata[0] = reg | 0x80; /* 写数据的时候首寄存器地址 bit8 要置 1 */
  601. trf->tx_buf = txdata; /* 要发送的数据 */
  602. trf->rx_buf = rxdata; /* 要读取的数据 */
  603. trf->len = len+1; /* trf->len = 发送的长度+读取的长度 */
  604. spi_message_init(&msg); /* 初始化 spi_message */
  605. spi_message_add_tail(trf, &msg);/* 将 spi_transfer 添加到 spi_message*/
  606. ret = spi_sync(spi, &msg); /* 同步发送 */
  607. if(ret) {
  608. goto out2;
  609. }
  610. memcpy(buf , rxdata+1, len); /* 只需要读取的数据 */
  611. out2:
  612. kfree(rxdata); /* 释放内存 */
  613. out1:
  614. kfree(trf); /* 释放内存 */
  615. return ret;
  616. }
  617. /**
  618. * @brief 向 spi 设备中同时读写多个寄存器数据
  619. *
  620. * @param spi spi 设备
  621. * @param reg 要写入的寄存器首地址
  622. * @param write_buf 要写入的数据缓冲区
  623. * @param read_buf 要读取的数据缓冲区
  624. * @param len 要写入的数据长度
  625. * @return 返回执行结果
  626. */
  627. static s32 spi_read_write_regs(struct spi_device *spi, u8 reg, u8 *write_buf, u8 *read_buf, u8 len)
  628. {
  629. int ret = -1;
  630. unsigned char *txdata;
  631. struct spi_message msg;
  632. struct spi_transfer *trf;
  633. unsigned char * rxdata;
  634. /* 申请内存*/
  635. trf = kzalloc(sizeof(struct spi_transfer), GFP_KERNEL);
  636. if(!trf) {
  637. return -ENOMEM;
  638. }
  639. txdata = kzalloc(sizeof(char)+len, GFP_KERNEL);
  640. if(!txdata) {
  641. goto out1;
  642. }
  643. /* 申请内存 */
  644. rxdata = kzalloc(sizeof(char) * len, GFP_KERNEL);
  645. if(!rxdata) {
  646. goto out1;
  647. }
  648. /* 一共发送 len+1 个字节的数据,第一个字节为寄存器首地址,len 为要写入的寄存器的集合,*/
  649. *txdata = reg & ~0x80;
  650. memcpy(txdata+1, write_buf, len); /* 把 len 个数据拷贝到 txdata 里 */
  651. trf->tx_buf = txdata; /* 要发送的数据 */
  652. trf->rx_buf = rxdata; /* 要读取的数据 */
  653. trf->len = len+1; /* trf->len = 发送的长度+读取的长度 */
  654. spi_message_init(&msg); /* 初始化 spi_message */
  655. spi_message_add_tail(trf, &msg);/*添加到 spi_message 队列 */
  656. ret = spi_sync(spi, &msg); /* 同步发送 */
  657. if(ret) {
  658. goto out2;
  659. }
  660. memcpy(read_buf , rxdata+1, len); /* 只需要读取的数据 */
  661. out2:
  662. kfree(txdata); /* 释放内存 */
  663. kfree(rxdata); /* 释放内存 */
  664. out1:
  665. kfree(trf); /* 释放内存 */
  666. return ret;
  667. }
  668. /**
  669. * @brief 打开设备
  670. *
  671. * @param inode 传递给驱动的 inode
  672. * @param filp 设备文件,file 结构体有个叫做 private_data 的成员变量
  673. * 一般在 open 的时候将 private_data 指向设备结构体。
  674. * @return 0 成功;其他 失败
  675. */
  676. static int rc522_open(struct inode *inode, struct file *filp)
  677. {
  678. u8 value[5];
  679. u8 buf[5] = {0x11, 0x22, 0x33, 0x44, 0x55};
  680. int res = -1;
  681. struct cdev *cdev = filp->f_path.dentry->d_inode->i_cdev;
  682. struct rc522_dev_s *rc522_dev = container_of(cdev, struct rc522_dev_s, cdev);
  683. filp->private_data = rc522_dev;
  684. // pr_info("rc522_open\n");
  685. /* 复位 RC522 */
  686. res = rc522_reset_dev(rc522_dev);
  687. /* 关闭天线 */
  688. res |= rc522_antenna_off(rc522_dev);
  689. msleep (1);
  690. /* 打开天线,天线操作之间需要间隔 1ms */
  691. res |= rc522_antenna_on(rc522_dev);
  692. /* 设置 RC522 的工作模式*/
  693. res |= rc522_config_iso_type(rc522_dev, 1);
  694. if (MI_OK != res)
  695. {
  696. return MI_NOTAGERR;
  697. }
  698. return MI_OK;
  699. rc522_write_reg8(rc522_dev, 0x05, 0xFF);
  700. /* 验证 spi 是否正常工作 */
  701. mutex_lock(&rc522_dev->spi->dev.mutex);
  702. spi_read_write_regs(rc522_dev->spi, 0x01, buf, value, 5);
  703. mutex_unlock(&rc522_dev->spi->dev.mutex);
  704. pr_info("spi read value is: %x %x %x %x %x\n", value[0], value[1], value[2], value[3], value[4]);
  705. }
  706. /**
  707. * @brief 从设备读取数据
  708. *
  709. * @param filp 要打开的设备文件(文件描述符)
  710. * @param buf 返回给用户空间的数据缓冲区
  711. * @param cnt 要读取的数据长度
  712. * @param offt 相对于文件首地址的偏移
  713. * @return 0 成功;其他 失败
  714. */
  715. static ssize_t rc522_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
  716. {
  717. int res = 0;
  718. unsigned char card_data[16];
  719. int read_position = *offt/16; // 用户空间读取的位置
  720. struct rc522_dev_s *rc522_dev = filp->private_data;
  721. /* RC522 只有16个扇区,每个扇区4个块,总共64块 */
  722. if (read_position > 66)
  723. {
  724. return MI_NOTAGERR;
  725. }
  726. /* 寻卡 */
  727. if (64 == read_position)
  728. {
  729. res = rc522_request_card(rc522_dev, 0x26, card_type);
  730. if (MI_OK != res)
  731. {
  732. return MI_NOTAGERR;
  733. }
  734. /* 将卡片 id 拷贝到用户空间中 */
  735. return copy_to_user(buf, &card_type, cnt);
  736. }
  737. /* 防冲撞,读取卡的ID */
  738. if (65 == read_position)
  739. {
  740. res = rcc_anticoll_card(rc522_dev, card_id);
  741. if (MI_OK != res)
  742. {
  743. return MI_NOTAGERR;
  744. }
  745. return copy_to_user(buf, card_id, cnt);
  746. }
  747. /* 读取卡片密码 */
  748. if (66 == read_position)
  749. {
  750. return copy_to_user(buf, card_cipher, cnt);
  751. }
  752. /* 验证卡片密码 */
  753. res = rc522_auth_state(rc522_dev, card_auth_mode, read_position, card_cipher, card_id);
  754. if (MI_OK != res)
  755. {
  756. // pr_info("Verification card password setting error when reading\n");
  757. return MI_NOTAGERR;
  758. }
  759. /* 读取指定块中的数据 */
  760. memset(card_data, 0, sizeof(card_data));
  761. res = rc522_read_card(rc522_dev, read_position, card_data);
  762. if (MI_OK != res)
  763. {
  764. // pr_info("Failed to read card\n");
  765. return MI_NOTAGERR;
  766. }
  767. return copy_to_user(buf, card_data, cnt);
  768. }
  769. /**
  770. * @brief 向设备写数据
  771. *
  772. * @param filp 设备文件,表示打开的文件描述符
  773. * @param buf 要写给设备写入的数据
  774. * @param cnt 要写入的数据长度
  775. * @param offt 相对于文件首地址的偏移
  776. * @return 写入的字节数,如果为负值,表示写入失败
  777. */
  778. static ssize_t rc522_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
  779. {
  780. int res = 0;
  781. unsigned char temp = 0;
  782. unsigned char card_data[16] = {0};
  783. struct rc522_dev_s *rc522_dev = filp->private_data;
  784. int write_position = *offt/16; // 用户空间读取的位置
  785. /* RC522 只有16个扇区,每个扇区4个块,总共64块 */
  786. if (write_position > 66)
  787. {
  788. return MI_NOTAGERR;
  789. }
  790. /* 设置密码验证方式 */
  791. if (64 == write_position)
  792. {
  793. res = copy_from_user(&temp, buf, 1);
  794. if (MI_OK != res)
  795. {
  796. return MI_NOTAGERR;
  797. }
  798. if (temp)
  799. {
  800. /* 验证 B 密钥 */
  801. card_auth_mode = 0x61;
  802. }
  803. else
  804. {
  805. /* 验证 A 密钥 */
  806. card_auth_mode = 0x60;
  807. }
  808. return MI_OK;
  809. }
  810. /* 选择卡片 */
  811. if (65 == write_position)
  812. {
  813. if (cnt > sizeof(card_id))
  814. {
  815. res = copy_from_user(card_id, buf, sizeof(card_id));
  816. }
  817. else
  818. {
  819. res = copy_from_user(card_id, buf, cnt);
  820. }
  821. if (MI_OK != res)
  822. {
  823. return MI_NOTAGERR;
  824. }
  825. /* 选择卡片 */
  826. res = rc522_select_card(rc522_dev, card_id);
  827. if (MI_OK != res)
  828. {
  829. // pr_info("Failed to select card when reading\n");
  830. return MI_NOTAGERR;
  831. }
  832. return MI_OK;
  833. }
  834. /* 设置卡片密码 */
  835. if (66 == write_position)
  836. {
  837. if (cnt > sizeof(card_cipher))
  838. {
  839. return copy_from_user(card_cipher, buf, sizeof(card_cipher));
  840. }
  841. return copy_from_user(card_cipher, buf, cnt);
  842. }
  843. /* 验证卡片密码 */
  844. res = rc522_auth_state(rc522_dev, card_auth_mode, write_position, card_cipher, card_id);
  845. if (MI_OK != res)
  846. {
  847. pr_info("Verification card password setting error when writing\n");
  848. return MI_NOTAGERR;
  849. }
  850. /* 向指定块中写数据 */
  851. memset(card_data, write_position, sizeof(card_data));
  852. if (cnt > sizeof(card_data))
  853. {
  854. res = copy_from_user(card_data, buf, sizeof(card_data));
  855. }
  856. else
  857. {
  858. res = copy_from_user(card_data, buf, cnt);
  859. }
  860. if (MI_OK != res)
  861. {
  862. return MI_NOTAGERR;
  863. }
  864. return rc522_write_card(rc522_dev, 6, card_data);
  865. }
  866. /**
  867. * @brief 关闭/释放设备
  868. *
  869. * @param filp 要关闭的设备文件(文件描述符)
  870. * @return 0 成功;其他 失败
  871. */
  872. static int rc522_release(struct inode *inode, struct file *filp)
  873. {
  874. int res = MI_OK;
  875. struct rc522_dev_s *rc522_dev = filp->private_data;
  876. // pr_info("rc522_release\n");
  877. /* 复位 RC522 */
  878. res = rc522_reset_dev(rc522_dev);
  879. if (MI_OK != res)
  880. {
  881. return MI_NOTAGERR;
  882. }
  883. /* 卡片进入休眠 */
  884. return rc522_halt_card(rc522_dev);
  885. }
  886. /**
  887. * @brief 修改文件读写的偏移位置
  888. *
  889. * @param filp 要关闭的设备文件(文件描述符)
  890. * @param loff_t 偏移位置
  891. * @param whence 文件位置
  892. * @return 0 成功;其他 失败
  893. */
  894. loff_t file_llseek (struct file *filp, loff_t offset, int whence)
  895. {
  896. loff_t new_pos; //新偏移量
  897. loff_t old_pos = filp->f_pos; //旧偏移量
  898. // pr_info("file llseek !\n");
  899. switch(whence){
  900. case SEEK_SET:
  901. new_pos = offset;
  902. break;
  903. case SEEK_CUR:
  904. new_pos = old_pos + offset;
  905. break;
  906. case SEEK_END:
  907. new_pos = RC522_MAX_OFFSET + offset;
  908. break;
  909. default:
  910. printk("error: Unknow whence !\n");
  911. return - EINVAL;
  912. }
  913. /* 偏移量的合法检查 */
  914. if(new_pos < 0 || new_pos > RC522_MAX_OFFSET){
  915. printk("error: Set offset error !\n");
  916. return - EINVAL;
  917. }
  918. filp->f_pos = new_pos;
  919. // printk("The new pos = %lld and offset = %lld!\n", new_pos, offset);
  920. return new_pos; //正确返回新的偏移量
  921. }
  922. /* 设备操作函数结构体 */
  923. static struct file_operations rc522_ops = {
  924. .owner = THIS_MODULE,
  925. .open = rc522_open,
  926. .read = rc522_read,
  927. .write = rc522_write,
  928. .release = rc522_release,
  929. .llseek = file_llseek,
  930. };
  931. /**
  932. * @brief spi 驱动的 probe 函数,当驱动与设备匹配以后此函数就会执行
  933. * @param client spi 设备
  934. * @param id spi 设备 ID
  935. * @return 0,成功;其他负值,失败
  936. */
  937. static int rc522_probe(struct spi_device *spi)
  938. {
  939. int ret = -1; // 保存错误状态码
  940. struct rc522_dev_s *rc522_dev; // 设备数据结构体
  941. /*---------------------注册字符设备驱动-----------------*/
  942. /* 驱动与总线设备匹配成功 */
  943. pr_info("\t %s match successed \r\n", spi->modalias);
  944. // dev_info(&spi->dev, "match successed\n");
  945. /* 申请内存并与 client->dev 进行绑定。*/
  946. /* 在 probe 函数中使用时,当设备驱动被卸载,该内存被自动释放,也可使用 devm_kfree() 函数直接释放 */
  947. rc522_dev = devm_kzalloc(&spi->dev, sizeof(*rc522_dev), GFP_KERNEL);
  948. if(!rc522_dev)
  949. {
  950. pr_err("Failed to request memory \r\n");
  951. return -ENOMEM;
  952. }
  953. /* 1、创建设备号 */
  954. /* 采用动态分配的方式,获取设备编号,次设备号为0 */
  955. /* 设备名称为 SPI_NAME,可通过命令 cat /proc/devices 查看 */
  956. /* RC522_CNT 为1,只申请一个设备编号 */
  957. ret = alloc_chrdev_region(&rc522_dev->devid, 0, RC522_CNT, RC522_NAME);
  958. if (ret < 0)
  959. {
  960. pr_err("%s Couldn't alloc_chrdev_region, ret = %d \r\n", RC522_NAME, ret);
  961. return -ENOMEM;
  962. }
  963. /* 2、初始化 cdev */
  964. /* 关联字符设备结构体 cdev 与文件操作结构体 file_operations */
  965. rc522_dev->cdev.owner = THIS_MODULE;
  966. cdev_init(&rc522_dev->cdev, &rc522_ops);
  967. /* 3、添加一个 cdev */
  968. /* 添加设备至cdev_map散列表中 */
  969. ret = cdev_add(&rc522_dev->cdev, rc522_dev->devid, RC522_CNT);
  970. if (ret < 0)
  971. {
  972. pr_err("fail to add cdev \r\n");
  973. goto del_unregister;
  974. }
  975. /* 4、创建类 */
  976. rc522_dev->class = class_create(THIS_MODULE, RC522_NAME);
  977. if (IS_ERR(rc522_dev->class))
  978. {
  979. pr_err("Failed to create device class \r\n");
  980. goto del_cdev;
  981. }
  982. /* 5、创建设备,设备名是 RC522_NAME */
  983. /*创建设备 RC522_NAME 指定设备名,*/
  984. rc522_dev->device = device_create(rc522_dev->class, NULL, rc522_dev->devid, NULL, RC522_NAME);
  985. if (IS_ERR(rc522_dev->device)) {
  986. goto destroy_class;
  987. }
  988. rc522_dev->spi = spi;
  989. /*初始化 rc522_device */
  990. spi->mode = SPI_MODE_0; /*MODE0,CPOL=0,CPHA=0*/
  991. spi_setup(spi);
  992. /* 保存 rc522_dev 结构体 */
  993. spi_set_drvdata(spi, rc522_dev);
  994. return 0;
  995. destroy_class:
  996. device_destroy(rc522_dev->class, rc522_dev->devid);
  997. del_cdev:
  998. cdev_del(&rc522_dev->cdev);
  999. del_unregister:
  1000. unregister_chrdev_region(rc522_dev->devid, RC522_CNT);
  1001. return -EIO;
  1002. }
  1003. /**
  1004. * @brief spi 驱动的 remove 函数,移除 spi 驱动的时候此函数会执行
  1005. * @param client spi 设备
  1006. * @return 0,成功;其他负值,失败
  1007. */
  1008. static int rc522_remove(struct spi_device *spi)
  1009. {
  1010. struct rc522_dev_s *rc522_dev = spi_get_drvdata(spi);
  1011. /*---------------------注销字符设备驱动-----------------*/
  1012. /* 1、删除 cdev */
  1013. cdev_del(&rc522_dev->cdev);
  1014. /* 2、注销设备号 */
  1015. unregister_chrdev_region(rc522_dev->devid, RC522_CNT);
  1016. /* 3、注销设备 */
  1017. device_destroy(rc522_dev->class, rc522_dev->devid);
  1018. /* 4、注销类 */
  1019. class_destroy(rc522_dev->class);
  1020. return 0;
  1021. }
  1022. /* 传统匹配方式 ID 列表 */
  1023. static const struct spi_device_id gtp_device_id[] = {
  1024. {"rfid,rfid_rc522", 0},
  1025. {}
  1026. };
  1027. /* 设备树匹配表 */
  1028. static const struct of_device_id rc522_of_match_table[] = {
  1029. {.compatible = "rfid,rfid_rc522"},
  1030. {/* sentinel */}
  1031. };
  1032. /* SPI 驱动结构体 */
  1033. static struct spi_driver rc522_driver = {
  1034. .probe = rc522_probe,
  1035. .remove = rc522_remove,
  1036. .id_table = gtp_device_id,
  1037. .driver = {
  1038. .name = "rfid,rfid_rc522",
  1039. .owner = THIS_MODULE,
  1040. .of_match_table = rc522_of_match_table,
  1041. },
  1042. };
  1043. /**
  1044. * @brief 驱动入口函数
  1045. * @return 0,成功;其他负值,失败
  1046. */
  1047. static int __init rc522_driver_init(void)
  1048. {
  1049. int ret;
  1050. pr_info("spi_driver_init\n");
  1051. ret = spi_register_driver(&rc522_driver);
  1052. return ret;
  1053. }
  1054. /**
  1055. * @brief 驱动出口函数
  1056. * @return 0,成功;其他负值,失败
  1057. */
  1058. static void __exit rc522_driver_exit(void)
  1059. {
  1060. pr_info("spi_driver_exit\n");
  1061. spi_unregister_driver(&rc522_driver);
  1062. }
  1063. /* 将上面两个函数指定为驱动的入口和出口函数 */
  1064. module_init(rc522_driver_init);
  1065. module_exit(rc522_driver_exit);
  1066. /* LICENSE 和作者信息 */
  1067. MODULE_LICENSE("GPL");
  1068. MODULE_AUTHOR("JIAOZHU");
  1069. MODULE_INFO(intree, "Y");

六、测试程序

  1. #include "sys/stat.h"
  2. #include <stdio.h>
  3. #include <linux/types.h>
  4. #include <stdlib.h>
  5. #include <fcntl.h>
  6. #include <unistd.h>
  7. #include <sys/types.h>
  8. #include <sys/ioctl.h>
  9. #include <errno.h>
  10. #include <assert.h>
  11. #include <string.h>
  12. #include <linux/i2c.h>
  13. #include <linux/i2c-dev.h>
  14. #include "rc522_device.h"
  15. /***************************************************************
  16. 文件名 : drive_read_app.c
  17. 作者 : jiaozhu
  18. 版本 : V1.0
  19. 描述 : 驱动读取测试
  20. 其他 : 使用方法:./drive_read_app [/dev/xxx]
  21. argv[1] 需要读取的驱动
  22. 日志 : 初版 V1.0 2023/1/4
  23. ***************************************************************/
  24. /**
  25. * @brief main 主程序
  26. * @param argc argv 数组元素个数
  27. * @param argv 具体参数
  28. * @return 0 成功;其他 失败
  29. */
  30. int main(int argc, char *argv[])
  31. {
  32. int fd;
  33. char *filename;
  34. unsigned char card_buf[16];
  35. unsigned char card_id[16];
  36. unsigned char write_buf[2];
  37. int value[18];
  38. int cmd;
  39. int ret = 0;
  40. if(argc != 2){
  41. printf("Error Usage!\r\n");
  42. return -1;
  43. }
  44. filename = argv[1];
  45. /* 打开驱动文件 */
  46. fd = open(filename, O_RDWR);
  47. if(!fd){
  48. printf("Can't open file %s\r\n", filename);
  49. return -1;
  50. }
  51. /* 设置卡片密码 */
  52. lseek(fd, 66*16, SEEK_SET);
  53. memset(card_buf, 0xFF, sizeof(card_buf));
  54. ret = write(fd, card_buf, sizeof(card_buf));
  55. if(ret < 0){
  56. printf("Failed to set integration time...........!\r\n");
  57. }
  58. /* 获取卡片类型 */
  59. //sleep(1);
  60. lseek(fd, 64*16, SEEK_SET);
  61. ret = read(fd, card_buf, sizeof(card_buf));
  62. if (ret == 0)
  63. {
  64. printf("Card type is : (0x%4x)\n", (card_buf[0] << 8) | card_buf[1]);
  65. }
  66. else
  67. {
  68. printf("read file %s failed!\r\n", filename);
  69. }
  70. /* 获取卡片id */
  71. //sleep(1);
  72. lseek(fd, 65*16, SEEK_SET);
  73. ret = read(fd, card_id, sizeof(card_id));
  74. if (ret == 0)
  75. {
  76. printf("card id is : %02x%02x%02x%02x\n", card_id[0], card_id[1], card_id[2], card_id[3]);
  77. }
  78. else
  79. {
  80. printf("read file %s failed!\r\n", filename);
  81. }
  82. /* 选择卡片 */
  83. //sleep(1);
  84. lseek(fd, 65*16, SEEK_SET);
  85. ret = write(fd, card_id, sizeof(card_id));
  86. if(ret < 0){
  87. printf("Failed to select card!\r\n");
  88. }
  89. /* 写数据 */
  90. //sleep(1);
  91. lseek(fd, 4*16, SEEK_SET);
  92. memset(card_buf, 0x58, sizeof(card_buf));
  93. ret = write(fd, card_buf, sizeof(card_buf));
  94. if(ret < 0){
  95. printf("Failed to write card block information\r\n");
  96. }
  97. /* 获取卡片块数据 */
  98. sleep(1);
  99. lseek(fd, 0*16, SEEK_SET);
  100. ret = read(fd, card_buf, sizeof(card_buf));
  101. if (ret == 0)
  102. {
  103. for (int i = 0; i < 16; i++)
  104. {
  105. printf("%02x ", card_buf[i]);
  106. }
  107. printf("\r\n ");
  108. }
  109. else
  110. {
  111. printf("Failed to read card block information");
  112. }
  113. //sleep(1);
  114. close(fd);
  115. return 0;
  116. }

注意:以上程序只供学习使用,还有许多需要完善的地方,这里我就不继续优化了,有需要的小伙伴可以根据自己的需求进行完善即可。

参考链接

MFRC522中文手册:https://www.doc88.com/p-4042994624020.html?r=1

rfid-rc522模块中文资料_驱动模块:https://cloud.tencent.com/developer/article/2152140

【Linux SPI】RFID RC522 设备驱动的更多相关文章

  1. Linux SPI总线和设备驱动架构之四:SPI数据传输的队列化

    我们知道,SPI数据传输可以有两种方式:同步方式和异步方式.所谓同步方式是指数据传输的发起者必须等待本次传输的结束,期间不能做其它事情,用代码来解释就是,调用传输的函数后,直到数据传输完成,函数才会返 ...

  2. Linux SPI总线和设备驱动架构之三:SPI控制器驱动

    通过第一篇文章,我们已经知道,整个SPI驱动架构可以分为协议驱动.通用接口层和控制器驱动三大部分.其中,控制器驱动负责最底层的数据收发工作,为了完成数据的收发工作,控制器驱动需要完成以下这些功能:1. ...

  3. Linux SPI总线和设备驱动架构之二:SPI通用接口层

    通过上一篇文章的介绍,我们知道,SPI通用接口层用于把具体SPI设备的协议驱动和SPI控制器驱动联接在一起,通用接口层除了为协议驱动和控制器驱动提供一系列的标准接口API,同时还为这些接口API定义了 ...

  4. Linux SPI总线和设备驱动架构之一:系统概述

    SPI是"Serial Peripheral Interface" 的缩写,是一种四线制的同步串行通信接口,用来连接微控制器.传感器.存储设备,SPI设备分为主设备和从设备两种,用 ...

  5. Linux SPI总线和设备驱动架构之一:系统概述【转】

    转自:http://blog.csdn.net/droidphone/article/details/23367051/ 版权声明:本文为博主原创文章,未经博主允许不得转载.   目录(?)[-] 硬 ...

  6. Linux学习 : 总线-设备-驱动模型

    platform总线是一种虚拟的总线,相应的设备则为platform_device,而驱动则为platform_driver.Linux 2.6的设备驱动模型中,把I2C.RTC.LCD等都归纳为pl ...

  7. Linux与Windows的设备驱动模型对比

    Linux与Windows的设备驱动模型对比 名词缩写: API 应用程序接口(Application Program Interface ) ABI 应用系统二进制接口(Application Bi ...

  8. 【Linux操作系统分析】设备驱动处理流程

    1 驱动程序,操作系统,文件系统和应用程序之间的关系 字符设备和块设备映射到操作系统中的文件系统,由文件系统向上提供给应用程序统一的接口用以访问设备. Linux把设备视为文件,称为设备文件,通过对设 ...

  9. linux PMBus总线及设备驱动分析

    PMBus协议规范介绍 PMBus是一套对电源进行配置.控制和监控的通讯协议标准.其最新版本为1.3,该规范还在不断演进中,比如新标准中新增的zone PMBus.AVSBus等特性.在其官网上有详细 ...

  10. Linux Platform devices 平台设备驱动

    设备总线驱动模型:http://blog.csdn.net/lizuobin2/article/details/51570196 本文主要参考:http://www.wowotech.net/devi ...

随机推荐

  1. MySQL数据库和Python的交互

    一.缘由 这是之前学习的时候写下的基础代码,包含着MySQL数据库和Python交互的基本操作. 二.代码展示 import pymysql ''' 1.数据库的链接和创建视图 ''' # db=py ...

  2. JS执行机制及ES6

    一.JS执行机制 JS语言有个特点是单线程,即同一时间只能做一件事.单线程就意味着,所有的任务需要排队,前一个任务结束,才会执行后一个任务,可能造成页面渲染不连贯. 为了解决这个问题,利用多核CPU的 ...

  3. sha1_b64_scrape

    过无限debugger:https://www.cnblogs.com/hkwJsxl/p/16702143.html 网站 aHR0cHM6Ly9hbnRpc3BpZGVyOC5zY3JhcGUuY ...

  4. Mybatis-9.28

    Mybatis-9.28 环境: JDK1.8 Mysql 5.7 maven 3.6.1 IDEA 回顾: JDBC Mysql Java基础 Maven Junit SSM框架:配置文件的. 最好 ...

  5. 【JVM故障问题排查心得】「内存诊断系列」Xmx和Xms的大小是小于Docker容器以及Pod的大小的,为啥还是会出现OOMKilled?

    为什么我设置的大小关系没有错,还会OOMKilled? 这种问题常发生在JDK8u131或者JDK9版本之后所出现在容器中运行JVM的问题:在大多数情况下,JVM将一般默认会采用宿主机Node节点的内 ...

  6. SSM框架——Spring

    Spring 轻量级.非侵入式的框架 支持控制反转(IOC).面向切面编程(AOP) 支持事务的处理和声明.框架整合 1.HelloSpring(入门) 1.1导入依赖 <!-- https:/ ...

  7. Visual Studio2017快速收缩/扩展代码块

    首先要设置伸缩函数的同时也伸缩region块: 快捷键 Ctrl+M+O 收缩所有方法 Ctrl+M+L 展开所有方法

  8. [C++]union联合体总结

    特点一:成员公用内存,且按所占内存最大的数据成员分配内存 //举例1 union A{ char a;//1个字节 int b;//4个字节 char c;//1个字节 } cout<<s ...

  9. ZROI3

    题解 ZROI3 T1 与<滑动窗口>类似,用单调队列维护 #include <queue> #include <cstdio> #include <cstr ...

  10. Ubuntu 安装播放器

    安装 VLC sudo snap install vlc snap 下载慢的看这里https://www.cnblogs.com/Ahtelek/p/ubuntu-snap.html