+ *callp = (struct chan_call *)calloc(1, sizeof(struct chan_call));
+ if (*callp)
+ memset(*callp, 0, sizeof(struct chan_call));
+ if (pipe((*callp)->pipe) < 0) {
+ CERROR(*callp, NULL, "Failed to create pipe.\n");
+ free_call(*callp);
+ return NULL;
+ }
+ fcntl((*callp)->pipe[0], F_SETFL, O_NONBLOCK);
+ CDEBUG(*callp, NULL, "Call instance allocated.\n");
+ return *callp;
+}
+
+unsigned short new_bridge_id(void)
+{
+ struct chan_call *call;
+ unsigned short id = 1;
+
+ /* search for lowest bridge id that is not in use and not 0 */
+ while(id) {
+ call = call_first;
+ while(call) {
+ if (call->bridge_id == id)
+ break;
+ call = call->next;
+ }
+ if (!call)
+ break;
+ id++;
+ }
+ CDEBUG(NULL, NULL, "New bridge ID %d.\n", id);
+ return id;
+}
+
+/*
+ * enque message to LCR
+ */
+int send_message(int message_type, unsigned int ref, union parameter *param)
+{
+ struct admin_list *admin, **adminp;
+
+ if (lcr_sock < 0) {
+ CDEBUG(NULL, NULL, "Ignoring message %d, because socket is closed.\n", message_type);
+ return -1;
+ }
+ CDEBUG(NULL, NULL, "Sending %s to socket.\n", messages_txt[message_type]);
+
+ adminp = &admin_first;
+ while(*adminp)
+ adminp = &((*adminp)->next);
+ admin = (struct admin_list *)calloc(1, sizeof(struct admin_list));
+ if (!admin) {
+ CERROR(NULL, NULL, "No memory for message to LCR.\n");
+ return -1;
+ }
+ *adminp = admin;
+
+ admin->msg.message = ADMIN_MESSAGE;
+ admin->msg.u.msg.type = message_type;
+ admin->msg.u.msg.ref = ref;
+ memcpy(&admin->msg.u.msg.param, param, sizeof(union parameter));
+ socket_fd.when |= LCR_FD_WRITE;
+ if (!wake_global) {
+ wake_global = 1;
+ char byte = 0;
+ write(wake_pipe[1], &byte, 1);
+ }
+
+ return 0;
+}
+
+/*
+ * apply options (in locked state)
+ */
+void apply_opt(struct chan_call *call, char *data)
+{
+ union parameter newparam;
+ char string[1024], *p = string, *opt, *key;
+ int gain, i;
+
+ if (!data[0])
+ return; // no opts
+
+ strncpy(string, data, sizeof(string)-1);
+ string[sizeof(string)-1] = '\0';
+
+ /* parse options */
+ while((opt = strsep(&p, ":"))) {
+ switch(opt[0]) {
+ case 'd':
+ if (opt[1] == '\0') {
+ CERROR(call, call->ast, "Option 'd' (display) expects parameter.\n", opt);
+ break;
+ }
+ CDEBUG(call, call->ast, "Option 'd' (display) with text '%s'.\n", opt+1);
+ if (call->state == CHAN_LCR_STATE_OUT_PREPARE)
+ strncpy(call->display, opt+1, sizeof(call->display)-1);
+ else {
+ memset(&newparam, 0, sizeof(union parameter));
+ strncpy(newparam.notifyinfo.display, opt+1, sizeof(newparam.notifyinfo.display)-1);
+ send_message(MESSAGE_NOTIFY, call->ref, &newparam);
+ }
+ break;
+ case 'n':
+ if (opt[1] != '\0') {
+ CERROR(call, call->ast, "Option 'n' (no DTMF) expects no parameter.\n", opt);
+ break;
+ }
+ CDEBUG(call, call->ast, "Option 'n' (no DTMF).\n");
+ if (call->dsp_dtmf) {
+ call->dsp_dtmf = 0;
+ if (call->bchannel)
+ bchannel_dtmf(call->bchannel, 0);
+ }
+ break;
+ case 'c':
+ if (opt[1] == '\0') {
+ CERROR(call, call->ast, "Option 'c' (encrypt) expects key parameter.\n", opt);
+ break;
+ }
+ key = opt+1;
+ /* check for 0xXXXX... type of key */
+ if (!!strncmp((char *)key, "0x", 2)) {
+ CERROR(call, call->ast, "Option 'c' (encrypt) expects key parameter starting with '0x'.\n", opt);
+ break;
+ }
+ key+=2;
+ if (strlen(key) > 56*2 || (strlen(key) % 1)) {
+ CERROR(call, call->ast, "Option 'c' (encrypt) expects key parameter with max 56 bytes ('0x' + 112 characters)\n", opt);
+ break;
+ }
+ i = 0;
+ while(*key) {
+ if (*key>='0' && *key<='9')
+ call->bf_key[i] = (*key-'0') << 8;
+ else if (*key>='a' && *key<='f')
+ call->bf_key[i] = (*key-'a'+10) << 8;
+ else if (*key>='A' && *key<='F')
+ call->bf_key[i] = (*key-'A'+10) << 8;
+ else
+ break;
+ key++;
+ if (*key>='0' && *key<='9')
+ call->bf_key[i] += (*key - '0');
+ else if (*key>='a' && *key<='f')
+ call->bf_key[i] += (*key - 'a' + 10);
+ else if (*key>='A' && *key<='F')
+ call->bf_key[i] += (*key - 'A' + 10);
+ else
+ break;
+ key++;
+ i++;
+ }
+ if (*key) {
+ CERROR(call, call->ast, "Option 'c' (encrypt) expects key parameter with hex values 0-9,a-f.\n");
+ break;
+ }
+ call->bf_len = i;
+ CDEBUG(call, call->ast, "Option 'c' (encrypt) blowfish key '%s' (len=%d).\n", opt+1, i);
+ if (call->bchannel)
+ bchannel_blowfish(call->bchannel, call->bf_key, call->bf_len);
+ break;
+ case 'h':
+ if (opt[1] != '\0') {
+ CERROR(call, call->ast, "Option 'h' (HDLC) expects no parameter.\n", opt);
+ break;
+ }
+ CDEBUG(call, call->ast, "Option 'h' (HDLC).\n");
+ if (!call->hdlc)
+ call->hdlc = 1;
+ break;
+ case 't':
+ if (opt[1] != '\0') {
+ CERROR(call, call->ast, "Option 't' (no_dsp) expects no parameter.\n", opt);
+ break;
+ }
+ CDEBUG(call, call->ast, "Option 't' (no dsp).\n");
+ if (!call->nodsp)
+ call->nodsp = 1;
+ break;
+ case 'q':
+ if (opt[1] == '\0') {
+ CERROR(call, call->ast, "Option 'q' (queue) expects parameter.\n", opt);
+ break;
+ }
+ CDEBUG(call, call->ast, "Option 'q' (queue).\n");
+ call->nodsp_queue = atoi(opt+1);
+ break;
+ case 'e':
+ if (opt[1] == '\0') {
+ CERROR(call, call->ast, "Option 'e' (echo cancel) expects parameter.\n", opt);
+ break;
+ }
+ CDEBUG(call, call->ast, "Option 'e' (echo cancel) with config '%s'.\n", opt+1);
+ strncpy(call->pipeline, opt+1, sizeof(call->pipeline)-1);
+ if (call->bchannel)
+ bchannel_pipeline(call->bchannel, call->pipeline);
+ break;
+ case 'f':
+ if (opt[1] == '\0') {
+ CERROR(call, call->ast, "Option 'f' (faxdetect) expects parameter.\n", opt);
+ break;
+ }
+ call->faxdetect=atoi(opt+1);
+ if (!call->dsp)
+ call->dsp=ast_dsp_new();
+ if (call->dsp) {
+ #ifdef LCR_FOR_CALLWEAVER
+ ast_dsp_set_features(call->dsp, DSP_FEATURE_DTMF_DETECT| DSP_FEATURE_FAX_CNG_DETECT);
+ #endif
+ #ifdef LCR_FOR_ASTERISK
+ #ifdef DSP_FEATURE_DTMF_DETECT
+ ast_dsp_set_features(call->dsp, DSP_FEATURE_DTMF_DETECT| DSP_FEATURE_FAX_DETECT);
+ #else
+ ast_dsp_set_features(call->dsp, DSP_FEATURE_DIGIT_DETECT| DSP_FEATURE_FAX_DETECT);
+ #endif
+
+ #endif
+ if (!call->trans)
+ #ifdef LCR_FOR_CALLWEAVER
+ call->trans=ast_translator_build_path(AST_FORMAT_SLINEAR, 8000, (options.law=='a')?AST_FORMAT_ALAW:AST_FORMAT_ULAW, 8000);
+ #endif
+ #ifdef LCR_FOR_ASTERISK
+ call->trans=ast_translator_build_path(AST_FORMAT_SLINEAR, (options.law=='a')?AST_FORMAT_ALAW:AST_FORMAT_ULAW);
+ #endif
+ }
+ CDEBUG(call, call->ast, "Option 'f' (faxdetect) with config '%s'.\n", call->faxdetect);
+ break;
+ case 'r':
+ if (opt[1] != '\0') {
+ CERROR(call, call->ast, "Option 'r' (re-buffer 160 bytes) expects no parameter.\n", opt);
+ break;
+ }
+ CDEBUG(call, call->ast, "Option 'r' (re-buffer 160 bytes)");
+ call->rebuffer = 1;
+ call->framepos = 0;
+ break;
+ case 's':
+ if (opt[1] != '\0') {
+ CERROR(call, call->ast, "Option 's' (inband DTMF) expects no parameter.\n", opt);
+ break;
+ }
+ CDEBUG(call, call->ast, "Option 's' (inband DTMF).\n");
+ call->inband_dtmf = 1;
+ break;
+ case 'v':
+ if (opt[1] != 'r' && opt[1] != 't') {
+ CERROR(call, call->ast, "Option 'v' (volume) expects parameter.\n", opt);
+ break;
+ }
+ gain = atoi(opt+2);
+ if (gain < -8 || gain >8) {
+ CERROR(call, call->ast, "Option 'v' (volume) expects parameter in range of -8 through 8.\n");
+ break;
+ }
+ CDEBUG(call, call->ast, "Option 'v' (volume) with gain 2^%d.\n", gain);
+ if (opt[1] == 'r') {
+ call->rx_gain = gain;
+ if (call->bchannel)
+ bchannel_gain(call->bchannel, call->rx_gain, 0);
+ } else {
+ call->tx_gain = gain;
+ if (call->bchannel)
+ bchannel_gain(call->bchannel, call->tx_gain, 1);
+ }
+ break;
+ case 'k':
+ if (opt[1] != '\0') {
+ CERROR(call, call->ast, "Option 'k' (keypad) expects no parameter.\n", opt);
+ break;
+ }
+ CDEBUG(call, call->ast, "Option 'k' (keypad).\n");
+ if (!call->keypad)
+ call->keypad = 1;
+ break;
+ default:
+ CERROR(call, call->ast, "Option '%s' unknown.\n", opt);
+ }
+ }
+
+ /* re-open, if bchannel is created */
+ if (call->bchannel && call->bchannel->b_sock > -1) {
+ bchannel_destroy(call->bchannel);
+ if (bchannel_create(call->bchannel, ((call->nodsp || call->faxdetect > 0)?1:0) + ((call->hdlc)?2:0), call->nodsp_queue))
+ bchannel_activate(call->bchannel, 1);
+ }
+}
+
+/*
+ * send setup info to LCR
+ * this function is called, when asterisk call is received and ref is received
+ */
+static void send_setup_to_lcr(struct chan_call *call)
+{
+ union parameter newparam;
+ struct ast_channel *ast = call->ast;
+ const char *tmp;
+
+ if (!call->ast || !call->ref)
+ return;
+
+ CDEBUG(call, call->ast, "Sending setup to LCR. (interface=%s dialstring=%s, cid=%s)\n", call->interface, call->dialstring, call->cid_num);
+
+ /* send setup message to LCR */
+ memset(&newparam, 0, sizeof(union parameter));
+ newparam.setup.dialinginfo.itype = INFO_ITYPE_ISDN;
+ newparam.setup.dialinginfo.ntype = INFO_NTYPE_UNKNOWN;
+ if (call->keypad)
+ strncpy(newparam.setup.dialinginfo.keypad, call->dialstring, sizeof(newparam.setup.dialinginfo.keypad)-1);
+ else
+ strncpy(newparam.setup.dialinginfo.id, call->dialstring, sizeof(newparam.setup.dialinginfo.id)-1);
+ strncpy(newparam.setup.dialinginfo.interfaces, call->interface, sizeof(newparam.setup.dialinginfo.interfaces)-1);
+ newparam.setup.callerinfo.itype = INFO_ITYPE_CHAN;
+ newparam.setup.callerinfo.ntype = INFO_NTYPE_UNKNOWN;
+ strncpy(newparam.setup.callerinfo.display, call->display, sizeof(newparam.setup.callerinfo.display)-1);
+ call->display[0] = '\0';
+ if (call->cid_num[0])
+ strncpy(newparam.setup.callerinfo.id, call->cid_num, sizeof(newparam.setup.callerinfo.id)-1);
+ if (call->cid_name[0])
+ strncpy(newparam.setup.callerinfo.name, call->cid_name, sizeof(newparam.setup.callerinfo.name)-1);
+ if (call->cid_rdnis[0]) {
+ strncpy(newparam.setup.redirinfo.id, call->cid_rdnis, sizeof(newparam.setup.redirinfo.id)-1);
+ newparam.setup.redirinfo.itype = INFO_ITYPE_CHAN;
+ newparam.setup.redirinfo.ntype = INFO_NTYPE_UNKNOWN;
+ }
+ switch(ast->cid.cid_pres & AST_PRES_RESTRICTION) {
+ case AST_PRES_RESTRICTED:
+ newparam.setup.callerinfo.present = INFO_PRESENT_RESTRICTED;
+ break;
+ case AST_PRES_UNAVAILABLE:
+ newparam.setup.callerinfo.present = INFO_PRESENT_NOTAVAIL;
+ break;
+ case AST_PRES_ALLOWED:
+ default:
+ newparam.setup.callerinfo.present = INFO_PRESENT_ALLOWED;
+ }
+ switch(ast->cid.cid_ton) {
+ case 4:
+ newparam.setup.callerinfo.ntype = INFO_NTYPE_SUBSCRIBER;
+ break;
+ case 2:
+ newparam.setup.callerinfo.ntype = INFO_NTYPE_NATIONAL;
+ break;
+ case 1:
+ newparam.setup.callerinfo.ntype = INFO_NTYPE_INTERNATIONAL;
+ break;
+ default:
+ newparam.setup.callerinfo.ntype = INFO_NTYPE_UNKNOWN;
+ }
+ tmp = pbx_builtin_getvar_helper(ast, "LCR_TRANSFERCAPABILITY");
+ if (tmp && *tmp)
+ ast->transfercapability = atoi(tmp);
+ newparam.setup.capainfo.bearer_capa = ast->transfercapability;
+ newparam.setup.capainfo.bearer_mode = INFO_BMODE_CIRCUIT;
+ if (call->hdlc)
+ newparam.setup.capainfo.source_mode = B_MODE_HDLC;
+ else {
+ newparam.setup.capainfo.source_mode = B_MODE_TRANSPARENT;
+ newparam.setup.capainfo.bearer_info1 = (options.law=='a')?3:2;
+ }
+ newparam.setup.capainfo.hlc = INFO_HLC_NONE;
+ newparam.setup.capainfo.exthlc = INFO_HLC_NONE;
+ send_message(MESSAGE_SETUP, call->ref, &newparam);
+
+ /* change to outgoing setup state */
+ call->state = CHAN_LCR_STATE_OUT_SETUP;
+}
+
+/*
+ * send dialing info to LCR
+ * this function is called, when setup acknowledge is received and dialing
+ * info is available.
+ */
+static void send_dialque_to_lcr(struct chan_call *call)
+{
+ union parameter newparam;
+
+ if (!call->ast || !call->ref || !call->dialque[0])
+ return;
+
+ CDEBUG(call, call->ast, "Sending dial queue to LCR. (dialing=%s)\n", call->dialque);
+
+ /* send setup message to LCR */
+ memset(&newparam, 0, sizeof(union parameter));
+ if (call->keypad)
+ strncpy(newparam.information.keypad, call->dialque, sizeof(newparam.information.keypad)-1);
+ else
+ strncpy(newparam.information.id, call->dialque, sizeof(newparam.information.id)-1);
+ call->dialque[0] = '\0';
+ send_message(MESSAGE_INFORMATION, call->ref, &newparam);
+}
+
+/*
+ * in case of a bridge, the unsupported message can be forwarded directly
+ * to the remote call.
+ */
+static void bridge_message_if_bridged(struct chan_call *call, int message_type, union parameter *param)
+{
+ /* check bridge */
+ if (!call) return;
+ if (!call->bridge_call) return;
+ CDEBUG(call, NULL, "Sending message due bridging.\n");
+ send_message(message_type, call->bridge_call->ref, param);
+}
+
+/*
+ * send release message to LCR and import bchannel if exported
+ */
+static void send_release_and_import(struct chan_call *call, int cause, int location)
+{
+ union parameter newparam;
+
+ /* importing channel */
+ if (call->bchannel) {
+ memset(&newparam, 0, sizeof(union parameter));
+ newparam.bchannel.type = BCHANNEL_RELEASE;
+ newparam.bchannel.handle = call->bchannel->handle;
+ send_message(MESSAGE_BCHANNEL, call->ref, &newparam);
+ }
+ /* sending release */
+ memset(&newparam, 0, sizeof(union parameter));
+ newparam.disconnectinfo.cause = cause;
+ newparam.disconnectinfo.location = LOCATION_PRIVATE_LOCAL;
+ send_message(MESSAGE_RELEASE, call->ref, &newparam);
+}
+
+/*
+ * check if extension matches and start asterisk
+ * if it can match, proceed
+ * if not, release
+ */
+static void lcr_start_pbx(struct chan_call *call, struct ast_channel *ast, int complete)
+{
+ int cause, ret;
+ union parameter newparam;
+ char *exten = ast->exten;
+ if (!*exten)
+ exten = "s";
+
+ CDEBUG(call, ast, "Try to start pbx. (exten=%s context=%s complete=%s)\n", exten, ast->context, complete?"yes":"no");
+
+ if (complete) {
+ /* if not match */
+ if (!ast_canmatch_extension(ast, ast->context, exten, 1, call->oad)) {
+ CDEBUG(call, ast, "Got 'sending complete', but extension '%s' will not match at context '%s' - releasing.\n", exten, ast->context);
+ cause = 1;
+ goto release;
+ }
+ if (!ast_exists_extension(ast, ast->context, exten, 1, call->oad)) {
+ CDEBUG(call, ast, "Got 'sending complete', but extension '%s' would match at context '%s', if more digits would be dialed - releasing.\n", exten, ast->context);
+ cause = 28;
+ goto release;
+ }
+ CDEBUG(call, ast, "Got 'sending complete', extensions matches.\n");
+ /* send setup acknowledge to lcr */
+ memset(&newparam, 0, sizeof(union parameter));
+ send_message(MESSAGE_PROCEEDING, call->ref, &newparam);
+
+ /* change state */
+ call->state = CHAN_LCR_STATE_IN_PROCEEDING;
+
+ goto start;
+ }
+
+ if (ast_canmatch_extension(ast, ast->context, exten, 1, call->oad)) {
+ /* send setup acknowledge to lcr */
+ if (call->state != CHAN_LCR_STATE_IN_DIALING) {
+ memset(&newparam, 0, sizeof(union parameter));
+ send_message(MESSAGE_OVERLAP, call->ref, &newparam);
+ }
+
+ /* change state */
+ call->state = CHAN_LCR_STATE_IN_DIALING;
+
+ /* if match, start pbx */
+ if (ast_exists_extension(ast, ast->context, exten, 1, call->oad)) {
+ CDEBUG(call, ast, "Extensions matches.\n");
+ goto start;
+ }
+
+ /* if can match */
+ CDEBUG(call, ast, "Extensions may match, if more digits are dialed.\n");
+ return;
+ }
+
+ if (!*ast->exten) {
+ /* if can match */
+ CDEBUG(call, ast, "There is no 's' extension (and we tried to match it implicitly). Extensions may match, if more digits are dialed.\n");
+ return;
+ }
+
+ /* if not match */
+ cause = 1;
+ release:
+ /* release lcr */
+ CDEBUG(call, ast, "Releasing due to extension missmatch.\n");
+ send_release_and_import(call, cause, LOCATION_PRIVATE_LOCAL);
+ call->ref = 0;
+ /* release asterisk */
+ ast->hangupcause = call->cause;
+ /* change to release state */
+ call->state = CHAN_LCR_STATE_RELEASE;
+ ast_hangup(ast); // call will be destroyed here
+ return;
+
+ start:
+ /* send setup to asterisk */
+ CDEBUG(call, ast, "Starting call to Asterisk due to matching extension.\n");
+
+ #ifdef LCR_FOR_CALLWEAVER
+ ast->type = "LCR";
+ snprintf(ast->name, sizeof(ast->name), "LCR/%s-%04x",ast->cid.cid_num, ast_random() & 0xffff);
+ #endif
+
+ ret = ast_pbx_start(ast);
+ if (ret < 0) {
+ cause = (ret==-2)?34:27;
+ goto release;
+ }
+ call->pbx_started = 1;
+ ast_setstate(ast, AST_STATE_RING);
+}
+
+/*
+ * incoming setup from LCR
+ */
+static void lcr_in_setup(struct chan_call *call, int message_type, union parameter *param)
+{
+ struct ast_channel *ast;
+
+ CDEBUG(call, NULL, "Incomming setup from LCR. (callerid %s, dialing %s)\n", param->setup.callerinfo.id, param->setup.dialinginfo.id);
+
+ /* create asterisk channel instrance */
+
+ #ifdef LCR_FOR_CALLWEAVER
+ ast = ast_channel_alloc(1);
+ #endif
+
+ #ifdef LCR_FOR_ASTERISK
+ ast = ast_channel_alloc(1, AST_STATE_RESERVED, NULL, NULL, "", NULL, "", 0, "%s/%d", lcr_type, ++glob_channel);
+ #endif
+
+ if (!ast) {
+ /* release */
+ CERROR(call, NULL, "Failed to create Asterisk channel - releasing.\n");
+ send_release_and_import(call, CAUSE_RESSOURCEUNAVAIL, LOCATION_PRIVATE_LOCAL);
+ /* remove call */
+ free_call(call);
+ return;
+ }
+ /* link together */
+ call->ast = ast;
+ ast->tech_pvt = call;
+ ast->tech = &lcr_tech;
+ ast->fds[0] = call->pipe[0];
+
+ /* fill setup information */
+ if (param->setup.dialinginfo.id)
+ strncpy(ast->exten, param->setup.dialinginfo.id, AST_MAX_EXTENSION-1);
+ if (param->setup.context[0])
+ strncpy(ast->context, param->setup.context, AST_MAX_CONTEXT-1);
+ else
+ strncpy(ast->context, param->setup.callerinfo.interface, AST_MAX_CONTEXT-1);
+ if (param->setup.callerinfo.id[0])
+ ast->cid.cid_num = strdup(param->setup.callerinfo.id);
+ if (param->setup.callerinfo.name[0])
+ ast->cid.cid_name = strdup(param->setup.callerinfo.name);
+ if (param->setup.redirinfo.id[0])
+ ast->cid.cid_rdnis = strdup(numberrize_callerinfo(param->setup.redirinfo.id, param->setup.redirinfo.ntype, options.national, options.international));
+ switch (param->setup.callerinfo.present) {
+ case INFO_PRESENT_ALLOWED:
+ ast->cid.cid_pres = AST_PRES_ALLOWED;
+ break;
+ case INFO_PRESENT_RESTRICTED:
+ ast->cid.cid_pres = AST_PRES_RESTRICTED;
+ break;
+ default:
+ ast->cid.cid_pres = AST_PRES_UNAVAILABLE;
+ }
+ switch (param->setup.callerinfo.ntype) {
+ case INFO_NTYPE_SUBSCRIBER:
+ ast->cid.cid_ton = 4;
+ break;
+ case INFO_NTYPE_NATIONAL:
+ ast->cid.cid_ton = 2;
+ break;
+ case INFO_NTYPE_INTERNATIONAL:
+ ast->cid.cid_ton = 1;
+ break;
+ default:
+ ast->cid.cid_ton = 0;
+ }
+ ast->transfercapability = param->setup.capainfo.bearer_capa;
+ /* enable hdlc if transcap is data */
+ if (param->setup.capainfo.source_mode == B_MODE_HDLC)
+ call->hdlc = 1;
+ strncpy(call->oad, numberrize_callerinfo(param->setup.callerinfo.id, param->setup.callerinfo.ntype, options.national, options.international), sizeof(call->oad)-1);
+
+ /* configure channel */
+ ast->nativeformats = (options.law=='a')?AST_FORMAT_ALAW:AST_FORMAT_ULAW;
+ ast->readformat = ast->rawreadformat = ast->nativeformats;
+ ast->writeformat = ast->rawwriteformat = ast->nativeformats;
+ ast->priority = 1;
+ ast->hangupcause = 0;
+
+ /* change state */
+ call->state = CHAN_LCR_STATE_IN_SETUP;
+
+ if (!call->pbx_started)
+ lcr_start_pbx(call, ast, param->setup.dialinginfo.sending_complete);
+}
+
+/*
+ * incoming setup acknowledge from LCR
+ */
+static void lcr_in_overlap(struct chan_call *call, int message_type, union parameter *param)
+{
+ if (!call->ast) return;
+
+ CDEBUG(call, call->ast, "Incomming setup acknowledge from LCR.\n");
+
+ /* send pending digits in dialque */
+ if (call->dialque[0])
+ send_dialque_to_lcr(call);
+ /* change to overlap state */
+ call->state = CHAN_LCR_STATE_OUT_DIALING;
+}
+
+/*
+ * incoming proceeding from LCR
+ */
+static void lcr_in_proceeding(struct chan_call *call, int message_type, union parameter *param)
+{
+ CDEBUG(call, call->ast, "Incomming proceeding from LCR.\n");
+
+ /* change state */
+ call->state = CHAN_LCR_STATE_OUT_PROCEEDING;
+ /* queue event for asterisk */
+ if (call->ast && call->pbx_started) {
+ if (!wake_global) {
+ wake_global = 1;
+ char byte = 0;
+ write(wake_pipe[1], &byte, 1);
+ }
+ strncat(call->queue_string, "P", sizeof(call->queue_string)-1);
+ }
+
+}
+
+/*
+ * incoming alerting from LCR
+ */
+static void lcr_in_alerting(struct chan_call *call, int message_type, union parameter *param)
+{
+ CDEBUG(call, call->ast, "Incomming alerting from LCR.\n");
+
+ /* change state */
+ call->state = CHAN_LCR_STATE_OUT_ALERTING;
+ /* queue event to asterisk */
+ if (call->ast && call->pbx_started) {
+ if (!wake_global) {
+ wake_global = 1;
+ char byte = 0;
+ write(wake_pipe[1], &byte, 1);
+ }
+ strncat(call->queue_string, "R", sizeof(call->queue_string)-1);
+ }
+}
+
+/*
+ * incoming connect from LCR
+ */
+static void lcr_in_connect(struct chan_call *call, int message_type, union parameter *param)
+{
+ union parameter newparam;
+
+ CDEBUG(call, call->ast, "Incomming connect (answer) from LCR.\n");
+
+ /* change state */
+ call->state = CHAN_LCR_STATE_CONNECT;
+ /* request bchannel */
+ if (!call->bchannel) {
+ CDEBUG(call, call->ast, "Requesting B-channel.\n");
+ memset(&newparam, 0, sizeof(union parameter));
+ newparam.bchannel.type = BCHANNEL_REQUEST;
+ send_message(MESSAGE_BCHANNEL, call->ref, &newparam);
+ }
+ /* copy connectinfo */
+ memcpy(&call->connectinfo, ¶m->connectinfo, sizeof(struct connect_info));
+ /* queue event to asterisk */
+ if (call->ast && call->pbx_started) {
+ if (!wake_global) {
+ wake_global = 1;
+ char byte = 0;
+ write(wake_pipe[1], &byte, 1);
+ }
+ strncat(call->queue_string, "N", sizeof(call->queue_string)-1);
+ }
+}
+
+/*
+ * incoming disconnect from LCR
+ */
+static void lcr_in_disconnect(struct chan_call *call, int message_type, union parameter *param)
+{
+ struct ast_channel *ast = call->ast;
+
+ CDEBUG(call, call->ast, "Incomming disconnect from LCR. (cause=%d)\n", param->disconnectinfo.cause);
+
+ /* change state */
+ call->state = CHAN_LCR_STATE_IN_DISCONNECT;
+ /* save cause */
+ call->cause = param->disconnectinfo.cause;
+ call->location = param->disconnectinfo.location;
+ /* if bridge, forward disconnect and return */
+#ifdef TODO
+ feature flag
+ if (call->bridge_call) {
+ CDEBUG(call, call->ast, "Only signal disconnect via bridge.\n");
+ bridge_message_if_bridged(call, message_type, param);
+ return;
+ }
+#endif
+ /* release lcr with same cause */
+ send_release_and_import(call, call->cause, call->location);
+ call->ref = 0;
+ /* change to release state */
+ call->state = CHAN_LCR_STATE_RELEASE;
+ /* queue release asterisk */
+ if (ast) {
+ ast->hangupcause = call->cause;
+ if (call->pbx_started) {
+ if (!wake_global) {
+ wake_global = 1;
+ char byte = 0;
+ write(wake_pipe[1], &byte, 1);
+ }
+ strcpy(call->queue_string, "H"); // overwrite other indications
+ } else {
+ ast_hangup(ast); // call will be destroyed here
+ }
+ }
+}
+
+/*
+ * incoming release from LCR
+ */
+static void lcr_in_release(struct chan_call *call, int message_type, union parameter *param)
+{
+ struct ast_channel *ast = call->ast;
+
+ CDEBUG(call, call->ast, "Incomming release from LCR, releasing ref. (cause=%d)\n", param->disconnectinfo.cause);
+
+ /* release ref */
+ call->ref = 0;
+ /* change to release state */
+ call->state = CHAN_LCR_STATE_RELEASE;
+ /* copy release info */
+ if (!call->cause) {
+ call->cause = param->disconnectinfo.cause;
+ call->location = param->disconnectinfo.location;
+ }
+ /* if we have an asterisk instance, queue hangup, else we are done */
+ if (ast) {
+ ast->hangupcause = call->cause;
+ if (call->pbx_started) {
+ if (!wake_global) {
+ wake_global = 1;
+ char byte = 0;
+ write(wake_pipe[1], &byte, 1);
+ }
+ strcpy(call->queue_string, "H");
+ } else {
+ ast_hangup(ast); // call will be destroyed here
+ }
+ } else {
+ free_call(call);
+ }
+