/*
 * fifo_k.c -- Routines for reading interally typed fifos.
 * These are all that need to be included in the kernel.
 */
#include <sys/typed.h>

#ifdef KERNEL
/* The input channel from tboot to the kernel.  */
TYPED_SPACE(boot_gift, 512, T_FIFO_SIC);
#endif

/* Read a typed space from a fifo.
 * Return a pointer to the next typed space in the fifo ffp.  Returns
 * NULL on end of fifo.
 *
 * This read assumes that ffp->f_space has type T_FIFO_SIC.
 */
typed_space *
fifo_read(ffp)
	register FIFO *ffp;
{
	typed_space *retval;

	/* Read MUST be set.  */
	if (F_READ != F_READ & ffp->f_flags ) {
		return(T_NULL);  /* This ffp is not open for reading.  */
	}

	/* From here to the end of fifo_read is really fifo_read_sic().  */


	/* Space of size 0 marks EOFIFO.  */
	if ((int32)0 == ffp->f_offset->ts_size) {
		retval = T_NULL;
	} else {
		/* Return the next space.  */
		retval = ffp->f_offset;
		/* Advance to the next space.  */
		(char *) ffp->f_offset += ffp->f_offset->ts_size;
	}

	return(retval);
} /* fifo_read() */

/* Go back to the start of the fifo.
 * Takes a FIFO.  For reading, go back to the first space; for writing
 * truncate the FIFO to empty.
 * Returns 1 on success, 0 otherwise.
 */
int
fifo_rewind(ffp)
	FIFO *ffp;
{
	/* Error checking of arguments.  */
	if (0 == ffp->f_flags) {
		return(0);	/* This ffp is not open.  */
	}

	/* Either reading or writing MUST be set.  */
	if ((F_READ != F_READ & ffp->f_flags) &&
	    (F_WRITE != F_WRITE & ffp->f_flags) ){
		return(0);	/* Illegal flags.  */
	}

	/* Rewind to the first space in this fifo (skip the header).  */
	ffp->f_offset = ((typed_space *) (ffp->f_space)) + 1;

	return(1);
} /* fifo_rewind() */

/* Finish with using a typed space as a fifo.
 * Free up FIFO structure associated with a typed space.
 * Returns 0 if ffp was not open, 1 otherwise.
 */
int
fifo_close(ffp)
	FIFO *ffp;
{
	if (0 == ffp->f_flags) {
		return(0);	/* This ffp is not open.  */
	}
	ffp->f_space = F_NULL;
	ffp->f_offset = T_NULL;
	ffp->f_flags = 0;

	return(1);
} /* fifo_close() */

/* Open a typed space as a fifo.
 *
 * Takes a typed_space that is already allocated, and a mode.    The type of
 * the typed space must be a FIFO.  Only T_FIFO_SIC has been implimented
 * (static, in-core fifo).
 *
 * The mode indicates whether to open for reading or writing.
 *  mode == 0 means read only.
 *  mode == 1 means write only.
 *  Other values are illegal.
 *
 * Returns a pointer to an initialized FIFO structure.  FIFO structures are
 * allocated from a pre-allocated array.  Returns F_NULL if it can't open
 * the fifo.
 */
FIFO *
fifo_open(fifo_space, mode)
	typed_space *fifo_space;
	int mode;
{
	/* ff_table is a table of FIFO structures which can be allocated on
	 * demand.  It is functionally similiar to the file descritor table
	 * in the kernel.
	 */
	static FIFO ff_table[NFIFOS];
	static int inited = (1==2);	/* Has ff_table been initialized?  */

	int i;		/* A handy counter.  */
	FIFO *the_fifo;	/* The fifo we are going to allocate.  */

	/* Initilize ff_table the first time we get called.  */
	if (!inited) {
		for (i = 0; i < NFIFOS; ++i) {
			ff_table[i].f_space = F_NULL;
			ff_table[i].f_flags = 0;
		}
		inited = (1==1);
	}

	/* Check the type of the space we were passed.  */
	switch (fifo_space->ts_type) {
	case T_FIFO:	/* Overly general type, assuming SIC.  */
		fifo_space->ts_type = T_FIFO_SIC;
		break;
	case T_FIFO_SIC:/* Static In-core Fifo.  */
		break;
	case T_FIFO_DIC:/* Dynamic In-core Fifo (can grow).  */
		return(F_NULL);	/* Unimplimented.  */
	case T_FIFO_SP:	/* Static Permanent Fifo (fixed size file). */
		return(F_NULL);	/* Unimplimented.  */
	case T_FIFO_DP:	/* Dynamic Permanent Fifo (ordinary file).  */
		return(F_NULL);	/* Unimplimented.  */
	default:
		return(F_NULL);	/* Illegal type encountered.  */
	}


	/* Is this space at least big enough for an empty fifo?  */
	if (fifo_space->ts_size < 2 * (sizeof(typed_space))) {
		return(F_NULL);
	}

	/* ASSERTION: fifo_space is a valid and implimented FIFO.  */

	/* Find the first free FIFO structure.  */

	/* This should be re-implimented using a malloc-based scheme.
	 * At the moment, the tertiary boot libraries do not include a
	 * malloc.
	 */
	for (i = 0; (i < NFIFOS) && (0 != ff_table[i].f_flags); ++i) {
		/* Do nothing else.  */
	}

	if (NFIFOS == i) {
		return(F_NULL);	/* No more free fifo structs.  */
	}

	the_fifo = &(ff_table[i]);

	/* ASSERTION: the_fifo points at a FIFO we can take.  */

	/* Initialize the FIFO struct.  */
	the_fifo->f_space = fifo_space;
	the_fifo->f_offset = fifo_space->ts_data;

	/* Note that an open for write does not truncate.  */

	/* Initilize the flags.  */
	switch(mode) {
	case 0:	/* read */
		the_fifo->f_flags |= F_READ;
		break;
	case 1:	/* write */
		the_fifo->f_flags |= F_WRITE;
		break;
	default:
		return(F_NULL);	/* Illegal mode flag.  */
	}
		
	return(the_fifo);
} /* fifo_open() */


#ifdef TEST
#include <stdio.h>

/* This is the typed space we will use for our FIFO operations.  */
TYPED_SPACE(global_space, 128, T_FIFO_SIC);	/* Static In-Core Fifo.  */

int
main()
{
	FIFO *ffp;		/* Fifo pointer for a handle.  */
	char line[1024];	/* Place to put input lines.  */
	long size;		/* Length for line.  (Sizes are all long.)  */
	int i;

	typed_space *local_space;

	/* Open the fifo for writing.  */
	if (F_NULL == (ffp = fifo_open(&global_space, 1))) {
		fprintf(stderr, "Can't open global_space for writing.\n");
		exit(1);
	}

	do {
		printf("Feed me: ");
		gets(line);
		size = (int32) (strlen(line) + 1);
	} while (T_NULL != fifo_write_untyped(ffp, line, size, T_STR_STR));

	if (0 == fifo_close(ffp)) {
		fprintf(stderr, "Failed to close global_space.\n");
		exit(1);
	}

	printf("OK, global_space is now full.\n");
	/* ASSERTION: We've filled global_space with strings.  */

	/* Open the fifo for reading.  */
	if (F_NULL == (ffp = fifo_open(&global_space, 0))) {
		fprintf(stderr, "Can't open global_space for reading.\n");
		exit(1);
	}

	/* Dump the contents of this FIFO.  */
	for (i = 1; T_NULL != (local_space = fifo_read(ffp)); ++i) {
		printf("%d: size: %ld: type: 0x%x\n", i,
			local_space->ts_size,
			local_space->ts_type);

		/* Assume everything is a NUL terminated string.  */
		printf("datum: %s\n\n", local_space->ts_data);
		printf("Hit <RETURN>");
		gets(line);
	}


	/* Rewind the file, and dump it out again.  */
	printf("Rewinding.\n");
	if (0 == fifo_rewind(ffp)) {
		fprintf("Can't rewind global_space.\n");
		exit(1);
	}

	for (i = 1; T_NULL != (local_space = fifo_read(ffp)); ++i) {
		printf("%d: size: %ld: type: 0x%x\n", i,
			local_space->ts_size,
			local_space->ts_type);

		/* Assume everything is a NUL terminated string.  */
		printf("datum: %s\n\n", local_space->ts_data);
	}

	if (0 == fifo_close(ffp)) {
		fprintf(stderr, "Failed to close global_space.\n");
		exit(1);
	}

	exit(0);
} /* main() */

#endif /* TEST */
