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