1/* $NetBSD: uchcom.c,v 1.1 2007/09/03 17:57:37 tshiozak Exp $ */ 2 3/*- 4 * Copyright (c) 2007, Takanori Watanabe 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29/* 30 * Copyright (c) 2007 The NetBSD Foundation, Inc. 31 * All rights reserved. 32 * 33 * This code is derived from software contributed to The NetBSD Foundation 34 * by Takuya SHIOZAKI (tshiozak@netbsd.org). 35 * 36 * Redistribution and use in source and binary forms, with or without 37 * modification, are permitted provided that the following conditions 38 * are met: 39 * 1. Redistributions of source code must retain the above copyright 40 * notice, this list of conditions and the following disclaimer. 41 * 2. Redistributions in binary form must reproduce the above copyright 42 * notice, this list of conditions and the following disclaimer in the 43 * documentation and/or other materials provided with the distribution. 44 * 3. All advertising materials mentioning features or use of this software 45 * must display the following acknowledgement: 46 * This product includes software developed by the NetBSD 47 * Foundation, Inc. and its contributors. 48 * 4. Neither the name of The NetBSD Foundation nor the names of its 49 * contributors may be used to endorse or promote products derived 50 * from this software without specific prior written permission. 51 * 52 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 53 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 54 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 55 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 56 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 57 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 58 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 59 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 60 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 61 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 62 * POSSIBILITY OF SUCH DAMAGE. 63 */ 64 65#include <sys/cdefs.h>
|
66__FBSDID("$FreeBSD: head/sys/dev/usb2/serial/uchcom2.c 187970 2009-02-01 00:51:25Z thompsa $");
|
66__FBSDID("$FreeBSD: head/sys/dev/usb2/serial/uchcom2.c 188413 2009-02-09 22:05:25Z thompsa $"); |
67 68/* 69 * driver for WinChipHead CH341/340, the worst USB-serial chip in the world. 70 */ 71 72#include <dev/usb2/include/usb2_devid.h> 73#include <dev/usb2/include/usb2_standard.h> 74#include <dev/usb2/include/usb2_mfunc.h> 75#include <dev/usb2/include/usb2_error.h> 76#include <dev/usb2/include/usb2_cdc.h> 77#include <dev/usb2/include/usb2_ioctl.h> 78 79#define USB_DEBUG_VAR uchcom_debug 80 81#include <dev/usb2/core/usb2_core.h> 82#include <dev/usb2/core/usb2_debug.h> 83#include <dev/usb2/core/usb2_process.h> 84#include <dev/usb2/core/usb2_request.h> 85#include <dev/usb2/core/usb2_lookup.h> 86#include <dev/usb2/core/usb2_util.h> 87#include <dev/usb2/core/usb2_busdma.h> 88 89#include <dev/usb2/serial/usb2_serial.h> 90 91#if USB_DEBUG 92static int uchcom_debug = 0; 93 94SYSCTL_NODE(_hw_usb2, OID_AUTO, uchcom, CTLFLAG_RW, 0, "USB uchcom"); 95SYSCTL_INT(_hw_usb2_uchcom, OID_AUTO, debug, CTLFLAG_RW, 96 &uchcom_debug, 0, "uchcom debug level"); 97#endif 98 99#define UCHCOM_IFACE_INDEX 0 100#define UCHCOM_CONFIG_INDEX 0 101 102#define UCHCOM_REV_CH340 0x0250 103#define UCHCOM_INPUT_BUF_SIZE 8 104 105#define UCHCOM_REQ_GET_VERSION 0x5F 106#define UCHCOM_REQ_READ_REG 0x95 107#define UCHCOM_REQ_WRITE_REG 0x9A 108#define UCHCOM_REQ_RESET 0xA1 109#define UCHCOM_REQ_SET_DTRRTS 0xA4 110 111#define UCHCOM_REG_STAT1 0x06 112#define UCHCOM_REG_STAT2 0x07 113#define UCHCOM_REG_BPS_PRE 0x12 114#define UCHCOM_REG_BPS_DIV 0x13 115#define UCHCOM_REG_BPS_MOD 0x14 116#define UCHCOM_REG_BPS_PAD 0x0F 117#define UCHCOM_REG_BREAK1 0x05 118#define UCHCOM_REG_BREAK2 0x18 119#define UCHCOM_REG_LCR1 0x18 120#define UCHCOM_REG_LCR2 0x25 121 122#define UCHCOM_VER_20 0x20 123 124#define UCHCOM_BASE_UNKNOWN 0 125#define UCHCOM_BPS_MOD_BASE 20000000 126#define UCHCOM_BPS_MOD_BASE_OFS 1100 127 128#define UCHCOM_DTR_MASK 0x20 129#define UCHCOM_RTS_MASK 0x40 130 131#define UCHCOM_BRK1_MASK 0x01 132#define UCHCOM_BRK2_MASK 0x40 133 134#define UCHCOM_LCR1_MASK 0xAF 135#define UCHCOM_LCR2_MASK 0x07 136#define UCHCOM_LCR1_PARENB 0x80 137#define UCHCOM_LCR2_PAREVEN 0x07 138#define UCHCOM_LCR2_PARODD 0x06 139#define UCHCOM_LCR2_PARMARK 0x05 140#define UCHCOM_LCR2_PARSPACE 0x04 141 142#define UCHCOM_INTR_STAT1 0x02 143#define UCHCOM_INTR_STAT2 0x03 144#define UCHCOM_INTR_LEAST 4 145 146#define UCHCOM_BULK_BUF_SIZE 1024 /* bytes */ 147 148enum { 149 UCHCOM_BULK_DT_WR, 150 UCHCOM_BULK_DT_RD,
|
151 UCHCOM_BULK_CS_WR,
152 UCHCOM_BULK_CS_RD,
|
151 UCHCOM_INTR_DT_RD,
|
154 UCHCOM_INTR_CS_RD,
155 UCHCOM_N_TRANSFER = 6,
|
152 UCHCOM_N_TRANSFER, |
153}; 154 155struct uchcom_softc { 156 struct usb2_com_super_softc sc_super_ucom; 157 struct usb2_com_softc sc_ucom; 158 159 struct usb2_xfer *sc_xfer[UCHCOM_N_TRANSFER]; 160 struct usb2_device *sc_udev; 161 162 uint8_t sc_dtr; /* local copy */ 163 uint8_t sc_rts; /* local copy */ 164 uint8_t sc_version; 165 uint8_t sc_msr; 166 uint8_t sc_lsr; /* local status register */
|
170 uint8_t sc_flag;
171#define UCHCOM_FLAG_INTR_STALL 0x01
172#define UCHCOM_FLAG_READ_STALL 0x02
173#define UCHCOM_FLAG_WRITE_STALL 0x04
|
167}; 168 169struct uchcom_divider { 170 uint8_t dv_prescaler; 171 uint8_t dv_div; 172 uint8_t dv_mod; 173}; 174 175struct uchcom_divider_record { 176 uint32_t dvr_high; 177 uint32_t dvr_low; 178 uint32_t dvr_base_clock; 179 struct uchcom_divider dvr_divider; 180}; 181 182static const struct uchcom_divider_record dividers[] = 183{ 184 {307200, 307200, UCHCOM_BASE_UNKNOWN, {7, 0xD9, 0}}, 185 {921600, 921600, UCHCOM_BASE_UNKNOWN, {7, 0xF3, 0}}, 186 {2999999, 23530, 6000000, {3, 0, 0}}, 187 {23529, 2942, 750000, {2, 0, 0}}, 188 {2941, 368, 93750, {1, 0, 0}}, 189 {367, 1, 11719, {0, 0, 0}}, 190}; 191 192#define NUM_DIVIDERS (sizeof (dividers) / sizeof (dividers[0])) 193 194static const struct usb2_device_id uchcom_devs[] = { 195 {USB_VPI(USB_VENDOR_WCH, USB_PRODUCT_WCH_CH341SER, 0)}, 196}; 197 198/* protypes */ 199 200static int uchcom_pre_param(struct usb2_com_softc *, struct termios *); 201static void uchcom_cfg_get_status(struct usb2_com_softc *, uint8_t *, 202 uint8_t *); 203static void uchcom_cfg_param(struct usb2_com_softc *, struct termios *); 204static void uchcom_cfg_set_break(struct usb2_com_softc *, uint8_t); 205static void uchcom_cfg_set_dtr(struct usb2_com_softc *, uint8_t); 206static void uchcom_cfg_set_rts(struct usb2_com_softc *, uint8_t); 207static void uchcom_start_read(struct usb2_com_softc *); 208static void uchcom_start_write(struct usb2_com_softc *); 209static void uchcom_stop_read(struct usb2_com_softc *); 210static void uchcom_stop_write(struct usb2_com_softc *); 211static void uchcom_update_version(struct uchcom_softc *); 212static void uchcom_convert_status(struct uchcom_softc *, uint8_t); 213static void uchcom_update_status(struct uchcom_softc *); 214static void uchcom_set_dtrrts(struct uchcom_softc *); 215static int uchcom_calc_divider_settings(struct uchcom_divider *, uint32_t); 216static void uchcom_set_dte_rate(struct uchcom_softc *, uint32_t); 217static void uchcom_set_line_control(struct uchcom_softc *, tcflag_t); 218static void uchcom_clear_chip(struct uchcom_softc *); 219static void uchcom_reset_chip(struct uchcom_softc *); 220 221static device_probe_t uchcom_probe; 222static device_attach_t uchcom_attach; 223static device_detach_t uchcom_detach; 224 225static usb2_callback_t uchcom_intr_callback;
|
233static usb2_callback_t uchcom_intr_clear_stall_callback;
|
226static usb2_callback_t uchcom_write_callback;
|
235static usb2_callback_t uchcom_write_clear_stall_callback;
|
227static usb2_callback_t uchcom_read_callback;
|
237static usb2_callback_t uchcom_read_clear_stall_callback;
|
228 229static const struct usb2_config uchcom_config_data[UCHCOM_N_TRANSFER] = { 230 231 [UCHCOM_BULK_DT_WR] = { 232 .type = UE_BULK, 233 .endpoint = UE_ADDR_ANY, 234 .direction = UE_DIR_OUT, 235 .mh.bufsize = UCHCOM_BULK_BUF_SIZE, 236 .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,}, 237 .mh.callback = &uchcom_write_callback, 238 }, 239 240 [UCHCOM_BULK_DT_RD] = { 241 .type = UE_BULK, 242 .endpoint = UE_ADDR_ANY, 243 .direction = UE_DIR_IN, 244 .mh.bufsize = UCHCOM_BULK_BUF_SIZE, 245 .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, 246 .mh.callback = &uchcom_read_callback, 247 }, 248
|
259 [UCHCOM_BULK_CS_WR] = {
260 .type = UE_CONTROL,
261 .endpoint = 0x00, /* Control pipe */
262 .direction = UE_DIR_ANY,
263 .mh.bufsize = sizeof(struct usb2_device_request),
264 .mh.callback = &uchcom_write_clear_stall_callback,
265 .mh.timeout = 1000, /* 1 second */
266 .mh.interval = 50, /* 50ms */
267 },
268
269 [UCHCOM_BULK_CS_RD] = {
270 .type = UE_CONTROL,
271 .endpoint = 0x00, /* Control pipe */
272 .direction = UE_DIR_ANY,
273 .mh.bufsize = sizeof(struct usb2_device_request),
274 .mh.callback = &uchcom_read_clear_stall_callback,
275 .mh.timeout = 1000, /* 1 second */
276 .mh.interval = 50, /* 50ms */
277 },
278
|
249 [UCHCOM_INTR_DT_RD] = { 250 .type = UE_INTERRUPT, 251 .endpoint = UE_ADDR_ANY, 252 .direction = UE_DIR_IN, 253 .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, 254 .mh.bufsize = 0, /* use wMaxPacketSize */ 255 .mh.callback = &uchcom_intr_callback, 256 },
|
287
288 [UCHCOM_INTR_CS_RD] = {
289 .type = UE_CONTROL,
290 .endpoint = 0x00, /* Control pipe */
291 .direction = UE_DIR_ANY,
292 .mh.bufsize = sizeof(struct usb2_device_request),
293 .mh.callback = &uchcom_intr_clear_stall_callback,
294 .mh.timeout = 1000, /* 1 second */
295 .mh.interval = 50, /* 50ms */
296 },
|
257}; 258 259struct usb2_com_callback uchcom_callback = { 260 .usb2_com_cfg_get_status = &uchcom_cfg_get_status, 261 .usb2_com_cfg_set_dtr = &uchcom_cfg_set_dtr, 262 .usb2_com_cfg_set_rts = &uchcom_cfg_set_rts, 263 .usb2_com_cfg_set_break = &uchcom_cfg_set_break, 264 .usb2_com_cfg_param = &uchcom_cfg_param, 265 .usb2_com_pre_param = &uchcom_pre_param, 266 .usb2_com_start_read = &uchcom_start_read, 267 .usb2_com_stop_read = &uchcom_stop_read, 268 .usb2_com_start_write = &uchcom_start_write, 269 .usb2_com_stop_write = &uchcom_stop_write, 270}; 271 272/* ---------------------------------------------------------------------- 273 * driver entry points 274 */ 275 276static int 277uchcom_probe(device_t dev) 278{ 279 struct usb2_attach_arg *uaa = device_get_ivars(dev); 280 281 DPRINTFN(11, "\n"); 282 283 if (uaa->usb2_mode != USB_MODE_HOST) { 284 return (ENXIO); 285 } 286 if (uaa->info.bConfigIndex != UCHCOM_CONFIG_INDEX) { 287 return (ENXIO); 288 } 289 if (uaa->info.bIfaceIndex != UCHCOM_IFACE_INDEX) { 290 return (ENXIO); 291 } 292 return (usb2_lookup_id_by_uaa(uchcom_devs, sizeof(uchcom_devs), uaa)); 293} 294 295static int 296uchcom_attach(device_t dev) 297{ 298 struct uchcom_softc *sc = device_get_softc(dev); 299 struct usb2_attach_arg *uaa = device_get_ivars(dev); 300 int error; 301 uint8_t iface_index; 302 303 DPRINTFN(11, "\n"); 304 305 device_set_usb2_desc(dev); 306 307 sc->sc_udev = uaa->device; 308 309 switch (uaa->info.bcdDevice) { 310 case UCHCOM_REV_CH340: 311 device_printf(dev, "CH340 detected\n"); 312 break; 313 default: 314 device_printf(dev, "CH341 detected\n"); 315 break; 316 } 317 318 iface_index = UCHCOM_IFACE_INDEX; 319 error = usb2_transfer_setup(uaa->device, 320 &iface_index, sc->sc_xfer, uchcom_config_data, 321 UCHCOM_N_TRANSFER, sc, &Giant); 322 323 if (error) { 324 DPRINTF("one or more missing USB endpoints, " 325 "error=%s\n", usb2_errstr(error)); 326 goto detach; 327 } 328 /* 329 * Do the initialization during attach so that the system does not 330 * sleep during open: 331 */ 332 uchcom_update_version(sc); 333 uchcom_clear_chip(sc); 334 uchcom_reset_chip(sc); 335 uchcom_update_status(sc); 336 337 sc->sc_dtr = 1; 338 sc->sc_rts = 1; 339 340 /* clear stall at first run */
|
381 sc->sc_flag |= (UCHCOM_FLAG_READ_STALL |
382 UCHCOM_FLAG_WRITE_STALL);
|
341 usb2_transfer_set_stall(sc->sc_xfer[UCHCOM_BULK_DT_WR]); 342 usb2_transfer_set_stall(sc->sc_xfer[UCHCOM_BULK_DT_RD]); |
343 344 error = usb2_com_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc, 345 &uchcom_callback, &Giant); 346 if (error) { 347 goto detach; 348 } 349 return (0); 350 351detach: 352 uchcom_detach(dev); 353 return (ENXIO); 354} 355 356static int 357uchcom_detach(device_t dev) 358{ 359 struct uchcom_softc *sc = device_get_softc(dev); 360 361 DPRINTFN(11, "\n"); 362 363 usb2_com_detach(&sc->sc_super_ucom, &sc->sc_ucom, 1); 364 365 usb2_transfer_unsetup(sc->sc_xfer, UCHCOM_N_TRANSFER); 366 367 return (0); 368} 369 370/* ---------------------------------------------------------------------- 371 * low level i/o 372 */ 373 374static void
|
415uchcom_do_request(struct uchcom_softc *sc,
416 struct usb2_device_request *req, void *data)
417{
418 uint16_t length;
419 uint16_t actlen;
420 usb2_error_t err;
421
422 length = UGETW(req->wLength);
423 actlen = 0;
424
425 if (usb2_com_cfg_is_gone(&sc->sc_ucom)) {
426 goto done;
427 }
428 err = usb2_do_request_flags(sc->sc_udev, &Giant, req,
429 data, USB_SHORT_XFER_OK, &actlen, 1000);
430
431 if (err) {
432 DPRINTFN(0, "device request failed, err=%s "
433 "(ignored)\n", usb2_errstr(err));
434 }
435done:
436 if (length != actlen) {
437 if (req->bmRequestType & UT_READ) {
438 bzero(USB_ADD_BYTES(data, actlen), length - actlen);
439 }
440 }
441}
442
443static void
|
375uchcom_ctrl_write(struct uchcom_softc *sc, uint8_t reqno, 376 uint16_t value, uint16_t index) 377{ 378 struct usb2_device_request req; 379 380 req.bmRequestType = UT_WRITE_VENDOR_DEVICE; 381 req.bRequest = reqno; 382 USETW(req.wValue, value); 383 USETW(req.wIndex, index); 384 USETW(req.wLength, 0); 385
|
455 uchcom_do_request(sc, &req, NULL);
|
386 usb2_com_cfg_do_request(sc->sc_udev, 387 &sc->sc_ucom, &req, NULL, 0, 1000); |
388} 389 390static void 391uchcom_ctrl_read(struct uchcom_softc *sc, uint8_t reqno, 392 uint16_t value, uint16_t index, void *buf, uint16_t buflen) 393{ 394 struct usb2_device_request req; 395 396 req.bmRequestType = UT_READ_VENDOR_DEVICE; 397 req.bRequest = reqno; 398 USETW(req.wValue, value); 399 USETW(req.wIndex, index); 400 USETW(req.wLength, buflen); 401
|
470 uchcom_do_request(sc, &req, buf);
|
402 usb2_com_cfg_do_request(sc->sc_udev, 403 &sc->sc_ucom, &req, buf, USB_SHORT_XFER_OK, 1000); |
404} 405 406static void 407uchcom_write_reg(struct uchcom_softc *sc, 408 uint8_t reg1, uint8_t val1, uint8_t reg2, uint8_t val2) 409{ 410 DPRINTF("0x%02X<-0x%02X, 0x%02X<-0x%02X\n", 411 (unsigned)reg1, (unsigned)val1, 412 (unsigned)reg2, (unsigned)val2); 413 uchcom_ctrl_write( 414 sc, UCHCOM_REQ_WRITE_REG, 415 reg1 | ((uint16_t)reg2 << 8), val1 | ((uint16_t)val2 << 8)); 416} 417 418static void 419uchcom_read_reg(struct uchcom_softc *sc, 420 uint8_t reg1, uint8_t *rval1, uint8_t reg2, uint8_t *rval2) 421{ 422 uint8_t buf[UCHCOM_INPUT_BUF_SIZE]; 423 424 uchcom_ctrl_read( 425 sc, UCHCOM_REQ_READ_REG, 426 reg1 | ((uint16_t)reg2 << 8), 0, buf, sizeof(buf)); 427 428 DPRINTF("0x%02X->0x%02X, 0x%02X->0x%02X\n", 429 (unsigned)reg1, (unsigned)buf[0], 430 (unsigned)reg2, (unsigned)buf[1]); 431 432 if (rval1) 433 *rval1 = buf[0]; 434 if (rval2) 435 *rval2 = buf[1]; 436} 437 438static void 439uchcom_get_version(struct uchcom_softc *sc, uint8_t *rver) 440{ 441 uint8_t buf[UCHCOM_INPUT_BUF_SIZE]; 442 443 uchcom_ctrl_read( 444 sc, UCHCOM_REQ_GET_VERSION, 0, 0, buf, sizeof(buf)); 445 446 if (rver) 447 *rver = buf[0]; 448} 449 450static void 451uchcom_get_status(struct uchcom_softc *sc, uint8_t *rval) 452{ 453 uchcom_read_reg(sc, UCHCOM_REG_STAT1, rval, UCHCOM_REG_STAT2, NULL); 454} 455 456static void 457uchcom_set_dtrrts_10(struct uchcom_softc *sc, uint8_t val) 458{ 459 uchcom_write_reg(sc, UCHCOM_REG_STAT1, val, UCHCOM_REG_STAT1, val); 460} 461 462static void 463uchcom_set_dtrrts_20(struct uchcom_softc *sc, uint8_t val) 464{ 465 uchcom_ctrl_write(sc, UCHCOM_REQ_SET_DTRRTS, val, 0); 466} 467 468 469/* ---------------------------------------------------------------------- 470 * middle layer 471 */ 472 473static void 474uchcom_update_version(struct uchcom_softc *sc) 475{ 476 uchcom_get_version(sc, &sc->sc_version); 477} 478 479static void 480uchcom_convert_status(struct uchcom_softc *sc, uint8_t cur) 481{ 482 sc->sc_dtr = !(cur & UCHCOM_DTR_MASK); 483 sc->sc_rts = !(cur & UCHCOM_RTS_MASK); 484 485 cur = ~cur & 0x0F; 486 sc->sc_msr = (cur << 4) | ((sc->sc_msr >> 4) ^ cur); 487} 488 489static void 490uchcom_update_status(struct uchcom_softc *sc) 491{ 492 uint8_t cur; 493 494 uchcom_get_status(sc, &cur); 495 uchcom_convert_status(sc, cur); 496} 497 498 499static void 500uchcom_set_dtrrts(struct uchcom_softc *sc) 501{ 502 uint8_t val = 0; 503 504 if (sc->sc_dtr) 505 val |= UCHCOM_DTR_MASK; 506 if (sc->sc_rts) 507 val |= UCHCOM_RTS_MASK; 508 509 if (sc->sc_version < UCHCOM_VER_20) 510 uchcom_set_dtrrts_10(sc, ~val); 511 else 512 uchcom_set_dtrrts_20(sc, ~val); 513} 514 515static void 516uchcom_cfg_set_break(struct usb2_com_softc *ucom, uint8_t onoff) 517{ 518 struct uchcom_softc *sc = ucom->sc_parent; 519 uint8_t brk1; 520 uint8_t brk2; 521 522 uchcom_read_reg(sc, UCHCOM_REG_BREAK1, &brk1, UCHCOM_REG_BREAK2, &brk2); 523 if (onoff) { 524 /* on - clear bits */ 525 brk1 &= ~UCHCOM_BRK1_MASK; 526 brk2 &= ~UCHCOM_BRK2_MASK; 527 } else { 528 /* off - set bits */ 529 brk1 |= UCHCOM_BRK1_MASK; 530 brk2 |= UCHCOM_BRK2_MASK; 531 } 532 uchcom_write_reg(sc, UCHCOM_REG_BREAK1, brk1, UCHCOM_REG_BREAK2, brk2); 533} 534 535static int 536uchcom_calc_divider_settings(struct uchcom_divider *dp, uint32_t rate) 537{ 538 const struct uchcom_divider_record *rp; 539 uint32_t div; 540 uint32_t rem; 541 uint32_t mod; 542 uint8_t i; 543 544 /* find record */ 545 for (i = 0; i != NUM_DIVIDERS; i++) { 546 if (dividers[i].dvr_high >= rate && 547 dividers[i].dvr_low <= rate) { 548 rp = ÷rs[i]; 549 goto found; 550 } 551 } 552 return (-1); 553 554found: 555 dp->dv_prescaler = rp->dvr_divider.dv_prescaler; 556 if (rp->dvr_base_clock == UCHCOM_BASE_UNKNOWN) 557 dp->dv_div = rp->dvr_divider.dv_div; 558 else { 559 div = rp->dvr_base_clock / rate; 560 rem = rp->dvr_base_clock % rate; 561 if (div == 0 || div >= 0xFF) 562 return (-1); 563 if ((rem << 1) >= rate) 564 div += 1; 565 dp->dv_div = (uint8_t)-div; 566 } 567 568 mod = UCHCOM_BPS_MOD_BASE / rate + UCHCOM_BPS_MOD_BASE_OFS; 569 mod = mod + mod / 2; 570 571 dp->dv_mod = mod / 0x100; 572 573 return (0); 574} 575 576static void 577uchcom_set_dte_rate(struct uchcom_softc *sc, uint32_t rate) 578{ 579 struct uchcom_divider dv; 580 581 if (uchcom_calc_divider_settings(&dv, rate)) 582 return; 583 584 uchcom_write_reg(sc, 585 UCHCOM_REG_BPS_PRE, dv.dv_prescaler, 586 UCHCOM_REG_BPS_DIV, dv.dv_div); 587 uchcom_write_reg(sc, 588 UCHCOM_REG_BPS_MOD, dv.dv_mod, 589 UCHCOM_REG_BPS_PAD, 0); 590} 591 592static void 593uchcom_set_line_control(struct uchcom_softc *sc, tcflag_t cflag) 594{ 595 uint8_t lcr1 = 0; 596 uint8_t lcr2 = 0; 597 598 uchcom_read_reg(sc, UCHCOM_REG_LCR1, &lcr1, UCHCOM_REG_LCR2, &lcr2); 599 600 lcr1 &= ~UCHCOM_LCR1_MASK; 601 lcr2 &= ~UCHCOM_LCR2_MASK; 602 603 /* 604 * XXX: it is difficult to handle the line control appropriately: 605 * - CS8, !CSTOPB and any parity mode seems ok, but 606 * - the chip doesn't have the function to calculate parity 607 * in !CS8 mode. 608 * - it is unclear that the chip supports CS5,6 mode. 609 * - it is unclear how to handle stop bits. 610 */ 611 612 if (cflag & PARENB) { 613 lcr1 |= UCHCOM_LCR1_PARENB; 614 if (cflag & PARODD) 615 lcr2 |= UCHCOM_LCR2_PARODD; 616 else 617 lcr2 |= UCHCOM_LCR2_PAREVEN; 618 } 619 uchcom_write_reg(sc, UCHCOM_REG_LCR1, lcr1, UCHCOM_REG_LCR2, lcr2); 620} 621 622static void 623uchcom_clear_chip(struct uchcom_softc *sc) 624{ 625 DPRINTF("\n"); 626 uchcom_ctrl_write(sc, UCHCOM_REQ_RESET, 0, 0); 627} 628 629static void 630uchcom_reset_chip(struct uchcom_softc *sc) 631{ 632 uint16_t val; 633 uint16_t idx; 634 uint8_t lcr1; 635 uint8_t lcr2; 636 uint8_t pre; 637 uint8_t div; 638 uint8_t mod; 639 640 uchcom_read_reg(sc, UCHCOM_REG_LCR1, &lcr1, UCHCOM_REG_LCR2, &lcr2); 641 uchcom_read_reg(sc, UCHCOM_REG_BPS_PRE, &pre, UCHCOM_REG_BPS_DIV, &div); 642 uchcom_read_reg(sc, UCHCOM_REG_BPS_MOD, &mod, UCHCOM_REG_BPS_PAD, NULL); 643 644 val = 0; 645 idx = 0; 646 val |= (uint16_t)(lcr1 & 0xF0) << 8; 647 val |= 0x01; 648 val |= (uint16_t)(lcr2 & 0x0F) << 8; 649 val |= 0x02; 650 idx |= pre & 0x07; 651 val |= 0x04; 652 idx |= (uint16_t)div << 8; 653 val |= 0x08; 654 idx |= mod & 0xF8; 655 val |= 0x10; 656 657 DPRINTF("reset v=0x%04X, i=0x%04X\n", val, idx); 658 659 uchcom_ctrl_write(sc, UCHCOM_REQ_RESET, val, idx); 660} 661 662/* ---------------------------------------------------------------------- 663 * methods for ucom 664 */ 665static void 666uchcom_cfg_get_status(struct usb2_com_softc *ucom, uint8_t *lsr, uint8_t *msr) 667{ 668 struct uchcom_softc *sc = ucom->sc_parent; 669 670 DPRINTF("\n"); 671 672 *lsr = sc->sc_lsr; 673 *msr = sc->sc_msr; 674} 675 676static void 677uchcom_cfg_set_dtr(struct usb2_com_softc *ucom, uint8_t onoff) 678{ 679 struct uchcom_softc *sc = ucom->sc_parent; 680 681 DPRINTF("onoff = %d\n", onoff); 682 683 sc->sc_dtr = onoff; 684 uchcom_set_dtrrts(sc); 685} 686 687static void 688uchcom_cfg_set_rts(struct usb2_com_softc *ucom, uint8_t onoff) 689{ 690 struct uchcom_softc *sc = ucom->sc_parent; 691 692 DPRINTF("onoff = %d\n", onoff); 693 694 sc->sc_rts = onoff; 695 uchcom_set_dtrrts(sc); 696} 697 698static int 699uchcom_pre_param(struct usb2_com_softc *ucom, struct termios *t) 700{ 701 struct uchcom_divider dv; 702 703 switch (t->c_cflag & CSIZE) { 704 case CS5: 705 case CS6: 706 case CS7: 707 return (EIO); 708 default: 709 break; 710 } 711 712 if (uchcom_calc_divider_settings(&dv, t->c_ospeed)) { 713 return (EIO); 714 } 715 return (0); /* success */ 716} 717 718static void 719uchcom_cfg_param(struct usb2_com_softc *ucom, struct termios *t) 720{ 721 struct uchcom_softc *sc = ucom->sc_parent; 722 723 uchcom_set_line_control(sc, t->c_cflag); 724 uchcom_set_dte_rate(sc, t->c_ospeed); 725} 726 727static void 728uchcom_start_read(struct usb2_com_softc *ucom) 729{ 730 struct uchcom_softc *sc = ucom->sc_parent; 731 732 /* start interrupt endpoint */ 733 usb2_transfer_start(sc->sc_xfer[UCHCOM_INTR_DT_RD]); 734 735 /* start read endpoint */ 736 usb2_transfer_start(sc->sc_xfer[UCHCOM_BULK_DT_RD]); 737} 738 739static void 740uchcom_stop_read(struct usb2_com_softc *ucom) 741{ 742 struct uchcom_softc *sc = ucom->sc_parent; 743 744 /* stop interrupt endpoint */ 745 usb2_transfer_stop(sc->sc_xfer[UCHCOM_INTR_DT_RD]); 746 747 /* stop read endpoint */
|
815 usb2_transfer_stop(sc->sc_xfer[UCHCOM_BULK_CS_RD]);
|
748 usb2_transfer_stop(sc->sc_xfer[UCHCOM_BULK_DT_RD]); 749} 750 751static void 752uchcom_start_write(struct usb2_com_softc *ucom) 753{ 754 struct uchcom_softc *sc = ucom->sc_parent; 755 756 usb2_transfer_start(sc->sc_xfer[UCHCOM_BULK_DT_WR]); 757} 758 759static void 760uchcom_stop_write(struct usb2_com_softc *ucom) 761{ 762 struct uchcom_softc *sc = ucom->sc_parent; 763
|
832 usb2_transfer_stop(sc->sc_xfer[UCHCOM_BULK_CS_WR]);
|
764 usb2_transfer_stop(sc->sc_xfer[UCHCOM_BULK_DT_WR]); 765} 766 767/* ---------------------------------------------------------------------- 768 * callback when the modem status is changed. 769 */ 770static void 771uchcom_intr_callback(struct usb2_xfer *xfer) 772{ 773 struct uchcom_softc *sc = xfer->priv_sc; 774 uint8_t buf[UCHCOM_INTR_LEAST]; 775 776 switch (USB_GET_STATE(xfer)) { 777 case USB_ST_TRANSFERRED: 778 779 DPRINTF("actlen = %u\n", xfer->actlen); 780 781 if (xfer->actlen >= UCHCOM_INTR_LEAST) { 782 usb2_copy_out(xfer->frbuffers, 0, buf, 783 UCHCOM_INTR_LEAST); 784 785 DPRINTF("data = 0x%02X 0x%02X 0x%02X 0x%02X\n", 786 (unsigned)buf[0], (unsigned)buf[1], 787 (unsigned)buf[2], (unsigned)buf[3]); 788 789 uchcom_convert_status(sc, buf[UCHCOM_INTR_STAT1]); 790 usb2_com_status_change(&sc->sc_ucom); 791 } 792 case USB_ST_SETUP:
|
862 if (sc->sc_flag & UCHCOM_FLAG_INTR_STALL) {
863 usb2_transfer_start(sc->sc_xfer[UCHCOM_INTR_CS_RD]);
864 } else {
865 xfer->frlengths[0] = xfer->max_data_length;
866 usb2_start_hardware(xfer);
867 }
|
793tr_setup: 794 xfer->frlengths[0] = xfer->max_data_length; 795 usb2_start_hardware(xfer); |
796 break; 797 798 default: /* Error */ 799 if (xfer->error != USB_ERR_CANCELLED) {
|
872 sc->sc_flag |= UCHCOM_FLAG_INTR_STALL;
873 usb2_transfer_start(sc->sc_xfer[UCHCOM_INTR_CS_RD]);
|
800 /* try to clear stall first */ 801 xfer->flags.stall_pipe = 1; 802 goto tr_setup; |
803 } 804 break; 805 } 806} 807 808static void
|
880uchcom_intr_clear_stall_callback(struct usb2_xfer *xfer)
881{
882 struct uchcom_softc *sc = xfer->priv_sc;
883 struct usb2_xfer *xfer_other = sc->sc_xfer[UCHCOM_INTR_DT_RD];
884
885 if (usb2_clear_stall_callback(xfer, xfer_other)) {
886 DPRINTF("stall cleared\n");
887 sc->sc_flag &= ~UCHCOM_FLAG_INTR_STALL;
888 usb2_transfer_start(xfer_other);
889 }
890}
891
892static void
|
809uchcom_write_callback(struct usb2_xfer *xfer) 810{ 811 struct uchcom_softc *sc = xfer->priv_sc; 812 uint32_t actlen; 813 814 switch (USB_GET_STATE(xfer)) { 815 case USB_ST_SETUP: 816 case USB_ST_TRANSFERRED:
|
901 if (sc->sc_flag & UCHCOM_FLAG_WRITE_STALL) {
902 usb2_transfer_start(sc->sc_xfer[UCHCOM_BULK_CS_WR]);
903 return;
904 }
|
817tr_setup: |
818 if (usb2_com_get_data(&sc->sc_ucom, xfer->frbuffers, 0, 819 UCHCOM_BULK_BUF_SIZE, &actlen)) { 820 821 DPRINTF("actlen = %d\n", actlen); 822 823 xfer->frlengths[0] = actlen; 824 usb2_start_hardware(xfer); 825 } 826 return; 827 828 default: /* Error */ 829 if (xfer->error != USB_ERR_CANCELLED) {
|
917 sc->sc_flag |= UCHCOM_FLAG_WRITE_STALL;
918 usb2_transfer_start(sc->sc_xfer[UCHCOM_BULK_CS_WR]);
|
830 /* try to clear stall first */ 831 xfer->flags.stall_pipe = 1; 832 goto tr_setup; |
833 } 834 return; 835 836 } 837} 838 839static void
|
926uchcom_write_clear_stall_callback(struct usb2_xfer *xfer)
927{
928 struct uchcom_softc *sc = xfer->priv_sc;
929 struct usb2_xfer *xfer_other = sc->sc_xfer[UCHCOM_BULK_DT_WR];
930
931 if (usb2_clear_stall_callback(xfer, xfer_other)) {
932 DPRINTF("stall cleared\n");
933 sc->sc_flag &= ~UCHCOM_FLAG_WRITE_STALL;
934 usb2_transfer_start(xfer_other);
935 }
936}
937
938static void
|
840uchcom_read_callback(struct usb2_xfer *xfer) 841{ 842 struct uchcom_softc *sc = xfer->priv_sc; 843 844 switch (USB_GET_STATE(xfer)) { 845 case USB_ST_TRANSFERRED: 846 usb2_com_put_data(&sc->sc_ucom, xfer->frbuffers, 0, xfer->actlen); 847 848 case USB_ST_SETUP:
|
948 if (sc->sc_flag & UCHCOM_FLAG_READ_STALL) {
949 usb2_transfer_start(sc->sc_xfer[UCHCOM_BULK_CS_RD]);
950 } else {
951 xfer->frlengths[0] = xfer->max_data_length;
952 usb2_start_hardware(xfer);
953 }
|
849tr_setup: 850 xfer->frlengths[0] = xfer->max_data_length; 851 usb2_start_hardware(xfer); |
852 return; 853 854 default: /* Error */ 855 if (xfer->error != USB_ERR_CANCELLED) {
|
958 sc->sc_flag |= UCHCOM_FLAG_READ_STALL;
959 usb2_transfer_start(sc->sc_xfer[UCHCOM_BULK_CS_RD]);
|
856 /* try to clear stall first */ 857 xfer->flags.stall_pipe = 1; 858 goto tr_setup; |
859 } 860 return;
|
962
|
861 } 862} 863
|
966static void
967uchcom_read_clear_stall_callback(struct usb2_xfer *xfer)
968{
969 struct uchcom_softc *sc = xfer->priv_sc;
970 struct usb2_xfer *xfer_other = sc->sc_xfer[UCHCOM_BULK_DT_RD];
971
972 if (usb2_clear_stall_callback(xfer, xfer_other)) {
973 DPRINTF("stall cleared\n");
974 sc->sc_flag &= ~UCHCOM_FLAG_READ_STALL;
975 usb2_transfer_start(xfer_other);
976 }
977}
978
|
864static device_method_t uchcom_methods[] = { 865 /* Device interface */ 866 DEVMETHOD(device_probe, uchcom_probe), 867 DEVMETHOD(device_attach, uchcom_attach), 868 DEVMETHOD(device_detach, uchcom_detach), 869 870 {0, 0} 871}; 872 873static driver_t uchcom_driver = { 874 "ucom", 875 uchcom_methods, 876 sizeof(struct uchcom_softc) 877}; 878 879static devclass_t uchcom_devclass; 880 881DRIVER_MODULE(uchcom, ushub, uchcom_driver, uchcom_devclass, NULL, 0); 882MODULE_DEPEND(uchcom, usb2_serial, 1, 1, 1); 883MODULE_DEPEND(uchcom, usb2_core, 1, 1, 1);
|