You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

434 lines
9.5 KiB

#define _GNU_SOURCE /* for memrchr() */
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <xtalk/debug.h>
#include <autoconfig.h>
#include "xusb_common.h"
#define DBG_MASK 0x01
const char *xusb_tt_name(enum xusb_transfer_type tt)
{
switch (tt) {
case XUSB_TT_BULK: return "BULK";
case XUSB_TT_INTERRUPT: return "INTERRUPT";
case XUSB_TT_ILLEGAL:
break;
}
return "ILLEGAL";
}
/* GCC versions before 4.6 did not support neither push and pop on
* the diagnostic pragma nor applying it inside a function.
*/
#ifndef HAVE_GCC_PRAGMA_DIAG_STACK
#pragma GCC diagnostic ignored "-Wformat-security"
#endif
int xusb_printf(const struct xusb_iface *iface, int level, int debug_mask,
const char *prefix, const char *fmt, ...)
{
int n;
va_list ap;
char fmtbuf[BUFSIZ];
char tmpbuf[BUFSIZ];
snprintf(fmtbuf, sizeof(fmtbuf), "%s%03d/%03d[%d] %s",
prefix,
xusb_bus_num(iface->xusb_device),
xusb_device_num(iface->xusb_device),
xusb_interface_num(iface),
fmt);
va_start(ap, fmt);
n = vsnprintf(tmpbuf, sizeof(tmpbuf), fmtbuf, ap);
va_end(ap);
#ifdef HAVE_GCC_PRAGMA_DIAG_STACK
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat-security"
#endif
log_function(level, debug_mask, tmpbuf);
#ifdef HAVE_GCC_PRAGMA_DIAG_STACK
#pragma GCC diagnostic pop
#endif
return n;
}
#ifndef HAVE_GCC_PRAGMA_DIAG_STACK
#pragma GCC diagnostic error "-Wformat-security"
#endif
int xusb_printf_details(const struct xusb_iface *iface, int level, int debug_mask,
const char *file, int line, const char *severity, const char *func,
const char *fmt, ...)
{
int n;
va_list ap;
char prefix[BUFSIZ];
char tmpbuf[BUFSIZ];
va_start(ap, fmt);
vsnprintf(tmpbuf, sizeof(tmpbuf), fmt, ap);
va_end(ap);
snprintf(prefix, sizeof(prefix), "%s:%d: %s(%s): ",
file, line, severity, func);
va_start(ap, fmt);
n = xusb_printf(iface, level, DBG_MASK, prefix, tmpbuf);
va_end(ap);
return n;
}
void xusb_init_spec(struct xusb_spec *spec, char *name,
uint16_t vendor_id, uint16_t product_id)
{
DBG("Initialize [%02X:%02X] - %s\n", vendor_id, product_id, name);
memset(spec, 0, sizeof(*spec));
spec->name = name;
spec->vendor_id = vendor_id;
spec->product_id = product_id;
}
const struct xusb_spec *xusb_device_spec(const struct xusb_device *xusb_device)
{
return xusb_device->spec;
}
/*
* Match the string "tail" as the tail of string "path"
* Returns 1 in case they match, 0 otherwise
*/
int match_devpath(const char *path, const char *tail)
{
int len_path = strlen(path);
int len_tail = strlen(tail);
int path_offset = len_path - len_tail;
if (path_offset < 0)
return 0;
return strstr(path + path_offset, tail) != NULL;
}
int match_device(const struct xusb_device *xusb_device,
const struct xusb_spec *spec)
{
assert(xusb_device);
DBG("Checking: %04X:%04X: "
"\"%s\"\n",
spec->vendor_id,
spec->product_id,
spec->name);
if (xusb_device->idVendor != spec->vendor_id) {
DBG("Wrong vendor id 0x%X\n", xusb_device->idVendor);
return 0;
}
if (xusb_device->idProduct != spec->product_id) {
DBG("Wrong product id 0x%X\n", xusb_device->idProduct);
return 0;
}
return 1;
}
struct xusb_device *xusb_deviceof(struct xusb_iface *iface)
{
return iface->xusb_device;
}
int xusb_is_claimed(struct xusb_iface *iface)
{
return iface->is_claimed != 0;
}
struct xusb_iface *xusb_interface_of(const struct xusb_device *dev, int num)
{
return dev->interfaces[num];
}
#define XUSB_IFACE_DUMP(prefix, level, iface) \
XUSB_PRINT((iface), level, "%s%d\tep_out=0x%2X ep_in=0x%02X [%s]\n", \
(prefix), \
(iface)->interface_num, \
(iface)->ep_out, \
(iface)->ep_in, \
(iface)->iInterface)
void xusb_list_dump(struct xlist_node *xusb_list)
{
struct xlist_node *curr;
struct xusb_device *xusb_device;
for (curr = xusb_list->next; curr != xusb_list; curr = curr->next) {
struct xusb_iface **piface;
xusb_device = curr->data;
assert(xusb_device);
DBG("%s: usb:ID=%04X:%04X [%s / %s / %s]\n",
xusb_device->devpath_tail,
xusb_device->idVendor,
xusb_device->idProduct,
xusb_device->iManufacturer,
xusb_device->iProduct,
xusb_device->iSerialNumber
);
for (piface = xusb_device->interfaces; *piface; piface++)
XUSB_IFACE_DUMP("\t", DEBUG, *piface);
}
}
void xusb_destroy_interface(struct xusb_iface *iface)
{
if (iface) {
xusb_release(iface);
XUSB_DBG(iface, "MEM: FREE interface\n");
memset(iface, 0, sizeof(*iface));
free(iface);
iface = NULL;
}
}
static const char *path_tail(const char *path)
{
const char *p;
assert(path != NULL);
/* Find last '/' */
p = memrchr(path, '/', strlen(path));
if (!p) {
ERR("Missing a '/' in %s\n", path);
return NULL;
}
/* Search for a '/' before that */
p = memrchr(path, '/', p - path);
if (!p)
p = path; /* No more '/' */
else
p++; /* skip '/' */
return p;
}
int xusb_filter_bypath(const struct xusb_device *xusb_device, void *data)
{
const char *p;
const char *path = data;
DBG("%s\n", path);
assert(path != NULL);
p = path_tail(path);
if (strcmp(xusb_device->devpath_tail, p) != 0) {
DBG("%s: device path missmatch (!= '%s')\n",
xusb_device->devpath_tail, p);
return 0;
}
return 1;
}
struct xusb_iface *xusb_open_one(const struct xusb_spec *specs, int numspecs,
int interface_num,
xusb_filter_t filterfunc, void *data)
{
struct xlist_node *xusb_list;
struct xlist_node *curr;
int num;
struct xusb_device *xusb_device = NULL;
struct xusb_iface *iface = NULL;
int ret;
xusb_list = xusb_find_byproduct(specs, numspecs, filterfunc, data);
num = xlist_length(xusb_list);
DBG("total %d devices\n", num);
switch (num) {
case 0:
ERR("No matching device.\n");
break;
case 1:
curr = xlist_shift(xusb_list);
xusb_device = curr->data;
xlist_destroy(curr, NULL);
xlist_destroy(xusb_list, NULL);
ret = xusb_claim(xusb_device, interface_num, &iface);
if (ret < 0) {
ERR("%s: Failed claiming interface %d (ret = %d)\n",
xusb_device->devpath_tail,
interface_num,
ret);
xusb_destroy(xusb_device);
return NULL;
}
break;
default:
ERR("Too many devices (%d). Aborting.\n", num);
break;
}
return iface;
}
int xusb_interface_num(const struct xusb_iface *iface)
{
return iface->interface_num;
}
uint16_t xusb_vendor_id(const struct xusb_device *xusb_device)
{
return xusb_device->idVendor;
}
uint16_t xusb_product_id(const struct xusb_device *xusb_device)
{
return xusb_device->idProduct;
}
size_t xusb_packet_size(const struct xusb_device *xusb_device)
{
return xusb_device->packet_size;
}
const char *xusb_serial(const struct xusb_device *xusb_device)
{
return xusb_device->iSerialNumber;
}
const char *xusb_devpath(const struct xusb_device *xusb_device)
{
return xusb_device->devpath_tail;
}
uint16_t xusb_bus_num(const struct xusb_device *xusb_device)
{
return xusb_device->bus_num;
}
uint16_t xusb_device_num(const struct xusb_device *xusb_device)
{
return xusb_device->device_num;
}
const char *xusb_interface_name(const struct xusb_iface *iface)
{
return iface->iInterface;
}
const char *xusb_manufacturer(const struct xusb_device *xusb_device)
{
return xusb_device->iManufacturer;
}
const char *xusb_product(const struct xusb_device *xusb_device)
{
return xusb_device->iProduct;
}
const struct xusb_spec *xusb_spec(const struct xusb_device *xusb_device)
{
return xusb_device->spec;
}
int xusb_flushread(struct xusb_iface *iface)
{
char tmpbuf[BUFSIZ];
int ret;
XUSB_DBG(iface, "starting...\n");
memset(tmpbuf, 0, BUFSIZ);
ret = xusb_recv(iface, tmpbuf, BUFSIZ, 1);
if (ret < 0 && ret != -ETIMEDOUT) {
XUSB_ERR(iface, "ret=%d\n", ret);
return ret;
} else if (ret > 0) {
XUSB_DBG(iface, "Got %d bytes:\n", ret);
dump_packet(LOG_DEBUG, DBG_MASK, __func__, tmpbuf, ret);
}
return 0;
}
static int use_clear_halt = 0;
static int xtalk_one_option(const char *option_string)
{
if (strcmp(option_string, "use-clear-halt") == 0) {
use_clear_halt = 1;
return 0;
}
if (strcmp(option_string, "no-use-clear-halt") == 0) {
use_clear_halt = 0;
return 0;
}
ERR("Unknown XTALK_OPTIONS content: '%s'\n", option_string);
return -EINVAL;
}
int xtalk_option_use_clear_halt(void)
{
return use_clear_halt;
}
static void chomp(char *buf)
{
char *p;
int len;
if (!buf)
return;
len = strlen(buf);
for (p = buf + len - 1; p >= buf && isspace(*p); p--)
*p = '\0';
}
static const char *OPTION_VAR = "XTALK_OPTIONS";
/* Caller should free the returned string if it is not NULL */
static char *read_options(const char *fname)
{
FILE *fp;
char buf[BUFSIZ];
char *p;
char *ret_buf;
fp = fopen(fname, "r");
if (!fp) {
DBG("Failed opening '$fname': %s\n", strerror(errno));
return NULL;
}
while (fgets(buf, sizeof(buf), fp) != NULL) {
chomp(buf);
if (buf[0] == '\0' || buf[0] == '#')
continue;
if (strncmp(buf, OPTION_VAR, strlen(OPTION_VAR)) != 0)
continue;
/* fprintf(stderr, "INPUT> '%s'\n", p); */
p = buf + strlen(OPTION_VAR);
while (*p && (isspace(*p) || *p == '='))
p++;
ret_buf = malloc(sizeof(buf));
strcpy(ret_buf, p); /* Cannot overflow */
return ret_buf;
}
fclose(fp);
return NULL;
}
int xtalk_parse_options(void)
{
char *xtalk_options;
char *saveptr;
char *token;
int ret;
int free_options = 0;
xtalk_options = getenv("XTALK_OPTIONS");
if (!xtalk_options) {
xtalk_options = read_options(XTALK_OPTIONS_FILE);
if (!xtalk_options)
return 0;
free_options = 1;
}
token = strtok_r(xtalk_options, " \t", &saveptr);
while (token) {
ret = xtalk_one_option(token);
if (ret < 0)
return ret;
token = strtok_r(NULL, " \t", &saveptr);
}
if (free_options)
free(xtalk_options);
return 0;
}