1 /* 2 * Copyright 2001-2010 Haiku Inc. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Originally written based on Parallel port addon by Michael Pfeiffer, 6 * changes by Andreas Benzler, Philippe Houdoin 7 * Rewritten to use the USBKit by Ithamar R. Adema. 8 * (Using code from usb_printer.cpp by Michael Lotz) 9 * 10 * Authors: 11 * Ithamar R. Adema, <ithamar.adema@team-embedded.nl> 12 * Michael Pfeiffer, 13 * Andreas Benzler, 14 * Philippe Houdoin, 15 */ 16 17 #include "PrintTransportAddOn.h" 18 19 #include <USBKit.h> 20 #include <String.h> 21 22 #include <HashString.h> 23 #include <HashMap.h> 24 25 #define PRINTER_INTERFACE_CLASS 0x07 26 #define PRINTER_INTERFACE_SUBCLASS 0x01 27 28 // printer interface types 29 #define PIT_UNIDIRECTIONAL 0x01 30 #define PIT_BIDIRECTIONAL 0x02 31 #define PIT_1284_4_COMPATIBLE 0x03 32 #define PIT_VENDOR_SPECIFIC 0xff 33 34 35 class USBPrinter 36 { 37 public: 38 USBPrinter(const BString& id, const BString& name, 39 const BUSBInterface *interface, const BUSBEndpoint* in, const BUSBEndpoint* out); 40 41 ssize_t Write(const void *buf, size_t size); 42 ssize_t Read(void *buf, size_t size); 43 44 const BUSBInterface *fInterface; 45 const BUSBEndpoint *fOut; 46 const BUSBEndpoint *fIn; 47 BString fName; 48 BString fID; 49 }; 50 51 52 class USBPrinterRoster : public BUSBRoster { 53 public: 54 USBPrinterRoster(); 55 56 status_t DeviceAdded(BUSBDevice* dev); 57 void DeviceRemoved(BUSBDevice* dev); 58 59 USBPrinter *Printer(const BString& key); 60 61 status_t ListPrinters(BMessage *msg); 62 private: 63 typedef HashMap<HashString,USBPrinter*> PrinterMap; 64 PrinterMap fPrinters; 65 }; 66 67 68 class USBTransport : public BDataIO 69 { 70 public: 71 USBTransport(BDirectory *printer, BMessage *msg); 72 ~USBTransport(); 73 74 status_t InitCheck() { return fPrinter ? B_OK : B_ERROR; }; 75 76 ssize_t Read(void *buffer, size_t size); 77 ssize_t Write(const void *buffer, size_t size); 78 79 private: 80 USBPrinter *fPrinter; 81 }; 82 83 84 // Set transport_features so we stay loaded 85 uint32 transport_features = B_TRANSPORT_IS_HOTPLUG; 86 87 USBPrinterRoster gUSBPrinterRoster; 88 89 90 USBPrinterRoster::USBPrinterRoster() 91 { 92 Start(); 93 } 94 95 96 USBPrinter *USBPrinterRoster::Printer(const BString& key) 97 { 98 if (fPrinters.ContainsKey(key.String())) 99 return fPrinters.Get(key.String()); 100 101 return NULL; 102 } 103 104 105 status_t USBPrinterRoster::DeviceAdded(BUSBDevice* dev) 106 { 107 const BUSBConfiguration *config = dev->ActiveConfiguration(); 108 const BUSBEndpoint *in = NULL, *out = NULL; 109 const BUSBInterface *printer = NULL; 110 111 // Try to find a working printer interface in this device 112 if (config) { 113 for (uint32 idx = 0; printer == NULL && idx < config->CountInterfaces(); idx++) { 114 const BUSBInterface *interface = config->InterfaceAt(idx); 115 if (interface->Class() == PRINTER_INTERFACE_CLASS && 116 interface->Subclass() == PRINTER_INTERFACE_SUBCLASS && 117 (interface->Protocol() == PIT_UNIDIRECTIONAL || 118 interface->Protocol() == PIT_BIDIRECTIONAL || 119 interface->Protocol() == PIT_1284_4_COMPATIBLE)) { 120 // Found a usable Printer interface! 121 for (uint32 endpointIdx = 0; endpointIdx < interface->CountEndpoints(); endpointIdx++) { 122 const BUSBEndpoint *endpoint = interface->EndpointAt(endpointIdx); 123 if (!endpoint->IsBulk()) 124 continue; 125 126 if (endpoint->IsInput()) 127 in = endpoint; 128 else if (endpoint->IsOutput()) 129 out = endpoint; 130 131 if (!in || !out) 132 continue; 133 134 printer = interface; 135 break; 136 } 137 } 138 } 139 } 140 141 if (printer != NULL) { 142 // We found a working printer interface, lets determine a unique ID 143 // for it now, and a user identification for display in the Printers 144 // preference. 145 BString portId = dev->SerialNumberString(); 146 if (!portId.Length()) { 147 // No persistent unique ID available, use the vendor/product 148 // ID for now. This will be unique as long as no two similar 149 // devices are attached. 150 portId << dev->VendorID() << "/" << dev->ProductID(); 151 } 152 153 BString portName = dev->ManufacturerString(); 154 if (portName.Length()) 155 portName << " "; 156 portName << dev->ProductString(); 157 158 //TODO: Do we want to use usb.ids to find proper name if strings 159 // are not supplied by USB? 160 161 fPrinters.Put(portId.String(), new USBPrinter(portId, portName, 162 printer, in, out)); 163 } 164 165 return B_OK; 166 } 167 168 169 void USBPrinterRoster::DeviceRemoved(BUSBDevice* dev) 170 { 171 PrinterMap::Iterator iterator = fPrinters.GetIterator(); 172 while (iterator.HasNext()) { 173 const PrinterMap::Entry& entry = iterator.Next(); 174 // If the device is in the list, remove it 175 if (entry.value->fInterface->Device() == dev) { 176 fPrinters.Remove(entry.key); 177 delete entry.value; 178 break; 179 } 180 } 181 } 182 183 184 status_t USBPrinterRoster::ListPrinters(BMessage* msg) 185 { 186 PrinterMap::Iterator iterator = fPrinters.GetIterator(); 187 while (iterator.HasNext()) { 188 const PrinterMap::Entry& entry = iterator.Next(); 189 msg->AddString("port_id", entry.value->fID); 190 msg->AddString("port_name", entry.value->fName); 191 } 192 193 return B_OK; 194 } 195 196 197 USBPrinter::USBPrinter(const BString& id, const BString& name, 198 const BUSBInterface *intf, const BUSBEndpoint* in, const BUSBEndpoint* out) 199 : fInterface(intf), fOut(out), fIn(in), fName(name), fID(id) 200 { 201 } 202 203 204 //TODO: see usb_printer.cpp for error handling during read/write! 205 ssize_t USBPrinter::Write(const void *buf, size_t size) 206 { 207 if (!buf || size <= 0) 208 return B_BAD_VALUE; 209 210 // NOTE: we can safely cast below as we're sending data _out_ 211 return fOut->BulkTransfer((void*)buf, size); 212 } 213 214 215 ssize_t USBPrinter::Read(void *buf, size_t size) 216 { 217 if (!buf || size <= 0) 218 return B_BAD_VALUE; 219 220 return fIn->BulkTransfer(buf, size); 221 } 222 223 224 // Implementation of transport add-on interface 225 226 BDataIO * 227 instantiate_transport(BDirectory *printer, BMessage *msg) 228 { 229 USBTransport * transport = new USBTransport(printer, msg); 230 if (transport->InitCheck() == B_OK) 231 return transport; 232 233 delete transport; 234 return NULL; 235 } 236 237 238 // List detected printers 239 status_t list_transport_ports(BMessage* msg) 240 { 241 return gUSBPrinterRoster.ListPrinters(msg); 242 } 243 244 245 // Implementation of USBTransport 246 USBTransport::USBTransport(BDirectory *printer, BMessage *msg) 247 : fPrinter(NULL) 248 { 249 BString key; 250 251 if (printer->ReadAttrString("transport_address", &key) < 0) 252 return; 253 254 fPrinter = gUSBPrinterRoster.Printer(key.String()); 255 if (!fPrinter) 256 return; 257 258 // If caller doesn't care... 259 if (!msg) 260 return; 261 262 // Fill up the message 263 msg->what = 'okok'; 264 } 265 266 267 USBTransport::~USBTransport() 268 { 269 } 270 271 272 ssize_t 273 USBTransport::Read(void *buffer, size_t size) 274 { 275 return fPrinter ? fPrinter->Read(buffer, size) : B_ERROR; 276 } 277 278 279 ssize_t 280 USBTransport::Write(const void *buffer, size_t size) 281 { 282 return fPrinter ? fPrinter->Write(buffer, size) : B_ERROR; 283 } 284