diff --git a/2.0/nfc/crc.go b/2.0/nfc/crc.go new file mode 100644 index 0000000..dd025b7 --- /dev/null +++ b/2.0/nfc/crc.go @@ -0,0 +1,106 @@ +// Copyright (c) 2014, Robert Clausecker +// +// This program is free software: you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License as published by the +// Free Software Foundation, version 3. +// +// 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 Lesser General Public License +// along with this program. If not, see + +// The code in this file is a very direct translation of the code in +// iso14443-subr.c of the libnfc version 1.7.0 translated to Go for performance +// reasons. + +package nfc + +// Calculate an ISO 14443a CRC. Code translated from the code in +// iso14443a_crc(). +func ISO14443aCRC(data []byte) [2]byte { + crc := uint32(0x6363) + for _, bt := range data { + bt ^= uint8(crc & 0xff) + bt ^= bt << 4 + bt32 := uint32(bt) + crc = (crc >> 8) ^ (bt32 << 8) ^ (bt32 << 3) ^ (bt32 >> 4) + } + + return [2]byte{byte(crc & 0xff), byte((crc >> 8) & 0xff)} +} + +// Calculate an ISO 14443a CRC and append it to the supplied slice. +func AppendISO14443aCRC(data []byte) []byte { + crc := ISO14443aCRC(data) + return append(data, crc[0], crc[1]) +} + +// Calculate an ISO 14443b CRC. Code translated from the code in +// iso14443b_crc(). +func ISO14443bCRC(data []byte) [2]byte { + crc := uint32(0xffff) + for _, bt := range data { + bt ^= uint8(crc & 0xff) + bt ^= bt << 4 + bt32 := uint32(bt) + crc = (crc >> 8) ^ (bt32 << 8) ^ (bt32 << 3) ^ (bt32 >> 4) + } + + return [2]byte{byte(crc & 0xff), byte((crc >> 8) & 0xff)} +} + +// Calculate an ISO 14443b CRC and append it to the supplied slice. +func AppendISO14443bCRC(data []byte) []byte { + crc := ISO14443bCRC(data) + return append(data, crc[0], crc[1]) +} + +// Locate historical bytes according to ISO/IEC 14443-4 sec. 5.2.7. Return nil +// if that fails. +func ISO14443aLocateHistoricalBytes(ats []byte) []byte { + if len(ats) > 0 { + offset := 1 + if ats[0]&0x10 != 0 { + offset++ + } + + if ats[1]&0x20 != 0 { + offset++ + } + + if ats[2]&0x40 != 0 { + offset++ + } + + if len(ats) > offset { + return ats[offset:] + } + } + + return nil +} + +// Add cascade tags (0x88) in UID. See ISO/IEC 14443-3 sec. 6.4.4. +func ISO14443CascadeUID(uid []byte) (cascadedUID []byte) { + switch len(uid) { + case 7: + cascadedUID = make([]byte, 8) + cascadedUID[0] = 0x88 + copy(cascadedUID[1:], uid) + case 10: + cascadedUID = make([]byte, 12) + cascadedUID[0] = 0x88 + copy(cascadedUID[1:4], uid) + cascadedUID[4] = 0x88 + copy(cascadedUID[5:], uid[3:]) + case 4: + fallthrough + default: + cascadedUID = append(cascadedUID, uid...) + } + + return +} diff --git a/2.0/nfc/device.go b/2.0/nfc/device.go new file mode 100644 index 0000000..3bf6a09 --- /dev/null +++ b/2.0/nfc/device.go @@ -0,0 +1,460 @@ +// Copyright (c) 2014, Robert Clausecker +// +// This program is free software: you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License as published by the +// Free Software Foundation, version 3. +// +// 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 Lesser General Public License +// along with this program. If not, see + +package nfc + +// #include +// #include +import "C" +import "unsafe" +import "errors" +import "fmt" + +// NFC device +type Device struct { + d **C.nfc_device +} + +// Return a pointer to the wrapped nfc_device. This is useful if you try to use +// this wrapper to wrap other C code that builds onto the libnfc. +func (d Device) Pointer() uintptr { + return uintptr(unsafe.Pointer(*d.d)) +} + +// Open an NFC device. See documentation of Open() for more details +func (c *context) open(conn string) (d Device, err error) { + c.m.Lock() + defer c.m.Unlock() + c.initContext() + + cs, err := newConnstring(conn) + if err != nil { + return + } + + defer cs.Free() + + dev := C.nfc_open(c.c, cs.ptr) + + if dev == nil { + err = errors.New("cannot open NFC device") + return + } + + d = Device{&dev} + return +} + +// open a connection to an NFC device. If conn is "", the first available device +// will be used. If this operation fails, check the log on stderr for more +// details as the libnfc is not particulary verbose to us. +// +// Depending on the desired operation mode, the device needs to be configured +// by using InitiatorInit() or TargetInit(), optionally followed by manual +// tuning of the parameters if the default parameters are not suiting your +// goals. +func Open(conn string) (Device, error) { + return theContext.open(conn) +} + +// the error returned by the last operation on d. Every function that wraps some +// functions operating on an nfc_device should call this function and return the +// result. This wraps nfc_device_get_last_error. +func (d Device) LastError() error { + if *d.d == nil { + return errors.New("device closed") + } + + err := Error(C.nfc_device_get_last_error(*d.d)) + + if err == 0 { + return nil + } + + return err +} + +// Close an NFC device. +func (d Device) Close() error { + if *d.d == nil { + // closing a closed device is a nop + return nil + } + + *d.d = nil + C.nfc_close(*d.d) + + return nil +} + +// Abort current running command. Some commands (ie. TargetInit()) are blocking +// functions and will return only in particular conditions (ie. external +// initiator request). This function attempt to abort the current running +// command. +func (d Device) AbortCommand() error { + if *d.d == nil { + return errors.New("device closed") + } + + return Error(C.nfc_abort_command(*d.d)) +} + +// Turn NFC device in idle mode. In initiator mode, the RF field is turned off +// and the device is set to low power mode (if avaible); In target mode, the +// emulation is stoped (no target available from external initiator) and the +// device is set to low power mode (if avaible). +func (d Device) Idle() error { + if *d.d == nil { + return errors.New("device closed") + } + + return Error(C.nfc_idle(*d.d)) +} + +// Print information about an NFC device. +func (d Device) Information() (string, error) { + if *d.d == nil { + return "", errors.New("device closed") + } + + var ptr *C.char + buflen := C.nfc_device_get_information_about(*d.d, &ptr) + + if buflen < 0 { + return "", Error(buflen) + } + + // documentation for nfc_device_get_information_about says that buflen + // contains the length of the string that is returned. Apparently, for + // some drivers, buflen is always 0 so we disregard it. + str := C.GoString(ptr) + C.nfc_free(unsafe.Pointer(ptr)) + + return str, nil +} + +// Returns the device's connection string. If the device has been closed before, +// this function returns the empty string. +func (d Device) Connection() string { + if *d.d == nil { + return "" + } + + cptr := C.nfc_device_get_connstring(*d.d) + return C.GoString(cptr) +} + +// Returns the device's name. This information is not enough to uniquely +// determine the device. +func (d Device) String() string { + if *d.d == nil { + return "" + } + + cptr := C.nfc_device_get_name(*d.d) + return C.GoString(cptr) +} + +// Return Go code that could be used to reproduce this device. +func (d Device) GoString() string { + if *d.d == nil { + return "nil" + } + + return fmt.Sprintf("nfc.Open(%q)", d.Connection()) +} + +// Set a device's integer-property value. Returns nil on success, otherwise an +// error. See integer constants in this package for possible properties. +func (d Device) SetPropertyInt(property, value int) error { + if *d.d == nil { + return errors.New("device closed") + } + + err := C.nfc_device_set_property_int(*d.d, C.nfc_property(property), C.int(value)) + + if err != 0 { + return Error(err) + } + + return nil +} + +// Set a device's boolean-property value. Returns nil on success, otherwise an +// error. See integer constants in this package for possible properties. +func (d Device) SetPropertyBool(property int, value bool) error { + if *d.d == nil { + return errors.New("device closed") + } + + err := C.nfc_device_set_property_bool(*d.d, C.nfc_property(property), C.bool(value)) + + if err != 0 { + return Error(err) + } + + return nil +} + +// Get supported modulations. Returns a slice of supported modulations or an +// error. Pass either TARGET or INITIATOR as mode. This function wraps +// nfc_device_get_supported_modulation() +func (d Device) SupportedModulations(mode int) ([]int, error) { + if *d.d == nil { + return nil, errors.New("device closed") + } + + // The documentation inside the libnfc is a bit unclear on how the + // array returned through supported_mt is to be threated. The code + // itself suggest that it points to an array of entries terminated with + // UNDEFINED = 0. + var mt_arr *C.nfc_modulation_type + ret := C.nfc_device_get_supported_modulation(*d.d, C.nfc_mode(mode), &mt_arr) + if ret != 0 { + return nil, Error(ret) + } + + mods := []int{} + type mod C.nfc_modulation_type + ptr := unsafe.Pointer(mt_arr) + + for *(*mod)(ptr) != 0 { + mods = append(mods, int(*(*mod)(ptr))) + ptr = unsafe.Pointer(uintptr(ptr) + unsafe.Sizeof(*mt_arr)) + } + + return mods, nil +} + +// Get suported baud rates. Returns either a slice of supported baud rates or an +// error. This function wraps nfc_device_get_supported_baud_rate(). +func (d Device) SupportedBaudRates(modulationType int) ([]int, error) { + if *d.d == nil { + return nil, errors.New("device closed") + } + + // The documentation inside the libnfc is a bit unclear on how the + // array returned through supported_mt is to be threated. The code + // itself suggest that it points to an array of entries terminated with + // UNDEFINED = 0. + var br_arr *C.nfc_baud_rate + ret := C.nfc_device_get_supported_baud_rate( + *d.d, + C.nfc_modulation_type(modulationType), + &br_arr, + ) + if ret != 0 { + return nil, Error(ret) + } + + brs := []int{} + type br C.nfc_baud_rate + ptr := unsafe.Pointer(br_arr) + + for *(*br)(ptr) != 0 { + brs = append(brs, int(*(*br)(ptr))) + ptr = unsafe.Pointer(uintptr(ptr) + unsafe.Sizeof(*br_arr)) + } + + return brs, nil +} + +// Initialize NFC device as an emulated tag. n contains the received byte count +// on success, or is meaningless on error. The current implementation will +// return the libnfc error code in case of error, but this is subject to change. +// The returned target tt will be the result of the modifications +// nfc_target_init() applies to t. Such modifications might happen if you set +// an Baud or DepMode to UNDEFINED. The fields will be updated with concrete +// values. timeout contains a timeout in milliseconds. +// +// This function initializes NFC device in target mode in order to emulate a tag +// as the specified target t. +// - Crc is handled by the device (HANDLE_CRC = true) +// - Parity is handled the device (HANDLE_PARITY = true) +// - Cryto1 cipher is disabled (ACTIVATE_CRYPTO1 = false) +// - Auto-switching in ISO14443-4 mode is enabled (AUTO_ISO14443_4 = true) +// - Easy framing is disabled (EASY_FRAMING = false) +// - Invalid frames are not accepted (ACCEPT_INVALID_FRAMES = false) +// - Multiple frames are not accepted (ACCEPT_MULTIPLE_FRAMES = false) +// - RF field is dropped +// +// Warning: Be aware that this function will wait (hang) until a command is +// received that is not part of the anti-collision. The RATS command for example +// would wake up the emulator. After this is received, the send and receive +// functions can be used. +// +// If timeout equals to 0, the function blocks indefinitely (until an error is +// raised or function is completed). If timeout equals to -1, the default +// timeout will be used. +func (d Device) TargetInit(t Target, rx []byte, timeout int) (n int, tt Target, err error) { + if *d.d == nil { + return ESOFT, t, errors.New("device closed") + } + + tar := (*C.nfc_target)(unsafe.Pointer(t.Marshall())) + defer C.free(unsafe.Pointer(tar)) + + n = int(C.nfc_target_init( + *d.d, tar, + (*C.uint8_t)(&rx[0]), C.size_t(len(rx)), + C.int(timeout), + )) + + if n < 0 { + err = Error(n) + } + + tt = unmarshallTarget(tar) + return +} + +// Send bytes and APDU frames. n contains the sent byte count on success, or is +// meaningless on error. The current implementation will return the libnfc +// error code in case of error, but this is subject to change. timeout contains +// a timeout in milliseconds. +// +// This function make the NFC device (configured as target) send byte frames +// (e.g. APDU responses) to the initiator. +// +// If timeout equals to 0, the function blocks indefinitely (until an error is +// raised or function is completed). If timeout equals to -1, the default +// timeout will be used. +func (d Device) TargetSendBytes(tx []byte, timeout int) (n int, err error) { + if *d.d == nil { + return ESOFT, errors.New("device closed") + } + + n = int(C.nfc_target_send_bytes( + *d.d, + (*C.uint8_t)(&tx[0]), C.size_t(len(tx)), + C.int(timeout), + )) + + if n < 0 { + err = Error(n) + } + + return +} + +// Receive bytes and APDU frames. n contains the received byte count on success, +// or is meaningless on error. The current implementation will return the libnfc +// error code in case of error, but this is subject to change. timeout contains +// a timeout in milliseconds. +// +// This function retrieves bytes frames (e.g. ADPU) sent by the initiator to the +// NFC device (configured as target). +// +// If timeout equals to 0, the function blocks indefinitely (until an error is +// raised or function is completed). If timeout equals to -1, the default +// timeout will be used. +func (d Device) TargetReceiveBytes(rx []byte, timeout int) (n int, err error) { + if *d.d == nil { + return ESOFT, errors.New("device closed") + } + + n = int(C.nfc_target_receive_bytes( + *d.d, + (*C.uint8_t)(&rx[0]), C.size_t(len(rx)), + C.int(timeout), + )) + + if n < 0 { + err = Error(n) + } + + return +} + +// Send raw bit-frames. Returns sent bits count on success, n contains the sent +// bit count on success, or is meaningless on error. The current implementation +// will return the libnfc error code in case of error, but this is subject to +// change. txPar has to have the same length as tx, an error will occur if this +// invariant does not hold. +// +// tx contains a byte slice of the frame that needs to be transmitted. txLength +// contains its length in bits. txPar contains a byte slice of the corresponding +// parity bits needed to send per byte. +// +// his function can be used to transmit (raw) bit-frames to the initiator using +// the specified NFC device (configured as target). +func (d Device) TargetSendBits(tx []byte, txPar []byte, txLength uint) (n int, err error) { + if *d.d == nil { + return ESOFT, errors.New("device closed") + } + + if len(tx) != len(txPar) { + return ESOFT, errors.New("invariant doesn't hold") + } + + if uint(len(tx))*8 < txLength { + return ESOFT, errors.New("slice shorter than specified bit count") + } + + n = int(C.nfc_target_send_bits( + *d.d, + (*C.uint8_t)(&tx[0]), + C.size_t(txLength), + (*C.uint8_t)(&txPar[0]), + )) + + if n < 0 { + err = Error(n) + } + + return +} + +// Receive bit-frames. Returns received bits count on success, n contains the +// received bit count on success, or is meaningless on error. The current +// implementation will return the libnfc error code in case of error, but this +// is subject to change. rxPar has to have the same length as rx, an error will +// occur if this invariant does not hold. +// +// rx contains a byte slice of the frame that you want to receive. rxLength +// contains its length in bits. rxPar contains a byte slice of the corresponding +// parity bits received per byte. +// +// This function makes it possible to receive (raw) bit-frames. It returns all +// the messages that are stored in the FIFO buffer of the PN53x chip. It +// does not require to send any frame and thereby could be used to snoop frames +// that are transmitted by a nearby initiator. Check out the +// ACCEPT_MULTIPLE_FRAMES configuration option to avoid losing transmitted +// frames. +func (d Device) TargetTransceiveBits(rx []byte, rxPar []byte, rxLength uint) (n int, err error) { + if *d.d == nil { + return ESOFT, errors.New("device closed") + } + + if len(rx) != len(rxPar) { + return ESOFT, errors.New("invariant doesn't hold") + } + + if uint(len(rx))*8 < rxLength { + return ESOFT, errors.New("slice shorter than specified bit count") + } + + n = int(C.nfc_target_receive_bits( + *d.d, + (*C.uint8_t)(&rx[0]), + C.size_t(rxLength), + (*C.uint8_t)(&rxPar[0]), + )) + + if n < 0 { + err = Error(n) + } + + return +} diff --git a/2.0/nfc/etc.go b/2.0/nfc/etc.go new file mode 100644 index 0000000..c3b55a7 --- /dev/null +++ b/2.0/nfc/etc.go @@ -0,0 +1,168 @@ +// Copyright (c) 2014, Robert Clausecker +// +// This program is free software: you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License as published by the +// Free Software Foundation, version 3. +// +// 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 Lesser General Public License +// along with this program. If not, see + +package nfc + +/* +#cgo LDFLAGS: -lnfc +#include +#include + +struct device_listing { + int count; // is an error code if negative + char *entries; + +}; + +struct device_listing list_devices_wrapper(nfc_context *context) { + size_t cstr_len = 16, actual_count; + nfc_connstring *cstr = NULL, *cstr_tmp; + struct device_listing dev; + + // call nfc_list_devices as long as our array might be too short + for (;;) { + cstr_tmp = realloc(cstr, cstr_len * sizeof *cstr); + if (cstr_tmp == NULL) { + actual_count = NFC_ESOFT; + break; + } + + cstr = cstr_tmp; + actual_count = nfc_list_devices(context, cstr, cstr_len); + + // also covers the case where actual_count is an error + if (actual_count < cstr_len) break; + + cstr_len += 16; + } + + dev.count = actual_count; + dev.entries = (char*)cstr; + + return dev; +} +*/ +import "C" +import "errors" +import "sync" +import "unsafe" + +// Get library version. This function returns the version of the libnfc wrapped +// by this package as returned by nfc_version(). +func Version() string { + cstr := C.nfc_version() + return C.GoString(cstr) +} + +// NFC context +type context struct { + c *C.nfc_context + m sync.Mutex +} + +// Initialize the library. This is an internal function that assumes that the +// appropriate lock is held by the surrounding function. This function is a nop +// if the library is already initialized. This function panics if the library +// cannot be initialized for any reason. +func (c *context) initContext() { + if c.c != nil { + return + } + + C.nfc_init(&c.c) + + if c.c == nil { + panic(errors.New("cannot initialize libnfc")) + } + + return +} + +// deinitialize the library +func (c *context) deinitContext() { + c.m.Lock() + defer c.m.Unlock() + + if c.c != nil { + C.nfc_exit(c.c) + } +} + +// Scan for discoverable supported devices (ie. only available for some drivers. +// Returns a slice of strings that can be passed to Open() to open the devices +// found. +func ListDevices() ([]string, error) { + return theContext.listDevices() +} + +// See ListDevices() for documentation +func (c *context) listDevices() ([]string, error) { + c.m.Lock() + defer c.m.Unlock() + c.initContext() + + dev := C.list_devices_wrapper(c.c) + defer C.free(unsafe.Pointer(dev.entries)) + if dev.count < 0 { + return nil, Error(dev.count) + } + + dev_entries := C.GoBytes(unsafe.Pointer(dev.entries), dev.count*BufsizeConnstring) + + devices := make([]string, dev.count) + for i := range devices { + charptr := (*C.char)(unsafe.Pointer(&dev_entries[i*BufsizeConnstring])) + devices[i] = connstring{charptr}.String() + } + + return devices, nil +} + +// Connection string +type connstring struct { + ptr *C.char +} + +func (c connstring) String() string { + str := C.GoStringN(c.ptr, BufsizeConnstring) + i := 0 + + for ; i < len(str) && str[i] != '\000'; i++ { + } + + return str[:i+1] +} + +// Makes a connstring. Notice that the string must not be longer than +// 1023 characters. If "" is passed, return nil instead. Call Free() when the +// string is no longer in use. +func newConnstring(s string) (connstring, error) { + if s == "" { + return connstring{nil}, nil + } + + if len(s) >= BufsizeConnstring { + return connstring{nil}, errors.New("string too long for Connstring") + } + + return connstring{C.CString(s)}, nil +} + +// Frees a connstring. Do not dereference afterwards. Free can be called on +// connstrings that contain a nil pointer. +func (c connstring) Free() { + if c.ptr != nil { + C.free(unsafe.Pointer(c.ptr)) + } +} diff --git a/2.0/nfc/initiator.go b/2.0/nfc/initiator.go new file mode 100644 index 0000000..b41fcd6 --- /dev/null +++ b/2.0/nfc/initiator.go @@ -0,0 +1,419 @@ +// Copyright (c) 2014, Robert Clausecker +// +// This program is free software: you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License as published by the +// Free Software Foundation, version 3. +// +// 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 Lesser General Public License +// along with this program. If not, see + +package nfc + +/* +#include +#include + +struct target_listing { + int count; // is an error code if negative + nfc_target *entries; +}; + +// this function works analogeous to list_devices_wrapper but for the function +// nfc_initiator_list_passive_targets. +struct target_listing list_targets_wrapper(nfc_device *device, const nfc_modulation nm) { + size_t targets_len = 16, actual_count; // 16 + nfc_target *targets = NULL, *targets_tmp; + struct target_listing tar; + + // call nfc_list_devices as long as our array might be too short + for (;;) { + targets_tmp = realloc(targets, targets_len * sizeof *targets); + if (targets_tmp == NULL) { + actual_count = NFC_ESOFT; + break; + } + + targets = targets_tmp; + actual_count = nfc_initiator_list_passive_targets(device, nm, targets, targets_len); + + // also covers the case where actual_count is an error + if (actual_count < targets_len) break; + + targets_len += 16; + } + + tar.count = actual_count; + tar.entries = targets; + + return tar; +} +*/ +import "C" +import "errors" +import "unsafe" + +// Send data to target then retrieve data from target. n contains received bytes +// count on success, or is meaningless on error. The current implementation will +// return the libnfc error code in case of error, but this is subject to change. +// This function will return EOVFLOW if more bytes are being received than the +// length of rx. +// +// The NFC device (configured as initiator) will transmit the supplied bytes +// (tx) to the target. It waits for the response and stores the received bytes +// in rx. If the received bytes exceed rx, the error status will be NFC_EOVFLOW +// and rx will contain len(rx) received bytes. +// +// If EASY_FRAMING option is disabled the frames will sent and received in raw +// mode: PN53x will not handle input neither output data. +// +// The parity bits are handled by the PN53x chip. The CRC can be generated +// automatically or handled manually. Using this function, frames can be +// communicated very fast via the NFC initiator to the tag. +// +// Tests show that on average this way of communicating is much faster than +// using the regular driver/middle-ware (often supplied by manufacturers). +// +// Warning: The configuration option HANDLE_PARITY must be set to true (the +// default value). +// +// If timeout equals to 0, the function blocks indefinitely (until an error is +// raised or function is completed). If timeout equals to -1, the default +// timeout will be used. +func (d Device) InitiatorTransceiveBytes(tx, rx []byte, timeout int) (n int, err error) { + if *d.d == nil { + return ESOFT, errors.New("device closed") + } + + txptr := (*C.uint8_t)(&tx[0]) + rxptr := (*C.uint8_t)(&rx[0]) + + n = int(C.nfc_initiator_transceive_bytes( + *d.d, + txptr, C.size_t(len(tx)), + rxptr, C.size_t(len(rx)), + C.int(timeout), + )) + + if n < 0 { + err = Error(n) + } + + return +} + +// Transceive raw bit-frame to a target. n contains the received byte count on +// success, or is meaningless on error. The current implementation will return +// the libnfc error code in case of error, but this is subject to change. If +// txLength is longer than the supplied slice, an error will occur. txPar has to +// have the same length as tx, dito for rxPar and rx. An error will occur if any +// of these invariants do not hold. +// +// tx contains a byte slice of the frame that needs to be transmitted. txLength +// contains its length in bits. +// +// For example the REQA (0x26) command (the first anti-collision command of +// ISO14443-A) must be precise 7 bits long. This is not possible using +// Device.InitiatorTransceiveBytes(). With that function you can only +// communicate frames that consist of full bytes. When you send a full byte (8 +// bits + 1 parity) with the value of REQA (0x26), a tag will simply not +// respond. +// +// txPar contains a byte slice of the corresponding parity bits needed to send +// per byte. +// +// For example if you send the SELECT_ALL (0x93, 0x20) = [ 10010011, 00100000 ] +// command, you have to supply the following parity bytes (0x01, 0x00) to define +// the correct odd parity bits. This is only an example to explain how it works, +// if you just are sending two bytes with ISO14443-A compliant parity bits you +// better can use the Device.InitiatorTransceiveBytes() method. +// +// rx will contain the response from the target. This function will return +// EOVFLOW if more bytes are received than the length of rx. rxPar contains a +// byte slice of the corresponding parity bits. +// +// The NFC device (configured as initiator) will transmit low-level messages +// where only the modulation is handled by the PN53x chip. Construction of the +// frame (data, CRC and parity) is completely done by libnfc. This can be very +// useful for testing purposes. Some protocols (e.g. MIFARE Classic) require to +// violate the ISO14443-A standard by sending incorrect parity and CRC bytes. +// Using this feature you are able to simulate these frames. +func (d Device) InitiatorTransceiveBits(tx, txPar []byte, txLength uint, rx, rxPar []byte) (n int, err error) { + if *d.d == nil { + return ESOFT, errors.New("device closed") + } + + if len(tx) != len(txPar) || len(rx) != len(rxPar) { + return ESOFT, errors.New("invariant doesn't hold") + } + + if uint(len(tx))*8 < txLength { + return ESOFT, errors.New("slice shorter than specified bit count") + } + + txptr := (*C.uint8_t)(&tx[0]) + txparptr := (*C.uint8_t)(&txPar[0]) + rxptr := (*C.uint8_t)(&rx[0]) + rxparptr := (*C.uint8_t)(&rxPar[0]) + + n = int(C.nfc_initiator_transceive_bits( + *d.d, + txptr, C.size_t(txLength), txparptr, + rxptr, C.size_t(len(rx)), rxparptr, + )) + + if n < 0 { + err = Error(n) + } + + return +} + +// Send data to target then retrieve data from target with timing control. n +// contains the received byte count on success, or is meaningless on error. c +// will contain the actual number of cycles waited. The current implementation +// will return the libnfc error code in case of error, but this is subject to +// change. This function will return EOVFLOW if more bytes are being received +// than the length of rx. +// +// This function is similar to Device.InitiatorTransceiveBytes() with the +// following differences: +// +// - A precise cycles counter will indicate the number of cycles between emission & reception of frames. +// - Only modes with EASY_FRAMING option disabled are supported. +// - Overall communication with the host is heavier and slower. +// +// By default, the timer configuration tries to maximize the precision, which +// also limits the maximum cycle count before saturation / timeout. E.g. with +// PN53x it can count up to 65535 cycles, avout 4.8ms with a precision of about +// 73ns. If you're ok with the defaults, call this function with cycles = 0. If +// you need to count more cycles, set cycles to the maximum you exprect, but +// don't forget you'll loose in precision and it'll take more time before +// timeout, so don't abuse! +// +// Warning: The configuration option EASY_FRAMING must be set to false; the +// configuration option HANDLE_PARITY must be set to true (default value). +func (d Device) InitiatorTransceiveBytesTimed(tx, rx []byte, cycles uint32) (n int, c uint32, err error) { + if *d.d == nil { + return ESOFT, 0, errors.New("device closed") + } + + var cptr *C.uint32_t + *cptr = C.uint32_t(cycles) + + txptr := (*C.uint8_t)(&tx[0]) + rxptr := (*C.uint8_t)(&rx[0]) + + n = int(C.nfc_initiator_transceive_bytes_timed( + *d.d, + txptr, C.size_t(len(tx)), + rxptr, C.size_t(len(rx)), + cptr, + )) + + if n < 0 { + err = Error(n) + } + + c = uint32(*cptr) + + return +} + +// Transceive raw bit-frames to a target. n contains the received byte count on +// success, or is meaningless on error. c will contain the actual number of +// cycles waited. The current implementation will return the libnfc error code +// in case of error, but this is subject to change. If txLength is longer than +// the supplied slice, an error will occur. txPar has to have the same length as +// tx, dito for rxPar and rx. An error will occur if any of these invariants do +// not hold. +// +// This function is similar to Device.InitiatorTransceiveBits() with the +// following differences: +// +// - A precise cycles counter will indicate the number of cycles between emission & reception of frames. +// - Only modes with EASY_FRAMING option disabled are supported and CRC must be handled manually. +// - Overall communication with the host is heavier and slower. +// +// By default the timer configuration tries to maximize the precision, which +// also limits the maximum cycle count before saturation / timeout. E.g. with +// PN53x it can count up to 65535 cycles, avout 4.8ms with a precision of about +// 73ns. If you're ok with the defaults, call this function with cycles = 0. If +// you need to count more cycles, set cycles to the maximum you exprect, but +// don't forget you'll loose in precision and it'll take more time before +// timeout, so don't abuse! +// +// Warning: The configuration option EASY_FRAMING must be set to false; the +// configuration option HANDLE_CRC must be set to false; the configuration +// option HANDLE_PARITY must be set to true (the default value). +func (d Device) InitiatorTransceiveBitsTimed(tx, txPar []byte, txLength uint, rx, rxPar []byte, cycles uint32) (n int, c uint32, err error) { + if *d.d == nil { + return ESOFT, 0, errors.New("device closed") + } + + if len(tx) != len(txPar) || len(rx) != len(rxPar) { + return ESOFT, 0, errors.New("invariant doesn't hold") + } + + if uint(len(tx))*8 < txLength { + return ESOFT, 0, errors.New("slice shorter than specified bit count") + } + + var cptr *C.uint32_t + *cptr = C.uint32_t(cycles) + + txptr := (*C.uint8_t)(&tx[0]) + txparptr := (*C.uint8_t)(&txPar[0]) + rxptr := (*C.uint8_t)(&rx[0]) + rxparptr := (*C.uint8_t)(&rxPar[0]) + + n = int(C.nfc_initiator_transceive_bits_timed( + *d.d, + txptr, C.size_t(txLength), txparptr, + rxptr, C.size_t(len(rx)), rxparptr, + cptr, + )) + + c = uint32(*cptr) + + if n < 0 { + err = Error(n) + } + + return +} + +// Check target presence. Returns nil on success, an error otherwise. The +// target has to be selected before you can check its presence. To run the test, +// one or more commands will be sent to the target. +func (d Device) InitiatorTargetIsPresent(t Target) error { + if *d.d == nil { + return errors.New("device closed") + } + + ctarget := (*C.nfc_target)(unsafe.Pointer(t.Marshall())) + defer C.free(unsafe.Pointer(ctarget)) + + n := C.nfc_initiator_target_is_present(*d.d, ctarget) + if n != 0 { + return Error(n) + } + + return nil +} + +// Initialize NFC device as initiator (reader). After initialization it can be +// used to communicate to passive RFID tags and active NFC devices. The reader +// will act as initiator to communicate peer 2 peer (NFCIP) to other active NFC +// devices. The NFC device will be initialized with the following properties: +// * CRC is handled by the device (NP_HANDLE_CRC = true) +// * Parity is handled the device (NP_HANDLE_PARITY = true) +// * Cryto1 cipher is disabled (NP_ACTIVATE_CRYPTO1 = false) +// * Easy framing is enabled (NP_EASY_FRAMING = true) +// * Auto-switching in ISO14443-4 mode is enabled (NP_AUTO_ISO14443_4 = true) +// * Invalid frames are not accepted (NP_ACCEPT_INVALID_FRAMES = false) +// * Multiple frames are not accepted (NP_ACCEPT_MULTIPLE_FRAMES = false) +// * 14443-A mode is activated (NP_FORCE_ISO14443_A = true) +// * speed is set to 106 kbps (NP_FORCE_SPEED_106 = true) +// * Let the device try forever to find a target (NP_INFINITE_SELECT = true) +// * RF field is shortly dropped (if it was enabled) then activated again +func (d Device) InitiatorInit() error { + if *d.d == nil { + return errors.New("device closed") + } + + n := C.nfc_initiator_init(*d.d) + if n != 0 { + return Error(n) + } + + return nil +} + +// Initialize NFC device as initiator with its secure element initiator +// (reader). After initialization it can be used to communicate with the secure +// element. The RF field is deactivated in order to save power. +func (d Device) InitiatorInitSecureElement() error { + if *d.d == nil { + return errors.New("device closed") + } + + return Error(C.nfc_initiator_init_secure_element(*d.d)) +} + +// Select a passive or emulated tag. +func (d Device) InitiatorSelectPassiveTarget(m Modulation, initData []byte) (Target, error) { + if *d.d == nil { + return nil, errors.New("device closed") + } + + var pnt C.nfc_target + + n := C.nfc_initiator_select_passive_target( + *d.d, + C.nfc_modulation{C.nfc_modulation_type(m.Type), C.nfc_baud_rate(m.BaudRate)}, + (*C.uint8_t)(&initData[0]), + C.size_t(len(initData)), + &pnt) + if n != 0 { + return nil, Error(n) + } + + return unmarshallTarget(&pnt), nil +} + +// List passive or emulated tags. The NFC device will try to find the available +// passive tags. Some NFC devices are capable to emulate passive tags. The +// standards (ISO18092 and ECMA-340) describe the modulation that can be used +// for reader to passive communications. The chip needs to know with what kind +// of tag it is dealing with, therefore the initial modulation and speed (106, +// 212 or 424 kbps) should be supplied. +func (d Device) InitiatorListPassiveTargets(m Modulation) ([]Target, error) { + if *d.d == nil { + return nil, errors.New("device closed") + } + + mod := C.nfc_modulation{ + nmt: C.nfc_modulation_type(m.Type), + nbr: C.nfc_baud_rate(m.BaudRate), + } + + tar := C.list_targets_wrapper(*d.d, mod) + defer C.free(unsafe.Pointer(tar.entries)) + if tar.count < 0 { + return nil, Error(tar.count) + } + + targets := make([]Target, tar.count) + for i := range targets { + // index the C array using pointer arithmetic + ptr := uintptr(unsafe.Pointer(tar.entries)) + uintptr(i)*unsafe.Sizeof(*tar.entries) + targets[i] = unmarshallTarget((*C.nfc_target)(unsafe.Pointer(ptr))) + } + + return targets, nil +} + +// Deselect a selected passive or emulated tag. After selecting and +// communicating with a passive tag, this function could be used to deactivate +// and release the tag. This is very useful when there are multiple tags +// available in the field. It is possible to use the +// InitiatorSelectPassiveTarget() method to select the first available tag, test +// it for the available features and support, deselect it and skip to the next +// tag until the correct tag is found. +func (d Device) InitiatorDeselectTarget() error { + if *d.d == nil { + return errors.New("device closed") + } + + n := C.nfc_initiator_deselect_target(*d.d) + if n != 0 { + return Error(n) + } + + return nil +} diff --git a/2.0/nfc/marshall.c b/2.0/nfc/marshall.c new file mode 100644 index 0000000..72474bd --- /dev/null +++ b/2.0/nfc/marshall.c @@ -0,0 +1,247 @@ +/*- + * Copyright (c) 2014, Robert Clausecker + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, version 3. + * + * 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 Lesser General Public License + * along with this program. If not, see + */ +#include +#include "marshall.h" + +/* see marshall.h for what this code does */ + +extern int +getModulationType(const nfc_target *nt) +{ + return (nt->nm.nmt); +} + +extern void +unmarshallDEPTarget(struct DEPTarget *dt, const nfc_target *nt) +{ + const nfc_dep_info *di = &nt->nti.ndi; + + memcpy(dt->NFCID3, di->abtNFCID3, sizeof(dt->NFCID3)); + dt->DID = di->btDID; + dt->BS = di->btBS; + dt->BR = di->btBR; + dt->TO = di->btTO; + dt->PP = di->btPP; + memcpy(dt->GB, di->abtGB, sizeof(dt->GB)); + dt->GBLen = di->szGB; + dt->DepMode = di->ndm; + + dt->Baud = nt->nm.nbr; +} + +extern void +marshallDEPTarget(nfc_target *nt, const struct DEPTarget *dt) +{ + nfc_dep_info *di = &nt->nti.ndi; + + memcpy(di->abtNFCID3, dt->NFCID3, sizeof(dt->NFCID3)); + di->btDID = dt->DID; + di->btBS = dt->BS; + di->btBR = dt->BR; + di->btTO = dt->TO; + di->btPP = dt->PP; + memcpy(di->abtGB, dt->GB, sizeof(dt->GB)); + di->szGB = dt->GBLen; + di->ndm = dt->DepMode; + + nt->nm.nbr = dt->Baud; + nt->nm.nmt = NMT_DEP; +} + +extern void +unmarshallISO14443aTarget(struct ISO14443aTarget *it, const nfc_target *nt) +{ + const nfc_iso14443a_info *ii = &nt->nti.nai; + + memcpy(it->Atqa, ii->abtAtqa, sizeof(it->Atqa)); + it->Sak = ii->btSak; + it->UIDLen = ii->szUidLen; + memcpy(it->UID, ii->abtUid, sizeof(it->UID)); + it->AtsLen = ii->szAtsLen; + memcpy(it->Ats, ii->abtAts, sizeof(it->Ats)); + + it->Baud = nt->nm.nbr; +} + +extern void +marshallISO14443aTarget(nfc_target *nt, const struct ISO14443aTarget *it) +{ + nfc_iso14443a_info *ii = &nt->nti.nai; + + memcpy(ii->abtAtqa, it->Atqa, sizeof(it->Atqa)); + ii->btSak = it->Sak; + ii->szUidLen = it->UIDLen; + memcpy(ii->abtUid, it->UID, sizeof(it->UID)); + ii->szAtsLen = it->AtsLen; + memcpy(ii->abtAts, it->Ats, sizeof(it->Ats)); + + nt->nm.nbr = it->Baud; + nt->nm.nmt = NMT_ISO14443A; +} + +extern void +unmarshallFelicaTarget(struct FelicaTarget *ft, const nfc_target *nt) +{ + const nfc_felica_info *fi = &nt->nti.nfi; + + ft->Len = fi->szLen; + ft->ResCode = fi->btResCode; + memcpy(ft->ID, fi->abtId, sizeof(ft->ID)); + memcpy(ft->Pad, fi->abtPad, sizeof(ft->Pad)); + memcpy(ft->SysCode, fi->abtSysCode, sizeof(ft->SysCode)); + + ft->Baud = nt->nm.nbr; +} + +extern void +marshallFelicaTarget(nfc_target *nt, const struct FelicaTarget *ft) +{ + nfc_felica_info *fi = &nt->nti.nfi; + + fi->szLen = ft->Len; + fi->btResCode = ft->ResCode; + memcpy(fi->abtId, ft->ID, sizeof(ft->ID)); + memcpy(fi->abtPad, ft->Pad, sizeof(ft->Pad)); + memcpy(fi->abtSysCode, ft->SysCode, sizeof(ft->SysCode)); + + nt->nm.nbr = ft->Baud; + nt->nm.nmt = NMT_FELICA; +} + +extern void +unmarshallISO14443bTarget(struct ISO14443bTarget *it, const nfc_target *nt) +{ + const nfc_iso14443b_info *ii = &nt->nti.nbi; + + memcpy(it->Pupi, ii->abtPupi, sizeof(it->Pupi)); + memcpy(it->ApplicationData, ii->abtApplicationData, sizeof(it->ApplicationData)); + memcpy(it->ProtocolInfo, ii->abtProtocolInfo, sizeof(it->ProtocolInfo)); + it->CardIdentifier = ii->ui8CardIdentifier; + + it->Baud = nt->nm.nbr; +} + +extern void +marshallISO14443bTarget(nfc_target *nt, const struct ISO14443bTarget *it) +{ + nfc_iso14443b_info *ii = &nt->nti.nbi; + + memcpy(ii->abtPupi, it->Pupi, sizeof(it->Pupi)); + memcpy(ii->abtApplicationData, it->ApplicationData, sizeof(it->ApplicationData)); + memcpy(ii->abtProtocolInfo, it->ProtocolInfo, sizeof(it->ProtocolInfo)); + ii->ui8CardIdentifier = it->CardIdentifier; + + nt->nm.nbr = it->Baud; + nt->nm.nmt = NMT_ISO14443B; +} + +extern void +unmarshallISO14443biTarget(struct ISO14443biTarget *it, const nfc_target *nt) +{ + const nfc_iso14443bi_info *ii = &nt->nti.nii; + + memcpy(it->DIV, ii->abtDIV, sizeof(it->DIV)); + it->VerLog = ii->btVerLog; + it->Config = ii->btConfig; + it->AtrLen = ii->szAtrLen; + memcpy(it->Atr, ii->abtAtr, sizeof(it->Atr)); + + it->Baud = nt->nm.nbr; +} + +extern void +marshallISO14443biTarget(nfc_target *nt, const struct ISO14443biTarget *it) +{ + nfc_iso14443bi_info *ii = &nt->nti.nii; + + memcpy(ii->abtDIV, it->DIV, sizeof(it->DIV)); + ii->btVerLog = it->VerLog; + ii->btConfig = it->Config; + ii->szAtrLen = it->AtrLen; + memcpy(ii->abtAtr, it->Atr, sizeof(it->Atr)); + + nt->nm.nbr = it->Baud; + nt->nm.nmt = NMT_ISO14443BI; +} + +extern void +unmarshallISO14443b2srTarget(struct ISO14443b2srTarget *it, const nfc_target *nt) +{ + const nfc_iso14443b2sr_info *ii = &nt->nti.nsi; + + memcpy(it->UID, ii->abtUID, sizeof(it->UID)); + + it->Baud = nt->nm.nbr; +} + +extern void +marshallISO14443b2srTarget(nfc_target *nt, const struct ISO14443b2srTarget *it) +{ + nfc_iso14443b2sr_info *ii = &nt->nti.nsi; + + memcpy(ii->abtUID, it->UID, sizeof(it->UID)); + + nt->nm.nbr = it->Baud; + nt->nm.nmt = NMT_ISO14443B2SR; +} + +extern void +unmarshallISO14443b2ctTarget(struct ISO14443b2ctTarget *it, const nfc_target *nt) +{ + const nfc_iso14443b2ct_info *ii = &nt->nti.nci; + + memcpy(it->UID, ii->abtUID, sizeof(it->UID)); + it->ProdCode = ii->btProdCode; + it->FabCode = ii->btFabCode; + + it->Baud = nt->nm.nbr; +} + +extern void +marshallISO14443b2ctTarget(nfc_target *nt, const struct ISO14443b2ctTarget *it) +{ + nfc_iso14443b2ct_info *ii = &nt->nti.nci; + + memcpy(ii->abtUID, it->UID, sizeof(it->UID)); + ii->btProdCode = it->ProdCode; + ii->btFabCode = it->FabCode; + + nt->nm.nbr = it->Baud; + nt->nm.nmt = NMT_ISO14443B2CT; +} + +extern void +unmarshallJewelTarget(struct JewelTarget *jt, const nfc_target *nt) +{ + const nfc_jewel_info *ji = &nt->nti.nji; + + memcpy(jt->SensRes, ji->btSensRes, sizeof(jt->SensRes)); + memcpy(jt->ID, ji->btId, sizeof(jt->ID)); + + jt->Baud = nt->nm.nbr; +} + +extern void +marshallJewelTarget(nfc_target *nt, const struct JewelTarget *jt) +{ + nfc_jewel_info *ji = &nt->nti.nji; + + memcpy(ji->btSensRes, jt->SensRes, sizeof(jt->SensRes)); + memcpy(ji->btId, jt->ID, sizeof(jt->ID)); + + nt->nm.nbr = jt->Baud; + nt->nm.nmt = NMT_JEWEL; +} diff --git a/2.0/nfc/marshall.h b/2.0/nfc/marshall.h new file mode 100644 index 0000000..3a8e262 --- /dev/null +++ b/2.0/nfc/marshall.h @@ -0,0 +1,140 @@ +/*- + * Copyright (c) 2014, Robert Clausecker + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, version 3. + * + * 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 Lesser General Public License + * along with this program. If not, see + */ +#ifndef MARSHALL_H +#define MARSHALL_H + +#include +#include +#include + +/* + * Marshalling code. The code in this translation unit marshalls between the + * nfc_xxx_info and the XxxTarget types. + * + * The marshalling code cannot easily be written in Go as the libnfc uses + * #pragma pack to change the alignment of some structures from something + * perfectly usably to something cgo cannot work with. Writing the marshalling + * code in straight C is also not possible as there is no easy way to refer to + * Go types from within C code. + * + * The following code uses a somewhat hacky approach: For each Go structure we + * create an equal C structure and hope that they match. Then we marshall the + * data in C and cast the pointers to corresponding Go type. + */ + +/* + * On all platforms where a Go port exists, the Go type int has the same + * attributes as the C type ptrdiff_t. + */ +typedef ptrdiff_t GoInt; + +/* return field nfc_target.nm.nmt */ +extern int getModulationType(const nfc_target*); + +/* functions to deal with specific targets */ + +struct DEPTarget { + uint8_t NFCID3[10]; + uint8_t DID; + uint8_t BS; + uint8_t BR; + uint8_t TO; + uint8_t PP; + uint8_t GB[48]; + GoInt GBLen; + GoInt DepMode; + GoInt Baud; +}; + +extern void unmarshallDEPTarget(struct DEPTarget*, const nfc_target*); +extern void marshallDEPTarget(nfc_target*, const struct DEPTarget*); + +struct ISO14443aTarget { + uint8_t Atqa[2]; + uint8_t Sak; + GoInt UIDLen; + uint8_t UID[10]; + GoInt AtsLen; + uint8_t Ats[254]; + GoInt Baud; +}; + +extern void unmarshallISO14443aTarget(struct ISO14443aTarget*, const nfc_target*); +extern void marshallISO14443aTarget(nfc_target*, const struct ISO14443aTarget*); + +struct FelicaTarget { + GoInt Len; + uint8_t ResCode; + uint8_t ID[8]; + uint8_t Pad[8]; + uint8_t SysCode[2]; + GoInt Baud; +}; + +extern void unmarshallFelicaTarget(struct FelicaTarget*, const nfc_target*); +extern void marshallFelicaTarget(nfc_target*, const struct FelicaTarget*); + +struct ISO14443bTarget { + uint8_t Pupi[4]; + uint8_t ApplicationData[4]; + uint8_t ProtocolInfo[3]; + uint8_t CardIdentifier; + GoInt Baud; +}; + +extern void unmarshallISO14443bTarget(struct ISO14443bTarget*, const nfc_target*); +extern void marshallISO14443bTarget(nfc_target*, const struct ISO14443bTarget*); + +struct ISO14443biTarget { + uint8_t DIV[4]; + uint8_t VerLog; + uint8_t Config; + GoInt AtrLen; + uint8_t Atr[33]; + GoInt Baud; +}; + +extern void unmarshallISO14443biTarget(struct ISO14443biTarget*, const nfc_target*); +extern void marshallISO14443biTarget(nfc_target*, const struct ISO14443biTarget*); + +struct ISO14443b2srTarget { + uint8_t UID[8]; + GoInt Baud; +}; + +extern void unmarshallISO14443b2srTarget(struct ISO14443b2srTarget*, const nfc_target*); +extern void marshallISO14443b2srTarget(nfc_target*, const struct ISO14443b2srTarget*); + +struct ISO14443b2ctTarget { + uint8_t UID[4]; + uint8_t ProdCode; + uint8_t FabCode; + GoInt Baud; +}; + +extern void unmarshallISO14443b2ctTarget(struct ISO14443b2ctTarget*, const nfc_target*); +extern void marshallISO14443b2ctTarget(nfc_target*, const struct ISO14443b2ctTarget*); + +struct JewelTarget { + uint8_t SensRes[2]; + uint8_t ID[4]; + GoInt Baud; +}; + +extern void unmarshallJewelTarget(struct JewelTarget*, const nfc_target*); +extern void marshallJewelTarget(nfc_target*, const struct JewelTarget*); + +#endif /* MARSHALL_H */ diff --git a/2.0/nfc/nfc.go b/2.0/nfc/nfc.go new file mode 100644 index 0000000..e7bf45b --- /dev/null +++ b/2.0/nfc/nfc.go @@ -0,0 +1,206 @@ +// Copyright (c) 2014, Robert Clausecker +// +// This program is free software: you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License as published by the +// Free Software Foundation, version 3. +// +// 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 Lesser General Public License +// along with this program. If not, see + +// This package wraps the libnfc to provide an API for Go. Most documentation +// was taken unchanged from the documentation inside the libnfc. Some functions +// and names have been altered to fit the conventions and idioms used in Go. +// +// This package is licensed under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation, version 3. +package nfc + +import "fmt" + +// Maximum length for an NFC connection string +const BufsizeConnstring = 1024 + +// Properties for (*Device).SetPropertyInt() and (*Device).SetPropertyBool(). +const ( + // Default command processing timeout + // Property value's (duration) unit is ms and 0 means no timeout (infinite). + // Default value is set by driver layer + TimeoutCommand = iota + + // Timeout between ATR_REQ and ATR_RES + // When the device is in initiator mode, a target is considered as mute + // if no valid ATR_RES is received within this timeout value. + // Default value for this property is 103 ms on PN53x based devices. + TimeoutATR + + // Timeout value to give up reception from the target in case of no answer. + // Default value for this property is 52 ms). + TimeoutCom + + // Let the PN53X chip handle the CRC bytes. This means that the chip + // appends the CRC bytes to the frames that are transmitted. It will + // parse the last bytes from received frames as incoming CRC bytes. They + // will be verified against the used modulation and protocol. If a frame + // is expected with incorrect CRC bytes this option should be disabled. + // Example frames where this is useful are the ATQA and UID+BCC that are + // transmitted without CRC bytes during the anti-collision phase of the + // ISO14443-A protocol. + HandleCRC + + // Parity bits in the network layer of ISO14443-A are by default + // generated and validated in the PN53X chip. This is a very convenient + // feature. On certain times though it is useful to get full control of + // the transmitted data. The proprietary MIFARE Classic protocol uses + // for example custom (encrypted) parity bits. For interoperability it + // is required to be completely compatible, including the arbitrary + // parity bits. When this option is disabled, the functions to + // communicating bits should be used. + HandleParity + + // This option can be used to enable or disable the electronic field of + // the NFC device. + ActivateField + + // The internal CRYPTO1 co-processor can be used to transmit messages + // encrypted. This option is automatically activated after a successful + // MIFARE Classic authentication. + ActivateCrypto1 + + // The default configuration defines that the PN53X chip will try + // indefinitely to invite a tag in the field to respond. This could be + // desired when it is certain a tag will enter the field. On the other + // hand, when this is uncertain, it will block the application. This + // option could best be compared to the (NON)BLOCKING option used by + // (socket)network programming. + InfiniteSelect + + // If this option is enabled, frames that carry less than 4 bits are + // allowed. According to the standards these frames should normally be + // handles as invalid frames. + AcceptInvalidFrames + + // If the NFC device should only listen to frames, it could be useful to + // let it gather multiple frames in a sequence. They will be stored in + // the internal FIFO of the PN53X chip. This could be retrieved by using + // the receive data functions. Note that if the chip runs out of bytes + // (FIFO = 64 bytes long), it will overwrite the first received frames, + // so quick retrieving of the received data is desirable. + AcceptMultipleFrames + + // This option can be used to enable or disable the auto-switching mode + // to ISO14443-4 is device is compliant. + // In initiator mode, it means that NFC chip will send RATS + // automatically when select and it will automatically poll for + // ISO14443-4 card when ISO14443A is requested. + // In target mode, with a NFC chip compliant (ie. PN532), the chip will + // emulate a 14443-4 PICC using hardware capability. + AutoISO14443_4 + + // Use automatic frames encapsulation and chaining. + EasyFraming + + // Force the chip to switch in ISO14443-A + ForceISO14443a + + // Force the chip to switch in ISO14443-B + ForceISO14443b + + // Force the chip to run at 106 kbps + ForceSpeed106 +) + +// NFC modulation types +const ( + ISO14443a = iota + 1 + Jewel + ISO14443b + ISO14443bi // pre-ISO14443B aka ISO/IEC 14443 B' or Type B' + ISO14443b2sr // ISO14443-2B ST SRx + ISO14443b2ct // ISO14443-2B ASK CTx + Felica + DEP +) + +// NFC baud rates. UNDEFINED is also a valid baud rate, albeit defined +// further below. +const ( + Nbr106 = iota + 1 + Nbr212 + Nbr424 + Nbr847 +) + +// NFC modes. An NFC device can either be a target or an initiator. +const ( + TargetMode = iota + InitiatorMode +) + +// NFC modulation structure. Use the supplied constants. +type Modulation struct { + Type int + BaudRate int +} + +// An error as reported by various methods of Device. If device returns an error +// that is not castable to Error, something outside on the Go side went wrong. +type Error int + +// Returns the same strings as nfc_errstr except if the error is not among the +// known errors. Instead of reporting an "Unknown error", Error() will return +// something like "Error -123". +func (e Error) Error() string { + if errorMessages[int(e)] == "" { + return fmt.Sprintf("Error %d", int(e)) + } + + return errorMessages[int(e)] +} + +// Error codes. Casted to Error, these yield all possible errors this package +// provides. Use nfc.Error(errorcode).Error() to get a descriptive string for an +// error code. +const ( + SUCCESS = 0 // Success (no error) + EIO = -1 // Input / output error, device may not be usable anymore without re-open it + EINVARG = -2 // Invalid argument(s) + EDEVNOTSUPP = -3 // Operation not supported by device + ENOTSUCHDEV = -4 // No such device + EOVFLOW = -5 // Buffer overflow + ETIMEOUT = -6 // Operation timed out + EOPABORTED = -7 // Operation aborted (by user) + ENOTIMPL = -8 // Not (yet) implemented + ETGRELEASED = -10 // Target released + ERFTRANS = -20 // Error during RF transmission + EMFCAUTHFAIL = -30 // MIFARE Classic: authentication failed + ESOFT = -80 // Software error (allocation, file/pipe creation, etc.) + ECHIP = -90 // Device's internal chip error +) + +// replicate error messages here because the libnfc is incapable of giving +// direct access to the error strings. Stupidly, only the error string for the +// error code of an nfc_device can be read out. +var errorMessages = map[int]string{ + SUCCESS: "success", + EIO: "input / output error", + EINVARG: "invalid argument(s)", + EDEVNOTSUPP: "not supported by device", + ENOTSUCHDEV: "no such device", + EOVFLOW: "buffer overflow", + ETIMEOUT: "timeout", + EOPABORTED: "operation aborted", + ENOTIMPL: "not (yet) implemented", + ETGRELEASED: "target released", + EMFCAUTHFAIL: "Mifare Classic authentication failed", + ERFTRANS: "RF transmission error", + ESOFT: "software error", + ECHIP: "device's internal chip error", +} + +// the global library context +var theContext *context = &context{} diff --git a/2.0/nfc/target.go b/2.0/nfc/target.go new file mode 100644 index 0000000..1e6a07b --- /dev/null +++ b/2.0/nfc/target.go @@ -0,0 +1,452 @@ +// Copyright (c) 2014, Robert Clausecker +// +// This program is free software: you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License as published by the +// Free Software Foundation, version 3. +// +// 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 Lesser General Public License +// along with this program. If not, see + +package nfc + +// #include +// #include "marshall.h" +import "C" +import "unsafe" +import "errors" + +// allocate space using C.malloc() for a C.nfc_target. +func mallocTarget() *C.nfc_target { + targetSize := C.size_t(unsafe.Sizeof(C.nfc_target{})) + return (*C.nfc_target)(C.malloc(targetSize)) +} + +// generic implementation for the String() functions of the Target interface. +// Notice that this panics when TargetString returns an error. +func tString(t Target) string { + str, err := TargetString(t, true) + + if err != nil { + panic(err) + } + + return str +} + +// Go wrapper for nfc_target. Since the nfc_target structure contains a union, +// we cannot directly map it to a Go type. Modulation() can be used to figure +// out what kind of modulation was used for this Target and what type an +// interface can be casted into. +// +// Marshall() returns a pointer to an nfc_target allocated with C.malloc() that +// contains the same data as the Target. Don't forget to C.free() the result of +// Marshall() afterwards. A runtime panic may occur if any slice referenced by a +// Target has been made larger than the maximum length mentioned in the +// respective comments. +type Target interface { + Modulation() Modulation + Marshall() uintptr + String() string // uses TargetString() with verbose = true +} + +// Make a string from a target with proper error reporting. This is a wrapper +// around str_nfc_target. +func TargetString(t Target, verbose bool) (string, error) { + ptr := unsafe.Pointer(t.Marshall()) + + var result *C.char = nil + + length := C.str_nfc_target(&result, (*C.nfc_target)(ptr), C.bool(verbose)) + defer C.nfc_free(unsafe.Pointer(result)) + + if length < 0 { + return "", Error(length) + } + + return C.GoStringN(result, C.int(length)), nil +} + +// Make a target from a pointer to an nfc_target. If the object you pass it not +// an nfc_target, undefined behavior occurs and your program is likely to blow +// up. +func UnmarshallTarget(ptr unsafe.Pointer) Target { + return unmarshallTarget((*C.nfc_target)(ptr)) +} + +// internal wrapper with C types for convenience +func unmarshallTarget(t *C.nfc_target) Target { + switch C.getModulationType(t) { + case ISO14443a: + r := unmarshallISO14443aTarget(t) + return &r + case Jewel: + r := unmarshallJewelTarget(t) + return &r + case ISO14443b: + r := unmarshallISO14443bTarget(t) + return &r + case ISO14443bi: + r := unmarshallISO14443biTarget(t) + return &r + case ISO14443b2sr: + r := unmarshallISO14443b2srTarget(t) + return &r + case ISO14443b2ct: + r := unmarshallISO14443b2ctTarget(t) + return &r + case Felica: + r := unmarshallFelicaTarget(t) + return &r + case DEP: + r := unmarshallDEPTarget(t) + return &r + default: + panic(errors.New("cannot determine target type")) + } +} + +// NFC D.E.P. (Data Exchange Protocol) active/passive mode +const ( + Undefined = iota + Passive + Active +) + +// NFC target information in D.E.P. (Data Exchange Protocol) see ISO/IEC 18092 +// (NFCIP-1). DEPTarget mirrors nfc_dep_info. +type DEPTarget struct { + NFCID3 [10]byte // NFCID3 + DID byte // DID + BS byte // supported send-bit rate + BR byte // supported receive-bit rate + TO byte // timeout value + PP byte // PP parameters + GB [48]byte // general bytes + GBLen int // length of the GB field + DepMode int // DEP mode + Baud int // Baud rate +} + +func (t *DEPTarget) String() string { + return tString(t) +} + +// Type is always DEP +func (t *DEPTarget) Modulation() Modulation { + return Modulation{DEP, t.Baud} +} + +// Make a DEPTarget from an nfc_dep_info +func unmarshallDEPTarget(c *C.nfc_target) DEPTarget { + var dt DEPTarget + + C.unmarshallDEPTarget((*C.struct_DEPTarget)(unsafe.Pointer(&dt)), c) + + return dt +} + +// Marshall() returns a pointer to an nfc_target allocated with C.malloc() that +// contains the same data as the Target. Don't forget to C.free() the result of +// Marshall() afterwards. A runtime panic may occur if any slice referenced by a +// Target has been made larger than the maximum length mentioned in the +// respective comments. +func (d *DEPTarget) Marshall() uintptr { + nt := mallocTarget() + dt := (*C.struct_DEPTarget)(unsafe.Pointer(d)) + + C.marshallDEPTarget(nt, dt) + + return uintptr(unsafe.Pointer(nt)) +} + +// NFC ISO14443A tag (MIFARE) information. ISO14443aTarget mirrors +// nfc_iso14443a_info. +type ISO14443aTarget struct { + Atqa [2]byte + Sak byte + UIDLen int // length of the Uid field + UID [10]byte + AtsLen int // length of the ATS field + // Maximal theoretical ATS is FSD-2, FSD=256 for FSDI=8 in RATS + Ats [254]byte // up to 254 bytes + Baud int // Baud rate +} + +func (t *ISO14443aTarget) String() string { + return tString(t) +} + +// Type is always ISO14443A +func (t *ISO14443aTarget) Modulation() Modulation { + return Modulation{ISO14443a, t.Baud} +} + +// Make an ISO14443aTarget from an nfc_iso14443a_info +func unmarshallISO14443aTarget(c *C.nfc_target) ISO14443aTarget { + var it ISO14443aTarget + + C.unmarshallISO14443aTarget((*C.struct_ISO14443aTarget)(unsafe.Pointer(&it)), c) + + return it +} + +// Marshall() returns a pointer to an nfc_target allocated with C.malloc() that +// contains the same data as the Target. Don't forget to C.free() the result of +// Marshall() afterwards. A runtime panic may occur if any slice referenced by a +// Target has been made larger than the maximum length mentioned in the +// respective comments. +func (d *ISO14443aTarget) Marshall() uintptr { + nt := mallocTarget() + it := (*C.struct_ISO14443aTarget)(unsafe.Pointer(d)) + + C.marshallISO14443aTarget(nt, it) + + return uintptr(unsafe.Pointer(nt)) +} + +// NFC FeLiCa tag information +type FelicaTarget struct { + Len uint + ResCode byte + ID [8]byte + Pad [8]byte + SysCode [2]byte + Baud int +} + +func (t *FelicaTarget) String() string { + return tString(t) +} + +// Type is always FELICA +func (t *FelicaTarget) Modulation() Modulation { + return Modulation{Felica, t.Baud} +} + +// Make an FelicaTarget from an nfc_felica_info +func unmarshallFelicaTarget(c *C.nfc_target) FelicaTarget { + var ft FelicaTarget + + C.unmarshallFelicaTarget((*C.struct_FelicaTarget)(unsafe.Pointer(&ft)), c) + + return ft +} + +// Marshall() returns a pointer to an nfc_target allocated with C.malloc() that +// contains the same data as the Target. Don't forget to C.free() the result of +// Marshall() afterwards. A runtime panic may occur if any slice referenced by a +// Target has been made larger than the maximum length mentioned in the +// respective comments. +func (d *FelicaTarget) Marshall() uintptr { + nt := mallocTarget() + ft := (*C.struct_FelicaTarget)(unsafe.Pointer(d)) + + C.marshallFelicaTarget(nt, ft) + + return uintptr(unsafe.Pointer(nt)) +} + +// NFC ISO14443B tag information. See ISO14443-3 for more details. +type ISO14443bTarget struct { + Pupi [4]byte // stores PUPI contained in ATQB (Answer To reQuest of type B) + ApplicationData [4]byte // stores Application Data contained in ATQB + ProtocolInfo [3]byte // stores Protocol Info contained in ATQB + CardIdentifier byte // store CID (Card Identifier) attributted by PCD to the PICC + Baud int +} + +func (t *ISO14443bTarget) String() string { + return tString(t) +} + +// Type is always ISO14443B +func (t *ISO14443bTarget) Modulation() Modulation { + return Modulation{ISO14443b, t.Baud} +} + +// Make an ISO14443bTarget from an nfc_iso14443b_info +func unmarshallISO14443bTarget(c *C.nfc_target) ISO14443bTarget { + var it ISO14443bTarget + + C.unmarshallISO14443bTarget((*C.struct_ISO14443bTarget)(unsafe.Pointer(&it)), c) + + return it +} + +// Marshall() returns a pointer to an nfc_target allocated with C.malloc() that +// contains the same data as the Target. Don't forget to C.free() the result of +// Marshall() afterwards. A runtime panic may occur if any slice referenced by a +// Target has been made larger than the maximum length mentioned in the +// respective comments. +func (d *ISO14443bTarget) Marshall() uintptr { + nt := mallocTarget() + it := (*C.struct_ISO14443bTarget)(unsafe.Pointer(d)) + + C.marshallISO14443bTarget(nt, it) + + return uintptr(unsafe.Pointer(nt)) +} + +// NFC ISO14443B' tag information +type ISO14443biTarget struct { + DIV [4]byte // 4 LSBytes of tag serial number + VerLog byte // Software version & type of REPGEN + Config byte // Config Byte, present if long REPGEN + AtrLen int // length of the Atr field + Atr [33]byte // ATR, if any. At most 33 bytes + Baud int +} + +func (t *ISO14443biTarget) String() string { + return tString(t) +} + +// Type is always ISO14443BI +func (t *ISO14443biTarget) Modulation() Modulation { + return Modulation{ISO14443bi, t.Baud} +} + +// Make an ISO14443biTarget from an nfc_iso14443bi_info +func unmarshallISO14443biTarget(c *C.nfc_target) ISO14443biTarget { + var it ISO14443biTarget + + C.unmarshallISO14443biTarget((*C.struct_ISO14443biTarget)(unsafe.Pointer(&it)), c) + + return it +} + +// Marshall() returns a pointer to an nfc_target allocated with C.malloc() that +// contains the same data as the Target. Don't forget to C.free() the result of +// Marshall() afterwards. A runtime panic may occur if any slice referenced by a +// Target has been made larger than the maximum length mentioned in the +// respective comments. +func (d *ISO14443biTarget) Marshall() uintptr { + nt := mallocTarget() + it := (*C.struct_ISO14443biTarget)(unsafe.Pointer(d)) + + C.marshallISO14443biTarget(nt, it) + + return uintptr(unsafe.Pointer(nt)) +} + +// NFC ISO14443-2B ST SRx tag information +type ISO14443b2srTarget struct { + UID [8]byte + Baud int +} + +func (t *ISO14443b2srTarget) String() string { + return tString(t) +} + +// Type is always ISO14443B2SR +func (t *ISO14443b2srTarget) Modulation() Modulation { + return Modulation{ISO14443b2sr, t.Baud} +} + +// Make an ISO14443b2srTarget from an nfc_iso14443b2sr_info +func unmarshallISO14443b2srTarget(c *C.nfc_target) ISO14443b2srTarget { + var it ISO14443b2srTarget + + C.unmarshallISO14443b2srTarget((*C.struct_ISO14443b2srTarget)(unsafe.Pointer(&it)), c) + + return it +} + +// Marshall() returns a pointer to an nfc_target allocated with C.malloc() that +// contains the same data as the Target. Don't forget to C.free() the result of +// Marshall() afterwards. A runtime panic may occur if any slice referenced by a +// Target has been made larger than the maximum length mentioned in the +// respective comments. +func (d *ISO14443b2srTarget) Marshall() uintptr { + nt := mallocTarget() + it := (*C.struct_ISO14443b2srTarget)(unsafe.Pointer(d)) + + C.marshallISO14443b2srTarget(nt, it) + + return uintptr(unsafe.Pointer(nt)) +} + +// NFC ISO14443-2B ASK CTx tag information +type ISO14443b2ctTarget struct { + UID [4]byte + ProdCode byte + FabCode byte + Baud int +} + +func (t *ISO14443b2ctTarget) String() string { + return tString(t) +} + +// Type is always ISO1444B2CT +func (t *ISO14443b2ctTarget) Modulation() Modulation { + return Modulation{ISO14443b2ct, t.Baud} +} + +// Make an ISO14443b2ctTarget from an nfc_iso14443b2ct_info +func unmarshallISO14443b2ctTarget(c *C.nfc_target) ISO14443b2ctTarget { + var it ISO14443b2ctTarget + + C.unmarshallISO14443b2ctTarget((*C.struct_ISO14443b2ctTarget)(unsafe.Pointer(&it)), c) + + return it +} + +// Marshall() returns a pointer to an nfc_target allocated with C.malloc() that +// contains the same data as the Target. Don't forget to C.free() the result of +// Marshall() afterwards. A runtime panic may occur if any slice referenced by a +// Target has been made larger than the maximum length mentioned in the +// respective comments. +func (d *ISO14443b2ctTarget) Marshall() uintptr { + nt := mallocTarget() + it := (*C.struct_ISO14443b2ctTarget)(unsafe.Pointer(d)) + + C.marshallISO14443b2ctTarget(nt, it) + + return uintptr(unsafe.Pointer(nt)) +} + +// NFC Jewel tag information +type JewelTarget struct { + SensRes [2]byte + ID [4]byte + Baud int +} + +func (t *JewelTarget) String() string { + return tString(t) +} + +// Type is always JEWEL +func (t *JewelTarget) Modulation() Modulation { + return Modulation{Jewel, t.Baud} +} + +// Make a JewelTarget from an nfc_jewel_info +func unmarshallJewelTarget(c *C.nfc_target) JewelTarget { + var jt JewelTarget + + C.unmarshallJewelTarget((*C.struct_JewelTarget)(unsafe.Pointer(&jt)), c) + + return jt +} + +// Marshall() returns a pointer to an nfc_target allocated with C.malloc() that +// contains the same data as the Target. Don't forget to C.free() the result of +// Marshall() afterwards. A runtime panic may occur if any slice referenced by a +// Target has been made larger than the maximum length mentioned in the +// respective comments. +func (d *JewelTarget) Marshall() uintptr { + nt := mallocTarget() + jt := (*C.struct_JewelTarget)(unsafe.Pointer(d)) + + C.marshallJewelTarget(nt, jt) + + return uintptr(unsafe.Pointer(nt)) +} diff --git a/CHANGES b/CHANGES index 84f6ce6..7ee3f72 100644 --- a/CHANGES +++ b/CHANGES @@ -17,9 +17,20 @@ Release 0.2 (2014-03-03): B Fix issue #2 Release 1.0 (2014-04-06): - R This major release is considered stable. The interface of this wrapper is - stable until further notice. + R This major release is considered stable. The interface of this + wrapper is stable until further notice. I Rename constants to reflect Go naming conventions I Change methods of type nfc.Device to use a value receiver - B Fix an issue were copying structs of type nfc.Device could cause a pointer - into unallocated memory to come into existence + B Fix an issue were copying structs of type nfc.Device could cause a + pointer into unallocated memory to come into existence + +Release 2.0 (2014-08-30) + B Make this wrapper compile under Go 1.3 and newer. + I Change the layout of some Target structures. This is needed to + simplify the marshalling code. The new target structures also follow + the underlying C structures more closely. + I Some names where changed to match Go naming conventions. This was + done now as I believe it is better to lump incompatibly changes into + one release as much as possible. + C Error strings were converted to lower case. + B Some typos where fixed in comments. diff --git a/README b/README index 33ba9a6..fe928d8 100644 --- a/README +++ b/README @@ -1,9 +1,9 @@ This is a Go wrapper for the libnfc. It has been written against version 1.7.0. The code has not been thoroughly tested yet. Please report any errors to the -project. The API is considered stable as of version 1.0 and will not change in -incompatible ways in the near future. +project. The API is considered stable as of version 2.0 and will probably not +change in incompatible ways in the near future. -The current release of this wrapper is release 1.0. Successive releases will be +The current release of this wrapper is release 2.0. Successive releases will be placed in different subfolders as to not change the semantics of code that links against a specific version of this wrapper. The general folder structure is diff --git a/latest b/latest index 9f8e9b6..415b19f 120000 --- a/latest +++ b/latest @@ -1 +1 @@ -1.0 \ No newline at end of file +2.0 \ No newline at end of file