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