443 lines
10 KiB
Plaintext
443 lines
10 KiB
Plaintext
/*****************************************************************************
|
|
* libhpsangoma.c: Sangoma High Performance TDM API - Span Based Library
|
|
*
|
|
* Author(s): Nenad Corbic <ncorbic@sangoma.com>
|
|
*
|
|
* Copyright: (c) 2008 Nenad Corbic <ncorbic@sangoma.com>
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version
|
|
* 2 of the License, or (at your option) any later version.
|
|
* ============================================================================
|
|
*
|
|
*/
|
|
|
|
#include "libhpsangoma.h"
|
|
#include "libhpsangoma_priv.h"
|
|
|
|
/*---------------------------------------------------------
|
|
PRIVATE STRUCTURES
|
|
----------------------------------------------------------*/
|
|
|
|
void (*lib_log)(int level, FILE *fp, char *file, const char *func, int line, char *fmt, ...)=NULL;
|
|
|
|
|
|
/*!
|
|
\brief Channel Method: User tx a chunk into a channel
|
|
\param chan channel object
|
|
\param data pointer to user voice chunk
|
|
\param len size of user voice chunk
|
|
\return -1 = packet too large, -2 = channel closed, 1 = busy, 0 = tx ok
|
|
*/
|
|
|
|
static int sangoma_hp_tdm_chan_push(struct sangoma_hptdm_chan *chan, char *data, int len)
|
|
{
|
|
hp_tmd_chunk_t *tx_chunk;
|
|
int free_slots;
|
|
|
|
if (!chan->init) {
|
|
return -2;
|
|
}
|
|
|
|
if (len >= SMG_HP_MAX_CHAN_DATA) {
|
|
/* Data Too Big */
|
|
lib_printf(0,NULL,"chan_push c%i tx chunk len %i too big\n",
|
|
chan->span_no+1,chan->chan_no+1,chan->tx_idx_in,len);
|
|
return -1;
|
|
}
|
|
|
|
/* Lock */
|
|
if (chan->tx_idx_in >= chan->tx_idx_out) {
|
|
free_slots=SMG_HP_TDM_CHUNK_IDX_SZ-(chan->tx_idx_in-chan->tx_idx_out);
|
|
} else {
|
|
free_slots=chan->tx_idx_out-chan->tx_idx_in;
|
|
}
|
|
/* Un Lock */
|
|
|
|
if (!free_slots) {
|
|
/* We have just overruned the tx buffer */
|
|
lib_printf(0,NULL,"chan_push c%i failed no free slots in %i out %i\n",
|
|
chan->span_no+1,chan->chan_no+1, chan->tx_idx_in,chan->tx_idx_out);
|
|
return 1;
|
|
}
|
|
|
|
tx_chunk = &chan->tx_idx[chan->tx_idx_in];
|
|
if (tx_chunk->init) {
|
|
/* This should NEVER happen the chunk should be free */
|
|
lib_printf(15,NULL,"chan_push s%ic%i tx chunk overrun in %i \n",
|
|
chan->span_no+1,chan->chan_no+1,chan->tx_idx_in);
|
|
return 1;
|
|
}
|
|
|
|
memset(tx_chunk,0,sizeof(hp_tmd_chunk_t));
|
|
memcpy(&tx_chunk->data,data,len);
|
|
tx_chunk->len=len;
|
|
tx_chunk->init=1;
|
|
|
|
lib_printf(15,NULL,"chan_push s%ic%i tx chunk in %i \n",
|
|
chan->span_no+1,chan->chan_no+1,chan->tx_idx_in);
|
|
|
|
chan->tx_idx_in++;
|
|
if (chan->tx_idx_in >= SMG_HP_TDM_CHUNK_IDX_SZ) {
|
|
chan->tx_idx_in=0;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*-------------------------------------------------
|
|
Internal Span Methods
|
|
-------------------------------------------------*/
|
|
|
|
|
|
/*!
|
|
\brief Span Method: Open a channel inside of span
|
|
\param span span object
|
|
\param cfg channel registration structure
|
|
\param chan_no channel number
|
|
\param chan_ptr pass user the channel pointer reference
|
|
\return 0 = pass, non zero fail
|
|
*/
|
|
|
|
static int sangoma_hp_tdm_open_chan(sangoma_hptdm_span_t *span,
|
|
sangoma_hptdm_chan_reg_t *cfg,
|
|
unsigned int chan_no,
|
|
sangoma_hptdm_chan_t **chan_ptr)
|
|
{
|
|
sangoma_hptdm_chan_t *chan;
|
|
|
|
if (!span->init) {
|
|
return -1;
|
|
}
|
|
|
|
if (chan_no >= SMG_HP_TDM_MAX_CHANS) {
|
|
lib_printf(0,NULL,"open_chan failed chan_no %i >= max chans %i\n",
|
|
chan_no, SMG_HP_TDM_MAX_CHANS);
|
|
return -1;
|
|
}
|
|
|
|
if (!cfg->rx_data || !cfg->p) {
|
|
return -1;
|
|
}
|
|
|
|
if (span->chan_idx[chan_no].chan_no_hw < 0) {
|
|
lib_printf(0,NULL,"open_chan failed chan_no s%ic%i is not mapped to hardware\n",
|
|
span->span_no+1,chan_no+1);
|
|
return -1;
|
|
}
|
|
|
|
chan = &span->chan_idx[chan_no].chan;
|
|
if (chan->init) {
|
|
/* Chan Busy */
|
|
lib_printf(0,NULL,"open_chan failed chan_no s%ic%i is busy\n",
|
|
span->span_no+1,chan_no+1);
|
|
return 1;
|
|
}
|
|
|
|
memset(chan,0,sizeof(sangoma_hptdm_chan_t));
|
|
|
|
chan->chan_no = chan_no;
|
|
chan->span = span;
|
|
memcpy(&chan->chan_reg, cfg, sizeof(sangoma_hptdm_chan_reg_t));
|
|
|
|
chan->push = sangoma_hp_tdm_chan_push;
|
|
|
|
chan->init=1;
|
|
lib_printf(15,NULL,"open_chan chan_no s%ic%i ok\n",
|
|
span->span_no+1,chan_no+1);
|
|
|
|
*chan_ptr = chan;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
/*!
|
|
\brief Span Method: Close channel
|
|
\param chan channel object
|
|
\return 0 = pass, non zero fail
|
|
*/
|
|
|
|
static int sangoma_hp_tdm_close_chan(sangoma_hptdm_chan_t *chan)
|
|
{
|
|
chan->init=0;
|
|
chan->chan_reg.p=NULL;
|
|
lib_printf(15,NULL,"close_chan chan_no s%ic%i ok\n",
|
|
chan->span_no+1,chan->chan_no+1);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*!
|
|
\brief Span Method: Test if channel is closed
|
|
\param chan channel object
|
|
\return 0 = channel is NOT closed, non zero channel IS closed
|
|
*/
|
|
static int sangoma_hp_tdm_is_chan_closed (sangoma_hptdm_chan_t *chan)
|
|
{
|
|
return (chan->init == 0) ? 1:0;
|
|
}
|
|
|
|
/*!
|
|
\brief Span Method: Close span
|
|
\param span span object
|
|
\return 0 = pass, non zero fail
|
|
*/
|
|
static int sangoma_hp_tdm_close_span(sangoma_hptdm_span_t *span)
|
|
{
|
|
int i;
|
|
sangoma_hptdm_chan_t *chan=NULL;
|
|
|
|
for (i=0;i<SMG_HP_TDM_MAX_CHANS;i++) {
|
|
chan = &span->chan_idx[i].chan;
|
|
if (chan->init) {
|
|
chan->init=0;
|
|
}
|
|
}
|
|
|
|
close(span->sock);
|
|
span->sock=-1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*!
|
|
\brief Span Method: User passes cmd to the span
|
|
\param span span object
|
|
\return 0 = pass, non zero fail
|
|
*/
|
|
static int sangoma_hp_tdm_event_ctrl_span(sangoma_hptdm_span_t *span, hp_tdmapi_tx_event_t *event)
|
|
{
|
|
int err;
|
|
|
|
if (!span->init || span->sock < 0) {
|
|
return -1;
|
|
}
|
|
|
|
err = ioctl(span->sock,SIOC_WANPIPE_API,event);
|
|
if (err < 0){
|
|
lib_printf(0,NULL,"Error: SPAN %i Failed to execute event!\n",
|
|
span->span_no+1);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*!
|
|
\brief Span Method: User requests full span configuration
|
|
\param span span object
|
|
\return 0 = pass, non zero fail
|
|
*/
|
|
static int sangoma_hp_tdm_event_get_cfg(sangoma_hptdm_span_t *span, wan_if_cfg_t *if_cfg)
|
|
{
|
|
memcpy(if_cfg,&span->span_cfg,sizeof(if_cfg));
|
|
return 0;
|
|
}
|
|
|
|
/*!
|
|
\brief Span Method: handle the span
|
|
\param span span object
|
|
\return 0 = pass, non zero fail
|
|
*/
|
|
static int sangoma_hp_tdm_run_span(sangoma_hptdm_span_t *span)
|
|
{
|
|
int err=0;
|
|
fd_set sock_read,sock_write,sock_oob;
|
|
|
|
if (!span->init) {
|
|
lib_printf(0, NULL, "Span %i not initialized %i\n",span->span_no+1);
|
|
return -1;
|
|
}
|
|
|
|
lib_printf(15, NULL, "Starting RUN SPAN %i Sock=%i\n",span->span_no+1, span->sock);
|
|
|
|
if (span->sock < 0) {
|
|
err=sangoma_hptdm_span_open(span);
|
|
if (err) {
|
|
usleep(500000);
|
|
err=-2;
|
|
goto sangoma_hp_tdm_run_span_exit;
|
|
}
|
|
}
|
|
|
|
/* Initialize all select() descriptors */
|
|
FD_ZERO(&sock_read);
|
|
FD_ZERO(&sock_write);
|
|
FD_ZERO(&sock_oob);
|
|
|
|
FD_SET(span->sock,&sock_oob);
|
|
FD_SET(span->sock,&sock_read);
|
|
FD_SET(span->sock,&sock_write);
|
|
|
|
err=select(span->sock + 1,&sock_read, NULL, &sock_oob, NULL);
|
|
|
|
if (err > 0) {
|
|
|
|
if (FD_ISSET(span->sock,&sock_oob)){
|
|
err=sangoma_hp_tdm_handle_oob_event(span);
|
|
if (err) {
|
|
lib_printf(0, NULL, "RUN SPAN: %i oob err %i\n",
|
|
span->span_no+1, err);
|
|
err=-3;
|
|
goto sangoma_hp_tdm_run_span_exit;
|
|
}
|
|
}
|
|
if (FD_ISSET(span->sock,&sock_read)){
|
|
err=sangoma_hp_tdm_handle_read_event(span);
|
|
if (err) {
|
|
lib_printf(0, NULL, "RUN SPAN: %i read err %i\n",
|
|
span->span_no+1, err);
|
|
err=-4;
|
|
goto sangoma_hp_tdm_run_span_exit;
|
|
}
|
|
|
|
err=sangoma_hp_tdm_handle_write_event(span);
|
|
if (err) {
|
|
lib_printf(0, NULL, "RUN SPAN: %i write err %i\n",
|
|
span->span_no+1, err);
|
|
err=-5;
|
|
goto sangoma_hp_tdm_run_span_exit;
|
|
}
|
|
}
|
|
|
|
} else if (err==0) {
|
|
/* Timeout continue */
|
|
return 0;
|
|
|
|
} else {
|
|
/* Error */
|
|
if (errno == EAGAIN) {
|
|
goto sangoma_hp_tdm_run_span_exit;
|
|
}
|
|
|
|
err=-6;
|
|
}
|
|
|
|
sangoma_hp_tdm_run_span_exit:
|
|
|
|
if (err < 0) {
|
|
if (span->sock) {
|
|
close(span->sock);
|
|
span->sock=-1;
|
|
}
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
|
|
|
|
/*---------------------------------------------------------
|
|
PUBLIC STRUCTURES
|
|
----------------------------------------------------------*/
|
|
|
|
|
|
/*
|
|
\brief Initialize and Configure Span - private functions not to be used directly!
|
|
\param span_no span number - integer
|
|
\param cfg span registration struct
|
|
\param version library version number added by the macro
|
|
\return NULL: fail, Span Object: pass
|
|
*
|
|
* The __sangoma_hptdm_api_span_init() function must NOT be called directly!
|
|
* One MUST use defined sangoma_hptdm_api_span_init() macro instead
|
|
*/
|
|
|
|
sangoma_hptdm_span_t * __sangoma_hptdm_api_span_init(int span_no, sangoma_hptdm_span_reg_t *cfg, int version)
|
|
{
|
|
int err,i,ch=0;
|
|
sangoma_hptdm_span_t *span;
|
|
|
|
span = malloc(sizeof(sangoma_hptdm_span_t));
|
|
if (!span) {
|
|
return NULL;
|
|
}
|
|
|
|
memset(span,0,sizeof(sangoma_hptdm_span_t));
|
|
|
|
span->span_no=span_no;
|
|
sprintf(span->if_name,"w%ig1",span_no+1);
|
|
|
|
if (cfg) {
|
|
memcpy(&span->span_reg,cfg,sizeof(sangoma_hptdm_span_reg_t));
|
|
if (!lib_log) {
|
|
lib_log=cfg->log;
|
|
}
|
|
}
|
|
|
|
err=sangoma_hptdm_span_open(span);
|
|
if (err) {
|
|
free(span);
|
|
return NULL;
|
|
}
|
|
|
|
if (span->span_cfg.media == WAN_MEDIA_E1) {
|
|
span->span_cfg.active_ch = span->span_cfg.active_ch >> 1;
|
|
}
|
|
|
|
lib_printf(0,NULL,"Span %i Configuration\n",span->span_no+1);
|
|
lib_printf(0,NULL,"Used By\t:%i\n",span->span_cfg.usedby);
|
|
lib_printf(0,NULL,"Media\t:%i\n",span->span_cfg.media);
|
|
lib_printf(0,NULL,"Active Ch\t:0x%08X\n",span->span_cfg.active_ch);
|
|
lib_printf(0,NULL,"Chunk Sz\t:%i\n",span->span_cfg.chunk_sz);
|
|
lib_printf(0,NULL,"HW Coding\t:%i\n",span->span_cfg.hw_coding);
|
|
lib_printf(0,NULL,"If Number\t:%i\n",span->span_cfg.interface_number);
|
|
|
|
|
|
/* Map all channels to the actually configued on hardware */
|
|
for (i=0;i<SMG_HP_TDM_MAX_CHANS;i++) {
|
|
span->chan_idx[i].chan_no_hw=-1;
|
|
if (span->span_cfg.active_ch & (1<<i)) {
|
|
span->chan_idx[i].chan_no_hw=ch;
|
|
lib_printf(0,NULL,"Chan %i Mapped to %i",i,ch);
|
|
ch++;
|
|
span->max_chans++;
|
|
} else {
|
|
lib_printf(0,NULL,"Chan %i Not Mapped",i);
|
|
}
|
|
}
|
|
|
|
lib_printf(0,NULL,"Total Chans\t:%i\n",span->max_chans);
|
|
|
|
/* Must be configurable */
|
|
span->chunk_sz=span->span_cfg.chunk_sz;
|
|
span->tx_size=span->max_chans*span->chunk_sz;
|
|
|
|
span->init=1;
|
|
span->idle=0xFF;
|
|
|
|
span->open_chan = sangoma_hp_tdm_open_chan;
|
|
span->close_chan = sangoma_hp_tdm_close_chan;
|
|
span->is_chan_closed = sangoma_hp_tdm_is_chan_closed;
|
|
span->run_span =sangoma_hp_tdm_run_span;
|
|
span->close_span = sangoma_hp_tdm_close_span;
|
|
span->event_ctrl = sangoma_hp_tdm_event_ctrl_span;
|
|
span->get_cfg = sangoma_hp_tdm_event_get_cfg;
|
|
|
|
lib_printf(5, NULL, "Span %i Initialized\n",span->span_no+1);
|
|
|
|
return span;
|
|
}
|
|
|
|
/*
|
|
\brief Free, Un-Initialize Span
|
|
\param span_no span object
|
|
\return 0 = pass, non zero fail
|
|
*/
|
|
|
|
int sangoma_hptdm_api_span_free(sangoma_hptdm_span_t *span)
|
|
{
|
|
if (span->sock >= 0) {
|
|
span->close_span(span);
|
|
}
|
|
|
|
free(span);
|
|
span=NULL;
|
|
|
|
return 0;
|
|
}
|
|
|