Deleted Added
full compact
ucycom.c (185950) ucycom.c (187176)
1#include <sys/cdefs.h>
1#include <sys/cdefs.h>
2__FBSDID("$FreeBSD: head/sys/dev/usb2/serial/ucycom2.c 185950 2008-12-11 23:17:48Z thompsa $");
2__FBSDID("$FreeBSD: head/sys/dev/usb2/serial/ucycom2.c 187176 2009-01-13 19:03:47Z thompsa $");
3
4/*-
5 * Copyright (c) 2004 Dag-Erling Co�dan Sm�rgrav
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer
13 * in this position and unchanged.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. The name of the author may not be used to endorse or promote products
18 * derived from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
24 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32/*
33 * Device driver for Cypress CY7C637xx and CY7C640/1xx series USB to
34 * RS232 bridges.
35 */
36
37#include <dev/usb2/include/usb2_devid.h>
38#include <dev/usb2/include/usb2_standard.h>
39#include <dev/usb2/include/usb2_mfunc.h>
40#include <dev/usb2/include/usb2_error.h>
41#include <dev/usb2/include/usb2_cdc.h>
42#include <dev/usb2/include/usb2_ioctl.h>
43#include <dev/usb2/include/usb2_hid.h>
44
45#define USB_DEBUG_VAR usb2_debug
46
47#include <dev/usb2/core/usb2_core.h>
48#include <dev/usb2/core/usb2_debug.h>
49#include <dev/usb2/core/usb2_process.h>
3
4/*-
5 * Copyright (c) 2004 Dag-Erling Co�dan Sm�rgrav
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer
13 * in this position and unchanged.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. The name of the author may not be used to endorse or promote products
18 * derived from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
24 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32/*
33 * Device driver for Cypress CY7C637xx and CY7C640/1xx series USB to
34 * RS232 bridges.
35 */
36
37#include <dev/usb2/include/usb2_devid.h>
38#include <dev/usb2/include/usb2_standard.h>
39#include <dev/usb2/include/usb2_mfunc.h>
40#include <dev/usb2/include/usb2_error.h>
41#include <dev/usb2/include/usb2_cdc.h>
42#include <dev/usb2/include/usb2_ioctl.h>
43#include <dev/usb2/include/usb2_hid.h>
44
45#define USB_DEBUG_VAR usb2_debug
46
47#include <dev/usb2/core/usb2_core.h>
48#include <dev/usb2/core/usb2_debug.h>
49#include <dev/usb2/core/usb2_process.h>
50#include <dev/usb2/core/usb2_config_td.h>
51#include <dev/usb2/core/usb2_request.h>
52#include <dev/usb2/core/usb2_lookup.h>
53#include <dev/usb2/core/usb2_util.h>
54#include <dev/usb2/core/usb2_busdma.h>
55#include <dev/usb2/core/usb2_hid.h>
56
57#include <dev/usb2/serial/usb2_serial.h>
58
59#define UCYCOM_MAX_IOLEN (1024 + 2) /* bytes */
60
61#define UCYCOM_ENDPT_MAX 3 /* units */
62#define UCYCOM_IFACE_INDEX 0
63
64struct ucycom_softc {
65 struct usb2_com_super_softc sc_super_ucom;
66 struct usb2_com_softc sc_ucom;
67
68 struct usb2_device *sc_udev;
69 struct usb2_xfer *sc_xfer[UCYCOM_ENDPT_MAX];
70
71 uint32_t sc_model;
72#define MODEL_CY7C63743 0x63743
73#define MODEL_CY7C64013 0x64013
74
75 uint16_t sc_flen; /* feature report length */
76 uint16_t sc_ilen; /* input report length */
77 uint16_t sc_olen; /* output report length */
78
79 uint8_t sc_fid; /* feature report id */
80 uint8_t sc_iid; /* input report id */
81 uint8_t sc_oid; /* output report id */
82 uint8_t sc_cfg;
83#define UCYCOM_CFG_RESET 0x80
84#define UCYCOM_CFG_PARODD 0x20
85#define UCYCOM_CFG_PAREN 0x10
86#define UCYCOM_CFG_STOPB 0x08
87#define UCYCOM_CFG_DATAB 0x03
88 uint8_t sc_ist; /* status flags from last input */
89 uint8_t sc_flags;
90#define UCYCOM_FLAG_INTR_STALL 0x01
91 uint8_t sc_name[16];
92 uint8_t sc_iface_no;
93 uint8_t sc_temp_cfg[32];
94};
95
96/* prototypes */
97
98static device_probe_t ucycom_probe;
99static device_attach_t ucycom_attach;
100static device_detach_t ucycom_detach;
101
102static usb2_callback_t ucycom_ctrl_write_callback;
103static usb2_callback_t ucycom_intr_read_clear_stall_callback;
104static usb2_callback_t ucycom_intr_read_callback;
105
106static void ucycom_cfg_open(struct usb2_com_softc *);
107static void ucycom_start_read(struct usb2_com_softc *);
108static void ucycom_stop_read(struct usb2_com_softc *);
109static void ucycom_start_write(struct usb2_com_softc *);
110static void ucycom_stop_write(struct usb2_com_softc *);
111static void ucycom_cfg_write(struct ucycom_softc *, uint32_t, uint8_t);
112static int ucycom_pre_param(struct usb2_com_softc *, struct termios *);
113static void ucycom_cfg_param(struct usb2_com_softc *, struct termios *);
114
115static const struct usb2_config ucycom_config[UCYCOM_ENDPT_MAX] = {
116
117 [0] = {
118 .type = UE_CONTROL,
119 .endpoint = 0x00, /* Control pipe */
120 .direction = UE_DIR_ANY,
121 .mh.bufsize = (sizeof(struct usb2_device_request) + UCYCOM_MAX_IOLEN),
122 .mh.flags = {},
123 .mh.callback = &ucycom_ctrl_write_callback,
124 .mh.timeout = 1000, /* 1 second */
125 },
126
127 [1] = {
128 .type = UE_INTERRUPT,
129 .endpoint = UE_ADDR_ANY,
130 .direction = UE_DIR_IN,
131 .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
132 .mh.bufsize = UCYCOM_MAX_IOLEN,
133 .mh.callback = &ucycom_intr_read_callback,
134 },
135
136 [2] = {
137 .type = UE_CONTROL,
138 .endpoint = 0x00, /* Control pipe */
139 .direction = UE_DIR_ANY,
140 .mh.bufsize = sizeof(struct usb2_device_request),
141 .mh.flags = {},
142 .mh.callback = &ucycom_intr_read_clear_stall_callback,
143 .mh.timeout = 1000, /* 1 second */
144 .mh.interval = 50, /* 50ms */
145 },
146};
147
148static const struct usb2_com_callback ucycom_callback = {
149 .usb2_com_cfg_param = &ucycom_cfg_param,
150 .usb2_com_cfg_open = &ucycom_cfg_open,
151 .usb2_com_pre_param = &ucycom_pre_param,
152 .usb2_com_start_read = &ucycom_start_read,
153 .usb2_com_stop_read = &ucycom_stop_read,
154 .usb2_com_start_write = &ucycom_start_write,
155 .usb2_com_stop_write = &ucycom_stop_write,
156};
157
158static device_method_t ucycom_methods[] = {
159 DEVMETHOD(device_probe, ucycom_probe),
160 DEVMETHOD(device_attach, ucycom_attach),
161 DEVMETHOD(device_detach, ucycom_detach),
162 {0, 0}
163};
164
165static devclass_t ucycom_devclass;
166
167static driver_t ucycom_driver = {
168 .name = "ucycom",
169 .methods = ucycom_methods,
170 .size = sizeof(struct ucycom_softc),
171};
172
173DRIVER_MODULE(ucycom, ushub, ucycom_driver, ucycom_devclass, NULL, 0);
174MODULE_DEPEND(ucycom, usb2_serial, 1, 1, 1);
175MODULE_DEPEND(ucycom, usb2_core, 1, 1, 1);
176
177/*
178 * Supported devices
179 */
180static const struct usb2_device_id ucycom_devs[] = {
181 {USB_VPI(USB_VENDOR_DELORME, USB_PRODUCT_DELORME_EARTHMATE, MODEL_CY7C64013)},
182};
183
184#define UCYCOM_DEFAULT_RATE 4800
185#define UCYCOM_DEFAULT_CFG 0x03 /* N-8-1 */
186
187static int
188ucycom_probe(device_t dev)
189{
190 struct usb2_attach_arg *uaa = device_get_ivars(dev);
191
192 if (uaa->usb2_mode != USB_MODE_HOST) {
193 return (ENXIO);
194 }
195 if (uaa->info.bConfigIndex != 0) {
196 return (ENXIO);
197 }
198 if (uaa->info.bIfaceIndex != UCYCOM_IFACE_INDEX) {
199 return (ENXIO);
200 }
201 return (usb2_lookup_id_by_uaa(ucycom_devs, sizeof(ucycom_devs), uaa));
202}
203
204static int
205ucycom_attach(device_t dev)
206{
207 struct usb2_attach_arg *uaa = device_get_ivars(dev);
208 struct ucycom_softc *sc = device_get_softc(dev);
209 void *urd_ptr = NULL;
210 int32_t error;
211 uint16_t urd_len;
212 uint8_t iface_index;
213
214 if (sc == NULL) {
215 return (ENOMEM);
216 }
217 sc->sc_udev = uaa->device;
218
219 device_set_usb2_desc(dev);
220
221 snprintf(sc->sc_name, sizeof(sc->sc_name),
222 "%s", device_get_nameunit(dev));
223
224 DPRINTF("\n");
225
226 /* get chip model */
227 sc->sc_model = USB_GET_DRIVER_INFO(uaa);
228 if (sc->sc_model == 0) {
229 device_printf(dev, "unsupported device\n");
230 goto detach;
231 }
232 device_printf(dev, "Cypress CY7C%X USB to RS232 bridge\n", sc->sc_model);
233
234 /* get report descriptor */
235
236 error = usb2_req_get_hid_desc
237 (uaa->device, &Giant,
238 &urd_ptr, &urd_len, M_USBDEV,
239 UCYCOM_IFACE_INDEX);
240
241 if (error) {
242 device_printf(dev, "failed to get report "
243 "descriptor: %s\n",
244 usb2_errstr(error));
245 goto detach;
246 }
247 /* get report sizes */
248
249 sc->sc_flen = hid_report_size(urd_ptr, urd_len, hid_feature, &sc->sc_fid);
250 sc->sc_ilen = hid_report_size(urd_ptr, urd_len, hid_input, &sc->sc_iid);
251 sc->sc_olen = hid_report_size(urd_ptr, urd_len, hid_output, &sc->sc_oid);
252
253 if ((sc->sc_ilen > UCYCOM_MAX_IOLEN) || (sc->sc_ilen < 1) ||
254 (sc->sc_olen > UCYCOM_MAX_IOLEN) || (sc->sc_olen < 2) ||
255 (sc->sc_flen > UCYCOM_MAX_IOLEN) || (sc->sc_flen < 5)) {
256 device_printf(dev, "invalid report size i=%d, o=%d, f=%d, max=%d\n",
257 sc->sc_ilen, sc->sc_olen, sc->sc_flen,
258 UCYCOM_MAX_IOLEN);
259 goto detach;
260 }
261 sc->sc_iface_no = uaa->info.bIfaceNum;
262
263 iface_index = UCYCOM_IFACE_INDEX;
264 error = usb2_transfer_setup(uaa->device, &iface_index,
265 sc->sc_xfer, ucycom_config, UCYCOM_ENDPT_MAX,
266 sc, &Giant);
267 if (error) {
268 device_printf(dev, "allocating USB "
269 "transfers failed!\n");
270 goto detach;
271 }
272 error = usb2_com_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
273 &ucycom_callback, &Giant);
274
275 if (error) {
276 goto detach;
277 }
278 if (urd_ptr) {
279 free(urd_ptr, M_USBDEV);
280 }
281 return (0); /* success */
282
283detach:
284 if (urd_ptr) {
285 free(urd_ptr, M_USBDEV);
286 }
287 ucycom_detach(dev);
288 return (ENXIO);
289}
290
291static int
292ucycom_detach(device_t dev)
293{
294 struct ucycom_softc *sc = device_get_softc(dev);
295
296 usb2_com_detach(&sc->sc_super_ucom, &sc->sc_ucom, 1);
297
298 usb2_transfer_unsetup(sc->sc_xfer, UCYCOM_ENDPT_MAX);
299
300 return (0);
301}
302
303static void
304ucycom_cfg_open(struct usb2_com_softc *ucom)
305{
306 struct ucycom_softc *sc = ucom->sc_parent;
307
308 /* set default configuration */
309 ucycom_cfg_write(sc, UCYCOM_DEFAULT_RATE, UCYCOM_DEFAULT_CFG);
310}
311
312static void
313ucycom_start_read(struct usb2_com_softc *ucom)
314{
315 struct ucycom_softc *sc = ucom->sc_parent;
316
317 usb2_transfer_start(sc->sc_xfer[1]);
318}
319
320static void
321ucycom_stop_read(struct usb2_com_softc *ucom)
322{
323 struct ucycom_softc *sc = ucom->sc_parent;
324
325 usb2_transfer_stop(sc->sc_xfer[2]);
326 usb2_transfer_stop(sc->sc_xfer[1]);
327}
328
329static void
330ucycom_start_write(struct usb2_com_softc *ucom)
331{
332 struct ucycom_softc *sc = ucom->sc_parent;
333
334 usb2_transfer_start(sc->sc_xfer[0]);
335}
336
337static void
338ucycom_stop_write(struct usb2_com_softc *ucom)
339{
340 struct ucycom_softc *sc = ucom->sc_parent;
341
342 usb2_transfer_stop(sc->sc_xfer[0]);
343}
344
345static void
346ucycom_ctrl_write_callback(struct usb2_xfer *xfer)
347{
348 struct ucycom_softc *sc = xfer->priv_sc;
349 struct usb2_device_request req;
350 uint8_t data[2];
351 uint8_t offset;
352 uint32_t actlen;
353
354 switch (USB_GET_STATE(xfer)) {
355 case USB_ST_TRANSFERRED:
356tr_transferred:
357 case USB_ST_SETUP:
358
359 switch (sc->sc_model) {
360 case MODEL_CY7C63743:
361 offset = 1;
362 break;
363 case MODEL_CY7C64013:
364 offset = 2;
365 break;
366 default:
367 offset = 0;
368 break;
369 }
370
371 if (usb2_com_get_data(&sc->sc_ucom, xfer->frbuffers + 1, offset,
372 sc->sc_olen - offset, &actlen)) {
373
374 req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
375 req.bRequest = UR_SET_REPORT;
376 USETW2(req.wValue, UHID_OUTPUT_REPORT, sc->sc_oid);
377 req.wIndex[0] = sc->sc_iface_no;
378 req.wIndex[1] = 0;
379 USETW(req.wLength, sc->sc_olen);
380
381 switch (sc->sc_model) {
382 case MODEL_CY7C63743:
383 data[0] = actlen;
384 break;
385 case MODEL_CY7C64013:
386 data[0] = 0;
387 data[1] = actlen;
388 break;
389 default:
390 break;
391 }
392
393 usb2_copy_in(xfer->frbuffers, 0, &req, sizeof(req));
394 usb2_copy_in(xfer->frbuffers + 1, 0, data, offset);
395
396 xfer->frlengths[0] = sizeof(req);
397 xfer->frlengths[1] = sc->sc_olen;
398 xfer->nframes = xfer->frlengths[1] ? 2 : 1;
399 usb2_start_hardware(xfer);
400 }
401 return;
402
403 default: /* Error */
404 if (xfer->error == USB_ERR_CANCELLED) {
405 return;
406 }
407 DPRINTF("error=%s\n",
408 usb2_errstr(xfer->error));
409 goto tr_transferred;
410 }
411}
412
413static void
414ucycom_cfg_write(struct ucycom_softc *sc, uint32_t baud, uint8_t cfg)
415{
416 struct usb2_device_request req;
417 uint16_t len;
418 usb2_error_t err;
419
420 len = sc->sc_flen;
421 if (len > sizeof(sc->sc_temp_cfg)) {
422 len = sizeof(sc->sc_temp_cfg);
423 }
424 sc->sc_cfg = cfg;
425
426 req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
427 req.bRequest = UR_SET_REPORT;
428 USETW2(req.wValue, UHID_FEATURE_REPORT, sc->sc_fid);
429 req.wIndex[0] = sc->sc_iface_no;
430 req.wIndex[1] = 0;
431 USETW(req.wLength, len);
432
433 sc->sc_temp_cfg[0] = (baud & 0xff);
434 sc->sc_temp_cfg[1] = (baud >> 8) & 0xff;
435 sc->sc_temp_cfg[2] = (baud >> 16) & 0xff;
436 sc->sc_temp_cfg[3] = (baud >> 24) & 0xff;
437 sc->sc_temp_cfg[4] = cfg;
438
439 if (usb2_com_cfg_is_gone(&sc->sc_ucom)) {
440 return;
441 }
442 err = usb2_do_request_flags
443 (sc->sc_udev, &Giant, &req, sc->sc_temp_cfg, 0, NULL, 1000);
444
445 if (err) {
446 DPRINTFN(0, "device request failed, err=%s "
447 "(ignored)\n", usb2_errstr(err));
448 }
449}
450
451static int
452ucycom_pre_param(struct usb2_com_softc *ucom, struct termios *t)
453{
454 switch (t->c_ospeed) {
455 case 600:
456 case 1200:
457 case 2400:
458 case 4800:
459 case 9600:
460 case 19200:
461 case 38400:
462 case 57600:
463#if 0
464 /*
465 * Stock chips only support standard baud rates in the 600 - 57600
466 * range, but higher rates can be achieved using custom firmware.
467 */
468 case 115200:
469 case 153600:
470 case 192000:
471#endif
472 break;
473 default:
474 return (EINVAL);
475 }
476 return (0);
477}
478
479static void
480ucycom_cfg_param(struct usb2_com_softc *ucom, struct termios *t)
481{
482 struct ucycom_softc *sc = ucom->sc_parent;
483 uint8_t cfg;
484
485 DPRINTF("\n");
486
487 if (t->c_cflag & CIGNORE) {
488 cfg = sc->sc_cfg;
489 } else {
490 cfg = 0;
491 switch (t->c_cflag & CSIZE) {
492 default:
493 case CS8:
494 ++cfg;
495 case CS7:
496 ++cfg;
497 case CS6:
498 ++cfg;
499 case CS5:
500 break;
501 }
502
503 if (t->c_cflag & CSTOPB)
504 cfg |= UCYCOM_CFG_STOPB;
505 if (t->c_cflag & PARENB)
506 cfg |= UCYCOM_CFG_PAREN;
507 if (t->c_cflag & PARODD)
508 cfg |= UCYCOM_CFG_PARODD;
509 }
510
511 ucycom_cfg_write(sc, t->c_ospeed, cfg);
512}
513
514static void
515ucycom_intr_read_clear_stall_callback(struct usb2_xfer *xfer)
516{
517 struct ucycom_softc *sc = xfer->priv_sc;
518 struct usb2_xfer *xfer_other = sc->sc_xfer[1];
519
520 if (usb2_clear_stall_callback(xfer, xfer_other)) {
521 DPRINTF("stall cleared\n");
522 sc->sc_flags &= ~UCYCOM_FLAG_INTR_STALL;
523 usb2_transfer_start(xfer_other);
524 }
525}
526
527static void
528ucycom_intr_read_callback(struct usb2_xfer *xfer)
529{
530 struct ucycom_softc *sc = xfer->priv_sc;
531 uint8_t buf[2];
532 uint32_t offset;
533 uint32_t len;
534
535 switch (USB_GET_STATE(xfer)) {
536 case USB_ST_TRANSFERRED:
537 switch (sc->sc_model) {
538 case MODEL_CY7C63743:
539 if (xfer->actlen < 1) {
540 goto tr_setup;
541 }
542 usb2_copy_out(xfer->frbuffers, 0, buf, 1);
543
544 sc->sc_ist = buf[0] & ~0x07;
545 len = buf[0] & 0x07;
546
547 (xfer->actlen)--;
548
549 offset = 1;
550
551 break;
552
553 case MODEL_CY7C64013:
554 if (xfer->actlen < 2) {
555 goto tr_setup;
556 }
557 usb2_copy_out(xfer->frbuffers, 0, buf, 2);
558
559 sc->sc_ist = buf[0] & ~0x07;
560 len = buf[1];
561
562 (xfer->actlen) -= 2;
563
564 offset = 2;
565
566 break;
567
568 default:
569 DPRINTFN(0, "unsupported model number!\n");
570 goto tr_setup;
571 }
572
573 if (len > xfer->actlen) {
574 len = xfer->actlen;
575 }
576 if (len) {
577 usb2_com_put_data(&sc->sc_ucom, xfer->frbuffers,
578 offset, len);
579 }
580 case USB_ST_SETUP:
581tr_setup:
582 if (sc->sc_flags & UCYCOM_FLAG_INTR_STALL) {
583 usb2_transfer_start(sc->sc_xfer[2]);
584 } else {
585 xfer->frlengths[0] = sc->sc_ilen;
586 usb2_start_hardware(xfer);
587 }
588 return;
589
590 default: /* Error */
591 if (xfer->error != USB_ERR_CANCELLED) {
592 sc->sc_flags |= UCYCOM_FLAG_INTR_STALL;
593 usb2_transfer_start(sc->sc_xfer[2]);
594 }
595 return;
596
597 }
598}
50#include <dev/usb2/core/usb2_request.h>
51#include <dev/usb2/core/usb2_lookup.h>
52#include <dev/usb2/core/usb2_util.h>
53#include <dev/usb2/core/usb2_busdma.h>
54#include <dev/usb2/core/usb2_hid.h>
55
56#include <dev/usb2/serial/usb2_serial.h>
57
58#define UCYCOM_MAX_IOLEN (1024 + 2) /* bytes */
59
60#define UCYCOM_ENDPT_MAX 3 /* units */
61#define UCYCOM_IFACE_INDEX 0
62
63struct ucycom_softc {
64 struct usb2_com_super_softc sc_super_ucom;
65 struct usb2_com_softc sc_ucom;
66
67 struct usb2_device *sc_udev;
68 struct usb2_xfer *sc_xfer[UCYCOM_ENDPT_MAX];
69
70 uint32_t sc_model;
71#define MODEL_CY7C63743 0x63743
72#define MODEL_CY7C64013 0x64013
73
74 uint16_t sc_flen; /* feature report length */
75 uint16_t sc_ilen; /* input report length */
76 uint16_t sc_olen; /* output report length */
77
78 uint8_t sc_fid; /* feature report id */
79 uint8_t sc_iid; /* input report id */
80 uint8_t sc_oid; /* output report id */
81 uint8_t sc_cfg;
82#define UCYCOM_CFG_RESET 0x80
83#define UCYCOM_CFG_PARODD 0x20
84#define UCYCOM_CFG_PAREN 0x10
85#define UCYCOM_CFG_STOPB 0x08
86#define UCYCOM_CFG_DATAB 0x03
87 uint8_t sc_ist; /* status flags from last input */
88 uint8_t sc_flags;
89#define UCYCOM_FLAG_INTR_STALL 0x01
90 uint8_t sc_name[16];
91 uint8_t sc_iface_no;
92 uint8_t sc_temp_cfg[32];
93};
94
95/* prototypes */
96
97static device_probe_t ucycom_probe;
98static device_attach_t ucycom_attach;
99static device_detach_t ucycom_detach;
100
101static usb2_callback_t ucycom_ctrl_write_callback;
102static usb2_callback_t ucycom_intr_read_clear_stall_callback;
103static usb2_callback_t ucycom_intr_read_callback;
104
105static void ucycom_cfg_open(struct usb2_com_softc *);
106static void ucycom_start_read(struct usb2_com_softc *);
107static void ucycom_stop_read(struct usb2_com_softc *);
108static void ucycom_start_write(struct usb2_com_softc *);
109static void ucycom_stop_write(struct usb2_com_softc *);
110static void ucycom_cfg_write(struct ucycom_softc *, uint32_t, uint8_t);
111static int ucycom_pre_param(struct usb2_com_softc *, struct termios *);
112static void ucycom_cfg_param(struct usb2_com_softc *, struct termios *);
113
114static const struct usb2_config ucycom_config[UCYCOM_ENDPT_MAX] = {
115
116 [0] = {
117 .type = UE_CONTROL,
118 .endpoint = 0x00, /* Control pipe */
119 .direction = UE_DIR_ANY,
120 .mh.bufsize = (sizeof(struct usb2_device_request) + UCYCOM_MAX_IOLEN),
121 .mh.flags = {},
122 .mh.callback = &ucycom_ctrl_write_callback,
123 .mh.timeout = 1000, /* 1 second */
124 },
125
126 [1] = {
127 .type = UE_INTERRUPT,
128 .endpoint = UE_ADDR_ANY,
129 .direction = UE_DIR_IN,
130 .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
131 .mh.bufsize = UCYCOM_MAX_IOLEN,
132 .mh.callback = &ucycom_intr_read_callback,
133 },
134
135 [2] = {
136 .type = UE_CONTROL,
137 .endpoint = 0x00, /* Control pipe */
138 .direction = UE_DIR_ANY,
139 .mh.bufsize = sizeof(struct usb2_device_request),
140 .mh.flags = {},
141 .mh.callback = &ucycom_intr_read_clear_stall_callback,
142 .mh.timeout = 1000, /* 1 second */
143 .mh.interval = 50, /* 50ms */
144 },
145};
146
147static const struct usb2_com_callback ucycom_callback = {
148 .usb2_com_cfg_param = &ucycom_cfg_param,
149 .usb2_com_cfg_open = &ucycom_cfg_open,
150 .usb2_com_pre_param = &ucycom_pre_param,
151 .usb2_com_start_read = &ucycom_start_read,
152 .usb2_com_stop_read = &ucycom_stop_read,
153 .usb2_com_start_write = &ucycom_start_write,
154 .usb2_com_stop_write = &ucycom_stop_write,
155};
156
157static device_method_t ucycom_methods[] = {
158 DEVMETHOD(device_probe, ucycom_probe),
159 DEVMETHOD(device_attach, ucycom_attach),
160 DEVMETHOD(device_detach, ucycom_detach),
161 {0, 0}
162};
163
164static devclass_t ucycom_devclass;
165
166static driver_t ucycom_driver = {
167 .name = "ucycom",
168 .methods = ucycom_methods,
169 .size = sizeof(struct ucycom_softc),
170};
171
172DRIVER_MODULE(ucycom, ushub, ucycom_driver, ucycom_devclass, NULL, 0);
173MODULE_DEPEND(ucycom, usb2_serial, 1, 1, 1);
174MODULE_DEPEND(ucycom, usb2_core, 1, 1, 1);
175
176/*
177 * Supported devices
178 */
179static const struct usb2_device_id ucycom_devs[] = {
180 {USB_VPI(USB_VENDOR_DELORME, USB_PRODUCT_DELORME_EARTHMATE, MODEL_CY7C64013)},
181};
182
183#define UCYCOM_DEFAULT_RATE 4800
184#define UCYCOM_DEFAULT_CFG 0x03 /* N-8-1 */
185
186static int
187ucycom_probe(device_t dev)
188{
189 struct usb2_attach_arg *uaa = device_get_ivars(dev);
190
191 if (uaa->usb2_mode != USB_MODE_HOST) {
192 return (ENXIO);
193 }
194 if (uaa->info.bConfigIndex != 0) {
195 return (ENXIO);
196 }
197 if (uaa->info.bIfaceIndex != UCYCOM_IFACE_INDEX) {
198 return (ENXIO);
199 }
200 return (usb2_lookup_id_by_uaa(ucycom_devs, sizeof(ucycom_devs), uaa));
201}
202
203static int
204ucycom_attach(device_t dev)
205{
206 struct usb2_attach_arg *uaa = device_get_ivars(dev);
207 struct ucycom_softc *sc = device_get_softc(dev);
208 void *urd_ptr = NULL;
209 int32_t error;
210 uint16_t urd_len;
211 uint8_t iface_index;
212
213 if (sc == NULL) {
214 return (ENOMEM);
215 }
216 sc->sc_udev = uaa->device;
217
218 device_set_usb2_desc(dev);
219
220 snprintf(sc->sc_name, sizeof(sc->sc_name),
221 "%s", device_get_nameunit(dev));
222
223 DPRINTF("\n");
224
225 /* get chip model */
226 sc->sc_model = USB_GET_DRIVER_INFO(uaa);
227 if (sc->sc_model == 0) {
228 device_printf(dev, "unsupported device\n");
229 goto detach;
230 }
231 device_printf(dev, "Cypress CY7C%X USB to RS232 bridge\n", sc->sc_model);
232
233 /* get report descriptor */
234
235 error = usb2_req_get_hid_desc
236 (uaa->device, &Giant,
237 &urd_ptr, &urd_len, M_USBDEV,
238 UCYCOM_IFACE_INDEX);
239
240 if (error) {
241 device_printf(dev, "failed to get report "
242 "descriptor: %s\n",
243 usb2_errstr(error));
244 goto detach;
245 }
246 /* get report sizes */
247
248 sc->sc_flen = hid_report_size(urd_ptr, urd_len, hid_feature, &sc->sc_fid);
249 sc->sc_ilen = hid_report_size(urd_ptr, urd_len, hid_input, &sc->sc_iid);
250 sc->sc_olen = hid_report_size(urd_ptr, urd_len, hid_output, &sc->sc_oid);
251
252 if ((sc->sc_ilen > UCYCOM_MAX_IOLEN) || (sc->sc_ilen < 1) ||
253 (sc->sc_olen > UCYCOM_MAX_IOLEN) || (sc->sc_olen < 2) ||
254 (sc->sc_flen > UCYCOM_MAX_IOLEN) || (sc->sc_flen < 5)) {
255 device_printf(dev, "invalid report size i=%d, o=%d, f=%d, max=%d\n",
256 sc->sc_ilen, sc->sc_olen, sc->sc_flen,
257 UCYCOM_MAX_IOLEN);
258 goto detach;
259 }
260 sc->sc_iface_no = uaa->info.bIfaceNum;
261
262 iface_index = UCYCOM_IFACE_INDEX;
263 error = usb2_transfer_setup(uaa->device, &iface_index,
264 sc->sc_xfer, ucycom_config, UCYCOM_ENDPT_MAX,
265 sc, &Giant);
266 if (error) {
267 device_printf(dev, "allocating USB "
268 "transfers failed!\n");
269 goto detach;
270 }
271 error = usb2_com_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
272 &ucycom_callback, &Giant);
273
274 if (error) {
275 goto detach;
276 }
277 if (urd_ptr) {
278 free(urd_ptr, M_USBDEV);
279 }
280 return (0); /* success */
281
282detach:
283 if (urd_ptr) {
284 free(urd_ptr, M_USBDEV);
285 }
286 ucycom_detach(dev);
287 return (ENXIO);
288}
289
290static int
291ucycom_detach(device_t dev)
292{
293 struct ucycom_softc *sc = device_get_softc(dev);
294
295 usb2_com_detach(&sc->sc_super_ucom, &sc->sc_ucom, 1);
296
297 usb2_transfer_unsetup(sc->sc_xfer, UCYCOM_ENDPT_MAX);
298
299 return (0);
300}
301
302static void
303ucycom_cfg_open(struct usb2_com_softc *ucom)
304{
305 struct ucycom_softc *sc = ucom->sc_parent;
306
307 /* set default configuration */
308 ucycom_cfg_write(sc, UCYCOM_DEFAULT_RATE, UCYCOM_DEFAULT_CFG);
309}
310
311static void
312ucycom_start_read(struct usb2_com_softc *ucom)
313{
314 struct ucycom_softc *sc = ucom->sc_parent;
315
316 usb2_transfer_start(sc->sc_xfer[1]);
317}
318
319static void
320ucycom_stop_read(struct usb2_com_softc *ucom)
321{
322 struct ucycom_softc *sc = ucom->sc_parent;
323
324 usb2_transfer_stop(sc->sc_xfer[2]);
325 usb2_transfer_stop(sc->sc_xfer[1]);
326}
327
328static void
329ucycom_start_write(struct usb2_com_softc *ucom)
330{
331 struct ucycom_softc *sc = ucom->sc_parent;
332
333 usb2_transfer_start(sc->sc_xfer[0]);
334}
335
336static void
337ucycom_stop_write(struct usb2_com_softc *ucom)
338{
339 struct ucycom_softc *sc = ucom->sc_parent;
340
341 usb2_transfer_stop(sc->sc_xfer[0]);
342}
343
344static void
345ucycom_ctrl_write_callback(struct usb2_xfer *xfer)
346{
347 struct ucycom_softc *sc = xfer->priv_sc;
348 struct usb2_device_request req;
349 uint8_t data[2];
350 uint8_t offset;
351 uint32_t actlen;
352
353 switch (USB_GET_STATE(xfer)) {
354 case USB_ST_TRANSFERRED:
355tr_transferred:
356 case USB_ST_SETUP:
357
358 switch (sc->sc_model) {
359 case MODEL_CY7C63743:
360 offset = 1;
361 break;
362 case MODEL_CY7C64013:
363 offset = 2;
364 break;
365 default:
366 offset = 0;
367 break;
368 }
369
370 if (usb2_com_get_data(&sc->sc_ucom, xfer->frbuffers + 1, offset,
371 sc->sc_olen - offset, &actlen)) {
372
373 req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
374 req.bRequest = UR_SET_REPORT;
375 USETW2(req.wValue, UHID_OUTPUT_REPORT, sc->sc_oid);
376 req.wIndex[0] = sc->sc_iface_no;
377 req.wIndex[1] = 0;
378 USETW(req.wLength, sc->sc_olen);
379
380 switch (sc->sc_model) {
381 case MODEL_CY7C63743:
382 data[0] = actlen;
383 break;
384 case MODEL_CY7C64013:
385 data[0] = 0;
386 data[1] = actlen;
387 break;
388 default:
389 break;
390 }
391
392 usb2_copy_in(xfer->frbuffers, 0, &req, sizeof(req));
393 usb2_copy_in(xfer->frbuffers + 1, 0, data, offset);
394
395 xfer->frlengths[0] = sizeof(req);
396 xfer->frlengths[1] = sc->sc_olen;
397 xfer->nframes = xfer->frlengths[1] ? 2 : 1;
398 usb2_start_hardware(xfer);
399 }
400 return;
401
402 default: /* Error */
403 if (xfer->error == USB_ERR_CANCELLED) {
404 return;
405 }
406 DPRINTF("error=%s\n",
407 usb2_errstr(xfer->error));
408 goto tr_transferred;
409 }
410}
411
412static void
413ucycom_cfg_write(struct ucycom_softc *sc, uint32_t baud, uint8_t cfg)
414{
415 struct usb2_device_request req;
416 uint16_t len;
417 usb2_error_t err;
418
419 len = sc->sc_flen;
420 if (len > sizeof(sc->sc_temp_cfg)) {
421 len = sizeof(sc->sc_temp_cfg);
422 }
423 sc->sc_cfg = cfg;
424
425 req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
426 req.bRequest = UR_SET_REPORT;
427 USETW2(req.wValue, UHID_FEATURE_REPORT, sc->sc_fid);
428 req.wIndex[0] = sc->sc_iface_no;
429 req.wIndex[1] = 0;
430 USETW(req.wLength, len);
431
432 sc->sc_temp_cfg[0] = (baud & 0xff);
433 sc->sc_temp_cfg[1] = (baud >> 8) & 0xff;
434 sc->sc_temp_cfg[2] = (baud >> 16) & 0xff;
435 sc->sc_temp_cfg[3] = (baud >> 24) & 0xff;
436 sc->sc_temp_cfg[4] = cfg;
437
438 if (usb2_com_cfg_is_gone(&sc->sc_ucom)) {
439 return;
440 }
441 err = usb2_do_request_flags
442 (sc->sc_udev, &Giant, &req, sc->sc_temp_cfg, 0, NULL, 1000);
443
444 if (err) {
445 DPRINTFN(0, "device request failed, err=%s "
446 "(ignored)\n", usb2_errstr(err));
447 }
448}
449
450static int
451ucycom_pre_param(struct usb2_com_softc *ucom, struct termios *t)
452{
453 switch (t->c_ospeed) {
454 case 600:
455 case 1200:
456 case 2400:
457 case 4800:
458 case 9600:
459 case 19200:
460 case 38400:
461 case 57600:
462#if 0
463 /*
464 * Stock chips only support standard baud rates in the 600 - 57600
465 * range, but higher rates can be achieved using custom firmware.
466 */
467 case 115200:
468 case 153600:
469 case 192000:
470#endif
471 break;
472 default:
473 return (EINVAL);
474 }
475 return (0);
476}
477
478static void
479ucycom_cfg_param(struct usb2_com_softc *ucom, struct termios *t)
480{
481 struct ucycom_softc *sc = ucom->sc_parent;
482 uint8_t cfg;
483
484 DPRINTF("\n");
485
486 if (t->c_cflag & CIGNORE) {
487 cfg = sc->sc_cfg;
488 } else {
489 cfg = 0;
490 switch (t->c_cflag & CSIZE) {
491 default:
492 case CS8:
493 ++cfg;
494 case CS7:
495 ++cfg;
496 case CS6:
497 ++cfg;
498 case CS5:
499 break;
500 }
501
502 if (t->c_cflag & CSTOPB)
503 cfg |= UCYCOM_CFG_STOPB;
504 if (t->c_cflag & PARENB)
505 cfg |= UCYCOM_CFG_PAREN;
506 if (t->c_cflag & PARODD)
507 cfg |= UCYCOM_CFG_PARODD;
508 }
509
510 ucycom_cfg_write(sc, t->c_ospeed, cfg);
511}
512
513static void
514ucycom_intr_read_clear_stall_callback(struct usb2_xfer *xfer)
515{
516 struct ucycom_softc *sc = xfer->priv_sc;
517 struct usb2_xfer *xfer_other = sc->sc_xfer[1];
518
519 if (usb2_clear_stall_callback(xfer, xfer_other)) {
520 DPRINTF("stall cleared\n");
521 sc->sc_flags &= ~UCYCOM_FLAG_INTR_STALL;
522 usb2_transfer_start(xfer_other);
523 }
524}
525
526static void
527ucycom_intr_read_callback(struct usb2_xfer *xfer)
528{
529 struct ucycom_softc *sc = xfer->priv_sc;
530 uint8_t buf[2];
531 uint32_t offset;
532 uint32_t len;
533
534 switch (USB_GET_STATE(xfer)) {
535 case USB_ST_TRANSFERRED:
536 switch (sc->sc_model) {
537 case MODEL_CY7C63743:
538 if (xfer->actlen < 1) {
539 goto tr_setup;
540 }
541 usb2_copy_out(xfer->frbuffers, 0, buf, 1);
542
543 sc->sc_ist = buf[0] & ~0x07;
544 len = buf[0] & 0x07;
545
546 (xfer->actlen)--;
547
548 offset = 1;
549
550 break;
551
552 case MODEL_CY7C64013:
553 if (xfer->actlen < 2) {
554 goto tr_setup;
555 }
556 usb2_copy_out(xfer->frbuffers, 0, buf, 2);
557
558 sc->sc_ist = buf[0] & ~0x07;
559 len = buf[1];
560
561 (xfer->actlen) -= 2;
562
563 offset = 2;
564
565 break;
566
567 default:
568 DPRINTFN(0, "unsupported model number!\n");
569 goto tr_setup;
570 }
571
572 if (len > xfer->actlen) {
573 len = xfer->actlen;
574 }
575 if (len) {
576 usb2_com_put_data(&sc->sc_ucom, xfer->frbuffers,
577 offset, len);
578 }
579 case USB_ST_SETUP:
580tr_setup:
581 if (sc->sc_flags & UCYCOM_FLAG_INTR_STALL) {
582 usb2_transfer_start(sc->sc_xfer[2]);
583 } else {
584 xfer->frlengths[0] = sc->sc_ilen;
585 usb2_start_hardware(xfer);
586 }
587 return;
588
589 default: /* Error */
590 if (xfer->error != USB_ERR_CANCELLED) {
591 sc->sc_flags |= UCYCOM_FLAG_INTR_STALL;
592 usb2_transfer_start(sc->sc_xfer[2]);
593 }
594 return;
595
596 }
597}