bbcinit.c 8.78 KB
#include "bbclocal.h"

bbc_hand *handles[BBC_MAX_DEVS];

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 int domux(const char *, int, int, int);

static void
pipehandler(int sig)
{
    BBC_LOG(MSG_INFO, "Got SIGPIPE\n");
    signal(SIGPIPE, pipehandler);
}

struct {
    int    (*init)(bbc_hand *, const char *);
} initfunc[] = {
    { __bbc_new_reader },
    { __bbc_new_direct },
    { NULL },	/* no ifc for mux only */
    { __bbc_new_file },
};

BBCHandle
BBCInit(const char *device, int callType)
{
    int fd, h, pid, rv, type;
    int inpipe[2];
    int outpipe[2];
    struct sigaction newaction;
    struct stat sb;
    bbc_hand *hp;

    /*
     * Look at the inode to determine which type of device this is
     */ 
    if ((rv = stat(device, &sb)) < 0) {
        BBC_LOG_SYSERROR(device);
        return BBC_NOCARD;
    }
    if (S_ISCHR(sb.st_mode)) {
        type = (major(sb.st_rdev) == BBC_SCSI_MAJOR) ? BBC_HT_READER :
               ((major(sb.st_rdev) == BBC_USB_MAJOR) ? BBC_HT_DIRECT :
	       BBC_HT_UNKNOWN);
    } else if (S_ISREG(sb.st_mode))
	type = BBC_HT_FILE;
    else
	type = BBC_HT_UNKNOWN;

    BBC_LOG(MSG_DIAG, "BBCInit: %s type %d\n", device, type);

    if (type == BBC_HT_UNKNOWN) {
	BBC_LOG(MSG_ERR, "%s: unrecognized file type\n", device);
	return BBC_NODEV;
    }

    /*
     * Allocate handle and call the appropriate init routine
     */
    if ((h = find_handle()) < 0) {
	BBC_LOG_SYSERROR("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, device);
    if (rv != BBC_OK) {
	fd = -1;
	goto err1;
    }
    fd = hp->bh_ufd;
    strncpy(hp->bh_devname, device, sizeof (hp->bh_devname));
    /*
     * 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) < 0) {
	BBC_LOG_SYSERROR("pipe");
	rv = BBC_ERROR;
	goto err2;
    }
    if (pipe(outpipe) < 0) {
	BBC_LOG_SYSERROR("pipe");
	rv = BBC_ERROR;
	goto err3;
    }
    if ((pid = fork()) == 0) {
	prctl(PR_SET_PDEATHSIG, SIGTERM);
	newaction.sa_handler = SIG_DFL;
	sigaction(SIGTERM, &newaction, NULL);
	fflush(stdout);
	fflush(stderr);
	(void) close(inpipe[0]);
	(void) close(outpipe[1]);
	(void) domux(hp->bh_devname, fd, outpipe[0], inpipe[1]);
	myexit(0);
    } else if (pid < 0) {
	BBC_LOG_SYSERROR("BBCInit: fork failed");
	rv = BBC_ERROR;
	goto err4;
    }
    BBC_LOG(MSG_DEBUG, "%d: created mux process %d\n", getpid(), pid);
    fflush(stdout);
    fflush(stderr);

    hp->bh_muxpid = pid;
    hp->bh_pifd = inpipe[0];
    hp->bh_pofd = outpipe[1];
    (void) close(inpipe[1]);
    (void) close(outpipe[0]);
    signal(SIGPIPE, pipehandler);

    /*
     * Read printserver pid from mux
     */
#if 0
    { int pserv_pid, ct;
	ct = read(hp->bh_pifd, &pserv_pid, 4);
	if (ct < 4)
	    BBC_LOG(MSG_DEBUG, "first read from mux returns %d\n", ct);
	else
	    BBC_LOG(MSG_DEBUG, "%d: mux returns printserver pid %d\n", getpid(), pserv_pid);
    }
#endif

    /*
     * Check for the presence of a card and init sequence number
     */
    if ((rv = __BBC_SetCardSeqno(hp)) < 0) {
	/*
	 * 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) {
            BBC_LOG(MSG_DEBUG, "BBCInit: SetSeqno ret %d\n", rv);
	    return h;
        }
	BBC_LOG(MSG_ERR, "BBCInit: SetSeqno ret %d\n", rv);
	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)
        free(hp->bh_flashif);
    free(hp);
    handles[h] = NULL;
    close(fd);
    BBC_LOG(MSG_DIAG, "BBCInit error %d\n", rv);
    return rv;
}

/*
 * This call is intended to allow emsh to work
 * with an older usbmon, which doesn't support
 * all the commands supplied by emsmon.
 */
BBCHandle
BBCOpenMux(const char *device, int callType)
{
    int fd, h, pid, rv;
    int inpipe[2];
    int outpipe[2];
    struct sigaction newaction;
    bbc_hand *hp;
    
    if ((fd = open(device, O_RDWR)) < 0) {
	BBC_LOG_SYSERROR(device);
	return BBC_NOCARD;
    }
    if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) {
	BBC_LOG_SYSERROR("BBCOpenMux: fcntl failed");
    }

    /*
     * For direct USB, the open fails if no player
     * is present.  For the BBC card reader, we need
     * to perform io to discover whether a card is
     * present.
     */
    if ((h = find_handle()) < 0) {
	BBC_LOG_SYSERROR("BBCOpenMux: out of handles");
	rv = BBC_TOOMANY;
	goto err0;
    }

    if ((hp = malloc(sizeof (bbc_hand))) == NULL) {
	BBC_LOG_SYSERROR("BBCOpenMux: malloc failed");
	rv = BBC_NOMEM;
	goto err0;
    }

    handles[h] = hp;
    memset(hp, 0, sizeof (bbc_hand));
    hp->bh_type = BBC_HT_MUX_ONLY;
    hp->bh_ufd = fd;
    strncpy(hp->bh_devname, device, sizeof (hp->bh_devname));

    if (pipe(inpipe) < 0) {
	BBC_LOG_SYSERROR("BBCOpenMux: pipe failed");
	rv = BBC_ERROR;
	goto err1;
    }
    if (pipe(outpipe) < 0) {
	BBC_LOG_SYSERROR("BBCOpenMux: pipe failed");
	rv = BBC_ERROR;
	goto err2;
    }
    if ((pid = fork()) == 0) {
	prctl(PR_SET_PDEATHSIG, SIGTERM);
	newaction.sa_handler = SIG_DFL;
	sigaction(SIGTERM, &newaction, NULL);
	fflush(stdout);
	fflush(stderr);
	(void) close(inpipe[0]);
	(void) close(outpipe[1]);
	(void) domux(hp->bh_devname, fd, outpipe[0], inpipe[1]);
	myexit(0);
    } else if (pid < 0) {
	BBC_LOG_SYSERROR("BBCOpenMux: fork failed");
	rv = BBC_ERROR;
	goto err3;
    }

    hp->bh_muxpid = pid;
    hp->bh_pifd = inpipe[0];
    hp->bh_pofd = outpipe[1];
    (void) close(inpipe[1]);
    (void) close(outpipe[0]);

    return h;

err3:
    (void) close(outpipe[0]);
    (void) close(outpipe[1]);
err2:
    (void) close(inpipe[0]);
    (void) close(inpipe[1]);
err1:
    (void) free(hp);
    handles[h] = NULL;
err0:
    (void) close(fd);
    return rv;
}

static void
__bbc_kill_mux(bbc_hand *hp)
{
    int i, rv;
    int deathcookie = 0xdeadbeef;

    /*
     * Kill the mux process and clean up the
     * zombie.  Note that the print server
     * process will be killed as a side-effect
     * and it will become an orphan and get
     * reaped by init.
     *
     * Can't just blow away mux, since that creates a race
     * with the last command going out to mon to wake up
     * the writer in osWriteHost.
     *
     * Send special death cookie through the pipe that mux
     * will notice when it is ready to process the next command.
     */
    if (write(hp->bh_pofd, &deathcookie, 4) < 4)
	BBC_LOG(MSG_WARNING, "write of deathcookie fails, mux already gone?\n");

    for (i = 0; i < 20; i++) {
        rv = waitpid(hp->bh_muxpid, NULL, 0);
	BBC_LOG(MSG_INFO, "%d: waitpid returns %d\n", getpid(), rv);
	if (rv == hp->bh_muxpid)
	    break;
	if (rv < 0 && errno != EINTR)
	    break;
    }
}

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

    BBC_LOG(MSG_DEBUG, "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);
        (void) close(hp->bh_ufd);
        (void) close(hp->bh_pifd);
        (void) close(hp->bh_pofd);
    } 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;

    return BBC_OK;
}