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
InitCheck()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
USBPrinterRoster()90 USBPrinterRoster::USBPrinterRoster()
91 {
92 Start();
93 }
94
95
96 USBPrinter *
Printer(const BString & key)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
DeviceAdded(BUSBDevice * dev)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
DeviceRemoved(BUSBDevice * dev)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
ListPrinters(BMessage * msg)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
USBPrinter(const BString & id,const BString & name,const BUSBInterface * intf,const BUSBEndpoint * in,const BUSBEndpoint * out)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
Write(const void * buf,size_t size)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
Read(void * buf,size_t size)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 *
instantiate_transport(BDirectory * printer,BMessage * msg)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
list_transport_ports(BMessage * msg)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
USBTransport(BDirectory * printer,BMessage * msg)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
~USBTransport()289 USBTransport::~USBTransport()
290 {
291 if (fRoster != NULL) {
292 fRoster->Stop();
293 delete fRoster;
294 }
295 }
296
297
298 ssize_t
Read(void * buffer,size_t size)299 USBTransport::Read(void *buffer, size_t size)
300 {
301 return fPrinter ? fPrinter->Read(buffer, size) : B_ERROR;
302 }
303
304
305 ssize_t
Write(const void * buffer,size_t size)306 USBTransport::Write(const void *buffer, size_t size)
307 {
308 return fPrinter ? fPrinter->Write(buffer, size) : B_ERROR;
309 }
310