/* * (CAPI*) * * An implementation of Common ISDN API 2.0 for Asterisk * * Copyright (C) 2006-2007 Cytronics & Melware * * Armin Schindler * * This program is free software and may be modified and * distributed under the terms of the GNU Public License. */ #include #include #include "chan_capi20.h" #include "chan_capi.h" #include "chan_capi_utils.h" #include "chan_capi_supplementary.h" int capidebug = 0; char *emptyid = "\0"; AST_MUTEX_DEFINE_STATIC(verbose_lock); AST_MUTEX_DEFINE_STATIC(messagenumber_lock); AST_MUTEX_DEFINE_STATIC(capi_put_lock); static _cword capi_MessageNumber; #define CAPI_MAX_PEERLINKCHANNELS 32 static struct ast_channel *peerlinkchannel[CAPI_MAX_PEERLINKCHANNELS]; /* * helper for _verbose with different verbose settings */ void cc_verbose(int o_v, int c_d, char *text, ...) { char line[4096]; va_list ap; va_start(ap, text); vsnprintf(line, sizeof(line), text, ap); va_end(ap); if ((o_v == 0) || (option_verbose > o_v)) { if ((!c_d) || ((c_d) && (capidebug))) { cc_mutex_lock(&verbose_lock); cc_pbx_verbose(line); cc_mutex_unlock(&verbose_lock); } } } /* * get a new capi message number automically */ _cword get_capi_MessageNumber(void) { _cword mn; cc_mutex_lock(&messagenumber_lock); capi_MessageNumber++; if (capi_MessageNumber == 0) { /* avoid zero */ capi_MessageNumber = 1; } mn = capi_MessageNumber; cc_mutex_unlock(&messagenumber_lock); return(mn); } /* * write a capi message to capi device */ MESSAGE_EXCHANGE_ERROR _capi_put_cmsg(_cmsg *CMSG) { MESSAGE_EXCHANGE_ERROR error; if (cc_mutex_lock(&capi_put_lock)) { cc_log(LOG_WARNING, "Unable to lock capi put!\n"); return -1; } error = capi20_put_cmsg(CMSG); if (cc_mutex_unlock(&capi_put_lock)) { cc_log(LOG_WARNING, "Unable to unlock capi put!\n"); return -1; } if (error) { cc_log(LOG_ERROR, "CAPI error sending %s (NCCI=%#x) (error=%#x %s)\n", capi_cmsg2str(CMSG), (unsigned int)HEADER_CID(CMSG), error, capi_info_string((unsigned int)error)); } else { unsigned short wCmd = HEADER_CMD(CMSG); if ((wCmd == CAPI_P_REQ(DATA_B3)) || (wCmd == CAPI_P_RESP(DATA_B3))) { cc_verbose(7, 1, "%s\n", capi_cmsg2str(CMSG)); } else { cc_verbose(4, 1, "%s\n", capi_cmsg2str(CMSG)); } } return error; } /* * wait some time for a new capi message */ MESSAGE_EXCHANGE_ERROR capidev_check_wait_get_cmsg(_cmsg *CMSG) { MESSAGE_EXCHANGE_ERROR Info; struct timeval tv; repeat: Info = capi_get_cmsg(CMSG, capi_ApplID); #if (CAPI_OS_HINT == 1) || (CAPI_OS_HINT == 2) /* * For BSD allow controller 0: */ if ((HEADER_CID(CMSG) & 0xFF) == 0) { HEADER_CID(CMSG) += capi_num_controllers; } #endif /* if queue is empty */ if (Info == 0x1104) { /* try waiting a maximum of 0.100 seconds for a message */ tv.tv_sec = 0; tv.tv_usec = 10000; Info = capi20_waitformessage(capi_ApplID, &tv); if (Info == 0x0000) goto repeat; } if ((Info != 0x0000) && (Info != 0x1104)) { if (capidebug) { cc_log(LOG_DEBUG, "Error waiting for cmsg... INFO = %#x\n", Info); } } return Info; } /* * decode capi 2.0 info word */ char *capi_info_string(unsigned int info) { switch (info) { /* informative values (corresponding message was processed) */ case 0x0001: return "NCPI not supported by current protocol, NCPI ignored"; case 0x0002: return "Flags not supported by current protocol, flags ignored"; case 0x0003: return "Alert already sent by another application"; /* error information concerning CAPI_REGISTER */ case 0x1001: return "Too many applications"; case 0x1002: return "Logical block size to small, must be at least 128 Bytes"; case 0x1003: return "Buffer exceeds 64 kByte"; case 0x1004: return "Message buffer size too small, must be at least 1024 Bytes"; case 0x1005: return "Max. number of logical connections not supported"; case 0x1006: return "Reserved"; case 0x1007: return "The message could not be accepted because of an internal busy condition"; case 0x1008: return "OS resource error (no memory ?)"; case 0x1009: return "CAPI not installed"; case 0x100A: return "Controller does not support external equipment"; case 0x100B: return "Controller does only support external equipment"; /* error information concerning message exchange functions */ case 0x1101: return "Illegal application number"; case 0x1102: return "Illegal command or subcommand or message length less than 12 bytes"; case 0x1103: return "The message could not be accepted because of a queue full condition !! The error code does not imply that CAPI cannot receive messages directed to another controller, PLCI or NCCI"; case 0x1104: return "Queue is empty"; case 0x1105: return "Queue overflow, a message was lost !! This indicates a configuration error. The only recovery from this error is to perform a CAPI_RELEASE"; case 0x1106: return "Unknown notification parameter"; case 0x1107: return "The Message could not be accepted because of an internal busy condition"; case 0x1108: return "OS Resource error (no memory ?)"; case 0x1109: return "CAPI not installed"; case 0x110A: return "Controller does not support external equipment"; case 0x110B: return "Controller does only support external equipment"; /* error information concerning resource / coding problems */ case 0x2001: return "Message not supported in current state"; case 0x2002: return "Illegal Controller / PLCI / NCCI"; case 0x2003: return "Out of PLCIs"; case 0x2004: return "Out of NCCIs"; case 0x2005: return "Out of LISTEN requests"; case 0x2006: return "Out of FAX resources (protocol T.30)"; case 0x2007: return "Illegal message parameter coding"; /* error information concerning requested services */ case 0x3001: return "B1 protocol not supported"; case 0x3002: return "B2 protocol not supported"; case 0x3003: return "B3 protocol not supported"; case 0x3004: return "B1 protocol parameter not supported"; case 0x3005: return "B2 protocol parameter not supported"; case 0x3006: return "B3 protocol parameter not supported"; case 0x3007: return "B protocol combination not supported"; case 0x3008: return "NCPI not supported"; case 0x3009: return "CIP Value unknown"; case 0x300A: return "Flags not supported (reserved bits)"; case 0x300B: return "Facility not supported"; case 0x300C: return "Data length not supported by current protocol"; case 0x300D: return "Reset procedure not supported by current protocol"; case 0x300E: return "TEI assignment failed or supplementary service not supported"; case 0x3010: return "Request not allowed in this state"; /* informations about the clearing of a physical connection */ case 0x3301: return "Protocol error layer 1 (broken line or B-channel removed by signalling protocol)"; case 0x3302: return "Protocol error layer 2"; case 0x3303: return "Protocol error layer 3"; case 0x3304: return "Another application got that call"; /* T.30 specific reasons */ case 0x3311: return "Connecting not successful (remote station is no FAX G3 machine)"; case 0x3312: return "Connecting not successful (training error)"; case 0x3313: return "Disconnected before transfer (remote station does not support transfer mode, e.g. resolution)"; case 0x3314: return "Disconnected during transfer (remote abort)"; case 0x3315: return "Disconnected during transfer (remote procedure error, e.g. unsuccessful repetition of T.30 commands)"; case 0x3316: return "Disconnected during transfer (local tx data underrun)"; case 0x3317: return "Disconnected during transfer (local rx data overflow)"; case 0x3318: return "Disconnected during transfer (local abort)"; case 0x3319: return "Illegal parameter coding (e.g. SFF coding error)"; /* disconnect causes from the network according to ETS 300 102-1/Q.931 */ case 0x3481: return "Unallocated (unassigned) number"; case 0x3482: return "No route to specified transit network"; case 0x3483: return "No route to destination"; case 0x3486: return "Channel unacceptable"; case 0x3487: return "Call awarded and being delivered in an established channel"; case 0x3490: return "Normal call clearing"; case 0x3491: return "User busy"; case 0x3492: return "No user responding"; case 0x3493: return "No answer from user (user alerted)"; case 0x3495: return "Call rejected"; case 0x3496: return "Number changed"; case 0x349A: return "Non-selected user clearing"; case 0x349B: return "Destination out of order"; case 0x349C: return "Invalid number format"; case 0x349D: return "Facility rejected"; case 0x349E: return "Response to STATUS ENQUIRY"; case 0x349F: return "Normal, unspecified"; case 0x34A2: return "No circuit / channel available"; case 0x34A6: return "Network out of order"; case 0x34A9: return "Temporary failure"; case 0x34AA: return "Switching equipment congestion"; case 0x34AB: return "Access information discarded"; case 0x34AC: return "Requested circuit / channel not available"; case 0x34AF: return "Resources unavailable, unspecified"; case 0x34B1: return "Quality of service unavailable"; case 0x34B2: return "Requested facility not subscribed"; case 0x34B9: return "Bearer capability not authorized"; case 0x34BA: return "Bearer capability not presently available"; case 0x34BF: return "Service or option not available, unspecified"; case 0x34C1: return "Bearer capability not implemented"; case 0x34C2: return "Channel type not implemented"; case 0x34C5: return "Requested facility not implemented"; case 0x34C6: return "Only restricted digital information bearer capability is available"; case 0x34CF: return "Service or option not implemented, unspecified"; case 0x34D1: return "Invalid call reference value"; case 0x34D2: return "Identified channel does not exist"; case 0x34D3: return "A suspended call exists, but this call identity does not"; case 0x34D4: return "Call identity in use"; case 0x34D5: return "No call suspended"; case 0x34D6: return "Call having the requested call identity has been cleared"; case 0x34D8: return "Incompatible destination"; case 0x34DB: return "Invalid transit network selection"; case 0x34DF: return "Invalid message, unspecified"; case 0x34E0: return "Mandatory information element is missing"; case 0x34E1: return "Message type non-existent or not implemented"; case 0x34E2: return "Message not compatible with call state or message type non-existent or not implemented"; case 0x34E3: return "Information element non-existent or not implemented"; case 0x34E4: return "Invalid information element contents"; case 0x34E5: return "Message not compatible with call state"; case 0x34E6: return "Recovery on timer expiry"; case 0x34EF: return "Protocol error, unspecified"; case 0x34FF: return "Interworking, unspecified"; /* B3 protocol 7 (Modem) */ case 0x3500: return "Normal end of connection"; case 0x3501: return "Carrier lost"; case 0x3502: return "Error on negotiation, i.e. no modem with error correction at other end"; case 0x3503: return "No answer to protocol request"; case 0x3504: return "Remote modem only works in synchronous mode"; case 0x3505: return "Framing fails"; case 0x3506: return "Protocol negotiation fails"; case 0x3507: return "Other modem sends wrong protocol request"; case 0x3508: return "Sync information (data or flags) missing"; case 0x3509: return "Normal end of connection from the other modem"; case 0x350a: return "No answer from other modem"; case 0x350b: return "Protocol error"; case 0x350c: return "Error on compression"; case 0x350d: return "No connect (timeout or wrong modulation)"; case 0x350e: return "No protocol fall-back allowed"; case 0x350f: return "No modem or fax at requested number"; case 0x3510: return "Handshake error"; /* error info concerning the requested supplementary service */ case 0x3600: return "Supplementary service not subscribed"; case 0x3603: return "Supplementary service not available"; case 0x3604: return "Supplementary service not implemented"; case 0x3606: return "Invalid served user number"; case 0x3607: return "Invalid call state"; case 0x3608: return "Basic service not provided"; case 0x3609: return "Supplementary service not requested for an incoming call"; case 0x360a: return "Supplementary service interaction not allowed"; case 0x360b: return "Resource unavailable"; /* error info concerning the context of a supplementary service request */ case 0x3700: return "Duplicate invocation"; case 0x3701: return "Unrecognized operation"; case 0x3702: return "Mistyped argument"; case 0x3703: return "Resource limitation"; case 0x3704: return "Initiator releasing"; case 0x3705: return "Unrecognized linked-ID"; case 0x3706: return "Linked response unexpected"; case 0x3707: return "Unexpected child operation"; /* Line Interconnect */ case 0x3800: return "PLCI has no B-channel"; case 0x3801: return "Lines not compatible"; case 0x3802: return "PLCI(s) is (are) not in any or not in the same interconnection"; default: return NULL; } } /* * show the text for a CAPI message info value */ void show_capi_info(struct capi_pvt *i, _cword info) { char *p; char *name = "?"; if (info == 0x0000) { /* no error, do nothing */ return; } if (!(p = capi_info_string((unsigned int)info))) { /* message not available */ return; } if (i) name = i->vname; cc_verbose(3, 0, VERBOSE_PREFIX_4 "%s: CAPI INFO 0x%04x: %s\n", name, info, p); return; } /* * send Listen to specified controller */ unsigned ListenOnController(unsigned long CIPmask, unsigned controller) { MESSAGE_EXCHANGE_ERROR error; _cmsg CMSG; int waitcount = 50; LISTEN_REQ_HEADER(&CMSG, capi_ApplID, get_capi_MessageNumber(), controller); LISTEN_REQ_INFOMASK(&CMSG) = 0xffff; /* lots of info ;) + early B3 connect */ /* 0x00ff if no early B3 should be done */ LISTEN_REQ_CIPMASK(&CMSG) = CIPmask; error = _capi_put_cmsg(&CMSG); if (error) goto done; while (waitcount) { error = capidev_check_wait_get_cmsg(&CMSG); if (IS_LISTEN_CONF(&CMSG)) { error = LISTEN_CONF_INFO(&CMSG); ListenOnSupplementary(controller); break; } usleep(30000); waitcount--; } if (!waitcount) error = 0x100F; done: return error; } /* * convert a number */ char *capi_number_func(unsigned char *data, unsigned int strip, char *buf) { unsigned int len; if (data[0] == 0xff) { len = read_capi_word(&data[1]); data += 2; } else { len = data[0]; data += 1; } if (len > (AST_MAX_EXTENSION - 1)) len = (AST_MAX_EXTENSION - 1); /* convert a capi struct to a \0 terminated string */ if ((!len) || (len < strip)) return NULL; len = len - strip; data += strip; memcpy(buf, data, len); buf[len] = '\0'; return buf; } /* * parse the dialstring */ void parse_dialstring(char *buffer, char **interface, char **dest, char **param, char **ocid) { int cp = 0; char *buffer_p = buffer; char *oc; /* interface is the first part of the string */ *interface = buffer; *dest = emptyid; *param = emptyid; *ocid = NULL; while (*buffer_p) { if (*buffer_p == '/') { *buffer_p = 0; buffer_p++; if (cp == 0) { *dest = buffer_p; cp++; } else if (cp == 1) { *param = buffer_p; cp++; } else { cc_log(LOG_WARNING, "Too many parts in dialstring '%s'\n", buffer); } continue; } buffer_p++; } if ((oc = strchr(*dest, ':')) != NULL) { *ocid = *dest; *oc = '\0'; *dest = oc + 1; } cc_verbose(3, 1, VERBOSE_PREFIX_4 "parsed dialstring: '%s' '%s' '%s' '%s'\n", *interface, (*ocid) ? *ocid : "NULL", *dest, *param); return; } /* * Add a new peer link id */ int cc_add_peer_link_id(struct ast_channel *c) { int a; for (a = 0; a < CAPI_MAX_PEERLINKCHANNELS; a++) { if (peerlinkchannel[a] == NULL) { peerlinkchannel[a] = c; break; } } if (a == CAPI_MAX_PEERLINKCHANNELS) { return -1; } return a; } /* * Get and remove peer link id */ struct ast_channel *cc_get_peer_link_id(const char *p) { int id = -1; struct ast_channel *chan = NULL; if (p) { id = (int)strtol(p, NULL, 0); } if ((id >= 0) && (id < CAPI_MAX_PEERLINKCHANNELS)) { chan = peerlinkchannel[id]; peerlinkchannel[id] = NULL; } return chan; }