1 /* $OpenBSD: uchcom.c,v 1.28 2020/07/31 10:49:33 mglocker 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_BREAK1 0x05 76 #define UCHCOM_REG_BREAK2 0x18 77 #define UCHCOM_REG_LCR1 0x18 78 #define UCHCOM_REG_LCR2 0x25 79 80 #define UCHCOM_VER_20 0x20 81 82 #define UCHCOM_BASE_UNKNOWN 0 83 #define UCHCOM_BPS_MOD_BASE 20000000 84 #define UCHCOM_BPS_MOD_BASE_OFS 1100 85 86 #define UCHCOM_DTR_MASK 0x20 87 #define UCHCOM_RTS_MASK 0x40 88 89 #define UCHCOM_BRK1_MASK 0x01 90 #define UCHCOM_BRK2_MASK 0x40 91 92 #define UCHCOM_INTR_STAT1 0x02 93 #define UCHCOM_INTR_STAT2 0x03 94 #define UCHCOM_INTR_LEAST 4 95 96 /* 97 * XXX - these magic numbers come from Linux (drivers/usb/serial/ch341.c). 98 * The manufacturer was unresponsive when asked for documentation. 99 */ 100 #define UCHCOM_RESET_VALUE 0x501F /* line mode? */ 101 #define UCHCOM_RESET_INDEX 0xD90A /* baud rate? */ 102 103 #define UCHCOMIBUFSIZE 256 104 #define UCHCOMOBUFSIZE 256 105 106 struct uchcom_softc 107 { 108 struct device sc_dev; 109 struct usbd_device *sc_udev; 110 struct device *sc_subdev; 111 struct usbd_interface *sc_iface; 112 /* */ 113 int sc_intr_endpoint; 114 struct usbd_pipe *sc_intr_pipe; 115 u_char *sc_intr_buf; 116 int sc_isize; 117 /* */ 118 uint8_t sc_version; 119 int sc_dtr; 120 int sc_rts; 121 u_char sc_lsr; 122 u_char sc_msr; 123 int sc_lcr1; 124 int sc_lcr2; 125 }; 126 127 struct uchcom_endpoints 128 { 129 int ep_bulkin; 130 int ep_bulkout; 131 int ep_intr; 132 int ep_intr_size; 133 }; 134 135 struct uchcom_divider 136 { 137 uint8_t dv_prescaler; 138 uint8_t dv_div; 139 uint8_t dv_mod; 140 }; 141 142 struct uchcom_divider_record 143 { 144 uint32_t dvr_high; 145 uint32_t dvr_low; 146 uint32_t dvr_base_clock; 147 struct uchcom_divider dvr_divider; 148 }; 149 150 static const struct uchcom_divider_record dividers[] = 151 { 152 { 307200, 307200, UCHCOM_BASE_UNKNOWN, { 7, 0xD9, 0 } }, 153 { 921600, 921600, UCHCOM_BASE_UNKNOWN, { 7, 0xF3, 0 } }, 154 { 2999999, 23530, 6000000, { 3, 0, 0 } }, 155 { 23529, 2942, 750000, { 2, 0, 0 } }, 156 { 2941, 368, 93750, { 1, 0, 0 } }, 157 { 367, 1, 11719, { 0, 0, 0 } }, 158 }; 159 160 void uchcom_get_status(void *, int, u_char *, u_char *); 161 void uchcom_set(void *, int, int, int); 162 int uchcom_param(void *, int, struct termios *); 163 int uchcom_open(void *, int); 164 void uchcom_close(void *, int); 165 void uchcom_intr(struct usbd_xfer *, void *, usbd_status); 166 167 int uchcom_find_ifaces(struct uchcom_softc *, 168 struct usbd_interface **); 169 int uchcom_find_endpoints(struct uchcom_softc *, 170 struct uchcom_endpoints *); 171 void uchcom_close_intr_pipe(struct uchcom_softc *); 172 173 174 usbd_status uchcom_generic_control_out(struct uchcom_softc *sc, 175 uint8_t reqno, uint16_t value, uint16_t index); 176 usbd_status uchcom_generic_control_in(struct uchcom_softc *, uint8_t, 177 uint16_t, uint16_t, void *, int, int *); 178 usbd_status uchcom_write_reg(struct uchcom_softc *, uint8_t, uint8_t, 179 uint8_t, uint8_t); 180 usbd_status uchcom_read_reg(struct uchcom_softc *, uint8_t, uint8_t *, 181 uint8_t, uint8_t *); 182 usbd_status uchcom_get_version(struct uchcom_softc *, uint8_t *); 183 usbd_status uchcom_read_status(struct uchcom_softc *, uint8_t *); 184 usbd_status uchcom_set_dtrrts_10(struct uchcom_softc *, uint8_t); 185 usbd_status uchcom_set_dtrrts_20(struct uchcom_softc *, uint8_t); 186 int uchcom_update_version(struct uchcom_softc *); 187 void uchcom_convert_status(struct uchcom_softc *, uint8_t); 188 int uchcom_update_status(struct uchcom_softc *); 189 int uchcom_set_dtrrts(struct uchcom_softc *, int, int); 190 int uchcom_set_break(struct uchcom_softc *, int); 191 int uchcom_calc_divider_settings(struct uchcom_divider *, uint32_t); 192 int uchcom_set_dte_rate(struct uchcom_softc *, uint32_t); 193 int uchcom_set_line_control(struct uchcom_softc *, tcflag_t); 194 int uchcom_clear_chip(struct uchcom_softc *); 195 int uchcom_reset_chip(struct uchcom_softc *); 196 int uchcom_setup_comm(struct uchcom_softc *); 197 int uchcom_setup_intr_pipe(struct uchcom_softc *); 198 199 200 int uchcom_match(struct device *, void *, void *); 201 void uchcom_attach(struct device *, struct device *, void *); 202 int uchcom_detach(struct device *, int); 203 204 struct ucom_methods uchcom_methods = { 205 uchcom_get_status, 206 uchcom_set, 207 uchcom_param, 208 NULL, 209 uchcom_open, 210 uchcom_close, 211 NULL, 212 NULL, 213 }; 214 215 static const struct usb_devno uchcom_devs[] = { 216 { USB_VENDOR_WCH, USB_PRODUCT_WCH_CH341 }, 217 { USB_VENDOR_WCH2, USB_PRODUCT_WCH2_CH340 }, 218 { USB_VENDOR_WCH2, USB_PRODUCT_WCH2_CH341A } 219 }; 220 221 struct cfdriver uchcom_cd = { 222 NULL, "uchcom", DV_DULL 223 }; 224 225 const struct cfattach uchcom_ca = { 226 sizeof(struct uchcom_softc), uchcom_match, uchcom_attach, uchcom_detach 227 }; 228 229 /* ---------------------------------------------------------------------- 230 * driver entry points 231 */ 232 233 int 234 uchcom_match(struct device *parent, void *match, void *aux) 235 { 236 struct usb_attach_arg *uaa = aux; 237 238 if (uaa->iface == NULL) 239 return UMATCH_NONE; 240 241 return (usb_lookup(uchcom_devs, uaa->vendor, uaa->product) != NULL ? 242 UMATCH_VENDOR_PRODUCT : UMATCH_NONE); 243 } 244 245 void 246 uchcom_attach(struct device *parent, struct device *self, void *aux) 247 { 248 struct uchcom_softc *sc = (struct uchcom_softc *)self; 249 struct usb_attach_arg *uaa = aux; 250 struct ucom_attach_args uca; 251 struct usbd_device *dev = uaa->device; 252 struct uchcom_endpoints endpoints; 253 254 sc->sc_udev = dev; 255 sc->sc_dtr = sc->sc_rts = -1; 256 257 DPRINTF(("\n\nuchcom attach: sc=%p\n", sc)); 258 259 switch (uaa->release) { 260 case UCHCOM_REV_CH340: 261 printf("%s: CH340\n", sc->sc_dev.dv_xname); 262 break; 263 default: 264 printf("%s: CH341\n", sc->sc_dev.dv_xname); 265 break; 266 } 267 268 if (uchcom_find_ifaces(sc, &sc->sc_iface)) 269 goto failed; 270 271 if (uchcom_find_endpoints(sc, &endpoints)) 272 goto failed; 273 274 sc->sc_intr_endpoint = endpoints.ep_intr; 275 sc->sc_isize = endpoints.ep_intr_size; 276 277 /* setup ucom layer */ 278 uca.portno = UCOM_UNK_PORTNO; 279 uca.bulkin = endpoints.ep_bulkin; 280 uca.bulkout = endpoints.ep_bulkout; 281 uca.ibufsize = UCHCOMIBUFSIZE; 282 uca.obufsize = UCHCOMOBUFSIZE; 283 uca.ibufsizepad = UCHCOMIBUFSIZE; 284 uca.opkthdrlen = 0; 285 uca.device = dev; 286 uca.iface = sc->sc_iface; 287 uca.methods = &uchcom_methods; 288 uca.arg = sc; 289 uca.info = NULL; 290 291 sc->sc_subdev = config_found_sm(self, &uca, ucomprint, ucomsubmatch); 292 293 return; 294 295 failed: 296 usbd_deactivate(sc->sc_udev); 297 } 298 299 int 300 uchcom_detach(struct device *self, int flags) 301 { 302 struct uchcom_softc *sc = (struct uchcom_softc *)self; 303 int rv = 0; 304 305 DPRINTF(("uchcom_detach: sc=%p flags=%d\n", sc, flags)); 306 307 uchcom_close_intr_pipe(sc); 308 309 if (sc->sc_subdev != NULL) { 310 rv = config_detach(sc->sc_subdev, flags); 311 sc->sc_subdev = NULL; 312 } 313 314 return rv; 315 } 316 317 int 318 uchcom_find_ifaces(struct uchcom_softc *sc, struct usbd_interface **riface) 319 { 320 usbd_status err; 321 322 err = usbd_device2interface_handle(sc->sc_udev, UCHCOM_IFACE_INDEX, 323 riface); 324 if (err) { 325 printf("\n%s: failed to get interface: %s\n", 326 sc->sc_dev.dv_xname, usbd_errstr(err)); 327 return -1; 328 } 329 330 return 0; 331 } 332 333 int 334 uchcom_find_endpoints(struct uchcom_softc *sc, 335 struct uchcom_endpoints *endpoints) 336 { 337 int i, bin=-1, bout=-1, intr=-1, isize=0; 338 usb_interface_descriptor_t *id; 339 usb_endpoint_descriptor_t *ed; 340 341 id = usbd_get_interface_descriptor(sc->sc_iface); 342 343 for (i = 0; i < id->bNumEndpoints; i++) { 344 ed = usbd_interface2endpoint_descriptor(sc->sc_iface, i); 345 if (ed == NULL) { 346 printf("%s: no endpoint descriptor for %d\n", 347 sc->sc_dev.dv_xname, i); 348 return -1; 349 } 350 351 if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN && 352 UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT) { 353 intr = ed->bEndpointAddress; 354 isize = UGETW(ed->wMaxPacketSize); 355 } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN && 356 UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) { 357 bin = ed->bEndpointAddress; 358 } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT && 359 UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) { 360 bout = ed->bEndpointAddress; 361 } 362 } 363 364 if (intr == -1 || bin == -1 || bout == -1) { 365 if (intr == -1) { 366 printf("%s: no interrupt end point\n", 367 sc->sc_dev.dv_xname); 368 } 369 if (bin == -1) { 370 printf("%s: no data bulk in end point\n", 371 sc->sc_dev.dv_xname); 372 } 373 if (bout == -1) { 374 printf("%s: no data bulk out end point\n", 375 sc->sc_dev.dv_xname); 376 } 377 return -1; 378 } 379 if (isize < UCHCOM_INTR_LEAST) { 380 printf("%s: intr pipe is too short", sc->sc_dev.dv_xname); 381 return -1; 382 } 383 384 DPRINTF(("%s: bulkin=%d, bulkout=%d, intr=%d, isize=%d\n", 385 sc->sc_dev.dv_xname, bin, bout, intr, isize)); 386 387 endpoints->ep_intr = intr; 388 endpoints->ep_intr_size = isize; 389 endpoints->ep_bulkin = bin; 390 endpoints->ep_bulkout = bout; 391 392 return 0; 393 } 394 395 396 /* ---------------------------------------------------------------------- 397 * low level i/o 398 */ 399 400 usbd_status 401 uchcom_generic_control_out(struct uchcom_softc *sc, uint8_t reqno, 402 uint16_t value, uint16_t index) 403 { 404 usb_device_request_t req; 405 406 req.bmRequestType = UT_WRITE_VENDOR_DEVICE; 407 req.bRequest = reqno; 408 USETW(req.wValue, value); 409 USETW(req.wIndex, index); 410 USETW(req.wLength, 0); 411 412 return usbd_do_request(sc->sc_udev, &req, 0); 413 } 414 415 usbd_status 416 uchcom_generic_control_in(struct uchcom_softc *sc, uint8_t reqno, 417 uint16_t value, uint16_t index, void *buf, int buflen, int *actlen) 418 { 419 usb_device_request_t req; 420 421 req.bmRequestType = UT_READ_VENDOR_DEVICE; 422 req.bRequest = reqno; 423 USETW(req.wValue, value); 424 USETW(req.wIndex, index); 425 USETW(req.wLength, (uint16_t)buflen); 426 427 return usbd_do_request_flags(sc->sc_udev, &req, buf, 428 USBD_SHORT_XFER_OK, actlen, 429 USBD_DEFAULT_TIMEOUT); 430 } 431 432 usbd_status 433 uchcom_write_reg(struct uchcom_softc *sc, 434 uint8_t reg1, uint8_t val1, uint8_t reg2, uint8_t val2) 435 { 436 DPRINTF(("uchcom: write reg 0x%02X<-0x%02X, 0x%02X<-0x%02X\n", 437 (unsigned)reg1, (unsigned)val1, 438 (unsigned)reg2, (unsigned)val2)); 439 return uchcom_generic_control_out( 440 sc, UCHCOM_REQ_WRITE_REG, 441 reg1|((uint16_t)reg2<<8), val1|((uint16_t)val2<<8)); 442 } 443 444 usbd_status 445 uchcom_read_reg(struct uchcom_softc *sc, 446 uint8_t reg1, uint8_t *rval1, uint8_t reg2, uint8_t *rval2) 447 { 448 uint8_t buf[UCHCOM_INPUT_BUF_SIZE]; 449 usbd_status err; 450 int actin; 451 452 err = uchcom_generic_control_in( 453 sc, UCHCOM_REQ_READ_REG, 454 reg1|((uint16_t)reg2<<8), 0, buf, sizeof buf, &actin); 455 if (err) 456 return err; 457 458 DPRINTF(("uchcom: read reg 0x%02X->0x%02X, 0x%02X->0x%02X\n", 459 (unsigned)reg1, (unsigned)buf[0], 460 (unsigned)reg2, (unsigned)buf[1])); 461 462 if (rval1) *rval1 = buf[0]; 463 if (rval2) *rval2 = buf[1]; 464 465 return USBD_NORMAL_COMPLETION; 466 } 467 468 usbd_status 469 uchcom_get_version(struct uchcom_softc *sc, uint8_t *rver) 470 { 471 uint8_t buf[UCHCOM_INPUT_BUF_SIZE]; 472 usbd_status err; 473 int actin; 474 475 err = uchcom_generic_control_in( 476 sc, UCHCOM_REQ_GET_VERSION, 0, 0, buf, sizeof buf, &actin); 477 if (err) 478 return err; 479 480 if (rver) *rver = buf[0]; 481 482 return USBD_NORMAL_COMPLETION; 483 } 484 485 usbd_status 486 uchcom_read_status(struct uchcom_softc *sc, uint8_t *rval) 487 { 488 return uchcom_read_reg(sc, UCHCOM_REG_STAT1, rval, UCHCOM_REG_STAT2, 489 NULL); 490 } 491 492 usbd_status 493 uchcom_set_dtrrts_10(struct uchcom_softc *sc, uint8_t val) 494 { 495 return uchcom_write_reg(sc, UCHCOM_REG_STAT1, val, UCHCOM_REG_STAT1, 496 val); 497 } 498 499 usbd_status 500 uchcom_set_dtrrts_20(struct uchcom_softc *sc, uint8_t val) 501 { 502 return uchcom_generic_control_out(sc, UCHCOM_REQ_SET_DTRRTS, val, 0); 503 } 504 505 506 /* ---------------------------------------------------------------------- 507 * middle layer 508 */ 509 510 int 511 uchcom_update_version(struct uchcom_softc *sc) 512 { 513 usbd_status err; 514 515 err = uchcom_get_version(sc, &sc->sc_version); 516 if (err) { 517 printf("%s: cannot get version: %s\n", 518 sc->sc_dev.dv_xname, usbd_errstr(err)); 519 return EIO; 520 } 521 522 return 0; 523 } 524 525 void 526 uchcom_convert_status(struct uchcom_softc *sc, uint8_t cur) 527 { 528 sc->sc_dtr = !(cur & UCHCOM_DTR_MASK); 529 sc->sc_rts = !(cur & UCHCOM_RTS_MASK); 530 531 cur = ~cur & 0x0F; 532 sc->sc_msr = (cur << 4) | ((sc->sc_msr >> 4) ^ cur); 533 } 534 535 int 536 uchcom_update_status(struct uchcom_softc *sc) 537 { 538 usbd_status err; 539 uint8_t cur; 540 541 err = uchcom_read_status(sc, &cur); 542 if (err) { 543 printf("%s: cannot update status: %s\n", 544 sc->sc_dev.dv_xname, usbd_errstr(err)); 545 return EIO; 546 } 547 uchcom_convert_status(sc, cur); 548 549 return 0; 550 } 551 552 553 int 554 uchcom_set_dtrrts(struct uchcom_softc *sc, int dtr, int rts) 555 { 556 usbd_status err; 557 uint8_t val = 0; 558 559 if (dtr) val |= UCHCOM_DTR_MASK; 560 if (rts) val |= UCHCOM_RTS_MASK; 561 562 if (sc->sc_version < UCHCOM_VER_20) 563 err = uchcom_set_dtrrts_10(sc, ~val); 564 else 565 err = uchcom_set_dtrrts_20(sc, ~val); 566 567 if (err) { 568 printf("%s: cannot set DTR/RTS: %s\n", 569 sc->sc_dev.dv_xname, usbd_errstr(err)); 570 return EIO; 571 } 572 573 return 0; 574 } 575 576 int 577 uchcom_set_break(struct uchcom_softc *sc, int onoff) 578 { 579 usbd_status err; 580 uint8_t brk1, brk2; 581 582 err = uchcom_read_reg(sc, UCHCOM_REG_BREAK1, &brk1, UCHCOM_REG_BREAK2, 583 &brk2); 584 if (err) 585 return EIO; 586 if (onoff) { 587 /* on - clear bits */ 588 brk1 &= ~UCHCOM_BRK1_MASK; 589 brk2 &= ~UCHCOM_BRK2_MASK; 590 } else { 591 /* off - set bits */ 592 brk1 |= UCHCOM_BRK1_MASK; 593 brk2 |= UCHCOM_BRK2_MASK; 594 } 595 err = uchcom_write_reg(sc, UCHCOM_REG_BREAK1, brk1, UCHCOM_REG_BREAK2, 596 brk2); 597 if (err) 598 return EIO; 599 600 return 0; 601 } 602 603 int 604 uchcom_calc_divider_settings(struct uchcom_divider *dp, uint32_t rate) 605 { 606 int i; 607 const struct uchcom_divider_record *rp; 608 uint32_t div, rem, mod; 609 610 /* find record */ 611 for (i=0; i<nitems(dividers); i++) { 612 if (dividers[i].dvr_high >= rate && 613 dividers[i].dvr_low <= rate) { 614 rp = ÷rs[i]; 615 goto found; 616 } 617 } 618 return -1; 619 620 found: 621 dp->dv_prescaler = rp->dvr_divider.dv_prescaler; 622 if (rp->dvr_base_clock == UCHCOM_BASE_UNKNOWN) 623 dp->dv_div = rp->dvr_divider.dv_div; 624 else { 625 div = rp->dvr_base_clock / rate; 626 rem = rp->dvr_base_clock % rate; 627 if (div==0 || div>=0xFF) 628 return -1; 629 if ((rem<<1) >= rate) 630 div += 1; 631 dp->dv_div = (uint8_t)-div; 632 } 633 634 mod = UCHCOM_BPS_MOD_BASE/rate + UCHCOM_BPS_MOD_BASE_OFS; 635 mod = mod + mod/2; 636 637 dp->dv_mod = mod / 0x100; 638 639 return 0; 640 } 641 642 int 643 uchcom_set_dte_rate(struct uchcom_softc *sc, uint32_t rate) 644 { 645 usbd_status err; 646 struct uchcom_divider dv; 647 648 if (uchcom_calc_divider_settings(&dv, rate)) 649 return EINVAL; 650 651 if ((err = uchcom_write_reg(sc, 652 UCHCOM_REG_BPS_PRE, dv.dv_prescaler, 653 UCHCOM_REG_BPS_DIV, dv.dv_div)) || 654 (err = uchcom_write_reg(sc, 655 UCHCOM_REG_BPS_MOD, dv.dv_mod, 656 UCHCOM_REG_BPS_PAD, 0))) { 657 printf("%s: cannot set DTE rate: %s\n", 658 sc->sc_dev.dv_xname, usbd_errstr(err)); 659 return EIO; 660 } 661 662 return 0; 663 } 664 665 int 666 uchcom_set_line_control(struct uchcom_softc *sc, tcflag_t cflag) 667 { 668 /* 669 * XXX: it is difficult to handle the line control appropriately: 670 * work as chip default - CS8, no parity, !CSTOPB 671 * other modes are not supported. 672 */ 673 674 switch (ISSET(cflag, CSIZE)) { 675 case CS5: 676 case CS6: 677 case CS7: 678 return EINVAL; 679 case CS8: 680 break; 681 } 682 683 if (ISSET(cflag, PARENB) || ISSET(cflag, CSTOPB)) 684 return EINVAL; 685 686 return 0; 687 } 688 689 int 690 uchcom_clear_chip(struct uchcom_softc *sc) 691 { 692 usbd_status err; 693 694 DPRINTF(("%s: clear\n", sc->sc_dev.dv_xname)); 695 err = uchcom_generic_control_out(sc, UCHCOM_REQ_RESET, 0, 0); 696 if (err) { 697 printf("%s: cannot clear: %s\n", 698 sc->sc_dev.dv_xname, usbd_errstr(err)); 699 return EIO; 700 } 701 702 return 0; 703 } 704 705 int 706 uchcom_reset_chip(struct uchcom_softc *sc) 707 { 708 usbd_status err; 709 710 DPRINTF(("%s: reset\n", sc->sc_dev.dv_xname)); 711 712 err = uchcom_generic_control_out(sc, UCHCOM_REQ_RESET, 713 UCHCOM_RESET_VALUE, 714 UCHCOM_RESET_INDEX); 715 if (err) 716 goto failed; 717 718 return 0; 719 720 failed: 721 printf("%s: cannot reset: %s\n", 722 sc->sc_dev.dv_xname, usbd_errstr(err)); 723 return EIO; 724 } 725 726 int 727 uchcom_setup_comm(struct uchcom_softc *sc) 728 { 729 int ret; 730 731 ret = uchcom_update_version(sc); 732 if (ret) 733 return ret; 734 735 ret = uchcom_clear_chip(sc); 736 if (ret) 737 return ret; 738 739 ret = uchcom_set_dte_rate(sc, TTYDEF_SPEED); 740 if (ret) 741 return ret; 742 743 ret = uchcom_set_line_control(sc, CS8); 744 if (ret) 745 return ret; 746 747 ret = uchcom_update_status(sc); 748 if (ret) 749 return ret; 750 751 ret = uchcom_reset_chip(sc); 752 if (ret) 753 return ret; 754 755 ret = uchcom_set_dte_rate(sc, TTYDEF_SPEED); /* XXX */ 756 if (ret) 757 return ret; 758 759 sc->sc_dtr = sc->sc_rts = 1; 760 ret = uchcom_set_dtrrts(sc, sc->sc_dtr, sc->sc_rts); 761 if (ret) 762 return ret; 763 764 return 0; 765 } 766 767 int 768 uchcom_setup_intr_pipe(struct uchcom_softc *sc) 769 { 770 usbd_status err; 771 772 if (sc->sc_intr_endpoint != -1 && sc->sc_intr_pipe == NULL) { 773 sc->sc_intr_buf = malloc(sc->sc_isize, M_USBDEV, M_WAITOK); 774 err = usbd_open_pipe_intr(sc->sc_iface, 775 sc->sc_intr_endpoint, 776 USBD_SHORT_XFER_OK, 777 &sc->sc_intr_pipe, sc, 778 sc->sc_intr_buf, 779 sc->sc_isize, 780 uchcom_intr, USBD_DEFAULT_INTERVAL); 781 if (err) { 782 printf("%s: cannot open interrupt pipe: %s\n", 783 sc->sc_dev.dv_xname, 784 usbd_errstr(err)); 785 return EIO; 786 } 787 } 788 return 0; 789 } 790 791 void 792 uchcom_close_intr_pipe(struct uchcom_softc *sc) 793 { 794 usbd_status err; 795 796 if (sc->sc_intr_pipe != NULL) { 797 err = usbd_close_pipe(sc->sc_intr_pipe); 798 if (err) 799 printf("%s: close interrupt pipe failed: %s\n", 800 sc->sc_dev.dv_xname, usbd_errstr(err)); 801 free(sc->sc_intr_buf, M_USBDEV, sc->sc_isize); 802 sc->sc_intr_pipe = NULL; 803 } 804 } 805 806 807 /* ---------------------------------------------------------------------- 808 * methods for ucom 809 */ 810 void 811 uchcom_get_status(void *arg, int portno, u_char *rlsr, u_char *rmsr) 812 { 813 struct uchcom_softc *sc = arg; 814 815 if (usbd_is_dying(sc->sc_udev)) 816 return; 817 818 *rlsr = sc->sc_lsr; 819 *rmsr = sc->sc_msr; 820 } 821 822 void 823 uchcom_set(void *arg, int portno, int reg, int onoff) 824 { 825 struct uchcom_softc *sc = arg; 826 827 if (usbd_is_dying(sc->sc_udev)) 828 return; 829 830 switch (reg) { 831 case UCOM_SET_DTR: 832 sc->sc_dtr = !!onoff; 833 uchcom_set_dtrrts(sc, sc->sc_dtr, sc->sc_rts); 834 break; 835 case UCOM_SET_RTS: 836 sc->sc_rts = !!onoff; 837 uchcom_set_dtrrts(sc, sc->sc_dtr, sc->sc_rts); 838 break; 839 case UCOM_SET_BREAK: 840 uchcom_set_break(sc, onoff); 841 break; 842 } 843 } 844 845 int 846 uchcom_param(void *arg, int portno, struct termios *t) 847 { 848 struct uchcom_softc *sc = arg; 849 int ret; 850 851 if (usbd_is_dying(sc->sc_udev)) 852 return 0; 853 854 ret = uchcom_set_line_control(sc, t->c_cflag); 855 if (ret) 856 return ret; 857 858 ret = uchcom_set_dte_rate(sc, t->c_ospeed); 859 if (ret) 860 return ret; 861 862 return 0; 863 } 864 865 int 866 uchcom_open(void *arg, int portno) 867 { 868 int ret; 869 struct uchcom_softc *sc = arg; 870 871 if (usbd_is_dying(sc->sc_udev)) 872 return EIO; 873 874 ret = uchcom_setup_intr_pipe(sc); 875 if (ret) 876 return ret; 877 878 ret = uchcom_setup_comm(sc); 879 if (ret) 880 return ret; 881 882 return 0; 883 } 884 885 void 886 uchcom_close(void *arg, int portno) 887 { 888 struct uchcom_softc *sc = arg; 889 890 uchcom_close_intr_pipe(sc); 891 } 892 893 894 /* ---------------------------------------------------------------------- 895 * callback when the modem status is changed. 896 */ 897 void 898 uchcom_intr(struct usbd_xfer *xfer, void *priv, usbd_status status) 899 { 900 struct uchcom_softc *sc = priv; 901 u_char *buf = sc->sc_intr_buf; 902 903 if (usbd_is_dying(sc->sc_udev)) 904 return; 905 906 if (status != USBD_NORMAL_COMPLETION) { 907 if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) 908 return; 909 910 DPRINTF(("%s: abnormal status: %s\n", 911 sc->sc_dev.dv_xname, usbd_errstr(status))); 912 usbd_clear_endpoint_stall_async(sc->sc_intr_pipe); 913 return; 914 } 915 DPRINTF(("%s: intr: 0x%02X 0x%02X 0x%02X 0x%02X " 916 "0x%02X 0x%02X 0x%02X 0x%02X\n", 917 sc->sc_dev.dv_xname, 918 (unsigned)buf[0], (unsigned)buf[1], 919 (unsigned)buf[2], (unsigned)buf[3], 920 (unsigned)buf[4], (unsigned)buf[5], 921 (unsigned)buf[6], (unsigned)buf[7])); 922 923 uchcom_convert_status(sc, buf[UCHCOM_INTR_STAT1]); 924 ucom_status_change((struct ucom_softc *) sc->sc_subdev); 925 } 926