xref: /freebsd/share/examples/ppi/ppilcd.c (revision a343b08831c940cff6a3dd5c89f888785685ec45)
1b1303e6eSMike Smith /*
2b1303e6eSMike Smith  * Control LCD module hung off parallel port using the
3b1303e6eSMike Smith  * ppi 'geek port' interface.
4b1303e6eSMike Smith  *
57f3dea24SPeter Wemm  * $FreeBSD$
6b1303e6eSMike Smith  */
7b1303e6eSMike Smith 
8b1303e6eSMike Smith #include <stdio.h>
9b1303e6eSMike Smith #include <stdlib.h>
10b1303e6eSMike Smith #include <string.h>
11b1303e6eSMike Smith #include <ctype.h>
12b1303e6eSMike Smith #include <fcntl.h>
13b1303e6eSMike Smith #include <unistd.h>
14b1303e6eSMike Smith #include <err.h>
15b1303e6eSMike Smith #include <sysexits.h>
16b1303e6eSMike Smith 
17b1303e6eSMike Smith /* XXX should be in <machine/> */
18b1303e6eSMike Smith #include "ppbconf.h"
19b1303e6eSMike Smith #include "ppi.h"
20b1303e6eSMike Smith 
21b1303e6eSMike Smith #define debug(lev, fmt, args...)	if (debuglevel >= lev) fprintf(stderr, fmt "\n" , ## args);
22b1303e6eSMike Smith 
23b1303e6eSMike Smith static void	usage(void);
24b1303e6eSMike Smith static char	*progname;
25b1303e6eSMike Smith 
26b1303e6eSMike Smith #define	DEFAULT_DEVICE	"/dev/ppi0"
27b1303e6eSMike Smith 
28b1303e6eSMike Smith /* Driver functions */
29b1303e6eSMike Smith static void	hd44780_prepare(char *devname, char *options);
30b1303e6eSMike Smith static void	hd44780_finish(void);
31b1303e6eSMike Smith static void	hd44780_command(int cmd);
32b1303e6eSMike Smith static void	hd44780_putc(int c);
33b1303e6eSMike Smith 
34b1303e6eSMike Smith /*
35b1303e6eSMike Smith  * Commands
36b1303e6eSMike Smith  * Note that unrecognised command escapes are passed through with
37b1303e6eSMike Smith  * the command value set to the ASCII value of the escaped character.
38b1303e6eSMike Smith  */
39b1303e6eSMike Smith #define CMD_RESET	0
40b1303e6eSMike Smith #define CMD_BKSP	1
41b1303e6eSMike Smith #define CMD_CLR		2
42b1303e6eSMike Smith #define CMD_NL		3
43b1303e6eSMike Smith #define CMD_CR		4
44b1303e6eSMike Smith #define CMD_HOME	5
45b1303e6eSMike Smith 
46b1303e6eSMike Smith #define MAX_DRVOPT	10	/* maximum driver-specific options */
47b1303e6eSMike Smith 
48b1303e6eSMike Smith struct lcd_driver
49b1303e6eSMike Smith {
50b1303e6eSMike Smith     char	*l_code;
51b1303e6eSMike Smith     char	*l_name;
52b1303e6eSMike Smith     char	*l_options[MAX_DRVOPT];
53b1303e6eSMike Smith     void	(* l_prepare)(char *name, char *options);
54b1303e6eSMike Smith     void	(* l_finish)(void);
55b1303e6eSMike Smith     void	(* l_command)(int cmd);
56b1303e6eSMike Smith     void	(* l_putc)(int c);
57b1303e6eSMike Smith };
58b1303e6eSMike Smith 
59b1303e6eSMike Smith static struct lcd_driver lcd_drivertab[] = {
60b1303e6eSMike Smith     {
61b1303e6eSMike Smith 	"hd44780",
62b1303e6eSMike Smith 	"Hitachi HD44780 and compatibles",
63b1303e6eSMike Smith 	{
64b1303e6eSMike Smith 	    "Reset options:",
65b1303e6eSMike Smith 	    "    1     1-line display (default 2)",
66b1303e6eSMike Smith 	    "    B     Cursor blink enable",
67b1303e6eSMike Smith 	    "    C     Cursor enable",
68b1303e6eSMike Smith 	    "    F     Large font select",
69b1303e6eSMike Smith 	    NULL
70b1303e6eSMike Smith 	},
71b1303e6eSMike Smith 	hd44780_prepare,
72b1303e6eSMike Smith 	hd44780_finish,
73b1303e6eSMike Smith 	hd44780_command,
74b1303e6eSMike Smith 	hd44780_putc
75b1303e6eSMike Smith     },
76b1303e6eSMike Smith     {
77b1303e6eSMike Smith 	NULL,
78b1303e6eSMike Smith 	NULL,
79b1303e6eSMike Smith 	{
80b1303e6eSMike Smith 	    NULL
81b1303e6eSMike Smith 	},
82b1303e6eSMike Smith 	NULL,
83b1303e6eSMike Smith 	NULL
84b1303e6eSMike Smith     }
85b1303e6eSMike Smith };
86b1303e6eSMike Smith 
87b1303e6eSMike Smith static void	do_char(struct lcd_driver *driver, char ch);
88b1303e6eSMike Smith 
89b1303e6eSMike Smith int	debuglevel = 0;
90b1303e6eSMike Smith int	vflag = 0;
91b1303e6eSMike Smith 
92a343b088SNick Hibma int
93b1303e6eSMike Smith main(int argc, char *argv[])
94b1303e6eSMike Smith {
95b1303e6eSMike Smith     extern char		*optarg;
96b1303e6eSMike Smith     extern int		optind;
97b1303e6eSMike Smith     struct lcd_driver	*driver = &lcd_drivertab[0];
98b1303e6eSMike Smith     char		*drivertype, *cp;
99b1303e6eSMike Smith     char		*devname = DEFAULT_DEVICE;
100b1303e6eSMike Smith     char		*drvopts = NULL;
101b1303e6eSMike Smith     int			ch, i;
102b1303e6eSMike Smith 
103b1303e6eSMike Smith     if ((progname = strrchr(argv[0], '/'))) {
104b1303e6eSMike Smith 	progname++;
105b1303e6eSMike Smith     } else {
106b1303e6eSMike Smith 	progname = argv[0];
107b1303e6eSMike Smith     }
108b1303e6eSMike Smith 
109b1303e6eSMike Smith     drivertype = getenv("LCD_TYPE");
110b1303e6eSMike Smith 
111b1303e6eSMike Smith     while ((ch = getopt(argc, argv, "Dd:f:o:v")) != EOF) {
112b1303e6eSMike Smith 	switch(ch) {
113b1303e6eSMike Smith 	case 'D':
114b1303e6eSMike Smith 	    debuglevel++;
115b1303e6eSMike Smith 	    break;
116b1303e6eSMike Smith 	case 'd':
117b1303e6eSMike Smith 	    drivertype = optarg;
118b1303e6eSMike Smith 	    break;
119b1303e6eSMike Smith 	case 'f':
120b1303e6eSMike Smith 	    devname = optarg;
121b1303e6eSMike Smith 	    break;
122b1303e6eSMike Smith 	case 'o':
123b1303e6eSMike Smith 	    drvopts = optarg;
124b1303e6eSMike Smith 	    break;
125b1303e6eSMike Smith 	case 'v':
126b1303e6eSMike Smith 	    vflag = 1;
127b1303e6eSMike Smith 	    break;
128b1303e6eSMike Smith 	default:
129b1303e6eSMike Smith 	    usage();
130b1303e6eSMike Smith 	}
131b1303e6eSMike Smith     }
132b1303e6eSMike Smith     argc -= optind;
133b1303e6eSMike Smith     argv += optind;
134b1303e6eSMike Smith 
135b1303e6eSMike Smith     /* If an LCD type was specified, look it up */
136b1303e6eSMike Smith     if (drivertype != NULL) {
137b1303e6eSMike Smith 	driver = NULL;
138b1303e6eSMike Smith 	for (i = 0; lcd_drivertab[i].l_code != NULL; i++) {
139b1303e6eSMike Smith 	    if (!strcmp(drivertype, lcd_drivertab[i].l_code)) {
140b1303e6eSMike Smith 		driver = &lcd_drivertab[i];
141b1303e6eSMike Smith 		break;
142b1303e6eSMike Smith 	    }
143b1303e6eSMike Smith 	}
144b1303e6eSMike Smith 	if (driver == NULL) {
145b1303e6eSMike Smith 	    warnx("LCD driver '%s' not known", drivertype);
146b1303e6eSMike Smith 	    usage();
147b1303e6eSMike Smith 	}
148b1303e6eSMike Smith     }
149b1303e6eSMike Smith     debug(1, "Driver selected for %s", driver->l_name);
150b1303e6eSMike Smith     driver->l_prepare(devname, drvopts);
151b1303e6eSMike Smith     atexit(driver->l_finish);
152b1303e6eSMike Smith 
153b1303e6eSMike Smith     if (argc > 0) {
154b1303e6eSMike Smith 	debug(2, "reading input from %d argument%s", argc, (argc > 1) ? "s" : "");
155b1303e6eSMike Smith 	for (i = 0; i < argc; i++)
156b1303e6eSMike Smith 	    for (cp = argv[i]; *cp; cp++)
157b1303e6eSMike Smith 		do_char(driver, *cp);
158b1303e6eSMike Smith     } else {
159b1303e6eSMike Smith 	debug(2, "reading input from stdin");
160b1303e6eSMike Smith 	setvbuf(stdin, NULL, _IONBF, 0);
161b1303e6eSMike Smith 	while ((ch = fgetc(stdin)) != EOF)
162b1303e6eSMike Smith 	    do_char(driver, (char)ch);
163b1303e6eSMike Smith     }
164b1303e6eSMike Smith     exit(EX_OK);
165b1303e6eSMike Smith }
166b1303e6eSMike Smith 
167b1303e6eSMike Smith static void
168b1303e6eSMike Smith usage(void)
169b1303e6eSMike Smith {
170b1303e6eSMike Smith     int		i, j;
171b1303e6eSMike Smith 
172b1303e6eSMike Smith     fprintf(stderr, "usage: %s [-v] [-d drivername] [-f device] [-o options] [args...]\n", progname);
173b1303e6eSMike Smith     fprintf(stderr, "   -D      Increase debugging\n");
174b1303e6eSMike Smith     fprintf(stderr, "   -f      Specify device, default is '%s'\n", DEFAULT_DEVICE);
175b1303e6eSMike Smith     fprintf(stderr, "   -d      Specify driver, one of:\n");
176b1303e6eSMike Smith     for (i = 0; lcd_drivertab[i].l_code != NULL; i++) {
177b1303e6eSMike Smith 	fprintf(stderr, "              %-10s (%s)%s\n",
178b1303e6eSMike Smith 		lcd_drivertab[i].l_code, lcd_drivertab[i].l_name, (i == 0) ? " *default*" : "");
179b1303e6eSMike Smith 	if (lcd_drivertab[i].l_options[0] != NULL) {
180b1303e6eSMike Smith 
181b1303e6eSMike Smith 	    for (j = 0; lcd_drivertab[i].l_options[j] != NULL; j++)
182b1303e6eSMike Smith 		fprintf(stderr, "                  %s\n", lcd_drivertab[i].l_options[j]);
183b1303e6eSMike Smith 	}
184b1303e6eSMike Smith     }
185b1303e6eSMike Smith     fprintf(stderr, "  -o       Specify driver option string\n");
186b1303e6eSMike Smith     fprintf(stderr, "  args     Message strings.  Embedded escapes supported:\n");
187b1303e6eSMike Smith     fprintf(stderr, "                  \\b	Backspace\n");
188b1303e6eSMike Smith     fprintf(stderr, "                  \\f	Clear display, home cursor\n");
189b1303e6eSMike Smith     fprintf(stderr, "                  \\n	Newline\n");
190b1303e6eSMike Smith     fprintf(stderr, "                  \\r	Carriage return\n");
191b1303e6eSMike Smith     fprintf(stderr, "                  \\R	Reset display\n");
192b1303e6eSMike Smith     fprintf(stderr, "                  \\v	Home cursor\n");
193b1303e6eSMike Smith     fprintf(stderr, "                  \\\\	Literal \\\n");
194b1303e6eSMike Smith     fprintf(stderr, "           If args not supplied, strings are read from standard input\n");
195b1303e6eSMike Smith     exit(EX_USAGE);
196b1303e6eSMike Smith }
197b1303e6eSMike Smith 
198b1303e6eSMike Smith static void
199b1303e6eSMike Smith do_char(struct lcd_driver *driver, char ch)
200b1303e6eSMike Smith {
201b1303e6eSMike Smith     static int	esc = 0;
202b1303e6eSMike Smith 
203b1303e6eSMike Smith     if (esc) {
204b1303e6eSMike Smith 	switch(ch) {
205b1303e6eSMike Smith 	case 'b':
206b1303e6eSMike Smith 	    driver->l_command(CMD_BKSP);
207b1303e6eSMike Smith 	    break;
208b1303e6eSMike Smith 	case 'f':
209b1303e6eSMike Smith 	    driver->l_command(CMD_CLR);
210b1303e6eSMike Smith 	    break;
211b1303e6eSMike Smith 	case 'n':
212b1303e6eSMike Smith 	    driver->l_command(CMD_NL);
213b1303e6eSMike Smith 	    break;
214b1303e6eSMike Smith 	case 'r':
215b1303e6eSMike Smith 	    driver->l_command(CMD_CR);
216b1303e6eSMike Smith 	    break;
217b1303e6eSMike Smith 	case 'R':
218b1303e6eSMike Smith 	    driver->l_command(CMD_RESET);
219b1303e6eSMike Smith 	    break;
220b1303e6eSMike Smith 	case 'v':
221b1303e6eSMike Smith 	    driver->l_command(CMD_HOME);
222b1303e6eSMike Smith 	    break;
223b1303e6eSMike Smith 	case '\\':
224b1303e6eSMike Smith 	    driver->l_putc('\\');
225b1303e6eSMike Smith 	    break;
226b1303e6eSMike Smith 	default:
227b1303e6eSMike Smith 	    driver->l_command(ch);
228b1303e6eSMike Smith 	    break;
229b1303e6eSMike Smith 	}
230b1303e6eSMike Smith 	esc = 0;
231b1303e6eSMike Smith     } else {
232b1303e6eSMike Smith 	if (ch == '\\') {
233b1303e6eSMike Smith 	    esc = 1;
234b1303e6eSMike Smith 	} else {
235b1303e6eSMike Smith 	    if (vflag || isprint(ch))
236b1303e6eSMike Smith 		driver->l_putc(ch);
237b1303e6eSMike Smith 	}
238b1303e6eSMike Smith     }
239b1303e6eSMike Smith }
240b1303e6eSMike Smith 
241b1303e6eSMike Smith 
242b1303e6eSMike Smith /******************************************************************************
243b1303e6eSMike Smith  * Driver for the Hitachi HD44780.  This is probably *the* most common driver
244b1303e6eSMike Smith  * to be found on one- and two-line alphanumeric LCDs.
245b1303e6eSMike Smith  *
246b1303e6eSMike Smith  * This driver assumes the following connections :
247b1303e6eSMike Smith  *
248b1303e6eSMike Smith  * Parallel Port	LCD Module
249b1303e6eSMike Smith  * --------------------------------
250b1303e6eSMike Smith  * Strobe (1)		Enable (6)
251b1303e6eSMike Smith  * Data (2-9)		Data (7-14)
252b1303e6eSMike Smith  * Select(13)		RS (4)
253b1303e6eSMike Smith  * Auto Feed (14)	R/W (5)
254b1303e6eSMike Smith  *
255b1303e6eSMike Smith  * In addition, power must be supplied to the module, normally with
256b1303e6eSMike Smith  * a circuit similar to this:
257b1303e6eSMike Smith  *
258b1303e6eSMike Smith  * VCC (+5V) O------o-------o--------O Module pin 2
259b1303e6eSMike Smith  *                  |       | +
260b1303e6eSMike Smith  *                  /      ---
261b1303e6eSMike Smith  *                  \      --- 1uF
262b1303e6eSMike Smith  *                  /       | -
263b1303e6eSMike Smith  *                  \ <-----o--------O Module pin 3
264b1303e6eSMike Smith  *                  /
265b1303e6eSMike Smith  *                  \
266b1303e6eSMike Smith  *                  |
267b1303e6eSMike Smith  * GND       O------o----------------O Module pin 1
268b1303e6eSMike Smith  *
269b1303e6eSMike Smith  * The ground line should also be connected to the parallel port, on
270b1303e6eSMike Smith  * one of the ground pins (eg. pin 25).
271b1303e6eSMike Smith  *
272b1303e6eSMike Smith  * Note that the pinning on some LCD modules has the odd and even pins
273b1303e6eSMike Smith  * arranged as though reversed; check carefully before conecting a module
274b1303e6eSMike Smith  * as it is possible to toast the HD44780 if the power is reversed.
275b1303e6eSMike Smith  */
276b1303e6eSMike Smith 
277b1303e6eSMike Smith static int	hd_fd;
278b1303e6eSMike Smith static u_int8_t	hd_cbits;
279b1303e6eSMike Smith static int	hd_lines = 2;
280b1303e6eSMike Smith static int	hd_blink = 0;
281b1303e6eSMike Smith static int 	hd_cursor = 0;
282b1303e6eSMike Smith static int	hd_font = 0;
283b1303e6eSMike Smith 
284b1303e6eSMike Smith #define HD_COMMAND	SELECTIN
285b1303e6eSMike Smith #define HD_DATA		0
286b1303e6eSMike Smith #define HD_READ		0
287b1303e6eSMike Smith #define HD_WRITE	AUTOFEED
288b1303e6eSMike Smith 
289b1303e6eSMike Smith #define HD_BF		0x80		/* internal busy flag */
290b1303e6eSMike Smith #define HD_ADDRMASK	0x7f		/* DDRAM address mask */
291b1303e6eSMike Smith 
292b1303e6eSMike Smith #define hd_sctrl(v)	{u_int8_t _val; _val = hd_cbits | v; ioctl(hd_fd, PPISCTRL, &_val);}
293b1303e6eSMike Smith #define hd_sdata(v)	{u_int8_t _val; _val = v; ioctl(hd_fd, PPISDATA, &_val);}
294b1303e6eSMike Smith #define hd_gdata(v)	ioctl(hd_fd, PPIGDATA, &v)
295b1303e6eSMike Smith 
296b1303e6eSMike Smith static void
297b1303e6eSMike Smith hd44780_output(int type, int data)
298b1303e6eSMike Smith {
299b1303e6eSMike Smith     debug(3, "%s -> 0x%02x", (type == HD_COMMAND) ? "cmd " : "data", data);
300b1303e6eSMike Smith     hd_sctrl(type | HD_WRITE | STROBE);	/* set direction, address */
301b1303e6eSMike Smith     hd_sctrl(type | HD_WRITE);		/* raise E */
302b1303e6eSMike Smith     hd_sdata((u_int8_t) data);		/* drive data */
303b1303e6eSMike Smith     hd_sctrl(type | HD_WRITE | STROBE);	/* lower E */
304b1303e6eSMike Smith }
305b1303e6eSMike Smith 
306b1303e6eSMike Smith static int
307b1303e6eSMike Smith hd44780_input(int type)
308b1303e6eSMike Smith {
309b1303e6eSMike Smith     u_int8_t	val;
310b1303e6eSMike Smith 
311b1303e6eSMike Smith     hd_sctrl(type | HD_READ | STROBE);	/* set direction, address */
312b1303e6eSMike Smith     hd_sctrl(type | HD_READ);		/* raise E */
313b1303e6eSMike Smith     hd_gdata(val);			/* read data */
314b1303e6eSMike Smith     hd_sctrl(type | HD_READ | STROBE);	/* lower E */
315b1303e6eSMike Smith 
316b1303e6eSMike Smith     debug(3, "0x%02x -> %s", val, (type == HD_COMMAND) ? "cmd " : "data");
317b1303e6eSMike Smith     return(val);
318b1303e6eSMike Smith }
319b1303e6eSMike Smith 
320b1303e6eSMike Smith static void
321b1303e6eSMike Smith hd44780_prepare(char *devname, char *options)
322b1303e6eSMike Smith {
323b1303e6eSMike Smith     char	*cp = options;
324b1303e6eSMike Smith 
325b1303e6eSMike Smith     if ((hd_fd = open(devname, O_RDWR, 0)) == -1)
326b1303e6eSMike Smith 	err(EX_OSFILE, "can't open '%s'", devname);
327b1303e6eSMike Smith 
328b1303e6eSMike Smith     /* parse options */
329b1303e6eSMike Smith     while (cp && *cp) {
330b1303e6eSMike Smith 	switch (*cp++) {
331b1303e6eSMike Smith 	case '1':
332b1303e6eSMike Smith 	    hd_lines = 1;
333b1303e6eSMike Smith 	    break;
334b1303e6eSMike Smith 	case 'B':
335b1303e6eSMike Smith 	    hd_blink = 1;
336b1303e6eSMike Smith 	    break;
337b1303e6eSMike Smith 	case 'C':
338b1303e6eSMike Smith 	    hd_cursor = 1;
339b1303e6eSMike Smith 	    break;
340b1303e6eSMike Smith 	case 'F':
341b1303e6eSMike Smith 	    hd_font = 1;
342b1303e6eSMike Smith 	    break;
343b1303e6eSMike Smith 	default:
344b1303e6eSMike Smith 	    errx(EX_USAGE, "hd44780: unknown option code '%c'", *(cp-1));
345b1303e6eSMike Smith 	}
346b1303e6eSMike Smith     }
347b1303e6eSMike Smith 
348b1303e6eSMike Smith     /* Put LCD in idle state */
349b1303e6eSMike Smith     if (ioctl(hd_fd, PPIGCTRL, &hd_cbits))		/* save other control bits */
350b1303e6eSMike Smith 	err(EX_IOERR, "ioctl PPIGCTRL failed (not a ppi device?)");
351b1303e6eSMike Smith     hd_cbits &= ~(STROBE | SELECTIN | AUTOFEED);	/* set strobe, RS, R/W low */
352b1303e6eSMike Smith     debug(2, "static control bits 0x%x", hd_cbits);
353b1303e6eSMike Smith     hd_sctrl(STROBE);
354b1303e6eSMike Smith     hd_sdata(0);
355b1303e6eSMike Smith 
356b1303e6eSMike Smith }
357b1303e6eSMike Smith 
358b1303e6eSMike Smith static void
359b1303e6eSMike Smith hd44780_finish(void)
360b1303e6eSMike Smith {
361b1303e6eSMike Smith     close(hd_fd);
362b1303e6eSMike Smith }
363b1303e6eSMike Smith 
364b1303e6eSMike Smith static void
365b1303e6eSMike Smith hd44780_command(int cmd)
366b1303e6eSMike Smith {
367b1303e6eSMike Smith     u_int8_t	val;
368b1303e6eSMike Smith 
369b1303e6eSMike Smith     switch (cmd) {
370b1303e6eSMike Smith     case CMD_RESET:	/* full manual reset and reconfigure as per datasheet */
371b1303e6eSMike Smith 	debug(1, "hd44780: reset to %d lines, %s font,%s%s cursor",
372b1303e6eSMike Smith 	      hd_lines, hd_font ? "5x10" : "5x7", hd_cursor ? "" : " no", hd_blink ? " blinking" : "");
373b1303e6eSMike Smith 	val = 0x30;
374b1303e6eSMike Smith 	if (hd_lines == 2)
375b1303e6eSMike Smith 	    val |= 0x08;
376b1303e6eSMike Smith 	if (hd_font)
377b1303e6eSMike Smith 	    val |= 0x04;
378b1303e6eSMike Smith 	hd44780_output(HD_COMMAND, val);
379b1303e6eSMike Smith 	usleep(10000);
380b1303e6eSMike Smith 	hd44780_output(HD_COMMAND, val);
381b1303e6eSMike Smith 	usleep(1000);
382b1303e6eSMike Smith 	hd44780_output(HD_COMMAND, val);
383b1303e6eSMike Smith 	usleep(1000);
384b1303e6eSMike Smith 	val = 0x08;				/* display off */
385b1303e6eSMike Smith 	hd44780_output(HD_COMMAND, val);
386b1303e6eSMike Smith 	usleep(1000);
387b1303e6eSMike Smith 	val |= 0x04;				/* display on */
388b1303e6eSMike Smith 	if (hd_cursor)
389b1303e6eSMike Smith 	    val |= 0x02;
390b1303e6eSMike Smith 	if (hd_blink)
391b1303e6eSMike Smith 	    val |= 0x01;
392b1303e6eSMike Smith 	hd44780_output(HD_COMMAND, val);
393b1303e6eSMike Smith 	usleep(1000);
394b1303e6eSMike Smith 	hd44780_output(HD_COMMAND, 0x06);	/* shift cursor by increment */
395b1303e6eSMike Smith 	usleep(1000);
396b1303e6eSMike Smith 	/* FALLTHROUGH */
397b1303e6eSMike Smith 
398b1303e6eSMike Smith     case CMD_CLR:
399b1303e6eSMike Smith 	hd44780_output(HD_COMMAND, 0x01);
400b1303e6eSMike Smith 	usleep(2000);
401b1303e6eSMike Smith 	break;
402b1303e6eSMike Smith 
403b1303e6eSMike Smith     case CMD_BKSP:
404b1303e6eSMike Smith 	hd44780_output(HD_DATA, 0x10);		/* shift cursor left one */
405b1303e6eSMike Smith 	break;
406b1303e6eSMike Smith 
407b1303e6eSMike Smith     case CMD_NL:
408b1303e6eSMike Smith 	if (hd_lines == 2)
409b1303e6eSMike Smith 	    hd44780_output(HD_COMMAND, 0xc0);	/* beginning of second line */
410b1303e6eSMike Smith 	break;
411b1303e6eSMike Smith 
412b1303e6eSMike Smith     case CMD_CR:
413b1303e6eSMike Smith 	/* XXX will not work in 4-line mode, or where readback fails */
414b1303e6eSMike Smith 	val = hd44780_input(HD_COMMAND) & 0x3f;	/* mask character position, save line pos */
415b1303e6eSMike Smith 	hd44780_output(HD_COMMAND, 0x80 | val);
416b1303e6eSMike Smith 	break;
417b1303e6eSMike Smith 
418b1303e6eSMike Smith     case CMD_HOME:
419b1303e6eSMike Smith 	hd44780_output(HD_COMMAND, 0x02);
420b1303e6eSMike Smith 	usleep(2000);
421b1303e6eSMike Smith 	break;
422b1303e6eSMike Smith 
423b1303e6eSMike Smith     default:
424b1303e6eSMike Smith 	if (isprint(cmd)) {
425b1303e6eSMike Smith 	    warnx("unknown command %c", cmd);
426b1303e6eSMike Smith 	} else {
427b1303e6eSMike Smith 	    warnx("unknown command 0x%x", cmd);
428b1303e6eSMike Smith 	}
429b1303e6eSMike Smith     }
430b1303e6eSMike Smith     usleep(40);
431b1303e6eSMike Smith }
432b1303e6eSMike Smith 
433b1303e6eSMike Smith static void
434b1303e6eSMike Smith hd44780_putc(int c)
435b1303e6eSMike Smith {
436b1303e6eSMike Smith     hd44780_output(HD_DATA, c);
437b1303e6eSMike Smith     usleep(40);
438b1303e6eSMike Smith }
439b1303e6eSMike Smith 
440