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