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