162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci   CMTP implementation for Linux Bluetooth stack (BlueZ).
362306a36Sopenharmony_ci   Copyright (C) 2002-2003 Marcel Holtmann <marcel@holtmann.org>
462306a36Sopenharmony_ci
562306a36Sopenharmony_ci   This program is free software; you can redistribute it and/or modify
662306a36Sopenharmony_ci   it under the terms of the GNU General Public License version 2 as
762306a36Sopenharmony_ci   published by the Free Software Foundation;
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
1062306a36Sopenharmony_ci   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1162306a36Sopenharmony_ci   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
1262306a36Sopenharmony_ci   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
1362306a36Sopenharmony_ci   CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
1462306a36Sopenharmony_ci   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1562306a36Sopenharmony_ci   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1662306a36Sopenharmony_ci   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
1962306a36Sopenharmony_ci   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
2062306a36Sopenharmony_ci   SOFTWARE IS DISCLAIMED.
2162306a36Sopenharmony_ci*/
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#include <linux/export.h>
2462306a36Sopenharmony_ci#include <linux/proc_fs.h>
2562306a36Sopenharmony_ci#include <linux/seq_file.h>
2662306a36Sopenharmony_ci#include <linux/types.h>
2762306a36Sopenharmony_ci#include <linux/errno.h>
2862306a36Sopenharmony_ci#include <linux/kernel.h>
2962306a36Sopenharmony_ci#include <linux/sched/signal.h>
3062306a36Sopenharmony_ci#include <linux/slab.h>
3162306a36Sopenharmony_ci#include <linux/poll.h>
3262306a36Sopenharmony_ci#include <linux/fcntl.h>
3362306a36Sopenharmony_ci#include <linux/skbuff.h>
3462306a36Sopenharmony_ci#include <linux/socket.h>
3562306a36Sopenharmony_ci#include <linux/ioctl.h>
3662306a36Sopenharmony_ci#include <linux/file.h>
3762306a36Sopenharmony_ci#include <linux/wait.h>
3862306a36Sopenharmony_ci#include <linux/kthread.h>
3962306a36Sopenharmony_ci#include <net/sock.h>
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci#include <linux/isdn/capilli.h>
4262306a36Sopenharmony_ci#include <linux/isdn/capicmd.h>
4362306a36Sopenharmony_ci#include <linux/isdn/capiutil.h>
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci#include "cmtp.h"
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci#define CAPI_INTEROPERABILITY		0x20
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci#define CAPI_INTEROPERABILITY_REQ	CAPICMD(CAPI_INTEROPERABILITY, CAPI_REQ)
5062306a36Sopenharmony_ci#define CAPI_INTEROPERABILITY_CONF	CAPICMD(CAPI_INTEROPERABILITY, CAPI_CONF)
5162306a36Sopenharmony_ci#define CAPI_INTEROPERABILITY_IND	CAPICMD(CAPI_INTEROPERABILITY, CAPI_IND)
5262306a36Sopenharmony_ci#define CAPI_INTEROPERABILITY_RESP	CAPICMD(CAPI_INTEROPERABILITY, CAPI_RESP)
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci#define CAPI_INTEROPERABILITY_REQ_LEN	(CAPI_MSG_BASELEN + 2)
5562306a36Sopenharmony_ci#define CAPI_INTEROPERABILITY_CONF_LEN	(CAPI_MSG_BASELEN + 4)
5662306a36Sopenharmony_ci#define CAPI_INTEROPERABILITY_IND_LEN	(CAPI_MSG_BASELEN + 2)
5762306a36Sopenharmony_ci#define CAPI_INTEROPERABILITY_RESP_LEN	(CAPI_MSG_BASELEN + 2)
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci#define CAPI_FUNCTION_REGISTER		0
6062306a36Sopenharmony_ci#define CAPI_FUNCTION_RELEASE		1
6162306a36Sopenharmony_ci#define CAPI_FUNCTION_GET_PROFILE	2
6262306a36Sopenharmony_ci#define CAPI_FUNCTION_GET_MANUFACTURER	3
6362306a36Sopenharmony_ci#define CAPI_FUNCTION_GET_VERSION	4
6462306a36Sopenharmony_ci#define CAPI_FUNCTION_GET_SERIAL_NUMBER	5
6562306a36Sopenharmony_ci#define CAPI_FUNCTION_MANUFACTURER	6
6662306a36Sopenharmony_ci#define CAPI_FUNCTION_LOOPBACK		7
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci#define CMTP_MSGNUM	1
7062306a36Sopenharmony_ci#define CMTP_APPLID	2
7162306a36Sopenharmony_ci#define CMTP_MAPPING	3
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_cistatic struct cmtp_application *cmtp_application_add(struct cmtp_session *session, __u16 appl)
7462306a36Sopenharmony_ci{
7562306a36Sopenharmony_ci	struct cmtp_application *app = kzalloc(sizeof(*app), GFP_KERNEL);
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	BT_DBG("session %p application %p appl %u", session, app, appl);
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	if (!app)
8062306a36Sopenharmony_ci		return NULL;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	app->state = BT_OPEN;
8362306a36Sopenharmony_ci	app->appl = appl;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	list_add_tail(&app->list, &session->applications);
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	return app;
8862306a36Sopenharmony_ci}
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_cistatic void cmtp_application_del(struct cmtp_session *session, struct cmtp_application *app)
9162306a36Sopenharmony_ci{
9262306a36Sopenharmony_ci	BT_DBG("session %p application %p", session, app);
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	if (app) {
9562306a36Sopenharmony_ci		list_del(&app->list);
9662306a36Sopenharmony_ci		kfree(app);
9762306a36Sopenharmony_ci	}
9862306a36Sopenharmony_ci}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_cistatic struct cmtp_application *cmtp_application_get(struct cmtp_session *session, int pattern, __u16 value)
10162306a36Sopenharmony_ci{
10262306a36Sopenharmony_ci	struct cmtp_application *app;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	list_for_each_entry(app, &session->applications, list) {
10562306a36Sopenharmony_ci		switch (pattern) {
10662306a36Sopenharmony_ci		case CMTP_MSGNUM:
10762306a36Sopenharmony_ci			if (app->msgnum == value)
10862306a36Sopenharmony_ci				return app;
10962306a36Sopenharmony_ci			break;
11062306a36Sopenharmony_ci		case CMTP_APPLID:
11162306a36Sopenharmony_ci			if (app->appl == value)
11262306a36Sopenharmony_ci				return app;
11362306a36Sopenharmony_ci			break;
11462306a36Sopenharmony_ci		case CMTP_MAPPING:
11562306a36Sopenharmony_ci			if (app->mapping == value)
11662306a36Sopenharmony_ci				return app;
11762306a36Sopenharmony_ci			break;
11862306a36Sopenharmony_ci		}
11962306a36Sopenharmony_ci	}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	return NULL;
12262306a36Sopenharmony_ci}
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_cistatic int cmtp_msgnum_get(struct cmtp_session *session)
12562306a36Sopenharmony_ci{
12662306a36Sopenharmony_ci	session->msgnum++;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	if ((session->msgnum & 0xff) > 200)
12962306a36Sopenharmony_ci		session->msgnum = CMTP_INITIAL_MSGNUM + 1;
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	return session->msgnum;
13262306a36Sopenharmony_ci}
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_cistatic void cmtp_send_capimsg(struct cmtp_session *session, struct sk_buff *skb)
13562306a36Sopenharmony_ci{
13662306a36Sopenharmony_ci	struct cmtp_scb *scb = (void *) skb->cb;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	BT_DBG("session %p skb %p len %u", session, skb, skb->len);
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	scb->id = -1;
14162306a36Sopenharmony_ci	scb->data = (CAPIMSG_COMMAND(skb->data) == CAPI_DATA_B3);
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	skb_queue_tail(&session->transmit, skb);
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	wake_up_interruptible(sk_sleep(session->sock->sk));
14662306a36Sopenharmony_ci}
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_cistatic void cmtp_send_interopmsg(struct cmtp_session *session,
14962306a36Sopenharmony_ci					__u8 subcmd, __u16 appl, __u16 msgnum,
15062306a36Sopenharmony_ci					__u16 function, unsigned char *buf, int len)
15162306a36Sopenharmony_ci{
15262306a36Sopenharmony_ci	struct sk_buff *skb;
15362306a36Sopenharmony_ci	unsigned char *s;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	BT_DBG("session %p subcmd 0x%02x appl %u msgnum %u", session, subcmd, appl, msgnum);
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	skb = alloc_skb(CAPI_MSG_BASELEN + 6 + len, GFP_ATOMIC);
15862306a36Sopenharmony_ci	if (!skb) {
15962306a36Sopenharmony_ci		BT_ERR("Can't allocate memory for interoperability packet");
16062306a36Sopenharmony_ci		return;
16162306a36Sopenharmony_ci	}
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	s = skb_put(skb, CAPI_MSG_BASELEN + 6 + len);
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	capimsg_setu16(s, 0, CAPI_MSG_BASELEN + 6 + len);
16662306a36Sopenharmony_ci	capimsg_setu16(s, 2, appl);
16762306a36Sopenharmony_ci	capimsg_setu8 (s, 4, CAPI_INTEROPERABILITY);
16862306a36Sopenharmony_ci	capimsg_setu8 (s, 5, subcmd);
16962306a36Sopenharmony_ci	capimsg_setu16(s, 6, msgnum);
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	/* Interoperability selector (Bluetooth Device Management) */
17262306a36Sopenharmony_ci	capimsg_setu16(s, 8, 0x0001);
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	capimsg_setu8 (s, 10, 3 + len);
17562306a36Sopenharmony_ci	capimsg_setu16(s, 11, function);
17662306a36Sopenharmony_ci	capimsg_setu8 (s, 13, len);
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	if (len > 0)
17962306a36Sopenharmony_ci		memcpy(s + 14, buf, len);
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	cmtp_send_capimsg(session, skb);
18262306a36Sopenharmony_ci}
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_cistatic void cmtp_recv_interopmsg(struct cmtp_session *session, struct sk_buff *skb)
18562306a36Sopenharmony_ci{
18662306a36Sopenharmony_ci	struct capi_ctr *ctrl = &session->ctrl;
18762306a36Sopenharmony_ci	struct cmtp_application *application;
18862306a36Sopenharmony_ci	__u16 appl, msgnum, func, info;
18962306a36Sopenharmony_ci	__u32 controller;
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	BT_DBG("session %p skb %p len %u", session, skb, skb->len);
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	switch (CAPIMSG_SUBCOMMAND(skb->data)) {
19462306a36Sopenharmony_ci	case CAPI_CONF:
19562306a36Sopenharmony_ci		if (skb->len < CAPI_MSG_BASELEN + 10)
19662306a36Sopenharmony_ci			break;
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci		func = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 5);
19962306a36Sopenharmony_ci		info = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 8);
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci		switch (func) {
20262306a36Sopenharmony_ci		case CAPI_FUNCTION_REGISTER:
20362306a36Sopenharmony_ci			msgnum = CAPIMSG_MSGID(skb->data);
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci			application = cmtp_application_get(session, CMTP_MSGNUM, msgnum);
20662306a36Sopenharmony_ci			if (application) {
20762306a36Sopenharmony_ci				application->state = BT_CONNECTED;
20862306a36Sopenharmony_ci				application->msgnum = 0;
20962306a36Sopenharmony_ci				application->mapping = CAPIMSG_APPID(skb->data);
21062306a36Sopenharmony_ci				wake_up_interruptible(&session->wait);
21162306a36Sopenharmony_ci			}
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci			break;
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci		case CAPI_FUNCTION_RELEASE:
21662306a36Sopenharmony_ci			appl = CAPIMSG_APPID(skb->data);
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci			application = cmtp_application_get(session, CMTP_MAPPING, appl);
21962306a36Sopenharmony_ci			if (application) {
22062306a36Sopenharmony_ci				application->state = BT_CLOSED;
22162306a36Sopenharmony_ci				application->msgnum = 0;
22262306a36Sopenharmony_ci				wake_up_interruptible(&session->wait);
22362306a36Sopenharmony_ci			}
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci			break;
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci		case CAPI_FUNCTION_GET_PROFILE:
22862306a36Sopenharmony_ci			if (skb->len < CAPI_MSG_BASELEN + 11 + sizeof(capi_profile))
22962306a36Sopenharmony_ci				break;
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci			controller = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 11);
23262306a36Sopenharmony_ci			msgnum = CAPIMSG_MSGID(skb->data);
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci			if (!info && (msgnum == CMTP_INITIAL_MSGNUM)) {
23562306a36Sopenharmony_ci				session->ncontroller = controller;
23662306a36Sopenharmony_ci				wake_up_interruptible(&session->wait);
23762306a36Sopenharmony_ci				break;
23862306a36Sopenharmony_ci			}
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci			if (!info && ctrl) {
24162306a36Sopenharmony_ci				memcpy(&ctrl->profile,
24262306a36Sopenharmony_ci					skb->data + CAPI_MSG_BASELEN + 11,
24362306a36Sopenharmony_ci					sizeof(capi_profile));
24462306a36Sopenharmony_ci				session->state = BT_CONNECTED;
24562306a36Sopenharmony_ci				capi_ctr_ready(ctrl);
24662306a36Sopenharmony_ci			}
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci			break;
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci		case CAPI_FUNCTION_GET_MANUFACTURER:
25162306a36Sopenharmony_ci			if (skb->len < CAPI_MSG_BASELEN + 15)
25262306a36Sopenharmony_ci				break;
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci			if (!info && ctrl) {
25562306a36Sopenharmony_ci				int len = min_t(uint, CAPI_MANUFACTURER_LEN,
25662306a36Sopenharmony_ci						skb->data[CAPI_MSG_BASELEN + 14]);
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci				memset(ctrl->manu, 0, CAPI_MANUFACTURER_LEN);
25962306a36Sopenharmony_ci				strncpy(ctrl->manu,
26062306a36Sopenharmony_ci					skb->data + CAPI_MSG_BASELEN + 15, len);
26162306a36Sopenharmony_ci			}
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci			break;
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci		case CAPI_FUNCTION_GET_VERSION:
26662306a36Sopenharmony_ci			if (skb->len < CAPI_MSG_BASELEN + 32)
26762306a36Sopenharmony_ci				break;
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci			if (!info && ctrl) {
27062306a36Sopenharmony_ci				ctrl->version.majorversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 16);
27162306a36Sopenharmony_ci				ctrl->version.minorversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 20);
27262306a36Sopenharmony_ci				ctrl->version.majormanuversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 24);
27362306a36Sopenharmony_ci				ctrl->version.minormanuversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 28);
27462306a36Sopenharmony_ci			}
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci			break;
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci		case CAPI_FUNCTION_GET_SERIAL_NUMBER:
27962306a36Sopenharmony_ci			if (skb->len < CAPI_MSG_BASELEN + 17)
28062306a36Sopenharmony_ci				break;
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci			if (!info && ctrl) {
28362306a36Sopenharmony_ci				int len = min_t(uint, CAPI_SERIAL_LEN,
28462306a36Sopenharmony_ci						skb->data[CAPI_MSG_BASELEN + 16]);
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci				memset(ctrl->serial, 0, CAPI_SERIAL_LEN);
28762306a36Sopenharmony_ci				strncpy(ctrl->serial,
28862306a36Sopenharmony_ci					skb->data + CAPI_MSG_BASELEN + 17, len);
28962306a36Sopenharmony_ci			}
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci			break;
29262306a36Sopenharmony_ci		}
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci		break;
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	case CAPI_IND:
29762306a36Sopenharmony_ci		if (skb->len < CAPI_MSG_BASELEN + 6)
29862306a36Sopenharmony_ci			break;
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci		func = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 3);
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci		if (func == CAPI_FUNCTION_LOOPBACK) {
30362306a36Sopenharmony_ci			int len = min_t(uint, skb->len - CAPI_MSG_BASELEN - 6,
30462306a36Sopenharmony_ci						skb->data[CAPI_MSG_BASELEN + 5]);
30562306a36Sopenharmony_ci			appl = CAPIMSG_APPID(skb->data);
30662306a36Sopenharmony_ci			msgnum = CAPIMSG_MSGID(skb->data);
30762306a36Sopenharmony_ci			cmtp_send_interopmsg(session, CAPI_RESP, appl, msgnum, func,
30862306a36Sopenharmony_ci						skb->data + CAPI_MSG_BASELEN + 6, len);
30962306a36Sopenharmony_ci		}
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci		break;
31262306a36Sopenharmony_ci	}
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	kfree_skb(skb);
31562306a36Sopenharmony_ci}
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_civoid cmtp_recv_capimsg(struct cmtp_session *session, struct sk_buff *skb)
31862306a36Sopenharmony_ci{
31962306a36Sopenharmony_ci	struct capi_ctr *ctrl = &session->ctrl;
32062306a36Sopenharmony_ci	struct cmtp_application *application;
32162306a36Sopenharmony_ci	__u16 appl;
32262306a36Sopenharmony_ci	__u32 contr;
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	BT_DBG("session %p skb %p len %u", session, skb, skb->len);
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	if (skb->len < CAPI_MSG_BASELEN)
32762306a36Sopenharmony_ci		return;
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	if (CAPIMSG_COMMAND(skb->data) == CAPI_INTEROPERABILITY) {
33062306a36Sopenharmony_ci		cmtp_recv_interopmsg(session, skb);
33162306a36Sopenharmony_ci		return;
33262306a36Sopenharmony_ci	}
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	if (session->flags & BIT(CMTP_LOOPBACK)) {
33562306a36Sopenharmony_ci		kfree_skb(skb);
33662306a36Sopenharmony_ci		return;
33762306a36Sopenharmony_ci	}
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	appl = CAPIMSG_APPID(skb->data);
34062306a36Sopenharmony_ci	contr = CAPIMSG_CONTROL(skb->data);
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	application = cmtp_application_get(session, CMTP_MAPPING, appl);
34362306a36Sopenharmony_ci	if (application) {
34462306a36Sopenharmony_ci		appl = application->appl;
34562306a36Sopenharmony_ci		CAPIMSG_SETAPPID(skb->data, appl);
34662306a36Sopenharmony_ci	} else {
34762306a36Sopenharmony_ci		BT_ERR("Can't find application with id %u", appl);
34862306a36Sopenharmony_ci		kfree_skb(skb);
34962306a36Sopenharmony_ci		return;
35062306a36Sopenharmony_ci	}
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	if ((contr & 0x7f) == 0x01) {
35362306a36Sopenharmony_ci		contr = (contr & 0xffffff80) | session->num;
35462306a36Sopenharmony_ci		CAPIMSG_SETCONTROL(skb->data, contr);
35562306a36Sopenharmony_ci	}
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	capi_ctr_handle_message(ctrl, appl, skb);
35862306a36Sopenharmony_ci}
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_cistatic int cmtp_load_firmware(struct capi_ctr *ctrl, capiloaddata *data)
36162306a36Sopenharmony_ci{
36262306a36Sopenharmony_ci	BT_DBG("ctrl %p data %p", ctrl, data);
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	return 0;
36562306a36Sopenharmony_ci}
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_cistatic void cmtp_reset_ctr(struct capi_ctr *ctrl)
36862306a36Sopenharmony_ci{
36962306a36Sopenharmony_ci	struct cmtp_session *session = ctrl->driverdata;
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	BT_DBG("ctrl %p", ctrl);
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	capi_ctr_down(ctrl);
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	atomic_inc(&session->terminate);
37662306a36Sopenharmony_ci	wake_up_process(session->task);
37762306a36Sopenharmony_ci}
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_cistatic void cmtp_register_appl(struct capi_ctr *ctrl, __u16 appl, capi_register_params *rp)
38062306a36Sopenharmony_ci{
38162306a36Sopenharmony_ci	DECLARE_WAITQUEUE(wait, current);
38262306a36Sopenharmony_ci	struct cmtp_session *session = ctrl->driverdata;
38362306a36Sopenharmony_ci	struct cmtp_application *application;
38462306a36Sopenharmony_ci	unsigned long timeo = CMTP_INTEROP_TIMEOUT;
38562306a36Sopenharmony_ci	unsigned char buf[8];
38662306a36Sopenharmony_ci	int err = 0, nconn, want = rp->level3cnt;
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	BT_DBG("ctrl %p appl %u level3cnt %u datablkcnt %u datablklen %u",
38962306a36Sopenharmony_ci	       ctrl, appl, rp->level3cnt, rp->datablkcnt, rp->datablklen);
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	application = cmtp_application_add(session, appl);
39262306a36Sopenharmony_ci	if (!application) {
39362306a36Sopenharmony_ci		BT_ERR("Can't allocate memory for new application");
39462306a36Sopenharmony_ci		return;
39562306a36Sopenharmony_ci	}
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	if (want < 0)
39862306a36Sopenharmony_ci		nconn = ctrl->profile.nbchannel * -want;
39962306a36Sopenharmony_ci	else
40062306a36Sopenharmony_ci		nconn = want;
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	if (nconn == 0)
40362306a36Sopenharmony_ci		nconn = ctrl->profile.nbchannel;
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	capimsg_setu16(buf, 0, nconn);
40662306a36Sopenharmony_ci	capimsg_setu16(buf, 2, rp->datablkcnt);
40762306a36Sopenharmony_ci	capimsg_setu16(buf, 4, rp->datablklen);
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	application->state = BT_CONFIG;
41062306a36Sopenharmony_ci	application->msgnum = cmtp_msgnum_get(session);
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci	cmtp_send_interopmsg(session, CAPI_REQ, 0x0000, application->msgnum,
41362306a36Sopenharmony_ci				CAPI_FUNCTION_REGISTER, buf, 6);
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	add_wait_queue(&session->wait, &wait);
41662306a36Sopenharmony_ci	while (1) {
41762306a36Sopenharmony_ci		set_current_state(TASK_INTERRUPTIBLE);
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci		if (!timeo) {
42062306a36Sopenharmony_ci			err = -EAGAIN;
42162306a36Sopenharmony_ci			break;
42262306a36Sopenharmony_ci		}
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci		if (application->state == BT_CLOSED) {
42562306a36Sopenharmony_ci			err = -application->err;
42662306a36Sopenharmony_ci			break;
42762306a36Sopenharmony_ci		}
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci		if (application->state == BT_CONNECTED)
43062306a36Sopenharmony_ci			break;
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci		if (signal_pending(current)) {
43362306a36Sopenharmony_ci			err = -EINTR;
43462306a36Sopenharmony_ci			break;
43562306a36Sopenharmony_ci		}
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci		timeo = schedule_timeout(timeo);
43862306a36Sopenharmony_ci	}
43962306a36Sopenharmony_ci	set_current_state(TASK_RUNNING);
44062306a36Sopenharmony_ci	remove_wait_queue(&session->wait, &wait);
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	if (err) {
44362306a36Sopenharmony_ci		cmtp_application_del(session, application);
44462306a36Sopenharmony_ci		return;
44562306a36Sopenharmony_ci	}
44662306a36Sopenharmony_ci}
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_cistatic void cmtp_release_appl(struct capi_ctr *ctrl, __u16 appl)
44962306a36Sopenharmony_ci{
45062306a36Sopenharmony_ci	struct cmtp_session *session = ctrl->driverdata;
45162306a36Sopenharmony_ci	struct cmtp_application *application;
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	BT_DBG("ctrl %p appl %u", ctrl, appl);
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	application = cmtp_application_get(session, CMTP_APPLID, appl);
45662306a36Sopenharmony_ci	if (!application) {
45762306a36Sopenharmony_ci		BT_ERR("Can't find application");
45862306a36Sopenharmony_ci		return;
45962306a36Sopenharmony_ci	}
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	application->msgnum = cmtp_msgnum_get(session);
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	cmtp_send_interopmsg(session, CAPI_REQ, application->mapping, application->msgnum,
46462306a36Sopenharmony_ci				CAPI_FUNCTION_RELEASE, NULL, 0);
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	wait_event_interruptible_timeout(session->wait,
46762306a36Sopenharmony_ci			(application->state == BT_CLOSED), CMTP_INTEROP_TIMEOUT);
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	cmtp_application_del(session, application);
47062306a36Sopenharmony_ci}
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_cistatic u16 cmtp_send_message(struct capi_ctr *ctrl, struct sk_buff *skb)
47362306a36Sopenharmony_ci{
47462306a36Sopenharmony_ci	struct cmtp_session *session = ctrl->driverdata;
47562306a36Sopenharmony_ci	struct cmtp_application *application;
47662306a36Sopenharmony_ci	__u16 appl;
47762306a36Sopenharmony_ci	__u32 contr;
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	BT_DBG("ctrl %p skb %p", ctrl, skb);
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	appl = CAPIMSG_APPID(skb->data);
48262306a36Sopenharmony_ci	contr = CAPIMSG_CONTROL(skb->data);
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	application = cmtp_application_get(session, CMTP_APPLID, appl);
48562306a36Sopenharmony_ci	if ((!application) || (application->state != BT_CONNECTED)) {
48662306a36Sopenharmony_ci		BT_ERR("Can't find application with id %u", appl);
48762306a36Sopenharmony_ci		return CAPI_ILLAPPNR;
48862306a36Sopenharmony_ci	}
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	CAPIMSG_SETAPPID(skb->data, application->mapping);
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	if ((contr & 0x7f) == session->num) {
49362306a36Sopenharmony_ci		contr = (contr & 0xffffff80) | 0x01;
49462306a36Sopenharmony_ci		CAPIMSG_SETCONTROL(skb->data, contr);
49562306a36Sopenharmony_ci	}
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	cmtp_send_capimsg(session, skb);
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	return CAPI_NOERROR;
50062306a36Sopenharmony_ci}
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_cistatic char *cmtp_procinfo(struct capi_ctr *ctrl)
50362306a36Sopenharmony_ci{
50462306a36Sopenharmony_ci	return "CAPI Message Transport Protocol";
50562306a36Sopenharmony_ci}
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_cistatic int cmtp_proc_show(struct seq_file *m, void *v)
50862306a36Sopenharmony_ci{
50962306a36Sopenharmony_ci	struct capi_ctr *ctrl = m->private;
51062306a36Sopenharmony_ci	struct cmtp_session *session = ctrl->driverdata;
51162306a36Sopenharmony_ci	struct cmtp_application *app;
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	seq_printf(m, "%s\n\n", cmtp_procinfo(ctrl));
51462306a36Sopenharmony_ci	seq_printf(m, "addr %s\n", session->name);
51562306a36Sopenharmony_ci	seq_printf(m, "ctrl %d\n", session->num);
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	list_for_each_entry(app, &session->applications, list) {
51862306a36Sopenharmony_ci		seq_printf(m, "appl %u -> %u\n", app->appl, app->mapping);
51962306a36Sopenharmony_ci	}
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci	return 0;
52262306a36Sopenharmony_ci}
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ciint cmtp_attach_device(struct cmtp_session *session)
52562306a36Sopenharmony_ci{
52662306a36Sopenharmony_ci	unsigned char buf[4];
52762306a36Sopenharmony_ci	long ret;
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	BT_DBG("session %p", session);
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	capimsg_setu32(buf, 0, 0);
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci	cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, CMTP_INITIAL_MSGNUM,
53462306a36Sopenharmony_ci				CAPI_FUNCTION_GET_PROFILE, buf, 4);
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	ret = wait_event_interruptible_timeout(session->wait,
53762306a36Sopenharmony_ci			session->ncontroller, CMTP_INTEROP_TIMEOUT);
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci	BT_INFO("Found %d CAPI controller(s) on device %s", session->ncontroller, session->name);
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci	if (!ret)
54262306a36Sopenharmony_ci		return -ETIMEDOUT;
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	if (!session->ncontroller)
54562306a36Sopenharmony_ci		return -ENODEV;
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	if (session->ncontroller > 1)
54862306a36Sopenharmony_ci		BT_INFO("Setting up only CAPI controller 1");
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	session->ctrl.owner      = THIS_MODULE;
55162306a36Sopenharmony_ci	session->ctrl.driverdata = session;
55262306a36Sopenharmony_ci	strcpy(session->ctrl.name, session->name);
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci	session->ctrl.driver_name   = "cmtp";
55562306a36Sopenharmony_ci	session->ctrl.load_firmware = cmtp_load_firmware;
55662306a36Sopenharmony_ci	session->ctrl.reset_ctr     = cmtp_reset_ctr;
55762306a36Sopenharmony_ci	session->ctrl.register_appl = cmtp_register_appl;
55862306a36Sopenharmony_ci	session->ctrl.release_appl  = cmtp_release_appl;
55962306a36Sopenharmony_ci	session->ctrl.send_message  = cmtp_send_message;
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci	session->ctrl.procinfo      = cmtp_procinfo;
56262306a36Sopenharmony_ci	session->ctrl.proc_show     = cmtp_proc_show;
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci	if (attach_capi_ctr(&session->ctrl) < 0) {
56562306a36Sopenharmony_ci		BT_ERR("Can't attach new controller");
56662306a36Sopenharmony_ci		return -EBUSY;
56762306a36Sopenharmony_ci	}
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	session->num = session->ctrl.cnr;
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci	BT_DBG("session %p num %d", session, session->num);
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci	capimsg_setu32(buf, 0, 1);
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci	cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session),
57662306a36Sopenharmony_ci				CAPI_FUNCTION_GET_MANUFACTURER, buf, 4);
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci	cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session),
57962306a36Sopenharmony_ci				CAPI_FUNCTION_GET_VERSION, buf, 4);
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci	cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session),
58262306a36Sopenharmony_ci				CAPI_FUNCTION_GET_SERIAL_NUMBER, buf, 4);
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci	cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session),
58562306a36Sopenharmony_ci				CAPI_FUNCTION_GET_PROFILE, buf, 4);
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci	return 0;
58862306a36Sopenharmony_ci}
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_civoid cmtp_detach_device(struct cmtp_session *session)
59162306a36Sopenharmony_ci{
59262306a36Sopenharmony_ci	BT_DBG("session %p", session);
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci	detach_capi_ctr(&session->ctrl);
59562306a36Sopenharmony_ci}
596