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
38class USBPrinter {
39public:
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
54class USBPrinterRoster : public BUSBRoster {
55public:
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);
64private:
65	typedef HashMap<HashString,USBPrinter*> PrinterMap;
66	PrinterMap fPrinters;
67};
68
69
70class USBTransport : public BDataIO {
71public:
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
80private:
81	USBPrinter *fPrinter;
82	USBPrinterRoster *fRoster;
83};
84
85
86// Set transport_features so we stay loaded
87uint32 transport_features = B_TRANSPORT_IS_HOTPLUG;
88
89
90USBPrinterRoster::USBPrinterRoster()
91{
92	Start();
93}
94
95
96USBPrinter *
97USBPrinterRoster::Printer(const BString& key)
98{
99	if (fPrinters.ContainsKey(key.String()))
100		return fPrinters.Get(key.String());
101
102	return NULL;
103}
104
105
106status_t
107USBPrinterRoster::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
179void
180USBPrinterRoster::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
195status_t
196USBPrinterRoster::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
209USBPrinter::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!
217ssize_t
218USBPrinter::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
228ssize_t
229USBPrinter::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
240BDataIO *
241instantiate_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
253status_t
254list_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
264USBTransport::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
289USBTransport::~USBTransport()
290{
291	if (fRoster != NULL) {
292		fRoster->Stop();
293		delete fRoster;
294	}
295}
296
297
298ssize_t
299USBTransport::Read(void *buffer, size_t size)
300{
301	return fPrinter ? fPrinter->Read(buffer, size) : B_ERROR;
302}
303
304
305ssize_t
306USBTransport::Write(const void *buffer, size_t size)
307{
308	return fPrinter ? fPrinter->Write(buffer, size) : B_ERROR;
309}
310