Hola.
Pues voy a empezar a desnudar un poco chan_sebi para los que quieran trapichear con el fuente que sepan como está la cosa actualmente
Primero empezaremos con el arranque del módulo, que tiene la función mas “sucia” del código…
static int load_module(void)
{
if(!modem_drv_config()){
ast_log(LOG_ERROR, “No devices found. Not loading module.\n”);
return AST_MODULE_LOAD_DECLINE;
}if (!modem_load_config()) {
ast_log(LOG_ERROR, “Unable to read config file %s. Not loading module.\n”, MODEM_CONFIG);
return AST_MODULE_LOAD_DECLINE;
}ast_cli_register_multiple(modem_cli, sizeof(modem_cli) / sizeof(modem_cli[0]));
ast_register_application(app_sebistatus, modem_status_exec, sebistatus_synopsis, sebistatus_desc);
ast_register_application(app_sebisendsms, modem_sendsms_exec, sebisendsms_synopsis, sebisendsms_desc);/* Make sure we can register our channel type */
if (ast_channel_register(&modem_tech)) {
ast_log(LOG_ERROR, “Unable to register channel class %s\n”, “Sebi”);
return AST_MODULE_LOAD_FAILURE;
}return 0;
}
Primero que nada llamamos a modem_drv_config. Esta funcion busca los modems que están conectados en el sistema y escribe sebi_devices.conf. Es bastante “cochina” pero funciona. Seguidamente llamamos a modem_load_config, que lee sebi.conf y sebi_devices.conf y crea las estructuras de los modems si se cumplen las condiciones (más adelante veremos con detalle las funciones). Si tenemos dispositivos que pueden arrancarse los registramos en asterisk. Ahora entramos a detallar las funciones. Comenzamos por modem_drv_config
static int modem_drv_config(void)
{char dirname[100],
subdirname[100],
ttydirname[100],
filename[100],
conf_path[200],
value[5],
fvalue[5],
dataport[15],
imei[16],
*p=NULL;
FILE *f, *g, *h, *conf_file;
int conf_socket, res;
DIR *d, *subd, *ttyd;
struct dirent *fn;
struct dirent *subfn;
struct dirent *ttyfn;snprintf (conf_path, sizeof(conf_path), “%s/%s”, ast_config_AST_CONFIG_DIR, DEVICE_CONFIG);
conf_file = fopen(conf_path, “w”);
snprintf (dirname, sizeof(dirname), “/sys/bus/usb/devices/”);
d = opendir (dirname);
if (!d){
fclose (conf_file);
return 0;
}while ((fn = readdir (d)) != NULL){
if ((!(strstr(fn->d_name, “:”))) && (!(strstr(fn->d_name, “.”))) && (!(strstr(fn->d_name, “usb”)))){
snprintf (subdirname, sizeof(subdirname), “%s%s/”, dirname, fn->d_name);
snprintf (filename, sizeof(filename), “%s%s/idVendor”, dirname, fn->d_name);
f = fopen(filename, “r”);
if (f){
if (strstr(fgets(value, 5, f), “12d1″)){ // vendor Huawei FIXME Create ID Vendor / product table
snprintf (filename, sizeof(filename), “%s%s/idProduct”, dirname, fn->d_name);
g = fopen(filename, “r”);
if (g){
if (strstr(fgets(fvalue, 5, g), “1001″)){ // 1001 device – Search Interfaces 01 & 02
fprintf (conf_file, “[%s]\n”, fn->d_name);
ast_log(LOG_DEBUG, “device usb 12d1/1001 bus usb %s.\n”, fn->d_name);
subd = opendir (subdirname);
while ((subfn = readdir(subd)) != NULL){
if (strstr(subfn->d_name, “:”)){
snprintf (filename, sizeof(filename), “%s%s/%s/bInterfaceNumber”, dirname, fn->d_name, subfn->d_name);
h = fopen(filename, “r”);
if (h > 0){
if (strstr(fgets(value, 3, h), “01″)){
snprintf (ttydirname, sizeof(ttydirname), “%s%s/”, subdirname, subfn->d_name);
ttyd = opendir (ttydirname);
while ((ttyfn = readdir (ttyd)) != NULL){
if (strstr(ttyfn->d_name, “ttyUSB”)){
fprintf (conf_file, “voice = /dev/%s\n”, ttyfn->d_name);
ast_log(LOG_DEBUG, “device usb 12d1/1001 (voice) port %s.\n”, ttyfn->d_name);
}
}
if (ttyd) closedir (ttyd);
} else if (strstr(value, “02″)){
snprintf (ttydirname, sizeof(ttydirname), “%s%s/”, subdirname, subfn->d_name);
ttyd = opendir (ttydirname);
while ((ttyfn = readdir (ttyd)) != NULL){
if (strstr(ttyfn->d_name, “ttyUSB”)){
fprintf (conf_file, “data = /dev/%s\n”, ttyfn->d_name);
ast_log(LOG_DEBUG, “device usb 12d1/1001 (data) port %s.\n”, ttyfn->d_name);
snprintf (dataport, sizeof(dataport),”/dev/%s\n”, ttyfn->d_name);
snprintf (temp_port, sizeof(temp_port),”/dev/%s”, ttyfn->d_name);
res = modem_get_imei();
ast_log(LOG_DEBUG, “device usb 12d1/1001 (imei) %s.\n”, temp_imei);
fprintf (conf_file, “imei = %s\n”, temp_imei);
}
}
if (ttyd) closedir (ttyd);
}
}
if (h) fclose(h);
}
}
if (subd) closedir(subd);
} else if (strstr(fvalue, “1003″)){ // 1003 device – Search Interface 01
fprintf (conf_file, “[%s]\n”, fn->d_name);
ast_log(LOG_DEBUG, “device usb 12d1/1003 bus usb %s.\n”, fn->d_name);
subd = opendir (subdirname);
while ((subfn = readdir(subd)) != NULL){
if (strstr(subfn->d_name, “:”)){
snprintf (filename, sizeof(filename), “%s%s/%s/bInterfaceNumber”, dirname, fn->d_name, subfn->d_name);
h = fopen(filename, “r”);
if (h > 0){
if (strstr(fgets(value, 3, h), “01″)){
snprintf (ttydirname, sizeof(ttydirname), “%s%s/”, subdirname, subfn->d_name);
ttyd = opendir (ttydirname);
while ((ttyfn = readdir (ttyd)) != NULL){
if (strstr(ttyfn->d_name, “ttyUSB”)){
fprintf (conf_file, “data = /dev/%s\n”, ttyfn->d_name);
ast_log(LOG_DEBUG, “device usb 12d1/1003 (data) port %s.\n”, ttyfn->d_name);
snprintf (dataport, sizeof(dataport),”/dev/%s\n”, ttyfn->d_name);
snprintf (temp_port, sizeof(temp_port),”/dev/%s”, ttyfn->d_name);
res = modem_get_imei();
ast_log(LOG_DEBUG, “device usb 12d1/1003 (imei) %s.\n”, temp_imei);
fprintf (conf_file, “imei = %s\n”, temp_imei);
}
}
if (ttyd) closedir (ttyd);
}
}
if (h) fclose(h);
}
}
if (subd) closedir(subd);
}
if (g) fclose(g);
}
if (f) fclose(f);
}
}
}
}
if (d)
closedir (d);
if (conf_file) fclose(conf_file);
return 1;
}
Pues lo que hace es bien simple aunque la funcion se vea bastante enrevesada. Leemos la carpeta /sys/bus/usb/devices buscando dispositivos cuyo vendor_id sea 12d1 (huawei). Si encuentra un dispositivo cuyo ven_id es 12d1 busca que su prod_id sea 1001 o 1003 que son todos los modems de la familia. Si es un 1001 buscamos los subids 01 (puerto de voz) y 02 (datos). Ademas leemos el IMEI llamando a la funcion modem_get_imei. Anotamos el bus_id de usb y todos estos datos en sebi_devices.conf. Nos queda algo asi como:
[2-1] => El bus_id de usb
data= ttyUSB2
voice = ttyUSB1
imei = 35xxxxxxxxxxxxx
Recursivamente exploramos toda la estructura usb y cerramos los ficheros y directorios que hemos abierto. Si hemos encontrado problemas al ejecutarlo devolvemos cero y se detiene la carga del modulo. En caso contrario la carga del modulo continua.
Repasemos modem_get_imei.
static int modem_get_imei()
{
struct termios options;
int dataport, s;
char monitor = 1;
//char temp_port[15];
char buf[256];
int state = 0;
int retries = 0;
temp_buf[0] = 0×00;
snprintf (temp_imei, sizeof(temp_imei), “ERROR”);dataport= open( temp_port, O_RDWR | O_NOCTTY );
if (dataport <0)
return -1;
options.c_cflag = B460800 | CRTSCTS | CS8 | CLOCAL | CREAD | O_NDELAY;
options.c_iflag = IGNPAR;
options.c_oflag = 0;
options.c_lflag = 0;
options.c_cc[VMIN]=1;
options.c_cc[VTIME]=0;
tcflush(dataport, TCIOFLUSH);
tcsetattr(dataport,TCSANOW,&options);if (!port_write(dataport, “ATE0\r”))
monitor = 0;while (monitor) {
s = port_read(dataport, buf, 1);
if ((s > 0) && (buf[0] != 0×0) && (buf[0] != ‘\r’) && (buf[0] != ‘^’)) {
switch (state) {
case 0:
if (strstr(buf, “OK”)) {
port_write(dataport, “AT+CGSN\r”);
state++;
} else {
if (!port_write(dataport, “ATE0\r”)) monitor = 0;
}
break;case 1: // Get IMEI
if ((!(strstr(buf, “OK”))) && (!(strstr(buf, “ERROR”)))) {
snprintf (temp_imei, sizeof(temp_imei), “%s”, buf);
monitor = 0;
close (dataport);
return 1;
} else if (strstr(buf, “ERROR”)){
monitor = 0;
close (dataport);
return 0;
}
break;
}
}if (s == 0) { // Timeouts
if (retries > 2) {
close (dataport);
return 0;
}
if (state == 0){
if (!port_write(dataport, “ATE0\r”)) monitor = 0;
retries++;
} else {
if (!port_write(dataport, “AT+CGSN\r”)) monitor = 0;
retries++;
}
}
}
close (dataport);
return -1;
}
Conecta al puerto de datos del modem y lee el valor del imei con el comando AT correspondiente. No tiene secretos…
A continuacion cargamos modem_load_config.
static int modem_load_config(void)
{
struct ast_config *cfg = NULL;
struct ast_config *devices_cfg = NULL;
char *cat = NULL;
char *device_cat = NULL;
struct ast_variable *var;
const char *dataport, *voiceport, *imei, *device_imei,
*pin, *csca, *sms_enabled, *voice_enabled, *context,
*group, *volume, *nocallsetup, *language;
struct modem_pvt *pvt;
char buf[256];
uint16_t vs;
char *dr;
int res, res2, s;devices_cfg = ast_config_load(DEVICE_CONFIG);
if (!devices_cfg)
return 0;cfg = ast_config_load(MODEM_CONFIG);
if (!cfg)
return 0;device_cat = ast_category_browse(devices_cfg, NULL);
cat = ast_category_browse(cfg, NULL);
while (device_cat) {
ast_log(LOG_DEBUG, “Loading device %s.\n”, device_cat);
dataport = ast_variable_retrieve(devices_cfg, device_cat, “data”);
voiceport = ast_variable_retrieve(devices_cfg, device_cat, “voice”);
device_imei = ast_variable_retrieve(devices_cfg, device_cat, “imei”);while (cat) {
imei = ast_variable_retrieve(cfg, cat, “imei”);
if (!strstr(imei, device_imei)) {
cat = ast_category_browse(cfg, cat);
continue;
}
ast_log(LOG_DEBUG, “Imei found for usb device %s => channel %s.\n”, device_cat, cat);pin = ast_variable_retrieve(cfg, cat, “pin”);
csca = ast_variable_retrieve(cfg, cat, “csca”);
sms_enabled = ast_variable_retrieve(cfg, cat, “sms”);
voice_enabled = ast_variable_retrieve(cfg, cat, “voice”);
context = ast_variable_retrieve(cfg, cat, “context”);
group = ast_variable_retrieve(cfg, cat, “group”);
volume = ast_variable_retrieve(cfg, cat, “volume”);
language = ast_variable_retrieve(cfg, cat, “language”);
nocallsetup = ast_variable_retrieve(cfg, cat, “nocallsetup”);if (pvt = ast_calloc(1, sizeof(*pvt))) {
ast_copy_string(pvt->data_port, dataport, sizeof(pvt->data_port));
ast_copy_string(pvt->imei, imei, sizeof(pvt->imei));
if (!ast_strlen_zero(pin))
ast_copy_string(pvt->pin, pin, sizeof(pvt->pin));
if (!ast_strlen_zero(csca))
ast_copy_string(pvt->csca, csca, sizeof(pvt->csca));if (voiceport && voice_enabled && ((*voice_enabled == ‘y’) || (*voice_enabled == ‘Y’))){
pvt->type = MODEM_TYPE_VOICE;
ast_copy_string(pvt->voice_port, voiceport, sizeof(pvt->voice_port));
}else{
pvt->type = MODEM_TYPE_SMS_ONLY;
}
ast_copy_string(pvt->id, cat, sizeof(pvt->id));
ast_copy_string(pvt->context, S_OR(context, “default”), sizeof(pvt->context));
ast_copy_string(pvt->language, S_OR(language, “en”), sizeof(pvt->language));
if (group)
pvt->group = atoi(group); /* group 0 if invalid */
if (!ast_strlen_zero(volume)){
if ((atoi(volume) >0) && (atoi(volume) <6)){
pvt->volume = atoi(volume);
}
}else{
pvt->volume = 4;
}
if (!ast_strlen_zero(sms_enabled)) {
if ((*sms_enabled == ‘y’) || (*sms_enabled == ‘Y’)) {
pvt->sms_enabled = 1;
}
}
if (!ast_strlen_zero(nocallsetup)) {
if ((*nocallsetup == ‘y’) || (*nocallsetup == ‘Y’)) {
pvt->no_callsetup = 1;
}
}pvt->state = MODEM_STATE_INIT;
pvt->data_socket = -1;
pvt->voice_socket = -1;
pvt->monitor_thread = AST_PTHREADT_NULL;
pvt->dsp = ast_dsp_new();res= data_connect(pvt); //Conect modem data port
if (res < 0) {
ast_log(LOG_ERROR, “Unable to open data port %s. Not loading device %s.\n”, pvt->data_port, pvt->id);
break;
}else{
pvt->data_socket = res;
}
if (pvt->type == MODEM_TYPE_VOICE){ //Try voice port
res2 = voice_connect(pvt);
if (res2 < 0){
ast_log(LOG_ERROR, “Unable to open voice port %s. Not loading device %s.\n”, pvt->voice_port, pvt->id);
break;
}else{
pvt->voice_socket = res2;
}
}ast_dsp_set_features(pvt->dsp, DSP_FEATURE_DTMF_DETECT);
ast_dsp_digitmode(pvt->dsp, DSP_DIGITMODE_DTMF | DSP_DIGITMODE_RELAXDTMF);
AST_RWLIST_WRLOCK(&devices);
AST_RWLIST_INSERT_HEAD(&devices, pvt, entry);
AST_RWLIST_UNLOCK(&devices);if (start_monitor(pvt)) {
//pvt->connected = 1;
manager_event(EVENT_FLAG_SYSTEM, “ModemStatus”, “Status: Connect\r\nDevice: %s\r\n”, pvt->id);
if (option_verbose > 2)
ast_verbose(VERBOSE_PREFIX_3 “Modem Device %s has connected.\n”, pvt->id);
}
}
cat = ast_category_browse(cfg, cat);
}
cat = ast_category_browse(cfg, NULL);
device_cat = ast_category_browse(devices_cfg, device_cat);
}
ast_config_destroy(cfg);
ast_config_destroy(devices_cfg);
return 1;
}
Leemos el fichero de configuracion sebi.conf. Vamos analizando cada uno de los modems configurados y comparando el imei de sebi.conf con el de sebi_devices.conf. Si coinciden continuamos el proceso de inicializacion del modem, recogiendo los puertos de sebi_devices.conf y el resto de configuración de sebi.conf. Haciendo esto se garantiza que siempre aplicamos los valores (pin, csca, etc) al modem que hemos elegido, y no a otro. Si no lo hicieramos así podríamos bloquear una tarjeta SIM intentando aplicarle un pin incorrecto por ejemplo. Despues de eso intentamos abrir los puertos de voz y comunicaciones (si no tiene el usuario que corre asterisk los permisos correctos no podrá abrir los puertos y el dispositivo no se cargará. Despues de comprobar que todo está ok arrancamos el thread de comunicaciones “start_monitor(pvt)”. Este thread es el que se encarga de leer y escribir los eventos del modem.
Y pasamos al monitor, que es el corazon del modulo en tiempo de ejecucion.
static void *do_monitor_phone(void *data)
{struct modem_pvt *pvt = (struct modem_pvt *)data;
struct ast_channel *chn;
char monitor = 1;
char buf[256];
char cid_num[AST_MAX_EXTENSION], *pcids, *pcide;
int s, t, i, j, smsi;
int send_pin;
char brsf, nsmode, *p, *p1;
char sms_src[13];
char sms_txt[160];
char sms_tmp_txt[160];
send_pin = 0;if (!data_write(pvt, “ATE0\r”)) //First of all test write & disable echo…
monitor = 0;while (monitor) {
if (pvt->state == MODEM_STATE_DIAL1)
t = pvt->dial_timeout;
else if (pvt->state == MODEM_STATE_HANGUP)
t = 2;
else if (pvt->state == MODEM_STATE_OUTSMS1)
t = 2;
else if (pvt->state == MODEM_STATE_OUTSMS2)
t = 15;
else
t = 1;s = data_read(pvt, buf, 0, t);
if ((s > 0) && (buf[0] != 0×0) && (buf[0] != ‘\r’) && (!strstr(buf, “^BOOT:”))) {
ast_log(LOG_DEBUG, “data_read() (%s) [%s]\n”, pvt->id, buf);
switch (pvt->state) {
case MODEM_STATE_INIT: // Echoing off state
if (strstr(buf, “OK”)) {
data_write(pvt, “AT+CGSN\r”);
pvt->state++;;
} else { // Hmmm, garbage…. Let’s try again ATE0
if (!data_write(pvt, “ATE0\r”)) monitor = 0;
}
break;case MODEM_STATE_INIT1: // IMEI Checking – Is this the right modem ??
if (!(strstr(buf, “OK”))) {
if (!strstr(buf, pvt->imei)){
ast_log(LOG_ERROR, “IMEI in conf file for %s (%s) is different from device (%s). Disconnecting device.\n”, pvt->id, pvt->imei, buf);
ast_copy_string(pvt->imei, “*–*INVALID*–*”, sizeof(pvt->imei));
monitor = 0;
}else{
data_write(pvt, “AT+CPIN?\r”);
pvt->state++;
}
}
break;case MODEM_STATE_INIT2: // Pin Checking
if (strstr(buf, “+CPIN: READY”)) { // OK
pvt->state++;
data_write(pvt, “AT^CARDLOCK?\r”);
}else if (strstr(buf, “+CPIN: SIM PUK”)){ // SIM Locked
ast_log(LOG_ERROR, “Device %s – PIN Locked. Disconnecting device. Unlock it & retry.\n”, pvt->id);
ast_copy_string(pvt->imei, “** PIN Locked**”, sizeof(pvt->imei));
monitor = 0;
} else if ((pvt->pin) && (strstr(buf,”+CPIN: SIM PIN”))){
if (send_pin == 0){ //Send SIM
snprintf(buf, sizeof(buf), “AT+CPIN=%s\r”, pvt->pin);
data_write(pvt, buf);
send_pin =1;
sleep(1);
data_write(pvt, “AT+CPIN?\r”);
}else{ // SIM Error
ast_log(LOG_ERROR, “Device %s – Wrong PIN. Disconnecting device.\n”, pvt->id);
ast_copy_string(pvt->imei, “** PIN Error **”, sizeof(pvt->imei));
monitor = 0;
}
}
break;case MODEM_STATE_INIT3: // NetLock Checking
if (strstr(buf, “^CARDLOCK: 2″)) {
data_write(pvt, “AT+CLIP=1\r”);
pvt->state++;
}else if (strstr(buf, “^CARDLOCK: 1″)){
ast_log(LOG_ERROR, “Device %s – Net Locked. Disconnecting device.\n”, pvt->id);
ast_copy_string(pvt->imei, “** Net Lock **”, sizeof(pvt->imei));
monitor = 0;
}
break;
case MODEM_STATE_INIT4:
if (strstr(buf, “OK”)) {
data_write(pvt, “AT+CREG?\r”); // Network registered??
pvt->state++;
}
break;
case MODEM_STATE_INIT5:
if (strstr(buf, “+CREG: 0,1″)) { // Network OK
data_write(pvt, “AT+COPS=3,0\r”);
pvt->state++;
}else if (strstr(buf, “+CREG: 0,2″)) { // Not network – request again …
pvt->state–;
sleep(5); // wait a little, please…
data_write(pvt, “ATE0\r”); //we need OK in previous state
}
break;
case MODEM_STATE_INIT6:
if (strstr(buf, “OK”)) {
data_write(pvt, “AT+COPS?\r”);
pvt->state++;
}
break;
case MODEM_STATE_INIT7:
if (strstr(buf, “+COPS: “)) { // Get Network Provider friendly name
memset(pvt->net_name, 0×00, sizeof(pvt->net_name));
if ((p = strchr(buf, ‘”‘))) {
if (*(++p) != ‘”‘)
p++;
if ((p1 = strchr(p, ‘”‘))) {
if (*(–p1) != ‘”‘)
p1–;
memset(pvt->net_name, 0×00, sizeof(pvt->net_name));
strncpy(pvt->net_name, p – 1, p1 – p + 3);
}
} //Modem is online – Now starting Voice-SMS init phase, see timeouts
if (pvt->type == MODEM_TYPE_VOICE){
pvt->state++;
}else{
pvt->state = MODEM_STATE_INIT_SMS;
}
}
break;
case MODEM_STATE_INIT_VOICE:
snprintf(buf, sizeof(buf), “AT+CLVL=%d\r”, pvt->volume); // Adjust Volume
data_write(pvt, buf);
pvt->state++;
break;
case MODEM_STATE_INIT_VOICE1:
if (strstr(buf, “OK”)){
data_write(pvt, “AT^DDSETEX=2\r”); // Pre-Voice Command
pvt->state++;
}
break;
case MODEM_STATE_INIT_VOICE2:
if (strstr(buf, “OK”)){
data_write(pvt, “AT^CVOICE=?\r”); // Check Voice support
pvt->state++;
}else if (strstr(buf, “COMMAND NOT SUPPORT”)){
pvt->has_voice = 0;
pvt->state = MODEM_STATE_INIT_SMS;
}
break;
case MODEM_STATE_INIT_VOICE3:
if (strstr(buf, “^CVOICE:”)){
pvt->has_voice = 1;
pvt->state++;
}else if (strstr(buf, “COMMAND NOT SUPPORT”)){
pvt->has_voice = 0;
pvt->state++;
}
break;
case MODEM_STATE_INIT_SMS:
if (pvt->sms_enabled){
data_write(pvt,”AT+CMGF=1;+CPMS=\”ME\”,\”ME\”,\”ME\”;+CNMI=1,1,0,1,0\r”); // Enable SMS Info
pvt->state++;
}else{
pvt->has_sms = 0;
pvt->state = MODEM_STATE_PREIDLE;
}
break;
case MODEM_STATE_INIT_SMS1:
if (strstr(buf, “OK”)){
snprintf(buf, sizeof(buf), “AT+CSCA=\”%s\”,145\r”, pvt->csca);
data_write(pvt, buf);
pvt->state++;
}else if ((strstr(buf, “ERROR”)) || (strstr(buf, “COMMAND NOT SUPPORT”))){
pvt->has_sms = 0;
pvt->state = MODEM_STATE_PREIDLE;
}
break;
case MODEM_STATE_INIT_SMS2:
if (strstr(buf, “OK”)){
pvt->has_sms = 1;
pvt->state++;
}else if ((strstr(buf, “ERROR”)) || (strstr(buf, “COMMAND NOT SUPPORT”))){
pvt->has_sms = 0;
pvt->state++;
}
break;
case MODEM_STATE_PREIDLE: // Nothing handled here, wait for timeout, then off we go… //
break;
case MODEM_STATE_IDLE:
if ((strstr(buf, “RING”)) && (pvt->has_voice)) {
pvt->state = MODEM_STATE_RING;
}
break;case MODEM_STATE_DIAL: // Nothing handled here, we need to wait for a timeout
break;
case MODEM_STATE_DIAL1:
if (strstr(buf, “OK”)) {
if (pvt->no_callsetup) {
ast_queue_control(pvt->owner, AST_CONTROL_ANSWER);
} else {
ast_setstate(pvt->owner, AST_STATE_RINGING);
}
pvt->state = MODEM_STATE_OUTGOING;
}
break;
case MODEM_STATE_OUTGOING:
if (strstr(buf, “^CEND”)) ast_queue_control(pvt->owner, AST_CONTROL_HANGUP);
if (strstr(buf, “^CONN”)) ast_queue_control(pvt->owner, AST_CONTROL_ANSWER);
if (strstr(buf, “^CONF”)) ast_queue_control(pvt->owner, AST_CONTROL_RINGING);break;
case MODEM_STATE_RING:
cid_num[0] = 0×00;
if ((pcids = strstr(buf, “+CLIP:”))) {
if ((pcids = strchr(pcids, ‘”‘))) {
if ((pcide = strchr(pcids+1, ‘”‘))) {
strncpy(cid_num, pcids+1, pcide – pcids – 1);
cid_num[pcide - pcids - 1] = 0×00;
}
}
chn = modem_new(AST_STATE_RING, pvt, cid_num);
if (chn) {
if (ast_pbx_start(chn)) {
ast_log(LOG_ERROR, “Unable to start pbx on incoming call.\n”);
ast_hangup(chn);
} else
pvt->state = MODEM_STATE_RING3;
} else {
ast_log(LOG_ERROR, “Unable to allocate channel for incoming call.\n”);
data_write(pvt, “AT+CHUP\r”);
pvt->state = MODEM_STATE_IDLE;
}
}
break;
case MODEM_STATE_RING2:
chn = modem_new(AST_STATE_RING, pvt, cid_num);
if (chn) {
if (ast_pbx_start(chn)) {
ast_log(LOG_ERROR, “Unable to start pbx on incoming call.\n”);
ast_hangup(chn);
} else
pvt->state = MODEM_STATE_RING3;
} else {
ast_log(LOG_ERROR, “Unable to allocate channel for incoming call.\n”);
data_write(pvt, “AT+CHUP\r”);
pvt->state = MODEM_STATE_IDLE;
}
break;
case MODEM_STATE_RING3:
if (strstr(buf, “^CEND”)) { // Caller disconnected
ast_queue_control(pvt->owner, AST_CONTROL_HANGUP);
}
break;
case MODEM_STATE_INCOMING:
if (strstr(buf, “^CEND”)) {
pvt->do_hangup = 0;
ast_queue_control(pvt->owner, AST_CONTROL_HANGUP);
}
break;
case MODEM_STATE_HANGUP:
if (strstr(buf, “OK”)) {
if (pvt->voice_socket > -1){
close(pvt->voice_socket);
pvt->voice_socket = -1;
}
pvt->state = MODEM_STATE_IDLE;
}
break;
case MODEM_STATE_INSMS:
if (strstr(buf, “+CMGR:”)) {
ast_copy_string(sms_txt, “”, sizeof(sms_txt));
memset(sms_src, 0×00, sizeof(sms_src));
if ((p = strchr(buf, ‘,’))) {
if (*(++p) == ‘”‘)
p++;
if ((p1 = strchr(p, ‘,’))) {
if (*(–p1) == ‘”‘)
p1–;
memset(sms_src, 0×00, sizeof(sms_src));
strncpy(sms_src, p, p1 – p + 1);
}
}
} else if (strstr(buf, “OK”)) {
chn = modem_new(AST_STATE_DOWN, pvt, NULL);
strcpy(chn->exten, “sms”);
pbx_builtin_setvar_helper(chn, “SMSSRC”, sms_src);
pbx_builtin_setvar_helper(chn, “SMSTXT”, sms_txt);
ast_log(LOG_DEBUG, “sms received(%s) From(%s) Text[%s]\n”, pvt->id, sms_src, sms_txt);
if (ast_pbx_start(chn))
ast_log(LOG_ERROR, “Unable to start pbx on incoming sms.\n”);
snprintf(buf, sizeof(buf), “AT+CMGD=%d\r”, smsi);
data_write(pvt, buf); // delete sms
pvt->state = MODEM_STATE_IDLE;
} else {
ast_log(LOG_DEBUG, “sms line received(%s) From(%s) Text[%s] Buf [%s]\n”, pvt->id, sms_src, sms_txt, buf);
snprintf(sms_tmp_txt,sizeof(sms_tmp_txt),sms_txt);
snprintf(sms_txt, sizeof(sms_txt), “%s%s\n”, sms_tmp_txt, buf);
}
break;
case MODEM_STATE_OUTSMS:
break;
case MODEM_STATE_OUTSMS1:
break;
case MODEM_STATE_OUTSMS2:
if (strstr(buf, “OK”)) {
pvt->state = MODEM_STATE_IDLE;
}
break;
}
// Unsolicited responsesif (strstr(buf, “+CMTI:”)) { // SMS Incoming…
if ((p = strchr(buf, ‘,’))) {
p++;
smsi = atoi(p);
if (pvt->has_sms) {
snprintf(buf, sizeof(buf), “AT+CMGR=%d\r”, smsi);
data_write(pvt, buf);
pvt->state = MODEM_STATE_INSMS;
}
}
}} else if (s == 0) { // Timeouts if (!data_write(pvt, “ATE0\r”)) monitor = 0;
if (pvt->state == MODEM_STATE_INIT){
if (!data_write(pvt, “ATE0\r”)) monitor = 0;
}else if (pvt->state == MODEM_STATE_PREIDLE) {
pvt->connected = 1;
ast_verbose(VERBOSE_PREFIX_3 “Modem Device %s initialised and ready.\n”, pvt->id);
pvt->state = MODEM_STATE_IDLE;
} else if (pvt->state == MODEM_STATE_DIAL) {
if (pvt->has_voice){
snprintf(buf, sizeof(buf), “ATD%s;\r”, pvt->dial_number);
if (!data_write(pvt, buf)) {
ast_log(LOG_ERROR, “Dial failed on %s state %d\n”, pvt->owner->name, pvt->state);
ast_queue_control(pvt->owner, AST_CONTROL_CONGESTION);
pvt->state = MODEM_STATE_IDLE;
} else {
pvt->state = MODEM_STATE_DIAL1;
}
}else {
ast_log(LOG_ERROR, “Dial failed on %s state %d\n”, pvt->owner->name, pvt->state);
ast_queue_control(pvt->owner, AST_CONTROL_CONGESTION);
pvt->state = MODEM_STATE_IDLE;
}
} else if (pvt->state == MODEM_STATE_DIAL1) {
ast_log(LOG_ERROR, “Dial failed on %s state %d\n”, pvt->owner->name, pvt->state);
ast_queue_control(pvt->owner, AST_CONTROL_CONGESTION);
ast_queue_control(pvt->owner, AST_CONTROL_HANGUP);
pvt->state = MODEM_STATE_IDLE;
} else if (pvt->state == MODEM_STATE_RING) { // No CLIP?, bump it
pvt->state = MODEM_STATE_RING2;
} else if (pvt->state == MODEM_STATE_HANGUP) {
if (pvt->do_hangup) {
if (pvt->hangup_count == 6) {
ast_log(LOG_DEBUG, “Device %s failed to hangup after 6 tries, disconnecting.\n”, pvt->id);
monitor = 0;
}
data_write(pvt, “AT+CHUP\r”);
data_write(pvt, “AT^DDSETEX=2\r”);
pvt->hangup_count++;
} else
pvt->state = MODEM_STATE_IDLE;
} else if (pvt->state == MODEM_STATE_OUTSMS) {
snprintf(buf, sizeof(buf), “AT+CMGS=\”%s\”\r”, pvt->dial_number);
data_write(pvt, buf);
pvt->state = MODEM_STATE_OUTSMS1;
} else if (pvt->state == MODEM_STATE_OUTSMS1) {
if (pvt->data_buf[0] == ‘>’) {
snprintf(buf, sizeof(buf), “%s%c”, pvt->sms_txt, 0×1a);
data_write(pvt, buf);
pvt->state = MODEM_STATE_OUTSMS2;
} else {
ast_log(LOG_ERROR, “Failed to send SMS to %s on device %s\n”, pvt->dial_number, pvt->id);
pvt->state = MODEM_STATE_IDLE;
}
}
} else if (s == -1) {
if (option_verbose > 2)
ast_verbose(VERBOSE_PREFIX_3 “Modem Device %s has disconnected, reason (%d).\n”, pvt->id, errno);
monitor = 0;
}}
if (pvt->data_socket > -1)
close(pvt->data_socket);
if (pvt->voice_socket > -1)
close(pvt->voice_socket);
pvt->voice_socket = -1;
pvt->data_socket = -1;
pvt->connected = 0;
pvt->monitor_thread = AST_PTHREADT_NULL;manager_event(EVENT_FLAG_SYSTEM, “SebiStatus”, “Status: Disconnect\r\nDevice: %s\r\n”, pvt->id);
return NULL;
}
Vayamos por partes aqui, ya que la funcion es bastante extensa.
Case_modem_init_xxxx. Todas estas rutinas van leyendo y escribiendo datos al modem para ponerlo en disposicion de trabajar y comprobar que la configuración es correcta y coherente. Una simple lectura detallada aclara bastante que es lo que se va haciendo al inicializarlo.
Despues de haberlo inicializado tenemos otra seccion donde nos dedicamos a “escuchar al modem” (llamada entrante o sms entrante al recibir un ring o un cmti respectivamente del modem) y una tercera zona donde gestionamos las llamadas y el envio/recepcion de sms
Intentaré seguir con la gestión del audio cuando pueda. Un saludo desde Qosqo (Cusco)





