File Index
Files and directories
Sign in
FILE

capi.c

File preview with syntax highlighting.

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