442 lines
12 KiB
C
442 lines
12 KiB
C
/* linux_port.c
|
|
|
|
This file is part of a program that implements a Software-Defined Radio.
|
|
|
|
Copyright (C) 2013 Warren Pratt, NR0V and John Melton, G0ORX/N6LYT
|
|
|
|
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.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
The author can be reached by email at
|
|
|
|
warren@wpratt.com
|
|
john.d.melton@googlemail.com
|
|
|
|
*/
|
|
|
|
#include <errno.h>
|
|
|
|
#include "linux_port.h"
|
|
#include "comm.h"
|
|
|
|
/********************************************************************************************************
|
|
* *
|
|
* Linux Port Utilities *
|
|
* *
|
|
********************************************************************************************************/
|
|
|
|
#if defined(linux) || defined(__APPLE__)
|
|
|
|
void QueueUserWorkItem(void *function,void *context,int flags) {
|
|
pthread_t t;
|
|
pthread_create(&t, NULL, function, context);
|
|
pthread_join(t, NULL);
|
|
}
|
|
|
|
static inline void init_crit_section(pthread_mutex_t *mutex) {
|
|
pthread_mutexattr_t mAttr;
|
|
pthread_mutexattr_init(&mAttr);
|
|
#ifdef __APPLE__
|
|
// DL1YCF: MacOS X does not have PTHREAD_MUTEX_RECURSIVE_NP
|
|
pthread_mutexattr_settype(&mAttr,PTHREAD_MUTEX_RECURSIVE);
|
|
#else
|
|
pthread_mutexattr_settype(&mAttr,PTHREAD_MUTEX_RECURSIVE_NP);
|
|
#endif
|
|
pthread_mutex_init(mutex,&mAttr);
|
|
pthread_mutexattr_destroy(&mAttr);
|
|
}
|
|
|
|
void InitializeCriticalSection(pthread_mutex_t *mutex) {
|
|
init_crit_section(mutex);
|
|
}
|
|
|
|
void InitializeCriticalSectionAndSpinCount(pthread_mutex_t *mutex, int count) {
|
|
init_crit_section(mutex);
|
|
}
|
|
|
|
void EnterCriticalSection(pthread_mutex_t *mutex) {
|
|
pthread_mutex_lock(mutex);
|
|
}
|
|
|
|
void LeaveCriticalSection(pthread_mutex_t *mutex) {
|
|
pthread_mutex_unlock(mutex);
|
|
}
|
|
|
|
void DeleteCriticalSection(pthread_mutex_t *mutex) {
|
|
pthread_mutex_destroy(mutex);
|
|
}
|
|
|
|
int LinuxWaitForSingleObject(sem_t *sem,int ms) {
|
|
int result=0;
|
|
if(ms==INFINITE) {
|
|
// wait for the lock
|
|
result=sem_wait(sem);
|
|
} else {
|
|
for (int i = 0; i < ms; i++) {
|
|
result=sem_trywait(sem);
|
|
if (result == 0) break;
|
|
Sleep(1);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
sem_t *LinuxCreateSemaphore(int attributes,int initial_count,int maximum_count,char *name) {
|
|
sem_t *sem;
|
|
#ifdef __APPLE__
|
|
//
|
|
// DL1YCF
|
|
// This routine is usually invoked with name=NULL, so we have to make
|
|
// a unique name of tpye WDSPxxxxxxxx for each invocation, since MacOS only
|
|
// supports named semaphores. We shall unlink in due course, but first we
|
|
// need to check whether the name is possibly already in use, e.g. by
|
|
// another SDR program running on the same machine.
|
|
//
|
|
static long semcount=0;
|
|
char sname[20];
|
|
for (;;) {
|
|
sprintf(sname,"WDSP%08ld",semcount++);
|
|
sem=sem_open(sname, O_CREAT | O_EXCL, 0700, initial_count);
|
|
if (sem == SEM_FAILED && errno == EEXIST) continue;
|
|
break;
|
|
}
|
|
if (sem == SEM_FAILED) {
|
|
perror("WDSP:CreateSemaphore");
|
|
}
|
|
//
|
|
// we can unlink the semaphore NOW. It will remain functional
|
|
// until sem_close() has been called by all threads using that
|
|
// semaphore.
|
|
//
|
|
sem_unlink(sname);
|
|
#else
|
|
sem=malloc0(sizeof(sem_t));
|
|
int result;
|
|
// DL1YCF: added correct initial count
|
|
result=sem_init(sem, 0, initial_count);
|
|
if (result < 0) {
|
|
perror("WDSP:CreateSemaphore");
|
|
}
|
|
#endif
|
|
return sem;
|
|
}
|
|
|
|
void LinuxReleaseSemaphore(sem_t* sem,int release_count, int* previous_count) {
|
|
//
|
|
// Note WDSP always calls this with previous_count==NULL
|
|
// so we do not bother about obtaining the previous value and
|
|
// storing it in *previous_count.
|
|
//
|
|
while(release_count>0) {
|
|
sem_post(sem);
|
|
release_count--;
|
|
}
|
|
}
|
|
|
|
sem_t *CreateEvent(void* security_attributes,int bManualReset,int bInitialState,char* name) {
|
|
//
|
|
// This is always called with bManualReset = bInitialState = FALSE
|
|
//
|
|
sem_t *sem;
|
|
sem=LinuxCreateSemaphore(0,0,0,0);
|
|
return sem;
|
|
}
|
|
|
|
void LinuxSetEvent(sem_t* sem) {
|
|
//
|
|
// WDSP uses this to set the semaphore (event) to
|
|
// a "releasing" state.
|
|
// we simulate this by posting
|
|
sem_post(sem);
|
|
}
|
|
|
|
void LinuxResetEvent(sem_t* sem) {
|
|
//
|
|
// WDSP uses this to set the semaphore (event) to
|
|
// a blocking state.
|
|
// We mimic this by calling sem_trywait as long as it succeeds
|
|
//
|
|
while (sem_trywait(sem) == 0) ;
|
|
}
|
|
|
|
HANDLE _beginthread( void( __cdecl *start_address )( void * ), unsigned stack_size, void *arglist) {
|
|
pthread_t threadid;
|
|
pthread_attr_t attr;
|
|
|
|
if (pthread_attr_init(&attr)) {
|
|
return (HANDLE)-1;
|
|
}
|
|
|
|
if(stack_size!=0) {
|
|
if (pthread_attr_setstacksize(&attr, stack_size)) {
|
|
return (HANDLE)-1;
|
|
}
|
|
}
|
|
|
|
if(pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED)) {
|
|
return (HANDLE)-1;
|
|
}
|
|
|
|
if (pthread_create(&threadid, &attr, (void*(*)(void*))start_address, arglist)) {
|
|
return (HANDLE)-1;
|
|
}
|
|
|
|
//pthread_attr_destroy(&attr);
|
|
#if !defined(__APPLE__) && !defined(NO_PTHREAD_SETNAME_NP)
|
|
//
|
|
// pthread_setname_np does not exist (or exists with
|
|
// different semantics) on MacOS and on certain
|
|
// lightweight LINUX variants such as "DietPi".
|
|
// Using pthread_setname_np() serves no function except that
|
|
// one sees what the individual threads are doing when
|
|
// watching the system via "top -h"
|
|
//
|
|
void sendbuf(void *arg); // declared in analyzer.c but not in header file
|
|
char tname[64];
|
|
if (start_address == &wdspmain) {
|
|
snprintf(tname, sizeof(tname), "Wchan%d", (int)(uintptr_t)arglist);
|
|
} else if (start_address == &sendbuf) {
|
|
snprintf(tname, sizeof(tname), "Wdisp%d", (int)(uintptr_t)arglist);
|
|
} else if (start_address == &flushChannel) {
|
|
snprintf(tname, sizeof(tname), "Wflush%d", (int)(uintptr_t)arglist);
|
|
} else if (start_address == &syncb_main) {
|
|
snprintf(tname, sizeof(tname), "WSync");
|
|
} else if (start_address == &doPSCalcCorrection
|
|
|| start_address == &doPSTurnoff
|
|
|| start_address == &PSSaveCorrection
|
|
|| start_address == &PSRestoreCorrection) {
|
|
snprintf(tname, sizeof(tname), "PURESIGNAL");
|
|
} else {
|
|
// in case there are more worker types
|
|
snprintf(tname, sizeof(tname), "WDSP");
|
|
}
|
|
//
|
|
// Ignore return value since we continue anyway.
|
|
//
|
|
(void) pthread_setname_np(threadid, tname);
|
|
#endif
|
|
|
|
return (HANDLE)threadid;
|
|
|
|
}
|
|
|
|
void _endthread() {
|
|
pthread_exit(NULL);
|
|
}
|
|
|
|
void SetThreadPriority(HANDLE thread, int priority) {
|
|
//
|
|
// In Linux, the scheduling priority only affects
|
|
// real-time threads (SCHED_FIFO, SCHED_RR), so this
|
|
// is basically a no-op here.
|
|
//
|
|
/*
|
|
int policy;
|
|
struct sched_param param;
|
|
|
|
pthread_getschedparam(thread, &policy, ¶m);
|
|
param.sched_priority = sched_get_priority_max(policy);
|
|
pthread_setschedparam(thread, policy, ¶m);
|
|
*/
|
|
}
|
|
|
|
void CloseHandle(HANDLE hObject) {
|
|
//
|
|
// This routine is *ONLY* called to release semaphores
|
|
// The WDSP transmitter thread terminates upon each TX/RX
|
|
// transition, where it closes and re-opens a semaphore
|
|
// in flush_buffs() in iobuffs.c. Therefore, we have to
|
|
// release any resource associated with this semaphore, which
|
|
// may be a small memory patch (LINUX) or a file descriptor
|
|
// (MacOS).
|
|
//
|
|
#ifdef __APPLE__
|
|
if (sem_close((sem_t *)hObject) < 0) {
|
|
perror("WDSP:CloseHandle:SemCLose");
|
|
}
|
|
#else
|
|
if (sem_destroy((sem_t *)hObject) < 0) {
|
|
perror("WDSP:CloseHandle:SemDestroy");
|
|
} else {
|
|
// if sem_destroy failed, do not release storage
|
|
_aligned_free(hObject);
|
|
}
|
|
#endif
|
|
|
|
return;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// MALLOC debug facility.
|
|
// In the header file (linux_port.h), one can #define
|
|
//
|
|
// _aligned_malloc(a,b) ==> my_malloc(a)
|
|
// _aligned_free(a) ==> my_free(a)
|
|
//
|
|
// Then all memory allocations/deallocations will be done via my_malloc() my_free()
|
|
// Note this is thread-safe, since an explicit mutex is used.
|
|
//
|
|
// my_alloc will build a "fence", 1k wide, to both sides of the allocated area,
|
|
// and fill it with some bit pattern.
|
|
//
|
|
// my_free will check for the integrity of the "fence" and report how many bytes
|
|
// in the upper and lower fence have illegally been changed
|
|
//
|
|
// furthermore, my_free will complain (and terminate the program) if its argument
|
|
// does not point to an active memory block allocated with my_malloc.
|
|
//
|
|
// P.S.1: Using "valgrind" with such time-critical programs is not a good idea,
|
|
// so here is a solution.
|
|
//
|
|
// P.S.2: Further extensions are possible, e.g. include __FUNCTION__ and __LINE__
|
|
// in the argument list of my_malloc(), store this in MEM_LIST, and report
|
|
// upon failure.
|
|
//
|
|
// P.S.3: The standard definitions in linux_port.h are
|
|
//
|
|
// __aligned_malloc(a,b) ==> malloc(a)
|
|
// __aligned_free(a) ==> free(a)
|
|
//
|
|
// and with these, "MALLOC debug" code is not used.
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static pthread_mutex_t malloc_mutex = PTHREAD_MUTEX_INITIALIZER;
|
|
|
|
struct _MEM_LIST {
|
|
void *baseptr;
|
|
void *freeptr;
|
|
size_t size;
|
|
int in_use;
|
|
};
|
|
|
|
typedef struct _MEM_LIST MEM_LIST;
|
|
|
|
#define MEM_LIST_SIZE 32768
|
|
|
|
MEM_LIST malloc_slot[MEM_LIST_SIZE] = {0};
|
|
|
|
void *my_malloc(size_t size) {
|
|
int slot;
|
|
void *baseptr, *freeptr;;
|
|
uint8_t *p1, *p2;
|
|
|
|
pthread_mutex_lock(&malloc_mutex);
|
|
//
|
|
// locate a free slot
|
|
//
|
|
slot=-1;
|
|
for (int i=0; i<MEM_LIST_SIZE; i++) {
|
|
if (malloc_slot[i].in_use == 0) {
|
|
slot=i;
|
|
break;
|
|
}
|
|
}
|
|
if (slot < 0) {
|
|
fprintf(stderr,"my_malloc: All Slots Exhausted.\n");
|
|
fflush(stderr);
|
|
pthread_mutex_unlock(&malloc_mutex);
|
|
_exit(8);
|
|
}
|
|
baseptr=malloc(size+2048);
|
|
if (baseptr == NULL) { return NULL; }
|
|
|
|
freeptr=baseptr+1024;
|
|
|
|
malloc_slot[slot].in_use = 1;
|
|
malloc_slot[slot].baseptr = baseptr;
|
|
malloc_slot[slot].freeptr = freeptr;
|
|
malloc_slot[slot].size = size;
|
|
|
|
//
|
|
// Create a "fence" around the allocated area
|
|
//
|
|
p1 = baseptr;
|
|
p2 = freeptr + size;
|
|
|
|
for (int i=0; i<256; i++) {
|
|
*p1++ = 0xAA;
|
|
*p1++ = 0x55;
|
|
*p1++ = 0xEF;
|
|
*p1++ = 0xFE;
|
|
*p2++ = 0xAA;
|
|
*p2++ = 0x55;
|
|
*p2++ = 0xEF;
|
|
*p2++ = 0xFE;
|
|
}
|
|
pthread_mutex_unlock(&malloc_mutex);
|
|
//fprintf(stderr,"my_malloc: Allocated Block slot=%d addr=%p\n", slot, freeptr);
|
|
return freeptr;
|
|
}
|
|
|
|
void my_free(void *ptr) {
|
|
int slot;
|
|
uint8_t *p1, *p2;
|
|
|
|
pthread_mutex_lock(&malloc_mutex);
|
|
//
|
|
// Search for block
|
|
//
|
|
slot=-1;
|
|
for (int i=0; i<4096; i++) {
|
|
if (malloc_slot[i].in_use == 1 && malloc_slot[i].freeptr == ptr) {
|
|
slot = i;
|
|
break;
|
|
}
|
|
}
|
|
if (slot < 0) {
|
|
fprintf(stderr,"my_free: Trying to free non-allocated block at addr=%p\n",ptr);
|
|
fflush(stderr);
|
|
pthread_mutex_unlock(&malloc_mutex);
|
|
_exit(8);
|
|
}
|
|
//
|
|
// Verify integrity of fence
|
|
//
|
|
int under_count=0;
|
|
int over_count=0;
|
|
|
|
p1 = malloc_slot[slot].baseptr;
|
|
p2 = malloc_slot[slot].freeptr+malloc_slot[slot].size;
|
|
|
|
for (int i=0; i<256; i++) {
|
|
if (*p1++ != 0xAA) under_count++;
|
|
if (*p1++ != 0x55) under_count++;
|
|
if (*p1++ != 0xEF) under_count++;
|
|
if (*p1++ != 0xFE) under_count++;
|
|
if (*p2++ != 0xAA) over_count++;
|
|
if (*p2++ != 0x55) over_count++;
|
|
if (*p2++ != 0xEF) over_count++;
|
|
if (*p2++ != 0xFE) over_count++;
|
|
}
|
|
if (under_count > 0) {
|
|
fprintf(stderr,"WARNING: my_free: Fence underrun =%d\n", under_count);
|
|
}
|
|
if (over_count > 0) {
|
|
fprintf(stderr,"WARNING: my_free: Fence overrun =%d\n", over_count);
|
|
}
|
|
if (over_count > 0 || under_count > 0) {
|
|
fprintf(stderr,"WARNING: my_free: Block slot=%d size=%ld allocated addr=%p\n", slot,
|
|
(long) malloc_slot[slot].size, malloc_slot[slot].freeptr);
|
|
}
|
|
free(malloc_slot[slot].baseptr);
|
|
malloc_slot[slot].in_use=0;
|
|
|
|
pthread_mutex_unlock(&malloc_mutex);
|
|
}
|
|
|
|
#endif
|