blob: 2285c91833468a39c5bcac6b845313756ab0bd57 [file] [log] [blame]
/** \file sys_service.c
* sys_service.c
*
* This file implements the required system services for the API-II using a
* Linux OS running on the UVB. The user should replace the functions provided
* here with the equivalent based on their OS and hardware.
*
* Copyright (c) 2008, Zarlink Semiconductor, Inc.
*/
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/stat.h>
#include <time.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <stdlib.h> /* exit() */
#include <stdio.h>
#include "vp_api_cfg.h"
#include "vp_api_types.h"
#include "sys_service.h"
#include "vp_api_common.h"
#define CLOCKID CLOCK_REALTIME
#define SIG SIGRTMIN
#define GET_DEV_STATUS(deviceId) vpapi_dev_status[deviceId]
#define GET_LINE_STATUS(lineId) vpapi_line_status[lineId]
#define MAX_EVENT_QUEUE_SIZE 256
/* VE880 */
#if defined(ZARLINK_SLIC_VE880)
#define MAX_DEVICES 2
#define MAX_LINES 4
/* VE792 */
#elif defined(ZARLINK_SLIC_VE792)
#define MAX_DEVICES 4
#define MAX_LINES 32
#endif
static const key_t key = 0xADEF;
static const int flagMakeSem = S_IRUSR | S_IWUSR | S_IROTH | S_IWOTH | IPC_CREAT | IPC_EXCL;
static const int flagGetSem = S_IRUSR | S_IWUSR | S_IROTH | S_IWOTH;
static const int NUM_SEMS = 1; /*VP_NUM_DEVICES * VP_NUM_CRITICAL_SEC_TYPES;*/
static int critDepth = 0;
/*
* VPD socket file descriptor: This is opened only once, the first time it is
* needed. It is closed automatically by the OS when the process exits. This
* policy is to avoid the overhead of reopening the socket each time a VpSys...()
* function is called.
*/
static int vpd_fd = -1;
/* Enumurators */
typedef struct {
unsigned char valid; /* valid event */
VpEventType vp_event;
} vpapi_event;
timer_t timerid;
pthread_mutex_t vp_lock;
unsigned int event_count;
vpapi_event event_queue[MAX_EVENT_QUEUE_SIZE];
volatile unsigned int next_event = 0, curr_event = 0;
extern VpDevCtxType pDevCtx[MAX_DEVICES];
extern unsigned char vpapi_dev_status[MAX_DEVICES];
extern unsigned char vpapi_line_status[MAX_LINES];
/*
* VpSysWait() function implementation is needed only for CSLAC devices
* (880, 790). For other devices this function could be commented.
*/
void
VpSysWait(
uint8 time) /* Time specified in increments of 125uS (e.g. 4 = 500uS) */
{
usleep(125 * time);
}
void VpSysSemaphoreInit(int semaphoreId)
{
int i;
int rc;
unsigned short initialValues[NUM_SEMS];
union semun semArg;
pid_t pid = 0;
#ifdef SYS_SERVICE_DEBUG
pid = getpid();
#endif
for (i=0; i < NUM_SEMS; i++) {
initialValues[i] = 1;
}
semArg.array = initialValues;
dprintf("%u:initing semaphore key=0x%X, id=0x%X...",pid, key, semaphoreId);
if ((rc = semctl(semaphoreId, 0, SETALL, semArg)) == -1) {
#ifdef SYS_SERVICE_DEBUG
perror("semctl SETALL");
#endif
exit(1);
}
}
/*
* VpSysEnterCritical(), VpSysExitCritical():
*
* These functions allow for disabling interrupts while executing nonreentrant
* portions of VoicePath API code. Note that the following implementations of
* enter/exit critical section functions are simple implementations. These
* functions could be expanded (if required) to handle different critical
* section types differently.
*
* Params:
* VpDeviceIdType deviceId: Device Id (chip select ID)
* VpCriticalSecType: Critical section type
*
* Return:
* Number of critical sections currently entered for the device.
*/
uint8
VpSysEnterCritical(
VpDeviceIdType deviceId,
VpCriticalSecType criticalSecType)
{
int rc;
int semaphoreId;
struct sembuf semOp[1];
pid_t pid = 0;
#ifdef SYS_SERVICE_DEBUG
pid = getpid();
#endif
/* semOp[0].sem_num = deviceId*VP_NUM_CRITICAL_SEC_TYPES + criticalSecType; */
semOp[0].sem_num = 0;
semOp[0].sem_op = -1;
semOp[0].sem_flg = SEM_UNDO;
critDepth++;
if (critDepth == 1) {
if ((semaphoreId = semget(key, NUM_SEMS, flagMakeSem)) >= 0) {
/* we just created the semaphore and it needs to be inited */
VpSysSemaphoreInit(semaphoreId);
dprintf("%u:Made new semaphore %d (hopefully sem_val=1)\n", pid, semaphoreId);
} else {
/* semaphore already exists, we just need to get an id */
if ((semaphoreId = semget(key, NUM_SEMS, flagGetSem)) < 0) {
/* something has gone sour */
#ifdef SYS_SERVICE_DEBUG
perror("semget flagGetSem");
#endif
exit(1);
}
/* dprintf("%u:Got existing semaphore %d\n",pid, semaphoreId); */
}
/* dprintf("%u:?v,d=%d\n",pid); */
if ((rc = semop(semaphoreId, semOp, 1)) < 0) {
#ifdef SYS_SERVICE_DEBUG
perror("semop -1");
#endif
exit(1);
}
if (rc) {
dprintf("semaphore error with deviceId=0x%X, criticalSecType=0x%X\n",
deviceId, criticalSecType);
return -1;
}
}
dprintf("%u:v,d=%d\n",pid, critDepth);
return critDepth;
} /* VpSysEnterCritical() */
uint8
VpSysExitCritical(
VpDeviceIdType deviceId,
VpCriticalSecType criticalSecType)
{
int rc;
int semaphoreId;
struct sembuf semOp[1];
pid_t pid = 0;
#ifdef SYS_SERVICE_DEBUG
pid = getpid();
#endif
/* semOp[0].sem_num = deviceId*VP_NUM_CRITICAL_SEC_TYPES + criticalSecType; */
semOp[0].sem_num = 0;
semOp[0].sem_op = 1;
semOp[0].sem_flg = SEM_UNDO;
critDepth--;
if (critDepth == 0) {
/* we can free the semaphore now */
if ((semaphoreId = semget(key, NUM_SEMS, flagGetSem)) < 0) {
#ifdef SYS_SERVICE_DEBUG
perror("semget");
#endif
exit(1);
}
/* dprintf("%u:Got existing semaphore %d\n",pid, semaphoreId); */
dprintf("%u:^,d=%d\n",pid,critDepth);
if ((rc = semop(semaphoreId, semOp, 1)) == -1) {
#ifdef SYS_SERVICE_DEBUG
perror("semop +1");
#endif
exit(1);
}
if (rc) {
dprintf("semaphore error with deviceId=0x%X, criticalSecType=0x%X",
deviceId, criticalSecType);
}
} else {
dprintf("%u:^,d=%d\n",pid,critDepth);
}
/*dprintf("%u:VpSysExitCritical finished with critDepth=%d.\n",pid, critDepth);*/
return critDepth;
} /* VpSysExitCritical() */
/**
* VpSysDisableInt(), VpSysEnableInt(), and VpSysTestInt()
*
* These functions are used by the CSLAC device family for interrupt driven
* polling modes. These are called by the API to detect when a non-masked
* device status has changed. If using SIMPLE_POLL mode, these functions do not
* require implementation.
*
* Preconditions:
* None. The implementation of these functions is architecture dependent.
*
* Postconditions:
* VpSysDisableInt() - The interrupt associated with the deviceId passed is
* disabled.
*
* VpSysEnableInt() - The interrupt associated with the deviceId passed is
* enabled.
*
* VpSysTestInt() - The return value is TRUE if an interrupt occurred, otherwise
* return FALSE.
*
* These functions are needed only for CSLAC devices
* (880, 790). For other devices these functions could be commented.
*
*/
void
VpSysDisableInt(
VpDeviceIdType deviceId)
{
}
void
VpSysEnableInt(
VpDeviceIdType deviceId)
{
}
bool
VpSysTestInt(
VpDeviceIdType deviceId)
{
return FALSE;
}
/**
* VpSysDtmfDetEnable(), VpSysDtmfDetDisable()
*
* These functions are used by the CSLAC device family for devices that do not
* internally detect DTMF. It is used for Caller ID type-II and is provided to
* enable external DTMF detection.
*
* Preconditions:
* None. The implementation of these functions is application dependent.
*
* Postconditions:
* VpSysDtmfDetEnable() - The device/channel resource for DTMF detection is
* enabled.
*
* VpSysDtmfDetDisable() - The device/channel resource for DTMF detection is
* disabled.
*
* These functions are needed only for CSLAC devices
* (880, 790). For other devices these functions could be commented.
*
*/
void
VpSysDtmfDetEnable(
VpDeviceIdType deviceId,
uint8 channelId)
{
}
void
VpSysDtmfDetDisable(
VpDeviceIdType deviceId,
uint8 channelId)
{
}
/*
* The following functions VpSysTestHeapAcquire(), VpSysTestHeapRelease()
* VpSysPcmCollectAndProcess() and are needed only for CSLAC devices
* (880). For other devices these functions could be commented. Please see
* the LineTest API documentation for function requirements.
*/
void *
VpSysTestHeapAcquire(
uint8 *pHeapId)
{
return VP_NULL;
} /* VpSysTestHeapAcquire() */
bool
VpSysTestHeapRelease(
uint8 heapId)
{
return TRUE;
} /* VpSysTestHeapRelease() */
void
VpSysPcmCollectAndProcess(
void *pLineCtx,
VpDeviceIdType deviceId,
uint8 channelId,
uint8 startTimeslot,
uint16 operationTime,
uint16 settlingTime,
uint16 operationMask)
{
} /* VpSysPcmCollectAndProcess() */
#ifdef ENABLE_DBG_TAG
int
VpSysDebugPrintf(
const char *format, ...)
{
va_list ap;
static char buf[1024];
static uint16 indexBuf;
static bool beginLine = TRUE;
uint16 bufLen;
int retval;
if (beginLine == TRUE) {
printf("<DBG> ");
beginLine = FALSE;
}
/* print in a string to check if there's a \n */
va_start(ap, format);
retval = vsprintf(&buf[indexBuf], format, ap);
va_end(ap);
bufLen = strlen(&buf[indexBuf]);
if (buf[indexBuf + bufLen - 1] == '\n') {
buf[indexBuf + bufLen - 1] = ' ';
printf("%s</DBG>\n", buf);
indexBuf = 0;
beginLine = TRUE;
} else {
indexBuf += bufLen;
/* Just in case of a buffer overflow, not suppose to append */
if (indexBuf > 800) {
retval = printf("%s</DBG>\n", buf);
indexBuf = 0;
beginLine = TRUE;
}
}
return retval;
}
#endif /* ENABLE_DBG_TAG */
void sys_service_init()
{
pthread_mutex_init(&vp_lock, NULL);
/* Clear event queue */
memset(event_queue, 0, (MAX_EVENT_QUEUE_SIZE * sizeof(vpapi_event)));
}
static void sys_service_tick_handler()
{
unsigned char deviceId;
unsigned long flags;
vpapi_event *pEvent;
#if !defined(ZARLINK_SLIC_VE792)
bool eventStatus;
#endif
sigset_t mask;
pthread_mutex_lock(&vp_lock);
for(deviceId = 0; deviceId < MAX_DEVICES; deviceId++) {
if(GET_DEV_STATUS(deviceId) == 0)
continue;
/* Check for free resources */
if(event_count >= MAX_EVENT_QUEUE_SIZE)
goto timer_exit;
#if !defined(ZARLINK_SLIC_VE792)
if(VP_STATUS_SUCCESS == VpApiTick(&pDevCtx[deviceId], &eventStatus)) {
if(eventStatus == TRUE) {
#endif
pEvent = &event_queue[next_event];
while(VpGetEvent(&pDevCtx[deviceId], &pEvent->vp_event) == TRUE) {
if(pEvent->vp_event.status != VP_STATUS_SUCCESS) {
printf("%s: bad status(%d)\n", __func__, pEvent->vp_event.status);
break;
}
if(pEvent->vp_event.eventId == 0) {
printf("%s: warning, empty event\n", __func__);
break;
}
next_event++;
if(next_event == MAX_EVENT_QUEUE_SIZE) {
next_event = 0;
}
event_count++;
if(pEvent->valid == 0) {
pEvent->valid = 1;
}
else {
printf("%s: error, event(%u) was overrided\n", __func__, next_event);
break;
}
pEvent = &event_queue[next_event];
}
#if !defined(ZARLINK_SLIC_VE792)
}
}
#endif
}
pthread_mutex_unlock(&vp_lock);
return;
timer_exit:
return;
}
bool sys_service_get_event(VpDeviceIdType dev_id, VpEventType *event_p)
{
bool newEvent;
pthread_mutex_lock(&vp_lock);
if(event_count == 0) {
newEvent = FALSE;
}
else {
memcpy(event_p, &event_queue[curr_event].vp_event, sizeof(VpEventType));
event_queue[curr_event].valid = 0;
newEvent = TRUE;
event_count--;
curr_event++;
if(curr_event == MAX_EVENT_QUEUE_SIZE)
curr_event = 0;
}
pthread_mutex_unlock(&vp_lock);
return newEvent;
}
void *sys_service_timer_function (void *arg)
{
while(1)
{
/* Do useful work */
sys_service_tick_handler();
/* Wait 10ms */
usleep(10000);
}
return NULL;
}