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 // TODO handle disconnection of printer during printing 36 // currently the USBPrinter will be deleted and USBTransport will still 37 // access the memory 38 class USBPrinter { 39 public: 40 USBPrinter(const BString& id, const BString& name, 41 const BUSBInterface *interface, const BUSBEndpoint *in, const BUSBEndpoint *out); 42 43 ssize_t Write(const void *buf, size_t size); 44 ssize_t Read(void *buf, size_t size); 45 46 const BUSBInterface *fInterface; 47 const BUSBEndpoint *fOut; 48 const BUSBEndpoint *fIn; 49 BString fName; 50 BString fID; 51 }; 52 53 54 class USBPrinterRoster : public BUSBRoster { 55 public: 56 USBPrinterRoster(); 57 58 status_t DeviceAdded(BUSBDevice *dev); 59 void DeviceRemoved(BUSBDevice *dev); 60 61 USBPrinter *Printer(const BString& key); 62 63 status_t ListPrinters(BMessage *msg); 64 private: 65 typedef HashMap<HashString,USBPrinter*> PrinterMap; 66 PrinterMap fPrinters; 67 }; 68 69 70 class USBTransport : public BDataIO { 71 public: 72 USBTransport(BDirectory *printer, BMessage *msg); 73 ~USBTransport(); 74 75 status_t InitCheck() { return fPrinter ? B_OK : B_ERROR; }; 76 77 ssize_t Read(void *buffer, size_t size); 78 ssize_t Write(const void *buffer, size_t size); 79 80 private: 81 USBPrinter *fPrinter; 82 USBPrinterRoster *fRoster; 83 }; 84 85 86 // Set transport_features so we stay loaded 87 uint32 transport_features = B_TRANSPORT_IS_HOTPLUG; 88 89 90 USBPrinterRoster::USBPrinterRoster() 91 { 92 Start(); 93 } 94 95 96 USBPrinter * 97 USBPrinterRoster::Printer(const BString& key) 98 { 99 if (fPrinters.ContainsKey(key.String())) 100 return fPrinters.Get(key.String()); 101 102 return NULL; 103 } 104 105 106 status_t 107 USBPrinterRoster::DeviceAdded(BUSBDevice *dev) 108 { 109 const BUSBConfiguration *config = dev->ActiveConfiguration(); 110 const BUSBEndpoint *in = NULL, *out = NULL; 111 const BUSBInterface *printer = NULL; 112 113 // Try to find a working printer interface in this device 114 if (config) { 115 for (uint32 idx = 0; printer == NULL && idx < config->CountInterfaces(); idx++) { 116 const BUSBInterface *interface = config->InterfaceAt(idx); 117 if (interface->Class() == PRINTER_INTERFACE_CLASS 118 && interface->Subclass() == PRINTER_INTERFACE_SUBCLASS 119 && (interface->Protocol() == PIT_UNIDIRECTIONAL 120 || interface->Protocol() == PIT_BIDIRECTIONAL 121 || interface->Protocol() == PIT_1284_4_COMPATIBLE)) { 122 // Found a usable Printer interface! 123 for (uint32 endpointIdx = 0; endpointIdx < interface->CountEndpoints(); endpointIdx++) { 124 const BUSBEndpoint *endpoint = interface->EndpointAt(endpointIdx); 125 if (!endpoint->IsBulk()) 126 continue; 127 128 if (endpoint->IsInput()) 129 in = endpoint; 130 else if (endpoint->IsOutput()) 131 out = endpoint; 132 133 if (!in || !out) 134 continue; 135 136 printer = interface; 137 break; 138 } 139 } 140 } 141 } 142 143 if (printer != NULL) { 144 // We found a working printer interface, lets determine a unique ID 145 // for it now, and a user identification for display in the Printers 146 // preference. 147 BString portId = dev->SerialNumberString(); 148 if (!portId.Length()) { 149 // No persistent unique ID available, use the vendor/product 150 // ID for now. This will be unique as long as no two similar 151 // devices are attached. 152 portId << dev->VendorID() << "/" << dev->ProductID(); 153 } 154 155 BString portName = dev->ManufacturerString(); 156 if (portName.Length()) 157 portName << " "; 158 portName << dev->ProductString(); 159 160 //TODO: Do we want to use usb.ids to find proper name if strings 161 // are not supplied by USB? 162 163 fPrinters.Put(portId.String(), new USBPrinter(portId, portName, 164 printer, in, out)); 165 } 166 167 return B_OK; 168 } 169 170 171 void 172 USBPrinterRoster::DeviceRemoved(BUSBDevice *dev) 173 { 174 PrinterMap::Iterator iterator = fPrinters.GetIterator(); 175 while (iterator.HasNext()) { 176 const PrinterMap::Entry& entry = iterator.Next(); 177 // If the device is in the list, remove it 178 if (entry.value->fInterface->Device() == dev) { 179 fPrinters.Remove(entry.key); 180 delete entry.value; 181 break; 182 } 183 } 184 } 185 186 187 status_t 188 USBPrinterRoster::ListPrinters(BMessage *msg) 189 { 190 PrinterMap::Iterator iterator = fPrinters.GetIterator(); 191 while (iterator.HasNext()) { 192 const PrinterMap::Entry& entry = iterator.Next(); 193 msg->AddString("port_id", entry.value->fID); 194 msg->AddString("port_name", entry.value->fName); 195 } 196 197 return B_OK; 198 } 199 200 201 USBPrinter::USBPrinter(const BString& id, const BString& name, 202 const BUSBInterface *intf, const BUSBEndpoint *in, const BUSBEndpoint *out) 203 : fInterface(intf), fOut(out), fIn(in), fName(name), fID(id) 204 { 205 } 206 207 208 //TODO: see usb_printer.cpp for error handling during read/write! 209 ssize_t 210 USBPrinter::Write(const void *buf, size_t size) 211 { 212 if (!buf || size <= 0) 213 return B_BAD_VALUE; 214 215 // NOTE: we can safely cast below as we're sending data _out_ 216 return fOut->BulkTransfer((void*)buf, size); 217 } 218 219 220 ssize_t 221 USBPrinter::Read(void *buf, size_t size) 222 { 223 if (!buf || size <= 0) 224 return B_BAD_VALUE; 225 226 return fIn->BulkTransfer(buf, size); 227 } 228 229 230 // Implementation of transport add-on interface 231 232 BDataIO * 233 instantiate_transport(BDirectory *printer, BMessage *msg) 234 { 235 USBTransport *transport = new(std::nothrow) USBTransport(printer, msg); 236 if (transport != NULL && transport->InitCheck() == B_OK) 237 return transport; 238 239 delete transport; 240 return NULL; 241 } 242 243 244 // List detected printers 245 status_t 246 list_transport_ports(BMessage *msg) 247 { 248 USBPrinterRoster roster; 249 status_t status = roster.ListPrinters(msg); 250 roster.Stop(); 251 return status; 252 } 253 254 255 // Implementation of USBTransport 256 USBTransport::USBTransport(BDirectory *printer, BMessage *msg) 257 : fPrinter(NULL) 258 { 259 BString key; 260 261 if (printer->ReadAttrString("transport_address", &key) != B_OK) 262 return; 263 264 fRoster = new(std::nothrow) USBPrinterRoster; 265 if (fRoster == NULL) 266 return; 267 268 fPrinter = fRoster->Printer(key.String()); 269 if (fPrinter == NULL) 270 return; 271 272 // If caller doesn't care... 273 if (msg == NULL) 274 return; 275 276 // Fill up the message 277 msg->what = 'okok'; 278 } 279 280 281 USBTransport::~USBTransport() 282 { 283 if (fRoster != NULL) { 284 fRoster->Stop(); 285 delete fRoster; 286 } 287 } 288 289 290 ssize_t 291 USBTransport::Read(void *buffer, size_t size) 292 { 293 return fPrinter ? fPrinter->Read(buffer, size) : B_ERROR; 294 } 295 296 297 ssize_t 298 USBTransport::Write(const void *buffer, size_t size) 299 { 300 return fPrinter ? fPrinter->Write(buffer, size) : B_ERROR; 301 } 302