3 4/*- 5 * Copyright (c) 2003 Scott Long 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 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 * 29 */ 30 31/* 32 * Driver for the MCT (Magic Control Technology) USB-RS232 Converter. 33 * Based on the superb documentation from the linux mct_u232 driver by 34 * Wolfgang Grandeggar <wolfgang@cec.ch>. 35 * This device smells a lot like the Belkin F5U103, except that it has 36 * suffered some mild brain-damage. This driver is based off of the ubsa.c 37 * driver from Alexander Kabaev <kan@FreeBSD.org>. Merging the two together 38 * might be useful, though the subtle differences might lead to lots of 39 * #ifdef's. 40 */ 41 42/* 43 * NOTE: all function names beginning like "umct_cfg_" can only 44 * be called from within the config thread function ! 45 */ 46 47#include <sys/stdint.h> 48#include <sys/stddef.h> 49#include <sys/param.h> 50#include <sys/queue.h> 51#include <sys/types.h> 52#include <sys/systm.h> 53#include <sys/kernel.h> 54#include <sys/bus.h> 55#include <sys/linker_set.h> 56#include <sys/module.h> 57#include <sys/lock.h> 58#include <sys/mutex.h> 59#include <sys/condvar.h> 60#include <sys/sysctl.h> 61#include <sys/sx.h> 62#include <sys/unistd.h> 63#include <sys/callout.h> 64#include <sys/malloc.h> 65#include <sys/priv.h> 66 67#include <dev/usb/usb.h> 68#include <dev/usb/usbdi.h> 69#include <dev/usb/usbdi_util.h> 70#include "usbdevs.h" 71 72#define USB_DEBUG_VAR usb_debug 73#include <dev/usb/usb_debug.h> 74#include <dev/usb/usb_process.h> 75 76#include <dev/usb/serial/usb_serial.h> 77 78/* The UMCT advertises the standard 8250 UART registers */ 79#define UMCT_GET_MSR 2 /* Get Modem Status Register */ 80#define UMCT_GET_MSR_SIZE 1 81#define UMCT_GET_LCR 6 /* Get Line Control Register */ 82#define UMCT_GET_LCR_SIZE 1 83#define UMCT_SET_BAUD 5 /* Set the Baud Rate Divisor */ 84#define UMCT_SET_BAUD_SIZE 4 85#define UMCT_SET_LCR 7 /* Set Line Control Register */ 86#define UMCT_SET_LCR_SIZE 1 87#define UMCT_SET_MCR 10 /* Set Modem Control Register */ 88#define UMCT_SET_MCR_SIZE 1 89 90#define UMCT_INTR_INTERVAL 100 91#define UMCT_IFACE_INDEX 0 92#define UMCT_CONFIG_INDEX 0 93 94enum { 95 UMCT_BULK_DT_WR, 96 UMCT_BULK_DT_RD, 97 UMCT_INTR_DT_RD, 98 UMCT_N_TRANSFER, 99}; 100 101struct umct_softc { 102 struct ucom_super_softc sc_super_ucom; 103 struct ucom_softc sc_ucom; 104 105 struct usb_device *sc_udev; 106 struct usb_xfer *sc_xfer[UMCT_N_TRANSFER]; 107 struct mtx sc_mtx; 108 109 uint32_t sc_unit; 110 111 uint16_t sc_obufsize; 112 113 uint8_t sc_lsr; 114 uint8_t sc_msr; 115 uint8_t sc_lcr; 116 uint8_t sc_mcr; 117 uint8_t sc_iface_no; 118 uint8_t sc_swap_cb; 119 uint8_t sc_name[16]; 120}; 121 122/* prototypes */ 123 124static device_probe_t umct_probe; 125static device_attach_t umct_attach; 126static device_detach_t umct_detach; 127 128static usb_callback_t umct_intr_callback; 129static usb_callback_t umct_intr_callback_sub; 130static usb_callback_t umct_read_callback; 131static usb_callback_t umct_read_callback_sub; 132static usb_callback_t umct_write_callback; 133 134static void umct_cfg_do_request(struct umct_softc *sc, uint8_t request, 135 uint16_t len, uint32_t value); 136static void umct_cfg_get_status(struct ucom_softc *, uint8_t *, 137 uint8_t *); 138static void umct_cfg_set_break(struct ucom_softc *, uint8_t); 139static void umct_cfg_set_dtr(struct ucom_softc *, uint8_t); 140static void umct_cfg_set_rts(struct ucom_softc *, uint8_t); 141static uint8_t umct_calc_baud(uint32_t); 142static int umct_pre_param(struct ucom_softc *, struct termios *); 143static void umct_cfg_param(struct ucom_softc *, struct termios *); 144static void umct_start_read(struct ucom_softc *); 145static void umct_stop_read(struct ucom_softc *); 146static void umct_start_write(struct ucom_softc *); 147static void umct_stop_write(struct ucom_softc *); 148static void umct_poll(struct ucom_softc *ucom); 149 150static const struct usb_config umct_config[UMCT_N_TRANSFER] = { 151 152 [UMCT_BULK_DT_WR] = { 153 .type = UE_BULK, 154 .endpoint = UE_ADDR_ANY, 155 .direction = UE_DIR_OUT, 156 .bufsize = 0, /* use wMaxPacketSize */ 157 .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, 158 .callback = &umct_write_callback, 159 }, 160 161 [UMCT_BULK_DT_RD] = { 162 .type = UE_INTERRUPT, 163 .endpoint = UE_ADDR_ANY, 164 .direction = UE_DIR_IN, 165 .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, 166 .bufsize = 0, /* use wMaxPacketSize */ 167 .callback = &umct_read_callback, 168 .ep_index = 0, /* first interrupt endpoint */ 169 }, 170 171 [UMCT_INTR_DT_RD] = { 172 .type = UE_INTERRUPT, 173 .endpoint = UE_ADDR_ANY, 174 .direction = UE_DIR_IN, 175 .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, 176 .bufsize = 0, /* use wMaxPacketSize */ 177 .callback = &umct_intr_callback, 178 .ep_index = 1, /* second interrupt endpoint */ 179 }, 180}; 181 182static const struct ucom_callback umct_callback = { 183 .ucom_cfg_get_status = &umct_cfg_get_status, 184 .ucom_cfg_set_dtr = &umct_cfg_set_dtr, 185 .ucom_cfg_set_rts = &umct_cfg_set_rts, 186 .ucom_cfg_set_break = &umct_cfg_set_break, 187 .ucom_cfg_param = &umct_cfg_param, 188 .ucom_pre_param = &umct_pre_param, 189 .ucom_start_read = &umct_start_read, 190 .ucom_stop_read = &umct_stop_read, 191 .ucom_start_write = &umct_start_write, 192 .ucom_stop_write = &umct_stop_write, 193 .ucom_poll = &umct_poll, 194}; 195 196static const struct usb_device_id umct_devs[] = { 197 {USB_VPI(USB_VENDOR_MCT, USB_PRODUCT_MCT_USB232, 0)}, 198 {USB_VPI(USB_VENDOR_MCT, USB_PRODUCT_MCT_SITECOM_USB232, 0)}, 199 {USB_VPI(USB_VENDOR_MCT, USB_PRODUCT_MCT_DU_H3SP_USB232, 0)}, 200 {USB_VPI(USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5U109, 0)}, 201 {USB_VPI(USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5U409, 0)}, 202}; 203 204static device_method_t umct_methods[] = { 205 DEVMETHOD(device_probe, umct_probe), 206 DEVMETHOD(device_attach, umct_attach), 207 DEVMETHOD(device_detach, umct_detach), 208 {0, 0} 209}; 210 211static devclass_t umct_devclass; 212 213static driver_t umct_driver = { 214 .name = "umct", 215 .methods = umct_methods, 216 .size = sizeof(struct umct_softc), 217}; 218 219DRIVER_MODULE(umct, uhub, umct_driver, umct_devclass, NULL, 0); 220MODULE_DEPEND(umct, ucom, 1, 1, 1); 221MODULE_DEPEND(umct, usb, 1, 1, 1);
|
223 224static int 225umct_probe(device_t dev) 226{ 227 struct usb_attach_arg *uaa = device_get_ivars(dev); 228 229 if (uaa->usb_mode != USB_MODE_HOST) { 230 return (ENXIO); 231 } 232 if (uaa->info.bConfigIndex != UMCT_CONFIG_INDEX) { 233 return (ENXIO); 234 } 235 if (uaa->info.bIfaceIndex != UMCT_IFACE_INDEX) { 236 return (ENXIO); 237 } 238 return (usbd_lookup_id_by_uaa(umct_devs, sizeof(umct_devs), uaa)); 239} 240 241static int 242umct_attach(device_t dev) 243{ 244 struct usb_attach_arg *uaa = device_get_ivars(dev); 245 struct umct_softc *sc = device_get_softc(dev); 246 int32_t error; 247 uint16_t maxp; 248 uint8_t iface_index; 249 250 sc->sc_udev = uaa->device; 251 sc->sc_unit = device_get_unit(dev); 252 253 device_set_usb_desc(dev); 254 mtx_init(&sc->sc_mtx, "umct", NULL, MTX_DEF); 255 256 snprintf(sc->sc_name, sizeof(sc->sc_name), 257 "%s", device_get_nameunit(dev)); 258 259 sc->sc_iface_no = uaa->info.bIfaceNum; 260 261 iface_index = UMCT_IFACE_INDEX; 262 error = usbd_transfer_setup(uaa->device, &iface_index, 263 sc->sc_xfer, umct_config, UMCT_N_TRANSFER, sc, &sc->sc_mtx); 264 265 if (error) { 266 device_printf(dev, "allocating USB " 267 "transfers failed\n"); 268 goto detach; 269 } 270 271 /* 272 * The real bulk-in endpoint is also marked as an interrupt. 273 * The only way to differentiate it from the real interrupt 274 * endpoint is to look at the wMaxPacketSize field. 275 */ 276 maxp = usbd_xfer_max_framelen(sc->sc_xfer[UMCT_BULK_DT_RD]); 277 if (maxp == 0x2) { 278 279 /* guessed wrong - switch around endpoints */ 280 281 struct usb_xfer *temp = sc->sc_xfer[UMCT_INTR_DT_RD]; 282 283 sc->sc_xfer[UMCT_INTR_DT_RD] = sc->sc_xfer[UMCT_BULK_DT_RD]; 284 sc->sc_xfer[UMCT_BULK_DT_RD] = temp; 285 sc->sc_swap_cb = 1; 286 } 287 288 sc->sc_obufsize = usbd_xfer_max_len(sc->sc_xfer[UMCT_BULK_DT_WR]); 289 290 if (uaa->info.idProduct == USB_PRODUCT_MCT_SITECOM_USB232) { 291 if (sc->sc_obufsize > 16) { 292 sc->sc_obufsize = 16; 293 } 294 } 295 error = ucom_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc, 296 &umct_callback, &sc->sc_mtx); 297 if (error) { 298 goto detach; 299 } 300 return (0); /* success */ 301 302detach: 303 umct_detach(dev); 304 return (ENXIO); /* failure */ 305} 306 307static int 308umct_detach(device_t dev) 309{ 310 struct umct_softc *sc = device_get_softc(dev); 311 312 ucom_detach(&sc->sc_super_ucom, &sc->sc_ucom, 1); 313 usbd_transfer_unsetup(sc->sc_xfer, UMCT_N_TRANSFER); 314 mtx_destroy(&sc->sc_mtx); 315 316 return (0); 317} 318 319static void 320umct_cfg_do_request(struct umct_softc *sc, uint8_t request, 321 uint16_t len, uint32_t value) 322{ 323 struct usb_device_request req; 324 usb_error_t err; 325 uint8_t temp[4]; 326 327 if (len > 4) 328 len = 4; 329 req.bmRequestType = UT_WRITE_VENDOR_DEVICE; 330 req.bRequest = request; 331 USETW(req.wValue, 0); 332 req.wIndex[0] = sc->sc_iface_no; 333 req.wIndex[1] = 0; 334 USETW(req.wLength, len); 335 USETDW(temp, value); 336 337 err = ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 338 &req, temp, 0, 1000); 339 if (err) { 340 DPRINTFN(0, "device request failed, err=%s " 341 "(ignored)\n", usbd_errstr(err)); 342 } 343 return; 344} 345 346static void 347umct_intr_callback_sub(struct usb_xfer *xfer, usb_error_t error) 348{ 349 struct umct_softc *sc = usbd_xfer_softc(xfer); 350 struct usb_page_cache *pc; 351 uint8_t buf[2]; 352 int actlen; 353 354 usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); 355 356 switch (USB_GET_STATE(xfer)) { 357 case USB_ST_TRANSFERRED: 358 if (actlen < 2) { 359 DPRINTF("too short message\n"); 360 goto tr_setup; 361 } 362 pc = usbd_xfer_get_frame(xfer, 0); 363 usbd_copy_out(pc, 0, buf, sizeof(buf)); 364 365 sc->sc_msr = buf[0]; 366 sc->sc_lsr = buf[1]; 367 368 ucom_status_change(&sc->sc_ucom); 369 370 case USB_ST_SETUP: 371tr_setup: 372 usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); 373 usbd_transfer_submit(xfer); 374 return; 375 376 default: /* Error */ 377 if (error != USB_ERR_CANCELLED) { 378 /* try to clear stall first */ 379 usbd_xfer_set_stall(xfer); 380 goto tr_setup; 381 } 382 return; 383 } 384} 385 386static void 387umct_cfg_get_status(struct ucom_softc *ucom, uint8_t *lsr, uint8_t *msr) 388{ 389 struct umct_softc *sc = ucom->sc_parent; 390 391 *lsr = sc->sc_lsr; 392 *msr = sc->sc_msr; 393} 394 395static void 396umct_cfg_set_break(struct ucom_softc *ucom, uint8_t onoff) 397{ 398 struct umct_softc *sc = ucom->sc_parent; 399 400 if (onoff) 401 sc->sc_lcr |= 0x40; 402 else 403 sc->sc_lcr &= ~0x40; 404 405 umct_cfg_do_request(sc, UMCT_SET_LCR, UMCT_SET_LCR_SIZE, sc->sc_lcr); 406} 407 408static void 409umct_cfg_set_dtr(struct ucom_softc *ucom, uint8_t onoff) 410{ 411 struct umct_softc *sc = ucom->sc_parent; 412 413 if (onoff) 414 sc->sc_mcr |= 0x01; 415 else 416 sc->sc_mcr &= ~0x01; 417 418 umct_cfg_do_request(sc, UMCT_SET_MCR, UMCT_SET_MCR_SIZE, sc->sc_mcr); 419} 420 421static void 422umct_cfg_set_rts(struct ucom_softc *ucom, uint8_t onoff) 423{ 424 struct umct_softc *sc = ucom->sc_parent; 425 426 if (onoff) 427 sc->sc_mcr |= 0x02; 428 else 429 sc->sc_mcr &= ~0x02; 430 431 umct_cfg_do_request(sc, UMCT_SET_MCR, UMCT_SET_MCR_SIZE, sc->sc_mcr); 432} 433 434static uint8_t 435umct_calc_baud(uint32_t baud) 436{ 437 switch (baud) { 438 case B300:return (0x1); 439 case B600: 440 return (0x2); 441 case B1200: 442 return (0x3); 443 case B2400: 444 return (0x4); 445 case B4800: 446 return (0x6); 447 case B9600: 448 return (0x8); 449 case B19200: 450 return (0x9); 451 case B38400: 452 return (0xa); 453 case B57600: 454 return (0xb); 455 case 115200: 456 return (0xc); 457 case B0: 458 default: 459 break; 460 } 461 return (0x0); 462} 463 464static int 465umct_pre_param(struct ucom_softc *ucom, struct termios *t) 466{ 467 return (0); /* we accept anything */ 468} 469 470static void 471umct_cfg_param(struct ucom_softc *ucom, struct termios *t) 472{ 473 struct umct_softc *sc = ucom->sc_parent; 474 uint32_t value; 475 476 value = umct_calc_baud(t->c_ospeed); 477 umct_cfg_do_request(sc, UMCT_SET_BAUD, UMCT_SET_BAUD_SIZE, value); 478 479 value = (sc->sc_lcr & 0x40); 480 481 switch (t->c_cflag & CSIZE) { 482 case CS5: 483 value |= 0x0; 484 break; 485 case CS6: 486 value |= 0x1; 487 break; 488 case CS7: 489 value |= 0x2; 490 break; 491 default: 492 case CS8: 493 value |= 0x3; 494 break; 495 } 496 497 value |= (t->c_cflag & CSTOPB) ? 0x4 : 0; 498 if (t->c_cflag & PARENB) { 499 value |= 0x8; 500 value |= (t->c_cflag & PARODD) ? 0x0 : 0x10; 501 } 502 /* 503 * XXX There doesn't seem to be a way to tell the device 504 * to use flow control. 505 */ 506 507 sc->sc_lcr = value; 508 umct_cfg_do_request(sc, UMCT_SET_LCR, UMCT_SET_LCR_SIZE, value); 509} 510 511static void 512umct_start_read(struct ucom_softc *ucom) 513{ 514 struct umct_softc *sc = ucom->sc_parent; 515 516 /* start interrupt endpoint */ 517 usbd_transfer_start(sc->sc_xfer[UMCT_INTR_DT_RD]); 518 519 /* start read endpoint */ 520 usbd_transfer_start(sc->sc_xfer[UMCT_BULK_DT_RD]); 521} 522 523static void 524umct_stop_read(struct ucom_softc *ucom) 525{ 526 struct umct_softc *sc = ucom->sc_parent; 527 528 /* stop interrupt endpoint */ 529 usbd_transfer_stop(sc->sc_xfer[UMCT_INTR_DT_RD]); 530 531 /* stop read endpoint */ 532 usbd_transfer_stop(sc->sc_xfer[UMCT_BULK_DT_RD]); 533} 534 535static void 536umct_start_write(struct ucom_softc *ucom) 537{ 538 struct umct_softc *sc = ucom->sc_parent; 539 540 usbd_transfer_start(sc->sc_xfer[UMCT_BULK_DT_WR]); 541} 542 543static void 544umct_stop_write(struct ucom_softc *ucom) 545{ 546 struct umct_softc *sc = ucom->sc_parent; 547 548 usbd_transfer_stop(sc->sc_xfer[UMCT_BULK_DT_WR]); 549} 550 551static void 552umct_read_callback(struct usb_xfer *xfer, usb_error_t error) 553{ 554 struct umct_softc *sc = usbd_xfer_softc(xfer); 555 556 if (sc->sc_swap_cb) 557 umct_intr_callback_sub(xfer, error); 558 else 559 umct_read_callback_sub(xfer, error); 560} 561 562static void 563umct_intr_callback(struct usb_xfer *xfer, usb_error_t error) 564{ 565 struct umct_softc *sc = usbd_xfer_softc(xfer); 566 567 if (sc->sc_swap_cb) 568 umct_read_callback_sub(xfer, error); 569 else 570 umct_intr_callback_sub(xfer, error); 571} 572 573static void 574umct_write_callback(struct usb_xfer *xfer, usb_error_t error) 575{ 576 struct umct_softc *sc = usbd_xfer_softc(xfer); 577 struct usb_page_cache *pc; 578 uint32_t actlen; 579 580 switch (USB_GET_STATE(xfer)) { 581 case USB_ST_SETUP: 582 case USB_ST_TRANSFERRED: 583tr_setup: 584 pc = usbd_xfer_get_frame(xfer, 0); 585 if (ucom_get_data(&sc->sc_ucom, pc, 0, 586 sc->sc_obufsize, &actlen)) { 587 588 usbd_xfer_set_frame_len(xfer, 0, actlen); 589 usbd_transfer_submit(xfer); 590 } 591 return; 592 593 default: /* Error */ 594 if (error != USB_ERR_CANCELLED) { 595 /* try to clear stall first */ 596 usbd_xfer_set_stall(xfer); 597 goto tr_setup; 598 } 599 return; 600 } 601} 602 603static void 604umct_read_callback_sub(struct usb_xfer *xfer, usb_error_t error) 605{ 606 struct umct_softc *sc = usbd_xfer_softc(xfer); 607 struct usb_page_cache *pc; 608 int actlen; 609 610 usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); 611 612 switch (USB_GET_STATE(xfer)) { 613 case USB_ST_TRANSFERRED: 614 pc = usbd_xfer_get_frame(xfer, 0); 615 ucom_put_data(&sc->sc_ucom, pc, 0, actlen); 616 617 case USB_ST_SETUP: 618tr_setup: 619 usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); 620 usbd_transfer_submit(xfer); 621 return; 622 623 default: /* Error */ 624 if (error != USB_ERR_CANCELLED) { 625 /* try to clear stall first */ 626 usbd_xfer_set_stall(xfer); 627 goto tr_setup; 628 } 629 return; 630 } 631} 632 633static void 634umct_poll(struct ucom_softc *ucom) 635{ 636 struct umct_softc *sc = ucom->sc_parent; 637 usbd_transfer_poll(sc->sc_xfer, UMCT_N_TRANSFER); 638}
|