1.Linux主机驱动和外设驱动分离思想(I2C驱动里有)
SPI驱动总线架构:SPI核心层(x),SPI控制器驱动层(x),SPI设备驱动层(√)。前面两个设备驱动搞明白了可以去看
2.教程中介绍:SPI函数接口(API):
简单介绍SPI协议,硬件原理(4412datasheet)
3.教程中介绍:SPI函数接口(API):
简单介绍SPI协议,硬件原理(4412datasheet)
- SDI(数据输入),SDO(数据输出),SCLK(时钟),CS(片选)
- SPI(rfid模块)的硬件基础知识(增加reset)
- SPI CLK管脚复用:Android4.0(wifi不能用)
- rfid的驱动配置:
- Device Drivers
- →SPI support
- →SPI_RC522
- can的驱动配置:
- Networking support
- →CAN bus subsystem support
- →CAN Device Drivers
- Platform CAN drivers with Netlink support(默认配置,不用动)
- CAN_MCP251X
设备注册:spi_board_info,spi_register_board_info
驱动注册函数和结构体:spi_register_driver/spi_unregister_driver,spi_driver
读写函数和结构体:spi_transfer和spi_message结构体,spi_async
1.驱动“设备注册,驱动注册,probe,数据下传,数据上传”的小结
虚拟平台:
①因为LED,蜂鸣器等等,因为这些驱动本身不是总线。所以都注册在虚拟平台总线上(platform_device)。
②然后platfrom_driver_register就是在驱动程序中使用了,注册时的platfrom_driver结构体中有name成员对应platform_device的名字。此结构当然还有probe,remove,suspend,resume等配套的功能接口。
③然后对LED来说,GPIO的操作基本是在probe中初始化。
④然后应用层如果需要调用,那么在probe里就需要注册杂项设备、字符设备等。
I2C:
①I2C的设备注册和平台注册类似,都是在arc/arm/mach-exynos/mach-itop4412.c中完成的
②和平台设备类似,I2C使用的函数不一样罢了,而且结构体也是i2c_driver了
③I2C对硬件的传输,使用i2c_transfer结合i2c_msg
④然后应用层如果需要调用,那么需要注册杂项设备,和虚拟平台类似。
SPI:
基本和I2C类似不同的是操作硬件的函数,spi_transfer要配置一下,然后传输用spi_sync。SPI让应用层调用使用字符设备。
2.设备-SPI设备注册一级设备注册之后的查询方法
配置SPI的内核之后可以用,查询到
[root@iTOP-]# cat sys/bus/spi/devices/spi2./modalias
rc522
SPI_board_info参数
spi_board_info参数
.modalias = "rc522", //初始化设备的名称
.platform_data = NULL,
.max_speed_hz = **, //初始化传输速率
.bus_num = , //控制器编号
.chip_select = , //控制器片选的编号
.mode = SPI_MODE_0, //spi的模式
.controller_data = &spi2_csi[], //片选IO的信息
spi2_board_info设备描述结构体,设备注册函数spi_register_board_info
增加一个spi设备my_rc522,然后去掉rfid和can驱动
/* add by cym 20141222 for RC522 RFID */
#ifdef CONFIG_SPI_RC522
{
.modalias = "rc522",
.platform_data = NULL,
.max_speed_hz = **,
.bus_num = ,
.chip_select = ,
.mode = SPI_MODE_0,
.controller_data = &spi2_csi[],
}
#endif
/* end add */
/* add by ct 2018825 */
{
.modalias = "my_rc522",
.platform_data = NULL,
.max_speed_hz = **,
.bus_num = ,
.chip_select = ,
.mode = SPI_MODE_0,
.controller_data = &spi2_csi[],
}
/* end add */
mach-itop4412.c
cat sys/bus/spi/devices/spi2.0/modalias
rfid的设备名称my_rc522
3.驱动-spi驱动注册和卸载。I2C设备初始化完成-进入probe函数。(不能加载wifi驱动)
#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/compat.h> #include <linux/spi/spi.h>
#include <linux/spi/spidev.h> #include <asm/uaccess.h> #include <linux/gpio.h>
#include <mach/gpio.h>
#include <plat/gpio-cfg.h>
#include <linux/delay.h> static int __devinit my_rc522_probe(struct spi_device *spi)
{
/* reset */
printk(KERN_EMERG "my_rc522_probe!\n"); return ;
} static int __devexit my_rc522_remove(struct spi_device *spi)
{
printk(KERN_EMERG "my_rc522_remove!\n");
return ;
} static struct spi_driver my_rc522_spi_driver = {
.driver = {
.name = "my_rc522",
.owner = THIS_MODULE,
},
.probe = my_rc522_probe,
.remove = __devexit_p(my_rc522_remove),
}; static int __init my_rc522_init(void)
{
spi_register_driver(&my_rc522_spi_driver);
return ;
} static void __exit my_rc522_exit(void)
{
spi_unregister_driver(&my_rc522_spi_driver);
} module_init(my_rc522_init);
module_exit(my_rc522_exit); MODULE_AUTHOR("topeet: ct");
MODULE_LICENSE("GPL");
my_rc522.c
4.驱动-spi数据的传输(rfid模块,不能加载wifi驱动)
- 1.本节实验需要RFID的硬件模块
- 2.1平台文件中RC522的设备名称直接改为my_rc522
- 2.2需要配置rfid对应的menuconfig
- 2.3 drivers/spi/Makefile中注释掉rc522.c文件的编译
- 3.从rc522驱动中提取spi传输的核心代码
- 4.直接在probe中做复位,读,写测试
- 写:rc522_write→rc522_sync_write→rc522_sync→spi_async
- 读:rc522_read→rc522_sync_read→rc522_sync→spi_async
my_rc522
#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/compat.h>
#include <linux/spi/spi.h>
#include <linux/spi/spidev.h>
#include <asm/uaccess.h>
#include <linux/gpio.h>
#include <mach/gpio.h>
#include <plat/gpio-cfg.h>
#include <linux/delay.h> #include "spidev_test.h"
#include "spidev.h" struct spi_device *my_spi; #define RC522_RESET_PIN EXYNOS4_GPK1(0)
void my_rc522_reset()
{
//printk("************************ %s\n", __FUNCTION__);
if(gpio_request_one(RC522_RESET_PIN, GPIOF_OUT_INIT_HIGH, "RC522_RESET"))
pr_err("failed to request GPK1_0 for RC522 reset control\n"); s3c_gpio_setpull(RC522_RESET_PIN, S3C_GPIO_PULL_UP);
gpio_set_value(RC522_RESET_PIN, ); mdelay(); gpio_set_value(RC522_RESET_PIN, );
gpio_free(RC522_RESET_PIN);
} static int write_test(unsigned char *buffer, int len)
{
int status;
struct spi_transfer t = {
.tx_buf = buffer,
.len = len,
};
struct spi_message m;
spi_message_init(&m);
spi_message_add_tail(&t, &m);
DECLARE_COMPLETION_ONSTACK(done);
m.complete = complete;
m.context = &done; printk("spi_async send begin!\n");
status = spi_async(my_spi,&m);
if(status == ){
wait_for_completion(&done);
status = m.status;
if (status == )
status = m.actual_length;
}
return status;
} static int read_test(unsigned char *buffer, int len)
{
int status;
struct spi_transfer t = {
.rx_buf = buffer,
.len = len,
};
struct spi_message m;
spi_message_init(&m);
spi_message_add_tail(&t, &m);
DECLARE_COMPLETION_ONSTACK(done);
m.complete = complete;
m.context = &done; printk("spi_async read begin!\n");
status = spi_async(my_spi,&m);
if(status == ){
wait_for_completion(&done);
status = m.status;
if (status == )
status = m.actual_length;
}
return status;
} static unsigned char ReadRawRC(int addr)
{
int ret;
unsigned char ReData;
unsigned char Address; Address = (unsigned char)addr << ;
Address |= ( << );
Address &= ~( << ); ret = write_test(&Address, );
if (ret < )
printk("spi:SPI Write error\n"); udelay(); ret = read_test(&ReData, );
if (ret < )
printk("spi:SPI Read error\n"); return ReData;
} static int WriteRawRC(int addr, int data)
{
int ret;
unsigned char TxBuf[]; //bit7:MSB=0,bit6~1:addr,bit0:RFU=0
TxBuf[] = ((unsigned char)addr << )&0x7E;
//TxBuf[0] &= 0x7E; TxBuf[] = (unsigned char)data; ret = write_test(TxBuf, ); if (ret < )
printk("spi:SPI Write error\n"); udelay(); return ret;
} static int rc522_init()
{
int ret;
char version = ; //reset
WriteRawRC(CommandReg, PCD_RESETPHASE);
udelay();
WriteRawRC(ModeReg, 0x3D);
WriteRawRC(TReloadRegL, );
WriteRawRC(TReloadRegH, );
WriteRawRC(TModeReg, 0x8D);
WriteRawRC(TPrescalerReg, 0x3E); version = ReadRawRC(VersionReg);
printk("Chip Version: 0x%x\n", version); return ;
} static int __devinit my_rc522_probe(struct spi_device *spi)
{ printk("my_rc522_probe!\n"); /* reset */
my_rc522_reset();
my_spi = spi;
rc522_init(); return ;
} static int __devexit my_rc522_remove(struct spi_device *spi)
{
printk("my_rc522_remove!\n");
return ;
} static struct spi_driver my_rc522_spi_driver = {
.driver = {
.name = "my_rc522",
.owner = THIS_MODULE,
},
.probe = my_rc522_probe,
.remove = __devexit_p(my_rc522_remove), /* NOTE: suspend/resume methods are not necessary here.
* We don't do anything except pass the requests to/from
* the underlying controller. The refrigerator handles
* most issues; the controller driver handles the rest.
*/
}; static int __init my_rc522_init(void)
{
spi_register_driver(&my_rc522_spi_driver);
return ;
} static void __exit my_rc522_exit(void)
{
spi_unregister_driver(&my_rc522_spi_driver);
} module_exit(my_rc522_exit);
module_init(my_rc522_init); MODULE_AUTHOR("topeet: rty");
MODULE_LICENSE("GPL");
my_rc522.c
spidev.h
/*
* include/linux/spi/spidev.h
*
* Copyright (C) 2006 SWAPP
* Andrea Paterniani <[email protected]>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/ #ifndef SPIDEV_H
#define SPIDEV_H #include <linux/types.h> /* User space versions of kernel symbols for SPI clocking modes,
* matching <linux/spi/spi.h>
*/ #define SPI_CPHA 0x01
#define SPI_CPOL 0x02 #define SPI_MODE_0 (0|0)
#define SPI_MODE_1 (0|SPI_CPHA)
#define SPI_MODE_2 (SPI_CPOL|0)
#define SPI_MODE_3 (SPI_CPOL|SPI_CPHA) #define SPI_CS_HIGH 0x04
#define SPI_LSB_FIRST 0x08
#define SPI_3WIRE 0x10
#define SPI_LOOP 0x20
#define SPI_NO_CS 0x40
#define SPI_READY 0x80 /*---------------------------------------------------------------------------*/ /* IOCTL commands */ #define SPI_IOC_MAGIC 'k' /**
* struct spi_ioc_transfer - describes a single SPI transfer
* @tx_buf: Holds pointer to userspace buffer with transmit data, or null.
* If no data is provided, zeroes are shifted out.
* @rx_buf: Holds pointer to userspace buffer for receive data, or null.
* @len: Length of tx and rx buffers, in bytes.
* @speed_hz: Temporary override of the device's bitrate.
* @bits_per_word: Temporary override of the device's wordsize.
* @delay_usecs: If nonzero, how long to delay after the last bit transfer
* before optionally deselecting the device before the next transfer.
* @cs_change: True to deselect device before starting the next transfer.
*
* This structure is mapped directly to the kernel spi_transfer structure;
* the fields have the same meanings, except of course that the pointers
* are in a different address space (and may be of different sizes in some
* cases, such as 32-bit i386 userspace over a 64-bit x86_64 kernel).
* Zero-initialize the structure, including currently unused fields, to
* accommodate potential future updates.
*
* SPI_IOC_MESSAGE gives userspace the equivalent of kernel spi_sync().
* Pass it an array of related transfers, they'll execute together.
* Each transfer may be half duplex (either direction) or full duplex.
*
* struct spi_ioc_transfer mesg[4];
* ...
* status = ioctl(fd, SPI_IOC_MESSAGE(4), mesg);
*
* So for example one transfer might send a nine bit command (right aligned
* in a 16-bit word), the next could read a block of 8-bit data before
* terminating that command by temporarily deselecting the chip; the next
* could send a different nine bit command (re-selecting the chip), and the
* last transfer might write some register values.
*/
struct spi_ioc_transfer {
__u64 tx_buf;
__u64 rx_buf; __u32 len;
__u32 speed_hz; __u16 delay_usecs;
__u8 bits_per_word;
__u8 cs_change;
__u32 pad; /* If the contents of 'struct spi_ioc_transfer' ever change
* incompatibly, then the ioctl number (currently 0) must change;
* ioctls with constant size fields get a bit more in the way of
* error checking than ones (like this) where that field varies.
*
* NOTE: struct layout is the same in 64bit and 32bit userspace.
*/
}; /* not all platforms use <asm-generic/ioctl.h> or _IOC_TYPECHECK() ... */
#define SPI_MSGSIZE(N) \
((((N)*(sizeof (struct spi_ioc_transfer))) < ( << _IOC_SIZEBITS)) \
? ((N)*(sizeof (struct spi_ioc_transfer))) : )
#define SPI_IOC_MESSAGE(N) _IOW(SPI_IOC_MAGIC, 0, char[SPI_MSGSIZE(N)]) /* Read / Write of SPI mode (SPI_MODE_0..SPI_MODE_3) */
#define SPI_IOC_RD_MODE _IOR(SPI_IOC_MAGIC, 1, __u8)
#define SPI_IOC_WR_MODE _IOW(SPI_IOC_MAGIC, 1, __u8) /* Read / Write SPI bit justification */
#define SPI_IOC_RD_LSB_FIRST _IOR(SPI_IOC_MAGIC, 2, __u8)
#define SPI_IOC_WR_LSB_FIRST _IOW(SPI_IOC_MAGIC, 2, __u8) /* Read / Write SPI device word length (1..N) */
#define SPI_IOC_RD_BITS_PER_WORD _IOR(SPI_IOC_MAGIC, 3, __u8)
#define SPI_IOC_WR_BITS_PER_WORD _IOW(SPI_IOC_MAGIC, 3, __u8) /* Read / Write SPI device default max speed hz */
#define SPI_IOC_RD_MAX_SPEED_HZ _IOR(SPI_IOC_MAGIC, 4, __u32)
#define SPI_IOC_WR_MAX_SPEED_HZ _IOW(SPI_IOC_MAGIC, 4, __u32) #endif /* SPIDEV_H */
spidev.h
spidev_test.h
#define MAXRLEN 18
#define MIN_STRENGTH 228
//******************************************************************/
// RC522 FIFO���ȶ��� /
//******************************************************************/
#define DEF_FIFO_LENGTH 64 //FIFO size=64byte //******************************************************************/
// RC522������ /
//******************************************************************/
#define PCD_IDLE 0x00 //ȡ����ǰ����
#define PCD_AUTHENT 0x0E //��֤��Կ
#define PCD_RECEIVE 0x08 //��������
#define PCD_TRANSMIT 0x04 //��������
#define PCD_TRANSCEIVE 0x0C //���Ͳ���������
#define PCD_RESETPHASE 0x0F //��λ
#define PCD_CALCCRC 0x03 //CRC���� //******************************************************************/
// Mifare_One��Ƭ������ */
//******************************************************************/
#define PICC_REQIDL 0x26 //Ѱ��������δ��������״̬
#define PICC_REQALL 0x52 //Ѱ��������ȫ����
#define PICC_ANTICOLL1 0x93 //����ײ
#define PICC_ANTICOLL2 0x95 //����ײ
#define PICC_AUTHENT1A 0x60 //��֤A��Կ
#define PICC_AUTHENT1B 0x61 //��֤B��Կ
#define PICC_READ 0x30 //����
#define PICC_WRITE 0xA0 //�
#define PICC_DECREMENT 0xC0 //�ۿ�
#define PICC_INCREMENT 0xC1 //��ֵ
#define PICC_RESTORE 0xC2 //�������ݵ�������
#define PICC_TRANSFER 0xB0 //���滺����������
#define PICC_HALT 0x50 //���� //******************************************************************/
// MF522�Ĵ������� /
//******************************************************************/
// PAGE 0
#define RFU00 0x00
#define CommandReg 0x01
#define ComIEnReg 0x02
#define DivlEnReg 0x03
#define ComIrqReg 0x04
#define DivIrqReg 0x05
#define ErrorReg 0x06
#define Status1Reg 0x07
#define Status2Reg 0x08
#define FIFODataReg 0x09
#define FIFOLevelReg 0x0A
#define WaterLevelReg 0x0B
#define ControlReg 0x0C
#define BitFramingReg 0x0D
#define CollReg 0x0E
#define RFU0F 0x0F
// PAGE 1
#define RFU10 0x10
#define ModeReg 0x11
#define TxModeReg 0x12
#define RxModeReg 0x13
#define TxControlReg 0x14
#define TxASKReg 0x15
#define TxSelReg 0x16
#define RxSelReg 0x17
#define RxThresholdReg 0x18
#define DemodReg 0x19
#define RFU1A 0x1A
#define RFU1B 0x1B
#define MifareReg 0x1C
#define RFU1D 0x1D
#define RFU1E 0x1E
#define SerialSpeedReg 0x1F
// PAGE 2
#define RFU20 0x20
#define CRCResultRegM 0x21
#define CRCResultRegL 0x22
#define RFU23 0x23
#define ModWidthReg 0x24
#define RFU25 0x25
#define RFCfgReg 0x26
#define GsNReg 0x27
#define CWGsCfgReg 0x28
#define ModGsCfgReg 0x29
#define TModeReg 0x2A
#define TPrescalerReg 0x2B
#define TReloadRegH 0x2C
#define TReloadRegL 0x2D
#define TCounterValueRegH 0x2E
#define TCounterValueRegL 0x2F
// PAGE 3
#define RFU30 0x30
#define TestSel1Reg 0x31
#define TestSel2Reg 0x32
#define TestPinEnReg 0x33
#define TestPinValueReg 0x34
#define TestBusReg 0x35
#define AutoTestReg 0x36
#define VersionReg 0x37
#define AnalogTestReg 0x38
#define TestDAC1Reg 0x39
#define TestDAC2Reg 0x3A
#define TestADCReg 0x3B
#define RFU3C 0x3C
#define RFU3D 0x3D
#define RFU3E 0x3E
#define RFU3F 0x3F //******************************************************************/
// RC522ͨѶ���ش������ /
//******************************************************************/
#define MI_ERR 0xFE
//#define MI_ERR //(-2) // Mifare Error Codes
// Each function returns a status value, which corresponds to the
// mifare error codes. #define MI_OK 0
#define MI_CHK_OK 0
#define MI_CRC_ZERO 0 #define MI_CRC_NOTZERO 1 #define MI_NOTAGERR 0xFF
#define MI_CHK_FAILED 0xFF
#define MI_CRCERR 0xFE
#define MI_CHK_COMPERR 0xFE
#define MI_EMPTY 0xFD
#define MI_AUTHERR 0xFC
#define MI_PARITYERR 0xFB
#define MI_CODEERR 0xFA #define MI_SERNRERR 0xF8
#define MI_KEYERR 0xF7
#define MI_NOTAUTHERR 0xF6
#define MI_BITCOUNTERR 0xF5
#define MI_BYTECOUNTERR 0xF4
#define MI_IDLE 0xF3
#define MI_TRANSERR 0xF2
#define MI_WRITEERR 0xF1
#define MI_INCRERR 0xF0
#define MI_DECRERR 0xEF
#define MI_READERR 0xEE
#define MI_OVFLERR 0xED
#define MI_POLLING 0xEC
#define MI_FRAMINGERR 0xEB
#define MI_ACCESSERR 0xEA
#define MI_UNKNOWN_COMMAND 0xE9
#define MI_COLLERR 0xE8
#define MI_RESETERR 0xE7
#define MI_INITERR 0xE7
#define MI_INTERFACEERR 0xE7
#define MI_ACCESSTIMEOUT 0xE5
#define MI_NOBITWISEANTICOLL 0xE4
#define MI_QUIT 0xE2 #define MI_RECBUF_OVERFLOW 0xCF
#define MI_SENDBYTENR 0xCE #define MI_SENDBUF_OVERFLOW 0xCC
#define MI_BAUDRATE_NOT_SUPPORTED 0xCB
#define MI_SAME_BAUDRATE_REQUIRED 0xCA #define MI_WRONG_PARAMETER_VALUE 0xC5 #define MI_BREAK 0x9E
#define MI_NY_IMPLEMENTED 0x9D
#define MI_NO_MFRC 0x9C
#define MI_MFRC_NOTAUTH 0x9B
#define MI_WRONG_DES_MODE 0x9A
#define MI_HOST_AUTH_FAILED 0x99 #define MI_WRONG_LOAD_MODE 0x97
#define MI_WRONG_DESKEY 0x96
#define MI_MKLOAD_FAILED 0x95
#define MI_FIFOERR 0x94
#define MI_WRONG_ADDR 0x93
#define MI_DESKEYLOAD_FAILED 0x92 #define MI_WRONG_SEL_CNT 0x8F
#define MI_RC531_WRONG_READVALUE 0x8E //LI ADDED 09-4-24
#define MI_WRONG_TEST_MODE 0x8C
#define MI_TEST_FAILED 0x8B
#define MI_TOC_ERROR 0x8A
#define MI_COMM_ABORT 0x89
#define MI_INVALID_BASE 0x88
#define MI_MFRC_RESET 0x87
#define MI_WRONG_VALUE 0x86
#define MI_VALERR 0x85
spidev_test.h
5.Linux-spi利用字符驱动完成应用层对spi的读和写
驱动:
#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/compat.h>
#include <linux/spi/spi.h>
#include <linux/spi/spidev.h>
#include <asm/uaccess.h>
#include <linux/gpio.h>
#include <mach/gpio.h>
#include <plat/gpio-cfg.h>
#include <linux/delay.h>
#include <linux/miscdevice.h> struct spi_device *my_spi; #define RC522_RESET_PIN EXYNOS4_GPK1(0)
void my_rc522_reset()
{
//printk("************************ %s\n", __FUNCTION__);
if(gpio_request_one(RC522_RESET_PIN, GPIOF_OUT_INIT_HIGH, "RC522_RESET"))
pr_err("failed to request GPK1_0 for RC522 reset control\n"); s3c_gpio_setpull(RC522_RESET_PIN, S3C_GPIO_PULL_UP);
gpio_set_value(RC522_RESET_PIN, ); mdelay(); gpio_set_value(RC522_RESET_PIN, );
gpio_free(RC522_RESET_PIN);
} //static ssize_t rc522_write(unsigned char *buffer, int len)
static ssize_t rc522_write(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
int status;
unsigned char tx_buf[]; status = copy_from_user(tx_buf,buf,count); struct spi_transfer t = {
.tx_buf = tx_buf,
.len = count,
};
struct spi_message m;
spi_message_init(&m);
spi_message_add_tail(&t, &m);
DECLARE_COMPLETION_ONSTACK(done);
m.complete = complete;
m.context = &done; printk("spi_async send begin!\n");
status = spi_async(my_spi,&m);
if(status == ){
wait_for_completion(&done);
status = m.status;
if (status == )
status = m.actual_length;
}
return status;
} //static ssize_t rc522_read(unsigned char *buffer, int len)
static ssize_t rc522_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
int status;
unsigned char *rx_buf; struct spi_transfer t = {
.rx_buf = &rx_buf,
.len = count,
};
struct spi_message m;
spi_message_init(&m);
spi_message_add_tail(&t, &m);
DECLARE_COMPLETION_ONSTACK(done);
m.complete = complete;
m.context = &done; printk("spi_async read begin!\n");
status = spi_async(my_spi,&m);
if(status == ){
wait_for_completion(&done);
status = m.status;
if (status == )
status = m.actual_length;
} status = copy_to_user(buf,&rx_buf,status); return status;
} int rc522_open(struct inode *inode,struct file *filp)
{
return ;
} static struct file_operations rc522_ops = {
.owner = THIS_MODULE,
.open = rc522_open,
.read = rc522_read,
.write = rc522_write,
}; static struct miscdevice rc522_dev = {
.minor = MISC_DYNAMIC_MINOR,
.fops = &rc522_ops,
.name = "rc522",
}; static int __devinit my_rc522_probe(struct spi_device *spi)
{ printk("my_rc522_probe!\n"); /* reset */
my_rc522_reset();
my_spi = spi; misc_register(&rc522_dev); return ;
} static int __devexit my_rc522_remove(struct spi_device *spi)
{
printk("my_rc522_remove!\n");
misc_deregister(&rc522_dev);
return ;
} static struct spi_driver my_rc522_spi_driver = {
.driver = {
.name = "my_rc522",
.owner = THIS_MODULE,
},
.probe = my_rc522_probe,
.remove = __devexit_p(my_rc522_remove), /* NOTE: suspend/resume methods are not necessary here.
* We don't do anything except pass the requests to/from
* the underlying controller. The refrigerator handles
* most issues; the controller driver handles the rest.
*/
}; static int __init my_rc522_init(void)
{
spi_register_driver(&my_rc522_spi_driver);
return ;
} static void __exit my_rc522_exit(void)
{
spi_unregister_driver(&my_rc522_spi_driver);
} module_exit(my_rc522_exit);
module_init(my_rc522_init); MODULE_AUTHOR("topeet: rty");
MODULE_LICENSE("GPL");
my_rc522.c
app:
/*
* SPI testing utility (using spidev driver)
*
* Copyright (c) 2007 MontaVista Software, Inc.
* Copyright (c) 2007 Anton Vorontsov <[email protected]>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License.
*
* Cross-compile with cross-gcc -I/path/to/cross-kernel/include
*/ #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 "spidev.h"
#include "spidev_test.h" #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) static void pabort(const char *s)
{
perror(s); abort();
} static const char *device = "/dev/rc522";
static uint8_t mode;
static uint8_t bits = ;
static uint32_t speed = * ;//500000;
static uint16_t delay; int g_SPI_Fd = ; unsigned char UID[], Temp[]; /*******************************************
�������ƣ�tochar
�� �ܣ�����16���ƺ���
�� ����id
����ֵ ����
********************************************/
#if 0
void tochar(unsigned char id)
{
switch(id)
{
case 0x00:printf("");break;
case 0x01:printf("");break;
case 0x02:printf("");break;
case 0x03:printf("");break;
case 0x04:printf("");break;
case 0x05:printf("");break;
case 0x06:printf("");break;
case 0x07:printf("");break;
case 0x08:printf("");break;
case 0x09:printf("");break;
case 0x0a:printf("0a");break;
case 0x0b:printf("0b");break;
case 0x0c:printf("0c");break;
case 0x0d:printf("0d");break;
case 0x0e:printf("0e");break;
case 0x0f:printf("0f");break;
case 0x10:printf("");break;
case 0x11:printf("");break;
case 0x12:printf("");break;
case 0x13:printf("");break;
case 0x14:printf("");break;
case 0x15:printf("");break;
case 0x16:printf("");break;
case 0x17:printf("");break;
case 0x18:printf("");break;
case 0x19:printf("");break;
case 0x1a:printf("1a");break;
case 0x1b:printf("1b");break;
case 0x1c:printf("1c");break;
case 0x1d:printf("1d");break;
case 0x1e:printf("1e");break;
case 0x1f:printf("1f");break;
case 0x20:printf("");break;
case 0x21:printf("");break;
case 0x22:printf("");break;
case 0x23:printf("");break;
case 0x24:printf("");break;
case 0x25:printf("");break;
case 0x26:printf("");break;
case 0x27:printf("");break;
case 0x28:printf("");break;
case 0x29:printf("");break;
case 0x2a:printf("2a");break;
case 0x2b:printf("2b");break;
case 0x2c:printf("2c");break;
case 0x2d:printf("2d");break;
case 0x2e:printf("2e");break;
case 0x2f:printf("2f");break;
case 0x30:printf("");break;
case 0x31:printf("");break;
case 0x32:printf("");break;
case 0x33:printf("");break;
case 0x34:printf("");break;
case 0x35:printf("");break;
case 0x36:printf("");break;
case 0x37:printf("");break;
case 0x38:printf("");break;
case 0x39:printf("");break;
case 0x3a:printf("3a");break;
case 0x3b:printf("3b");break;
case 0x3c:printf("3c");break;
case 0x3d:printf("3d");break;
case 0x3e:printf("3e");break;
case 0x3f:printf("3f");break;
case 0x40:printf("");break;
case 0x41:printf("");break;
case 0x42:printf("");break;
case 0x43:printf("");break;
case 0x44:printf("");break;
case 0x45:printf("");break;
case 0x46:printf("");break;
case 0x47:printf("");break;
case 0x48:printf("");break;
case 0x49:printf("");break;
case 0x4a:printf("4a");break;
case 0x4b:printf("4b");break;
case 0x4c:printf("4c");break;
case 0x4d:printf("4d");break;
case 0x4e:printf("4e");break;
case 0x4f:printf("4f");break;
case 0x50:printf("");break;
case 0x51:printf("");break;
case 0x52:printf("");break;
case 0x53:printf("");break;
case 0x54:printf("");break;
case 0x55:printf("");break;
case 0x56:printf("");break;
case 0x57:printf("");break;
case 0x58:printf("");break;
case 0x59:printf("");break;
case 0x5a:printf("5a");break;
case 0x5b:printf("5b");break;
case 0x5c:printf("5c");break;
case 0x5d:printf("5d");break;
case 0x5e:printf("5e");break;
case 0x5f:printf("5f");break;
case 0x60:printf("");break;
case 0x61:printf("");break;
case 0x62:printf("");break;
case 0x63:printf("");break;
case 0x64:printf("");break;
case 0x65:printf("");break;
case 0x66:printf("");break;
case 0x67:printf("");break;
case 0x68:printf("");break;
case 0x69:printf("");break;
case 0x6a:printf("6a");break;
case 0x6b:printf("6b");break;
case 0x6c:printf("6c");break;
case 0x6d:printf("6d");break;
case 0x6e:printf("6e");break;
case 0x6f:printf("6f");break;
case 0x70:printf("");break;
case 0x71:printf("");break;
case 0x72:printf("");break;
case 0x73:printf("");break;
case 0x74:printf("");break;
case 0x75:printf("");break;
case 0x76:printf("");break;
case 0x77:printf("");break;
case 0x78:printf("");break;
case 0x79:printf("");break;
case 0x7a:printf("7a");break;
case 0x7b:printf("7b");break;
case 0x7c:printf("7c");break;
case 0x7d:printf("7d");break;
case 0x7e:printf("7e");break;
case 0x7f:printf("7f");break;
case 0x80:printf("");break;
case 0x81:printf("");break;
case 0x82:printf("");break;
case 0x83:printf("");break;
case 0x84:printf("");break;
case 0x85:printf("");break;
case 0x86:printf("");break;
case 0x87:printf("");break;
case 0x88:printf("");break;
case 0x89:printf("");break;
case 0x8a:printf("8a");break;
case 0x8b:printf("8b");break;
case 0x8c:printf("8c");break;
case 0x8d:printf("8d");break;
case 0x8e:printf("8e");break;
case 0x8f:printf("8f");break;
case 0x90:printf("");break;
case 0x91:printf("");break;
case 0x92:printf("");break;
case 0x93:printf("");break;
case 0x94:printf("");break;
case 0x95:printf("");break;
case 0x96:printf("");break;
case 0x97:printf("");break;
case 0x98:printf("");break;
case 0x99:printf("");break;
case 0x9a:printf("9a");break;
case 0x9b:printf("9b");break;
case 0x9c:printf("9c");break;
case 0x9d:printf("9d");break;
case 0x9e:printf("9e");break;
case 0x9f:printf("9f");break;
case 0xa0:printf("a0");break;
case 0xa1:printf("a1");break;
case 0xa2:printf("a2");break;
case 0xa3:printf("a3");break;
case 0xa4:printf("a4");break;
case 0xa5:printf("a5");break;
case 0xa6:printf("a6");break;
case 0xa7:printf("a7");break;
case 0xa8:printf("a8");break;
case 0xa9:printf("a9");break;
case 0xaa:printf("aa");break;
case 0xab:printf("ab");break;
case 0xac:printf("ac");break;
case 0xad:printf("ad");break;
case 0xae:printf("ae");break;
case 0xaf:printf("af");break;
case 0xb0:printf("b0");break;
case 0xb1:printf("b1");break;
case 0xb2:printf("b2");break;
case 0xb3:printf("b3");break;
case 0xb4:printf("b4");break;
case 0xb5:printf("b5");break;
case 0xb6:printf("b6");break;
case 0xb7:printf("b7");break;
case 0xb8:printf("b8");break;
case 0xb9:printf("b9");break;
case 0xba:printf("ba");break;
case 0xbb:printf("bb");break;
case 0xbc:printf("bc");break;
case 0xbd:printf("bd");break;
case 0xbe:printf("be");break;
case 0xbf:printf("bf");break;
case 0xc0:printf("c0");break;
case 0xc1:printf("c1");break;
case 0xc2:printf("c2");break;
case 0xc3:printf("c3");break;
case 0xc4:printf("c4");break;
case 0xc5:printf("c5");break;
case 0xc6:printf("c6");break;
case 0xc7:printf("c7");break;
case 0xc8:printf("c8");break;
case 0xc9:printf("c9");break;
case 0xca:printf("ca");break;
case 0xcb:printf("cb");break;
case 0xcc:printf("cc");break;
case 0xcd:printf("cd");break;
case 0xce:printf("ce");break;
case 0xcf:printf("cf");break;
case 0xd0:printf("d0");break;
case 0xd1:printf("d1");break;
case 0xd2:printf("d2");break;
case 0xd3:printf("d3");break;
case 0xd4:printf("d4");break;
case 0xd5:printf("d5");break;
case 0xd6:printf("d6");break;
case 0xd7:printf("d7");break;
case 0xd8:printf("d8");break;
case 0xd9:printf("d9");break;
case 0xda:printf("da");break;
case 0xdb:printf("db");break;
case 0xdc:printf("dc");break;
case 0xdd:printf("dd");break;
case 0xde:printf("de");break;
case 0xdf:printf("df");break;
case 0xe0:printf("e0");break;
case 0xe1:printf("e1");break;
case 0xe2:printf("e2");break;
case 0xe3:printf("e3");break;
case 0xe4:printf("e4");break;
case 0xe5:printf("e5");break;
case 0xe6:printf("e6");break;
case 0xe7:printf("e7");break;
case 0xe8:printf("e8");break;
case 0xe9:printf("e9");break;
case 0xea:printf("ea");break;
case 0xeb:printf("eb");break;
case 0xec:printf("ec");break;
case 0xed:printf("ed");break;
case 0xee:printf("ee");break;
case 0xef:printf("ef");break;
case 0xf0:printf("f0");break;
case 0xf1:printf("f1");break;
case 0xf2:printf("f2");break;
case 0xf3:printf("f3");break;
case 0xf4:printf("f4");break;
case 0xf5:printf("f5");break;
case 0xf6:printf("f6");break;
case 0xf7:printf("f7");break;
case 0xf8:printf("f8");break;
case 0xf9:printf("f9");break;
case 0xfa:printf("fa");break;
case 0xfb:printf("fb");break;
case 0xfc:printf("fc");break;
case 0xfd:printf("fd");break;
case 0xfe:printf("fe");break;
case 0xff:printf("ff");break;
default:
;
}
}
#endif int WriteRawRC(int addr, int data)
{
int ret;
int fd = g_SPI_Fd;
unsigned char TxBuf[]; //bit7:MSB=0,bit6~1:addr,bit0:RFU=0
TxBuf[] = ((unsigned char)addr << )&0x7E;
//TxBuf[0] &= 0x7E; TxBuf[] = (unsigned char)data; ret = write(fd, TxBuf, );
if (ret < )
printf("spi:SPI Write error\n"); usleep(); return ret;
} unsigned char ReadRawRC(int addr)
{
int ret;
int fd = g_SPI_Fd;
unsigned char ReData;
unsigned char Address; Address = (unsigned char)addr << ;
Address |= ( << );
Address &= ~( << ); ret = write(fd, &Address, );
if (ret < )
printf("spi:SPI Write error\n"); usleep(); ret = read(fd, &ReData, );
if (ret < )
printf("spi:SPI Read error\n"); return ReData;
} void SetBitMask(unsigned char reg,unsigned char mask)
{
char tmp = 0x0; tmp = ReadRawRC(reg) | mask; WriteRawRC(reg,tmp | mask);
} //******************************************************************/
//�� �ܣ���RC522�Ĵ���λ
//����˵����reg[IN]:�Ĵ�����ַ
// mask[IN]:��λֵ
//******************************************************************/
void ClearBitMask(unsigned char reg, unsigned char mask)
{
char tmp = 0x0; tmp = ReadRawRC(reg)&(~mask); WriteRawRC(reg, tmp); // clear bit mask
} int rc522_init()
{
int ret;
char version = ; //reset
WriteRawRC(CommandReg, PCD_RESETPHASE);
usleep();
WriteRawRC(ModeReg, 0x3D);
WriteRawRC(TReloadRegL, );
WriteRawRC(TReloadRegH, );
WriteRawRC(TModeReg, 0x8D);
WriteRawRC(TPrescalerReg, 0x3E); version = ReadRawRC(VersionReg);
printf("Chip Version: 0x%x\n", version);
usleep(); return ;
} void PcdAntennaOn()
{
unsigned char i; WriteRawRC(TxASKReg, 0x40);
usleep(); i = ReadRawRC(TxControlReg);
if(!(i&0x03))
SetBitMask(TxControlReg, 0x03); i = ReadRawRC(TxASKReg);
} 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();
} static void parse_opts(int argc, char *argv[])
{
while () {
static const struct option lopts[] = {
{ "device", , , 'D' },
{ "speed", , , 's' },
{ "delay", , , 'd' },
{ "bpw", , , 'b' },
{ "loop", , , 'l' },
{ "cpha", , , 'H' },
{ "cpol", , , 'O' },
{ "lsb", , , 'L' },
{ "cs-high", , , 'C' },
{ "3wire", , , '' },
{ "no-cs", , , 'N' },
{ "ready", , , 'R' },
{ NULL, , , },
};
int c; c = getopt_long(argc, argv, "D:s:d:b:lHOLC3NR", lopts, NULL); if (c == -)
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':
mode |= SPI_LSB_FIRST;
break;
case 'C':
mode |= SPI_CS_HIGH;
break;
case '':
mode |= SPI_3WIRE;
break;
case 'N':
mode |= SPI_NO_CS;
break;
case 'R':
mode |= SPI_READY;
break;
default:
print_usage(argv[]);
break;
}
}
} //******************************************************************/
//�� �ܣ�ͨ��RC522��ISO14443��ͨѶ
//����˵����Command[IN]:RC522������
// pInData[IN]:ͨ��RC522���͵���Ƭ������
// InLenByte[IN]:�������ݵ��ֽڳ���
// pOutData[OUT]:���յ��Ŀ�Ƭ��������
// *pOutLenBit[OUT]:�������ݵ�λ����
//******************************************************************/
char PcdComMF522(unsigned char Command, unsigned char *pInData,
unsigned char InLenByte, unsigned char *pOutData,
unsigned int *pOutLenBit)
{
char status = MI_ERR;
unsigned char irqEn = 0x00;
unsigned char waitFor = 0x00;
unsigned char lastBits;
unsigned char n;
unsigned int i; switch (Command)
{
case PCD_AUTHENT:
irqEn = 0x12;
waitFor = 0x10;
break;
case PCD_TRANSCEIVE:
irqEn = 0x77;
waitFor = 0x30;
break;
default:
break;
} WriteRawRC(ComIEnReg, irqEn|0x80);
ClearBitMask(ComIrqReg, 0x80);
WriteRawRC(CommandReg, PCD_IDLE);
SetBitMask(FIFOLevelReg, 0x80); // ���FIFO
for(i=; i<InLenByte; i++)
WriteRawRC(FIFODataReg, pInData[i]); // ����д��FIFO WriteRawRC(CommandReg, Command); // ����д������Ĵ��� if(Command == PCD_TRANSCEIVE)
SetBitMask(BitFramingReg,0x80); // ��ʼ���� i = ; //����ʱ��Ƶ�ʵ���������M1�����ȴ�ʱ��25ms
do
{
n = ReadRawRC(ComIrqReg);
i--;
}
while((i!=)&&!(n&0x01)&&!(n&waitFor)); ClearBitMask(BitFramingReg, 0x80); if(i!=)
{
if(!(ReadRawRC(ErrorReg) & 0x1B))
{
status = MI_OK;
if (n&irqEn&0x01)
status = MI_NOTAGERR;
if(Command == PCD_TRANSCEIVE)
{
n = ReadRawRC(FIFOLevelReg); lastBits = ReadRawRC(ControlReg) & 0x07;
if(lastBits)
*pOutLenBit = (n-)* + lastBits;
else
*pOutLenBit = n*; if(n == )
n = ;
if(n>MAXRLEN)
n = MAXRLEN; for (i=; i<n; i++)
pOutData[i] = ReadRawRC(FIFODataReg);
}
}
else
{
status = MI_ERR;
}
} SetBitMask(ControlReg, 0x80);// stop timer now
WriteRawRC(CommandReg, PCD_IDLE); return status;
} char PcdRequest(unsigned char req_code, unsigned char *pTagType)
{
char status;
unsigned int unLen;
unsigned char ucComMF522Buf[MAXRLEN]; ClearBitMask(Status2Reg, 0x08);
WriteRawRC(BitFramingReg, 0x07);
SetBitMask(TxControlReg, 0x03); ucComMF522Buf[] = req_code; status = PcdComMF522(PCD_TRANSCEIVE, ucComMF522Buf,
, ucComMF522Buf, &unLen); if ((status == MI_OK) && (unLen == 0x10))
{
*pTagType = ucComMF522Buf[];
*(pTagType+) = ucComMF522Buf[];
}
else
{
status = MI_ERR;
} return status;
} //******************************************************************/
//�� �ܣ�����ײ /
//����˵��: pSnr[OUT]:��Ƭ���кţ�4�ֽ� /
//�� ��: �ɹ�����MI_OK /
//******************************************************************/
char PcdAnticoll(unsigned char *pSnr)
{
char status;
unsigned char i, snr_check = ;
unsigned int unLen;
unsigned char ucComMF522Buf[MAXRLEN]; ClearBitMask(Status2Reg, 0x08);
WriteRawRC(BitFramingReg, 0x00);
ClearBitMask(CollReg, 0x80); ucComMF522Buf[] = PICC_ANTICOLL1;
ucComMF522Buf[] = 0x20; status = PcdComMF522(PCD_TRANSCEIVE, ucComMF522Buf,
, ucComMF522Buf, &unLen); if(status == MI_OK)
{
for (i=; i<; i++)
{
*(pSnr+i) = ucComMF522Buf[i];
snr_check ^= ucComMF522Buf[i];
}
if (snr_check != ucComMF522Buf[i])
{
status = MI_ERR;
}
} SetBitMask(CollReg,0x80); return status;
} void Find_Card(void)
{
if(PcdRequest(0x52, Temp) == MI_OK)
{
if(Temp[]==0x04 && Temp[]==0x00)
printf("MFOne-S50\n");
else if(Temp[]==0x02 && Temp[] == 0x00)
printf("MFOne-S70\n");
else if(Temp[]==0x44 && Temp[]==0x00)
printf("MF-UltraLight\n");
else if(Temp[]==0x08 && Temp[]==0x00)
printf("MF-Pro\n");
else if(Temp[]==0x44 && Temp[]==0x03)
printf("MF Desire\n");
else
printf("Unknown\n"); printf("SUCCESS!\n");
}
else
{
printf("No card!\n");
}
} void Auto_Reader(void)
{
int i = ;
unsigned long num = ; // while(1)
//{
if(PcdRequest(0x52,Temp) == MI_OK)
{
if(Temp[]==0x04 && Temp[]==0x00)
printf("MFOne-S50\n");
else if(Temp[]==0x02 && Temp[]==0x00)
printf("MFOne-S70\n");
else if(Temp[]==0x44 && Temp[]==0x00)
printf("MF-UltraLight\n");
else if(Temp[]==0x08 && Temp[]==0x00)
printf("MF-Pro\n");
else if(Temp[]==0x44 && Temp[]==0x03)
printf("MF Desire\n");
else
printf("Unknown\n"); if(PcdAnticoll(UID) == MI_OK)
{
printf("Card Id is(%d):", num++);
#if 1
for(i=; i<; i++)
printf("%x", UID[i]);
#else
tochar(UID[]);
tochar(UID[]);
tochar(UID[]);
tochar(UID[]);
#endif
printf("\n"); PcdRequest(0x52,Temp);//clear
}
else
{
printf("no serial num read\n");
}
}
else
{
printf("No Card!\n");
} usleep();
// }
} void HandleConfigMenu(unsigned char inputvalue)
{
#if 0
switch(toupper(inputvalue))
{
case 'A':
Auto_Reader();
break;
case 'F':
Find_Card();
break;
default:
DisplayConfigMenu();
}
#endif //Find_Card(); Auto_Reader();
} int main(int argc, char *argv[])
{
unsigned char i; int ret = ;
int fd; parse_opts(argc, argv); fd = open(device, O_RDWR);
if (fd < )
pabort("can't open device"); g_SPI_Fd = fd; #if 0
/*
* spi mode
*/
ret = ioctl(fd, SPI_IOC_WR_MODE, &mode);
if (ret == -)
pabort("can't set spi mode"); ret = ioctl(fd, SPI_IOC_RD_MODE, &mode);
if (ret == -)
pabort("can't get spi mode"); /*
* bits per word
*/
ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);
if (ret == -)
pabort("can't set bits per word"); ret = ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &bits);
if (ret == -)
pabort("can't get bits per word"); /*
* max speed hz
*/
ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);
if (ret == -)
pabort("can't set max speed hz"); ret = ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed);
if (ret == -)
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/);
#endif rc522_init(); PcdAntennaOn(); HandleConfigMenu(i); close(fd); return ret;
}
spidev_test.c