1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun CMTP implementation for Linux Bluetooth stack (BlueZ).
3*4882a593Smuzhiyun Copyright (C) 2002-2003 Marcel Holtmann <marcel@holtmann.org>
4*4882a593Smuzhiyun
5*4882a593Smuzhiyun This program is free software; you can redistribute it and/or modify
6*4882a593Smuzhiyun it under the terms of the GNU General Public License version 2 as
7*4882a593Smuzhiyun published by the Free Software Foundation;
8*4882a593Smuzhiyun
9*4882a593Smuzhiyun THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
10*4882a593Smuzhiyun OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
11*4882a593Smuzhiyun FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
12*4882a593Smuzhiyun IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
13*4882a593Smuzhiyun CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
14*4882a593Smuzhiyun WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15*4882a593Smuzhiyun ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16*4882a593Smuzhiyun OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17*4882a593Smuzhiyun
18*4882a593Smuzhiyun ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
19*4882a593Smuzhiyun COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
20*4882a593Smuzhiyun SOFTWARE IS DISCLAIMED.
21*4882a593Smuzhiyun */
22*4882a593Smuzhiyun
23*4882a593Smuzhiyun #include <linux/export.h>
24*4882a593Smuzhiyun #include <linux/proc_fs.h>
25*4882a593Smuzhiyun #include <linux/seq_file.h>
26*4882a593Smuzhiyun #include <linux/types.h>
27*4882a593Smuzhiyun #include <linux/errno.h>
28*4882a593Smuzhiyun #include <linux/kernel.h>
29*4882a593Smuzhiyun #include <linux/sched/signal.h>
30*4882a593Smuzhiyun #include <linux/slab.h>
31*4882a593Smuzhiyun #include <linux/poll.h>
32*4882a593Smuzhiyun #include <linux/fcntl.h>
33*4882a593Smuzhiyun #include <linux/skbuff.h>
34*4882a593Smuzhiyun #include <linux/socket.h>
35*4882a593Smuzhiyun #include <linux/ioctl.h>
36*4882a593Smuzhiyun #include <linux/file.h>
37*4882a593Smuzhiyun #include <linux/wait.h>
38*4882a593Smuzhiyun #include <linux/kthread.h>
39*4882a593Smuzhiyun #include <net/sock.h>
40*4882a593Smuzhiyun
41*4882a593Smuzhiyun #include <linux/isdn/capilli.h>
42*4882a593Smuzhiyun #include <linux/isdn/capicmd.h>
43*4882a593Smuzhiyun #include <linux/isdn/capiutil.h>
44*4882a593Smuzhiyun
45*4882a593Smuzhiyun #include "cmtp.h"
46*4882a593Smuzhiyun
47*4882a593Smuzhiyun #define CAPI_INTEROPERABILITY 0x20
48*4882a593Smuzhiyun
49*4882a593Smuzhiyun #define CAPI_INTEROPERABILITY_REQ CAPICMD(CAPI_INTEROPERABILITY, CAPI_REQ)
50*4882a593Smuzhiyun #define CAPI_INTEROPERABILITY_CONF CAPICMD(CAPI_INTEROPERABILITY, CAPI_CONF)
51*4882a593Smuzhiyun #define CAPI_INTEROPERABILITY_IND CAPICMD(CAPI_INTEROPERABILITY, CAPI_IND)
52*4882a593Smuzhiyun #define CAPI_INTEROPERABILITY_RESP CAPICMD(CAPI_INTEROPERABILITY, CAPI_RESP)
53*4882a593Smuzhiyun
54*4882a593Smuzhiyun #define CAPI_INTEROPERABILITY_REQ_LEN (CAPI_MSG_BASELEN + 2)
55*4882a593Smuzhiyun #define CAPI_INTEROPERABILITY_CONF_LEN (CAPI_MSG_BASELEN + 4)
56*4882a593Smuzhiyun #define CAPI_INTEROPERABILITY_IND_LEN (CAPI_MSG_BASELEN + 2)
57*4882a593Smuzhiyun #define CAPI_INTEROPERABILITY_RESP_LEN (CAPI_MSG_BASELEN + 2)
58*4882a593Smuzhiyun
59*4882a593Smuzhiyun #define CAPI_FUNCTION_REGISTER 0
60*4882a593Smuzhiyun #define CAPI_FUNCTION_RELEASE 1
61*4882a593Smuzhiyun #define CAPI_FUNCTION_GET_PROFILE 2
62*4882a593Smuzhiyun #define CAPI_FUNCTION_GET_MANUFACTURER 3
63*4882a593Smuzhiyun #define CAPI_FUNCTION_GET_VERSION 4
64*4882a593Smuzhiyun #define CAPI_FUNCTION_GET_SERIAL_NUMBER 5
65*4882a593Smuzhiyun #define CAPI_FUNCTION_MANUFACTURER 6
66*4882a593Smuzhiyun #define CAPI_FUNCTION_LOOPBACK 7
67*4882a593Smuzhiyun
68*4882a593Smuzhiyun
69*4882a593Smuzhiyun #define CMTP_MSGNUM 1
70*4882a593Smuzhiyun #define CMTP_APPLID 2
71*4882a593Smuzhiyun #define CMTP_MAPPING 3
72*4882a593Smuzhiyun
cmtp_application_add(struct cmtp_session * session,__u16 appl)73*4882a593Smuzhiyun static struct cmtp_application *cmtp_application_add(struct cmtp_session *session, __u16 appl)
74*4882a593Smuzhiyun {
75*4882a593Smuzhiyun struct cmtp_application *app = kzalloc(sizeof(*app), GFP_KERNEL);
76*4882a593Smuzhiyun
77*4882a593Smuzhiyun BT_DBG("session %p application %p appl %d", session, app, appl);
78*4882a593Smuzhiyun
79*4882a593Smuzhiyun if (!app)
80*4882a593Smuzhiyun return NULL;
81*4882a593Smuzhiyun
82*4882a593Smuzhiyun app->state = BT_OPEN;
83*4882a593Smuzhiyun app->appl = appl;
84*4882a593Smuzhiyun
85*4882a593Smuzhiyun list_add_tail(&app->list, &session->applications);
86*4882a593Smuzhiyun
87*4882a593Smuzhiyun return app;
88*4882a593Smuzhiyun }
89*4882a593Smuzhiyun
cmtp_application_del(struct cmtp_session * session,struct cmtp_application * app)90*4882a593Smuzhiyun static void cmtp_application_del(struct cmtp_session *session, struct cmtp_application *app)
91*4882a593Smuzhiyun {
92*4882a593Smuzhiyun BT_DBG("session %p application %p", session, app);
93*4882a593Smuzhiyun
94*4882a593Smuzhiyun if (app) {
95*4882a593Smuzhiyun list_del(&app->list);
96*4882a593Smuzhiyun kfree(app);
97*4882a593Smuzhiyun }
98*4882a593Smuzhiyun }
99*4882a593Smuzhiyun
cmtp_application_get(struct cmtp_session * session,int pattern,__u16 value)100*4882a593Smuzhiyun static struct cmtp_application *cmtp_application_get(struct cmtp_session *session, int pattern, __u16 value)
101*4882a593Smuzhiyun {
102*4882a593Smuzhiyun struct cmtp_application *app;
103*4882a593Smuzhiyun
104*4882a593Smuzhiyun list_for_each_entry(app, &session->applications, list) {
105*4882a593Smuzhiyun switch (pattern) {
106*4882a593Smuzhiyun case CMTP_MSGNUM:
107*4882a593Smuzhiyun if (app->msgnum == value)
108*4882a593Smuzhiyun return app;
109*4882a593Smuzhiyun break;
110*4882a593Smuzhiyun case CMTP_APPLID:
111*4882a593Smuzhiyun if (app->appl == value)
112*4882a593Smuzhiyun return app;
113*4882a593Smuzhiyun break;
114*4882a593Smuzhiyun case CMTP_MAPPING:
115*4882a593Smuzhiyun if (app->mapping == value)
116*4882a593Smuzhiyun return app;
117*4882a593Smuzhiyun break;
118*4882a593Smuzhiyun }
119*4882a593Smuzhiyun }
120*4882a593Smuzhiyun
121*4882a593Smuzhiyun return NULL;
122*4882a593Smuzhiyun }
123*4882a593Smuzhiyun
cmtp_msgnum_get(struct cmtp_session * session)124*4882a593Smuzhiyun static int cmtp_msgnum_get(struct cmtp_session *session)
125*4882a593Smuzhiyun {
126*4882a593Smuzhiyun session->msgnum++;
127*4882a593Smuzhiyun
128*4882a593Smuzhiyun if ((session->msgnum & 0xff) > 200)
129*4882a593Smuzhiyun session->msgnum = CMTP_INITIAL_MSGNUM + 1;
130*4882a593Smuzhiyun
131*4882a593Smuzhiyun return session->msgnum;
132*4882a593Smuzhiyun }
133*4882a593Smuzhiyun
cmtp_send_capimsg(struct cmtp_session * session,struct sk_buff * skb)134*4882a593Smuzhiyun static void cmtp_send_capimsg(struct cmtp_session *session, struct sk_buff *skb)
135*4882a593Smuzhiyun {
136*4882a593Smuzhiyun struct cmtp_scb *scb = (void *) skb->cb;
137*4882a593Smuzhiyun
138*4882a593Smuzhiyun BT_DBG("session %p skb %p len %d", session, skb, skb->len);
139*4882a593Smuzhiyun
140*4882a593Smuzhiyun scb->id = -1;
141*4882a593Smuzhiyun scb->data = (CAPIMSG_COMMAND(skb->data) == CAPI_DATA_B3);
142*4882a593Smuzhiyun
143*4882a593Smuzhiyun skb_queue_tail(&session->transmit, skb);
144*4882a593Smuzhiyun
145*4882a593Smuzhiyun wake_up_interruptible(sk_sleep(session->sock->sk));
146*4882a593Smuzhiyun }
147*4882a593Smuzhiyun
cmtp_send_interopmsg(struct cmtp_session * session,__u8 subcmd,__u16 appl,__u16 msgnum,__u16 function,unsigned char * buf,int len)148*4882a593Smuzhiyun static void cmtp_send_interopmsg(struct cmtp_session *session,
149*4882a593Smuzhiyun __u8 subcmd, __u16 appl, __u16 msgnum,
150*4882a593Smuzhiyun __u16 function, unsigned char *buf, int len)
151*4882a593Smuzhiyun {
152*4882a593Smuzhiyun struct sk_buff *skb;
153*4882a593Smuzhiyun unsigned char *s;
154*4882a593Smuzhiyun
155*4882a593Smuzhiyun BT_DBG("session %p subcmd 0x%02x appl %d msgnum %d", session, subcmd, appl, msgnum);
156*4882a593Smuzhiyun
157*4882a593Smuzhiyun skb = alloc_skb(CAPI_MSG_BASELEN + 6 + len, GFP_ATOMIC);
158*4882a593Smuzhiyun if (!skb) {
159*4882a593Smuzhiyun BT_ERR("Can't allocate memory for interoperability packet");
160*4882a593Smuzhiyun return;
161*4882a593Smuzhiyun }
162*4882a593Smuzhiyun
163*4882a593Smuzhiyun s = skb_put(skb, CAPI_MSG_BASELEN + 6 + len);
164*4882a593Smuzhiyun
165*4882a593Smuzhiyun capimsg_setu16(s, 0, CAPI_MSG_BASELEN + 6 + len);
166*4882a593Smuzhiyun capimsg_setu16(s, 2, appl);
167*4882a593Smuzhiyun capimsg_setu8 (s, 4, CAPI_INTEROPERABILITY);
168*4882a593Smuzhiyun capimsg_setu8 (s, 5, subcmd);
169*4882a593Smuzhiyun capimsg_setu16(s, 6, msgnum);
170*4882a593Smuzhiyun
171*4882a593Smuzhiyun /* Interoperability selector (Bluetooth Device Management) */
172*4882a593Smuzhiyun capimsg_setu16(s, 8, 0x0001);
173*4882a593Smuzhiyun
174*4882a593Smuzhiyun capimsg_setu8 (s, 10, 3 + len);
175*4882a593Smuzhiyun capimsg_setu16(s, 11, function);
176*4882a593Smuzhiyun capimsg_setu8 (s, 13, len);
177*4882a593Smuzhiyun
178*4882a593Smuzhiyun if (len > 0)
179*4882a593Smuzhiyun memcpy(s + 14, buf, len);
180*4882a593Smuzhiyun
181*4882a593Smuzhiyun cmtp_send_capimsg(session, skb);
182*4882a593Smuzhiyun }
183*4882a593Smuzhiyun
cmtp_recv_interopmsg(struct cmtp_session * session,struct sk_buff * skb)184*4882a593Smuzhiyun static void cmtp_recv_interopmsg(struct cmtp_session *session, struct sk_buff *skb)
185*4882a593Smuzhiyun {
186*4882a593Smuzhiyun struct capi_ctr *ctrl = &session->ctrl;
187*4882a593Smuzhiyun struct cmtp_application *application;
188*4882a593Smuzhiyun __u16 appl, msgnum, func, info;
189*4882a593Smuzhiyun __u32 controller;
190*4882a593Smuzhiyun
191*4882a593Smuzhiyun BT_DBG("session %p skb %p len %d", session, skb, skb->len);
192*4882a593Smuzhiyun
193*4882a593Smuzhiyun switch (CAPIMSG_SUBCOMMAND(skb->data)) {
194*4882a593Smuzhiyun case CAPI_CONF:
195*4882a593Smuzhiyun if (skb->len < CAPI_MSG_BASELEN + 10)
196*4882a593Smuzhiyun break;
197*4882a593Smuzhiyun
198*4882a593Smuzhiyun func = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 5);
199*4882a593Smuzhiyun info = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 8);
200*4882a593Smuzhiyun
201*4882a593Smuzhiyun switch (func) {
202*4882a593Smuzhiyun case CAPI_FUNCTION_REGISTER:
203*4882a593Smuzhiyun msgnum = CAPIMSG_MSGID(skb->data);
204*4882a593Smuzhiyun
205*4882a593Smuzhiyun application = cmtp_application_get(session, CMTP_MSGNUM, msgnum);
206*4882a593Smuzhiyun if (application) {
207*4882a593Smuzhiyun application->state = BT_CONNECTED;
208*4882a593Smuzhiyun application->msgnum = 0;
209*4882a593Smuzhiyun application->mapping = CAPIMSG_APPID(skb->data);
210*4882a593Smuzhiyun wake_up_interruptible(&session->wait);
211*4882a593Smuzhiyun }
212*4882a593Smuzhiyun
213*4882a593Smuzhiyun break;
214*4882a593Smuzhiyun
215*4882a593Smuzhiyun case CAPI_FUNCTION_RELEASE:
216*4882a593Smuzhiyun appl = CAPIMSG_APPID(skb->data);
217*4882a593Smuzhiyun
218*4882a593Smuzhiyun application = cmtp_application_get(session, CMTP_MAPPING, appl);
219*4882a593Smuzhiyun if (application) {
220*4882a593Smuzhiyun application->state = BT_CLOSED;
221*4882a593Smuzhiyun application->msgnum = 0;
222*4882a593Smuzhiyun wake_up_interruptible(&session->wait);
223*4882a593Smuzhiyun }
224*4882a593Smuzhiyun
225*4882a593Smuzhiyun break;
226*4882a593Smuzhiyun
227*4882a593Smuzhiyun case CAPI_FUNCTION_GET_PROFILE:
228*4882a593Smuzhiyun if (skb->len < CAPI_MSG_BASELEN + 11 + sizeof(capi_profile))
229*4882a593Smuzhiyun break;
230*4882a593Smuzhiyun
231*4882a593Smuzhiyun controller = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 11);
232*4882a593Smuzhiyun msgnum = CAPIMSG_MSGID(skb->data);
233*4882a593Smuzhiyun
234*4882a593Smuzhiyun if (!info && (msgnum == CMTP_INITIAL_MSGNUM)) {
235*4882a593Smuzhiyun session->ncontroller = controller;
236*4882a593Smuzhiyun wake_up_interruptible(&session->wait);
237*4882a593Smuzhiyun break;
238*4882a593Smuzhiyun }
239*4882a593Smuzhiyun
240*4882a593Smuzhiyun if (!info && ctrl) {
241*4882a593Smuzhiyun memcpy(&ctrl->profile,
242*4882a593Smuzhiyun skb->data + CAPI_MSG_BASELEN + 11,
243*4882a593Smuzhiyun sizeof(capi_profile));
244*4882a593Smuzhiyun session->state = BT_CONNECTED;
245*4882a593Smuzhiyun capi_ctr_ready(ctrl);
246*4882a593Smuzhiyun }
247*4882a593Smuzhiyun
248*4882a593Smuzhiyun break;
249*4882a593Smuzhiyun
250*4882a593Smuzhiyun case CAPI_FUNCTION_GET_MANUFACTURER:
251*4882a593Smuzhiyun if (skb->len < CAPI_MSG_BASELEN + 15)
252*4882a593Smuzhiyun break;
253*4882a593Smuzhiyun
254*4882a593Smuzhiyun if (!info && ctrl) {
255*4882a593Smuzhiyun int len = min_t(uint, CAPI_MANUFACTURER_LEN,
256*4882a593Smuzhiyun skb->data[CAPI_MSG_BASELEN + 14]);
257*4882a593Smuzhiyun
258*4882a593Smuzhiyun memset(ctrl->manu, 0, CAPI_MANUFACTURER_LEN);
259*4882a593Smuzhiyun strncpy(ctrl->manu,
260*4882a593Smuzhiyun skb->data + CAPI_MSG_BASELEN + 15, len);
261*4882a593Smuzhiyun }
262*4882a593Smuzhiyun
263*4882a593Smuzhiyun break;
264*4882a593Smuzhiyun
265*4882a593Smuzhiyun case CAPI_FUNCTION_GET_VERSION:
266*4882a593Smuzhiyun if (skb->len < CAPI_MSG_BASELEN + 32)
267*4882a593Smuzhiyun break;
268*4882a593Smuzhiyun
269*4882a593Smuzhiyun if (!info && ctrl) {
270*4882a593Smuzhiyun ctrl->version.majorversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 16);
271*4882a593Smuzhiyun ctrl->version.minorversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 20);
272*4882a593Smuzhiyun ctrl->version.majormanuversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 24);
273*4882a593Smuzhiyun ctrl->version.minormanuversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 28);
274*4882a593Smuzhiyun }
275*4882a593Smuzhiyun
276*4882a593Smuzhiyun break;
277*4882a593Smuzhiyun
278*4882a593Smuzhiyun case CAPI_FUNCTION_GET_SERIAL_NUMBER:
279*4882a593Smuzhiyun if (skb->len < CAPI_MSG_BASELEN + 17)
280*4882a593Smuzhiyun break;
281*4882a593Smuzhiyun
282*4882a593Smuzhiyun if (!info && ctrl) {
283*4882a593Smuzhiyun int len = min_t(uint, CAPI_SERIAL_LEN,
284*4882a593Smuzhiyun skb->data[CAPI_MSG_BASELEN + 16]);
285*4882a593Smuzhiyun
286*4882a593Smuzhiyun memset(ctrl->serial, 0, CAPI_SERIAL_LEN);
287*4882a593Smuzhiyun strncpy(ctrl->serial,
288*4882a593Smuzhiyun skb->data + CAPI_MSG_BASELEN + 17, len);
289*4882a593Smuzhiyun }
290*4882a593Smuzhiyun
291*4882a593Smuzhiyun break;
292*4882a593Smuzhiyun }
293*4882a593Smuzhiyun
294*4882a593Smuzhiyun break;
295*4882a593Smuzhiyun
296*4882a593Smuzhiyun case CAPI_IND:
297*4882a593Smuzhiyun if (skb->len < CAPI_MSG_BASELEN + 6)
298*4882a593Smuzhiyun break;
299*4882a593Smuzhiyun
300*4882a593Smuzhiyun func = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 3);
301*4882a593Smuzhiyun
302*4882a593Smuzhiyun if (func == CAPI_FUNCTION_LOOPBACK) {
303*4882a593Smuzhiyun int len = min_t(uint, skb->len - CAPI_MSG_BASELEN - 6,
304*4882a593Smuzhiyun skb->data[CAPI_MSG_BASELEN + 5]);
305*4882a593Smuzhiyun appl = CAPIMSG_APPID(skb->data);
306*4882a593Smuzhiyun msgnum = CAPIMSG_MSGID(skb->data);
307*4882a593Smuzhiyun cmtp_send_interopmsg(session, CAPI_RESP, appl, msgnum, func,
308*4882a593Smuzhiyun skb->data + CAPI_MSG_BASELEN + 6, len);
309*4882a593Smuzhiyun }
310*4882a593Smuzhiyun
311*4882a593Smuzhiyun break;
312*4882a593Smuzhiyun }
313*4882a593Smuzhiyun
314*4882a593Smuzhiyun kfree_skb(skb);
315*4882a593Smuzhiyun }
316*4882a593Smuzhiyun
cmtp_recv_capimsg(struct cmtp_session * session,struct sk_buff * skb)317*4882a593Smuzhiyun void cmtp_recv_capimsg(struct cmtp_session *session, struct sk_buff *skb)
318*4882a593Smuzhiyun {
319*4882a593Smuzhiyun struct capi_ctr *ctrl = &session->ctrl;
320*4882a593Smuzhiyun struct cmtp_application *application;
321*4882a593Smuzhiyun __u16 appl;
322*4882a593Smuzhiyun __u32 contr;
323*4882a593Smuzhiyun
324*4882a593Smuzhiyun BT_DBG("session %p skb %p len %d", session, skb, skb->len);
325*4882a593Smuzhiyun
326*4882a593Smuzhiyun if (skb->len < CAPI_MSG_BASELEN)
327*4882a593Smuzhiyun return;
328*4882a593Smuzhiyun
329*4882a593Smuzhiyun if (CAPIMSG_COMMAND(skb->data) == CAPI_INTEROPERABILITY) {
330*4882a593Smuzhiyun cmtp_recv_interopmsg(session, skb);
331*4882a593Smuzhiyun return;
332*4882a593Smuzhiyun }
333*4882a593Smuzhiyun
334*4882a593Smuzhiyun if (session->flags & BIT(CMTP_LOOPBACK)) {
335*4882a593Smuzhiyun kfree_skb(skb);
336*4882a593Smuzhiyun return;
337*4882a593Smuzhiyun }
338*4882a593Smuzhiyun
339*4882a593Smuzhiyun appl = CAPIMSG_APPID(skb->data);
340*4882a593Smuzhiyun contr = CAPIMSG_CONTROL(skb->data);
341*4882a593Smuzhiyun
342*4882a593Smuzhiyun application = cmtp_application_get(session, CMTP_MAPPING, appl);
343*4882a593Smuzhiyun if (application) {
344*4882a593Smuzhiyun appl = application->appl;
345*4882a593Smuzhiyun CAPIMSG_SETAPPID(skb->data, appl);
346*4882a593Smuzhiyun } else {
347*4882a593Smuzhiyun BT_ERR("Can't find application with id %d", appl);
348*4882a593Smuzhiyun kfree_skb(skb);
349*4882a593Smuzhiyun return;
350*4882a593Smuzhiyun }
351*4882a593Smuzhiyun
352*4882a593Smuzhiyun if ((contr & 0x7f) == 0x01) {
353*4882a593Smuzhiyun contr = (contr & 0xffffff80) | session->num;
354*4882a593Smuzhiyun CAPIMSG_SETCONTROL(skb->data, contr);
355*4882a593Smuzhiyun }
356*4882a593Smuzhiyun
357*4882a593Smuzhiyun capi_ctr_handle_message(ctrl, appl, skb);
358*4882a593Smuzhiyun }
359*4882a593Smuzhiyun
cmtp_load_firmware(struct capi_ctr * ctrl,capiloaddata * data)360*4882a593Smuzhiyun static int cmtp_load_firmware(struct capi_ctr *ctrl, capiloaddata *data)
361*4882a593Smuzhiyun {
362*4882a593Smuzhiyun BT_DBG("ctrl %p data %p", ctrl, data);
363*4882a593Smuzhiyun
364*4882a593Smuzhiyun return 0;
365*4882a593Smuzhiyun }
366*4882a593Smuzhiyun
cmtp_reset_ctr(struct capi_ctr * ctrl)367*4882a593Smuzhiyun static void cmtp_reset_ctr(struct capi_ctr *ctrl)
368*4882a593Smuzhiyun {
369*4882a593Smuzhiyun struct cmtp_session *session = ctrl->driverdata;
370*4882a593Smuzhiyun
371*4882a593Smuzhiyun BT_DBG("ctrl %p", ctrl);
372*4882a593Smuzhiyun
373*4882a593Smuzhiyun capi_ctr_down(ctrl);
374*4882a593Smuzhiyun
375*4882a593Smuzhiyun atomic_inc(&session->terminate);
376*4882a593Smuzhiyun wake_up_process(session->task);
377*4882a593Smuzhiyun }
378*4882a593Smuzhiyun
cmtp_register_appl(struct capi_ctr * ctrl,__u16 appl,capi_register_params * rp)379*4882a593Smuzhiyun static void cmtp_register_appl(struct capi_ctr *ctrl, __u16 appl, capi_register_params *rp)
380*4882a593Smuzhiyun {
381*4882a593Smuzhiyun DECLARE_WAITQUEUE(wait, current);
382*4882a593Smuzhiyun struct cmtp_session *session = ctrl->driverdata;
383*4882a593Smuzhiyun struct cmtp_application *application;
384*4882a593Smuzhiyun unsigned long timeo = CMTP_INTEROP_TIMEOUT;
385*4882a593Smuzhiyun unsigned char buf[8];
386*4882a593Smuzhiyun int err = 0, nconn, want = rp->level3cnt;
387*4882a593Smuzhiyun
388*4882a593Smuzhiyun BT_DBG("ctrl %p appl %d level3cnt %d datablkcnt %d datablklen %d",
389*4882a593Smuzhiyun ctrl, appl, rp->level3cnt, rp->datablkcnt, rp->datablklen);
390*4882a593Smuzhiyun
391*4882a593Smuzhiyun application = cmtp_application_add(session, appl);
392*4882a593Smuzhiyun if (!application) {
393*4882a593Smuzhiyun BT_ERR("Can't allocate memory for new application");
394*4882a593Smuzhiyun return;
395*4882a593Smuzhiyun }
396*4882a593Smuzhiyun
397*4882a593Smuzhiyun if (want < 0)
398*4882a593Smuzhiyun nconn = ctrl->profile.nbchannel * -want;
399*4882a593Smuzhiyun else
400*4882a593Smuzhiyun nconn = want;
401*4882a593Smuzhiyun
402*4882a593Smuzhiyun if (nconn == 0)
403*4882a593Smuzhiyun nconn = ctrl->profile.nbchannel;
404*4882a593Smuzhiyun
405*4882a593Smuzhiyun capimsg_setu16(buf, 0, nconn);
406*4882a593Smuzhiyun capimsg_setu16(buf, 2, rp->datablkcnt);
407*4882a593Smuzhiyun capimsg_setu16(buf, 4, rp->datablklen);
408*4882a593Smuzhiyun
409*4882a593Smuzhiyun application->state = BT_CONFIG;
410*4882a593Smuzhiyun application->msgnum = cmtp_msgnum_get(session);
411*4882a593Smuzhiyun
412*4882a593Smuzhiyun cmtp_send_interopmsg(session, CAPI_REQ, 0x0000, application->msgnum,
413*4882a593Smuzhiyun CAPI_FUNCTION_REGISTER, buf, 6);
414*4882a593Smuzhiyun
415*4882a593Smuzhiyun add_wait_queue(&session->wait, &wait);
416*4882a593Smuzhiyun while (1) {
417*4882a593Smuzhiyun set_current_state(TASK_INTERRUPTIBLE);
418*4882a593Smuzhiyun
419*4882a593Smuzhiyun if (!timeo) {
420*4882a593Smuzhiyun err = -EAGAIN;
421*4882a593Smuzhiyun break;
422*4882a593Smuzhiyun }
423*4882a593Smuzhiyun
424*4882a593Smuzhiyun if (application->state == BT_CLOSED) {
425*4882a593Smuzhiyun err = -application->err;
426*4882a593Smuzhiyun break;
427*4882a593Smuzhiyun }
428*4882a593Smuzhiyun
429*4882a593Smuzhiyun if (application->state == BT_CONNECTED)
430*4882a593Smuzhiyun break;
431*4882a593Smuzhiyun
432*4882a593Smuzhiyun if (signal_pending(current)) {
433*4882a593Smuzhiyun err = -EINTR;
434*4882a593Smuzhiyun break;
435*4882a593Smuzhiyun }
436*4882a593Smuzhiyun
437*4882a593Smuzhiyun timeo = schedule_timeout(timeo);
438*4882a593Smuzhiyun }
439*4882a593Smuzhiyun set_current_state(TASK_RUNNING);
440*4882a593Smuzhiyun remove_wait_queue(&session->wait, &wait);
441*4882a593Smuzhiyun
442*4882a593Smuzhiyun if (err) {
443*4882a593Smuzhiyun cmtp_application_del(session, application);
444*4882a593Smuzhiyun return;
445*4882a593Smuzhiyun }
446*4882a593Smuzhiyun }
447*4882a593Smuzhiyun
cmtp_release_appl(struct capi_ctr * ctrl,__u16 appl)448*4882a593Smuzhiyun static void cmtp_release_appl(struct capi_ctr *ctrl, __u16 appl)
449*4882a593Smuzhiyun {
450*4882a593Smuzhiyun struct cmtp_session *session = ctrl->driverdata;
451*4882a593Smuzhiyun struct cmtp_application *application;
452*4882a593Smuzhiyun
453*4882a593Smuzhiyun BT_DBG("ctrl %p appl %d", ctrl, appl);
454*4882a593Smuzhiyun
455*4882a593Smuzhiyun application = cmtp_application_get(session, CMTP_APPLID, appl);
456*4882a593Smuzhiyun if (!application) {
457*4882a593Smuzhiyun BT_ERR("Can't find application");
458*4882a593Smuzhiyun return;
459*4882a593Smuzhiyun }
460*4882a593Smuzhiyun
461*4882a593Smuzhiyun application->msgnum = cmtp_msgnum_get(session);
462*4882a593Smuzhiyun
463*4882a593Smuzhiyun cmtp_send_interopmsg(session, CAPI_REQ, application->mapping, application->msgnum,
464*4882a593Smuzhiyun CAPI_FUNCTION_RELEASE, NULL, 0);
465*4882a593Smuzhiyun
466*4882a593Smuzhiyun wait_event_interruptible_timeout(session->wait,
467*4882a593Smuzhiyun (application->state == BT_CLOSED), CMTP_INTEROP_TIMEOUT);
468*4882a593Smuzhiyun
469*4882a593Smuzhiyun cmtp_application_del(session, application);
470*4882a593Smuzhiyun }
471*4882a593Smuzhiyun
cmtp_send_message(struct capi_ctr * ctrl,struct sk_buff * skb)472*4882a593Smuzhiyun static u16 cmtp_send_message(struct capi_ctr *ctrl, struct sk_buff *skb)
473*4882a593Smuzhiyun {
474*4882a593Smuzhiyun struct cmtp_session *session = ctrl->driverdata;
475*4882a593Smuzhiyun struct cmtp_application *application;
476*4882a593Smuzhiyun __u16 appl;
477*4882a593Smuzhiyun __u32 contr;
478*4882a593Smuzhiyun
479*4882a593Smuzhiyun BT_DBG("ctrl %p skb %p", ctrl, skb);
480*4882a593Smuzhiyun
481*4882a593Smuzhiyun appl = CAPIMSG_APPID(skb->data);
482*4882a593Smuzhiyun contr = CAPIMSG_CONTROL(skb->data);
483*4882a593Smuzhiyun
484*4882a593Smuzhiyun application = cmtp_application_get(session, CMTP_APPLID, appl);
485*4882a593Smuzhiyun if ((!application) || (application->state != BT_CONNECTED)) {
486*4882a593Smuzhiyun BT_ERR("Can't find application with id %d", appl);
487*4882a593Smuzhiyun return CAPI_ILLAPPNR;
488*4882a593Smuzhiyun }
489*4882a593Smuzhiyun
490*4882a593Smuzhiyun CAPIMSG_SETAPPID(skb->data, application->mapping);
491*4882a593Smuzhiyun
492*4882a593Smuzhiyun if ((contr & 0x7f) == session->num) {
493*4882a593Smuzhiyun contr = (contr & 0xffffff80) | 0x01;
494*4882a593Smuzhiyun CAPIMSG_SETCONTROL(skb->data, contr);
495*4882a593Smuzhiyun }
496*4882a593Smuzhiyun
497*4882a593Smuzhiyun cmtp_send_capimsg(session, skb);
498*4882a593Smuzhiyun
499*4882a593Smuzhiyun return CAPI_NOERROR;
500*4882a593Smuzhiyun }
501*4882a593Smuzhiyun
cmtp_procinfo(struct capi_ctr * ctrl)502*4882a593Smuzhiyun static char *cmtp_procinfo(struct capi_ctr *ctrl)
503*4882a593Smuzhiyun {
504*4882a593Smuzhiyun return "CAPI Message Transport Protocol";
505*4882a593Smuzhiyun }
506*4882a593Smuzhiyun
cmtp_proc_show(struct seq_file * m,void * v)507*4882a593Smuzhiyun static int cmtp_proc_show(struct seq_file *m, void *v)
508*4882a593Smuzhiyun {
509*4882a593Smuzhiyun struct capi_ctr *ctrl = m->private;
510*4882a593Smuzhiyun struct cmtp_session *session = ctrl->driverdata;
511*4882a593Smuzhiyun struct cmtp_application *app;
512*4882a593Smuzhiyun
513*4882a593Smuzhiyun seq_printf(m, "%s\n\n", cmtp_procinfo(ctrl));
514*4882a593Smuzhiyun seq_printf(m, "addr %s\n", session->name);
515*4882a593Smuzhiyun seq_printf(m, "ctrl %d\n", session->num);
516*4882a593Smuzhiyun
517*4882a593Smuzhiyun list_for_each_entry(app, &session->applications, list) {
518*4882a593Smuzhiyun seq_printf(m, "appl %d -> %d\n", app->appl, app->mapping);
519*4882a593Smuzhiyun }
520*4882a593Smuzhiyun
521*4882a593Smuzhiyun return 0;
522*4882a593Smuzhiyun }
523*4882a593Smuzhiyun
cmtp_attach_device(struct cmtp_session * session)524*4882a593Smuzhiyun int cmtp_attach_device(struct cmtp_session *session)
525*4882a593Smuzhiyun {
526*4882a593Smuzhiyun unsigned char buf[4];
527*4882a593Smuzhiyun long ret;
528*4882a593Smuzhiyun
529*4882a593Smuzhiyun BT_DBG("session %p", session);
530*4882a593Smuzhiyun
531*4882a593Smuzhiyun capimsg_setu32(buf, 0, 0);
532*4882a593Smuzhiyun
533*4882a593Smuzhiyun cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, CMTP_INITIAL_MSGNUM,
534*4882a593Smuzhiyun CAPI_FUNCTION_GET_PROFILE, buf, 4);
535*4882a593Smuzhiyun
536*4882a593Smuzhiyun ret = wait_event_interruptible_timeout(session->wait,
537*4882a593Smuzhiyun session->ncontroller, CMTP_INTEROP_TIMEOUT);
538*4882a593Smuzhiyun
539*4882a593Smuzhiyun BT_INFO("Found %d CAPI controller(s) on device %s", session->ncontroller, session->name);
540*4882a593Smuzhiyun
541*4882a593Smuzhiyun if (!ret)
542*4882a593Smuzhiyun return -ETIMEDOUT;
543*4882a593Smuzhiyun
544*4882a593Smuzhiyun if (!session->ncontroller)
545*4882a593Smuzhiyun return -ENODEV;
546*4882a593Smuzhiyun
547*4882a593Smuzhiyun if (session->ncontroller > 1)
548*4882a593Smuzhiyun BT_INFO("Setting up only CAPI controller 1");
549*4882a593Smuzhiyun
550*4882a593Smuzhiyun session->ctrl.owner = THIS_MODULE;
551*4882a593Smuzhiyun session->ctrl.driverdata = session;
552*4882a593Smuzhiyun strcpy(session->ctrl.name, session->name);
553*4882a593Smuzhiyun
554*4882a593Smuzhiyun session->ctrl.driver_name = "cmtp";
555*4882a593Smuzhiyun session->ctrl.load_firmware = cmtp_load_firmware;
556*4882a593Smuzhiyun session->ctrl.reset_ctr = cmtp_reset_ctr;
557*4882a593Smuzhiyun session->ctrl.register_appl = cmtp_register_appl;
558*4882a593Smuzhiyun session->ctrl.release_appl = cmtp_release_appl;
559*4882a593Smuzhiyun session->ctrl.send_message = cmtp_send_message;
560*4882a593Smuzhiyun
561*4882a593Smuzhiyun session->ctrl.procinfo = cmtp_procinfo;
562*4882a593Smuzhiyun session->ctrl.proc_show = cmtp_proc_show;
563*4882a593Smuzhiyun
564*4882a593Smuzhiyun if (attach_capi_ctr(&session->ctrl) < 0) {
565*4882a593Smuzhiyun BT_ERR("Can't attach new controller");
566*4882a593Smuzhiyun return -EBUSY;
567*4882a593Smuzhiyun }
568*4882a593Smuzhiyun
569*4882a593Smuzhiyun session->num = session->ctrl.cnr;
570*4882a593Smuzhiyun
571*4882a593Smuzhiyun BT_DBG("session %p num %d", session, session->num);
572*4882a593Smuzhiyun
573*4882a593Smuzhiyun capimsg_setu32(buf, 0, 1);
574*4882a593Smuzhiyun
575*4882a593Smuzhiyun cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session),
576*4882a593Smuzhiyun CAPI_FUNCTION_GET_MANUFACTURER, buf, 4);
577*4882a593Smuzhiyun
578*4882a593Smuzhiyun cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session),
579*4882a593Smuzhiyun CAPI_FUNCTION_GET_VERSION, buf, 4);
580*4882a593Smuzhiyun
581*4882a593Smuzhiyun cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session),
582*4882a593Smuzhiyun CAPI_FUNCTION_GET_SERIAL_NUMBER, buf, 4);
583*4882a593Smuzhiyun
584*4882a593Smuzhiyun cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session),
585*4882a593Smuzhiyun CAPI_FUNCTION_GET_PROFILE, buf, 4);
586*4882a593Smuzhiyun
587*4882a593Smuzhiyun return 0;
588*4882a593Smuzhiyun }
589*4882a593Smuzhiyun
cmtp_detach_device(struct cmtp_session * session)590*4882a593Smuzhiyun void cmtp_detach_device(struct cmtp_session *session)
591*4882a593Smuzhiyun {
592*4882a593Smuzhiyun BT_DBG("session %p", session);
593*4882a593Smuzhiyun
594*4882a593Smuzhiyun detach_capi_ctr(&session->ctrl);
595*4882a593Smuzhiyun }
596