/** * @brief This function reads data from the card. * * @param priv A pointer to bt_private structure * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE */ static int sd_card_to_host(bt_private * priv) { int ret = BT_STATUS_SUCCESS; u16 buf_len = 0; int buf_block_len; int blksz; struct sk_buff *skb = NULL; u32 type; u8 *payload = NULL; struct hci_dev *hdev = priv->bt_dev.hcidev; struct sdio_mmc_card *card = priv->bt_dev.card; ENTER(); if (!card || !card->func) { PRINTM(ERROR, "card or function is NULL!\n"); ret = BT_STATUS_FAILURE; goto exit; } /* Read the length of data to be transferred */ ret = sd_read_rx_len(priv, &buf_len); if (ret < 0) { PRINTM(ERROR, "card_to_host, read scratch reg failed\n"); ret = BT_STATUS_FAILURE; goto exit; } /* Allocate buffer */ blksz = SD_BLOCK_SIZE; buf_block_len = (buf_len + blksz - 1) / blksz; if (buf_len <= BT_HEADER_LEN || (buf_block_len * blksz) > ALLOC_BUF_SIZE) { PRINTM(ERROR, "card_to_host, invalid packet length: %d\n", buf_len); ret = BT_STATUS_FAILURE; goto exit; } skb = bt_skb_alloc(buf_block_len * blksz + PXA3XX_DMA_ALIGNMENT, GFP_ATOMIC); if (skb == NULL) { PRINTM(WARN, "No free skb\n"); goto exit; } if ((u32) skb->data & (PXA3XX_DMA_ALIGNMENT - 1)) { skb_put(skb, (u32) skb->data & (PXA3XX_DMA_ALIGNMENT - 1)); skb_pull(skb, (u32) skb->data & (PXA3XX_DMA_ALIGNMENT - 1)); } payload = skb->tail; ret = sdio_readsb(card->func, payload, priv->bt_dev.ioport, buf_block_len * blksz); if (ret < 0) { PRINTM(ERROR, "card_to_host, read iomem failed: %d\n", ret); ret = BT_STATUS_FAILURE; goto exit; } DBG_HEXDUMP(DAT_D, "SDIO Blk Rd", payload, blksz * buf_block_len); /* This is SDIO specific header length: byte[2][1][0], type: byte[3] (HCI_COMMAND = 1, ACL_DATA = 2, SCO_DATA = 3, 0xFE = Vendor) */ buf_len = payload[0]; buf_len |= (u16) payload[1] << 8; type = payload[3]; PRINTM(DATA, "SDIO Blk Rd dev%d: len=%d type=%d\n", hdev->id, buf_len, type); switch (type) { case HCI_ACLDATA_PKT: case HCI_SCODATA_PKT: case HCI_EVENT_PKT: bt_cb(skb)->pkt_type = type; skb->dev = (void *) hdev; skb_put(skb, buf_len); skb_pull(skb, BT_HEADER_LEN); if (type == HCI_EVENT_PKT) check_evtpkt(priv, skb); hci_recv_frame(skb); hdev->stat.byte_rx += buf_len; break; case MRVL_VENDOR_PKT: bt_cb(skb)->pkt_type = HCI_VENDOR_PKT; skb->dev = (void *) hdev; skb_put(skb, buf_len); skb_pull(skb, BT_HEADER_LEN); if (BT_STATUS_SUCCESS != bt_process_event(priv, skb)) hci_recv_frame(skb); hdev->stat.byte_rx += buf_len; break; default: /* Driver specified event and command resp should be handle here */ PRINTM(INFO, "Unknow PKT type:%d\n", type); kfree_skb(skb); skb = NULL; break; } exit: if (ret) { hdev->stat.err_rx++; if (skb) kfree_skb(skb); } LEAVE(); return ret; }
/** * @brief This function reads data from the card. * * @param priv A pointer to bt_private structure * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE */ static int sd_card_to_host(bt_private *priv) { int ret = BT_STATUS_SUCCESS; u16 buf_len = 0; int buf_block_len; int blksz; struct sk_buff *skb = NULL; u32 type; u8 *payload = NULL; struct mbt_dev *mbt_dev = NULL; struct m_dev *mdev_bt = &(priv->bt_dev.m_dev[BT_SEQ]); struct m_dev *mdev_fm = &(priv->bt_dev.m_dev[FM_SEQ]); struct m_dev *mdev_nfc = &(priv->bt_dev.m_dev[NFC_SEQ]); struct nfc_dev *nfc_dev = (struct nfc_dev *)priv->bt_dev.m_dev[NFC_SEQ].dev_pointer; struct fm_dev *fm_dev = (struct fm_dev *)priv->bt_dev.m_dev[FM_SEQ].dev_pointer; struct m_dev *mdev_debug = &(priv->bt_dev.m_dev[DEBUG_SEQ]); struct debug_dev *debug_dev = (struct debug_dev *)priv->bt_dev.m_dev[DEBUG_SEQ].dev_pointer; struct sdio_mmc_card *card = priv->bt_dev.card; ENTER(); if (priv->bt_dev.m_dev[BT_SEQ].spec_type != BLUEZ_SPEC) mbt_dev = (struct mbt_dev *)priv->bt_dev.m_dev[BT_SEQ].dev_pointer; if (!card || !card->func) { PRINTM(ERROR, "BT: card or function is NULL!\n"); ret = BT_STATUS_FAILURE; goto exit; } /* Read the length of data to be transferred */ ret = sd_read_rx_len(priv, &buf_len); if (ret < 0) { PRINTM(ERROR, "BT: card_to_host, read scratch reg failed\n"); ret = BT_STATUS_FAILURE; goto exit; } /* Allocate buffer */ blksz = SD_BLOCK_SIZE; buf_block_len = (buf_len + blksz - 1) / blksz; if (buf_len <= BT_HEADER_LEN || (buf_block_len * blksz) > ALLOC_BUF_SIZE) { PRINTM(ERROR, "BT: card_to_host, invalid packet length: %d\n", buf_len); ret = BT_STATUS_FAILURE; goto exit; } skb = bt_skb_alloc(buf_block_len * blksz + DMA_ALIGNMENT, GFP_ATOMIC); if (skb == NULL) { PRINTM(WARN, "BT: No free skb\n"); goto exit; } if ((t_ptr)skb->data & (DMA_ALIGNMENT - 1)) { skb_put(skb, DMA_ALIGNMENT - ((t_ptr)skb->data & (DMA_ALIGNMENT - 1))); skb_pull(skb, DMA_ALIGNMENT - ((t_ptr)skb->data & (DMA_ALIGNMENT - 1))); } payload = skb->data; ret = sdio_readsb(card->func, payload, priv->bt_dev.ioport, buf_block_len * blksz); if (ret < 0) { PRINTM(ERROR, "BT: card_to_host, read iomem failed: %d\n", ret); kfree_skb(skb); skb = NULL; ret = BT_STATUS_FAILURE; goto exit; } /* This is SDIO specific header length: byte[2][1][0], * type: byte[3] (HCI_COMMAND = 1, ACL_DATA = 2, SCO_DATA = 3, 0xFE = Vendor) */ buf_len = payload[0]; buf_len |= (u16) payload[1] << 8; type = payload[3]; PRINTM(DATA, "BT: SDIO Blk Rd %s: len=%d type=%d\n", mbt_dev->name, buf_len, type); if (buf_len > buf_block_len * blksz) { PRINTM(ERROR, "BT: Drop invalid rx pkt, len in hdr=%d, cmd53 length=%d\n", buf_len, buf_block_len * blksz); ret = BT_STATUS_FAILURE; kfree_skb(skb); skb = NULL; goto exit; } DBG_HEXDUMP(DAT_D, "BT: SDIO Blk Rd", payload, buf_len); switch (type) { case HCI_ACLDATA_PKT: bt_cb(skb)->pkt_type = type; skb_put(skb, buf_len); skb_pull(skb, BT_HEADER_LEN); if (mbt_dev) { skb->dev = (void *)mdev_bt; mdev_recv_frame(skb); mdev_bt->stat.byte_rx += buf_len; } break; case HCI_SCODATA_PKT: bt_cb(skb)->pkt_type = type; skb_put(skb, buf_len); skb_pull(skb, BT_HEADER_LEN); if (mbt_dev) { skb->dev = (void *)mdev_bt; mdev_recv_frame(skb); mdev_bt->stat.byte_rx += buf_len; } break; case HCI_EVENT_PKT: /** add EVT Demux */ bt_cb(skb)->pkt_type = type; skb_put(skb, buf_len); skb_pull(skb, BT_HEADER_LEN); if (BT_STATUS_SUCCESS == check_evtpkt(priv, skb)) break; switch (skb->data[0]) { case 0x0E: /** cmd complete */ if (priv->debug_device_pending) { if (priv->debug_ocf_ogf[0] == skb->data[3] && priv->debug_ocf_ogf[1] == skb->data[4]) { priv->debug_device_pending = 0; priv->debug_ocf_ogf[0] = 0; priv->debug_ocf_ogf[1] = 0; /** debug cmd complete */ if (debug_dev) { skb->dev = (void *)mdev_debug; mdev_recv_frame(skb); mdev_debug->stat.byte_rx += buf_len; } break; } } if (skb->data[3] == 0x80 && skb->data[4] == 0xFE) { /** FM cmd complete */ if (fm_dev) { skb->dev = (void *)mdev_fm; mdev_recv_frame(skb); mdev_fm->stat.byte_rx += buf_len; } } else if (skb->data[3] == 0x81 && skb->data[4] == 0xFE) { /** NFC cmd complete */ if (nfc_dev) { skb->dev = (void *)mdev_nfc; mdev_recv_frame(skb); mdev_nfc->stat.byte_rx += buf_len; } } else { if (mbt_dev) { skb->dev = (void *)mdev_bt; mdev_recv_frame(skb); mdev_bt->stat.byte_rx += buf_len; } } break; case 0x0F: /** cmd status */ if (skb->data[4] == 0x80 && skb->data[5] == 0xFE) { /** FM cmd ststus */ if (fm_dev) { skb->dev = (void *)mdev_fm; mdev_recv_frame(skb); mdev_fm->stat.byte_rx += buf_len; } } else if (skb->data[4] == 0x81 && skb->data[5] == 0xFE) { /** NFC cmd ststus */ if (nfc_dev) { skb->dev = (void *)mdev_nfc; mdev_recv_frame(skb); mdev_nfc->stat.byte_rx += buf_len; } } else { /** BT cmd status */ if (mbt_dev) { skb->dev = (void *)mdev_bt; mdev_recv_frame(skb); mdev_bt->stat.byte_rx += buf_len; } } break; case 0xFF: /** Vendor specific pkt */ if (skb->data[2] == 0xC0) { /** NFC EVT */ if (nfc_dev) { skb->dev = (void *)mdev_nfc; mdev_recv_frame(skb); mdev_nfc->stat.byte_rx += buf_len; } } else if (skb->data[2] >= 0x80 && skb->data[2] <= 0xAF) { /** FM EVT */ if (fm_dev) { skb->dev = (void *)mdev_fm; mdev_recv_frame(skb); mdev_fm->stat.byte_rx += buf_len; } } else { /** BT EVT */ if (mbt_dev) { skb->dev = (void *)mdev_bt; mdev_recv_frame(skb); mdev_bt->stat.byte_rx += buf_len; } } break; default: /** BT EVT */ if (mbt_dev) { skb->dev = (void *)mdev_bt; mdev_recv_frame(skb); mdev_bt->stat.byte_rx += buf_len; } break; } break; case MRVL_VENDOR_PKT: /* Just think here need to back compatible FM */ bt_cb(skb)->pkt_type = HCI_VENDOR_PKT; skb_put(skb, buf_len); skb_pull(skb, BT_HEADER_LEN); if (mbt_dev) { if (BT_STATUS_SUCCESS != bt_process_event(priv, skb)) { skb->dev = (void *)mdev_bt; mdev_recv_frame(skb); mdev_bt->stat.byte_rx += buf_len; } } break; default: /* Driver specified event and command resp should be handle here */ PRINTM(INFO, "BT: Unknown PKT type:%d\n", type); kfree_skb(skb); skb = NULL; break; } exit: if (ret) { if (mbt_dev) mdev_bt->stat.err_rx++; PRINTM(ERROR, "error when recv pkt!\n"); } LEAVE(); return ret; }