1*eb4fd8cdSElina Pasheva /* 2*eb4fd8cdSElina Pasheva * USB-to-WWAN Driver for Sierra Wireless modems 3*eb4fd8cdSElina Pasheva * 4*eb4fd8cdSElina Pasheva * Copyright (C) 2008, 2009, 2010 Paxton Smith, Matthew Safar, Rory Filer 5*eb4fd8cdSElina Pasheva * <linux@sierrawireless.com> 6*eb4fd8cdSElina Pasheva * 7*eb4fd8cdSElina Pasheva * Portions of this based on the cdc_ether driver by David Brownell (2003-2005) 8*eb4fd8cdSElina Pasheva * and Ole Andre Vadla Ravnas (ActiveSync) (2006). 9*eb4fd8cdSElina Pasheva * 10*eb4fd8cdSElina Pasheva * IMPORTANT DISCLAIMER: This driver is not commercially supported by 11*eb4fd8cdSElina Pasheva * Sierra Wireless. Use at your own risk. 12*eb4fd8cdSElina Pasheva * 13*eb4fd8cdSElina Pasheva * This program is free software; you can redistribute it and/or modify 14*eb4fd8cdSElina Pasheva * it under the terms of the GNU General Public License as published by 15*eb4fd8cdSElina Pasheva * the Free Software Foundation; either version 2 of the License, or 16*eb4fd8cdSElina Pasheva * (at your option) any later version. 17*eb4fd8cdSElina Pasheva * 18*eb4fd8cdSElina Pasheva * This program is distributed in the hope that it will be useful, 19*eb4fd8cdSElina Pasheva * but WITHOUT ANY WARRANTY; without even the implied warranty of 20*eb4fd8cdSElina Pasheva * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21*eb4fd8cdSElina Pasheva * GNU General Public License for more details. 22*eb4fd8cdSElina Pasheva * 23*eb4fd8cdSElina Pasheva * You should have received a copy of the GNU General Public License 24*eb4fd8cdSElina Pasheva * along with this program; if not, write to the Free Software 25*eb4fd8cdSElina Pasheva * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 26*eb4fd8cdSElina Pasheva */ 27*eb4fd8cdSElina Pasheva 28*eb4fd8cdSElina Pasheva #define DRIVER_VERSION "v.2.0" 29*eb4fd8cdSElina Pasheva #define DRIVER_AUTHOR "Paxton Smith, Matthew Safar, Rory Filer" 30*eb4fd8cdSElina Pasheva #define DRIVER_DESC "USB-to-WWAN Driver for Sierra Wireless modems" 31*eb4fd8cdSElina Pasheva static const char driver_name[] = "sierra_net"; 32*eb4fd8cdSElina Pasheva 33*eb4fd8cdSElina Pasheva /* if defined debug messages enabled */ 34*eb4fd8cdSElina Pasheva /*#define DEBUG*/ 35*eb4fd8cdSElina Pasheva 36*eb4fd8cdSElina Pasheva #include <linux/module.h> 37*eb4fd8cdSElina Pasheva #include <linux/etherdevice.h> 38*eb4fd8cdSElina Pasheva #include <linux/ethtool.h> 39*eb4fd8cdSElina Pasheva #include <linux/mii.h> 40*eb4fd8cdSElina Pasheva #include <linux/sched.h> 41*eb4fd8cdSElina Pasheva #include <linux/timer.h> 42*eb4fd8cdSElina Pasheva #include <linux/usb.h> 43*eb4fd8cdSElina Pasheva #include <linux/usb/cdc.h> 44*eb4fd8cdSElina Pasheva #include <net/ip.h> 45*eb4fd8cdSElina Pasheva #include <net/udp.h> 46*eb4fd8cdSElina Pasheva #include <asm/unaligned.h> 47*eb4fd8cdSElina Pasheva #include <linux/usb/usbnet.h> 48*eb4fd8cdSElina Pasheva 49*eb4fd8cdSElina Pasheva #define SWI_USB_REQUEST_GET_FW_ATTR 0x06 50*eb4fd8cdSElina Pasheva #define SWI_GET_FW_ATTR_MASK 0x08 51*eb4fd8cdSElina Pasheva 52*eb4fd8cdSElina Pasheva /* atomic counter partially included in MAC address to make sure 2 devices 53*eb4fd8cdSElina Pasheva * do not end up with the same MAC - concept breaks in case of > 255 ifaces 54*eb4fd8cdSElina Pasheva */ 55*eb4fd8cdSElina Pasheva static atomic_t iface_counter = ATOMIC_INIT(0); 56*eb4fd8cdSElina Pasheva 57*eb4fd8cdSElina Pasheva /* 58*eb4fd8cdSElina Pasheva * SYNC Timer Delay definition used to set the expiry time 59*eb4fd8cdSElina Pasheva */ 60*eb4fd8cdSElina Pasheva #define SIERRA_NET_SYNCDELAY (2*HZ) 61*eb4fd8cdSElina Pasheva 62*eb4fd8cdSElina Pasheva /* Max. MTU supported. The modem buffers are limited to 1500 */ 63*eb4fd8cdSElina Pasheva #define SIERRA_NET_MAX_SUPPORTED_MTU 1500 64*eb4fd8cdSElina Pasheva 65*eb4fd8cdSElina Pasheva /* The SIERRA_NET_USBCTL_BUF_LEN defines a buffer size allocated for control 66*eb4fd8cdSElina Pasheva * message reception ... and thus the max. received packet. 67*eb4fd8cdSElina Pasheva * (May be the cause for parse_hip returning -EINVAL) 68*eb4fd8cdSElina Pasheva */ 69*eb4fd8cdSElina Pasheva #define SIERRA_NET_USBCTL_BUF_LEN 1024 70*eb4fd8cdSElina Pasheva 71*eb4fd8cdSElina Pasheva /* list of interface numbers - used for constructing interface lists */ 72*eb4fd8cdSElina Pasheva struct sierra_net_iface_info { 73*eb4fd8cdSElina Pasheva const u32 infolen; /* number of interface numbers on list */ 74*eb4fd8cdSElina Pasheva const u8 *ifaceinfo; /* pointer to the array holding the numbers */ 75*eb4fd8cdSElina Pasheva }; 76*eb4fd8cdSElina Pasheva 77*eb4fd8cdSElina Pasheva struct sierra_net_info_data { 78*eb4fd8cdSElina Pasheva u16 rx_urb_size; 79*eb4fd8cdSElina Pasheva struct sierra_net_iface_info whitelist; 80*eb4fd8cdSElina Pasheva }; 81*eb4fd8cdSElina Pasheva 82*eb4fd8cdSElina Pasheva /* Private data structure */ 83*eb4fd8cdSElina Pasheva struct sierra_net_data { 84*eb4fd8cdSElina Pasheva 85*eb4fd8cdSElina Pasheva u8 ethr_hdr_tmpl[ETH_HLEN]; /* ethernet header template for rx'd pkts */ 86*eb4fd8cdSElina Pasheva 87*eb4fd8cdSElina Pasheva u16 link_up; /* air link up or down */ 88*eb4fd8cdSElina Pasheva u8 tx_hdr_template[4]; /* part of HIP hdr for tx'd packets */ 89*eb4fd8cdSElina Pasheva 90*eb4fd8cdSElina Pasheva u8 sync_msg[4]; /* SYNC message */ 91*eb4fd8cdSElina Pasheva u8 shdwn_msg[4]; /* Shutdown message */ 92*eb4fd8cdSElina Pasheva 93*eb4fd8cdSElina Pasheva /* Backpointer to the container */ 94*eb4fd8cdSElina Pasheva struct usbnet *usbnet; 95*eb4fd8cdSElina Pasheva 96*eb4fd8cdSElina Pasheva u8 ifnum; /* interface number */ 97*eb4fd8cdSElina Pasheva 98*eb4fd8cdSElina Pasheva /* Bit masks, must be a power of 2 */ 99*eb4fd8cdSElina Pasheva #define SIERRA_NET_EVENT_RESP_AVAIL 0x01 100*eb4fd8cdSElina Pasheva #define SIERRA_NET_TIMER_EXPIRY 0x02 101*eb4fd8cdSElina Pasheva unsigned long kevent_flags; 102*eb4fd8cdSElina Pasheva struct work_struct sierra_net_kevent; 103*eb4fd8cdSElina Pasheva struct timer_list sync_timer; /* For retrying SYNC sequence */ 104*eb4fd8cdSElina Pasheva }; 105*eb4fd8cdSElina Pasheva 106*eb4fd8cdSElina Pasheva struct param { 107*eb4fd8cdSElina Pasheva int is_present; 108*eb4fd8cdSElina Pasheva union { 109*eb4fd8cdSElina Pasheva void *ptr; 110*eb4fd8cdSElina Pasheva u32 dword; 111*eb4fd8cdSElina Pasheva u16 word; 112*eb4fd8cdSElina Pasheva u8 byte; 113*eb4fd8cdSElina Pasheva }; 114*eb4fd8cdSElina Pasheva }; 115*eb4fd8cdSElina Pasheva 116*eb4fd8cdSElina Pasheva /* HIP message type */ 117*eb4fd8cdSElina Pasheva #define SIERRA_NET_HIP_EXTENDEDID 0x7F 118*eb4fd8cdSElina Pasheva #define SIERRA_NET_HIP_HSYNC_ID 0x60 /* Modem -> host */ 119*eb4fd8cdSElina Pasheva #define SIERRA_NET_HIP_RESTART_ID 0x62 /* Modem -> host */ 120*eb4fd8cdSElina Pasheva #define SIERRA_NET_HIP_MSYNC_ID 0x20 /* Host -> modem */ 121*eb4fd8cdSElina Pasheva #define SIERRA_NET_HIP_SHUTD_ID 0x26 /* Host -> modem */ 122*eb4fd8cdSElina Pasheva 123*eb4fd8cdSElina Pasheva #define SIERRA_NET_HIP_EXT_IP_IN_ID 0x0202 124*eb4fd8cdSElina Pasheva #define SIERRA_NET_HIP_EXT_IP_OUT_ID 0x0002 125*eb4fd8cdSElina Pasheva 126*eb4fd8cdSElina Pasheva /* 3G UMTS Link Sense Indication definitions */ 127*eb4fd8cdSElina Pasheva #define SIERRA_NET_HIP_LSI_UMTSID 0x78 128*eb4fd8cdSElina Pasheva 129*eb4fd8cdSElina Pasheva /* Reverse Channel Grant Indication HIP message */ 130*eb4fd8cdSElina Pasheva #define SIERRA_NET_HIP_RCGI 0x64 131*eb4fd8cdSElina Pasheva 132*eb4fd8cdSElina Pasheva /* LSI Protocol types */ 133*eb4fd8cdSElina Pasheva #define SIERRA_NET_PROTOCOL_UMTS 0x01 134*eb4fd8cdSElina Pasheva /* LSI Coverage */ 135*eb4fd8cdSElina Pasheva #define SIERRA_NET_COVERAGE_NONE 0x00 136*eb4fd8cdSElina Pasheva #define SIERRA_NET_COVERAGE_NOPACKET 0x01 137*eb4fd8cdSElina Pasheva 138*eb4fd8cdSElina Pasheva /* LSI Session */ 139*eb4fd8cdSElina Pasheva #define SIERRA_NET_SESSION_IDLE 0x00 140*eb4fd8cdSElina Pasheva /* LSI Link types */ 141*eb4fd8cdSElina Pasheva #define SIERRA_NET_AS_LINK_TYPE_IPv4 0x00 142*eb4fd8cdSElina Pasheva 143*eb4fd8cdSElina Pasheva struct lsi_umts { 144*eb4fd8cdSElina Pasheva u8 protocol; 145*eb4fd8cdSElina Pasheva u8 unused1; 146*eb4fd8cdSElina Pasheva __be16 length; 147*eb4fd8cdSElina Pasheva /* eventually use a union for the rest - assume umts for now */ 148*eb4fd8cdSElina Pasheva u8 coverage; 149*eb4fd8cdSElina Pasheva u8 unused2[41]; 150*eb4fd8cdSElina Pasheva u8 session_state; 151*eb4fd8cdSElina Pasheva u8 unused3[33]; 152*eb4fd8cdSElina Pasheva u8 link_type; 153*eb4fd8cdSElina Pasheva u8 pdp_addr_len; /* NW-supplied PDP address len */ 154*eb4fd8cdSElina Pasheva u8 pdp_addr[16]; /* NW-supplied PDP address (bigendian)) */ 155*eb4fd8cdSElina Pasheva u8 unused4[23]; 156*eb4fd8cdSElina Pasheva u8 dns1_addr_len; /* NW-supplied 1st DNS address len (bigendian) */ 157*eb4fd8cdSElina Pasheva u8 dns1_addr[16]; /* NW-supplied 1st DNS address */ 158*eb4fd8cdSElina Pasheva u8 dns2_addr_len; /* NW-supplied 2nd DNS address len */ 159*eb4fd8cdSElina Pasheva u8 dns2_addr[16]; /* NW-supplied 2nd DNS address (bigendian)*/ 160*eb4fd8cdSElina Pasheva u8 wins1_addr_len; /* NW-supplied 1st Wins address len */ 161*eb4fd8cdSElina Pasheva u8 wins1_addr[16]; /* NW-supplied 1st Wins address (bigendian)*/ 162*eb4fd8cdSElina Pasheva u8 wins2_addr_len; /* NW-supplied 2nd Wins address len */ 163*eb4fd8cdSElina Pasheva u8 wins2_addr[16]; /* NW-supplied 2nd Wins address (bigendian) */ 164*eb4fd8cdSElina Pasheva u8 unused5[4]; 165*eb4fd8cdSElina Pasheva u8 gw_addr_len; /* NW-supplied GW address len */ 166*eb4fd8cdSElina Pasheva u8 gw_addr[16]; /* NW-supplied GW address (bigendian) */ 167*eb4fd8cdSElina Pasheva u8 reserved[8]; 168*eb4fd8cdSElina Pasheva } __attribute__ ((packed)); 169*eb4fd8cdSElina Pasheva 170*eb4fd8cdSElina Pasheva #define SIERRA_NET_LSI_COMMON_LEN 4 171*eb4fd8cdSElina Pasheva #define SIERRA_NET_LSI_UMTS_LEN (sizeof(struct lsi_umts)) 172*eb4fd8cdSElina Pasheva #define SIERRA_NET_LSI_UMTS_STATUS_LEN \ 173*eb4fd8cdSElina Pasheva (SIERRA_NET_LSI_UMTS_LEN - SIERRA_NET_LSI_COMMON_LEN) 174*eb4fd8cdSElina Pasheva 175*eb4fd8cdSElina Pasheva /* Forward definitions */ 176*eb4fd8cdSElina Pasheva static void sierra_sync_timer(unsigned long syncdata); 177*eb4fd8cdSElina Pasheva static int sierra_net_change_mtu(struct net_device *net, int new_mtu); 178*eb4fd8cdSElina Pasheva 179*eb4fd8cdSElina Pasheva /* Our own net device operations structure */ 180*eb4fd8cdSElina Pasheva static const struct net_device_ops sierra_net_device_ops = { 181*eb4fd8cdSElina Pasheva .ndo_open = usbnet_open, 182*eb4fd8cdSElina Pasheva .ndo_stop = usbnet_stop, 183*eb4fd8cdSElina Pasheva .ndo_start_xmit = usbnet_start_xmit, 184*eb4fd8cdSElina Pasheva .ndo_tx_timeout = usbnet_tx_timeout, 185*eb4fd8cdSElina Pasheva .ndo_change_mtu = sierra_net_change_mtu, 186*eb4fd8cdSElina Pasheva .ndo_set_mac_address = eth_mac_addr, 187*eb4fd8cdSElina Pasheva .ndo_validate_addr = eth_validate_addr, 188*eb4fd8cdSElina Pasheva }; 189*eb4fd8cdSElina Pasheva 190*eb4fd8cdSElina Pasheva /* get private data associated with passed in usbnet device */ 191*eb4fd8cdSElina Pasheva static inline struct sierra_net_data *sierra_net_get_private(struct usbnet *dev) 192*eb4fd8cdSElina Pasheva { 193*eb4fd8cdSElina Pasheva return (struct sierra_net_data *)dev->data[0]; 194*eb4fd8cdSElina Pasheva } 195*eb4fd8cdSElina Pasheva 196*eb4fd8cdSElina Pasheva /* set private data associated with passed in usbnet device */ 197*eb4fd8cdSElina Pasheva static inline void sierra_net_set_private(struct usbnet *dev, 198*eb4fd8cdSElina Pasheva struct sierra_net_data *priv) 199*eb4fd8cdSElina Pasheva { 200*eb4fd8cdSElina Pasheva dev->data[0] = (unsigned long)priv; 201*eb4fd8cdSElina Pasheva } 202*eb4fd8cdSElina Pasheva 203*eb4fd8cdSElina Pasheva /* is packet IPv4 */ 204*eb4fd8cdSElina Pasheva static inline int is_ip(struct sk_buff *skb) 205*eb4fd8cdSElina Pasheva { 206*eb4fd8cdSElina Pasheva return (skb->protocol == cpu_to_be16(ETH_P_IP)); 207*eb4fd8cdSElina Pasheva } 208*eb4fd8cdSElina Pasheva 209*eb4fd8cdSElina Pasheva /* 210*eb4fd8cdSElina Pasheva * check passed in packet and make sure that: 211*eb4fd8cdSElina Pasheva * - it is linear (no scatter/gather) 212*eb4fd8cdSElina Pasheva * - it is ethernet (mac_header properly set) 213*eb4fd8cdSElina Pasheva */ 214*eb4fd8cdSElina Pasheva static int check_ethip_packet(struct sk_buff *skb, struct usbnet *dev) 215*eb4fd8cdSElina Pasheva { 216*eb4fd8cdSElina Pasheva skb_reset_mac_header(skb); /* ethernet header */ 217*eb4fd8cdSElina Pasheva 218*eb4fd8cdSElina Pasheva if (skb_is_nonlinear(skb)) { 219*eb4fd8cdSElina Pasheva netdev_err(dev->net, "Non linear buffer-dropping\n"); 220*eb4fd8cdSElina Pasheva return 0; 221*eb4fd8cdSElina Pasheva } 222*eb4fd8cdSElina Pasheva 223*eb4fd8cdSElina Pasheva if (!pskb_may_pull(skb, ETH_HLEN)) 224*eb4fd8cdSElina Pasheva return 0; 225*eb4fd8cdSElina Pasheva skb->protocol = eth_hdr(skb)->h_proto; 226*eb4fd8cdSElina Pasheva 227*eb4fd8cdSElina Pasheva return 1; 228*eb4fd8cdSElina Pasheva } 229*eb4fd8cdSElina Pasheva 230*eb4fd8cdSElina Pasheva static const u8 *save16bit(struct param *p, const u8 *datap) 231*eb4fd8cdSElina Pasheva { 232*eb4fd8cdSElina Pasheva p->is_present = 1; 233*eb4fd8cdSElina Pasheva p->word = get_unaligned_be16(datap); 234*eb4fd8cdSElina Pasheva return datap + sizeof(p->word); 235*eb4fd8cdSElina Pasheva } 236*eb4fd8cdSElina Pasheva 237*eb4fd8cdSElina Pasheva static const u8 *save8bit(struct param *p, const u8 *datap) 238*eb4fd8cdSElina Pasheva { 239*eb4fd8cdSElina Pasheva p->is_present = 1; 240*eb4fd8cdSElina Pasheva p->byte = *datap; 241*eb4fd8cdSElina Pasheva return datap + sizeof(p->byte); 242*eb4fd8cdSElina Pasheva } 243*eb4fd8cdSElina Pasheva 244*eb4fd8cdSElina Pasheva /*----------------------------------------------------------------------------* 245*eb4fd8cdSElina Pasheva * BEGIN HIP * 246*eb4fd8cdSElina Pasheva *----------------------------------------------------------------------------*/ 247*eb4fd8cdSElina Pasheva /* HIP header */ 248*eb4fd8cdSElina Pasheva #define SIERRA_NET_HIP_HDR_LEN 4 249*eb4fd8cdSElina Pasheva /* Extended HIP header */ 250*eb4fd8cdSElina Pasheva #define SIERRA_NET_HIP_EXT_HDR_LEN 6 251*eb4fd8cdSElina Pasheva 252*eb4fd8cdSElina Pasheva struct hip_hdr { 253*eb4fd8cdSElina Pasheva int hdrlen; 254*eb4fd8cdSElina Pasheva struct param payload_len; 255*eb4fd8cdSElina Pasheva struct param msgid; 256*eb4fd8cdSElina Pasheva struct param msgspecific; 257*eb4fd8cdSElina Pasheva struct param extmsgid; 258*eb4fd8cdSElina Pasheva }; 259*eb4fd8cdSElina Pasheva 260*eb4fd8cdSElina Pasheva static int parse_hip(const u8 *buf, const u32 buflen, struct hip_hdr *hh) 261*eb4fd8cdSElina Pasheva { 262*eb4fd8cdSElina Pasheva const u8 *curp = buf; 263*eb4fd8cdSElina Pasheva int padded; 264*eb4fd8cdSElina Pasheva 265*eb4fd8cdSElina Pasheva if (buflen < SIERRA_NET_HIP_HDR_LEN) 266*eb4fd8cdSElina Pasheva return -EPROTO; 267*eb4fd8cdSElina Pasheva 268*eb4fd8cdSElina Pasheva curp = save16bit(&hh->payload_len, curp); 269*eb4fd8cdSElina Pasheva curp = save8bit(&hh->msgid, curp); 270*eb4fd8cdSElina Pasheva curp = save8bit(&hh->msgspecific, curp); 271*eb4fd8cdSElina Pasheva 272*eb4fd8cdSElina Pasheva padded = hh->msgid.byte & 0x80; 273*eb4fd8cdSElina Pasheva hh->msgid.byte &= 0x7F; /* 7 bits */ 274*eb4fd8cdSElina Pasheva 275*eb4fd8cdSElina Pasheva hh->extmsgid.is_present = (hh->msgid.byte == SIERRA_NET_HIP_EXTENDEDID); 276*eb4fd8cdSElina Pasheva if (hh->extmsgid.is_present) { 277*eb4fd8cdSElina Pasheva if (buflen < SIERRA_NET_HIP_EXT_HDR_LEN) 278*eb4fd8cdSElina Pasheva return -EPROTO; 279*eb4fd8cdSElina Pasheva 280*eb4fd8cdSElina Pasheva hh->payload_len.word &= 0x3FFF; /* 14 bits */ 281*eb4fd8cdSElina Pasheva 282*eb4fd8cdSElina Pasheva curp = save16bit(&hh->extmsgid, curp); 283*eb4fd8cdSElina Pasheva hh->extmsgid.word &= 0x03FF; /* 10 bits */ 284*eb4fd8cdSElina Pasheva 285*eb4fd8cdSElina Pasheva hh->hdrlen = SIERRA_NET_HIP_EXT_HDR_LEN; 286*eb4fd8cdSElina Pasheva } else { 287*eb4fd8cdSElina Pasheva hh->payload_len.word &= 0x07FF; /* 11 bits */ 288*eb4fd8cdSElina Pasheva hh->hdrlen = SIERRA_NET_HIP_HDR_LEN; 289*eb4fd8cdSElina Pasheva } 290*eb4fd8cdSElina Pasheva 291*eb4fd8cdSElina Pasheva if (padded) { 292*eb4fd8cdSElina Pasheva hh->hdrlen++; 293*eb4fd8cdSElina Pasheva hh->payload_len.word--; 294*eb4fd8cdSElina Pasheva } 295*eb4fd8cdSElina Pasheva 296*eb4fd8cdSElina Pasheva /* if real packet shorter than the claimed length */ 297*eb4fd8cdSElina Pasheva if (buflen < (hh->hdrlen + hh->payload_len.word)) 298*eb4fd8cdSElina Pasheva return -EINVAL; 299*eb4fd8cdSElina Pasheva 300*eb4fd8cdSElina Pasheva return 0; 301*eb4fd8cdSElina Pasheva } 302*eb4fd8cdSElina Pasheva 303*eb4fd8cdSElina Pasheva static void build_hip(u8 *buf, const u16 payloadlen, 304*eb4fd8cdSElina Pasheva struct sierra_net_data *priv) 305*eb4fd8cdSElina Pasheva { 306*eb4fd8cdSElina Pasheva /* the following doesn't have the full functionality. We 307*eb4fd8cdSElina Pasheva * currently build only one kind of header, so it is faster this way 308*eb4fd8cdSElina Pasheva */ 309*eb4fd8cdSElina Pasheva put_unaligned_be16(payloadlen, buf); 310*eb4fd8cdSElina Pasheva memcpy(buf+2, priv->tx_hdr_template, sizeof(priv->tx_hdr_template)); 311*eb4fd8cdSElina Pasheva } 312*eb4fd8cdSElina Pasheva /*----------------------------------------------------------------------------* 313*eb4fd8cdSElina Pasheva * END HIP * 314*eb4fd8cdSElina Pasheva *----------------------------------------------------------------------------*/ 315*eb4fd8cdSElina Pasheva 316*eb4fd8cdSElina Pasheva static int sierra_net_send_cmd(struct usbnet *dev, 317*eb4fd8cdSElina Pasheva u8 *cmd, int cmdlen, const char * cmd_name) 318*eb4fd8cdSElina Pasheva { 319*eb4fd8cdSElina Pasheva struct sierra_net_data *priv = sierra_net_get_private(dev); 320*eb4fd8cdSElina Pasheva int status; 321*eb4fd8cdSElina Pasheva 322*eb4fd8cdSElina Pasheva status = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0), 323*eb4fd8cdSElina Pasheva USB_CDC_SEND_ENCAPSULATED_COMMAND, 324*eb4fd8cdSElina Pasheva USB_DIR_OUT|USB_TYPE_CLASS|USB_RECIP_INTERFACE, 0, 325*eb4fd8cdSElina Pasheva priv->ifnum, cmd, cmdlen, USB_CTRL_SET_TIMEOUT); 326*eb4fd8cdSElina Pasheva 327*eb4fd8cdSElina Pasheva if (status != cmdlen && status != -ENODEV) 328*eb4fd8cdSElina Pasheva netdev_err(dev->net, "Submit %s failed %d\n", cmd_name, status); 329*eb4fd8cdSElina Pasheva 330*eb4fd8cdSElina Pasheva return status; 331*eb4fd8cdSElina Pasheva } 332*eb4fd8cdSElina Pasheva 333*eb4fd8cdSElina Pasheva static int sierra_net_send_sync(struct usbnet *dev) 334*eb4fd8cdSElina Pasheva { 335*eb4fd8cdSElina Pasheva int status; 336*eb4fd8cdSElina Pasheva struct sierra_net_data *priv = sierra_net_get_private(dev); 337*eb4fd8cdSElina Pasheva 338*eb4fd8cdSElina Pasheva dev_dbg(&dev->udev->dev, "%s", __func__); 339*eb4fd8cdSElina Pasheva 340*eb4fd8cdSElina Pasheva status = sierra_net_send_cmd(dev, priv->sync_msg, 341*eb4fd8cdSElina Pasheva sizeof(priv->sync_msg), "SYNC"); 342*eb4fd8cdSElina Pasheva 343*eb4fd8cdSElina Pasheva return status; 344*eb4fd8cdSElina Pasheva } 345*eb4fd8cdSElina Pasheva 346*eb4fd8cdSElina Pasheva static void sierra_net_set_ctx_index(struct sierra_net_data *priv, u8 ctx_ix) 347*eb4fd8cdSElina Pasheva { 348*eb4fd8cdSElina Pasheva dev_dbg(&(priv->usbnet->udev->dev), "%s %d", __func__, ctx_ix); 349*eb4fd8cdSElina Pasheva priv->tx_hdr_template[0] = 0x3F; 350*eb4fd8cdSElina Pasheva priv->tx_hdr_template[1] = ctx_ix; 351*eb4fd8cdSElina Pasheva *((u16 *)&priv->tx_hdr_template[2]) = 352*eb4fd8cdSElina Pasheva cpu_to_be16(SIERRA_NET_HIP_EXT_IP_OUT_ID); 353*eb4fd8cdSElina Pasheva } 354*eb4fd8cdSElina Pasheva 355*eb4fd8cdSElina Pasheva static inline int sierra_net_is_valid_addrlen(u8 len) 356*eb4fd8cdSElina Pasheva { 357*eb4fd8cdSElina Pasheva return (len == sizeof(struct in_addr)); 358*eb4fd8cdSElina Pasheva } 359*eb4fd8cdSElina Pasheva 360*eb4fd8cdSElina Pasheva static int sierra_net_parse_lsi(struct usbnet *dev, char *data, int datalen) 361*eb4fd8cdSElina Pasheva { 362*eb4fd8cdSElina Pasheva struct lsi_umts *lsi = (struct lsi_umts *)data; 363*eb4fd8cdSElina Pasheva 364*eb4fd8cdSElina Pasheva if (datalen < sizeof(struct lsi_umts)) { 365*eb4fd8cdSElina Pasheva netdev_err(dev->net, "%s: Data length %d, exp %Zu\n", 366*eb4fd8cdSElina Pasheva __func__, datalen, 367*eb4fd8cdSElina Pasheva sizeof(struct lsi_umts)); 368*eb4fd8cdSElina Pasheva return -1; 369*eb4fd8cdSElina Pasheva } 370*eb4fd8cdSElina Pasheva 371*eb4fd8cdSElina Pasheva if (lsi->length != cpu_to_be16(SIERRA_NET_LSI_UMTS_STATUS_LEN)) { 372*eb4fd8cdSElina Pasheva netdev_err(dev->net, "%s: LSI_UMTS_STATUS_LEN %d, exp %u\n", 373*eb4fd8cdSElina Pasheva __func__, be16_to_cpu(lsi->length), 374*eb4fd8cdSElina Pasheva (u32)SIERRA_NET_LSI_UMTS_STATUS_LEN); 375*eb4fd8cdSElina Pasheva return -1; 376*eb4fd8cdSElina Pasheva } 377*eb4fd8cdSElina Pasheva 378*eb4fd8cdSElina Pasheva /* Validate the protocol - only support UMTS for now */ 379*eb4fd8cdSElina Pasheva if (lsi->protocol != SIERRA_NET_PROTOCOL_UMTS) { 380*eb4fd8cdSElina Pasheva netdev_err(dev->net, "Protocol unsupported, 0x%02x\n", 381*eb4fd8cdSElina Pasheva lsi->protocol); 382*eb4fd8cdSElina Pasheva return -1; 383*eb4fd8cdSElina Pasheva } 384*eb4fd8cdSElina Pasheva 385*eb4fd8cdSElina Pasheva /* Validate the link type */ 386*eb4fd8cdSElina Pasheva if (lsi->link_type != SIERRA_NET_AS_LINK_TYPE_IPv4) { 387*eb4fd8cdSElina Pasheva netdev_err(dev->net, "Link type unsupported: 0x%02x\n", 388*eb4fd8cdSElina Pasheva lsi->link_type); 389*eb4fd8cdSElina Pasheva return -1; 390*eb4fd8cdSElina Pasheva } 391*eb4fd8cdSElina Pasheva 392*eb4fd8cdSElina Pasheva /* Validate the coverage */ 393*eb4fd8cdSElina Pasheva if (lsi->coverage == SIERRA_NET_COVERAGE_NONE 394*eb4fd8cdSElina Pasheva || lsi->coverage == SIERRA_NET_COVERAGE_NOPACKET) { 395*eb4fd8cdSElina Pasheva netdev_err(dev->net, "No coverage, 0x%02x\n", lsi->coverage); 396*eb4fd8cdSElina Pasheva return 0; 397*eb4fd8cdSElina Pasheva } 398*eb4fd8cdSElina Pasheva 399*eb4fd8cdSElina Pasheva /* Validate the session state */ 400*eb4fd8cdSElina Pasheva if (lsi->session_state == SIERRA_NET_SESSION_IDLE) { 401*eb4fd8cdSElina Pasheva netdev_err(dev->net, "Session idle, 0x%02x\n", 402*eb4fd8cdSElina Pasheva lsi->session_state); 403*eb4fd8cdSElina Pasheva return 0; 404*eb4fd8cdSElina Pasheva } 405*eb4fd8cdSElina Pasheva 406*eb4fd8cdSElina Pasheva /* Set link_sense true */ 407*eb4fd8cdSElina Pasheva return 1; 408*eb4fd8cdSElina Pasheva } 409*eb4fd8cdSElina Pasheva 410*eb4fd8cdSElina Pasheva static void sierra_net_handle_lsi(struct usbnet *dev, char *data, 411*eb4fd8cdSElina Pasheva struct hip_hdr *hh) 412*eb4fd8cdSElina Pasheva { 413*eb4fd8cdSElina Pasheva struct sierra_net_data *priv = sierra_net_get_private(dev); 414*eb4fd8cdSElina Pasheva int link_up; 415*eb4fd8cdSElina Pasheva 416*eb4fd8cdSElina Pasheva link_up = sierra_net_parse_lsi(dev, data + hh->hdrlen, 417*eb4fd8cdSElina Pasheva hh->payload_len.word); 418*eb4fd8cdSElina Pasheva if (link_up < 0) { 419*eb4fd8cdSElina Pasheva netdev_err(dev->net, "Invalid LSI\n"); 420*eb4fd8cdSElina Pasheva return; 421*eb4fd8cdSElina Pasheva } 422*eb4fd8cdSElina Pasheva if (link_up) { 423*eb4fd8cdSElina Pasheva sierra_net_set_ctx_index(priv, hh->msgspecific.byte); 424*eb4fd8cdSElina Pasheva priv->link_up = 1; 425*eb4fd8cdSElina Pasheva netif_carrier_on(dev->net); 426*eb4fd8cdSElina Pasheva } else { 427*eb4fd8cdSElina Pasheva priv->link_up = 0; 428*eb4fd8cdSElina Pasheva netif_carrier_off(dev->net); 429*eb4fd8cdSElina Pasheva } 430*eb4fd8cdSElina Pasheva } 431*eb4fd8cdSElina Pasheva 432*eb4fd8cdSElina Pasheva static void sierra_net_dosync(struct usbnet *dev) 433*eb4fd8cdSElina Pasheva { 434*eb4fd8cdSElina Pasheva int status; 435*eb4fd8cdSElina Pasheva struct sierra_net_data *priv = sierra_net_get_private(dev); 436*eb4fd8cdSElina Pasheva 437*eb4fd8cdSElina Pasheva dev_dbg(&dev->udev->dev, "%s", __func__); 438*eb4fd8cdSElina Pasheva 439*eb4fd8cdSElina Pasheva /* tell modem we are ready */ 440*eb4fd8cdSElina Pasheva status = sierra_net_send_sync(dev); 441*eb4fd8cdSElina Pasheva if (status < 0) 442*eb4fd8cdSElina Pasheva netdev_err(dev->net, 443*eb4fd8cdSElina Pasheva "Send SYNC failed, status %d\n", status); 444*eb4fd8cdSElina Pasheva status = sierra_net_send_sync(dev); 445*eb4fd8cdSElina Pasheva if (status < 0) 446*eb4fd8cdSElina Pasheva netdev_err(dev->net, 447*eb4fd8cdSElina Pasheva "Send SYNC failed, status %d\n", status); 448*eb4fd8cdSElina Pasheva 449*eb4fd8cdSElina Pasheva /* Now, start a timer and make sure we get the Restart Indication */ 450*eb4fd8cdSElina Pasheva priv->sync_timer.function = sierra_sync_timer; 451*eb4fd8cdSElina Pasheva priv->sync_timer.data = (unsigned long) dev; 452*eb4fd8cdSElina Pasheva priv->sync_timer.expires = jiffies + SIERRA_NET_SYNCDELAY; 453*eb4fd8cdSElina Pasheva add_timer(&priv->sync_timer); 454*eb4fd8cdSElina Pasheva } 455*eb4fd8cdSElina Pasheva 456*eb4fd8cdSElina Pasheva static void sierra_net_kevent(struct work_struct *work) 457*eb4fd8cdSElina Pasheva { 458*eb4fd8cdSElina Pasheva struct sierra_net_data *priv = 459*eb4fd8cdSElina Pasheva container_of(work, struct sierra_net_data, sierra_net_kevent); 460*eb4fd8cdSElina Pasheva struct usbnet *dev = priv->usbnet; 461*eb4fd8cdSElina Pasheva int len; 462*eb4fd8cdSElina Pasheva int err; 463*eb4fd8cdSElina Pasheva u8 *buf; 464*eb4fd8cdSElina Pasheva u8 ifnum; 465*eb4fd8cdSElina Pasheva 466*eb4fd8cdSElina Pasheva if (test_bit(SIERRA_NET_EVENT_RESP_AVAIL, &priv->kevent_flags)) { 467*eb4fd8cdSElina Pasheva clear_bit(SIERRA_NET_EVENT_RESP_AVAIL, &priv->kevent_flags); 468*eb4fd8cdSElina Pasheva 469*eb4fd8cdSElina Pasheva /* Query the modem for the LSI message */ 470*eb4fd8cdSElina Pasheva buf = kzalloc(SIERRA_NET_USBCTL_BUF_LEN, GFP_KERNEL); 471*eb4fd8cdSElina Pasheva if (!buf) { 472*eb4fd8cdSElina Pasheva netdev_err(dev->net, 473*eb4fd8cdSElina Pasheva "failed to allocate buf for LS msg\n"); 474*eb4fd8cdSElina Pasheva return; 475*eb4fd8cdSElina Pasheva } 476*eb4fd8cdSElina Pasheva ifnum = priv->ifnum; 477*eb4fd8cdSElina Pasheva len = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0), 478*eb4fd8cdSElina Pasheva USB_CDC_GET_ENCAPSULATED_RESPONSE, 479*eb4fd8cdSElina Pasheva USB_DIR_IN|USB_TYPE_CLASS|USB_RECIP_INTERFACE, 480*eb4fd8cdSElina Pasheva 0, ifnum, buf, SIERRA_NET_USBCTL_BUF_LEN, 481*eb4fd8cdSElina Pasheva USB_CTRL_SET_TIMEOUT); 482*eb4fd8cdSElina Pasheva 483*eb4fd8cdSElina Pasheva if (len < 0) { 484*eb4fd8cdSElina Pasheva netdev_err(dev->net, 485*eb4fd8cdSElina Pasheva "usb_control_msg failed, status %d\n", len); 486*eb4fd8cdSElina Pasheva } else { 487*eb4fd8cdSElina Pasheva struct hip_hdr hh; 488*eb4fd8cdSElina Pasheva 489*eb4fd8cdSElina Pasheva dev_dbg(&dev->udev->dev, "%s: Received status message," 490*eb4fd8cdSElina Pasheva " %04x bytes", __func__, len); 491*eb4fd8cdSElina Pasheva 492*eb4fd8cdSElina Pasheva err = parse_hip(buf, len, &hh); 493*eb4fd8cdSElina Pasheva if (err) { 494*eb4fd8cdSElina Pasheva netdev_err(dev->net, "%s: Bad packet," 495*eb4fd8cdSElina Pasheva " parse result %d\n", __func__, err); 496*eb4fd8cdSElina Pasheva kfree(buf); 497*eb4fd8cdSElina Pasheva return; 498*eb4fd8cdSElina Pasheva } 499*eb4fd8cdSElina Pasheva 500*eb4fd8cdSElina Pasheva /* Validate packet length */ 501*eb4fd8cdSElina Pasheva if (len != hh.hdrlen + hh.payload_len.word) { 502*eb4fd8cdSElina Pasheva netdev_err(dev->net, "%s: Bad packet, received" 503*eb4fd8cdSElina Pasheva " %d, expected %d\n", __func__, len, 504*eb4fd8cdSElina Pasheva hh.hdrlen + hh.payload_len.word); 505*eb4fd8cdSElina Pasheva kfree(buf); 506*eb4fd8cdSElina Pasheva return; 507*eb4fd8cdSElina Pasheva } 508*eb4fd8cdSElina Pasheva 509*eb4fd8cdSElina Pasheva /* Switch on received message types */ 510*eb4fd8cdSElina Pasheva switch (hh.msgid.byte) { 511*eb4fd8cdSElina Pasheva case SIERRA_NET_HIP_LSI_UMTSID: 512*eb4fd8cdSElina Pasheva dev_dbg(&dev->udev->dev, "LSI for ctx:%d", 513*eb4fd8cdSElina Pasheva hh.msgspecific.byte); 514*eb4fd8cdSElina Pasheva sierra_net_handle_lsi(dev, buf, &hh); 515*eb4fd8cdSElina Pasheva break; 516*eb4fd8cdSElina Pasheva case SIERRA_NET_HIP_RESTART_ID: 517*eb4fd8cdSElina Pasheva dev_dbg(&dev->udev->dev, "Restart reported: %d," 518*eb4fd8cdSElina Pasheva " stopping sync timer", 519*eb4fd8cdSElina Pasheva hh.msgspecific.byte); 520*eb4fd8cdSElina Pasheva /* Got sync resp - stop timer & clear mask */ 521*eb4fd8cdSElina Pasheva del_timer_sync(&priv->sync_timer); 522*eb4fd8cdSElina Pasheva clear_bit(SIERRA_NET_TIMER_EXPIRY, 523*eb4fd8cdSElina Pasheva &priv->kevent_flags); 524*eb4fd8cdSElina Pasheva break; 525*eb4fd8cdSElina Pasheva case SIERRA_NET_HIP_HSYNC_ID: 526*eb4fd8cdSElina Pasheva dev_dbg(&dev->udev->dev, "SYNC received"); 527*eb4fd8cdSElina Pasheva err = sierra_net_send_sync(dev); 528*eb4fd8cdSElina Pasheva if (err < 0) 529*eb4fd8cdSElina Pasheva netdev_err(dev->net, 530*eb4fd8cdSElina Pasheva "Send SYNC failed %d\n", err); 531*eb4fd8cdSElina Pasheva break; 532*eb4fd8cdSElina Pasheva case SIERRA_NET_HIP_EXTENDEDID: 533*eb4fd8cdSElina Pasheva netdev_err(dev->net, "Unrecognized HIP msg, " 534*eb4fd8cdSElina Pasheva "extmsgid 0x%04x\n", hh.extmsgid.word); 535*eb4fd8cdSElina Pasheva break; 536*eb4fd8cdSElina Pasheva case SIERRA_NET_HIP_RCGI: 537*eb4fd8cdSElina Pasheva /* Ignored */ 538*eb4fd8cdSElina Pasheva break; 539*eb4fd8cdSElina Pasheva default: 540*eb4fd8cdSElina Pasheva netdev_err(dev->net, "Unrecognized HIP msg, " 541*eb4fd8cdSElina Pasheva "msgid 0x%02x\n", hh.msgid.byte); 542*eb4fd8cdSElina Pasheva break; 543*eb4fd8cdSElina Pasheva } 544*eb4fd8cdSElina Pasheva } 545*eb4fd8cdSElina Pasheva kfree(buf); 546*eb4fd8cdSElina Pasheva } 547*eb4fd8cdSElina Pasheva /* The sync timer bit might be set */ 548*eb4fd8cdSElina Pasheva if (test_bit(SIERRA_NET_TIMER_EXPIRY, &priv->kevent_flags)) { 549*eb4fd8cdSElina Pasheva clear_bit(SIERRA_NET_TIMER_EXPIRY, &priv->kevent_flags); 550*eb4fd8cdSElina Pasheva dev_dbg(&dev->udev->dev, "Deferred sync timer expiry"); 551*eb4fd8cdSElina Pasheva sierra_net_dosync(priv->usbnet); 552*eb4fd8cdSElina Pasheva } 553*eb4fd8cdSElina Pasheva 554*eb4fd8cdSElina Pasheva if (priv->kevent_flags) 555*eb4fd8cdSElina Pasheva dev_dbg(&dev->udev->dev, "sierra_net_kevent done, " 556*eb4fd8cdSElina Pasheva "kevent_flags = 0x%lx", priv->kevent_flags); 557*eb4fd8cdSElina Pasheva } 558*eb4fd8cdSElina Pasheva 559*eb4fd8cdSElina Pasheva static void sierra_net_defer_kevent(struct usbnet *dev, int work) 560*eb4fd8cdSElina Pasheva { 561*eb4fd8cdSElina Pasheva struct sierra_net_data *priv = sierra_net_get_private(dev); 562*eb4fd8cdSElina Pasheva 563*eb4fd8cdSElina Pasheva set_bit(work, &priv->kevent_flags); 564*eb4fd8cdSElina Pasheva schedule_work(&priv->sierra_net_kevent); 565*eb4fd8cdSElina Pasheva } 566*eb4fd8cdSElina Pasheva 567*eb4fd8cdSElina Pasheva /* 568*eb4fd8cdSElina Pasheva * Sync Retransmit Timer Handler. On expiry, kick the work queue 569*eb4fd8cdSElina Pasheva */ 570*eb4fd8cdSElina Pasheva void sierra_sync_timer(unsigned long syncdata) 571*eb4fd8cdSElina Pasheva { 572*eb4fd8cdSElina Pasheva struct usbnet *dev = (struct usbnet *)syncdata; 573*eb4fd8cdSElina Pasheva 574*eb4fd8cdSElina Pasheva dev_dbg(&dev->udev->dev, "%s", __func__); 575*eb4fd8cdSElina Pasheva /* Kick the tasklet */ 576*eb4fd8cdSElina Pasheva sierra_net_defer_kevent(dev, SIERRA_NET_TIMER_EXPIRY); 577*eb4fd8cdSElina Pasheva } 578*eb4fd8cdSElina Pasheva 579*eb4fd8cdSElina Pasheva static void sierra_net_status(struct usbnet *dev, struct urb *urb) 580*eb4fd8cdSElina Pasheva { 581*eb4fd8cdSElina Pasheva struct usb_cdc_notification *event; 582*eb4fd8cdSElina Pasheva 583*eb4fd8cdSElina Pasheva dev_dbg(&dev->udev->dev, "%s", __func__); 584*eb4fd8cdSElina Pasheva 585*eb4fd8cdSElina Pasheva if (urb->actual_length < sizeof *event) 586*eb4fd8cdSElina Pasheva return; 587*eb4fd8cdSElina Pasheva 588*eb4fd8cdSElina Pasheva /* Add cases to handle other standard notifications. */ 589*eb4fd8cdSElina Pasheva event = urb->transfer_buffer; 590*eb4fd8cdSElina Pasheva switch (event->bNotificationType) { 591*eb4fd8cdSElina Pasheva case USB_CDC_NOTIFY_NETWORK_CONNECTION: 592*eb4fd8cdSElina Pasheva case USB_CDC_NOTIFY_SPEED_CHANGE: 593*eb4fd8cdSElina Pasheva /* USB 305 sends those */ 594*eb4fd8cdSElina Pasheva break; 595*eb4fd8cdSElina Pasheva case USB_CDC_NOTIFY_RESPONSE_AVAILABLE: 596*eb4fd8cdSElina Pasheva sierra_net_defer_kevent(dev, SIERRA_NET_EVENT_RESP_AVAIL); 597*eb4fd8cdSElina Pasheva break; 598*eb4fd8cdSElina Pasheva default: 599*eb4fd8cdSElina Pasheva netdev_err(dev->net, ": unexpected notification %02x!\n", 600*eb4fd8cdSElina Pasheva event->bNotificationType); 601*eb4fd8cdSElina Pasheva break; 602*eb4fd8cdSElina Pasheva } 603*eb4fd8cdSElina Pasheva } 604*eb4fd8cdSElina Pasheva 605*eb4fd8cdSElina Pasheva static void sierra_net_get_drvinfo(struct net_device *net, 606*eb4fd8cdSElina Pasheva struct ethtool_drvinfo *info) 607*eb4fd8cdSElina Pasheva { 608*eb4fd8cdSElina Pasheva /* Inherit standard device info */ 609*eb4fd8cdSElina Pasheva usbnet_get_drvinfo(net, info); 610*eb4fd8cdSElina Pasheva strncpy(info->driver, driver_name, sizeof info->driver); 611*eb4fd8cdSElina Pasheva strncpy(info->version, DRIVER_VERSION, sizeof info->version); 612*eb4fd8cdSElina Pasheva } 613*eb4fd8cdSElina Pasheva 614*eb4fd8cdSElina Pasheva static u32 sierra_net_get_link(struct net_device *net) 615*eb4fd8cdSElina Pasheva { 616*eb4fd8cdSElina Pasheva struct usbnet *dev = netdev_priv(net); 617*eb4fd8cdSElina Pasheva /* Report link is down whenever the interface is down */ 618*eb4fd8cdSElina Pasheva return sierra_net_get_private(dev)->link_up && netif_running(net); 619*eb4fd8cdSElina Pasheva } 620*eb4fd8cdSElina Pasheva 621*eb4fd8cdSElina Pasheva static struct ethtool_ops sierra_net_ethtool_ops = { 622*eb4fd8cdSElina Pasheva .get_drvinfo = sierra_net_get_drvinfo, 623*eb4fd8cdSElina Pasheva .get_link = sierra_net_get_link, 624*eb4fd8cdSElina Pasheva .get_msglevel = usbnet_get_msglevel, 625*eb4fd8cdSElina Pasheva .set_msglevel = usbnet_set_msglevel, 626*eb4fd8cdSElina Pasheva .get_settings = usbnet_get_settings, 627*eb4fd8cdSElina Pasheva .set_settings = usbnet_set_settings, 628*eb4fd8cdSElina Pasheva .nway_reset = usbnet_nway_reset, 629*eb4fd8cdSElina Pasheva }; 630*eb4fd8cdSElina Pasheva 631*eb4fd8cdSElina Pasheva /* MTU can not be more than 1500 bytes, enforce it. */ 632*eb4fd8cdSElina Pasheva static int sierra_net_change_mtu(struct net_device *net, int new_mtu) 633*eb4fd8cdSElina Pasheva { 634*eb4fd8cdSElina Pasheva if (new_mtu > SIERRA_NET_MAX_SUPPORTED_MTU) 635*eb4fd8cdSElina Pasheva return -EINVAL; 636*eb4fd8cdSElina Pasheva 637*eb4fd8cdSElina Pasheva return usbnet_change_mtu(net, new_mtu); 638*eb4fd8cdSElina Pasheva } 639*eb4fd8cdSElina Pasheva 640*eb4fd8cdSElina Pasheva static int is_whitelisted(const u8 ifnum, 641*eb4fd8cdSElina Pasheva const struct sierra_net_iface_info *whitelist) 642*eb4fd8cdSElina Pasheva { 643*eb4fd8cdSElina Pasheva if (whitelist) { 644*eb4fd8cdSElina Pasheva const u8 *list = whitelist->ifaceinfo; 645*eb4fd8cdSElina Pasheva int i; 646*eb4fd8cdSElina Pasheva 647*eb4fd8cdSElina Pasheva for (i = 0; i < whitelist->infolen; i++) { 648*eb4fd8cdSElina Pasheva if (list[i] == ifnum) 649*eb4fd8cdSElina Pasheva return 1; 650*eb4fd8cdSElina Pasheva } 651*eb4fd8cdSElina Pasheva } 652*eb4fd8cdSElina Pasheva return 0; 653*eb4fd8cdSElina Pasheva } 654*eb4fd8cdSElina Pasheva 655*eb4fd8cdSElina Pasheva static int sierra_net_get_fw_attr(struct usbnet *dev, u16 *datap) 656*eb4fd8cdSElina Pasheva { 657*eb4fd8cdSElina Pasheva int result = 0; 658*eb4fd8cdSElina Pasheva u16 *attrdata; 659*eb4fd8cdSElina Pasheva 660*eb4fd8cdSElina Pasheva attrdata = kmalloc(sizeof(*attrdata), GFP_KERNEL); 661*eb4fd8cdSElina Pasheva if (!attrdata) 662*eb4fd8cdSElina Pasheva return -ENOMEM; 663*eb4fd8cdSElina Pasheva 664*eb4fd8cdSElina Pasheva result = usb_control_msg( 665*eb4fd8cdSElina Pasheva dev->udev, 666*eb4fd8cdSElina Pasheva usb_rcvctrlpipe(dev->udev, 0), 667*eb4fd8cdSElina Pasheva /* _u8 vendor specific request */ 668*eb4fd8cdSElina Pasheva SWI_USB_REQUEST_GET_FW_ATTR, 669*eb4fd8cdSElina Pasheva USB_DIR_IN | USB_TYPE_VENDOR, /* __u8 request type */ 670*eb4fd8cdSElina Pasheva 0x0000, /* __u16 value not used */ 671*eb4fd8cdSElina Pasheva 0x0000, /* __u16 index not used */ 672*eb4fd8cdSElina Pasheva attrdata, /* char *data */ 673*eb4fd8cdSElina Pasheva sizeof(*attrdata), /* __u16 size */ 674*eb4fd8cdSElina Pasheva USB_CTRL_SET_TIMEOUT); /* int timeout */ 675*eb4fd8cdSElina Pasheva 676*eb4fd8cdSElina Pasheva if (result < 0) { 677*eb4fd8cdSElina Pasheva kfree(attrdata); 678*eb4fd8cdSElina Pasheva return -EIO; 679*eb4fd8cdSElina Pasheva } 680*eb4fd8cdSElina Pasheva 681*eb4fd8cdSElina Pasheva *datap = *attrdata; 682*eb4fd8cdSElina Pasheva 683*eb4fd8cdSElina Pasheva kfree(attrdata); 684*eb4fd8cdSElina Pasheva return result; 685*eb4fd8cdSElina Pasheva } 686*eb4fd8cdSElina Pasheva 687*eb4fd8cdSElina Pasheva /* 688*eb4fd8cdSElina Pasheva * collects the bulk endpoints, the status endpoint. 689*eb4fd8cdSElina Pasheva */ 690*eb4fd8cdSElina Pasheva static int sierra_net_bind(struct usbnet *dev, struct usb_interface *intf) 691*eb4fd8cdSElina Pasheva { 692*eb4fd8cdSElina Pasheva u8 ifacenum; 693*eb4fd8cdSElina Pasheva u8 numendpoints; 694*eb4fd8cdSElina Pasheva u16 fwattr = 0; 695*eb4fd8cdSElina Pasheva int status; 696*eb4fd8cdSElina Pasheva struct ethhdr *eth; 697*eb4fd8cdSElina Pasheva struct sierra_net_data *priv; 698*eb4fd8cdSElina Pasheva static const u8 sync_tmplate[sizeof(priv->sync_msg)] = { 699*eb4fd8cdSElina Pasheva 0x00, 0x00, SIERRA_NET_HIP_MSYNC_ID, 0x00}; 700*eb4fd8cdSElina Pasheva static const u8 shdwn_tmplate[sizeof(priv->shdwn_msg)] = { 701*eb4fd8cdSElina Pasheva 0x00, 0x00, SIERRA_NET_HIP_SHUTD_ID, 0x00}; 702*eb4fd8cdSElina Pasheva 703*eb4fd8cdSElina Pasheva struct sierra_net_info_data *data = 704*eb4fd8cdSElina Pasheva (struct sierra_net_info_data *)dev->driver_info->data; 705*eb4fd8cdSElina Pasheva 706*eb4fd8cdSElina Pasheva dev_dbg(&dev->udev->dev, "%s", __func__); 707*eb4fd8cdSElina Pasheva 708*eb4fd8cdSElina Pasheva ifacenum = intf->cur_altsetting->desc.bInterfaceNumber; 709*eb4fd8cdSElina Pasheva /* We only accept certain interfaces */ 710*eb4fd8cdSElina Pasheva if (!is_whitelisted(ifacenum, &data->whitelist)) { 711*eb4fd8cdSElina Pasheva dev_dbg(&dev->udev->dev, "Ignoring interface: %d", ifacenum); 712*eb4fd8cdSElina Pasheva return -ENODEV; 713*eb4fd8cdSElina Pasheva } 714*eb4fd8cdSElina Pasheva numendpoints = intf->cur_altsetting->desc.bNumEndpoints; 715*eb4fd8cdSElina Pasheva /* We have three endpoints, bulk in and out, and a status */ 716*eb4fd8cdSElina Pasheva if (numendpoints != 3) { 717*eb4fd8cdSElina Pasheva dev_err(&dev->udev->dev, "Expected 3 endpoints, found: %d", 718*eb4fd8cdSElina Pasheva numendpoints); 719*eb4fd8cdSElina Pasheva return -ENODEV; 720*eb4fd8cdSElina Pasheva } 721*eb4fd8cdSElina Pasheva /* Status endpoint set in usbnet_get_endpoints() */ 722*eb4fd8cdSElina Pasheva dev->status = NULL; 723*eb4fd8cdSElina Pasheva status = usbnet_get_endpoints(dev, intf); 724*eb4fd8cdSElina Pasheva if (status < 0) { 725*eb4fd8cdSElina Pasheva dev_err(&dev->udev->dev, "Error in usbnet_get_endpoints (%d)", 726*eb4fd8cdSElina Pasheva status); 727*eb4fd8cdSElina Pasheva return -ENODEV; 728*eb4fd8cdSElina Pasheva } 729*eb4fd8cdSElina Pasheva /* Initialize sierra private data */ 730*eb4fd8cdSElina Pasheva priv = kzalloc(sizeof *priv, GFP_KERNEL); 731*eb4fd8cdSElina Pasheva if (!priv) { 732*eb4fd8cdSElina Pasheva dev_err(&dev->udev->dev, "No memory"); 733*eb4fd8cdSElina Pasheva return -ENOMEM; 734*eb4fd8cdSElina Pasheva } 735*eb4fd8cdSElina Pasheva 736*eb4fd8cdSElina Pasheva priv->usbnet = dev; 737*eb4fd8cdSElina Pasheva priv->ifnum = ifacenum; 738*eb4fd8cdSElina Pasheva dev->net->netdev_ops = &sierra_net_device_ops; 739*eb4fd8cdSElina Pasheva 740*eb4fd8cdSElina Pasheva /* change MAC addr to include, ifacenum, and to be unique */ 741*eb4fd8cdSElina Pasheva dev->net->dev_addr[ETH_ALEN-2] = atomic_inc_return(&iface_counter); 742*eb4fd8cdSElina Pasheva dev->net->dev_addr[ETH_ALEN-1] = ifacenum; 743*eb4fd8cdSElina Pasheva 744*eb4fd8cdSElina Pasheva /* we will have to manufacture ethernet headers, prepare template */ 745*eb4fd8cdSElina Pasheva eth = (struct ethhdr *)priv->ethr_hdr_tmpl; 746*eb4fd8cdSElina Pasheva memcpy(ð->h_dest, dev->net->dev_addr, ETH_ALEN); 747*eb4fd8cdSElina Pasheva eth->h_proto = cpu_to_be16(ETH_P_IP); 748*eb4fd8cdSElina Pasheva 749*eb4fd8cdSElina Pasheva /* prepare shutdown message template */ 750*eb4fd8cdSElina Pasheva memcpy(priv->shdwn_msg, shdwn_tmplate, sizeof(priv->shdwn_msg)); 751*eb4fd8cdSElina Pasheva /* set context index initially to 0 - prepares tx hdr template */ 752*eb4fd8cdSElina Pasheva sierra_net_set_ctx_index(priv, 0); 753*eb4fd8cdSElina Pasheva 754*eb4fd8cdSElina Pasheva /* decrease the rx_urb_size and max_tx_size to 4k on USB 1.1 */ 755*eb4fd8cdSElina Pasheva dev->rx_urb_size = data->rx_urb_size; 756*eb4fd8cdSElina Pasheva if (dev->udev->speed != USB_SPEED_HIGH) 757*eb4fd8cdSElina Pasheva dev->rx_urb_size = min_t(size_t, 4096, data->rx_urb_size); 758*eb4fd8cdSElina Pasheva 759*eb4fd8cdSElina Pasheva dev->net->hard_header_len += SIERRA_NET_HIP_EXT_HDR_LEN; 760*eb4fd8cdSElina Pasheva dev->hard_mtu = dev->net->mtu + dev->net->hard_header_len; 761*eb4fd8cdSElina Pasheva 762*eb4fd8cdSElina Pasheva /* Set up the netdev */ 763*eb4fd8cdSElina Pasheva dev->net->flags |= IFF_NOARP; 764*eb4fd8cdSElina Pasheva dev->net->ethtool_ops = &sierra_net_ethtool_ops; 765*eb4fd8cdSElina Pasheva netif_carrier_off(dev->net); 766*eb4fd8cdSElina Pasheva 767*eb4fd8cdSElina Pasheva sierra_net_set_private(dev, priv); 768*eb4fd8cdSElina Pasheva 769*eb4fd8cdSElina Pasheva priv->kevent_flags = 0; 770*eb4fd8cdSElina Pasheva 771*eb4fd8cdSElina Pasheva /* Use the shared workqueue */ 772*eb4fd8cdSElina Pasheva INIT_WORK(&priv->sierra_net_kevent, sierra_net_kevent); 773*eb4fd8cdSElina Pasheva 774*eb4fd8cdSElina Pasheva /* Only need to do this once */ 775*eb4fd8cdSElina Pasheva init_timer(&priv->sync_timer); 776*eb4fd8cdSElina Pasheva 777*eb4fd8cdSElina Pasheva /* verify fw attributes */ 778*eb4fd8cdSElina Pasheva status = sierra_net_get_fw_attr(dev, &fwattr); 779*eb4fd8cdSElina Pasheva dev_dbg(&dev->udev->dev, "Fw attr: %x\n", fwattr); 780*eb4fd8cdSElina Pasheva 781*eb4fd8cdSElina Pasheva /* test whether firmware supports DHCP */ 782*eb4fd8cdSElina Pasheva if (!(status == sizeof(fwattr) && (fwattr & SWI_GET_FW_ATTR_MASK))) { 783*eb4fd8cdSElina Pasheva /* found incompatible firmware version */ 784*eb4fd8cdSElina Pasheva dev_err(&dev->udev->dev, "Incompatible driver and firmware" 785*eb4fd8cdSElina Pasheva " versions\n"); 786*eb4fd8cdSElina Pasheva kfree(priv); 787*eb4fd8cdSElina Pasheva return -ENODEV; 788*eb4fd8cdSElina Pasheva } 789*eb4fd8cdSElina Pasheva /* prepare sync message from template */ 790*eb4fd8cdSElina Pasheva memcpy(priv->sync_msg, sync_tmplate, sizeof(priv->sync_msg)); 791*eb4fd8cdSElina Pasheva 792*eb4fd8cdSElina Pasheva return 0; 793*eb4fd8cdSElina Pasheva } 794*eb4fd8cdSElina Pasheva 795*eb4fd8cdSElina Pasheva static void sierra_net_unbind(struct usbnet *dev, struct usb_interface *intf) 796*eb4fd8cdSElina Pasheva { 797*eb4fd8cdSElina Pasheva int status; 798*eb4fd8cdSElina Pasheva struct sierra_net_data *priv = sierra_net_get_private(dev); 799*eb4fd8cdSElina Pasheva 800*eb4fd8cdSElina Pasheva dev_dbg(&dev->udev->dev, "%s", __func__); 801*eb4fd8cdSElina Pasheva 802*eb4fd8cdSElina Pasheva /* Kill the timer then flush the work queue */ 803*eb4fd8cdSElina Pasheva del_timer_sync(&priv->sync_timer); 804*eb4fd8cdSElina Pasheva 805*eb4fd8cdSElina Pasheva flush_scheduled_work(); 806*eb4fd8cdSElina Pasheva 807*eb4fd8cdSElina Pasheva /* tell modem we are going away */ 808*eb4fd8cdSElina Pasheva status = sierra_net_send_cmd(dev, priv->shdwn_msg, 809*eb4fd8cdSElina Pasheva sizeof(priv->shdwn_msg), "Shutdown"); 810*eb4fd8cdSElina Pasheva if (status < 0) 811*eb4fd8cdSElina Pasheva netdev_err(dev->net, 812*eb4fd8cdSElina Pasheva "usb_control_msg failed, status %d\n", status); 813*eb4fd8cdSElina Pasheva 814*eb4fd8cdSElina Pasheva sierra_net_set_private(dev, NULL); 815*eb4fd8cdSElina Pasheva 816*eb4fd8cdSElina Pasheva kfree(priv); 817*eb4fd8cdSElina Pasheva } 818*eb4fd8cdSElina Pasheva 819*eb4fd8cdSElina Pasheva static struct sk_buff *sierra_net_skb_clone(struct usbnet *dev, 820*eb4fd8cdSElina Pasheva struct sk_buff *skb, int len) 821*eb4fd8cdSElina Pasheva { 822*eb4fd8cdSElina Pasheva struct sk_buff *new_skb; 823*eb4fd8cdSElina Pasheva 824*eb4fd8cdSElina Pasheva /* clone skb */ 825*eb4fd8cdSElina Pasheva new_skb = skb_clone(skb, GFP_ATOMIC); 826*eb4fd8cdSElina Pasheva 827*eb4fd8cdSElina Pasheva /* remove len bytes from original */ 828*eb4fd8cdSElina Pasheva skb_pull(skb, len); 829*eb4fd8cdSElina Pasheva 830*eb4fd8cdSElina Pasheva /* trim next packet to it's length */ 831*eb4fd8cdSElina Pasheva if (new_skb) { 832*eb4fd8cdSElina Pasheva skb_trim(new_skb, len); 833*eb4fd8cdSElina Pasheva } else { 834*eb4fd8cdSElina Pasheva if (netif_msg_rx_err(dev)) 835*eb4fd8cdSElina Pasheva netdev_err(dev->net, "failed to get skb\n"); 836*eb4fd8cdSElina Pasheva dev->net->stats.rx_dropped++; 837*eb4fd8cdSElina Pasheva } 838*eb4fd8cdSElina Pasheva 839*eb4fd8cdSElina Pasheva return new_skb; 840*eb4fd8cdSElina Pasheva } 841*eb4fd8cdSElina Pasheva 842*eb4fd8cdSElina Pasheva /* ---------------------------- Receive data path ----------------------*/ 843*eb4fd8cdSElina Pasheva static int sierra_net_rx_fixup(struct usbnet *dev, struct sk_buff *skb) 844*eb4fd8cdSElina Pasheva { 845*eb4fd8cdSElina Pasheva int err; 846*eb4fd8cdSElina Pasheva struct hip_hdr hh; 847*eb4fd8cdSElina Pasheva struct sk_buff *new_skb; 848*eb4fd8cdSElina Pasheva 849*eb4fd8cdSElina Pasheva dev_dbg(&dev->udev->dev, "%s", __func__); 850*eb4fd8cdSElina Pasheva 851*eb4fd8cdSElina Pasheva /* could contain multiple packets */ 852*eb4fd8cdSElina Pasheva while (likely(skb->len)) { 853*eb4fd8cdSElina Pasheva err = parse_hip(skb->data, skb->len, &hh); 854*eb4fd8cdSElina Pasheva if (err) { 855*eb4fd8cdSElina Pasheva if (netif_msg_rx_err(dev)) 856*eb4fd8cdSElina Pasheva netdev_err(dev->net, "Invalid HIP header %d\n", 857*eb4fd8cdSElina Pasheva err); 858*eb4fd8cdSElina Pasheva /* dev->net->stats.rx_errors incremented by caller */ 859*eb4fd8cdSElina Pasheva dev->net->stats.rx_length_errors++; 860*eb4fd8cdSElina Pasheva return 0; 861*eb4fd8cdSElina Pasheva } 862*eb4fd8cdSElina Pasheva 863*eb4fd8cdSElina Pasheva /* Validate Extended HIP header */ 864*eb4fd8cdSElina Pasheva if (!hh.extmsgid.is_present 865*eb4fd8cdSElina Pasheva || hh.extmsgid.word != SIERRA_NET_HIP_EXT_IP_IN_ID) { 866*eb4fd8cdSElina Pasheva if (netif_msg_rx_err(dev)) 867*eb4fd8cdSElina Pasheva netdev_err(dev->net, "HIP/ETH: Invalid pkt\n"); 868*eb4fd8cdSElina Pasheva 869*eb4fd8cdSElina Pasheva dev->net->stats.rx_frame_errors++; 870*eb4fd8cdSElina Pasheva /* dev->net->stats.rx_errors incremented by caller */; 871*eb4fd8cdSElina Pasheva return 0; 872*eb4fd8cdSElina Pasheva } 873*eb4fd8cdSElina Pasheva 874*eb4fd8cdSElina Pasheva skb_pull(skb, hh.hdrlen); 875*eb4fd8cdSElina Pasheva 876*eb4fd8cdSElina Pasheva /* We are going to accept this packet, prepare it */ 877*eb4fd8cdSElina Pasheva memcpy(skb->data, sierra_net_get_private(dev)->ethr_hdr_tmpl, 878*eb4fd8cdSElina Pasheva ETH_HLEN); 879*eb4fd8cdSElina Pasheva 880*eb4fd8cdSElina Pasheva /* Last packet in batch handled by usbnet */ 881*eb4fd8cdSElina Pasheva if (hh.payload_len.word == skb->len) 882*eb4fd8cdSElina Pasheva return 1; 883*eb4fd8cdSElina Pasheva 884*eb4fd8cdSElina Pasheva new_skb = sierra_net_skb_clone(dev, skb, hh.payload_len.word); 885*eb4fd8cdSElina Pasheva if (new_skb) 886*eb4fd8cdSElina Pasheva usbnet_skb_return(dev, new_skb); 887*eb4fd8cdSElina Pasheva 888*eb4fd8cdSElina Pasheva } /* while */ 889*eb4fd8cdSElina Pasheva 890*eb4fd8cdSElina Pasheva return 0; 891*eb4fd8cdSElina Pasheva } 892*eb4fd8cdSElina Pasheva 893*eb4fd8cdSElina Pasheva /* ---------------------------- Transmit data path ----------------------*/ 894*eb4fd8cdSElina Pasheva struct sk_buff *sierra_net_tx_fixup(struct usbnet *dev, struct sk_buff *skb, 895*eb4fd8cdSElina Pasheva gfp_t flags) 896*eb4fd8cdSElina Pasheva { 897*eb4fd8cdSElina Pasheva struct sierra_net_data *priv = sierra_net_get_private(dev); 898*eb4fd8cdSElina Pasheva u16 len; 899*eb4fd8cdSElina Pasheva bool need_tail; 900*eb4fd8cdSElina Pasheva 901*eb4fd8cdSElina Pasheva dev_dbg(&dev->udev->dev, "%s", __func__); 902*eb4fd8cdSElina Pasheva if (priv->link_up && check_ethip_packet(skb, dev) && is_ip(skb)) { 903*eb4fd8cdSElina Pasheva /* enough head room as is? */ 904*eb4fd8cdSElina Pasheva if (SIERRA_NET_HIP_EXT_HDR_LEN <= skb_headroom(skb)) { 905*eb4fd8cdSElina Pasheva /* Save the Eth/IP length and set up HIP hdr */ 906*eb4fd8cdSElina Pasheva len = skb->len; 907*eb4fd8cdSElina Pasheva skb_push(skb, SIERRA_NET_HIP_EXT_HDR_LEN); 908*eb4fd8cdSElina Pasheva /* Handle ZLP issue */ 909*eb4fd8cdSElina Pasheva need_tail = ((len + SIERRA_NET_HIP_EXT_HDR_LEN) 910*eb4fd8cdSElina Pasheva % dev->maxpacket == 0); 911*eb4fd8cdSElina Pasheva if (need_tail) { 912*eb4fd8cdSElina Pasheva if (unlikely(skb_tailroom(skb) == 0)) { 913*eb4fd8cdSElina Pasheva netdev_err(dev->net, "tx_fixup:" 914*eb4fd8cdSElina Pasheva "no room for packet\n"); 915*eb4fd8cdSElina Pasheva dev_kfree_skb_any(skb); 916*eb4fd8cdSElina Pasheva return NULL; 917*eb4fd8cdSElina Pasheva } else { 918*eb4fd8cdSElina Pasheva skb->data[skb->len] = 0; 919*eb4fd8cdSElina Pasheva __skb_put(skb, 1); 920*eb4fd8cdSElina Pasheva len = len + 1; 921*eb4fd8cdSElina Pasheva } 922*eb4fd8cdSElina Pasheva } 923*eb4fd8cdSElina Pasheva build_hip(skb->data, len, priv); 924*eb4fd8cdSElina Pasheva return skb; 925*eb4fd8cdSElina Pasheva } else { 926*eb4fd8cdSElina Pasheva /* 927*eb4fd8cdSElina Pasheva * compensate in the future if necessary 928*eb4fd8cdSElina Pasheva */ 929*eb4fd8cdSElina Pasheva netdev_err(dev->net, "tx_fixup: no room for HIP\n"); 930*eb4fd8cdSElina Pasheva } /* headroom */ 931*eb4fd8cdSElina Pasheva } 932*eb4fd8cdSElina Pasheva 933*eb4fd8cdSElina Pasheva if (!priv->link_up) 934*eb4fd8cdSElina Pasheva dev->net->stats.tx_carrier_errors++; 935*eb4fd8cdSElina Pasheva 936*eb4fd8cdSElina Pasheva /* tx_dropped incremented by usbnet */ 937*eb4fd8cdSElina Pasheva 938*eb4fd8cdSElina Pasheva /* filter the packet out, release it */ 939*eb4fd8cdSElina Pasheva dev_kfree_skb_any(skb); 940*eb4fd8cdSElina Pasheva return NULL; 941*eb4fd8cdSElina Pasheva } 942*eb4fd8cdSElina Pasheva 943*eb4fd8cdSElina Pasheva static const u8 sierra_net_ifnum_list[] = { 7, 10, 11 }; 944*eb4fd8cdSElina Pasheva static const struct sierra_net_info_data sierra_net_info_data_68A3 = { 945*eb4fd8cdSElina Pasheva .rx_urb_size = 8 * 1024, 946*eb4fd8cdSElina Pasheva .whitelist = { 947*eb4fd8cdSElina Pasheva .infolen = ARRAY_SIZE(sierra_net_ifnum_list), 948*eb4fd8cdSElina Pasheva .ifaceinfo = sierra_net_ifnum_list 949*eb4fd8cdSElina Pasheva } 950*eb4fd8cdSElina Pasheva }; 951*eb4fd8cdSElina Pasheva 952*eb4fd8cdSElina Pasheva static const struct driver_info sierra_net_info_68A3 = { 953*eb4fd8cdSElina Pasheva .description = "Sierra Wireless USB-to-WWAN Modem", 954*eb4fd8cdSElina Pasheva .flags = FLAG_WWAN | FLAG_SEND_ZLP, 955*eb4fd8cdSElina Pasheva .bind = sierra_net_bind, 956*eb4fd8cdSElina Pasheva .unbind = sierra_net_unbind, 957*eb4fd8cdSElina Pasheva .status = sierra_net_status, 958*eb4fd8cdSElina Pasheva .rx_fixup = sierra_net_rx_fixup, 959*eb4fd8cdSElina Pasheva .tx_fixup = sierra_net_tx_fixup, 960*eb4fd8cdSElina Pasheva .data = (unsigned long)&sierra_net_info_data_68A3, 961*eb4fd8cdSElina Pasheva }; 962*eb4fd8cdSElina Pasheva 963*eb4fd8cdSElina Pasheva static const struct usb_device_id products[] = { 964*eb4fd8cdSElina Pasheva {USB_DEVICE(0x1199, 0x68A3), /* Sierra Wireless USB-to-WWAN modem */ 965*eb4fd8cdSElina Pasheva .driver_info = (unsigned long) &sierra_net_info_68A3}, 966*eb4fd8cdSElina Pasheva 967*eb4fd8cdSElina Pasheva {}, /* last item */ 968*eb4fd8cdSElina Pasheva }; 969*eb4fd8cdSElina Pasheva MODULE_DEVICE_TABLE(usb, products); 970*eb4fd8cdSElina Pasheva 971*eb4fd8cdSElina Pasheva /* We are based on usbnet, so let it handle the USB driver specifics */ 972*eb4fd8cdSElina Pasheva static struct usb_driver sierra_net_driver = { 973*eb4fd8cdSElina Pasheva .name = "sierra_net", 974*eb4fd8cdSElina Pasheva .id_table = products, 975*eb4fd8cdSElina Pasheva .probe = usbnet_probe, 976*eb4fd8cdSElina Pasheva .disconnect = usbnet_disconnect, 977*eb4fd8cdSElina Pasheva .suspend = usbnet_suspend, 978*eb4fd8cdSElina Pasheva .resume = usbnet_resume, 979*eb4fd8cdSElina Pasheva .no_dynamic_id = 1, 980*eb4fd8cdSElina Pasheva }; 981*eb4fd8cdSElina Pasheva 982*eb4fd8cdSElina Pasheva static int __init sierra_net_init(void) 983*eb4fd8cdSElina Pasheva { 984*eb4fd8cdSElina Pasheva BUILD_BUG_ON(FIELD_SIZEOF(struct usbnet, data) 985*eb4fd8cdSElina Pasheva < sizeof(struct cdc_state)); 986*eb4fd8cdSElina Pasheva 987*eb4fd8cdSElina Pasheva return usb_register(&sierra_net_driver); 988*eb4fd8cdSElina Pasheva } 989*eb4fd8cdSElina Pasheva 990*eb4fd8cdSElina Pasheva static void __exit sierra_net_exit(void) 991*eb4fd8cdSElina Pasheva { 992*eb4fd8cdSElina Pasheva usb_deregister(&sierra_net_driver); 993*eb4fd8cdSElina Pasheva } 994*eb4fd8cdSElina Pasheva 995*eb4fd8cdSElina Pasheva module_exit(sierra_net_exit); 996*eb4fd8cdSElina Pasheva module_init(sierra_net_init); 997*eb4fd8cdSElina Pasheva 998*eb4fd8cdSElina Pasheva MODULE_AUTHOR(DRIVER_AUTHOR); 999*eb4fd8cdSElina Pasheva MODULE_DESCRIPTION(DRIVER_DESC); 1000*eb4fd8cdSElina Pasheva MODULE_VERSION(DRIVER_VERSION); 1001*eb4fd8cdSElina Pasheva MODULE_LICENSE("GPL"); 1002