bbcdirif.c 10.9 KB
#include "bbclocal.h"

static void
__bbc_direct_close(void *f)
{
}

static int
__bbc_direct_setled(void *f, int ledmask)
{
    bbc_hand *hp = f;
    unsigned int hbuf[2];
    int rv;

    hbuf[0] = REQ_SET_LEDS;
    hbuf[1] = ledmask;
    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_SET_LEDS) {
	BBC_LOG(MSG_ERR, "__bbc_direct_setled: sync loss on REQ_SET_LEDS\n");
	return BBC_SYNCLOST;
    }
    return BBC_OK;
}

#define BYTE(x)	((u8)((x) & 0xff))

static int
__bbc_direct_settime(void *f, time_t curtime)
{
    bbc_hand *hp = f;
    unsigned int hbuf[3];
    struct tm *tm;
#ifndef WIN32
    struct tm tms;
    struct timespec ts;
#endif
    int rv;

    /*
     * Convert to local time in China.  Note that on the depot, the
     * timezone is not set so we hardwire the local time conversion
     * and use gmtime_r to crack it into components to be safe.
     */
    curtime += 8*60*60;		/* PRC time zone = UTC + 8 hours */
#ifndef WIN32
    tm = gmtime_r(&curtime, &tms); /* gmtime_r doesn't change TZ */
#else
    tm = gmtime(&curtime);     /* gmtime doesn't change TZ */
#endif
    if (tm == NULL) {
	BBC_LOG(MSG_ERR, "__bbc_direct_settime: gmtime failed\n");
	return BBC_ERROR;
    }
    tm->tm_mon += 1;		/* tm_mon ranges from 0 to 11 */
    if (tm->tm_year > 100)	/* tm_year is years since 1900 */
	tm->tm_year -= 100;
    if (tm->tm_wday == 0)
	tm->tm_wday = 7;		/* RTC uses 1 = Mon, ... , 7 = Sun */

    hbuf[0] = REQ_SET_TIME;
    hbuf[1] = BYTE(tm->tm_year)<<24 | BYTE(tm->tm_mon)<<16
	      | BYTE(tm->tm_mday)<<8 | BYTE(tm->tm_wday);
    hbuf[2] = BYTE(tm->tm_hour)<<16 | BYTE(tm->tm_min)<<8 | BYTE(tm->tm_sec);

    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_SET_TIME) {
	BBC_LOG(MSG_ERR, "__bbc_direct_settime: sync loss on REQ_SET_TIME\n");
	return BBC_SYNCLOST;
    }
    if ((rv = __bbc_send_cmd(hp->bh_pofd, &hbuf[2], 4)) < 0) return rv;

#ifndef WIN32
    /*
     * Setting the RTC on BB takes a few milliseconds.  Make
     * sure that the calling program stalls long enough for it
     * to complete before proceeding.  Needless to say, this is
     * a kludge.  The protocol should have been synchronous at 
     * this level, but changing it now is too messy.
     */
again:
    ts.tv_sec = 1;
    ts.tv_nsec = 0;
    while (nanosleep(&ts, &ts) < 0) {
        BBC_LOG(MSG_ERR, "nanosleep interrupted: sec %ld, nsec %ld\n", ts.tv_sec, ts.tv_nsec);
        if (ts.tv_sec < 0 || ts.tv_sec > 1 || ts.tv_nsec < 0 || ts.tv_nsec > 999999999L)
	    goto again;
    }
#endif
    return BBC_OK;
}

static unsigned char junk[BB_FL_BLOCK_SIZE];

static int
__bbc_direct_read_blocks(void* f, u32 blk, int nblks, void* data, void* spare)
{
    bbc_hand *hp = f;
    unsigned int hbuf[2], r;
    int rv;

    BBC_LOG(MSG_DEBUG, "drb: READ_BLOCKS %d count %d (spare 0x%x)\n",
            (int)blk, nblks, (int)spare);
    /* Initialize spare area */
    if (spare) {
        memset(spare, 0xff, nblks * BB_FL_SPARE_SIZE);
    }
    while (nblks-- > 0) {
        hbuf[0] = spare ? REQ_READ_BLOCK_SP : REQ_READ_BLOCK;
        r = 255-hbuf[0];
        hbuf[1] = blk;
        if ((rv = __bbc_send_cmd(hp->bh_pofd, hbuf, 8)) < 0) {
            BBC_LOG(MSG_ERR, "drd: read block %ld: send cmd %d failed with %d\n",
                    blk, hbuf[0], rv);
            return rv;
        }
        if ((rv = __bbc_read_rsp(hp->bh_pifd, hbuf, 8)) < 0) {
            BBC_LOG(MSG_ERR, "drd: read block %ld: read rsp failed with %d\n",
                    blk, rv);
            return rv;
        }
        if (data == NULL) data = junk;
        if ((rv = __bbc_read_data(hp->bh_pifd, data, BB_FL_BLOCK_SIZE)) < 0) {
            BBC_LOG(MSG_ERR, "drd: read block %ld: read data (block) failed with %d\n",
                    blk, rv);
            return rv;
        }
        if (spare) 
            if ((rv = __bbc_read_data(hp->bh_pifd, spare, BB_FL_SPARE_SIZE)) < 0) {
                BBC_LOG(MSG_ERR, "drd: read block %ld: read data (spare) failed with %d\n",
                        blk, rv);
                return rv;
            }

        if (hbuf[0] != r) {
            BBC_LOG(MSG_ERR, "drd: read block %ld failed (sync loss)\n", blk);
            return BBC_SYNCLOST;
        }
        else if (hbuf[1] != 0) {
            BBC_LOG(MSG_ERR, "drd: read block %ld failed with error %d\n",
                    blk, hbuf[1]);
            if ((hbuf[1] == BBCARD_ERR_FAIL) && (spare != NULL)) {
                /*
		 * Check first to see if block marked bad by manufacturer.
		 * Note that ECC is not computed on the spare area, so single
		 * bit errors on the status byte will not be corrected.  To
		 * match the way the ROM boot code handles the status byte, 
		 * only consider the block marked bad if the status byte 
		 * contains more than one zero bit.
		 */
                unsigned char *sparep = (unsigned char *)spare;
                if (__bbc_zerobits(sparep[BB_FL_BLOCK_STATUS_OFF]) > 1) {
                    BBC_LOG(MSG_WARNING, "drd: Blk %ld marked bad by MFG (status %d)\n", blk, 
                        sparep[BB_FL_BLOCK_STATUS_OFF]);
                    return BBC_BADBLK;
                }
            }

            /* Try to map the error codes we got into libbbc error codes */
            switch (hbuf[1]) {
                case BBCARD_ERR_FAIL:     return BBC_DATAERR;
                case BBCARD_ERR_NO_CARD:  return BBC_NOCARD;
                case BBCARD_ERR_CHANGED:  return BBC_CARDCHANGE;

                case BBCARD_ERR_INVALID:
                default: return BBC_ERROR; /* Some unknown error */
            }
        }

	/*
	 * Need to check spare area here also, since a marked bad block
	 * may read without DBE (XXX check Samsung spec).
	 */
        if (spare) {
            /* Check to see if block marked bad by manufacturer */
            unsigned char *sparep = (unsigned char *)spare;
            if (__bbc_zerobits(sparep[BB_FL_BLOCK_STATUS_OFF]) > 1) {
                BBC_LOG(MSG_WARNING, "drd: Blk %ld marked bad by MFG (status %d)\n", blk, 
                    sparep[BB_FL_BLOCK_STATUS_OFF]);
                return BBC_BADBLK;
            }
        }

 	blk++;
	if (data && data != junk)
	    (char *)data += BB_FL_BLOCK_SIZE;
	if (spare)
	    (char *)spare += BB_FL_SPARE_SIZE;
    }
    
    return BBC_OK;
}

/*
 * write blocks to the flash device.  if spare is non-zero, use it
 * as the spare area data for all pages in the block.
 */
static int
__bbc_direct_write_blocks(void* f, u32 blk, int nblks, const void* data, void* spare)
{
    bbc_hand *hp = f;
    unsigned int hbuf[2], r;
    int rv;

    BBC_LOG(MSG_DEBUG, "dwb: WRITE_BLOCK %d count %d spare 0x%x\n",
            (int)blk, nblks, (int)spare);
    while (nblks-- > 0) {
        hbuf[0] = spare ? REQ_WRITE_BLOCK_SP : REQ_WRITE_BLOCK;
        r = 255-hbuf[0];
        hbuf[1] = blk;
        if ((rv = __bbc_send_cmd(hp->bh_pofd, hbuf, 8)) < 0) {
            BBC_LOG(MSG_ERR, "dwb: write block %ld: send cmd %d failed with %d\n",
                    blk, hbuf[0], rv);
            return rv;
        }
        if ((rv = __bbc_send_data(hp->bh_pofd, data, BB_FL_BLOCK_SIZE)) < 0) {
            BBC_LOG(MSG_ERR, "dwb: write block %ld: send data (block) failed with %d\n",
                    blk, rv);
            return rv;
        }
        if (spare)
	    if ((rv = __bbc_send_data(hp->bh_pofd, spare, BB_FL_SPARE_SIZE)) < 0) {
                BBC_LOG(MSG_ERR, "dwb: write block %ld: send data (spare) failed with %d\n",
                        blk, rv);
                return rv;
            }
        if ((rv = __bbc_read_rsp(hp->bh_pifd, hbuf, 8)) < 0) {
            BBC_LOG(MSG_ERR, "dwb: write block %ld: read rsp failed with %d\n",
                    blk, rv);
            return rv;
        }
        if (hbuf[0] != r) {
            BBC_LOG(MSG_ERR, "dwb: write block %ld failed (sync loss was 0x%x not 0x%x)\n", blk, hbuf[0], r);
            return BBC_SYNCLOST;
        }
        else if (hbuf[1] != 0) {
            BBC_LOG(MSG_ERR, "dwb: write block %ld failed with error %d\n",
                    blk, hbuf[1]);

            /* Try to map the error codes we got into libbbc error codes */
            switch (hbuf[1]) {
                case BBCARD_ERR_FAIL:     return BBC_DATAERR;
                case BBCARD_ERR_NO_CARD:  return BBC_NOCARD;
                case BBCARD_ERR_CHANGED:  return BBC_CARDCHANGE;

                case BBCARD_ERR_INVALID:
                default: return BBC_ERROR; /* Some unknown error */
            }
        }
	blk++;
	(char *)data += BB_FL_BLOCK_SIZE;
	if (spare)
	    (char *)spare += BB_FL_SPARE_SIZE;
    }

    return BBC_OK;
}

int
#ifndef WIN32
__bbc_new_direct(bbc_hand *hp, const char *device)
#else
__bbc_new_direct(bbc_hand *hp)
#endif
{
    flashif_t* f;
#ifndef WIN32
    int fd;
#else
    HANDLE hUsbWrite = INVALID_HANDLE_VALUE;
    HANDLE hUsbRead = INVALID_HANDLE_VALUE;
    char usbReadName[512], usbWriteName[512];
    int bbrDevices=0, bbwDevices=0;
    int rv;
#endif

#ifndef WIN32
    BBC_LOG(MSG_DEBUG, "__bbc_new_direct %s\n", device);
    if ((fd = open(device, O_RDWR)) < 0) {
	perror(device);
	return BBC_NODEV;
    }
    if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) {
	BBC_LOG_SYSERROR("__bbc_new_direct: fcntl failed");
    }
    hp->bh_ufd = fd;
#else
    hUsbWrite = open_usb_pipe(RDB_OUT_PIPE, usbWriteName, &bbrDevices);
    if (hUsbWrite != INVALID_HANDLE_VALUE)
        hUsbRead = open_usb_pipe(RDB_IN_PIPE, usbReadName, &bbwDevices);

    if (bbrDevices>1 || bbwDevices>1) {
        BBC_LOG(MSG_DEBUG, "Multiple BB USB device\n");
        if (hUsbWrite != INVALID_HANDLE_VALUE) 
            CloseHandle(hUsbWrite);
        if (hUsbRead != INVALID_HANDLE_VALUE) 
            CloseHandle(hUsbRead);
        return BBC_MULTIPLE_BB;
    } else if (hUsbWrite == INVALID_HANDLE_VALUE || hUsbRead == INVALID_HANDLE_VALUE) {
        BBC_LOG(MSG_DEBUG, "Cannot open USB device\n");
        if (GetLastError() == ERROR_SHARING_VIOLATION) {
            rv = BBC_DEVBUSY;
        }
        else {
            rv = BBC_NODEV;
        }
        if (hUsbWrite != INVALID_HANDLE_VALUE) 
            CloseHandle(hUsbWrite);
        if (hUsbRead != INVALID_HANDLE_VALUE) 
            CloseHandle(hUsbRead);
        return rv;
    } 

    hp->bh_uwh = hUsbWrite;
    hp->bh_urh = hUsbRead;
    strncpy(hp->bh_readname, usbReadName, sizeof(hp->bh_readname));
    strncpy(hp->bh_writename, usbWriteName, sizeof(hp->bh_writename));
#endif

    if ((f = malloc(sizeof(flashif_t))) == NULL) {
        BBC_LOG_SYSERROR("__bbc_new_direct: malloc failed");
#ifndef WIN32
	close(fd);
#else
        CloseHandle(hUsbWrite);
        CloseHandle(hUsbRead);
#endif
	return BBC_NOMEM;
    }
    hp->bh_flashif = f;
    memset(f, 0, sizeof(flashif_t));
    f->f = hp;
    f->close = __bbc_direct_close;
    f->setled = __bbc_direct_setled;
    f->settime = __bbc_direct_settime;
    f->read_blocks = __bbc_direct_read_blocks;
    f->write_blocks = __bbc_direct_write_blocks;
    return BBC_OK;
}