Commit | Line | Data |
---|---|---|
7dec65c8 JH |
1 | /* |
2 | * | |
3 | * Bluetooth HCI Three-wire UART driver | |
4 | * | |
5 | * Copyright (C) 2012 Intel Corporation | |
6 | * | |
7 | * | |
8 | * This program is free software; you can redistribute it and/or modify | |
9 | * it under the terms of the GNU General Public License as published by | |
10 | * the Free Software Foundation; either version 2 of the License, or | |
11 | * (at your option) any later version. | |
12 | * | |
13 | * This program is distributed in the hope that it will be useful, | |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | * GNU General Public License for more details. | |
17 | * | |
18 | * You should have received a copy of the GNU General Public License | |
19 | * along with this program; if not, write to the Free Software | |
20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
21 | * | |
22 | */ | |
23 | ||
24 | #include <linux/kernel.h> | |
25 | #include <linux/errno.h> | |
26 | #include <linux/skbuff.h> | |
27 | ||
28 | #include <net/bluetooth/bluetooth.h> | |
29 | #include <net/bluetooth/hci_core.h> | |
30 | ||
31 | #include "hci_uart.h" | |
32 | ||
7d664fba JH |
33 | struct h5 { |
34 | struct sk_buff_head unack; /* Unack'ed packets queue */ | |
35 | struct sk_buff_head rel; /* Reliable packets queue */ | |
36 | struct sk_buff_head unrel; /* Unreliable packets queue */ | |
37 | ||
38 | struct sk_buff *rx_skb; | |
39 | ||
40 | bool txack_req; | |
41 | ||
42 | u8 msgq_txseq; | |
43 | }; | |
44 | ||
7dec65c8 JH |
45 | static int h5_open(struct hci_uart *hu) |
46 | { | |
7d664fba JH |
47 | struct h5 *h5; |
48 | ||
49 | BT_DBG("hu %p", hu); | |
50 | ||
51 | h5 = kzalloc(sizeof(*h5), GFP_KERNEL); | |
52 | if (!h5) | |
53 | return -ENOMEM; | |
54 | ||
55 | hu->priv = h5; | |
56 | ||
57 | skb_queue_head_init(&h5->unack); | |
58 | skb_queue_head_init(&h5->rel); | |
59 | skb_queue_head_init(&h5->unrel); | |
60 | ||
61 | return 0; | |
7dec65c8 JH |
62 | } |
63 | ||
64 | static int h5_close(struct hci_uart *hu) | |
65 | { | |
7d664fba JH |
66 | struct h5 *h5 = hu->priv; |
67 | ||
68 | skb_queue_purge(&h5->unack); | |
69 | skb_queue_purge(&h5->rel); | |
70 | skb_queue_purge(&h5->unrel); | |
71 | ||
72 | kfree(h5); | |
73 | ||
74 | return 0; | |
7dec65c8 JH |
75 | } |
76 | ||
77 | static int h5_recv(struct hci_uart *hu, void *data, int count) | |
78 | { | |
79 | return -ENOSYS; | |
80 | } | |
81 | ||
82 | static int h5_enqueue(struct hci_uart *hu, struct sk_buff *skb) | |
83 | { | |
7d664fba JH |
84 | struct h5 *h5 = hu->priv; |
85 | ||
86 | if (skb->len > 0xfff) { | |
87 | BT_ERR("Packet too long (%u bytes)", skb->len); | |
88 | kfree_skb(skb); | |
89 | return 0; | |
90 | } | |
91 | ||
92 | switch (bt_cb(skb)->pkt_type) { | |
93 | case HCI_ACLDATA_PKT: | |
94 | case HCI_COMMAND_PKT: | |
95 | skb_queue_tail(&h5->rel, skb); | |
96 | break; | |
97 | ||
98 | case HCI_SCODATA_PKT: | |
99 | skb_queue_tail(&h5->unrel, skb); | |
100 | break; | |
101 | ||
102 | default: | |
103 | BT_ERR("Unknown packet type %u", bt_cb(skb)->pkt_type); | |
104 | kfree_skb(skb); | |
105 | break; | |
106 | } | |
107 | ||
108 | return 0; | |
109 | } | |
110 | ||
111 | static struct sk_buff *h5_prepare_pkt(struct h5 *h5, struct sk_buff *skb) | |
112 | { | |
113 | h5->txack_req = false; | |
114 | return NULL; | |
115 | } | |
116 | ||
117 | static struct sk_buff *h5_prepare_ack(struct h5 *h5) | |
118 | { | |
119 | h5->txack_req = false; | |
120 | return NULL; | |
7dec65c8 JH |
121 | } |
122 | ||
123 | static struct sk_buff *h5_dequeue(struct hci_uart *hu) | |
124 | { | |
7d664fba JH |
125 | struct h5 *h5 = hu->priv; |
126 | struct sk_buff *skb, *nskb; | |
127 | ||
128 | if ((skb = skb_dequeue(&h5->unrel)) != NULL) { | |
129 | nskb = h5_prepare_pkt(h5, skb); | |
130 | if (nskb) { | |
131 | kfree_skb(skb); | |
132 | return nskb; | |
133 | } | |
134 | ||
135 | skb_queue_head(&h5->unrel, skb); | |
136 | BT_ERR("Could not dequeue pkt because alloc_skb failed"); | |
137 | } | |
138 | ||
139 | if (h5->txack_req) | |
140 | return h5_prepare_ack(h5); | |
141 | ||
7dec65c8 JH |
142 | return NULL; |
143 | } | |
144 | ||
145 | static int h5_flush(struct hci_uart *hu) | |
146 | { | |
7d664fba JH |
147 | BT_DBG("hu %p", hu); |
148 | return 0; | |
7dec65c8 JH |
149 | } |
150 | ||
151 | static struct hci_uart_proto h5p = { | |
152 | .id = HCI_UART_3WIRE, | |
153 | .open = h5_open, | |
154 | .close = h5_close, | |
155 | .recv = h5_recv, | |
156 | .enqueue = h5_enqueue, | |
157 | .dequeue = h5_dequeue, | |
158 | .flush = h5_flush, | |
159 | }; | |
160 | ||
161 | int __init h5_init(void) | |
162 | { | |
163 | int err = hci_uart_register_proto(&h5p); | |
164 | ||
165 | if (!err) | |
166 | BT_INFO("HCI Three-wire UART (H5) protocol initialized"); | |
167 | else | |
168 | BT_ERR("HCI Three-wire UART (H5) protocol init failed"); | |
169 | ||
170 | return err; | |
171 | } | |
172 | ||
173 | int __exit h5_deinit(void) | |
174 | { | |
175 | return hci_uart_unregister_proto(&h5p); | |
176 | } |