|
@@ -30,14 +30,48 @@
|
|
|
|
|
|
#include "hci_uart.h"
|
|
|
|
|
|
+struct h5 {
|
|
|
+ struct sk_buff_head unack; /* Unack'ed packets queue */
|
|
|
+ struct sk_buff_head rel; /* Reliable packets queue */
|
|
|
+ struct sk_buff_head unrel; /* Unreliable packets queue */
|
|
|
+
|
|
|
+ struct sk_buff *rx_skb;
|
|
|
+
|
|
|
+ bool txack_req;
|
|
|
+
|
|
|
+ u8 msgq_txseq;
|
|
|
+};
|
|
|
+
|
|
|
static int h5_open(struct hci_uart *hu)
|
|
|
{
|
|
|
- return -ENOSYS;
|
|
|
+ struct h5 *h5;
|
|
|
+
|
|
|
+ BT_DBG("hu %p", hu);
|
|
|
+
|
|
|
+ h5 = kzalloc(sizeof(*h5), GFP_KERNEL);
|
|
|
+ if (!h5)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ hu->priv = h5;
|
|
|
+
|
|
|
+ skb_queue_head_init(&h5->unack);
|
|
|
+ skb_queue_head_init(&h5->rel);
|
|
|
+ skb_queue_head_init(&h5->unrel);
|
|
|
+
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
static int h5_close(struct hci_uart *hu)
|
|
|
{
|
|
|
- return -ENOSYS;
|
|
|
+ struct h5 *h5 = hu->priv;
|
|
|
+
|
|
|
+ skb_queue_purge(&h5->unack);
|
|
|
+ skb_queue_purge(&h5->rel);
|
|
|
+ skb_queue_purge(&h5->unrel);
|
|
|
+
|
|
|
+ kfree(h5);
|
|
|
+
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
static int h5_recv(struct hci_uart *hu, void *data, int count)
|
|
@@ -47,17 +81,71 @@ static int h5_recv(struct hci_uart *hu, void *data, int count)
|
|
|
|
|
|
static int h5_enqueue(struct hci_uart *hu, struct sk_buff *skb)
|
|
|
{
|
|
|
- return -ENOSYS;
|
|
|
+ struct h5 *h5 = hu->priv;
|
|
|
+
|
|
|
+ if (skb->len > 0xfff) {
|
|
|
+ BT_ERR("Packet too long (%u bytes)", skb->len);
|
|
|
+ kfree_skb(skb);
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ switch (bt_cb(skb)->pkt_type) {
|
|
|
+ case HCI_ACLDATA_PKT:
|
|
|
+ case HCI_COMMAND_PKT:
|
|
|
+ skb_queue_tail(&h5->rel, skb);
|
|
|
+ break;
|
|
|
+
|
|
|
+ case HCI_SCODATA_PKT:
|
|
|
+ skb_queue_tail(&h5->unrel, skb);
|
|
|
+ break;
|
|
|
+
|
|
|
+ default:
|
|
|
+ BT_ERR("Unknown packet type %u", bt_cb(skb)->pkt_type);
|
|
|
+ kfree_skb(skb);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static struct sk_buff *h5_prepare_pkt(struct h5 *h5, struct sk_buff *skb)
|
|
|
+{
|
|
|
+ h5->txack_req = false;
|
|
|
+ return NULL;
|
|
|
+}
|
|
|
+
|
|
|
+static struct sk_buff *h5_prepare_ack(struct h5 *h5)
|
|
|
+{
|
|
|
+ h5->txack_req = false;
|
|
|
+ return NULL;
|
|
|
}
|
|
|
|
|
|
static struct sk_buff *h5_dequeue(struct hci_uart *hu)
|
|
|
{
|
|
|
+ struct h5 *h5 = hu->priv;
|
|
|
+ struct sk_buff *skb, *nskb;
|
|
|
+
|
|
|
+ if ((skb = skb_dequeue(&h5->unrel)) != NULL) {
|
|
|
+ nskb = h5_prepare_pkt(h5, skb);
|
|
|
+ if (nskb) {
|
|
|
+ kfree_skb(skb);
|
|
|
+ return nskb;
|
|
|
+ }
|
|
|
+
|
|
|
+ skb_queue_head(&h5->unrel, skb);
|
|
|
+ BT_ERR("Could not dequeue pkt because alloc_skb failed");
|
|
|
+ }
|
|
|
+
|
|
|
+ if (h5->txack_req)
|
|
|
+ return h5_prepare_ack(h5);
|
|
|
+
|
|
|
return NULL;
|
|
|
}
|
|
|
|
|
|
static int h5_flush(struct hci_uart *hu)
|
|
|
{
|
|
|
- return -ENOSYS;
|
|
|
+ BT_DBG("hu %p", hu);
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
static struct hci_uart_proto h5p = {
|