bcdc.c revision bee1b848877b9e4512bdda480f73cda12b593e2f
15b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/* 25b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * Copyright (c) 2010 Broadcom Corporation 35b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * 45b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * Permission to use, copy, modify, and/or distribute this software for any 55b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * purpose with or without fee is hereby granted, provided that the above 65b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * copyright notice and this permission notice appear in all copies. 75b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * 85b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 95b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 105b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 115b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 125b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION 135b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 145b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 155b435de0d786869c95d1962121af0d7df2542009Arend van Spriel */ 165b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 175b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/******************************************************************************* 185b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * Communicates with the dongle by using dcmd codes. 195b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * For certain dcmd codes, the dongle interprets string data from the host. 205b435de0d786869c95d1962121af0d7df2542009Arend van Spriel ******************************************************************************/ 215b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 225b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#include <linux/types.h> 235b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#include <linux/netdevice.h> 245b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 255b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#include <brcmu_utils.h> 265b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#include <brcmu_wifi.h> 275b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 285b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#include "dhd.h" 295b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#include "dhd_proto.h" 305b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#include "dhd_bus.h" 315b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#include "dhd_dbg.h" 325b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 335b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstruct brcmf_proto_cdc_dcmd { 345b435de0d786869c95d1962121af0d7df2542009Arend van Spriel __le32 cmd; /* dongle command value */ 355b435de0d786869c95d1962121af0d7df2542009Arend van Spriel __le32 len; /* lower 16: output buflen; 365b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * upper 16: input buflen (excludes header) */ 375b435de0d786869c95d1962121af0d7df2542009Arend van Spriel __le32 flags; /* flag defns given below */ 385b435de0d786869c95d1962121af0d7df2542009Arend van Spriel __le32 status; /* status code returned from the device */ 395b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}; 405b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 415b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/* Max valid buffer size that can be sent to the dongle */ 425b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define CDC_MAX_MSG_SIZE (ETH_FRAME_LEN+ETH_FCS_LEN) 435b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 445b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/* CDC flag definitions */ 455b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define CDC_DCMD_ERROR 0x01 /* 1=cmd failed */ 465b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define CDC_DCMD_SET 0x02 /* 0=get, 1=set cmd */ 475b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define CDC_DCMD_IF_MASK 0xF000 /* I/F index */ 485b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define CDC_DCMD_IF_SHIFT 12 495b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define CDC_DCMD_ID_MASK 0xFFFF0000 /* id an cmd pairing */ 505b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define CDC_DCMD_ID_SHIFT 16 /* ID Mask shift bits */ 515b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define CDC_DCMD_ID(flags) \ 525b435de0d786869c95d1962121af0d7df2542009Arend van Spriel (((flags) & CDC_DCMD_ID_MASK) >> CDC_DCMD_ID_SHIFT) 535b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 545b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/* 555b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * BDC header - Broadcom specific extension of CDC. 565b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * Used on data packets to convey priority across USB. 575b435de0d786869c95d1962121af0d7df2542009Arend van Spriel */ 585b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define BDC_HEADER_LEN 4 59e40aed0638ac84d63a2ff33502e215ac81010a89Franky Lin#define BDC_PROTO_VER 2 /* Protocol version */ 605b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define BDC_FLAG_VER_MASK 0xf0 /* Protocol version mask */ 615b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define BDC_FLAG_VER_SHIFT 4 /* Protocol version shift */ 625b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define BDC_FLAG_SUM_GOOD 0x04 /* Good RX checksums */ 635b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define BDC_FLAG_SUM_NEEDED 0x08 /* Dongle needs to do TX checksums */ 645b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define BDC_PRIORITY_MASK 0x7 655b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define BDC_FLAG2_IF_MASK 0x0f /* packet rx interface in APSTA */ 665b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define BDC_FLAG2_IF_SHIFT 0 675b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 685b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define BDC_GET_IF_IDX(hdr) \ 695b435de0d786869c95d1962121af0d7df2542009Arend van Spriel ((int)((((hdr)->flags2) & BDC_FLAG2_IF_MASK) >> BDC_FLAG2_IF_SHIFT)) 705b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define BDC_SET_IF_IDX(hdr, idx) \ 715b435de0d786869c95d1962121af0d7df2542009Arend van Spriel ((hdr)->flags2 = (((hdr)->flags2 & ~BDC_FLAG2_IF_MASK) | \ 725b435de0d786869c95d1962121af0d7df2542009Arend van Spriel ((idx) << BDC_FLAG2_IF_SHIFT))) 735b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 74bee1b848877b9e4512bdda480f73cda12b593e2fArend van Spriel/** 75bee1b848877b9e4512bdda480f73cda12b593e2fArend van Spriel * struct brcmf_proto_bdc_header - BDC header format 76bee1b848877b9e4512bdda480f73cda12b593e2fArend van Spriel * 77bee1b848877b9e4512bdda480f73cda12b593e2fArend van Spriel * @flags: flags contain protocol and checksum info. 78bee1b848877b9e4512bdda480f73cda12b593e2fArend van Spriel * @priority: 802.1d priority and USB flow control info (bit 4:7). 79bee1b848877b9e4512bdda480f73cda12b593e2fArend van Spriel * @flags2: additional flags containing dongle interface index. 80bee1b848877b9e4512bdda480f73cda12b593e2fArend van Spriel * @data_offset: start of packet data. header is following by firmware signals. 81bee1b848877b9e4512bdda480f73cda12b593e2fArend van Spriel */ 825b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstruct brcmf_proto_bdc_header { 835b435de0d786869c95d1962121af0d7df2542009Arend van Spriel u8 flags; 84bee1b848877b9e4512bdda480f73cda12b593e2fArend van Spriel u8 priority; 855b435de0d786869c95d1962121af0d7df2542009Arend van Spriel u8 flags2; 86e40aed0638ac84d63a2ff33502e215ac81010a89Franky Lin u8 data_offset; 875b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}; 885b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 89bee1b848877b9e4512bdda480f73cda12b593e2fArend van Spriel/* 90bee1b848877b9e4512bdda480f73cda12b593e2fArend van Spriel * maximum length of firmware signal data between 91bee1b848877b9e4512bdda480f73cda12b593e2fArend van Spriel * the BDC header and packet data in the tx path. 92bee1b848877b9e4512bdda480f73cda12b593e2fArend van Spriel */ 93bee1b848877b9e4512bdda480f73cda12b593e2fArend van Spriel#define BRCMF_PROT_FW_SIGNAL_MAX_TXBYTES 12 945b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 955b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define RETRIES 2 /* # of retries to retrieve matching dcmd response */ 966e3c712807237ab2b50e860d94dc8f15a81d03cdFranky Lin#define BUS_HEADER_LEN (16+64) /* Must be atleast SDPCM_RESERVE 975b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * (amount of header tha might be added) 985b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * plus any space that might be needed 996e3c712807237ab2b50e860d94dc8f15a81d03cdFranky Lin * for bus alignment padding. 1005b435de0d786869c95d1962121af0d7df2542009Arend van Spriel */ 1016e3c712807237ab2b50e860d94dc8f15a81d03cdFranky Lin#define ROUND_UP_MARGIN 2048 /* Biggest bus block size possible for 1025b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * round off at the end of buffer 1036e3c712807237ab2b50e860d94dc8f15a81d03cdFranky Lin * Currently is SDIO 1045b435de0d786869c95d1962121af0d7df2542009Arend van Spriel */ 1055b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 1065b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstruct brcmf_proto { 1075b435de0d786869c95d1962121af0d7df2542009Arend van Spriel u16 reqid; 1085b435de0d786869c95d1962121af0d7df2542009Arend van Spriel u8 bus_header[BUS_HEADER_LEN]; 1095b435de0d786869c95d1962121af0d7df2542009Arend van Spriel struct brcmf_proto_cdc_dcmd msg; 1105b435de0d786869c95d1962121af0d7df2542009Arend van Spriel unsigned char buf[BRCMF_DCMD_MAXLEN + ROUND_UP_MARGIN]; 1115b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}; 1125b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 1135b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstatic int brcmf_proto_cdc_msg(struct brcmf_pub *drvr) 1145b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{ 1155b435de0d786869c95d1962121af0d7df2542009Arend van Spriel struct brcmf_proto *prot = drvr->prot; 1165b435de0d786869c95d1962121af0d7df2542009Arend van Spriel int len = le32_to_cpu(prot->msg.len) + 1175b435de0d786869c95d1962121af0d7df2542009Arend van Spriel sizeof(struct brcmf_proto_cdc_dcmd); 1185b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 119a6cfb1477d23aa251a1a4b0285fb385d94cdde51Hante Meuleman brcmf_dbg(CDC, "Enter\n"); 1205b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 1215b435de0d786869c95d1962121af0d7df2542009Arend van Spriel /* NOTE : cdc->msg.len holds the desired length of the buffer to be 1225b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * returned. Only up to CDC_MAX_MSG_SIZE of this buffer area 1235b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * is actually sent to the dongle 1245b435de0d786869c95d1962121af0d7df2542009Arend van Spriel */ 1255b435de0d786869c95d1962121af0d7df2542009Arend van Spriel if (len > CDC_MAX_MSG_SIZE) 1265b435de0d786869c95d1962121af0d7df2542009Arend van Spriel len = CDC_MAX_MSG_SIZE; 1275b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 1285b435de0d786869c95d1962121af0d7df2542009Arend van Spriel /* Send request */ 129d9cb2596503d937ccf68b83d3aff1056765a6b1eArend van Spriel return brcmf_bus_txctl(drvr->bus_if, (unsigned char *)&prot->msg, len); 1305b435de0d786869c95d1962121af0d7df2542009Arend van Spriel} 1315b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 1325b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstatic int brcmf_proto_cdc_cmplt(struct brcmf_pub *drvr, u32 id, u32 len) 1335b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{ 1345b435de0d786869c95d1962121af0d7df2542009Arend van Spriel int ret; 1355b435de0d786869c95d1962121af0d7df2542009Arend van Spriel struct brcmf_proto *prot = drvr->prot; 1365b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 137a6cfb1477d23aa251a1a4b0285fb385d94cdde51Hante Meuleman brcmf_dbg(CDC, "Enter\n"); 138d9cb2596503d937ccf68b83d3aff1056765a6b1eArend van Spriel len += sizeof(struct brcmf_proto_cdc_dcmd); 1395b435de0d786869c95d1962121af0d7df2542009Arend van Spriel do { 140d9cb2596503d937ccf68b83d3aff1056765a6b1eArend van Spriel ret = brcmf_bus_rxctl(drvr->bus_if, (unsigned char *)&prot->msg, 141d9cb2596503d937ccf68b83d3aff1056765a6b1eArend van Spriel len); 1425b435de0d786869c95d1962121af0d7df2542009Arend van Spriel if (ret < 0) 1435b435de0d786869c95d1962121af0d7df2542009Arend van Spriel break; 1445b435de0d786869c95d1962121af0d7df2542009Arend van Spriel } while (CDC_DCMD_ID(le32_to_cpu(prot->msg.flags)) != id); 1455b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 1465b435de0d786869c95d1962121af0d7df2542009Arend van Spriel return ret; 1475b435de0d786869c95d1962121af0d7df2542009Arend van Spriel} 1485b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 1495b435de0d786869c95d1962121af0d7df2542009Arend van Sprielint 1505b435de0d786869c95d1962121af0d7df2542009Arend van Sprielbrcmf_proto_cdc_query_dcmd(struct brcmf_pub *drvr, int ifidx, uint cmd, 1515b435de0d786869c95d1962121af0d7df2542009Arend van Spriel void *buf, uint len) 1525b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{ 1535b435de0d786869c95d1962121af0d7df2542009Arend van Spriel struct brcmf_proto *prot = drvr->prot; 1545b435de0d786869c95d1962121af0d7df2542009Arend van Spriel struct brcmf_proto_cdc_dcmd *msg = &prot->msg; 1555b435de0d786869c95d1962121af0d7df2542009Arend van Spriel void *info; 1565b435de0d786869c95d1962121af0d7df2542009Arend van Spriel int ret = 0, retries = 0; 1575b435de0d786869c95d1962121af0d7df2542009Arend van Spriel u32 id, flags; 1585b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 159a6cfb1477d23aa251a1a4b0285fb385d94cdde51Hante Meuleman brcmf_dbg(CDC, "Enter, cmd %d len %d\n", cmd, len); 1605b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 1615b435de0d786869c95d1962121af0d7df2542009Arend van Spriel memset(msg, 0, sizeof(struct brcmf_proto_cdc_dcmd)); 1625b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 1635b435de0d786869c95d1962121af0d7df2542009Arend van Spriel msg->cmd = cpu_to_le32(cmd); 1645b435de0d786869c95d1962121af0d7df2542009Arend van Spriel msg->len = cpu_to_le32(len); 1655b435de0d786869c95d1962121af0d7df2542009Arend van Spriel flags = (++prot->reqid << CDC_DCMD_ID_SHIFT); 1665b435de0d786869c95d1962121af0d7df2542009Arend van Spriel flags = (flags & ~CDC_DCMD_IF_MASK) | 1675b435de0d786869c95d1962121af0d7df2542009Arend van Spriel (ifidx << CDC_DCMD_IF_SHIFT); 1685b435de0d786869c95d1962121af0d7df2542009Arend van Spriel msg->flags = cpu_to_le32(flags); 1695b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 1705b435de0d786869c95d1962121af0d7df2542009Arend van Spriel if (buf) 1715b435de0d786869c95d1962121af0d7df2542009Arend van Spriel memcpy(prot->buf, buf, len); 1725b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 1735b435de0d786869c95d1962121af0d7df2542009Arend van Spriel ret = brcmf_proto_cdc_msg(drvr); 1745b435de0d786869c95d1962121af0d7df2542009Arend van Spriel if (ret < 0) { 1755e8149f5036afe2d94b5fafc9ff752283804a752Arend van Spriel brcmf_err("brcmf_proto_cdc_msg failed w/status %d\n", 1765b435de0d786869c95d1962121af0d7df2542009Arend van Spriel ret); 1775b435de0d786869c95d1962121af0d7df2542009Arend van Spriel goto done; 1785b435de0d786869c95d1962121af0d7df2542009Arend van Spriel } 1795b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 1805b435de0d786869c95d1962121af0d7df2542009Arend van Sprielretry: 1815b435de0d786869c95d1962121af0d7df2542009Arend van Spriel /* wait for interrupt and get first fragment */ 1825b435de0d786869c95d1962121af0d7df2542009Arend van Spriel ret = brcmf_proto_cdc_cmplt(drvr, prot->reqid, len); 1835b435de0d786869c95d1962121af0d7df2542009Arend van Spriel if (ret < 0) 1845b435de0d786869c95d1962121af0d7df2542009Arend van Spriel goto done; 1855b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 1865b435de0d786869c95d1962121af0d7df2542009Arend van Spriel flags = le32_to_cpu(msg->flags); 1875b435de0d786869c95d1962121af0d7df2542009Arend van Spriel id = (flags & CDC_DCMD_ID_MASK) >> CDC_DCMD_ID_SHIFT; 1885b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 1895b435de0d786869c95d1962121af0d7df2542009Arend van Spriel if ((id < prot->reqid) && (++retries < RETRIES)) 1905b435de0d786869c95d1962121af0d7df2542009Arend van Spriel goto retry; 1915b435de0d786869c95d1962121af0d7df2542009Arend van Spriel if (id != prot->reqid) { 1925e8149f5036afe2d94b5fafc9ff752283804a752Arend van Spriel brcmf_err("%s: unexpected request id %d (expected %d)\n", 1935b435de0d786869c95d1962121af0d7df2542009Arend van Spriel brcmf_ifname(drvr, ifidx), id, prot->reqid); 1945b435de0d786869c95d1962121af0d7df2542009Arend van Spriel ret = -EINVAL; 1955b435de0d786869c95d1962121af0d7df2542009Arend van Spriel goto done; 1965b435de0d786869c95d1962121af0d7df2542009Arend van Spriel } 1975b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 1985b435de0d786869c95d1962121af0d7df2542009Arend van Spriel /* Check info buffer */ 1995b435de0d786869c95d1962121af0d7df2542009Arend van Spriel info = (void *)&msg[1]; 2005b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 2015b435de0d786869c95d1962121af0d7df2542009Arend van Spriel /* Copy info buffer */ 2025b435de0d786869c95d1962121af0d7df2542009Arend van Spriel if (buf) { 2035b435de0d786869c95d1962121af0d7df2542009Arend van Spriel if (ret < (int)len) 2045b435de0d786869c95d1962121af0d7df2542009Arend van Spriel len = ret; 2055b435de0d786869c95d1962121af0d7df2542009Arend van Spriel memcpy(buf, info, len); 2065b435de0d786869c95d1962121af0d7df2542009Arend van Spriel } 2075b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 2085b435de0d786869c95d1962121af0d7df2542009Arend van Spriel /* Check the ERROR flag */ 20903abad08bb837650feb2bd7581ef14c8a1c51964Hante Meuleman if (flags & CDC_DCMD_ERROR) 2105b435de0d786869c95d1962121af0d7df2542009Arend van Spriel ret = le32_to_cpu(msg->status); 2115b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 2125b435de0d786869c95d1962121af0d7df2542009Arend van Sprieldone: 2135b435de0d786869c95d1962121af0d7df2542009Arend van Spriel return ret; 2145b435de0d786869c95d1962121af0d7df2542009Arend van Spriel} 2155b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 2165b435de0d786869c95d1962121af0d7df2542009Arend van Sprielint brcmf_proto_cdc_set_dcmd(struct brcmf_pub *drvr, int ifidx, uint cmd, 2175b435de0d786869c95d1962121af0d7df2542009Arend van Spriel void *buf, uint len) 2185b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{ 2195b435de0d786869c95d1962121af0d7df2542009Arend van Spriel struct brcmf_proto *prot = drvr->prot; 2205b435de0d786869c95d1962121af0d7df2542009Arend van Spriel struct brcmf_proto_cdc_dcmd *msg = &prot->msg; 2215b435de0d786869c95d1962121af0d7df2542009Arend van Spriel int ret = 0; 2225b435de0d786869c95d1962121af0d7df2542009Arend van Spriel u32 flags, id; 2235b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 224a6cfb1477d23aa251a1a4b0285fb385d94cdde51Hante Meuleman brcmf_dbg(CDC, "Enter, cmd %d len %d\n", cmd, len); 2255b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 2265b435de0d786869c95d1962121af0d7df2542009Arend van Spriel memset(msg, 0, sizeof(struct brcmf_proto_cdc_dcmd)); 2275b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 2285b435de0d786869c95d1962121af0d7df2542009Arend van Spriel msg->cmd = cpu_to_le32(cmd); 2295b435de0d786869c95d1962121af0d7df2542009Arend van Spriel msg->len = cpu_to_le32(len); 2305b435de0d786869c95d1962121af0d7df2542009Arend van Spriel flags = (++prot->reqid << CDC_DCMD_ID_SHIFT) | CDC_DCMD_SET; 2315b435de0d786869c95d1962121af0d7df2542009Arend van Spriel flags = (flags & ~CDC_DCMD_IF_MASK) | 2325b435de0d786869c95d1962121af0d7df2542009Arend van Spriel (ifidx << CDC_DCMD_IF_SHIFT); 2335b435de0d786869c95d1962121af0d7df2542009Arend van Spriel msg->flags = cpu_to_le32(flags); 2345b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 2355b435de0d786869c95d1962121af0d7df2542009Arend van Spriel if (buf) 2365b435de0d786869c95d1962121af0d7df2542009Arend van Spriel memcpy(prot->buf, buf, len); 2375b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 2385b435de0d786869c95d1962121af0d7df2542009Arend van Spriel ret = brcmf_proto_cdc_msg(drvr); 2395b435de0d786869c95d1962121af0d7df2542009Arend van Spriel if (ret < 0) 2405b435de0d786869c95d1962121af0d7df2542009Arend van Spriel goto done; 2415b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 2425b435de0d786869c95d1962121af0d7df2542009Arend van Spriel ret = brcmf_proto_cdc_cmplt(drvr, prot->reqid, len); 2435b435de0d786869c95d1962121af0d7df2542009Arend van Spriel if (ret < 0) 2445b435de0d786869c95d1962121af0d7df2542009Arend van Spriel goto done; 2455b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 2465b435de0d786869c95d1962121af0d7df2542009Arend van Spriel flags = le32_to_cpu(msg->flags); 2475b435de0d786869c95d1962121af0d7df2542009Arend van Spriel id = (flags & CDC_DCMD_ID_MASK) >> CDC_DCMD_ID_SHIFT; 2485b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 2495b435de0d786869c95d1962121af0d7df2542009Arend van Spriel if (id != prot->reqid) { 2505e8149f5036afe2d94b5fafc9ff752283804a752Arend van Spriel brcmf_err("%s: unexpected request id %d (expected %d)\n", 2515b435de0d786869c95d1962121af0d7df2542009Arend van Spriel brcmf_ifname(drvr, ifidx), id, prot->reqid); 2525b435de0d786869c95d1962121af0d7df2542009Arend van Spriel ret = -EINVAL; 2535b435de0d786869c95d1962121af0d7df2542009Arend van Spriel goto done; 2545b435de0d786869c95d1962121af0d7df2542009Arend van Spriel } 2555b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 2565b435de0d786869c95d1962121af0d7df2542009Arend van Spriel /* Check the ERROR flag */ 25703abad08bb837650feb2bd7581ef14c8a1c51964Hante Meuleman if (flags & CDC_DCMD_ERROR) 2585b435de0d786869c95d1962121af0d7df2542009Arend van Spriel ret = le32_to_cpu(msg->status); 2595b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 2605b435de0d786869c95d1962121af0d7df2542009Arend van Sprieldone: 2615b435de0d786869c95d1962121af0d7df2542009Arend van Spriel return ret; 2625b435de0d786869c95d1962121af0d7df2542009Arend van Spriel} 2635b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 2645b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstatic bool pkt_sum_needed(struct sk_buff *skb) 2655b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{ 2665b435de0d786869c95d1962121af0d7df2542009Arend van Spriel return skb->ip_summed == CHECKSUM_PARTIAL; 2675b435de0d786869c95d1962121af0d7df2542009Arend van Spriel} 2685b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 2695b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstatic void pkt_set_sum_good(struct sk_buff *skb, bool x) 2705b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{ 2715b435de0d786869c95d1962121af0d7df2542009Arend van Spriel skb->ip_summed = (x ? CHECKSUM_UNNECESSARY : CHECKSUM_NONE); 2725b435de0d786869c95d1962121af0d7df2542009Arend van Spriel} 2735b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 2745b435de0d786869c95d1962121af0d7df2542009Arend van Sprielvoid brcmf_proto_hdrpush(struct brcmf_pub *drvr, int ifidx, 2755b435de0d786869c95d1962121af0d7df2542009Arend van Spriel struct sk_buff *pktbuf) 2765b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{ 2775b435de0d786869c95d1962121af0d7df2542009Arend van Spriel struct brcmf_proto_bdc_header *h; 2785b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 279a6cfb1477d23aa251a1a4b0285fb385d94cdde51Hante Meuleman brcmf_dbg(CDC, "Enter\n"); 2805b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 2815b435de0d786869c95d1962121af0d7df2542009Arend van Spriel /* Push BDC header used to convey priority for buses that don't */ 2825b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 2835b435de0d786869c95d1962121af0d7df2542009Arend van Spriel skb_push(pktbuf, BDC_HEADER_LEN); 2845b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 2855b435de0d786869c95d1962121af0d7df2542009Arend van Spriel h = (struct brcmf_proto_bdc_header *)(pktbuf->data); 2865b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 2875b435de0d786869c95d1962121af0d7df2542009Arend van Spriel h->flags = (BDC_PROTO_VER << BDC_FLAG_VER_SHIFT); 2885b435de0d786869c95d1962121af0d7df2542009Arend van Spriel if (pkt_sum_needed(pktbuf)) 2895b435de0d786869c95d1962121af0d7df2542009Arend van Spriel h->flags |= BDC_FLAG_SUM_NEEDED; 2905b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 2915b435de0d786869c95d1962121af0d7df2542009Arend van Spriel h->priority = (pktbuf->priority & BDC_PRIORITY_MASK); 2925b435de0d786869c95d1962121af0d7df2542009Arend van Spriel h->flags2 = 0; 293e40aed0638ac84d63a2ff33502e215ac81010a89Franky Lin h->data_offset = 0; 2945b435de0d786869c95d1962121af0d7df2542009Arend van Spriel BDC_SET_IF_IDX(h, ifidx); 2955b435de0d786869c95d1962121af0d7df2542009Arend van Spriel} 2965b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 297a43af515f6252e62604a1bfc139d43fa43ef5b6fArend van Sprielint brcmf_proto_hdrpull(struct brcmf_pub *drvr, u8 *ifidx, 2985b435de0d786869c95d1962121af0d7df2542009Arend van Spriel struct sk_buff *pktbuf) 2995b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{ 3005b435de0d786869c95d1962121af0d7df2542009Arend van Spriel struct brcmf_proto_bdc_header *h; 3015b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 302a6cfb1477d23aa251a1a4b0285fb385d94cdde51Hante Meuleman brcmf_dbg(CDC, "Enter\n"); 3035b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 3045b435de0d786869c95d1962121af0d7df2542009Arend van Spriel /* Pop BDC header used to convey priority for buses that don't */ 3055b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 3065b435de0d786869c95d1962121af0d7df2542009Arend van Spriel if (pktbuf->len < BDC_HEADER_LEN) { 3075e8149f5036afe2d94b5fafc9ff752283804a752Arend van Spriel brcmf_err("rx data too short (%d < %d)\n", 3085b435de0d786869c95d1962121af0d7df2542009Arend van Spriel pktbuf->len, BDC_HEADER_LEN); 3095b435de0d786869c95d1962121af0d7df2542009Arend van Spriel return -EBADE; 3105b435de0d786869c95d1962121af0d7df2542009Arend van Spriel } 3115b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 3125b435de0d786869c95d1962121af0d7df2542009Arend van Spriel h = (struct brcmf_proto_bdc_header *)(pktbuf->data); 3135b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 3145b435de0d786869c95d1962121af0d7df2542009Arend van Spriel *ifidx = BDC_GET_IF_IDX(h); 3155b435de0d786869c95d1962121af0d7df2542009Arend van Spriel if (*ifidx >= BRCMF_MAX_IFS) { 3165e8149f5036afe2d94b5fafc9ff752283804a752Arend van Spriel brcmf_err("rx data ifnum out of range (%d)\n", *ifidx); 3175b435de0d786869c95d1962121af0d7df2542009Arend van Spriel return -EBADE; 3185b435de0d786869c95d1962121af0d7df2542009Arend van Spriel } 3192880b86859967af710c72f7d34fb421a86a71e22Hante Meuleman /* The ifidx is the idx to map to matching netdev/ifp. When receiving 3202880b86859967af710c72f7d34fb421a86a71e22Hante Meuleman * events this is easy because it contains the bssidx which maps 3212880b86859967af710c72f7d34fb421a86a71e22Hante Meuleman * 1-on-1 to the netdev/ifp. But for data frames the ifidx is rcvd. 3222880b86859967af710c72f7d34fb421a86a71e22Hante Meuleman * bssidx 1 is used for p2p0 and no data can be received or 3232880b86859967af710c72f7d34fb421a86a71e22Hante Meuleman * transmitted on it. Therefor bssidx is ifidx + 1 if ifidx > 0 3242880b86859967af710c72f7d34fb421a86a71e22Hante Meuleman */ 3252880b86859967af710c72f7d34fb421a86a71e22Hante Meuleman if (*ifidx) 3262880b86859967af710c72f7d34fb421a86a71e22Hante Meuleman (*ifidx)++; 3275b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 3285b435de0d786869c95d1962121af0d7df2542009Arend van Spriel if (((h->flags & BDC_FLAG_VER_MASK) >> BDC_FLAG_VER_SHIFT) != 3295b435de0d786869c95d1962121af0d7df2542009Arend van Spriel BDC_PROTO_VER) { 3305e8149f5036afe2d94b5fafc9ff752283804a752Arend van Spriel brcmf_err("%s: non-BDC packet received, flags 0x%x\n", 3315b435de0d786869c95d1962121af0d7df2542009Arend van Spriel brcmf_ifname(drvr, *ifidx), h->flags); 3325b435de0d786869c95d1962121af0d7df2542009Arend van Spriel return -EBADE; 3335b435de0d786869c95d1962121af0d7df2542009Arend van Spriel } 3345b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 3355b435de0d786869c95d1962121af0d7df2542009Arend van Spriel if (h->flags & BDC_FLAG_SUM_GOOD) { 336a6cfb1477d23aa251a1a4b0285fb385d94cdde51Hante Meuleman brcmf_dbg(CDC, "%s: BDC rcv, good checksum, flags 0x%x\n", 3375b435de0d786869c95d1962121af0d7df2542009Arend van Spriel brcmf_ifname(drvr, *ifidx), h->flags); 3385b435de0d786869c95d1962121af0d7df2542009Arend van Spriel pkt_set_sum_good(pktbuf, true); 3395b435de0d786869c95d1962121af0d7df2542009Arend van Spriel } 3405b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 3415b435de0d786869c95d1962121af0d7df2542009Arend van Spriel pktbuf->priority = h->priority & BDC_PRIORITY_MASK; 3425b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 3435b435de0d786869c95d1962121af0d7df2542009Arend van Spriel skb_pull(pktbuf, BDC_HEADER_LEN); 3448f1ab44d8de5154cf6746d07f0902ea73f059c2bFranky Lin skb_pull(pktbuf, h->data_offset << 2); 3455b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 346a43af515f6252e62604a1bfc139d43fa43ef5b6fArend van Spriel if (pktbuf->len == 0) 347a43af515f6252e62604a1bfc139d43fa43ef5b6fArend van Spriel return -ENODATA; 3485b435de0d786869c95d1962121af0d7df2542009Arend van Spriel return 0; 3495b435de0d786869c95d1962121af0d7df2542009Arend van Spriel} 3505b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 3515b435de0d786869c95d1962121af0d7df2542009Arend van Sprielint brcmf_proto_attach(struct brcmf_pub *drvr) 3525b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{ 3535b435de0d786869c95d1962121af0d7df2542009Arend van Spriel struct brcmf_proto *cdc; 3545b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 3555b435de0d786869c95d1962121af0d7df2542009Arend van Spriel cdc = kzalloc(sizeof(struct brcmf_proto), GFP_ATOMIC); 3565b435de0d786869c95d1962121af0d7df2542009Arend van Spriel if (!cdc) 3575b435de0d786869c95d1962121af0d7df2542009Arend van Spriel goto fail; 3585b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 3595b435de0d786869c95d1962121af0d7df2542009Arend van Spriel /* ensure that the msg buf directly follows the cdc msg struct */ 3605b435de0d786869c95d1962121af0d7df2542009Arend van Spriel if ((unsigned long)(&cdc->msg + 1) != (unsigned long)cdc->buf) { 3615e8149f5036afe2d94b5fafc9ff752283804a752Arend van Spriel brcmf_err("struct brcmf_proto is not correctly defined\n"); 3625b435de0d786869c95d1962121af0d7df2542009Arend van Spriel goto fail; 3635b435de0d786869c95d1962121af0d7df2542009Arend van Spriel } 3645b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 3655b435de0d786869c95d1962121af0d7df2542009Arend van Spriel drvr->prot = cdc; 366bee1b848877b9e4512bdda480f73cda12b593e2fArend van Spriel drvr->hdrlen += BDC_HEADER_LEN + BRCMF_PROT_FW_SIGNAL_MAX_TXBYTES; 367b01a6b3ca714e2bb86ee387aee487c7360363c93Franky Lin drvr->bus_if->maxctl = BRCMF_DCMD_MAXLEN + 3685b435de0d786869c95d1962121af0d7df2542009Arend van Spriel sizeof(struct brcmf_proto_cdc_dcmd) + ROUND_UP_MARGIN; 3695b435de0d786869c95d1962121af0d7df2542009Arend van Spriel return 0; 3705b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 3715b435de0d786869c95d1962121af0d7df2542009Arend van Sprielfail: 3725b435de0d786869c95d1962121af0d7df2542009Arend van Spriel kfree(cdc); 3735b435de0d786869c95d1962121af0d7df2542009Arend van Spriel return -ENOMEM; 3745b435de0d786869c95d1962121af0d7df2542009Arend van Spriel} 3755b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 3765b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/* ~NOTE~ What if another thread is waiting on the semaphore? Holding it? */ 3775b435de0d786869c95d1962121af0d7df2542009Arend van Sprielvoid brcmf_proto_detach(struct brcmf_pub *drvr) 3785b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{ 3795b435de0d786869c95d1962121af0d7df2542009Arend van Spriel kfree(drvr->prot); 3805b435de0d786869c95d1962121af0d7df2542009Arend van Spriel drvr->prot = NULL; 3815b435de0d786869c95d1962121af0d7df2542009Arend van Spriel} 3825b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 3835b435de0d786869c95d1962121af0d7df2542009Arend van Sprielvoid brcmf_proto_stop(struct brcmf_pub *drvr) 3845b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{ 3855b435de0d786869c95d1962121af0d7df2542009Arend van Spriel /* Nothing to do for CDC */ 3865b435de0d786869c95d1962121af0d7df2542009Arend van Spriel} 387