/* * (CAPI*) * * An implementation of Common ISDN API 2.0 for Asterisk * * Copyright (C) 2006-2007 Cytronics & Melware * * Armin Schindler * * capi_sendf() by Eicon Networks / Dialogic * * This program is free software and may be modified and * distributed under the terms of the GNU Public License. */ #include #include #include #include #include #include #include "xlaw.h" #include "chan_capi20.h" #include "chan_capi.h" #include "chan_capi_rtp.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); AST_MUTEX_DEFINE_STATIC(peerlink_lock); AST_MUTEX_DEFINE_STATIC(nullif_lock); static _cword capi_MessageNumber; static struct capi_pvt *nulliflist = NULL; static int controller_nullplcis[CAPI_MAX_CONTROLLERS]; #define CAPI_MAX_PEERLINKCHANNELS 32 static struct peerlink_s { struct ast_channel *channel; time_t age; } 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 0 { FILE *fp; if ((fp = fopen("/tmp/cclog", "a")) != NULL) { fprintf(fp, "%s", line); fclose(fp); } } #endif 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); } } } /* * hangup and remove null-interface */ void capi_remove_nullif(struct capi_pvt *i) { struct capi_pvt *ii; struct capi_pvt *tmp = NULL; int state; if (i->channeltype != CAPI_CHANNELTYPE_NULL) { return; } if (i->PLCI != 0) { /* if the interface is in use, hangup first */ cc_mutex_lock(&i->lock); state = i->state; i->state = CAPI_STATE_DISCONNECTING; capi_activehangup(i, state); cc_mutex_unlock(&i->lock); return; } cc_mutex_lock(&nullif_lock); ii = nulliflist; while (ii) { if (ii == i) { if (!tmp) { nulliflist = ii->next; } else { tmp->next = ii->next; } cc_verbose(3, 1, VERBOSE_PREFIX_4 "%s: removed null-interface from controller %d.\n", i->vname, i->controller); if (i->smoother) ast_smoother_free(i->smoother); cc_mutex_destroy(&i->lock); ast_cond_destroy(&i->event_trigger); controller_nullplcis[i->controller - 1]--; free(i); break; } tmp = ii; ii = ii->next; } cc_mutex_unlock(&nullif_lock); } /* * create new null-interface */ struct capi_pvt *capi_mknullif(struct ast_channel *c, unsigned long controllermask) { struct capi_pvt *tmp; unsigned int controller = 1; int contrcount; int channelcount = 0xffff; /* find the next controller of mask with least plcis used */ for (contrcount = 0; contrcount < CAPI_MAX_CONTROLLERS; contrcount++) { if ((controllermask & (1 << contrcount))) { if (controller_nullplcis[contrcount] < channelcount) { channelcount = controller_nullplcis[contrcount]; controller = contrcount + 1; } } } tmp = malloc(sizeof(struct capi_pvt)); if (!tmp) { return NULL; } memset(tmp, 0, sizeof(struct capi_pvt)); cc_mutex_init(&tmp->lock); ast_cond_init(&tmp->event_trigger, NULL); snprintf(tmp->name, sizeof(tmp->name) - 1, "%s-NULLPLCI", c->name); snprintf(tmp->vname, sizeof(tmp->vname) - 1, "%s", tmp->name); tmp->channeltype = CAPI_CHANNELTYPE_NULL; tmp->used = c; tmp->peer = c; tmp->cip = CAPI_CIPI_SPEECH; tmp->transfercapability = PRI_TRANS_CAP_SPEECH; tmp->controller = controller; tmp->doEC = 1; tmp->doEC_global = 1; tmp->ecOption = EC_OPTION_DISABLE_NEVER; tmp->ecTail = EC_DEFAULT_TAIL; tmp->isdnmode = CAPI_ISDNMODE_MSN; tmp->ecSelector = FACILITYSELECTOR_ECHO_CANCEL; tmp->capability = capi_capability; tmp->rxgain = 1.0; tmp->txgain = 1.0; capi_gains(&tmp->g, 1.0, 1.0); if (!(capi_create_reader_writer_pipe(tmp))) { free(tmp); return NULL; } tmp->bproto = CC_BPROTO_TRANSPARENT; tmp->doB3 = CAPI_B3_DONT; tmp->smoother = ast_smoother_new(CAPI_MAX_B3_BLOCK_SIZE); tmp->isdnstate |= CAPI_ISDN_STATE_PBX; cc_mutex_lock(&nullif_lock); tmp->next = nulliflist; /* prepend */ nulliflist = tmp; controller_nullplcis[tmp->controller - 1]++; cc_mutex_unlock(&nullif_lock); /* connect to driver */ tmp->outgoing = 1; tmp->state = CAPI_STATE_CONNECTPENDING; tmp->MessageNumber = get_capi_MessageNumber(); capi_sendf(NULL, 0, CAPI_CONNECT_REQ, controller, tmp->MessageNumber, "w()()()()(www()()()())()()()((wwbbb)()()())", 0, 1,1,0, 3,0,0,0,0); cc_verbose(3, 1, VERBOSE_PREFIX_4 "%s: created null-interface on controller %d.\n", tmp->vname, tmp->controller); return tmp; } /* * get a new capi message number atomically */ _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); } /* * find the interface (pvt) the PLCI belongs to */ struct capi_pvt *capi_find_interface_by_plci(unsigned int plci) { struct capi_pvt *i; if (plci == 0) return NULL; for (i = capi_iflist; i; i = i->next) { if (i->PLCI == plci) return i; } cc_mutex_lock(&nullif_lock); for (i = nulliflist; i; i = i->next) { if (i->PLCI == plci) break; } cc_mutex_unlock(&nullif_lock); return i; } /* * find the interface (pvt) the messagenumber belongs to */ struct capi_pvt *capi_find_interface_by_msgnum(unsigned short msgnum) { struct capi_pvt *i; if (msgnum == 0x0000) return NULL; for (i = capi_iflist; i; i = i->next) { if ((i->PLCI == 0) && (i->MessageNumber == msgnum)) return i; } cc_mutex_lock(&nullif_lock); for (i = nulliflist; i; i = i->next) { if ((i->PLCI == 0) && (i->MessageNumber == msgnum)) break; } cc_mutex_unlock(&nullif_lock); return i; } /* * wait for a specific message */ MESSAGE_EXCHANGE_ERROR capi_wait_conf(struct capi_pvt *i, unsigned short wCmd) { MESSAGE_EXCHANGE_ERROR error = 0; struct timespec abstime; unsigned char command, subcommand; subcommand = wCmd & 0xff; command = (wCmd & 0xff00) >> 8; i->waitevent = (unsigned int)wCmd; abstime.tv_sec = time(NULL) + 2; abstime.tv_nsec = 0; cc_verbose(4, 1, "%s: wait for %s (0x%x)\n", i->vname, capi_cmd2str(command, subcommand), i->waitevent); if (ast_cond_timedwait(&i->event_trigger, &i->lock, &abstime) != 0) { error = -1; cc_log(LOG_WARNING, "%s: timed out waiting for %s\n", i->vname, capi_cmd2str(command, subcommand)); } else { cc_verbose(4, 1, "%s: cond signal received for %s\n", i->vname, capi_cmd2str(command, subcommand)); } return error; } /* * log an error in sending capi message */ static void log_capi_error_message(MESSAGE_EXCHANGE_ERROR err, _cmsg *CMSG) { if (err) { cc_log(LOG_ERROR, "CAPI error sending %s (NCCI=%#x) (error=%#x %s)\n", capi_cmsg2str(CMSG), (unsigned int)HEADER_CID(CMSG), err, capi_info_string((unsigned int)err)); } } /* * log verbose a capi message */ static void log_capi_message(_cmsg *CMSG) { unsigned short wCmd; 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)); } } /* * write a capi message to capi device */ static MESSAGE_EXCHANGE_ERROR _capi_put_msg(unsigned char *msg) { MESSAGE_EXCHANGE_ERROR error; _cmsg CMSG; if (cc_mutex_lock(&capi_put_lock)) { cc_log(LOG_WARNING, "Unable to lock capi put!\n"); return -1; } capi_message2cmsg(&CMSG, msg); log_capi_message(&CMSG); error = capi20_put_message(capi_ApplID, msg); if (cc_mutex_unlock(&capi_put_lock)) { cc_log(LOG_WARNING, "Unable to unlock capi put!\n"); return -1; } log_capi_error_message(error, &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; tv.tv_sec = 0; tv.tv_usec = 500000; Info = capi20_waitformessage(capi_ApplID, &tv); if (Info == 0x0000) { Info = capi_get_cmsg(CMSG, capi_ApplID); #if (CAPI_OS_HINT == 1) || (CAPI_OS_HINT == 2) if (Info == 0x0000) { /* * For BSD allow controller 0: */ if ((HEADER_CID(CMSG) & 0xFF) == 0) { HEADER_CID(CMSG) += capi_num_controllers; } } #endif } if ((Info != 0x0000) && (Info != 0x1104)) { if (capidebug) { cc_log(LOG_DEBUG, "Error waiting for cmsg... INFO = %#x\n", Info); } } return Info; } /* * Eicon's capi_sendf() function to create capi messages easily * and send this message. * Copyright by Eicon Networks / Dialogic */ MESSAGE_EXCHANGE_ERROR capi_sendf( struct capi_pvt *capii, int waitconf, _cword command, _cdword Id, _cword Number, char * format, ...) { MESSAGE_EXCHANGE_ERROR ret; int i, j; unsigned int d; unsigned char *p, *p_length; unsigned char *string; va_list ap; capi_prestruct_t *s; unsigned char msg[2048]; write_capi_word(&msg[2], capi_ApplID); write_capi_word(&msg[4], ((command >> 8) & 0xff)); write_capi_word(&msg[5], (command & 0xff)); write_capi_word(&msg[6], Number); write_capi_dword(&msg[8], Id); p = &msg[12]; p_length = 0; va_start(ap, format); for (i = 0; format[i]; i++) { if (((p - (&msg[0])) + 12) >= sizeof(msg)) { cc_log(LOG_ERROR, "capi_sendf: message too big (%d)\n", (p - (&msg[0]))); return 0x1004; } switch(format[i]) { case 'b': /* byte */ d = (unsigned char)va_arg(ap, unsigned int); *(p++) = (unsigned char) d; break; case 'w': /* word (2 bytes) */ d = (unsigned short)va_arg(ap, unsigned int); *(p++) = (unsigned char) d; *(p++) = (unsigned char)(d >> 8); break; case 'd': /* double word (4 bytes) */ d = va_arg(ap, unsigned int); *(p++) = (unsigned char) d; *(p++) = (unsigned char)(d >> 8); *(p++) = (unsigned char)(d >> 16); *(p++) = (unsigned char)(d >> 24); break; case 's': /* struct, length is the first byte */ string = va_arg(ap, unsigned char *); if (string == NULL) { *(p++) = 0; } else { for (j = 0; j <= string[0]; j++) *(p++) = string[j]; } break; case 'a': /* ascii string, NULL terminated string */ string = va_arg(ap, unsigned char *); for (j = 0; string[j] != '\0'; j++) *(++p) = string[j]; *((p++)-j) = (unsigned char) j; break; case 'c': /* predefined capi_prestruct_t */ s = va_arg(ap, capi_prestruct_t *); if (s->wLen < 0xff) { *(p++) = (unsigned char)(s->wLen); } else { *(p++) = 0xff; *(p++) = (unsigned char)(s->wLen); *(p++) = (unsigned char)(s->wLen >> 8); } for (j = 0; j < s->wLen; j++) *(p++) = s->info[j]; break; case '(': /* begin of a structure */ *p = (p_length) ? p - p_length : 0; p_length = p++; break; case ')': /* end of structure */ if (p_length) { j = *p_length; *p_length = (unsigned char)((p - p_length) - 1); p_length = (j != 0) ? p_length - j : 0; } else { cc_log(LOG_ERROR, "capi_sendf: inconsistent format \"%s\"\n", format); } break; default: cc_log(LOG_ERROR, "capi_sendf: unknown format \"%s\"\n", format); } } va_end(ap); if (p_length) { cc_log(LOG_ERROR, "capi_sendf: inconsistent format \"%s\"\n", format); } write_capi_word(&msg[0], (unsigned short)(p - (&msg[0]))); ret = _capi_put_msg(&msg[0]); if ((!(ret)) && (waitconf)) { ret = capi_wait_conf(capii, (command & 0xff00) | CAPI_CONF); } return (ret); } /* * 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 capi_ListenOnController(unsigned int CIPmask, unsigned controller) { MESSAGE_EXCHANGE_ERROR error; int waitcount = 50; _cmsg CMSG; error = capi_sendf(NULL, 0, CAPI_LISTEN_REQ, controller, get_capi_MessageNumber(), "ddd()()", 0x0000ffff, CIPmask, 0 ); 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 capi_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; cc_mutex_lock(&peerlink_lock); for (a = 0; a < CAPI_MAX_PEERLINKCHANNELS; a++) { if (peerlinkchannel[a].channel == NULL) { peerlinkchannel[a].channel = c; peerlinkchannel[a].age = time(NULL); break; } else { /* remove too old entries */ if ((peerlinkchannel[a].age + 60) < time(NULL)) { peerlinkchannel[a].channel = NULL; cc_verbose(3, 1, VERBOSE_PREFIX_4 "capi: peerlink %d timeout-erase\n", a); } } } cc_mutex_unlock(&peerlink_lock); 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); } cc_mutex_lock(&peerlink_lock); if ((id >= 0) && (id < CAPI_MAX_PEERLINKCHANNELS)) { chan = peerlinkchannel[id].channel; peerlinkchannel[id].channel = NULL; } cc_verbose(3, 1, VERBOSE_PREFIX_4 "capi: peerlink %d allocated, peer is %s\n", id, (chan)?chan->name:"unlinked"); cc_mutex_unlock(&peerlink_lock); return chan; } /* * create pipe for interface connection */ int capi_create_reader_writer_pipe(struct capi_pvt *i) { int fds[2]; int flags; if (pipe(fds) != 0) { cc_log(LOG_ERROR, "%s: unable to create pipe.\n", i->vname); return 0; } i->readerfd = fds[0]; i->writerfd = fds[1]; flags = fcntl(i->readerfd, F_GETFL); fcntl(i->readerfd, F_SETFL, flags | O_NONBLOCK); flags = fcntl(i->writerfd, F_GETFL); fcntl(i->writerfd, F_SETFL, flags | O_NONBLOCK); return 1; } /* * read a frame from the pipe */ struct ast_frame *capi_read_pipeframe(struct capi_pvt *i) { struct ast_frame *f; int readsize; if (i == NULL) { cc_log(LOG_ERROR, "channel has no interface\n"); return NULL; } if (i->readerfd == -1) { cc_log(LOG_ERROR, "no readerfd\n"); return NULL; } f = &i->f; f->frametype = AST_FRAME_NULL; f->subclass = 0; readsize = read(i->readerfd, f, sizeof(struct ast_frame)); if ((readsize != sizeof(struct ast_frame)) && (readsize > 0)) { cc_log(LOG_ERROR, "did not read a whole frame (len=%d, err=%d)\n", readsize, errno); } f->mallocd = 0; f->data = NULL; if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP)) { return NULL; } if ((f->frametype == AST_FRAME_VOICE) && (f->datalen > 0)) { if (f->datalen > sizeof(i->frame_data)) { cc_log(LOG_ERROR, "f.datalen(%d) greater than space of frame_data(%d)\n", f->datalen, sizeof(i->frame_data)); f->datalen = sizeof(i->frame_data); } readsize = read(i->readerfd, i->frame_data + AST_FRIENDLY_OFFSET, f->datalen); if (readsize != f->datalen) { cc_log(LOG_ERROR, "did not read whole frame data\n"); } f->data = i->frame_data + AST_FRIENDLY_OFFSET; } return f; } /* * write for a channel */ int capi_write_frame(struct capi_pvt *i, struct ast_frame *f) { MESSAGE_EXCHANGE_ERROR error; int j = 0; unsigned char *buf; struct ast_frame *fsmooth; int txavg=0; int ret = 0; if (!i) { cc_log(LOG_ERROR, "channel has no interface\n"); return -1; } if ((!(i->isdnstate & CAPI_ISDN_STATE_B3_UP)) || (!i->NCCI) || ((i->isdnstate & (CAPI_ISDN_STATE_B3_CHANGE | CAPI_ISDN_STATE_LI)))) { return 0; } if ((!(i->ntmode)) && (i->state != CAPI_STATE_CONNECTED)) { return 0; } if (f->frametype == AST_FRAME_NULL) { return 0; } if (f->frametype == AST_FRAME_DTMF) { cc_log(LOG_ERROR, "dtmf frame should be written\n"); return 0; } if (f->frametype != AST_FRAME_VOICE) { cc_log(LOG_ERROR,"not a voice frame\n"); return 0; } if (i->FaxState & CAPI_FAX_STATE_ACTIVE) { cc_verbose(3, 1, VERBOSE_PREFIX_2 "%s: write on fax activity?\n", i->vname); return 0; } if ((!f->data) || (!f->datalen)) { cc_log(LOG_DEBUG, "No data for FRAME_VOICE %s\n", i->vname); return 0; } if (i->isdnstate & CAPI_ISDN_STATE_RTP) { if ((!(f->subclass & i->codec)) && (f->subclass != capi_capability)) { cc_log(LOG_ERROR, "don't know how to write subclass %s(%d)\n", ast_getformatname(f->subclass), f->subclass); return 0; } return capi_write_rtp(i, f); } if (i->B3count >= CAPI_MAX_B3_BLOCKS) { cc_verbose(3, 1, VERBOSE_PREFIX_4 "%s: B3count is full, dropping packet.\n", i->vname); return 0; } if ((!i->smoother) || (ast_smoother_feed(i->smoother, f) != 0)) { cc_log(LOG_ERROR, "%s: failed to fill smoother\n", i->vname); return 0; } for (fsmooth = ast_smoother_read(i->smoother); fsmooth != NULL; fsmooth = ast_smoother_read(i->smoother)) { buf = &(i->send_buffer[(i->send_buffer_handle % CAPI_MAX_B3_BLOCKS) * (CAPI_MAX_B3_BLOCK_SIZE + AST_FRIENDLY_OFFSET)]); i->send_buffer_handle++; if ((i->doES == 1) && (!capi_tcap_is_digital(i->transfercapability))) { for (j = 0; j < fsmooth->datalen; j++) { buf[j] = capi_reversebits[ ((unsigned char *)fsmooth->data)[j] ]; if (capi_capability == AST_FORMAT_ULAW) { txavg += abs( capiULAW2INT[capi_reversebits[ ((unsigned char*)fsmooth->data)[j]]] ); } else { txavg += abs( capiALAW2INT[capi_reversebits[ ((unsigned char*)fsmooth->data)[j]]] ); } } txavg = txavg / j; for(j = 0; j < ECHO_TX_COUNT - 1; j++) { i->txavg[j] = i->txavg[j+1]; } i->txavg[ECHO_TX_COUNT - 1] = txavg; } else { if ((i->txgain == 1.0) || (capi_tcap_is_digital(i->transfercapability))) { for (j = 0; j < fsmooth->datalen; j++) { buf[j] = capi_reversebits[((unsigned char *)fsmooth->data)[j]]; } } else { for (j = 0; j < fsmooth->datalen; j++) { buf[j] = i->g.txgains[capi_reversebits[((unsigned char *)fsmooth->data)[j]]]; } } } error = 1; if (i->B3q > 0) { error = capi_sendf(NULL, 0, CAPI_DATA_B3_REQ, i->NCCI, get_capi_MessageNumber(), "dwww", buf, fsmooth->datalen, i->send_buffer_handle, 0); } else { cc_verbose(3, 1, VERBOSE_PREFIX_4 "%s: too much voice to send for NCCI=%#x\n", i->vname, i->NCCI); } if (!error) { cc_mutex_lock(&i->lock); i->B3count++; i->B3q -= fsmooth->datalen; if (i->B3q < 0) i->B3q = 0; cc_mutex_unlock(&i->lock); } } return ret; }