bbcfs.c 9.15 KB
#include "bbclocal.h"

/*
 * Utility routine to count the number of zero
 * bits in a byte (needed to detect bad block markings)
 */
int
__bbc_zerobits(unsigned char b)
{
    int c = 0;
    int i;

    for (i = 0; i < 8; i++)
	if ((b & (1<<i)) == 0) c++;
    return c;
}

static int
__uhReadGame(int fd, void *addr, int nbytes)
{
    int             bytesRead = 0;
    int             rv = 0;
    unsigned char   *dPtr = (unsigned char*)addr;

    BBC_LOG(MSG_ALL, "__uhReadGame: Reading %d bytes\n", nbytes);
    while(bytesRead < nbytes)
    {
        rv = read(fd, &dPtr[bytesRead], nbytes - bytesRead);
        if (rv < 0) 
            BBC_LOG_PERROR("uhReadGame: read failed");
        if (rv <= 0) {
            BBC_LOG(MSG_DEBUG, "uhReadGame: 0 bytes read\n");
            break;
		}
        bytesRead += rv;
    }
    BBC_LOG(MSG_ALL, "__uhReadGame: Done read %d bytes, rv = %d\n",
            bytesRead, rv);

    return(bytesRead);
}

static int
__uhWriteGame(int fd, void *addr, int nbytes)
{
    int   retVal;

    /* send byte count first */
    BBC_LOG(MSG_ALL, "__uhWriteGame: Writing %d bytes\n", nbytes);
    if ((retVal = write(fd, &nbytes, sizeof nbytes)) < 0) {
        BBC_LOG_PERROR("uhWriteGame: write failed");
        BBC_LOG(MSG_DEBUG, "uhWriteGame: Error writing count %d\n", nbytes);
        return retVal;
    }

    /* send data */
    retVal = write(fd,addr,nbytes);
    if (retVal < 0) {
        BBC_LOG_SYSERROR("uhWriteGame: write failed");
    }
    BBC_LOG(MSG_ALL, "__uhWriteGame: Done writing %d bytes, rv = %d\n",
            nbytes, retVal);
    return(retVal);
}

static int
__bbc_send_common(int fd, const void* buf, int len, int hton)
{
    int rv = BBC_EIO, i;

    if (hton) {
        for(i = 0; i < len/4; i++)
            *((unsigned int*)buf+i) = htonl(*((unsigned int*)buf+i));
    }

    if ((rv = __uhWriteGame(fd, (void*)buf, len)) < len) {
        rv = BBC_EIO;
    }

    return rv;
}

#ifdef WIN32 
static int
__bbc_send_win(int fd, const void* buf, int len, int hton)
{
    int rv = BBC_EIO;
    DWORD dwWaitResult;
    HANDLE hDeviceEvents[2];
    hDeviceEvents[0] = hDeviceErrorEvent;
    hDeviceEvents[1] = hDeviceReadyEvent;

    BBC_LOG(MSG_DEBUG, "__bbc_send_win: Waiting for device ready or error event to send %d bytes\n", len);
    dwWaitResult = myWaitForMultipleObjects( 
        2,             /* number of handles in array      */
        hDeviceEvents, /* array of device-event handles   */
        FALSE,         /* wait until only one is signaled */
        WAIT_DEVICE_READY_LONG_TIMEOUT);  /* 60 second wait, fix for 2K+SiS chipset */

    if (dwWaitResult == WAIT_OBJECT_0 + 1) {        
        BBC_LOG(MSG_ALL, "__bbc_send_win: Sending %d bytes\n", len);
        SetEvent(hDeviceDataSendEvent);
        ResetEvent(hDeviceReadyEvent);
        rv = __bbc_send_common(fd, buf, len, hton);
        BBC_LOG(MSG_ALL, "__bbc_send_win: Send done with %d\n", rv);
    }
    else {
        if (dwWaitResult == WAIT_OBJECT_0) {
            rv = BBC_DEVERR;
            BBC_LOG(MSG_ERR, "__bbc_send_win: Device error\n");
        }
        else if (dwWaitResult == WAIT_TIMEOUT) {
            rv = BBC_TIMEOUT;
            BBC_LOG(MSG_ERR, "__bbc_send_win: Device timeout\n");
        }
		else {
			rv = BBC_EIO;
            BBC_LOG(MSG_ERR, "__bbc_send_win: Error waiting for device ready, got %d\n", dwWaitResult);
		}
    }

    return rv;
}

int __bbc_send_cmd(int fd, const void* buf, int len)
{
    /* Send with host to network conversion */
    return __bbc_send_win(fd, buf, len, 1);
}

int __bbc_send_data(int fd, const void* buf, int len)
{
    /* Send without host to network conversion */
    return __bbc_send_win(fd, buf, len, 0);
}

#else /* Not WIN32 */

int __bbc_send_cmd(int fd, const void* buf, int len)
{
    /* Send with host to network conversion */
    return __bbc_send_common(fd, buf, len, 1);
}

int __bbc_send_data(int fd, const void* buf, int len)
{
    /* Send without host to network conversion */
    return __bbc_send_common(fd, buf, len, 0);
}

#endif

int
__bbc_read_rsp(int fd, void* buf, int len)
{
    int rv, i;
    BBC_LOG(MSG_DEBUG, "__bbc_read_rsp: Reading %d bytes\n", len);
    if ((rv = __uhReadGame(fd, buf, len)) < len) {
        return BBC_EIO;
    }
    for(i = 0; i < len/4; i++)
        *((unsigned int*)buf+i) = ntohl(*((unsigned int*)buf+i));
    return rv;
}

int
__bbc_read_data(int fd, const void* buf, int len)
{
    int rv;

    BBC_LOG(MSG_DEBUG, "__bbc_read_data: Reading %d bytes\n", len);
    if ((rv = __uhReadGame(fd, (void*)buf, len)) < len) {
        rv = BBC_EIO;
    }
    return rv;
}

int
__bbc_sync_fat(bbc_hand *hp)
{
    int rv;
    BBC_LOG(MSG_ALL, "__bbc_sync_fat\n");
    if (hp->bh_type == BBC_HT_DIRECT) {
        int hbuf[2];
        hbuf[0] = REQ_FS_INIT;
		hbuf[1] = 0;
        if ((rv = __bbc_send_cmd(hp->bh_pofd, hbuf, 8)) < 0) return rv;
        if ((rv = __bbc_read_rsp(hp->bh_pifd, hbuf, 8)) < 0) return rv;
        if (hbuf[0] != 255-REQ_FS_INIT) {
            BBC_LOG(MSG_ERR, "__bbc_sync_fat: sync loss on REQ_FS_INIT\n");
            return BBC_SYNCLOST;
        }
        if ((int) hbuf[1] < 0) {
            BBC_LOG(MSG_ERR, "__bbc_sync_fat: fs reload failed\n");
            hp->bh_fat_valid = 0;
            /* Try to map the error codes we got into libbbc error codes */
            switch (hbuf[1]) {
                case BBCARD_ERR_NO_CARD:  return BBC_NOCARD;
                case BBCARD_ERR_CHANGED:  return BBC_CARDCHANGE;

                /* All other errors, treat as bad format */
                case BBCARD_ERR_FAIL:     
                case BBCARD_ERR_INVALID:
                default: return BBC_NOFORMAT; 
            }
        }
    }
    BBC_LOG(MSG_ALL, "__bbc_sync_fat SUCCESS\n");
    return BBC_OK;
}

int
__BBC_FStat(bbc_hand *hp, const char *name, OSBbStatBuf *sb, u16 *blist, u32 blen)
{
    int fd;
    int rv;

    BBC_LOG(MSG_ALL, "__BBC_FStat\n");
    if ((fd = __bbc_fopen(hp, name, "r")) < 0) {
        BBC_LOG(MSG_DEBUG, "__BBC_FStat: __bbc_fopen error %d\n", fd);
        BBC_LOG(MSG_DEBUG, "__BBC_FStat %s failed\n", name);
        return fd;
    }
    if ((rv = __bbc_fstat(hp, fd, sb, blist, blen)) != BBC_OK) {
        BBC_LOG(MSG_DEBUG, "__BBC_FStat: __bbc_fstat error %d\n", rv);
        BBC_LOG(MSG_DEBUG, "__BBC_FStat %s failed\n", name);
        return rv;
    }
    BBC_LOG(MSG_ALL, "__BBC_FStat SUCCESS\n");
    return fd;
}

static unsigned int
cksum(const unsigned char *p, int n)
{
    unsigned int s = 0;
    int i;
    for(i = 0; i < n; i++)
        s += p[i];
    return s;
}

int
__BBC_VerifyFile(bbc_hand *hp, const char *name, const void *buf, int len)
{
    unsigned int hbuf[2];
    unsigned int csum;
    char *rbuf;
    int fd;
    int rv;

    BBC_LOG(MSG_ALL, "__BBC_VerifyFile %s\n", name);

    /* Direct (i.e. BBPlayer connected via USB), do checksum check */
    if (hp->bh_type == BBC_HT_DIRECT) {
        /* BBC_HT_DIRECT */
        csum = cksum((unsigned char *)buf, len);

        /* Sync the FAT first */
        if ((rv = __bbc_sync_fat(hp)) < 0) return rv;
        
        hbuf[0] = REQ_CKSUM_FILE;
        hbuf[1] = strlen(name)+1;
        if ((rv = __bbc_send_cmd(hp->bh_pofd, hbuf, sizeof hbuf)) < 0) return rv;
        if ((rv = __bbc_send_data(hp->bh_pofd, name, RND(strlen(name)+1))) < 0) return rv;
        hbuf[0] = csum;
        hbuf[1] = len;
        if ((rv = __bbc_send_cmd(hp->bh_pofd, hbuf, sizeof hbuf)) < 0) return rv;
        if ((rv = __bbc_read_rsp(hp->bh_pifd, hbuf, sizeof hbuf)) < 0) return rv;
        
        if (hbuf[0] != 255-REQ_CKSUM_FILE) {
            BBC_LOG(MSG_ERR, "__BBC_VerifyFile %s: sync loss on REQ_CKSUM_FILE\n",
                    name);
            return BBC_SYNCLOST;
        }
        if (hbuf[1] != 0) {
            BBC_LOG(MSG_ERR, "VerifyFile %s fails: cksum error\n", name);
            return BBC_VERIFYFAIL;
        }

        BBC_LOG(MSG_DIAG, "__BBC_VerifyFile %s checksum SUCCESS\n", name);

        /* Normally, if checksum okay - we are done.  In diag mode, drop
           through to actually read back the blocks and double check */
        if (!bbc_diag_mode) {
            return BBC_OK;
        }
    }
        
    /* In diag mode or not direct, read back blocks and compare */
    if ((fd = __bbc_fopen(hp, name, "r")) < 0) {
        BBC_LOG_BBCERROR("__BBC_VerifyFile: __bbc_fopen error", fd);
        BBC_LOG(MSG_ERR, "__BBC_VerifyFile %s failed\n", name);
        return fd;
    }
    if ((rbuf = malloc(len)) == NULL) {
        BBC_LOG_SYSERROR("__BBC_VerifyFile: malloc failed");
        BBC_LOG(MSG_ERR, "__BBC_VerifyFile %s failed\n", name);
            return BBC_NOMEM;
    }
    
    if ((rv = __BBC_FRead(hp, fd, 0, rbuf, len)) < len) { 
        BBC_LOG(MSG_ERR, "Verify fread %s length %d returns %d\n", name, len, rv);
        if (rv >= 0) {
            rv = BBC_ERROR;
        }
        else {
            BBC_LOG_BBCERROR("__BBC_VerifyFile: __BBC_FRead error", rv);
        }
        goto err;
    }
    
    if (memcmp(buf, rbuf, len)) {
        BBC_LOG(MSG_ERR, "Verify %s fails: data miscompare\n", name);
        rv = BBC_VERIFYFAIL;
    } else {
        rv = BBC_OK;
        BBC_LOG(MSG_DIAG, "__BBC_VerifyFile %s data compare SUCCESS\n", name);
    }
err:
    free(rbuf);
    BBC_LOG(MSG_DEBUG, "Verify %s returns %d\n", name, rv);
    return rv;
}