ucycom.c revision 190734
1254721Semaste#include <sys/cdefs.h> 2254721Semaste__FBSDID("$FreeBSD: head/sys/dev/usb/serial/ucycom.c 190734 2009-04-05 18:20:38Z thompsa $"); 3353358Sdim 4353358Sdim/*- 5353358Sdim * Copyright (c) 2004 Dag-Erling Co�dan Sm�rgrav 6254721Semaste * All rights reserved. 7254721Semaste * 8254721Semaste * Redistribution and use in source and binary forms, with or without 9296417Sdim * modification, are permitted provided that the following conditions 10321369Sdim * are met: 11254721Semaste * 1. Redistributions of source code must retain the above copyright 12321369Sdim * notice, this list of conditions and the following disclaimer 13254721Semaste * in this position and unchanged. 14254721Semaste * 2. Redistributions in binary form must reproduce the above copyright 15254721Semaste * notice, this list of conditions and the following disclaimer in the 16254721Semaste * documentation and/or other materials provided with the distribution. 17321369Sdim * 3. The name of the author may not be used to endorse or promote products 18254721Semaste * derived from this software without specific prior written permission. 19254721Semaste * 20254721Semaste * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 21254721Semaste * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 22314564Sdim * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 23314564Sdim * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 24254721Semaste * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 25314564Sdim * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26314564Sdim * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27314564Sdim * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28314564Sdim * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 29314564Sdim * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30314564Sdim */ 31314564Sdim 32314564Sdim/* 33314564Sdim * Device driver for Cypress CY7C637xx and CY7C640/1xx series USB to 34314564Sdim * RS232 bridges. 35314564Sdim */ 36314564Sdim 37314564Sdim#include "usbdevs.h" 38254721Semaste#include <dev/usb/usb.h> 39314564Sdim#include <dev/usb/usb_mfunc.h> 40314564Sdim#include <dev/usb/usb_error.h> 41314564Sdim#include <dev/usb/usb_cdc.h> 42314564Sdim#include <dev/usb/usb_ioctl.h> 43254721Semaste#include <dev/usb/usbhid.h> 44314564Sdim 45314564Sdim#define USB_DEBUG_VAR usb2_debug 46254721Semaste 47314564Sdim#include <dev/usb/usb_core.h> 48314564Sdim#include <dev/usb/usb_debug.h> 49314564Sdim#include <dev/usb/usb_process.h> 50314564Sdim#include <dev/usb/usb_request.h> 51314564Sdim#include <dev/usb/usb_lookup.h> 52314564Sdim#include <dev/usb/usb_util.h> 53344779Sdim#include <dev/usb/usb_busdma.h> 54314564Sdim#include <dev/usb/usb_hid.h> 55344779Sdim 56344779Sdim#include <dev/usb/serial/usb_serial.h> 57314564Sdim 58314564Sdim#define UCYCOM_MAX_IOLEN (1024 + 2) /* bytes */ 59314564Sdim 60314564Sdim#define UCYCOM_IFACE_INDEX 0 61314564Sdim 62254721Semasteenum { 63314564Sdim UCYCOM_CTRL_RD, 64254721Semaste UCYCOM_INTR_RD, 65314564Sdim UCYCOM_N_TRANSFER, 66314564Sdim}; 67314564Sdim 68314564Sdimstruct ucycom_softc { 69314564Sdim struct usb2_com_super_softc sc_super_ucom; 70314564Sdim struct usb2_com_softc sc_ucom; 71314564Sdim 72314564Sdim struct usb2_device *sc_udev; 73314564Sdim struct usb2_xfer *sc_xfer[UCYCOM_N_TRANSFER]; 74314564Sdim struct mtx sc_mtx; 75314564Sdim 76254721Semaste uint32_t sc_model; 77314564Sdim#define MODEL_CY7C63743 0x63743 78254721Semaste#define MODEL_CY7C64013 0x64013 79254721Semaste 80314564Sdim uint16_t sc_flen; /* feature report length */ 81254721Semaste uint16_t sc_ilen; /* input report length */ 82314564Sdim uint16_t sc_olen; /* output report length */ 83314564Sdim 84314564Sdim uint8_t sc_fid; /* feature report id */ 85314564Sdim uint8_t sc_iid; /* input report id */ 86314564Sdim uint8_t sc_oid; /* output report id */ 87314564Sdim uint8_t sc_cfg; 88314564Sdim#define UCYCOM_CFG_RESET 0x80 89254721Semaste#define UCYCOM_CFG_PARODD 0x20 90314564Sdim#define UCYCOM_CFG_PAREN 0x10 91314564Sdim#define UCYCOM_CFG_STOPB 0x08 92314564Sdim#define UCYCOM_CFG_DATAB 0x03 93254721Semaste uint8_t sc_ist; /* status flags from last input */ 94314564Sdim uint8_t sc_name[16]; 95314564Sdim uint8_t sc_iface_no; 96344779Sdim uint8_t sc_temp_cfg[32]; 97254721Semaste}; 98254721Semaste 99314564Sdim/* prototypes */ 100314564Sdim 101314564Sdimstatic device_probe_t ucycom_probe; 102314564Sdimstatic device_attach_t ucycom_attach; 103314564Sdimstatic device_detach_t ucycom_detach; 104314564Sdim 105314564Sdimstatic usb2_callback_t ucycom_ctrl_write_callback; 106314564Sdimstatic usb2_callback_t ucycom_intr_read_callback; 107314564Sdim 108314564Sdimstatic void ucycom_cfg_open(struct usb2_com_softc *); 109314564Sdimstatic void ucycom_start_read(struct usb2_com_softc *); 110314564Sdimstatic void ucycom_stop_read(struct usb2_com_softc *); 111314564Sdimstatic void ucycom_start_write(struct usb2_com_softc *); 112314564Sdimstatic void ucycom_stop_write(struct usb2_com_softc *); 113314564Sdimstatic void ucycom_cfg_write(struct ucycom_softc *, uint32_t, uint8_t); 114314564Sdimstatic int ucycom_pre_param(struct usb2_com_softc *, struct termios *); 115314564Sdimstatic void ucycom_cfg_param(struct usb2_com_softc *, struct termios *); 116314564Sdim 117314564Sdimstatic const struct usb2_config ucycom_config[UCYCOM_N_TRANSFER] = { 118314564Sdim 119314564Sdim [UCYCOM_CTRL_RD] = { 120254721Semaste .type = UE_CONTROL, 121314564Sdim .endpoint = 0x00, /* Control pipe */ 122314564Sdim .direction = UE_DIR_ANY, 123314564Sdim .bufsize = (sizeof(struct usb2_device_request) + UCYCOM_MAX_IOLEN), 124254721Semaste .callback = &ucycom_ctrl_write_callback, 125254721Semaste .timeout = 1000, /* 1 second */ 126314564Sdim }, 127344779Sdim 128344779Sdim [UCYCOM_INTR_RD] = { 129344779Sdim .type = UE_INTERRUPT, 130344779Sdim .endpoint = UE_ADDR_ANY, 131314564Sdim .direction = UE_DIR_IN, 132344779Sdim .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, 133344779Sdim .bufsize = UCYCOM_MAX_IOLEN, 134344779Sdim .callback = &ucycom_intr_read_callback, 135344779Sdim }, 136344779Sdim}; 137314564Sdim 138314564Sdimstatic const struct usb2_com_callback ucycom_callback = { 139314564Sdim .usb2_com_cfg_param = &ucycom_cfg_param, 140254721Semaste .usb2_com_cfg_open = &ucycom_cfg_open, 141254721Semaste .usb2_com_pre_param = &ucycom_pre_param, 142314564Sdim .usb2_com_start_read = &ucycom_start_read, 143314564Sdim .usb2_com_stop_read = &ucycom_stop_read, 144254721Semaste .usb2_com_start_write = &ucycom_start_write, 145254721Semaste .usb2_com_stop_write = &ucycom_stop_write, 146314564Sdim}; 147314564Sdim 148314564Sdimstatic device_method_t ucycom_methods[] = { 149314564Sdim DEVMETHOD(device_probe, ucycom_probe), 150314564Sdim DEVMETHOD(device_attach, ucycom_attach), 151314564Sdim DEVMETHOD(device_detach, ucycom_detach), 152314564Sdim {0, 0} 153314564Sdim}; 154314564Sdim 155314564Sdimstatic devclass_t ucycom_devclass; 156314564Sdim 157314564Sdimstatic driver_t ucycom_driver = { 158341825Sdim .name = "ucycom", 159341825Sdim .methods = ucycom_methods, 160314564Sdim .size = sizeof(struct ucycom_softc), 161314564Sdim}; 162314564Sdim 163314564SdimDRIVER_MODULE(ucycom, uhub, ucycom_driver, ucycom_devclass, NULL, 0); 164314564SdimMODULE_DEPEND(ucycom, ucom, 1, 1, 1); 165254721SemasteMODULE_DEPEND(ucycom, usb, 1, 1, 1); 166314564Sdim 167254721Semaste/* 168314564Sdim * Supported devices 169314564Sdim */ 170341825Sdimstatic const struct usb2_device_id ucycom_devs[] = { 171341825Sdim {USB_VPI(USB_VENDOR_DELORME, USB_PRODUCT_DELORME_EARTHMATE, MODEL_CY7C64013)}, 172341825Sdim}; 173341825Sdim 174341825Sdim#define UCYCOM_DEFAULT_RATE 4800 175341825Sdim#define UCYCOM_DEFAULT_CFG 0x03 /* N-8-1 */ 176341825Sdim 177314564Sdimstatic int 178314564Sdimucycom_probe(device_t dev) 179254721Semaste{ 180314564Sdim struct usb2_attach_arg *uaa = device_get_ivars(dev); 181296417Sdim 182314564Sdim if (uaa->usb2_mode != USB_MODE_HOST) { 183314564Sdim return (ENXIO); 184314564Sdim } 185314564Sdim if (uaa->info.bConfigIndex != 0) { 186314564Sdim return (ENXIO); 187314564Sdim } 188314564Sdim if (uaa->info.bIfaceIndex != UCYCOM_IFACE_INDEX) { 189314564Sdim return (ENXIO); 190314564Sdim } 191314564Sdim return (usb2_lookup_id_by_uaa(ucycom_devs, sizeof(ucycom_devs), uaa)); 192314564Sdim} 193314564Sdim 194314564Sdimstatic int 195314564Sdimucycom_attach(device_t dev) 196314564Sdim{ 197314564Sdim struct usb2_attach_arg *uaa = device_get_ivars(dev); 198314564Sdim struct ucycom_softc *sc = device_get_softc(dev); 199314564Sdim void *urd_ptr = NULL; 200314564Sdim int32_t error; 201314564Sdim uint16_t urd_len; 202314564Sdim uint8_t iface_index; 203314564Sdim 204314564Sdim sc->sc_udev = uaa->device; 205314564Sdim 206314564Sdim device_set_usb2_desc(dev); 207314564Sdim mtx_init(&sc->sc_mtx, "ucycom", NULL, MTX_DEF); 208314564Sdim 209314564Sdim snprintf(sc->sc_name, sizeof(sc->sc_name), 210314564Sdim "%s", device_get_nameunit(dev)); 211341825Sdim 212341825Sdim DPRINTF("\n"); 213314564Sdim 214314564Sdim /* get chip model */ 215314564Sdim sc->sc_model = USB_GET_DRIVER_INFO(uaa); 216314564Sdim if (sc->sc_model == 0) { 217314564Sdim device_printf(dev, "unsupported device\n"); 218314564Sdim goto detach; 219314564Sdim } 220314564Sdim device_printf(dev, "Cypress CY7C%X USB to RS232 bridge\n", sc->sc_model); 221314564Sdim 222314564Sdim /* get report descriptor */ 223296417Sdim 224314564Sdim error = usb2_req_get_hid_desc(uaa->device, NULL, 225314564Sdim &urd_ptr, &urd_len, M_USBDEV, 226314564Sdim UCYCOM_IFACE_INDEX); 227296417Sdim 228314564Sdim if (error) { 229254721Semaste device_printf(dev, "failed to get report " 230314564Sdim "descriptor: %s\n", 231314564Sdim usb2_errstr(error)); 232341825Sdim goto detach; 233341825Sdim } 234314564Sdim /* get report sizes */ 235314564Sdim 236314564Sdim sc->sc_flen = hid_report_size(urd_ptr, urd_len, hid_feature, &sc->sc_fid); 237314564Sdim sc->sc_ilen = hid_report_size(urd_ptr, urd_len, hid_input, &sc->sc_iid); 238314564Sdim sc->sc_olen = hid_report_size(urd_ptr, urd_len, hid_output, &sc->sc_oid); 239254721Semaste 240296417Sdim if ((sc->sc_ilen > UCYCOM_MAX_IOLEN) || (sc->sc_ilen < 1) || 241314564Sdim (sc->sc_olen > UCYCOM_MAX_IOLEN) || (sc->sc_olen < 2) || 242254721Semaste (sc->sc_flen > UCYCOM_MAX_IOLEN) || (sc->sc_flen < 5)) { 243314564Sdim device_printf(dev, "invalid report size i=%d, o=%d, f=%d, max=%d\n", 244341825Sdim sc->sc_ilen, sc->sc_olen, sc->sc_flen, 245341825Sdim UCYCOM_MAX_IOLEN); 246314564Sdim goto detach; 247314564Sdim } 248314564Sdim sc->sc_iface_no = uaa->info.bIfaceNum; 249314564Sdim 250314564Sdim iface_index = UCYCOM_IFACE_INDEX; 251314564Sdim error = usb2_transfer_setup(uaa->device, &iface_index, 252254721Semaste sc->sc_xfer, ucycom_config, UCYCOM_N_TRANSFER, 253314564Sdim sc, &sc->sc_mtx); 254254721Semaste if (error) { 255254721Semaste device_printf(dev, "allocating USB " 256314564Sdim "transfers failed!\n"); 257314564Sdim goto detach; 258341825Sdim } 259314564Sdim error = usb2_com_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc, 260314564Sdim &ucycom_callback, &sc->sc_mtx); 261254721Semaste 262254721Semaste if (error) { 263314564Sdim goto detach; 264341825Sdim } 265341825Sdim if (urd_ptr) { 266341825Sdim free(urd_ptr, M_USBDEV); 267254721Semaste } 268314564Sdim return (0); /* success */ 269314564Sdim 270314564Sdimdetach: 271254721Semaste if (urd_ptr) { 272314564Sdim free(urd_ptr, M_USBDEV); 273314564Sdim } 274254721Semaste ucycom_detach(dev); 275254721Semaste return (ENXIO); 276314564Sdim} 277254721Semaste 278314564Sdimstatic int 279254721Semasteucycom_detach(device_t dev) 280314564Sdim{ 281314564Sdim struct ucycom_softc *sc = device_get_softc(dev); 282314564Sdim 283314564Sdim usb2_com_detach(&sc->sc_super_ucom, &sc->sc_ucom, 1); 284314564Sdim usb2_transfer_unsetup(sc->sc_xfer, UCYCOM_N_TRANSFER); 285314564Sdim mtx_destroy(&sc->sc_mtx); 286314564Sdim 287314564Sdim return (0); 288314564Sdim} 289254721Semaste 290314564Sdimstatic void 291314564Sdimucycom_cfg_open(struct usb2_com_softc *ucom) 292314564Sdim{ 293314564Sdim struct ucycom_softc *sc = ucom->sc_parent; 294314564Sdim 295314564Sdim /* set default configuration */ 296314564Sdim ucycom_cfg_write(sc, UCYCOM_DEFAULT_RATE, UCYCOM_DEFAULT_CFG); 297254721Semaste} 298314564Sdim 299314564Sdimstatic void 300314564Sdimucycom_start_read(struct usb2_com_softc *ucom) 301314564Sdim{ 302314564Sdim struct ucycom_softc *sc = ucom->sc_parent; 303314564Sdim 304254721Semaste usb2_transfer_start(sc->sc_xfer[UCYCOM_INTR_RD]); 305254721Semaste} 306314564Sdim 307314564Sdimstatic void 308314564Sdimucycom_stop_read(struct usb2_com_softc *ucom) 309314564Sdim{ 310314564Sdim struct ucycom_softc *sc = ucom->sc_parent; 311314564Sdim 312254721Semaste usb2_transfer_stop(sc->sc_xfer[UCYCOM_INTR_RD]); 313314564Sdim} 314314564Sdim 315314564Sdimstatic void 316314564Sdimucycom_start_write(struct usb2_com_softc *ucom) 317314564Sdim{ 318254721Semaste struct ucycom_softc *sc = ucom->sc_parent; 319314564Sdim 320314564Sdim usb2_transfer_start(sc->sc_xfer[UCYCOM_CTRL_RD]); 321254721Semaste} 322254721Semaste 323314564Sdimstatic void 324314564Sdimucycom_stop_write(struct usb2_com_softc *ucom) 325314564Sdim{ 326314564Sdim struct ucycom_softc *sc = ucom->sc_parent; 327314564Sdim 328314564Sdim usb2_transfer_stop(sc->sc_xfer[UCYCOM_CTRL_RD]); 329360784Sdim} 330254721Semaste 331314564Sdimstatic void 332314564Sdimucycom_ctrl_write_callback(struct usb2_xfer *xfer) 333314564Sdim{ 334314564Sdim struct ucycom_softc *sc = xfer->priv_sc; 335314564Sdim struct usb2_device_request req; 336254721Semaste uint8_t data[2]; 337314564Sdim uint8_t offset; 338254721Semaste uint32_t actlen; 339 340 switch (USB_GET_STATE(xfer)) { 341 case USB_ST_TRANSFERRED: 342tr_transferred: 343 case USB_ST_SETUP: 344 345 switch (sc->sc_model) { 346 case MODEL_CY7C63743: 347 offset = 1; 348 break; 349 case MODEL_CY7C64013: 350 offset = 2; 351 break; 352 default: 353 offset = 0; 354 break; 355 } 356 357 if (usb2_com_get_data(&sc->sc_ucom, xfer->frbuffers + 1, offset, 358 sc->sc_olen - offset, &actlen)) { 359 360 req.bmRequestType = UT_WRITE_CLASS_INTERFACE; 361 req.bRequest = UR_SET_REPORT; 362 USETW2(req.wValue, UHID_OUTPUT_REPORT, sc->sc_oid); 363 req.wIndex[0] = sc->sc_iface_no; 364 req.wIndex[1] = 0; 365 USETW(req.wLength, sc->sc_olen); 366 367 switch (sc->sc_model) { 368 case MODEL_CY7C63743: 369 data[0] = actlen; 370 break; 371 case MODEL_CY7C64013: 372 data[0] = 0; 373 data[1] = actlen; 374 break; 375 default: 376 break; 377 } 378 379 usb2_copy_in(xfer->frbuffers, 0, &req, sizeof(req)); 380 usb2_copy_in(xfer->frbuffers + 1, 0, data, offset); 381 382 xfer->frlengths[0] = sizeof(req); 383 xfer->frlengths[1] = sc->sc_olen; 384 xfer->nframes = xfer->frlengths[1] ? 2 : 1; 385 usb2_start_hardware(xfer); 386 } 387 return; 388 389 default: /* Error */ 390 if (xfer->error == USB_ERR_CANCELLED) { 391 return; 392 } 393 DPRINTF("error=%s\n", 394 usb2_errstr(xfer->error)); 395 goto tr_transferred; 396 } 397} 398 399static void 400ucycom_cfg_write(struct ucycom_softc *sc, uint32_t baud, uint8_t cfg) 401{ 402 struct usb2_device_request req; 403 uint16_t len; 404 usb2_error_t err; 405 406 len = sc->sc_flen; 407 if (len > sizeof(sc->sc_temp_cfg)) { 408 len = sizeof(sc->sc_temp_cfg); 409 } 410 sc->sc_cfg = cfg; 411 412 req.bmRequestType = UT_WRITE_CLASS_INTERFACE; 413 req.bRequest = UR_SET_REPORT; 414 USETW2(req.wValue, UHID_FEATURE_REPORT, sc->sc_fid); 415 req.wIndex[0] = sc->sc_iface_no; 416 req.wIndex[1] = 0; 417 USETW(req.wLength, len); 418 419 sc->sc_temp_cfg[0] = (baud & 0xff); 420 sc->sc_temp_cfg[1] = (baud >> 8) & 0xff; 421 sc->sc_temp_cfg[2] = (baud >> 16) & 0xff; 422 sc->sc_temp_cfg[3] = (baud >> 24) & 0xff; 423 sc->sc_temp_cfg[4] = cfg; 424 425 err = usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 426 &req, sc->sc_temp_cfg, 0, 1000); 427 if (err) { 428 DPRINTFN(0, "device request failed, err=%s " 429 "(ignored)\n", usb2_errstr(err)); 430 } 431} 432 433static int 434ucycom_pre_param(struct usb2_com_softc *ucom, struct termios *t) 435{ 436 switch (t->c_ospeed) { 437 case 600: 438 case 1200: 439 case 2400: 440 case 4800: 441 case 9600: 442 case 19200: 443 case 38400: 444 case 57600: 445#if 0 446 /* 447 * Stock chips only support standard baud rates in the 600 - 57600 448 * range, but higher rates can be achieved using custom firmware. 449 */ 450 case 115200: 451 case 153600: 452 case 192000: 453#endif 454 break; 455 default: 456 return (EINVAL); 457 } 458 return (0); 459} 460 461static void 462ucycom_cfg_param(struct usb2_com_softc *ucom, struct termios *t) 463{ 464 struct ucycom_softc *sc = ucom->sc_parent; 465 uint8_t cfg; 466 467 DPRINTF("\n"); 468 469 if (t->c_cflag & CIGNORE) { 470 cfg = sc->sc_cfg; 471 } else { 472 cfg = 0; 473 switch (t->c_cflag & CSIZE) { 474 default: 475 case CS8: 476 ++cfg; 477 case CS7: 478 ++cfg; 479 case CS6: 480 ++cfg; 481 case CS5: 482 break; 483 } 484 485 if (t->c_cflag & CSTOPB) 486 cfg |= UCYCOM_CFG_STOPB; 487 if (t->c_cflag & PARENB) 488 cfg |= UCYCOM_CFG_PAREN; 489 if (t->c_cflag & PARODD) 490 cfg |= UCYCOM_CFG_PARODD; 491 } 492 493 ucycom_cfg_write(sc, t->c_ospeed, cfg); 494} 495 496static void 497ucycom_intr_read_callback(struct usb2_xfer *xfer) 498{ 499 struct ucycom_softc *sc = xfer->priv_sc; 500 uint8_t buf[2]; 501 uint32_t offset; 502 uint32_t len; 503 504 switch (USB_GET_STATE(xfer)) { 505 case USB_ST_TRANSFERRED: 506 switch (sc->sc_model) { 507 case MODEL_CY7C63743: 508 if (xfer->actlen < 1) { 509 goto tr_setup; 510 } 511 usb2_copy_out(xfer->frbuffers, 0, buf, 1); 512 513 sc->sc_ist = buf[0] & ~0x07; 514 len = buf[0] & 0x07; 515 516 (xfer->actlen)--; 517 518 offset = 1; 519 520 break; 521 522 case MODEL_CY7C64013: 523 if (xfer->actlen < 2) { 524 goto tr_setup; 525 } 526 usb2_copy_out(xfer->frbuffers, 0, buf, 2); 527 528 sc->sc_ist = buf[0] & ~0x07; 529 len = buf[1]; 530 531 (xfer->actlen) -= 2; 532 533 offset = 2; 534 535 break; 536 537 default: 538 DPRINTFN(0, "unsupported model number!\n"); 539 goto tr_setup; 540 } 541 542 if (len > xfer->actlen) { 543 len = xfer->actlen; 544 } 545 if (len) { 546 usb2_com_put_data(&sc->sc_ucom, xfer->frbuffers, 547 offset, len); 548 } 549 case USB_ST_SETUP: 550tr_setup: 551 xfer->frlengths[0] = sc->sc_ilen; 552 usb2_start_hardware(xfer); 553 return; 554 555 default: /* Error */ 556 if (xfer->error != USB_ERR_CANCELLED) { 557 /* try to clear stall first */ 558 xfer->flags.stall_pipe = 1; 559 goto tr_setup; 560 } 561 return; 562 563 } 564} 565