/*
  This program is provided AS IS. 
  Copyright (c) 2000,2001 Noriaki Mitsunaga
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <err.h>
#include <ctype.h>
#include <errno.h>
#include <libusb.h>
#include <dev/usb/usb.h>
#include <dev/usb/usbhid.h>

#undef DEBUG

#define USB_IO_OUT0 0x1
#define USB_IO_OUT1 0x2
#define USB_IO_IN0  0x3
#define USB_IO_IN1  0x4

#define DATA	0x1
#define RD	0x2
#define STB	0x4

void usb_io_out(int port, int data, int fno, hid_item_t *hi);


void lcd_cmdout8(int port, int data,
		int fno, hid_item_t *hi) {
	data &= 0xff;
	usb_io_out(port, data, fno, hi);
	usb_io_out(port, data | STB, fno, hi);
	usb_io_out(port, data, fno, hi);
}

void lcd_cmdout(int port, int data,
		int fno, hid_item_t *hi) {
	int upper = data & 0xf0;
	int lower = (data & 0xf)<<4;

	usb_io_out(port, upper, fno, hi);
	usb_io_out(port, upper | STB, fno, hi);
	usb_io_out(port, upper, fno, hi);

	usb_io_out(port, lower, fno, hi);
	usb_io_out(port, lower | STB, fno, hi);
	usb_io_out(port, lower, fno, hi);
}

void lcd_dataout(int port, int data,
		int fno, hid_item_t *hi) {
	int upper = data & 0xf0;
	int lower = (data & 0xf)<<4;

	usb_io_out(port, upper | DATA, fno, hi);
	usb_io_out(port, upper | DATA| STB, fno, hi);
	usb_io_out(port, upper | DATA, fno, hi);

	usb_io_out(port, lower | DATA, fno, hi);
	usb_io_out(port, lower | DATA | STB, fno, hi);
	usb_io_out(port, lower | DATA, fno, hi);
}

void usb_io_out(int port, int data,
		int fno, hid_item_t *hi) {
  int i;
	char bf[BUFSIZ];
	int size;
	u_char buf[8];
	int len=8;

  if (len<2)
    return;

  hid_set_data(buf, hi, port & 0xff);
  hid_set_data(buf+1, hi, data & 0xff);
  for (i=2; i<len; i++)
    hid_set_data(buf+i, hi, 0);

  write(fno, buf, len);
#if 0
	printf("out: ");
	for(i=0; i<len; i++)
		printf("%x ", buf[i]);

	printf("got: ");
	size = read(fno, bf, BUFSIZ);
	for(i=0; i<size; i++)
		printf("%x ", bf[i] & 0xff);
	printf("\n");
#endif
}

int usb_io_in(int port, int fno, hid_item_t *shi, hid_item_t *rhi) {
  int i, pos;
  u_char buf[BUFSIZ];
	char bf[BUFSIZ];
	int size;
	u_char sbuf[8], rbuf[8];
	int slen = 8;
	int rlen = 8;

  if (slen<2)
    return -1;

  /* flush buffer */
 /* read(fno, buf, BUFSIZ); */

  hid_set_data(sbuf, shi, port & 0xff);
  for (i=1; i<slen; i++)
    hid_set_data(sbuf+i, shi, 0);

  write(fno, sbuf, slen);

  /* read(fno, rbuf, rlen);
*/
#if 0
	printf("out: ");
	for(i=0; i<slen; i++)
		printf("%x ", sbuf[i]);

	printf("got: ");
#endif
	size = read(fno, bf, BUFSIZ);
#if 0
	for(i=0; i<size; i++) {
		if(i<rlen)
			rbuf[i] = bf[i];
		printf("%x ", bf[i] & 0xff);
	}
	printf("\n");
#endif

  if ((hid_get_data(rbuf, rhi) & 0xff) != port) {
	fprintf(stderr, "err %x %x\n", rbuf[0], rbuf[1]);
  	return(-2);
  }

  return(hid_get_data(rbuf+1, rhi) & 0xff);
}

int main(int argc, char **argv)
{
	FILE *fp;
	int fno, i;
        report_desc_t r;
        char devname[100], *dev = 0;
        int ch;
        int repdump = 0;
        int loop = 0;
        char *table = 0;
	hid_data_t hd;
	hid_item_t shi, rhi;
	int slen, rlen;
	char buf[BUFSIZ];
	u_char *sbuf, *rbuf;
	int pos;

	hid_init(NULL);

	if((fno = open("/dev/uhid0", O_RDWR)) < 0) {
		perror(argv[0]);
	}

	/* read header */
        r = hid_get_report_desc(fno);
        if (r == 0) 
                errx(1, "USB_GET_REPORT_DESC");

	/* parse */
	for (hd = hid_start_parse(r, 1<<hid_output);
	     hid_get_item(hd, &shi); ) {
		if(shi.kind == hid_output)
		break;
	}
	hid_end_parse(hd);

	for (hd = hid_start_parse(r, 1<<hid_input);
	     hid_get_item(hd, &rhi); ) {
		if(rhi.kind == hid_input)
		break;
	}
	hid_end_parse(hd);

	/* clear file inputs */
	read(fno, buf, BUFSIZ);

#if 0
	printf("port 0: %d\n", 
	       usb_io_in(USB_IO_IN0, fno, &shi, &rhi));

	usb_io_out(USB_IO_OUT0, 0x55, fno, &shi);
	/* usb_io_out(USB_IO_OUT0, 0x55, fno, sbuf, slen, &shi); */

	printf("port 0: %d\n", 
	       usb_io_in(USB_IO_IN0, fno, &shi, &rhi));
	printf("port 1: %d\n", 
	       usb_io_in(USB_IO_IN1, fno, &shi, &rhi));
#endif

	usleep(1500);

	/* set 8bit mode */
	lcd_cmdout8(USB_IO_OUT0, 0x30, fno, &shi);

	usleep(4100);

	/* set 8bit mode */
	lcd_cmdout8(USB_IO_OUT0, 0x30, fno, &shi);

	usleep(100);

	/* set 8bit mode */
	lcd_cmdout8(USB_IO_OUT0, 0x30, fno, &shi);
	/* set 4bit mode */
	lcd_cmdout8(USB_IO_OUT0, 0x20, fno, &shi);

	lcd_cmdout(USB_IO_OUT0, 0x2c, fno, &shi);
	lcd_cmdout(USB_IO_OUT0, 0x08, fno, &shi);
	lcd_cmdout(USB_IO_OUT0, 0x0c, fno, &shi);
	lcd_cmdout(USB_IO_OUT0, 0x06, fno, &shi);

	lcd_cmdout(USB_IO_OUT0, 0x01, fno, &shi);

	if(argc == 1) {
		lcd_dataout(USB_IO_OUT0, 'H', fno, &shi);
		lcd_dataout(USB_IO_OUT0, 'e', fno, &shi);
		lcd_dataout(USB_IO_OUT0, 'l', fno, &shi);
		lcd_dataout(USB_IO_OUT0, 'l', fno, &shi);
		lcd_dataout(USB_IO_OUT0, 'o', fno, &shi);
		lcd_dataout(USB_IO_OUT0, ',', fno, &shi);
		lcd_dataout(USB_IO_OUT0, ' ', fno, &shi);
		lcd_dataout(USB_IO_OUT0, 'w', fno, &shi);
		lcd_dataout(USB_IO_OUT0, 'o', fno, &shi);
		lcd_dataout(USB_IO_OUT0, 'r', fno, &shi);
		lcd_dataout(USB_IO_OUT0, 'l', fno, &shi);
		lcd_dataout(USB_IO_OUT0, 'd', fno, &shi);
	} else {
		int num = strlen(argv[1]);

		for (i=0; i<num; i++)
			lcd_dataout(USB_IO_OUT0, argv[1][i], fno, &shi);

		if(argc>2) {
			lcd_cmdout(USB_IO_OUT0, 0xc0, fno, &shi);
			num = strlen(argv[2]);
			for (i=0; i<num; i++)
				lcd_dataout(USB_IO_OUT0, argv[2][i], fno, &shi);
		}
	}

	hid_dispose_report_desc(r);

	close(fno);

	exit(0);
}
