FreeBSD Bugzilla – Attachment 255918 Details for
Bug 263783
USB MBIM: Support for LTE/4G USB modems (MBIM)
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
[patch]
0001-Introduce-the-USB-umb-4-network-driver.patch
0001-Introduce-the-USB-umb-4-network-driver.patch (text/plain), 152.13 KB, created by
Pierre Pronchery
on 2024-12-17 16:18:33 UTC
(
hide
)
Description:
0001-Introduce-the-USB-umb-4-network-driver.patch
Filename:
MIME Type:
Creator:
Pierre Pronchery
Created:
2024-12-17 16:18:33 UTC
Size:
152.13 KB
patch
obsolete
>From a09355160a388e2a48013116f894585247a30a08 Mon Sep 17 00:00:00 2001 >From: Pierre Pronchery <khorben@defora.org> >Date: Mon, 29 Apr 2024 20:20:56 +0200 >Subject: [PATCH] Introduce the USB umb(4) network driver > >This includes: > >* The umb(4) kernel driver >* The umbctl(8) companion tool >* Corresponding changes to ifconfig(8) (media type...) >--- > lib/libifconfig/libifconfig_media.c | 5 + > sbin/Makefile | 1 + > sbin/umbctl/Makefile | 8 + > sbin/umbctl/umbctl.8 | 172 ++ > sbin/umbctl/umbctl.c | 767 +++++++ > share/man/man4/Makefile | 1 + > share/man/man4/umb.4 | 90 + > sys/conf/files | 1 + > sys/dev/usb/net/if_umb.c | 2931 +++++++++++++++++++++++++++ > sys/dev/usb/net/if_umbreg.h | 441 ++++ > sys/dev/usb/net/mbim.h | 724 +++++++ > sys/modules/usb/Makefile | 2 +- > sys/modules/usb/umb/Makefile | 35 + > sys/net/if_media.c | 11 + > sys/net/if_media.h | 40 + > sys/net/if_types.h | 1 + > sys/sys/sockio.h | 4 + > 17 files changed, 5233 insertions(+), 1 deletion(-) > create mode 100644 sbin/umbctl/Makefile > create mode 100644 sbin/umbctl/umbctl.8 > create mode 100644 sbin/umbctl/umbctl.c > create mode 100644 share/man/man4/umb.4 > create mode 100644 sys/dev/usb/net/if_umb.c > create mode 100644 sys/dev/usb/net/if_umbreg.h > create mode 100644 sys/dev/usb/net/mbim.h > create mode 100644 sys/modules/usb/umb/Makefile > >diff --git a/lib/libifconfig/libifconfig_media.c b/lib/libifconfig/libifconfig_media.c >index 1f74bde03461..739847bbd254 100644 >--- a/lib/libifconfig/libifconfig_media.c >+++ b/lib/libifconfig/libifconfig_media.c >@@ -78,6 +78,10 @@ static const struct ifmedia_description > ifm_subtype_ethernet_option_descriptions[] = > IFM_SUBTYPE_ETHERNET_OPTION_DESCRIPTIONS; > >+static const struct ifmedia_description >+ ifm_subtype_mbim_descriptions[] = >+ IFM_SUBTYPE_MBIM_DESCRIPTIONS; >+ > static const struct ifmedia_description > ifm_subtype_ieee80211_descriptions[] = > IFM_SUBTYPE_IEEE80211_DESCRIPTIONS; >@@ -492,6 +496,7 @@ ifconfig_media_get_status(const struct ifmediareq *ifmr) > switch (IFM_TYPE(ifmr->ifm_active)) { > case IFM_ETHER: > case IFM_ATM: >+ case IFM_MBIM: > if (ifmr->ifm_status & IFM_ACTIVE) { > return ("active"); > } else { >diff --git a/sbin/Makefile b/sbin/Makefile >index 790112b05f6f..5e5a8943c67a 100644 >--- a/sbin/Makefile >+++ b/sbin/Makefile >@@ -63,6 +63,7 @@ SUBDIR=adjkerntz \ > swapon \ > sysctl \ > tunefs \ >+ umbctl \ > umount > > .if ${MK_INET} != "no" || ${MK_INET6} != "no" >diff --git a/sbin/umbctl/Makefile b/sbin/umbctl/Makefile >new file mode 100644 >index 000000000000..35afb1bcfd4b >--- /dev/null >+++ b/sbin/umbctl/Makefile >@@ -0,0 +1,8 @@ >+CFLAGS+= -I${SRCTOP}/sys/dev/usb/net >+ >+PROG= umbctl >+MAN= umbctl.8 >+ >+BINDIR= /sbin >+ >+.include <bsd.prog.mk> >diff --git a/sbin/umbctl/umbctl.8 b/sbin/umbctl/umbctl.8 >new file mode 100644 >index 000000000000..28d820d26450 >--- /dev/null >+++ b/sbin/umbctl/umbctl.8 >@@ -0,0 +1,172 @@ >+.\" $NetBSD$ >+.\" >+.\" Copyright (c) 2018 by Pierre Pronchery <khorben@defora.org> >+.\" All rights reserved. >+.\" >+.\" Redistribution and use in source and binary forms, with or without >+.\" modification, are permitted provided that the following conditions >+.\" are met: >+.\" 1. Redistributions of source code must retain the above copyright >+.\" notice, this list of conditions and the following disclaimer. >+.\" 2. Redistributions in binary form must reproduce the above copyright >+.\" notice, this list of conditions and the following disclaimer in the >+.\" documentation and/or other materials provided with the distribution. >+.\" >+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS >+.\" OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED >+.\" WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE >+.\" DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, >+.\" INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES >+.\" (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR >+.\" SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) >+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, >+.\" STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING >+.\" IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE >+.\" POSSIBILITY OF SUCH DAMAGE. >+.\" >+.\" From: pppoectl.8,v 1.30 2016/09/12 05:35:20 sevan Exp $ >+.\" >+.\" $Id$ >+.\" >+.\" last edit-date: [Thu Aug 31 10:47:33 2000] >+.\" >+.Dd March 22, 2020 >+.Dt UMBCTL 8 >+.Os >+.Sh NAME >+.Nm umbctl >+.Nd display or set MBIM cellular modem interface parameters (4G/LTE) >+.Sh SYNOPSIS >+.Nm umbctl >+.Op Fl v >+.Ar ifname >+.Op Ar parameter Op Ar value >+.Ar ... >+.Pp >+.Nm umbctl >+.Op Fl v >+.Fl f Ar config-file >+.Ar ifname >+.Pp >+.Nm umbctl >+.Op Fl v >+.Fl m Ar format >+.Ar ifname >+.Pp >+.Sh DESCRIPTION >+.Nm >+supports the following options: >+.Bl -tag -width XfXconfigXfileXXX -offset indent >+.It Fl f Ar config-file >+Parse >+.Ar config-file >+for >+.Ar parameter Ns Op \&= Ns Ar value >+pairs, one per line, as if they had been specified on the command line. >+This allows the password or PIN codes to be not passed as command line >+arguments. >+Comments starting with # to the end of the current line are ignored. >+.It Fl m Ar format >+Displays the current settings in the >+.Ar format >+specified. >+.It Fl v >+Enables verbose mode. >+.El >+.Pp >+The >+.Xr umb 4 >+driver may require a number of additional arguments or optional >+parameters besides the settings that can be adjusted with >+.Xr ifconfig 8 . >+These may be credentials or other tunable connectivity variables. >+The >+.Nm >+utility can be used to display the current settings, or adjust these >+parameters as required. >+.Pp >+For whatever intent >+.Nm >+is being called, at least the parameter >+.Ar ifname >+needs to be specified, naming the interface for which the settings >+are to be performed or displayed. >+Use >+.Xr ifconfig 8 >+or >+.Xr netstat 1 >+to see which interfaces are available. >+.Pp >+If no other parameter is given, >+.Nm >+will just list the current status for >+.Ar ifname >+and exit. >+.Pp >+If any additional parameter is supplied, superuser privileges are >+required, and the command works in >+.Ql set >+mode. >+This is normally done quietly, unless the option >+.Fl v >+is also enabled, which will cause a final printout of the status as >+described above once all other actions have been taken. >+.Pp >+The parameters currently supported include: >+.Bl -tag -width xxxxxxxxxxxxxxxxxxxxxxxxx -offset indent >+.It Ar apn Ns \&= Ns Em access-point >+Set the APN to >+.Em access-point . >+.It Ar username Ns \&= Ns Em username >+Set the username to >+.Em username . >+.It Ar password Ns \&= Ns Em password >+Set the password to >+.Em password . >+.It Ar pin Ns \&= Ns Em pin-code >+Enter the PIN >+.Em pin-code . >+.It Ar puk Ns \&= Ns Em puk-code >+Enter the PUK >+.Em puk-code . >+.It Ar roaming >+Allow data connections when roaming. >+.It Ar -roaming >+Deny data connections when roaming. >+.El >+.Sh EXAMPLES >+.Bd -literal >+# umbctl umb0 >+umb0: state up, mode automatic, registration home network >+ provider "BSD-Net", dataclass LTE, signal good >+ phone number "+15554242", roaming "" (denied) >+ APN "", TX 50000000, RX 100000000 >+ firmware "MBIM_FW_V1.0", hardware "MBIM_HW_V1.0" >+.Ed >+.Pp >+Display the settings for umb0. >+.Bd -literal >+# umbctl umb0 apn operator.internet username mobile password mobile >+.Ed >+.Pp >+Configure the connection parameters for umb0 from the command line. >+.Bd -literal >+# umbctl -f /dev/stdin umb0 << EOF >+pin=1234 >+EOF >+.Ed >+.Pp >+Configure the connection parameters for umb0 from a file. >+.Sh SEE ALSO >+.Xr netstat 1 , >+.Xr umb 4 , >+.Xr ifconfig 8 , >+.Xr ifwatchd 8 >+.Sh HISTORY >+The >+.Nm >+utility first appeared in >+.Nx 9.0 . >+.Sh AUTHORS >+The program was written by >+.An Pierre Pronchery . >diff --git a/sbin/umbctl/umbctl.c b/sbin/umbctl/umbctl.c >new file mode 100644 >index 000000000000..88167baf40e7 >--- /dev/null >+++ b/sbin/umbctl/umbctl.c >@@ -0,0 +1,767 @@ >+/* $FreeBSD$ */ >+/* $NetBSD$ */ >+/* >+ * Original copyright (c) 2018 Pierre Pronchery <khorben@defora.org> (for the >+ * NetBSD Project) >+ * >+ * Redistribution and use in source and binary forms, with or without >+ * modification, are permitted provided that the following conditions >+ * are met: >+ * 1. Redistributions of source code must retain the above copyright >+ * notice, this list of conditions and the following disclaimer. >+ * 2. Redistributions in binary form must reproduce the above copyright >+ * notice, this list of conditions and the following disclaimer in the >+ * documentation and/or other materials provided with the distribution. >+ * >+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY EXPRESS OR >+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES >+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. >+ * IN NO EVENT SHALL THE DEVELOPERS BE LIABLE FOR ANY DIRECT, INDIRECT, >+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT >+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, >+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY >+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT >+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF >+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. >+ * >+ * Copyright (c) 2022 ADISTA SAS (FreeBSD updates) >+ * >+ * Updates for FreeBSD by Pierre Pronchery <pierre@defora.net> >+ * >+ * Redistribution and use in source and binary forms, with or without >+ * modification, are permitted provided that the following conditions are met: >+ * >+ * - Redistributions of source code must retain the above copyright notice, >+ * this list of conditions and the following disclaimer. >+ * - Redistributions in binary form must reproduce the above copyright notice, >+ * this list of conditions and the following disclaimer in the documentation >+ * and/or other materials provided with the distribution. >+ * - Neither the name of the copyright holder nor the names of its contributors >+ * may be used to endorse or promote products derived from this software >+ * without specific prior written permission. >+ * >+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" >+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE >+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE >+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE >+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR >+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF >+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS >+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN >+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) >+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE >+ * POSSIBILITY OF SUCH DAMAGE. >+ */ >+ >+ >+ >+#include <sys/endian.h> >+#include <sys/ioctl.h> >+#include <sys/socket.h> >+#include <sys/sockio.h> >+ >+#include <net/if.h> >+#include <netinet/in.h> >+#include <arpa/inet.h> >+ >+#include <ctype.h> >+#include <errno.h> >+#include <inttypes.h> >+#include <stdarg.h> >+#include <stdio.h> >+#include <string.h> >+#include <strings.h> >+#include <unistd.h> >+ >+#include "mbim.h" >+#include "if_umbreg.h" >+ >+ >+/* constants */ >+static const struct umb_valdescr _umb_actstate[] = >+ MBIM_ACTIVATION_STATE_DESCRIPTIONS; >+ >+static const struct umb_valdescr _umb_simstate[] = >+ MBIM_SIMSTATE_DESCRIPTIONS; >+ >+static const struct umb_valdescr _umb_regstate[] = >+ MBIM_REGSTATE_DESCRIPTIONS; >+ >+static const struct umb_valdescr _umb_pktstate[] = >+ MBIM_PKTSRV_STATE_DESCRIPTIONS; >+ >+static const struct umb_valdescr _umb_dataclass[] = >+ MBIM_DATACLASS_DESCRIPTIONS; >+ >+static const struct umb_valdescr _umb_state[] = >+ UMB_INTERNAL_STATE_DESCRIPTIONS; >+ >+static const struct umb_valdescr _umb_pin_state[] = >+{ >+ { UMB_PIN_REQUIRED, "PIN required"}, >+ { UMB_PIN_UNLOCKED, "PIN unlocked"}, >+ { UMB_PUK_REQUIRED, "PUK required"}, >+ { 0, NULL } >+}; >+ >+static const struct umb_valdescr _umb_regmode[] = >+{ >+ { MBIM_REGMODE_UNKNOWN, "unknown" }, >+ { MBIM_REGMODE_AUTOMATIC, "automatic" }, >+ { MBIM_REGMODE_MANUAL, "manual" }, >+ { 0, NULL } >+}; >+ >+static const struct umb_valdescr _umb_ber[] = >+{ >+ { UMB_BER_EXCELLENT, "excellent" }, >+ { UMB_BER_VERYGOOD, "very good" }, >+ { UMB_BER_GOOD, "good" }, >+ { UMB_BER_OK, "ok" }, >+ { UMB_BER_MEDIUM, "medium" }, >+ { UMB_BER_BAD, "bad" }, >+ { UMB_BER_VERYBAD, "very bad" }, >+ { UMB_BER_EXTREMELYBAD, "extremely bad" }, >+ { 0, NULL } >+}; >+ >+ >+/* prototypes */ >+static int _char_to_utf16(const char * in, uint16_t * out, size_t outlen); >+static int _error(int ret, char const * format, ...); >+static int _umbctl(char const * ifname, int verbose, int argc, char * argv[]); >+static int _umbctl_file(char const * ifname, char const * filename, >+ int verbose); >+static int _umbctl_dump(char const * ifname, const char * format, int verbose); >+static void _umbctl_dump_json(char const * ifname, struct umb_info * umbi); >+static int _umbctl_gateway(char const * ifname); >+static void _umbctl_info(char const * ifname, struct umb_info * umbi); >+static int _umbctl_ioctl(char const * ifname, int fd, unsigned long request, >+ struct ifreq * ifr); >+static int _umbctl_set(char const * ifname, struct umb_parameter * umbp, >+ int argc, char * argv[]); >+static int _umbctl_socket(void); >+static int _usage(void); >+static void _print_utf16(uint16_t * in, int inlen); >+static void _utf16_to_char(uint16_t * in, int inlen, char * out, size_t outlen); >+ >+ >+/* functions */ >+/* char_to_utf16 */ >+/* this function is from OpenBSD's ifconfig(8) */ >+static int _char_to_utf16(const char * in, uint16_t * out, size_t outlen) >+{ >+ int n = 0; >+ uint16_t c; >+ >+ for (;;) { >+ c = *in++; >+ >+ if (c == '\0') { >+ /* >+ * NUL termination is not required, but zero out the >+ * residual buffer >+ */ >+ memset(out, 0, outlen); >+ return n; >+ } >+ if (outlen < sizeof(*out)) >+ return -1; >+ >+ *out++ = htole16(c); >+ n += sizeof(*out); >+ outlen -= sizeof(*out); >+ } >+} >+ >+ >+/* error */ >+static int _error(int ret, char const * format, ...) >+{ >+ va_list ap; >+ >+ fputs("umbctl: ", stderr); >+ va_start(ap, format); >+ vfprintf(stderr, format, ap); >+ va_end(ap); >+ fputs("\n", stderr); >+ return ret; >+} >+ >+ >+/* umbctl */ >+static int _umbctl(char const * ifname, int verbose, int argc, char * argv[]) >+{ >+ int fd; >+ struct ifreq ifr; >+ struct umb_info umbi; >+ struct umb_parameter umbp; >+ >+ if((fd = _umbctl_socket()) < 0) >+ return 2; >+ memset(&ifr, 0, sizeof(ifr)); >+ strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); >+ if(argc != 0) >+ { >+ memset(&umbp, 0, sizeof(umbp)); >+ ifr.ifr_data = (caddr_t)&umbp; >+ if(_umbctl_ioctl(ifname, fd, SIOCGUMBPARAM, &ifr) != 0 >+ || _umbctl_set(ifname, &umbp, argc, argv) != 0 >+ || _umbctl_ioctl(ifname, fd, SIOCSUMBPARAM, >+ &ifr) != 0) >+ { >+ close(fd); >+ return 2; >+ } >+ } >+ if(argc == 0 || verbose > 0) >+ { >+ ifr.ifr_data = (caddr_t)&umbi; >+ if(_umbctl_ioctl(ifname, fd, SIOCGUMBINFO, &ifr) != 0) >+ { >+ close(fd); >+ return 3; >+ } >+ _umbctl_info(ifname, &umbi); >+ } >+ if(close(fd) != 0) >+ return _error(2, "%s: %s", ifname, strerror(errno)); >+ return 0; >+} >+ >+ >+/* umbctl_file */ >+static int _file_parse(char const * ifname, struct umb_parameter * umbp, >+ char const * filename); >+ >+static int _umbctl_file(char const * ifname, char const * filename, int verbose) >+{ >+ int fd; >+ struct ifreq ifr; >+ struct umb_parameter umbp; >+ struct umb_info umbi; >+ >+ if((fd = _umbctl_socket()) < 0) >+ return 2; >+ memset(&ifr, 0, sizeof(ifr)); >+ strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); >+ ifr.ifr_data = (caddr_t)&umbp; >+ memset(&umbp, 0, sizeof(umbp)); >+ if(_umbctl_ioctl(ifname, fd, SIOCGUMBPARAM, &ifr) != 0 >+ || _file_parse(ifname, &umbp, filename) != 0 >+ || _umbctl_ioctl(ifname, fd, SIOCSUMBPARAM, &ifr) != 0) >+ { >+ close(fd); >+ return 2; >+ } >+ if(verbose > 0) >+ { >+ ifr.ifr_data = (caddr_t)&umbi; >+ if(_umbctl_ioctl(ifname, fd, SIOCGUMBINFO, &ifr) != 0) >+ { >+ close(fd); >+ return 3; >+ } >+ _umbctl_info(ifname, &umbi); >+ } >+ if(close(fd) != 0) >+ return _error(2, "%s: %s", ifname, strerror(errno)); >+ return 0; >+} >+ >+static int _file_parse(char const * ifname, struct umb_parameter * umbp, >+ char const * filename) >+{ >+ int ret = 0; >+ FILE * fp; >+ char buf[512]; >+ size_t len; >+ int i; >+ int eof; >+ char * tokens[3] = { buf, NULL, NULL }; >+ char * p; >+ >+ if((fp = fopen(filename, "r")) == NULL) >+ return _error(2, "%s: %s", filename, strerror(errno)); >+ while(fgets(buf, sizeof(buf), fp) != NULL) >+ { >+ if(buf[0] == '#') >+ continue; >+ buf[sizeof(buf) - 1] = '\0'; >+ if((len = strlen(buf)) > 0) >+ { >+ if(buf[len - 1] != '\n') >+ { >+ ret = _error(2, "%s: %s", filename, >+ "Line too long"); >+ while((i = fgetc(fp)) != EOF && i != '\n'); >+ continue; >+ } >+ buf[len - 1] = '\0'; >+ } >+ if((p = strchr(buf, '=')) != NULL) >+ { >+ tokens[1] = p + 1; >+ *p = '\0'; >+ } else >+ tokens[1] = NULL; >+ ret |= _umbctl_set(ifname, umbp, (p != NULL) ? 2 : 1, tokens) >+ ? 2 : 0; >+ } >+ eof = feof(fp); >+ if(fclose(fp) != 0 || !eof) >+ return _error(2, "%s: %s", filename, strerror(errno)); >+ return ret; >+} >+ >+ >+/* umbctl_dump */ >+static int _umbctl_dump(char const * ifname, const char * format, int verbose) >+{ >+ int fd; >+ struct ifreq ifr; >+ struct umb_info umbi; >+ (void) verbose; >+ >+ if((fd = _umbctl_socket()) < 0) >+ return 2; >+ memset(&ifr, 0, sizeof(ifr)); >+ strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); >+ ifr.ifr_data = (caddr_t)&umbi; >+ if(_umbctl_ioctl(ifname, fd, SIOCGUMBINFO, &ifr) != 0) >+ { >+ close(fd); >+ return 3; >+ } >+ if(close(fd) != 0) >+ return _error(2, "%s: %s", ifname, strerror(errno)); >+ if(strncasecmp(format, "JSON", 4) == 0) >+ _umbctl_dump_json(ifname, &umbi); >+ else >+ return _error(2, "%s: Unsupported format", format); >+ return 0; >+} >+ >+ >+/* umbctl_dump_json */ >+static void _umbctl_dump_json(char const * ifname, struct umb_info * umbi) >+{ >+#define JSON_START printf("{") >+#define JSON_END printf("}\n") >+#define JSON_SEP printf(", ") >+#define JSON_ARRAY_START printf("[") >+#define JSON_ARRAY_END printf("]") >+ >+#define JSON_STRING(key, val) printf("\"%s\": \"%s\"", key, val) >+#define JSON_INT(key, val) printf("\"%s\": %d", key, val) >+#define JSON_ULONG(key, val) printf("\"%s\": %lu", key, val) >+#define JSON_BOOL(key, val) printf("\"%s\": %s", key, (val)?"true":"false") >+#define JSON_NULL(key) printf("\"%s\": null", key) >+#define JSON_KEY(key) printf("\"%s\": ", key) >+ >+#define JSON_WSTRING(key, val, len) (printf("\"%s\": \"", key),\ >+ _print_utf16(val, len),printf("\"")) >+ >+#define JSON_NULL_EMPTY(key, val) (*val != '\0')?JSON_STRING(key, val):\ >+ JSON_NULL(key) >+#define JSON_NULL_WEMPTY(key, val, len) ((char)htole16(*val) != '\0')?\ >+ JSON_WSTRING(key, val, len):\ >+ JSON_NULL(key) >+ >+ JSON_START; >+ JSON_STRING("interface", ifname); >+ JSON_SEP; >+ JSON_NULL_EMPTY("state", umb_val2descr(_umb_state, umbi->state)); >+ JSON_SEP; >+ JSON_NULL_EMPTY("pin-status", umb_val2descr(_umb_pin_state, umbi->pin_state)); >+ JSON_SEP; >+ JSON_INT("pin-attempts-left", umbi->pin_attempts_left); >+ JSON_SEP; >+ JSON_NULL_EMPTY("activation", umb_val2descr(_umb_actstate, umbi->activation)); >+ JSON_SEP; >+ JSON_NULL_EMPTY("sim-state", umb_val2descr(_umb_simstate, umbi->sim_state)); >+ JSON_SEP; >+ JSON_NULL_EMPTY("sim-registration", umb_val2descr(_umb_regstate, umbi->regstate)); >+ JSON_SEP; >+ JSON_NULL_EMPTY("sim-registration-mode", umb_val2descr(_umb_regmode, umbi->regmode)); >+ JSON_SEP; >+ JSON_INT("network-errors", umbi->nwerror); >+ JSON_SEP; >+ JSON_NULL_EMPTY("packet-state", umb_val2descr(_umb_pktstate, umbi->packetstate)); >+ JSON_SEP; >+ JSON_NULL_EMPTY("supported-dataclasses", umb_val2descr(_umb_dataclass, umbi->supportedclasses)); >+ JSON_SEP; >+ JSON_NULL_EMPTY("preferred-dataclasses", umb_val2descr(_umb_dataclass, umbi->preferredclasses)); >+ JSON_SEP; >+ JSON_NULL_EMPTY("offered-dataclasses", umb_val2descr(_umb_dataclass, umbi->highestclass)); >+ JSON_SEP; >+ JSON_NULL_EMPTY("dataclass", umb_val2descr(_umb_dataclass, umbi->cellclass)); >+ JSON_SEP; >+ JSON_NULL_WEMPTY("provider", umbi->provider, UMB_PROVIDERNAME_MAXLEN); >+ JSON_SEP; >+ JSON_NULL_WEMPTY("phone-number", umbi->pn, UMB_PHONENR_MAXLEN); >+ JSON_SEP; >+ JSON_NULL_WEMPTY("subscriber-id", umbi->sid, UMB_SUBSCRIBERID_MAXLEN); >+ JSON_SEP; >+ JSON_NULL_WEMPTY("iccid", umbi->iccid, UMB_ICCID_MAXLEN); >+ JSON_SEP; >+ JSON_BOOL("roaming-on", umbi->enable_roaming); >+ JSON_SEP; >+ JSON_NULL_WEMPTY("roaming-text", umbi->roamingtxt, UMB_ROAMINGTEXT_MAXLEN); >+ JSON_SEP; >+ JSON_NULL_WEMPTY("device-id", umbi->devid, UMB_DEVID_MAXLEN); >+ JSON_SEP; >+ JSON_NULL_WEMPTY("firmware", umbi->fwinfo, UMB_FWINFO_MAXLEN); >+ JSON_SEP; >+ JSON_NULL_WEMPTY("hardware", umbi->hwinfo, UMB_HWINFO_MAXLEN); >+ JSON_SEP; >+ JSON_NULL_WEMPTY("access-point-name", umbi->apn, UMB_APN_MAXLEN); >+ JSON_SEP; >+ JSON_NULL_WEMPTY("username", umbi->username, UMB_USERNAME_MAXLEN); >+ JSON_SEP; >+ JSON_NULL_WEMPTY("password", umbi->password, UMB_PASSWORD_MAXLEN); >+ JSON_SEP; >+ JSON_INT("rssi", umbi->rssi); >+ JSON_SEP; >+ JSON_INT("bit-error-rate", umbi->ber); >+ JSON_SEP; >+ JSON_BOOL("hw-radio-on", umbi->hw_radio_on); >+ JSON_SEP; >+ JSON_BOOL("sw-radio-on", umbi->sw_radio_on); >+ JSON_SEP; >+ JSON_ULONG("uplink-speed", umbi->uplink_speed); >+ JSON_SEP; >+ JSON_ULONG("downlink-speed", umbi->downlink_speed); >+ JSON_SEP; >+ JSON_KEY("ipv4-dns"); >+ JSON_ARRAY_START; >+ /* TODO: Implement the DNS servers in the JSON object >+ #define UMB_MAX_DNSSRV 2 >+ u_int32_t ipv4dns[UMB_MAX_DNSSRV]; >+ >+ XXX: The ipv4dns is either of maximum size or null terminated >+ (it's memset to 0) >+ */ >+ JSON_ARRAY_END; >+ JSON_END; >+} >+ >+ >+/* umbctl_gateway */ >+static int _umbctl_gateway(char const * ifname) >+{ >+ int fd; >+ struct ifreq ifr; >+ struct sockaddr_in * sin; >+ >+ if((fd = _umbctl_socket()) < 0) >+ return 2; >+ memset(&ifr, 0, sizeof(ifr)); >+ strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); >+ if(_umbctl_ioctl(ifname, fd, SIOCGIFDSTADDR, &ifr) != 0) >+ { >+ close(fd); >+ return 2; >+ } >+ sin = (struct sockaddr_in *)&ifr.ifr_dstaddr; >+ printf("%s\n", inet_ntoa(sin->sin_addr)); >+ if(close(fd) != 0) >+ return _error(2, "%s: %s", ifname, strerror(errno)); >+ return 0; >+} >+ >+ >+/* umbctl_info */ >+static void _umbctl_info(char const * ifname, struct umb_info * umbi) >+{ >+ char provider[UMB_PROVIDERNAME_MAXLEN + 1]; >+ char pn[UMB_PHONENR_MAXLEN + 1]; >+ char roaming[UMB_ROAMINGTEXT_MAXLEN + 1]; >+ char apn[UMB_APN_MAXLEN + 1]; >+ char fwinfo[UMB_FWINFO_MAXLEN + 1]; >+ char hwinfo[UMB_HWINFO_MAXLEN + 1]; >+ >+ _utf16_to_char(umbi->provider, UMB_PROVIDERNAME_MAXLEN, >+ provider, sizeof(provider)); >+ _utf16_to_char(umbi->pn, UMB_PHONENR_MAXLEN, pn, sizeof(pn)); >+ _utf16_to_char(umbi->roamingtxt, UMB_ROAMINGTEXT_MAXLEN, >+ roaming, sizeof(roaming)); >+ _utf16_to_char(umbi->apn, UMB_APN_MAXLEN, apn, sizeof(apn)); >+ _utf16_to_char(umbi->fwinfo, UMB_FWINFO_MAXLEN, fwinfo, sizeof(fwinfo)); >+ _utf16_to_char(umbi->hwinfo, UMB_HWINFO_MAXLEN, hwinfo, sizeof(hwinfo)); >+ printf("%s: state %s, mode %s, registration %s\n" >+ "\tprovider \"%s\", dataclass %s, signal %s\n" >+ "\tphone number \"%s\", roaming \"%s\" (%s)\n" >+ "\tAPN \"%s\", TX %" PRIu64 ", RX %" PRIu64 "\n" >+ "\tfirmware \"%s\", hardware \"%s\"\n", >+ ifname, umb_val2descr(_umb_state, umbi->state), >+ umb_val2descr(_umb_regmode, umbi->regmode), >+ umb_val2descr(_umb_regstate, umbi->regstate), provider, >+ umb_val2descr(_umb_dataclass, umbi->cellclass), >+ umb_val2descr(_umb_ber, umbi->ber), pn, roaming, >+ umbi->enable_roaming ? "allowed" : "denied", >+ apn, umbi->uplink_speed, umbi->downlink_speed, >+ fwinfo, hwinfo); >+} >+ >+/* print_utf16 */ >+static void _print_utf16(uint16_t * in, int inlen) >+{ >+ /* XXX Note to readers: >+ * >+ * The function was originally: >+ * do >+ * putc(isascii((char)htole16(*in)) ? (char)htole16(*in) : '?', stdout); >+ * while ((char)htole16(*(in++)) != '\0' && --inlen > 0); >+ * >+ * but in an effort to reuse the code, it is better to call >+ * _utf16_to_char instead. >+ */ >+ char buf[inlen + 1]; >+ >+ _utf16_to_char(in, inlen, buf, inlen); >+ buf[inlen] = '\0'; >+ printf("%s", buf); >+} >+ >+/* umbctl_ioctl */ >+static int _umbctl_ioctl(char const * ifname, int fd, unsigned long request, >+ struct ifreq * ifr) >+{ >+ if(ioctl(fd, request, ifr) != 0) >+ return _error(-1, "%s: %s", ifname, strerror(errno)); >+ return 0; >+} >+ >+ >+/* umbctl_set */ >+/* callbacks */ >+static int _set_apn(char const *, struct umb_parameter *, char const *); >+static int _set_username(char const *, struct umb_parameter *, char const *); >+static int _set_password(char const *, struct umb_parameter *, char const *); >+static int _set_pin(char const *, struct umb_parameter *, char const *); >+static int _set_puk(char const *, struct umb_parameter *, char const *); >+static int _set_roaming_allow(char const *, struct umb_parameter *, >+ char const *); >+static int _set_roaming_deny(char const *, struct umb_parameter *, >+ char const *); >+ >+static int _umbctl_set(char const * ifname, struct umb_parameter * umbp, >+ int argc, char * argv[]) >+{ >+ struct >+ { >+ char const * name; >+ int (*callback)(char const *, >+ struct umb_parameter *, char const *); >+ int parameter; >+ } callbacks[] = >+ { >+ { "apn", _set_apn, 1 }, >+ { "username", _set_username, 1 }, >+ { "password", _set_password, 1 }, >+ { "pin", _set_pin, 1 }, >+ { "puk", _set_puk, 1 }, >+ { "roaming", _set_roaming_allow, 0 }, >+ { "-roaming", _set_roaming_deny, 0 }, >+ }; >+ int i; >+ size_t j; >+ >+ for(i = 0; i < argc; i++) >+ { >+ for(j = 0; j < sizeof(callbacks) / sizeof(*callbacks); j++) >+ if(strcmp(argv[i], callbacks[j].name) == 0) >+ { >+ if(callbacks[j].parameter && i + 1 == argc) >+ return _error(-1, "%s: Incomplete" >+ " parameter", argv[i]); >+ if(callbacks[j].callback(ifname, umbp, >+ callbacks[j].parameter >+ ? argv[i + 1] : NULL)) >+ return -1; >+ if(callbacks[j].parameter) >+ i++; >+ break; >+ } >+ if(j == sizeof(callbacks) / sizeof(*callbacks)) >+ return _error(-1, "%s: Unknown parameter", argv[i]); >+ } >+ return 0; >+} >+ >+static int _set_apn(char const * ifname, struct umb_parameter * umbp, >+ char const * apn) >+{ >+ umbp->apnlen = _char_to_utf16(apn, umbp->apn, sizeof(umbp->apn)); >+ if(umbp->apnlen < 0 || (size_t)umbp->apnlen > sizeof(umbp->apn)) >+ return _error(-1, "%s: %s", ifname, "APN too long"); >+ return 0; >+} >+ >+static int _set_username(char const * ifname, struct umb_parameter * umbp, >+ char const * username) >+{ >+ umbp->usernamelen = _char_to_utf16(username, umbp->username, >+ sizeof(umbp->username)); >+ if(umbp->usernamelen < 0 >+ || (size_t)umbp->usernamelen > sizeof(umbp->username)) >+ return _error(-1, "%s: %s", ifname, "Username too long"); >+ return 0; >+} >+ >+static int _set_password(char const * ifname, struct umb_parameter * umbp, >+ char const * password) >+{ >+ umbp->passwordlen = _char_to_utf16(password, umbp->password, >+ sizeof(umbp->password)); >+ if(umbp->passwordlen < 0 >+ || (size_t)umbp->passwordlen > sizeof(umbp->password)) >+ return _error(-1, "%s: %s", ifname, "Password too long"); >+ return 0; >+} >+ >+static int _set_pin(char const * ifname, struct umb_parameter * umbp, >+ char const * pin) >+{ >+ umbp->is_puk = 0; >+ umbp->op = MBIM_PIN_OP_ENTER; >+ umbp->pinlen = _char_to_utf16(pin, umbp->pin, sizeof(umbp->pin)); >+ if(umbp->pinlen < 0 || (size_t)umbp->pinlen >+ > sizeof(umbp->pin)) >+ return _error(-1, "%s: %s", ifname, "PIN code too long"); >+ return 0; >+} >+ >+static int _set_puk(char const * ifname, struct umb_parameter * umbp, >+ char const * puk) >+{ >+ umbp->is_puk = 1; >+ umbp->op = MBIM_PIN_OP_ENTER; >+ umbp->pinlen = _char_to_utf16(puk, umbp->pin, sizeof(umbp->pin)); >+ if(umbp->pinlen < 0 || (size_t)umbp->pinlen > sizeof(umbp->pin)) >+ return _error(-1, "%s: %s", ifname, "PUK code too long"); >+ return 0; >+} >+ >+static int _set_roaming_allow(char const * ifname, struct umb_parameter * umbp, >+ char const * unused) >+{ >+ (void) ifname; >+ (void) unused; >+ >+ umbp->roaming = 1; >+ return 0; >+} >+ >+static int _set_roaming_deny(char const * ifname, struct umb_parameter * umbp, >+ char const * unused) >+{ >+ (void) ifname; >+ (void) unused; >+ >+ umbp->roaming = 0; >+ return 0; >+} >+ >+ >+/* umbctl_socket */ >+static int _umbctl_socket(void) >+{ >+ int fd; >+ >+ if((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) >+ return _error(-1, "socket: %s", strerror(errno)); >+ return fd; >+} >+ >+ >+/* usage */ >+static int _usage(void) >+{ >+ fputs("Usage: umbctl [-v] ifname [parameter [value]] [...]\n" >+" umbctl -f config-file ifname\n" >+" umbctl -g ifname\n" >+" umbctl -m format ifname [...]\n" >+"\n" >+"Note: -f and -m are mutually exclusive. The only supported format is JSON.\n", >+ stderr); >+ return 1; >+} >+ >+ >+/* utf16_to_char */ >+static void _utf16_to_char(uint16_t * in, int inlen, char * out, size_t outlen) >+{ >+ uint16_t c; >+ >+ while (outlen > 0) { >+ c = inlen > 0 ? htole16(*in) : 0; >+ if (c == 0 || --outlen == 0) { >+ /* always NUL terminate result */ >+ *out = '\0'; >+ break; >+ } >+ *out++ = isascii(c) ? (char)c : '?'; >+ in++; >+ inlen--; >+ } >+} >+ >+ >+/* main */ >+int main(int argc, char * argv[]) >+{ >+ int o; >+ char const * filename = NULL; >+ char const * format = NULL; >+ int gateway = 0; >+ int verbose = 0; >+ >+ while((o = getopt(argc, argv, "f:gm:v")) != -1) >+ switch(o) >+ { >+ case 'f': >+ filename = optarg; >+ format = NULL; >+ gateway = 0; >+ break; >+ case 'g': >+ filename = NULL; >+ format = NULL; >+ gateway = 1; >+ break; >+ case 'm': >+ filename = NULL; >+ format = optarg; >+ gateway = 0; >+ break; >+ case 'v': >+ verbose++; >+ break; >+ default: >+ return _usage(); >+ } >+ if(optind == argc) >+ return _usage(); >+ if(filename != NULL) >+ { >+ if(optind + 1 != argc) >+ return _usage(); >+ return _umbctl_file(argv[optind], filename, verbose); >+ } >+ if(format != NULL) >+ { >+ if(optind + 1 != argc) >+ return _usage(); >+ return _umbctl_dump(argv[optind], format, verbose); >+ } >+ if(gateway) >+ { >+ if(optind + 1 != argc) >+ return _usage(); >+ return _umbctl_gateway(argv[optind]); >+ } >+ return _umbctl(argv[optind], verbose, argc - optind - 1, >+ &argv[optind + 1]); >+} >diff --git a/share/man/man4/Makefile b/share/man/man4/Makefile >index ad1cf5dd3785..34b36b2b093b 100644 >--- a/share/man/man4/Makefile >+++ b/share/man/man4/Makefile >@@ -1045,6 +1045,7 @@ MAN+= \ > uled.4 \ > ulpt.4 \ > umass.4 \ >+ umb.4 \ > umcs.4 \ > umct.4 \ > umodem.4 \ >diff --git a/share/man/man4/umb.4 b/share/man/man4/umb.4 >new file mode 100644 >index 000000000000..a055f9e6f3d6 >--- /dev/null >+++ b/share/man/man4/umb.4 >@@ -0,0 +1,90 @@ >+.\" $NetBSD: umb.4,v 1.4 2019/08/30 09:22:17 wiz Exp $ >+.\" >+.\" Copyright (c) 2016 genua mbH >+.\" >+.\" Permission to use, copy, modify, and distribute this software for any >+.\" purpose with or without fee is hereby granted, provided that the above >+.\" copyright notice and this permission notice appear in all copies. >+.\" >+.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES >+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF >+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR >+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES >+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN >+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF >+.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. >+.\" >+.Dd August 24, 2019 >+.Dt UMB 4 >+.Os >+.Sh NAME >+.Nm umb >+.Nd USB Mobile Broadband Interface Model (MBIM) >+.Sh SYNOPSIS >+.Cd "umb* at uhub? port?" >+.Sh DESCRIPTION >+The >+.Nm >+driver provides support for USB MBIM devices. >+.Pp >+MBIM devices establish connections via cellular networks such as >+GPRS, UMTS, and LTE. >+They appear as a regular point-to-point network interface, >+transporting raw IP frames. >+.Pp >+Required configuration parameters like PIN and APN have to be set >+with >+.Xr umbctl 8 . >+Once the SIM card has been unlocked with the correct PIN, it >+will remain in this state until the MBIM device is power-cycled. >+In case the device is connected to an "always-on" USB port, >+it may be possible to connect to a provider without entering the >+PIN again even if the system was rebooted. >+.Sh HARDWARE >+The following devices should work: >+.Pp >+.Bl -tag -width Ds -offset indent -compact >+.It Ericsson H5321gw and N5321gw >+.It Fibocom L831-EAU >+.It Medion Mobile S4222 (MediaTek OEM) >+.It Sierra Wireless EM7345 >+.It Sierra Wireless EM7455 >+.It Sierra Wireless EM8805 >+.It Sierra Wireless MC8305 >+.El >+.Sh SEE ALSO >+.Xr intro 4 , >+.Xr netintro 4 , >+.Xr usb 4 , >+.Xr ifconfig.if 5 , >+.Xr ifconfig 8 , >+.Xr umbctl 8 >+.Rs >+.%T "Universal Serial Bus Communications Class Subclass Specification for Mobile Broadband Interface Model" >+.%U http://www.usb.org/developers/docs/devclass_docs/MBIM10Errata1_073013.zip >+.Re >+.Sh HISTORY >+The >+.Nm >+device driver first appeared in >+.Ox 6.0 >+and >+.Nx 9.0 . >+.Sh AUTHORS >+.An -nosplit >+The >+.Nm >+driver was written by >+.An Gerhard Roth Aq Mt gerhard@openbsd.org >+and ported from >+.Ox >+by >+.An Pierre Pronchery Aq Mt khorben@defora.org . >+.Sh CAVEATS >+The >+.Nm >+driver does not support IPv6. >+.Pp >+Devices which fail to provide a conforming MBIM implementation will >+probably be attached as some other driver, such as >+.Xr u3g 4 . >diff --git a/sys/conf/files b/sys/conf/files >index 7818104b8d50..42129256b0e7 100644 >--- a/sys/conf/files >+++ b/sys/conf/files >@@ -3307,6 +3307,7 @@ dev/usb/net/if_muge.c optional muge > dev/usb/net/if_rue.c optional rue > dev/usb/net/if_smsc.c optional smsc > dev/usb/net/if_udav.c optional udav >+dev/usb/net/if_umb.c optional umb > dev/usb/net/if_ure.c optional ure > dev/usb/net/if_usie.c optional usie > dev/usb/net/if_urndis.c optional urndis >diff --git a/sys/dev/usb/net/if_umb.c b/sys/dev/usb/net/if_umb.c >new file mode 100644 >index 000000000000..857928c32e02 >--- /dev/null >+++ b/sys/dev/usb/net/if_umb.c >@@ -0,0 +1,2931 @@ >+/* $FreeBSD$ */ >+/* $NetBSD$ */ >+/* $OpenBSD: if_umb.c,v 1.18 2018/02/19 08:59:52 mpi Exp $ */ >+ >+/* >+ * Original copyright (c) 2016 genua mbH (OpenBSD version) >+ * >+ * Permission to use, copy, modify, and distribute this software for any >+ * purpose with or without fee is hereby granted, provided that the above >+ * copyright notice and this permission notice appear in all copies. >+ * >+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES >+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF >+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR >+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES >+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN >+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF >+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. >+ * >+ * Copyright (c) 2022 ADISTA SAS (re-write for FreeBSD) >+ * >+ * Re-write for FreeBSD by Pierre Pronchery <pierre@defora.net> >+ * >+ * Redistribution and use in source and binary forms, with or without >+ * modification, are permitted provided that the following conditions are met: >+ * >+ * - Redistributions of source code must retain the above copyright notice, >+ * this list of conditions and the following disclaimer. >+ * - Redistributions in binary form must reproduce the above copyright notice, >+ * this list of conditions and the following disclaimer in the documentation >+ * and/or other materials provided with the distribution. >+ * - Neither the name of the copyright holder nor the names of its contributors >+ * may be used to endorse or promote products derived from this software >+ * without specific prior written permission. >+ * >+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" >+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE >+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE >+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE >+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR >+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF >+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS >+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN >+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) >+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE >+ * POSSIBILITY OF SUCH DAMAGE. >+ */ >+ >+/* >+ * Mobile Broadband Interface Model specification: >+ * http://www.usb.org/developers/docs/devclass_docs/MBIM10Errata1_073013.zip >+ * Compliance testing guide >+ * http://www.usb.org/developers/docs/devclass_docs/MBIM-Compliance-1.0.pdf >+ */ >+ >+#include <sys/param.h> >+#include <sys/module.h> >+#include <sys/endian.h> >+#include <sys/kernel.h> >+#include <sys/mbuf.h> >+#include <sys/priv.h> >+#include <sys/socket.h> >+#include <sys/sockio.h> >+#include <sys/systm.h> >+#include <sys/syslog.h> >+#include <sys/kernel.h> >+#include <sys/queue.h> >+ >+#include <sys/conf.h> >+#include <sys/bus.h> >+#include <sys/mutex.h> >+#include <sys/condvar.h> >+#include <sys/taskqueue.h> >+ >+#include <machine/_inttypes.h> >+ >+#include <net/bpf.h> >+#include <net/if.h> >+#include <net/if_media.h> >+#include <net/if_types.h> >+#include <net/if_var.h> >+#include <net/netisr.h> >+#include <net/route.h> >+ >+#include <netinet/in.h> >+#include <netinet/in_var.h> >+#include <netinet/ip.h> >+ >+#include <dev/usb/usb.h> >+#include <dev/usb/usb_cdc.h> >+#include <dev/usb/usbdi.h> >+#include <dev/usb/usb_device.h> >+#include <dev/usb/usb_process.h> >+#include <dev/usb/usbdi_util.h> >+#include "usb_if.h" >+ >+#include "mbim.h" >+#include "if_umbreg.h" >+ >+MALLOC_DECLARE(M_MBIM_CID_CONNECT); >+MALLOC_DEFINE(M_MBIM_CID_CONNECT, "mbim_cid_connect", >+ "Connection parameters for MBIM"); >+ >+#ifdef UMB_DEBUG >+#define DPRINTF(x...) \ >+ do { if (umb_debug) log(LOG_DEBUG, x); } while (0) >+ >+#define DPRINTFN(n, x...) \ >+ do { if (umb_debug >= (n)) log(LOG_DEBUG, x); } while (0) >+ >+#define DDUMPN(n, b, l) \ >+ do { \ >+ if (umb_debug >= (n)) \ >+ umb_dump((b), (l)); \ >+ } while (0) >+ >+const int umb_debug = 1; >+static char *umb_uuid2str(uint8_t [MBIM_UUID_LEN]); >+static void umb_dump(void *, int); >+ >+#else >+#define DPRINTF(x...) do { } while (0) >+#define DPRINTFN(n, x...) do { } while (0) >+#define DDUMPN(n, b, l) do { } while (0) >+#endif >+ >+#define DEVNAM(sc) device_get_nameunit((sc)->sc_dev) >+ >+/* >+ * State change timeout >+ */ >+#define UMB_STATE_CHANGE_TIMEOUT 30 >+ >+/* >+ * State change flags >+ */ >+#define UMB_NS_DONT_DROP 0x0001 /* do not drop below current state */ >+#define UMB_NS_DONT_RAISE 0x0002 /* do not raise below current state */ >+ >+/* >+ * Diagnostic macros >+ */ >+const struct umb_valdescr umb_regstates[] = MBIM_REGSTATE_DESCRIPTIONS; >+const struct umb_valdescr umb_dataclasses[] = MBIM_DATACLASS_DESCRIPTIONS; >+const struct umb_valdescr umb_simstate[] = MBIM_SIMSTATE_DESCRIPTIONS; >+const struct umb_valdescr umb_messages[] = MBIM_MESSAGES_DESCRIPTIONS; >+const struct umb_valdescr umb_status[] = MBIM_STATUS_DESCRIPTIONS; >+const struct umb_valdescr umb_cids[] = MBIM_CID_DESCRIPTIONS; >+const struct umb_valdescr umb_pktstate[] = MBIM_PKTSRV_STATE_DESCRIPTIONS; >+const struct umb_valdescr umb_actstate[] = MBIM_ACTIVATION_STATE_DESCRIPTIONS; >+const struct umb_valdescr umb_error[] = MBIM_ERROR_DESCRIPTIONS; >+const struct umb_valdescr umb_pintype[] = MBIM_PINTYPE_DESCRIPTIONS; >+const struct umb_valdescr umb_istate[] = UMB_INTERNAL_STATE_DESCRIPTIONS; >+ >+#define umb_regstate(c) umb_val2descr(umb_regstates, (c)) >+#define umb_dataclass(c) umb_val2descr(umb_dataclasses, (c)) >+#define umb_simstate(s) umb_val2descr(umb_simstate, (s)) >+#define umb_request2str(m) umb_val2descr(umb_messages, (m)) >+#define umb_status2str(s) umb_val2descr(umb_status, (s)) >+#define umb_cid2str(c) umb_val2descr(umb_cids, (c)) >+#define umb_packet_state(s) umb_val2descr(umb_pktstate, (s)) >+#define umb_activation(s) umb_val2descr(umb_actstate, (s)) >+#define umb_error2str(e) umb_val2descr(umb_error, (e)) >+#define umb_pin_type(t) umb_val2descr(umb_pintype, (t)) >+#define umb_istate(s) umb_val2descr(umb_istate, (s)) >+ >+static device_probe_t umb_probe; >+static device_attach_t umb_attach; >+static device_detach_t umb_detach; >+static device_suspend_t umb_suspend; >+static device_resume_t umb_resume; >+static void umb_attach_task(struct usb_proc_msg *); >+static usb_handle_request_t umb_handle_request; >+static int umb_deactivate(device_t); >+static void umb_ncm_setup(struct umb_softc *, struct usb_config *); >+static void umb_close_bulkpipes(struct umb_softc *); >+static int umb_ioctl(struct ifnet *, u_long, caddr_t); >+static void umb_init(void *); >+#ifdef DEV_NETMAP >+static void umb_input(struct ifnet *, struct mbuf *); >+#endif >+static int umb_output(struct ifnet *, struct mbuf *, >+ const struct sockaddr *, struct route *); >+static void umb_start(struct ifnet *); >+static void umb_start_task(struct usb_proc_msg *); >+#if 0 >+static void umb_watchdog(struct ifnet *); >+#endif >+static void umb_statechg_timeout(void *); >+ >+static int umb_mediachange(struct ifnet *); >+static void umb_mediastatus(struct ifnet *, struct ifmediareq *); >+ >+static void umb_add_task(struct umb_softc *sc, usb_proc_callback_t, >+ struct usb_proc_msg *, struct usb_proc_msg *, int); >+static void umb_newstate(struct umb_softc *, enum umb_state, int); >+static void umb_state_task(struct usb_proc_msg *); >+static void umb_up(struct umb_softc *); >+static void umb_down(struct umb_softc *, int); >+ >+static void umb_get_response_task(struct usb_proc_msg *); >+ >+static void umb_decode_response(struct umb_softc *, void *, int); >+static void umb_handle_indicate_status_msg(struct umb_softc *, void *, >+ int); >+static void umb_handle_opendone_msg(struct umb_softc *, void *, int); >+static void umb_handle_closedone_msg(struct umb_softc *, void *, int); >+static int umb_decode_register_state(struct umb_softc *, void *, int); >+static int umb_decode_devices_caps(struct umb_softc *, void *, int); >+static int umb_decode_subscriber_status(struct umb_softc *, void *, int); >+static int umb_decode_radio_state(struct umb_softc *, void *, int); >+static int umb_decode_pin(struct umb_softc *, void *, int); >+static int umb_decode_packet_service(struct umb_softc *, void *, int); >+static int umb_decode_signal_state(struct umb_softc *, void *, int); >+static int umb_decode_connect_info(struct umb_softc *, void *, int); >+static int umb_decode_ip_configuration(struct umb_softc *, void *, int); >+static void umb_rx(struct umb_softc *); >+static usb_callback_t umb_rxeof; >+static void umb_rxflush(struct umb_softc *); >+static int umb_encap(struct umb_softc *, struct mbuf *, struct usb_xfer *); >+static usb_callback_t umb_txeof; >+static void umb_txflush(struct umb_softc *); >+static void umb_decap(struct umb_softc *, struct usb_xfer *, int); >+ >+static usb_error_t umb_send_encap_command(struct umb_softc *, void *, int); >+static int umb_get_encap_response(struct umb_softc *, void *, int *); >+static void umb_ctrl_msg(struct umb_softc *, uint32_t, void *, int); >+ >+static void umb_open(struct umb_softc *); >+static void umb_close(struct umb_softc *); >+ >+static int umb_setpin(struct umb_softc *, int, int, void *, int, void *, >+ int); >+static void umb_setdataclass(struct umb_softc *); >+static void umb_radio(struct umb_softc *, int); >+static void umb_allocate_cid(struct umb_softc *); >+static void umb_send_fcc_auth(struct umb_softc *); >+static void umb_packet_service(struct umb_softc *, int); >+static void umb_connect(struct umb_softc *); >+static void umb_disconnect(struct umb_softc *); >+static void umb_send_connect(struct umb_softc *, int); >+ >+static void umb_qry_ipconfig(struct umb_softc *); >+static void umb_cmd(struct umb_softc *, int, int, const void *, int); >+static void umb_cmd1(struct umb_softc *, int, int, const void *, int, uint8_t *); >+static void umb_command_done(struct umb_softc *, void *, int); >+static void umb_decode_cid(struct umb_softc *, uint32_t, void *, int); >+static void umb_decode_qmi(struct umb_softc *, uint8_t *, int); >+ >+static usb_callback_t umb_intr; >+ >+static char *umb_ntop(struct sockaddr *); >+ >+static const int umb_xfer_tout = USB_DEFAULT_TIMEOUT; >+ >+static uint8_t umb_uuid_basic_connect[] = MBIM_UUID_BASIC_CONNECT; >+static uint8_t umb_uuid_context_internet[] = MBIM_UUID_CONTEXT_INTERNET; >+static uint8_t umb_uuid_qmi_mbim[] = MBIM_UUID_QMI_MBIM; >+static uint32_t umb_session_id = 0; >+ >+static const struct usb_config umb_config[UMB_N_TRANSFER] = { >+ [UMB_INTR_RX] = { >+ .type = UE_INTERRUPT, >+ .endpoint = UE_ADDR_ANY, >+ .direction = UE_DIR_IN, >+ .if_index = 1, >+ .callback = umb_intr, >+ .bufsize = sizeof (struct usb_cdc_notification), >+ .flags = {.pipe_bof = 1,.short_xfer_ok = 1}, >+ .usb_mode = USB_MODE_HOST, >+ }, >+ [UMB_BULK_RX] = { >+ .type = UE_BULK, >+ .endpoint = UE_ADDR_ANY, >+ .direction = UE_DIR_IN, >+ .if_index = 0, >+ .callback = umb_rxeof, >+ .bufsize = 8 * 1024, >+ .flags = {.pipe_bof = 1,.short_xfer_ok = 1,.ext_buffer = 1}, >+ .usb_mode = USB_MODE_HOST, >+ }, >+ [UMB_BULK_TX] = { >+ .type = UE_BULK, >+ .endpoint = UE_ADDR_ANY, >+ .direction = UE_DIR_OUT, >+ .if_index = 0, >+ .callback = umb_txeof, >+ .bufsize = 8 * 1024, >+ .flags = {.pipe_bof = 1,.force_short_xfer = 1,.ext_buffer = 1}, >+ .timeout = umb_xfer_tout, >+ .usb_mode = USB_MODE_HOST, >+ }, >+}; >+ >+static device_method_t umb_methods[] = { >+ /* USB interface */ >+ DEVMETHOD(usb_handle_request, umb_handle_request), >+ >+ /* Device interface */ >+ DEVMETHOD(device_probe, umb_probe), >+ DEVMETHOD(device_attach, umb_attach), >+ DEVMETHOD(device_detach, umb_detach), >+ DEVMETHOD(device_suspend, umb_suspend), >+ DEVMETHOD(device_resume, umb_resume), >+ >+ DEVMETHOD_END >+}; >+ >+static driver_t umb_driver = { >+ .name = "umb", >+ .methods = umb_methods, >+ .size = sizeof (struct umb_softc), >+}; >+ >+MALLOC_DEFINE(M_USB_UMB, "USB UMB", "USB MBIM driver"); >+ >+const int umb_delay = 4000; >+ >+/* >+ * These devices require an "FCC Authentication" command. >+ */ >+#ifndef USB_VENDOR_SIERRA >+# define USB_VENDOR_SIERRA 0x1199 >+#endif >+#ifndef USB_PRODUCT_SIERRA_EM7455 >+# define USB_PRODUCT_SIERRA_EM7455 0x9079 >+#endif >+const struct usb_device_id umb_fccauth_devs[] = { >+ { >+ .match_flag_vendor = 1, >+ .match_flag_product = 1, >+ .idVendor = USB_VENDOR_SIERRA, >+ .idProduct = USB_PRODUCT_SIERRA_EM7455 >+ } >+}; >+ >+static const uint8_t umb_qmi_alloc_cid[] = { >+ 0x01, >+ 0x0f, 0x00, /* len */ >+ 0x00, /* QMUX flags */ >+ 0x00, /* service "ctl" */ >+ 0x00, /* CID */ >+ 0x00, /* QMI flags */ >+ 0x01, /* transaction */ >+ 0x22, 0x00, /* msg "Allocate CID" */ >+ 0x04, 0x00, /* TLV len */ >+ 0x01, 0x01, 0x00, 0x02 /* TLV */ >+}; >+ >+static const uint8_t umb_qmi_fcc_auth[] = { >+ 0x01, >+ 0x0c, 0x00, /* len */ >+ 0x00, /* QMUX flags */ >+ 0x02, /* service "dms" */ >+#define UMB_QMI_CID_OFFS 5 >+ 0x00, /* CID (filled in later) */ >+ 0x00, /* QMI flags */ >+ 0x01, 0x00, /* transaction */ >+ 0x5f, 0x55, /* msg "Send FCC Authentication" */ >+ 0x00, 0x00 /* TLV len */ >+}; >+ >+static int >+umb_probe(device_t dev) >+{ >+ struct usb_attach_arg *uaa = device_get_ivars(dev); >+ usb_interface_descriptor_t *id; >+ >+ if (uaa->usb_mode != USB_MODE_HOST) >+ return (ENXIO); >+ if ((id = usbd_get_interface_descriptor(uaa->iface)) == NULL) >+ return (ENXIO); >+ >+ /* >+ * If this function implements NCM, check if alternate setting >+ * 1 implements MBIM. >+ */ >+ if (id->bInterfaceClass == UICLASS_CDC && >+ id->bInterfaceSubClass == >+ UISUBCLASS_NETWORK_CONTROL_MODEL) { >+ id = usbd_get_interface_descriptor( >+ usbd_get_iface(uaa->device, >+ uaa->info.bIfaceIndex + 1)); >+ if (id == NULL || id->bAlternateSetting != 1) >+ return (ENXIO); >+ } >+ >+#ifndef UISUBCLASS_MOBILE_BROADBAND_INTERFACE_MODEL >+# define UISUBCLASS_MOBILE_BROADBAND_INTERFACE_MODEL 14 >+#endif >+ if (id->bInterfaceClass == UICLASS_CDC && >+ id->bInterfaceSubClass == >+ UISUBCLASS_MOBILE_BROADBAND_INTERFACE_MODEL && >+ id->bInterfaceProtocol == 0) >+ return (BUS_PROBE_SPECIFIC); >+ >+ return (ENXIO); >+} >+ >+static int >+umb_attach(device_t dev) >+{ >+ struct umb_softc *sc = device_get_softc(dev); >+ struct usb_attach_arg *uaa = device_get_ivars(dev); >+ struct usb_config config[UMB_N_TRANSFER]; >+ int v; >+ const struct usb_cdc_union_descriptor *ud; >+ const struct mbim_descriptor *md; >+ int i; >+ usb_interface_descriptor_t *id; >+ struct usb_interface *iface; >+ int data_ifaceno = -1; >+ usb_error_t error; >+ >+ sc->sc_dev = dev; >+ sc->sc_udev = uaa->device; >+ >+ memcpy(config, umb_config, sizeof (config)); >+ >+ device_set_usb_desc(dev); >+ >+ sc->sc_ctrl_ifaceno = uaa->info.bIfaceNum; >+ >+ mtx_init(&sc->sc_mutex, device_get_nameunit(dev), NULL, MTX_DEF); >+ >+ /* >+ * Some MBIM hardware does not provide the mandatory CDC Union >+ * Descriptor, so we also look at matching Interface >+ * Association Descriptors to find out the MBIM Data Interface >+ * number. >+ */ >+ sc->sc_ver_maj = sc->sc_ver_min = -1; >+ sc->sc_maxpktlen = MBIM_MAXSEGSZ_MINVAL; >+ id = usbd_get_interface_descriptor(uaa->iface); >+ >+ ud = usbd_find_descriptor(sc->sc_udev, id, uaa->info.bIfaceIndex, >+ UDESC_CS_INTERFACE, 0xff, UDESCSUB_CDC_UNION, 0xff); >+ if (ud != NULL) { >+ data_ifaceno = ud->bSlaveInterface[0]; >+ } >+ >+ md = usbd_find_descriptor(sc->sc_udev, id, uaa->info.bIfaceIndex, >+ UDESC_CS_INTERFACE, 0xff, UDESCSUB_MBIM, 0xff); >+ if (md != NULL) { >+ v = UGETW(md->bcdMBIMVersion); >+ sc->sc_ver_maj = MBIM_VER_MAJOR(v); >+ sc->sc_ver_min = MBIM_VER_MINOR(v); >+ sc->sc_ctrl_len = UGETW(md->wMaxControlMessage); >+ /* Never trust a USB device! Could try to exploit us */ >+ if (sc->sc_ctrl_len < MBIM_CTRLMSG_MINLEN || >+ sc->sc_ctrl_len > MBIM_CTRLMSG_MAXLEN) { >+ DPRINTF("control message len %d out of " >+ "bounds [%d .. %d]\n", >+ sc->sc_ctrl_len, MBIM_CTRLMSG_MINLEN, >+ MBIM_CTRLMSG_MAXLEN); >+ /* continue anyway */ >+ } >+ sc->sc_maxpktlen = UGETW(md->wMaxSegmentSize); >+ DPRINTFN(2, "ctrl_len=%d, maxpktlen=%d, cap=0x%x\n", >+ sc->sc_ctrl_len, sc->sc_maxpktlen, >+ md->bmNetworkCapabilities); >+ } >+ if (sc->sc_ver_maj < 0) { >+ device_printf(dev, "error: missing MBIM descriptor\n"); >+ goto fail; >+ } >+ >+ device_printf(dev, "version %d.%d\n", sc->sc_ver_maj, >+ sc->sc_ver_min); >+ >+ if (usbd_lookup_id_by_uaa(umb_fccauth_devs, sizeof (umb_fccauth_devs), >+ uaa)) { >+ sc->sc_flags |= UMBFLG_FCC_AUTH_REQUIRED; >+ sc->sc_cid = -1; >+ } >+ >+ for (i = 0; i < sc->sc_udev->ifaces_max; i++) { >+ iface = usbd_get_iface(sc->sc_udev, i); >+ id = usbd_get_interface_descriptor(iface); >+ if (id == NULL) >+ break; >+ >+ if (id->bInterfaceNumber == data_ifaceno) { >+ sc->sc_data_iface = iface; >+ sc->sc_ifaces_index[0] = i; >+ sc->sc_ifaces_index[1] = uaa->info.bIfaceIndex; >+ break; >+ } >+ } >+ if (sc->sc_data_iface == NULL) { >+ device_printf(dev, "error: no data interface found\n"); >+ goto fail; >+ } >+ >+ /* >+ * If this is a combined NCM/MBIM function, switch to >+ * alternate setting one to enable MBIM. >+ */ >+ id = usbd_get_interface_descriptor(uaa->iface); >+ if (id != NULL && id->bInterfaceClass == UICLASS_CDC && >+ id->bInterfaceSubClass == UISUBCLASS_NETWORK_CONTROL_MODEL) { >+ device_printf(sc->sc_dev, "combined NCM/MBIM\n"); >+ error = usbd_req_set_alt_interface_no(sc->sc_udev, >+ NULL, uaa->info.bIfaceIndex, 1); >+ if (error != USB_ERR_NORMAL_COMPLETION) { >+ device_printf(dev, "error: Could not switch to" >+ " alternate setting for MBIM\n"); >+ goto fail; >+ } >+ sc->sc_ifaces_index[1] = uaa->info.bIfaceIndex + 1; >+ } >+ >+ if (usb_proc_create(&sc->sc_taskqueue, &sc->sc_mutex, >+ device_get_nameunit(sc->sc_dev), >+ USB_PRI_MED) != 0) >+ goto fail; >+ >+ DPRINTFN(2, "ctrl-ifno#%d: data-ifno#%d\n", sc->sc_ctrl_ifaceno, >+ data_ifaceno); >+ >+ usb_callout_init_mtx(&sc->sc_statechg_timer, &sc->sc_mutex, 0); >+ >+ umb_ncm_setup(sc, config); >+ DPRINTFN(2, "%s: rx/tx size %d/%d\n", DEVNAM(sc), >+ sc->sc_rx_bufsz, sc->sc_tx_bufsz); >+ >+ sc->sc_rx_buf = malloc(sc->sc_rx_bufsz, M_DEVBUF, M_WAITOK); >+ sc->sc_tx_buf = malloc(sc->sc_tx_bufsz, M_DEVBUF, M_WAITOK); >+ >+ for (i = 0; i != 32; i++) { >+ error = usbd_set_alt_interface_index(sc->sc_udev, >+ sc->sc_ifaces_index[0], i); >+ if (error) >+ break; >+ >+ error = usbd_transfer_setup(sc->sc_udev, sc->sc_ifaces_index, >+ sc->sc_xfer, config, UMB_N_TRANSFER, >+ sc, &sc->sc_mutex); >+ if (error == USB_ERR_NORMAL_COMPLETION) >+ break; >+ } >+ if (error || (i == 32)) { >+ device_printf(sc->sc_dev, "error: failed to setup xfers\n"); >+ goto fail; >+ } >+ >+ sc->sc_resp_buf = malloc(sc->sc_ctrl_len, M_DEVBUF, M_WAITOK); >+ sc->sc_ctrl_msg = malloc(sc->sc_ctrl_len, M_DEVBUF, M_WAITOK); >+ >+ sc->sc_info.regstate = MBIM_REGSTATE_UNKNOWN; >+ sc->sc_info.pin_attempts_left = UMB_VALUE_UNKNOWN; >+ sc->sc_info.rssi = UMB_VALUE_UNKNOWN; >+ sc->sc_info.ber = UMB_VALUE_UNKNOWN; >+ >+ /* defer attaching the interface */ >+ mtx_lock(&sc->sc_mutex); >+ umb_add_task(sc, umb_attach_task, >+ &sc->sc_proc_attach_task[0].hdr, >+ &sc->sc_proc_attach_task[1].hdr, 0); >+ mtx_unlock(&sc->sc_mutex); >+ >+ return (0); >+ >+fail: >+ umb_detach(sc->sc_dev); >+ return (ENXIO); >+} >+ >+static void >+umb_attach_task(struct usb_proc_msg *msg) >+{ >+ struct umb_task *task = (struct umb_task *)msg; >+ struct umb_softc *sc = task->sc; >+ struct ifnet *ifp; >+ >+ mtx_unlock(&sc->sc_mutex); >+ >+ CURVNET_SET_QUIET(vnet0); >+ >+ /* initialize the interface */ >+ sc->sc_if = ifp = if_alloc(IFT_MBIM); >+ if_initname(ifp, "umb", device_get_unit(sc->sc_dev)); >+ >+ if_setsoftc(ifp, sc); >+ if_setflags(ifp, IFF_SIMPLEX | IFF_MULTICAST | IFF_POINTOPOINT); >+ if_setioctlfn(ifp, umb_ioctl); >+#ifdef DEV_NETMAP >+ if_setinputfn(ifp, umb_input); >+#endif >+ if_setoutputfn(ifp, umb_output); >+ ifp->if_start = umb_start; >+ if_setinitfn(ifp, umb_init); >+ >+#if 0 >+ ifp->if_watchdog = umb_watchdog; >+#endif >+ ifp->if_link_state = LINK_STATE_DOWN; >+ ifmedia_init(&sc->sc_im, 0, umb_mediachange, umb_mediastatus); >+ ifmedia_add(&sc->sc_im, IFM_NONE | IFM_AUTO, 0, NULL); >+ >+ ifp->if_addrlen = 0; >+ ifp->if_hdrlen = sizeof (struct ncm_header16) + >+ sizeof (struct ncm_pointer16); >+ /* XXX hard-coded atm */ >+ if_setmtu(ifp, MIN(2048, sc->sc_maxpktlen)); >+ IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen); >+ ifp->if_snd.ifq_drv_maxlen = ifqmaxlen; >+ IFQ_SET_READY(&ifp->if_snd); >+ >+ /* attach the interface */ >+ if_attach(ifp); >+ bpfattach(ifp, DLT_RAW, 0); >+ >+ sc->sc_attached = 1; >+ >+ CURVNET_RESTORE(); >+ >+ umb_init(sc); >+ mtx_lock(&sc->sc_mutex); >+} >+ >+static int >+umb_detach(device_t dev) >+{ >+ struct umb_softc *sc = device_get_softc(dev); >+ struct ifnet *ifp = GET_IFP(sc); >+ >+ usb_proc_drain(&sc->sc_taskqueue); >+ >+ mtx_lock(&sc->sc_mutex); >+ if (ifp != NULL && (ifp->if_drv_flags & IFF_DRV_RUNNING)) >+ umb_down(sc, 1); >+ umb_close(sc); >+ mtx_unlock(&sc->sc_mutex); >+ >+ usbd_transfer_unsetup(sc->sc_xfer, UMB_N_TRANSFER); >+ >+ free(sc->sc_tx_buf, M_DEVBUF); >+ free(sc->sc_rx_buf, M_DEVBUF); >+ >+ usb_callout_drain(&sc->sc_statechg_timer); >+ >+ usb_proc_free(&sc->sc_taskqueue); >+ >+ mtx_destroy(&sc->sc_mutex); >+ >+ free(sc->sc_ctrl_msg, M_DEVBUF); >+ free(sc->sc_resp_buf, M_DEVBUF); >+ >+ if (ifp != NULL && ifp->if_softc) { >+ ifmedia_removeall(&sc->sc_im); >+ } >+ if (sc->sc_attached) { >+ bpfdetach(ifp); >+ if_detach(ifp); >+ if_free(ifp); >+ sc->sc_if = NULL; >+ } >+ >+ return 0; >+} >+ >+static void >+umb_ncm_setup(struct umb_softc *sc, struct usb_config * config) >+{ >+ usb_device_request_t req; >+ struct ncm_ntb_parameters np; >+ usb_error_t error; >+ >+ /* Query NTB tranfers sizes */ >+ req.bmRequestType = UT_READ_CLASS_INTERFACE; >+ req.bRequest = NCM_GET_NTB_PARAMETERS; >+ USETW(req.wValue, 0); >+ USETW(req.wIndex, sc->sc_ctrl_ifaceno); >+ USETW(req.wLength, sizeof (np)); >+ mtx_lock(&sc->sc_mutex); >+ error = usbd_do_request(sc->sc_udev, &sc->sc_mutex, &req, &np); >+ mtx_unlock(&sc->sc_mutex); >+ if (error == USB_ERR_NORMAL_COMPLETION && >+ UGETW(np.wLength) == sizeof (np)) { >+ config[UMB_BULK_RX].bufsize = UGETDW(np.dwNtbInMaxSize); >+ config[UMB_BULK_TX].bufsize = UGETDW(np.dwNtbOutMaxSize); >+ } >+ sc->sc_rx_bufsz = config[UMB_BULK_RX].bufsize; >+ sc->sc_tx_bufsz = config[UMB_BULK_TX].bufsize; >+} >+ >+static int >+umb_handle_request(device_t dev, >+ const void *preq, void **pptr, uint16_t *plen, >+ uint16_t offset, uint8_t *pstate) >+{ >+ /* FIXME really implement */ >+ >+ return (ENXIO); >+} >+ >+static int >+umb_suspend(device_t dev) >+{ >+ device_printf(dev, "Suspending\n"); >+ return (0); >+} >+ >+static int >+umb_resume(device_t dev) >+{ >+ device_printf(dev, "Resuming\n"); >+ return (0); >+} >+ >+static int >+umb_deactivate(device_t dev) >+{ >+ struct umb_softc *sc = device_get_softc(dev); >+ struct ifnet *ifp = GET_IFP(sc); >+ >+ if (ifp != NULL) { >+ if_dead(ifp); >+ } >+ sc->sc_dying = 1; >+ return 0; >+} >+ >+static void >+umb_close_bulkpipes(struct umb_softc *sc) >+{ >+ struct ifnet *ifp = GET_IFP(sc); >+ >+ ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); >+ >+ umb_rxflush(sc); >+ umb_txflush(sc); >+ >+ usbd_transfer_stop(sc->sc_xfer[UMB_BULK_RX]); >+ usbd_transfer_stop(sc->sc_xfer[UMB_BULK_TX]); >+} >+ >+static int >+umb_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) >+{ >+ struct umb_softc *sc = ifp->if_softc; >+ struct in_ifaddr *ia = (struct in_ifaddr *)data; >+ struct ifreq *ifr = (struct ifreq *)data; >+ int error = 0; >+ struct umb_parameter mp; >+ >+ if (sc->sc_dying) >+ return EIO; >+ >+ switch (cmd) { >+ case SIOCSIFADDR: >+ switch (ia->ia_ifa.ifa_addr->sa_family) { >+ case AF_INET: >+ break; >+#ifdef INET6 >+ case AF_INET6: >+ break; >+#endif /* INET6 */ >+ default: >+ error = EAFNOSUPPORT; >+ break; >+ } >+ break; >+ case SIOCSIFFLAGS: >+ mtx_lock(&sc->sc_mutex); >+ umb_add_task(sc, umb_state_task, >+ &sc->sc_proc_state_task[0].hdr, >+ &sc->sc_proc_state_task[1].hdr, 1); >+ mtx_unlock(&sc->sc_mutex); >+ break; >+ case SIOCGUMBINFO: >+ error = copyout(&sc->sc_info, ifr->ifr_ifru.ifru_data, >+ sizeof (sc->sc_info)); >+ break; >+ case SIOCSUMBPARAM: >+ error = priv_check(curthread, PRIV_NET_SETIFPHYS); >+ if (error) >+ break; >+ >+ if ((error = copyin(ifr->ifr_ifru.ifru_data, &mp, sizeof (mp))) != 0) >+ break; >+ >+ if ((error = umb_setpin(sc, mp.op, mp.is_puk, mp.pin, mp.pinlen, >+ mp.newpin, mp.newpinlen)) != 0) >+ break; >+ >+ if (mp.apnlen < 0 || mp.apnlen > sizeof (sc->sc_info.apn)) { >+ error = EINVAL; >+ break; >+ } >+ sc->sc_roaming = mp.roaming ? 1 : 0; >+ memset(sc->sc_info.apn, 0, sizeof (sc->sc_info.apn)); >+ memcpy(sc->sc_info.apn, mp.apn, mp.apnlen); >+ sc->sc_info.apnlen = mp.apnlen; >+ memset(sc->sc_info.username, 0, sizeof (sc->sc_info.username)); >+ memcpy(sc->sc_info.username, mp.username, mp.usernamelen); >+ sc->sc_info.usernamelen = mp.usernamelen; >+ memset(sc->sc_info.password, 0, sizeof (sc->sc_info.password)); >+ memcpy(sc->sc_info.password, mp.password, mp.passwordlen); >+ sc->sc_info.passwordlen = mp.passwordlen; >+ sc->sc_info.preferredclasses = mp.preferredclasses; >+ umb_setdataclass(sc); >+ break; >+ case SIOCGUMBPARAM: >+ memset(&mp, 0, sizeof (mp)); >+ memcpy(mp.apn, sc->sc_info.apn, sc->sc_info.apnlen); >+ mp.apnlen = sc->sc_info.apnlen; >+ mp.roaming = sc->sc_roaming; >+ mp.preferredclasses = sc->sc_info.preferredclasses; >+ error = copyout(&mp, ifr->ifr_ifru.ifru_data, sizeof (mp)); >+ break; >+ case SIOCSIFMTU: >+ /* Does this include the NCM headers and tail? */ >+ if (ifr->ifr_mtu > ifp->if_mtu) { >+ error = EINVAL; >+ break; >+ } >+ ifp->if_mtu = ifr->ifr_mtu; >+ break; >+ case SIOCAIFADDR: >+ case SIOCSIFDSTADDR: >+ case SIOCADDMULTI: >+ case SIOCDELMULTI: >+ break; >+ case SIOCGIFMEDIA: >+ error = ifmedia_ioctl(ifp, ifr, &sc->sc_im, cmd); >+ break; >+ default: >+ error = EINVAL; >+ break; >+ } >+ return (error); >+} >+ >+static void >+umb_init(void *arg) >+{ >+ struct umb_softc *sc = arg; >+ >+ mtx_lock(&sc->sc_mutex); >+ umb_add_task(sc, umb_start_task, >+ &sc->sc_proc_start_task[0].hdr, >+ &sc->sc_proc_start_task[1].hdr, 0); >+ mtx_unlock(&sc->sc_mutex); >+} >+ >+static void >+umb_input(struct ifnet *ifp, struct mbuf *m) >+{ >+ struct mbuf *mn; >+ struct epoch_tracker et; >+ >+ while (m) { >+ mn = m->m_nextpkt; >+ m->m_nextpkt = NULL; >+ >+ NET_EPOCH_ENTER(et); >+ BPF_MTAP(ifp, m); >+ >+ CURVNET_SET_QUIET(vnet0); >+ >+ netisr_dispatch(NETISR_IP, m); >+ m = mn; >+ >+ CURVNET_RESTORE(); >+ NET_EPOCH_EXIT(et); >+ } >+} >+ >+static int >+umb_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst, >+ struct route *rtp) >+{ >+ int error; >+ >+ DPRINTFN(10, "%s: enter\n", __func__); >+ >+ switch (dst->sa_family) { >+#ifdef INET6 >+ case AF_INET6: >+ /* fall through */ >+#endif >+ case AF_INET: >+ break; >+ >+ /* silently drop dhclient packets */ >+ case AF_UNSPEC: >+ m_freem(m); >+ return (0); >+ >+ /* drop other packet types */ >+ default: >+ m_freem(m); >+ return (EAFNOSUPPORT); >+ } >+ >+ /* >+ * Queue message on interface, and start output if interface >+ * not yet active. >+ */ >+ error = (ifp->if_transmit)(ifp, m); >+ if (error) { >+ if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); >+ return (ENOBUFS); >+ } >+ >+ return (0); >+} >+ >+static void >+umb_start(struct ifnet *ifp) >+{ >+ struct umb_softc *sc = ifp->if_softc; >+ >+ if (sc->sc_dying || !(ifp->if_drv_flags & IFF_DRV_RUNNING)) >+ return; >+ >+ mtx_lock(&sc->sc_mutex); >+ usbd_transfer_start(sc->sc_xfer[UMB_BULK_TX]); >+ mtx_unlock(&sc->sc_mutex); >+} >+ >+static void >+umb_start_task(struct usb_proc_msg *msg) >+{ >+ struct umb_task *task = (struct umb_task *)msg; >+ struct umb_softc *sc = task->sc; >+ struct ifnet *ifp = GET_IFP(sc); >+ >+ DPRINTF("%s()\n", __func__); >+ >+ mtx_assert(&sc->sc_mutex, MA_OWNED); >+ >+ ifp->if_drv_flags |= IFF_DRV_RUNNING; >+ >+ /* start interrupt transfer */ >+ usbd_transfer_start(sc->sc_xfer[UMB_INTR_RX]); >+ >+ umb_open(sc); >+} >+ >+#if 0 >+static void >+umb_watchdog(struct ifnet *ifp) >+{ >+ struct umb_softc *sc = ifp->if_softc; >+ >+ if (sc->sc_dying) >+ return; >+ >+ if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); >+ device_printf(sc->sc_dev, "watchdog timeout\n"); >+ usbd_transfer_drain(sc->sc_xfer[UMB_BULK_TX]); >+ return; >+} >+#endif >+ >+static void >+umb_statechg_timeout(void *arg) >+{ >+ struct umb_softc *sc = arg; >+ struct ifnet *ifp = GET_IFP(sc); >+ >+ mtx_assert(&sc->sc_mutex, MA_OWNED); >+ >+ if (sc->sc_info.regstate != MBIM_REGSTATE_ROAMING || sc->sc_roaming) >+ if (ifp->if_flags & IFF_DEBUG) >+ log(LOG_DEBUG, "%s: state change timeout\n", >+ DEVNAM(sc)); >+ >+ umb_add_task(sc, umb_state_task, >+ &sc->sc_proc_state_task[0].hdr, >+ &sc->sc_proc_state_task[1].hdr, 0); >+} >+ >+static int >+umb_mediachange(struct ifnet * ifp) >+{ >+ return 0; >+} >+ >+static void >+umb_mediastatus(struct ifnet * ifp, struct ifmediareq * imr) >+{ >+ switch (ifp->if_link_state) { >+ case LINK_STATE_UP: >+ imr->ifm_status = IFM_AVALID | IFM_ACTIVE; >+ break; >+ case LINK_STATE_DOWN: >+ imr->ifm_status = IFM_AVALID; >+ break; >+ default: >+ imr->ifm_status = 0; >+ break; >+ } >+} >+ >+static void >+umb_add_task(struct umb_softc *sc, usb_proc_callback_t callback, >+ struct usb_proc_msg *t0, struct usb_proc_msg *t1, int sync) >+{ >+ struct umb_task * task; >+ >+ mtx_assert(&sc->sc_mutex, MA_OWNED); >+ >+ if (usb_proc_is_gone(&sc->sc_taskqueue)) { >+ return; >+ } >+ >+ task = usb_proc_msignal(&sc->sc_taskqueue, t0, t1); >+ >+ task->hdr.pm_callback = callback; >+ task->sc = sc; >+ >+ if (sync) { >+ usb_proc_mwait(&sc->sc_taskqueue, t0, t1); >+ } >+} >+ >+static void >+umb_newstate(struct umb_softc *sc, enum umb_state newstate, int flags) >+{ >+ struct ifnet *ifp = GET_IFP(sc); >+ >+ if (newstate == sc->sc_state) >+ return; >+ if (((flags & UMB_NS_DONT_DROP) && newstate < sc->sc_state) || >+ ((flags & UMB_NS_DONT_RAISE) && newstate > sc->sc_state)) >+ return; >+ if (ifp->if_flags & IFF_DEBUG) >+ log(LOG_DEBUG, "%s: state going %s from '%s' to '%s'\n", >+ DEVNAM(sc), newstate > sc->sc_state ? "up" : "down", >+ umb_istate(sc->sc_state), umb_istate(newstate)); >+ sc->sc_state = newstate; >+ umb_add_task(sc, umb_state_task, >+ &sc->sc_proc_state_task[0].hdr, >+ &sc->sc_proc_state_task[1].hdr, 0); >+} >+ >+static void >+umb_state_task(struct usb_proc_msg *msg) >+{ >+ struct umb_task *task = (struct umb_task *)msg; >+ struct umb_softc *sc = task->sc; >+ struct ifnet *ifp = GET_IFP(sc); >+ struct ifreq ifr; >+ int state; >+ >+ DPRINTF("%s()\n", __func__); >+ >+ if (sc->sc_info.regstate == MBIM_REGSTATE_ROAMING && !sc->sc_roaming) { >+ /* >+ * Query the registration state until we're with the home >+ * network again. >+ */ >+ umb_cmd(sc, MBIM_CID_REGISTER_STATE, MBIM_CMDOP_QRY, NULL, 0); >+ return; >+ } >+ >+ if (ifp->if_flags & IFF_UP) >+ umb_up(sc); >+ else >+ umb_down(sc, 0); >+ >+ state = (sc->sc_state == UMB_S_UP) ? LINK_STATE_UP : LINK_STATE_DOWN; >+ if (ifp->if_link_state != state) { >+ if (ifp->if_flags & IFF_DEBUG) >+ log(LOG_DEBUG, "%s: link state changed from %s to %s\n", >+ DEVNAM(sc), >+ (ifp->if_link_state == LINK_STATE_UP) >+ ? "up" : "down", >+ (state == LINK_STATE_UP) ? "up" : "down"); >+ ifp->if_link_state = state; >+ if (state != LINK_STATE_UP) { >+ /* >+ * Purge any existing addresses >+ */ >+ memset(sc->sc_info.ipv4dns, 0, >+ sizeof (sc->sc_info.ipv4dns)); >+ mtx_unlock(&sc->sc_mutex); >+ CURVNET_SET_QUIET(vnet0); >+ if (in_control(NULL, SIOCGIFADDR, (caddr_t)&ifr, ifp, >+ curthread) == 0 && >+ satosin(&ifr.ifr_addr)->sin_addr.s_addr != >+ INADDR_ANY) { >+ in_control(NULL, SIOCDIFADDR, (caddr_t)&ifr, >+ ifp, curthread); >+ } >+ CURVNET_RESTORE(); >+ mtx_lock(&sc->sc_mutex); >+ } >+ if_link_state_change(ifp, state); >+ } >+} >+ >+static void >+umb_up(struct umb_softc *sc) >+{ >+ struct ifnet *ifp = GET_IFP(sc); >+ >+ switch (sc->sc_state) { >+ case UMB_S_DOWN: >+ DPRINTF("init: opening ...\n"); >+ umb_open(sc); >+ break; >+ case UMB_S_OPEN: >+ if (sc->sc_flags & UMBFLG_FCC_AUTH_REQUIRED) { >+ if (sc->sc_cid == -1) { >+ DPRINTF("init: allocating CID ...\n"); >+ umb_allocate_cid(sc); >+ break; >+ } else >+ umb_newstate(sc, UMB_S_CID, UMB_NS_DONT_DROP); >+ } else { >+ DPRINTF("init: turning radio on ...\n"); >+ umb_radio(sc, 1); >+ break; >+ } >+ /*FALLTHROUGH*/ >+ case UMB_S_CID: >+ DPRINTF("init: sending FCC auth ...\n"); >+ umb_send_fcc_auth(sc); >+ break; >+ case UMB_S_RADIO: >+ DPRINTF("init: checking SIM state ...\n"); >+ umb_cmd(sc, MBIM_CID_SUBSCRIBER_READY_STATUS, MBIM_CMDOP_QRY, >+ NULL, 0); >+ break; >+ case UMB_S_SIMREADY: >+ DPRINTF("init: attaching ...\n"); >+ umb_packet_service(sc, 1); >+ break; >+ case UMB_S_ATTACHED: >+ sc->sc_tx_seq = 0; >+ DPRINTF("init: connecting ...\n"); >+ umb_connect(sc); >+ break; >+ case UMB_S_CONNECTED: >+ DPRINTF("init: getting IP config ...\n"); >+ umb_qry_ipconfig(sc); >+ break; >+ case UMB_S_UP: >+ DPRINTF("init: reached state UP\n"); >+ if (!(ifp->if_flags & IFF_DRV_RUNNING)) { >+ ifp->if_drv_flags |= IFF_DRV_RUNNING; >+ ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; >+ umb_rx(sc); >+ } >+ break; >+ } >+ if (sc->sc_state < UMB_S_UP) >+ usb_callout_reset(&sc->sc_statechg_timer, >+ UMB_STATE_CHANGE_TIMEOUT * hz, umb_statechg_timeout, sc); >+ else { >+ usb_callout_stop(&sc->sc_statechg_timer); >+ } >+ return; >+} >+ >+static void >+umb_down(struct umb_softc *sc, int force) >+{ >+ umb_close_bulkpipes(sc); >+ >+ switch (sc->sc_state) { >+ case UMB_S_UP: >+ case UMB_S_CONNECTED: >+ DPRINTF("stop: disconnecting ...\n"); >+ umb_disconnect(sc); >+ if (!force) >+ break; >+ /*FALLTHROUGH*/ >+ case UMB_S_ATTACHED: >+ DPRINTF("stop: detaching ...\n"); >+ umb_packet_service(sc, 0); >+ if (!force) >+ break; >+ /*FALLTHROUGH*/ >+ case UMB_S_SIMREADY: >+ case UMB_S_RADIO: >+ DPRINTF("stop: turning radio off ...\n"); >+ umb_radio(sc, 0); >+ if (!force) >+ break; >+ /*FALLTHROUGH*/ >+ case UMB_S_CID: >+ case UMB_S_OPEN: >+ case UMB_S_DOWN: >+ /* Do not close the device */ >+ DPRINTF("stop: reached state DOWN\n"); >+ break; >+ } >+ if (force) >+ sc->sc_state = UMB_S_OPEN; >+ >+ if (sc->sc_state > UMB_S_OPEN) >+ usb_callout_reset(&sc->sc_statechg_timer, >+ UMB_STATE_CHANGE_TIMEOUT * hz, umb_statechg_timeout, sc); >+ else >+ usb_callout_stop(&sc->sc_statechg_timer); >+} >+ >+static void >+umb_get_response_task(struct usb_proc_msg *msg) >+{ >+ struct umb_task *task = (struct umb_task *)msg; >+ struct umb_softc *sc = task->sc; >+ int len; >+ >+ DPRINTF("%s()\n", __func__); >+ /* >+ * Function is required to send on RESPONSE_AVAILABLE notification for >+ * each encapsulated response that is to be processed by the host. >+ * But of course, we can receive multiple notifications before the >+ * response task is run. >+ */ >+ while (sc->sc_nresp > 0) { >+ --sc->sc_nresp; >+ len = sc->sc_ctrl_len; >+ if (umb_get_encap_response(sc, sc->sc_resp_buf, &len)) >+ umb_decode_response(sc, sc->sc_resp_buf, len); >+ } >+} >+ >+static void >+umb_decode_response(struct umb_softc *sc, void *response, int len) >+{ >+ struct mbim_msghdr *hdr = response; >+ struct mbim_fragmented_msg_hdr *fraghdr; >+ uint32_t type; >+ >+ DPRINTFN(3, "got response: len %d\n", len); >+ DDUMPN(4, response, len); >+ >+ if (len < sizeof (*hdr) || le32toh(hdr->len) != len) { >+ /* >+ * We should probably cancel a transaction, but since the >+ * message is too short, we cannot decode the transaction >+ * id (tid) and hence don't know, whom to cancel. Must wait >+ * for the timeout. >+ */ >+ DPRINTF("received short response (len %d)\n", >+ len); >+ return; >+ } >+ >+ /* >+ * XXX FIXME: if message is fragmented, store it until last frag >+ * is received and then re-assemble all fragments. >+ */ >+ type = le32toh(hdr->type); >+ switch (type) { >+ case MBIM_INDICATE_STATUS_MSG: >+ case MBIM_COMMAND_DONE: >+ fraghdr = response; >+ if (le32toh(fraghdr->frag.nfrag) != 1) { >+ DPRINTF("discarding fragmented messages\n"); >+ return; >+ } >+ break; >+ default: >+ break; >+ } >+ >+ DPRINTF("<- rcv %s (tid %u)\n", umb_request2str(type), >+ le32toh(hdr->tid)); >+ switch (type) { >+ case MBIM_FUNCTION_ERROR_MSG: >+ case MBIM_HOST_ERROR_MSG: >+ { >+ struct mbim_f2h_hosterr *e; >+ int err; >+ >+ if (len >= sizeof (*e)) { >+ e = response; >+ err = le32toh(e->err); >+ >+ DPRINTF("%s message, error %s (tid %u)\n", >+ umb_request2str(type), >+ umb_error2str(err), le32toh(hdr->tid)); >+ if (err == MBIM_ERROR_NOT_OPENED) >+ umb_newstate(sc, UMB_S_DOWN, 0); >+ } >+ break; >+ } >+ case MBIM_INDICATE_STATUS_MSG: >+ umb_handle_indicate_status_msg(sc, response, len); >+ break; >+ case MBIM_OPEN_DONE: >+ umb_handle_opendone_msg(sc, response, len); >+ break; >+ case MBIM_CLOSE_DONE: >+ umb_handle_closedone_msg(sc, response, len); >+ break; >+ case MBIM_COMMAND_DONE: >+ umb_command_done(sc, response, len); >+ break; >+ default: >+ DPRINTF("discard message %s\n", >+ umb_request2str(type)); >+ break; >+ } >+} >+ >+static void >+umb_handle_indicate_status_msg(struct umb_softc *sc, void *data, int len) >+{ >+ struct mbim_f2h_indicate_status *m = data; >+ uint32_t infolen; >+ uint32_t cid; >+ >+ if (len < sizeof (*m)) { >+ DPRINTF("discard short %s message\n", >+ umb_request2str(le32toh(m->hdr.type))); >+ return; >+ } >+ if (memcmp(m->devid, umb_uuid_basic_connect, sizeof (m->devid))) { >+ DPRINTF("discard %s message for other UUID '%s'\n", >+ umb_request2str(le32toh(m->hdr.type)), >+ umb_uuid2str(m->devid)); >+ return; >+ } >+ infolen = le32toh(m->infolen); >+ if (len < sizeof (*m) + infolen) { >+ DPRINTF("discard truncated %s message (want %d, got %d)\n", >+ umb_request2str(le32toh(m->hdr.type)), >+ (int)sizeof (*m) + infolen, len); >+ return; >+ } >+ >+ cid = le32toh(m->cid); >+ DPRINTF("indicate %s status\n", umb_cid2str(cid)); >+ umb_decode_cid(sc, cid, m->info, infolen); >+} >+ >+static void >+umb_handle_opendone_msg(struct umb_softc *sc, void *data, int len) >+{ >+ struct mbim_f2h_openclosedone *resp = data; >+ struct ifnet *ifp = GET_IFP(sc); >+ uint32_t status; >+ >+ status = le32toh(resp->status); >+ if (status == MBIM_STATUS_SUCCESS) { >+ if (sc->sc_maxsessions == 0) { >+ umb_cmd(sc, MBIM_CID_DEVICE_CAPS, MBIM_CMDOP_QRY, NULL, >+ 0); >+ umb_cmd(sc, MBIM_CID_PIN, MBIM_CMDOP_QRY, NULL, 0); >+ umb_cmd(sc, MBIM_CID_REGISTER_STATE, MBIM_CMDOP_QRY, >+ NULL, 0); >+ } >+ umb_newstate(sc, UMB_S_OPEN, UMB_NS_DONT_DROP); >+ } else if (ifp->if_flags & IFF_DEBUG) >+ log(LOG_ERR, "%s: open error: %s\n", DEVNAM(sc), >+ umb_status2str(status)); >+ return; >+} >+ >+static void >+umb_handle_closedone_msg(struct umb_softc *sc, void *data, int len) >+{ >+ struct mbim_f2h_openclosedone *resp = data; >+ uint32_t status; >+ >+ status = le32toh(resp->status); >+ if (status == MBIM_STATUS_SUCCESS) >+ umb_newstate(sc, UMB_S_DOWN, 0); >+ else >+ DPRINTF("close error: %s\n", >+ umb_status2str(status)); >+ return; >+} >+ >+static inline void >+umb_getinfobuf(char *in, int inlen, uint32_t offs, uint32_t sz, >+ void *out, size_t outlen) >+{ >+ offs = le32toh(offs); >+ sz = le32toh(sz); >+ if (inlen >= offs + sz) { >+ memset(out, 0, outlen); >+ memcpy(out, in + offs, MIN(sz, outlen)); >+ } >+} >+ >+static inline int >+umb_padding(void *data, int len, size_t sz) >+{ >+ char *p = data; >+ int np = 0; >+ >+ while (len < sz && (len % 4) != 0) { >+ *p++ = '\0'; >+ len++; >+ np++; >+ } >+ return np; >+} >+ >+static inline int >+umb_addstr(void *buf, size_t bufsz, int *offs, void *str, int slen, >+ uint32_t *offsmember, uint32_t *sizemember) >+{ >+ if (*offs + slen > bufsz) >+ return 0; >+ >+ *sizemember = htole32((uint32_t)slen); >+ if (slen && str) { >+ *offsmember = htole32((uint32_t)*offs); >+ memcpy((char *)buf + *offs, str, slen); >+ *offs += slen; >+ *offs += umb_padding(buf, *offs, bufsz); >+ } else >+ *offsmember = htole32(0); >+ return 1; >+} >+ >+static void >+umb_in_len2mask(struct in_addr *mask, int len) >+{ >+ int i; >+ u_char *p; >+ >+ p = (u_char *)mask; >+ memset(mask, 0, sizeof (*mask)); >+ for (i = 0; i < len / 8; i++) >+ p[i] = 0xff; >+ if (len % 8) >+ p[i] = (0xff00 >> (len % 8)) & 0xff; >+} >+ >+static int >+umb_decode_register_state(struct umb_softc *sc, void *data, int len) >+{ >+ struct mbim_cid_registration_state_info *rs = data; >+ struct ifnet *ifp = GET_IFP(sc); >+ >+ if (len < sizeof (*rs)) >+ return 0; >+ sc->sc_info.nwerror = le32toh(rs->nwerror); >+ sc->sc_info.regstate = le32toh(rs->regstate); >+ sc->sc_info.regmode = le32toh(rs->regmode); >+ sc->sc_info.cellclass = le32toh(rs->curcellclass); >+ >+ /* XXX should we remember the provider_id? */ >+ umb_getinfobuf(data, len, rs->provname_offs, rs->provname_size, >+ sc->sc_info.provider, sizeof (sc->sc_info.provider)); >+ umb_getinfobuf(data, len, rs->roamingtxt_offs, rs->roamingtxt_size, >+ sc->sc_info.roamingtxt, sizeof (sc->sc_info.roamingtxt)); >+ >+ DPRINTFN(2, "%s, availclass 0x%x, class 0x%x, regmode %d\n", >+ umb_regstate(sc->sc_info.regstate), >+ le32toh(rs->availclasses), sc->sc_info.cellclass, >+ sc->sc_info.regmode); >+ >+ if (sc->sc_info.regstate == MBIM_REGSTATE_ROAMING && >+ !sc->sc_roaming && >+ sc->sc_info.activation == MBIM_ACTIVATION_STATE_ACTIVATED) { >+ if (ifp->if_flags & IFF_DEBUG) >+ log(LOG_INFO, >+ "%s: disconnecting from roaming network\n", >+ DEVNAM(sc)); >+ umb_disconnect(sc); >+ } >+ return 1; >+} >+ >+static int >+umb_decode_devices_caps(struct umb_softc *sc, void *data, int len) >+{ >+ struct mbim_cid_device_caps *dc = data; >+ >+ if (len < sizeof (*dc)) >+ return 0; >+ sc->sc_maxsessions = le32toh(dc->max_sessions); >+ sc->sc_info.supportedclasses = le32toh(dc->dataclass); >+ umb_getinfobuf(data, len, dc->devid_offs, dc->devid_size, >+ sc->sc_info.devid, sizeof (sc->sc_info.devid)); >+ umb_getinfobuf(data, len, dc->fwinfo_offs, dc->fwinfo_size, >+ sc->sc_info.fwinfo, sizeof (sc->sc_info.fwinfo)); >+ umb_getinfobuf(data, len, dc->hwinfo_offs, dc->hwinfo_size, >+ sc->sc_info.hwinfo, sizeof (sc->sc_info.hwinfo)); >+ DPRINTFN(2, "max sessions %d, supported classes 0x%x\n", >+ sc->sc_maxsessions, sc->sc_info.supportedclasses); >+ return 1; >+} >+ >+static int >+umb_decode_subscriber_status(struct umb_softc *sc, void *data, int len) >+{ >+ struct mbim_cid_subscriber_ready_info *si = data; >+ struct ifnet *ifp = GET_IFP(sc); >+ int npn; >+ >+ if (len < sizeof (*si)) >+ return 0; >+ sc->sc_info.sim_state = le32toh(si->ready); >+ >+ umb_getinfobuf(data, len, si->sid_offs, si->sid_size, >+ sc->sc_info.sid, sizeof (sc->sc_info.sid)); >+ umb_getinfobuf(data, len, si->icc_offs, si->icc_size, >+ sc->sc_info.iccid, sizeof (sc->sc_info.iccid)); >+ >+ npn = le32toh(si->no_pn); >+ if (npn > 0) >+ umb_getinfobuf(data, len, si->pn[0].offs, si->pn[0].size, >+ sc->sc_info.pn, sizeof (sc->sc_info.pn)); >+ else >+ memset(sc->sc_info.pn, 0, sizeof (sc->sc_info.pn)); >+ >+ if (sc->sc_info.sim_state == MBIM_SIMSTATE_LOCKED) >+ sc->sc_info.pin_state = UMB_PIN_REQUIRED; >+ if (ifp->if_flags & IFF_DEBUG) >+ log(LOG_INFO, "%s: SIM %s\n", DEVNAM(sc), >+ umb_simstate(sc->sc_info.sim_state)); >+ if (sc->sc_info.sim_state == MBIM_SIMSTATE_INITIALIZED) >+ umb_newstate(sc, UMB_S_SIMREADY, UMB_NS_DONT_DROP); >+ return 1; >+} >+ >+static int >+umb_decode_radio_state(struct umb_softc *sc, void *data, int len) >+{ >+ struct mbim_cid_radio_state_info *rs = data; >+ struct ifnet *ifp = GET_IFP(sc); >+ >+ if (len < sizeof (*rs)) >+ return 0; >+ >+ sc->sc_info.hw_radio_on = >+ (le32toh(rs->hw_state) == MBIM_RADIO_STATE_ON) ? 1 : 0; >+ sc->sc_info.sw_radio_on = >+ (le32toh(rs->sw_state) == MBIM_RADIO_STATE_ON) ? 1 : 0; >+ if (!sc->sc_info.hw_radio_on) { >+ device_printf(sc->sc_dev, "radio is disabled by hardware switch\n"); >+ /* >+ * XXX do we need a time to poll the state of the rfkill switch >+ * or will the device send an unsolicited notification >+ * in case the state changes? >+ */ >+ umb_newstate(sc, UMB_S_OPEN, 0); >+ } else if (!sc->sc_info.sw_radio_on) { >+ if (ifp->if_flags & IFF_DEBUG) >+ log(LOG_INFO, "%s: radio is off\n", DEVNAM(sc)); >+ umb_newstate(sc, UMB_S_OPEN, 0); >+ } else >+ umb_newstate(sc, UMB_S_RADIO, UMB_NS_DONT_DROP); >+ return 1; >+} >+ >+static int >+umb_decode_pin(struct umb_softc *sc, void *data, int len) >+{ >+ struct mbim_cid_pin_info *pi = data; >+ struct ifnet *ifp = GET_IFP(sc); >+ uint32_t attempts_left; >+ >+ if (len < sizeof (*pi)) >+ return 0; >+ >+ attempts_left = le32toh(pi->remaining_attempts); >+ if (attempts_left != 0xffffffff) >+ sc->sc_info.pin_attempts_left = attempts_left; >+ >+ switch (le32toh(pi->state)) { >+ case MBIM_PIN_STATE_UNLOCKED: >+ sc->sc_info.pin_state = UMB_PIN_UNLOCKED; >+ break; >+ case MBIM_PIN_STATE_LOCKED: >+ switch (le32toh(pi->type)) { >+ case MBIM_PIN_TYPE_PIN1: >+ sc->sc_info.pin_state = UMB_PIN_REQUIRED; >+ break; >+ case MBIM_PIN_TYPE_PUK1: >+ sc->sc_info.pin_state = UMB_PUK_REQUIRED; >+ break; >+ case MBIM_PIN_TYPE_PIN2: >+ case MBIM_PIN_TYPE_PUK2: >+ /* Assume that PIN1 was accepted */ >+ sc->sc_info.pin_state = UMB_PIN_UNLOCKED; >+ break; >+ } >+ break; >+ } >+ if (ifp->if_flags & IFF_DEBUG) >+ log(LOG_INFO, "%s: %s state %s (%d attempts left)\n", >+ DEVNAM(sc), umb_pin_type(le32toh(pi->type)), >+ (le32toh(pi->state) == MBIM_PIN_STATE_UNLOCKED) ? >+ "unlocked" : "locked", >+ le32toh(pi->remaining_attempts)); >+ >+ /* >+ * In case the PIN was set after IFF_UP, retrigger the state machine >+ */ >+ umb_add_task(sc, umb_state_task, >+ &sc->sc_proc_state_task[0].hdr, >+ &sc->sc_proc_state_task[1].hdr, 0); >+ return 1; >+} >+ >+static int >+umb_decode_packet_service(struct umb_softc *sc, void *data, int len) >+{ >+ struct mbim_cid_packet_service_info *psi = data; >+ int state, highestclass; >+ uint64_t up_speed, down_speed; >+ struct ifnet *ifp = GET_IFP(sc); >+ >+ if (len < sizeof (*psi)) >+ return 0; >+ >+ sc->sc_info.nwerror = le32toh(psi->nwerror); >+ state = le32toh(psi->state); >+ highestclass = le32toh(psi->highest_dataclass); >+ up_speed = le64toh(psi->uplink_speed); >+ down_speed = le64toh(psi->downlink_speed); >+ if (sc->sc_info.packetstate != state || >+ sc->sc_info.uplink_speed != up_speed || >+ sc->sc_info.downlink_speed != down_speed) { >+ if (ifp->if_flags & IFF_DEBUG) { >+ log(LOG_INFO, "%s: packet service ", DEVNAM(sc)); >+ if (sc->sc_info.packetstate != state) >+ log(LOG_INFO, "changed from %s to ", >+ umb_packet_state(sc->sc_info.packetstate)); >+ log(LOG_INFO, "%s, class %s, speed: %" PRIu64 " up / %" PRIu64 " down\n", >+ umb_packet_state(state), >+ umb_dataclass(highestclass), up_speed, down_speed); >+ } >+ } >+ sc->sc_info.packetstate = state; >+ sc->sc_info.highestclass = highestclass; >+ sc->sc_info.uplink_speed = up_speed; >+ sc->sc_info.downlink_speed = down_speed; >+ >+ if (sc->sc_info.regmode == MBIM_REGMODE_AUTOMATIC) { >+ /* >+ * For devices using automatic registration mode, just proceed, >+ * once registration has completed. >+ */ >+ if (ifp->if_flags & IFF_UP) { >+ switch (sc->sc_info.regstate) { >+ case MBIM_REGSTATE_HOME: >+ case MBIM_REGSTATE_ROAMING: >+ case MBIM_REGSTATE_PARTNER: >+ umb_newstate(sc, UMB_S_ATTACHED, >+ UMB_NS_DONT_DROP); >+ break; >+ default: >+ break; >+ } >+ } else >+ umb_newstate(sc, UMB_S_SIMREADY, UMB_NS_DONT_RAISE); >+ } else switch (sc->sc_info.packetstate) { >+ case MBIM_PKTSERVICE_STATE_ATTACHED: >+ umb_newstate(sc, UMB_S_ATTACHED, UMB_NS_DONT_DROP); >+ break; >+ case MBIM_PKTSERVICE_STATE_DETACHED: >+ umb_newstate(sc, UMB_S_SIMREADY, UMB_NS_DONT_RAISE); >+ break; >+ } >+ return 1; >+} >+ >+static int >+umb_decode_signal_state(struct umb_softc *sc, void *data, int len) >+{ >+ struct mbim_cid_signal_state *ss = data; >+ struct ifnet *ifp = GET_IFP(sc); >+ int rssi; >+ >+ if (len < sizeof (*ss)) >+ return 0; >+ >+ if (le32toh(ss->rssi) == 99) >+ rssi = UMB_VALUE_UNKNOWN; >+ else { >+ rssi = -113 + 2 * le32toh(ss->rssi); >+ if ((ifp->if_flags & IFF_DEBUG) && sc->sc_info.rssi != rssi && >+ sc->sc_state >= UMB_S_CONNECTED) >+ log(LOG_INFO, "%s: rssi %d dBm\n", DEVNAM(sc), rssi); >+ } >+ sc->sc_info.rssi = rssi; >+ sc->sc_info.ber = le32toh(ss->err_rate); >+ if (sc->sc_info.ber == -99) >+ sc->sc_info.ber = UMB_VALUE_UNKNOWN; >+ return 1; >+} >+ >+static int >+umb_decode_connect_info(struct umb_softc *sc, void *data, int len) >+{ >+ struct mbim_cid_connect_info *ci = data; >+ struct ifnet *ifp = GET_IFP(sc); >+ int act; >+ >+ if (len < sizeof (*ci)) >+ return 0; >+ >+ if (le32toh(ci->sessionid) != umb_session_id) { >+ DPRINTF("discard connection info for session %u\n", >+ le32toh(ci->sessionid)); >+ return 1; >+ } >+ if (memcmp(ci->context, umb_uuid_context_internet, >+ sizeof (ci->context))) { >+ DPRINTF("discard connection info for other context\n"); >+ return 1; >+ } >+ act = le32toh(ci->activation); >+ if (sc->sc_info.activation != act) { >+ if (ifp->if_flags & IFF_DEBUG) >+ log(LOG_INFO, "%s: connection %s\n", DEVNAM(sc), >+ umb_activation(act)); >+ if ((ifp->if_flags & IFF_DEBUG) && >+ le32toh(ci->iptype) != MBIM_CONTEXT_IPTYPE_DEFAULT && >+ le32toh(ci->iptype) != MBIM_CONTEXT_IPTYPE_IPV4) >+ log(LOG_DEBUG, "%s: got iptype %d connection\n", >+ DEVNAM(sc), le32toh(ci->iptype)); >+ >+ sc->sc_info.activation = act; >+ sc->sc_info.nwerror = le32toh(ci->nwerror); >+ >+ if (sc->sc_info.activation == MBIM_ACTIVATION_STATE_ACTIVATED) >+ umb_newstate(sc, UMB_S_CONNECTED, UMB_NS_DONT_DROP); >+ else if (sc->sc_info.activation == >+ MBIM_ACTIVATION_STATE_DEACTIVATED) >+ umb_newstate(sc, UMB_S_ATTACHED, 0); >+ /* else: other states are purely transitional */ >+ } >+ return 1; >+} >+ >+static int >+umb_add_inet_config(struct umb_softc *sc, struct in_addr ip, u_int prefixlen, >+ struct in_addr gw) >+{ >+ struct ifnet *ifp = GET_IFP(sc); >+ struct in_aliasreq ifra; >+ struct sockaddr_in *sin; >+ int rv; >+ >+ memset(&ifra, 0, sizeof (ifra)); >+ sin = (struct sockaddr_in *)&ifra.ifra_addr; >+ sin->sin_family = AF_INET; >+ sin->sin_len = sizeof (*sin); >+ sin->sin_addr = ip; >+ >+ sin = (struct sockaddr_in *)&ifra.ifra_dstaddr; >+ sin->sin_family = AF_INET; >+ sin->sin_len = sizeof (*sin); >+ sin->sin_addr = gw; >+ >+ sin = (struct sockaddr_in *)&ifra.ifra_mask; >+ sin->sin_family = AF_INET; >+ sin->sin_len = sizeof (*sin); >+ umb_in_len2mask(&sin->sin_addr, prefixlen); >+ >+ mtx_unlock(&sc->sc_mutex); >+ CURVNET_SET_QUIET(vnet0); >+ rv = in_control(NULL, SIOCAIFADDR, (caddr_t)&ifra, ifp, curthread); >+ CURVNET_RESTORE(); >+ mtx_lock(&sc->sc_mutex); >+ if (rv != 0) { >+ device_printf(sc->sc_dev, "unable to set IPv4 address, error %d\n", >+ rv); >+ return rv; >+ } >+ >+ if (ifp->if_flags & IFF_DEBUG) >+ log(LOG_INFO, "%s: IPv4 addr %s, mask %s, " >+ "gateway %s\n", DEVNAM(sc), >+ umb_ntop(sintosa(&ifra.ifra_addr)), >+ umb_ntop(sintosa(&ifra.ifra_mask)), >+ umb_ntop(sintosa(&ifra.ifra_dstaddr))); >+ >+ return 0; >+} >+ >+static int >+umb_decode_ip_configuration(struct umb_softc *sc, void *data, int len) >+{ >+ struct mbim_cid_ip_configuration_info *ic = data; >+ struct ifnet *ifp = GET_IFP(sc); >+ uint32_t avail_v4; >+ uint32_t val; >+ int n, i; >+ int off; >+ struct mbim_cid_ipv4_element ipv4elem; >+ struct in_addr addr, gw; >+ int state = -1; >+ int rv; >+ >+ if (len < sizeof (*ic)) >+ return 0; >+ if (le32toh(ic->sessionid) != umb_session_id) { >+ DPRINTF("ignore IP configuration for session id %d\n", >+ le32toh(ic->sessionid)); >+ return 0; >+ } >+ >+ /* >+ * IPv4 configuration >+ */ >+ avail_v4 = le32toh(ic->ipv4_available); >+ if ((avail_v4 & (MBIM_IPCONF_HAS_ADDRINFO | MBIM_IPCONF_HAS_GWINFO)) == >+ (MBIM_IPCONF_HAS_ADDRINFO | MBIM_IPCONF_HAS_GWINFO)) { >+ n = le32toh(ic->ipv4_naddr); >+ off = le32toh(ic->ipv4_addroffs); >+ >+ if (n == 0 || off + sizeof (ipv4elem) > len) >+ goto tryv6; >+ if (n != 1 && ifp->if_flags & IFF_DEBUG) >+ log(LOG_INFO, "%s: more than one IPv4 addr: %d\n", >+ DEVNAM(sc), n); >+ >+ /* Only pick the first one */ >+ memcpy(&ipv4elem, (char *)data + off, sizeof (ipv4elem)); >+ ipv4elem.prefixlen = le32toh(ipv4elem.prefixlen); >+ addr.s_addr = ipv4elem.addr; >+ >+ off = le32toh(ic->ipv4_gwoffs); >+ if (off + sizeof (gw) > len) >+ goto done; >+ memcpy(&gw, (char *)data + off, sizeof (gw)); >+ >+ rv = umb_add_inet_config(sc, addr, ipv4elem.prefixlen, gw); >+ if (rv == 0) >+ state = UMB_S_UP; >+ } >+ >+ memset(sc->sc_info.ipv4dns, 0, sizeof (sc->sc_info.ipv4dns)); >+ if (avail_v4 & MBIM_IPCONF_HAS_DNSINFO) { >+ n = le32toh(ic->ipv4_ndnssrv); >+ off = le32toh(ic->ipv4_dnssrvoffs); >+ i = 0; >+ while (n-- > 0) { >+ if (off + sizeof (addr) > len) >+ break; >+ memcpy(&addr, (char *)data + off, sizeof(addr)); >+ if (i < UMB_MAX_DNSSRV) >+ sc->sc_info.ipv4dns[i++] = addr; >+ off += sizeof(addr); >+ } >+ } >+ >+ if ((avail_v4 & MBIM_IPCONF_HAS_MTUINFO)) { >+ val = le32toh(ic->ipv4_mtu); >+ if (ifp->if_mtu != val && val <= sc->sc_maxpktlen) { >+ ifp->if_mtu = val; >+ if (ifp->if_mtu > val) >+ ifp->if_mtu = val; >+ if (ifp->if_flags & IFF_DEBUG) >+ log(LOG_INFO, "%s: MTU %d\n", DEVNAM(sc), val); >+ } >+ } >+ >+ avail_v4 = le32toh(ic->ipv6_available); >+ if ((ifp->if_flags & IFF_DEBUG) && avail_v4 & MBIM_IPCONF_HAS_ADDRINFO) { >+ /* XXX FIXME: IPv6 configuration missing */ >+ log(LOG_INFO, "%s: ignoring IPv6 configuration\n", DEVNAM(sc)); >+ } >+ if (state != -1) >+ umb_newstate(sc, state, 0); >+ >+tryv6: >+done: >+ return 1; >+} >+ >+static void >+umb_rx(struct umb_softc *sc) >+{ >+ mtx_assert(&sc->sc_mutex, MA_OWNED); >+ >+ usbd_transfer_start(sc->sc_xfer[UMB_BULK_RX]); >+} >+ >+static void >+umb_rxeof(struct usb_xfer *xfer, usb_error_t status) >+{ >+ struct umb_softc *sc = usbd_xfer_softc(xfer); >+ struct ifnet *ifp = GET_IFP(sc); >+ int actlen; >+ int aframes; >+ int i; >+ >+ DPRINTF("%s(%u): state=%u\n", __func__, status, USB_GET_STATE(xfer)); >+ >+ mtx_assert(&sc->sc_mutex, MA_OWNED); >+ >+ usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL); >+ >+ switch (USB_GET_STATE(xfer)) { >+ case USB_ST_TRANSFERRED: >+ DPRINTF("received %u bytes in %u frames\n", actlen, aframes); >+ >+ if (actlen == 0) { >+ if (sc->sc_rx_nerr >= 4) >+ /* throttle transfers */ >+ usbd_xfer_set_interval(xfer, 500); >+ else >+ sc->sc_rx_nerr++; >+ } >+ else { >+ /* disable throttling */ >+ usbd_xfer_set_interval(xfer, 0); >+ sc->sc_rx_nerr = 0; >+ } >+ >+ for(i = 0; i < aframes; i++) { >+ umb_decap(sc, xfer, i); >+ } >+ >+ /* fall through */ >+ case USB_ST_SETUP: >+ usbd_xfer_set_frame_data(xfer, 0, sc->sc_rx_buf, >+ sc->sc_rx_bufsz); >+ usbd_xfer_set_frames(xfer, 1); >+ usbd_transfer_submit(xfer); >+ >+ umb_rxflush(sc); >+ break; >+ default: >+ DPRINTF("rx error: %s\n", usbd_errstr(status)); >+ >+ /* disable throttling */ >+ usbd_xfer_set_interval(xfer, 0); >+ >+ if (status != USB_ERR_CANCELLED) { >+ /* try to clear stall first */ >+ usbd_xfer_set_stall(xfer); >+ usbd_xfer_set_frames(xfer, 0); >+ usbd_transfer_submit(xfer); >+ if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); >+ } >+ else if (++sc->sc_rx_nerr > 100) { >+ log(LOG_ERR, "%s: too many rx errors, disabling\n", >+ DEVNAM(sc)); >+ umb_deactivate(sc->sc_dev); >+ } >+ break; >+ } >+} >+ >+static void >+umb_rxflush(struct umb_softc *sc) >+{ >+ struct ifnet *ifp = GET_IFP(sc); >+ struct mbuf *m; >+ >+ mtx_assert(&sc->sc_mutex, MA_OWNED); >+ >+ for (;;) { >+ _IF_DEQUEUE(&sc->sc_rx_queue, m); >+ if (m == NULL) >+ break; >+ >+ /* >+ * The USB xfer has been resubmitted so its safe to unlock now. >+ */ >+ mtx_unlock(&sc->sc_mutex); >+ CURVNET_SET_QUIET(vnet0); >+ if (ifp->if_drv_flags & IFF_DRV_RUNNING) >+ ifp->if_input(ifp, m); >+ else >+ m_freem(m); >+ CURVNET_RESTORE(); >+ mtx_lock(&sc->sc_mutex); >+ } >+} >+ >+static int >+umb_encap(struct umb_softc *sc, struct mbuf *m, struct usb_xfer *xfer) >+{ >+ struct ncm_header16 *hdr; >+ struct ncm_pointer16 *ptr; >+ int len; >+ >+ KASSERT(sc->sc_tx_m == NULL, >+ ("Assertion failed in umb_encap()")); >+ >+ /* All size constraints have been validated by the caller! */ >+ hdr = (struct ncm_header16 *)sc->sc_tx_buf; >+ ptr = (struct ncm_pointer16 *)(hdr + 1); >+ >+ USETDW(hdr->dwSignature, NCM_HDR16_SIG); >+ USETW(hdr->wHeaderLength, sizeof (*hdr)); >+ USETW(hdr->wSequence, sc->sc_tx_seq); >+ sc->sc_tx_seq++; >+ USETW(hdr->wNdpIndex, sizeof (*hdr)); >+ >+ len = m->m_pkthdr.len; >+ USETDW(ptr->dwSignature, MBIM_NCM_NTH16_SIG(umb_session_id)); >+ USETW(ptr->wLength, sizeof (*ptr)); >+ USETW(ptr->wNextNdpIndex, 0); >+ USETW(ptr->dgram[0].wDatagramIndex, MBIM_HDR16_LEN); >+ USETW(ptr->dgram[0].wDatagramLen, len); >+ USETW(ptr->dgram[1].wDatagramIndex, 0); >+ USETW(ptr->dgram[1].wDatagramLen, 0); >+ >+ KASSERT(len + MBIM_HDR16_LEN <= sc->sc_tx_bufsz, >+ ("Assertion failed in umb_encap()")); >+ m_copydata(m, 0, len, (char *)(ptr + 1)); >+ sc->sc_tx_m = m; >+ len += MBIM_HDR16_LEN; >+ USETW(hdr->wBlockLength, len); >+ >+ usbd_xfer_set_frame_data(xfer, 0, sc->sc_tx_buf, len); >+ usbd_xfer_set_interval(xfer, 0); >+ usbd_xfer_set_frames(xfer, 1); >+ >+ DPRINTFN(3, "%s: encap %d bytes\n", DEVNAM(sc), len); >+ DDUMPN(5, sc->sc_tx_buf, len); >+ return 0; >+} >+ >+static void >+umb_txeof(struct usb_xfer *xfer, usb_error_t status) >+{ >+ struct umb_softc *sc = usbd_xfer_softc(xfer); >+ struct ifnet *ifp = GET_IFP(sc); >+ struct mbuf *m; >+ >+ DPRINTF("%s(%u) state=%u\n", __func__, status, USB_GET_STATE(xfer)); >+ >+ mtx_assert(&sc->sc_mutex, MA_OWNED); >+ >+ switch (USB_GET_STATE(xfer)) { >+ case USB_ST_TRANSFERRED: >+ ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; >+ if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); >+ >+ umb_txflush(sc); >+ >+ /* fall through */ >+ case USB_ST_SETUP: >+tr_setup: >+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) >+ break; >+ >+ IFQ_DRV_DEQUEUE(&ifp->if_snd, m); >+ if (m == NULL) >+ break; >+ >+ if (umb_encap(sc, m, xfer)) { >+ if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); >+ umb_txflush(sc); >+ break; >+ } >+ >+ BPF_MTAP(ifp, m); >+ >+ ifp->if_drv_flags |= IFF_DRV_OACTIVE; >+ usbd_transfer_submit(xfer); >+ >+ break; >+ >+ default: >+ umb_txflush(sc); >+ >+ /* count output errors */ >+ if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); >+ DPRINTF("tx error: %s\n", >+ usbd_errstr(status)); >+ >+ if (status != USB_ERR_CANCELLED) { >+ /* try to clear stall first */ >+ usbd_xfer_set_stall(xfer); >+ goto tr_setup; >+ } >+ break; >+ } >+} >+ >+static void >+umb_txflush(struct umb_softc *sc) >+{ >+ mtx_assert(&sc->sc_mutex, MA_OWNED); >+ >+ if (sc->sc_tx_m != NULL) { >+ m_freem(sc->sc_tx_m); >+ sc->sc_tx_m = NULL; >+ } >+} >+ >+static void >+umb_decap(struct umb_softc *sc, struct usb_xfer *xfer, int frame) >+{ >+ struct ifnet *ifp = GET_IFP(sc); >+ char *buf; >+ int len; >+ char *dp; >+ struct ncm_header16 *hdr16; >+ struct ncm_header32 *hdr32; >+ struct ncm_pointer16 *ptr16; >+ struct ncm_pointer16_dgram *dgram16; >+ struct ncm_pointer32_dgram *dgram32; >+ uint32_t hsig, psig; >+ int hlen, blen; >+ int ptrlen, ptroff, dgentryoff; >+ uint32_t doff, dlen; >+ struct mbuf *m; >+ >+ usbd_xfer_frame_data(xfer, frame, (void **)&buf, &len); >+ DPRINTFN(4, "recv %d bytes\n", len); >+ DDUMPN(5, buf, len); >+ if (len < sizeof (*hdr16)) >+ goto toosmall; >+ >+ hdr16 = (struct ncm_header16 *)buf; >+ hsig = UGETDW(hdr16->dwSignature); >+ hlen = UGETW(hdr16->wHeaderLength); >+ if (len < hlen) >+ goto toosmall; >+ if (len > sc->sc_rx_bufsz) { >+ DPRINTF("packet too large (%d)\n", len); >+ goto fail; >+ } >+ switch (hsig) { >+ case NCM_HDR16_SIG: >+ blen = UGETW(hdr16->wBlockLength); >+ ptroff = UGETW(hdr16->wNdpIndex); >+ if (hlen != sizeof (*hdr16)) { >+ DPRINTF("%s: bad header len %d for NTH16 (exp %zu)\n", >+ DEVNAM(sc), hlen, sizeof (*hdr16)); >+ goto fail; >+ } >+ break; >+ case NCM_HDR32_SIG: >+ hdr32 = (struct ncm_header32 *)hdr16; >+ blen = UGETDW(hdr32->dwBlockLength); >+ ptroff = UGETDW(hdr32->dwNdpIndex); >+ if (hlen != sizeof (*hdr32)) { >+ DPRINTF("%s: bad header len %d for NTH32 (exp %zu)\n", >+ DEVNAM(sc), hlen, sizeof (*hdr32)); >+ goto fail; >+ } >+ break; >+ default: >+ DPRINTF("%s: unsupported NCM header signature (0x%08x)\n", >+ DEVNAM(sc), hsig); >+ goto fail; >+ } >+ if (len < blen) { >+ DPRINTF("%s: bad NTB len (%d) for %d bytes of data\n", >+ DEVNAM(sc), blen, len); >+ goto fail; >+ } >+ >+ ptr16 = (struct ncm_pointer16 *)(buf + ptroff); >+ psig = UGETDW(ptr16->dwSignature); >+ ptrlen = UGETW(ptr16->wLength); >+ if (len < ptrlen + ptroff) >+ goto toosmall; >+ if (!MBIM_NCM_NTH16_ISISG(psig) && !MBIM_NCM_NTH32_ISISG(psig)) { >+ DPRINTF("%s: unsupported NCM pointer signature (0x%08x)\n", >+ DEVNAM(sc), psig); >+ goto fail; >+ } >+ >+ switch (hsig) { >+ case NCM_HDR16_SIG: >+ dgentryoff = offsetof(struct ncm_pointer16, dgram); >+ break; >+ case NCM_HDR32_SIG: >+ dgentryoff = offsetof(struct ncm_pointer32, dgram); >+ break; >+ default: >+ goto fail; >+ } >+ >+ while (dgentryoff < ptrlen) { >+ switch (hsig) { >+ case NCM_HDR16_SIG: >+ if (ptroff + dgentryoff < sizeof (*dgram16)) >+ goto done; >+ dgram16 = (struct ncm_pointer16_dgram *) >+ (buf + ptroff + dgentryoff); >+ dgentryoff += sizeof (*dgram16); >+ dlen = UGETW(dgram16->wDatagramLen); >+ doff = UGETW(dgram16->wDatagramIndex); >+ break; >+ case NCM_HDR32_SIG: >+ if (ptroff + dgentryoff < sizeof (*dgram32)) >+ goto done; >+ dgram32 = (struct ncm_pointer32_dgram *) >+ (buf + ptroff + dgentryoff); >+ dgentryoff += sizeof (*dgram32); >+ dlen = UGETDW(dgram32->dwDatagramLen); >+ doff = UGETDW(dgram32->dwDatagramIndex); >+ break; >+ default: >+ if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); >+ goto done; >+ } >+ >+ /* Terminating zero entry */ >+ if (dlen == 0 || doff == 0) >+ break; >+ if (len < dlen + doff) { >+ /* Skip giant datagram but continue processing */ >+ DPRINTF("%s: datagram too large (%d @ off %d)\n", >+ DEVNAM(sc), dlen, doff); >+ continue; >+ } >+ >+ dp = buf + doff; >+ DPRINTFN(3, "%s: decap %d bytes\n", DEVNAM(sc), dlen); >+ m = m_devget(dp, dlen, 0, ifp, NULL); >+ if (m == NULL) { >+ if_inc_counter(ifp, IFCOUNTER_IQDROPS, 1); >+ continue; >+ } >+ >+ /* enqueue for later when the lock can be released */ >+ _IF_ENQUEUE(&sc->sc_rx_queue, m); >+ >+ if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1); >+ >+ } >+done: >+ sc->sc_rx_nerr = 0; >+ return; >+toosmall: >+ DPRINTF("%s: packet too small (%d)\n", DEVNAM(sc), len); >+fail: >+ if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); >+} >+ >+static usb_error_t >+umb_send_encap_command(struct umb_softc *sc, void *data, int len) >+{ >+ usb_device_request_t req; >+ >+ if (len > sc->sc_ctrl_len) >+ return USB_ERR_INVAL; >+ >+ /* XXX FIXME: if (total len > sc->sc_ctrl_len) => must fragment */ >+ req.bmRequestType = UT_WRITE_CLASS_INTERFACE; >+ req.bRequest = UCDC_SEND_ENCAPSULATED_COMMAND; >+ USETW(req.wValue, 0); >+ USETW(req.wIndex, sc->sc_ctrl_ifaceno); >+ USETW(req.wLength, len); >+ mtx_unlock(&sc->sc_mutex); >+ DELAY(umb_delay); >+ mtx_lock(&sc->sc_mutex); >+ return usbd_do_request_flags(sc->sc_udev, &sc->sc_mutex, &req, data, 0, >+ NULL, umb_xfer_tout); >+} >+ >+static int >+umb_get_encap_response(struct umb_softc *sc, void *buf, int *len) >+{ >+ usb_device_request_t req; >+ usb_error_t err; >+ uint16_t l = *len; >+ >+ req.bmRequestType = UT_READ_CLASS_INTERFACE; >+ req.bRequest = UCDC_GET_ENCAPSULATED_RESPONSE; >+ USETW(req.wValue, 0); >+ USETW(req.wIndex, sc->sc_ctrl_ifaceno); >+ USETW(req.wLength, l); >+ /* XXX FIXME: re-assemble fragments */ >+ >+ mtx_unlock(&sc->sc_mutex); >+ DELAY(umb_delay); >+ mtx_lock(&sc->sc_mutex); >+ err = usbd_do_request_flags(sc->sc_udev, &sc->sc_mutex, &req, buf, >+ USB_SHORT_XFER_OK, &l, umb_xfer_tout); >+ if (err == USB_ERR_NORMAL_COMPLETION) { >+ *len = l; >+ return 1; >+ } >+ DPRINTF("ctrl recv: %s\n", usbd_errstr(err)); >+ return 0; >+} >+ >+static void >+umb_ctrl_msg(struct umb_softc *sc, uint32_t req, void *data, int len) >+{ >+ struct ifnet *ifp = GET_IFP(sc); >+ uint32_t tid; >+ struct mbim_msghdr *hdr = data; >+ usb_error_t err; >+ >+ if (sc->sc_dying) >+ return; >+ if (len < sizeof (*hdr)) >+ return; >+ tid = ++sc->sc_tid; >+ >+ hdr->type = htole32(req); >+ hdr->len = htole32(len); >+ hdr->tid = htole32(tid); >+ >+#ifdef UMB_DEBUG >+ if (umb_debug) { >+ const char *op, *str; >+ if (req == MBIM_COMMAND_MSG) { >+ struct mbim_h2f_cmd *c = data; >+ if (le32toh(c->op) == MBIM_CMDOP_SET) >+ op = "set"; >+ else >+ op = "qry"; >+ str = umb_cid2str(le32toh(c->cid)); >+ } else { >+ op = "snd"; >+ str = umb_request2str(req); >+ } >+ DPRINTF("-> %s %s (tid %u)\n", op, str, tid); >+ } >+#endif >+ err = umb_send_encap_command(sc, data, len); >+ if (err != USB_ERR_NORMAL_COMPLETION) { >+ if (ifp->if_flags & IFF_DEBUG) >+ log(LOG_ERR, "%s: send %s msg (tid %u) failed: %s\n", >+ DEVNAM(sc), umb_request2str(req), tid, >+ usbd_errstr(err)); >+ >+ /* will affect other transactions, too */ >+ usbd_transfer_stop(sc->sc_xfer[UMB_INTR_RX]); >+ } else { >+ DPRINTFN(2, "sent %s (tid %u)\n", >+ umb_request2str(req), tid); >+ DDUMPN(3, data, len); >+ } >+ return; >+} >+ >+static void >+umb_open(struct umb_softc *sc) >+{ >+ struct mbim_h2f_openmsg msg; >+ >+ memset(&msg, 0, sizeof (msg)); >+ msg.maxlen = htole32(sc->sc_ctrl_len); >+ umb_ctrl_msg(sc, MBIM_OPEN_MSG, &msg, sizeof (msg)); >+ return; >+} >+ >+static void >+umb_close(struct umb_softc *sc) >+{ >+ struct mbim_h2f_closemsg msg; >+ >+ memset(&msg, 0, sizeof (msg)); >+ umb_ctrl_msg(sc, MBIM_CLOSE_MSG, &msg, sizeof (msg)); >+} >+ >+static int >+umb_setpin(struct umb_softc *sc, int op, int is_puk, void *pin, int pinlen, >+ void *newpin, int newpinlen) >+{ >+ struct mbim_cid_pin cp; >+ int off; >+ >+ if (pinlen == 0) >+ return 0; >+ if (pinlen < 0 || pinlen > MBIM_PIN_MAXLEN || >+ newpinlen < 0 || newpinlen > MBIM_PIN_MAXLEN || >+ op < 0 || op > MBIM_PIN_OP_CHANGE || >+ (is_puk && op != MBIM_PIN_OP_ENTER)) >+ return EINVAL; >+ >+ memset(&cp, 0, sizeof (cp)); >+ cp.type = htole32(is_puk ? MBIM_PIN_TYPE_PUK1 : MBIM_PIN_TYPE_PIN1); >+ >+ off = offsetof(struct mbim_cid_pin, data); >+ if (!umb_addstr(&cp, sizeof (cp), &off, pin, pinlen, >+ &cp.pin_offs, &cp.pin_size)) >+ return EINVAL; >+ >+ cp.op = htole32(op); >+ if (newpinlen) { >+ if (!umb_addstr(&cp, sizeof (cp), &off, newpin, newpinlen, >+ &cp.newpin_offs, &cp.newpin_size)) >+ return EINVAL; >+ } else { >+ if ((op == MBIM_PIN_OP_CHANGE) || is_puk) >+ return EINVAL; >+ if (!umb_addstr(&cp, sizeof (cp), &off, NULL, 0, >+ &cp.newpin_offs, &cp.newpin_size)) >+ return EINVAL; >+ } >+ mtx_lock(&sc->sc_mutex); >+ umb_cmd(sc, MBIM_CID_PIN, MBIM_CMDOP_SET, &cp, off); >+ mtx_unlock(&sc->sc_mutex); >+ return 0; >+} >+ >+static void >+umb_setdataclass(struct umb_softc *sc) >+{ >+ struct mbim_cid_registration_state rs; >+ uint32_t classes; >+ >+ if (sc->sc_info.supportedclasses == MBIM_DATACLASS_NONE) >+ return; >+ >+ memset(&rs, 0, sizeof (rs)); >+ rs.regaction = htole32(MBIM_REGACTION_AUTOMATIC); >+ classes = sc->sc_info.supportedclasses; >+ if (sc->sc_info.preferredclasses != MBIM_DATACLASS_NONE) >+ classes &= sc->sc_info.preferredclasses; >+ rs.data_class = htole32(classes); >+ mtx_lock(&sc->sc_mutex); >+ umb_cmd(sc, MBIM_CID_REGISTER_STATE, MBIM_CMDOP_SET, &rs, sizeof (rs)); >+ mtx_unlock(&sc->sc_mutex); >+} >+ >+static void >+umb_radio(struct umb_softc *sc, int on) >+{ >+ struct mbim_cid_radio_state s; >+ >+ DPRINTF("set radio %s\n", on ? "on" : "off"); >+ memset(&s, 0, sizeof (s)); >+ s.state = htole32(on ? MBIM_RADIO_STATE_ON : MBIM_RADIO_STATE_OFF); >+ umb_cmd(sc, MBIM_CID_RADIO_STATE, MBIM_CMDOP_SET, &s, sizeof (s)); >+} >+ >+static void >+umb_allocate_cid(struct umb_softc *sc) >+{ >+ umb_cmd1(sc, MBIM_CID_DEVICE_CAPS, MBIM_CMDOP_SET, >+ umb_qmi_alloc_cid, sizeof (umb_qmi_alloc_cid), umb_uuid_qmi_mbim); >+} >+ >+static void >+umb_send_fcc_auth(struct umb_softc *sc) >+{ >+ uint8_t fccauth[sizeof (umb_qmi_fcc_auth)]; >+ >+ if (sc->sc_cid == -1) { >+ DPRINTF("missing CID, cannot send FCC auth\n"); >+ umb_allocate_cid(sc); >+ return; >+ } >+ memcpy(fccauth, umb_qmi_fcc_auth, sizeof (fccauth)); >+ fccauth[UMB_QMI_CID_OFFS] = sc->sc_cid; >+ umb_cmd1(sc, MBIM_CID_DEVICE_CAPS, MBIM_CMDOP_SET, >+ fccauth, sizeof (fccauth), umb_uuid_qmi_mbim); >+} >+ >+static void >+umb_packet_service(struct umb_softc *sc, int attach) >+{ >+ struct mbim_cid_packet_service s; >+ >+ DPRINTF("%s packet service\n", >+ attach ? "attach" : "detach"); >+ memset(&s, 0, sizeof (s)); >+ s.action = htole32(attach ? >+ MBIM_PKTSERVICE_ACTION_ATTACH : MBIM_PKTSERVICE_ACTION_DETACH); >+ umb_cmd(sc, MBIM_CID_PACKET_SERVICE, MBIM_CMDOP_SET, &s, sizeof (s)); >+} >+ >+static void >+umb_connect(struct umb_softc *sc) >+{ >+ struct ifnet *ifp = GET_IFP(sc); >+ >+ if (sc->sc_info.regstate == MBIM_REGSTATE_ROAMING && !sc->sc_roaming) { >+ log(LOG_INFO, "%s: connection disabled in roaming network\n", >+ DEVNAM(sc)); >+ return; >+ } >+ if (ifp->if_flags & IFF_DEBUG) >+ log(LOG_DEBUG, "%s: connecting ...\n", DEVNAM(sc)); >+ umb_send_connect(sc, MBIM_CONNECT_ACTIVATE); >+} >+ >+static void >+umb_disconnect(struct umb_softc *sc) >+{ >+ struct ifnet *ifp = GET_IFP(sc); >+ >+ if (ifp->if_flags & IFF_DEBUG) >+ log(LOG_DEBUG, "%s: disconnecting ...\n", DEVNAM(sc)); >+ umb_send_connect(sc, MBIM_CONNECT_DEACTIVATE); >+} >+ >+static void >+umb_send_connect(struct umb_softc *sc, int command) >+{ >+ struct mbim_cid_connect *c; >+ int off; >+ >+ /* Too large for the stack */ >+ mtx_unlock(&sc->sc_mutex); >+ c = malloc(sizeof (*c), M_MBIM_CID_CONNECT, M_WAITOK | M_ZERO); >+ mtx_lock(&sc->sc_mutex); >+ c->sessionid = htole32(umb_session_id); >+ c->command = htole32(command); >+ off = offsetof(struct mbim_cid_connect, data); >+ if (!umb_addstr(c, sizeof (*c), &off, sc->sc_info.apn, >+ sc->sc_info.apnlen, &c->access_offs, &c->access_size)) >+ goto done; >+ if (!umb_addstr(c, sizeof (*c), &off, sc->sc_info.username, >+ sc->sc_info.usernamelen, &c->user_offs, &c->user_size)) >+ goto done; >+ if (!umb_addstr(c, sizeof (*c), &off, sc->sc_info.password, >+ sc->sc_info.passwordlen, &c->passwd_offs, &c->passwd_size)) >+ goto done; >+ c->authprot = htole32(MBIM_AUTHPROT_NONE); >+ c->compression = htole32(MBIM_COMPRESSION_NONE); >+ c->iptype = htole32(MBIM_CONTEXT_IPTYPE_IPV4); >+ memcpy(c->context, umb_uuid_context_internet, sizeof (c->context)); >+ umb_cmd(sc, MBIM_CID_CONNECT, MBIM_CMDOP_SET, c, off); >+done: >+ free(c, M_MBIM_CID_CONNECT); >+ return; >+} >+ >+static void >+umb_qry_ipconfig(struct umb_softc *sc) >+{ >+ struct mbim_cid_ip_configuration_info ipc; >+ >+ memset(&ipc, 0, sizeof (ipc)); >+ ipc.sessionid = htole32(umb_session_id); >+ umb_cmd(sc, MBIM_CID_IP_CONFIGURATION, MBIM_CMDOP_QRY, >+ &ipc, sizeof (ipc)); >+} >+ >+static void >+umb_cmd(struct umb_softc *sc, int cid, int op, const void *data, int len) >+{ >+ umb_cmd1(sc, cid, op, data, len, umb_uuid_basic_connect); >+} >+ >+static void >+umb_cmd1(struct umb_softc *sc, int cid, int op, const void *data, int len, >+ uint8_t *uuid) >+{ >+ struct mbim_h2f_cmd *cmd; >+ int totlen; >+ >+ /* XXX FIXME support sending fragments */ >+ if (sizeof (*cmd) + len > sc->sc_ctrl_len) { >+ DPRINTF("set %s msg too long: cannot send\n", >+ umb_cid2str(cid)); >+ return; >+ } >+ cmd = sc->sc_ctrl_msg; >+ memset(cmd, 0, sizeof (*cmd)); >+ cmd->frag.nfrag = htole32(1); >+ memcpy(cmd->devid, uuid, sizeof (cmd->devid)); >+ cmd->cid = htole32(cid); >+ cmd->op = htole32(op); >+ cmd->infolen = htole32(len); >+ totlen = sizeof (*cmd); >+ if (len > 0) { >+ memcpy(cmd + 1, data, len); >+ totlen += len; >+ } >+ umb_ctrl_msg(sc, MBIM_COMMAND_MSG, cmd, totlen); >+} >+ >+static void >+umb_command_done(struct umb_softc *sc, void *data, int len) >+{ >+ struct mbim_f2h_cmddone *cmd = data; >+ struct ifnet *ifp = GET_IFP(sc); >+ uint32_t status; >+ uint32_t cid; >+ uint32_t infolen; >+ int qmimsg = 0; >+ >+ if (len < sizeof (*cmd)) { >+ DPRINTF("discard short %s message\n", >+ umb_request2str(le32toh(cmd->hdr.type))); >+ return; >+ } >+ cid = le32toh(cmd->cid); >+ if (memcmp(cmd->devid, umb_uuid_basic_connect, sizeof (cmd->devid))) { >+ if (memcmp(cmd->devid, umb_uuid_qmi_mbim, >+ sizeof (cmd->devid))) { >+ DPRINTF("discard %s message for other UUID '%s'\n", >+ umb_request2str(le32toh(cmd->hdr.type)), >+ umb_uuid2str(cmd->devid)); >+ return; >+ } else >+ qmimsg = 1; >+ } >+ >+ status = le32toh(cmd->status); >+ switch (status) { >+ case MBIM_STATUS_SUCCESS: >+ break; >+ case MBIM_STATUS_NOT_INITIALIZED: >+ if (ifp->if_flags & IFF_DEBUG) >+ log(LOG_ERR, "%s: SIM not initialized (PIN missing)\n", >+ DEVNAM(sc)); >+ return; >+ case MBIM_STATUS_PIN_REQUIRED: >+ sc->sc_info.pin_state = UMB_PIN_REQUIRED; >+ /*FALLTHROUGH*/ >+ default: >+ if (ifp->if_flags & IFF_DEBUG) >+ log(LOG_ERR, "%s: set/qry %s failed: %s\n", DEVNAM(sc), >+ umb_cid2str(cid), umb_status2str(status)); >+ return; >+ } >+ >+ infolen = le32toh(cmd->infolen); >+ if (len < sizeof (*cmd) + infolen) { >+ DPRINTF("discard truncated %s message (want %d, got %d)\n", >+ umb_cid2str(cid), >+ (int)sizeof (*cmd) + infolen, len); >+ return; >+ } >+ if (qmimsg) { >+ if (sc->sc_flags & UMBFLG_FCC_AUTH_REQUIRED) >+ umb_decode_qmi(sc, cmd->info, infolen); >+ } else { >+ DPRINTFN(2, "set/qry %s done\n", >+ umb_cid2str(cid)); >+ umb_decode_cid(sc, cid, cmd->info, infolen); >+ } >+} >+ >+static void >+umb_decode_cid(struct umb_softc *sc, uint32_t cid, void *data, int len) >+{ >+ int ok = 1; >+ >+ switch (cid) { >+ case MBIM_CID_DEVICE_CAPS: >+ ok = umb_decode_devices_caps(sc, data, len); >+ break; >+ case MBIM_CID_SUBSCRIBER_READY_STATUS: >+ ok = umb_decode_subscriber_status(sc, data, len); >+ break; >+ case MBIM_CID_RADIO_STATE: >+ ok = umb_decode_radio_state(sc, data, len); >+ break; >+ case MBIM_CID_PIN: >+ ok = umb_decode_pin(sc, data, len); >+ break; >+ case MBIM_CID_REGISTER_STATE: >+ ok = umb_decode_register_state(sc, data, len); >+ break; >+ case MBIM_CID_PACKET_SERVICE: >+ ok = umb_decode_packet_service(sc, data, len); >+ break; >+ case MBIM_CID_SIGNAL_STATE: >+ ok = umb_decode_signal_state(sc, data, len); >+ break; >+ case MBIM_CID_CONNECT: >+ ok = umb_decode_connect_info(sc, data, len); >+ break; >+ case MBIM_CID_IP_CONFIGURATION: >+ ok = umb_decode_ip_configuration(sc, data, len); >+ break; >+ default: >+ /* >+ * Note: the above list is incomplete and only contains >+ * mandatory CIDs from the BASIC_CONNECT set. >+ * So alternate values are not unusual. >+ */ >+ DPRINTFN(4, "ignore %s\n", umb_cid2str(cid)); >+ break; >+ } >+ if (!ok) >+ DPRINTF("discard %s with bad info length %d\n", >+ umb_cid2str(cid), len); >+ return; >+} >+ >+static void >+umb_decode_qmi(struct umb_softc *sc, uint8_t *data, int len) >+{ >+ uint8_t srv; >+ uint16_t msg, tlvlen; >+ uint32_t val; >+ >+#define UMB_QMI_QMUXLEN 6 >+ if (len < UMB_QMI_QMUXLEN) >+ goto tooshort; >+ >+ srv = data[4]; >+ data += UMB_QMI_QMUXLEN; >+ len -= UMB_QMI_QMUXLEN; >+ >+#define UMB_GET16(p) ((uint16_t)*p | (uint16_t)*(p + 1) << 8) >+#define UMB_GET32(p) ((uint32_t)*p | (uint32_t)*(p + 1) << 8 | \ >+ (uint32_t)*(p + 2) << 16 |(uint32_t)*(p + 3) << 24) >+ switch (srv) { >+ case 0: /* ctl */ >+#define UMB_QMI_CTLLEN 6 >+ if (len < UMB_QMI_CTLLEN) >+ goto tooshort; >+ msg = UMB_GET16(&data[2]); >+ tlvlen = UMB_GET16(&data[4]); >+ data += UMB_QMI_CTLLEN; >+ len -= UMB_QMI_CTLLEN; >+ break; >+ case 2: /* dms */ >+#define UMB_QMI_DMSLEN 7 >+ if (len < UMB_QMI_DMSLEN) >+ goto tooshort; >+ msg = UMB_GET16(&data[3]); >+ tlvlen = UMB_GET16(&data[5]); >+ data += UMB_QMI_DMSLEN; >+ len -= UMB_QMI_DMSLEN; >+ break; >+ default: >+ DPRINTF("discard QMI message for unknown service type %d\n", >+ srv); >+ return; >+ } >+ >+ if (len < tlvlen) >+ goto tooshort; >+ >+#define UMB_QMI_TLVLEN 3 >+ while (len > 0) { >+ if (len < UMB_QMI_TLVLEN) >+ goto tooshort; >+ tlvlen = UMB_GET16(&data[1]); >+ if (len < UMB_QMI_TLVLEN + tlvlen) >+ goto tooshort; >+ switch (data[0]) { >+ case 1: /* allocation info */ >+ if (msg == 0x0022) { /* Allocate CID */ >+ if (tlvlen != 2 || data[3] != 2) /* dms */ >+ break; >+ sc->sc_cid = data[4]; >+ DPRINTF("QMI CID %d allocated\n", >+ sc->sc_cid); >+ umb_newstate(sc, UMB_S_CID, UMB_NS_DONT_DROP); >+ } >+ break; >+ case 2: /* response */ >+ if (tlvlen != sizeof (val)) >+ break; >+ val = UMB_GET32(&data[3]); >+ switch (msg) { >+ case 0x0022: /* Allocate CID */ >+ if (val != 0) { >+ log(LOG_ERR, "%s: allocation of QMI CID" >+ " failed, error 0x%x\n", DEVNAM(sc), >+ val); >+ /* XXX how to proceed? */ >+ return; >+ } >+ break; >+ case 0x555f: /* Send FCC Authentication */ >+ if (val == 0) >+ DPRINTF("%s: send FCC " >+ "Authentication succeeded\n", >+ DEVNAM(sc)); >+ else if (val == 0x001a0001) >+ DPRINTF("%s: FCC Authentication " >+ "not required\n", DEVNAM(sc)); >+ else >+ log(LOG_INFO, "%s: send FCC " >+ "Authentication failed, " >+ "error 0x%x\n", DEVNAM(sc), val); >+ >+ /* FCC Auth is needed only once after power-on*/ >+ sc->sc_flags &= ~UMBFLG_FCC_AUTH_REQUIRED; >+ >+ /* Try to proceed anyway */ >+ DPRINTF("init: turning radio on ...\n"); >+ umb_radio(sc, 1); >+ break; >+ default: >+ break; >+ } >+ break; >+ default: >+ break; >+ } >+ data += UMB_QMI_TLVLEN + tlvlen; >+ len -= UMB_QMI_TLVLEN + tlvlen; >+ } >+ return; >+ >+tooshort: >+ DPRINTF("discard short QMI message\n"); >+ return; >+} >+ >+static void >+umb_intr(struct usb_xfer *xfer, usb_error_t status) >+{ >+ struct umb_softc *sc = usbd_xfer_softc(xfer); >+ struct usb_cdc_notification notification; >+ struct usb_page_cache *pc; >+ struct ifnet *ifp = GET_IFP(sc); >+ int total_len; >+ >+ mtx_assert(&sc->sc_mutex, MA_OWNED); >+ >+ /* FIXME use actlen or total_len? */ >+ usbd_xfer_status(xfer, &total_len, NULL, NULL, NULL); >+ >+ switch (USB_GET_STATE(xfer)) { >+ case USB_ST_TRANSFERRED: >+ DPRINTF("Received %d bytes\n", total_len); >+ >+ if (total_len < UCDC_NOTIFICATION_LENGTH) { >+ DPRINTF("short notification (%d<%d)\n", >+ total_len, UCDC_NOTIFICATION_LENGTH); >+ return; >+ } >+ >+ pc = usbd_xfer_get_frame(xfer, 0); >+ usbd_copy_out(pc, 0, ¬ification, sizeof (notification)); >+ >+ if (notification.bmRequestType != UCDC_NOTIFICATION) { >+ DPRINTF("unexpected notification (type=0x%02x)\n", >+ notification.bmRequestType); >+ return; >+ } >+ >+ switch (notification.bNotification) { >+ case UCDC_N_NETWORK_CONNECTION: >+ if (ifp->if_flags & IFF_DEBUG) >+ log(LOG_DEBUG, "%s: network %sconnected\n", >+ DEVNAM(sc), >+ UGETW(notification.wValue) >+ ? "" : "dis"); >+ break; >+ case UCDC_N_RESPONSE_AVAILABLE: >+ DPRINTFN(2, "umb_intr: response available\n"); >+ ++sc->sc_nresp; >+ umb_add_task(sc, umb_get_response_task, >+ &sc->sc_proc_get_response_task[0].hdr, >+ &sc->sc_proc_get_response_task[1].hdr, >+ 0); >+ break; >+ case UCDC_N_CONNECTION_SPEED_CHANGE: >+ DPRINTFN(2, "umb_intr: connection speed changed\n"); >+ break; >+ default: >+ DPRINTF("unexpected notification (0x%02x)\n", >+ notification.bNotification); >+ break; >+ } >+ /* fallthrough */ >+ case USB_ST_SETUP: >+tr_setup: >+ usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); >+ usbd_transfer_submit(xfer); >+ break; >+ default: >+ if (status != USB_ERR_CANCELLED) { >+ /* start clear stall */ >+ usbd_xfer_set_stall(xfer); >+ goto tr_setup; >+ } >+ break; >+ } >+} >+ >+/* >+ * Diagnostic routines >+ */ >+static char * >+umb_ntop(struct sockaddr *sa) >+{ >+#define NUMBUFS 4 >+ static char astr[NUMBUFS][INET_ADDRSTRLEN]; >+ static unsigned nbuf = 0; >+ char *s; >+ >+ s = astr[nbuf++]; >+ if (nbuf >= NUMBUFS) >+ nbuf = 0; >+ >+ switch (sa->sa_family) { >+ case AF_INET: >+ default: >+ inet_ntop(AF_INET, &satosin(sa)->sin_addr, s, sizeof (astr[0])); >+ break; >+ case AF_INET6: >+ inet_ntop(AF_INET6, &satosin6(sa)->sin6_addr, s, >+ sizeof (astr[0])); >+ break; >+ } >+ return s; >+} >+ >+#ifdef UMB_DEBUG >+static char * >+umb_uuid2str(uint8_t uuid[MBIM_UUID_LEN]) >+{ >+ static char uuidstr[2 * MBIM_UUID_LEN + 5]; >+ >+#define UUID_BFMT "%02X" >+#define UUID_SEP "-" >+ snprintf(uuidstr, sizeof (uuidstr), >+ UUID_BFMT UUID_BFMT UUID_BFMT UUID_BFMT UUID_SEP >+ UUID_BFMT UUID_BFMT UUID_SEP >+ UUID_BFMT UUID_BFMT UUID_SEP >+ UUID_BFMT UUID_BFMT UUID_SEP >+ UUID_BFMT UUID_BFMT UUID_BFMT UUID_BFMT UUID_BFMT UUID_BFMT, >+ uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5], >+ uuid[6], uuid[7], uuid[8], uuid[9], uuid[10], uuid[11], >+ uuid[12], uuid[13], uuid[14], uuid[15]); >+ return uuidstr; >+} >+ >+static void >+umb_dump(void *buf, int len) >+{ >+ int i = 0; >+ uint8_t *c = buf; >+ >+ if (len == 0) >+ return; >+ while (i < len) { >+ if ((i % 16) == 0) { >+ if (i > 0) >+ log(LOG_DEBUG, "\n"); >+ log(LOG_DEBUG, "%4d: ", i); >+ } >+ log(LOG_DEBUG, " %02x", *c); >+ c++; >+ i++; >+ } >+ log(LOG_DEBUG, "\n"); >+} >+#endif /* UMB_DEBUG */ >+ >+DRIVER_MODULE(umb, uhub, umb_driver, NULL, NULL); >+MODULE_DEPEND(umb, usb, 1, 1, 1); >diff --git a/sys/dev/usb/net/if_umbreg.h b/sys/dev/usb/net/if_umbreg.h >new file mode 100644 >index 000000000000..a134a3073455 >--- /dev/null >+++ b/sys/dev/usb/net/if_umbreg.h >@@ -0,0 +1,441 @@ >+/* $OpenBSD: if_umb.h,v 1.4 2017/04/18 13:27:55 gerhard Exp $ */ >+ >+/* >+ * Original copyright (c) 2016 genua mbH (OpenBSD version) >+ * >+ * Permission to use, copy, modify, and distribute this software for any >+ * purpose with or without fee is hereby granted, provided that the above >+ * copyright notice and this permission notice appear in all copies. >+ * >+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES >+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF >+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR >+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES >+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN >+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF >+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. >+ * >+ * Copyright (c) 2022 ADISTA SAS (re-write for FreeBSD) >+ * >+ * Re-write for FreeBSD by Pierre Pronchery <pierre@defora.net> >+ * >+ * Redistribution and use in source and binary forms, with or without >+ * modification, are permitted provided that the following conditions are met: >+ * >+ * - Redistributions of source code must retain the above copyright notice, >+ * this list of conditions and the following disclaimer. >+ * - Redistributions in binary form must reproduce the above copyright notice, >+ * this list of conditions and the following disclaimer in the documentation >+ * and/or other materials provided with the distribution. >+ * - Neither the name of the copyright holder nor the names of its contributors >+ * may be used to endorse or promote products derived from this software >+ * without specific prior written permission. >+ * >+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" >+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE >+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE >+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE >+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR >+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF >+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS >+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN >+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) >+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE >+ * POSSIBILITY OF SUCH DAMAGE. >+ */ >+ >+/* >+ * Mobile Broadband Interface Model >+ * http://www.usb.org/developers/docs/devclass_docs/MBIM-Compliance-1.0.pdf >+ */ >+ >+struct umb_valdescr { >+ int val; >+ char const *descr; >+}; >+ >+static const char * >+umb_val2descr(const struct umb_valdescr *vdp, int val) >+{ >+ static char sval[32]; >+ >+ while (vdp->descr != NULL) { >+ if (vdp->val == val) >+ return vdp->descr; >+ vdp++; >+ } >+ snprintf(sval, sizeof (sval), "#%d", val); >+ return sval; >+} >+ >+#define MBIM_REGSTATE_DESCRIPTIONS { \ >+ { MBIM_REGSTATE_UNKNOWN, "unknown" }, \ >+ { MBIM_REGSTATE_DEREGISTERED, "not registered" }, \ >+ { MBIM_REGSTATE_SEARCHING, "searching" }, \ >+ { MBIM_REGSTATE_HOME, "home network" }, \ >+ { MBIM_REGSTATE_ROAMING, "roaming network" }, \ >+ { MBIM_REGSTATE_PARTNER, "partner network" }, \ >+ { MBIM_REGSTATE_DENIED, "access denied" }, \ >+ { 0, NULL } } >+ >+#define MBIM_DATACLASS_DESCRIPTIONS { \ >+ { MBIM_DATACLASS_NONE, "none" }, \ >+ { MBIM_DATACLASS_GPRS, "GPRS" }, \ >+ { MBIM_DATACLASS_EDGE, "EDGE" }, \ >+ { MBIM_DATACLASS_UMTS, "UMTS" }, \ >+ { MBIM_DATACLASS_HSDPA, "HSDPA" }, \ >+ { MBIM_DATACLASS_HSUPA, "HSUPA" }, \ >+ { MBIM_DATACLASS_HSDPA|MBIM_DATACLASS_HSUPA, "HSPA" }, \ >+ { MBIM_DATACLASS_LTE, "LTE" }, \ >+ { MBIM_DATACLASS_1XRTT, "CDMA2000" }, \ >+ { MBIM_DATACLASS_1XEVDO, "CDMA2000" }, \ >+ { MBIM_DATACLASS_1XEVDO_REV_A, "CDMA2000" }, \ >+ { MBIM_DATACLASS_1XEVDV, "CDMA2000" }, \ >+ { MBIM_DATACLASS_3XRTT, "CDMA2000" }, \ >+ { MBIM_DATACLASS_1XEVDO_REV_B, "CDMA2000" }, \ >+ { MBIM_DATACLASS_UMB, "CDMA2000" }, \ >+ { MBIM_DATACLASS_CUSTOM, "custom" }, \ >+ { 0, NULL } } >+ >+#define MBIM_1TO1_DESCRIPTION(m) { (m), #m } >+#define MBIM_MESSAGES_DESCRIPTIONS { \ >+ MBIM_1TO1_DESCRIPTION(MBIM_OPEN_MSG), \ >+ MBIM_1TO1_DESCRIPTION(MBIM_CLOSE_MSG), \ >+ MBIM_1TO1_DESCRIPTION(MBIM_COMMAND_MSG), \ >+ MBIM_1TO1_DESCRIPTION(MBIM_HOST_ERROR_MSG), \ >+ MBIM_1TO1_DESCRIPTION(MBIM_OPEN_DONE), \ >+ MBIM_1TO1_DESCRIPTION(MBIM_CLOSE_DONE), \ >+ MBIM_1TO1_DESCRIPTION(MBIM_COMMAND_DONE), \ >+ MBIM_1TO1_DESCRIPTION(MBIM_FUNCTION_ERROR_MSG), \ >+ MBIM_1TO1_DESCRIPTION(MBIM_INDICATE_STATUS_MSG), \ >+ { 0, NULL } } >+ >+#define MBIM_STATUS_DESCRIPTION(m) { MBIM_STATUS_ ## m, #m } >+#define MBIM_STATUS_DESCRIPTIONS { \ >+ MBIM_STATUS_DESCRIPTION(SUCCESS), \ >+ MBIM_STATUS_DESCRIPTION(BUSY), \ >+ MBIM_STATUS_DESCRIPTION(FAILURE), \ >+ MBIM_STATUS_DESCRIPTION(SIM_NOT_INSERTED), \ >+ MBIM_STATUS_DESCRIPTION(BAD_SIM), \ >+ MBIM_STATUS_DESCRIPTION(PIN_REQUIRED), \ >+ MBIM_STATUS_DESCRIPTION(PIN_DISABLED), \ >+ MBIM_STATUS_DESCRIPTION(NOT_REGISTERED), \ >+ MBIM_STATUS_DESCRIPTION(PROVIDERS_NOT_FOUND), \ >+ MBIM_STATUS_DESCRIPTION(NO_DEVICE_SUPPORT), \ >+ MBIM_STATUS_DESCRIPTION(PROVIDER_NOT_VISIBLE), \ >+ MBIM_STATUS_DESCRIPTION(DATA_CLASS_NOT_AVAILABLE), \ >+ MBIM_STATUS_DESCRIPTION(PACKET_SERVICE_DETACHED), \ >+ MBIM_STATUS_DESCRIPTION(MAX_ACTIVATED_CONTEXTS), \ >+ MBIM_STATUS_DESCRIPTION(NOT_INITIALIZED), \ >+ MBIM_STATUS_DESCRIPTION(VOICE_CALL_IN_PROGRESS), \ >+ MBIM_STATUS_DESCRIPTION(CONTEXT_NOT_ACTIVATED), \ >+ MBIM_STATUS_DESCRIPTION(SERVICE_NOT_ACTIVATED), \ >+ MBIM_STATUS_DESCRIPTION(INVALID_ACCESS_STRING), \ >+ MBIM_STATUS_DESCRIPTION(INVALID_USER_NAME_PWD), \ >+ MBIM_STATUS_DESCRIPTION(RADIO_POWER_OFF), \ >+ MBIM_STATUS_DESCRIPTION(INVALID_PARAMETERS), \ >+ MBIM_STATUS_DESCRIPTION(READ_FAILURE), \ >+ MBIM_STATUS_DESCRIPTION(WRITE_FAILURE), \ >+ MBIM_STATUS_DESCRIPTION(NO_PHONEBOOK), \ >+ MBIM_STATUS_DESCRIPTION(PARAMETER_TOO_LONG), \ >+ MBIM_STATUS_DESCRIPTION(STK_BUSY), \ >+ MBIM_STATUS_DESCRIPTION(OPERATION_NOT_ALLOWED), \ >+ MBIM_STATUS_DESCRIPTION(MEMORY_FAILURE), \ >+ MBIM_STATUS_DESCRIPTION(INVALID_MEMORY_INDEX), \ >+ MBIM_STATUS_DESCRIPTION(MEMORY_FULL), \ >+ MBIM_STATUS_DESCRIPTION(FILTER_NOT_SUPPORTED), \ >+ MBIM_STATUS_DESCRIPTION(DSS_INSTANCE_LIMIT), \ >+ MBIM_STATUS_DESCRIPTION(INVALID_DEVICE_SERVICE_OPERATION), \ >+ MBIM_STATUS_DESCRIPTION(AUTH_INCORRECT_AUTN), \ >+ MBIM_STATUS_DESCRIPTION(AUTH_SYNC_FAILURE), \ >+ MBIM_STATUS_DESCRIPTION(AUTH_AMF_NOT_SET), \ >+ MBIM_STATUS_DESCRIPTION(CONTEXT_NOT_SUPPORTED), \ >+ MBIM_STATUS_DESCRIPTION(SMS_UNKNOWN_SMSC_ADDRESS), \ >+ MBIM_STATUS_DESCRIPTION(SMS_NETWORK_TIMEOUT), \ >+ MBIM_STATUS_DESCRIPTION(SMS_LANG_NOT_SUPPORTED), \ >+ MBIM_STATUS_DESCRIPTION(SMS_ENCODING_NOT_SUPPORTED), \ >+ MBIM_STATUS_DESCRIPTION(SMS_FORMAT_NOT_SUPPORTED), \ >+ { 0, NULL } } >+ >+#define MBIM_ERROR_DESCRIPTION(m) { MBIM_ERROR_ ## m, #m } >+#define MBIM_ERROR_DESCRIPTIONS { \ >+ MBIM_ERROR_DESCRIPTION(TIMEOUT_FRAGMENT), \ >+ MBIM_ERROR_DESCRIPTION(FRAGMENT_OUT_OF_SEQUENCE), \ >+ MBIM_ERROR_DESCRIPTION(LENGTH_MISMATCH), \ >+ MBIM_ERROR_DESCRIPTION(DUPLICATED_TID), \ >+ MBIM_ERROR_DESCRIPTION(NOT_OPENED), \ >+ MBIM_ERROR_DESCRIPTION(UNKNOWN), \ >+ MBIM_ERROR_DESCRIPTION(CANCEL), \ >+ MBIM_ERROR_DESCRIPTION(MAX_TRANSFER), \ >+ { 0, NULL } } >+ >+#define MBIM_CID_DESCRIPTIONS { \ >+ MBIM_1TO1_DESCRIPTION(MBIM_CID_DEVICE_CAPS), \ >+ MBIM_1TO1_DESCRIPTION(MBIM_CID_SUBSCRIBER_READY_STATUS), \ >+ MBIM_1TO1_DESCRIPTION(MBIM_CID_RADIO_STATE), \ >+ MBIM_1TO1_DESCRIPTION(MBIM_CID_PIN), \ >+ MBIM_1TO1_DESCRIPTION(MBIM_CID_PIN_LIST), \ >+ MBIM_1TO1_DESCRIPTION(MBIM_CID_HOME_PROVIDER), \ >+ MBIM_1TO1_DESCRIPTION(MBIM_CID_PREFERRED_PROVIDERS), \ >+ MBIM_1TO1_DESCRIPTION(MBIM_CID_VISIBLE_PROVIDERS), \ >+ MBIM_1TO1_DESCRIPTION(MBIM_CID_REGISTER_STATE), \ >+ MBIM_1TO1_DESCRIPTION(MBIM_CID_PACKET_SERVICE), \ >+ MBIM_1TO1_DESCRIPTION(MBIM_CID_SIGNAL_STATE), \ >+ MBIM_1TO1_DESCRIPTION(MBIM_CID_CONNECT), \ >+ MBIM_1TO1_DESCRIPTION(MBIM_CID_PROVISIONED_CONTEXTS), \ >+ MBIM_1TO1_DESCRIPTION(MBIM_CID_SERVICE_ACTIVATION), \ >+ MBIM_1TO1_DESCRIPTION(MBIM_CID_IP_CONFIGURATION), \ >+ MBIM_1TO1_DESCRIPTION(MBIM_CID_DEVICE_SERVICES), \ >+ MBIM_1TO1_DESCRIPTION(MBIM_CID_DEVICE_SERVICE_SUBSCRIBE_LIST), \ >+ MBIM_1TO1_DESCRIPTION(MBIM_CID_PACKET_STATISTICS), \ >+ MBIM_1TO1_DESCRIPTION(MBIM_CID_NETWORK_IDLE_HINT), \ >+ MBIM_1TO1_DESCRIPTION(MBIM_CID_EMERGENCY_MODE), \ >+ MBIM_1TO1_DESCRIPTION(MBIM_CID_IP_PACKET_FILTERS), \ >+ MBIM_1TO1_DESCRIPTION(MBIM_CID_MULTICARRIER_PROVIDERS), \ >+ { 0, NULL } } >+ >+#define MBIM_SIMSTATE_DESCRIPTIONS { \ >+ { MBIM_SIMSTATE_NOTINITIALIZED, "not initialized" }, \ >+ { MBIM_SIMSTATE_INITIALIZED, "initialized" }, \ >+ { MBIM_SIMSTATE_NOTINSERTED, "not inserted" }, \ >+ { MBIM_SIMSTATE_BADSIM, "bad type" }, \ >+ { MBIM_SIMSTATE_FAILURE, "failed" }, \ >+ { MBIM_SIMSTATE_NOTACTIVATED, "not activated" }, \ >+ { MBIM_SIMSTATE_LOCKED, "locked" }, \ >+ { 0, NULL } } >+ >+#define MBIM_PINTYPE_DESCRIPTIONS { \ >+ { MBIM_PIN_TYPE_NONE, "none" }, \ >+ { MBIM_PIN_TYPE_CUSTOM, "custom" }, \ >+ { MBIM_PIN_TYPE_PIN1, "PIN1" }, \ >+ { MBIM_PIN_TYPE_PIN2, "PIN2" }, \ >+ { MBIM_PIN_TYPE_DEV_SIM_PIN, "device PIN" }, \ >+ { MBIM_PIN_TYPE_DEV_FIRST_SIM_PIN, "device 1st PIN" }, \ >+ { MBIM_PIN_TYPE_NETWORK_PIN, "network PIN" }, \ >+ { MBIM_PIN_TYPE_NETWORK_SUBSET_PIN, "network subset PIN" }, \ >+ { MBIM_PIN_TYPE_SERVICE_PROVIDER_PIN, "provider PIN" }, \ >+ { MBIM_PIN_TYPE_CORPORATE_PIN, "corporate PIN" }, \ >+ { MBIM_PIN_TYPE_SUBSIDY_LOCK, "subsidy lock" }, \ >+ { MBIM_PIN_TYPE_PUK1, "PUK" }, \ >+ { MBIM_PIN_TYPE_PUK2, "PUK2" }, \ >+ { MBIM_PIN_TYPE_DEV_FIRST_SIM_PUK, "device 1st PUK" }, \ >+ { MBIM_PIN_TYPE_NETWORK_PUK, "network PUK" }, \ >+ { MBIM_PIN_TYPE_NETWORK_SUBSET_PUK, "network subset PUK" }, \ >+ { MBIM_PIN_TYPE_SERVICE_PROVIDER_PUK, "provider PUK" }, \ >+ { MBIM_PIN_TYPE_CORPORATE_PUK, "corporate PUK" }, \ >+ { 0, NULL } } >+ >+#define MBIM_PKTSRV_STATE_DESCRIPTIONS { \ >+ { MBIM_PKTSERVICE_STATE_UNKNOWN, "unknown" }, \ >+ { MBIM_PKTSERVICE_STATE_ATTACHING, "attaching" }, \ >+ { MBIM_PKTSERVICE_STATE_ATTACHED, "attached" }, \ >+ { MBIM_PKTSERVICE_STATE_DETACHING, "detaching" }, \ >+ { MBIM_PKTSERVICE_STATE_DETACHED, "detached" }, \ >+ { 0, NULL } } >+ >+#define MBIM_ACTIVATION_STATE_DESCRIPTIONS { \ >+ { MBIM_ACTIVATION_STATE_UNKNOWN, "unknown" }, \ >+ { MBIM_ACTIVATION_STATE_ACTIVATED, "activated" }, \ >+ { MBIM_ACTIVATION_STATE_ACTIVATING, "activating" }, \ >+ { MBIM_ACTIVATION_STATE_DEACTIVATED, "deactivated" }, \ >+ { MBIM_ACTIVATION_STATE_DEACTIVATING, "deactivating" }, \ >+ { 0, NULL } } >+ >+/* >+ * Driver internal state >+ */ >+enum umb_state { >+ UMB_S_DOWN = 0, /* interface down */ >+ UMB_S_OPEN, /* MBIM device has been opened */ >+ UMB_S_CID, /* QMI client id allocated */ >+ UMB_S_RADIO, /* radio is on */ >+ UMB_S_SIMREADY, /* SIM is ready */ >+ UMB_S_ATTACHED, /* packet service is attached */ >+ UMB_S_CONNECTED, /* connected to provider */ >+ UMB_S_UP, /* have IP configuration */ >+}; >+ >+#define UMB_INTERNAL_STATE_DESCRIPTIONS { \ >+ { UMB_S_DOWN, "down" }, \ >+ { UMB_S_OPEN, "open" }, \ >+ { UMB_S_CID, "CID allocated" }, \ >+ { UMB_S_RADIO, "radio on" }, \ >+ { UMB_S_SIMREADY, "SIM is ready" }, \ >+ { UMB_S_ATTACHED, "attached" }, \ >+ { UMB_S_CONNECTED, "connected" }, \ >+ { UMB_S_UP, "up" }, \ >+ { 0, NULL } } >+ >+/* >+ * UMB parameters (SIOC[GS]UMBPARAM ioctls) >+ */ >+struct umb_parameter { >+ int op; >+ int is_puk; >+ uint16_t pin[MBIM_PIN_MAXLEN]; >+ int pinlen; >+ >+ uint16_t newpin[MBIM_PIN_MAXLEN]; >+ int newpinlen; >+ >+#define UMB_APN_MAXLEN 100 >+ uint16_t apn[UMB_APN_MAXLEN]; >+ int apnlen; >+ >+#define UMB_USERNAME_MAXLEN 205 >+ uint16_t username[UMB_USERNAME_MAXLEN]; >+ int usernamelen; >+ >+#define UMB_PASSWORD_MAXLEN 205 >+ uint16_t password[UMB_PASSWORD_MAXLEN]; >+ int passwordlen; >+ >+ int roaming; >+ uint32_t preferredclasses; >+}; >+ >+/* >+ * UMB device status info (SIOCGUMBINFO ioctl) >+ */ >+struct umb_info { >+ enum umb_state state; >+ int enable_roaming; >+#define UMB_PIN_REQUIRED 0 >+#define UMB_PIN_UNLOCKED 1 >+#define UMB_PUK_REQUIRED 2 >+ int pin_state; >+ int pin_attempts_left; >+ int activation; >+ int sim_state; >+ int regstate; >+ int regmode; >+ int nwerror; >+ int packetstate; >+ uint32_t supportedclasses; /* what the hw supports */ >+ uint32_t preferredclasses; /* what the user prefers */ >+ uint32_t highestclass; /* what the network offers */ >+ uint32_t cellclass; >+#define UMB_PROVIDERNAME_MAXLEN 20 >+ uint16_t provider[UMB_PROVIDERNAME_MAXLEN]; >+#define UMB_PHONENR_MAXLEN 22 >+ uint16_t pn[UMB_PHONENR_MAXLEN]; >+#define UMB_SUBSCRIBERID_MAXLEN 15 >+ uint16_t sid[UMB_SUBSCRIBERID_MAXLEN]; >+#define UMB_ICCID_MAXLEN 20 >+ uint16_t iccid[UMB_ICCID_MAXLEN]; >+#define UMB_ROAMINGTEXT_MAXLEN 63 >+ uint16_t roamingtxt[UMB_ROAMINGTEXT_MAXLEN]; >+ >+#define UMB_DEVID_MAXLEN 18 >+ uint16_t devid[UMB_DEVID_MAXLEN]; >+#define UMB_FWINFO_MAXLEN 30 >+ uint16_t fwinfo[UMB_FWINFO_MAXLEN]; >+#define UMB_HWINFO_MAXLEN 30 >+ uint16_t hwinfo[UMB_HWINFO_MAXLEN]; >+ >+ uint16_t apn[UMB_APN_MAXLEN]; >+ int apnlen; >+ >+ uint16_t username[UMB_USERNAME_MAXLEN]; >+ int usernamelen; >+ >+ uint16_t password[UMB_PASSWORD_MAXLEN]; >+ int passwordlen; >+ >+#define UMB_VALUE_UNKNOWN -999 >+ int rssi; >+#define UMB_BER_EXCELLENT 0 >+#define UMB_BER_VERYGOOD 1 >+#define UMB_BER_GOOD 2 >+#define UMB_BER_OK 3 >+#define UMB_BER_MEDIUM 4 >+#define UMB_BER_BAD 5 >+#define UMB_BER_VERYBAD 6 >+#define UMB_BER_EXTREMELYBAD 7 >+ int ber; >+ >+ int hw_radio_on; >+ int sw_radio_on; >+ >+ uint64_t uplink_speed; >+ uint64_t downlink_speed; >+ >+#define UMB_MAX_DNSSRV 2 >+ struct in_addr ipv4dns[UMB_MAX_DNSSRV]; >+}; >+ >+#if !defined(ifr_mtu) >+#define ifr_mtu ifr_ifru.ifru_metric >+#endif >+ >+#ifdef _KERNEL >+/* >+ * UMB device >+ */ >+enum { >+ UMB_INTR_RX, >+ UMB_BULK_RX, >+ UMB_BULK_TX, >+ UMB_N_TRANSFER, >+}; >+ >+struct umb_task { >+ struct usb_proc_msg hdr; >+ struct umb_softc *sc; >+}; >+ >+struct umb_softc { >+ device_t sc_dev; >+ struct ifnet *sc_if; >+#define GET_IFP(sc) ((sc)->sc_if) >+ struct ifmedia sc_im; >+ struct usb_device *sc_udev; >+ struct usb_xfer *sc_xfer[UMB_N_TRANSFER]; >+ uint8_t sc_ifaces_index[2]; >+ >+ int sc_ver_maj; >+ int sc_ver_min; >+ int sc_ctrl_len; >+ int sc_maxpktlen; >+ int sc_maxsessions; >+ >+#define UMBFLG_FCC_AUTH_REQUIRED 0x0001 >+#define UMBFLG_NO_INET6 0x0002 >+ uint32_t sc_flags; >+ int sc_cid; >+ >+ struct usb_process sc_taskqueue; >+ struct umb_task sc_proc_attach_task[2]; >+ struct umb_task sc_proc_start_task[2]; >+ struct umb_task sc_proc_state_task[2]; >+ struct umb_task sc_proc_get_response_task[2]; >+ >+ int sc_nresp; >+ struct mtx sc_mutex; >+ struct usb_callout sc_statechg_timer; >+ char sc_dying; >+ char sc_attached; >+ >+ uint8_t sc_ctrl_ifaceno; >+ struct usb_interface *sc_data_iface; >+ >+ void *sc_resp_buf; >+ void *sc_ctrl_msg; >+ >+ void *sc_rx_buf; >+ uint32_t sc_rx_bufsz; >+ unsigned sc_rx_nerr; >+ struct ifqueue sc_rx_queue; >+ >+ void *sc_tx_buf; >+ struct mbuf *sc_tx_m; >+ uint32_t sc_tx_bufsz; >+ uint32_t sc_tx_seq; >+ >+ uint32_t sc_tid; >+ >+#define sc_state sc_info.state >+#define sc_roaming sc_info.enable_roaming >+ struct umb_info sc_info; >+}; >+#endif /* _KERNEL */ >diff --git a/sys/dev/usb/net/mbim.h b/sys/dev/usb/net/mbim.h >new file mode 100644 >index 000000000000..5d7ecff114b9 >--- /dev/null >+++ b/sys/dev/usb/net/mbim.h >@@ -0,0 +1,724 @@ >+/* $OpenBSD: mbim.h,v 1.4 2017/04/18 13:27:55 gerhard Exp $ */ >+ >+/* >+ * Original copyright (c) 2016 genua mbH (OpenBSD version) >+ * >+ * Permission to use, copy, modify, and distribute this software for any >+ * purpose with or without fee is hereby granted, provided that the above >+ * copyright notice and this permission notice appear in all copies. >+ * >+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES >+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF >+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR >+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES >+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN >+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF >+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. >+ * >+ * Copyright (c) 2022 ADISTA SAS (re-write for FreeBSD) >+ * >+ * Re-write for FreeBSD by Pierre Pronchery <pierre@defora.net> >+ * >+ * Redistribution and use in source and binary forms, with or without >+ * modification, are permitted provided that the following conditions are met: >+ * >+ * - Redistributions of source code must retain the above copyright notice, >+ * this list of conditions and the following disclaimer. >+ * - Redistributions in binary form must reproduce the above copyright notice, >+ * this list of conditions and the following disclaimer in the documentation >+ * and/or other materials provided with the distribution. >+ * - Neither the name of the copyright holder nor the names of its contributors >+ * may be used to endorse or promote products derived from this software >+ * without specific prior written permission. >+ * >+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" >+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE >+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE >+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE >+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR >+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF >+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS >+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN >+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) >+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE >+ * POSSIBILITY OF SUCH DAMAGE. >+ */ >+ >+/* >+ * Mobile Broadband Interface Model >+ * http://www.usb.org/developers/docs/devclass_docs/MBIM-Compliance-1.0.pdf >+ */ >+#ifndef _MBIM_H_ >+#define _MBIM_H_ >+ >+#define UDESCSUB_MBIM 27 >+#define MBIM_INTERFACE_ALTSETTING 1 >+ >+#define MBIM_RESET_FUNCTION 0x05 >+ >+/* >+ * Registration state (MBIM_REGISTER_STATE) >+ */ >+#define MBIM_REGSTATE_UNKNOWN 0 >+#define MBIM_REGSTATE_DEREGISTERED 1 >+#define MBIM_REGSTATE_SEARCHING 2 >+#define MBIM_REGSTATE_HOME 3 >+#define MBIM_REGSTATE_ROAMING 4 >+#define MBIM_REGSTATE_PARTNER 5 >+#define MBIM_REGSTATE_DENIED 6 >+ >+/* >+ * Data classes mask (MBIM_DATA_CLASS) >+ */ >+#define MBIM_DATACLASS_NONE 0x00000000 >+#define MBIM_DATACLASS_GPRS 0x00000001 >+#define MBIM_DATACLASS_EDGE 0x00000002 >+#define MBIM_DATACLASS_UMTS 0x00000004 >+#define MBIM_DATACLASS_HSDPA 0x00000008 >+#define MBIM_DATACLASS_HSUPA 0x00000010 >+#define MBIM_DATACLASS_LTE 0x00000020 >+#define MBIM_DATACLASS_1XRTT 0x00010000 >+#define MBIM_DATACLASS_1XEVDO 0x00020000 >+#define MBIM_DATACLASS_1XEVDO_REV_A 0x00040000 >+#define MBIM_DATACLASS_1XEVDV 0x00080000 >+#define MBIM_DATACLASS_3XRTT 0x00100000 >+#define MBIM_DATACLASS_1XEVDO_REV_B 0x00200000 >+#define MBIM_DATACLASS_UMB 0x00400000 >+#define MBIM_DATACLASS_CUSTOM 0x80000000 >+ >+/* >+ * Cell classes mask (MBIM_CELLULAR_CLASS) >+ */ >+#define MBIM_CELLCLASS_GSM 0x00000001 >+#define MBIM_CELLCLASS_CDMA 0x00000002 >+ >+/* >+ * UUIDs >+ */ >+#define MBIM_UUID_LEN 16 >+ >+#define MBIM_UUID_BASIC_CONNECT { \ >+ 0xa2, 0x89, 0xcc, 0x33, 0xbc, 0xbb, 0x8b, 0x4f, \ >+ 0xb6, 0xb0, 0x13, 0x3e, 0xc2, 0xaa, 0xe6, 0xdf \ >+ } >+ >+#define MBIM_UUID_CONTEXT_INTERNET { \ >+ 0x7e, 0x5e, 0x2a, 0x7e, 0x4e, 0x6f, 0x72, 0x72, \ >+ 0x73, 0x6b, 0x65, 0x6e, 0x7e, 0x5e, 0x2a, 0x7e \ >+ } >+ >+#define MBIM_UUID_CONTEXT_VPN { \ >+ 0x9b, 0x9f, 0x7b, 0xbe, 0x89, 0x52, 0x44, 0xb7, \ >+ 0x83, 0xac, 0xca, 0x41, 0x31, 0x8d, 0xf7, 0xa0 \ >+ } >+ >+#define MBIM_UUID_QMI_MBIM { \ >+ 0xd1, 0xa3, 0x0b, 0xc2, 0xf9, 0x7a, 0x6e, 0x43, \ >+ 0xbf, 0x65, 0xc7, 0xe2, 0x4f, 0xb0, 0xf0, 0xd3 \ >+ } >+ >+#define MBIM_CTRLMSG_MINLEN 64 >+#define MBIM_CTRLMSG_MAXLEN (4 * 1204) >+ >+#define MBIM_MAXSEGSZ_MINVAL (2 * 1024) >+ >+/* >+ * Control messages (host to function) >+ */ >+#define MBIM_OPEN_MSG 1U >+#define MBIM_CLOSE_MSG 2U >+#define MBIM_COMMAND_MSG 3U >+#define MBIM_HOST_ERROR_MSG 4U >+ >+/* >+ * Control messages (function to host) >+ */ >+#define MBIM_OPEN_DONE 0x80000001U >+#define MBIM_CLOSE_DONE 0x80000002U >+#define MBIM_COMMAND_DONE 0x80000003U >+#define MBIM_FUNCTION_ERROR_MSG 0x80000004U >+#define MBIM_INDICATE_STATUS_MSG 0x80000007U >+ >+/* >+ * Generic status codes >+ */ >+#define MBIM_STATUS_SUCCESS 0 >+#define MBIM_STATUS_BUSY 1 >+#define MBIM_STATUS_FAILURE 2 >+#define MBIM_STATUS_SIM_NOT_INSERTED 3 >+#define MBIM_STATUS_BAD_SIM 4 >+#define MBIM_STATUS_PIN_REQUIRED 5 >+#define MBIM_STATUS_PIN_DISABLED 6 >+#define MBIM_STATUS_NOT_REGISTERED 7 >+#define MBIM_STATUS_PROVIDERS_NOT_FOUND 8 >+#define MBIM_STATUS_NO_DEVICE_SUPPORT 9 >+#define MBIM_STATUS_PROVIDER_NOT_VISIBLE 10 >+#define MBIM_STATUS_DATA_CLASS_NOT_AVAILABLE 11 >+#define MBIM_STATUS_PACKET_SERVICE_DETACHED 12 >+#define MBIM_STATUS_MAX_ACTIVATED_CONTEXTS 13 >+#define MBIM_STATUS_NOT_INITIALIZED 14 >+#define MBIM_STATUS_VOICE_CALL_IN_PROGRESS 15 >+#define MBIM_STATUS_CONTEXT_NOT_ACTIVATED 16 >+#define MBIM_STATUS_SERVICE_NOT_ACTIVATED 17 >+#define MBIM_STATUS_INVALID_ACCESS_STRING 18 >+#define MBIM_STATUS_INVALID_USER_NAME_PWD 19 >+#define MBIM_STATUS_RADIO_POWER_OFF 20 >+#define MBIM_STATUS_INVALID_PARAMETERS 21 >+#define MBIM_STATUS_READ_FAILURE 22 >+#define MBIM_STATUS_WRITE_FAILURE 23 >+#define MBIM_STATUS_NO_PHONEBOOK 25 >+#define MBIM_STATUS_PARAMETER_TOO_LONG 26 >+#define MBIM_STATUS_STK_BUSY 27 >+#define MBIM_STATUS_OPERATION_NOT_ALLOWED 28 >+#define MBIM_STATUS_MEMORY_FAILURE 29 >+#define MBIM_STATUS_INVALID_MEMORY_INDEX 30 >+#define MBIM_STATUS_MEMORY_FULL 31 >+#define MBIM_STATUS_FILTER_NOT_SUPPORTED 32 >+#define MBIM_STATUS_DSS_INSTANCE_LIMIT 33 >+#define MBIM_STATUS_INVALID_DEVICE_SERVICE_OPERATION 34 >+#define MBIM_STATUS_AUTH_INCORRECT_AUTN 35 >+#define MBIM_STATUS_AUTH_SYNC_FAILURE 36 >+#define MBIM_STATUS_AUTH_AMF_NOT_SET 37 >+#define MBIM_STATUS_CONTEXT_NOT_SUPPORTED 38 >+#define MBIM_STATUS_SMS_UNKNOWN_SMSC_ADDRESS 100 >+#define MBIM_STATUS_SMS_NETWORK_TIMEOUT 101 >+#define MBIM_STATUS_SMS_LANG_NOT_SUPPORTED 102 >+#define MBIM_STATUS_SMS_ENCODING_NOT_SUPPORTED 103 >+#define MBIM_STATUS_SMS_FORMAT_NOT_SUPPORTED 104 >+ >+/* >+ * Message formats >+ */ >+struct mbim_msghdr { >+ /* Msg header */ >+ uint32_t type; /* message type */ >+ uint32_t len; /* message length */ >+ uint32_t tid; /* transaction id */ >+} __packed; >+ >+struct mbim_fraghdr { >+ uint32_t nfrag; /* total # of fragments */ >+ uint32_t currfrag; /* current fragment */ >+} __packed; >+ >+struct mbim_fragmented_msg_hdr { >+ struct mbim_msghdr hdr; >+ struct mbim_fraghdr frag; >+} __packed; >+ >+struct mbim_h2f_openmsg { >+ struct mbim_msghdr hdr; >+ uint32_t maxlen; >+} __packed; >+ >+struct mbim_h2f_closemsg { >+ struct mbim_msghdr hdr; >+} __packed; >+ >+struct mbim_h2f_cmd { >+ struct mbim_msghdr hdr; >+ struct mbim_fraghdr frag; >+ uint8_t devid[MBIM_UUID_LEN]; >+ uint32_t cid; /* command id */ >+#define MBIM_CMDOP_QRY 0 >+#define MBIM_CMDOP_SET 1 >+ uint32_t op; >+ uint32_t infolen; >+ uint8_t info[]; >+} __packed; >+ >+struct mbim_f2h_indicate_status { >+ struct mbim_msghdr hdr; >+ struct mbim_fraghdr frag; >+ uint8_t devid[MBIM_UUID_LEN]; >+ uint32_t cid; /* command id */ >+ uint32_t infolen; >+ uint8_t info[]; >+} __packed; >+ >+struct mbim_f2h_hosterr { >+ struct mbim_msghdr hdr; >+ >+#define MBIM_ERROR_TIMEOUT_FRAGMENT 1 >+#define MBIM_ERROR_FRAGMENT_OUT_OF_SEQUENCE 2 >+#define MBIM_ERROR_LENGTH_MISMATCH 3 >+#define MBIM_ERROR_DUPLICATED_TID 4 >+#define MBIM_ERROR_NOT_OPENED 5 >+#define MBIM_ERROR_UNKNOWN 6 >+#define MBIM_ERROR_CANCEL 7 >+#define MBIM_ERROR_MAX_TRANSFER 8 >+ uint32_t err; >+} __packed; >+ >+struct mbim_f2h_openclosedone { >+ struct mbim_msghdr hdr; >+ int32_t status; >+} __packed; >+ >+struct mbim_f2h_cmddone { >+ struct mbim_msghdr hdr; >+ struct mbim_fraghdr frag; >+ uint8_t devid[MBIM_UUID_LEN]; >+ uint32_t cid; /* command id */ >+ int32_t status; >+ uint32_t infolen; >+ uint8_t info[]; >+} __packed; >+ >+/* >+ * Messages and commands for MBIM_UUID_BASIC_CONNECT >+ */ >+#define MBIM_CID_DEVICE_CAPS 1 >+#define MBIM_CID_SUBSCRIBER_READY_STATUS 2 >+#define MBIM_CID_RADIO_STATE 3 >+#define MBIM_CID_PIN 4 >+#define MBIM_CID_PIN_LIST 5 >+#define MBIM_CID_HOME_PROVIDER 6 >+#define MBIM_CID_PREFERRED_PROVIDERS 7 >+#define MBIM_CID_VISIBLE_PROVIDERS 8 >+#define MBIM_CID_REGISTER_STATE 9 >+#define MBIM_CID_PACKET_SERVICE 10 >+#define MBIM_CID_SIGNAL_STATE 11 >+#define MBIM_CID_CONNECT 12 >+#define MBIM_CID_PROVISIONED_CONTEXTS 13 >+#define MBIM_CID_SERVICE_ACTIVATION 14 >+#define MBIM_CID_IP_CONFIGURATION 15 >+#define MBIM_CID_DEVICE_SERVICES 16 >+#define MBIM_CID_DEVICE_SERVICE_SUBSCRIBE_LIST 19 >+#define MBIM_CID_PACKET_STATISTICS 20 >+#define MBIM_CID_NETWORK_IDLE_HINT 21 >+#define MBIM_CID_EMERGENCY_MODE 22 >+#define MBIM_CID_IP_PACKET_FILTERS 23 >+#define MBIM_CID_MULTICARRIER_PROVIDERS 24 >+ >+struct mbim_cid_subscriber_ready_info { >+#define MBIM_SIMSTATE_NOTINITIALIZED 0 >+#define MBIM_SIMSTATE_INITIALIZED 1 >+#define MBIM_SIMSTATE_NOTINSERTED 2 >+#define MBIM_SIMSTATE_BADSIM 3 >+#define MBIM_SIMSTATE_FAILURE 4 >+#define MBIM_SIMSTATE_NOTACTIVATED 5 >+#define MBIM_SIMSTATE_LOCKED 6 >+ uint32_t ready; >+ >+ uint32_t sid_offs; >+ uint32_t sid_size; >+ >+ uint32_t icc_offs; >+ uint32_t icc_size; >+ >+#define MBIM_SIMUNIQEID_NONE 0 >+#define MBIM_SIMUNIQEID_PROTECT 1 >+ uint32_t info; >+ >+ uint32_t no_pn; >+ struct { >+ uint32_t offs; >+ uint32_t size; >+ } >+ pn[]; >+} __packed; >+ >+struct mbim_cid_radio_state { >+#define MBIM_RADIO_STATE_OFF 0 >+#define MBIM_RADIO_STATE_ON 1 >+ uint32_t state; >+} __packed; >+ >+struct mbim_cid_radio_state_info { >+ uint32_t hw_state; >+ uint32_t sw_state; >+} __packed; >+ >+struct mbim_cid_pin { >+#define MBIM_PIN_TYPE_NONE 0 >+#define MBIM_PIN_TYPE_CUSTOM 1 >+#define MBIM_PIN_TYPE_PIN1 2 >+#define MBIM_PIN_TYPE_PIN2 3 >+#define MBIM_PIN_TYPE_DEV_SIM_PIN 4 >+#define MBIM_PIN_TYPE_DEV_FIRST_SIM_PIN 5 >+#define MBIM_PIN_TYPE_NETWORK_PIN 6 >+#define MBIM_PIN_TYPE_NETWORK_SUBSET_PIN 7 >+#define MBIM_PIN_TYPE_SERVICE_PROVIDER_PIN 8 >+#define MBIM_PIN_TYPE_CORPORATE_PIN 9 >+#define MBIM_PIN_TYPE_SUBSIDY_LOCK 10 >+#define MBIM_PIN_TYPE_PUK1 11 >+#define MBIM_PIN_TYPE_PUK2 12 >+#define MBIM_PIN_TYPE_DEV_FIRST_SIM_PUK 13 >+#define MBIM_PIN_TYPE_NETWORK_PUK 14 >+#define MBIM_PIN_TYPE_NETWORK_SUBSET_PUK 15 >+#define MBIM_PIN_TYPE_SERVICE_PROVIDER_PUK 16 >+#define MBIM_PIN_TYPE_CORPORATE_PUK 17 >+ uint32_t type; >+ >+#define MBIM_PIN_OP_ENTER 0 >+#define MBIM_PIN_OP_ENABLE 1 >+#define MBIM_PIN_OP_DISABLE 2 >+#define MBIM_PIN_OP_CHANGE 3 >+ uint32_t op; >+ uint32_t pin_offs; >+ uint32_t pin_size; >+ uint32_t newpin_offs; >+ uint32_t newpin_size; >+#define MBIM_PIN_MAXLEN 32 >+ uint8_t data[2 * MBIM_PIN_MAXLEN]; >+} __packed; >+ >+struct mbim_cid_pin_info { >+ uint32_t type; >+ >+#define MBIM_PIN_STATE_UNLOCKED 0 >+#define MBIM_PIN_STATE_LOCKED 1 >+ uint32_t state; >+ uint32_t remaining_attempts; >+} __packed; >+ >+struct mbim_cid_pin_list_info { >+ struct mbim_pin_desc { >+ >+#define MBIM_PINMODE_NOTSUPPORTED 0 >+#define MBIM_PINMODE_ENABLED 1 >+#define MBIM_PINMODE_DISABLED 2 >+ uint32_t mode; >+ >+#define MBIM_PINFORMAT_UNKNOWN 0 >+#define MBIM_PINFORMAT_NUMERIC 1 >+#define MBIM_PINFORMAT_ALPHANUMERIC 2 >+ uint32_t format; >+ >+ uint32_t minlen; >+ uint32_t maxlen; >+ } >+ pin1, >+ pin2, >+ dev_sim_pin, >+ first_dev_sim_pin, >+ net_pin, >+ net_sub_pin, >+ svp_pin, >+ corp_pin, >+ subsidy_lock, >+ custom; >+} __packed; >+ >+struct mbim_cid_device_caps { >+#define MBIM_DEVTYPE_UNKNOWN 0 >+#define MBIM_DEVTYPE_EMBEDDED 1 >+#define MBIM_DEVTYPE_REMOVABLE 2 >+#define MBIM_DEVTYPE_REMOTE 3 >+ uint32_t devtype; >+ >+ uint32_t cellclass; /* values: MBIM_CELLULAR_CLASS */ >+ uint32_t voiceclass; >+ uint32_t simclass; >+ uint32_t dataclass; /* values: MBIM_DATA_CLASS */ >+ uint32_t smscaps; >+ uint32_t cntrlcaps; >+ uint32_t max_sessions; >+ >+ uint32_t custdataclass_offs; >+ uint32_t custdataclass_size; >+ >+ uint32_t devid_offs; >+ uint32_t devid_size; >+ >+ uint32_t fwinfo_offs; >+ uint32_t fwinfo_size; >+ >+ uint32_t hwinfo_offs; >+ uint32_t hwinfo_size; >+ >+ uint32_t data[]; >+} __packed; >+ >+struct mbim_cid_registration_state { >+ uint32_t provid_offs; >+ uint32_t provid_size; >+ >+#define MBIM_REGACTION_AUTOMATIC 0 >+#define MBIM_REGACTION_MANUAL 1 >+ uint32_t regaction; >+ uint32_t data_class; >+ >+ uint32_t data[]; >+} __packed; >+ >+struct mbim_cid_registration_state_info { >+ uint32_t nwerror; >+ >+ uint32_t regstate; /* values: MBIM_REGISTER_STATE */ >+ >+#define MBIM_REGMODE_UNKNOWN 0 >+#define MBIM_REGMODE_AUTOMATIC 1 >+#define MBIM_REGMODE_MANUAL 2 >+ uint32_t regmode; >+ >+ uint32_t availclasses; /* values: MBIM_DATA_CLASS */ >+ uint32_t curcellclass; /* values: MBIM_CELLULAR_CLASS */ >+ >+ uint32_t provid_offs; >+ uint32_t provid_size; >+ >+ uint32_t provname_offs; >+ uint32_t provname_size; >+ >+ uint32_t roamingtxt_offs; >+ uint32_t roamingtxt_size; >+ >+#define MBIM_REGFLAGS_NONE 0 >+#define MBIM_REGFLAGS_MANUAL_NOT_AVAILABLE 1 >+#define MBIM_REGFLAGS_PACKETSERVICE_AUTOATTACH 2 >+ uint32_t regflag; >+ >+ uint32_t data[]; >+} __packed; >+ >+struct mbim_cid_packet_service { >+#define MBIM_PKTSERVICE_ACTION_ATTACH 0 >+#define MBIM_PKTSERVICE_ACTION_DETACH 1 >+ uint32_t action; >+} __packed; >+ >+struct mbim_cid_packet_service_info { >+ uint32_t nwerror; >+ >+#define MBIM_PKTSERVICE_STATE_UNKNOWN 0 >+#define MBIM_PKTSERVICE_STATE_ATTACHING 1 >+#define MBIM_PKTSERVICE_STATE_ATTACHED 2 >+#define MBIM_PKTSERVICE_STATE_DETACHING 3 >+#define MBIM_PKTSERVICE_STATE_DETACHED 4 >+ uint32_t state; >+ >+ uint32_t highest_dataclass; >+ uint64_t uplink_speed; >+ uint64_t downlink_speed; >+} __packed; >+ >+struct mbim_cid_signal_state { >+ uint32_t rssi; >+ uint32_t err_rate; >+ uint32_t ss_intvl; >+ uint32_t rssi_thr; >+ uint32_t err_thr; >+} __packed; >+ >+struct mbim_cid_connect { >+ uint32_t sessionid; >+ >+#define MBIM_CONNECT_DEACTIVATE 0 >+#define MBIM_CONNECT_ACTIVATE 1 >+ uint32_t command; >+ >+#define MBIM_ACCESS_MAXLEN 200 >+ uint32_t access_offs; >+ uint32_t access_size; >+ >+#define MBIM_USER_MAXLEN 510 >+ uint32_t user_offs; >+ uint32_t user_size; >+ >+#define MBIM_PASSWD_MAXLEN 510 >+ uint32_t passwd_offs; >+ uint32_t passwd_size; >+ >+#define MBIM_COMPRESSION_NONE 0 >+#define MBIM_COMPRESSION_ENABLE 1 >+ uint32_t compression; >+ >+#define MBIM_AUTHPROT_NONE 0 >+#define MBIM_AUTHPROT_PAP 1 >+#define MBIM_AUTHPROT_CHAP 2 >+#define MBIM_AUTHPROT_MSCHAP 3 >+ uint32_t authprot; >+ >+#define MBIM_CONTEXT_IPTYPE_DEFAULT 0 >+#define MBIM_CONTEXT_IPTYPE_IPV4 1 >+#define MBIM_CONTEXT_IPTYPE_IPV6 2 >+#define MBIM_CONTEXT_IPTYPE_IPV4V6 3 >+#define MBIM_CONTEXT_IPTYPE_IPV4ANDV6 4 >+ uint32_t iptype; >+ >+ uint8_t context[MBIM_UUID_LEN]; >+ >+ uint8_t data[MBIM_ACCESS_MAXLEN + MBIM_USER_MAXLEN + >+ MBIM_PASSWD_MAXLEN]; >+ >+} __packed; >+ >+struct mbim_cid_connect_info { >+ uint32_t sessionid; >+ >+#define MBIM_ACTIVATION_STATE_UNKNOWN 0 >+#define MBIM_ACTIVATION_STATE_ACTIVATED 1 >+#define MBIM_ACTIVATION_STATE_ACTIVATING 2 >+#define MBIM_ACTIVATION_STATE_DEACTIVATED 3 >+#define MBIM_ACTIVATION_STATE_DEACTIVATING 4 >+ uint32_t activation; >+ >+ uint32_t voice; >+ uint32_t iptype; >+ uint8_t context[MBIM_UUID_LEN]; >+ uint32_t nwerror; >+} __packed; >+ >+struct mbim_cid_ipv4_element { >+ uint32_t prefixlen; >+ uint32_t addr; >+} __packed; >+ >+struct mbim_cid_ipv6_element { >+ uint32_t prefixlen; >+ uint8_t addr[16]; >+} __packed; >+ >+struct mbim_cid_ip_configuration_info { >+ uint32_t sessionid; >+ >+#define MBIM_IPCONF_HAS_ADDRINFO 0x0001 >+#define MBIM_IPCONF_HAS_GWINFO 0x0002 >+#define MBIM_IPCONF_HAS_DNSINFO 0x0004 >+#define MBIM_IPCONF_HAS_MTUINFO 0x0008 >+ uint32_t ipv4_available; >+ uint32_t ipv6_available; >+ >+ uint32_t ipv4_naddr; >+ uint32_t ipv4_addroffs; >+ uint32_t ipv6_naddr; >+ uint32_t ipv6_addroffs; >+ >+ uint32_t ipv4_gwoffs; >+ uint32_t ipv6_gwoffs; >+ >+ uint32_t ipv4_ndnssrv; >+ uint32_t ipv4_dnssrvoffs; >+ uint32_t ipv6_ndnssrv; >+ uint32_t ipv6_dnssrvoffs; >+ >+ uint32_t ipv4_mtu; >+ uint32_t ipv6_mtu; >+ >+ uint32_t data[]; >+} __packed; >+ >+struct mbim_cid_packet_statistics_info { >+ uint32_t in_discards; >+ uint32_t in_errors; >+ uint64_t in_octets; >+ uint64_t in_packets; >+ uint64_t out_octets; >+ uint64_t out_packets; >+ uint32_t out_errors; >+ uint32_t out_discards; >+} __packed; >+ >+ >+#ifdef _KERNEL >+ >+struct mbim_descriptor { >+ uByte bLength; >+ uByte bDescriptorType; >+ uByte bDescriptorSubtype; >+#define MBIM_VER_MAJOR(v) (((v) >> 8) & 0x0f) >+#define MBIM_VER_MINOR(v) ((v) & 0x0f) >+ uWord bcdMBIMVersion; >+ uWord wMaxControlMessage; >+ uByte bNumberFilters; >+ uByte bMaxFilterSize; >+ uWord wMaxSegmentSize; >+ uByte bmNetworkCapabilities; >+} __packed; >+ >+/* >+ * NCM Parameters >+ */ >+#define NCM_GET_NTB_PARAMETERS 0x80 >+ >+struct ncm_ntb_parameters { >+ uWord wLength; >+ uWord bmNtbFormatsSupported; >+#define NCM_FORMAT_NTB16 0x0001 >+#define NCM_FORMAT_NTB32 0x0002 >+ uDWord dwNtbInMaxSize; >+ uWord wNdpInDivisor; >+ uWord wNdpInPayloadRemainder; >+ uWord wNdpInAlignment; >+ uWord wReserved1; >+ uDWord dwNtbOutMaxSize; >+ uWord wNdpOutDivisor; >+ uWord wNdpOutPayloadRemainder; >+ uWord wNdpOutAlignment; >+ uWord wNtbOutMaxDatagrams; >+} __packed; >+ >+/* >+ * NCM Encoding >+ */ >+#define MBIM_HDR16_LEN \ >+ (sizeof(struct ncm_header16) + sizeof(struct ncm_pointer16)) >+#define MBIM_HDR32_LEN \ >+ (sizeof(struct ncm_header32) + sizeof(struct ncm_pointer32)) >+ >+struct ncm_header16 { >+#define NCM_HDR16_SIG 0x484d434e >+ uDWord dwSignature; >+ uWord wHeaderLength; >+ uWord wSequence; >+ uWord wBlockLength; >+ uWord wNdpIndex; >+} __packed; >+ >+struct ncm_header32 { >+#define NCM_HDR32_SIG 0x686d636e >+ uDWord dwSignature; >+ uWord wHeaderLength; >+ uWord wSequence; >+ uDWord dwBlockLength; >+ uDWord dwNdpIndex; >+} __packed; >+ >+ >+#define MBIM_NCM_NTH_SIDSHIFT 24 >+#define MBIM_NCM_NTH_GETSID(s) (((s) > MBIM_NCM_NTH_SIDSHIFT) & 0xff) >+ >+struct ncm_pointer16_dgram { >+ uWord wDatagramIndex; >+ uWord wDatagramLen; >+} __packed; >+ >+struct ncm_pointer16 { >+#define MBIM_NCM_NTH16_IPS 0x00535049 >+#define MBIM_NCM_NTH16_ISISG(s) (((s) & 0x00ffffff) == MBIM_NCM_NTH16_IPS) >+#define MBIM_NCM_NTH16_SIG(s) \ >+ ((((s) & 0xff) << MBIM_NCM_NTH_SIDSHIFT) | MBIM_NCM_NTH16_IPS) >+ uDWord dwSignature; >+ uWord wLength; >+ uWord wNextNdpIndex; >+ >+ /* Minimum is two datagrams, but can be more */ >+ struct ncm_pointer16_dgram dgram[2]; >+} __packed; >+ >+struct ncm_pointer32_dgram { >+ uDWord dwDatagramIndex; >+ uDWord dwDatagramLen; >+} __packed; >+ >+struct ncm_pointer32 { >+#define MBIM_NCM_NTH32_IPS 0x00737069 >+#define MBIM_NCM_NTH32_ISISG(s) \ >+ (((s) & 0x00ffffff) == MBIM_NCM_NTH32_IPS) >+#define MBIM_NCM_NTH32_SIG(s) \ >+ ((((s) & 0xff) << MBIM_NCM_NTH_SIDSHIFT) | MBIM_NCM_NTH32_IPS) >+ uDWord dwSignature; >+ uWord wLength; >+ uWord wReserved6; >+ uDWord dwNextNdpIndex; >+ uDWord dwReserved12; >+ >+ /* Minimum is two datagrams, but can be more */ >+ struct ncm_pointer32_dgram dgram[2]; >+} __packed; >+ >+#endif /* _KERNEL */ >+ >+#endif /* _MBIM_H_ */ >diff --git a/sys/modules/usb/Makefile b/sys/modules/usb/Makefile >index 2d166a95dc03..ac483458ea2a 100644 >--- a/sys/modules/usb/Makefile >+++ b/sys/modules/usb/Makefile >@@ -49,7 +49,7 @@ SUBDIR += ${_rum} ${_run} ${_runfw} ${_uath} upgt usie ural ${_zyd} ${_urtw} > SUBDIR += atp cfumass uhid uhid_snes ukbd ums udbp uep wmt wsp ugold uled \ > usbhid > SUBDIR += ucom u3g uark ubsa ubser uchcom ucycom ufoma uftdi ugensa uipaq ulpt \ >- umct umcs umodem umoscom uplcom uslcom uvisor uvscom >+ umb umct umcs umodem umoscom uplcom uslcom uvisor uvscom > SUBDIR += i2ctinyusb > SUBDIR += cp2112 > SUBDIR += udl >diff --git a/sys/modules/usb/umb/Makefile b/sys/modules/usb/umb/Makefile >new file mode 100644 >index 000000000000..c17592d94dfb >--- /dev/null >+++ b/sys/modules/usb/umb/Makefile >@@ -0,0 +1,35 @@ >+# >+# >+# Copyright (c) 2008 Hans Petter Selasky. All rights reserved. >+# >+# Redistribution and use in source and binary forms, with or without >+# modification, are permitted provided that the following conditions >+# are met: >+# 1. Redistributions of source code must retain the above copyright >+# notice, this list of conditions and the following disclaimer. >+# 2. Redistributions in binary form must reproduce the above copyright >+# notice, this list of conditions and the following disclaimer in the >+# documentation and/or other materials provided with the distribution. >+# >+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND >+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE >+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE >+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE >+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL >+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS >+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) >+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT >+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY >+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF >+# SUCH DAMAGE. >+# >+ >+S= ${SRCTOP}/sys >+ >+.PATH: $S/dev/usb/net >+ >+KMOD= umb >+SRCS= opt_bus.h opt_usb.h device_if.h bus_if.h usb_if.h usbdevs.h \ >+ if_umb.c >+ >+.include <bsd.kmod.mk> >diff --git a/sys/net/if_media.c b/sys/net/if_media.c >index 0cc88a0929e9..8988d8a2bcf1 100644 >--- a/sys/net/if_media.c >+++ b/sys/net/if_media.c >@@ -398,6 +398,12 @@ static const struct ifmedia_description ifm_subtype_atm_descriptions[] = > static const struct ifmedia_description ifm_subtype_atm_option_descriptions[] = > IFM_SUBTYPE_ATM_OPTION_DESCRIPTIONS; > >+static const struct ifmedia_description ifm_subtype_mbim_descriptions[] = >+ IFM_SUBTYPE_MBIM_DESCRIPTIONS; >+ >+static const struct ifmedia_description ifm_subtype_mbim_option_descriptions[] = >+ IFM_SUBTYPE_MBIM_OPTION_DESCRIPTIONS; >+ > static const struct ifmedia_description ifm_subtype_shared_descriptions[] = > IFM_SUBTYPE_SHARED_DESCRIPTIONS; > >@@ -427,6 +433,11 @@ static const struct ifmedia_type_to_subtype ifmedia_types_to_subtypes[] = { > &ifm_subtype_atm_option_descriptions[0], > NULL, > }, >+ { >+ &ifm_subtype_mbim_descriptions[0], >+ &ifm_subtype_mbim_option_descriptions[0], >+ NULL, >+ }, > }; > > /* >diff --git a/sys/net/if_media.h b/sys/net/if_media.h >index c03a4c87b355..2812ca2f4ada 100644 >--- a/sys/net/if_media.h >+++ b/sys/net/if_media.h >@@ -338,6 +338,18 @@ uint64_t ifmedia_baudrate(int); > #define IFM_ATM_NOSCRAMB 0x00000200 /* no scrambling */ > #define IFM_ATM_UNASSIGNED 0x00000400 /* unassigned cells */ > >+/* >+ * MBIM >+ */ >+#define IFM_MBIM 0x00000100 >+#define IFM_MBIM_UNKNOWN 3 >+#define IFM_MBIM_GPRS 4 >+#define IFM_MBIM_EDGE 5 >+#define IFM_MBIM_UMTS 6 >+#define IFM_MBIM_HSDPA 7 >+#define IFM_MBIM_HSUPA 8 >+#define IFM_MBIM_LTE 9 >+ > /* > * Shared media sub-types > */ >@@ -425,6 +437,7 @@ struct ifmedia_description { > { IFM_ETHER, "Ethernet" }, \ > { IFM_IEEE80211, "IEEE 802.11 Wireless Ethernet" }, \ > { IFM_ATM, "ATM" }, \ >+ { IFM_MBIM, "MBIM" }, \ > { 0, NULL }, \ > } > >@@ -728,6 +741,31 @@ struct ifmedia_description { > { 0, NULL }, \ > } > >+#define IFM_SUBTYPE_MBIM_DESCRIPTIONS { \ >+ { IFM_MBIM_UNKNOWN, "Unknown" }, \ >+ { IFM_MBIM_GPRS, "GPRS" }, \ >+ { IFM_MBIM_EDGE, "EDGE" }, \ >+ { IFM_MBIM_UMTS, "UMTS" }, \ >+ { IFM_MBIM_HSDPA, "HSDPA" }, \ >+ { IFM_MBIM_HSUPA, "HSUPA" }, \ >+ { IFM_MBIM_LTE, "LTE" }, \ >+} >+ >+#define IFM_SUBTYPE_MBIM_ALIASES { \ >+ { IFM_MBIM_UNKNOWN, "UNKNOWN" }, \ >+ { IFM_MBIM_GPRS, "2G" }, \ >+ { IFM_MBIM_EDGE, "2.5G" }, \ >+ { IFM_MBIM_UMTS, "3G" }, \ >+ { IFM_MBIM_HSDPA, "3G" }, \ >+ { IFM_MBIM_HSUPA, "3G" }, \ >+ { IFM_MBIM_LTE, "4G" }, \ >+ { 0, NULL }, \ >+} >+ >+#define IFM_SUBTYPE_MBIM_OPTION_DESCRIPTIONS { \ >+ { 0, NULL }, \ >+} >+ > #define IFM_SUBTYPE_SHARED_DESCRIPTIONS { \ > { IFM_AUTO, "autoselect" }, \ > { IFM_MANUAL, "manual" }, \ >@@ -932,6 +970,8 @@ struct ifmedia_status_description { > { "no network", "active" } }, \ > { IFM_ATM, IFM_AVALID, IFM_ACTIVE, \ > { "no network", "active" } }, \ >+ { IFM_MBIM, IFM_AVALID, IFM_ACTIVE, \ >+ { "no network", "active" } }, \ > { 0, 0, 0, \ > { NULL, NULL } } \ > } >diff --git a/sys/net/if_types.h b/sys/net/if_types.h >index b0692af69308..4cb29fd51ebd 100644 >--- a/sys/net/if_types.h >+++ b/sys/net/if_types.h >@@ -254,6 +254,7 @@ typedef enum { > IFT_PFLOG = 0xf6, /* PF packet filter logging */ > IFT_PFSYNC = 0xf7, /* PF packet filter synchronization */ > IFT_WIREGUARD = 0xf8, /* WireGuard tunnel */ >+ IFT_MBIM = 0xf9 /* Mobile Broadband Interface Model */ > } ifType; > > /* >diff --git a/sys/sys/sockio.h b/sys/sys/sockio.h >index 824b77e57a17..cf612079c2f3 100644 >--- a/sys/sys/sockio.h >+++ b/sys/sys/sockio.h >@@ -147,4 +147,8 @@ > #define SIOCSIFCAPNV _IOW('i', 155, struct ifreq) /* set IF features */ > #define SIOCGIFCAPNV _IOWR('i', 156, struct ifreq) /* get IF features */ > >+#define SIOCGUMBINFO _IOWR('i', 157, struct ifreq) /* get MBIM info */ >+#define SIOCSUMBPARAM _IOW('i', 158, struct ifreq) /* set MBIM param */ >+#define SIOCGUMBPARAM _IOWR('i', 159, struct ifreq) /* get MBIM param */ >+ > #endif /* !_SYS_SOCKIO_H_ */ >-- >2.46.2 >
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
Attachments on
bug 263783
:
233735
|
250309
|
251587
|
255103
|
255765
|
255811
| 255918