/*
CMTP implementation for Linux Bluetooth stack ( BlueZ ) .
Copyright ( C ) 2002 - 2003 Marcel Holtmann < marcel @ holtmann . org >
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License version 2 as
published by the Free Software Foundation ;
THE SOFTWARE IS PROVIDED " AS IS " , WITHOUT WARRANTY OF ANY KIND , EXPRESS
OR IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY ,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS .
IN NO EVENT SHALL THE COPYRIGHT HOLDER ( S ) AND AUTHOR ( S ) BE LIABLE FOR ANY
CLAIM , OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES , OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE , DATA OR PROFITS , WHETHER IN AN
ACTION OF CONTRACT , NEGLIGENCE OR OTHER TORTIOUS ACTION , ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE .
ALL LIABILITY , INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS ,
COPYRIGHTS , TRADEMARKS OR OTHER RIGHTS , RELATING TO USE OF THIS
SOFTWARE IS DISCLAIMED .
*/
#include <linux/export.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/sched/signal.h>
#include <linux/slab.h>
#include <linux/poll.h>
#include <linux/fcntl.h>
#include <linux/skbuff.h>
#include <linux/socket.h>
#include <linux/ioctl.h>
#include <linux/file.h>
#include <linux/wait.h>
#include <linux/kthread.h>
#include <net/sock.h>
#include <linux/isdn/capilli.h>
#include <linux/isdn/capicmd.h>
#include <linux/isdn/capiutil.h>
#include "cmtp.h"
#define CAPI_INTEROPERABILITY 0 x20
#define CAPI_INTEROPERABILITY_REQ CAPICMD(CAPI_INTEROPERABILITY, CAPI_REQ)
#define CAPI_INTEROPERABILITY_CONF CAPICMD(CAPI_INTEROPERABILITY, CAPI_CONF)
#define CAPI_INTEROPERABILITY_IND CAPICMD(CAPI_INTEROPERABILITY, CAPI_IND)
#define CAPI_INTEROPERABILITY_RESP CAPICMD(CAPI_INTEROPERABILITY, CAPI_RESP)
#define CAPI_INTEROPERABILITY_REQ_LEN (CAPI_MSG_BASELEN + 2 )
#define CAPI_INTEROPERABILITY_CONF_LEN (CAPI_MSG_BASELEN + 4 )
#define CAPI_INTEROPERABILITY_IND_LEN (CAPI_MSG_BASELEN + 2 )
#define CAPI_INTEROPERABILITY_RESP_LEN (CAPI_MSG_BASELEN + 2 )
#define CAPI_FUNCTION_REGISTER 0
#define CAPI_FUNCTION_RELEASE 1
#define CAPI_FUNCTION_GET_PROFILE 2
#define CAPI_FUNCTION_GET_MANUFACTURER 3
#define CAPI_FUNCTION_GET_VERSION 4
#define CAPI_FUNCTION_GET_SERIAL_NUMBER 5
#define CAPI_FUNCTION_MANUFACTURER 6
#define CAPI_FUNCTION_LOOPBACK 7
#define CMTP_MSGNUM 1
#define CMTP_APPLID 2
#define CMTP_MAPPING 3
static struct cmtp_application *cmtp_application_add(struct cmtp_session *session, __u16 appl)
{
struct cmtp_application *app = kzalloc(sizeof (*app), GFP_KERNEL);
BT_DBG("session %p application %p appl %u" , session, app, appl);
if (!app)
return NULL;
app->state = BT_OPEN;
app->appl = appl;
list_add_tail(&app->list, &session->applications);
return app;
}
static void cmtp_application_del(struct cmtp_session *session, struct cmtp_application *app)
{
BT_DBG("session %p application %p" , session, app);
if (app) {
list_del(&app->list);
kfree(app);
}
}
static struct cmtp_application *cmtp_application_get(struct cmtp_session *session, int pattern, __u16 value)
{
struct cmtp_application *app;
list_for_each_entry(app, &session->applications, list) {
switch (pattern) {
case CMTP_MSGNUM:
if (app->msgnum == value)
return app;
break ;
case CMTP_APPLID:
if (app->appl == value)
return app;
break ;
case CMTP_MAPPING:
if (app->mapping == value)
return app;
break ;
}
}
return NULL;
}
static int cmtp_msgnum_get(struct cmtp_session *session)
{
session->msgnum++;
if ((session->msgnum & 0 xff) > 200 )
session->msgnum = CMTP_INITIAL_MSGNUM + 1 ;
return session->msgnum;
}
static void cmtp_send_capimsg(struct cmtp_session *session, struct sk_buff *skb)
{
struct cmtp_scb *scb = (void *) skb->cb;
BT_DBG("session %p skb %p len %u" , session, skb, skb->len);
scb->id = -1 ;
scb->data = (CAPIMSG_COMMAND(skb->data) == CAPI_DATA_B3);
skb_queue_tail(&session->transmit, skb);
wake_up_interruptible(sk_sleep(session->sock->sk));
}
static void cmtp_send_interopmsg(struct cmtp_session *session,
__u8 subcmd, __u16 appl, __u16 msgnum,
__u16 function, unsigned char *buf, int len)
{
struct sk_buff *skb;
unsigned char *s;
BT_DBG("session %p subcmd 0x%02x appl %u msgnum %u" , session, subcmd, appl, msgnum);
skb = alloc_skb(CAPI_MSG_BASELEN + 6 + len, GFP_ATOMIC);
if (!skb) {
BT_ERR("Can't allocate memory for interoperability packet" );
return ;
}
s = skb_put(skb, CAPI_MSG_BASELEN + 6 + len);
capimsg_setu16(s, 0 , CAPI_MSG_BASELEN + 6 + len);
capimsg_setu16(s, 2 , appl);
capimsg_setu8 (s, 4 , CAPI_INTEROPERABILITY);
capimsg_setu8 (s, 5 , subcmd);
capimsg_setu16(s, 6 , msgnum);
/* Interoperability selector (Bluetooth Device Management) */
capimsg_setu16(s, 8 , 0 x0001);
capimsg_setu8 (s, 10 , 3 + len);
capimsg_setu16(s, 11 , function);
capimsg_setu8 (s, 13 , len);
if (len > 0 )
memcpy(s + 14 , buf, len);
cmtp_send_capimsg(session, skb);
}
static void cmtp_recv_interopmsg(struct cmtp_session *session, struct sk_buff *skb)
{
struct capi_ctr *ctrl = &session->ctrl;
struct cmtp_application *application;
__u16 appl, msgnum, func, info;
__u32 controller;
BT_DBG("session %p skb %p len %u" , session, skb, skb->len);
switch (CAPIMSG_SUBCOMMAND(skb->data)) {
case CAPI_CONF:
if (skb->len < CAPI_MSG_BASELEN + 10 )
break ;
func = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 5 );
info = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 8 );
switch (func) {
case CAPI_FUNCTION_REGISTER:
msgnum = CAPIMSG_MSGID(skb->data);
application = cmtp_application_get(session, CMTP_MSGNUM, msgnum);
if (application) {
application->state = BT_CONNECTED;
application->msgnum = 0 ;
application->mapping = CAPIMSG_APPID(skb->data);
wake_up_interruptible(&session->wait);
}
break ;
case CAPI_FUNCTION_RELEASE:
appl = CAPIMSG_APPID(skb->data);
application = cmtp_application_get(session, CMTP_MAPPING, appl);
if (application) {
application->state = BT_CLOSED;
application->msgnum = 0 ;
wake_up_interruptible(&session->wait);
}
break ;
case CAPI_FUNCTION_GET_PROFILE:
if (skb->len < CAPI_MSG_BASELEN + 11 + sizeof (capi_profile))
break ;
controller = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 11 );
msgnum = CAPIMSG_MSGID(skb->data);
if (!info && (msgnum == CMTP_INITIAL_MSGNUM)) {
session->ncontroller = controller;
wake_up_interruptible(&session->wait);
break ;
}
if (!info && ctrl) {
memcpy(&ctrl->profile,
skb->data + CAPI_MSG_BASELEN + 11 ,
sizeof (capi_profile));
session->state = BT_CONNECTED;
capi_ctr_ready(ctrl);
}
break ;
case CAPI_FUNCTION_GET_MANUFACTURER:
if (!info && ctrl && skb->len > CAPI_MSG_BASELEN + 14 )
strscpy_pad(ctrl->manu,
skb->data + CAPI_MSG_BASELEN + 15 ,
skb->data[CAPI_MSG_BASELEN + 14 ]);
break ;
case CAPI_FUNCTION_GET_VERSION:
if (skb->len < CAPI_MSG_BASELEN + 32 )
break ;
if (!info && ctrl) {
ctrl->version.majorversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 16 );
ctrl->version.minorversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 20 );
ctrl->version.majormanuversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 24 );
ctrl->version.minormanuversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 28 );
}
break ;
case CAPI_FUNCTION_GET_SERIAL_NUMBER:
if (!info && ctrl && skb->len > CAPI_MSG_BASELEN + 16 )
strscpy_pad(ctrl->serial,
skb->data + CAPI_MSG_BASELEN + 17 ,
skb->data[CAPI_MSG_BASELEN + 16 ]);
break ;
}
break ;
case CAPI_IND:
if (skb->len < CAPI_MSG_BASELEN + 6 )
break ;
func = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 3 );
if (func == CAPI_FUNCTION_LOOPBACK) {
int len = min_t(uint, skb->len - CAPI_MSG_BASELEN - 6 ,
skb->data[CAPI_MSG_BASELEN + 5 ]);
appl = CAPIMSG_APPID(skb->data);
msgnum = CAPIMSG_MSGID(skb->data);
cmtp_send_interopmsg(session, CAPI_RESP, appl, msgnum, func,
skb->data + CAPI_MSG_BASELEN + 6 , len);
}
break ;
}
kfree_skb(skb);
}
void cmtp_recv_capimsg(struct cmtp_session *session, struct sk_buff *skb)
{
struct capi_ctr *ctrl = &session->ctrl;
struct cmtp_application *application;
__u16 appl;
__u32 contr;
BT_DBG("session %p skb %p len %u" , session, skb, skb->len);
if (skb->len < CAPI_MSG_BASELEN)
return ;
if (CAPIMSG_COMMAND(skb->data) == CAPI_INTEROPERABILITY) {
cmtp_recv_interopmsg(session, skb);
return ;
}
if (session->flags & BIT(CMTP_LOOPBACK)) {
kfree_skb(skb);
return ;
}
appl = CAPIMSG_APPID(skb->data);
contr = CAPIMSG_CONTROL(skb->data);
application = cmtp_application_get(session, CMTP_MAPPING, appl);
if (application) {
appl = application->appl;
CAPIMSG_SETAPPID(skb->data, appl);
} else {
BT_ERR("Can't find application with id %u" , appl);
kfree_skb(skb);
return ;
}
if ((contr & 0 x7f) == 0 x01) {
contr = (contr & 0 xffffff80) | session->num;
CAPIMSG_SETCONTROL(skb->data, contr);
}
capi_ctr_handle_message(ctrl, appl, skb);
}
static int cmtp_load_firmware(struct capi_ctr *ctrl, capiloaddata *data)
{
BT_DBG("ctrl %p data %p" , ctrl, data);
return 0 ;
}
static void cmtp_reset_ctr(struct capi_ctr *ctrl)
{
struct cmtp_session *session = ctrl->driverdata;
BT_DBG("ctrl %p" , ctrl);
capi_ctr_down(ctrl);
atomic_inc(&session->terminate);
wake_up_process(session->task);
}
static void cmtp_register_appl(struct capi_ctr *ctrl, __u16 appl, capi_register_params *rp)
{
DECLARE_WAITQUEUE(wait, current);
struct cmtp_session *session = ctrl->driverdata;
struct cmtp_application *application;
unsigned long timeo = CMTP_INTEROP_TIMEOUT;
unsigned char buf[8 ];
int err = 0 , nconn, want = rp->level3cnt;
BT_DBG("ctrl %p appl %u level3cnt %u datablkcnt %u datablklen %u" ,
ctrl, appl, rp->level3cnt, rp->datablkcnt, rp->datablklen);
application = cmtp_application_add(session, appl);
if (!application) {
BT_ERR("Can't allocate memory for new application" );
return ;
}
if (want < 0 )
nconn = ctrl->profile.nbchannel * -want;
else
nconn = want;
if (nconn == 0 )
nconn = ctrl->profile.nbchannel;
capimsg_setu16(buf, 0 , nconn);
capimsg_setu16(buf, 2 , rp->datablkcnt);
capimsg_setu16(buf, 4 , rp->datablklen);
application->state = BT_CONFIG;
application->msgnum = cmtp_msgnum_get(session);
cmtp_send_interopmsg(session, CAPI_REQ, 0 x0000, application->msgnum,
CAPI_FUNCTION_REGISTER, buf, 6 );
add_wait_queue(&session->wait, &wait);
while (1 ) {
set_current_state(TASK_INTERRUPTIBLE);
if (!timeo) {
err = -EAGAIN;
break ;
}
if (application->state == BT_CLOSED) {
err = -application->err;
break ;
}
if (application->state == BT_CONNECTED)
break ;
if (signal_pending(current)) {
err = -EINTR;
break ;
}
timeo = schedule_timeout(timeo);
}
set_current_state(TASK_RUNNING);
remove_wait_queue(&session->wait, &wait);
if (err) {
cmtp_application_del(session, application);
return ;
}
}
static void cmtp_release_appl(struct capi_ctr *ctrl, __u16 appl)
{
struct cmtp_session *session = ctrl->driverdata;
struct cmtp_application *application;
BT_DBG("ctrl %p appl %u" , ctrl, appl);
application = cmtp_application_get(session, CMTP_APPLID, appl);
if (!application) {
BT_ERR("Can't find application" );
return ;
}
application->msgnum = cmtp_msgnum_get(session);
cmtp_send_interopmsg(session, CAPI_REQ, application->mapping, application->msgnum,
CAPI_FUNCTION_RELEASE, NULL, 0 );
wait_event_interruptible_timeout(session->wait,
(application->state == BT_CLOSED), CMTP_INTEROP_TIMEOUT);
cmtp_application_del(session, application);
}
static u16 cmtp_send_message(struct capi_ctr *ctrl, struct sk_buff *skb)
{
struct cmtp_session *session = ctrl->driverdata;
struct cmtp_application *application;
__u16 appl;
__u32 contr;
BT_DBG("ctrl %p skb %p" , ctrl, skb);
appl = CAPIMSG_APPID(skb->data);
contr = CAPIMSG_CONTROL(skb->data);
application = cmtp_application_get(session, CMTP_APPLID, appl);
if ((!application) || (application->state != BT_CONNECTED)) {
BT_ERR("Can't find application with id %u" , appl);
return CAPI_ILLAPPNR;
}
CAPIMSG_SETAPPID(skb->data, application->mapping);
if ((contr & 0 x7f) == session->num) {
contr = (contr & 0 xffffff80) | 0 x01;
CAPIMSG_SETCONTROL(skb->data, contr);
}
cmtp_send_capimsg(session, skb);
return CAPI_NOERROR;
}
static char *cmtp_procinfo(struct capi_ctr *ctrl)
{
return "CAPI Message Transport Protocol" ;
}
static int cmtp_proc_show(struct seq_file *m, void *v)
{
struct capi_ctr *ctrl = m->private ;
struct cmtp_session *session = ctrl->driverdata;
struct cmtp_application *app;
seq_printf(m, "%s\n\n" , cmtp_procinfo(ctrl));
seq_printf(m, "addr %s\n" , session->name);
seq_printf(m, "ctrl %d\n" , session->num);
list_for_each_entry(app, &session->applications, list) {
seq_printf(m, "appl %u -> %u\n" , app->appl, app->mapping);
}
return 0 ;
}
int cmtp_attach_device(struct cmtp_session *session)
{
unsigned char buf[4 ];
long ret;
BT_DBG("session %p" , session);
capimsg_setu32(buf, 0 , 0 );
cmtp_send_interopmsg(session, CAPI_REQ, 0 xffff, CMTP_INITIAL_MSGNUM,
CAPI_FUNCTION_GET_PROFILE, buf, 4 );
ret = wait_event_interruptible_timeout(session->wait,
session->ncontroller, CMTP_INTEROP_TIMEOUT);
BT_INFO("Found %d CAPI controller(s) on device %s" , session->ncontroller, session->name);
if (!ret)
return -ETIMEDOUT;
if (!session->ncontroller)
return -ENODEV;
if (session->ncontroller > 1 )
BT_INFO("Setting up only CAPI controller 1" );
session->ctrl.owner = THIS_MODULE;
session->ctrl.driverdata = session;
strcpy(session->ctrl.name, session->name);
session->ctrl.driver_name = "cmtp" ;
session->ctrl.load_firmware = cmtp_load_firmware;
session->ctrl.reset_ctr = cmtp_reset_ctr;
session->ctrl.register_appl = cmtp_register_appl;
session->ctrl.release_appl = cmtp_release_appl;
session->ctrl.send_message = cmtp_send_message;
session->ctrl.procinfo = cmtp_procinfo;
session->ctrl.proc_show = cmtp_proc_show;
if (attach_capi_ctr(&session->ctrl) < 0 ) {
BT_ERR("Can't attach new controller" );
return -EBUSY;
}
session->num = session->ctrl.cnr;
BT_DBG("session %p num %d" , session, session->num);
capimsg_setu32(buf, 0 , 1 );
cmtp_send_interopmsg(session, CAPI_REQ, 0 xffff, cmtp_msgnum_get(session),
CAPI_FUNCTION_GET_MANUFACTURER, buf, 4 );
cmtp_send_interopmsg(session, CAPI_REQ, 0 xffff, cmtp_msgnum_get(session),
CAPI_FUNCTION_GET_VERSION, buf, 4 );
cmtp_send_interopmsg(session, CAPI_REQ, 0 xffff, cmtp_msgnum_get(session),
CAPI_FUNCTION_GET_SERIAL_NUMBER, buf, 4 );
cmtp_send_interopmsg(session, CAPI_REQ, 0 xffff, cmtp_msgnum_get(session),
CAPI_FUNCTION_GET_PROFILE, buf, 4 );
return 0 ;
}
void cmtp_detach_device(struct cmtp_session *session)
{
BT_DBG("session %p" , session);
detach_capi_ctr(&session->ctrl);
}
Messung V0.5 in Prozent C=100 H=100 G=100
¤ Dauer der Verarbeitung: 0.12 Sekunden
(vorverarbeitet am 2026-09-27)
¤
*© Formatika GbR, Deutschland