399 lines
12 KiB
C
399 lines
12 KiB
C
|
|
#include "chan_capi_platform.h"
|
|
#include "chan_capi20.h"
|
|
#include "chan_capi.h"
|
|
#include "chan_capi_utils.h"
|
|
|
|
#include "platform.h"
|
|
#include "diva_streaming_result.h"
|
|
#include "diva_streaming_messages.h"
|
|
#include "diva_streaming_vector.h"
|
|
#include "diva_streaming_manager.h"
|
|
#include "chan_capi_divastreaming_utils.h"
|
|
|
|
/*
|
|
LOCALS
|
|
*/
|
|
static int diva_streaming_disabled;
|
|
AST_MUTEX_DEFINE_STATIC(stream_write_lock);
|
|
|
|
static diva_entity_queue_t diva_streaming_new; /* protected by stream_write_lock, new streams */
|
|
|
|
int capi_DivaStreamingSupported (unsigned controller)
|
|
{
|
|
MESSAGE_EXCHANGE_ERROR error;
|
|
int waitcount = 50;
|
|
unsigned char manbuf[CAPI_MANUFACTURER_LEN];
|
|
_cmsg CMSG;
|
|
int ret = 0;
|
|
|
|
if (capi20_get_manufacturer(controller, manbuf) == NULL) {
|
|
goto done;
|
|
}
|
|
if ((strstr((char *)manbuf, "Eicon") == 0) &&
|
|
(strstr((char *)manbuf, "Dialogic") == 0)) {
|
|
goto done;
|
|
}
|
|
|
|
error = capi_sendf (NULL, 0, CAPI_MANUFACTURER_REQ, controller, get_capi_MessageNumber(),
|
|
"dw(bs)", _DI_MANU_ID, _DI_STREAM_CTRL, 2, "");
|
|
|
|
if (error)
|
|
goto done;
|
|
|
|
while (waitcount) {
|
|
error = capidev_check_wait_get_cmsg(&CMSG);
|
|
|
|
if (IS_MANUFACTURER_CONF(&CMSG) && (CMSG.ManuID == _DI_MANU_ID) &&
|
|
((CMSG.Class & 0xffff) == _DI_STREAM_CTRL)) {
|
|
error = (MESSAGE_EXCHANGE_ERROR)(CMSG.Class >> 16);
|
|
ret = (error == 0);
|
|
break;
|
|
}
|
|
usleep(30000);
|
|
waitcount--;
|
|
}
|
|
|
|
done:
|
|
return ret;
|
|
}
|
|
|
|
static int divaStreamingMessageRx (void* user_context, dword message, dword length, const struct _diva_streaming_vector* v, dword nr_v)
|
|
{
|
|
diva_stream_scheduling_entry_t* pE = (diva_stream_scheduling_entry_t*)user_context;
|
|
dword message_type = (message & 0xff);
|
|
|
|
if (message_type == 0) { /* IDI message */
|
|
dword offset = 0;
|
|
diva_streaming_vector_t vind[8];
|
|
byte Ind = 0;
|
|
int vind_nr = 0;
|
|
int process_indication;
|
|
|
|
do {
|
|
vind_nr = sizeof(vind)/sizeof(vind[0]);
|
|
offset = diva_streaming_get_indication_data (offset, message, length, v, nr_v, &Ind, vind, &vind_nr);
|
|
|
|
process_indication = (diva_streaming_idi_supported_ind (Ind, vind_nr != 0, vind_nr != 0 ? (byte*)vind->data : (byte*)"") != 0);
|
|
|
|
if (likely(process_indication != 0)) {
|
|
if (likely(Ind == 8)) {
|
|
if (likely(pE->i != 0 && pE->i->NCCI != 0)) {
|
|
if (pE->i->bridgePeer != 0) {
|
|
struct capi_pvt* bridgePeer = pE->i->bridgePeer;
|
|
if (bridgePeer->NCCI != 0 && bridgePeer->diva_stream_entry != 0 &&
|
|
bridgePeer->diva_stream_entry->diva_stream_state == DivaStreamActive &&
|
|
bridgePeer->diva_stream_entry->diva_stream->get_tx_free (bridgePeer->diva_stream_entry->diva_stream) > 2*CAPI_MAX_B3_BLOCK_SIZE+128) {
|
|
dword i = 0, k = 0, b3len;
|
|
byte b3buf[CAPI_MAX_B3_BLOCK_SIZE];
|
|
b3len = diva_streaming_read_vector_data(vind, vind_nr, &i, &k, b3buf, CAPI_MAX_B3_BLOCK_SIZE);
|
|
bridgePeer->diva_stream_entry->diva_stream->write (bridgePeer->diva_stream_entry->diva_stream,
|
|
8U << 8 | DIVA_STREAM_MESSAGE_TX_IDI_REQUEST,
|
|
b3buf, b3len);
|
|
bridgePeer->diva_stream_entry->diva_stream->flush_stream(bridgePeer->diva_stream_entry->diva_stream);
|
|
}
|
|
} else {
|
|
capidev_handle_data_b3_indication_vector (pE->i, vind, vind_nr);
|
|
}
|
|
}
|
|
} else {
|
|
dword i = 0, k = 0;
|
|
word data_length;
|
|
byte ind_data_buffer[2048+512];
|
|
data_length = (word)diva_streaming_read_vector_data (vind, vind_nr, &i, &k, ind_data_buffer, sizeof(ind_data_buffer));
|
|
|
|
DBG_TRC(("Ind: %02x length:%u", Ind, data_length))
|
|
}
|
|
}
|
|
} while (offset != 0);
|
|
} else if (message_type == 0xff) { /* System message */
|
|
switch ((byte)(message >> 8)) {
|
|
case DIVA_STREAM_MESSAGE_INIT: /* Stream active */
|
|
if (pE->PLCI == 0 && pE->i != 0) {
|
|
pE->PLCI = pE->i->PLCI;
|
|
}
|
|
cc_verbose(3, 0, VERBOSE_PREFIX_2 "%s: stream active (PLCI=%#x)\n", pE->vname, pE->PLCI);
|
|
if (pE->diva_stream_state == DivaStreamCreated) {
|
|
pE->diva_stream_state = DivaStreamActive;
|
|
} else if (pE->diva_stream_state == DivaStreamCancelSent) {
|
|
pE->diva_stream->release_stream(pE->diva_stream);
|
|
pE->i = 0;
|
|
pE->diva_stream_state = DivaStreamDisconnectSent;
|
|
}
|
|
break;
|
|
|
|
case DIVA_STREAM_MESSAGE_RX_TX_ACK: /* Resolved Tx flow control */
|
|
/* cc_verbose(4, 1, "%s: stream tx ack (PLCI=%#x)\n", pE->vname, pE->PLCI); */
|
|
break;
|
|
|
|
case DIVA_STREAM_MESSAGE_SYNC_ACK: /* Sync ack request acknowledge */
|
|
cc_verbose(4, 1, "%s: stream sync ack (PLCI=%#x, sequence=%08x)\n", pE->vname, pE->PLCI, length);
|
|
break;
|
|
|
|
case DIVA_STREAM_MESSAGE_RELEASE_ACK: /* Release stream acknowledge */
|
|
pE->diva_stream_state = DivaStreamDisconnected;
|
|
pE->diva_stream = 0;
|
|
cc_verbose(3, 0, VERBOSE_PREFIX_2 "%s: stream released (PLCI=%#x)\n", pE->vname, pE->PLCI);
|
|
break;
|
|
|
|
case DIVA_STREAM_MESSAGE_INIT_ERROR: /* Failed to initialize stream */
|
|
pE->diva_stream_state = DivaStreamDisconnected;
|
|
pE->diva_stream = 0;
|
|
cc_verbose(3, 0, VERBOSE_PREFIX_2 "%s: stream init error (PLCI=%#x, error=%d)\n", pE->vname, pE->PLCI, length);
|
|
break;
|
|
|
|
default:
|
|
cc_verbose(3, 0, VERBOSE_PREFIX_2 "%s: stream unknown system message (PLCI=%#x, message=%08x)\n", pE->vname, pE->PLCI, message);
|
|
break;
|
|
}
|
|
} else {
|
|
cc_verbose(3, 0, VERBOSE_PREFIX_2 "%s: unknown stream message (PLCI=%#x, message=%08x)\n", pE->vname, pE->PLCI, message);
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Create Diva stream
|
|
*
|
|
*/
|
|
void capi_DivaStreamingOn(struct capi_pvt *i, byte streamCommand, _cword messageNumber)
|
|
{
|
|
diva_stream_scheduling_entry_t* pE;
|
|
int ret;
|
|
char trace_ident[8];
|
|
unsigned int effectivePLCI;
|
|
|
|
if (diva_streaming_disabled)
|
|
return;
|
|
|
|
pE = ast_malloc (sizeof(*pE));
|
|
if (pE == 0)
|
|
return;
|
|
|
|
snprintf (trace_ident, sizeof(trace_ident), "C%02x", (byte)i->PLCI);
|
|
trace_ident[sizeof(trace_ident)-1] = 0;
|
|
|
|
cc_mutex_lock(&stream_write_lock);
|
|
|
|
ret = diva_stream_create (&pE->diva_stream, NULL, 255, divaStreamingMessageRx, pE, trace_ident);
|
|
|
|
if (ret == 0) {
|
|
byte* description = (byte*)pE->diva_stream->description (pE->diva_stream);
|
|
MESSAGE_EXCHANGE_ERROR error;
|
|
|
|
description[1] = streamCommand;
|
|
|
|
description[3] |= 0x01;
|
|
|
|
#if 0 /*! \todo create function to add info elements to stream description */
|
|
byte* start = &description[3];
|
|
|
|
start[0] |= 2U;
|
|
start = start + start[-1];
|
|
*start++ = 0x2c;
|
|
*start++ = 8;
|
|
*start++ = 1;
|
|
*start++ = 2;
|
|
*start++ = 3;
|
|
*start++ = 4;
|
|
*start++ = 'a';
|
|
*start++ = 'b';
|
|
*start++ = 'c';
|
|
*start++ = 'd';
|
|
*start++ = 0;
|
|
|
|
description[2] += 11;
|
|
description[0] += 11;
|
|
#endif
|
|
|
|
if (streamCommand == 0) {
|
|
messageNumber = get_capi_MessageNumber();
|
|
effectivePLCI = i->PLCI;
|
|
} else {
|
|
/*
|
|
PLCI still not assigned. Send to controller and tag with message number
|
|
where command receives effective
|
|
*/
|
|
effectivePLCI = i->controller;
|
|
}
|
|
|
|
error = capi_sendf (NULL, 0, CAPI_MANUFACTURER_REQ, effectivePLCI, messageNumber,
|
|
"dws", _DI_MANU_ID, _DI_STREAM_CTRL, description);
|
|
if (error == 0) {
|
|
pE->diva_stream_state = DivaStreamCreated;
|
|
pE->PLCI = i->PLCI;
|
|
pE->i = i;
|
|
i->diva_stream_entry = pE;
|
|
memcpy (pE->vname, i->vname, MIN(sizeof(pE->vname), sizeof(i->vname)));
|
|
pE->vname[sizeof(pE->vname)-1] = 0;
|
|
pE->rx_flow_control = 0;
|
|
pE->tx_flow_control = 0;
|
|
diva_q_add_tail (&diva_streaming_new, &pE->link);
|
|
} else {
|
|
pE->diva_stream->release (pE->diva_stream);
|
|
ast_free (pE);
|
|
}
|
|
}
|
|
|
|
cc_mutex_unlock(&stream_write_lock);
|
|
}
|
|
|
|
/*
|
|
* Remove stream info
|
|
*
|
|
* To remove stream from one active connection:
|
|
* remove stream info
|
|
* disconnect B3
|
|
* remove stream
|
|
* select_b
|
|
*/
|
|
void capi_DivaStreamingRemoveInfo(struct capi_pvt *i)
|
|
{
|
|
byte description[] = { 2, 0, 0 };
|
|
MESSAGE_EXCHANGE_ERROR error;
|
|
int send;
|
|
|
|
cc_mutex_lock(&stream_write_lock);
|
|
send = i->diva_stream_entry != 0;
|
|
cc_mutex_unlock(&stream_write_lock);
|
|
|
|
if (send != 0)
|
|
error = capi_sendf (NULL, 0, CAPI_MANUFACTURER_REQ, i->PLCI, get_capi_MessageNumber(),
|
|
"dws", _DI_MANU_ID, _DI_STREAM_CTRL, description);
|
|
}
|
|
|
|
/*!
|
|
* \brief Send empty stream to inform no Diva streaming is used for this PLCI
|
|
*/
|
|
void capi_DivaStreamingStreamNotUsed(struct capi_pvt *i, byte streamCommand, _cword messageNumber)
|
|
{
|
|
byte description[] = { 0x04, 0x00, 0x02, 0x00, 0x00 };
|
|
unsigned int effectivePLCI;
|
|
MESSAGE_EXCHANGE_ERROR error;
|
|
|
|
description[1] = streamCommand;
|
|
|
|
if (streamCommand == 0) {
|
|
messageNumber = get_capi_MessageNumber();
|
|
effectivePLCI = i->PLCI;
|
|
} else {
|
|
/*
|
|
PLCI still not assigned. Send to controller and tag with message number
|
|
where command receives effective
|
|
*/
|
|
effectivePLCI = i->controller;
|
|
}
|
|
|
|
error = capi_sendf (NULL, 0, CAPI_MANUFACTURER_REQ, effectivePLCI, messageNumber,
|
|
"dws", _DI_MANU_ID, _DI_STREAM_CTRL, description);
|
|
}
|
|
|
|
void capi_DivaStreamingRemove(struct capi_pvt *i)
|
|
{
|
|
diva_stream_scheduling_entry_t* pE = i->diva_stream_entry;
|
|
int send_cancel = 0;
|
|
|
|
cc_mutex_lock(&stream_write_lock);
|
|
pE = i->diva_stream_entry;
|
|
if (pE != 0) {
|
|
i->diva_stream_entry = 0;
|
|
pE->i = 0;
|
|
if (pE->diva_stream_state == DivaStreamCreated) {
|
|
|
|
if (i->NCCI != 0) {
|
|
/*
|
|
If NCCI is not sen then this is no possibility to send cancel request
|
|
to queued in the IDI L2 streaming info. But in user mode this is OK,
|
|
if removing PLCI CAPI removes networking entity and this operation
|
|
causes cancellation of create streaming request.
|
|
Timeout is only for the rare case where create streaming request was newer
|
|
sent to hardware.
|
|
*/
|
|
send_cancel = 1;
|
|
}
|
|
pE->diva_stream_state = DivaStreamCancelSent;
|
|
pE->cancel_start = time(NULL) + 5;
|
|
DBG_LOG(("stream cancelled [%p]", pE->diva_stream))
|
|
} else if (pE->diva_stream_state == DivaStreamActive) {
|
|
pE->diva_stream->release_stream(pE->diva_stream);
|
|
pE->diva_stream_state = DivaStreamDisconnectSent;
|
|
}
|
|
}
|
|
cc_mutex_unlock(&stream_write_lock);
|
|
|
|
if (send_cancel != 0) {
|
|
static byte data[] = { 0x8 /* CONTROL */, 0x01 /* CANCEL */};
|
|
MESSAGE_EXCHANGE_ERROR error;
|
|
|
|
error = capi_sendf(NULL, 0, CAPI_DATA_B3_REQ, i->NCCI, get_capi_MessageNumber(),
|
|
"dwww", data, sizeof(data), 0, 1U << 4);
|
|
if (likely(error == 0)) {
|
|
cc_mutex_lock(&i->lock);
|
|
i->B3count++;
|
|
cc_mutex_unlock(&i->lock);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
This is only one used to access streaming scheduling list routine.
|
|
This ensures list can be accessed w/o anly locks
|
|
|
|
To ensure exclusive access scheduling queue is static function variable
|
|
*/
|
|
void divaStreamingWakeup (void)
|
|
{
|
|
static diva_entity_queue_t active_streams;
|
|
diva_entity_link_t* link;
|
|
time_t current_time = time (NULL);
|
|
|
|
cc_mutex_lock(&stream_write_lock);
|
|
while ((link = diva_q_get_head (&diva_streaming_new)) != 0) {
|
|
diva_stream_scheduling_entry_t* pE = DIVAS_CONTAINING_RECORD(link, diva_stream_scheduling_entry_t, link);
|
|
diva_q_remove (&diva_streaming_new, &pE->link);
|
|
diva_q_add_tail (&active_streams, &pE->link);
|
|
}
|
|
cc_mutex_unlock(&stream_write_lock);
|
|
|
|
for (link = diva_q_get_head (&active_streams); likely(link != 0);) {
|
|
diva_entity_link_t* next = diva_q_get_next(link);
|
|
diva_stream_scheduling_entry_t* pE = DIVAS_CONTAINING_RECORD(link, diva_stream_scheduling_entry_t, link);
|
|
|
|
cc_mutex_lock(&stream_write_lock);
|
|
pE->diva_stream->wakeup (pE->diva_stream);
|
|
if (unlikely(pE->diva_stream_state == DivaStreamCancelSent && pE->cancel_start < current_time)) {
|
|
DBG_LOG(("stream reclaimed [%p]", pE->diva_stream))
|
|
pE->diva_stream->release (pE->diva_stream);
|
|
pE->diva_stream_state = DivaStreamDisconnected;
|
|
pE->diva_stream = 0;
|
|
}
|
|
|
|
if (unlikely(pE->diva_stream == 0)) {
|
|
diva_q_remove (&active_streams, &pE->link);
|
|
if (pE->i != 0) {
|
|
pE->i->diva_stream_entry = 0;
|
|
}
|
|
ast_free (pE);
|
|
}
|
|
cc_mutex_unlock(&stream_write_lock);
|
|
|
|
link = next;
|
|
}
|
|
}
|
|
|
|
void capi_DivaStreamLock (void)
|
|
{
|
|
cc_mutex_lock(&stream_write_lock);
|
|
}
|
|
|
|
void capi_DivaStreamUnLock (void)
|
|
{
|
|
cc_mutex_unlock(&stream_write_lock);
|
|
}
|
|
|
|
void capi_DivaStreamingDisable (void) {
|
|
diva_streaming_disabled = 1;
|
|
}
|
|
|