static netdev_tx_t brcmf_netdev_start_xmit(struct sk_buff *skb, struct net_device *ndev) { int ret; struct brcmf_if *ifp = netdev_priv(ndev); struct brcmf_pub *drvr = ifp->drvr; struct ethhdr *eh; int head_delta; brcmf_dbg(DATA, "Enter, bsscfgidx=%d\n", ifp->bsscfgidx); /* Can the device send data? */ if (drvr->bus_if->state != BRCMF_BUS_UP) { bphy_err(drvr, "xmit rejected state=%d\n", drvr->bus_if->state); netif_stop_queue(ndev); dev_kfree_skb(skb); ret = -ENODEV; goto done; } /* Some recent Broadcom's firmwares disassociate STA when they receive * an 802.11f ADD frame. This behavior can lead to a local DoS security * issue. Attacker may trigger disassociation of any STA by sending a * proper Ethernet frame to the wireless interface. * * Moreover this feature may break AP interfaces in some specific * setups. This applies e.g. to the bridge with hairpin mode enabled and * IFLA_BRPORT_MCAST_TO_UCAST set. IAPP packet generated by a firmware * will get passed back to the wireless interface and cause immediate * disassociation of a just-connected STA. */ if (!drvr->settings->iapp && brcmf_skb_is_iapp(skb)) { dev_kfree_skb(skb); ret = -EINVAL; goto done; } /* Make sure there's enough writeable headroom */ if (skb_headroom(skb) < drvr->hdrlen || skb_header_cloned(skb)) { head_delta = max_t(int, drvr->hdrlen - skb_headroom(skb), 0); brcmf_dbg(INFO, "%s: insufficient headroom (%d)\n", brcmf_ifname(ifp), head_delta); atomic_inc(&drvr->bus_if->stats.pktcowed); ret = pskb_expand_head(skb, ALIGN(head_delta, NET_SKB_PAD), 0, GFP_ATOMIC); if (ret < 0) { bphy_err(drvr, "%s: failed to expand headroom\n", brcmf_ifname(ifp)); atomic_inc(&drvr->bus_if->stats.pktcow_failed); goto done; } }
static s32 brcmf_fil_cmd_data(struct brcmf_if *ifp, u32 cmd, void *data, u32 len, bool set) { struct brcmf_pub *drvr = ifp->drvr; s32 err, fwerr; if (drvr->bus_if->state != BRCMF_BUS_UP) { bphy_err(drvr, "bus is down. we have nothing to do.\n"); return -EIO; } if (data != NULL) len = min_t(uint, len, BRCMF_DCMD_MAXLEN); if (set) err = brcmf_proto_set_dcmd(drvr, ifp->ifidx, cmd, data, len, &fwerr); else err = brcmf_proto_query_dcmd(drvr, ifp->ifidx, cmd, data, len, &fwerr); if (err) { brcmf_dbg(FIL, "Failed: error=%d\n", err); } else if (fwerr < 0) { brcmf_dbg(FIL, "Firmware error: %s (%d)\n", brcmf_fil_get_errstr((u32)(-fwerr)), fwerr); err = -EBADE; } if (ifp->fwil_fwerr) return fwerr; return err; }
s32 brcmf_fil_bsscfg_data_get(struct brcmf_if *ifp, char *name, void *data, u32 len) { struct brcmf_pub *drvr = ifp->drvr; s32 err; u32 buflen; mutex_lock(&drvr->proto_block); buflen = brcmf_create_bsscfg(ifp->bsscfgidx, name, data, len, drvr->proto_buf, sizeof(drvr->proto_buf)); if (buflen) { err = brcmf_fil_cmd_data(ifp, BRCMF_C_GET_VAR, drvr->proto_buf, buflen, false); if (err == 0) memcpy(data, drvr->proto_buf, len); } else { err = -EPERM; bphy_err(drvr, "Creating bsscfg failed\n"); } brcmf_dbg(FIL, "ifidx=%d, bsscfgidx=%d, name=%s, len=%d\n", ifp->ifidx, ifp->bsscfgidx, name, len); brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data, min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n"); mutex_unlock(&drvr->proto_block); return err; }
s32 brcmf_fil_iovar_data_set(struct brcmf_if *ifp, char *name, const void *data, u32 len) { struct brcmf_pub *drvr = ifp->drvr; s32 err; u32 buflen; mutex_lock(&drvr->proto_block); brcmf_dbg(FIL, "ifidx=%d, name=%s, len=%d\n", ifp->ifidx, name, len); brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data, min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n"); buflen = brcmf_create_iovar(name, data, len, drvr->proto_buf, sizeof(drvr->proto_buf)); if (buflen) { err = brcmf_fil_cmd_data(ifp, BRCMF_C_SET_VAR, drvr->proto_buf, buflen, true); } else { err = -EPERM; bphy_err(drvr, "Creating iovar failed\n"); } mutex_unlock(&drvr->proto_block); return err; }
struct brcmf_if *brcmf_get_ifp(struct brcmf_pub *drvr, int ifidx) { struct brcmf_if *ifp; s32 bsscfgidx; if (ifidx < 0 || ifidx >= BRCMF_MAX_IFS) { bphy_err(drvr, "ifidx %d out of range\n", ifidx); return NULL; } ifp = NULL; bsscfgidx = drvr->if2bss[ifidx]; if (bsscfgidx >= 0) ifp = drvr->iflist[bsscfgidx]; return ifp; }
static int brcmf_msgbuf_tx_ioctl(struct brcmf_pub *drvr, int ifidx, uint cmd, void *buf, uint len) { struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd; struct brcmf_commonring *commonring; struct msgbuf_ioctl_req_hdr *request; u16 buf_len; void *ret_ptr; int err; commonring = msgbuf->commonrings[BRCMF_H2D_MSGRING_CONTROL_SUBMIT]; brcmf_commonring_lock(commonring); ret_ptr = brcmf_commonring_reserve_for_write(commonring); if (!ret_ptr) { bphy_err(drvr, "Failed to reserve space in commonring\n"); brcmf_commonring_unlock(commonring); return -ENOMEM; } msgbuf->reqid++; request = (struct msgbuf_ioctl_req_hdr *)ret_ptr; request->msg.msgtype = MSGBUF_TYPE_IOCTLPTR_REQ; request->msg.ifidx = (u8)ifidx; request->msg.flags = 0; request->msg.request_id = cpu_to_le32(BRCMF_IOCTL_REQ_PKTID); request->cmd = cpu_to_le32(cmd); request->output_buf_len = cpu_to_le16(len); request->trans_id = cpu_to_le16(msgbuf->reqid); buf_len = min_t(u16, len, BRCMF_TX_IOCTL_MAX_MSG_SIZE); request->input_buf_len = cpu_to_le16(buf_len); request->req_buf_addr.high_addr = cpu_to_le32(msgbuf->ioctbuf_phys_hi); request->req_buf_addr.low_addr = cpu_to_le32(msgbuf->ioctbuf_phys_lo); if (buf) memcpy(msgbuf->ioctbuf, buf, buf_len); else memset(msgbuf->ioctbuf, 0, buf_len); err = brcmf_commonring_write_complete(commonring); brcmf_commonring_unlock(commonring); return err; }
static int brcmf_netdev_set_mac_address(struct net_device *ndev, void *addr) { struct brcmf_if *ifp = netdev_priv(ndev); struct sockaddr *sa = (struct sockaddr *)addr; struct brcmf_pub *drvr = ifp->drvr; int err; brcmf_dbg(TRACE, "Enter, bsscfgidx=%d\n", ifp->bsscfgidx); err = brcmf_fil_iovar_data_set(ifp, "cur_etheraddr", sa->sa_data, ETH_ALEN); if (err < 0) { bphy_err(drvr, "Setting cur_etheraddr failed, %d\n", err); } else { brcmf_dbg(TRACE, "updated to %pM\n", sa->sa_data); memcpy(ifp->mac_addr, sa->sa_data, ETH_ALEN); memcpy(ifp->ndev->dev_addr, ifp->mac_addr, ETH_ALEN); } return err; }
static void _brcmf_update_ndtable(struct work_struct *work) { struct brcmf_if *ifp = container_of(work, struct brcmf_if, ndoffload_work); struct brcmf_pub *drvr = ifp->drvr; int i, ret; /* clear the table in firmware */ ret = brcmf_fil_iovar_data_set(ifp, "nd_hostip_clear", NULL, 0); if (ret) { brcmf_dbg(TRACE, "fail to clear nd ip table err:%d\n", ret); return; } for (i = 0; i < ifp->ipv6addr_idx; i++) { ret = brcmf_fil_iovar_data_set(ifp, "nd_hostip", &ifp->ipv6_addr_tbl[i], sizeof(struct in6_addr)); if (ret) bphy_err(drvr, "add nd ip err %d\n", ret); } }
static int brcmf_msgbuf_query_dcmd(struct brcmf_pub *drvr, int ifidx, uint cmd, void *buf, uint len, int *fwerr) { struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd; struct sk_buff *skb = NULL; int timeout; int err; brcmf_dbg(MSGBUF, "ifidx=%d, cmd=%d, len=%d\n", ifidx, cmd, len); *fwerr = 0; msgbuf->ctl_completed = false; err = brcmf_msgbuf_tx_ioctl(drvr, ifidx, cmd, buf, len); if (err) return err; timeout = brcmf_msgbuf_ioctl_resp_wait(msgbuf); if (!timeout) { bphy_err(drvr, "Timeout on response for query command\n"); return -EIO; } skb = brcmf_msgbuf_get_pktid(msgbuf->drvr->bus_if->dev, msgbuf->rx_pktids, msgbuf->ioctl_resp_pktid); if (msgbuf->ioctl_resp_ret_len != 0) { if (!skb) return -EBADF; memcpy(buf, skb->data, (len < msgbuf->ioctl_resp_ret_len) ? len : msgbuf->ioctl_resp_ret_len); } brcmu_pkt_buf_free_skb(skb); *fwerr = msgbuf->ioctl_resp_status; return 0; }
static void _brcmf_set_multicast_list(struct work_struct *work) { struct brcmf_if *ifp = container_of(work, struct brcmf_if, multicast_work); struct brcmf_pub *drvr = ifp->drvr; struct net_device *ndev; struct netdev_hw_addr *ha; u32 cmd_value, cnt; __le32 cnt_le; char *buf, *bufp; u32 buflen; s32 err; brcmf_dbg(TRACE, "Enter, bsscfgidx=%d\n", ifp->bsscfgidx); ndev = ifp->ndev; /* Determine initial value of allmulti flag */ cmd_value = (ndev->flags & IFF_ALLMULTI) ? true : false; /* Send down the multicast list first. */ cnt = netdev_mc_count(ndev); buflen = sizeof(cnt) + (cnt * ETH_ALEN); buf = kmalloc(buflen, GFP_ATOMIC); if (!buf) return; bufp = buf; cnt_le = cpu_to_le32(cnt); memcpy(bufp, &cnt_le, sizeof(cnt_le)); bufp += sizeof(cnt_le); netdev_for_each_mc_addr(ha, ndev) { if (!cnt) break; memcpy(bufp, ha->addr, ETH_ALEN); bufp += ETH_ALEN; cnt--; } err = brcmf_fil_iovar_data_set(ifp, "mcast_list", buf, buflen); if (err < 0) { bphy_err(drvr, "Setting mcast_list failed, %d\n", err); cmd_value = cnt ? true : cmd_value; } kfree(buf); /* * Now send the allmulti setting. This is based on the setting in the * net_device flags, but might be modified above to be turned on if we * were trying to set some addresses and dongle rejected it... */ err = brcmf_fil_iovar_int_set(ifp, "allmulti", cmd_value); if (err < 0) bphy_err(drvr, "Setting allmulti failed, %d\n", err); /*Finally, pick up the PROMISC flag */ cmd_value = (ndev->flags & IFF_PROMISC) ? true : false; err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PROMISC, cmd_value); if (err < 0) bphy_err(drvr, "Setting BRCMF_C_SET_PROMISC failed, %d\n", err); brcmf_configure_arp_nd_offload(ifp, !cmd_value); }