1*fabcfecbSjsg /* $OpenBSD: uchcom.c,v 1.37 2024/10/22 21:50:02 jsg Exp $ */ 2ad942b15Sjsg /* $NetBSD: uchcom.c,v 1.1 2007/09/03 17:57:37 tshiozak Exp $ */ 3ad942b15Sjsg 4ad942b15Sjsg /* 5ad942b15Sjsg * Copyright (c) 2007 The NetBSD Foundation, Inc. 6ad942b15Sjsg * All rights reserved. 7ad942b15Sjsg * 8ad942b15Sjsg * This code is derived from software contributed to The NetBSD Foundation 9ad942b15Sjsg * by Takuya SHIOZAKI (tshiozak@netbsd.org). 10ad942b15Sjsg * 11ad942b15Sjsg * Redistribution and use in source and binary forms, with or without 12ad942b15Sjsg * modification, are permitted provided that the following conditions 13ad942b15Sjsg * are met: 14ad942b15Sjsg * 1. Redistributions of source code must retain the above copyright 15ad942b15Sjsg * notice, this list of conditions and the following disclaimer. 16ad942b15Sjsg * 2. Redistributions in binary form must reproduce the above copyright 17ad942b15Sjsg * notice, this list of conditions and the following disclaimer in the 18ad942b15Sjsg * documentation and/or other materials provided with the distribution. 19ad942b15Sjsg * 20ad942b15Sjsg * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 21ad942b15Sjsg * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22ad942b15Sjsg * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23ad942b15Sjsg * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 24ad942b15Sjsg * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25ad942b15Sjsg * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26ad942b15Sjsg * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27ad942b15Sjsg * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28ad942b15Sjsg * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29ad942b15Sjsg * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30ad942b15Sjsg * POSSIBILITY OF SUCH DAMAGE. 31ad942b15Sjsg */ 32ad942b15Sjsg 33ad942b15Sjsg /* 34e990f8b2Skevlo * driver for WinChipHead CH9102/343/341/340. 35ad942b15Sjsg */ 36ad942b15Sjsg 37ad942b15Sjsg #include <sys/param.h> 38ad942b15Sjsg #include <sys/systm.h> 39ad942b15Sjsg #include <sys/malloc.h> 40ad942b15Sjsg #include <sys/tty.h> 41ad942b15Sjsg #include <sys/device.h> 42ad942b15Sjsg 4335f382c9Skevlo #include <machine/bus.h> 4435f382c9Skevlo 45ad942b15Sjsg #include <dev/usb/usb.h> 46ad942b15Sjsg #include <dev/usb/usbdi.h> 4735f382c9Skevlo #include <dev/usb/usbdivar.h> 48ad942b15Sjsg #include <dev/usb/usbdevs.h> 49ad942b15Sjsg #include <dev/usb/ucomvar.h> 50ad942b15Sjsg 51ad942b15Sjsg #ifdef UCHCOM_DEBUG 52637ea8a0Sjsg #define DPRINTFN(n, x) do { if (uchcomdebug > (n)) printf x; } while (0) 53ad942b15Sjsg int uchcomdebug = 0; 54ad942b15Sjsg #else 55ad942b15Sjsg #define DPRINTFN(n, x) 56ad942b15Sjsg #endif 57ad942b15Sjsg #define DPRINTF(x) DPRINTFN(0, x) 58ad942b15Sjsg 59ad942b15Sjsg #define UCHCOM_IFACE_INDEX 0 6035f382c9Skevlo #define UCHCOM_SECOND_IFACE_INDEX 1 61ad942b15Sjsg 62ad942b15Sjsg #define UCHCOM_REV_CH340 0x0250 6335f382c9Skevlo #define UCHCOM_REV_CH343 0x0440 64ad942b15Sjsg #define UCHCOM_INPUT_BUF_SIZE 8 65ad942b15Sjsg 66ad942b15Sjsg #define UCHCOM_REQ_GET_VERSION 0x5F 67ad942b15Sjsg #define UCHCOM_REQ_READ_REG 0x95 68ad942b15Sjsg #define UCHCOM_REQ_WRITE_REG 0x9A 69ad942b15Sjsg #define UCHCOM_REQ_RESET 0xA1 70ad942b15Sjsg #define UCHCOM_REQ_SET_DTRRTS 0xA4 7135f382c9Skevlo #define UCHCOM_REQ_CH343_WRITE_REG 0xA8 7235f382c9Skevlo #define UCHCOM_REQ_SET_BAUDRATE UCHCOM_REQ_RESET 73ad942b15Sjsg 74ad942b15Sjsg #define UCHCOM_REG_STAT1 0x06 75ad942b15Sjsg #define UCHCOM_REG_STAT2 0x07 76ad942b15Sjsg #define UCHCOM_REG_BPS_PRE 0x12 77ad942b15Sjsg #define UCHCOM_REG_BPS_DIV 0x13 78ad942b15Sjsg #define UCHCOM_REG_BPS_MOD 0x14 79ad942b15Sjsg #define UCHCOM_REG_BPS_PAD 0x0F 800b89560cSdlg #define UCHCOM_REG_BREAK 0x05 810b89560cSdlg #define UCHCOM_REG_LCR 0x18 82ad942b15Sjsg #define UCHCOM_REG_LCR2 0x25 83ad942b15Sjsg 84ad942b15Sjsg #define UCHCOM_VER_20 0x20 85ad942b15Sjsg 86ad942b15Sjsg #define UCHCOM_BASE_UNKNOWN 0 87ad942b15Sjsg #define UCHCOM_BPS_MOD_BASE 20000000 88ad942b15Sjsg #define UCHCOM_BPS_MOD_BASE_OFS 1100 89ad942b15Sjsg 900b89560cSdlg #define UCHCOM_BPS_PRE_IMM 0x80 /* CH341: immediate RX forwarding */ 910b89560cSdlg 92ad942b15Sjsg #define UCHCOM_DTR_MASK 0x20 93ad942b15Sjsg #define UCHCOM_RTS_MASK 0x40 94ad942b15Sjsg 950b89560cSdlg #define UCHCOM_BREAK_MASK 0x01 9635f382c9Skevlo #define UCHCOM_ABREAK_MASK 0x10 9735f382c9Skevlo #define UCHCOM_CH343_BREAK_MASK 0x80 980b89560cSdlg 990b89560cSdlg #define UCHCOM_LCR_CS5 0x00 1000b89560cSdlg #define UCHCOM_LCR_CS6 0x01 1010b89560cSdlg #define UCHCOM_LCR_CS7 0x02 1020b89560cSdlg #define UCHCOM_LCR_CS8 0x03 1030b89560cSdlg #define UCHCOM_LCR_STOPB 0x04 1040b89560cSdlg #define UCHCOM_LCR_PARENB 0x08 1050b89560cSdlg #define UCHCOM_LCR_PARODD 0x00 1060b89560cSdlg #define UCHCOM_LCR_PAREVEN 0x10 1070b89560cSdlg #define UCHCOM_LCR_PARMARK 0x20 1080b89560cSdlg #define UCHCOM_LCR_PARSPACE 0x30 1090b89560cSdlg #define UCHCOM_LCR_TXE 0x40 1100b89560cSdlg #define UCHCOM_LCR_RXE 0x80 111ad942b15Sjsg 112ad942b15Sjsg #define UCHCOM_INTR_STAT1 0x02 113ad942b15Sjsg #define UCHCOM_INTR_STAT2 0x03 114ad942b15Sjsg #define UCHCOM_INTR_LEAST 4 115ad942b15Sjsg 11635f382c9Skevlo #define UCHCOM_T 0x08 11735f382c9Skevlo #define UCHCOM_CL 0x04 11835f382c9Skevlo #define UCHCOM_CT 0x80 1195165e579Ssasano /* 1205165e579Ssasano * XXX - these magic numbers come from Linux (drivers/usb/serial/ch341.c). 1215165e579Ssasano * The manufacturer was unresponsive when asked for documentation. 1225165e579Ssasano */ 1235165e579Ssasano #define UCHCOM_RESET_VALUE 0x501F /* line mode? */ 1245165e579Ssasano #define UCHCOM_RESET_INDEX 0xD90A /* baud rate? */ 1255165e579Ssasano 126ad942b15Sjsg #define UCHCOMOBUFSIZE 256 127ad942b15Sjsg 12835f382c9Skevlo #define UCHCOM_TYPE_CH343 1 12935f382c9Skevlo 130*fabcfecbSjsg struct uchcom_softc { 131ad942b15Sjsg struct device sc_dev; 132ab0b1be7Smglocker struct usbd_device *sc_udev; 133ad942b15Sjsg struct device *sc_subdev; 13435f382c9Skevlo struct usbd_interface *sc_intr_iface; 13535f382c9Skevlo struct usbd_interface *sc_data_iface; 136ad942b15Sjsg /* */ 137ad942b15Sjsg int sc_intr_endpoint; 138ab0b1be7Smglocker struct usbd_pipe *sc_intr_pipe; 139ad942b15Sjsg u_char *sc_intr_buf; 140234dfda1Sderaadt int sc_isize; 141ad942b15Sjsg /* */ 142e219843cSdlg int sc_release; 143ad942b15Sjsg uint8_t sc_version; 14435f382c9Skevlo int sc_type; 145ad942b15Sjsg int sc_dtr; 146ad942b15Sjsg int sc_rts; 147ad942b15Sjsg u_char sc_lsr; 148ad942b15Sjsg u_char sc_msr; 149ad942b15Sjsg int sc_lcr1; 150ad942b15Sjsg int sc_lcr2; 151ad942b15Sjsg }; 152ad942b15Sjsg 153*fabcfecbSjsg struct uchcom_endpoints { 154ad942b15Sjsg int ep_bulkin; 1557f0d0f7fSuaa int ep_bulkin_size; 156ad942b15Sjsg int ep_bulkout; 157ad942b15Sjsg int ep_intr; 158ad942b15Sjsg int ep_intr_size; 159ad942b15Sjsg }; 160ad942b15Sjsg 161*fabcfecbSjsg struct uchcom_divider { 162ad942b15Sjsg uint8_t dv_prescaler; 163ad942b15Sjsg uint8_t dv_div; 164ad942b15Sjsg uint8_t dv_mod; 165ad942b15Sjsg }; 166ad942b15Sjsg 167*fabcfecbSjsg struct uchcom_divider_record { 168ad942b15Sjsg uint32_t dvr_high; 169ad942b15Sjsg uint32_t dvr_low; 170ad942b15Sjsg uint32_t dvr_base_clock; 171ad942b15Sjsg struct uchcom_divider dvr_divider; 172ad942b15Sjsg }; 173ad942b15Sjsg 174ad942b15Sjsg static const struct uchcom_divider_record dividers[] = 175ad942b15Sjsg { 176ad942b15Sjsg { 307200, 307200, UCHCOM_BASE_UNKNOWN, { 7, 0xD9, 0 } }, 177ad942b15Sjsg { 921600, 921600, UCHCOM_BASE_UNKNOWN, { 7, 0xF3, 0 } }, 178ad942b15Sjsg { 2999999, 23530, 6000000, { 3, 0, 0 } }, 179ad942b15Sjsg { 23529, 2942, 750000, { 2, 0, 0 } }, 180ad942b15Sjsg { 2941, 368, 93750, { 1, 0, 0 } }, 181ad942b15Sjsg { 367, 1, 11719, { 0, 0, 0 } }, 182ad942b15Sjsg }; 183ad942b15Sjsg 184ad942b15Sjsg void uchcom_get_status(void *, int, u_char *, u_char *); 185ad942b15Sjsg void uchcom_set(void *, int, int, int); 186ad942b15Sjsg int uchcom_param(void *, int, struct termios *); 187ad942b15Sjsg int uchcom_open(void *, int); 188ad942b15Sjsg void uchcom_close(void *, int); 189ab0b1be7Smglocker void uchcom_intr(struct usbd_xfer *, void *, usbd_status); 190ad942b15Sjsg 191aaf33dadSjsg int uchcom_find_endpoints(struct uchcom_softc *, 192ad942b15Sjsg struct uchcom_endpoints *); 193aaf33dadSjsg void uchcom_close_intr_pipe(struct uchcom_softc *); 194ad942b15Sjsg 195ad942b15Sjsg 196aaf33dadSjsg usbd_status uchcom_generic_control_out(struct uchcom_softc *sc, 197aaf33dadSjsg uint8_t reqno, uint16_t value, uint16_t index); 198aaf33dadSjsg usbd_status uchcom_generic_control_in(struct uchcom_softc *, uint8_t, 199ad942b15Sjsg uint16_t, uint16_t, void *, int, int *); 200aaf33dadSjsg usbd_status uchcom_write_reg(struct uchcom_softc *, uint8_t, uint8_t, 201aaf33dadSjsg uint8_t, uint8_t); 202aaf33dadSjsg usbd_status uchcom_read_reg(struct uchcom_softc *, uint8_t, uint8_t *, 203aaf33dadSjsg uint8_t, uint8_t *); 204aaf33dadSjsg usbd_status uchcom_get_version(struct uchcom_softc *, uint8_t *); 205aaf33dadSjsg usbd_status uchcom_read_status(struct uchcom_softc *, uint8_t *); 206aaf33dadSjsg usbd_status uchcom_set_dtrrts_10(struct uchcom_softc *, uint8_t); 207aaf33dadSjsg usbd_status uchcom_set_dtrrts_20(struct uchcom_softc *, uint8_t); 208aaf33dadSjsg int uchcom_update_version(struct uchcom_softc *); 209aaf33dadSjsg void uchcom_convert_status(struct uchcom_softc *, uint8_t); 210aaf33dadSjsg int uchcom_update_status(struct uchcom_softc *); 211aaf33dadSjsg int uchcom_set_dtrrts(struct uchcom_softc *, int, int); 212aaf33dadSjsg int uchcom_set_break(struct uchcom_softc *, int); 21335f382c9Skevlo int uchcom_set_break_ch343(struct uchcom_softc *, int); 21435f382c9Skevlo void uchcom_calc_baudrate_ch343(uint32_t, uint8_t *, uint8_t *); 215aaf33dadSjsg int uchcom_calc_divider_settings(struct uchcom_divider *, uint32_t); 21635f382c9Skevlo int uchcom_set_dte_rate_ch343(struct uchcom_softc *, uint32_t, 21735f382c9Skevlo uint16_t); 218aaf33dadSjsg int uchcom_set_dte_rate(struct uchcom_softc *, uint32_t); 21935f382c9Skevlo uint16_t uchcom_set_line_control(struct uchcom_softc *, tcflag_t, 22035f382c9Skevlo uint16_t *); 221aaf33dadSjsg int uchcom_clear_chip(struct uchcom_softc *); 222aaf33dadSjsg int uchcom_reset_chip(struct uchcom_softc *); 223aaf33dadSjsg int uchcom_setup_comm(struct uchcom_softc *); 224aaf33dadSjsg int uchcom_setup_intr_pipe(struct uchcom_softc *); 225ad942b15Sjsg 226ad942b15Sjsg 227ad942b15Sjsg int uchcom_match(struct device *, void *, void *); 228ad942b15Sjsg void uchcom_attach(struct device *, struct device *, void *); 229ad942b15Sjsg int uchcom_detach(struct device *, int); 230ad942b15Sjsg 231c520a48cSnaddy const struct ucom_methods uchcom_methods = { 2323ab3d9c7Sjsg uchcom_get_status, 2333ab3d9c7Sjsg uchcom_set, 2343ab3d9c7Sjsg uchcom_param, 2353ab3d9c7Sjsg NULL, 2363ab3d9c7Sjsg uchcom_open, 2373ab3d9c7Sjsg uchcom_close, 2383ab3d9c7Sjsg NULL, 2393ab3d9c7Sjsg NULL, 240ad942b15Sjsg }; 241ad942b15Sjsg 242ad942b15Sjsg static const struct usb_devno uchcom_devs[] = { 243ad942b15Sjsg { USB_VENDOR_WCH, USB_PRODUCT_WCH_CH341 }, 244abb3e393Sjsg { USB_VENDOR_WCH2, USB_PRODUCT_WCH2_CH340 }, 24535f382c9Skevlo { USB_VENDOR_WCH2, USB_PRODUCT_WCH2_CH341A }, 246e990f8b2Skevlo { USB_VENDOR_WCH2, USB_PRODUCT_WCH2_CH343 }, 247e990f8b2Skevlo { USB_VENDOR_WCH2, USB_PRODUCT_WCH2_CH9102 } 248ad942b15Sjsg }; 249ad942b15Sjsg 250ad942b15Sjsg struct cfdriver uchcom_cd = { 251ad942b15Sjsg NULL, "uchcom", DV_DULL 252ad942b15Sjsg }; 253ad942b15Sjsg 254ad942b15Sjsg const struct cfattach uchcom_ca = { 255cf8c8cdaSmpi sizeof(struct uchcom_softc), uchcom_match, uchcom_attach, uchcom_detach 256ad942b15Sjsg }; 257ad942b15Sjsg 258ad942b15Sjsg /* ---------------------------------------------------------------------- 259ad942b15Sjsg * driver entry points 260ad942b15Sjsg */ 261ad942b15Sjsg 262ad942b15Sjsg int 263ad942b15Sjsg uchcom_match(struct device *parent, void *match, void *aux) 264ad942b15Sjsg { 265ad942b15Sjsg struct usb_attach_arg *uaa = aux; 266ad942b15Sjsg 267c0d38480Smpi if (uaa->iface == NULL) 268ad942b15Sjsg return UMATCH_NONE; 269ad942b15Sjsg 270df857f07Sjasper return (usb_lookup(uchcom_devs, uaa->vendor, uaa->product) != NULL ? 271ad942b15Sjsg UMATCH_VENDOR_PRODUCT : UMATCH_NONE); 272ad942b15Sjsg } 273ad942b15Sjsg 274ad942b15Sjsg void 275ad942b15Sjsg uchcom_attach(struct device *parent, struct device *self, void *aux) 276ad942b15Sjsg { 277ad942b15Sjsg struct uchcom_softc *sc = (struct uchcom_softc *)self; 278ad942b15Sjsg struct usb_attach_arg *uaa = aux; 279ad942b15Sjsg struct ucom_attach_args uca; 280ad942b15Sjsg struct uchcom_endpoints endpoints; 281ad942b15Sjsg 28235f382c9Skevlo sc->sc_udev = uaa->device; 28335f382c9Skevlo sc->sc_intr_iface = uaa->iface; 284ad942b15Sjsg sc->sc_dtr = sc->sc_rts = -1; 285e219843cSdlg sc->sc_release = uaa->release; 286ad942b15Sjsg 287ad942b15Sjsg DPRINTF(("\n\nuchcom attach: sc=%p\n", sc)); 288ad942b15Sjsg 28935f382c9Skevlo if (sc->sc_release >= UCHCOM_REV_CH343) { 29035f382c9Skevlo printf("%s: CH343\n", sc->sc_dev.dv_xname); 29135f382c9Skevlo sc->sc_type = UCHCOM_TYPE_CH343; 29235f382c9Skevlo } else if (sc->sc_release == UCHCOM_REV_CH340) 293fc5c6ac5Sderaadt printf("%s: CH340\n", sc->sc_dev.dv_xname); 29435f382c9Skevlo else 295fc5c6ac5Sderaadt printf("%s: CH341\n", sc->sc_dev.dv_xname); 296ad942b15Sjsg 297aaf33dadSjsg if (uchcom_find_endpoints(sc, &endpoints)) 298ad942b15Sjsg goto failed; 299ad942b15Sjsg 300ad942b15Sjsg sc->sc_intr_endpoint = endpoints.ep_intr; 301234dfda1Sderaadt sc->sc_isize = endpoints.ep_intr_size; 302ad942b15Sjsg 303ad942b15Sjsg /* setup ucom layer */ 304ad942b15Sjsg uca.portno = UCOM_UNK_PORTNO; 305ad942b15Sjsg uca.bulkin = endpoints.ep_bulkin; 306ad942b15Sjsg uca.bulkout = endpoints.ep_bulkout; 3077f0d0f7fSuaa uca.ibufsize = endpoints.ep_bulkin_size; 308ad942b15Sjsg uca.obufsize = UCHCOMOBUFSIZE; 3097f0d0f7fSuaa uca.ibufsizepad = endpoints.ep_bulkin_size; 310ad942b15Sjsg uca.opkthdrlen = 0; 31135f382c9Skevlo uca.device = sc->sc_udev; 31235f382c9Skevlo uca.iface = sc->sc_data_iface; 313ad942b15Sjsg uca.methods = &uchcom_methods; 314ad942b15Sjsg uca.arg = sc; 315ad942b15Sjsg uca.info = NULL; 316ad942b15Sjsg 317ad942b15Sjsg sc->sc_subdev = config_found_sm(self, &uca, ucomprint, ucomsubmatch); 318ad942b15Sjsg 319ad942b15Sjsg return; 320ad942b15Sjsg 321ad942b15Sjsg failed: 322cbb8e2d4Spirofti usbd_deactivate(sc->sc_udev); 323ad942b15Sjsg } 324ad942b15Sjsg 325ad942b15Sjsg int 326ad942b15Sjsg uchcom_detach(struct device *self, int flags) 327ad942b15Sjsg { 328ad942b15Sjsg struct uchcom_softc *sc = (struct uchcom_softc *)self; 329ad942b15Sjsg int rv = 0; 330ad942b15Sjsg 331ad942b15Sjsg DPRINTF(("uchcom_detach: sc=%p flags=%d\n", sc, flags)); 332ad942b15Sjsg 333aaf33dadSjsg uchcom_close_intr_pipe(sc); 334ad942b15Sjsg 335ad942b15Sjsg if (sc->sc_subdev != NULL) { 336ad942b15Sjsg rv = config_detach(sc->sc_subdev, flags); 337ad942b15Sjsg sc->sc_subdev = NULL; 338ad942b15Sjsg } 339ad942b15Sjsg 340ad942b15Sjsg return rv; 341ad942b15Sjsg } 342ad942b15Sjsg 343ad942b15Sjsg int 344aaf33dadSjsg uchcom_find_endpoints(struct uchcom_softc *sc, 345aaf33dadSjsg struct uchcom_endpoints *endpoints) 346ad942b15Sjsg { 3477f0d0f7fSuaa int i, bin=-1, bout=-1, intr=-1, binsize=0, isize=0; 34835f382c9Skevlo usb_config_descriptor_t *cdesc; 349ad942b15Sjsg usb_interface_descriptor_t *id; 350ad942b15Sjsg usb_endpoint_descriptor_t *ed; 35135f382c9Skevlo usbd_status err; 35235f382c9Skevlo uint8_t ifaceno; 353ad942b15Sjsg 35435f382c9Skevlo /* Get the config descriptor. */ 35535f382c9Skevlo cdesc = usbd_get_config_descriptor(sc->sc_udev); 35635f382c9Skevlo 35735f382c9Skevlo if (cdesc == NULL) { 35835f382c9Skevlo printf("%s: failed to get configuration descriptor\n", 35935f382c9Skevlo sc->sc_dev.dv_xname); 36035f382c9Skevlo return -1; 36135f382c9Skevlo } 36235f382c9Skevlo 36335f382c9Skevlo id = usbd_get_interface_descriptor(sc->sc_intr_iface); 364ad942b15Sjsg 365ad942b15Sjsg for (i = 0; i < id->bNumEndpoints; i++) { 36635f382c9Skevlo ed = usbd_interface2endpoint_descriptor(sc->sc_intr_iface, i); 367ad942b15Sjsg if (ed == NULL) { 368ad942b15Sjsg printf("%s: no endpoint descriptor for %d\n", 369ad942b15Sjsg sc->sc_dev.dv_xname, i); 370ad942b15Sjsg return -1; 371ad942b15Sjsg } 372ad942b15Sjsg 373ad942b15Sjsg if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN && 374ad942b15Sjsg UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT) { 375ad942b15Sjsg intr = ed->bEndpointAddress; 376ad942b15Sjsg isize = UGETW(ed->wMaxPacketSize); 37735f382c9Skevlo } 37835f382c9Skevlo } 37935f382c9Skevlo 38035f382c9Skevlo ifaceno = (cdesc->bNumInterfaces == 2) ? 38135f382c9Skevlo UCHCOM_SECOND_IFACE_INDEX : UCHCOM_IFACE_INDEX; 38235f382c9Skevlo 38335f382c9Skevlo err = usbd_device2interface_handle(sc->sc_udev, ifaceno, 38435f382c9Skevlo &sc->sc_data_iface); 38535f382c9Skevlo if (err) { 38635f382c9Skevlo printf("\n%s: failed to get second interface, err=%s\n", 38735f382c9Skevlo sc->sc_dev.dv_xname, usbd_errstr(err)); 38835f382c9Skevlo return -1; 38935f382c9Skevlo } 39035f382c9Skevlo 39135f382c9Skevlo id = usbd_get_interface_descriptor(sc->sc_data_iface); 39235f382c9Skevlo 39335f382c9Skevlo for (i = 0; i < id->bNumEndpoints; i++) { 39435f382c9Skevlo ed = usbd_interface2endpoint_descriptor(sc->sc_data_iface, i); 39535f382c9Skevlo if (ed == NULL) { 39635f382c9Skevlo printf("%s: no endpoint descriptor for %d\n", 39735f382c9Skevlo sc->sc_dev.dv_xname, i); 39835f382c9Skevlo return -1; 39935f382c9Skevlo } 40035f382c9Skevlo 40135f382c9Skevlo if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN && 402ad942b15Sjsg UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) { 403ad942b15Sjsg bin = ed->bEndpointAddress; 4047f0d0f7fSuaa binsize = UGETW(ed->wMaxPacketSize); 405ad942b15Sjsg } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT && 406ad942b15Sjsg UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) { 407ad942b15Sjsg bout = ed->bEndpointAddress; 408ad942b15Sjsg } 409ad942b15Sjsg } 410ad942b15Sjsg 411ad942b15Sjsg if (intr == -1 || bin == -1 || bout == -1) { 412ad942b15Sjsg if (intr == -1) { 413ad942b15Sjsg printf("%s: no interrupt end point\n", 414ad942b15Sjsg sc->sc_dev.dv_xname); 415ad942b15Sjsg } 416ad942b15Sjsg if (bin == -1) { 417ad942b15Sjsg printf("%s: no data bulk in end point\n", 418ad942b15Sjsg sc->sc_dev.dv_xname); 419ad942b15Sjsg } 420ad942b15Sjsg if (bout == -1) { 421ad942b15Sjsg printf("%s: no data bulk out end point\n", 422ad942b15Sjsg sc->sc_dev.dv_xname); 423ad942b15Sjsg } 424ad942b15Sjsg return -1; 425ad942b15Sjsg } 426ad942b15Sjsg if (isize < UCHCOM_INTR_LEAST) { 427ad942b15Sjsg printf("%s: intr pipe is too short", sc->sc_dev.dv_xname); 428ad942b15Sjsg return -1; 429ad942b15Sjsg } 430ad942b15Sjsg 431ad942b15Sjsg DPRINTF(("%s: bulkin=%d, bulkout=%d, intr=%d, isize=%d\n", 432ad942b15Sjsg sc->sc_dev.dv_xname, bin, bout, intr, isize)); 433ad942b15Sjsg 43435f382c9Skevlo usbd_claim_iface(sc->sc_udev, ifaceno); 43535f382c9Skevlo 436ad942b15Sjsg endpoints->ep_intr = intr; 437ad942b15Sjsg endpoints->ep_intr_size = isize; 438ad942b15Sjsg endpoints->ep_bulkin = bin; 4397f0d0f7fSuaa endpoints->ep_bulkin_size = binsize; 440ad942b15Sjsg endpoints->ep_bulkout = bout; 441ad942b15Sjsg 442ad942b15Sjsg return 0; 443ad942b15Sjsg } 444ad942b15Sjsg 445ad942b15Sjsg 446ad942b15Sjsg /* ---------------------------------------------------------------------- 447ad942b15Sjsg * low level i/o 448ad942b15Sjsg */ 449ad942b15Sjsg 450ad942b15Sjsg usbd_status 451aaf33dadSjsg uchcom_generic_control_out(struct uchcom_softc *sc, uint8_t reqno, 452ad942b15Sjsg uint16_t value, uint16_t index) 453ad942b15Sjsg { 454ad942b15Sjsg usb_device_request_t req; 455ad942b15Sjsg 456ad942b15Sjsg req.bmRequestType = UT_WRITE_VENDOR_DEVICE; 457ad942b15Sjsg req.bRequest = reqno; 458ad942b15Sjsg USETW(req.wValue, value); 459ad942b15Sjsg USETW(req.wIndex, index); 460ad942b15Sjsg USETW(req.wLength, 0); 461ad942b15Sjsg 462ad942b15Sjsg return usbd_do_request(sc->sc_udev, &req, 0); 463ad942b15Sjsg } 464ad942b15Sjsg 465ad942b15Sjsg usbd_status 466aaf33dadSjsg uchcom_generic_control_in(struct uchcom_softc *sc, uint8_t reqno, 467ad942b15Sjsg uint16_t value, uint16_t index, void *buf, int buflen, int *actlen) 468ad942b15Sjsg { 469ad942b15Sjsg usb_device_request_t req; 470ad942b15Sjsg 471ad942b15Sjsg req.bmRequestType = UT_READ_VENDOR_DEVICE; 472ad942b15Sjsg req.bRequest = reqno; 473ad942b15Sjsg USETW(req.wValue, value); 474ad942b15Sjsg USETW(req.wIndex, index); 475ad942b15Sjsg USETW(req.wLength, (uint16_t)buflen); 476ad942b15Sjsg 477ad942b15Sjsg return usbd_do_request_flags(sc->sc_udev, &req, buf, 478ad942b15Sjsg USBD_SHORT_XFER_OK, actlen, 479ad942b15Sjsg USBD_DEFAULT_TIMEOUT); 480ad942b15Sjsg } 481ad942b15Sjsg 482ad942b15Sjsg usbd_status 483aaf33dadSjsg uchcom_write_reg(struct uchcom_softc *sc, 484ad942b15Sjsg uint8_t reg1, uint8_t val1, uint8_t reg2, uint8_t val2) 485ad942b15Sjsg { 486ad942b15Sjsg DPRINTF(("uchcom: write reg 0x%02X<-0x%02X, 0x%02X<-0x%02X\n", 487ad942b15Sjsg (unsigned)reg1, (unsigned)val1, 488ad942b15Sjsg (unsigned)reg2, (unsigned)val2)); 48935f382c9Skevlo return uchcom_generic_control_out(sc, 49035f382c9Skevlo (sc->sc_type != UCHCOM_TYPE_CH343) ? 49135f382c9Skevlo UCHCOM_REQ_WRITE_REG : UCHCOM_REQ_CH343_WRITE_REG, 492ad942b15Sjsg reg1|((uint16_t)reg2<<8), val1|((uint16_t)val2<<8)); 493ad942b15Sjsg } 494ad942b15Sjsg 495ad942b15Sjsg usbd_status 496aaf33dadSjsg uchcom_read_reg(struct uchcom_softc *sc, 497ad942b15Sjsg uint8_t reg1, uint8_t *rval1, uint8_t reg2, uint8_t *rval2) 498ad942b15Sjsg { 499ad942b15Sjsg uint8_t buf[UCHCOM_INPUT_BUF_SIZE]; 500ad942b15Sjsg usbd_status err; 501ad942b15Sjsg int actin; 502ad942b15Sjsg 503aaf33dadSjsg err = uchcom_generic_control_in( 504ad942b15Sjsg sc, UCHCOM_REQ_READ_REG, 505ad942b15Sjsg reg1|((uint16_t)reg2<<8), 0, buf, sizeof buf, &actin); 506ad942b15Sjsg if (err) 507ad942b15Sjsg return err; 508ad942b15Sjsg 509ad942b15Sjsg DPRINTF(("uchcom: read reg 0x%02X->0x%02X, 0x%02X->0x%02X\n", 510ad942b15Sjsg (unsigned)reg1, (unsigned)buf[0], 511ad942b15Sjsg (unsigned)reg2, (unsigned)buf[1])); 512ad942b15Sjsg 513ad942b15Sjsg if (rval1) *rval1 = buf[0]; 514ad942b15Sjsg if (rval2) *rval2 = buf[1]; 515ad942b15Sjsg 516ad942b15Sjsg return USBD_NORMAL_COMPLETION; 517ad942b15Sjsg } 518ad942b15Sjsg 519ad942b15Sjsg usbd_status 520aaf33dadSjsg uchcom_get_version(struct uchcom_softc *sc, uint8_t *rver) 521ad942b15Sjsg { 522ad942b15Sjsg uint8_t buf[UCHCOM_INPUT_BUF_SIZE]; 523ad942b15Sjsg usbd_status err; 524ad942b15Sjsg int actin; 525ad942b15Sjsg 526aaf33dadSjsg err = uchcom_generic_control_in( 527ad942b15Sjsg sc, UCHCOM_REQ_GET_VERSION, 0, 0, buf, sizeof buf, &actin); 528ad942b15Sjsg if (err) 529ad942b15Sjsg return err; 530ad942b15Sjsg 531ad942b15Sjsg if (rver) *rver = buf[0]; 532ad942b15Sjsg 533ad942b15Sjsg return USBD_NORMAL_COMPLETION; 534ad942b15Sjsg } 535ad942b15Sjsg 536ad942b15Sjsg usbd_status 537aaf33dadSjsg uchcom_read_status(struct uchcom_softc *sc, uint8_t *rval) 538ad942b15Sjsg { 539aaf33dadSjsg return uchcom_read_reg(sc, UCHCOM_REG_STAT1, rval, UCHCOM_REG_STAT2, 540aaf33dadSjsg NULL); 541ad942b15Sjsg } 542ad942b15Sjsg 543ad942b15Sjsg usbd_status 544aaf33dadSjsg uchcom_set_dtrrts_10(struct uchcom_softc *sc, uint8_t val) 545ad942b15Sjsg { 546aaf33dadSjsg return uchcom_write_reg(sc, UCHCOM_REG_STAT1, val, UCHCOM_REG_STAT1, 547aaf33dadSjsg val); 548ad942b15Sjsg } 549ad942b15Sjsg 550ad942b15Sjsg usbd_status 551aaf33dadSjsg uchcom_set_dtrrts_20(struct uchcom_softc *sc, uint8_t val) 552ad942b15Sjsg { 553aaf33dadSjsg return uchcom_generic_control_out(sc, UCHCOM_REQ_SET_DTRRTS, val, 0); 554ad942b15Sjsg } 555ad942b15Sjsg 556ad942b15Sjsg 557ad942b15Sjsg /* ---------------------------------------------------------------------- 558ad942b15Sjsg * middle layer 559ad942b15Sjsg */ 560ad942b15Sjsg 561ad942b15Sjsg int 562aaf33dadSjsg uchcom_update_version(struct uchcom_softc *sc) 563ad942b15Sjsg { 564ad942b15Sjsg usbd_status err; 565ad942b15Sjsg 566aaf33dadSjsg err = uchcom_get_version(sc, &sc->sc_version); 567ad942b15Sjsg if (err) { 568ad942b15Sjsg printf("%s: cannot get version: %s\n", 569ad942b15Sjsg sc->sc_dev.dv_xname, usbd_errstr(err)); 570ad942b15Sjsg return EIO; 571ad942b15Sjsg } 572ad942b15Sjsg 573ad942b15Sjsg return 0; 574ad942b15Sjsg } 575ad942b15Sjsg 576ad942b15Sjsg void 577aaf33dadSjsg uchcom_convert_status(struct uchcom_softc *sc, uint8_t cur) 578ad942b15Sjsg { 579ad942b15Sjsg sc->sc_dtr = !(cur & UCHCOM_DTR_MASK); 580ad942b15Sjsg sc->sc_rts = !(cur & UCHCOM_RTS_MASK); 581ad942b15Sjsg 582ad942b15Sjsg cur = ~cur & 0x0F; 583ad942b15Sjsg sc->sc_msr = (cur << 4) | ((sc->sc_msr >> 4) ^ cur); 584ad942b15Sjsg } 585ad942b15Sjsg 586ad942b15Sjsg int 587aaf33dadSjsg uchcom_update_status(struct uchcom_softc *sc) 588ad942b15Sjsg { 589ad942b15Sjsg usbd_status err; 590ad942b15Sjsg uint8_t cur; 591ad942b15Sjsg 592aaf33dadSjsg err = uchcom_read_status(sc, &cur); 593ad942b15Sjsg if (err) { 594ad942b15Sjsg printf("%s: cannot update status: %s\n", 595ad942b15Sjsg sc->sc_dev.dv_xname, usbd_errstr(err)); 596ad942b15Sjsg return EIO; 597ad942b15Sjsg } 598aaf33dadSjsg uchcom_convert_status(sc, cur); 599ad942b15Sjsg 600ad942b15Sjsg return 0; 601ad942b15Sjsg } 602ad942b15Sjsg 603ad942b15Sjsg 604ad942b15Sjsg int 605aaf33dadSjsg uchcom_set_dtrrts(struct uchcom_softc *sc, int dtr, int rts) 606ad942b15Sjsg { 607ad942b15Sjsg usbd_status err; 608ad942b15Sjsg uint8_t val = 0; 609ad942b15Sjsg 610ad942b15Sjsg if (dtr) val |= UCHCOM_DTR_MASK; 611ad942b15Sjsg if (rts) val |= UCHCOM_RTS_MASK; 612ad942b15Sjsg 613ad942b15Sjsg if (sc->sc_version < UCHCOM_VER_20) 614aaf33dadSjsg err = uchcom_set_dtrrts_10(sc, ~val); 615ad942b15Sjsg else 616aaf33dadSjsg err = uchcom_set_dtrrts_20(sc, ~val); 617ad942b15Sjsg 618ad942b15Sjsg if (err) { 619ad942b15Sjsg printf("%s: cannot set DTR/RTS: %s\n", 620ad942b15Sjsg sc->sc_dev.dv_xname, usbd_errstr(err)); 621ad942b15Sjsg return EIO; 622ad942b15Sjsg } 623ad942b15Sjsg 624ad942b15Sjsg return 0; 625ad942b15Sjsg } 626ad942b15Sjsg 627ad942b15Sjsg int 628aaf33dadSjsg uchcom_set_break(struct uchcom_softc *sc, int onoff) 629ad942b15Sjsg { 630ad942b15Sjsg usbd_status err; 6310b89560cSdlg uint8_t brk, lcr; 632ad942b15Sjsg 6330b89560cSdlg err = uchcom_read_reg(sc, UCHCOM_REG_BREAK, &brk, UCHCOM_REG_LCR, &lcr); 634ad942b15Sjsg if (err) 635ad942b15Sjsg return EIO; 636ad942b15Sjsg if (onoff) { 637ad942b15Sjsg /* on - clear bits */ 6380b89560cSdlg brk &= ~UCHCOM_BREAK_MASK; 6390b89560cSdlg lcr &= ~UCHCOM_LCR_TXE; 640ad942b15Sjsg } else { 641ad942b15Sjsg /* off - set bits */ 6420b89560cSdlg brk |= UCHCOM_BREAK_MASK; 6430b89560cSdlg lcr |= UCHCOM_LCR_TXE; 644ad942b15Sjsg } 6450b89560cSdlg err = uchcom_write_reg(sc, UCHCOM_REG_BREAK, brk, UCHCOM_REG_LCR, lcr); 646ad942b15Sjsg if (err) 647ad942b15Sjsg return EIO; 648ad942b15Sjsg 649ad942b15Sjsg return 0; 650ad942b15Sjsg } 651ad942b15Sjsg 652ad942b15Sjsg int 65335f382c9Skevlo uchcom_set_break_ch343(struct uchcom_softc *sc, int onoff) 65435f382c9Skevlo { 65535f382c9Skevlo usbd_status err; 65635f382c9Skevlo uint8_t brk = UCHCOM_CH343_BREAK_MASK; 65735f382c9Skevlo 65835f382c9Skevlo if (!onoff) 65935f382c9Skevlo brk |= UCHCOM_ABREAK_MASK; 66035f382c9Skevlo 66135f382c9Skevlo err = uchcom_write_reg(sc, brk, 0, 0, 0); 66235f382c9Skevlo if (err) 66335f382c9Skevlo return EIO; 66435f382c9Skevlo 66535f382c9Skevlo return 0; 66635f382c9Skevlo } 66735f382c9Skevlo 66835f382c9Skevlo void 66935f382c9Skevlo uchcom_calc_baudrate_ch343(uint32_t rate, uint8_t *divisor, uint8_t *factor) 67035f382c9Skevlo { 67135f382c9Skevlo uint32_t clk = 12000000; 67235f382c9Skevlo 67335f382c9Skevlo if (rate >= 256000) 67435f382c9Skevlo *divisor = 7; 67535f382c9Skevlo else if (rate > 23529) { 67635f382c9Skevlo clk /= 2; 67735f382c9Skevlo *divisor = 3; 67835f382c9Skevlo } else if (rate > 2941) { 67935f382c9Skevlo clk /= 16; 68035f382c9Skevlo *divisor = 2; 68135f382c9Skevlo } else if (rate > 367) { 68235f382c9Skevlo clk /= 128; 68335f382c9Skevlo *divisor = 1; 68435f382c9Skevlo } else { 68535f382c9Skevlo clk = 11719; 68635f382c9Skevlo *divisor = 0; 68735f382c9Skevlo } 68835f382c9Skevlo 68935f382c9Skevlo *factor = 256 - clk / rate; 69035f382c9Skevlo } 69135f382c9Skevlo 69235f382c9Skevlo int 693aaf33dadSjsg uchcom_calc_divider_settings(struct uchcom_divider *dp, uint32_t rate) 694ad942b15Sjsg { 695ad942b15Sjsg int i; 696ad942b15Sjsg const struct uchcom_divider_record *rp; 697ad942b15Sjsg uint32_t div, rem, mod; 698ad942b15Sjsg 699ad942b15Sjsg /* find record */ 7007d128c60Sjasper for (i=0; i<nitems(dividers); i++) { 701ad942b15Sjsg if (dividers[i].dvr_high >= rate && 702ad942b15Sjsg dividers[i].dvr_low <= rate) { 703ad942b15Sjsg rp = ÷rs[i]; 704ad942b15Sjsg goto found; 705ad942b15Sjsg } 706ad942b15Sjsg } 707ad942b15Sjsg return -1; 708ad942b15Sjsg 709ad942b15Sjsg found: 710ad942b15Sjsg dp->dv_prescaler = rp->dvr_divider.dv_prescaler; 711ad942b15Sjsg if (rp->dvr_base_clock == UCHCOM_BASE_UNKNOWN) 712ad942b15Sjsg dp->dv_div = rp->dvr_divider.dv_div; 713ad942b15Sjsg else { 714ad942b15Sjsg div = rp->dvr_base_clock / rate; 715ad942b15Sjsg rem = rp->dvr_base_clock % rate; 716ad942b15Sjsg if (div==0 || div>=0xFF) 717ad942b15Sjsg return -1; 718ad942b15Sjsg if ((rem<<1) >= rate) 719ad942b15Sjsg div += 1; 720ad942b15Sjsg dp->dv_div = (uint8_t)-div; 721ad942b15Sjsg } 722ad942b15Sjsg 723ad942b15Sjsg mod = UCHCOM_BPS_MOD_BASE/rate + UCHCOM_BPS_MOD_BASE_OFS; 724ad942b15Sjsg mod = mod + mod/2; 725ad942b15Sjsg 726ad942b15Sjsg dp->dv_mod = mod / 0x100; 727ad942b15Sjsg 728ad942b15Sjsg return 0; 729ad942b15Sjsg } 730ad942b15Sjsg 731ad942b15Sjsg int 73235f382c9Skevlo uchcom_set_dte_rate_ch343(struct uchcom_softc *sc, uint32_t rate, uint16_t val) 73335f382c9Skevlo { 73435f382c9Skevlo usbd_status err; 73535f382c9Skevlo uint16_t idx; 73635f382c9Skevlo uint8_t factor, div; 73735f382c9Skevlo 73835f382c9Skevlo uchcom_calc_baudrate_ch343(rate, &div, &factor); 73935f382c9Skevlo idx = (factor << 8) | div; 74035f382c9Skevlo 74135f382c9Skevlo err = uchcom_generic_control_out(sc, UCHCOM_REQ_SET_BAUDRATE, val, idx); 74235f382c9Skevlo if (err) { 74335f382c9Skevlo printf("%s: cannot set DTE rate: %s\n", 74435f382c9Skevlo sc->sc_dev.dv_xname, usbd_errstr(err)); 74535f382c9Skevlo return EIO; 74635f382c9Skevlo } 74735f382c9Skevlo 74835f382c9Skevlo return 0; 74935f382c9Skevlo } 75035f382c9Skevlo 75135f382c9Skevlo int 752aaf33dadSjsg uchcom_set_dte_rate(struct uchcom_softc *sc, uint32_t rate) 753ad942b15Sjsg { 754ad942b15Sjsg usbd_status err; 755ad942b15Sjsg struct uchcom_divider dv; 756ad942b15Sjsg 757aaf33dadSjsg if (uchcom_calc_divider_settings(&dv, rate)) 758ad942b15Sjsg return EINVAL; 759ad942b15Sjsg 760aaf33dadSjsg if ((err = uchcom_write_reg(sc, 761ad942b15Sjsg UCHCOM_REG_BPS_PRE, dv.dv_prescaler, 762ad942b15Sjsg UCHCOM_REG_BPS_DIV, dv.dv_div)) || 763aaf33dadSjsg (err = uchcom_write_reg(sc, 764ad942b15Sjsg UCHCOM_REG_BPS_MOD, dv.dv_mod, 765ad942b15Sjsg UCHCOM_REG_BPS_PAD, 0))) { 766ad942b15Sjsg printf("%s: cannot set DTE rate: %s\n", 767ad942b15Sjsg sc->sc_dev.dv_xname, usbd_errstr(err)); 768ad942b15Sjsg return EIO; 769ad942b15Sjsg } 770ad942b15Sjsg 771ad942b15Sjsg return 0; 772ad942b15Sjsg } 773ad942b15Sjsg 77435f382c9Skevlo uint16_t 77535f382c9Skevlo uchcom_set_line_control(struct uchcom_softc *sc, tcflag_t cflag, uint16_t *val) 776ad942b15Sjsg { 7770b89560cSdlg usbd_status err; 7780b89560cSdlg uint8_t lcr = 0, lcr2 = 0; 7790b89560cSdlg 780e219843cSdlg if (sc->sc_release == UCHCOM_REV_CH340) { 781e219843cSdlg /* 782e219843cSdlg * XXX: it is difficult to handle the line control 783e219843cSdlg * appropriately on CH340: 784e219843cSdlg * work as chip default - CS8, no parity, !CSTOPB 785e219843cSdlg * other modes are not supported. 786e219843cSdlg */ 787e219843cSdlg switch (ISSET(cflag, CSIZE)) { 788e219843cSdlg case CS5: 789e219843cSdlg case CS6: 790e219843cSdlg case CS7: 791e219843cSdlg return EINVAL; 792e219843cSdlg case CS8: 793e219843cSdlg break; 794e219843cSdlg } 795e219843cSdlg if (ISSET(cflag, PARENB) || ISSET(cflag, CSTOPB)) 796e219843cSdlg return EINVAL; 797e219843cSdlg return 0; 798e219843cSdlg } 799e219843cSdlg 80035f382c9Skevlo if (sc->sc_type != UCHCOM_TYPE_CH343) { 80135f382c9Skevlo err = uchcom_read_reg(sc, UCHCOM_REG_LCR, &lcr, UCHCOM_REG_LCR2, 80235f382c9Skevlo &lcr2); 8030b89560cSdlg if (err) { 8040b89560cSdlg printf("%s: cannot get LCR: %s\n", 8050b89560cSdlg sc->sc_dev.dv_xname, usbd_errstr(err)); 8060b89560cSdlg return EIO; 8070b89560cSdlg } 80835f382c9Skevlo } 8090b89560cSdlg 8100b89560cSdlg lcr = UCHCOM_LCR_RXE | UCHCOM_LCR_TXE; 811ad942b15Sjsg 812ad942b15Sjsg switch (ISSET(cflag, CSIZE)) { 813ad942b15Sjsg case CS5: 8140b89560cSdlg lcr |= UCHCOM_LCR_CS5; 8150b89560cSdlg break; 816ad942b15Sjsg case CS6: 8170b89560cSdlg lcr |= UCHCOM_LCR_CS6; 8180b89560cSdlg break; 819ad942b15Sjsg case CS7: 8200b89560cSdlg lcr |= UCHCOM_LCR_CS7; 8210b89560cSdlg break; 822ad942b15Sjsg case CS8: 8230b89560cSdlg lcr |= UCHCOM_LCR_CS8; 824ad942b15Sjsg break; 825ad942b15Sjsg } 826ad942b15Sjsg 8270b89560cSdlg if (ISSET(cflag, PARENB)) { 8280b89560cSdlg lcr |= UCHCOM_LCR_PARENB; 8290b89560cSdlg if (!ISSET(cflag, PARODD)) 8300b89560cSdlg lcr |= UCHCOM_LCR_PAREVEN; 8310b89560cSdlg } 8320b89560cSdlg 8330b89560cSdlg if (ISSET(cflag, CSTOPB)) { 8340b89560cSdlg lcr |= UCHCOM_LCR_STOPB; 8350b89560cSdlg } 8360b89560cSdlg 83735f382c9Skevlo if (sc->sc_type != UCHCOM_TYPE_CH343) { 83835f382c9Skevlo err = uchcom_write_reg(sc, UCHCOM_REG_LCR, lcr, UCHCOM_REG_LCR2, 83935f382c9Skevlo lcr2); 8400b89560cSdlg if (err) { 8410b89560cSdlg printf("%s: cannot set LCR: %s\n", 8420b89560cSdlg sc->sc_dev.dv_xname, usbd_errstr(err)); 8430b89560cSdlg return EIO; 8440b89560cSdlg } 84535f382c9Skevlo } else 84635f382c9Skevlo *val = UCHCOM_T | UCHCOM_CL | UCHCOM_CT | lcr << 8; 847ad942b15Sjsg 848ad942b15Sjsg return 0; 849ad942b15Sjsg } 850ad942b15Sjsg 851ad942b15Sjsg int 852aaf33dadSjsg uchcom_clear_chip(struct uchcom_softc *sc) 853ad942b15Sjsg { 854ad942b15Sjsg usbd_status err; 855ad942b15Sjsg 856ad942b15Sjsg DPRINTF(("%s: clear\n", sc->sc_dev.dv_xname)); 857aaf33dadSjsg err = uchcom_generic_control_out(sc, UCHCOM_REQ_RESET, 0, 0); 858ad942b15Sjsg if (err) { 859ad942b15Sjsg printf("%s: cannot clear: %s\n", 860ad942b15Sjsg sc->sc_dev.dv_xname, usbd_errstr(err)); 861ad942b15Sjsg return EIO; 862ad942b15Sjsg } 863ad942b15Sjsg 864ad942b15Sjsg return 0; 865ad942b15Sjsg } 866ad942b15Sjsg 867ad942b15Sjsg int 868aaf33dadSjsg uchcom_reset_chip(struct uchcom_softc *sc) 869ad942b15Sjsg { 870ad942b15Sjsg usbd_status err; 871ad942b15Sjsg 8725165e579Ssasano DPRINTF(("%s: reset\n", sc->sc_dev.dv_xname)); 873ad942b15Sjsg 8745165e579Ssasano err = uchcom_generic_control_out(sc, UCHCOM_REQ_RESET, 8755165e579Ssasano UCHCOM_RESET_VALUE, 8765165e579Ssasano UCHCOM_RESET_INDEX); 877ad942b15Sjsg if (err) 878ad942b15Sjsg goto failed; 879ad942b15Sjsg 880ad942b15Sjsg return 0; 881ad942b15Sjsg 882ad942b15Sjsg failed: 883ad942b15Sjsg printf("%s: cannot reset: %s\n", 884ad942b15Sjsg sc->sc_dev.dv_xname, usbd_errstr(err)); 885ad942b15Sjsg return EIO; 886ad942b15Sjsg } 887ad942b15Sjsg 888ad942b15Sjsg int 889aaf33dadSjsg uchcom_setup_comm(struct uchcom_softc *sc) 890ad942b15Sjsg { 891ad942b15Sjsg int ret; 892ad942b15Sjsg 893aaf33dadSjsg ret = uchcom_clear_chip(sc); 894ad942b15Sjsg if (ret) 895ad942b15Sjsg return ret; 896ad942b15Sjsg 897aaf33dadSjsg ret = uchcom_set_dte_rate(sc, TTYDEF_SPEED); 898ad942b15Sjsg if (ret) 899ad942b15Sjsg return ret; 900ad942b15Sjsg 90135f382c9Skevlo ret = uchcom_set_line_control(sc, CS8, 0); 902ad942b15Sjsg if (ret) 903ad942b15Sjsg return ret; 904ad942b15Sjsg 905aaf33dadSjsg ret = uchcom_update_status(sc); 906ad942b15Sjsg if (ret) 907ad942b15Sjsg return ret; 908ad942b15Sjsg 909aaf33dadSjsg ret = uchcom_reset_chip(sc); 910ad942b15Sjsg if (ret) 911ad942b15Sjsg return ret; 912ad942b15Sjsg 913aaf33dadSjsg ret = uchcom_set_dte_rate(sc, TTYDEF_SPEED); /* XXX */ 914ad942b15Sjsg if (ret) 915ad942b15Sjsg return ret; 916ad942b15Sjsg 917ad942b15Sjsg return 0; 918ad942b15Sjsg } 919ad942b15Sjsg 920ad942b15Sjsg int 921aaf33dadSjsg uchcom_setup_intr_pipe(struct uchcom_softc *sc) 922ad942b15Sjsg { 923ad942b15Sjsg usbd_status err; 924ad942b15Sjsg 925ad942b15Sjsg if (sc->sc_intr_endpoint != -1 && sc->sc_intr_pipe == NULL) { 926234dfda1Sderaadt sc->sc_intr_buf = malloc(sc->sc_isize, M_USBDEV, M_WAITOK); 92735f382c9Skevlo err = usbd_open_pipe_intr(sc->sc_intr_iface, 928ad942b15Sjsg sc->sc_intr_endpoint, 929ad942b15Sjsg USBD_SHORT_XFER_OK, 930ad942b15Sjsg &sc->sc_intr_pipe, sc, 931ad942b15Sjsg sc->sc_intr_buf, 932234dfda1Sderaadt sc->sc_isize, 933ad942b15Sjsg uchcom_intr, USBD_DEFAULT_INTERVAL); 934ad942b15Sjsg if (err) { 935ad942b15Sjsg printf("%s: cannot open interrupt pipe: %s\n", 936ad942b15Sjsg sc->sc_dev.dv_xname, 937ad942b15Sjsg usbd_errstr(err)); 938ad942b15Sjsg return EIO; 939ad942b15Sjsg } 940ad942b15Sjsg } 941ad942b15Sjsg return 0; 942ad942b15Sjsg } 943ad942b15Sjsg 944ad942b15Sjsg void 945aaf33dadSjsg uchcom_close_intr_pipe(struct uchcom_softc *sc) 946ad942b15Sjsg { 947ad942b15Sjsg usbd_status err; 948ad942b15Sjsg 949ad942b15Sjsg if (sc->sc_intr_pipe != NULL) { 950ad942b15Sjsg err = usbd_close_pipe(sc->sc_intr_pipe); 951ad942b15Sjsg if (err) 952ad942b15Sjsg printf("%s: close interrupt pipe failed: %s\n", 953ad942b15Sjsg sc->sc_dev.dv_xname, usbd_errstr(err)); 954234dfda1Sderaadt free(sc->sc_intr_buf, M_USBDEV, sc->sc_isize); 955ad942b15Sjsg sc->sc_intr_pipe = NULL; 956ad942b15Sjsg } 957ad942b15Sjsg } 958ad942b15Sjsg 959ad942b15Sjsg 960ad942b15Sjsg /* ---------------------------------------------------------------------- 961ad942b15Sjsg * methods for ucom 962ad942b15Sjsg */ 963ad942b15Sjsg void 964ad942b15Sjsg uchcom_get_status(void *arg, int portno, u_char *rlsr, u_char *rmsr) 965ad942b15Sjsg { 966ad942b15Sjsg struct uchcom_softc *sc = arg; 967ad942b15Sjsg 968cbb8e2d4Spirofti if (usbd_is_dying(sc->sc_udev)) 969ad942b15Sjsg return; 970ad942b15Sjsg 971ad942b15Sjsg *rlsr = sc->sc_lsr; 972ad942b15Sjsg *rmsr = sc->sc_msr; 973ad942b15Sjsg } 974ad942b15Sjsg 975ad942b15Sjsg void 976ad942b15Sjsg uchcom_set(void *arg, int portno, int reg, int onoff) 977ad942b15Sjsg { 978ad942b15Sjsg struct uchcom_softc *sc = arg; 979ad942b15Sjsg 980cbb8e2d4Spirofti if (usbd_is_dying(sc->sc_udev)) 981ad942b15Sjsg return; 982ad942b15Sjsg 983ad942b15Sjsg switch (reg) { 984ad942b15Sjsg case UCOM_SET_DTR: 985ad942b15Sjsg sc->sc_dtr = !!onoff; 986aaf33dadSjsg uchcom_set_dtrrts(sc, sc->sc_dtr, sc->sc_rts); 987ad942b15Sjsg break; 988ad942b15Sjsg case UCOM_SET_RTS: 989ad942b15Sjsg sc->sc_rts = !!onoff; 990aaf33dadSjsg uchcom_set_dtrrts(sc, sc->sc_dtr, sc->sc_rts); 991ad942b15Sjsg break; 992ad942b15Sjsg case UCOM_SET_BREAK: 99335f382c9Skevlo if (sc->sc_type == UCHCOM_TYPE_CH343) 99435f382c9Skevlo uchcom_set_break_ch343(sc, onoff); 99535f382c9Skevlo else 996aaf33dadSjsg uchcom_set_break(sc, onoff); 997ad942b15Sjsg break; 998ad942b15Sjsg } 999ad942b15Sjsg } 1000ad942b15Sjsg 1001ad942b15Sjsg int 1002ad942b15Sjsg uchcom_param(void *arg, int portno, struct termios *t) 1003ad942b15Sjsg { 1004ad942b15Sjsg struct uchcom_softc *sc = arg; 100535f382c9Skevlo uint16_t val = 0; 1006ad942b15Sjsg int ret; 1007ad942b15Sjsg 1008cbb8e2d4Spirofti if (usbd_is_dying(sc->sc_udev)) 1009ad942b15Sjsg return 0; 1010ad942b15Sjsg 101135f382c9Skevlo ret = uchcom_set_line_control(sc, t->c_cflag, &val); 1012ad942b15Sjsg if (ret) 1013ad942b15Sjsg return ret; 1014ad942b15Sjsg 101535f382c9Skevlo if (sc->sc_type == UCHCOM_TYPE_CH343) 101635f382c9Skevlo ret = uchcom_set_dte_rate_ch343(sc, t->c_ospeed, val); 101735f382c9Skevlo else 1018aaf33dadSjsg ret = uchcom_set_dte_rate(sc, t->c_ospeed); 1019ad942b15Sjsg if (ret) 1020ad942b15Sjsg return ret; 1021ad942b15Sjsg 1022ad942b15Sjsg return 0; 1023ad942b15Sjsg } 1024ad942b15Sjsg 1025ad942b15Sjsg int 1026ad942b15Sjsg uchcom_open(void *arg, int portno) 1027ad942b15Sjsg { 1028ad942b15Sjsg int ret; 1029ad942b15Sjsg struct uchcom_softc *sc = arg; 1030ad942b15Sjsg 1031cbb8e2d4Spirofti if (usbd_is_dying(sc->sc_udev)) 1032ad942b15Sjsg return EIO; 1033ad942b15Sjsg 1034aaf33dadSjsg ret = uchcom_setup_intr_pipe(sc); 1035ad942b15Sjsg if (ret) 1036ad942b15Sjsg return ret; 1037ad942b15Sjsg 103835f382c9Skevlo ret = uchcom_update_version(sc); 103935f382c9Skevlo if (ret) 104035f382c9Skevlo return ret; 104135f382c9Skevlo 104235f382c9Skevlo if (sc->sc_type == UCHCOM_TYPE_CH343) 104335f382c9Skevlo ret = uchcom_update_status(sc); 104435f382c9Skevlo else 1045aaf33dadSjsg ret = uchcom_setup_comm(sc); 1046ad942b15Sjsg if (ret) 1047ad942b15Sjsg return ret; 1048ad942b15Sjsg 104935f382c9Skevlo sc->sc_dtr = sc->sc_rts = 1; 105035f382c9Skevlo ret = uchcom_set_dtrrts(sc, sc->sc_dtr, sc->sc_rts); 105135f382c9Skevlo if (ret) 105235f382c9Skevlo return ret; 105335f382c9Skevlo 1054ad942b15Sjsg return 0; 1055ad942b15Sjsg } 1056ad942b15Sjsg 1057ad942b15Sjsg void 1058ad942b15Sjsg uchcom_close(void *arg, int portno) 1059ad942b15Sjsg { 1060ad942b15Sjsg struct uchcom_softc *sc = arg; 1061ad942b15Sjsg 1062aaf33dadSjsg uchcom_close_intr_pipe(sc); 1063ad942b15Sjsg } 1064ad942b15Sjsg 1065ad942b15Sjsg 1066ad942b15Sjsg /* ---------------------------------------------------------------------- 1067ad942b15Sjsg * callback when the modem status is changed. 1068ad942b15Sjsg */ 1069ad942b15Sjsg void 1070ab0b1be7Smglocker uchcom_intr(struct usbd_xfer *xfer, void *priv, usbd_status status) 1071ad942b15Sjsg { 1072ad942b15Sjsg struct uchcom_softc *sc = priv; 1073ad942b15Sjsg u_char *buf = sc->sc_intr_buf; 107435f382c9Skevlo uint32_t intrstat; 1075ad942b15Sjsg 1076cbb8e2d4Spirofti if (usbd_is_dying(sc->sc_udev)) 1077ad942b15Sjsg return; 1078ad942b15Sjsg 1079ad942b15Sjsg if (status != USBD_NORMAL_COMPLETION) { 1080ad942b15Sjsg if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) 1081ad942b15Sjsg return; 1082ad942b15Sjsg 1083ad942b15Sjsg DPRINTF(("%s: abnormal status: %s\n", 1084ad942b15Sjsg sc->sc_dev.dv_xname, usbd_errstr(status))); 1085ad942b15Sjsg usbd_clear_endpoint_stall_async(sc->sc_intr_pipe); 1086ad942b15Sjsg return; 1087ad942b15Sjsg } 1088ad942b15Sjsg DPRINTF(("%s: intr: 0x%02X 0x%02X 0x%02X 0x%02X " 1089ad942b15Sjsg "0x%02X 0x%02X 0x%02X 0x%02X\n", 1090ad942b15Sjsg sc->sc_dev.dv_xname, 1091ad942b15Sjsg (unsigned)buf[0], (unsigned)buf[1], 1092ad942b15Sjsg (unsigned)buf[2], (unsigned)buf[3], 1093ad942b15Sjsg (unsigned)buf[4], (unsigned)buf[5], 1094ad942b15Sjsg (unsigned)buf[6], (unsigned)buf[7])); 1095ad942b15Sjsg 109635f382c9Skevlo intrstat = (sc->sc_type == UCHCOM_TYPE_CH343) ? 109735f382c9Skevlo xfer->actlen - 1 : UCHCOM_INTR_STAT1; 109835f382c9Skevlo 109935f382c9Skevlo uchcom_convert_status(sc, buf[intrstat]); 1100ad942b15Sjsg ucom_status_change((struct ucom_softc *) sc->sc_subdev); 1101ad942b15Sjsg } 1102