2009-10-06 16:19:17 +04:00
/*
* Kernel CAPI interface for the Gigaset driver
*
* Copyright ( c ) 2009 by Tilman Schmidt < tilman @ imap . cc > .
*
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation ; either version 2 of
* the License , or ( at your option ) any later version .
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
*/
# include "gigaset.h"
2010-01-14 14:10:54 +03:00
# include <linux/proc_fs.h>
# include <linux/seq_file.h>
2009-10-06 16:19:17 +04:00
# include <linux/isdn/capilli.h>
# include <linux/isdn/capicmd.h>
# include <linux/isdn/capiutil.h>
2011-07-10 20:23:16 +04:00
# include <linux/export.h>
2009-10-06 16:19:17 +04:00
/* missing from kernelcapi.h */
# define CapiNcpiNotSupportedByProtocol 0x0001
# define CapiFlagsNotSupportedByProtocol 0x0002
# define CapiAlertAlreadySent 0x0003
# define CapiFacilitySpecificFunctionNotSupported 0x3011
/* missing from capicmd.h */
# define CAPI_CONNECT_IND_BASELEN (CAPI_MSG_BASELEN+4+2+8*1)
# define CAPI_CONNECT_ACTIVE_IND_BASELEN (CAPI_MSG_BASELEN+4+3*1)
# define CAPI_CONNECT_B3_IND_BASELEN (CAPI_MSG_BASELEN+4+1)
# define CAPI_CONNECT_B3_ACTIVE_IND_BASELEN (CAPI_MSG_BASELEN+4+1)
# define CAPI_DATA_B3_REQ_LEN64 (CAPI_MSG_BASELEN+4+4+2+2+2+8)
# define CAPI_DATA_B3_CONF_LEN (CAPI_MSG_BASELEN+4+2+2)
# define CAPI_DISCONNECT_IND_LEN (CAPI_MSG_BASELEN+4+2)
# define CAPI_DISCONNECT_B3_IND_BASELEN (CAPI_MSG_BASELEN+4+2+1)
# define CAPI_FACILITY_CONF_BASELEN (CAPI_MSG_BASELEN+4+2+2+1)
/* most _CONF messages contain only Controller/PLCI/NCCI and Info parameters */
# define CAPI_STDCONF_LEN (CAPI_MSG_BASELEN+4+2)
# define CAPI_FACILITY_HANDSET 0x0000
# define CAPI_FACILITY_DTMF 0x0001
# define CAPI_FACILITY_V42BIS 0x0002
# define CAPI_FACILITY_SUPPSVC 0x0003
# define CAPI_FACILITY_WAKEUP 0x0004
# define CAPI_FACILITY_LI 0x0005
# define CAPI_SUPPSVC_GETSUPPORTED 0x0000
2010-07-05 18:19:04 +04:00
# define CAPI_SUPPSVC_LISTEN 0x0001
2009-10-06 16:19:17 +04:00
/* missing from capiutil.h */
# define CAPIMSG_PLCI_PART(m) CAPIMSG_U8(m, 9)
# define CAPIMSG_NCCI_PART(m) CAPIMSG_U16(m, 10)
# define CAPIMSG_HANDLE_REQ(m) CAPIMSG_U16(m, 18) /* DATA_B3_REQ/_IND only! */
# define CAPIMSG_FLAGS(m) CAPIMSG_U16(m, 20)
# define CAPIMSG_SETCONTROLLER(m, contr) capimsg_setu8(m, 8, contr)
# define CAPIMSG_SETPLCI_PART(m, plci) capimsg_setu8(m, 9, plci)
# define CAPIMSG_SETNCCI_PART(m, ncci) capimsg_setu16(m, 10, ncci)
# define CAPIMSG_SETFLAGS(m, flags) capimsg_setu16(m, 20, flags)
/* parameters with differing location in DATA_B3_CONF/_RESP: */
# define CAPIMSG_SETHANDLE_CONF(m, handle) capimsg_setu16(m, 12, handle)
# define CAPIMSG_SETINFO_CONF(m, info) capimsg_setu16(m, 14, info)
/* Flags (DATA_B3_REQ/_IND) */
# define CAPI_FLAGS_DELIVERY_CONFIRMATION 0x04
# define CAPI_FLAGS_RESERVED (~0x1f)
/* buffer sizes */
# define MAX_BC_OCTETS 11
# define MAX_HLC_OCTETS 3
# define MAX_NUMBER_DIGITS 20
# define MAX_FMT_IE_LEN 20
2010-06-21 17:55:20 +04:00
/* values for bcs->apconnstate */
2009-10-06 16:19:17 +04:00
# define APCONN_NONE 0 /* inactive/listening */
# define APCONN_SETUP 1 /* connecting */
# define APCONN_ACTIVE 2 /* B channel up */
/* registered application data structure */
struct gigaset_capi_appl {
struct list_head ctrlist ;
struct gigaset_capi_appl * bcnext ;
u16 id ;
2010-06-21 17:54:19 +04:00
struct capi_register_params rp ;
2009-10-06 16:19:17 +04:00
u16 nextMessageNumber ;
u32 listenInfoMask ;
u32 listenCIPmask ;
} ;
/* CAPI specific controller data structure */
struct gigaset_capi_ctr {
struct capi_ctr ctr ;
struct list_head appls ;
struct sk_buff_head sendqueue ;
atomic_t sendqlen ;
/* two _cmsg structures possibly used concurrently: */
_cmsg hcmsg ; /* for message composition triggered from hardware */
_cmsg acmsg ; /* for dissection of messages sent from application */
u8 bc_buf [ MAX_BC_OCTETS + 1 ] ;
u8 hlc_buf [ MAX_HLC_OCTETS + 1 ] ;
u8 cgpty_buf [ MAX_NUMBER_DIGITS + 3 ] ;
u8 cdpty_buf [ MAX_NUMBER_DIGITS + 2 ] ;
} ;
/* CIP Value table (from CAPI 2.0 standard, ch. 6.1) */
static struct {
u8 * bc ;
u8 * hlc ;
} cip2bchlc [ ] = {
[ 1 ] = { " 8090A3 " , NULL } ,
/* Speech (A-law) */
[ 2 ] = { " 8890 " , NULL } ,
/* Unrestricted digital information */
[ 3 ] = { " 8990 " , NULL } ,
/* Restricted digital information */
[ 4 ] = { " 9090A3 " , NULL } ,
/* 3,1 kHz audio (A-law) */
[ 5 ] = { " 9190 " , NULL } ,
/* 7 kHz audio */
[ 6 ] = { " 9890 " , NULL } ,
/* Video */
[ 7 ] = { " 88C0C6E6 " , NULL } ,
/* Packet mode */
[ 8 ] = { " 8890218F " , NULL } ,
/* 56 kbit/s rate adaptation */
[ 9 ] = { " 9190A5 " , NULL } ,
/* Unrestricted digital information with tones/announcements */
[ 16 ] = { " 8090A3 " , " 9181 " } ,
/* Telephony */
[ 17 ] = { " 9090A3 " , " 9184 " } ,
/* Group 2/3 facsimile */
[ 18 ] = { " 8890 " , " 91A1 " } ,
/* Group 4 facsimile Class 1 */
[ 19 ] = { " 8890 " , " 91A4 " } ,
/* Teletex service basic and mixed mode
and Group 4 facsimile service Classes II and III */
[ 20 ] = { " 8890 " , " 91A8 " } ,
/* Teletex service basic and processable mode */
[ 21 ] = { " 8890 " , " 91B1 " } ,
/* Teletex service basic mode */
[ 22 ] = { " 8890 " , " 91B2 " } ,
/* International interworking for Videotex */
[ 23 ] = { " 8890 " , " 91B5 " } ,
/* Telex */
[ 24 ] = { " 8890 " , " 91B8 " } ,
/* Message Handling Systems in accordance with X.400 */
[ 25 ] = { " 8890 " , " 91C1 " } ,
/* OSI application in accordance with X.200 */
[ 26 ] = { " 9190A5 " , " 9181 " } ,
/* 7 kHz telephony */
[ 27 ] = { " 9190A5 " , " 916001 " } ,
/* Video telephony, first connection */
[ 28 ] = { " 8890 " , " 916002 " } ,
/* Video telephony, second connection */
} ;
/*
* helper functions
* = = = = = = = = = = = = = = = =
*/
/*
* emit unsupported parameter warning
*/
static inline void ignore_cstruct_param ( struct cardstate * cs , _cstruct param ,
char * msgname , char * paramname )
{
if ( param & & * param )
dev_warn ( cs - > dev , " %s: ignoring unsupported parameter: %s \n " ,
msgname , paramname ) ;
}
/*
* convert an IE from Gigaset hex string to ETSI binary representation
* including length byte
* return value : result length , - 1 on error
*/
static int encode_ie ( char * in , u8 * out , int maxlen )
{
int l = 0 ;
while ( * in ) {
2010-02-22 16:10:22 +03:00
if ( ! isxdigit ( in [ 0 ] ) | | ! isxdigit ( in [ 1 ] ) | | l > = maxlen )
2009-10-06 16:19:17 +04:00
return - 1 ;
2010-05-25 01:33:25 +04:00
out [ + + l ] = ( hex_to_bin ( in [ 0 ] ) < < 4 ) + hex_to_bin ( in [ 1 ] ) ;
2009-10-06 16:19:17 +04:00
in + = 2 ;
}
out [ 0 ] = l ;
return l ;
}
/*
* convert an IE from ETSI binary representation including length byte
* to Gigaset hex string
*/
static void decode_ie ( u8 * in , char * out )
{
int i = * in ;
while ( i - - > 0 ) {
/* ToDo: conversion to upper case necessary? */
* out + + = toupper ( hex_asc_hi ( * + + in ) ) ;
* out + + = toupper ( hex_asc_lo ( * in ) ) ;
}
}
/*
* retrieve application data structure for an application ID
*/
static inline struct gigaset_capi_appl *
get_appl ( struct gigaset_capi_ctr * iif , u16 appl )
{
struct gigaset_capi_appl * ap ;
list_for_each_entry ( ap , & iif - > appls , ctrlist )
if ( ap - > id = = appl )
return ap ;
return NULL ;
}
/*
* dump CAPI message to kernel messages for debugging
*/
static inline void dump_cmsg ( enum debuglevel level , const char * tag , _cmsg * p )
{
# ifdef CONFIG_GIGASET_DEBUG
_cdebbuf * cdb ;
if ( ! ( gigaset_debuglevel & level ) )
return ;
cdb = capi_cmsg2str ( p ) ;
if ( cdb ) {
gig_dbg ( level , " %s: [%d] %s " , tag , p - > ApplId , cdb - > buf ) ;
cdebbuf_free ( cdb ) ;
} else {
gig_dbg ( level , " %s: [%d] %s " , tag , p - > ApplId ,
capi_cmd2str ( p - > Command , p - > Subcommand ) ) ;
}
# endif
}
static inline void dump_rawmsg ( enum debuglevel level , const char * tag ,
unsigned char * data )
{
# ifdef CONFIG_GIGASET_DEBUG
char * dbgline ;
int i , l ;
if ( ! ( gigaset_debuglevel & level ) )
return ;
l = CAPIMSG_LEN ( data ) ;
if ( l < 12 ) {
gig_dbg ( level , " %s: ??? LEN=%04d " , tag , l ) ;
return ;
}
gig_dbg ( level , " %s: 0x%02x:0x%02x: ID=%03d #0x%04x LEN=%04d NCCI=0x%x " ,
tag , CAPIMSG_COMMAND ( data ) , CAPIMSG_SUBCOMMAND ( data ) ,
CAPIMSG_APPID ( data ) , CAPIMSG_MSGID ( data ) , l ,
CAPIMSG_CONTROL ( data ) ) ;
l - = 12 ;
dbgline = kmalloc ( 3 * l , GFP_ATOMIC ) ;
if ( ! dbgline )
return ;
for ( i = 0 ; i < l ; i + + ) {
dbgline [ 3 * i ] = hex_asc_hi ( data [ 12 + i ] ) ;
dbgline [ 3 * i + 1 ] = hex_asc_lo ( data [ 12 + i ] ) ;
dbgline [ 3 * i + 2 ] = ' ' ;
}
dbgline [ 3 * l - 1 ] = ' \0 ' ;
gig_dbg ( level , " %s " , dbgline ) ;
kfree ( dbgline ) ;
if ( CAPIMSG_COMMAND ( data ) = = CAPI_DATA_B3 & &
( CAPIMSG_SUBCOMMAND ( data ) = = CAPI_REQ | |
2010-07-05 18:18:59 +04:00
CAPIMSG_SUBCOMMAND ( data ) = = CAPI_IND ) ) {
2009-10-06 16:19:17 +04:00
l = CAPIMSG_DATALEN ( data ) ;
2010-07-05 18:18:59 +04:00
gig_dbg ( level , " DataLength=%d " , l ) ;
if ( l < = 0 | | ! ( gigaset_debuglevel & DEBUG_LLDATA ) )
return ;
if ( l > 64 )
l = 64 ; /* arbitrary limit */
2009-10-06 16:19:17 +04:00
dbgline = kmalloc ( 3 * l , GFP_ATOMIC ) ;
if ( ! dbgline )
return ;
data + = CAPIMSG_LEN ( data ) ;
for ( i = 0 ; i < l ; i + + ) {
dbgline [ 3 * i ] = hex_asc_hi ( data [ i ] ) ;
dbgline [ 3 * i + 1 ] = hex_asc_lo ( data [ i ] ) ;
dbgline [ 3 * i + 2 ] = ' ' ;
}
dbgline [ 3 * l - 1 ] = ' \0 ' ;
gig_dbg ( level , " %s " , dbgline ) ;
kfree ( dbgline ) ;
}
# endif
}
/*
* format CAPI IE as string
*/
static const char * format_ie ( const char * ie )
{
static char result [ 3 * MAX_FMT_IE_LEN ] ;
int len , count ;
char * pout = result ;
if ( ! ie )
return " NULL " ;
count = len = ie [ 0 ] ;
if ( count > MAX_FMT_IE_LEN )
count = MAX_FMT_IE_LEN - 1 ;
while ( count - - ) {
* pout + + = hex_asc_hi ( * + + ie ) ;
* pout + + = hex_asc_lo ( * ie ) ;
* pout + + = ' ' ;
}
if ( len > MAX_FMT_IE_LEN ) {
* pout + + = ' . ' ;
* pout + + = ' . ' ;
* pout + + = ' . ' ;
}
* - - pout = 0 ;
return result ;
}
2010-06-21 17:54:50 +04:00
/*
* emit DATA_B3_CONF message
*/
static void send_data_b3_conf ( struct cardstate * cs , struct capi_ctr * ctr ,
u16 appl , u16 msgid , int channel ,
u16 handle , u16 info )
{
struct sk_buff * cskb ;
u8 * msg ;
cskb = alloc_skb ( CAPI_DATA_B3_CONF_LEN , GFP_ATOMIC ) ;
if ( ! cskb ) {
dev_err ( cs - > dev , " %s: out of memory \n " , __func__ ) ;
return ;
}
/* frequent message, avoid _cmsg overhead */
msg = __skb_put ( cskb , CAPI_DATA_B3_CONF_LEN ) ;
CAPIMSG_SETLEN ( msg , CAPI_DATA_B3_CONF_LEN ) ;
CAPIMSG_SETAPPID ( msg , appl ) ;
CAPIMSG_SETCOMMAND ( msg , CAPI_DATA_B3 ) ;
CAPIMSG_SETSUBCOMMAND ( msg , CAPI_CONF ) ;
CAPIMSG_SETMSGID ( msg , msgid ) ;
CAPIMSG_SETCONTROLLER ( msg , ctr - > cnr ) ;
CAPIMSG_SETPLCI_PART ( msg , channel ) ;
CAPIMSG_SETNCCI_PART ( msg , 1 ) ;
CAPIMSG_SETHANDLE_CONF ( msg , handle ) ;
CAPIMSG_SETINFO_CONF ( msg , info ) ;
/* emit message */
dump_rawmsg ( DEBUG_MCMD , __func__ , msg ) ;
capi_ctr_handle_message ( ctr , appl , cskb ) ;
}
2009-10-06 16:19:17 +04:00
/*
* driver interface functions
* = = = = = = = = = = = = = = = = = = = = = = = = = =
*/
/**
* gigaset_skb_sent ( ) - acknowledge transmission of outgoing skb
* @ bcs : B channel descriptor structure .
* @ skb : sent data .
*
* Called by hardware module { bas , ser , usb } _gigaset when the data in a
* skb has been successfully sent , for signalling completion to the LL .
*/
void gigaset_skb_sent ( struct bc_state * bcs , struct sk_buff * dskb )
{
struct cardstate * cs = bcs - > cs ;
struct gigaset_capi_ctr * iif = cs - > iif ;
struct gigaset_capi_appl * ap = bcs - > ap ;
2009-10-25 12:29:57 +03:00
unsigned char * req = skb_mac_header ( dskb ) ;
2009-10-06 16:19:17 +04:00
u16 flags ;
/* update statistics */
+ + bcs - > trans_up ;
if ( ! ap ) {
2010-07-05 18:19:14 +04:00
gig_dbg ( DEBUG_MCMD , " %s: application gone " , __func__ ) ;
2009-10-06 16:19:17 +04:00
return ;
}
/* don't send further B3 messages if disconnected */
2010-06-21 17:55:20 +04:00
if ( bcs - > apconnstate < APCONN_ACTIVE ) {
2010-07-05 18:19:14 +04:00
gig_dbg ( DEBUG_MCMD , " %s: disconnected " , __func__ ) ;
2009-10-06 16:19:17 +04:00
return ;
}
2010-06-21 17:54:50 +04:00
/*
* send DATA_B3_CONF if " delivery confirmation " bit was set in request ;
* otherwise it has already been sent by do_data_b3_req ( )
*/
2009-10-25 12:29:57 +03:00
flags = CAPIMSG_FLAGS ( req ) ;
2010-06-21 17:54:50 +04:00
if ( flags & CAPI_FLAGS_DELIVERY_CONFIRMATION )
send_data_b3_conf ( cs , & iif - > ctr , ap - > id , CAPIMSG_MSGID ( req ) ,
bcs - > channel + 1 , CAPIMSG_HANDLE_REQ ( req ) ,
( flags & ~ CAPI_FLAGS_DELIVERY_CONFIRMATION ) ?
CapiFlagsNotSupportedByProtocol :
CAPI_NOERROR ) ;
2009-10-06 16:19:17 +04:00
}
EXPORT_SYMBOL_GPL ( gigaset_skb_sent ) ;
/**
* gigaset_skb_rcvd ( ) - pass received skb to LL
* @ bcs : B channel descriptor structure .
* @ skb : received data .
*
* Called by hardware module { bas , ser , usb } _gigaset when user data has
* been successfully received , for passing to the LL .
* Warning : skb must not be accessed anymore !
*/
void gigaset_skb_rcvd ( struct bc_state * bcs , struct sk_buff * skb )
{
struct cardstate * cs = bcs - > cs ;
struct gigaset_capi_ctr * iif = cs - > iif ;
struct gigaset_capi_appl * ap = bcs - > ap ;
int len = skb - > len ;
/* update statistics */
bcs - > trans_down + + ;
if ( ! ap ) {
2010-07-05 18:19:14 +04:00
gig_dbg ( DEBUG_MCMD , " %s: application gone " , __func__ ) ;
dev_kfree_skb_any ( skb ) ;
2009-10-06 16:19:17 +04:00
return ;
}
/* don't send further B3 messages if disconnected */
2010-06-21 17:55:20 +04:00
if ( bcs - > apconnstate < APCONN_ACTIVE ) {
2010-07-05 18:19:14 +04:00
gig_dbg ( DEBUG_MCMD , " %s: disconnected " , __func__ ) ;
2009-10-25 12:29:57 +03:00
dev_kfree_skb_any ( skb ) ;
2009-10-06 16:19:17 +04:00
return ;
}
/*
* prepend DATA_B3_IND message to payload
* Parameters : NCCI = 1 , all others 0 / unused
* frequent message , avoid _cmsg overhead
*/
skb_push ( skb , CAPI_DATA_B3_REQ_LEN ) ;
CAPIMSG_SETLEN ( skb - > data , CAPI_DATA_B3_REQ_LEN ) ;
CAPIMSG_SETAPPID ( skb - > data , ap - > id ) ;
CAPIMSG_SETCOMMAND ( skb - > data , CAPI_DATA_B3 ) ;
CAPIMSG_SETSUBCOMMAND ( skb - > data , CAPI_IND ) ;
CAPIMSG_SETMSGID ( skb - > data , ap - > nextMessageNumber + + ) ;
CAPIMSG_SETCONTROLLER ( skb - > data , iif - > ctr . cnr ) ;
CAPIMSG_SETPLCI_PART ( skb - > data , bcs - > channel + 1 ) ;
CAPIMSG_SETNCCI_PART ( skb - > data , 1 ) ;
/* Data parameter not used */
CAPIMSG_SETDATALEN ( skb - > data , len ) ;
/* Data handle parameter not used */
CAPIMSG_SETFLAGS ( skb - > data , 0 ) ;
/* Data64 parameter not present */
/* emit message */
2010-07-05 18:18:59 +04:00
dump_rawmsg ( DEBUG_MCMD , __func__ , skb - > data ) ;
2009-10-06 16:19:17 +04:00
capi_ctr_handle_message ( & iif - > ctr , ap - > id , skb ) ;
}
EXPORT_SYMBOL_GPL ( gigaset_skb_rcvd ) ;
/**
* gigaset_isdn_rcv_err ( ) - signal receive error
* @ bcs : B channel descriptor structure .
*
* Called by hardware module { bas , ser , usb } _gigaset when a receive error
* has occurred , for signalling to the LL .
*/
void gigaset_isdn_rcv_err ( struct bc_state * bcs )
{
/* if currently ignoring packets, just count down */
if ( bcs - > ignore ) {
bcs - > ignore - - ;
return ;
}
/* update statistics */
bcs - > corrupted + + ;
/* ToDo: signal error -> LL */
}
EXPORT_SYMBOL_GPL ( gigaset_isdn_rcv_err ) ;
/**
* gigaset_isdn_icall ( ) - signal incoming call
* @ at_state : connection state structure .
*
* Called by main module at tasklet level to notify the LL that an incoming
* call has been received . @ at_state contains the parameters of the call .
*
* Return value : call disposition ( ICALL_ * )
*/
int gigaset_isdn_icall ( struct at_state_t * at_state )
{
struct cardstate * cs = at_state - > cs ;
struct bc_state * bcs = at_state - > bcs ;
struct gigaset_capi_ctr * iif = cs - > iif ;
struct gigaset_capi_appl * ap ;
u32 actCIPmask ;
struct sk_buff * skb ;
unsigned int msgsize ;
2010-06-21 17:55:20 +04:00
unsigned long flags ;
2009-10-06 16:19:17 +04:00
int i ;
/*
* ToDo : signal calls without a free B channel , too
* ( requires a u8 handle for the at_state structure that can
* be stored in the PLCI and used in the CONNECT_RESP message
* handler to retrieve it )
*/
if ( ! bcs )
return ICALL_IGNORE ;
/* prepare CONNECT_IND message, using B channel number as PLCI */
capi_cmsg_header ( & iif - > hcmsg , 0 , CAPI_CONNECT , CAPI_IND , 0 ,
iif - > ctr . cnr | ( ( bcs - > channel + 1 ) < < 8 ) ) ;
/* minimum size, all structs empty */
msgsize = CAPI_CONNECT_IND_BASELEN ;
/* Bearer Capability (mandatory) */
if ( at_state - > str_var [ STR_ZBC ] ) {
/* pass on BC from Gigaset */
if ( encode_ie ( at_state - > str_var [ STR_ZBC ] , iif - > bc_buf ,
MAX_BC_OCTETS ) < 0 ) {
dev_warn ( cs - > dev , " RING ignored - bad BC %s \n " ,
at_state - > str_var [ STR_ZBC ] ) ;
return ICALL_IGNORE ;
}
/* look up corresponding CIP value */
iif - > hcmsg . CIPValue = 0 ; /* default if nothing found */
for ( i = 0 ; i < ARRAY_SIZE ( cip2bchlc ) ; i + + )
if ( cip2bchlc [ i ] . bc ! = NULL & &
cip2bchlc [ i ] . hlc = = NULL & &
! strcmp ( cip2bchlc [ i ] . bc ,
at_state - > str_var [ STR_ZBC ] ) ) {
iif - > hcmsg . CIPValue = i ;
break ;
}
} else {
/* no BC (internal call): assume CIP 1 (speech, A-law) */
iif - > hcmsg . CIPValue = 1 ;
encode_ie ( cip2bchlc [ 1 ] . bc , iif - > bc_buf , MAX_BC_OCTETS ) ;
}
iif - > hcmsg . BC = iif - > bc_buf ;
msgsize + = iif - > hcmsg . BC [ 0 ] ;
/* High Layer Compatibility (optional) */
if ( at_state - > str_var [ STR_ZHLC ] ) {
/* pass on HLC from Gigaset */
if ( encode_ie ( at_state - > str_var [ STR_ZHLC ] , iif - > hlc_buf ,
MAX_HLC_OCTETS ) < 0 ) {
dev_warn ( cs - > dev , " RING ignored - bad HLC %s \n " ,
at_state - > str_var [ STR_ZHLC ] ) ;
return ICALL_IGNORE ;
}
iif - > hcmsg . HLC = iif - > hlc_buf ;
msgsize + = iif - > hcmsg . HLC [ 0 ] ;
/* look up corresponding CIP value */
/* keep BC based CIP value if none found */
if ( at_state - > str_var [ STR_ZBC ] )
for ( i = 0 ; i < ARRAY_SIZE ( cip2bchlc ) ; i + + )
if ( cip2bchlc [ i ] . hlc ! = NULL & &
! strcmp ( cip2bchlc [ i ] . hlc ,
at_state - > str_var [ STR_ZHLC ] ) & &
! strcmp ( cip2bchlc [ i ] . bc ,
at_state - > str_var [ STR_ZBC ] ) ) {
iif - > hcmsg . CIPValue = i ;
break ;
}
}
/* Called Party Number (optional) */
if ( at_state - > str_var [ STR_ZCPN ] ) {
i = strlen ( at_state - > str_var [ STR_ZCPN ] ) ;
if ( i > MAX_NUMBER_DIGITS ) {
dev_warn ( cs - > dev , " RING ignored - bad number %s \n " ,
at_state - > str_var [ STR_ZBC ] ) ;
return ICALL_IGNORE ;
}
iif - > cdpty_buf [ 0 ] = i + 1 ;
iif - > cdpty_buf [ 1 ] = 0x80 ; /* type / numbering plan unknown */
memcpy ( iif - > cdpty_buf + 2 , at_state - > str_var [ STR_ZCPN ] , i ) ;
iif - > hcmsg . CalledPartyNumber = iif - > cdpty_buf ;
msgsize + = iif - > hcmsg . CalledPartyNumber [ 0 ] ;
}
/* Calling Party Number (optional) */
if ( at_state - > str_var [ STR_NMBR ] ) {
i = strlen ( at_state - > str_var [ STR_NMBR ] ) ;
if ( i > MAX_NUMBER_DIGITS ) {
dev_warn ( cs - > dev , " RING ignored - bad number %s \n " ,
at_state - > str_var [ STR_ZBC ] ) ;
return ICALL_IGNORE ;
}
iif - > cgpty_buf [ 0 ] = i + 2 ;
iif - > cgpty_buf [ 1 ] = 0x00 ; /* type / numbering plan unknown */
iif - > cgpty_buf [ 2 ] = 0x80 ; /* pres. allowed, not screened */
memcpy ( iif - > cgpty_buf + 3 , at_state - > str_var [ STR_NMBR ] , i ) ;
iif - > hcmsg . CallingPartyNumber = iif - > cgpty_buf ;
msgsize + = iif - > hcmsg . CallingPartyNumber [ 0 ] ;
}
/* remaining parameters (not supported, always left NULL):
* - CalledPartySubaddress
* - CallingPartySubaddress
* - AdditionalInfo
* - BChannelinformation
* - Keypadfacility
* - Useruserdata
* - Facilitydataarray
*/
gig_dbg ( DEBUG_CMD , " icall: PLCI %x CIP %d BC %s " ,
iif - > hcmsg . adr . adrPLCI , iif - > hcmsg . CIPValue ,
format_ie ( iif - > hcmsg . BC ) ) ;
gig_dbg ( DEBUG_CMD , " icall: HLC %s " ,
format_ie ( iif - > hcmsg . HLC ) ) ;
gig_dbg ( DEBUG_CMD , " icall: CgPty %s " ,
format_ie ( iif - > hcmsg . CallingPartyNumber ) ) ;
gig_dbg ( DEBUG_CMD , " icall: CdPty %s " ,
format_ie ( iif - > hcmsg . CalledPartyNumber ) ) ;
/* scan application list for matching listeners */
2010-06-21 17:55:20 +04:00
spin_lock_irqsave ( & bcs - > aplock , flags ) ;
if ( bcs - > ap ! = NULL | | bcs - > apconnstate ! = APCONN_NONE ) {
dev_warn ( cs - > dev , " %s: channel not properly cleared (%p/%d) \n " ,
__func__ , bcs - > ap , bcs - > apconnstate ) ;
bcs - > ap = NULL ;
bcs - > apconnstate = APCONN_NONE ;
}
spin_unlock_irqrestore ( & bcs - > aplock , flags ) ;
2009-10-06 16:19:17 +04:00
actCIPmask = 1 | ( 1 < < iif - > hcmsg . CIPValue ) ;
list_for_each_entry ( ap , & iif - > appls , ctrlist )
if ( actCIPmask & ap - > listenCIPmask ) {
/* build CONNECT_IND message for this application */
iif - > hcmsg . ApplId = ap - > id ;
iif - > hcmsg . Messagenumber = ap - > nextMessageNumber + + ;
skb = alloc_skb ( msgsize , GFP_ATOMIC ) ;
if ( ! skb ) {
dev_err ( cs - > dev , " %s: out of memory \n " ,
__func__ ) ;
break ;
}
capi_cmsg2message ( & iif - > hcmsg , __skb_put ( skb , msgsize ) ) ;
dump_cmsg ( DEBUG_CMD , __func__ , & iif - > hcmsg ) ;
/* add to listeners on this B channel, update state */
2010-06-21 17:55:20 +04:00
spin_lock_irqsave ( & bcs - > aplock , flags ) ;
2009-10-06 16:19:17 +04:00
ap - > bcnext = bcs - > ap ;
bcs - > ap = ap ;
bcs - > chstate | = CHS_NOTIFY_LL ;
2010-06-21 17:55:20 +04:00
bcs - > apconnstate = APCONN_SETUP ;
spin_unlock_irqrestore ( & bcs - > aplock , flags ) ;
2009-10-06 16:19:17 +04:00
/* emit message */
capi_ctr_handle_message ( & iif - > ctr , ap - > id , skb ) ;
}
/*
* Return " accept " if any listeners .
* Gigaset will send ALERTING .
* There doesn ' t seem to be a way to avoid this .
*/
return bcs - > ap ? ICALL_ACCEPT : ICALL_IGNORE ;
}
/*
* send a DISCONNECT_IND message to an application
* does not sleep , clobbers the controller ' s hcmsg structure
*/
static void send_disconnect_ind ( struct bc_state * bcs ,
struct gigaset_capi_appl * ap , u16 reason )
{
struct cardstate * cs = bcs - > cs ;
struct gigaset_capi_ctr * iif = cs - > iif ;
struct sk_buff * skb ;
2010-06-21 17:55:20 +04:00
if ( bcs - > apconnstate = = APCONN_NONE )
2009-10-06 16:19:17 +04:00
return ;
capi_cmsg_header ( & iif - > hcmsg , ap - > id , CAPI_DISCONNECT , CAPI_IND ,
ap - > nextMessageNumber + + ,
iif - > ctr . cnr | ( ( bcs - > channel + 1 ) < < 8 ) ) ;
iif - > hcmsg . Reason = reason ;
skb = alloc_skb ( CAPI_DISCONNECT_IND_LEN , GFP_ATOMIC ) ;
if ( ! skb ) {
dev_err ( cs - > dev , " %s: out of memory \n " , __func__ ) ;
return ;
}
capi_cmsg2message ( & iif - > hcmsg , __skb_put ( skb , CAPI_DISCONNECT_IND_LEN ) ) ;
dump_cmsg ( DEBUG_CMD , __func__ , & iif - > hcmsg ) ;
capi_ctr_handle_message ( & iif - > ctr , ap - > id , skb ) ;
}
/*
* send a DISCONNECT_B3_IND message to an application
* Parameters : NCCI = 1 , NCPI empty , Reason_B3 = 0
* does not sleep , clobbers the controller ' s hcmsg structure
*/
static void send_disconnect_b3_ind ( struct bc_state * bcs ,
struct gigaset_capi_appl * ap )
{
struct cardstate * cs = bcs - > cs ;
struct gigaset_capi_ctr * iif = cs - > iif ;
struct sk_buff * skb ;
/* nothing to do if no logical connection active */
2010-06-21 17:55:20 +04:00
if ( bcs - > apconnstate < APCONN_ACTIVE )
2009-10-06 16:19:17 +04:00
return ;
2010-06-21 17:55:20 +04:00
bcs - > apconnstate = APCONN_SETUP ;
2009-10-06 16:19:17 +04:00
capi_cmsg_header ( & iif - > hcmsg , ap - > id , CAPI_DISCONNECT_B3 , CAPI_IND ,
ap - > nextMessageNumber + + ,
iif - > ctr . cnr | ( ( bcs - > channel + 1 ) < < 8 ) | ( 1 < < 16 ) ) ;
skb = alloc_skb ( CAPI_DISCONNECT_B3_IND_BASELEN , GFP_ATOMIC ) ;
if ( ! skb ) {
dev_err ( cs - > dev , " %s: out of memory \n " , __func__ ) ;
return ;
}
capi_cmsg2message ( & iif - > hcmsg ,
__skb_put ( skb , CAPI_DISCONNECT_B3_IND_BASELEN ) ) ;
dump_cmsg ( DEBUG_CMD , __func__ , & iif - > hcmsg ) ;
capi_ctr_handle_message ( & iif - > ctr , ap - > id , skb ) ;
}
/**
* gigaset_isdn_connD ( ) - signal D channel connect
* @ bcs : B channel descriptor structure .
*
* Called by main module at tasklet level to notify the LL that the D channel
* connection has been established .
*/
void gigaset_isdn_connD ( struct bc_state * bcs )
{
struct cardstate * cs = bcs - > cs ;
struct gigaset_capi_ctr * iif = cs - > iif ;
2010-06-21 17:55:20 +04:00
struct gigaset_capi_appl * ap ;
2009-10-06 16:19:17 +04:00
struct sk_buff * skb ;
unsigned int msgsize ;
2010-06-21 17:55:20 +04:00
unsigned long flags ;
2009-10-06 16:19:17 +04:00
2010-06-21 17:55:20 +04:00
spin_lock_irqsave ( & bcs - > aplock , flags ) ;
ap = bcs - > ap ;
2009-10-06 16:19:17 +04:00
if ( ! ap ) {
2010-06-21 17:55:20 +04:00
spin_unlock_irqrestore ( & bcs - > aplock , flags ) ;
2010-07-05 18:19:14 +04:00
gig_dbg ( DEBUG_CMD , " %s: application gone " , __func__ ) ;
2009-10-06 16:19:17 +04:00
return ;
}
2010-06-21 17:55:20 +04:00
if ( bcs - > apconnstate = = APCONN_NONE ) {
spin_unlock_irqrestore ( & bcs - > aplock , flags ) ;
dev_warn ( cs - > dev , " %s: application %u not connected \n " ,
__func__ , ap - > id ) ;
return ;
}
spin_unlock_irqrestore ( & bcs - > aplock , flags ) ;
2009-10-06 16:19:17 +04:00
while ( ap - > bcnext ) {
/* this should never happen */
dev_warn ( cs - > dev , " %s: dropping extra application %u \n " ,
__func__ , ap - > bcnext - > id ) ;
send_disconnect_ind ( bcs , ap - > bcnext ,
CapiCallGivenToOtherApplication ) ;
ap - > bcnext = ap - > bcnext - > bcnext ;
}
/* prepare CONNECT_ACTIVE_IND message
* Note : LLC not supported by device
*/
capi_cmsg_header ( & iif - > hcmsg , ap - > id , CAPI_CONNECT_ACTIVE , CAPI_IND ,
ap - > nextMessageNumber + + ,
iif - > ctr . cnr | ( ( bcs - > channel + 1 ) < < 8 ) ) ;
/* minimum size, all structs empty */
msgsize = CAPI_CONNECT_ACTIVE_IND_BASELEN ;
/* ToDo: set parameter: Connected number
* ( requires ev - layer state machine extension to collect
* ZCON device reply )
*/
/* build and emit CONNECT_ACTIVE_IND message */
skb = alloc_skb ( msgsize , GFP_ATOMIC ) ;
if ( ! skb ) {
dev_err ( cs - > dev , " %s: out of memory \n " , __func__ ) ;
return ;
}
capi_cmsg2message ( & iif - > hcmsg , __skb_put ( skb , msgsize ) ) ;
dump_cmsg ( DEBUG_CMD , __func__ , & iif - > hcmsg ) ;
capi_ctr_handle_message ( & iif - > ctr , ap - > id , skb ) ;
}
/**
* gigaset_isdn_hupD ( ) - signal D channel hangup
* @ bcs : B channel descriptor structure .
*
* Called by main module at tasklet level to notify the LL that the D channel
* connection has been shut down .
*/
void gigaset_isdn_hupD ( struct bc_state * bcs )
{
struct gigaset_capi_appl * ap ;
2010-06-21 17:55:20 +04:00
unsigned long flags ;
2009-10-06 16:19:17 +04:00
/*
* ToDo : pass on reason code reported by device
* ( requires ev - layer state machine extension to collect
* ZCAU device reply )
*/
2010-06-21 17:55:20 +04:00
spin_lock_irqsave ( & bcs - > aplock , flags ) ;
while ( bcs - > ap ! = NULL ) {
ap = bcs - > ap ;
bcs - > ap = ap - > bcnext ;
spin_unlock_irqrestore ( & bcs - > aplock , flags ) ;
2009-10-06 16:19:17 +04:00
send_disconnect_b3_ind ( bcs , ap ) ;
send_disconnect_ind ( bcs , ap , 0 ) ;
2010-06-21 17:55:20 +04:00
spin_lock_irqsave ( & bcs - > aplock , flags ) ;
2009-10-06 16:19:17 +04:00
}
2010-06-21 17:55:20 +04:00
bcs - > apconnstate = APCONN_NONE ;
spin_unlock_irqrestore ( & bcs - > aplock , flags ) ;
2009-10-06 16:19:17 +04:00
}
/**
* gigaset_isdn_connB ( ) - signal B channel connect
* @ bcs : B channel descriptor structure .
*
* Called by main module at tasklet level to notify the LL that the B channel
* connection has been established .
*/
void gigaset_isdn_connB ( struct bc_state * bcs )
{
struct cardstate * cs = bcs - > cs ;
struct gigaset_capi_ctr * iif = cs - > iif ;
2010-06-21 17:55:20 +04:00
struct gigaset_capi_appl * ap ;
2009-10-06 16:19:17 +04:00
struct sk_buff * skb ;
2010-06-21 17:55:20 +04:00
unsigned long flags ;
2009-10-06 16:19:17 +04:00
unsigned int msgsize ;
u8 command ;
2010-06-21 17:55:20 +04:00
spin_lock_irqsave ( & bcs - > aplock , flags ) ;
ap = bcs - > ap ;
2009-10-06 16:19:17 +04:00
if ( ! ap ) {
2010-06-21 17:55:20 +04:00
spin_unlock_irqrestore ( & bcs - > aplock , flags ) ;
2010-07-05 18:19:14 +04:00
gig_dbg ( DEBUG_CMD , " %s: application gone " , __func__ ) ;
2009-10-06 16:19:17 +04:00
return ;
}
2010-06-21 17:55:20 +04:00
if ( ! bcs - > apconnstate ) {
spin_unlock_irqrestore ( & bcs - > aplock , flags ) ;
2009-10-06 16:19:17 +04:00
dev_warn ( cs - > dev , " %s: application %u not connected \n " ,
__func__ , ap - > id ) ;
return ;
}
/*
* emit CONNECT_B3_ACTIVE_IND if we already got CONNECT_B3_REQ ;
* otherwise we have to emit CONNECT_B3_IND first , and follow up with
* CONNECT_B3_ACTIVE_IND in reply to CONNECT_B3_RESP
* Parameters in both cases always : NCCI = 1 , NCPI empty
*/
2010-06-21 17:55:20 +04:00
if ( bcs - > apconnstate > = APCONN_ACTIVE ) {
2009-10-06 16:19:17 +04:00
command = CAPI_CONNECT_B3_ACTIVE ;
msgsize = CAPI_CONNECT_B3_ACTIVE_IND_BASELEN ;
} else {
command = CAPI_CONNECT_B3 ;
msgsize = CAPI_CONNECT_B3_IND_BASELEN ;
}
2010-06-21 17:55:20 +04:00
bcs - > apconnstate = APCONN_ACTIVE ;
spin_unlock_irqrestore ( & bcs - > aplock , flags ) ;
while ( ap - > bcnext ) {
/* this should never happen */
dev_warn ( cs - > dev , " %s: dropping extra application %u \n " ,
__func__ , ap - > bcnext - > id ) ;
send_disconnect_ind ( bcs , ap - > bcnext ,
CapiCallGivenToOtherApplication ) ;
ap - > bcnext = ap - > bcnext - > bcnext ;
}
2009-10-06 16:19:17 +04:00
capi_cmsg_header ( & iif - > hcmsg , ap - > id , command , CAPI_IND ,
ap - > nextMessageNumber + + ,
iif - > ctr . cnr | ( ( bcs - > channel + 1 ) < < 8 ) | ( 1 < < 16 ) ) ;
skb = alloc_skb ( msgsize , GFP_ATOMIC ) ;
if ( ! skb ) {
dev_err ( cs - > dev , " %s: out of memory \n " , __func__ ) ;
return ;
}
capi_cmsg2message ( & iif - > hcmsg , __skb_put ( skb , msgsize ) ) ;
dump_cmsg ( DEBUG_CMD , __func__ , & iif - > hcmsg ) ;
capi_ctr_handle_message ( & iif - > ctr , ap - > id , skb ) ;
}
/**
* gigaset_isdn_hupB ( ) - signal B channel hangup
* @ bcs : B channel descriptor structure .
*
* Called by main module to notify the LL that the B channel connection has
* been shut down .
*/
void gigaset_isdn_hupB ( struct bc_state * bcs )
{
struct gigaset_capi_appl * ap = bcs - > ap ;
/* ToDo: assure order of DISCONNECT_B3_IND and DISCONNECT_IND ? */
if ( ! ap ) {
2010-07-05 18:19:14 +04:00
gig_dbg ( DEBUG_CMD , " %s: application gone " , __func__ ) ;
2009-10-06 16:19:17 +04:00
return ;
}
send_disconnect_b3_ind ( bcs , ap ) ;
}
/**
* gigaset_isdn_start ( ) - signal device availability
* @ cs : device descriptor structure .
*
* Called by main module to notify the LL that the device is available for
* use .
*/
void gigaset_isdn_start ( struct cardstate * cs )
{
struct gigaset_capi_ctr * iif = cs - > iif ;
/* fill profile data: manufacturer name */
strcpy ( iif - > ctr . manu , " Siemens " ) ;
/* CAPI and device version */
iif - > ctr . version . majorversion = 2 ; /* CAPI 2.0 */
iif - > ctr . version . minorversion = 0 ;
/* ToDo: check/assert cs->gotfwver? */
iif - > ctr . version . majormanuversion = cs - > fwver [ 0 ] ;
iif - > ctr . version . minormanuversion = cs - > fwver [ 1 ] ;
/* number of B channels supported */
iif - > ctr . profile . nbchannel = cs - > channels ;
/* global options: internal controller, supplementary services */
iif - > ctr . profile . goptions = 0x11 ;
/* B1 protocols: 64 kbit/s HDLC or transparent */
iif - > ctr . profile . support1 = 0x03 ;
/* B2 protocols: transparent only */
/* ToDo: X.75 SLP ? */
iif - > ctr . profile . support2 = 0x02 ;
/* B3 protocols: transparent only */
iif - > ctr . profile . support3 = 0x01 ;
/* no serial number */
strcpy ( iif - > ctr . serial , " 0 " ) ;
capi_ctr_ready ( & iif - > ctr ) ;
}
/**
* gigaset_isdn_stop ( ) - signal device unavailability
* @ cs : device descriptor structure .
*
* Called by main module to notify the LL that the device is no longer
* available for use .
*/
void gigaset_isdn_stop ( struct cardstate * cs )
{
struct gigaset_capi_ctr * iif = cs - > iif ;
capi_ctr_down ( & iif - > ctr ) ;
}
/*
* kernel CAPI callback methods
* = = = = = = = = = = = = = = = = = = = = = = = = = = = =
*/
/*
* register CAPI application
*/
static void gigaset_register_appl ( struct capi_ctr * ctr , u16 appl ,
capi_register_params * rp )
{
struct gigaset_capi_ctr * iif
= container_of ( ctr , struct gigaset_capi_ctr , ctr ) ;
struct cardstate * cs = ctr - > driverdata ;
struct gigaset_capi_appl * ap ;
2010-07-05 18:18:59 +04:00
gig_dbg ( DEBUG_CMD , " %s [%u] l3cnt=%u blkcnt=%u blklen=%u " ,
__func__ , appl , rp - > level3cnt , rp - > datablkcnt , rp - > datablklen ) ;
2009-10-06 16:19:17 +04:00
list_for_each_entry ( ap , & iif - > appls , ctrlist )
if ( ap - > id = = appl ) {
dev_notice ( cs - > dev ,
" application %u already registered \n " , appl ) ;
return ;
}
ap = kzalloc ( sizeof ( * ap ) , GFP_KERNEL ) ;
if ( ! ap ) {
dev_err ( cs - > dev , " %s: out of memory \n " , __func__ ) ;
return ;
}
ap - > id = appl ;
2010-06-21 17:54:19 +04:00
ap - > rp = * rp ;
2009-10-06 16:19:17 +04:00
list_add ( & ap - > ctrlist , & iif - > appls ) ;
2010-06-21 17:55:20 +04:00
dev_info ( cs - > dev , " application %u registered \n " , ap - > id ) ;
}
/*
* remove CAPI application from channel
* helper function to keep indentation levels down and stay in 80 columns
*/
static inline void remove_appl_from_channel ( struct bc_state * bcs ,
struct gigaset_capi_appl * ap )
{
struct cardstate * cs = bcs - > cs ;
struct gigaset_capi_appl * bcap ;
unsigned long flags ;
int prevconnstate ;
spin_lock_irqsave ( & bcs - > aplock , flags ) ;
bcap = bcs - > ap ;
if ( bcap = = NULL ) {
spin_unlock_irqrestore ( & bcs - > aplock , flags ) ;
return ;
}
/* check first application on channel */
if ( bcap = = ap ) {
bcs - > ap = ap - > bcnext ;
if ( bcs - > ap ! = NULL ) {
spin_unlock_irqrestore ( & bcs - > aplock , flags ) ;
return ;
}
/* none left, clear channel state */
prevconnstate = bcs - > apconnstate ;
bcs - > apconnstate = APCONN_NONE ;
spin_unlock_irqrestore ( & bcs - > aplock , flags ) ;
if ( prevconnstate = = APCONN_ACTIVE ) {
dev_notice ( cs - > dev , " %s: hanging up channel %u \n " ,
__func__ , bcs - > channel ) ;
gigaset_add_event ( cs , & bcs - > at_state ,
EV_HUP , NULL , 0 , NULL ) ;
gigaset_schedule_event ( cs ) ;
}
return ;
}
/* check remaining list */
do {
if ( bcap - > bcnext = = ap ) {
bcap - > bcnext = bcap - > bcnext - > bcnext ;
2010-08-06 02:23:23 +04:00
spin_unlock_irqrestore ( & bcs - > aplock , flags ) ;
2010-06-21 17:55:20 +04:00
return ;
}
bcap = bcap - > bcnext ;
} while ( bcap ! = NULL ) ;
spin_unlock_irqrestore ( & bcs - > aplock , flags ) ;
2009-10-06 16:19:17 +04:00
}
/*
* release CAPI application
*/
static void gigaset_release_appl ( struct capi_ctr * ctr , u16 appl )
{
struct gigaset_capi_ctr * iif
= container_of ( ctr , struct gigaset_capi_ctr , ctr ) ;
struct cardstate * cs = iif - > ctr . driverdata ;
struct gigaset_capi_appl * ap , * tmp ;
2010-06-21 17:55:20 +04:00
unsigned ch ;
2009-10-06 16:19:17 +04:00
2010-07-05 18:18:59 +04:00
gig_dbg ( DEBUG_CMD , " %s [%u] " , __func__ , appl ) ;
2009-10-06 16:19:17 +04:00
list_for_each_entry_safe ( ap , tmp , & iif - > appls , ctrlist )
if ( ap - > id = = appl ) {
2010-06-21 17:55:20 +04:00
/* remove from any channels */
for ( ch = 0 ; ch < cs - > channels ; ch + + )
remove_appl_from_channel ( & cs - > bcs [ ch ] , ap ) ;
/* remove from registration list */
2009-10-06 16:19:17 +04:00
list_del ( & ap - > ctrlist ) ;
kfree ( ap ) ;
2010-06-21 17:55:20 +04:00
dev_info ( cs - > dev , " application %u released \n " , appl ) ;
2009-10-06 16:19:17 +04:00
}
}
/*
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
* outgoing CAPI message handler
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
*/
/*
* helper function : emit reply message with given Info value
*/
static void send_conf ( struct gigaset_capi_ctr * iif ,
struct gigaset_capi_appl * ap ,
struct sk_buff * skb ,
u16 info )
{
/*
* _CONF replies always only have NCCI and Info parameters
* so they ' ll fit into the _REQ message skb
*/
capi_cmsg_answer ( & iif - > acmsg ) ;
iif - > acmsg . Info = info ;
capi_cmsg2message ( & iif - > acmsg , skb - > data ) ;
__skb_trim ( skb , CAPI_STDCONF_LEN ) ;
dump_cmsg ( DEBUG_CMD , __func__ , & iif - > acmsg ) ;
capi_ctr_handle_message ( & iif - > ctr , ap - > id , skb ) ;
}
/*
* process FACILITY_REQ message
*/
static void do_facility_req ( struct gigaset_capi_ctr * iif ,
struct gigaset_capi_appl * ap ,
struct sk_buff * skb )
{
struct cardstate * cs = iif - > ctr . driverdata ;
2009-10-25 12:29:37 +03:00
_cmsg * cmsg = & iif - > acmsg ;
2009-10-06 16:19:17 +04:00
struct sk_buff * cskb ;
u8 * pparam ;
unsigned int msgsize = CAPI_FACILITY_CONF_BASELEN ;
u16 function , info ;
static u8 confparam [ 10 ] ; /* max. 9 octets + length byte */
/* decode message */
2009-10-25 12:29:37 +03:00
capi_message2cmsg ( cmsg , skb - > data ) ;
dump_cmsg ( DEBUG_CMD , __func__ , cmsg ) ;
2009-10-06 16:19:17 +04:00
/*
* Facility Request Parameter is not decoded by capi_message2cmsg ( )
* encoding depends on Facility Selector
*/
2009-10-25 12:29:37 +03:00
switch ( cmsg - > FacilitySelector ) {
2009-10-06 16:19:17 +04:00
case CAPI_FACILITY_DTMF : /* ToDo */
info = CapiFacilityNotSupported ;
confparam [ 0 ] = 2 ; /* length */
/* DTMF information: Unknown DTMF request */
capimsg_setu16 ( confparam , 1 , 2 ) ;
break ;
case CAPI_FACILITY_V42BIS : /* not supported */
info = CapiFacilityNotSupported ;
confparam [ 0 ] = 2 ; /* length */
/* V.42 bis information: not available */
capimsg_setu16 ( confparam , 1 , 1 ) ;
break ;
case CAPI_FACILITY_SUPPSVC :
/* decode Function parameter */
2009-10-25 12:29:37 +03:00
pparam = cmsg - > FacilityRequestParameter ;
2010-07-05 18:19:04 +04:00
if ( pparam = = NULL | | pparam [ 0 ] < 2 ) {
2009-10-06 16:19:17 +04:00
dev_notice ( cs - > dev , " %s: %s missing \n " , " FACILITY_REQ " ,
" Facility Request Parameter " ) ;
send_conf ( iif , ap , skb , CapiIllMessageParmCoding ) ;
return ;
}
function = CAPIMSG_U16 ( pparam , 1 ) ;
switch ( function ) {
case CAPI_SUPPSVC_GETSUPPORTED :
info = CapiSuccess ;
/* Supplementary Service specific parameter */
confparam [ 3 ] = 6 ; /* length */
/* Supplementary services info: Success */
capimsg_setu16 ( confparam , 4 , CapiSuccess ) ;
/* Supported Services: none */
capimsg_setu32 ( confparam , 6 , 0 ) ;
break ;
2010-07-05 18:19:04 +04:00
case CAPI_SUPPSVC_LISTEN :
if ( pparam [ 0 ] < 7 | | pparam [ 3 ] < 4 ) {
dev_notice ( cs - > dev , " %s: %s missing \n " ,
" FACILITY_REQ " , " Notification Mask " ) ;
send_conf ( iif , ap , skb ,
CapiIllMessageParmCoding ) ;
return ;
}
if ( CAPIMSG_U32 ( pparam , 4 ) ! = 0 ) {
dev_notice ( cs - > dev ,
" %s: unsupported supplementary service notification mask 0x%x \n " ,
" FACILITY_REQ " , CAPIMSG_U32 ( pparam , 4 ) ) ;
info = CapiFacilitySpecificFunctionNotSupported ;
confparam [ 3 ] = 2 ; /* length */
capimsg_setu16 ( confparam , 4 ,
CapiSupplementaryServiceNotSupported ) ;
}
info = CapiSuccess ;
confparam [ 3 ] = 2 ; /* length */
capimsg_setu16 ( confparam , 4 , CapiSuccess ) ;
break ;
2009-10-06 16:19:17 +04:00
/* ToDo: add supported services */
default :
2010-07-05 18:19:04 +04:00
dev_notice ( cs - > dev ,
" %s: unsupported supplementary service function 0x%04x \n " ,
" FACILITY_REQ " , function ) ;
2009-10-06 16:19:17 +04:00
info = CapiFacilitySpecificFunctionNotSupported ;
/* Supplementary Service specific parameter */
confparam [ 3 ] = 2 ; /* length */
/* Supplementary services info: not supported */
capimsg_setu16 ( confparam , 4 ,
CapiSupplementaryServiceNotSupported ) ;
}
/* Facility confirmation parameter */
confparam [ 0 ] = confparam [ 3 ] + 3 ; /* total length */
/* Function: copy from _REQ message */
capimsg_setu16 ( confparam , 1 , function ) ;
/* Supplementary Service specific parameter already set above */
break ;
case CAPI_FACILITY_WAKEUP : /* ToDo */
info = CapiFacilityNotSupported ;
confparam [ 0 ] = 2 ; /* length */
/* Number of accepted awake request parameters: 0 */
capimsg_setu16 ( confparam , 1 , 0 ) ;
break ;
default :
info = CapiFacilityNotSupported ;
confparam [ 0 ] = 0 ; /* empty struct */
}
/* send FACILITY_CONF with given Info and confirmation parameter */
2009-10-25 12:29:37 +03:00
capi_cmsg_answer ( cmsg ) ;
cmsg - > Info = info ;
cmsg - > FacilityConfirmationParameter = confparam ;
2009-10-06 16:19:17 +04:00
msgsize + = confparam [ 0 ] ; /* length */
cskb = alloc_skb ( msgsize , GFP_ATOMIC ) ;
if ( ! cskb ) {
dev_err ( cs - > dev , " %s: out of memory \n " , __func__ ) ;
return ;
}
2009-10-25 12:29:37 +03:00
capi_cmsg2message ( cmsg , __skb_put ( cskb , msgsize ) ) ;
dump_cmsg ( DEBUG_CMD , __func__ , cmsg ) ;
capi_ctr_handle_message ( & iif - > ctr , ap - > id , cskb ) ;
2009-10-06 16:19:17 +04:00
}
/*
* process LISTEN_REQ message
* just store the masks in the application data structure
*/
static void do_listen_req ( struct gigaset_capi_ctr * iif ,
struct gigaset_capi_appl * ap ,
struct sk_buff * skb )
{
/* decode message */
capi_message2cmsg ( & iif - > acmsg , skb - > data ) ;
dump_cmsg ( DEBUG_CMD , __func__ , & iif - > acmsg ) ;
/* store listening parameters */
ap - > listenInfoMask = iif - > acmsg . InfoMask ;
ap - > listenCIPmask = iif - > acmsg . CIPmask ;
send_conf ( iif , ap , skb , CapiSuccess ) ;
}
/*
* process ALERT_REQ message
* nothing to do , Gigaset always alerts anyway
*/
static void do_alert_req ( struct gigaset_capi_ctr * iif ,
struct gigaset_capi_appl * ap ,
struct sk_buff * skb )
{
/* decode message */
capi_message2cmsg ( & iif - > acmsg , skb - > data ) ;
dump_cmsg ( DEBUG_CMD , __func__ , & iif - > acmsg ) ;
send_conf ( iif , ap , skb , CapiAlertAlreadySent ) ;
}
/*
* process CONNECT_REQ message
* allocate a B channel , prepare dial commands , queue a DIAL event ,
* emit CONNECT_CONF reply
*/
static void do_connect_req ( struct gigaset_capi_ctr * iif ,
struct gigaset_capi_appl * ap ,
struct sk_buff * skb )
{
struct cardstate * cs = iif - > ctr . driverdata ;
_cmsg * cmsg = & iif - > acmsg ;
struct bc_state * bcs ;
char * * commands ;
char * s ;
u8 * pp ;
2010-06-21 17:55:20 +04:00
unsigned long flags ;
2010-06-21 17:55:05 +04:00
int i , l , lbc , lhlc ;
2009-10-06 16:19:17 +04:00
u16 info ;
/* decode message */
2009-10-25 12:29:37 +03:00
capi_message2cmsg ( cmsg , skb - > data ) ;
dump_cmsg ( DEBUG_CMD , __func__ , cmsg ) ;
2009-10-06 16:19:17 +04:00
/* get free B channel & construct PLCI */
bcs = gigaset_get_free_channel ( cs ) ;
if ( ! bcs ) {
dev_notice ( cs - > dev , " %s: no B channel available \n " ,
" CONNECT_REQ " ) ;
send_conf ( iif , ap , skb , CapiNoPlciAvailable ) ;
return ;
}
2010-06-21 17:55:20 +04:00
spin_lock_irqsave ( & bcs - > aplock , flags ) ;
if ( bcs - > ap ! = NULL | | bcs - > apconnstate ! = APCONN_NONE )
dev_warn ( cs - > dev , " %s: channel not properly cleared (%p/%d) \n " ,
__func__ , bcs - > ap , bcs - > apconnstate ) ;
2009-10-06 16:19:17 +04:00
ap - > bcnext = NULL ;
bcs - > ap = ap ;
2010-06-21 17:55:20 +04:00
bcs - > apconnstate = APCONN_SETUP ;
spin_unlock_irqrestore ( & bcs - > aplock , flags ) ;
2010-06-21 17:54:19 +04:00
bcs - > rx_bufsize = ap - > rp . datablklen ;
dev_kfree_skb ( bcs - > rx_skb ) ;
gigaset_new_rx_skb ( bcs ) ;
2009-10-06 16:19:17 +04:00
cmsg - > adr . adrPLCI | = ( bcs - > channel + 1 ) < < 8 ;
/* build command table */
commands = kzalloc ( AT_NUM * ( sizeof * commands ) , GFP_KERNEL ) ;
if ( ! commands )
goto oom ;
/* encode parameter: Called party number */
pp = cmsg - > CalledPartyNumber ;
if ( pp = = NULL | | * pp = = 0 ) {
dev_notice ( cs - > dev , " %s: %s missing \n " ,
" CONNECT_REQ " , " Called party number " ) ;
info = CapiIllMessageParmCoding ;
goto error ;
}
l = * pp + + ;
/* check type of number/numbering plan byte */
switch ( * pp ) {
case 0x80 : /* unknown type / unknown numbering plan */
case 0x81 : /* unknown type / ISDN/Telephony numbering plan */
break ;
default : /* others: warn about potential misinterpretation */
dev_notice ( cs - > dev , " %s: %s type/plan 0x%02x unsupported \n " ,
" CONNECT_REQ " , " Called party number " , * pp ) ;
}
pp + + ;
l - - ;
/* translate "**" internal call prefix to CTP value */
if ( l > = 2 & & pp [ 0 ] = = ' * ' & & pp [ 1 ] = = ' * ' ) {
s = " ^SCTP=0 \r " ;
pp + = 2 ;
l - = 2 ;
} else {
s = " ^SCTP=1 \r " ;
}
commands [ AT_TYPE ] = kstrdup ( s , GFP_KERNEL ) ;
if ( ! commands [ AT_TYPE ] )
goto oom ;
commands [ AT_DIAL ] = kmalloc ( l + 3 , GFP_KERNEL ) ;
if ( ! commands [ AT_DIAL ] )
goto oom ;
2009-10-25 12:29:47 +03:00
snprintf ( commands [ AT_DIAL ] , l + 3 , " D%.*s \r " , l , pp ) ;
2009-10-06 16:19:17 +04:00
/* encode parameter: Calling party number */
pp = cmsg - > CallingPartyNumber ;
if ( pp ! = NULL & & * pp > 0 ) {
l = * pp + + ;
/* check type of number/numbering plan byte */
/* ToDo: allow for/handle Ext=1? */
switch ( * pp ) {
case 0x00 : /* unknown type / unknown numbering plan */
case 0x01 : /* unknown type / ISDN/Telephony num. plan */
break ;
default :
dev_notice ( cs - > dev ,
" %s: %s type/plan 0x%02x unsupported \n " ,
" CONNECT_REQ " , " Calling party number " , * pp ) ;
}
pp + + ;
l - - ;
/* check presentation indicator */
if ( ! l ) {
dev_notice ( cs - > dev , " %s: %s IE truncated \n " ,
" CONNECT_REQ " , " Calling party number " ) ;
info = CapiIllMessageParmCoding ;
goto error ;
}
switch ( * pp & 0xfc ) { /* ignore Screening indicator */
case 0x80 : /* Presentation allowed */
s = " ^SCLIP=1 \r " ;
break ;
case 0xa0 : /* Presentation restricted */
s = " ^SCLIP=0 \r " ;
break ;
default :
dev_notice ( cs - > dev , " %s: invalid %s 0x%02x \n " ,
" CONNECT_REQ " ,
" Presentation/Screening indicator " ,
* pp ) ;
s = " ^SCLIP=1 \r " ;
}
commands [ AT_CLIP ] = kstrdup ( s , GFP_KERNEL ) ;
if ( ! commands [ AT_CLIP ] )
goto oom ;
pp + + ;
l - - ;
if ( l ) {
/* number */
commands [ AT_MSN ] = kmalloc ( l + 8 , GFP_KERNEL ) ;
if ( ! commands [ AT_MSN ] )
goto oom ;
snprintf ( commands [ AT_MSN ] , l + 8 , " ^SMSN=%*s \r " , l , pp ) ;
}
}
/* check parameter: CIP Value */
2010-03-16 10:04:01 +03:00
if ( cmsg - > CIPValue > = ARRAY_SIZE ( cip2bchlc ) | |
2009-10-06 16:19:17 +04:00
( cmsg - > CIPValue > 0 & & cip2bchlc [ cmsg - > CIPValue ] . bc = = NULL ) ) {
dev_notice ( cs - > dev , " %s: unknown CIP value %d \n " ,
" CONNECT_REQ " , cmsg - > CIPValue ) ;
info = CapiCipValueUnknown ;
goto error ;
}
2010-06-21 17:55:05 +04:00
/*
* check / encode parameters : BC & HLC
* must be encoded together as device doesn ' t accept HLC separately
* explicit parameters override values derived from CIP
*/
/* determine lengths */
if ( cmsg - > BC & & cmsg - > BC [ 0 ] ) /* BC specified explicitly */
lbc = 2 * cmsg - > BC [ 0 ] ;
else if ( cip2bchlc [ cmsg - > CIPValue ] . bc ) /* BC derived from CIP */
lbc = strlen ( cip2bchlc [ cmsg - > CIPValue ] . bc ) ;
else /* no BC */
lbc = 0 ;
if ( cmsg - > HLC & & cmsg - > HLC [ 0 ] ) /* HLC specified explicitly */
lhlc = 2 * cmsg - > HLC [ 0 ] ;
else if ( cip2bchlc [ cmsg - > CIPValue ] . hlc ) /* HLC derived from CIP */
lhlc = strlen ( cip2bchlc [ cmsg - > CIPValue ] . hlc ) ;
else /* no HLC */
lhlc = 0 ;
if ( lbc ) {
/* have BC: allocate and assemble command string */
l = lbc + 7 ; /* "^SBC=" + value + "\r" + null byte */
if ( lhlc )
l + = lhlc + 7 ; /* ";^SHLC=" + value */
2009-10-06 16:19:17 +04:00
commands [ AT_BC ] = kmalloc ( l , GFP_KERNEL ) ;
if ( ! commands [ AT_BC ] )
goto oom ;
strcpy ( commands [ AT_BC ] , " ^SBC= " ) ;
2010-06-21 17:55:05 +04:00
if ( cmsg - > BC & & cmsg - > BC [ 0 ] ) /* BC specified explicitly */
decode_ie ( cmsg - > BC , commands [ AT_BC ] + 5 ) ;
else /* BC derived from CIP */
strcpy ( commands [ AT_BC ] + 5 ,
cip2bchlc [ cmsg - > CIPValue ] . bc ) ;
if ( lhlc ) {
strcpy ( commands [ AT_BC ] + lbc + 5 , " ;^SHLC= " ) ;
if ( cmsg - > HLC & & cmsg - > HLC [ 0 ] )
/* HLC specified explicitly */
decode_ie ( cmsg - > HLC ,
commands [ AT_BC ] + lbc + 12 ) ;
else /* HLC derived from CIP */
strcpy ( commands [ AT_BC ] + lbc + 12 ,
cip2bchlc [ cmsg - > CIPValue ] . hlc ) ;
}
2009-10-06 16:19:17 +04:00
strcpy ( commands [ AT_BC ] + l - 2 , " \r " ) ;
2010-06-21 17:55:05 +04:00
} else {
/* no BC */
if ( lhlc ) {
dev_notice ( cs - > dev , " %s: cannot set HLC without BC \n " ,
" CONNECT_REQ " ) ;
info = CapiIllMessageParmCoding ; /* ? */
goto error ;
}
2009-10-06 16:19:17 +04:00
}
/* check/encode parameter: B Protocol */
if ( cmsg - > BProtocol = = CAPI_DEFAULT ) {
bcs - > proto2 = L2_HDLC ;
dev_warn ( cs - > dev ,
" B2 Protocol X.75 SLP unsupported, using Transparent \n " ) ;
} else {
switch ( cmsg - > B1protocol ) {
case 0 :
bcs - > proto2 = L2_HDLC ;
break ;
case 1 :
2010-06-21 17:54:35 +04:00
bcs - > proto2 = L2_VOICE ;
2009-10-06 16:19:17 +04:00
break ;
default :
dev_warn ( cs - > dev ,
" B1 Protocol %u unsupported, using Transparent \n " ,
cmsg - > B1protocol ) ;
2010-06-21 17:54:35 +04:00
bcs - > proto2 = L2_VOICE ;
2009-10-06 16:19:17 +04:00
}
if ( cmsg - > B2protocol ! = 1 )
dev_warn ( cs - > dev ,
" B2 Protocol %u unsupported, using Transparent \n " ,
cmsg - > B2protocol ) ;
if ( cmsg - > B3protocol ! = 0 )
dev_warn ( cs - > dev ,
" B3 Protocol %u unsupported, using Transparent \n " ,
cmsg - > B3protocol ) ;
ignore_cstruct_param ( cs , cmsg - > B1configuration ,
" CONNECT_REQ " , " B1 Configuration " ) ;
ignore_cstruct_param ( cs , cmsg - > B2configuration ,
" CONNECT_REQ " , " B2 Configuration " ) ;
ignore_cstruct_param ( cs , cmsg - > B3configuration ,
" CONNECT_REQ " , " B3 Configuration " ) ;
}
commands [ AT_PROTO ] = kmalloc ( 9 , GFP_KERNEL ) ;
if ( ! commands [ AT_PROTO ] )
goto oom ;
snprintf ( commands [ AT_PROTO ] , 9 , " ^SBPR=%u \r " , bcs - > proto2 ) ;
/* ToDo: check/encode remaining parameters */
ignore_cstruct_param ( cs , cmsg - > CalledPartySubaddress ,
" CONNECT_REQ " , " Called pty subaddr " ) ;
ignore_cstruct_param ( cs , cmsg - > CallingPartySubaddress ,
" CONNECT_REQ " , " Calling pty subaddr " ) ;
ignore_cstruct_param ( cs , cmsg - > LLC ,
" CONNECT_REQ " , " LLC " ) ;
2009-10-25 12:29:37 +03:00
if ( cmsg - > AdditionalInfo ! = CAPI_DEFAULT ) {
ignore_cstruct_param ( cs , cmsg - > BChannelinformation ,
" CONNECT_REQ " , " B Channel Information " ) ;
ignore_cstruct_param ( cs , cmsg - > Keypadfacility ,
" CONNECT_REQ " , " Keypad Facility " ) ;
ignore_cstruct_param ( cs , cmsg - > Useruserdata ,
" CONNECT_REQ " , " User-User Data " ) ;
ignore_cstruct_param ( cs , cmsg - > Facilitydataarray ,
" CONNECT_REQ " , " Facility Data Array " ) ;
}
2009-10-06 16:19:17 +04:00
/* encode parameter: B channel to use */
commands [ AT_ISO ] = kmalloc ( 9 , GFP_KERNEL ) ;
if ( ! commands [ AT_ISO ] )
goto oom ;
snprintf ( commands [ AT_ISO ] , 9 , " ^SISO=%u \r " ,
( unsigned ) bcs - > channel + 1 ) ;
/* queue & schedule EV_DIAL event */
if ( ! gigaset_add_event ( cs , & bcs - > at_state , EV_DIAL , commands ,
2010-02-22 16:09:52 +03:00
bcs - > at_state . seq_index , NULL ) ) {
info = CAPI_MSGOSRESOURCEERR ;
goto error ;
}
2009-10-06 16:19:17 +04:00
gigaset_schedule_event ( cs ) ;
send_conf ( iif , ap , skb , CapiSuccess ) ;
return ;
oom :
dev_err ( cs - > dev , " %s: out of memory \n " , __func__ ) ;
info = CAPI_MSGOSRESOURCEERR ;
error :
if ( commands )
for ( i = 0 ; i < AT_NUM ; i + + )
kfree ( commands [ i ] ) ;
kfree ( commands ) ;
gigaset_free_channel ( bcs ) ;
send_conf ( iif , ap , skb , info ) ;
}
/*
* process CONNECT_RESP message
* checks protocol parameters and queues an ACCEPT or HUP event
*/
static void do_connect_resp ( struct gigaset_capi_ctr * iif ,
struct gigaset_capi_appl * ap ,
struct sk_buff * skb )
{
struct cardstate * cs = iif - > ctr . driverdata ;
_cmsg * cmsg = & iif - > acmsg ;
struct bc_state * bcs ;
struct gigaset_capi_appl * oap ;
2010-06-21 17:55:20 +04:00
unsigned long flags ;
2009-10-06 16:19:17 +04:00
int channel ;
/* decode message */
2009-10-25 12:29:37 +03:00
capi_message2cmsg ( cmsg , skb - > data ) ;
dump_cmsg ( DEBUG_CMD , __func__ , cmsg ) ;
2009-10-25 12:29:57 +03:00
dev_kfree_skb_any ( skb ) ;
2009-10-06 16:19:17 +04:00
/* extract and check channel number from PLCI */
channel = ( cmsg - > adr . adrPLCI > > 8 ) & 0xff ;
if ( ! channel | | channel > cs - > channels ) {
dev_notice ( cs - > dev , " %s: invalid %s 0x%02x \n " ,
" CONNECT_RESP " , " PLCI " , cmsg - > adr . adrPLCI ) ;
return ;
}
bcs = cs - > bcs + channel - 1 ;
switch ( cmsg - > Reject ) {
case 0 : /* Accept */
/* drop all competing applications, keep only this one */
2010-06-21 17:55:20 +04:00
spin_lock_irqsave ( & bcs - > aplock , flags ) ;
while ( bcs - > ap ! = NULL ) {
oap = bcs - > ap ;
bcs - > ap = oap - > bcnext ;
if ( oap ! = ap ) {
spin_unlock_irqrestore ( & bcs - > aplock , flags ) ;
2009-10-06 16:19:17 +04:00
send_disconnect_ind ( bcs , oap ,
CapiCallGivenToOtherApplication ) ;
2010-06-21 17:55:20 +04:00
spin_lock_irqsave ( & bcs - > aplock , flags ) ;
}
}
2009-10-06 16:19:17 +04:00
ap - > bcnext = NULL ;
bcs - > ap = ap ;
2010-06-21 17:55:20 +04:00
spin_unlock_irqrestore ( & bcs - > aplock , flags ) ;
2010-06-21 17:54:19 +04:00
bcs - > rx_bufsize = ap - > rp . datablklen ;
dev_kfree_skb ( bcs - > rx_skb ) ;
gigaset_new_rx_skb ( bcs ) ;
2009-10-06 16:19:17 +04:00
bcs - > chstate | = CHS_NOTIFY_LL ;
/* check/encode B channel protocol */
if ( cmsg - > BProtocol = = CAPI_DEFAULT ) {
bcs - > proto2 = L2_HDLC ;
dev_warn ( cs - > dev ,
" B2 Protocol X.75 SLP unsupported, using Transparent \n " ) ;
} else {
switch ( cmsg - > B1protocol ) {
case 0 :
bcs - > proto2 = L2_HDLC ;
break ;
case 1 :
2010-06-21 17:54:35 +04:00
bcs - > proto2 = L2_VOICE ;
2009-10-06 16:19:17 +04:00
break ;
default :
dev_warn ( cs - > dev ,
" B1 Protocol %u unsupported, using Transparent \n " ,
cmsg - > B1protocol ) ;
2010-06-21 17:54:35 +04:00
bcs - > proto2 = L2_VOICE ;
2009-10-06 16:19:17 +04:00
}
if ( cmsg - > B2protocol ! = 1 )
dev_warn ( cs - > dev ,
" B2 Protocol %u unsupported, using Transparent \n " ,
cmsg - > B2protocol ) ;
if ( cmsg - > B3protocol ! = 0 )
dev_warn ( cs - > dev ,
" B3 Protocol %u unsupported, using Transparent \n " ,
cmsg - > B3protocol ) ;
ignore_cstruct_param ( cs , cmsg - > B1configuration ,
" CONNECT_RESP " , " B1 Configuration " ) ;
ignore_cstruct_param ( cs , cmsg - > B2configuration ,
" CONNECT_RESP " , " B2 Configuration " ) ;
ignore_cstruct_param ( cs , cmsg - > B3configuration ,
" CONNECT_RESP " , " B3 Configuration " ) ;
}
/* ToDo: check/encode remaining parameters */
ignore_cstruct_param ( cs , cmsg - > ConnectedNumber ,
" CONNECT_RESP " , " Connected Number " ) ;
ignore_cstruct_param ( cs , cmsg - > ConnectedSubaddress ,
" CONNECT_RESP " , " Connected Subaddress " ) ;
ignore_cstruct_param ( cs , cmsg - > LLC ,
" CONNECT_RESP " , " LLC " ) ;
2009-10-25 12:29:37 +03:00
if ( cmsg - > AdditionalInfo ! = CAPI_DEFAULT ) {
ignore_cstruct_param ( cs , cmsg - > BChannelinformation ,
" CONNECT_RESP " , " BChannel Information " ) ;
ignore_cstruct_param ( cs , cmsg - > Keypadfacility ,
" CONNECT_RESP " , " Keypad Facility " ) ;
ignore_cstruct_param ( cs , cmsg - > Useruserdata ,
" CONNECT_RESP " , " User-User Data " ) ;
ignore_cstruct_param ( cs , cmsg - > Facilitydataarray ,
" CONNECT_RESP " , " Facility Data Array " ) ;
}
2009-10-06 16:19:17 +04:00
/* Accept call */
if ( ! gigaset_add_event ( cs , & cs - > bcs [ channel - 1 ] . at_state ,
EV_ACCEPT , NULL , 0 , NULL ) )
return ;
gigaset_schedule_event ( cs ) ;
return ;
case 1 : /* Ignore */
/* send DISCONNECT_IND to this application */
send_disconnect_ind ( bcs , ap , 0 ) ;
/* remove it from the list of listening apps */
2010-06-21 17:55:20 +04:00
spin_lock_irqsave ( & bcs - > aplock , flags ) ;
2009-10-06 16:19:17 +04:00
if ( bcs - > ap = = ap ) {
bcs - > ap = ap - > bcnext ;
2010-06-21 17:55:20 +04:00
if ( bcs - > ap = = NULL ) {
2009-10-06 16:19:17 +04:00
/* last one: stop ev-layer hupD notifications */
2010-06-21 17:55:20 +04:00
bcs - > apconnstate = APCONN_NONE ;
2009-10-06 16:19:17 +04:00
bcs - > chstate & = ~ CHS_NOTIFY_LL ;
2010-06-21 17:55:20 +04:00
}
spin_unlock_irqrestore ( & bcs - > aplock , flags ) ;
2009-10-06 16:19:17 +04:00
return ;
}
for ( oap = bcs - > ap ; oap ! = NULL ; oap = oap - > bcnext ) {
if ( oap - > bcnext = = ap ) {
oap - > bcnext = oap - > bcnext - > bcnext ;
2010-06-21 17:55:20 +04:00
spin_unlock_irqrestore ( & bcs - > aplock , flags ) ;
2009-10-06 16:19:17 +04:00
return ;
}
}
2010-06-21 17:55:20 +04:00
spin_unlock_irqrestore ( & bcs - > aplock , flags ) ;
2009-10-06 16:19:17 +04:00
dev_err ( cs - > dev , " %s: application %u not found \n " ,
__func__ , ap - > id ) ;
return ;
default : /* Reject */
/* drop all competing applications, keep only this one */
2010-06-21 17:55:20 +04:00
spin_lock_irqsave ( & bcs - > aplock , flags ) ;
while ( bcs - > ap ! = NULL ) {
oap = bcs - > ap ;
bcs - > ap = oap - > bcnext ;
if ( oap ! = ap ) {
spin_unlock_irqrestore ( & bcs - > aplock , flags ) ;
2009-10-06 16:19:17 +04:00
send_disconnect_ind ( bcs , oap ,
CapiCallGivenToOtherApplication ) ;
2010-06-21 17:55:20 +04:00
spin_lock_irqsave ( & bcs - > aplock , flags ) ;
}
}
2009-10-06 16:19:17 +04:00
ap - > bcnext = NULL ;
bcs - > ap = ap ;
2010-06-21 17:55:20 +04:00
spin_unlock_irqrestore ( & bcs - > aplock , flags ) ;
2009-10-06 16:19:17 +04:00
/* reject call - will trigger DISCONNECT_IND for this app */
dev_info ( cs - > dev , " %s: Reject=%x \n " ,
" CONNECT_RESP " , cmsg - > Reject ) ;
if ( ! gigaset_add_event ( cs , & cs - > bcs [ channel - 1 ] . at_state ,
EV_HUP , NULL , 0 , NULL ) )
return ;
gigaset_schedule_event ( cs ) ;
return ;
}
}
/*
* process CONNECT_B3_REQ message
* build NCCI and emit CONNECT_B3_CONF reply
*/
static void do_connect_b3_req ( struct gigaset_capi_ctr * iif ,
struct gigaset_capi_appl * ap ,
struct sk_buff * skb )
{
struct cardstate * cs = iif - > ctr . driverdata ;
2009-10-25 12:29:37 +03:00
_cmsg * cmsg = & iif - > acmsg ;
2010-06-21 17:55:20 +04:00
struct bc_state * bcs ;
2009-10-06 16:19:17 +04:00
int channel ;
/* decode message */
2009-10-25 12:29:37 +03:00
capi_message2cmsg ( cmsg , skb - > data ) ;
dump_cmsg ( DEBUG_CMD , __func__ , cmsg ) ;
2009-10-06 16:19:17 +04:00
/* extract and check channel number from PLCI */
2009-10-25 12:29:37 +03:00
channel = ( cmsg - > adr . adrPLCI > > 8 ) & 0xff ;
2009-10-06 16:19:17 +04:00
if ( ! channel | | channel > cs - > channels ) {
dev_notice ( cs - > dev , " %s: invalid %s 0x%02x \n " ,
2009-10-25 12:29:37 +03:00
" CONNECT_B3_REQ " , " PLCI " , cmsg - > adr . adrPLCI ) ;
2009-10-06 16:19:17 +04:00
send_conf ( iif , ap , skb , CapiIllContrPlciNcci ) ;
return ;
}
2010-06-21 17:55:20 +04:00
bcs = & cs - > bcs [ channel - 1 ] ;
2009-10-06 16:19:17 +04:00
/* mark logical connection active */
2010-06-21 17:55:20 +04:00
bcs - > apconnstate = APCONN_ACTIVE ;
2009-10-06 16:19:17 +04:00
/* build NCCI: always 1 (one B3 connection only) */
2009-10-25 12:29:37 +03:00
cmsg - > adr . adrNCCI | = 1 < < 16 ;
2009-10-06 16:19:17 +04:00
/* NCPI parameter: not applicable for B3 Transparent */
2009-10-25 12:29:37 +03:00
ignore_cstruct_param ( cs , cmsg - > NCPI , " CONNECT_B3_REQ " , " NCPI " ) ;
send_conf ( iif , ap , skb , ( cmsg - > NCPI & & cmsg - > NCPI [ 0 ] ) ?
CapiNcpiNotSupportedByProtocol : CapiSuccess ) ;
2009-10-06 16:19:17 +04:00
}
/*
* process CONNECT_B3_RESP message
* Depending on the Reject parameter , either emit CONNECT_B3_ACTIVE_IND
* or queue EV_HUP and emit DISCONNECT_B3_IND .
* The emitted message is always shorter than the received one ,
* allowing to reuse the skb .
*/
static void do_connect_b3_resp ( struct gigaset_capi_ctr * iif ,
struct gigaset_capi_appl * ap ,
struct sk_buff * skb )
{
struct cardstate * cs = iif - > ctr . driverdata ;
2009-10-25 12:29:37 +03:00
_cmsg * cmsg = & iif - > acmsg ;
struct bc_state * bcs ;
2009-10-06 16:19:17 +04:00
int channel ;
unsigned int msgsize ;
u8 command ;
/* decode message */
2009-10-25 12:29:37 +03:00
capi_message2cmsg ( cmsg , skb - > data ) ;
dump_cmsg ( DEBUG_CMD , __func__ , cmsg ) ;
2009-10-06 16:19:17 +04:00
/* extract and check channel number and NCCI */
2009-10-25 12:29:37 +03:00
channel = ( cmsg - > adr . adrNCCI > > 8 ) & 0xff ;
2009-10-06 16:19:17 +04:00
if ( ! channel | | channel > cs - > channels | |
2009-10-25 12:29:37 +03:00
( ( cmsg - > adr . adrNCCI > > 16 ) & 0xffff ) ! = 1 ) {
2009-10-06 16:19:17 +04:00
dev_notice ( cs - > dev , " %s: invalid %s 0x%02x \n " ,
2009-10-25 12:29:37 +03:00
" CONNECT_B3_RESP " , " NCCI " , cmsg - > adr . adrNCCI ) ;
2009-10-25 12:29:57 +03:00
dev_kfree_skb_any ( skb ) ;
2009-10-06 16:19:17 +04:00
return ;
}
bcs = & cs - > bcs [ channel - 1 ] ;
2009-10-25 12:29:37 +03:00
if ( cmsg - > Reject ) {
2009-10-06 16:19:17 +04:00
/* Reject: clear B3 connect received flag */
2010-06-21 17:55:20 +04:00
bcs - > apconnstate = APCONN_SETUP ;
2009-10-06 16:19:17 +04:00
/* trigger hangup, causing eventual DISCONNECT_IND */
if ( ! gigaset_add_event ( cs , & bcs - > at_state ,
EV_HUP , NULL , 0 , NULL ) ) {
2009-10-25 12:29:57 +03:00
dev_kfree_skb_any ( skb ) ;
2009-10-06 16:19:17 +04:00
return ;
}
gigaset_schedule_event ( cs ) ;
/* emit DISCONNECT_B3_IND */
command = CAPI_DISCONNECT_B3 ;
msgsize = CAPI_DISCONNECT_B3_IND_BASELEN ;
} else {
/*
* Accept : emit CONNECT_B3_ACTIVE_IND immediately , as
* we only send CONNECT_B3_IND if the B channel is up
*/
command = CAPI_CONNECT_B3_ACTIVE ;
msgsize = CAPI_CONNECT_B3_ACTIVE_IND_BASELEN ;
}
2009-10-25 12:29:37 +03:00
capi_cmsg_header ( cmsg , ap - > id , command , CAPI_IND ,
ap - > nextMessageNumber + + , cmsg - > adr . adrNCCI ) ;
2009-10-06 16:19:17 +04:00
__skb_trim ( skb , msgsize ) ;
2009-10-25 12:29:37 +03:00
capi_cmsg2message ( cmsg , skb - > data ) ;
dump_cmsg ( DEBUG_CMD , __func__ , cmsg ) ;
2009-10-06 16:19:17 +04:00
capi_ctr_handle_message ( & iif - > ctr , ap - > id , skb ) ;
}
/*
* process DISCONNECT_REQ message
* schedule EV_HUP and emit DISCONNECT_B3_IND if necessary ,
* emit DISCONNECT_CONF reply
*/
static void do_disconnect_req ( struct gigaset_capi_ctr * iif ,
struct gigaset_capi_appl * ap ,
struct sk_buff * skb )
{
struct cardstate * cs = iif - > ctr . driverdata ;
2009-10-25 12:29:37 +03:00
_cmsg * cmsg = & iif - > acmsg ;
2009-10-06 16:19:17 +04:00
struct bc_state * bcs ;
_cmsg * b3cmsg ;
struct sk_buff * b3skb ;
int channel ;
/* decode message */
2009-10-25 12:29:37 +03:00
capi_message2cmsg ( cmsg , skb - > data ) ;
dump_cmsg ( DEBUG_CMD , __func__ , cmsg ) ;
2009-10-06 16:19:17 +04:00
/* extract and check channel number from PLCI */
2009-10-25 12:29:37 +03:00
channel = ( cmsg - > adr . adrPLCI > > 8 ) & 0xff ;
2009-10-06 16:19:17 +04:00
if ( ! channel | | channel > cs - > channels ) {
dev_notice ( cs - > dev , " %s: invalid %s 0x%02x \n " ,
2009-10-25 12:29:37 +03:00
" DISCONNECT_REQ " , " PLCI " , cmsg - > adr . adrPLCI ) ;
2009-10-06 16:19:17 +04:00
send_conf ( iif , ap , skb , CapiIllContrPlciNcci ) ;
return ;
}
bcs = cs - > bcs + channel - 1 ;
/* ToDo: process parameter: Additional info */
2009-10-25 12:29:37 +03:00
if ( cmsg - > AdditionalInfo ! = CAPI_DEFAULT ) {
ignore_cstruct_param ( cs , cmsg - > BChannelinformation ,
" DISCONNECT_REQ " , " B Channel Information " ) ;
ignore_cstruct_param ( cs , cmsg - > Keypadfacility ,
" DISCONNECT_REQ " , " Keypad Facility " ) ;
ignore_cstruct_param ( cs , cmsg - > Useruserdata ,
" DISCONNECT_REQ " , " User-User Data " ) ;
ignore_cstruct_param ( cs , cmsg - > Facilitydataarray ,
" DISCONNECT_REQ " , " Facility Data Array " ) ;
}
2009-10-06 16:19:17 +04:00
/* skip if DISCONNECT_IND already sent */
2010-06-21 17:55:20 +04:00
if ( ! bcs - > apconnstate )
2009-10-06 16:19:17 +04:00
return ;
/* check for active logical connection */
2010-06-21 17:55:20 +04:00
if ( bcs - > apconnstate > = APCONN_ACTIVE ) {
2009-10-06 16:19:17 +04:00
/*
* emit DISCONNECT_B3_IND with cause 0x3301
* use separate cmsg structure , as the content of iif - > acmsg
* is still needed for creating the _CONF message
*/
b3cmsg = kmalloc ( sizeof ( * b3cmsg ) , GFP_KERNEL ) ;
if ( ! b3cmsg ) {
dev_err ( cs - > dev , " %s: out of memory \n " , __func__ ) ;
send_conf ( iif , ap , skb , CAPI_MSGOSRESOURCEERR ) ;
return ;
}
capi_cmsg_header ( b3cmsg , ap - > id , CAPI_DISCONNECT_B3 , CAPI_IND ,
ap - > nextMessageNumber + + ,
2009-10-25 12:29:37 +03:00
cmsg - > adr . adrPLCI | ( 1 < < 16 ) ) ;
2009-10-06 16:19:17 +04:00
b3cmsg - > Reason_B3 = CapiProtocolErrorLayer1 ;
b3skb = alloc_skb ( CAPI_DISCONNECT_B3_IND_BASELEN , GFP_KERNEL ) ;
if ( b3skb = = NULL ) {
dev_err ( cs - > dev , " %s: out of memory \n " , __func__ ) ;
send_conf ( iif , ap , skb , CAPI_MSGOSRESOURCEERR ) ;
ISDN, Gigaset: Fix memory leak in do_disconnect_req()
Hi,
In drivers/isdn/gigaset/capi.c::do_disconnect_req() we will leak the
memory allocated (with kmalloc) to 'b3cmsg' if the call to alloc_skb()
fails.
...
b3cmsg = kmalloc(sizeof(*b3cmsg), GFP_KERNEL);
allocation here ------^
if (!b3cmsg) {
dev_err(cs->dev, "%s: out of memory\n", __func__);
send_conf(iif, ap, skb, CAPI_MSGOSRESOURCEERR);
return;
}
capi_cmsg_header(b3cmsg, ap->id, CAPI_DISCONNECT_B3, CAPI_IND,
ap->nextMessageNumber++,
cmsg->adr.adrPLCI | (1 << 16));
b3cmsg->Reason_B3 = CapiProtocolErrorLayer1;
b3skb = alloc_skb(CAPI_DISCONNECT_B3_IND_BASELEN, GFP_KERNEL);
if (b3skb == NULL) {
dev_err(cs->dev, "%s: out of memory\n", __func__);
send_conf(iif, ap, skb, CAPI_MSGOSRESOURCEERR);
return;
leak here ------^
...
This leak is easily fixed by just kfree()'ing the memory allocated to
'b3cmsg' right before we return. The following patch does that.
Signed-off-by: Jesper Juhl <jj@chaosbits.net>
Acked-by: Tilman Schmidt <tilman@imap.cc>
Signed-off-by: David S. Miller <davem@davemloft.net>
2010-12-26 12:59:58 +03:00
kfree ( b3cmsg ) ;
2009-10-06 16:19:17 +04:00
return ;
}
capi_cmsg2message ( b3cmsg ,
__skb_put ( b3skb , CAPI_DISCONNECT_B3_IND_BASELEN ) ) ;
kfree ( b3cmsg ) ;
capi_ctr_handle_message ( & iif - > ctr , ap - > id , b3skb ) ;
}
/* trigger hangup, causing eventual DISCONNECT_IND */
if ( ! gigaset_add_event ( cs , & bcs - > at_state , EV_HUP , NULL , 0 , NULL ) ) {
send_conf ( iif , ap , skb , CAPI_MSGOSRESOURCEERR ) ;
return ;
}
gigaset_schedule_event ( cs ) ;
/* emit reply */
send_conf ( iif , ap , skb , CapiSuccess ) ;
}
/*
* process DISCONNECT_B3_REQ message
* schedule EV_HUP and emit DISCONNECT_B3_CONF reply
*/
static void do_disconnect_b3_req ( struct gigaset_capi_ctr * iif ,
struct gigaset_capi_appl * ap ,
struct sk_buff * skb )
{
struct cardstate * cs = iif - > ctr . driverdata ;
2009-10-25 12:29:37 +03:00
_cmsg * cmsg = & iif - > acmsg ;
2010-06-21 17:55:20 +04:00
struct bc_state * bcs ;
2009-10-06 16:19:17 +04:00
int channel ;
/* decode message */
2009-10-25 12:29:37 +03:00
capi_message2cmsg ( cmsg , skb - > data ) ;
dump_cmsg ( DEBUG_CMD , __func__ , cmsg ) ;
2009-10-06 16:19:17 +04:00
/* extract and check channel number and NCCI */
2009-10-25 12:29:37 +03:00
channel = ( cmsg - > adr . adrNCCI > > 8 ) & 0xff ;
2009-10-06 16:19:17 +04:00
if ( ! channel | | channel > cs - > channels | |
2009-10-25 12:29:37 +03:00
( ( cmsg - > adr . adrNCCI > > 16 ) & 0xffff ) ! = 1 ) {
2009-10-06 16:19:17 +04:00
dev_notice ( cs - > dev , " %s: invalid %s 0x%02x \n " ,
2009-10-25 12:29:37 +03:00
" DISCONNECT_B3_REQ " , " NCCI " , cmsg - > adr . adrNCCI ) ;
2009-10-06 16:19:17 +04:00
send_conf ( iif , ap , skb , CapiIllContrPlciNcci ) ;
return ;
}
2010-06-21 17:55:20 +04:00
bcs = & cs - > bcs [ channel - 1 ] ;
2009-10-06 16:19:17 +04:00
/* reject if logical connection not active */
2010-06-21 17:55:20 +04:00
if ( bcs - > apconnstate < APCONN_ACTIVE ) {
2009-10-06 16:19:17 +04:00
send_conf ( iif , ap , skb ,
CapiMessageNotSupportedInCurrentState ) ;
return ;
}
/* trigger hangup, causing eventual DISCONNECT_B3_IND */
2010-06-21 17:55:20 +04:00
if ( ! gigaset_add_event ( cs , & bcs - > at_state , EV_HUP , NULL , 0 , NULL ) ) {
2009-10-06 16:19:17 +04:00
send_conf ( iif , ap , skb , CAPI_MSGOSRESOURCEERR ) ;
return ;
}
gigaset_schedule_event ( cs ) ;
/* NCPI parameter: not applicable for B3 Transparent */
2009-10-25 12:29:37 +03:00
ignore_cstruct_param ( cs , cmsg - > NCPI ,
2009-10-06 16:19:17 +04:00
" DISCONNECT_B3_REQ " , " NCPI " ) ;
2009-10-25 12:29:37 +03:00
send_conf ( iif , ap , skb , ( cmsg - > NCPI & & cmsg - > NCPI [ 0 ] ) ?
CapiNcpiNotSupportedByProtocol : CapiSuccess ) ;
2009-10-06 16:19:17 +04:00
}
/*
* process DATA_B3_REQ message
*/
static void do_data_b3_req ( struct gigaset_capi_ctr * iif ,
struct gigaset_capi_appl * ap ,
struct sk_buff * skb )
{
struct cardstate * cs = iif - > ctr . driverdata ;
2010-06-21 17:55:20 +04:00
struct bc_state * bcs ;
2009-10-06 16:19:17 +04:00
int channel = CAPIMSG_PLCI_PART ( skb - > data ) ;
u16 ncci = CAPIMSG_NCCI_PART ( skb - > data ) ;
u16 msglen = CAPIMSG_LEN ( skb - > data ) ;
u16 datalen = CAPIMSG_DATALEN ( skb - > data ) ;
u16 flags = CAPIMSG_FLAGS ( skb - > data ) ;
2010-06-21 17:54:50 +04:00
u16 msgid = CAPIMSG_MSGID ( skb - > data ) ;
u16 handle = CAPIMSG_HANDLE_REQ ( skb - > data ) ;
2009-10-06 16:19:17 +04:00
/* frequent message, avoid _cmsg overhead */
2010-07-05 18:18:59 +04:00
dump_rawmsg ( DEBUG_MCMD , __func__ , skb - > data ) ;
2009-10-06 16:19:17 +04:00
/* check parameters */
if ( channel = = 0 | | channel > cs - > channels | | ncci ! = 1 ) {
dev_notice ( cs - > dev , " %s: invalid %s 0x%02x \n " ,
" DATA_B3_REQ " , " NCCI " , CAPIMSG_NCCI ( skb - > data ) ) ;
send_conf ( iif , ap , skb , CapiIllContrPlciNcci ) ;
return ;
}
2010-06-21 17:55:20 +04:00
bcs = & cs - > bcs [ channel - 1 ] ;
2009-10-06 16:19:17 +04:00
if ( msglen ! = CAPI_DATA_B3_REQ_LEN & & msglen ! = CAPI_DATA_B3_REQ_LEN64 )
dev_notice ( cs - > dev , " %s: unexpected length %d \n " ,
" DATA_B3_REQ " , msglen ) ;
if ( msglen + datalen ! = skb - > len )
dev_notice ( cs - > dev , " %s: length mismatch (%d+%d!=%d) \n " ,
" DATA_B3_REQ " , msglen , datalen , skb - > len ) ;
if ( msglen + datalen > skb - > len ) {
/* message too short for announced data length */
send_conf ( iif , ap , skb , CapiIllMessageParmCoding ) ; /* ? */
return ;
}
if ( flags & CAPI_FLAGS_RESERVED ) {
dev_notice ( cs - > dev , " %s: reserved flags set (%x) \n " ,
" DATA_B3_REQ " , flags ) ;
send_conf ( iif , ap , skb , CapiIllMessageParmCoding ) ;
return ;
}
/* reject if logical connection not active */
2010-06-21 17:55:20 +04:00
if ( bcs - > apconnstate < APCONN_ACTIVE ) {
2009-10-06 16:19:17 +04:00
send_conf ( iif , ap , skb , CapiMessageNotSupportedInCurrentState ) ;
return ;
}
2009-10-25 12:29:57 +03:00
/* pull CAPI message into link layer header */
skb_reset_mac_header ( skb ) ;
skb - > mac_len = msglen ;
2009-10-06 16:19:17 +04:00
skb_pull ( skb , msglen ) ;
2009-10-25 12:29:57 +03:00
/* pass to device-specific module */
2010-06-21 17:55:20 +04:00
if ( cs - > ops - > send_skb ( bcs , skb ) < 0 ) {
2009-10-06 16:19:17 +04:00
send_conf ( iif , ap , skb , CAPI_MSGOSRESOURCEERR ) ;
return ;
}
/*
2010-06-21 17:54:50 +04:00
* DATA_B3_CONF will be sent by gigaset_skb_sent ( ) only if " delivery
* confirmation " bit is set; otherwise we have to send it now
2009-10-06 16:19:17 +04:00
*/
2010-06-21 17:54:50 +04:00
if ( ! ( flags & CAPI_FLAGS_DELIVERY_CONFIRMATION ) )
send_data_b3_conf ( cs , & iif - > ctr , ap - > id , msgid , channel , handle ,
flags ? CapiFlagsNotSupportedByProtocol
: CAPI_NOERROR ) ;
2009-10-06 16:19:17 +04:00
}
/*
* process RESET_B3_REQ message
* just always reply " not supported by current protocol "
*/
static void do_reset_b3_req ( struct gigaset_capi_ctr * iif ,
struct gigaset_capi_appl * ap ,
struct sk_buff * skb )
{
/* decode message */
capi_message2cmsg ( & iif - > acmsg , skb - > data ) ;
dump_cmsg ( DEBUG_CMD , __func__ , & iif - > acmsg ) ;
send_conf ( iif , ap , skb ,
CapiResetProcedureNotSupportedByCurrentProtocol ) ;
}
/*
* dump unsupported / ignored messages at most twice per minute ,
* some apps send those very frequently
*/
static unsigned long ignored_msg_dump_time ;
/*
* unsupported CAPI message handler
*/
static void do_unsupported ( struct gigaset_capi_ctr * iif ,
struct gigaset_capi_appl * ap ,
struct sk_buff * skb )
{
/* decode message */
capi_message2cmsg ( & iif - > acmsg , skb - > data ) ;
if ( printk_timed_ratelimit ( & ignored_msg_dump_time , 30 * 1000 ) )
dump_cmsg ( DEBUG_CMD , __func__ , & iif - > acmsg ) ;
send_conf ( iif , ap , skb , CapiMessageNotSupportedInCurrentState ) ;
}
/*
* CAPI message handler : no - op
*/
static void do_nothing ( struct gigaset_capi_ctr * iif ,
struct gigaset_capi_appl * ap ,
struct sk_buff * skb )
{
if ( printk_timed_ratelimit ( & ignored_msg_dump_time , 30 * 1000 ) ) {
/* decode message */
capi_message2cmsg ( & iif - > acmsg , skb - > data ) ;
dump_cmsg ( DEBUG_CMD , __func__ , & iif - > acmsg ) ;
}
2009-10-25 12:29:57 +03:00
dev_kfree_skb_any ( skb ) ;
2009-10-06 16:19:17 +04:00
}
static void do_data_b3_resp ( struct gigaset_capi_ctr * iif ,
struct gigaset_capi_appl * ap ,
struct sk_buff * skb )
{
2010-07-05 18:18:59 +04:00
dump_rawmsg ( DEBUG_MCMD , __func__ , skb - > data ) ;
2009-10-25 12:29:57 +03:00
dev_kfree_skb_any ( skb ) ;
2009-10-06 16:19:17 +04:00
}
/* table of outgoing CAPI message handlers with lookup function */
typedef void ( * capi_send_handler_t ) ( struct gigaset_capi_ctr * ,
struct gigaset_capi_appl * ,
struct sk_buff * ) ;
static struct {
u16 cmd ;
capi_send_handler_t handler ;
} capi_send_handler_table [ ] = {
/* most frequent messages first for faster lookup */
{ CAPI_DATA_B3_REQ , do_data_b3_req } ,
{ CAPI_DATA_B3_RESP , do_data_b3_resp } ,
{ CAPI_ALERT_REQ , do_alert_req } ,
{ CAPI_CONNECT_ACTIVE_RESP , do_nothing } ,
{ CAPI_CONNECT_B3_ACTIVE_RESP , do_nothing } ,
{ CAPI_CONNECT_B3_REQ , do_connect_b3_req } ,
{ CAPI_CONNECT_B3_RESP , do_connect_b3_resp } ,
{ CAPI_CONNECT_B3_T90_ACTIVE_RESP , do_nothing } ,
{ CAPI_CONNECT_REQ , do_connect_req } ,
{ CAPI_CONNECT_RESP , do_connect_resp } ,
{ CAPI_DISCONNECT_B3_REQ , do_disconnect_b3_req } ,
{ CAPI_DISCONNECT_B3_RESP , do_nothing } ,
{ CAPI_DISCONNECT_REQ , do_disconnect_req } ,
{ CAPI_DISCONNECT_RESP , do_nothing } ,
{ CAPI_FACILITY_REQ , do_facility_req } ,
{ CAPI_FACILITY_RESP , do_nothing } ,
{ CAPI_LISTEN_REQ , do_listen_req } ,
{ CAPI_SELECT_B_PROTOCOL_REQ , do_unsupported } ,
{ CAPI_RESET_B3_REQ , do_reset_b3_req } ,
{ CAPI_RESET_B3_RESP , do_nothing } ,
/*
* ToDo : support overlap sending ( requires ev - layer state
* machine extension to generate additional ATD commands )
*/
{ CAPI_INFO_REQ , do_unsupported } ,
{ CAPI_INFO_RESP , do_nothing } ,
/*
* ToDo : what ' s the proper response for these ?
*/
{ CAPI_MANUFACTURER_REQ , do_nothing } ,
{ CAPI_MANUFACTURER_RESP , do_nothing } ,
} ;
/* look up handler */
static inline capi_send_handler_t lookup_capi_send_handler ( const u16 cmd )
{
size_t i ;
for ( i = 0 ; i < ARRAY_SIZE ( capi_send_handler_table ) ; i + + )
if ( capi_send_handler_table [ i ] . cmd = = cmd )
return capi_send_handler_table [ i ] . handler ;
return NULL ;
}
/**
* gigaset_send_message ( ) - accept a CAPI message from an application
* @ ctr : controller descriptor structure .
* @ skb : CAPI message .
*
* Return value : CAPI error code
* Note : capidrv ( and probably others , too ) only uses the return value to
* decide whether it has to free the skb ( only if result ! = CAPI_NOERROR ( 0 ) )
*/
static u16 gigaset_send_message ( struct capi_ctr * ctr , struct sk_buff * skb )
{
struct gigaset_capi_ctr * iif
= container_of ( ctr , struct gigaset_capi_ctr , ctr ) ;
struct cardstate * cs = ctr - > driverdata ;
struct gigaset_capi_appl * ap ;
capi_send_handler_t handler ;
/* can only handle linear sk_buffs */
if ( skb_linearize ( skb ) < 0 ) {
dev_warn ( cs - > dev , " %s: skb_linearize failed \n " , __func__ ) ;
return CAPI_MSGOSRESOURCEERR ;
}
/* retrieve application data structure */
ap = get_appl ( iif , CAPIMSG_APPID ( skb - > data ) ) ;
if ( ! ap ) {
dev_notice ( cs - > dev , " %s: application %u not registered \n " ,
__func__ , CAPIMSG_APPID ( skb - > data ) ) ;
return CAPI_ILLAPPNR ;
}
/* look up command */
handler = lookup_capi_send_handler ( CAPIMSG_CMD ( skb - > data ) ) ;
if ( ! handler ) {
/* unknown/unsupported message type */
if ( printk_ratelimit ( ) )
dev_notice ( cs - > dev , " %s: unsupported message %u \n " ,
__func__ , CAPIMSG_CMD ( skb - > data ) ) ;
return CAPI_ILLCMDORSUBCMDORMSGTOSMALL ;
}
/* serialize */
if ( atomic_add_return ( 1 , & iif - > sendqlen ) > 1 ) {
/* queue behind other messages */
skb_queue_tail ( & iif - > sendqueue , skb ) ;
return CAPI_NOERROR ;
}
/* process message */
handler ( iif , ap , skb ) ;
/* process other messages arrived in the meantime */
while ( atomic_sub_return ( 1 , & iif - > sendqlen ) > 0 ) {
skb = skb_dequeue ( & iif - > sendqueue ) ;
if ( ! skb ) {
/* should never happen */
dev_err ( cs - > dev , " %s: send queue empty \n " , __func__ ) ;
continue ;
}
ap = get_appl ( iif , CAPIMSG_APPID ( skb - > data ) ) ;
if ( ! ap ) {
/* could that happen? */
dev_warn ( cs - > dev , " %s: application %u vanished \n " ,
__func__ , CAPIMSG_APPID ( skb - > data ) ) ;
continue ;
}
handler = lookup_capi_send_handler ( CAPIMSG_CMD ( skb - > data ) ) ;
if ( ! handler ) {
/* should never happen */
dev_err ( cs - > dev , " %s: handler %x vanished \n " ,
__func__ , CAPIMSG_CMD ( skb - > data ) ) ;
continue ;
}
handler ( iif , ap , skb ) ;
}
return CAPI_NOERROR ;
}
/**
* gigaset_procinfo ( ) - build single line description for controller
* @ ctr : controller descriptor structure .
*
* Return value : pointer to generated string ( null terminated )
*/
static char * gigaset_procinfo ( struct capi_ctr * ctr )
{
return ctr - > name ; /* ToDo: more? */
}
2010-01-14 14:10:54 +03:00
static int gigaset_proc_show ( struct seq_file * m , void * v )
2009-10-06 16:19:17 +04:00
{
2010-01-14 14:10:54 +03:00
struct capi_ctr * ctr = m - > private ;
2009-10-06 16:19:17 +04:00
struct cardstate * cs = ctr - > driverdata ;
char * s ;
int i ;
2010-01-14 14:10:54 +03:00
seq_printf ( m , " %-16s %s \n " , " name " , ctr - > name ) ;
seq_printf ( m , " %-16s %s %s \n " , " dev " ,
2009-10-06 16:19:17 +04:00
dev_driver_string ( cs - > dev ) , dev_name ( cs - > dev ) ) ;
2010-01-14 14:10:54 +03:00
seq_printf ( m , " %-16s %d \n " , " id " , cs - > myid ) ;
2009-10-06 16:19:17 +04:00
if ( cs - > gotfwver )
2010-01-14 14:10:54 +03:00
seq_printf ( m , " %-16s %d.%d.%d.%d \n " , " firmware " ,
2009-10-06 16:19:17 +04:00
cs - > fwver [ 0 ] , cs - > fwver [ 1 ] , cs - > fwver [ 2 ] , cs - > fwver [ 3 ] ) ;
2010-01-14 14:10:54 +03:00
seq_printf ( m , " %-16s %d \n " , " channels " , cs - > channels ) ;
seq_printf ( m , " %-16s %s \n " , " onechannel " , cs - > onechannel ? " yes " : " no " ) ;
2009-10-06 16:19:17 +04:00
switch ( cs - > mode ) {
case M_UNKNOWN :
s = " unknown " ;
break ;
case M_CONFIG :
s = " config " ;
break ;
case M_UNIMODEM :
s = " Unimodem " ;
break ;
case M_CID :
s = " CID " ;
break ;
default :
s = " ?? " ;
}
2010-01-14 14:10:54 +03:00
seq_printf ( m , " %-16s %s \n " , " mode " , s ) ;
2009-10-06 16:19:17 +04:00
switch ( cs - > mstate ) {
case MS_UNINITIALIZED :
s = " uninitialized " ;
break ;
case MS_INIT :
s = " init " ;
break ;
case MS_LOCKED :
s = " locked " ;
break ;
case MS_SHUTDOWN :
s = " shutdown " ;
break ;
case MS_RECOVER :
s = " recover " ;
break ;
case MS_READY :
s = " ready " ;
break ;
default :
s = " ?? " ;
}
2010-01-14 14:10:54 +03:00
seq_printf ( m , " %-16s %s \n " , " mstate " , s ) ;
2009-10-06 16:19:17 +04:00
2010-01-14 14:10:54 +03:00
seq_printf ( m , " %-16s %s \n " , " running " , cs - > running ? " yes " : " no " ) ;
seq_printf ( m , " %-16s %s \n " , " connected " , cs - > connected ? " yes " : " no " ) ;
seq_printf ( m , " %-16s %s \n " , " isdn_up " , cs - > isdn_up ? " yes " : " no " ) ;
seq_printf ( m , " %-16s %s \n " , " cidmode " , cs - > cidmode ? " yes " : " no " ) ;
2009-10-06 16:19:17 +04:00
for ( i = 0 ; i < cs - > channels ; i + + ) {
2010-01-14 14:10:54 +03:00
seq_printf ( m , " [%d]%-13s %d \n " , i , " corrupted " ,
2009-10-06 16:19:17 +04:00
cs - > bcs [ i ] . corrupted ) ;
2010-01-14 14:10:54 +03:00
seq_printf ( m , " [%d]%-13s %d \n " , i , " trans_down " ,
2009-10-06 16:19:17 +04:00
cs - > bcs [ i ] . trans_down ) ;
2010-01-14 14:10:54 +03:00
seq_printf ( m , " [%d]%-13s %d \n " , i , " trans_up " ,
2009-10-06 16:19:17 +04:00
cs - > bcs [ i ] . trans_up ) ;
2010-01-14 14:10:54 +03:00
seq_printf ( m , " [%d]%-13s %d \n " , i , " chstate " ,
2009-10-06 16:19:17 +04:00
cs - > bcs [ i ] . chstate ) ;
switch ( cs - > bcs [ i ] . proto2 ) {
case L2_BITSYNC :
s = " bitsync " ;
break ;
case L2_HDLC :
s = " HDLC " ;
break ;
case L2_VOICE :
s = " voice " ;
break ;
default :
s = " ?? " ;
}
2010-01-14 14:10:54 +03:00
seq_printf ( m , " [%d]%-13s %s \n " , i , " proto2 " , s ) ;
2009-10-06 16:19:17 +04:00
}
2010-01-14 14:10:54 +03:00
return 0 ;
2009-10-06 16:19:17 +04:00
}
2010-01-14 14:10:54 +03:00
static int gigaset_proc_open ( struct inode * inode , struct file * file )
{
return single_open ( file , gigaset_proc_show , PDE ( inode ) - > data ) ;
}
static const struct file_operations gigaset_proc_fops = {
. owner = THIS_MODULE ,
. open = gigaset_proc_open ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = single_release ,
} ;
2009-10-06 16:19:17 +04:00
/**
2010-03-14 15:58:05 +03:00
* gigaset_isdn_regdev ( ) - register device to LL
2009-10-06 16:19:17 +04:00
* @ cs : device descriptor structure .
* @ isdnid : device name .
*
* Return value : 1 for success , 0 for failure
*/
2010-03-14 15:58:05 +03:00
int gigaset_isdn_regdev ( struct cardstate * cs , const char * isdnid )
2009-10-06 16:19:17 +04:00
{
struct gigaset_capi_ctr * iif ;
int rc ;
iif = kmalloc ( sizeof ( * iif ) , GFP_KERNEL ) ;
if ( ! iif ) {
pr_err ( " %s: out of memory \n " , __func__ ) ;
return 0 ;
}
/* prepare controller structure */
iif - > ctr . owner = THIS_MODULE ;
iif - > ctr . driverdata = cs ;
strncpy ( iif - > ctr . name , isdnid , sizeof ( iif - > ctr . name ) ) ;
iif - > ctr . driver_name = " gigaset " ;
2010-05-23 05:02:38 +04:00
iif - > ctr . load_firmware = NULL ;
iif - > ctr . reset_ctr = NULL ;
2009-10-06 16:19:17 +04:00
iif - > ctr . register_appl = gigaset_register_appl ;
iif - > ctr . release_appl = gigaset_release_appl ;
iif - > ctr . send_message = gigaset_send_message ;
iif - > ctr . procinfo = gigaset_procinfo ;
2010-01-14 14:10:54 +03:00
iif - > ctr . proc_fops = & gigaset_proc_fops ;
2009-10-06 16:19:17 +04:00
INIT_LIST_HEAD ( & iif - > appls ) ;
skb_queue_head_init ( & iif - > sendqueue ) ;
atomic_set ( & iif - > sendqlen , 0 ) ;
/* register controller with CAPI */
rc = attach_capi_ctr ( & iif - > ctr ) ;
if ( rc ) {
pr_err ( " attach_capi_ctr failed (%d) \n " , rc ) ;
kfree ( iif ) ;
return 0 ;
}
cs - > iif = iif ;
cs - > hw_hdr_len = CAPI_DATA_B3_REQ_LEN ;
return 1 ;
}
/**
2010-03-14 15:58:05 +03:00
* gigaset_isdn_unregdev ( ) - unregister device from LL
2009-10-06 16:19:17 +04:00
* @ cs : device descriptor structure .
*/
2010-03-14 15:58:05 +03:00
void gigaset_isdn_unregdev ( struct cardstate * cs )
2009-10-06 16:19:17 +04:00
{
struct gigaset_capi_ctr * iif = cs - > iif ;
detach_capi_ctr ( & iif - > ctr ) ;
kfree ( iif ) ;
cs - > iif = NULL ;
2010-03-14 15:58:05 +03:00
}
static struct capi_driver capi_driver_gigaset = {
. name = " gigaset " ,
. revision = " 1.0 " ,
} ;
/**
* gigaset_isdn_regdrv ( ) - register driver to LL
*/
void gigaset_isdn_regdrv ( void )
{
pr_info ( " Kernel CAPI interface \n " ) ;
register_capi_driver ( & capi_driver_gigaset ) ;
}
/**
* gigaset_isdn_unregdrv ( ) - unregister driver from LL
*/
void gigaset_isdn_unregdrv ( void )
{
2009-10-06 16:19:17 +04:00
unregister_capi_driver ( & capi_driver_gigaset ) ;
}