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