|
@@ -533,9 +533,11 @@ struct brcmf_sdio {
|
|
|
u8 *rxbuf; /* Buffer for receiving control packets */
|
|
|
uint rxblen; /* Allocated length of rxbuf */
|
|
|
u8 *rxctl; /* Aligned pointer into rxbuf */
|
|
|
+ u8 *rxctl_orig; /* pointer for freeing rxctl */
|
|
|
u8 *databuf; /* Buffer for receiving big glom packet */
|
|
|
u8 *dataptr; /* Aligned pointer into databuf */
|
|
|
uint rxlen; /* Length of valid data in buffer */
|
|
|
+ spinlock_t rxctl_lock; /* protection lock for ctrl frame resources */
|
|
|
|
|
|
u8 sdpcm_ver; /* Bus protocol reported by dongle */
|
|
|
|
|
@@ -1442,21 +1444,24 @@ static void
|
|
|
brcmf_sdbrcm_read_control(struct brcmf_sdio *bus, u8 *hdr, uint len, uint doff)
|
|
|
{
|
|
|
uint rdlen, pad;
|
|
|
-
|
|
|
+ u8 *buf = NULL, *rbuf;
|
|
|
int sdret;
|
|
|
|
|
|
brcmf_dbg(TRACE, "Enter\n");
|
|
|
|
|
|
- /* Set rxctl for frame (w/optional alignment) */
|
|
|
- bus->rxctl = bus->rxbuf;
|
|
|
- bus->rxctl += BRCMF_FIRSTREAD;
|
|
|
- pad = ((unsigned long)bus->rxctl % BRCMF_SDALIGN);
|
|
|
+ if (bus->rxblen)
|
|
|
+ buf = vzalloc(bus->rxblen);
|
|
|
+ if (!buf) {
|
|
|
+ brcmf_dbg(ERROR, "no memory for control frame\n");
|
|
|
+ goto done;
|
|
|
+ }
|
|
|
+ rbuf = bus->rxbuf;
|
|
|
+ pad = ((unsigned long)rbuf % BRCMF_SDALIGN);
|
|
|
if (pad)
|
|
|
- bus->rxctl += (BRCMF_SDALIGN - pad);
|
|
|
- bus->rxctl -= BRCMF_FIRSTREAD;
|
|
|
+ rbuf += (BRCMF_SDALIGN - pad);
|
|
|
|
|
|
/* Copy the already-read portion over */
|
|
|
- memcpy(bus->rxctl, hdr, BRCMF_FIRSTREAD);
|
|
|
+ memcpy(buf, hdr, BRCMF_FIRSTREAD);
|
|
|
if (len <= BRCMF_FIRSTREAD)
|
|
|
goto gotpkt;
|
|
|
|
|
@@ -1493,11 +1498,11 @@ brcmf_sdbrcm_read_control(struct brcmf_sdio *bus, u8 *hdr, uint len, uint doff)
|
|
|
goto done;
|
|
|
}
|
|
|
|
|
|
- /* Read remainder of frame body into the rxctl buffer */
|
|
|
+ /* Read remain of frame body */
|
|
|
sdret = brcmf_sdcard_recv_buf(bus->sdiodev,
|
|
|
bus->sdiodev->sbwad,
|
|
|
SDIO_FUNC_2,
|
|
|
- F2SYNC, (bus->rxctl + BRCMF_FIRSTREAD), rdlen);
|
|
|
+ F2SYNC, rbuf, rdlen);
|
|
|
bus->sdcnt.f2rxdata++;
|
|
|
|
|
|
/* Control frame failures need retransmission */
|
|
@@ -1507,16 +1512,26 @@ brcmf_sdbrcm_read_control(struct brcmf_sdio *bus, u8 *hdr, uint len, uint doff)
|
|
|
bus->sdcnt.rxc_errors++;
|
|
|
brcmf_sdbrcm_rxfail(bus, true, true);
|
|
|
goto done;
|
|
|
- }
|
|
|
+ } else
|
|
|
+ memcpy(buf + BRCMF_FIRSTREAD, rbuf, rdlen);
|
|
|
|
|
|
gotpkt:
|
|
|
|
|
|
brcmf_dbg_hex_dump(BRCMF_BYTES_ON() && BRCMF_CTL_ON(),
|
|
|
- bus->rxctl, len, "RxCtrl:\n");
|
|
|
+ buf, len, "RxCtrl:\n");
|
|
|
|
|
|
/* Point to valid data and indicate its length */
|
|
|
- bus->rxctl += doff;
|
|
|
+ spin_lock_bh(&bus->rxctl_lock);
|
|
|
+ if (bus->rxctl) {
|
|
|
+ brcmf_dbg(ERROR, "last control frame is being processed.\n");
|
|
|
+ spin_unlock_bh(&bus->rxctl_lock);
|
|
|
+ vfree(buf);
|
|
|
+ goto done;
|
|
|
+ }
|
|
|
+ bus->rxctl = buf + doff;
|
|
|
+ bus->rxctl_orig = buf;
|
|
|
bus->rxlen = len - doff;
|
|
|
+ spin_unlock_bh(&bus->rxctl_lock);
|
|
|
|
|
|
done:
|
|
|
/* Awake any waiters */
|
|
@@ -2023,7 +2038,9 @@ static void brcmf_sdbrcm_bus_stop(struct device *dev)
|
|
|
brcmf_sdbrcm_free_glom(bus);
|
|
|
|
|
|
/* Clear rx control and wake any waiters */
|
|
|
+ spin_lock_bh(&bus->rxctl_lock);
|
|
|
bus->rxlen = 0;
|
|
|
+ spin_unlock_bh(&bus->rxctl_lock);
|
|
|
brcmf_sdbrcm_dcmd_resp_wake(bus);
|
|
|
|
|
|
/* Reset some F2 state stuff */
|
|
@@ -2989,6 +3006,7 @@ brcmf_sdbrcm_bus_rxctl(struct device *dev, unsigned char *msg, uint msglen)
|
|
|
int timeleft;
|
|
|
uint rxlen = 0;
|
|
|
bool pending;
|
|
|
+ u8 *buf;
|
|
|
struct brcmf_bus *bus_if = dev_get_drvdata(dev);
|
|
|
struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
|
|
|
struct brcmf_sdio *bus = sdiodev->bus;
|
|
@@ -2998,11 +3016,15 @@ brcmf_sdbrcm_bus_rxctl(struct device *dev, unsigned char *msg, uint msglen)
|
|
|
/* Wait until control frame is available */
|
|
|
timeleft = brcmf_sdbrcm_dcmd_resp_wait(bus, &bus->rxlen, &pending);
|
|
|
|
|
|
- down(&bus->sdsem);
|
|
|
+ spin_lock_bh(&bus->rxctl_lock);
|
|
|
rxlen = bus->rxlen;
|
|
|
memcpy(msg, bus->rxctl, min(msglen, rxlen));
|
|
|
+ bus->rxctl = NULL;
|
|
|
+ buf = bus->rxctl_orig;
|
|
|
+ bus->rxctl_orig = NULL;
|
|
|
bus->rxlen = 0;
|
|
|
- up(&bus->sdsem);
|
|
|
+ spin_unlock_bh(&bus->rxctl_lock);
|
|
|
+ vfree(buf);
|
|
|
|
|
|
if (rxlen) {
|
|
|
brcmf_dbg(CTL, "resumed on rxctl frame, got %d expected %d\n",
|
|
@@ -3860,6 +3882,7 @@ void *brcmf_sdbrcm_probe(u32 regsva, struct brcmf_sdio_dev *sdiodev)
|
|
|
goto fail;
|
|
|
}
|
|
|
|
|
|
+ spin_lock_init(&bus->rxctl_lock);
|
|
|
spin_lock_init(&bus->txqlock);
|
|
|
init_waitqueue_head(&bus->ctrl_wait);
|
|
|
init_waitqueue_head(&bus->dcmd_resp_wait);
|