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