102ac6454SAndrew Thompson /*-
25d38a4d4SEd Schouten * Copyright (c) 2004 Bernd Walter <ticso@FreeBSD.org>
302ac6454SAndrew Thompson *
402ac6454SAndrew Thompson * $URL: https://devel.bwct.de/svn/projects/ubser/ubser.c $
502ac6454SAndrew Thompson * $Date: 2004-02-29 01:53:10 +0100 (Sun, 29 Feb 2004) $
602ac6454SAndrew Thompson * $Author: ticso $
702ac6454SAndrew Thompson * $Rev: 1127 $
802ac6454SAndrew Thompson */
902ac6454SAndrew Thompson
1002ac6454SAndrew Thompson /*-
11*eebd9d53SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
12718cf2ccSPedro F. Giffuni *
1302ac6454SAndrew Thompson * Copyright (c) 2001-2002, Shunsuke Akiyama <akiyama@jp.FreeBSD.org>.
1402ac6454SAndrew Thompson * All rights reserved.
1502ac6454SAndrew Thompson *
1602ac6454SAndrew Thompson * Redistribution and use in source and binary forms, with or without
1702ac6454SAndrew Thompson * modification, are permitted provided that the following conditions
1802ac6454SAndrew Thompson * are met:
1902ac6454SAndrew Thompson * 1. Redistributions of source code must retain the above copyright
2002ac6454SAndrew Thompson * notice, this list of conditions and the following disclaimer.
2102ac6454SAndrew Thompson * 2. Redistributions in binary form must reproduce the above copyright
2202ac6454SAndrew Thompson * notice, this list of conditions and the following disclaimer in the
2302ac6454SAndrew Thompson * documentation and/or other materials provided with the distribution.
2402ac6454SAndrew Thompson *
2502ac6454SAndrew Thompson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
2602ac6454SAndrew Thompson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2702ac6454SAndrew Thompson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2802ac6454SAndrew Thompson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2902ac6454SAndrew Thompson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
3002ac6454SAndrew Thompson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
3102ac6454SAndrew Thompson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3202ac6454SAndrew Thompson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3302ac6454SAndrew Thompson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3402ac6454SAndrew Thompson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3502ac6454SAndrew Thompson * SUCH DAMAGE.
3602ac6454SAndrew Thompson */
3702ac6454SAndrew Thompson
3802ac6454SAndrew Thompson /*-
3902ac6454SAndrew Thompson * Copyright (c) 2000 The NetBSD Foundation, Inc.
4002ac6454SAndrew Thompson * All rights reserved.
4102ac6454SAndrew Thompson *
4202ac6454SAndrew Thompson * This code is derived from software contributed to The NetBSD Foundation
4302ac6454SAndrew Thompson * by Lennart Augustsson (lennart@augustsson.net).
4402ac6454SAndrew Thompson *
4502ac6454SAndrew Thompson * Redistribution and use in source and binary forms, with or without
4602ac6454SAndrew Thompson * modification, are permitted provided that the following conditions
4702ac6454SAndrew Thompson * are met:
4802ac6454SAndrew Thompson * 1. Redistributions of source code must retain the above copyright
4902ac6454SAndrew Thompson * notice, this list of conditions and the following disclaimer.
5002ac6454SAndrew Thompson * 2. Redistributions in binary form must reproduce the above copyright
5102ac6454SAndrew Thompson * notice, this list of conditions and the following disclaimer in the
5202ac6454SAndrew Thompson * documentation and/or other materials provided with the distribution.
5302ac6454SAndrew Thompson *
5402ac6454SAndrew Thompson * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
5502ac6454SAndrew Thompson * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
5602ac6454SAndrew Thompson * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
5702ac6454SAndrew Thompson * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
5802ac6454SAndrew Thompson * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
5902ac6454SAndrew Thompson * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
6002ac6454SAndrew Thompson * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
6102ac6454SAndrew Thompson * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
6202ac6454SAndrew Thompson * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
6302ac6454SAndrew Thompson * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
6402ac6454SAndrew Thompson * POSSIBILITY OF SUCH DAMAGE.
6502ac6454SAndrew Thompson */
6602ac6454SAndrew Thompson
6702ac6454SAndrew Thompson #include <sys/cdefs.h>
6802ac6454SAndrew Thompson /*
6902ac6454SAndrew Thompson * BWCT serial adapter driver
7002ac6454SAndrew Thompson */
7102ac6454SAndrew Thompson
72ed6d949aSAndrew Thompson #include <sys/stdint.h>
73ed6d949aSAndrew Thompson #include <sys/stddef.h>
74ed6d949aSAndrew Thompson #include <sys/param.h>
75ed6d949aSAndrew Thompson #include <sys/queue.h>
76ed6d949aSAndrew Thompson #include <sys/types.h>
77ed6d949aSAndrew Thompson #include <sys/systm.h>
78ed6d949aSAndrew Thompson #include <sys/kernel.h>
79ed6d949aSAndrew Thompson #include <sys/bus.h>
80ed6d949aSAndrew Thompson #include <sys/module.h>
81ed6d949aSAndrew Thompson #include <sys/lock.h>
82ed6d949aSAndrew Thompson #include <sys/mutex.h>
83ed6d949aSAndrew Thompson #include <sys/condvar.h>
84ed6d949aSAndrew Thompson #include <sys/sysctl.h>
85ed6d949aSAndrew Thompson #include <sys/sx.h>
86ed6d949aSAndrew Thompson #include <sys/unistd.h>
87ed6d949aSAndrew Thompson #include <sys/callout.h>
88ed6d949aSAndrew Thompson #include <sys/malloc.h>
89ed6d949aSAndrew Thompson #include <sys/priv.h>
90ed6d949aSAndrew Thompson
9102ac6454SAndrew Thompson #include <dev/usb/usb.h>
92ed6d949aSAndrew Thompson #include <dev/usb/usbdi.h>
93ed6d949aSAndrew Thompson #include <dev/usb/usbdi_util.h>
94ed6d949aSAndrew Thompson #include "usbdevs.h"
9502ac6454SAndrew Thompson
9602ac6454SAndrew Thompson #define USB_DEBUG_VAR ubser_debug
9702ac6454SAndrew Thompson #include <dev/usb/usb_debug.h>
9802ac6454SAndrew Thompson #include <dev/usb/usb_process.h>
9902ac6454SAndrew Thompson
10002ac6454SAndrew Thompson #include <dev/usb/serial/usb_serial.h>
10102ac6454SAndrew Thompson
10202ac6454SAndrew Thompson #define UBSER_UNIT_MAX 32
10302ac6454SAndrew Thompson
10402ac6454SAndrew Thompson /* Vendor Interface Requests */
10502ac6454SAndrew Thompson #define VENDOR_GET_NUMSER 0x01
10602ac6454SAndrew Thompson #define VENDOR_SET_BREAK 0x02
10702ac6454SAndrew Thompson #define VENDOR_CLEAR_BREAK 0x03
10802ac6454SAndrew Thompson
109b850ecc1SAndrew Thompson #ifdef USB_DEBUG
11002ac6454SAndrew Thompson static int ubser_debug = 0;
11102ac6454SAndrew Thompson
112f8d2b1f3SPawel Biernacki static SYSCTL_NODE(_hw_usb, OID_AUTO, ubser, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
113f8d2b1f3SPawel Biernacki "USB ubser");
114ece4b0bdSHans Petter Selasky SYSCTL_INT(_hw_usb_ubser, OID_AUTO, debug, CTLFLAG_RWTUN,
11502ac6454SAndrew Thompson &ubser_debug, 0, "ubser debug level");
11602ac6454SAndrew Thompson #endif
11702ac6454SAndrew Thompson
11802ac6454SAndrew Thompson enum {
11902ac6454SAndrew Thompson UBSER_BULK_DT_WR,
12002ac6454SAndrew Thompson UBSER_BULK_DT_RD,
12102ac6454SAndrew Thompson UBSER_N_TRANSFER,
12202ac6454SAndrew Thompson };
12302ac6454SAndrew Thompson
12402ac6454SAndrew Thompson struct ubser_softc {
125760bc48eSAndrew Thompson struct ucom_super_softc sc_super_ucom;
126760bc48eSAndrew Thompson struct ucom_softc sc_ucom[UBSER_UNIT_MAX];
12702ac6454SAndrew Thompson
128760bc48eSAndrew Thompson struct usb_xfer *sc_xfer[UBSER_N_TRANSFER];
129760bc48eSAndrew Thompson struct usb_device *sc_udev;
130deefe583SAndrew Thompson struct mtx sc_mtx;
13102ac6454SAndrew Thompson
13202ac6454SAndrew Thompson uint16_t sc_tx_size;
13302ac6454SAndrew Thompson
13402ac6454SAndrew Thompson uint8_t sc_numser;
13502ac6454SAndrew Thompson uint8_t sc_iface_no;
13602ac6454SAndrew Thompson uint8_t sc_iface_index;
13702ac6454SAndrew Thompson uint8_t sc_curr_tx_unit;
13802ac6454SAndrew Thompson };
13902ac6454SAndrew Thompson
14002ac6454SAndrew Thompson /* prototypes */
14102ac6454SAndrew Thompson
14202ac6454SAndrew Thompson static device_probe_t ubser_probe;
14302ac6454SAndrew Thompson static device_attach_t ubser_attach;
14402ac6454SAndrew Thompson static device_detach_t ubser_detach;
145c01fc06eSHans Petter Selasky static void ubser_free_softc(struct ubser_softc *);
14602ac6454SAndrew Thompson
147e0a69b51SAndrew Thompson static usb_callback_t ubser_write_callback;
148e0a69b51SAndrew Thompson static usb_callback_t ubser_read_callback;
14902ac6454SAndrew Thompson
1505805d178SHans Petter Selasky static void ubser_free(struct ucom_softc *);
151760bc48eSAndrew Thompson static int ubser_pre_param(struct ucom_softc *, struct termios *);
152760bc48eSAndrew Thompson static void ubser_cfg_set_break(struct ucom_softc *, uint8_t);
153760bc48eSAndrew Thompson static void ubser_cfg_get_status(struct ucom_softc *, uint8_t *,
15402ac6454SAndrew Thompson uint8_t *);
155760bc48eSAndrew Thompson static void ubser_start_read(struct ucom_softc *);
156760bc48eSAndrew Thompson static void ubser_stop_read(struct ucom_softc *);
157760bc48eSAndrew Thompson static void ubser_start_write(struct ucom_softc *);
158760bc48eSAndrew Thompson static void ubser_stop_write(struct ucom_softc *);
159655dc9d0SAndrew Thompson static void ubser_poll(struct ucom_softc *ucom);
16002ac6454SAndrew Thompson
161760bc48eSAndrew Thompson static const struct usb_config ubser_config[UBSER_N_TRANSFER] = {
16202ac6454SAndrew Thompson [UBSER_BULK_DT_WR] = {
16302ac6454SAndrew Thompson .type = UE_BULK,
16402ac6454SAndrew Thompson .endpoint = UE_ADDR_ANY,
16502ac6454SAndrew Thompson .direction = UE_DIR_OUT,
1664eae601eSAndrew Thompson .bufsize = 0, /* use wMaxPacketSize */
1674eae601eSAndrew Thompson .flags = {.pipe_bof = 1,.force_short_xfer = 1,},
1684eae601eSAndrew Thompson .callback = &ubser_write_callback,
16902ac6454SAndrew Thompson },
17002ac6454SAndrew Thompson
17102ac6454SAndrew Thompson [UBSER_BULK_DT_RD] = {
17202ac6454SAndrew Thompson .type = UE_BULK,
17302ac6454SAndrew Thompson .endpoint = UE_ADDR_ANY,
17402ac6454SAndrew Thompson .direction = UE_DIR_IN,
1754eae601eSAndrew Thompson .bufsize = 0, /* use wMaxPacketSize */
1764eae601eSAndrew Thompson .flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
1774eae601eSAndrew Thompson .callback = &ubser_read_callback,
17802ac6454SAndrew Thompson },
17902ac6454SAndrew Thompson };
18002ac6454SAndrew Thompson
181760bc48eSAndrew Thompson static const struct ucom_callback ubser_callback = {
182a593f6b8SAndrew Thompson .ucom_cfg_set_break = &ubser_cfg_set_break,
183a593f6b8SAndrew Thompson .ucom_cfg_get_status = &ubser_cfg_get_status,
184a593f6b8SAndrew Thompson .ucom_pre_param = &ubser_pre_param,
185a593f6b8SAndrew Thompson .ucom_start_read = &ubser_start_read,
186a593f6b8SAndrew Thompson .ucom_stop_read = &ubser_stop_read,
187a593f6b8SAndrew Thompson .ucom_start_write = &ubser_start_write,
188a593f6b8SAndrew Thompson .ucom_stop_write = &ubser_stop_write,
189655dc9d0SAndrew Thompson .ucom_poll = &ubser_poll,
1905805d178SHans Petter Selasky .ucom_free = &ubser_free,
19102ac6454SAndrew Thompson };
19202ac6454SAndrew Thompson
19302ac6454SAndrew Thompson static device_method_t ubser_methods[] = {
19402ac6454SAndrew Thompson DEVMETHOD(device_probe, ubser_probe),
19502ac6454SAndrew Thompson DEVMETHOD(device_attach, ubser_attach),
19602ac6454SAndrew Thompson DEVMETHOD(device_detach, ubser_detach),
1975805d178SHans Petter Selasky DEVMETHOD_END
19802ac6454SAndrew Thompson };
19902ac6454SAndrew Thompson
20002ac6454SAndrew Thompson static driver_t ubser_driver = {
20102ac6454SAndrew Thompson .name = "ubser",
20202ac6454SAndrew Thompson .methods = ubser_methods,
20302ac6454SAndrew Thompson .size = sizeof(struct ubser_softc),
20402ac6454SAndrew Thompson };
20502ac6454SAndrew Thompson
206bc9372d7SJohn Baldwin DRIVER_MODULE(ubser, uhub, ubser_driver, NULL, NULL);
20702ac6454SAndrew Thompson MODULE_DEPEND(ubser, ucom, 1, 1, 1);
20802ac6454SAndrew Thompson MODULE_DEPEND(ubser, usb, 1, 1, 1);
209910cb8feSAndrew Thompson MODULE_VERSION(ubser, 1);
21002ac6454SAndrew Thompson
21102ac6454SAndrew Thompson static int
ubser_probe(device_t dev)21202ac6454SAndrew Thompson ubser_probe(device_t dev)
21302ac6454SAndrew Thompson {
214760bc48eSAndrew Thompson struct usb_attach_arg *uaa = device_get_ivars(dev);
21502ac6454SAndrew Thompson
216f29a0724SAndrew Thompson if (uaa->usb_mode != USB_MODE_HOST) {
21702ac6454SAndrew Thompson return (ENXIO);
21802ac6454SAndrew Thompson }
21902ac6454SAndrew Thompson /* check if this is a BWCT vendor specific ubser interface */
220ae538d85SAndrew Thompson if ((strcmp(usb_get_manufacturer(uaa->device), "BWCT") == 0) &&
22102ac6454SAndrew Thompson (uaa->info.bInterfaceClass == 0xff) &&
22202ac6454SAndrew Thompson (uaa->info.bInterfaceSubClass == 0x00))
22302ac6454SAndrew Thompson return (0);
22402ac6454SAndrew Thompson
22502ac6454SAndrew Thompson return (ENXIO);
22602ac6454SAndrew Thompson }
22702ac6454SAndrew Thompson
22802ac6454SAndrew Thompson static int
ubser_attach(device_t dev)22902ac6454SAndrew Thompson ubser_attach(device_t dev)
23002ac6454SAndrew Thompson {
231760bc48eSAndrew Thompson struct usb_attach_arg *uaa = device_get_ivars(dev);
23202ac6454SAndrew Thompson struct ubser_softc *sc = device_get_softc(dev);
233760bc48eSAndrew Thompson struct usb_device_request req;
23402ac6454SAndrew Thompson uint8_t n;
23502ac6454SAndrew Thompson int error;
23602ac6454SAndrew Thompson
237a593f6b8SAndrew Thompson device_set_usb_desc(dev);
238deefe583SAndrew Thompson mtx_init(&sc->sc_mtx, "ubser", NULL, MTX_DEF);
2395805d178SHans Petter Selasky ucom_ref(&sc->sc_super_ucom);
24002ac6454SAndrew Thompson
24102ac6454SAndrew Thompson sc->sc_iface_no = uaa->info.bIfaceNum;
24202ac6454SAndrew Thompson sc->sc_iface_index = uaa->info.bIfaceIndex;
24302ac6454SAndrew Thompson sc->sc_udev = uaa->device;
24402ac6454SAndrew Thompson
24502ac6454SAndrew Thompson /* get number of serials */
24602ac6454SAndrew Thompson req.bmRequestType = UT_READ_VENDOR_INTERFACE;
24702ac6454SAndrew Thompson req.bRequest = VENDOR_GET_NUMSER;
24802ac6454SAndrew Thompson USETW(req.wValue, 0);
24902ac6454SAndrew Thompson req.wIndex[0] = sc->sc_iface_no;
25002ac6454SAndrew Thompson req.wIndex[1] = 0;
25102ac6454SAndrew Thompson USETW(req.wLength, 1);
252a593f6b8SAndrew Thompson error = usbd_do_request_flags(uaa->device, NULL,
253296ade60SAndrew Thompson &req, &sc->sc_numser,
25402ac6454SAndrew Thompson 0, NULL, USB_DEFAULT_TIMEOUT);
25502ac6454SAndrew Thompson
25602ac6454SAndrew Thompson if (error || (sc->sc_numser == 0)) {
25702ac6454SAndrew Thompson device_printf(dev, "failed to get number "
25802ac6454SAndrew Thompson "of serial ports: %s\n",
259a593f6b8SAndrew Thompson usbd_errstr(error));
26002ac6454SAndrew Thompson goto detach;
26102ac6454SAndrew Thompson }
26202ac6454SAndrew Thompson if (sc->sc_numser > UBSER_UNIT_MAX)
26302ac6454SAndrew Thompson sc->sc_numser = UBSER_UNIT_MAX;
26402ac6454SAndrew Thompson
26502ac6454SAndrew Thompson device_printf(dev, "found %i serials\n", sc->sc_numser);
26602ac6454SAndrew Thompson
267a593f6b8SAndrew Thompson error = usbd_transfer_setup(uaa->device, &sc->sc_iface_index,
268deefe583SAndrew Thompson sc->sc_xfer, ubser_config, UBSER_N_TRANSFER, sc, &sc->sc_mtx);
26902ac6454SAndrew Thompson if (error) {
27002ac6454SAndrew Thompson goto detach;
27102ac6454SAndrew Thompson }
272ed6d949aSAndrew Thompson sc->sc_tx_size = usbd_xfer_max_len(sc->sc_xfer[UBSER_BULK_DT_WR]);
27302ac6454SAndrew Thompson
27402ac6454SAndrew Thompson if (sc->sc_tx_size == 0) {
275767cb2e2SAndrew Thompson DPRINTFN(0, "invalid tx_size\n");
27602ac6454SAndrew Thompson goto detach;
27702ac6454SAndrew Thompson }
27802ac6454SAndrew Thompson /* initialize port numbers */
27902ac6454SAndrew Thompson
28002ac6454SAndrew Thompson for (n = 0; n < sc->sc_numser; n++) {
28102ac6454SAndrew Thompson sc->sc_ucom[n].sc_portno = n;
28202ac6454SAndrew Thompson }
28302ac6454SAndrew Thompson
284a593f6b8SAndrew Thompson error = ucom_attach(&sc->sc_super_ucom, sc->sc_ucom,
285deefe583SAndrew Thompson sc->sc_numser, sc, &ubser_callback, &sc->sc_mtx);
28602ac6454SAndrew Thompson if (error) {
28702ac6454SAndrew Thompson goto detach;
28802ac6454SAndrew Thompson }
2896416c259SNick Hibma ucom_set_pnpinfo_usb(&sc->sc_super_ucom, dev);
29002ac6454SAndrew Thompson
291deefe583SAndrew Thompson mtx_lock(&sc->sc_mtx);
292ec97e9caSHans Petter Selasky usbd_xfer_set_stall(sc->sc_xfer[UBSER_BULK_DT_WR]);
293ec97e9caSHans Petter Selasky usbd_xfer_set_stall(sc->sc_xfer[UBSER_BULK_DT_RD]);
294a593f6b8SAndrew Thompson usbd_transfer_start(sc->sc_xfer[UBSER_BULK_DT_RD]);
295deefe583SAndrew Thompson mtx_unlock(&sc->sc_mtx);
29602ac6454SAndrew Thompson
29702ac6454SAndrew Thompson return (0); /* success */
29802ac6454SAndrew Thompson
29902ac6454SAndrew Thompson detach:
30002ac6454SAndrew Thompson ubser_detach(dev);
30102ac6454SAndrew Thompson return (ENXIO); /* failure */
30202ac6454SAndrew Thompson }
30302ac6454SAndrew Thompson
30402ac6454SAndrew Thompson static int
ubser_detach(device_t dev)30502ac6454SAndrew Thompson ubser_detach(device_t dev)
30602ac6454SAndrew Thompson {
30702ac6454SAndrew Thompson struct ubser_softc *sc = device_get_softc(dev);
30802ac6454SAndrew Thompson
30902ac6454SAndrew Thompson DPRINTF("\n");
31002ac6454SAndrew Thompson
311015bb88fSNick Hibma ucom_detach(&sc->sc_super_ucom, sc->sc_ucom);
312a593f6b8SAndrew Thompson usbd_transfer_unsetup(sc->sc_xfer, UBSER_N_TRANSFER);
31302ac6454SAndrew Thompson
314c01fc06eSHans Petter Selasky device_claim_softc(dev);
315c01fc06eSHans Petter Selasky
316c01fc06eSHans Petter Selasky ubser_free_softc(sc);
317c01fc06eSHans Petter Selasky
31802ac6454SAndrew Thompson return (0);
31902ac6454SAndrew Thompson }
32002ac6454SAndrew Thompson
3215805d178SHans Petter Selasky UCOM_UNLOAD_DRAIN(ubser);
3225805d178SHans Petter Selasky
3235805d178SHans Petter Selasky static void
ubser_free_softc(struct ubser_softc * sc)324c01fc06eSHans Petter Selasky ubser_free_softc(struct ubser_softc *sc)
3255805d178SHans Petter Selasky {
3265805d178SHans Petter Selasky if (ucom_unref(&sc->sc_super_ucom)) {
3275805d178SHans Petter Selasky mtx_destroy(&sc->sc_mtx);
328c01fc06eSHans Petter Selasky device_free_softc(sc);
3295805d178SHans Petter Selasky }
3305805d178SHans Petter Selasky }
3315805d178SHans Petter Selasky
3325805d178SHans Petter Selasky static void
ubser_free(struct ucom_softc * ucom)3335805d178SHans Petter Selasky ubser_free(struct ucom_softc *ucom)
3345805d178SHans Petter Selasky {
335c01fc06eSHans Petter Selasky ubser_free_softc(ucom->sc_parent);
3365805d178SHans Petter Selasky }
3375805d178SHans Petter Selasky
33802ac6454SAndrew Thompson static int
ubser_pre_param(struct ucom_softc * ucom,struct termios * t)339760bc48eSAndrew Thompson ubser_pre_param(struct ucom_softc *ucom, struct termios *t)
34002ac6454SAndrew Thompson {
34102ac6454SAndrew Thompson DPRINTF("\n");
34202ac6454SAndrew Thompson
34302ac6454SAndrew Thompson /*
34402ac6454SAndrew Thompson * The firmware on our devices can only do 8n1@9600bps
34502ac6454SAndrew Thompson * without handshake.
34602ac6454SAndrew Thompson * We refuse to accept other configurations.
34702ac6454SAndrew Thompson */
34802ac6454SAndrew Thompson
34902ac6454SAndrew Thompson /* ensure 9600bps */
35002ac6454SAndrew Thompson switch (t->c_ospeed) {
35102ac6454SAndrew Thompson case 9600:
35202ac6454SAndrew Thompson break;
35302ac6454SAndrew Thompson default:
35402ac6454SAndrew Thompson return (EINVAL);
35502ac6454SAndrew Thompson }
35602ac6454SAndrew Thompson
35702ac6454SAndrew Thompson /* 2 stop bits not possible */
35802ac6454SAndrew Thompson if (t->c_cflag & CSTOPB)
35902ac6454SAndrew Thompson return (EINVAL);
36002ac6454SAndrew Thompson
36102ac6454SAndrew Thompson /* XXX parity handling not possible with current firmware */
36202ac6454SAndrew Thompson if (t->c_cflag & PARENB)
36302ac6454SAndrew Thompson return (EINVAL);
36402ac6454SAndrew Thompson
36502ac6454SAndrew Thompson /* we can only do 8 data bits */
36602ac6454SAndrew Thompson switch (t->c_cflag & CSIZE) {
36702ac6454SAndrew Thompson case CS8:
36802ac6454SAndrew Thompson break;
36902ac6454SAndrew Thompson default:
37002ac6454SAndrew Thompson return (EINVAL);
37102ac6454SAndrew Thompson }
37202ac6454SAndrew Thompson
37302ac6454SAndrew Thompson /* we can't do any kind of hardware handshaking */
37402ac6454SAndrew Thompson if ((t->c_cflag &
37502ac6454SAndrew Thompson (CRTS_IFLOW | CDTR_IFLOW | CDSR_OFLOW | CCAR_OFLOW)) != 0)
37602ac6454SAndrew Thompson return (EINVAL);
37702ac6454SAndrew Thompson
37802ac6454SAndrew Thompson /*
37902ac6454SAndrew Thompson * XXX xon/xoff not supported by the firmware!
38002ac6454SAndrew Thompson * This is handled within FreeBSD only and may overflow buffers
38102ac6454SAndrew Thompson * because of delayed reaction due to device buffering.
38202ac6454SAndrew Thompson */
38302ac6454SAndrew Thompson
38402ac6454SAndrew Thompson return (0);
38502ac6454SAndrew Thompson }
38602ac6454SAndrew Thompson
38702ac6454SAndrew Thompson static __inline void
ubser_inc_tx_unit(struct ubser_softc * sc)38802ac6454SAndrew Thompson ubser_inc_tx_unit(struct ubser_softc *sc)
38902ac6454SAndrew Thompson {
39002ac6454SAndrew Thompson sc->sc_curr_tx_unit++;
39102ac6454SAndrew Thompson if (sc->sc_curr_tx_unit >= sc->sc_numser) {
39202ac6454SAndrew Thompson sc->sc_curr_tx_unit = 0;
39302ac6454SAndrew Thompson }
39402ac6454SAndrew Thompson }
39502ac6454SAndrew Thompson
39602ac6454SAndrew Thompson static void
ubser_write_callback(struct usb_xfer * xfer,usb_error_t error)397ed6d949aSAndrew Thompson ubser_write_callback(struct usb_xfer *xfer, usb_error_t error)
39802ac6454SAndrew Thompson {
399ed6d949aSAndrew Thompson struct ubser_softc *sc = usbd_xfer_softc(xfer);
400ed6d949aSAndrew Thompson struct usb_page_cache *pc;
40102ac6454SAndrew Thompson uint8_t buf[1];
40202ac6454SAndrew Thompson uint8_t first_unit = sc->sc_curr_tx_unit;
40302ac6454SAndrew Thompson uint32_t actlen;
40402ac6454SAndrew Thompson
40502ac6454SAndrew Thompson switch (USB_GET_STATE(xfer)) {
40602ac6454SAndrew Thompson case USB_ST_SETUP:
40702ac6454SAndrew Thompson case USB_ST_TRANSFERRED:
40802ac6454SAndrew Thompson tr_setup:
409ed6d949aSAndrew Thompson pc = usbd_xfer_get_frame(xfer, 0);
41002ac6454SAndrew Thompson do {
411a593f6b8SAndrew Thompson if (ucom_get_data(sc->sc_ucom + sc->sc_curr_tx_unit,
412ed6d949aSAndrew Thompson pc, 1, sc->sc_tx_size - 1,
41302ac6454SAndrew Thompson &actlen)) {
41402ac6454SAndrew Thompson buf[0] = sc->sc_curr_tx_unit;
41502ac6454SAndrew Thompson
416ed6d949aSAndrew Thompson usbd_copy_in(pc, 0, buf, 1);
41702ac6454SAndrew Thompson
418ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, 0, actlen + 1);
419a593f6b8SAndrew Thompson usbd_transfer_submit(xfer);
42002ac6454SAndrew Thompson
42102ac6454SAndrew Thompson ubser_inc_tx_unit(sc); /* round robin */
42202ac6454SAndrew Thompson
42302ac6454SAndrew Thompson break;
42402ac6454SAndrew Thompson }
42502ac6454SAndrew Thompson ubser_inc_tx_unit(sc);
42602ac6454SAndrew Thompson
42702ac6454SAndrew Thompson } while (sc->sc_curr_tx_unit != first_unit);
42802ac6454SAndrew Thompson
429ec97e9caSHans Petter Selasky return;
43002ac6454SAndrew Thompson
43102ac6454SAndrew Thompson default: /* Error */
432ed6d949aSAndrew Thompson if (error != USB_ERR_CANCELLED) {
43302ac6454SAndrew Thompson /* try to clear stall first */
434ed6d949aSAndrew Thompson usbd_xfer_set_stall(xfer);
43502ac6454SAndrew Thompson goto tr_setup;
43602ac6454SAndrew Thompson }
437ec97e9caSHans Petter Selasky return;
43802ac6454SAndrew Thompson }
43902ac6454SAndrew Thompson }
44002ac6454SAndrew Thompson
44102ac6454SAndrew Thompson static void
ubser_read_callback(struct usb_xfer * xfer,usb_error_t error)442ed6d949aSAndrew Thompson ubser_read_callback(struct usb_xfer *xfer, usb_error_t error)
44302ac6454SAndrew Thompson {
444ed6d949aSAndrew Thompson struct ubser_softc *sc = usbd_xfer_softc(xfer);
445ed6d949aSAndrew Thompson struct usb_page_cache *pc;
44602ac6454SAndrew Thompson uint8_t buf[1];
447ed6d949aSAndrew Thompson int actlen;
448ed6d949aSAndrew Thompson
449ed6d949aSAndrew Thompson usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
45002ac6454SAndrew Thompson
45102ac6454SAndrew Thompson switch (USB_GET_STATE(xfer)) {
45202ac6454SAndrew Thompson case USB_ST_TRANSFERRED:
453ed6d949aSAndrew Thompson if (actlen < 1) {
45402ac6454SAndrew Thompson DPRINTF("invalid actlen=0!\n");
45502ac6454SAndrew Thompson goto tr_setup;
45602ac6454SAndrew Thompson }
457ed6d949aSAndrew Thompson pc = usbd_xfer_get_frame(xfer, 0);
458ed6d949aSAndrew Thompson usbd_copy_out(pc, 0, buf, 1);
45902ac6454SAndrew Thompson
46002ac6454SAndrew Thompson if (buf[0] >= sc->sc_numser) {
46102ac6454SAndrew Thompson DPRINTF("invalid serial number!\n");
46202ac6454SAndrew Thompson goto tr_setup;
46302ac6454SAndrew Thompson }
464ed6d949aSAndrew Thompson ucom_put_data(sc->sc_ucom + buf[0], pc, 1, actlen - 1);
46502ac6454SAndrew Thompson
46602ac6454SAndrew Thompson case USB_ST_SETUP:
46702ac6454SAndrew Thompson tr_setup:
468ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
469a593f6b8SAndrew Thompson usbd_transfer_submit(xfer);
47002ac6454SAndrew Thompson return;
47102ac6454SAndrew Thompson
47202ac6454SAndrew Thompson default: /* Error */
473ed6d949aSAndrew Thompson if (error != USB_ERR_CANCELLED) {
47402ac6454SAndrew Thompson /* try to clear stall first */
475ed6d949aSAndrew Thompson usbd_xfer_set_stall(xfer);
47602ac6454SAndrew Thompson goto tr_setup;
47702ac6454SAndrew Thompson }
47802ac6454SAndrew Thompson return;
47902ac6454SAndrew Thompson }
48002ac6454SAndrew Thompson }
48102ac6454SAndrew Thompson
48202ac6454SAndrew Thompson static void
ubser_cfg_set_break(struct ucom_softc * ucom,uint8_t onoff)483760bc48eSAndrew Thompson ubser_cfg_set_break(struct ucom_softc *ucom, uint8_t onoff)
48402ac6454SAndrew Thompson {
48502ac6454SAndrew Thompson struct ubser_softc *sc = ucom->sc_parent;
48602ac6454SAndrew Thompson uint8_t x = ucom->sc_portno;
487760bc48eSAndrew Thompson struct usb_device_request req;
488e0a69b51SAndrew Thompson usb_error_t err;
48902ac6454SAndrew Thompson
49002ac6454SAndrew Thompson if (onoff) {
49102ac6454SAndrew Thompson req.bmRequestType = UT_READ_VENDOR_INTERFACE;
49202ac6454SAndrew Thompson req.bRequest = VENDOR_SET_BREAK;
49302ac6454SAndrew Thompson req.wValue[0] = x;
49402ac6454SAndrew Thompson req.wValue[1] = 0;
49502ac6454SAndrew Thompson req.wIndex[0] = sc->sc_iface_no;
49602ac6454SAndrew Thompson req.wIndex[1] = 0;
49702ac6454SAndrew Thompson USETW(req.wLength, 0);
49802ac6454SAndrew Thompson
499a593f6b8SAndrew Thompson err = ucom_cfg_do_request(sc->sc_udev, ucom,
50002ac6454SAndrew Thompson &req, NULL, 0, 1000);
50102ac6454SAndrew Thompson if (err) {
50202ac6454SAndrew Thompson DPRINTFN(0, "send break failed, error=%s\n",
503a593f6b8SAndrew Thompson usbd_errstr(err));
50402ac6454SAndrew Thompson }
50502ac6454SAndrew Thompson }
50602ac6454SAndrew Thompson }
50702ac6454SAndrew Thompson
50802ac6454SAndrew Thompson static void
ubser_cfg_get_status(struct ucom_softc * ucom,uint8_t * lsr,uint8_t * msr)509760bc48eSAndrew Thompson ubser_cfg_get_status(struct ucom_softc *ucom, uint8_t *lsr, uint8_t *msr)
51002ac6454SAndrew Thompson {
51102ac6454SAndrew Thompson /* fake status bits */
51202ac6454SAndrew Thompson *lsr = 0;
51302ac6454SAndrew Thompson *msr = SER_DCD;
51402ac6454SAndrew Thompson }
51502ac6454SAndrew Thompson
51602ac6454SAndrew Thompson static void
ubser_start_read(struct ucom_softc * ucom)517760bc48eSAndrew Thompson ubser_start_read(struct ucom_softc *ucom)
51802ac6454SAndrew Thompson {
51902ac6454SAndrew Thompson struct ubser_softc *sc = ucom->sc_parent;
52002ac6454SAndrew Thompson
521a593f6b8SAndrew Thompson usbd_transfer_start(sc->sc_xfer[UBSER_BULK_DT_RD]);
52202ac6454SAndrew Thompson }
52302ac6454SAndrew Thompson
52402ac6454SAndrew Thompson static void
ubser_stop_read(struct ucom_softc * ucom)525760bc48eSAndrew Thompson ubser_stop_read(struct ucom_softc *ucom)
52602ac6454SAndrew Thompson {
52702ac6454SAndrew Thompson struct ubser_softc *sc = ucom->sc_parent;
52802ac6454SAndrew Thompson
529a593f6b8SAndrew Thompson usbd_transfer_stop(sc->sc_xfer[UBSER_BULK_DT_RD]);
53002ac6454SAndrew Thompson }
53102ac6454SAndrew Thompson
53202ac6454SAndrew Thompson static void
ubser_start_write(struct ucom_softc * ucom)533760bc48eSAndrew Thompson ubser_start_write(struct ucom_softc *ucom)
53402ac6454SAndrew Thompson {
53502ac6454SAndrew Thompson struct ubser_softc *sc = ucom->sc_parent;
53602ac6454SAndrew Thompson
537a593f6b8SAndrew Thompson usbd_transfer_start(sc->sc_xfer[UBSER_BULK_DT_WR]);
53802ac6454SAndrew Thompson }
53902ac6454SAndrew Thompson
54002ac6454SAndrew Thompson static void
ubser_stop_write(struct ucom_softc * ucom)541760bc48eSAndrew Thompson ubser_stop_write(struct ucom_softc *ucom)
54202ac6454SAndrew Thompson {
54302ac6454SAndrew Thompson struct ubser_softc *sc = ucom->sc_parent;
54402ac6454SAndrew Thompson
545a593f6b8SAndrew Thompson usbd_transfer_stop(sc->sc_xfer[UBSER_BULK_DT_WR]);
54602ac6454SAndrew Thompson }
547655dc9d0SAndrew Thompson
548655dc9d0SAndrew Thompson static void
ubser_poll(struct ucom_softc * ucom)549655dc9d0SAndrew Thompson ubser_poll(struct ucom_softc *ucom)
550655dc9d0SAndrew Thompson {
551655dc9d0SAndrew Thompson struct ubser_softc *sc = ucom->sc_parent;
552655dc9d0SAndrew Thompson usbd_transfer_poll(sc->sc_xfer, UBSER_N_TRANSFER);
553655dc9d0SAndrew Thompson }
554