Home | History | Annotate | Line # | Download | only in usb
uhso.c revision 1.7
      1  1.7  jakllsch /*	$NetBSD: uhso.c,v 1.7 2011/12/23 00:51:46 jakllsch Exp $	*/
      2  1.1    plunky 
      3  1.1    plunky /*-
      4  1.1    plunky  * Copyright (c) 2009 Iain Hibbert
      5  1.1    plunky  * Copyright (c) 2008 Fredrik Lindberg
      6  1.1    plunky  * All rights reserved.
      7  1.1    plunky  *
      8  1.1    plunky  * Redistribution and use in source and binary forms, with or without
      9  1.1    plunky  * modification, are permitted provided that the following conditions
     10  1.1    plunky  * are met:
     11  1.1    plunky  * 1. Redistributions of source code must retain the above copyright
     12  1.1    plunky  *    notice, this list of conditions and the following disclaimer.
     13  1.1    plunky  * 2. Redistributions in binary form must reproduce the above copyright
     14  1.1    plunky  *    notice, this list of conditions and the following disclaimer in the
     15  1.1    plunky  *    documentation and/or other materials provided with the distribution.
     16  1.1    plunky  *
     17  1.1    plunky  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     18  1.1    plunky  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     19  1.1    plunky  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     20  1.1    plunky  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     21  1.1    plunky  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     22  1.1    plunky  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     23  1.1    plunky  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     24  1.1    plunky  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     25  1.1    plunky  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     26  1.1    plunky  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     27  1.1    plunky  */
     28  1.1    plunky 
     29  1.1    plunky /*
     30  1.1    plunky  *   This driver originated as the hso module for FreeBSD written by
     31  1.1    plunky  * Fredrik Lindberg[1]. It has been rewritten almost completely for
     32  1.1    plunky  * NetBSD, and to support more devices with information extracted from
     33  1.1    plunky  * the Linux hso driver provided by Option N.V.[2]
     34  1.1    plunky  *
     35  1.1    plunky  *   [1] http://www.shapeshifter.se/code/hso
     36  1.1    plunky  *   [2] http://www.pharscape.org/hso.htm
     37  1.1    plunky  */
     38  1.1    plunky 
     39  1.1    plunky #include <sys/cdefs.h>
     40  1.7  jakllsch __KERNEL_RCSID(0, "$NetBSD: uhso.c,v 1.7 2011/12/23 00:51:46 jakllsch Exp $");
     41  1.1    plunky 
     42  1.1    plunky #include "opt_inet.h"
     43  1.1    plunky 
     44  1.1    plunky #include <sys/param.h>
     45  1.1    plunky #include <sys/conf.h>
     46  1.1    plunky #include <sys/fcntl.h>
     47  1.1    plunky #include <sys/kauth.h>
     48  1.1    plunky #include <sys/kernel.h>
     49  1.1    plunky #include <sys/kmem.h>
     50  1.1    plunky #include <sys/mbuf.h>
     51  1.1    plunky #include <sys/poll.h>
     52  1.1    plunky #include <sys/queue.h>
     53  1.1    plunky #include <sys/socket.h>
     54  1.1    plunky #include <sys/sysctl.h>
     55  1.1    plunky #include <sys/systm.h>
     56  1.1    plunky #include <sys/tty.h>
     57  1.1    plunky #include <sys/vnode.h>
     58  1.3  uebayasi #include <sys/lwp.h>
     59  1.1    plunky 
     60  1.1    plunky #include <net/bpf.h>
     61  1.1    plunky #include <net/if.h>
     62  1.1    plunky #include <net/if_dl.h>
     63  1.1    plunky #include <net/if_types.h>
     64  1.1    plunky #include <net/netisr.h>
     65  1.1    plunky 
     66  1.1    plunky #include <netinet/in_systm.h>
     67  1.1    plunky #include <netinet/in_var.h>
     68  1.1    plunky #include <netinet/ip.h>
     69  1.1    plunky 
     70  1.1    plunky #include <dev/usb/usb.h>
     71  1.1    plunky #include <dev/usb/usbcdc.h>
     72  1.1    plunky #include <dev/usb/usbdi.h>
     73  1.1    plunky #include <dev/usb/usbdi_util.h>
     74  1.1    plunky #include <dev/usb/umassvar.h>
     75  1.1    plunky 
     76  1.1    plunky #include <dev/scsipi/scsi_disk.h>
     77  1.1    plunky 
     78  1.1    plunky #include "usbdevs.h"
     79  1.1    plunky 
     80  1.1    plunky #undef DPRINTF
     81  1.1    plunky #ifdef UHSO_DEBUG
     82  1.1    plunky /*
     83  1.1    plunky  * defined levels
     84  1.1    plunky  *	0	warnings only
     85  1.1    plunky  *	1	informational
     86  1.1    plunky  *	5	really chatty
     87  1.1    plunky  */
     88  1.1    plunky int uhso_debug = 0;
     89  1.1    plunky 
     90  1.4    plunky #define DPRINTF(n, ...)	do {			\
     91  1.4    plunky 	if (uhso_debug >= (n)) {		\
     92  1.4    plunky 		printf("%s: ", __func__);	\
     93  1.4    plunky 		printf(__VA_ARGS__);		\
     94  1.4    plunky 	}					\
     95  1.1    plunky } while (/* CONSTCOND */0)
     96  1.1    plunky #else
     97  1.1    plunky #define DPRINTF(...)	((void)0)
     98  1.1    plunky #endif
     99  1.1    plunky 
    100  1.1    plunky /*
    101  1.1    plunky  * When first attached, the device class will be 0 and the modem
    102  1.1    plunky  * will attach as UMASS until a SCSI REZERO_UNIT command is sent,
    103  1.1    plunky  * in which case it will detach and reattach with device class set
    104  1.1    plunky  * to UDCLASS_VENDOR (0xff) and provide the serial interfaces.
    105  1.1    plunky  *
    106  1.1    plunky  * If autoswitch is set (the default) this will happen automatically.
    107  1.1    plunky  */
    108  1.1    plunky Static int uhso_autoswitch = 1;
    109  1.1    plunky 
    110  1.1    plunky SYSCTL_SETUP(sysctl_hw_uhso_setup, "uhso sysctl setup")
    111  1.1    plunky {
    112  1.1    plunky 	const struct sysctlnode *node = NULL;
    113  1.1    plunky 
    114  1.1    plunky 	sysctl_createv(clog, 0, NULL, NULL,
    115  1.1    plunky 		CTLFLAG_PERMANENT,
    116  1.1    plunky 		CTLTYPE_NODE, "hw",
    117  1.1    plunky 		NULL,
    118  1.1    plunky 		NULL, 0,
    119  1.1    plunky 		NULL, 0,
    120  1.1    plunky 		CTL_HW, CTL_EOL);
    121  1.1    plunky 
    122  1.1    plunky 	sysctl_createv(clog, 0, NULL, &node,
    123  1.1    plunky 		CTLFLAG_PERMANENT,
    124  1.1    plunky 		CTLTYPE_NODE, "uhso",
    125  1.1    plunky 		NULL,
    126  1.1    plunky 		NULL, 0,
    127  1.1    plunky 		NULL, 0,
    128  1.1    plunky 		CTL_HW, CTL_CREATE, CTL_EOL);
    129  1.1    plunky 
    130  1.1    plunky 	if (node == NULL)
    131  1.1    plunky 		return;
    132  1.1    plunky 
    133  1.1    plunky #ifdef UHSO_DEBUG
    134  1.1    plunky 	sysctl_createv(clog, 0, &node, NULL,
    135  1.1    plunky 		CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
    136  1.1    plunky 		CTLTYPE_INT, "debug",
    137  1.1    plunky 		SYSCTL_DESCR("uhso debug level (0, 1, 5)"),
    138  1.1    plunky 		NULL, 0,
    139  1.1    plunky 		&uhso_debug, sizeof(uhso_debug),
    140  1.1    plunky 		CTL_CREATE, CTL_EOL);
    141  1.1    plunky #endif
    142  1.1    plunky 
    143  1.1    plunky 	sysctl_createv(clog, 0, &node, NULL,
    144  1.1    plunky 		CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
    145  1.1    plunky 		CTLTYPE_INT, "autoswitch",
    146  1.1    plunky 		SYSCTL_DESCR("automatically switch device into modem mode"),
    147  1.1    plunky 		NULL, 0,
    148  1.1    plunky 		&uhso_autoswitch, sizeof(uhso_autoswitch),
    149  1.1    plunky 		CTL_CREATE, CTL_EOL);
    150  1.1    plunky }
    151  1.1    plunky 
    152  1.1    plunky /*
    153  1.1    plunky  * The uhso modems have a number of interfaces providing a variety of
    154  1.1    plunky  * IO ports using the bulk endpoints, or multiplexed on the control
    155  1.1    plunky  * endpoints. We separate the ports by function and provide each with
    156  1.1    plunky  * a predictable index number used to construct the device minor number.
    157  1.1    plunky  *
    158  1.1    plunky  * The Network port is configured as a network interface rather than
    159  1.1    plunky  * a tty as it provides raw IPv4 packets.
    160  1.1    plunky  */
    161  1.1    plunky 
    162  1.1    plunky Static const char *uhso_port_name[] = {
    163  1.1    plunky 	"Control",
    164  1.1    plunky 	"Diagnostic",
    165  1.1    plunky 	"Diagnostic2",
    166  1.1    plunky 	"Application",
    167  1.1    plunky 	"Application2",
    168  1.1    plunky 	"GPS",
    169  1.1    plunky 	"GPS Control",
    170  1.1    plunky 	"PC Smartcard",
    171  1.1    plunky 	"Modem",
    172  1.1    plunky 	"MSD",			/* "Modem Sharing Device" ? */
    173  1.1    plunky 	"Voice",
    174  1.1    plunky 	"Network",
    175  1.1    plunky };
    176  1.1    plunky 
    177  1.1    plunky #define UHSO_PORT_CONTROL	0x00
    178  1.1    plunky #define UHSO_PORT_DIAG		0x01
    179  1.1    plunky #define UHSO_PORT_DIAG2		0x02
    180  1.1    plunky #define UHSO_PORT_APP		0x03
    181  1.1    plunky #define UHSO_PORT_APP2		0x04
    182  1.1    plunky #define UHSO_PORT_GPS		0x05
    183  1.1    plunky #define UHSO_PORT_GPS_CONTROL	0x06
    184  1.1    plunky #define UHSO_PORT_PCSC		0x07
    185  1.1    plunky #define UHSO_PORT_MODEM		0x08
    186  1.1    plunky #define UHSO_PORT_MSD		0x09
    187  1.1    plunky #define UHSO_PORT_VOICE		0x0a
    188  1.1    plunky #define UHSO_PORT_NETWORK	0x0b
    189  1.1    plunky 
    190  1.1    plunky #define UHSO_PORT_MAX		__arraycount(uhso_port_name)
    191  1.1    plunky 
    192  1.1    plunky #define UHSO_IFACE_MUX		0x20
    193  1.1    plunky #define UHSO_IFACE_BULK		0x40
    194  1.1    plunky #define UHSO_IFACE_IFNET	0x80
    195  1.1    plunky 
    196  1.1    plunky /*
    197  1.1    plunky  * The interface specification can sometimes be deduced from the device
    198  1.1    plunky  * type and interface number, or some modems support a vendor specific
    199  1.1    plunky  * way to read config info which we can translate to the port index.
    200  1.1    plunky  */
    201  1.1    plunky Static const uint8_t uhso_spec_default[] = {
    202  1.1    plunky 	UHSO_IFACE_IFNET | UHSO_PORT_NETWORK | UHSO_IFACE_MUX,
    203  1.1    plunky 	UHSO_IFACE_BULK | UHSO_PORT_DIAG,
    204  1.1    plunky 	UHSO_IFACE_BULK | UHSO_PORT_MODEM,
    205  1.1    plunky };
    206  1.1    plunky 
    207  1.1    plunky Static const uint8_t uhso_spec_icon321[] = {
    208  1.1    plunky 	UHSO_IFACE_IFNET | UHSO_PORT_NETWORK | UHSO_IFACE_MUX,
    209  1.1    plunky 	UHSO_IFACE_BULK | UHSO_PORT_DIAG2,
    210  1.1    plunky 	UHSO_IFACE_BULK | UHSO_PORT_MODEM,
    211  1.1    plunky 	UHSO_IFACE_BULK | UHSO_PORT_DIAG,
    212  1.1    plunky };
    213  1.1    plunky 
    214  1.1    plunky Static const uint8_t uhso_spec_config[] = {
    215  1.1    plunky 	0,
    216  1.1    plunky 	UHSO_IFACE_BULK | UHSO_PORT_DIAG,
    217  1.1    plunky 	UHSO_IFACE_BULK | UHSO_PORT_GPS,
    218  1.1    plunky 	UHSO_IFACE_BULK | UHSO_PORT_GPS_CONTROL,
    219  1.1    plunky 	UHSO_IFACE_BULK | UHSO_PORT_APP,
    220  1.1    plunky 	UHSO_IFACE_BULK | UHSO_PORT_APP2,
    221  1.1    plunky 	UHSO_IFACE_BULK | UHSO_PORT_CONTROL,
    222  1.1    plunky 	UHSO_IFACE_IFNET | UHSO_PORT_NETWORK,
    223  1.1    plunky 	UHSO_IFACE_BULK | UHSO_PORT_MODEM,
    224  1.1    plunky 	UHSO_IFACE_BULK | UHSO_PORT_MSD,
    225  1.1    plunky 	UHSO_IFACE_BULK | UHSO_PORT_PCSC,
    226  1.1    plunky 	UHSO_IFACE_BULK | UHSO_PORT_VOICE,
    227  1.1    plunky };
    228  1.1    plunky 
    229  1.1    plunky struct uhso_dev {
    230  1.1    plunky 	uint16_t vendor;
    231  1.1    plunky 	uint16_t product;
    232  1.1    plunky 	uint16_t type;
    233  1.1    plunky };
    234  1.1    plunky 
    235  1.1    plunky #define UHSOTYPE_DEFAULT	1
    236  1.1    plunky #define UHSOTYPE_ICON321	2
    237  1.1    plunky #define UHSOTYPE_CONFIG		3
    238  1.1    plunky 
    239  1.1    plunky Static const struct uhso_dev uhso_devs[] = {
    240  1.1    plunky     { USB_VENDOR_OPTIONNV, USB_PRODUCT_OPTIONNV_MAXHSDPA,    UHSOTYPE_DEFAULT },
    241  1.1    plunky     { USB_VENDOR_OPTIONNV, USB_PRODUCT_OPTIONNV_GSICON72,    UHSOTYPE_DEFAULT },
    242  1.1    plunky     { USB_VENDOR_OPTIONNV, USB_PRODUCT_OPTIONNV_ICON225,     UHSOTYPE_DEFAULT },
    243  1.1    plunky     { USB_VENDOR_OPTIONNV, USB_PRODUCT_OPTIONNV_GEHSUPA,     UHSOTYPE_DEFAULT },
    244  1.1    plunky     { USB_VENDOR_OPTIONNV, USB_PRODUCT_OPTIONNV_GTHSUPA,     UHSOTYPE_DEFAULT },
    245  1.1    plunky     { USB_VENDOR_OPTIONNV, USB_PRODUCT_OPTIONNV_GSHSUPA,     UHSOTYPE_DEFAULT },
    246  1.1    plunky     { USB_VENDOR_OPTIONNV, USB_PRODUCT_OPTIONNV_GE40X1,      UHSOTYPE_CONFIG },
    247  1.1    plunky     { USB_VENDOR_OPTIONNV, USB_PRODUCT_OPTIONNV_GE40X2,      UHSOTYPE_CONFIG },
    248  1.1    plunky     { USB_VENDOR_OPTIONNV, USB_PRODUCT_OPTIONNV_GE40X3,      UHSOTYPE_CONFIG },
    249  1.1    plunky     { USB_VENDOR_OPTIONNV, USB_PRODUCT_OPTIONNV_ICON401,     UHSOTYPE_CONFIG },
    250  1.1    plunky     { USB_VENDOR_OPTIONNV, USB_PRODUCT_OPTIONNV_GTM382,	     UHSOTYPE_CONFIG },
    251  1.1    plunky     { USB_VENDOR_OPTIONNV, USB_PRODUCT_OPTIONNV_GE40X4,      UHSOTYPE_CONFIG },
    252  1.1    plunky     { USB_VENDOR_OPTIONNV, USB_PRODUCT_OPTIONNV_ICONEDGE,    UHSOTYPE_DEFAULT },
    253  1.1    plunky     { USB_VENDOR_OPTIONNV, USB_PRODUCT_OPTIONNV_MODHSXPA,    UHSOTYPE_ICON321 },
    254  1.1    plunky     { USB_VENDOR_OPTIONNV, USB_PRODUCT_OPTIONNV_ICON321,     UHSOTYPE_ICON321 },
    255  1.1    plunky     { USB_VENDOR_OPTIONNV, USB_PRODUCT_OPTIONNV_ICON322,     UHSOTYPE_ICON321 },
    256  1.1    plunky     { USB_VENDOR_OPTIONNV, USB_PRODUCT_OPTIONNV_ICON505,     UHSOTYPE_CONFIG },
    257  1.1    plunky };
    258  1.1    plunky 
    259  1.1    plunky #define uhso_lookup(p, v)  ((const struct uhso_dev *)usb_lookup(uhso_devs, (p), (v)))
    260  1.1    plunky 
    261  1.1    plunky /* IO buffer sizes */
    262  1.1    plunky #define UHSO_MUX_WSIZE		64
    263  1.1    plunky #define UHSO_MUX_RSIZE		1024
    264  1.1    plunky #define UHSO_BULK_WSIZE		8192
    265  1.1    plunky #define UHSO_BULK_RSIZE		4096
    266  1.1    plunky #define UHSO_IFNET_MTU		1500
    267  1.1    plunky 
    268  1.1    plunky /*
    269  1.1    plunky  * Each IO port provided by the modem can be mapped to a network
    270  1.1    plunky  * interface (when hp_ifp != NULL) or a tty (when hp_tp != NULL)
    271  1.1    plunky  * which may be multiplexed and sharing interrupt and control endpoints
    272  1.1    plunky  * from an interface, or using the dedicated bulk endpoints.
    273  1.1    plunky  */
    274  1.1    plunky 
    275  1.1    plunky struct uhso_port;
    276  1.1    plunky struct uhso_softc;
    277  1.1    plunky 
    278  1.1    plunky /* uhso callback functions return errno on failure */
    279  1.1    plunky typedef int (*uhso_callback)(struct uhso_port *);
    280  1.1    plunky 
    281  1.1    plunky struct uhso_port {
    282  1.1    plunky 	struct uhso_softc      *hp_sc;		/* master softc */
    283  1.1    plunky 	struct tty	       *hp_tp;		/* tty pointer */
    284  1.1    plunky 	struct ifnet	       *hp_ifp;		/* ifnet pointer */
    285  1.1    plunky 	unsigned int		hp_flags;	/* see below */
    286  1.1    plunky 	int			hp_swflags;	/* persistent tty flags */
    287  1.1    plunky 	int			hp_status;	/* modem status */
    288  1.1    plunky 
    289  1.1    plunky 	/* port type specific handlers */
    290  1.1    plunky 	uhso_callback		hp_abort;	/* abort any transfers */
    291  1.1    plunky 	uhso_callback		hp_detach;	/* detach port completely */
    292  1.1    plunky 	uhso_callback		hp_init;	/* init port (first open) */
    293  1.1    plunky 	uhso_callback		hp_clean;	/* clean port (last close) */
    294  1.1    plunky 	uhso_callback		hp_write;	/* write data */
    295  1.1    plunky 	usbd_callback		hp_write_cb;	/* write callback */
    296  1.1    plunky 	uhso_callback		hp_read;	/* read data */
    297  1.1    plunky 	usbd_callback		hp_read_cb;	/* read callback */
    298  1.1    plunky 	uhso_callback		hp_control;	/* set control lines */
    299  1.1    plunky 
    300  1.1    plunky 	usbd_interface_handle	hp_ifh;		/* interface handle */
    301  1.1    plunky 	unsigned int		hp_index;	/* usb request index */
    302  1.1    plunky 
    303  1.1    plunky 	int			hp_iaddr;	/* interrupt endpoint */
    304  1.1    plunky 	usbd_pipe_handle	hp_ipipe;	/* interrupt pipe */
    305  1.1    plunky 	void		       *hp_ibuf;	/* interrupt buffer */
    306  1.1    plunky 	size_t			hp_isize;	/* allocated size */
    307  1.1    plunky 
    308  1.1    plunky 	int			hp_raddr;	/* bulk in endpoint */
    309  1.1    plunky 	usbd_pipe_handle	hp_rpipe;	/* bulk in pipe */
    310  1.1    plunky 	usbd_xfer_handle	hp_rxfer;	/* input xfer */
    311  1.1    plunky 	void		       *hp_rbuf;	/* input buffer */
    312  1.1    plunky 	size_t			hp_rlen;	/* fill length */
    313  1.1    plunky 	size_t			hp_rsize;	/* allocated size */
    314  1.1    plunky 
    315  1.1    plunky 	int			hp_waddr;	/* bulk out endpoint */
    316  1.1    plunky 	usbd_pipe_handle	hp_wpipe;	/* bulk out pipe */
    317  1.1    plunky 	usbd_xfer_handle	hp_wxfer;	/* output xfer */
    318  1.1    plunky 	void		       *hp_wbuf;	/* output buffer */
    319  1.1    plunky 	size_t			hp_wlen;	/* fill length */
    320  1.1    plunky 	size_t			hp_wsize;	/* allocated size */
    321  1.1    plunky 
    322  1.1    plunky 	struct mbuf	       *hp_mbuf;	/* partial packet */
    323  1.1    plunky };
    324  1.1    plunky 
    325  1.1    plunky /* hp_flags */
    326  1.1    plunky #define UHSO_PORT_MUXPIPE	__BIT(0)	/* duplicate ipipe/ibuf references */
    327  1.1    plunky #define UHSO_PORT_MUXREADY	__BIT(1)	/* input is ready */
    328  1.1    plunky #define UHSO_PORT_MUXBUSY	__BIT(2)	/* read in progress */
    329  1.1    plunky 
    330  1.1    plunky struct uhso_softc {
    331  1.1    plunky 	device_t		sc_dev;		/* self */
    332  1.1    plunky 	usbd_device_handle	sc_udev;
    333  1.1    plunky 	int			sc_refcnt;
    334  1.1    plunky 	struct uhso_port       *sc_port[UHSO_PORT_MAX];
    335  1.1    plunky };
    336  1.1    plunky 
    337  1.1    plunky #define UHSO_CONFIG_NO		1
    338  1.1    plunky 
    339  1.1    plunky int uhso_match(device_t, cfdata_t, void *);
    340  1.1    plunky void uhso_attach(device_t, device_t, void *);
    341  1.1    plunky int uhso_detach(device_t, int);
    342  1.1    plunky 
    343  1.1    plunky extern struct cfdriver uhso_cd;
    344  1.1    plunky 
    345  1.1    plunky CFATTACH_DECL_NEW(uhso, sizeof(struct uhso_softc), uhso_match, uhso_attach,
    346  1.1    plunky     uhso_detach, NULL);
    347  1.1    plunky 
    348  1.1    plunky Static int uhso_switch_mode(usbd_device_handle);
    349  1.1    plunky Static int uhso_get_iface_spec(struct usb_attach_arg *, uint8_t, uint8_t *);
    350  1.1    plunky Static usb_endpoint_descriptor_t *uhso_get_endpoint(usbd_interface_handle, int, int);
    351  1.1    plunky 
    352  1.1    plunky Static void uhso_mux_attach(struct uhso_softc *, usbd_interface_handle, int);
    353  1.1    plunky Static int  uhso_mux_abort(struct uhso_port *);
    354  1.1    plunky Static int  uhso_mux_detach(struct uhso_port *);
    355  1.1    plunky Static int  uhso_mux_init(struct uhso_port *);
    356  1.1    plunky Static int  uhso_mux_clean(struct uhso_port *);
    357  1.1    plunky Static int  uhso_mux_write(struct uhso_port *);
    358  1.1    plunky Static int  uhso_mux_read(struct uhso_port *);
    359  1.1    plunky Static int  uhso_mux_control(struct uhso_port *);
    360  1.1    plunky Static void uhso_mux_intr(usbd_xfer_handle, usbd_private_handle, usbd_status);
    361  1.1    plunky 
    362  1.1    plunky Static void uhso_bulk_attach(struct uhso_softc *, usbd_interface_handle, int);
    363  1.1    plunky Static int  uhso_bulk_abort(struct uhso_port *);
    364  1.1    plunky Static int  uhso_bulk_detach(struct uhso_port *);
    365  1.1    plunky Static int  uhso_bulk_init(struct uhso_port *);
    366  1.1    plunky Static int  uhso_bulk_clean(struct uhso_port *);
    367  1.1    plunky Static int  uhso_bulk_write(struct uhso_port *);
    368  1.1    plunky Static int  uhso_bulk_read(struct uhso_port *);
    369  1.1    plunky Static int  uhso_bulk_control(struct uhso_port *);
    370  1.1    plunky Static void uhso_bulk_intr(usbd_xfer_handle, usbd_private_handle, usbd_status);
    371  1.1    plunky 
    372  1.1    plunky Static void uhso_tty_attach(struct uhso_port *);
    373  1.1    plunky Static void uhso_tty_detach(struct uhso_port *);
    374  1.1    plunky Static void uhso_tty_read_cb(usbd_xfer_handle, usbd_private_handle, usbd_status);
    375  1.1    plunky Static void uhso_tty_write_cb(usbd_xfer_handle, usbd_private_handle, usbd_status);
    376  1.1    plunky 
    377  1.1    plunky dev_type_open(uhso_tty_open);
    378  1.1    plunky dev_type_close(uhso_tty_close);
    379  1.1    plunky dev_type_read(uhso_tty_read);
    380  1.1    plunky dev_type_write(uhso_tty_write);
    381  1.1    plunky dev_type_ioctl(uhso_tty_ioctl);
    382  1.1    plunky dev_type_stop(uhso_tty_stop);
    383  1.1    plunky dev_type_tty(uhso_tty_tty);
    384  1.1    plunky dev_type_poll(uhso_tty_poll);
    385  1.1    plunky 
    386  1.1    plunky const struct cdevsw uhso_cdevsw = {
    387  1.1    plunky 	.d_open = uhso_tty_open,
    388  1.1    plunky 	.d_close = uhso_tty_close,
    389  1.1    plunky 	.d_read = uhso_tty_read,
    390  1.1    plunky 	.d_write = uhso_tty_write,
    391  1.1    plunky 	.d_ioctl = uhso_tty_ioctl,
    392  1.1    plunky 	.d_stop = uhso_tty_stop,
    393  1.1    plunky 	.d_tty = uhso_tty_tty,
    394  1.1    plunky 	.d_poll = uhso_tty_poll,
    395  1.1    plunky 	.d_mmap = nommap,
    396  1.1    plunky 	.d_kqfilter = ttykqfilter,
    397  1.1    plunky 	.d_flag = D_TTY,
    398  1.1    plunky };
    399  1.1    plunky 
    400  1.1    plunky Static int  uhso_tty_init(struct uhso_port *);
    401  1.1    plunky Static void uhso_tty_clean(struct uhso_port *);
    402  1.1    plunky Static int  uhso_tty_do_ioctl(struct uhso_port *, u_long, void *, int, struct lwp *);
    403  1.1    plunky Static void uhso_tty_start(struct tty *);
    404  1.1    plunky Static int  uhso_tty_param(struct tty *, struct termios *);
    405  1.1    plunky Static int  uhso_tty_control(struct uhso_port *, u_long, int);
    406  1.1    plunky 
    407  1.1    plunky #define UHSO_UNIT_MASK		0x0fff0
    408  1.1    plunky #define UHSO_PORT_MASK		0x0000f
    409  1.1    plunky #define UHSO_DIALOUT_MASK	0x80000
    410  1.1    plunky #define UHSO_CALLUNIT_MASK	0x40000
    411  1.1    plunky 
    412  1.1    plunky #define UHSOUNIT(x)	((minor(x) & UHSO_UNIT_MASK) >> 4)
    413  1.1    plunky #define UHSOPORT(x)	(minor(x) & UHSO_PORT_MASK)
    414  1.1    plunky #define UHSODIALOUT(x)	(minor(x) & UHSO_DIALOUT_MASK)
    415  1.1    plunky #define UHSOMINOR(u, p)	((((u) << 4) & UHSO_UNIT_MASK) | ((p) & UHSO_UNIT_MASK))
    416  1.1    plunky 
    417  1.1    plunky Static void uhso_ifnet_attach(struct uhso_softc *, usbd_interface_handle, int);
    418  1.1    plunky Static int  uhso_ifnet_abort(struct uhso_port *);
    419  1.1    plunky Static int  uhso_ifnet_detach(struct uhso_port *);
    420  1.1    plunky Static void uhso_ifnet_read_cb(usbd_xfer_handle, usbd_private_handle, usbd_status);
    421  1.1    plunky Static void uhso_ifnet_input(struct ifnet *, struct mbuf **, uint8_t *, size_t);
    422  1.1    plunky Static void uhso_ifnet_write_cb(usbd_xfer_handle, usbd_private_handle, usbd_status);
    423  1.1    plunky 
    424  1.1    plunky Static int  uhso_ifnet_ioctl(struct ifnet *, u_long, void *);
    425  1.1    plunky Static int  uhso_ifnet_init(struct uhso_port *);
    426  1.1    plunky Static void uhso_ifnet_clean(struct uhso_port *);
    427  1.1    plunky Static void uhso_ifnet_start(struct ifnet *);
    428  1.1    plunky Static int  uhso_ifnet_output(struct ifnet *, struct mbuf *, const struct sockaddr *, struct rtentry *);
    429  1.1    plunky 
    430  1.1    plunky 
    431  1.1    plunky /*******************************************************************************
    432  1.1    plunky  *
    433  1.1    plunky  *	USB autoconfig
    434  1.1    plunky  *
    435  1.1    plunky  */
    436  1.1    plunky 
    437  1.1    plunky int
    438  1.1    plunky uhso_match(device_t parent, cfdata_t match, void *aux)
    439  1.1    plunky {
    440  1.1    plunky 	struct usb_attach_arg *uaa = aux;
    441  1.1    plunky 
    442  1.1    plunky 	/*
    443  1.1    plunky 	 * don't claim this device if autoswitch is disabled
    444  1.1    plunky 	 * and it is not in modem mode already
    445  1.1    plunky 	 */
    446  1.1    plunky 	if (!uhso_autoswitch && uaa->class != UDCLASS_VENDOR)
    447  1.1    plunky 		return UMATCH_NONE;
    448  1.1    plunky 
    449  1.1    plunky 	if (uhso_lookup(uaa->vendor, uaa->product))
    450  1.1    plunky 		return UMATCH_VENDOR_PRODUCT;
    451  1.1    plunky 
    452  1.1    plunky 	return UMATCH_NONE;
    453  1.1    plunky }
    454  1.1    plunky 
    455  1.1    plunky void
    456  1.1    plunky uhso_attach(device_t parent, device_t self, void *aux)
    457  1.1    plunky {
    458  1.1    plunky 	struct uhso_softc *sc = device_private(self);
    459  1.1    plunky 	struct usb_attach_arg *uaa = aux;
    460  1.1    plunky 	usbd_interface_handle ifh;
    461  1.1    plunky 	char *devinfop;
    462  1.1    plunky 	uint8_t count, i, spec;
    463  1.1    plunky 	usbd_status status;
    464  1.1    plunky 
    465  1.1    plunky 	DPRINTF(1, ": sc = %p, self=%p", sc, self);
    466  1.1    plunky 
    467  1.1    plunky 	sc->sc_dev = self;
    468  1.1    plunky 	sc->sc_udev = uaa->device;
    469  1.1    plunky 
    470  1.1    plunky 	aprint_naive("\n");
    471  1.1    plunky 	aprint_normal("\n");
    472  1.1    plunky 
    473  1.1    plunky 	devinfop = usbd_devinfo_alloc(uaa->device, 0);
    474  1.1    plunky 	aprint_normal_dev(self, "%s\n", devinfop);
    475  1.1    plunky 	usbd_devinfo_free(devinfop);
    476  1.1    plunky 
    477  1.1    plunky 	usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev, sc->sc_dev);
    478  1.1    plunky 
    479  1.1    plunky 	status = usbd_set_config_no(sc->sc_udev, UHSO_CONFIG_NO, 1);
    480  1.1    plunky 	if (status != USBD_NORMAL_COMPLETION) {
    481  1.1    plunky 		aprint_error_dev(self, "could not set config no %d: %s\n",
    482  1.1    plunky 		    UHSO_CONFIG_NO, usbd_errstr(status));
    483  1.1    plunky 
    484  1.1    plunky 		return;
    485  1.1    plunky 	}
    486  1.1    plunky 
    487  1.1    plunky 	if (uaa->class != UDCLASS_VENDOR) {
    488  1.1    plunky 		aprint_verbose_dev(self, "Switching device into modem mode..\n");
    489  1.1    plunky 		if (uhso_switch_mode(uaa->device) != 0)
    490  1.1    plunky 			aprint_error_dev(self, "modem switch failed\n");
    491  1.1    plunky 
    492  1.1    plunky 		return;
    493  1.1    plunky 	}
    494  1.1    plunky 
    495  1.1    plunky 	count = 0;
    496  1.1    plunky 	(void)usbd_interface_count(sc->sc_udev, &count);
    497  1.1    plunky 	DPRINTF(1, "interface count %d\n", count);
    498  1.1    plunky 
    499  1.1    plunky 	for (i = 0; i < count; i++) {
    500  1.1    plunky 		status = usbd_device2interface_handle(sc->sc_udev, i, &ifh);
    501  1.1    plunky 		if (status != USBD_NORMAL_COMPLETION) {
    502  1.1    plunky 			aprint_error_dev(self,
    503  1.1    plunky 			    "could not get interface %d: %s\n",
    504  1.1    plunky 			    i, usbd_errstr(status));
    505  1.1    plunky 
    506  1.1    plunky 			return;
    507  1.1    plunky 		}
    508  1.1    plunky 
    509  1.1    plunky 		if (!uhso_get_iface_spec(uaa, i, &spec)) {
    510  1.1    plunky 			aprint_error_dev(self,
    511  1.1    plunky 			    "could not get interface %d specification\n", i);
    512  1.1    plunky 
    513  1.1    plunky 			return;
    514  1.1    plunky 		}
    515  1.1    plunky 
    516  1.1    plunky 		if (ISSET(spec, UHSO_IFACE_MUX))
    517  1.1    plunky 			uhso_mux_attach(sc, ifh, UHSOPORT(spec));
    518  1.1    plunky 
    519  1.1    plunky 		if (ISSET(spec, UHSO_IFACE_BULK))
    520  1.1    plunky 			uhso_bulk_attach(sc, ifh, UHSOPORT(spec));
    521  1.1    plunky 
    522  1.1    plunky 		if (ISSET(spec, UHSO_IFACE_IFNET))
    523  1.1    plunky 			uhso_ifnet_attach(sc, ifh, UHSOPORT(spec));
    524  1.1    plunky 	}
    525  1.1    plunky 
    526  1.1    plunky 	if (!pmf_device_register(self, NULL, NULL))
    527  1.1    plunky 		aprint_error_dev(self, "couldn't establish power handler\n");
    528  1.1    plunky }
    529  1.1    plunky 
    530  1.1    plunky int
    531  1.1    plunky uhso_detach(device_t self, int flags)
    532  1.1    plunky {
    533  1.1    plunky 	struct uhso_softc *sc = device_private(self);
    534  1.1    plunky 	struct uhso_port *hp;
    535  1.1    plunky 	devmajor_t major;
    536  1.1    plunky 	devminor_t minor;
    537  1.1    plunky 	unsigned int i;
    538  1.1    plunky 	int s;
    539  1.1    plunky 
    540  1.1    plunky 	if (device_pmf_is_registered(self))
    541  1.1    plunky 		pmf_device_deregister(self);
    542  1.1    plunky 
    543  1.1    plunky 	for (i = 0; i < UHSO_PORT_MAX; i++) {
    544  1.1    plunky 		hp = sc->sc_port[i];
    545  1.1    plunky 		if (hp != NULL)
    546  1.1    plunky 			(*hp->hp_abort)(hp);
    547  1.1    plunky 	}
    548  1.1    plunky 
    549  1.1    plunky 	s = splusb();
    550  1.1    plunky 	if (sc->sc_refcnt-- > 0) {
    551  1.1    plunky 		DPRINTF(1, "waiting for refcnt (%d)..\n", sc->sc_refcnt);
    552  1.1    plunky 		usb_detach_wait(sc->sc_dev);
    553  1.1    plunky 	}
    554  1.1    plunky 	splx(s);
    555  1.1    plunky 
    556  1.1    plunky 	/*
    557  1.1    plunky 	 * XXX the tty close routine increases/decreases refcnt causing
    558  1.1    plunky 	 * XXX another usb_detach_wakeup() does it matter, should these
    559  1.1    plunky 	 * XXX be before the detach_wait? or before the abort?
    560  1.1    plunky 	 */
    561  1.1    plunky 
    562  1.1    plunky 	/* Nuke the vnodes for any open instances (calls close). */
    563  1.1    plunky 	major = cdevsw_lookup_major(&uhso_cdevsw);
    564  1.1    plunky 	minor = UHSOMINOR(device_unit(sc->sc_dev), 0);
    565  1.1    plunky 	vdevgone(major, minor, minor + UHSO_PORT_MAX, VCHR);
    566  1.1    plunky 	minor = UHSOMINOR(device_unit(sc->sc_dev), 0) | UHSO_DIALOUT_MASK;
    567  1.1    plunky 	vdevgone(major, minor, minor + UHSO_PORT_MAX, VCHR);
    568  1.1    plunky 	minor = UHSOMINOR(device_unit(sc->sc_dev), 0) | UHSO_CALLUNIT_MASK;
    569  1.1    plunky 	vdevgone(major, minor, minor + UHSO_PORT_MAX, VCHR);
    570  1.1    plunky 
    571  1.1    plunky 	for (i = 0; i < UHSO_PORT_MAX; i++) {
    572  1.1    plunky 		hp = sc->sc_port[i];
    573  1.1    plunky 		if (hp != NULL)
    574  1.1    plunky 			(*hp->hp_detach)(hp);
    575  1.1    plunky 	}
    576  1.1    plunky 
    577  1.1    plunky 	usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev, sc->sc_dev);
    578  1.1    plunky 
    579  1.1    plunky 	return 0;
    580  1.1    plunky }
    581  1.1    plunky 
    582  1.1    plunky /*
    583  1.1    plunky  * Send SCSI REZERO_UNIT command to switch device into modem mode
    584  1.1    plunky  */
    585  1.1    plunky Static int
    586  1.1    plunky uhso_switch_mode(usbd_device_handle udev)
    587  1.1    plunky {
    588  1.1    plunky 	umass_bbb_cbw_t	cmd;
    589  1.1    plunky 	usb_endpoint_descriptor_t *ed;
    590  1.1    plunky 	usbd_interface_handle ifh;
    591  1.1    plunky 	usbd_pipe_handle pipe;
    592  1.1    plunky 	usbd_xfer_handle xfer;
    593  1.1    plunky 	usbd_status status;
    594  1.1    plunky 
    595  1.1    plunky 	status = usbd_device2interface_handle(udev, 0, &ifh);
    596  1.1    plunky 	if (status != USBD_NORMAL_COMPLETION)
    597  1.1    plunky 		return EIO;
    598  1.1    plunky 
    599  1.1    plunky 	ed = uhso_get_endpoint(ifh, UE_BULK, UE_DIR_OUT);
    600  1.1    plunky 	if (ed == NULL)
    601  1.1    plunky 		return ENODEV;
    602  1.1    plunky 
    603  1.1    plunky 	status = usbd_open_pipe(ifh, ed->bEndpointAddress, 0, &pipe);
    604  1.1    plunky 	if (status != USBD_NORMAL_COMPLETION)
    605  1.1    plunky 		return EIO;
    606  1.1    plunky 
    607  1.1    plunky 	xfer = usbd_alloc_xfer(udev);
    608  1.1    plunky 	if (xfer == NULL)
    609  1.1    plunky 		return ENOMEM;
    610  1.7  jakllsch 
    611  1.1    plunky 	USETDW(cmd.dCBWSignature, CBWSIGNATURE);
    612  1.1    plunky 	USETDW(cmd.dCBWTag, 1);
    613  1.1    plunky 	USETDW(cmd.dCBWDataTransferLength, 0);
    614  1.1    plunky 	cmd.bCBWFlags = CBWFLAGS_OUT;
    615  1.1    plunky 	cmd.bCBWLUN = 0;
    616  1.1    plunky 	cmd.bCDBLength = 6;
    617  1.1    plunky 
    618  1.1    plunky 	memset(&cmd.CBWCDB, 0, CBWCDBLENGTH);
    619  1.1    plunky 	cmd.CBWCDB[0] = SCSI_REZERO_UNIT;
    620  1.1    plunky 
    621  1.1    plunky 	usbd_setup_xfer(xfer, pipe, NULL, &cmd, sizeof(cmd),
    622  1.1    plunky 		USBD_SYNCHRONOUS, USBD_DEFAULT_TIMEOUT, NULL);
    623  1.1    plunky 
    624  1.1    plunky 	status = usbd_transfer(xfer);
    625  1.1    plunky 
    626  1.1    plunky 	usbd_abort_pipe(pipe);
    627  1.1    plunky 	usbd_close_pipe(pipe);
    628  1.1    plunky 	usbd_free_xfer(xfer);
    629  1.1    plunky 
    630  1.1    plunky 	return (status == USBD_NORMAL_COMPLETION ? 0 : EIO);
    631  1.1    plunky }
    632  1.1    plunky 
    633  1.1    plunky Static int
    634  1.1    plunky uhso_get_iface_spec(struct usb_attach_arg *uaa, uint8_t ifnum, uint8_t *spec)
    635  1.1    plunky {
    636  1.1    plunky 	const struct uhso_dev *hd;
    637  1.1    plunky 	uint8_t config[17];
    638  1.1    plunky 	usb_device_request_t req;
    639  1.1    plunky 	usbd_status status;
    640  1.1    plunky 
    641  1.1    plunky 	hd = uhso_lookup(uaa->vendor, uaa->product);
    642  1.1    plunky 	KASSERT(hd != NULL);
    643  1.1    plunky 
    644  1.1    plunky 	switch (hd->type) {
    645  1.1    plunky 	case UHSOTYPE_DEFAULT:
    646  1.1    plunky 		if (ifnum > __arraycount(uhso_spec_default))
    647  1.1    plunky 			break;
    648  1.1    plunky 
    649  1.1    plunky 		*spec = uhso_spec_default[ifnum];
    650  1.1    plunky 		return 1;
    651  1.1    plunky 
    652  1.1    plunky 	case UHSOTYPE_ICON321:
    653  1.1    plunky 		if (ifnum > __arraycount(uhso_spec_icon321))
    654  1.1    plunky 			break;
    655  1.1    plunky 
    656  1.1    plunky 		*spec = uhso_spec_icon321[ifnum];
    657  1.1    plunky 		return 1;
    658  1.1    plunky 
    659  1.1    plunky 	case UHSOTYPE_CONFIG:
    660  1.1    plunky 		req.bmRequestType = UT_READ_VENDOR_DEVICE;
    661  1.1    plunky 		req.bRequest = 0x86;	/* "Config Info" */
    662  1.1    plunky 		USETW(req.wValue, 0);
    663  1.1    plunky 		USETW(req.wIndex, 0);
    664  1.1    plunky 		USETW(req.wLength, sizeof(config));
    665  1.1    plunky 
    666  1.1    plunky 		status = usbd_do_request(uaa->device, &req, config);
    667  1.1    plunky 		if (status != USBD_NORMAL_COMPLETION)
    668  1.1    plunky 			break;
    669  1.1    plunky 
    670  1.1    plunky 		if (ifnum > __arraycount(config)
    671  1.1    plunky 		    || config[ifnum] > __arraycount(uhso_spec_config))
    672  1.1    plunky 			break;
    673  1.1    plunky 
    674  1.1    plunky 		*spec = uhso_spec_config[config[ifnum]];
    675  1.1    plunky 
    676  1.1    plunky 		/*
    677  1.1    plunky 		 * Apparently some modems also have a CRC bug that is
    678  1.1    plunky 		 * indicated by ISSET(config[16], __BIT(0)) but we dont
    679  1.1    plunky 		 * handle it at this time.
    680  1.1    plunky 		 */
    681  1.1    plunky 		return 1;
    682  1.1    plunky 
    683  1.1    plunky 	default:
    684  1.1    plunky 		DPRINTF(0, "unknown interface type\n");
    685  1.1    plunky 		break;
    686  1.1    plunky 	}
    687  1.1    plunky 
    688  1.1    plunky 	return 0;
    689  1.1    plunky }
    690  1.1    plunky 
    691  1.1    plunky Static usb_endpoint_descriptor_t *
    692  1.1    plunky uhso_get_endpoint(usbd_interface_handle ifh, int type, int dir)
    693  1.1    plunky {
    694  1.1    plunky 	usb_endpoint_descriptor_t *ed;
    695  1.1    plunky 	uint8_t count, i;
    696  1.1    plunky 
    697  1.1    plunky 	count = 0;
    698  1.1    plunky 	(void)usbd_endpoint_count(ifh, &count);
    699  1.1    plunky 
    700  1.1    plunky 	for (i = 0; i < count; i++) {
    701  1.1    plunky 		ed = usbd_interface2endpoint_descriptor(ifh, i);
    702  1.1    plunky 		if (ed != NULL
    703  1.1    plunky 		    && UE_GET_XFERTYPE(ed->bmAttributes) == type
    704  1.1    plunky 		    && UE_GET_DIR(ed->bEndpointAddress) == dir)
    705  1.1    plunky 			return ed;
    706  1.1    plunky 	}
    707  1.1    plunky 
    708  1.1    plunky 	return NULL;
    709  1.1    plunky }
    710  1.1    plunky 
    711  1.1    plunky 
    712  1.1    plunky /******************************************************************************
    713  1.1    plunky  *
    714  1.1    plunky  *	Multiplexed ports signal with the interrupt endpoint to indicate
    715  1.1    plunky  *  when data is available for reading, and a separate request is made on
    716  1.1    plunky  *  the control endpoint to read or write on each port. The offsets in the
    717  1.1    plunky  *  table below relate to bit numbers in the mux mask, identifying each port.
    718  1.1    plunky  */
    719  1.1    plunky 
    720  1.1    plunky Static const int uhso_mux_port[] = {
    721  1.1    plunky 	UHSO_PORT_CONTROL,
    722  1.1    plunky 	UHSO_PORT_APP,
    723  1.1    plunky 	UHSO_PORT_PCSC,
    724  1.1    plunky 	UHSO_PORT_GPS,
    725  1.1    plunky 	UHSO_PORT_APP2,
    726  1.1    plunky };
    727  1.1    plunky 
    728  1.1    plunky Static void
    729  1.1    plunky uhso_mux_attach(struct uhso_softc *sc, usbd_interface_handle ifh, int index)
    730  1.1    plunky {
    731  1.1    plunky 	usbd_desc_iter_t iter;
    732  1.1    plunky 	const usb_descriptor_t *desc;
    733  1.1    plunky 	usb_endpoint_descriptor_t *ed;
    734  1.1    plunky 	usbd_pipe_handle pipe;
    735  1.1    plunky 	struct uhso_port *hp;
    736  1.1    plunky 	uint8_t *buf;
    737  1.1    plunky 	size_t size;
    738  1.1    plunky 	unsigned int i, mux, flags;
    739  1.1    plunky 	int addr;
    740  1.1    plunky 	usbd_status status;
    741  1.1    plunky 
    742  1.1    plunky 	ed = uhso_get_endpoint(ifh, UE_INTERRUPT, UE_DIR_IN);
    743  1.1    plunky 	if (ed == NULL) {
    744  1.1    plunky 		aprint_error_dev(sc->sc_dev, "no interrupt endpoint\n");
    745  1.1    plunky 		return;
    746  1.1    plunky 	}
    747  1.1    plunky 	addr = ed->bEndpointAddress;
    748  1.1    plunky 	size = UGETW(ed->wMaxPacketSize);
    749  1.1    plunky 
    750  1.1    plunky 	/*
    751  1.1    plunky 	 * There should be an additional "Class Specific" descriptor on
    752  1.1    plunky 	 * the mux interface containing a single byte with a bitmask of
    753  1.1    plunky 	 * enabled ports. We need to look through the device descriptor
    754  1.1    plunky 	 * to find it and the port index is found from the uhso_mux_port
    755  1.1    plunky 	 * array, above.
    756  1.1    plunky 	 */
    757  1.1    plunky 	usb_desc_iter_init(sc->sc_udev, &iter);
    758  1.1    plunky 
    759  1.1    plunky 	/* skip past the current interface descriptor */
    760  1.1    plunky 	iter.cur = (const uByte *)usbd_get_interface_descriptor(ifh);
    761  1.1    plunky 	desc = usb_desc_iter_next(&iter);
    762  1.1    plunky 
    763  1.1    plunky 	for (;;) {
    764  1.1    plunky 		desc = usb_desc_iter_next(&iter);
    765  1.1    plunky 		if (desc == NULL
    766  1.1    plunky 		    || desc->bDescriptorType == UDESC_INTERFACE) {
    767  1.1    plunky 			mux = 0;
    768  1.1    plunky 			break;	/* not found */
    769  1.1    plunky 		}
    770  1.1    plunky 
    771  1.1    plunky 		if (desc->bDescriptorType == UDESC_CS_INTERFACE
    772  1.1    plunky 		    && desc->bLength == 3) {
    773  1.1    plunky 			mux = ((const uint8_t *)desc)[2];
    774  1.1    plunky 			break;
    775  1.1    plunky 		}
    776  1.1    plunky 	}
    777  1.1    plunky 
    778  1.1    plunky 	DPRINTF(1, "addr=%d, size=%zd, mux=0x%02x\n", addr, size, mux);
    779  1.1    plunky 
    780  1.1    plunky 	buf = kmem_alloc(size, KM_SLEEP);
    781  1.1    plunky 	status = usbd_open_pipe_intr(ifh, addr, USBD_SHORT_XFER_OK, &pipe,
    782  1.1    plunky 	    sc, buf, size, uhso_mux_intr, USBD_DEFAULT_INTERVAL);
    783  1.1    plunky 
    784  1.1    plunky 	if (status != USBD_NORMAL_COMPLETION) {
    785  1.1    plunky 		aprint_error_dev(sc->sc_dev, "failed to open interrupt pipe: %s",
    786  1.1    plunky 		    usbd_errstr(status));
    787  1.1    plunky 
    788  1.1    plunky 		kmem_free(buf, size);
    789  1.1    plunky 		return;
    790  1.1    plunky 	}
    791  1.1    plunky 
    792  1.1    plunky 	flags = 0;
    793  1.1    plunky 	for (i = 0; i < __arraycount(uhso_mux_port); i++) {
    794  1.1    plunky 		if (ISSET(mux, __BIT(i))) {
    795  1.1    plunky 			if (sc->sc_port[uhso_mux_port[i]] != NULL) {
    796  1.1    plunky 				aprint_error_dev(sc->sc_dev,
    797  1.1    plunky 				    "mux port %d is duplicate!\n", i);
    798  1.1    plunky 
    799  1.1    plunky 				continue;
    800  1.1    plunky 			}
    801  1.1    plunky 
    802  1.1    plunky 			hp = kmem_zalloc(sizeof(struct uhso_port), KM_SLEEP);
    803  1.1    plunky 			sc->sc_port[uhso_mux_port[i]] = hp;
    804  1.1    plunky 
    805  1.1    plunky 			hp->hp_sc = sc;
    806  1.1    plunky 			hp->hp_index = i;
    807  1.1    plunky 			hp->hp_ipipe = pipe;
    808  1.1    plunky 			hp->hp_ibuf = buf;
    809  1.1    plunky 			hp->hp_isize = size;
    810  1.1    plunky 			hp->hp_flags = flags;
    811  1.1    plunky 			hp->hp_abort = uhso_mux_abort;
    812  1.1    plunky 			hp->hp_detach = uhso_mux_detach;
    813  1.1    plunky 			hp->hp_init = uhso_mux_init;
    814  1.1    plunky 			hp->hp_clean = uhso_mux_clean;
    815  1.1    plunky 			hp->hp_write = uhso_mux_write;
    816  1.1    plunky 			hp->hp_write_cb = uhso_tty_write_cb;
    817  1.1    plunky 			hp->hp_read = uhso_mux_read;
    818  1.1    plunky 			hp->hp_read_cb = uhso_tty_read_cb;
    819  1.1    plunky 			hp->hp_control = uhso_mux_control;
    820  1.1    plunky 			hp->hp_wsize = UHSO_MUX_WSIZE;
    821  1.1    plunky 			hp->hp_rsize = UHSO_MUX_RSIZE;
    822  1.1    plunky 
    823  1.1    plunky 			uhso_tty_attach(hp);
    824  1.1    plunky 
    825  1.1    plunky 			aprint_normal_dev(sc->sc_dev,
    826  1.1    plunky 			    "%s (port %d) attached as mux tty\n",
    827  1.1    plunky 			    uhso_port_name[uhso_mux_port[i]], uhso_mux_port[i]);
    828  1.1    plunky 
    829  1.1    plunky 			/*
    830  1.1    plunky 			 * As the pipe handle is stored in each mux, mark
    831  1.1    plunky 			 * secondary references so they don't get released
    832  1.1    plunky 			 */
    833  1.1    plunky 			flags = UHSO_PORT_MUXPIPE;
    834  1.1    plunky 		}
    835  1.1    plunky 	}
    836  1.1    plunky 
    837  1.1    plunky 	if (flags == 0) {
    838  1.1    plunky 		/* for whatever reasons, nothing was attached */
    839  1.1    plunky 		usbd_abort_pipe(pipe);
    840  1.1    plunky 		usbd_close_pipe(pipe);
    841  1.1    plunky 		kmem_free(buf, size);
    842  1.1    plunky 	}
    843  1.1    plunky }
    844  1.1    plunky 
    845  1.1    plunky Static int
    846  1.1    plunky uhso_mux_abort(struct uhso_port *hp)
    847  1.1    plunky {
    848  1.1    plunky 	struct uhso_softc *sc = hp->hp_sc;
    849  1.1    plunky 
    850  1.1    plunky 	DPRINTF(1, "hp=%p\n", hp);
    851  1.1    plunky 
    852  1.1    plunky 	if (!ISSET(hp->hp_flags, UHSO_PORT_MUXPIPE))
    853  1.1    plunky 		usbd_abort_pipe(hp->hp_ipipe);
    854  1.1    plunky 
    855  1.1    plunky 	usbd_abort_default_pipe(sc->sc_udev);
    856  1.1    plunky 
    857  1.1    plunky 	return (*hp->hp_clean)(hp);
    858  1.1    plunky }
    859  1.1    plunky 
    860  1.1    plunky Static int
    861  1.1    plunky uhso_mux_detach(struct uhso_port *hp)
    862  1.1    plunky {
    863  1.1    plunky 
    864  1.1    plunky 	DPRINTF(1, "hp=%p\n", hp);
    865  1.1    plunky 
    866  1.1    plunky 	if (!ISSET(hp->hp_flags, UHSO_PORT_MUXPIPE)) {
    867  1.1    plunky 		DPRINTF(1, "interrupt pipe closed\n");
    868  1.1    plunky 		usbd_abort_pipe(hp->hp_ipipe);
    869  1.1    plunky 		usbd_close_pipe(hp->hp_ipipe);
    870  1.1    plunky 		kmem_free(hp->hp_ibuf, hp->hp_isize);
    871  1.1    plunky 	}
    872  1.1    plunky 
    873  1.1    plunky 	uhso_tty_detach(hp);
    874  1.1    plunky 	kmem_free(hp, sizeof(struct uhso_port));
    875  1.1    plunky 	return 0;
    876  1.1    plunky }
    877  1.1    plunky 
    878  1.1    plunky Static int
    879  1.1    plunky uhso_mux_init(struct uhso_port *hp)
    880  1.1    plunky {
    881  1.1    plunky 
    882  1.1    plunky 	DPRINTF(1, "hp=%p\n", hp);
    883  1.1    plunky 
    884  1.1    plunky 	CLR(hp->hp_flags, UHSO_PORT_MUXBUSY | UHSO_PORT_MUXREADY);
    885  1.1    plunky 	SET(hp->hp_status, TIOCM_DSR | TIOCM_CAR);
    886  1.1    plunky 	return 0;
    887  1.1    plunky }
    888  1.1    plunky 
    889  1.1    plunky Static int
    890  1.1    plunky uhso_mux_clean(struct uhso_port *hp)
    891  1.1    plunky {
    892  1.1    plunky 
    893  1.1    plunky 	DPRINTF(1, "hp=%p\n", hp);
    894  1.1    plunky 
    895  1.1    plunky 	CLR(hp->hp_flags, UHSO_PORT_MUXREADY);
    896  1.1    plunky 	CLR(hp->hp_status, TIOCM_DTR | TIOCM_DSR | TIOCM_CAR);
    897  1.1    plunky 	return 0;
    898  1.1    plunky }
    899  1.1    plunky 
    900  1.1    plunky Static int
    901  1.1    plunky uhso_mux_write(struct uhso_port *hp)
    902  1.1    plunky {
    903  1.1    plunky 	struct uhso_softc *sc = hp->hp_sc;
    904  1.1    plunky 	usb_device_request_t req;
    905  1.1    plunky 	usbd_status status;
    906  1.1    plunky 
    907  1.1    plunky 	DPRINTF(5, "hp=%p, index=%d, wlen=%zd\n", hp, hp->hp_index, hp->hp_wlen);
    908  1.1    plunky 
    909  1.1    plunky 	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
    910  1.1    plunky 	req.bRequest = UCDC_SEND_ENCAPSULATED_COMMAND;
    911  1.1    plunky 	USETW(req.wValue, 0);
    912  1.1    plunky 	USETW(req.wIndex, hp->hp_index);
    913  1.1    plunky 	USETW(req.wLength, hp->hp_wlen);
    914  1.1    plunky 
    915  1.1    plunky 	usbd_setup_default_xfer(hp->hp_wxfer, sc->sc_udev, hp, USBD_NO_TIMEOUT,
    916  1.1    plunky 	    &req, hp->hp_wbuf, hp->hp_wlen, USBD_NO_COPY, hp->hp_write_cb);
    917  1.1    plunky 
    918  1.1    plunky 	status = usbd_transfer(hp->hp_wxfer);
    919  1.1    plunky 	if (status != USBD_IN_PROGRESS) {
    920  1.1    plunky 		DPRINTF(0, "non-normal status %s\n", usbd_errstr(status));
    921  1.1    plunky 		return EIO;
    922  1.1    plunky 	}
    923  1.1    plunky 
    924  1.1    plunky 	sc->sc_refcnt++;
    925  1.1    plunky 	return 0;
    926  1.1    plunky }
    927  1.1    plunky 
    928  1.1    plunky Static int
    929  1.1    plunky uhso_mux_read(struct uhso_port *hp)
    930  1.1    plunky {
    931  1.1    plunky 	struct uhso_softc *sc = hp->hp_sc;
    932  1.1    plunky 	usb_device_request_t req;
    933  1.1    plunky 	usbd_status status;
    934  1.1    plunky 
    935  1.1    plunky 	CLR(hp->hp_flags, UHSO_PORT_MUXBUSY);
    936  1.1    plunky 
    937  1.1    plunky 	if (hp->hp_rlen == 0 && !ISSET(hp->hp_flags, UHSO_PORT_MUXREADY))
    938  1.1    plunky 		return 0;
    939  1.1    plunky 
    940  1.1    plunky 	SET(hp->hp_flags, UHSO_PORT_MUXBUSY);
    941  1.1    plunky 	CLR(hp->hp_flags, UHSO_PORT_MUXREADY);
    942  1.1    plunky 
    943  1.1    plunky 	DPRINTF(5, "hp=%p, index=%d\n", hp, hp->hp_index);
    944  1.1    plunky 
    945  1.1    plunky 	req.bmRequestType = UT_READ_CLASS_INTERFACE;
    946  1.1    plunky 	req.bRequest = UCDC_GET_ENCAPSULATED_RESPONSE;
    947  1.1    plunky 	USETW(req.wValue, 0);
    948  1.1    plunky 	USETW(req.wIndex, hp->hp_index);
    949  1.1    plunky 	USETW(req.wLength, hp->hp_rsize);
    950  1.1    plunky 
    951  1.1    plunky 	usbd_setup_default_xfer(hp->hp_rxfer, sc->sc_udev, hp, USBD_NO_TIMEOUT,
    952  1.1    plunky 	    &req, hp->hp_rbuf, hp->hp_rsize, USBD_NO_COPY | USBD_SHORT_XFER_OK,
    953  1.1    plunky 	    hp->hp_read_cb);
    954  1.1    plunky 
    955  1.1    plunky 	status = usbd_transfer(hp->hp_rxfer);
    956  1.1    plunky 	if (status != USBD_IN_PROGRESS) {
    957  1.1    plunky 		DPRINTF(0, "non-normal status %s\n", usbd_errstr(status));
    958  1.1    plunky 		CLR(hp->hp_flags, UHSO_PORT_MUXBUSY);
    959  1.1    plunky 		return EIO;
    960  1.1    plunky 	}
    961  1.1    plunky 
    962  1.1    plunky 	sc->sc_refcnt++;
    963  1.1    plunky 	return 0;
    964  1.1    plunky }
    965  1.1    plunky 
    966  1.1    plunky Static int
    967  1.1    plunky uhso_mux_control(struct uhso_port *hp)
    968  1.1    plunky {
    969  1.1    plunky 
    970  1.1    plunky 	DPRINTF(1, "hp=%p\n", hp);
    971  1.1    plunky 
    972  1.1    plunky 	return 0;
    973  1.1    plunky }
    974  1.1    plunky 
    975  1.1    plunky Static void
    976  1.1    plunky uhso_mux_intr(usbd_xfer_handle xfer, usbd_private_handle p, usbd_status status)
    977  1.1    plunky {
    978  1.1    plunky 	struct uhso_softc *sc = p;
    979  1.1    plunky 	struct uhso_port *hp;
    980  1.1    plunky 	uint32_t cc;
    981  1.1    plunky 	uint8_t *buf;
    982  1.1    plunky 	unsigned int i;
    983  1.1    plunky 
    984  1.1    plunky 	if (status != USBD_NORMAL_COMPLETION) {
    985  1.1    plunky 		DPRINTF(0, "non-normal status %s\n", usbd_errstr(status));
    986  1.1    plunky 		return;
    987  1.1    plunky 	}
    988  1.1    plunky 
    989  1.1    plunky 	usbd_get_xfer_status(xfer, NULL, (void **)&buf, &cc, NULL);
    990  1.1    plunky 	if (cc == 0)
    991  1.1    plunky 		return;
    992  1.1    plunky 
    993  1.1    plunky 	DPRINTF(5, "mux mask 0x%02x, cc=%u\n", buf[0], cc);
    994  1.1    plunky 
    995  1.1    plunky 	for (i = 0; i < __arraycount(uhso_mux_port); i++) {
    996  1.1    plunky 		if (!ISSET(buf[0], __BIT(i)))
    997  1.1    plunky 			continue;
    998  1.1    plunky 
    999  1.1    plunky 		DPRINTF(5, "mux %d port %d\n", i, uhso_mux_port[i]);
   1000  1.1    plunky 		hp = sc->sc_port[uhso_mux_port[i]];
   1001  1.1    plunky 		if (hp == NULL
   1002  1.1    plunky 		    || hp->hp_tp == NULL
   1003  1.1    plunky 		    || !ISSET(hp->hp_status, TIOCM_DTR))
   1004  1.1    plunky 			continue;
   1005  1.1    plunky 
   1006  1.1    plunky 		SET(hp->hp_flags, UHSO_PORT_MUXREADY);
   1007  1.1    plunky 		if (ISSET(hp->hp_flags, UHSO_PORT_MUXBUSY))
   1008  1.1    plunky 			continue;
   1009  1.1    plunky 
   1010  1.1    plunky 		uhso_mux_read(hp);
   1011  1.1    plunky 	}
   1012  1.1    plunky }
   1013  1.1    plunky 
   1014  1.1    plunky 
   1015  1.1    plunky /******************************************************************************
   1016  1.1    plunky  *
   1017  1.1    plunky  *	Bulk ports operate using the bulk endpoints on an interface, though
   1018  1.1    plunky  *   the Modem port (at least) may have an interrupt endpoint that will pass
   1019  1.1    plunky  *   CDC Notification messages with the modem status.
   1020  1.1    plunky  */
   1021  1.1    plunky 
   1022  1.1    plunky Static void
   1023  1.1    plunky uhso_bulk_attach(struct uhso_softc *sc, usbd_interface_handle ifh, int index)
   1024  1.1    plunky {
   1025  1.1    plunky 	usb_endpoint_descriptor_t *ed;
   1026  1.1    plunky 	usb_interface_descriptor_t *id;
   1027  1.1    plunky 	struct uhso_port *hp;
   1028  1.1    plunky 	int in, out;
   1029  1.1    plunky 
   1030  1.1    plunky 	ed = uhso_get_endpoint(ifh, UE_BULK, UE_DIR_IN);
   1031  1.1    plunky 	if (ed == NULL) {
   1032  1.1    plunky 		aprint_error_dev(sc->sc_dev, "bulk-in endpoint not found\n");
   1033  1.1    plunky 		return;
   1034  1.1    plunky 	}
   1035  1.1    plunky 	in = ed->bEndpointAddress;
   1036  1.1    plunky 
   1037  1.1    plunky 	ed = uhso_get_endpoint(ifh, UE_BULK, UE_DIR_OUT);
   1038  1.1    plunky 	if (ed == NULL) {
   1039  1.1    plunky 		aprint_error_dev(sc->sc_dev, "bulk-out endpoint not found\n");
   1040  1.1    plunky 		return;
   1041  1.1    plunky 	}
   1042  1.1    plunky 	out = ed->bEndpointAddress;
   1043  1.1    plunky 
   1044  1.1    plunky 	id = usbd_get_interface_descriptor(ifh);
   1045  1.1    plunky 	if (id == NULL) {
   1046  1.1    plunky 		aprint_error_dev(sc->sc_dev, "interface descriptor not found\n");
   1047  1.1    plunky 		return;
   1048  1.1    plunky 	}
   1049  1.1    plunky 
   1050  1.1    plunky 	DPRINTF(1, "bulk endpoints in=%x, out=%x\n", in, out);
   1051  1.1    plunky 
   1052  1.1    plunky 	if (sc->sc_port[index] != NULL) {
   1053  1.1    plunky 		aprint_error_dev(sc->sc_dev, "bulk port %d is duplicate!\n",
   1054  1.1    plunky 		    index);
   1055  1.1    plunky 
   1056  1.1    plunky 		return;
   1057  1.1    plunky 	}
   1058  1.1    plunky 
   1059  1.1    plunky 	hp = kmem_zalloc(sizeof(struct uhso_port), KM_SLEEP);
   1060  1.1    plunky 	sc->sc_port[index] = hp;
   1061  1.1    plunky 
   1062  1.1    plunky 	hp->hp_sc = sc;
   1063  1.1    plunky 	hp->hp_ifh = ifh;
   1064  1.1    plunky 	hp->hp_index = id->bInterfaceNumber;
   1065  1.1    plunky 	hp->hp_raddr = in;
   1066  1.1    plunky 	hp->hp_waddr = out;
   1067  1.1    plunky 	hp->hp_abort = uhso_bulk_abort;
   1068  1.1    plunky 	hp->hp_detach = uhso_bulk_detach;
   1069  1.1    plunky 	hp->hp_init = uhso_bulk_init;
   1070  1.1    plunky 	hp->hp_clean = uhso_bulk_clean;
   1071  1.1    plunky 	hp->hp_write = uhso_bulk_write;
   1072  1.1    plunky 	hp->hp_write_cb = uhso_tty_write_cb;
   1073  1.1    plunky 	hp->hp_read = uhso_bulk_read;
   1074  1.1    plunky 	hp->hp_read_cb = uhso_tty_read_cb;
   1075  1.1    plunky 	hp->hp_control = uhso_bulk_control;
   1076  1.1    plunky 	hp->hp_wsize = UHSO_BULK_WSIZE;
   1077  1.1    plunky 	hp->hp_rsize = UHSO_BULK_RSIZE;
   1078  1.1    plunky 
   1079  1.1    plunky 	if (index == UHSO_PORT_MODEM) {
   1080  1.1    plunky 		ed = uhso_get_endpoint(ifh, UE_INTERRUPT, UE_DIR_IN);
   1081  1.1    plunky 		if (ed != NULL) {
   1082  1.1    plunky 			hp->hp_iaddr = ed->bEndpointAddress;
   1083  1.1    plunky 			hp->hp_isize = UGETW(ed->wMaxPacketSize);
   1084  1.1    plunky 		}
   1085  1.1    plunky 	}
   1086  1.1    plunky 
   1087  1.1    plunky 	uhso_tty_attach(hp);
   1088  1.1    plunky 
   1089  1.1    plunky 	aprint_normal_dev(sc->sc_dev,
   1090  1.1    plunky 	    "%s (port %d) attached as bulk tty\n",
   1091  1.1    plunky 	    uhso_port_name[index], index);
   1092  1.1    plunky }
   1093  1.1    plunky 
   1094  1.1    plunky Static int
   1095  1.1    plunky uhso_bulk_abort(struct uhso_port *hp)
   1096  1.1    plunky {
   1097  1.1    plunky 
   1098  1.1    plunky 	DPRINTF(1, "hp=%p\n", hp);
   1099  1.1    plunky 
   1100  1.1    plunky 	return (*hp->hp_clean)(hp);
   1101  1.1    plunky }
   1102  1.1    plunky 
   1103  1.1    plunky Static int
   1104  1.1    plunky uhso_bulk_detach(struct uhso_port *hp)
   1105  1.1    plunky {
   1106  1.1    plunky 
   1107  1.1    plunky 	DPRINTF(1, "hp=%p\n", hp);
   1108  1.1    plunky 
   1109  1.1    plunky 	uhso_tty_detach(hp);
   1110  1.1    plunky 	kmem_free(hp, sizeof(struct uhso_port));
   1111  1.1    plunky 	return 0;
   1112  1.1    plunky }
   1113  1.1    plunky 
   1114  1.1    plunky Static int
   1115  1.1    plunky uhso_bulk_init(struct uhso_port *hp)
   1116  1.1    plunky {
   1117  1.1    plunky 	usbd_status status;
   1118  1.1    plunky 
   1119  1.1    plunky 	DPRINTF(1, "hp=%p\n", hp);
   1120  1.1    plunky 
   1121  1.1    plunky 	if (hp->hp_isize > 0) {
   1122  1.1    plunky 		hp->hp_ibuf = kmem_alloc(hp->hp_isize, KM_SLEEP);
   1123  1.1    plunky 
   1124  1.1    plunky 		status = usbd_open_pipe_intr(hp->hp_ifh, hp->hp_iaddr,
   1125  1.1    plunky 		    USBD_SHORT_XFER_OK, &hp->hp_ipipe, hp, hp->hp_ibuf,
   1126  1.1    plunky 		    hp->hp_isize, uhso_bulk_intr, USBD_DEFAULT_INTERVAL);
   1127  1.1    plunky 
   1128  1.1    plunky 		if (status != USBD_NORMAL_COMPLETION) {
   1129  1.1    plunky 			DPRINTF(0, "interrupt pipe open failed: %s\n",
   1130  1.1    plunky 			    usbd_errstr(status));
   1131  1.1    plunky 
   1132  1.1    plunky 			return EIO;
   1133  1.1    plunky 		}
   1134  1.1    plunky 	}
   1135  1.1    plunky 
   1136  1.1    plunky 	status = usbd_open_pipe(hp->hp_ifh, hp->hp_raddr, 0, &hp->hp_rpipe);
   1137  1.1    plunky 	if (status != USBD_NORMAL_COMPLETION) {
   1138  1.1    plunky 		DPRINTF(0, "read pipe open failed: %s\n", usbd_errstr(status));
   1139  1.1    plunky 		return EIO;
   1140  1.1    plunky 	}
   1141  1.1    plunky 
   1142  1.1    plunky 	status = usbd_open_pipe(hp->hp_ifh, hp->hp_waddr, 0, &hp->hp_wpipe);
   1143  1.1    plunky 	if (status != USBD_NORMAL_COMPLETION) {
   1144  1.1    plunky 		DPRINTF(0, "write pipe open failed: %s\n", usbd_errstr(status));
   1145  1.1    plunky 		return EIO;
   1146  1.1    plunky 	}
   1147  1.1    plunky 
   1148  1.1    plunky 	return 0;
   1149  1.1    plunky }
   1150  1.1    plunky 
   1151  1.1    plunky Static int
   1152  1.1    plunky uhso_bulk_clean(struct uhso_port *hp)
   1153  1.1    plunky {
   1154  1.1    plunky 
   1155  1.1    plunky 	DPRINTF(1, "hp=%p\n", hp);
   1156  1.1    plunky 
   1157  1.1    plunky 	if (hp->hp_ipipe != NULL) {
   1158  1.1    plunky 		usbd_abort_pipe(hp->hp_ipipe);
   1159  1.1    plunky 		usbd_close_pipe(hp->hp_ipipe);
   1160  1.1    plunky 		hp->hp_ipipe = NULL;
   1161  1.1    plunky 	}
   1162  1.1    plunky 
   1163  1.1    plunky 	if (hp->hp_ibuf != NULL) {
   1164  1.1    plunky 		kmem_free(hp->hp_ibuf, hp->hp_isize);
   1165  1.1    plunky 		hp->hp_ibuf = NULL;
   1166  1.1    plunky 	}
   1167  1.1    plunky 
   1168  1.1    plunky 	if (hp->hp_rpipe != NULL) {
   1169  1.1    plunky 		usbd_abort_pipe(hp->hp_rpipe);
   1170  1.1    plunky 		usbd_close_pipe(hp->hp_rpipe);
   1171  1.1    plunky 		hp->hp_rpipe = NULL;
   1172  1.1    plunky 	}
   1173  1.1    plunky 
   1174  1.1    plunky 	if (hp->hp_wpipe != NULL) {
   1175  1.1    plunky 		usbd_abort_pipe(hp->hp_wpipe);
   1176  1.1    plunky 		usbd_close_pipe(hp->hp_wpipe);
   1177  1.1    plunky 		hp->hp_wpipe = NULL;
   1178  1.1    plunky 	}
   1179  1.1    plunky 
   1180  1.1    plunky 	return 0;
   1181  1.1    plunky }
   1182  1.1    plunky 
   1183  1.1    plunky Static int
   1184  1.1    plunky uhso_bulk_write(struct uhso_port *hp)
   1185  1.1    plunky {
   1186  1.1    plunky 	struct uhso_softc *sc = hp->hp_sc;
   1187  1.1    plunky 	usbd_status status;
   1188  1.1    plunky 
   1189  1.1    plunky 	DPRINTF(5, "hp=%p, wlen=%zd\n", hp, hp->hp_wlen);
   1190  1.1    plunky 
   1191  1.1    plunky 	usbd_setup_xfer(hp->hp_wxfer, hp->hp_wpipe, hp, hp->hp_wbuf,
   1192  1.1    plunky 	    hp->hp_wlen, USBD_NO_COPY, USBD_NO_TIMEOUT, hp->hp_write_cb);
   1193  1.1    plunky 
   1194  1.1    plunky 	status = usbd_transfer(hp->hp_wxfer);
   1195  1.1    plunky 	if (status != USBD_IN_PROGRESS) {
   1196  1.1    plunky 		DPRINTF(0, "non-normal status %s\n", usbd_errstr(status));
   1197  1.1    plunky 		return EIO;
   1198  1.1    plunky 	}
   1199  1.1    plunky 
   1200  1.1    plunky 	sc->sc_refcnt++;
   1201  1.1    plunky 	return 0;
   1202  1.1    plunky }
   1203  1.1    plunky 
   1204  1.1    plunky Static int
   1205  1.1    plunky uhso_bulk_read(struct uhso_port *hp)
   1206  1.1    plunky {
   1207  1.1    plunky 	struct uhso_softc *sc = hp->hp_sc;
   1208  1.1    plunky 	usbd_status status;
   1209  1.1    plunky 
   1210  1.1    plunky 	DPRINTF(5, "hp=%p\n", hp);
   1211  1.1    plunky 
   1212  1.1    plunky 	usbd_setup_xfer(hp->hp_rxfer, hp->hp_rpipe, hp, hp->hp_rbuf,
   1213  1.1    plunky 	    hp->hp_rsize, USBD_NO_COPY | USBD_SHORT_XFER_OK,
   1214  1.1    plunky 	    USBD_NO_TIMEOUT, hp->hp_read_cb);
   1215  1.1    plunky 
   1216  1.1    plunky 	status = usbd_transfer(hp->hp_rxfer);
   1217  1.1    plunky 	if (status != USBD_IN_PROGRESS) {
   1218  1.1    plunky 		DPRINTF(0, "non-normal status %s\n", usbd_errstr(status));
   1219  1.1    plunky 		return EIO;
   1220  1.1    plunky 	}
   1221  1.1    plunky 
   1222  1.1    plunky 	sc->sc_refcnt++;
   1223  1.1    plunky 	return 0;
   1224  1.1    plunky }
   1225  1.1    plunky 
   1226  1.1    plunky Static int
   1227  1.1    plunky uhso_bulk_control(struct uhso_port *hp)
   1228  1.1    plunky {
   1229  1.1    plunky 	struct uhso_softc *sc = hp->hp_sc;
   1230  1.1    plunky 	usb_device_request_t req;
   1231  1.1    plunky 	usbd_status status;
   1232  1.1    plunky 	int val;
   1233  1.1    plunky 
   1234  1.1    plunky 	DPRINTF(1, "hp=%p\n", hp);
   1235  1.1    plunky 
   1236  1.1    plunky 	if (hp->hp_isize == 0)
   1237  1.1    plunky 		return 0;
   1238  1.1    plunky 
   1239  1.1    plunky 	val = 0;
   1240  1.1    plunky 	if (ISSET(hp->hp_status, TIOCM_DTR))
   1241  1.1    plunky 		SET(val, UCDC_LINE_DTR);
   1242  1.1    plunky 	if (ISSET(hp->hp_status, TIOCM_RTS))
   1243  1.1    plunky 		SET(val, UCDC_LINE_RTS);
   1244  1.1    plunky 
   1245  1.1    plunky 	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
   1246  1.1    plunky 	req.bRequest = UCDC_SET_CONTROL_LINE_STATE;
   1247  1.1    plunky 	USETW(req.wValue, val);
   1248  1.1    plunky 	USETW(req.wIndex, hp->hp_index);
   1249  1.1    plunky 	USETW(req.wLength, 0);
   1250  1.1    plunky 
   1251  1.1    plunky 	sc->sc_refcnt++;
   1252  1.1    plunky 
   1253  1.1    plunky 	status = usbd_do_request(sc->sc_udev, &req, NULL);
   1254  1.1    plunky 
   1255  1.1    plunky 	if (--sc->sc_refcnt < 0)
   1256  1.1    plunky 		usb_detach_wakeup(sc->sc_dev);
   1257  1.1    plunky 
   1258  1.1    plunky 	if (status != USBD_NORMAL_COMPLETION) {
   1259  1.1    plunky 		DPRINTF(0, "non-normal status %s\n", usbd_errstr(status));
   1260  1.1    plunky 		return EIO;
   1261  1.1    plunky 	}
   1262  1.1    plunky 
   1263  1.1    plunky 	return 0;
   1264  1.1    plunky }
   1265  1.1    plunky 
   1266  1.1    plunky Static void
   1267  1.1    plunky uhso_bulk_intr(usbd_xfer_handle xfer, usbd_private_handle p, usbd_status status)
   1268  1.1    plunky {
   1269  1.1    plunky 	struct uhso_port *hp = p;
   1270  1.1    plunky 	struct tty *tp = hp->hp_tp;
   1271  1.1    plunky 	usb_cdc_notification_t *msg;
   1272  1.1    plunky 	uint32_t cc;
   1273  1.1    plunky 	int s, old;
   1274  1.1    plunky 
   1275  1.1    plunky 	if (status != USBD_NORMAL_COMPLETION) {
   1276  1.1    plunky 		DPRINTF(0, "non-normal status %s\n", usbd_errstr(status));
   1277  1.1    plunky 		return;
   1278  1.1    plunky 	}
   1279  1.1    plunky 
   1280  1.1    plunky 	usbd_get_xfer_status(xfer, NULL, (void **)&msg, &cc, NULL);
   1281  1.1    plunky 
   1282  1.1    plunky 	if (cc < UCDC_NOTIFICATION_LENGTH
   1283  1.1    plunky 	    || msg->bmRequestType != UCDC_NOTIFICATION
   1284  1.1    plunky 	    || msg->bNotification != UCDC_N_SERIAL_STATE
   1285  1.1    plunky 	    || UGETW(msg->wValue) != 0
   1286  1.1    plunky 	    || UGETW(msg->wIndex) != hp->hp_index
   1287  1.1    plunky 	    || UGETW(msg->wLength) < 1)
   1288  1.1    plunky 		return;
   1289  1.1    plunky 
   1290  1.1    plunky 	DPRINTF(5, "state=%02x\n", msg->data[0]);
   1291  1.1    plunky 
   1292  1.1    plunky 	old = hp->hp_status;
   1293  1.1    plunky 	CLR(hp->hp_status, TIOCM_RNG | TIOCM_DSR | TIOCM_CAR);
   1294  1.1    plunky 	if (ISSET(msg->data[0], UCDC_N_SERIAL_RI))
   1295  1.1    plunky 		SET(hp->hp_status, TIOCM_RNG);
   1296  1.1    plunky 	if (ISSET(msg->data[0], UCDC_N_SERIAL_DSR))
   1297  1.1    plunky 		SET(hp->hp_status, TIOCM_DSR);
   1298  1.1    plunky 	if (ISSET(msg->data[0], UCDC_N_SERIAL_DCD))
   1299  1.1    plunky 		SET(hp->hp_status, TIOCM_CAR);
   1300  1.1    plunky 
   1301  1.1    plunky 	if (ISSET(hp->hp_status ^ old, TIOCM_CAR)) {
   1302  1.1    plunky 		s = spltty();
   1303  1.1    plunky 		tp->t_linesw->l_modem(tp, ISSET(hp->hp_status, TIOCM_CAR));
   1304  1.1    plunky 		splx(s);
   1305  1.1    plunky 	}
   1306  1.1    plunky 
   1307  1.1    plunky 	if (ISSET((hp->hp_status ^ old), TIOCM_RNG | TIOCM_DSR | TIOCM_CAR))
   1308  1.1    plunky 		DPRINTF(1, "RNG %s, DSR %s, DCD %s\n",
   1309  1.1    plunky 		    (ISSET(hp->hp_status, TIOCM_RNG) ? "on" : "off"),
   1310  1.1    plunky 		    (ISSET(hp->hp_status, TIOCM_DSR) ? "on" : "off"),
   1311  1.1    plunky 		    (ISSET(hp->hp_status, TIOCM_CAR) ? "on" : "off"));
   1312  1.1    plunky }
   1313  1.1    plunky 
   1314  1.1    plunky 
   1315  1.1    plunky /******************************************************************************
   1316  1.1    plunky  *
   1317  1.1    plunky  *	TTY management
   1318  1.1    plunky  *
   1319  1.1    plunky  */
   1320  1.1    plunky 
   1321  1.1    plunky Static void
   1322  1.1    plunky uhso_tty_attach(struct uhso_port *hp)
   1323  1.1    plunky {
   1324  1.1    plunky 	struct tty *tp;
   1325  1.1    plunky 
   1326  1.5     rmind 	tp = tty_alloc();
   1327  1.1    plunky 	tp->t_oproc = uhso_tty_start;
   1328  1.1    plunky 	tp->t_param = uhso_tty_param;
   1329  1.1    plunky 
   1330  1.1    plunky 	hp->hp_tp = tp;
   1331  1.1    plunky 	tty_attach(tp);
   1332  1.1    plunky 
   1333  1.1    plunky 	DPRINTF(1, "hp=%p, tp=%p\n", hp, tp);
   1334  1.1    plunky }
   1335  1.1    plunky 
   1336  1.1    plunky Static void
   1337  1.1    plunky uhso_tty_detach(struct uhso_port *hp)
   1338  1.1    plunky {
   1339  1.1    plunky 
   1340  1.1    plunky 	DPRINTF(1, "hp=%p\n", hp);
   1341  1.1    plunky 
   1342  1.1    plunky 	uhso_tty_clean(hp);
   1343  1.1    plunky 
   1344  1.1    plunky 	tty_detach(hp->hp_tp);
   1345  1.5     rmind 	tty_free(hp->hp_tp);
   1346  1.1    plunky 	hp->hp_tp = NULL;
   1347  1.1    plunky }
   1348  1.1    plunky 
   1349  1.1    plunky Static void
   1350  1.1    plunky uhso_tty_write_cb(usbd_xfer_handle xfer, usbd_private_handle p, usbd_status status)
   1351  1.1    plunky {
   1352  1.1    plunky 	struct uhso_port *hp = p;
   1353  1.1    plunky 	struct uhso_softc *sc = hp->hp_sc;
   1354  1.1    plunky 	struct tty *tp = hp->hp_tp;
   1355  1.1    plunky 	uint32_t cc;
   1356  1.1    plunky 	int s;
   1357  1.1    plunky 
   1358  1.1    plunky 	if (--sc->sc_refcnt < 0)
   1359  1.1    plunky 		usb_detach_wakeup(sc->sc_dev);
   1360  1.1    plunky 
   1361  1.1    plunky 	if (status != USBD_NORMAL_COMPLETION) {
   1362  1.1    plunky 		DPRINTF(0, "non-normal status %s\n", usbd_errstr(status));
   1363  1.1    plunky 
   1364  1.1    plunky 		if (status == USBD_STALLED && hp->hp_wpipe != NULL)
   1365  1.1    plunky 			usbd_clear_endpoint_stall_async(hp->hp_wpipe);
   1366  1.1    plunky 		else
   1367  1.1    plunky 			return;
   1368  1.1    plunky 	} else {
   1369  1.1    plunky 		usbd_get_xfer_status(xfer, NULL, NULL, &cc, NULL);
   1370  1.1    plunky 
   1371  1.1    plunky 		DPRINTF(5, "wrote %d bytes (of %zd)\n", cc, hp->hp_wlen);
   1372  1.1    plunky 		if (cc != hp->hp_wlen)
   1373  1.1    plunky 			DPRINTF(0, "cc=%u, wlen=%zd\n", cc, hp->hp_wlen);
   1374  1.1    plunky 	}
   1375  1.1    plunky 
   1376  1.1    plunky 	s = spltty();
   1377  1.1    plunky 	CLR(tp->t_state, TS_BUSY);
   1378  1.1    plunky 	tp->t_linesw->l_start(tp);
   1379  1.1    plunky 	splx(s);
   1380  1.1    plunky }
   1381  1.1    plunky 
   1382  1.1    plunky Static void
   1383  1.1    plunky uhso_tty_read_cb(usbd_xfer_handle xfer, usbd_private_handle p, usbd_status status)
   1384  1.1    plunky {
   1385  1.1    plunky 	struct uhso_port *hp = p;
   1386  1.1    plunky 	struct uhso_softc *sc = hp->hp_sc;
   1387  1.1    plunky 	struct tty *tp = hp->hp_tp;
   1388  1.1    plunky 	uint8_t *cp;
   1389  1.1    plunky 	uint32_t cc;
   1390  1.1    plunky 	int s;
   1391  1.1    plunky 
   1392  1.1    plunky 	if (--sc->sc_refcnt < 0)
   1393  1.1    plunky 		usb_detach_wakeup(sc->sc_dev);
   1394  1.1    plunky 
   1395  1.1    plunky 	if (status != USBD_NORMAL_COMPLETION) {
   1396  1.1    plunky 		DPRINTF(0, "non-normal status: %s\n", usbd_errstr(status));
   1397  1.1    plunky 
   1398  1.1    plunky 		if (status == USBD_STALLED && hp->hp_rpipe != NULL)
   1399  1.1    plunky 			usbd_clear_endpoint_stall_async(hp->hp_rpipe);
   1400  1.1    plunky 		else
   1401  1.1    plunky 			return;
   1402  1.1    plunky 
   1403  1.1    plunky 		hp->hp_rlen = 0;
   1404  1.1    plunky 	} else {
   1405  1.1    plunky 		usbd_get_xfer_status(xfer, NULL, (void **)&cp, &cc, NULL);
   1406  1.1    plunky 
   1407  1.1    plunky 		hp->hp_rlen = cc;
   1408  1.1    plunky 		DPRINTF(5, "read %d bytes\n", cc);
   1409  1.1    plunky 
   1410  1.1    plunky 		s = spltty();
   1411  1.1    plunky 		while (cc > 0) {
   1412  1.1    plunky 			if (tp->t_linesw->l_rint(*cp++, tp) == -1) {
   1413  1.1    plunky 				DPRINTF(0, "lost %d bytes\n", cc);
   1414  1.1    plunky 				break;
   1415  1.1    plunky 			}
   1416  1.1    plunky 
   1417  1.1    plunky 			cc--;
   1418  1.1    plunky 		}
   1419  1.1    plunky 		splx(s);
   1420  1.1    plunky 	}
   1421  1.1    plunky 
   1422  1.1    plunky 	(*hp->hp_read)(hp);
   1423  1.1    plunky }
   1424  1.1    plunky 
   1425  1.1    plunky 
   1426  1.1    plunky /******************************************************************************
   1427  1.1    plunky  *
   1428  1.1    plunky  *	TTY subsystem
   1429  1.1    plunky  *
   1430  1.1    plunky  */
   1431  1.1    plunky 
   1432  1.1    plunky int
   1433  1.1    plunky uhso_tty_open(dev_t dev, int flag, int mode, struct lwp *l)
   1434  1.1    plunky {
   1435  1.1    plunky 	struct uhso_softc *sc;
   1436  1.1    plunky 	struct uhso_port *hp;
   1437  1.1    plunky 	struct tty *tp;
   1438  1.1    plunky 	int error, s;
   1439  1.1    plunky 
   1440  1.1    plunky 	DPRINTF(1, "unit %d port %d\n", UHSOUNIT(dev), UHSOPORT(dev));
   1441  1.1    plunky 
   1442  1.1    plunky 	sc = device_lookup_private(&uhso_cd, UHSOUNIT(dev));
   1443  1.1    plunky 	if (sc == NULL
   1444  1.1    plunky 	    || !device_is_active(sc->sc_dev)
   1445  1.1    plunky 	    || UHSOPORT(dev) >= UHSO_PORT_MAX)
   1446  1.1    plunky 		return ENXIO;
   1447  1.1    plunky 
   1448  1.1    plunky 	hp = sc->sc_port[UHSOPORT(dev)];
   1449  1.1    plunky 	if (hp == NULL || hp->hp_tp == NULL)
   1450  1.1    plunky 		return ENXIO;
   1451  1.1    plunky 
   1452  1.1    plunky 	tp = hp->hp_tp;
   1453  1.1    plunky 	if (kauth_authorize_device_tty(l->l_cred, KAUTH_DEVICE_TTY_OPEN, tp))
   1454  1.1    plunky 		return EBUSY;
   1455  1.1    plunky 
   1456  1.1    plunky 	error = 0;
   1457  1.1    plunky 	s = spltty();
   1458  1.1    plunky 	if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) {
   1459  1.1    plunky 		tp->t_dev = dev;
   1460  1.1    plunky 		error = uhso_tty_init(hp);
   1461  1.1    plunky 	}
   1462  1.1    plunky 	splx(s);
   1463  1.1    plunky 
   1464  1.1    plunky 	if (error == 0) {
   1465  1.1    plunky 		error = ttyopen(tp, UHSODIALOUT(dev), ISSET(flag, O_NONBLOCK));
   1466  1.1    plunky 		if (error == 0) {
   1467  1.1    plunky 			error = tp->t_linesw->l_open(dev, tp);
   1468  1.1    plunky 		}
   1469  1.1    plunky 	}
   1470  1.1    plunky 
   1471  1.1    plunky 	if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0)
   1472  1.1    plunky 		uhso_tty_clean(hp);
   1473  1.1    plunky 
   1474  1.1    plunky 	DPRINTF(1, "sc=%p, hp=%p, tp=%p, error=%d\n", sc, hp, tp, error);
   1475  1.1    plunky 
   1476  1.1    plunky 	return error;
   1477  1.1    plunky }
   1478  1.1    plunky 
   1479  1.1    plunky Static int
   1480  1.1    plunky uhso_tty_init(struct uhso_port *hp)
   1481  1.1    plunky {
   1482  1.1    plunky 	struct uhso_softc *sc = hp->hp_sc;
   1483  1.1    plunky 	struct tty *tp = hp->hp_tp;
   1484  1.1    plunky 	struct termios t;
   1485  1.1    plunky 	int error;
   1486  1.1    plunky 
   1487  1.1    plunky 	DPRINTF(1, "sc=%p, hp=%p, tp=%p\n", sc, hp, tp);
   1488  1.1    plunky 
   1489  1.1    plunky 	/*
   1490  1.1    plunky 	 * Initialize the termios status to the defaults.  Add in the
   1491  1.1    plunky 	 * sticky bits from TIOCSFLAGS.
   1492  1.1    plunky 	 */
   1493  1.1    plunky 	t.c_ispeed = 0;
   1494  1.1    plunky 	t.c_ospeed = TTYDEF_SPEED;
   1495  1.1    plunky 	t.c_cflag = TTYDEF_CFLAG;
   1496  1.1    plunky 	if (ISSET(hp->hp_swflags, TIOCFLAG_CLOCAL))
   1497  1.1    plunky 		SET(t.c_cflag, CLOCAL);
   1498  1.1    plunky 	if (ISSET(hp->hp_swflags, TIOCFLAG_CRTSCTS))
   1499  1.1    plunky 		SET(t.c_cflag, CRTSCTS);
   1500  1.1    plunky 	if (ISSET(hp->hp_swflags, TIOCFLAG_MDMBUF))
   1501  1.1    plunky 		SET(t.c_cflag, MDMBUF);
   1502  1.1    plunky 
   1503  1.1    plunky 	/* Ensure uhso_tty_param() will do something. */
   1504  1.1    plunky 	tp->t_ospeed = 0;
   1505  1.1    plunky 	(void)uhso_tty_param(tp, &t);
   1506  1.1    plunky 
   1507  1.1    plunky 	tp->t_iflag = TTYDEF_IFLAG;
   1508  1.1    plunky 	tp->t_oflag = TTYDEF_OFLAG;
   1509  1.1    plunky 	tp->t_lflag = TTYDEF_LFLAG;
   1510  1.1    plunky 	ttychars(tp);
   1511  1.1    plunky 	ttsetwater(tp);
   1512  1.1    plunky 
   1513  1.1    plunky 	hp->hp_status = 0;
   1514  1.1    plunky 	error = (*hp->hp_init)(hp);
   1515  1.1    plunky 	if (error != 0)
   1516  1.1    plunky 		return error;
   1517  1.1    plunky 
   1518  1.1    plunky 	hp->hp_rxfer = usbd_alloc_xfer(sc->sc_udev);
   1519  1.1    plunky 	if (hp->hp_rxfer == NULL)
   1520  1.1    plunky 		return ENOMEM;
   1521  1.1    plunky 
   1522  1.1    plunky 	hp->hp_rbuf = usbd_alloc_buffer(hp->hp_rxfer, hp->hp_rsize);
   1523  1.1    plunky 	if (hp->hp_rbuf == NULL)
   1524  1.1    plunky 		return ENOMEM;
   1525  1.1    plunky 
   1526  1.1    plunky 	hp->hp_wxfer = usbd_alloc_xfer(sc->sc_udev);
   1527  1.1    plunky 	if (hp->hp_wxfer == NULL)
   1528  1.1    plunky 		return ENOMEM;
   1529  1.1    plunky 
   1530  1.1    plunky 	hp->hp_wbuf = usbd_alloc_buffer(hp->hp_wxfer, hp->hp_wsize);
   1531  1.1    plunky 	if (hp->hp_wbuf == NULL)
   1532  1.1    plunky 		return ENOMEM;
   1533  1.1    plunky 
   1534  1.1    plunky 	/*
   1535  1.1    plunky 	 * Turn on DTR.  We must always do this, even if carrier is not
   1536  1.1    plunky 	 * present, because otherwise we'd have to use TIOCSDTR
   1537  1.1    plunky 	 * immediately after setting CLOCAL, which applications do not
   1538  1.1    plunky 	 * expect.  We always assert DTR while the port is open
   1539  1.1    plunky 	 * unless explicitly requested to deassert it.  Ditto RTS.
   1540  1.1    plunky 	 */
   1541  1.1    plunky 	uhso_tty_control(hp, TIOCMBIS, TIOCM_DTR | TIOCM_RTS);
   1542  1.1    plunky 
   1543  1.1    plunky 	/* and start reading */
   1544  1.1    plunky 	error = (*hp->hp_read)(hp);
   1545  1.1    plunky 	if (error != 0)
   1546  1.1    plunky 		return error;
   1547  1.1    plunky 
   1548  1.1    plunky 	return 0;
   1549  1.1    plunky }
   1550  1.1    plunky 
   1551  1.1    plunky int
   1552  1.1    plunky uhso_tty_close(dev_t dev, int flag, int mode, struct lwp *l)
   1553  1.1    plunky {
   1554  1.1    plunky 	struct uhso_softc *sc = device_lookup_private(&uhso_cd, UHSOUNIT(dev));
   1555  1.1    plunky 	struct uhso_port *hp = sc->sc_port[UHSOPORT(dev)];
   1556  1.1    plunky 	struct tty *tp = hp->hp_tp;
   1557  1.1    plunky 
   1558  1.1    plunky 	if (!ISSET(tp->t_state, TS_ISOPEN))
   1559  1.1    plunky 		return 0;
   1560  1.1    plunky 
   1561  1.1    plunky 	DPRINTF(1, "sc=%p, hp=%p, tp=%p\n", sc, hp, tp);
   1562  1.1    plunky 
   1563  1.1    plunky 	sc->sc_refcnt++;
   1564  1.1    plunky 
   1565  1.1    plunky 	tp->t_linesw->l_close(tp, flag);
   1566  1.1    plunky 	ttyclose(tp);
   1567  1.1    plunky 
   1568  1.1    plunky 	if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0)
   1569  1.1    plunky 		uhso_tty_clean(hp);
   1570  1.1    plunky 
   1571  1.1    plunky 	if (--sc->sc_refcnt < 0)
   1572  1.1    plunky 		usb_detach_wakeup(sc->sc_dev);
   1573  1.1    plunky 
   1574  1.1    plunky 	return 0;
   1575  1.1    plunky }
   1576  1.1    plunky 
   1577  1.1    plunky Static void
   1578  1.1    plunky uhso_tty_clean(struct uhso_port *hp)
   1579  1.1    plunky {
   1580  1.1    plunky 
   1581  1.1    plunky 	DPRINTF(1, "hp=%p\n", hp);
   1582  1.1    plunky 
   1583  1.1    plunky 	if (ISSET(hp->hp_status, TIOCM_DTR)
   1584  1.1    plunky 	    && ISSET(hp->hp_tp->t_cflag, HUPCL))
   1585  1.1    plunky 		uhso_tty_control(hp, TIOCMBIC, TIOCM_DTR);
   1586  1.1    plunky 
   1587  1.1    plunky 	(*hp->hp_clean)(hp);
   1588  1.1    plunky 
   1589  1.1    plunky 	if (hp->hp_rxfer != NULL) {
   1590  1.1    plunky 		usbd_free_xfer(hp->hp_rxfer);
   1591  1.1    plunky 		hp->hp_rxfer = NULL;
   1592  1.1    plunky 		hp->hp_rbuf = NULL;
   1593  1.1    plunky 	}
   1594  1.1    plunky 
   1595  1.1    plunky 	if (hp->hp_wxfer != NULL) {
   1596  1.1    plunky 		usbd_free_xfer(hp->hp_wxfer);
   1597  1.1    plunky 		hp->hp_wxfer = NULL;
   1598  1.1    plunky 		hp->hp_wbuf = NULL;
   1599  1.1    plunky 	}
   1600  1.1    plunky }
   1601  1.1    plunky 
   1602  1.1    plunky int
   1603  1.1    plunky uhso_tty_read(dev_t dev, struct uio *uio, int flag)
   1604  1.1    plunky {
   1605  1.1    plunky 	struct uhso_softc *sc = device_lookup_private(&uhso_cd, UHSOUNIT(dev));
   1606  1.1    plunky 	struct uhso_port *hp = sc->sc_port[UHSOPORT(dev)];
   1607  1.1    plunky 	struct tty *tp = hp->hp_tp;
   1608  1.1    plunky 	int error;
   1609  1.1    plunky 
   1610  1.1    plunky 	if (!device_is_active(sc->sc_dev))
   1611  1.1    plunky 		return EIO;
   1612  1.1    plunky 
   1613  1.1    plunky 	DPRINTF(5, "sc=%p, hp=%p, tp=%p\n", sc, hp, tp);
   1614  1.1    plunky 
   1615  1.1    plunky 	sc->sc_refcnt++;
   1616  1.1    plunky 
   1617  1.1    plunky 	error = tp->t_linesw->l_read(tp, uio, flag);
   1618  1.1    plunky 
   1619  1.1    plunky 	if (--sc->sc_refcnt < 0)
   1620  1.1    plunky 		usb_detach_wakeup(sc->sc_dev);
   1621  1.1    plunky 
   1622  1.1    plunky 	return error;
   1623  1.1    plunky }
   1624  1.1    plunky 
   1625  1.1    plunky int
   1626  1.1    plunky uhso_tty_write(dev_t dev, struct uio *uio, int flag)
   1627  1.1    plunky {
   1628  1.1    plunky 	struct uhso_softc *sc = device_lookup_private(&uhso_cd, UHSOUNIT(dev));
   1629  1.1    plunky 	struct uhso_port *hp = sc->sc_port[UHSOPORT(dev)];
   1630  1.1    plunky 	struct tty *tp = hp->hp_tp;
   1631  1.1    plunky 	int error;
   1632  1.1    plunky 
   1633  1.1    plunky 	if (!device_is_active(sc->sc_dev))
   1634  1.1    plunky 		return EIO;
   1635  1.1    plunky 
   1636  1.1    plunky 	DPRINTF(5, "sc=%p, hp=%p, tp=%p\n", sc, hp, tp);
   1637  1.1    plunky 
   1638  1.1    plunky 	sc->sc_refcnt++;
   1639  1.1    plunky 
   1640  1.1    plunky 	error = tp->t_linesw->l_write(tp, uio, flag);
   1641  1.1    plunky 
   1642  1.1    plunky 	if (--sc->sc_refcnt < 0)
   1643  1.1    plunky 		usb_detach_wakeup(sc->sc_dev);
   1644  1.1    plunky 
   1645  1.1    plunky 	return error;
   1646  1.1    plunky }
   1647  1.1    plunky 
   1648  1.1    plunky int
   1649  1.1    plunky uhso_tty_ioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
   1650  1.1    plunky {
   1651  1.1    plunky 	struct uhso_softc *sc = device_lookup_private(&uhso_cd, UHSOUNIT(dev));
   1652  1.1    plunky 	struct uhso_port *hp = sc->sc_port[UHSOPORT(dev)];
   1653  1.1    plunky 	int error;
   1654  1.1    plunky 
   1655  1.1    plunky 	if (!device_is_active(sc->sc_dev))
   1656  1.1    plunky 		return EIO;
   1657  1.1    plunky 
   1658  1.1    plunky 	DPRINTF(1, "sc=%p, hp=%p\n", sc, hp);
   1659  1.1    plunky 
   1660  1.1    plunky 	sc->sc_refcnt++;
   1661  1.1    plunky 
   1662  1.1    plunky 	error = uhso_tty_do_ioctl(hp, cmd, data, flag, l);
   1663  1.1    plunky 
   1664  1.1    plunky 	if (--sc->sc_refcnt < 0)
   1665  1.1    plunky 		usb_detach_wakeup(sc->sc_dev);
   1666  1.1    plunky 
   1667  1.1    plunky 	return error;
   1668  1.1    plunky }
   1669  1.1    plunky 
   1670  1.1    plunky Static int
   1671  1.1    plunky uhso_tty_do_ioctl(struct uhso_port *hp, u_long cmd, void *data, int flag,
   1672  1.1    plunky     struct lwp *l)
   1673  1.1    plunky {
   1674  1.1    plunky 	struct tty *tp = hp->hp_tp;
   1675  1.1    plunky 	int error, s;
   1676  1.1    plunky 
   1677  1.1    plunky 	error = tp->t_linesw->l_ioctl(tp, cmd, data, flag, l);
   1678  1.1    plunky 	if (error != EPASSTHROUGH)
   1679  1.1    plunky 		return error;
   1680  1.1    plunky 
   1681  1.1    plunky 	error = ttioctl(tp, cmd, data, flag, l);
   1682  1.1    plunky 	if (error != EPASSTHROUGH)
   1683  1.1    plunky 		return error;
   1684  1.1    plunky 
   1685  1.1    plunky 	error = 0;
   1686  1.1    plunky 
   1687  1.1    plunky 	s = spltty();
   1688  1.1    plunky 
   1689  1.1    plunky 	switch (cmd) {
   1690  1.1    plunky 	case TIOCSDTR:
   1691  1.1    plunky 		error = uhso_tty_control(hp, TIOCMBIS, TIOCM_DTR);
   1692  1.1    plunky 		break;
   1693  1.1    plunky 
   1694  1.1    plunky 	case TIOCCDTR:
   1695  1.1    plunky 		error = uhso_tty_control(hp, TIOCMBIC, TIOCM_DTR);
   1696  1.1    plunky 		break;
   1697  1.1    plunky 
   1698  1.1    plunky 	case TIOCGFLAGS:
   1699  1.1    plunky 		*(int *)data = hp->hp_swflags;
   1700  1.1    plunky 		break;
   1701  1.1    plunky 
   1702  1.1    plunky 	case TIOCSFLAGS:
   1703  1.1    plunky 		error = kauth_authorize_device_tty(l->l_cred,
   1704  1.1    plunky 		    KAUTH_DEVICE_TTY_PRIVSET, tp);
   1705  1.1    plunky 
   1706  1.1    plunky 		if (error)
   1707  1.1    plunky 			break;
   1708  1.1    plunky 
   1709  1.1    plunky 		hp->hp_swflags = *(int *)data;
   1710  1.1    plunky 		break;
   1711  1.1    plunky 
   1712  1.1    plunky 	case TIOCMSET:
   1713  1.1    plunky 	case TIOCMBIS:
   1714  1.1    plunky 	case TIOCMBIC:
   1715  1.1    plunky 		error = uhso_tty_control(hp, cmd, *(int *)data);
   1716  1.1    plunky 		break;
   1717  1.1    plunky 
   1718  1.1    plunky 	case TIOCMGET:
   1719  1.1    plunky 		*(int *)data = hp->hp_status;
   1720  1.1    plunky 		break;
   1721  1.1    plunky 
   1722  1.1    plunky 	default:
   1723  1.1    plunky 		error = EPASSTHROUGH;
   1724  1.1    plunky 		break;
   1725  1.1    plunky 	}
   1726  1.1    plunky 
   1727  1.1    plunky 	splx(s);
   1728  1.1    plunky 
   1729  1.1    plunky 	return error;
   1730  1.1    plunky }
   1731  1.1    plunky 
   1732  1.1    plunky /* this is called with tty_lock held */
   1733  1.1    plunky void
   1734  1.1    plunky uhso_tty_stop(struct tty *tp, int flag)
   1735  1.1    plunky {
   1736  1.1    plunky #if 0
   1737  1.1    plunky 	struct uhso_softc *sc = device_lookup_private(&uhso_cd, UHSOUNIT(tp->t_dev));
   1738  1.1    plunky 	struct uhso_port *hp = sc->sc_port[UHSOPORT(tp->t_dev)];
   1739  1.1    plunky #endif
   1740  1.1    plunky }
   1741  1.1    plunky 
   1742  1.1    plunky struct tty *
   1743  1.1    plunky uhso_tty_tty(dev_t dev)
   1744  1.1    plunky {
   1745  1.1    plunky 	struct uhso_softc *sc = device_lookup_private(&uhso_cd, UHSOUNIT(dev));
   1746  1.1    plunky 	struct uhso_port *hp = sc->sc_port[UHSOPORT(dev)];
   1747  1.1    plunky 
   1748  1.1    plunky 	return hp->hp_tp;
   1749  1.1    plunky }
   1750  1.1    plunky 
   1751  1.1    plunky int
   1752  1.1    plunky uhso_tty_poll(dev_t dev, int events, struct lwp *l)
   1753  1.1    plunky {
   1754  1.1    plunky 	struct uhso_softc *sc = device_lookup_private(&uhso_cd, UHSOUNIT(dev));
   1755  1.1    plunky 	struct uhso_port *hp = sc->sc_port[UHSOPORT(dev)];
   1756  1.1    plunky 	struct tty *tp = hp->hp_tp;
   1757  1.1    plunky         int revents;
   1758  1.1    plunky 
   1759  1.1    plunky 	if (!device_is_active(sc->sc_dev))
   1760  1.1    plunky                 return POLLHUP;
   1761  1.1    plunky 
   1762  1.1    plunky 	sc->sc_refcnt++;
   1763  1.1    plunky 
   1764  1.1    plunky         revents = tp->t_linesw->l_poll(tp, events, l);
   1765  1.1    plunky 
   1766  1.1    plunky 	if (--sc->sc_refcnt < 0)
   1767  1.1    plunky 		usb_detach_wakeup(sc->sc_dev);
   1768  1.1    plunky 
   1769  1.1    plunky         return revents;
   1770  1.1    plunky }
   1771  1.1    plunky 
   1772  1.1    plunky Static int
   1773  1.1    plunky uhso_tty_param(struct tty *tp, struct termios *t)
   1774  1.1    plunky {
   1775  1.1    plunky 	struct uhso_softc *sc = device_lookup_private(&uhso_cd, UHSOUNIT(tp->t_dev));
   1776  1.1    plunky 	struct uhso_port *hp = sc->sc_port[UHSOPORT(tp->t_dev)];
   1777  1.1    plunky 
   1778  1.1    plunky 	if (!device_is_active(sc->sc_dev))
   1779  1.1    plunky 		return EIO;
   1780  1.1    plunky 
   1781  1.1    plunky 	DPRINTF(1, "hp=%p, tp=%p, termios iflag=%x, oflag=%x, cflag=%x\n",
   1782  1.1    plunky 	    hp, tp, t->c_iflag, t->c_oflag, t->c_cflag);
   1783  1.1    plunky 
   1784  1.1    plunky 	/* Check requested parameters. */
   1785  1.1    plunky 	if (t->c_ispeed != 0
   1786  1.1    plunky 	    && t->c_ispeed != t->c_ospeed)
   1787  1.1    plunky 		return EINVAL;
   1788  1.1    plunky 
   1789  1.1    plunky 	/* force CLOCAL and !HUPCL for console */
   1790  1.1    plunky 	if (ISSET(hp->hp_swflags, TIOCFLAG_SOFTCAR)) {
   1791  1.1    plunky 		SET(t->c_cflag, CLOCAL);
   1792  1.1    plunky 		CLR(t->c_cflag, HUPCL);
   1793  1.1    plunky 	}
   1794  1.1    plunky 
   1795  1.1    plunky 	/* If there were no changes, don't do anything.  */
   1796  1.1    plunky 	if (tp->t_ospeed == t->c_ospeed
   1797  1.1    plunky 	    && tp->t_cflag == t->c_cflag)
   1798  1.1    plunky 		return 0;
   1799  1.1    plunky 
   1800  1.1    plunky 	tp->t_ispeed = 0;
   1801  1.1    plunky 	tp->t_ospeed = t->c_ospeed;
   1802  1.1    plunky 	tp->t_cflag = t->c_cflag;
   1803  1.1    plunky 
   1804  1.1    plunky 	/* update tty layers idea of carrier bit */
   1805  1.1    plunky 	tp->t_linesw->l_modem(tp, ISSET(hp->hp_status, TIOCM_CAR));
   1806  1.1    plunky 	return 0;
   1807  1.1    plunky }
   1808  1.1    plunky 
   1809  1.1    plunky /* this is called with tty_lock held */
   1810  1.1    plunky Static void
   1811  1.1    plunky uhso_tty_start(struct tty *tp)
   1812  1.1    plunky {
   1813  1.1    plunky 	struct uhso_softc *sc = device_lookup_private(&uhso_cd, UHSOUNIT(tp->t_dev));
   1814  1.1    plunky 	struct uhso_port *hp = sc->sc_port[UHSOPORT(tp->t_dev)];
   1815  1.1    plunky 	int s;
   1816  1.1    plunky 
   1817  1.1    plunky 	if (!device_is_active(sc->sc_dev))
   1818  1.1    plunky 		return;
   1819  1.1    plunky 
   1820  1.1    plunky 	s = spltty();
   1821  1.1    plunky 
   1822  1.1    plunky 	if (!ISSET(tp->t_state, TS_BUSY | TS_TIMEOUT | TS_TTSTOP)
   1823  1.1    plunky 	    && ttypull(tp) != 0) {
   1824  1.1    plunky 		hp->hp_wlen = q_to_b(&tp->t_outq, hp->hp_wbuf, hp->hp_wsize);
   1825  1.1    plunky 		if (hp->hp_wlen > 0) {
   1826  1.1    plunky 			SET(tp->t_state, TS_BUSY);
   1827  1.1    plunky 			(*hp->hp_write)(hp);
   1828  1.1    plunky 		}
   1829  1.1    plunky 	}
   1830  1.1    plunky 
   1831  1.1    plunky 	splx(s);
   1832  1.1    plunky }
   1833  1.1    plunky 
   1834  1.1    plunky Static int
   1835  1.1    plunky uhso_tty_control(struct uhso_port *hp, u_long cmd, int bits)
   1836  1.1    plunky {
   1837  1.1    plunky 
   1838  1.1    plunky 	bits &= (TIOCM_DTR | TIOCM_RTS);
   1839  1.1    plunky 	DPRINTF(1, "cmd %s, DTR=%d, RTS=%d\n",
   1840  1.1    plunky 	    (cmd == TIOCMBIC ? "BIC" : (cmd == TIOCMBIS ? "BIS" : "SET")),
   1841  1.1    plunky 	    (bits & TIOCM_DTR) ? 1 : 0,
   1842  1.1    plunky 	    (bits & TIOCM_RTS) ? 1 : 0);
   1843  1.1    plunky 
   1844  1.1    plunky 	switch (cmd) {
   1845  1.1    plunky 	case TIOCMBIC:
   1846  1.1    plunky 		CLR(hp->hp_status, bits);
   1847  1.1    plunky 		break;
   1848  1.1    plunky 
   1849  1.1    plunky 	case TIOCMBIS:
   1850  1.1    plunky 		SET(hp->hp_status, bits);
   1851  1.1    plunky 		break;
   1852  1.1    plunky 
   1853  1.1    plunky 	case TIOCMSET:
   1854  1.1    plunky 		CLR(hp->hp_status, TIOCM_DTR | TIOCM_RTS);
   1855  1.1    plunky 		SET(hp->hp_status, bits);
   1856  1.1    plunky 		break;
   1857  1.1    plunky 	}
   1858  1.1    plunky 
   1859  1.1    plunky 	return (*hp->hp_control)(hp);
   1860  1.1    plunky }
   1861  1.1    plunky 
   1862  1.1    plunky 
   1863  1.1    plunky /******************************************************************************
   1864  1.1    plunky  *
   1865  1.1    plunky  *	Network Interface
   1866  1.1    plunky  *
   1867  1.1    plunky  */
   1868  1.1    plunky 
   1869  1.1    plunky Static void
   1870  1.1    plunky uhso_ifnet_attach(struct uhso_softc *sc, usbd_interface_handle ifh, int index)
   1871  1.1    plunky {
   1872  1.1    plunky 	usb_endpoint_descriptor_t *ed;
   1873  1.1    plunky 	struct uhso_port *hp;
   1874  1.1    plunky 	struct ifnet *ifp;
   1875  1.1    plunky 	int in, out;
   1876  1.1    plunky 
   1877  1.1    plunky 	ed = uhso_get_endpoint(ifh, UE_BULK, UE_DIR_IN);
   1878  1.1    plunky 	if (ed == NULL) {
   1879  1.1    plunky 		aprint_error_dev(sc->sc_dev,
   1880  1.1    plunky 		    "could not find bulk-in endpoint\n");
   1881  1.1    plunky 
   1882  1.1    plunky 		return;
   1883  1.1    plunky 	}
   1884  1.1    plunky 	in = ed->bEndpointAddress;
   1885  1.1    plunky 
   1886  1.1    plunky 	ed = uhso_get_endpoint(ifh, UE_BULK, UE_DIR_OUT);
   1887  1.1    plunky 	if (ed == NULL) {
   1888  1.1    plunky 		aprint_error_dev(sc->sc_dev,
   1889  1.1    plunky 		    "could not find bulk-out endpoint\n");
   1890  1.1    plunky 
   1891  1.1    plunky 		return;
   1892  1.1    plunky 	}
   1893  1.1    plunky 	out = ed->bEndpointAddress;
   1894  1.1    plunky 
   1895  1.1    plunky 	DPRINTF(1, "in=%d, out=%d\n", in, out);
   1896  1.1    plunky 
   1897  1.1    plunky 	if (sc->sc_port[index] != NULL) {
   1898  1.1    plunky 		aprint_error_dev(sc->sc_dev,
   1899  1.1    plunky 		    "ifnet port %d is duplicate!\n", index);
   1900  1.1    plunky 
   1901  1.1    plunky 		return;
   1902  1.1    plunky 	}
   1903  1.1    plunky 
   1904  1.1    plunky 	hp = kmem_zalloc(sizeof(struct uhso_port), KM_SLEEP);
   1905  1.1    plunky 	sc->sc_port[index] = hp;
   1906  1.1    plunky 
   1907  1.1    plunky 	ifp = if_alloc(IFT_IP);
   1908  1.1    plunky 	strlcpy(ifp->if_xname, device_xname(sc->sc_dev), IFNAMSIZ);
   1909  1.1    plunky 	ifp->if_softc = hp;
   1910  1.1    plunky 	ifp->if_mtu = UHSO_IFNET_MTU;
   1911  1.1    plunky 	ifp->if_dlt = DLT_RAW;
   1912  1.1    plunky 	ifp->if_type = IFT_IP;
   1913  1.1    plunky 	ifp->if_flags = IFF_NOARP | IFF_SIMPLEX;
   1914  1.1    plunky 	ifp->if_ioctl = uhso_ifnet_ioctl;
   1915  1.1    plunky 	ifp->if_start = uhso_ifnet_start;
   1916  1.1    plunky 	ifp->if_output = uhso_ifnet_output;
   1917  1.1    plunky 	IFQ_SET_READY(&ifp->if_snd);
   1918  1.1    plunky 
   1919  1.1    plunky 	hp->hp_sc = sc;
   1920  1.1    plunky 	hp->hp_ifp = ifp;
   1921  1.1    plunky 	hp->hp_ifh = ifh;
   1922  1.1    plunky 	hp->hp_raddr = in;
   1923  1.1    plunky 	hp->hp_waddr = out;
   1924  1.1    plunky 	hp->hp_abort = uhso_ifnet_abort;
   1925  1.1    plunky 	hp->hp_detach = uhso_ifnet_detach;
   1926  1.1    plunky 	hp->hp_init = uhso_bulk_init;
   1927  1.1    plunky 	hp->hp_clean = uhso_bulk_clean;
   1928  1.1    plunky 	hp->hp_write = uhso_bulk_write;
   1929  1.1    plunky 	hp->hp_write_cb = uhso_ifnet_write_cb;
   1930  1.1    plunky 	hp->hp_read = uhso_bulk_read;
   1931  1.1    plunky 	hp->hp_read_cb = uhso_ifnet_read_cb;
   1932  1.1    plunky 	hp->hp_wsize = MCLBYTES;
   1933  1.1    plunky 	hp->hp_rsize = MCLBYTES;
   1934  1.1    plunky 
   1935  1.1    plunky 	if_attach(ifp);
   1936  1.1    plunky 	if_alloc_sadl(ifp);
   1937  1.2     joerg 	bpf_attach(ifp, DLT_RAW, 0);
   1938  1.1    plunky 
   1939  1.1    plunky 	aprint_normal_dev(sc->sc_dev, "%s (port %d) attached as ifnet\n",
   1940  1.1    plunky 	    uhso_port_name[index], index);
   1941  1.1    plunky }
   1942  1.1    plunky 
   1943  1.1    plunky Static int
   1944  1.1    plunky uhso_ifnet_abort(struct uhso_port *hp)
   1945  1.1    plunky {
   1946  1.1    plunky 	struct ifnet *ifp = hp->hp_ifp;
   1947  1.1    plunky 
   1948  1.1    plunky 	/* All ifnet IO will abort when IFF_RUNNING is not set */
   1949  1.1    plunky 	CLR(ifp->if_flags, IFF_RUNNING);
   1950  1.1    plunky 
   1951  1.1    plunky 	return (*hp->hp_clean)(hp);
   1952  1.1    plunky }
   1953  1.1    plunky 
   1954  1.1    plunky Static int
   1955  1.1    plunky uhso_ifnet_detach(struct uhso_port *hp)
   1956  1.1    plunky {
   1957  1.1    plunky 	struct ifnet *ifp = hp->hp_ifp;
   1958  1.1    plunky 	int s;
   1959  1.1    plunky 
   1960  1.1    plunky 	s = splnet();
   1961  1.2     joerg 	bpf_detach(ifp);
   1962  1.1    plunky 	if_detach(ifp);
   1963  1.1    plunky 	splx(s);
   1964  1.1    plunky 
   1965  1.1    plunky 	kmem_free(hp, sizeof(struct uhso_port));
   1966  1.1    plunky 	return 0;
   1967  1.1    plunky }
   1968  1.1    plunky 
   1969  1.1    plunky Static void
   1970  1.1    plunky uhso_ifnet_write_cb(usbd_xfer_handle xfer, usbd_private_handle p, usbd_status status)
   1971  1.1    plunky {
   1972  1.1    plunky 	struct uhso_port *hp = p;
   1973  1.1    plunky 	struct uhso_softc *sc= hp->hp_sc;
   1974  1.1    plunky 	struct ifnet *ifp = hp->hp_ifp;
   1975  1.1    plunky 	uint32_t cc;
   1976  1.1    plunky 	int s;
   1977  1.1    plunky 
   1978  1.1    plunky 	if (--sc->sc_refcnt < 0)
   1979  1.1    plunky 		usb_detach_wakeup(sc->sc_dev);
   1980  1.1    plunky 
   1981  1.1    plunky 	if (!ISSET(ifp->if_flags, IFF_RUNNING))
   1982  1.1    plunky 		return;
   1983  1.1    plunky 
   1984  1.1    plunky 	if (status != USBD_NORMAL_COMPLETION) {
   1985  1.1    plunky 		DPRINTF(0, "non-normal status %s\n", usbd_errstr(status));
   1986  1.1    plunky 
   1987  1.1    plunky 		if (status == USBD_STALLED && hp->hp_wpipe != NULL)
   1988  1.1    plunky 			usbd_clear_endpoint_stall_async(hp->hp_wpipe);
   1989  1.1    plunky 		else
   1990  1.1    plunky 			return;
   1991  1.1    plunky 
   1992  1.1    plunky 		ifp->if_oerrors++;
   1993  1.1    plunky 	} else {
   1994  1.1    plunky 		usbd_get_xfer_status(xfer, NULL, NULL, &cc, NULL);
   1995  1.1    plunky 		DPRINTF(5, "wrote %d bytes (of %zd)\n", cc, hp->hp_wlen);
   1996  1.1    plunky 
   1997  1.1    plunky 		if (cc != hp->hp_wlen)
   1998  1.1    plunky 			DPRINTF(0, "cc=%u, wlen=%zd\n", cc, hp->hp_wlen);
   1999  1.1    plunky 
   2000  1.1    plunky 		ifp->if_opackets++;
   2001  1.1    plunky 	}
   2002  1.1    plunky 
   2003  1.1    plunky 	s = splnet();
   2004  1.1    plunky 	CLR(ifp->if_flags, IFF_OACTIVE);
   2005  1.1    plunky 	ifp->if_start(ifp);
   2006  1.1    plunky 	splx(s);
   2007  1.1    plunky }
   2008  1.1    plunky 
   2009  1.1    plunky Static void
   2010  1.1    plunky uhso_ifnet_read_cb(usbd_xfer_handle xfer, usbd_private_handle p,
   2011  1.1    plunky     usbd_status status)
   2012  1.1    plunky {
   2013  1.1    plunky 	struct uhso_port *hp = p;
   2014  1.1    plunky 	struct uhso_softc *sc= hp->hp_sc;
   2015  1.1    plunky 	struct ifnet *ifp = hp->hp_ifp;
   2016  1.1    plunky 	void *cp;
   2017  1.1    plunky 	uint32_t cc;
   2018  1.1    plunky 
   2019  1.1    plunky 	if (--sc->sc_refcnt < 0)
   2020  1.1    plunky 		usb_detach_wakeup(sc->sc_dev);
   2021  1.1    plunky 
   2022  1.1    plunky 	if (!ISSET(ifp->if_flags, IFF_RUNNING))
   2023  1.1    plunky 		return;
   2024  1.1    plunky 
   2025  1.1    plunky 	if (status != USBD_NORMAL_COMPLETION) {
   2026  1.1    plunky 		DPRINTF(0, "non-normal status: %s\n", usbd_errstr(status));
   2027  1.1    plunky 
   2028  1.1    plunky 		if (status == USBD_STALLED && hp->hp_rpipe != NULL)
   2029  1.1    plunky 			usbd_clear_endpoint_stall_async(hp->hp_rpipe);
   2030  1.1    plunky 		else
   2031  1.1    plunky 			return;
   2032  1.1    plunky 
   2033  1.1    plunky 		ifp->if_ierrors++;
   2034  1.1    plunky 		hp->hp_rlen = 0;
   2035  1.1    plunky 	} else {
   2036  1.1    plunky 		usbd_get_xfer_status(xfer, NULL, (void **)&cp, &cc, NULL);
   2037  1.1    plunky 
   2038  1.1    plunky 		hp->hp_rlen = cc;
   2039  1.1    plunky 		DPRINTF(5, "read %d bytes\n", cc);
   2040  1.1    plunky 
   2041  1.1    plunky 		uhso_ifnet_input(ifp, &hp->hp_mbuf, cp, cc);
   2042  1.1    plunky 	}
   2043  1.1    plunky 
   2044  1.1    plunky 	(*hp->hp_read)(hp);
   2045  1.1    plunky }
   2046  1.1    plunky 
   2047  1.1    plunky Static void
   2048  1.1    plunky uhso_ifnet_input(struct ifnet *ifp, struct mbuf **mb, uint8_t *cp, size_t cc)
   2049  1.1    plunky {
   2050  1.1    plunky 	struct mbuf *m;
   2051  1.1    plunky 	size_t got, len, want;
   2052  1.1    plunky 	int s;
   2053  1.1    plunky 
   2054  1.1    plunky 	/*
   2055  1.1    plunky 	 * Several IP packets might be in the same buffer, we need to
   2056  1.1    plunky 	 * separate them before handing it to the ip-stack.  We might
   2057  1.1    plunky 	 * also receive partial packets which we need to defer until
   2058  1.1    plunky 	 * we get more data.
   2059  1.1    plunky 	 */
   2060  1.1    plunky 	while (cc > 0) {
   2061  1.1    plunky 		if (*mb == NULL) {
   2062  1.1    plunky 			MGETHDR(m, M_DONTWAIT, MT_DATA);
   2063  1.1    plunky 			if (m == NULL) {
   2064  1.1    plunky 				aprint_error_ifnet(ifp, "no mbufs\n");
   2065  1.1    plunky 				ifp->if_ierrors++;
   2066  1.1    plunky 				break;
   2067  1.1    plunky 			}
   2068  1.1    plunky 
   2069  1.1    plunky 			MCLGET(m, M_DONTWAIT);
   2070  1.1    plunky 			if (!ISSET(m->m_flags, M_EXT)) {
   2071  1.1    plunky 				aprint_error_ifnet(ifp, "no mbuf clusters\n");
   2072  1.1    plunky 				ifp->if_ierrors++;
   2073  1.1    plunky 				m_freem(m);
   2074  1.1    plunky 				break;
   2075  1.1    plunky 			}
   2076  1.1    plunky 
   2077  1.1    plunky 			got = 0;
   2078  1.1    plunky 		} else {
   2079  1.1    plunky 			m = *mb;
   2080  1.1    plunky 			*mb = NULL;
   2081  1.1    plunky 			got = m->m_pkthdr.len;
   2082  1.1    plunky 		}
   2083  1.1    plunky 
   2084  1.1    plunky 		/* make sure that the incoming packet is ok */
   2085  1.1    plunky 		if (got == 0)
   2086  1.1    plunky 			mtod(m, uint8_t *)[0] = cp[0];
   2087  1.1    plunky 
   2088  1.1    plunky 		want = mtod(m, struct ip *)->ip_hl << 2;
   2089  1.1    plunky 		if (mtod(m, struct ip *)->ip_v != 4
   2090  1.1    plunky 		    || want != sizeof(struct ip)) {
   2091  1.1    plunky 			aprint_error_ifnet(ifp, "bad IP header (v=%d, hl=%zd)\n",
   2092  1.1    plunky 			    mtod(m, struct ip *)->ip_v, want);
   2093  1.1    plunky 
   2094  1.1    plunky 			ifp->if_ierrors++;
   2095  1.1    plunky 			m_freem(m);
   2096  1.1    plunky 			break;
   2097  1.1    plunky 		}
   2098  1.1    plunky 
   2099  1.1    plunky 		/* ensure we have the IP header.. */
   2100  1.1    plunky 		if (got < want) {
   2101  1.1    plunky 			len = MIN(want - got, cc);
   2102  1.1    plunky 			memcpy(mtod(m, uint8_t *) + got, cp, len);
   2103  1.1    plunky 			got += len;
   2104  1.1    plunky 			cc -= len;
   2105  1.1    plunky 			cp += len;
   2106  1.1    plunky 
   2107  1.1    plunky 			if (got < want) {
   2108  1.1    plunky 				DPRINTF(5, "waiting for IP header "
   2109  1.1    plunky 					   "(got %zd want %zd)\n", got, want);
   2110  1.1    plunky 
   2111  1.1    plunky 				m->m_pkthdr.len = got;
   2112  1.1    plunky 				*mb = m;
   2113  1.1    plunky 				break;
   2114  1.1    plunky 			}
   2115  1.1    plunky 		}
   2116  1.1    plunky 
   2117  1.1    plunky 		/* ..and the packet body */
   2118  1.1    plunky 		want = ntohs(mtod(m, struct ip *)->ip_len);
   2119  1.1    plunky 		if (got < want) {
   2120  1.1    plunky 			len = MIN(want - got, cc);
   2121  1.1    plunky 			memcpy(mtod(m, uint8_t *) + got, cp, len);
   2122  1.1    plunky 			got += len;
   2123  1.1    plunky 			cc -= len;
   2124  1.1    plunky 			cp += len;
   2125  1.1    plunky 
   2126  1.1    plunky 			if (got < want) {
   2127  1.1    plunky 				DPRINTF(5, "waiting for IP packet "
   2128  1.1    plunky 					   "(got %zd want %zd)\n", got, want);
   2129  1.1    plunky 
   2130  1.1    plunky 				m->m_pkthdr.len = got;
   2131  1.1    plunky 				*mb = m;
   2132  1.1    plunky 				break;
   2133  1.1    plunky 			}
   2134  1.1    plunky 		} else if (want > got) {
   2135  1.1    plunky 			aprint_error_ifnet(ifp, "bad IP packet (len=%zd)\n",
   2136  1.1    plunky 			    want);
   2137  1.1    plunky 
   2138  1.1    plunky 			ifp->if_ierrors++;
   2139  1.1    plunky 			m_freem(m);
   2140  1.1    plunky 			break;
   2141  1.1    plunky 		}
   2142  1.1    plunky 
   2143  1.1    plunky 		m->m_pkthdr.rcvif = ifp;
   2144  1.1    plunky 		m->m_pkthdr.len = m->m_len = got;
   2145  1.1    plunky 
   2146  1.1    plunky 		s = splnet();
   2147  1.1    plunky 
   2148  1.2     joerg 		bpf_mtap(ifp, m);
   2149  1.1    plunky 
   2150  1.1    plunky 		ifp->if_ipackets++;
   2151  1.1    plunky 		ifp->if_ibytes += m->m_pkthdr.len;
   2152  1.1    plunky 
   2153  1.1    plunky 		if (IF_QFULL(&ipintrq)) {
   2154  1.1    plunky 			IF_DROP(&ipintrq);
   2155  1.1    plunky 			m_freem(m);
   2156  1.1    plunky 		} else {
   2157  1.1    plunky 			IF_ENQUEUE(&ipintrq, m);
   2158  1.1    plunky 			schednetisr(NETISR_IP);
   2159  1.1    plunky 		}
   2160  1.1    plunky 
   2161  1.1    plunky 		splx(s);
   2162  1.1    plunky 	}
   2163  1.1    plunky }
   2164  1.1    plunky 
   2165  1.1    plunky Static int
   2166  1.1    plunky uhso_ifnet_ioctl(struct ifnet *ifp, u_long cmd, void *data)
   2167  1.1    plunky {
   2168  1.1    plunky 	struct uhso_port *hp = ifp->if_softc;
   2169  1.1    plunky 	int error, s;
   2170  1.1    plunky 
   2171  1.1    plunky 	s = splnet();
   2172  1.1    plunky 
   2173  1.1    plunky 	switch (cmd) {
   2174  1.1    plunky 	case SIOCINITIFADDR:
   2175  1.1    plunky 		switch (((struct ifaddr *)data)->ifa_addr->sa_family) {
   2176  1.1    plunky #ifdef INET
   2177  1.1    plunky 		case AF_INET:
   2178  1.1    plunky 			if (!ISSET(ifp->if_flags, IFF_RUNNING)) {
   2179  1.1    plunky 				SET(ifp->if_flags, IFF_UP);
   2180  1.1    plunky 				error = uhso_ifnet_init(hp);
   2181  1.1    plunky 				if (error != 0) {
   2182  1.1    plunky 					uhso_ifnet_clean(hp);
   2183  1.1    plunky 					break;
   2184  1.1    plunky 				}
   2185  1.1    plunky 
   2186  1.1    plunky 				SET(ifp->if_flags, IFF_RUNNING);
   2187  1.1    plunky 				DPRINTF(1, "hp=%p, ifp=%p INITIFADDR\n", hp, ifp);
   2188  1.1    plunky 				break;
   2189  1.1    plunky 			}
   2190  1.1    plunky 
   2191  1.1    plunky 			error = 0;
   2192  1.1    plunky 			break;
   2193  1.1    plunky #endif
   2194  1.1    plunky 
   2195  1.1    plunky 		default:
   2196  1.1    plunky 			error = EAFNOSUPPORT;
   2197  1.1    plunky 			break;
   2198  1.1    plunky 		}
   2199  1.1    plunky 		break;
   2200  1.1    plunky 
   2201  1.1    plunky 	case SIOCSIFMTU:
   2202  1.1    plunky 		if (((struct ifreq *)data)->ifr_mtu > hp->hp_wsize) {
   2203  1.1    plunky 			error = EINVAL;
   2204  1.1    plunky 			break;
   2205  1.1    plunky 		}
   2206  1.1    plunky 
   2207  1.1    plunky 		error = ifioctl_common(ifp, cmd, data);
   2208  1.1    plunky 		if (error == ENETRESET)
   2209  1.1    plunky 			error = 0;
   2210  1.1    plunky 
   2211  1.1    plunky 		break;
   2212  1.1    plunky 
   2213  1.1    plunky 	case SIOCSIFFLAGS:
   2214  1.1    plunky 		error = ifioctl_common(ifp, cmd, data);
   2215  1.1    plunky 		if (error != 0)
   2216  1.1    plunky 			break;
   2217  1.1    plunky 
   2218  1.1    plunky 		switch (ifp->if_flags & (IFF_UP | IFF_RUNNING)) {
   2219  1.1    plunky 		case IFF_UP:
   2220  1.1    plunky 			error = uhso_ifnet_init(hp);
   2221  1.1    plunky 			if (error != 0) {
   2222  1.1    plunky 				uhso_ifnet_clean(hp);
   2223  1.1    plunky 				break;
   2224  1.1    plunky 			}
   2225  1.1    plunky 
   2226  1.1    plunky 			SET(ifp->if_flags, IFF_RUNNING);
   2227  1.1    plunky 			DPRINTF(1, "hp=%p, ifp=%p RUNNING\n", hp, ifp);
   2228  1.1    plunky 			break;
   2229  1.1    plunky 
   2230  1.1    plunky 		case IFF_RUNNING:
   2231  1.1    plunky 			uhso_ifnet_clean(hp);
   2232  1.1    plunky 			CLR(ifp->if_flags, IFF_RUNNING);
   2233  1.1    plunky 			DPRINTF(1, "hp=%p, ifp=%p STOPPED\n", hp, ifp);
   2234  1.1    plunky 			break;
   2235  1.1    plunky 
   2236  1.1    plunky 		default:
   2237  1.1    plunky 			break;
   2238  1.1    plunky 		}
   2239  1.1    plunky 		break;
   2240  1.1    plunky 
   2241  1.1    plunky 	default:
   2242  1.1    plunky 		error = ifioctl_common(ifp, cmd, data);
   2243  1.1    plunky 		break;
   2244  1.1    plunky 	}
   2245  1.1    plunky 
   2246  1.1    plunky 	splx(s);
   2247  1.1    plunky 
   2248  1.1    plunky 	return error;
   2249  1.1    plunky }
   2250  1.1    plunky 
   2251  1.1    plunky /* is only called if IFF_RUNNING not set */
   2252  1.1    plunky Static int
   2253  1.1    plunky uhso_ifnet_init(struct uhso_port *hp)
   2254  1.1    plunky {
   2255  1.1    plunky 	struct uhso_softc *sc = hp->hp_sc;
   2256  1.1    plunky 	int error;
   2257  1.1    plunky 
   2258  1.1    plunky 	DPRINTF(1, "sc=%p, hp=%p\n", sc, hp);
   2259  1.1    plunky 
   2260  1.1    plunky 	if (!device_is_active(sc->sc_dev))
   2261  1.1    plunky 		return EIO;
   2262  1.1    plunky 
   2263  1.1    plunky 	error = (*hp->hp_init)(hp);
   2264  1.1    plunky 	if (error != 0)
   2265  1.1    plunky 		return error;
   2266  1.1    plunky 
   2267  1.1    plunky 	hp->hp_rxfer = usbd_alloc_xfer(sc->sc_udev);
   2268  1.1    plunky 	if (hp->hp_rxfer == NULL)
   2269  1.1    plunky 		return ENOMEM;
   2270  1.1    plunky 
   2271  1.1    plunky 	hp->hp_rbuf = usbd_alloc_buffer(hp->hp_rxfer, hp->hp_rsize);
   2272  1.1    plunky 	if (hp->hp_rbuf == NULL)
   2273  1.1    plunky 		return ENOMEM;
   2274  1.1    plunky 
   2275  1.1    plunky 	hp->hp_wxfer = usbd_alloc_xfer(sc->sc_udev);
   2276  1.1    plunky 	if (hp->hp_wxfer == NULL)
   2277  1.1    plunky 		return ENOMEM;
   2278  1.1    plunky 
   2279  1.1    plunky 	hp->hp_wbuf = usbd_alloc_buffer(hp->hp_wxfer, hp->hp_wsize);
   2280  1.1    plunky 	if (hp->hp_wbuf == NULL)
   2281  1.1    plunky 		return ENOMEM;
   2282  1.1    plunky 
   2283  1.1    plunky 	error = (*hp->hp_read)(hp);
   2284  1.1    plunky 	if (error != 0)
   2285  1.1    plunky 		return error;
   2286  1.1    plunky 
   2287  1.1    plunky 	return 0;
   2288  1.1    plunky }
   2289  1.1    plunky 
   2290  1.1    plunky Static void
   2291  1.1    plunky uhso_ifnet_clean(struct uhso_port *hp)
   2292  1.1    plunky {
   2293  1.1    plunky 
   2294  1.1    plunky 	DPRINTF(1, "hp=%p\n", hp);
   2295  1.1    plunky 
   2296  1.1    plunky 	(*hp->hp_clean)(hp);
   2297  1.1    plunky 
   2298  1.1    plunky 	if (hp->hp_rxfer != NULL) {
   2299  1.1    plunky 		usbd_free_xfer(hp->hp_rxfer);
   2300  1.1    plunky 		hp->hp_rxfer = NULL;
   2301  1.1    plunky 		hp->hp_rbuf = NULL;
   2302  1.1    plunky 	}
   2303  1.1    plunky 
   2304  1.1    plunky 	if (hp->hp_wxfer != NULL) {
   2305  1.1    plunky 		usbd_free_xfer(hp->hp_wxfer);
   2306  1.1    plunky 		hp->hp_wxfer = NULL;
   2307  1.1    plunky 		hp->hp_wbuf = NULL;
   2308  1.1    plunky 	}
   2309  1.1    plunky }
   2310  1.1    plunky 
   2311  1.1    plunky /* called at splnet() with IFF_OACTIVE not set */
   2312  1.1    plunky Static void
   2313  1.1    plunky uhso_ifnet_start(struct ifnet *ifp)
   2314  1.1    plunky {
   2315  1.1    plunky 	struct uhso_port *hp = ifp->if_softc;
   2316  1.1    plunky 	struct mbuf *m;
   2317  1.1    plunky 
   2318  1.1    plunky 	KASSERT(!ISSET(ifp->if_flags, IFF_OACTIVE));
   2319  1.1    plunky 
   2320  1.1    plunky 	if (!ISSET(ifp->if_flags, IFF_RUNNING))
   2321  1.1    plunky 		return;
   2322  1.1    plunky 
   2323  1.1    plunky 	if (IFQ_IS_EMPTY(&ifp->if_snd)) {
   2324  1.1    plunky 		DPRINTF(5, "finished sending\n");
   2325  1.1    plunky 		return;
   2326  1.1    plunky 	}
   2327  1.1    plunky 
   2328  1.1    plunky 	SET(ifp->if_flags, IFF_OACTIVE);
   2329  1.1    plunky 	IFQ_DEQUEUE(&ifp->if_snd, m);
   2330  1.1    plunky 	hp->hp_wlen = m->m_pkthdr.len;
   2331  1.1    plunky 	if (hp->hp_wlen > hp->hp_wsize) {
   2332  1.1    plunky 		aprint_error_ifnet(ifp,
   2333  1.1    plunky 		    "packet too long (%zd > %zd), truncating\n",
   2334  1.1    plunky 		    hp->hp_wlen, hp->hp_wsize);
   2335  1.1    plunky 
   2336  1.1    plunky 		hp->hp_wlen = hp->hp_wsize;
   2337  1.1    plunky 	}
   2338  1.1    plunky 
   2339  1.2     joerg 	bpf_mtap(ifp, m);
   2340  1.1    plunky 
   2341  1.1    plunky 	m_copydata(m, 0, hp->hp_wlen, hp->hp_wbuf);
   2342  1.1    plunky 	m_freem(m);
   2343  1.1    plunky 
   2344  1.1    plunky 	if ((*hp->hp_write)(hp) != 0) {
   2345  1.1    plunky 		ifp->if_oerrors++;
   2346  1.1    plunky 		CLR(ifp->if_flags, IFF_OACTIVE);
   2347  1.1    plunky 	}
   2348  1.1    plunky }
   2349  1.1    plunky 
   2350  1.1    plunky Static int
   2351  1.1    plunky uhso_ifnet_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst,
   2352  1.1    plunky     struct rtentry *rt0)
   2353  1.1    plunky {
   2354  1.1    plunky 	ALTQ_DECL(struct altq_pktattr pktattr);
   2355  1.1    plunky 	int error;
   2356  1.1    plunky 
   2357  1.1    plunky 	if (!ISSET(ifp->if_flags, IFF_RUNNING))
   2358  1.1    plunky 		return EIO;
   2359  1.1    plunky 
   2360  1.1    plunky 	IFQ_CLASSIFY(&ifp->if_snd, m, dst->sa_family, &pktattr);
   2361  1.1    plunky 
   2362  1.1    plunky 	switch (dst->sa_family) {
   2363  1.1    plunky #ifdef INET
   2364  1.1    plunky 	case AF_INET:
   2365  1.1    plunky 		error = ifq_enqueue(ifp, m ALTQ_COMMA ALTQ_DECL(&pktattr));
   2366  1.1    plunky 		break;
   2367  1.1    plunky #endif
   2368  1.1    plunky 
   2369  1.1    plunky 	default:
   2370  1.1    plunky 		DPRINTF(0, "unsupported address family %d\n", dst->sa_family);
   2371  1.1    plunky 		error = EAFNOSUPPORT;
   2372  1.1    plunky 		m_freem(m);
   2373  1.1    plunky 		break;
   2374  1.1    plunky 	}
   2375  1.1    plunky 
   2376  1.1    plunky 	return error;
   2377  1.1    plunky }
   2378