static void hci_h4p_disable_rx(struct hci_h4p_info *info) { if (!info->pm_enabled) return; info->rx_enabled = 0; if (hci_h4p_inb(info, UART_LSR) & UART_LSR_DR) return; if (!(hci_h4p_inb(info, UART_LSR) & UART_LSR_TEMT)) return; __hci_h4p_set_auto_ctsrts(info, 0, UART_EFR_RTS); info->autorts = 0; hci_h4p_set_clk(info, &info->rx_clocks_en, 0); }
static void hci_h4p_rx_tasklet(unsigned long data) { u8 byte; struct hci_h4p_info *info = (struct hci_h4p_info *)data; NBT_DBG("tasklet woke up\n"); NBT_DBG_TRANSFER("rx_tasklet woke up\ndata "); while (hci_h4p_inb(info, UART_LSR) & UART_LSR_DR) { byte = hci_h4p_inb(info, UART_RX); if (info->garbage_bytes) { info->garbage_bytes--; continue; } if (info->rx_skb == NULL) { info->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC | GFP_DMA); if (!info->rx_skb) { dev_err(info->dev, "No memory for new packet\n"); goto finish_rx; } info->rx_state = WAIT_FOR_PKT_TYPE; info->rx_skb->dev = (void *)info->hdev; } info->hdev->stat.byte_rx++; NBT_DBG_TRANSFER_NF("0x%.2x ", byte); hci_h4p_handle_byte(info, byte); } if (!info->rx_enabled) { if (hci_h4p_inb(info, UART_LSR) & UART_LSR_TEMT && info->autorts) { __hci_h4p_set_auto_ctsrts(info, 0 , UART_EFR_RTS); info->autorts = 0; } /* Flush posted write to avoid spurious interrupts */ hci_h4p_inb(info, UART_OMAP_SCR); hci_h4p_set_clk(info, &info->rx_clocks_en, 0); } finish_rx: NBT_DBG_TRANSFER_NF("\n"); NBT_DBG("rx_ended\n"); }
static irqreturn_t hci_h4p_interrupt(int irq, void *data) { struct hci_h4p_info *info = (struct hci_h4p_info *)data; u8 iir, msr; int ret; ret = IRQ_NONE; iir = hci_h4p_inb(info, UART_IIR); if (iir & UART_IIR_NO_INT) { return IRQ_HANDLED; } NBT_DBG("In interrupt handler iir 0x%.2x\n", iir); iir &= UART_IIR_ID; if (iir == UART_IIR_MSI) { msr = hci_h4p_inb(info, UART_MSR); ret = IRQ_HANDLED; } if (iir == UART_IIR_RLSI) { hci_h4p_inb(info, UART_RX); hci_h4p_inb(info, UART_LSR); ret = IRQ_HANDLED; } if (iir == UART_IIR_RDI) { hci_h4p_rx_tasklet((unsigned long)data); ret = IRQ_HANDLED; } if (iir == UART_IIR_THRI) { hci_h4p_tx_tasklet((unsigned long)data); ret = IRQ_HANDLED; } return ret; }
static int hci_h4p_send_negotiation(struct hci_h4p_info *info, struct sk_buff *skb) { unsigned long flags; int err; NBT_DBG("Sending negotiation..\n"); hci_h4p_change_speed(info, INIT_SPEED); hci_h4p_set_rts(info, 1); info->init_error = 0; init_completion(&info->init_completion); skb_queue_tail(&info->txq, skb); spin_lock_irqsave(&info->lock, flags); hci_h4p_outb(info, UART_IER, hci_h4p_inb(info, UART_IER) | UART_IER_THRI); spin_unlock_irqrestore(&info->lock, flags); if (!wait_for_completion_interruptible_timeout(&info->init_completion, msecs_to_jiffies(1000))) return -ETIMEDOUT; if (info->init_error < 0) return info->init_error; /* Change to operational settings */ hci_h4p_set_auto_ctsrts(info, 0, UART_EFR_RTS); hci_h4p_set_rts(info, 0); hci_h4p_change_speed(info, MAX_BAUD_RATE); err = hci_h4p_wait_for_cts(info, 1, 100); if (err < 0) return err; hci_h4p_set_auto_ctsrts(info, 1, UART_EFR_RTS); init_completion(&info->init_completion); err = hci_h4p_send_alive_packet(info); if (err < 0) return err; if (!wait_for_completion_interruptible_timeout(&info->init_completion, msecs_to_jiffies(1000))) return -ETIMEDOUT; if (info->init_error < 0) return info->init_error; NBT_DBG("Negotiation succesful\n"); return 0; }
/* Power management functions */ void hci_h4p_smart_idle(struct hci_h4p_info *info, bool enable) { u8 v; v = hci_h4p_inb(info, UART_OMAP_SYSC); v &= ~(UART_OMAP_SYSC_IDLEMASK); if (enable) v |= UART_OMAP_SYSC_SMART_IDLE; else v |= UART_OMAP_SYSC_NO_IDLE; hci_h4p_outb(info, UART_OMAP_SYSC, v); }
/* Negotiation functions */ int hci_h4p_send_alive_packet(struct hci_h4p_info *info) { unsigned long flags; NBT_DBG("Sending alive packet\n"); if (!info->alive_cmd_skb) return -EINVAL; /* Keep reference to buffer so we can reuse it */ info->alive_cmd_skb = skb_get(info->alive_cmd_skb); skb_queue_tail(&info->txq, info->alive_cmd_skb); spin_lock_irqsave(&info->lock, flags); hci_h4p_outb(info, UART_IER, hci_h4p_inb(info, UART_IER) | UART_IER_THRI); spin_unlock_irqrestore(&info->lock, flags); NBT_DBG("Alive packet sent\n"); return 0; }
void hci_h4p_enable_tx(struct hci_h4p_info *info) { unsigned long flags; NBT_DBG_POWER("\n"); if (!info->pm_enabled) return; spin_lock_irqsave(&info->lock, flags); del_timer(&info->lazy_release); hci_h4p_set_clk(info, &info->tx_clocks_en, 1); info->tx_enabled = 1; gpio_set_value(info->bt_wakeup_gpio, 1); hci_h4p_outb(info, UART_IER, hci_h4p_inb(info, UART_IER) | UART_IER_THRI); /* * Disable smart-idle as UART TX interrupts * are not wake-up capable */ hci_h4p_smart_idle(info, 0); spin_unlock_irqrestore(&info->lock, flags); }
int hci_h4p_bc4_send_fw(struct hci_h4p_info *info, struct sk_buff_head *fw_queue) { static const u8 nokia_oui[3] = {0x00, 0x19, 0x4F}; struct sk_buff *skb; unsigned int offset; int retries, count, i, not_valid; unsigned long flags; info->fw_error = 0; BT_DBG("Sending firmware"); skb = skb_dequeue(fw_queue); if (!skb) return -ENOMSG; /* Check if this is bd_address packet */ if (skb->data[15] == 0x01 && skb->data[16] == 0x00) { offset = 21; skb->data[offset + 1] = 0x00; skb->data[offset + 5] = 0x00; not_valid = 1; for (i = 0; i < 6; i++) { if (info->bd_addr[i] != 0x00) { not_valid = 0; break; } } if (not_valid) { dev_info(info->dev, "Valid bluetooth address not found, setting some random\n"); /* When address is not valid, use some random */ memcpy(info->bd_addr, nokia_oui, 3); get_random_bytes(info->bd_addr + 3, 3); } skb->data[offset + 7] = info->bd_addr[0]; skb->data[offset + 6] = info->bd_addr[1]; skb->data[offset + 4] = info->bd_addr[2]; skb->data[offset + 0] = info->bd_addr[3]; skb->data[offset + 3] = info->bd_addr[4]; skb->data[offset + 2] = info->bd_addr[5]; } for (count = 1; ; count++) { BT_DBG("Sending firmware command %d", count); init_completion(&info->fw_completion); skb_queue_tail(&info->txq, skb); spin_lock_irqsave(&info->lock, flags); hci_h4p_outb(info, UART_IER, hci_h4p_inb(info, UART_IER) | UART_IER_THRI); spin_unlock_irqrestore(&info->lock, flags); skb = skb_dequeue(fw_queue); if (!skb) break; if (!wait_for_completion_timeout(&info->fw_completion, msecs_to_jiffies(1000))) { dev_err(info->dev, "No reply to fw command\n"); return -ETIMEDOUT; } if (info->fw_error) { dev_err(info->dev, "FW error\n"); return -EPROTO; } }; /* Wait for chip warm reset */ retries = 100; while ((!skb_queue_empty(&info->txq) || !(hci_h4p_inb(info, UART_LSR) & UART_LSR_TEMT)) && retries--) { msleep(10); } if (!retries) { dev_err(info->dev, "Transmitter not empty\n"); return -ETIMEDOUT; } hci_h4p_change_speed(info, BC4_MAX_BAUD_RATE); if (hci_h4p_wait_for_cts(info, 1, 100)) { dev_err(info->dev, "cts didn't deassert after final speed\n"); return -ETIMEDOUT; } retries = 100; do { init_completion(&info->init_completion); hci_h4p_send_alive_packet(info); retries--; } while (!wait_for_completion_timeout(&info->init_completion, 100) && retries > 0); if (!retries) { dev_err(info->dev, "No alive reply after speed change\n"); return -ETIMEDOUT; } return 0; }
static void hci_h4p_tx_tasklet(unsigned long data) { unsigned int sent = 0; struct sk_buff *skb; struct hci_h4p_info *info = (struct hci_h4p_info *)data; NBT_DBG("tasklet woke up\n"); NBT_DBG_TRANSFER("tx_tasklet woke up\n data "); if (info->autorts != info->rx_enabled) { if (hci_h4p_inb(info, UART_LSR) & UART_LSR_TEMT) { if (info->autorts && !info->rx_enabled) { __hci_h4p_set_auto_ctsrts(info, 0, UART_EFR_RTS); info->autorts = 0; } if (!info->autorts && info->rx_enabled) { __hci_h4p_set_auto_ctsrts(info, 1, UART_EFR_RTS); info->autorts = 1; } } else { hci_h4p_outb(info, UART_OMAP_SCR, hci_h4p_inb(info, UART_OMAP_SCR) | UART_OMAP_SCR_EMPTY_THR); goto finish_tx; } } skb = skb_dequeue(&info->txq); if (!skb) { /* No data in buffer */ NBT_DBG("skb ready\n"); if (hci_h4p_inb(info, UART_LSR) & UART_LSR_TEMT) { hci_h4p_outb(info, UART_IER, hci_h4p_inb(info, UART_IER) & ~UART_IER_THRI); hci_h4p_inb(info, UART_OMAP_SCR); hci_h4p_disable_tx(info); return; } else hci_h4p_outb(info, UART_OMAP_SCR, hci_h4p_inb(info, UART_OMAP_SCR) | UART_OMAP_SCR_EMPTY_THR); goto finish_tx; } /* Copy data to tx fifo */ while (!(hci_h4p_inb(info, UART_OMAP_SSR) & UART_OMAP_SSR_TXFULL) && (sent < skb->len)) { NBT_DBG_TRANSFER_NF("0x%.2x ", skb->data[sent]); hci_h4p_outb(info, UART_TX, skb->data[sent]); sent++; } info->hdev->stat.byte_tx += sent; NBT_DBG_TRANSFER_NF("\n"); if (skb->len == sent) { kfree_skb(skb); } else { skb_pull(skb, sent); skb_queue_head(&info->txq, skb); } hci_h4p_outb(info, UART_OMAP_SCR, hci_h4p_inb(info, UART_OMAP_SCR) & ~UART_OMAP_SCR_EMPTY_THR); hci_h4p_outb(info, UART_IER, hci_h4p_inb(info, UART_IER) | UART_IER_THRI); finish_tx: /* Flush posted write to avoid spurious interrupts */ hci_h4p_inb(info, UART_OMAP_SCR); }