xref: /dragonfly/sys/bus/u4b/serial/ubser.c (revision 2b3f93ea)
112bd3c8bSSascha Wildner /*-
212bd3c8bSSascha Wildner  * Copyright (c) 2004 Bernd Walter <ticso@FreeBSD.org>
312bd3c8bSSascha Wildner  *
412bd3c8bSSascha Wildner  * $URL: https://devel.bwct.de/svn/projects/ubser/ubser.c $
512bd3c8bSSascha Wildner  * $Date: 2004-02-29 01:53:10 +0100 (Sun, 29 Feb 2004) $
612bd3c8bSSascha Wildner  * $Author: ticso $
712bd3c8bSSascha Wildner  * $Rev: 1127 $
812bd3c8bSSascha Wildner  */
912bd3c8bSSascha Wildner 
1012bd3c8bSSascha Wildner /*-
1112bd3c8bSSascha Wildner  * Copyright (c) 2001-2002, Shunsuke Akiyama <akiyama@jp.FreeBSD.org>.
1212bd3c8bSSascha Wildner  * All rights reserved.
1312bd3c8bSSascha Wildner  *
1412bd3c8bSSascha Wildner  * Redistribution and use in source and binary forms, with or without
1512bd3c8bSSascha Wildner  * modification, are permitted provided that the following conditions
1612bd3c8bSSascha Wildner  * are met:
1712bd3c8bSSascha Wildner  * 1. Redistributions of source code must retain the above copyright
1812bd3c8bSSascha Wildner  *    notice, this list of conditions and the following disclaimer.
1912bd3c8bSSascha Wildner  * 2. Redistributions in binary form must reproduce the above copyright
2012bd3c8bSSascha Wildner  *    notice, this list of conditions and the following disclaimer in the
2112bd3c8bSSascha Wildner  *    documentation and/or other materials provided with the distribution.
2212bd3c8bSSascha Wildner  *
2312bd3c8bSSascha Wildner  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
2412bd3c8bSSascha Wildner  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2512bd3c8bSSascha Wildner  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2612bd3c8bSSascha Wildner  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2712bd3c8bSSascha Wildner  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2812bd3c8bSSascha Wildner  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2912bd3c8bSSascha Wildner  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3012bd3c8bSSascha Wildner  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3112bd3c8bSSascha Wildner  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3212bd3c8bSSascha Wildner  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3312bd3c8bSSascha Wildner  * SUCH DAMAGE.
3412bd3c8bSSascha Wildner  */
3512bd3c8bSSascha Wildner 
3612bd3c8bSSascha Wildner /*-
3712bd3c8bSSascha Wildner  * Copyright (c) 2000 The NetBSD Foundation, Inc.
3812bd3c8bSSascha Wildner  * All rights reserved.
3912bd3c8bSSascha Wildner  *
4012bd3c8bSSascha Wildner  * This code is derived from software contributed to The NetBSD Foundation
4112bd3c8bSSascha Wildner  * by Lennart Augustsson (lennart@augustsson.net).
4212bd3c8bSSascha Wildner  *
4312bd3c8bSSascha Wildner  * Redistribution and use in source and binary forms, with or without
4412bd3c8bSSascha Wildner  * modification, are permitted provided that the following conditions
4512bd3c8bSSascha Wildner  * are met:
4612bd3c8bSSascha Wildner  * 1. Redistributions of source code must retain the above copyright
4712bd3c8bSSascha Wildner  *    notice, this list of conditions and the following disclaimer.
4812bd3c8bSSascha Wildner  * 2. Redistributions in binary form must reproduce the above copyright
4912bd3c8bSSascha Wildner  *    notice, this list of conditions and the following disclaimer in the
5012bd3c8bSSascha Wildner  *    documentation and/or other materials provided with the distribution.
5112bd3c8bSSascha Wildner  *
5212bd3c8bSSascha Wildner  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
5312bd3c8bSSascha Wildner  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
5412bd3c8bSSascha Wildner  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
5512bd3c8bSSascha Wildner  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
5612bd3c8bSSascha Wildner  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
5712bd3c8bSSascha Wildner  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
5812bd3c8bSSascha Wildner  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
5912bd3c8bSSascha Wildner  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
6012bd3c8bSSascha Wildner  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
6112bd3c8bSSascha Wildner  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
6212bd3c8bSSascha Wildner  * POSSIBILITY OF SUCH DAMAGE.
6312bd3c8bSSascha Wildner  */
6412bd3c8bSSascha Wildner 
6512bd3c8bSSascha Wildner /*
6612bd3c8bSSascha Wildner  * BWCT serial adapter driver
6712bd3c8bSSascha Wildner  */
6812bd3c8bSSascha Wildner 
6912bd3c8bSSascha Wildner #include <sys/stdint.h>
7012bd3c8bSSascha Wildner #include <sys/param.h>
7112bd3c8bSSascha Wildner #include <sys/queue.h>
7212bd3c8bSSascha Wildner #include <sys/types.h>
7312bd3c8bSSascha Wildner #include <sys/systm.h>
7412bd3c8bSSascha Wildner #include <sys/kernel.h>
7512bd3c8bSSascha Wildner #include <sys/bus.h>
7612bd3c8bSSascha Wildner #include <sys/module.h>
7712bd3c8bSSascha Wildner #include <sys/lock.h>
7812bd3c8bSSascha Wildner #include <sys/condvar.h>
7912bd3c8bSSascha Wildner #include <sys/sysctl.h>
8012bd3c8bSSascha Wildner #include <sys/unistd.h>
8112bd3c8bSSascha Wildner #include <sys/callout.h>
8212bd3c8bSSascha Wildner #include <sys/malloc.h>
83*2b3f93eaSMatthew Dillon #include <sys/caps.h>
84fd10b026SMarkus Pfeiffer #include <sys/serial.h>
8512bd3c8bSSascha Wildner 
86a81829f2SSascha Wildner #include <bus/u4b/usb.h>
87a81829f2SSascha Wildner #include <bus/u4b/usbdi.h>
88a81829f2SSascha Wildner #include <bus/u4b/usbdi_util.h>
8959f64dacSMarkus Pfeiffer #include "usbdevs.h"
9012bd3c8bSSascha Wildner 
9112bd3c8bSSascha Wildner #define	USB_DEBUG_VAR ubser_debug
92a81829f2SSascha Wildner #include <bus/u4b/usb_debug.h>
93a81829f2SSascha Wildner #include <bus/u4b/usb_process.h>
9412bd3c8bSSascha Wildner 
95a81829f2SSascha Wildner #include <bus/u4b/serial/usb_serial.h>
9612bd3c8bSSascha Wildner 
9712bd3c8bSSascha Wildner #define	UBSER_UNIT_MAX	32
9812bd3c8bSSascha Wildner 
9912bd3c8bSSascha Wildner /* Vendor Interface Requests */
10012bd3c8bSSascha Wildner #define	VENDOR_GET_NUMSER		0x01
10112bd3c8bSSascha Wildner #define	VENDOR_SET_BREAK		0x02
10212bd3c8bSSascha Wildner #define	VENDOR_CLEAR_BREAK		0x03
10312bd3c8bSSascha Wildner 
10412bd3c8bSSascha Wildner #ifdef USB_DEBUG
10512bd3c8bSSascha Wildner static int ubser_debug = 0;
10612bd3c8bSSascha Wildner 
10712bd3c8bSSascha Wildner static SYSCTL_NODE(_hw_usb, OID_AUTO, ubser, CTLFLAG_RW, 0, "USB ubser");
108a9b765b7SSascha Wildner SYSCTL_INT(_hw_usb_ubser, OID_AUTO, debug, CTLFLAG_RW,
10912bd3c8bSSascha Wildner     &ubser_debug, 0, "ubser debug level");
11012bd3c8bSSascha Wildner #endif
11112bd3c8bSSascha Wildner 
11212bd3c8bSSascha Wildner enum {
11312bd3c8bSSascha Wildner 	UBSER_BULK_DT_WR,
11412bd3c8bSSascha Wildner 	UBSER_BULK_DT_RD,
11512bd3c8bSSascha Wildner 	UBSER_N_TRANSFER,
11612bd3c8bSSascha Wildner };
11712bd3c8bSSascha Wildner 
11812bd3c8bSSascha Wildner struct ubser_softc {
11912bd3c8bSSascha Wildner 	struct ucom_super_softc sc_super_ucom;
12012bd3c8bSSascha Wildner 	struct ucom_softc sc_ucom[UBSER_UNIT_MAX];
12112bd3c8bSSascha Wildner 
12212bd3c8bSSascha Wildner 	struct usb_xfer *sc_xfer[UBSER_N_TRANSFER];
12312bd3c8bSSascha Wildner 	struct usb_device *sc_udev;
124a81829f2SSascha Wildner 	struct lock sc_lock;
12512bd3c8bSSascha Wildner 
12612bd3c8bSSascha Wildner 	uint16_t sc_tx_size;
12712bd3c8bSSascha Wildner 
12812bd3c8bSSascha Wildner 	uint8_t	sc_numser;
12912bd3c8bSSascha Wildner 	uint8_t	sc_iface_no;
13012bd3c8bSSascha Wildner 	uint8_t	sc_iface_index;
13112bd3c8bSSascha Wildner 	uint8_t	sc_curr_tx_unit;
13212bd3c8bSSascha Wildner 	uint8_t	sc_name[16];
13312bd3c8bSSascha Wildner };
13412bd3c8bSSascha Wildner 
13512bd3c8bSSascha Wildner /* prototypes */
13612bd3c8bSSascha Wildner 
13712bd3c8bSSascha Wildner static device_probe_t ubser_probe;
13812bd3c8bSSascha Wildner static device_attach_t ubser_attach;
13912bd3c8bSSascha Wildner static device_detach_t ubser_detach;
14012bd3c8bSSascha Wildner 
14112bd3c8bSSascha Wildner static usb_callback_t ubser_write_callback;
14212bd3c8bSSascha Wildner static usb_callback_t ubser_read_callback;
14312bd3c8bSSascha Wildner 
14412bd3c8bSSascha Wildner static int	ubser_pre_param(struct ucom_softc *, struct termios *);
14512bd3c8bSSascha Wildner static void	ubser_cfg_set_break(struct ucom_softc *, uint8_t);
14612bd3c8bSSascha Wildner static void	ubser_cfg_get_status(struct ucom_softc *, uint8_t *,
14712bd3c8bSSascha Wildner 		    uint8_t *);
14812bd3c8bSSascha Wildner static void	ubser_start_read(struct ucom_softc *);
14912bd3c8bSSascha Wildner static void	ubser_stop_read(struct ucom_softc *);
15012bd3c8bSSascha Wildner static void	ubser_start_write(struct ucom_softc *);
15112bd3c8bSSascha Wildner static void	ubser_stop_write(struct ucom_softc *);
15212bd3c8bSSascha Wildner static void	ubser_poll(struct ucom_softc *ucom);
15312bd3c8bSSascha Wildner 
15412bd3c8bSSascha Wildner static const struct usb_config ubser_config[UBSER_N_TRANSFER] = {
15512bd3c8bSSascha Wildner 
15612bd3c8bSSascha Wildner 	[UBSER_BULK_DT_WR] = {
15712bd3c8bSSascha Wildner 		.type = UE_BULK,
15812bd3c8bSSascha Wildner 		.endpoint = UE_ADDR_ANY,
15912bd3c8bSSascha Wildner 		.direction = UE_DIR_OUT,
16012bd3c8bSSascha Wildner 		.bufsize = 0,	/* use wMaxPacketSize */
16112bd3c8bSSascha Wildner 		.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
16212bd3c8bSSascha Wildner 		.callback = &ubser_write_callback,
16312bd3c8bSSascha Wildner 	},
16412bd3c8bSSascha Wildner 
16512bd3c8bSSascha Wildner 	[UBSER_BULK_DT_RD] = {
16612bd3c8bSSascha Wildner 		.type = UE_BULK,
16712bd3c8bSSascha Wildner 		.endpoint = UE_ADDR_ANY,
16812bd3c8bSSascha Wildner 		.direction = UE_DIR_IN,
16912bd3c8bSSascha Wildner 		.bufsize = 0,	/* use wMaxPacketSize */
17012bd3c8bSSascha Wildner 		.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
17112bd3c8bSSascha Wildner 		.callback = &ubser_read_callback,
17212bd3c8bSSascha Wildner 	},
17312bd3c8bSSascha Wildner };
17412bd3c8bSSascha Wildner 
17512bd3c8bSSascha Wildner static const struct ucom_callback ubser_callback = {
17612bd3c8bSSascha Wildner 	.ucom_cfg_set_break = &ubser_cfg_set_break,
17712bd3c8bSSascha Wildner 	.ucom_cfg_get_status = &ubser_cfg_get_status,
17812bd3c8bSSascha Wildner 	.ucom_pre_param = &ubser_pre_param,
17912bd3c8bSSascha Wildner 	.ucom_start_read = &ubser_start_read,
18012bd3c8bSSascha Wildner 	.ucom_stop_read = &ubser_stop_read,
18112bd3c8bSSascha Wildner 	.ucom_start_write = &ubser_start_write,
18212bd3c8bSSascha Wildner 	.ucom_stop_write = &ubser_stop_write,
18312bd3c8bSSascha Wildner 	.ucom_poll = &ubser_poll,
18412bd3c8bSSascha Wildner };
18512bd3c8bSSascha Wildner 
18612bd3c8bSSascha Wildner static device_method_t ubser_methods[] = {
18712bd3c8bSSascha Wildner 	DEVMETHOD(device_probe, ubser_probe),
18812bd3c8bSSascha Wildner 	DEVMETHOD(device_attach, ubser_attach),
18912bd3c8bSSascha Wildner 	DEVMETHOD(device_detach, ubser_detach),
190d3c9c58eSSascha Wildner 	DEVMETHOD_END
19112bd3c8bSSascha Wildner };
19212bd3c8bSSascha Wildner 
19312bd3c8bSSascha Wildner static devclass_t ubser_devclass;
19412bd3c8bSSascha Wildner 
19512bd3c8bSSascha Wildner static driver_t ubser_driver = {
19612bd3c8bSSascha Wildner 	.name = "ubser",
19712bd3c8bSSascha Wildner 	.methods = ubser_methods,
19812bd3c8bSSascha Wildner 	.size = sizeof(struct ubser_softc),
19912bd3c8bSSascha Wildner };
20012bd3c8bSSascha Wildner 
2013a25be87SSascha Wildner DRIVER_MODULE(ubser, uhub, ubser_driver, ubser_devclass, NULL, NULL);
20212bd3c8bSSascha Wildner MODULE_DEPEND(ubser, ucom, 1, 1, 1);
20312bd3c8bSSascha Wildner MODULE_DEPEND(ubser, usb, 1, 1, 1);
20412bd3c8bSSascha Wildner MODULE_VERSION(ubser, 1);
20512bd3c8bSSascha Wildner 
20612bd3c8bSSascha Wildner static int
ubser_probe(device_t dev)20712bd3c8bSSascha Wildner ubser_probe(device_t dev)
20812bd3c8bSSascha Wildner {
20912bd3c8bSSascha Wildner 	struct usb_attach_arg *uaa = device_get_ivars(dev);
21012bd3c8bSSascha Wildner 
21112bd3c8bSSascha Wildner 	if (uaa->usb_mode != USB_MODE_HOST) {
21212bd3c8bSSascha Wildner 		return (ENXIO);
21312bd3c8bSSascha Wildner 	}
21412bd3c8bSSascha Wildner 	/* check if this is a BWCT vendor specific ubser interface */
21512bd3c8bSSascha Wildner 	if ((strcmp(usb_get_manufacturer(uaa->device), "BWCT") == 0) &&
21612bd3c8bSSascha Wildner 	    (uaa->info.bInterfaceClass == 0xff) &&
21712bd3c8bSSascha Wildner 	    (uaa->info.bInterfaceSubClass == 0x00))
21812bd3c8bSSascha Wildner 		return (0);
21912bd3c8bSSascha Wildner 
22012bd3c8bSSascha Wildner 	return (ENXIO);
22112bd3c8bSSascha Wildner }
22212bd3c8bSSascha Wildner 
22312bd3c8bSSascha Wildner static int
ubser_attach(device_t dev)22412bd3c8bSSascha Wildner ubser_attach(device_t dev)
22512bd3c8bSSascha Wildner {
22612bd3c8bSSascha Wildner 	struct usb_attach_arg *uaa = device_get_ivars(dev);
22712bd3c8bSSascha Wildner 	struct ubser_softc *sc = device_get_softc(dev);
22812bd3c8bSSascha Wildner 	struct usb_device_request req;
22912bd3c8bSSascha Wildner 	uint8_t n;
23012bd3c8bSSascha Wildner 	int error;
23112bd3c8bSSascha Wildner 
23212bd3c8bSSascha Wildner 	device_set_usb_desc(dev);
233a81829f2SSascha Wildner 	lockinit(&sc->sc_lock, "ubser", 0, LK_CANRECURSE);
23412bd3c8bSSascha Wildner 
235a81829f2SSascha Wildner 	ksnprintf(sc->sc_name, sizeof(sc->sc_name), "%s",
23612bd3c8bSSascha Wildner 	    device_get_nameunit(dev));
23712bd3c8bSSascha Wildner 
23812bd3c8bSSascha Wildner 	sc->sc_iface_no = uaa->info.bIfaceNum;
23912bd3c8bSSascha Wildner 	sc->sc_iface_index = uaa->info.bIfaceIndex;
24012bd3c8bSSascha Wildner 	sc->sc_udev = uaa->device;
24112bd3c8bSSascha Wildner 
24212bd3c8bSSascha Wildner 	/* get number of serials */
24312bd3c8bSSascha Wildner 	req.bmRequestType = UT_READ_VENDOR_INTERFACE;
24412bd3c8bSSascha Wildner 	req.bRequest = VENDOR_GET_NUMSER;
24512bd3c8bSSascha Wildner 	USETW(req.wValue, 0);
24612bd3c8bSSascha Wildner 	req.wIndex[0] = sc->sc_iface_no;
24712bd3c8bSSascha Wildner 	req.wIndex[1] = 0;
24812bd3c8bSSascha Wildner 	USETW(req.wLength, 1);
24912bd3c8bSSascha Wildner 	error = usbd_do_request_flags(uaa->device, NULL,
25012bd3c8bSSascha Wildner 	    &req, &sc->sc_numser,
25112bd3c8bSSascha Wildner 	    0, NULL, USB_DEFAULT_TIMEOUT);
25212bd3c8bSSascha Wildner 
25312bd3c8bSSascha Wildner 	if (error || (sc->sc_numser == 0)) {
25412bd3c8bSSascha Wildner 		device_printf(dev, "failed to get number "
25512bd3c8bSSascha Wildner 		    "of serial ports: %s\n",
25612bd3c8bSSascha Wildner 		    usbd_errstr(error));
25712bd3c8bSSascha Wildner 		goto detach;
25812bd3c8bSSascha Wildner 	}
25912bd3c8bSSascha Wildner 	if (sc->sc_numser > UBSER_UNIT_MAX)
26012bd3c8bSSascha Wildner 		sc->sc_numser = UBSER_UNIT_MAX;
26112bd3c8bSSascha Wildner 
26212bd3c8bSSascha Wildner 	device_printf(dev, "found %i serials\n", sc->sc_numser);
26312bd3c8bSSascha Wildner 
26412bd3c8bSSascha Wildner 	error = usbd_transfer_setup(uaa->device, &sc->sc_iface_index,
265a81829f2SSascha Wildner 	    sc->sc_xfer, ubser_config, UBSER_N_TRANSFER, sc, &sc->sc_lock);
26612bd3c8bSSascha Wildner 	if (error) {
26712bd3c8bSSascha Wildner 		goto detach;
26812bd3c8bSSascha Wildner 	}
26912bd3c8bSSascha Wildner 	sc->sc_tx_size = usbd_xfer_max_len(sc->sc_xfer[UBSER_BULK_DT_WR]);
27012bd3c8bSSascha Wildner 
27112bd3c8bSSascha Wildner 	if (sc->sc_tx_size == 0) {
27212bd3c8bSSascha Wildner 		DPRINTFN(0, "invalid tx_size\n");
27312bd3c8bSSascha Wildner 		goto detach;
27412bd3c8bSSascha Wildner 	}
27512bd3c8bSSascha Wildner 	/* initialize port numbers */
27612bd3c8bSSascha Wildner 
27712bd3c8bSSascha Wildner 	for (n = 0; n < sc->sc_numser; n++) {
27812bd3c8bSSascha Wildner 		sc->sc_ucom[n].sc_portno = n;
27912bd3c8bSSascha Wildner 	}
28012bd3c8bSSascha Wildner 
28112bd3c8bSSascha Wildner 	error = ucom_attach(&sc->sc_super_ucom, sc->sc_ucom,
282a81829f2SSascha Wildner 	    sc->sc_numser, sc, &ubser_callback, &sc->sc_lock);
28312bd3c8bSSascha Wildner 	if (error) {
28412bd3c8bSSascha Wildner 		goto detach;
28512bd3c8bSSascha Wildner 	}
28612bd3c8bSSascha Wildner 	ucom_set_pnpinfo_usb(&sc->sc_super_ucom, dev);
28712bd3c8bSSascha Wildner 
288a81829f2SSascha Wildner 	lockmgr(&sc->sc_lock, LK_EXCLUSIVE);
28912bd3c8bSSascha Wildner 	usbd_xfer_set_stall(sc->sc_xfer[UBSER_BULK_DT_WR]);
29012bd3c8bSSascha Wildner 	usbd_xfer_set_stall(sc->sc_xfer[UBSER_BULK_DT_RD]);
29112bd3c8bSSascha Wildner 	usbd_transfer_start(sc->sc_xfer[UBSER_BULK_DT_RD]);
292a81829f2SSascha Wildner 	lockmgr(&sc->sc_lock, LK_RELEASE);
29312bd3c8bSSascha Wildner 
29412bd3c8bSSascha Wildner 	return (0);			/* success */
29512bd3c8bSSascha Wildner 
29612bd3c8bSSascha Wildner detach:
29712bd3c8bSSascha Wildner 	ubser_detach(dev);
29812bd3c8bSSascha Wildner 	return (ENXIO);			/* failure */
29912bd3c8bSSascha Wildner }
30012bd3c8bSSascha Wildner 
30112bd3c8bSSascha Wildner static int
ubser_detach(device_t dev)30212bd3c8bSSascha Wildner ubser_detach(device_t dev)
30312bd3c8bSSascha Wildner {
30412bd3c8bSSascha Wildner 	struct ubser_softc *sc = device_get_softc(dev);
30512bd3c8bSSascha Wildner 
30612bd3c8bSSascha Wildner 	DPRINTF("\n");
30712bd3c8bSSascha Wildner 
30812bd3c8bSSascha Wildner 	ucom_detach(&sc->sc_super_ucom, sc->sc_ucom);
30912bd3c8bSSascha Wildner 	usbd_transfer_unsetup(sc->sc_xfer, UBSER_N_TRANSFER);
310a81829f2SSascha Wildner 	lockuninit(&sc->sc_lock);
31112bd3c8bSSascha Wildner 
31212bd3c8bSSascha Wildner 	return (0);
31312bd3c8bSSascha Wildner }
31412bd3c8bSSascha Wildner 
31512bd3c8bSSascha Wildner static int
ubser_pre_param(struct ucom_softc * ucom,struct termios * t)31612bd3c8bSSascha Wildner ubser_pre_param(struct ucom_softc *ucom, struct termios *t)
31712bd3c8bSSascha Wildner {
31812bd3c8bSSascha Wildner 	DPRINTF("\n");
31912bd3c8bSSascha Wildner 
32012bd3c8bSSascha Wildner 	/*
32112bd3c8bSSascha Wildner 	 * The firmware on our devices can only do 8n1@9600bps
32212bd3c8bSSascha Wildner 	 * without handshake.
32312bd3c8bSSascha Wildner 	 * We refuse to accept other configurations.
32412bd3c8bSSascha Wildner 	 */
32512bd3c8bSSascha Wildner 
32612bd3c8bSSascha Wildner 	/* ensure 9600bps */
32712bd3c8bSSascha Wildner 	switch (t->c_ospeed) {
32812bd3c8bSSascha Wildner 	case 9600:
32912bd3c8bSSascha Wildner 		break;
33012bd3c8bSSascha Wildner 	default:
33112bd3c8bSSascha Wildner 		return (EINVAL);
33212bd3c8bSSascha Wildner 	}
33312bd3c8bSSascha Wildner 
33412bd3c8bSSascha Wildner 	/* 2 stop bits not possible */
33512bd3c8bSSascha Wildner 	if (t->c_cflag & CSTOPB)
33612bd3c8bSSascha Wildner 		return (EINVAL);
33712bd3c8bSSascha Wildner 
33812bd3c8bSSascha Wildner 	/* XXX parity handling not possible with current firmware */
33912bd3c8bSSascha Wildner 	if (t->c_cflag & PARENB)
34012bd3c8bSSascha Wildner 		return (EINVAL);
34112bd3c8bSSascha Wildner 
34212bd3c8bSSascha Wildner 	/* we can only do 8 data bits */
34312bd3c8bSSascha Wildner 	switch (t->c_cflag & CSIZE) {
34412bd3c8bSSascha Wildner 	case CS8:
34512bd3c8bSSascha Wildner 		break;
34612bd3c8bSSascha Wildner 	default:
34712bd3c8bSSascha Wildner 		return (EINVAL);
34812bd3c8bSSascha Wildner 	}
34912bd3c8bSSascha Wildner 
35012bd3c8bSSascha Wildner 	/* we can't do any kind of hardware handshaking */
35112bd3c8bSSascha Wildner 	if ((t->c_cflag &
35212bd3c8bSSascha Wildner 	    (CRTS_IFLOW | CDTR_IFLOW | CDSR_OFLOW | CCAR_OFLOW)) != 0)
35312bd3c8bSSascha Wildner 		return (EINVAL);
35412bd3c8bSSascha Wildner 
35512bd3c8bSSascha Wildner 	/*
35612bd3c8bSSascha Wildner 	 * XXX xon/xoff not supported by the firmware!
35712bd3c8bSSascha Wildner 	 * This is handled within FreeBSD only and may overflow buffers
35812bd3c8bSSascha Wildner 	 * because of delayed reaction due to device buffering.
35912bd3c8bSSascha Wildner 	 */
36012bd3c8bSSascha Wildner 
36112bd3c8bSSascha Wildner 	return (0);
36212bd3c8bSSascha Wildner }
36312bd3c8bSSascha Wildner 
36412bd3c8bSSascha Wildner static __inline void
ubser_inc_tx_unit(struct ubser_softc * sc)36512bd3c8bSSascha Wildner ubser_inc_tx_unit(struct ubser_softc *sc)
36612bd3c8bSSascha Wildner {
36712bd3c8bSSascha Wildner 	sc->sc_curr_tx_unit++;
36812bd3c8bSSascha Wildner 	if (sc->sc_curr_tx_unit >= sc->sc_numser) {
36912bd3c8bSSascha Wildner 		sc->sc_curr_tx_unit = 0;
37012bd3c8bSSascha Wildner 	}
37112bd3c8bSSascha Wildner }
37212bd3c8bSSascha Wildner 
37312bd3c8bSSascha Wildner static void
ubser_write_callback(struct usb_xfer * xfer,usb_error_t error)37412bd3c8bSSascha Wildner ubser_write_callback(struct usb_xfer *xfer, usb_error_t error)
37512bd3c8bSSascha Wildner {
37612bd3c8bSSascha Wildner 	struct ubser_softc *sc = usbd_xfer_softc(xfer);
37712bd3c8bSSascha Wildner 	struct usb_page_cache *pc;
37812bd3c8bSSascha Wildner 	uint8_t buf[1];
37912bd3c8bSSascha Wildner 	uint8_t first_unit = sc->sc_curr_tx_unit;
38012bd3c8bSSascha Wildner 	uint32_t actlen;
38112bd3c8bSSascha Wildner 
38212bd3c8bSSascha Wildner 	switch (USB_GET_STATE(xfer)) {
38312bd3c8bSSascha Wildner 	case USB_ST_SETUP:
38412bd3c8bSSascha Wildner 	case USB_ST_TRANSFERRED:
38512bd3c8bSSascha Wildner tr_setup:
38612bd3c8bSSascha Wildner 		pc = usbd_xfer_get_frame(xfer, 0);
38712bd3c8bSSascha Wildner 		do {
38812bd3c8bSSascha Wildner 			if (ucom_get_data(sc->sc_ucom + sc->sc_curr_tx_unit,
38912bd3c8bSSascha Wildner 			    pc, 1, sc->sc_tx_size - 1,
39012bd3c8bSSascha Wildner 			    &actlen)) {
39112bd3c8bSSascha Wildner 
39212bd3c8bSSascha Wildner 				buf[0] = sc->sc_curr_tx_unit;
39312bd3c8bSSascha Wildner 
39412bd3c8bSSascha Wildner 				usbd_copy_in(pc, 0, buf, 1);
39512bd3c8bSSascha Wildner 
39612bd3c8bSSascha Wildner 				usbd_xfer_set_frame_len(xfer, 0, actlen + 1);
39712bd3c8bSSascha Wildner 				usbd_transfer_submit(xfer);
39812bd3c8bSSascha Wildner 
39912bd3c8bSSascha Wildner 				ubser_inc_tx_unit(sc);	/* round robin */
40012bd3c8bSSascha Wildner 
40112bd3c8bSSascha Wildner 				break;
40212bd3c8bSSascha Wildner 			}
40312bd3c8bSSascha Wildner 			ubser_inc_tx_unit(sc);
40412bd3c8bSSascha Wildner 
40512bd3c8bSSascha Wildner 		} while (sc->sc_curr_tx_unit != first_unit);
40612bd3c8bSSascha Wildner 
40712bd3c8bSSascha Wildner 		return;
40812bd3c8bSSascha Wildner 
40912bd3c8bSSascha Wildner 	default:			/* Error */
41012bd3c8bSSascha Wildner 		if (error != USB_ERR_CANCELLED) {
41112bd3c8bSSascha Wildner 			/* try to clear stall first */
41212bd3c8bSSascha Wildner 			usbd_xfer_set_stall(xfer);
41312bd3c8bSSascha Wildner 			goto tr_setup;
41412bd3c8bSSascha Wildner 		}
41512bd3c8bSSascha Wildner 		return;
41612bd3c8bSSascha Wildner 
41712bd3c8bSSascha Wildner 	}
41812bd3c8bSSascha Wildner }
41912bd3c8bSSascha Wildner 
42012bd3c8bSSascha Wildner static void
ubser_read_callback(struct usb_xfer * xfer,usb_error_t error)42112bd3c8bSSascha Wildner ubser_read_callback(struct usb_xfer *xfer, usb_error_t error)
42212bd3c8bSSascha Wildner {
42312bd3c8bSSascha Wildner 	struct ubser_softc *sc = usbd_xfer_softc(xfer);
42412bd3c8bSSascha Wildner 	struct usb_page_cache *pc;
42512bd3c8bSSascha Wildner 	uint8_t buf[1];
42612bd3c8bSSascha Wildner 	int actlen;
42712bd3c8bSSascha Wildner 
42812bd3c8bSSascha Wildner 	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
42912bd3c8bSSascha Wildner 
43012bd3c8bSSascha Wildner 	switch (USB_GET_STATE(xfer)) {
43112bd3c8bSSascha Wildner 	case USB_ST_TRANSFERRED:
43212bd3c8bSSascha Wildner 		if (actlen < 1) {
43312bd3c8bSSascha Wildner 			DPRINTF("invalid actlen=0!\n");
43412bd3c8bSSascha Wildner 			goto tr_setup;
43512bd3c8bSSascha Wildner 		}
43612bd3c8bSSascha Wildner 		pc = usbd_xfer_get_frame(xfer, 0);
43712bd3c8bSSascha Wildner 		usbd_copy_out(pc, 0, buf, 1);
43812bd3c8bSSascha Wildner 
43912bd3c8bSSascha Wildner 		if (buf[0] >= sc->sc_numser) {
44012bd3c8bSSascha Wildner 			DPRINTF("invalid serial number!\n");
44112bd3c8bSSascha Wildner 			goto tr_setup;
44212bd3c8bSSascha Wildner 		}
44312bd3c8bSSascha Wildner 		ucom_put_data(sc->sc_ucom + buf[0], pc, 1, actlen - 1);
44412bd3c8bSSascha Wildner 
44512bd3c8bSSascha Wildner 	case USB_ST_SETUP:
44612bd3c8bSSascha Wildner tr_setup:
44712bd3c8bSSascha Wildner 		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
44812bd3c8bSSascha Wildner 		usbd_transfer_submit(xfer);
44912bd3c8bSSascha Wildner 		return;
45012bd3c8bSSascha Wildner 
45112bd3c8bSSascha Wildner 	default:			/* Error */
45212bd3c8bSSascha Wildner 		if (error != USB_ERR_CANCELLED) {
45312bd3c8bSSascha Wildner 			/* try to clear stall first */
45412bd3c8bSSascha Wildner 			usbd_xfer_set_stall(xfer);
45512bd3c8bSSascha Wildner 			goto tr_setup;
45612bd3c8bSSascha Wildner 		}
45712bd3c8bSSascha Wildner 		return;
45812bd3c8bSSascha Wildner 
45912bd3c8bSSascha Wildner 	}
46012bd3c8bSSascha Wildner }
46112bd3c8bSSascha Wildner 
46212bd3c8bSSascha Wildner static void
ubser_cfg_set_break(struct ucom_softc * ucom,uint8_t onoff)46312bd3c8bSSascha Wildner ubser_cfg_set_break(struct ucom_softc *ucom, uint8_t onoff)
46412bd3c8bSSascha Wildner {
46512bd3c8bSSascha Wildner 	struct ubser_softc *sc = ucom->sc_parent;
46612bd3c8bSSascha Wildner 	uint8_t x = ucom->sc_portno;
46712bd3c8bSSascha Wildner 	struct usb_device_request req;
46812bd3c8bSSascha Wildner 	usb_error_t err;
46912bd3c8bSSascha Wildner 
47012bd3c8bSSascha Wildner 	if (onoff) {
47112bd3c8bSSascha Wildner 
47212bd3c8bSSascha Wildner 		req.bmRequestType = UT_READ_VENDOR_INTERFACE;
47312bd3c8bSSascha Wildner 		req.bRequest = VENDOR_SET_BREAK;
47412bd3c8bSSascha Wildner 		req.wValue[0] = x;
47512bd3c8bSSascha Wildner 		req.wValue[1] = 0;
47612bd3c8bSSascha Wildner 		req.wIndex[0] = sc->sc_iface_no;
47712bd3c8bSSascha Wildner 		req.wIndex[1] = 0;
47812bd3c8bSSascha Wildner 		USETW(req.wLength, 0);
47912bd3c8bSSascha Wildner 
48012bd3c8bSSascha Wildner 		err = ucom_cfg_do_request(sc->sc_udev, ucom,
48112bd3c8bSSascha Wildner 		    &req, NULL, 0, 1000);
48212bd3c8bSSascha Wildner 		if (err) {
48312bd3c8bSSascha Wildner 			DPRINTFN(0, "send break failed, error=%s\n",
48412bd3c8bSSascha Wildner 			    usbd_errstr(err));
48512bd3c8bSSascha Wildner 		}
48612bd3c8bSSascha Wildner 	}
48712bd3c8bSSascha Wildner }
48812bd3c8bSSascha Wildner 
48912bd3c8bSSascha Wildner static void
ubser_cfg_get_status(struct ucom_softc * ucom,uint8_t * lsr,uint8_t * msr)49012bd3c8bSSascha Wildner ubser_cfg_get_status(struct ucom_softc *ucom, uint8_t *lsr, uint8_t *msr)
49112bd3c8bSSascha Wildner {
49212bd3c8bSSascha Wildner 	/* fake status bits */
49312bd3c8bSSascha Wildner 	*lsr = 0;
49412bd3c8bSSascha Wildner 	*msr = SER_DCD;
49512bd3c8bSSascha Wildner }
49612bd3c8bSSascha Wildner 
49712bd3c8bSSascha Wildner static void
ubser_start_read(struct ucom_softc * ucom)49812bd3c8bSSascha Wildner ubser_start_read(struct ucom_softc *ucom)
49912bd3c8bSSascha Wildner {
50012bd3c8bSSascha Wildner 	struct ubser_softc *sc = ucom->sc_parent;
50112bd3c8bSSascha Wildner 
50212bd3c8bSSascha Wildner 	usbd_transfer_start(sc->sc_xfer[UBSER_BULK_DT_RD]);
50312bd3c8bSSascha Wildner }
50412bd3c8bSSascha Wildner 
50512bd3c8bSSascha Wildner static void
ubser_stop_read(struct ucom_softc * ucom)50612bd3c8bSSascha Wildner ubser_stop_read(struct ucom_softc *ucom)
50712bd3c8bSSascha Wildner {
50812bd3c8bSSascha Wildner 	struct ubser_softc *sc = ucom->sc_parent;
50912bd3c8bSSascha Wildner 
51012bd3c8bSSascha Wildner 	usbd_transfer_stop(sc->sc_xfer[UBSER_BULK_DT_RD]);
51112bd3c8bSSascha Wildner }
51212bd3c8bSSascha Wildner 
51312bd3c8bSSascha Wildner static void
ubser_start_write(struct ucom_softc * ucom)51412bd3c8bSSascha Wildner ubser_start_write(struct ucom_softc *ucom)
51512bd3c8bSSascha Wildner {
51612bd3c8bSSascha Wildner 	struct ubser_softc *sc = ucom->sc_parent;
51712bd3c8bSSascha Wildner 
51812bd3c8bSSascha Wildner 	usbd_transfer_start(sc->sc_xfer[UBSER_BULK_DT_WR]);
51912bd3c8bSSascha Wildner }
52012bd3c8bSSascha Wildner 
52112bd3c8bSSascha Wildner static void
ubser_stop_write(struct ucom_softc * ucom)52212bd3c8bSSascha Wildner ubser_stop_write(struct ucom_softc *ucom)
52312bd3c8bSSascha Wildner {
52412bd3c8bSSascha Wildner 	struct ubser_softc *sc = ucom->sc_parent;
52512bd3c8bSSascha Wildner 
52612bd3c8bSSascha Wildner 	usbd_transfer_stop(sc->sc_xfer[UBSER_BULK_DT_WR]);
52712bd3c8bSSascha Wildner }
52812bd3c8bSSascha Wildner 
52912bd3c8bSSascha Wildner static void
ubser_poll(struct ucom_softc * ucom)53012bd3c8bSSascha Wildner ubser_poll(struct ucom_softc *ucom)
53112bd3c8bSSascha Wildner {
53212bd3c8bSSascha Wildner 	struct ubser_softc *sc = ucom->sc_parent;
53312bd3c8bSSascha Wildner 	usbd_transfer_poll(sc->sc_xfer, UBSER_N_TRANSFER);
53412bd3c8bSSascha Wildner }
535