static int mmc_submit_one(struct mmc_data *md, struct mmc_request *mr, struct mmc_command *mc, int sg_cnt, int req_sz, int func_blk_sz, u32 *addr, struct brcmf_sdio_dev *sdiodev, struct sdio_func *func, int write) { int ret; md->sg_len = sg_cnt; md->blocks = req_sz / func_blk_sz; mc->arg |= (*addr & 0x1FFFF) << 9; /* address */ mc->arg |= md->blocks & 0x1FF; /* block count */ /* incrementing addr for function 1 */ if (func->num == 1) *addr += req_sz; mmc_set_data_timeout(md, func->card); mmc_wait_for_req(func->card->host, mr); ret = mc->error ? mc->error : md->error; if (ret == -ENOMEDIUM) { brcmf_sdiod_change_state(sdiodev, BRCMF_SDIOD_NOMEDIUM); } else if (ret != 0) { brcmf_err("CMD53 sg block %s failed %d\n", write ? "write" : "read", ret); ret = -EIO; } return ret; }
static int brcmf_sdiod_skbuff_read(struct brcmf_sdio_dev *sdiodev, struct sdio_func *func, u32 addr, struct sk_buff *skb) { unsigned int req_sz; int err; /* Single skb use the standard mmc interface */ req_sz = skb->len + 3; req_sz &= (uint)~3; switch (func->num) { case 1: err = sdio_memcpy_fromio(func, ((u8 *)(skb->data)), addr, req_sz); break; case 2: err = sdio_readsb(func, ((u8 *)(skb->data)), addr, req_sz); break; default: /* bail out as things are really fishy here */ WARN(1, "invalid sdio function number: %d\n", func->num); err = -ENOMEDIUM; } if (err == -ENOMEDIUM) brcmf_sdiod_change_state(sdiodev, BRCMF_SDIOD_NOMEDIUM); return err; }
static int brcmf_sdiod_skbuff_write(struct brcmf_sdio_dev *sdiodev, struct sdio_func *func, u32 addr, struct sk_buff *skb) { unsigned int req_sz; int err; /* Single skb use the standard mmc interface */ req_sz = skb->len + 3; req_sz &= (uint)~3; err = sdio_memcpy_toio(func, addr, ((u8 *)(skb->data)), req_sz); if (err == -ENOMEDIUM) brcmf_sdiod_change_state(sdiodev, BRCMF_SDIOD_NOMEDIUM); return err; }
/** * brcmf_sdiod_sglist_rw - SDIO interface function for block data access * @sdiodev: brcmfmac sdio device * @func: SDIO function * @write: direction flag * @addr: dongle memory address as source/destination * @pkt: skb pointer * * This function takes the respbonsibility as the interface function to MMC * stack for block data access. It assumes that the skb passed down by the * caller has already been padded and aligned. */ static int brcmf_sdiod_sglist_rw(struct brcmf_sdio_dev *sdiodev, struct sdio_func *func, bool write, u32 addr, struct sk_buff_head *pktlist) { unsigned int req_sz, func_blk_sz, sg_cnt, sg_data_sz, pkt_offset; unsigned int max_req_sz, orig_offset, dst_offset; unsigned short max_seg_cnt, seg_sz; unsigned char *pkt_data, *orig_data, *dst_data; struct sk_buff *pkt_next = NULL, *local_pkt_next; struct sk_buff_head local_list, *target_list; struct mmc_request mmc_req; struct mmc_command mmc_cmd; struct mmc_data mmc_dat; struct scatterlist *sgl; int ret = 0; if (!pktlist->qlen) return -EINVAL; target_list = pktlist; /* for host with broken sg support, prepare a page aligned list */ __skb_queue_head_init(&local_list); if (!write && sdiodev->settings->bus.sdio.broken_sg_support) { req_sz = 0; skb_queue_walk(pktlist, pkt_next) req_sz += pkt_next->len; req_sz = ALIGN(req_sz, func->cur_blksize); while (req_sz > PAGE_SIZE) { pkt_next = brcmu_pkt_buf_get_skb(PAGE_SIZE); if (pkt_next == NULL) { ret = -ENOMEM; goto exit; } __skb_queue_tail(&local_list, pkt_next); req_sz -= PAGE_SIZE; } pkt_next = brcmu_pkt_buf_get_skb(req_sz); if (pkt_next == NULL) { ret = -ENOMEM; goto exit; } __skb_queue_tail(&local_list, pkt_next); target_list = &local_list; } func_blk_sz = func->cur_blksize; max_req_sz = sdiodev->max_request_size; max_seg_cnt = min_t(unsigned short, sdiodev->max_segment_count, target_list->qlen); seg_sz = target_list->qlen; pkt_offset = 0; pkt_next = target_list->next; memset(&mmc_req, 0, sizeof(struct mmc_request)); memset(&mmc_cmd, 0, sizeof(struct mmc_command)); memset(&mmc_dat, 0, sizeof(struct mmc_data)); mmc_dat.sg = sdiodev->sgtable.sgl; mmc_dat.blksz = func_blk_sz; mmc_dat.flags = write ? MMC_DATA_WRITE : MMC_DATA_READ; mmc_cmd.opcode = SD_IO_RW_EXTENDED; mmc_cmd.arg = write ? 1<<31 : 0; /* write flag */ mmc_cmd.arg |= (func->num & 0x7) << 28; /* SDIO func num */ mmc_cmd.arg |= 1 << 27; /* block mode */ /* for function 1 the addr will be incremented */ mmc_cmd.arg |= (func->num == 1) ? 1 << 26 : 0; mmc_cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_ADTC; mmc_req.cmd = &mmc_cmd; mmc_req.data = &mmc_dat; while (seg_sz) { req_sz = 0; sg_cnt = 0; sgl = sdiodev->sgtable.sgl; /* prep sg table */ while (pkt_next != (struct sk_buff *)target_list) { pkt_data = pkt_next->data + pkt_offset; sg_data_sz = pkt_next->len - pkt_offset; if (sg_data_sz > sdiodev->max_segment_size) sg_data_sz = sdiodev->max_segment_size; if (sg_data_sz > max_req_sz - req_sz) sg_data_sz = max_req_sz - req_sz; sg_set_buf(sgl, pkt_data, sg_data_sz); sg_cnt++; sgl = sg_next(sgl); req_sz += sg_data_sz; pkt_offset += sg_data_sz; if (pkt_offset == pkt_next->len) { pkt_offset = 0; pkt_next = pkt_next->next; } if (req_sz >= max_req_sz || sg_cnt >= max_seg_cnt) break; } seg_sz -= sg_cnt; if (req_sz % func_blk_sz != 0) { brcmf_err("sg request length %u is not %u aligned\n", req_sz, func_blk_sz); ret = -ENOTBLK; goto exit; } mmc_dat.sg_len = sg_cnt; mmc_dat.blocks = req_sz / func_blk_sz; mmc_cmd.arg |= (addr & 0x1FFFF) << 9; /* address */ mmc_cmd.arg |= mmc_dat.blocks & 0x1FF; /* block count */ /* incrementing addr for function 1 */ if (func->num == 1) addr += req_sz; mmc_set_data_timeout(&mmc_dat, func->card); mmc_wait_for_req(func->card->host, &mmc_req); ret = mmc_cmd.error ? mmc_cmd.error : mmc_dat.error; if (ret == -ENOMEDIUM) { brcmf_sdiod_change_state(sdiodev, BRCMF_SDIOD_NOMEDIUM); break; } else if (ret != 0) { brcmf_err("CMD53 sg block %s failed %d\n", write ? "write" : "read", ret); ret = -EIO; break; } } if (!write && sdiodev->settings->bus.sdio.broken_sg_support) { local_pkt_next = local_list.next; orig_offset = 0; skb_queue_walk(pktlist, pkt_next) { dst_offset = 0; do { req_sz = local_pkt_next->len - orig_offset; req_sz = min_t(uint, pkt_next->len - dst_offset, req_sz); orig_data = local_pkt_next->data + orig_offset; dst_data = pkt_next->data + dst_offset; memcpy(dst_data, orig_data, req_sz); orig_offset += req_sz; dst_offset += req_sz; if (orig_offset == local_pkt_next->len) { orig_offset = 0; local_pkt_next = local_pkt_next->next; } if (dst_offset == pkt_next->len) break; } while (!skb_queue_empty(&local_list)); }