Bluetooth: A2MP: Process A2MP messages
[deliverable/linux.git] / net / bluetooth / a2mp.c
CommitLineData
466f8004
AE
1/*
2 Copyright (c) 2010,2011 Code Aurora Forum. All rights reserved.
3 Copyright (c) 2011,2012 Intel Corp.
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License version 2 and
7 only version 2 as published by the Free Software Foundation.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13*/
14
15#include <net/bluetooth/bluetooth.h>
16#include <net/bluetooth/hci_core.h>
17#include <net/bluetooth/l2cap.h>
9740e49d 18#include <net/bluetooth/a2mp.h>
466f8004 19
f6d3c6e7
AE
20/* A2MP build & send command helper functions */
21static struct a2mp_cmd *__a2mp_build(u8 code, u8 ident, u16 len, void *data)
22{
23 struct a2mp_cmd *cmd;
24 int plen;
25
26 plen = sizeof(*cmd) + len;
27 cmd = kzalloc(plen, GFP_KERNEL);
28 if (!cmd)
29 return NULL;
30
31 cmd->code = code;
32 cmd->ident = ident;
33 cmd->len = cpu_to_le16(len);
34
35 memcpy(cmd->data, data, len);
36
37 return cmd;
38}
39
40static void a2mp_send(struct amp_mgr *mgr, u8 code, u8 ident, u16 len,
41 void *data)
42{
43 struct l2cap_chan *chan = mgr->a2mp_chan;
44 struct a2mp_cmd *cmd;
45 u16 total_len = len + sizeof(*cmd);
46 struct kvec iv;
47 struct msghdr msg;
48
49 cmd = __a2mp_build(code, ident, len, data);
50 if (!cmd)
51 return;
52
53 iv.iov_base = cmd;
54 iv.iov_len = total_len;
55
56 memset(&msg, 0, sizeof(msg));
57
58 msg.msg_iov = (struct iovec *) &iv;
59 msg.msg_iovlen = 1;
60
61 l2cap_chan_send(chan, &msg, total_len, 0);
62
63 kfree(cmd);
64}
65
6b44d9b8
AE
66/* Handle A2MP signalling */
67static int a2mp_chan_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb)
68{
69 struct a2mp_cmd *hdr = (void *) skb->data;
70 struct amp_mgr *mgr = chan->data;
71 int err = 0;
72
73 amp_mgr_get(mgr);
74
75 while (skb->len >= sizeof(*hdr)) {
76 struct a2mp_cmd *hdr = (void *) skb->data;
77 u16 len = le16_to_cpu(hdr->len);
78
79 BT_DBG("code 0x%02x id %d len %d", hdr->code, hdr->ident, len);
80
81 skb_pull(skb, sizeof(*hdr));
82
83 if (len > skb->len || !hdr->ident) {
84 err = -EINVAL;
85 break;
86 }
87
88 mgr->ident = hdr->ident;
89
90 switch (hdr->code) {
91 case A2MP_COMMAND_REJ:
92 case A2MP_DISCOVER_REQ:
93 case A2MP_CHANGE_NOTIFY:
94 case A2MP_GETINFO_REQ:
95 case A2MP_GETAMPASSOC_REQ:
96 case A2MP_CREATEPHYSLINK_REQ:
97 case A2MP_DISCONNPHYSLINK_REQ:
98 case A2MP_CHANGE_RSP:
99 case A2MP_DISCOVER_RSP:
100 case A2MP_GETINFO_RSP:
101 case A2MP_GETAMPASSOC_RSP:
102 case A2MP_CREATEPHYSLINK_RSP:
103 case A2MP_DISCONNPHYSLINK_RSP:
104 default:
105 BT_ERR("Unknown A2MP sig cmd 0x%2.2x", hdr->code);
106 err = -EINVAL;
107 break;
108 }
109 }
110
111 if (err) {
112 struct a2mp_cmd_rej rej;
113 rej.reason = __constant_cpu_to_le16(0);
114
115 BT_DBG("Send A2MP Rej: cmd 0x%2.2x err %d", hdr->code, err);
116
117 a2mp_send(mgr, A2MP_COMMAND_REJ, hdr->ident, sizeof(rej),
118 &rej);
119 }
120
121 /* Always free skb and return success error code to prevent
122 from sending L2CAP Disconnect over A2MP channel */
123 kfree_skb(skb);
124
125 amp_mgr_put(mgr);
126
127 return 0;
128}
129
46d5c908
AE
130static void a2mp_chan_close_cb(struct l2cap_chan *chan)
131{
132 l2cap_chan_destroy(chan);
133}
134
135static void a2mp_chan_state_change_cb(struct l2cap_chan *chan, int state)
136{
137 struct amp_mgr *mgr = chan->data;
138
139 if (!mgr)
140 return;
141
142 BT_DBG("chan %p state %s", chan, state_to_string(state));
143
144 chan->state = state;
145
146 switch (state) {
147 case BT_CLOSED:
148 if (mgr)
149 amp_mgr_put(mgr);
150 break;
151 }
152}
153
154static struct sk_buff *a2mp_chan_alloc_skb_cb(struct l2cap_chan *chan,
155 unsigned long len, int nb)
156{
157 return bt_skb_alloc(len, GFP_KERNEL);
158}
159
160static struct l2cap_chan *a2mp_chan_no_new_conn_cb(struct l2cap_chan *chan)
161{
162 BT_ERR("new_connection for chan %p not implemented", chan);
163
164 return NULL;
165}
166
167static void a2mp_chan_no_teardown_cb(struct l2cap_chan *chan, int err)
168{
169 BT_ERR("teardown for chan %p not implemented", chan);
170}
171
172static void a2mp_chan_no_ready(struct l2cap_chan *chan)
173{
174 BT_ERR("ready for chan %p not implemented", chan);
175}
176
466f8004
AE
177static struct l2cap_ops a2mp_chan_ops = {
178 .name = "L2CAP A2MP channel",
6b44d9b8 179 .recv = a2mp_chan_recv_cb,
46d5c908
AE
180 .close = a2mp_chan_close_cb,
181 .state_change = a2mp_chan_state_change_cb,
182 .alloc_skb = a2mp_chan_alloc_skb_cb,
183
184 /* Not implemented for A2MP */
185 .new_connection = a2mp_chan_no_new_conn_cb,
186 .teardown = a2mp_chan_no_teardown_cb,
187 .ready = a2mp_chan_no_ready,
466f8004
AE
188};
189
190static struct l2cap_chan *a2mp_chan_open(struct l2cap_conn *conn)
191{
192 struct l2cap_chan *chan;
193 int err;
194
195 chan = l2cap_chan_create();
196 if (!chan)
197 return NULL;
198
199 BT_DBG("chan %p", chan);
200
201 hci_conn_hold(conn->hcon);
202
203 chan->omtu = L2CAP_A2MP_DEFAULT_MTU;
204 chan->imtu = L2CAP_A2MP_DEFAULT_MTU;
205 chan->flush_to = L2CAP_DEFAULT_FLUSH_TO;
206
207 chan->ops = &a2mp_chan_ops;
208
209 l2cap_chan_set_defaults(chan);
210 chan->remote_max_tx = chan->max_tx;
211 chan->remote_tx_win = chan->tx_win;
212
213 chan->retrans_timeout = L2CAP_DEFAULT_RETRANS_TO;
214 chan->monitor_timeout = L2CAP_DEFAULT_MONITOR_TO;
215
216 skb_queue_head_init(&chan->tx_q);
217
218 chan->mode = L2CAP_MODE_ERTM;
219
220 err = l2cap_ertm_init(chan);
221 if (err < 0) {
222 l2cap_chan_del(chan, 0);
223 return NULL;
224 }
225
226 chan->conf_state = 0;
227
228 l2cap_chan_add(conn, chan);
229
230 chan->remote_mps = chan->omtu;
231 chan->mps = chan->omtu;
232
233 chan->state = BT_CONNECTED;
234
235 return chan;
236}
9740e49d
AE
237
238/* AMP Manager functions */
239void amp_mgr_get(struct amp_mgr *mgr)
240{
241 BT_DBG("mgr %p", mgr);
242
243 kref_get(&mgr->kref);
244}
245
246static void amp_mgr_destroy(struct kref *kref)
247{
248 struct amp_mgr *mgr = container_of(kref, struct amp_mgr, kref);
249
250 BT_DBG("mgr %p", mgr);
251
252 kfree(mgr);
253}
254
255int amp_mgr_put(struct amp_mgr *mgr)
256{
257 BT_DBG("mgr %p", mgr);
258
259 return kref_put(&mgr->kref, &amp_mgr_destroy);
260}
261
262static struct amp_mgr *amp_mgr_create(struct l2cap_conn *conn)
263{
264 struct amp_mgr *mgr;
265 struct l2cap_chan *chan;
266
267 mgr = kzalloc(sizeof(*mgr), GFP_KERNEL);
268 if (!mgr)
269 return NULL;
270
271 BT_DBG("conn %p mgr %p", conn, mgr);
272
273 mgr->l2cap_conn = conn;
274
275 chan = a2mp_chan_open(conn);
276 if (!chan) {
277 kfree(mgr);
278 return NULL;
279 }
280
281 mgr->a2mp_chan = chan;
282 chan->data = mgr;
283
284 conn->hcon->amp_mgr = mgr;
285
286 kref_init(&mgr->kref);
287
288 return mgr;
289}
This page took 0.039698 seconds and 5 git commands to generate.