1*0f1bf1c2SAdrian Chadd /*-
2*0f1bf1c2SAdrian Chadd * SPDX-License-Identifier: BSD-3-Clause
3*0f1bf1c2SAdrian Chadd *
4*0f1bf1c2SAdrian Chadd * Original copyright (c) 2016 genua mbH (OpenBSD version)
5*0f1bf1c2SAdrian Chadd *
6*0f1bf1c2SAdrian Chadd * Permission to use, copy, modify, and distribute this software for any
7*0f1bf1c2SAdrian Chadd * purpose with or without fee is hereby granted, provided that the above
8*0f1bf1c2SAdrian Chadd * copyright notice and this permission notice appear in all copies.
9*0f1bf1c2SAdrian Chadd *
10*0f1bf1c2SAdrian Chadd * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11*0f1bf1c2SAdrian Chadd * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12*0f1bf1c2SAdrian Chadd * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13*0f1bf1c2SAdrian Chadd * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14*0f1bf1c2SAdrian Chadd * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15*0f1bf1c2SAdrian Chadd * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16*0f1bf1c2SAdrian Chadd * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17*0f1bf1c2SAdrian Chadd *
18*0f1bf1c2SAdrian Chadd * Copyright (c) 2022 ADISTA SAS (re-write for FreeBSD)
19*0f1bf1c2SAdrian Chadd *
20*0f1bf1c2SAdrian Chadd * Re-write for FreeBSD by Pierre Pronchery <pierre@defora.net>
21*0f1bf1c2SAdrian Chadd *
22*0f1bf1c2SAdrian Chadd * Redistribution and use in source and binary forms, with or without
23*0f1bf1c2SAdrian Chadd * modification, are permitted provided that the following conditions are met:
24*0f1bf1c2SAdrian Chadd *
25*0f1bf1c2SAdrian Chadd * - Redistributions of source code must retain the above copyright notice,
26*0f1bf1c2SAdrian Chadd * this list of conditions and the following disclaimer.
27*0f1bf1c2SAdrian Chadd * - Redistributions in binary form must reproduce the above copyright notice,
28*0f1bf1c2SAdrian Chadd * this list of conditions and the following disclaimer in the documentation
29*0f1bf1c2SAdrian Chadd * and/or other materials provided with the distribution.
30*0f1bf1c2SAdrian Chadd * - Neither the name of the copyright holder nor the names of its contributors
31*0f1bf1c2SAdrian Chadd * may be used to endorse or promote products derived from this software
32*0f1bf1c2SAdrian Chadd * without specific prior written permission.
33*0f1bf1c2SAdrian Chadd *
34*0f1bf1c2SAdrian Chadd * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
35*0f1bf1c2SAdrian Chadd * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
36*0f1bf1c2SAdrian Chadd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
37*0f1bf1c2SAdrian Chadd * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
38*0f1bf1c2SAdrian Chadd * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
39*0f1bf1c2SAdrian Chadd * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
40*0f1bf1c2SAdrian Chadd * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
41*0f1bf1c2SAdrian Chadd * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
42*0f1bf1c2SAdrian Chadd * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
43*0f1bf1c2SAdrian Chadd * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
44*0f1bf1c2SAdrian Chadd * POSSIBILITY OF SUCH DAMAGE.
45*0f1bf1c2SAdrian Chadd *
46*0f1bf1c2SAdrian Chadd * $NetBSD: if_umb.c,v 1.5 2018/09/20 09:45:16 khorben Exp $
47*0f1bf1c2SAdrian Chadd * $OpenBSD: if_umb.c,v 1.18 2018/02/19 08:59:52 mpi Exp $
48*0f1bf1c2SAdrian Chadd */
49*0f1bf1c2SAdrian Chadd
50*0f1bf1c2SAdrian Chadd /*
51*0f1bf1c2SAdrian Chadd * Mobile Broadband Interface Model specification:
52*0f1bf1c2SAdrian Chadd * http://www.usb.org/developers/docs/devclass_docs/MBIM10Errata1_073013.zip
53*0f1bf1c2SAdrian Chadd * Compliance testing guide
54*0f1bf1c2SAdrian Chadd * http://www.usb.org/developers/docs/devclass_docs/MBIM-Compliance-1.0.pdf
55*0f1bf1c2SAdrian Chadd */
56*0f1bf1c2SAdrian Chadd
57*0f1bf1c2SAdrian Chadd #include <sys/param.h>
58*0f1bf1c2SAdrian Chadd #include <sys/module.h>
59*0f1bf1c2SAdrian Chadd #include <sys/endian.h>
60*0f1bf1c2SAdrian Chadd #include <sys/kernel.h>
61*0f1bf1c2SAdrian Chadd #include <sys/mbuf.h>
62*0f1bf1c2SAdrian Chadd #include <sys/priv.h>
63*0f1bf1c2SAdrian Chadd #include <sys/socket.h>
64*0f1bf1c2SAdrian Chadd #include <sys/sockio.h>
65*0f1bf1c2SAdrian Chadd #include <sys/systm.h>
66*0f1bf1c2SAdrian Chadd #include <sys/syslog.h>
67*0f1bf1c2SAdrian Chadd #include <sys/kernel.h>
68*0f1bf1c2SAdrian Chadd #include <sys/queue.h>
69*0f1bf1c2SAdrian Chadd
70*0f1bf1c2SAdrian Chadd #include <sys/conf.h>
71*0f1bf1c2SAdrian Chadd #include <sys/bus.h>
72*0f1bf1c2SAdrian Chadd #include <sys/mutex.h>
73*0f1bf1c2SAdrian Chadd #include <sys/condvar.h>
74*0f1bf1c2SAdrian Chadd #include <sys/taskqueue.h>
75*0f1bf1c2SAdrian Chadd
76*0f1bf1c2SAdrian Chadd #include <machine/_inttypes.h>
77*0f1bf1c2SAdrian Chadd
78*0f1bf1c2SAdrian Chadd #include <net/bpf.h>
79*0f1bf1c2SAdrian Chadd #include <net/if.h>
80*0f1bf1c2SAdrian Chadd #include <net/if_media.h>
81*0f1bf1c2SAdrian Chadd #include <net/if_types.h>
82*0f1bf1c2SAdrian Chadd #include <net/if_var.h>
83*0f1bf1c2SAdrian Chadd #include <net/netisr.h>
84*0f1bf1c2SAdrian Chadd #include <net/route.h>
85*0f1bf1c2SAdrian Chadd
86*0f1bf1c2SAdrian Chadd #include <netinet/in.h>
87*0f1bf1c2SAdrian Chadd #include <netinet/in_var.h>
88*0f1bf1c2SAdrian Chadd #include <netinet/ip.h>
89*0f1bf1c2SAdrian Chadd
90*0f1bf1c2SAdrian Chadd #include <dev/usb/usb.h>
91*0f1bf1c2SAdrian Chadd #include <dev/usb/usb_cdc.h>
92*0f1bf1c2SAdrian Chadd #include <dev/usb/usbdi.h>
93*0f1bf1c2SAdrian Chadd #include <dev/usb/usb_device.h>
94*0f1bf1c2SAdrian Chadd #include <dev/usb/usb_process.h>
95*0f1bf1c2SAdrian Chadd #include <dev/usb/usbdi_util.h>
96*0f1bf1c2SAdrian Chadd #include "usb_if.h"
97*0f1bf1c2SAdrian Chadd
98*0f1bf1c2SAdrian Chadd #include "mbim.h"
99*0f1bf1c2SAdrian Chadd #include "if_umbreg.h"
100*0f1bf1c2SAdrian Chadd
101*0f1bf1c2SAdrian Chadd MALLOC_DECLARE(M_MBIM_CID_CONNECT);
102*0f1bf1c2SAdrian Chadd MALLOC_DEFINE(M_MBIM_CID_CONNECT, "mbim_cid_connect",
103*0f1bf1c2SAdrian Chadd "Connection parameters for MBIM");
104*0f1bf1c2SAdrian Chadd
105*0f1bf1c2SAdrian Chadd #ifdef UMB_DEBUG
106*0f1bf1c2SAdrian Chadd #define DPRINTF(x...) \
107*0f1bf1c2SAdrian Chadd do { if (umb_debug) log(LOG_DEBUG, x); } while (0)
108*0f1bf1c2SAdrian Chadd
109*0f1bf1c2SAdrian Chadd #define DPRINTFN(n, x...) \
110*0f1bf1c2SAdrian Chadd do { if (umb_debug >= (n)) log(LOG_DEBUG, x); } while (0)
111*0f1bf1c2SAdrian Chadd
112*0f1bf1c2SAdrian Chadd #define DDUMPN(n, b, l) \
113*0f1bf1c2SAdrian Chadd do { \
114*0f1bf1c2SAdrian Chadd if (umb_debug >= (n)) \
115*0f1bf1c2SAdrian Chadd umb_dump((b), (l)); \
116*0f1bf1c2SAdrian Chadd } while (0)
117*0f1bf1c2SAdrian Chadd
118*0f1bf1c2SAdrian Chadd const int umb_debug = 1;
119*0f1bf1c2SAdrian Chadd static char *umb_uuid2str(uint8_t [MBIM_UUID_LEN]);
120*0f1bf1c2SAdrian Chadd static void umb_dump(void *, int);
121*0f1bf1c2SAdrian Chadd
122*0f1bf1c2SAdrian Chadd #else
123*0f1bf1c2SAdrian Chadd #define DPRINTF(x...) do { } while (0)
124*0f1bf1c2SAdrian Chadd #define DPRINTFN(n, x...) do { } while (0)
125*0f1bf1c2SAdrian Chadd #define DDUMPN(n, b, l) do { } while (0)
126*0f1bf1c2SAdrian Chadd #endif
127*0f1bf1c2SAdrian Chadd
128*0f1bf1c2SAdrian Chadd #define DEVNAM(sc) device_get_nameunit((sc)->sc_dev)
129*0f1bf1c2SAdrian Chadd
130*0f1bf1c2SAdrian Chadd /*
131*0f1bf1c2SAdrian Chadd * State change timeout
132*0f1bf1c2SAdrian Chadd */
133*0f1bf1c2SAdrian Chadd #define UMB_STATE_CHANGE_TIMEOUT 30
134*0f1bf1c2SAdrian Chadd
135*0f1bf1c2SAdrian Chadd /*
136*0f1bf1c2SAdrian Chadd * State change flags
137*0f1bf1c2SAdrian Chadd */
138*0f1bf1c2SAdrian Chadd #define UMB_NS_DONT_DROP 0x0001 /* do not drop below current state */
139*0f1bf1c2SAdrian Chadd #define UMB_NS_DONT_RAISE 0x0002 /* do not raise below current state */
140*0f1bf1c2SAdrian Chadd
141*0f1bf1c2SAdrian Chadd /*
142*0f1bf1c2SAdrian Chadd * Diagnostic macros
143*0f1bf1c2SAdrian Chadd */
144*0f1bf1c2SAdrian Chadd const struct umb_valdescr umb_regstates[] = MBIM_REGSTATE_DESCRIPTIONS;
145*0f1bf1c2SAdrian Chadd const struct umb_valdescr umb_dataclasses[] = MBIM_DATACLASS_DESCRIPTIONS;
146*0f1bf1c2SAdrian Chadd const struct umb_valdescr umb_simstate[] = MBIM_SIMSTATE_DESCRIPTIONS;
147*0f1bf1c2SAdrian Chadd const struct umb_valdescr umb_messages[] = MBIM_MESSAGES_DESCRIPTIONS;
148*0f1bf1c2SAdrian Chadd const struct umb_valdescr umb_status[] = MBIM_STATUS_DESCRIPTIONS;
149*0f1bf1c2SAdrian Chadd const struct umb_valdescr umb_cids[] = MBIM_CID_DESCRIPTIONS;
150*0f1bf1c2SAdrian Chadd const struct umb_valdescr umb_pktstate[] = MBIM_PKTSRV_STATE_DESCRIPTIONS;
151*0f1bf1c2SAdrian Chadd const struct umb_valdescr umb_actstate[] = MBIM_ACTIVATION_STATE_DESCRIPTIONS;
152*0f1bf1c2SAdrian Chadd const struct umb_valdescr umb_error[] = MBIM_ERROR_DESCRIPTIONS;
153*0f1bf1c2SAdrian Chadd const struct umb_valdescr umb_pintype[] = MBIM_PINTYPE_DESCRIPTIONS;
154*0f1bf1c2SAdrian Chadd const struct umb_valdescr umb_istate[] = UMB_INTERNAL_STATE_DESCRIPTIONS;
155*0f1bf1c2SAdrian Chadd
156*0f1bf1c2SAdrian Chadd #define umb_regstate(c) umb_val2descr(umb_regstates, (c))
157*0f1bf1c2SAdrian Chadd #define umb_dataclass(c) umb_val2descr(umb_dataclasses, (c))
158*0f1bf1c2SAdrian Chadd #define umb_simstate(s) umb_val2descr(umb_simstate, (s))
159*0f1bf1c2SAdrian Chadd #define umb_request2str(m) umb_val2descr(umb_messages, (m))
160*0f1bf1c2SAdrian Chadd #define umb_status2str(s) umb_val2descr(umb_status, (s))
161*0f1bf1c2SAdrian Chadd #define umb_cid2str(c) umb_val2descr(umb_cids, (c))
162*0f1bf1c2SAdrian Chadd #define umb_packet_state(s) umb_val2descr(umb_pktstate, (s))
163*0f1bf1c2SAdrian Chadd #define umb_activation(s) umb_val2descr(umb_actstate, (s))
164*0f1bf1c2SAdrian Chadd #define umb_error2str(e) umb_val2descr(umb_error, (e))
165*0f1bf1c2SAdrian Chadd #define umb_pin_type(t) umb_val2descr(umb_pintype, (t))
166*0f1bf1c2SAdrian Chadd #define umb_istate(s) umb_val2descr(umb_istate, (s))
167*0f1bf1c2SAdrian Chadd
168*0f1bf1c2SAdrian Chadd static device_probe_t umb_probe;
169*0f1bf1c2SAdrian Chadd static device_attach_t umb_attach;
170*0f1bf1c2SAdrian Chadd static device_detach_t umb_detach;
171*0f1bf1c2SAdrian Chadd static device_suspend_t umb_suspend;
172*0f1bf1c2SAdrian Chadd static device_resume_t umb_resume;
173*0f1bf1c2SAdrian Chadd static void umb_attach_task(struct usb_proc_msg *);
174*0f1bf1c2SAdrian Chadd static usb_handle_request_t umb_handle_request;
175*0f1bf1c2SAdrian Chadd static int umb_deactivate(device_t);
176*0f1bf1c2SAdrian Chadd static void umb_ncm_setup(struct umb_softc *, struct usb_config *);
177*0f1bf1c2SAdrian Chadd static void umb_close_bulkpipes(struct umb_softc *);
178*0f1bf1c2SAdrian Chadd static int umb_ioctl(if_t , u_long, caddr_t);
179*0f1bf1c2SAdrian Chadd static void umb_init(void *);
180*0f1bf1c2SAdrian Chadd #ifdef DEV_NETMAP
181*0f1bf1c2SAdrian Chadd static void umb_input(if_t , struct mbuf *);
182*0f1bf1c2SAdrian Chadd #endif
183*0f1bf1c2SAdrian Chadd static int umb_output(if_t , struct mbuf *,
184*0f1bf1c2SAdrian Chadd const struct sockaddr *, struct route *);
185*0f1bf1c2SAdrian Chadd static void umb_start(if_t );
186*0f1bf1c2SAdrian Chadd static void umb_start_task(struct usb_proc_msg *);
187*0f1bf1c2SAdrian Chadd #if 0
188*0f1bf1c2SAdrian Chadd static void umb_watchdog(if_t );
189*0f1bf1c2SAdrian Chadd #endif
190*0f1bf1c2SAdrian Chadd static void umb_statechg_timeout(void *);
191*0f1bf1c2SAdrian Chadd
192*0f1bf1c2SAdrian Chadd static int umb_mediachange(if_t );
193*0f1bf1c2SAdrian Chadd static void umb_mediastatus(if_t , struct ifmediareq *);
194*0f1bf1c2SAdrian Chadd
195*0f1bf1c2SAdrian Chadd static void umb_add_task(struct umb_softc *sc, usb_proc_callback_t,
196*0f1bf1c2SAdrian Chadd struct usb_proc_msg *, struct usb_proc_msg *, int);
197*0f1bf1c2SAdrian Chadd static void umb_newstate(struct umb_softc *, enum umb_state, int);
198*0f1bf1c2SAdrian Chadd static void umb_state_task(struct usb_proc_msg *);
199*0f1bf1c2SAdrian Chadd static void umb_up(struct umb_softc *);
200*0f1bf1c2SAdrian Chadd static void umb_down(struct umb_softc *, int);
201*0f1bf1c2SAdrian Chadd
202*0f1bf1c2SAdrian Chadd static void umb_get_response_task(struct usb_proc_msg *);
203*0f1bf1c2SAdrian Chadd
204*0f1bf1c2SAdrian Chadd static void umb_decode_response(struct umb_softc *, void *, int);
205*0f1bf1c2SAdrian Chadd static void umb_handle_indicate_status_msg(struct umb_softc *, void *,
206*0f1bf1c2SAdrian Chadd int);
207*0f1bf1c2SAdrian Chadd static void umb_handle_opendone_msg(struct umb_softc *, void *, int);
208*0f1bf1c2SAdrian Chadd static void umb_handle_closedone_msg(struct umb_softc *, void *, int);
209*0f1bf1c2SAdrian Chadd static int umb_decode_register_state(struct umb_softc *, void *, int);
210*0f1bf1c2SAdrian Chadd static int umb_decode_devices_caps(struct umb_softc *, void *, int);
211*0f1bf1c2SAdrian Chadd static int umb_decode_subscriber_status(struct umb_softc *, void *, int);
212*0f1bf1c2SAdrian Chadd static int umb_decode_radio_state(struct umb_softc *, void *, int);
213*0f1bf1c2SAdrian Chadd static int umb_decode_pin(struct umb_softc *, void *, int);
214*0f1bf1c2SAdrian Chadd static int umb_decode_packet_service(struct umb_softc *, void *, int);
215*0f1bf1c2SAdrian Chadd static int umb_decode_signal_state(struct umb_softc *, void *, int);
216*0f1bf1c2SAdrian Chadd static int umb_decode_connect_info(struct umb_softc *, void *, int);
217*0f1bf1c2SAdrian Chadd static int umb_decode_ip_configuration(struct umb_softc *, void *, int);
218*0f1bf1c2SAdrian Chadd static void umb_rx(struct umb_softc *);
219*0f1bf1c2SAdrian Chadd static usb_callback_t umb_rxeof;
220*0f1bf1c2SAdrian Chadd static void umb_rxflush(struct umb_softc *);
221*0f1bf1c2SAdrian Chadd static int umb_encap(struct umb_softc *, struct mbuf *, struct usb_xfer *);
222*0f1bf1c2SAdrian Chadd static usb_callback_t umb_txeof;
223*0f1bf1c2SAdrian Chadd static void umb_txflush(struct umb_softc *);
224*0f1bf1c2SAdrian Chadd static void umb_decap(struct umb_softc *, struct usb_xfer *, int);
225*0f1bf1c2SAdrian Chadd
226*0f1bf1c2SAdrian Chadd static usb_error_t umb_send_encap_command(struct umb_softc *, void *, int);
227*0f1bf1c2SAdrian Chadd static int umb_get_encap_response(struct umb_softc *, void *, int *);
228*0f1bf1c2SAdrian Chadd static void umb_ctrl_msg(struct umb_softc *, uint32_t, void *, int);
229*0f1bf1c2SAdrian Chadd
230*0f1bf1c2SAdrian Chadd static void umb_open(struct umb_softc *);
231*0f1bf1c2SAdrian Chadd static void umb_close(struct umb_softc *);
232*0f1bf1c2SAdrian Chadd
233*0f1bf1c2SAdrian Chadd static int umb_setpin(struct umb_softc *, int, int, void *, int, void *,
234*0f1bf1c2SAdrian Chadd int);
235*0f1bf1c2SAdrian Chadd static void umb_setdataclass(struct umb_softc *);
236*0f1bf1c2SAdrian Chadd static void umb_radio(struct umb_softc *, int);
237*0f1bf1c2SAdrian Chadd static void umb_allocate_cid(struct umb_softc *);
238*0f1bf1c2SAdrian Chadd static void umb_send_fcc_auth(struct umb_softc *);
239*0f1bf1c2SAdrian Chadd static void umb_packet_service(struct umb_softc *, int);
240*0f1bf1c2SAdrian Chadd static void umb_connect(struct umb_softc *);
241*0f1bf1c2SAdrian Chadd static void umb_disconnect(struct umb_softc *);
242*0f1bf1c2SAdrian Chadd static void umb_send_connect(struct umb_softc *, int);
243*0f1bf1c2SAdrian Chadd
244*0f1bf1c2SAdrian Chadd static void umb_qry_ipconfig(struct umb_softc *);
245*0f1bf1c2SAdrian Chadd static void umb_cmd(struct umb_softc *, int, int, const void *, int);
246*0f1bf1c2SAdrian Chadd static void umb_cmd1(struct umb_softc *, int, int, const void *, int, uint8_t *);
247*0f1bf1c2SAdrian Chadd static void umb_command_done(struct umb_softc *, void *, int);
248*0f1bf1c2SAdrian Chadd static void umb_decode_cid(struct umb_softc *, uint32_t, void *, int);
249*0f1bf1c2SAdrian Chadd static void umb_decode_qmi(struct umb_softc *, uint8_t *, int);
250*0f1bf1c2SAdrian Chadd
251*0f1bf1c2SAdrian Chadd static usb_callback_t umb_intr;
252*0f1bf1c2SAdrian Chadd
253*0f1bf1c2SAdrian Chadd static char *umb_ntop(struct sockaddr *);
254*0f1bf1c2SAdrian Chadd
255*0f1bf1c2SAdrian Chadd static const int umb_xfer_tout = USB_DEFAULT_TIMEOUT;
256*0f1bf1c2SAdrian Chadd
257*0f1bf1c2SAdrian Chadd static uint8_t umb_uuid_basic_connect[] = MBIM_UUID_BASIC_CONNECT;
258*0f1bf1c2SAdrian Chadd static uint8_t umb_uuid_context_internet[] = MBIM_UUID_CONTEXT_INTERNET;
259*0f1bf1c2SAdrian Chadd static uint8_t umb_uuid_qmi_mbim[] = MBIM_UUID_QMI_MBIM;
260*0f1bf1c2SAdrian Chadd static uint32_t umb_session_id = 0;
261*0f1bf1c2SAdrian Chadd
262*0f1bf1c2SAdrian Chadd static const struct usb_config umb_config[UMB_N_TRANSFER] = {
263*0f1bf1c2SAdrian Chadd [UMB_INTR_RX] = {
264*0f1bf1c2SAdrian Chadd .type = UE_INTERRUPT,
265*0f1bf1c2SAdrian Chadd .endpoint = UE_ADDR_ANY,
266*0f1bf1c2SAdrian Chadd .direction = UE_DIR_IN,
267*0f1bf1c2SAdrian Chadd .if_index = 1,
268*0f1bf1c2SAdrian Chadd .callback = umb_intr,
269*0f1bf1c2SAdrian Chadd .bufsize = sizeof (struct usb_cdc_notification),
270*0f1bf1c2SAdrian Chadd .flags = {.pipe_bof = 1,.short_xfer_ok = 1},
271*0f1bf1c2SAdrian Chadd .usb_mode = USB_MODE_HOST,
272*0f1bf1c2SAdrian Chadd },
273*0f1bf1c2SAdrian Chadd [UMB_BULK_RX] = {
274*0f1bf1c2SAdrian Chadd .type = UE_BULK,
275*0f1bf1c2SAdrian Chadd .endpoint = UE_ADDR_ANY,
276*0f1bf1c2SAdrian Chadd .direction = UE_DIR_IN,
277*0f1bf1c2SAdrian Chadd .if_index = 0,
278*0f1bf1c2SAdrian Chadd .callback = umb_rxeof,
279*0f1bf1c2SAdrian Chadd .bufsize = 8 * 1024,
280*0f1bf1c2SAdrian Chadd .flags = {.pipe_bof = 1,.short_xfer_ok = 1,.ext_buffer = 1},
281*0f1bf1c2SAdrian Chadd .usb_mode = USB_MODE_HOST,
282*0f1bf1c2SAdrian Chadd },
283*0f1bf1c2SAdrian Chadd [UMB_BULK_TX] = {
284*0f1bf1c2SAdrian Chadd .type = UE_BULK,
285*0f1bf1c2SAdrian Chadd .endpoint = UE_ADDR_ANY,
286*0f1bf1c2SAdrian Chadd .direction = UE_DIR_OUT,
287*0f1bf1c2SAdrian Chadd .if_index = 0,
288*0f1bf1c2SAdrian Chadd .callback = umb_txeof,
289*0f1bf1c2SAdrian Chadd .bufsize = 8 * 1024,
290*0f1bf1c2SAdrian Chadd .flags = {.pipe_bof = 1,.force_short_xfer = 1,.ext_buffer = 1},
291*0f1bf1c2SAdrian Chadd .timeout = umb_xfer_tout,
292*0f1bf1c2SAdrian Chadd .usb_mode = USB_MODE_HOST,
293*0f1bf1c2SAdrian Chadd },
294*0f1bf1c2SAdrian Chadd };
295*0f1bf1c2SAdrian Chadd
296*0f1bf1c2SAdrian Chadd static device_method_t umb_methods[] = {
297*0f1bf1c2SAdrian Chadd /* USB interface */
298*0f1bf1c2SAdrian Chadd DEVMETHOD(usb_handle_request, umb_handle_request),
299*0f1bf1c2SAdrian Chadd
300*0f1bf1c2SAdrian Chadd /* Device interface */
301*0f1bf1c2SAdrian Chadd DEVMETHOD(device_probe, umb_probe),
302*0f1bf1c2SAdrian Chadd DEVMETHOD(device_attach, umb_attach),
303*0f1bf1c2SAdrian Chadd DEVMETHOD(device_detach, umb_detach),
304*0f1bf1c2SAdrian Chadd DEVMETHOD(device_suspend, umb_suspend),
305*0f1bf1c2SAdrian Chadd DEVMETHOD(device_resume, umb_resume),
306*0f1bf1c2SAdrian Chadd
307*0f1bf1c2SAdrian Chadd DEVMETHOD_END
308*0f1bf1c2SAdrian Chadd };
309*0f1bf1c2SAdrian Chadd
310*0f1bf1c2SAdrian Chadd static driver_t umb_driver = {
311*0f1bf1c2SAdrian Chadd .name = "umb",
312*0f1bf1c2SAdrian Chadd .methods = umb_methods,
313*0f1bf1c2SAdrian Chadd .size = sizeof (struct umb_softc),
314*0f1bf1c2SAdrian Chadd };
315*0f1bf1c2SAdrian Chadd
316*0f1bf1c2SAdrian Chadd MALLOC_DEFINE(M_USB_UMB, "USB UMB", "USB MBIM driver");
317*0f1bf1c2SAdrian Chadd
318*0f1bf1c2SAdrian Chadd const int umb_delay = 4000;
319*0f1bf1c2SAdrian Chadd
320*0f1bf1c2SAdrian Chadd /*
321*0f1bf1c2SAdrian Chadd * These devices require an "FCC Authentication" command.
322*0f1bf1c2SAdrian Chadd */
323*0f1bf1c2SAdrian Chadd #ifndef USB_VENDOR_SIERRA
324*0f1bf1c2SAdrian Chadd # define USB_VENDOR_SIERRA 0x1199
325*0f1bf1c2SAdrian Chadd #endif
326*0f1bf1c2SAdrian Chadd #ifndef USB_PRODUCT_SIERRA_EM7455
327*0f1bf1c2SAdrian Chadd # define USB_PRODUCT_SIERRA_EM7455 0x9079
328*0f1bf1c2SAdrian Chadd #endif
329*0f1bf1c2SAdrian Chadd const struct usb_device_id umb_fccauth_devs[] = {
330*0f1bf1c2SAdrian Chadd {
331*0f1bf1c2SAdrian Chadd .match_flag_vendor = 1,
332*0f1bf1c2SAdrian Chadd .match_flag_product = 1,
333*0f1bf1c2SAdrian Chadd .idVendor = USB_VENDOR_SIERRA,
334*0f1bf1c2SAdrian Chadd .idProduct = USB_PRODUCT_SIERRA_EM7455
335*0f1bf1c2SAdrian Chadd }
336*0f1bf1c2SAdrian Chadd };
337*0f1bf1c2SAdrian Chadd
338*0f1bf1c2SAdrian Chadd static const uint8_t umb_qmi_alloc_cid[] = {
339*0f1bf1c2SAdrian Chadd 0x01,
340*0f1bf1c2SAdrian Chadd 0x0f, 0x00, /* len */
341*0f1bf1c2SAdrian Chadd 0x00, /* QMUX flags */
342*0f1bf1c2SAdrian Chadd 0x00, /* service "ctl" */
343*0f1bf1c2SAdrian Chadd 0x00, /* CID */
344*0f1bf1c2SAdrian Chadd 0x00, /* QMI flags */
345*0f1bf1c2SAdrian Chadd 0x01, /* transaction */
346*0f1bf1c2SAdrian Chadd 0x22, 0x00, /* msg "Allocate CID" */
347*0f1bf1c2SAdrian Chadd 0x04, 0x00, /* TLV len */
348*0f1bf1c2SAdrian Chadd 0x01, 0x01, 0x00, 0x02 /* TLV */
349*0f1bf1c2SAdrian Chadd };
350*0f1bf1c2SAdrian Chadd
351*0f1bf1c2SAdrian Chadd static const uint8_t umb_qmi_fcc_auth[] = {
352*0f1bf1c2SAdrian Chadd 0x01,
353*0f1bf1c2SAdrian Chadd 0x0c, 0x00, /* len */
354*0f1bf1c2SAdrian Chadd 0x00, /* QMUX flags */
355*0f1bf1c2SAdrian Chadd 0x02, /* service "dms" */
356*0f1bf1c2SAdrian Chadd #define UMB_QMI_CID_OFFS 5
357*0f1bf1c2SAdrian Chadd 0x00, /* CID (filled in later) */
358*0f1bf1c2SAdrian Chadd 0x00, /* QMI flags */
359*0f1bf1c2SAdrian Chadd 0x01, 0x00, /* transaction */
360*0f1bf1c2SAdrian Chadd 0x5f, 0x55, /* msg "Send FCC Authentication" */
361*0f1bf1c2SAdrian Chadd 0x00, 0x00 /* TLV len */
362*0f1bf1c2SAdrian Chadd };
363*0f1bf1c2SAdrian Chadd
364*0f1bf1c2SAdrian Chadd static int
umb_probe(device_t dev)365*0f1bf1c2SAdrian Chadd umb_probe(device_t dev)
366*0f1bf1c2SAdrian Chadd {
367*0f1bf1c2SAdrian Chadd struct usb_attach_arg *uaa = device_get_ivars(dev);
368*0f1bf1c2SAdrian Chadd usb_interface_descriptor_t *id;
369*0f1bf1c2SAdrian Chadd
370*0f1bf1c2SAdrian Chadd if (uaa->usb_mode != USB_MODE_HOST)
371*0f1bf1c2SAdrian Chadd return (ENXIO);
372*0f1bf1c2SAdrian Chadd if ((id = usbd_get_interface_descriptor(uaa->iface)) == NULL)
373*0f1bf1c2SAdrian Chadd return (ENXIO);
374*0f1bf1c2SAdrian Chadd
375*0f1bf1c2SAdrian Chadd /*
376*0f1bf1c2SAdrian Chadd * If this function implements NCM, check if alternate setting
377*0f1bf1c2SAdrian Chadd * 1 implements MBIM.
378*0f1bf1c2SAdrian Chadd */
379*0f1bf1c2SAdrian Chadd if (id->bInterfaceClass == UICLASS_CDC &&
380*0f1bf1c2SAdrian Chadd id->bInterfaceSubClass ==
381*0f1bf1c2SAdrian Chadd UISUBCLASS_NETWORK_CONTROL_MODEL) {
382*0f1bf1c2SAdrian Chadd id = usbd_get_interface_descriptor(
383*0f1bf1c2SAdrian Chadd usbd_get_iface(uaa->device,
384*0f1bf1c2SAdrian Chadd uaa->info.bIfaceIndex + 1));
385*0f1bf1c2SAdrian Chadd if (id == NULL || id->bAlternateSetting != 1)
386*0f1bf1c2SAdrian Chadd return (ENXIO);
387*0f1bf1c2SAdrian Chadd }
388*0f1bf1c2SAdrian Chadd
389*0f1bf1c2SAdrian Chadd #ifndef UISUBCLASS_MOBILE_BROADBAND_INTERFACE_MODEL
390*0f1bf1c2SAdrian Chadd # define UISUBCLASS_MOBILE_BROADBAND_INTERFACE_MODEL 14
391*0f1bf1c2SAdrian Chadd #endif
392*0f1bf1c2SAdrian Chadd if (id->bInterfaceClass == UICLASS_CDC &&
393*0f1bf1c2SAdrian Chadd id->bInterfaceSubClass ==
394*0f1bf1c2SAdrian Chadd UISUBCLASS_MOBILE_BROADBAND_INTERFACE_MODEL &&
395*0f1bf1c2SAdrian Chadd id->bInterfaceProtocol == 0)
396*0f1bf1c2SAdrian Chadd return (BUS_PROBE_SPECIFIC);
397*0f1bf1c2SAdrian Chadd
398*0f1bf1c2SAdrian Chadd return (ENXIO);
399*0f1bf1c2SAdrian Chadd }
400*0f1bf1c2SAdrian Chadd
401*0f1bf1c2SAdrian Chadd static int
umb_attach(device_t dev)402*0f1bf1c2SAdrian Chadd umb_attach(device_t dev)
403*0f1bf1c2SAdrian Chadd {
404*0f1bf1c2SAdrian Chadd struct umb_softc *sc = device_get_softc(dev);
405*0f1bf1c2SAdrian Chadd struct usb_attach_arg *uaa = device_get_ivars(dev);
406*0f1bf1c2SAdrian Chadd struct usb_config config[UMB_N_TRANSFER];
407*0f1bf1c2SAdrian Chadd int v;
408*0f1bf1c2SAdrian Chadd const struct usb_cdc_union_descriptor *ud;
409*0f1bf1c2SAdrian Chadd const struct mbim_descriptor *md;
410*0f1bf1c2SAdrian Chadd int i;
411*0f1bf1c2SAdrian Chadd usb_interface_descriptor_t *id;
412*0f1bf1c2SAdrian Chadd struct usb_interface *iface;
413*0f1bf1c2SAdrian Chadd int data_ifaceno = -1;
414*0f1bf1c2SAdrian Chadd usb_error_t error;
415*0f1bf1c2SAdrian Chadd
416*0f1bf1c2SAdrian Chadd sc->sc_dev = dev;
417*0f1bf1c2SAdrian Chadd sc->sc_udev = uaa->device;
418*0f1bf1c2SAdrian Chadd
419*0f1bf1c2SAdrian Chadd memcpy(config, umb_config, sizeof (config));
420*0f1bf1c2SAdrian Chadd
421*0f1bf1c2SAdrian Chadd device_set_usb_desc(dev);
422*0f1bf1c2SAdrian Chadd
423*0f1bf1c2SAdrian Chadd sc->sc_ctrl_ifaceno = uaa->info.bIfaceNum;
424*0f1bf1c2SAdrian Chadd
425*0f1bf1c2SAdrian Chadd mtx_init(&sc->sc_mutex, device_get_nameunit(dev), NULL, MTX_DEF);
426*0f1bf1c2SAdrian Chadd
427*0f1bf1c2SAdrian Chadd /*
428*0f1bf1c2SAdrian Chadd * Some MBIM hardware does not provide the mandatory CDC Union
429*0f1bf1c2SAdrian Chadd * Descriptor, so we also look at matching Interface
430*0f1bf1c2SAdrian Chadd * Association Descriptors to find out the MBIM Data Interface
431*0f1bf1c2SAdrian Chadd * number.
432*0f1bf1c2SAdrian Chadd */
433*0f1bf1c2SAdrian Chadd sc->sc_ver_maj = sc->sc_ver_min = -1;
434*0f1bf1c2SAdrian Chadd sc->sc_maxpktlen = MBIM_MAXSEGSZ_MINVAL;
435*0f1bf1c2SAdrian Chadd id = usbd_get_interface_descriptor(uaa->iface);
436*0f1bf1c2SAdrian Chadd
437*0f1bf1c2SAdrian Chadd ud = usbd_find_descriptor(sc->sc_udev, id, uaa->info.bIfaceIndex,
438*0f1bf1c2SAdrian Chadd UDESC_CS_INTERFACE, 0xff, UDESCSUB_CDC_UNION, 0xff);
439*0f1bf1c2SAdrian Chadd if (ud != NULL) {
440*0f1bf1c2SAdrian Chadd data_ifaceno = ud->bSlaveInterface[0];
441*0f1bf1c2SAdrian Chadd }
442*0f1bf1c2SAdrian Chadd
443*0f1bf1c2SAdrian Chadd md = usbd_find_descriptor(sc->sc_udev, id, uaa->info.bIfaceIndex,
444*0f1bf1c2SAdrian Chadd UDESC_CS_INTERFACE, 0xff, UDESCSUB_MBIM, 0xff);
445*0f1bf1c2SAdrian Chadd if (md != NULL) {
446*0f1bf1c2SAdrian Chadd v = UGETW(md->bcdMBIMVersion);
447*0f1bf1c2SAdrian Chadd sc->sc_ver_maj = MBIM_VER_MAJOR(v);
448*0f1bf1c2SAdrian Chadd sc->sc_ver_min = MBIM_VER_MINOR(v);
449*0f1bf1c2SAdrian Chadd sc->sc_ctrl_len = UGETW(md->wMaxControlMessage);
450*0f1bf1c2SAdrian Chadd /* Never trust a USB device! Could try to exploit us */
451*0f1bf1c2SAdrian Chadd if (sc->sc_ctrl_len < MBIM_CTRLMSG_MINLEN ||
452*0f1bf1c2SAdrian Chadd sc->sc_ctrl_len > MBIM_CTRLMSG_MAXLEN) {
453*0f1bf1c2SAdrian Chadd DPRINTF("control message len %d out of "
454*0f1bf1c2SAdrian Chadd "bounds [%d .. %d]\n",
455*0f1bf1c2SAdrian Chadd sc->sc_ctrl_len, MBIM_CTRLMSG_MINLEN,
456*0f1bf1c2SAdrian Chadd MBIM_CTRLMSG_MAXLEN);
457*0f1bf1c2SAdrian Chadd /* continue anyway */
458*0f1bf1c2SAdrian Chadd }
459*0f1bf1c2SAdrian Chadd sc->sc_maxpktlen = UGETW(md->wMaxSegmentSize);
460*0f1bf1c2SAdrian Chadd DPRINTFN(2, "ctrl_len=%d, maxpktlen=%d, cap=0x%x\n",
461*0f1bf1c2SAdrian Chadd sc->sc_ctrl_len, sc->sc_maxpktlen,
462*0f1bf1c2SAdrian Chadd md->bmNetworkCapabilities);
463*0f1bf1c2SAdrian Chadd }
464*0f1bf1c2SAdrian Chadd if (sc->sc_ver_maj < 0) {
465*0f1bf1c2SAdrian Chadd device_printf(dev, "error: missing MBIM descriptor\n");
466*0f1bf1c2SAdrian Chadd goto fail;
467*0f1bf1c2SAdrian Chadd }
468*0f1bf1c2SAdrian Chadd
469*0f1bf1c2SAdrian Chadd device_printf(dev, "version %d.%d\n", sc->sc_ver_maj,
470*0f1bf1c2SAdrian Chadd sc->sc_ver_min);
471*0f1bf1c2SAdrian Chadd
472*0f1bf1c2SAdrian Chadd if (usbd_lookup_id_by_uaa(umb_fccauth_devs, sizeof (umb_fccauth_devs),
473*0f1bf1c2SAdrian Chadd uaa)) {
474*0f1bf1c2SAdrian Chadd sc->sc_flags |= UMBFLG_FCC_AUTH_REQUIRED;
475*0f1bf1c2SAdrian Chadd sc->sc_cid = -1;
476*0f1bf1c2SAdrian Chadd }
477*0f1bf1c2SAdrian Chadd
478*0f1bf1c2SAdrian Chadd for (i = 0; i < sc->sc_udev->ifaces_max; i++) {
479*0f1bf1c2SAdrian Chadd iface = usbd_get_iface(sc->sc_udev, i);
480*0f1bf1c2SAdrian Chadd id = usbd_get_interface_descriptor(iface);
481*0f1bf1c2SAdrian Chadd if (id == NULL)
482*0f1bf1c2SAdrian Chadd break;
483*0f1bf1c2SAdrian Chadd
484*0f1bf1c2SAdrian Chadd if (id->bInterfaceNumber == data_ifaceno) {
485*0f1bf1c2SAdrian Chadd sc->sc_data_iface = iface;
486*0f1bf1c2SAdrian Chadd sc->sc_ifaces_index[0] = i;
487*0f1bf1c2SAdrian Chadd sc->sc_ifaces_index[1] = uaa->info.bIfaceIndex;
488*0f1bf1c2SAdrian Chadd break;
489*0f1bf1c2SAdrian Chadd }
490*0f1bf1c2SAdrian Chadd }
491*0f1bf1c2SAdrian Chadd if (sc->sc_data_iface == NULL) {
492*0f1bf1c2SAdrian Chadd device_printf(dev, "error: no data interface found\n");
493*0f1bf1c2SAdrian Chadd goto fail;
494*0f1bf1c2SAdrian Chadd }
495*0f1bf1c2SAdrian Chadd
496*0f1bf1c2SAdrian Chadd /*
497*0f1bf1c2SAdrian Chadd * If this is a combined NCM/MBIM function, switch to
498*0f1bf1c2SAdrian Chadd * alternate setting one to enable MBIM.
499*0f1bf1c2SAdrian Chadd */
500*0f1bf1c2SAdrian Chadd id = usbd_get_interface_descriptor(uaa->iface);
501*0f1bf1c2SAdrian Chadd if (id != NULL && id->bInterfaceClass == UICLASS_CDC &&
502*0f1bf1c2SAdrian Chadd id->bInterfaceSubClass == UISUBCLASS_NETWORK_CONTROL_MODEL) {
503*0f1bf1c2SAdrian Chadd device_printf(sc->sc_dev, "combined NCM/MBIM\n");
504*0f1bf1c2SAdrian Chadd error = usbd_req_set_alt_interface_no(sc->sc_udev,
505*0f1bf1c2SAdrian Chadd NULL, uaa->info.bIfaceIndex, 1);
506*0f1bf1c2SAdrian Chadd if (error != USB_ERR_NORMAL_COMPLETION) {
507*0f1bf1c2SAdrian Chadd device_printf(dev, "error: Could not switch to"
508*0f1bf1c2SAdrian Chadd " alternate setting for MBIM\n");
509*0f1bf1c2SAdrian Chadd goto fail;
510*0f1bf1c2SAdrian Chadd }
511*0f1bf1c2SAdrian Chadd sc->sc_ifaces_index[1] = uaa->info.bIfaceIndex + 1;
512*0f1bf1c2SAdrian Chadd }
513*0f1bf1c2SAdrian Chadd
514*0f1bf1c2SAdrian Chadd if (usb_proc_create(&sc->sc_taskqueue, &sc->sc_mutex,
515*0f1bf1c2SAdrian Chadd device_get_nameunit(sc->sc_dev),
516*0f1bf1c2SAdrian Chadd USB_PRI_MED) != 0)
517*0f1bf1c2SAdrian Chadd goto fail;
518*0f1bf1c2SAdrian Chadd
519*0f1bf1c2SAdrian Chadd DPRINTFN(2, "ctrl-ifno#%d: data-ifno#%d\n", sc->sc_ctrl_ifaceno,
520*0f1bf1c2SAdrian Chadd data_ifaceno);
521*0f1bf1c2SAdrian Chadd
522*0f1bf1c2SAdrian Chadd usb_callout_init_mtx(&sc->sc_statechg_timer, &sc->sc_mutex, 0);
523*0f1bf1c2SAdrian Chadd
524*0f1bf1c2SAdrian Chadd umb_ncm_setup(sc, config);
525*0f1bf1c2SAdrian Chadd DPRINTFN(2, "%s: rx/tx size %d/%d\n", DEVNAM(sc),
526*0f1bf1c2SAdrian Chadd sc->sc_rx_bufsz, sc->sc_tx_bufsz);
527*0f1bf1c2SAdrian Chadd
528*0f1bf1c2SAdrian Chadd sc->sc_rx_buf = malloc(sc->sc_rx_bufsz, M_DEVBUF, M_WAITOK);
529*0f1bf1c2SAdrian Chadd sc->sc_tx_buf = malloc(sc->sc_tx_bufsz, M_DEVBUF, M_WAITOK);
530*0f1bf1c2SAdrian Chadd
531*0f1bf1c2SAdrian Chadd for (i = 0; i != 32; i++) {
532*0f1bf1c2SAdrian Chadd error = usbd_set_alt_interface_index(sc->sc_udev,
533*0f1bf1c2SAdrian Chadd sc->sc_ifaces_index[0], i);
534*0f1bf1c2SAdrian Chadd if (error)
535*0f1bf1c2SAdrian Chadd break;
536*0f1bf1c2SAdrian Chadd
537*0f1bf1c2SAdrian Chadd error = usbd_transfer_setup(sc->sc_udev, sc->sc_ifaces_index,
538*0f1bf1c2SAdrian Chadd sc->sc_xfer, config, UMB_N_TRANSFER,
539*0f1bf1c2SAdrian Chadd sc, &sc->sc_mutex);
540*0f1bf1c2SAdrian Chadd if (error == USB_ERR_NORMAL_COMPLETION)
541*0f1bf1c2SAdrian Chadd break;
542*0f1bf1c2SAdrian Chadd }
543*0f1bf1c2SAdrian Chadd if (error || (i == 32)) {
544*0f1bf1c2SAdrian Chadd device_printf(sc->sc_dev, "error: failed to setup xfers\n");
545*0f1bf1c2SAdrian Chadd goto fail;
546*0f1bf1c2SAdrian Chadd }
547*0f1bf1c2SAdrian Chadd
548*0f1bf1c2SAdrian Chadd sc->sc_resp_buf = malloc(sc->sc_ctrl_len, M_DEVBUF, M_WAITOK);
549*0f1bf1c2SAdrian Chadd sc->sc_ctrl_msg = malloc(sc->sc_ctrl_len, M_DEVBUF, M_WAITOK);
550*0f1bf1c2SAdrian Chadd
551*0f1bf1c2SAdrian Chadd sc->sc_info.regstate = MBIM_REGSTATE_UNKNOWN;
552*0f1bf1c2SAdrian Chadd sc->sc_info.pin_attempts_left = UMB_VALUE_UNKNOWN;
553*0f1bf1c2SAdrian Chadd sc->sc_info.rssi = UMB_VALUE_UNKNOWN;
554*0f1bf1c2SAdrian Chadd sc->sc_info.ber = UMB_VALUE_UNKNOWN;
555*0f1bf1c2SAdrian Chadd
556*0f1bf1c2SAdrian Chadd /* defer attaching the interface */
557*0f1bf1c2SAdrian Chadd mtx_lock(&sc->sc_mutex);
558*0f1bf1c2SAdrian Chadd umb_add_task(sc, umb_attach_task,
559*0f1bf1c2SAdrian Chadd &sc->sc_proc_attach_task[0].hdr,
560*0f1bf1c2SAdrian Chadd &sc->sc_proc_attach_task[1].hdr, 0);
561*0f1bf1c2SAdrian Chadd mtx_unlock(&sc->sc_mutex);
562*0f1bf1c2SAdrian Chadd
563*0f1bf1c2SAdrian Chadd return (0);
564*0f1bf1c2SAdrian Chadd
565*0f1bf1c2SAdrian Chadd fail:
566*0f1bf1c2SAdrian Chadd umb_detach(sc->sc_dev);
567*0f1bf1c2SAdrian Chadd return (ENXIO);
568*0f1bf1c2SAdrian Chadd }
569*0f1bf1c2SAdrian Chadd
570*0f1bf1c2SAdrian Chadd static void
umb_attach_task(struct usb_proc_msg * msg)571*0f1bf1c2SAdrian Chadd umb_attach_task(struct usb_proc_msg *msg)
572*0f1bf1c2SAdrian Chadd {
573*0f1bf1c2SAdrian Chadd struct umb_task *task = (struct umb_task *)msg;
574*0f1bf1c2SAdrian Chadd struct umb_softc *sc = task->sc;
575*0f1bf1c2SAdrian Chadd if_t ifp;
576*0f1bf1c2SAdrian Chadd
577*0f1bf1c2SAdrian Chadd mtx_unlock(&sc->sc_mutex);
578*0f1bf1c2SAdrian Chadd
579*0f1bf1c2SAdrian Chadd CURVNET_SET_QUIET(vnet0);
580*0f1bf1c2SAdrian Chadd
581*0f1bf1c2SAdrian Chadd /* initialize the interface */
582*0f1bf1c2SAdrian Chadd sc->sc_if = ifp = if_alloc(IFT_MBIM);
583*0f1bf1c2SAdrian Chadd if_initname(ifp, "umb", device_get_unit(sc->sc_dev));
584*0f1bf1c2SAdrian Chadd
585*0f1bf1c2SAdrian Chadd if_setsoftc(ifp, sc);
586*0f1bf1c2SAdrian Chadd if_setflags(ifp, IFF_SIMPLEX | IFF_MULTICAST | IFF_POINTOPOINT);
587*0f1bf1c2SAdrian Chadd if_setioctlfn(ifp, umb_ioctl);
588*0f1bf1c2SAdrian Chadd #ifdef DEV_NETMAP
589*0f1bf1c2SAdrian Chadd if_setinputfn(ifp, umb_input);
590*0f1bf1c2SAdrian Chadd #endif
591*0f1bf1c2SAdrian Chadd if_setoutputfn(ifp, umb_output);
592*0f1bf1c2SAdrian Chadd if_setstartfn(ifp, umb_start);
593*0f1bf1c2SAdrian Chadd if_setinitfn(ifp, umb_init);
594*0f1bf1c2SAdrian Chadd
595*0f1bf1c2SAdrian Chadd #if 0
596*0f1bf1c2SAdrian Chadd if_setwatchdog(ifp, umb_watchdog);
597*0f1bf1c2SAdrian Chadd #endif
598*0f1bf1c2SAdrian Chadd if_link_state_change(ifp, LINK_STATE_DOWN);
599*0f1bf1c2SAdrian Chadd ifmedia_init(&sc->sc_im, 0, umb_mediachange, umb_mediastatus);
600*0f1bf1c2SAdrian Chadd ifmedia_add(&sc->sc_im, IFM_NONE | IFM_AUTO, 0, NULL);
601*0f1bf1c2SAdrian Chadd
602*0f1bf1c2SAdrian Chadd if_setifheaderlen(ifp, sizeof (struct ncm_header16) +
603*0f1bf1c2SAdrian Chadd sizeof (struct ncm_pointer16)); /* XXX - IFAPI */
604*0f1bf1c2SAdrian Chadd /* XXX hard-coded atm */
605*0f1bf1c2SAdrian Chadd if_setmtu(ifp, MIN(2048, sc->sc_maxpktlen));
606*0f1bf1c2SAdrian Chadd if_setsendqlen(ifp, ifqmaxlen);
607*0f1bf1c2SAdrian Chadd if_setsendqready(ifp);
608*0f1bf1c2SAdrian Chadd
609*0f1bf1c2SAdrian Chadd /* attach the interface */
610*0f1bf1c2SAdrian Chadd if_attach(ifp);
611*0f1bf1c2SAdrian Chadd bpfattach(ifp, DLT_RAW, 0);
612*0f1bf1c2SAdrian Chadd
613*0f1bf1c2SAdrian Chadd sc->sc_attached = 1;
614*0f1bf1c2SAdrian Chadd
615*0f1bf1c2SAdrian Chadd CURVNET_RESTORE();
616*0f1bf1c2SAdrian Chadd
617*0f1bf1c2SAdrian Chadd umb_init(sc);
618*0f1bf1c2SAdrian Chadd mtx_lock(&sc->sc_mutex);
619*0f1bf1c2SAdrian Chadd }
620*0f1bf1c2SAdrian Chadd
621*0f1bf1c2SAdrian Chadd static int
umb_detach(device_t dev)622*0f1bf1c2SAdrian Chadd umb_detach(device_t dev)
623*0f1bf1c2SAdrian Chadd {
624*0f1bf1c2SAdrian Chadd struct umb_softc *sc = device_get_softc(dev);
625*0f1bf1c2SAdrian Chadd if_t ifp = GET_IFP(sc);
626*0f1bf1c2SAdrian Chadd
627*0f1bf1c2SAdrian Chadd usb_proc_drain(&sc->sc_taskqueue);
628*0f1bf1c2SAdrian Chadd
629*0f1bf1c2SAdrian Chadd mtx_lock(&sc->sc_mutex);
630*0f1bf1c2SAdrian Chadd if (ifp != NULL && (if_getdrvflags(ifp) & IFF_DRV_RUNNING))
631*0f1bf1c2SAdrian Chadd umb_down(sc, 1);
632*0f1bf1c2SAdrian Chadd umb_close(sc);
633*0f1bf1c2SAdrian Chadd mtx_unlock(&sc->sc_mutex);
634*0f1bf1c2SAdrian Chadd
635*0f1bf1c2SAdrian Chadd usbd_transfer_unsetup(sc->sc_xfer, UMB_N_TRANSFER);
636*0f1bf1c2SAdrian Chadd
637*0f1bf1c2SAdrian Chadd free(sc->sc_tx_buf, M_DEVBUF);
638*0f1bf1c2SAdrian Chadd free(sc->sc_rx_buf, M_DEVBUF);
639*0f1bf1c2SAdrian Chadd
640*0f1bf1c2SAdrian Chadd usb_callout_drain(&sc->sc_statechg_timer);
641*0f1bf1c2SAdrian Chadd
642*0f1bf1c2SAdrian Chadd usb_proc_free(&sc->sc_taskqueue);
643*0f1bf1c2SAdrian Chadd
644*0f1bf1c2SAdrian Chadd mtx_destroy(&sc->sc_mutex);
645*0f1bf1c2SAdrian Chadd
646*0f1bf1c2SAdrian Chadd free(sc->sc_ctrl_msg, M_DEVBUF);
647*0f1bf1c2SAdrian Chadd free(sc->sc_resp_buf, M_DEVBUF);
648*0f1bf1c2SAdrian Chadd
649*0f1bf1c2SAdrian Chadd if (ifp != NULL && if_getsoftc(ifp)) {
650*0f1bf1c2SAdrian Chadd ifmedia_removeall(&sc->sc_im);
651*0f1bf1c2SAdrian Chadd }
652*0f1bf1c2SAdrian Chadd if (sc->sc_attached) {
653*0f1bf1c2SAdrian Chadd bpfdetach(ifp);
654*0f1bf1c2SAdrian Chadd if_detach(ifp);
655*0f1bf1c2SAdrian Chadd if_free(ifp);
656*0f1bf1c2SAdrian Chadd sc->sc_if = NULL;
657*0f1bf1c2SAdrian Chadd }
658*0f1bf1c2SAdrian Chadd
659*0f1bf1c2SAdrian Chadd return 0;
660*0f1bf1c2SAdrian Chadd }
661*0f1bf1c2SAdrian Chadd
662*0f1bf1c2SAdrian Chadd static void
umb_ncm_setup(struct umb_softc * sc,struct usb_config * config)663*0f1bf1c2SAdrian Chadd umb_ncm_setup(struct umb_softc *sc, struct usb_config * config)
664*0f1bf1c2SAdrian Chadd {
665*0f1bf1c2SAdrian Chadd usb_device_request_t req;
666*0f1bf1c2SAdrian Chadd struct ncm_ntb_parameters np;
667*0f1bf1c2SAdrian Chadd usb_error_t error;
668*0f1bf1c2SAdrian Chadd
669*0f1bf1c2SAdrian Chadd /* Query NTB tranfers sizes */
670*0f1bf1c2SAdrian Chadd req.bmRequestType = UT_READ_CLASS_INTERFACE;
671*0f1bf1c2SAdrian Chadd req.bRequest = NCM_GET_NTB_PARAMETERS;
672*0f1bf1c2SAdrian Chadd USETW(req.wValue, 0);
673*0f1bf1c2SAdrian Chadd USETW(req.wIndex, sc->sc_ctrl_ifaceno);
674*0f1bf1c2SAdrian Chadd USETW(req.wLength, sizeof (np));
675*0f1bf1c2SAdrian Chadd mtx_lock(&sc->sc_mutex);
676*0f1bf1c2SAdrian Chadd error = usbd_do_request(sc->sc_udev, &sc->sc_mutex, &req, &np);
677*0f1bf1c2SAdrian Chadd mtx_unlock(&sc->sc_mutex);
678*0f1bf1c2SAdrian Chadd if (error == USB_ERR_NORMAL_COMPLETION &&
679*0f1bf1c2SAdrian Chadd UGETW(np.wLength) == sizeof (np)) {
680*0f1bf1c2SAdrian Chadd config[UMB_BULK_RX].bufsize = UGETDW(np.dwNtbInMaxSize);
681*0f1bf1c2SAdrian Chadd config[UMB_BULK_TX].bufsize = UGETDW(np.dwNtbOutMaxSize);
682*0f1bf1c2SAdrian Chadd }
683*0f1bf1c2SAdrian Chadd sc->sc_rx_bufsz = config[UMB_BULK_RX].bufsize;
684*0f1bf1c2SAdrian Chadd sc->sc_tx_bufsz = config[UMB_BULK_TX].bufsize;
685*0f1bf1c2SAdrian Chadd }
686*0f1bf1c2SAdrian Chadd
687*0f1bf1c2SAdrian Chadd static int
umb_handle_request(device_t dev,const void * preq,void ** pptr,uint16_t * plen,uint16_t offset,uint8_t * pstate)688*0f1bf1c2SAdrian Chadd umb_handle_request(device_t dev,
689*0f1bf1c2SAdrian Chadd const void *preq, void **pptr, uint16_t *plen,
690*0f1bf1c2SAdrian Chadd uint16_t offset, uint8_t *pstate)
691*0f1bf1c2SAdrian Chadd {
692*0f1bf1c2SAdrian Chadd /* FIXME really implement */
693*0f1bf1c2SAdrian Chadd
694*0f1bf1c2SAdrian Chadd return (ENXIO);
695*0f1bf1c2SAdrian Chadd }
696*0f1bf1c2SAdrian Chadd
697*0f1bf1c2SAdrian Chadd static int
umb_suspend(device_t dev)698*0f1bf1c2SAdrian Chadd umb_suspend(device_t dev)
699*0f1bf1c2SAdrian Chadd {
700*0f1bf1c2SAdrian Chadd device_printf(dev, "Suspending\n");
701*0f1bf1c2SAdrian Chadd return (0);
702*0f1bf1c2SAdrian Chadd }
703*0f1bf1c2SAdrian Chadd
704*0f1bf1c2SAdrian Chadd static int
umb_resume(device_t dev)705*0f1bf1c2SAdrian Chadd umb_resume(device_t dev)
706*0f1bf1c2SAdrian Chadd {
707*0f1bf1c2SAdrian Chadd device_printf(dev, "Resuming\n");
708*0f1bf1c2SAdrian Chadd return (0);
709*0f1bf1c2SAdrian Chadd }
710*0f1bf1c2SAdrian Chadd
711*0f1bf1c2SAdrian Chadd static int
umb_deactivate(device_t dev)712*0f1bf1c2SAdrian Chadd umb_deactivate(device_t dev)
713*0f1bf1c2SAdrian Chadd {
714*0f1bf1c2SAdrian Chadd struct umb_softc *sc = device_get_softc(dev);
715*0f1bf1c2SAdrian Chadd if_t ifp = GET_IFP(sc);
716*0f1bf1c2SAdrian Chadd
717*0f1bf1c2SAdrian Chadd if (ifp != NULL) {
718*0f1bf1c2SAdrian Chadd if_dead(ifp);
719*0f1bf1c2SAdrian Chadd }
720*0f1bf1c2SAdrian Chadd sc->sc_dying = 1;
721*0f1bf1c2SAdrian Chadd return 0;
722*0f1bf1c2SAdrian Chadd }
723*0f1bf1c2SAdrian Chadd
724*0f1bf1c2SAdrian Chadd static void
umb_close_bulkpipes(struct umb_softc * sc)725*0f1bf1c2SAdrian Chadd umb_close_bulkpipes(struct umb_softc *sc)
726*0f1bf1c2SAdrian Chadd {
727*0f1bf1c2SAdrian Chadd if_t ifp = GET_IFP(sc);
728*0f1bf1c2SAdrian Chadd
729*0f1bf1c2SAdrian Chadd if_setdrvflagbits(ifp, 0, (IFF_DRV_RUNNING | IFF_DRV_OACTIVE));
730*0f1bf1c2SAdrian Chadd
731*0f1bf1c2SAdrian Chadd umb_rxflush(sc);
732*0f1bf1c2SAdrian Chadd umb_txflush(sc);
733*0f1bf1c2SAdrian Chadd
734*0f1bf1c2SAdrian Chadd usbd_transfer_stop(sc->sc_xfer[UMB_BULK_RX]);
735*0f1bf1c2SAdrian Chadd usbd_transfer_stop(sc->sc_xfer[UMB_BULK_TX]);
736*0f1bf1c2SAdrian Chadd }
737*0f1bf1c2SAdrian Chadd
738*0f1bf1c2SAdrian Chadd static int
umb_ioctl(if_t ifp,u_long cmd,caddr_t data)739*0f1bf1c2SAdrian Chadd umb_ioctl(if_t ifp, u_long cmd, caddr_t data)
740*0f1bf1c2SAdrian Chadd {
741*0f1bf1c2SAdrian Chadd struct umb_softc *sc = if_getsoftc(ifp);
742*0f1bf1c2SAdrian Chadd struct in_ifaddr *ia = (struct in_ifaddr *)data;
743*0f1bf1c2SAdrian Chadd struct ifreq *ifr = (struct ifreq *)data;
744*0f1bf1c2SAdrian Chadd int error = 0;
745*0f1bf1c2SAdrian Chadd struct umb_parameter mp;
746*0f1bf1c2SAdrian Chadd
747*0f1bf1c2SAdrian Chadd if (sc->sc_dying)
748*0f1bf1c2SAdrian Chadd return EIO;
749*0f1bf1c2SAdrian Chadd
750*0f1bf1c2SAdrian Chadd switch (cmd) {
751*0f1bf1c2SAdrian Chadd case SIOCSIFADDR:
752*0f1bf1c2SAdrian Chadd switch (ia->ia_ifa.ifa_addr->sa_family) {
753*0f1bf1c2SAdrian Chadd case AF_INET:
754*0f1bf1c2SAdrian Chadd break;
755*0f1bf1c2SAdrian Chadd #ifdef INET6
756*0f1bf1c2SAdrian Chadd case AF_INET6:
757*0f1bf1c2SAdrian Chadd break;
758*0f1bf1c2SAdrian Chadd #endif /* INET6 */
759*0f1bf1c2SAdrian Chadd default:
760*0f1bf1c2SAdrian Chadd error = EAFNOSUPPORT;
761*0f1bf1c2SAdrian Chadd break;
762*0f1bf1c2SAdrian Chadd }
763*0f1bf1c2SAdrian Chadd break;
764*0f1bf1c2SAdrian Chadd case SIOCSIFFLAGS:
765*0f1bf1c2SAdrian Chadd mtx_lock(&sc->sc_mutex);
766*0f1bf1c2SAdrian Chadd umb_add_task(sc, umb_state_task,
767*0f1bf1c2SAdrian Chadd &sc->sc_proc_state_task[0].hdr,
768*0f1bf1c2SAdrian Chadd &sc->sc_proc_state_task[1].hdr, 1);
769*0f1bf1c2SAdrian Chadd mtx_unlock(&sc->sc_mutex);
770*0f1bf1c2SAdrian Chadd break;
771*0f1bf1c2SAdrian Chadd case SIOCGUMBINFO:
772*0f1bf1c2SAdrian Chadd error = copyout(&sc->sc_info, ifr->ifr_ifru.ifru_data,
773*0f1bf1c2SAdrian Chadd sizeof (sc->sc_info));
774*0f1bf1c2SAdrian Chadd break;
775*0f1bf1c2SAdrian Chadd case SIOCSUMBPARAM:
776*0f1bf1c2SAdrian Chadd error = priv_check(curthread, PRIV_NET_SETIFPHYS);
777*0f1bf1c2SAdrian Chadd if (error)
778*0f1bf1c2SAdrian Chadd break;
779*0f1bf1c2SAdrian Chadd
780*0f1bf1c2SAdrian Chadd if ((error = copyin(ifr->ifr_ifru.ifru_data, &mp, sizeof (mp))) != 0)
781*0f1bf1c2SAdrian Chadd break;
782*0f1bf1c2SAdrian Chadd
783*0f1bf1c2SAdrian Chadd if ((error = umb_setpin(sc, mp.op, mp.is_puk, mp.pin, mp.pinlen,
784*0f1bf1c2SAdrian Chadd mp.newpin, mp.newpinlen)) != 0)
785*0f1bf1c2SAdrian Chadd break;
786*0f1bf1c2SAdrian Chadd
787*0f1bf1c2SAdrian Chadd if (mp.apnlen < 0 || mp.apnlen > sizeof (sc->sc_info.apn)) {
788*0f1bf1c2SAdrian Chadd error = EINVAL;
789*0f1bf1c2SAdrian Chadd break;
790*0f1bf1c2SAdrian Chadd }
791*0f1bf1c2SAdrian Chadd sc->sc_roaming = mp.roaming ? 1 : 0;
792*0f1bf1c2SAdrian Chadd memset(sc->sc_info.apn, 0, sizeof (sc->sc_info.apn));
793*0f1bf1c2SAdrian Chadd memcpy(sc->sc_info.apn, mp.apn, mp.apnlen);
794*0f1bf1c2SAdrian Chadd sc->sc_info.apnlen = mp.apnlen;
795*0f1bf1c2SAdrian Chadd memset(sc->sc_info.username, 0, sizeof (sc->sc_info.username));
796*0f1bf1c2SAdrian Chadd memcpy(sc->sc_info.username, mp.username, mp.usernamelen);
797*0f1bf1c2SAdrian Chadd sc->sc_info.usernamelen = mp.usernamelen;
798*0f1bf1c2SAdrian Chadd memset(sc->sc_info.password, 0, sizeof (sc->sc_info.password));
799*0f1bf1c2SAdrian Chadd memcpy(sc->sc_info.password, mp.password, mp.passwordlen);
800*0f1bf1c2SAdrian Chadd sc->sc_info.passwordlen = mp.passwordlen;
801*0f1bf1c2SAdrian Chadd sc->sc_info.preferredclasses = mp.preferredclasses;
802*0f1bf1c2SAdrian Chadd umb_setdataclass(sc);
803*0f1bf1c2SAdrian Chadd break;
804*0f1bf1c2SAdrian Chadd case SIOCGUMBPARAM:
805*0f1bf1c2SAdrian Chadd memset(&mp, 0, sizeof (mp));
806*0f1bf1c2SAdrian Chadd memcpy(mp.apn, sc->sc_info.apn, sc->sc_info.apnlen);
807*0f1bf1c2SAdrian Chadd mp.apnlen = sc->sc_info.apnlen;
808*0f1bf1c2SAdrian Chadd mp.roaming = sc->sc_roaming;
809*0f1bf1c2SAdrian Chadd mp.preferredclasses = sc->sc_info.preferredclasses;
810*0f1bf1c2SAdrian Chadd error = copyout(&mp, ifr->ifr_ifru.ifru_data, sizeof (mp));
811*0f1bf1c2SAdrian Chadd break;
812*0f1bf1c2SAdrian Chadd case SIOCSIFMTU:
813*0f1bf1c2SAdrian Chadd /* Does this include the NCM headers and tail? */
814*0f1bf1c2SAdrian Chadd if (ifr->ifr_mtu > if_getmtu(ifp)) {
815*0f1bf1c2SAdrian Chadd error = EINVAL;
816*0f1bf1c2SAdrian Chadd break;
817*0f1bf1c2SAdrian Chadd }
818*0f1bf1c2SAdrian Chadd if_setmtu(ifp, ifr->ifr_mtu);
819*0f1bf1c2SAdrian Chadd break;
820*0f1bf1c2SAdrian Chadd case SIOCAIFADDR:
821*0f1bf1c2SAdrian Chadd case SIOCSIFDSTADDR:
822*0f1bf1c2SAdrian Chadd case SIOCADDMULTI:
823*0f1bf1c2SAdrian Chadd case SIOCDELMULTI:
824*0f1bf1c2SAdrian Chadd break;
825*0f1bf1c2SAdrian Chadd case SIOCGIFMEDIA:
826*0f1bf1c2SAdrian Chadd error = ifmedia_ioctl(ifp, ifr, &sc->sc_im, cmd);
827*0f1bf1c2SAdrian Chadd break;
828*0f1bf1c2SAdrian Chadd default:
829*0f1bf1c2SAdrian Chadd error = EINVAL;
830*0f1bf1c2SAdrian Chadd break;
831*0f1bf1c2SAdrian Chadd }
832*0f1bf1c2SAdrian Chadd return (error);
833*0f1bf1c2SAdrian Chadd }
834*0f1bf1c2SAdrian Chadd
835*0f1bf1c2SAdrian Chadd static void
umb_init(void * arg)836*0f1bf1c2SAdrian Chadd umb_init(void *arg)
837*0f1bf1c2SAdrian Chadd {
838*0f1bf1c2SAdrian Chadd struct umb_softc *sc = arg;
839*0f1bf1c2SAdrian Chadd
840*0f1bf1c2SAdrian Chadd mtx_lock(&sc->sc_mutex);
841*0f1bf1c2SAdrian Chadd umb_add_task(sc, umb_start_task,
842*0f1bf1c2SAdrian Chadd &sc->sc_proc_start_task[0].hdr,
843*0f1bf1c2SAdrian Chadd &sc->sc_proc_start_task[1].hdr, 0);
844*0f1bf1c2SAdrian Chadd mtx_unlock(&sc->sc_mutex);
845*0f1bf1c2SAdrian Chadd }
846*0f1bf1c2SAdrian Chadd
847*0f1bf1c2SAdrian Chadd static void
umb_input(if_t ifp,struct mbuf * m)848*0f1bf1c2SAdrian Chadd umb_input(if_t ifp, struct mbuf *m)
849*0f1bf1c2SAdrian Chadd {
850*0f1bf1c2SAdrian Chadd struct mbuf *mn;
851*0f1bf1c2SAdrian Chadd struct epoch_tracker et;
852*0f1bf1c2SAdrian Chadd
853*0f1bf1c2SAdrian Chadd while (m) {
854*0f1bf1c2SAdrian Chadd mn = m->m_nextpkt;
855*0f1bf1c2SAdrian Chadd m->m_nextpkt = NULL;
856*0f1bf1c2SAdrian Chadd
857*0f1bf1c2SAdrian Chadd NET_EPOCH_ENTER(et);
858*0f1bf1c2SAdrian Chadd BPF_MTAP(ifp, m);
859*0f1bf1c2SAdrian Chadd
860*0f1bf1c2SAdrian Chadd CURVNET_SET_QUIET(if_getvnet(ifp));
861*0f1bf1c2SAdrian Chadd
862*0f1bf1c2SAdrian Chadd netisr_dispatch(NETISR_IP, m);
863*0f1bf1c2SAdrian Chadd m = mn;
864*0f1bf1c2SAdrian Chadd
865*0f1bf1c2SAdrian Chadd CURVNET_RESTORE();
866*0f1bf1c2SAdrian Chadd NET_EPOCH_EXIT(et);
867*0f1bf1c2SAdrian Chadd }
868*0f1bf1c2SAdrian Chadd }
869*0f1bf1c2SAdrian Chadd
870*0f1bf1c2SAdrian Chadd static int
umb_output(if_t ifp,struct mbuf * m,const struct sockaddr * dst,struct route * rtp)871*0f1bf1c2SAdrian Chadd umb_output(if_t ifp, struct mbuf *m, const struct sockaddr *dst,
872*0f1bf1c2SAdrian Chadd struct route *rtp)
873*0f1bf1c2SAdrian Chadd {
874*0f1bf1c2SAdrian Chadd int error;
875*0f1bf1c2SAdrian Chadd
876*0f1bf1c2SAdrian Chadd DPRINTFN(10, "%s: enter\n", __func__);
877*0f1bf1c2SAdrian Chadd
878*0f1bf1c2SAdrian Chadd switch (dst->sa_family) {
879*0f1bf1c2SAdrian Chadd #ifdef INET6
880*0f1bf1c2SAdrian Chadd case AF_INET6:
881*0f1bf1c2SAdrian Chadd /* fall through */
882*0f1bf1c2SAdrian Chadd #endif
883*0f1bf1c2SAdrian Chadd case AF_INET:
884*0f1bf1c2SAdrian Chadd break;
885*0f1bf1c2SAdrian Chadd
886*0f1bf1c2SAdrian Chadd /* silently drop dhclient packets */
887*0f1bf1c2SAdrian Chadd case AF_UNSPEC:
888*0f1bf1c2SAdrian Chadd m_freem(m);
889*0f1bf1c2SAdrian Chadd return (0);
890*0f1bf1c2SAdrian Chadd
891*0f1bf1c2SAdrian Chadd /* drop other packet types */
892*0f1bf1c2SAdrian Chadd default:
893*0f1bf1c2SAdrian Chadd m_freem(m);
894*0f1bf1c2SAdrian Chadd return (EAFNOSUPPORT);
895*0f1bf1c2SAdrian Chadd }
896*0f1bf1c2SAdrian Chadd
897*0f1bf1c2SAdrian Chadd /*
898*0f1bf1c2SAdrian Chadd * Queue message on interface, and start output if interface
899*0f1bf1c2SAdrian Chadd * not yet active.
900*0f1bf1c2SAdrian Chadd */
901*0f1bf1c2SAdrian Chadd error = if_transmit(ifp, m);
902*0f1bf1c2SAdrian Chadd if (error) {
903*0f1bf1c2SAdrian Chadd if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
904*0f1bf1c2SAdrian Chadd return (ENOBUFS);
905*0f1bf1c2SAdrian Chadd }
906*0f1bf1c2SAdrian Chadd
907*0f1bf1c2SAdrian Chadd return (0);
908*0f1bf1c2SAdrian Chadd }
909*0f1bf1c2SAdrian Chadd
910*0f1bf1c2SAdrian Chadd static void
umb_start(if_t ifp)911*0f1bf1c2SAdrian Chadd umb_start(if_t ifp)
912*0f1bf1c2SAdrian Chadd {
913*0f1bf1c2SAdrian Chadd struct umb_softc *sc = if_getsoftc(ifp);
914*0f1bf1c2SAdrian Chadd
915*0f1bf1c2SAdrian Chadd if (sc->sc_dying || !(if_getdrvflags(ifp) & IFF_DRV_RUNNING))
916*0f1bf1c2SAdrian Chadd return;
917*0f1bf1c2SAdrian Chadd
918*0f1bf1c2SAdrian Chadd mtx_lock(&sc->sc_mutex);
919*0f1bf1c2SAdrian Chadd usbd_transfer_start(sc->sc_xfer[UMB_BULK_TX]);
920*0f1bf1c2SAdrian Chadd mtx_unlock(&sc->sc_mutex);
921*0f1bf1c2SAdrian Chadd }
922*0f1bf1c2SAdrian Chadd
923*0f1bf1c2SAdrian Chadd static void
umb_start_task(struct usb_proc_msg * msg)924*0f1bf1c2SAdrian Chadd umb_start_task(struct usb_proc_msg *msg)
925*0f1bf1c2SAdrian Chadd {
926*0f1bf1c2SAdrian Chadd struct umb_task *task = (struct umb_task *)msg;
927*0f1bf1c2SAdrian Chadd struct umb_softc *sc = task->sc;
928*0f1bf1c2SAdrian Chadd if_t ifp = GET_IFP(sc);
929*0f1bf1c2SAdrian Chadd
930*0f1bf1c2SAdrian Chadd DPRINTF("%s()\n", __func__);
931*0f1bf1c2SAdrian Chadd
932*0f1bf1c2SAdrian Chadd mtx_assert(&sc->sc_mutex, MA_OWNED);
933*0f1bf1c2SAdrian Chadd
934*0f1bf1c2SAdrian Chadd if_setdrvflagbits(ifp, IFF_DRV_RUNNING, 0);
935*0f1bf1c2SAdrian Chadd
936*0f1bf1c2SAdrian Chadd /* start interrupt transfer */
937*0f1bf1c2SAdrian Chadd usbd_transfer_start(sc->sc_xfer[UMB_INTR_RX]);
938*0f1bf1c2SAdrian Chadd
939*0f1bf1c2SAdrian Chadd umb_open(sc);
940*0f1bf1c2SAdrian Chadd }
941*0f1bf1c2SAdrian Chadd
942*0f1bf1c2SAdrian Chadd #if 0
943*0f1bf1c2SAdrian Chadd static void
944*0f1bf1c2SAdrian Chadd umb_watchdog(if_t ifp)
945*0f1bf1c2SAdrian Chadd {
946*0f1bf1c2SAdrian Chadd struct umb_softc *sc = if_getsoftc(ifp);
947*0f1bf1c2SAdrian Chadd
948*0f1bf1c2SAdrian Chadd if (sc->sc_dying)
949*0f1bf1c2SAdrian Chadd return;
950*0f1bf1c2SAdrian Chadd
951*0f1bf1c2SAdrian Chadd if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
952*0f1bf1c2SAdrian Chadd device_printf(sc->sc_dev, "watchdog timeout\n");
953*0f1bf1c2SAdrian Chadd usbd_transfer_drain(sc->sc_xfer[UMB_BULK_TX]);
954*0f1bf1c2SAdrian Chadd return;
955*0f1bf1c2SAdrian Chadd }
956*0f1bf1c2SAdrian Chadd #endif
957*0f1bf1c2SAdrian Chadd
958*0f1bf1c2SAdrian Chadd static void
umb_statechg_timeout(void * arg)959*0f1bf1c2SAdrian Chadd umb_statechg_timeout(void *arg)
960*0f1bf1c2SAdrian Chadd {
961*0f1bf1c2SAdrian Chadd struct umb_softc *sc = arg;
962*0f1bf1c2SAdrian Chadd if_t ifp = GET_IFP(sc);
963*0f1bf1c2SAdrian Chadd
964*0f1bf1c2SAdrian Chadd mtx_assert(&sc->sc_mutex, MA_OWNED);
965*0f1bf1c2SAdrian Chadd
966*0f1bf1c2SAdrian Chadd if (sc->sc_info.regstate != MBIM_REGSTATE_ROAMING || sc->sc_roaming)
967*0f1bf1c2SAdrian Chadd if (if_getflags(ifp) & IFF_DEBUG)
968*0f1bf1c2SAdrian Chadd log(LOG_DEBUG, "%s: state change timeout\n",
969*0f1bf1c2SAdrian Chadd DEVNAM(sc));
970*0f1bf1c2SAdrian Chadd
971*0f1bf1c2SAdrian Chadd umb_add_task(sc, umb_state_task,
972*0f1bf1c2SAdrian Chadd &sc->sc_proc_state_task[0].hdr,
973*0f1bf1c2SAdrian Chadd &sc->sc_proc_state_task[1].hdr, 0);
974*0f1bf1c2SAdrian Chadd }
975*0f1bf1c2SAdrian Chadd
976*0f1bf1c2SAdrian Chadd static int
umb_mediachange(if_t ifp)977*0f1bf1c2SAdrian Chadd umb_mediachange(if_t ifp)
978*0f1bf1c2SAdrian Chadd {
979*0f1bf1c2SAdrian Chadd return 0;
980*0f1bf1c2SAdrian Chadd }
981*0f1bf1c2SAdrian Chadd
982*0f1bf1c2SAdrian Chadd static void
umb_mediastatus(if_t ifp,struct ifmediareq * imr)983*0f1bf1c2SAdrian Chadd umb_mediastatus(if_t ifp, struct ifmediareq * imr)
984*0f1bf1c2SAdrian Chadd {
985*0f1bf1c2SAdrian Chadd switch (if_getlinkstate(ifp)) {
986*0f1bf1c2SAdrian Chadd case LINK_STATE_UP:
987*0f1bf1c2SAdrian Chadd imr->ifm_status = IFM_AVALID | IFM_ACTIVE;
988*0f1bf1c2SAdrian Chadd break;
989*0f1bf1c2SAdrian Chadd case LINK_STATE_DOWN:
990*0f1bf1c2SAdrian Chadd imr->ifm_status = IFM_AVALID;
991*0f1bf1c2SAdrian Chadd break;
992*0f1bf1c2SAdrian Chadd default:
993*0f1bf1c2SAdrian Chadd imr->ifm_status = 0;
994*0f1bf1c2SAdrian Chadd break;
995*0f1bf1c2SAdrian Chadd }
996*0f1bf1c2SAdrian Chadd }
997*0f1bf1c2SAdrian Chadd
998*0f1bf1c2SAdrian Chadd static void
umb_add_task(struct umb_softc * sc,usb_proc_callback_t callback,struct usb_proc_msg * t0,struct usb_proc_msg * t1,int sync)999*0f1bf1c2SAdrian Chadd umb_add_task(struct umb_softc *sc, usb_proc_callback_t callback,
1000*0f1bf1c2SAdrian Chadd struct usb_proc_msg *t0, struct usb_proc_msg *t1, int sync)
1001*0f1bf1c2SAdrian Chadd {
1002*0f1bf1c2SAdrian Chadd struct umb_task * task;
1003*0f1bf1c2SAdrian Chadd
1004*0f1bf1c2SAdrian Chadd mtx_assert(&sc->sc_mutex, MA_OWNED);
1005*0f1bf1c2SAdrian Chadd
1006*0f1bf1c2SAdrian Chadd if (usb_proc_is_gone(&sc->sc_taskqueue)) {
1007*0f1bf1c2SAdrian Chadd return;
1008*0f1bf1c2SAdrian Chadd }
1009*0f1bf1c2SAdrian Chadd
1010*0f1bf1c2SAdrian Chadd task = usb_proc_msignal(&sc->sc_taskqueue, t0, t1);
1011*0f1bf1c2SAdrian Chadd
1012*0f1bf1c2SAdrian Chadd task->hdr.pm_callback = callback;
1013*0f1bf1c2SAdrian Chadd task->sc = sc;
1014*0f1bf1c2SAdrian Chadd
1015*0f1bf1c2SAdrian Chadd if (sync) {
1016*0f1bf1c2SAdrian Chadd usb_proc_mwait(&sc->sc_taskqueue, t0, t1);
1017*0f1bf1c2SAdrian Chadd }
1018*0f1bf1c2SAdrian Chadd }
1019*0f1bf1c2SAdrian Chadd
1020*0f1bf1c2SAdrian Chadd static void
umb_newstate(struct umb_softc * sc,enum umb_state newstate,int flags)1021*0f1bf1c2SAdrian Chadd umb_newstate(struct umb_softc *sc, enum umb_state newstate, int flags)
1022*0f1bf1c2SAdrian Chadd {
1023*0f1bf1c2SAdrian Chadd if_t ifp = GET_IFP(sc);
1024*0f1bf1c2SAdrian Chadd
1025*0f1bf1c2SAdrian Chadd if (newstate == sc->sc_state)
1026*0f1bf1c2SAdrian Chadd return;
1027*0f1bf1c2SAdrian Chadd if (((flags & UMB_NS_DONT_DROP) && newstate < sc->sc_state) ||
1028*0f1bf1c2SAdrian Chadd ((flags & UMB_NS_DONT_RAISE) && newstate > sc->sc_state))
1029*0f1bf1c2SAdrian Chadd return;
1030*0f1bf1c2SAdrian Chadd if (if_getflags(ifp) & IFF_DEBUG)
1031*0f1bf1c2SAdrian Chadd log(LOG_DEBUG, "%s: state going %s from '%s' to '%s'\n",
1032*0f1bf1c2SAdrian Chadd DEVNAM(sc), newstate > sc->sc_state ? "up" : "down",
1033*0f1bf1c2SAdrian Chadd umb_istate(sc->sc_state), umb_istate(newstate));
1034*0f1bf1c2SAdrian Chadd sc->sc_state = newstate;
1035*0f1bf1c2SAdrian Chadd umb_add_task(sc, umb_state_task,
1036*0f1bf1c2SAdrian Chadd &sc->sc_proc_state_task[0].hdr,
1037*0f1bf1c2SAdrian Chadd &sc->sc_proc_state_task[1].hdr, 0);
1038*0f1bf1c2SAdrian Chadd }
1039*0f1bf1c2SAdrian Chadd
1040*0f1bf1c2SAdrian Chadd static void
umb_state_task(struct usb_proc_msg * msg)1041*0f1bf1c2SAdrian Chadd umb_state_task(struct usb_proc_msg *msg)
1042*0f1bf1c2SAdrian Chadd {
1043*0f1bf1c2SAdrian Chadd struct umb_task *task = (struct umb_task *)msg;
1044*0f1bf1c2SAdrian Chadd struct umb_softc *sc = task->sc;
1045*0f1bf1c2SAdrian Chadd if_t ifp = GET_IFP(sc);
1046*0f1bf1c2SAdrian Chadd struct ifreq ifr;
1047*0f1bf1c2SAdrian Chadd int state;
1048*0f1bf1c2SAdrian Chadd
1049*0f1bf1c2SAdrian Chadd DPRINTF("%s()\n", __func__);
1050*0f1bf1c2SAdrian Chadd
1051*0f1bf1c2SAdrian Chadd if (sc->sc_info.regstate == MBIM_REGSTATE_ROAMING && !sc->sc_roaming) {
1052*0f1bf1c2SAdrian Chadd /*
1053*0f1bf1c2SAdrian Chadd * Query the registration state until we're with the home
1054*0f1bf1c2SAdrian Chadd * network again.
1055*0f1bf1c2SAdrian Chadd */
1056*0f1bf1c2SAdrian Chadd umb_cmd(sc, MBIM_CID_REGISTER_STATE, MBIM_CMDOP_QRY, NULL, 0);
1057*0f1bf1c2SAdrian Chadd return;
1058*0f1bf1c2SAdrian Chadd }
1059*0f1bf1c2SAdrian Chadd
1060*0f1bf1c2SAdrian Chadd if (if_getflags(ifp) & IFF_UP)
1061*0f1bf1c2SAdrian Chadd umb_up(sc);
1062*0f1bf1c2SAdrian Chadd else
1063*0f1bf1c2SAdrian Chadd umb_down(sc, 0);
1064*0f1bf1c2SAdrian Chadd
1065*0f1bf1c2SAdrian Chadd state = (sc->sc_state == UMB_S_UP) ? LINK_STATE_UP : LINK_STATE_DOWN;
1066*0f1bf1c2SAdrian Chadd if (if_getlinkstate(ifp) != state) {
1067*0f1bf1c2SAdrian Chadd if (if_getflags(ifp) & IFF_DEBUG)
1068*0f1bf1c2SAdrian Chadd log(LOG_DEBUG, "%s: link state changed from %s to %s\n",
1069*0f1bf1c2SAdrian Chadd DEVNAM(sc),
1070*0f1bf1c2SAdrian Chadd (if_getlinkstate(ifp) == LINK_STATE_UP)
1071*0f1bf1c2SAdrian Chadd ? "up" : "down",
1072*0f1bf1c2SAdrian Chadd (state == LINK_STATE_UP) ? "up" : "down");
1073*0f1bf1c2SAdrian Chadd if_link_state_change(ifp, state); /* XXX - IFAPI */
1074*0f1bf1c2SAdrian Chadd if (state != LINK_STATE_UP) {
1075*0f1bf1c2SAdrian Chadd /*
1076*0f1bf1c2SAdrian Chadd * Purge any existing addresses
1077*0f1bf1c2SAdrian Chadd */
1078*0f1bf1c2SAdrian Chadd memset(sc->sc_info.ipv4dns, 0,
1079*0f1bf1c2SAdrian Chadd sizeof (sc->sc_info.ipv4dns));
1080*0f1bf1c2SAdrian Chadd mtx_unlock(&sc->sc_mutex);
1081*0f1bf1c2SAdrian Chadd CURVNET_SET_QUIET(if_getvnet(ifp));
1082*0f1bf1c2SAdrian Chadd if (in_control(NULL, SIOCGIFADDR, (caddr_t)&ifr, ifp,
1083*0f1bf1c2SAdrian Chadd curthread) == 0 &&
1084*0f1bf1c2SAdrian Chadd satosin(&ifr.ifr_addr)->sin_addr.s_addr !=
1085*0f1bf1c2SAdrian Chadd INADDR_ANY) {
1086*0f1bf1c2SAdrian Chadd in_control(NULL, SIOCDIFADDR, (caddr_t)&ifr,
1087*0f1bf1c2SAdrian Chadd ifp, curthread);
1088*0f1bf1c2SAdrian Chadd }
1089*0f1bf1c2SAdrian Chadd CURVNET_RESTORE();
1090*0f1bf1c2SAdrian Chadd mtx_lock(&sc->sc_mutex);
1091*0f1bf1c2SAdrian Chadd }
1092*0f1bf1c2SAdrian Chadd if_link_state_change(ifp, state);
1093*0f1bf1c2SAdrian Chadd }
1094*0f1bf1c2SAdrian Chadd }
1095*0f1bf1c2SAdrian Chadd
1096*0f1bf1c2SAdrian Chadd static void
umb_up(struct umb_softc * sc)1097*0f1bf1c2SAdrian Chadd umb_up(struct umb_softc *sc)
1098*0f1bf1c2SAdrian Chadd {
1099*0f1bf1c2SAdrian Chadd if_t ifp = GET_IFP(sc);
1100*0f1bf1c2SAdrian Chadd
1101*0f1bf1c2SAdrian Chadd switch (sc->sc_state) {
1102*0f1bf1c2SAdrian Chadd case UMB_S_DOWN:
1103*0f1bf1c2SAdrian Chadd DPRINTF("init: opening ...\n");
1104*0f1bf1c2SAdrian Chadd umb_open(sc);
1105*0f1bf1c2SAdrian Chadd break;
1106*0f1bf1c2SAdrian Chadd case UMB_S_OPEN:
1107*0f1bf1c2SAdrian Chadd if (sc->sc_flags & UMBFLG_FCC_AUTH_REQUIRED) {
1108*0f1bf1c2SAdrian Chadd if (sc->sc_cid == -1) {
1109*0f1bf1c2SAdrian Chadd DPRINTF("init: allocating CID ...\n");
1110*0f1bf1c2SAdrian Chadd umb_allocate_cid(sc);
1111*0f1bf1c2SAdrian Chadd break;
1112*0f1bf1c2SAdrian Chadd } else
1113*0f1bf1c2SAdrian Chadd umb_newstate(sc, UMB_S_CID, UMB_NS_DONT_DROP);
1114*0f1bf1c2SAdrian Chadd } else {
1115*0f1bf1c2SAdrian Chadd DPRINTF("init: turning radio on ...\n");
1116*0f1bf1c2SAdrian Chadd umb_radio(sc, 1);
1117*0f1bf1c2SAdrian Chadd break;
1118*0f1bf1c2SAdrian Chadd }
1119*0f1bf1c2SAdrian Chadd /*FALLTHROUGH*/
1120*0f1bf1c2SAdrian Chadd case UMB_S_CID:
1121*0f1bf1c2SAdrian Chadd DPRINTF("init: sending FCC auth ...\n");
1122*0f1bf1c2SAdrian Chadd umb_send_fcc_auth(sc);
1123*0f1bf1c2SAdrian Chadd break;
1124*0f1bf1c2SAdrian Chadd case UMB_S_RADIO:
1125*0f1bf1c2SAdrian Chadd DPRINTF("init: checking SIM state ...\n");
1126*0f1bf1c2SAdrian Chadd umb_cmd(sc, MBIM_CID_SUBSCRIBER_READY_STATUS, MBIM_CMDOP_QRY,
1127*0f1bf1c2SAdrian Chadd NULL, 0);
1128*0f1bf1c2SAdrian Chadd break;
1129*0f1bf1c2SAdrian Chadd case UMB_S_SIMREADY:
1130*0f1bf1c2SAdrian Chadd DPRINTF("init: attaching ...\n");
1131*0f1bf1c2SAdrian Chadd umb_packet_service(sc, 1);
1132*0f1bf1c2SAdrian Chadd break;
1133*0f1bf1c2SAdrian Chadd case UMB_S_ATTACHED:
1134*0f1bf1c2SAdrian Chadd sc->sc_tx_seq = 0;
1135*0f1bf1c2SAdrian Chadd DPRINTF("init: connecting ...\n");
1136*0f1bf1c2SAdrian Chadd umb_connect(sc);
1137*0f1bf1c2SAdrian Chadd break;
1138*0f1bf1c2SAdrian Chadd case UMB_S_CONNECTED:
1139*0f1bf1c2SAdrian Chadd DPRINTF("init: getting IP config ...\n");
1140*0f1bf1c2SAdrian Chadd umb_qry_ipconfig(sc);
1141*0f1bf1c2SAdrian Chadd break;
1142*0f1bf1c2SAdrian Chadd case UMB_S_UP:
1143*0f1bf1c2SAdrian Chadd DPRINTF("init: reached state UP\n");
1144*0f1bf1c2SAdrian Chadd if (!(if_getflags(ifp) & IFF_DRV_RUNNING)) {
1145*0f1bf1c2SAdrian Chadd if_setdrvflagbits(ifp, IFF_DRV_RUNNING, 0);
1146*0f1bf1c2SAdrian Chadd if_setdrvflagbits(ifp, 0, IFF_DRV_OACTIVE);
1147*0f1bf1c2SAdrian Chadd umb_rx(sc);
1148*0f1bf1c2SAdrian Chadd }
1149*0f1bf1c2SAdrian Chadd break;
1150*0f1bf1c2SAdrian Chadd }
1151*0f1bf1c2SAdrian Chadd if (sc->sc_state < UMB_S_UP)
1152*0f1bf1c2SAdrian Chadd usb_callout_reset(&sc->sc_statechg_timer,
1153*0f1bf1c2SAdrian Chadd UMB_STATE_CHANGE_TIMEOUT * hz, umb_statechg_timeout, sc);
1154*0f1bf1c2SAdrian Chadd else {
1155*0f1bf1c2SAdrian Chadd usb_callout_stop(&sc->sc_statechg_timer);
1156*0f1bf1c2SAdrian Chadd }
1157*0f1bf1c2SAdrian Chadd return;
1158*0f1bf1c2SAdrian Chadd }
1159*0f1bf1c2SAdrian Chadd
1160*0f1bf1c2SAdrian Chadd static void
umb_down(struct umb_softc * sc,int force)1161*0f1bf1c2SAdrian Chadd umb_down(struct umb_softc *sc, int force)
1162*0f1bf1c2SAdrian Chadd {
1163*0f1bf1c2SAdrian Chadd umb_close_bulkpipes(sc);
1164*0f1bf1c2SAdrian Chadd
1165*0f1bf1c2SAdrian Chadd switch (sc->sc_state) {
1166*0f1bf1c2SAdrian Chadd case UMB_S_UP:
1167*0f1bf1c2SAdrian Chadd case UMB_S_CONNECTED:
1168*0f1bf1c2SAdrian Chadd DPRINTF("stop: disconnecting ...\n");
1169*0f1bf1c2SAdrian Chadd umb_disconnect(sc);
1170*0f1bf1c2SAdrian Chadd if (!force)
1171*0f1bf1c2SAdrian Chadd break;
1172*0f1bf1c2SAdrian Chadd /*FALLTHROUGH*/
1173*0f1bf1c2SAdrian Chadd case UMB_S_ATTACHED:
1174*0f1bf1c2SAdrian Chadd DPRINTF("stop: detaching ...\n");
1175*0f1bf1c2SAdrian Chadd umb_packet_service(sc, 0);
1176*0f1bf1c2SAdrian Chadd if (!force)
1177*0f1bf1c2SAdrian Chadd break;
1178*0f1bf1c2SAdrian Chadd /*FALLTHROUGH*/
1179*0f1bf1c2SAdrian Chadd case UMB_S_SIMREADY:
1180*0f1bf1c2SAdrian Chadd case UMB_S_RADIO:
1181*0f1bf1c2SAdrian Chadd DPRINTF("stop: turning radio off ...\n");
1182*0f1bf1c2SAdrian Chadd umb_radio(sc, 0);
1183*0f1bf1c2SAdrian Chadd if (!force)
1184*0f1bf1c2SAdrian Chadd break;
1185*0f1bf1c2SAdrian Chadd /*FALLTHROUGH*/
1186*0f1bf1c2SAdrian Chadd case UMB_S_CID:
1187*0f1bf1c2SAdrian Chadd case UMB_S_OPEN:
1188*0f1bf1c2SAdrian Chadd case UMB_S_DOWN:
1189*0f1bf1c2SAdrian Chadd /* Do not close the device */
1190*0f1bf1c2SAdrian Chadd DPRINTF("stop: reached state DOWN\n");
1191*0f1bf1c2SAdrian Chadd break;
1192*0f1bf1c2SAdrian Chadd }
1193*0f1bf1c2SAdrian Chadd if (force)
1194*0f1bf1c2SAdrian Chadd sc->sc_state = UMB_S_OPEN;
1195*0f1bf1c2SAdrian Chadd
1196*0f1bf1c2SAdrian Chadd if (sc->sc_state > UMB_S_OPEN)
1197*0f1bf1c2SAdrian Chadd usb_callout_reset(&sc->sc_statechg_timer,
1198*0f1bf1c2SAdrian Chadd UMB_STATE_CHANGE_TIMEOUT * hz, umb_statechg_timeout, sc);
1199*0f1bf1c2SAdrian Chadd else
1200*0f1bf1c2SAdrian Chadd usb_callout_stop(&sc->sc_statechg_timer);
1201*0f1bf1c2SAdrian Chadd }
1202*0f1bf1c2SAdrian Chadd
1203*0f1bf1c2SAdrian Chadd static void
umb_get_response_task(struct usb_proc_msg * msg)1204*0f1bf1c2SAdrian Chadd umb_get_response_task(struct usb_proc_msg *msg)
1205*0f1bf1c2SAdrian Chadd {
1206*0f1bf1c2SAdrian Chadd struct umb_task *task = (struct umb_task *)msg;
1207*0f1bf1c2SAdrian Chadd struct umb_softc *sc = task->sc;
1208*0f1bf1c2SAdrian Chadd int len;
1209*0f1bf1c2SAdrian Chadd
1210*0f1bf1c2SAdrian Chadd DPRINTF("%s()\n", __func__);
1211*0f1bf1c2SAdrian Chadd /*
1212*0f1bf1c2SAdrian Chadd * Function is required to send on RESPONSE_AVAILABLE notification for
1213*0f1bf1c2SAdrian Chadd * each encapsulated response that is to be processed by the host.
1214*0f1bf1c2SAdrian Chadd * But of course, we can receive multiple notifications before the
1215*0f1bf1c2SAdrian Chadd * response task is run.
1216*0f1bf1c2SAdrian Chadd */
1217*0f1bf1c2SAdrian Chadd while (sc->sc_nresp > 0) {
1218*0f1bf1c2SAdrian Chadd --sc->sc_nresp;
1219*0f1bf1c2SAdrian Chadd len = sc->sc_ctrl_len;
1220*0f1bf1c2SAdrian Chadd if (umb_get_encap_response(sc, sc->sc_resp_buf, &len))
1221*0f1bf1c2SAdrian Chadd umb_decode_response(sc, sc->sc_resp_buf, len);
1222*0f1bf1c2SAdrian Chadd }
1223*0f1bf1c2SAdrian Chadd }
1224*0f1bf1c2SAdrian Chadd
1225*0f1bf1c2SAdrian Chadd static void
umb_decode_response(struct umb_softc * sc,void * response,int len)1226*0f1bf1c2SAdrian Chadd umb_decode_response(struct umb_softc *sc, void *response, int len)
1227*0f1bf1c2SAdrian Chadd {
1228*0f1bf1c2SAdrian Chadd struct mbim_msghdr *hdr = response;
1229*0f1bf1c2SAdrian Chadd struct mbim_fragmented_msg_hdr *fraghdr;
1230*0f1bf1c2SAdrian Chadd uint32_t type;
1231*0f1bf1c2SAdrian Chadd
1232*0f1bf1c2SAdrian Chadd DPRINTFN(3, "got response: len %d\n", len);
1233*0f1bf1c2SAdrian Chadd DDUMPN(4, response, len);
1234*0f1bf1c2SAdrian Chadd
1235*0f1bf1c2SAdrian Chadd if (len < sizeof (*hdr) || le32toh(hdr->len) != len) {
1236*0f1bf1c2SAdrian Chadd /*
1237*0f1bf1c2SAdrian Chadd * We should probably cancel a transaction, but since the
1238*0f1bf1c2SAdrian Chadd * message is too short, we cannot decode the transaction
1239*0f1bf1c2SAdrian Chadd * id (tid) and hence don't know, whom to cancel. Must wait
1240*0f1bf1c2SAdrian Chadd * for the timeout.
1241*0f1bf1c2SAdrian Chadd */
1242*0f1bf1c2SAdrian Chadd DPRINTF("received short response (len %d)\n",
1243*0f1bf1c2SAdrian Chadd len);
1244*0f1bf1c2SAdrian Chadd return;
1245*0f1bf1c2SAdrian Chadd }
1246*0f1bf1c2SAdrian Chadd
1247*0f1bf1c2SAdrian Chadd /*
1248*0f1bf1c2SAdrian Chadd * XXX FIXME: if message is fragmented, store it until last frag
1249*0f1bf1c2SAdrian Chadd * is received and then re-assemble all fragments.
1250*0f1bf1c2SAdrian Chadd */
1251*0f1bf1c2SAdrian Chadd type = le32toh(hdr->type);
1252*0f1bf1c2SAdrian Chadd switch (type) {
1253*0f1bf1c2SAdrian Chadd case MBIM_INDICATE_STATUS_MSG:
1254*0f1bf1c2SAdrian Chadd case MBIM_COMMAND_DONE:
1255*0f1bf1c2SAdrian Chadd fraghdr = response;
1256*0f1bf1c2SAdrian Chadd if (le32toh(fraghdr->frag.nfrag) != 1) {
1257*0f1bf1c2SAdrian Chadd DPRINTF("discarding fragmented messages\n");
1258*0f1bf1c2SAdrian Chadd return;
1259*0f1bf1c2SAdrian Chadd }
1260*0f1bf1c2SAdrian Chadd break;
1261*0f1bf1c2SAdrian Chadd default:
1262*0f1bf1c2SAdrian Chadd break;
1263*0f1bf1c2SAdrian Chadd }
1264*0f1bf1c2SAdrian Chadd
1265*0f1bf1c2SAdrian Chadd DPRINTF("<- rcv %s (tid %u)\n", umb_request2str(type),
1266*0f1bf1c2SAdrian Chadd le32toh(hdr->tid));
1267*0f1bf1c2SAdrian Chadd switch (type) {
1268*0f1bf1c2SAdrian Chadd case MBIM_FUNCTION_ERROR_MSG:
1269*0f1bf1c2SAdrian Chadd case MBIM_HOST_ERROR_MSG:
1270*0f1bf1c2SAdrian Chadd {
1271*0f1bf1c2SAdrian Chadd struct mbim_f2h_hosterr *e;
1272*0f1bf1c2SAdrian Chadd int err;
1273*0f1bf1c2SAdrian Chadd
1274*0f1bf1c2SAdrian Chadd if (len >= sizeof (*e)) {
1275*0f1bf1c2SAdrian Chadd e = response;
1276*0f1bf1c2SAdrian Chadd err = le32toh(e->err);
1277*0f1bf1c2SAdrian Chadd
1278*0f1bf1c2SAdrian Chadd DPRINTF("%s message, error %s (tid %u)\n",
1279*0f1bf1c2SAdrian Chadd umb_request2str(type),
1280*0f1bf1c2SAdrian Chadd umb_error2str(err), le32toh(hdr->tid));
1281*0f1bf1c2SAdrian Chadd if (err == MBIM_ERROR_NOT_OPENED)
1282*0f1bf1c2SAdrian Chadd umb_newstate(sc, UMB_S_DOWN, 0);
1283*0f1bf1c2SAdrian Chadd }
1284*0f1bf1c2SAdrian Chadd break;
1285*0f1bf1c2SAdrian Chadd }
1286*0f1bf1c2SAdrian Chadd case MBIM_INDICATE_STATUS_MSG:
1287*0f1bf1c2SAdrian Chadd umb_handle_indicate_status_msg(sc, response, len);
1288*0f1bf1c2SAdrian Chadd break;
1289*0f1bf1c2SAdrian Chadd case MBIM_OPEN_DONE:
1290*0f1bf1c2SAdrian Chadd umb_handle_opendone_msg(sc, response, len);
1291*0f1bf1c2SAdrian Chadd break;
1292*0f1bf1c2SAdrian Chadd case MBIM_CLOSE_DONE:
1293*0f1bf1c2SAdrian Chadd umb_handle_closedone_msg(sc, response, len);
1294*0f1bf1c2SAdrian Chadd break;
1295*0f1bf1c2SAdrian Chadd case MBIM_COMMAND_DONE:
1296*0f1bf1c2SAdrian Chadd umb_command_done(sc, response, len);
1297*0f1bf1c2SAdrian Chadd break;
1298*0f1bf1c2SAdrian Chadd default:
1299*0f1bf1c2SAdrian Chadd DPRINTF("discard message %s\n",
1300*0f1bf1c2SAdrian Chadd umb_request2str(type));
1301*0f1bf1c2SAdrian Chadd break;
1302*0f1bf1c2SAdrian Chadd }
1303*0f1bf1c2SAdrian Chadd }
1304*0f1bf1c2SAdrian Chadd
1305*0f1bf1c2SAdrian Chadd static void
umb_handle_indicate_status_msg(struct umb_softc * sc,void * data,int len)1306*0f1bf1c2SAdrian Chadd umb_handle_indicate_status_msg(struct umb_softc *sc, void *data, int len)
1307*0f1bf1c2SAdrian Chadd {
1308*0f1bf1c2SAdrian Chadd struct mbim_f2h_indicate_status *m = data;
1309*0f1bf1c2SAdrian Chadd uint32_t infolen;
1310*0f1bf1c2SAdrian Chadd uint32_t cid;
1311*0f1bf1c2SAdrian Chadd
1312*0f1bf1c2SAdrian Chadd if (len < sizeof (*m)) {
1313*0f1bf1c2SAdrian Chadd DPRINTF("discard short %s message\n",
1314*0f1bf1c2SAdrian Chadd umb_request2str(le32toh(m->hdr.type)));
1315*0f1bf1c2SAdrian Chadd return;
1316*0f1bf1c2SAdrian Chadd }
1317*0f1bf1c2SAdrian Chadd if (memcmp(m->devid, umb_uuid_basic_connect, sizeof (m->devid))) {
1318*0f1bf1c2SAdrian Chadd DPRINTF("discard %s message for other UUID '%s'\n",
1319*0f1bf1c2SAdrian Chadd umb_request2str(le32toh(m->hdr.type)),
1320*0f1bf1c2SAdrian Chadd umb_uuid2str(m->devid));
1321*0f1bf1c2SAdrian Chadd return;
1322*0f1bf1c2SAdrian Chadd }
1323*0f1bf1c2SAdrian Chadd infolen = le32toh(m->infolen);
1324*0f1bf1c2SAdrian Chadd if (len < sizeof (*m) + infolen) {
1325*0f1bf1c2SAdrian Chadd DPRINTF("discard truncated %s message (want %d, got %d)\n",
1326*0f1bf1c2SAdrian Chadd umb_request2str(le32toh(m->hdr.type)),
1327*0f1bf1c2SAdrian Chadd (int)sizeof (*m) + infolen, len);
1328*0f1bf1c2SAdrian Chadd return;
1329*0f1bf1c2SAdrian Chadd }
1330*0f1bf1c2SAdrian Chadd
1331*0f1bf1c2SAdrian Chadd cid = le32toh(m->cid);
1332*0f1bf1c2SAdrian Chadd DPRINTF("indicate %s status\n", umb_cid2str(cid));
1333*0f1bf1c2SAdrian Chadd umb_decode_cid(sc, cid, m->info, infolen);
1334*0f1bf1c2SAdrian Chadd }
1335*0f1bf1c2SAdrian Chadd
1336*0f1bf1c2SAdrian Chadd static void
umb_handle_opendone_msg(struct umb_softc * sc,void * data,int len)1337*0f1bf1c2SAdrian Chadd umb_handle_opendone_msg(struct umb_softc *sc, void *data, int len)
1338*0f1bf1c2SAdrian Chadd {
1339*0f1bf1c2SAdrian Chadd struct mbim_f2h_openclosedone *resp = data;
1340*0f1bf1c2SAdrian Chadd if_t ifp = GET_IFP(sc);
1341*0f1bf1c2SAdrian Chadd uint32_t status;
1342*0f1bf1c2SAdrian Chadd
1343*0f1bf1c2SAdrian Chadd status = le32toh(resp->status);
1344*0f1bf1c2SAdrian Chadd if (status == MBIM_STATUS_SUCCESS) {
1345*0f1bf1c2SAdrian Chadd if (sc->sc_maxsessions == 0) {
1346*0f1bf1c2SAdrian Chadd umb_cmd(sc, MBIM_CID_DEVICE_CAPS, MBIM_CMDOP_QRY, NULL,
1347*0f1bf1c2SAdrian Chadd 0);
1348*0f1bf1c2SAdrian Chadd umb_cmd(sc, MBIM_CID_PIN, MBIM_CMDOP_QRY, NULL, 0);
1349*0f1bf1c2SAdrian Chadd umb_cmd(sc, MBIM_CID_REGISTER_STATE, MBIM_CMDOP_QRY,
1350*0f1bf1c2SAdrian Chadd NULL, 0);
1351*0f1bf1c2SAdrian Chadd }
1352*0f1bf1c2SAdrian Chadd umb_newstate(sc, UMB_S_OPEN, UMB_NS_DONT_DROP);
1353*0f1bf1c2SAdrian Chadd } else if (if_getflags(ifp) & IFF_DEBUG)
1354*0f1bf1c2SAdrian Chadd log(LOG_ERR, "%s: open error: %s\n", DEVNAM(sc),
1355*0f1bf1c2SAdrian Chadd umb_status2str(status));
1356*0f1bf1c2SAdrian Chadd return;
1357*0f1bf1c2SAdrian Chadd }
1358*0f1bf1c2SAdrian Chadd
1359*0f1bf1c2SAdrian Chadd static void
umb_handle_closedone_msg(struct umb_softc * sc,void * data,int len)1360*0f1bf1c2SAdrian Chadd umb_handle_closedone_msg(struct umb_softc *sc, void *data, int len)
1361*0f1bf1c2SAdrian Chadd {
1362*0f1bf1c2SAdrian Chadd struct mbim_f2h_openclosedone *resp = data;
1363*0f1bf1c2SAdrian Chadd uint32_t status;
1364*0f1bf1c2SAdrian Chadd
1365*0f1bf1c2SAdrian Chadd status = le32toh(resp->status);
1366*0f1bf1c2SAdrian Chadd if (status == MBIM_STATUS_SUCCESS)
1367*0f1bf1c2SAdrian Chadd umb_newstate(sc, UMB_S_DOWN, 0);
1368*0f1bf1c2SAdrian Chadd else
1369*0f1bf1c2SAdrian Chadd DPRINTF("close error: %s\n",
1370*0f1bf1c2SAdrian Chadd umb_status2str(status));
1371*0f1bf1c2SAdrian Chadd return;
1372*0f1bf1c2SAdrian Chadd }
1373*0f1bf1c2SAdrian Chadd
1374*0f1bf1c2SAdrian Chadd static inline void
umb_getinfobuf(char * in,int inlen,uint32_t offs,uint32_t sz,void * out,size_t outlen)1375*0f1bf1c2SAdrian Chadd umb_getinfobuf(char *in, int inlen, uint32_t offs, uint32_t sz,
1376*0f1bf1c2SAdrian Chadd void *out, size_t outlen)
1377*0f1bf1c2SAdrian Chadd {
1378*0f1bf1c2SAdrian Chadd offs = le32toh(offs);
1379*0f1bf1c2SAdrian Chadd sz = le32toh(sz);
1380*0f1bf1c2SAdrian Chadd if (inlen >= offs + sz) {
1381*0f1bf1c2SAdrian Chadd memset(out, 0, outlen);
1382*0f1bf1c2SAdrian Chadd memcpy(out, in + offs, MIN(sz, outlen));
1383*0f1bf1c2SAdrian Chadd }
1384*0f1bf1c2SAdrian Chadd }
1385*0f1bf1c2SAdrian Chadd
1386*0f1bf1c2SAdrian Chadd static inline int
umb_padding(void * data,int len,size_t sz)1387*0f1bf1c2SAdrian Chadd umb_padding(void *data, int len, size_t sz)
1388*0f1bf1c2SAdrian Chadd {
1389*0f1bf1c2SAdrian Chadd char *p = data;
1390*0f1bf1c2SAdrian Chadd int np = 0;
1391*0f1bf1c2SAdrian Chadd
1392*0f1bf1c2SAdrian Chadd while (len < sz && (len % 4) != 0) {
1393*0f1bf1c2SAdrian Chadd *p++ = '\0';
1394*0f1bf1c2SAdrian Chadd len++;
1395*0f1bf1c2SAdrian Chadd np++;
1396*0f1bf1c2SAdrian Chadd }
1397*0f1bf1c2SAdrian Chadd return np;
1398*0f1bf1c2SAdrian Chadd }
1399*0f1bf1c2SAdrian Chadd
1400*0f1bf1c2SAdrian Chadd static inline int
umb_addstr(void * buf,size_t bufsz,int * offs,void * str,int slen,uint32_t * offsmember,uint32_t * sizemember)1401*0f1bf1c2SAdrian Chadd umb_addstr(void *buf, size_t bufsz, int *offs, void *str, int slen,
1402*0f1bf1c2SAdrian Chadd uint32_t *offsmember, uint32_t *sizemember)
1403*0f1bf1c2SAdrian Chadd {
1404*0f1bf1c2SAdrian Chadd if (*offs + slen > bufsz)
1405*0f1bf1c2SAdrian Chadd return 0;
1406*0f1bf1c2SAdrian Chadd
1407*0f1bf1c2SAdrian Chadd *sizemember = htole32((uint32_t)slen);
1408*0f1bf1c2SAdrian Chadd if (slen && str) {
1409*0f1bf1c2SAdrian Chadd *offsmember = htole32((uint32_t)*offs);
1410*0f1bf1c2SAdrian Chadd memcpy((char *)buf + *offs, str, slen);
1411*0f1bf1c2SAdrian Chadd *offs += slen;
1412*0f1bf1c2SAdrian Chadd *offs += umb_padding(buf, *offs, bufsz);
1413*0f1bf1c2SAdrian Chadd } else
1414*0f1bf1c2SAdrian Chadd *offsmember = htole32(0);
1415*0f1bf1c2SAdrian Chadd return 1;
1416*0f1bf1c2SAdrian Chadd }
1417*0f1bf1c2SAdrian Chadd
1418*0f1bf1c2SAdrian Chadd static void
umb_in_len2mask(struct in_addr * mask,int len)1419*0f1bf1c2SAdrian Chadd umb_in_len2mask(struct in_addr *mask, int len)
1420*0f1bf1c2SAdrian Chadd {
1421*0f1bf1c2SAdrian Chadd int i;
1422*0f1bf1c2SAdrian Chadd u_char *p;
1423*0f1bf1c2SAdrian Chadd
1424*0f1bf1c2SAdrian Chadd p = (u_char *)mask;
1425*0f1bf1c2SAdrian Chadd memset(mask, 0, sizeof (*mask));
1426*0f1bf1c2SAdrian Chadd for (i = 0; i < len / 8; i++)
1427*0f1bf1c2SAdrian Chadd p[i] = 0xff;
1428*0f1bf1c2SAdrian Chadd if (len % 8)
1429*0f1bf1c2SAdrian Chadd p[i] = (0xff00 >> (len % 8)) & 0xff;
1430*0f1bf1c2SAdrian Chadd }
1431*0f1bf1c2SAdrian Chadd
1432*0f1bf1c2SAdrian Chadd static int
umb_decode_register_state(struct umb_softc * sc,void * data,int len)1433*0f1bf1c2SAdrian Chadd umb_decode_register_state(struct umb_softc *sc, void *data, int len)
1434*0f1bf1c2SAdrian Chadd {
1435*0f1bf1c2SAdrian Chadd struct mbim_cid_registration_state_info *rs = data;
1436*0f1bf1c2SAdrian Chadd if_t ifp = GET_IFP(sc);
1437*0f1bf1c2SAdrian Chadd
1438*0f1bf1c2SAdrian Chadd if (len < sizeof (*rs))
1439*0f1bf1c2SAdrian Chadd return 0;
1440*0f1bf1c2SAdrian Chadd sc->sc_info.nwerror = le32toh(rs->nwerror);
1441*0f1bf1c2SAdrian Chadd sc->sc_info.regstate = le32toh(rs->regstate);
1442*0f1bf1c2SAdrian Chadd sc->sc_info.regmode = le32toh(rs->regmode);
1443*0f1bf1c2SAdrian Chadd sc->sc_info.cellclass = le32toh(rs->curcellclass);
1444*0f1bf1c2SAdrian Chadd
1445*0f1bf1c2SAdrian Chadd /* XXX should we remember the provider_id? */
1446*0f1bf1c2SAdrian Chadd umb_getinfobuf(data, len, rs->provname_offs, rs->provname_size,
1447*0f1bf1c2SAdrian Chadd sc->sc_info.provider, sizeof (sc->sc_info.provider));
1448*0f1bf1c2SAdrian Chadd umb_getinfobuf(data, len, rs->roamingtxt_offs, rs->roamingtxt_size,
1449*0f1bf1c2SAdrian Chadd sc->sc_info.roamingtxt, sizeof (sc->sc_info.roamingtxt));
1450*0f1bf1c2SAdrian Chadd
1451*0f1bf1c2SAdrian Chadd DPRINTFN(2, "%s, availclass 0x%x, class 0x%x, regmode %d\n",
1452*0f1bf1c2SAdrian Chadd umb_regstate(sc->sc_info.regstate),
1453*0f1bf1c2SAdrian Chadd le32toh(rs->availclasses), sc->sc_info.cellclass,
1454*0f1bf1c2SAdrian Chadd sc->sc_info.regmode);
1455*0f1bf1c2SAdrian Chadd
1456*0f1bf1c2SAdrian Chadd if (sc->sc_info.regstate == MBIM_REGSTATE_ROAMING &&
1457*0f1bf1c2SAdrian Chadd !sc->sc_roaming &&
1458*0f1bf1c2SAdrian Chadd sc->sc_info.activation == MBIM_ACTIVATION_STATE_ACTIVATED) {
1459*0f1bf1c2SAdrian Chadd if (if_getflags(ifp) & IFF_DEBUG)
1460*0f1bf1c2SAdrian Chadd log(LOG_INFO,
1461*0f1bf1c2SAdrian Chadd "%s: disconnecting from roaming network\n",
1462*0f1bf1c2SAdrian Chadd DEVNAM(sc));
1463*0f1bf1c2SAdrian Chadd umb_disconnect(sc);
1464*0f1bf1c2SAdrian Chadd }
1465*0f1bf1c2SAdrian Chadd return 1;
1466*0f1bf1c2SAdrian Chadd }
1467*0f1bf1c2SAdrian Chadd
1468*0f1bf1c2SAdrian Chadd static int
umb_decode_devices_caps(struct umb_softc * sc,void * data,int len)1469*0f1bf1c2SAdrian Chadd umb_decode_devices_caps(struct umb_softc *sc, void *data, int len)
1470*0f1bf1c2SAdrian Chadd {
1471*0f1bf1c2SAdrian Chadd struct mbim_cid_device_caps *dc = data;
1472*0f1bf1c2SAdrian Chadd
1473*0f1bf1c2SAdrian Chadd if (len < sizeof (*dc))
1474*0f1bf1c2SAdrian Chadd return 0;
1475*0f1bf1c2SAdrian Chadd sc->sc_maxsessions = le32toh(dc->max_sessions);
1476*0f1bf1c2SAdrian Chadd sc->sc_info.supportedclasses = le32toh(dc->dataclass);
1477*0f1bf1c2SAdrian Chadd umb_getinfobuf(data, len, dc->devid_offs, dc->devid_size,
1478*0f1bf1c2SAdrian Chadd sc->sc_info.devid, sizeof (sc->sc_info.devid));
1479*0f1bf1c2SAdrian Chadd umb_getinfobuf(data, len, dc->fwinfo_offs, dc->fwinfo_size,
1480*0f1bf1c2SAdrian Chadd sc->sc_info.fwinfo, sizeof (sc->sc_info.fwinfo));
1481*0f1bf1c2SAdrian Chadd umb_getinfobuf(data, len, dc->hwinfo_offs, dc->hwinfo_size,
1482*0f1bf1c2SAdrian Chadd sc->sc_info.hwinfo, sizeof (sc->sc_info.hwinfo));
1483*0f1bf1c2SAdrian Chadd DPRINTFN(2, "max sessions %d, supported classes 0x%x\n",
1484*0f1bf1c2SAdrian Chadd sc->sc_maxsessions, sc->sc_info.supportedclasses);
1485*0f1bf1c2SAdrian Chadd return 1;
1486*0f1bf1c2SAdrian Chadd }
1487*0f1bf1c2SAdrian Chadd
1488*0f1bf1c2SAdrian Chadd static int
umb_decode_subscriber_status(struct umb_softc * sc,void * data,int len)1489*0f1bf1c2SAdrian Chadd umb_decode_subscriber_status(struct umb_softc *sc, void *data, int len)
1490*0f1bf1c2SAdrian Chadd {
1491*0f1bf1c2SAdrian Chadd struct mbim_cid_subscriber_ready_info *si = data;
1492*0f1bf1c2SAdrian Chadd if_t ifp = GET_IFP(sc);
1493*0f1bf1c2SAdrian Chadd int npn;
1494*0f1bf1c2SAdrian Chadd
1495*0f1bf1c2SAdrian Chadd if (len < sizeof (*si))
1496*0f1bf1c2SAdrian Chadd return 0;
1497*0f1bf1c2SAdrian Chadd sc->sc_info.sim_state = le32toh(si->ready);
1498*0f1bf1c2SAdrian Chadd
1499*0f1bf1c2SAdrian Chadd umb_getinfobuf(data, len, si->sid_offs, si->sid_size,
1500*0f1bf1c2SAdrian Chadd sc->sc_info.sid, sizeof (sc->sc_info.sid));
1501*0f1bf1c2SAdrian Chadd umb_getinfobuf(data, len, si->icc_offs, si->icc_size,
1502*0f1bf1c2SAdrian Chadd sc->sc_info.iccid, sizeof (sc->sc_info.iccid));
1503*0f1bf1c2SAdrian Chadd
1504*0f1bf1c2SAdrian Chadd npn = le32toh(si->no_pn);
1505*0f1bf1c2SAdrian Chadd if (npn > 0)
1506*0f1bf1c2SAdrian Chadd umb_getinfobuf(data, len, si->pn[0].offs, si->pn[0].size,
1507*0f1bf1c2SAdrian Chadd sc->sc_info.pn, sizeof (sc->sc_info.pn));
1508*0f1bf1c2SAdrian Chadd else
1509*0f1bf1c2SAdrian Chadd memset(sc->sc_info.pn, 0, sizeof (sc->sc_info.pn));
1510*0f1bf1c2SAdrian Chadd
1511*0f1bf1c2SAdrian Chadd if (sc->sc_info.sim_state == MBIM_SIMSTATE_LOCKED)
1512*0f1bf1c2SAdrian Chadd sc->sc_info.pin_state = UMB_PIN_REQUIRED;
1513*0f1bf1c2SAdrian Chadd if (if_getflags(ifp) & IFF_DEBUG)
1514*0f1bf1c2SAdrian Chadd log(LOG_INFO, "%s: SIM %s\n", DEVNAM(sc),
1515*0f1bf1c2SAdrian Chadd umb_simstate(sc->sc_info.sim_state));
1516*0f1bf1c2SAdrian Chadd if (sc->sc_info.sim_state == MBIM_SIMSTATE_INITIALIZED)
1517*0f1bf1c2SAdrian Chadd umb_newstate(sc, UMB_S_SIMREADY, UMB_NS_DONT_DROP);
1518*0f1bf1c2SAdrian Chadd return 1;
1519*0f1bf1c2SAdrian Chadd }
1520*0f1bf1c2SAdrian Chadd
1521*0f1bf1c2SAdrian Chadd static int
umb_decode_radio_state(struct umb_softc * sc,void * data,int len)1522*0f1bf1c2SAdrian Chadd umb_decode_radio_state(struct umb_softc *sc, void *data, int len)
1523*0f1bf1c2SAdrian Chadd {
1524*0f1bf1c2SAdrian Chadd struct mbim_cid_radio_state_info *rs = data;
1525*0f1bf1c2SAdrian Chadd if_t ifp = GET_IFP(sc);
1526*0f1bf1c2SAdrian Chadd
1527*0f1bf1c2SAdrian Chadd if (len < sizeof (*rs))
1528*0f1bf1c2SAdrian Chadd return 0;
1529*0f1bf1c2SAdrian Chadd
1530*0f1bf1c2SAdrian Chadd sc->sc_info.hw_radio_on =
1531*0f1bf1c2SAdrian Chadd (le32toh(rs->hw_state) == MBIM_RADIO_STATE_ON) ? 1 : 0;
1532*0f1bf1c2SAdrian Chadd sc->sc_info.sw_radio_on =
1533*0f1bf1c2SAdrian Chadd (le32toh(rs->sw_state) == MBIM_RADIO_STATE_ON) ? 1 : 0;
1534*0f1bf1c2SAdrian Chadd if (!sc->sc_info.hw_radio_on) {
1535*0f1bf1c2SAdrian Chadd device_printf(sc->sc_dev, "radio is disabled by hardware switch\n");
1536*0f1bf1c2SAdrian Chadd /*
1537*0f1bf1c2SAdrian Chadd * XXX do we need a time to poll the state of the rfkill switch
1538*0f1bf1c2SAdrian Chadd * or will the device send an unsolicited notification
1539*0f1bf1c2SAdrian Chadd * in case the state changes?
1540*0f1bf1c2SAdrian Chadd */
1541*0f1bf1c2SAdrian Chadd umb_newstate(sc, UMB_S_OPEN, 0);
1542*0f1bf1c2SAdrian Chadd } else if (!sc->sc_info.sw_radio_on) {
1543*0f1bf1c2SAdrian Chadd if (if_getflags(ifp) & IFF_DEBUG)
1544*0f1bf1c2SAdrian Chadd log(LOG_INFO, "%s: radio is off\n", DEVNAM(sc));
1545*0f1bf1c2SAdrian Chadd umb_newstate(sc, UMB_S_OPEN, 0);
1546*0f1bf1c2SAdrian Chadd } else
1547*0f1bf1c2SAdrian Chadd umb_newstate(sc, UMB_S_RADIO, UMB_NS_DONT_DROP);
1548*0f1bf1c2SAdrian Chadd return 1;
1549*0f1bf1c2SAdrian Chadd }
1550*0f1bf1c2SAdrian Chadd
1551*0f1bf1c2SAdrian Chadd static int
umb_decode_pin(struct umb_softc * sc,void * data,int len)1552*0f1bf1c2SAdrian Chadd umb_decode_pin(struct umb_softc *sc, void *data, int len)
1553*0f1bf1c2SAdrian Chadd {
1554*0f1bf1c2SAdrian Chadd struct mbim_cid_pin_info *pi = data;
1555*0f1bf1c2SAdrian Chadd if_t ifp = GET_IFP(sc);
1556*0f1bf1c2SAdrian Chadd uint32_t attempts_left;
1557*0f1bf1c2SAdrian Chadd
1558*0f1bf1c2SAdrian Chadd if (len < sizeof (*pi))
1559*0f1bf1c2SAdrian Chadd return 0;
1560*0f1bf1c2SAdrian Chadd
1561*0f1bf1c2SAdrian Chadd attempts_left = le32toh(pi->remaining_attempts);
1562*0f1bf1c2SAdrian Chadd if (attempts_left != 0xffffffff)
1563*0f1bf1c2SAdrian Chadd sc->sc_info.pin_attempts_left = attempts_left;
1564*0f1bf1c2SAdrian Chadd
1565*0f1bf1c2SAdrian Chadd switch (le32toh(pi->state)) {
1566*0f1bf1c2SAdrian Chadd case MBIM_PIN_STATE_UNLOCKED:
1567*0f1bf1c2SAdrian Chadd sc->sc_info.pin_state = UMB_PIN_UNLOCKED;
1568*0f1bf1c2SAdrian Chadd break;
1569*0f1bf1c2SAdrian Chadd case MBIM_PIN_STATE_LOCKED:
1570*0f1bf1c2SAdrian Chadd switch (le32toh(pi->type)) {
1571*0f1bf1c2SAdrian Chadd case MBIM_PIN_TYPE_PIN1:
1572*0f1bf1c2SAdrian Chadd sc->sc_info.pin_state = UMB_PIN_REQUIRED;
1573*0f1bf1c2SAdrian Chadd break;
1574*0f1bf1c2SAdrian Chadd case MBIM_PIN_TYPE_PUK1:
1575*0f1bf1c2SAdrian Chadd sc->sc_info.pin_state = UMB_PUK_REQUIRED;
1576*0f1bf1c2SAdrian Chadd break;
1577*0f1bf1c2SAdrian Chadd case MBIM_PIN_TYPE_PIN2:
1578*0f1bf1c2SAdrian Chadd case MBIM_PIN_TYPE_PUK2:
1579*0f1bf1c2SAdrian Chadd /* Assume that PIN1 was accepted */
1580*0f1bf1c2SAdrian Chadd sc->sc_info.pin_state = UMB_PIN_UNLOCKED;
1581*0f1bf1c2SAdrian Chadd break;
1582*0f1bf1c2SAdrian Chadd }
1583*0f1bf1c2SAdrian Chadd break;
1584*0f1bf1c2SAdrian Chadd }
1585*0f1bf1c2SAdrian Chadd if (if_getflags(ifp) & IFF_DEBUG)
1586*0f1bf1c2SAdrian Chadd log(LOG_INFO, "%s: %s state %s (%d attempts left)\n",
1587*0f1bf1c2SAdrian Chadd DEVNAM(sc), umb_pin_type(le32toh(pi->type)),
1588*0f1bf1c2SAdrian Chadd (le32toh(pi->state) == MBIM_PIN_STATE_UNLOCKED) ?
1589*0f1bf1c2SAdrian Chadd "unlocked" : "locked",
1590*0f1bf1c2SAdrian Chadd le32toh(pi->remaining_attempts));
1591*0f1bf1c2SAdrian Chadd
1592*0f1bf1c2SAdrian Chadd /*
1593*0f1bf1c2SAdrian Chadd * In case the PIN was set after IFF_UP, retrigger the state machine
1594*0f1bf1c2SAdrian Chadd */
1595*0f1bf1c2SAdrian Chadd umb_add_task(sc, umb_state_task,
1596*0f1bf1c2SAdrian Chadd &sc->sc_proc_state_task[0].hdr,
1597*0f1bf1c2SAdrian Chadd &sc->sc_proc_state_task[1].hdr, 0);
1598*0f1bf1c2SAdrian Chadd return 1;
1599*0f1bf1c2SAdrian Chadd }
1600*0f1bf1c2SAdrian Chadd
1601*0f1bf1c2SAdrian Chadd static int
umb_decode_packet_service(struct umb_softc * sc,void * data,int len)1602*0f1bf1c2SAdrian Chadd umb_decode_packet_service(struct umb_softc *sc, void *data, int len)
1603*0f1bf1c2SAdrian Chadd {
1604*0f1bf1c2SAdrian Chadd struct mbim_cid_packet_service_info *psi = data;
1605*0f1bf1c2SAdrian Chadd int state, highestclass;
1606*0f1bf1c2SAdrian Chadd uint64_t up_speed, down_speed;
1607*0f1bf1c2SAdrian Chadd if_t ifp = GET_IFP(sc);
1608*0f1bf1c2SAdrian Chadd
1609*0f1bf1c2SAdrian Chadd if (len < sizeof (*psi))
1610*0f1bf1c2SAdrian Chadd return 0;
1611*0f1bf1c2SAdrian Chadd
1612*0f1bf1c2SAdrian Chadd sc->sc_info.nwerror = le32toh(psi->nwerror);
1613*0f1bf1c2SAdrian Chadd state = le32toh(psi->state);
1614*0f1bf1c2SAdrian Chadd highestclass = le32toh(psi->highest_dataclass);
1615*0f1bf1c2SAdrian Chadd up_speed = le64toh(psi->uplink_speed);
1616*0f1bf1c2SAdrian Chadd down_speed = le64toh(psi->downlink_speed);
1617*0f1bf1c2SAdrian Chadd if (sc->sc_info.packetstate != state ||
1618*0f1bf1c2SAdrian Chadd sc->sc_info.uplink_speed != up_speed ||
1619*0f1bf1c2SAdrian Chadd sc->sc_info.downlink_speed != down_speed) {
1620*0f1bf1c2SAdrian Chadd if (if_getflags(ifp) & IFF_DEBUG) {
1621*0f1bf1c2SAdrian Chadd log(LOG_INFO, "%s: packet service ", DEVNAM(sc));
1622*0f1bf1c2SAdrian Chadd if (sc->sc_info.packetstate != state)
1623*0f1bf1c2SAdrian Chadd log(LOG_INFO, "changed from %s to ",
1624*0f1bf1c2SAdrian Chadd umb_packet_state(sc->sc_info.packetstate));
1625*0f1bf1c2SAdrian Chadd log(LOG_INFO, "%s, class %s, speed: %" PRIu64 " up / %" PRIu64 " down\n",
1626*0f1bf1c2SAdrian Chadd umb_packet_state(state),
1627*0f1bf1c2SAdrian Chadd umb_dataclass(highestclass), up_speed, down_speed);
1628*0f1bf1c2SAdrian Chadd }
1629*0f1bf1c2SAdrian Chadd }
1630*0f1bf1c2SAdrian Chadd sc->sc_info.packetstate = state;
1631*0f1bf1c2SAdrian Chadd sc->sc_info.highestclass = highestclass;
1632*0f1bf1c2SAdrian Chadd sc->sc_info.uplink_speed = up_speed;
1633*0f1bf1c2SAdrian Chadd sc->sc_info.downlink_speed = down_speed;
1634*0f1bf1c2SAdrian Chadd
1635*0f1bf1c2SAdrian Chadd if (sc->sc_info.regmode == MBIM_REGMODE_AUTOMATIC) {
1636*0f1bf1c2SAdrian Chadd /*
1637*0f1bf1c2SAdrian Chadd * For devices using automatic registration mode, just proceed,
1638*0f1bf1c2SAdrian Chadd * once registration has completed.
1639*0f1bf1c2SAdrian Chadd */
1640*0f1bf1c2SAdrian Chadd if (if_getflags(ifp) & IFF_UP) {
1641*0f1bf1c2SAdrian Chadd switch (sc->sc_info.regstate) {
1642*0f1bf1c2SAdrian Chadd case MBIM_REGSTATE_HOME:
1643*0f1bf1c2SAdrian Chadd case MBIM_REGSTATE_ROAMING:
1644*0f1bf1c2SAdrian Chadd case MBIM_REGSTATE_PARTNER:
1645*0f1bf1c2SAdrian Chadd umb_newstate(sc, UMB_S_ATTACHED,
1646*0f1bf1c2SAdrian Chadd UMB_NS_DONT_DROP);
1647*0f1bf1c2SAdrian Chadd break;
1648*0f1bf1c2SAdrian Chadd default:
1649*0f1bf1c2SAdrian Chadd break;
1650*0f1bf1c2SAdrian Chadd }
1651*0f1bf1c2SAdrian Chadd } else
1652*0f1bf1c2SAdrian Chadd umb_newstate(sc, UMB_S_SIMREADY, UMB_NS_DONT_RAISE);
1653*0f1bf1c2SAdrian Chadd } else switch (sc->sc_info.packetstate) {
1654*0f1bf1c2SAdrian Chadd case MBIM_PKTSERVICE_STATE_ATTACHED:
1655*0f1bf1c2SAdrian Chadd umb_newstate(sc, UMB_S_ATTACHED, UMB_NS_DONT_DROP);
1656*0f1bf1c2SAdrian Chadd break;
1657*0f1bf1c2SAdrian Chadd case MBIM_PKTSERVICE_STATE_DETACHED:
1658*0f1bf1c2SAdrian Chadd umb_newstate(sc, UMB_S_SIMREADY, UMB_NS_DONT_RAISE);
1659*0f1bf1c2SAdrian Chadd break;
1660*0f1bf1c2SAdrian Chadd }
1661*0f1bf1c2SAdrian Chadd return 1;
1662*0f1bf1c2SAdrian Chadd }
1663*0f1bf1c2SAdrian Chadd
1664*0f1bf1c2SAdrian Chadd static int
umb_decode_signal_state(struct umb_softc * sc,void * data,int len)1665*0f1bf1c2SAdrian Chadd umb_decode_signal_state(struct umb_softc *sc, void *data, int len)
1666*0f1bf1c2SAdrian Chadd {
1667*0f1bf1c2SAdrian Chadd struct mbim_cid_signal_state *ss = data;
1668*0f1bf1c2SAdrian Chadd if_t ifp = GET_IFP(sc);
1669*0f1bf1c2SAdrian Chadd int rssi;
1670*0f1bf1c2SAdrian Chadd
1671*0f1bf1c2SAdrian Chadd if (len < sizeof (*ss))
1672*0f1bf1c2SAdrian Chadd return 0;
1673*0f1bf1c2SAdrian Chadd
1674*0f1bf1c2SAdrian Chadd if (le32toh(ss->rssi) == 99)
1675*0f1bf1c2SAdrian Chadd rssi = UMB_VALUE_UNKNOWN;
1676*0f1bf1c2SAdrian Chadd else {
1677*0f1bf1c2SAdrian Chadd rssi = -113 + 2 * le32toh(ss->rssi);
1678*0f1bf1c2SAdrian Chadd if ((if_getflags(ifp) & IFF_DEBUG) && sc->sc_info.rssi != rssi &&
1679*0f1bf1c2SAdrian Chadd sc->sc_state >= UMB_S_CONNECTED)
1680*0f1bf1c2SAdrian Chadd log(LOG_INFO, "%s: rssi %d dBm\n", DEVNAM(sc), rssi);
1681*0f1bf1c2SAdrian Chadd }
1682*0f1bf1c2SAdrian Chadd sc->sc_info.rssi = rssi;
1683*0f1bf1c2SAdrian Chadd sc->sc_info.ber = le32toh(ss->err_rate);
1684*0f1bf1c2SAdrian Chadd if (sc->sc_info.ber == -99)
1685*0f1bf1c2SAdrian Chadd sc->sc_info.ber = UMB_VALUE_UNKNOWN;
1686*0f1bf1c2SAdrian Chadd return 1;
1687*0f1bf1c2SAdrian Chadd }
1688*0f1bf1c2SAdrian Chadd
1689*0f1bf1c2SAdrian Chadd static int
umb_decode_connect_info(struct umb_softc * sc,void * data,int len)1690*0f1bf1c2SAdrian Chadd umb_decode_connect_info(struct umb_softc *sc, void *data, int len)
1691*0f1bf1c2SAdrian Chadd {
1692*0f1bf1c2SAdrian Chadd struct mbim_cid_connect_info *ci = data;
1693*0f1bf1c2SAdrian Chadd if_t ifp = GET_IFP(sc);
1694*0f1bf1c2SAdrian Chadd int act;
1695*0f1bf1c2SAdrian Chadd
1696*0f1bf1c2SAdrian Chadd if (len < sizeof (*ci))
1697*0f1bf1c2SAdrian Chadd return 0;
1698*0f1bf1c2SAdrian Chadd
1699*0f1bf1c2SAdrian Chadd if (le32toh(ci->sessionid) != umb_session_id) {
1700*0f1bf1c2SAdrian Chadd DPRINTF("discard connection info for session %u\n",
1701*0f1bf1c2SAdrian Chadd le32toh(ci->sessionid));
1702*0f1bf1c2SAdrian Chadd return 1;
1703*0f1bf1c2SAdrian Chadd }
1704*0f1bf1c2SAdrian Chadd if (memcmp(ci->context, umb_uuid_context_internet,
1705*0f1bf1c2SAdrian Chadd sizeof (ci->context))) {
1706*0f1bf1c2SAdrian Chadd DPRINTF("discard connection info for other context\n");
1707*0f1bf1c2SAdrian Chadd return 1;
1708*0f1bf1c2SAdrian Chadd }
1709*0f1bf1c2SAdrian Chadd act = le32toh(ci->activation);
1710*0f1bf1c2SAdrian Chadd if (sc->sc_info.activation != act) {
1711*0f1bf1c2SAdrian Chadd if (if_getflags(ifp) & IFF_DEBUG)
1712*0f1bf1c2SAdrian Chadd log(LOG_INFO, "%s: connection %s\n", DEVNAM(sc),
1713*0f1bf1c2SAdrian Chadd umb_activation(act));
1714*0f1bf1c2SAdrian Chadd if ((if_getflags(ifp) & IFF_DEBUG) &&
1715*0f1bf1c2SAdrian Chadd le32toh(ci->iptype) != MBIM_CONTEXT_IPTYPE_DEFAULT &&
1716*0f1bf1c2SAdrian Chadd le32toh(ci->iptype) != MBIM_CONTEXT_IPTYPE_IPV4)
1717*0f1bf1c2SAdrian Chadd log(LOG_DEBUG, "%s: got iptype %d connection\n",
1718*0f1bf1c2SAdrian Chadd DEVNAM(sc), le32toh(ci->iptype));
1719*0f1bf1c2SAdrian Chadd
1720*0f1bf1c2SAdrian Chadd sc->sc_info.activation = act;
1721*0f1bf1c2SAdrian Chadd sc->sc_info.nwerror = le32toh(ci->nwerror);
1722*0f1bf1c2SAdrian Chadd
1723*0f1bf1c2SAdrian Chadd if (sc->sc_info.activation == MBIM_ACTIVATION_STATE_ACTIVATED)
1724*0f1bf1c2SAdrian Chadd umb_newstate(sc, UMB_S_CONNECTED, UMB_NS_DONT_DROP);
1725*0f1bf1c2SAdrian Chadd else if (sc->sc_info.activation ==
1726*0f1bf1c2SAdrian Chadd MBIM_ACTIVATION_STATE_DEACTIVATED)
1727*0f1bf1c2SAdrian Chadd umb_newstate(sc, UMB_S_ATTACHED, 0);
1728*0f1bf1c2SAdrian Chadd /* else: other states are purely transitional */
1729*0f1bf1c2SAdrian Chadd }
1730*0f1bf1c2SAdrian Chadd return 1;
1731*0f1bf1c2SAdrian Chadd }
1732*0f1bf1c2SAdrian Chadd
1733*0f1bf1c2SAdrian Chadd static int
umb_add_inet_config(struct umb_softc * sc,struct in_addr ip,u_int prefixlen,struct in_addr gw)1734*0f1bf1c2SAdrian Chadd umb_add_inet_config(struct umb_softc *sc, struct in_addr ip, u_int prefixlen,
1735*0f1bf1c2SAdrian Chadd struct in_addr gw)
1736*0f1bf1c2SAdrian Chadd {
1737*0f1bf1c2SAdrian Chadd if_t ifp = GET_IFP(sc);
1738*0f1bf1c2SAdrian Chadd struct in_aliasreq ifra;
1739*0f1bf1c2SAdrian Chadd struct sockaddr_in *sin;
1740*0f1bf1c2SAdrian Chadd int rv;
1741*0f1bf1c2SAdrian Chadd
1742*0f1bf1c2SAdrian Chadd memset(&ifra, 0, sizeof (ifra));
1743*0f1bf1c2SAdrian Chadd sin = (struct sockaddr_in *)&ifra.ifra_addr;
1744*0f1bf1c2SAdrian Chadd sin->sin_family = AF_INET;
1745*0f1bf1c2SAdrian Chadd sin->sin_len = sizeof (*sin);
1746*0f1bf1c2SAdrian Chadd sin->sin_addr = ip;
1747*0f1bf1c2SAdrian Chadd
1748*0f1bf1c2SAdrian Chadd sin = (struct sockaddr_in *)&ifra.ifra_dstaddr;
1749*0f1bf1c2SAdrian Chadd sin->sin_family = AF_INET;
1750*0f1bf1c2SAdrian Chadd sin->sin_len = sizeof (*sin);
1751*0f1bf1c2SAdrian Chadd sin->sin_addr = gw;
1752*0f1bf1c2SAdrian Chadd
1753*0f1bf1c2SAdrian Chadd sin = (struct sockaddr_in *)&ifra.ifra_mask;
1754*0f1bf1c2SAdrian Chadd sin->sin_family = AF_INET;
1755*0f1bf1c2SAdrian Chadd sin->sin_len = sizeof (*sin);
1756*0f1bf1c2SAdrian Chadd umb_in_len2mask(&sin->sin_addr, prefixlen);
1757*0f1bf1c2SAdrian Chadd
1758*0f1bf1c2SAdrian Chadd mtx_unlock(&sc->sc_mutex);
1759*0f1bf1c2SAdrian Chadd CURVNET_SET_QUIET(if_getvnet(ifp));
1760*0f1bf1c2SAdrian Chadd rv = in_control(NULL, SIOCAIFADDR, (caddr_t)&ifra, ifp, curthread);
1761*0f1bf1c2SAdrian Chadd CURVNET_RESTORE();
1762*0f1bf1c2SAdrian Chadd mtx_lock(&sc->sc_mutex);
1763*0f1bf1c2SAdrian Chadd if (rv != 0) {
1764*0f1bf1c2SAdrian Chadd device_printf(sc->sc_dev, "unable to set IPv4 address, error %d\n",
1765*0f1bf1c2SAdrian Chadd rv);
1766*0f1bf1c2SAdrian Chadd return rv;
1767*0f1bf1c2SAdrian Chadd }
1768*0f1bf1c2SAdrian Chadd
1769*0f1bf1c2SAdrian Chadd if (if_getflags(ifp) & IFF_DEBUG)
1770*0f1bf1c2SAdrian Chadd log(LOG_INFO, "%s: IPv4 addr %s, mask %s, "
1771*0f1bf1c2SAdrian Chadd "gateway %s\n", DEVNAM(sc),
1772*0f1bf1c2SAdrian Chadd umb_ntop(sintosa(&ifra.ifra_addr)),
1773*0f1bf1c2SAdrian Chadd umb_ntop(sintosa(&ifra.ifra_mask)),
1774*0f1bf1c2SAdrian Chadd umb_ntop(sintosa(&ifra.ifra_dstaddr)));
1775*0f1bf1c2SAdrian Chadd
1776*0f1bf1c2SAdrian Chadd return 0;
1777*0f1bf1c2SAdrian Chadd }
1778*0f1bf1c2SAdrian Chadd
1779*0f1bf1c2SAdrian Chadd static int
umb_decode_ip_configuration(struct umb_softc * sc,void * data,int len)1780*0f1bf1c2SAdrian Chadd umb_decode_ip_configuration(struct umb_softc *sc, void *data, int len)
1781*0f1bf1c2SAdrian Chadd {
1782*0f1bf1c2SAdrian Chadd struct mbim_cid_ip_configuration_info *ic = data;
1783*0f1bf1c2SAdrian Chadd if_t ifp = GET_IFP(sc);
1784*0f1bf1c2SAdrian Chadd uint32_t avail_v4;
1785*0f1bf1c2SAdrian Chadd uint32_t val;
1786*0f1bf1c2SAdrian Chadd int n, i;
1787*0f1bf1c2SAdrian Chadd int off;
1788*0f1bf1c2SAdrian Chadd struct mbim_cid_ipv4_element ipv4elem;
1789*0f1bf1c2SAdrian Chadd struct in_addr addr, gw;
1790*0f1bf1c2SAdrian Chadd int state = -1;
1791*0f1bf1c2SAdrian Chadd int rv;
1792*0f1bf1c2SAdrian Chadd
1793*0f1bf1c2SAdrian Chadd if (len < sizeof (*ic))
1794*0f1bf1c2SAdrian Chadd return 0;
1795*0f1bf1c2SAdrian Chadd if (le32toh(ic->sessionid) != umb_session_id) {
1796*0f1bf1c2SAdrian Chadd DPRINTF("ignore IP configuration for session id %d\n",
1797*0f1bf1c2SAdrian Chadd le32toh(ic->sessionid));
1798*0f1bf1c2SAdrian Chadd return 0;
1799*0f1bf1c2SAdrian Chadd }
1800*0f1bf1c2SAdrian Chadd
1801*0f1bf1c2SAdrian Chadd /*
1802*0f1bf1c2SAdrian Chadd * IPv4 configuration
1803*0f1bf1c2SAdrian Chadd */
1804*0f1bf1c2SAdrian Chadd avail_v4 = le32toh(ic->ipv4_available);
1805*0f1bf1c2SAdrian Chadd if ((avail_v4 & (MBIM_IPCONF_HAS_ADDRINFO | MBIM_IPCONF_HAS_GWINFO)) ==
1806*0f1bf1c2SAdrian Chadd (MBIM_IPCONF_HAS_ADDRINFO | MBIM_IPCONF_HAS_GWINFO)) {
1807*0f1bf1c2SAdrian Chadd n = le32toh(ic->ipv4_naddr);
1808*0f1bf1c2SAdrian Chadd off = le32toh(ic->ipv4_addroffs);
1809*0f1bf1c2SAdrian Chadd
1810*0f1bf1c2SAdrian Chadd if (n == 0 || off + sizeof (ipv4elem) > len)
1811*0f1bf1c2SAdrian Chadd goto tryv6;
1812*0f1bf1c2SAdrian Chadd if (n != 1 && if_getflags(ifp) & IFF_DEBUG)
1813*0f1bf1c2SAdrian Chadd log(LOG_INFO, "%s: more than one IPv4 addr: %d\n",
1814*0f1bf1c2SAdrian Chadd DEVNAM(sc), n);
1815*0f1bf1c2SAdrian Chadd
1816*0f1bf1c2SAdrian Chadd /* Only pick the first one */
1817*0f1bf1c2SAdrian Chadd memcpy(&ipv4elem, (char *)data + off, sizeof (ipv4elem));
1818*0f1bf1c2SAdrian Chadd ipv4elem.prefixlen = le32toh(ipv4elem.prefixlen);
1819*0f1bf1c2SAdrian Chadd addr.s_addr = ipv4elem.addr;
1820*0f1bf1c2SAdrian Chadd
1821*0f1bf1c2SAdrian Chadd off = le32toh(ic->ipv4_gwoffs);
1822*0f1bf1c2SAdrian Chadd if (off + sizeof (gw) > len)
1823*0f1bf1c2SAdrian Chadd goto done;
1824*0f1bf1c2SAdrian Chadd memcpy(&gw, (char *)data + off, sizeof (gw));
1825*0f1bf1c2SAdrian Chadd
1826*0f1bf1c2SAdrian Chadd rv = umb_add_inet_config(sc, addr, ipv4elem.prefixlen, gw);
1827*0f1bf1c2SAdrian Chadd if (rv == 0)
1828*0f1bf1c2SAdrian Chadd state = UMB_S_UP;
1829*0f1bf1c2SAdrian Chadd }
1830*0f1bf1c2SAdrian Chadd
1831*0f1bf1c2SAdrian Chadd memset(sc->sc_info.ipv4dns, 0, sizeof (sc->sc_info.ipv4dns));
1832*0f1bf1c2SAdrian Chadd if (avail_v4 & MBIM_IPCONF_HAS_DNSINFO) {
1833*0f1bf1c2SAdrian Chadd n = le32toh(ic->ipv4_ndnssrv);
1834*0f1bf1c2SAdrian Chadd off = le32toh(ic->ipv4_dnssrvoffs);
1835*0f1bf1c2SAdrian Chadd i = 0;
1836*0f1bf1c2SAdrian Chadd while (n-- > 0) {
1837*0f1bf1c2SAdrian Chadd if (off + sizeof (addr) > len)
1838*0f1bf1c2SAdrian Chadd break;
1839*0f1bf1c2SAdrian Chadd memcpy(&addr, (char *)data + off, sizeof(addr));
1840*0f1bf1c2SAdrian Chadd if (i < UMB_MAX_DNSSRV)
1841*0f1bf1c2SAdrian Chadd sc->sc_info.ipv4dns[i++] = addr;
1842*0f1bf1c2SAdrian Chadd off += sizeof(addr);
1843*0f1bf1c2SAdrian Chadd }
1844*0f1bf1c2SAdrian Chadd }
1845*0f1bf1c2SAdrian Chadd
1846*0f1bf1c2SAdrian Chadd if ((avail_v4 & MBIM_IPCONF_HAS_MTUINFO)) {
1847*0f1bf1c2SAdrian Chadd val = le32toh(ic->ipv4_mtu);
1848*0f1bf1c2SAdrian Chadd if (if_getmtu(ifp) != val && val <= sc->sc_maxpktlen) {
1849*0f1bf1c2SAdrian Chadd if_setmtu(ifp, val);
1850*0f1bf1c2SAdrian Chadd if (if_getmtu(ifp) > val)
1851*0f1bf1c2SAdrian Chadd if_setmtu(ifp, val);
1852*0f1bf1c2SAdrian Chadd if (if_getflags(ifp) & IFF_DEBUG)
1853*0f1bf1c2SAdrian Chadd log(LOG_INFO, "%s: MTU %d\n", DEVNAM(sc), val);
1854*0f1bf1c2SAdrian Chadd }
1855*0f1bf1c2SAdrian Chadd }
1856*0f1bf1c2SAdrian Chadd
1857*0f1bf1c2SAdrian Chadd avail_v4 = le32toh(ic->ipv6_available);
1858*0f1bf1c2SAdrian Chadd if ((if_getflags(ifp) & IFF_DEBUG) && avail_v4 & MBIM_IPCONF_HAS_ADDRINFO) {
1859*0f1bf1c2SAdrian Chadd /* XXX FIXME: IPv6 configuration missing */
1860*0f1bf1c2SAdrian Chadd log(LOG_INFO, "%s: ignoring IPv6 configuration\n", DEVNAM(sc));
1861*0f1bf1c2SAdrian Chadd }
1862*0f1bf1c2SAdrian Chadd if (state != -1)
1863*0f1bf1c2SAdrian Chadd umb_newstate(sc, state, 0);
1864*0f1bf1c2SAdrian Chadd
1865*0f1bf1c2SAdrian Chadd tryv6:
1866*0f1bf1c2SAdrian Chadd done:
1867*0f1bf1c2SAdrian Chadd return 1;
1868*0f1bf1c2SAdrian Chadd }
1869*0f1bf1c2SAdrian Chadd
1870*0f1bf1c2SAdrian Chadd static void
umb_rx(struct umb_softc * sc)1871*0f1bf1c2SAdrian Chadd umb_rx(struct umb_softc *sc)
1872*0f1bf1c2SAdrian Chadd {
1873*0f1bf1c2SAdrian Chadd mtx_assert(&sc->sc_mutex, MA_OWNED);
1874*0f1bf1c2SAdrian Chadd
1875*0f1bf1c2SAdrian Chadd usbd_transfer_start(sc->sc_xfer[UMB_BULK_RX]);
1876*0f1bf1c2SAdrian Chadd }
1877*0f1bf1c2SAdrian Chadd
1878*0f1bf1c2SAdrian Chadd static void
umb_rxeof(struct usb_xfer * xfer,usb_error_t status)1879*0f1bf1c2SAdrian Chadd umb_rxeof(struct usb_xfer *xfer, usb_error_t status)
1880*0f1bf1c2SAdrian Chadd {
1881*0f1bf1c2SAdrian Chadd struct umb_softc *sc = usbd_xfer_softc(xfer);
1882*0f1bf1c2SAdrian Chadd if_t ifp = GET_IFP(sc);
1883*0f1bf1c2SAdrian Chadd int actlen;
1884*0f1bf1c2SAdrian Chadd int aframes;
1885*0f1bf1c2SAdrian Chadd int i;
1886*0f1bf1c2SAdrian Chadd
1887*0f1bf1c2SAdrian Chadd DPRINTF("%s(%u): state=%u\n", __func__, status, USB_GET_STATE(xfer));
1888*0f1bf1c2SAdrian Chadd
1889*0f1bf1c2SAdrian Chadd mtx_assert(&sc->sc_mutex, MA_OWNED);
1890*0f1bf1c2SAdrian Chadd
1891*0f1bf1c2SAdrian Chadd usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL);
1892*0f1bf1c2SAdrian Chadd
1893*0f1bf1c2SAdrian Chadd switch (USB_GET_STATE(xfer)) {
1894*0f1bf1c2SAdrian Chadd case USB_ST_TRANSFERRED:
1895*0f1bf1c2SAdrian Chadd DPRINTF("received %u bytes in %u frames\n", actlen, aframes);
1896*0f1bf1c2SAdrian Chadd
1897*0f1bf1c2SAdrian Chadd if (actlen == 0) {
1898*0f1bf1c2SAdrian Chadd if (sc->sc_rx_nerr >= 4)
1899*0f1bf1c2SAdrian Chadd /* throttle transfers */
1900*0f1bf1c2SAdrian Chadd usbd_xfer_set_interval(xfer, 500);
1901*0f1bf1c2SAdrian Chadd else
1902*0f1bf1c2SAdrian Chadd sc->sc_rx_nerr++;
1903*0f1bf1c2SAdrian Chadd }
1904*0f1bf1c2SAdrian Chadd else {
1905*0f1bf1c2SAdrian Chadd /* disable throttling */
1906*0f1bf1c2SAdrian Chadd usbd_xfer_set_interval(xfer, 0);
1907*0f1bf1c2SAdrian Chadd sc->sc_rx_nerr = 0;
1908*0f1bf1c2SAdrian Chadd }
1909*0f1bf1c2SAdrian Chadd
1910*0f1bf1c2SAdrian Chadd for(i = 0; i < aframes; i++) {
1911*0f1bf1c2SAdrian Chadd umb_decap(sc, xfer, i);
1912*0f1bf1c2SAdrian Chadd }
1913*0f1bf1c2SAdrian Chadd
1914*0f1bf1c2SAdrian Chadd /* fall through */
1915*0f1bf1c2SAdrian Chadd case USB_ST_SETUP:
1916*0f1bf1c2SAdrian Chadd usbd_xfer_set_frame_data(xfer, 0, sc->sc_rx_buf,
1917*0f1bf1c2SAdrian Chadd sc->sc_rx_bufsz);
1918*0f1bf1c2SAdrian Chadd usbd_xfer_set_frames(xfer, 1);
1919*0f1bf1c2SAdrian Chadd usbd_transfer_submit(xfer);
1920*0f1bf1c2SAdrian Chadd
1921*0f1bf1c2SAdrian Chadd umb_rxflush(sc);
1922*0f1bf1c2SAdrian Chadd break;
1923*0f1bf1c2SAdrian Chadd default:
1924*0f1bf1c2SAdrian Chadd DPRINTF("rx error: %s\n", usbd_errstr(status));
1925*0f1bf1c2SAdrian Chadd
1926*0f1bf1c2SAdrian Chadd /* disable throttling */
1927*0f1bf1c2SAdrian Chadd usbd_xfer_set_interval(xfer, 0);
1928*0f1bf1c2SAdrian Chadd
1929*0f1bf1c2SAdrian Chadd if (status != USB_ERR_CANCELLED) {
1930*0f1bf1c2SAdrian Chadd /* try to clear stall first */
1931*0f1bf1c2SAdrian Chadd usbd_xfer_set_stall(xfer);
1932*0f1bf1c2SAdrian Chadd usbd_xfer_set_frames(xfer, 0);
1933*0f1bf1c2SAdrian Chadd usbd_transfer_submit(xfer);
1934*0f1bf1c2SAdrian Chadd if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
1935*0f1bf1c2SAdrian Chadd }
1936*0f1bf1c2SAdrian Chadd else if (++sc->sc_rx_nerr > 100) {
1937*0f1bf1c2SAdrian Chadd log(LOG_ERR, "%s: too many rx errors, disabling\n",
1938*0f1bf1c2SAdrian Chadd DEVNAM(sc));
1939*0f1bf1c2SAdrian Chadd umb_deactivate(sc->sc_dev);
1940*0f1bf1c2SAdrian Chadd }
1941*0f1bf1c2SAdrian Chadd break;
1942*0f1bf1c2SAdrian Chadd }
1943*0f1bf1c2SAdrian Chadd }
1944*0f1bf1c2SAdrian Chadd
1945*0f1bf1c2SAdrian Chadd static void
umb_rxflush(struct umb_softc * sc)1946*0f1bf1c2SAdrian Chadd umb_rxflush(struct umb_softc *sc)
1947*0f1bf1c2SAdrian Chadd {
1948*0f1bf1c2SAdrian Chadd if_t ifp = GET_IFP(sc);
1949*0f1bf1c2SAdrian Chadd struct mbuf *m;
1950*0f1bf1c2SAdrian Chadd
1951*0f1bf1c2SAdrian Chadd mtx_assert(&sc->sc_mutex, MA_OWNED);
1952*0f1bf1c2SAdrian Chadd
1953*0f1bf1c2SAdrian Chadd for (;;) {
1954*0f1bf1c2SAdrian Chadd _IF_DEQUEUE(&sc->sc_rx_queue, m);
1955*0f1bf1c2SAdrian Chadd if (m == NULL)
1956*0f1bf1c2SAdrian Chadd break;
1957*0f1bf1c2SAdrian Chadd
1958*0f1bf1c2SAdrian Chadd /*
1959*0f1bf1c2SAdrian Chadd * The USB xfer has been resubmitted so it's safe to unlock now.
1960*0f1bf1c2SAdrian Chadd */
1961*0f1bf1c2SAdrian Chadd mtx_unlock(&sc->sc_mutex);
1962*0f1bf1c2SAdrian Chadd CURVNET_SET_QUIET(if_getvnet(ifp));
1963*0f1bf1c2SAdrian Chadd if (if_getdrvflags(ifp) & IFF_DRV_RUNNING)
1964*0f1bf1c2SAdrian Chadd if_input(ifp, m);
1965*0f1bf1c2SAdrian Chadd else
1966*0f1bf1c2SAdrian Chadd m_freem(m);
1967*0f1bf1c2SAdrian Chadd CURVNET_RESTORE();
1968*0f1bf1c2SAdrian Chadd mtx_lock(&sc->sc_mutex);
1969*0f1bf1c2SAdrian Chadd }
1970*0f1bf1c2SAdrian Chadd }
1971*0f1bf1c2SAdrian Chadd
1972*0f1bf1c2SAdrian Chadd static int
umb_encap(struct umb_softc * sc,struct mbuf * m,struct usb_xfer * xfer)1973*0f1bf1c2SAdrian Chadd umb_encap(struct umb_softc *sc, struct mbuf *m, struct usb_xfer *xfer)
1974*0f1bf1c2SAdrian Chadd {
1975*0f1bf1c2SAdrian Chadd struct ncm_header16 *hdr;
1976*0f1bf1c2SAdrian Chadd struct ncm_pointer16 *ptr;
1977*0f1bf1c2SAdrian Chadd int len;
1978*0f1bf1c2SAdrian Chadd
1979*0f1bf1c2SAdrian Chadd KASSERT(sc->sc_tx_m == NULL,
1980*0f1bf1c2SAdrian Chadd ("Assertion failed in umb_encap()"));
1981*0f1bf1c2SAdrian Chadd
1982*0f1bf1c2SAdrian Chadd /* All size constraints have been validated by the caller! */
1983*0f1bf1c2SAdrian Chadd hdr = (struct ncm_header16 *)sc->sc_tx_buf;
1984*0f1bf1c2SAdrian Chadd ptr = (struct ncm_pointer16 *)(hdr + 1);
1985*0f1bf1c2SAdrian Chadd
1986*0f1bf1c2SAdrian Chadd USETDW(hdr->dwSignature, NCM_HDR16_SIG);
1987*0f1bf1c2SAdrian Chadd USETW(hdr->wHeaderLength, sizeof (*hdr));
1988*0f1bf1c2SAdrian Chadd USETW(hdr->wSequence, sc->sc_tx_seq);
1989*0f1bf1c2SAdrian Chadd sc->sc_tx_seq++;
1990*0f1bf1c2SAdrian Chadd USETW(hdr->wNdpIndex, sizeof (*hdr));
1991*0f1bf1c2SAdrian Chadd
1992*0f1bf1c2SAdrian Chadd len = m->m_pkthdr.len;
1993*0f1bf1c2SAdrian Chadd USETDW(ptr->dwSignature, MBIM_NCM_NTH16_SIG(umb_session_id));
1994*0f1bf1c2SAdrian Chadd USETW(ptr->wLength, sizeof (*ptr));
1995*0f1bf1c2SAdrian Chadd USETW(ptr->wNextNdpIndex, 0);
1996*0f1bf1c2SAdrian Chadd USETW(ptr->dgram[0].wDatagramIndex, MBIM_HDR16_LEN);
1997*0f1bf1c2SAdrian Chadd USETW(ptr->dgram[0].wDatagramLen, len);
1998*0f1bf1c2SAdrian Chadd USETW(ptr->dgram[1].wDatagramIndex, 0);
1999*0f1bf1c2SAdrian Chadd USETW(ptr->dgram[1].wDatagramLen, 0);
2000*0f1bf1c2SAdrian Chadd
2001*0f1bf1c2SAdrian Chadd KASSERT(len + MBIM_HDR16_LEN <= sc->sc_tx_bufsz,
2002*0f1bf1c2SAdrian Chadd ("Assertion failed in umb_encap()"));
2003*0f1bf1c2SAdrian Chadd m_copydata(m, 0, len, (char *)(ptr + 1));
2004*0f1bf1c2SAdrian Chadd sc->sc_tx_m = m;
2005*0f1bf1c2SAdrian Chadd len += MBIM_HDR16_LEN;
2006*0f1bf1c2SAdrian Chadd USETW(hdr->wBlockLength, len);
2007*0f1bf1c2SAdrian Chadd
2008*0f1bf1c2SAdrian Chadd usbd_xfer_set_frame_data(xfer, 0, sc->sc_tx_buf, len);
2009*0f1bf1c2SAdrian Chadd usbd_xfer_set_interval(xfer, 0);
2010*0f1bf1c2SAdrian Chadd usbd_xfer_set_frames(xfer, 1);
2011*0f1bf1c2SAdrian Chadd
2012*0f1bf1c2SAdrian Chadd DPRINTFN(3, "%s: encap %d bytes\n", DEVNAM(sc), len);
2013*0f1bf1c2SAdrian Chadd DDUMPN(5, sc->sc_tx_buf, len);
2014*0f1bf1c2SAdrian Chadd return 0;
2015*0f1bf1c2SAdrian Chadd }
2016*0f1bf1c2SAdrian Chadd
2017*0f1bf1c2SAdrian Chadd static void
umb_txeof(struct usb_xfer * xfer,usb_error_t status)2018*0f1bf1c2SAdrian Chadd umb_txeof(struct usb_xfer *xfer, usb_error_t status)
2019*0f1bf1c2SAdrian Chadd {
2020*0f1bf1c2SAdrian Chadd struct umb_softc *sc = usbd_xfer_softc(xfer);
2021*0f1bf1c2SAdrian Chadd if_t ifp = GET_IFP(sc);
2022*0f1bf1c2SAdrian Chadd struct mbuf *m;
2023*0f1bf1c2SAdrian Chadd
2024*0f1bf1c2SAdrian Chadd DPRINTF("%s(%u) state=%u\n", __func__, status, USB_GET_STATE(xfer));
2025*0f1bf1c2SAdrian Chadd
2026*0f1bf1c2SAdrian Chadd mtx_assert(&sc->sc_mutex, MA_OWNED);
2027*0f1bf1c2SAdrian Chadd
2028*0f1bf1c2SAdrian Chadd switch (USB_GET_STATE(xfer)) {
2029*0f1bf1c2SAdrian Chadd case USB_ST_TRANSFERRED:
2030*0f1bf1c2SAdrian Chadd if_setdrvflagbits(ifp, 0, IFF_DRV_OACTIVE);
2031*0f1bf1c2SAdrian Chadd if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1);
2032*0f1bf1c2SAdrian Chadd
2033*0f1bf1c2SAdrian Chadd umb_txflush(sc);
2034*0f1bf1c2SAdrian Chadd
2035*0f1bf1c2SAdrian Chadd /* fall through */
2036*0f1bf1c2SAdrian Chadd case USB_ST_SETUP:
2037*0f1bf1c2SAdrian Chadd tr_setup:
2038*0f1bf1c2SAdrian Chadd if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) == 0)
2039*0f1bf1c2SAdrian Chadd break;
2040*0f1bf1c2SAdrian Chadd
2041*0f1bf1c2SAdrian Chadd m = if_dequeue(ifp); /* XXX - IFAPI */
2042*0f1bf1c2SAdrian Chadd if (m == NULL)
2043*0f1bf1c2SAdrian Chadd break;
2044*0f1bf1c2SAdrian Chadd
2045*0f1bf1c2SAdrian Chadd if (umb_encap(sc, m, xfer)) {
2046*0f1bf1c2SAdrian Chadd if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
2047*0f1bf1c2SAdrian Chadd umb_txflush(sc);
2048*0f1bf1c2SAdrian Chadd break;
2049*0f1bf1c2SAdrian Chadd }
2050*0f1bf1c2SAdrian Chadd
2051*0f1bf1c2SAdrian Chadd BPF_MTAP(ifp, m);
2052*0f1bf1c2SAdrian Chadd
2053*0f1bf1c2SAdrian Chadd if_setdrvflagbits(ifp, IFF_DRV_OACTIVE, 0);
2054*0f1bf1c2SAdrian Chadd usbd_transfer_submit(xfer);
2055*0f1bf1c2SAdrian Chadd
2056*0f1bf1c2SAdrian Chadd break;
2057*0f1bf1c2SAdrian Chadd
2058*0f1bf1c2SAdrian Chadd default:
2059*0f1bf1c2SAdrian Chadd umb_txflush(sc);
2060*0f1bf1c2SAdrian Chadd
2061*0f1bf1c2SAdrian Chadd /* count output errors */
2062*0f1bf1c2SAdrian Chadd if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
2063*0f1bf1c2SAdrian Chadd DPRINTF("tx error: %s\n",
2064*0f1bf1c2SAdrian Chadd usbd_errstr(status));
2065*0f1bf1c2SAdrian Chadd
2066*0f1bf1c2SAdrian Chadd if (status != USB_ERR_CANCELLED) {
2067*0f1bf1c2SAdrian Chadd /* try to clear stall first */
2068*0f1bf1c2SAdrian Chadd usbd_xfer_set_stall(xfer);
2069*0f1bf1c2SAdrian Chadd goto tr_setup;
2070*0f1bf1c2SAdrian Chadd }
2071*0f1bf1c2SAdrian Chadd break;
2072*0f1bf1c2SAdrian Chadd }
2073*0f1bf1c2SAdrian Chadd }
2074*0f1bf1c2SAdrian Chadd
2075*0f1bf1c2SAdrian Chadd static void
umb_txflush(struct umb_softc * sc)2076*0f1bf1c2SAdrian Chadd umb_txflush(struct umb_softc *sc)
2077*0f1bf1c2SAdrian Chadd {
2078*0f1bf1c2SAdrian Chadd mtx_assert(&sc->sc_mutex, MA_OWNED);
2079*0f1bf1c2SAdrian Chadd
2080*0f1bf1c2SAdrian Chadd if (sc->sc_tx_m != NULL) {
2081*0f1bf1c2SAdrian Chadd m_freem(sc->sc_tx_m);
2082*0f1bf1c2SAdrian Chadd sc->sc_tx_m = NULL;
2083*0f1bf1c2SAdrian Chadd }
2084*0f1bf1c2SAdrian Chadd }
2085*0f1bf1c2SAdrian Chadd
2086*0f1bf1c2SAdrian Chadd static void
umb_decap(struct umb_softc * sc,struct usb_xfer * xfer,int frame)2087*0f1bf1c2SAdrian Chadd umb_decap(struct umb_softc *sc, struct usb_xfer *xfer, int frame)
2088*0f1bf1c2SAdrian Chadd {
2089*0f1bf1c2SAdrian Chadd if_t ifp = GET_IFP(sc);
2090*0f1bf1c2SAdrian Chadd char *buf;
2091*0f1bf1c2SAdrian Chadd int len;
2092*0f1bf1c2SAdrian Chadd char *dp;
2093*0f1bf1c2SAdrian Chadd struct ncm_header16 *hdr16;
2094*0f1bf1c2SAdrian Chadd struct ncm_header32 *hdr32;
2095*0f1bf1c2SAdrian Chadd struct ncm_pointer16 *ptr16;
2096*0f1bf1c2SAdrian Chadd struct ncm_pointer16_dgram *dgram16;
2097*0f1bf1c2SAdrian Chadd struct ncm_pointer32_dgram *dgram32;
2098*0f1bf1c2SAdrian Chadd uint32_t hsig, psig;
2099*0f1bf1c2SAdrian Chadd int hlen, blen;
2100*0f1bf1c2SAdrian Chadd int ptrlen, ptroff, dgentryoff;
2101*0f1bf1c2SAdrian Chadd uint32_t doff, dlen;
2102*0f1bf1c2SAdrian Chadd struct mbuf *m;
2103*0f1bf1c2SAdrian Chadd
2104*0f1bf1c2SAdrian Chadd usbd_xfer_frame_data(xfer, frame, (void **)&buf, &len);
2105*0f1bf1c2SAdrian Chadd DPRINTFN(4, "recv %d bytes\n", len);
2106*0f1bf1c2SAdrian Chadd DDUMPN(5, buf, len);
2107*0f1bf1c2SAdrian Chadd if (len < sizeof (*hdr16))
2108*0f1bf1c2SAdrian Chadd goto toosmall;
2109*0f1bf1c2SAdrian Chadd
2110*0f1bf1c2SAdrian Chadd hdr16 = (struct ncm_header16 *)buf;
2111*0f1bf1c2SAdrian Chadd hsig = UGETDW(hdr16->dwSignature);
2112*0f1bf1c2SAdrian Chadd hlen = UGETW(hdr16->wHeaderLength);
2113*0f1bf1c2SAdrian Chadd if (len < hlen)
2114*0f1bf1c2SAdrian Chadd goto toosmall;
2115*0f1bf1c2SAdrian Chadd if (len > sc->sc_rx_bufsz) {
2116*0f1bf1c2SAdrian Chadd DPRINTF("packet too large (%d)\n", len);
2117*0f1bf1c2SAdrian Chadd goto fail;
2118*0f1bf1c2SAdrian Chadd }
2119*0f1bf1c2SAdrian Chadd switch (hsig) {
2120*0f1bf1c2SAdrian Chadd case NCM_HDR16_SIG:
2121*0f1bf1c2SAdrian Chadd blen = UGETW(hdr16->wBlockLength);
2122*0f1bf1c2SAdrian Chadd ptroff = UGETW(hdr16->wNdpIndex);
2123*0f1bf1c2SAdrian Chadd if (hlen != sizeof (*hdr16)) {
2124*0f1bf1c2SAdrian Chadd DPRINTF("%s: bad header len %d for NTH16 (exp %zu)\n",
2125*0f1bf1c2SAdrian Chadd DEVNAM(sc), hlen, sizeof (*hdr16));
2126*0f1bf1c2SAdrian Chadd goto fail;
2127*0f1bf1c2SAdrian Chadd }
2128*0f1bf1c2SAdrian Chadd break;
2129*0f1bf1c2SAdrian Chadd case NCM_HDR32_SIG:
2130*0f1bf1c2SAdrian Chadd hdr32 = (struct ncm_header32 *)hdr16;
2131*0f1bf1c2SAdrian Chadd blen = UGETDW(hdr32->dwBlockLength);
2132*0f1bf1c2SAdrian Chadd ptroff = UGETDW(hdr32->dwNdpIndex);
2133*0f1bf1c2SAdrian Chadd if (hlen != sizeof (*hdr32)) {
2134*0f1bf1c2SAdrian Chadd DPRINTF("%s: bad header len %d for NTH32 (exp %zu)\n",
2135*0f1bf1c2SAdrian Chadd DEVNAM(sc), hlen, sizeof (*hdr32));
2136*0f1bf1c2SAdrian Chadd goto fail;
2137*0f1bf1c2SAdrian Chadd }
2138*0f1bf1c2SAdrian Chadd break;
2139*0f1bf1c2SAdrian Chadd default:
2140*0f1bf1c2SAdrian Chadd DPRINTF("%s: unsupported NCM header signature (0x%08x)\n",
2141*0f1bf1c2SAdrian Chadd DEVNAM(sc), hsig);
2142*0f1bf1c2SAdrian Chadd goto fail;
2143*0f1bf1c2SAdrian Chadd }
2144*0f1bf1c2SAdrian Chadd if (len < blen) {
2145*0f1bf1c2SAdrian Chadd DPRINTF("%s: bad NTB len (%d) for %d bytes of data\n",
2146*0f1bf1c2SAdrian Chadd DEVNAM(sc), blen, len);
2147*0f1bf1c2SAdrian Chadd goto fail;
2148*0f1bf1c2SAdrian Chadd }
2149*0f1bf1c2SAdrian Chadd
2150*0f1bf1c2SAdrian Chadd ptr16 = (struct ncm_pointer16 *)(buf + ptroff);
2151*0f1bf1c2SAdrian Chadd psig = UGETDW(ptr16->dwSignature);
2152*0f1bf1c2SAdrian Chadd ptrlen = UGETW(ptr16->wLength);
2153*0f1bf1c2SAdrian Chadd if (len < ptrlen + ptroff)
2154*0f1bf1c2SAdrian Chadd goto toosmall;
2155*0f1bf1c2SAdrian Chadd if (!MBIM_NCM_NTH16_ISISG(psig) && !MBIM_NCM_NTH32_ISISG(psig)) {
2156*0f1bf1c2SAdrian Chadd DPRINTF("%s: unsupported NCM pointer signature (0x%08x)\n",
2157*0f1bf1c2SAdrian Chadd DEVNAM(sc), psig);
2158*0f1bf1c2SAdrian Chadd goto fail;
2159*0f1bf1c2SAdrian Chadd }
2160*0f1bf1c2SAdrian Chadd
2161*0f1bf1c2SAdrian Chadd switch (hsig) {
2162*0f1bf1c2SAdrian Chadd case NCM_HDR16_SIG:
2163*0f1bf1c2SAdrian Chadd dgentryoff = offsetof(struct ncm_pointer16, dgram);
2164*0f1bf1c2SAdrian Chadd break;
2165*0f1bf1c2SAdrian Chadd case NCM_HDR32_SIG:
2166*0f1bf1c2SAdrian Chadd dgentryoff = offsetof(struct ncm_pointer32, dgram);
2167*0f1bf1c2SAdrian Chadd break;
2168*0f1bf1c2SAdrian Chadd default:
2169*0f1bf1c2SAdrian Chadd goto fail;
2170*0f1bf1c2SAdrian Chadd }
2171*0f1bf1c2SAdrian Chadd
2172*0f1bf1c2SAdrian Chadd while (dgentryoff < ptrlen) {
2173*0f1bf1c2SAdrian Chadd switch (hsig) {
2174*0f1bf1c2SAdrian Chadd case NCM_HDR16_SIG:
2175*0f1bf1c2SAdrian Chadd if (ptroff + dgentryoff < sizeof (*dgram16))
2176*0f1bf1c2SAdrian Chadd goto done;
2177*0f1bf1c2SAdrian Chadd dgram16 = (struct ncm_pointer16_dgram *)
2178*0f1bf1c2SAdrian Chadd (buf + ptroff + dgentryoff);
2179*0f1bf1c2SAdrian Chadd dgentryoff += sizeof (*dgram16);
2180*0f1bf1c2SAdrian Chadd dlen = UGETW(dgram16->wDatagramLen);
2181*0f1bf1c2SAdrian Chadd doff = UGETW(dgram16->wDatagramIndex);
2182*0f1bf1c2SAdrian Chadd break;
2183*0f1bf1c2SAdrian Chadd case NCM_HDR32_SIG:
2184*0f1bf1c2SAdrian Chadd if (ptroff + dgentryoff < sizeof (*dgram32))
2185*0f1bf1c2SAdrian Chadd goto done;
2186*0f1bf1c2SAdrian Chadd dgram32 = (struct ncm_pointer32_dgram *)
2187*0f1bf1c2SAdrian Chadd (buf + ptroff + dgentryoff);
2188*0f1bf1c2SAdrian Chadd dgentryoff += sizeof (*dgram32);
2189*0f1bf1c2SAdrian Chadd dlen = UGETDW(dgram32->dwDatagramLen);
2190*0f1bf1c2SAdrian Chadd doff = UGETDW(dgram32->dwDatagramIndex);
2191*0f1bf1c2SAdrian Chadd break;
2192*0f1bf1c2SAdrian Chadd default:
2193*0f1bf1c2SAdrian Chadd if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
2194*0f1bf1c2SAdrian Chadd goto done;
2195*0f1bf1c2SAdrian Chadd }
2196*0f1bf1c2SAdrian Chadd
2197*0f1bf1c2SAdrian Chadd /* Terminating zero entry */
2198*0f1bf1c2SAdrian Chadd if (dlen == 0 || doff == 0)
2199*0f1bf1c2SAdrian Chadd break;
2200*0f1bf1c2SAdrian Chadd if (len < dlen + doff) {
2201*0f1bf1c2SAdrian Chadd /* Skip giant datagram but continue processing */
2202*0f1bf1c2SAdrian Chadd DPRINTF("%s: datagram too large (%d @ off %d)\n",
2203*0f1bf1c2SAdrian Chadd DEVNAM(sc), dlen, doff);
2204*0f1bf1c2SAdrian Chadd continue;
2205*0f1bf1c2SAdrian Chadd }
2206*0f1bf1c2SAdrian Chadd
2207*0f1bf1c2SAdrian Chadd dp = buf + doff;
2208*0f1bf1c2SAdrian Chadd DPRINTFN(3, "%s: decap %d bytes\n", DEVNAM(sc), dlen);
2209*0f1bf1c2SAdrian Chadd m = m_devget(dp, dlen, 0, ifp, NULL);
2210*0f1bf1c2SAdrian Chadd if (m == NULL) {
2211*0f1bf1c2SAdrian Chadd if_inc_counter(ifp, IFCOUNTER_IQDROPS, 1);
2212*0f1bf1c2SAdrian Chadd continue;
2213*0f1bf1c2SAdrian Chadd }
2214*0f1bf1c2SAdrian Chadd
2215*0f1bf1c2SAdrian Chadd /* enqueue for later when the lock can be released */
2216*0f1bf1c2SAdrian Chadd _IF_ENQUEUE(&sc->sc_rx_queue, m);
2217*0f1bf1c2SAdrian Chadd
2218*0f1bf1c2SAdrian Chadd if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1);
2219*0f1bf1c2SAdrian Chadd
2220*0f1bf1c2SAdrian Chadd }
2221*0f1bf1c2SAdrian Chadd done:
2222*0f1bf1c2SAdrian Chadd sc->sc_rx_nerr = 0;
2223*0f1bf1c2SAdrian Chadd return;
2224*0f1bf1c2SAdrian Chadd toosmall:
2225*0f1bf1c2SAdrian Chadd DPRINTF("%s: packet too small (%d)\n", DEVNAM(sc), len);
2226*0f1bf1c2SAdrian Chadd fail:
2227*0f1bf1c2SAdrian Chadd if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
2228*0f1bf1c2SAdrian Chadd }
2229*0f1bf1c2SAdrian Chadd
2230*0f1bf1c2SAdrian Chadd static usb_error_t
umb_send_encap_command(struct umb_softc * sc,void * data,int len)2231*0f1bf1c2SAdrian Chadd umb_send_encap_command(struct umb_softc *sc, void *data, int len)
2232*0f1bf1c2SAdrian Chadd {
2233*0f1bf1c2SAdrian Chadd usb_device_request_t req;
2234*0f1bf1c2SAdrian Chadd
2235*0f1bf1c2SAdrian Chadd if (len > sc->sc_ctrl_len)
2236*0f1bf1c2SAdrian Chadd return USB_ERR_INVAL;
2237*0f1bf1c2SAdrian Chadd
2238*0f1bf1c2SAdrian Chadd /* XXX FIXME: if (total len > sc->sc_ctrl_len) => must fragment */
2239*0f1bf1c2SAdrian Chadd req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
2240*0f1bf1c2SAdrian Chadd req.bRequest = UCDC_SEND_ENCAPSULATED_COMMAND;
2241*0f1bf1c2SAdrian Chadd USETW(req.wValue, 0);
2242*0f1bf1c2SAdrian Chadd USETW(req.wIndex, sc->sc_ctrl_ifaceno);
2243*0f1bf1c2SAdrian Chadd USETW(req.wLength, len);
2244*0f1bf1c2SAdrian Chadd mtx_unlock(&sc->sc_mutex);
2245*0f1bf1c2SAdrian Chadd DELAY(umb_delay);
2246*0f1bf1c2SAdrian Chadd mtx_lock(&sc->sc_mutex);
2247*0f1bf1c2SAdrian Chadd return usbd_do_request_flags(sc->sc_udev, &sc->sc_mutex, &req, data, 0,
2248*0f1bf1c2SAdrian Chadd NULL, umb_xfer_tout);
2249*0f1bf1c2SAdrian Chadd }
2250*0f1bf1c2SAdrian Chadd
2251*0f1bf1c2SAdrian Chadd static int
umb_get_encap_response(struct umb_softc * sc,void * buf,int * len)2252*0f1bf1c2SAdrian Chadd umb_get_encap_response(struct umb_softc *sc, void *buf, int *len)
2253*0f1bf1c2SAdrian Chadd {
2254*0f1bf1c2SAdrian Chadd usb_device_request_t req;
2255*0f1bf1c2SAdrian Chadd usb_error_t err;
2256*0f1bf1c2SAdrian Chadd uint16_t l = *len;
2257*0f1bf1c2SAdrian Chadd
2258*0f1bf1c2SAdrian Chadd req.bmRequestType = UT_READ_CLASS_INTERFACE;
2259*0f1bf1c2SAdrian Chadd req.bRequest = UCDC_GET_ENCAPSULATED_RESPONSE;
2260*0f1bf1c2SAdrian Chadd USETW(req.wValue, 0);
2261*0f1bf1c2SAdrian Chadd USETW(req.wIndex, sc->sc_ctrl_ifaceno);
2262*0f1bf1c2SAdrian Chadd USETW(req.wLength, l);
2263*0f1bf1c2SAdrian Chadd /* XXX FIXME: re-assemble fragments */
2264*0f1bf1c2SAdrian Chadd
2265*0f1bf1c2SAdrian Chadd mtx_unlock(&sc->sc_mutex);
2266*0f1bf1c2SAdrian Chadd DELAY(umb_delay);
2267*0f1bf1c2SAdrian Chadd mtx_lock(&sc->sc_mutex);
2268*0f1bf1c2SAdrian Chadd err = usbd_do_request_flags(sc->sc_udev, &sc->sc_mutex, &req, buf,
2269*0f1bf1c2SAdrian Chadd USB_SHORT_XFER_OK, &l, umb_xfer_tout);
2270*0f1bf1c2SAdrian Chadd if (err == USB_ERR_NORMAL_COMPLETION) {
2271*0f1bf1c2SAdrian Chadd *len = l;
2272*0f1bf1c2SAdrian Chadd return 1;
2273*0f1bf1c2SAdrian Chadd }
2274*0f1bf1c2SAdrian Chadd DPRINTF("ctrl recv: %s\n", usbd_errstr(err));
2275*0f1bf1c2SAdrian Chadd return 0;
2276*0f1bf1c2SAdrian Chadd }
2277*0f1bf1c2SAdrian Chadd
2278*0f1bf1c2SAdrian Chadd static void
umb_ctrl_msg(struct umb_softc * sc,uint32_t req,void * data,int len)2279*0f1bf1c2SAdrian Chadd umb_ctrl_msg(struct umb_softc *sc, uint32_t req, void *data, int len)
2280*0f1bf1c2SAdrian Chadd {
2281*0f1bf1c2SAdrian Chadd if_t ifp = GET_IFP(sc);
2282*0f1bf1c2SAdrian Chadd uint32_t tid;
2283*0f1bf1c2SAdrian Chadd struct mbim_msghdr *hdr = data;
2284*0f1bf1c2SAdrian Chadd usb_error_t err;
2285*0f1bf1c2SAdrian Chadd
2286*0f1bf1c2SAdrian Chadd if (sc->sc_dying)
2287*0f1bf1c2SAdrian Chadd return;
2288*0f1bf1c2SAdrian Chadd if (len < sizeof (*hdr))
2289*0f1bf1c2SAdrian Chadd return;
2290*0f1bf1c2SAdrian Chadd tid = ++sc->sc_tid;
2291*0f1bf1c2SAdrian Chadd
2292*0f1bf1c2SAdrian Chadd hdr->type = htole32(req);
2293*0f1bf1c2SAdrian Chadd hdr->len = htole32(len);
2294*0f1bf1c2SAdrian Chadd hdr->tid = htole32(tid);
2295*0f1bf1c2SAdrian Chadd
2296*0f1bf1c2SAdrian Chadd #ifdef UMB_DEBUG
2297*0f1bf1c2SAdrian Chadd if (umb_debug) {
2298*0f1bf1c2SAdrian Chadd const char *op, *str;
2299*0f1bf1c2SAdrian Chadd if (req == MBIM_COMMAND_MSG) {
2300*0f1bf1c2SAdrian Chadd struct mbim_h2f_cmd *c = data;
2301*0f1bf1c2SAdrian Chadd if (le32toh(c->op) == MBIM_CMDOP_SET)
2302*0f1bf1c2SAdrian Chadd op = "set";
2303*0f1bf1c2SAdrian Chadd else
2304*0f1bf1c2SAdrian Chadd op = "qry";
2305*0f1bf1c2SAdrian Chadd str = umb_cid2str(le32toh(c->cid));
2306*0f1bf1c2SAdrian Chadd } else {
2307*0f1bf1c2SAdrian Chadd op = "snd";
2308*0f1bf1c2SAdrian Chadd str = umb_request2str(req);
2309*0f1bf1c2SAdrian Chadd }
2310*0f1bf1c2SAdrian Chadd DPRINTF("-> %s %s (tid %u)\n", op, str, tid);
2311*0f1bf1c2SAdrian Chadd }
2312*0f1bf1c2SAdrian Chadd #endif
2313*0f1bf1c2SAdrian Chadd err = umb_send_encap_command(sc, data, len);
2314*0f1bf1c2SAdrian Chadd if (err != USB_ERR_NORMAL_COMPLETION) {
2315*0f1bf1c2SAdrian Chadd if (if_getflags(ifp) & IFF_DEBUG)
2316*0f1bf1c2SAdrian Chadd log(LOG_ERR, "%s: send %s msg (tid %u) failed: %s\n",
2317*0f1bf1c2SAdrian Chadd DEVNAM(sc), umb_request2str(req), tid,
2318*0f1bf1c2SAdrian Chadd usbd_errstr(err));
2319*0f1bf1c2SAdrian Chadd
2320*0f1bf1c2SAdrian Chadd /* will affect other transactions, too */
2321*0f1bf1c2SAdrian Chadd usbd_transfer_stop(sc->sc_xfer[UMB_INTR_RX]);
2322*0f1bf1c2SAdrian Chadd } else {
2323*0f1bf1c2SAdrian Chadd DPRINTFN(2, "sent %s (tid %u)\n",
2324*0f1bf1c2SAdrian Chadd umb_request2str(req), tid);
2325*0f1bf1c2SAdrian Chadd DDUMPN(3, data, len);
2326*0f1bf1c2SAdrian Chadd }
2327*0f1bf1c2SAdrian Chadd return;
2328*0f1bf1c2SAdrian Chadd }
2329*0f1bf1c2SAdrian Chadd
2330*0f1bf1c2SAdrian Chadd static void
umb_open(struct umb_softc * sc)2331*0f1bf1c2SAdrian Chadd umb_open(struct umb_softc *sc)
2332*0f1bf1c2SAdrian Chadd {
2333*0f1bf1c2SAdrian Chadd struct mbim_h2f_openmsg msg;
2334*0f1bf1c2SAdrian Chadd
2335*0f1bf1c2SAdrian Chadd memset(&msg, 0, sizeof (msg));
2336*0f1bf1c2SAdrian Chadd msg.maxlen = htole32(sc->sc_ctrl_len);
2337*0f1bf1c2SAdrian Chadd umb_ctrl_msg(sc, MBIM_OPEN_MSG, &msg, sizeof (msg));
2338*0f1bf1c2SAdrian Chadd return;
2339*0f1bf1c2SAdrian Chadd }
2340*0f1bf1c2SAdrian Chadd
2341*0f1bf1c2SAdrian Chadd static void
umb_close(struct umb_softc * sc)2342*0f1bf1c2SAdrian Chadd umb_close(struct umb_softc *sc)
2343*0f1bf1c2SAdrian Chadd {
2344*0f1bf1c2SAdrian Chadd struct mbim_h2f_closemsg msg;
2345*0f1bf1c2SAdrian Chadd
2346*0f1bf1c2SAdrian Chadd memset(&msg, 0, sizeof (msg));
2347*0f1bf1c2SAdrian Chadd umb_ctrl_msg(sc, MBIM_CLOSE_MSG, &msg, sizeof (msg));
2348*0f1bf1c2SAdrian Chadd }
2349*0f1bf1c2SAdrian Chadd
2350*0f1bf1c2SAdrian Chadd static int
umb_setpin(struct umb_softc * sc,int op,int is_puk,void * pin,int pinlen,void * newpin,int newpinlen)2351*0f1bf1c2SAdrian Chadd umb_setpin(struct umb_softc *sc, int op, int is_puk, void *pin, int pinlen,
2352*0f1bf1c2SAdrian Chadd void *newpin, int newpinlen)
2353*0f1bf1c2SAdrian Chadd {
2354*0f1bf1c2SAdrian Chadd struct mbim_cid_pin cp;
2355*0f1bf1c2SAdrian Chadd int off;
2356*0f1bf1c2SAdrian Chadd
2357*0f1bf1c2SAdrian Chadd if (pinlen == 0)
2358*0f1bf1c2SAdrian Chadd return 0;
2359*0f1bf1c2SAdrian Chadd if (pinlen < 0 || pinlen > MBIM_PIN_MAXLEN ||
2360*0f1bf1c2SAdrian Chadd newpinlen < 0 || newpinlen > MBIM_PIN_MAXLEN ||
2361*0f1bf1c2SAdrian Chadd op < 0 || op > MBIM_PIN_OP_CHANGE ||
2362*0f1bf1c2SAdrian Chadd (is_puk && op != MBIM_PIN_OP_ENTER))
2363*0f1bf1c2SAdrian Chadd return EINVAL;
2364*0f1bf1c2SAdrian Chadd
2365*0f1bf1c2SAdrian Chadd memset(&cp, 0, sizeof (cp));
2366*0f1bf1c2SAdrian Chadd cp.type = htole32(is_puk ? MBIM_PIN_TYPE_PUK1 : MBIM_PIN_TYPE_PIN1);
2367*0f1bf1c2SAdrian Chadd
2368*0f1bf1c2SAdrian Chadd off = offsetof(struct mbim_cid_pin, data);
2369*0f1bf1c2SAdrian Chadd if (!umb_addstr(&cp, sizeof (cp), &off, pin, pinlen,
2370*0f1bf1c2SAdrian Chadd &cp.pin_offs, &cp.pin_size))
2371*0f1bf1c2SAdrian Chadd return EINVAL;
2372*0f1bf1c2SAdrian Chadd
2373*0f1bf1c2SAdrian Chadd cp.op = htole32(op);
2374*0f1bf1c2SAdrian Chadd if (newpinlen) {
2375*0f1bf1c2SAdrian Chadd if (!umb_addstr(&cp, sizeof (cp), &off, newpin, newpinlen,
2376*0f1bf1c2SAdrian Chadd &cp.newpin_offs, &cp.newpin_size))
2377*0f1bf1c2SAdrian Chadd return EINVAL;
2378*0f1bf1c2SAdrian Chadd } else {
2379*0f1bf1c2SAdrian Chadd if ((op == MBIM_PIN_OP_CHANGE) || is_puk)
2380*0f1bf1c2SAdrian Chadd return EINVAL;
2381*0f1bf1c2SAdrian Chadd if (!umb_addstr(&cp, sizeof (cp), &off, NULL, 0,
2382*0f1bf1c2SAdrian Chadd &cp.newpin_offs, &cp.newpin_size))
2383*0f1bf1c2SAdrian Chadd return EINVAL;
2384*0f1bf1c2SAdrian Chadd }
2385*0f1bf1c2SAdrian Chadd mtx_lock(&sc->sc_mutex);
2386*0f1bf1c2SAdrian Chadd umb_cmd(sc, MBIM_CID_PIN, MBIM_CMDOP_SET, &cp, off);
2387*0f1bf1c2SAdrian Chadd mtx_unlock(&sc->sc_mutex);
2388*0f1bf1c2SAdrian Chadd return 0;
2389*0f1bf1c2SAdrian Chadd }
2390*0f1bf1c2SAdrian Chadd
2391*0f1bf1c2SAdrian Chadd static void
umb_setdataclass(struct umb_softc * sc)2392*0f1bf1c2SAdrian Chadd umb_setdataclass(struct umb_softc *sc)
2393*0f1bf1c2SAdrian Chadd {
2394*0f1bf1c2SAdrian Chadd struct mbim_cid_registration_state rs;
2395*0f1bf1c2SAdrian Chadd uint32_t classes;
2396*0f1bf1c2SAdrian Chadd
2397*0f1bf1c2SAdrian Chadd if (sc->sc_info.supportedclasses == MBIM_DATACLASS_NONE)
2398*0f1bf1c2SAdrian Chadd return;
2399*0f1bf1c2SAdrian Chadd
2400*0f1bf1c2SAdrian Chadd memset(&rs, 0, sizeof (rs));
2401*0f1bf1c2SAdrian Chadd rs.regaction = htole32(MBIM_REGACTION_AUTOMATIC);
2402*0f1bf1c2SAdrian Chadd classes = sc->sc_info.supportedclasses;
2403*0f1bf1c2SAdrian Chadd if (sc->sc_info.preferredclasses != MBIM_DATACLASS_NONE)
2404*0f1bf1c2SAdrian Chadd classes &= sc->sc_info.preferredclasses;
2405*0f1bf1c2SAdrian Chadd rs.data_class = htole32(classes);
2406*0f1bf1c2SAdrian Chadd mtx_lock(&sc->sc_mutex);
2407*0f1bf1c2SAdrian Chadd umb_cmd(sc, MBIM_CID_REGISTER_STATE, MBIM_CMDOP_SET, &rs, sizeof (rs));
2408*0f1bf1c2SAdrian Chadd mtx_unlock(&sc->sc_mutex);
2409*0f1bf1c2SAdrian Chadd }
2410*0f1bf1c2SAdrian Chadd
2411*0f1bf1c2SAdrian Chadd static void
umb_radio(struct umb_softc * sc,int on)2412*0f1bf1c2SAdrian Chadd umb_radio(struct umb_softc *sc, int on)
2413*0f1bf1c2SAdrian Chadd {
2414*0f1bf1c2SAdrian Chadd struct mbim_cid_radio_state s;
2415*0f1bf1c2SAdrian Chadd
2416*0f1bf1c2SAdrian Chadd DPRINTF("set radio %s\n", on ? "on" : "off");
2417*0f1bf1c2SAdrian Chadd memset(&s, 0, sizeof (s));
2418*0f1bf1c2SAdrian Chadd s.state = htole32(on ? MBIM_RADIO_STATE_ON : MBIM_RADIO_STATE_OFF);
2419*0f1bf1c2SAdrian Chadd umb_cmd(sc, MBIM_CID_RADIO_STATE, MBIM_CMDOP_SET, &s, sizeof (s));
2420*0f1bf1c2SAdrian Chadd }
2421*0f1bf1c2SAdrian Chadd
2422*0f1bf1c2SAdrian Chadd static void
umb_allocate_cid(struct umb_softc * sc)2423*0f1bf1c2SAdrian Chadd umb_allocate_cid(struct umb_softc *sc)
2424*0f1bf1c2SAdrian Chadd {
2425*0f1bf1c2SAdrian Chadd umb_cmd1(sc, MBIM_CID_DEVICE_CAPS, MBIM_CMDOP_SET,
2426*0f1bf1c2SAdrian Chadd umb_qmi_alloc_cid, sizeof (umb_qmi_alloc_cid), umb_uuid_qmi_mbim);
2427*0f1bf1c2SAdrian Chadd }
2428*0f1bf1c2SAdrian Chadd
2429*0f1bf1c2SAdrian Chadd static void
umb_send_fcc_auth(struct umb_softc * sc)2430*0f1bf1c2SAdrian Chadd umb_send_fcc_auth(struct umb_softc *sc)
2431*0f1bf1c2SAdrian Chadd {
2432*0f1bf1c2SAdrian Chadd uint8_t fccauth[sizeof (umb_qmi_fcc_auth)];
2433*0f1bf1c2SAdrian Chadd
2434*0f1bf1c2SAdrian Chadd if (sc->sc_cid == -1) {
2435*0f1bf1c2SAdrian Chadd DPRINTF("missing CID, cannot send FCC auth\n");
2436*0f1bf1c2SAdrian Chadd umb_allocate_cid(sc);
2437*0f1bf1c2SAdrian Chadd return;
2438*0f1bf1c2SAdrian Chadd }
2439*0f1bf1c2SAdrian Chadd memcpy(fccauth, umb_qmi_fcc_auth, sizeof (fccauth));
2440*0f1bf1c2SAdrian Chadd fccauth[UMB_QMI_CID_OFFS] = sc->sc_cid;
2441*0f1bf1c2SAdrian Chadd umb_cmd1(sc, MBIM_CID_DEVICE_CAPS, MBIM_CMDOP_SET,
2442*0f1bf1c2SAdrian Chadd fccauth, sizeof (fccauth), umb_uuid_qmi_mbim);
2443*0f1bf1c2SAdrian Chadd }
2444*0f1bf1c2SAdrian Chadd
2445*0f1bf1c2SAdrian Chadd static void
umb_packet_service(struct umb_softc * sc,int attach)2446*0f1bf1c2SAdrian Chadd umb_packet_service(struct umb_softc *sc, int attach)
2447*0f1bf1c2SAdrian Chadd {
2448*0f1bf1c2SAdrian Chadd struct mbim_cid_packet_service s;
2449*0f1bf1c2SAdrian Chadd
2450*0f1bf1c2SAdrian Chadd DPRINTF("%s packet service\n",
2451*0f1bf1c2SAdrian Chadd attach ? "attach" : "detach");
2452*0f1bf1c2SAdrian Chadd memset(&s, 0, sizeof (s));
2453*0f1bf1c2SAdrian Chadd s.action = htole32(attach ?
2454*0f1bf1c2SAdrian Chadd MBIM_PKTSERVICE_ACTION_ATTACH : MBIM_PKTSERVICE_ACTION_DETACH);
2455*0f1bf1c2SAdrian Chadd umb_cmd(sc, MBIM_CID_PACKET_SERVICE, MBIM_CMDOP_SET, &s, sizeof (s));
2456*0f1bf1c2SAdrian Chadd }
2457*0f1bf1c2SAdrian Chadd
2458*0f1bf1c2SAdrian Chadd static void
umb_connect(struct umb_softc * sc)2459*0f1bf1c2SAdrian Chadd umb_connect(struct umb_softc *sc)
2460*0f1bf1c2SAdrian Chadd {
2461*0f1bf1c2SAdrian Chadd if_t ifp = GET_IFP(sc);
2462*0f1bf1c2SAdrian Chadd
2463*0f1bf1c2SAdrian Chadd if (sc->sc_info.regstate == MBIM_REGSTATE_ROAMING && !sc->sc_roaming) {
2464*0f1bf1c2SAdrian Chadd log(LOG_INFO, "%s: connection disabled in roaming network\n",
2465*0f1bf1c2SAdrian Chadd DEVNAM(sc));
2466*0f1bf1c2SAdrian Chadd return;
2467*0f1bf1c2SAdrian Chadd }
2468*0f1bf1c2SAdrian Chadd if (if_getflags(ifp) & IFF_DEBUG)
2469*0f1bf1c2SAdrian Chadd log(LOG_DEBUG, "%s: connecting ...\n", DEVNAM(sc));
2470*0f1bf1c2SAdrian Chadd umb_send_connect(sc, MBIM_CONNECT_ACTIVATE);
2471*0f1bf1c2SAdrian Chadd }
2472*0f1bf1c2SAdrian Chadd
2473*0f1bf1c2SAdrian Chadd static void
umb_disconnect(struct umb_softc * sc)2474*0f1bf1c2SAdrian Chadd umb_disconnect(struct umb_softc *sc)
2475*0f1bf1c2SAdrian Chadd {
2476*0f1bf1c2SAdrian Chadd if_t ifp = GET_IFP(sc);
2477*0f1bf1c2SAdrian Chadd
2478*0f1bf1c2SAdrian Chadd if (if_getflags(ifp) & IFF_DEBUG)
2479*0f1bf1c2SAdrian Chadd log(LOG_DEBUG, "%s: disconnecting ...\n", DEVNAM(sc));
2480*0f1bf1c2SAdrian Chadd umb_send_connect(sc, MBIM_CONNECT_DEACTIVATE);
2481*0f1bf1c2SAdrian Chadd }
2482*0f1bf1c2SAdrian Chadd
2483*0f1bf1c2SAdrian Chadd static void
umb_send_connect(struct umb_softc * sc,int command)2484*0f1bf1c2SAdrian Chadd umb_send_connect(struct umb_softc *sc, int command)
2485*0f1bf1c2SAdrian Chadd {
2486*0f1bf1c2SAdrian Chadd struct mbim_cid_connect *c;
2487*0f1bf1c2SAdrian Chadd int off;
2488*0f1bf1c2SAdrian Chadd
2489*0f1bf1c2SAdrian Chadd /* Too large for the stack */
2490*0f1bf1c2SAdrian Chadd mtx_unlock(&sc->sc_mutex);
2491*0f1bf1c2SAdrian Chadd c = malloc(sizeof (*c), M_MBIM_CID_CONNECT, M_WAITOK | M_ZERO);
2492*0f1bf1c2SAdrian Chadd mtx_lock(&sc->sc_mutex);
2493*0f1bf1c2SAdrian Chadd c->sessionid = htole32(umb_session_id);
2494*0f1bf1c2SAdrian Chadd c->command = htole32(command);
2495*0f1bf1c2SAdrian Chadd off = offsetof(struct mbim_cid_connect, data);
2496*0f1bf1c2SAdrian Chadd if (!umb_addstr(c, sizeof (*c), &off, sc->sc_info.apn,
2497*0f1bf1c2SAdrian Chadd sc->sc_info.apnlen, &c->access_offs, &c->access_size))
2498*0f1bf1c2SAdrian Chadd goto done;
2499*0f1bf1c2SAdrian Chadd if (!umb_addstr(c, sizeof (*c), &off, sc->sc_info.username,
2500*0f1bf1c2SAdrian Chadd sc->sc_info.usernamelen, &c->user_offs, &c->user_size))
2501*0f1bf1c2SAdrian Chadd goto done;
2502*0f1bf1c2SAdrian Chadd if (!umb_addstr(c, sizeof (*c), &off, sc->sc_info.password,
2503*0f1bf1c2SAdrian Chadd sc->sc_info.passwordlen, &c->passwd_offs, &c->passwd_size))
2504*0f1bf1c2SAdrian Chadd goto done;
2505*0f1bf1c2SAdrian Chadd c->authprot = htole32(MBIM_AUTHPROT_NONE);
2506*0f1bf1c2SAdrian Chadd c->compression = htole32(MBIM_COMPRESSION_NONE);
2507*0f1bf1c2SAdrian Chadd c->iptype = htole32(MBIM_CONTEXT_IPTYPE_IPV4);
2508*0f1bf1c2SAdrian Chadd memcpy(c->context, umb_uuid_context_internet, sizeof (c->context));
2509*0f1bf1c2SAdrian Chadd umb_cmd(sc, MBIM_CID_CONNECT, MBIM_CMDOP_SET, c, off);
2510*0f1bf1c2SAdrian Chadd done:
2511*0f1bf1c2SAdrian Chadd free(c, M_MBIM_CID_CONNECT);
2512*0f1bf1c2SAdrian Chadd return;
2513*0f1bf1c2SAdrian Chadd }
2514*0f1bf1c2SAdrian Chadd
2515*0f1bf1c2SAdrian Chadd static void
umb_qry_ipconfig(struct umb_softc * sc)2516*0f1bf1c2SAdrian Chadd umb_qry_ipconfig(struct umb_softc *sc)
2517*0f1bf1c2SAdrian Chadd {
2518*0f1bf1c2SAdrian Chadd struct mbim_cid_ip_configuration_info ipc;
2519*0f1bf1c2SAdrian Chadd
2520*0f1bf1c2SAdrian Chadd memset(&ipc, 0, sizeof (ipc));
2521*0f1bf1c2SAdrian Chadd ipc.sessionid = htole32(umb_session_id);
2522*0f1bf1c2SAdrian Chadd umb_cmd(sc, MBIM_CID_IP_CONFIGURATION, MBIM_CMDOP_QRY,
2523*0f1bf1c2SAdrian Chadd &ipc, sizeof (ipc));
2524*0f1bf1c2SAdrian Chadd }
2525*0f1bf1c2SAdrian Chadd
2526*0f1bf1c2SAdrian Chadd static void
umb_cmd(struct umb_softc * sc,int cid,int op,const void * data,int len)2527*0f1bf1c2SAdrian Chadd umb_cmd(struct umb_softc *sc, int cid, int op, const void *data, int len)
2528*0f1bf1c2SAdrian Chadd {
2529*0f1bf1c2SAdrian Chadd umb_cmd1(sc, cid, op, data, len, umb_uuid_basic_connect);
2530*0f1bf1c2SAdrian Chadd }
2531*0f1bf1c2SAdrian Chadd
2532*0f1bf1c2SAdrian Chadd static void
umb_cmd1(struct umb_softc * sc,int cid,int op,const void * data,int len,uint8_t * uuid)2533*0f1bf1c2SAdrian Chadd umb_cmd1(struct umb_softc *sc, int cid, int op, const void *data, int len,
2534*0f1bf1c2SAdrian Chadd uint8_t *uuid)
2535*0f1bf1c2SAdrian Chadd {
2536*0f1bf1c2SAdrian Chadd struct mbim_h2f_cmd *cmd;
2537*0f1bf1c2SAdrian Chadd int totlen;
2538*0f1bf1c2SAdrian Chadd
2539*0f1bf1c2SAdrian Chadd /* XXX FIXME support sending fragments */
2540*0f1bf1c2SAdrian Chadd if (sizeof (*cmd) + len > sc->sc_ctrl_len) {
2541*0f1bf1c2SAdrian Chadd DPRINTF("set %s msg too long: cannot send\n",
2542*0f1bf1c2SAdrian Chadd umb_cid2str(cid));
2543*0f1bf1c2SAdrian Chadd return;
2544*0f1bf1c2SAdrian Chadd }
2545*0f1bf1c2SAdrian Chadd cmd = sc->sc_ctrl_msg;
2546*0f1bf1c2SAdrian Chadd memset(cmd, 0, sizeof (*cmd));
2547*0f1bf1c2SAdrian Chadd cmd->frag.nfrag = htole32(1);
2548*0f1bf1c2SAdrian Chadd memcpy(cmd->devid, uuid, sizeof (cmd->devid));
2549*0f1bf1c2SAdrian Chadd cmd->cid = htole32(cid);
2550*0f1bf1c2SAdrian Chadd cmd->op = htole32(op);
2551*0f1bf1c2SAdrian Chadd cmd->infolen = htole32(len);
2552*0f1bf1c2SAdrian Chadd totlen = sizeof (*cmd);
2553*0f1bf1c2SAdrian Chadd if (len > 0) {
2554*0f1bf1c2SAdrian Chadd memcpy(cmd + 1, data, len);
2555*0f1bf1c2SAdrian Chadd totlen += len;
2556*0f1bf1c2SAdrian Chadd }
2557*0f1bf1c2SAdrian Chadd umb_ctrl_msg(sc, MBIM_COMMAND_MSG, cmd, totlen);
2558*0f1bf1c2SAdrian Chadd }
2559*0f1bf1c2SAdrian Chadd
2560*0f1bf1c2SAdrian Chadd static void
umb_command_done(struct umb_softc * sc,void * data,int len)2561*0f1bf1c2SAdrian Chadd umb_command_done(struct umb_softc *sc, void *data, int len)
2562*0f1bf1c2SAdrian Chadd {
2563*0f1bf1c2SAdrian Chadd struct mbim_f2h_cmddone *cmd = data;
2564*0f1bf1c2SAdrian Chadd if_t ifp = GET_IFP(sc);
2565*0f1bf1c2SAdrian Chadd uint32_t status;
2566*0f1bf1c2SAdrian Chadd uint32_t cid;
2567*0f1bf1c2SAdrian Chadd uint32_t infolen;
2568*0f1bf1c2SAdrian Chadd int qmimsg = 0;
2569*0f1bf1c2SAdrian Chadd
2570*0f1bf1c2SAdrian Chadd if (len < sizeof (*cmd)) {
2571*0f1bf1c2SAdrian Chadd DPRINTF("discard short %s message\n",
2572*0f1bf1c2SAdrian Chadd umb_request2str(le32toh(cmd->hdr.type)));
2573*0f1bf1c2SAdrian Chadd return;
2574*0f1bf1c2SAdrian Chadd }
2575*0f1bf1c2SAdrian Chadd cid = le32toh(cmd->cid);
2576*0f1bf1c2SAdrian Chadd if (memcmp(cmd->devid, umb_uuid_basic_connect, sizeof (cmd->devid))) {
2577*0f1bf1c2SAdrian Chadd if (memcmp(cmd->devid, umb_uuid_qmi_mbim,
2578*0f1bf1c2SAdrian Chadd sizeof (cmd->devid))) {
2579*0f1bf1c2SAdrian Chadd DPRINTF("discard %s message for other UUID '%s'\n",
2580*0f1bf1c2SAdrian Chadd umb_request2str(le32toh(cmd->hdr.type)),
2581*0f1bf1c2SAdrian Chadd umb_uuid2str(cmd->devid));
2582*0f1bf1c2SAdrian Chadd return;
2583*0f1bf1c2SAdrian Chadd } else
2584*0f1bf1c2SAdrian Chadd qmimsg = 1;
2585*0f1bf1c2SAdrian Chadd }
2586*0f1bf1c2SAdrian Chadd
2587*0f1bf1c2SAdrian Chadd status = le32toh(cmd->status);
2588*0f1bf1c2SAdrian Chadd switch (status) {
2589*0f1bf1c2SAdrian Chadd case MBIM_STATUS_SUCCESS:
2590*0f1bf1c2SAdrian Chadd break;
2591*0f1bf1c2SAdrian Chadd case MBIM_STATUS_NOT_INITIALIZED:
2592*0f1bf1c2SAdrian Chadd if (if_getflags(ifp) & IFF_DEBUG)
2593*0f1bf1c2SAdrian Chadd log(LOG_ERR, "%s: SIM not initialized (PIN missing)\n",
2594*0f1bf1c2SAdrian Chadd DEVNAM(sc));
2595*0f1bf1c2SAdrian Chadd return;
2596*0f1bf1c2SAdrian Chadd case MBIM_STATUS_PIN_REQUIRED:
2597*0f1bf1c2SAdrian Chadd sc->sc_info.pin_state = UMB_PIN_REQUIRED;
2598*0f1bf1c2SAdrian Chadd /*FALLTHROUGH*/
2599*0f1bf1c2SAdrian Chadd default:
2600*0f1bf1c2SAdrian Chadd if (if_getflags(ifp) & IFF_DEBUG)
2601*0f1bf1c2SAdrian Chadd log(LOG_ERR, "%s: set/qry %s failed: %s\n", DEVNAM(sc),
2602*0f1bf1c2SAdrian Chadd umb_cid2str(cid), umb_status2str(status));
2603*0f1bf1c2SAdrian Chadd return;
2604*0f1bf1c2SAdrian Chadd }
2605*0f1bf1c2SAdrian Chadd
2606*0f1bf1c2SAdrian Chadd infolen = le32toh(cmd->infolen);
2607*0f1bf1c2SAdrian Chadd if (len < sizeof (*cmd) + infolen) {
2608*0f1bf1c2SAdrian Chadd DPRINTF("discard truncated %s message (want %d, got %d)\n",
2609*0f1bf1c2SAdrian Chadd umb_cid2str(cid),
2610*0f1bf1c2SAdrian Chadd (int)sizeof (*cmd) + infolen, len);
2611*0f1bf1c2SAdrian Chadd return;
2612*0f1bf1c2SAdrian Chadd }
2613*0f1bf1c2SAdrian Chadd if (qmimsg) {
2614*0f1bf1c2SAdrian Chadd if (sc->sc_flags & UMBFLG_FCC_AUTH_REQUIRED)
2615*0f1bf1c2SAdrian Chadd umb_decode_qmi(sc, cmd->info, infolen);
2616*0f1bf1c2SAdrian Chadd } else {
2617*0f1bf1c2SAdrian Chadd DPRINTFN(2, "set/qry %s done\n",
2618*0f1bf1c2SAdrian Chadd umb_cid2str(cid));
2619*0f1bf1c2SAdrian Chadd umb_decode_cid(sc, cid, cmd->info, infolen);
2620*0f1bf1c2SAdrian Chadd }
2621*0f1bf1c2SAdrian Chadd }
2622*0f1bf1c2SAdrian Chadd
2623*0f1bf1c2SAdrian Chadd static void
umb_decode_cid(struct umb_softc * sc,uint32_t cid,void * data,int len)2624*0f1bf1c2SAdrian Chadd umb_decode_cid(struct umb_softc *sc, uint32_t cid, void *data, int len)
2625*0f1bf1c2SAdrian Chadd {
2626*0f1bf1c2SAdrian Chadd int ok = 1;
2627*0f1bf1c2SAdrian Chadd
2628*0f1bf1c2SAdrian Chadd switch (cid) {
2629*0f1bf1c2SAdrian Chadd case MBIM_CID_DEVICE_CAPS:
2630*0f1bf1c2SAdrian Chadd ok = umb_decode_devices_caps(sc, data, len);
2631*0f1bf1c2SAdrian Chadd break;
2632*0f1bf1c2SAdrian Chadd case MBIM_CID_SUBSCRIBER_READY_STATUS:
2633*0f1bf1c2SAdrian Chadd ok = umb_decode_subscriber_status(sc, data, len);
2634*0f1bf1c2SAdrian Chadd break;
2635*0f1bf1c2SAdrian Chadd case MBIM_CID_RADIO_STATE:
2636*0f1bf1c2SAdrian Chadd ok = umb_decode_radio_state(sc, data, len);
2637*0f1bf1c2SAdrian Chadd break;
2638*0f1bf1c2SAdrian Chadd case MBIM_CID_PIN:
2639*0f1bf1c2SAdrian Chadd ok = umb_decode_pin(sc, data, len);
2640*0f1bf1c2SAdrian Chadd break;
2641*0f1bf1c2SAdrian Chadd case MBIM_CID_REGISTER_STATE:
2642*0f1bf1c2SAdrian Chadd ok = umb_decode_register_state(sc, data, len);
2643*0f1bf1c2SAdrian Chadd break;
2644*0f1bf1c2SAdrian Chadd case MBIM_CID_PACKET_SERVICE:
2645*0f1bf1c2SAdrian Chadd ok = umb_decode_packet_service(sc, data, len);
2646*0f1bf1c2SAdrian Chadd break;
2647*0f1bf1c2SAdrian Chadd case MBIM_CID_SIGNAL_STATE:
2648*0f1bf1c2SAdrian Chadd ok = umb_decode_signal_state(sc, data, len);
2649*0f1bf1c2SAdrian Chadd break;
2650*0f1bf1c2SAdrian Chadd case MBIM_CID_CONNECT:
2651*0f1bf1c2SAdrian Chadd ok = umb_decode_connect_info(sc, data, len);
2652*0f1bf1c2SAdrian Chadd break;
2653*0f1bf1c2SAdrian Chadd case MBIM_CID_IP_CONFIGURATION:
2654*0f1bf1c2SAdrian Chadd ok = umb_decode_ip_configuration(sc, data, len);
2655*0f1bf1c2SAdrian Chadd break;
2656*0f1bf1c2SAdrian Chadd default:
2657*0f1bf1c2SAdrian Chadd /*
2658*0f1bf1c2SAdrian Chadd * Note: the above list is incomplete and only contains
2659*0f1bf1c2SAdrian Chadd * mandatory CIDs from the BASIC_CONNECT set.
2660*0f1bf1c2SAdrian Chadd * So alternate values are not unusual.
2661*0f1bf1c2SAdrian Chadd */
2662*0f1bf1c2SAdrian Chadd DPRINTFN(4, "ignore %s\n", umb_cid2str(cid));
2663*0f1bf1c2SAdrian Chadd break;
2664*0f1bf1c2SAdrian Chadd }
2665*0f1bf1c2SAdrian Chadd if (!ok)
2666*0f1bf1c2SAdrian Chadd DPRINTF("discard %s with bad info length %d\n",
2667*0f1bf1c2SAdrian Chadd umb_cid2str(cid), len);
2668*0f1bf1c2SAdrian Chadd return;
2669*0f1bf1c2SAdrian Chadd }
2670*0f1bf1c2SAdrian Chadd
2671*0f1bf1c2SAdrian Chadd static void
umb_decode_qmi(struct umb_softc * sc,uint8_t * data,int len)2672*0f1bf1c2SAdrian Chadd umb_decode_qmi(struct umb_softc *sc, uint8_t *data, int len)
2673*0f1bf1c2SAdrian Chadd {
2674*0f1bf1c2SAdrian Chadd uint8_t srv;
2675*0f1bf1c2SAdrian Chadd uint16_t msg, tlvlen;
2676*0f1bf1c2SAdrian Chadd uint32_t val;
2677*0f1bf1c2SAdrian Chadd
2678*0f1bf1c2SAdrian Chadd #define UMB_QMI_QMUXLEN 6
2679*0f1bf1c2SAdrian Chadd if (len < UMB_QMI_QMUXLEN)
2680*0f1bf1c2SAdrian Chadd goto tooshort;
2681*0f1bf1c2SAdrian Chadd
2682*0f1bf1c2SAdrian Chadd srv = data[4];
2683*0f1bf1c2SAdrian Chadd data += UMB_QMI_QMUXLEN;
2684*0f1bf1c2SAdrian Chadd len -= UMB_QMI_QMUXLEN;
2685*0f1bf1c2SAdrian Chadd
2686*0f1bf1c2SAdrian Chadd #define UMB_GET16(p) ((uint16_t)*p | (uint16_t)*(p + 1) << 8)
2687*0f1bf1c2SAdrian Chadd #define UMB_GET32(p) ((uint32_t)*p | (uint32_t)*(p + 1) << 8 | \
2688*0f1bf1c2SAdrian Chadd (uint32_t)*(p + 2) << 16 |(uint32_t)*(p + 3) << 24)
2689*0f1bf1c2SAdrian Chadd switch (srv) {
2690*0f1bf1c2SAdrian Chadd case 0: /* ctl */
2691*0f1bf1c2SAdrian Chadd #define UMB_QMI_CTLLEN 6
2692*0f1bf1c2SAdrian Chadd if (len < UMB_QMI_CTLLEN)
2693*0f1bf1c2SAdrian Chadd goto tooshort;
2694*0f1bf1c2SAdrian Chadd msg = UMB_GET16(&data[2]);
2695*0f1bf1c2SAdrian Chadd tlvlen = UMB_GET16(&data[4]);
2696*0f1bf1c2SAdrian Chadd data += UMB_QMI_CTLLEN;
2697*0f1bf1c2SAdrian Chadd len -= UMB_QMI_CTLLEN;
2698*0f1bf1c2SAdrian Chadd break;
2699*0f1bf1c2SAdrian Chadd case 2: /* dms */
2700*0f1bf1c2SAdrian Chadd #define UMB_QMI_DMSLEN 7
2701*0f1bf1c2SAdrian Chadd if (len < UMB_QMI_DMSLEN)
2702*0f1bf1c2SAdrian Chadd goto tooshort;
2703*0f1bf1c2SAdrian Chadd msg = UMB_GET16(&data[3]);
2704*0f1bf1c2SAdrian Chadd tlvlen = UMB_GET16(&data[5]);
2705*0f1bf1c2SAdrian Chadd data += UMB_QMI_DMSLEN;
2706*0f1bf1c2SAdrian Chadd len -= UMB_QMI_DMSLEN;
2707*0f1bf1c2SAdrian Chadd break;
2708*0f1bf1c2SAdrian Chadd default:
2709*0f1bf1c2SAdrian Chadd DPRINTF("discard QMI message for unknown service type %d\n",
2710*0f1bf1c2SAdrian Chadd srv);
2711*0f1bf1c2SAdrian Chadd return;
2712*0f1bf1c2SAdrian Chadd }
2713*0f1bf1c2SAdrian Chadd
2714*0f1bf1c2SAdrian Chadd if (len < tlvlen)
2715*0f1bf1c2SAdrian Chadd goto tooshort;
2716*0f1bf1c2SAdrian Chadd
2717*0f1bf1c2SAdrian Chadd #define UMB_QMI_TLVLEN 3
2718*0f1bf1c2SAdrian Chadd while (len > 0) {
2719*0f1bf1c2SAdrian Chadd if (len < UMB_QMI_TLVLEN)
2720*0f1bf1c2SAdrian Chadd goto tooshort;
2721*0f1bf1c2SAdrian Chadd tlvlen = UMB_GET16(&data[1]);
2722*0f1bf1c2SAdrian Chadd if (len < UMB_QMI_TLVLEN + tlvlen)
2723*0f1bf1c2SAdrian Chadd goto tooshort;
2724*0f1bf1c2SAdrian Chadd switch (data[0]) {
2725*0f1bf1c2SAdrian Chadd case 1: /* allocation info */
2726*0f1bf1c2SAdrian Chadd if (msg == 0x0022) { /* Allocate CID */
2727*0f1bf1c2SAdrian Chadd if (tlvlen != 2 || data[3] != 2) /* dms */
2728*0f1bf1c2SAdrian Chadd break;
2729*0f1bf1c2SAdrian Chadd sc->sc_cid = data[4];
2730*0f1bf1c2SAdrian Chadd DPRINTF("QMI CID %d allocated\n",
2731*0f1bf1c2SAdrian Chadd sc->sc_cid);
2732*0f1bf1c2SAdrian Chadd umb_newstate(sc, UMB_S_CID, UMB_NS_DONT_DROP);
2733*0f1bf1c2SAdrian Chadd }
2734*0f1bf1c2SAdrian Chadd break;
2735*0f1bf1c2SAdrian Chadd case 2: /* response */
2736*0f1bf1c2SAdrian Chadd if (tlvlen != sizeof (val))
2737*0f1bf1c2SAdrian Chadd break;
2738*0f1bf1c2SAdrian Chadd val = UMB_GET32(&data[3]);
2739*0f1bf1c2SAdrian Chadd switch (msg) {
2740*0f1bf1c2SAdrian Chadd case 0x0022: /* Allocate CID */
2741*0f1bf1c2SAdrian Chadd if (val != 0) {
2742*0f1bf1c2SAdrian Chadd log(LOG_ERR, "%s: allocation of QMI CID"
2743*0f1bf1c2SAdrian Chadd " failed, error 0x%x\n", DEVNAM(sc),
2744*0f1bf1c2SAdrian Chadd val);
2745*0f1bf1c2SAdrian Chadd /* XXX how to proceed? */
2746*0f1bf1c2SAdrian Chadd return;
2747*0f1bf1c2SAdrian Chadd }
2748*0f1bf1c2SAdrian Chadd break;
2749*0f1bf1c2SAdrian Chadd case 0x555f: /* Send FCC Authentication */
2750*0f1bf1c2SAdrian Chadd if (val == 0)
2751*0f1bf1c2SAdrian Chadd DPRINTF("%s: send FCC "
2752*0f1bf1c2SAdrian Chadd "Authentication succeeded\n",
2753*0f1bf1c2SAdrian Chadd DEVNAM(sc));
2754*0f1bf1c2SAdrian Chadd else if (val == 0x001a0001)
2755*0f1bf1c2SAdrian Chadd DPRINTF("%s: FCC Authentication "
2756*0f1bf1c2SAdrian Chadd "not required\n", DEVNAM(sc));
2757*0f1bf1c2SAdrian Chadd else
2758*0f1bf1c2SAdrian Chadd log(LOG_INFO, "%s: send FCC "
2759*0f1bf1c2SAdrian Chadd "Authentication failed, "
2760*0f1bf1c2SAdrian Chadd "error 0x%x\n", DEVNAM(sc), val);
2761*0f1bf1c2SAdrian Chadd
2762*0f1bf1c2SAdrian Chadd /* FCC Auth is needed only once after power-on*/
2763*0f1bf1c2SAdrian Chadd sc->sc_flags &= ~UMBFLG_FCC_AUTH_REQUIRED;
2764*0f1bf1c2SAdrian Chadd
2765*0f1bf1c2SAdrian Chadd /* Try to proceed anyway */
2766*0f1bf1c2SAdrian Chadd DPRINTF("init: turning radio on ...\n");
2767*0f1bf1c2SAdrian Chadd umb_radio(sc, 1);
2768*0f1bf1c2SAdrian Chadd break;
2769*0f1bf1c2SAdrian Chadd default:
2770*0f1bf1c2SAdrian Chadd break;
2771*0f1bf1c2SAdrian Chadd }
2772*0f1bf1c2SAdrian Chadd break;
2773*0f1bf1c2SAdrian Chadd default:
2774*0f1bf1c2SAdrian Chadd break;
2775*0f1bf1c2SAdrian Chadd }
2776*0f1bf1c2SAdrian Chadd data += UMB_QMI_TLVLEN + tlvlen;
2777*0f1bf1c2SAdrian Chadd len -= UMB_QMI_TLVLEN + tlvlen;
2778*0f1bf1c2SAdrian Chadd }
2779*0f1bf1c2SAdrian Chadd return;
2780*0f1bf1c2SAdrian Chadd
2781*0f1bf1c2SAdrian Chadd tooshort:
2782*0f1bf1c2SAdrian Chadd DPRINTF("discard short QMI message\n");
2783*0f1bf1c2SAdrian Chadd return;
2784*0f1bf1c2SAdrian Chadd }
2785*0f1bf1c2SAdrian Chadd
2786*0f1bf1c2SAdrian Chadd static void
umb_intr(struct usb_xfer * xfer,usb_error_t status)2787*0f1bf1c2SAdrian Chadd umb_intr(struct usb_xfer *xfer, usb_error_t status)
2788*0f1bf1c2SAdrian Chadd {
2789*0f1bf1c2SAdrian Chadd struct umb_softc *sc = usbd_xfer_softc(xfer);
2790*0f1bf1c2SAdrian Chadd struct usb_cdc_notification notification;
2791*0f1bf1c2SAdrian Chadd struct usb_page_cache *pc;
2792*0f1bf1c2SAdrian Chadd if_t ifp = GET_IFP(sc);
2793*0f1bf1c2SAdrian Chadd int total_len;
2794*0f1bf1c2SAdrian Chadd
2795*0f1bf1c2SAdrian Chadd mtx_assert(&sc->sc_mutex, MA_OWNED);
2796*0f1bf1c2SAdrian Chadd
2797*0f1bf1c2SAdrian Chadd /* FIXME use actlen or total_len? */
2798*0f1bf1c2SAdrian Chadd usbd_xfer_status(xfer, &total_len, NULL, NULL, NULL);
2799*0f1bf1c2SAdrian Chadd
2800*0f1bf1c2SAdrian Chadd switch (USB_GET_STATE(xfer)) {
2801*0f1bf1c2SAdrian Chadd case USB_ST_TRANSFERRED:
2802*0f1bf1c2SAdrian Chadd DPRINTF("Received %d bytes\n", total_len);
2803*0f1bf1c2SAdrian Chadd
2804*0f1bf1c2SAdrian Chadd if (total_len < UCDC_NOTIFICATION_LENGTH) {
2805*0f1bf1c2SAdrian Chadd DPRINTF("short notification (%d<%d)\n",
2806*0f1bf1c2SAdrian Chadd total_len, UCDC_NOTIFICATION_LENGTH);
2807*0f1bf1c2SAdrian Chadd return;
2808*0f1bf1c2SAdrian Chadd }
2809*0f1bf1c2SAdrian Chadd
2810*0f1bf1c2SAdrian Chadd pc = usbd_xfer_get_frame(xfer, 0);
2811*0f1bf1c2SAdrian Chadd usbd_copy_out(pc, 0, ¬ification, sizeof (notification));
2812*0f1bf1c2SAdrian Chadd
2813*0f1bf1c2SAdrian Chadd if (notification.bmRequestType != UCDC_NOTIFICATION) {
2814*0f1bf1c2SAdrian Chadd DPRINTF("unexpected notification (type=0x%02x)\n",
2815*0f1bf1c2SAdrian Chadd notification.bmRequestType);
2816*0f1bf1c2SAdrian Chadd return;
2817*0f1bf1c2SAdrian Chadd }
2818*0f1bf1c2SAdrian Chadd
2819*0f1bf1c2SAdrian Chadd switch (notification.bNotification) {
2820*0f1bf1c2SAdrian Chadd case UCDC_N_NETWORK_CONNECTION:
2821*0f1bf1c2SAdrian Chadd if (if_getflags(ifp) & IFF_DEBUG)
2822*0f1bf1c2SAdrian Chadd log(LOG_DEBUG, "%s: network %sconnected\n",
2823*0f1bf1c2SAdrian Chadd DEVNAM(sc),
2824*0f1bf1c2SAdrian Chadd UGETW(notification.wValue)
2825*0f1bf1c2SAdrian Chadd ? "" : "dis");
2826*0f1bf1c2SAdrian Chadd break;
2827*0f1bf1c2SAdrian Chadd case UCDC_N_RESPONSE_AVAILABLE:
2828*0f1bf1c2SAdrian Chadd DPRINTFN(2, "umb_intr: response available\n");
2829*0f1bf1c2SAdrian Chadd ++sc->sc_nresp;
2830*0f1bf1c2SAdrian Chadd umb_add_task(sc, umb_get_response_task,
2831*0f1bf1c2SAdrian Chadd &sc->sc_proc_get_response_task[0].hdr,
2832*0f1bf1c2SAdrian Chadd &sc->sc_proc_get_response_task[1].hdr,
2833*0f1bf1c2SAdrian Chadd 0);
2834*0f1bf1c2SAdrian Chadd break;
2835*0f1bf1c2SAdrian Chadd case UCDC_N_CONNECTION_SPEED_CHANGE:
2836*0f1bf1c2SAdrian Chadd DPRINTFN(2, "umb_intr: connection speed changed\n");
2837*0f1bf1c2SAdrian Chadd break;
2838*0f1bf1c2SAdrian Chadd default:
2839*0f1bf1c2SAdrian Chadd DPRINTF("unexpected notification (0x%02x)\n",
2840*0f1bf1c2SAdrian Chadd notification.bNotification);
2841*0f1bf1c2SAdrian Chadd break;
2842*0f1bf1c2SAdrian Chadd }
2843*0f1bf1c2SAdrian Chadd /* fallthrough */
2844*0f1bf1c2SAdrian Chadd case USB_ST_SETUP:
2845*0f1bf1c2SAdrian Chadd tr_setup:
2846*0f1bf1c2SAdrian Chadd usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
2847*0f1bf1c2SAdrian Chadd usbd_transfer_submit(xfer);
2848*0f1bf1c2SAdrian Chadd break;
2849*0f1bf1c2SAdrian Chadd default:
2850*0f1bf1c2SAdrian Chadd if (status != USB_ERR_CANCELLED) {
2851*0f1bf1c2SAdrian Chadd /* start clear stall */
2852*0f1bf1c2SAdrian Chadd usbd_xfer_set_stall(xfer);
2853*0f1bf1c2SAdrian Chadd goto tr_setup;
2854*0f1bf1c2SAdrian Chadd }
2855*0f1bf1c2SAdrian Chadd break;
2856*0f1bf1c2SAdrian Chadd }
2857*0f1bf1c2SAdrian Chadd }
2858*0f1bf1c2SAdrian Chadd
2859*0f1bf1c2SAdrian Chadd /*
2860*0f1bf1c2SAdrian Chadd * Diagnostic routines
2861*0f1bf1c2SAdrian Chadd */
2862*0f1bf1c2SAdrian Chadd static char *
umb_ntop(struct sockaddr * sa)2863*0f1bf1c2SAdrian Chadd umb_ntop(struct sockaddr *sa)
2864*0f1bf1c2SAdrian Chadd {
2865*0f1bf1c2SAdrian Chadd #define NUMBUFS 4
2866*0f1bf1c2SAdrian Chadd static char astr[NUMBUFS][INET_ADDRSTRLEN];
2867*0f1bf1c2SAdrian Chadd static unsigned nbuf = 0;
2868*0f1bf1c2SAdrian Chadd char *s;
2869*0f1bf1c2SAdrian Chadd
2870*0f1bf1c2SAdrian Chadd s = astr[nbuf++];
2871*0f1bf1c2SAdrian Chadd if (nbuf >= NUMBUFS)
2872*0f1bf1c2SAdrian Chadd nbuf = 0;
2873*0f1bf1c2SAdrian Chadd
2874*0f1bf1c2SAdrian Chadd switch (sa->sa_family) {
2875*0f1bf1c2SAdrian Chadd case AF_INET:
2876*0f1bf1c2SAdrian Chadd default:
2877*0f1bf1c2SAdrian Chadd inet_ntop(AF_INET, &satosin(sa)->sin_addr, s, sizeof (astr[0]));
2878*0f1bf1c2SAdrian Chadd break;
2879*0f1bf1c2SAdrian Chadd case AF_INET6:
2880*0f1bf1c2SAdrian Chadd inet_ntop(AF_INET6, &satosin6(sa)->sin6_addr, s,
2881*0f1bf1c2SAdrian Chadd sizeof (astr[0]));
2882*0f1bf1c2SAdrian Chadd break;
2883*0f1bf1c2SAdrian Chadd }
2884*0f1bf1c2SAdrian Chadd return s;
2885*0f1bf1c2SAdrian Chadd }
2886*0f1bf1c2SAdrian Chadd
2887*0f1bf1c2SAdrian Chadd #ifdef UMB_DEBUG
2888*0f1bf1c2SAdrian Chadd static char *
umb_uuid2str(uint8_t uuid[MBIM_UUID_LEN])2889*0f1bf1c2SAdrian Chadd umb_uuid2str(uint8_t uuid[MBIM_UUID_LEN])
2890*0f1bf1c2SAdrian Chadd {
2891*0f1bf1c2SAdrian Chadd static char uuidstr[2 * MBIM_UUID_LEN + 5];
2892*0f1bf1c2SAdrian Chadd
2893*0f1bf1c2SAdrian Chadd #define UUID_BFMT "%02X"
2894*0f1bf1c2SAdrian Chadd #define UUID_SEP "-"
2895*0f1bf1c2SAdrian Chadd snprintf(uuidstr, sizeof (uuidstr),
2896*0f1bf1c2SAdrian Chadd UUID_BFMT UUID_BFMT UUID_BFMT UUID_BFMT UUID_SEP
2897*0f1bf1c2SAdrian Chadd UUID_BFMT UUID_BFMT UUID_SEP
2898*0f1bf1c2SAdrian Chadd UUID_BFMT UUID_BFMT UUID_SEP
2899*0f1bf1c2SAdrian Chadd UUID_BFMT UUID_BFMT UUID_SEP
2900*0f1bf1c2SAdrian Chadd UUID_BFMT UUID_BFMT UUID_BFMT UUID_BFMT UUID_BFMT UUID_BFMT,
2901*0f1bf1c2SAdrian Chadd uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5],
2902*0f1bf1c2SAdrian Chadd uuid[6], uuid[7], uuid[8], uuid[9], uuid[10], uuid[11],
2903*0f1bf1c2SAdrian Chadd uuid[12], uuid[13], uuid[14], uuid[15]);
2904*0f1bf1c2SAdrian Chadd return uuidstr;
2905*0f1bf1c2SAdrian Chadd }
2906*0f1bf1c2SAdrian Chadd
2907*0f1bf1c2SAdrian Chadd static void
umb_dump(void * buf,int len)2908*0f1bf1c2SAdrian Chadd umb_dump(void *buf, int len)
2909*0f1bf1c2SAdrian Chadd {
2910*0f1bf1c2SAdrian Chadd int i = 0;
2911*0f1bf1c2SAdrian Chadd uint8_t *c = buf;
2912*0f1bf1c2SAdrian Chadd
2913*0f1bf1c2SAdrian Chadd if (len == 0)
2914*0f1bf1c2SAdrian Chadd return;
2915*0f1bf1c2SAdrian Chadd while (i < len) {
2916*0f1bf1c2SAdrian Chadd if ((i % 16) == 0) {
2917*0f1bf1c2SAdrian Chadd if (i > 0)
2918*0f1bf1c2SAdrian Chadd log(LOG_DEBUG, "\n");
2919*0f1bf1c2SAdrian Chadd log(LOG_DEBUG, "%4d: ", i);
2920*0f1bf1c2SAdrian Chadd }
2921*0f1bf1c2SAdrian Chadd log(LOG_DEBUG, " %02x", *c);
2922*0f1bf1c2SAdrian Chadd c++;
2923*0f1bf1c2SAdrian Chadd i++;
2924*0f1bf1c2SAdrian Chadd }
2925*0f1bf1c2SAdrian Chadd log(LOG_DEBUG, "\n");
2926*0f1bf1c2SAdrian Chadd }
2927*0f1bf1c2SAdrian Chadd #endif /* UMB_DEBUG */
2928*0f1bf1c2SAdrian Chadd
2929*0f1bf1c2SAdrian Chadd DRIVER_MODULE(umb, uhub, umb_driver, NULL, NULL);
2930*0f1bf1c2SAdrian Chadd MODULE_DEPEND(umb, usb, 1, 1, 1);
2931