/* Copyright (c)1994-2000 Begemot Computer Associates. All rights reserved.
 * See the file COPYRIGHT for details of redistribution and use. */

/*
 * line printer
 *
 * Arguments:
 *	ctrl lp csr_base vector irq
 *	  | prog-name args ...
 * or
 *	  file-name
 */

# include "proc.h"

RCSID("$Id: dev_lp.c 454 2000-11-24 10:34:24Z hbb $")

void 	pipeto(int, char *[], pid_t *, int *, char *);

typedef struct LP LP;

struct LP {
	u_int	csr_base;
	u_short vector;
	u_short ireq;
	int	lpint;		/* interrupt requested */
	
	u_char	lpbuf;
	u_short	lpcsr;

	pid_t	pid;
	int	fd;
	LP	*next;
};

void	lp_ctrl_create(iodev_t *, int, char **);
void	lp_dev_create(iodev_t *, int, char **);
void	lp_ctrl_complete(iodev_t *);
void	lp_reset(iodev_t *);
u_short	lp_fetch(iodev_t *, u_int);
void	lp_store(iodev_t *, u_int, mmode_t, u_short);
u_short	lp_vector(iodev_t *);
void	lp_info(iodev_t *);
void	lp_kill(void);
void	lp_command(iodev_t *, int, char **);

static int	lpc_info(iodev_t *, int argc, char **);
static int	lpc_eject(iodev_t *, int argc, char **);

iocmd_t lp_cmds[] = {
	{ "?",		"[command ...]",	dev_help },
	{ "help",	"[command ...]",	dev_help },
	{ "info",	"",			lpc_info },
	{ "eject",	"",			lpc_eject },
	{ NULL,		NULL,			NULL }
};

ioops_t	lp_ops = {
	lp_ctrl_create,		/* ctrl_create */
	lp_dev_create,		/* dev_create */
	lp_ctrl_complete,	/* ctrl_complete */
	lp_reset,		/* reset */
	lp_fetch,		/* fetch */
	lp_store,		/* store */
	lp_vector,		/* vector */
	0,			/* dma */
	0,			/* onsig */
	lp_info,		/* info */
	0,			/* flush */
	lp_cmds,		/* cmds */
};

static LP	*lps = 0;

/*
 * create controller for parallel interface
 * - nothing to do
 */
void
lp_ctrl_create(iodev_t *dev, int argc, char **argv)
{
	LP *lp;

	if(argc != 3)
		conf_panic("lp: need 3 args in controller configuration");
	lp = dev->data = xalloc(sizeof(LP));
	(void)memset(lp, 0, sizeof(LP));
	lp->next = lps;
	lps = lp;

	lp->csr_base = parse_csr(argv[0], "lp");
	lp->vector = parse_vec(argv[1], "lp");
	lp->ireq = parse_irq(argv[2], "lp");

	proc.iopage[IOP(lp->csr_base+0)] = dev;
	proc.iopage[IOP(lp->csr_base+2)] = dev;
}

/*
 * create device
 */
void
lp_dev_create(iodev_t *dev, int argc, char **argv)
{
	LP *lp = dev->data;

	if(argc == 0)
		conf_panic("lp: missing args in device configuration");

	if(!strcmp(argv[0], "|")) {
		/*
		 * pipe into program
		 */
		pipeto(--argc, ++argv, &lp->pid, &lp->fd, "lp");
		atexit(lp_kill);

	} else {
		/*
		 * write to file
		 */
		if(argc != 1)
			conf_warning("lp: too many args in dev config\n");

		if((lp->fd = open(argv[0], O_WRONLY)) < 0)
			conf_panic("lp: cannot open %s: %s\n", argv[0], strerror(errno));
		lp->pid = 0;
	}
}


void	
lp_ctrl_complete(iodev_t *dev UNUSED)
{
}

/*
 * kill output processes
 */
void
lp_kill(void)
{
	LP *lp = lps;

	while(lp) {
		if(lp->pid)
			kill(lp->pid, SIGINT);
		lp = lp->next;
	}
}

/*
 * bus reset
 */
void	
lp_reset(iodev_t *dev)
{
	LP *lp = dev->data;

	lp->lpint = 0;
	lp->lpbuf = 0;
	lp->lpcsr = 0200;
}

/*
 * fetch from csr/buf
 */
u_short	
lp_fetch(iodev_t *dev, u_int a)
{
	LP *lp = dev->data;

	if(a == lp->csr_base)
		return lp->lpcsr;
	else if(a == lp->csr_base + 2)
		return lp->lpbuf;
	printf("lp_fetch(%o)\n", a);
	Trap4(0100);
}

/*
 * store into csr/buffer 
 */
void	
lp_store(iodev_t *dev, u_int a, mmode_t mode, u_short v)
{
	LP *lp = dev->data;
	char c;

	if(a == lp->csr_base) {
		if(mode == M_High)
			return;
		if(!(lp->lpcsr & 0100) && (v & 0100)) {
			IRQ(dev, lp->ireq);
			lp->lpint = 1;
		}
		lp->lpcsr = (v & 0100) | 0200;
		return;
	}

	if(a == lp->csr_base + 2) {
		if(mode == M_High)
			return;
		c = v;
		write(lp->fd, &c, 1);
		if(lp->lpcsr & 0100) {
			IRQ(dev, lp->ireq);
			lp->lpint = 1;
		}
		return;
	}

	printf("lp_store(%o)\n", a);
	Trap4(0100);
}

/*
 * return vector on interrupt
 */
u_short	
lp_vector(iodev_t *dev)
{
	LP *lp = dev->data;

	if(!lp->lpint)
		return 04;
	dev->reqpri = 0;
	return lp->vector;
}

/*
 * print info about device
 */
void	
lp_info(iodev_t *dev)
{
	LP *lp = dev->data;

	printf("LP parallel interface\n");
	printf("CSR at %08o: %06o\n", lp->csr_base, lp->lpcsr);
	printf("BUF at %08o: %06o\n", lp->csr_base+2, lp->lpbuf);
}


/*
 * create a pipe
 */
void
pipeto(int argc, char *argv[], pid_t *ppid, int *pfd, char *s)
{
	int p[2];
	char *arg;
	int len, i;

	if(argc == 0)
		conf_panic("%s: nothing to pipe", s);

	/*
	 * collect args
	 */
	for(i = len = 0; i < argc; i++)
		len += strlen(argv[i]);
	len += argc;
	len += 5;	/* "exec " */
	arg = xalloc(len+1);		/* get one more */
	strcpy(arg, "exec ");
	for(i = 0; i < argc; i++) {
		strcat(arg, argv[i]);
		strcat(arg, " ");
	}

	if(socketpair(PF_UNIX, SOCK_STREAM, 0, p) < 0)
		conf_panic("%s: can't create pipe: %s", s, strerror(errno));

	if((*ppid = fork()) == 0) {
		/*
		 * child
		 */
		int fd;
		static char *av[4] = { _PATH_BSHELL, "-c", 0, 0 };

		close(0);
		dup(p[0]);
		for(fd = 3; fd < getdtablesize(); fd++)
			close(fd);
		av[2] = arg;
		signal(SIGINT, SIG_IGN);
		signal(SIGQUIT, SIG_IGN);
		execv(av[0], av);
		panic("%s: can't exec %s: %s", s, av[0], strerror(errno));
	}
	if(*ppid < 0)
		conf_panic("%s: cannot fork\n", s);
	close(p[0]);
	*pfd = p[1];
	free(arg);
}

static int
lpc_info(iodev_t *dev, int argc, char **argv UNUSED)
{
	if(argc != 0)
		return 1;
	lp_info(dev);
	return 0;
}

static int
lpc_eject(iodev_t *dev, int argc, char **argv UNUSED)
{
	LP *lp = dev->data;

	if(argc != 0)
		return 1;
	if(lp->pid == 0) {
		printf("lp: no process associated with device");
		return 0;
	}
	kill(lp->pid, SIGUSR1);
	return 0;
}
