X-Git-Url: http://git.eversberg.eu/gitweb.cgi?p=lcr.git;a=blobdiff_plain;f=port.cpp;h=0a5687c1d6fcd5120e959119a4d050ae6cdaa22a;hp=4afca9c7978729533a701f6f57f547a1835540d4;hb=5566f74eb29be75da44e29ba72ee6f015249ce61;hpb=5463e1b62a39ce417b610584e3d34a8bc30ac15e diff --git a/port.cpp b/port.cpp index 4afca9c..0a5687c 100644 --- a/port.cpp +++ b/port.cpp @@ -47,6 +47,9 @@ Functions: #include "main.h" +/* enable to test conference mixing, even if only two members are bridged */ +//#define TEST_CONFERENCE 1 + #define SHORT_MIN -32768 #define SHORT_MAX 32767 @@ -145,7 +148,7 @@ struct epoint_list *Port::epointlist_new(unsigned int epoint_id) /* * port constructor */ -Port::Port(int type, const char *portname, struct port_settings *settings) +Port::Port(int type, const char *portname, struct port_settings *settings, struct interface *interface) { class Port *temp, **tempp; @@ -156,6 +159,10 @@ Port::Port(int type, const char *portname, struct port_settings *settings) memset(&p_settings, 0, sizeof(p_settings)); } SCPY(p_name, portname); + if (interface) { + SCPY(p_interface_name, interface->name); + SCPY(p_tones_interface, interface->tones_dir); + } p_tone_dir[0] = '\0'; p_type = type; p_serial = port_serial++; @@ -174,6 +181,7 @@ Port::Port(int type, const char *portname, struct port_settings *settings) /* call recording */ p_record = NULL; + p_tap = 0; p_record_type = 0; p_record_length = 0; p_record_skip = 0; @@ -182,6 +190,13 @@ Port::Port(int type, const char *portname, struct port_settings *settings) p_record_buffer_writep = 0; p_record_buffer_dir = 0; + /* VoOTP */ +#ifdef WITH_VOOTP + p_vootp = NULL; +#endif + /* D-O-V */ + dov_init(); + /* append port to chain */ next = NULL; temp = port_first; @@ -194,7 +209,7 @@ Port::Port(int type, const char *portname, struct port_settings *settings) classuse++; - PDEBUG(DEBUG_PORT, "new port (%d) of type 0x%x, name '%s'\n", p_serial, type, portname); + PDEBUG(DEBUG_PORT, "new port (%d) of type 0x%x, name '%s' interface '%s'\n", p_serial, type, portname, p_interface_name); } @@ -206,7 +221,14 @@ Port::~Port(void) class Port *temp, **tempp; struct lcr_msg *message; - PDEBUG(DEBUG_PORT, "removing port (%d) of type 0x%x, name '%s'\n", p_serial, p_type, p_name); + PDEBUG(DEBUG_PORT, "removing port (%d) of type 0x%x, name '%s' interface '%s'\n", p_serial, p_type, p_name, p_interface_name); + +#ifdef WITH_VOOTP + if (p_vootp) { + vootp_destroy(p_vootp); + p_vootp = NULL; + } +#endif if (p_bridge) { PDEBUG(DEBUG_PORT, "Removing us from bridge %u\n", p_bridge->bridge_id); @@ -216,6 +238,8 @@ Port::~Port(void) if (p_record) close_record(0, 0); + dov_exit(); + classuse--; /* disconnect port from endpoint */ @@ -301,8 +325,12 @@ void Port::set_tone(const char *dir, const char *name) if (name == NULL) name = ""; - if (!dir || !dir[0]) - dir = options.tones_dir; /* just in case we have no PmISDN instance */ + if (!dir || !dir[0]) { + if (p_tones_interface[0]) + dir = p_tones_interface; + else + dir = options.tones_dir; /* just in case we have no PmISDN instance */ + } /* no counter, no eof, normal speed */ p_tone_counter = 0; @@ -371,6 +399,10 @@ void Port::set_tone(const char *dir, const char *name) } +void Port::set_display(const char *text) +{ +} + /* * set the file in the tone directory for vbox playback * also set the play_eof-flag @@ -631,6 +663,23 @@ int Port::message_epoint(unsigned int epoint_id, int message_id, union parameter PDEBUG(DEBUG_PORT, "PORT(%s) bridging to id %d\n", p_name, param->bridge_id); bridge(param->bridge_id); return 1; + +#ifdef WITH_VOOTP + case MESSAGE_VOOTP: /* enable / disable VoOTP */ + PDEBUG(DEBUG_PORT, "PORT(%s) VoOTP enabled: %d\n", p_name, param->vootp.enable); + set_vootp(¶m->vootp); + return 1; +#endif + + case MESSAGE_DOV_REQUEST: /* Data-Over-Voice message */ + PDEBUG(DEBUG_PORT, "PORT(%s) sending data over voice message (len=%d)\n", p_name, param->dov.length); + dov_sendmsg(param->dov.data, param->dov.length, (enum dov_type)param->dov.type, param->dov.level); + return 1; + + case MESSAGE_DOV_LISTEN: /* Data-Over-Voice listen order */ + PDEBUG(DEBUG_PORT, "PORT(%s) sending data over voice listen order\n", p_name); + dov_listen((enum dov_type)param->dov.type); + return 1; } return 0; @@ -660,7 +709,7 @@ int Port::open_record(int type, int vbox, int skip, char *extension, int anon_ig char filename[256]; time_t now; struct tm *now_tm; - int ret; + int __attribute__((__unused__)) ret; if (!extension) { PERROR("Port(%d) not an extension\n", p_serial); @@ -725,6 +774,7 @@ int Port::open_record(int type, int vbox, int skip, char *extension, int anon_ig case CODEC_MONO: case CODEC_STEREO: case CODEC_8BIT: + memset(&dummyheader, 0, sizeof(dummyheader)); ret = fwrite(dummyheader, sizeof(dummyheader), 1, p_record); break; @@ -753,7 +803,7 @@ void Port::close_record(int beep, int mute) char *p; struct caller_info callerinfo; const char *valid_chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890_.-!$%&/()=+*;~"; - int ret; + int __attribute__((__unused__)) ret; if (!p_record) return; @@ -954,7 +1004,7 @@ void Port::record(unsigned char *data, int length, int dir_fromup) signed short *s; int free, i, ii; signed int sample; - int ret; + int __attribute__((__unused__)) ret; /* no recording */ if (!p_record || !length) @@ -1159,6 +1209,10 @@ different_again: } +void Port::tap(unsigned char *data, int length, int dir_fromup) +{ +} + void Port::update_rxoff(void) { } @@ -1172,25 +1226,36 @@ void Port::update_load(void) * bridge handling */ +int bridge_timeout(struct lcr_timer *timer, void *instance, int index); + static void remove_bridge(struct port_bridge *bridge, class Port *port) { struct port_bridge **temp = &p_bridge_first; while (*temp) { if (*temp == bridge) { - int remove = 0; - - /* Remove us from bridge. If bridge is empty, remove it completely. */ - if (bridge->sunrise == port) { - bridge->sunrise = NULL; - if (!bridge->sunset) - remove = 1; - } - if (bridge->sunset == port) { - bridge->sunset = NULL; - if (!bridge->sunrise) - remove = 1; + struct port_bridge_member **memberp = &bridge->first, *member; + + /* loop until we are found */ + while(*memberp) { + if ((*memberp)->port == port) { + member = *memberp; + *memberp = member->next; + FREE(member, sizeof(struct port_bridge_member)); + memuse--; +#ifndef TEST_CONFERENCE + if (bridge->first && bridge->first->next && !bridge->first->next->next) { +#else + if (bridge->first && !bridge->first->next) { +#endif + PDEBUG(DEBUG_PORT, "bridge %u is no conference anymore\n", bridge->bridge_id); + del_timer(&bridge->timer); + } + break; + } + memberp = &((*memberp)->next); } - if (remove) { + /* if bridge is empty, remove it */ + if (bridge->first == NULL) { PDEBUG(DEBUG_PORT, "Remove bridge %u\n", bridge->bridge_id); *temp = bridge->next; FREE(bridge, sizeof(struct port_bridge)); @@ -1205,6 +1270,8 @@ static void remove_bridge(struct port_bridge *bridge, class Port *port) void Port::bridge(unsigned int bridge_id) { + struct port_bridge_member **memberp; + /* Remove bridge, if we leave bridge or if we join a different bridge. */ if (p_bridge && bridge_id != p_bridge->bridge_id) { PDEBUG(DEBUG_PORT, "Remove port %u from bridge %u, because out new bridge is %u\n", p_serial, p_bridge->bridge_id, bridge_id); @@ -1237,7 +1304,6 @@ void Port::bridge(unsigned int bridge_id) p_bridge = (struct port_bridge *) MALLOC(sizeof(struct port_bridge)); memuse++; p_bridge->bridge_id = bridge_id; - p_bridge->sunrise = this; /* attach bridge instance to list */ while (*temp) @@ -1246,46 +1312,211 @@ void Port::bridge(unsigned int bridge_id) PDEBUG(DEBUG_PORT, "Port %d creating not existing bridge %u.\n", p_serial, p_bridge->bridge_id); } - /* already joined */ - if (p_bridge->sunrise == this || p_bridge->sunset == this) - return; - - /* join bridge */ - if (!p_bridge->sunrise) { - p_bridge->sunrise = this; - return; + /* attach to bridge */ + memberp = &p_bridge->first; + while(*memberp) { + if ((*memberp)->port == this) { + /* already joined */ + return; + } + memberp = &((*memberp)->next); } - if (!p_bridge->sunset) { - p_bridge->sunset = this; - return; + *memberp = (struct port_bridge_member *) MALLOC(sizeof(struct port_bridge_member)); + memuse++; + (*memberp)->port = this; + /* check if bridge becomes a conference */ +#ifndef TEST_CONFERENCE + if (p_bridge->first->next && p_bridge->first->next->next && !p_bridge->first->next->next->next) { + p_bridge->first->next->next->write_p = 0; + p_bridge->first->next->next->min_space = 0; + memset(p_bridge->first->next->next->buffer, silence, sizeof((*memberp)->buffer)); +#else + if (p_bridge->first->next && !p_bridge->first->next->next) { +#endif + p_bridge->first->next->write_p = 0; + p_bridge->first->next->min_space = 0; + memset(p_bridge->first->next->buffer, silence, sizeof((*memberp)->buffer)); + p_bridge->first->write_p = 0; + p_bridge->first->min_space = 0; + memset(p_bridge->first->buffer, silence, sizeof((*memberp)->buffer)); + memset(p_bridge->sum_buffer, 0, sizeof(p_bridge->sum_buffer)); + p_bridge->read_p = 0; + add_timer(&p_bridge->timer, bridge_timeout, p_bridge, 0); + schedule_timer(&p_bridge->timer, 0, 20000); /* 20 MS */ + p_bridge->sample_count = 0; + PDEBUG(DEBUG_PORT, "bridge %u became a conference\n", p_bridge->bridge_id); } - - PERROR("Bridge ID %u cannot be joined by port %u, because it is already occupied by ports %u and %u.\n", p_bridge->bridge_id, p_serial, p_bridge->sunrise->p_serial, p_bridge->sunset->p_serial); - p_bridge = NULL; } -/* send data to remote Port */ +/* send data to remote Port or add to sum buffer */ int Port::bridge_tx(unsigned char *data, int len) { - class Port *to_port = NULL; - - /* get remote port from bridge */ - if (!p_bridge) + int write_p, space; + struct port_bridge_member *member; + signed long *sum; + unsigned char *buf; + +#ifdef WITH_VOOTP + if (p_vootp) + vootp_encrypt_stream(p_vootp, data, len); +#endif + + /* less than two ports, so drop */ + if (!p_bridge || !p_bridge->first || !p_bridge->first->next) + return -EIO; +#ifndef TEST_CONFERENCE + /* two ports, so bridge */ + if (!p_bridge->first->next->next) { + if (p_bridge->first->port == this) + return p_bridge->first->next->port->bridge_rx(data, len); + if (p_bridge->first->next->port == this) + return p_bridge->first->port->bridge_rx(data, len); return -EINVAL; - if (p_bridge->sunrise == this) - to_port = p_bridge->sunset; - if (p_bridge->sunset == this) - to_port = p_bridge->sunrise; - if (!to_port) + } +#endif + /* more than two ports... */ + member = p_bridge->first; + while (member) { + if (member->port == this) + break; + member = member->next; + } + if (!member) return -EINVAL; + write_p = member->write_p; + /* calculate space, so write pointer will not overrun (or reach) read pointer in ring buffer */ + space = (p_bridge->read_p - write_p - 1) & (BRIDGE_BUFFER - 1); + /* clip len, if it does not fit */ + if (space < len) + len = space; + /* apply audio samples to sum buffer */ + sum = p_bridge->sum_buffer; + buf = member->buffer; + while (len--) { + sum[write_p] += audio_law_to_s32[*data]; + buf[write_p] = *data++; + write_p = (write_p + 1) & (BRIDGE_BUFFER - 1); + } + /* raise write pointer */ + member->write_p = write_p; + + return 0; +} + +int bridge_timeout(struct lcr_timer *timer, void *instance, int index) +{ + struct port_bridge *bridge = (struct port_bridge *)instance; + struct port_bridge_member *member = bridge->first; + unsigned long long timer_time; + signed long *sum, sample; + unsigned char buffer[160], *buf, *d; + int i, read_p, space; + + bridge->sample_count += 160; + + /* schedule exactly 20ms from last schedule */ + timer_time = timer->timeout.tv_sec * MICRO_SECONDS + timer->timeout.tv_usec; + timer_time += 20000; /* 20 MS */ + timer->timeout.tv_sec = timer_time / MICRO_SECONDS; + timer->timeout.tv_usec = timer_time % MICRO_SECONDS; + timer->active = 1; + + while (member) { + /* calculate transmit data */ + read_p = bridge->read_p; + sum = bridge->sum_buffer; + buf = member->buffer; + d = buffer; + for (i = 0; i < 160; i++) { + sample = sum[read_p]; + sample -= audio_law_to_s32[buf[read_p]]; + buf[read_p] = silence; + if (sample < SHORT_MIN) sample = SHORT_MIN; + if (sample > SHORT_MAX) sample = SHORT_MAX; + *d++ = audio_s16_to_law[sample & 0xffff]; + read_p = (read_p + 1) & (BRIDGE_BUFFER - 1); + } + /* send data */ + member->port->bridge_rx(buffer, 160); + /* raise write pointer, if read pointer would overrun them */ + space = ((member->write_p - bridge->read_p) & (BRIDGE_BUFFER - 1)) - 160; + if (space < 0) { + space = 0; + member->write_p = read_p; +// PDEBUG(DEBUG_PORT, "bridge %u member %d has buffer underrun\n", bridge->bridge_id, member->port->p_serial); + } + /* find minimum delay */ + if (space < member->min_space) + member->min_space = space; + /* check if we should reduce buffer */ + if (bridge->sample_count >= 8000*5) { + /* reduce buffer by minimum delay */ +// PDEBUG(DEBUG_PORT, "bridge %u member %d has min space of %d samples\n", bridge->bridge_id, member->port->p_serial, member->min_space); + member->write_p = (member->write_p - member->min_space) & (BRIDGE_BUFFER - 1); + member->min_space = 1000000; /* infinite */ + } + member = member->next; + } + + /* clear sample data */ + read_p = bridge->read_p; + sum = bridge->sum_buffer; + for (i = 0; i < 160; i++) { + sum[read_p] = 0; + read_p = (read_p + 1) & (BRIDGE_BUFFER - 1); + } + + /* raise read pointer */ + bridge->read_p = read_p; + + if (bridge->sample_count >= 8000*5) + bridge->sample_count = 0; -// printf("Traffic: %u -> %u (bridge %u)\n", p_serial, to_port->p_serial, p_bridge->bridge_id); - return to_port->bridge_rx(data, len); + return 0; } -/* receive data from remote Port (dummy, needs to be inherited) */ + +/* receive data from remote Port */ int Port::bridge_rx(unsigned char *data, int len) { - return 0; /* datenklo */ + +#ifdef WITH_VOOTP + if (p_vootp) + vootp_decrypt_stream(p_vootp, data, len); +#endif + + return 0; +} + +#ifdef WITH_VOOTP +static void vootp_info(void *priv, const char *text) +{ + class Port *port = (class Port *)priv; + char display[strlen(text) + 1]; + + SCPY(display, text); + if (display[0]) + display[strlen(display) - 1] = '\0'; + + port->set_display(display); } +void Port::set_vootp(struct param_vootp *vootp) +{ + if (p_vootp) { + vootp_destroy(p_vootp); + p_vootp = NULL; + } + if (vootp->enable) { + p_vootp = vootp_create(this, (options.law=='a'), options.otp_dir, NULL, NULL, vootp->id, vootp_info); +// vootp_loglevel(VOOTP_LOGL_DEBUG); + if (!p_vootp) { + struct lcr_msg *message; + + message = message_create(p_serial, p_epointlist->epoint_id, PORT_TO_EPOINT, MESSAGE_VOOTP); + message->param.vootp.failed = 1; + message_put(message); + } + } +} +#endif