/* 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 #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 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