diff --git a/callerid.c b/callerid.c index 034b4e8a2854d48eac1f47458f51022544bae458..f78714fbf45ab1b574c52292296b7446b81953d1 100755 --- a/callerid.c +++ b/callerid.c @@ -24,6 +24,8 @@ #include <asterisk/callerid.h> #include <asterisk/logger.h> #include <asterisk/fskmodem.h> +#include "sas.h" +#include "cas.h" struct callerid_state { @@ -96,6 +98,33 @@ void callerid_get(struct callerid_state *cid, char **name, char **number, int *f *number = cid->number; } +int ast_callerid_gen_cas(unsigned char *outbuf, int len) +{ + int pos = 0; + int cnt; + int saslen=2400; + if (len < saslen) + return -1; + while(saslen) { + cnt = saslen; + if (cnt > sizeof(sas)) + cnt = sizeof(sas); + memcpy(outbuf + pos, sas, cnt); + pos += cnt; + len -= cnt; + saslen -= cnt; + } + while(len) { + cnt = len; + if (cnt > sizeof(cas)) + cnt = sizeof(cas); + memcpy(outbuf + pos, cas, cnt); + pos += cnt; + len -= cnt; + } + return 0; +} + int callerid_feed(struct callerid_state *cid, unsigned char *ubuf, int len) { int mylen = len; @@ -363,7 +392,7 @@ static inline float callerid_getcarrier(float *cr, float *ci, int bit) PUT_CLID_BAUD(1); /* Stop bit */ \ } while(0); -int callerid_generate(unsigned char *buf, char *number, char *name, int flags) +int callerid_generate(unsigned char *buf, char *number, char *name, int flags, int callwaiting) { int bytes=0; int x, sum; @@ -373,11 +402,14 @@ int callerid_generate(unsigned char *buf, char *number, char *name, int flags) float scont = 0.0; char msg[256]; callerid_genmsg(msg, sizeof(msg), number, name, flags); - for (x=0;x<4000;x++) - PUT_BYTE(0x7f); - /* Transmit 30 0x55's (looks like a square wave */ - for (x=0;x<30;x++) - PUT_CLID(0x55); + if (!callwaiting) { + /* Wait a half a second */ + for (x=0;x<4000;x++) + PUT_BYTE(0x7f); + /* Transmit 30 0x55's (looks like a square wave) for channel seizure */ + for (x=0;x<30;x++) + PUT_CLID(0x55); + } /* Send 150ms of callerid marks */ for (x=0;x<150;x++) PUT_CLID_MARKMS; @@ -412,6 +444,8 @@ void ast_shrink_phone_number(char *n) int ast_isphonenumber(char *n) { int x; + if (!n) + return 0; for (x=0;n[x];x++) if (!strchr("0123456789", n[x])) return 0; @@ -422,6 +456,7 @@ int ast_callerid_parse(char *instr, char **name, char **location) { char *ns, *ne; char *ls, *le; + char tmp[256]; /* Try for "name" <location> format or name <location> format */ if ((ls = strchr(instr, '<')) && (le = strchr(ls, '>'))) { @@ -446,29 +481,46 @@ int ast_callerid_parse(char *instr, char **name, char **location) return 0; } } else { - /* Assume it's just a location */ - *name = NULL; - *location = instr; + strncpy(tmp, instr, sizeof(tmp)); + ast_shrink_phone_number(tmp); + if (!ast_isphonenumber(tmp)) { + /* Assume it's just a location */ + *name = NULL; + *location = instr; + } else { + /* Assume it's just a name */ + *name = instr; + *location = NULL; + } return 0; } return -1; } -int ast_callerid_generate(unsigned char *buf, char *callerid) +static int __ast_callerid_generate(unsigned char *buf, char *callerid, int callwaiting) { char tmp[256]; char *n, *l; if (!callerid) - return callerid_generate(buf, NULL, NULL, 0); + return callerid_generate(buf, NULL, NULL, 0, callwaiting); strncpy(tmp, callerid, sizeof(tmp)); if (ast_callerid_parse(tmp, &n, &l)) { ast_log(LOG_WARNING, "Unable to parse '%s' into CallerID name & number\n", callerid); - return callerid_generate(buf, NULL, NULL, 0); + return callerid_generate(buf, NULL, NULL, 0, callwaiting); } - ast_shrink_phone_number(l); - if (!n && (!ast_isphonenumber(l))) - return callerid_generate(buf, NULL, NULL, 0); - if (!ast_isphonenumber(l)) - return callerid_generate(buf, NULL, n, 0); - return callerid_generate(buf, l, n, 0); + if (l) + ast_shrink_phone_number(l); + if (!ast_isphonenumber(l)) + return callerid_generate(buf, NULL, n, 0, callwaiting); + return callerid_generate(buf, l, n, 0, callwaiting); +} + +int ast_callerid_generate(unsigned char *buf, char *callerid) +{ + return __ast_callerid_generate(buf, callerid, 0); +} + +int ast_callerid_callwaiting_generate(unsigned char *buf, char *callerid) +{ + return __ast_callerid_generate(buf, callerid, 1); } diff --git a/channel.c b/channel.c index 3a53a640b4bd167873c000c4b3a25c4027ddf02d..0f720016210411d385a3e99ead1d271f32e2c1f4 100755 --- a/channel.c +++ b/channel.c @@ -28,6 +28,7 @@ #include <asterisk/translate.h> +/* XXX Lock appropriately in more functions XXX */ #ifdef DEBUG_MUTEX /* Convenient mutex debugging functions */ @@ -36,16 +37,16 @@ static int __PTHREAD_MUTEX_LOCK(char *f, pthread_mutex_t *a) { ast_log(LOG_DEBUG, "Locking %p (%s)\n", a, f); - return pthread_mutex_lock(a); + return ast_pthread_mutex_lock(a); } static int __PTHREAD_MUTEX_UNLOCK(char *f, pthread_mutex_t *a) { ast_log(LOG_DEBUG, "Unlocking %p (%s)\n", a, f); - return pthread_mutex_unlock(a); + return ast_pthread_mutex_unlock(a); } #else -#define PTHREAD_MUTEX_LOCK(a) pthread_mutex_lock(a) -#define PTHREAD_MUTEX_UNLOCK(a) pthread_mutex_unlock(a) +#define PTHREAD_MUTEX_LOCK(a) ast_pthread_mutex_lock(a) +#define PTHREAD_MUTEX_UNLOCK(a) ast_pthread_mutex_unlock(a) #endif struct chanlist { @@ -103,10 +104,35 @@ int ast_channel_register(char *type, char *description, int capabilities, return 0; } +char *ast_state2str(int state) +{ + switch(state) { + case AST_STATE_DOWN: + return "Down"; + case AST_STATE_RESERVED: + return "Rsrvd"; + case AST_STATE_OFFHOOK: + return "OffHook"; + case AST_STATE_DIALING: + return "Dialing"; + case AST_STATE_RING: + return "Ring"; + case AST_STATE_RINGING: + return "Ringing"; + case AST_STATE_UP: + return "Up"; + case AST_STATE_BUSY: + return "Busy"; + default: + return "Unknown"; + } +} + struct ast_channel *ast_channel_alloc(void) { struct ast_channel *tmp; struct ast_channel_pvt *pvt; + int x; PTHREAD_MUTEX_LOCK(&chlock); tmp = malloc(sizeof(struct ast_channel)); memset(tmp, 0, sizeof(struct ast_channel)); @@ -116,7 +142,8 @@ struct ast_channel *ast_channel_alloc(void) memset(pvt, 0, sizeof(struct ast_channel_pvt)); tmp->sched = sched_context_create(); if (tmp->sched) { - tmp->fd = -1; + for (x=0;x<AST_MAX_FDS;x++) + tmp->fds[x] = -1; strncpy(tmp->name, "**Unknown**", sizeof(tmp->name)); tmp->pvt = pvt; tmp->state = AST_STATE_DOWN; @@ -206,16 +233,10 @@ void ast_channel_free(struct ast_channel *chan) int ast_softhangup(struct ast_channel *chan) { int res = 0; - if (chan->stream) - ast_stopstream(chan); if (option_debug) ast_log(LOG_DEBUG, "Soft-Hanging up channel '%s'\n", chan->name); - if (chan->pvt->hangup) - res = chan->pvt->hangup(chan); - if (chan->pvt->pvt) - ast_log(LOG_WARNING, "Channel '%s' may not have been hung up properly\n", chan->name); - if (chan->pbx) - ast_log(LOG_WARNING, "PBX may not have been terminated properly on '%s'\n", chan->name); + /* Inform channel driver that we need to be hung up, if it cares */ + chan->softhangup = 1; /* Interrupt any select call or such */ if (chan->blocking) pthread_kill(chan->blocker, SIGURG); @@ -225,6 +246,16 @@ int ast_softhangup(struct ast_channel *chan) int ast_hangup(struct ast_channel *chan) { int res = 0; + /* Don't actually hang up a channel that will masquerade as someone else, or + if someone is going to masquerade as us */ + if (chan->masq) + return 0; + /* If this channel is one which will be masqueraded into something, + mark it as a zombie already, so we know to free it later */ + if (chan->masqr) { + chan->zombie=1; + return 0; + } if (chan->stream) ast_stopstream(chan); if (chan->sched) @@ -235,10 +266,15 @@ int ast_hangup(struct ast_channel *chan) pthread_self(), chan->name, chan->blocker, chan->blockproc); CRASH; } - if (option_debug) - ast_log(LOG_DEBUG, "Hanging up channel '%s'\n", chan->name); - if (chan->pvt->hangup) - res = chan->pvt->hangup(chan); + if (!chan->zombie) { + if (option_debug) + ast_log(LOG_DEBUG, "Hanging up channel '%s'\n", chan->name); + if (chan->pvt->hangup) + res = chan->pvt->hangup(chan); + } else + if (option_debug) + ast_log(LOG_DEBUG, "Hanging up zombie '%s'\n", chan->name); + ast_channel_free(chan); return res; } @@ -271,10 +307,18 @@ void ast_channel_unregister(char *type) int ast_answer(struct ast_channel *chan) { - /* Answer the line, if possible */ - if (chan->state == AST_STATE_RING) { + /* Stop if we're a zombie or need a soft hangup */ + if (chan->zombie || chan->softhangup) + return -1; + switch(chan->state) { + case AST_STATE_RING: + case AST_STATE_RINGING: if (chan->pvt->answer) return chan->pvt->answer(chan); + chan->state = AST_STATE_UP; + break; + case AST_STATE_UP: + break; } return 0; } @@ -293,84 +337,125 @@ int ast_waitfor_n_fd(int *fds, int n, int *ms, int *exception) FD_ZERO(&rfds); FD_ZERO(&efds); for (x=0;x<n;x++) { - FD_SET(fds[x], &rfds); - FD_SET(fds[x], &efds); - if (fds[x] > max) - max = fds[x]; + if (fds[x] > -1) { + FD_SET(fds[x], &rfds); + FD_SET(fds[x], &efds); + if (fds[x] > max) + max = fds[x]; + } } if (*ms >= 0) res = select(max + 1, &rfds, NULL, &efds, &tv); else res = select(max + 1, &rfds, NULL, &efds, NULL); + + if (res < 0) { + /* Simulate a timeout if we were interrupted */ + if (errno != EINTR) + *ms = -1; + else + *ms = 0; + return -1; + } + for (x=0;x<n;x++) { - if ((FD_ISSET(fds[x], &rfds) || FD_ISSET(fds[x], &efds)) && (winner < 0)) { + if ((fds[x] > -1) && (FD_ISSET(fds[x], &rfds) || FD_ISSET(fds[x], &efds)) && (winner < 0)) { if (exception) *exception = FD_ISSET(fds[x], &efds); winner = fds[x]; } } *ms = tv.tv_sec * 1000 + tv.tv_usec / 1000; - if (res < 0) - *ms = -10; return winner; } +static int ast_do_masquerade(struct ast_channel *original); + struct ast_channel *ast_waitfor_n(struct ast_channel **c, int n, int *ms) { /* Wait for x amount of time on a file descriptor to have input. */ struct timeval tv; fd_set rfds, efds; int res; - int x, max=-1; + int x, y, max=-1; struct ast_channel *winner = NULL; + /* Perform any pending masquerades */ + for (x=0;x<n;x++) { + if (c[x]->masq) { + if (ast_do_masquerade(c[x])) { + ast_log(LOG_WARNING, "Masquerade failed\n"); + *ms = -1; + return NULL; + } + } + } + tv.tv_sec = *ms / 1000; tv.tv_usec = (*ms % 1000) * 1000; FD_ZERO(&rfds); FD_ZERO(&efds); for (x=0;x<n;x++) { - FD_SET(c[x]->fd, &rfds); - FD_SET(c[x]->fd, &efds); + for (y=0;y<AST_MAX_FDS;y++) { + if (c[x]->fds[y] > 0) { + FD_SET(c[x]->fds[y], &rfds); + FD_SET(c[x]->fds[y], &efds); + if (c[x]->fds[y] > max) + max = c[x]->fds[y]; + } + } CHECK_BLOCKING(c[x]); - if (c[x]->fd > max) - max = c[x]->fd; } if (*ms >= 0) res = select(max + 1, &rfds, NULL, &efds, &tv); else res = select(max + 1, &rfds, NULL, &efds, NULL); + + if (res < 0) { + for (x=0;x<n;x++) + c[x]->blocking = 0; + /* Simulate a timeout if we were interrupted */ + if (errno != EINTR) + *ms = -1; + else + *ms = 0; + return NULL; + } + for (x=0;x<n;x++) { c[x]->blocking = 0; - if ((FD_ISSET(c[x]->fd, &rfds) || FD_ISSET(c[x]->fd, &efds)) && !winner) { - /* Set exception flag if appropriate */ - if (FD_ISSET(c[x]->fd, &efds)) - c[x]->exception = 1; - winner = c[x]; + for (y=0;y<AST_MAX_FDS;y++) { + if (c[x]->fds[y] > -1) { + if ((FD_ISSET(c[x]->fds[y], &rfds) || FD_ISSET(c[x]->fds[y], &efds)) && !winner) { + /* Set exception flag if appropriate */ + if (FD_ISSET(c[x]->fds[y], &efds)) + c[x]->exception = 1; + c[x]->fdno = y; + winner = c[x]; + } + } } } *ms = tv.tv_sec * 1000 + tv.tv_usec / 1000; - if (res < 0) - *ms = -10; return winner; } int ast_waitfor(struct ast_channel *c, int ms) { - if (ast_waitfor_n(&c, 1, &ms)) { - if (ms < 0) - return -ms; - return ms; - } - /* Error if ms < 0 */ - if (ms < 0) + struct ast_channel *chan; + chan = ast_waitfor_n(&c, 1, &ms); + if (ms < 0) return -1; - return 0; + return ms; } char ast_waitfordigit(struct ast_channel *c, int ms) { struct ast_frame *f; char result = 0; + /* Stop if we're a zombie or need a soft hangup */ + if (c->zombie || c->softhangup) + return -1; /* Wait for a digit, no more than ms milliseconds total. */ while(ms && !result) { ms = ast_waitfor(c, ms); @@ -397,6 +482,24 @@ struct ast_frame *ast_read(struct ast_channel *chan) { AST_FRAME_NULL, }; + + pthread_mutex_lock(&chan->lock); + if (chan->masq) { + if (ast_do_masquerade(chan)) { + ast_log(LOG_WARNING, "Failed to perform masquerade\n"); + f = NULL; + } else + f = &null_frame; + pthread_mutex_unlock(&chan->lock); + return f; + } + + /* Stop if we're a zombie or need a soft hangup */ + if (chan->zombie || chan->softhangup) { + pthread_mutex_unlock(&chan->lock); + return NULL; + } + chan->blocker = pthread_self(); if (chan->exception) { if (chan->pvt->exception) @@ -417,12 +520,35 @@ struct ast_frame *ast_read(struct ast_channel *chan) f = &null_frame; } } + /* Make sure we always return NULL in the future */ + if (!f) + chan->softhangup = 1; + pthread_mutex_unlock(&chan->lock); + return f; } +int ast_indicate(struct ast_channel *chan, int condition) +{ + int res = -1; + /* Stop if we're a zombie or need a soft hangup */ + if (chan->zombie || chan->softhangup) + return -1; + if (chan->pvt->indicate) { + res = chan->pvt->indicate(chan, condition); + if (res) + ast_log(LOG_WARNING, "Driver for channel '%s' failed to indicate condition %d\n", chan->name, condition); + } else + ast_log(LOG_WARNING, "Driver for channel '%s' does not support indication\n", chan->name); + return res; +} + int ast_sendtext(struct ast_channel *chan, char *text) { int res = 0; + /* Stop if we're a zombie or need a soft hangup */ + if (chan->zombie || chan->softhangup) + return -1; CHECK_BLOCKING(chan); if (chan->pvt->send_text) res = chan->pvt->send_text(chan, text); @@ -434,6 +560,18 @@ int ast_write(struct ast_channel *chan, struct ast_frame *fr) { int res = -1; struct ast_frame *f; + /* Stop if we're a zombie or need a soft hangup */ + if (chan->zombie || chan->softhangup) + return -1; + /* Handle any pending masquerades */ + if (chan->masq) { + if (ast_do_masquerade(chan)) { + ast_log(LOG_WARNING, "Failed to perform masquerade\n"); + return -1; + } + } + if (chan->masqr) + return 0; CHECK_BLOCKING(chan); switch(fr->frametype) { case AST_FRAME_CONTROL: @@ -453,6 +591,8 @@ int ast_write(struct ast_channel *chan, struct ast_frame *fr) f = fr; if (f) res = chan->pvt->write(chan, f); + else + res = 0; } } chan->blocking = 0; @@ -483,7 +623,8 @@ int ast_set_write_format(struct ast_channel *chan, int fmts) ast_translator_free_path(chan->pvt->writetrans); /* Build a translation path from the user write format to the raw writing format */ chan->pvt->writetrans = ast_translator_build_path(chan->pvt->rawwriteformat, chan->writeformat); - ast_log(LOG_DEBUG, "Set channel %s to format %d\n", chan->name, chan->writeformat); + if (option_debug) + ast_log(LOG_DEBUG, "Set channel %s to format %d\n", chan->name, chan->writeformat); return 0; } @@ -555,8 +696,12 @@ int ast_call(struct ast_channel *chan, char *addr, int timeout) If the remote end does not answer within the timeout, then do NOT hang up, but return anyway. */ int res = -1; - if (chan->pvt->call) - res = chan->pvt->call(chan, addr, timeout); + /* Stop if we're a zombie or need a soft hangup */ + pthread_mutex_lock(&chan->lock); + if (!chan->zombie && !chan->softhangup) + if (chan->pvt->call) + res = chan->pvt->call(chan, addr, timeout); + pthread_mutex_unlock(&chan->lock); return res; } @@ -565,6 +710,9 @@ int ast_readstring(struct ast_channel *c, char *s, int len, int timeout, int fti int pos=0; int to = ftimeout; char d; + /* Stop if we're a zombie or need a soft hangup */ + if (c->zombie || c->softhangup) + return -1; if (!len) return -1; do { @@ -638,7 +786,134 @@ int ast_channel_make_compatible(struct ast_channel *chan, struct ast_channel *pe return 0; } +int ast_channel_masquerade(struct ast_channel *original, struct ast_channel *clone) +{ + ast_log(LOG_DEBUG, "Planning to masquerade %s into the structure of %s\n", + clone->name, original->name); + if (original->masq) { + ast_log(LOG_WARNING, "%s is already going to masquerade as %s\n", + original->masq->name, original->name); + return -1; + } + if (clone->masqr) { + ast_log(LOG_WARNING, "%s is already going to masquerade as %s\n", + clone->name, clone->masqr->name); + return -1; + } + original->masq = clone; + clone->masqr = original; + return 0; +} + +static int ast_do_masquerade(struct ast_channel *original) +{ + int x; + int res=0; + char *tmp; + struct ast_channel_pvt *p; + struct ast_channel *clone = original->masq; + ast_log(LOG_DEBUG, "Actually Masquerading %s(%d) into the structure of %s(%d)\n", + clone->name, clone->state, original->name, original->state); + /* XXX This is a seriously wacked out operation. We're essentially putting the guts of + the clone channel into the original channel. Start by killing off the original + channel's backend. I'm not sure we're going to keep this function, because + while the features are nice, the cost is very high in terms of pure nastiness. XXX */ + + /* We need the clone's lock, too */ + pthread_mutex_lock(&clone->lock); + + /* Unlink the masquerade */ + original->masq = NULL; + clone->masqr = NULL; + + /* Copy the name from the clone channel */ + strncpy(original->name, clone->name, sizeof(original->name)); + + /* Mangle the name of the clone channel */ + strncat(clone->name, "<MASQ>", sizeof(clone->name)); + + /* Swap the guts */ + p = original->pvt; + original->pvt = clone->pvt; + clone->pvt = p; + + /* Start by disconnecting the original's physical side */ + if (clone->pvt->hangup) + res = clone->pvt->hangup(clone); + if (res) { + ast_log(LOG_WARNING, "Hangup failed! Strange things may happen!\n"); + pthread_mutex_unlock(&clone->lock); + return -1; + } + + /* Mangle the name of the clone channel */ + snprintf(clone->name, sizeof(clone->name), "%s<ZOMBIE>", original->name); + + /* Keep the same language. */ + /* Update the type. */ + original->type = clone->type; + /* Copy the FD's */ + for (x=0;x<AST_MAX_FDS;x++) + original->fds[x] = clone->fds[x]; + /* Bridge remains the same */ + /* CDR fields remain the same */ + /* XXX What about blocking, softhangup, blocker, and lock and blockproc? XXX */ + /* Application and data remain the same */ + /* Clone exception becomes real one, as with fdno */ + original->exception = clone->exception; + original->fdno = clone->fdno; + /* Schedule context remains the same */ + /* Stream stuff stays the same */ + /* Keep the original state. The fixup code will need to work with it most likely */ + + /* dnid and callerid change to become the new, HOWEVER, we also link the original's + fields back into the defunct 'clone' so that they will be freed when + ast_frfree is eventually called */ + tmp = original->dnid; + original->dnid = clone->dnid; + clone->dnid = tmp; + + tmp = original->callerid; + original->callerid = clone->callerid; + clone->callerid = tmp; + + /* Context, extension, priority, app data, jump table, remain the same */ + /* pvt switches. pbx stays the same, as does next */ + + /* Now, at this point, the "clone" channel is totally F'd up. We mark it as + a zombie so nothing tries to touch it. If it's already been marked as a + zombie, then free it now (since it already is considered invalid). */ + if (clone->zombie) { + pthread_mutex_unlock(&clone->lock); + ast_channel_free(clone); + } else { + clone->zombie=1; + pthread_mutex_unlock(&clone->lock); + } + /* Set the write format */ + ast_set_write_format(original, original->writeformat); + + /* Set the read format */ + ast_set_read_format(original, original->readformat); + /* Okay. Last thing is to let the channel driver know about all this mess, so he + can fix up everything as best as possible */ + if (original->pvt->fixup) { + res = original->pvt->fixup(clone, original); + if (res) { + ast_log(LOG_WARNING, "Driver for '%s' could not fixup channel %s\n", + original->type, original->name); + return -1; + } + } else + ast_log(LOG_WARNING, "Driver '%s' does not have a fixup routine (for %s)! Bad things may happen.\n", + original->type, original->name); + + /* Signal any blocker */ + if (original->blocking) + pthread_kill(original->blocker, SIGURG); + return 0; +} int ast_channel_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, struct ast_frame **fo, struct ast_channel **rc) { @@ -648,11 +923,33 @@ int ast_channel_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags int to = -1; struct ast_frame *f; struct ast_channel *who; + int res; + /* Stop if we're a zombie or need a soft hangup */ + if (c0->zombie || c0->softhangup || c1->zombie || c1->softhangup) + return -1; + if (c0->bridge) { + ast_log(LOG_WARNING, "%s is already in a bridge with %s\n", + c0->name, c0->bridge->name); + return -1; + } + if (c1->bridge) { + ast_log(LOG_WARNING, "%s is already in a bridge with %s\n", + c1->name, c1->bridge->name); + return -1; + } + + /* Keep track of bridge */ + c0->bridge = c1; + c1->bridge = c0; + if (c0->pvt->bridge && (c0->pvt->bridge == c1->pvt->bridge)) { /* Looks like they share a bridge code */ - if (!c0->pvt->bridge(c0, c1, flags, fo, rc)) + if (!c0->pvt->bridge(c0, c1, flags, fo, rc)) { + c0->bridge = NULL; + c1->bridge = NULL; return 0; + } ast_log(LOG_WARNING, "Private bridge between %s and %s failed\n", c0->name, c1->name); /* If they return non-zero then continue on normally */ } @@ -664,36 +961,47 @@ int ast_channel_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags for (/* ever */;;) { who = ast_waitfor_n(cs, 2, &to); if (!who) { - ast_log(LOG_WARNING, "Nobody there??\n"); + ast_log(LOG_WARNING, "Nobody there??\n"); continue; } f = ast_read(who); if (!f) { *fo = NULL; *rc = who; - return 0; + res = 0; + break; } if ((f->frametype == AST_FRAME_CONTROL) && !(flags & AST_BRIDGE_IGNORE_SIGS)) { *fo = f; *rc = who; - return 0; + res = 0; + break; } if ((f->frametype == AST_FRAME_VOICE) || (f->frametype == AST_FRAME_TEXT) || (f->frametype == AST_FRAME_VIDEO) || (f->frametype == AST_FRAME_IMAGE) || (f->frametype == AST_FRAME_DTMF)) { - if ((f->frametype == AST_FRAME_DTMF) && (flags & (AST_BRIDGE_DTMF_CHANNEL_0 | AST_BRIDGE_DTMF_CHANNEL_1))) { - if ((who == c0) && (flags & AST_BRIDGE_DTMF_CHANNEL_0)) { - *rc = c0; - *fo = f; - /* Take out of conference mode */ - return 0; + if ((f->frametype == AST_FRAME_DTMF) && + (flags & (AST_BRIDGE_DTMF_CHANNEL_0 | AST_BRIDGE_DTMF_CHANNEL_1))) { + if ((who == c0)) { + if ((flags & AST_BRIDGE_DTMF_CHANNEL_0)) { + *rc = c0; + *fo = f; + /* Take out of conference mode */ + res = 0; + break; + } else + goto tackygoto; } else - if ((who == c1) && (flags & AST_BRIDGE_DTMF_CHANNEL_1)) { - *rc = c1; - *fo = f; - return 0; + if ((who == c1)) { + if (flags & AST_BRIDGE_DTMF_CHANNEL_1) { + *rc = c1; + *fo = f; + res = 0; + break; + } else + goto tackygoto; } } else { #if 0 @@ -702,6 +1010,7 @@ int ast_channel_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags ast_log(LOG_DEBUG, "Servicing channel %s twice in a row?\n", last->name); last = who; #endif +tackygoto: if (who == c0) ast_write(c1, f); else @@ -715,5 +1024,27 @@ int ast_channel_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags cs[0] = cs[1]; cs[1] = cs[2]; } + c0->bridge = NULL; + c1->bridge = NULL; + return res; +} + +int ast_channel_setoption(struct ast_channel *chan, int option, void *data, int datalen, int block) +{ + int res; + if (chan->pvt->setoption) { + res = chan->pvt->setoption(chan, option, data, datalen); + if (res < 0) + return res; + } else { + errno = ENOSYS; + return -1; + } + if (block) { + /* XXX Implement blocking -- just wait for our option frame reply, discarding + intermediate packets. XXX */ + ast_log(LOG_ERROR, "XXX Blocking not implemented yet XXX\n"); + return -1; + } return 0; } diff --git a/include/asterisk/channel.h b/include/asterisk/channel.h index 3f7d5e74491a15a31e54543ea104804e18c64852..3d0a3027755c50dd80556a1028d64d195e8896dc 100755 --- a/include/asterisk/channel.h +++ b/include/asterisk/channel.h @@ -25,6 +25,60 @@ extern "C" { #include <pthread.h> +#ifdef DEBUG_THREADS + +#define TRIES 500 + +#include <errno.h> +#include <string.h> +#include <stdio.h> +#include <unistd.h> + +struct mutex_info { + pthread_mutex_t *mutex; + char *file; + int lineno; + char *func; + struct mutex_info *next; +}; + +static inline int __ast_pthread_mutex_lock(char *filename, int lineno, char *func, pthread_mutex_t *t) { + int res; + int tries = TRIES; + do { + res = pthread_mutex_trylock(t); + /* If we can't run, yield */ + if (res) { + sched_yield(); + usleep(1); + } + } while(res && tries--); + if (res) { + fprintf(stderr, "%s line %d (%s): Error obtaining mutex: %s\n", + filename, lineno, func, strerror(res)); + res = pthread_mutex_lock(t); + fprintf(stderr, "%s line %d (%s): Got it eventually...\n", + filename, lineno, func); + } + return res; +} + +#define ast_pthread_mutex_lock(a) __ast_pthread_mutex_lock(__FILE__, __LINE__, __PRETTY_FUNCTION__, a) + +static inline int __ast_pthread_mutex_unlock(char *filename, int lineno, char *func, pthread_mutex_t *t) { + int res; + res = pthread_mutex_unlock(t); + if (res) + fprintf(stderr, "%s line %d (%s): Error releasing mutex: %s\n", + filename, lineno, func, strerror(res)); + return res; +} +#define ast_pthread_mutex_unlock(a) __ast_pthread_mutex_unlock(__FILE__, __LINE__, __PRETTY_FUNCTION__, a) +#else +#define ast_pthread_mutex_lock pthread_mutex_lock +#define ast_pthread_mutex_unlock pthread_mutex_unlock +#endif + #define AST_CHANNEL_NAME 80 #define AST_CHANNEL_MAX_STACK 32 @@ -33,14 +87,24 @@ extern "C" { /* Max length an extension can be (unique) is this number */ #define AST_MAX_EXTENSION 80 +#define AST_MAX_FDS 4 + struct ast_channel { char name[AST_CHANNEL_NAME]; /* ASCII Description of channel name */ char language[MAX_LANGUAGE]; /* Language requested */ char *type; /* Type of channel */ - int fd; /* File descriptor for channel -- all must have - a file descriptor! */ + int fds[AST_MAX_FDS]; /* File descriptor for channel -- Drivers will poll + on these file descriptors, so at least one must be + non -1. */ + struct ast_channel *bridge; /* Who are we bridged to, if we're bridged */ + struct ast_channel *masq; /* Channel that will masquerade as us */ + struct ast_channel *masqr; /* Who we are masquerading as */ + int cdrflags; /* Call Detail Record Flags */ + int blocking; /* Whether or not we're blocking */ + int softhangup; /* Whether or not we have been hung up */ + int zombie; /* Non-zero if this is a zombie channel */ pthread_t blocker; /* If anyone is blocking, this is them */ pthread_mutex_t lock; /* Lock, can be used to lock a channel for some operations */ char *blockproc; /* Procedure causing blocking */ @@ -49,6 +113,7 @@ struct ast_channel { char *data; /* Data passed to current application */ int exception; /* Has an exception been detected */ + int fdno; /* Which fd had an event detected on */ struct sched_context *sched; /* Schedule context */ int streamid; /* For streaming playback, the schedule ID */ @@ -79,6 +144,11 @@ struct ast_channel { }; +#define AST_CDR_TRANSFER (1 << 0) +#define AST_CDR_FORWARD (1 << 1) +#define AST_CDR_CALLWAIT (1 << 2) +#define AST_CDR_CONFERENCE (1 << 3) + /* Bits 0-15 of state are reserved for the state (up/down) of the line */ #define AST_STATE_DOWN 0 /* Channel is down and available */ @@ -123,6 +193,9 @@ int ast_answer(struct ast_channel *chan); the number of seconds the connect took otherwise. */ int ast_call(struct ast_channel *chan, char *addr, int timeout); +/* Indicate a condition such as AST_CONTROL_BUSY, AST_CONTROL_RINGING, or AST_CONTROL_CONGESTION on a channel */ +int ast_indicate(struct ast_channel *chan, int condition); + /* Misc stuff */ /* Wait for input on a channel for a given # of milliseconds (<0 for indefinite). @@ -180,6 +253,28 @@ int ast_channel_make_compatible(struct ast_channel *c0, struct ast_channel *c1); *rf (remember, it could be NULL) and which channel (0 or 1) in rc */ int ast_channel_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, struct ast_frame **fo, struct ast_channel **rc); +/* This is a very strange and freaky function used primarily for transfer. Suppose that + "original" and "clone" are two channels in random situations. This function takes + the guts out of "clone" and puts them into the "original" channel, then alerts the + channel driver of the change, asking it to fixup any private information (like the + p->owner pointer) that is affected by the change. The physical layer of the original + channel is hung up. */ +int ast_channel_masquerade(struct ast_channel *original, struct ast_channel *clone); + +/* Give a name to a state */ +char *ast_state2str(int state); + +/* Options: Some low-level drivers may implement "options" allowing fine tuning of the + low level channel. See frame.h for options. Note that many channel drivers may support + none or a subset of those features, and you should not count on this if you want your + asterisk application to be portable. They're mainly useful for tweaking performance */ + +/* Set an option on a channel (see frame.h), optionally blocking awaiting the reply */ +int ast_channel_setoption(struct ast_channel *channel, int option, void *data, int datalen, int block); + +/* Query the value of an option, optionally blocking until a reply is received */ +struct ast_frame *ast_channel_queryoption(struct ast_channel *channel, int option, void *data, int *datalen, int block); + #ifdef DO_CRASH #define CRASH do { *((int *)0) = 0; } while(0) #else diff --git a/include/asterisk/channel_pvt.h b/include/asterisk/channel_pvt.h index 7fc464ebbed715e958abea80fe33cc4f1f07d119..a8a2e654d83db3aa0378b11dda84edab4f55f891 100755 --- a/include/asterisk/channel_pvt.h +++ b/include/asterisk/channel_pvt.h @@ -23,7 +23,7 @@ extern "C" { struct ast_channel_pvt { /* Private data used by channel backend */ - void *pvt; + void *pvt; /* Write translation path */ struct ast_trans_pvt *writetrans; /* Read translation path */ @@ -51,6 +51,14 @@ struct ast_channel_pvt { struct ast_frame * (*exception)(struct ast_channel *chan); /* Bridge two channels of the same type together */ int (*bridge)(struct ast_channel *c0, struct ast_channel *c1, int flags, struct ast_frame **fo, struct ast_channel **rc); + /* Indicate a particular condition (e.g. AST_CONTROL_BUSY or AST_CONTROL_RINGING or AST_CONTROL_CONGESTION */ + int (*indicate)(struct ast_channel *c, int condition); + /* Fix up a channel: If a channel is consumed, this is called. Basically update any ->owner links */ + int (*fixup)(struct ast_channel *oldchan, struct ast_channel *newchan); + /* Set a given option */ + int (*setoption)(struct ast_channel *chan, int option, void *data, int datalen); + /* Query a given option */ + int (*queryoption)(struct ast_channel *chan, int option, void *data, int *datalen); }; /* Create a channel structure */