/*
* 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
* 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
*/
/*
*/
#include <stdlib.h>
#include <stdio.h>
#include <dirent.h>
#include <errno.h>
#include <strings.h>
#include <dlfcn.h>
#include <thread.h>
#include <synch.h>
#include <stdarg.h>
#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 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 int is_libusb_plugin(char *);
#ifdef _LP64
#else /* _LP64 */
#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
{
int pindex = 0;
int plugin_found = 0;
char *alt_dir;
char *suffix;
}
(PATH_MAX - sizeof (PLUGIN_PREFIX))) {
"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/
*/
return (FAILURE);
}
while (dirp) {
errno = 0;
/* skip . and .. entries */
continue;
}
continue;
}
/*
* Ignore files that are not *.so.X
* supported
*/
continue;
"did not load %s:%s\n",
continue;
}
/*
* using PATH_MAX len here
*/
"pathname > PATH_MAX %s",
/* go on to try read next module */
continue;
}
/*
* 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
*/
SUCCESS) {
continue;
}
plugin_found = 1;
if (++pindex == MAX_PLUGINS) {
"Max plugins read %d\n", pindex);
break;
}
} else {
if (errno == 0) {
if (!plugin_found) {
"No plugin found \n");
return (FAILURE);
} else {
/* end of dir stream */
return (SUCCESS);
}
} else {
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
{
void *hdl;
void *hdl_sym1;
void *hdl_sym2;
"%s could not be loaded \n", modname);
return (FAILURE);
}
"%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
* Also loads all the symbols also and store the handles
* Lock: called with bus_lock held
* called from usb_init
*/
static int
{
int pindex = 0;
void *handle;
int module_loaded = 0;
if (read_plugin_entries() != SUCCESS) {
"Failed to load libusb plugins \n");
return (FAILURE);
}
/*
* Will load at most MAX_PLUGINS
*/
while (pindex < MAX_PLUGINS) {
/* reached the end of modules read into the array */
break;
}
/* just try to load the next one */
pindex += 1;
continue;
} else {
}
"symbols for plugin %s\n",
pindex += 1;
/* try the next plugin */
continue;
}
module_loaded = 1;
pindex += 1;
}
if (!module_loaded) {
return (FAILURE);
} else {
/* ploaded is the highest index that had a module entry */
return (SUCCESS);
}
}
/*
* For debugging of bus pointers
*/
static void
{
}
}
/*
* Used to unload plugin
* calling libusb_fini
*/
static void
{
int sym_idx;
int (*hdl_libusb_fini)(void);
/* call the plugins libusb_fini here */
(void) usb_dprintf(DEBUG_RECOVERABLE,
"hdl_libusb_fini is NULL \n");
} else {
(*hdl_libusb_fini)();
}
}
}
}
/*
* 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
{
int pindex;
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
int pindex)
{
(void) mutex_lock(&bus_lock);
/* first device handle to be added */
"Error adding device to list \n");
(void) mutex_unlock(&bus_lock);
return (FAILURE);
}
} else {
}
/* curr_devh points to last devh handle */
malloc(sizeof (dev_handles_t));
"Error adding device to list \n");
(void) mutex_unlock(&bus_lock);
return (FAILURE);
}
}
(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
{
(void) mutex_lock(&bus_lock);
/* Not the last dev hdl */
"d_dev->next != NULL\n");
}
/* Not the first dev hdl */
"d_dev->prev != NULL\n");
} else {
/*
* first dev hdl on list
* if only handle then point to NULL
*/
"d_dev->next != NULL\n");
}
}
break;
}
}
(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
{
return (FAILURE);
"sym_hdl[%d] is null \n", sym_index);
return (FAILURE);
} else {
return (SUCCESS);
}
}
/*
* returns a valid symbol handle or NULL
*/
static void *
{
int pindex;
return (NULL);
}
return (NULL);
"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);
(void) mutex_unlock(&bus_lock);
}
}
/*
* 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
{
int pindex = 0;
busp = usb_busses;
"get_index_: busp is 0x%x\n", busp);
"devicep = 0x%x\n", devicep);
"devicep: pindex = %d\n",
pindex);
return (pindex);
}
}
}
}
}
return (FAILURE);
}
static int
{
char *symbol;
char *prefix;
int prefix_len = 0;
int sym_len;
int sym_idx;
char **handle_libusb_prefix;
/* can have a valid handle but a null prefix */
if ((handle_libusb_prefix != NULL) &&
(*handle_libusb_prefix != NULL)) {
} else {
}
"load_plugin_syms():prefix is %s\n", prefix);
}
"could not alloc space for prefix\n");
return (FAILURE);
}
}
return (SUCCESS);
}
/*
* Used to form prefixed interface symbols for plugins
*/
static void
{
} else {
}
}
/*
* 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
{
int pindex;
"%s: Invalid device handle \n", func);
return (-EINVAL);
}
if (pindex < 0) {
"%s: device handle not found\n", func);
return (-ENODEV);
}
} else {
}
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;
int (*hdl_libusb_init)(void);
void (*hdl_usb_init)(void);
char **hdl_libusb_version;
int ret;
char *version;
char *token;
char *wr_token;
char *debug_str;
int active_count = 0;
/*
* If usb_init() already called then do not reinit
*/
return;
}
(void) mutex_lock(&bus_lock);
}
ret = load_plugins();
if (ret < 0) {
"%s: could not load plugin modules\n", func);
(void) mutex_unlock(&bus_lock);
return;
}
continue;
}
/* condition for libusb_init not implemented */
(void) usb_dprintf(DEBUG_RECOVERABLE,
"hdl_libusb_init is NULL \n");
active_count += 1;
continue;
}
ret = (*hdl_libusb_init)();
(void) usb_dprintf(DEBUG_DETAILED,
"%s: libusb_init() returned %d\n",
switch (ret) {
case SUCCESS:
active_count += 1;
break;
case FAILURE:
continue;
case PLUGIN_EXCLUSIVE:
/* first plugin to set exclusive */
if (exclusive != 1) {
exclusive = 1;
}
active_count += 1;
break;
default:
"value for libusb_init\n");
}
/*
* If there is no version defined we accept it
* but if there is a version mismatch we will skip
*/
"No Version number for plugin found \n");
} else {
hdl_libusb_version = (char **)
"version string exceeds max"
"characters, truncating\n");
}
(void) strncpy(wr_version_store,
sizeof (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
*/
"plugin version %s not supported\n",
version);
}
}
}
if (active_count != 1) {
}
}
(void) usb_dprintf(DEBUG_DETAILED,
continue;
}
}
}
"could not get symbol for %s\n", func);
}
(*hdl_usb_init)();
}
}
(void) mutex_unlock(&bus_lock);
}
void
{
int pindex;
void (*hdl)(int);
char *debug_str;
/* env debug variables override completely what the app sets */
} else {
if (level < 0)
return;
}
"could not find symbol for %s\n", func);
continue;
}
(*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;
int (*hdl_usb_find_busses)(void);
int ret_bus = 0;
int found_bus = 0;
(void) mutex_lock(&bus_lock);
"could not get symbol for %s\n", func);
continue;
}
/* calling the find_busses whose symbols can be found */
/*
* updated usb_busses pointer for the plugins
* this could be NULL
*/
"could not get symbol for %s\n", usb_busses);
continue;
}
found_bus = 1;
}
if (!found_bus) {
"could not find a usb_bus pointer \n");
(void) mutex_unlock(&bus_lock);
return (FAILURE);
}
/* Init tmp_usb_busses */
if (tmp_usb_busses == NULL) {
continue;
}
}
continue;
}
continue;
}
}
}
dump_busses();
(void) mutex_unlock(&bus_lock);
return (ret_bus);
}
struct usb_dev_handle *
{
int pindex;
/* could not find this device pointer */
if (pindex < 0) {
"%s: could not map device pointer to bus\n", func);
return (NULL);
}
} else {
}
return (NULL);
}
return (dev);
} else {
return (NULL);
}
}
int
{
int pindex;
int ret;
int (*hdl)(usb_dev_handle *);
if (pindex < 0) {
return (pindex);
}
"could not find symbol for %s\n", func);
return (FAILURE);
}
return (ret);
}
int
{
if ((hdl = USB_GET_STRING_CAST
"could not find symbol for %s\n", func);
return (-ENOTSUP);
}
}
int
{
if ((hdl = USB_GET_STRING_SIMPLE_CAST
"could not find symbol for %s\n", func);
return (-ENOTSUP);
}
}
int
{
int (*hdl)(usb_dev_handle *, int, unsigned char,
unsigned char, void *, int);
== NULL) {
"could not find symbol for %s\n", func);
return (-ENOTSUP);
}
}
int
{
int (*hdl)(usb_dev_handle *, unsigned char,
unsigned char, void *, int);
if ((hdl = USB_GET_DESCRIPTOR_CAST
"could not find symbol for %s\n", func);
return (-ENOTSUP);
}
}
int
{
int (*hdl)(usb_dev_handle *, int, char *, int, int);
if ((hdl = USB_BULK_WRITE_CAST
"could not find symbol for %s\n", func);
return (-ENOTSUP);
}
}
int
{
int (*hdl)(usb_dev_handle *, int, char *, int, int);
if ((hdl = USB_BULK_READ_CAST
"could not find symbol for %s\n", func);
return (-ENOTSUP);
}
}
int
{
int (*hdl)(usb_dev_handle *, int, char *, int, int);
if ((hdl = USB_INTERRUPT_READ_CAST
"could not find symbol for %s\n", func);
return (-ENOTSUP);
}
}
int
{
int (*hdl)(usb_dev_handle *, int, char *, int, int);
if ((hdl = USB_INTERRUPT_WRITE_CAST
"could not find symbol for %s\n", func);
return (-ENOTSUP);
}
}
int
{
int (*hdl)(usb_dev_handle *, int, int, int, int, char *, int, int);
if ((hdl = USB_CONTROL_MSG_CAST
"could not find symbol for %s\n", func);
return (-ENOTSUP);
}
}
int
{
int (*hdl)(usb_dev_handle *, int);
if ((hdl = USB_SET_CONFIGURATION_CAST
"could not find symbol for %s\n", func);
return (-ENOTSUP);
}
}
int
{
int (*hdl)(usb_dev_handle *, int);
if ((hdl = USB_CLAIM_INTERFACE_CAST
"could not find symbol for %s\n", func);
return (-ENOTSUP);
}
}
int
{
int (*hdl)(usb_dev_handle *, int);
if ((hdl = USB_RELEASE_INTERFACE_CAST
"could not find symbol for %s\n", func);
return (-ENOTSUP);
}
}
int
{
int (*hdl)(usb_dev_handle *, int);
if ((hdl = USB_SET_ALTINTERFACE_CAST
"could not find symbol for %s\n", func);
return (-ENOTSUP);
}
}
int
{
int (*hdl)(usb_dev_handle *, unsigned int);
if ((hdl = USB_RESETEP_CAST
"could not find symbol for %s\n", func);
return (-ENOTSUP);
}
}
int
{
int (*hdl)(usb_dev_handle *, unsigned int);
if ((hdl = USB_CLEAR_HALT_CAST
"could not find symbol for %s\n", func);
return (-ENOTSUP);
}
}
int
{
int (*hdl)(usb_dev_handle *);
if ((hdl = USB_RESET_CAST
"could not find symbol for %s\n", func);
return (-ENOTSUP);
}
}
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
*/
continue;
}
ret_val = (*hdl_usb_find_devices)();
if (ret_val < 0) {
} else {
}
}
if (!err_store) {
return (ret_val);
} else {
/* return any error for multiple busses */
return (err_store);
}
}
struct usb_device *
{
if ((hdl = USB_DEVICE_CAST
"could not find symbol for %s\n", func);
return (NULL);
}
}
/*
* 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 *(*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
*/
"could not find symbol for %s\n", func);
continue;
}
return ((*hdl_usb_strerror)());
}
}
return (NULL);
}
static void
{
if (libusb_debug >= level) {
}
}