xref: /freebsd/sys/dev/usb/serial/umodem.c (revision ca48e43ba9ee73a07cdbad8365117793b01273bb)
102ac6454SAndrew Thompson /*	$NetBSD: umodem.c,v 1.45 2002/09/23 05:51:23 simonb Exp $	*/
202ac6454SAndrew Thompson 
302ac6454SAndrew Thompson /*-
4*eebd9d53SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
5718cf2ccSPedro F. Giffuni  *
6f86e6000SWarner Losh  * Copyright (c) 2003 M. Warner Losh <imp@FreeBSD.org>
702ac6454SAndrew Thompson  *
802ac6454SAndrew Thompson  * Redistribution and use in source and binary forms, with or without
902ac6454SAndrew Thompson  * modification, are permitted provided that the following conditions
1002ac6454SAndrew Thompson  * are met:
1102ac6454SAndrew Thompson  * 1. Redistributions of source code must retain the above copyright
1202ac6454SAndrew Thompson  *    notice, this list of conditions and the following disclaimer.
1302ac6454SAndrew Thompson  * 2. Redistributions in binary form must reproduce the above copyright
1402ac6454SAndrew Thompson  *    notice, this list of conditions and the following disclaimer in the
1502ac6454SAndrew Thompson  *    documentation and/or other materials provided with the distribution.
1602ac6454SAndrew Thompson  *
1702ac6454SAndrew Thompson  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1802ac6454SAndrew Thompson  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1902ac6454SAndrew Thompson  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2002ac6454SAndrew Thompson  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2102ac6454SAndrew Thompson  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2202ac6454SAndrew Thompson  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2302ac6454SAndrew Thompson  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2402ac6454SAndrew Thompson  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2502ac6454SAndrew Thompson  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2602ac6454SAndrew Thompson  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2702ac6454SAndrew Thompson  * SUCH DAMAGE.
2802ac6454SAndrew Thompson  */
2902ac6454SAndrew Thompson 
3002ac6454SAndrew Thompson /*-
3102ac6454SAndrew Thompson  * Copyright (c) 1998 The NetBSD Foundation, Inc.
3202ac6454SAndrew Thompson  * All rights reserved.
3302ac6454SAndrew Thompson  *
3402ac6454SAndrew Thompson  * This code is derived from software contributed to The NetBSD Foundation
3502ac6454SAndrew Thompson  * by Lennart Augustsson (lennart@augustsson.net) at
3602ac6454SAndrew Thompson  * Carlstedt Research & Technology.
3702ac6454SAndrew Thompson  *
3802ac6454SAndrew Thompson  * Redistribution and use in source and binary forms, with or without
3902ac6454SAndrew Thompson  * modification, are permitted provided that the following conditions
4002ac6454SAndrew Thompson  * are met:
4102ac6454SAndrew Thompson  * 1. Redistributions of source code must retain the above copyright
4202ac6454SAndrew Thompson  *    notice, this list of conditions and the following disclaimer.
4302ac6454SAndrew Thompson  * 2. Redistributions in binary form must reproduce the above copyright
4402ac6454SAndrew Thompson  *    notice, this list of conditions and the following disclaimer in the
4502ac6454SAndrew Thompson  *    documentation and/or other materials provided with the distribution.
4602ac6454SAndrew Thompson  *
4702ac6454SAndrew Thompson  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
4802ac6454SAndrew Thompson  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
4902ac6454SAndrew Thompson  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
5002ac6454SAndrew Thompson  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
5102ac6454SAndrew Thompson  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
5202ac6454SAndrew Thompson  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
5302ac6454SAndrew Thompson  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
5402ac6454SAndrew Thompson  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
5502ac6454SAndrew Thompson  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
5602ac6454SAndrew Thompson  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
5702ac6454SAndrew Thompson  * POSSIBILITY OF SUCH DAMAGE.
5802ac6454SAndrew Thompson  */
5902ac6454SAndrew Thompson 
6002ac6454SAndrew Thompson /*
6102ac6454SAndrew Thompson  * Comm Class spec:  http://www.usb.org/developers/devclass_docs/usbccs10.pdf
6202ac6454SAndrew Thompson  *                   http://www.usb.org/developers/devclass_docs/usbcdc11.pdf
63c3beab6aSAndrew Thompson  *                   http://www.usb.org/developers/devclass_docs/cdc_wmc10.zip
6402ac6454SAndrew Thompson  */
6502ac6454SAndrew Thompson 
6602ac6454SAndrew Thompson /*
6702ac6454SAndrew Thompson  * TODO:
6802ac6454SAndrew Thompson  * - Add error recovery in various places; the big problem is what
6902ac6454SAndrew Thompson  *   to do in a callback if there is an error.
7002ac6454SAndrew Thompson  * - Implement a Call Device for modems without multiplexed commands.
7102ac6454SAndrew Thompson  *
7202ac6454SAndrew Thompson  */
7302ac6454SAndrew Thompson 
74ed6d949aSAndrew Thompson #include <sys/stdint.h>
75ed6d949aSAndrew Thompson #include <sys/stddef.h>
76ed6d949aSAndrew Thompson #include <sys/param.h>
77ed6d949aSAndrew Thompson #include <sys/queue.h>
78ed6d949aSAndrew Thompson #include <sys/types.h>
79ed6d949aSAndrew Thompson #include <sys/systm.h>
80ed6d949aSAndrew Thompson #include <sys/kernel.h>
81ed6d949aSAndrew Thompson #include <sys/bus.h>
82ed6d949aSAndrew Thompson #include <sys/module.h>
83ed6d949aSAndrew Thompson #include <sys/lock.h>
84ed6d949aSAndrew Thompson #include <sys/mutex.h>
85ed6d949aSAndrew Thompson #include <sys/condvar.h>
86ed6d949aSAndrew Thompson #include <sys/sysctl.h>
87ed6d949aSAndrew Thompson #include <sys/sx.h>
88ed6d949aSAndrew Thompson #include <sys/unistd.h>
89ed6d949aSAndrew Thompson #include <sys/callout.h>
90ed6d949aSAndrew Thompson #include <sys/malloc.h>
91ed6d949aSAndrew Thompson #include <sys/priv.h>
92ed6d949aSAndrew Thompson 
9302ac6454SAndrew Thompson #include <dev/usb/usb.h>
94ed6d949aSAndrew Thompson #include <dev/usb/usbdi.h>
95ed6d949aSAndrew Thompson #include <dev/usb/usbdi_util.h>
96ed6d949aSAndrew Thompson #include <dev/usb/usbhid.h>
9702ac6454SAndrew Thompson #include <dev/usb/usb_cdc.h>
98ed6d949aSAndrew Thompson #include "usbdevs.h"
9927aae196SHans Petter Selasky #include "usb_if.h"
100ed6d949aSAndrew Thompson 
10102ac6454SAndrew Thompson #include <dev/usb/usb_ioctl.h>
10202ac6454SAndrew Thompson 
10302ac6454SAndrew Thompson #define	USB_DEBUG_VAR umodem_debug
10402ac6454SAndrew Thompson #include <dev/usb/usb_debug.h>
10502ac6454SAndrew Thompson #include <dev/usb/usb_process.h>
1064b5437a1SAndrew Thompson #include <dev/usb/quirk/usb_quirk.h>
10702ac6454SAndrew Thompson 
10802ac6454SAndrew Thompson #include <dev/usb/serial/usb_serial.h>
10902ac6454SAndrew Thompson 
110b850ecc1SAndrew Thompson #ifdef USB_DEBUG
11102ac6454SAndrew Thompson static int umodem_debug = 0;
11202ac6454SAndrew Thompson 
113f8d2b1f3SPawel Biernacki static SYSCTL_NODE(_hw_usb, OID_AUTO, umodem, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
114f8d2b1f3SPawel Biernacki     "USB umodem");
115ece4b0bdSHans Petter Selasky SYSCTL_INT(_hw_usb_umodem, OID_AUTO, debug, CTLFLAG_RWTUN,
11602ac6454SAndrew Thompson     &umodem_debug, 0, "Debug level");
11702ac6454SAndrew Thompson #endif
11802ac6454SAndrew Thompson 
11927aae196SHans Petter Selasky static const STRUCT_USB_DUAL_ID umodem_dual_devs[] = {
12002ac6454SAndrew Thompson 	/* Generic Modem class match */
12102ac6454SAndrew Thompson 	{USB_IFACE_CLASS(UICLASS_CDC),
12202ac6454SAndrew Thompson 		USB_IFACE_SUBCLASS(UISUBCLASS_ABSTRACT_CONTROL_MODEL),
12302ac6454SAndrew Thompson 		USB_IFACE_PROTOCOL(UIPROTO_CDC_AT)},
124c48efaecSAdrian Chadd 	{USB_IFACE_CLASS(UICLASS_CDC),
125c48efaecSAdrian Chadd 		USB_IFACE_SUBCLASS(UISUBCLASS_ABSTRACT_CONTROL_MODEL),
126c48efaecSAdrian Chadd 		USB_IFACE_PROTOCOL(UIPROTO_CDC_NONE)},
12727aae196SHans Petter Selasky };
12827aae196SHans Petter Selasky 
12927aae196SHans Petter Selasky static const STRUCT_USB_HOST_ID umodem_host_devs[] = {
13013ceb654SHans Petter Selasky 	/* Huawei Modem class match */
131cdadbbb0SHans Petter Selasky 	{USB_VENDOR(USB_VENDOR_HUAWEI), USB_IFACE_CLASS(UICLASS_VENDOR),
132cdadbbb0SHans Petter Selasky 		USB_IFACE_SUBCLASS(0x02), USB_IFACE_PROTOCOL(0x01)},
133cdadbbb0SHans Petter Selasky 	{USB_VENDOR(USB_VENDOR_HUAWEI), USB_IFACE_CLASS(UICLASS_VENDOR),
134cdadbbb0SHans Petter Selasky 		USB_IFACE_SUBCLASS(0x02), USB_IFACE_PROTOCOL(0x02)},
135cdadbbb0SHans Petter Selasky 	{USB_VENDOR(USB_VENDOR_HUAWEI), USB_IFACE_CLASS(UICLASS_VENDOR),
136cdadbbb0SHans Petter Selasky 		USB_IFACE_SUBCLASS(0x02), USB_IFACE_PROTOCOL(0x10)},
137cdadbbb0SHans Petter Selasky 	{USB_VENDOR(USB_VENDOR_HUAWEI), USB_IFACE_CLASS(UICLASS_VENDOR),
138cdadbbb0SHans Petter Selasky 		USB_IFACE_SUBCLASS(0x02), USB_IFACE_PROTOCOL(0x12)},
139cdadbbb0SHans Petter Selasky 	{USB_VENDOR(USB_VENDOR_HUAWEI), USB_IFACE_CLASS(UICLASS_VENDOR),
140cdadbbb0SHans Petter Selasky 		USB_IFACE_SUBCLASS(0x02), USB_IFACE_PROTOCOL(0x61)},
141cdadbbb0SHans Petter Selasky 	{USB_VENDOR(USB_VENDOR_HUAWEI), USB_IFACE_CLASS(UICLASS_VENDOR),
142cdadbbb0SHans Petter Selasky 		USB_IFACE_SUBCLASS(0x02), USB_IFACE_PROTOCOL(0x62)},
1433d9b56b0SHans Petter Selasky 	{USB_VENDOR(USB_VENDOR_HUAWEI),USB_IFACE_CLASS(UICLASS_CDC),
14413ceb654SHans Petter Selasky 		USB_IFACE_SUBCLASS(UISUBCLASS_ABSTRACT_CONTROL_MODEL),
14513ceb654SHans Petter Selasky 		USB_IFACE_PROTOCOL(0xFF)},
14628d54982SKornel Duleba 	{USB_VENDOR(USB_VENDOR_HUAWEI), USB_IFACE_CLASS(0xFF),
14728d54982SKornel Duleba 		USB_IFACE_SUBCLASS(0xF), USB_IFACE_PROTOCOL(0xFF)},
14802ac6454SAndrew Thompson 	/* Kyocera AH-K3001V */
14902ac6454SAndrew Thompson 	{USB_VPI(USB_VENDOR_KYOCERA, USB_PRODUCT_KYOCERA_AHK3001V, 1)},
15002ac6454SAndrew Thompson 	{USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC5720, 1)},
15102ac6454SAndrew Thompson 	{USB_VPI(USB_VENDOR_CURITEL, USB_PRODUCT_CURITEL_PC5740, 1)},
1528463bd8aSHans Petter Selasky 	/* Winbond */
1538463bd8aSHans Petter Selasky 	{USB_VENDOR(USB_VENDOR_WINBOND), USB_PRODUCT(USB_PRODUCT_WINBOND_CDC)},
15402ac6454SAndrew Thompson };
15502ac6454SAndrew Thompson 
15602ac6454SAndrew Thompson /*
1574815449eSThomas Quinot  * As speeds for umodem devices increase, these numbers will need to
15802ac6454SAndrew Thompson  * be increased. They should be good for G3 speeds and below.
15902ac6454SAndrew Thompson  *
16002ac6454SAndrew Thompson  * TODO: The TTY buffers should be increased!
16102ac6454SAndrew Thompson  */
16202ac6454SAndrew Thompson #define	UMODEM_BUF_SIZE 1024
16302ac6454SAndrew Thompson 
16402ac6454SAndrew Thompson enum {
16502ac6454SAndrew Thompson 	UMODEM_BULK_WR,
16602ac6454SAndrew Thompson 	UMODEM_BULK_RD,
16727aae196SHans Petter Selasky 	UMODEM_INTR_WR,
16802ac6454SAndrew Thompson 	UMODEM_INTR_RD,
16902ac6454SAndrew Thompson 	UMODEM_N_TRANSFER,
17002ac6454SAndrew Thompson };
17102ac6454SAndrew Thompson 
17202ac6454SAndrew Thompson #define	UMODEM_MODVER			1	/* module version */
17302ac6454SAndrew Thompson 
17402ac6454SAndrew Thompson struct umodem_softc {
175760bc48eSAndrew Thompson 	struct ucom_super_softc sc_super_ucom;
176760bc48eSAndrew Thompson 	struct ucom_softc sc_ucom;
17702ac6454SAndrew Thompson 
178760bc48eSAndrew Thompson 	struct usb_xfer *sc_xfer[UMODEM_N_TRANSFER];
179760bc48eSAndrew Thompson 	struct usb_device *sc_udev;
180deefe583SAndrew Thompson 	struct mtx sc_mtx;
18102ac6454SAndrew Thompson 
18202ac6454SAndrew Thompson 	uint16_t sc_line;
18302ac6454SAndrew Thompson 
18402ac6454SAndrew Thompson 	uint8_t	sc_lsr;			/* local status register */
18502ac6454SAndrew Thompson 	uint8_t	sc_msr;			/* modem status register */
18602ac6454SAndrew Thompson 	uint8_t	sc_ctrl_iface_no;
18702ac6454SAndrew Thompson 	uint8_t	sc_data_iface_no;
18802ac6454SAndrew Thompson 	uint8_t sc_iface_index[2];
18902ac6454SAndrew Thompson 	uint8_t	sc_cm_over_data;
19002ac6454SAndrew Thompson 	uint8_t	sc_cm_cap;		/* CM capabilities */
19102ac6454SAndrew Thompson 	uint8_t	sc_acm_cap;		/* ACM capabilities */
19227aae196SHans Petter Selasky 	uint8_t	sc_line_coding[32];	/* used in USB device mode */
19327aae196SHans Petter Selasky 	uint8_t	sc_abstract_state[32];	/* used in USB device mode */
19402ac6454SAndrew Thompson };
19502ac6454SAndrew Thompson 
19602ac6454SAndrew Thompson static device_probe_t umodem_probe;
19702ac6454SAndrew Thompson static device_attach_t umodem_attach;
19802ac6454SAndrew Thompson static device_detach_t umodem_detach;
19927aae196SHans Petter Selasky static usb_handle_request_t umodem_handle_request;
20027aae196SHans Petter Selasky 
201c01fc06eSHans Petter Selasky static void umodem_free_softc(struct umodem_softc *);
20202ac6454SAndrew Thompson 
20327aae196SHans Petter Selasky static usb_callback_t umodem_intr_read_callback;
20427aae196SHans Petter Selasky static usb_callback_t umodem_intr_write_callback;
205e0a69b51SAndrew Thompson static usb_callback_t umodem_write_callback;
206e0a69b51SAndrew Thompson static usb_callback_t umodem_read_callback;
20702ac6454SAndrew Thompson 
2085805d178SHans Petter Selasky static void	umodem_free(struct ucom_softc *);
209760bc48eSAndrew Thompson static void	umodem_start_read(struct ucom_softc *);
210760bc48eSAndrew Thompson static void	umodem_stop_read(struct ucom_softc *);
211760bc48eSAndrew Thompson static void	umodem_start_write(struct ucom_softc *);
212760bc48eSAndrew Thompson static void	umodem_stop_write(struct ucom_softc *);
213760bc48eSAndrew Thompson static void	umodem_get_caps(struct usb_attach_arg *, uint8_t *, uint8_t *);
214760bc48eSAndrew Thompson static void	umodem_cfg_get_status(struct ucom_softc *, uint8_t *,
21502ac6454SAndrew Thompson 		    uint8_t *);
216760bc48eSAndrew Thompson static int	umodem_pre_param(struct ucom_softc *, struct termios *);
217760bc48eSAndrew Thompson static void	umodem_cfg_param(struct ucom_softc *, struct termios *);
21840e43b05SHans Petter Selasky static void	umodem_cfg_open(struct ucom_softc *);
219760bc48eSAndrew Thompson static int	umodem_ioctl(struct ucom_softc *, uint32_t, caddr_t, int,
22002ac6454SAndrew Thompson 		    struct thread *);
221760bc48eSAndrew Thompson static void	umodem_cfg_set_dtr(struct ucom_softc *, uint8_t);
222760bc48eSAndrew Thompson static void	umodem_cfg_set_rts(struct ucom_softc *, uint8_t);
223760bc48eSAndrew Thompson static void	umodem_cfg_set_break(struct ucom_softc *, uint8_t);
224760bc48eSAndrew Thompson static void	*umodem_get_desc(struct usb_attach_arg *, uint8_t, uint8_t);
225e0a69b51SAndrew Thompson static usb_error_t umodem_set_comm_feature(struct usb_device *, uint8_t,
22602ac6454SAndrew Thompson 		    uint16_t, uint16_t);
227655dc9d0SAndrew Thompson static void	umodem_poll(struct ucom_softc *ucom);
228a1538640SHans Petter Selasky static void	umodem_find_data_iface(struct usb_attach_arg *uaa,
229a1538640SHans Petter Selasky 		    uint8_t, uint8_t *, uint8_t *);
23002ac6454SAndrew Thompson 
231760bc48eSAndrew Thompson static const struct usb_config umodem_config[UMODEM_N_TRANSFER] = {
23202ac6454SAndrew Thompson 	[UMODEM_BULK_WR] = {
23302ac6454SAndrew Thompson 		.type = UE_BULK,
23402ac6454SAndrew Thompson 		.endpoint = UE_ADDR_ANY,
23527aae196SHans Petter Selasky 		.direction = UE_DIR_TX,
23602ac6454SAndrew Thompson 		.if_index = 0,
2374eae601eSAndrew Thompson 		.bufsize = UMODEM_BUF_SIZE,
2384eae601eSAndrew Thompson 		.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
2394eae601eSAndrew Thompson 		.callback = &umodem_write_callback,
24027aae196SHans Petter Selasky 		.usb_mode = USB_MODE_DUAL,
24102ac6454SAndrew Thompson 	},
24202ac6454SAndrew Thompson 
24302ac6454SAndrew Thompson 	[UMODEM_BULK_RD] = {
24402ac6454SAndrew Thompson 		.type = UE_BULK,
24502ac6454SAndrew Thompson 		.endpoint = UE_ADDR_ANY,
24627aae196SHans Petter Selasky 		.direction = UE_DIR_RX,
24702ac6454SAndrew Thompson 		.if_index = 0,
2484eae601eSAndrew Thompson 		.bufsize = UMODEM_BUF_SIZE,
2494eae601eSAndrew Thompson 		.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
2504eae601eSAndrew Thompson 		.callback = &umodem_read_callback,
25127aae196SHans Petter Selasky 		.usb_mode = USB_MODE_DUAL,
25227aae196SHans Petter Selasky 	},
25327aae196SHans Petter Selasky 
25427aae196SHans Petter Selasky 	[UMODEM_INTR_WR] = {
25527aae196SHans Petter Selasky 		.type = UE_INTERRUPT,
25627aae196SHans Petter Selasky 		.endpoint = UE_ADDR_ANY,
25727aae196SHans Petter Selasky 		.direction = UE_DIR_TX,
25827aae196SHans Petter Selasky 		.if_index = 1,
25927aae196SHans Petter Selasky 		.flags = {.pipe_bof = 1,.short_xfer_ok = 1,.no_pipe_ok = 1,},
26027aae196SHans Petter Selasky 		.bufsize = 0,	/* use wMaxPacketSize */
26127aae196SHans Petter Selasky 		.callback = &umodem_intr_write_callback,
26227aae196SHans Petter Selasky 		.usb_mode = USB_MODE_DEVICE,
26302ac6454SAndrew Thompson 	},
26402ac6454SAndrew Thompson 
26502ac6454SAndrew Thompson 	[UMODEM_INTR_RD] = {
26602ac6454SAndrew Thompson 		.type = UE_INTERRUPT,
26702ac6454SAndrew Thompson 		.endpoint = UE_ADDR_ANY,
26827aae196SHans Petter Selasky 		.direction = UE_DIR_RX,
26902ac6454SAndrew Thompson 		.if_index = 1,
2704eae601eSAndrew Thompson 		.flags = {.pipe_bof = 1,.short_xfer_ok = 1,.no_pipe_ok = 1,},
2714eae601eSAndrew Thompson 		.bufsize = 0,	/* use wMaxPacketSize */
27227aae196SHans Petter Selasky 		.callback = &umodem_intr_read_callback,
27327aae196SHans Petter Selasky 		.usb_mode = USB_MODE_HOST,
27402ac6454SAndrew Thompson 	},
27502ac6454SAndrew Thompson };
27602ac6454SAndrew Thompson 
277760bc48eSAndrew Thompson static const struct ucom_callback umodem_callback = {
278a593f6b8SAndrew Thompson 	.ucom_cfg_get_status = &umodem_cfg_get_status,
279a593f6b8SAndrew Thompson 	.ucom_cfg_set_dtr = &umodem_cfg_set_dtr,
280a593f6b8SAndrew Thompson 	.ucom_cfg_set_rts = &umodem_cfg_set_rts,
281a593f6b8SAndrew Thompson 	.ucom_cfg_set_break = &umodem_cfg_set_break,
282a593f6b8SAndrew Thompson 	.ucom_cfg_param = &umodem_cfg_param,
283a593f6b8SAndrew Thompson 	.ucom_pre_param = &umodem_pre_param,
28440e43b05SHans Petter Selasky 	.ucom_cfg_open = &umodem_cfg_open,
285a593f6b8SAndrew Thompson 	.ucom_ioctl = &umodem_ioctl,
286a593f6b8SAndrew Thompson 	.ucom_start_read = &umodem_start_read,
287a593f6b8SAndrew Thompson 	.ucom_stop_read = &umodem_stop_read,
288a593f6b8SAndrew Thompson 	.ucom_start_write = &umodem_start_write,
289a593f6b8SAndrew Thompson 	.ucom_stop_write = &umodem_stop_write,
290655dc9d0SAndrew Thompson 	.ucom_poll = &umodem_poll,
2915805d178SHans Petter Selasky 	.ucom_free = &umodem_free,
29202ac6454SAndrew Thompson };
29302ac6454SAndrew Thompson 
29402ac6454SAndrew Thompson static device_method_t umodem_methods[] = {
29527aae196SHans Petter Selasky 	/* USB interface */
29627aae196SHans Petter Selasky 	DEVMETHOD(usb_handle_request, umodem_handle_request),
29727aae196SHans Petter Selasky 
29827aae196SHans Petter Selasky 	/* Device interface */
29902ac6454SAndrew Thompson 	DEVMETHOD(device_probe, umodem_probe),
30002ac6454SAndrew Thompson 	DEVMETHOD(device_attach, umodem_attach),
30102ac6454SAndrew Thompson 	DEVMETHOD(device_detach, umodem_detach),
3025805d178SHans Petter Selasky 	DEVMETHOD_END
30302ac6454SAndrew Thompson };
30402ac6454SAndrew Thompson 
30502ac6454SAndrew Thompson static driver_t umodem_driver = {
30602ac6454SAndrew Thompson 	.name = "umodem",
30702ac6454SAndrew Thompson 	.methods = umodem_methods,
30802ac6454SAndrew Thompson 	.size = sizeof(struct umodem_softc),
30902ac6454SAndrew Thompson };
31002ac6454SAndrew Thompson 
311bc9372d7SJohn Baldwin DRIVER_MODULE(umodem, uhub, umodem_driver, NULL, NULL);
31202ac6454SAndrew Thompson MODULE_DEPEND(umodem, ucom, 1, 1, 1);
31302ac6454SAndrew Thompson MODULE_DEPEND(umodem, usb, 1, 1, 1);
31402ac6454SAndrew Thompson MODULE_VERSION(umodem, UMODEM_MODVER);
315f809f280SWarner Losh USB_PNP_DUAL_INFO(umodem_dual_devs);
316f809f280SWarner Losh USB_PNP_HOST_INFO(umodem_host_devs);
31702ac6454SAndrew Thompson 
31802ac6454SAndrew Thompson static int
umodem_probe(device_t dev)31902ac6454SAndrew Thompson umodem_probe(device_t dev)
32002ac6454SAndrew Thompson {
321760bc48eSAndrew Thompson 	struct usb_attach_arg *uaa = device_get_ivars(dev);
32202ac6454SAndrew Thompson 	int error;
32302ac6454SAndrew Thompson 
32402ac6454SAndrew Thompson 	DPRINTFN(11, "\n");
32502ac6454SAndrew Thompson 
32627aae196SHans Petter Selasky 	error = usbd_lookup_id_by_uaa(umodem_host_devs,
32727aae196SHans Petter Selasky 	    sizeof(umodem_host_devs), uaa);
32827aae196SHans Petter Selasky 	if (error) {
32927aae196SHans Petter Selasky 		error = usbd_lookup_id_by_uaa(umodem_dual_devs,
33027aae196SHans Petter Selasky 		    sizeof(umodem_dual_devs), uaa);
3319cfbe3e7SHans Petter Selasky 		if (error)
33202ac6454SAndrew Thompson 			return (error);
33327aae196SHans Petter Selasky 	}
3349cfbe3e7SHans Petter Selasky 	return (BUS_PROBE_GENERIC);
33502ac6454SAndrew Thompson }
33602ac6454SAndrew Thompson 
33702ac6454SAndrew Thompson static int
umodem_attach(device_t dev)33802ac6454SAndrew Thompson umodem_attach(device_t dev)
33902ac6454SAndrew Thompson {
340760bc48eSAndrew Thompson 	struct usb_attach_arg *uaa = device_get_ivars(dev);
34102ac6454SAndrew Thompson 	struct umodem_softc *sc = device_get_softc(dev);
342760bc48eSAndrew Thompson 	struct usb_cdc_cm_descriptor *cmd;
343760bc48eSAndrew Thompson 	struct usb_cdc_union_descriptor *cud;
34402ac6454SAndrew Thompson 	uint8_t i;
34502ac6454SAndrew Thompson 	int error;
34602ac6454SAndrew Thompson 
347a593f6b8SAndrew Thompson 	device_set_usb_desc(dev);
348deefe583SAndrew Thompson 	mtx_init(&sc->sc_mtx, "umodem", NULL, MTX_DEF);
3495805d178SHans Petter Selasky 	ucom_ref(&sc->sc_super_ucom);
35002ac6454SAndrew Thompson 
35102ac6454SAndrew Thompson 	sc->sc_ctrl_iface_no = uaa->info.bIfaceNum;
35202ac6454SAndrew Thompson 	sc->sc_iface_index[1] = uaa->info.bIfaceIndex;
35302ac6454SAndrew Thompson 	sc->sc_udev = uaa->device;
35402ac6454SAndrew Thompson 
35502ac6454SAndrew Thompson 	umodem_get_caps(uaa, &sc->sc_cm_cap, &sc->sc_acm_cap);
35602ac6454SAndrew Thompson 
35702ac6454SAndrew Thompson 	/* get the data interface number */
35802ac6454SAndrew Thompson 
35973c3e8b1SSteffen Dirkwinkel 	cmd = NULL;
36073c3e8b1SSteffen Dirkwinkel 	if (!usb_test_quirk(uaa, UQ_IGNORE_CDC_CM))
36102ac6454SAndrew Thompson 		cmd = umodem_get_desc(uaa, UDESC_CS_INTERFACE, UDESCSUB_CDC_CM);
36202ac6454SAndrew Thompson 
36302ac6454SAndrew Thompson 	if ((cmd == NULL) || (cmd->bLength < sizeof(*cmd))) {
3643c8d24f4SAndrew Thompson 		cud = usbd_find_descriptor(uaa->device, NULL,
365c3beab6aSAndrew Thompson 		    uaa->info.bIfaceIndex, UDESC_CS_INTERFACE,
3666d917491SHans Petter Selasky 		    0xFF, UDESCSUB_CDC_UNION, 0xFF);
367c3beab6aSAndrew Thompson 
368c3beab6aSAndrew Thompson 		if ((cud == NULL) || (cud->bLength < sizeof(*cud))) {
369a1538640SHans Petter Selasky 			DPRINTF("Missing descriptor. "
3703e889e24SAndrew Thompson 			    "Assuming data interface is next.\n");
371a1538640SHans Petter Selasky 			if (sc->sc_ctrl_iface_no == 0xFF) {
37202ac6454SAndrew Thompson 				goto detach;
373a1538640SHans Petter Selasky 			} else {
374a1538640SHans Petter Selasky 				uint8_t class_match = 0;
375a1538640SHans Petter Selasky 
376a1538640SHans Petter Selasky 				/* set default interface number */
377a1538640SHans Petter Selasky 				sc->sc_data_iface_no = 0xFF;
378a1538640SHans Petter Selasky 
379a1538640SHans Petter Selasky 				/* try to find the data interface backwards */
380a1538640SHans Petter Selasky 				umodem_find_data_iface(uaa,
381a1538640SHans Petter Selasky 				    uaa->info.bIfaceIndex - 1,
382a1538640SHans Petter Selasky 				    &sc->sc_data_iface_no, &class_match);
383a1538640SHans Petter Selasky 
384a1538640SHans Petter Selasky 				/* try to find the data interface forwards */
385a1538640SHans Petter Selasky 				umodem_find_data_iface(uaa,
386a1538640SHans Petter Selasky 				    uaa->info.bIfaceIndex + 1,
387a1538640SHans Petter Selasky 				    &sc->sc_data_iface_no, &class_match);
388a1538640SHans Petter Selasky 
389a1538640SHans Petter Selasky 				/* check if nothing was found */
390a1538640SHans Petter Selasky 				if (sc->sc_data_iface_no == 0xFF)
391a1538640SHans Petter Selasky 					goto detach;
392a1538640SHans Petter Selasky 			}
3933e889e24SAndrew Thompson 		} else {
394c3beab6aSAndrew Thompson 			sc->sc_data_iface_no = cud->bSlaveInterface[0];
3953e889e24SAndrew Thompson 		}
396c3beab6aSAndrew Thompson 	} else {
39702ac6454SAndrew Thompson 		sc->sc_data_iface_no = cmd->bDataInterface;
398c3beab6aSAndrew Thompson 	}
39902ac6454SAndrew Thompson 
40002ac6454SAndrew Thompson 	device_printf(dev, "data interface %d, has %sCM over "
40102ac6454SAndrew Thompson 	    "data, has %sbreak\n",
40202ac6454SAndrew Thompson 	    sc->sc_data_iface_no,
40302ac6454SAndrew Thompson 	    sc->sc_cm_cap & USB_CDC_CM_OVER_DATA ? "" : "no ",
40402ac6454SAndrew Thompson 	    sc->sc_acm_cap & USB_CDC_ACM_HAS_BREAK ? "" : "no ");
40502ac6454SAndrew Thompson 
40602ac6454SAndrew Thompson 	/* get the data interface too */
40702ac6454SAndrew Thompson 
40802ac6454SAndrew Thompson 	for (i = 0;; i++) {
409760bc48eSAndrew Thompson 		struct usb_interface *iface;
410760bc48eSAndrew Thompson 		struct usb_interface_descriptor *id;
41102ac6454SAndrew Thompson 
412a593f6b8SAndrew Thompson 		iface = usbd_get_iface(uaa->device, i);
41302ac6454SAndrew Thompson 
41402ac6454SAndrew Thompson 		if (iface) {
415a593f6b8SAndrew Thompson 			id = usbd_get_interface_descriptor(iface);
41602ac6454SAndrew Thompson 
41702ac6454SAndrew Thompson 			if (id && (id->bInterfaceNumber == sc->sc_data_iface_no)) {
41802ac6454SAndrew Thompson 				sc->sc_iface_index[0] = i;
419a593f6b8SAndrew Thompson 				usbd_set_parent_iface(uaa->device, i, uaa->info.bIfaceIndex);
42002ac6454SAndrew Thompson 				break;
42102ac6454SAndrew Thompson 			}
42202ac6454SAndrew Thompson 		} else {
423767cb2e2SAndrew Thompson 			device_printf(dev, "no data interface\n");
42402ac6454SAndrew Thompson 			goto detach;
42502ac6454SAndrew Thompson 		}
42602ac6454SAndrew Thompson 	}
42702ac6454SAndrew Thompson 
4284b5437a1SAndrew Thompson 	if (usb_test_quirk(uaa, UQ_ASSUME_CM_OVER_DATA)) {
4294b5437a1SAndrew Thompson 		sc->sc_cm_over_data = 1;
4304b5437a1SAndrew Thompson 	} else {
43102ac6454SAndrew Thompson 		if (sc->sc_cm_cap & USB_CDC_CM_OVER_DATA) {
43202ac6454SAndrew Thompson 			if (sc->sc_acm_cap & USB_CDC_ACM_HAS_FEATURE) {
43302ac6454SAndrew Thompson 				error = umodem_set_comm_feature
43402ac6454SAndrew Thompson 				(uaa->device, sc->sc_ctrl_iface_no,
43502ac6454SAndrew Thompson 				 UCDC_ABSTRACT_STATE, UCDC_DATA_MULTIPLEXED);
43602ac6454SAndrew Thompson 
43702ac6454SAndrew Thompson 				/* ignore any errors */
43802ac6454SAndrew Thompson 			}
43902ac6454SAndrew Thompson 			sc->sc_cm_over_data = 1;
44002ac6454SAndrew Thompson 		}
4414b5437a1SAndrew Thompson 	}
442a593f6b8SAndrew Thompson 	error = usbd_transfer_setup(uaa->device,
44302ac6454SAndrew Thompson 	    sc->sc_iface_index, sc->sc_xfer,
44402ac6454SAndrew Thompson 	    umodem_config, UMODEM_N_TRANSFER,
445deefe583SAndrew Thompson 	    sc, &sc->sc_mtx);
44602ac6454SAndrew Thompson 	if (error) {
44727aae196SHans Petter Selasky 		device_printf(dev, "Can't setup transfer\n");
44802ac6454SAndrew Thompson 		goto detach;
44902ac6454SAndrew Thompson 	}
45002ac6454SAndrew Thompson 
4517762e8a1SEdward Tomasz Napierala 	ucom_set_usb_mode(&sc->sc_super_ucom, uaa->usb_mode);
4527762e8a1SEdward Tomasz Napierala 
453a593f6b8SAndrew Thompson 	error = ucom_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
454deefe583SAndrew Thompson 	    &umodem_callback, &sc->sc_mtx);
45502ac6454SAndrew Thompson 	if (error) {
45627aae196SHans Petter Selasky 		device_printf(dev, "Can't attach com\n");
45702ac6454SAndrew Thompson 		goto detach;
45802ac6454SAndrew Thompson 	}
4596416c259SNick Hibma 	ucom_set_pnpinfo_usb(&sc->sc_super_ucom, dev);
4606416c259SNick Hibma 
46102ac6454SAndrew Thompson 	return (0);
46202ac6454SAndrew Thompson 
46302ac6454SAndrew Thompson detach:
46402ac6454SAndrew Thompson 	umodem_detach(dev);
46502ac6454SAndrew Thompson 	return (ENXIO);
46602ac6454SAndrew Thompson }
46702ac6454SAndrew Thompson 
46802ac6454SAndrew Thompson static void
umodem_find_data_iface(struct usb_attach_arg * uaa,uint8_t iface_index,uint8_t * p_data_no,uint8_t * p_match_class)469a1538640SHans Petter Selasky umodem_find_data_iface(struct usb_attach_arg *uaa,
470a1538640SHans Petter Selasky     uint8_t iface_index, uint8_t *p_data_no, uint8_t *p_match_class)
471a1538640SHans Petter Selasky {
472a1538640SHans Petter Selasky 	struct usb_interface_descriptor *id;
473a1538640SHans Petter Selasky 	struct usb_interface *iface;
474a1538640SHans Petter Selasky 
475a1538640SHans Petter Selasky 	iface = usbd_get_iface(uaa->device, iface_index);
476a1538640SHans Petter Selasky 
477a1538640SHans Petter Selasky 	/* check for end of interfaces */
478a1538640SHans Petter Selasky 	if (iface == NULL)
479a1538640SHans Petter Selasky 		return;
480a1538640SHans Petter Selasky 
481a1538640SHans Petter Selasky 	id = usbd_get_interface_descriptor(iface);
482a1538640SHans Petter Selasky 
483a1538640SHans Petter Selasky 	/* check for non-matching interface class */
484a1538640SHans Petter Selasky 	if (id->bInterfaceClass != UICLASS_CDC_DATA ||
485a1538640SHans Petter Selasky 	    id->bInterfaceSubClass != UISUBCLASS_DATA) {
486a1538640SHans Petter Selasky 		/* if we got a class match then return */
487a1538640SHans Petter Selasky 		if (*p_match_class)
488a1538640SHans Petter Selasky 			return;
489a1538640SHans Petter Selasky 	} else {
490a1538640SHans Petter Selasky 		*p_match_class = 1;
491a1538640SHans Petter Selasky 	}
492a1538640SHans Petter Selasky 
493a1538640SHans Petter Selasky 	DPRINTFN(11, "Match at index %u\n", iface_index);
494a1538640SHans Petter Selasky 
495a1538640SHans Petter Selasky 	*p_data_no = id->bInterfaceNumber;
496a1538640SHans Petter Selasky }
497a1538640SHans Petter Selasky 
498a1538640SHans Petter Selasky static void
umodem_start_read(struct ucom_softc * ucom)499760bc48eSAndrew Thompson umodem_start_read(struct ucom_softc *ucom)
50002ac6454SAndrew Thompson {
50102ac6454SAndrew Thompson 	struct umodem_softc *sc = ucom->sc_parent;
50202ac6454SAndrew Thompson 
50302ac6454SAndrew Thompson 	/* start interrupt endpoint, if any */
504a593f6b8SAndrew Thompson 	usbd_transfer_start(sc->sc_xfer[UMODEM_INTR_RD]);
50502ac6454SAndrew Thompson 
50602ac6454SAndrew Thompson 	/* start read endpoint */
507a593f6b8SAndrew Thompson 	usbd_transfer_start(sc->sc_xfer[UMODEM_BULK_RD]);
50802ac6454SAndrew Thompson }
50902ac6454SAndrew Thompson 
51002ac6454SAndrew Thompson static void
umodem_stop_read(struct ucom_softc * ucom)511760bc48eSAndrew Thompson umodem_stop_read(struct ucom_softc *ucom)
51202ac6454SAndrew Thompson {
51302ac6454SAndrew Thompson 	struct umodem_softc *sc = ucom->sc_parent;
51402ac6454SAndrew Thompson 
51502ac6454SAndrew Thompson 	/* stop interrupt endpoint, if any */
516a593f6b8SAndrew Thompson 	usbd_transfer_stop(sc->sc_xfer[UMODEM_INTR_RD]);
51702ac6454SAndrew Thompson 
51802ac6454SAndrew Thompson 	/* stop read endpoint */
519a593f6b8SAndrew Thompson 	usbd_transfer_stop(sc->sc_xfer[UMODEM_BULK_RD]);
52002ac6454SAndrew Thompson }
52102ac6454SAndrew Thompson 
52202ac6454SAndrew Thompson static void
umodem_start_write(struct ucom_softc * ucom)523760bc48eSAndrew Thompson umodem_start_write(struct ucom_softc *ucom)
52402ac6454SAndrew Thompson {
52502ac6454SAndrew Thompson 	struct umodem_softc *sc = ucom->sc_parent;
52602ac6454SAndrew Thompson 
52727aae196SHans Petter Selasky 	usbd_transfer_start(sc->sc_xfer[UMODEM_INTR_WR]);
528a593f6b8SAndrew Thompson 	usbd_transfer_start(sc->sc_xfer[UMODEM_BULK_WR]);
52902ac6454SAndrew Thompson }
53002ac6454SAndrew Thompson 
53102ac6454SAndrew Thompson static void
umodem_stop_write(struct ucom_softc * ucom)532760bc48eSAndrew Thompson umodem_stop_write(struct ucom_softc *ucom)
53302ac6454SAndrew Thompson {
53402ac6454SAndrew Thompson 	struct umodem_softc *sc = ucom->sc_parent;
53502ac6454SAndrew Thompson 
53627aae196SHans Petter Selasky 	usbd_transfer_stop(sc->sc_xfer[UMODEM_INTR_WR]);
537a593f6b8SAndrew Thompson 	usbd_transfer_stop(sc->sc_xfer[UMODEM_BULK_WR]);
53802ac6454SAndrew Thompson }
53902ac6454SAndrew Thompson 
54002ac6454SAndrew Thompson static void
umodem_get_caps(struct usb_attach_arg * uaa,uint8_t * cm,uint8_t * acm)541760bc48eSAndrew Thompson umodem_get_caps(struct usb_attach_arg *uaa, uint8_t *cm, uint8_t *acm)
54202ac6454SAndrew Thompson {
543760bc48eSAndrew Thompson 	struct usb_cdc_cm_descriptor *cmd;
544760bc48eSAndrew Thompson 	struct usb_cdc_acm_descriptor *cad;
54502ac6454SAndrew Thompson 
54602ac6454SAndrew Thompson 	cmd = umodem_get_desc(uaa, UDESC_CS_INTERFACE, UDESCSUB_CDC_CM);
54702ac6454SAndrew Thompson 	if ((cmd == NULL) || (cmd->bLength < sizeof(*cmd))) {
548c3beab6aSAndrew Thompson 		DPRINTF("no CM desc (faking one)\n");
549c3beab6aSAndrew Thompson 		*cm = USB_CDC_CM_DOES_CM | USB_CDC_CM_OVER_DATA;
550c3beab6aSAndrew Thompson 	} else
55102ac6454SAndrew Thompson 		*cm = cmd->bmCapabilities;
55202ac6454SAndrew Thompson 
55302ac6454SAndrew Thompson 	cad = umodem_get_desc(uaa, UDESC_CS_INTERFACE, UDESCSUB_CDC_ACM);
55402ac6454SAndrew Thompson 	if ((cad == NULL) || (cad->bLength < sizeof(*cad))) {
55502ac6454SAndrew Thompson 		DPRINTF("no ACM desc\n");
556c3beab6aSAndrew Thompson 		*acm = 0;
557c3beab6aSAndrew Thompson 	} else
55802ac6454SAndrew Thompson 		*acm = cad->bmCapabilities;
55902ac6454SAndrew Thompson }
56002ac6454SAndrew Thompson 
56102ac6454SAndrew Thompson static void
umodem_cfg_get_status(struct ucom_softc * ucom,uint8_t * lsr,uint8_t * msr)562760bc48eSAndrew Thompson umodem_cfg_get_status(struct ucom_softc *ucom, uint8_t *lsr, uint8_t *msr)
56302ac6454SAndrew Thompson {
56402ac6454SAndrew Thompson 	struct umodem_softc *sc = ucom->sc_parent;
56502ac6454SAndrew Thompson 
56602ac6454SAndrew Thompson 	DPRINTF("\n");
56702ac6454SAndrew Thompson 
568fe67c846SIan Lepore 	/* XXX Note: sc_lsr is always zero */
56902ac6454SAndrew Thompson 	*lsr = sc->sc_lsr;
57002ac6454SAndrew Thompson 	*msr = sc->sc_msr;
57102ac6454SAndrew Thompson }
57202ac6454SAndrew Thompson 
57302ac6454SAndrew Thompson static int
umodem_pre_param(struct ucom_softc * ucom,struct termios * t)574760bc48eSAndrew Thompson umodem_pre_param(struct ucom_softc *ucom, struct termios *t)
57502ac6454SAndrew Thompson {
57602ac6454SAndrew Thompson 	return (0);			/* we accept anything */
57702ac6454SAndrew Thompson }
57802ac6454SAndrew Thompson 
57902ac6454SAndrew Thompson static void
umodem_cfg_param(struct ucom_softc * ucom,struct termios * t)580760bc48eSAndrew Thompson umodem_cfg_param(struct ucom_softc *ucom, struct termios *t)
58102ac6454SAndrew Thompson {
58202ac6454SAndrew Thompson 	struct umodem_softc *sc = ucom->sc_parent;
583760bc48eSAndrew Thompson 	struct usb_cdc_line_state ls;
584760bc48eSAndrew Thompson 	struct usb_device_request req;
58502ac6454SAndrew Thompson 
58602ac6454SAndrew Thompson 	DPRINTF("sc=%p\n", sc);
58702ac6454SAndrew Thompson 
588271ae033SHans Petter Selasky 	memset(&ls, 0, sizeof(ls));
58902ac6454SAndrew Thompson 
59002ac6454SAndrew Thompson 	USETDW(ls.dwDTERate, t->c_ospeed);
59102ac6454SAndrew Thompson 
59202ac6454SAndrew Thompson 	ls.bCharFormat = (t->c_cflag & CSTOPB) ?
59302ac6454SAndrew Thompson 	    UCDC_STOP_BIT_2 : UCDC_STOP_BIT_1;
59402ac6454SAndrew Thompson 
59502ac6454SAndrew Thompson 	ls.bParityType = (t->c_cflag & PARENB) ?
59602ac6454SAndrew Thompson 	    ((t->c_cflag & PARODD) ?
59702ac6454SAndrew Thompson 	    UCDC_PARITY_ODD : UCDC_PARITY_EVEN) : UCDC_PARITY_NONE;
59802ac6454SAndrew Thompson 
59902ac6454SAndrew Thompson 	switch (t->c_cflag & CSIZE) {
60002ac6454SAndrew Thompson 	case CS5:
60102ac6454SAndrew Thompson 		ls.bDataBits = 5;
60202ac6454SAndrew Thompson 		break;
60302ac6454SAndrew Thompson 	case CS6:
60402ac6454SAndrew Thompson 		ls.bDataBits = 6;
60502ac6454SAndrew Thompson 		break;
60602ac6454SAndrew Thompson 	case CS7:
60702ac6454SAndrew Thompson 		ls.bDataBits = 7;
60802ac6454SAndrew Thompson 		break;
60902ac6454SAndrew Thompson 	case CS8:
61002ac6454SAndrew Thompson 		ls.bDataBits = 8;
61102ac6454SAndrew Thompson 		break;
61202ac6454SAndrew Thompson 	}
61302ac6454SAndrew Thompson 
61402ac6454SAndrew Thompson 	DPRINTF("rate=%d fmt=%d parity=%d bits=%d\n",
61502ac6454SAndrew Thompson 	    UGETDW(ls.dwDTERate), ls.bCharFormat,
61602ac6454SAndrew Thompson 	    ls.bParityType, ls.bDataBits);
61702ac6454SAndrew Thompson 
61802ac6454SAndrew Thompson 	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
61902ac6454SAndrew Thompson 	req.bRequest = UCDC_SET_LINE_CODING;
62002ac6454SAndrew Thompson 	USETW(req.wValue, 0);
62102ac6454SAndrew Thompson 	req.wIndex[0] = sc->sc_ctrl_iface_no;
62202ac6454SAndrew Thompson 	req.wIndex[1] = 0;
62302ac6454SAndrew Thompson 	USETW(req.wLength, sizeof(ls));
62402ac6454SAndrew Thompson 
625a593f6b8SAndrew Thompson 	ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
62602ac6454SAndrew Thompson 	    &req, &ls, 0, 1000);
62702ac6454SAndrew Thompson }
62802ac6454SAndrew Thompson 
62940e43b05SHans Petter Selasky static void
umodem_cfg_open(struct ucom_softc * ucom)63040e43b05SHans Petter Selasky umodem_cfg_open(struct ucom_softc *ucom)
63140e43b05SHans Petter Selasky {
63240e43b05SHans Petter Selasky 	struct umodem_softc *sc = ucom->sc_parent;
63340e43b05SHans Petter Selasky 
63440e43b05SHans Petter Selasky 	/* clear stall, if in USB host mode */
63540e43b05SHans Petter Selasky 	if ((sc->sc_super_ucom.sc_flag & UCOM_FLAG_DEVICE_MODE) == 0) {
63640e43b05SHans Petter Selasky 		usbd_xfer_set_stall(sc->sc_xfer[UMODEM_BULK_WR]);
63740e43b05SHans Petter Selasky 		usbd_xfer_set_stall(sc->sc_xfer[UMODEM_BULK_RD]);
63840e43b05SHans Petter Selasky 	}
63940e43b05SHans Petter Selasky }
64040e43b05SHans Petter Selasky 
64102ac6454SAndrew Thompson static int
umodem_ioctl(struct ucom_softc * ucom,uint32_t cmd,caddr_t data,int flag,struct thread * td)642760bc48eSAndrew Thompson umodem_ioctl(struct ucom_softc *ucom, uint32_t cmd, caddr_t data,
64302ac6454SAndrew Thompson     int flag, struct thread *td)
64402ac6454SAndrew Thompson {
64502ac6454SAndrew Thompson 	struct umodem_softc *sc = ucom->sc_parent;
64602ac6454SAndrew Thompson 	int error = 0;
64702ac6454SAndrew Thompson 
64802ac6454SAndrew Thompson 	DPRINTF("cmd=0x%08x\n", cmd);
64902ac6454SAndrew Thompson 
65002ac6454SAndrew Thompson 	switch (cmd) {
65102ac6454SAndrew Thompson 	case USB_GET_CM_OVER_DATA:
65202ac6454SAndrew Thompson 		*(int *)data = sc->sc_cm_over_data;
65302ac6454SAndrew Thompson 		break;
65402ac6454SAndrew Thompson 
65502ac6454SAndrew Thompson 	case USB_SET_CM_OVER_DATA:
65602ac6454SAndrew Thompson 		if (*(int *)data != sc->sc_cm_over_data) {
65702ac6454SAndrew Thompson 			/* XXX change it */
65802ac6454SAndrew Thompson 		}
65902ac6454SAndrew Thompson 		break;
66002ac6454SAndrew Thompson 
66102ac6454SAndrew Thompson 	default:
66202ac6454SAndrew Thompson 		DPRINTF("unknown\n");
66302ac6454SAndrew Thompson 		error = ENOIOCTL;
66402ac6454SAndrew Thompson 		break;
66502ac6454SAndrew Thompson 	}
66602ac6454SAndrew Thompson 
66702ac6454SAndrew Thompson 	return (error);
66802ac6454SAndrew Thompson }
66902ac6454SAndrew Thompson 
67002ac6454SAndrew Thompson static void
umodem_cfg_set_dtr(struct ucom_softc * ucom,uint8_t onoff)671760bc48eSAndrew Thompson umodem_cfg_set_dtr(struct ucom_softc *ucom, uint8_t onoff)
67202ac6454SAndrew Thompson {
67302ac6454SAndrew Thompson 	struct umodem_softc *sc = ucom->sc_parent;
674760bc48eSAndrew Thompson 	struct usb_device_request req;
67502ac6454SAndrew Thompson 
67602ac6454SAndrew Thompson 	DPRINTF("onoff=%d\n", onoff);
67702ac6454SAndrew Thompson 
67802ac6454SAndrew Thompson 	if (onoff)
67902ac6454SAndrew Thompson 		sc->sc_line |= UCDC_LINE_DTR;
68002ac6454SAndrew Thompson 	else
68102ac6454SAndrew Thompson 		sc->sc_line &= ~UCDC_LINE_DTR;
68202ac6454SAndrew Thompson 
68302ac6454SAndrew Thompson 	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
68402ac6454SAndrew Thompson 	req.bRequest = UCDC_SET_CONTROL_LINE_STATE;
68502ac6454SAndrew Thompson 	USETW(req.wValue, sc->sc_line);
68602ac6454SAndrew Thompson 	req.wIndex[0] = sc->sc_ctrl_iface_no;
68702ac6454SAndrew Thompson 	req.wIndex[1] = 0;
68802ac6454SAndrew Thompson 	USETW(req.wLength, 0);
68902ac6454SAndrew Thompson 
690a593f6b8SAndrew Thompson 	ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
69102ac6454SAndrew Thompson 	    &req, NULL, 0, 1000);
69202ac6454SAndrew Thompson }
69302ac6454SAndrew Thompson 
69402ac6454SAndrew Thompson static void
umodem_cfg_set_rts(struct ucom_softc * ucom,uint8_t onoff)695760bc48eSAndrew Thompson umodem_cfg_set_rts(struct ucom_softc *ucom, uint8_t onoff)
69602ac6454SAndrew Thompson {
69702ac6454SAndrew Thompson 	struct umodem_softc *sc = ucom->sc_parent;
698760bc48eSAndrew Thompson 	struct usb_device_request req;
69902ac6454SAndrew Thompson 
70002ac6454SAndrew Thompson 	DPRINTF("onoff=%d\n", onoff);
70102ac6454SAndrew Thompson 
70202ac6454SAndrew Thompson 	if (onoff)
70302ac6454SAndrew Thompson 		sc->sc_line |= UCDC_LINE_RTS;
70402ac6454SAndrew Thompson 	else
70502ac6454SAndrew Thompson 		sc->sc_line &= ~UCDC_LINE_RTS;
70602ac6454SAndrew Thompson 
70702ac6454SAndrew Thompson 	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
70802ac6454SAndrew Thompson 	req.bRequest = UCDC_SET_CONTROL_LINE_STATE;
70902ac6454SAndrew Thompson 	USETW(req.wValue, sc->sc_line);
71002ac6454SAndrew Thompson 	req.wIndex[0] = sc->sc_ctrl_iface_no;
71102ac6454SAndrew Thompson 	req.wIndex[1] = 0;
71202ac6454SAndrew Thompson 	USETW(req.wLength, 0);
71302ac6454SAndrew Thompson 
714a593f6b8SAndrew Thompson 	ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
71502ac6454SAndrew Thompson 	    &req, NULL, 0, 1000);
71602ac6454SAndrew Thompson }
71702ac6454SAndrew Thompson 
71802ac6454SAndrew Thompson static void
umodem_cfg_set_break(struct ucom_softc * ucom,uint8_t onoff)719760bc48eSAndrew Thompson umodem_cfg_set_break(struct ucom_softc *ucom, uint8_t onoff)
72002ac6454SAndrew Thompson {
72102ac6454SAndrew Thompson 	struct umodem_softc *sc = ucom->sc_parent;
722760bc48eSAndrew Thompson 	struct usb_device_request req;
72302ac6454SAndrew Thompson 	uint16_t temp;
72402ac6454SAndrew Thompson 
72502ac6454SAndrew Thompson 	DPRINTF("onoff=%d\n", onoff);
72602ac6454SAndrew Thompson 
72702ac6454SAndrew Thompson 	if (sc->sc_acm_cap & USB_CDC_ACM_HAS_BREAK) {
72802ac6454SAndrew Thompson 		temp = onoff ? UCDC_BREAK_ON : UCDC_BREAK_OFF;
72902ac6454SAndrew Thompson 
73002ac6454SAndrew Thompson 		req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
73102ac6454SAndrew Thompson 		req.bRequest = UCDC_SEND_BREAK;
73202ac6454SAndrew Thompson 		USETW(req.wValue, temp);
73302ac6454SAndrew Thompson 		req.wIndex[0] = sc->sc_ctrl_iface_no;
73402ac6454SAndrew Thompson 		req.wIndex[1] = 0;
73502ac6454SAndrew Thompson 		USETW(req.wLength, 0);
73602ac6454SAndrew Thompson 
737a593f6b8SAndrew Thompson 		ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
73802ac6454SAndrew Thompson 		    &req, NULL, 0, 1000);
73902ac6454SAndrew Thompson 	}
74002ac6454SAndrew Thompson }
74102ac6454SAndrew Thompson 
74202ac6454SAndrew Thompson static void
umodem_intr_write_callback(struct usb_xfer * xfer,usb_error_t error)74327aae196SHans Petter Selasky umodem_intr_write_callback(struct usb_xfer *xfer, usb_error_t error)
74427aae196SHans Petter Selasky {
74527aae196SHans Petter Selasky 	int actlen;
74627aae196SHans Petter Selasky 
74727aae196SHans Petter Selasky 	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
74827aae196SHans Petter Selasky 
74927aae196SHans Petter Selasky 	switch (USB_GET_STATE(xfer)) {
75027aae196SHans Petter Selasky 	case USB_ST_TRANSFERRED:
75127aae196SHans Petter Selasky 
75227aae196SHans Petter Selasky 		DPRINTF("Transferred %d bytes\n", actlen);
75327aae196SHans Petter Selasky 
75427aae196SHans Petter Selasky 		/* FALLTHROUGH */
75527aae196SHans Petter Selasky 	case USB_ST_SETUP:
75627aae196SHans Petter Selasky tr_setup:
75727aae196SHans Petter Selasky 		break;
75827aae196SHans Petter Selasky 
75927aae196SHans Petter Selasky 	default:			/* Error */
76027aae196SHans Petter Selasky 		if (error != USB_ERR_CANCELLED) {
76127aae196SHans Petter Selasky 			/* start clear stall */
76227aae196SHans Petter Selasky 			usbd_xfer_set_stall(xfer);
76327aae196SHans Petter Selasky 			goto tr_setup;
76427aae196SHans Petter Selasky 		}
76527aae196SHans Petter Selasky 		break;
76627aae196SHans Petter Selasky 	}
76727aae196SHans Petter Selasky }
76827aae196SHans Petter Selasky 
76927aae196SHans Petter Selasky static void
umodem_intr_read_callback(struct usb_xfer * xfer,usb_error_t error)77027aae196SHans Petter Selasky umodem_intr_read_callback(struct usb_xfer *xfer, usb_error_t error)
77102ac6454SAndrew Thompson {
772760bc48eSAndrew Thompson 	struct usb_cdc_notification pkt;
773ed6d949aSAndrew Thompson 	struct umodem_softc *sc = usbd_xfer_softc(xfer);
774ed6d949aSAndrew Thompson 	struct usb_page_cache *pc;
77502ac6454SAndrew Thompson 	uint16_t wLen;
776ed6d949aSAndrew Thompson 	int actlen;
777ed6d949aSAndrew Thompson 
778ed6d949aSAndrew Thompson 	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
77902ac6454SAndrew Thompson 
78002ac6454SAndrew Thompson 	switch (USB_GET_STATE(xfer)) {
78102ac6454SAndrew Thompson 	case USB_ST_TRANSFERRED:
78202ac6454SAndrew Thompson 
783ed6d949aSAndrew Thompson 		if (actlen < 8) {
78402ac6454SAndrew Thompson 			DPRINTF("received short packet, "
785ed6d949aSAndrew Thompson 			    "%d bytes\n", actlen);
78602ac6454SAndrew Thompson 			goto tr_setup;
78702ac6454SAndrew Thompson 		}
7886d917491SHans Petter Selasky 		if (actlen > (int)sizeof(pkt)) {
78902ac6454SAndrew Thompson 			DPRINTF("truncating message\n");
790ed6d949aSAndrew Thompson 			actlen = sizeof(pkt);
79102ac6454SAndrew Thompson 		}
792ed6d949aSAndrew Thompson 		pc = usbd_xfer_get_frame(xfer, 0);
793ed6d949aSAndrew Thompson 		usbd_copy_out(pc, 0, &pkt, actlen);
79402ac6454SAndrew Thompson 
795ed6d949aSAndrew Thompson 		actlen -= 8;
79602ac6454SAndrew Thompson 
79702ac6454SAndrew Thompson 		wLen = UGETW(pkt.wLength);
798ed6d949aSAndrew Thompson 		if (actlen > wLen) {
799ed6d949aSAndrew Thompson 			actlen = wLen;
80002ac6454SAndrew Thompson 		}
80102ac6454SAndrew Thompson 		if (pkt.bmRequestType != UCDC_NOTIFICATION) {
80202ac6454SAndrew Thompson 			DPRINTF("unknown message type, "
80302ac6454SAndrew Thompson 			    "0x%02x, on notify pipe!\n",
80402ac6454SAndrew Thompson 			    pkt.bmRequestType);
80502ac6454SAndrew Thompson 			goto tr_setup;
80602ac6454SAndrew Thompson 		}
80702ac6454SAndrew Thompson 		switch (pkt.bNotification) {
80802ac6454SAndrew Thompson 		case UCDC_N_SERIAL_STATE:
80902ac6454SAndrew Thompson 			/*
81002ac6454SAndrew Thompson 			 * Set the serial state in ucom driver based on
81102ac6454SAndrew Thompson 			 * the bits from the notify message
81202ac6454SAndrew Thompson 			 */
813ed6d949aSAndrew Thompson 			if (actlen < 2) {
81402ac6454SAndrew Thompson 				DPRINTF("invalid notification "
815ed6d949aSAndrew Thompson 				    "length, %d bytes!\n", actlen);
81602ac6454SAndrew Thompson 				break;
81702ac6454SAndrew Thompson 			}
81802ac6454SAndrew Thompson 			DPRINTF("notify bytes = %02x%02x\n",
81902ac6454SAndrew Thompson 			    pkt.data[0],
82002ac6454SAndrew Thompson 			    pkt.data[1]);
82102ac6454SAndrew Thompson 
82202ac6454SAndrew Thompson 			/* Currently, lsr is always zero. */
82302ac6454SAndrew Thompson 			sc->sc_lsr = 0;
82402ac6454SAndrew Thompson 			sc->sc_msr = 0;
82502ac6454SAndrew Thompson 
82602ac6454SAndrew Thompson 			if (pkt.data[0] & UCDC_N_SERIAL_RI) {
82702ac6454SAndrew Thompson 				sc->sc_msr |= SER_RI;
82802ac6454SAndrew Thompson 			}
82902ac6454SAndrew Thompson 			if (pkt.data[0] & UCDC_N_SERIAL_DSR) {
83002ac6454SAndrew Thompson 				sc->sc_msr |= SER_DSR;
83102ac6454SAndrew Thompson 			}
83202ac6454SAndrew Thompson 			if (pkt.data[0] & UCDC_N_SERIAL_DCD) {
83302ac6454SAndrew Thompson 				sc->sc_msr |= SER_DCD;
83402ac6454SAndrew Thompson 			}
835a593f6b8SAndrew Thompson 			ucom_status_change(&sc->sc_ucom);
83602ac6454SAndrew Thompson 			break;
83702ac6454SAndrew Thompson 
83802ac6454SAndrew Thompson 		default:
83902ac6454SAndrew Thompson 			DPRINTF("unknown notify message: 0x%02x\n",
84002ac6454SAndrew Thompson 			    pkt.bNotification);
84102ac6454SAndrew Thompson 			break;
84202ac6454SAndrew Thompson 		}
84302ac6454SAndrew Thompson 
84402ac6454SAndrew Thompson 	case USB_ST_SETUP:
84502ac6454SAndrew Thompson tr_setup:
846ed6d949aSAndrew Thompson 		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
847a593f6b8SAndrew Thompson 		usbd_transfer_submit(xfer);
84802ac6454SAndrew Thompson 		return;
84902ac6454SAndrew Thompson 
85002ac6454SAndrew Thompson 	default:			/* Error */
851ed6d949aSAndrew Thompson 		if (error != USB_ERR_CANCELLED) {
85202ac6454SAndrew Thompson 			/* try to clear stall first */
853ed6d949aSAndrew Thompson 			usbd_xfer_set_stall(xfer);
85402ac6454SAndrew Thompson 			goto tr_setup;
85502ac6454SAndrew Thompson 		}
85602ac6454SAndrew Thompson 		return;
85702ac6454SAndrew Thompson 	}
85802ac6454SAndrew Thompson }
85902ac6454SAndrew Thompson 
86002ac6454SAndrew Thompson static void
umodem_write_callback(struct usb_xfer * xfer,usb_error_t error)861ed6d949aSAndrew Thompson umodem_write_callback(struct usb_xfer *xfer, usb_error_t error)
86202ac6454SAndrew Thompson {
863ed6d949aSAndrew Thompson 	struct umodem_softc *sc = usbd_xfer_softc(xfer);
864ed6d949aSAndrew Thompson 	struct usb_page_cache *pc;
86502ac6454SAndrew Thompson 	uint32_t actlen;
86602ac6454SAndrew Thompson 
86702ac6454SAndrew Thompson 	switch (USB_GET_STATE(xfer)) {
86802ac6454SAndrew Thompson 	case USB_ST_SETUP:
86902ac6454SAndrew Thompson 	case USB_ST_TRANSFERRED:
87002ac6454SAndrew Thompson tr_setup:
871ed6d949aSAndrew Thompson 		pc = usbd_xfer_get_frame(xfer, 0);
872ed6d949aSAndrew Thompson 		if (ucom_get_data(&sc->sc_ucom, pc, 0,
87302ac6454SAndrew Thompson 		    UMODEM_BUF_SIZE, &actlen)) {
874ed6d949aSAndrew Thompson 			usbd_xfer_set_frame_len(xfer, 0, actlen);
875a593f6b8SAndrew Thompson 			usbd_transfer_submit(xfer);
87602ac6454SAndrew Thompson 		}
877ec97e9caSHans Petter Selasky 		return;
87802ac6454SAndrew Thompson 
87902ac6454SAndrew Thompson 	default:			/* Error */
880ed6d949aSAndrew Thompson 		if (error != USB_ERR_CANCELLED) {
88102ac6454SAndrew Thompson 			/* try to clear stall first */
882ed6d949aSAndrew Thompson 			usbd_xfer_set_stall(xfer);
88302ac6454SAndrew Thompson 			goto tr_setup;
88402ac6454SAndrew Thompson 		}
885ec97e9caSHans Petter Selasky 		return;
88602ac6454SAndrew Thompson 	}
88702ac6454SAndrew Thompson }
88802ac6454SAndrew Thompson 
88902ac6454SAndrew Thompson static void
umodem_read_callback(struct usb_xfer * xfer,usb_error_t error)890ed6d949aSAndrew Thompson umodem_read_callback(struct usb_xfer *xfer, usb_error_t error)
89102ac6454SAndrew Thompson {
892ed6d949aSAndrew Thompson 	struct umodem_softc *sc = usbd_xfer_softc(xfer);
893ed6d949aSAndrew Thompson 	struct usb_page_cache *pc;
894ed6d949aSAndrew Thompson 	int actlen;
895ed6d949aSAndrew Thompson 
896ed6d949aSAndrew Thompson 	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
89702ac6454SAndrew Thompson 
89802ac6454SAndrew Thompson 	switch (USB_GET_STATE(xfer)) {
89902ac6454SAndrew Thompson 	case USB_ST_TRANSFERRED:
90002ac6454SAndrew Thompson 
901ed6d949aSAndrew Thompson 		DPRINTF("actlen=%d\n", actlen);
90202ac6454SAndrew Thompson 
903ed6d949aSAndrew Thompson 		pc = usbd_xfer_get_frame(xfer, 0);
904ed6d949aSAndrew Thompson 		ucom_put_data(&sc->sc_ucom, pc, 0, actlen);
90502ac6454SAndrew Thompson 
90602ac6454SAndrew Thompson 	case USB_ST_SETUP:
90702ac6454SAndrew Thompson tr_setup:
908ed6d949aSAndrew Thompson 		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
909a593f6b8SAndrew Thompson 		usbd_transfer_submit(xfer);
91002ac6454SAndrew Thompson 		return;
91102ac6454SAndrew Thompson 
91202ac6454SAndrew Thompson 	default:			/* Error */
913ed6d949aSAndrew Thompson 		if (error != USB_ERR_CANCELLED) {
91402ac6454SAndrew Thompson 			/* try to clear stall first */
915ed6d949aSAndrew Thompson 			usbd_xfer_set_stall(xfer);
91602ac6454SAndrew Thompson 			goto tr_setup;
91702ac6454SAndrew Thompson 		}
91802ac6454SAndrew Thompson 		return;
91902ac6454SAndrew Thompson 	}
92002ac6454SAndrew Thompson }
92102ac6454SAndrew Thompson 
92202ac6454SAndrew Thompson static void *
umodem_get_desc(struct usb_attach_arg * uaa,uint8_t type,uint8_t subtype)923760bc48eSAndrew Thompson umodem_get_desc(struct usb_attach_arg *uaa, uint8_t type, uint8_t subtype)
92402ac6454SAndrew Thompson {
9253c8d24f4SAndrew Thompson 	return (usbd_find_descriptor(uaa->device, NULL, uaa->info.bIfaceIndex,
9266d917491SHans Petter Selasky 	    type, 0xFF, subtype, 0xFF));
92702ac6454SAndrew Thompson }
92802ac6454SAndrew Thompson 
929e0a69b51SAndrew Thompson static usb_error_t
umodem_set_comm_feature(struct usb_device * udev,uint8_t iface_no,uint16_t feature,uint16_t state)930760bc48eSAndrew Thompson umodem_set_comm_feature(struct usb_device *udev, uint8_t iface_no,
93102ac6454SAndrew Thompson     uint16_t feature, uint16_t state)
93202ac6454SAndrew Thompson {
933760bc48eSAndrew Thompson 	struct usb_device_request req;
934760bc48eSAndrew Thompson 	struct usb_cdc_abstract_state ast;
93502ac6454SAndrew Thompson 
93602ac6454SAndrew Thompson 	DPRINTF("feature=%d state=%d\n",
93702ac6454SAndrew Thompson 	    feature, state);
93802ac6454SAndrew Thompson 
93902ac6454SAndrew Thompson 	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
94002ac6454SAndrew Thompson 	req.bRequest = UCDC_SET_COMM_FEATURE;
94102ac6454SAndrew Thompson 	USETW(req.wValue, feature);
94202ac6454SAndrew Thompson 	req.wIndex[0] = iface_no;
94302ac6454SAndrew Thompson 	req.wIndex[1] = 0;
94402ac6454SAndrew Thompson 	USETW(req.wLength, UCDC_ABSTRACT_STATE_LENGTH);
94502ac6454SAndrew Thompson 	USETW(ast.wState, state);
94602ac6454SAndrew Thompson 
947a593f6b8SAndrew Thompson 	return (usbd_do_request(udev, NULL, &req, &ast));
94802ac6454SAndrew Thompson }
94902ac6454SAndrew Thompson 
95002ac6454SAndrew Thompson static int
umodem_detach(device_t dev)95102ac6454SAndrew Thompson umodem_detach(device_t dev)
95202ac6454SAndrew Thompson {
95302ac6454SAndrew Thompson 	struct umodem_softc *sc = device_get_softc(dev);
95402ac6454SAndrew Thompson 
95502ac6454SAndrew Thompson 	DPRINTF("sc=%p\n", sc);
95602ac6454SAndrew Thompson 
957015bb88fSNick Hibma 	ucom_detach(&sc->sc_super_ucom, &sc->sc_ucom);
958a593f6b8SAndrew Thompson 	usbd_transfer_unsetup(sc->sc_xfer, UMODEM_N_TRANSFER);
95902ac6454SAndrew Thompson 
960c01fc06eSHans Petter Selasky 	device_claim_softc(dev);
961c01fc06eSHans Petter Selasky 
962c01fc06eSHans Petter Selasky 	umodem_free_softc(sc);
963c01fc06eSHans Petter Selasky 
96402ac6454SAndrew Thompson 	return (0);
96502ac6454SAndrew Thompson }
966655dc9d0SAndrew Thompson 
9675805d178SHans Petter Selasky UCOM_UNLOAD_DRAIN(umodem);
9685805d178SHans Petter Selasky 
9695805d178SHans Petter Selasky static void
umodem_free_softc(struct umodem_softc * sc)970c01fc06eSHans Petter Selasky umodem_free_softc(struct umodem_softc *sc)
9715805d178SHans Petter Selasky {
9725805d178SHans Petter Selasky 	if (ucom_unref(&sc->sc_super_ucom)) {
9735805d178SHans Petter Selasky 		mtx_destroy(&sc->sc_mtx);
974c01fc06eSHans Petter Selasky 		device_free_softc(sc);
9755805d178SHans Petter Selasky 	}
9765805d178SHans Petter Selasky }
9775805d178SHans Petter Selasky 
9785805d178SHans Petter Selasky static void
umodem_free(struct ucom_softc * ucom)9795805d178SHans Petter Selasky umodem_free(struct ucom_softc *ucom)
9805805d178SHans Petter Selasky {
981c01fc06eSHans Petter Selasky 	umodem_free_softc(ucom->sc_parent);
9825805d178SHans Petter Selasky }
9835805d178SHans Petter Selasky 
984655dc9d0SAndrew Thompson static void
umodem_poll(struct ucom_softc * ucom)985655dc9d0SAndrew Thompson umodem_poll(struct ucom_softc *ucom)
986655dc9d0SAndrew Thompson {
987655dc9d0SAndrew Thompson 	struct umodem_softc *sc = ucom->sc_parent;
988655dc9d0SAndrew Thompson 	usbd_transfer_poll(sc->sc_xfer, UMODEM_N_TRANSFER);
989655dc9d0SAndrew Thompson }
99027aae196SHans Petter Selasky 
99127aae196SHans Petter Selasky static int
umodem_handle_request(device_t dev,const void * preq,void ** pptr,uint16_t * plen,uint16_t offset,uint8_t * pstate)99227aae196SHans Petter Selasky umodem_handle_request(device_t dev,
99327aae196SHans Petter Selasky     const void *preq, void **pptr, uint16_t *plen,
99427aae196SHans Petter Selasky     uint16_t offset, uint8_t *pstate)
99527aae196SHans Petter Selasky {
99627aae196SHans Petter Selasky 	struct umodem_softc *sc = device_get_softc(dev);
99727aae196SHans Petter Selasky 	const struct usb_device_request *req = preq;
99827aae196SHans Petter Selasky 	uint8_t is_complete = *pstate;
99927aae196SHans Petter Selasky 
100027aae196SHans Petter Selasky 	DPRINTF("sc=%p\n", sc);
100127aae196SHans Petter Selasky 
100227aae196SHans Petter Selasky 	if (!is_complete) {
100327aae196SHans Petter Selasky 		if ((req->bmRequestType == UT_WRITE_CLASS_INTERFACE) &&
100427aae196SHans Petter Selasky 		    (req->bRequest == UCDC_SET_LINE_CODING) &&
100527aae196SHans Petter Selasky 		    (req->wIndex[0] == sc->sc_ctrl_iface_no) &&
100627aae196SHans Petter Selasky 		    (req->wIndex[1] == 0x00) &&
100727aae196SHans Petter Selasky 		    (req->wValue[0] == 0x00) &&
100827aae196SHans Petter Selasky 		    (req->wValue[1] == 0x00)) {
100927aae196SHans Petter Selasky 			if (offset == 0) {
101027aae196SHans Petter Selasky 				*plen = sizeof(sc->sc_line_coding);
101127aae196SHans Petter Selasky 				*pptr = &sc->sc_line_coding;
101227aae196SHans Petter Selasky 			} else {
101327aae196SHans Petter Selasky 				*plen = 0;
101427aae196SHans Petter Selasky 			}
101527aae196SHans Petter Selasky 			return (0);
101627aae196SHans Petter Selasky 		} else if ((req->bmRequestType == UT_WRITE_CLASS_INTERFACE) &&
101727aae196SHans Petter Selasky 		    (req->wIndex[0] == sc->sc_ctrl_iface_no) &&
101827aae196SHans Petter Selasky 		    (req->wIndex[1] == 0x00) &&
101927aae196SHans Petter Selasky 		    (req->bRequest == UCDC_SET_COMM_FEATURE)) {
102027aae196SHans Petter Selasky 			if (offset == 0) {
102127aae196SHans Petter Selasky 				*plen = sizeof(sc->sc_abstract_state);
102227aae196SHans Petter Selasky 				*pptr = &sc->sc_abstract_state;
102327aae196SHans Petter Selasky 			} else {
102427aae196SHans Petter Selasky 				*plen = 0;
102527aae196SHans Petter Selasky 			}
102627aae196SHans Petter Selasky 			return (0);
102727aae196SHans Petter Selasky 		} else if ((req->bmRequestType == UT_WRITE_CLASS_INTERFACE) &&
102827aae196SHans Petter Selasky 		    (req->wIndex[0] == sc->sc_ctrl_iface_no) &&
102927aae196SHans Petter Selasky 		    (req->wIndex[1] == 0x00) &&
103027aae196SHans Petter Selasky 		    (req->bRequest == UCDC_SET_CONTROL_LINE_STATE)) {
103127aae196SHans Petter Selasky 			*plen = 0;
103227aae196SHans Petter Selasky 			return (0);
103327aae196SHans Petter Selasky 		} else if ((req->bmRequestType == UT_WRITE_CLASS_INTERFACE) &&
103427aae196SHans Petter Selasky 		    (req->wIndex[0] == sc->sc_ctrl_iface_no) &&
103527aae196SHans Petter Selasky 		    (req->wIndex[1] == 0x00) &&
103627aae196SHans Petter Selasky 		    (req->bRequest == UCDC_SEND_BREAK)) {
103727aae196SHans Petter Selasky 			*plen = 0;
103827aae196SHans Petter Selasky 			return (0);
103927aae196SHans Petter Selasky 		}
104027aae196SHans Petter Selasky 	}
104127aae196SHans Petter Selasky 	return (ENXIO);			/* use builtin handler */
104227aae196SHans Petter Selasky }
1043