1 | /* $NetBSD: uvisor.c,v 1.47 2016/07/07 06:55:42 msaitoh Exp $ */ |
2 | |
3 | /* |
4 | * Copyright (c) 2000 The NetBSD Foundation, Inc. |
5 | * All rights reserved. |
6 | * |
7 | * This code is derived from software contributed to The NetBSD Foundation |
8 | * by Lennart Augustsson (lennart@augustsson.net) at |
9 | * Carlstedt Research & Technology. |
10 | * |
11 | * Redistribution and use in source and binary forms, with or without |
12 | * modification, are permitted provided that the following conditions |
13 | * are met: |
14 | * 1. Redistributions of source code must retain the above copyright |
15 | * notice, this list of conditions and the following disclaimer. |
16 | * 2. Redistributions in binary form must reproduce the above copyright |
17 | * notice, this list of conditions and the following disclaimer in the |
18 | * documentation and/or other materials provided with the distribution. |
19 | * |
20 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS |
21 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
22 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
23 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS |
24 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
25 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
26 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
27 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
28 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
29 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
30 | * POSSIBILITY OF SUCH DAMAGE. |
31 | */ |
32 | |
33 | /* |
34 | * Handspring Visor (Palmpilot compatible PDA) driver |
35 | */ |
36 | |
37 | #include <sys/cdefs.h> |
38 | __KERNEL_RCSID(0, "$NetBSD: uvisor.c,v 1.47 2016/07/07 06:55:42 msaitoh Exp $" ); |
39 | |
40 | #include <sys/param.h> |
41 | #include <sys/systm.h> |
42 | #include <sys/kernel.h> |
43 | #include <sys/device.h> |
44 | #include <sys/conf.h> |
45 | #include <sys/tty.h> |
46 | |
47 | #include <dev/usb/usb.h> |
48 | |
49 | #include <dev/usb/usbdi.h> |
50 | #include <dev/usb/usbdi_util.h> |
51 | #include <dev/usb/usbdevs.h> |
52 | |
53 | #include <dev/usb/ucomvar.h> |
54 | |
55 | #ifdef UVISOR_DEBUG |
56 | #define DPRINTF(x) if (uvisordebug) printf x |
57 | #define DPRINTFN(n,x) if (uvisordebug>(n)) printf x |
58 | int uvisordebug = 0; |
59 | #else |
60 | #define DPRINTF(x) |
61 | #define DPRINTFN(n,x) |
62 | #endif |
63 | |
64 | #define UVISOR_CONFIG_INDEX 0 |
65 | #define UVISOR_IFACE_INDEX 0 |
66 | |
67 | /* From the Linux driver */ |
68 | /* |
69 | * UVISOR_REQUEST_BYTES_AVAILABLE asks the visor for the number of bytes that |
70 | * are available to be transfered to the host for the specified endpoint. |
71 | * Currently this is not used, and always returns 0x0001 |
72 | */ |
73 | #define UVISOR_REQUEST_BYTES_AVAILABLE 0x01 |
74 | |
75 | /* |
76 | * UVISOR_CLOSE_NOTIFICATION is set to the device to notify it that the host |
77 | * is now closing the pipe. An empty packet is sent in response. |
78 | */ |
79 | #define UVISOR_CLOSE_NOTIFICATION 0x02 |
80 | |
81 | /* |
82 | * UVISOR_GET_CONNECTION_INFORMATION is sent by the host during enumeration to |
83 | * get the endpoints used by the connection. |
84 | */ |
85 | #define UVISOR_GET_CONNECTION_INFORMATION 0x03 |
86 | |
87 | |
88 | /* |
89 | * UVISOR_GET_CONNECTION_INFORMATION returns data in the following format |
90 | */ |
91 | #define UVISOR_MAX_CONN 8 |
92 | struct uvisor_connection_info { |
93 | uWord num_ports; |
94 | struct { |
95 | uByte port_function_id; |
96 | uByte port; |
97 | } connections[UVISOR_MAX_CONN]; |
98 | }; |
99 | #define UVISOR_CONNECTION_INFO_SIZE 18 |
100 | |
101 | /* struct uvisor_connection_info.connection[x].port_function_id defines: */ |
102 | #define UVISOR_FUNCTION_GENERIC 0x00 |
103 | #define UVISOR_FUNCTION_DEBUGGER 0x01 |
104 | #define UVISOR_FUNCTION_HOTSYNC 0x02 |
105 | #define UVISOR_FUNCTION_CONSOLE 0x03 |
106 | #define UVISOR_FUNCTION_REMOTE_FILE_SYS 0x04 |
107 | |
108 | /* |
109 | * Unknown PalmOS stuff. |
110 | */ |
111 | #define UVISOR_GET_PALM_INFORMATION 0x04 |
112 | #define UVISOR_GET_PALM_INFORMATION_LEN 0x44 |
113 | |
114 | struct uvisor_palm_connection_info { |
115 | uByte num_ports; |
116 | uByte endpoint_numbers_different; |
117 | uWord reserved1; |
118 | struct { |
119 | uDWord port_function_id; |
120 | uByte port; |
121 | uByte end_point_info; |
122 | uWord reserved; |
123 | } connections[UVISOR_MAX_CONN]; |
124 | }; |
125 | |
126 | |
127 | |
128 | #define UVISORIBUFSIZE 64 |
129 | #define UVISOROBUFSIZE 1024 |
130 | |
131 | struct uvisor_softc { |
132 | device_t sc_dev; /* base device */ |
133 | struct usbd_device * sc_udev; /* device */ |
134 | struct usbd_interface * sc_iface; /* interface */ |
135 | |
136 | device_t sc_subdevs[UVISOR_MAX_CONN]; |
137 | int sc_numcon; |
138 | |
139 | uint16_t sc_flags; |
140 | |
141 | u_char sc_dying; |
142 | }; |
143 | |
144 | Static usbd_status uvisor_init(struct uvisor_softc *, |
145 | struct uvisor_connection_info *, |
146 | struct uvisor_palm_connection_info *); |
147 | |
148 | Static void uvisor_close(void *, int); |
149 | |
150 | |
151 | struct ucom_methods uvisor_methods = { |
152 | .ucom_param = NULL, |
153 | .ucom_ioctl = NULL, |
154 | .ucom_open = NULL, |
155 | .ucom_close = uvisor_close, |
156 | .ucom_read = NULL, |
157 | .ucom_write = NULL, |
158 | .ucom_get_status = NULL, |
159 | .ucom_set = NULL, |
160 | }; |
161 | |
162 | struct uvisor_type { |
163 | struct usb_devno uv_dev; |
164 | uint16_t uv_flags; |
165 | #define PALM4 0x0001 |
166 | #define VISOR 0x0002 |
167 | |
168 | }; |
169 | static const struct uvisor_type uvisor_devs[] = { |
170 | {{ USB_VENDOR_HANDSPRING, USB_PRODUCT_HANDSPRING_VISOR }, VISOR }, |
171 | {{ USB_VENDOR_HANDSPRING, USB_PRODUCT_HANDSPRING_TREO }, PALM4 }, |
172 | {{ USB_VENDOR_HANDSPRING, USB_PRODUCT_HANDSPRING_TREO600 }, PALM4 }, |
173 | {{ USB_VENDOR_PALM, USB_PRODUCT_PALM_M500 }, PALM4 }, |
174 | {{ USB_VENDOR_PALM, USB_PRODUCT_PALM_M505 }, PALM4 }, |
175 | {{ USB_VENDOR_PALM, USB_PRODUCT_PALM_M515 }, PALM4 }, |
176 | {{ USB_VENDOR_PALM, USB_PRODUCT_PALM_I705 }, PALM4 }, |
177 | {{ USB_VENDOR_PALM, USB_PRODUCT_PALM_M125 }, PALM4 }, |
178 | {{ USB_VENDOR_PALM, USB_PRODUCT_PALM_M130 }, PALM4 }, |
179 | {{ USB_VENDOR_PALM, USB_PRODUCT_PALM_TUNGSTEN_Z }, PALM4 }, |
180 | {{ USB_VENDOR_PALM, USB_PRODUCT_PALM_TUNGSTEN_T }, PALM4 }, |
181 | {{ USB_VENDOR_PALM, USB_PRODUCT_PALM_ZIRE31 }, PALM4 }, |
182 | {{ USB_VENDOR_PALM, USB_PRODUCT_PALM_ZIRE }, PALM4 }, |
183 | {{ USB_VENDOR_SONY, USB_PRODUCT_SONY_CLIE_40 }, PALM4 }, |
184 | {{ USB_VENDOR_SONY, USB_PRODUCT_SONY_CLIE_41 }, PALM4 }, |
185 | {{ USB_VENDOR_SONY, USB_PRODUCT_SONY_CLIE_S360 }, PALM4 }, |
186 | {{ USB_VENDOR_SONY, USB_PRODUCT_SONY_CLIE_NX60 }, PALM4 }, |
187 | {{ USB_VENDOR_SONY, USB_PRODUCT_SONY_CLIE_35 }, 0 }, |
188 | /* {{ USB_VENDOR_SONY, USB_PRODUCT_SONY_CLIE_25 }, PALM4 },*/ |
189 | }; |
190 | #define uvisor_lookup(v, p) ((const struct uvisor_type *)usb_lookup(uvisor_devs, v, p)) |
191 | |
192 | int uvisor_match(device_t, cfdata_t, void *); |
193 | void uvisor_attach(device_t, device_t, void *); |
194 | void uvisor_childdet(device_t, device_t); |
195 | int uvisor_detach(device_t, int); |
196 | int uvisor_activate(device_t, enum devact); |
197 | extern struct cfdriver uvisor_cd; |
198 | CFATTACH_DECL2_NEW(uvisor, sizeof(struct uvisor_softc), uvisor_match, |
199 | uvisor_attach, uvisor_detach, uvisor_activate, NULL, uvisor_childdet); |
200 | |
201 | int |
202 | uvisor_match(device_t parent, cfdata_t match, void *aux) |
203 | { |
204 | struct usb_attach_arg *uaa = aux; |
205 | |
206 | DPRINTFN(20,("uvisor: vendor=0x%x, product=0x%x\n" , |
207 | uaa->uaa_vendor, uaa->uaa_product)); |
208 | |
209 | return uvisor_lookup(uaa->uaa_vendor, uaa->uaa_product) != NULL ? |
210 | UMATCH_VENDOR_PRODUCT : UMATCH_NONE; |
211 | } |
212 | |
213 | void |
214 | uvisor_attach(device_t parent, device_t self, void *aux) |
215 | { |
216 | struct uvisor_softc *sc = device_private(self); |
217 | struct usb_attach_arg *uaa = aux; |
218 | struct usbd_device *dev = uaa->uaa_device; |
219 | struct usbd_interface *iface; |
220 | usb_interface_descriptor_t *id; |
221 | struct uvisor_connection_info coninfo; |
222 | struct uvisor_palm_connection_info palmconinfo; |
223 | usb_endpoint_descriptor_t *ed; |
224 | char *devinfop; |
225 | const char *devname = device_xname(self); |
226 | int i, j, hasin, hasout, port; |
227 | usbd_status err; |
228 | struct ucom_attach_args ucaa; |
229 | |
230 | DPRINTFN(10,("\nuvisor_attach: sc=%p\n" , sc)); |
231 | |
232 | sc->sc_dev = self; |
233 | |
234 | aprint_naive("\n" ); |
235 | aprint_normal("\n" ); |
236 | |
237 | devinfop = usbd_devinfo_alloc(dev, 0); |
238 | aprint_normal_dev(self, "%s\n" , devinfop); |
239 | usbd_devinfo_free(devinfop); |
240 | |
241 | /* Move the device into the configured state. */ |
242 | err = usbd_set_config_index(dev, UVISOR_CONFIG_INDEX, 1); |
243 | if (err) { |
244 | aprint_error("\n%s: failed to set configuration, err=%s\n" , |
245 | devname, usbd_errstr(err)); |
246 | goto bad; |
247 | } |
248 | |
249 | err = usbd_device2interface_handle(dev, UVISOR_IFACE_INDEX, &iface); |
250 | if (err) { |
251 | aprint_error("\n%s: failed to get interface, err=%s\n" , |
252 | devname, usbd_errstr(err)); |
253 | goto bad; |
254 | } |
255 | |
256 | sc->sc_flags = uvisor_lookup(uaa->uaa_vendor, uaa->uaa_product)->uv_flags; |
257 | |
258 | if ((sc->sc_flags & (VISOR | PALM4)) == 0) { |
259 | aprint_error_dev(self, |
260 | "init failed, device type is neither visor nor palm\n" ); |
261 | goto bad; |
262 | } |
263 | |
264 | id = usbd_get_interface_descriptor(iface); |
265 | |
266 | sc->sc_udev = dev; |
267 | sc->sc_iface = iface; |
268 | |
269 | ucaa.ucaa_ibufsize = UVISORIBUFSIZE; |
270 | ucaa.ucaa_obufsize = UVISOROBUFSIZE; |
271 | ucaa.ucaa_ibufsizepad = UVISORIBUFSIZE; |
272 | ucaa.ucaa_opkthdrlen = 0; |
273 | ucaa.ucaa_device = dev; |
274 | ucaa.ucaa_iface = iface; |
275 | ucaa.ucaa_methods = &uvisor_methods; |
276 | ucaa.ucaa_arg = sc; |
277 | |
278 | err = uvisor_init(sc, &coninfo, &palmconinfo); |
279 | if (err) { |
280 | aprint_error_dev(self, "init failed, %s\n" , usbd_errstr(err)); |
281 | goto bad; |
282 | } |
283 | |
284 | usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev, sc->sc_dev); |
285 | |
286 | if (sc->sc_flags & VISOR) { |
287 | sc->sc_numcon = UGETW(coninfo.num_ports); |
288 | if (sc->sc_numcon > UVISOR_MAX_CONN) |
289 | sc->sc_numcon = UVISOR_MAX_CONN; |
290 | |
291 | /* Attach a ucom for each connection. */ |
292 | for (i = 0; i < sc->sc_numcon; ++i) { |
293 | switch (coninfo.connections[i].port_function_id) { |
294 | case UVISOR_FUNCTION_GENERIC: |
295 | ucaa.ucaa_info = "Generic" ; |
296 | break; |
297 | case UVISOR_FUNCTION_DEBUGGER: |
298 | ucaa.ucaa_info = "Debugger" ; |
299 | break; |
300 | case UVISOR_FUNCTION_HOTSYNC: |
301 | ucaa.ucaa_info = "HotSync" ; |
302 | break; |
303 | case UVISOR_FUNCTION_REMOTE_FILE_SYS: |
304 | ucaa.ucaa_info = "Remote File System" ; |
305 | break; |
306 | default: |
307 | ucaa.ucaa_info = "unknown" ; |
308 | break; |
309 | } |
310 | port = coninfo.connections[i].port; |
311 | ucaa.ucaa_portno = port; |
312 | ucaa.ucaa_bulkin = port | UE_DIR_IN; |
313 | ucaa.ucaa_bulkout = port | UE_DIR_OUT; |
314 | /* Verify that endpoints exist. */ |
315 | hasin = 0; |
316 | hasout = 0; |
317 | for (j = 0; j < id->bNumEndpoints; j++) { |
318 | ed = usbd_interface2endpoint_descriptor(iface, j); |
319 | if (ed == NULL) |
320 | break; |
321 | if (UE_GET_ADDR(ed->bEndpointAddress) == port && |
322 | (ed->bmAttributes & UE_XFERTYPE) == UE_BULK) { |
323 | if (UE_GET_DIR(ed->bEndpointAddress) |
324 | == UE_DIR_IN) |
325 | hasin++; |
326 | else |
327 | hasout++; |
328 | } |
329 | } |
330 | if (hasin == 1 && hasout == 1) |
331 | sc->sc_subdevs[i] = config_found_sm_loc(self, |
332 | "ucombus" , NULL, &ucaa, |
333 | ucomprint, ucomsubmatch); |
334 | else |
335 | aprint_error_dev(self, |
336 | "no proper endpoints for port %d (%d,%d)\n" , |
337 | port, hasin, hasout); |
338 | } |
339 | |
340 | } else { |
341 | sc->sc_numcon = palmconinfo.num_ports; |
342 | if (sc->sc_numcon > UVISOR_MAX_CONN) |
343 | sc->sc_numcon = UVISOR_MAX_CONN; |
344 | |
345 | /* Attach a ucom for each connection. */ |
346 | for (i = 0; i < sc->sc_numcon; ++i) { |
347 | /* |
348 | * XXX this should copy out 4-char string from the |
349 | * XXX port_function_id, but where would the string go? |
350 | * XXX ucaa.ucaa_info is a const char *, not an array. |
351 | */ |
352 | ucaa.ucaa_info = "sync" ; |
353 | ucaa.ucaa_portno = i; |
354 | if (palmconinfo.endpoint_numbers_different) { |
355 | port = palmconinfo.connections[i].end_point_info; |
356 | ucaa.ucaa_bulkin = (port >> 4) | UE_DIR_IN; |
357 | ucaa.ucaa_bulkout = (port & 0xf) | UE_DIR_OUT; |
358 | } else { |
359 | port = palmconinfo.connections[i].port; |
360 | ucaa.ucaa_bulkin = port | UE_DIR_IN; |
361 | ucaa.ucaa_bulkout = port | UE_DIR_OUT; |
362 | } |
363 | sc->sc_subdevs[i] = config_found_sm_loc(self, "ucombus" , |
364 | NULL, &ucaa, ucomprint, ucomsubmatch); |
365 | |
366 | |
367 | } |
368 | } |
369 | |
370 | return; |
371 | |
372 | bad: |
373 | DPRINTF(("uvisor_attach: ATTACH ERROR\n" )); |
374 | sc->sc_dying = 1; |
375 | return; |
376 | } |
377 | |
378 | int |
379 | uvisor_activate(device_t self, enum devact act) |
380 | { |
381 | struct uvisor_softc *sc = device_private(self); |
382 | |
383 | switch (act) { |
384 | case DVACT_DEACTIVATE: |
385 | sc->sc_dying = 1; |
386 | return 0; |
387 | default: |
388 | return EOPNOTSUPP; |
389 | } |
390 | } |
391 | |
392 | void |
393 | uvisor_childdet(device_t self, device_t child) |
394 | { |
395 | int i; |
396 | struct uvisor_softc *sc = device_private(self); |
397 | |
398 | for (i = 0; i < sc->sc_numcon; i++) { |
399 | if (sc->sc_subdevs[i] == child) |
400 | break; |
401 | } |
402 | KASSERT(i < sc->sc_numcon); |
403 | sc->sc_subdevs[i] = NULL; |
404 | } |
405 | |
406 | int |
407 | uvisor_detach(device_t self, int flags) |
408 | { |
409 | struct uvisor_softc *sc = device_private(self); |
410 | int rv = 0; |
411 | int i; |
412 | |
413 | DPRINTF(("uvisor_detach: sc=%p flags=%d\n" , sc, flags)); |
414 | sc->sc_dying = 1; |
415 | for (i = 0; i < sc->sc_numcon; i++) { |
416 | if (sc->sc_subdevs[i] != NULL) |
417 | rv |= config_detach(sc->sc_subdevs[i], flags); |
418 | } |
419 | |
420 | if (sc->sc_udev) |
421 | usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev, |
422 | sc->sc_dev); |
423 | |
424 | |
425 | return rv; |
426 | } |
427 | |
428 | usbd_status |
429 | uvisor_init(struct uvisor_softc *sc, struct uvisor_connection_info *ci, |
430 | struct uvisor_palm_connection_info *cpi) |
431 | { |
432 | usbd_status err; |
433 | usb_device_request_t req; |
434 | int actlen; |
435 | uWord avail; |
436 | |
437 | if (sc->sc_flags & VISOR) { |
438 | DPRINTF(("uvisor_init: getting Visor connection info\n" )); |
439 | req.bmRequestType = UT_READ_VENDOR_ENDPOINT; |
440 | req.bRequest = UVISOR_GET_CONNECTION_INFORMATION; |
441 | USETW(req.wValue, 0); |
442 | USETW(req.wIndex, 0); |
443 | USETW(req.wLength, UVISOR_CONNECTION_INFO_SIZE); |
444 | err = usbd_do_request_flags(sc->sc_udev, &req, ci, |
445 | USBD_SHORT_XFER_OK, &actlen, USBD_DEFAULT_TIMEOUT); |
446 | if (err) |
447 | return err; |
448 | } |
449 | |
450 | if (sc->sc_flags & PALM4) { |
451 | DPRINTF(("uvisor_init: getting Palm connection info\n" )); |
452 | req.bmRequestType = UT_READ_VENDOR_ENDPOINT; |
453 | req.bRequest = UVISOR_GET_PALM_INFORMATION; |
454 | USETW(req.wValue, 0); |
455 | USETW(req.wIndex, 0); |
456 | USETW(req.wLength, UVISOR_GET_PALM_INFORMATION_LEN); |
457 | err = usbd_do_request_flags(sc->sc_udev, &req, cpi, |
458 | USBD_SHORT_XFER_OK, &actlen, USBD_DEFAULT_TIMEOUT); |
459 | if (err) |
460 | return err; |
461 | } |
462 | |
463 | DPRINTF(("uvisor_init: getting available bytes\n" )); |
464 | req.bmRequestType = UT_READ_VENDOR_ENDPOINT; |
465 | req.bRequest = UVISOR_REQUEST_BYTES_AVAILABLE; |
466 | USETW(req.wValue, 0); |
467 | USETW(req.wIndex, 5); |
468 | USETW(req.wLength, sizeof(avail)); |
469 | err = usbd_do_request(sc->sc_udev, &req, &avail); |
470 | if (err) |
471 | return err; |
472 | DPRINTF(("uvisor_init: avail=%d\n" , UGETW(avail))); |
473 | |
474 | DPRINTF(("uvisor_init: done\n" )); |
475 | return err; |
476 | } |
477 | |
478 | void |
479 | uvisor_close(void *addr, int portno) |
480 | { |
481 | struct uvisor_softc *sc = addr; |
482 | usb_device_request_t req; |
483 | struct uvisor_connection_info coninfo; /* XXX ? */ |
484 | int actlen; |
485 | |
486 | if (sc->sc_dying) |
487 | return; |
488 | |
489 | req.bmRequestType = UT_READ_VENDOR_ENDPOINT; /* XXX read? */ |
490 | req.bRequest = UVISOR_CLOSE_NOTIFICATION; |
491 | USETW(req.wValue, 0); |
492 | USETW(req.wIndex, 0); |
493 | USETW(req.wLength, UVISOR_CONNECTION_INFO_SIZE); |
494 | (void)usbd_do_request_flags(sc->sc_udev, &req, &coninfo, |
495 | USBD_SHORT_XFER_OK, &actlen, USBD_DEFAULT_TIMEOUT); |
496 | } |
497 | |