/* * (CAPI*) * * An implementation of Common ISDN API 2.0 for Asterisk * * Copyright (C) 2005-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 #include #include #include "chan_capi20.h" #include "chan_capi.h" #include "chan_capi_chat.h" #include "chan_capi_utils.h" #define CHAT_FLAG_MOH 0x0001 struct capichat_s { char name[16]; unsigned int number; int active; struct capi_pvt *i; struct capichat_s *next; }; static struct capichat_s *chat_list = NULL; AST_MUTEX_DEFINE_STATIC(chat_lock); /* * update the capi mixer for the given char room */ static void update_capi_mixer(int remove, unsigned int roomnumber, struct capi_pvt *i) { struct capi_pvt *ii; struct capichat_s *room; unsigned char p_list[360]; _cdword dest; _cdword datapath; capi_prestruct_t p_struct; unsigned int found = 0; _cword j = 0; if (i->PLCI == 0) { cc_verbose(2, 0, VERBOSE_PREFIX_3 "capi mixer: %s: PLCI is unset, abort.\n", i->vname); return; } cc_mutex_lock(&chat_lock); room = chat_list; while (room) { if ((room->number == roomnumber) && (room->i != i)) { found++; if (j + 9 > sizeof(p_list)) { /* maybe we need to split capi messages here */ break; } ii = room->i; p_list[j++] = 8; p_list[j++] = (_cbyte)(ii->PLCI); p_list[j++] = (_cbyte)(ii->PLCI >> 8); p_list[j++] = (_cbyte)(ii->PLCI >> 16); p_list[j++] = (_cbyte)(ii->PLCI >> 24); dest = (remove) ? 0x00000000 : 0x00000003; if (ii->channeltype == CAPI_CHANNELTYPE_NULL) { dest |= 0x00000030; } p_list[j++] = (_cbyte)(dest); p_list[j++] = (_cbyte)(dest >> 8); p_list[j++] = (_cbyte)(dest >> 16); p_list[j++] = (_cbyte)(dest >> 24); cc_verbose(3, 1, VERBOSE_PREFIX_3 "capi mixer: listed %s PLCI=0x%04x LI=0x%x\n", ii->vname, ii->PLCI, dest); } room = room->next; } room = chat_list; while (room) { if (room->number == roomnumber) { room->active = found + ((remove) ? 0 : 1); } room = room->next; } cc_mutex_unlock(&chat_lock); if (found) { p_struct.wLen = j; p_struct.info = p_list; /* don't send DATA_B3 to me */ datapath = 0x00000000; if (remove) { /* now we need DATA_B3 again */ datapath = 0x0000000c; if (found == 1) { /* only one left, enable DATA_B3 too */ p_list[5] |= 0x0c; } } if (i->channeltype == CAPI_CHANNELTYPE_NULL) { if (!remove) { datapath |= 0x00000030; } } cc_verbose(3, 1, VERBOSE_PREFIX_3 "capi mixer: %s PLCI=0x%04x LI=0x%x\n", i->vname, i->PLCI, datapath); capi_sendf(NULL, 0, CAPI_FACILITY_REQ, i->PLCI, get_capi_MessageNumber(), "w(w(dc))", FACILITYSELECTOR_LINE_INTERCONNECT, 0x0001, /* CONNECT */ datapath, &p_struct ); } } /* * delete a chat member */ static void del_chat_member(struct capichat_s *room) { struct capichat_s *tmproom; struct capichat_s *tmproom2 = NULL; unsigned int roomnumber = room->number; struct capi_pvt *i = room->i; cc_mutex_lock(&chat_lock); tmproom = chat_list; while (tmproom) { if (tmproom == room) { if (!tmproom2) { chat_list = tmproom->next; } else { tmproom2->next = tmproom->next; } cc_verbose(3, 0, VERBOSE_PREFIX_3 "%s: removed chat member from room '%s' (%d)\n", room->i->vname, room->name, room->number); free(room); } tmproom2 = tmproom; tmproom = tmproom->next; } cc_mutex_unlock(&chat_lock); update_capi_mixer(1, roomnumber, i); } /* * add a new chat member */ static struct capichat_s *add_chat_member(char *roomname, struct capi_pvt *i) { struct capichat_s *room = NULL; struct capichat_s *tmproom; unsigned int roomnumber = 1; room = malloc(sizeof(struct capichat_s)); if (room == NULL) { cc_log(LOG_ERROR, "Unable to allocate capi chat struct.\n"); return NULL; } memset(room, 0, sizeof(struct capichat_s)); strncpy(room->name, roomname, sizeof(room->name)); room->name[sizeof(room->name) - 1] = 0; room->i = i; cc_mutex_lock(&chat_lock); tmproom = chat_list; while (tmproom) { if (!strcmp(tmproom->name, roomname)) { roomnumber = tmproom->number; break; } else { if (tmproom->number == roomnumber) { roomnumber++; } } tmproom = tmproom->next; } room->number = roomnumber; room->next = chat_list; chat_list = room; cc_mutex_unlock(&chat_lock); cc_verbose(3, 0, VERBOSE_PREFIX_3 "%s: added new chat member to room '%s' (%d)\n", i->vname, roomname, roomnumber); update_capi_mixer(0, roomnumber, i); return room; } /* * loop during chat */ static void chat_handle_events(struct ast_channel *c, struct capi_pvt *i, struct capichat_s *room, unsigned int flags) { struct ast_frame *f; int ms; int exception; int ready_fd; int waitfd; int nfds = 0; struct ast_channel *rchan; struct ast_channel *chan = c; int moh_active = 0; ast_indicate(chan, -1); waitfd = i->readerfd; if (i->channeltype == CAPI_CHANNELTYPE_NULL) { nfds = 1; ast_set_read_format(chan, capi_capability); ast_set_write_format(chan, capi_capability); } if ((flags & CHAT_FLAG_MOH) && (room->active < 2)) { ast_moh_start(chan, NULL, NULL); moh_active = 1; } while (1) { ready_fd = 0; ms = 100; errno = 0; exception = 0; rchan = ast_waitfor_nandfds(&chan, 1, &waitfd, nfds, &exception, &ready_fd, &ms); if (rchan) { f = ast_read(chan); if (!f) { cc_verbose(3, 1, VERBOSE_PREFIX_3 "%s: chat: no frame, hangup.\n", i->vname); break; } if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP)) { cc_verbose(3, 1, VERBOSE_PREFIX_3 "%s: chat: hangup frame.\n", i->vname); ast_frfree(f); break; } else if (f->frametype == AST_FRAME_VOICE) { cc_verbose(5, 1, VERBOSE_PREFIX_3 "%s: chat: voice frame.\n", i->vname); if (i->channeltype == CAPI_CHANNELTYPE_NULL) { capi_write_frame(i, f); } } else if (f->frametype == AST_FRAME_NULL) { /* ignore NULL frame */ cc_verbose(5, 1, VERBOSE_PREFIX_3 "%s: chat: NULL frame, ignoring.\n", i->vname); } else { cc_verbose(3, 1, VERBOSE_PREFIX_3 "%s: chat: unhandled frame %d/%d.\n", i->vname, f->frametype, f->subclass); } ast_frfree(f); } else if (ready_fd == i->readerfd) { if (exception) { cc_verbose(1, 0, VERBOSE_PREFIX_3 "%s: chat: exception on readerfd\n", i->vname); break; } f = capi_read_pipeframe(i); if (f->frametype == AST_FRAME_VOICE) { ast_write(chan, f); } /* ignore other nullplci frames */ } else { if ((ready_fd < 0) && ms) { if (errno == 0 || errno == EINTR) continue; cc_log(LOG_WARNING, "%s: Wait failed (%s).\n", chan->name, strerror(errno)); break; } } if ((moh_active) && (room->active > 1)) { ast_moh_stop(chan); moh_active = 0; } } } /* * start the chat */ int pbx_capi_chat(struct ast_channel *c, char *param) { struct capi_pvt *i = NULL; char *roomname, *controller, *options; char *p; struct capichat_s *room; ast_group_t tmpcntr; unsigned long contr = 0; unsigned int flags = 0; roomname = strsep(¶m, "|"); options = strsep(¶m, "|"); controller = param; if (!roomname) { cc_log(LOG_WARNING, "capi chat requires room name.\n"); return -1; } if (controller) { for (p = controller; p && *p; p++) { if (*p == '|') *p = ','; } tmpcntr = ast_get_group(controller); contr = (unsigned long)(tmpcntr >> 1); } while ((options) && (*options)) { switch (*options) { case 'm': flags |= CHAT_FLAG_MOH; break; default: cc_log(LOG_WARNING, "Unknown chat option '%c'.\n", *options); break; } options++; } cc_verbose(3, 1, VERBOSE_PREFIX_3 "capi chat: %s: roomname=%s " "options=%s controller=%s (0x%x)\n", c->name, roomname, options, controller, contr); if (c->tech == &capi_tech) { i = CC_CHANNEL_PVT(c); } else { /* virtual CAPI channel */ i = capi_mknullif(c, contr); if (!i) { return -1; } } if (c->_state != AST_STATE_UP) { ast_answer(c); } capi_wait_for_answered(i); if (!(capi_wait_for_b3_up(i))) { goto out; } room = add_chat_member(roomname, i); if (!room) { cc_log(LOG_WARNING, "Unable to open capi chat room.\n"); return -1; } /* main loop */ chat_handle_events(c, i, room, flags); del_chat_member(room); out: capi_remove_nullif(i); return 0; } /* * do command capi chatinfo */ int pbxcli_capi_chatinfo(int fd, int argc, char *argv[]) { struct capichat_s *room = NULL; struct ast_channel *c; if (argc != 2) return RESULT_SHOWUSAGE; if (chat_list == NULL) { ast_cli(fd, "There are no members in CAPI CHAT.\n"); return RESULT_SUCCESS; } ast_cli(fd, "CAPI CHAT\n"); ast_cli(fd, "Room# Roomname Member Caller\n"); cc_mutex_lock(&chat_lock); room = chat_list; while (room) { c = room->i->owner; if (!c) { c = room->i->used; } if (!c) { ast_cli(fd, "%3d %-12s%-30s\"%s\" <%s>\n", room->number, room->name, room->i->vname, "?", "?"); } else { ast_cli(fd, "%3d %-12s%-30s\"%s\" <%s>\n", room->number, room->name, c->name, (c->cid.cid_name) ? c->cid.cid_name:"", c->cid.cid_num); } room = room->next; } cc_mutex_unlock(&chat_lock); return RESULT_SUCCESS; }