嵌入式linux之go语言开发(三)卡库的封装

简介: 嵌入式linux之go语言开发(三)卡库的封装

卡库的封装和调用,这是一个重头戏,完成了它,则就完整了所有的封装。至于网络通信,记录存储等,则可以使用go本身的模块去做。后续做一版完整的go语言版B503应用。


截至目前,非接触式卡库的封装接近尾声,这部分花了不少精力。


package drivers
/*
#cgo CFLAGS: -Iinclude
#cgo LDFLAGS: -Llib -lpicc
#include <stdlib.h>
#include "pcd_apis.h"
*/
import "C"
import "unsafe"
import (
  "fmt"
  "log"
)
const (
  DEF_PCD_SeleTypeA int = 1
  DEF_PCD_SeleTypeB int = 2
)
type __Pcd14443Cfg struct {
  PPS   int //AB  0:标准的支持PPS,非0:强制不支持PPS
  M1CPU int //AB  0:自动识别,1:强制M1,2:强制CPU
}
type __Pcd14443Info struct {
  ATQA []byte //A专用   REQA命令返回的ATQA值      固定有效2字节
  UID  []byte //A专用   卡片的UID,         长度为4,7,10字节
  UID_Size byte //A专用   卡片的UID长度          长度为4,7,10字节
  SAK      byte //A专用   卡片选卡成功返回的SAK值     固定有效1字节
  TypeAB   byte //A/B共用 当前是A卡还是B卡     DEF_PCD_SeleTypeA=A卡,DEF_PCD_SeleTypeB=B卡
  ATS_Size byte //A专用   接收到的ATS数据长度
  BActive  byte //B激活状态 0:非激活  非0:激活
  Rvs08bit byte
  Rvs16bit byte
  ATS []byte //A专用   ATS接收数据缓冲区 按中国金融规定 PICC回的数据最长为21字节
  ATQB []byte //B专用   卡片应答数据
} //14443应用数据结构
var (
  Pcd14443CfgMode int = DEF_PCD_SeleTypeA //PCD选择TypeA卡 操作PICC类型定义
  Pcd14443CfgA    __Pcd14443Cfg
  Pcd14443CfgB    __Pcd14443Cfg
  Pcd14443Info    __Pcd14443Info //14443协议层数据缓冲区
  PiccCid int
)
func init() {
  Pcd14443Info.ATQA = make([]byte, 4)
  Pcd14443Info.UID = make([]byte, 16)
  Pcd14443Info.ATS = make([]byte, 40)
  Pcd14443Info.ATQB = make([]byte, 20)
}
func ICC_PCD_SysCfg(mode, m1, pps int) {
  Pcd14443CfgMode = mode
  if DEF_PCD_SeleTypeA == Pcd14443CfgMode {
    Pcd14443CfgA.PPS = pps
    Pcd14443CfgA.M1CPU = m1
  } else if DEF_PCD_SeleTypeB == Pcd14443CfgMode {
    Pcd14443CfgB.PPS = pps
    Pcd14443CfgB.M1CPU = m1
  }
}
func ICC_PCD_Init() int {
  tpe := make([]byte, 4)
  para := make([]byte, 50)
  //var cardType *C.uchar = (*C.uchar)(unsafe.Pointer(&tpe[0]))
  //var rfPara *C.uchar = (*C.uchar)(unsafe.Pointer(&para[0]))
  cardType := (*C.uchar)(unsafe.Pointer(&tpe[0]))
  rfPara := (*C.uchar)(unsafe.Pointer(&para[0]))
  ret := C.PcdInit(cardType, rfPara)
  if ret != 0 {
    return 1
  }
  fmt.Println(tpe)
  fmt.Println(para)
  ret = C.PiccOpen()
  return int(ret)
}
func ICC_PCD_Open() int {
  ret := C.PiccOpen()
  return int(ret)
}
func ICC_PCD_Close() {
  C.PiccClose()
}
//ISO14443 A/B 使卡进入HALT状态
func ICC_PCD_Halt() int {
  //nc_iso14443_debug("%s","ICC_PCD_Halt\n");
  ret := C.PiccRemove('H', C.uchar(PiccCid))
  return int(ret)
}
//ISO14443 A/B 使已经进入HALT状态的卡激活,并且进行冲突循环,选卡操作
func ICC_PCD_WakeUp() int {
  ret := ICC_PCD_Request(Pcd14443CfgMode)
  return int(ret)
}
func ICC_PCD_Request(mode int) int {
  tpe := make([]byte, 4)
  sno := make([]byte, 100)
  oth := make([]byte, 100)
  pid := make([]byte, 1)
  lenth := 0
  ptr := 0
  ret := C.uchar(0)
  cardType := (*C.uchar)(unsafe.Pointer(&tpe[0]))
  serialNo := (*C.uchar)(unsafe.Pointer(&sno[0]))
  other := (*C.uchar)(unsafe.Pointer(&oth[0]))
  piccid := (*C.uchar)(unsafe.Pointer(&pid[0]))
  if DEF_PCD_SeleTypeA == Pcd14443CfgMode {
    if DEF_PCD_SeleTypeA == Pcd14443CfgA.M1CPU { //强制M1
      ret = C.PiccDetect('M', cardType, serialNo, piccid, other)
    } else if DEF_PCD_SeleTypeB == Pcd14443CfgA.M1CPU { //强制CPUA
      ret = C.PiccDetect('A', cardType, serialNo, piccid, other)
    } else if 3 == Pcd14443CfgA.M1CPU { //自动检测A,B,无法检测到纯M1 操作一次28ms
      ret = C.PiccDetect(0x01, cardType, serialNo, piccid, other)
    } else { //自动CPUA/M1 操作一次18ms
      ret = C.PiccDetect('X', cardType, serialNo, piccid, other)
    }
    //    printf(">>>>>>>>>>>>PiccDetect, ret=%d   count=%d\n", ret, count++);
  } else if DEF_PCD_SeleTypeB == Pcd14443CfgMode { //强制CPUB
    ret = C.PiccDetect('B', cardType, serialNo, piccid, other)
  } else {
    log.Fatal("err config!")
  }
  //fmt.Printf("ret = %d\n", int(ret))
  PiccCid = int(pid[0])
  if ret == 0 {
    //fmt.Printf("tpe:%x\n", tpe)
    //fmt.Printf("sno:%x\n", sno)
    //fmt.Printf("oth:%x\n", oth)
    if 'B' == tpe[0] {
      Pcd14443Info.TypeAB = byte(DEF_PCD_SeleTypeB) //TYPE B
    } else if 'M' == tpe[0] {
      Pcd14443Info.TypeAB = byte(DEF_PCD_SeleTypeA) //TYPE A
    } else {
      Pcd14443Info.TypeAB = byte(DEF_PCD_SeleTypeA) //TYPE A
    }
    if sno[0] > 10 { //序列号长度不能大于10
      return 1
    }
    Pcd14443Info.UID_Size = sno[0]
    copy(Pcd14443Info.UID, sno[1:1+Pcd14443Info.UID_Size])
    if oth[0] > 2 {
      ptr = 3
      lenth = int(oth[ptr])
      ptr += 1
      if ptr+lenth < len(oth) {
        copy(Pcd14443Info.ATQA, oth[ptr:ptr+2])
        //fmt.Printf("ATQA:%x\n", Pcd14443Info.ATQA)
      }
      ptr += lenth
      lenth = int(oth[ptr])
      ptr += 1
      if ptr+lenth < len(oth) {
        Pcd14443Info.SAK = oth[ptr]
      }
      ptr += lenth
      lenth = int(oth[ptr])
      if 'A' == tpe[0] && (ptr+lenth < len(oth)) {
        copy(Pcd14443Info.ATS, oth[ptr:ptr+lenth])
        Pcd14443Info.ATS_Size = byte(lenth)
      }
      //fmt.Println(Pcd14443Info)
      fmt.Printf("ATQA:%x\n", Pcd14443Info.ATQA)
      fmt.Printf("SAK:%x\n", Pcd14443Info.SAK)
      fmt.Printf("UID:%x\n", Pcd14443Info.UID)
    }
  } else {
    Pcd14443Info.ATQA[0] = 0
    Pcd14443Info.ATQA[1] = 0
    Pcd14443Info.ATQA[2] = 0
    Pcd14443Info.ATQA[3] = 0
  }
  return int(ret)
}
//ISO14443 A/B 检测卡片是否存在,卡片激活状态后调用
func ICC_PCD_CheckPICCRounge() int {
  ret := C.PiccRemove('C', C.uchar(PiccCid))
  //nc_iso14443_debug("PiccRemove('C').ret=%d\n", ret);
  if 0x06 == ret { //卡片仍在感应区
    //nc_iso14443_debug( "%s", "Card exist\n" );  //PiccRemove('C')停活卡片,需要重新寻卡以激活卡片
    return 0 //返回卡片仍在
  } else {
    //nc_iso14443_debug( "%s", "Card removed\n" );
    return 1 //返回卡片离开
  }
}
//ISO14443 A/B 检测PICC是否移出工作场
func ICC_PCD_CheckPICCrf() int {
  ret := C.PiccRemove('R', C.uchar(PiccCid))
  //nc_iso14443_debug("PiccRemove('C').ret=%d\n", ret);
  if 0x06 == ret { //卡片仍在感应区
    //nc_iso14443_debug( "%s", "Card exist\n" );  //PiccRemove('C')停活卡片,需要重新寻卡以激活卡片
    return 0 //返回卡片仍在
  } else {
    //nc_iso14443_debug( "%s", "Card removed\n" );
    return 1 //返回卡片离开
  }
}
//ISO14443 A/B 等待PICC移出工作场
func ICC_PCD_WaitPICCrf() int {
  for true {
    ret := C.PiccRemove('C', C.uchar(PiccCid))
    //nc_iso14443_debug("PiccRemove('C').ret=%d\n", ret);
    if 0x06 == ret {
      //nc_iso14443_debug( "%s", "Card exist\n" );
      continue //返回卡片仍在
    } else {
      //nc_iso14443_debug( "%s", "Card removed\n" );
      break //卡片移出
    }
  }
  return 0
}
//ISO14443 A/B 复位工作场
func ICC_PCD_ResetPCDrf() int {
  C.PiccClose()
  ret := C.PiccOpen()
  //nc_iso14443_debug("ICC_PCD_ResetPCDrf. ret = %d\n", ret);
  return int(ret)
}
func ICC_PCD_APDUCommand(in []byte, inlen int, out []byte, outlen *int, maxsize int, lc, le byte) int {
  var ApduSend C.APDU_SEND
  var ApduResp C.APDU_RESP
  ApduSend.Command[0] = C.uchar(in[0])
  ApduSend.Command[1] = C.uchar(in[1])
  ApduSend.Command[2] = C.uchar(in[2])
  ApduSend.Command[3] = C.uchar(in[3])
  ApduSend.Lc = C.ushort(lc)
  for i := 0; i < int(lc); i++ {
    ApduSend.DataIn[i] = C.uchar(in[5+i])
  }
  if le != 0 { //此处须填非0值,若le非0则填实际值,否则固定填256
    ApduSend.Le = C.ushort(le)
  } else {
    ApduSend.Le = 256
  }
  fmt.Printf("->APDU:%x\n", in[0:inlen])
  ret := C.PiccIsoCommand(C.uchar(PiccCid), &ApduSend, &ApduResp)
  if ret != 0 {
    return int(ret)
  }
  if (le != 0) && (byte(ApduResp.LenOut) != le) {
  }
  if int(ApduResp.LenOut+2) < maxsize { //还有两个字节的状态字节 SWA/SWB
    //memcpy( out, &ApduResp.DataOut[0], ApduResp.LenOut );
    for i := 0; i < int(ApduResp.LenOut); i++ {
      out[i] = byte(ApduResp.DataOut[i])
    }
    out[ApduResp.LenOut] = byte(ApduResp.SWA)
    out[ApduResp.LenOut+1] = byte(ApduResp.SWB)
  } else {
    //memcpy( out, &ApduResp.DataOut[0], maxsize-2 );
    for i := 0; i < maxsize-2; i++ {
      out[i] = byte(ApduResp.DataOut[i])
    }
    out[ApduResp.LenOut] = byte(ApduResp.SWA)
    out[ApduResp.LenOut+1] = byte(ApduResp.SWB)
  }
  *outlen = int(ApduResp.LenOut + 2)
  fmt.Printf("<-APDU:%x\n", out[0:ApduResp.LenOut+2])
  return 0
}
func main() {
  fmt.Println("Hello Go")
  ret := ICC_PCD_Init()
  if ret == 0 {
    fmt.Println("ICC PCD init ok!")
    ICC_PCD_SysCfg(DEF_PCD_SeleTypeA, 0, 1)
    for i := 0; i < 100; i++ {
      ret = ICC_PCD_Request(DEF_PCD_SeleTypeA)
      if ret == 0 {
        fmt.Println("find card ok!")
      } else {
        fmt.Println("not find card!")
      }
    }
  } else {
    fmt.Printf("ICC PCD init err!,code=%d\n", ret)
  }
  name := ""
  fmt.Println("over!press any key to continue: ")
  fmt.Scanln(&name)
}


package card
import (
  "encoding/hex"
  "fmt"
  "go8583/drivers"
  "math/rand"
  "time"
)
func ICF_GetChallenge8B(Rnd []byte, ich int) int {
  CmdBuffer[0] = 0x00 //CLA
  CmdBuffer[1] = 0x84 //INS
  CmdBuffer[2] = 0x00 //P1
  CmdBuffer[3] = 0x00 //P2
  CmdBuffer[4] = 0x08 //Le
  rcode := drivers.ICC_APDU_Exchange(ich, CmdBuffer, 5, RcvBuffer, &Grcv_Len, 260, 0, CmdBuffer[4])
  if rcode != 0 {
    fmt.Printf("ICC_APDU_Exchange err,code=%d\n", rcode)
    return rcode
  }
  if Grcv_Len < 2 {
    return 2
  }
  rcode = ((int(RcvBuffer[Grcv_Len-2]) << 8) | int(RcvBuffer[Grcv_Len-1]))
  if rcode != 0x9000 {
    return rcode
  }
  copy(Rnd, RcvBuffer[0:8])
  return rcode
}
func ICF_SelectAID(AID []byte, ilen int, ich int) int {
  CmdBuffer[0] = 0x00       //CLA
  CmdBuffer[1] = 0xA4       //INS
  CmdBuffer[2] = 0x04       //P1
  CmdBuffer[3] = 0x00       //P2
  CmdBuffer[4] = byte(ilen) //Lc
  copy(CmdBuffer[5:], AID[0:ilen])
  rcode := drivers.ICC_APDU_Exchange(ich, CmdBuffer, ilen+5, RcvBuffer, &Grcv_Len, 260, CmdBuffer[4], 0)
  if rcode != 0 {
    fmt.Printf("ICC_APDU_Exchange err,code=%d\n", rcode)
    return rcode
  }
  if Grcv_Len < 2 {
    return 2
  }
  rcode = ((int(RcvBuffer[Grcv_Len-2]) << 8) | int(RcvBuffer[Grcv_Len-1]))
  if rcode != 0x9000 {
    return rcode
  }
  return rcode
}
//{9f38 18 9f66049f02069f03069f1a0295055f2a029a039c019f3704}
func UP_GPO(pdoc []byte, lenth byte, ich int) int {
  CmdBuffer[0] = 0x80      //CLA
  CmdBuffer[1] = 0xA8      //INS
  CmdBuffer[2] = 0x00      //P1
  CmdBuffer[3] = 0x00      //P2 ‘01’用于ED(电子存折,需要个人密码PIN ‘02’用于EP(电子钱包)
  CmdBuffer[4] = lenth + 2 //Lc
  CmdBuffer[5] = 0x83
  CmdBuffer[6] = lenth
  copy(CmdBuffer[7:], pdoc[0:lenth])
  rcode := drivers.ICC_APDU_Exchange(ich, CmdBuffer, int(lenth+8), RcvBuffer, &Grcv_Len, 260, CmdBuffer[4], 0)
  if rcode != 0 {
    fmt.Printf("ICC_APDU_Exchange err,code=%d\n", rcode)
    return rcode
  }
  if Grcv_Len < 2 {
    return 2
  }
  rcode = ((int(RcvBuffer[Grcv_Len-2]) << 8) | int(RcvBuffer[Grcv_Len-1]))
  if rcode != 0x9000 {
    return rcode
  }
  return rcode
}
/*
*双免GPO组包
 */
//{9f38 18 9f66049f02069f03069f1a0295055f2a029a039c019f3704}
func UP_qUICS(money int, opdt string, ich int) int {
  t9F66 := "........"                  //交易属性
  t9F02 := fmt.Sprintf("%012d", money) //授权金额
  t9F03 := "000000000000"
  t9F1A := "0156"
  t95 := "0000000000"
  t5F2A := "0156"
  t9A := opdt
  t9C := "00"
  rand.Seed(time.Now().Unix())
  t9F37 := fmt.Sprintf("%08x", rand.Int31())
  pdoc := t9F66 + t9F02 + t9F03 + t9F1A + t95 + t5F2A + t9A + t9C + t9F37
  fmt.Printf("pdoc:%s\n", pdoc)
  bpdoc, err := hex.DecodeString(pdoc)
  if err != nil {
    fmt.Printf("DecodeString error:%s\n", err.Error())
  }
  fmt.Printf("bpdoc:%x\n", bpdoc)
  return UP_GPO(bpdoc, byte(len(bpdoc)), ich)
}


root@b503_lcd:/app/city_app/opt# ./cardlib
Hello Go
ATQA:08000000
SAK:20
UID:5deaa62a000000000000000000000000
find card ok!
ATQA:04000000
SAK:28
UID:2f7bb136000000000000000000000000
find card ok!
->APDU:00a404000e325041592e5359532e4444463031
<-APDU:6f30840e325041592e5359532e4444463031a51ebf0c1b61194f08a000000333010101500a50424f432044454249548701019000
ICF_SelectAID ok!
[{6f 30 840e325041592e5359532e4444463031a51ebf0c1b61194f08a000000333010101500a50424f43204445424954870101} {84 0e 325041592e5359532e4444463031} {a5 1e bf0c1b61194f08a000000333010101500a50424f43204445424954870101} {bf0c 1b 61194f08a000000333010101500a50424f43204445424954870101} {61 19 4f08a000000333010101500a50424f43204445424954870101} {4f 08 a000000333010101} {50 0a 50424f43204445424954} {87 01 01}]


相关文章
|
6天前
|
Linux 编译器 Android开发
FFmpeg开发笔记(九)Linux交叉编译Android的x265库
在Linux环境下,本文指导如何交叉编译x265的so库以适应Android。首先,需安装cmake和下载android-ndk-r21e。接着,下载x265源码,修改crosscompile.cmake的编译器设置。配置x265源码,使用指定的NDK路径,并在配置界面修改相关选项。随后,修改编译规则,编译并安装x265,调整pc描述文件并更新PKG_CONFIG_PATH。最后,修改FFmpeg配置脚本启用x265支持,编译安装FFmpeg,将生成的so文件导入Android工程,调整gradle配置以确保顺利运行。
54 1
FFmpeg开发笔记(九)Linux交叉编译Android的x265库
|
6天前
|
Unix Linux Shell
FFmpeg开发笔记(八)Linux交叉编译Android的FFmpeg库
在Linux环境下交叉编译Android所需的FFmpeg so库,首先下载`android-ndk-r21e`,然后解压。接着,上传FFmpeg及相关库(如x264、freetype、lame)源码,修改相关sh文件,将`SYSTEM=windows-x86_64`改为`SYSTEM=linux-x86_64`并删除回车符。对x264的configure文件进行修改,然后编译x264。同样编译其他第三方库。设置环境变量`PKG_CONFIG_PATH`,最后在FFmpeg源码目录执行配置、编译和安装命令,生成的so文件复制到App工程指定目录。
51 9
FFmpeg开发笔记(八)Linux交叉编译Android的FFmpeg库
|
1天前
|
编解码 Linux 5G
FFmpeg开发笔记(二十)Linux环境给FFmpeg集成AVS3解码器
AVS3,中国制定的第三代音视频标准,是首个针对8K和5G的视频编码标准,相比AVS2和HEVC性能提升约30%。uavs3d是AVS3的解码器,支持8K/60P实时解码,且在各平台有优秀表现。要为FFmpeg集成AVS3解码器libuavs3d,需从GitHub下载最新源码,解压后配置、编译和安装。之后,重新配置FFmpeg,启用libuavs3d并编译安装,通过`ffmpeg -version`确认成功集成。
11 0
FFmpeg开发笔记(二十)Linux环境给FFmpeg集成AVS3解码器
|
3天前
|
运维 监控 关系型数据库
【Zabbix 6(1),Linux运维组件化开发教程
【Zabbix 6(1),Linux运维组件化开发教程
|
6天前
|
Linux C语言
|
6天前
|
安全 Linux Android开发
FFmpeg开发笔记(十六)Linux交叉编译Android的OpenSSL库
该文介绍了如何在Linux服务器上交叉编译Android的FFmpeg库以支持HTTPS视频播放。首先,从GitHub下载openssl源码,解压后通过编译脚本`build_openssl.sh`生成64位静态库。接着,更新环境变量加载openssl,并编辑FFmpeg配置脚本`config_ffmpeg_openssl.sh`启用openssl支持。然后,编译安装FFmpeg。最后,将编译好的库文件导入App工程的相应目录,修改视频链接为HTTPS,App即可播放HTTPS在线视频。
30 3
FFmpeg开发笔记(十六)Linux交叉编译Android的OpenSSL库
|
6天前
|
Ubuntu 算法 Linux
嵌入式Linux的学习误区
该文指出了学习嵌入式Linux开发的两个常见误区。一是过分专注于学习桌面或服务器版Linux,而非关注嵌入式开发本身,实际上只需熟悉基本操作即可。二是试图在没有基础的情况下直接阅读Linux内核源代码,这是不切实际的,应先建立基础知识再进行源码学习。文章还提到了在嵌入式系统中获取和处理屏幕数据的示例,包括使用gsnap工具将framebuffer数据转为图像,以及涉及的交叉编译过程。
12 0
|
6天前
|
前端开发 Linux iOS开发
【Flutter前端技术开发专栏】Flutter在桌面应用(Windows/macOS/Linux)的开发实践
【4月更文挑战第30天】Flutter扩展至桌面应用开发,允许开发者用同一代码库构建Windows、macOS和Linux应用,提高效率并保持平台一致性。创建桌面应用需指定目标平台,如`flutter create -t windows my_desktop_app`。开发中注意UI适配、性能优化、系统交互及测试部署。UI适配利用布局组件和`MediaQuery`,性能优化借助`PerformanceLogging`、`Isolate`和`compute`。
【Flutter前端技术开发专栏】Flutter在桌面应用(Windows/macOS/Linux)的开发实践
|
6天前
|
编解码 Linux
FFmpeg开发笔记(十二)Linux环境给FFmpeg集成libopus和libvpx
在《FFmpeg开发实战》一书中,介绍了如何在Linux环境下为FFmpeg集成libopus和libvpx,以支持WebM格式的Opus和VP8/VP9编码。首先,下载并安装libopus。接着,下载并安装libvpx。最后,在FFmpeg源码目录下,重新配置FFmpeg,启用libopus和libvpx,编译并安装。通过`ffmpeg -version`检查版本信息,确认libopus和libvpx已启用。
33 1
FFmpeg开发笔记(十二)Linux环境给FFmpeg集成libopus和libvpx
|
6天前
|
编解码 Linux
FFmpeg开发笔记(十)Linux环境给FFmpeg集成vorbis和amr
在Linux环境下,为FFmpeg添加对AAC、MP3、OGG和AMR音频格式的支持,需安装libogg、libvorbis和opencore-amr库。首先,从官方源下载各库的最新源码,如libogg-1.3.5、libvorbis-1.3.7和opencore-amr-0.1.6,然后解压并依次执行`./configure`、`make`和`make install`进行编译安装。接着,在FFmpeg源码目录中,使用`./configure`命令重新配置,并重新编译安装FFmpeg。最后,验证FFmpeg版本信息确认已启用ogg和amr支持。
24 0
FFmpeg开发笔记(十)Linux环境给FFmpeg集成vorbis和amr