diff options
Diffstat (limited to 'drivers/isdn/gigaset/capi.c')
| -rw-r--r-- | drivers/isdn/gigaset/capi.c | 940 |
1 files changed, 538 insertions, 402 deletions
diff --git a/drivers/isdn/gigaset/capi.c b/drivers/isdn/gigaset/capi.c index 3f5cd06af10..3286903a95d 100644 --- a/drivers/isdn/gigaset/capi.c +++ b/drivers/isdn/gigaset/capi.c @@ -12,10 +12,13 @@ */ #include "gigaset.h" -#include <linux/ctype.h> +#include <linux/proc_fs.h> +#include <linux/seq_file.h> +#include <linux/ratelimit.h> #include <linux/isdn/capilli.h> #include <linux/isdn/capicmd.h> #include <linux/isdn/capiutil.h> +#include <linux/export.h> /* missing from kernelcapi.h */ #define CapiNcpiNotSupportedByProtocol 0x0001 @@ -24,17 +27,17 @@ #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) +#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_STDCONF_LEN (CAPI_MSG_BASELEN + 4 + 2) #define CAPI_FACILITY_HANDSET 0x0000 #define CAPI_FACILITY_DTMF 0x0001 @@ -44,6 +47,7 @@ #define CAPI_FACILITY_LI 0x0005 #define CAPI_SUPPSVC_GETSUPPORTED 0x0000 +#define CAPI_SUPPSVC_LISTEN 0x0001 /* missing from capiutil.h */ #define CAPIMSG_PLCI_PART(m) CAPIMSG_U8(m, 9) @@ -69,7 +73,7 @@ #define MAX_NUMBER_DIGITS 20 #define MAX_FMT_IE_LEN 20 -/* values for gigaset_capi_appl.connected */ +/* values for bcs->apconnstate */ #define APCONN_NONE 0 /* inactive/listening */ #define APCONN_SETUP 1 /* connecting */ #define APCONN_ACTIVE 2 /* B channel up */ @@ -79,10 +83,10 @@ struct gigaset_capi_appl { struct list_head ctrlist; struct gigaset_capi_appl *bcnext; u16 id; + struct capi_register_params rp; u16 nextMessageNumber; u32 listenInfoMask; u32 listenCIPmask; - int connected; }; /* CAPI specific controller data structure */ @@ -94,10 +98,10 @@ struct gigaset_capi_ctr { /* 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]; + 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) */ @@ -105,51 +109,35 @@ 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 */ + [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 */ }; /* @@ -161,7 +149,7 @@ static struct { * emit unsupported parameter warning */ static inline void ignore_cstruct_param(struct cardstate *cs, _cstruct param, - char *msgname, char *paramname) + char *msgname, char *paramname) { if (param && *param) dev_warn(cs->dev, "%s: ignoring unsupported parameter: %s\n", @@ -169,31 +157,6 @@ static inline void ignore_cstruct_param(struct cardstate *cs, _cstruct param, } /* - * check for legal hex digit - */ -static inline int ishexdigit(char c) -{ - if (c >= '0' && c <= '9') - return 1; - if (c >= 'A' && c <= 'F') - return 1; - if (c >= 'a' && c <= 'f') - return 1; - return 0; -} - -/* - * convert hex to binary - */ -static inline u8 hex2bin(char c) -{ - int result = c & 0x0f; - if (c & 0x40) - result += 9; - return result; -} - -/* * convert an IE from Gigaset hex string to ETSI binary representation * including length byte * return value: result length, -1 on error @@ -202,9 +165,9 @@ static int encode_ie(char *in, u8 *out, int maxlen) { int l = 0; while (*in) { - if (!ishexdigit(in[0]) || !ishexdigit(in[1]) || l >= maxlen) + if (!isxdigit(in[0]) || !isxdigit(in[1]) || l >= maxlen) return -1; - out[++l] = (hex2bin(in[0]) << 4) + hex2bin(in[1]); + out[++l] = (hex_to_bin(in[0]) << 4) + hex_to_bin(in[1]); in += 2; } out[0] = l; @@ -245,10 +208,14 @@ get_appl(struct gigaset_capi_ctr *iif, u16 appl) static inline void dump_cmsg(enum debuglevel level, const char *tag, _cmsg *p) { #ifdef CONFIG_GIGASET_DEBUG + /* dump at most 20 messages in 20 secs */ + static DEFINE_RATELIMIT_STATE(msg_dump_ratelimit, 20 * HZ, 20); _cdebbuf *cdb; if (!(gigaset_debuglevel & level)) return; + if (!___ratelimit(&msg_dump_ratelimit, tag)) + return; cdb = capi_cmsg2str(p); if (cdb) { @@ -281,32 +248,38 @@ static inline void dump_rawmsg(enum debuglevel level, const char *tag, CAPIMSG_APPID(data), CAPIMSG_MSGID(data), l, CAPIMSG_CONTROL(data)); l -= 12; - dbgline = kmalloc(3*l, GFP_ATOMIC); + if (l <= 0) + return; + 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 * 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'; + dbgline[3 * l - 1] = '\0'; gig_dbg(level, " %s", dbgline); kfree(dbgline); if (CAPIMSG_COMMAND(data) == CAPI_DATA_B3 && (CAPIMSG_SUBCOMMAND(data) == CAPI_REQ || - CAPIMSG_SUBCOMMAND(data) == CAPI_IND) && - CAPIMSG_DATALEN(data) > 0) { + CAPIMSG_SUBCOMMAND(data) == CAPI_IND)) { l = CAPIMSG_DATALEN(data); - dbgline = kmalloc(3*l, GFP_ATOMIC); + gig_dbg(level, " DataLength=%d", l); + if (l <= 0 || !(gigaset_debuglevel & DEBUG_LLDATA)) + return; + if (l > 64) + l = 64; /* arbitrary limit */ + 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 * i] = hex_asc_hi(data[i]); + dbgline[3 * i + 1] = hex_asc_lo(data[i]); + dbgline[3 * i + 2] = ' '; } - dbgline[3*l-1] = '\0'; + dbgline[3 * l - 1] = '\0'; gig_dbg(level, " %s", dbgline); kfree(dbgline); } @@ -317,9 +290,10 @@ static inline void dump_rawmsg(enum debuglevel level, const char *tag, * format CAPI IE as string */ +#ifdef CONFIG_GIGASET_DEBUG static const char *format_ie(const char *ie) { - static char result[3*MAX_FMT_IE_LEN]; + static char result[3 * MAX_FMT_IE_LEN]; int len, count; char *pout = result; @@ -328,7 +302,7 @@ static const char *format_ie(const char *ie) count = len = ie[0]; if (count > MAX_FMT_IE_LEN) - count = MAX_FMT_IE_LEN-1; + count = MAX_FMT_IE_LEN - 1; while (count--) { *pout++ = hex_asc_hi(*++ie); *pout++ = hex_asc_lo(*ie); @@ -342,6 +316,40 @@ static const char *format_ie(const char *ie) *--pout = 0; return result; } +#endif + +/* + * 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); +} /* @@ -363,51 +371,33 @@ void gigaset_skb_sent(struct bc_state *bcs, struct sk_buff *dskb) struct gigaset_capi_ctr *iif = cs->iif; struct gigaset_capi_appl *ap = bcs->ap; unsigned char *req = skb_mac_header(dskb); - struct sk_buff *cskb; u16 flags; /* update statistics */ ++bcs->trans_up; if (!ap) { - dev_err(cs->dev, "%s: no application\n", __func__); + gig_dbg(DEBUG_MCMD, "%s: application gone", __func__); return; } /* don't send further B3 messages if disconnected */ - if (ap->connected < APCONN_ACTIVE) { - gig_dbg(DEBUG_LLDATA, "disconnected, discarding ack"); + if (bcs->apconnstate < APCONN_ACTIVE) { + gig_dbg(DEBUG_MCMD, "%s: disconnected", __func__); return; } - /* ToDo: honor unset "delivery confirmation" bit */ + /* + * send DATA_B3_CONF if "delivery confirmation" bit was set in request; + * otherwise it has already been sent by do_data_b3_req() + */ flags = CAPIMSG_FLAGS(req); - - /* build DATA_B3_CONF message */ - 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 */ - CAPIMSG_SETLEN(cskb->data, CAPI_DATA_B3_CONF_LEN); - CAPIMSG_SETAPPID(cskb->data, ap->id); - CAPIMSG_SETCOMMAND(cskb->data, CAPI_DATA_B3); - CAPIMSG_SETSUBCOMMAND(cskb->data, CAPI_CONF); - CAPIMSG_SETMSGID(cskb->data, CAPIMSG_MSGID(req)); - CAPIMSG_SETCONTROLLER(cskb->data, iif->ctr.cnr); - CAPIMSG_SETPLCI_PART(cskb->data, bcs->channel + 1); - CAPIMSG_SETNCCI_PART(cskb->data, 1); - CAPIMSG_SETHANDLE_CONF(cskb->data, CAPIMSG_HANDLE_REQ(req)); - if (flags & ~CAPI_FLAGS_DELIVERY_CONFIRMATION) - CAPIMSG_SETINFO_CONF(cskb->data, - CapiFlagsNotSupportedByProtocol); - else - CAPIMSG_SETINFO_CONF(cskb->data, CAPI_NOERROR); - - /* emit message */ - dump_rawmsg(DEBUG_LLDATA, "DATA_B3_CONF", cskb->data); - capi_ctr_handle_message(&iif->ctr, ap->id, cskb); + 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); } EXPORT_SYMBOL_GPL(gigaset_skb_sent); @@ -431,13 +421,14 @@ void gigaset_skb_rcvd(struct bc_state *bcs, struct sk_buff *skb) bcs->trans_down++; if (!ap) { - dev_err(cs->dev, "%s: no application\n", __func__); + gig_dbg(DEBUG_MCMD, "%s: application gone", __func__); + dev_kfree_skb_any(skb); return; } /* don't send further B3 messages if disconnected */ - if (ap->connected < APCONN_ACTIVE) { - gig_dbg(DEBUG_LLDATA, "disconnected, discarding data"); + if (bcs->apconnstate < APCONN_ACTIVE) { + gig_dbg(DEBUG_MCMD, "%s: disconnected", __func__); dev_kfree_skb_any(skb); return; } @@ -463,7 +454,7 @@ void gigaset_skb_rcvd(struct bc_state *bcs, struct sk_buff *skb) /* Data64 parameter not present */ /* emit message */ - dump_rawmsg(DEBUG_LLDATA, "DATA_B3_IND", skb->data); + dump_rawmsg(DEBUG_MCMD, __func__, skb->data); capi_ctr_handle_message(&iif->ctr, ap->id, skb); } EXPORT_SYMBOL_GPL(gigaset_skb_rcvd); @@ -508,6 +499,7 @@ int gigaset_isdn_icall(struct at_state_t *at_state) u32 actCIPmask; struct sk_buff *skb; unsigned int msgsize; + unsigned long flags; int i; /* @@ -590,7 +582,7 @@ int gigaset_isdn_icall(struct at_state_t *at_state) } 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); + memcpy(iif->cdpty_buf + 2, at_state->str_var[STR_ZCPN], i); iif->hcmsg.CalledPartyNumber = iif->cdpty_buf; msgsize += iif->hcmsg.CalledPartyNumber[0]; } @@ -606,7 +598,7 @@ int gigaset_isdn_icall(struct at_state_t *at_state) 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); + memcpy(iif->cgpty_buf + 3, at_state->str_var[STR_NMBR], i); iif->hcmsg.CallingPartyNumber = iif->cgpty_buf; msgsize += iif->hcmsg.CallingPartyNumber[0]; } @@ -632,7 +624,14 @@ int gigaset_isdn_icall(struct at_state_t *at_state) format_ie(iif->hcmsg.CalledPartyNumber)); /* scan application list for matching listeners */ - bcs->ap = NULL; + 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); actCIPmask = 1 | (1 << iif->hcmsg.CIPValue); list_for_each_entry(ap, &iif->appls, ctrlist) if (actCIPmask & ap->listenCIPmask) { @@ -650,10 +649,12 @@ int gigaset_isdn_icall(struct at_state_t *at_state) dump_cmsg(DEBUG_CMD, __func__, &iif->hcmsg); /* add to listeners on this B channel, update state */ + spin_lock_irqsave(&bcs->aplock, flags); ap->bcnext = bcs->ap; bcs->ap = ap; bcs->chstate |= CHS_NOTIFY_LL; - ap->connected = APCONN_SETUP; + bcs->apconnstate = APCONN_SETUP; + spin_unlock_irqrestore(&bcs->aplock, flags); /* emit message */ capi_ctr_handle_message(&iif->ctr, ap->id, skb); @@ -678,7 +679,7 @@ static void send_disconnect_ind(struct bc_state *bcs, struct gigaset_capi_ctr *iif = cs->iif; struct sk_buff *skb; - if (ap->connected == APCONN_NONE) + if (bcs->apconnstate == APCONN_NONE) return; capi_cmsg_header(&iif->hcmsg, ap->id, CAPI_DISCONNECT, CAPI_IND, @@ -692,7 +693,6 @@ static void send_disconnect_ind(struct bc_state *bcs, } capi_cmsg2message(&iif->hcmsg, __skb_put(skb, CAPI_DISCONNECT_IND_LEN)); dump_cmsg(DEBUG_CMD, __func__, &iif->hcmsg); - ap->connected = APCONN_NONE; capi_ctr_handle_message(&iif->ctr, ap->id, skb); } @@ -709,9 +709,9 @@ static void send_disconnect_b3_ind(struct bc_state *bcs, struct sk_buff *skb; /* nothing to do if no logical connection active */ - if (ap->connected < APCONN_ACTIVE) + if (bcs->apconnstate < APCONN_ACTIVE) return; - ap->connected = APCONN_SETUP; + bcs->apconnstate = APCONN_SETUP; capi_cmsg_header(&iif->hcmsg, ap->id, CAPI_DISCONNECT_B3, CAPI_IND, ap->nextMessageNumber++, @@ -738,14 +738,25 @@ void gigaset_isdn_connD(struct bc_state *bcs) { struct cardstate *cs = bcs->cs; struct gigaset_capi_ctr *iif = cs->iif; - struct gigaset_capi_appl *ap = bcs->ap; + struct gigaset_capi_appl *ap; struct sk_buff *skb; unsigned int msgsize; + unsigned long flags; + spin_lock_irqsave(&bcs->aplock, flags); + ap = bcs->ap; if (!ap) { - dev_err(cs->dev, "%s: no application\n", __func__); + spin_unlock_irqrestore(&bcs->aplock, flags); + gig_dbg(DEBUG_CMD, "%s: application gone", __func__); + return; + } + 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); while (ap->bcnext) { /* this should never happen */ dev_warn(cs->dev, "%s: dropping extra application %u\n", @@ -754,11 +765,6 @@ void gigaset_isdn_connD(struct bc_state *bcs) CapiCallGivenToOtherApplication); ap->bcnext = ap->bcnext->bcnext; } - if (ap->connected == APCONN_NONE) { - dev_warn(cs->dev, "%s: application %u not connected\n", - __func__, ap->id); - return; - } /* prepare CONNECT_ACTIVE_IND message * Note: LLC not supported by device @@ -796,17 +802,24 @@ void gigaset_isdn_connD(struct bc_state *bcs) void gigaset_isdn_hupD(struct bc_state *bcs) { struct gigaset_capi_appl *ap; + unsigned long flags; /* * ToDo: pass on reason code reported by device * (requires ev-layer state machine extension to collect * ZCAU device reply) */ - for (ap = bcs->ap; ap != NULL; ap = ap->bcnext) { + spin_lock_irqsave(&bcs->aplock, flags); + while (bcs->ap != NULL) { + ap = bcs->ap; + bcs->ap = ap->bcnext; + spin_unlock_irqrestore(&bcs->aplock, flags); send_disconnect_b3_ind(bcs, ap); send_disconnect_ind(bcs, ap, 0); + spin_lock_irqsave(&bcs->aplock, flags); } - bcs->ap = NULL; + bcs->apconnstate = APCONN_NONE; + spin_unlock_irqrestore(&bcs->aplock, flags); } /** @@ -820,24 +833,21 @@ void gigaset_isdn_connB(struct bc_state *bcs) { struct cardstate *cs = bcs->cs; struct gigaset_capi_ctr *iif = cs->iif; - struct gigaset_capi_appl *ap = bcs->ap; + struct gigaset_capi_appl *ap; struct sk_buff *skb; + unsigned long flags; unsigned int msgsize; u8 command; + spin_lock_irqsave(&bcs->aplock, flags); + ap = bcs->ap; if (!ap) { - dev_err(cs->dev, "%s: no application\n", __func__); + spin_unlock_irqrestore(&bcs->aplock, flags); + gig_dbg(DEBUG_CMD, "%s: application gone", __func__); return; } - 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; - } - if (!ap->connected) { + if (!bcs->apconnstate) { + spin_unlock_irqrestore(&bcs->aplock, flags); dev_warn(cs->dev, "%s: application %u not connected\n", __func__, ap->id); return; @@ -849,13 +859,26 @@ void gigaset_isdn_connB(struct bc_state *bcs) * CONNECT_B3_ACTIVE_IND in reply to CONNECT_B3_RESP * Parameters in both cases always: NCCI = 1, NCPI empty */ - if (ap->connected >= APCONN_ACTIVE) { + if (bcs->apconnstate >= APCONN_ACTIVE) { command = CAPI_CONNECT_B3_ACTIVE; msgsize = CAPI_CONNECT_B3_ACTIVE_IND_BASELEN; } else { command = CAPI_CONNECT_B3; msgsize = CAPI_CONNECT_B3_IND_BASELEN; } + 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; + } + capi_cmsg_header(&iif->hcmsg, ap->id, command, CAPI_IND, ap->nextMessageNumber++, iif->ctr.cnr | ((bcs->channel + 1) << 8) | (1 << 16)); @@ -866,7 +889,6 @@ void gigaset_isdn_connB(struct bc_state *bcs) } capi_cmsg2message(&iif->hcmsg, __skb_put(skb, msgsize)); dump_cmsg(DEBUG_CMD, __func__, &iif->hcmsg); - ap->connected = APCONN_ACTIVE; capi_ctr_handle_message(&iif->ctr, ap->id, skb); } @@ -879,13 +901,12 @@ void gigaset_isdn_connB(struct bc_state *bcs) */ void gigaset_isdn_hupB(struct bc_state *bcs) { - struct cardstate *cs = bcs->cs; struct gigaset_capi_appl *ap = bcs->ap; /* ToDo: assure order of DISCONNECT_B3_IND and DISCONNECT_IND ? */ if (!ap) { - dev_err(cs->dev, "%s: no application\n", __func__); + gig_dbg(DEBUG_CMD, "%s: application gone", __func__); return; } @@ -946,40 +967,19 @@ void gigaset_isdn_stop(struct cardstate *cs) */ /* - * load firmware - */ -static int gigaset_load_firmware(struct capi_ctr *ctr, capiloaddata *data) -{ - struct cardstate *cs = ctr->driverdata; - - /* AVM specific operation, not needed for Gigaset -- ignore */ - dev_notice(cs->dev, "load_firmware ignored\n"); - - return 0; -} - -/* - * reset (deactivate) controller - */ -static void gigaset_reset_ctr(struct capi_ctr *ctr) -{ - struct cardstate *cs = ctr->driverdata; - - /* AVM specific operation, not needed for Gigaset -- ignore */ - dev_notice(cs->dev, "reset_ctr ignored\n"); -} - -/* * register CAPI application */ static void gigaset_register_appl(struct capi_ctr *ctr, u16 appl, - capi_register_params *rp) + 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; + gig_dbg(DEBUG_CMD, "%s [%u] l3cnt=%u blkcnt=%u blklen=%u", + __func__, appl, rp->level3cnt, rp->datablkcnt, rp->datablklen); + list_for_each_entry(ap, &iif->appls, ctrlist) if (ap->id == appl) { dev_notice(cs->dev, @@ -993,8 +993,65 @@ static void gigaset_register_appl(struct capi_ctr *ctr, u16 appl, return; } ap->id = appl; + ap->rp = *rp; list_add(&ap->ctrlist, &iif->appls); + 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; + spin_unlock_irqrestore(&bcs->aplock, flags); + return; + } + bcap = bcap->bcnext; + } while (bcap != NULL); + spin_unlock_irqrestore(&bcs->aplock, flags); } /* @@ -1006,19 +1063,21 @@ static void gigaset_release_appl(struct capi_ctr *ctr, u16 appl) = container_of(ctr, struct gigaset_capi_ctr, ctr); struct cardstate *cs = iif->ctr.driverdata; struct gigaset_capi_appl *ap, *tmp; + unsigned ch; + + gig_dbg(DEBUG_CMD, "%s [%u]", __func__, appl); list_for_each_entry_safe(ap, tmp, &iif->appls, ctrlist) if (ap->id == appl) { - if (ap->connected != APCONN_NONE) { - dev_err(cs->dev, - "%s: application %u still connected\n", - __func__, ap->id); - /* ToDo: clear active connection */ - } + /* remove from any channels */ + for (ch = 0; ch < cs->channels; ch++) + remove_appl_from_channel(&cs->bcs[ch], ap); + + /* remove from registration list */ list_del(&ap->ctrlist); kfree(ap); + dev_info(cs->dev, "application %u released\n", appl); } - } /* @@ -1088,7 +1147,7 @@ static void do_facility_req(struct gigaset_capi_ctr *iif, case CAPI_FACILITY_SUPPSVC: /* decode Function parameter */ pparam = cmsg->FacilityRequestParameter; - if (pparam == NULL || *pparam < 2) { + if (pparam == NULL || pparam[0] < 2) { dev_notice(cs->dev, "%s: %s missing\n", "FACILITY_REQ", "Facility Request Parameter"); send_conf(iif, ap, skb, CapiIllMessageParmCoding); @@ -1105,8 +1164,34 @@ static void do_facility_req(struct gigaset_capi_ctr *iif, /* Supported Services: none */ capimsg_setu32(confparam, 6, 0); break; + 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; + /* ToDo: add supported services */ + default: + dev_notice(cs->dev, + "%s: unsupported supplementary service function 0x%04x\n", + "FACILITY_REQ", function); info = CapiFacilitySpecificFunctionNotSupported; /* Supplementary Service specific parameter */ confparam[3] = 2; /* length */ @@ -1197,7 +1282,8 @@ static void do_connect_req(struct gigaset_capi_ctr *iif, char **commands; char *s; u8 *pp; - int i, l; + unsigned long flags; + int i, l, lbc, lhlc; u16 info; /* decode message */ @@ -1212,12 +1298,22 @@ static void do_connect_req(struct gigaset_capi_ctr *iif, send_conf(iif, ap, skb, CapiNoPlciAvailable); return; } + 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); ap->bcnext = NULL; bcs->ap = ap; + bcs->apconnstate = APCONN_SETUP; + spin_unlock_irqrestore(&bcs->aplock, flags); + + bcs->rx_bufsize = ap->rp.datablklen; + dev_kfree_skb(bcs->rx_skb); + gigaset_new_rx_skb(bcs); cmsg->adr.adrPLCI |= (bcs->channel + 1) << 8; /* build command table */ - commands = kzalloc(AT_NUM*(sizeof *commands), GFP_KERNEL); + commands = kzalloc(AT_NUM * (sizeof *commands), GFP_KERNEL); if (!commands) goto oom; @@ -1252,10 +1348,10 @@ static void do_connect_req(struct gigaset_capi_ctr *iif, commands[AT_TYPE] = kstrdup(s, GFP_KERNEL); if (!commands[AT_TYPE]) goto oom; - commands[AT_DIAL] = kmalloc(l+3, GFP_KERNEL); + commands[AT_DIAL] = kmalloc(l + 3, GFP_KERNEL); if (!commands[AT_DIAL]) goto oom; - snprintf(commands[AT_DIAL], l+3, "D%.*s\r", l, pp); + snprintf(commands[AT_DIAL], l + 3, "D%.*s\r", l, pp); /* encode parameter: Calling party number */ pp = cmsg->CallingPartyNumber; @@ -1305,15 +1401,15 @@ static void do_connect_req(struct gigaset_capi_ctr *iif, if (l) { /* number */ - commands[AT_MSN] = kmalloc(l+8, GFP_KERNEL); + 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); + snprintf(commands[AT_MSN], l + 8, "^SMSN=%*s\r", l, pp); } } /* check parameter: CIP Value */ - if (cmsg->CIPValue > ARRAY_SIZE(cip2bchlc) || + if (cmsg->CIPValue >= ARRAY_SIZE(cip2bchlc) || (cmsg->CIPValue > 0 && cip2bchlc[cmsg->CIPValue].bc == NULL)) { dev_notice(cs->dev, "%s: unknown CIP value %d\n", "CONNECT_REQ", cmsg->CIPValue); @@ -1321,77 +1417,94 @@ static void do_connect_req(struct gigaset_capi_ctr *iif, goto error; } - /* check/encode parameter: BC */ - if (cmsg->BC && cmsg->BC[0]) { - /* explicit BC overrides CIP */ - l = 2*cmsg->BC[0] + 7; + /* + * 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 */ commands[AT_BC] = kmalloc(l, GFP_KERNEL); if (!commands[AT_BC]) goto oom; strcpy(commands[AT_BC], "^SBC="); - decode_ie(cmsg->BC, commands[AT_BC]+5); + 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); + } strcpy(commands[AT_BC] + l - 2, "\r"); - } else if (cip2bchlc[cmsg->CIPValue].bc) { - l = strlen(cip2bchlc[cmsg->CIPValue].bc) + 7; - commands[AT_BC] = kmalloc(l, GFP_KERNEL); - if (!commands[AT_BC]) - goto oom; - snprintf(commands[AT_BC], l, "^SBC=%s\r", - cip2bchlc[cmsg->CIPValue].bc); - } - - /* check/encode parameter: HLC */ - if (cmsg->HLC && cmsg->HLC[0]) { - /* explicit HLC overrides CIP */ - l = 2*cmsg->HLC[0] + 7; - commands[AT_HLC] = kmalloc(l, GFP_KERNEL); - if (!commands[AT_HLC]) - goto oom; - strcpy(commands[AT_HLC], "^SHLC="); - decode_ie(cmsg->HLC, commands[AT_HLC]+5); - strcpy(commands[AT_HLC] + l - 2, "\r"); - } else if (cip2bchlc[cmsg->CIPValue].hlc) { - l = strlen(cip2bchlc[cmsg->CIPValue].hlc) + 7; - commands[AT_HLC] = kmalloc(l, GFP_KERNEL); - if (!commands[AT_HLC]) - goto oom; - snprintf(commands[AT_HLC], l, "^SHLC=%s\r", - cip2bchlc[cmsg->CIPValue].hlc); + } else { + /* no BC */ + if (lhlc) { + dev_notice(cs->dev, "%s: cannot set HLC without BC\n", + "CONNECT_REQ"); + info = CapiIllMessageParmCoding; /* ? */ + goto error; + } } /* 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"); + "B2 Protocol X.75 SLP unsupported, using Transparent\n"); } else { switch (cmsg->B1protocol) { case 0: bcs->proto2 = L2_HDLC; break; case 1: - bcs->proto2 = L2_BITSYNC; + bcs->proto2 = L2_VOICE; break; default: dev_warn(cs->dev, - "B1 Protocol %u unsupported, using Transparent\n", + "B1 Protocol %u unsupported, using Transparent\n", cmsg->B1protocol); - bcs->proto2 = L2_BITSYNC; + bcs->proto2 = L2_VOICE; } if (cmsg->B2protocol != 1) dev_warn(cs->dev, - "B2 Protocol %u unsupported, using Transparent\n", + "B2 Protocol %u unsupported, using Transparent\n", cmsg->B2protocol); if (cmsg->B3protocol != 0) dev_warn(cs->dev, - "B3 Protocol %u unsupported, using Transparent\n", + "B3 Protocol %u unsupported, using Transparent\n", cmsg->B3protocol); ignore_cstruct_param(cs, cmsg->B1configuration, - "CONNECT_REQ", "B1 Configuration"); + "CONNECT_REQ", "B1 Configuration"); ignore_cstruct_param(cs, cmsg->B2configuration, - "CONNECT_REQ", "B2 Configuration"); + "CONNECT_REQ", "B2 Configuration"); ignore_cstruct_param(cs, cmsg->B3configuration, - "CONNECT_REQ", "B3 Configuration"); + "CONNECT_REQ", "B3 Configuration"); } commands[AT_PROTO] = kmalloc(9, GFP_KERNEL); if (!commands[AT_PROTO]) @@ -1400,20 +1513,20 @@ static void do_connect_req(struct gigaset_capi_ctr *iif, /* ToDo: check/encode remaining parameters */ ignore_cstruct_param(cs, cmsg->CalledPartySubaddress, - "CONNECT_REQ", "Called pty subaddr"); + "CONNECT_REQ", "Called pty subaddr"); ignore_cstruct_param(cs, cmsg->CallingPartySubaddress, - "CONNECT_REQ", "Calling pty subaddr"); + "CONNECT_REQ", "Calling pty subaddr"); ignore_cstruct_param(cs, cmsg->LLC, - "CONNECT_REQ", "LLC"); + "CONNECT_REQ", "LLC"); if (cmsg->AdditionalInfo != CAPI_DEFAULT) { ignore_cstruct_param(cs, cmsg->BChannelinformation, - "CONNECT_REQ", "B Channel Information"); + "CONNECT_REQ", "B Channel Information"); ignore_cstruct_param(cs, cmsg->Keypadfacility, - "CONNECT_REQ", "Keypad Facility"); + "CONNECT_REQ", "Keypad Facility"); ignore_cstruct_param(cs, cmsg->Useruserdata, - "CONNECT_REQ", "User-User Data"); + "CONNECT_REQ", "User-User Data"); ignore_cstruct_param(cs, cmsg->Facilitydataarray, - "CONNECT_REQ", "Facility Data Array"); + "CONNECT_REQ", "Facility Data Array"); } /* encode parameter: B channel to use */ @@ -1425,11 +1538,11 @@ static void do_connect_req(struct gigaset_capi_ctr *iif, /* queue & schedule EV_DIAL event */ if (!gigaset_add_event(cs, &bcs->at_state, EV_DIAL, commands, - bcs->at_state.seq_index, NULL)) - goto oom; - gig_dbg(DEBUG_CMD, "scheduling DIAL"); + bcs->at_state.seq_index, NULL)) { + info = CAPI_MSGOSRESOURCEERR; + goto error; + } gigaset_schedule_event(cs); - ap->connected = APCONN_SETUP; send_conf(iif, ap, skb, CapiSuccess); return; @@ -1457,6 +1570,7 @@ static void do_connect_resp(struct gigaset_capi_ctr *iif, _cmsg *cmsg = &iif->acmsg; struct bc_state *bcs; struct gigaset_capi_appl *oap; + unsigned long flags; int channel; /* decode message */ @@ -1476,72 +1590,83 @@ static void do_connect_resp(struct gigaset_capi_ctr *iif, switch (cmsg->Reject) { case 0: /* Accept */ /* drop all competing applications, keep only this one */ - for (oap = bcs->ap; oap != NULL; oap = oap->bcnext) - if (oap != ap) + 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); send_disconnect_ind(bcs, oap, - CapiCallGivenToOtherApplication); + CapiCallGivenToOtherApplication); + spin_lock_irqsave(&bcs->aplock, flags); + } + } ap->bcnext = NULL; bcs->ap = ap; + spin_unlock_irqrestore(&bcs->aplock, flags); + + bcs->rx_bufsize = ap->rp.datablklen; + dev_kfree_skb(bcs->rx_skb); + gigaset_new_rx_skb(bcs); 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"); + "B2 Protocol X.75 SLP unsupported, using Transparent\n"); } else { switch (cmsg->B1protocol) { case 0: bcs->proto2 = L2_HDLC; break; case 1: - bcs->proto2 = L2_BITSYNC; + bcs->proto2 = L2_VOICE; break; default: dev_warn(cs->dev, - "B1 Protocol %u unsupported, using Transparent\n", + "B1 Protocol %u unsupported, using Transparent\n", cmsg->B1protocol); - bcs->proto2 = L2_BITSYNC; + bcs->proto2 = L2_VOICE; } if (cmsg->B2protocol != 1) dev_warn(cs->dev, - "B2 Protocol %u unsupported, using Transparent\n", + "B2 Protocol %u unsupported, using Transparent\n", cmsg->B2protocol); if (cmsg->B3protocol != 0) dev_warn(cs->dev, - "B3 Protocol %u unsupported, using Transparent\n", + "B3 Protocol %u unsupported, using Transparent\n", cmsg->B3protocol); ignore_cstruct_param(cs, cmsg->B1configuration, - "CONNECT_RESP", "B1 Configuration"); + "CONNECT_RESP", "B1 Configuration"); ignore_cstruct_param(cs, cmsg->B2configuration, - "CONNECT_RESP", "B2 Configuration"); + "CONNECT_RESP", "B2 Configuration"); ignore_cstruct_param(cs, cmsg->B3configuration, - "CONNECT_RESP", "B3 Configuration"); + "CONNECT_RESP", "B3 Configuration"); } /* ToDo: check/encode remaining parameters */ ignore_cstruct_param(cs, cmsg->ConnectedNumber, - "CONNECT_RESP", "Connected Number"); + "CONNECT_RESP", "Connected Number"); ignore_cstruct_param(cs, cmsg->ConnectedSubaddress, - "CONNECT_RESP", "Connected Subaddress"); + "CONNECT_RESP", "Connected Subaddress"); ignore_cstruct_param(cs, cmsg->LLC, - "CONNECT_RESP", "LLC"); + "CONNECT_RESP", "LLC"); if (cmsg->AdditionalInfo != CAPI_DEFAULT) { ignore_cstruct_param(cs, cmsg->BChannelinformation, - "CONNECT_RESP", "BChannel Information"); + "CONNECT_RESP", "BChannel Information"); ignore_cstruct_param(cs, cmsg->Keypadfacility, - "CONNECT_RESP", "Keypad Facility"); + "CONNECT_RESP", "Keypad Facility"); ignore_cstruct_param(cs, cmsg->Useruserdata, - "CONNECT_RESP", "User-User Data"); + "CONNECT_RESP", "User-User Data"); ignore_cstruct_param(cs, cmsg->Facilitydataarray, - "CONNECT_RESP", "Facility Data Array"); + "CONNECT_RESP", "Facility Data Array"); } /* Accept call */ - if (!gigaset_add_event(cs, &cs->bcs[channel-1].at_state, + if (!gigaset_add_event(cs, &cs->bcs[channel - 1].at_state, EV_ACCEPT, NULL, 0, NULL)) return; - gig_dbg(DEBUG_CMD, "scheduling ACCEPT"); gigaset_schedule_event(cs); return; @@ -1550,39 +1675,52 @@ static void do_connect_resp(struct gigaset_capi_ctr *iif, send_disconnect_ind(bcs, ap, 0); /* remove it from the list of listening apps */ + spin_lock_irqsave(&bcs->aplock, flags); if (bcs->ap == ap) { bcs->ap = ap->bcnext; - if (bcs->ap == NULL) + if (bcs->ap == NULL) { /* last one: stop ev-layer hupD notifications */ + bcs->apconnstate = APCONN_NONE; bcs->chstate &= ~CHS_NOTIFY_LL; + } + spin_unlock_irqrestore(&bcs->aplock, flags); return; } for (oap = bcs->ap; oap != NULL; oap = oap->bcnext) { if (oap->bcnext == ap) { oap->bcnext = oap->bcnext->bcnext; + spin_unlock_irqrestore(&bcs->aplock, flags); return; } } + spin_unlock_irqrestore(&bcs->aplock, flags); dev_err(cs->dev, "%s: application %u not found\n", __func__, ap->id); return; default: /* Reject */ /* drop all competing applications, keep only this one */ - for (oap = bcs->ap; oap != NULL; oap = oap->bcnext) - if (oap != ap) + 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); send_disconnect_ind(bcs, oap, - CapiCallGivenToOtherApplication); + CapiCallGivenToOtherApplication); + spin_lock_irqsave(&bcs->aplock, flags); + } + } ap->bcnext = NULL; bcs->ap = ap; + spin_unlock_irqrestore(&bcs->aplock, flags); /* 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, + if (!gigaset_add_event(cs, &cs->bcs[channel - 1].at_state, EV_HUP, NULL, 0, NULL)) return; - gig_dbg(DEBUG_CMD, "scheduling HUP"); gigaset_schedule_event(cs); return; } @@ -1598,6 +1736,7 @@ static void do_connect_b3_req(struct gigaset_capi_ctr *iif, { struct cardstate *cs = iif->ctr.driverdata; _cmsg *cmsg = &iif->acmsg; + struct bc_state *bcs; int channel; /* decode message */ @@ -1612,17 +1751,19 @@ static void do_connect_b3_req(struct gigaset_capi_ctr *iif, send_conf(iif, ap, skb, CapiIllContrPlciNcci); return; } + bcs = &cs->bcs[channel - 1]; /* mark logical connection active */ - ap->connected = APCONN_ACTIVE; + bcs->apconnstate = APCONN_ACTIVE; /* build NCCI: always 1 (one B3 connection only) */ cmsg->adr.adrNCCI |= 1 << 16; /* NCPI parameter: not applicable for B3 Transparent */ ignore_cstruct_param(cs, cmsg->NCPI, "CONNECT_B3_REQ", "NCPI"); - send_conf(iif, ap, skb, (cmsg->NCPI && cmsg->NCPI[0]) ? - CapiNcpiNotSupportedByProtocol : CapiSuccess); + send_conf(iif, ap, skb, + (cmsg->NCPI && cmsg->NCPI[0]) ? + CapiNcpiNotSupportedByProtocol : CapiSuccess); } /* @@ -1656,20 +1797,18 @@ static void do_connect_b3_resp(struct gigaset_capi_ctr *iif, dev_kfree_skb_any(skb); return; } - bcs = &cs->bcs[channel-1]; + bcs = &cs->bcs[channel - 1]; if (cmsg->Reject) { /* Reject: clear B3 connect received flag */ - ap->connected = APCONN_SETUP; + bcs->apconnstate = APCONN_SETUP; /* trigger hangup, causing eventual DISCONNECT_IND */ if (!gigaset_add_event(cs, &bcs->at_state, EV_HUP, NULL, 0, NULL)) { - dev_err(cs->dev, "%s: out of memory\n", __func__); dev_kfree_skb_any(skb); return; } - gig_dbg(DEBUG_CMD, "scheduling HUP"); gigaset_schedule_event(cs); /* emit DISCONNECT_B3_IND */ @@ -1734,11 +1873,14 @@ static void do_disconnect_req(struct gigaset_capi_ctr *iif, } /* skip if DISCONNECT_IND already sent */ - if (!ap->connected) + if (!bcs->apconnstate) return; /* check for active logical connection */ - if (ap->connected >= APCONN_ACTIVE) { + if (bcs->apconnstate >= APCONN_ACTIVE) { + /* clear it */ + bcs->apconnstate = APCONN_SETUP; + /* * emit DISCONNECT_B3_IND with cause 0x3301 * use separate cmsg structure, as the content of iif->acmsg @@ -1758,21 +1900,21 @@ static void do_disconnect_req(struct gigaset_capi_ctr *iif, if (b3skb == NULL) { dev_err(cs->dev, "%s: out of memory\n", __func__); send_conf(iif, ap, skb, CAPI_MSGOSRESOURCEERR); + kfree(b3cmsg); return; } capi_cmsg2message(b3cmsg, - __skb_put(b3skb, CAPI_DISCONNECT_B3_IND_BASELEN)); + __skb_put(b3skb, CAPI_DISCONNECT_B3_IND_BASELEN)); + dump_cmsg(DEBUG_CMD, __func__, b3cmsg); 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)) { - dev_err(cs->dev, "%s: out of memory\n", __func__); send_conf(iif, ap, skb, CAPI_MSGOSRESOURCEERR); return; } - gig_dbg(DEBUG_CMD, "scheduling HUP"); gigaset_schedule_event(cs); /* emit reply */ @@ -1789,6 +1931,7 @@ static void do_disconnect_b3_req(struct gigaset_capi_ctr *iif, { struct cardstate *cs = iif->ctr.driverdata; _cmsg *cmsg = &iif->acmsg; + struct bc_state *bcs; int channel; /* decode message */ @@ -1804,29 +1947,28 @@ static void do_disconnect_b3_req(struct gigaset_capi_ctr *iif, send_conf(iif, ap, skb, CapiIllContrPlciNcci); return; } + bcs = &cs->bcs[channel - 1]; /* reject if logical connection not active */ - if (ap->connected < APCONN_ACTIVE) { + if (bcs->apconnstate < APCONN_ACTIVE) { send_conf(iif, ap, skb, CapiMessageNotSupportedInCurrentState); return; } /* trigger hangup, causing eventual DISCONNECT_B3_IND */ - if (!gigaset_add_event(cs, &cs->bcs[channel-1].at_state, - EV_HUP, NULL, 0, NULL)) { - dev_err(cs->dev, "%s: out of memory\n", __func__); + if (!gigaset_add_event(cs, &bcs->at_state, EV_HUP, NULL, 0, NULL)) { send_conf(iif, ap, skb, CAPI_MSGOSRESOURCEERR); return; } - gig_dbg(DEBUG_CMD, "scheduling HUP"); gigaset_schedule_event(cs); /* NCPI parameter: not applicable for B3 Transparent */ ignore_cstruct_param(cs, cmsg->NCPI, - "DISCONNECT_B3_REQ", "NCPI"); - send_conf(iif, ap, skb, (cmsg->NCPI && cmsg->NCPI[0]) ? - CapiNcpiNotSupportedByProtocol : CapiSuccess); + "DISCONNECT_B3_REQ", "NCPI"); + send_conf(iif, ap, skb, + (cmsg->NCPI && cmsg->NCPI[0]) ? + CapiNcpiNotSupportedByProtocol : CapiSuccess); } /* @@ -1837,18 +1979,17 @@ static void do_data_b3_req(struct gigaset_capi_ctr *iif, struct sk_buff *skb) { struct cardstate *cs = iif->ctr.driverdata; + struct bc_state *bcs; 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); + u16 msgid = CAPIMSG_MSGID(skb->data); + u16 handle = CAPIMSG_HANDLE_REQ(skb->data); /* frequent message, avoid _cmsg overhead */ - dump_rawmsg(DEBUG_LLDATA, "DATA_B3_REQ", skb->data); - - gig_dbg(DEBUG_LLDATA, - "Receiving data from LL (ch: %d, flg: %x, sz: %d|%d)", - channel, flags, msglen, datalen); + dump_rawmsg(DEBUG_MCMD, __func__, skb->data); /* check parameters */ if (channel == 0 || channel > cs->channels || ncci != 1) { @@ -1857,6 +1998,7 @@ static void do_data_b3_req(struct gigaset_capi_ctr *iif, send_conf(iif, ap, skb, CapiIllContrPlciNcci); return; } + bcs = &cs->bcs[channel - 1]; 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); @@ -1876,7 +2018,7 @@ static void do_data_b3_req(struct gigaset_capi_ctr *iif, } /* reject if logical connection not active */ - if (ap->connected < APCONN_ACTIVE) { + if (bcs->apconnstate < APCONN_ACTIVE) { send_conf(iif, ap, skb, CapiMessageNotSupportedInCurrentState); return; } @@ -1887,17 +2029,19 @@ static void do_data_b3_req(struct gigaset_capi_ctr *iif, skb_pull(skb, msglen); /* pass to device-specific module */ - if (cs->ops->send_skb(&cs->bcs[channel-1], skb) < 0) { + if (cs->ops->send_skb(bcs, skb) < 0) { send_conf(iif, ap, skb, CAPI_MSGOSRESOURCEERR); return; } - /* DATA_B3_CONF reply will be sent by gigaset_skb_sent() */ - /* - * ToDo: honor unset "delivery confirmation" bit - * (send DATA_B3_CONF immediately?) + * DATA_B3_CONF will be sent by gigaset_skb_sent() only if "delivery + * confirmation" bit is set; otherwise we have to send it now */ + if (!(flags & CAPI_FLAGS_DELIVERY_CONFIRMATION)) + send_data_b3_conf(cs, &iif->ctr, ap->id, msgid, channel, handle, + flags ? CapiFlagsNotSupportedByProtocol + : CAPI_NOERROR); } /* @@ -1916,12 +2060,6 @@ static void do_reset_b3_req(struct gigaset_capi_ctr *iif, } /* - * 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, @@ -1930,8 +2068,7 @@ static void do_unsupported(struct gigaset_capi_ctr *iif, { /* 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); + dump_cmsg(DEBUG_CMD, __func__, &iif->acmsg); send_conf(iif, ap, skb, CapiMessageNotSupportedInCurrentState); } @@ -1942,11 +2079,9 @@ 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); - } + /* decode message */ + capi_message2cmsg(&iif->acmsg, skb->data); + dump_cmsg(DEBUG_CMD, __func__, &iif->acmsg); dev_kfree_skb_any(skb); } @@ -1954,7 +2089,7 @@ static void do_data_b3_resp(struct gigaset_capi_ctr *iif, struct gigaset_capi_appl *ap, struct sk_buff *skb) { - dump_rawmsg(DEBUG_LLDATA, __func__, skb->data); + dump_rawmsg(DEBUG_MCMD, __func__, skb->data); dev_kfree_skb_any(skb); } @@ -2106,35 +2241,22 @@ static char *gigaset_procinfo(struct capi_ctr *ctr) return ctr->name; /* ToDo: more? */ } -/** - * gigaset_ctr_read_proc() - build controller proc file entry - * @page: buffer of PAGE_SIZE bytes for receiving the entry. - * @start: unused. - * @off: unused. - * @count: unused. - * @eof: unused. - * @ctr: controller descriptor structure. - * - * Return value: length of generated entry - */ -static int gigaset_ctr_read_proc(char *page, char **start, off_t off, - int count, int *eof, struct capi_ctr *ctr) +static int gigaset_proc_show(struct seq_file *m, void *v) { + struct capi_ctr *ctr = m->private; struct cardstate *cs = ctr->driverdata; char *s; int i; - int len = 0; - len += sprintf(page+len, "%-16s %s\n", "name", ctr->name); - len += sprintf(page+len, "%-16s %s %s\n", "dev", - dev_driver_string(cs->dev), dev_name(cs->dev)); - len += sprintf(page+len, "%-16s %d\n", "id", cs->myid); + + seq_printf(m, "%-16s %s\n", "name", ctr->name); + seq_printf(m, "%-16s %s %s\n", "dev", + dev_driver_string(cs->dev), dev_name(cs->dev)); + seq_printf(m, "%-16s %d\n", "id", cs->myid); if (cs->gotfwver) - len += sprintf(page+len, "%-16s %d.%d.%d.%d\n", "firmware", - cs->fwver[0], cs->fwver[1], cs->fwver[2], cs->fwver[3]); - len += sprintf(page+len, "%-16s %d\n", "channels", - cs->channels); - len += sprintf(page+len, "%-16s %s\n", "onechannel", - cs->onechannel ? "yes" : "no"); + seq_printf(m, "%-16s %d.%d.%d.%d\n", "firmware", + cs->fwver[0], cs->fwver[1], cs->fwver[2], cs->fwver[3]); + seq_printf(m, "%-16s %d\n", "channels", cs->channels); + seq_printf(m, "%-16s %s\n", "onechannel", cs->onechannel ? "yes" : "no"); switch (cs->mode) { case M_UNKNOWN: @@ -2152,7 +2274,7 @@ static int gigaset_ctr_read_proc(char *page, char **start, off_t off, default: s = "??"; } - len += sprintf(page+len, "%-16s %s\n", "mode", s); + seq_printf(m, "%-16s %s\n", "mode", s); switch (cs->mstate) { case MS_UNINITIALIZED: @@ -2176,26 +2298,22 @@ static int gigaset_ctr_read_proc(char *page, char **start, off_t off, default: s = "??"; } - len += sprintf(page+len, "%-16s %s\n", "mstate", s); + seq_printf(m, "%-16s %s\n", "mstate", s); - len += sprintf(page+len, "%-16s %s\n", "running", - cs->running ? "yes" : "no"); - len += sprintf(page+len, "%-16s %s\n", "connected", - cs->connected ? "yes" : "no"); - len += sprintf(page+len, "%-16s %s\n", "isdn_up", - cs->isdn_up ? "yes" : "no"); - len += sprintf(page+len, "%-16s %s\n", "cidmode", - cs->cidmode ? "yes" : "no"); + 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"); for (i = 0; i < cs->channels; i++) { - len += sprintf(page+len, "[%d]%-13s %d\n", i, "corrupted", - cs->bcs[i].corrupted); - len += sprintf(page+len, "[%d]%-13s %d\n", i, "trans_down", - cs->bcs[i].trans_down); - len += sprintf(page+len, "[%d]%-13s %d\n", i, "trans_up", - cs->bcs[i].trans_up); - len += sprintf(page+len, "[%d]%-13s %d\n", i, "chstate", - cs->bcs[i].chstate); + seq_printf(m, "[%d]%-13s %d\n", i, "corrupted", + cs->bcs[i].corrupted); + seq_printf(m, "[%d]%-13s %d\n", i, "trans_down", + cs->bcs[i].trans_down); + seq_printf(m, "[%d]%-13s %d\n", i, "trans_up", + cs->bcs[i].trans_up); + seq_printf(m, "[%d]%-13s %d\n", i, "chstate", + cs->bcs[i].chstate); switch (cs->bcs[i].proto2) { case L2_BITSYNC: s = "bitsync"; @@ -2209,54 +2327,54 @@ static int gigaset_ctr_read_proc(char *page, char **start, off_t off, default: s = "??"; } - len += sprintf(page+len, "[%d]%-13s %s\n", i, "proto2", s); + seq_printf(m, "[%d]%-13s %s\n", i, "proto2", s); } - return len; + return 0; } +static int gigaset_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, gigaset_proc_show, PDE_DATA(inode)); +} -static struct capi_driver capi_driver_gigaset = { - .name = "gigaset", - .revision = "1.0", +static const struct file_operations gigaset_proc_fops = { + .owner = THIS_MODULE, + .open = gigaset_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, }; /** - * gigaset_isdn_register() - register to LL + * gigaset_isdn_regdev() - register device to LL * @cs: device descriptor structure. * @isdnid: device name. * - * Called by main module to register the device with the LL. - * - * Return value: 1 for success, 0 for failure + * Return value: 0 on success, error code < 0 on failure */ -int gigaset_isdn_register(struct cardstate *cs, const char *isdnid) +int gigaset_isdn_regdev(struct cardstate *cs, const char *isdnid) { struct gigaset_capi_ctr *iif; int rc; - pr_info("Kernel CAPI interface\n"); - iif = kmalloc(sizeof(*iif), GFP_KERNEL); if (!iif) { pr_err("%s: out of memory\n", __func__); - return 0; + return -ENOMEM; } - /* register driver with CAPI (ToDo: what for?) */ - register_capi_driver(&capi_driver_gigaset); - /* 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"; - iif->ctr.load_firmware = gigaset_load_firmware; - iif->ctr.reset_ctr = gigaset_reset_ctr; + iif->ctr.load_firmware = NULL; + iif->ctr.reset_ctr = NULL; 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; - iif->ctr.ctr_read_proc = gigaset_ctr_read_proc; + iif->ctr.proc_fops = &gigaset_proc_fops; INIT_LIST_HEAD(&iif->appls); skb_queue_head_init(&iif->sendqueue); atomic_set(&iif->sendqlen, 0); @@ -2265,28 +2383,46 @@ int gigaset_isdn_register(struct cardstate *cs, const char *isdnid) rc = attach_capi_ctr(&iif->ctr); if (rc) { pr_err("attach_capi_ctr failed (%d)\n", rc); - unregister_capi_driver(&capi_driver_gigaset); kfree(iif); - return 0; + return rc; } cs->iif = iif; cs->hw_hdr_len = CAPI_DATA_B3_REQ_LEN; - return 1; + return 0; } /** - * gigaset_isdn_unregister() - unregister from LL + * gigaset_isdn_unregdev() - unregister device from LL * @cs: device descriptor structure. - * - * Called by main module to unregister the device from the LL. */ -void gigaset_isdn_unregister(struct cardstate *cs) +void gigaset_isdn_unregdev(struct cardstate *cs) { struct gigaset_capi_ctr *iif = cs->iif; detach_capi_ctr(&iif->ctr); kfree(iif); cs->iif = NULL; +} + +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) +{ unregister_capi_driver(&capi_driver_gigaset); } |
