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