1 /* $OpenBSD: uchcom.c,v 1.31 2021/11/19 07:58:34 dlg Exp $ */ 2 /* $NetBSD: uchcom.c,v 1.1 2007/09/03 17:57:37 tshiozak Exp $ */ 3 4 /* 5 * Copyright (c) 2007 The NetBSD Foundation, Inc. 6 * All rights reserved. 7 * 8 * This code is derived from software contributed to The NetBSD Foundation 9 * by Takuya SHIOZAKI (tshiozak@netbsd.org). 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 * POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 /* 34 * driver for WinChipHead CH341/340, the worst USB-serial chip in the world. 35 */ 36 37 #include <sys/param.h> 38 #include <sys/systm.h> 39 #include <sys/kernel.h> 40 #include <sys/malloc.h> 41 #include <sys/tty.h> 42 #include <sys/device.h> 43 44 #include <dev/usb/usb.h> 45 #include <dev/usb/usbdi.h> 46 #include <dev/usb/usbdi_util.h> 47 #include <dev/usb/usbdevs.h> 48 #include <dev/usb/ucomvar.h> 49 50 #ifdef UCHCOM_DEBUG 51 #define DPRINTFN(n, x) do { if (uchcomdebug > (n)) printf x; } while (0) 52 int uchcomdebug = 0; 53 #else 54 #define DPRINTFN(n, x) 55 #endif 56 #define DPRINTF(x) DPRINTFN(0, x) 57 58 #define UCHCOM_IFACE_INDEX 0 59 60 #define UCHCOM_REV_CH340 0x0250 61 #define UCHCOM_INPUT_BUF_SIZE 8 62 63 #define UCHCOM_REQ_GET_VERSION 0x5F 64 #define UCHCOM_REQ_READ_REG 0x95 65 #define UCHCOM_REQ_WRITE_REG 0x9A 66 #define UCHCOM_REQ_RESET 0xA1 67 #define UCHCOM_REQ_SET_DTRRTS 0xA4 68 69 #define UCHCOM_REG_STAT1 0x06 70 #define UCHCOM_REG_STAT2 0x07 71 #define UCHCOM_REG_BPS_PRE 0x12 72 #define UCHCOM_REG_BPS_DIV 0x13 73 #define UCHCOM_REG_BPS_MOD 0x14 74 #define UCHCOM_REG_BPS_PAD 0x0F 75 #define UCHCOM_REG_BREAK 0x05 76 #define UCHCOM_REG_LCR 0x18 77 #define UCHCOM_REG_LCR2 0x25 78 79 #define UCHCOM_VER_20 0x20 80 81 #define UCHCOM_BASE_UNKNOWN 0 82 #define UCHCOM_BPS_MOD_BASE 20000000 83 #define UCHCOM_BPS_MOD_BASE_OFS 1100 84 85 #define UCHCOM_BPS_PRE_IMM 0x80 /* CH341: immediate RX forwarding */ 86 87 #define UCHCOM_DTR_MASK 0x20 88 #define UCHCOM_RTS_MASK 0x40 89 90 #define UCHCOM_BREAK_MASK 0x01 91 92 #define UCHCOM_LCR_CS5 0x00 93 #define UCHCOM_LCR_CS6 0x01 94 #define UCHCOM_LCR_CS7 0x02 95 #define UCHCOM_LCR_CS8 0x03 96 #define UCHCOM_LCR_STOPB 0x04 97 #define UCHCOM_LCR_PARENB 0x08 98 #define UCHCOM_LCR_PARODD 0x00 99 #define UCHCOM_LCR_PAREVEN 0x10 100 #define UCHCOM_LCR_PARMARK 0x20 101 #define UCHCOM_LCR_PARSPACE 0x30 102 #define UCHCOM_LCR_TXE 0x40 103 #define UCHCOM_LCR_RXE 0x80 104 105 #define UCHCOM_INTR_STAT1 0x02 106 #define UCHCOM_INTR_STAT2 0x03 107 #define UCHCOM_INTR_LEAST 4 108 109 /* 110 * XXX - these magic numbers come from Linux (drivers/usb/serial/ch341.c). 111 * The manufacturer was unresponsive when asked for documentation. 112 */ 113 #define UCHCOM_RESET_VALUE 0x501F /* line mode? */ 114 #define UCHCOM_RESET_INDEX 0xD90A /* baud rate? */ 115 116 #define UCHCOMIBUFSIZE 256 117 #define UCHCOMOBUFSIZE 256 118 119 struct uchcom_softc 120 { 121 struct device sc_dev; 122 struct usbd_device *sc_udev; 123 struct device *sc_subdev; 124 struct usbd_interface *sc_iface; 125 /* */ 126 int sc_intr_endpoint; 127 struct usbd_pipe *sc_intr_pipe; 128 u_char *sc_intr_buf; 129 int sc_isize; 130 /* */ 131 int sc_release; 132 uint8_t sc_version; 133 int sc_dtr; 134 int sc_rts; 135 u_char sc_lsr; 136 u_char sc_msr; 137 int sc_lcr1; 138 int sc_lcr2; 139 }; 140 141 struct uchcom_endpoints 142 { 143 int ep_bulkin; 144 int ep_bulkout; 145 int ep_intr; 146 int ep_intr_size; 147 }; 148 149 struct uchcom_divider 150 { 151 uint8_t dv_prescaler; 152 uint8_t dv_div; 153 uint8_t dv_mod; 154 }; 155 156 struct uchcom_divider_record 157 { 158 uint32_t dvr_high; 159 uint32_t dvr_low; 160 uint32_t dvr_base_clock; 161 struct uchcom_divider dvr_divider; 162 }; 163 164 static const struct uchcom_divider_record dividers[] = 165 { 166 { 307200, 307200, UCHCOM_BASE_UNKNOWN, { 7, 0xD9, 0 } }, 167 { 921600, 921600, UCHCOM_BASE_UNKNOWN, { 7, 0xF3, 0 } }, 168 { 2999999, 23530, 6000000, { 3, 0, 0 } }, 169 { 23529, 2942, 750000, { 2, 0, 0 } }, 170 { 2941, 368, 93750, { 1, 0, 0 } }, 171 { 367, 1, 11719, { 0, 0, 0 } }, 172 }; 173 174 void uchcom_get_status(void *, int, u_char *, u_char *); 175 void uchcom_set(void *, int, int, int); 176 int uchcom_param(void *, int, struct termios *); 177 int uchcom_open(void *, int); 178 void uchcom_close(void *, int); 179 void uchcom_intr(struct usbd_xfer *, void *, usbd_status); 180 181 int uchcom_find_ifaces(struct uchcom_softc *, 182 struct usbd_interface **); 183 int uchcom_find_endpoints(struct uchcom_softc *, 184 struct uchcom_endpoints *); 185 void uchcom_close_intr_pipe(struct uchcom_softc *); 186 187 188 usbd_status uchcom_generic_control_out(struct uchcom_softc *sc, 189 uint8_t reqno, uint16_t value, uint16_t index); 190 usbd_status uchcom_generic_control_in(struct uchcom_softc *, uint8_t, 191 uint16_t, uint16_t, void *, int, int *); 192 usbd_status uchcom_write_reg(struct uchcom_softc *, uint8_t, uint8_t, 193 uint8_t, uint8_t); 194 usbd_status uchcom_read_reg(struct uchcom_softc *, uint8_t, uint8_t *, 195 uint8_t, uint8_t *); 196 usbd_status uchcom_get_version(struct uchcom_softc *, uint8_t *); 197 usbd_status uchcom_read_status(struct uchcom_softc *, uint8_t *); 198 usbd_status uchcom_set_dtrrts_10(struct uchcom_softc *, uint8_t); 199 usbd_status uchcom_set_dtrrts_20(struct uchcom_softc *, uint8_t); 200 int uchcom_update_version(struct uchcom_softc *); 201 void uchcom_convert_status(struct uchcom_softc *, uint8_t); 202 int uchcom_update_status(struct uchcom_softc *); 203 int uchcom_set_dtrrts(struct uchcom_softc *, int, int); 204 int uchcom_set_break(struct uchcom_softc *, int); 205 int uchcom_calc_divider_settings(struct uchcom_divider *, uint32_t); 206 int uchcom_set_dte_rate(struct uchcom_softc *, uint32_t); 207 int uchcom_set_line_control(struct uchcom_softc *, tcflag_t); 208 int uchcom_clear_chip(struct uchcom_softc *); 209 int uchcom_reset_chip(struct uchcom_softc *); 210 int uchcom_setup_comm(struct uchcom_softc *); 211 int uchcom_setup_intr_pipe(struct uchcom_softc *); 212 213 214 int uchcom_match(struct device *, void *, void *); 215 void uchcom_attach(struct device *, struct device *, void *); 216 int uchcom_detach(struct device *, int); 217 218 struct ucom_methods uchcom_methods = { 219 uchcom_get_status, 220 uchcom_set, 221 uchcom_param, 222 NULL, 223 uchcom_open, 224 uchcom_close, 225 NULL, 226 NULL, 227 }; 228 229 static const struct usb_devno uchcom_devs[] = { 230 { USB_VENDOR_WCH, USB_PRODUCT_WCH_CH341 }, 231 { USB_VENDOR_WCH2, USB_PRODUCT_WCH2_CH340 }, 232 { USB_VENDOR_WCH2, USB_PRODUCT_WCH2_CH341A } 233 }; 234 235 struct cfdriver uchcom_cd = { 236 NULL, "uchcom", DV_DULL 237 }; 238 239 const struct cfattach uchcom_ca = { 240 sizeof(struct uchcom_softc), uchcom_match, uchcom_attach, uchcom_detach 241 }; 242 243 /* ---------------------------------------------------------------------- 244 * driver entry points 245 */ 246 247 int 248 uchcom_match(struct device *parent, void *match, void *aux) 249 { 250 struct usb_attach_arg *uaa = aux; 251 252 if (uaa->iface == NULL) 253 return UMATCH_NONE; 254 255 return (usb_lookup(uchcom_devs, uaa->vendor, uaa->product) != NULL ? 256 UMATCH_VENDOR_PRODUCT : UMATCH_NONE); 257 } 258 259 void 260 uchcom_attach(struct device *parent, struct device *self, void *aux) 261 { 262 struct uchcom_softc *sc = (struct uchcom_softc *)self; 263 struct usb_attach_arg *uaa = aux; 264 struct ucom_attach_args uca; 265 struct usbd_device *dev = uaa->device; 266 struct uchcom_endpoints endpoints; 267 268 sc->sc_udev = dev; 269 sc->sc_dtr = sc->sc_rts = -1; 270 sc->sc_release = uaa->release; 271 272 DPRINTF(("\n\nuchcom attach: sc=%p\n", sc)); 273 274 switch (sc->sc_release) { 275 case UCHCOM_REV_CH340: 276 printf("%s: CH340\n", sc->sc_dev.dv_xname); 277 break; 278 default: 279 printf("%s: CH341\n", sc->sc_dev.dv_xname); 280 break; 281 } 282 283 if (uchcom_find_ifaces(sc, &sc->sc_iface)) 284 goto failed; 285 286 if (uchcom_find_endpoints(sc, &endpoints)) 287 goto failed; 288 289 sc->sc_intr_endpoint = endpoints.ep_intr; 290 sc->sc_isize = endpoints.ep_intr_size; 291 292 /* setup ucom layer */ 293 uca.portno = UCOM_UNK_PORTNO; 294 uca.bulkin = endpoints.ep_bulkin; 295 uca.bulkout = endpoints.ep_bulkout; 296 uca.ibufsize = UCHCOMIBUFSIZE; 297 uca.obufsize = UCHCOMOBUFSIZE; 298 uca.ibufsizepad = UCHCOMIBUFSIZE; 299 uca.opkthdrlen = 0; 300 uca.device = dev; 301 uca.iface = sc->sc_iface; 302 uca.methods = &uchcom_methods; 303 uca.arg = sc; 304 uca.info = NULL; 305 306 sc->sc_subdev = config_found_sm(self, &uca, ucomprint, ucomsubmatch); 307 308 return; 309 310 failed: 311 usbd_deactivate(sc->sc_udev); 312 } 313 314 int 315 uchcom_detach(struct device *self, int flags) 316 { 317 struct uchcom_softc *sc = (struct uchcom_softc *)self; 318 int rv = 0; 319 320 DPRINTF(("uchcom_detach: sc=%p flags=%d\n", sc, flags)); 321 322 uchcom_close_intr_pipe(sc); 323 324 if (sc->sc_subdev != NULL) { 325 rv = config_detach(sc->sc_subdev, flags); 326 sc->sc_subdev = NULL; 327 } 328 329 return rv; 330 } 331 332 int 333 uchcom_find_ifaces(struct uchcom_softc *sc, struct usbd_interface **riface) 334 { 335 usbd_status err; 336 337 err = usbd_device2interface_handle(sc->sc_udev, UCHCOM_IFACE_INDEX, 338 riface); 339 if (err) { 340 printf("\n%s: failed to get interface: %s\n", 341 sc->sc_dev.dv_xname, usbd_errstr(err)); 342 return -1; 343 } 344 345 return 0; 346 } 347 348 int 349 uchcom_find_endpoints(struct uchcom_softc *sc, 350 struct uchcom_endpoints *endpoints) 351 { 352 int i, bin=-1, bout=-1, intr=-1, isize=0; 353 usb_interface_descriptor_t *id; 354 usb_endpoint_descriptor_t *ed; 355 356 id = usbd_get_interface_descriptor(sc->sc_iface); 357 358 for (i = 0; i < id->bNumEndpoints; i++) { 359 ed = usbd_interface2endpoint_descriptor(sc->sc_iface, i); 360 if (ed == NULL) { 361 printf("%s: no endpoint descriptor for %d\n", 362 sc->sc_dev.dv_xname, i); 363 return -1; 364 } 365 366 if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN && 367 UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT) { 368 intr = ed->bEndpointAddress; 369 isize = UGETW(ed->wMaxPacketSize); 370 } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN && 371 UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) { 372 bin = ed->bEndpointAddress; 373 } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT && 374 UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) { 375 bout = ed->bEndpointAddress; 376 } 377 } 378 379 if (intr == -1 || bin == -1 || bout == -1) { 380 if (intr == -1) { 381 printf("%s: no interrupt end point\n", 382 sc->sc_dev.dv_xname); 383 } 384 if (bin == -1) { 385 printf("%s: no data bulk in end point\n", 386 sc->sc_dev.dv_xname); 387 } 388 if (bout == -1) { 389 printf("%s: no data bulk out end point\n", 390 sc->sc_dev.dv_xname); 391 } 392 return -1; 393 } 394 if (isize < UCHCOM_INTR_LEAST) { 395 printf("%s: intr pipe is too short", sc->sc_dev.dv_xname); 396 return -1; 397 } 398 399 DPRINTF(("%s: bulkin=%d, bulkout=%d, intr=%d, isize=%d\n", 400 sc->sc_dev.dv_xname, bin, bout, intr, isize)); 401 402 endpoints->ep_intr = intr; 403 endpoints->ep_intr_size = isize; 404 endpoints->ep_bulkin = bin; 405 endpoints->ep_bulkout = bout; 406 407 return 0; 408 } 409 410 411 /* ---------------------------------------------------------------------- 412 * low level i/o 413 */ 414 415 usbd_status 416 uchcom_generic_control_out(struct uchcom_softc *sc, uint8_t reqno, 417 uint16_t value, uint16_t index) 418 { 419 usb_device_request_t req; 420 421 req.bmRequestType = UT_WRITE_VENDOR_DEVICE; 422 req.bRequest = reqno; 423 USETW(req.wValue, value); 424 USETW(req.wIndex, index); 425 USETW(req.wLength, 0); 426 427 return usbd_do_request(sc->sc_udev, &req, 0); 428 } 429 430 usbd_status 431 uchcom_generic_control_in(struct uchcom_softc *sc, uint8_t reqno, 432 uint16_t value, uint16_t index, void *buf, int buflen, int *actlen) 433 { 434 usb_device_request_t req; 435 436 req.bmRequestType = UT_READ_VENDOR_DEVICE; 437 req.bRequest = reqno; 438 USETW(req.wValue, value); 439 USETW(req.wIndex, index); 440 USETW(req.wLength, (uint16_t)buflen); 441 442 return usbd_do_request_flags(sc->sc_udev, &req, buf, 443 USBD_SHORT_XFER_OK, actlen, 444 USBD_DEFAULT_TIMEOUT); 445 } 446 447 usbd_status 448 uchcom_write_reg(struct uchcom_softc *sc, 449 uint8_t reg1, uint8_t val1, uint8_t reg2, uint8_t val2) 450 { 451 DPRINTF(("uchcom: write reg 0x%02X<-0x%02X, 0x%02X<-0x%02X\n", 452 (unsigned)reg1, (unsigned)val1, 453 (unsigned)reg2, (unsigned)val2)); 454 return uchcom_generic_control_out( 455 sc, UCHCOM_REQ_WRITE_REG, 456 reg1|((uint16_t)reg2<<8), val1|((uint16_t)val2<<8)); 457 } 458 459 usbd_status 460 uchcom_read_reg(struct uchcom_softc *sc, 461 uint8_t reg1, uint8_t *rval1, uint8_t reg2, uint8_t *rval2) 462 { 463 uint8_t buf[UCHCOM_INPUT_BUF_SIZE]; 464 usbd_status err; 465 int actin; 466 467 err = uchcom_generic_control_in( 468 sc, UCHCOM_REQ_READ_REG, 469 reg1|((uint16_t)reg2<<8), 0, buf, sizeof buf, &actin); 470 if (err) 471 return err; 472 473 DPRINTF(("uchcom: read reg 0x%02X->0x%02X, 0x%02X->0x%02X\n", 474 (unsigned)reg1, (unsigned)buf[0], 475 (unsigned)reg2, (unsigned)buf[1])); 476 477 if (rval1) *rval1 = buf[0]; 478 if (rval2) *rval2 = buf[1]; 479 480 return USBD_NORMAL_COMPLETION; 481 } 482 483 usbd_status 484 uchcom_get_version(struct uchcom_softc *sc, uint8_t *rver) 485 { 486 uint8_t buf[UCHCOM_INPUT_BUF_SIZE]; 487 usbd_status err; 488 int actin; 489 490 err = uchcom_generic_control_in( 491 sc, UCHCOM_REQ_GET_VERSION, 0, 0, buf, sizeof buf, &actin); 492 if (err) 493 return err; 494 495 if (rver) *rver = buf[0]; 496 497 return USBD_NORMAL_COMPLETION; 498 } 499 500 usbd_status 501 uchcom_read_status(struct uchcom_softc *sc, uint8_t *rval) 502 { 503 return uchcom_read_reg(sc, UCHCOM_REG_STAT1, rval, UCHCOM_REG_STAT2, 504 NULL); 505 } 506 507 usbd_status 508 uchcom_set_dtrrts_10(struct uchcom_softc *sc, uint8_t val) 509 { 510 return uchcom_write_reg(sc, UCHCOM_REG_STAT1, val, UCHCOM_REG_STAT1, 511 val); 512 } 513 514 usbd_status 515 uchcom_set_dtrrts_20(struct uchcom_softc *sc, uint8_t val) 516 { 517 return uchcom_generic_control_out(sc, UCHCOM_REQ_SET_DTRRTS, val, 0); 518 } 519 520 521 /* ---------------------------------------------------------------------- 522 * middle layer 523 */ 524 525 int 526 uchcom_update_version(struct uchcom_softc *sc) 527 { 528 usbd_status err; 529 530 err = uchcom_get_version(sc, &sc->sc_version); 531 if (err) { 532 printf("%s: cannot get version: %s\n", 533 sc->sc_dev.dv_xname, usbd_errstr(err)); 534 return EIO; 535 } 536 537 return 0; 538 } 539 540 void 541 uchcom_convert_status(struct uchcom_softc *sc, uint8_t cur) 542 { 543 sc->sc_dtr = !(cur & UCHCOM_DTR_MASK); 544 sc->sc_rts = !(cur & UCHCOM_RTS_MASK); 545 546 cur = ~cur & 0x0F; 547 sc->sc_msr = (cur << 4) | ((sc->sc_msr >> 4) ^ cur); 548 } 549 550 int 551 uchcom_update_status(struct uchcom_softc *sc) 552 { 553 usbd_status err; 554 uint8_t cur; 555 556 err = uchcom_read_status(sc, &cur); 557 if (err) { 558 printf("%s: cannot update status: %s\n", 559 sc->sc_dev.dv_xname, usbd_errstr(err)); 560 return EIO; 561 } 562 uchcom_convert_status(sc, cur); 563 564 return 0; 565 } 566 567 568 int 569 uchcom_set_dtrrts(struct uchcom_softc *sc, int dtr, int rts) 570 { 571 usbd_status err; 572 uint8_t val = 0; 573 574 if (dtr) val |= UCHCOM_DTR_MASK; 575 if (rts) val |= UCHCOM_RTS_MASK; 576 577 if (sc->sc_version < UCHCOM_VER_20) 578 err = uchcom_set_dtrrts_10(sc, ~val); 579 else 580 err = uchcom_set_dtrrts_20(sc, ~val); 581 582 if (err) { 583 printf("%s: cannot set DTR/RTS: %s\n", 584 sc->sc_dev.dv_xname, usbd_errstr(err)); 585 return EIO; 586 } 587 588 return 0; 589 } 590 591 int 592 uchcom_set_break(struct uchcom_softc *sc, int onoff) 593 { 594 usbd_status err; 595 uint8_t brk, lcr; 596 597 err = uchcom_read_reg(sc, UCHCOM_REG_BREAK, &brk, UCHCOM_REG_LCR, &lcr); 598 if (err) 599 return EIO; 600 if (onoff) { 601 /* on - clear bits */ 602 brk &= ~UCHCOM_BREAK_MASK; 603 lcr &= ~UCHCOM_LCR_TXE; 604 } else { 605 /* off - set bits */ 606 brk |= UCHCOM_BREAK_MASK; 607 lcr |= UCHCOM_LCR_TXE; 608 } 609 err = uchcom_write_reg(sc, UCHCOM_REG_BREAK, brk, UCHCOM_REG_LCR, lcr); 610 if (err) 611 return EIO; 612 613 return 0; 614 } 615 616 int 617 uchcom_calc_divider_settings(struct uchcom_divider *dp, uint32_t rate) 618 { 619 int i; 620 const struct uchcom_divider_record *rp; 621 uint32_t div, rem, mod; 622 623 /* find record */ 624 for (i=0; i<nitems(dividers); i++) { 625 if (dividers[i].dvr_high >= rate && 626 dividers[i].dvr_low <= rate) { 627 rp = ÷rs[i]; 628 goto found; 629 } 630 } 631 return -1; 632 633 found: 634 dp->dv_prescaler = rp->dvr_divider.dv_prescaler; 635 if (rp->dvr_base_clock == UCHCOM_BASE_UNKNOWN) 636 dp->dv_div = rp->dvr_divider.dv_div; 637 else { 638 div = rp->dvr_base_clock / rate; 639 rem = rp->dvr_base_clock % rate; 640 if (div==0 || div>=0xFF) 641 return -1; 642 if ((rem<<1) >= rate) 643 div += 1; 644 dp->dv_div = (uint8_t)-div; 645 } 646 647 mod = UCHCOM_BPS_MOD_BASE/rate + UCHCOM_BPS_MOD_BASE_OFS; 648 mod = mod + mod/2; 649 650 dp->dv_mod = mod / 0x100; 651 652 return 0; 653 } 654 655 int 656 uchcom_set_dte_rate(struct uchcom_softc *sc, uint32_t rate) 657 { 658 usbd_status err; 659 struct uchcom_divider dv; 660 661 if (uchcom_calc_divider_settings(&dv, rate)) 662 return EINVAL; 663 664 if ((err = uchcom_write_reg(sc, 665 UCHCOM_REG_BPS_PRE, dv.dv_prescaler, 666 UCHCOM_REG_BPS_DIV, dv.dv_div)) || 667 (err = uchcom_write_reg(sc, 668 UCHCOM_REG_BPS_MOD, dv.dv_mod, 669 UCHCOM_REG_BPS_PAD, 0))) { 670 printf("%s: cannot set DTE rate: %s\n", 671 sc->sc_dev.dv_xname, usbd_errstr(err)); 672 return EIO; 673 } 674 675 return 0; 676 } 677 678 int 679 uchcom_set_line_control(struct uchcom_softc *sc, tcflag_t cflag) 680 { 681 usbd_status err; 682 uint8_t lcr = 0, lcr2 = 0; 683 684 if (sc->sc_release == UCHCOM_REV_CH340) { 685 /* 686 * XXX: it is difficult to handle the line control 687 * appropriately on CH340: 688 * work as chip default - CS8, no parity, !CSTOPB 689 * other modes are not supported. 690 */ 691 switch (ISSET(cflag, CSIZE)) { 692 case CS5: 693 case CS6: 694 case CS7: 695 return EINVAL; 696 case CS8: 697 break; 698 } 699 if (ISSET(cflag, PARENB) || ISSET(cflag, CSTOPB)) 700 return EINVAL; 701 return 0; 702 } 703 704 err = uchcom_read_reg(sc, UCHCOM_REG_LCR, &lcr, 705 UCHCOM_REG_LCR2, &lcr2); 706 if (err) { 707 printf("%s: cannot get LCR: %s\n", 708 sc->sc_dev.dv_xname, usbd_errstr(err)); 709 return EIO; 710 } 711 712 lcr = UCHCOM_LCR_RXE | UCHCOM_LCR_TXE; 713 714 switch (ISSET(cflag, CSIZE)) { 715 case CS5: 716 lcr |= UCHCOM_LCR_CS5; 717 break; 718 case CS6: 719 lcr |= UCHCOM_LCR_CS6; 720 break; 721 case CS7: 722 lcr |= UCHCOM_LCR_CS7; 723 break; 724 case CS8: 725 lcr |= UCHCOM_LCR_CS8; 726 break; 727 } 728 729 if (ISSET(cflag, PARENB)) { 730 lcr |= UCHCOM_LCR_PARENB; 731 if (!ISSET(cflag, PARODD)) 732 lcr |= UCHCOM_LCR_PAREVEN; 733 } 734 735 if (ISSET(cflag, CSTOPB)) { 736 lcr |= UCHCOM_LCR_STOPB; 737 } 738 739 err = uchcom_write_reg(sc, UCHCOM_REG_LCR, lcr, UCHCOM_REG_LCR2, lcr2); 740 if (err) { 741 printf("%s: cannot set LCR: %s\n", 742 sc->sc_dev.dv_xname, usbd_errstr(err)); 743 return EIO; 744 } 745 746 return 0; 747 } 748 749 int 750 uchcom_clear_chip(struct uchcom_softc *sc) 751 { 752 usbd_status err; 753 754 DPRINTF(("%s: clear\n", sc->sc_dev.dv_xname)); 755 err = uchcom_generic_control_out(sc, UCHCOM_REQ_RESET, 0, 0); 756 if (err) { 757 printf("%s: cannot clear: %s\n", 758 sc->sc_dev.dv_xname, usbd_errstr(err)); 759 return EIO; 760 } 761 762 return 0; 763 } 764 765 int 766 uchcom_reset_chip(struct uchcom_softc *sc) 767 { 768 usbd_status err; 769 770 DPRINTF(("%s: reset\n", sc->sc_dev.dv_xname)); 771 772 err = uchcom_generic_control_out(sc, UCHCOM_REQ_RESET, 773 UCHCOM_RESET_VALUE, 774 UCHCOM_RESET_INDEX); 775 if (err) 776 goto failed; 777 778 return 0; 779 780 failed: 781 printf("%s: cannot reset: %s\n", 782 sc->sc_dev.dv_xname, usbd_errstr(err)); 783 return EIO; 784 } 785 786 int 787 uchcom_setup_comm(struct uchcom_softc *sc) 788 { 789 int ret; 790 791 ret = uchcom_update_version(sc); 792 if (ret) 793 return ret; 794 795 ret = uchcom_clear_chip(sc); 796 if (ret) 797 return ret; 798 799 ret = uchcom_set_dte_rate(sc, TTYDEF_SPEED); 800 if (ret) 801 return ret; 802 803 ret = uchcom_set_line_control(sc, CS8); 804 if (ret) 805 return ret; 806 807 ret = uchcom_update_status(sc); 808 if (ret) 809 return ret; 810 811 ret = uchcom_reset_chip(sc); 812 if (ret) 813 return ret; 814 815 ret = uchcom_set_dte_rate(sc, TTYDEF_SPEED); /* XXX */ 816 if (ret) 817 return ret; 818 819 sc->sc_dtr = sc->sc_rts = 1; 820 ret = uchcom_set_dtrrts(sc, sc->sc_dtr, sc->sc_rts); 821 if (ret) 822 return ret; 823 824 return 0; 825 } 826 827 int 828 uchcom_setup_intr_pipe(struct uchcom_softc *sc) 829 { 830 usbd_status err; 831 832 if (sc->sc_intr_endpoint != -1 && sc->sc_intr_pipe == NULL) { 833 sc->sc_intr_buf = malloc(sc->sc_isize, M_USBDEV, M_WAITOK); 834 err = usbd_open_pipe_intr(sc->sc_iface, 835 sc->sc_intr_endpoint, 836 USBD_SHORT_XFER_OK, 837 &sc->sc_intr_pipe, sc, 838 sc->sc_intr_buf, 839 sc->sc_isize, 840 uchcom_intr, USBD_DEFAULT_INTERVAL); 841 if (err) { 842 printf("%s: cannot open interrupt pipe: %s\n", 843 sc->sc_dev.dv_xname, 844 usbd_errstr(err)); 845 return EIO; 846 } 847 } 848 return 0; 849 } 850 851 void 852 uchcom_close_intr_pipe(struct uchcom_softc *sc) 853 { 854 usbd_status err; 855 856 if (sc->sc_intr_pipe != NULL) { 857 err = usbd_close_pipe(sc->sc_intr_pipe); 858 if (err) 859 printf("%s: close interrupt pipe failed: %s\n", 860 sc->sc_dev.dv_xname, usbd_errstr(err)); 861 free(sc->sc_intr_buf, M_USBDEV, sc->sc_isize); 862 sc->sc_intr_pipe = NULL; 863 } 864 } 865 866 867 /* ---------------------------------------------------------------------- 868 * methods for ucom 869 */ 870 void 871 uchcom_get_status(void *arg, int portno, u_char *rlsr, u_char *rmsr) 872 { 873 struct uchcom_softc *sc = arg; 874 875 if (usbd_is_dying(sc->sc_udev)) 876 return; 877 878 *rlsr = sc->sc_lsr; 879 *rmsr = sc->sc_msr; 880 } 881 882 void 883 uchcom_set(void *arg, int portno, int reg, int onoff) 884 { 885 struct uchcom_softc *sc = arg; 886 887 if (usbd_is_dying(sc->sc_udev)) 888 return; 889 890 switch (reg) { 891 case UCOM_SET_DTR: 892 sc->sc_dtr = !!onoff; 893 uchcom_set_dtrrts(sc, sc->sc_dtr, sc->sc_rts); 894 break; 895 case UCOM_SET_RTS: 896 sc->sc_rts = !!onoff; 897 uchcom_set_dtrrts(sc, sc->sc_dtr, sc->sc_rts); 898 break; 899 case UCOM_SET_BREAK: 900 uchcom_set_break(sc, onoff); 901 break; 902 } 903 } 904 905 int 906 uchcom_param(void *arg, int portno, struct termios *t) 907 { 908 struct uchcom_softc *sc = arg; 909 int ret; 910 911 if (usbd_is_dying(sc->sc_udev)) 912 return 0; 913 914 ret = uchcom_set_line_control(sc, t->c_cflag); 915 if (ret) 916 return ret; 917 918 ret = uchcom_set_dte_rate(sc, t->c_ospeed); 919 if (ret) 920 return ret; 921 922 return 0; 923 } 924 925 int 926 uchcom_open(void *arg, int portno) 927 { 928 int ret; 929 struct uchcom_softc *sc = arg; 930 931 if (usbd_is_dying(sc->sc_udev)) 932 return EIO; 933 934 ret = uchcom_setup_intr_pipe(sc); 935 if (ret) 936 return ret; 937 938 ret = uchcom_setup_comm(sc); 939 if (ret) 940 return ret; 941 942 return 0; 943 } 944 945 void 946 uchcom_close(void *arg, int portno) 947 { 948 struct uchcom_softc *sc = arg; 949 950 uchcom_close_intr_pipe(sc); 951 } 952 953 954 /* ---------------------------------------------------------------------- 955 * callback when the modem status is changed. 956 */ 957 void 958 uchcom_intr(struct usbd_xfer *xfer, void *priv, usbd_status status) 959 { 960 struct uchcom_softc *sc = priv; 961 u_char *buf = sc->sc_intr_buf; 962 963 if (usbd_is_dying(sc->sc_udev)) 964 return; 965 966 if (status != USBD_NORMAL_COMPLETION) { 967 if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) 968 return; 969 970 DPRINTF(("%s: abnormal status: %s\n", 971 sc->sc_dev.dv_xname, usbd_errstr(status))); 972 usbd_clear_endpoint_stall_async(sc->sc_intr_pipe); 973 return; 974 } 975 DPRINTF(("%s: intr: 0x%02X 0x%02X 0x%02X 0x%02X " 976 "0x%02X 0x%02X 0x%02X 0x%02X\n", 977 sc->sc_dev.dv_xname, 978 (unsigned)buf[0], (unsigned)buf[1], 979 (unsigned)buf[2], (unsigned)buf[3], 980 (unsigned)buf[4], (unsigned)buf[5], 981 (unsigned)buf[6], (unsigned)buf[7])); 982 983 uchcom_convert_status(sc, buf[UCHCOM_INTR_STAT1]); 984 ucom_status_change((struct ucom_softc *) sc->sc_subdev); 985 } 986