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