many bug fixes and inbound calling: SKINNY/line@device

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@1507 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
Jeremy McNamara
2003-09-13 23:30:30 +00:00
parent 56f915e783
commit c1c5fcd426

View File

@@ -478,7 +478,6 @@ struct hostent *hp;
static int skinnysock = -1; static int skinnysock = -1;
static pthread_t tcp_thread; static pthread_t tcp_thread;
static pthread_t accept_t; static pthread_t accept_t;
static ast_mutex_t devicelock = AST_MUTEX_INITIALIZER;
static char context[AST_MAX_EXTENSION] = "default"; static char context[AST_MAX_EXTENSION] = "default";
static char language[MAX_LANGUAGE] = ""; static char language[MAX_LANGUAGE] = "";
static char musicclass[MAX_LANGUAGE] = ""; static char musicclass[MAX_LANGUAGE] = "";
@@ -528,6 +527,17 @@ static int callnums = 1;
#define SKINNY_REORDER 37 #define SKINNY_REORDER 37
#define SKINNY_CALLWAITTONE 45 #define SKINNY_CALLWAITTONE 45
#define SKINNY_LAMP_OFF 1
#define SKINNY_LAMP_ON 2
#define SKINNY_LAMP_WINK 3
#define SKINNY_LAMP_FLASH 4
#define SKINNY_LAMP_BLINK 5
#define SKINNY_RING_OFF 1
#define SKINNY_RING_INSIDE 2
#define SKINNY_RING_OUTSIDE 3
#define SKINNY_RING_FEATURE 4
#define TYPE_TRUNK 1 #define TYPE_TRUNK 1
#define TYPE_LINE 2 #define TYPE_LINE 2
@@ -571,9 +581,12 @@ static ast_mutex_t usecnt_lock = AST_MUTEX_INITIALIZER;
/* Protect the monitoring thread, so only one process can kill or start it, and not /* Protect the monitoring thread, so only one process can kill or start it, and not
when it's doing something critical. */ when it's doing something critical. */
static ast_mutex_t monlock = AST_MUTEX_INITIALIZER; static ast_mutex_t monlock = AST_MUTEX_INITIALIZER;
/* Protect the network socket */
static ast_mutex_t netlock = AST_MUTEX_INITIALIZER; static ast_mutex_t netlock = AST_MUTEX_INITIALIZER;
/* Protect the session list */
static ast_mutex_t sessionlock = AST_MUTEX_INITIALIZER; static ast_mutex_t sessionlock = AST_MUTEX_INITIALIZER;
/* Protect the device list */
static ast_mutex_t devicelock = AST_MUTEX_INITIALIZER;
/* This is the thread for the monitor which checks for input on the channels /* This is the thread for the monitor which checks for input on the channels
which are not currently in use. */ which are not currently in use. */
@@ -679,7 +692,7 @@ static skinny_req *req_alloc(size_t size)
return req; return req;
} }
static struct skinny_subchannel *find_subchannel(struct skinny_line *l) static struct skinny_subchannel *find_subchannel_by_line(struct skinny_line *l)
{ {
/* Need to figure out how to determine which sub we want */ /* Need to figure out how to determine which sub we want */
@@ -687,6 +700,59 @@ static struct skinny_subchannel *find_subchannel(struct skinny_line *l)
return sub; return sub;
} }
static struct skinny_subchannel *find_subchannel_by_name(char *dest)
{
struct skinny_line *l;
struct skinny_device *d;
char line[256];
char *at;
char *device;
printf("dest: %s\n", dest);
strncpy(line, dest, sizeof(line) - 1);
at = strchr(line, '@');
if (!at) {
ast_log(LOG_NOTICE, "Device '%s' has no @ (at) sign!\n", dest);
return NULL;
}
*at = '\0';
at++;
device = at;
printf("line: %s\n", line);
printf("device: %s\n", device);
ast_mutex_lock(&devicelock);
d = devices;
while(d) {
if (!strcasecmp(d->name, device)) {
if (skinnydebug) {
printf("Found device: %s\n", d->name);
}
/* Found the device */
l = d->lines;
while (l) {
/* Search for the right line */
if (!strcasecmp(l->name, line)) {
ast_mutex_unlock(&devicelock);
if (skinnydebug) {
printf("Found line: %s\n", l->name);
}
return l->sub;
}
printf("line cycle\n");
l = l->next;
}
}
printf("device cycle\n");
d = d->next;
}
/* Device not found*/
ast_mutex_unlock(&devicelock);
return NULL;
}
static int transmit_response(struct skinnysession *s, skinny_req *req) static int transmit_response(struct skinnysession *s, skinny_req *req)
{ {
int res = 0; int res = 0;
@@ -713,7 +779,7 @@ static void transmit_speaker_mode(struct skinnysession *s, int mode)
{ {
skinny_req *req; skinny_req *req;
req = req_alloc(sizeof(struct set_ringer_message)); req = req_alloc(sizeof(struct set_speaker_message));
if (!req) { if (!req) {
ast_log(LOG_ERROR, "Unable to allocate skinny_request, this is bad\n"); ast_log(LOG_ERROR, "Unable to allocate skinny_request, this is bad\n");
return; return;
@@ -838,7 +904,7 @@ static void transmit_lamp_indication(struct skinnysession *s, int instance, int
} }
req->len = sizeof(set_lamp_message)+4; req->len = sizeof(set_lamp_message)+4;
req->e = SET_LAMP_MESSAGE; req->e = SET_LAMP_MESSAGE;
req->data.setlamp.stimulus = 9; // magic number req->data.setlamp.stimulus = 0x9; // magic number
req->data.setlamp.stimulusInstance = instance; req->data.setlamp.stimulusInstance = instance;
req->data.setlamp.deviceStimulus = indication; req->data.setlamp.deviceStimulus = indication;
transmit_response(s, req); transmit_response(s, req);
@@ -859,9 +925,6 @@ static void transmit_ringer_mode(struct skinnysession *s, int mode)
transmit_response(s, req); transmit_response(s, req);
} }
/* I do not believe skinny can deal with video. /* I do not believe skinny can deal with video.
Anyone know differently? */ Anyone know differently? */
static struct ast_rtp *skinny_get_vrtp_peer(struct ast_channel *chan) static struct ast_rtp *skinny_get_vrtp_peer(struct ast_channel *chan)
@@ -1369,9 +1432,16 @@ static void *skinny_ss(void *data)
static int skinny_call(struct ast_channel *ast, char *dest, int timeout) static int skinny_call(struct ast_channel *ast, char *dest, int timeout)
{ {
int res; int res = 0;
int tone = 0;
struct skinny_line *l; struct skinny_line *l;
struct skinny_subchannel *sub; struct skinny_subchannel *sub;
struct skinnysession *session;
if ((ast->_state != AST_STATE_DOWN) && (ast->_state != AST_STATE_RESERVED)) {
ast_log(LOG_WARNING, "skinny_call called on %s, neither down nor reserved\n", ast->name);
return -1;
}
if (skinnydebug) { if (skinnydebug) {
ast_verbose(VERBOSE_PREFIX_3 "skinny_call(%s)\n", ast->name); ast_verbose(VERBOSE_PREFIX_3 "skinny_call(%s)\n", ast->name);
@@ -1379,48 +1449,60 @@ static int skinny_call(struct ast_channel *ast, char *dest, int timeout)
sub = ast->pvt->pvt; sub = ast->pvt->pvt;
l = sub->parent; l = sub->parent;
session = l->parent->session;
switch (l->hookstate) { if (l->dnd) {
ast_queue_control(ast, AST_CONTROL_BUSY, 0);
return 0;
}
switch (l->hookstate) {
case SKINNY_OFFHOOK: case SKINNY_OFFHOOK:
// call waiting tone = SKINNY_CALLWAITTONE;
break; break;
case SKINNY_ONHOOK: case SKINNY_ONHOOK:
tone = SKINNY_ALERT;
break;
default: default:
// ring ast_log(LOG_ERROR, "Don't know how to deal with hookstate %d\n", l->hookstate);
break; break;
} }
if ((ast->_state != AST_STATE_DOWN) && (ast->_state != AST_STATE_RESERVED)) { transmit_lamp_indication(session, l->instance, SKINNY_LAMP_BLINK);
ast_log(LOG_WARNING, "skinny_call called on %s, neither down nor reserved\n", ast->name); transmit_ringer_mode(session, SKINNY_RING_INSIDE);
return -1; transmit_tone(session, tone);
} transmit_callstate(session, l->instance, SKINNY_RINGIN, sub->callid);
// Set the prompt
// Select the active softkeys
ast_setstate(ast, AST_STATE_RINGING);
ast_queue_control(ast, AST_CONTROL_RINGING, 0);
res = 0;
sub->outgoing = 1; sub->outgoing = 1;
sub->cxmode = SKINNY_CX_RECVONLY; // sub->cxmode = SKINNY_CX_RECVONLY;
if (l->type == TYPE_LINE) { if (l->type == TYPE_LINE) {
if (!sub->rtp) { if (!sub->rtp) {
start_rtp(sub); start_rtp(sub);
} else { } else {
/* do we need to anything if there already is an RTP allocated? */
// transmit_modify_request(sub); // transmit_modify_request(sub);
} }
#if 0
if (sub->next->owner && sub->next->callid) { if (sub->next->owner && sub->next->callid) {
/* try to prevent a callwait from disturbing the other connection */ /* try to prevent a callwait from disturbing the other connection */
sub->next->cxmode = SKINNY_CX_RECVONLY; sub->next->cxmode = SKINNY_CX_RECVONLY;
// transmit_modify_request(sub->next); // transmit_modify_request(sub->next);
} }
// transmit_notify_request_with_callerid(sub, tone, ast->callerid);
ast_setstate(ast, AST_STATE_RINGING);
ast_queue_control(ast, AST_CONTROL_RINGING, 0);
/* not sure what this doing */ /* not sure what this doing */
if (sub->next->owner && sub->next->callid) { if (sub->next->owner && sub->next->callid) {
/* Put the connection back in sendrecv */ /* Put the connection back in sendrecv */
sub->next->cxmode = SKINNY_CX_SENDRECV; sub->next->cxmode = SKINNY_CX_SENDRECV;
// transmit_modify_request(sub->next); // transmit_modify_request(sub->next);
} }
#endif
} else { } else {
ast_log(LOG_NOTICE, "Don't know how to dial on trunks yet\n"); ast_log(LOG_NOTICE, "Don't know how to dial on trunks yet\n");
@@ -1720,6 +1802,12 @@ static int handle_message(skinny_req *req, struct skinnysession *s)
struct tm *cmtime; struct tm *cmtime;
pthread_t t; pthread_t t;
if ( (!s->device) && (req->e != REGISTER_MESSAGE && req->e != ALARM_MESSAGE)) {
ast_log(LOG_WARNING, "Client sent message #%d without first registering.\n", req->e);
free(req);
return 0;
}
switch(req->e) { switch(req->e) {
case ALARM_MESSAGE: case ALARM_MESSAGE:
@@ -1804,7 +1892,7 @@ static int handle_message(skinny_req *req, struct skinnysession *s)
if (skinnydebug) if (skinnydebug)
printf("Recieved Stimulus: Line\n"); printf("Recieved Stimulus: Line\n");
sub = find_subchannel(s->device->lines); sub = find_subchannel_by_line(s->device->lines);
transmit_speaker_mode(s, 1); // Turn on transmit_speaker_mode(s, 1); // Turn on
break; break;
default: default:
@@ -1921,7 +2009,7 @@ static int handle_message(skinny_req *req, struct skinnysession *s)
memset(req, 0, SKINNY_MAX_PACKET); memset(req, 0, SKINNY_MAX_PACKET);
req->len = sizeof(line_stat_res_message)+4; req->len = sizeof(line_stat_res_message)+4;
req->e = LINE_STAT_RES_MESSAGE; req->e = LINE_STAT_RES_MESSAGE;
sub = find_subchannel(s->device->lines); sub = find_subchannel_by_line(s->device->lines);
if (!sub) { if (!sub) {
ast_log(LOG_NOTICE, "No available lines on: %s\n", s->device->name); ast_log(LOG_NOTICE, "No available lines on: %s\n", s->device->name);
return 0; return 0;
@@ -1951,38 +2039,44 @@ static int handle_message(skinny_req *req, struct skinnysession *s)
transmit_response(s, req); transmit_response(s, req);
break; break;
case OFFHOOK_MESSAGE: case OFFHOOK_MESSAGE:
transmit_ringer_mode(s,1); // Ring off transmit_ringer_mode(s,SKINNY_RING_OFF);
transmit_lamp_indication(s, s->device->lines->instance, 2); // Lamp on transmit_lamp_indication(s, s->device->lines->instance, SKINNY_LAMP_ON);
sub = find_subchannel(s->device->lines); sub = find_subchannel_by_line(s->device->lines);
if (!sub) { if (!sub) {
ast_log(LOG_NOTICE, "No available lines on: %s\n", s->device->name); ast_log(LOG_NOTICE, "No available lines on: %s\n", s->device->name);
return 0; return 0;
} }
sub->parent->hookstate = SKINNY_OFFHOOK; sub->parent->hookstate = SKINNY_OFFHOOK;
if (!sub->owner) {
if (sub->outgoing) { if (sub->outgoing) {
// deal with asterisk skinny outbound calls
} else {
transmit_callstate(s, s->device->lines->instance, SKINNY_OFFHOOK, sub->callid); transmit_callstate(s, s->device->lines->instance, SKINNY_OFFHOOK, sub->callid);
transmit_tone(s, SKINNY_DIALTONE); transmit_tone(s, SKINNY_SILENCE);
c = skinny_new(sub, AST_STATE_DOWN); ast_setstate(sub->owner, AST_STATE_UP);
if (c) { // select soft keys
/* start switch */ } else {
if (pthread_create(&t, NULL, skinny_ss, c)) { if (!sub->owner) {
ast_log(LOG_WARNING, "Unable to create switch thread: %s\n", strerror(errno));
ast_hangup(c); transmit_callstate(s, s->device->lines->instance, SKINNY_OFFHOOK, sub->callid);
transmit_tone(s, SKINNY_DIALTONE);
c = skinny_new(sub, AST_STATE_DOWN);
if(c) {
/* start switch */
if (pthread_create(&t, NULL, skinny_ss, c)) {
ast_log(LOG_WARNING, "Unable to create switch thread: %s\n", strerror(errno));
ast_hangup(c);
}
} else {
ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", sub->parent->name, s->device->name);
} }
} else { } else {
ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", sub->parent->name, s->device->name); ast_log(LOG_DEBUG, "Current sub [%s] already has owner\n", sub->owner->name);
} }
} }
} else {
ast_log(LOG_DEBUG, "Current sub [%s] already has owner\n", sub->owner->name);
}
break; break;
case ONHOOK_MESSAGE: case ONHOOK_MESSAGE:
sub = find_subchannel(s->device->lines); sub = find_subchannel_by_line(s->device->lines);
if (sub->parent->hookstate == SKINNY_ONHOOK) { if (sub->parent->hookstate == SKINNY_ONHOOK) {
/* Somthing else already put us back on hook */ /* Somthing else already put us back on hook */
break; break;
@@ -2022,9 +2116,9 @@ static int handle_message(skinny_req *req, struct skinnysession *s)
} }
if ((sub->parent->hookstate == SKINNY_ONHOOK) && (!sub->rtp) && (!sub->next->rtp)) { if ((sub->parent->hookstate == SKINNY_ONHOOK) && (!sub->rtp) && (!sub->next->rtp)) {
if (has_voicemail(sub->parent)) { if (has_voicemail(sub->parent)) {
transmit_lamp_indication(s, s->device->lines->instance, 4); // Flash transmit_lamp_indication(s, s->device->lines->instance, SKINNY_LAMP_FLASH);
} else { } else {
transmit_lamp_indication(s, s->device->lines->instance, 1); // Off transmit_lamp_indication(s, s->device->lines->instance, SKINNY_LAMP_OFF);
} }
} }
break; break;
@@ -2043,7 +2137,7 @@ static int handle_message(skinny_req *req, struct skinnysession *s)
} }
f.subclass = d; f.subclass = d;
f.src = "skinny"; f.src = "skinny";
sub = find_subchannel(s->device->lines); sub = find_subchannel_by_line(s->device->lines);
if (sub->owner) { if (sub->owner) {
/* XXX MUST queue this frame to all subs in threeway call if threeway call is active */ /* XXX MUST queue this frame to all subs in threeway call if threeway call is active */
@@ -2069,7 +2163,7 @@ static int handle_message(skinny_req *req, struct skinnysession *s)
memcpy(&sin.sin_addr, addr, sizeof(sin.sin_addr)); // Endian? memcpy(&sin.sin_addr, addr, sizeof(sin.sin_addr)); // Endian?
sin.sin_port = htons(port); sin.sin_port = htons(port);
sub = find_subchannel(s->device->lines); sub = find_subchannel_by_line(s->device->lines);
ast_rtp_set_peer(sub->rtp, &sin); ast_rtp_set_peer(sub->rtp, &sin);
ast_rtp_get_us(sub->rtp, &us); ast_rtp_get_us(sub->rtp, &us);
@@ -2141,16 +2235,17 @@ static int get_input(struct skinnysession *s)
if (res < 0) { if (res < 0) {
ast_log(LOG_WARNING, "Select returned error: %s\n", strerror(errno)); ast_log(LOG_WARNING, "Select returned error: %s\n", strerror(errno));
} else if (res > 0) { } else if (res > 0) {
ast_mutex_lock(&s->lock);
memset(s->inbuf,0,sizeof(s->inbuf)); memset(s->inbuf,0,sizeof(s->inbuf));
res = read(s->fd, s->inbuf, 4); res = read(s->fd, s->inbuf, 4);
dlen = *(int *)s->inbuf; if (res != 4) {
if (res < 1) { ast_log(LOG_WARNING, "Skinny Client sent less data than expected.\n");
return -1; return -1;
} }
dlen = *(int *)s->inbuf;
res = read(s->fd, s->inbuf+4, dlen+4); res = read(s->fd, s->inbuf+4, dlen+4);
ast_mutex_unlock(&s->lock); ast_mutex_unlock(&s->lock);
if (res < 1) { if (res != (dlen+4)) {
ast_log(LOG_WARNING, "Skinny Client sent less data than expected.\n");
return -1; return -1;
} }
@@ -2172,6 +2267,7 @@ static skinny_req *skinny_req_parse(struct skinnysession *s)
memcpy(req, s->inbuf, *(int*)(s->inbuf)+8); // +8 memcpy(req, s->inbuf, *(int*)(s->inbuf)+8); // +8
if (req->e < 0) { if (req->e < 0) {
ast_log(LOG_ERROR, "Event Message is NULL from socket %d, This is bad\n", s->fd); ast_log(LOG_ERROR, "Event Message is NULL from socket %d, This is bad\n", s->fd);
free(req);
return NULL; return NULL;
} }
return req; return req;
@@ -2325,7 +2421,6 @@ static struct ast_channel *skinny_request(char *type, int format, void *data)
{ {
int oldformat; int oldformat;
struct skinny_subchannel *sub; struct skinny_subchannel *sub;
struct skinny_device *d = NULL;
struct ast_channel *tmpc = NULL; struct ast_channel *tmpc = NULL;
char tmp[256]; char tmp[256];
char *dest = data; char *dest = data;
@@ -2336,16 +2431,16 @@ static struct ast_channel *skinny_request(char *type, int format, void *data)
ast_log(LOG_NOTICE, "Asked to get a channel of unsupported format '%d'\n", format); ast_log(LOG_NOTICE, "Asked to get a channel of unsupported format '%d'\n", format);
return NULL; return NULL;
} }
strncpy(tmp, dest, sizeof(tmp) - 1); // XXX FIX
strncpy(tmp, dest, sizeof(tmp) - 1);
if (!strlen(tmp)) { if (!strlen(tmp)) {
ast_log(LOG_NOTICE, "Skinny channels require something!?\n"); ast_log(LOG_NOTICE, "Skinny channels require a device\n");
return NULL; return NULL;
} }
sub = find_subchannel(d->lines); sub = find_subchannel_by_name(tmp);
if (!sub) { if (!sub) {
ast_log(LOG_NOTICE, "No available lines on: %s\n", d->name); ast_log(LOG_NOTICE, "No available lines on: %s\n", dest);
return NULL; return NULL;
} }
@@ -2376,14 +2471,14 @@ static int reload_config(void)
if (gethostname(ourhost, sizeof(ourhost))) { if (gethostname(ourhost, sizeof(ourhost))) {
ast_log(LOG_WARNING, "Unable to get hostname, Skinny disabled\n"); ast_log(LOG_WARNING, "Unable to get hostname, Skinny disabled\n");
return 0; return 1;
} }
cfg = ast_load(config); cfg = ast_load(config);
/* We *must* have a config file otherwise stop immediately */ /* We *must* have a config file otherwise stop immediately */
if (!cfg) { if (!cfg) {
ast_log(LOG_NOTICE, "Unable to load config %s, Skinny disabled\n", config); ast_log(LOG_NOTICE, "Unable to load config %s, Skinny disabled\n", config);
return 0; return 1;
} }
/* load the general section */ /* load the general section */