1/* $NetBSD: uplcom.c,v 1.94 2022/07/06 15:24:14 hannken Exp $ */ 2 3/* 4 * Copyright (c) 2001 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Ichiro FUKUHARA (ichiro@ichiro.org). 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32/* 33 * General information: http://www.prolific.com.tw/fr_pl2303.htm 34 * http://www.hitachi-hitec.com/jyouhou/prolific/2303.pdf 35 */ 36 37#include <sys/cdefs.h> 38__KERNEL_RCSID(0, "$NetBSD: uplcom.c,v 1.94 2022/07/06 15:24:14 hannken Exp $"); 39 40#ifdef _KERNEL_OPT 41#include "opt_usb.h" 42#endif 43 44#include <sys/param.h> 45#include <sys/systm.h> 46#include <sys/kernel.h> 47#include <sys/kmem.h> 48#include <sys/ioctl.h> 49#include <sys/conf.h> 50#include <sys/tty.h> 51#include <sys/file.h> 52#include <sys/select.h> 53#include <sys/proc.h> 54#include <sys/device.h> 55#include <sys/poll.h> 56#include <sys/sysctl.h> 57 58#include <dev/usb/usb.h> 59#include <dev/usb/usbcdc.h> 60 61#include <dev/usb/usbdi.h> 62#include <dev/usb/usbdi_util.h> 63#include <dev/usb/usbdevs.h> 64#include <dev/usb/usb_quirks.h> 65#include <dev/usb/usbhist.h> 66 67#include <dev/usb/ucomvar.h> 68 69#ifdef USB_DEBUG 70#ifndef UPLCOM_DEBUG 71#define uplcomdebug 0 72#else 73int uplcomdebug = 0; 74 75SYSCTL_SETUP(sysctl_hw_uplcom_setup, "sysctl hw.uplcom setup") 76{ 77 int err; 78 const struct sysctlnode *rnode; 79 const struct sysctlnode *cnode; 80 81 err = sysctl_createv(clog, 0, NULL, &rnode, 82 CTLFLAG_PERMANENT, CTLTYPE_NODE, "uplcom", 83 SYSCTL_DESCR("uplcom global controls"), 84 NULL, 0, NULL, 0, CTL_HW, CTL_CREATE, CTL_EOL); 85 86 if (err) 87 goto fail; 88 89 /* control debugging printfs */ 90 err = sysctl_createv(clog, 0, &rnode, &cnode, 91 CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_INT, 92 "debug", SYSCTL_DESCR("Enable debugging output"), 93 NULL, 0, &uplcomdebug, sizeof(uplcomdebug), CTL_CREATE, CTL_EOL); 94 if (err) 95 goto fail; 96 97 return; 98fail: 99 aprint_error("%s: sysctl_createv failed (err = %d)\n", __func__, err); 100} 101 102#endif /* UCOM_DEBUG */ 103#endif /* USB_DEBUG */ 104 105 106#define DPRINTF(FMT,A,B,C,D) USBHIST_LOGN(uplcomdebug,1,FMT,A,B,C,D) 107#define DPRINTFN(N,FMT,A,B,C,D) USBHIST_LOGN(uplcomdebug,N,FMT,A,B,C,D) 108#define UPLCOMHIST_FUNC() USBHIST_FUNC() 109#define UPLCOMHIST_CALLED(name) USBHIST_CALLED(uplcomdebug) 110 111#define UPLCOM_CONFIG_INDEX 0 112#define UPLCOM_IFACE_INDEX 0 113#define UPLCOM_SECOND_IFACE_INDEX 1 114 115#define UPLCOM_SET_REQUEST 0x01 116#define UPLCOM_SET_CRTSCTS_0 0x41 117#define UPLCOM_SET_CRTSCTS_HX 0x61 118#define UPLCOM_HX_STATUS_REG 0x8080 119 120#define UPLCOM_N_SERIAL_CTS 0x80 121 122#define UPLCOM_HXN_SET_REQUEST 0x80 123#define UPLCOM_HXN_SET_CRTSCTS_REG 0x0A 124#define UPLCOM_HXN_SET_CRTSCTS 0xFA 125 126enum pl2303_type { 127 UPLCOM_TYPE_0, /* we use this for all non-HX variants */ 128 UPLCOM_TYPE_HX, 129 UPLCOM_TYPE_HXN, 130}; 131 132struct uplcom_softc { 133 device_t sc_dev; /* base device */ 134 struct usbd_device * sc_udev; /* USB device */ 135 struct usbd_interface * sc_iface; /* interface */ 136 int sc_iface_number; /* interface number */ 137 138 struct usbd_interface * sc_intr_iface; /* interrupt interface */ 139 int sc_intr_number; /* interrupt number */ 140 struct usbd_pipe * sc_intr_pipe; /* interrupt pipe */ 141 u_char *sc_intr_buf; /* interrupt buffer */ 142 int sc_isize; 143 144 usb_cdc_line_state_t sc_line_state; /* current line state */ 145 int sc_dtr; /* current DTR state */ 146 int sc_rts; /* current RTS state */ 147 148 device_t sc_subdev; /* ucom device */ 149 150 bool sc_dying; /* disconnecting */ 151 152 u_char sc_lsr; /* Local status register */ 153 u_char sc_msr; /* uplcom status register */ 154 155 enum pl2303_type sc_type; /* PL2303 chip type */ 156}; 157 158/* 159 * These are the maximum number of bytes transferred per frame. 160 * The output buffer size cannot be increased due to the size encoding. 161 */ 162#define UPLCOMIBUFSIZE 256 163#define UPLCOMOBUFSIZE 256 164 165static usbd_status uplcom_reset(struct uplcom_softc *); 166static usbd_status uplcom_set_line_coding(struct uplcom_softc *, 167 usb_cdc_line_state_t *); 168static usbd_status uplcom_set_crtscts(struct uplcom_softc *); 169static void uplcom_intr(struct usbd_xfer *, void *, usbd_status); 170 171static void uplcom_set(void *, int, int, int); 172static void uplcom_dtr(struct uplcom_softc *, int); 173static void uplcom_rts(struct uplcom_softc *, int); 174static void uplcom_break(struct uplcom_softc *, int); 175static void uplcom_set_line_state(struct uplcom_softc *); 176static void uplcom_get_status(void *, int, u_char *, u_char *); 177#if TODO 178static int uplcom_ioctl(void *, int, u_long, void *, int, proc_t *); 179#endif 180static int uplcom_param(void *, int, struct termios *); 181static int uplcom_open(void *, int); 182static void uplcom_close(void *, int); 183static usbd_status uplcom_vendor_control_write(struct usbd_device *, uint16_t, uint16_t); 184static void uplcom_close_pipe(struct uplcom_softc *); 185 186static const struct ucom_methods uplcom_methods = { 187 .ucom_get_status = uplcom_get_status, 188 .ucom_set = uplcom_set, 189 .ucom_param = uplcom_param, 190 .ucom_ioctl = NULL, /* TODO */ 191 .ucom_open = uplcom_open, 192 .ucom_close = uplcom_close, 193}; 194 195static const struct usb_devno uplcom_devs[] = { 196 /* I/O DATA USB-RSAQ2 */ 197 { USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_RSAQ2 }, 198 /* I/O DATA USB-RSAQ3 */ 199 { USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_RSAQ3 }, 200 /* I/O DATA USB-RSAQ */ 201 { USB_VENDOR_IODATA, USB_PRODUCT_IODATA_USBRSAQ }, 202 /* I/O DATA USB-RSAQ5 */ 203 { USB_VENDOR_IODATA, USB_PRODUCT_IODATA_USBRSAQ5 }, 204 /* PLANEX USB-RS232 URS-03 */ 205 { USB_VENDOR_ATEN, USB_PRODUCT_ATEN_UC232A }, 206 /* various */ 207 { USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PL2303 }, 208 /* SMART Technologies USB to serial */ 209 { USB_VENDOR_PROLIFIC2, USB_PRODUCT_PROLIFIC2_PL2303 }, 210 /* IOGEAR/ATENTRIPPLITE */ 211 { USB_VENDOR_TRIPPLITE, USB_PRODUCT_TRIPPLITE_U209 }, 212 /* ELECOM UC-SGT */ 213 { USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_UCSGT }, 214 /* ELECOM UC-SGT0 */ 215 { USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_UCSGT0 }, 216 /* Panasonic 50" Touch Panel */ 217 { USB_VENDOR_PANASONIC, USB_PRODUCT_PANASONIC_TYTP50P6S }, 218 /* RATOC REX-USB60 */ 219 { USB_VENDOR_RATOC, USB_PRODUCT_RATOC_REXUSB60 }, 220 /* TDK USB-PHS Adapter UHA6400 */ 221 { USB_VENDOR_TDK, USB_PRODUCT_TDK_UHA6400 }, 222 /* TDK USB-PDC Adapter UPA9664 */ 223 { USB_VENDOR_TDK, USB_PRODUCT_TDK_UPA9664 }, 224 /* Sony Ericsson USB Cable */ 225 { USB_VENDOR_SUSTEEN, USB_PRODUCT_SUSTEEN_DCU10 }, 226 /* SOURCENEXT KeikaiDenwa 8 */ 227 { USB_VENDOR_SOURCENEXT, USB_PRODUCT_SOURCENEXT_KEIKAI8 }, 228 /* SOURCENEXT KeikaiDenwa 8 with charger */ 229 { USB_VENDOR_SOURCENEXT, USB_PRODUCT_SOURCENEXT_KEIKAI8_CHG }, 230 /* HAL Corporation Crossam2+USB */ 231 { USB_VENDOR_HAL, USB_PRODUCT_HAL_IMR001 }, 232 /* Sitecom USB to serial cable */ 233 { USB_VENDOR_SITECOM, USB_PRODUCT_SITECOM_CN104 }, 234 /* Pharos USB GPS - Microsoft version */ 235 { USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PL2303X }, 236 /* Willcom WS002IN (DD) */ 237 { USB_VENDOR_NETINDEX, USB_PRODUCT_NETINDEX_WS002IN }, 238 /* COREGA CG-USBRS232R */ 239 { USB_VENDOR_COREGA, USB_PRODUCT_COREGA_CGUSBRS232R }, 240 /* Sharp CE-175TU (USB to Zaurus option port 15 adapter) */ 241 { USB_VENDOR_SHARP, USB_PRODUCT_SHARP_CE175TU }, 242 /* Various */ 243 { USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PL2303GB }, 244 { USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PL2303GC }, 245 { USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PL2303GE }, 246 { USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PL2303GL }, 247 { USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PL2303GS }, 248 { USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PL2303GT }, 249}; 250#define uplcom_lookup(v, p) usb_lookup(uplcom_devs, v, p) 251 252static int uplcom_match(device_t, cfdata_t, void *); 253static void uplcom_attach(device_t, device_t, void *); 254static void uplcom_childdet(device_t, device_t); 255static int uplcom_detach(device_t, int); 256 257CFATTACH_DECL2_NEW(uplcom, sizeof(struct uplcom_softc), uplcom_match, 258 uplcom_attach, uplcom_detach, NULL, NULL, uplcom_childdet); 259 260static int 261uplcom_match(device_t parent, cfdata_t match, void *aux) 262{ 263 struct usb_attach_arg *uaa = aux; 264 265 return uplcom_lookup(uaa->uaa_vendor, uaa->uaa_product) != NULL ? 266 UMATCH_VENDOR_PRODUCT : UMATCH_NONE; 267} 268 269static void 270uplcom_attach(device_t parent, device_t self, void *aux) 271{ 272 struct uplcom_softc *sc = device_private(self); 273 struct usb_attach_arg *uaa = aux; 274 struct usbd_device *dev = uaa->uaa_device; 275 usb_device_descriptor_t *ddesc; 276 usb_config_descriptor_t *cdesc; 277 usb_interface_descriptor_t *id; 278 usb_endpoint_descriptor_t *ed; 279 usb_device_request_t req; 280 char *devinfop; 281 const char *devname = device_xname(self); 282 usbd_status err; 283 uint8_t val; 284 int i; 285 struct ucom_attach_args ucaa; 286 287 UPLCOMHIST_FUNC(); UPLCOMHIST_CALLED(); 288 DPRINTF("sc=%#jx", (uintptr_t)sc, 0, 0, 0); 289 290 sc->sc_dev = self; 291 sc->sc_dying = false; 292 293 aprint_naive("\n"); 294 aprint_normal("\n"); 295 296 devinfop = usbd_devinfo_alloc(dev, 0); 297 aprint_normal_dev(self, "%s\n", devinfop); 298 usbd_devinfo_free(devinfop); 299 300 sc->sc_udev = dev; 301 302 /* initialize endpoints */ 303 ucaa.ucaa_bulkin = ucaa.ucaa_bulkout = -1; 304 sc->sc_intr_number = -1; 305 sc->sc_intr_pipe = NULL; 306 307 /* Move the device into the configured state. */ 308 err = usbd_set_config_index(dev, UPLCOM_CONFIG_INDEX, 1); 309 if (err) { 310 aprint_error("\n%s: failed to set configuration, err=%s\n", 311 devname, usbd_errstr(err)); 312 sc->sc_dying = true; 313 return; 314 } 315 316 /* determine chip type */ 317 ddesc = usbd_get_device_descriptor(dev); 318 if (ddesc->bDeviceClass != UDCLASS_COMM && 319 ddesc->bMaxPacketSize == 0x40) 320 sc->sc_type = UPLCOM_TYPE_HX; 321 322 if (sc->sc_type == UPLCOM_TYPE_HX) { 323 req.bmRequestType = UT_READ_VENDOR_DEVICE; 324 req.bRequest = UPLCOM_SET_REQUEST; 325 USETW(req.wValue, UPLCOM_HX_STATUS_REG); 326 USETW(req.wIndex, sc->sc_iface_number); 327 USETW(req.wLength, 1); 328 329 err = usbd_do_request(sc->sc_udev, &req, &val); 330 if (err) 331 sc->sc_type = UPLCOM_TYPE_HXN; 332 } 333 334#ifdef UPLCOM_DEBUG 335 /* print the chip type */ 336 if (sc->sc_type == UPLCOM_TYPE_HXN) { 337 DPRINTF("chiptype HXN", 0, 0, 0, 0); 338 } else if (sc->sc_type == UPLCOM_TYPE_HX) { 339 DPRINTF("chiptype HX", 0, 0, 0, 0); 340 } else { 341 DPRINTF("chiptype 0", 0, 0, 0, 0); 342 } 343#endif 344 345 /* Move the device into the configured state. */ 346 err = usbd_set_config_index(dev, UPLCOM_CONFIG_INDEX, 1); 347 if (err) { 348 aprint_error_dev(self, "failed to set configuration: %s\n", 349 usbd_errstr(err)); 350 sc->sc_dying = true; 351 return; 352 } 353 354 /* get the config descriptor */ 355 cdesc = usbd_get_config_descriptor(sc->sc_udev); 356 357 if (cdesc == NULL) { 358 aprint_error_dev(self, 359 "failed to get configuration descriptor\n"); 360 sc->sc_dying = true; 361 return; 362 } 363 364 /* get the (first/common) interface */ 365 err = usbd_device2interface_handle(dev, UPLCOM_IFACE_INDEX, 366 &sc->sc_iface); 367 if (err) { 368 aprint_error("\n%s: failed to get interface, err=%s\n", 369 devname, usbd_errstr(err)); 370 sc->sc_dying = true; 371 return; 372 } 373 374 /* Find the interrupt endpoints */ 375 376 id = usbd_get_interface_descriptor(sc->sc_iface); 377 sc->sc_iface_number = id->bInterfaceNumber; 378 379 for (i = 0; i < id->bNumEndpoints; i++) { 380 ed = usbd_interface2endpoint_descriptor(sc->sc_iface, i); 381 if (ed == NULL) { 382 aprint_error_dev(self, 383 "no endpoint descriptor for %d\n", i); 384 sc->sc_dying = true; 385 return; 386 } 387 388 if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN && 389 UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT) { 390 sc->sc_intr_number = ed->bEndpointAddress; 391 sc->sc_isize = UGETW(ed->wMaxPacketSize); 392 } 393 } 394 395 if (sc->sc_intr_number== -1) { 396 aprint_error_dev(self, "Could not find interrupt in\n"); 397 sc->sc_dying = true; 398 return; 399 } 400 401 /* keep interface for interrupt */ 402 sc->sc_intr_iface = sc->sc_iface; 403 404 /* 405 * USB-RSAQ1 has two interface 406 * 407 * USB-RSAQ1 | USB-RSAQ2 408 * -----------------+----------------- 409 * Interface 0 |Interface 0 410 * Interrupt(0x81) | Interrupt(0x81) 411 * -----------------+ BulkIN(0x02) 412 * Interface 1 | BulkOUT(0x83) 413 * BulkIN(0x02) | 414 * BulkOUT(0x83) | 415 */ 416 if (cdesc->bNumInterface == 2) { 417 err = usbd_device2interface_handle(dev, 418 UPLCOM_SECOND_IFACE_INDEX, &sc->sc_iface); 419 if (err) { 420 aprint_error("\n%s: failed to get second interface, " 421 "err=%s\n", devname, usbd_errstr(err)); 422 sc->sc_dying = true; 423 return; 424 } 425 } 426 427 /* Find the bulk{in,out} endpoints */ 428 429 id = usbd_get_interface_descriptor(sc->sc_iface); 430 sc->sc_iface_number = id->bInterfaceNumber; 431 432 for (i = 0; i < id->bNumEndpoints; i++) { 433 ed = usbd_interface2endpoint_descriptor(sc->sc_iface, i); 434 if (ed == NULL) { 435 aprint_error_dev(self, 436 "no endpoint descriptor for %d\n", i); 437 sc->sc_dying = true; 438 return; 439 } 440 441 if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN && 442 UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) { 443 ucaa.ucaa_bulkin = ed->bEndpointAddress; 444 } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT && 445 UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) { 446 ucaa.ucaa_bulkout = ed->bEndpointAddress; 447 } 448 } 449 450 if (ucaa.ucaa_bulkin == -1) { 451 aprint_error_dev(self, "Could not find data bulk in\n"); 452 sc->sc_dying = true; 453 return; 454 } 455 456 if (ucaa.ucaa_bulkout == -1) { 457 aprint_error_dev(self, "Could not find data bulk out\n"); 458 sc->sc_dying = true; 459 return; 460 } 461 462 sc->sc_dtr = sc->sc_rts = -1; 463 ucaa.ucaa_portno = UCOM_UNK_PORTNO; 464 /* ucaa_bulkin, ucaa_bulkout set above */ 465 ucaa.ucaa_ibufsize = UPLCOMIBUFSIZE; 466 ucaa.ucaa_obufsize = UPLCOMOBUFSIZE; 467 ucaa.ucaa_ibufsizepad = UPLCOMIBUFSIZE; 468 ucaa.ucaa_opkthdrlen = 0; 469 ucaa.ucaa_device = dev; 470 ucaa.ucaa_iface = sc->sc_iface; 471 ucaa.ucaa_methods = &uplcom_methods; 472 ucaa.ucaa_arg = sc; 473 ucaa.ucaa_info = NULL; 474 475 err = uplcom_reset(sc); 476 477 if (err) { 478 aprint_error_dev(self, "reset failed, %s\n", usbd_errstr(err)); 479 sc->sc_dying = true; 480 return; 481 } 482 483 usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev, sc->sc_dev); 484 485 DPRINTF("in=%#jx out=%#jx intr=%#jx", 486 ucaa.ucaa_bulkin, ucaa.ucaa_bulkout, sc->sc_intr_number, 0); 487 sc->sc_subdev = config_found(self, &ucaa, ucomprint, 488 CFARGS(.submatch = ucomsubmatch)); 489 490 if (!pmf_device_register(self, NULL, NULL)) 491 aprint_error_dev(self, "couldn't establish power handler\n"); 492 493 return; 494} 495 496static void 497uplcom_childdet(device_t self, device_t child) 498{ 499 struct uplcom_softc *sc = device_private(self); 500 501 UPLCOMHIST_FUNC(); UPLCOMHIST_CALLED(); 502 503 KASSERT(sc->sc_subdev == child); 504 sc->sc_subdev = NULL; 505} 506 507static void 508uplcom_close_pipe(struct uplcom_softc *sc) 509{ 510 511 if (sc->sc_intr_pipe != NULL) { 512 usbd_abort_pipe(sc->sc_intr_pipe); 513 usbd_close_pipe(sc->sc_intr_pipe); 514 sc->sc_intr_pipe = NULL; 515 } 516 if (sc->sc_intr_buf != NULL) { 517 kmem_free(sc->sc_intr_buf, sc->sc_isize); 518 sc->sc_intr_buf = NULL; 519 } 520} 521 522static int 523uplcom_detach(device_t self, int flags) 524{ 525 struct uplcom_softc *sc = device_private(self); 526 int rv = 0; 527 528 UPLCOMHIST_FUNC(); UPLCOMHIST_CALLED(); 529 DPRINTF("sc=%#jx flags=%jd", (uintptr_t)sc, flags, 0, 0); 530 531 sc->sc_dying = true; 532 533 uplcom_close_pipe(sc); 534 535 if (sc->sc_subdev != NULL) { 536 rv = config_detach(sc->sc_subdev, flags); 537 sc->sc_subdev = NULL; 538 } 539 540 usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev, sc->sc_dev); 541 542 pmf_device_deregister(self); 543 544 return rv; 545} 546 547usbd_status 548uplcom_reset(struct uplcom_softc *sc) 549{ 550 usb_device_request_t req; 551 usbd_status err; 552 553 if (sc->sc_type == UPLCOM_TYPE_HXN) 554 return 0; 555 556 req.bmRequestType = UT_WRITE_VENDOR_DEVICE; 557 req.bRequest = UPLCOM_SET_REQUEST; 558 USETW(req.wValue, 0); 559 USETW(req.wIndex, sc->sc_iface_number); 560 USETW(req.wLength, 0); 561 562 err = usbd_do_request(sc->sc_udev, &req, 0); 563 if (err) 564 return EIO; 565 566 return 0; 567} 568 569struct pl2303x_init { 570 uint8_t req_type; 571 uint8_t request; 572 uint16_t value; 573 uint16_t index; 574}; 575 576static const struct pl2303x_init pl2303x[] = { 577 { UT_READ_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x8484, 0 }, 578 { UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x0404, 0 }, 579 { UT_READ_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x8484, 0 }, 580 { UT_READ_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x8383, 0 }, 581 { UT_READ_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x8484, 0 }, 582 { UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x0404, 1 }, 583 { UT_READ_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x8484, 0 }, 584 { UT_READ_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x8383, 0 }, 585 { UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0, 1 }, 586 { UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 1, 0 }, 587 { UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 2, 0x44 } 588}; 589#define N_PL2302X_INIT (sizeof(pl2303x)/sizeof(pl2303x[0])) 590 591static usbd_status 592uplcom_pl2303x_init(struct uplcom_softc *sc) 593{ 594 usb_device_request_t req; 595 usbd_status err; 596 int i; 597 598 for (i = 0; i < N_PL2302X_INIT; i++) { 599 char buf[1]; 600 void *b; 601 602 req.bmRequestType = pl2303x[i].req_type; 603 req.bRequest = pl2303x[i].request; 604 USETW(req.wValue, pl2303x[i].value); 605 USETW(req.wIndex, pl2303x[i].index); 606 if (UT_GET_DIR(req.bmRequestType) == UT_READ) { 607 b = buf; 608 USETW(req.wLength, sizeof(buf)); 609 } else { 610 b = NULL; 611 USETW(req.wLength, 0); 612 } 613 614 err = usbd_do_request(sc->sc_udev, &req, b); 615 if (err) { 616 aprint_error_dev(sc->sc_dev, 617 "uplcom_pl2303x_init failed: %s\n", 618 usbd_errstr(err)); 619 return EIO; 620 } 621 } 622 623 return 0; 624} 625 626static void 627uplcom_set_line_state(struct uplcom_softc *sc) 628{ 629 usb_device_request_t req; 630 int ls; 631 632 /* make sure we have initialized state for sc_dtr and sc_rts */ 633 if (sc->sc_dtr == -1) 634 sc->sc_dtr = 0; 635 if (sc->sc_rts == -1) 636 sc->sc_rts = 0; 637 638 ls = (sc->sc_dtr ? UCDC_LINE_DTR : 0) | 639 (sc->sc_rts ? UCDC_LINE_RTS : 0); 640 641 req.bmRequestType = UT_WRITE_CLASS_INTERFACE; 642 req.bRequest = UCDC_SET_CONTROL_LINE_STATE; 643 USETW(req.wValue, ls); 644 USETW(req.wIndex, sc->sc_iface_number); 645 USETW(req.wLength, 0); 646 647 (void)usbd_do_request(sc->sc_udev, &req, 0); 648} 649 650static void 651uplcom_set(void *addr, int portno, int reg, int onoff) 652{ 653 struct uplcom_softc *sc = addr; 654 655 if (sc->sc_dying) 656 return; 657 658 switch (reg) { 659 case UCOM_SET_DTR: 660 uplcom_dtr(sc, onoff); 661 break; 662 case UCOM_SET_RTS: 663 uplcom_rts(sc, onoff); 664 break; 665 case UCOM_SET_BREAK: 666 uplcom_break(sc, onoff); 667 break; 668 default: 669 break; 670 } 671} 672 673static void 674uplcom_dtr(struct uplcom_softc *sc, int onoff) 675{ 676 677 UPLCOMHIST_FUNC(); UPLCOMHIST_CALLED(); 678 DPRINTF("onoff=%jd", onoff, 0, 0, 0); 679 680 if (sc->sc_dtr != -1 && !sc->sc_dtr == !onoff) 681 return; 682 683 sc->sc_dtr = !!onoff; 684 685 uplcom_set_line_state(sc); 686} 687 688static void 689uplcom_rts(struct uplcom_softc *sc, int onoff) 690{ 691 UPLCOMHIST_FUNC(); UPLCOMHIST_CALLED(); 692 DPRINTF("onoff=%jd", onoff, 0, 0, 0); 693 694 if (sc->sc_rts != -1 && !sc->sc_rts == !onoff) 695 return; 696 697 sc->sc_rts = !!onoff; 698 699 uplcom_set_line_state(sc); 700} 701 702static void 703uplcom_break(struct uplcom_softc *sc, int onoff) 704{ 705 usb_device_request_t req; 706 707 UPLCOMHIST_FUNC(); UPLCOMHIST_CALLED(); 708 DPRINTF("onoff=%jd", onoff, 0, 0, 0); 709 710 req.bmRequestType = UT_WRITE_CLASS_INTERFACE; 711 req.bRequest = UCDC_SEND_BREAK; 712 USETW(req.wValue, onoff ? UCDC_BREAK_ON : UCDC_BREAK_OFF); 713 USETW(req.wIndex, sc->sc_iface_number); 714 USETW(req.wLength, 0); 715 716 (void)usbd_do_request(sc->sc_udev, &req, 0); 717} 718 719static usbd_status 720uplcom_set_crtscts(struct uplcom_softc *sc) 721{ 722 usb_device_request_t req; 723 usbd_status err; 724 725 UPLCOMHIST_FUNC(); UPLCOMHIST_CALLED(); 726 727 req.bmRequestType = UT_WRITE_VENDOR_DEVICE; 728 if (sc->sc_type == UPLCOM_TYPE_HXN) { 729 req.bRequest = UPLCOM_HXN_SET_REQUEST; 730 USETW(req.wValue, UPLCOM_HXN_SET_CRTSCTS_REG); 731 } else { 732 req.bRequest = UPLCOM_SET_REQUEST; 733 USETW(req.wValue, 0); 734 } 735 736 if (sc->sc_type == UPLCOM_TYPE_HXN) 737 USETW(req.wIndex, UPLCOM_HXN_SET_CRTSCTS); 738 else if (sc->sc_type == UPLCOM_TYPE_HX) 739 USETW(req.wIndex, UPLCOM_SET_CRTSCTS_HX); 740 else 741 USETW(req.wIndex, UPLCOM_SET_CRTSCTS_0); 742 USETW(req.wLength, 0); 743 744 err = usbd_do_request(sc->sc_udev, &req, 0); 745 if (err) { 746 DPRINTF("failed, err=%jd", err, 0, 0, 0); 747 return err; 748 } 749 750 return USBD_NORMAL_COMPLETION; 751} 752 753static usbd_status 754uplcom_set_line_coding(struct uplcom_softc *sc, usb_cdc_line_state_t *state) 755{ 756 usb_device_request_t req; 757 usbd_status err; 758 759 UPLCOMHIST_FUNC(); UPLCOMHIST_CALLED(); 760 761 DPRINTF("rate=%jd fmt=%jd parity=%jd bits=%jd", 762 UGETDW(state->dwDTERate), state->bCharFormat, 763 state->bParityType, state->bDataBits); 764 765 if (memcmp(state, &sc->sc_line_state, UCDC_LINE_STATE_LENGTH) == 0) { 766 DPRINTF("already set", 0, 0, 0, 0); 767 return USBD_NORMAL_COMPLETION; 768 } 769 770 req.bmRequestType = UT_WRITE_CLASS_INTERFACE; 771 req.bRequest = UCDC_SET_LINE_CODING; 772 USETW(req.wValue, 0); 773 USETW(req.wIndex, sc->sc_iface_number); 774 USETW(req.wLength, UCDC_LINE_STATE_LENGTH); 775 776 err = usbd_do_request(sc->sc_udev, &req, state); 777 if (err) { 778 DPRINTF("failed, err=%ju", err, 0, 0, 0); 779 return err; 780 } 781 782 sc->sc_line_state = *state; 783 784 return USBD_NORMAL_COMPLETION; 785} 786 787static int 788uplcom_param(void *addr, int portno, struct termios *t) 789{ 790 struct uplcom_softc *sc = addr; 791 usbd_status err; 792 usb_cdc_line_state_t ls; 793 794 UPLCOMHIST_FUNC(); UPLCOMHIST_CALLED(); 795 DPRINTF("sc=%#jx", (uintptr_t)sc, 0, 0, 0); 796 797 if (sc->sc_dying) 798 return EIO; 799 800 USETDW(ls.dwDTERate, t->c_ospeed); 801 if (ISSET(t->c_cflag, CSTOPB)) 802 ls.bCharFormat = UCDC_STOP_BIT_2; 803 else 804 ls.bCharFormat = UCDC_STOP_BIT_1; 805 if (ISSET(t->c_cflag, PARENB)) { 806 if (ISSET(t->c_cflag, PARODD)) 807 ls.bParityType = UCDC_PARITY_ODD; 808 else 809 ls.bParityType = UCDC_PARITY_EVEN; 810 } else 811 ls.bParityType = UCDC_PARITY_NONE; 812 switch (ISSET(t->c_cflag, CSIZE)) { 813 case CS5: 814 ls.bDataBits = 5; 815 break; 816 case CS6: 817 ls.bDataBits = 6; 818 break; 819 case CS7: 820 ls.bDataBits = 7; 821 break; 822 case CS8: 823 ls.bDataBits = 8; 824 break; 825 } 826 827 err = uplcom_set_line_coding(sc, &ls); 828 if (err) { 829 DPRINTF("err=%jd", err, 0, 0, 0); 830 return EIO; 831 } 832 833 if (ISSET(t->c_cflag, CRTSCTS)) 834 uplcom_set_crtscts(sc); 835 836 if (sc->sc_rts == -1 || sc->sc_dtr == -1) 837 uplcom_set_line_state(sc); 838 839 if (err) { 840 DPRINTF("err=%jd", err, 0, 0, 0); 841 return EIO; 842 } 843 844 return 0; 845} 846 847static usbd_status 848uplcom_vendor_control_write(struct usbd_device *dev, uint16_t value, 849 uint16_t index) 850{ 851 usb_device_request_t req; 852 usbd_status err; 853 854 UPLCOMHIST_FUNC(); UPLCOMHIST_CALLED(); 855 856 req.bmRequestType = UT_WRITE_VENDOR_DEVICE; 857 req.bRequest = UPLCOM_SET_REQUEST; 858 USETW(req.wValue, value); 859 USETW(req.wIndex, index); 860 USETW(req.wLength, 0); 861 862 err = usbd_do_request(dev, &req, NULL); 863 864 if (err) { 865 DPRINTF("vendor write failed, err=%jd", err, 0, 0, 0); 866 } 867 868 return err; 869} 870 871static int 872uplcom_open(void *addr, int portno) 873{ 874 struct uplcom_softc *sc = addr; 875 usbd_status err = 0; 876 877 UPLCOMHIST_FUNC(); UPLCOMHIST_CALLED(); 878 DPRINTF("sc=%#jx", (uintptr_t)sc, 0, 0, 0); 879 880 if (sc->sc_dying) 881 return EIO; 882 883 /* Some unknown device frobbing. */ 884 if (sc->sc_type == UPLCOM_TYPE_HX) 885 uplcom_vendor_control_write(sc->sc_udev, 2, 0x44); 886 else 887 uplcom_vendor_control_write(sc->sc_udev, 2, 0x24); 888 889 if (sc->sc_intr_number != -1 && sc->sc_intr_pipe == NULL) { 890 sc->sc_intr_buf = kmem_alloc(sc->sc_isize, KM_SLEEP); 891 err = usbd_open_pipe_intr(sc->sc_intr_iface, sc->sc_intr_number, 892 USBD_SHORT_XFER_OK, &sc->sc_intr_pipe, sc, 893 sc->sc_intr_buf, sc->sc_isize, 894 uplcom_intr, USBD_DEFAULT_INTERVAL); 895 if (err) { 896 DPRINTF("cannot open interrupt pipe (addr %jd)", 897 sc->sc_intr_number, 0, 0, 0); 898 } 899 } 900 901 if (err == 0 && sc->sc_type == UPLCOM_TYPE_HX) 902 err = uplcom_pl2303x_init(sc); 903 904 return err; 905} 906 907static void 908uplcom_close(void *addr, int portno) 909{ 910 struct uplcom_softc *sc = addr; 911 912 UPLCOMHIST_FUNC(); UPLCOMHIST_CALLED(); 913 DPRINTF("sc=%#jx", (uintptr_t)sc, 0, 0, 0); 914 915 if (sc->sc_dying) 916 return; 917 918 uplcom_close_pipe(sc); 919} 920 921static void 922uplcom_intr(struct usbd_xfer *xfer, void *priv, usbd_status status) 923{ 924 struct uplcom_softc *sc = priv; 925 u_char *buf = sc->sc_intr_buf; 926 u_char pstatus; 927 928 UPLCOMHIST_FUNC(); UPLCOMHIST_CALLED(); 929 930 if (sc->sc_dying) 931 return; 932 933 if (status != USBD_NORMAL_COMPLETION) { 934 if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) 935 return; 936 937 DPRINTF("abnormal status: %ju", status, 0, 0, 0); 938 usbd_clear_endpoint_stall_async(sc->sc_intr_pipe); 939 return; 940 } 941 942 DPRINTF("uplcom status = %02jx", buf[8], 0, 0, 0); 943 944 sc->sc_lsr = sc->sc_msr = 0; 945 pstatus = buf[8]; 946 if (ISSET(pstatus, UPLCOM_N_SERIAL_CTS)) 947 sc->sc_msr |= UMSR_CTS; 948 if (ISSET(pstatus, UCDC_N_SERIAL_RI)) 949 sc->sc_msr |= UMSR_RI; 950 if (ISSET(pstatus, UCDC_N_SERIAL_DSR)) 951 sc->sc_msr |= UMSR_DSR; 952 if (ISSET(pstatus, UCDC_N_SERIAL_DCD)) 953 sc->sc_msr |= UMSR_DCD; 954 ucom_status_change(device_private(sc->sc_subdev)); 955} 956 957static void 958uplcom_get_status(void *addr, int portno, u_char *lsr, u_char *msr) 959{ 960 struct uplcom_softc *sc = addr; 961 962 UPLCOMHIST_FUNC(); UPLCOMHIST_CALLED(); 963 964 if (sc->sc_dying) 965 return; 966 967 *lsr = sc->sc_lsr; 968 *msr = sc->sc_msr; 969} 970 971#if TODO 972static int 973uplcom_ioctl(void *addr, int portno, u_long cmd, void *data, int flag, 974 proc_t *p) 975{ 976 struct uplcom_softc *sc = addr; 977 int error = 0; 978 979 UPLCOMHIST_FUNC(); UPLCOMHIST_CALLED(); 980 981 if (sc->sc_dying) 982 return EIO; 983 984 DPRINTF("cmd=0x%08lx", cmd, 0, 0, 0); 985 986 switch (cmd) { 987 case TIOCNOTTY: 988 case TIOCMGET: 989 case TIOCMSET: 990 case USB_GET_CM_OVER_DATA: 991 case USB_SET_CM_OVER_DATA: 992 break; 993 994 default: 995 DPRINTF("unknown", 0, 0, 0, 0); 996 error = ENOTTY; 997 break; 998 } 999 1000 return error; 1001} 1002#endif 1003