/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2004, 2011, Oracle and/or its affiliates. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #include "usb.h" #include "wr_libusb.h" /* * libusb wrapper library * * Implements the libusb specification and calls into platform dependent * library implementations of sunray and solaris. * * Loads the .so's found in the default directory or pointed * to by the environment variable SUN_LIBUSBPLUGIN_DIR * If there are problems with the module then unload * the module and continue * * Reads plugin versions from the libusb_version variable * Reads plugin prefixes from libusb_prefix variable * Calls libusb_init implementation of the plugin to determine whether * the plugin bus is supported, unsupported, or exclusive. * This is from the return values 0, -1, 2 * * In the exclusive case all others plugins will be disabled * Case1: ret = PLUGIN_EXCLUSIVE [2]: * Sun Ray Environment where server bus is not requested * via an environment variable * Case2: ret = FAILURE [-1] * In the failure case the scenario is an app running * in a workstation where the sunray bus cannot be made available. * Case3: ret = SUCCESS [0]: * All busses are supported * * The plugins implement certain policies to determine whether access * to their bus will be granted and how. * These policies are internal to the plugin and not relevant to the wrapper * The wrapper merely supports the implementation of these policies * via environment variables and init return values. * * Loads all the symbols of the plugins and maps apps calls -> plugin * * The wrapper library maintains the open device handle states to be * able to map the device handle to the correct plugin. As such each * open and close results in updates to the dev handle list. * */ static int read_plugin_entries(void); static int load_plugin_syms(plugin_info_t *, void *, int); static void wusb_symbol(char **, int, char *, char *); static int get_pindex(struct usb_dev_handle *, const char *); static void unload_plugin(int); static int load_plugins(); static int get_index_devicep(struct usb_device *); static int get_index_devhdl(struct usb_dev_handle *); static int usb_add_dev(struct usb_dev_handle *, struct usb_device *, int); static void usb_del_dev(struct usb_dev_handle *, int); static int check_sym_hdl(int, int); static void *lookup_sym_hdl(usb_dev_handle *, int, const char *); static void usb_dprintf(int level, char *format, ...); static int is_libusb_plugin(char *); static int libusb_debug = DEBUG_NONE; struct usb_bus *usb_busses; static mutex_t bus_lock = DEFAULTMUTEX; static wrapper_info_t winfo; static plugin_info_t p_info[MAX_PLUGINS]; #ifdef _LP64 #define PLUGIN_PREFIX "/64/" #else /* _LP64 */ #define PLUGIN_PREFIX "" #endif /* _LP64 */ /* * Reads the plugin libraries from the specified dir * or the user supplied SUN_LIBUSBPLUGIN_DIR * populates the p_name and p_path fields of the array of plugin_info structs * increments the array index pindex after each .so is read * returns -1(FAILURE) on error */ static int read_plugin_entries() { int pindex = 0; int plugin_found = 0; char *alt_dir; DIR *dirp; struct dirent *dp; char *tmpplugindir = PLUGIN_DIR; char *suffix; char modpath[PATH_MAX + 1]; char plugindir[PATH_MAX + 1]; if ((alt_dir = getenv("SUN_LIBUSBPLUGIN_DIR")) != NULL) { tmpplugindir = alt_dir; } if (strnlen(tmpplugindir, PATH_MAX) > (PATH_MAX - sizeof (PLUGIN_PREFIX))) { usb_dprintf(DEBUG_FUNCTIONS, "Invalid plugin directory: %s\n", tmpplugindir); return (FAILURE); } /* * Construct the plugin directory * Default plugin for 32 bit: /usr/lib/libusb_plugins/ * for 64 bit: /usr/lib/libusb_plugins/64/ * * If SUN_LIBUSBPLUGIN_DIR is set, the plugin is: * 32bit: SUN_LIBUSBPLUGIN_DIR/ * 64bit: SUN_LIBUSBPLUGIN_DIR/64/ */ snprintf(plugindir, PATH_MAX, "%s%s", tmpplugindir, PLUGIN_PREFIX); usb_dprintf(DEBUG_FUNCTIONS, "plugins dir: %s\n", plugindir); if ((dirp = opendir(plugindir)) == NULL) { usb_dprintf(DEBUG_ERRORS, "%s: %s \n", plugindir, strerror(errno)); return (FAILURE); } while (dirp) { errno = 0; if ((dp = readdir(dirp)) != NULL) { usb_dprintf(DEBUG_FUNCTIONS, "reading entry %s\n", dp->d_name); /* skip . and .. entries */ if (strncmp(dp->d_name, ".", 1) == 0) { continue; } if (strncmp(dp->d_name, "..", 2) == 0) { continue; } /* * Ignore files that are not *.so.X * this ensures that libusb.so.1 and plugins.so.1 * and libusb.so.2 and plugin.so.2 can both be * supported */ if ((suffix = strstr(dp->d_name, MOD_SUFFIX)) == NULL) { continue; } else if (suffix[strlen(MOD_SUFFIX)] != '.') { usb_dprintf(DEBUG_RECOVERABLE, "did not load %s:%s\n", plugindir, dp->d_name); continue; } usb_dprintf(DEBUG_FUNCTIONS, "reading .so file %s:%s\n", plugindir, dp->d_name); p_info[pindex].p_name = strdup(dp->d_name); /* * using PATH_MAX len here */ if (strlen(p_info[pindex].p_name) > PATH_MAX) { usb_dprintf(DEBUG_RECOVERABLE, "pathname > PATH_MAX %s", p_info[pindex].p_name); /* go on to try read next module */ free(p_info[pindex].p_name); continue; } (void) strncpy(modpath, plugindir, PATH_MAX); (void) strncat(modpath, "/", 1); (void) strncat(modpath, p_info[pindex].p_name, PATH_MAX); p_info[pindex].p_path = strdup(modpath); usb_dprintf(DEBUG_FUNCTIONS, "Path is %s\n", p_info[pindex].p_path); /* * If we do not detect a valid libusb plugin * then let's skip and continue * do not update pindex if we want to continue * we will dlopen and dlclose in this check */ if (is_libusb_plugin(p_info[pindex].p_path) != SUCCESS) { free(p_info[pindex].p_path); free(p_info[pindex].p_name); continue; } plugin_found = 1; if (++pindex == MAX_PLUGINS) { usb_dprintf(DEBUG_FUNCTIONS, "Max plugins read %d\n", pindex); break; } } else { if (errno == 0) { (void) closedir(dirp); if (!plugin_found) { usb_dprintf(DEBUG_ERRORS, "No plugin found \n"); return (FAILURE); } else { /* end of dir stream */ return (SUCCESS); } } else { (void) closedir(dirp); return (FAILURE); } } } return (SUCCESS); } /* * In a directory crowded with a lot of .so's * filter out potential libusb plugins - helps * the plugin loader to only load valid plugins * uses the libusb_version symbol as a filter */ static int is_libusb_plugin(char *modname) { void *hdl; void *hdl_sym1; void *hdl_sym2; char *symbol1 = "usb_init"; char *symbol2 = "libusb_prefix"; hdl = dlopen(modname, RTLD_NOW); if (hdl == NULL) { usb_dprintf(DEBUG_RECOVERABLE, "%s could not be loaded \n", modname); return (FAILURE); } hdl_sym1 = (void *)(dlsym(hdl, symbol1)); hdl_sym2 = (void *)(dlsym(hdl, symbol2)); (void) dlclose(hdl); if ((hdl_sym1 == NULL) && (hdl_sym2 == NULL)) { usb_dprintf(DEBUG_FUNCTIONS, "%s not a libusb plugin: unload\n", modname); return (FAILURE); } else { return (SUCCESS); } } /* * Read the plugin entry names * Load the plugins and populate the proper data structures * this includes things like bus/dev ptr mappings, handles, etc. * Also loads all the symbols also and store the handles * Lock: called with bus_lock held * called from usb_init */ static int load_plugins() { int pindex = 0; void *handle; int module_loaded = 0; if (read_plugin_entries() != SUCCESS) { usb_dprintf(DEBUG_FUNCTIONS, "Failed to load libusb plugins \n"); return (FAILURE); } usb_dprintf(DEBUG_FUNCTIONS, "load_plugin: modname is %s\n", p_info[pindex].p_name); /* * Will load at most MAX_PLUGINS */ while (pindex < MAX_PLUGINS) { /* reached the end of modules read into the array */ if (p_info[pindex].p_name == NULL) { break; } usb_dprintf(DEBUG_FUNCTIONS, "loading:%s pindex:%d\n", p_info[pindex].p_name, pindex); handle = dlopen(p_info[pindex].p_path, RTLD_NOW); if (handle == NULL) { usb_dprintf(DEBUG_RECOVERABLE, "handle for %s is null\n", p_info[pindex].p_name); usb_dprintf(DEBUG_RECOVERABLE, dlerror()); p_info[pindex].p_handle = NULL; free(p_info[pindex].p_name); free(p_info[pindex].p_path); /* just try to load the next one */ pindex += 1; continue; } else { p_info[pindex].p_handle = handle; } if (load_plugin_syms(p_info, handle, pindex) != SUCCESS) { usb_dprintf(DEBUG_FUNCTIONS, "Failed to load" "symbols for plugin %s\n", p_info[pindex].p_name); unload_plugin(pindex); pindex += 1; /* try the next plugin */ continue; } module_loaded = 1; pindex += 1; } if (!module_loaded) { usb_dprintf(DEBUG_FUNCTIONS, "No module could be loaded \n"); return (FAILURE); } else { /* ploaded is the highest index that had a module entry */ winfo.ploaded = pindex; return (SUCCESS); } } /* * For debugging of bus pointers */ static void dump_busses() { struct usb_bus *busp; for (busp = usb_busses; busp != NULL; busp = busp->next) { usb_dprintf(DEBUG_DETAILED, "busp is 0x%x\n", busp); } } /* * Used to unload plugin * calling libusb_fini */ static void unload_plugin(int pindex) { int sym_idx; int (*hdl_libusb_fini)(void); free(p_info[pindex].p_name); free(p_info[pindex].p_path); p_info[pindex].p_name = NULL; /* call the plugins libusb_fini here */ if (check_sym_hdl(pindex, LIBUSB_FINI) < 0) { (void) usb_dprintf(DEBUG_RECOVERABLE, "hdl_libusb_fini is NULL \n"); } else { hdl_libusb_fini = LIBUSB_FINI_CAST(p_info[pindex].sym_hdl[LIBUSB_FINI]); (*hdl_libusb_fini)(); } if (p_info[pindex].p_handle != NULL) { (void) dlclose(p_info[pindex].p_handle); p_info[pindex].exclusive_flag = 0; p_info[pindex].active_flag = 0; p_info[pindex].p_handle = NULL; } for (sym_idx = 0; sym_idx < NUM_SYMS; sym_idx ++) { p_info[pindex].sym_hdl[sym_idx] = NULL; } } /* * In trying to map the device handle to a bus * walk through the dev handle pointers added * during open and find the matching dev handle * that exists for that bus. On no match return * FAILURE on match return the bus index for this * dev handle */ static int get_index_devhdl(struct usb_dev_handle *dev) { int pindex; dev_handles_t *devh; for (pindex = 0; pindex < winfo.ploaded; pindex ++) { for (devh = p_info[pindex].dev_handles; devh != NULL; devh = devh->next) { if (dev == devh->dev) { return (pindex); } } } return (FAILURE); } /* * upon a call to open adds the device handle to the * list of handles the wrapper will maintain * so that it can map device handles to bus * holds bus_lock * This is needed simply because we want to able * to map the device handles to the plugin module */ static int usb_add_dev(struct usb_dev_handle *dev, struct usb_device *device, int pindex) { dev_handles_t *devh, *curr_devh; (void) mutex_lock(&bus_lock); /* first device handle to be added */ if (p_info[pindex].dev_handles == NULL) { devh = (dev_handles_t *)malloc(sizeof (dev_handles_t)); if (devh == NULL) { usb_dprintf(DEBUG_FUNCTIONS, "Error adding device to list \n"); (void) mutex_unlock(&bus_lock); return (FAILURE); } p_info[pindex].dev_handles = devh; devh->prev = NULL; devh->next = NULL; } else { curr_devh = p_info[pindex].dev_handles; while (curr_devh->next != NULL) { curr_devh = curr_devh->next; } /* curr_devh points to last devh handle */ curr_devh->next = (dev_handles_t *) malloc(sizeof (dev_handles_t)); if (curr_devh->next == NULL) { usb_dprintf(DEBUG_FUNCTIONS, "Error adding device to list \n"); (void) mutex_unlock(&bus_lock); return (FAILURE); } devh = curr_devh->next; devh->next = NULL; devh->prev = curr_devh; } devh->device = device; devh->dev = dev; (void) mutex_unlock(&bus_lock); return (SUCCESS); } /* * upon a call to usb_close removes the device handle from the * list of handles the wrapper will maintain * entries do not get removed on a device removal only on close * holds bus_lock */ static void usb_del_dev(struct usb_dev_handle *dev, int pindex) { dev_handles_t *d_dev; (void) mutex_lock(&bus_lock); d_dev = p_info[pindex].dev_handles; while (d_dev != NULL) { if (d_dev->dev == dev) { /* Not the last dev hdl */ if (d_dev->next != NULL) { usb_dprintf(DEBUG_DETAILED, "d_dev->next != NULL\n"); d_dev->next->prev = d_dev->prev; } /* Not the first dev hdl */ if (d_dev->prev != NULL) { usb_dprintf(DEBUG_DETAILED, "d_dev->prev != NULL\n"); d_dev->prev->next = d_dev->next; } else { /* * first dev hdl on list * if only handle then point to NULL */ p_info[pindex].dev_handles = d_dev->next; if (d_dev->next != NULL) { usb_dprintf(DEBUG_DETAILED, "d_dev->next != NULL\n"); d_dev->next->prev = NULL; } } free(d_dev); break; } d_dev = d_dev->next; } (void) mutex_unlock(&bus_lock); } /* * checks if a function has a valid symbol handle * there also needs to be a valid dlopen hdl for the plugin */ static int check_sym_hdl(int pindex, int sym_index) { if (p_info[pindex].p_handle == NULL) { return (FAILURE); } else if (p_info[pindex].sym_hdl[sym_index] == NULL) { usb_dprintf(DEBUG_FUNCTIONS, "sym_hdl[%d] is null \n", sym_index); return (FAILURE); } else { return (SUCCESS); } } /* * returns a valid symbol handle or NULL */ static void * lookup_sym_hdl(usb_dev_handle *dev, int sym_index, const char *func) { int pindex; if ((pindex = get_pindex(dev, func)) < 0) { return (NULL); } if (p_info[pindex].p_handle == NULL) { return (NULL); } else if (p_info[pindex].sym_hdl[sym_index] == NULL) { usb_dprintf(DEBUG_FUNCTIONS, "sym_hdl[%d] is null \n", sym_index); return (NULL); } else { /* this is needed to support strerror() of last call */ (void) mutex_lock(&bus_lock); winfo.last_pindex = pindex; (void) mutex_unlock(&bus_lock); return (p_info[pindex].sym_hdl[sym_index]); } } /* * Used to find the plugin whose bus links this device ptr * We will walk the bus list and then traverse the device list * of each bus to find the matching device * Once we have a match we know the bus that this device hangs off of * so we do backtrack and find a match for this bus and plugins. * A match means we have a plugin index which essentially tells us * the plugin to use */ static int get_index_devicep(struct usb_device *device) { int pindex = 0; struct usb_device *devicep; struct usb_bus *busp; busp = usb_busses; while (busp != NULL) { usb_dprintf(DEBUG_DETAILED, "get_index_: busp is 0x%x\n", busp); for (devicep = busp->devices; devicep != NULL; devicep = devicep->next) { usb_dprintf(DEBUG_DETAILED, "devicep = 0x%x\n", devicep); if (devicep == device) { for (pindex = 0; pindex < winfo.ploaded; pindex ++) { if (p_info[pindex].busp == busp) { usb_dprintf(DEBUG_DETAILED, "devicep: pindex = %d\n", pindex); return (pindex); } } } } busp = busp->next; } return (FAILURE); } static int load_plugin_syms(plugin_info_t *p_info, void *handle, int pindex) { char *symbol; char *prefix; int prefix_len = 0; int sym_len; int sym_idx; char **handle_libusb_prefix; handle_libusb_prefix = (char **)(dlsym(handle, "libusb_prefix")); /* can have a valid handle but a null prefix */ if ((handle_libusb_prefix != NULL) && (*handle_libusb_prefix != NULL)) { prefix_len = (int)strlen(*handle_libusb_prefix); p_info[pindex].prefix = *handle_libusb_prefix; } else { p_info[pindex].prefix = NULL; } prefix = p_info[pindex].prefix; if (prefix != NULL) { usb_dprintf(DEBUG_FUNCTIONS, "load_plugin_syms():prefix is %s\n", prefix); } usb_dprintf(DEBUG_DETAILED, "NUM_SYMS is %d\n", NUM_SYMS); for (sym_idx = 0; sym_idx < NUM_SYMS; sym_idx ++) { sym_len = (int)strlen(sym_names[sym_idx]) + prefix_len + 2; symbol = (char *)malloc(sym_len); if (symbol == NULL) { usb_dprintf(DEBUG_FUNCTIONS, "could not alloc space for prefix\n"); return (FAILURE); } wusb_symbol(&symbol, sym_len, prefix, sym_names[sym_idx]); p_info[pindex].sym_hdl[sym_idx] = (void *)dlsym(handle, symbol); usb_dprintf(DEBUG_DETAILED, "handle[%d]=0x%x, name = %s\n", sym_idx, p_info[pindex].sym_hdl[sym_idx], symbol); free(symbol); } return (SUCCESS); } /* * Used to form prefixed interface symbols for plugins */ static void wusb_symbol(char **init_str, int len, char *prefix, char *func_name) { if (prefix != NULL) { (void) snprintf(*init_str, len, "%s", prefix); } else { (void) memset(*init_str, 0, len); } (void) strncat(*init_str, func_name, len); } /* * Given a device handle map it to a bus * if active_index = -1 it means more than * a single plugin bus is active so we need * walk the dev handles lists. Else active * index simply points to the index of the * single bus that is active and so we use that */ int get_pindex(struct usb_dev_handle *dev, const char *func) { int pindex; if (dev == NULL) { usb_dprintf(DEBUG_ERRORS, "%s: Invalid device handle \n", func); return (-EINVAL); } if (winfo.active_index == -1) { pindex = get_index_devhdl(dev); if (pindex < 0) { usb_dprintf(DEBUG_FUNCTIONS, "%s: device handle not found\n", func); return (-ENODEV); } } else { pindex = winfo.active_index; } return (pindex); } /* * Entry points for standard libusb interfaces * Given a device handle, device pointer * map the device to the bus and call into * the loaded module. For calls without * arguments - cycle through all modules * that are active (per some policies implemented by plugin) * and make calls into the implementation specific * functions. */ /* * usb_init() entry point: * * This will call the libusb-init implementation of each plugin * loaded and will determine which busses will be supported. * For the plugin busses that are supported usb_init * of those plugins will be called. This routine will also * invalidate plugin entries that are not supported */ void usb_init(void) { int pindex; boolean_t exclusive = 0; int (*hdl_libusb_init)(void); void (*hdl_usb_init)(void); char **hdl_libusb_version; int ret; char *func = "usb_init"; char *version; char version_store[MAX_VERSION_LEN + 1]; char wr_version_store[sizeof (LIBUSB_WRAPPER_VERSION)]; char *token; char *wr_token; char *debug_str; int active_count = 0; /* * If usb_init() already called then do not reinit */ if (winfo.head_busp != NULL) { return; } (void) mutex_lock(&bus_lock); if ((debug_str = getenv("SUN_LIBUSB_DEBUG")) != NULL) { libusb_debug = atoi(debug_str); } usb_dprintf(DEBUG_FUNCTIONS, "the wrapper's debug level is %d\n", libusb_debug); ret = load_plugins(); if (ret < 0) { usb_dprintf(DEBUG_ERRORS, "%s: could not load plugin modules\n", func); (void) mutex_unlock(&bus_lock); return; } winfo.exclusive_index = -1; winfo.active_index = -1; winfo.head_busp = NULL; for (pindex = 0; pindex < winfo.ploaded; pindex ++) { if (p_info[pindex].p_handle == NULL) { continue; } /* condition for libusb_init not implemented */ if (check_sym_hdl(pindex, LIBUSB_INIT) < 0) { (void) usb_dprintf(DEBUG_RECOVERABLE, "hdl_libusb_init is NULL \n"); p_info[pindex].exclusive_flag = 0; p_info[pindex].active_flag = 1; active_count += 1; continue; } hdl_libusb_init = LIBUSB_INIT_CAST(p_info[pindex].sym_hdl[LIBUSB_INIT]); ret = (*hdl_libusb_init)(); (void) usb_dprintf(DEBUG_DETAILED, "%s: libusb_init() returned %d\n", p_info[pindex].p_name, ret); switch (ret) { case SUCCESS: p_info[pindex].exclusive_flag = 0; p_info[pindex].active_flag = 1; active_count += 1; winfo.active_index = pindex; break; case FAILURE: usb_dprintf(DEBUG_FUNCTIONS, "unloading plugin %s\n", p_info[pindex].p_name); unload_plugin(pindex); continue; case PLUGIN_EXCLUSIVE: /* first plugin to set exclusive */ if (exclusive != 1) { p_info[pindex].exclusive_flag = 1; exclusive = 1; winfo.exclusive_index = pindex; } p_info[pindex].active_flag = 1; active_count += 1; break; default: usb_dprintf(DEBUG_RECOVERABLE, "unsupported return" "value for libusb_init\n"); } /* * If there is no version defined we accept it * but if there is a version mismatch we will skip */ if (check_sym_hdl(pindex, LIBUSB_VERSION) < 0) { usb_dprintf(DEBUG_RECOVERABLE, "No Version number for plugin found \n"); } else { hdl_libusb_version = (char **) (p_info[pindex].sym_hdl[LIBUSB_VERSION]); if ((version = *hdl_libusb_version) != NULL) { if (strlen(version) > MAX_VERSION_LEN) { usb_dprintf(DEBUG_RECOVERABLE, "version string exceeds max" "characters, truncating\n"); } (void) strncpy(version_store, version, MAX_VERSION_LEN); token = strtok(version_store, "."); (void) strncpy(wr_version_store, LIBUSB_WRAPPER_VERSION, sizeof (wr_version_store)); wr_token = strtok(wr_version_store, "."); /* * Initial wrapper version is 1.1 * if plugin major_rev is != wrapper major_rev * then do not load. If the version is not * supported set active to FALSE */ usb_dprintf(DEBUG_DETAILED, "plugin rev is %d\n", atoi(token)); usb_dprintf(DEBUG_DETAILED, "wrapper rev is %d\n", atoi(wr_token)); if (atoi(token) != atoi(wr_token)) { usb_dprintf(DEBUG_ERRORS, "plugin version %s not supported\n", version); unload_plugin(pindex); } } winfo.last_pindex = pindex; } if (active_count != 1) { winfo.active_index = -1; } } (void) usb_dprintf(DEBUG_DETAILED, "winfo.ploaded is %d\n", winfo.ploaded); for (pindex = 0; pindex < winfo.ploaded; pindex ++) { if (p_info[pindex].p_handle == NULL) { continue; } if (exclusive && p_info[pindex].exclusive_flag == 1) { winfo.exclusive_index = pindex; winfo.active_index = pindex; } if (exclusive && p_info[pindex].exclusive_flag != 1) { unload_plugin(pindex); } if (p_info[pindex].active_flag) { if (check_sym_hdl(pindex, USB_INIT) < 0) { usb_dprintf(DEBUG_ERRORS, "could not get symbol for %s\n", func); } hdl_usb_init = USB_INIT_CAST(p_info[pindex]. sym_hdl[USB_INIT]); (*hdl_usb_init)(); } } (void) mutex_unlock(&bus_lock); usb_dprintf(DEBUG_DETAILED, "completed usb init()\n"); } void usb_set_debug(int level) { int pindex; char *func = "usb_set_debug"; void (*hdl)(int); char *debug_str; /* env debug variables override completely what the app sets */ if ((debug_str = getenv("SUN_LIBUSB_DEBUG")) != NULL) { libusb_debug = atoi(debug_str); } else { if (level < 0) return; libusb_debug = level; } usb_dprintf(DEBUG_FUNCTIONS, "libusb debug level is %d\n", libusb_debug); for (pindex = 0; pindex < winfo.ploaded; pindex ++) { if (check_sym_hdl(pindex, USB_SET_DEBUG) < 0) { usb_dprintf(DEBUG_ERRORS, "could not find symbol for %s\n", func); continue; } hdl = USB_SET_DEBUG_CAST (p_info[pindex].sym_hdl[USB_SET_DEBUG]); (*hdl)(libusb_debug); } } /* * This will manage the usb_busses pointer for each plugin * The wrapper library will expose its own usb_busses pointer * This will be built by loading the plugin usb_busses pointer * and linking all the bussses. The wrapper libraries usb_bus * pointer will in sync every time usb_find_busses is called. * Applications are shielded from the underlying plugin usb_busses * pointers. * ret_bus is supposed to be the number of busses changed * since last call */ int usb_find_busses(void) { int pindex; char *func = "usb_find_busses"; int (*hdl_usb_find_busses)(void); struct usb_bus **hdl_usb_busses; int ret_find_busses[MAX_PLUGINS]; struct usb_bus *tmp_usb_busses = NULL; struct usb_bus *mv_usb_busses = NULL; struct usb_bus *last_usb_busses = NULL; int ret_bus = 0; int found_bus = 0; (void) mutex_lock(&bus_lock); for (pindex = 0; pindex < winfo.ploaded; pindex ++) { if (check_sym_hdl(pindex, USB_FIND_BUSSES) < 0) { usb_dprintf(DEBUG_ERRORS, "could not get symbol for %s\n", func); continue; } hdl_usb_find_busses = USB_FIND_BUSSES_CAST (p_info[pindex].sym_hdl[USB_FIND_BUSSES]); /* calling the find_busses whose symbols can be found */ ret_find_busses[pindex] = (*hdl_usb_find_busses)(); ret_bus += ret_find_busses[pindex]; /* * updated usb_busses pointer for the plugins * this could be NULL */ if (check_sym_hdl(pindex, USB_BUSSES) < 0) { usb_dprintf(DEBUG_ERRORS, "could not get symbol for %s\n", usb_busses); p_info[pindex].busp = NULL; continue; } hdl_usb_busses = USB_BUSSES_CAST(p_info[pindex].sym_hdl[USB_BUSSES]); p_info[pindex].busp = *hdl_usb_busses; usb_dprintf(DEBUG_DETAILED, "usb_bus ptr = 0x%x\n", p_info[pindex].busp); found_bus = 1; winfo.last_pindex = pindex; } if (!found_bus) { usb_dprintf(DEBUG_ERRORS, "could not find a usb_bus pointer \n"); (void) mutex_unlock(&bus_lock); return (FAILURE); } /* Init tmp_usb_busses */ for (pindex = 0; pindex < winfo.ploaded; pindex ++) { if (tmp_usb_busses == NULL) { if (p_info[pindex].busp == NULL) { continue; } tmp_usb_busses = p_info[pindex].busp; winfo.head_busp = tmp_usb_busses; mv_usb_busses = tmp_usb_busses; while (mv_usb_busses->next != NULL) { mv_usb_busses = mv_usb_busses->next; } last_usb_busses = mv_usb_busses; continue; } if (p_info[pindex].busp == NULL) { continue; } last_usb_busses->next = p_info[pindex].busp; mv_usb_busses = p_info[pindex].busp; while (mv_usb_busses->next != NULL) { mv_usb_busses = mv_usb_busses->next; } last_usb_busses = mv_usb_busses; } usb_busses = winfo.head_busp; dump_busses(); (void) mutex_unlock(&bus_lock); return (ret_bus); } struct usb_dev_handle * usb_open(struct usb_device *device) { int pindex; struct usb_dev_handle *dev; struct usb_dev_handle *(*hdl)(struct usb_device *); char *func = "usb_open"; usb_dprintf(DEBUG_DETAILED, "usb_open: device ptr is 0x%x\n", device); if (winfo.active_index == -1) { usb_dprintf(DEBUG_DETAILED, "usb_open: active_index = -1 \n"); pindex = get_index_devicep(device); /* could not find this device pointer */ if (pindex < 0) { usb_dprintf(DEBUG_ERRORS, "%s: could not map device pointer to bus\n", func); return (NULL); } } else { usb_dprintf(DEBUG_FUNCTIONS, "usb_open: pindex = %d\n", winfo.active_index); pindex = winfo.active_index; } if (check_sym_hdl(pindex, USB_OPEN) < 0) { usb_dprintf(DEBUG_ERRORS, "%s: Symbol not found \n", func); return (NULL); } hdl = USB_OPEN_CAST(p_info[pindex].sym_hdl[USB_OPEN]); dev = (*hdl)(device); if (usb_add_dev(dev, device, pindex) == SUCCESS) { return (dev); } else { usb_dprintf(DEBUG_ERRORS, "%s:No Memory to add device\n", func); return (NULL); } } int usb_close(usb_dev_handle *dev) { int pindex; int ret; char *func = "usb_close"; int (*hdl)(usb_dev_handle *); pindex = get_pindex(dev, func); if (pindex < 0) { return (pindex); } if (check_sym_hdl(pindex, USB_CLOSE) < 0) { usb_dprintf(DEBUG_ERRORS, "could not find symbol for %s\n", func); return (FAILURE); } hdl = USB_CLOSE_CAST(p_info[pindex].sym_hdl[USB_CLOSE]); usb_del_dev(dev, pindex); ret = (*hdl)(dev); return (ret); } int usb_get_string(usb_dev_handle *dev, int index, int langid, char *buf, size_t buflen) { char *func = "usb_get_string"; int (*hdl)(usb_dev_handle *, int, int, char *, size_t); if ((hdl = USB_GET_STRING_CAST (lookup_sym_hdl(dev, USB_GET_STRING, func))) == NULL) { usb_dprintf(DEBUG_ERRORS, "could not find symbol for %s\n", func); return (-ENOTSUP); } return ((*hdl)(dev, index, langid, buf, buflen)); } int usb_get_string_simple(usb_dev_handle *dev, int index, char *buf, size_t buflen) { char *func = "usb_get_string_simple"; int (*hdl)(usb_dev_handle *, int, char *, size_t); if ((hdl = USB_GET_STRING_SIMPLE_CAST (lookup_sym_hdl(dev, USB_GET_STRING_SIMPLE, func))) == NULL) { usb_dprintf(DEBUG_ERRORS, "could not find symbol for %s\n", func); return (-ENOTSUP); } return ((*hdl)(dev, index, buf, buflen)); } int usb_get_descriptor_by_endpoint(usb_dev_handle *dev, int ep, unsigned char type, unsigned char index, void *buf, int size) { char *func = "usb_get_descriptor_by_endpoint"; int (*hdl)(usb_dev_handle *, int, unsigned char, unsigned char, void *, int); if ((hdl = USB_GET_DESCRIPTOR_BY_ENDPOINT_CAST (lookup_sym_hdl(dev, USB_GET_DESCRIPTOR_BY_ENDPOINT, func))) == NULL) { usb_dprintf(DEBUG_ERRORS, "could not find symbol for %s\n", func); return (-ENOTSUP); } return ((*hdl)(dev, ep, type, index, buf, size)); } int usb_get_descriptor(usb_dev_handle *dev, unsigned char type, unsigned char index, void *buf, int size) { char *func = "usb_get_descriptor"; int (*hdl)(usb_dev_handle *, unsigned char, unsigned char, void *, int); if ((hdl = USB_GET_DESCRIPTOR_CAST (lookup_sym_hdl(dev, USB_GET_DESCRIPTOR, func))) == NULL) { usb_dprintf(DEBUG_ERRORS, "could not find symbol for %s\n", func); return (-ENOTSUP); } return ((*hdl)(dev, type, index, buf, size)); } int usb_bulk_write(usb_dev_handle *dev, int ep, char *bytes, int size, int timeout) { char *func = "usb_bulk_write"; int (*hdl)(usb_dev_handle *, int, char *, int, int); if ((hdl = USB_BULK_WRITE_CAST (lookup_sym_hdl(dev, USB_BULK_WRITE, func))) == NULL) { usb_dprintf(DEBUG_ERRORS, "could not find symbol for %s\n", func); return (-ENOTSUP); } return (*hdl)(dev, ep, bytes, size, timeout); } int usb_bulk_read(usb_dev_handle *dev, int ep, char *bytes, int size, int timeout) { char *func = "usb_bulk_read"; int (*hdl)(usb_dev_handle *, int, char *, int, int); if ((hdl = USB_BULK_READ_CAST (lookup_sym_hdl(dev, USB_BULK_READ, func))) == NULL) { usb_dprintf(DEBUG_ERRORS, "could not find symbol for %s\n", func); return (-ENOTSUP); } return ((*hdl)(dev, ep, bytes, size, timeout)); } int usb_interrupt_read(usb_dev_handle *dev, int ep, char *bytes, int size, int timeout) { char *func = "usb_interrupt_read"; int (*hdl)(usb_dev_handle *, int, char *, int, int); if ((hdl = USB_INTERRUPT_READ_CAST (lookup_sym_hdl(dev, USB_INTERRUPT_READ, func))) == NULL) { usb_dprintf(DEBUG_ERRORS, "could not find symbol for %s\n", func); return (-ENOTSUP); } return ((*hdl)(dev, ep, bytes, size, timeout)); } int usb_interrupt_write(usb_dev_handle *dev, int ep, char *bytes, int size, int timeout) { char *func = "usb_interrupt_write"; int (*hdl)(usb_dev_handle *, int, char *, int, int); if ((hdl = USB_INTERRUPT_WRITE_CAST (lookup_sym_hdl(dev, USB_INTERRUPT_WRITE, func))) == NULL) { usb_dprintf(DEBUG_ERRORS, "could not find symbol for %s\n", func); return (-ENOTSUP); } return ((*hdl)(dev, ep, bytes, size, timeout)); } int usb_control_msg(usb_dev_handle *dev, int requesttype, int request, int value, int index, char *bytes, int size, int timeout) { char *func = "usb_control_msg"; int (*hdl)(usb_dev_handle *, int, int, int, int, char *, int, int); if ((hdl = USB_CONTROL_MSG_CAST (lookup_sym_hdl(dev, USB_CONTROL_MSG, func))) == NULL) { usb_dprintf(DEBUG_ERRORS, "could not find symbol for %s\n", func); return (-ENOTSUP); } return ((*hdl)(dev, requesttype, request, value, index, bytes, size, timeout)); } int usb_set_configuration(usb_dev_handle *dev, int configuration) { char *func = "usb_set_configuration"; int (*hdl)(usb_dev_handle *, int); if ((hdl = USB_SET_CONFIGURATION_CAST (lookup_sym_hdl(dev, USB_SET_CONFIGURATION, func))) == NULL) { usb_dprintf(DEBUG_ERRORS, "could not find symbol for %s\n", func); return (-ENOTSUP); } return ((*hdl)(dev, configuration)); } int usb_claim_interface(usb_dev_handle *dev, int interface) { char *func = "usb_claim_interface"; int (*hdl)(usb_dev_handle *, int); if ((hdl = USB_CLAIM_INTERFACE_CAST (lookup_sym_hdl(dev, USB_CLAIM_INTERFACE, func))) == NULL) { usb_dprintf(DEBUG_ERRORS, "could not find symbol for %s\n", func); return (-ENOTSUP); } return ((*hdl)(dev, interface)); } int usb_release_interface(usb_dev_handle *dev, int interface) { char *func = "usb_release_interface"; int (*hdl)(usb_dev_handle *, int); if ((hdl = USB_RELEASE_INTERFACE_CAST (lookup_sym_hdl(dev, USB_RELEASE_INTERFACE, func))) == NULL) { usb_dprintf(DEBUG_ERRORS, "could not find symbol for %s\n", func); return (-ENOTSUP); } return ((*hdl)(dev, interface)); } int usb_set_altinterface(usb_dev_handle *dev, int alternate) { char *func = "usb_set_altinterface"; int (*hdl)(usb_dev_handle *, int); if ((hdl = USB_SET_ALTINTERFACE_CAST (lookup_sym_hdl(dev, USB_SET_ALTINTERFACE, func))) == NULL) { usb_dprintf(DEBUG_ERRORS, "could not find symbol for %s\n", func); return (-ENOTSUP); } return ((*hdl)(dev, alternate)); } int usb_resetep(usb_dev_handle *dev, unsigned int ep) { char *func = "usb_resetep"; int (*hdl)(usb_dev_handle *, unsigned int); if ((hdl = USB_RESETEP_CAST (lookup_sym_hdl(dev, USB_RESETEP, func))) == NULL) { usb_dprintf(DEBUG_ERRORS, "could not find symbol for %s\n", func); return (-ENOTSUP); } return ((*hdl)(dev, ep)); } int usb_clear_halt(usb_dev_handle *dev, unsigned int ep) { char *func = "usb_clear_halt"; int (*hdl)(usb_dev_handle *, unsigned int); if ((hdl = USB_CLEAR_HALT_CAST (lookup_sym_hdl(dev, USB_CLEAR_HALT, func))) == NULL) { usb_dprintf(DEBUG_ERRORS, "could not find symbol for %s\n", func); return (-ENOTSUP); } return ((*hdl)(dev, ep)); } int usb_reset(usb_dev_handle *dev) { char *func = "usb_reset"; int (*hdl)(usb_dev_handle *); if ((hdl = USB_RESET_CAST (lookup_sym_hdl(dev, USB_RESET, func))) == NULL) { usb_dprintf(DEBUG_ERRORS, "could not find symbol for %s\n", func); return (-ENOTSUP); } return ((*hdl)(dev)); } int usb_find_devices(void) { int pindex; int (*hdl_usb_find_devices)(void); int ret_val = 0; int err_store = 0; /* * devices can go away here. * devices can get added also. * the dev handles list does not update it only updates * for usb_close and usb_open */ for (pindex = 0; pindex < winfo.ploaded; pindex ++) { if (check_sym_hdl(pindex, USB_FIND_DEVICES) < 0) { continue; } hdl_usb_find_devices = USB_FIND_DEVICES_CAST (p_info[pindex].sym_hdl[USB_FIND_DEVICES]); ret_val = (*hdl_usb_find_devices)(); if (ret_val < 0) { err_store = ret_val; } else { ret_val += ret_val; } } if (!err_store) { return (ret_val); } else { /* return any error for multiple busses */ return (err_store); } } struct usb_device * usb_device(usb_dev_handle *dev) { char *func = "usb_device"; struct usb_device *((*hdl)(usb_dev_handle *)); if ((hdl = USB_DEVICE_CAST (lookup_sym_hdl(dev, USB_DEVICE, func))) == NULL) { usb_dprintf(DEBUG_ERRORS, "could not find symbol for %s\n", func); return (NULL); } return ((*hdl)(dev)); } /* * This returns the wrapper's usb_busses pointer not the plugins */ struct usb_bus * usb_get_busses(void) { return (usb_busses); } /* * Makes sense to only return a single * str error - so using the strerror of the * last plugin that was used */ char * usb_strerror(void) { int pindex; char *func = "usb_strerror"; char *(*hdl_usb_strerror)(void); /* * usb_strerror is only of interest for the last * call to the plugin. So call it for the last * plugin used */ for (pindex = 0; pindex < winfo.ploaded; pindex ++) { if (check_sym_hdl(pindex, USB_STRERROR) < 0) { usb_dprintf(DEBUG_ERRORS, "could not find symbol for %s\n", func); continue; } if (pindex == winfo.last_pindex) { hdl_usb_strerror = USB_STRERROR_CAST (p_info[pindex].sym_hdl[USB_STRERROR]); return ((*hdl_usb_strerror)()); } } return (NULL); } static void usb_dprintf(int level, char *format, ...) { va_list ap; char buf[512]; va_start(ap, format); (void) vsnprintf(buf, sizeof (buf), format, ap); if (libusb_debug >= level) { (void) fprintf(stderr, buf); } va_end(ap); }