uchcom.c revision 190581
1153486Sphk/* $NetBSD: uchcom.c,v 1.1 2007/09/03 17:57:37 tshiozak Exp $ */ 2153486Sphk 3153486Sphk/*- 4153486Sphk * Copyright (c) 2007, Takanori Watanabe 5153486Sphk * All rights reserved. 6153486Sphk * 7153486Sphk * Redistribution and use in source and binary forms, with or without 8153486Sphk * modification, are permitted provided that the following conditions 9153486Sphk * are met: 10153486Sphk * 1. Redistributions of source code must retain the above copyright 11153486Sphk * notice, this list of conditions and the following disclaimer. 12153486Sphk * 2. Redistributions in binary form must reproduce the above copyright 13153486Sphk * notice, this list of conditions and the following disclaimer in the 14153486Sphk * documentation and/or other materials provided with the distribution. 15153486Sphk * 16153486Sphk * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17153486Sphk * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18153486Sphk * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19153486Sphk * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20153486Sphk * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21153486Sphk * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22153486Sphk * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23153486Sphk * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24153486Sphk * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25153486Sphk * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26153486Sphk * SUCH DAMAGE. 27153486Sphk */ 28153486Sphk 29153486Sphk/* 30153486Sphk * Copyright (c) 2007 The NetBSD Foundation, Inc. 31153486Sphk * All rights reserved. 32153486Sphk * 33153486Sphk * This code is derived from software contributed to The NetBSD Foundation 34153486Sphk * by Takuya SHIOZAKI (tshiozak@netbsd.org). 35153486Sphk * 36153486Sphk * Redistribution and use in source and binary forms, with or without 37153486Sphk * modification, are permitted provided that the following conditions 38153486Sphk * are met: 39153486Sphk * 1. Redistributions of source code must retain the above copyright 40153486Sphk * notice, this list of conditions and the following disclaimer. 41153486Sphk * 2. Redistributions in binary form must reproduce the above copyright 42153486Sphk * notice, this list of conditions and the following disclaimer in the 43153486Sphk * documentation and/or other materials provided with the distribution. 44153486Sphk * 3. All advertising materials mentioning features or use of this software 45153486Sphk * must display the following acknowledgement: 46153486Sphk * This product includes software developed by the NetBSD 47153486Sphk * Foundation, Inc. and its contributors. 48153486Sphk * 4. Neither the name of The NetBSD Foundation nor the names of its 49153486Sphk * contributors may be used to endorse or promote products derived 50153486Sphk * from this software without specific prior written permission. 51153486Sphk * 52153486Sphk * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 53153486Sphk * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 54153486Sphk * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 55153486Sphk * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 56153486Sphk * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 57153486Sphk * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 58153486Sphk * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 59153486Sphk * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 60153486Sphk * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 61153486Sphk * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 62153486Sphk * POSSIBILITY OF SUCH DAMAGE. 63153486Sphk */ 64153486Sphk 65153486Sphk#include <sys/cdefs.h> 66153486Sphk__FBSDID("$FreeBSD: head/sys/dev/usb/serial/uchcom.c 190581 2009-03-30 22:18:38Z mav $"); 67153486Sphk 68153486Sphk/* 69153486Sphk * driver for WinChipHead CH341/340, the worst USB-serial chip in the world. 70153486Sphk */ 71153486Sphk 72153486Sphk#include "usbdevs.h" 73153486Sphk#include <dev/usb/usb.h> 74153486Sphk#include <dev/usb/usb_mfunc.h> 75153486Sphk#include <dev/usb/usb_error.h> 76153486Sphk#include <dev/usb/usb_cdc.h> 77153486Sphk#include <dev/usb/usb_ioctl.h> 78153486Sphk 79153486Sphk#define USB_DEBUG_VAR uchcom_debug 80153486Sphk 81153486Sphk#include <dev/usb/usb_core.h> 82153486Sphk#include <dev/usb/usb_debug.h> 83153486Sphk#include <dev/usb/usb_process.h> 84153486Sphk#include <dev/usb/usb_request.h> 85153486Sphk#include <dev/usb/usb_lookup.h> 86153486Sphk#include <dev/usb/usb_util.h> 87153486Sphk#include <dev/usb/usb_busdma.h> 88153486Sphk 89153486Sphk#include <dev/usb/serial/usb_serial.h> 90153486Sphk 91153486Sphk#if USB_DEBUG 92153486Sphkstatic int uchcom_debug = 0; 93153486Sphk 94153486SphkSYSCTL_NODE(_hw_usb2, OID_AUTO, uchcom, CTLFLAG_RW, 0, "USB uchcom"); 95153486SphkSYSCTL_INT(_hw_usb2_uchcom, OID_AUTO, debug, CTLFLAG_RW, 96153486Sphk &uchcom_debug, 0, "uchcom debug level"); 97153486Sphk#endif 98153486Sphk 99153486Sphk#define UCHCOM_IFACE_INDEX 0 100153486Sphk#define UCHCOM_CONFIG_INDEX 0 101153486Sphk 102153486Sphk#define UCHCOM_REV_CH340 0x0250 103153486Sphk#define UCHCOM_INPUT_BUF_SIZE 8 104153486Sphk 105153486Sphk#define UCHCOM_REQ_GET_VERSION 0x5F 106153486Sphk#define UCHCOM_REQ_READ_REG 0x95 107153486Sphk#define UCHCOM_REQ_WRITE_REG 0x9A 108153486Sphk#define UCHCOM_REQ_RESET 0xA1 109153486Sphk#define UCHCOM_REQ_SET_DTRRTS 0xA4 110153486Sphk 111153486Sphk#define UCHCOM_REG_STAT1 0x06 112153486Sphk#define UCHCOM_REG_STAT2 0x07 113153486Sphk#define UCHCOM_REG_BPS_PRE 0x12 114153486Sphk#define UCHCOM_REG_BPS_DIV 0x13 115153486Sphk#define UCHCOM_REG_BPS_MOD 0x14 116153486Sphk#define UCHCOM_REG_BPS_PAD 0x0F 117153486Sphk#define UCHCOM_REG_BREAK1 0x05 118153486Sphk#define UCHCOM_REG_BREAK2 0x18 119153486Sphk#define UCHCOM_REG_LCR1 0x18 120153486Sphk#define UCHCOM_REG_LCR2 0x25 121153486Sphk 122153486Sphk#define UCHCOM_VER_20 0x20 123153486Sphk 124153486Sphk#define UCHCOM_BASE_UNKNOWN 0 125153486Sphk#define UCHCOM_BPS_MOD_BASE 20000000 126153486Sphk#define UCHCOM_BPS_MOD_BASE_OFS 1100 127153486Sphk 128153486Sphk#define UCHCOM_DTR_MASK 0x20 129153486Sphk#define UCHCOM_RTS_MASK 0x40 130153486Sphk 131153486Sphk#define UCHCOM_BRK1_MASK 0x01 132153486Sphk#define UCHCOM_BRK2_MASK 0x40 133153486Sphk 134153486Sphk#define UCHCOM_LCR1_MASK 0xAF 135153486Sphk#define UCHCOM_LCR2_MASK 0x07 136153486Sphk#define UCHCOM_LCR1_PARENB 0x80 137153486Sphk#define UCHCOM_LCR2_PAREVEN 0x07 138153486Sphk#define UCHCOM_LCR2_PARODD 0x06 139153486Sphk#define UCHCOM_LCR2_PARMARK 0x05 140153486Sphk#define UCHCOM_LCR2_PARSPACE 0x04 141153486Sphk 142153486Sphk#define UCHCOM_INTR_STAT1 0x02 143153486Sphk#define UCHCOM_INTR_STAT2 0x03 144153486Sphk#define UCHCOM_INTR_LEAST 4 145153486Sphk 146153486Sphk#define UCHCOM_BULK_BUF_SIZE 1024 /* bytes */ 147153486Sphk 148153486Sphkenum { 149153486Sphk UCHCOM_BULK_DT_WR, 150153486Sphk UCHCOM_BULK_DT_RD, 151153486Sphk UCHCOM_INTR_DT_RD, 152153486Sphk UCHCOM_N_TRANSFER, 153153486Sphk}; 154153486Sphk 155153486Sphkstruct uchcom_softc { 156153486Sphk struct usb2_com_super_softc sc_super_ucom; 157153486Sphk struct usb2_com_softc sc_ucom; 158153486Sphk 159153486Sphk struct usb2_xfer *sc_xfer[UCHCOM_N_TRANSFER]; 160153486Sphk struct usb2_device *sc_udev; 161153486Sphk struct mtx sc_mtx; 162153486Sphk 163153486Sphk uint8_t sc_dtr; /* local copy */ 164153486Sphk uint8_t sc_rts; /* local copy */ 165153486Sphk uint8_t sc_version; 166153486Sphk uint8_t sc_msr; 167153486Sphk uint8_t sc_lsr; /* local status register */ 168153486Sphk}; 169153486Sphk 170153486Sphkstruct uchcom_divider { 171153486Sphk uint8_t dv_prescaler; 172153486Sphk uint8_t dv_div; 173153486Sphk uint8_t dv_mod; 174153486Sphk}; 175153486Sphk 176153486Sphkstruct uchcom_divider_record { 177153486Sphk uint32_t dvr_high; 178153486Sphk uint32_t dvr_low; 179153486Sphk uint32_t dvr_base_clock; 180153486Sphk struct uchcom_divider dvr_divider; 181153486Sphk}; 182153486Sphk 183153486Sphkstatic const struct uchcom_divider_record dividers[] = 184153486Sphk{ 185153486Sphk {307200, 307200, UCHCOM_BASE_UNKNOWN, {7, 0xD9, 0}}, 186153486Sphk {921600, 921600, UCHCOM_BASE_UNKNOWN, {7, 0xF3, 0}}, 187153486Sphk {2999999, 23530, 6000000, {3, 0, 0}}, 188153486Sphk {23529, 2942, 750000, {2, 0, 0}}, 189153486Sphk {2941, 368, 93750, {1, 0, 0}}, 190153486Sphk {367, 1, 11719, {0, 0, 0}}, 191153486Sphk}; 192153486Sphk 193153486Sphk#define NUM_DIVIDERS (sizeof (dividers) / sizeof (dividers[0])) 194153486Sphk 195153486Sphkstatic const struct usb2_device_id uchcom_devs[] = { 196153486Sphk {USB_VPI(USB_VENDOR_WCH, USB_PRODUCT_WCH_CH341SER, 0)}, 197153486Sphk}; 198153486Sphk 199153486Sphk/* protypes */ 200153486Sphk 201153486Sphkstatic int uchcom_pre_param(struct usb2_com_softc *, struct termios *); 202153486Sphkstatic void uchcom_cfg_get_status(struct usb2_com_softc *, uint8_t *, 203153486Sphk uint8_t *); 204153486Sphkstatic void uchcom_cfg_param(struct usb2_com_softc *, struct termios *); 205153486Sphkstatic void uchcom_cfg_set_break(struct usb2_com_softc *, uint8_t); 206153486Sphkstatic void uchcom_cfg_set_dtr(struct usb2_com_softc *, uint8_t); 207153486Sphkstatic void uchcom_cfg_set_rts(struct usb2_com_softc *, uint8_t); 208153486Sphkstatic void uchcom_start_read(struct usb2_com_softc *); 209153486Sphkstatic void uchcom_start_write(struct usb2_com_softc *); 210153486Sphkstatic void uchcom_stop_read(struct usb2_com_softc *); 211153486Sphkstatic void uchcom_stop_write(struct usb2_com_softc *); 212153486Sphkstatic void uchcom_update_version(struct uchcom_softc *); 213153486Sphkstatic void uchcom_convert_status(struct uchcom_softc *, uint8_t); 214153486Sphkstatic void uchcom_update_status(struct uchcom_softc *); 215153486Sphkstatic void uchcom_set_dtrrts(struct uchcom_softc *); 216153486Sphkstatic int uchcom_calc_divider_settings(struct uchcom_divider *, uint32_t); 217153486Sphkstatic void uchcom_set_dte_rate(struct uchcom_softc *, uint32_t); 218153486Sphkstatic void uchcom_set_line_control(struct uchcom_softc *, tcflag_t); 219153486Sphkstatic void uchcom_clear_chip(struct uchcom_softc *); 220153486Sphkstatic void uchcom_reset_chip(struct uchcom_softc *); 221153486Sphk 222153486Sphkstatic device_probe_t uchcom_probe; 223153486Sphkstatic device_attach_t uchcom_attach; 224153486Sphkstatic device_detach_t uchcom_detach; 225153486Sphk 226153486Sphkstatic usb2_callback_t uchcom_intr_callback; 227153486Sphkstatic usb2_callback_t uchcom_write_callback; 228153486Sphkstatic usb2_callback_t uchcom_read_callback; 229153486Sphk 230153486Sphkstatic const struct usb2_config uchcom_config_data[UCHCOM_N_TRANSFER] = { 231153486Sphk 232153486Sphk [UCHCOM_BULK_DT_WR] = { 233153486Sphk .type = UE_BULK, 234153486Sphk .endpoint = UE_ADDR_ANY, 235153486Sphk .direction = UE_DIR_OUT, 236153486Sphk .mh.bufsize = UCHCOM_BULK_BUF_SIZE, 237153486Sphk .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,}, 238153486Sphk .mh.callback = &uchcom_write_callback, 239153486Sphk }, 240153486Sphk 241153486Sphk [UCHCOM_BULK_DT_RD] = { 242153486Sphk .type = UE_BULK, 243153486Sphk .endpoint = UE_ADDR_ANY, 244153486Sphk .direction = UE_DIR_IN, 245153486Sphk .mh.bufsize = UCHCOM_BULK_BUF_SIZE, 246153486Sphk .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, 247153486Sphk .mh.callback = &uchcom_read_callback, 248153486Sphk }, 249153486Sphk 250153486Sphk [UCHCOM_INTR_DT_RD] = { 251153486Sphk .type = UE_INTERRUPT, 252153486Sphk .endpoint = UE_ADDR_ANY, 253153486Sphk .direction = UE_DIR_IN, 254153486Sphk .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, 255153486Sphk .mh.bufsize = 0, /* use wMaxPacketSize */ 256153486Sphk .mh.callback = &uchcom_intr_callback, 257153486Sphk }, 258153486Sphk}; 259153486Sphk 260153486Sphkstruct usb2_com_callback uchcom_callback = { 261153486Sphk .usb2_com_cfg_get_status = &uchcom_cfg_get_status, 262153486Sphk .usb2_com_cfg_set_dtr = &uchcom_cfg_set_dtr, 263153486Sphk .usb2_com_cfg_set_rts = &uchcom_cfg_set_rts, 264153486Sphk .usb2_com_cfg_set_break = &uchcom_cfg_set_break, 265153486Sphk .usb2_com_cfg_param = &uchcom_cfg_param, 266153486Sphk .usb2_com_pre_param = &uchcom_pre_param, 267153486Sphk .usb2_com_start_read = &uchcom_start_read, 268153486Sphk .usb2_com_stop_read = &uchcom_stop_read, 269153486Sphk .usb2_com_start_write = &uchcom_start_write, 270153486Sphk .usb2_com_stop_write = &uchcom_stop_write, 271153486Sphk}; 272153486Sphk 273153486Sphk/* ---------------------------------------------------------------------- 274153486Sphk * driver entry points 275153486Sphk */ 276153486Sphk 277153486Sphkstatic int 278153486Sphkuchcom_probe(device_t dev) 279153486Sphk{ 280153486Sphk struct usb2_attach_arg *uaa = device_get_ivars(dev); 281153486Sphk 282153486Sphk DPRINTFN(11, "\n"); 283153486Sphk 284153486Sphk if (uaa->usb2_mode != USB_MODE_HOST) { 285153486Sphk return (ENXIO); 286153486Sphk } 287153486Sphk if (uaa->info.bConfigIndex != UCHCOM_CONFIG_INDEX) { 288153486Sphk return (ENXIO); 289153486Sphk } 290153486Sphk if (uaa->info.bIfaceIndex != UCHCOM_IFACE_INDEX) { 291153486Sphk return (ENXIO); 292153486Sphk } 293153486Sphk return (usb2_lookup_id_by_uaa(uchcom_devs, sizeof(uchcom_devs), uaa)); 294153486Sphk} 295153486Sphk 296153486Sphkstatic int 297153486Sphkuchcom_attach(device_t dev) 298153486Sphk{ 299153486Sphk struct uchcom_softc *sc = device_get_softc(dev); 300153486Sphk struct usb2_attach_arg *uaa = device_get_ivars(dev); 301153486Sphk int error; 302153486Sphk uint8_t iface_index; 303153486Sphk 304153486Sphk DPRINTFN(11, "\n"); 305153486Sphk 306153486Sphk device_set_usb2_desc(dev); 307153486Sphk mtx_init(&sc->sc_mtx, "uchcom", NULL, MTX_DEF); 308153486Sphk 309153486Sphk sc->sc_udev = uaa->device; 310153486Sphk 311153486Sphk switch (uaa->info.bcdDevice) { 312153486Sphk case UCHCOM_REV_CH340: 313153486Sphk device_printf(dev, "CH340 detected\n"); 314153486Sphk break; 315153486Sphk default: 316153486Sphk device_printf(dev, "CH341 detected\n"); 317153486Sphk break; 318153486Sphk } 319153486Sphk 320153486Sphk iface_index = UCHCOM_IFACE_INDEX; 321153486Sphk error = usb2_transfer_setup(uaa->device, 322153486Sphk &iface_index, sc->sc_xfer, uchcom_config_data, 323153486Sphk UCHCOM_N_TRANSFER, sc, &sc->sc_mtx); 324153486Sphk 325153486Sphk if (error) { 326153486Sphk DPRINTF("one or more missing USB endpoints, " 327153486Sphk "error=%s\n", usb2_errstr(error)); 328153486Sphk goto detach; 329153486Sphk } 330153486Sphk /* 331153486Sphk * Do the initialization during attach so that the system does not 332153486Sphk * sleep during open: 333153486Sphk */ 334153486Sphk uchcom_update_version(sc); 335153486Sphk uchcom_clear_chip(sc); 336153486Sphk uchcom_reset_chip(sc); 337153641Scognet uchcom_update_status(sc); 338153641Scognet 339153486Sphk sc->sc_dtr = 1; 340153486Sphk sc->sc_rts = 1; 341153486Sphk 342153486Sphk /* clear stall at first run */ 343153486Sphk mtx_lock(&sc->sc_mtx); 344153486Sphk usb2_transfer_set_stall(sc->sc_xfer[UCHCOM_BULK_DT_WR]); 345153486Sphk usb2_transfer_set_stall(sc->sc_xfer[UCHCOM_BULK_DT_RD]); 346153486Sphk mtx_unlock(&sc->sc_mtx); 347153486Sphk 348153486Sphk error = usb2_com_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc, 349153486Sphk &uchcom_callback, &sc->sc_mtx); 350153486Sphk if (error) { 351153486Sphk goto detach; 352153486Sphk } 353153486Sphk return (0); 354153486Sphk 355153486Sphkdetach: 356153486Sphk uchcom_detach(dev); 357153486Sphk return (ENXIO); 358153486Sphk} 359153486Sphk 360153486Sphkstatic int 361153486Sphkuchcom_detach(device_t dev) 362153486Sphk{ 363153486Sphk struct uchcom_softc *sc = device_get_softc(dev); 364153486Sphk 365153486Sphk DPRINTFN(11, "\n"); 366153486Sphk 367153486Sphk usb2_com_detach(&sc->sc_super_ucom, &sc->sc_ucom, 1); 368153486Sphk usb2_transfer_unsetup(sc->sc_xfer, UCHCOM_N_TRANSFER); 369153486Sphk mtx_destroy(&sc->sc_mtx); 370153486Sphk 371153486Sphk return (0); 372153486Sphk} 373153486Sphk 374153486Sphk/* ---------------------------------------------------------------------- 375153486Sphk * low level i/o 376153486Sphk */ 377153486Sphk 378153486Sphkstatic void 379153486Sphkuchcom_ctrl_write(struct uchcom_softc *sc, uint8_t reqno, 380153486Sphk uint16_t value, uint16_t index) 381153486Sphk{ 382153486Sphk struct usb2_device_request req; 383153486Sphk 384153486Sphk req.bmRequestType = UT_WRITE_VENDOR_DEVICE; 385153486Sphk req.bRequest = reqno; 386153486Sphk USETW(req.wValue, value); 387153486Sphk USETW(req.wIndex, index); 388153486Sphk USETW(req.wLength, 0); 389153486Sphk 390153486Sphk usb2_com_cfg_do_request(sc->sc_udev, 391153486Sphk &sc->sc_ucom, &req, NULL, 0, 1000); 392153486Sphk} 393153486Sphk 394153486Sphkstatic void 395153486Sphkuchcom_ctrl_read(struct uchcom_softc *sc, uint8_t reqno, 396153486Sphk uint16_t value, uint16_t index, void *buf, uint16_t buflen) 397153486Sphk{ 398153486Sphk struct usb2_device_request req; 399153486Sphk 400153486Sphk req.bmRequestType = UT_READ_VENDOR_DEVICE; 401153486Sphk req.bRequest = reqno; 402153486Sphk USETW(req.wValue, value); 403153486Sphk USETW(req.wIndex, index); 404153486Sphk USETW(req.wLength, buflen); 405153486Sphk 406153486Sphk usb2_com_cfg_do_request(sc->sc_udev, 407153486Sphk &sc->sc_ucom, &req, buf, USB_SHORT_XFER_OK, 1000); 408153486Sphk} 409153486Sphk 410153486Sphkstatic void 411153486Sphkuchcom_write_reg(struct uchcom_softc *sc, 412153486Sphk uint8_t reg1, uint8_t val1, uint8_t reg2, uint8_t val2) 413153486Sphk{ 414153486Sphk DPRINTF("0x%02X<-0x%02X, 0x%02X<-0x%02X\n", 415153486Sphk (unsigned)reg1, (unsigned)val1, 416153486Sphk (unsigned)reg2, (unsigned)val2); 417153486Sphk uchcom_ctrl_write( 418153486Sphk sc, UCHCOM_REQ_WRITE_REG, 419153486Sphk reg1 | ((uint16_t)reg2 << 8), val1 | ((uint16_t)val2 << 8)); 420153486Sphk} 421153486Sphk 422153486Sphkstatic void 423153486Sphkuchcom_read_reg(struct uchcom_softc *sc, 424153486Sphk uint8_t reg1, uint8_t *rval1, uint8_t reg2, uint8_t *rval2) 425153486Sphk{ 426153486Sphk uint8_t buf[UCHCOM_INPUT_BUF_SIZE]; 427153486Sphk 428153486Sphk uchcom_ctrl_read( 429153486Sphk sc, UCHCOM_REQ_READ_REG, 430153486Sphk reg1 | ((uint16_t)reg2 << 8), 0, buf, sizeof(buf)); 431153486Sphk 432153486Sphk DPRINTF("0x%02X->0x%02X, 0x%02X->0x%02X\n", 433153486Sphk (unsigned)reg1, (unsigned)buf[0], 434153486Sphk (unsigned)reg2, (unsigned)buf[1]); 435153486Sphk 436153486Sphk if (rval1) 437153486Sphk *rval1 = buf[0]; 438153486Sphk if (rval2) 439153486Sphk *rval2 = buf[1]; 440153486Sphk} 441153486Sphk 442153486Sphkstatic void 443153486Sphkuchcom_get_version(struct uchcom_softc *sc, uint8_t *rver) 444153486Sphk{ 445153486Sphk uint8_t buf[UCHCOM_INPUT_BUF_SIZE]; 446153486Sphk 447153486Sphk uchcom_ctrl_read( 448153486Sphk sc, UCHCOM_REQ_GET_VERSION, 0, 0, buf, sizeof(buf)); 449153486Sphk 450153486Sphk if (rver) 451153486Sphk *rver = buf[0]; 452153486Sphk} 453153486Sphk 454153486Sphkstatic void 455153486Sphkuchcom_get_status(struct uchcom_softc *sc, uint8_t *rval) 456153486Sphk{ 457153486Sphk uchcom_read_reg(sc, UCHCOM_REG_STAT1, rval, UCHCOM_REG_STAT2, NULL); 458153486Sphk} 459153486Sphk 460153486Sphkstatic void 461153486Sphkuchcom_set_dtrrts_10(struct uchcom_softc *sc, uint8_t val) 462153486Sphk{ 463153486Sphk uchcom_write_reg(sc, UCHCOM_REG_STAT1, val, UCHCOM_REG_STAT1, val); 464153486Sphk} 465153486Sphk 466153486Sphkstatic void 467153486Sphkuchcom_set_dtrrts_20(struct uchcom_softc *sc, uint8_t val) 468153486Sphk{ 469153486Sphk uchcom_ctrl_write(sc, UCHCOM_REQ_SET_DTRRTS, val, 0); 470153486Sphk} 471153486Sphk 472 473/* ---------------------------------------------------------------------- 474 * middle layer 475 */ 476 477static void 478uchcom_update_version(struct uchcom_softc *sc) 479{ 480 uchcom_get_version(sc, &sc->sc_version); 481} 482 483static void 484uchcom_convert_status(struct uchcom_softc *sc, uint8_t cur) 485{ 486 sc->sc_dtr = !(cur & UCHCOM_DTR_MASK); 487 sc->sc_rts = !(cur & UCHCOM_RTS_MASK); 488 489 cur = ~cur & 0x0F; 490 sc->sc_msr = (cur << 4) | ((sc->sc_msr >> 4) ^ cur); 491} 492 493static void 494uchcom_update_status(struct uchcom_softc *sc) 495{ 496 uint8_t cur; 497 498 uchcom_get_status(sc, &cur); 499 uchcom_convert_status(sc, cur); 500} 501 502 503static void 504uchcom_set_dtrrts(struct uchcom_softc *sc) 505{ 506 uint8_t val = 0; 507 508 if (sc->sc_dtr) 509 val |= UCHCOM_DTR_MASK; 510 if (sc->sc_rts) 511 val |= UCHCOM_RTS_MASK; 512 513 if (sc->sc_version < UCHCOM_VER_20) 514 uchcom_set_dtrrts_10(sc, ~val); 515 else 516 uchcom_set_dtrrts_20(sc, ~val); 517} 518 519static void 520uchcom_cfg_set_break(struct usb2_com_softc *ucom, uint8_t onoff) 521{ 522 struct uchcom_softc *sc = ucom->sc_parent; 523 uint8_t brk1; 524 uint8_t brk2; 525 526 uchcom_read_reg(sc, UCHCOM_REG_BREAK1, &brk1, UCHCOM_REG_BREAK2, &brk2); 527 if (onoff) { 528 /* on - clear bits */ 529 brk1 &= ~UCHCOM_BRK1_MASK; 530 brk2 &= ~UCHCOM_BRK2_MASK; 531 } else { 532 /* off - set bits */ 533 brk1 |= UCHCOM_BRK1_MASK; 534 brk2 |= UCHCOM_BRK2_MASK; 535 } 536 uchcom_write_reg(sc, UCHCOM_REG_BREAK1, brk1, UCHCOM_REG_BREAK2, brk2); 537} 538 539static int 540uchcom_calc_divider_settings(struct uchcom_divider *dp, uint32_t rate) 541{ 542 const struct uchcom_divider_record *rp; 543 uint32_t div; 544 uint32_t rem; 545 uint32_t mod; 546 uint8_t i; 547 548 /* find record */ 549 for (i = 0; i != NUM_DIVIDERS; i++) { 550 if (dividers[i].dvr_high >= rate && 551 dividers[i].dvr_low <= rate) { 552 rp = ÷rs[i]; 553 goto found; 554 } 555 } 556 return (-1); 557 558found: 559 dp->dv_prescaler = rp->dvr_divider.dv_prescaler; 560 if (rp->dvr_base_clock == UCHCOM_BASE_UNKNOWN) 561 dp->dv_div = rp->dvr_divider.dv_div; 562 else { 563 div = rp->dvr_base_clock / rate; 564 rem = rp->dvr_base_clock % rate; 565 if (div == 0 || div >= 0xFF) 566 return (-1); 567 if ((rem << 1) >= rate) 568 div += 1; 569 dp->dv_div = (uint8_t)-div; 570 } 571 572 mod = UCHCOM_BPS_MOD_BASE / rate + UCHCOM_BPS_MOD_BASE_OFS; 573 mod = mod + mod / 2; 574 575 dp->dv_mod = mod / 0x100; 576 577 return (0); 578} 579 580static void 581uchcom_set_dte_rate(struct uchcom_softc *sc, uint32_t rate) 582{ 583 struct uchcom_divider dv; 584 585 if (uchcom_calc_divider_settings(&dv, rate)) 586 return; 587 588 uchcom_write_reg(sc, 589 UCHCOM_REG_BPS_PRE, dv.dv_prescaler, 590 UCHCOM_REG_BPS_DIV, dv.dv_div); 591 uchcom_write_reg(sc, 592 UCHCOM_REG_BPS_MOD, dv.dv_mod, 593 UCHCOM_REG_BPS_PAD, 0); 594} 595 596static void 597uchcom_set_line_control(struct uchcom_softc *sc, tcflag_t cflag) 598{ 599 uint8_t lcr1 = 0; 600 uint8_t lcr2 = 0; 601 602 uchcom_read_reg(sc, UCHCOM_REG_LCR1, &lcr1, UCHCOM_REG_LCR2, &lcr2); 603 604 lcr1 &= ~UCHCOM_LCR1_MASK; 605 lcr2 &= ~UCHCOM_LCR2_MASK; 606 607 /* 608 * XXX: it is difficult to handle the line control appropriately: 609 * - CS8, !CSTOPB and any parity mode seems ok, but 610 * - the chip doesn't have the function to calculate parity 611 * in !CS8 mode. 612 * - it is unclear that the chip supports CS5,6 mode. 613 * - it is unclear how to handle stop bits. 614 */ 615 616 if (cflag & PARENB) { 617 lcr1 |= UCHCOM_LCR1_PARENB; 618 if (cflag & PARODD) 619 lcr2 |= UCHCOM_LCR2_PARODD; 620 else 621 lcr2 |= UCHCOM_LCR2_PAREVEN; 622 } 623 uchcom_write_reg(sc, UCHCOM_REG_LCR1, lcr1, UCHCOM_REG_LCR2, lcr2); 624} 625 626static void 627uchcom_clear_chip(struct uchcom_softc *sc) 628{ 629 DPRINTF("\n"); 630 uchcom_ctrl_write(sc, UCHCOM_REQ_RESET, 0, 0); 631} 632 633static void 634uchcom_reset_chip(struct uchcom_softc *sc) 635{ 636 uint16_t val; 637 uint16_t idx; 638 uint8_t lcr1; 639 uint8_t lcr2; 640 uint8_t pre; 641 uint8_t div; 642 uint8_t mod; 643 644 uchcom_read_reg(sc, UCHCOM_REG_LCR1, &lcr1, UCHCOM_REG_LCR2, &lcr2); 645 uchcom_read_reg(sc, UCHCOM_REG_BPS_PRE, &pre, UCHCOM_REG_BPS_DIV, &div); 646 uchcom_read_reg(sc, UCHCOM_REG_BPS_MOD, &mod, UCHCOM_REG_BPS_PAD, NULL); 647 648 val = 0; 649 idx = 0; 650 val |= (uint16_t)(lcr1 & 0xF0) << 8; 651 val |= 0x01; 652 val |= (uint16_t)(lcr2 & 0x0F) << 8; 653 val |= 0x02; 654 idx |= pre & 0x07; 655 val |= 0x04; 656 idx |= (uint16_t)div << 8; 657 val |= 0x08; 658 idx |= mod & 0xF8; 659 val |= 0x10; 660 661 DPRINTF("reset v=0x%04X, i=0x%04X\n", val, idx); 662 663 uchcom_ctrl_write(sc, UCHCOM_REQ_RESET, val, idx); 664} 665 666/* ---------------------------------------------------------------------- 667 * methods for ucom 668 */ 669static void 670uchcom_cfg_get_status(struct usb2_com_softc *ucom, uint8_t *lsr, uint8_t *msr) 671{ 672 struct uchcom_softc *sc = ucom->sc_parent; 673 674 DPRINTF("\n"); 675 676 *lsr = sc->sc_lsr; 677 *msr = sc->sc_msr; 678} 679 680static void 681uchcom_cfg_set_dtr(struct usb2_com_softc *ucom, uint8_t onoff) 682{ 683 struct uchcom_softc *sc = ucom->sc_parent; 684 685 DPRINTF("onoff = %d\n", onoff); 686 687 sc->sc_dtr = onoff; 688 uchcom_set_dtrrts(sc); 689} 690 691static void 692uchcom_cfg_set_rts(struct usb2_com_softc *ucom, uint8_t onoff) 693{ 694 struct uchcom_softc *sc = ucom->sc_parent; 695 696 DPRINTF("onoff = %d\n", onoff); 697 698 sc->sc_rts = onoff; 699 uchcom_set_dtrrts(sc); 700} 701 702static int 703uchcom_pre_param(struct usb2_com_softc *ucom, struct termios *t) 704{ 705 struct uchcom_divider dv; 706 707 switch (t->c_cflag & CSIZE) { 708 case CS5: 709 case CS6: 710 case CS7: 711 return (EIO); 712 default: 713 break; 714 } 715 716 if (uchcom_calc_divider_settings(&dv, t->c_ospeed)) { 717 return (EIO); 718 } 719 return (0); /* success */ 720} 721 722static void 723uchcom_cfg_param(struct usb2_com_softc *ucom, struct termios *t) 724{ 725 struct uchcom_softc *sc = ucom->sc_parent; 726 727 uchcom_set_line_control(sc, t->c_cflag); 728 uchcom_set_dte_rate(sc, t->c_ospeed); 729} 730 731static void 732uchcom_start_read(struct usb2_com_softc *ucom) 733{ 734 struct uchcom_softc *sc = ucom->sc_parent; 735 736 /* start interrupt endpoint */ 737 usb2_transfer_start(sc->sc_xfer[UCHCOM_INTR_DT_RD]); 738 739 /* start read endpoint */ 740 usb2_transfer_start(sc->sc_xfer[UCHCOM_BULK_DT_RD]); 741} 742 743static void 744uchcom_stop_read(struct usb2_com_softc *ucom) 745{ 746 struct uchcom_softc *sc = ucom->sc_parent; 747 748 /* stop interrupt endpoint */ 749 usb2_transfer_stop(sc->sc_xfer[UCHCOM_INTR_DT_RD]); 750 751 /* stop read endpoint */ 752 usb2_transfer_stop(sc->sc_xfer[UCHCOM_BULK_DT_RD]); 753} 754 755static void 756uchcom_start_write(struct usb2_com_softc *ucom) 757{ 758 struct uchcom_softc *sc = ucom->sc_parent; 759 760 usb2_transfer_start(sc->sc_xfer[UCHCOM_BULK_DT_WR]); 761} 762 763static void 764uchcom_stop_write(struct usb2_com_softc *ucom) 765{ 766 struct uchcom_softc *sc = ucom->sc_parent; 767 768 usb2_transfer_stop(sc->sc_xfer[UCHCOM_BULK_DT_WR]); 769} 770 771/* ---------------------------------------------------------------------- 772 * callback when the modem status is changed. 773 */ 774static void 775uchcom_intr_callback(struct usb2_xfer *xfer) 776{ 777 struct uchcom_softc *sc = xfer->priv_sc; 778 uint8_t buf[UCHCOM_INTR_LEAST]; 779 780 switch (USB_GET_STATE(xfer)) { 781 case USB_ST_TRANSFERRED: 782 783 DPRINTF("actlen = %u\n", xfer->actlen); 784 785 if (xfer->actlen >= UCHCOM_INTR_LEAST) { 786 usb2_copy_out(xfer->frbuffers, 0, buf, 787 UCHCOM_INTR_LEAST); 788 789 DPRINTF("data = 0x%02X 0x%02X 0x%02X 0x%02X\n", 790 (unsigned)buf[0], (unsigned)buf[1], 791 (unsigned)buf[2], (unsigned)buf[3]); 792 793 uchcom_convert_status(sc, buf[UCHCOM_INTR_STAT1]); 794 usb2_com_status_change(&sc->sc_ucom); 795 } 796 case USB_ST_SETUP: 797tr_setup: 798 xfer->frlengths[0] = xfer->max_data_length; 799 usb2_start_hardware(xfer); 800 break; 801 802 default: /* Error */ 803 if (xfer->error != USB_ERR_CANCELLED) { 804 /* try to clear stall first */ 805 xfer->flags.stall_pipe = 1; 806 goto tr_setup; 807 } 808 break; 809 } 810} 811 812static void 813uchcom_write_callback(struct usb2_xfer *xfer) 814{ 815 struct uchcom_softc *sc = xfer->priv_sc; 816 uint32_t actlen; 817 818 switch (USB_GET_STATE(xfer)) { 819 case USB_ST_SETUP: 820 case USB_ST_TRANSFERRED: 821tr_setup: 822 if (usb2_com_get_data(&sc->sc_ucom, xfer->frbuffers, 0, 823 UCHCOM_BULK_BUF_SIZE, &actlen)) { 824 825 DPRINTF("actlen = %d\n", actlen); 826 827 xfer->frlengths[0] = actlen; 828 usb2_start_hardware(xfer); 829 } 830 return; 831 832 default: /* Error */ 833 if (xfer->error != USB_ERR_CANCELLED) { 834 /* try to clear stall first */ 835 xfer->flags.stall_pipe = 1; 836 goto tr_setup; 837 } 838 return; 839 840 } 841} 842 843static void 844uchcom_read_callback(struct usb2_xfer *xfer) 845{ 846 struct uchcom_softc *sc = xfer->priv_sc; 847 848 switch (USB_GET_STATE(xfer)) { 849 case USB_ST_TRANSFERRED: 850 usb2_com_put_data(&sc->sc_ucom, xfer->frbuffers, 0, xfer->actlen); 851 852 case USB_ST_SETUP: 853tr_setup: 854 xfer->frlengths[0] = xfer->max_data_length; 855 usb2_start_hardware(xfer); 856 return; 857 858 default: /* Error */ 859 if (xfer->error != USB_ERR_CANCELLED) { 860 /* try to clear stall first */ 861 xfer->flags.stall_pipe = 1; 862 goto tr_setup; 863 } 864 return; 865 } 866} 867 868static device_method_t uchcom_methods[] = { 869 /* Device interface */ 870 DEVMETHOD(device_probe, uchcom_probe), 871 DEVMETHOD(device_attach, uchcom_attach), 872 DEVMETHOD(device_detach, uchcom_detach), 873 874 {0, 0} 875}; 876 877static driver_t uchcom_driver = { 878 "ucom", 879 uchcom_methods, 880 sizeof(struct uchcom_softc) 881}; 882 883static devclass_t uchcom_devclass; 884 885DRIVER_MODULE(uchcom, uhub, uchcom_driver, uchcom_devclass, NULL, 0); 886MODULE_DEPEND(uchcom, ucom, 1, 1, 1); 887MODULE_DEPEND(uchcom, usb, 1, 1, 1); 888