/*---------------------------------------------------------------------------*\

    FILE....: GenericLinux.cpp
    TYPE....: C Functions
    AUTHOR..: David Rowe
    DATE....: 14/9/99

    FreeBSD implementation of Generic functions.


         Voicetronix Voice Processing Board (VPB) Software
         Copyright (C) 1999-2007 Voicetronix www.voicetronix.com.au

         This library is free software; you can redistribute it and/or
         modify it under the terms of the GNU Lesser General Public
         License as published by the Free Software Foundation; either
         version 2.1 of the License, or (at your option) any later version.

         This library 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
         Lesser General Public License for more details.

         You should have received a copy of the GNU Lesser General Public
         License along with this library; if not, write to the Free Software
         Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
         MA  02110-1301  USA

\*---------------------------------------------------------------------------*/

#include "config.h"

#include <assert.h>
#include <pthread.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include "generic.h"
#include "vpb_ioctl.h"

void GenericInitializeCriticalSection(GENERIC_CRITICAL_SECTION *cs) {
	int                 ret;
	pthread_mutex_t     *pmut;
	pthread_mutexattr_t  mutexattr;

	// create mutex
        pmut = new pthread_mutex_t;
	assert(pmut != NULL);

	pthread_mutexattr_init(&mutexattr);
	pthread_mutexattr_settype(&mutexattr, PTHREAD_MUTEX_RECURSIVE);
	assert(pthread_mutex_init(pmut,&mutexattr) == 0);
	pthread_mutexattr_destroy(&mutexattr);

	cs->v = (void*)pmut;
}

void GenericDeleteCriticalSection(GENERIC_CRITICAL_SECTION *cs) {
	int ret;

	// destroy mutex
	ret = pthread_mutex_destroy((pthread_mutex_t*)cs->v);
	assert(ret == 0);
	delete (pthread_mutex_t*)cs->v;
}

void GenericEnterCriticalSection(GENERIC_CRITICAL_SECTION *cs)  {
	int ret;
	ret = pthread_mutex_lock((pthread_mutex_t*)cs->v);
	assert(ret == 0);
}

void GenericLeaveCriticalSection(GENERIC_CRITICAL_SECTION *cs)  {
	int ret;
	ret = pthread_mutex_unlock((pthread_mutex_t*)cs->v);
	assert(ret == 0);
}

// return time in ms since system startup
unsigned long GenerictimeGetTime() {
	struct timeval tval;
	gettimeofday(&tval, NULL);
	return (tval.tv_sec*1000 + tval.tv_usec/1000);
}

// determies OS, returns -1 for fail

int GenericGetOS() {
	return GET_OS_LINUX;
}

// sleep for ms milliseconds
void GenericSleep(unsigned int ms)
{
	long ms_l = ms;
	struct timespec ts;
	ts.tv_sec = ms_l/1000;
	ts.tv_nsec = (ms_l-ts.tv_sec*1000)*1000000l;
	nanosleep(&ts, NULL);
}

void Generic_beginthread(void(*func)(void*), int stack, void *data) {
	pthread_t	thread;
	pthread_create(&thread, NULL, (void*(*)(void*))func, data);
}

void Generic_endthread()
{
	pthread_detach(pthread_self());
}

void GenericSetThreadPriorityHigh() {
}

// do nothing for initial implementation
void GenericSetThreadPriorityNormal() {
}

int Generic_open() {
	return open("/dev/vpb0",O_RDWR);
}

int Generic_close(int fd) {
	return close(fd);
}

// do nothing in FreeBSD
int GenericDeviceIoControl(
	void		*hndFile,	// Handle to device
        long	        IoctlCode,      // IO Control code for Read
        void		*InBuffer,      // Buffer to driver.
	long		InBufferSize,
        void		*OutBuffer,     // Buffer from driver
	long		OutBufferSize,
	unsigned long	*BytesReturned,	// no. bytes returned from driver
	void		*Overlapped	// not used, 
)
{
	return 0;
}

// Device driver PCI I/O functions

int Generic_pci_dsp_reset(int fd, unsigned short board) {
	VPB_DATA params;
	int      ret;

	params.pci_num = board;
	ret = ioctl(fd, VPB_IOC_PCI_DSP_RESET, &params);

	return ret;
}

int Generic_pci_dsp_run(int fd, unsigned short board) {
	VPB_DATA params;
	int      ret;

	params.pci_num = board;
	ret = ioctl(fd, VPB_IOC_PCI_DSP_RUN, &params);

	return ret;
}

int Generic_pci_block_read(
			   int fd,
			   unsigned short board,
			   unsigned short addr,
			   unsigned short length,
			   unsigned short *buf)
{
	VPB_DATA params;

	// set up ioctlparams
	params.length = length;
	params.dsp_addr = addr;
	params.pci_num = board;
	params.data = buf;

	return ioctl(fd, VPB_IOC_PCI_BLOCK_READ, &params);
}

int Generic_pci_block_write(
			    int fd,
			    unsigned short board,
			    unsigned short addr,
			    unsigned short length,
			    unsigned short *buf)
{
	VPB_DATA params;

	// set up ioctlparams
	params.length = length;
	params.dsp_addr = addr;
	params.pci_num = board;
	params.data = buf;

	return ioctl(fd, VPB_IOC_PCI_BLOCK_WRITE, &params);
}

int Generic_pci_block_eeread(
			   int fd,
			   unsigned short board,
			   unsigned short addr,
			   unsigned short length,
			   unsigned short *buf)
{
	VPB_DATA params;

	// set up ioctlparams
	params.length = length;
	params.dsp_addr = addr;
	params.pci_num = board;
	params.data = buf;

	return ioctl(fd, VPB_IOC_PCI_BLOCK_EEREAD, &params);
}

//XXX
#if 0
// determines what type of Voicetronix PCI card we are using, base on
// which kernel mode drivers are installed.  We assume that the user
// has installed the correct kernel mode driver :-)

int GenericGetCardType() {
    int fd;

    // test for presence of V4PCI (OpenLine4)
    fd = open("/dev/vpb0", O_RDWR);
    if (fd != -1) {
        close(fd);
        return VPB_V4PCI;
    }

/*
    fprintf(stderr,
        "\nERROR opening Voicetronix kernel mode driver:\n"
        "- cant open /dev/vpb0 OR /dev/vpbhp0\n"
        "- Check the driver README\n\n");
    exit(1);
    return 0;
*/
    return -1;
}

// Asks kernel mode driver how many cards are in system.  This function 
// should be called before driver is opened, as it opens then quickly closes
// the kernel mode driver, and we should probably having it opened twice.
// I cant help thinking there should be a better way to do this........

int GenericGetNumCards(int vpb_model) {
    int fd;
    int numCards, ret;

    if ((vpb_model == VPB_V4PCI) || (vpb_model == VPB_V4LOG)) {
	fd = open("/dev/vpb0", O_RDWR);
	if (fd == -1) {
		printf("Error opening kernel mode driver!\n");
		printf("-Check to see if vpb.o is installed "
		       "using 'lsmod'\n");
		printf("-Check to see that node /dev/vpb0 exists\n");
		exit(1);
	}

	VPB_DATA params;

	// set up ioctlparams
	params.length = 0;
	params.dsp_addr = 0;
	params.pci_num = 0;
	params.data = (word*)&numCards;

	ret = ioctl(fd, VPB_IOC_PCI_GET_NUM_CARDS, &params);
	close(fd);
    	return numCards;
    }
    else {
        fd = open("/dev/vpbhp0", O_RDWR);
        if (fd == -1) {
             fprintf(stderr,
                    "\nERROR opening OpenSwitch12 kernel mode "
                    "driver /dev/vpbhp0!\n"
                    "- Check to see if vpbhp.o is installed using "
                    "'lsmod'.\n"
                    "- Check that device node /dev/vpbhp0 "
                    "exists and /dev/vpbhp0 permissions are correct.\n"
                    "- Is an OpenSwitch12 card installed?\n"
                    "- Is the 'VPB_MODEL' environment variable set for "
                    "your card (Optional)?\n"
                    "\n");
              exit(1);
        }
        VPBHP_DATA params;

        // set up ioctlparams
        params.length = 0;
        params.vpbhp_addr = 0;
        params.pci_num = 0;
        params.data = (word*)&numCards;

	ret = ioctl(fd, VPBHP_IOC_PCI_GET_NUM_CARDS, &params);
        close(fd);
        return numCards;
    }
}
#endif

