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 * 3. All advertising materials mentioning features or use of this software 45 * must display the following acknowledgement: 46 * This product includes software developed by the NetBSD 47 * Foundation, Inc. and its contributors. 48 * 4. Neither the name of The NetBSD Foundation nor the names of its 49 * contributors may be used to endorse or promote products derived 50 * from this software without specific prior written permission. 51 * 52 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 53 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 54 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 55 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 56 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 57 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 58 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 59 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 60 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 61 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 62 * POSSIBILITY OF SUCH DAMAGE. 63 */ 64 65 #include <sys/cdefs.h> 66 __FBSDID("$FreeBSD$"); 67 68 /* 69 * driver for WinChipHead CH341/340, the worst USB-serial chip in the world. 70 */ 71 72 #include "usbdevs.h" 73 #include <dev/usb/usb.h> 74 #include <dev/usb/usb_mfunc.h> 75 #include <dev/usb/usb_error.h> 76 #include <dev/usb/usb_cdc.h> 77 #include <dev/usb/usb_ioctl.h> 78 79 #define USB_DEBUG_VAR uchcom_debug 80 81 #include <dev/usb/usb_core.h> 82 #include <dev/usb/usb_debug.h> 83 #include <dev/usb/usb_process.h> 84 #include <dev/usb/usb_request.h> 85 #include <dev/usb/usb_lookup.h> 86 #include <dev/usb/usb_util.h> 87 #include <dev/usb/usb_busdma.h> 88 89 #include <dev/usb/serial/usb_serial.h> 90 91 #if USB_DEBUG 92 static int uchcom_debug = 0; 93 94 SYSCTL_NODE(_hw_usb2, OID_AUTO, uchcom, CTLFLAG_RW, 0, "USB uchcom"); 95 SYSCTL_INT(_hw_usb2_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 usb2_com_super_softc sc_super_ucom; 157 struct usb2_com_softc sc_ucom; 158 159 struct usb2_xfer *sc_xfer[UCHCOM_N_TRANSFER]; 160 struct usb2_device *sc_udev; 161 162 uint8_t sc_dtr; /* local copy */ 163 uint8_t sc_rts; /* local copy */ 164 uint8_t sc_version; 165 uint8_t sc_msr; 166 uint8_t sc_lsr; /* local status register */ 167 }; 168 169 struct uchcom_divider { 170 uint8_t dv_prescaler; 171 uint8_t dv_div; 172 uint8_t dv_mod; 173 }; 174 175 struct uchcom_divider_record { 176 uint32_t dvr_high; 177 uint32_t dvr_low; 178 uint32_t dvr_base_clock; 179 struct uchcom_divider dvr_divider; 180 }; 181 182 static const struct uchcom_divider_record dividers[] = 183 { 184 {307200, 307200, UCHCOM_BASE_UNKNOWN, {7, 0xD9, 0}}, 185 {921600, 921600, UCHCOM_BASE_UNKNOWN, {7, 0xF3, 0}}, 186 {2999999, 23530, 6000000, {3, 0, 0}}, 187 {23529, 2942, 750000, {2, 0, 0}}, 188 {2941, 368, 93750, {1, 0, 0}}, 189 {367, 1, 11719, {0, 0, 0}}, 190 }; 191 192 #define NUM_DIVIDERS (sizeof (dividers) / sizeof (dividers[0])) 193 194 static const struct usb2_device_id uchcom_devs[] = { 195 {USB_VPI(USB_VENDOR_WCH, USB_PRODUCT_WCH_CH341SER, 0)}, 196 }; 197 198 /* protypes */ 199 200 static int uchcom_pre_param(struct usb2_com_softc *, struct termios *); 201 static void uchcom_cfg_get_status(struct usb2_com_softc *, uint8_t *, 202 uint8_t *); 203 static void uchcom_cfg_param(struct usb2_com_softc *, struct termios *); 204 static void uchcom_cfg_set_break(struct usb2_com_softc *, uint8_t); 205 static void uchcom_cfg_set_dtr(struct usb2_com_softc *, uint8_t); 206 static void uchcom_cfg_set_rts(struct usb2_com_softc *, uint8_t); 207 static void uchcom_start_read(struct usb2_com_softc *); 208 static void uchcom_start_write(struct usb2_com_softc *); 209 static void uchcom_stop_read(struct usb2_com_softc *); 210 static void uchcom_stop_write(struct usb2_com_softc *); 211 static void uchcom_update_version(struct uchcom_softc *); 212 static void uchcom_convert_status(struct uchcom_softc *, uint8_t); 213 static void uchcom_update_status(struct uchcom_softc *); 214 static void uchcom_set_dtrrts(struct uchcom_softc *); 215 static int uchcom_calc_divider_settings(struct uchcom_divider *, uint32_t); 216 static void uchcom_set_dte_rate(struct uchcom_softc *, uint32_t); 217 static void uchcom_set_line_control(struct uchcom_softc *, tcflag_t); 218 static void uchcom_clear_chip(struct uchcom_softc *); 219 static void uchcom_reset_chip(struct uchcom_softc *); 220 221 static device_probe_t uchcom_probe; 222 static device_attach_t uchcom_attach; 223 static device_detach_t uchcom_detach; 224 225 static usb2_callback_t uchcom_intr_callback; 226 static usb2_callback_t uchcom_write_callback; 227 static usb2_callback_t uchcom_read_callback; 228 229 static const struct usb2_config uchcom_config_data[UCHCOM_N_TRANSFER] = { 230 231 [UCHCOM_BULK_DT_WR] = { 232 .type = UE_BULK, 233 .endpoint = UE_ADDR_ANY, 234 .direction = UE_DIR_OUT, 235 .mh.bufsize = UCHCOM_BULK_BUF_SIZE, 236 .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,}, 237 .mh.callback = &uchcom_write_callback, 238 }, 239 240 [UCHCOM_BULK_DT_RD] = { 241 .type = UE_BULK, 242 .endpoint = UE_ADDR_ANY, 243 .direction = UE_DIR_IN, 244 .mh.bufsize = UCHCOM_BULK_BUF_SIZE, 245 .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, 246 .mh.callback = &uchcom_read_callback, 247 }, 248 249 [UCHCOM_INTR_DT_RD] = { 250 .type = UE_INTERRUPT, 251 .endpoint = UE_ADDR_ANY, 252 .direction = UE_DIR_IN, 253 .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, 254 .mh.bufsize = 0, /* use wMaxPacketSize */ 255 .mh.callback = &uchcom_intr_callback, 256 }, 257 }; 258 259 struct usb2_com_callback uchcom_callback = { 260 .usb2_com_cfg_get_status = &uchcom_cfg_get_status, 261 .usb2_com_cfg_set_dtr = &uchcom_cfg_set_dtr, 262 .usb2_com_cfg_set_rts = &uchcom_cfg_set_rts, 263 .usb2_com_cfg_set_break = &uchcom_cfg_set_break, 264 .usb2_com_cfg_param = &uchcom_cfg_param, 265 .usb2_com_pre_param = &uchcom_pre_param, 266 .usb2_com_start_read = &uchcom_start_read, 267 .usb2_com_stop_read = &uchcom_stop_read, 268 .usb2_com_start_write = &uchcom_start_write, 269 .usb2_com_stop_write = &uchcom_stop_write, 270 }; 271 272 /* ---------------------------------------------------------------------- 273 * driver entry points 274 */ 275 276 static int 277 uchcom_probe(device_t dev) 278 { 279 struct usb2_attach_arg *uaa = device_get_ivars(dev); 280 281 DPRINTFN(11, "\n"); 282 283 if (uaa->usb2_mode != USB_MODE_HOST) { 284 return (ENXIO); 285 } 286 if (uaa->info.bConfigIndex != UCHCOM_CONFIG_INDEX) { 287 return (ENXIO); 288 } 289 if (uaa->info.bIfaceIndex != UCHCOM_IFACE_INDEX) { 290 return (ENXIO); 291 } 292 return (usb2_lookup_id_by_uaa(uchcom_devs, sizeof(uchcom_devs), uaa)); 293 } 294 295 static int 296 uchcom_attach(device_t dev) 297 { 298 struct uchcom_softc *sc = device_get_softc(dev); 299 struct usb2_attach_arg *uaa = device_get_ivars(dev); 300 int error; 301 uint8_t iface_index; 302 303 DPRINTFN(11, "\n"); 304 305 device_set_usb2_desc(dev); 306 307 sc->sc_udev = uaa->device; 308 309 switch (uaa->info.bcdDevice) { 310 case UCHCOM_REV_CH340: 311 device_printf(dev, "CH340 detected\n"); 312 break; 313 default: 314 device_printf(dev, "CH341 detected\n"); 315 break; 316 } 317 318 iface_index = UCHCOM_IFACE_INDEX; 319 error = usb2_transfer_setup(uaa->device, 320 &iface_index, sc->sc_xfer, uchcom_config_data, 321 UCHCOM_N_TRANSFER, sc, &Giant); 322 323 if (error) { 324 DPRINTF("one or more missing USB endpoints, " 325 "error=%s\n", usb2_errstr(error)); 326 goto detach; 327 } 328 /* 329 * Do the initialization during attach so that the system does not 330 * sleep during open: 331 */ 332 uchcom_update_version(sc); 333 uchcom_clear_chip(sc); 334 uchcom_reset_chip(sc); 335 uchcom_update_status(sc); 336 337 sc->sc_dtr = 1; 338 sc->sc_rts = 1; 339 340 /* clear stall at first run */ 341 usb2_transfer_set_stall(sc->sc_xfer[UCHCOM_BULK_DT_WR]); 342 usb2_transfer_set_stall(sc->sc_xfer[UCHCOM_BULK_DT_RD]); 343 344 error = usb2_com_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc, 345 &uchcom_callback, &Giant); 346 if (error) { 347 goto detach; 348 } 349 return (0); 350 351 detach: 352 uchcom_detach(dev); 353 return (ENXIO); 354 } 355 356 static int 357 uchcom_detach(device_t dev) 358 { 359 struct uchcom_softc *sc = device_get_softc(dev); 360 361 DPRINTFN(11, "\n"); 362 363 usb2_com_detach(&sc->sc_super_ucom, &sc->sc_ucom, 1); 364 365 usb2_transfer_unsetup(sc->sc_xfer, UCHCOM_N_TRANSFER); 366 367 return (0); 368 } 369 370 /* ---------------------------------------------------------------------- 371 * low level i/o 372 */ 373 374 static void 375 uchcom_ctrl_write(struct uchcom_softc *sc, uint8_t reqno, 376 uint16_t value, uint16_t index) 377 { 378 struct usb2_device_request req; 379 380 req.bmRequestType = UT_WRITE_VENDOR_DEVICE; 381 req.bRequest = reqno; 382 USETW(req.wValue, value); 383 USETW(req.wIndex, index); 384 USETW(req.wLength, 0); 385 386 usb2_com_cfg_do_request(sc->sc_udev, 387 &sc->sc_ucom, &req, NULL, 0, 1000); 388 } 389 390 static void 391 uchcom_ctrl_read(struct uchcom_softc *sc, uint8_t reqno, 392 uint16_t value, uint16_t index, void *buf, uint16_t buflen) 393 { 394 struct usb2_device_request req; 395 396 req.bmRequestType = UT_READ_VENDOR_DEVICE; 397 req.bRequest = reqno; 398 USETW(req.wValue, value); 399 USETW(req.wIndex, index); 400 USETW(req.wLength, buflen); 401 402 usb2_com_cfg_do_request(sc->sc_udev, 403 &sc->sc_ucom, &req, buf, USB_SHORT_XFER_OK, 1000); 404 } 405 406 static void 407 uchcom_write_reg(struct uchcom_softc *sc, 408 uint8_t reg1, uint8_t val1, uint8_t reg2, uint8_t val2) 409 { 410 DPRINTF("0x%02X<-0x%02X, 0x%02X<-0x%02X\n", 411 (unsigned)reg1, (unsigned)val1, 412 (unsigned)reg2, (unsigned)val2); 413 uchcom_ctrl_write( 414 sc, UCHCOM_REQ_WRITE_REG, 415 reg1 | ((uint16_t)reg2 << 8), val1 | ((uint16_t)val2 << 8)); 416 } 417 418 static void 419 uchcom_read_reg(struct uchcom_softc *sc, 420 uint8_t reg1, uint8_t *rval1, uint8_t reg2, uint8_t *rval2) 421 { 422 uint8_t buf[UCHCOM_INPUT_BUF_SIZE]; 423 424 uchcom_ctrl_read( 425 sc, UCHCOM_REQ_READ_REG, 426 reg1 | ((uint16_t)reg2 << 8), 0, buf, sizeof(buf)); 427 428 DPRINTF("0x%02X->0x%02X, 0x%02X->0x%02X\n", 429 (unsigned)reg1, (unsigned)buf[0], 430 (unsigned)reg2, (unsigned)buf[1]); 431 432 if (rval1) 433 *rval1 = buf[0]; 434 if (rval2) 435 *rval2 = buf[1]; 436 } 437 438 static void 439 uchcom_get_version(struct uchcom_softc *sc, uint8_t *rver) 440 { 441 uint8_t buf[UCHCOM_INPUT_BUF_SIZE]; 442 443 uchcom_ctrl_read( 444 sc, UCHCOM_REQ_GET_VERSION, 0, 0, buf, sizeof(buf)); 445 446 if (rver) 447 *rver = buf[0]; 448 } 449 450 static void 451 uchcom_get_status(struct uchcom_softc *sc, uint8_t *rval) 452 { 453 uchcom_read_reg(sc, UCHCOM_REG_STAT1, rval, UCHCOM_REG_STAT2, NULL); 454 } 455 456 static void 457 uchcom_set_dtrrts_10(struct uchcom_softc *sc, uint8_t val) 458 { 459 uchcom_write_reg(sc, UCHCOM_REG_STAT1, val, UCHCOM_REG_STAT1, val); 460 } 461 462 static void 463 uchcom_set_dtrrts_20(struct uchcom_softc *sc, uint8_t val) 464 { 465 uchcom_ctrl_write(sc, UCHCOM_REQ_SET_DTRRTS, val, 0); 466 } 467 468 469 /* ---------------------------------------------------------------------- 470 * middle layer 471 */ 472 473 static void 474 uchcom_update_version(struct uchcom_softc *sc) 475 { 476 uchcom_get_version(sc, &sc->sc_version); 477 } 478 479 static void 480 uchcom_convert_status(struct uchcom_softc *sc, uint8_t cur) 481 { 482 sc->sc_dtr = !(cur & UCHCOM_DTR_MASK); 483 sc->sc_rts = !(cur & UCHCOM_RTS_MASK); 484 485 cur = ~cur & 0x0F; 486 sc->sc_msr = (cur << 4) | ((sc->sc_msr >> 4) ^ cur); 487 } 488 489 static void 490 uchcom_update_status(struct uchcom_softc *sc) 491 { 492 uint8_t cur; 493 494 uchcom_get_status(sc, &cur); 495 uchcom_convert_status(sc, cur); 496 } 497 498 499 static void 500 uchcom_set_dtrrts(struct uchcom_softc *sc) 501 { 502 uint8_t val = 0; 503 504 if (sc->sc_dtr) 505 val |= UCHCOM_DTR_MASK; 506 if (sc->sc_rts) 507 val |= UCHCOM_RTS_MASK; 508 509 if (sc->sc_version < UCHCOM_VER_20) 510 uchcom_set_dtrrts_10(sc, ~val); 511 else 512 uchcom_set_dtrrts_20(sc, ~val); 513 } 514 515 static void 516 uchcom_cfg_set_break(struct usb2_com_softc *ucom, uint8_t onoff) 517 { 518 struct uchcom_softc *sc = ucom->sc_parent; 519 uint8_t brk1; 520 uint8_t brk2; 521 522 uchcom_read_reg(sc, UCHCOM_REG_BREAK1, &brk1, UCHCOM_REG_BREAK2, &brk2); 523 if (onoff) { 524 /* on - clear bits */ 525 brk1 &= ~UCHCOM_BRK1_MASK; 526 brk2 &= ~UCHCOM_BRK2_MASK; 527 } else { 528 /* off - set bits */ 529 brk1 |= UCHCOM_BRK1_MASK; 530 brk2 |= UCHCOM_BRK2_MASK; 531 } 532 uchcom_write_reg(sc, UCHCOM_REG_BREAK1, brk1, UCHCOM_REG_BREAK2, brk2); 533 } 534 535 static int 536 uchcom_calc_divider_settings(struct uchcom_divider *dp, uint32_t rate) 537 { 538 const struct uchcom_divider_record *rp; 539 uint32_t div; 540 uint32_t rem; 541 uint32_t mod; 542 uint8_t i; 543 544 /* find record */ 545 for (i = 0; i != NUM_DIVIDERS; i++) { 546 if (dividers[i].dvr_high >= rate && 547 dividers[i].dvr_low <= rate) { 548 rp = ÷rs[i]; 549 goto found; 550 } 551 } 552 return (-1); 553 554 found: 555 dp->dv_prescaler = rp->dvr_divider.dv_prescaler; 556 if (rp->dvr_base_clock == UCHCOM_BASE_UNKNOWN) 557 dp->dv_div = rp->dvr_divider.dv_div; 558 else { 559 div = rp->dvr_base_clock / rate; 560 rem = rp->dvr_base_clock % rate; 561 if (div == 0 || div >= 0xFF) 562 return (-1); 563 if ((rem << 1) >= rate) 564 div += 1; 565 dp->dv_div = (uint8_t)-div; 566 } 567 568 mod = UCHCOM_BPS_MOD_BASE / rate + UCHCOM_BPS_MOD_BASE_OFS; 569 mod = mod + mod / 2; 570 571 dp->dv_mod = mod / 0x100; 572 573 return (0); 574 } 575 576 static void 577 uchcom_set_dte_rate(struct uchcom_softc *sc, uint32_t rate) 578 { 579 struct uchcom_divider dv; 580 581 if (uchcom_calc_divider_settings(&dv, rate)) 582 return; 583 584 uchcom_write_reg(sc, 585 UCHCOM_REG_BPS_PRE, dv.dv_prescaler, 586 UCHCOM_REG_BPS_DIV, dv.dv_div); 587 uchcom_write_reg(sc, 588 UCHCOM_REG_BPS_MOD, dv.dv_mod, 589 UCHCOM_REG_BPS_PAD, 0); 590 } 591 592 static void 593 uchcom_set_line_control(struct uchcom_softc *sc, tcflag_t cflag) 594 { 595 uint8_t lcr1 = 0; 596 uint8_t lcr2 = 0; 597 598 uchcom_read_reg(sc, UCHCOM_REG_LCR1, &lcr1, UCHCOM_REG_LCR2, &lcr2); 599 600 lcr1 &= ~UCHCOM_LCR1_MASK; 601 lcr2 &= ~UCHCOM_LCR2_MASK; 602 603 /* 604 * XXX: it is difficult to handle the line control appropriately: 605 * - CS8, !CSTOPB and any parity mode seems ok, but 606 * - the chip doesn't have the function to calculate parity 607 * in !CS8 mode. 608 * - it is unclear that the chip supports CS5,6 mode. 609 * - it is unclear how to handle stop bits. 610 */ 611 612 if (cflag & PARENB) { 613 lcr1 |= UCHCOM_LCR1_PARENB; 614 if (cflag & PARODD) 615 lcr2 |= UCHCOM_LCR2_PARODD; 616 else 617 lcr2 |= UCHCOM_LCR2_PAREVEN; 618 } 619 uchcom_write_reg(sc, UCHCOM_REG_LCR1, lcr1, UCHCOM_REG_LCR2, lcr2); 620 } 621 622 static void 623 uchcom_clear_chip(struct uchcom_softc *sc) 624 { 625 DPRINTF("\n"); 626 uchcom_ctrl_write(sc, UCHCOM_REQ_RESET, 0, 0); 627 } 628 629 static void 630 uchcom_reset_chip(struct uchcom_softc *sc) 631 { 632 uint16_t val; 633 uint16_t idx; 634 uint8_t lcr1; 635 uint8_t lcr2; 636 uint8_t pre; 637 uint8_t div; 638 uint8_t mod; 639 640 uchcom_read_reg(sc, UCHCOM_REG_LCR1, &lcr1, UCHCOM_REG_LCR2, &lcr2); 641 uchcom_read_reg(sc, UCHCOM_REG_BPS_PRE, &pre, UCHCOM_REG_BPS_DIV, &div); 642 uchcom_read_reg(sc, UCHCOM_REG_BPS_MOD, &mod, UCHCOM_REG_BPS_PAD, NULL); 643 644 val = 0; 645 idx = 0; 646 val |= (uint16_t)(lcr1 & 0xF0) << 8; 647 val |= 0x01; 648 val |= (uint16_t)(lcr2 & 0x0F) << 8; 649 val |= 0x02; 650 idx |= pre & 0x07; 651 val |= 0x04; 652 idx |= (uint16_t)div << 8; 653 val |= 0x08; 654 idx |= mod & 0xF8; 655 val |= 0x10; 656 657 DPRINTF("reset v=0x%04X, i=0x%04X\n", val, idx); 658 659 uchcom_ctrl_write(sc, UCHCOM_REQ_RESET, val, idx); 660 } 661 662 /* ---------------------------------------------------------------------- 663 * methods for ucom 664 */ 665 static void 666 uchcom_cfg_get_status(struct usb2_com_softc *ucom, uint8_t *lsr, uint8_t *msr) 667 { 668 struct uchcom_softc *sc = ucom->sc_parent; 669 670 DPRINTF("\n"); 671 672 *lsr = sc->sc_lsr; 673 *msr = sc->sc_msr; 674 } 675 676 static void 677 uchcom_cfg_set_dtr(struct usb2_com_softc *ucom, uint8_t onoff) 678 { 679 struct uchcom_softc *sc = ucom->sc_parent; 680 681 DPRINTF("onoff = %d\n", onoff); 682 683 sc->sc_dtr = onoff; 684 uchcom_set_dtrrts(sc); 685 } 686 687 static void 688 uchcom_cfg_set_rts(struct usb2_com_softc *ucom, uint8_t onoff) 689 { 690 struct uchcom_softc *sc = ucom->sc_parent; 691 692 DPRINTF("onoff = %d\n", onoff); 693 694 sc->sc_rts = onoff; 695 uchcom_set_dtrrts(sc); 696 } 697 698 static int 699 uchcom_pre_param(struct usb2_com_softc *ucom, struct termios *t) 700 { 701 struct uchcom_divider dv; 702 703 switch (t->c_cflag & CSIZE) { 704 case CS5: 705 case CS6: 706 case CS7: 707 return (EIO); 708 default: 709 break; 710 } 711 712 if (uchcom_calc_divider_settings(&dv, t->c_ospeed)) { 713 return (EIO); 714 } 715 return (0); /* success */ 716 } 717 718 static void 719 uchcom_cfg_param(struct usb2_com_softc *ucom, struct termios *t) 720 { 721 struct uchcom_softc *sc = ucom->sc_parent; 722 723 uchcom_set_line_control(sc, t->c_cflag); 724 uchcom_set_dte_rate(sc, t->c_ospeed); 725 } 726 727 static void 728 uchcom_start_read(struct usb2_com_softc *ucom) 729 { 730 struct uchcom_softc *sc = ucom->sc_parent; 731 732 /* start interrupt endpoint */ 733 usb2_transfer_start(sc->sc_xfer[UCHCOM_INTR_DT_RD]); 734 735 /* start read endpoint */ 736 usb2_transfer_start(sc->sc_xfer[UCHCOM_BULK_DT_RD]); 737 } 738 739 static void 740 uchcom_stop_read(struct usb2_com_softc *ucom) 741 { 742 struct uchcom_softc *sc = ucom->sc_parent; 743 744 /* stop interrupt endpoint */ 745 usb2_transfer_stop(sc->sc_xfer[UCHCOM_INTR_DT_RD]); 746 747 /* stop read endpoint */ 748 usb2_transfer_stop(sc->sc_xfer[UCHCOM_BULK_DT_RD]); 749 } 750 751 static void 752 uchcom_start_write(struct usb2_com_softc *ucom) 753 { 754 struct uchcom_softc *sc = ucom->sc_parent; 755 756 usb2_transfer_start(sc->sc_xfer[UCHCOM_BULK_DT_WR]); 757 } 758 759 static void 760 uchcom_stop_write(struct usb2_com_softc *ucom) 761 { 762 struct uchcom_softc *sc = ucom->sc_parent; 763 764 usb2_transfer_stop(sc->sc_xfer[UCHCOM_BULK_DT_WR]); 765 } 766 767 /* ---------------------------------------------------------------------- 768 * callback when the modem status is changed. 769 */ 770 static void 771 uchcom_intr_callback(struct usb2_xfer *xfer) 772 { 773 struct uchcom_softc *sc = xfer->priv_sc; 774 uint8_t buf[UCHCOM_INTR_LEAST]; 775 776 switch (USB_GET_STATE(xfer)) { 777 case USB_ST_TRANSFERRED: 778 779 DPRINTF("actlen = %u\n", xfer->actlen); 780 781 if (xfer->actlen >= UCHCOM_INTR_LEAST) { 782 usb2_copy_out(xfer->frbuffers, 0, buf, 783 UCHCOM_INTR_LEAST); 784 785 DPRINTF("data = 0x%02X 0x%02X 0x%02X 0x%02X\n", 786 (unsigned)buf[0], (unsigned)buf[1], 787 (unsigned)buf[2], (unsigned)buf[3]); 788 789 uchcom_convert_status(sc, buf[UCHCOM_INTR_STAT1]); 790 usb2_com_status_change(&sc->sc_ucom); 791 } 792 case USB_ST_SETUP: 793 tr_setup: 794 xfer->frlengths[0] = xfer->max_data_length; 795 usb2_start_hardware(xfer); 796 break; 797 798 default: /* Error */ 799 if (xfer->error != USB_ERR_CANCELLED) { 800 /* try to clear stall first */ 801 xfer->flags.stall_pipe = 1; 802 goto tr_setup; 803 } 804 break; 805 } 806 } 807 808 static void 809 uchcom_write_callback(struct usb2_xfer *xfer) 810 { 811 struct uchcom_softc *sc = xfer->priv_sc; 812 uint32_t actlen; 813 814 switch (USB_GET_STATE(xfer)) { 815 case USB_ST_SETUP: 816 case USB_ST_TRANSFERRED: 817 tr_setup: 818 if (usb2_com_get_data(&sc->sc_ucom, xfer->frbuffers, 0, 819 UCHCOM_BULK_BUF_SIZE, &actlen)) { 820 821 DPRINTF("actlen = %d\n", actlen); 822 823 xfer->frlengths[0] = actlen; 824 usb2_start_hardware(xfer); 825 } 826 return; 827 828 default: /* Error */ 829 if (xfer->error != USB_ERR_CANCELLED) { 830 /* try to clear stall first */ 831 xfer->flags.stall_pipe = 1; 832 goto tr_setup; 833 } 834 return; 835 836 } 837 } 838 839 static void 840 uchcom_read_callback(struct usb2_xfer *xfer) 841 { 842 struct uchcom_softc *sc = xfer->priv_sc; 843 844 switch (USB_GET_STATE(xfer)) { 845 case USB_ST_TRANSFERRED: 846 usb2_com_put_data(&sc->sc_ucom, xfer->frbuffers, 0, xfer->actlen); 847 848 case USB_ST_SETUP: 849 tr_setup: 850 xfer->frlengths[0] = xfer->max_data_length; 851 usb2_start_hardware(xfer); 852 return; 853 854 default: /* Error */ 855 if (xfer->error != USB_ERR_CANCELLED) { 856 /* try to clear stall first */ 857 xfer->flags.stall_pipe = 1; 858 goto tr_setup; 859 } 860 return; 861 } 862 } 863 864 static device_method_t uchcom_methods[] = { 865 /* Device interface */ 866 DEVMETHOD(device_probe, uchcom_probe), 867 DEVMETHOD(device_attach, uchcom_attach), 868 DEVMETHOD(device_detach, uchcom_detach), 869 870 {0, 0} 871 }; 872 873 static driver_t uchcom_driver = { 874 "ucom", 875 uchcom_methods, 876 sizeof(struct uchcom_softc) 877 }; 878 879 static devclass_t uchcom_devclass; 880 881 DRIVER_MODULE(uchcom, ushub, uchcom_driver, uchcom_devclass, NULL, 0); 882 MODULE_DEPEND(uchcom, ucom, 1, 1, 1); 883 MODULE_DEPEND(uchcom, usb, 1, 1, 1); 884