uplcom.c revision 194228
1184610Salfred/* $NetBSD: uplcom.c,v 1.21 2001/11/13 06:24:56 lukem Exp $ */ 2184610Salfred 3184610Salfred#include <sys/cdefs.h> 4184610Salfred__FBSDID("$FreeBSD: head/sys/dev/usb/serial/uplcom.c 194228 2009-06-15 01:02:43Z thompsa $"); 5184610Salfred 6184610Salfred/*- 7184610Salfred * Copyright (c) 2001-2003, 2005 Shunsuke Akiyama <akiyama@jp.FreeBSD.org>. 8184610Salfred * All rights reserved. 9184610Salfred * 10184610Salfred * Redistribution and use in source and binary forms, with or without 11184610Salfred * modification, are permitted provided that the following conditions 12184610Salfred * are met: 13184610Salfred * 1. Redistributions of source code must retain the above copyright 14184610Salfred * notice, this list of conditions and the following disclaimer. 15184610Salfred * 2. Redistributions in binary form must reproduce the above copyright 16184610Salfred * notice, this list of conditions and the following disclaimer in the 17184610Salfred * documentation and/or other materials provided with the distribution. 18184610Salfred * 19184610Salfred * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 20184610Salfred * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21184610Salfred * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22184610Salfred * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 23184610Salfred * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24184610Salfred * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25184610Salfred * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26184610Salfred * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27184610Salfred * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28184610Salfred * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29184610Salfred * SUCH DAMAGE. 30184610Salfred */ 31184610Salfred 32184610Salfred/*- 33184610Salfred * Copyright (c) 2001 The NetBSD Foundation, Inc. 34184610Salfred * All rights reserved. 35184610Salfred * 36184610Salfred * This code is derived from software contributed to The NetBSD Foundation 37184610Salfred * by Ichiro FUKUHARA (ichiro@ichiro.org). 38184610Salfred * 39184610Salfred * Redistribution and use in source and binary forms, with or without 40184610Salfred * modification, are permitted provided that the following conditions 41184610Salfred * are met: 42184610Salfred * 1. Redistributions of source code must retain the above copyright 43184610Salfred * notice, this list of conditions and the following disclaimer. 44184610Salfred * 2. Redistributions in binary form must reproduce the above copyright 45184610Salfred * notice, this list of conditions and the following disclaimer in the 46184610Salfred * documentation and/or other materials provided with the distribution. 47184610Salfred * 3. All advertising materials mentioning features or use of this software 48184610Salfred * must display the following acknowledgement: 49184610Salfred * This product includes software developed by the NetBSD 50184610Salfred * Foundation, Inc. and its contributors. 51184610Salfred * 4. Neither the name of The NetBSD Foundation nor the names of its 52184610Salfred * contributors may be used to endorse or promote products derived 53184610Salfred * from this software without specific prior written permission. 54184610Salfred * 55184610Salfred * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 56184610Salfred * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 57184610Salfred * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 58184610Salfred * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 59184610Salfred * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 60184610Salfred * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 61184610Salfred * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 62184610Salfred * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 63184610Salfred * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 64184610Salfred * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 65184610Salfred * POSSIBILITY OF SUCH DAMAGE. 66184610Salfred */ 67184610Salfred 68184610Salfred/* 69184610Salfred * This driver supports several USB-to-RS232 serial adapters driven by 70184610Salfred * Prolific PL-2303, PL-2303X and probably PL-2303HX USB-to-RS232 71184610Salfred * bridge chip. The adapters are sold under many different brand 72184610Salfred * names. 73184610Salfred * 74184610Salfred * Datasheets are available at Prolific www site at 75184610Salfred * http://www.prolific.com.tw. The datasheets don't contain full 76184610Salfred * programming information for the chip. 77184610Salfred * 78184610Salfred * PL-2303HX is probably programmed the same as PL-2303X. 79184610Salfred * 80184610Salfred * There are several differences between PL-2303 and PL-2303(H)X. 81184610Salfred * PL-2303(H)X can do higher bitrate in bulk mode, has _probably_ 82184610Salfred * different command for controlling CRTSCTS and needs special 83184610Salfred * sequence of commands for initialization which aren't also 84184610Salfred * documented in the datasheet. 85184610Salfred */ 86184610Salfred 87188746Sthompsa#include "usbdevs.h" 88188942Sthompsa#include <dev/usb/usb.h> 89188942Sthompsa#include <dev/usb/usb_mfunc.h> 90188942Sthompsa#include <dev/usb/usb_error.h> 91188942Sthompsa#include <dev/usb/usb_cdc.h> 92184610Salfred 93184610Salfred#define USB_DEBUG_VAR uplcom_debug 94184610Salfred 95188942Sthompsa#include <dev/usb/usb_core.h> 96188942Sthompsa#include <dev/usb/usb_debug.h> 97188942Sthompsa#include <dev/usb/usb_process.h> 98188942Sthompsa#include <dev/usb/usb_request.h> 99188942Sthompsa#include <dev/usb/usb_lookup.h> 100188942Sthompsa#include <dev/usb/usb_util.h> 101188942Sthompsa#include <dev/usb/usb_busdma.h> 102184610Salfred 103188942Sthompsa#include <dev/usb/serial/usb_serial.h> 104184610Salfred 105184610Salfred#if USB_DEBUG 106184610Salfredstatic int uplcom_debug = 0; 107184610Salfred 108192502SthompsaSYSCTL_NODE(_hw_usb, OID_AUTO, uplcom, CTLFLAG_RW, 0, "USB uplcom"); 109192502SthompsaSYSCTL_INT(_hw_usb_uplcom, OID_AUTO, debug, CTLFLAG_RW, 110184610Salfred &uplcom_debug, 0, "Debug level"); 111184610Salfred#endif 112184610Salfred 113184610Salfred#define UPLCOM_MODVER 1 /* module version */ 114184610Salfred 115184610Salfred#define UPLCOM_CONFIG_INDEX 0 116184610Salfred#define UPLCOM_IFACE_INDEX 0 117184610Salfred#define UPLCOM_SECOND_IFACE_INDEX 1 118184610Salfred 119184610Salfred#ifndef UPLCOM_INTR_INTERVAL 120184610Salfred#define UPLCOM_INTR_INTERVAL 0 /* default */ 121184610Salfred#endif 122184610Salfred 123184610Salfred#define UPLCOM_BULK_BUF_SIZE 1024 /* bytes */ 124184610Salfred 125184610Salfred#define UPLCOM_SET_REQUEST 0x01 126184610Salfred#define UPLCOM_SET_CRTSCTS 0x41 127184610Salfred#define UPLCOM_SET_CRTSCTS_PL2303X 0x61 128184610Salfred#define RSAQ_STATUS_CTS 0x80 129184610Salfred#define RSAQ_STATUS_DSR 0x02 130184610Salfred#define RSAQ_STATUS_DCD 0x01 131184610Salfred 132184610Salfred#define TYPE_PL2303 0 133184610Salfred#define TYPE_PL2303X 1 134184610Salfred 135187259Sthompsaenum { 136187259Sthompsa UPLCOM_BULK_DT_WR, 137187259Sthompsa UPLCOM_BULK_DT_RD, 138187259Sthompsa UPLCOM_INTR_DT_RD, 139188413Sthompsa UPLCOM_N_TRANSFER, 140187259Sthompsa}; 141187259Sthompsa 142184610Salfredstruct uplcom_softc { 143192984Sthompsa struct ucom_super_softc sc_super_ucom; 144192984Sthompsa struct ucom_softc sc_ucom; 145184610Salfred 146192984Sthompsa struct usb_xfer *sc_xfer[UPLCOM_N_TRANSFER]; 147192984Sthompsa struct usb_device *sc_udev; 148189265Sthompsa struct mtx sc_mtx; 149184610Salfred 150184610Salfred uint16_t sc_line; 151184610Salfred 152184610Salfred uint8_t sc_lsr; /* local status register */ 153184610Salfred uint8_t sc_msr; /* uplcom status register */ 154184610Salfred uint8_t sc_chiptype; /* type of chip */ 155184610Salfred uint8_t sc_ctrl_iface_no; 156184610Salfred uint8_t sc_data_iface_no; 157184610Salfred uint8_t sc_iface_index[2]; 158184610Salfred}; 159184610Salfred 160184610Salfred/* prototypes */ 161184610Salfred 162193045Sthompsastatic usb_error_t uplcom_reset(struct uplcom_softc *, struct usb_device *); 163192984Sthompsastatic int uplcom_pl2303x_init(struct usb_device *); 164192984Sthompsastatic void uplcom_cfg_set_dtr(struct ucom_softc *, uint8_t); 165192984Sthompsastatic void uplcom_cfg_set_rts(struct ucom_softc *, uint8_t); 166192984Sthompsastatic void uplcom_cfg_set_break(struct ucom_softc *, uint8_t); 167192984Sthompsastatic int uplcom_pre_param(struct ucom_softc *, struct termios *); 168192984Sthompsastatic void uplcom_cfg_param(struct ucom_softc *, struct termios *); 169192984Sthompsastatic void uplcom_start_read(struct ucom_softc *); 170192984Sthompsastatic void uplcom_stop_read(struct ucom_softc *); 171192984Sthompsastatic void uplcom_start_write(struct ucom_softc *); 172192984Sthompsastatic void uplcom_stop_write(struct ucom_softc *); 173192984Sthompsastatic void uplcom_cfg_get_status(struct ucom_softc *, uint8_t *, 174185948Sthompsa uint8_t *); 175184610Salfred 176184610Salfredstatic device_probe_t uplcom_probe; 177184610Salfredstatic device_attach_t uplcom_attach; 178184610Salfredstatic device_detach_t uplcom_detach; 179184610Salfred 180193045Sthompsastatic usb_callback_t uplcom_intr_callback; 181193045Sthompsastatic usb_callback_t uplcom_write_callback; 182193045Sthompsastatic usb_callback_t uplcom_read_callback; 183184610Salfred 184192984Sthompsastatic const struct usb_config uplcom_config_data[UPLCOM_N_TRANSFER] = { 185184610Salfred 186187259Sthompsa [UPLCOM_BULK_DT_WR] = { 187184610Salfred .type = UE_BULK, 188184610Salfred .endpoint = UE_ADDR_ANY, 189184610Salfred .direction = UE_DIR_OUT, 190190734Sthompsa .bufsize = UPLCOM_BULK_BUF_SIZE, 191190734Sthompsa .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, 192190734Sthompsa .callback = &uplcom_write_callback, 193184610Salfred .if_index = 0, 194184610Salfred }, 195184610Salfred 196187259Sthompsa [UPLCOM_BULK_DT_RD] = { 197184610Salfred .type = UE_BULK, 198184610Salfred .endpoint = UE_ADDR_ANY, 199184610Salfred .direction = UE_DIR_IN, 200190734Sthompsa .bufsize = UPLCOM_BULK_BUF_SIZE, 201190734Sthompsa .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, 202190734Sthompsa .callback = &uplcom_read_callback, 203184610Salfred .if_index = 0, 204184610Salfred }, 205184610Salfred 206187259Sthompsa [UPLCOM_INTR_DT_RD] = { 207184610Salfred .type = UE_INTERRUPT, 208184610Salfred .endpoint = UE_ADDR_ANY, 209184610Salfred .direction = UE_DIR_IN, 210190734Sthompsa .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, 211190734Sthompsa .bufsize = 0, /* use wMaxPacketSize */ 212190734Sthompsa .callback = &uplcom_intr_callback, 213184610Salfred .if_index = 1, 214184610Salfred }, 215184610Salfred}; 216184610Salfred 217194099Sthompsastatic struct ucom_callback uplcom_callback = { 218194228Sthompsa .ucom_cfg_get_status = &uplcom_cfg_get_status, 219194228Sthompsa .ucom_cfg_set_dtr = &uplcom_cfg_set_dtr, 220194228Sthompsa .ucom_cfg_set_rts = &uplcom_cfg_set_rts, 221194228Sthompsa .ucom_cfg_set_break = &uplcom_cfg_set_break, 222194228Sthompsa .ucom_cfg_param = &uplcom_cfg_param, 223194228Sthompsa .ucom_pre_param = &uplcom_pre_param, 224194228Sthompsa .ucom_start_read = &uplcom_start_read, 225194228Sthompsa .ucom_stop_read = &uplcom_stop_read, 226194228Sthompsa .ucom_start_write = &uplcom_start_write, 227194228Sthompsa .ucom_stop_write = &uplcom_stop_write, 228184610Salfred}; 229184610Salfred 230184610Salfred#define USB_UPL(v,p,rl,rh,t) \ 231184610Salfred USB_VENDOR(v), USB_PRODUCT(p), USB_DEV_BCD_GTEQ(rl), \ 232184610Salfred USB_DEV_BCD_LTEQ(rh), USB_DRIVER_INFO(t) 233184610Salfred 234192984Sthompsastatic const struct usb_device_id uplcom_devs[] = { 235184610Salfred /* Belkin F5U257 */ 236184610Salfred {USB_UPL(USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5U257, 0, 0xFFFF, TYPE_PL2303X)}, 237184610Salfred /* I/O DATA USB-RSAQ */ 238184610Salfred {USB_UPL(USB_VENDOR_IODATA, USB_PRODUCT_IODATA_USBRSAQ, 0, 0xFFFF, TYPE_PL2303)}, 239184610Salfred /* I/O DATA USB-RSAQ2 */ 240184610Salfred {USB_UPL(USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_RSAQ2, 0, 0xFFFF, TYPE_PL2303)}, 241184610Salfred /* I/O DATA USB-RSAQ3 */ 242184610Salfred {USB_UPL(USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_RSAQ3, 0, 0xFFFF, TYPE_PL2303X)}, 243184610Salfred /* PLANEX USB-RS232 URS-03 */ 244184610Salfred {USB_UPL(USB_VENDOR_ATEN, USB_PRODUCT_ATEN_UC232A, 0, 0xFFFF, TYPE_PL2303)}, 245184610Salfred /* TrendNet TU-S9 */ 246184610Salfred {USB_UPL(USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PL2303, 0x0400, 0xFFFF, TYPE_PL2303X)}, 247184610Salfred /* ST Lab USB-SERIAL-4 */ 248184610Salfred {USB_UPL(USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PL2303, 0x0300, 0x03FF, TYPE_PL2303X)}, 249184610Salfred /* IOGEAR/ATEN UC-232A (also ST Lab USB-SERIAL-1) */ 250184610Salfred {USB_UPL(USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PL2303, 0, 0x02FF, TYPE_PL2303)}, 251184610Salfred /* TDK USB-PHS Adapter UHA6400 */ 252184610Salfred {USB_UPL(USB_VENDOR_TDK, USB_PRODUCT_TDK_UHA6400, 0, 0xFFFF, TYPE_PL2303)}, 253184610Salfred /* RATOC REX-USB60 */ 254184610Salfred {USB_UPL(USB_VENDOR_RATOC, USB_PRODUCT_RATOC_REXUSB60, 0, 0xFFFF, TYPE_PL2303)}, 255184610Salfred /* ELECOM UC-SGT */ 256184610Salfred {USB_UPL(USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_UCSGT, 0, 0xFFFF, TYPE_PL2303)}, 257184610Salfred {USB_UPL(USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_UCSGT0, 0, 0xFFFF, TYPE_PL2303)}, 258184610Salfred /* Sagem USB-Serial Controller */ 259184610Salfred {USB_UPL(USB_VENDOR_SAGEM, USB_PRODUCT_SAGEM_USBSERIAL, 0, 0xFFFF, TYPE_PL2303X)}, 260184610Salfred /* Sony Ericsson USB Cable */ 261184610Salfred {USB_UPL(USB_VENDOR_SONYERICSSON, USB_PRODUCT_SONYERICSSON_DCU10, 0, 0xFFFF, TYPE_PL2303)}, 262184610Salfred /* SOURCENEXT KeikaiDenwa 8 */ 263184610Salfred {USB_UPL(USB_VENDOR_SOURCENEXT, USB_PRODUCT_SOURCENEXT_KEIKAI8, 0, 0xFFFF, TYPE_PL2303)}, 264184610Salfred /* SOURCENEXT KeikaiDenwa 8 with charger */ 265184610Salfred {USB_UPL(USB_VENDOR_SOURCENEXT, USB_PRODUCT_SOURCENEXT_KEIKAI8_CHG, 0, 0, TYPE_PL2303)}, 266184610Salfred /* HAL Corporation Crossam2+USB */ 267184610Salfred {USB_UPL(USB_VENDOR_HAL, USB_PRODUCT_HAL_IMR001, 0, 0xFFFF, TYPE_PL2303)}, 268184610Salfred /* Sitecom USB to Serial */ 269184610Salfred {USB_UPL(USB_VENDOR_SITECOM, USB_PRODUCT_SITECOM_SERIAL, 0, 0xFFFF, TYPE_PL2303)}, 270184610Salfred /* Tripp-Lite U209-000-R */ 271184610Salfred {USB_UPL(USB_VENDOR_TRIPPLITE, USB_PRODUCT_TRIPPLITE_U209, 0, 0xFFFF, TYPE_PL2303X)}, 272184610Salfred {USB_UPL(USB_VENDOR_RADIOSHACK, USB_PRODUCT_RADIOSHACK_USBCABLE, 0, 0xFFFF, TYPE_PL2303)}, 273184610Salfred /* Prolific Pharos */ 274184610Salfred {USB_UPL(USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PHAROS, 0, 0xFFFF, TYPE_PL2303)}, 275184610Salfred /* Willcom W-SIM */ 276184610Salfred {USB_UPL(USB_VENDOR_PROLIFIC2, USB_PRODUCT_PROLIFIC2_WSIM, 0, 0xFFFF, TYPE_PL2303X)}, 277189360Sthompsa /* Mobile Action MA-620 Infrared Adapter */ 278189360Sthompsa {USB_UPL(USB_VENDOR_MOBILEACTION, USB_PRODUCT_MOBILEACTION_MA620, 0, 0xFFFF, TYPE_PL2303X)}, 279189360Sthompsa 280184610Salfred}; 281184610Salfred 282184610Salfredstatic device_method_t uplcom_methods[] = { 283184610Salfred DEVMETHOD(device_probe, uplcom_probe), 284184610Salfred DEVMETHOD(device_attach, uplcom_attach), 285184610Salfred DEVMETHOD(device_detach, uplcom_detach), 286184610Salfred {0, 0} 287184610Salfred}; 288184610Salfred 289184610Salfredstatic devclass_t uplcom_devclass; 290184610Salfred 291184610Salfredstatic driver_t uplcom_driver = { 292184610Salfred .name = "uplcom", 293184610Salfred .methods = uplcom_methods, 294184610Salfred .size = sizeof(struct uplcom_softc), 295184610Salfred}; 296184610Salfred 297189275SthompsaDRIVER_MODULE(uplcom, uhub, uplcom_driver, uplcom_devclass, NULL, 0); 298188942SthompsaMODULE_DEPEND(uplcom, ucom, 1, 1, 1); 299188942SthompsaMODULE_DEPEND(uplcom, usb, 1, 1, 1); 300184610SalfredMODULE_VERSION(uplcom, UPLCOM_MODVER); 301184610Salfred 302184610Salfredstatic int 303184610Salfreduplcom_probe(device_t dev) 304184610Salfred{ 305192984Sthompsa struct usb_attach_arg *uaa = device_get_ivars(dev); 306184610Salfred 307184610Salfred DPRINTFN(11, "\n"); 308184610Salfred 309192499Sthompsa if (uaa->usb_mode != USB_MODE_HOST) { 310184610Salfred return (ENXIO); 311184610Salfred } 312184610Salfred if (uaa->info.bConfigIndex != UPLCOM_CONFIG_INDEX) { 313184610Salfred return (ENXIO); 314184610Salfred } 315184610Salfred if (uaa->info.bIfaceIndex != UPLCOM_IFACE_INDEX) { 316184610Salfred return (ENXIO); 317184610Salfred } 318194228Sthompsa return (usbd_lookup_id_by_uaa(uplcom_devs, sizeof(uplcom_devs), uaa)); 319184610Salfred} 320184610Salfred 321184610Salfredstatic int 322184610Salfreduplcom_attach(device_t dev) 323184610Salfred{ 324192984Sthompsa struct usb_attach_arg *uaa = device_get_ivars(dev); 325184610Salfred struct uplcom_softc *sc = device_get_softc(dev); 326192984Sthompsa struct usb_interface *iface; 327192984Sthompsa struct usb_interface_descriptor *id; 328184610Salfred int error; 329184610Salfred 330184610Salfred DPRINTFN(11, "\n"); 331184610Salfred 332194228Sthompsa device_set_usb_desc(dev); 333189265Sthompsa mtx_init(&sc->sc_mtx, "uplcom", NULL, MTX_DEF); 334184610Salfred 335184610Salfred DPRINTF("sc = %p\n", sc); 336184610Salfred 337184610Salfred sc->sc_chiptype = USB_GET_DRIVER_INFO(uaa); 338184610Salfred sc->sc_udev = uaa->device; 339184610Salfred 340184610Salfred DPRINTF("chiptype: %s\n", 341184610Salfred (sc->sc_chiptype == TYPE_PL2303X) ? 342184610Salfred "2303X" : "2303"); 343184610Salfred 344184610Salfred /* 345184610Salfred * USB-RSAQ1 has two interface 346184610Salfred * 347184610Salfred * USB-RSAQ1 | USB-RSAQ2 348184610Salfred * -----------------+----------------- 349184610Salfred * Interface 0 |Interface 0 350184610Salfred * Interrupt(0x81) | Interrupt(0x81) 351184610Salfred * -----------------+ BulkIN(0x02) 352184610Salfred * Interface 1 | BulkOUT(0x83) 353184610Salfred * BulkIN(0x02) | 354184610Salfred * BulkOUT(0x83) | 355184610Salfred */ 356184610Salfred 357184610Salfred sc->sc_ctrl_iface_no = uaa->info.bIfaceNum; 358184610Salfred sc->sc_iface_index[1] = UPLCOM_IFACE_INDEX; 359184610Salfred 360194228Sthompsa iface = usbd_get_iface(uaa->device, UPLCOM_SECOND_IFACE_INDEX); 361184610Salfred if (iface) { 362194228Sthompsa id = usbd_get_interface_descriptor(iface); 363184610Salfred if (id == NULL) { 364184610Salfred device_printf(dev, "no interface descriptor (2)!\n"); 365184610Salfred goto detach; 366184610Salfred } 367184610Salfred sc->sc_data_iface_no = id->bInterfaceNumber; 368184610Salfred sc->sc_iface_index[0] = UPLCOM_SECOND_IFACE_INDEX; 369194228Sthompsa usbd_set_parent_iface(uaa->device, 370184610Salfred UPLCOM_SECOND_IFACE_INDEX, uaa->info.bIfaceIndex); 371184610Salfred } else { 372184610Salfred sc->sc_data_iface_no = sc->sc_ctrl_iface_no; 373184610Salfred sc->sc_iface_index[0] = UPLCOM_IFACE_INDEX; 374184610Salfred } 375184610Salfred 376194228Sthompsa error = usbd_transfer_setup(uaa->device, 377184610Salfred sc->sc_iface_index, sc->sc_xfer, uplcom_config_data, 378189265Sthompsa UPLCOM_N_TRANSFER, sc, &sc->sc_mtx); 379184610Salfred if (error) { 380184610Salfred DPRINTF("one or more missing USB endpoints, " 381194228Sthompsa "error=%s\n", usbd_errstr(error)); 382184610Salfred goto detach; 383184610Salfred } 384184610Salfred error = uplcom_reset(sc, uaa->device); 385184610Salfred if (error) { 386184610Salfred device_printf(dev, "reset failed, error=%s\n", 387194228Sthompsa usbd_errstr(error)); 388184610Salfred goto detach; 389184610Salfred } 390184610Salfred /* clear stall at first run */ 391189265Sthompsa mtx_lock(&sc->sc_mtx); 392194228Sthompsa usbd_transfer_set_stall(sc->sc_xfer[UPLCOM_BULK_DT_WR]); 393194228Sthompsa usbd_transfer_set_stall(sc->sc_xfer[UPLCOM_BULK_DT_RD]); 394189265Sthompsa mtx_unlock(&sc->sc_mtx); 395184610Salfred 396194228Sthompsa error = ucom_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc, 397189265Sthompsa &uplcom_callback, &sc->sc_mtx); 398184610Salfred if (error) { 399184610Salfred goto detach; 400184610Salfred } 401184610Salfred /* 402184610Salfred * do the initialization during attach so that the system does not 403184610Salfred * sleep during open: 404184610Salfred */ 405184610Salfred if (sc->sc_chiptype == TYPE_PL2303X) { 406184610Salfred if (uplcom_pl2303x_init(uaa->device)) { 407184610Salfred device_printf(dev, "init failed!\n"); 408184610Salfred goto detach; 409184610Salfred } 410184610Salfred } 411184610Salfred return (0); 412184610Salfred 413184610Salfreddetach: 414184610Salfred uplcom_detach(dev); 415184610Salfred return (ENXIO); 416184610Salfred} 417184610Salfred 418184610Salfredstatic int 419184610Salfreduplcom_detach(device_t dev) 420184610Salfred{ 421184610Salfred struct uplcom_softc *sc = device_get_softc(dev); 422184610Salfred 423184610Salfred DPRINTF("sc=%p\n", sc); 424184610Salfred 425194228Sthompsa ucom_detach(&sc->sc_super_ucom, &sc->sc_ucom, 1); 426194228Sthompsa usbd_transfer_unsetup(sc->sc_xfer, UPLCOM_N_TRANSFER); 427189265Sthompsa mtx_destroy(&sc->sc_mtx); 428184610Salfred 429184610Salfred return (0); 430184610Salfred} 431184610Salfred 432193045Sthompsastatic usb_error_t 433192984Sthompsauplcom_reset(struct uplcom_softc *sc, struct usb_device *udev) 434184610Salfred{ 435192984Sthompsa struct usb_device_request req; 436184610Salfred 437184610Salfred req.bmRequestType = UT_WRITE_VENDOR_DEVICE; 438184610Salfred req.bRequest = UPLCOM_SET_REQUEST; 439184610Salfred USETW(req.wValue, 0); 440184610Salfred req.wIndex[0] = sc->sc_data_iface_no; 441184610Salfred req.wIndex[1] = 0; 442184610Salfred USETW(req.wLength, 0); 443184610Salfred 444194228Sthompsa return (usbd_do_request(udev, NULL, &req, NULL)); 445184610Salfred} 446184610Salfred 447184610Salfredstruct pl2303x_init { 448184610Salfred uint8_t req_type; 449184610Salfred uint8_t request; 450184610Salfred uint16_t value; 451184610Salfred uint16_t index; 452184610Salfred uint16_t length; 453184610Salfred}; 454184610Salfred 455184610Salfredstatic const struct pl2303x_init pl2303x[] = { 456184610Salfred {UT_READ_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x8484, 0, 1}, 457184610Salfred {UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x0404, 0, 0}, 458184610Salfred {UT_READ_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x8484, 0, 1}, 459184610Salfred {UT_READ_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x8383, 0, 1}, 460184610Salfred {UT_READ_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x8484, 0, 1}, 461184610Salfred {UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x0404, 1, 0}, 462184610Salfred {UT_READ_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x8484, 0, 1}, 463184610Salfred {UT_READ_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x8383, 0, 1}, 464184610Salfred {UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0, 1, 0}, 465184610Salfred {UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 1, 0, 0}, 466184610Salfred {UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 2, 0x44, 0}, 467184610Salfred {UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 8, 0, 0}, 468184610Salfred {UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 9, 0, 0}, 469184610Salfred}; 470184610Salfred 471184610Salfred#define N_PL2302X_INIT (sizeof(pl2303x)/sizeof(pl2303x[0])) 472184610Salfred 473184610Salfredstatic int 474192984Sthompsauplcom_pl2303x_init(struct usb_device *udev) 475184610Salfred{ 476192984Sthompsa struct usb_device_request req; 477193045Sthompsa usb_error_t err; 478184610Salfred uint8_t buf[4]; 479184610Salfred uint8_t i; 480184610Salfred 481184610Salfred for (i = 0; i != N_PL2302X_INIT; i++) { 482184610Salfred req.bmRequestType = pl2303x[i].req_type; 483184610Salfred req.bRequest = pl2303x[i].request; 484184610Salfred USETW(req.wValue, pl2303x[i].value); 485184610Salfred USETW(req.wIndex, pl2303x[i].index); 486184610Salfred USETW(req.wLength, pl2303x[i].length); 487184610Salfred 488194228Sthompsa err = usbd_do_request(udev, NULL, &req, buf); 489184610Salfred if (err) { 490194228Sthompsa DPRINTF("error=%s\n", usbd_errstr(err)); 491184610Salfred return (EIO); 492184610Salfred } 493184610Salfred } 494184610Salfred return (0); 495184610Salfred} 496184610Salfred 497184610Salfredstatic void 498192984Sthompsauplcom_cfg_set_dtr(struct ucom_softc *ucom, uint8_t onoff) 499184610Salfred{ 500184610Salfred struct uplcom_softc *sc = ucom->sc_parent; 501192984Sthompsa struct usb_device_request req; 502184610Salfred 503184610Salfred DPRINTF("onoff = %d\n", onoff); 504184610Salfred 505184610Salfred if (onoff) 506184610Salfred sc->sc_line |= UCDC_LINE_DTR; 507184610Salfred else 508184610Salfred sc->sc_line &= ~UCDC_LINE_DTR; 509184610Salfred 510184610Salfred req.bmRequestType = UT_WRITE_CLASS_INTERFACE; 511184610Salfred req.bRequest = UCDC_SET_CONTROL_LINE_STATE; 512184610Salfred USETW(req.wValue, sc->sc_line); 513184610Salfred req.wIndex[0] = sc->sc_data_iface_no; 514184610Salfred req.wIndex[1] = 0; 515184610Salfred USETW(req.wLength, 0); 516184610Salfred 517194228Sthompsa ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 518188413Sthompsa &req, NULL, 0, 1000); 519184610Salfred} 520184610Salfred 521184610Salfredstatic void 522192984Sthompsauplcom_cfg_set_rts(struct ucom_softc *ucom, uint8_t onoff) 523184610Salfred{ 524184610Salfred struct uplcom_softc *sc = ucom->sc_parent; 525192984Sthompsa struct usb_device_request req; 526184610Salfred 527184610Salfred DPRINTF("onoff = %d\n", onoff); 528184610Salfred 529184610Salfred if (onoff) 530184610Salfred sc->sc_line |= UCDC_LINE_RTS; 531184610Salfred else 532184610Salfred sc->sc_line &= ~UCDC_LINE_RTS; 533184610Salfred 534184610Salfred req.bmRequestType = UT_WRITE_CLASS_INTERFACE; 535184610Salfred req.bRequest = UCDC_SET_CONTROL_LINE_STATE; 536184610Salfred USETW(req.wValue, sc->sc_line); 537184610Salfred req.wIndex[0] = sc->sc_data_iface_no; 538184610Salfred req.wIndex[1] = 0; 539184610Salfred USETW(req.wLength, 0); 540184610Salfred 541194228Sthompsa ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 542188413Sthompsa &req, NULL, 0, 1000); 543184610Salfred} 544184610Salfred 545184610Salfredstatic void 546192984Sthompsauplcom_cfg_set_break(struct ucom_softc *ucom, uint8_t onoff) 547184610Salfred{ 548184610Salfred struct uplcom_softc *sc = ucom->sc_parent; 549192984Sthompsa struct usb_device_request req; 550184610Salfred uint16_t temp; 551184610Salfred 552184610Salfred DPRINTF("onoff = %d\n", onoff); 553184610Salfred 554184610Salfred temp = (onoff ? UCDC_BREAK_ON : UCDC_BREAK_OFF); 555184610Salfred 556184610Salfred req.bmRequestType = UT_WRITE_CLASS_INTERFACE; 557184610Salfred req.bRequest = UCDC_SEND_BREAK; 558184610Salfred USETW(req.wValue, temp); 559184610Salfred req.wIndex[0] = sc->sc_data_iface_no; 560184610Salfred req.wIndex[1] = 0; 561184610Salfred USETW(req.wLength, 0); 562184610Salfred 563194228Sthompsa ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 564188413Sthompsa &req, NULL, 0, 1000); 565184610Salfred} 566184610Salfred 567184610Salfredstatic const int32_t uplcom_rates[] = { 568184610Salfred 75, 150, 300, 600, 1200, 1800, 2400, 3600, 4800, 7200, 9600, 14400, 569184610Salfred 19200, 28800, 38400, 57600, 115200, 570184610Salfred /* 571184610Salfred * Higher speeds are probably possible. PL2303X supports up to 572184610Salfred * 6Mb and can set any rate 573184610Salfred */ 574184610Salfred 230400, 460800, 614400, 921600, 1228800 575184610Salfred}; 576184610Salfred 577184610Salfred#define N_UPLCOM_RATES (sizeof(uplcom_rates)/sizeof(uplcom_rates[0])) 578184610Salfred 579184610Salfredstatic int 580192984Sthompsauplcom_pre_param(struct ucom_softc *ucom, struct termios *t) 581184610Salfred{ 582184610Salfred uint8_t i; 583184610Salfred 584184610Salfred DPRINTF("\n"); 585184610Salfred 586184610Salfred /* check requested baud rate */ 587184610Salfred 588184610Salfred for (i = 0;; i++) { 589184610Salfred 590184610Salfred if (i != N_UPLCOM_RATES) { 591184610Salfred if (uplcom_rates[i] == t->c_ospeed) { 592184610Salfred break; 593184610Salfred } 594184610Salfred } else { 595184610Salfred DPRINTF("invalid baud rate (%d)\n", t->c_ospeed); 596184610Salfred return (EIO); 597184610Salfred } 598184610Salfred } 599184610Salfred 600184610Salfred return (0); 601184610Salfred} 602184610Salfred 603184610Salfredstatic void 604192984Sthompsauplcom_cfg_param(struct ucom_softc *ucom, struct termios *t) 605184610Salfred{ 606184610Salfred struct uplcom_softc *sc = ucom->sc_parent; 607192984Sthompsa struct usb_cdc_line_state ls; 608192984Sthompsa struct usb_device_request req; 609184610Salfred 610184610Salfred DPRINTF("sc = %p\n", sc); 611184610Salfred 612184610Salfred bzero(&ls, sizeof(ls)); 613184610Salfred 614184610Salfred USETDW(ls.dwDTERate, t->c_ospeed); 615184610Salfred 616184610Salfred if (t->c_cflag & CSTOPB) { 617184610Salfred ls.bCharFormat = UCDC_STOP_BIT_2; 618184610Salfred } else { 619184610Salfred ls.bCharFormat = UCDC_STOP_BIT_1; 620184610Salfred } 621184610Salfred 622184610Salfred if (t->c_cflag & PARENB) { 623184610Salfred if (t->c_cflag & PARODD) { 624184610Salfred ls.bParityType = UCDC_PARITY_ODD; 625184610Salfred } else { 626184610Salfred ls.bParityType = UCDC_PARITY_EVEN; 627184610Salfred } 628184610Salfred } else { 629184610Salfred ls.bParityType = UCDC_PARITY_NONE; 630184610Salfred } 631184610Salfred 632184610Salfred switch (t->c_cflag & CSIZE) { 633184610Salfred case CS5: 634184610Salfred ls.bDataBits = 5; 635184610Salfred break; 636184610Salfred case CS6: 637184610Salfred ls.bDataBits = 6; 638184610Salfred break; 639184610Salfred case CS7: 640184610Salfred ls.bDataBits = 7; 641184610Salfred break; 642184610Salfred case CS8: 643184610Salfred ls.bDataBits = 8; 644184610Salfred break; 645184610Salfred } 646184610Salfred 647184610Salfred DPRINTF("rate=%d fmt=%d parity=%d bits=%d\n", 648184610Salfred UGETDW(ls.dwDTERate), ls.bCharFormat, 649184610Salfred ls.bParityType, ls.bDataBits); 650184610Salfred 651184610Salfred req.bmRequestType = UT_WRITE_CLASS_INTERFACE; 652184610Salfred req.bRequest = UCDC_SET_LINE_CODING; 653184610Salfred USETW(req.wValue, 0); 654184610Salfred req.wIndex[0] = sc->sc_data_iface_no; 655184610Salfred req.wIndex[1] = 0; 656184610Salfred USETW(req.wLength, UCDC_LINE_STATE_LENGTH); 657184610Salfred 658194228Sthompsa ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 659188413Sthompsa &req, &ls, 0, 1000); 660184610Salfred 661184610Salfred if (t->c_cflag & CRTSCTS) { 662184610Salfred 663184610Salfred DPRINTF("crtscts = on\n"); 664184610Salfred 665184610Salfred req.bmRequestType = UT_WRITE_VENDOR_DEVICE; 666184610Salfred req.bRequest = UPLCOM_SET_REQUEST; 667184610Salfred USETW(req.wValue, 0); 668184610Salfred if (sc->sc_chiptype == TYPE_PL2303X) 669184610Salfred USETW(req.wIndex, UPLCOM_SET_CRTSCTS_PL2303X); 670184610Salfred else 671184610Salfred USETW(req.wIndex, UPLCOM_SET_CRTSCTS); 672184610Salfred USETW(req.wLength, 0); 673184610Salfred 674194228Sthompsa ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 675188413Sthompsa &req, NULL, 0, 1000); 676184610Salfred } else { 677184610Salfred req.bmRequestType = UT_WRITE_VENDOR_DEVICE; 678184610Salfred req.bRequest = UPLCOM_SET_REQUEST; 679184610Salfred USETW(req.wValue, 0); 680184610Salfred USETW(req.wIndex, 0); 681184610Salfred USETW(req.wLength, 0); 682194228Sthompsa ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 683188413Sthompsa &req, NULL, 0, 1000); 684184610Salfred } 685184610Salfred} 686184610Salfred 687184610Salfredstatic void 688192984Sthompsauplcom_start_read(struct ucom_softc *ucom) 689184610Salfred{ 690184610Salfred struct uplcom_softc *sc = ucom->sc_parent; 691184610Salfred 692184610Salfred /* start interrupt endpoint */ 693194228Sthompsa usbd_transfer_start(sc->sc_xfer[UPLCOM_INTR_DT_RD]); 694184610Salfred 695184610Salfred /* start read endpoint */ 696194228Sthompsa usbd_transfer_start(sc->sc_xfer[UPLCOM_BULK_DT_RD]); 697184610Salfred} 698184610Salfred 699184610Salfredstatic void 700192984Sthompsauplcom_stop_read(struct ucom_softc *ucom) 701184610Salfred{ 702184610Salfred struct uplcom_softc *sc = ucom->sc_parent; 703184610Salfred 704184610Salfred /* stop interrupt endpoint */ 705194228Sthompsa usbd_transfer_stop(sc->sc_xfer[UPLCOM_INTR_DT_RD]); 706184610Salfred 707184610Salfred /* stop read endpoint */ 708194228Sthompsa usbd_transfer_stop(sc->sc_xfer[UPLCOM_BULK_DT_RD]); 709184610Salfred} 710184610Salfred 711184610Salfredstatic void 712192984Sthompsauplcom_start_write(struct ucom_softc *ucom) 713184610Salfred{ 714184610Salfred struct uplcom_softc *sc = ucom->sc_parent; 715184610Salfred 716194228Sthompsa usbd_transfer_start(sc->sc_xfer[UPLCOM_BULK_DT_WR]); 717184610Salfred} 718184610Salfred 719184610Salfredstatic void 720192984Sthompsauplcom_stop_write(struct ucom_softc *ucom) 721184610Salfred{ 722184610Salfred struct uplcom_softc *sc = ucom->sc_parent; 723184610Salfred 724194228Sthompsa usbd_transfer_stop(sc->sc_xfer[UPLCOM_BULK_DT_WR]); 725184610Salfred} 726184610Salfred 727184610Salfredstatic void 728192984Sthompsauplcom_cfg_get_status(struct ucom_softc *ucom, uint8_t *lsr, uint8_t *msr) 729184610Salfred{ 730184610Salfred struct uplcom_softc *sc = ucom->sc_parent; 731184610Salfred 732184610Salfred DPRINTF("\n"); 733184610Salfred 734184610Salfred *lsr = sc->sc_lsr; 735184610Salfred *msr = sc->sc_msr; 736184610Salfred} 737184610Salfred 738184610Salfredstatic void 739192984Sthompsauplcom_intr_callback(struct usb_xfer *xfer) 740184610Salfred{ 741184610Salfred struct uplcom_softc *sc = xfer->priv_sc; 742184610Salfred uint8_t buf[9]; 743184610Salfred 744184610Salfred switch (USB_GET_STATE(xfer)) { 745184610Salfred case USB_ST_TRANSFERRED: 746184610Salfred 747184610Salfred DPRINTF("actlen = %u\n", xfer->actlen); 748184610Salfred 749184610Salfred if (xfer->actlen >= 9) { 750184610Salfred 751194228Sthompsa usbd_copy_out(xfer->frbuffers, 0, buf, sizeof(buf)); 752184610Salfred 753184610Salfred DPRINTF("status = 0x%02x\n", buf[8]); 754184610Salfred 755184610Salfred sc->sc_lsr = 0; 756184610Salfred sc->sc_msr = 0; 757184610Salfred 758184610Salfred if (buf[8] & RSAQ_STATUS_CTS) { 759184610Salfred sc->sc_msr |= SER_CTS; 760184610Salfred } 761184610Salfred if (buf[8] & RSAQ_STATUS_DSR) { 762184610Salfred sc->sc_msr |= SER_DSR; 763184610Salfred } 764184610Salfred if (buf[8] & RSAQ_STATUS_DCD) { 765184610Salfred sc->sc_msr |= SER_DCD; 766184610Salfred } 767194228Sthompsa ucom_status_change(&sc->sc_ucom); 768184610Salfred } 769184610Salfred case USB_ST_SETUP: 770188413Sthompsatr_setup: 771188413Sthompsa xfer->frlengths[0] = xfer->max_data_length; 772194228Sthompsa usbd_transfer_submit(xfer); 773184610Salfred return; 774184610Salfred 775184610Salfred default: /* Error */ 776184610Salfred if (xfer->error != USB_ERR_CANCELLED) { 777188413Sthompsa /* try to clear stall first */ 778188413Sthompsa xfer->flags.stall_pipe = 1; 779188413Sthompsa goto tr_setup; 780184610Salfred } 781184610Salfred return; 782184610Salfred } 783184610Salfred} 784184610Salfred 785184610Salfredstatic void 786192984Sthompsauplcom_write_callback(struct usb_xfer *xfer) 787184610Salfred{ 788184610Salfred struct uplcom_softc *sc = xfer->priv_sc; 789184610Salfred uint32_t actlen; 790184610Salfred 791184610Salfred switch (USB_GET_STATE(xfer)) { 792184610Salfred case USB_ST_SETUP: 793184610Salfred case USB_ST_TRANSFERRED: 794188413Sthompsatr_setup: 795194228Sthompsa if (ucom_get_data(&sc->sc_ucom, xfer->frbuffers, 0, 796184610Salfred UPLCOM_BULK_BUF_SIZE, &actlen)) { 797184610Salfred 798184610Salfred DPRINTF("actlen = %d\n", actlen); 799184610Salfred 800184610Salfred xfer->frlengths[0] = actlen; 801194228Sthompsa usbd_transfer_submit(xfer); 802184610Salfred } 803184610Salfred return; 804184610Salfred 805184610Salfred default: /* Error */ 806184610Salfred if (xfer->error != USB_ERR_CANCELLED) { 807188413Sthompsa /* try to clear stall first */ 808188413Sthompsa xfer->flags.stall_pipe = 1; 809188413Sthompsa goto tr_setup; 810184610Salfred } 811184610Salfred return; 812184610Salfred } 813184610Salfred} 814184610Salfred 815184610Salfredstatic void 816192984Sthompsauplcom_read_callback(struct usb_xfer *xfer) 817184610Salfred{ 818184610Salfred struct uplcom_softc *sc = xfer->priv_sc; 819184610Salfred 820184610Salfred switch (USB_GET_STATE(xfer)) { 821184610Salfred case USB_ST_TRANSFERRED: 822194228Sthompsa ucom_put_data(&sc->sc_ucom, xfer->frbuffers, 0, xfer->actlen); 823184610Salfred 824184610Salfred case USB_ST_SETUP: 825188413Sthompsatr_setup: 826188413Sthompsa xfer->frlengths[0] = xfer->max_data_length; 827194228Sthompsa usbd_transfer_submit(xfer); 828184610Salfred return; 829184610Salfred 830184610Salfred default: /* Error */ 831184610Salfred if (xfer->error != USB_ERR_CANCELLED) { 832188413Sthompsa /* try to clear stall first */ 833188413Sthompsa xfer->flags.stall_pipe = 1; 834188413Sthompsa goto tr_setup; 835184610Salfred } 836184610Salfred return; 837184610Salfred } 838184610Salfred} 839