众所周知,Linux系统具有卓越的稳定性和高效性,对硬件配置要求低,真正的多用户等特点。因此,Linux系统被广泛用于IT服务器Linux系统应用领域、嵌入式Linux系统应用领域、个人桌面Linux应用领域、移动端Linux应用领域。随着移动互联网技术的普及,Linux嵌入式的设备和应用场景越来越丰富,嵌入式Linux设备被广泛工控、仿真、医疗仪器、自助终端设备等领域。
在Linux系统的自助医疗终端设备中会使用到社保卡阅读器,所谓,“工欲善其事,必先利其器”今天,我来一一作答。
首先,要了解社保卡阅读器和二代证阅读器具有哪种通信接口。一般情况下是提供USB免驱和RS232串口免驱,也有部分型号支持TTL串口,低功耗蓝牙BLE4.2,标准TCP/IP通信等多种通信方式。针对不同应用场景选择对应的通信端口,例如,上位机距离社保卡阅读器和二代证阅读器的距离很远,USB和RS232串口通信距离达不到的情况下,最好是选择TCP/IP通信。如果上位机是Android设备,没有USB接口和RS232串口引出,而设备有蓝牙通信,可以选择蓝牙通信接口的阅读设备。要因地制宜,根据使用场景合理选择适合自己的设备。
其次,是要选择适合当前应用场景中使用的操作系统并能提供相应开发接口的设备。常见使用的操作系统大多数是windows,随着移动互联网应用的发展和普及越来越多的Android操作系统设备应用在生活的方方面面。除此之外,还有Linux,嵌入式的,国产操作系统统信OS,麒麟,银行之类诸多操作系统。
再次,除了了解设备是否支持当前应用的操作系统之外,还要选择与之对应的开发语言。方便设备与软件系统的对接,提供对接的接口函数和相应API。
首先,我们进行错误代码定义,方便程序调试。
#ifndef __LOTUS_CARD_DRIVER_H__
#define __LOTUS_CARD_DRIVER_H__
#if 1 //作为VC demo调用头文件 把0改为1
typedef int BOOL;
typedef unsigned char byte;
//#undef FALSE
//#undef TRUE
//#undef NULL
#define FALSE 0
#define TRUE 1
#define NULL 0
#define WINAPI
#define RT_NOT_HALT 0x26
#define RT_ALL 0x52
#define AM_A 0x60
#define AM_B 0x61
#define COS_RESULT_BUFFER_LENGTH 256
#define COS_SEND_BUFFER_LENGTH 256
//#define FOR_WINDOWS10 //WINDOWS 10 用long long
#ifdef FOR_WINDOWS10
typedef long long LotusHandle; //针对句柄单独搞个类型 便于改动
#else
typedef long LotusHandle; //针对句柄单独搞个类型 便于改动
#endif
//错误代码
typedef enum _LotusCardErrorCode
{
LCEC_OK = 0, //正常执行
LCEC_UNKNOWN, //未知的
LCEC_SEND_FALSE, //发送失败
LCEC_RECV_TIME_OUT, //接收超时
LCEC_RECV_ZERO_LEN, //接收长度为0
LCEC_RECV_CRC_FALSE, //接收校验失败
LCEC_REQUEST, //寻卡
LCEC_ANTICOLL, //防冲突
LCEC_SELECT, //选卡
LCEC_AUTHENTICATION, //三次验证
LCEC_HALT, //中止
LCEC_READ, //读
LCEC_WRITE, //写
LCEC_INCREMENT, //加值
LCEC_DECREMENT, //减值
LCEC_LOADKEY, //装载密码
LCEC_BEEP, //蜂鸣
LCEC_RESTORE, //卡数据块传入卡的内部寄存器
LCEC_TRANSFER, //内部寄存器传入卡的卡数据块
LCEC_SEND_COMMAND, //发送14443指令
LCEC_WIFI_SCANAP_BEGIN, //发送扫描动作开始信号
LCEC_WIFI_SCANAP_COUNT, //获取扫描AP计数
LCEC_WIFI_SCANAP_RESULT, //获取扫描结果
LCEC_WIFI_GET_STAINFO, //获取STA信息
LCEC_WIFI_SET_STAINFO, //设置STA信息
LCEC_WIFI_GET_MODE, //获取模式
LCEC_WIFI_SET_MODE, //设置模式
LCEC_WIFI_RESET, //复位WIFI模块
LCEC_FELICA_POLLING, //FELICA寻卡动作
LCEC_REQUESTB, //typeb寻卡错误
LCEC_SELECTB, //typeb选卡
LCEC_HALTB, //typeb中止
LCEC_M100_INVENTORY_FAIL, //轮询操作失败。没有标签返回或者返回数据CRC 校验错误。
}LotusCardErrorCode;
社保卡结构定义,由于社保卡有二代社保和三代社保卡,这部分定义进行了分开定义。
typedef struct _SocialSecurityCardStruct//社保卡结构 里面存放公开信息
{
char szCardNo[16];//实际只有9个字符
char szName[32];//一般是4-8个字符 考虑到特殊人名 搞多点
char szSSCNo[32];//一般是身份证号18个字符
char szSex[8];//性别
char szNation[32];//民族 有的民族名称有点长
char szBankNo[32];//对应的银行卡号
char szSignedDate[32];//签发日期
char szValidityEndDate[32];//到期日期
}SocialSecurityCardStruct;
typedef struct _SocialSecurityCardStructV3//社保卡结构 里面存放公开信息
{
//以下为发卡机构数据文件EF05 根据V2.0 构造 后续有了V3.0的东东再处理 这个应该是一致的
char szIdentificationCode[64];//卡的识别码 实际只有16个字节 转成hex字串 便于显示 长度就搞长点
char szType[4];//卡类型 实际只有1个字节
char szVersion[4];//卡版本 实际4个字节
char szInstitutionalNumber[32];//发卡机构 实际只有12个字节
char szSignedDate[32];//签发日期 实际只有4个字节
char szValidityEndDate[32];//到期日期 实际只有4个字节
char szCardNo[32];//卡号 实际只有9个字节
}SocialSecurityCardStructV3;
完成错误代码定义和社保卡结构定义之后,进行社保卡阅读功能实现。
#include "LotusCardDriver.h"
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include <unistd.h>
#define CHAR_LEN_UNICODE 2
#define CHAR_LEN_UTF8 3
void TestIcCard(long hLotusCard);
void TestGetTwoGenerationIdCardNo(long hLotusCard);
void TestIdCard(LotusHandle hLotusCard);
void Test7816(LotusHandle hLotusCard);
int wstrlen(char * pWideChar,int Len = 0);
int wstrlen(char * pWideChar,int Len)
{
int Result=0;
short * pWideTemp=(short *)pWideChar;
if(Len)
{
for(long lngCyc=0;lngCyc < Len/CHAR_LEN_UNICODE; lngCyc++)
{
if(pWideTemp[lngCyc]==0)
{
Result=lngCyc;
break;
}
}
}
else
{
while(pWideTemp[Result])
{
Result++;
}
}
return Result;
}
void testSocialSecurityCard(LotusCardDriver driver, long nHandle) {
LotusCardSocialSecurityCardParam tSocialSecurityCardParam = new LotusCardSocialSecurityCardParam();
LotusCardAppConfig tAppConfig = new LotusCardAppConfig();
boolean bResult = false;
String temp;
int nErrorCode = 0;
int nCardInsertStatus = 0;
TwoIdInfoParam tTwoIdInfo = new TwoIdInfoParam();
LotusCardParam tLotusCardParam1 = new LotusCardParam();
if (null == driver) return;
if (nHandle == -1) return;
nCardInsertStatus = driver.Get7816CardInsertStatus(nHandle);
if (1 == (nCardInsertStatus & 0xff)) {
AddLog("card insert !");
} else {
AddLog("card not insert !");
return;
}
// 以下动作设置5V输出 5V:VSel0=0 VSel1=1 3V:VSel0=1 VSel1=0 1.8V:VSel0=1 VSel1=1
bResult = driver.SetSamVSel0(nHandle, (byte) 0, (byte) 0);
if (!bResult) {
AddLog("Call SetSamVSel0 Error!");
return;
}
bResult = driver.SetSamVSel1(nHandle, (byte) 0, (byte) 1);
if (!bResult) {
AddLog("Call SetSamVSel1 Error!");
return;
}
// 下电
bResult = driver.SetSamPowerOnOff(nHandle, (byte) 0);
if (!bResult) {
AddLog("Call SetSamPowerOnOff Error!");
return;
}
// 上电
bResult = driver.SetSamPowerOnOff(nHandle, (byte) 1);
if (!bResult) {
AddLog("Call SetSamPowerOnOff Error!");
return;
}
//AddLog("设置5V");
AddLog("设置5V");
bResult = driver.ResetSam(nHandle, tLotusCardParam1);
if (!bResult) {
AddLog("Call ResetSam Error!");
return;
}
if (0 == tLotusCardParam1.unCosReultBufferLength) {
AddLog("unCosReultBufferLength Error!");
return;
}
AddLog("Call ResetSam Ok!");
AddLog("Reset result:"
+ driver.toHexString(tLotusCardParam1.arrCosResultBuffer, 0, tLotusCardParam1.unCosReultBufferLength));
//测试一下社保卡
bResult = driver.GetSocialSecurityInfoBy7816(nHandle, tSocialSecurityCardParam);
if (!bResult) {
AddLog("Call GetSocialSecurityInfoBy7816 Error!");
return;
}
AddLog("卡号:" + tSocialSecurityCardParam.strCardNo);
AddLog("姓名:" + new String(tSocialSecurityCardParam.arrName, Charset.forName("gbk")));
AddLog("身份证号:" + tSocialSecurityCardParam.strSSCNo);
if (tSocialSecurityCardParam.strSex.equals("1")) {
AddLog("性别:男");
} else {
AddLog("性别:女");
}
AddLog("民族:" + new String(tSocialSecurityCardParam.arrNation, Charset.forName("gbk")));
AddLog("银行卡号:" + tSocialSecurityCardParam.strBankNo);
AddLog("签发日期:" + tSocialSecurityCardParam.strSignedDate);
AddLog("到期日期:" + tSocialSecurityCardParam.strValidityEndDate);
}
以上内容是在Linux系统中进行社保卡阅读的部分内容,产品功能持续更新中。。。。。。