Allow a Skinny device to monitor a dialplan hint (w00t!).

See skinny.conf.sample for configuration example.


Note: Some devices (seen on 12SP+/30VIP) will lock up if they monitor too many hints.
This seems to be a hardware limitation - there isn't anything we can do about it.


git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@56594 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
Jason Parker
2007-02-24 02:23:43 +00:00
parent a23096985b
commit 97ab07a9e8
2 changed files with 183 additions and 42 deletions

View File

@@ -409,8 +409,8 @@ struct button_definition_template {
/* Custom button types - add our own between 0xB0 and 0xCF. /* Custom button types - add our own between 0xB0 and 0xCF.
This may need to be revised in the future, This may need to be revised in the future,
if stimuluses are ever added in this range. */ if stimuluses are ever added in this range. */
#define BT_CUST_LINESPEEDDIAL 0xB0 /* line or speeddial */ #define BT_CUST_LINESPEEDDIAL 0xB0 /* line or speeddial with/without hint */
#define BT_CUST_HINT 0xB1 /* pipe dream */ #define BT_CUST_LINE 0xB1 /* line or speeddial with hint only */
struct button_template_res_message { struct button_template_res_message {
uint32_t buttonOffset; uint32_t buttonOffset;
@@ -830,6 +830,7 @@ static int callnums = 1;
#define SKINNY_TRANSFER 10 #define SKINNY_TRANSFER 10
#define SKINNY_PARK 11 #define SKINNY_PARK 11
#define SKINNY_PROGRESS 12 #define SKINNY_PROGRESS 12
#define SKINNY_CALLREMOTEMULTILINE 13
#define SKINNY_INVALID 14 #define SKINNY_INVALID 14
#define SKINNY_SILENCE 0x00 #define SKINNY_SILENCE 0x00
@@ -974,8 +975,12 @@ struct skinny_line {
struct skinny_speeddial { struct skinny_speeddial {
ast_mutex_t lock; ast_mutex_t lock;
char label[42]; char label[42];
char context[AST_MAX_CONTEXT];
char exten[AST_MAX_EXTENSION]; char exten[AST_MAX_EXTENSION];
int instance; int instance;
int stateid;
int laststate;
int isHint;
struct skinny_speeddial *next; struct skinny_speeddial *next;
struct skinny_device *parent; struct skinny_device *parent;
@@ -1059,6 +1064,8 @@ static const struct ast_channel_tech skinny_tech = {
/* .bridge = ast_rtp_bridge, */ /* .bridge = ast_rtp_bridge, */
}; };
static int skinny_extensionstate_cb(char *context, char* exten, int state, void *data);
static void *get_button_template(struct skinnysession *s, struct button_definition_template *btn) static void *get_button_template(struct skinnysession *s, struct button_definition_template *btn)
{ {
struct skinny_device *d = s->device; struct skinny_device *d = s->device;
@@ -1070,7 +1077,7 @@ static void *get_button_template(struct skinnysession *s, struct button_definiti
case SKINNY_DEVICE_30VIP: case SKINNY_DEVICE_30VIP:
/* 13 rows, 2 columns */ /* 13 rows, 2 columns */
for (i = 0; i < 4; i++) for (i = 0; i < 4; i++)
(btn++)->buttonDefinition = BT_LINE; (btn++)->buttonDefinition = BT_CUST_LINE;
(btn++)->buttonDefinition = BT_REDIAL; (btn++)->buttonDefinition = BT_REDIAL;
(btn++)->buttonDefinition = BT_VOICEMAIL; (btn++)->buttonDefinition = BT_VOICEMAIL;
(btn++)->buttonDefinition = BT_CALLPARK; (btn++)->buttonDefinition = BT_CALLPARK;
@@ -1087,16 +1094,15 @@ static void *get_button_template(struct skinnysession *s, struct button_definiti
case SKINNY_DEVICE_12: case SKINNY_DEVICE_12:
/* 6 rows, 2 columns */ /* 6 rows, 2 columns */
for (i = 0; i < 2; i++) for (i = 0; i < 2; i++)
(btn++)->buttonDefinition = BT_LINE; (btn++)->buttonDefinition = BT_CUST_LINE;
(btn++)->buttonDefinition = BT_REDIAL; for (i = 0; i < 4; i++)
for (i = 0; i < 3; i++)
(btn++)->buttonDefinition = BT_SPEEDDIAL; (btn++)->buttonDefinition = BT_SPEEDDIAL;
(btn++)->buttonDefinition = BT_HOLD; (btn++)->buttonDefinition = BT_HOLD;
(btn++)->buttonDefinition = BT_REDIAL;
(btn++)->buttonDefinition = BT_TRANSFER; (btn++)->buttonDefinition = BT_TRANSFER;
(btn++)->buttonDefinition = BT_FORWARDALL; (btn++)->buttonDefinition = BT_FORWARDALL;
(btn++)->buttonDefinition = BT_CALLPARK; (btn++)->buttonDefinition = BT_CALLPARK;
(btn++)->buttonDefinition = BT_VOICEMAIL; (btn++)->buttonDefinition = BT_VOICEMAIL;
(btn++)->buttonDefinition = BT_CONFERENCE;
break; break;
case SKINNY_DEVICE_7910: case SKINNY_DEVICE_7910:
(btn++)->buttonDefinition = BT_LINE; (btn++)->buttonDefinition = BT_LINE;
@@ -1285,12 +1291,12 @@ static struct skinny_subchannel *find_subchannel_by_reference(struct skinny_devi
return sub; return sub;
} }
static struct skinny_speeddial *find_speeddial_by_instance(struct skinny_device *d, int instance) static struct skinny_speeddial *find_speeddial_by_instance(struct skinny_device *d, int instance, int isHint)
{ {
struct skinny_speeddial *sd; struct skinny_speeddial *sd;
for (sd = d->speeddials; sd; sd = sd->next) { for (sd = d->speeddials; sd; sd = sd->next) {
if (sd->instance == instance) if (sd->isHint == isHint && sd->instance == instance)
break; break;
} }
@@ -1344,10 +1350,11 @@ static int codec_ast2skinny(int astcodec)
} }
} }
static int skinny_register(struct skinny_req *req, struct skinnysession *s) static int skinny_register(struct skinny_req *req, struct skinnysession *s)
{ {
struct skinny_device *d; struct skinny_device *d;
struct skinny_line *l;
struct skinny_speeddial *sd;
struct sockaddr_in sin; struct sockaddr_in sin;
socklen_t slen; socklen_t slen;
@@ -1369,6 +1376,13 @@ static int skinny_register(struct skinny_req *req, struct skinnysession *s)
sin.sin_addr = __ourip; sin.sin_addr = __ourip;
} }
d->ourip = sin.sin_addr; d->ourip = sin.sin_addr;
for (sd = d->speeddials; sd; sd = sd->next) {
sd->stateid = ast_extension_state_add(sd->context, sd->exten, skinny_extensionstate_cb, sd);
}
for (l = d->lines; l; l = l->next) {
ast_device_state_changed("Skinny/%s@%s", l->name, d->name);
}
break; break;
} }
} }
@@ -1382,12 +1396,22 @@ static int skinny_register(struct skinny_req *req, struct skinnysession *s)
static int skinny_unregister(struct skinny_req *req, struct skinnysession *s) static int skinny_unregister(struct skinny_req *req, struct skinnysession *s)
{ {
struct skinny_device *d; struct skinny_device *d;
struct skinny_line *l;
struct skinny_speeddial *sd;
d = s->device; d = s->device;
if (d) { if (d) {
d->session = NULL; d->session = NULL;
d->registered = 0; d->registered = 0;
for (sd = d->speeddials; sd; sd = sd->next) {
if (sd->stateid > -1)
ast_extension_state_del(sd->stateid, NULL);
}
for (l = d->lines; l; l = l->next) {
ast_device_state_changed("Skinny/%s@%s", l->name, d->name);
}
} }
return -1; /* main loop will destroy the session */ return -1; /* main loop will destroy the session */
@@ -1684,6 +1708,59 @@ static void transmit_dialednumber(struct skinnysession *s, const char *text, int
transmit_response(s, req); transmit_response(s, req);
} }
static int skinny_extensionstate_cb(char *context, char *exten, int state, void *data)
{
struct skinny_speeddial *sd = data;
struct skinny_device *d = sd->parent;
struct skinnysession *s = d->session;
char hint[AST_MAX_EXTENSION];
int callstate = SKINNY_CALLREMOTEMULTILINE;
int lamp = SKINNY_LAMP_OFF;
switch (state) {
case AST_EXTENSION_DEACTIVATED: /* Retry after a while */
case AST_EXTENSION_REMOVED: /* Extension is gone */
ast_verbose(VERBOSE_PREFIX_2 "Extension state: Watcher for hint %s %s. Notify Device %s\n", exten, state == AST_EXTENSION_DEACTIVATED ? "deactivated" : "removed", d->name);
sd->stateid = -1;
callstate = SKINNY_ONHOOK;
lamp = SKINNY_LAMP_OFF;
break;
case AST_EXTENSION_RINGING:
case AST_EXTENSION_UNAVAILABLE:
callstate = SKINNY_RINGIN;
lamp = SKINNY_LAMP_BLINK;
break;
case AST_EXTENSION_BUSY: /* callstate = SKINNY_BUSY wasn't wanting to work - I'll settle for this */
case AST_EXTENSION_INUSE:
callstate = SKINNY_CALLREMOTEMULTILINE;
lamp = SKINNY_LAMP_ON;
break;
case AST_EXTENSION_ONHOLD:
callstate = SKINNY_HOLD;
lamp = SKINNY_LAMP_WINK;
break;
case AST_EXTENSION_NOT_INUSE:
default:
callstate = SKINNY_ONHOOK;
lamp = SKINNY_LAMP_OFF;
break;
}
if (ast_get_hint(hint, sizeof(hint), NULL, 0, NULL, sd->context, sd->exten)) {
/* If they are not registered, we will override notification and show no availability */
if (ast_device_state(hint) == AST_DEVICE_UNAVAILABLE) {
callstate = SKINNY_ONHOOK;
lamp = SKINNY_LAMP_FLASH;
}
}
transmit_lamp_indication(s, STIMULUS_LINE, sd->instance, lamp);
transmit_callstate(s, sd->instance, callstate, 0);
sd->laststate = state;
return 0;
}
/* /*
static int has_voicemail(struct skinny_line *l) static int has_voicemail(struct skinny_line *l)
{ {
@@ -2099,17 +2176,25 @@ static struct skinny_device *build_device(const char *cat, struct ast_variable *
if (!(sd = ast_calloc(1, sizeof(struct skinny_speeddial)))) { if (!(sd = ast_calloc(1, sizeof(struct skinny_speeddial)))) {
return NULL; return NULL;
} else { } else {
char *stringp, *exten, *label; char *stringp, *exten, *context, *label;
stringp = v->value; stringp = v->value;
exten = strsep(&stringp, ","); exten = strsep(&stringp, ",");
label = strsep(&stringp, ","); if ((context = strchr(exten, '@'))) {
*context++ = '\0';
}
label = stringp;
ast_mutex_init(&sd->lock); ast_mutex_init(&sd->lock);
ast_copy_string(sd->exten, exten, sizeof(sd->exten)); ast_copy_string(sd->exten, exten, sizeof(sd->exten));
if (label) if (!ast_strlen_zero(context)) {
ast_copy_string(sd->label, label, sizeof(sd->label)); sd->isHint = 1;
else sd->instance = lineInstance++;
ast_copy_string(sd->label, exten, sizeof(sd->label)); ast_copy_string(sd->context, context, sizeof(sd->context));
sd->instance = speeddialInstance++; } else {
sd->isHint = 0;
sd->instance = speeddialInstance++;
sd->context[0] = '\0';
}
ast_copy_string(sd->label, S_OR(label, exten), sizeof(sd->label));
sd->parent = d; sd->parent = d;
@@ -2833,7 +2918,7 @@ static int skinny_hold(struct skinny_subchannel *sub)
req->data.stopmedia.passThruPartyId = htolel(sub->callid); req->data.stopmedia.passThruPartyId = htolel(sub->callid);
transmit_response(s, req); transmit_response(s, req);
transmit_lamp_indication(s, STIMULUS_LINE, l->instance, SKINNY_LAMP_BLINK); transmit_lamp_indication(s, STIMULUS_LINE, l->instance, SKINNY_LAMP_WINK);
sub->onhold = 1; sub->onhold = 1;
return 1; return 1;
} }
@@ -3058,7 +3143,7 @@ static int handle_stimulus_message(struct skinny_req *req, struct skinnysession
ast_verbose("Received Stimulus: SpeedDial(%d)\n", instance); ast_verbose("Received Stimulus: SpeedDial(%d)\n", instance);
#if 0 #if 0
if (!(sd = find_speeddial_by_instance(d, instance))) { if (!(sd = find_speeddial_by_instance(d, instance, 0))) {
return 0; return 0;
} }
@@ -3162,7 +3247,7 @@ static int handle_stimulus_message(struct skinny_req *req, struct skinnysession
if (skinnydebug) if (skinnydebug)
ast_verbose("Received Stimulus: Line(%d)\n", instance); ast_verbose("Received Stimulus: Line(%d)\n", instance);
l = find_line_by_instance(s->device, instance); l = find_line_by_instance(d, instance);
if (!l) { if (!l) {
return 0; return 0;
@@ -3175,6 +3260,8 @@ static int handle_stimulus_message(struct skinny_req *req, struct skinnysession
l->hookstate = SKINNY_OFFHOOK; l->hookstate = SKINNY_OFFHOOK;
ast_device_state_changed("Skinny/%s@%s", l->name, d->name);
if (sub && sub->outgoing) { if (sub && sub->outgoing) {
/* We're answering a ringing call */ /* We're answering a ringing call */
ast_queue_control(sub->owner, AST_CONTROL_ANSWER); ast_queue_control(sub->owner, AST_CONTROL_ANSWER);
@@ -3216,6 +3303,8 @@ static int handle_stimulus_message(struct skinny_req *req, struct skinnysession
ast_verbose("RECEIVED UNKNOWN STIMULUS: %d(%d)\n", event, instance); ast_verbose("RECEIVED UNKNOWN STIMULUS: %d(%d)\n", event, instance);
break; break;
} }
ast_device_state_changed("Skinny/%s@%s", l->name, d->name);
return 1; return 1;
} }
@@ -3243,15 +3332,16 @@ static int handle_offhook_message(struct skinny_req *req, struct skinnysession *
l = sub->parent; l = sub->parent;
} }
transmit_ringer_mode(s, SKINNY_RING_OFF);
l->hookstate = SKINNY_OFFHOOK;
ast_device_state_changed("Skinny/%s@%s", l->name, d->name);
if (sub && sub->onhold) { if (sub && sub->onhold) {
transmit_ringer_mode(s, SKINNY_RING_OFF);
l->hookstate = SKINNY_OFFHOOK;
return 1; return 1;
} }
transmit_ringer_mode(s, SKINNY_RING_OFF);
transmit_lamp_indication(s, STIMULUS_LINE, l->instance, SKINNY_LAMP_ON); transmit_lamp_indication(s, STIMULUS_LINE, l->instance, SKINNY_LAMP_ON);
l->hookstate = SKINNY_OFFHOOK;
if (sub && sub->outgoing) { if (sub && sub->outgoing) {
/* We're answering a ringing call */ /* We're answering a ringing call */
@@ -3308,17 +3398,19 @@ static int handle_onhook_message(struct skinny_req *req, struct skinnysession *s
} }
l = sub->parent; l = sub->parent;
if (sub->onhold) {
l->hookstate = SKINNY_ONHOOK;
return 0;
}
if (l->hookstate == SKINNY_ONHOOK) { if (l->hookstate == SKINNY_ONHOOK) {
/* Something else already put us back on hook */ /* Something else already put us back on hook */
return 0; return 0;
} }
sub->cxmode = SKINNY_CX_RECVONLY;
l->hookstate = SKINNY_ONHOOK; l->hookstate = SKINNY_ONHOOK;
ast_device_state_changed("Skinny/%s@%s", l->name, d->name);
if (sub->onhold) {
return 0;
}
sub->cxmode = SKINNY_CX_RECVONLY;
transmit_callstate(s, l->instance, l->hookstate, sub->callid); transmit_callstate(s, l->instance, l->hookstate, sub->callid);
if (skinnydebug) if (skinnydebug)
ast_verbose("Skinny %s@%s went on hook\n", l->name, d->name); ast_verbose("Skinny %s@%s went on hook\n", l->name, d->name);
@@ -3393,7 +3485,7 @@ static int handle_speed_dial_stat_req_message(struct skinny_req *req, struct ski
instance = letohl(req->data.speeddialreq.speedDialNumber); instance = letohl(req->data.speeddialreq.speedDialNumber);
sd = find_speeddial_by_instance(d, instance); sd = find_speeddial_by_instance(d, instance, 0);
if (!sd) { if (!sd) {
return 0; return 0;
@@ -3414,6 +3506,7 @@ static int handle_line_state_req_message(struct skinny_req *req, struct skinnyse
{ {
struct skinny_device *d = s->device; struct skinny_device *d = s->device;
struct skinny_line *l; struct skinny_line *l;
struct skinny_speeddial *sd = NULL;
int instance; int instance;
instance = letohl(req->data.line.lineNumber); instance = letohl(req->data.line.lineNumber);
@@ -3423,6 +3516,10 @@ static int handle_line_state_req_message(struct skinny_req *req, struct skinnyse
l = find_line_by_instance(d, instance); l = find_line_by_instance(d, instance);
if (!l) { if (!l) {
sd = find_speeddial_by_instance(d, instance, 1);
}
if (!l && !sd) {
return 0; return 0;
} }
@@ -3432,10 +3529,13 @@ static int handle_line_state_req_message(struct skinny_req *req, struct skinnyse
return -1; return -1;
req->data.linestat.lineNumber = letohl(instance); req->data.linestat.lineNumber = letohl(instance);
memcpy(req->data.linestat.lineDirNumber, l->name, if (!l) {
sizeof(req->data.linestat.lineDirNumber)); memcpy(req->data.linestat.lineDirNumber, sd->label, sizeof(req->data.linestat.lineDirNumber));
memcpy(req->data.linestat.lineDisplayName, l->label, memcpy(req->data.linestat.lineDisplayName, sd->label, sizeof(req->data.linestat.lineDisplayName));
sizeof(req->data.linestat.lineDisplayName)); } else {
memcpy(req->data.linestat.lineDirNumber, l->name, sizeof(req->data.linestat.lineDirNumber));
memcpy(req->data.linestat.lineDisplayName, l->label, sizeof(req->data.linestat.lineDisplayName));
}
transmit_response(s,req); transmit_response(s,req);
return 1; return 1;
} }
@@ -3483,6 +3583,37 @@ static int handle_button_template_req_message(struct skinny_req *req, struct ski
for (i=0; i<42; i++) { for (i=0; i<42; i++) {
int btnSet = 0; int btnSet = 0;
switch (btn[i].buttonDefinition) { switch (btn[i].buttonDefinition) {
case BT_CUST_LINE:
/* assume failure */
req->data.buttontemplate.definition[i].buttonDefinition = BT_NONE;
req->data.buttontemplate.definition[i].instanceNumber = htolel(0);
for (l = d->lines; l; l = l->next) {
if (l->instance == lineInstance) {
ast_verbose("Adding button: %d, %d\n", BT_LINE, lineInstance);
req->data.buttontemplate.definition[i].buttonDefinition = BT_LINE;
req->data.buttontemplate.definition[i].instanceNumber = htolel(lineInstance);
lineInstance++;
buttonCount++;
btnSet = 1;
break;
}
}
if (!btnSet) {
for (sd = d->speeddials; sd; sd = sd->next) {
if (sd->isHint && sd->instance == lineInstance) {
ast_verbose("Adding button: %d, %d\n", BT_LINE, lineInstance);
req->data.buttontemplate.definition[i].buttonDefinition = BT_LINE;
req->data.buttontemplate.definition[i].instanceNumber = htolel(lineInstance);
lineInstance++;
buttonCount++;
btnSet = 1;
break;
}
}
}
break;
case BT_CUST_LINESPEEDDIAL: case BT_CUST_LINESPEEDDIAL:
/* assume failure */ /* assume failure */
req->data.buttontemplate.definition[i].buttonDefinition = BT_NONE; req->data.buttontemplate.definition[i].buttonDefinition = BT_NONE;
@@ -3502,7 +3633,15 @@ static int handle_button_template_req_message(struct skinny_req *req, struct ski
if (!btnSet) { if (!btnSet) {
for (sd = d->speeddials; sd; sd = sd->next) { for (sd = d->speeddials; sd; sd = sd->next) {
if (sd->instance == speeddialInstance) { if (sd->isHint && sd->instance == lineInstance) {
ast_verbose("Adding button: %d, %d\n", BT_LINE, lineInstance);
req->data.buttontemplate.definition[i].buttonDefinition = BT_LINE;
req->data.buttontemplate.definition[i].instanceNumber = htolel(lineInstance);
lineInstance++;
buttonCount++;
btnSet = 1;
break;
} else if (!sd->isHint && sd->instance == speeddialInstance) {
ast_verbose("Adding button: %d, %d\n", BT_SPEEDDIAL, speeddialInstance); ast_verbose("Adding button: %d, %d\n", BT_SPEEDDIAL, speeddialInstance);
req->data.buttontemplate.definition[i].buttonDefinition = BT_SPEEDDIAL; req->data.buttontemplate.definition[i].buttonDefinition = BT_SPEEDDIAL;
req->data.buttontemplate.definition[i].instanceNumber = htolel(speeddialInstance); req->data.buttontemplate.definition[i].instanceNumber = htolel(speeddialInstance);
@@ -3535,10 +3674,10 @@ static int handle_button_template_req_message(struct skinny_req *req, struct ski
req->data.buttontemplate.definition[i].instanceNumber = 0; req->data.buttontemplate.definition[i].instanceNumber = 0;
for (sd = d->speeddials; sd; sd = sd->next) { for (sd = d->speeddials; sd; sd = sd->next) {
if (sd->instance == speeddialInstance) { if (!sd->isHint && sd->instance == speeddialInstance) {
ast_verbose("Adding button: %d, %d\n", BT_SPEEDDIAL, speeddialInstance); ast_verbose("Adding button: %d, %d\n", BT_SPEEDDIAL, speeddialInstance);
req->data.buttontemplate.definition[i].buttonDefinition = BT_SPEEDDIAL; req->data.buttontemplate.definition[i].buttonDefinition = BT_SPEEDDIAL;
req->data.buttontemplate.definition[i].instanceNumber = htolel(speeddialInstance); req->data.buttontemplate.definition[i].instanceNumber = htolel(speeddialInstance - 1);
speeddialInstance++; speeddialInstance++;
buttonCount++; buttonCount++;
btnSet = 1; btnSet = 1;
@@ -3546,8 +3685,6 @@ static int handle_button_template_req_message(struct skinny_req *req, struct ski
} }
} }
break; break;
case BT_CUST_HINT:
break;
case BT_NONE: case BT_NONE:
break; break;
default: default:
@@ -3738,6 +3875,8 @@ static int handle_soft_key_event_message(struct skinny_req *req, struct skinnyse
return 0; return 0;
} }
ast_device_state_changed("Skinny/%s@%s", l->name, d->name);
switch(event) { switch(event) {
case SOFTKEY_NONE: case SOFTKEY_NONE:
if (skinnydebug) if (skinnydebug)
@@ -3965,6 +4104,8 @@ static int handle_soft_key_event_message(struct skinny_req *req, struct skinnyse
ast_verbose("Received unknown Softkey Event: %d(%d)\n", event, instance); ast_verbose("Received unknown Softkey Event: %d(%d)\n", event, instance);
break; break;
} }
ast_device_state_changed("Skinny/%s@%s", l->name, d->name);
return 1; return 1;
} }

View File

@@ -83,7 +83,7 @@ keepalive=120
;linelabel="John" ;linelabel="John"
;mailbox=110 ;mailbox=110
;line => 110 ;line => 110
;speeddial => 111,Jack Smith ;speeddial => 111,Jack Smith ; Adds a speeddial button to a device.
;speeddial => 112,Bob Peterson ;speeddial => 112@hints,Bob Peterson ; When a context is specified, the speeddial watches a dialplan hint.
;addon => 7914 ;addon => 7914
;addon => 7914 ;addon => 7914