Reimplementing usb code with libusb-1.0

This commit is contained in:
Kenspeckle 2022-07-10 12:05:48 +02:00
parent c4e04d52d3
commit 1ac0d803da
5 changed files with 1451 additions and 2409 deletions

394
NEWS.md
View File

@ -1,394 +0,0 @@
New in 1.8.0:
API Changes:
- Restore nfc_modulation_type enum order to keep compatibility with libnfc 1.7.1
- Bump library version to 6.0.0
New in 1.7.2:
Drivers:
* New driver for pn71xx NXP's NFC Controllers through Linux Libnfc-nci (untested)
* New driver for contactless PC/SC readers (only as initiator)
API Changes:
* nfc_device_get_supported_baud_rate() now takes also "mode" as argument
* New nfc_device_get_supported_baud_rate_target_mode()
* New NFC modulation type NMT_BARCODE and nfc_barcode_info struct to support Thinfilm NFC Barcode protocol
* New NFC modulation type NMT_ISO14443BICLASS and NMT_ISO14443BICLASS struct to support HID iClass (Picopass)
* pn53x_transceive() is now part of public API
New in 1.7.1:
API Changes:
* nfc_initiator_select_passive_target() provides defaults if pbtInitData=NULL
* nfc_initiator_target_is_present() allow NULL pointer to tag
New in 1.7.0:
Drivers:
* New PN532 over I2C driver, see contrib/libnfc/pn532_i2c_on_rpi.conf.sample
API Changes:
* New function iso14443b_crc_append()
New in 1.7.0-rc7:
Drivers:
* New PN532 over SPI driver, see contrib/libnfc/pn532_spi_on_rpi.conf.sample
API Changes:
* Functions
- nfc_initiator_target_is_present() & str_nfc_target():
now take a pointer to nfc_target as argument
- nfc_init(): upon malloc error, doesn't force exit() anymore
so now you should test if context != NULL after nfc_init() call
New in 1.7.0-rc5:
API Changes:
* Functions
- New nfc_register_driver() function allowing to hook custom drivers.
New in 1.7.0-rc3:
API Changes:
* Functions
- Add timeout param to nfc_emulate_target()
New in 1.7.0-rc2:
Configuration:
libnfc can now use a configuration file for special setups, or features
activation. This file (/etc/nfc/libnfc.conf under GNU/Linux systems)
supports already some keywords:
- "allow_autoscan" to enable/disable device auto-detection feature;
- "allow_intrusive_scan" to enable/disable intrusive auto-detection
(ie. serial port probing);
- "log_level" to select library verbosity;
- "device.name" and "device.connstring" to define a user device,
this is the recommended method if user has a not easily detectable
device (ie. a serial one).
It is also possible to define devices using dedicated configuration files and
put them into device search directory (/etc/nfc/devices.d under GNU/Linux).
Example for the OpenPCD2: create /etc/nfc/devices.d/openpcd2.conf with:
name = "OpenPCD2"
connstring = "pn532_uart:/dev/ttyACM0"
optional = true
The keyword "optional" does not mandate the device to be present always
(it detects if the reader is indeed present before using it)
API Changes:
* Types
- New NFC_ESOFT error to handle software errors (allocations, pipe
creation, etc.)
* Functions
- Remove nfc_get_default_device() function: the default device is now the
first in nfc_list_devices() or could be open using NULL connstring with
nfc_open() function.
- New enum-to-string converter functions str_nfc_modulation_type() and
str_nfc_baud_rate()
- New str_nfc_target() to convert nfc_target struct into allocated string
- New nfc_device_get_information_about() function to retreive some device's
information
- No more in/out function parameter: nfc_initiator_transceive_*() now
take a constant size for Rx buffer
- New nfc_initiator_target_is_present() to test is the previously selected
target is available in the field
- nfc_initiator_transceive_bytes() returns NFC_EMFCAUTHFAIL when AUTH
command failed on a Mifare Classic
- New nfc_initiator_init_secure_element() to initiate a connection with
secure element (Only supported with a PN532 with SAM equipped)
New in 1.6.0-rc1:
API Changes:
* Types
- '_t' suffix removed from all types (e.g. nfc_device_t is now nfc_device)
- All errors removed in flavour of NFC_EIO, NFC_EINVARG, NFC_EDEVNOTSUPP,
NFC_ENOTSUCHDEV, NFC_EOVFLOW, NFC_ETIMEOUT, NFC_EOPABORTED, NFC_ENOTIMPL,
NFC_ETGRELEASED, NFC_ERFTRANS, NFC_ECHIP and NFC_SUCCESS
- nfc_device_desc_t replaced by nfc_connstring: libnfc now uses connection
strings to describe a device
- byte_t typedef removed, libnfc now uses uint8_t from C99
- nfc_device is now an opaque type
- nfc_properties replaces nfc_options
* Functions
- New nfc_get_default_device() function that allows to grab the connstring
stored in LIBNFC_DEFAULT_DEVICE environnement variable or returns the
first available device if not set
- New nfc_device_get_connstring() accessor function to know the device
connstring
- New nfc_device_set_property_bool() function that replace nfc_configure()
- New nfc_device_set_property_int() function to set integer property
- nfc_device_name() renamed to nfc_device_get_name() for the sake of
consistency
- New nfc_device_get_last_error() function, an accessor to last error occured
- Whole libnfc's functions now return 0 (NFC_SUCCESS) or positive value if
appropriated on success and libnfc's error code on failure
- nfc_connect(), nfc_disconnect() renamed to nfc_open(), nfc_close()
respectively
- Add 2 new functions: initialization and deinitialization functions:
nfc_init() and nfc_exit()
- New nfc_device_get_supported_modulation() and
nfc_device_get_supported_baud_rate() functions
* Dependencies
- log4c is not anymore used for debugging facility. It was a bad choice,
sorry for inconvenience.
New in 1.5.1:
API Changes
* Types
- Communication-level errors DEIO and DETIMEOUT are now know as ECOMIO,
ECOMTIMEOUT respectively
- Common device-level errors DEINVAL and DEABORT are now know as EINVALARG,
EOPABORT respectively
- New errors: EFRAACKMISMATCH, EFRAISERRFRAME, EDEVNOTSUP and ENOTIMPL
* Functions
- nfc_abort_command() returns a boolean
- timeout (struct timeval) pointer added to
nfc_initiator_transceive_bytes(), nfc_target_send_bytes() and
nfc_target_receive_bytes()
- timed functions nfc_initiator_transceive_bytes_timed() and
nfc_initiator_transceive_bits_timed() now takes uint32_t as cycles
pointer
- nfc_initiator_poll_targets() renamed to nfc_initiator_poll_target() and
only return one target
New in 1.5.0:
Installed files
- nfc-message.h have been removed, internal macros are not part of API.
- New nfc-emulation.h file offers a middle level API to handle emulation (see
nfc-emulate-forum-tag4 example)
API Changes
* Types
- New error: DEABORT raised when operation is aborted by user (using
nfc_abort_command())
- nfc_chip_t type removed from public API (have been renamed to pn53x_type
in chips/pn53x)
- nfc_device_spec_t removed, each driver can use his own way to keep a
connection pointer
* Structures
- nfc_device_t now have a nfc_driver_t struct pointer (named .driver) and
void pointer (.driver_data) to handle device specific wrapping
- nfc_device_t now have a void pointer (.chip_data) to keep some chip
specific data
- nfc_device_t now have an file descriptor array to manage to abort request
- nfc_device_t does have .nc member (nfc_chip_t) anymore (different chips
handling in now in chip level)
- nfc_device_t does have .nds member (nfc_device_spec_t) anymore, each
driver handle its communication using driver_data pointer
- nfc_device_t does have .bActive member (bool) anymore, this variable was
almost not used and was not efficient
- nfc_device_t does have chip's register caches anymore, this is handle in
chip level (using chip_data pointer)
- driver_callbacks structure have been removed from public API
- New nfc_emulator structure used by the new emulation API (see
nfc_emulate_target())
- New nfc_emulation_state_machine structure used by the new emulation API,
it handles an I/O function and data pointer to create a software based
state-machine.
* Functions
- New nfc_abort_command() function to abort current running command.
- New nfc_initiator_transceive_bits_timed() and
nfc_initiator_transceive_bytes_timed() to transceive bits/bytes and
measure the time to have a reply
- New nfc_emulate_target() function to start a target emulation using an
nfc_emulator structure (it contains a custom state-machine
(nfc_emulation_state_machine struct) and a custom target (nfc_target_t)
(see nfc-emulate-forum-tag4 to have a look on how-to use it)
New in 1.4.1:
API Changes
* Types
- New error: ETGUIDNOTSUP raised when UID is not 4 bytes long or does not
start with 0x08 (Security restriction present in the NXP PN53x chips)
New in 1.4.0:
API Changes
* Types
- New nfc_device_option value (enum): NDO_FORCE_ISO14443_A to force the
chip to switch in ISO14443-A
- New nfc_dep_mode_t (enum) for DEP mode:
NDM_UNDEFINED, NDM_PASSIVE, NDM_ACTIVE
- New nfc_modulation_type_t (enum) that lists modulation types:
NMT_ISO14443A, NMT_ISO14443B, NMT_FELICA, NMT_JEWEL, NMT_DEP
- New nfc_baud_rate_t (enum): list of baud rates:
NBR_UNDEFINED, NBR_106, NBR_212, NBR_424, NBR_847
- nfc_target_type_t have been removed from API (use nfc_modulation_t
instead)
* Structures
- nfc_device_t now have a boolean bAutoIso14443_4 to keep the locally the
state of NDO_AUTO_ISO14443_4 (should not be directly set, use
nfc_configure() with NDO_AUTO_ISO14443_4)
- nfc_device_t now have an uint8_t ui8Parameters to cache PN53x parameters
- nfc_device_t now have a byte_t btSupportByte to cache supported
modulations
- nfc_dep_info_t have completely changed, please see API documentation
- nfc_iso14443b_info_t have completely changed, please see API
documentation
- nfc_modulation_t have completely changed: it now contains a
nfc_modulation_type_t and nfc_baud_rate_t couple. Initialization example:
nfc_modulation_t nm = {
.nmt = NMT_ISO14443A,
.nbr = NBR_106,
};
- nfc_target_t now contains new nfc_modulation_t instead of
nfc_target_type_t. Initialization example:
nfc_target_t nt = {
.nm.nmt = NMT_ISO14443A,
.nm.nbr = NBR_UNDEFINED,
.nti.nai.abtAtqa = { 0x03, 0x44 },
.nti.nai.abtUid = { 0x08, 0xab, 0xcd, 0xef },
.nti.nai.btSak = 0x20,
.nti.nai.szUidLen = 4,
.nti.nai.abtAts = { 0x75, 0x77, 0x81, 0x02, 0x80 },
.nti.nai.szAtsLen = 5,
};
* Functions
- nfc_initiator_select_passive_target() now use new nfc_modulation_t and
nfc_target_t instead of nfc_target_info_t
- nfc_initiator_list_passive_targets() now use new nfc_modulation_t and
nfc_target_t instead of nfc_target_info_t
- nfc_initiator_poll_targets() use new nfc_modulation_t instead of
nfc_target_type_t
- nfc_initiator_select_dep_target() completely changed, use now
nfc_dep_mode_t, nfc_baudrate_t, nfc_dep_info_t and nfc_target_t, please
see API documentation
- nfc_target_init() have an additional argument: nfc_target_t to describe
the wanted target
- append_iso14443a_crc() was renamed to iso14443a_crc_append()
- New iso14443a_locate_historical_bytes() to locate historical bytes in ATS
New in 1.3.9 (since 1.3.4):
Installed files
- mifaretag.h and mifareultag.h are removed, Mifare features are not a part
of libnfc API anymore (these features are always available in examples/)
API Changes
* Types
- New nfc_device_option_t value (enum): NDO_AUTO_14443_4, an option to
enable/disable auto-switching to ISO/IEC 14443-4 if device is compliliant
- New nfc_device_option_t value (enum): NDO_EASY_FRAMING, an option to
enable/disable automatic frames encapsulation and chaining
- New nfc_target_type_t (enum), with values like NTT_MIFARE,
NTT_ISO14443B_106, NTT_DEP_ACTIVE_424, etc.
- Mifare related types have been removed from API: mifare_cmd,
mifare_param_auth, mifare_param_data, mifare_param_value, mifare_param
* Structures
- nfc_device_t now have boolean bEasyFraming to enable/disable "easy
framing" feature (should not be directly set, use nfc_configure() with
NDO_EASY_FRAMING)
- nfc_device_t now have integer iLastError to handle last error
- New chip_callbacks to handle error lookup per chip
- driver_callbacks now have a pointer to chip_callbacks
- New nfc_target_t that contains nfc_target_info_t and nfc_target_type_t
* Functions
- nfc_initiator_select_tag() became nfc_initiator_select_passive_target()
- New nfc_initiator_list_passive_targets() returns a list of detected
target on desired modulation
- (experimental) New nfc_initiator_poll_targets() returns targets that are
detected during hardware polling (available only with PN532)
- nfc_initiator_transceive_dep_bytes(), nfc_target_receive_dep_bytes() and
nfc_target_send_dep_bytes() have been removed from API, use
NDO_EASY_FRAMING option to switch from raw mode to "easy framing"
- nfc_initiator_mifare_cmd() have been removed: no more Mifare related
stuff in libnfc's API
- New nfc_strerror(), nfc_strerror_r() and nfc_perror() to report errors
- New append_iso14443a_crc() to append iso14443a_crc() to a string
New in 1.3.4 (since 1.2.1):
Installed files
- Headers are now installed in include/nfc instead of include/libnfc
- libnfc.h have been renamed to nfc.h
- defines.h and types.h have been merge into nfc-types.h
- bitutils.h is not installed anymore, some functions are now in
examples/nfc-utils.c
- devices.h, dev_acr122.h, dev_arygon.h, dev_pn531.h, dev_pn533.h and rs232.h
are not installed anymore
- New header mifareultag.h, like mifaretag.h for Mifare UltraLight
- New header nfc-messages.h with messages macros (DBG, ERR, INFO)
API Changes
* Types
- uint32_t which was used as size now are size_t
- chip_type became nfc_chip_t (enum)
- init_modulation became nfc_modulation_t (enum), and now have
NM_ACTIVE_DEP and NM_PASSIVE_DEP modulation values added
* Structures
- dev_info became nfc_device_t
- dev_config_option became nfc_device_option_t
- New nfc_device_desc_t to describe the way to access to a NFC device.
Initialisation example:
nfc_device_desc_t ndd = {
ndd.pcDriver = "ARYGON";
ndd.pcPort = "/dev/ttyUSB0";
ndd.uiSpeed = 115200;
};
- dev_callbacks became driver_callbacks and now have two function pointers
more: pick_device() and list_devices()
- New nfc_dep_info_t to handle DEP targets info
- tag_info_iso14443a became nfc_iso14443a_info_t
- tag_info_iso14443b became nfc_iso14443b_info_t
- tag_info_felica became nfc_felica_info_t
- tag_info_jewel became nfc_jewel_info_t
- tag_info became nfc_target_info_t, and now have extended union to
nfc_dep_info_t
* Functions
- nfc_connect() now takes 1 nfc_devive_desc_t argument (can be NULL)
- New nfc_list_devices(), it find available NFC devices using all know
drivers
- (experimental) New nfc_initiator_select_dep(), it looks for DEP targets
- (experimental) New nfc_initiator_transceive_dep_bytes(), like
nfc_initiator_transceive_bytes() for DEP targets
- (experimental) New nfc_target_receive_dep_bytes() and
nfc_target_send_dep_bytes(), to receive/send bytes to DEP target
(configured as initiator) while local NFC device is configured as target
- New nfc_device_name() returns the device's name
- New iso14443a_crc() computes CRC as described in ISO/IEC 14443
- New nfc_version() returns the actual version of libnfc (with SVN
revision, if available)

View File

@ -7,6 +7,7 @@
* Copyright (C) 2010-2012 Romain Tartière
* Copyright (C) 2010-2013 Philippe Teuwen
* Copyright (C) 2012-2013 Ludovic Rousseau
* Copyright (C) 2022 Kenspeckle
* See AUTHORS file for a more comprehensive list of contributors.
* Additional contributors of this file:
*
@ -24,634 +25,300 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
/**
* @file usbbus.c
* @brief libusb 0.1 driver wrapper
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif // HAVE_CONFIG_H
#include <stdbool.h>
#include <string.h>
#include <stdlib.h>
#include <libusb.h>
#include <stdint.h>
#include "usbbus.h"
#include "log.h"
#define LOG_CATEGORY "libnfc.buses.usbbus"
#define LOG_CATEGORY "libnfc.bus.usbbus"
#define LOG_GROUP NFC_LOG_GROUP_DRIVER
/*
* This file embeds partially libusb-compat-0.1 by:
* Copyright (C) 2008 Daniel Drake <dsd@gentoo.org>
* Copyright (c) 2000-2003 Johannes Erdfelt <johannes@erdfelt.com>
* This layer will be removed ASAP before integration in the main trunk
*/
#define LIST_ADD(begin, ent) \
do { \
if (begin) { \
ent->next = begin; \
ent->next->prev = ent; \
} else \
ent->next = NULL; \
ent->prev = NULL; \
begin = ent; \
} while(0)
#define LIST_DEL(begin, ent) \
do { \
if (ent->prev) \
ent->prev->next = ent->next; \
else \
begin = ent->next; \
if (ent->next) \
ent->next->prev = ent->prev; \
ent->prev = NULL; \
ent->next = NULL; \
} while (0)
#define USBBUS_DT_CONFIG_SIZE 9
#define USBBUS_DT_INTERFACE_SIZE 9
#define USBBUS_DT_ENDPOINT_AUDIO_SIZE 9
static libusb_context *ctx = NULL;
struct usbbus_bus *usb_busses = NULL;
uint8_t get_usb_num_configs(struct libusb_device *dev);
static void _usb_finalize(void)
{
if (ctx) {
libusb_exit(ctx);
ctx = NULL;
}
}
static void usb_init(void)
{
if (!ctx) {
int r;
r = libusb_init(&ctx);
if (r < 0) {
return;
}
atexit(_usb_finalize);
}
}
static int find_busses(struct usbbus_bus **ret)
{
libusb_device **dev_list = NULL;
struct usbbus_bus *busses = NULL;
struct usbbus_bus *bus;
int dev_list_len = 0;
int i;
int r;
r = libusb_get_device_list(ctx, &dev_list);
if (r < 0) {
return r;
}
if (r == 0) {
libusb_free_device_list(dev_list, 1);
/* no buses */
return 0;
}
/* iterate over the device list, identifying the individual busses.
* we use the location field of the usbbus_bus structure to store the
* bus number. */
dev_list_len = r;
for (i = 0; i < dev_list_len; i++) {
libusb_device *dev = dev_list[i];
uint8_t bus_num = libusb_get_bus_number(dev);
/* if we already know about it, continue */
if (busses) {
bus = busses;
int found = 0;
do {
if (bus_num == bus->location) {
found = 1;
break;
}
} while ((bus = bus->next) != NULL);
if (found)
continue;
}
/* add it to the list of busses */
bus = malloc(sizeof(*bus));
if (!bus)
goto err;
memset(bus, 0, sizeof(*bus));
bus->location = bus_num;
snprintf(bus->dirname, USBBUS_PATH_MAX, "%03d", bus_num);
LIST_ADD(busses, bus);
}
libusb_free_device_list(dev_list, 1);
*ret = busses;
return 0;
err:
bus = busses;
while (bus) {
struct usbbus_bus *tbus = bus->next;
free(bus);
bus = tbus;
}
return LIBUSB_ERROR_NO_MEM;
}
static int usb_find_busses(void)
{
struct usbbus_bus *new_busses = NULL;
struct usbbus_bus *bus;
int changes = 0;
int r;
/* libusb-1.0 initialization might have failed, but we can't indicate
* this with libusb-0.1, so trap that situation here */
if (!ctx)
return 0;
r = find_busses(&new_busses);
if (r < 0) {
return r;
}
/* walk through all busses we already know about, removing duplicates
* from the new list. if we do not find it in the new list, the bus
* has been removed. */
bus = usb_busses;
while (bus) {
struct usbbus_bus *tbus = bus->next;
struct usbbus_bus *nbus = new_busses;
int found = 0;
while (nbus) {
struct usbbus_bus *tnbus = nbus->next;
if (bus->location == nbus->location) {
LIST_DEL(new_busses, nbus);
free(nbus);
found = 1;
break;
}
nbus = tnbus;
}
if (!found) {
/* bus removed */
changes++;
LIST_DEL(usb_busses, bus);
free(bus);
}
bus = tbus;
}
/* anything remaining in new_busses is a new bus */
bus = new_busses;
while (bus) {
struct usbbus_bus *tbus = bus->next;
LIST_DEL(new_busses, bus);
LIST_ADD(usb_busses, bus);
changes++;
bus = tbus;
}
return changes;
}
static int find_devices(libusb_device **dev_list, int dev_list_len,
struct usbbus_bus *bus, struct usbbus_device **ret)
{
struct usbbus_device *devices = NULL;
struct usbbus_device *dev;
int i;
for (i = 0; i < dev_list_len; i++) {
libusb_device *newlib_dev = dev_list[i];
uint8_t bus_num = libusb_get_bus_number(newlib_dev);
if (bus_num != bus->location)
continue;
dev = malloc(sizeof(*dev));
if (!dev)
goto err;
/* No need to reference the device now, just take the pointer. We
* increase the reference count later if we keep the device. */
dev->dev = newlib_dev;
dev->bus = bus;
dev->devnum = libusb_get_device_address(newlib_dev);
snprintf(dev->filename, USBBUS_PATH_MAX, "%03d", dev->devnum);
LIST_ADD(devices, dev);
}
*ret = devices;
return 0;
err:
dev = devices;
while (dev) {
struct usbbus_device *tdev = dev->next;
free(dev);
dev = tdev;
}
return LIBUSB_ERROR_NO_MEM;
}
static void clear_endpoint_descriptor(struct usbbus_endpoint_descriptor *ep)
{
if (ep->extra)
free(ep->extra);
}
static void clear_interface_descriptor(struct usbbus_interface_descriptor *iface)
{
if (iface->extra)
free(iface->extra);
if (iface->endpoint) {
int i;
for (i = 0; i < iface->bNumEndpoints; i++)
clear_endpoint_descriptor(iface->endpoint + i);
free(iface->endpoint);
}
}
static void clear_interface(struct usbbus_interface *iface)
{
if (iface->altsetting) {
int i;
for (i = 0; i < iface->num_altsetting; i++)
clear_interface_descriptor(iface->altsetting + i);
free(iface->altsetting);
}
}
static void clear_config_descriptor(struct usbbus_config_descriptor *config)
{
if (config->extra)
free(config->extra);
if (config->interface) {
int i;
for (i = 0; i < config->bNumInterfaces; i++)
clear_interface(config->interface + i);
free(config->interface);
}
}
static void clear_device(struct usbbus_device *dev)
{
int i;
for (i = 0; i < dev->descriptor.bNumConfigurations; i++)
clear_config_descriptor(dev->config + i);
}
static int copy_endpoint_descriptor(struct usbbus_endpoint_descriptor *dest,
const struct libusb_endpoint_descriptor *src)
{
memcpy(dest, src, USBBUS_DT_ENDPOINT_AUDIO_SIZE);
dest->extralen = src->extra_length;
if (src->extra_length) {
dest->extra = malloc(src->extra_length);
if (!dest->extra)
return LIBUSB_ERROR_NO_MEM;
memcpy(dest->extra, src->extra, src->extra_length);
}
return 0;
}
static int copy_interface_descriptor(struct usbbus_interface_descriptor *dest,
const struct libusb_interface_descriptor *src)
{
int i;
int num_endpoints = src->bNumEndpoints;
size_t alloc_size = sizeof(struct usbbus_endpoint_descriptor) * num_endpoints;
memcpy(dest, src, USBBUS_DT_INTERFACE_SIZE);
dest->endpoint = malloc(alloc_size);
if (!dest->endpoint)
return LIBUSB_ERROR_NO_MEM;
memset(dest->endpoint, 0, alloc_size);
for (i = 0; i < num_endpoints; i++) {
int r = copy_endpoint_descriptor(dest->endpoint + i, &src->endpoint[i]);
if (r < 0) {
clear_interface_descriptor(dest);
return r;
}
}
dest->extralen = src->extra_length;
if (src->extra_length) {
dest->extra = malloc(src->extra_length);
if (!dest->extra) {
clear_interface_descriptor(dest);
return LIBUSB_ERROR_NO_MEM;
}
memcpy(dest->extra, src->extra, src->extra_length);
}
return 0;
}
static int copy_interface(struct usbbus_interface *dest,
const struct libusb_interface *src)
{
int i;
int num_altsetting = src->num_altsetting;
size_t alloc_size = sizeof(struct usbbus_interface_descriptor)
* num_altsetting;
dest->num_altsetting = num_altsetting;
dest->altsetting = malloc(alloc_size);
if (!dest->altsetting)
return LIBUSB_ERROR_NO_MEM;
memset(dest->altsetting, 0, alloc_size);
for (i = 0; i < num_altsetting; i++) {
int r = copy_interface_descriptor(dest->altsetting + i,
&src->altsetting[i]);
if (r < 0) {
clear_interface(dest);
return r;
}
}
return 0;
}
static int copy_config_descriptor(struct usbbus_config_descriptor *dest,
const struct libusb_config_descriptor *src)
{
int i;
int num_interfaces = src->bNumInterfaces;
size_t alloc_size = sizeof(struct usbbus_interface) * num_interfaces;
memcpy(dest, src, USBBUS_DT_CONFIG_SIZE);
dest->interface = malloc(alloc_size);
if (!dest->interface)
return LIBUSB_ERROR_NO_MEM;
memset(dest->interface, 0, alloc_size);
for (i = 0; i < num_interfaces; i++) {
int r = copy_interface(dest->interface + i, &src->interface[i]);
if (r < 0) {
clear_config_descriptor(dest);
return r;
}
}
dest->extralen = src->extra_length;
if (src->extra_length) {
dest->extra = malloc(src->extra_length);
if (!dest->extra) {
clear_config_descriptor(dest);
return LIBUSB_ERROR_NO_MEM;
}
memcpy(dest->extra, src->extra, src->extra_length);
}
return 0;
}
static int initialize_device(struct usbbus_device *dev)
{
libusb_device *newlib_dev = dev->dev;
int num_configurations;
size_t alloc_size;
int r;
int i;
/* device descriptor is identical in both libs */
r = libusb_get_device_descriptor(newlib_dev,
(struct libusb_device_descriptor *) &dev->descriptor);
if (r < 0) {
return r;
}
num_configurations = dev->descriptor.bNumConfigurations;
alloc_size = sizeof(struct usbbus_config_descriptor) * num_configurations;
dev->config = malloc(alloc_size);
if (!dev->config)
return LIBUSB_ERROR_NO_MEM;
memset(dev->config, 0, alloc_size);
for (i = 0; i < num_configurations; i++) {
struct libusb_config_descriptor *newlib_config;
r = libusb_get_config_descriptor(newlib_dev, i, &newlib_config);
if (r < 0) {
clear_device(dev);
free(dev->config);
return r;
}
r = copy_config_descriptor(dev->config + i, newlib_config);
libusb_free_config_descriptor(newlib_config);
if (r < 0) {
clear_device(dev);
free(dev->config);
return r;
}
}
dev->num_children = 0;
dev->children = NULL;
libusb_ref_device(newlib_dev);
return 0;
}
static void free_device(struct usbbus_device *dev)
{
clear_device(dev);
libusb_unref_device(dev->dev);
free(dev);
}
static int usb_find_devices(void)
{
struct usbbus_bus *bus;
libusb_device **dev_list;
int dev_list_len;
int changes = 0;
/* libusb-1.0 initialization might have failed, but we can't indicate
* this with libusb-0.1, so trap that situation here */
if (!ctx)
return 0;
dev_list_len = libusb_get_device_list(ctx, &dev_list);
if (dev_list_len < 0)
return dev_list_len;
for (bus = usb_busses; bus; bus = bus->next) {
int r;
struct usbbus_device *new_devices = NULL;
struct usbbus_device *dev;
r = find_devices(dev_list, dev_list_len, bus, &new_devices);
if (r < 0) {
libusb_free_device_list(dev_list, 1);
return r;
}
/* walk through the devices we already know about, removing duplicates
* from the new list. if we do not find it in the new list, the device
* has been removed. */
dev = bus->devices;
while (dev) {
int found = 0;
struct usbbus_device *tdev = dev->next;
struct usbbus_device *ndev = new_devices;
while (ndev) {
if (ndev->devnum == dev->devnum) {
LIST_DEL(new_devices, ndev);
free(ndev);
found = 1;
break;
}
ndev = ndev->next;
}
if (!found) {
LIST_DEL(bus->devices, dev);
free_device(dev);
changes++;
}
dev = tdev;
}
/* anything left in new_devices is a new device */
dev = new_devices;
while (dev) {
struct usbbus_device *tdev = dev->next;
r = initialize_device(dev);
if (r < 0) {
dev = tdev;
continue;
}
LIST_DEL(new_devices, dev);
LIST_ADD(bus->devices, dev);
changes++;
dev = tdev;
}
}
libusb_free_device_list(dev_list, 1);
return changes;
}
int usbbus_prepare(void)
{
static bool usb_initialized = false;
if (!usb_initialized) {
int usbbus_prepare() {
static bool usb_initialized = false;
int res;
if (!usb_initialized) {
#ifdef ENVVARS
char *env_log_level = getenv("LIBNFC_LOG_LEVEL");
// Set libusb debug only if asked explicitely:
// LIBUSB_LOG_LEVEL=12288 (= NFC_LOG_PRIORITY_DEBUG * 2 ^ NFC_LOG_GROUP_LIBUSB)
if (env_log_level && (((atoi(env_log_level) >> (NFC_LOG_GROUP_LIBUSB * 2)) & 0x00000003) >= NFC_LOG_PRIORITY_DEBUG)) {
setenv("USB_DEBUG", "255", 1);
}
char *env_log_level = getenv("LIBNFC_LOG_LEVEL");
// Set libusb debug only if asked explicitely:
// LIBUSB_LOG_LEVEL=12288 (= NFC_LOG_PRIORITY_DEBUG * 2 ^ NFC_LOG_GROUP_LIBUSB)
if (env_log_level
&& (((atoi(env_log_level) >> (NFC_LOG_GROUP_LIBUSB * 2)) & 0x00000003) >= NFC_LOG_PRIORITY_DEBUG)) {
setenv("USB_DEBUG", "255", 1);
}
#endif
usb_init();
usb_initialized = true;
}
res = libusb_init(&ctx);
if (res != 0) {
log_put(LOG_GROUP,
LOG_CATEGORY,
NFC_LOG_PRIORITY_ERROR,
"Unable to init libusb (%s)",
libusb_strerror(res));
return res;
}
usb_initialized = true;
}
int res;
// usb_find_busses will find all of the busses on the system. Returns the
// number of changes since previous call to this function (total of new
// busses and busses removed).
if ((res = usb_find_busses()) < 0) {
log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "Unable to find USB busses (%s)", usbbus_strerror(res));
return -1;
}
// usb_find_devices will find all of the devices on each bus. This should be
// called after usb_find_busses. Returns the number of changes since the
// previous call to this function (total of new device and devices removed).
if ((res = usb_find_devices()) < 0) {
log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "Unable to find USB devices (%s)", usbbus_strerror(res));
return -1;
}
return 0;
// usb_find_devices will find all of the devices on each bus. This should be
// called after usb_find_busses. Returns the number of changes since the
// previous call to this function (total of new device and devices removed).
libusb_device **tmp_devices;
ssize_t num_devices = libusb_get_device_list(ctx, &tmp_devices);
libusb_free_device_list(tmp_devices, (int) num_devices);
if (num_devices <= 0) {
log_put(LOG_GROUP,
LOG_CATEGORY,
NFC_LOG_PRIORITY_ERROR,
"Unable to find USB devices (%s)",
libusb_strerror((int) num_devices));
return -1;
}
return 0;
}
usbbus_device_handle *usbbus_open(struct usbbus_device *dev)
{
int r;
usbbus_device_handle *udev;
r = libusb_open((libusb_device *) dev->dev, (libusb_device_handle **)&udev);
if (r < 0) {
return NULL;
}
return (usbbus_device_handle *)udev;
TODO kenspeckle
beim ende vom programm libusb dinge wieder freigeben
size_t usbbus_usb_scan(char **connstrings,
const size_t connstrings_len,
struct usbbus_device *nfc_usb_devices,
const size_t num_nfc_usb_devices,
char *usb_driver_name) {
usbbus_prepare();
size_t device_found = 0;
struct libusb_device **devices;
ssize_t num_devices = libusb_get_device_list(ctx, &devices);
for (size_t i = 0; i < num_devices; i++) {
struct libusb_device *dev = devices[i];
for (size_t nfc_dev_idx = 0; nfc_dev_idx < num_nfc_usb_devices; nfc_dev_idx++) {
if (nfc_usb_devices[nfc_dev_idx].vendor_id == usbbus_get_vendor_id(dev)
&& nfc_usb_devices[nfc_dev_idx].product_id == usbbus_get_product_id(dev)) {
size_t valid_config_idx = 1;
// Make sure there are 2 endpoints available
// with libusb-win32 we got some null pointers so be robust before looking at endpoints
if (nfc_usb_devices[nfc_dev_idx].max_packet_size == 0) {
bool found_valid_config = false;
for (size_t config_idx = 0; config_idx < get_usb_num_configs(dev); i++) {
struct libusb_config_descriptor *usb_config;
int r = libusb_get_config_descriptor(dev, config_idx, &usb_config);
if (r != 0
|| usb_config->interface == NULL
|| usb_config->interface->altsetting == NULL
|| usb_config->interface->altsetting->bNumEndpoints < 2) {
// Nope, we maybe want the next one, let's try to find another
libusb_free_config_descriptor(usb_config);
continue;
}
libusb_free_config_descriptor(usb_config);
found_valid_config = true;
valid_config_idx = config_idx;
break;
}
if (!found_valid_config) {
continue;
}
}
libusb_device_handle *udev;
int res = libusb_open(dev, &udev);
if (res < 0 && udev == NULL) {
continue;
}
// Set configuration
res = libusb_set_configuration(udev, (int) valid_config_idx);
if (res < 0) {
log_put(LOG_GROUP,
LOG_CATEGORY,
NFC_LOG_PRIORITY_ERROR,
"Unable to set USB configuration (%s)",
libusb_strerror(res));
libusb_close(udev);
// we failed to use the device
continue;
}
log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "device found: Vendor-Id: %d Product-Id %d",
usbbus_get_vendor_id(dev), usbbus_get_product_id(dev));
libusb_close(udev);
uint8_t dev_address = libusb_get_device_address(dev);
size_t size_new_str = snprintf(
connstrings[device_found],
sizeof(nfc_connstring),
"%s:%03d:%03d",
usb_driver_name,
dev_address, (int) valid_config_idx);
if (size_new_str >= (int) sizeof(nfc_connstring)) {
// truncation occurred, skipping that one
continue;
}
device_found++;
// Test if we reach the maximum "wanted" devices
if (device_found == connstrings_len) {
return device_found;
}
}
}
}
return device_found;
}
void usbbus_close(usbbus_device_handle *dev)
{
libusb_close((libusb_device_handle *)dev);
void usbbus_get_usb_endpoints(struct libusb_device *dev,
uint8_t *endpoint_in,
uint8_t *endpoint_out,
uint16_t *max_packet_size) {
bool endpoint_in_set = false;
bool endpoint_out_set = false;
size_t num_configs = get_usb_num_configs(dev);
for (size_t config_idx = 0; config_idx < num_configs; config_idx++) {
struct libusb_config_descriptor *usb_config;
int r = libusb_get_config_descriptor(dev, config_idx, &usb_config);
if (r != 0) {
continue;
}
if (!usb_config->interface) {
continue;
}
for (size_t interface_idx = 0; interface_idx < usb_config->bNumInterfaces; interface_idx++) {
struct libusb_interface interface = usb_config->interface[interface_idx];
if (!interface.altsetting) {
continue;
}
for (size_t settings_idx = 0; settings_idx < interface.num_altsetting; settings_idx++) {
struct libusb_interface_descriptor settings = interface.altsetting[settings_idx];
if (!settings.endpoint) {
continue;
}
// 3 Endpoints maximum: Interrupt In, Bulk In, Bulk Out
for (size_t endpoint_idx = 0; endpoint_idx < settings.bNumEndpoints; endpoint_idx++) {
struct libusb_endpoint_descriptor endpoint = settings.endpoint[endpoint_idx];
// Only accept bulk transfer endpoints (ignore interrupt endpoints)
if (endpoint.bmAttributes != LIBUSB_ENDPOINT_TRANSFER_TYPE_BULK) {
continue;
}
// Copy the endpoint to a local var, makes it more readable code
uint8_t endpoint_address = endpoint.bEndpointAddress;
// Test if we dealing with a bulk IN endpoint
if ((endpoint_address & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_IN && !endpoint_in_set) {
*endpoint_in = endpoint_address;
*max_packet_size = endpoint.wMaxPacketSize;
endpoint_in_set = true;
}
// Test if we dealing with a bulk OUT endpoint
if ((endpoint_address & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_OUT && !endpoint_out_set) {
*endpoint_out = endpoint_address;
*max_packet_size = endpoint.wMaxPacketSize;
endpoint_out_set = true;
}
if (endpoint_in_set && endpoint_out_set) {
libusb_free_config_descriptor(usb_config);
return;
}
}
}
}
libusb_free_config_descriptor(usb_config);
}
}
int usbbus_set_configuration(usbbus_device_handle *dev, int configuration)
{
return libusb_set_configuration((libusb_device_handle *)dev, configuration);
uint8_t get_usb_num_configs(struct libusb_device *dev) {
struct libusb_device_descriptor descriptor;
libusb_get_device_descriptor(dev, &descriptor);
return descriptor.bNumConfigurations;
}
int usbbus_get_string_simple(usbbus_device_handle *dev, int index, char *buf, size_t buflen)
{
return libusb_get_string_descriptor_ascii((libusb_device_handle *)dev, index & 0xff,
(unsigned char *) buf, (int) buflen);
void usbbus_get_usb_device_name(struct libusb_device *dev, libusb_device_handle *udev, char *buffer, size_t len) {
struct libusb_device_descriptor descriptor;
libusb_get_device_descriptor(dev, &descriptor);
if (descriptor.iManufacturer || descriptor.iProduct) {
if (udev) {
libusb_get_string_descriptor_ascii(udev, descriptor.iManufacturer & 0xff, (unsigned char *) buffer, len);
if (strlen(buffer) > 0) {
strncpy(buffer + strlen(buffer), " / ", 4);
}
libusb_get_string_descriptor_ascii(udev,
descriptor.iProduct & 0xff,
(unsigned char *) buffer + strlen(buffer),
len - strlen(buffer));
}
}
}
int usbbus_bulk_transfer(usbbus_device_handle *dev, int ep, char *bytes, int size, int *actual_length, int timeout)
{
return libusb_bulk_transfer((libusb_device_handle *)dev, ep & 0xff, (unsigned char *)bytes, size, actual_length, timeout);
void usbbus_get_device(uint8_t dev_address, struct libusb_device * dev, struct libusb_device_handle * dev_handle) {
struct libusb_device ** device_list;
ssize_t num_devices = libusb_get_device_list(ctx, &device_list);
for (size_t i = 0; i < num_devices; i++) {
if (libusb_get_device_address(device_list[i]) != dev_address) {
continue;
} else {
dev = device_list[i];
int res = libusb_open(dev, &dev_handle);
if (res != 0 || dev_handle == NULL) {
log_put(LOG_GROUP,LOG_CATEGORY,NFC_LOG_PRIORITY_ERROR,
"Unable to open libusb device (%s)",libusb_strerror(res));
continue;
}
}
}
// libusb works with a reference counter which is set to 1 for each device when calling libusb_get_device_list and increased
// by libusb_open. Thus we decrease the counter by 1 for all devices and only the "real" device will survive
libusb_free_device_list(device_list, num_devices);
}
int usbbus_claim_interface(usbbus_device_handle *dev, int interface)
{
return libusb_claim_interface((libusb_device_handle *)dev, interface);
uint16_t usbbus_get_vendor_id(struct libusb_device *dev) {
struct libusb_device_descriptor descriptor;
libusb_get_device_descriptor(dev, &descriptor);
return descriptor.idVendor;
}
int usbbus_release_interface(usbbus_device_handle *dev, int interface)
{
return libusb_release_interface((libusb_device_handle *)dev, interface);
uint16_t usbbus_get_product_id(struct libusb_device *dev) {
struct libusb_device_descriptor descriptor;
libusb_get_device_descriptor(dev, &descriptor);
return descriptor.idProduct;
}
int usbbus_set_interface_alt_setting(usbbus_device_handle *dev, int interface, int alternate)
{
return libusb_set_interface_alt_setting((libusb_device_handle *)dev, interface, alternate);
int usbbus_get_num_alternate_settings(struct libusb_device *dev, uint8_t config_idx) {
struct libusb_config_descriptor *usb_config;
int r = libusb_get_config_descriptor(dev, config_idx, &usb_config);
if (r != 0 || usb_config == NULL) {
return -1;
}
libusb_free_config_descriptor(usb_config);
return usb_config->interface->num_altsetting;
}
int usbbus_reset(usbbus_device_handle *dev)
{
return libusb_reset_device((libusb_device_handle *)dev);
}
const char *usbbus_strerror(int errcode)
{
return libusb_strerror((enum libusb_error)errcode);
}
struct usbbus_bus *usbbus_get_busses(void)
{
return usb_busses;
}

View File

@ -7,6 +7,7 @@
* Copyright (C) 2010-2012 Romain Tartière
* Copyright (C) 2010-2013 Philippe Teuwen
* Copyright (C) 2012-2013 Ludovic Rousseau
* Copyright (C) 2022 Kenspeckle
* See AUTHORS file for a more comprehensive list of contributors.
* Additional contributors of this file:
*
@ -25,150 +26,29 @@
*
*/
/**
* @file usbbus.h
* @brief libusb 0.1 driver header
*/
#ifndef __NFC_BUS_USBBUS_H__
#define __NFC_BUS_USBBUS_H__
#ifndef __NFC_BUS_USB_H__
# define __NFC_BUS_USB_H__
#include <libusb-1.0/libusb.h>
#include <stdbool.h>
#include <string.h>
#define USBBUS_ERROR_ACCESS -3
#define USBBUS_ERROR_TIMEOUT -7
int usbbus_prepare(void);
// Libusb-0.1 API:
#define USBBUS_ENDPOINT_DIR_MASK 0x80
#define USBBUS_ENDPOINT_TYPE_BULK 2
#define USBBUS_ENDPOINT_IN 0x80
#define USBBUS_ENDPOINT_OUT 0x00
#ifdef PATH_MAX
#define USBBUS_PATH_MAX PATH_MAX
#else
#define USBBUS_PATH_MAX 4096
#endif
struct usbbus_device_handle;
typedef struct usbbus_device_handle usbbus_device_handle;
struct usbbus_endpoint_descriptor {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bEndpointAddress;
uint8_t bmAttributes;
uint16_t wMaxPacketSize;
uint8_t bInterval;
uint8_t bRefresh;
uint8_t bSynchAddress;
unsigned char *extra;
int extralen;
};
struct usbbus_interface_descriptor {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bInterfaceNumber;
uint8_t bAlternateSetting;
uint8_t bNumEndpoints;
uint8_t bInterfaceClass;
uint8_t bInterfaceSubClass;
uint8_t bInterfaceProtocol;
uint8_t iInterface;
struct usbbus_endpoint_descriptor *endpoint;
unsigned char *extra;
int extralen;
};
struct usbbus_interface {
struct usbbus_interface_descriptor *altsetting;
int num_altsetting;
};
struct usbbus_config_descriptor {
uint8_t bLength;
uint8_t bDescriptorType;
uint16_t wTotalLength;
uint8_t bNumInterfaces;
uint8_t bConfigurationValue;
uint8_t iConfiguration;
uint8_t bmAttributes;
uint8_t MaxPower;
struct usbbus_interface *interface;
unsigned char *extra;
int extralen;
};
/* Device descriptor */
struct usbbus_device_descriptor {
uint8_t bLength;
uint8_t bDescriptorType;
uint16_t bcdUSB;
uint8_t bDeviceClass;
uint8_t bDeviceSubClass;
uint8_t bDeviceProtocol;
uint8_t bMaxPacketSize0;
uint16_t idVendor;
uint16_t idProduct;
uint16_t bcdDevice;
uint8_t iManufacturer;
uint8_t iProduct;
uint8_t iSerialNumber;
uint8_t bNumConfigurations;
};
struct usbbus_bus;
#define EMPTY_STRING "\0";
struct usbbus_device {
struct usbbus_device *next, *prev;
char filename[USBBUS_PATH_MAX + 1];
struct usbbus_bus *bus;
struct usbbus_device_descriptor descriptor;
struct usbbus_config_descriptor *config;
void *dev;
uint8_t devnum;
unsigned char num_children;
struct usbbus_device **children;
};
struct usbbus_bus {
struct usbbus_bus *next, *prev;
char dirname[USBBUS_PATH_MAX + 1];
struct usbbus_device *devices;
uint32_t location;
struct usbbus_device *root_dev;
uint16_t vendor_id;
uint16_t product_id;
const char *name;
uint16_t max_packet_size;
};
usbbus_device_handle *usbbus_open(struct usbbus_device *dev);
void usbbus_close(usbbus_device_handle *dev);
int usbbus_set_configuration(usbbus_device_handle *dev, int configuration);
int usbbus_get_string_simple(usbbus_device_handle *dev, int index, char *buf, size_t buflen);
int usbbus_bulk_transfer(usbbus_device_handle *dev, int ep, char *bytes, int size, int *actual_length, int timeout);
int usbbus_claim_interface(usbbus_device_handle *dev, int interface);
int usbbus_release_interface(usbbus_device_handle *dev, int interface);
int usbbus_set_interface_alt_setting(usbbus_device_handle *dev, int interface, int alternate);
int usbbus_reset(usbbus_device_handle *dev);
const char *usbbus_strerror(int errcode);
struct usbbus_bus *usbbus_get_busses(void);
#endif // __NFC_BUS_USB_H__
int usbbus_prepare();
size_t usbbus_usb_scan(char ** connstrings, size_t connstrings_len, struct usbbus_device * nfc_usb_devices, size_t num_nfc_usb_devices, char * usb_driver_name);
void usbbus_get_usb_endpoints(struct libusb_device *dev, uint8_t * endpoint_in, uint8_t * endpoint_out, uint16_t * max_packet_size);
void usbbus_get_usb_device_name(struct libusb_device * dev, libusb_device_handle *udev, char *buffer, size_t len);
void usbbus_get_device(uint8_t dev_address, struct libusb_device * dev, struct libusb_device_handle * dev_handle);
uint16_t usbbus_get_vendor_id(struct libusb_device * dev);
uint16_t usbbus_get_product_id(struct libusb_device * dev);
int usbbus_get_num_alternate_settings(struct libusb_device *dev, uint8_t config_idx);
#endif

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff