From owner-p4-projects@FreeBSD.ORG Thu Aug 31 12:44:38 2006 Return-Path: X-Original-To: p4-projects@freebsd.org Delivered-To: p4-projects@freebsd.org Received: by hub.freebsd.org (Postfix, from userid 32767) id 572F916A4E5; Thu, 31 Aug 2006 12:44:38 +0000 (UTC) X-Original-To: perforce@FreeBSD.org Delivered-To: perforce@FreeBSD.org Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125]) by hub.freebsd.org (Postfix) with ESMTP id 0C86116A4DA for ; Thu, 31 Aug 2006 12:44:38 +0000 (UTC) (envelope-from hselasky@FreeBSD.org) Received: from repoman.freebsd.org (repoman.freebsd.org [216.136.204.115]) by mx1.FreeBSD.org (Postfix) with ESMTP id AB4E643D45 for ; Thu, 31 Aug 2006 12:44:37 +0000 (GMT) (envelope-from hselasky@FreeBSD.org) Received: from repoman.freebsd.org (localhost [127.0.0.1]) by repoman.freebsd.org (8.13.6/8.13.6) with ESMTP id k7VCibMf030533 for ; Thu, 31 Aug 2006 12:44:37 GMT (envelope-from hselasky@FreeBSD.org) Received: (from perforce@localhost) by repoman.freebsd.org (8.13.6/8.13.4/Submit) id k7VCibG5030530 for perforce@freebsd.org; Thu, 31 Aug 2006 12:44:37 GMT (envelope-from hselasky@FreeBSD.org) Date: Thu, 31 Aug 2006 12:44:37 GMT Message-Id: <200608311244.k7VCibG5030530@repoman.freebsd.org> X-Authentication-Warning: repoman.freebsd.org: perforce set sender to hselasky@FreeBSD.org using -f From: Hans Petter Selasky To: Perforce Change Reviews Cc: Subject: PERFORCE change 105399 for review X-BeenThere: p4-projects@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: p4 projects tree changes List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 31 Aug 2006 12:44:38 -0000 http://perforce.freebsd.org/chv.cgi?CH=105399 Change 105399 by hselasky@hselasky_mini_itx on 2006/08/31 12:44:19 Finished reworking "uftdi". Please test! Affected files ... .. //depot/projects/usb/src/sys/dev/usb/uftdi.c#4 edit .. //depot/projects/usb/src/sys/dev/usb/uftdireg.h#3 add Differences ... ==== //depot/projects/usb/src/sys/dev/usb/uftdi.c#4 (text+ko) ==== @@ -1,0 +1,1087 @@ +/* $NetBSD: uftdi.c,v 1.13 2002/09/23 05:51:23 simonb Exp $ */ + +/*- + * Copyright (c) 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Lennart Augustsson (lennart@augustsson.net). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * NOTE: all function names beginning like "uftdi_cfg_" can only + * be called from within the config thread function ! + */ + +/* + * FTDI FT8U100AX serial adapter driver + */ + +#include +#include +#include +#include +#include +#include + +#define usbd_config_td_cc uftdi_config_copy +#define usbd_config_td_softc uftdi_softc + +#include +#include +#include +#include +#include + +#include +#include + +#include "usbdevs.h" + +__FBSDID("$FreeBSD: src/sys/dev/usb/uftdi.c,v 1.22 2005/04/05 22:09:18 ticso Exp $"); + +#ifdef USB_DEBUG +#define DPRINTF(sc,n,fmt,...) \ + do { if (uftdi_debug > (n)) { \ + printf("%s: %s: " fmt, (sc)->sc_name, \ + __FUNCTION__,## __VA_ARGS__); } } while (0) + +static int uftdi_debug = 0; +SYSCTL_NODE(_hw_usb, OID_AUTO, uftdi, CTLFLAG_RW, 0, "USB uftdi"); +SYSCTL_INT(_hw_usb_uftdi, OID_AUTO, debug, CTLFLAG_RW, + &uftdi_debug, 0, "uftdi debug level"); +#else +#define DPRINTF(...) +#endif + +#define UFTDI_CONFIG_INDEX 0 +#define UFTDI_IFACE_INDEX 0 +#define UFTDI_ENDPT_MAX 4 + +/* + * These are the maximum number of bytes transferred per frame. + * The output buffer size cannot be increased due to the size encoding. + */ +#define UFTDI_IBUFSIZE 64 +#define UFTDI_OBUFSIZE 64 + +struct uftdi_softc { + struct ucom_softc sc_ucom; + struct usbd_config_td sc_config_td; + struct usbd_memory_wait sc_mem_wait; + + struct usbd_device * sc_udev; + struct usbd_xfer * sc_xfer[UFTDI_ENDPT_MAX]; + device_t sc_dev; + + u_int32_t sc_unit; + enum uftdi_type sc_type; + + u_int16_t sc_last_lcr; + u_int16_t sc_rate; + + u_int8_t sc_iface_index; + u_int8_t sc_hdrlen; + + u_int8_t sc_msr; + u_int8_t sc_lsr; + + u_int8_t sc_dtr_onoff; + u_int8_t sc_rts_onoff; + u_int8_t sc_break_onoff; + + u_int8_t sc_flow; + u_int8_t sc_flow_v_start; + u_int8_t sc_flow_v_stop; + + u_int8_t sc_flag; +#define UFTDI_FLAG_WRITE_STALL 0x01 +#define UFTDI_FLAG_READ_STALL 0x02 + + u_int8_t sc_name[16]; +}; + +struct uftdi_config_copy { + u_int16_t last_lcr; + u_int16_t rate; + + u_int8_t dtr_onoff; + u_int8_t rts_onoff; + u_int8_t break_onoff; + + u_int8_t flow; + u_int8_t v_start; + u_int8_t v_stop; +}; + +/* prototypes */ + +static device_probe_t uftdi_probe; +static device_attach_t uftdi_attach; +static device_detach_t uftdi_detach; + +static void +uftdi_config_copy(struct uftdi_softc *sc, + struct uftdi_config_copy *cc, u_int16_t refcount); +static void +uftdi_cfg_do_request(struct uftdi_softc *sc, usb_device_request_t *req, + void *data); +static int +uftdi_open(struct ucom_softc *ucom); + +static void +uftdi_cfg_open(struct uftdi_softc *sc, + struct uftdi_config_copy *cc, u_int16_t refcount); +static void +uftdi_write_callback(struct usbd_xfer *xfer); + +static void +uftdi_write_clear_stall_callback(struct usbd_xfer *xfer); + +static void +uftdi_read_callback(struct usbd_xfer *xfer); + +static void +uftdi_read_clear_stall_callback(struct usbd_xfer *xfer); + +static void +uftdi_set_dtr(struct ucom_softc *ucom, u_int8_t onoff); + +static void +uftdi_cfg_set_dtr(struct uftdi_softc *sc, + struct uftdi_config_copy *cc, u_int16_t refcount); +static void +uftdi_set_rts(struct ucom_softc *ucom, u_int8_t onoff); + +static void +uftdi_cfg_set_rts(struct uftdi_softc *sc, + struct uftdi_config_copy *cc, u_int16_t refcount); +static void +uftdi_set_break(struct ucom_softc *ucom, u_int8_t onoff); + +static void +uftdi_cfg_set_break(struct uftdi_softc *sc, + struct uftdi_config_copy *cc, u_int16_t refcount); +static int +uftdi_set_parm_soft(struct uftdi_softc *sc, u_int32_t ospeed, + u_int8_t v_start, u_int8_t v_stop, + u_int32_t cflag, u_int32_t iflag); +static int +uftdi_param(struct ucom_softc *ucom, struct termios *t); + +static void +uftdi_cfg_parm(struct uftdi_softc *sc, + struct uftdi_config_copy *cc, u_int16_t refcount); +static void +uftdi_get_status(struct ucom_softc *ucom, u_int8_t *lsr, u_int8_t *msr); + +static void +uftdi_start_read(struct ucom_softc *ucom); + +static void +uftdi_stop_read(struct ucom_softc *ucom); + +static void +uftdi_start_write(struct ucom_softc *ucom); + +static void +uftdi_stop_write(struct ucom_softc *ucom); + +static const struct usbd_config uftdi_config[UFTDI_ENDPT_MAX] = { + + [0] = { + .type = UE_BULK, + .endpoint = -1, /* any */ + .direction = UE_DIR_OUT, + .bufsize = UFTDI_OBUFSIZE, + .flags = 0, + .callback = &uftdi_write_callback, + }, + + [1] = { + .type = UE_BULK, + .endpoint = -1, /* any */ + .direction = UE_DIR_IN, + .bufsize = UFTDI_IBUFSIZE, + .flags = USBD_SHORT_XFER_OK, + .callback = &uftdi_read_callback, + }, + + [2] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = -1, + .bufsize = sizeof(usb_device_request_t), + .flags = (USBD_USE_DMA), + .callback = &uftdi_write_clear_stall_callback, + .timeout = 1000, /* 1 second */ + }, + + [3] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* Control pipe */ + .direction = -1, + .bufsize = sizeof(usb_device_request_t), + .flags = (USBD_USE_DMA), + .callback = &uftdi_read_clear_stall_callback, + .timeout = 1000, /* 1 second */ + }, +}; + +static const struct ucom_callback uftdi_callback = { + .ucom_get_status = &uftdi_get_status, + .ucom_set_dtr = &uftdi_set_dtr, + .ucom_set_rts = &uftdi_set_rts, + .ucom_set_break = &uftdi_set_break, + .ucom_param = &uftdi_param, + .ucom_open = &uftdi_open, + .ucom_start_read = &uftdi_start_read, + .ucom_stop_read = &uftdi_stop_read, + .ucom_start_write = &uftdi_start_write, + .ucom_stop_write = &uftdi_stop_write, +}; + +static device_method_t uftdi_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, uftdi_probe), + DEVMETHOD(device_attach, uftdi_attach), + DEVMETHOD(device_detach, uftdi_detach), + + { 0, 0 } +}; + +static driver_t uftdi_driver = { + "ucom", + uftdi_methods, + sizeof (struct uftdi_softc) +}; + +DRIVER_MODULE(uftdi, uhub, uftdi_driver, ucom_devclass, usbd_driver_load, 0); +MODULE_DEPEND(uftdi, usb, 1, 1, 1); +MODULE_DEPEND(uftdi, ucom, UCOM_MINVER, UCOM_PREFVER, UCOM_MAXVER); + +static int +uftdi_probe(device_t dev) +{ + struct usb_attach_arg *uaa = device_get_ivars(dev); + + if (uaa->iface != NULL) { + if ((uaa->vendor == USB_VENDOR_FTDI) && + (uaa->product == USB_PRODUCT_FTDI_SERIAL_2232C)) { + return UMATCH_VENDOR_IFACESUBCLASS; + } + return UMATCH_NONE; + } + + if ((uaa->vendor == USB_VENDOR_FTDI) && + ((uaa->product == USB_PRODUCT_FTDI_SERIAL_8U100AX) || + (uaa->product == USB_PRODUCT_FTDI_SERIAL_8U232AM) || + (uaa->product == USB_PRODUCT_FTDI_SEMC_DSS20) || + (uaa->product == USB_PRODUCT_FTDI_CFA_631) || + (uaa->product == USB_PRODUCT_FTDI_CFA_632) || + (uaa->product == USB_PRODUCT_FTDI_CFA_633) || + (uaa->product == USB_PRODUCT_FTDI_CFA_634) || + (uaa->product == USB_PRODUCT_FTDI_USBSERIAL) || + (uaa->product == USB_PRODUCT_FTDI_MX2_3) || + (uaa->product == USB_PRODUCT_FTDI_MX4_5) || + (uaa->product == USB_PRODUCT_FTDI_LK202) || + (uaa->product == USB_PRODUCT_FTDI_LK204))) { + return UMATCH_VENDOR_PRODUCT; + } + + if ((uaa->vendor == USB_VENDOR_SIIG2) && + (uaa->product == USB_PRODUCT_SIIG2_US2308)) { + return UMATCH_VENDOR_PRODUCT; + } + + if ((uaa->vendor == USB_VENDOR_INTREPIDCS) && + ((uaa->product == USB_PRODUCT_INTREPIDCS_VALUECAN) || + (uaa->product == USB_PRODUCT_INTREPIDCS_NEOVI))) { + return UMATCH_VENDOR_PRODUCT; + } + + if ((uaa->vendor == USB_VENDOR_BBELECTRONICS) && + (uaa->product == USB_PRODUCT_BBELECTRONICS_USOTL4)) { + return UMATCH_VENDOR_PRODUCT; + } + + return (UMATCH_NONE); +} + +static int +uftdi_attach(device_t dev) +{ + struct usb_attach_arg *uaa = device_get_ivars(dev); + struct uftdi_softc *sc = device_get_softc(dev); + usb_interface_descriptor_t *id; + int32_t error; + + if (sc == NULL) { + return ENOMEM; + } + + sc->sc_udev = uaa->device; + sc->sc_dev = dev; + sc->sc_unit = device_get_unit(dev); + + usbd_set_desc(dev, uaa->device); + + snprintf(sc->sc_name, sizeof(sc->sc_name), + "%s", device_get_nameunit(dev)); + + DPRINTF(sc, 0, "\n"); + + if (uaa->iface == NULL) { + + error = usbd_set_config_index(uaa->device, UFTDI_CONFIG_INDEX, 1); + + if (error) { + device_printf(dev, "failed to set configuration, " + "error=%s\n", usbd_errstr(error)); + goto detach; + } + + sc->sc_iface_index = UFTDI_IFACE_INDEX; + } else { + sc->sc_iface_index = uaa->iface_index; + } + + switch( uaa->vendor ) { + case USB_VENDOR_FTDI: + switch( uaa->product ){ + case USB_PRODUCT_FTDI_SERIAL_8U100AX: + sc->sc_type = UFTDI_TYPE_SIO; + sc->sc_hdrlen = 1; + break; + + case USB_PRODUCT_FTDI_SEMC_DSS20: + case USB_PRODUCT_FTDI_SERIAL_8U232AM: + case USB_PRODUCT_FTDI_SERIAL_2232C: + case USB_PRODUCT_FTDI_CFA_631: + case USB_PRODUCT_FTDI_CFA_632: + case USB_PRODUCT_FTDI_CFA_633: + case USB_PRODUCT_FTDI_CFA_634: + case USB_PRODUCT_FTDI_USBSERIAL: + case USB_PRODUCT_FTDI_MX2_3: + case USB_PRODUCT_FTDI_MX4_5: + case USB_PRODUCT_FTDI_LK202: + case USB_PRODUCT_FTDI_LK204: + sc->sc_type = UFTDI_TYPE_8U232AM; + sc->sc_hdrlen = 0; + break; + + default: /* Can't happen */ + goto detach; + } + break; + + case USB_VENDOR_INTREPIDCS: + switch( uaa->product ){ + case USB_PRODUCT_INTREPIDCS_VALUECAN: + case USB_PRODUCT_INTREPIDCS_NEOVI: + sc->sc_type = UFTDI_TYPE_8U232AM; + sc->sc_hdrlen = 0; + break; + + default: /* Can't happen */ + goto detach; + } + break; + + case USB_VENDOR_SIIG2: + switch( uaa->product ){ + case USB_PRODUCT_SIIG2_US2308: + sc->sc_type = UFTDI_TYPE_8U232AM; + sc->sc_hdrlen = 0; + break; + + default: /* Can't happen */ + goto detach; + } + break; + + case USB_VENDOR_BBELECTRONICS: + switch( uaa->product ){ + case USB_PRODUCT_BBELECTRONICS_USOTL4: + sc->sc_type = UFTDI_TYPE_8U232AM; + sc->sc_hdrlen = 0; + break; + + default: /* Can't happen */ + goto detach; + } + break; + + default: /* Can't happen */ + goto detach; + } + + error = usbd_transfer_setup(uaa->device, sc->sc_iface_index, + sc->sc_xfer, uftdi_config, UFTDI_ENDPT_MAX, + sc, &Giant, &(sc->sc_mem_wait)); + if (error) { + device_printf(dev, "allocating USB " + "transfers failed!\n"); + goto detach; + } + + error = usbd_config_td_setup(&(sc->sc_config_td), sc, &Giant, + &uftdi_config_copy, NULL, + sizeof(struct uftdi_config_copy), 16); + if (error) { + device_printf(dev, "could not setup config " + "thread!\n"); + goto detach; + } + + sc->sc_ucom.sc_parent = sc; + sc->sc_ucom.sc_portno = FTDI_PIT_SIOA; + sc->sc_ucom.sc_callback = &uftdi_callback; + + if (uaa->iface) { + id = usbd_get_interface_descriptor(uaa->iface); + + if (id == NULL) { + goto detach; + } + + sc->sc_ucom.sc_portno += id->bInterfaceNumber; + } + + error = ucom_attach(&(sc->sc_ucom), dev); + + if (error) { + goto detach; + } + + return 0; /* success */ + + detach: + uftdi_detach(dev); + return ENXIO; +} + +static int +uftdi_detach(device_t dev) +{ + struct uftdi_softc *sc = device_get_softc(dev); + + mtx_lock(&Giant); + + usbd_config_td_stop(&(sc->sc_config_td)); + + mtx_unlock(&Giant); + + ucom_detach(&(sc->sc_ucom)); + + usbd_transfer_unsetup(sc->sc_xfer, UFTDI_ENDPT_MAX); + + usbd_transfer_drain(&(sc->sc_mem_wait), &Giant); + + usbd_config_td_unsetup(&(sc->sc_config_td)); + + return 0; +} + +static void +uftdi_config_copy(struct uftdi_softc *sc, + struct uftdi_config_copy *cc, u_int16_t refcount) +{ + bzero(cc, sizeof(*cc)); + + cc->dtr_onoff = sc->sc_dtr_onoff; + cc->rts_onoff = sc->sc_rts_onoff; + cc->break_onoff = sc->sc_break_onoff; + cc->last_lcr = sc->sc_last_lcr; + cc->rate = sc->sc_rate; + cc->v_stop = sc->sc_flow_v_stop; + cc->v_start = sc->sc_flow_v_start; + cc->flow = sc->sc_flow; + + return; +} + +static void +uftdi_cfg_do_request(struct uftdi_softc *sc, usb_device_request_t *req, + void *data) +{ + u_int16_t length; + usbd_status err; + + if (usbd_config_td_is_gone(&(sc->sc_config_td))) { + goto error; + } + + err = usbd_do_request_flags_mtx(sc->sc_udev, &Giant, req, + data, 0, NULL, 1000); + + if (err) { + + DPRINTF(sc, 0, "device request failed, err=%s " + "(ignored)\n", usbd_errstr(err)); + + error: + length = UGETW(req->wLength); + + if ((req->bmRequestType & UT_READ) && length) { + bzero(data, length); + } + } + return; +} + +static int +uftdi_open(struct ucom_softc *ucom) +{ + struct uftdi_softc *sc = ucom->sc_parent; + + sc->sc_flag |= (UFTDI_FLAG_WRITE_STALL| + UFTDI_FLAG_READ_STALL); + + usbd_config_td_queue_command + (&(sc->sc_config_td), &uftdi_cfg_open, 0); + + return 0; +} + +static void +uftdi_cfg_open(struct uftdi_softc *sc, + struct uftdi_config_copy *cc, u_int16_t refcount) +{ + u_int16_t wIndex = sc->sc_ucom.sc_portno; + usb_device_request_t req; + + if (cc == NULL) { + + /* set 9600 baud, 2 stop bits, + * no parity, 8 bits + */ + (void) uftdi_set_parm_soft + (sc, 9600, 0, 0, CSTOPB | CS8, 0); + + return; + } + + DPRINTF(sc, 0, ""); + + /* perform a full reset on the device */ + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = FTDI_SIO_RESET; + USETW(req.wValue, FTDI_SIO_RESET_SIO); + USETW(req.wIndex, wIndex); + USETW(req.wLength, 0); + uftdi_cfg_do_request(sc, &req, NULL); + + /* write parameters */ + + uftdi_cfg_parm(sc, cc, 0); + + /* turn on RTS/CTS flow control */ + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = FTDI_SIO_SET_FLOW_CTRL; + USETW(req.wValue, 0); + USETW2(req.wIndex, FTDI_SIO_RTS_CTS_HS, wIndex); + USETW(req.wLength, 0); + uftdi_cfg_do_request(sc, &req, NULL); + + return; +} + +static void +uftdi_write_callback(struct usbd_xfer *xfer) +{ + struct uftdi_softc *sc = xfer->priv_sc; + u_int32_t actlen; + + USBD_CHECK_STATUS(xfer); + +tr_error: + if (xfer->error != USBD_CANCELLED) { + sc->sc_flag |= UFTDI_FLAG_WRITE_STALL; + usbd_transfer_start(sc->sc_xfer[2]); + } + return; + +tr_setup: +tr_transferred: + if (sc->sc_flag & UFTDI_FLAG_WRITE_STALL) { + usbd_transfer_start(sc->sc_xfer[2]); + return; + } + + if(ucom_get_data(&(sc->sc_ucom), ((u_int8_t *)(xfer->buffer)) + + sc->sc_hdrlen, UFTDI_OBUFSIZE - sc->sc_hdrlen, + &actlen)) { + + if (sc->sc_hdrlen > 0) { + *(u_int8_t *)(xfer->buffer) = + FTDI_OUT_TAG(actlen, sc->sc_ucom.sc_portno); + } + + xfer->length = actlen + sc->sc_hdrlen; + + usbd_start_hardware(xfer); + } + return; +} + +static void +uftdi_write_clear_stall_callback(struct usbd_xfer *xfer) +{ + struct uftdi_softc *sc = xfer->priv_sc; + + USBD_CHECK_STATUS(xfer); + + tr_setup: + /* start clear stall */ + usbd_clear_stall_tr_setup(xfer, sc->sc_xfer[0]); + return; + + tr_transferred: + usbd_clear_stall_tr_transferred(xfer, sc->sc_xfer[0]); + sc->sc_flag &= ~UFTDI_FLAG_WRITE_STALL; + usbd_transfer_start(sc->sc_xfer[0]); + return; + + tr_error: + sc->sc_flag &= ~UFTDI_FLAG_WRITE_STALL; + DPRINTF(sc, 0, "clear stall failed, error=%s\n", + usbd_errstr(xfer->error)); + return; +} + +static void +uftdi_read_callback(struct usbd_xfer *xfer) +{ + struct uftdi_softc *sc = xfer->priv_sc; + u_int8_t *ptr = xfer->buffer; + u_int8_t msr; + u_int8_t lsr; + + USBD_CHECK_STATUS(xfer); + + tr_error: + if (xfer->error != USBD_CANCELLED) { + sc->sc_flag |= UFTDI_FLAG_READ_STALL; + usbd_transfer_start(sc->sc_xfer[3]); + } + return; + + tr_transferred: + + if (xfer->actlen < 2) { + goto tr_setup; + } + + msr = FTDI_GET_MSR(ptr); + lsr = FTDI_GET_LSR(ptr); + + if ((sc->sc_msr != msr) || + ((sc->sc_lsr & FTDI_LSR_MASK) != (lsr & FTDI_LSR_MASK))) { + DPRINTF(sc, 0, "status change msr=0x%02x (0x%02x) " + "lsr=0x%02x (0x%02x)\n", msr, sc->sc_msr, + lsr, sc->sc_lsr); + + sc->sc_msr = msr; + sc->sc_lsr = lsr; + + ucom_status_change(&(sc->sc_ucom)); + } + + xfer->actlen -= 2; + ptr += 2; + + if (xfer->actlen) { + ucom_put_data(&(sc->sc_ucom), ptr, xfer->actlen); + } + + tr_setup: + if (sc->sc_flag & UFTDI_FLAG_READ_STALL) { + usbd_transfer_start(sc->sc_xfer[3]); + } else { + usbd_start_hardware(xfer); + } + return; +} + +static void +uftdi_read_clear_stall_callback(struct usbd_xfer *xfer) +{ + struct uftdi_softc *sc = xfer->priv_sc; + + USBD_CHECK_STATUS(xfer); + + tr_setup: + /* start clear stall */ + usbd_clear_stall_tr_setup(xfer, sc->sc_xfer[1]); + return; + + tr_transferred: + usbd_clear_stall_tr_transferred(xfer, sc->sc_xfer[1]); + sc->sc_flag &= ~UFTDI_FLAG_READ_STALL; + usbd_transfer_start(sc->sc_xfer[1]); + return; + + tr_error: + sc->sc_flag &= ~UFTDI_FLAG_READ_STALL; + DPRINTF(sc, 0, "clear stall failed, error=%s\n", + usbd_errstr(xfer->error)); + return; +} + +static void +uftdi_set_dtr(struct ucom_softc *ucom, u_int8_t onoff) +{ + struct uftdi_softc *sc = ucom->sc_parent; + + sc->sc_dtr_onoff = onoff; + + usbd_config_td_queue_command + (&(sc->sc_config_td), &uftdi_cfg_set_dtr, 0); + + return; +} + +static void +uftdi_cfg_set_dtr(struct uftdi_softc *sc, + struct uftdi_config_copy *cc, u_int16_t refcount) +{ + u_int16_t wIndex = sc->sc_ucom.sc_portno; + u_int16_t wValue; + usb_device_request_t req; + + if (cc == NULL) { + /* nothing to do */ + return; + } + + wValue = cc->dtr_onoff ? FTDI_SIO_SET_DTR_HIGH : FTDI_SIO_SET_DTR_LOW; + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = FTDI_SIO_MODEM_CTRL; + USETW(req.wValue, wValue); + USETW(req.wIndex, wIndex); + USETW(req.wLength, 0); + uftdi_cfg_do_request(sc, &req, NULL); + + return; +} + +static void +uftdi_set_rts(struct ucom_softc *ucom, u_int8_t onoff) +{ + struct uftdi_softc *sc = ucom->sc_parent; + + sc->sc_rts_onoff = onoff; + + usbd_config_td_queue_command + (&(sc->sc_config_td), &uftdi_cfg_set_rts, 0); + + return; +} + +static void +uftdi_cfg_set_rts(struct uftdi_softc *sc, + struct uftdi_config_copy *cc, u_int16_t refcount) +{ + u_int16_t wIndex = sc->sc_ucom.sc_portno; + u_int16_t wValue; + usb_device_request_t req; + + if (cc == NULL) { + /* nothing to do */ + return; + } + + wValue = cc->rts_onoff ? FTDI_SIO_SET_RTS_HIGH : FTDI_SIO_SET_RTS_LOW; + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = FTDI_SIO_MODEM_CTRL; + USETW(req.wValue, wValue); + USETW(req.wIndex, wIndex); + USETW(req.wLength, 0); + uftdi_cfg_do_request(sc, &req, NULL); + + return; +} + +static void +uftdi_set_break(struct ucom_softc *ucom, u_int8_t onoff) +{ + struct uftdi_softc *sc = ucom->sc_parent; + + sc->sc_break_onoff = onoff; + + usbd_config_td_queue_command + (&(sc->sc_config_td), &uftdi_cfg_set_break, 0); + + return; +} + +static void +uftdi_cfg_set_break(struct uftdi_softc *sc, + struct uftdi_config_copy *cc, u_int16_t refcount) +{ + u_int16_t wIndex = sc->sc_ucom.sc_portno; + u_int16_t wValue; + usb_device_request_t req; + + if (cc == NULL) { + /* nothing to do */ + return; + } + + if (cc->break_onoff) { + wValue = cc->last_lcr | FTDI_SIO_SET_BREAK; + } else { + wValue = cc->last_lcr; + } + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = FTDI_SIO_SET_DATA; + USETW(req.wValue, wValue); + USETW(req.wIndex, wIndex); + USETW(req.wLength, 0); + uftdi_cfg_do_request(sc, &req, NULL); + + return; +} + +static int +uftdi_set_parm_soft(struct uftdi_softc *sc, u_int32_t ospeed, + u_int8_t v_start, u_int8_t v_stop, + u_int32_t cflag, u_int32_t iflag) +{ + u_int16_t rate = 0; + u_int16_t data = 0; + u_int8_t flow = 0; + + switch (sc->sc_type) { + case UFTDI_TYPE_SIO: + switch (ospeed) { + case 300: rate = ftdi_sio_b300; break; + case 600: rate = ftdi_sio_b600; break; + case 1200: rate = ftdi_sio_b1200; break; + case 2400: rate = ftdi_sio_b2400; break; + case 4800: rate = ftdi_sio_b4800; break; + case 9600: rate = ftdi_sio_b9600; break; + case 19200: rate = ftdi_sio_b19200; break; + case 38400: rate = ftdi_sio_b38400; break; + case 57600: rate = ftdi_sio_b57600; break; + case 115200: rate = ftdi_sio_b115200; break; + default: + return (EINVAL); + } + break; + + case UFTDI_TYPE_8U232AM: + switch(ospeed) { + case 300: rate = ftdi_8u232am_b300; break; + case 600: rate = ftdi_8u232am_b600; break; + case 1200: rate = ftdi_8u232am_b1200; break; + case 2400: rate = ftdi_8u232am_b2400; break; + case 4800: rate = ftdi_8u232am_b4800; break; + case 9600: rate = ftdi_8u232am_b9600; break; + case 19200: rate = ftdi_8u232am_b19200; break; + case 38400: rate = ftdi_8u232am_b38400; break; + case 57600: rate = ftdi_8u232am_b57600; break; + case 115200: rate = ftdi_8u232am_b115200; break; + case 230400: rate = ftdi_8u232am_b230400; break; + case 460800: rate = ftdi_8u232am_b460800; break; + case 921600: rate = ftdi_8u232am_b921600; break; + case 2000000: rate = ftdi_8u232am_b2000000; break; + case 3000000: rate = ftdi_8u232am_b3000000; break; + default: + return (EINVAL); + } + break; + } + sc->sc_rate = rate; + + if (cflag & CSTOPB) + data = FTDI_SIO_SET_DATA_STOP_BITS_2; + else + data = FTDI_SIO_SET_DATA_STOP_BITS_1; + + if (cflag & PARENB) { + if (cflag & PARODD) { + data |= FTDI_SIO_SET_DATA_PARITY_ODD; + } else { + data |= FTDI_SIO_SET_DATA_PARITY_EVEN; + } + } else { + data |= FTDI_SIO_SET_DATA_PARITY_NONE; + } + + switch (cflag & CSIZE) { + case CS5: + data |= FTDI_SIO_SET_DATA_BITS(5); + break; + + case CS6: + data |= FTDI_SIO_SET_DATA_BITS(6); + break; + + case CS7: + data |= FTDI_SIO_SET_DATA_BITS(7); + break; + + case CS8: + data |= FTDI_SIO_SET_DATA_BITS(8); + break; + } + sc->sc_last_lcr = data; + + if (cflag & CRTSCTS) { + flow = FTDI_SIO_RTS_CTS_HS; + v_start = 0; + v_stop = 0; + } else if (iflag & (IXON|IXOFF)) { + flow = FTDI_SIO_XON_XOFF_HS; + } else { + flow = FTDI_SIO_DISABLE_FLOW_CTRL; + v_start = 0; + v_stop = 0; + } + + sc->sc_flow = flow; + sc->sc_flow_v_start = v_start; + sc->sc_flow_v_stop = v_stop; + return 0; +} + +static int +uftdi_param(struct ucom_softc *ucom, struct termios *t) +{ + struct uftdi_softc *sc = ucom->sc_parent; >>> TRUNCATED FOR MAIL (1000 lines) <<<