18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci   CMTP implementation for Linux Bluetooth stack (BlueZ).
38c2ecf20Sopenharmony_ci   Copyright (C) 2002-2003 Marcel Holtmann <marcel@holtmann.org>
48c2ecf20Sopenharmony_ci
58c2ecf20Sopenharmony_ci   This program is free software; you can redistribute it and/or modify
68c2ecf20Sopenharmony_ci   it under the terms of the GNU General Public License version 2 as
78c2ecf20Sopenharmony_ci   published by the Free Software Foundation;
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
108c2ecf20Sopenharmony_ci   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
118c2ecf20Sopenharmony_ci   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
128c2ecf20Sopenharmony_ci   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
138c2ecf20Sopenharmony_ci   CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
148c2ecf20Sopenharmony_ci   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
158c2ecf20Sopenharmony_ci   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
168c2ecf20Sopenharmony_ci   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
198c2ecf20Sopenharmony_ci   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
208c2ecf20Sopenharmony_ci   SOFTWARE IS DISCLAIMED.
218c2ecf20Sopenharmony_ci*/
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#include <linux/export.h>
248c2ecf20Sopenharmony_ci#include <linux/proc_fs.h>
258c2ecf20Sopenharmony_ci#include <linux/seq_file.h>
268c2ecf20Sopenharmony_ci#include <linux/types.h>
278c2ecf20Sopenharmony_ci#include <linux/errno.h>
288c2ecf20Sopenharmony_ci#include <linux/kernel.h>
298c2ecf20Sopenharmony_ci#include <linux/sched/signal.h>
308c2ecf20Sopenharmony_ci#include <linux/slab.h>
318c2ecf20Sopenharmony_ci#include <linux/poll.h>
328c2ecf20Sopenharmony_ci#include <linux/fcntl.h>
338c2ecf20Sopenharmony_ci#include <linux/skbuff.h>
348c2ecf20Sopenharmony_ci#include <linux/socket.h>
358c2ecf20Sopenharmony_ci#include <linux/ioctl.h>
368c2ecf20Sopenharmony_ci#include <linux/file.h>
378c2ecf20Sopenharmony_ci#include <linux/wait.h>
388c2ecf20Sopenharmony_ci#include <linux/kthread.h>
398c2ecf20Sopenharmony_ci#include <net/sock.h>
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci#include <linux/isdn/capilli.h>
428c2ecf20Sopenharmony_ci#include <linux/isdn/capicmd.h>
438c2ecf20Sopenharmony_ci#include <linux/isdn/capiutil.h>
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci#include "cmtp.h"
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci#define CAPI_INTEROPERABILITY		0x20
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci#define CAPI_INTEROPERABILITY_REQ	CAPICMD(CAPI_INTEROPERABILITY, CAPI_REQ)
508c2ecf20Sopenharmony_ci#define CAPI_INTEROPERABILITY_CONF	CAPICMD(CAPI_INTEROPERABILITY, CAPI_CONF)
518c2ecf20Sopenharmony_ci#define CAPI_INTEROPERABILITY_IND	CAPICMD(CAPI_INTEROPERABILITY, CAPI_IND)
528c2ecf20Sopenharmony_ci#define CAPI_INTEROPERABILITY_RESP	CAPICMD(CAPI_INTEROPERABILITY, CAPI_RESP)
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci#define CAPI_INTEROPERABILITY_REQ_LEN	(CAPI_MSG_BASELEN + 2)
558c2ecf20Sopenharmony_ci#define CAPI_INTEROPERABILITY_CONF_LEN	(CAPI_MSG_BASELEN + 4)
568c2ecf20Sopenharmony_ci#define CAPI_INTEROPERABILITY_IND_LEN	(CAPI_MSG_BASELEN + 2)
578c2ecf20Sopenharmony_ci#define CAPI_INTEROPERABILITY_RESP_LEN	(CAPI_MSG_BASELEN + 2)
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci#define CAPI_FUNCTION_REGISTER		0
608c2ecf20Sopenharmony_ci#define CAPI_FUNCTION_RELEASE		1
618c2ecf20Sopenharmony_ci#define CAPI_FUNCTION_GET_PROFILE	2
628c2ecf20Sopenharmony_ci#define CAPI_FUNCTION_GET_MANUFACTURER	3
638c2ecf20Sopenharmony_ci#define CAPI_FUNCTION_GET_VERSION	4
648c2ecf20Sopenharmony_ci#define CAPI_FUNCTION_GET_SERIAL_NUMBER	5
658c2ecf20Sopenharmony_ci#define CAPI_FUNCTION_MANUFACTURER	6
668c2ecf20Sopenharmony_ci#define CAPI_FUNCTION_LOOPBACK		7
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci#define CMTP_MSGNUM	1
708c2ecf20Sopenharmony_ci#define CMTP_APPLID	2
718c2ecf20Sopenharmony_ci#define CMTP_MAPPING	3
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_cistatic struct cmtp_application *cmtp_application_add(struct cmtp_session *session, __u16 appl)
748c2ecf20Sopenharmony_ci{
758c2ecf20Sopenharmony_ci	struct cmtp_application *app = kzalloc(sizeof(*app), GFP_KERNEL);
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	BT_DBG("session %p application %p appl %d", session, app, appl);
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	if (!app)
808c2ecf20Sopenharmony_ci		return NULL;
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	app->state = BT_OPEN;
838c2ecf20Sopenharmony_ci	app->appl = appl;
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	list_add_tail(&app->list, &session->applications);
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	return app;
888c2ecf20Sopenharmony_ci}
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_cistatic void cmtp_application_del(struct cmtp_session *session, struct cmtp_application *app)
918c2ecf20Sopenharmony_ci{
928c2ecf20Sopenharmony_ci	BT_DBG("session %p application %p", session, app);
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	if (app) {
958c2ecf20Sopenharmony_ci		list_del(&app->list);
968c2ecf20Sopenharmony_ci		kfree(app);
978c2ecf20Sopenharmony_ci	}
988c2ecf20Sopenharmony_ci}
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_cistatic struct cmtp_application *cmtp_application_get(struct cmtp_session *session, int pattern, __u16 value)
1018c2ecf20Sopenharmony_ci{
1028c2ecf20Sopenharmony_ci	struct cmtp_application *app;
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	list_for_each_entry(app, &session->applications, list) {
1058c2ecf20Sopenharmony_ci		switch (pattern) {
1068c2ecf20Sopenharmony_ci		case CMTP_MSGNUM:
1078c2ecf20Sopenharmony_ci			if (app->msgnum == value)
1088c2ecf20Sopenharmony_ci				return app;
1098c2ecf20Sopenharmony_ci			break;
1108c2ecf20Sopenharmony_ci		case CMTP_APPLID:
1118c2ecf20Sopenharmony_ci			if (app->appl == value)
1128c2ecf20Sopenharmony_ci				return app;
1138c2ecf20Sopenharmony_ci			break;
1148c2ecf20Sopenharmony_ci		case CMTP_MAPPING:
1158c2ecf20Sopenharmony_ci			if (app->mapping == value)
1168c2ecf20Sopenharmony_ci				return app;
1178c2ecf20Sopenharmony_ci			break;
1188c2ecf20Sopenharmony_ci		}
1198c2ecf20Sopenharmony_ci	}
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	return NULL;
1228c2ecf20Sopenharmony_ci}
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_cistatic int cmtp_msgnum_get(struct cmtp_session *session)
1258c2ecf20Sopenharmony_ci{
1268c2ecf20Sopenharmony_ci	session->msgnum++;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	if ((session->msgnum & 0xff) > 200)
1298c2ecf20Sopenharmony_ci		session->msgnum = CMTP_INITIAL_MSGNUM + 1;
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	return session->msgnum;
1328c2ecf20Sopenharmony_ci}
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_cistatic void cmtp_send_capimsg(struct cmtp_session *session, struct sk_buff *skb)
1358c2ecf20Sopenharmony_ci{
1368c2ecf20Sopenharmony_ci	struct cmtp_scb *scb = (void *) skb->cb;
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	BT_DBG("session %p skb %p len %d", session, skb, skb->len);
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	scb->id = -1;
1418c2ecf20Sopenharmony_ci	scb->data = (CAPIMSG_COMMAND(skb->data) == CAPI_DATA_B3);
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	skb_queue_tail(&session->transmit, skb);
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	wake_up_interruptible(sk_sleep(session->sock->sk));
1468c2ecf20Sopenharmony_ci}
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_cistatic void cmtp_send_interopmsg(struct cmtp_session *session,
1498c2ecf20Sopenharmony_ci					__u8 subcmd, __u16 appl, __u16 msgnum,
1508c2ecf20Sopenharmony_ci					__u16 function, unsigned char *buf, int len)
1518c2ecf20Sopenharmony_ci{
1528c2ecf20Sopenharmony_ci	struct sk_buff *skb;
1538c2ecf20Sopenharmony_ci	unsigned char *s;
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	BT_DBG("session %p subcmd 0x%02x appl %d msgnum %d", session, subcmd, appl, msgnum);
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	skb = alloc_skb(CAPI_MSG_BASELEN + 6 + len, GFP_ATOMIC);
1588c2ecf20Sopenharmony_ci	if (!skb) {
1598c2ecf20Sopenharmony_ci		BT_ERR("Can't allocate memory for interoperability packet");
1608c2ecf20Sopenharmony_ci		return;
1618c2ecf20Sopenharmony_ci	}
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	s = skb_put(skb, CAPI_MSG_BASELEN + 6 + len);
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	capimsg_setu16(s, 0, CAPI_MSG_BASELEN + 6 + len);
1668c2ecf20Sopenharmony_ci	capimsg_setu16(s, 2, appl);
1678c2ecf20Sopenharmony_ci	capimsg_setu8 (s, 4, CAPI_INTEROPERABILITY);
1688c2ecf20Sopenharmony_ci	capimsg_setu8 (s, 5, subcmd);
1698c2ecf20Sopenharmony_ci	capimsg_setu16(s, 6, msgnum);
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	/* Interoperability selector (Bluetooth Device Management) */
1728c2ecf20Sopenharmony_ci	capimsg_setu16(s, 8, 0x0001);
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	capimsg_setu8 (s, 10, 3 + len);
1758c2ecf20Sopenharmony_ci	capimsg_setu16(s, 11, function);
1768c2ecf20Sopenharmony_ci	capimsg_setu8 (s, 13, len);
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	if (len > 0)
1798c2ecf20Sopenharmony_ci		memcpy(s + 14, buf, len);
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	cmtp_send_capimsg(session, skb);
1828c2ecf20Sopenharmony_ci}
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_cistatic void cmtp_recv_interopmsg(struct cmtp_session *session, struct sk_buff *skb)
1858c2ecf20Sopenharmony_ci{
1868c2ecf20Sopenharmony_ci	struct capi_ctr *ctrl = &session->ctrl;
1878c2ecf20Sopenharmony_ci	struct cmtp_application *application;
1888c2ecf20Sopenharmony_ci	__u16 appl, msgnum, func, info;
1898c2ecf20Sopenharmony_ci	__u32 controller;
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	BT_DBG("session %p skb %p len %d", session, skb, skb->len);
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	switch (CAPIMSG_SUBCOMMAND(skb->data)) {
1948c2ecf20Sopenharmony_ci	case CAPI_CONF:
1958c2ecf20Sopenharmony_ci		if (skb->len < CAPI_MSG_BASELEN + 10)
1968c2ecf20Sopenharmony_ci			break;
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci		func = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 5);
1998c2ecf20Sopenharmony_ci		info = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 8);
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci		switch (func) {
2028c2ecf20Sopenharmony_ci		case CAPI_FUNCTION_REGISTER:
2038c2ecf20Sopenharmony_ci			msgnum = CAPIMSG_MSGID(skb->data);
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci			application = cmtp_application_get(session, CMTP_MSGNUM, msgnum);
2068c2ecf20Sopenharmony_ci			if (application) {
2078c2ecf20Sopenharmony_ci				application->state = BT_CONNECTED;
2088c2ecf20Sopenharmony_ci				application->msgnum = 0;
2098c2ecf20Sopenharmony_ci				application->mapping = CAPIMSG_APPID(skb->data);
2108c2ecf20Sopenharmony_ci				wake_up_interruptible(&session->wait);
2118c2ecf20Sopenharmony_ci			}
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci			break;
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci		case CAPI_FUNCTION_RELEASE:
2168c2ecf20Sopenharmony_ci			appl = CAPIMSG_APPID(skb->data);
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci			application = cmtp_application_get(session, CMTP_MAPPING, appl);
2198c2ecf20Sopenharmony_ci			if (application) {
2208c2ecf20Sopenharmony_ci				application->state = BT_CLOSED;
2218c2ecf20Sopenharmony_ci				application->msgnum = 0;
2228c2ecf20Sopenharmony_ci				wake_up_interruptible(&session->wait);
2238c2ecf20Sopenharmony_ci			}
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci			break;
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci		case CAPI_FUNCTION_GET_PROFILE:
2288c2ecf20Sopenharmony_ci			if (skb->len < CAPI_MSG_BASELEN + 11 + sizeof(capi_profile))
2298c2ecf20Sopenharmony_ci				break;
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci			controller = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 11);
2328c2ecf20Sopenharmony_ci			msgnum = CAPIMSG_MSGID(skb->data);
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci			if (!info && (msgnum == CMTP_INITIAL_MSGNUM)) {
2358c2ecf20Sopenharmony_ci				session->ncontroller = controller;
2368c2ecf20Sopenharmony_ci				wake_up_interruptible(&session->wait);
2378c2ecf20Sopenharmony_ci				break;
2388c2ecf20Sopenharmony_ci			}
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci			if (!info && ctrl) {
2418c2ecf20Sopenharmony_ci				memcpy(&ctrl->profile,
2428c2ecf20Sopenharmony_ci					skb->data + CAPI_MSG_BASELEN + 11,
2438c2ecf20Sopenharmony_ci					sizeof(capi_profile));
2448c2ecf20Sopenharmony_ci				session->state = BT_CONNECTED;
2458c2ecf20Sopenharmony_ci				capi_ctr_ready(ctrl);
2468c2ecf20Sopenharmony_ci			}
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci			break;
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci		case CAPI_FUNCTION_GET_MANUFACTURER:
2518c2ecf20Sopenharmony_ci			if (skb->len < CAPI_MSG_BASELEN + 15)
2528c2ecf20Sopenharmony_ci				break;
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci			if (!info && ctrl) {
2558c2ecf20Sopenharmony_ci				int len = min_t(uint, CAPI_MANUFACTURER_LEN,
2568c2ecf20Sopenharmony_ci						skb->data[CAPI_MSG_BASELEN + 14]);
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci				memset(ctrl->manu, 0, CAPI_MANUFACTURER_LEN);
2598c2ecf20Sopenharmony_ci				strncpy(ctrl->manu,
2608c2ecf20Sopenharmony_ci					skb->data + CAPI_MSG_BASELEN + 15, len);
2618c2ecf20Sopenharmony_ci			}
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci			break;
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci		case CAPI_FUNCTION_GET_VERSION:
2668c2ecf20Sopenharmony_ci			if (skb->len < CAPI_MSG_BASELEN + 32)
2678c2ecf20Sopenharmony_ci				break;
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci			if (!info && ctrl) {
2708c2ecf20Sopenharmony_ci				ctrl->version.majorversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 16);
2718c2ecf20Sopenharmony_ci				ctrl->version.minorversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 20);
2728c2ecf20Sopenharmony_ci				ctrl->version.majormanuversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 24);
2738c2ecf20Sopenharmony_ci				ctrl->version.minormanuversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 28);
2748c2ecf20Sopenharmony_ci			}
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci			break;
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci		case CAPI_FUNCTION_GET_SERIAL_NUMBER:
2798c2ecf20Sopenharmony_ci			if (skb->len < CAPI_MSG_BASELEN + 17)
2808c2ecf20Sopenharmony_ci				break;
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci			if (!info && ctrl) {
2838c2ecf20Sopenharmony_ci				int len = min_t(uint, CAPI_SERIAL_LEN,
2848c2ecf20Sopenharmony_ci						skb->data[CAPI_MSG_BASELEN + 16]);
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci				memset(ctrl->serial, 0, CAPI_SERIAL_LEN);
2878c2ecf20Sopenharmony_ci				strncpy(ctrl->serial,
2888c2ecf20Sopenharmony_ci					skb->data + CAPI_MSG_BASELEN + 17, len);
2898c2ecf20Sopenharmony_ci			}
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci			break;
2928c2ecf20Sopenharmony_ci		}
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci		break;
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	case CAPI_IND:
2978c2ecf20Sopenharmony_ci		if (skb->len < CAPI_MSG_BASELEN + 6)
2988c2ecf20Sopenharmony_ci			break;
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci		func = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 3);
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci		if (func == CAPI_FUNCTION_LOOPBACK) {
3038c2ecf20Sopenharmony_ci			int len = min_t(uint, skb->len - CAPI_MSG_BASELEN - 6,
3048c2ecf20Sopenharmony_ci						skb->data[CAPI_MSG_BASELEN + 5]);
3058c2ecf20Sopenharmony_ci			appl = CAPIMSG_APPID(skb->data);
3068c2ecf20Sopenharmony_ci			msgnum = CAPIMSG_MSGID(skb->data);
3078c2ecf20Sopenharmony_ci			cmtp_send_interopmsg(session, CAPI_RESP, appl, msgnum, func,
3088c2ecf20Sopenharmony_ci						skb->data + CAPI_MSG_BASELEN + 6, len);
3098c2ecf20Sopenharmony_ci		}
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci		break;
3128c2ecf20Sopenharmony_ci	}
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	kfree_skb(skb);
3158c2ecf20Sopenharmony_ci}
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_civoid cmtp_recv_capimsg(struct cmtp_session *session, struct sk_buff *skb)
3188c2ecf20Sopenharmony_ci{
3198c2ecf20Sopenharmony_ci	struct capi_ctr *ctrl = &session->ctrl;
3208c2ecf20Sopenharmony_ci	struct cmtp_application *application;
3218c2ecf20Sopenharmony_ci	__u16 appl;
3228c2ecf20Sopenharmony_ci	__u32 contr;
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci	BT_DBG("session %p skb %p len %d", session, skb, skb->len);
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	if (skb->len < CAPI_MSG_BASELEN)
3278c2ecf20Sopenharmony_ci		return;
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	if (CAPIMSG_COMMAND(skb->data) == CAPI_INTEROPERABILITY) {
3308c2ecf20Sopenharmony_ci		cmtp_recv_interopmsg(session, skb);
3318c2ecf20Sopenharmony_ci		return;
3328c2ecf20Sopenharmony_ci	}
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	if (session->flags & BIT(CMTP_LOOPBACK)) {
3358c2ecf20Sopenharmony_ci		kfree_skb(skb);
3368c2ecf20Sopenharmony_ci		return;
3378c2ecf20Sopenharmony_ci	}
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	appl = CAPIMSG_APPID(skb->data);
3408c2ecf20Sopenharmony_ci	contr = CAPIMSG_CONTROL(skb->data);
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci	application = cmtp_application_get(session, CMTP_MAPPING, appl);
3438c2ecf20Sopenharmony_ci	if (application) {
3448c2ecf20Sopenharmony_ci		appl = application->appl;
3458c2ecf20Sopenharmony_ci		CAPIMSG_SETAPPID(skb->data, appl);
3468c2ecf20Sopenharmony_ci	} else {
3478c2ecf20Sopenharmony_ci		BT_ERR("Can't find application with id %d", appl);
3488c2ecf20Sopenharmony_ci		kfree_skb(skb);
3498c2ecf20Sopenharmony_ci		return;
3508c2ecf20Sopenharmony_ci	}
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	if ((contr & 0x7f) == 0x01) {
3538c2ecf20Sopenharmony_ci		contr = (contr & 0xffffff80) | session->num;
3548c2ecf20Sopenharmony_ci		CAPIMSG_SETCONTROL(skb->data, contr);
3558c2ecf20Sopenharmony_ci	}
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	capi_ctr_handle_message(ctrl, appl, skb);
3588c2ecf20Sopenharmony_ci}
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_cistatic int cmtp_load_firmware(struct capi_ctr *ctrl, capiloaddata *data)
3618c2ecf20Sopenharmony_ci{
3628c2ecf20Sopenharmony_ci	BT_DBG("ctrl %p data %p", ctrl, data);
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	return 0;
3658c2ecf20Sopenharmony_ci}
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_cistatic void cmtp_reset_ctr(struct capi_ctr *ctrl)
3688c2ecf20Sopenharmony_ci{
3698c2ecf20Sopenharmony_ci	struct cmtp_session *session = ctrl->driverdata;
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	BT_DBG("ctrl %p", ctrl);
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	capi_ctr_down(ctrl);
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	atomic_inc(&session->terminate);
3768c2ecf20Sopenharmony_ci	wake_up_process(session->task);
3778c2ecf20Sopenharmony_ci}
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_cistatic void cmtp_register_appl(struct capi_ctr *ctrl, __u16 appl, capi_register_params *rp)
3808c2ecf20Sopenharmony_ci{
3818c2ecf20Sopenharmony_ci	DECLARE_WAITQUEUE(wait, current);
3828c2ecf20Sopenharmony_ci	struct cmtp_session *session = ctrl->driverdata;
3838c2ecf20Sopenharmony_ci	struct cmtp_application *application;
3848c2ecf20Sopenharmony_ci	unsigned long timeo = CMTP_INTEROP_TIMEOUT;
3858c2ecf20Sopenharmony_ci	unsigned char buf[8];
3868c2ecf20Sopenharmony_ci	int err = 0, nconn, want = rp->level3cnt;
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	BT_DBG("ctrl %p appl %d level3cnt %d datablkcnt %d datablklen %d",
3898c2ecf20Sopenharmony_ci		ctrl, appl, rp->level3cnt, rp->datablkcnt, rp->datablklen);
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	application = cmtp_application_add(session, appl);
3928c2ecf20Sopenharmony_ci	if (!application) {
3938c2ecf20Sopenharmony_ci		BT_ERR("Can't allocate memory for new application");
3948c2ecf20Sopenharmony_ci		return;
3958c2ecf20Sopenharmony_ci	}
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci	if (want < 0)
3988c2ecf20Sopenharmony_ci		nconn = ctrl->profile.nbchannel * -want;
3998c2ecf20Sopenharmony_ci	else
4008c2ecf20Sopenharmony_ci		nconn = want;
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci	if (nconn == 0)
4038c2ecf20Sopenharmony_ci		nconn = ctrl->profile.nbchannel;
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci	capimsg_setu16(buf, 0, nconn);
4068c2ecf20Sopenharmony_ci	capimsg_setu16(buf, 2, rp->datablkcnt);
4078c2ecf20Sopenharmony_ci	capimsg_setu16(buf, 4, rp->datablklen);
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci	application->state = BT_CONFIG;
4108c2ecf20Sopenharmony_ci	application->msgnum = cmtp_msgnum_get(session);
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci	cmtp_send_interopmsg(session, CAPI_REQ, 0x0000, application->msgnum,
4138c2ecf20Sopenharmony_ci				CAPI_FUNCTION_REGISTER, buf, 6);
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	add_wait_queue(&session->wait, &wait);
4168c2ecf20Sopenharmony_ci	while (1) {
4178c2ecf20Sopenharmony_ci		set_current_state(TASK_INTERRUPTIBLE);
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci		if (!timeo) {
4208c2ecf20Sopenharmony_ci			err = -EAGAIN;
4218c2ecf20Sopenharmony_ci			break;
4228c2ecf20Sopenharmony_ci		}
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci		if (application->state == BT_CLOSED) {
4258c2ecf20Sopenharmony_ci			err = -application->err;
4268c2ecf20Sopenharmony_ci			break;
4278c2ecf20Sopenharmony_ci		}
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci		if (application->state == BT_CONNECTED)
4308c2ecf20Sopenharmony_ci			break;
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci		if (signal_pending(current)) {
4338c2ecf20Sopenharmony_ci			err = -EINTR;
4348c2ecf20Sopenharmony_ci			break;
4358c2ecf20Sopenharmony_ci		}
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci		timeo = schedule_timeout(timeo);
4388c2ecf20Sopenharmony_ci	}
4398c2ecf20Sopenharmony_ci	set_current_state(TASK_RUNNING);
4408c2ecf20Sopenharmony_ci	remove_wait_queue(&session->wait, &wait);
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ci	if (err) {
4438c2ecf20Sopenharmony_ci		cmtp_application_del(session, application);
4448c2ecf20Sopenharmony_ci		return;
4458c2ecf20Sopenharmony_ci	}
4468c2ecf20Sopenharmony_ci}
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_cistatic void cmtp_release_appl(struct capi_ctr *ctrl, __u16 appl)
4498c2ecf20Sopenharmony_ci{
4508c2ecf20Sopenharmony_ci	struct cmtp_session *session = ctrl->driverdata;
4518c2ecf20Sopenharmony_ci	struct cmtp_application *application;
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	BT_DBG("ctrl %p appl %d", ctrl, appl);
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci	application = cmtp_application_get(session, CMTP_APPLID, appl);
4568c2ecf20Sopenharmony_ci	if (!application) {
4578c2ecf20Sopenharmony_ci		BT_ERR("Can't find application");
4588c2ecf20Sopenharmony_ci		return;
4598c2ecf20Sopenharmony_ci	}
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	application->msgnum = cmtp_msgnum_get(session);
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci	cmtp_send_interopmsg(session, CAPI_REQ, application->mapping, application->msgnum,
4648c2ecf20Sopenharmony_ci				CAPI_FUNCTION_RELEASE, NULL, 0);
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci	wait_event_interruptible_timeout(session->wait,
4678c2ecf20Sopenharmony_ci			(application->state == BT_CLOSED), CMTP_INTEROP_TIMEOUT);
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci	cmtp_application_del(session, application);
4708c2ecf20Sopenharmony_ci}
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_cistatic u16 cmtp_send_message(struct capi_ctr *ctrl, struct sk_buff *skb)
4738c2ecf20Sopenharmony_ci{
4748c2ecf20Sopenharmony_ci	struct cmtp_session *session = ctrl->driverdata;
4758c2ecf20Sopenharmony_ci	struct cmtp_application *application;
4768c2ecf20Sopenharmony_ci	__u16 appl;
4778c2ecf20Sopenharmony_ci	__u32 contr;
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ci	BT_DBG("ctrl %p skb %p", ctrl, skb);
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci	appl = CAPIMSG_APPID(skb->data);
4828c2ecf20Sopenharmony_ci	contr = CAPIMSG_CONTROL(skb->data);
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ci	application = cmtp_application_get(session, CMTP_APPLID, appl);
4858c2ecf20Sopenharmony_ci	if ((!application) || (application->state != BT_CONNECTED)) {
4868c2ecf20Sopenharmony_ci		BT_ERR("Can't find application with id %d", appl);
4878c2ecf20Sopenharmony_ci		return CAPI_ILLAPPNR;
4888c2ecf20Sopenharmony_ci	}
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci	CAPIMSG_SETAPPID(skb->data, application->mapping);
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_ci	if ((contr & 0x7f) == session->num) {
4938c2ecf20Sopenharmony_ci		contr = (contr & 0xffffff80) | 0x01;
4948c2ecf20Sopenharmony_ci		CAPIMSG_SETCONTROL(skb->data, contr);
4958c2ecf20Sopenharmony_ci	}
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci	cmtp_send_capimsg(session, skb);
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci	return CAPI_NOERROR;
5008c2ecf20Sopenharmony_ci}
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_cistatic char *cmtp_procinfo(struct capi_ctr *ctrl)
5038c2ecf20Sopenharmony_ci{
5048c2ecf20Sopenharmony_ci	return "CAPI Message Transport Protocol";
5058c2ecf20Sopenharmony_ci}
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_cistatic int cmtp_proc_show(struct seq_file *m, void *v)
5088c2ecf20Sopenharmony_ci{
5098c2ecf20Sopenharmony_ci	struct capi_ctr *ctrl = m->private;
5108c2ecf20Sopenharmony_ci	struct cmtp_session *session = ctrl->driverdata;
5118c2ecf20Sopenharmony_ci	struct cmtp_application *app;
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci	seq_printf(m, "%s\n\n", cmtp_procinfo(ctrl));
5148c2ecf20Sopenharmony_ci	seq_printf(m, "addr %s\n", session->name);
5158c2ecf20Sopenharmony_ci	seq_printf(m, "ctrl %d\n", session->num);
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci	list_for_each_entry(app, &session->applications, list) {
5188c2ecf20Sopenharmony_ci		seq_printf(m, "appl %d -> %d\n", app->appl, app->mapping);
5198c2ecf20Sopenharmony_ci	}
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci	return 0;
5228c2ecf20Sopenharmony_ci}
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ciint cmtp_attach_device(struct cmtp_session *session)
5258c2ecf20Sopenharmony_ci{
5268c2ecf20Sopenharmony_ci	unsigned char buf[4];
5278c2ecf20Sopenharmony_ci	long ret;
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ci	BT_DBG("session %p", session);
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci	capimsg_setu32(buf, 0, 0);
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ci	cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, CMTP_INITIAL_MSGNUM,
5348c2ecf20Sopenharmony_ci				CAPI_FUNCTION_GET_PROFILE, buf, 4);
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci	ret = wait_event_interruptible_timeout(session->wait,
5378c2ecf20Sopenharmony_ci			session->ncontroller, CMTP_INTEROP_TIMEOUT);
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci	BT_INFO("Found %d CAPI controller(s) on device %s", session->ncontroller, session->name);
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_ci	if (!ret)
5428c2ecf20Sopenharmony_ci		return -ETIMEDOUT;
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci	if (!session->ncontroller)
5458c2ecf20Sopenharmony_ci		return -ENODEV;
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_ci	if (session->ncontroller > 1)
5488c2ecf20Sopenharmony_ci		BT_INFO("Setting up only CAPI controller 1");
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci	session->ctrl.owner      = THIS_MODULE;
5518c2ecf20Sopenharmony_ci	session->ctrl.driverdata = session;
5528c2ecf20Sopenharmony_ci	strcpy(session->ctrl.name, session->name);
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_ci	session->ctrl.driver_name   = "cmtp";
5558c2ecf20Sopenharmony_ci	session->ctrl.load_firmware = cmtp_load_firmware;
5568c2ecf20Sopenharmony_ci	session->ctrl.reset_ctr     = cmtp_reset_ctr;
5578c2ecf20Sopenharmony_ci	session->ctrl.register_appl = cmtp_register_appl;
5588c2ecf20Sopenharmony_ci	session->ctrl.release_appl  = cmtp_release_appl;
5598c2ecf20Sopenharmony_ci	session->ctrl.send_message  = cmtp_send_message;
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci	session->ctrl.procinfo      = cmtp_procinfo;
5628c2ecf20Sopenharmony_ci	session->ctrl.proc_show     = cmtp_proc_show;
5638c2ecf20Sopenharmony_ci
5648c2ecf20Sopenharmony_ci	if (attach_capi_ctr(&session->ctrl) < 0) {
5658c2ecf20Sopenharmony_ci		BT_ERR("Can't attach new controller");
5668c2ecf20Sopenharmony_ci		return -EBUSY;
5678c2ecf20Sopenharmony_ci	}
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_ci	session->num = session->ctrl.cnr;
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_ci	BT_DBG("session %p num %d", session, session->num);
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci	capimsg_setu32(buf, 0, 1);
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci	cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session),
5768c2ecf20Sopenharmony_ci				CAPI_FUNCTION_GET_MANUFACTURER, buf, 4);
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_ci	cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session),
5798c2ecf20Sopenharmony_ci				CAPI_FUNCTION_GET_VERSION, buf, 4);
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_ci	cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session),
5828c2ecf20Sopenharmony_ci				CAPI_FUNCTION_GET_SERIAL_NUMBER, buf, 4);
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_ci	cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session),
5858c2ecf20Sopenharmony_ci				CAPI_FUNCTION_GET_PROFILE, buf, 4);
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_ci	return 0;
5888c2ecf20Sopenharmony_ci}
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_civoid cmtp_detach_device(struct cmtp_session *session)
5918c2ecf20Sopenharmony_ci{
5928c2ecf20Sopenharmony_ci	BT_DBG("session %p", session);
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_ci	detach_capi_ctr(&session->ctrl);
5958c2ecf20Sopenharmony_ci}
596