diff options
| author | skrll <skrll@NetBSD.org> | 2005-07-30 06:14:49 +0000 |
|---|---|---|
| committer | skrll <skrll@NetBSD.org> | 2005-07-30 06:14:49 +0000 |
| commit | acd7c4c5caee14496a6bc5dced36d8cd20992a0d (patch) | |
| tree | 40b3b31a249922dad8630f1619e4497355838429 /sys | |
| parent | 7dab65d6685eb51c23f2b1bb792a1259950298cd (diff) | |
Add a driver for Cypress microcontroller based USB serial adapters.
XXX hw flow control is not supported.
Diffstat (limited to 'sys')
| -rw-r--r-- | sys/conf/majors | 3 | ||||
| -rw-r--r-- | sys/dev/DEVNAMES | 3 | ||||
| -rw-r--r-- | sys/dev/usb/files.usb | 7 | ||||
| -rw-r--r-- | sys/dev/usb/ucycom.c | 1073 | ||||
| -rw-r--r-- | sys/dev/usb/usb_port.h | 3 |
5 files changed, 1085 insertions, 4 deletions
diff --git a/sys/conf/majors b/sys/conf/majors index 25e9b904cda..ae736866078 100644 --- a/sys/conf/majors +++ b/sys/conf/majors @@ -1,4 +1,4 @@ -# $NetBSD: majors,v 1.15 2005/07/11 15:37:04 kiyohara Exp $ +# $NetBSD: majors,v 1.16 2005/07/30 06:14:50 skrll Exp $ # # Device majors for Machine-Independent drivers. # @@ -21,3 +21,4 @@ device-major dk char 168 block 168 device-major tap char 169 tap device-major veriexec char 170 veriexec device-major fw char 171 ieee1394if +device-major ucycom char 172 ucycom diff --git a/sys/dev/DEVNAMES b/sys/dev/DEVNAMES index a580918640c..9a41fb00a3c 100644 --- a/sys/dev/DEVNAMES +++ b/sys/dev/DEVNAMES @@ -1,4 +1,4 @@ -# $NetBSD: DEVNAMES,v 1.185 2005/07/18 11:42:28 augustss Exp $ +# $NetBSD: DEVNAMES,v 1.186 2005/07/30 06:14:50 skrll Exp $ # # This file contains all used device names and defined attributes in # alphabetical order. New devices added to the system somewhere should first @@ -1262,6 +1262,7 @@ ucbsnd hpcmips ucbtp hpcmips ucom MI ucombus MI Attribute +ucycom MI uda MI udav MI udsbr MI diff --git a/sys/dev/usb/files.usb b/sys/dev/usb/files.usb index 3a8616b457c..db94e25c8bc 100644 --- a/sys/dev/usb/files.usb +++ b/sys/dev/usb/files.usb @@ -1,4 +1,4 @@ -# $NetBSD: files.usb,v 1.63 2005/07/18 11:42:28 augustss Exp $ +# $NetBSD: files.usb,v 1.64 2005/07/30 06:14:50 skrll Exp $ # # Config file and device description for machine-independent USB code. # Included by ports that need it. Ports that use it must provide @@ -88,6 +88,11 @@ device uep: wsmousedev, tpcalib attach uep at uhub file dev/usb/uep.c uep needs-flag +# Cypress microcontroller based serial adpaters +device ucycom: hid +attach ucycom at uhidbus +file dev/usb/ucycom.c ucycom needs-flag + # Printers device ulpt attach ulpt at uhub diff --git a/sys/dev/usb/ucycom.c b/sys/dev/usb/ucycom.c new file mode 100644 index 00000000000..7803891dbe5 --- /dev/null +++ b/sys/dev/usb/ucycom.c @@ -0,0 +1,1073 @@ +/* $NetBSD: ucycom.c,v 1.1 2005/07/30 06:14:50 skrll Exp $ */ + +/* + * Copyright (c) 2005 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Nick Hudson + * + * 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. + */ +/* + * This code is based on the ucom driver. + */ + +/* + * Device driver for Cypress CY7C637xx and CY7C640/1xx series USB to + * RS232 bridges. + */ + +#include <sys/cdefs.h> +__RCSID("$NetBSD: ucycom.c,v 1.1 2005/07/30 06:14:50 skrll Exp $"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/conf.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/sysctl.h> +#include <sys/tty.h> +#include <sys/file.h> +#include <sys/vnode.h> + + +#include <dev/usb/usb.h> +#include <dev/usb/usbhid.h> + +#include <dev/usb/usbdi.h> +#include <dev/usb/usbdi_util.h> +#include <dev/usb/usbdevs.h> +#include <dev/usb/uhidev.h> +#include <dev/usb/hid.h> + +#include "ioconf.h" + +#undef UCYCOM_DEBUG +#define UCYCOM_DEBUG +#ifdef UCYCOM_DEBUG +#define DPRINTF(x) if (ucycomdebug) logprintf x +#define DPRINTFN(n, x) if (ucycomdebug > (n)) logprintf x +int ucycomdebug = 0; +#else +#define DPRINTF(x) +#define DPRINTFN(n,x) +#endif + + +#define UCYCOMUNIT_MASK 0x3ffff +#define UCYCOMDIALOUT_MASK 0x80000 +#define UCYCOMCALLUNIT_MASK 0x40000 + +#define UCYCOMUNIT(x) (minor(x) & UCYCOMUNIT_MASK) +#define UCYCOMDIALOUT(x) (minor(x) & UCYCOMDIALOUT_MASK) +#define UCYCOMCALLUNIT(x) (minor(x) & UCYCOMCALLUNIT_MASK) + +/* Macros to clear/set/test flags. */ +#define SET(t, f) (t) |= (f) +#define CLR(t, f) (t) &= ~(f) +#define ISSET(t, f) ((t) & (f)) + +/* Configuration Byte */ +#define UCYCOM_RESET 0x80 +#define UCYCOM_PARITY_TYPE_MASK 0x20 +#define UCYCOM_PARITY_ODD 0x20 +#define UCYCOM_PARITY_EVEN 0x00 +#define UCYCOM_PARITY_MASK 0x10 +#define UCYCOM_PARITY_ON 0x10 +#define UCYCOM_PARITY_OFF 0x00 +#define UCYCOM_STOP_MASK 0x08 +#define UCYCOM_STOP_BITS_2 0x08 +#define UCYCOM_STOP_BITS_1 0x00 +#define UCYCOM_DATA_MASK 0x03 +#define UCYCOM_DATA_BITS_8 0x03 +#define UCYCOM_DATA_BITS_7 0x02 +#define UCYCOM_DATA_BITS_6 0x01 +#define UCYCOM_DATA_BITS_5 0x00 + +/* Modem (Input) status byte */ +#define UCYCOM_RI 0x80 +#define UCYCOM_DCD 0x40 +#define UCYCOM_DSR 0x20 +#define UCYCOM_CTS 0x10 +#define UCYCOM_ERROR 0x08 +#define UCYCOM_LMASK 0x07 + +/* Modem (Output) control byte */ +#define UCYCOM_DTR 0x20 +#define UCYCOM_RTS 0x10 +#define UCYCOM_ORESET 0x08 + +struct ucycom_softc { + struct uhidev sc_hdev; + + struct tty *sc_tty; + + /* uhidev parameters */ + size_t sc_flen; /* feature report length */ + size_t sc_ilen; /* input report length */ + size_t sc_olen; /* output report length */ + + uint8_t *sc_obuf; + + /* settings */ + uint32_t sc_baud; + uint8_t sc_cfg; /* Data format */ + uint8_t sc_mcr; /* Modem control */ + uint8_t sc_msr; /* Modem status */ + int sc_swflags; + + /* flags */ + char sc_dying; +}; + +dev_type_open(ucycomopen); +dev_type_close(ucycomclose); +dev_type_read(ucycomread); +dev_type_write(ucycomwrite); +dev_type_ioctl(ucycomioctl); +dev_type_stop(ucycomstop); +dev_type_tty(ucycomtty); +dev_type_poll(ucycompoll); + +const struct cdevsw ucycom_cdevsw = { + ucycomopen, ucycomclose, ucycomread, ucycomwrite, ucycomioctl, + ucycomstop, ucycomtty, ucycompoll, nommap, ttykqfilter, D_TTY +}; + +Static int ucycomparam(struct tty *, struct termios *); +Static void ucycomstart(struct tty *); +Static void ucycom_intr(struct uhidev *, void *, u_int); +Static int ucycom_configure(struct ucycom_softc *, uint32_t, uint8_t); +Static void tiocm_to_ucycom(struct ucycom_softc *, u_long, int); +Static int ucycom_to_tiocm(struct ucycom_softc *); +Static void ucycom_set_status(struct ucycom_softc *); +Static void ucycom_dtr(struct ucycom_softc *, int); +#if 0 +Static void ucycom_rts(struct ucycom_softc *, int); +#endif +Static void ucycom_cleanup(struct ucycom_softc *sc); + +#ifdef UCYCOM_DEBUG +Static void ucycom_get_cfg(struct ucycom_softc *); +#endif + +Static const struct usb_devno ucycom_devs[] = { + { USB_VENDOR_CYPRESS, USB_PRODUCT_CYPRESS_USBRS232 }, + { USB_VENDOR_DELORME, USB_PRODUCT_DELORME_EARTHMATE }, +}; +#define ucycom_lookup(v, p) usb_lookup(ucycom_devs, v, p) + +USB_DECLARE_DRIVER(ucycom); + +int +ucycom_match(struct device *parent, struct cfdata *match, void *aux) +{ + struct uhidev_attach_arg *uha = aux; + + DPRINTF(("ucycom match\n")); + return (ucycom_lookup(uha->uaa->vendor, uha->uaa->product) != NULL ? + UMATCH_VENDOR_PRODUCT : UMATCH_NONE); +} + +void +ucycom_attach(struct device *parent, struct device *self, void *aux) +{ + struct ucycom_softc *sc = (struct ucycom_softc *)self; + struct uhidev_attach_arg *uha = aux; + int size, repid; + void *desc; + + DPRINTF(("ucycom attach: sc = %p\n", sc)); + + sc->sc_hdev.sc_intr = ucycom_intr; + sc->sc_hdev.sc_parent = uha->parent; + sc->sc_hdev.sc_report_id = uha->reportid; + + uhidev_get_report_desc(uha->parent, &desc, &size); + repid = uha->reportid; + sc->sc_ilen = hid_report_size(desc, size, hid_input, repid); + sc->sc_olen = hid_report_size(desc, size, hid_output, repid); + sc->sc_flen = hid_report_size(desc, size, hid_feature, repid); + + printf(": input=%d, output=%d, feature=%d\n", + sc->sc_ilen, sc->sc_olen, sc->sc_flen); + + DPRINTF(("ucycom attach: report_id = %d\n", uha->reportid)); + + sc->sc_msr = sc->sc_mcr = 0; + + /* set up tty */ + sc->sc_tty = ttymalloc(); + sc->sc_tty->t_sc = sc; + sc->sc_tty->t_oproc = ucycomstart; + sc->sc_tty->t_param = ucycomparam; + + DPRINTF(("ucycom_attach: tty_attach %p\n", sc->sc_tty)); + tty_attach(sc->sc_tty); + + DPRINTF(("ucycom_attach: complete\n")); +} + + +int +ucycom_detach(struct device *self, int flags) +{ + struct ucycom_softc *sc = (struct ucycom_softc *)self; + struct tty *tp = sc->sc_tty; + int maj, mn; + int s; + + DPRINTF(("ucycom_detach: sc=%p flags=%d tp=%p\n", sc, flags, tp)); + + sc->sc_dying = 1; + + s = splusb(); + if (tp != NULL) { + CLR(tp->t_state, TS_CARR_ON); + CLR(tp->t_cflag, CLOCAL | MDMBUF); + ttyflush(tp, FREAD|FWRITE); + } + /* Wait for processes to go away. */ + usb_detach_wait(USBDEV(sc->sc_hdev.sc_dev)); + splx(s); + + /* locate the major number */ + maj = cdevsw_lookup_major(&ucycom_cdevsw); + + /* Nuke the vnodes for any open instances. */ + mn = self->dv_unit; + + DPRINTFN(2, ("ucycom_detach: maj=%d mn=%d\n", maj, mn)); + vdevgone(maj, mn, mn, VCHR); + vdevgone(maj, mn | UCYCOMDIALOUT_MASK, mn | UCYCOMDIALOUT_MASK, VCHR); + vdevgone(maj, mn | UCYCOMCALLUNIT_MASK, mn | UCYCOMCALLUNIT_MASK, VCHR); + + /* Detach and free the tty. */ + if (tp != NULL) { + DPRINTF(("ucycom_detach: tty_detach %p\n", tp)); + tty_detach(tp); + ttyfree(tp); + sc->sc_tty = NULL; + } + + return 0; +} + +int +ucycom_activate(device_ptr_t self, enum devact act) +{ + struct ucycom_softc *sc = (struct ucycom_softc *)self; + + DPRINTFN(5,("ucycom_activate: %d\n", act)); + + switch (act) { + case DVACT_ACTIVATE: + return (EOPNOTSUPP); + + case DVACT_DEACTIVATE: + sc->sc_dying = 1; + break; + } + return (0); +} + +#if 0 +void +ucycom_shutdown(struct ucycom_softc *sc) +{ + struct tty *tp = sc->sc_tty; + + DPRINTF(("ucycom_shutdown\n")); + /* + * Hang up if necessary. Wait a bit, so the other side has time to + * notice even if we immediately open the port again. + */ + if (ISSET(tp->t_cflag, HUPCL)) { + ucycom_dtr(sc, 0); + (void)tsleep(sc, TTIPRI, ttclos, hz); + } +} +#endif + +int +ucycomopen(dev_t dev, int flag, int mode, struct proc *p) +{ + int unit = UCYCOMUNIT(dev); + struct ucycom_softc *sc; + struct tty *tp; + int s, err; + + DPRINTF(("ucycomopen: unit=%d\n", unit)); + + if (unit >= ucycom_cd.cd_ndevs) + return (ENXIO); + sc = ucycom_cd.cd_devs[unit]; + + DPRINTF(("ucycomopen: sc=%p\n", sc)); + + if (sc == NULL) + return (ENXIO); + + if (sc->sc_dying) + return (EIO); + + if (ISSET(sc->sc_hdev.sc_dev.dv_flags, DVF_ACTIVE) == 0) + return (ENXIO); + + tp = sc->sc_tty; + + DPRINTF(("ucycomopen: tp=%p\n", tp)); + + if (ISSET(tp->t_state, TS_ISOPEN) && + ISSET(tp->t_state, TS_XCLUDE) && + p->p_ucred->cr_uid != 0) + return (EBUSY); + + s = spltty(); + + if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) { + struct termios t; + + tp->t_dev = dev; + + err = uhidev_open(&sc->sc_hdev); + if (err) { + /* Any cleanup? */ + splx(s); + return (err); + } + + /* + * Initialize the termios status to the defaults. Add in the + * sticky bits from TIOCSFLAGS. + */ + t.c_ispeed = 0; + t.c_ospeed = TTYDEF_SPEED; + t.c_cflag = TTYDEF_CFLAG; + if (ISSET(sc->sc_swflags, TIOCFLAG_CLOCAL)) + SET(t.c_cflag, CLOCAL); + if (ISSET(sc->sc_swflags, TIOCFLAG_CRTSCTS)) + SET(t.c_cflag, CRTSCTS); + if (ISSET(sc->sc_swflags, TIOCFLAG_MDMBUF)) + SET(t.c_cflag, MDMBUF); + + tp->t_ospeed = 0; + (void) ucycomparam(tp, &t); + tp->t_iflag = TTYDEF_IFLAG; + tp->t_oflag = TTYDEF_OFLAG; + tp->t_lflag = TTYDEF_LFLAG; + ttychars(tp); + ttsetwater(tp); + + /* Allocate an output report buffer */ + sc->sc_obuf = malloc(sc->sc_olen, M_USBDEV, M_WAITOK); + + DPRINTF(("ucycomopen: sc->sc_obuf=%p\n", sc->sc_obuf)); + +#if 0 + /* XXX Don't do this as for some reason trying to do an interrupt + * XXX out transfer at this point means everything gets stuck!?! + */ + /* + * Turn on DTR. We must always do this, even if carrier is not + * present, because otherwise we'd have to use TIOCSDTR + * immediately after setting CLOCAL, which applications do not + * expect. We always assert DTR while the device is open + * unless explicitly requested to deassert it. + */ + ucycom_dtr(sc, 1); +#endif + +#if 0 + /* XXX CLR(sc->sc_rx_flags, RX_ANY_BLOCK);*/ + ucycom_hwiflow(sc); +#endif + + } + splx(s); + + err = ttyopen(tp, UCYCOMDIALOUT(dev), ISSET(flag, O_NONBLOCK)); + if (err) + goto bad; + + err = (*tp->t_linesw->l_open)(dev, tp); + if (err) + goto bad; + + return (0); + +bad: + if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) { + /* + * We failed to open the device, and nobody else had it opened. + * Clean up the state as appropriate. + */ + ucycom_cleanup(sc); + } + + return (err); + +} + + +int +ucycomclose(dev_t dev, int flag, int mode, struct proc *p) +{ + struct ucycom_softc *sc = ucycom_cd.cd_devs[UCYCOMUNIT(dev)]; + struct tty *tp = sc->sc_tty; + + DPRINTF(("ucycomclose: unit=%d\n", UCYCOMUNIT(dev))); + if (!ISSET(tp->t_state, TS_ISOPEN)) + return (0); + + (*tp->t_linesw->l_close)(tp, flag); + ttyclose(tp); + + if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) { + /* + * Although we got a last close, the device may still be in + * use; e.g. if this was the dialout node, and there are still + * processes waiting for carrier on the non-dialout node. + */ + ucycom_cleanup(sc); + } + + return (0); +} + +Static void +ucycomstart(struct tty *tp) +{ + struct ucycom_softc *sc = ucycom_cd.cd_devs[UCYCOMUNIT(tp->t_dev)]; + u_char *data; + int cnt, len, err, s; + + if (sc->sc_dying) + return; + + s = spltty(); + if (ISSET(tp->t_state, TS_BUSY | TS_TIMEOUT | TS_TTSTOP)) { + DPRINTFN(4,("ucycomstart: no go, state=0x%x\n", tp->t_state)); + goto out; + } + +#if 0 + /* HW FLOW CTL */ + if (sc->sc_tx_stopped) + goto out; +#endif + + if (tp->t_outq.c_cc <= tp->t_lowat) { + if (ISSET(tp->t_state, TS_ASLEEP)) { + CLR(tp->t_state, TS_ASLEEP); + wakeup(&tp->t_outq); + } + selwakeup(&tp->t_wsel); + if (tp->t_outq.c_cc == 0) + goto out; + } + + /* Grab the first contiguous region of buffer space. */ + data = tp->t_outq.c_cf; + cnt = ndqb(&tp->t_outq, 0); + + if (cnt == 0) { + DPRINTF(("ucycomstart: cnt == 0\n")); + goto out; + } + + SET(tp->t_state, TS_BUSY); + + /* + * The 8 byte output report uses byte 0 for control and byte + * count. + * + * The 32 byte output report uses byte 0 for control. Byte 1 + * is used for byte count. + */ + memset(sc->sc_obuf, 0, sc->sc_olen); + len = cnt; + switch (sc->sc_olen) { + case 8: + if (cnt > sc->sc_olen - 1) { + DPRINTF(("ucycomstart(8): big buffer %d chars\n", len)); + len = sc->sc_olen - 1; + } + + memcpy(sc->sc_obuf + 1, data, len); + sc->sc_obuf[0] = len | sc->sc_mcr; + + DPRINTF(("ucycomstart(8): sc->sc_obuf[0] = %d | %d = %d\n", len, sc->sc_mcr, sc->sc_obuf[0])); + if (ucycomdebug > 10) { + u_int32_t i; + u_int8_t *d = data; + + DPRINTF(("ucycomstart(8): data =")); + for (i = 0; i < len; i++) + DPRINTF((" %02x", d[i])); + DPRINTF(("\n")); + } + break; + + case 32: + if (cnt > sc->sc_olen - 2) { + DPRINTF(("ucycomstart(32): big buffer %d chars\n", len)); + len = sc->sc_olen - 2; + } + + memcpy(sc->sc_obuf + 2, data, len); + sc->sc_obuf[0] = sc->sc_mcr; + sc->sc_obuf[1] = len; + DPRINTF(("ucycomstart(32): sc->sc_obuf[0] = %d\nsc->sc_obuf[1] = %d\n", sc->sc_obuf[0], sc->sc_obuf[1])); + if (ucycomdebug > 10) { + u_int32_t i; + u_int8_t *d = data; + + DPRINTF(("ucycomstart(32): data =")); + for (i = 0; i < len; i++) + DPRINTF((" %02x", d[i])); + DPRINTF(("\n")); + } + break; + + default: + DPRINTFN(2,("ucycomstart: unknown output report size (%d)\n", + sc->sc_olen)); + goto out; + } + splx(s); + +#ifdef UCYCOM_DEBUG + if (ucycomdebug > 5) { + int i; + + if (len != 0) { + DPRINTF(("ucycomstart: sc->sc_obuf[0..%d) =", sc->sc_olen)); + for (i = 0; i < sc->sc_olen; i++) + DPRINTF((" %02x", sc->sc_obuf[i])); + DPRINTF(("\n")); + } + } +#endif + err = uhidev_write(sc->sc_hdev.sc_parent, sc->sc_obuf, sc->sc_olen); + + if (err) + DPRINTF(("ucycomstart: error doing uhidev_write = %d\n", err)); + +#ifdef UCYCOM_DEBUG + ucycom_get_cfg(sc); +#endif + DPRINTFN(4,("ucycomstart: req %d chars did %d chars\n", cnt, len)); + + s = spltty(); + CLR(tp->t_state, TS_BUSY); + if (ISSET(tp->t_state, TS_FLUSH)) + CLR(tp->t_state, TS_FLUSH); + else + ndflush(&tp->t_outq, len); + (*tp->t_linesw->l_start)(tp); + +out: + splx(s); +} + +Static int +ucycomparam(struct tty *tp, struct termios *t) +{ + struct ucycom_softc *sc = tp->t_sc; + uint32_t baud; + uint8_t cfg; + int err; + + if (t->c_ospeed < 0) { + DPRINTF(("ucycomparam: c_ospeed < 0\n")); + return (EINVAL); + } + + /* Check requested parameters. */ + if (t->c_ispeed && t->c_ispeed != t->c_ospeed) + return (EINVAL); + + /* + * For the console, always force CLOCAL and !HUPCL, so that the port + * is always active. + */ + if (ISSET(sc->sc_swflags, TIOCFLAG_SOFTCAR)) { + SET(t->c_cflag, CLOCAL); + CLR(t->c_cflag, HUPCL); + } + + /* + * If there were no changes, don't do anything. This avoids dropping + * input and improves performance when all we did was frob things like + * VMIN and VTIME. + */ + if (tp->t_ospeed == t->c_ospeed && + tp->t_cflag == t->c_cflag) + return (0); + + /* XXX lcr = ISSET(sc->sc_lcr, LCR_SBREAK) | cflag2lcr(t->c_cflag); */ + + /* And copy to tty. */ + tp->t_ispeed = 0; + tp->t_ospeed = t->c_ospeed; + tp->t_cflag = t->c_cflag; + + baud = t->c_ispeed; + DPRINTF(("ucycomparam: baud=%d\n", baud)); + + if (t->c_cflag & CIGNORE) { + cfg = sc->sc_cfg; + } else { + cfg = 0; + switch (t->c_cflag & CSIZE) { + case CS8: + cfg |= UCYCOM_DATA_BITS_8; + break; + case CS7: + cfg |= UCYCOM_DATA_BITS_7; + break; + case CS6: + cfg |= UCYCOM_DATA_BITS_6; + break; + case CS5: + cfg |= UCYCOM_DATA_BITS_5; + break; + default: + return (EINVAL); + } + cfg |= ISSET(t->c_cflag, CSTOPB) ? + UCYCOM_STOP_BITS_2 : UCYCOM_STOP_BITS_1; + cfg |= ISSET(t->c_cflag, PARENB) ? + UCYCOM_PARITY_ON : UCYCOM_PARITY_OFF; + cfg |= ISSET(t->c_cflag, PARODD) ? + UCYCOM_PARITY_ODD : UCYCOM_PARITY_EVEN; + } + + /* + * Update the tty layer's idea of the carrier bit, in case we changed + * CLOCAL or MDMBUF. We don't hang up here; we only do that by + * explicit request. + */ + DPRINTF(("ucycomparam: l_modem\n")); + (void) (*tp->t_linesw->l_modem)(tp, 1 /* XXX carrier */ ); + + err = ucycom_configure(sc, baud, cfg); + return (err); +} + +void +ucycomstop(struct tty *tp, int flag) +{ + DPRINTF(("ucycomstop: flag=%d\n", flag)); +} + +int +ucycomread(dev_t dev, struct uio *uio, int flag) +{ + struct ucycom_softc *sc = ucycom_cd.cd_devs[UCYCOMUNIT(dev)]; + struct tty *tp = sc->sc_tty; + int err; + + DPRINTF(("ucycomread: sc=%p, tp=%p, uio=%p, flag=%d\n", sc, tp, uio, flag)); + if (sc->sc_dying) + return (EIO); + + err = ((*tp->t_linesw->l_read)(tp, uio, flag)); + return (err); +} + + +int +ucycomwrite(dev_t dev, struct uio *uio, int flag) +{ + struct ucycom_softc *sc = ucycom_cd.cd_devs[UCYCOMUNIT(dev)]; + struct tty *tp = sc->sc_tty; + int err; + + DPRINTF(("ucycomwrite: sc=%p, tp=%p, uio=%p, flag=%d\n", sc, tp, uio, flag)); + if (sc->sc_dying) + return (EIO); + + err = ((*tp->t_linesw->l_write)(tp, uio, flag)); + return (err); +} + +struct tty * +ucycomtty(dev_t dev) +{ + struct ucycom_softc *sc = ucycom_cd.cd_devs[UCYCOMUNIT(dev)]; + struct tty *tp = sc->sc_tty; + + DPRINTF(("ucycomtty: sc=%p, tp=%p\n", sc, tp)); + + return (tp); +} + +int +ucycomioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) +{ + struct ucycom_softc *sc = ucycom_cd.cd_devs[UCYCOMUNIT(dev)]; + struct tty *tp = sc->sc_tty; + int err; + int s; + + if (sc->sc_dying) + return (EIO); + + DPRINTF(("ucycomioctl: sc=%p, tp=%p, data=%p\n", sc, tp, data)); + + err = (*tp->t_linesw->l_ioctl)(tp, cmd, data, flag, p); + if (err != EPASSTHROUGH) + return (err); + + err = ttioctl(tp, cmd, data, flag, p); + if (err != EPASSTHROUGH) + return (err); + + err = 0; + + DPRINTF(("ucycomioctl: our cmd=0x%08lx\n", cmd)); + s = spltty(); + + switch (cmd) { +/* case TIOCSBRK: + ucycom_break(sc, 1); + break; + + case TIOCCBRK: + ucycom_break(sc, 0); + break; +*/ + case TIOCSDTR: + ucycom_dtr(sc, 1); + break; + + case TIOCCDTR: + ucycom_dtr(sc, 0); + break; + + case TIOCGFLAGS: + *(int *)data = sc->sc_swflags; + break; + + case TIOCSFLAGS: + err = suser(p->p_ucred, &p->p_acflag); + if (err) + break; + sc->sc_swflags = *(int *)data; + break; + + case TIOCMSET: + case TIOCMBIS: + case TIOCMBIC: + tiocm_to_ucycom(sc, cmd, *(int *)data); + break; + + case TIOCMGET: + *(int *)data = ucycom_to_tiocm(sc); + break; + + default: + err = EPASSTHROUGH; + break; + } + + splx(s); + + return (err); +} + +int +ucycompoll(dev_t dev, int events, struct proc *p) +{ + struct ucycom_softc *sc = ucycom_cd.cd_devs[UCYCOMUNIT(dev)]; + struct tty *tp = sc->sc_tty; + int err; + + DPRINTF(("ucycompoll: sc=%p, tp=%p, events=%d, proc=%p\n", sc, tp, events, p)); + + if (sc->sc_dying) + return (EIO); + + err = ((*tp->t_linesw->l_poll)(tp, events, p)); + return (err); +} + +Static int +ucycom_configure(struct ucycom_softc *sc, uint32_t baud, uint8_t cfg) +{ + uint8_t report[5]; + int err; + + switch (baud) { + case 600: + case 1200: + case 2400: + case 4800: + case 9600: + case 19200: + case 38400: + case 57600: +#if 0 + /* + * Stock chips only support standard baud rates in the 600 - 57600 + * range, but higher rates can be achieved using custom firmware. + */ + case 115200: + case 153600: + case 192000: +#endif + break; + default: + return (EINVAL); + } + + DPRINTF(("ucycom_configure: setting %d baud, %d-%c-%d (%d)\n", baud, + 5 + (cfg & UCYCOM_DATA_MASK), + (cfg & UCYCOM_PARITY_MASK) ? + ((cfg & UCYCOM_PARITY_TYPE_MASK) ? 'O' : 'E') : 'N', + (cfg & UCYCOM_STOP_MASK) ? 2 : 1, cfg)); + + report[0] = baud & 0xff; + report[1] = (baud >> 8) & 0xff; + report[2] = (baud >> 16) & 0xff; + report[3] = (baud >> 24) & 0xff; + report[4] = cfg; + err = uhidev_set_report(&sc->sc_hdev, UHID_FEATURE_REPORT, + report, sc->sc_flen); + if (err != 0) { + DPRINTF(("%s\n", usbd_errstr(err))); + return EIO; + } + sc->sc_baud = baud; + sc->sc_cfg = cfg; + + return 0; +} + +Static void +ucycom_intr(struct uhidev *addr, void *ibuf, u_int len) +{ + struct ucycom_softc *sc = (struct ucycom_softc *)addr; + struct tty *tp = sc->sc_tty; + int (*rint)(int , struct tty *) = tp->t_linesw->l_rint; + uint8_t *cp = ibuf; + int s, n, st, chg; + + /* We understand 8 byte and 32 byte input records */ + switch (len) { + case 8: + n = cp[0] & UCYCOM_LMASK; + st = cp[0] & ~UCYCOM_LMASK; + cp++; + break; + + case 32: + st = cp[0]; + n = cp[1]; + cp += 2; + break; + + default: + DPRINTFN(3,("ucycom_intr: Unknown input report length\n")); + return; + } + +#ifdef UCYCOM_DEBUG + if (ucycomdebug > 5) { + u_int32_t i; + + if (n != 0) { + DPRINTF(("ucycom_intr: ibuf[0..%d) =", n)); + for (i = 0; i < n; i++) + DPRINTF((" %02x", cp[i])); + DPRINTF(("\n")); + } + } +#endif + s = spltty(); + + /* Give characters to tty layer. */ + while (n-- > 0) { + DPRINTFN(7,("ucycom_intr: char=0x%02x\n", *cp)); + if ((*rint)(*cp++, tp) == -1) { + /* XXX what should we do? */ + printf("%s: lost a character\n", USBDEVNAME(sc->sc_hdev.sc_dev)); + break; + } + } + splx(s); + chg = st ^ sc->sc_msr; + sc->sc_msr = st; + if (ISSET(chg, UCYCOM_DCD)) + (*tp->t_linesw->l_modem)(tp, + ISSET(sc->sc_msr, UCYCOM_DCD)); + +} + +Static void +tiocm_to_ucycom(struct ucycom_softc *sc, u_long how, int ttybits) +{ + u_char combits; + u_char before = sc->sc_mcr; + + combits = 0; + if (ISSET(ttybits, TIOCM_DTR)) + SET(combits, UCYCOM_DTR); + if (ISSET(ttybits, TIOCM_RTS)) + SET(combits, UCYCOM_RTS); + + switch (how) { + case TIOCMBIC: + CLR(sc->sc_mcr, combits); + break; + + case TIOCMBIS: + SET(sc->sc_mcr, combits); + break; + + case TIOCMSET: + CLR(sc->sc_mcr, UCYCOM_DTR | UCYCOM_RTS); + SET(sc->sc_mcr, combits); + break; + } + if (before ^ sc->sc_mcr) { + DPRINTF(("tiocm_to_ucycom: something has changed\n")); + ucycom_set_status(sc); + } +} + +Static int +ucycom_to_tiocm(struct ucycom_softc *sc) +{ + u_char combits; + int ttybits = 0; + + combits = sc->sc_mcr; + if (ISSET(combits, UCYCOM_DTR)) + SET(ttybits, TIOCM_DTR); + if (ISSET(combits, UCYCOM_RTS)) + SET(ttybits, TIOCM_RTS); + + combits = sc->sc_msr; + if (ISSET(combits, UCYCOM_DCD)) + SET(ttybits, TIOCM_CD); + if (ISSET(combits, UCYCOM_CTS)) + SET(ttybits, TIOCM_CTS); + if (ISSET(combits, UCYCOM_DSR)) + SET(ttybits, TIOCM_DSR); + if (ISSET(combits, UCYCOM_RI)) + SET(ttybits, TIOCM_RI); + + return (ttybits); +} + +Static void +ucycom_dtr(struct ucycom_softc *sc, int set) +{ + uint8_t old; + + old = sc->sc_mcr; + if (set) + SET(sc->sc_mcr, UCYCOM_DTR); + else + CLR(sc->sc_mcr, UCYCOM_DTR); + + if (old ^ sc->sc_mcr) + ucycom_set_status(sc); +} + +#if 0 +Static void +ucycom_rts(struct ucycom_softc *sc, int set) +{ + uint8_t old; + + old = sc->sc_msr; + if (set) + SET(sc->sc_mcr, UCYCOM_RTS); + else + CLR(sc->sc_mcr, UCYCOM_RTS); + + if (old ^ sc->sc_mcr) + ucycom_set_status(sc); +} +#endif + +Static void +ucycom_set_status(struct ucycom_softc *sc) +{ + int err; + + if (sc->sc_olen != 8 && sc->sc_olen != 32) { + DPRINTFN(2,("ucycom_set_status: unknown output report size (%d)\n", + sc->sc_olen)); + return; + } + + DPRINTF(("ucycom_set_status: %d\n", sc->sc_mcr)); + + memset(sc->sc_obuf, 0, sc->sc_olen); + sc->sc_obuf[0] = sc->sc_mcr; + + err = uhidev_write(sc->sc_hdev.sc_parent, sc->sc_obuf, sc->sc_olen); + if (err) + DPRINTF(("ucycom_set_status: err=%d\n", err)); +} + +#ifdef UCYCOM_DEBUG +Static void +ucycom_get_cfg(struct ucycom_softc *sc) +{ + int err, cfg, baud; + uint8_t report[5]; + + err = uhidev_get_report(&sc->sc_hdev, UHID_FEATURE_REPORT, + report, sc->sc_flen); + cfg = report[4]; + baud = (report[3] << 24) + (report[2] << 16) + (report[1] << 8) + report[0]; + DPRINTF(("ucycom_configure: device reports %d baud, %d-%c-%d (%d)\n", baud, + 5 + (cfg & UCYCOM_DATA_MASK), + (cfg & UCYCOM_PARITY_MASK) ? + ((cfg & UCYCOM_PARITY_TYPE_MASK) ? 'O' : 'E') : 'N', + (cfg & UCYCOM_STOP_MASK) ? 2 : 1, cfg)); +} +#endif + +Static void +ucycom_cleanup(struct ucycom_softc *sc) +{ + DPRINTF(("ucycom_cleanup: closing uhidev\n")); + + if (sc->sc_obuf !=NULL) + free (sc->sc_obuf, M_USBDEV); + uhidev_close(&sc->sc_hdev); +} diff --git a/sys/dev/usb/usb_port.h b/sys/dev/usb/usb_port.h index cdfa8155b70..ed591ee2bc1 100644 --- a/sys/dev/usb/usb_port.h +++ b/sys/dev/usb/usb_port.h @@ -1,5 +1,5 @@ /* $OpenBSD: usb_port.h,v 1.18 2000/09/06 22:42:10 rahnds Exp $ */ -/* $NetBSD: usb_port.h,v 1.67 2005/07/18 11:42:28 augustss Exp $ */ +/* $NetBSD: usb_port.h,v 1.68 2005/07/30 06:14:50 skrll Exp $ */ /* $FreeBSD: src/sys/dev/usb/usb_port.h,v 1.21 1999/11/17 22:33:47 n_hibma Exp $ */ /* @@ -98,6 +98,7 @@ MALLOC_DECLARE(M_USBHC); #define UBT_DEBUG 1 #define AXE_DEBUG 1 #define UIPAQ_DEBUG 1 +#define UCYCOM_DEBUG 1 #define Static #else #define Static static |
