--- sierra_net.c_bak 2014-01-16 19:31:29.914349701 +0100 +++ sierra_net.c 2014-01-16 20:00:21.087523130 +0100 @@ -122,7 +122,9 @@ #define SIERRA_NET_HIP_RCGI 0x64 /* LSI Protocol types */ +#define SIERRA_NET_PROTOCOL_PPP 0x00 #define SIERRA_NET_PROTOCOL_UMTS 0x01 +#define SIERRA_NET_PROTOCOL_UMTS_DS 0x04 /* LSI Coverage */ #define SIERRA_NET_COVERAGE_NONE 0x00 #define SIERRA_NET_COVERAGE_NOPACKET 0x01 @@ -131,20 +133,25 @@ #define SIERRA_NET_SESSION_IDLE 0x00 /* LSI Link types */ #define SIERRA_NET_AS_LINK_TYPE_IPv4 0x00 +#define SIERRA_NET_AS_LINK_TYPE_IPv6 0x02 -struct lsi_umts { +struct lsi_base { u8 protocol; u8 unused1; __be16 length; - /* eventually use a union for the rest - assume umts for now */ +} __packed; + +struct lsi_umts { + struct lsi_base base; u8 coverage; - u8 unused2[41]; + u8 net_name_len; + u8 net_name[40]; u8 session_state; - u8 unused3[33]; - u8 link_type; + u8 change_reason[33]; + u8 link_type; /* 0: IPv4, 2: IPv6 */ u8 pdp_addr_len; /* NW-supplied PDP address len */ u8 pdp_addr[16]; /* NW-supplied PDP address (bigendian)) */ - u8 unused4[23]; + u8 unused2[23]; u8 dns1_addr_len; /* NW-supplied 1st DNS address len (bigendian) */ u8 dns1_addr[16]; /* NW-supplied 1st DNS address */ u8 dns2_addr_len; /* NW-supplied 2nd DNS address len */ @@ -153,16 +160,33 @@ u8 wins1_addr[16]; /* NW-supplied 1st Wins address (bigendian)*/ u8 wins2_addr_len; /* NW-supplied 2nd Wins address len */ u8 wins2_addr[16]; /* NW-supplied 2nd Wins address (bigendian) */ - u8 unused5[4]; + u8 unused3[4]; u8 gw_addr_len; /* NW-supplied GW address len */ u8 gw_addr[16]; /* NW-supplied GW address (bigendian) */ u8 reserved[8]; } __packed; -#define SIERRA_NET_LSI_COMMON_LEN 4 +struct lsi_umts_ds { + struct lsi_base base; + u8 coverage; + u8 net_name_len; + u8 net_name[40]; + u8 session_state; + u8 change_reason[33]; + u8 pdp_addr4_len; /* NW-supplied PDP address len */ + u8 pdp_addr4[16]; /* NW-supplied PDP address (bigendian)) */ + u8 pdp_addr6_len; /* NW-supplied PDP address len */ + u8 pdp_addr6[16]; /* NW-supplied PDP address (bigendian)) */ + u8 unused2[40]; /* FIXME: wrong length */ +} __packed; + +#define SIERRA_NET_LSI_COMMON_LEN (sizeof(struct lsi)) #define SIERRA_NET_LSI_UMTS_LEN (sizeof(struct lsi_umts)) #define SIERRA_NET_LSI_UMTS_STATUS_LEN \ (SIERRA_NET_LSI_UMTS_LEN - SIERRA_NET_LSI_COMMON_LEN) +#define SIERRA_NET_LSI_UMTS_DS_LEN (sizeof(struct lsi_umts_ds)) +#define SIERRA_NET_LSI_UMTS_DS_STATUS_LEN \ + (SIERRA_NET_LSI_UMTS_DS_LEN - SIERRA_NET_LSI_COMMON_LEN) /* Forward definitions */ static void sierra_sync_timer(unsigned long syncdata); @@ -360,22 +384,16 @@ return -1; } - if (lsi->length != cpu_to_be16(SIERRA_NET_LSI_UMTS_STATUS_LEN)) { + if (lsi->base.length != cpu_to_be16(SIERRA_NET_LSI_UMTS_STATUS_LEN)) { netdev_err(dev->net, "%s: LSI_UMTS_STATUS_LEN %d, exp %u\n", __func__, be16_to_cpu(lsi->length), (u32)SIERRA_NET_LSI_UMTS_STATUS_LEN); return -1; } - /* Validate the protocol - only support UMTS for now */ - if (lsi->protocol != SIERRA_NET_PROTOCOL_UMTS) { - netdev_err(dev->net, "Protocol unsupported, 0x%02x\n", - lsi->protocol); - return -1; - } - /* Validate the link type */ - if (lsi->link_type != SIERRA_NET_AS_LINK_TYPE_IPv4) { + if ((lsi->link_type != SIERRA_NET_AS_LINK_TYPE_IPv4) && + (lsi->link_type != SIERRA_NET_AS_LINK_TYPE_IPv6)) { netdev_err(dev->net, "Link type unsupported: 0x%02x\n", lsi->link_type); return -1; @@ -399,14 +417,67 @@ return 1; } +static int sierra_net_parse_lsi_ds(struct usbnet *dev, char *data, int datalen) +{ + struct lsi_umts_ds *lsi = (struct lsi_umts_ds *)data; + + if (datalen < sizeof(struct lsi_umts_ds)) { + netdev_err(dev->net, "%s: Data length %d, exp %Zu\n", + __func__, datalen, + sizeof(struct lsi_umts_ds)); + return -1; + } + + if (lsi->base.length != cpu_to_be16(SIERRA_NET_LSI_UMTS_DS_STATUS_LEN)) { + netdev_err(dev->net, "%s: LSI_UMTS_DS_STATUS_LEN %d, exp %u\n", + __func__, be16_to_cpu(lsi->length), + (u32)SIERRA_NET_LSI_UMTS_DS_STATUS_LEN); + // return -1; + } + + /* Validate the coverage */ + if (lsi->coverage == SIERRA_NET_COVERAGE_NONE + || lsi->coverage == SIERRA_NET_COVERAGE_NOPACKET) { + netdev_err(dev->net, "No coverage, 0x%02x\n", lsi->coverage); + return 0; + } + + /* Validate the session state */ + if (lsi->session_state == SIERRA_NET_SESSION_IDLE) { + netdev_err(dev->net, "Session idle, 0x%02x\n", + lsi->session_state); + return 0; + } + + /* Set link_sense true */ + return 1; +} + static void sierra_net_handle_lsi(struct usbnet *dev, char *data, struct hip_hdr *hh) { struct sierra_net_data *priv = sierra_net_get_private(dev); int link_up; - link_up = sierra_net_parse_lsi(dev, data + hh->hdrlen, - hh->payload_len.word); + struct lsi_base *lsi = (struct lsi_umts *)(data + hh->hdrlen); + + if (hh->payload_len.word < sizeof(struct lsi_base)) { + netdev_err(dev->net, "%s: Data length %d, exp %Zu\n", + __func__, datalen, + sizeof(struct lsi_base)); + return -1; + } + + if (lsi->protocol == SIERRA_NET_PROTOCOL_UMTS) { + link_up = sierra_net_parse_lsi(dev, data + hh->hdrlen, + hh->payload_len.word); + } else if (lsi->protocol == SIERRA_NET_PROTOCOL_UMTS_DS) { + link_up = sierra_net_parse_lsi_ds(dev, data + hh->hdrlen, + hh->payload_len.word); + } else { + link_up = -1; + } + if (link_up < 0) { netdev_err(dev->net, "Invalid LSI\n"); return;