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/module.h>
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#include <linux/types.h>
268c2ecf20Sopenharmony_ci#include <linux/errno.h>
278c2ecf20Sopenharmony_ci#include <linux/kernel.h>
288c2ecf20Sopenharmony_ci#include <linux/sched.h>
298c2ecf20Sopenharmony_ci#include <linux/slab.h>
308c2ecf20Sopenharmony_ci#include <linux/poll.h>
318c2ecf20Sopenharmony_ci#include <linux/fcntl.h>
328c2ecf20Sopenharmony_ci#include <linux/freezer.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/init.h>
388c2ecf20Sopenharmony_ci#include <linux/kthread.h>
398c2ecf20Sopenharmony_ci#include <net/sock.h>
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci#include <linux/isdn/capilli.h>
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci#include <net/bluetooth/bluetooth.h>
448c2ecf20Sopenharmony_ci#include <net/bluetooth/l2cap.h>
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci#include "cmtp.h"
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci#define VERSION "1.0"
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_cistatic DECLARE_RWSEM(cmtp_session_sem);
518c2ecf20Sopenharmony_cistatic LIST_HEAD(cmtp_session_list);
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cistatic struct cmtp_session *__cmtp_get_session(bdaddr_t *bdaddr)
548c2ecf20Sopenharmony_ci{
558c2ecf20Sopenharmony_ci	struct cmtp_session *session;
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	BT_DBG("");
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	list_for_each_entry(session, &cmtp_session_list, list)
608c2ecf20Sopenharmony_ci		if (!bacmp(bdaddr, &session->bdaddr))
618c2ecf20Sopenharmony_ci			return session;
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	return NULL;
648c2ecf20Sopenharmony_ci}
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_cistatic void __cmtp_link_session(struct cmtp_session *session)
678c2ecf20Sopenharmony_ci{
688c2ecf20Sopenharmony_ci	list_add(&session->list, &cmtp_session_list);
698c2ecf20Sopenharmony_ci}
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_cistatic void __cmtp_unlink_session(struct cmtp_session *session)
728c2ecf20Sopenharmony_ci{
738c2ecf20Sopenharmony_ci	list_del(&session->list);
748c2ecf20Sopenharmony_ci}
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_cistatic void __cmtp_copy_session(struct cmtp_session *session, struct cmtp_conninfo *ci)
778c2ecf20Sopenharmony_ci{
788c2ecf20Sopenharmony_ci	u32 valid_flags = BIT(CMTP_LOOPBACK);
798c2ecf20Sopenharmony_ci	memset(ci, 0, sizeof(*ci));
808c2ecf20Sopenharmony_ci	bacpy(&ci->bdaddr, &session->bdaddr);
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	ci->flags = session->flags & valid_flags;
838c2ecf20Sopenharmony_ci	ci->state = session->state;
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	ci->num = session->num;
868c2ecf20Sopenharmony_ci}
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_cistatic inline int cmtp_alloc_block_id(struct cmtp_session *session)
908c2ecf20Sopenharmony_ci{
918c2ecf20Sopenharmony_ci	int i, id = -1;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	for (i = 0; i < 16; i++)
948c2ecf20Sopenharmony_ci		if (!test_and_set_bit(i, &session->blockids)) {
958c2ecf20Sopenharmony_ci			id = i;
968c2ecf20Sopenharmony_ci			break;
978c2ecf20Sopenharmony_ci		}
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	return id;
1008c2ecf20Sopenharmony_ci}
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_cistatic inline void cmtp_free_block_id(struct cmtp_session *session, int id)
1038c2ecf20Sopenharmony_ci{
1048c2ecf20Sopenharmony_ci	clear_bit(id, &session->blockids);
1058c2ecf20Sopenharmony_ci}
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_cistatic inline void cmtp_add_msgpart(struct cmtp_session *session, int id, const unsigned char *buf, int count)
1088c2ecf20Sopenharmony_ci{
1098c2ecf20Sopenharmony_ci	struct sk_buff *skb = session->reassembly[id], *nskb;
1108c2ecf20Sopenharmony_ci	int size;
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	BT_DBG("session %p buf %p count %d", session, buf, count);
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	size = (skb) ? skb->len + count : count;
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	nskb = alloc_skb(size, GFP_ATOMIC);
1178c2ecf20Sopenharmony_ci	if (!nskb) {
1188c2ecf20Sopenharmony_ci		BT_ERR("Can't allocate memory for CAPI message");
1198c2ecf20Sopenharmony_ci		return;
1208c2ecf20Sopenharmony_ci	}
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	if (skb && (skb->len > 0))
1238c2ecf20Sopenharmony_ci		skb_copy_from_linear_data(skb, skb_put(nskb, skb->len), skb->len);
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	skb_put_data(nskb, buf, count);
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	session->reassembly[id] = nskb;
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	kfree_skb(skb);
1308c2ecf20Sopenharmony_ci}
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_cistatic inline int cmtp_recv_frame(struct cmtp_session *session, struct sk_buff *skb)
1338c2ecf20Sopenharmony_ci{
1348c2ecf20Sopenharmony_ci	__u8 hdr, hdrlen, id;
1358c2ecf20Sopenharmony_ci	__u16 len;
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	BT_DBG("session %p skb %p len %d", session, skb, skb->len);
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	while (skb->len > 0) {
1408c2ecf20Sopenharmony_ci		hdr = skb->data[0];
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci		switch (hdr & 0xc0) {
1438c2ecf20Sopenharmony_ci		case 0x40:
1448c2ecf20Sopenharmony_ci			hdrlen = 2;
1458c2ecf20Sopenharmony_ci			len = skb->data[1];
1468c2ecf20Sopenharmony_ci			break;
1478c2ecf20Sopenharmony_ci		case 0x80:
1488c2ecf20Sopenharmony_ci			hdrlen = 3;
1498c2ecf20Sopenharmony_ci			len = skb->data[1] | (skb->data[2] << 8);
1508c2ecf20Sopenharmony_ci			break;
1518c2ecf20Sopenharmony_ci		default:
1528c2ecf20Sopenharmony_ci			hdrlen = 1;
1538c2ecf20Sopenharmony_ci			len = 0;
1548c2ecf20Sopenharmony_ci			break;
1558c2ecf20Sopenharmony_ci		}
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci		id = (hdr & 0x3c) >> 2;
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci		BT_DBG("hdr 0x%02x hdrlen %d len %d id %d", hdr, hdrlen, len, id);
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci		if (hdrlen + len > skb->len) {
1628c2ecf20Sopenharmony_ci			BT_ERR("Wrong size or header information in CMTP frame");
1638c2ecf20Sopenharmony_ci			break;
1648c2ecf20Sopenharmony_ci		}
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci		if (len == 0) {
1678c2ecf20Sopenharmony_ci			skb_pull(skb, hdrlen);
1688c2ecf20Sopenharmony_ci			continue;
1698c2ecf20Sopenharmony_ci		}
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci		switch (hdr & 0x03) {
1728c2ecf20Sopenharmony_ci		case 0x00:
1738c2ecf20Sopenharmony_ci			cmtp_add_msgpart(session, id, skb->data + hdrlen, len);
1748c2ecf20Sopenharmony_ci			cmtp_recv_capimsg(session, session->reassembly[id]);
1758c2ecf20Sopenharmony_ci			session->reassembly[id] = NULL;
1768c2ecf20Sopenharmony_ci			break;
1778c2ecf20Sopenharmony_ci		case 0x01:
1788c2ecf20Sopenharmony_ci			cmtp_add_msgpart(session, id, skb->data + hdrlen, len);
1798c2ecf20Sopenharmony_ci			break;
1808c2ecf20Sopenharmony_ci		default:
1818c2ecf20Sopenharmony_ci			kfree_skb(session->reassembly[id]);
1828c2ecf20Sopenharmony_ci			session->reassembly[id] = NULL;
1838c2ecf20Sopenharmony_ci			break;
1848c2ecf20Sopenharmony_ci		}
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci		skb_pull(skb, hdrlen + len);
1878c2ecf20Sopenharmony_ci	}
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	kfree_skb(skb);
1908c2ecf20Sopenharmony_ci	return 0;
1918c2ecf20Sopenharmony_ci}
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_cistatic int cmtp_send_frame(struct cmtp_session *session, unsigned char *data, int len)
1948c2ecf20Sopenharmony_ci{
1958c2ecf20Sopenharmony_ci	struct socket *sock = session->sock;
1968c2ecf20Sopenharmony_ci	struct kvec iv = { data, len };
1978c2ecf20Sopenharmony_ci	struct msghdr msg;
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	BT_DBG("session %p data %p len %d", session, data, len);
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	if (!len)
2028c2ecf20Sopenharmony_ci		return 0;
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	memset(&msg, 0, sizeof(msg));
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	return kernel_sendmsg(sock, &msg, &iv, 1, len);
2078c2ecf20Sopenharmony_ci}
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_cistatic void cmtp_process_transmit(struct cmtp_session *session)
2108c2ecf20Sopenharmony_ci{
2118c2ecf20Sopenharmony_ci	struct sk_buff *skb, *nskb;
2128c2ecf20Sopenharmony_ci	unsigned char *hdr;
2138c2ecf20Sopenharmony_ci	unsigned int size, tail;
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	BT_DBG("session %p", session);
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	nskb = alloc_skb(session->mtu, GFP_ATOMIC);
2188c2ecf20Sopenharmony_ci	if (!nskb) {
2198c2ecf20Sopenharmony_ci		BT_ERR("Can't allocate memory for new frame");
2208c2ecf20Sopenharmony_ci		return;
2218c2ecf20Sopenharmony_ci	}
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	while ((skb = skb_dequeue(&session->transmit))) {
2248c2ecf20Sopenharmony_ci		struct cmtp_scb *scb = (void *) skb->cb;
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci		tail = session->mtu - nskb->len;
2278c2ecf20Sopenharmony_ci		if (tail < 5) {
2288c2ecf20Sopenharmony_ci			cmtp_send_frame(session, nskb->data, nskb->len);
2298c2ecf20Sopenharmony_ci			skb_trim(nskb, 0);
2308c2ecf20Sopenharmony_ci			tail = session->mtu;
2318c2ecf20Sopenharmony_ci		}
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci		size = min_t(uint, ((tail < 258) ? (tail - 2) : (tail - 3)), skb->len);
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci		if (scb->id < 0) {
2368c2ecf20Sopenharmony_ci			scb->id = cmtp_alloc_block_id(session);
2378c2ecf20Sopenharmony_ci			if (scb->id < 0) {
2388c2ecf20Sopenharmony_ci				skb_queue_head(&session->transmit, skb);
2398c2ecf20Sopenharmony_ci				break;
2408c2ecf20Sopenharmony_ci			}
2418c2ecf20Sopenharmony_ci		}
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci		if (size < 256) {
2448c2ecf20Sopenharmony_ci			hdr = skb_put(nskb, 2);
2458c2ecf20Sopenharmony_ci			hdr[0] = 0x40
2468c2ecf20Sopenharmony_ci				| ((scb->id << 2) & 0x3c)
2478c2ecf20Sopenharmony_ci				| ((skb->len == size) ? 0x00 : 0x01);
2488c2ecf20Sopenharmony_ci			hdr[1] = size;
2498c2ecf20Sopenharmony_ci		} else {
2508c2ecf20Sopenharmony_ci			hdr = skb_put(nskb, 3);
2518c2ecf20Sopenharmony_ci			hdr[0] = 0x80
2528c2ecf20Sopenharmony_ci				| ((scb->id << 2) & 0x3c)
2538c2ecf20Sopenharmony_ci				| ((skb->len == size) ? 0x00 : 0x01);
2548c2ecf20Sopenharmony_ci			hdr[1] = size & 0xff;
2558c2ecf20Sopenharmony_ci			hdr[2] = size >> 8;
2568c2ecf20Sopenharmony_ci		}
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci		skb_copy_from_linear_data(skb, skb_put(nskb, size), size);
2598c2ecf20Sopenharmony_ci		skb_pull(skb, size);
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci		if (skb->len > 0) {
2628c2ecf20Sopenharmony_ci			skb_queue_head(&session->transmit, skb);
2638c2ecf20Sopenharmony_ci		} else {
2648c2ecf20Sopenharmony_ci			cmtp_free_block_id(session, scb->id);
2658c2ecf20Sopenharmony_ci			if (scb->data) {
2668c2ecf20Sopenharmony_ci				cmtp_send_frame(session, nskb->data, nskb->len);
2678c2ecf20Sopenharmony_ci				skb_trim(nskb, 0);
2688c2ecf20Sopenharmony_ci			}
2698c2ecf20Sopenharmony_ci			kfree_skb(skb);
2708c2ecf20Sopenharmony_ci		}
2718c2ecf20Sopenharmony_ci	}
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	cmtp_send_frame(session, nskb->data, nskb->len);
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	kfree_skb(nskb);
2768c2ecf20Sopenharmony_ci}
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_cistatic int cmtp_session(void *arg)
2798c2ecf20Sopenharmony_ci{
2808c2ecf20Sopenharmony_ci	struct cmtp_session *session = arg;
2818c2ecf20Sopenharmony_ci	struct sock *sk = session->sock->sk;
2828c2ecf20Sopenharmony_ci	struct sk_buff *skb;
2838c2ecf20Sopenharmony_ci	DEFINE_WAIT_FUNC(wait, woken_wake_function);
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	BT_DBG("session %p", session);
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	set_user_nice(current, -15);
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	add_wait_queue(sk_sleep(sk), &wait);
2908c2ecf20Sopenharmony_ci	while (1) {
2918c2ecf20Sopenharmony_ci		if (atomic_read(&session->terminate))
2928c2ecf20Sopenharmony_ci			break;
2938c2ecf20Sopenharmony_ci		if (sk->sk_state != BT_CONNECTED)
2948c2ecf20Sopenharmony_ci			break;
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci		while ((skb = skb_dequeue(&sk->sk_receive_queue))) {
2978c2ecf20Sopenharmony_ci			skb_orphan(skb);
2988c2ecf20Sopenharmony_ci			if (!skb_linearize(skb))
2998c2ecf20Sopenharmony_ci				cmtp_recv_frame(session, skb);
3008c2ecf20Sopenharmony_ci			else
3018c2ecf20Sopenharmony_ci				kfree_skb(skb);
3028c2ecf20Sopenharmony_ci		}
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci		cmtp_process_transmit(session);
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci		/*
3078c2ecf20Sopenharmony_ci		 * wait_woken() performs the necessary memory barriers
3088c2ecf20Sopenharmony_ci		 * for us; see the header comment for this primitive.
3098c2ecf20Sopenharmony_ci		 */
3108c2ecf20Sopenharmony_ci		wait_woken(&wait, TASK_INTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);
3118c2ecf20Sopenharmony_ci	}
3128c2ecf20Sopenharmony_ci	remove_wait_queue(sk_sleep(sk), &wait);
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	down_write(&cmtp_session_sem);
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	if (!(session->flags & BIT(CMTP_LOOPBACK)))
3178c2ecf20Sopenharmony_ci		cmtp_detach_device(session);
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	fput(session->sock->file);
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	__cmtp_unlink_session(session);
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	up_write(&cmtp_session_sem);
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	kfree(session);
3268c2ecf20Sopenharmony_ci	module_put_and_exit(0);
3278c2ecf20Sopenharmony_ci	return 0;
3288c2ecf20Sopenharmony_ci}
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ciint cmtp_add_connection(struct cmtp_connadd_req *req, struct socket *sock)
3318c2ecf20Sopenharmony_ci{
3328c2ecf20Sopenharmony_ci	u32 valid_flags = BIT(CMTP_LOOPBACK);
3338c2ecf20Sopenharmony_ci	struct cmtp_session *session, *s;
3348c2ecf20Sopenharmony_ci	int i, err;
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	BT_DBG("");
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	if (!l2cap_is_socket(sock))
3398c2ecf20Sopenharmony_ci		return -EBADFD;
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	if (req->flags & ~valid_flags)
3428c2ecf20Sopenharmony_ci		return -EINVAL;
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	session = kzalloc(sizeof(struct cmtp_session), GFP_KERNEL);
3458c2ecf20Sopenharmony_ci	if (!session)
3468c2ecf20Sopenharmony_ci		return -ENOMEM;
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	down_write(&cmtp_session_sem);
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	s = __cmtp_get_session(&l2cap_pi(sock->sk)->chan->dst);
3518c2ecf20Sopenharmony_ci	if (s && s->state == BT_CONNECTED) {
3528c2ecf20Sopenharmony_ci		err = -EEXIST;
3538c2ecf20Sopenharmony_ci		goto failed;
3548c2ecf20Sopenharmony_ci	}
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	bacpy(&session->bdaddr, &l2cap_pi(sock->sk)->chan->dst);
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	session->mtu = min_t(uint, l2cap_pi(sock->sk)->chan->omtu,
3598c2ecf20Sopenharmony_ci					l2cap_pi(sock->sk)->chan->imtu);
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	BT_DBG("mtu %d", session->mtu);
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci	sprintf(session->name, "%pMR", &session->bdaddr);
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	session->sock  = sock;
3668c2ecf20Sopenharmony_ci	session->state = BT_CONFIG;
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	init_waitqueue_head(&session->wait);
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	session->msgnum = CMTP_INITIAL_MSGNUM;
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&session->applications);
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	skb_queue_head_init(&session->transmit);
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	for (i = 0; i < 16; i++)
3778c2ecf20Sopenharmony_ci		session->reassembly[i] = NULL;
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	session->flags = req->flags;
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	__cmtp_link_session(session);
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	__module_get(THIS_MODULE);
3848c2ecf20Sopenharmony_ci	session->task = kthread_run(cmtp_session, session, "kcmtpd_ctr_%d",
3858c2ecf20Sopenharmony_ci								session->num);
3868c2ecf20Sopenharmony_ci	if (IS_ERR(session->task)) {
3878c2ecf20Sopenharmony_ci		module_put(THIS_MODULE);
3888c2ecf20Sopenharmony_ci		err = PTR_ERR(session->task);
3898c2ecf20Sopenharmony_ci		goto unlink;
3908c2ecf20Sopenharmony_ci	}
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci	if (!(session->flags & BIT(CMTP_LOOPBACK))) {
3938c2ecf20Sopenharmony_ci		err = cmtp_attach_device(session);
3948c2ecf20Sopenharmony_ci		if (err < 0) {
3958c2ecf20Sopenharmony_ci			/* Caller will call fput in case of failure, and so
3968c2ecf20Sopenharmony_ci			 * will cmtp_session kthread.
3978c2ecf20Sopenharmony_ci			 */
3988c2ecf20Sopenharmony_ci			get_file(session->sock->file);
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci			atomic_inc(&session->terminate);
4018c2ecf20Sopenharmony_ci			wake_up_interruptible(sk_sleep(session->sock->sk));
4028c2ecf20Sopenharmony_ci			up_write(&cmtp_session_sem);
4038c2ecf20Sopenharmony_ci			return err;
4048c2ecf20Sopenharmony_ci		}
4058c2ecf20Sopenharmony_ci	}
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci	up_write(&cmtp_session_sem);
4088c2ecf20Sopenharmony_ci	return 0;
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ciunlink:
4118c2ecf20Sopenharmony_ci	__cmtp_unlink_session(session);
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_cifailed:
4148c2ecf20Sopenharmony_ci	up_write(&cmtp_session_sem);
4158c2ecf20Sopenharmony_ci	kfree(session);
4168c2ecf20Sopenharmony_ci	return err;
4178c2ecf20Sopenharmony_ci}
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ciint cmtp_del_connection(struct cmtp_conndel_req *req)
4208c2ecf20Sopenharmony_ci{
4218c2ecf20Sopenharmony_ci	u32 valid_flags = 0;
4228c2ecf20Sopenharmony_ci	struct cmtp_session *session;
4238c2ecf20Sopenharmony_ci	int err = 0;
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	BT_DBG("");
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	if (req->flags & ~valid_flags)
4288c2ecf20Sopenharmony_ci		return -EINVAL;
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci	down_read(&cmtp_session_sem);
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci	session = __cmtp_get_session(&req->bdaddr);
4338c2ecf20Sopenharmony_ci	if (session) {
4348c2ecf20Sopenharmony_ci		/* Flush the transmit queue */
4358c2ecf20Sopenharmony_ci		skb_queue_purge(&session->transmit);
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci		/* Stop session thread */
4388c2ecf20Sopenharmony_ci		atomic_inc(&session->terminate);
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci		/*
4418c2ecf20Sopenharmony_ci		 * See the comment preceding the call to wait_woken()
4428c2ecf20Sopenharmony_ci		 * in cmtp_session().
4438c2ecf20Sopenharmony_ci		 */
4448c2ecf20Sopenharmony_ci		wake_up_interruptible(sk_sleep(session->sock->sk));
4458c2ecf20Sopenharmony_ci	} else
4468c2ecf20Sopenharmony_ci		err = -ENOENT;
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	up_read(&cmtp_session_sem);
4498c2ecf20Sopenharmony_ci	return err;
4508c2ecf20Sopenharmony_ci}
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ciint cmtp_get_connlist(struct cmtp_connlist_req *req)
4538c2ecf20Sopenharmony_ci{
4548c2ecf20Sopenharmony_ci	struct cmtp_session *session;
4558c2ecf20Sopenharmony_ci	int err = 0, n = 0;
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci	BT_DBG("");
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci	down_read(&cmtp_session_sem);
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	list_for_each_entry(session, &cmtp_session_list, list) {
4628c2ecf20Sopenharmony_ci		struct cmtp_conninfo ci;
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci		__cmtp_copy_session(session, &ci);
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci		if (copy_to_user(req->ci, &ci, sizeof(ci))) {
4678c2ecf20Sopenharmony_ci			err = -EFAULT;
4688c2ecf20Sopenharmony_ci			break;
4698c2ecf20Sopenharmony_ci		}
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci		if (++n >= req->cnum)
4728c2ecf20Sopenharmony_ci			break;
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci		req->ci++;
4758c2ecf20Sopenharmony_ci	}
4768c2ecf20Sopenharmony_ci	req->cnum = n;
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci	up_read(&cmtp_session_sem);
4798c2ecf20Sopenharmony_ci	return err;
4808c2ecf20Sopenharmony_ci}
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ciint cmtp_get_conninfo(struct cmtp_conninfo *ci)
4838c2ecf20Sopenharmony_ci{
4848c2ecf20Sopenharmony_ci	struct cmtp_session *session;
4858c2ecf20Sopenharmony_ci	int err = 0;
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_ci	down_read(&cmtp_session_sem);
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci	session = __cmtp_get_session(&ci->bdaddr);
4908c2ecf20Sopenharmony_ci	if (session)
4918c2ecf20Sopenharmony_ci		__cmtp_copy_session(session, ci);
4928c2ecf20Sopenharmony_ci	else
4938c2ecf20Sopenharmony_ci		err = -ENOENT;
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci	up_read(&cmtp_session_sem);
4968c2ecf20Sopenharmony_ci	return err;
4978c2ecf20Sopenharmony_ci}
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_cistatic int __init cmtp_init(void)
5018c2ecf20Sopenharmony_ci{
5028c2ecf20Sopenharmony_ci	BT_INFO("CMTP (CAPI Emulation) ver %s", VERSION);
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ci	return cmtp_init_sockets();
5058c2ecf20Sopenharmony_ci}
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_cistatic void __exit cmtp_exit(void)
5088c2ecf20Sopenharmony_ci{
5098c2ecf20Sopenharmony_ci	cmtp_cleanup_sockets();
5108c2ecf20Sopenharmony_ci}
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_cimodule_init(cmtp_init);
5138c2ecf20Sopenharmony_cimodule_exit(cmtp_exit);
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ciMODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
5168c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Bluetooth CMTP ver " VERSION);
5178c2ecf20Sopenharmony_ciMODULE_VERSION(VERSION);
5188c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
5198c2ecf20Sopenharmony_ciMODULE_ALIAS("bt-proto-5");
520