bbcinit.c 10.9 KB
#include "../bbclocal.h"

bbc_hand *handles[BBC_MAX_DEVS];
BBCMuxParam muxParam;
HANDLE hMuxThread = NULL;

static void __bbc_kill_mux(bbc_hand *hp);
extern int __BBC_SetCardSeqno(bbc_hand *hp);

static int
find_handle()
{
    int i;

    for (i = 0; i < BBC_MAX_DEVS; i++)
        if (handles[i] == NULL)
            return i;

    return -1;
}

int
next_seqno()
{
    static int seqno = 0;

    if (++seqno <= 0)
        seqno = 1;
    return seqno;
}

extern DWORD WINAPI domux_win32(LPVOID);

struct {
    int    (*init)(bbc_hand *);
} initfunc[] = {
    { NULL /*__bbc_new_reader*/ }, //TODO: support reader interface
    { __bbc_new_direct },
};

BBCHandle
BBCInit(int callType)
{
    unsigned int dwMuxId;
    int h, type, rv;
    int inpipe[2];
    int outpipe[2];
    bbc_hand *hp;

    BBC_LOG(MSG_DIAG, "BBCInit %d\n", callType);

    type = BBC_HT_DIRECT; //TODO: detect interface type

    /*
     * Allocate handle and call the appropriate init routine
     */
    if ((h = find_handle()) < 0) {
        BBC_LOG(MSG_ERR, "BBCInit: out of handles");
        return BBC_TOOMANY;
    }
    if ((hp = malloc(sizeof (bbc_hand))) == NULL) {
        BBC_LOG_SYSERROR("BBCInit: malloc failed");
        return BBC_NOMEM;
    }
    handles[h] = hp;
    memset(hp, 0, sizeof (bbc_hand));
    hp->bh_type = type;
    rv = (initfunc[type].init)(hp);
    if (rv != BBC_OK) {
        goto err1;  
    }

    /*
     * Allocate buffer for FAT blocks 
     */
    if ((hp->bh_fat = malloc(BB_FL_MAX_FATS*sizeof(BbFat16))) == NULL) {
        BBC_LOG_SYSERROR("BBCInit: malloc failed");
        rv = BBC_NOMEM;
        goto err1;
    }
    memset(hp->bh_fat, 0, BB_FL_MAX_FATS*sizeof(BbFat16));
    
    if (hp->bh_type != BBC_HT_DIRECT) {
        if (!hp->bh_card_present) {
            hp->bh_fat_valid = 0;
            return h;
        }
        if (hp->bh_cardsize != 4096 && hp->bh_cardsize != 8192)
            BBC_LOG(MSG_WARNING, "BBCInit: invalid card size %d blks\n", hp->bh_cardsize);
        /* read fat into allocated buffer */
        if ((rv = __bbc_read_fat(hp)) != BBC_OK && rv != BBC_NOFORMAT)
            goto err2;
        /* Delete the canonically named temp file used in create/atomic rename */
        (void) __BBC_FDelete(hp, TEMPFILE);
        return h;
    }

    if (_pipe(inpipe, MAX_PIPE_DATA, O_BINARY) < 0) {
        BBC_LOG_PERROR("BBCInit: pipe failed");
        rv = BBC_ERROR;
        goto err2;
    }
    if (_pipe(outpipe, MAX_PIPE_DATA, O_BINARY) < 0) {
        BBC_LOG_PERROR("BBCInit: pipe failed");
        rv = BBC_ERROR;
        goto err3;
    }


    // Create events for thread management
    rv = BBC_ERROR;
    InitializeCriticalSection( &CriticalSection );
    hMuxReadyEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    if (hMuxReadyEvent == NULL) {
        BBC_LOG_SYSERROR("BBCInit: Error creating MuxReadyEvent");
        goto err4;
    }
    hDeviceReadyEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    if (hDeviceReadyEvent == NULL) {
        BBC_LOG_SYSERROR("BBCInit: Error creating DeviceReadyEvent");
        goto err4;
    }
    hDeviceWriteDoneEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
    if (hDeviceWriteDoneEvent == NULL) {
        BBC_LOG_SYSERROR("BBCInit: Error creating DeviceWriteDoneEvent");
        goto err4;
    }
    hDeviceErrorEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    if (hDeviceErrorEvent == NULL) {
        BBC_LOG_SYSERROR("BBCInit: Error creating DeviceErrorEvent");
        goto err4;
    }
    hDeviceDataSendEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    if (hDeviceDataSendEvent == NULL) {
        BBC_LOG_SYSERROR("BBCInit: Error creating DeviceDataSendEvent");
        goto err4;
    }
    hBBCCloseEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    if (hBBCCloseEvent == NULL) {
        BBC_LOG_SYSERROR("BBCInit: Error creating BBCCloseEvent");
        goto err4;
    }

    // Make sure both read and write pipes are reset 
    if (do_usb_reset(hp->bh_urh) == FALSE)
        goto err4;
    if (do_usb_reset(hp->bh_uwh) == FALSE)
        goto err4;

    // Start Mux thread
    muxParam.devname = hp->bh_readname;
    muxParam.rh = hp->bh_urh;
    muxParam.wh = hp->bh_uwh;
    muxParam.ifd = outpipe[0];
    muxParam.ofd = inpipe[1];

    hMuxThread = (HANDLE) _beginthreadex(NULL, 0, &domux_win32,
                                &muxParam, 0, &dwMuxId);
                             
    if (hMuxThread == NULL) {
        BBC_LOG_SYSERROR("BBCInit: Error starting mux thread");
        rv = BBC_ERROR;
        goto err4;
    }
	else  {
		// Wait for Mux thread to be ready (or for the thread to terminate)
	    DWORD dwWaitResult;
		HANDLE hDeviceEvents[2];
		hDeviceEvents[0] = hMuxThread;
		hDeviceEvents[1] = hMuxReadyEvent;
		
		dwWaitResult = myWaitForMultipleObjects( 
			2,             /* number of handles in array      */
			hDeviceEvents, /* array of device-event handles   */
			FALSE,         /* wait until only one is signaled */
			WAIT_DEVICE_READY_TIMEOUT);  /* 2 second wait     */

		if (dwWaitResult == WAIT_OBJECT_0 + 1) {
			/* Mux is ready! Can go foward with our life now. */
		}
		else {
			/* Hmm, what's wrong with mux? */
			BBC_LOG(MSG_ERR, "BBCInit: Error waiting for mux to be ready");
			rv = BBC_ERROR;
			goto err4;
		}
	}


    // Start USB read thread
    if (bb_usb_read_start(&(hp->bh_urh)) < 0) {
        BBC_LOG_SYSERROR("BBCInit: Error starting USB thread");
        goto err4;
    }

    // Assign pipes to hp
    hp->bh_pifd = inpipe[0];
    hp->bh_pofd = outpipe[1];
                         
    BBC_LOG(MSG_DIAG, "BBCInit: Ready to communicate with player\n");
    
    /*
     * Check for the presence of a card and init sequence number
     */
    if ((rv = __BBC_SetCardSeqno(hp)) < 0) {
        BBC_LOG(MSG_DEBUG, "BBCInit: SetSeqno ret %d\n", rv);
        /*
         * It is ok for this to fail if no card is present or
         * the card has not yet been formatted
         */
        if (rv == BBC_NOCARD || rv == BBC_NOFORMAT) {
            return h;
        }
        goto err4;
    }

    /* Delete the canonically named temp file used in create/atomic rename */
    rv = __BBC_FDelete(hp, TEMPFILE);
    if (BBC_IO_ERR(rv)) {
        /* Abort if it was an IO error */
        goto err4;
    }

    BBC_LOG(MSG_DIAG, "BBCInit successful\n");

    return h;

err4:
    __bbc_kill_mux(hp);
    close(outpipe[0]);
    close(outpipe[1]);
err3:
    close(inpipe[0]);
    close(inpipe[1]);
err2:
    free(hp->bh_fat);
    hp->bh_fat = NULL;
err1:
    if (hp->bh_flashif != NULL)
        (void) free(hp->bh_flashif);
    free(hp);
    handles[h] = NULL;
    BBC_LOG(MSG_DIAG, "BBCInit error %d\n", rv);
    return rv;
}

static void
__bbc_kill_mux(bbc_hand *hp)
{
   // Tells our threads that we are closing
	if (hBBCCloseEvent)
		SetEvent(hBBCCloseEvent);
    BBC_LOG(MSG_DEBUG, "usb read end\n");
	bb_usb_read_end();
    if (hMuxThread) {
        BBC_LOG(MSG_INFO, "Wait for mux thread to terminate nicely\n");
        if (myWaitForSingleObject( hMuxThread, WAIT_THREAD_TERM_TIMEOUT ) != WAIT_OBJECT_0) {
            BBC_LOG(MSG_INFO, "Terminating mux thread\n");
            if (TerminateThread(hMuxThread, -1) == FALSE) {
                BBC_LOG_SYSERROR("Error terminating mux thread");
            }
        }
        else {
            BBC_LOG(MSG_INFO, "Mux thread terminated\n");
        }
        if (CloseHandle(hMuxThread) == FALSE) {
            BBC_LOG_SYSERROR("Error closing handle to mux thread");
        }
        hMuxThread = NULL;
    }
    BBC_LOG(MSG_DEBUG, "Delete CriticalSection\n");
    DeleteCriticalSection( &CriticalSection );

    // Close event handles
    if (hMuxReadyEvent) {
        BBC_LOG(MSG_DEBUG, "Close hMuxReadyEvent\n");
        if (CloseHandle(hMuxReadyEvent) == FALSE) {
            BBC_LOG_SYSERROR("Error closing handle to MuxReadyEvent");
        }
        hMuxReadyEvent = NULL;
    }
    if (hDeviceReadyEvent) {
        BBC_LOG(MSG_DEBUG, "Close hDeviceReadyEvent\n");
        if (CloseHandle(hDeviceReadyEvent) == FALSE) {
            BBC_LOG_SYSERROR("Error closing handle to DeviceReadyEvent");
        }
        hDeviceReadyEvent = NULL;
    }
    if (hDeviceWriteDoneEvent) {
        BBC_LOG(MSG_DEBUG, "Close hDeviceWriteDoneEvent\n");
        if (CloseHandle(hDeviceWriteDoneEvent) == FALSE) {
            BBC_LOG_SYSERROR("Error closing handle to DeviceWriteDoneEvent");
        }
        hDeviceWriteDoneEvent = NULL;
    }
    if (hDeviceErrorEvent) {
        BBC_LOG(MSG_DEBUG, "Close hDeviceErrorEvent\n");
        if (CloseHandle(hDeviceErrorEvent) == FALSE) {
            BBC_LOG_SYSERROR("Error closing handle to DeviceErrorEvent");
        }
        hDeviceErrorEvent = NULL;
    }
    if (hDeviceDataSendEvent) {    
        BBC_LOG(MSG_DEBUG, "Close hDeviceDataSendEvent\n");
        if (CloseHandle(hDeviceDataSendEvent) == FALSE) {
            BBC_LOG_SYSERROR("Error closing handle to DeviceDataSendEvent");
        }
        hDeviceDataSendEvent = NULL;
    }
    if (hBBCCloseEvent) {
        BBC_LOG(MSG_DEBUG, "Close hBBCCloseEvent\n");
        if (CloseHandle(hBBCCloseEvent) == FALSE) {
            BBC_LOG_SYSERROR("Error closing handle to hBBCCloseEvent");
        }
        hBBCCloseEvent = NULL;
    }

    // Close USB handles
    // Also send reset to BBPlayer to abort whatever
    // it is doing and turn off the LEDs
    if (hp->bh_uwh) {
        BBC_LOG(MSG_DEBUG, "Close uwh\n");
        do_usb_reset(hp->bh_uwh);
        if (CloseHandle(hp->bh_uwh) == FALSE) {
            BBC_LOG_SYSERROR("Error closing handle to USB Write");
        }
        hp->bh_uwh = NULL;
    }
    if (hp->bh_urh) {
        BBC_LOG(MSG_DEBUG, "Close urh\n");
        do_usb_reset(hp->bh_urh);
        if (CloseHandle(hp->bh_urh) == FALSE) {
            BBC_LOG_SYSERROR("Error closing handle to USB Read");
        }
        hp->bh_urh = NULL;
    }
}

int
BBCClose(BBCHandle h)
{
    bbc_hand *hp;
    flashif_t *fif;

    BBC_LOG(MSG_INFO, "BBCClose %d\n", h);
    if (h < 0 || h >= BBC_MAX_DEVS)
        return BBC_BADHANDLE;

    if ((hp = handles[h]) == NULL)
        return BBC_BADHANDLE;

    if (hp->bh_type == BBC_HT_DIRECT
            || hp->bh_type == BBC_HT_MUX_ONLY) {
        __bbc_kill_mux(hp);
        BBC_LOG(MSG_DEBUG, "Close hp->bh_pofd\n");
        (void) close(hp->bh_pofd);
        BBC_LOG(MSG_DEBUG, "Close hp->bh_pifd\n");
        (void) close(hp->bh_pifd);
        //BBC_LOG(MSG_DEBUG, "Close muxParam.ifd\n");
		//close(muxParam.ifd);
        //BBC_LOG(MSG_DEBUG, "Close muxParam.ofd\n");
		//close(muxParam.ofd);
    } 
    else if ((fif = hp->bh_flashif) != NULL) {
        (*fif->close)(fif->f);
    }

    if (hp->bh_fat)
        (void) free(hp->bh_fat);

    if (hp->bh_flashif)
        (void) free(hp->bh_flashif);
    (void) free(hp);
    handles[h] = NULL;

    BBC_LOG(MSG_DEBUG, "BBCClose done\n");
    return BBC_OK;
}

char*
BBCGetDevName(BBCHandle h, int type)
{
    bbc_hand *hp;

    if (h < 0 || h >= BBC_MAX_DEVS)
        return NULL;

    if ((hp = handles[h]) == NULL)
        return NULL;

    return type ? hp->bh_writename: hp->bh_readname;
}