1 /*- 2 * Copyright (c) 2004 Bernd Walter <ticso@FreeBSD.org> 3 * 4 * $URL: https://devel.bwct.de/svn/projects/ubser/ubser.c $ 5 * $Date: 2004-02-29 01:53:10 +0100 (Sun, 29 Feb 2004) $ 6 * $Author: ticso $ 7 * $Rev: 1127 $ 8 */ 9 10 /*- 11 * SPDX-License-Identifier: BSD-2-Clause 12 * 13 * Copyright (c) 2001-2002, Shunsuke Akiyama <akiyama@jp.FreeBSD.org>. 14 * All rights reserved. 15 * 16 * Redistribution and use in source and binary forms, with or without 17 * modification, are permitted provided that the following conditions 18 * are met: 19 * 1. Redistributions of source code must retain the above copyright 20 * notice, this list of conditions and the following disclaimer. 21 * 2. Redistributions in binary form must reproduce the above copyright 22 * notice, this list of conditions and the following disclaimer in the 23 * documentation and/or other materials provided with the distribution. 24 * 25 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 28 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 35 * SUCH DAMAGE. 36 */ 37 38 /*- 39 * Copyright (c) 2000 The NetBSD Foundation, Inc. 40 * All rights reserved. 41 * 42 * This code is derived from software contributed to The NetBSD Foundation 43 * by Lennart Augustsson (lennart@augustsson.net). 44 * 45 * Redistribution and use in source and binary forms, with or without 46 * modification, are permitted provided that the following conditions 47 * are met: 48 * 1. Redistributions of source code must retain the above copyright 49 * notice, this list of conditions and the following disclaimer. 50 * 2. Redistributions in binary form must reproduce the above copyright 51 * notice, this list of conditions and the following disclaimer in the 52 * documentation and/or other materials provided with the distribution. 53 * 54 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 55 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 56 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 57 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 58 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 59 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 60 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 61 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 62 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 63 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 64 * POSSIBILITY OF SUCH DAMAGE. 65 */ 66 67 /* 68 * BWCT serial adapter driver 69 */ 70 71 #include <sys/stdint.h> 72 #include <sys/stddef.h> 73 #include <sys/param.h> 74 #include <sys/queue.h> 75 #include <sys/types.h> 76 #include <sys/systm.h> 77 #include <sys/kernel.h> 78 #include <sys/bus.h> 79 #include <sys/module.h> 80 #include <sys/lock.h> 81 #include <sys/mutex.h> 82 #include <sys/condvar.h> 83 #include <sys/sysctl.h> 84 #include <sys/sx.h> 85 #include <sys/unistd.h> 86 #include <sys/callout.h> 87 #include <sys/malloc.h> 88 #include <sys/priv.h> 89 90 #include <dev/usb/usb.h> 91 #include <dev/usb/usbdi.h> 92 #include <dev/usb/usbdi_util.h> 93 #include "usbdevs.h" 94 95 #define USB_DEBUG_VAR ubser_debug 96 #include <dev/usb/usb_debug.h> 97 #include <dev/usb/usb_process.h> 98 99 #include <dev/usb/serial/usb_serial.h> 100 101 #define UBSER_UNIT_MAX 32 102 103 /* Vendor Interface Requests */ 104 #define VENDOR_GET_NUMSER 0x01 105 #define VENDOR_SET_BREAK 0x02 106 #define VENDOR_CLEAR_BREAK 0x03 107 108 #ifdef USB_DEBUG 109 static int ubser_debug = 0; 110 111 static SYSCTL_NODE(_hw_usb, OID_AUTO, ubser, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, 112 "USB ubser"); 113 SYSCTL_INT(_hw_usb_ubser, OID_AUTO, debug, CTLFLAG_RWTUN, 114 &ubser_debug, 0, "ubser debug level"); 115 #endif 116 117 enum { 118 UBSER_BULK_DT_WR, 119 UBSER_BULK_DT_RD, 120 UBSER_N_TRANSFER, 121 }; 122 123 struct ubser_softc { 124 struct ucom_super_softc sc_super_ucom; 125 struct ucom_softc sc_ucom[UBSER_UNIT_MAX]; 126 127 struct usb_xfer *sc_xfer[UBSER_N_TRANSFER]; 128 struct usb_device *sc_udev; 129 struct mtx sc_mtx; 130 131 uint16_t sc_tx_size; 132 133 uint8_t sc_numser; 134 uint8_t sc_iface_no; 135 uint8_t sc_iface_index; 136 uint8_t sc_curr_tx_unit; 137 }; 138 139 /* prototypes */ 140 141 static device_probe_t ubser_probe; 142 static device_attach_t ubser_attach; 143 static device_detach_t ubser_detach; 144 static void ubser_free_softc(struct ubser_softc *); 145 146 static usb_callback_t ubser_write_callback; 147 static usb_callback_t ubser_read_callback; 148 149 static void ubser_free(struct ucom_softc *); 150 static int ubser_pre_param(struct ucom_softc *, struct termios *); 151 static void ubser_cfg_set_break(struct ucom_softc *, uint8_t); 152 static void ubser_cfg_get_status(struct ucom_softc *, uint8_t *, 153 uint8_t *); 154 static void ubser_start_read(struct ucom_softc *); 155 static void ubser_stop_read(struct ucom_softc *); 156 static void ubser_start_write(struct ucom_softc *); 157 static void ubser_stop_write(struct ucom_softc *); 158 static void ubser_poll(struct ucom_softc *ucom); 159 160 static const struct usb_config ubser_config[UBSER_N_TRANSFER] = { 161 [UBSER_BULK_DT_WR] = { 162 .type = UE_BULK, 163 .endpoint = UE_ADDR_ANY, 164 .direction = UE_DIR_OUT, 165 .bufsize = 0, /* use wMaxPacketSize */ 166 .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, 167 .callback = &ubser_write_callback, 168 }, 169 170 [UBSER_BULK_DT_RD] = { 171 .type = UE_BULK, 172 .endpoint = UE_ADDR_ANY, 173 .direction = UE_DIR_IN, 174 .bufsize = 0, /* use wMaxPacketSize */ 175 .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, 176 .callback = &ubser_read_callback, 177 }, 178 }; 179 180 static const struct ucom_callback ubser_callback = { 181 .ucom_cfg_set_break = &ubser_cfg_set_break, 182 .ucom_cfg_get_status = &ubser_cfg_get_status, 183 .ucom_pre_param = &ubser_pre_param, 184 .ucom_start_read = &ubser_start_read, 185 .ucom_stop_read = &ubser_stop_read, 186 .ucom_start_write = &ubser_start_write, 187 .ucom_stop_write = &ubser_stop_write, 188 .ucom_poll = &ubser_poll, 189 .ucom_free = &ubser_free, 190 }; 191 192 static device_method_t ubser_methods[] = { 193 DEVMETHOD(device_probe, ubser_probe), 194 DEVMETHOD(device_attach, ubser_attach), 195 DEVMETHOD(device_detach, ubser_detach), 196 DEVMETHOD_END 197 }; 198 199 static driver_t ubser_driver = { 200 .name = "ubser", 201 .methods = ubser_methods, 202 .size = sizeof(struct ubser_softc), 203 }; 204 205 DRIVER_MODULE(ubser, uhub, ubser_driver, NULL, NULL); 206 MODULE_DEPEND(ubser, ucom, 1, 1, 1); 207 MODULE_DEPEND(ubser, usb, 1, 1, 1); 208 MODULE_VERSION(ubser, 1); 209 210 static int 211 ubser_probe(device_t dev) 212 { 213 struct usb_attach_arg *uaa = device_get_ivars(dev); 214 215 if (uaa->usb_mode != USB_MODE_HOST) { 216 return (ENXIO); 217 } 218 /* check if this is a BWCT vendor specific ubser interface */ 219 if ((strcmp(usb_get_manufacturer(uaa->device), "BWCT") == 0) && 220 (uaa->info.bInterfaceClass == 0xff) && 221 (uaa->info.bInterfaceSubClass == 0x00)) 222 return (0); 223 224 return (ENXIO); 225 } 226 227 static int 228 ubser_attach(device_t dev) 229 { 230 struct usb_attach_arg *uaa = device_get_ivars(dev); 231 struct ubser_softc *sc = device_get_softc(dev); 232 struct usb_device_request req; 233 uint8_t n; 234 int error; 235 236 device_set_usb_desc(dev); 237 mtx_init(&sc->sc_mtx, "ubser", NULL, MTX_DEF); 238 ucom_ref(&sc->sc_super_ucom); 239 240 sc->sc_iface_no = uaa->info.bIfaceNum; 241 sc->sc_iface_index = uaa->info.bIfaceIndex; 242 sc->sc_udev = uaa->device; 243 244 /* get number of serials */ 245 req.bmRequestType = UT_READ_VENDOR_INTERFACE; 246 req.bRequest = VENDOR_GET_NUMSER; 247 USETW(req.wValue, 0); 248 req.wIndex[0] = sc->sc_iface_no; 249 req.wIndex[1] = 0; 250 USETW(req.wLength, 1); 251 error = usbd_do_request_flags(uaa->device, NULL, 252 &req, &sc->sc_numser, 253 0, NULL, USB_DEFAULT_TIMEOUT); 254 255 if (error || (sc->sc_numser == 0)) { 256 device_printf(dev, "failed to get number " 257 "of serial ports: %s\n", 258 usbd_errstr(error)); 259 goto detach; 260 } 261 if (sc->sc_numser > UBSER_UNIT_MAX) 262 sc->sc_numser = UBSER_UNIT_MAX; 263 264 device_printf(dev, "found %i serials\n", sc->sc_numser); 265 266 error = usbd_transfer_setup(uaa->device, &sc->sc_iface_index, 267 sc->sc_xfer, ubser_config, UBSER_N_TRANSFER, sc, &sc->sc_mtx); 268 if (error) { 269 goto detach; 270 } 271 sc->sc_tx_size = usbd_xfer_max_len(sc->sc_xfer[UBSER_BULK_DT_WR]); 272 273 if (sc->sc_tx_size == 0) { 274 DPRINTFN(0, "invalid tx_size\n"); 275 goto detach; 276 } 277 /* initialize port numbers */ 278 279 for (n = 0; n < sc->sc_numser; n++) { 280 sc->sc_ucom[n].sc_portno = n; 281 } 282 283 error = ucom_attach(&sc->sc_super_ucom, sc->sc_ucom, 284 sc->sc_numser, sc, &ubser_callback, &sc->sc_mtx); 285 if (error) { 286 goto detach; 287 } 288 ucom_set_pnpinfo_usb(&sc->sc_super_ucom, dev); 289 290 mtx_lock(&sc->sc_mtx); 291 usbd_xfer_set_stall(sc->sc_xfer[UBSER_BULK_DT_WR]); 292 usbd_xfer_set_stall(sc->sc_xfer[UBSER_BULK_DT_RD]); 293 usbd_transfer_start(sc->sc_xfer[UBSER_BULK_DT_RD]); 294 mtx_unlock(&sc->sc_mtx); 295 296 return (0); /* success */ 297 298 detach: 299 ubser_detach(dev); 300 return (ENXIO); /* failure */ 301 } 302 303 static int 304 ubser_detach(device_t dev) 305 { 306 struct ubser_softc *sc = device_get_softc(dev); 307 308 DPRINTF("\n"); 309 310 ucom_detach(&sc->sc_super_ucom, sc->sc_ucom); 311 usbd_transfer_unsetup(sc->sc_xfer, UBSER_N_TRANSFER); 312 313 device_claim_softc(dev); 314 315 ubser_free_softc(sc); 316 317 return (0); 318 } 319 320 UCOM_UNLOAD_DRAIN(ubser); 321 322 static void 323 ubser_free_softc(struct ubser_softc *sc) 324 { 325 if (ucom_unref(&sc->sc_super_ucom)) { 326 mtx_destroy(&sc->sc_mtx); 327 device_free_softc(sc); 328 } 329 } 330 331 static void 332 ubser_free(struct ucom_softc *ucom) 333 { 334 ubser_free_softc(ucom->sc_parent); 335 } 336 337 static int 338 ubser_pre_param(struct ucom_softc *ucom, struct termios *t) 339 { 340 DPRINTF("\n"); 341 342 /* 343 * The firmware on our devices can only do 8n1@9600bps 344 * without handshake. 345 * We refuse to accept other configurations. 346 */ 347 348 /* ensure 9600bps */ 349 switch (t->c_ospeed) { 350 case 9600: 351 break; 352 default: 353 return (EINVAL); 354 } 355 356 /* 2 stop bits not possible */ 357 if (t->c_cflag & CSTOPB) 358 return (EINVAL); 359 360 /* XXX parity handling not possible with current firmware */ 361 if (t->c_cflag & PARENB) 362 return (EINVAL); 363 364 /* we can only do 8 data bits */ 365 switch (t->c_cflag & CSIZE) { 366 case CS8: 367 break; 368 default: 369 return (EINVAL); 370 } 371 372 /* we can't do any kind of hardware handshaking */ 373 if ((t->c_cflag & 374 (CRTS_IFLOW | CDTR_IFLOW | CDSR_OFLOW | CCAR_OFLOW)) != 0) 375 return (EINVAL); 376 377 /* 378 * XXX xon/xoff not supported by the firmware! 379 * This is handled within FreeBSD only and may overflow buffers 380 * because of delayed reaction due to device buffering. 381 */ 382 383 return (0); 384 } 385 386 static __inline void 387 ubser_inc_tx_unit(struct ubser_softc *sc) 388 { 389 sc->sc_curr_tx_unit++; 390 if (sc->sc_curr_tx_unit >= sc->sc_numser) { 391 sc->sc_curr_tx_unit = 0; 392 } 393 } 394 395 static void 396 ubser_write_callback(struct usb_xfer *xfer, usb_error_t error) 397 { 398 struct ubser_softc *sc = usbd_xfer_softc(xfer); 399 struct usb_page_cache *pc; 400 uint8_t buf[1]; 401 uint8_t first_unit = sc->sc_curr_tx_unit; 402 uint32_t actlen; 403 404 switch (USB_GET_STATE(xfer)) { 405 case USB_ST_SETUP: 406 case USB_ST_TRANSFERRED: 407 tr_setup: 408 pc = usbd_xfer_get_frame(xfer, 0); 409 do { 410 if (ucom_get_data(sc->sc_ucom + sc->sc_curr_tx_unit, 411 pc, 1, sc->sc_tx_size - 1, 412 &actlen)) { 413 buf[0] = sc->sc_curr_tx_unit; 414 415 usbd_copy_in(pc, 0, buf, 1); 416 417 usbd_xfer_set_frame_len(xfer, 0, actlen + 1); 418 usbd_transfer_submit(xfer); 419 420 ubser_inc_tx_unit(sc); /* round robin */ 421 422 break; 423 } 424 ubser_inc_tx_unit(sc); 425 426 } while (sc->sc_curr_tx_unit != first_unit); 427 428 return; 429 430 default: /* Error */ 431 if (error != USB_ERR_CANCELLED) { 432 /* try to clear stall first */ 433 usbd_xfer_set_stall(xfer); 434 goto tr_setup; 435 } 436 return; 437 } 438 } 439 440 static void 441 ubser_read_callback(struct usb_xfer *xfer, usb_error_t error) 442 { 443 struct ubser_softc *sc = usbd_xfer_softc(xfer); 444 struct usb_page_cache *pc; 445 uint8_t buf[1]; 446 int actlen; 447 448 usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); 449 450 switch (USB_GET_STATE(xfer)) { 451 case USB_ST_TRANSFERRED: 452 if (actlen < 1) { 453 DPRINTF("invalid actlen=0!\n"); 454 goto tr_setup; 455 } 456 pc = usbd_xfer_get_frame(xfer, 0); 457 usbd_copy_out(pc, 0, buf, 1); 458 459 if (buf[0] >= sc->sc_numser) { 460 DPRINTF("invalid serial number!\n"); 461 goto tr_setup; 462 } 463 ucom_put_data(sc->sc_ucom + buf[0], pc, 1, actlen - 1); 464 465 case USB_ST_SETUP: 466 tr_setup: 467 usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); 468 usbd_transfer_submit(xfer); 469 return; 470 471 default: /* Error */ 472 if (error != USB_ERR_CANCELLED) { 473 /* try to clear stall first */ 474 usbd_xfer_set_stall(xfer); 475 goto tr_setup; 476 } 477 return; 478 } 479 } 480 481 static void 482 ubser_cfg_set_break(struct ucom_softc *ucom, uint8_t onoff) 483 { 484 struct ubser_softc *sc = ucom->sc_parent; 485 uint8_t x = ucom->sc_portno; 486 struct usb_device_request req; 487 usb_error_t err; 488 489 if (onoff) { 490 req.bmRequestType = UT_READ_VENDOR_INTERFACE; 491 req.bRequest = VENDOR_SET_BREAK; 492 req.wValue[0] = x; 493 req.wValue[1] = 0; 494 req.wIndex[0] = sc->sc_iface_no; 495 req.wIndex[1] = 0; 496 USETW(req.wLength, 0); 497 498 err = ucom_cfg_do_request(sc->sc_udev, ucom, 499 &req, NULL, 0, 1000); 500 if (err) { 501 DPRINTFN(0, "send break failed, error=%s\n", 502 usbd_errstr(err)); 503 } 504 } 505 } 506 507 static void 508 ubser_cfg_get_status(struct ucom_softc *ucom, uint8_t *lsr, uint8_t *msr) 509 { 510 /* fake status bits */ 511 *lsr = 0; 512 *msr = SER_DCD; 513 } 514 515 static void 516 ubser_start_read(struct ucom_softc *ucom) 517 { 518 struct ubser_softc *sc = ucom->sc_parent; 519 520 usbd_transfer_start(sc->sc_xfer[UBSER_BULK_DT_RD]); 521 } 522 523 static void 524 ubser_stop_read(struct ucom_softc *ucom) 525 { 526 struct ubser_softc *sc = ucom->sc_parent; 527 528 usbd_transfer_stop(sc->sc_xfer[UBSER_BULK_DT_RD]); 529 } 530 531 static void 532 ubser_start_write(struct ucom_softc *ucom) 533 { 534 struct ubser_softc *sc = ucom->sc_parent; 535 536 usbd_transfer_start(sc->sc_xfer[UBSER_BULK_DT_WR]); 537 } 538 539 static void 540 ubser_stop_write(struct ucom_softc *ucom) 541 { 542 struct ubser_softc *sc = ucom->sc_parent; 543 544 usbd_transfer_stop(sc->sc_xfer[UBSER_BULK_DT_WR]); 545 } 546 547 static void 548 ubser_poll(struct ucom_softc *ucom) 549 { 550 struct ubser_softc *sc = ucom->sc_parent; 551 usbd_transfer_poll(sc->sc_xfer, UBSER_N_TRANSFER); 552 } 553