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 116 && idx < config->CountInterfaces(); idx++) { 117 const BUSBInterface *interface = config->InterfaceAt(idx); 118 for (uint32 alt = 0; alt < interface->CountAlternates(); alt++) { 119 const BUSBInterface *alternate = interface->AlternateAt(alt); 120 if (alternate->Class() == PRINTER_INTERFACE_CLASS 121 && alternate->Subclass() == PRINTER_INTERFACE_SUBCLASS 122 && (alternate->Protocol() == PIT_UNIDIRECTIONAL 123 || alternate->Protocol() == PIT_BIDIRECTIONAL 124 || alternate->Protocol() == PIT_1284_4_COMPATIBLE)) { 125 // Found a usable Printer interface! 126 for (uint32 endpointIdx = 0; 127 endpointIdx < alternate->CountEndpoints(); 128 endpointIdx++) { 129 const BUSBEndpoint *endpoint = 130 alternate->EndpointAt(endpointIdx); 131 if (!endpoint->IsBulk()) 132 continue; 133 134 if (endpoint->IsInput()) 135 in = endpoint; 136 else if (endpoint->IsOutput()) 137 out = endpoint; 138 139 if (!in || !out) 140 continue; 141 142 printer = alternate; 143 ((BUSBInterface*)interface)->SetAlternate(alt); 144 break; 145 } 146 } 147 } 148 } 149 } 150 151 if (printer != NULL) { 152 // We found a working printer interface, lets determine a unique ID 153 // for it now, and a user identification for display in the Printers 154 // preference. 155 BString portId = dev->SerialNumberString(); 156 if (!portId.Length()) { 157 // No persistent unique ID available, use the vendor/product 158 // ID for now. This will be unique as long as no two similar 159 // devices are attached. 160 portId << dev->VendorID() << "/" << dev->ProductID(); 161 } 162 163 BString portName = dev->ManufacturerString(); 164 if (portName.Length()) 165 portName << " "; 166 portName << dev->ProductString(); 167 168 //TODO: Do we want to use usb.ids to find proper name if strings 169 // are not supplied by USB? 170 171 fPrinters.Put(portId.String(), new USBPrinter(portId, portName, 172 printer, in, out)); 173 } 174 175 return B_OK; 176 } 177 178 179 void 180 USBPrinterRoster::DeviceRemoved(BUSBDevice *dev) 181 { 182 PrinterMap::Iterator iterator = fPrinters.GetIterator(); 183 while (iterator.HasNext()) { 184 const PrinterMap::Entry& entry = iterator.Next(); 185 // If the device is in the list, remove it 186 if (entry.value->fInterface->Device() == dev) { 187 fPrinters.Remove(entry.key); 188 delete entry.value; 189 break; 190 } 191 } 192 } 193 194 195 status_t 196 USBPrinterRoster::ListPrinters(BMessage *msg) 197 { 198 PrinterMap::Iterator iterator = fPrinters.GetIterator(); 199 while (iterator.HasNext()) { 200 const PrinterMap::Entry& entry = iterator.Next(); 201 msg->AddString("port_id", entry.value->fID); 202 msg->AddString("port_name", entry.value->fName); 203 } 204 205 return B_OK; 206 } 207 208 209 USBPrinter::USBPrinter(const BString& id, const BString& name, 210 const BUSBInterface *intf, const BUSBEndpoint *in, const BUSBEndpoint *out) 211 : fInterface(intf), fOut(out), fIn(in), fName(name), fID(id) 212 { 213 } 214 215 216 //TODO: see usb_printer.cpp for error handling during read/write! 217 ssize_t 218 USBPrinter::Write(const void *buf, size_t size) 219 { 220 if (!buf || size <= 0) 221 return B_BAD_VALUE; 222 223 // NOTE: we can safely cast below as we're sending data _out_ 224 return fOut->BulkTransfer((void*)buf, size); 225 } 226 227 228 ssize_t 229 USBPrinter::Read(void *buf, size_t size) 230 { 231 if (!buf || size <= 0) 232 return B_BAD_VALUE; 233 234 return fIn->BulkTransfer(buf, size); 235 } 236 237 238 // Implementation of transport add-on interface 239 240 BDataIO * 241 instantiate_transport(BDirectory *printer, BMessage *msg) 242 { 243 USBTransport *transport = new(std::nothrow) USBTransport(printer, msg); 244 if (transport != NULL && transport->InitCheck() == B_OK) 245 return transport; 246 247 delete transport; 248 return NULL; 249 } 250 251 252 // List detected printers 253 status_t 254 list_transport_ports(BMessage *msg) 255 { 256 USBPrinterRoster roster; 257 status_t status = roster.ListPrinters(msg); 258 roster.Stop(); 259 return status; 260 } 261 262 263 // Implementation of USBTransport 264 USBTransport::USBTransport(BDirectory *printer, BMessage *msg) 265 : fPrinter(NULL) 266 { 267 BString key; 268 269 if (printer->ReadAttrString("transport_address", &key) != B_OK) 270 return; 271 272 fRoster = new(std::nothrow) USBPrinterRoster; 273 if (fRoster == NULL) 274 return; 275 276 fPrinter = fRoster->Printer(key.String()); 277 if (fPrinter == NULL) 278 return; 279 280 // If caller doesn't care... 281 if (msg == NULL) 282 return; 283 284 // Fill up the message 285 msg->what = 'okok'; 286 } 287 288 289 USBTransport::~USBTransport() 290 { 291 if (fRoster != NULL) { 292 fRoster->Stop(); 293 delete fRoster; 294 } 295 } 296 297 298 ssize_t 299 USBTransport::Read(void *buffer, size_t size) 300 { 301 return fPrinter ? fPrinter->Read(buffer, size) : B_ERROR; 302 } 303 304 305 ssize_t 306 USBTransport::Write(const void *buffer, size_t size) 307 { 308 return fPrinter ? fPrinter->Write(buffer, size) : B_ERROR; 309 } 310