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