recv & refine & add modem manager py
This commit is contained in:
parent
6cf52b0e41
commit
4c2e8f14e0
8 changed files with 713 additions and 132 deletions
21
Makefile
21
Makefile
|
@ -1,26 +1,33 @@
|
|||
PJSIP_DIR=pjproject-2.11.1
|
||||
PJSIP_DIR=pjproject
|
||||
PKG_CONFIG_PATH=pjsip.install/lib/pkgconfig
|
||||
|
||||
all: d-modem slmodemd
|
||||
ifndef NO_PULSE
|
||||
DM_LDFLAGS += -l pulse -l pulse-simple
|
||||
DM_CFLAGS += -DHAS_PULSE
|
||||
endif
|
||||
|
||||
all: d-modem slmodemd $(if $(NO_PULSE),,d-modem.nopulse)
|
||||
|
||||
$(PKG_CONFIG_PATH)/libpjproject.pc:
|
||||
(cd $(PJSIP_DIR); [ -f ./config.status ] || ./configure --prefix=`pwd`/../pjsip.install --disable-video)
|
||||
(cd $(PJSIP_DIR); [ -f ./config.status ] || ./configure --prefix=`pwd`/../pjsip.install --disable-video --disable-sound --enable-ext-sound --disable-speex-aec --enable-g711-codec --disable-l16-codec --disable-gsm-codec --disable-g722-codec --disable-g7221-codec --disable-speex-codec --disable-ilbc-codec --disable-sdl --disable-ffmpeg --disable-v4l2 --disable-openh264 --disable-vpx --disable-android-mediacodec --disable-darwin-ssl --disable-ssl --disable-opencore-amr --disable-silk --disable-opus --disable-bcg729 --disable-libyuv --disable-libwebrtc)
|
||||
$(MAKE) -C $(PJSIP_DIR) && \
|
||||
$(MAKE) -C $(PJSIP_DIR) install
|
||||
|
||||
d-modem: d-modem.c $(PKG_CONFIG_PATH)/libpjproject.pc
|
||||
$(CC) -o $@ $< `PKG_CONFIG_PATH="$(PKG_CONFIG_PATH)" pkg-config --static --cflags --libs libpjproject`
|
||||
d-modem d-modem.nopulse : d-modem.c $(PKG_CONFIG_PATH)/libpjproject.pc
|
||||
$(CC) $(if $(findstring nopulse,$@),,$(DM_CFLAGS) $(DM_LDFLAGS)) -o $@ $< \
|
||||
`PKG_CONFIG_PATH="$(PKG_CONFIG_PATH)" pkg-config --static --cflags --libs libpjproject` \
|
||||
-O2 -s
|
||||
|
||||
slmodemd:
|
||||
$(MAKE) -C slmodemd
|
||||
|
||||
clean:
|
||||
rm -f d-modem.o d-modem
|
||||
rm -f d-modem.o d-modem d-modem.nopulse
|
||||
$(MAKE) -C slmodemd clean
|
||||
|
||||
realclean: clean
|
||||
$(MAKE) -C $(PJSIP_DIR) realclean
|
||||
rm -rf pjsip.install/*
|
||||
|
||||
|
||||
|
||||
.PHONY: all clean realclean slmodemd
|
||||
|
|
243
d-modem.c
243
d-modem.c
|
@ -16,6 +16,7 @@
|
|||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <stdbool.h>
|
||||
#include <time.h>
|
||||
|
@ -24,14 +25,38 @@
|
|||
#include <errno.h>
|
||||
// test
|
||||
#include <pjsua-lib/pjsua.h>
|
||||
#include <pjsua-lib/pjsua_internal.h>
|
||||
|
||||
#ifdef HAS_PULSE
|
||||
#include <pulse/simple.h>
|
||||
#endif
|
||||
|
||||
#define PCM_FILE "dmodem.%s.s16le.9600hz.pcm"
|
||||
#undef PCM_FILE
|
||||
//#define HAS_PULSE
|
||||
|
||||
#ifdef HAS_PULSE
|
||||
pa_simple *pa_s1 = NULL;
|
||||
pa_simple *pa_s2 = NULL;
|
||||
const pa_sample_spec pa_ss = {
|
||||
.format = PA_SAMPLE_S16LE,
|
||||
.rate = 9600,
|
||||
.channels = 1
|
||||
};
|
||||
#endif
|
||||
#ifdef PCM_FILE
|
||||
int wave_recv = -1;
|
||||
int wave_transmit = -1;
|
||||
#endif
|
||||
|
||||
#define SIGNATURE PJMEDIA_SIG_CLASS_PORT_AUD('D','M')
|
||||
#define DMODEM_DIAL_MODE 0
|
||||
#define DMODEM_ANSWER_MODE 2
|
||||
#define DMODEM_RING_DETECT_MODE 3
|
||||
#define DMODEM_ANSWER_MODE 1
|
||||
|
||||
uint8_t mode = DMODEM_DIAL_MODE;
|
||||
uint8_t ringing = 0;
|
||||
uint8_t answered = 0;
|
||||
int ppid;
|
||||
pjsua_call_id incoming;
|
||||
|
||||
struct dmodem {
|
||||
|
@ -44,20 +69,85 @@ static struct dmodem port;
|
|||
static bool destroying = false;
|
||||
static pj_pool_t *pool;
|
||||
|
||||
void stop_pa();
|
||||
static void error_exit(const char *title, pj_status_t status) {
|
||||
pjsua_perror(__FILE__, title, status);
|
||||
if (!destroying) {
|
||||
destroying = true;
|
||||
pjsua_destroy();
|
||||
stop_pa();
|
||||
if (answered)
|
||||
kill(ppid, SIGINT);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void start_pa() {
|
||||
#ifdef PCM_FILE
|
||||
char _wavbuf[50];
|
||||
sprintf(_wavbuf, PCM_FILE, "recv");
|
||||
wave_recv = open(_wavbuf, O_WRONLY | O_CREAT, 00644);
|
||||
sprintf(_wavbuf, PCM_FILE, "transmit");
|
||||
wave_recv = open(_wavbuf, O_WRONLY | O_CREAT, 00644);
|
||||
if (wave_recv <= 0 || wave_transmit <= 0)
|
||||
error_exit("open wave files", 1);
|
||||
#endif
|
||||
#ifdef HAS_PULSE
|
||||
pa_s1 = pa_simple_new(NULL, // Use the default server.
|
||||
"dmodem", // Our application's name.
|
||||
PA_STREAM_PLAYBACK,
|
||||
NULL, // Use the default device.
|
||||
"recv", // Description of our stream.
|
||||
&pa_ss, // Our sample format.
|
||||
NULL, // Use default channel map
|
||||
NULL, // Use default buffering attributes.
|
||||
NULL // Ignore error code.
|
||||
);
|
||||
pa_s2 = pa_simple_new(NULL, // Use the default server.
|
||||
"dmodem", // Our application's name.
|
||||
PA_STREAM_PLAYBACK,
|
||||
NULL, // Use the default device.
|
||||
"transmit", // Description of our stream.
|
||||
&pa_ss, // Our sample format.
|
||||
NULL, // Use default channel map
|
||||
NULL, // Use default buffering attributes.
|
||||
NULL // Ignore error code.
|
||||
);
|
||||
if (!pa_s1 || !pa_s2)
|
||||
error_exit("pulseaudio", 1);
|
||||
#endif
|
||||
}
|
||||
|
||||
void stop_pa() {
|
||||
#ifdef HAS_PULSE
|
||||
if(pa_s1)
|
||||
pa_simple_free(pa_s1);
|
||||
if(pa_s2)
|
||||
pa_simple_free(pa_s2);
|
||||
pa_s1 = 0;
|
||||
pa_s1 = 0;
|
||||
#endif
|
||||
#ifdef PCM_FILE
|
||||
if (wave_recv >= 0)
|
||||
close(wave_recv);
|
||||
if (wave_transmit >= 0)
|
||||
close(wave_transmit);
|
||||
wave_recv = -1;
|
||||
wave_transmit = -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
static pj_status_t dmodem_put_frame(pjmedia_port *this_port, pjmedia_frame *frame) {
|
||||
struct dmodem *sm = (struct dmodem *)this_port;
|
||||
int len;
|
||||
|
||||
if (frame->type == PJMEDIA_FRAME_TYPE_AUDIO) {
|
||||
#ifdef HAS_PULSE
|
||||
pa_simple_write(pa_s1, frame->buf, frame->size, NULL);
|
||||
#endif
|
||||
#ifdef PCM_FILE
|
||||
write(wave_recv, frame->buf, frame->size);
|
||||
#endif
|
||||
if ((len=write(sm->sock, frame->buf, frame->size)) != frame->size) {
|
||||
error_exit("error writing frame",0);
|
||||
}
|
||||
|
@ -78,12 +168,20 @@ static pj_status_t dmodem_get_frame(pjmedia_port *this_port, pjmedia_frame *fram
|
|||
frame->timestamp.u64 = sm->timestamp.u64;
|
||||
frame->type = PJMEDIA_FRAME_TYPE_AUDIO;
|
||||
sm->timestamp.u64 += PJMEDIA_PIA_SPF(&this_port->info);
|
||||
#ifdef HAS_PULSE
|
||||
pa_simple_write(pa_s2, frame->buf, frame->size, NULL);
|
||||
#endif
|
||||
#ifdef PCM_FILE
|
||||
write(wave_transmit, frame->buf, frame->size);
|
||||
#endif
|
||||
|
||||
return PJ_SUCCESS;
|
||||
}
|
||||
|
||||
static pj_status_t dmodem_on_destroy(pjmedia_port *this_port) {
|
||||
printf("destroy\n");
|
||||
if (answered)
|
||||
kill(ppid, SIGINT);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
|
@ -99,15 +197,15 @@ static void on_call_state(pjsua_call_id call_id, pjsip_event *e) {
|
|||
ci.state_text.ptr));
|
||||
|
||||
if (ci.state == PJSIP_INV_STATE_DISCONNECTED) {
|
||||
if(mode != DMODEM_RING_DETECT_MODE) {
|
||||
close(port.sock);
|
||||
if (!destroying) {
|
||||
destroying = true;
|
||||
pjsua_destroy();
|
||||
exit(0);
|
||||
}
|
||||
close(port.sock);
|
||||
if (!destroying) {
|
||||
destroying = true;
|
||||
pjsua_destroy();
|
||||
stop_pa();
|
||||
if (answered)
|
||||
kill(ppid, SIGINT);
|
||||
exit(0);
|
||||
}
|
||||
ringing = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -117,7 +215,12 @@ static void on_incoming_call(pjsua_acc_id acc_id, pjsua_call_id call_id, pjsip_r
|
|||
char* tmp = malloc(pj_strlen(&ci.remote_contact)+1);
|
||||
strcpy(tmp, ci.remote_contact.ptr);
|
||||
tmp[pj_strlen(&ci.remote_contact)] = '\0';
|
||||
printf("Incoming call from: %s\n", tmp);
|
||||
if (answered) {
|
||||
PJ_LOG(2,(__FILE__, "Incoming call rejected from: %s", tmp));
|
||||
pjsua_call_hangup(call_id, 0u, NULL, NULL);
|
||||
return;
|
||||
}
|
||||
PJ_LOG(2,(__FILE__, "Incoming call from: %s", tmp));
|
||||
free(tmp);
|
||||
incoming = call_id;
|
||||
ringing = 1;
|
||||
|
@ -144,28 +247,54 @@ static void on_call_media_state(pjsua_call_id call_id) {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
pjsua_acc_id acc_id;
|
||||
pj_status_t status;
|
||||
if (argc != 3) {
|
||||
if (argc != 5) {
|
||||
return -1;
|
||||
}
|
||||
ringing = 0;
|
||||
if(!strncmp(argv[1], "++", 2)) {
|
||||
ppid = atoi(argv[3]);
|
||||
char* _modemid_tmp = argv[4];
|
||||
for (size_t i = strlen(_modemid_tmp) - 1 ; ; i--) {
|
||||
if (i < 0 || _modemid_tmp[i] < '0' || _modemid_tmp[i] > '9') {
|
||||
_modemid_tmp += i+1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
int sip_port = atoi(_modemid_tmp);
|
||||
sip_port = sip_port >= 0 ? sip_port : 0;
|
||||
sip_port += 5060;
|
||||
sip_port = sip_port <= 65535 ? sip_port : 65535;
|
||||
if(!strncmp(argv[1], "rr", 2)) {
|
||||
mode = DMODEM_ANSWER_MODE;
|
||||
} else if(!strncmp(argv[1], "rr", 2)) {
|
||||
mode = DMODEM_RING_DETECT_MODE;
|
||||
}
|
||||
signal(SIGPIPE,SIG_IGN);
|
||||
char *dialstr = argv[1];
|
||||
|
||||
char *sip_user = "dialupuser";
|
||||
char *sip_domain = "192.168.1.2";
|
||||
char *sip_pass = "pppasswdModem1";
|
||||
printf("sip data: user: %s, passwd: %s, server: %s\nMODE: %d\n", sip_user, sip_pass, sip_domain, mode);
|
||||
int has_sip_user = 1;
|
||||
const char *_sip_user = getenv("SIP_LOGIN");
|
||||
char *sip_user = NULL;
|
||||
if (!_sip_user) {
|
||||
has_sip_user = 0;
|
||||
_sip_user = "placeholder:placeholder@placeholder";
|
||||
}
|
||||
sip_user = malloc(strlen(_sip_user) + 1);
|
||||
strcpy(sip_user, _sip_user);
|
||||
char *sip_domain = strchr(sip_user,'@');
|
||||
if (!sip_domain) {
|
||||
return -1;
|
||||
}
|
||||
*sip_domain++ = '\0';
|
||||
char *sip_pass = strchr(sip_user,':');
|
||||
if (!sip_pass) {
|
||||
return -1;
|
||||
}
|
||||
*sip_pass++ = '\0';
|
||||
|
||||
status = pjsua_create();
|
||||
if (status != PJ_SUCCESS) error_exit("Error in pjsua_create()", status);
|
||||
if (!has_sip_user)
|
||||
PJ_LOG(2,(__FILE__, "SIP_LOGIN is empty, no registration will be attempted"));
|
||||
|
||||
/* Init pjsua */
|
||||
{
|
||||
|
@ -177,19 +306,23 @@ int main(int argc, char *argv[]) {
|
|||
cfg.cb.on_call_media_state = &on_call_media_state;
|
||||
cfg.cb.on_call_state = &on_call_state;
|
||||
|
||||
if(mode == DMODEM_RING_DETECT_MODE) {
|
||||
if(mode == DMODEM_ANSWER_MODE) {
|
||||
cfg.cb.on_incoming_call = &on_incoming_call;
|
||||
}
|
||||
|
||||
pjsua_logging_config_default(&log_cfg);
|
||||
log_cfg.console_level = 4;
|
||||
// log_cfg.console_level = 4;
|
||||
log_cfg.console_level = 2;
|
||||
|
||||
pjsua_media_config_default(&med_cfg);
|
||||
med_cfg.no_vad = true;
|
||||
med_cfg.ec_tail_len = 0;
|
||||
med_cfg.jb_max = 2000;
|
||||
// med_cfg.jb_init = 200;
|
||||
med_cfg.audio_frame_ptime = 5;
|
||||
med_cfg.jb_max = -1;
|
||||
med_cfg.jb_init = -1;
|
||||
med_cfg.audio_frame_ptime = 10;
|
||||
med_cfg.quality = 10;
|
||||
med_cfg.enable_ice = PJ_FALSE;
|
||||
med_cfg.enable_turn = PJ_FALSE;
|
||||
|
||||
status = pjsua_init(&cfg, &log_cfg, &med_cfg);
|
||||
if (status != PJ_SUCCESS) error_exit("Error in pjsua_init()", status);
|
||||
|
@ -197,33 +330,38 @@ int main(int argc, char *argv[]) {
|
|||
|
||||
pjsua_set_ec(0,0); // maybe?
|
||||
pjsua_set_null_snd_dev();
|
||||
|
||||
|
||||
/* g711 only */
|
||||
pjsua_codec_info codecs[32];
|
||||
unsigned count = sizeof(codecs)/sizeof(*codecs);
|
||||
pjsua_enum_codecs(codecs,&count);
|
||||
for (int i=0; i<count; i++) {
|
||||
int pri = 0;
|
||||
if (pj_strcmp2(&codecs[i].codec_id,"PCMU/8000/1") == 0) {
|
||||
if (pj_strcmp2(&codecs[i].codec_id,"PCMA/8000/1") == 0) {
|
||||
pri = 1;
|
||||
} else if (pj_strcmp2(&codecs[i].codec_id,"PCMA/8000/1") == 0) {
|
||||
} else if (pj_strcmp2(&codecs[i].codec_id,"PCMU/8000/1") == 0) {
|
||||
pri = 2;
|
||||
}
|
||||
pjsua_codec_set_priority(&codecs[i].codec_id, pri);
|
||||
// printf("codec: %s %d\n",pj_strbuf(&codecs[i].codec_id),pri);
|
||||
}
|
||||
|
||||
pjsua_transport_id transport_id;
|
||||
/* Add UDP transport. */
|
||||
{
|
||||
pjsua_transport_config cfg;
|
||||
|
||||
pjsua_transport_config_default(&cfg);
|
||||
cfg.port = 5060;
|
||||
status = pjsua_transport_create(PJSIP_TRANSPORT_UDP, &cfg, NULL);
|
||||
if (mode)
|
||||
cfg.port = sip_port;
|
||||
if (getenv("PJSIP_IPV6"))
|
||||
status = pjsua_transport_create(PJSIP_TRANSPORT_UDP6, &cfg, &transport_id);
|
||||
else
|
||||
status = pjsua_transport_create(PJSIP_TRANSPORT_UDP, &cfg, &transport_id);
|
||||
if (status != PJ_SUCCESS) error_exit("Error creating transport", status);
|
||||
}
|
||||
char buf[384];
|
||||
printf("Initializing pool\n");
|
||||
//printf("Initializing pool\n");
|
||||
pj_caching_pool cp;
|
||||
pj_caching_pool_init(&cp, NULL, 1024*1024);
|
||||
pool = pj_pool_create(&cp.factory, "pool1", 4000, 4000, NULL);
|
||||
|
@ -232,8 +370,6 @@ int main(int argc, char *argv[]) {
|
|||
|
||||
memset(&port,0,sizeof(port));
|
||||
port.sock = atoi(argv[2]); // inherited from parent
|
||||
if(mode == DMODEM_RING_DETECT_MODE)
|
||||
port.sock = NULL;
|
||||
pjmedia_port_info_init(&port.base.info, &name, SIGNATURE, 9600, 1, 16, 192);
|
||||
port.base.put_frame = dmodem_put_frame;
|
||||
port.base.get_frame = dmodem_get_frame;
|
||||
|
@ -245,6 +381,7 @@ int main(int argc, char *argv[]) {
|
|||
status = pjsua_start();
|
||||
if (status != PJ_SUCCESS) error_exit("Error starting pjsua", status);
|
||||
|
||||
if (has_sip_user)
|
||||
{
|
||||
pjsua_acc_config cfg;
|
||||
pjsua_acc_config_default(&cfg);
|
||||
|
@ -263,32 +400,36 @@ int main(int argc, char *argv[]) {
|
|||
status = pjsua_acc_add(&cfg, PJ_TRUE, &acc_id);
|
||||
if (status != PJ_SUCCESS) error_exit("Error adding account", status);
|
||||
}
|
||||
else
|
||||
{
|
||||
status == pjsua_acc_add_local(transport_id, 1, &acc_id);
|
||||
if (status != PJ_SUCCESS) error_exit("Error adding local account", status);
|
||||
}
|
||||
pjsua_get_var()->tpdata[transport_id].has_bound_addr = PJ_TRUE;
|
||||
if (getenv("PJSIP_IPV6"))
|
||||
pjsua_get_var()->acc[acc_id].cfg.ipv6_media_use = PJSUA_IPV6_ENABLED;
|
||||
|
||||
start_pa();
|
||||
if(mode == DMODEM_DIAL_MODE) {
|
||||
snprintf(buf,sizeof(buf),"sip:%s@%s",dialstr,sip_domain);
|
||||
printf("calling %s\n",buf);
|
||||
if (has_sip_user)
|
||||
snprintf(buf,sizeof(buf),"sip:%s@%s",dialstr,sip_domain);
|
||||
else
|
||||
snprintf(buf,sizeof(buf),"sip:%s",dialstr);
|
||||
PJ_LOG(2,(__FILE__, "calling %s\n",buf));
|
||||
pj_str_t uri = pj_str(buf);
|
||||
pjsua_call_id callid;
|
||||
status = pjsua_call_make_call(acc_id, &uri, 0, NULL, NULL, &callid);
|
||||
if (status != PJ_SUCCESS) error_exit("Error making call", status);
|
||||
}
|
||||
if(mode == DMODEM_ANSWER_MODE) {
|
||||
pjsua_call_id id;
|
||||
char* cid = strrchr(argv[1], '+');
|
||||
id = atoi(cid);
|
||||
status = pjsua_call_answer(id, 200, NULL, NULL);
|
||||
if (status != PJ_SUCCESS) error_exit("Error answering call", status);
|
||||
}
|
||||
struct timespec ts = {100, 0};
|
||||
if(mode == DMODEM_RING_DETECT_MODE)
|
||||
ts.tv_sec = 1;
|
||||
time_t now = time(NULL);
|
||||
|
||||
struct timespec ts = {1, 0};
|
||||
while(1) {
|
||||
if(mode == DMODEM_RING_DETECT_MODE) {
|
||||
if(mode == DMODEM_ANSWER_MODE) {
|
||||
if(ringing) {
|
||||
char cid[11];
|
||||
snprintf(cid, 10, "%d", incoming);
|
||||
write(atoi(argv[2]), cid, strlen(cid));
|
||||
status = pjsua_call_answer(incoming, 200, NULL, NULL);
|
||||
if (status != PJ_SUCCESS) error_exit("Error answering call", status);
|
||||
ringing = 0;
|
||||
answered = 1;
|
||||
}
|
||||
}
|
||||
nanosleep(&ts,NULL);
|
||||
|
|
419
mm.py
Normal file
419
mm.py
Normal file
|
@ -0,0 +1,419 @@
|
|||
import subprocess
|
||||
import threading
|
||||
import select
|
||||
import time
|
||||
import tty
|
||||
import pathlib
|
||||
import re
|
||||
import os
|
||||
import signal
|
||||
import ipaddress
|
||||
import io
|
||||
import pty
|
||||
import logging
|
||||
import pwd
|
||||
import ctypes
|
||||
import argparse
|
||||
from typing import Union, Dict, List, Tuple, Callable, Any
|
||||
|
||||
logging.basicConfig(level=logging.DEBUG,format='%(threadName)-5s - %(levelname)s - %(message)s')
|
||||
logger = logging.getLogger()
|
||||
|
||||
PJSIP_V6 = False
|
||||
# exports PJSIP_IPV6=1 to d-modem
|
||||
# uses ipv6 sip transport to preserve address space
|
||||
# utilize ipv4 over v6 next hop so that no v4 is required in the container
|
||||
|
||||
PTY_LOC = pathlib.Path("/tmp")
|
||||
RUN_AS = "nobody"
|
||||
get_pty = lambda n: PTY_LOC / f"ttySL{n}"
|
||||
|
||||
MODEMD = "./slmodemd/slmodemd"
|
||||
D_MODEM = './d-modem.nopulse'
|
||||
|
||||
IP4RANGE = ipaddress.ip_network("10.0.0.0/27")
|
||||
IP6RANGE = ipaddress.ip_network("fd00::/64")
|
||||
|
||||
PRODUCTION = False
|
||||
|
||||
# Do we really need that many?
|
||||
# modem_configs: Dict[int, List[str]] = {
|
||||
# **{i: (["AT+MS=92,1" ], lambda: PPPProc) for i in range(00, 05)},
|
||||
# **{i: (["AT+MS=90,1" ], lambda: PPPProc) for i in range(05, 10)},
|
||||
# **{i: (["AT+MS=34,1" ], lambda: PPPProc) for i in range(10, 15)},
|
||||
# **{i: (["AT+MS=132,1"], lambda: PPPProc) for i in range(15, 20)},
|
||||
# **{i: (["AT+MS=32,1" ], lambda: PPPProc) for i in range(20, 25)}, # broken?
|
||||
# **{i: (["AT+MS=122,1"], lambda: PPPProc) for i in range(25, 30)},
|
||||
# **{i: (["AT+MS=22,1" ], lambda: PPPProc) for i in range(30, 35)},
|
||||
# **{i: (["AT+MS=212,1"], lambda: PPPProc) for i in range(35, 40)},
|
||||
# **{i: (["AT+MS=23,1" ], lambda: PPPProc) for i in range(40, 45)},
|
||||
# **{i: (["AT+MS=21,1" ], lambda: PPPProc) for i in range(45, 50)}, # broken?
|
||||
# **{i: (["AT+MS=103,1"], lambda: PPPProc) for i in range(50, 55)},
|
||||
# }
|
||||
|
||||
modem_configs: Dict[int, Tuple[List[str], Callable]] = {
|
||||
**{i: (["AT+MS=90,1"], lambda: PPPProc) for i in range(0, 1)},
|
||||
**{i: (["AT+MS=34,1"], lambda: PPPProc) for i in range(1, 2)},
|
||||
**{i: (["AT+MS=32,1"], lambda: PPPProc) for i in range(2, 3)},
|
||||
**{i: (["AT+MS=22,1"], lambda: PPPProc) for i in range(3, 5)},
|
||||
**{i: (["AT+MS=23,1"], lambda: PPPProc) for i in range(5, 7)},
|
||||
}
|
||||
|
||||
assert all(i >= 0 for i in modem_configs)
|
||||
|
||||
class BaseProc:
|
||||
def __init__(self) -> None:
|
||||
self.proc: subprocess.Popen = None
|
||||
def proc(self) -> subprocess.Popen:
|
||||
return self.proc
|
||||
def terminate(self, *args, **kwargs):
|
||||
return self.proc.terminate(*args, **kwargs)
|
||||
def wait(self, *args, **kwargs):
|
||||
return self.proc.wait(*args, **kwargs)
|
||||
def poll(self, *args, **kwargs):
|
||||
return self.proc.poll(*args, **kwargs)
|
||||
def send_signal(self, *args, **kwargs):
|
||||
return self.proc.send_signal(*args, **kwargs)
|
||||
|
||||
class ShellProc(BaseProc):
|
||||
''' danger! '''
|
||||
def __init__(self, ptyr: io.BufferedReader, ptyw: io.BufferedWriter, speed: int, no: int,
|
||||
pty_path: pathlib.Path, ip: Tuple[ipaddress.IPv4Address, ipaddress.IPv6Address]) -> None:
|
||||
assert not PRODUCTION
|
||||
self.proc = subprocess.Popen(
|
||||
['bash', '-c', 'stty sane;exec bash'],
|
||||
stdin=ptyr,
|
||||
stdout=ptyw,
|
||||
stderr=ptyw,
|
||||
preexec_fn=os.setsid,
|
||||
env=dict(os.environ, TERM='vt100')
|
||||
)
|
||||
|
||||
class PPPProc(BaseProc):
|
||||
def __init__(self, ptyr: io.BufferedReader, ptyw: io.BufferedWriter, speed: int, no: int,
|
||||
pty_path: pathlib.Path, ip: Tuple[ipaddress.IPv4Address, ipaddress.IPv6Address]) -> None:
|
||||
self.no = no
|
||||
self.ip = ip
|
||||
ptyw.write((
|
||||
f"Welcome to D-Modem\r\nExample pppd launch options:\r\n"
|
||||
f" pppd /dev/ttyACM0 {speed} defaultroute persist noproxyarp noauth modem nodetach\r\n"
|
||||
f"Your ipv6 address is {ip[1]}.\r\n"
|
||||
f"Please add Address={ip[1]}/{IP6RANGE.prefixlen} Gateway={IP6RANGE[1]}\r\n"
|
||||
"to your ppp network device.\r\n"
|
||||
"Enjoy!\r\n\r\n\r\n"
|
||||
).encode('utf-8'))
|
||||
ll_ident = lambda x: ipaddress.IPv6Address(int(x) & int(ipaddress.IPv6Address(2**(128-IP6RANGE.prefixlen)-1)))
|
||||
self.proc = PopenComm(
|
||||
[
|
||||
'pppd', str(pty_path.resolve()), str(speed), 'noproxyarp', 'passive', 'noauth',
|
||||
'unit', str(no), 'modem', 'nodefaultroute', 'nodetach', f"{IP4RANGE[0]}:{ip[0]}",
|
||||
'+ipv6', 'ipv6', f"{ll_ident(IP6RANGE[1])},{ll_ident(ip[1])}",
|
||||
'ipcp-max-configure', '100', 'ipcp-max-failure', '100', 'ipcp-restart', '15',
|
||||
'ipv6cp-max-configure', '100', 'ipv6cp-max-failure', '100', 'ipv6cp-restart', '15'
|
||||
]
|
||||
)
|
||||
def wait(self, *args, **kwargs):
|
||||
while (lines := self.proc.readline()) is not None:
|
||||
for line in lines:
|
||||
logger.info(f"pppd: {line}")
|
||||
if 'remote LL address' in line:
|
||||
try:
|
||||
subprocess.run(
|
||||
[
|
||||
'ip', '-6', 'a', 'replace', 'dev', f"ppp{self.no}",
|
||||
f"{IP6RANGE[1]}/128", 'peer', f"{self.ip[1]}/128"
|
||||
],
|
||||
stdin=subprocess.DEVNULL,
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.DEVNULL
|
||||
)
|
||||
logger.info(f"added v6 route to ppp{self.no}")
|
||||
except Exception:
|
||||
logger.exception(f"adding v6 route to ppp{self.no}")
|
||||
super().wait(*args, **kwargs)
|
||||
|
||||
class ModemManagerError(Exception):
|
||||
pass
|
||||
class ReadLineException(ModemManagerError):
|
||||
pass
|
||||
class PPPDDead(ModemManagerError):
|
||||
pass
|
||||
class CarrierLost(ModemManagerError):
|
||||
pass
|
||||
class ModemManager:
|
||||
terminating = False
|
||||
''' lol not that modem_manager '''
|
||||
def __init__(self):
|
||||
self.modems = {no: Modem(no, config) for no, config in modem_configs.items()}
|
||||
self.ip_used4 = {IP4RANGE[0]}
|
||||
self.ip_used6 = {IP6RANGE[0], IP6RANGE[1]}
|
||||
|
||||
class PopenComm(subprocess.Popen):
|
||||
def __init__(self, *args, **kwargs) -> None:
|
||||
try:
|
||||
self._popen_comm_master, self._popen_comm_slave = pty.openpty()
|
||||
os.set_blocking(self._popen_comm_master, False)
|
||||
self._popen_comm_r = open(self._popen_comm_master, "rb", buffering=0)
|
||||
kwargs['stdin'] = kwargs['stdout'] = kwargs['stderr'] = self._popen_comm_master
|
||||
super().__init__(*args, **kwargs)
|
||||
except Exception:
|
||||
self._popen_comm_cleanup()
|
||||
raise
|
||||
def w(self):
|
||||
try:
|
||||
super().wait(timeout=None)
|
||||
except Exception:
|
||||
logger.exception("Popen wait")
|
||||
self._popen_comm_cleanup()
|
||||
self.wtr = threading.Thread(target=w, args=(self,))
|
||||
self.wtr.start()
|
||||
|
||||
def wait(self, timeout=None) -> int:
|
||||
if timeout is not None:
|
||||
return super().wait(timeout=timeout)
|
||||
self.wtr.join()
|
||||
if self.poll() is None:
|
||||
return super().wait(timeout=None)
|
||||
return self.returncode
|
||||
|
||||
def _popen_comm_cleanup(self) -> None:
|
||||
try:
|
||||
os.close(self._popen_comm_slave)
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
self._popen_comm_r.close()
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
os.close(self._popen_comm_master)
|
||||
except Exception:
|
||||
pass
|
||||
logger.info(f"{self} pty pair closed")
|
||||
|
||||
def readline(self) -> Union[List[str], None]:
|
||||
''' blocks '''
|
||||
try:
|
||||
select.select([self._popen_comm_master], list(), list())
|
||||
read = self._popen_comm_r.read().decode('utf-8', errors='replace')
|
||||
except (OSError, ValueError):
|
||||
return None
|
||||
except Exception:
|
||||
logger.exception("readline")
|
||||
return None
|
||||
if not read:
|
||||
return None
|
||||
got = read.split('\n')
|
||||
if not got[-1]:
|
||||
got.pop(-1)
|
||||
return got
|
||||
|
||||
class IP_Request:
|
||||
lock = {4: threading.Lock(), 6: threading.Lock()}
|
||||
def __init__(self, version: int) -> None:
|
||||
assert version in {4, 6}
|
||||
self.v = version
|
||||
self.addr = None
|
||||
self.used = modem_manger.ip_used4 if self.v == 4 else modem_manger.ip_used6
|
||||
def __enter__(self):
|
||||
with self.lock[self.v]:
|
||||
for address in (IP4RANGE if self.v == 4 else IP6RANGE):
|
||||
if address in self.used:
|
||||
continue
|
||||
else:
|
||||
self.addr = address
|
||||
self.used.add(address)
|
||||
logger.info(f"ipv{self.v} lease: {str(self.addr)}")
|
||||
return address
|
||||
raise RuntimeError(f"error: ipv{self.v} address exhausted")
|
||||
def __exit__(self, *_):
|
||||
if self.addr:
|
||||
with self.lock[self.v]:
|
||||
self.used.remove(self.addr)
|
||||
logger.info(f"ipv{self.v} return: {str(self.addr)}")
|
||||
|
||||
class Modem:
|
||||
def __init__(self, no: int, config: Tuple[List[str], Callable]):
|
||||
self.no = no
|
||||
self.readline_quit = False
|
||||
self.readlinebuf = []
|
||||
self.modem_proc: Union[subprocess.Popen, None] = None
|
||||
self.ppp_proc: Union[subprocess.Popen, None] = None
|
||||
self.modem_cmd, get_ppp_func = config
|
||||
self.ppp_func = get_ppp_func()
|
||||
self.modem_cmd = [*self.modem_cmd, "ATA"]
|
||||
self.pty_path = get_pty(self.no)
|
||||
if os.path.exists(self.pty_path):
|
||||
logger.warning(f"pty link exists {str(self.pty_path)}")
|
||||
self.pty_path.unlink()
|
||||
self.modem_ctl: threading.Thread = threading.Thread(target=self._start_modem_ctl, name=f"md{no:03d}")
|
||||
self.ppp_ctl: threading.Thread = threading.Thread(target=self._start_ppp_ctl, name=f"pp{no:03d}")
|
||||
self.modem_ctl.start()
|
||||
self.ppp_ctl.start()
|
||||
|
||||
def _util_readline(self, dev: io.BufferedReader, timeout: float) -> bytes:
|
||||
readlinebuf = self.readlinebuf
|
||||
readlinebuf.clear()
|
||||
time_remaining = timeout
|
||||
while True:
|
||||
read_ready, _, _ = select.select([dev], list(), list(), 0.1)
|
||||
if self.readline_quit:
|
||||
self.readline_quit = False
|
||||
raise ReadLineException("process exit")
|
||||
if not read_ready:
|
||||
time_remaining -= 0.1
|
||||
if time_remaining < 0:
|
||||
break
|
||||
continue
|
||||
readlinebuf.append(dev.read(1))
|
||||
if readlinebuf[-1] == b'\n':
|
||||
return b''.join(readlinebuf)
|
||||
return b''.join(readlinebuf)
|
||||
|
||||
def _start_modem_ctl(self) -> None:
|
||||
while not ModemManager.terminating:
|
||||
try:
|
||||
if os.geteuid() == 0:
|
||||
pw_record = pwd.getpwnam(RUN_AS)
|
||||
uid, gid = pw_record.pw_uid, pw_record.pw_gid
|
||||
def demote():
|
||||
PR_SET_NO_NEW_PRIVS = 38
|
||||
PR_CAP_AMBIENT = 47
|
||||
PR_CAP_AMBIENT_CLEAR_ALL = 4
|
||||
PR_GET_SECUREBITS = 27
|
||||
PR_SET_SECUREBITS = 28
|
||||
libc = ctypes.CDLL('libc.so.6')
|
||||
libc.prctl.restype = ctypes.c_int
|
||||
assert libc.prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == 0
|
||||
assert libc.prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_CLEAR_ALL, 0, 0, 0) == 0
|
||||
assert libc.prctl(PR_SET_SECUREBITS, 0x2f) == 0
|
||||
# SECBIT_KEEP_CAPS_LOCKED | SECBIT_NO_SETUID_FIXUP | SECBIT_NO_SETUID_FIXUP_LOCKED | SECBIT_NOROOT | SECBIT_NOROOT_LOCKED
|
||||
assert libc.prctl(PR_GET_SECUREBITS) == 0x2f
|
||||
os.setgroups([])
|
||||
os.setresgid(gid, gid, gid)
|
||||
os.setresuid(uid, uid, uid)
|
||||
os.chdir(os.getcwd())
|
||||
preexec_func = demote
|
||||
env = {
|
||||
'USER': pw_record.pw_name,
|
||||
'LOGNAME': pw_record.pw_name,
|
||||
'HOME': '/',
|
||||
'PATH': os.environ['PATH'],
|
||||
}
|
||||
else:
|
||||
preexec_func = lambda: None
|
||||
env = dict(os.environ)
|
||||
logger.warning("d-modem is running as current user")
|
||||
assert not PRODUCTION
|
||||
if PJSIP_V6:
|
||||
env['PJSIP_IPV6'] = '1'
|
||||
else:
|
||||
if 'PJSIP_IPV6' in env:
|
||||
env.pop('PJSIP_IPV6')
|
||||
self.modem_proc = PopenComm(
|
||||
[MODEMD, '-e', D_MODEM, str(self.pty_path)],
|
||||
preexec_fn=preexec_func,
|
||||
env = env
|
||||
)
|
||||
while (lines := self.modem_proc.readline()) is not None:
|
||||
for line in lines:
|
||||
logger.info(f"slmodemd: {line}")
|
||||
self.modem_proc.wait()
|
||||
except Exception:
|
||||
logger.exception("unknown error")
|
||||
try:
|
||||
self.readline_quit = True
|
||||
if self.ppp_proc:
|
||||
self.ppp_proc.terminate()
|
||||
self.ppp_proc.wait()
|
||||
self.ppp_proc = None
|
||||
except Exception as err:
|
||||
logger.exception("unknown error")
|
||||
self.ppp_ctl.join()
|
||||
|
||||
def _start_ppp_ctl(self) -> None:
|
||||
time.sleep(1)
|
||||
while not ModemManager.terminating:
|
||||
commands = self.modem_cmd.copy()
|
||||
try:
|
||||
with open(self.pty_path.resolve(), "rb", buffering=0) as ptyr, open(self.pty_path.resolve(), "wb", buffering=0) as ptyw:
|
||||
tty.setraw(ptyr)
|
||||
tty.setraw(ptyw)
|
||||
while True:
|
||||
while (got := self._util_readline(ptyr, 0.3).decode('utf-8', errors='replace')):
|
||||
logger.debug(f"from pty: {got=}")
|
||||
if got:
|
||||
if (match := re.match(r'CONNECT ([0-9]+)', got)) and not self.ppp_proc:
|
||||
speed = int(match.groups()[0])
|
||||
assert speed > 0
|
||||
logger.info(f"connection {speed=}, start subprocess")
|
||||
ptyw.write(f"Remote speed {speed}\r\n".encode('utf-8'))
|
||||
try:
|
||||
with IP_Request(4) as ip4, IP_Request(6) as ip6:
|
||||
self.ppp_proc = self.ppp_func(ptyr, ptyw, speed, self.no, self.pty_path, (ip4, ip6))
|
||||
self.ppp_proc.wait()
|
||||
except Exception:
|
||||
logger.exception("unknown subprocess error")
|
||||
self.modem_proc.send_signal(signal.SIGINT)
|
||||
self.modem_proc.wait()
|
||||
raise PPPDDead
|
||||
elif got == 'NO CARRIER\r\n':
|
||||
self._util_readline(ptyr, 2)
|
||||
raise CarrierLost
|
||||
if commands:
|
||||
cmd = commands.pop(0)
|
||||
ptyw.write(f"{cmd}\n\r".encode('ascii'))
|
||||
logger.info(f"command: {cmd}")
|
||||
ptyw.flush()
|
||||
time.sleep(0.1)
|
||||
except ModemManagerError as err:
|
||||
logger.info(f"pty detach: {repr(err)}")
|
||||
time.sleep(0.1)
|
||||
except Exception:
|
||||
logger.exception("unknown error")
|
||||
time.sleep(2)
|
||||
|
||||
if __name__ == "__main__":
|
||||
abspath=os.path.abspath(__file__)
|
||||
abspath=os.path.dirname(abspath)
|
||||
os.chdir(abspath)
|
||||
|
||||
parser = argparse.ArgumentParser(description='ModemManager.py')
|
||||
parser.add_argument('-4', '--ipv4', type=str, default=str(IP4RANGE), help='ipv4 subnet')
|
||||
parser.add_argument('-6', '--ipv6', type=str, default=str(IP6RANGE), help='ipv6 subnet')
|
||||
parser.add_argument('-m', '--modemd', type=str, default=str(MODEMD), help='modemd location')
|
||||
parser.add_argument('-d', '--dmodem', type=str, default=str(D_MODEM), help='d-modem location')
|
||||
parser.add_argument('-p', '--pty', type=str, default=str(PTY_LOC), help='pty link location')
|
||||
parser.add_argument('-u', '--user', type=str, default=str(RUN_AS), help='run as user')
|
||||
parser.add_argument('-s', '--production', action='store_true', help='enable strict checks')
|
||||
parser.add_argument('--pjsip6', action='store_true', help='pjsip force v6')
|
||||
args = parser.parse_args()
|
||||
|
||||
def g(x: str, y: Any) -> None:
|
||||
globals()[x] = y
|
||||
g('IP4RANGE', ipaddress.ip_network(args.ipv4))
|
||||
g('IP6RANGE', ipaddress.ip_network(args.ipv6))
|
||||
g('MODEMD', args.modemd)
|
||||
g('D_MODEM', args.dmodem)
|
||||
g('PTY_LOC', pathlib.Path(args.pty))
|
||||
g('RUN_AS', args.user)
|
||||
g('PRODUCTION', args.production)
|
||||
g('PJSIP_V6', args.pjsip6)
|
||||
if args.production:
|
||||
logger.setLevel(logging.INFO)
|
||||
|
||||
modem_manger = ModemManager()
|
||||
|
||||
def sighandler(signum, _frame):
|
||||
if ModemManager.terminating:
|
||||
logger.error(f"received signal {signum} while terminating")
|
||||
else:
|
||||
ModemManager.terminating = True
|
||||
logger.warning(f"terminate on signal {signum}")
|
||||
for modem in modem_manger.modems.values():
|
||||
modem.modem_proc.terminate()
|
||||
for sig in (signal.SIGTERM, signal.SIGHUP, signal.SIGINT, signal.SIGABRT):
|
||||
signal.signal(sig, sighandler)
|
||||
|
||||
for modem in modem_manger.modems.values():
|
||||
modem.modem_ctl.join()
|
|
@ -24,7 +24,8 @@ endif
|
|||
|
||||
RM:= rm -f
|
||||
|
||||
CFLAGS+= -Wall -g -O -I. -DCONFIG_DEBUG_MODEM
|
||||
# CFLAGS+= -Wall -g -O -I. -DCONFIG_DEBUG_MODEM
|
||||
CFLAGS+= -Wall -O1 -I. -DCONFIG_DEBUG_MODEM
|
||||
|
||||
modem-objs:= \
|
||||
modem.o modem_datafile.o modem_at.o modem_timer.o \
|
||||
|
|
|
@ -70,6 +70,7 @@
|
|||
#define MODEM_DBG(fmt,arg...) dprintf("%s: " fmt , m->name , ##arg)
|
||||
#define MODEM_ERR(fmt,arg...) eprintf("%s: " fmt , m->name , ##arg)
|
||||
|
||||
short _MODEM_DO_ANSWER = 0;
|
||||
/* external symbols */
|
||||
extern int process_at_command(struct modem *m, char *buf);
|
||||
extern void *dp_runtime_create(struct modem *m);
|
||||
|
@ -1567,6 +1568,7 @@ int modem_recv_from_tty(struct modem *m, char *buf, int n)
|
|||
|
||||
int modem_answer(struct modem *m)
|
||||
{
|
||||
_MODEM_DO_ANSWER = 1;
|
||||
MODEM_DBG("modem answer...\n");
|
||||
if ( m->dp ) {
|
||||
MODEM_ERR("dp %d is already exists.\n", m->dp->id);
|
||||
|
@ -1613,6 +1615,7 @@ static int modem_dial_start(struct modem *m)
|
|||
|
||||
int modem_dial(struct modem *m)
|
||||
{
|
||||
_MODEM_DO_ANSWER = 0;
|
||||
int ret;
|
||||
MODEM_DBG("modem dial: %s...\n", m->dial_string);
|
||||
m->dp_requested = 0;
|
||||
|
|
|
@ -456,5 +456,7 @@ extern void modem_update_config(struct modem *m, struct modem_config *cfg);
|
|||
#define MODEM_DP(m) ((m)->sregs[SREG_DP])
|
||||
#define MODEM_AUTOMODE(m) ((m)->sregs[SREG_AUTOMODE])
|
||||
|
||||
extern short _MODEM_DO_ANSWER;
|
||||
|
||||
#endif /* __MODEM_H__ */
|
||||
|
||||
|
|
|
@ -68,10 +68,10 @@ extern unsigned int modem_debug_logging;
|
|||
|
||||
/* config parameters */
|
||||
const char *modem_dev_name = NULL;
|
||||
const char *modem_default_dev_name = "/dev/slamr0";
|
||||
const char *modem_default_dev_name = "/tmp/ttySL0";
|
||||
const char *modem_alsa_dev_name = "modem:1";
|
||||
const char *modem_exec = NULL;
|
||||
unsigned int need_realtime = 1;
|
||||
unsigned int need_realtime = 0;
|
||||
#ifdef MODEM_CONFIG_RING_DETECTOR
|
||||
unsigned int ring_detector = 0;
|
||||
#endif
|
||||
|
@ -125,7 +125,7 @@ static struct opt {
|
|||
{'s',"shortbuffer","use short buffer (4 periods length)"},
|
||||
{'d',"debug","debug level (developers only, for ./sl...)",OPTIONAL,INTEGER,"0"},
|
||||
{'l',"log","logging mode",OPTIONAL,INTEGER,"5"},
|
||||
{'e',"exec","path to external application that transmits audio over the socket (required)",MANDATORY,STRING,""},
|
||||
{'e',"exec","path to external application that transmits audio over the socket (required)",MANDATORY,STRING,"./d-modem"},
|
||||
{}
|
||||
};
|
||||
|
||||
|
|
|
@ -87,7 +87,7 @@
|
|||
#define DBG(fmt,args...) dprintf("main: " fmt, ##args)
|
||||
|
||||
|
||||
#define SLMODEMD_USER "nobody"
|
||||
//#define SLMODEMD_USER "nobody"
|
||||
#define LOCKED_MEM_MIN_KB (8UL * 1024)
|
||||
#define LOCKED_MEM_MIN (LOCKED_MEM_MIN_KB * 1024)
|
||||
|
||||
|
@ -217,19 +217,19 @@ static int alsa_device_setup(struct device_struct *dev, const char *dev_name)
|
|||
ret = snd_pcm_open(&dev->phandle, dev_name, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
|
||||
if(ret < 0) {
|
||||
ERR("alsa setup: cannot open playback device '%s': %s\n",
|
||||
dev_name, snd_strerror(ret));
|
||||
dev_name, snd_strerror(ret));
|
||||
return -1;
|
||||
}
|
||||
ret = snd_pcm_open(&dev->chandle, dev_name, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK);
|
||||
if(ret < 0) {
|
||||
ERR("alsa setup: cannot open playback device '%s': %s\n",
|
||||
dev_name, snd_strerror(ret));
|
||||
dev_name, snd_strerror(ret));
|
||||
return -1;
|
||||
}
|
||||
ret = snd_pcm_poll_descriptors(dev->chandle, &pfd, 1);
|
||||
if(ret <= 0) {
|
||||
ERR("alsa setup: cannot get poll descriptors of '%s': %s\n",
|
||||
dev_name, snd_strerror(ret));
|
||||
dev_name, snd_strerror(ret));
|
||||
return -1;
|
||||
}
|
||||
dev->fd = pfd.fd;
|
||||
|
@ -313,7 +313,7 @@ static int alsa_device_write(struct device_struct *dev, const char *buf, int cou
|
|||
if (ret == -EAGAIN)
|
||||
continue;
|
||||
if (ret == -EPIPE) {
|
||||
ret = alsa_xrun_recovery(dev);
|
||||
ret = alsa_xrun_recovery(dev);
|
||||
}
|
||||
written = ret;
|
||||
break;
|
||||
|
@ -373,7 +373,7 @@ static int setup_stream(snd_pcm_t *handle, struct modem *m, const char *stream_n
|
|||
ERR("cannot set format for %s: %s\n", stream_name, snd_strerror(err));
|
||||
return err;
|
||||
}
|
||||
err = snd_pcm_hw_params_set_channels(handle, hw_params, 1);
|
||||
err = snd_pcm_hw_params_set_channels(handle, hw_params, 1);
|
||||
if (err < 0) {
|
||||
ERR("cannot set channels for %s: %s\n", stream_name, snd_strerror(err));
|
||||
return err;
|
||||
|
@ -386,7 +386,7 @@ static int setup_stream(snd_pcm_t *handle, struct modem *m, const char *stream_n
|
|||
}
|
||||
if ( rrate != rate ) {
|
||||
ERR("rate %d is not supported by %s (%d).\n",
|
||||
rate, stream_name, rrate);
|
||||
rate, stream_name, rrate);
|
||||
return -1;
|
||||
}
|
||||
rsize = size = dev->period ;
|
||||
|
@ -397,7 +397,7 @@ static int setup_stream(snd_pcm_t *handle, struct modem *m, const char *stream_n
|
|||
}
|
||||
if ( rsize < size ) {
|
||||
ERR("period size %ld is not supported by %s (%ld).\n",
|
||||
size, stream_name, rsize);
|
||||
size, stream_name, rsize);
|
||||
return -1;
|
||||
}
|
||||
rsize = size = use_short_buffer ? rsize * dev->buf_periods : rsize * 32;
|
||||
|
@ -408,7 +408,7 @@ static int setup_stream(snd_pcm_t *handle, struct modem *m, const char *stream_n
|
|||
}
|
||||
if ( rsize != size ) {
|
||||
DBG("buffer size for %s is changed %ld -> %ld\n",
|
||||
stream_name, size, rsize);
|
||||
stream_name, size, rsize);
|
||||
}
|
||||
err = snd_pcm_hw_params(handle, hw_params);
|
||||
if (err < 0) {
|
||||
|
@ -523,9 +523,9 @@ static int alsa_ioctl(struct modem *m, unsigned int cmd, unsigned long arg)
|
|||
struct device_struct *dev = m->dev_data;
|
||||
DBG("alsa_ioctl: cmd %x, arg %lx...\n",cmd,arg);
|
||||
switch(cmd) {
|
||||
case MDMCTL_CAPABILITIES:
|
||||
return -EINVAL;
|
||||
case MDMCTL_HOOKSTATE:
|
||||
case MDMCTL_CAPABILITIES:
|
||||
return -EINVAL;
|
||||
case MDMCTL_HOOKSTATE:
|
||||
return (dev->hook_off_elem) ?
|
||||
snd_mixer_selem_set_playback_switch_all(
|
||||
dev->hook_off_elem,
|
||||
|
@ -534,9 +534,9 @@ static int alsa_ioctl(struct modem *m, unsigned int cmd, unsigned long arg)
|
|||
return (dev->speaker_elem) ?
|
||||
snd_mixer_selem_set_playback_volume_all(
|
||||
dev->speaker_elem, arg) : 0 ;
|
||||
case MDMCTL_CODECTYPE:
|
||||
return CODEC_SILABS;
|
||||
case MDMCTL_IODELAY:
|
||||
case MDMCTL_CODECTYPE:
|
||||
return CODEC_SILABS;
|
||||
case MDMCTL_IODELAY:
|
||||
DBG("delay = %d\n", dev->delay);
|
||||
return dev->delay;
|
||||
default:
|
||||
|
@ -547,10 +547,10 @@ static int alsa_ioctl(struct modem *m, unsigned int cmd, unsigned long arg)
|
|||
|
||||
|
||||
struct modem_driver alsa_modem_driver = {
|
||||
.name = "alsa modem driver",
|
||||
.start = alsa_start,
|
||||
.stop = alsa_stop,
|
||||
.ioctl = alsa_ioctl,
|
||||
.name = "alsa modem driver",
|
||||
.start = alsa_start,
|
||||
.stop = alsa_stop,
|
||||
.ioctl = alsa_ioctl,
|
||||
};
|
||||
|
||||
|
||||
|
@ -568,7 +568,7 @@ static int modemap_start (struct modem *m)
|
|||
int ret;
|
||||
DBG("modemap_start...\n");
|
||||
dev->delay = 0;
|
||||
ret = ioctl(dev->fd,100000+MDMCTL_START,0);
|
||||
ret = ioctl(dev->fd,100000+MDMCTL_START,0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = 192*2;
|
||||
|
@ -586,7 +586,7 @@ static int modemap_stop (struct modem *m)
|
|||
{
|
||||
struct device_struct *dev = m->dev_data;
|
||||
DBG("modemap_stop...\n");
|
||||
return ioctl(dev->fd,100000+MDMCTL_STOP,0);
|
||||
return ioctl(dev->fd,100000+MDMCTL_STOP,0);
|
||||
}
|
||||
|
||||
static int modemap_ioctl(struct modem *m, unsigned int cmd, unsigned long arg)
|
||||
|
@ -607,10 +607,10 @@ static int modemap_ioctl(struct modem *m, unsigned int cmd, unsigned long arg)
|
|||
|
||||
|
||||
struct modem_driver mdm_modem_driver = {
|
||||
.name = "modemap driver",
|
||||
.start = modemap_start,
|
||||
.stop = modemap_stop,
|
||||
.ioctl = modemap_ioctl,
|
||||
.name = "modemap driver",
|
||||
.start = modemap_start,
|
||||
.stop = modemap_stop,
|
||||
.ioctl = modemap_ioctl,
|
||||
};
|
||||
|
||||
static int socket_start (struct modem *m)
|
||||
|
@ -626,20 +626,27 @@ static int socket_start (struct modem *m)
|
|||
exit(-1);
|
||||
}
|
||||
|
||||
pid_t ppid = getpid();
|
||||
pid_t pid = fork();
|
||||
if (pid == -1) {
|
||||
perror("fork");
|
||||
exit(-1);
|
||||
}
|
||||
if (pid == 0) { // child
|
||||
char str[16];
|
||||
snprintf(str,sizeof(str),"%d",sockets[0]);
|
||||
char _str[12];
|
||||
char _parent[12];
|
||||
snprintf(_str,sizeof(_str),"%d",sockets[0]);
|
||||
snprintf(_parent,sizeof(_parent),"%d",ppid);
|
||||
const char *_modem_path = strrchr(modem_dev_name, '/');
|
||||
_modem_path = _modem_path == NULL ? modem_dev_name : _modem_path + 1;
|
||||
close(sockets[1]);
|
||||
if(m->hook == MODEM_HOOK_SNOOPING) {
|
||||
ret = execl(modem_exec,modem_exec,"rr",str,NULL);
|
||||
if(_MODEM_DO_ANSWER) {
|
||||
DBG("MODEM_ANSW execl arg: %s, %s, %s, %s, %s\n",modem_exec,"rr",_str,_parent,_modem_path);
|
||||
ret = execl(modem_exec,modem_exec,"rr",_str,_parent,_modem_path,NULL);
|
||||
}
|
||||
else {
|
||||
ret = execl(modem_exec,modem_exec,m->dial_string,str,NULL);
|
||||
DBG("MODEM_DIAL execl arg: %s, %s, %s, %s, %s\n",modem_exec,m->dial_string,_str,_parent,_modem_path);
|
||||
ret = execl(modem_exec,modem_exec,m->dial_string,_str,_parent,_modem_path,NULL);
|
||||
}
|
||||
if (ret == -1) {
|
||||
ERR("prog: %s\n", modem_exec);
|
||||
|
@ -653,13 +660,13 @@ static int socket_start (struct modem *m)
|
|||
ret = 192*2;
|
||||
memset(outbuf, 0 , ret);
|
||||
ret = write(dev->fd, outbuf, ret);
|
||||
DBG("done delay thing\n");
|
||||
if (ret < 0) {
|
||||
close(dev->fd);
|
||||
dev->fd = -1;
|
||||
return ret;
|
||||
}
|
||||
dev->delay = ret/2;
|
||||
DBG("done delay thing %d\n", dev->delay);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -713,10 +720,10 @@ static int socket_ioctl(struct modem *m, unsigned int cmd, unsigned long arg)
|
|||
}
|
||||
|
||||
struct modem_driver socket_modem_driver = {
|
||||
.name = "socket driver",
|
||||
.start = socket_start,
|
||||
.stop = socket_stop,
|
||||
.ioctl = socket_ioctl,
|
||||
.name = "socket driver",
|
||||
.start = socket_start,
|
||||
.stop = socket_stop,
|
||||
.ioctl = socket_ioctl,
|
||||
};
|
||||
|
||||
static int mdm_device_read(struct device_struct *dev, char *buf, int size)
|
||||
|
@ -788,11 +795,11 @@ int create_pty(struct modem *m)
|
|||
if(m->pty)
|
||||
close(m->pty);
|
||||
|
||||
pty = getpt();
|
||||
if (pty < 0 || grantpt(pty) < 0 || unlockpt(pty) < 0) {
|
||||
ERR("getpt: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
pty = getpt();
|
||||
if (pty < 0 || grantpt(pty) < 0 || unlockpt(pty) < 0) {
|
||||
ERR("getpt: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(m->pty) {
|
||||
termios = m->termios;
|
||||
|
@ -805,11 +812,11 @@ int create_pty(struct modem *m)
|
|||
cfsetospeed(&termios, B115200);
|
||||
}
|
||||
|
||||
ret = tcsetattr(pty, TCSANOW, &termios);
|
||||
if (ret) {
|
||||
ERR("tcsetattr: %s\n",strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
ret = tcsetattr(pty, TCSANOW, &termios);
|
||||
if (ret) {
|
||||
ERR("tcsetattr: %s\n",strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
fcntl(pty,F_SETFL,O_NONBLOCK);
|
||||
|
||||
|
@ -820,17 +827,18 @@ int create_pty(struct modem *m)
|
|||
|
||||
modem_update_termios(m,&termios);
|
||||
|
||||
modem_group = NULL;
|
||||
if(modem_group && *modem_group) {
|
||||
struct group *grp = getgrnam(modem_group);
|
||||
if(!grp) {
|
||||
ERR("cannot find group '%s': %s\n", modem_group,
|
||||
strerror(errno));
|
||||
strerror(errno));
|
||||
}
|
||||
else {
|
||||
ret = chown(pty_name, -1, grp->gr_gid);
|
||||
if(ret < 0) {
|
||||
ERR("cannot chown '%s' to ':%s': %s\n",
|
||||
pty_name, modem_group, strerror(errno));
|
||||
pty_name, modem_group, strerror(errno));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -838,19 +846,19 @@ int create_pty(struct modem *m)
|
|||
ret = chmod(pty_name, modem_perm);
|
||||
if (ret < 0) {
|
||||
ERR("cannot chmod '%s' to %o: %s\n",
|
||||
pty_name, modem_perm, strerror(errno));
|
||||
pty_name, modem_perm, strerror(errno));
|
||||
}
|
||||
|
||||
if(*link_name) {
|
||||
unlink(link_name);
|
||||
if(symlink(pty_name,link_name)) {
|
||||
ERR("cannot create symbolink link `%s' -> `%s': %s\n",
|
||||
link_name,pty_name,strerror(errno));
|
||||
link_name,pty_name,strerror(errno));
|
||||
*link_name = '\0';
|
||||
}
|
||||
else {
|
||||
INFO("symbolic link `%s' -> `%s' created.\n",
|
||||
link_name, pty_name);
|
||||
link_name, pty_name);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -898,9 +906,9 @@ static int modem_run(struct modem *m, struct device_struct *dev)
|
|||
modem_ring_detector_start(m);
|
||||
#endif
|
||||
|
||||
tmo.tv_sec = 1;
|
||||
tmo.tv_usec= 0;
|
||||
FD_ZERO(&rset);
|
||||
tmo.tv_sec = 1;
|
||||
tmo.tv_usec= 0;
|
||||
FD_ZERO(&rset);
|
||||
FD_ZERO(&eset);
|
||||
if(m->started)
|
||||
FD_SET(dev->fd,&rset);
|
||||
|
@ -918,14 +926,14 @@ static int modem_run(struct modem *m, struct device_struct *dev)
|
|||
if(m->pty > max_fd) max_fd = m->pty;
|
||||
}
|
||||
|
||||
ret = select(max_fd + 1,&rset,NULL,&eset,&tmo);
|
||||
ret = select(max_fd + 1,&rset,NULL,&eset,&tmo);
|
||||
|
||||
if (ret < 0) {
|
||||
if (ret < 0) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
ERR("select: %s\n",strerror(errno));
|
||||
return ret;
|
||||
}
|
||||
ERR("select: %s\n",strerror(errno));
|
||||
return ret;
|
||||
}
|
||||
|
||||
if ( ret == 0 )
|
||||
continue;
|
||||
|
@ -1002,7 +1010,7 @@ static int modem_run(struct modem *m, struct device_struct *dev)
|
|||
}
|
||||
if(count != m->update_delay) {
|
||||
ERR("cannot update delay: %d instead of %d.\n",
|
||||
count, m->update_delay);
|
||||
count, m->update_delay);
|
||||
return -1;
|
||||
}
|
||||
dev->delay += m->update_delay;
|
||||
|
@ -1074,7 +1082,7 @@ int modem_main(const char *dev_name)
|
|||
struct modem *m;
|
||||
int pty;
|
||||
int ret = 0;
|
||||
struct passwd *pwd;
|
||||
// struct passwd *pwd;
|
||||
|
||||
modem_debug_init(basename(dev_name));
|
||||
|
||||
|
@ -1089,7 +1097,7 @@ int modem_main(const char *dev_name)
|
|||
prop_dp_init();
|
||||
modem_timer_init();
|
||||
|
||||
sprintf(link_name,"/dev/ttySL%d", device.num);
|
||||
sprintf(link_name, "%s", modem_dev_name);
|
||||
|
||||
m = modem_create(modem_driver,basename(dev_name));
|
||||
m->name = basename(dev_name);
|
||||
|
@ -1103,10 +1111,10 @@ int modem_main(const char *dev_name)
|
|||
}
|
||||
|
||||
INFO("modem `%s' created. TTY is `%s'\n",
|
||||
m->name, m->pty_name);
|
||||
m->name, m->pty_name);
|
||||
|
||||
sprintf(path_name,"/var/lib/slmodem/data.%s",basename(dev_name));
|
||||
datafile_load_info(path_name,&m->dsp_info);
|
||||
// sprintf(path_name,"/var/lib/slmodem/data.%s",basename(dev_name));
|
||||
// datafile_load_info(path_name,&m->dsp_info);
|
||||
< |