Deleted Added
sdiff udiff text old ( 185950 ) new ( 187176 )
full compact
1#include <sys/cdefs.h>
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>
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}