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