xref: /haiku/src/add-ons/print/transports/usb_port/USBTransport.cpp (revision f0ae1d0c2cda664cdb1271ca88b40a92715c4e18)
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 {
37 public:
38 	USBPrinter(const BString& id, const BString& name,
39 		const BUSBInterface *interface, const BUSBEndpoint* in, const BUSBEndpoint* out);
40 
41 	ssize_t Write(const void *buf, size_t size);
42 	ssize_t Read(void *buf, size_t size);
43 
44 	const BUSBInterface	*fInterface;
45 	const BUSBEndpoint	*fOut;
46 	const BUSBEndpoint	*fIn;
47 	BString			fName;
48 	BString			fID;
49 };
50 
51 
52 class USBPrinterRoster : public BUSBRoster {
53 public:
54 	USBPrinterRoster();
55 
56 	status_t DeviceAdded(BUSBDevice* dev);
57 	void DeviceRemoved(BUSBDevice* dev);
58 
59 	USBPrinter *Printer(const BString& key);
60 
61 	status_t ListPrinters(BMessage *msg);
62 private:
63 	typedef HashMap<HashString,USBPrinter*> PrinterMap;
64 	PrinterMap fPrinters;
65 };
66 
67 
68 class USBTransport : public BDataIO
69 {
70 public:
71 	USBTransport(BDirectory *printer, BMessage *msg);
72 	~USBTransport();
73 
74 	status_t InitCheck() { return fPrinter ? B_OK : B_ERROR; };
75 
76 	ssize_t Read(void *buffer, size_t size);
77 	ssize_t Write(const void *buffer, size_t size);
78 
79 private:
80 	USBPrinter *fPrinter;
81 };
82 
83 
84 // Set transport_features so we stay loaded
85 uint32 transport_features = B_TRANSPORT_IS_HOTPLUG;
86 
87 USBPrinterRoster gUSBPrinterRoster;
88 
89 
90 USBPrinterRoster::USBPrinterRoster()
91 {
92 	Start();
93 }
94 
95 
96 USBPrinter *USBPrinterRoster::Printer(const BString& key)
97 {
98 	if (fPrinters.ContainsKey(key.String()))
99 		return fPrinters.Get(key.String());
100 
101 	return NULL;
102 }
103 
104 
105 status_t 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 USBPrinterRoster::DeviceRemoved(BUSBDevice* dev)
170 {
171 	PrinterMap::Iterator iterator = fPrinters.GetIterator();
172 	while (iterator.HasNext()) {
173 		const PrinterMap::Entry& entry = iterator.Next();
174 		// If the device is in the list, remove it
175 		if (entry.value->fInterface->Device() == dev) {
176 			fPrinters.Remove(entry.key);
177 			delete entry.value;
178 			break;
179 		}
180 	}
181 }
182 
183 
184 status_t USBPrinterRoster::ListPrinters(BMessage* msg)
185 {
186 	PrinterMap::Iterator iterator = fPrinters.GetIterator();
187 	while (iterator.HasNext()) {
188 		const PrinterMap::Entry& entry = iterator.Next();
189 		msg->AddString("port_id", entry.value->fID);
190 		msg->AddString("port_name", entry.value->fName);
191 	}
192 
193 	return B_OK;
194 }
195 
196 
197 USBPrinter::USBPrinter(const BString& id, const BString& name,
198 	const BUSBInterface *intf, const BUSBEndpoint* in, const BUSBEndpoint* out)
199 	: fInterface(intf), fOut(out), fIn(in), fName(name), fID(id)
200 {
201 }
202 
203 
204 //TODO: see usb_printer.cpp for error handling during read/write!
205 ssize_t USBPrinter::Write(const void *buf, size_t size)
206 {
207 	if (!buf || size <= 0)
208 		return B_BAD_VALUE;
209 
210 	// NOTE: we can safely cast below as we're sending data _out_
211 	return fOut->BulkTransfer((void*)buf, size);
212 }
213 
214 
215 ssize_t USBPrinter::Read(void *buf, size_t size)
216 {
217 	if (!buf || size <= 0)
218 		return B_BAD_VALUE;
219 
220 	return fIn->BulkTransfer(buf, size);
221 }
222 
223 
224 // Implementation of transport add-on interface
225 
226 BDataIO *
227 instantiate_transport(BDirectory *printer, BMessage *msg)
228 {
229 	USBTransport * transport = new USBTransport(printer, msg);
230 	if (transport->InitCheck() == B_OK)
231 		return transport;
232 
233 	delete transport;
234 	return NULL;
235 }
236 
237 
238 // List detected printers
239 status_t list_transport_ports(BMessage* msg)
240 {
241 	return gUSBPrinterRoster.ListPrinters(msg);
242 }
243 
244 
245 // Implementation of USBTransport
246 USBTransport::USBTransport(BDirectory *printer, BMessage *msg)
247 	: fPrinter(NULL)
248 {
249 	BString key;
250 
251 	if (printer->ReadAttrString("transport_address", &key) < 0)
252 		return;
253 
254 	fPrinter = gUSBPrinterRoster.Printer(key.String());
255 	if (!fPrinter)
256 		return;
257 
258 	// If caller doesn't care...
259 	if (!msg)
260 		return;
261 
262 	// Fill up the message
263 	msg->what = 'okok';
264 }
265 
266 
267 USBTransport::~USBTransport()
268 {
269 }
270 
271 
272 ssize_t
273 USBTransport::Read(void *buffer, size_t size)
274 {
275 	return fPrinter ? fPrinter->Read(buffer, size) : B_ERROR;
276 }
277 
278 
279 ssize_t
280 USBTransport::Write(const void *buffer, size_t size)
281 {
282 	return fPrinter ? fPrinter->Write(buffer, size) : B_ERROR;
283 }
284