xref: /haiku/src/add-ons/print/transports/usb_port/USBTransport.cpp (revision b6e09fad7a02dd9e280a68ea1218ee493f0b7099)
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