1b1303e6eSMike Smith /* 2b1303e6eSMike Smith * Control LCD module hung off parallel port using the 3b1303e6eSMike Smith * ppi 'geek port' interface. 4b1303e6eSMike Smith * 5b1303e6eSMike Smith * $Id$ 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 92b1303e6eSMike Smith void 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