/********************************************************************\ Name: musbstd.c Created by: Konstantin Olchanski, Stefan Ritt Contents: Midas USB access $Id$ \********************************************************************/ #include #include #include #ifdef _MSC_VER // Windows includes #include #include #include #include #include /* 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 #include #include #include #include #include #include #include #include #include #include #include #include #elif defined(OS_LINUX) // Linux includes #include #include #include #endif #ifdef HAVE_LIBUSB #include #include #endif #ifdef HAVE_LIBUSB10 #include #include #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, strerror(-status)); fprintf(stderr, "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, strerror(-status)); #ifdef _MSC_VER fprintf(stderr, "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 fprintf(stderr, "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 ; idev = 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() */ free(musb_interface); 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 */