最近一个linux项目需要用到smec98sp来做一个加密鉴权的功能。
鉴权方案就不写了,这里主要记录一下IIC驱动smec98sp的过程,顺便记录下linux下IIC设备的框架。
IIC总线介绍见上一篇 【硬件】IIC总线 。
在此不再赘述。
smec98sp是中巨伟业的一款加密芯片,用于进行系统的一些加密校验,可以从官网下载到SDK开发包,从淘宝购买对应的下载器。
建议再买个芯片座,这样方便批量烧芯片,烧完统一送去贴片。
SDK包(以V33为例)里包含:
管脚定义如下,看起来很明确。
8个脚,4个是NC,1个VCC(1.62V ~ 5.5V),1个GND,还有2个IIC的时钟和数据脚。
这没啥说的,直接IIC通信就行了。
开发就用keil C51写。
配套的SDK包给了很全面的demo的例子,我们可以新增自己的ID来做自己的鉴权算法,相关算法的推荐在SDK里配套的pdf中也给了推荐。
在下载使用的上位机 SMEC98SP Tool V2.0.0.6 中还隐含了一个很实用的小工具,I2C测试,这样可以在开发调试的时候直接测试自己的IIC消息对不对。
举一个最简单的获取版本号的例子,我这里使用5C这个ID来作为版本号获取,在对应的switch语句中加入0x5c的case处理即可。
如果想加入其他的加密算法,用其他ID来实现即可。
可以看出,我们发送了0x5c之后,可以收到 0x5c 0x20 0x09 0x06 0x01 0x00 的6个字节的回复,和我们的case 0x5c的处理语句是一致的。
整体来说,SDK还是比较简明易懂的,基本拿到看一遍就能上手开发。
IIC控制器是platform设备,属于platform总线。这个驱动一般芯片原厂会做好。
IIC设备是IIC设备,属于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,
};
&i2c1 {
clock-frequency = <100000>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_i2c1>;
status = "okay";
smec98sp@10 {
compatible = "taohu,smec98sp";
reg = <0x10>;
};
};
/
*
* @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 = ® /* 读取的首地址 */
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);
}
IIC流程调试的很顺利,没啥好说的,接收发送都很顺利,用逻辑分析仪抓的波形也没啥问题。
遇到的卡住的问题就是,我在我的加密算法中用到了哈希运算,但是同样的输入,加密芯片算的hash值和我在应用层算的hash值不一致,导致无法完成校验,为了理清这个hash算法,花了比较久的时间。
最初的时候,针对同一个输入,我能算出4个不同的值出来:
一脸蒙蔽,连个比对都没有,都不知道哪个是对的。
无奈,只好去找源码搜搜看,还好比较简单,大家的接口都比较一致,都是3个API。
1. SHA1Init()
2. SHA1Update()
3. SHA1Final()
其中
一个个看吧,为啥4个结果都不一样。
RSA.exe 和 网上工具的区别很快就定位出来了。因为RSA.exe是16进制读取的,网上工具是字符串读取的,所以不一样,把对应字符串的十六进制填入计算就一致了。
然后找为什么linux计算的不对,关于移植问题,去请教了一下smec98sp的技术支持顾工,建议把TMS320的工程移植到linux。
重新改了一下平台移植过来就可以了。
最后确认为什么加密芯片自己算的不对。定位了一下中间的大小端问题,改了一下也OK了。
整体来说还是比较简单易懂的流程,调试起来还挺方便的。