2007-02-09 17:24:33 +03:00
/*
2005-04-17 02:20:36 +04:00
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
2007-02-09 17:24:33 +03:00
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
2005-04-17 02:20:36 +04:00
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE .
2007-02-09 17:24:33 +03:00
ALL LIABILITY , INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS ,
COPYRIGHTS , TRADEMARKS OR OTHER RIGHTS , RELATING TO USE OF THIS
2005-04-17 02:20:36 +04:00
SOFTWARE IS DISCLAIMED .
*/
2012-10-23 17:32:18 +04:00
# include <linux/export.h>
2010-01-14 14:10:54 +03:00
# include <linux/proc_fs.h>
# include <linux/seq_file.h>
2005-04-17 02:20:36 +04:00
# include <linux/types.h>
# include <linux/errno.h>
# include <linux/kernel.h>
# include <linux/sched.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>
2011-03-21 16:20:06 +03:00
# include <linux/kthread.h>
2005-04-17 02:20:36 +04:00
# 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 0x20
# 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 )
{
2006-07-06 17:40:09 +04:00
struct cmtp_application * app = kzalloc ( sizeof ( * app ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
BT_DBG ( " session %p application %p appl %d " , 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 ;
struct list_head * p , * n ;
list_for_each_safe ( p , n , & session - > applications ) {
app = list_entry ( p , struct cmtp_application , 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 & 0xff ) > 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 %d " , session , skb , skb - > len ) ;
scb - > id = - 1 ;
scb - > data = ( CAPIMSG_COMMAND ( skb - > data ) = = CAPI_DATA_B3 ) ;
skb_queue_tail ( & session - > transmit , skb ) ;
2011-03-21 16:20:06 +03:00
wake_up_interruptible ( sk_sleep ( session - > sock - > sk ) ) ;
2005-04-17 02:20:36 +04:00
}
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 %d msgnum %d " , session , subcmd , appl , msgnum ) ;
2011-01-11 18:20:20 +03:00
skb = alloc_skb ( CAPI_MSG_BASELEN + 6 + len , GFP_ATOMIC ) ;
if ( ! skb ) {
2005-04-17 02:20:36 +04:00
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 , 0x0001 ) ;
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 %d " , session , skb , skb - > len ) ;
switch ( CAPIMSG_SUBCOMMAND ( skb - > data ) ) {
case CAPI_CONF :
2007-01-08 04:16:23 +03:00
if ( skb - > len < CAPI_MSG_BASELEN + 10 )
break ;
2005-04-17 02:20:36 +04:00
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 :
2007-01-08 04:16:23 +03:00
if ( skb - > len < CAPI_MSG_BASELEN + 11 + sizeof ( capi_profile ) )
break ;
2005-04-17 02:20:36 +04:00
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 :
2007-01-08 04:16:23 +03:00
if ( skb - > len < CAPI_MSG_BASELEN + 15 )
break ;
2005-04-17 02:20:36 +04:00
if ( ! info & & ctrl ) {
2007-01-08 04:16:23 +03:00
int len = min_t ( uint , CAPI_MANUFACTURER_LEN ,
skb - > data [ CAPI_MSG_BASELEN + 14 ] ) ;
memset ( ctrl - > manu , 0 , CAPI_MANUFACTURER_LEN ) ;
2005-04-17 02:20:36 +04:00
strncpy ( ctrl - > manu ,
2007-01-08 04:16:23 +03:00
skb - > data + CAPI_MSG_BASELEN + 15 , len ) ;
2005-04-17 02:20:36 +04:00
}
break ;
case CAPI_FUNCTION_GET_VERSION :
2007-01-08 04:16:23 +03:00
if ( skb - > len < CAPI_MSG_BASELEN + 32 )
break ;
2005-04-17 02:20:36 +04:00
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 :
2007-01-08 04:16:23 +03:00
if ( skb - > len < CAPI_MSG_BASELEN + 17 )
break ;
2005-04-17 02:20:36 +04:00
if ( ! info & & ctrl ) {
2007-01-08 04:16:23 +03:00
int len = min_t ( uint , CAPI_SERIAL_LEN ,
skb - > data [ CAPI_MSG_BASELEN + 16 ] ) ;
2005-04-17 02:20:36 +04:00
memset ( ctrl - > serial , 0 , CAPI_SERIAL_LEN ) ;
strncpy ( ctrl - > serial ,
2007-01-08 04:16:23 +03:00
skb - > data + CAPI_MSG_BASELEN + 17 , len ) ;
2005-04-17 02:20:36 +04:00
}
break ;
}
break ;
case CAPI_IND :
2007-01-08 04:16:23 +03:00
if ( skb - > len < CAPI_MSG_BASELEN + 6 )
break ;
2005-04-17 02:20:36 +04:00
func = CAPIMSG_U16 ( skb - > data , CAPI_MSG_BASELEN + 3 ) ;
if ( func = = CAPI_FUNCTION_LOOPBACK ) {
2007-01-08 04:16:23 +03:00
int len = min_t ( uint , skb - > len - CAPI_MSG_BASELEN - 6 ,
skb - > data [ CAPI_MSG_BASELEN + 5 ] ) ;
2005-04-17 02:20:36 +04:00
appl = CAPIMSG_APPID ( skb - > data ) ;
msgnum = CAPIMSG_MSGID ( skb - > data ) ;
cmtp_send_interopmsg ( session , CAPI_RESP , appl , msgnum , func ,
2007-01-08 04:16:23 +03:00
skb - > data + CAPI_MSG_BASELEN + 6 , len ) ;
2005-04-17 02:20:36 +04:00
}
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 ;
2011-05-20 01:50:05 +04:00
__u16 appl ;
2005-04-17 02:20:36 +04:00
__u32 contr ;
BT_DBG ( " session %p skb %p len %d " , session , skb , skb - > len ) ;
2007-01-08 04:16:23 +03:00
if ( skb - > len < CAPI_MSG_BASELEN )
return ;
2005-04-17 02:20:36 +04:00
if ( CAPIMSG_COMMAND ( skb - > data ) = = CAPI_INTEROPERABILITY ) {
cmtp_recv_interopmsg ( session , skb ) ;
return ;
}
2015-04-03 21:02:09 +03:00
if ( session - > flags & BIT ( CMTP_LOOPBACK ) ) {
2005-04-17 02:20:36 +04:00
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 %d " , appl ) ;
kfree_skb ( skb ) ;
return ;
}
if ( ( contr & 0x7f ) = = 0x01 ) {
contr = ( contr & 0xffffff80 ) | 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 ) ;
2009-06-07 13:09:23 +04:00
capi_ctr_down ( ctrl ) ;
2005-04-17 02:20:36 +04:00
2011-08-05 18:44:21 +04:00
atomic_inc ( & session - > terminate ) ;
wake_up_process ( session - > task ) ;
2005-04-17 02:20:36 +04:00
}
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 %d level3cnt %d datablkcnt %d datablklen %d " ,
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 , 0x0000 , 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 %d " , 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 %d " , appl ) ;
return CAPI_ILLAPPNR ;
}
CAPIMSG_SETAPPID ( skb - > data , application - > mapping ) ;
if ( ( contr & 0x7f ) = = session - > num ) {
contr = ( contr & 0xffffff80 ) | 0x01 ;
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 " ;
}
2010-01-14 14:10:54 +03:00
static int cmtp_proc_show ( struct seq_file * m , void * v )
2005-04-17 02:20:36 +04:00
{
2010-01-14 14:10:54 +03:00
struct capi_ctr * ctrl = m - > private ;
2005-04-17 02:20:36 +04:00
struct cmtp_session * session = ctrl - > driverdata ;
struct cmtp_application * app ;
struct list_head * p , * n ;
2010-01-14 14:10:54 +03:00
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 ) ;
2005-04-17 02:20:36 +04:00
list_for_each_safe ( p , n , & session - > applications ) {
app = list_entry ( p , struct cmtp_application , list ) ;
2010-01-14 14:10:54 +03:00
seq_printf ( m , " appl %d -> %d \n " , app - > appl , app - > mapping ) ;
2005-04-17 02:20:36 +04:00
}
2010-01-14 14:10:54 +03:00
return 0 ;
}
2005-04-17 02:20:36 +04:00
2010-01-14 14:10:54 +03:00
static int cmtp_proc_open ( struct inode * inode , struct file * file )
{
2013-04-01 02:16:14 +04:00
return single_open ( file , cmtp_proc_show , PDE_DATA ( inode ) ) ;
2005-04-17 02:20:36 +04:00
}
2010-01-14 14:10:54 +03:00
static const struct file_operations cmtp_proc_fops = {
. owner = THIS_MODULE ,
. open = cmtp_proc_open ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = single_release ,
} ;
2005-04-17 02:20:36 +04:00
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 , 0xffff , CMTP_INITIAL_MSGNUM ,
CAPI_FUNCTION_GET_PROFILE , buf , 4 ) ;
ret = wait_event_interruptible_timeout ( session - > wait ,
session - > ncontroller , CMTP_INTEROP_TIMEOUT ) ;
2007-02-09 17:24:33 +03:00
2005-04-17 02:20:36 +04:00
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 ;
2010-01-14 14:10:54 +03:00
session - > ctrl . proc_fops = & cmtp_proc_fops ;
2005-04-17 02:20:36 +04:00
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 , 0xffff , cmtp_msgnum_get ( session ) ,
CAPI_FUNCTION_GET_MANUFACTURER , buf , 4 ) ;
cmtp_send_interopmsg ( session , CAPI_REQ , 0xffff , cmtp_msgnum_get ( session ) ,
CAPI_FUNCTION_GET_VERSION , buf , 4 ) ;
cmtp_send_interopmsg ( session , CAPI_REQ , 0xffff , cmtp_msgnum_get ( session ) ,
CAPI_FUNCTION_GET_SERIAL_NUMBER , buf , 4 ) ;
cmtp_send_interopmsg ( session , CAPI_REQ , 0xffff , 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 ) ;
}