1 /* $NetBSD: uchcom.c,v 1.1 2007/09/03 17:57:37 tshiozak Exp $ */ 2 3 /*- 4 * Copyright (c) 2007, Takanori Watanabe 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 /* 30 * Copyright (c) 2007 The NetBSD Foundation, Inc. 31 * All rights reserved. 32 * 33 * This code is derived from software contributed to The NetBSD Foundation 34 * by Takuya SHIOZAKI (tshiozak@netbsd.org). 35 * 36 * Redistribution and use in source and binary forms, with or without 37 * modification, are permitted provided that the following conditions 38 * are met: 39 * 1. Redistributions of source code must retain the above copyright 40 * notice, this list of conditions and the following disclaimer. 41 * 2. Redistributions in binary form must reproduce the above copyright 42 * notice, this list of conditions and the following disclaimer in the 43 * documentation and/or other materials provided with the distribution. 44 * 45 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 46 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 47 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 48 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 49 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 50 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 51 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 52 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 53 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 54 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 55 * POSSIBILITY OF SUCH DAMAGE. 56 */ 57 58 /* 59 * Driver for WinChipHead CH341/340, the worst USB-serial chip in the 60 * world. 61 */ 62 63 #include <sys/stdint.h> 64 #include <sys/param.h> 65 #include <sys/queue.h> 66 #include <sys/types.h> 67 #include <sys/systm.h> 68 #include <sys/kernel.h> 69 #include <sys/bus.h> 70 #include <sys/module.h> 71 #include <sys/lock.h> 72 #include <sys/condvar.h> 73 #include <sys/sysctl.h> 74 #include <sys/unistd.h> 75 #include <sys/callout.h> 76 #include <sys/malloc.h> 77 #include <sys/priv.h> 78 79 #include <bus/u4b/usb.h> 80 #include <bus/u4b/usbdi.h> 81 #include <bus/u4b/usbdi_util.h> 82 83 #include "usbdevs.h" 84 85 #define USB_DEBUG_VAR uchcom_debug 86 #include <bus/u4b/usb_debug.h> 87 #include <bus/u4b/usb_process.h> 88 89 #include <bus/u4b/serial/usb_serial.h> 90 91 #ifdef USB_DEBUG 92 static int uchcom_debug = 0; 93 94 static SYSCTL_NODE(_hw_usb, OID_AUTO, uchcom, CTLFLAG_RW, 0, "USB uchcom"); 95 SYSCTL_INT(_hw_usb_uchcom, OID_AUTO, debug, CTLFLAG_RW, 96 &uchcom_debug, 0, "uchcom debug level"); 97 #endif 98 99 #define UCHCOM_IFACE_INDEX 0 100 #define UCHCOM_CONFIG_INDEX 0 101 102 #define UCHCOM_REV_CH340 0x0250 103 #define UCHCOM_INPUT_BUF_SIZE 8 104 105 #define UCHCOM_REQ_GET_VERSION 0x5F 106 #define UCHCOM_REQ_READ_REG 0x95 107 #define UCHCOM_REQ_WRITE_REG 0x9A 108 #define UCHCOM_REQ_RESET 0xA1 109 #define UCHCOM_REQ_SET_DTRRTS 0xA4 110 111 #define UCHCOM_REG_STAT1 0x06 112 #define UCHCOM_REG_STAT2 0x07 113 #define UCHCOM_REG_BPS_PRE 0x12 114 #define UCHCOM_REG_BPS_DIV 0x13 115 #define UCHCOM_REG_BPS_MOD 0x14 116 #define UCHCOM_REG_BPS_PAD 0x0F 117 #define UCHCOM_REG_BREAK1 0x05 118 #define UCHCOM_REG_BREAK2 0x18 119 #define UCHCOM_REG_LCR1 0x18 120 #define UCHCOM_REG_LCR2 0x25 121 122 #define UCHCOM_VER_20 0x20 123 124 #define UCHCOM_BASE_UNKNOWN 0 125 #define UCHCOM_BPS_MOD_BASE 20000000 126 #define UCHCOM_BPS_MOD_BASE_OFS 1100 127 128 #define UCHCOM_DTR_MASK 0x20 129 #define UCHCOM_RTS_MASK 0x40 130 131 #define UCHCOM_BRK1_MASK 0x01 132 #define UCHCOM_BRK2_MASK 0x40 133 134 #define UCHCOM_LCR1_MASK 0xAF 135 #define UCHCOM_LCR2_MASK 0x07 136 #define UCHCOM_LCR1_PARENB 0x80 137 #define UCHCOM_LCR2_PAREVEN 0x07 138 #define UCHCOM_LCR2_PARODD 0x06 139 #define UCHCOM_LCR2_PARMARK 0x05 140 #define UCHCOM_LCR2_PARSPACE 0x04 141 142 #define UCHCOM_INTR_STAT1 0x02 143 #define UCHCOM_INTR_STAT2 0x03 144 #define UCHCOM_INTR_LEAST 4 145 146 #define UCHCOM_BULK_BUF_SIZE 1024 /* bytes */ 147 148 enum { 149 UCHCOM_BULK_DT_WR, 150 UCHCOM_BULK_DT_RD, 151 UCHCOM_INTR_DT_RD, 152 UCHCOM_N_TRANSFER, 153 }; 154 155 struct uchcom_softc { 156 struct ucom_super_softc sc_super_ucom; 157 struct ucom_softc sc_ucom; 158 159 struct usb_xfer *sc_xfer[UCHCOM_N_TRANSFER]; 160 struct usb_device *sc_udev; 161 struct lock sc_lock; 162 163 uint8_t sc_dtr; /* local copy */ 164 uint8_t sc_rts; /* local copy */ 165 uint8_t sc_version; 166 uint8_t sc_msr; 167 uint8_t sc_lsr; /* local status register */ 168 }; 169 170 struct uchcom_divider { 171 uint8_t dv_prescaler; 172 uint8_t dv_div; 173 uint8_t dv_mod; 174 }; 175 176 struct uchcom_divider_record { 177 uint32_t dvr_high; 178 uint32_t dvr_low; 179 uint32_t dvr_base_clock; 180 struct uchcom_divider dvr_divider; 181 }; 182 183 static const struct uchcom_divider_record dividers[] = 184 { 185 {307200, 307200, UCHCOM_BASE_UNKNOWN, {7, 0xD9, 0}}, 186 {921600, 921600, UCHCOM_BASE_UNKNOWN, {7, 0xF3, 0}}, 187 {2999999, 23530, 6000000, {3, 0, 0}}, 188 {23529, 2942, 750000, {2, 0, 0}}, 189 {2941, 368, 93750, {1, 0, 0}}, 190 {367, 1, 11719, {0, 0, 0}}, 191 }; 192 193 static const STRUCT_USB_HOST_ID uchcom_devs[] = { 194 {USB_VPI(USB_VENDOR_WCH, USB_PRODUCT_WCH_CH341SER, 0)}, 195 {USB_VPI(USB_VENDOR_WCH2, USB_PRODUCT_WCH2_CH341SER, 0)}, 196 }; 197 198 /* protypes */ 199 200 static int uchcom_pre_param(struct ucom_softc *, struct termios *); 201 static void uchcom_cfg_get_status(struct ucom_softc *, uint8_t *, 202 uint8_t *); 203 static void uchcom_cfg_open(struct ucom_softc *ucom); 204 static void uchcom_cfg_param(struct ucom_softc *, struct termios *); 205 static void uchcom_cfg_set_break(struct ucom_softc *, uint8_t); 206 static void uchcom_cfg_set_dtr(struct ucom_softc *, uint8_t); 207 static void uchcom_cfg_set_rts(struct ucom_softc *, uint8_t); 208 static void uchcom_start_read(struct ucom_softc *); 209 static void uchcom_start_write(struct ucom_softc *); 210 static void uchcom_stop_read(struct ucom_softc *); 211 static void uchcom_stop_write(struct ucom_softc *); 212 static void uchcom_update_version(struct uchcom_softc *); 213 static void uchcom_convert_status(struct uchcom_softc *, uint8_t); 214 static void uchcom_update_status(struct uchcom_softc *); 215 static void uchcom_set_dtr_rts(struct uchcom_softc *); 216 static int uchcom_calc_divider_settings(struct uchcom_divider *, uint32_t); 217 static void uchcom_set_baudrate(struct uchcom_softc *, uint32_t); 218 static void uchcom_poll(struct ucom_softc *ucom); 219 220 static device_probe_t uchcom_probe; 221 static device_attach_t uchcom_attach; 222 static device_detach_t uchcom_detach; 223 224 static usb_callback_t uchcom_intr_callback; 225 static usb_callback_t uchcom_write_callback; 226 static usb_callback_t uchcom_read_callback; 227 228 static const struct usb_config uchcom_config_data[UCHCOM_N_TRANSFER] = { 229 230 [UCHCOM_BULK_DT_WR] = { 231 .type = UE_BULK, 232 .endpoint = UE_ADDR_ANY, 233 .direction = UE_DIR_OUT, 234 .bufsize = UCHCOM_BULK_BUF_SIZE, 235 .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, 236 .callback = &uchcom_write_callback, 237 }, 238 239 [UCHCOM_BULK_DT_RD] = { 240 .type = UE_BULK, 241 .endpoint = UE_ADDR_ANY, 242 .direction = UE_DIR_IN, 243 .bufsize = UCHCOM_BULK_BUF_SIZE, 244 .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, 245 .callback = &uchcom_read_callback, 246 }, 247 248 [UCHCOM_INTR_DT_RD] = { 249 .type = UE_INTERRUPT, 250 .endpoint = UE_ADDR_ANY, 251 .direction = UE_DIR_IN, 252 .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, 253 .bufsize = 0, /* use wMaxPacketSize */ 254 .callback = &uchcom_intr_callback, 255 }, 256 }; 257 258 static struct ucom_callback uchcom_callback = { 259 .ucom_cfg_get_status = &uchcom_cfg_get_status, 260 .ucom_cfg_set_dtr = &uchcom_cfg_set_dtr, 261 .ucom_cfg_set_rts = &uchcom_cfg_set_rts, 262 .ucom_cfg_set_break = &uchcom_cfg_set_break, 263 .ucom_cfg_open = &uchcom_cfg_open, 264 .ucom_cfg_param = &uchcom_cfg_param, 265 .ucom_pre_param = &uchcom_pre_param, 266 .ucom_start_read = &uchcom_start_read, 267 .ucom_stop_read = &uchcom_stop_read, 268 .ucom_start_write = &uchcom_start_write, 269 .ucom_stop_write = &uchcom_stop_write, 270 .ucom_poll = &uchcom_poll, 271 }; 272 273 /* ---------------------------------------------------------------------- 274 * driver entry points 275 */ 276 277 static int 278 uchcom_probe(device_t dev) 279 { 280 struct usb_attach_arg *uaa = device_get_ivars(dev); 281 282 DPRINTFN(11, "\n"); 283 284 if (uaa->usb_mode != USB_MODE_HOST) { 285 return (ENXIO); 286 } 287 if (uaa->info.bConfigIndex != UCHCOM_CONFIG_INDEX) { 288 return (ENXIO); 289 } 290 if (uaa->info.bIfaceIndex != UCHCOM_IFACE_INDEX) { 291 return (ENXIO); 292 } 293 return (usbd_lookup_id_by_uaa(uchcom_devs, sizeof(uchcom_devs), uaa)); 294 } 295 296 static int 297 uchcom_attach(device_t dev) 298 { 299 struct uchcom_softc *sc = device_get_softc(dev); 300 struct usb_attach_arg *uaa = device_get_ivars(dev); 301 int error; 302 uint8_t iface_index; 303 304 DPRINTFN(11, "\n"); 305 306 device_set_usb_desc(dev); 307 lockinit(&sc->sc_lock, "uchcom", 0, LK_CANRECURSE); 308 309 sc->sc_udev = uaa->device; 310 311 switch (uaa->info.bcdDevice) { 312 case UCHCOM_REV_CH340: 313 device_printf(dev, "CH340 detected\n"); 314 break; 315 default: 316 device_printf(dev, "CH341 detected\n"); 317 break; 318 } 319 320 iface_index = UCHCOM_IFACE_INDEX; 321 error = usbd_transfer_setup(uaa->device, 322 &iface_index, sc->sc_xfer, uchcom_config_data, 323 UCHCOM_N_TRANSFER, sc, &sc->sc_lock); 324 325 if (error) { 326 DPRINTF("one or more missing USB endpoints, " 327 "error=%s\n", usbd_errstr(error)); 328 goto detach; 329 } 330 331 /* clear stall at first run */ 332 lockmgr(&sc->sc_lock, LK_EXCLUSIVE); 333 usbd_xfer_set_stall(sc->sc_xfer[UCHCOM_BULK_DT_WR]); 334 usbd_xfer_set_stall(sc->sc_xfer[UCHCOM_BULK_DT_RD]); 335 lockmgr(&sc->sc_lock, LK_RELEASE); 336 337 error = ucom_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc, 338 &uchcom_callback, &sc->sc_lock); 339 if (error) { 340 goto detach; 341 } 342 ucom_set_pnpinfo_usb(&sc->sc_super_ucom, dev); 343 344 return (0); 345 346 detach: 347 uchcom_detach(dev); 348 return (ENXIO); 349 } 350 351 static int 352 uchcom_detach(device_t dev) 353 { 354 struct uchcom_softc *sc = device_get_softc(dev); 355 356 DPRINTFN(11, "\n"); 357 358 ucom_detach(&sc->sc_super_ucom, &sc->sc_ucom); 359 usbd_transfer_unsetup(sc->sc_xfer, UCHCOM_N_TRANSFER); 360 lockuninit(&sc->sc_lock); 361 362 return (0); 363 } 364 365 /* ---------------------------------------------------------------------- 366 * low level i/o 367 */ 368 369 static void 370 uchcom_ctrl_write(struct uchcom_softc *sc, uint8_t reqno, 371 uint16_t value, uint16_t index) 372 { 373 struct usb_device_request req; 374 375 req.bmRequestType = UT_WRITE_VENDOR_DEVICE; 376 req.bRequest = reqno; 377 USETW(req.wValue, value); 378 USETW(req.wIndex, index); 379 USETW(req.wLength, 0); 380 381 ucom_cfg_do_request(sc->sc_udev, 382 &sc->sc_ucom, &req, NULL, 0, 1000); 383 } 384 385 static void 386 uchcom_ctrl_read(struct uchcom_softc *sc, uint8_t reqno, 387 uint16_t value, uint16_t index, void *buf, uint16_t buflen) 388 { 389 struct usb_device_request req; 390 391 req.bmRequestType = UT_READ_VENDOR_DEVICE; 392 req.bRequest = reqno; 393 USETW(req.wValue, value); 394 USETW(req.wIndex, index); 395 USETW(req.wLength, buflen); 396 397 ucom_cfg_do_request(sc->sc_udev, 398 &sc->sc_ucom, &req, buf, USB_SHORT_XFER_OK, 1000); 399 } 400 401 static void 402 uchcom_write_reg(struct uchcom_softc *sc, 403 uint8_t reg1, uint8_t val1, uint8_t reg2, uint8_t val2) 404 { 405 DPRINTF("0x%02X<-0x%02X, 0x%02X<-0x%02X\n", 406 (unsigned)reg1, (unsigned)val1, 407 (unsigned)reg2, (unsigned)val2); 408 uchcom_ctrl_write( 409 sc, UCHCOM_REQ_WRITE_REG, 410 reg1 | ((uint16_t)reg2 << 8), val1 | ((uint16_t)val2 << 8)); 411 } 412 413 static void 414 uchcom_read_reg(struct uchcom_softc *sc, 415 uint8_t reg1, uint8_t *rval1, uint8_t reg2, uint8_t *rval2) 416 { 417 uint8_t buf[UCHCOM_INPUT_BUF_SIZE]; 418 419 uchcom_ctrl_read( 420 sc, UCHCOM_REQ_READ_REG, 421 reg1 | ((uint16_t)reg2 << 8), 0, buf, sizeof(buf)); 422 423 DPRINTF("0x%02X->0x%02X, 0x%02X->0x%02X\n", 424 (unsigned)reg1, (unsigned)buf[0], 425 (unsigned)reg2, (unsigned)buf[1]); 426 427 if (rval1) 428 *rval1 = buf[0]; 429 if (rval2) 430 *rval2 = buf[1]; 431 } 432 433 static void 434 uchcom_get_version(struct uchcom_softc *sc, uint8_t *rver) 435 { 436 uint8_t buf[UCHCOM_INPUT_BUF_SIZE]; 437 438 uchcom_ctrl_read(sc, UCHCOM_REQ_GET_VERSION, 0, 0, buf, sizeof(buf)); 439 440 if (rver) 441 *rver = buf[0]; 442 } 443 444 static void 445 uchcom_get_status(struct uchcom_softc *sc, uint8_t *rval) 446 { 447 uchcom_read_reg(sc, UCHCOM_REG_STAT1, rval, UCHCOM_REG_STAT2, NULL); 448 } 449 450 static void 451 uchcom_set_dtr_rts_10(struct uchcom_softc *sc, uint8_t val) 452 { 453 uchcom_write_reg(sc, UCHCOM_REG_STAT1, val, UCHCOM_REG_STAT1, val); 454 } 455 456 static void 457 uchcom_set_dtr_rts_20(struct uchcom_softc *sc, uint8_t val) 458 { 459 uchcom_ctrl_write(sc, UCHCOM_REQ_SET_DTRRTS, val, 0); 460 } 461 462 463 /* ---------------------------------------------------------------------- 464 * middle layer 465 */ 466 467 static void 468 uchcom_update_version(struct uchcom_softc *sc) 469 { 470 uchcom_get_version(sc, &sc->sc_version); 471 } 472 473 static void 474 uchcom_convert_status(struct uchcom_softc *sc, uint8_t cur) 475 { 476 sc->sc_dtr = !(cur & UCHCOM_DTR_MASK); 477 sc->sc_rts = !(cur & UCHCOM_RTS_MASK); 478 479 cur = ~cur & 0x0F; 480 sc->sc_msr = (cur << 4) | ((sc->sc_msr >> 4) ^ cur); 481 } 482 483 static void 484 uchcom_update_status(struct uchcom_softc *sc) 485 { 486 uint8_t cur; 487 488 uchcom_get_status(sc, &cur); 489 uchcom_convert_status(sc, cur); 490 } 491 492 493 static void 494 uchcom_set_dtr_rts(struct uchcom_softc *sc) 495 { 496 uint8_t val = 0; 497 498 if (sc->sc_dtr) 499 val |= UCHCOM_DTR_MASK; 500 if (sc->sc_rts) 501 val |= UCHCOM_RTS_MASK; 502 503 if (sc->sc_version < UCHCOM_VER_20) 504 uchcom_set_dtr_rts_10(sc, ~val); 505 else 506 uchcom_set_dtr_rts_20(sc, ~val); 507 } 508 509 static void 510 uchcom_cfg_set_break(struct ucom_softc *ucom, uint8_t onoff) 511 { 512 struct uchcom_softc *sc = ucom->sc_parent; 513 uint8_t brk1; 514 uint8_t brk2; 515 516 uchcom_read_reg(sc, UCHCOM_REG_BREAK1, &brk1, UCHCOM_REG_BREAK2, &brk2); 517 if (onoff) { 518 /* on - clear bits */ 519 brk1 &= ~UCHCOM_BRK1_MASK; 520 brk2 &= ~UCHCOM_BRK2_MASK; 521 } else { 522 /* off - set bits */ 523 brk1 |= UCHCOM_BRK1_MASK; 524 brk2 |= UCHCOM_BRK2_MASK; 525 } 526 uchcom_write_reg(sc, UCHCOM_REG_BREAK1, brk1, UCHCOM_REG_BREAK2, brk2); 527 } 528 529 static int 530 uchcom_calc_divider_settings(struct uchcom_divider *dp, uint32_t rate) 531 { 532 const struct uchcom_divider_record *rp; 533 uint32_t div; 534 uint32_t rem; 535 uint32_t mod; 536 uint8_t i; 537 538 /* find record */ 539 for (i = 0; i != NELEM(dividers); i++) { 540 if (dividers[i].dvr_high >= rate && 541 dividers[i].dvr_low <= rate) { 542 rp = ÷rs[i]; 543 goto found; 544 } 545 } 546 return (-1); 547 548 found: 549 dp->dv_prescaler = rp->dvr_divider.dv_prescaler; 550 if (rp->dvr_base_clock == UCHCOM_BASE_UNKNOWN) 551 dp->dv_div = rp->dvr_divider.dv_div; 552 else { 553 div = rp->dvr_base_clock / rate; 554 rem = rp->dvr_base_clock % rate; 555 if (div == 0 || div >= 0xFF) 556 return (-1); 557 if ((rem << 1) >= rate) 558 div += 1; 559 dp->dv_div = (uint8_t)-div; 560 } 561 562 mod = (UCHCOM_BPS_MOD_BASE / rate) + UCHCOM_BPS_MOD_BASE_OFS; 563 mod = mod + (mod / 2); 564 565 dp->dv_mod = (mod + 0xFF) / 0x100; 566 567 return (0); 568 } 569 570 static void 571 uchcom_set_baudrate(struct uchcom_softc *sc, uint32_t rate) 572 { 573 struct uchcom_divider dv; 574 575 if (uchcom_calc_divider_settings(&dv, rate)) 576 return; 577 578 uchcom_write_reg(sc, 579 UCHCOM_REG_BPS_PRE, dv.dv_prescaler, 580 UCHCOM_REG_BPS_DIV, dv.dv_div); 581 uchcom_write_reg(sc, 582 UCHCOM_REG_BPS_MOD, dv.dv_mod, 583 UCHCOM_REG_BPS_PAD, 0); 584 } 585 586 /* ---------------------------------------------------------------------- 587 * methods for ucom 588 */ 589 static void 590 uchcom_cfg_get_status(struct ucom_softc *ucom, uint8_t *lsr, uint8_t *msr) 591 { 592 struct uchcom_softc *sc = ucom->sc_parent; 593 594 DPRINTF("\n"); 595 596 *lsr = sc->sc_lsr; 597 *msr = sc->sc_msr; 598 } 599 600 static void 601 uchcom_cfg_set_dtr(struct ucom_softc *ucom, uint8_t onoff) 602 { 603 struct uchcom_softc *sc = ucom->sc_parent; 604 605 DPRINTF("onoff = %d\n", onoff); 606 607 sc->sc_dtr = onoff; 608 uchcom_set_dtr_rts(sc); 609 } 610 611 static void 612 uchcom_cfg_set_rts(struct ucom_softc *ucom, uint8_t onoff) 613 { 614 struct uchcom_softc *sc = ucom->sc_parent; 615 616 DPRINTF("onoff = %d\n", onoff); 617 618 sc->sc_rts = onoff; 619 uchcom_set_dtr_rts(sc); 620 } 621 622 static void 623 uchcom_cfg_open(struct ucom_softc *ucom) 624 { 625 struct uchcom_softc *sc = ucom->sc_parent; 626 627 DPRINTF("\n"); 628 629 uchcom_update_version(sc); 630 uchcom_update_status(sc); 631 } 632 633 static int 634 uchcom_pre_param(struct ucom_softc *ucom, struct termios *t) 635 { 636 struct uchcom_divider dv; 637 638 switch (t->c_cflag & CSIZE) { 639 case CS8: 640 break; 641 default: 642 return (EIO); 643 } 644 645 if (uchcom_calc_divider_settings(&dv, t->c_ospeed)) { 646 return (EIO); 647 } 648 return (0); /* success */ 649 } 650 651 static void 652 uchcom_cfg_param(struct ucom_softc *ucom, struct termios *t) 653 { 654 struct uchcom_softc *sc = ucom->sc_parent; 655 656 uchcom_get_version(sc, 0); 657 uchcom_ctrl_write(sc, UCHCOM_REQ_RESET, 0, 0); 658 uchcom_set_baudrate(sc, t->c_ospeed); 659 uchcom_read_reg(sc, 0x18, 0, 0x25, 0); 660 uchcom_write_reg(sc, 0x18, 0x50, 0x25, 0x00); 661 uchcom_update_status(sc); 662 uchcom_ctrl_write(sc, UCHCOM_REQ_RESET, 0x501f, 0xd90a); 663 uchcom_set_baudrate(sc, t->c_ospeed); 664 uchcom_set_dtr_rts(sc); 665 uchcom_update_status(sc); 666 } 667 668 static void 669 uchcom_start_read(struct ucom_softc *ucom) 670 { 671 struct uchcom_softc *sc = ucom->sc_parent; 672 673 /* start interrupt endpoint */ 674 usbd_transfer_start(sc->sc_xfer[UCHCOM_INTR_DT_RD]); 675 676 /* start read endpoint */ 677 usbd_transfer_start(sc->sc_xfer[UCHCOM_BULK_DT_RD]); 678 } 679 680 static void 681 uchcom_stop_read(struct ucom_softc *ucom) 682 { 683 struct uchcom_softc *sc = ucom->sc_parent; 684 685 /* stop interrupt endpoint */ 686 usbd_transfer_stop(sc->sc_xfer[UCHCOM_INTR_DT_RD]); 687 688 /* stop read endpoint */ 689 usbd_transfer_stop(sc->sc_xfer[UCHCOM_BULK_DT_RD]); 690 } 691 692 static void 693 uchcom_start_write(struct ucom_softc *ucom) 694 { 695 struct uchcom_softc *sc = ucom->sc_parent; 696 697 usbd_transfer_start(sc->sc_xfer[UCHCOM_BULK_DT_WR]); 698 } 699 700 static void 701 uchcom_stop_write(struct ucom_softc *ucom) 702 { 703 struct uchcom_softc *sc = ucom->sc_parent; 704 705 usbd_transfer_stop(sc->sc_xfer[UCHCOM_BULK_DT_WR]); 706 } 707 708 /* ---------------------------------------------------------------------- 709 * callback when the modem status is changed. 710 */ 711 static void 712 uchcom_intr_callback(struct usb_xfer *xfer, usb_error_t error) 713 { 714 struct uchcom_softc *sc = usbd_xfer_softc(xfer); 715 struct usb_page_cache *pc; 716 uint8_t buf[UCHCOM_INTR_LEAST]; 717 int actlen; 718 719 usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); 720 721 switch (USB_GET_STATE(xfer)) { 722 case USB_ST_TRANSFERRED: 723 724 DPRINTF("actlen = %u\n", actlen); 725 726 if (actlen >= UCHCOM_INTR_LEAST) { 727 pc = usbd_xfer_get_frame(xfer, 0); 728 usbd_copy_out(pc, 0, buf, UCHCOM_INTR_LEAST); 729 730 DPRINTF("data = 0x%02X 0x%02X 0x%02X 0x%02X\n", 731 (unsigned)buf[0], (unsigned)buf[1], 732 (unsigned)buf[2], (unsigned)buf[3]); 733 734 uchcom_convert_status(sc, buf[UCHCOM_INTR_STAT1]); 735 ucom_status_change(&sc->sc_ucom); 736 } 737 case USB_ST_SETUP: 738 tr_setup: 739 usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); 740 usbd_transfer_submit(xfer); 741 break; 742 743 default: /* Error */ 744 if (error != USB_ERR_CANCELLED) { 745 /* try to clear stall first */ 746 usbd_xfer_set_stall(xfer); 747 goto tr_setup; 748 } 749 break; 750 } 751 } 752 753 static void 754 uchcom_write_callback(struct usb_xfer *xfer, usb_error_t error) 755 { 756 struct uchcom_softc *sc = usbd_xfer_softc(xfer); 757 struct usb_page_cache *pc; 758 uint32_t actlen; 759 760 switch (USB_GET_STATE(xfer)) { 761 case USB_ST_SETUP: 762 case USB_ST_TRANSFERRED: 763 tr_setup: 764 pc = usbd_xfer_get_frame(xfer, 0); 765 if (ucom_get_data(&sc->sc_ucom, pc, 0, 766 usbd_xfer_max_len(xfer), &actlen)) { 767 768 DPRINTF("actlen = %d\n", actlen); 769 770 usbd_xfer_set_frame_len(xfer, 0, actlen); 771 usbd_transfer_submit(xfer); 772 } 773 break; 774 775 default: /* Error */ 776 if (error != USB_ERR_CANCELLED) { 777 /* try to clear stall first */ 778 usbd_xfer_set_stall(xfer); 779 goto tr_setup; 780 } 781 break; 782 } 783 } 784 785 static void 786 uchcom_read_callback(struct usb_xfer *xfer, usb_error_t error) 787 { 788 struct uchcom_softc *sc = usbd_xfer_softc(xfer); 789 struct usb_page_cache *pc; 790 int actlen; 791 792 usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); 793 794 switch (USB_GET_STATE(xfer)) { 795 case USB_ST_TRANSFERRED: 796 797 if (actlen > 0) { 798 pc = usbd_xfer_get_frame(xfer, 0); 799 ucom_put_data(&sc->sc_ucom, pc, 0, actlen); 800 } 801 802 case USB_ST_SETUP: 803 tr_setup: 804 usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); 805 usbd_transfer_submit(xfer); 806 break; 807 808 default: /* Error */ 809 if (error != USB_ERR_CANCELLED) { 810 /* try to clear stall first */ 811 usbd_xfer_set_stall(xfer); 812 goto tr_setup; 813 } 814 break; 815 } 816 } 817 818 static void 819 uchcom_poll(struct ucom_softc *ucom) 820 { 821 struct uchcom_softc *sc = ucom->sc_parent; 822 usbd_transfer_poll(sc->sc_xfer, UCHCOM_N_TRANSFER); 823 } 824 825 static device_method_t uchcom_methods[] = { 826 /* Device interface */ 827 DEVMETHOD(device_probe, uchcom_probe), 828 DEVMETHOD(device_attach, uchcom_attach), 829 DEVMETHOD(device_detach, uchcom_detach), 830 831 DEVMETHOD_END 832 }; 833 834 static driver_t uchcom_driver = { 835 "ucom", 836 uchcom_methods, 837 sizeof(struct uchcom_softc) 838 }; 839 840 static devclass_t uchcom_devclass; 841 842 DRIVER_MODULE(uchcom, uhub, uchcom_driver, uchcom_devclass, NULL, NULL); 843 MODULE_DEPEND(uchcom, ucom, 1, 1, 1); 844 MODULE_DEPEND(uchcom, usb, 1, 1, 1); 845 MODULE_VERSION(uchcom, 1); 846