linux/drivers/staging/brcm80211/brcmfmac/dhd_cdc.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2010 Broadcom Corporation
   3 *
   4 * Permission to use, copy, modify, and/or distribute this software for any
   5 * purpose with or without fee is hereby granted, provided that the above
   6 * copyright notice and this permission notice appear in all copies.
   7 *
   8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
   9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
  11 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
  13 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
  14 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  15 */
  16
  17#include <linux/types.h>
  18#include <linux/netdevice.h>
  19#include <bcmdefs.h>
  20
  21#include <bcmutils.h>
  22#include <bcmcdc.h>
  23
  24#include <dngl_stats.h>
  25#include <dhd.h>
  26#include <dhd_proto.h>
  27#include <dhd_bus.h>
  28#include <dhd_dbg.h>
  29#ifdef CUSTOMER_HW2
  30int wifi_get_mac_addr(unsigned char *buf);
  31#endif
  32
  33extern int dhd_preinit_ioctls(dhd_pub_t *dhd);
  34
  35/* Packet alignment for most efficient SDIO (can change based on platform) */
  36#ifndef DHD_SDALIGN
  37#define DHD_SDALIGN     32
  38#endif
  39#if !ISPOWEROF2(DHD_SDALIGN)
  40#error DHD_SDALIGN is not a power of 2!
  41#endif
  42
  43#define RETRIES 2       /* # of retries to retrieve matching ioctl response */
  44#define BUS_HEADER_LEN  (16+DHD_SDALIGN) /* Must be atleast SDPCM_RESERVE
  45                                         * defined in dhd_sdio.c
  46                                         * (amount of header tha might be added)
  47                                         * plus any space that might be needed
  48                                         * for alignment padding.
  49                                         */
  50#define ROUND_UP_MARGIN 2048    /* Biggest SDIO block size possible for
  51                                 * round off at the end of buffer
  52                                 */
  53
  54typedef struct dhd_prot {
  55        u16 reqid;
  56        u8 pending;
  57        u32 lastcmd;
  58        u8 bus_header[BUS_HEADER_LEN];
  59        cdc_ioctl_t msg;
  60        unsigned char buf[WLC_IOCTL_MAXLEN + ROUND_UP_MARGIN];
  61} dhd_prot_t;
  62
  63static int dhdcdc_msg(dhd_pub_t *dhd)
  64{
  65        dhd_prot_t *prot = dhd->prot;
  66        int len = le32_to_cpu(prot->msg.len) + sizeof(cdc_ioctl_t);
  67
  68        DHD_TRACE(("%s: Enter\n", __func__));
  69
  70        /* NOTE : cdc->msg.len holds the desired length of the buffer to be
  71         *        returned. Only up to CDC_MAX_MSG_SIZE of this buffer area
  72         *        is actually sent to the dongle
  73         */
  74        if (len > CDC_MAX_MSG_SIZE)
  75                len = CDC_MAX_MSG_SIZE;
  76
  77        /* Send request */
  78        return dhd_bus_txctl(dhd->bus, (unsigned char *)&prot->msg, len);
  79}
  80
  81static int dhdcdc_cmplt(dhd_pub_t *dhd, u32 id, u32 len)
  82{
  83        int ret;
  84        dhd_prot_t *prot = dhd->prot;
  85
  86        DHD_TRACE(("%s: Enter\n", __func__));
  87
  88        do {
  89                ret =
  90                    dhd_bus_rxctl(dhd->bus, (unsigned char *)&prot->msg,
  91                                  len + sizeof(cdc_ioctl_t));
  92                if (ret < 0)
  93                        break;
  94        } while (CDC_IOC_ID(le32_to_cpu(prot->msg.flags)) != id);
  95
  96        return ret;
  97}
  98
  99int
 100dhdcdc_query_ioctl(dhd_pub_t *dhd, int ifidx, uint cmd, void *buf, uint len)
 101{
 102        dhd_prot_t *prot = dhd->prot;
 103        cdc_ioctl_t *msg = &prot->msg;
 104        void *info;
 105        int ret = 0, retries = 0;
 106        u32 id, flags = 0;
 107
 108        DHD_TRACE(("%s: Enter\n", __func__));
 109        DHD_CTL(("%s: cmd %d len %d\n", __func__, cmd, len));
 110
 111        /* Respond "bcmerror" and "bcmerrorstr" with local cache */
 112        if (cmd == WLC_GET_VAR && buf) {
 113                if (!strcmp((char *)buf, "bcmerrorstr")) {
 114                        strncpy((char *)buf, "bcm_error",
 115                                BCME_STRLEN);
 116                        goto done;
 117                } else if (!strcmp((char *)buf, "bcmerror")) {
 118                        *(int *)buf = dhd->dongle_error;
 119                        goto done;
 120                }
 121        }
 122
 123        memset(msg, 0, sizeof(cdc_ioctl_t));
 124
 125        msg->cmd = cpu_to_le32(cmd);
 126        msg->len = cpu_to_le32(len);
 127        msg->flags = (++prot->reqid << CDCF_IOC_ID_SHIFT);
 128        CDC_SET_IF_IDX(msg, ifidx);
 129        msg->flags = cpu_to_le32(msg->flags);
 130
 131        if (buf)
 132                memcpy(prot->buf, buf, len);
 133
 134        ret = dhdcdc_msg(dhd);
 135        if (ret < 0) {
 136                DHD_ERROR(("dhdcdc_query_ioctl: dhdcdc_msg failed w/status "
 137                        "%d\n", ret));
 138                goto done;
 139        }
 140
 141retry:
 142        /* wait for interrupt and get first fragment */
 143        ret = dhdcdc_cmplt(dhd, prot->reqid, len);
 144        if (ret < 0)
 145                goto done;
 146
 147        flags = le32_to_cpu(msg->flags);
 148        id = (flags & CDCF_IOC_ID_MASK) >> CDCF_IOC_ID_SHIFT;
 149
 150        if ((id < prot->reqid) && (++retries < RETRIES))
 151                goto retry;
 152        if (id != prot->reqid) {
 153                DHD_ERROR(("%s: %s: unexpected request id %d (expected %d)\n",
 154                           dhd_ifname(dhd, ifidx), __func__, id, prot->reqid));
 155                ret = -EINVAL;
 156                goto done;
 157        }
 158
 159        /* Check info buffer */
 160        info = (void *)&msg[1];
 161
 162        /* Copy info buffer */
 163        if (buf) {
 164                if (ret < (int)len)
 165                        len = ret;
 166                memcpy(buf, info, len);
 167        }
 168
 169        /* Check the ERROR flag */
 170        if (flags & CDCF_IOC_ERROR) {
 171                ret = le32_to_cpu(msg->status);
 172                /* Cache error from dongle */
 173                dhd->dongle_error = ret;
 174        }
 175
 176done:
 177        return ret;
 178}
 179
 180int dhdcdc_set_ioctl(dhd_pub_t *dhd, int ifidx, uint cmd, void *buf, uint len)
 181{
 182        dhd_prot_t *prot = dhd->prot;
 183        cdc_ioctl_t *msg = &prot->msg;
 184        int ret = 0;
 185        u32 flags, id;
 186
 187        DHD_TRACE(("%s: Enter\n", __func__));
 188        DHD_CTL(("%s: cmd %d len %d\n", __func__, cmd, len));
 189
 190        memset(msg, 0, sizeof(cdc_ioctl_t));
 191
 192        msg->cmd = cpu_to_le32(cmd);
 193        msg->len = cpu_to_le32(len);
 194        msg->flags = (++prot->reqid << CDCF_IOC_ID_SHIFT) | CDCF_IOC_SET;
 195        CDC_SET_IF_IDX(msg, ifidx);
 196        msg->flags = cpu_to_le32(msg->flags);
 197
 198        if (buf)
 199                memcpy(prot->buf, buf, len);
 200
 201        ret = dhdcdc_msg(dhd);
 202        if (ret < 0)
 203                goto done;
 204
 205        ret = dhdcdc_cmplt(dhd, prot->reqid, len);
 206        if (ret < 0)
 207                goto done;
 208
 209        flags = le32_to_cpu(msg->flags);
 210        id = (flags & CDCF_IOC_ID_MASK) >> CDCF_IOC_ID_SHIFT;
 211
 212        if (id != prot->reqid) {
 213                DHD_ERROR(("%s: %s: unexpected request id %d (expected %d)\n",
 214                           dhd_ifname(dhd, ifidx), __func__, id, prot->reqid));
 215                ret = -EINVAL;
 216                goto done;
 217        }
 218
 219        /* Check the ERROR flag */
 220        if (flags & CDCF_IOC_ERROR) {
 221                ret = le32_to_cpu(msg->status);
 222                /* Cache error from dongle */
 223                dhd->dongle_error = ret;
 224        }
 225
 226done:
 227        return ret;
 228}
 229
 230extern int dhd_bus_interface(struct dhd_bus *bus, uint arg, void *arg2);
 231int
 232dhd_prot_ioctl(dhd_pub_t *dhd, int ifidx, wl_ioctl_t *ioc, void *buf, int len)
 233{
 234        dhd_prot_t *prot = dhd->prot;
 235        int ret = -1;
 236
 237        if (dhd->busstate == DHD_BUS_DOWN) {
 238                DHD_ERROR(("%s : bus is down. we have nothing to do\n",
 239                           __func__));
 240                return ret;
 241        }
 242        dhd_os_proto_block(dhd);
 243
 244        DHD_TRACE(("%s: Enter\n", __func__));
 245
 246        ASSERT(len <= WLC_IOCTL_MAXLEN);
 247
 248        if (len > WLC_IOCTL_MAXLEN)
 249                goto done;
 250
 251        if (prot->pending == true) {
 252                DHD_TRACE(("CDC packet is pending!!!! cmd=0x%x (%lu) "
 253                        "lastcmd=0x%x (%lu)\n",
 254                        ioc->cmd, (unsigned long)ioc->cmd, prot->lastcmd,
 255                        (unsigned long)prot->lastcmd));
 256                if ((ioc->cmd == WLC_SET_VAR) || (ioc->cmd == WLC_GET_VAR))
 257                        DHD_TRACE(("iovar cmd=%s\n", (char *)buf));
 258
 259                goto done;
 260        }
 261
 262        prot->pending = true;
 263        prot->lastcmd = ioc->cmd;
 264        if (ioc->set)
 265                ret = dhdcdc_set_ioctl(dhd, ifidx, ioc->cmd, buf, len);
 266        else {
 267                ret = dhdcdc_query_ioctl(dhd, ifidx, ioc->cmd, buf, len);
 268                if (ret > 0)
 269                        ioc->used = ret - sizeof(cdc_ioctl_t);
 270        }
 271
 272        /* Too many programs assume ioctl() returns 0 on success */
 273        if (ret >= 0)
 274                ret = 0;
 275        else {
 276                cdc_ioctl_t *msg = &prot->msg;
 277                /* len == needed when set/query fails from dongle */
 278                ioc->needed = le32_to_cpu(msg->len);
 279        }
 280
 281        /* Intercept the wme_dp ioctl here */
 282        if ((!ret) && (ioc->cmd == WLC_SET_VAR) && (!strcmp(buf, "wme_dp"))) {
 283                int slen, val = 0;
 284
 285                slen = strlen("wme_dp") + 1;
 286                if (len >= (int)(slen + sizeof(int)))
 287                        memcpy(&val, (char *)buf + slen, sizeof(int));
 288                dhd->wme_dp = (u8) le32_to_cpu(val);
 289        }
 290
 291        prot->pending = false;
 292
 293done:
 294        dhd_os_proto_unblock(dhd);
 295
 296        return ret;
 297}
 298
 299#define PKTSUMNEEDED(skb) \
 300                (((struct sk_buff *)(skb))->ip_summed == CHECKSUM_PARTIAL)
 301#define PKTSETSUMGOOD(skb, x) \
 302                (((struct sk_buff *)(skb))->ip_summed = \
 303                ((x) ? CHECKSUM_UNNECESSARY : CHECKSUM_NONE))
 304
 305/* PKTSETSUMNEEDED and PKTSUMGOOD are not possible because
 306        skb->ip_summed is overloaded */
 307
 308int
 309dhd_prot_iovar_op(dhd_pub_t *dhdp, const char *name,
 310                  void *params, int plen, void *arg, int len, bool set)
 311{
 312        return -ENOTSUPP;
 313}
 314
 315void dhd_prot_dump(dhd_pub_t *dhdp, struct bcmstrbuf *strbuf)
 316{
 317        bcm_bprintf(strbuf, "Protocol CDC: reqid %d\n", dhdp->prot->reqid);
 318}
 319
 320void dhd_prot_hdrpush(dhd_pub_t *dhd, int ifidx, struct sk_buff *pktbuf)
 321{
 322#ifdef BDC
 323        struct bdc_header *h;
 324#endif                          /* BDC */
 325
 326        DHD_TRACE(("%s: Enter\n", __func__));
 327
 328#ifdef BDC
 329        /* Push BDC header used to convey priority for buses that don't */
 330
 331        skb_push(pktbuf, BDC_HEADER_LEN);
 332
 333        h = (struct bdc_header *)(pktbuf->data);
 334
 335        h->flags = (BDC_PROTO_VER << BDC_FLAG_VER_SHIFT);
 336        if (PKTSUMNEEDED(pktbuf))
 337                h->flags |= BDC_FLAG_SUM_NEEDED;
 338
 339        h->priority = (pktbuf->priority & BDC_PRIORITY_MASK);
 340        h->flags2 = 0;
 341        h->rssi = 0;
 342#endif                          /* BDC */
 343        BDC_SET_IF_IDX(h, ifidx);
 344}
 345
 346int dhd_prot_hdrpull(dhd_pub_t *dhd, int *ifidx, struct sk_buff *pktbuf)
 347{
 348#ifdef BDC
 349        struct bdc_header *h;
 350#endif
 351
 352        DHD_TRACE(("%s: Enter\n", __func__));
 353
 354#ifdef BDC
 355        /* Pop BDC header used to convey priority for buses that don't */
 356
 357        if (pktbuf->len < BDC_HEADER_LEN) {
 358                DHD_ERROR(("%s: rx data too short (%d < %d)\n", __func__,
 359                           pktbuf->len, BDC_HEADER_LEN));
 360                return -EBADE;
 361        }
 362
 363        h = (struct bdc_header *)(pktbuf->data);
 364
 365        *ifidx = BDC_GET_IF_IDX(h);
 366        if (*ifidx >= DHD_MAX_IFS) {
 367                DHD_ERROR(("%s: rx data ifnum out of range (%d)\n",
 368                           __func__, *ifidx));
 369                return -EBADE;
 370        }
 371
 372        if (((h->flags & BDC_FLAG_VER_MASK) >> BDC_FLAG_VER_SHIFT) !=
 373            BDC_PROTO_VER) {
 374                DHD_ERROR(("%s: non-BDC packet received, flags 0x%x\n",
 375                           dhd_ifname(dhd, *ifidx), h->flags));
 376                return -EBADE;
 377        }
 378
 379        if (h->flags & BDC_FLAG_SUM_GOOD) {
 380                DHD_INFO(("%s: BDC packet received with good rx-csum, "
 381                        "flags 0x%x\n",
 382                        dhd_ifname(dhd, *ifidx), h->flags));
 383                PKTSETSUMGOOD(pktbuf, true);
 384        }
 385
 386        pktbuf->priority = h->priority & BDC_PRIORITY_MASK;
 387
 388        skb_pull(pktbuf, BDC_HEADER_LEN);
 389#endif                          /* BDC */
 390
 391        return 0;
 392}
 393
 394int dhd_prot_attach(dhd_pub_t *dhd)
 395{
 396        dhd_prot_t *cdc;
 397
 398        cdc = kzalloc(sizeof(dhd_prot_t), GFP_ATOMIC);
 399        if (!cdc) {
 400                DHD_ERROR(("%s: kmalloc failed\n", __func__));
 401                goto fail;
 402        }
 403
 404        /* ensure that the msg buf directly follows the cdc msg struct */
 405        if ((unsigned long)(&cdc->msg + 1) != (unsigned long)cdc->buf) {
 406                DHD_ERROR(("dhd_prot_t is not correctly defined\n"));
 407                goto fail;
 408        }
 409
 410        dhd->prot = cdc;
 411#ifdef BDC
 412        dhd->hdrlen += BDC_HEADER_LEN;
 413#endif
 414        dhd->maxctl = WLC_IOCTL_MAXLEN + sizeof(cdc_ioctl_t) + ROUND_UP_MARGIN;
 415        return 0;
 416
 417fail:
 418        kfree(cdc);
 419        return -ENOMEM;
 420}
 421
 422/* ~NOTE~ What if another thread is waiting on the semaphore?  Holding it? */
 423void dhd_prot_detach(dhd_pub_t *dhd)
 424{
 425        kfree(dhd->prot);
 426        dhd->prot = NULL;
 427}
 428
 429void dhd_prot_dstats(dhd_pub_t *dhd)
 430{
 431        /* No stats from dongle added yet, copy bus stats */
 432        dhd->dstats.tx_packets = dhd->tx_packets;
 433        dhd->dstats.tx_errors = dhd->tx_errors;
 434        dhd->dstats.rx_packets = dhd->rx_packets;
 435        dhd->dstats.rx_errors = dhd->rx_errors;
 436        dhd->dstats.rx_dropped = dhd->rx_dropped;
 437        dhd->dstats.multicast = dhd->rx_multicast;
 438        return;
 439}
 440
 441int dhd_prot_init(dhd_pub_t *dhd)
 442{
 443        int ret = 0;
 444        char buf[128];
 445
 446        DHD_TRACE(("%s: Enter\n", __func__));
 447
 448        dhd_os_proto_block(dhd);
 449
 450        /* Get the device MAC address */
 451        strcpy(buf, "cur_etheraddr");
 452        ret = dhdcdc_query_ioctl(dhd, 0, WLC_GET_VAR, buf, sizeof(buf));
 453        if (ret < 0) {
 454                dhd_os_proto_unblock(dhd);
 455                return ret;
 456        }
 457        memcpy(dhd->mac, buf, ETH_ALEN);
 458
 459        dhd_os_proto_unblock(dhd);
 460
 461#ifdef EMBEDDED_PLATFORM
 462        ret = dhd_preinit_ioctls(dhd);
 463#endif                          /* EMBEDDED_PLATFORM */
 464
 465        /* Always assumes wl for now */
 466        dhd->iswl = true;
 467
 468        return ret;
 469}
 470
 471void dhd_prot_stop(dhd_pub_t *dhd)
 472{
 473        /* Nothing to do for CDC */
 474}
 475