1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Character LCD driver for Linux 4 * 5 * Copyright (C) 2000-2008, Willy Tarreau <w@1wt.eu> 6 * Copyright (C) 2016-2017 Glider bvba 7 */ 8 9 #include <linux/atomic.h> 10 #include <linux/ctype.h> 11 #include <linux/fs.h> 12 #include <linux/miscdevice.h> 13 #include <linux/module.h> 14 #include <linux/notifier.h> 15 #include <linux/reboot.h> 16 #include <linux/slab.h> 17 #include <linux/uaccess.h> 18 #include <linux/workqueue.h> 19 20 #include <generated/utsrelease.h> 21 22 #include "charlcd.h" 23 24 /* Keep the backlight on this many seconds for each flash */ 25 #define LCD_BL_TEMPO_PERIOD 4 26 27 #define LCD_ESCAPE_LEN 24 /* Max chars for LCD escape command */ 28 #define LCD_ESCAPE_CHAR 27 /* Use char 27 for escape command */ 29 30 struct charlcd_priv { 31 struct charlcd lcd; 32 33 struct delayed_work bl_work; 34 struct mutex bl_tempo_lock; /* Protects access to bl_tempo */ 35 bool bl_tempo; 36 37 bool must_clear; 38 39 /* contains the LCD config state */ 40 unsigned long int flags; 41 42 /* Current escape sequence and it's length or -1 if outside */ 43 struct { 44 char buf[LCD_ESCAPE_LEN + 1]; 45 int len; 46 } esc_seq; 47 48 unsigned long long drvdata[]; 49 }; 50 51 #define charlcd_to_priv(p) container_of(p, struct charlcd_priv, lcd) 52 53 /* Device single-open policy control */ 54 static atomic_t charlcd_available = ATOMIC_INIT(1); 55 56 /* turn the backlight on or off */ 57 void charlcd_backlight(struct charlcd *lcd, enum charlcd_onoff on) 58 { 59 struct charlcd_priv *priv = charlcd_to_priv(lcd); 60 61 if (!lcd->ops->backlight) 62 return; 63 64 mutex_lock(&priv->bl_tempo_lock); 65 if (!priv->bl_tempo) 66 lcd->ops->backlight(lcd, on); 67 mutex_unlock(&priv->bl_tempo_lock); 68 } 69 EXPORT_SYMBOL_GPL(charlcd_backlight); 70 71 static void charlcd_bl_off(struct work_struct *work) 72 { 73 struct delayed_work *dwork = to_delayed_work(work); 74 struct charlcd_priv *priv = 75 container_of(dwork, struct charlcd_priv, bl_work); 76 77 mutex_lock(&priv->bl_tempo_lock); 78 if (priv->bl_tempo) { 79 priv->bl_tempo = false; 80 if (!(priv->flags & LCD_FLAG_L)) 81 priv->lcd.ops->backlight(&priv->lcd, CHARLCD_OFF); 82 } 83 mutex_unlock(&priv->bl_tempo_lock); 84 } 85 86 /* turn the backlight on for a little while */ 87 void charlcd_poke(struct charlcd *lcd) 88 { 89 struct charlcd_priv *priv = charlcd_to_priv(lcd); 90 91 if (!lcd->ops->backlight) 92 return; 93 94 cancel_delayed_work_sync(&priv->bl_work); 95 96 mutex_lock(&priv->bl_tempo_lock); 97 if (!priv->bl_tempo && !(priv->flags & LCD_FLAG_L)) 98 lcd->ops->backlight(lcd, CHARLCD_ON); 99 priv->bl_tempo = true; 100 schedule_delayed_work(&priv->bl_work, LCD_BL_TEMPO_PERIOD * HZ); 101 mutex_unlock(&priv->bl_tempo_lock); 102 } 103 EXPORT_SYMBOL_GPL(charlcd_poke); 104 105 static void charlcd_home(struct charlcd *lcd) 106 { 107 lcd->addr.x = 0; 108 lcd->addr.y = 0; 109 lcd->ops->home(lcd); 110 } 111 112 static void charlcd_print(struct charlcd *lcd, char c) 113 { 114 if (lcd->char_conv) 115 c = lcd->char_conv[(unsigned char)c]; 116 117 if (!lcd->ops->print(lcd, c)) 118 lcd->addr.x++; 119 120 /* prevents the cursor from wrapping onto the next line */ 121 if (lcd->addr.x == lcd->width) 122 lcd->ops->gotoxy(lcd, lcd->addr.x - 1, lcd->addr.y); 123 } 124 125 static void charlcd_clear_display(struct charlcd *lcd) 126 { 127 lcd->ops->clear_display(lcd); 128 lcd->addr.x = 0; 129 lcd->addr.y = 0; 130 } 131 132 /* 133 * Parses a movement command of the form "(.*);", where the group can be 134 * any number of subcommands of the form "(x|y)[0-9]+". 135 * 136 * Returns whether the command is valid. The position arguments are 137 * only written if the parsing was successful. 138 * 139 * For instance: 140 * - ";" returns (<original x>, <original y>). 141 * - "x1;" returns (1, <original y>). 142 * - "y2x1;" returns (1, 2). 143 * - "x12y34x56;" returns (56, 34). 144 * - "" fails. 145 * - "x" fails. 146 * - "x;" fails. 147 * - "x1" fails. 148 * - "xy12;" fails. 149 * - "x12yy12;" fails. 150 * - "xx" fails. 151 */ 152 static bool parse_xy(const char *s, unsigned long *x, unsigned long *y) 153 { 154 unsigned long new_x = *x; 155 unsigned long new_y = *y; 156 char *p; 157 158 for (;;) { 159 if (!*s) 160 return false; 161 162 if (*s == ';') 163 break; 164 165 if (*s == 'x') { 166 new_x = simple_strtoul(s + 1, &p, 10); 167 if (p == s + 1) 168 return false; 169 s = p; 170 } else if (*s == 'y') { 171 new_y = simple_strtoul(s + 1, &p, 10); 172 if (p == s + 1) 173 return false; 174 s = p; 175 } else { 176 return false; 177 } 178 } 179 180 *x = new_x; 181 *y = new_y; 182 return true; 183 } 184 185 /* 186 * These are the file operation function for user access to /dev/lcd 187 * This function can also be called from inside the kernel, by 188 * setting file and ppos to NULL. 189 * 190 */ 191 192 static inline int handle_lcd_special_code(struct charlcd *lcd) 193 { 194 struct charlcd_priv *priv = charlcd_to_priv(lcd); 195 196 /* LCD special codes */ 197 198 int processed = 0; 199 200 char *esc = priv->esc_seq.buf + 2; 201 int oldflags = priv->flags; 202 203 /* check for display mode flags */ 204 switch (*esc) { 205 case 'D': /* Display ON */ 206 priv->flags |= LCD_FLAG_D; 207 if (priv->flags != oldflags) 208 lcd->ops->display(lcd, CHARLCD_ON); 209 210 processed = 1; 211 break; 212 case 'd': /* Display OFF */ 213 priv->flags &= ~LCD_FLAG_D; 214 if (priv->flags != oldflags) 215 lcd->ops->display(lcd, CHARLCD_OFF); 216 217 processed = 1; 218 break; 219 case 'C': /* Cursor ON */ 220 priv->flags |= LCD_FLAG_C; 221 if (priv->flags != oldflags) 222 lcd->ops->cursor(lcd, CHARLCD_ON); 223 224 processed = 1; 225 break; 226 case 'c': /* Cursor OFF */ 227 priv->flags &= ~LCD_FLAG_C; 228 if (priv->flags != oldflags) 229 lcd->ops->cursor(lcd, CHARLCD_OFF); 230 231 processed = 1; 232 break; 233 case 'B': /* Blink ON */ 234 priv->flags |= LCD_FLAG_B; 235 if (priv->flags != oldflags) 236 lcd->ops->blink(lcd, CHARLCD_ON); 237 238 processed = 1; 239 break; 240 case 'b': /* Blink OFF */ 241 priv->flags &= ~LCD_FLAG_B; 242 if (priv->flags != oldflags) 243 lcd->ops->blink(lcd, CHARLCD_OFF); 244 245 processed = 1; 246 break; 247 case '+': /* Back light ON */ 248 priv->flags |= LCD_FLAG_L; 249 if (priv->flags != oldflags) 250 charlcd_backlight(lcd, CHARLCD_ON); 251 252 processed = 1; 253 break; 254 case '-': /* Back light OFF */ 255 priv->flags &= ~LCD_FLAG_L; 256 if (priv->flags != oldflags) 257 charlcd_backlight(lcd, CHARLCD_OFF); 258 259 processed = 1; 260 break; 261 case '*': /* Flash back light */ 262 charlcd_poke(lcd); 263 processed = 1; 264 break; 265 case 'f': /* Small Font */ 266 priv->flags &= ~LCD_FLAG_F; 267 if (priv->flags != oldflags) 268 lcd->ops->fontsize(lcd, CHARLCD_FONTSIZE_SMALL); 269 270 processed = 1; 271 break; 272 case 'F': /* Large Font */ 273 priv->flags |= LCD_FLAG_F; 274 if (priv->flags != oldflags) 275 lcd->ops->fontsize(lcd, CHARLCD_FONTSIZE_LARGE); 276 277 processed = 1; 278 break; 279 case 'n': /* One Line */ 280 priv->flags &= ~LCD_FLAG_N; 281 if (priv->flags != oldflags) 282 lcd->ops->lines(lcd, CHARLCD_LINES_1); 283 284 processed = 1; 285 break; 286 case 'N': /* Two Lines */ 287 priv->flags |= LCD_FLAG_N; 288 if (priv->flags != oldflags) 289 lcd->ops->lines(lcd, CHARLCD_LINES_2); 290 291 processed = 1; 292 break; 293 case 'l': /* Shift Cursor Left */ 294 if (lcd->addr.x > 0) { 295 if (!lcd->ops->shift_cursor(lcd, CHARLCD_SHIFT_LEFT)) 296 lcd->addr.x--; 297 } 298 299 processed = 1; 300 break; 301 case 'r': /* shift cursor right */ 302 if (lcd->addr.x < lcd->width) { 303 if (!lcd->ops->shift_cursor(lcd, CHARLCD_SHIFT_RIGHT)) 304 lcd->addr.x++; 305 } 306 307 processed = 1; 308 break; 309 case 'L': /* shift display left */ 310 lcd->ops->shift_display(lcd, CHARLCD_SHIFT_LEFT); 311 processed = 1; 312 break; 313 case 'R': /* shift display right */ 314 lcd->ops->shift_display(lcd, CHARLCD_SHIFT_RIGHT); 315 processed = 1; 316 break; 317 case 'k': { /* kill end of line */ 318 int x, xs, ys; 319 320 xs = lcd->addr.x; 321 ys = lcd->addr.y; 322 for (x = lcd->addr.x; x < lcd->width; x++) 323 lcd->ops->print(lcd, ' '); 324 325 /* restore cursor position */ 326 lcd->addr.x = xs; 327 lcd->addr.y = ys; 328 lcd->ops->gotoxy(lcd, lcd->addr.x, lcd->addr.y); 329 processed = 1; 330 break; 331 } 332 case 'I': /* reinitialize display */ 333 lcd->ops->init_display(lcd); 334 priv->flags = ((lcd->height > 1) ? LCD_FLAG_N : 0) | LCD_FLAG_D | 335 LCD_FLAG_C | LCD_FLAG_B; 336 processed = 1; 337 break; 338 case 'G': 339 if (lcd->ops->redefine_char) 340 processed = lcd->ops->redefine_char(lcd, esc); 341 else 342 processed = 1; 343 break; 344 345 case 'x': /* gotoxy : LxXXX[yYYY]; */ 346 case 'y': /* gotoxy : LyYYY[xXXX]; */ 347 if (priv->esc_seq.buf[priv->esc_seq.len - 1] != ';') 348 break; 349 350 /* If the command is valid, move to the new address */ 351 if (parse_xy(esc, &lcd->addr.x, &lcd->addr.y)) 352 lcd->ops->gotoxy(lcd, lcd->addr.x, lcd->addr.y); 353 354 /* Regardless of its validity, mark as processed */ 355 processed = 1; 356 break; 357 } 358 359 return processed; 360 } 361 362 static void charlcd_write_char(struct charlcd *lcd, char c) 363 { 364 struct charlcd_priv *priv = charlcd_to_priv(lcd); 365 366 /* first, we'll test if we're in escape mode */ 367 if ((c != '\n') && priv->esc_seq.len >= 0) { 368 /* yes, let's add this char to the buffer */ 369 priv->esc_seq.buf[priv->esc_seq.len++] = c; 370 priv->esc_seq.buf[priv->esc_seq.len] = '\0'; 371 } else { 372 /* aborts any previous escape sequence */ 373 priv->esc_seq.len = -1; 374 375 switch (c) { 376 case LCD_ESCAPE_CHAR: 377 /* start of an escape sequence */ 378 priv->esc_seq.len = 0; 379 priv->esc_seq.buf[priv->esc_seq.len] = '\0'; 380 break; 381 case '\b': 382 /* go back one char and clear it */ 383 if (lcd->addr.x > 0) { 384 /* back one char */ 385 if (!lcd->ops->shift_cursor(lcd, 386 CHARLCD_SHIFT_LEFT)) 387 lcd->addr.x--; 388 } 389 /* replace with a space */ 390 charlcd_print(lcd, ' '); 391 /* back one char again */ 392 if (!lcd->ops->shift_cursor(lcd, CHARLCD_SHIFT_LEFT)) 393 lcd->addr.x--; 394 395 break; 396 case '\f': 397 /* quickly clear the display */ 398 charlcd_clear_display(lcd); 399 break; 400 case '\n': 401 /* 402 * flush the remainder of the current line and 403 * go to the beginning of the next line 404 */ 405 for (; lcd->addr.x < lcd->width; lcd->addr.x++) 406 lcd->ops->print(lcd, ' '); 407 408 lcd->addr.x = 0; 409 lcd->addr.y = (lcd->addr.y + 1) % lcd->height; 410 lcd->ops->gotoxy(lcd, lcd->addr.x, lcd->addr.y); 411 break; 412 case '\r': 413 /* go to the beginning of the same line */ 414 lcd->addr.x = 0; 415 lcd->ops->gotoxy(lcd, lcd->addr.x, lcd->addr.y); 416 break; 417 case '\t': 418 /* print a space instead of the tab */ 419 charlcd_print(lcd, ' '); 420 break; 421 default: 422 /* simply print this char */ 423 charlcd_print(lcd, c); 424 break; 425 } 426 } 427 428 /* 429 * now we'll see if we're in an escape mode and if the current 430 * escape sequence can be understood. 431 */ 432 if (priv->esc_seq.len >= 2) { 433 int processed = 0; 434 435 if (!strcmp(priv->esc_seq.buf, "[2J")) { 436 /* clear the display */ 437 charlcd_clear_display(lcd); 438 processed = 1; 439 } else if (!strcmp(priv->esc_seq.buf, "[H")) { 440 /* cursor to home */ 441 charlcd_home(lcd); 442 processed = 1; 443 } 444 /* codes starting with ^[[L */ 445 else if ((priv->esc_seq.len >= 3) && 446 (priv->esc_seq.buf[0] == '[') && 447 (priv->esc_seq.buf[1] == 'L')) { 448 processed = handle_lcd_special_code(lcd); 449 } 450 451 /* LCD special escape codes */ 452 /* 453 * flush the escape sequence if it's been processed 454 * or if it is getting too long. 455 */ 456 if (processed || (priv->esc_seq.len >= LCD_ESCAPE_LEN)) 457 priv->esc_seq.len = -1; 458 } /* escape codes */ 459 } 460 461 static struct charlcd *the_charlcd; 462 463 static ssize_t charlcd_write(struct file *file, const char __user *buf, 464 size_t count, loff_t *ppos) 465 { 466 const char __user *tmp = buf; 467 char c; 468 469 for (; count-- > 0; (*ppos)++, tmp++) { 470 if (!in_interrupt() && (((count + 1) & 0x1f) == 0)) 471 /* 472 * let's be a little nice with other processes 473 * that need some CPU 474 */ 475 schedule(); 476 477 if (get_user(c, tmp)) 478 return -EFAULT; 479 480 charlcd_write_char(the_charlcd, c); 481 } 482 483 return tmp - buf; 484 } 485 486 static int charlcd_open(struct inode *inode, struct file *file) 487 { 488 struct charlcd_priv *priv = charlcd_to_priv(the_charlcd); 489 int ret; 490 491 ret = -EBUSY; 492 if (!atomic_dec_and_test(&charlcd_available)) 493 goto fail; /* open only once at a time */ 494 495 ret = -EPERM; 496 if (file->f_mode & FMODE_READ) /* device is write-only */ 497 goto fail; 498 499 if (priv->must_clear) { 500 priv->lcd.ops->clear_display(&priv->lcd); 501 priv->must_clear = false; 502 priv->lcd.addr.x = 0; 503 priv->lcd.addr.y = 0; 504 } 505 return nonseekable_open(inode, file); 506 507 fail: 508 atomic_inc(&charlcd_available); 509 return ret; 510 } 511 512 static int charlcd_release(struct inode *inode, struct file *file) 513 { 514 atomic_inc(&charlcd_available); 515 return 0; 516 } 517 518 static const struct file_operations charlcd_fops = { 519 .write = charlcd_write, 520 .open = charlcd_open, 521 .release = charlcd_release, 522 .llseek = no_llseek, 523 }; 524 525 static struct miscdevice charlcd_dev = { 526 .minor = LCD_MINOR, 527 .name = "lcd", 528 .fops = &charlcd_fops, 529 }; 530 531 static void charlcd_puts(struct charlcd *lcd, const char *s) 532 { 533 const char *tmp = s; 534 int count = strlen(s); 535 536 for (; count-- > 0; tmp++) { 537 if (!in_interrupt() && (((count + 1) & 0x1f) == 0)) 538 /* 539 * let's be a little nice with other processes 540 * that need some CPU 541 */ 542 schedule(); 543 544 charlcd_write_char(lcd, *tmp); 545 } 546 } 547 548 #ifdef CONFIG_PANEL_BOOT_MESSAGE 549 #define LCD_INIT_TEXT CONFIG_PANEL_BOOT_MESSAGE 550 #else 551 #define LCD_INIT_TEXT "Linux-" UTS_RELEASE "\n" 552 #endif 553 554 #ifdef CONFIG_CHARLCD_BL_ON 555 #define LCD_INIT_BL "\x1b[L+" 556 #elif defined(CONFIG_CHARLCD_BL_FLASH) 557 #define LCD_INIT_BL "\x1b[L*" 558 #else 559 #define LCD_INIT_BL "\x1b[L-" 560 #endif 561 562 /* initialize the LCD driver */ 563 static int charlcd_init(struct charlcd *lcd) 564 { 565 struct charlcd_priv *priv = charlcd_to_priv(lcd); 566 int ret; 567 568 priv->flags = ((lcd->height > 1) ? LCD_FLAG_N : 0) | LCD_FLAG_D | 569 LCD_FLAG_C | LCD_FLAG_B; 570 if (lcd->ops->backlight) { 571 mutex_init(&priv->bl_tempo_lock); 572 INIT_DELAYED_WORK(&priv->bl_work, charlcd_bl_off); 573 } 574 575 /* 576 * before this line, we must NOT send anything to the display. 577 * Since charlcd_init_display() needs to write data, we have to 578 * enable mark the LCD initialized just before. 579 */ 580 ret = lcd->ops->init_display(lcd); 581 if (ret) 582 return ret; 583 584 /* display a short message */ 585 charlcd_puts(lcd, "\x1b[Lc\x1b[Lb" LCD_INIT_BL LCD_INIT_TEXT); 586 587 /* clear the display on the next device opening */ 588 priv->must_clear = true; 589 charlcd_home(lcd); 590 return 0; 591 } 592 593 struct charlcd *charlcd_alloc(void) 594 { 595 struct charlcd_priv *priv; 596 struct charlcd *lcd; 597 598 priv = kzalloc(sizeof(*priv), GFP_KERNEL); 599 if (!priv) 600 return NULL; 601 602 priv->esc_seq.len = -1; 603 604 lcd = &priv->lcd; 605 606 return lcd; 607 } 608 EXPORT_SYMBOL_GPL(charlcd_alloc); 609 610 void charlcd_free(struct charlcd *lcd) 611 { 612 kfree(charlcd_to_priv(lcd)); 613 } 614 EXPORT_SYMBOL_GPL(charlcd_free); 615 616 static int panel_notify_sys(struct notifier_block *this, unsigned long code, 617 void *unused) 618 { 619 struct charlcd *lcd = the_charlcd; 620 621 switch (code) { 622 case SYS_DOWN: 623 charlcd_puts(lcd, 624 "\x0cReloading\nSystem...\x1b[Lc\x1b[Lb\x1b[L+"); 625 break; 626 case SYS_HALT: 627 charlcd_puts(lcd, "\x0cSystem Halted.\x1b[Lc\x1b[Lb\x1b[L+"); 628 break; 629 case SYS_POWER_OFF: 630 charlcd_puts(lcd, "\x0cPower off.\x1b[Lc\x1b[Lb\x1b[L+"); 631 break; 632 default: 633 break; 634 } 635 return NOTIFY_DONE; 636 } 637 638 static struct notifier_block panel_notifier = { 639 panel_notify_sys, 640 NULL, 641 0 642 }; 643 644 int charlcd_register(struct charlcd *lcd) 645 { 646 int ret; 647 648 ret = charlcd_init(lcd); 649 if (ret) 650 return ret; 651 652 ret = misc_register(&charlcd_dev); 653 if (ret) 654 return ret; 655 656 the_charlcd = lcd; 657 register_reboot_notifier(&panel_notifier); 658 return 0; 659 } 660 EXPORT_SYMBOL_GPL(charlcd_register); 661 662 int charlcd_unregister(struct charlcd *lcd) 663 { 664 struct charlcd_priv *priv = charlcd_to_priv(lcd); 665 666 unregister_reboot_notifier(&panel_notifier); 667 charlcd_puts(lcd, "\x0cLCD driver unloaded.\x1b[Lc\x1b[Lb\x1b[L-"); 668 misc_deregister(&charlcd_dev); 669 the_charlcd = NULL; 670 if (lcd->ops->backlight) { 671 cancel_delayed_work_sync(&priv->bl_work); 672 priv->lcd.ops->backlight(&priv->lcd, CHARLCD_OFF); 673 } 674 675 return 0; 676 } 677 EXPORT_SYMBOL_GPL(charlcd_unregister); 678 679 MODULE_LICENSE("GPL"); 680