【LINUX】i.MX6学习笔记(4) IIC驱动 smec98sp

1. 引言

最近一个linux项目需要用到smec98sp来做一个加密鉴权的功能。
鉴权方案就不写了,这里主要记录一下IIC驱动smec98sp的过程,顺便记录下linux下IIC设备的框架。

2. IIC简介

IIC总线介绍见上一篇 【硬件】IIC总线 。
在此不再赘述。

3. smec98sp简介

smec98sp是中巨伟业的一款加密芯片,用于进行系统的一些加密校验,可以从官网下载到SDK开发包,从淘宝购买对应的下载器。
建议再买个芯片座,这样方便批量烧芯片,烧完统一送去贴片。
【LINUX】i.MX6学习笔记(4) IIC驱动 smec98sp_第1张图片

3.1 SDK

SDK包(以V33为例)里包含:

  1. PDF介绍
  2. 下载烧录工具SMEC98SP Tool V2.1.0.1.exe
  3. 加密算法验证工具 RSA.EXE
  4. Demo程序
    【LINUX】i.MX6学习笔记(4) IIC驱动 smec98sp_第2张图片
    Demo里分
  5. 加密芯片样例
    就是烧录到smec98sp里的程序。里面有一个样例,很详细,用Keil C51打开,添加自己对应的算法和流程以及交互ID就行。
  6. 主控MCU样例
    这个里面分了好几个平台的,流程差不多,哈希的算法移植有些微不同,一些操作只在特定平台有,所以不同平台有区别。
    在这里插入图片描述
    51内核的就选8051,stm32就用STM32。
    但是没有给Linux平台的,我就试着都做了一下移植。

3.2 管脚定义

管脚定义如下,看起来很明确。
8个脚,4个是NC,1个VCC(1.62V ~ 5.5V),1个GND,还有2个IIC的时钟和数据脚。
【LINUX】i.MX6学习笔记(4) IIC驱动 smec98sp_第3张图片
这没啥说的,直接IIC通信就行了。

3.3 开发

开发就用keil C51写。
配套的SDK包给了很全面的demo的例子,我们可以新增自己的ID来做自己的鉴权算法,相关算法的推荐在SDK里配套的pdf中也给了推荐。
在下载使用的上位机 SMEC98SP Tool V2.0.0.6 中还隐含了一个很实用的小工具,I2C测试,这样可以在开发调试的时候直接测试自己的IIC消息对不对。
举一个最简单的获取版本号的例子,我这里使用5C这个ID来作为版本号获取,在对应的switch语句中加入0x5c的case处理即可。
如果想加入其他的加密算法,用其他ID来实现即可。
【LINUX】i.MX6学习笔记(4) IIC驱动 smec98sp_第4张图片
【LINUX】i.MX6学习笔记(4) IIC驱动 smec98sp_第5张图片
【LINUX】i.MX6学习笔记(4) IIC驱动 smec98sp_第6张图片
可以看出,我们发送了0x5c之后,可以收到 0x5c 0x20 0x09 0x06 0x01 0x00 的6个字节的回复,和我们的case 0x5c的处理语句是一致的。

整体来说,SDK还是比较简明易懂的,基本拿到看一遍就能上手开发。

4. Linux驱动

IIC控制器是platform设备,属于platform总线。这个驱动一般芯片原厂会做好。
IIC设备是IIC设备,属于IIC总线。这部分需要我们自己开发。

4.1 IIC代码

主要文件是i2c-core.c,i2c-imx.c
我们使用ret = i2c_transfer(client->adapter, &msg, 1);
其中client是在probe时候传入的,即调用i2c_imx_probe函数,进而拿到了client->adapter,确定了IIC的适配器,初始化了IIC的控制器,那资源是从哪里获取的?我们直接调用i2c_transfer,系统是如何知道应该驱动IIC-0还是IIC-1?

在i2c_imx_probe中,设备树中的 IIC0 和 IIC1 控制器都是平台设备,二者都匹配时都调动了probe函数,获取了对应设备树里的资源,完成初始化,同时通过i2c_add_adapter把IIC控制器挂到链表上。此时的i2c_adapter,不仅仅包含读写策略algo,同样包含使用的引脚,哪一个IIC的控制器 等等信息。
当IIC总线上的设备进行probe的时候,系统会根据此设备是挂载哪一个IIC控制器的结点下面,传递对应的client结构体过来,此结构体中就包含之前已经初始化的i2c_adapter,即包含读写策略和要使用的硬件资源。

在驱动中,我们调用i2c_transfer来完成IIC数据的发送,其实就是调用__i2c_transfer,调用adap->algo->master_xfer函数,在iMX6中,master_xfer被初始化为i2c_imx_xfer

static struct i2c_algorithm i2c_imx_algo = {
	.master_xfer	= i2c_imx_xfer,
	.functionality	= i2c_imx_func,
};

4.2 步骤

  1. 先在设备中增加设备结点,这样匹配了之后,驱动会probe挂载上,生成设备结点。
&i2c1 {
	clock-frequency = <100000>;
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_i2c1>;
	status = "okay";

	smec98sp@10 {
		compatible = "taohu,smec98sp";
		reg = <0x10>;
	};	
};
  1. 建立对应的读写接口

/
*
 * @description	: 从smec98sp读取多个寄存器数据
 * @param - dev:  smec98sp设备
 * @param - reg:  要读取的寄存器首地址
 * @param - val:  读取到的数据
 * @param - len:  要读取的数据长度
 * @return 		: 操作结果
 */
static int smec98sp_read_regs(struct smec98sp_dev *dev, u8 reg, void *val, int len)
{
	int ret;
	struct i2c_msg msg[2];
	struct i2c_client *client = (struct i2c_client *)dev->private_data;

	/* msg[0]为发送要读取的首地址 */
	msg[0].addr = client->addr;			/* smec98sp地址 */
	msg[0].flags = 0;					/* 标记为发送数据 */
	msg[0].buf = &reg;					/* 读取的首地址 */
	msg[0].len = 1;						/* reg长度*/

	/* msg[1]读取数据 */
	msg[1].addr = client->addr;			/* smec98sp地址 */
	msg[1].flags = I2C_M_RD;			/* 标记为读取数据*/
	msg[1].buf = val;					/* 读取数据缓冲区 */
	msg[1].len = len;					/* 要读取的数据长度*/

	ret = i2c_transfer(client->adapter, msg, 2);
	if(ret == 2) {
		ret = 0;
	} else {
		printk("i2c rd failed=%d reg=%06x len=%d\n",ret, reg, len);
		ret = -EREMOTEIO;
	}
	return ret;
}

/*
 * @description	: 向smec98sp的1个寄存器写入多个数据
 * @param - dev:  smec98sp设备
 * @param - reg:  要写入的寄存器首地址
 * @param - val:  要写入的数据缓冲区
 * @param - len:  要写入的数据长度
 * @return 	  :   操作结果
 */
static s32 smec98sp_write_regs(struct smec98sp_dev *dev, u8 reg, u8 *buf, u8 len)
{
	u8 b[256];
	struct i2c_msg msg;
	struct i2c_client *client = (struct i2c_client *)dev->private_data;
	
	b[0] = reg;					/* 寄存器首地址 */
	memcpy(&b[1],buf,len);		/* 将要写入的数据拷贝到数组b里面 */
		
	msg.addr = client->addr;	/* smec98sp地址 */
	msg.flags = 0;				/* 标记为写数据 */

	msg.buf = b;				/* 要写入的数据缓冲区 */
	msg.len = len + 1;			/* 要写入的数据长度 */

	return i2c_transfer(client->adapter, &msg, 1);
}
  1. 在ioctl中根据传递的魔数来完成不同的功能。

5. 哈希算法

IIC流程调试的很顺利,没啥好说的,接收发送都很顺利,用逻辑分析仪抓的波形也没啥问题。
遇到的卡住的问题就是,我在我的加密算法中用到了哈希运算,但是同样的输入,加密芯片算的hash值和我在应用层算的hash值不一致,导致无法完成校验,为了理清这个hash算法,花了比较久的时间。
最初的时候,针对同一个输入,我能算出4个不同的值出来:

  1. Smec98sp芯片内部算出来的哈希值a
  2. 移植到linux端后,哈希算法算出来的哈希值b
  3. 使用SDK包里的 RSA.exe 工具计算出来的哈希值c
  4. 使用一些网上的哈希工具 工具1 工具2 等等计算出来的哈希值d

一脸蒙蔽,连个比对都没有,都不知道哪个是对的。
无奈,只好去找源码搜搜看,还好比较简单,大家的接口都比较一致,都是3个API。

1. SHA1Init()
2. SHA1Update()
3. SHA1Final()

其中

  1. SHA1Init 用来初始化
  2. SHA1Update 用来做多段的hash,这里要注意,我们可以把数据ABCDE一起送进来做一次,也可以AB做一次,然后CDE做一次,不影响最终结果。这个是为了解决一些大文件直接输出太大,可以切成小片输入的问题。
  3. SHA1Final 获取最终的哈希值。 SHA1Final里面也调用了SHA1Update 去填了一些数,不用管,没关系,我们的输入就是SHA1Update 的部分

一个个看吧,为啥4个结果都不一样。
RSA.exe 和 网上工具的区别很快就定位出来了。因为RSA.exe是16进制读取的,网上工具是字符串读取的,所以不一样,把对应字符串的十六进制填入计算就一致了。
【LINUX】i.MX6学习笔记(4) IIC驱动 smec98sp_第7张图片
【LINUX】i.MX6学习笔记(4) IIC驱动 smec98sp_第8张图片
然后找为什么linux计算的不对,关于移植问题,去请教了一下smec98sp的技术支持顾工,建议把TMS320的工程移植到linux。
重新改了一下平台移植过来就可以了。
最后确认为什么加密芯片自己算的不对。定位了一下中间的大小端问题,改了一下也OK了。
整体来说还是比较简单易懂的流程,调试起来还挺方便的。

6. 参考资料

  • 哈希计算工具1 & 哈希计算工具2
  • SHA1–C语言实现–openssl-1.1.1改写(自动匹配芯片大小端)
  • 中巨伟业官网

你可能感兴趣的:(LINUX,IMX6,嵌入式)