#ifndef __USBDEVICE_H__ #define __USBDEVICE_H__ #include #include #include #include #include #include "stringutils.h" enum { XFER_TIMEOUT= 15000 }; struct vidpid { vidpid(int vid,int pid) : vid(vid), pid(pid) { } int vid; int pid; }; inline bool operator<(const vidpid& a, const vidpid& b) { return (a.vid vidpid_s; typedef boost::shared_ptr usbdev_ptr; typedef boost::shared_ptr usbdevlist_ptr; class usberror { std::string _msg; int _err; public: usberror(int err, const char*fmt, ...) : _err(err) { va_list ap; va_start(ap, fmt); _msg= stringvformat(fmt, ap); va_end(ap); } ~usberror() { printf("USBERROR: %s : %s\n", errmsg(_err).c_str(), _msg.c_str()); } static std::string errmsg(int err) { switch(err) { case LIBUSB_SUCCESS: return "SUCCESS"; case LIBUSB_ERROR_IO: return "ERROR_IO"; case LIBUSB_ERROR_INVALID_PARAM: return "ERROR_INVALID_PARAM"; case LIBUSB_ERROR_ACCESS: return "ERROR_ACCESS"; case LIBUSB_ERROR_NO_DEVICE: return "ERROR_NO_DEVICE"; case LIBUSB_ERROR_NOT_FOUND: return "ERROR_NOT_FOUND"; case LIBUSB_ERROR_BUSY: return "ERROR_BUSY"; case LIBUSB_ERROR_TIMEOUT: return "ERROR_TIMEOUT"; case LIBUSB_ERROR_OVERFLOW: return "ERROR_OVERFLOW"; case LIBUSB_ERROR_PIPE: return "ERROR_PIPE"; case LIBUSB_ERROR_INTERRUPTED: return "ERROR_INTERRUPTED"; case LIBUSB_ERROR_NO_MEM: return "ERROR_NO_MEM"; case LIBUSB_ERROR_NOT_SUPPORTED: return "ERROR_NOT_SUPPORTED"; case LIBUSB_ERROR_OTHER: return "ERROR_OTHER"; default: return stringformat("LIBUSB_ERR_%d", -err); } } }; class usbdev { public: vidpid_s _vpset; vidpid_s _prev; usbdev_ptr _phone; struct libusb_device_descriptor _desc; uint8_t _intep; uint8_t _outep; uint8_t _inep; std::vector _claimed; usbdev() : _intep(0), _outep(0), _inep(0) { int rc= libusb_init(NULL); if (rc<0) throw usberror(rc, "init"); } void support(const vidpid& vp) { _vpset.insert(vp); } static void closedevlist(libusb_device**devs) { libusb_free_device_list(devs, 1); } usbdevlist_ptr mkusbdevlistptr(libusb_device**h) { return usbdevlist_ptr(h, closedevlist); } usbdev_ptr mkusbdevptr(libusb_device_handle*h) { return usbdev_ptr(h, libusb_close); } void reset() { int rc; rc=libusb_reset_device(_phone.get()); _phone.reset(); if (rc<0) throw usberror(rc, "reset_device"); } void dumpset(const vidpid_s& s) { for (vidpid_s::const_iterator i= s.begin() ; i!=s.end() ; i++) { printf(" %04x:%04x", (*i).vid, (*i).pid); } } void dumpdiff(const vidpid_s& prev, const vidpid_s& cur) { vidpid_s added; for (vidpid_s::iterator i=cur.begin() ; i!=cur.end() ; i++) { if (_prev.find(*i)==_prev.end()) added.insert(*i); } if (!added.empty()) { printf("added:"); dumpset(added); printf("\n"); } vidpid_s removed; for (vidpid_s::iterator i=_prev.begin() ; i!=_prev.end() ; i++) { if (cur.find(*i)==cur.end()) removed.insert(*i); } if (!removed.empty()) { printf("removed:"); dumpset(removed); printf("\n"); } } static bool ismbpkeyboard(int vid, int pid) { return vid==0x5ac && pid==0x231; } static bool ismbpinternalhub(int vid, int pid) { return vid==0xa5c && pid==0x4500; } bool connect() { libusb_device **devs; int rc= libusb_get_device_list(NULL, &devs); if (rc<0) throw usberror(rc, "get_device_list"); return searchdevs(mkusbdevlistptr(devs)); } bool searchdevs(usbdevlist_ptr devs) { int rc; libusb_device *dev; struct libusb_device_descriptor desc; vidpid_s cur; _phone.reset(); for (int i=0 ; (dev= devs.get()[i])!=NULL ; i++) { rc = libusb_get_device_descriptor(dev, &desc); if (rc<0) throw usberror(rc, "get_device_descriptor(%d)", i); if (ismbpkeyboard(desc.idVendor, desc.idProduct)) continue; if (ismbpinternalhub(desc.idVendor, desc.idProduct)) continue; cur.insert(vidpid(desc.idVendor, desc.idProduct)); if (_vpset.find(vidpid(desc.idVendor, desc.idProduct))!=_vpset.end()) { printf("found dev: %04x:%04x (bus %d, addr %d)\n", desc.idVendor, desc.idProduct, libusb_get_bus_number(dev), libusb_get_device_address(dev)); break; } } if (dev) { _desc= desc; libusb_device_handle *handle; rc= libusb_open(dev, &handle); if (rc<0) throw usberror(rc, "open"); _phone = mkusbdevptr(handle); find_endpoints(&_desc, dev); return true; } if (!_prev.empty()) dumpdiff(_prev, cur); _prev.swap(cur); return false; } void find_endpoints(libusb_device_descriptor*devdesc, struct libusb_device *dev) { printf("DEV: l=%d, t=%d, spec=%04x class=%02x/%02x/%02x, mpkt=%d, vid/pid=%04x/%04x, relnum=%04x mfr/prod=%02x/%02x serial=%02x\n", devdesc->bLength, devdesc->bDescriptorType, devdesc->bcdUSB, devdesc->bDeviceClass, devdesc->bDeviceSubClass, devdesc->bDeviceProtocol, devdesc->bMaxPacketSize0, devdesc->idVendor, devdesc->idProduct, devdesc->bcdDevice, devdesc->iManufacturer, devdesc->iProduct, devdesc->iSerialNumber); printf("#cfg=%d\n", devdesc->bNumConfigurations); int rc; for (unsigned icfg=0; icfgbNumConfigurations ; icfg++) { struct libusb_config_descriptor *cfgdesc; rc= libusb_get_config_descriptor(dev, icfg, &cfgdesc); if (rc<0) throw usberror(rc, "get_config_descriptor(%d)", icfg); printf("CFG: l=%d, t=%d, tl=%d, nif=%d, val=%d, str=%d, maxpow=%d extra=%d\n", cfgdesc->bLength, cfgdesc->bDescriptorType, cfgdesc->wTotalLength, cfgdesc->bNumInterfaces, cfgdesc->bConfigurationValue, cfgdesc->iConfiguration, cfgdesc->bmAttributes, cfgdesc->MaxPower); printf("cfg%d[%02x] #if=%d\n", icfg, cfgdesc->bConfigurationValue, cfgdesc->bNumInterfaces); for (unsigned iif=0 ; iifbNumInterfaces ; iif++) { for (unsigned ia= 0 ; ia<(unsigned)cfgdesc->interface[iif].num_altsetting ; ia++) { const libusb_interface_descriptor *ifdesc = &cfgdesc->interface[iif].altsetting[ia]; bool claim= false; printf("IF: l=%d, t=%d, nr=%d, alt=%d, #ep=%d, class=%02x/%02x/%02x, str=%d\n", ifdesc->bLength, ifdesc->bDescriptorType, ifdesc->bInterfaceNumber, ifdesc->bAlternateSetting, ifdesc->bNumEndpoints, ifdesc->bInterfaceClass, ifdesc->bInterfaceSubClass, ifdesc->bInterfaceProtocol, ifdesc->iInterface); printf("cfg%d if%d:%d[%02x] #ep=%d\n", icfg, iif, ia, ifdesc->bInterfaceNumber, ifdesc->bNumEndpoints); for (unsigned iep = 0; iep < ifdesc->bNumEndpoints; iep++) { const libusb_endpoint_descriptor *ep = &ifdesc->endpoint[iep]; uint8_t type = ep->bmAttributes & LIBUSB_TRANSFER_TYPE_MASK; printf("EP: l=%d, t=%d, ep=%02x, attr=%02x, maxpkt=%d, int=%d, ref=%d, sync=%d\n", ep->bLength, ep->bDescriptorType, ep->bEndpointAddress, ep->bmAttributes, ep->wMaxPacketSize, ep->bInterval, ep->bRefresh, ep->bSynchAddress); if ((ep->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_IN) { if (type == LIBUSB_TRANSFER_TYPE_INTERRUPT) { _intep = ep->bEndpointAddress; claim= true; } else if (type == LIBUSB_TRANSFER_TYPE_BULK) { _inep = ep->bEndpointAddress; claim= true; } } else { if (type == LIBUSB_TRANSFER_TYPE_BULK) { _outep = ep->bEndpointAddress; claim= true; } } if (!claim) { printf("not claiming endpoint: cfg%d if%d ep%d: ep%02x\n", icfg, iif, iep, ep->bEndpointAddress); } } if (claim) { rc= libusb_set_configuration(_phone.get(), cfgdesc->bConfigurationValue); if (rc<0) throw usberror(rc, "set_configuration(%d)", icfg); rc= libusb_claim_interface(_phone.get(), ifdesc->bInterfaceNumber); if (rc<0) throw usberror(rc, "claim_interface(%d)", iif); rc= libusb_set_interface_alt_setting(_phone.get(), ifdesc->bInterfaceNumber, ifdesc->bAlternateSetting); if (rc<0) throw usberror(rc, "set_interface_alt_setting(%d, %d)", iif, ia); _claimed.push_back(ifdesc->bInterfaceNumber); printf("claimed cfg %d, if %d endpoints: int=%02x(max=%d) in=%02x(max=%d) out=%02x(max=%d)\n", icfg, iif, _intep, libusb_get_max_packet_size(dev, _intep), _inep, libusb_get_max_packet_size(dev, _inep), _outep, libusb_get_max_packet_size(dev, _outep)); } } } } printf("done claiming\n"); } void disconnect() { int rc; for (unsigned i=0 ; i<_claimed.size() ; i++) { rc= libusb_release_interface(_phone.get(), _claimed[i]); if (rc<0) throw usberror(rc, "release_interface(%d)", _claimed[i]); } } void controlwrite(uint8_t request_type, uint8_t request, uint16_t value, uint16_t index, const ByteVector& data) { printf("ctlrq %02x %02x %04x %04x l=%zd: %s\n", request_type, request, value, index, data.size(), hexdump(data).c_str()); int rc= libusb_control_transfer(_phone.get(), request_type, request, value, index, data.empty() ? 0 : const_cast(&data.front()), data.size(), XFER_TIMEOUT); if (rc<0) throw usberror(rc, "control_transfer"); } void controlread(uint8_t request_type, uint8_t request, uint16_t value, uint16_t index, size_t expected=0) { ByteVector data(expected); controlread(request_type, request, value, index, data); } void controlread(uint8_t request_type, uint8_t request, uint16_t value, uint16_t index, ByteVector& data) { printf("ctlrd %02x %02x %04x %04x l=%zd\n", request_type, request, value, index, data.size()); int rc= libusb_control_transfer(_phone.get(), request_type, request, value, index, data.empty() ? 0 : &data.front(), data.size(), XFER_TIMEOUT); if (rc==LIBUSB_ERROR_PIPE) { // unsupported req usberror(rc, "control_transfer"); return; } if (rc<0) throw usberror(rc, "control_transfer"); data.resize(rc); printf("controlread: %s\n", hexdump(data).c_str()); } void dumpdesc() { uint8_t buf[128]; int rc; rc= libusb_get_descriptor(_phone.get(), 1, 0, buf, 128); if (rc<0) throw usberror(rc, "get_descriptor"); printf("desc[1,0]:%02x %s\n", rc, hexdump(buf, rc).c_str()); rc= libusb_get_descriptor(_phone.get(), 2, 0, buf, 128); if (rc<0) throw usberror(rc, "get_descriptor"); printf("desc[2,0]:%02x %s\n", rc, hexdump(buf, rc).c_str()); } // void setaltinterface(int n) // { // int rc; // rc= usb_set_altinterface(_phone.get(), n); // if (rc<0) // throw usberror(rc, "set_altinterface"); // } template size_t bulkread(I begin, I end) { if (_inep==0) throw "no input endpoint"; int xferred=0; printf("bulkrq: %zd\n", end-begin); int rc; rc= libusb_bulk_transfer(_phone.get(), _inep, &*begin, end-begin, &xferred, XFER_TIMEOUT); printf("bulk: %d n=%d\n", rc, xferred); if (rc==LIBUSB_ERROR_OTHER || rc==LIBUSB_ERROR_TIMEOUT) // probably timeout return 0; if (rc<0) throw usberror(rc, "bulk_xfer(in, %d)", end-begin); return xferred; } size_t bulkwrite(const ByteVector& v) { return bulkwrite(v.begin(), v.end()); } template size_t bulkwrite(I begin, I end) { if (_outep==0) throw "no output endpoint"; int xferred; int rc; rc= libusb_bulk_transfer(_phone.get(), _outep, const_cast(&*begin), end-begin, &xferred, XFER_TIMEOUT); if (rc<0) throw usberror(rc, "bulk_xfer(out, %d)", end-begin); return xferred; } bool isserial() { printf("devclass: %02x/%x/%x\n", \ _desc.bDeviceClass, _desc.bDeviceSubClass, _desc.bDeviceProtocol); // todo return _desc.bDeviceClass!=0xef; } }; #endif