bluetooth: Add hci_h4p driver
Oliver Neukum
oneukum at suse.de
Mon Dec 15 02:01:50 PST 2014
Hi,
a few remarks about possible issues.
Regards
Oliver
> +static int h4p_send_negotiation(struct h4p_info *info)
> +{
> + struct h4p_neg_cmd *neg_cmd;
> + struct h4p_neg_hdr *neg_hdr;
> + struct sk_buff *skb;
> + int err, len;
> + u16 sysclk = 38400;
> +
> + printk("Sending negotiation..");
> + len = sizeof(*neg_cmd) + sizeof(*neg_hdr) + H4_TYPE_SIZE;
> +
> + skb = bt_skb_alloc(len, GFP_KERNEL);
> + if (!skb)
> + return -ENOMEM;
> +
> + memset(skb->data, 0x00, len);
> + *skb_put(skb, 1) = H4_NEG_PKT;
> + neg_hdr = (struct h4p_neg_hdr *)skb_put(skb, sizeof(*neg_hdr));
> + neg_cmd = (struct h4p_neg_cmd *)skb_put(skb, sizeof(*neg_cmd));
> +
> + neg_hdr->dlen = sizeof(*neg_cmd);
> + neg_cmd->ack = H4P_NEG_REQ;
> + neg_cmd->baud = cpu_to_le16(BT_BAUDRATE_DIVIDER/MAX_BAUD_RATE);
> + neg_cmd->proto = H4P_PROTO_BYTE;
> + neg_cmd->sys_clk = cpu_to_le16(sysclk);
> +
> + h4p_change_speed(info, INIT_SPEED);
> +
> + h4p_set_rts(info, 1);
> + info->init_error = 0;
> + init_completion(&info->init_completion);
> +
> + h4p_simple_send_frame(info, skb);
> +
> + if (!wait_for_completion_interruptible_timeout(&info->init_completion,
> + msecs_to_jiffies(1000))) {
> + printk("h4p: negotiation did not return\n");
Memory leak in the error case
> + return -ETIMEDOUT;
> + }
> +
> + if (info->init_error < 0)
> + return info->init_error;
> +
> + /* Change to operational settings */
> + h4p_set_auto_ctsrts(info, 0, UART_EFR_RTS);
> + h4p_set_rts(info, 0);
> + h4p_change_speed(info, MAX_BAUD_RATE);
> +
> + err = h4p_wait_for_cts(info, 1, 100);
> + if (err < 0)
> + return err;
> +
> + h4p_set_auto_ctsrts(info, 1, UART_EFR_RTS);
> + init_completion(&info->init_completion);
> + err = 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;
> +
> + printk("Negotiation successful\n");
> + return 0;
> +}
> +static unsigned int h4p_get_data_len(struct h4p_info *info,
> + struct sk_buff *skb)
> +{
> + long retval = -1;
> + struct hci_acl_hdr *acl_hdr;
> + struct hci_sco_hdr *sco_hdr;
> + struct hci_event_hdr *evt_hdr;
> + struct h4p_neg_hdr *neg_hdr;
> + struct h4p_alive_hdr *alive_hdr;
> + struct h4p_radio_hdr *radio_hdr;
> +
> + switch (bt_cb(skb)->pkt_type) {
> + case H4_EVT_PKT:
> + evt_hdr = (struct hci_event_hdr *)skb->data;
> + retval = evt_hdr->plen;
> + break;
> + case H4_ACL_PKT:
> + acl_hdr = (struct hci_acl_hdr *)skb->data;
> + retval = le16_to_cpu(acl_hdr->dlen);
Could you explain, why only this needs endianness converted?
> + break;
> + case H4_SCO_PKT:
> + sco_hdr = (struct hci_sco_hdr *)skb->data;
> + retval = sco_hdr->dlen;
> + break;
> + case H4_RADIO_PKT:
> + radio_hdr = (struct h4p_radio_hdr *)skb->data;
> + retval = radio_hdr->dlen;
> + break;
> + case H4_NEG_PKT:
> + neg_hdr = (struct h4p_neg_hdr *)skb->data;
> + retval = neg_hdr->dlen;
> + break;
> + case H4_ALIVE_PKT:
> + alive_hdr = (struct h4p_alive_hdr *)skb->data;
> + retval = alive_hdr->dlen;
> + break;
> + }
> +
> + return retval;
> +}
> +static void h4p_rx_tasklet(unsigned long data)
> +{
> + u8 byte;
> + struct h4p_info *info = (struct h4p_info *)data;
> +
> + BT_DBG("tasklet woke up");
> + BT_DBG("rx_tasklet woke up");
Isn't this a bit redundant?
> +
> + while (h4p_inb(info, UART_LSR) & UART_LSR_DR) {
> + byte = h4p_inb(info, UART_RX);
> + BT_DBG("[in: %02x]", byte);
> + 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++;
> + h4p_handle_byte(info, byte);
> + }
> +
> + if (!info->rx_enabled) {
> + if (h4p_inb(info, UART_LSR) & UART_LSR_TEMT &&
> + info->autorts) {
> + __h4p_set_auto_ctsrts(info, 0 , UART_EFR_RTS);
> + info->autorts = 0;
> + }
> + /* Flush posted write to avoid spurious interrupts */
> + h4p_inb(info, UART_OMAP_SCR);
> + h4p_set_clk(info, &info->rx_clocks_en, 0);
> + }
> +
> +finish_rx:
> + BT_DBG("rx_ended");
> +}
> +
> +static void h4p_tx_tasklet(unsigned long data)
> +{
> + unsigned int sent = 0;
> + struct sk_buff *skb;
> + struct h4p_info *info = (struct h4p_info *)data;
> +
> + BT_DBG("tasklet woke up");
> + BT_DBG("tx_tasklet woke up");
Doubled?
> + if (info->autorts != info->rx_enabled) {
> + if (h4p_inb(info, UART_LSR) & UART_LSR_TEMT) {
> + if (info->autorts && !info->rx_enabled) {
> + __h4p_set_auto_ctsrts(info, 0,
> + UART_EFR_RTS);
> + info->autorts = 0;
> + }
> + if (!info->autorts && info->rx_enabled) {
> + __h4p_set_auto_ctsrts(info, 1,
> + UART_EFR_RTS);
> + info->autorts = 1;
> + }
> + } else {
> + h4p_outb(info, UART_OMAP_SCR,
> + 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 */
> + BT_DBG("skb ready");
> + if (h4p_inb(info, UART_LSR) & UART_LSR_TEMT) {
> + h4p_outb(info, UART_IER,
> + h4p_inb(info, UART_IER) &
> + ~UART_IER_THRI);
> + h4p_inb(info, UART_OMAP_SCR);
> + h4p_disable_tx(info);
> + return;
> + }
> + h4p_outb(info, UART_OMAP_SCR,
> + h4p_inb(info, UART_OMAP_SCR) |
> + UART_OMAP_SCR_EMPTY_THR);
> + goto finish_tx;
> + }
> +
> + /* Copy data to tx fifo */
> + while (!(h4p_inb(info, UART_OMAP_SSR) & UART_OMAP_SSR_TXFULL) &&
> + (sent < skb->len)) {
> + //printk("[Out: %02x]", skb->data[sent]);
> + //printk("%02x ", skb->data[sent]);
> + h4p_outb(info, UART_TX, skb->data[sent]);
> + sent++;
> + }
> +
> + info->hdev->stat.byte_tx += sent;
> + if (skb->len == sent) {
> + kfree_skb(skb);
> + } else {
> + skb_pull(skb, sent);
> + skb_queue_head(&info->txq, skb);
> + }
> +
> + h4p_outb(info, UART_OMAP_SCR, h4p_inb(info, UART_OMAP_SCR) &
> + ~UART_OMAP_SCR_EMPTY_THR);
> + h4p_outb(info, UART_IER, h4p_inb(info, UART_IER) |
> + UART_IER_THRI);
> +
> +finish_tx:
> + /* Flush posted write to avoid spurious interrupts */
> + h4p_inb(info, UART_OMAP_SCR);
> +
> +}
More information about the linux-arm-kernel
mailing list