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); > + > +} -- To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html