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