/********************************************************************\
Name: musbstd.c
Created by: Konstantin Olchanski, Stefan Ritt
Contents: Midas USB access
$Id$
\********************************************************************/
#include <stdio.h>
#include <assert.h>
#include "musbstd.h"
#ifdef _MSC_VER // Windows includes
#include <windows.h>
#include <conio.h>
#include <winioctl.h>
#include <setupapi.h>
#include <initguid.h> /* Required for GUID definition */
// link with SetupAPI.Lib.
#pragma comment (lib, "setupapi.lib")
// disable "deprecated" warning
#pragma warning( disable: 4996)
// {CBEB3FB1-AE9F-471c-9016-9B6AC6DCD323}
DEFINE_GUID(GUID_CLASS_MSCB_BULK, 0xcbeb3fb1, 0xae9f, 0x471c, 0x90, 0x16, 0x9b, 0x6a, 0xc6, 0xdc, 0xd3, 0x23);
#elif defined(OS_DARWIN)
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <fcntl.h>
#include <assert.h>
#include <mach/mach.h>
#include <IOKit/IOKitLib.h>
#include <IOKit/IOCFPlugIn.h>
#include <IOKit/usb/IOUSBLib.h>
#elif defined(OS_LINUX) // Linux includes
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#endif
#ifdef HAVE_LIBUSB
#include <errno.h>
#include "usb.h"
#endif
#ifdef HAVE_LIBUSB10
#include <errno.h>
#include <libusb-1.0/libusb.h>
#endif
#if !defined(HAVE_LIBUSB) && !defined(HAVE_LIBUSB10)
#ifdef OS_DARWIN
IOReturn darwin_configure_device(MUSB_INTERFACE* musb)
{
IOReturn status;
io_iterator_t iter;
io_service_t service;
IOCFPlugInInterface **plugin;
SInt32 score;
IOUSBInterfaceInterface **uinterface;
UInt8 numend;
IOUSBDeviceInterface **device = (IOUSBDeviceInterface **)musb->device;
status = (*device)->SetConfiguration(device, musb->usb_configuration);
assert(status
== kIOReturnSuccess
);
IOUSBFindInterfaceRequest request;
request.bInterfaceClass = kIOUSBFindInterfaceDontCare;
request.bInterfaceSubClass = kIOUSBFindInterfaceDontCare;
request.bInterfaceProtocol = kIOUSBFindInterfaceDontCare;
request.bAlternateSetting = kIOUSBFindInterfaceDontCare;
status = (*device)->CreateInterfaceIterator(device, &request, &iter);
assert(status
== kIOReturnSuccess
);
while ((service = IOIteratorNext(iter))) {
int i;
status =
IOCreatePlugInInterfaceForService(service, kIOUSBInterfaceUserClientTypeID,
kIOCFPlugInInterfaceID, &plugin, &score);
assert(status
== kIOReturnSuccess
);
status =
(*plugin)->QueryInterface(plugin, CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID),
(void *) &uinterface);
assert(status
== kIOReturnSuccess
);
status = (*uinterface)->USBInterfaceOpen(uinterface);
fprintf(stderr
, "musb_open: USBInterfaceOpen status 0x%x\n", status
);
assert(status
== kIOReturnSuccess
);
status = (*uinterface)->GetNumEndpoints(uinterface, &numend);
assert(status
== kIOReturnSuccess
);
fprintf(stderr
, "musb_open: endpoints: %d\n", numend
);
for (i=1; i<=numend; i++) {
status = (*uinterface)->GetPipeStatus(uinterface, i);
fprintf(stderr
, "musb_open: pipe %d status: 0x%x\n", i
, status
);
#if 0
status = (*uinterface)->ClearPipeStall(uinterface, i);
fprintf(stderr
, "musb_open: pipe %d ClearPipeStall() status: 0x%x\n", i
, status
);
status = (*uinterface)->ResetPipe(uinterface, i);
fprintf(stderr
, "musb_open: pipe %d ResetPipe() status: 0x%x\n", i
, status
);
status = (*uinterface)->AbortPipe(uinterface, i);
fprintf(stderr
, "musb_open: pipe %d AbortPipe() status: 0x%x\n", i
, status
);
#endif
}
musb->interface = uinterface;
return kIOReturnSuccess;
}
assert(!"Should never be reached!");
return -1;
}
#endif
#endif
int musb_open(MUSB_INTERFACE **musb_interface, int vendor, int product, int instance, int configuration, int usbinterface)
{
#if defined(HAVE_LIBUSB)
struct usb_bus *bus;
struct usb_device *dev;
int count = 0;
usb_init();
usb_find_busses();
usb_find_devices();
usb_set_debug(3);
for (bus = usb_get_busses(); bus; bus = bus->next)
for (dev = bus->devices; dev; dev = dev->next)
if (dev->descriptor.idVendor == vendor && dev->descriptor.idProduct == product) {
if (count == instance) {
int status;
usb_dev_handle *udev;
udev = usb_open(dev);
if (!udev) {
fprintf(stderr
, "musb_open: usb_open() error\n");
return MUSB_ACCESS_ERROR;
}
status = usb_set_configuration(udev, configuration);
if (status < 0) {
fprintf(stderr
, "musb_open: usb_set_configuration() error %d (%s)\n", status
,
"musb_open: Found USB device 0x%04x:0x%04x instance %d, but cannot initialize it: please check permissions on \"/proc/bus/usb/%s/%s\" and \"/dev/bus/usb/%s/%s\"\n",
vendor, product, instance, bus->dirname, dev->filename, bus->dirname, dev->filename);
return MUSB_ACCESS_ERROR;
}
/* see if we have write access */
status = usb_claim_interface(udev, usbinterface);
if (status < 0) {
fprintf(stderr
, "musb_open: usb_claim_interface() error %d (%s)\n", status
,
#ifdef _MSC_VER
"musb_open: Found USB device 0x%04x:0x%04x instance %d, but cannot initialize it:\nDevice is probably used by another program\n",
vendor, product, instance);
#else
"musb_open: Found USB device 0x%04x:0x%04x instance %d, but cannot initialize it: please check permissions on \"/proc/bus/usb/%s/%s\"\n",
vendor, product, instance, bus->dirname, dev->filename);
#endif
return MUSB_ACCESS_ERROR;
}
*musb_interface
= (MUSB_INTERFACE
*)calloc(1, sizeof(MUSB_INTERFACE
));
(*musb_interface)->dev = udev;
(*musb_interface)->usb_configuration = configuration;
(*musb_interface)->usb_interface = usbinterface;
return MUSB_SUCCESS;
}
count++;
}
return MUSB_NOT_FOUND;
#elif defined(HAVE_LIBUSB10)
static int first_call = 1;
libusb_device **dev_list;
libusb_device_handle *dev;
struct libusb_device_descriptor desc;
int status, i, n;
int count = 0;
if (first_call) {
first_call = 0;
libusb_init(NULL);
// libusb_set_debug(NULL, 3);
}
n = libusb_get_device_list(NULL, &dev_list);
for (i=0 ; i<n ; i++) {
status = libusb_get_device_descriptor(dev_list[i], &desc);
if (desc.idVendor == vendor && desc.idProduct == product) {
if (count == instance) {
status = libusb_open(dev_list[i], &dev);
if (status < 0) {
fprintf(stderr
, "musb_open: libusb_open() error %d\n", status
);
return MUSB_ACCESS_ERROR;
}
status = libusb_set_configuration(dev, configuration);
if (status < 0) {
fprintf(stderr
, "musb_open: usb_set_configuration() error %d\n", status
);
"musb_open: Found USB device 0x%04x:0x%04x instance %d, but cannot initialize it: please check permissions on \"/proc/bus/usb/%d/%d\" and \"/dev/bus/usb/%d/%d\"\n",
vendor, product, instance, libusb_get_bus_number(dev_list[i]), libusb_get_device_address(dev_list[i]), libusb_get_bus_number(dev_list[i]), libusb_get_device_address(dev_list[i]));
return MUSB_ACCESS_ERROR;
}
/* see if we have write access */
status = libusb_claim_interface(dev, usbinterface);
if (status < 0) {
fprintf(stderr
, "musb_open: libusb_claim_interface() error %d\n", status
);
#ifdef _MSC_VER
"musb_open: Found USB device 0x%04x:0x%04x instance %d, but cannot initialize it:\nDevice is probably used by another program\n",
vendor, product, instance);
#else
"musb_open: Found USB device 0x%04x:0x%04x instance %d, but cannot initialize it: please check permissions on \"/proc/bus/usb/%d/%d\"\n",
vendor, product, instance, libusb_get_bus_number(dev_list[i]), libusb_get_device_address(dev_list[i]));
#endif
return MUSB_ACCESS_ERROR;
}
*musb_interface
= (MUSB_INTERFACE
*)calloc(1, sizeof(MUSB_INTERFACE
));
(*musb_interface)->dev = dev;
(*musb_interface)->usb_configuration = configuration;
(*musb_interface)->usb_interface = usbinterface;
return MUSB_SUCCESS;
}
count++;
}
}
libusb_free_device_list(dev_list, 1);
return MUSB_NOT_FOUND;
#elif defined(OS_DARWIN)
kern_return_t status;
io_iterator_t iter;
io_service_t service;
IOCFPlugInInterface **plugin;
SInt32 score;
IOUSBDeviceInterface **device;
UInt16 xvendor, xproduct;
int count = 0;
*musb_interface
= calloc(1, sizeof(MUSB_INTERFACE
));
status = IORegistryCreateIterator(kIOMasterPortDefault, kIOUSBPlane, kIORegistryIterateRecursively, &iter);
assert(status
== kIOReturnSuccess
);
while ((service = IOIteratorNext(iter))) {
status =
IOCreatePlugInInterfaceForService(service, kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID,
&plugin, &score);
assert(status
== kIOReturnSuccess
);
status = IOObjectRelease(service);
assert(status
== kIOReturnSuccess
);
status =
(*plugin)->QueryInterface(plugin, CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID), (void *) &device);
assert(status
== kIOReturnSuccess
);
status = (*plugin)->Release(plugin);
status = (*device)->GetDeviceVendor(device, &xvendor);
assert(status
== kIOReturnSuccess
);
status = (*device)->GetDeviceProduct(device, &xproduct);
assert(status
== kIOReturnSuccess
);
//fprintf(stderr, "musb_open: Found USB device: vendor 0x%04x, product 0x%04x\n", xvendor, xproduct);
if (xvendor == vendor && xproduct == product) {
if (count == instance) {
fprintf(stderr
, "musb_open: Found USB device: vendor 0x%04x, product 0x%04x, instance %d\n", xvendor
, xproduct
, instance
);
status = (*device)->USBDeviceOpen(device);
fprintf(stderr
, "musb_open: USBDeviceOpen status 0x%x\n", status
);
assert(status
== kIOReturnSuccess
);
(*musb_interface)->usb_configuration = configuration;
(*musb_interface)->usb_interface = usbinterface;
(*musb_interface)->device = (void*)device;
(*musb_interface)->interface = NULL;
status = darwin_configure_device(*musb_interface);
if (status == kIOReturnSuccess)
return MUSB_SUCCESS;
fprintf(stderr
, "musb_open: USB device exists, but configuration fails!");
return MUSB_NOT_FOUND;
}
count++;
}
(*device)->Release(device);
}
return MUSB_NOT_FOUND;
#elif defined(_MSC_VER)
GUID guid;
HDEVINFO hDevInfoList;
SP_DEVICE_INTERFACE_DATA deviceInfoData;
PSP_DEVICE_INTERFACE_DETAIL_DATA functionClassDeviceData;
ULONG predictedLength, requiredLength;
int status;
char device_name[256], str[256];
*musb_interface
= (MUSB_INTERFACE
*)calloc(1, sizeof(MUSB_INTERFACE
));
guid = GUID_CLASS_MSCB_BULK;
// Retrieve device list for GUID that has been specified.
hDevInfoList = SetupDiGetClassDevs(&guid, NULL, NULL, (DIGCF_PRESENT | DIGCF_DEVICEINTERFACE));
status = FALSE;
if (hDevInfoList != NULL) {
// Clear data structure
memset(&deviceInfoData
, 0, sizeof(deviceInfoData
));
deviceInfoData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
// retrieves a context structure for a device interface of a device information set.
if (SetupDiEnumDeviceInterfaces(hDevInfoList, 0, &guid, instance, &deviceInfoData)) {
// Must get the detailed information in two steps
// First get the length of the detailed information and allocate the buffer
// retrieves detailed information about a specified device interface.
functionClassDeviceData = NULL;
predictedLength = requiredLength = 0;
SetupDiGetDeviceInterfaceDetail(hDevInfoList, &deviceInfoData, NULL, // Not yet allocated
0, // Set output buffer length to zero
&requiredLength, // Find out memory requirement
NULL);
predictedLength = requiredLength;
functionClassDeviceData
= (PSP_DEVICE_INTERFACE_DETAIL_DATA
) malloc(predictedLength
);
functionClassDeviceData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
// Second, get the detailed information
if (SetupDiGetDeviceInterfaceDetail(hDevInfoList,
&deviceInfoData, functionClassDeviceData,
predictedLength, &requiredLength, NULL)) {
// Save the device name for subsequent pipe open calls
strcpy(device_name
, functionClassDeviceData
->DevicePath
);
free(functionClassDeviceData
);
// Signal device found
status = TRUE;
} else
free(functionClassDeviceData
);
}
}
// SetupDiDestroyDeviceInfoList() destroys a device information set
// and frees all associated memory.
SetupDiDestroyDeviceInfoList(hDevInfoList);
if (status) {
// Get the read handle
sprintf(str
, "%s\\PIPE00", device_name
);
(*musb_interface)->rhandle = CreateFile(str,
GENERIC_WRITE | GENERIC_READ,
FILE_SHARE_WRITE | FILE_SHARE_READ, NULL,
OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
if ((*musb_interface)->rhandle == INVALID_HANDLE_VALUE)
return MUSB_ACCESS_ERROR;
// Get the write handle
sprintf(str
, "%s\\PIPE01", device_name
);
(*musb_interface)->whandle = CreateFile(str,
GENERIC_WRITE | GENERIC_READ,
FILE_SHARE_WRITE | FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
if ((*musb_interface)->whandle == INVALID_HANDLE_VALUE)
return MUSB_ACCESS_ERROR;
return MUSB_SUCCESS;
}
return MUSB_NOT_FOUND;
#endif
}
int musb_set_altinterface(MUSB_INTERFACE *musb_interface, int index)
{
#if defined (HAVE_LIBUSB)
int status;
status = usb_set_altinterface(musb_interface->dev, index);
if (status < 0)
fprintf(stderr
, "musb_set_altinterface: usb_set_altinterface() error %d\n", status
);
return status;
#else
return -1;
#endif
}
int musb_close(MUSB_INTERFACE *musb_interface)
{
#if defined(HAVE_LIBUSB)
int status;
status = usb_release_interface(musb_interface->dev, musb_interface->usb_interface);
if (status < 0)
fprintf(stderr
, "musb_close: usb_release_interface() error %d\n", status
);
#ifdef OS_LINUX // linux wants a reset, otherwise the device cannot be accessed next time
musb_reset(musb_interface);
#endif
status = usb_close(musb_interface->dev);
if (status < 0)
fprintf(stderr
, "musb_close: usb_close() error %d\n", status
);
#elif defined(HAVE_LIBUSB10)
int status;
status = libusb_release_interface(musb_interface->dev, musb_interface->usb_interface);
if (status < 0)
fprintf(stderr
, "musb_close: libusb_release_interface() error %d\n", status
);
#ifdef OS_LINUX // linux wants a reset, otherwise the device cannot be accessed next time
musb_reset(musb_interface);
#endif
libusb_close(musb_interface->dev);
#elif defined(OS_DARWIN)
IOReturn status;
IOUSBInterfaceInterface **interface = (IOUSBInterfaceInterface **)musb_interface->interface;
status = (*interface)->USBInterfaceClose(interface);
if (status != kIOReturnSuccess)
fprintf(stderr
, "musb_close: USBInterfaceClose() status %d 0x%x\n", status
, status
);
status = (*interface)->Release(interface);
if (status != kIOReturnSuccess)
fprintf(stderr
, "musb_close: USB Interface Release() status %d 0x%x\n", status
, status
);
IOUSBDeviceInterface **device = (IOUSBDeviceInterface**)musb_interface->device;
status = (*device)->USBDeviceClose(device);
if (status != kIOReturnSuccess)
fprintf(stderr
, "musb_close: USBDeviceClose() status %d 0x%x\n", status
, status
);
status = (*device)->Release(device);
if (status != kIOReturnSuccess)
fprintf(stderr
, "musb_close: USB Device Release() status %d 0x%x\n", status
, status
);
#elif defined(_MSC_VER)
CloseHandle(musb_interface->rhandle);
CloseHandle(musb_interface->whandle);
#else
assert(!"musb_close() is not implemented");
#endif
/* free memory allocated in musb_open() */
return 0;
}
int musb_write(MUSB_INTERFACE *musb_interface, int endpoint, const void *buf, int count, int timeout)
{
int n_written;
#if defined(HAVE_LIBUSB)
n_written = usb_bulk_write(musb_interface->dev, endpoint, (char*)buf, count, timeout);
if (n_written != count) {
fprintf(stderr
, "musb_write: requested %d, wrote %d, errno %d (%s)\n", count
, n_written
, errno
, strerror(errno
));
}
#elif defined(HAVE_LIBUSB10)
int status = libusb_bulk_transfer(musb_interface->dev, endpoint, (unsigned char*)buf, count, &n_written, timeout);
if (n_written != count) {
fprintf(stderr
, "musb_write: requested %d, wrote %d, errno %d (%s)\n", count
, n_written
, status
, strerror(status
));
}
#elif defined(OS_DARWIN)
IOReturn status;
IOUSBInterfaceInterface182 **interface = (IOUSBInterfaceInterface182 **)musb_interface->interface;
status = (*interface)->WritePipeTO(interface, endpoint, buf, count, 0, timeout);
if (status != 0) {
fprintf(stderr
, "musb_write: WritePipe() status %d 0x%x\n", status
, status
);
return -1;
}
n_written = count;
#elif defined(_MSC_VER)
WriteFile(musb_interface->whandle, buf, count, &n_written, NULL);
#endif
//fprintf(stderr, "musb_write(ep %d, %d bytes) (%s) returns %d\n", endpoint, count, buf, n_written);
return n_written;
}
int musb_read(MUSB_INTERFACE *musb_interface, int endpoint, void *buf, int count, int timeout)
{
int n_read = 0;
#if defined(HAVE_LIBUSB)
n_read = usb_bulk_read(musb_interface->dev, endpoint | 0x80, (char*)buf, count, timeout);
/* errors should be handled in upper layer ....
if (n_read <= 0) {
fprintf(stderr, "musb_read: requested %d, read %d, errno %d (%s)\n", count, n_read, errno, strerror(errno));
}
*/
#elif defined(HAVE_LIBUSB10)
libusb_bulk_transfer(musb_interface->dev, endpoint | 0x80, (unsigned char*)buf, count, &n_read, timeout);
/* errors should be handled in upper layer ....
if (n_read <= 0) {
fprintf(stderr, "musb_read: requested %d, read %d, errno %d (%s)\n", count, n_read, status, strerror(status));
}
*/
#elif defined(OS_DARWIN)
UInt32 xcount = count;
IOReturn status;
IOUSBInterfaceInterface182 **interface = (IOUSBInterfaceInterface182 **)musb_interface->interface;
status = (*interface)->ReadPipeTO(interface, endpoint, buf, &xcount, 0, timeout);
if (status != kIOReturnSuccess) {
fprintf(stderr
, "musb_read: requested %d, read %d, ReadPipe() status %d 0x%x (%s)\n", count
, n_read
, status
, status
, strerror(status
));
return -1;
}
n_read = xcount;
#elif defined(_MSC_VER)
OVERLAPPED overlapped;
int status;
memset(&overlapped
, 0, sizeof(overlapped
));
overlapped.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
n_read = 0;
status = ReadFile(musb_interface->rhandle, buf, count, &n_read, &overlapped);
if (!status) {
status = GetLastError();
if (status != ERROR_IO_PENDING)
return 0;
/* wait for completion with timeout */
status = WaitForSingleObject(overlapped.hEvent, timeout);
if (status == WAIT_TIMEOUT)
CancelIo(musb_interface->rhandle);
else
GetOverlappedResult(musb_interface->rhandle, &overlapped, &n_read, FALSE);
}
CloseHandle(overlapped.hEvent);
#endif
//fprintf(stderr, "musb_read(ep %d, %d bytes) returns %d (%s)\n", endpoint, count, n_read, buf);
return n_read;
}
int musb_reset(MUSB_INTERFACE *musb_interface)
{
#if defined(HAVE_LIBUSB)
/* Causes re-enumeration: After calling usb_reset, the device will need
to re-enumerate and thusly, requires you to find the new device and
open a new handle. The handle used to call usb_reset will no longer work */
int status;
status = usb_reset(musb_interface->dev);
if (status < 0)
fprintf(stderr
, "musb_reset: usb_reset() status %d\n", status
);
#elif defined(HAVE_LIBUSB10)
int status;
status = libusb_reset_device(musb_interface->dev);
if (status < 0)
fprintf(stderr
, "musb_reset: usb_reset() status %d\n", status
);
#elif defined(OS_DARWIN)
IOReturn status;
IOUSBDeviceInterface **device = (IOUSBDeviceInterface**)musb_interface->device;
status = (*device)->ResetDevice(device);
fprintf(stderr
, "musb_reset: ResetDevice() status 0x%x\n", status
);
status = darwin_configure_device(musb_interface);
assert(status
== kIOReturnSuccess
);
#elif defined(_MSC_VER)
#define IOCTL_BULKUSB_RESET_DEVICE CTL_CODE(FILE_DEVICE_UNKNOWN, \
1, \
METHOD_BUFFERED, \
FILE_ANY_ACCESS)
#define IOCTL_BULKUSB_RESET_PIPE CTL_CODE(FILE_DEVICE_UNKNOWN, \
2, \
METHOD_BUFFERED, \
FILE_ANY_ACCESS)
int status, n_bytes;
status = DeviceIoControl(musb_interface->rhandle,
IOCTL_BULKUSB_RESET_DEVICE,
NULL, 0, NULL, 0, &n_bytes, NULL);
status = DeviceIoControl(musb_interface->whandle,
IOCTL_BULKUSB_RESET_DEVICE,
NULL, 0, NULL, 0, &n_bytes, NULL);
status = DeviceIoControl(musb_interface->rhandle,
IOCTL_BULKUSB_RESET_PIPE,
NULL, 0, NULL, 0, &n_bytes, NULL);
status = DeviceIoControl(musb_interface->whandle,
IOCTL_BULKUSB_RESET_PIPE,
NULL, 0, NULL, 0, &n_bytes, NULL);
return status;
#endif
return 0;
}
int musb_get_device(MUSB_INTERFACE *usb_interface)
{
#ifdef HAVE_LIBUSB
struct usb_device_descriptor d;
usb_get_descriptor(usb_interface->dev, USB_DT_DEVICE, 0, &d, sizeof(d));
return d.bcdDevice;
#elif HAVE_LIBUSB10
struct libusb_device_descriptor d;
libusb_get_descriptor(usb_interface->dev, LIBUSB_DT_DEVICE, 0, (unsigned char *)&d, sizeof(d));
return d.bcdDevice;
#else
return 0;
#endif
}
/* end */