#include <windows.h> /* for HANDLE */
#endif
-/** @cond PRIVATE */
#define LOG_PREFIX "serial-hid"
-/** @endcond */
#ifdef HAVE_SERIAL_COMM
/* }}} */
/* {{{ open/close/list/find HIDAPI connection, exchange HID requests and data */
+#define IOKIT_PATH_PREFIX "IOService:"
+
/*
* Convert a HIDAPI path (which depends on the target platform, and may
* depend on one of several available API variants on that platform) to
int has_colon;
int is_hex_colon;
- char *name;
+ const char *parse, *remain;
+ char *copy;
- has_colon = strchr(path, ':') != NULL;
- is_hex_colon = strspn(path, accept) == strlen(path);
- if (has_colon && !is_hex_colon) {
- sr_err("Unsupported HIDAPI path format: %s", path);
- return NULL;
- }
+ parse = path;
+ has_colon = strchr(parse, ':') != NULL;
+ is_hex_colon = strspn(parse, accept) == strlen(parse);
if (is_hex_colon) {
- name = g_strdup_printf("%s%s", SER_HID_USB_PREFIX, path);
- g_strcanon(name + strlen(SER_HID_USB_PREFIX), keep, '.');
- } else {
- name = g_strdup_printf("%s%s", SER_HID_RAW_PREFIX, path);
+ /* All hex digits and colon only. Simple substitution. */
+ copy = g_strdup_printf("%s%s", SER_HID_USB_PREFIX, parse);
+ g_strcanon(copy + strlen(SER_HID_USB_PREFIX), keep, '.');
+ return copy;
}
-
- return name;
+ if (!has_colon) {
+ /* "Something raw" and no colon. Add raw= prefix. */
+ copy = g_strdup_printf("%s%s", SER_HID_RAW_PREFIX, parse);
+ return copy;
+ }
+ if (g_str_has_prefix(parse, IOKIT_PATH_PREFIX)) do {
+ /*
+ * Path starts with Mac IOKit literal which contains the
+ * colon. Drop that literal from the start of the path,
+ * and check whether any colon remains which we cannot
+ * deal with. Fall though to other approaches which could
+ * be more generic, or to the error path.
+ */
+ remain = &parse[strlen(IOKIT_PATH_PREFIX)];
+ if (strchr(remain, ':'))
+ break;
+ copy = g_strdup_printf("%s%s", SER_HID_IOKIT_PREFIX, remain);
+ return copy;
+ } while (0);
+
+ /* TODO
+ * Consider adding support for more of the currently unhandled
+ * cases. When we get here, the HIDAPI path could be arbitrarily
+ * complex, none of the above "straight" approaches took effect.
+ * Proper escaping or other transformations could get applied,
+ * though they decrease usability the more they obfuscate the
+ * resulting port name. Ideally users remain able to recognize
+ * their device or cable or port after the manipulation.
+ */
+ sr_err("Unsupported HIDAPI path format: %s", path);
+ return NULL;
}
/*
* Strip off the "raw" prefix, or undo colon substitution. See @ref
* get_hidapi_path_copy() for details.
*/
-static const char *extract_hidapi_path(char *buffer)
+static char *extract_hidapi_path(const char *copy)
{
static const char *keep = "0123456789abcdefABCDEF:";
const char *p;
+ char *path;
- p = buffer;
+ p = copy;
if (!p || !*p)
return NULL;
- if (strncmp(p, SER_HID_RAW_PREFIX, strlen(SER_HID_RAW_PREFIX)) == 0) {
+ if (g_str_has_prefix(p, SER_HID_IOKIT_PREFIX)) {
+ p += strlen(SER_HID_IOKIT_PREFIX);
+ path = g_strdup_printf("%s%s", IOKIT_PATH_PREFIX, p);
+ return path;
+ }
+ if (g_str_has_prefix(p, SER_HID_RAW_PREFIX)) {
p += strlen(SER_HID_RAW_PREFIX);
- return p;
+ path = g_strdup(p);
+ return path;
}
- if (strncmp(p, SER_HID_USB_PREFIX, strlen(SER_HID_USB_PREFIX)) == 0) {
+ if (g_str_has_prefix(p, SER_HID_USB_PREFIX)) {
p += strlen(SER_HID_USB_PREFIX);
- g_strcanon(buffer, keep, ':');
- return p;
+ path = g_strdup(p);
+ g_strcanon(path, keep, ':');
+ return path;
}
return NULL;
*/
vid = curdev->vendor_id;
pid = curdev->product_id;
- sr_dbg("DIAG: hidapi enum, vid:pid %04x:%04x, path %s\n",
- curdev->vendor_id, curdev->product_id, curdev->path);
chipname = ser_hid_chip_find_name_vid_pid(vid, pid);
if (!chipname)
chipname = "<chip>";
pid = curdev->product_id;
desc = g_string_sized_new(128);
g_string_append_printf(desc, "HID");
- if (manuf)
+ if (manuf && wcslen(manuf) != 0)
g_string_append_printf(desc, " %ls", manuf);
- if (prod)
+ if (prod && wcslen(prod) != 0)
g_string_append_printf(desc, " %ls", prod);
- if (serno)
+ if (serno && wcslen(serno) != 0)
g_string_append_printf(desc, " %ls", serno);
if (vid && pid)
- g_string_append_printf(desc, " %04hx.%04hx", vid, pid);
+ g_string_append_printf(desc, " [%04hx.%04hx]", vid, pid);
list = append(list, name, desc->str);
g_string_free(desc, TRUE);
g_free(name);
/* Get the serial number of a device specified by path. */
static int ser_hid_hidapi_get_serno(const char *path, char *buf, size_t blen)
{
- char *usbpath;
- const char *hidpath;
+ char *hidpath;
hid_device *dev;
wchar_t *serno_wch;
int rc;
if (!path || !*path)
return SR_ERR_ARG;
- usbpath = g_strdup(path);
- hidpath = extract_hidapi_path(usbpath);
+ hidpath = extract_hidapi_path(path);
dev = hidpath ? hid_open_path(hidpath) : NULL;
- g_free(usbpath);
+ g_free(hidpath);
if (!dev)
return SR_ERR_IO;
* its meaning are said to be OS specific, which is why we may
* not assume anything about it...
*/
- char *usbpath;
- const char *hidpath;
+ char *hidpath;
struct hid_device_info *devs, *dev;
int found;
- usbpath = g_strdup(path);
- hidpath = extract_hidapi_path(usbpath);
- if (!hidpath) {
- g_free(usbpath);
+ hidpath = extract_hidapi_path(path);
+ if (!hidpath)
return SR_ERR_NA;
- }
devs = hid_enumerate(0x0000, 0x0000);
found = 0;
break;
}
hid_free_enumeration(devs);
- g_free(usbpath);
+ g_free(hidpath);
return found ? SR_OK : SR_ERR_NA;
#endif
if (!serial->hid_path)
serial->hid_path = extract_hidapi_path(serial->usb_path);
hid_dev = hid_open_path(serial->hid_path);
- sr_dbg("DBG: %s(), hid_open_path(\"%s\") => %p", __func__,
- serial->hid_path, hid_dev);
if (!hid_dev) {
+ g_free((void *)serial->hid_path);
serial->hid_path = NULL;
return SR_ERR_IO;
}
if (serial->hid_dev) {
hid_close(serial->hid_dev);
serial->hid_dev = NULL;
+ g_free((void *)serial->hid_path);
serial->hid_path = NULL;
}
g_slist_free_full(serial->hid_source_args, g_free);
uint8_t rx_buf[SER_HID_CHUNK_SIZE];
int rc;
- sr_dbg("DBG: %s() fd %d, evt 0x%x, data %p.", __func__, fd, revents, cb_data);
args = cb_data;
/*
rc = args->serial->hid_chip_funcs->read_bytes(args->serial,
rx_buf, sizeof(rx_buf), 0);
if (rc > 0) {
- sr_dbg("DBG: %s() queueing %d bytes.", __func__, rc);
ser_hid_mask_databits(args->serial, rx_buf, rc);
sr_ser_queue_rx_data(args->serial, rx_buf, rc);
}
if (sr_ser_has_queued_data(args->serial))
revents |= G_IO_IN;
rc = args->cb(fd, revents, args->cb_data);
- sr_dbg("DBG: %s() rc %d.", __func__, rc);
return rc;
}
struct hidapi_source_args_t *args;
int rc;
- sr_dbg("DBG: %s() evt 0x%x, to %d.", __func__, events, timeout);
(void)events;
/* Optionally enforce a minimum poll period. */
*/
rc = sr_session_source_add(session, -1, events, timeout,
hidapi_source_cb, args);
- sr_dbg("DBG: %s() added, rc %d.", __func__, rc);
if (rc != SR_OK) {
g_free(args);
return rc;
{
(void)serial;
- sr_dbg("DBG: %s().", __func__);
(void)sr_session_source_remove(session, -1);
/*
* Release callback args here already? Can there be more than
return NULL;
}
-static const char *ser_hid_chip_get_text(enum ser_hid_chip_t idx)
-{
- struct ser_hid_chip_functions *desc;
-
- desc = get_hid_chip_funcs(idx);
- if (!desc)
- return NULL;
-
- return desc->chipdesc;
-}
-
/**
* See if a text string is a valid USB path for a HID device.
* @param[in] serial The serial port that is about to get opened.
* @return 0 upon success, non-zero upon failure. Fills the *_ref output
* values.
*
- * @internal
- *
* Summary of parsing rules as they are implemented:
* - Insist on the "hid" prefix. Accept "hid" alone without any other
* additional field.
if (!serial || !spec || !*spec)
return SR_ERR_ARG;
- sr_dbg("DBG: %s(), input spec: %s", __func__, spec);
p = spec;
/* The "hid" prefix is mandatory. */
- if (!g_str_has_prefix(p, SER_HID_CONN_PREFIX)) {
- sr_dbg("DBG: %s(), not a HID port", __func__);
+ if (!g_str_has_prefix(p, SER_HID_CONN_PREFIX))
return SR_ERR_ARG;
- }
p += strlen(SER_HID_CONN_PREFIX);
/*
break;
if (g_str_has_prefix(p, SER_HID_USB_PREFIX)) {
rc = try_open_path(serial, p);
- sr_dbg("DBG: %s(), open usb path %s => rc %d", __func__, p, rc);
+ if (rc != SR_OK)
+ return rc;
+ path = g_strdup(p);
+ p += strlen(p);
+ } else if (g_str_has_prefix(p, SER_HID_IOKIT_PREFIX)) {
+ rc = try_open_path(serial, p);
if (rc != SR_OK)
return rc;
path = g_strdup(p);
p += strlen(p);
} else if (g_str_has_prefix(p, SER_HID_RAW_PREFIX)) {
rc = try_open_path(serial, p);
- sr_dbg("DBG: %s(), open raw path %s => rc %d", __func__, p, rc);
if (rc != SR_OK)
return rc;
path = g_strdup(p);
p += strlen(p);
} else if (g_str_has_prefix(p, SER_HID_SNR_PREFIX)) {
p += strlen(SER_HID_SNR_PREFIX);
- sr_dbg("DBG: %s(), snr %s", __func__, p);
serno = g_strdup(p);
p += strlen(p);
} else if (!chip) {
char *copy;
const char *endptr;
- const char *name;
copy = g_strdup(p);
endptr = copy;
chip = ser_hid_chip_find_enum(&endptr);
- sr_dbg("DBG: %s(), chip search, %s => %u", __func__, p, chip);
if (!chip) {
g_free(copy);
return SR_ERR_ARG;
}
p += endptr - copy;
g_free(copy);
- name = ser_hid_chip_get_text(chip);
- sr_dbg("DBG: %s(), chip %s", __func__, name);
} else {
sr_err("unsupported conn= spec %s, error at %s", spec, p);
return SR_ERR_ARG;
break;
}
- sr_dbg("DBG: %s() done, chip %d, path %s, serno %s", __func__, chip, path, serno);
if (chip_ref)
*chip_ref = chip;
if (path_ref && path)
/* Get and compare serial number. Boolean return value. */
static int check_serno(const char *path, const char *serno_want)
{
- char *usb_path;
- const char *hid_path;
+ char *hid_path;
char serno_got[128];
int rc;
- sr_dbg("DBG: %s(\"%s\", \"%s\")", __func__, path, serno_want);
-
- usb_path = g_strdup(path);
- hid_path = extract_hidapi_path(usb_path);
+ hid_path = extract_hidapi_path(path);
rc = ser_hid_hidapi_get_serno(hid_path, serno_got, sizeof(serno_got));
- sr_dbg("DBG: %s(), usb %s, hidapi %s => rc %d", __func__, usb_path, hid_path, rc);
- g_free(usb_path);
+ g_free(hid_path);
if (rc) {
sr_dbg("DBG: %s(), could not get serial number", __func__);
return 0;
}
- sr_dbg("DBG: %s(), got serno \"%s\"", __func__, serno_got);
-
- rc = strcmp(serno_got, serno_want) == 0;
- sr_dbg("DBG: %s(), return %d", __func__, rc);
- return rc;
+ return strcmp(serno_got, serno_want) == 0;
}
static GSList *append_find(GSList *devs, const char *path)
vid = vid_pids[idx].vid;
pid = vid_pids[idx].pid;
}
- sr_dbg("DBG: %s(), searching VID:PID %04hx:%04hx",
- __func__, vid, pid);
list = ser_hid_hidapi_find_usb(list, append_find, vid, pid);
if (!vid_pids)
break;
* @retval SR_OK upon success
* @retval SR_ERR_* upon failure.
*
- * @internal
- *
* This routine fills in blanks which the conn= spec parser left open.
* When not specified yet, the HID chip type gets determined. When a
* serial number was specified, then search the corresponding device.
if (!path_ref)
return SR_ERR_ARG;
path = *path_ref;
- sr_dbg("DBG: %s() enter, chip %d, path %s, serno %s", __func__,
- chip, path, serno ? serno : "<none>");
/*
* Simplify the more complex conditions somewhat by assigning
have_chip = (chip != SER_HID_CHIP_UNKNOWN) ? 1 : 0;
have_path = (path && *path) ? 1 : 0;
have_serno = (serno && *serno) ? 1 : 0;
- sr_dbg("DBG: %s(), have chip %d, path %d, serno %d", __func__,
- have_chip, have_path, have_serno);
if (have_path && have_serno) {
sr_err("Unsupported combination of USB path and serno");
return SR_ERR_ARG;
if (have_chip && !chip_funcs->vid_pid_items)
return SR_ERR_NA;
if (have_path && !have_chip) {
- sr_dbg("DBG: %s(), searching chip for path %s", __func__, path);
vid = pid = 0;
rc = ser_hid_hidapi_get_vid_pid(path, &vid, &pid);
- sr_dbg("DBG: %s(), rc %d, VID:PID %04x:%04x", __func__, rc, vid, pid);
if (rc != SR_OK)
return rc;
name = ser_hid_chip_find_name_vid_pid(vid, pid);
- sr_dbg("DBG: %s(), name %s", __func__, name);
if (!name || !*name)
return SR_ERR_NA;
chip = ser_hid_chip_find_enum(&name);
- sr_dbg("DBG: %s(), chip %d", __func__, chip);
if (chip == SER_HID_CHIP_UNKNOWN)
return SR_ERR_NA;
have_chip = 1;
}
if (have_serno) {
- sr_dbg("DBG: %s(), searching path for serno %s", __func__, serno);
vid_pids = have_chip ? chip_funcs->vid_pid_items : NULL;
list = list_paths_for_vids_pids(vid_pids);
- sr_dbg("DBG: %s(), vid/pid list for chip type %p", __func__, list);
if (!list)
return SR_ERR_NA;
matched = NULL;
for (tmplist = list; tmplist; tmplist = tmplist->next) {
path = get_hidapi_path_copy(tmplist->data);
- sr_dbg("DBG: %s(), checking %s", __func__, path);
serno_matched = check_serno(path, serno);
g_free(path);
if (!serno_matched)
if (!matched)
return SR_ERR_NA;
path = g_strdup(matched->data);
- sr_dbg("DBG: %s(), match, path %s", __func__, path);
have_path = 1;
g_slist_free_full(list, g_free);
}
if (!have_path) {
- sr_dbg("DBG: %s(), searching path, chip %d", __func__, chip);
vid_pids = have_chip ? chip_funcs->vid_pid_items : NULL;
list = list_paths_for_vids_pids(vid_pids);
if (!list)
return SR_ERR_NA;
- for (tmplist = list; tmplist; tmplist = tmplist->next) {
- path = tmplist->data;
- sr_dbg("DBG: %s(), path %s", __func__, path);
- }
matched = matched2 = NULL;
if (have_chip) {
/* List already only contains specified chip. */
matched = list;
- path = matched->data;
- sr_dbg("DBG: %s(), match 1 %s", __func__, path);
matched2 = list->next;
- if (matched2) {
- path = matched2->data;
- sr_dbg("DBG: %s(), match 2 %s", __func__, path);
- }
}
/* Works for lists with one or multiple chips. Saves indentation. */
for (tmplist = list; tmplist; tmplist = tmplist->next) {
continue;
if (!matched) {
matched = tmplist;
- sr_dbg("DBG: %s(), match 1 %s", __func__, path);
continue;
}
if (!matched2) {
matched2 = tmplist;
- sr_dbg("DBG: %s(), match 2 %s", __func__, path);
break;
}
}
if (matched2)
sr_info("More than one cable matches, random pick.");
path = get_hidapi_path_copy(matched->data);
- sr_dbg("DBG: %s(), match, path %s", __func__, path);
have_path = 1;
g_slist_free_full(list, g_free);
}
if (have_path && !have_chip) {
- sr_dbg("DBG: %s(), searching chip for path %s", __func__, path);
vid = pid = 0;
rc = ser_hid_hidapi_get_vid_pid(path, &vid, &pid);
- sr_dbg("DBG: %s(), rc %d, VID:PID %04x:%04x", __func__, rc, vid, pid);
if (rc != SR_OK)
return rc;
name = ser_hid_chip_find_name_vid_pid(vid, pid);
- sr_dbg("DBG: %s(), name %s", __func__, name);
if (!name || !*name)
return SR_ERR_NA;
chip = ser_hid_chip_find_enum(&name);
- sr_dbg("DBG: %s(), chip %d", __func__, chip);
if (chip == SER_HID_CHIP_UNKNOWN)
return SR_ERR_NA;
have_chip = 1;
}
- sr_dbg("DBG: %s() leave, chip %d, path %s", __func__, chip, path);
if (chip_ref)
*chip_ref = chip;
if (path_ref)
* device's USB path.
*/
if (!chip || !usbpath || serno) {
- sr_dbg("DBG: %s(), searching ...", __func__);
rc = ser_hid_chip_search(&chip, &usbpath, serno);
if (rc != 0)
return SR_ERR_NA;
serial->usb_serno = NULL;
return SR_ERR_IO;
}
- sr_dbg("DBG: %s() done, OK", __func__);
if (!serial->rcv_buffer)
serial->rcv_buffer = g_string_sized_new(SER_HID_CHUNK_SIZE);
static int ser_hid_close(struct sr_serial_dev_inst *serial)
{
- sr_dbg("DBG: %s()", __func__);
ser_hid_hidapi_close_dev(serial);
return SR_OK;
int baudrate, int bits, int parity, int stopbits,
int flowcontrol, int rts, int dtr)
{
- int rc;
-
- sr_dbg("DBG: %s() enter", __func__);
if (ser_hid_setup_funcs(serial) != 0)
return SR_ERR_NA;
- sr_dbg("DBG: %s() chip funcs set", __func__);
if (!serial->hid_chip_funcs || !serial->hid_chip_funcs->set_params)
return SR_ERR_NA;
- sr_dbg("DBG: %s() set params avail", __func__);
- rc = serial->hid_chip_funcs->set_params(serial,
+
+ return serial->hid_chip_funcs->set_params(serial,
baudrate, bits, parity, stopbits,
flowcontrol, rts, dtr);
- sr_dbg("DBG: %s() set params rc %d", __func__, rc);
-
- return rc;
}
static int ser_hid_setup_source_add(struct sr_session *session,
if (!serial->hid_chip_funcs->max_bytes_per_request)
return SR_ERR_NA;
- sr_dbg("DBG: %s() shall send %zu bytes TX data.", __func__, count);
total = 0;
max_chunk = serial->hid_chip_funcs->max_bytes_per_request;
while (count > 0) {
int rc;
unsigned int got;
- sr_dbg("DBG: %s() count %zd, block %d, to %u", __func__,
- count, !nonblocking, timeout_ms);
-
if (!serial->hid_chip_funcs || !serial->hid_chip_funcs->read_bytes)
return SR_ERR_NA;
if (!serial->hid_chip_funcs->max_bytes_per_request)
return SR_ERR;
}
if (rc) {
- sr_dbg("DBG: %s() queueing %d bytes.", __func__, rc);
ser_hid_mask_databits(serial, buffer, rc);
sr_ser_queue_rx_data(serial, buffer, rc);
}
*/
if (got > count)
got = count;
- sr_dbg("DBG: %s() passing %d bytes.", __func__, got);
return sr_ser_unqueue_rx_data(serial, buf, count);
}
.write = ser_hid_write,
.read = ser_hid_read,
.set_params = ser_hid_set_params,
+ .set_handshake = std_dummy_set_handshake,
.setup_source_add = ser_hid_setup_source_add,
.setup_source_remove = ser_hid_setup_source_remove,
.list = ser_hid_list,