/* * Copyright (c) 2012 * Lars-Dominik Braun * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include #include "usb.h" /* our software identifier */ #define HIDPP_SOFTWARE_ID 0xB #define HIDPP_USB_INTERFACE 2 /* send packet to device, blocking */ int hidppUsbSend (libusb_device_handle * const dev, const hidppPacket_t * const p) { assert (dev != NULL); assert (p != NULL); //assert (p->devId >= 0x1); assert (p->devId <= HIDPP_DEVID_ROOT); const unsigned int timeout = 500; unsigned char packet[7] = {p->id, p->devId, p->featureIdx, (p->function<<4) | p->softwareId}; int ret; memcpy (&packet[4], p->args, 3); /* FIXME: magic numbers */ ret = libusb_control_transfer (dev, LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE, LIBUSB_REQUEST_SET_CONFIGURATION, 0x0210, 0x0002, packet, sizeof (packet)/sizeof (*packet), timeout); if (ret < 0) { printf ("<< failed! %s", libusb_error_name (ret)); return HIDPP_RET_USB_ERROR; } dumphex ("<< ", packet, sizeof (packet)); return HIDPP_RET_OK; } /* receive single packet from device, blocking */ hidppReturn_t hidppUsbReceive (libusb_device_handle * const dev, hidppPacket_t * const p) { int len, ret; unsigned char packet[HIDPP_MAX_PACKETLEN]; const unsigned int timeout = 500; assert (dev != NULL); assert (p != NULL); ret = libusb_bulk_transfer (dev, 0x83, packet, sizeof (packet), &len, timeout); if (ret != LIBUSB_SUCCESS) { printf (">> failed! %s\n", libusb_error_name (ret)); return HIDPP_RET_USB_ERROR; } dumphex (">> ", packet, len); memset (p, 0, sizeof (*p)); p->id = packet[0]; p->devId = packet[1]; if (packet[2] == 0x8F || packet[2] == 0xFF) { /* error */ assert (len > 6); p->featureIdx = packet[3]; p->function = (packet[4] >> 4) & 0xf; p->softwareId = packet[4] & 0xf; p->error = packet[6]; } else { assert (len > 3); p->featureIdx = packet[2]; p->function = (packet[3] >> 4) & 0xf; p->softwareId = packet[3] & 0xf; if (len > 4) { memcpy (p->args, &packet[4], len-4); } } return HIDPP_RET_OK; } /* initialize packet */ void hidppUsbPacketInit (hidppPacket_t * const p, const hidppDeviceId_t devId, const uint8_t featureIdx, const uint8_t function, const uint8_t softwareId, const uint8_t * const args, const size_t argsSize) { assert (p != NULL); assert (argsSize < 16); memset (p, 0, sizeof (*p)); if (argsSize < 4) { p->id = 0x10; } else if (argsSize < 16) { p->id = 0x11; } p->devId = devId; p->featureIdx = featureIdx; /* function is just four bits */ assert (function < (1<<4)); p->function = function & 0xf; /* software id is just four bits */ assert (softwareId < (1<<4)); p->softwareId = softwareId & 0xf; if (args != NULL) { assert (argsSize > 0); assert (argsSize < sizeof (p->args)/sizeof (*p->args)); memcpy (p->args, args, argsSize); } } /* high-level request wrapper, drops events/notifications from device */ hidppReturn_t hidppUsbRequest (hidppDevice_t * const d, const hidppFeatureId_t feature, const uint8_t function, const uint8_t * const args, const size_t argsCount, hidppPacket_t * const rp) { hidppReturn_t ret; hidppPacket_t sp; uint8_t featureIdx; if (!hidppFeatureIdToIdx (d, feature, &featureIdx)) { assert (0); return HIDPP_RET_ERROR; } hidppUsbPacketInit (&sp, d->id, featureIdx, function, HIDPP_SOFTWARE_ID, args, argsCount); ret = hidppUsbSend (d->usbDev, &sp); while (ret == HIDPP_RET_OK) { if ((ret = hidppUsbReceive (d->usbDev, rp)) == HIDPP_RET_OK) { if ((rp->id != 0x10 && rp->id != 0x11) || rp->featureIdx != sp.featureIdx || rp->function != sp.function || rp->softwareId != sp.softwareId) { /* ignore non-hid++ packets, events and packets not meant for us */ continue; } else { return HIDPP_RET_OK; } } } return ret; } void hidppUsbDeviceOpen (libusb_device * const dev, libusb_device_handle **handle) { int ret; ret = libusb_open (dev, handle); assert (ret == LIBUSB_SUCCESS); if (libusb_kernel_driver_active (*handle, HIDPP_USB_INTERFACE) == 1) { ret = libusb_detach_kernel_driver (*handle, HIDPP_USB_INTERFACE); assert (ret == LIBUSB_SUCCESS); } ret = libusb_claim_interface (*handle, HIDPP_USB_INTERFACE); assert (ret == LIBUSB_SUCCESS); } void hidppUsbDeviceClose (libusb_device_handle *handle) { int ret; ret = libusb_release_interface (handle, HIDPP_USB_INTERFACE); assert (ret == LIBUSB_SUCCESS); ret = libusb_attach_kernel_driver (handle, HIDPP_USB_INTERFACE); assert (ret == LIBUSB_SUCCESS); libusb_close (handle); }