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