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 public: 37 USBPrinter(const BString& id, const BString& name, 38 const BUSBInterface *interface, const BUSBEndpoint *in, const BUSBEndpoint *out); 39 40 ssize_t Write(const void *buf, size_t size); 41 ssize_t Read(void *buf, size_t size); 42 43 const BUSBInterface *fInterface; 44 const BUSBEndpoint *fOut; 45 const BUSBEndpoint *fIn; 46 BString fName; 47 BString fID; 48 }; 49 50 51 class USBPrinterRoster : public BUSBRoster { 52 public: 53 USBPrinterRoster(); 54 55 status_t DeviceAdded(BUSBDevice *dev); 56 void DeviceRemoved(BUSBDevice *dev); 57 58 USBPrinter *Printer(const BString& key); 59 60 status_t ListPrinters(BMessage *msg); 61 private: 62 typedef HashMap<HashString,USBPrinter*> PrinterMap; 63 PrinterMap fPrinters; 64 }; 65 66 67 class USBTransport : public BDataIO { 68 public: 69 USBTransport(BDirectory *printer, BMessage *msg); 70 ~USBTransport(); 71 72 status_t InitCheck() { return fPrinter ? B_OK : B_ERROR; }; 73 74 ssize_t Read(void *buffer, size_t size); 75 ssize_t Write(const void *buffer, size_t size); 76 77 private: 78 USBPrinter *fPrinter; 79 }; 80 81 82 // Set transport_features so we stay loaded 83 uint32 transport_features = B_TRANSPORT_IS_HOTPLUG; 84 85 USBPrinterRoster gUSBPrinterRoster; 86 87 88 USBPrinterRoster::USBPrinterRoster() 89 { 90 Start(); 91 } 92 93 94 USBPrinter * 95 USBPrinterRoster::Printer(const BString& key) 96 { 97 if (fPrinters.ContainsKey(key.String())) 98 return fPrinters.Get(key.String()); 99 100 return NULL; 101 } 102 103 104 status_t 105 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 170 USBPrinterRoster::DeviceRemoved(BUSBDevice* dev) 171 { 172 PrinterMap::Iterator iterator = fPrinters.GetIterator(); 173 while (iterator.HasNext()) { 174 const PrinterMap::Entry& entry = iterator.Next(); 175 // If the device is in the list, remove it 176 if (entry.value->fInterface->Device() == dev) { 177 fPrinters.Remove(entry.key); 178 delete entry.value; 179 break; 180 } 181 } 182 } 183 184 185 status_t 186 USBPrinterRoster::ListPrinters(BMessage* msg) 187 { 188 PrinterMap::Iterator iterator = fPrinters.GetIterator(); 189 while (iterator.HasNext()) { 190 const PrinterMap::Entry& entry = iterator.Next(); 191 msg->AddString("port_id", entry.value->fID); 192 msg->AddString("port_name", entry.value->fName); 193 } 194 195 return B_OK; 196 } 197 198 199 USBPrinter::USBPrinter(const BString& id, const BString& name, 200 const BUSBInterface *intf, const BUSBEndpoint *in, const BUSBEndpoint *out) 201 : fInterface(intf), fOut(out), fIn(in), fName(name), fID(id) 202 { 203 } 204 205 206 //TODO: see usb_printer.cpp for error handling during read/write! 207 ssize_t 208 USBPrinter::Write(const void *buf, size_t size) 209 { 210 if (!buf || size <= 0) 211 return B_BAD_VALUE; 212 213 // NOTE: we can safely cast below as we're sending data _out_ 214 return fOut->BulkTransfer((void*)buf, size); 215 } 216 217 218 ssize_t 219 USBPrinter::Read(void *buf, size_t size) 220 { 221 if (!buf || size <= 0) 222 return B_BAD_VALUE; 223 224 return fIn->BulkTransfer(buf, size); 225 } 226 227 228 // Implementation of transport add-on interface 229 230 BDataIO * 231 instantiate_transport(BDirectory *printer, BMessage *msg) 232 { 233 USBTransport * transport = new USBTransport(printer, msg); 234 if (transport->InitCheck() == B_OK) 235 return transport; 236 237 delete transport; 238 return NULL; 239 } 240 241 242 // List detected printers 243 status_t 244 list_transport_ports(BMessage* msg) 245 { 246 return gUSBPrinterRoster.ListPrinters(msg); 247 } 248 249 250 // Implementation of USBTransport 251 USBTransport::USBTransport(BDirectory *printer, BMessage *msg) 252 : fPrinter(NULL) 253 { 254 BString key; 255 256 if (printer->ReadAttrString("transport_address", &key) < 0) 257 return; 258 259 fPrinter = gUSBPrinterRoster.Printer(key.String()); 260 if (!fPrinter) 261 return; 262 263 // If caller doesn't care... 264 if (!msg) 265 return; 266 267 // Fill up the message 268 msg->what = 'okok'; 269 } 270 271 272 USBTransport::~USBTransport() 273 { 274 } 275 276 277 ssize_t 278 USBTransport::Read(void *buffer, size_t size) 279 { 280 return fPrinter ? fPrinter->Read(buffer, size) : B_ERROR; 281 } 282 283 284 ssize_t 285 USBTransport::Write(const void *buffer, size_t size) 286 { 287 return fPrinter ? fPrinter->Write(buffer, size) : B_ERROR; 288 } 289