bcdc.c revision ea0737d6e24b44b632e9094108bb987b0338ea74
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" 31349e7104ff662eeacca1fffbb480c287562c45a1Arend van Spriel#include "fwsignal.h" 325b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#include "dhd_dbg.h" 33ea0737d6e24b44b632e9094108bb987b0338ea74Arend van Spriel#include "tracepoint.h" 345b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 355b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstruct brcmf_proto_cdc_dcmd { 365b435de0d786869c95d1962121af0d7df2542009Arend van Spriel __le32 cmd; /* dongle command value */ 375b435de0d786869c95d1962121af0d7df2542009Arend van Spriel __le32 len; /* lower 16: output buflen; 385b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * upper 16: input buflen (excludes header) */ 395b435de0d786869c95d1962121af0d7df2542009Arend van Spriel __le32 flags; /* flag defns given below */ 405b435de0d786869c95d1962121af0d7df2542009Arend van Spriel __le32 status; /* status code returned from the device */ 415b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}; 425b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 435b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/* Max valid buffer size that can be sent to the dongle */ 445b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define CDC_MAX_MSG_SIZE (ETH_FRAME_LEN+ETH_FCS_LEN) 455b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 465b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/* CDC flag definitions */ 475b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define CDC_DCMD_ERROR 0x01 /* 1=cmd failed */ 485b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define CDC_DCMD_SET 0x02 /* 0=get, 1=set cmd */ 495b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define CDC_DCMD_IF_MASK 0xF000 /* I/F index */ 505b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define CDC_DCMD_IF_SHIFT 12 515b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define CDC_DCMD_ID_MASK 0xFFFF0000 /* id an cmd pairing */ 525b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define CDC_DCMD_ID_SHIFT 16 /* ID Mask shift bits */ 535b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define CDC_DCMD_ID(flags) \ 545b435de0d786869c95d1962121af0d7df2542009Arend van Spriel (((flags) & CDC_DCMD_ID_MASK) >> CDC_DCMD_ID_SHIFT) 555b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 565b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/* 575b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * BDC header - Broadcom specific extension of CDC. 585b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * Used on data packets to convey priority across USB. 595b435de0d786869c95d1962121af0d7df2542009Arend van Spriel */ 605b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define BDC_HEADER_LEN 4 61e40aed0638ac84d63a2ff33502e215ac81010a89Franky Lin#define BDC_PROTO_VER 2 /* Protocol version */ 625b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define BDC_FLAG_VER_MASK 0xf0 /* Protocol version mask */ 635b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define BDC_FLAG_VER_SHIFT 4 /* Protocol version shift */ 645b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define BDC_FLAG_SUM_GOOD 0x04 /* Good RX checksums */ 655b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define BDC_FLAG_SUM_NEEDED 0x08 /* Dongle needs to do TX checksums */ 665b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define BDC_PRIORITY_MASK 0x7 675b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define BDC_FLAG2_IF_MASK 0x0f /* packet rx interface in APSTA */ 685b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define BDC_FLAG2_IF_SHIFT 0 695b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 705b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define BDC_GET_IF_IDX(hdr) \ 715b435de0d786869c95d1962121af0d7df2542009Arend van Spriel ((int)((((hdr)->flags2) & BDC_FLAG2_IF_MASK) >> BDC_FLAG2_IF_SHIFT)) 725b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define BDC_SET_IF_IDX(hdr, idx) \ 735b435de0d786869c95d1962121af0d7df2542009Arend van Spriel ((hdr)->flags2 = (((hdr)->flags2 & ~BDC_FLAG2_IF_MASK) | \ 745b435de0d786869c95d1962121af0d7df2542009Arend van Spriel ((idx) << BDC_FLAG2_IF_SHIFT))) 755b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 76bee1b848877b9e4512bdda480f73cda12b593e2fArend van Spriel/** 77bee1b848877b9e4512bdda480f73cda12b593e2fArend van Spriel * struct brcmf_proto_bdc_header - BDC header format 78bee1b848877b9e4512bdda480f73cda12b593e2fArend van Spriel * 79bee1b848877b9e4512bdda480f73cda12b593e2fArend van Spriel * @flags: flags contain protocol and checksum info. 80bee1b848877b9e4512bdda480f73cda12b593e2fArend van Spriel * @priority: 802.1d priority and USB flow control info (bit 4:7). 81bee1b848877b9e4512bdda480f73cda12b593e2fArend van Spriel * @flags2: additional flags containing dongle interface index. 82bee1b848877b9e4512bdda480f73cda12b593e2fArend van Spriel * @data_offset: start of packet data. header is following by firmware signals. 83bee1b848877b9e4512bdda480f73cda12b593e2fArend van Spriel */ 845b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstruct brcmf_proto_bdc_header { 855b435de0d786869c95d1962121af0d7df2542009Arend van Spriel u8 flags; 86bee1b848877b9e4512bdda480f73cda12b593e2fArend van Spriel u8 priority; 875b435de0d786869c95d1962121af0d7df2542009Arend van Spriel u8 flags2; 88e40aed0638ac84d63a2ff33502e215ac81010a89Franky Lin u8 data_offset; 895b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}; 905b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 91bee1b848877b9e4512bdda480f73cda12b593e2fArend van Spriel/* 92bee1b848877b9e4512bdda480f73cda12b593e2fArend van Spriel * maximum length of firmware signal data between 93bee1b848877b9e4512bdda480f73cda12b593e2fArend van Spriel * the BDC header and packet data in the tx path. 94bee1b848877b9e4512bdda480f73cda12b593e2fArend van Spriel */ 95bee1b848877b9e4512bdda480f73cda12b593e2fArend van Spriel#define BRCMF_PROT_FW_SIGNAL_MAX_TXBYTES 12 965b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 975b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define RETRIES 2 /* # of retries to retrieve matching dcmd response */ 986e3c712807237ab2b50e860d94dc8f15a81d03cdFranky Lin#define BUS_HEADER_LEN (16+64) /* Must be atleast SDPCM_RESERVE 995b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * (amount of header tha might be added) 1005b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * plus any space that might be needed 1016e3c712807237ab2b50e860d94dc8f15a81d03cdFranky Lin * for bus alignment padding. 1025b435de0d786869c95d1962121af0d7df2542009Arend van Spriel */ 1036e3c712807237ab2b50e860d94dc8f15a81d03cdFranky Lin#define ROUND_UP_MARGIN 2048 /* Biggest bus block size possible for 1045b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * round off at the end of buffer 1056e3c712807237ab2b50e860d94dc8f15a81d03cdFranky Lin * Currently is SDIO 1065b435de0d786869c95d1962121af0d7df2542009Arend van Spriel */ 1075b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 1085b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstruct brcmf_proto { 1095b435de0d786869c95d1962121af0d7df2542009Arend van Spriel u16 reqid; 1105b435de0d786869c95d1962121af0d7df2542009Arend van Spriel u8 bus_header[BUS_HEADER_LEN]; 1115b435de0d786869c95d1962121af0d7df2542009Arend van Spriel struct brcmf_proto_cdc_dcmd msg; 1125b435de0d786869c95d1962121af0d7df2542009Arend van Spriel unsigned char buf[BRCMF_DCMD_MAXLEN + ROUND_UP_MARGIN]; 1135b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}; 1145b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 1155b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstatic int brcmf_proto_cdc_msg(struct brcmf_pub *drvr) 1165b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{ 1175b435de0d786869c95d1962121af0d7df2542009Arend van Spriel struct brcmf_proto *prot = drvr->prot; 1185b435de0d786869c95d1962121af0d7df2542009Arend van Spriel int len = le32_to_cpu(prot->msg.len) + 1195b435de0d786869c95d1962121af0d7df2542009Arend van Spriel sizeof(struct brcmf_proto_cdc_dcmd); 1205b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 121a6cfb1477d23aa251a1a4b0285fb385d94cdde51Hante Meuleman brcmf_dbg(CDC, "Enter\n"); 1225b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 1235b435de0d786869c95d1962121af0d7df2542009Arend van Spriel /* NOTE : cdc->msg.len holds the desired length of the buffer to be 1245b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * returned. Only up to CDC_MAX_MSG_SIZE of this buffer area 1255b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * is actually sent to the dongle 1265b435de0d786869c95d1962121af0d7df2542009Arend van Spriel */ 1275b435de0d786869c95d1962121af0d7df2542009Arend van Spriel if (len > CDC_MAX_MSG_SIZE) 1285b435de0d786869c95d1962121af0d7df2542009Arend van Spriel len = CDC_MAX_MSG_SIZE; 1295b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 1305b435de0d786869c95d1962121af0d7df2542009Arend van Spriel /* Send request */ 131d9cb2596503d937ccf68b83d3aff1056765a6b1eArend van Spriel return brcmf_bus_txctl(drvr->bus_if, (unsigned char *)&prot->msg, len); 1325b435de0d786869c95d1962121af0d7df2542009Arend van Spriel} 1335b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 1345b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstatic int brcmf_proto_cdc_cmplt(struct brcmf_pub *drvr, u32 id, u32 len) 1355b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{ 1365b435de0d786869c95d1962121af0d7df2542009Arend van Spriel int ret; 1375b435de0d786869c95d1962121af0d7df2542009Arend van Spriel struct brcmf_proto *prot = drvr->prot; 1385b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 139a6cfb1477d23aa251a1a4b0285fb385d94cdde51Hante Meuleman brcmf_dbg(CDC, "Enter\n"); 140d9cb2596503d937ccf68b83d3aff1056765a6b1eArend van Spriel len += sizeof(struct brcmf_proto_cdc_dcmd); 1415b435de0d786869c95d1962121af0d7df2542009Arend van Spriel do { 142d9cb2596503d937ccf68b83d3aff1056765a6b1eArend van Spriel ret = brcmf_bus_rxctl(drvr->bus_if, (unsigned char *)&prot->msg, 143d9cb2596503d937ccf68b83d3aff1056765a6b1eArend van Spriel len); 1445b435de0d786869c95d1962121af0d7df2542009Arend van Spriel if (ret < 0) 1455b435de0d786869c95d1962121af0d7df2542009Arend van Spriel break; 1465b435de0d786869c95d1962121af0d7df2542009Arend van Spriel } while (CDC_DCMD_ID(le32_to_cpu(prot->msg.flags)) != id); 1475b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 1485b435de0d786869c95d1962121af0d7df2542009Arend van Spriel return ret; 1495b435de0d786869c95d1962121af0d7df2542009Arend van Spriel} 1505b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 1515b435de0d786869c95d1962121af0d7df2542009Arend van Sprielint 1525b435de0d786869c95d1962121af0d7df2542009Arend van Sprielbrcmf_proto_cdc_query_dcmd(struct brcmf_pub *drvr, int ifidx, uint cmd, 1535b435de0d786869c95d1962121af0d7df2542009Arend van Spriel void *buf, uint len) 1545b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{ 1555b435de0d786869c95d1962121af0d7df2542009Arend van Spriel struct brcmf_proto *prot = drvr->prot; 1565b435de0d786869c95d1962121af0d7df2542009Arend van Spriel struct brcmf_proto_cdc_dcmd *msg = &prot->msg; 1575b435de0d786869c95d1962121af0d7df2542009Arend van Spriel void *info; 1585b435de0d786869c95d1962121af0d7df2542009Arend van Spriel int ret = 0, retries = 0; 1595b435de0d786869c95d1962121af0d7df2542009Arend van Spriel u32 id, flags; 1605b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 161a6cfb1477d23aa251a1a4b0285fb385d94cdde51Hante Meuleman brcmf_dbg(CDC, "Enter, cmd %d len %d\n", cmd, len); 1625b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 1635b435de0d786869c95d1962121af0d7df2542009Arend van Spriel memset(msg, 0, sizeof(struct brcmf_proto_cdc_dcmd)); 1645b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 1655b435de0d786869c95d1962121af0d7df2542009Arend van Spriel msg->cmd = cpu_to_le32(cmd); 1665b435de0d786869c95d1962121af0d7df2542009Arend van Spriel msg->len = cpu_to_le32(len); 1675b435de0d786869c95d1962121af0d7df2542009Arend van Spriel flags = (++prot->reqid << CDC_DCMD_ID_SHIFT); 1685b435de0d786869c95d1962121af0d7df2542009Arend van Spriel flags = (flags & ~CDC_DCMD_IF_MASK) | 1695b435de0d786869c95d1962121af0d7df2542009Arend van Spriel (ifidx << CDC_DCMD_IF_SHIFT); 1705b435de0d786869c95d1962121af0d7df2542009Arend van Spriel msg->flags = cpu_to_le32(flags); 1715b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 1725b435de0d786869c95d1962121af0d7df2542009Arend van Spriel if (buf) 1735b435de0d786869c95d1962121af0d7df2542009Arend van Spriel memcpy(prot->buf, buf, len); 1745b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 1755b435de0d786869c95d1962121af0d7df2542009Arend van Spriel ret = brcmf_proto_cdc_msg(drvr); 1765b435de0d786869c95d1962121af0d7df2542009Arend van Spriel if (ret < 0) { 1775e8149f5036afe2d94b5fafc9ff752283804a752Arend van Spriel brcmf_err("brcmf_proto_cdc_msg failed w/status %d\n", 1785b435de0d786869c95d1962121af0d7df2542009Arend van Spriel ret); 1795b435de0d786869c95d1962121af0d7df2542009Arend van Spriel goto done; 1805b435de0d786869c95d1962121af0d7df2542009Arend van Spriel } 1815b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 1825b435de0d786869c95d1962121af0d7df2542009Arend van Sprielretry: 1835b435de0d786869c95d1962121af0d7df2542009Arend van Spriel /* wait for interrupt and get first fragment */ 1845b435de0d786869c95d1962121af0d7df2542009Arend van Spriel ret = brcmf_proto_cdc_cmplt(drvr, prot->reqid, len); 1855b435de0d786869c95d1962121af0d7df2542009Arend van Spriel if (ret < 0) 1865b435de0d786869c95d1962121af0d7df2542009Arend van Spriel goto done; 1875b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 1885b435de0d786869c95d1962121af0d7df2542009Arend van Spriel flags = le32_to_cpu(msg->flags); 1895b435de0d786869c95d1962121af0d7df2542009Arend van Spriel id = (flags & CDC_DCMD_ID_MASK) >> CDC_DCMD_ID_SHIFT; 1905b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 1915b435de0d786869c95d1962121af0d7df2542009Arend van Spriel if ((id < prot->reqid) && (++retries < RETRIES)) 1925b435de0d786869c95d1962121af0d7df2542009Arend van Spriel goto retry; 1935b435de0d786869c95d1962121af0d7df2542009Arend van Spriel if (id != prot->reqid) { 1945e8149f5036afe2d94b5fafc9ff752283804a752Arend van Spriel brcmf_err("%s: unexpected request id %d (expected %d)\n", 1955b435de0d786869c95d1962121af0d7df2542009Arend van Spriel brcmf_ifname(drvr, ifidx), id, prot->reqid); 1965b435de0d786869c95d1962121af0d7df2542009Arend van Spriel ret = -EINVAL; 1975b435de0d786869c95d1962121af0d7df2542009Arend van Spriel goto done; 1985b435de0d786869c95d1962121af0d7df2542009Arend van Spriel } 1995b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 2005b435de0d786869c95d1962121af0d7df2542009Arend van Spriel /* Check info buffer */ 2015b435de0d786869c95d1962121af0d7df2542009Arend van Spriel info = (void *)&msg[1]; 2025b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 2035b435de0d786869c95d1962121af0d7df2542009Arend van Spriel /* Copy info buffer */ 2045b435de0d786869c95d1962121af0d7df2542009Arend van Spriel if (buf) { 2055b435de0d786869c95d1962121af0d7df2542009Arend van Spriel if (ret < (int)len) 2065b435de0d786869c95d1962121af0d7df2542009Arend van Spriel len = ret; 2075b435de0d786869c95d1962121af0d7df2542009Arend van Spriel memcpy(buf, info, len); 2085b435de0d786869c95d1962121af0d7df2542009Arend van Spriel } 2095b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 2105b435de0d786869c95d1962121af0d7df2542009Arend van Spriel /* Check the ERROR flag */ 21103abad08bb837650feb2bd7581ef14c8a1c51964Hante Meuleman if (flags & CDC_DCMD_ERROR) 2125b435de0d786869c95d1962121af0d7df2542009Arend van Spriel ret = le32_to_cpu(msg->status); 2135b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 2145b435de0d786869c95d1962121af0d7df2542009Arend van Sprieldone: 2155b435de0d786869c95d1962121af0d7df2542009Arend van Spriel return ret; 2165b435de0d786869c95d1962121af0d7df2542009Arend van Spriel} 2175b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 2185b435de0d786869c95d1962121af0d7df2542009Arend van Sprielint brcmf_proto_cdc_set_dcmd(struct brcmf_pub *drvr, int ifidx, uint cmd, 2195b435de0d786869c95d1962121af0d7df2542009Arend van Spriel void *buf, uint len) 2205b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{ 2215b435de0d786869c95d1962121af0d7df2542009Arend van Spriel struct brcmf_proto *prot = drvr->prot; 2225b435de0d786869c95d1962121af0d7df2542009Arend van Spriel struct brcmf_proto_cdc_dcmd *msg = &prot->msg; 2235b435de0d786869c95d1962121af0d7df2542009Arend van Spriel int ret = 0; 2245b435de0d786869c95d1962121af0d7df2542009Arend van Spriel u32 flags, id; 2255b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 226a6cfb1477d23aa251a1a4b0285fb385d94cdde51Hante Meuleman brcmf_dbg(CDC, "Enter, cmd %d len %d\n", cmd, len); 2275b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 2285b435de0d786869c95d1962121af0d7df2542009Arend van Spriel memset(msg, 0, sizeof(struct brcmf_proto_cdc_dcmd)); 2295b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 2305b435de0d786869c95d1962121af0d7df2542009Arend van Spriel msg->cmd = cpu_to_le32(cmd); 2315b435de0d786869c95d1962121af0d7df2542009Arend van Spriel msg->len = cpu_to_le32(len); 2325b435de0d786869c95d1962121af0d7df2542009Arend van Spriel flags = (++prot->reqid << CDC_DCMD_ID_SHIFT) | CDC_DCMD_SET; 2335b435de0d786869c95d1962121af0d7df2542009Arend van Spriel flags = (flags & ~CDC_DCMD_IF_MASK) | 2345b435de0d786869c95d1962121af0d7df2542009Arend van Spriel (ifidx << CDC_DCMD_IF_SHIFT); 2355b435de0d786869c95d1962121af0d7df2542009Arend van Spriel msg->flags = cpu_to_le32(flags); 2365b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 2375b435de0d786869c95d1962121af0d7df2542009Arend van Spriel if (buf) 2385b435de0d786869c95d1962121af0d7df2542009Arend van Spriel memcpy(prot->buf, buf, len); 2395b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 2405b435de0d786869c95d1962121af0d7df2542009Arend van Spriel ret = brcmf_proto_cdc_msg(drvr); 2415b435de0d786869c95d1962121af0d7df2542009Arend van Spriel if (ret < 0) 2425b435de0d786869c95d1962121af0d7df2542009Arend van Spriel goto done; 2435b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 2445b435de0d786869c95d1962121af0d7df2542009Arend van Spriel ret = brcmf_proto_cdc_cmplt(drvr, prot->reqid, len); 2455b435de0d786869c95d1962121af0d7df2542009Arend van Spriel if (ret < 0) 2465b435de0d786869c95d1962121af0d7df2542009Arend van Spriel goto done; 2475b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 2485b435de0d786869c95d1962121af0d7df2542009Arend van Spriel flags = le32_to_cpu(msg->flags); 2495b435de0d786869c95d1962121af0d7df2542009Arend van Spriel id = (flags & CDC_DCMD_ID_MASK) >> CDC_DCMD_ID_SHIFT; 2505b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 2515b435de0d786869c95d1962121af0d7df2542009Arend van Spriel if (id != prot->reqid) { 2525e8149f5036afe2d94b5fafc9ff752283804a752Arend van Spriel brcmf_err("%s: unexpected request id %d (expected %d)\n", 2535b435de0d786869c95d1962121af0d7df2542009Arend van Spriel brcmf_ifname(drvr, ifidx), id, prot->reqid); 2545b435de0d786869c95d1962121af0d7df2542009Arend van Spriel ret = -EINVAL; 2555b435de0d786869c95d1962121af0d7df2542009Arend van Spriel goto done; 2565b435de0d786869c95d1962121af0d7df2542009Arend van Spriel } 2575b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 2585b435de0d786869c95d1962121af0d7df2542009Arend van Spriel /* Check the ERROR flag */ 25903abad08bb837650feb2bd7581ef14c8a1c51964Hante Meuleman if (flags & CDC_DCMD_ERROR) 2605b435de0d786869c95d1962121af0d7df2542009Arend van Spriel ret = le32_to_cpu(msg->status); 2615b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 2625b435de0d786869c95d1962121af0d7df2542009Arend van Sprieldone: 2635b435de0d786869c95d1962121af0d7df2542009Arend van Spriel return ret; 2645b435de0d786869c95d1962121af0d7df2542009Arend van Spriel} 2655b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 2665b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstatic bool pkt_sum_needed(struct sk_buff *skb) 2675b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{ 2685b435de0d786869c95d1962121af0d7df2542009Arend van Spriel return skb->ip_summed == CHECKSUM_PARTIAL; 2695b435de0d786869c95d1962121af0d7df2542009Arend van Spriel} 2705b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 2715b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstatic void pkt_set_sum_good(struct sk_buff *skb, bool x) 2725b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{ 2735b435de0d786869c95d1962121af0d7df2542009Arend van Spriel skb->ip_summed = (x ? CHECKSUM_UNNECESSARY : CHECKSUM_NONE); 2745b435de0d786869c95d1962121af0d7df2542009Arend van Spriel} 2755b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 2768f0c3b6d44e09f497f57ca2997d903c5602336a1Arend van Sprielvoid brcmf_proto_hdrpush(struct brcmf_pub *drvr, int ifidx, u8 offset, 2775b435de0d786869c95d1962121af0d7df2542009Arend van Spriel struct sk_buff *pktbuf) 2785b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{ 2795b435de0d786869c95d1962121af0d7df2542009Arend van Spriel struct brcmf_proto_bdc_header *h; 2805b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 281a6cfb1477d23aa251a1a4b0285fb385d94cdde51Hante Meuleman brcmf_dbg(CDC, "Enter\n"); 2825b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 2835b435de0d786869c95d1962121af0d7df2542009Arend van Spriel /* Push BDC header used to convey priority for buses that don't */ 2845b435de0d786869c95d1962121af0d7df2542009Arend van Spriel skb_push(pktbuf, BDC_HEADER_LEN); 2855b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 2865b435de0d786869c95d1962121af0d7df2542009Arend van Spriel h = (struct brcmf_proto_bdc_header *)(pktbuf->data); 2875b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 2885b435de0d786869c95d1962121af0d7df2542009Arend van Spriel h->flags = (BDC_PROTO_VER << BDC_FLAG_VER_SHIFT); 2895b435de0d786869c95d1962121af0d7df2542009Arend van Spriel if (pkt_sum_needed(pktbuf)) 2905b435de0d786869c95d1962121af0d7df2542009Arend van Spriel h->flags |= BDC_FLAG_SUM_NEEDED; 2915b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 2925b435de0d786869c95d1962121af0d7df2542009Arend van Spriel h->priority = (pktbuf->priority & BDC_PRIORITY_MASK); 2935b435de0d786869c95d1962121af0d7df2542009Arend van Spriel h->flags2 = 0; 2948f0c3b6d44e09f497f57ca2997d903c5602336a1Arend van Spriel h->data_offset = offset; 2955b435de0d786869c95d1962121af0d7df2542009Arend van Spriel BDC_SET_IF_IDX(h, ifidx); 296ea0737d6e24b44b632e9094108bb987b0338ea74Arend van Spriel trace_brcmf_bdchdr(pktbuf->data); 2975b435de0d786869c95d1962121af0d7df2542009Arend van Spriel} 2985b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 299349e7104ff662eeacca1fffbb480c287562c45a1Arend van Sprielint brcmf_proto_hdrpull(struct brcmf_pub *drvr, bool do_fws, u8 *ifidx, 3005b435de0d786869c95d1962121af0d7df2542009Arend van Spriel struct sk_buff *pktbuf) 3015b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{ 3025b435de0d786869c95d1962121af0d7df2542009Arend van Spriel struct brcmf_proto_bdc_header *h; 3035b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 304a6cfb1477d23aa251a1a4b0285fb385d94cdde51Hante Meuleman brcmf_dbg(CDC, "Enter\n"); 3055b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 3065b435de0d786869c95d1962121af0d7df2542009Arend van Spriel /* Pop BDC header used to convey priority for buses that don't */ 3075b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 3085cd02c7747596078e6b01f07f978fa8a10f4e9daPiotr Haber if (pktbuf->len <= BDC_HEADER_LEN) { 3095cd02c7747596078e6b01f07f978fa8a10f4e9daPiotr Haber brcmf_dbg(INFO, "rx data too short (%d <= %d)\n", 3105b435de0d786869c95d1962121af0d7df2542009Arend van Spriel pktbuf->len, BDC_HEADER_LEN); 3115b435de0d786869c95d1962121af0d7df2542009Arend van Spriel return -EBADE; 3125b435de0d786869c95d1962121af0d7df2542009Arend van Spriel } 3135b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 314ea0737d6e24b44b632e9094108bb987b0338ea74Arend van Spriel trace_brcmf_bdchdr(pktbuf->data); 3155b435de0d786869c95d1962121af0d7df2542009Arend van Spriel h = (struct brcmf_proto_bdc_header *)(pktbuf->data); 3165b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 3175b435de0d786869c95d1962121af0d7df2542009Arend van Spriel *ifidx = BDC_GET_IF_IDX(h); 3185b435de0d786869c95d1962121af0d7df2542009Arend van Spriel if (*ifidx >= BRCMF_MAX_IFS) { 3195e8149f5036afe2d94b5fafc9ff752283804a752Arend van Spriel brcmf_err("rx data ifnum out of range (%d)\n", *ifidx); 3205b435de0d786869c95d1962121af0d7df2542009Arend van Spriel return -EBADE; 3215b435de0d786869c95d1962121af0d7df2542009Arend van Spriel } 3222880b86859967af710c72f7d34fb421a86a71e22Hante Meuleman /* The ifidx is the idx to map to matching netdev/ifp. When receiving 3232880b86859967af710c72f7d34fb421a86a71e22Hante Meuleman * events this is easy because it contains the bssidx which maps 3242880b86859967af710c72f7d34fb421a86a71e22Hante Meuleman * 1-on-1 to the netdev/ifp. But for data frames the ifidx is rcvd. 3252880b86859967af710c72f7d34fb421a86a71e22Hante Meuleman * bssidx 1 is used for p2p0 and no data can be received or 3262880b86859967af710c72f7d34fb421a86a71e22Hante Meuleman * transmitted on it. Therefor bssidx is ifidx + 1 if ifidx > 0 3272880b86859967af710c72f7d34fb421a86a71e22Hante Meuleman */ 3282880b86859967af710c72f7d34fb421a86a71e22Hante Meuleman if (*ifidx) 3292880b86859967af710c72f7d34fb421a86a71e22Hante Meuleman (*ifidx)++; 3305b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 3315b435de0d786869c95d1962121af0d7df2542009Arend van Spriel if (((h->flags & BDC_FLAG_VER_MASK) >> BDC_FLAG_VER_SHIFT) != 3325b435de0d786869c95d1962121af0d7df2542009Arend van Spriel BDC_PROTO_VER) { 3335e8149f5036afe2d94b5fafc9ff752283804a752Arend van Spriel brcmf_err("%s: non-BDC packet received, flags 0x%x\n", 3345b435de0d786869c95d1962121af0d7df2542009Arend van Spriel brcmf_ifname(drvr, *ifidx), h->flags); 3355b435de0d786869c95d1962121af0d7df2542009Arend van Spriel return -EBADE; 3365b435de0d786869c95d1962121af0d7df2542009Arend van Spriel } 3375b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 3385b435de0d786869c95d1962121af0d7df2542009Arend van Spriel if (h->flags & BDC_FLAG_SUM_GOOD) { 339a6cfb1477d23aa251a1a4b0285fb385d94cdde51Hante Meuleman brcmf_dbg(CDC, "%s: BDC rcv, good checksum, flags 0x%x\n", 3405b435de0d786869c95d1962121af0d7df2542009Arend van Spriel brcmf_ifname(drvr, *ifidx), h->flags); 3415b435de0d786869c95d1962121af0d7df2542009Arend van Spriel pkt_set_sum_good(pktbuf, true); 3425b435de0d786869c95d1962121af0d7df2542009Arend van Spriel } 3435b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 3445b435de0d786869c95d1962121af0d7df2542009Arend van Spriel pktbuf->priority = h->priority & BDC_PRIORITY_MASK; 3455b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 3465b435de0d786869c95d1962121af0d7df2542009Arend van Spriel skb_pull(pktbuf, BDC_HEADER_LEN); 347349e7104ff662eeacca1fffbb480c287562c45a1Arend van Spriel if (do_fws) 348349e7104ff662eeacca1fffbb480c287562c45a1Arend van Spriel brcmf_fws_hdrpull(drvr, *ifidx, h->data_offset << 2, pktbuf); 349349e7104ff662eeacca1fffbb480c287562c45a1Arend van Spriel else 350349e7104ff662eeacca1fffbb480c287562c45a1Arend van Spriel skb_pull(pktbuf, h->data_offset << 2); 3515b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 352a43af515f6252e62604a1bfc139d43fa43ef5b6fArend van Spriel if (pktbuf->len == 0) 353a43af515f6252e62604a1bfc139d43fa43ef5b6fArend van Spriel return -ENODATA; 3545b435de0d786869c95d1962121af0d7df2542009Arend van Spriel return 0; 3555b435de0d786869c95d1962121af0d7df2542009Arend van Spriel} 3565b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 3575b435de0d786869c95d1962121af0d7df2542009Arend van Sprielint brcmf_proto_attach(struct brcmf_pub *drvr) 3585b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{ 3595b435de0d786869c95d1962121af0d7df2542009Arend van Spriel struct brcmf_proto *cdc; 3605b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 3615b435de0d786869c95d1962121af0d7df2542009Arend van Spriel cdc = kzalloc(sizeof(struct brcmf_proto), GFP_ATOMIC); 3625b435de0d786869c95d1962121af0d7df2542009Arend van Spriel if (!cdc) 3635b435de0d786869c95d1962121af0d7df2542009Arend van Spriel goto fail; 3645b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 3655b435de0d786869c95d1962121af0d7df2542009Arend van Spriel /* ensure that the msg buf directly follows the cdc msg struct */ 3665b435de0d786869c95d1962121af0d7df2542009Arend van Spriel if ((unsigned long)(&cdc->msg + 1) != (unsigned long)cdc->buf) { 3675e8149f5036afe2d94b5fafc9ff752283804a752Arend van Spriel brcmf_err("struct brcmf_proto is not correctly defined\n"); 3685b435de0d786869c95d1962121af0d7df2542009Arend van Spriel goto fail; 3695b435de0d786869c95d1962121af0d7df2542009Arend van Spriel } 3705b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 3715b435de0d786869c95d1962121af0d7df2542009Arend van Spriel drvr->prot = cdc; 372bee1b848877b9e4512bdda480f73cda12b593e2fArend van Spriel drvr->hdrlen += BDC_HEADER_LEN + BRCMF_PROT_FW_SIGNAL_MAX_TXBYTES; 373b01a6b3ca714e2bb86ee387aee487c7360363c93Franky Lin drvr->bus_if->maxctl = BRCMF_DCMD_MAXLEN + 3745b435de0d786869c95d1962121af0d7df2542009Arend van Spriel sizeof(struct brcmf_proto_cdc_dcmd) + ROUND_UP_MARGIN; 3755b435de0d786869c95d1962121af0d7df2542009Arend van Spriel return 0; 3765b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 3775b435de0d786869c95d1962121af0d7df2542009Arend van Sprielfail: 3785b435de0d786869c95d1962121af0d7df2542009Arend van Spriel kfree(cdc); 3795b435de0d786869c95d1962121af0d7df2542009Arend van Spriel return -ENOMEM; 3805b435de0d786869c95d1962121af0d7df2542009Arend van Spriel} 3815b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 3825b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/* ~NOTE~ What if another thread is waiting on the semaphore? Holding it? */ 3835b435de0d786869c95d1962121af0d7df2542009Arend van Sprielvoid brcmf_proto_detach(struct brcmf_pub *drvr) 3845b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{ 3855b435de0d786869c95d1962121af0d7df2542009Arend van Spriel kfree(drvr->prot); 3865b435de0d786869c95d1962121af0d7df2542009Arend van Spriel drvr->prot = NULL; 3875b435de0d786869c95d1962121af0d7df2542009Arend van Spriel} 3885b435de0d786869c95d1962121af0d7df2542009Arend van Spriel 3895b435de0d786869c95d1962121af0d7df2542009Arend van Sprielvoid brcmf_proto_stop(struct brcmf_pub *drvr) 3905b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{ 3915b435de0d786869c95d1962121af0d7df2542009Arend van Spriel /* Nothing to do for CDC */ 3925b435de0d786869c95d1962121af0d7df2542009Arend van Spriel} 393