/* PIP - Platform Independent Primitives USB, based on libusb Ivan Pelipenko peri4ko@yandex.ru This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . */ #include "piusb.h" #include "piincludes_p.h" #include "piconfig.h" #ifdef PIP_USB # ifdef WINDOWS # include # else # include # endif #endif REGISTER_DEVICE(PIUSB) PIUSB::PIUSB(ushort vid, ushort pid): PIIODevice("", ReadWrite) { vid_ = vid; pid_ = pid; intefrace_ = 0; hdev = 0; timeout_r = timeout_w = 0; interface_claimed = -1; setPath(PIString::fromNumber(vid_, 16).expandLeftTo(4, "0") + ":" + PIString::fromNumber(pid_, 16).expandLeftTo(4, "0")); setDeviceNumber(1); setTimeoutRead(1000); setTimeoutWrite(1000); } PIUSB::~PIUSB() { stop(); close(); } void PIUSB::Endpoint::parse() { synchronisation_type = NoSynchonisation; usage_type = DataEndpoint; direction = (Direction)((address >> 7) & 1); transfer_type = (TransferType)(attributes & 3); if (transfer_type == Isochronous) { synchronisation_type = (SynchronisationType)((attributes >> 2) & 3); usage_type = (UsageType)((attributes >> 4) & 3); } } PIUSB::Endpoint PIUSB::getEndpointByAddress(uchar address) { piForeachC (Endpoint & i, eps) if (i.address == address) return i; return Endpoint(); } PIVector PIUSB::endpointsRead() { PIVector ret; piForeachC (Endpoint & i, eps) if (i.direction == Endpoint::Read) ret << i; return ret; } PIVector PIUSB::endpointsWrite() { PIVector ret; piForeachC (Endpoint & i, eps) if (i.direction == Endpoint::Write) ret << i; return ret; } bool PIUSB::setConfiguration(uchar value) { #ifdef PIP_USB if (hdev == 0) return false; bool found = false; piForeachC (Configuration & c, desc_.configurations) if (c.value_to_select == value) {found = true; conf_ = c; break;} if (!found) { piCoutObj << "Can`t find configuration with \"value_to_select\" =" << value; return false; } if (interface_claimed >= 0) usb_release_interface(hdev, interface_claimed); interface_claimed = -1; return setInterface(conf_.interfaces.front().value_to_select); #else return false; #endif } bool PIUSB::setInterface(uchar value) { #ifdef PIP_USB if (hdev == 0) return false; bool found = false; piForeachC (Interface & i, conf_.interfaces) if (i.value_to_select == value) {found = true; iface_ = i; break;} if (!found) { piCoutObj << "Can`t find interface with \"value_to_select\" =" << value; return false; } if (interface_claimed >= 0) usb_release_interface(hdev, interface_claimed); interface_claimed = -1; if (usb_claim_interface(hdev, iface_.value_to_select) < 0) { piCoutObj << "Error: Cant`t claim interface!"; return false; } eps.clear(); eps = iface_.endpoints; ep_read = ep_write = Endpoint(); for (int i = 0; i < eps.size_s(); ++i) { if (eps[i].direction == Endpoint::Read && ep_read.isNull()) ep_read = eps[i]; if (eps[i].direction == Endpoint::Write && ep_write.isNull()) ep_write = eps[i]; } interface_claimed = value; return true; #else return false; #endif } bool PIUSB::configureDevice(const void * e_main, const void * e_parent) { #ifdef PIP_USB PIConfig::Entry * em = (PIConfig::Entry * )e_main; PIConfig::Entry * ep = (PIConfig::Entry * )e_parent; PIString vp = readDeviceSetting("device", "", em, ep); ushort v, p; if (vp.isEmpty()) { v = readDeviceSetting("vid", vendorID(), em, ep); p = readDeviceSetting("pid", productID(), em, ep); } else { v = vp.left(vp.find(":")).toInt(16); p = vp.right(vp.length() - vp.find(":") - 1).toInt(16); } setVendorID(v); setProductID(p); setDeviceNumber(readDeviceSetting("deviceNumber", deviceNumber(), em, ep)); setConfiguration(readDeviceSetting("configuration", currentConfiguration().value_to_select, em, ep)); setInterface(readDeviceSetting("interface", currentInterface().value_to_select, em, ep)); setEndpointRead(Endpoint(readDeviceSetting("endpointRead", endpointRead().address, em, ep))); setEndpointWrite(Endpoint(readDeviceSetting("endpointWrite", endpointWrite().address, em, ep))); return true; #else return false; #endif } bool PIUSB::openDevice() { #ifdef PIP_USB if (path().size_s() >= 8) { vid_ = path().left(4).toInt(16); pid_ = path().right(4).toInt(16); } if (hdev != 0) closeDevice(); hdev = 0; interface_claimed = -1; ep_write = ep_read = Endpoint(); usb_init(); //usb_set_debug(4); if (usb_find_busses() < 0) { piCoutObj << "Error: Cant`t find busses!"; return false; } if (usb_find_devices() < 0) { piCoutObj << "Error: Cant`t find devices!"; return false; } //piCoutObj << "Search for device ... " << flush; int cur_num = 1; bool found = false; struct usb_device * dev; struct usb_bus * bus; for (bus = usb_get_busses(); bus; bus = bus->next) { for (dev = bus->devices; dev; dev = dev->next) { if (dev->descriptor.idVendor == vid_ && dev->descriptor.idProduct == pid_) { if (cur_num == deviceNumber()) { struct usb_device_descriptor & dd(dev->descriptor); desc_.usb_spec_number = dd.bcdUSB; desc_.device_class = dd.bDeviceClass; desc_.device_subclass = dd.bDeviceSubClass; desc_.device_protocol = dd.bDeviceProtocol; desc_.max_packet_size = dd.bMaxPacketSize0; desc_.id_vendor = dd.idVendor; desc_.id_product = dd.idProduct; desc_.id_device_release = dd.bcdDevice; desc_.index_manufacturer = dd.iManufacturer; desc_.index_product = dd.iProduct; desc_.index_serial = dd.iSerialNumber; desc_.configurations.clear(); for (int c = 0; c < dd.bNumConfigurations; ++c) { desc_.configurations << Configuration(); Configuration & conf(desc_.configurations.back()); struct usb_config_descriptor & dc(dev->config[c]); conf.index = c; conf.value_to_select = dc.bConfigurationValue; conf.attributes = dc.bmAttributes; conf.max_power = ushort(dc.MaxPower) * 2; conf.self_powered = (conf.attributes >> 6) & 1; conf.remote_wakeup = (conf.attributes >> 5) & 1; conf.interfaces.clear(); for (int i = 0; i < dc.bNumInterfaces; ++i) { conf.interfaces << Interface(); Interface & infc(conf.interfaces.back()); struct usb_interface_descriptor * di(dc.interface[c].altsetting); infc.index = i; infc.value_to_select = di->bAlternateSetting; infc.class_code = di->bInterfaceClass; infc.subclass_code = di->bInterfaceSubClass; infc.protocol_code = di->bInterfaceProtocol; infc.endpoints.clear(); for (int e = 0; e < di->bNumEndpoints; ++e) { infc.endpoints << Endpoint(di->endpoint[e].bEndpointAddress, di->endpoint[e].bmAttributes, di->endpoint[e].wMaxPacketSize); } } } if (!desc_.configurations.isEmpty()) conf_ = desc_.configurations.front(); struct usb_interface_descriptor * is = dev->config->interface->altsetting; int epn = is->bNumEndpoints; eps.clear(); for (int i = 0; i < epn; ++i) { eps << Endpoint(is->endpoint[i].bEndpointAddress, is->endpoint[i].bmAttributes, is->endpoint[i].wMaxPacketSize); if (eps.back().direction == Endpoint::Write && (eps.back().address == ep_write.address || ep_write.address == 0)) ep_write = eps.back(); if (eps.back().direction == Endpoint::Read && (eps.back().address == ep_read.address || ep_read.address == 0)) ep_read = eps.back(); } //piCoutObj << "Device found at address:" << "Bus: " << dev->bus->dirname << ", Device: " << dev->filename; found = true; break; } else cur_num++; } } if (found) break; } if (!found) { piCoutObj << "Error: Cant`t find device!"; return false; } //piCoutObj << "Open ... " << flush; hdev = usb_open(dev); if (hdev == 0) { piCoutObj << "Error: Cant`t open device:" << usb_strerror(); return false; }// else piCoutObj << "ok"; //usb_reset(hdev); //usb_set_configuration(hdev, 1); //usb_set_altinterface(hdev, 0); # ifndef WINDOWS char tbuff[256]; //piCoutObj << "Check for bounded driver ... " << flush; if (usb_get_driver_np(hdev, intefrace_, tbuff, sizeof(tbuff) - 1) >= 0) { //piCoutObj << "yes" << "Found driver: " << tbuff; //piCoutObj << "Detach driver ... " << flush; if (usb_detach_kernel_driver_np(hdev, intefrace_)< 0) { piCoutObj << "Error: Cant`t detach bounded driver!"; return false; }// else piCoutObj << "ok"; }// else piCoutObj << "no"; # endif //piCoutObj << "Claim interface ... " << flush; if (usb_claim_interface(hdev, intefrace_) < 0) { piCoutObj << "Error: Cant`t claim interface:" << usb_strerror(); return false; } // else piCoutObj << "ok"; interface_claimed = intefrace_; return true; #else return false; #endif } bool PIUSB::closeDevice() { #ifdef PIP_USB if (hdev == 0) return true; usb_release_interface(hdev, intefrace_); usb_close(hdev); hdev = 0; interface_claimed = -1; return true; #else return false; #endif } ssize_t PIUSB::readDevice(void * read_to, ssize_t max_size) { #ifdef PIP_USB if (isClosed() || ep_read.isNull()) return -1; switch (ep_read.transfer_type) { case Endpoint::Bulk: /*piCoutObj << "bulk read" << max_size;*/ return usb_bulk_read(hdev, ep_read.address, (char * )read_to, max_size, timeout_r); case Endpoint::Interrupt: return usb_interrupt_read(hdev, ep_read.address, (char * )read_to, max_size, timeout_r); default: break; } return -1; #else return -1; #endif } ssize_t PIUSB::writeDevice(const void * data, ssize_t max_size) { #ifdef PIP_USB if (isClosed() || ep_write.isNull()) return -1; switch (ep_read.transfer_type) { case Endpoint::Bulk: /*piCoutObj << "bulk write" << max_size;*/ return usb_bulk_write(hdev, ep_write.address, (char * )const_cast(data), max_size, timeout_w); case Endpoint::Interrupt: return usb_interrupt_write(hdev, ep_read.address, (char * )data, max_size, timeout_w); default: break; } return -1; #else return -1; #endif } int PIUSB::controlWrite(const void * data, int max_size) { #ifdef PIP_USB if (isClosed()) return -1; return -1; #else return -1; #endif } void PIUSB::flush() { #ifdef PIP_USB if (isClosed()) return; if (!ep_read.isNull()) usb_resetep(hdev, ep_read.address); if (!ep_write.isNull()) usb_resetep(hdev, ep_write.address); #endif } PICout operator<<(PICout s, const PIUSB::Endpoint & v) { s.setControl(0, true); s << PICoutManipulators::NewLine << "{" << PICoutManipulators::NewLine; if (v.isNull()) s << " " << "Null Endpoint"; else { s << " " << "Address: " << v.address << PICoutManipulators::NewLine; s << " " << "Attributes: " << v.attributes << PICoutManipulators::NewLine; s << " " << "Direction: " << (v.direction == PIUSB::Endpoint::Write ? "Write" : "Read") << PICoutManipulators::NewLine; s << " " << "Transfer Type: "; switch (v.transfer_type) { case PIUSB::Endpoint::Control: s << "Control" << PICoutManipulators::NewLine; break; case PIUSB::Endpoint::Bulk: s << "Bulk" << PICoutManipulators::NewLine; break; case PIUSB::Endpoint::Interrupt: s << "Interrupt" << PICoutManipulators::NewLine; break; case PIUSB::Endpoint::Isochronous: s << "Isochronous" << PICoutManipulators::NewLine; break; default: break; } if (v.transfer_type == PIUSB::Endpoint::Isochronous) { s << " " << "Synchronisation Type: "; switch (v.synchronisation_type) { case PIUSB::Endpoint::NoSynchonisation: s << "No Synchonisation" << PICoutManipulators::NewLine; break; case PIUSB::Endpoint::Asynchronous: s << "Asynchronous" << PICoutManipulators::NewLine; break; case PIUSB::Endpoint::Adaptive: s << "Adaptive" << PICoutManipulators::NewLine; break; case PIUSB::Endpoint::Synchronous: s << "Synchronous" << PICoutManipulators::NewLine; break; default: break; } s << " " << "Usage Type: "; switch (v.usage_type) { case PIUSB::Endpoint::DataEndpoint: s << "Data Endpoint" << PICoutManipulators::NewLine; break; case PIUSB::Endpoint::FeedbackEndpoint: s << "Feedback Endpoint" << PICoutManipulators::NewLine; break; case PIUSB::Endpoint::ExplicitFeedbackDataEndpoint: s << "Explicit Feedback Data Endpoint" << PICoutManipulators::NewLine; break; default: break; } } s << " " << "Max Packet Size: " << v.max_packet_size << PICoutManipulators::NewLine; } s << "}" << PICoutManipulators::NewLine; s.restoreControl(); return s; } PIString PIUSB::constructFullPathDevice() const { PIString ret; ret << PIString::fromNumber(vendorID(), 16).toLowerCase() << ":" << PIString::fromNumber(productID(), 16).toLowerCase() << ":" << deviceNumber() << ":" << endpointRead().address << ":" << endpointWrite().address; return ret; } void PIUSB::configureFromFullPathDevice(const PIString & full_path) { PIStringList pl = full_path.split(":"); for (int i = 0; i < pl.size_s(); ++i) { PIString p(pl[i]); switch (i) { case 0: setVendorID(p.toUShort(16)); break; case 1: setProductID(p.toUShort(16)); break; case 2: setDeviceNumber(p.toInt()); break; case 3: setEndpointRead(Endpoint(p.toInt())); break; case 4: setEndpointWrite(Endpoint(p.toInt())); break; } } }