1 1.2 simonb /* $NetBSD: uxrcom.c,v 1.2 2020/07/09 13:43:04 simonb Exp $ */ 2 1.1 simonb /* $OpenBSD: uxrcom.c,v 1.1 2019/03/27 22:08:51 kettenis Exp $ */ 3 1.1 simonb 4 1.1 simonb /* 5 1.1 simonb * Copyright (c) 1998, 2020 The NetBSD Foundation, Inc. 6 1.1 simonb * All rights reserved. 7 1.1 simonb * 8 1.1 simonb * This code is derived from software contributed to The NetBSD Foundation 9 1.1 simonb * by Lennart Augustsson (lennart (at) augustsson.net) at 10 1.1 simonb * Carlstedt Research & Technology and Simon Burge. 11 1.1 simonb * 12 1.1 simonb * Redistribution and use in source and binary forms, with or without 13 1.1 simonb * modification, are permitted provided that the following conditions 14 1.1 simonb * are met: 15 1.1 simonb * 1. Redistributions of source code must retain the above copyright 16 1.1 simonb * notice, this list of conditions and the following disclaimer. 17 1.1 simonb * 2. Redistributions in binary form must reproduce the above copyright 18 1.1 simonb * notice, this list of conditions and the following disclaimer in the 19 1.1 simonb * documentation and/or other materials provided with the distribution. 20 1.1 simonb * 21 1.1 simonb * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 22 1.1 simonb * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 23 1.1 simonb * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 24 1.1 simonb * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 25 1.1 simonb * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 1.1 simonb * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 1.1 simonb * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 1.1 simonb * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 1.1 simonb * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 1.1 simonb * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 1.1 simonb * POSSIBILITY OF SUCH DAMAGE. 32 1.1 simonb */ 33 1.1 simonb 34 1.1 simonb /* 35 1.1 simonb * Copyright (c) 2006 Jonathan Gray <jsg (at) openbsd.org> 36 1.1 simonb * 37 1.1 simonb * Permission to use, copy, modify, and distribute this software for any 38 1.1 simonb * purpose with or without fee is hereby granted, provided that the above 39 1.1 simonb * copyright notice and this permission notice appear in all copies. 40 1.1 simonb * 41 1.1 simonb * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 42 1.1 simonb * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 43 1.1 simonb * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 44 1.1 simonb * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 45 1.1 simonb * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 46 1.1 simonb * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 47 1.1 simonb * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 48 1.1 simonb */ 49 1.1 simonb #include <sys/cdefs.h> 50 1.2 simonb __KERNEL_RCSID(0, "$NetBSD: uxrcom.c,v 1.2 2020/07/09 13:43:04 simonb Exp $"); 51 1.1 simonb 52 1.1 simonb #include <sys/param.h> 53 1.1 simonb #include <sys/systm.h> 54 1.1 simonb #include <sys/kernel.h> 55 1.1 simonb #include <sys/tty.h> 56 1.1 simonb #include <sys/device.h> 57 1.1 simonb 58 1.1 simonb #include <dev/usb/usb.h> 59 1.1 simonb #include <dev/usb/usbcdc.h> 60 1.1 simonb #include <dev/usb/usbdi.h> 61 1.1 simonb #include <dev/usb/usbdi_util.h> 62 1.1 simonb #include <dev/usb/usbdevs.h> 63 1.1 simonb #include <dev/usb/usbhist.h> 64 1.1 simonb 65 1.1 simonb #include <dev/usb/usbdevs.h> 66 1.1 simonb #include <dev/usb/ucomvar.h> 67 1.1 simonb #include <dev/usb/umodemvar.h> 68 1.1 simonb 69 1.1 simonb 70 1.1 simonb #define UXRCOMBUFSZ 64 71 1.1 simonb 72 1.1 simonb /* XXX uxrcomreg.h */ 73 1.1 simonb #define XR_SET_REG 0 74 1.1 simonb #define XR_GET_REGN 1 75 1.1 simonb 76 1.1 simonb #define XR_FLOW_CONTROL 0x000c 77 1.1 simonb #define XR_FLOW_CONTROL_ON 1 78 1.1 simonb #define XR_FLOW_CONTROL_OFF 0 79 1.1 simonb #define XR_TX_BREAK 0x0014 80 1.1 simonb #define XR_TX_BREAK_ON 1 81 1.1 simonb #define XR_TX_BREAK_OFF 0 82 1.1 simonb #define XR_GPIO_SET 0x001d 83 1.1 simonb #define XR_GPIO_CLEAR 0x001e 84 1.1 simonb #define XR_GPIO3 (1 << 3) 85 1.1 simonb #define XR_GPIO5 (1 << 5) 86 1.1 simonb 87 1.1 simonb /* for XR_SET_REG/XR_GET_REGN specify which uart block to use */ 88 1.1 simonb #define XR_UART_BLOCK(sc) (((sc)->sc_ctl_iface_no / 2) << NBBY) 89 1.1 simonb 90 1.1 simonb #ifdef UXRCOM_DEBUG 91 1.1 simonb #define DPRINTF(x) if (uxrcomdebug) printf x 92 1.1 simonb int uxrcomdebug = 0; 93 1.1 simonb #else 94 1.1 simonb #define DPRINTF(x) 95 1.1 simonb #endif 96 1.1 simonb 97 1.1 simonb 98 1.1 simonb static void uxrcom_set(void *, int, int, int); 99 1.1 simonb static int uxrcom_param(void *, int, struct termios *); 100 1.1 simonb static void uxrcom_break(void *, int, int); 101 1.1 simonb 102 1.1 simonb static const struct ucom_methods uxrcom_methods = { 103 1.1 simonb .ucom_get_status = umodem_get_status, 104 1.1 simonb .ucom_set = uxrcom_set, 105 1.1 simonb .ucom_param = uxrcom_param, 106 1.1 simonb .ucom_ioctl = NULL, /* TODO */ 107 1.1 simonb .ucom_open = umodem_open, 108 1.1 simonb .ucom_close = umodem_close, 109 1.1 simonb }; 110 1.1 simonb 111 1.1 simonb static const struct usb_devno uxrcom_devs[] = { 112 1.1 simonb { USB_VENDOR_EXAR, USB_PRODUCT_EXAR_XR21V1410 }, 113 1.1 simonb { USB_VENDOR_EXAR, USB_PRODUCT_EXAR_XR21V1412 }, 114 1.1 simonb { USB_VENDOR_EXAR, USB_PRODUCT_EXAR_XR21V1414 }, 115 1.1 simonb }; 116 1.1 simonb #define uxrcom_lookup(v, p) usb_lookup(uxrcom_devs, v, p) 117 1.1 simonb 118 1.1 simonb static int uxrcom_match(device_t, cfdata_t, void *); 119 1.1 simonb static void uxrcom_attach(device_t, device_t, void *); 120 1.1 simonb static int uxrcom_detach(device_t, int); 121 1.1 simonb 122 1.1 simonb CFATTACH_DECL_NEW(uxrcom, sizeof(struct umodem_softc), uxrcom_match, 123 1.1 simonb uxrcom_attach, uxrcom_detach, NULL); 124 1.1 simonb 125 1.1 simonb static int 126 1.1 simonb uxrcom_match(device_t parent, cfdata_t match, void *aux) 127 1.1 simonb { 128 1.1 simonb struct usbif_attach_arg *uiaa = aux; 129 1.1 simonb 130 1.1 simonb if (uiaa->uiaa_class != UICLASS_CDC || 131 1.1 simonb uiaa->uiaa_subclass != UISUBCLASS_ABSTRACT_CONTROL_MODEL || 132 1.1 simonb !(uiaa->uiaa_proto == UIPROTO_CDC_NOCLASS || 133 1.1 simonb uiaa->uiaa_proto == UIPROTO_CDC_AT)) 134 1.1 simonb return UMATCH_NONE; 135 1.1 simonb 136 1.1 simonb return uxrcom_lookup(uiaa->uiaa_vendor, uiaa->uiaa_product) != NULL ? 137 1.1 simonb UMATCH_VENDOR_PRODUCT : UMATCH_NONE; 138 1.1 simonb } 139 1.1 simonb 140 1.1 simonb static void 141 1.1 simonb uxrcom_attach(device_t parent, device_t self, void *aux) 142 1.1 simonb { 143 1.1 simonb struct umodem_softc *sc = device_private(self); 144 1.1 simonb struct usbif_attach_arg *uiaa = aux; 145 1.1 simonb struct ucom_attach_args ucaa; 146 1.1 simonb 147 1.1 simonb memset(&ucaa, 0, sizeof(ucaa)); 148 1.1 simonb 149 1.1 simonb ucaa.ucaa_portno = UCOM_UNK_PORTNO; 150 1.1 simonb ucaa.ucaa_methods = &uxrcom_methods; 151 1.1 simonb ucaa.ucaa_info = NULL; 152 1.1 simonb 153 1.1 simonb ucaa.ucaa_ibufsize = UXRCOMBUFSZ; 154 1.1 simonb ucaa.ucaa_obufsize = UXRCOMBUFSZ; 155 1.1 simonb ucaa.ucaa_ibufsizepad = UXRCOMBUFSZ; 156 1.1 simonb 157 1.1 simonb if (!pmf_device_register(self, NULL, NULL)) 158 1.1 simonb aprint_error_dev(self, "couldn't establish power handler"); 159 1.1 simonb 160 1.2 simonb umodem_common_attach(self, sc, uiaa, &ucaa); 161 1.1 simonb } 162 1.1 simonb 163 1.1 simonb static int 164 1.1 simonb uxrcom_detach(device_t self, int flags) 165 1.1 simonb { 166 1.1 simonb struct umodem_softc *sc = device_private(self); 167 1.1 simonb 168 1.1 simonb pmf_device_deregister(self); 169 1.1 simonb 170 1.1 simonb return umodem_common_detach(sc, flags); 171 1.1 simonb } 172 1.1 simonb 173 1.1 simonb static void 174 1.1 simonb uxrcom_set(void *addr, int portno, int reg, int onoff) 175 1.1 simonb { 176 1.1 simonb struct umodem_softc *sc = addr; 177 1.1 simonb usb_device_request_t req; 178 1.1 simonb uint16_t index; 179 1.1 simonb uint8_t value; 180 1.1 simonb 181 1.1 simonb if (sc->sc_dying) 182 1.1 simonb return; 183 1.1 simonb 184 1.1 simonb index = onoff ? XR_GPIO_SET : XR_GPIO_CLEAR; 185 1.1 simonb 186 1.1 simonb switch (reg) { 187 1.1 simonb case UCOM_SET_DTR: 188 1.1 simonb value = XR_GPIO3; 189 1.1 simonb break; 190 1.1 simonb case UCOM_SET_RTS: 191 1.1 simonb value = XR_GPIO5; 192 1.1 simonb break; 193 1.1 simonb case UCOM_SET_BREAK: 194 1.1 simonb uxrcom_break(sc, portno, onoff); 195 1.1 simonb return; 196 1.1 simonb default: 197 1.1 simonb return; 198 1.1 simonb } 199 1.1 simonb req.bmRequestType = UT_WRITE_VENDOR_DEVICE; 200 1.1 simonb req.bRequest = XR_SET_REG; 201 1.1 simonb USETW(req.wValue, value); 202 1.1 simonb USETW(req.wIndex, index | XR_UART_BLOCK(sc)); 203 1.1 simonb USETW(req.wLength, 0); 204 1.1 simonb usbd_do_request(sc->sc_udev, &req, NULL); 205 1.1 simonb } 206 1.1 simonb 207 1.1 simonb static usbd_status 208 1.1 simonb uxrcom_set_line_coding(struct umodem_softc *sc, usb_cdc_line_state_t *state) 209 1.1 simonb { 210 1.1 simonb usb_device_request_t req; 211 1.1 simonb usbd_status err; 212 1.1 simonb 213 1.1 simonb DPRINTF(("%s: rate=%d fmt=%d parity=%d bits=%d\n", __func__, 214 1.1 simonb UGETDW(state->dwDTERate), state->bCharFormat, 215 1.1 simonb state->bParityType, state->bDataBits)); 216 1.1 simonb 217 1.1 simonb if (memcmp(state, &sc->sc_line_state, UCDC_LINE_STATE_LENGTH) == 0) { 218 1.1 simonb DPRINTF(("%s: already set\n", __func__)); 219 1.1 simonb return USBD_NORMAL_COMPLETION; 220 1.1 simonb } 221 1.1 simonb 222 1.1 simonb req.bmRequestType = UT_WRITE_CLASS_INTERFACE; 223 1.1 simonb req.bRequest = UCDC_SET_LINE_CODING; 224 1.1 simonb USETW(req.wValue, 0); 225 1.1 simonb USETW(req.wIndex, sc->sc_ctl_iface_no); 226 1.1 simonb USETW(req.wLength, UCDC_LINE_STATE_LENGTH); 227 1.1 simonb 228 1.1 simonb err = usbd_do_request(sc->sc_udev, &req, state); 229 1.1 simonb if (err) { 230 1.1 simonb DPRINTF(("%s: failed, err=%u\n", __func__, err)); 231 1.1 simonb return err; 232 1.1 simonb } 233 1.1 simonb 234 1.1 simonb sc->sc_line_state = *state; 235 1.1 simonb 236 1.1 simonb return USBD_NORMAL_COMPLETION; 237 1.1 simonb } 238 1.1 simonb 239 1.1 simonb static int 240 1.1 simonb uxrcom_param(void *addr, int portno, struct termios *t) 241 1.1 simonb { 242 1.1 simonb struct umodem_softc *sc = addr; 243 1.1 simonb usb_device_request_t req; 244 1.1 simonb usbd_status err; 245 1.1 simonb usb_cdc_line_state_t ls; 246 1.1 simonb uint8_t flowctrl; 247 1.1 simonb 248 1.1 simonb if (sc->sc_dying) 249 1.1 simonb return EIO; 250 1.1 simonb 251 1.1 simonb /* slowest supported baud rate is 1200 bps, max is 12 Mbps */ 252 1.1 simonb if (t->c_ospeed < 1200 || t->c_ospeed > 12000000) 253 1.1 simonb return (EINVAL); 254 1.1 simonb 255 1.1 simonb USETDW(ls.dwDTERate, t->c_ospeed); 256 1.1 simonb if (ISSET(t->c_cflag, CSTOPB)) 257 1.1 simonb ls.bCharFormat = UCDC_STOP_BIT_2; 258 1.1 simonb else 259 1.1 simonb ls.bCharFormat = UCDC_STOP_BIT_1; 260 1.1 simonb if (ISSET(t->c_cflag, PARENB)) { 261 1.1 simonb if (ISSET(t->c_cflag, PARODD)) 262 1.1 simonb ls.bParityType = UCDC_PARITY_ODD; 263 1.1 simonb else 264 1.1 simonb ls.bParityType = UCDC_PARITY_EVEN; 265 1.1 simonb } else 266 1.1 simonb ls.bParityType = UCDC_PARITY_NONE; 267 1.1 simonb switch (ISSET(t->c_cflag, CSIZE)) { 268 1.1 simonb case CS5: 269 1.1 simonb ls.bDataBits = 5; 270 1.1 simonb break; 271 1.1 simonb case CS6: 272 1.1 simonb ls.bDataBits = 6; 273 1.1 simonb break; 274 1.1 simonb case CS7: 275 1.1 simonb ls.bDataBits = 7; 276 1.1 simonb break; 277 1.1 simonb case CS8: 278 1.1 simonb ls.bDataBits = 8; 279 1.1 simonb break; 280 1.1 simonb } 281 1.1 simonb 282 1.1 simonb err = uxrcom_set_line_coding(sc, &ls); 283 1.1 simonb if (err) { 284 1.1 simonb DPRINTF(("%s: err=%u\n", __func__, err)); 285 1.1 simonb return EIO; 286 1.1 simonb } 287 1.1 simonb 288 1.1 simonb if (ISSET(t->c_cflag, CRTSCTS)) { 289 1.1 simonb /* rts/cts flow ctl */ 290 1.1 simonb flowctrl = XR_FLOW_CONTROL_ON; 291 1.1 simonb } else { 292 1.1 simonb /* disable flow ctl */ 293 1.1 simonb flowctrl = XR_FLOW_CONTROL_OFF; 294 1.1 simonb } 295 1.1 simonb req.bmRequestType = UT_WRITE_VENDOR_DEVICE; 296 1.1 simonb req.bRequest = XR_SET_REG; 297 1.1 simonb USETW(req.wValue, flowctrl); 298 1.1 simonb USETW(req.wIndex, XR_FLOW_CONTROL | XR_UART_BLOCK(sc)); 299 1.1 simonb USETW(req.wLength, 0); 300 1.1 simonb usbd_do_request(sc->sc_udev, &req, NULL); 301 1.1 simonb 302 1.1 simonb return (0); 303 1.1 simonb } 304 1.1 simonb 305 1.1 simonb static void 306 1.1 simonb uxrcom_break(void *addr, int portno, int onoff) 307 1.1 simonb { 308 1.1 simonb struct umodem_softc *sc = addr; 309 1.1 simonb usb_device_request_t req; 310 1.1 simonb uint8_t brk = onoff ? UCDC_BREAK_ON : UCDC_BREAK_OFF; 311 1.1 simonb 312 1.1 simonb DPRINTF(("%s: port=%d onoff=%d\n", __func__, portno, onoff)); 313 1.1 simonb 314 1.1 simonb req.bmRequestType = UT_WRITE_VENDOR_DEVICE; 315 1.1 simonb req.bRequest = XR_SET_REG; 316 1.1 simonb USETW(req.wValue, brk); 317 1.1 simonb USETW(req.wIndex, XR_TX_BREAK | XR_UART_BLOCK(sc)); 318 1.1 simonb USETW(req.wLength, 0); 319 1.1 simonb 320 1.1 simonb (void)usbd_do_request(sc->sc_udev, &req, 0); 321 1.1 simonb } 322