linux/drivers/auxdisplay/charlcd.c
<<
>>
Prefs
   1/*
   2 * Character LCD driver for Linux
   3 *
   4 * Copyright (C) 2000-2008, Willy Tarreau <w@1wt.eu>
   5 * Copyright (C) 2016-2017 Glider bvba
   6 *
   7 * This program is free software; you can redistribute it and/or
   8 * modify it under the terms of the GNU General Public License
   9 * as published by the Free Software Foundation; either version
  10 * 2 of the License, or (at your option) any later version.
  11 */
  12
  13#include <linux/atomic.h>
  14#include <linux/delay.h>
  15#include <linux/fs.h>
  16#include <linux/miscdevice.h>
  17#include <linux/module.h>
  18#include <linux/notifier.h>
  19#include <linux/reboot.h>
  20#include <linux/slab.h>
  21#include <linux/uaccess.h>
  22#include <linux/workqueue.h>
  23
  24#include <generated/utsrelease.h>
  25
  26#include <misc/charlcd.h>
  27
  28#define LCD_MINOR               156
  29
  30#define DEFAULT_LCD_BWIDTH      40
  31#define DEFAULT_LCD_HWIDTH      64
  32
  33/* Keep the backlight on this many seconds for each flash */
  34#define LCD_BL_TEMPO_PERIOD     4
  35
  36#define LCD_FLAG_B              0x0004  /* Blink on */
  37#define LCD_FLAG_C              0x0008  /* Cursor on */
  38#define LCD_FLAG_D              0x0010  /* Display on */
  39#define LCD_FLAG_F              0x0020  /* Large font mode */
  40#define LCD_FLAG_N              0x0040  /* 2-rows mode */
  41#define LCD_FLAG_L              0x0080  /* Backlight enabled */
  42
  43/* LCD commands */
  44#define LCD_CMD_DISPLAY_CLEAR   0x01    /* Clear entire display */
  45
  46#define LCD_CMD_ENTRY_MODE      0x04    /* Set entry mode */
  47#define LCD_CMD_CURSOR_INC      0x02    /* Increment cursor */
  48
  49#define LCD_CMD_DISPLAY_CTRL    0x08    /* Display control */
  50#define LCD_CMD_DISPLAY_ON      0x04    /* Set display on */
  51#define LCD_CMD_CURSOR_ON       0x02    /* Set cursor on */
  52#define LCD_CMD_BLINK_ON        0x01    /* Set blink on */
  53
  54#define LCD_CMD_SHIFT           0x10    /* Shift cursor/display */
  55#define LCD_CMD_DISPLAY_SHIFT   0x08    /* Shift display instead of cursor */
  56#define LCD_CMD_SHIFT_RIGHT     0x04    /* Shift display/cursor to the right */
  57
  58#define LCD_CMD_FUNCTION_SET    0x20    /* Set function */
  59#define LCD_CMD_DATA_LEN_8BITS  0x10    /* Set data length to 8 bits */
  60#define LCD_CMD_TWO_LINES       0x08    /* Set to two display lines */
  61#define LCD_CMD_FONT_5X10_DOTS  0x04    /* Set char font to 5x10 dots */
  62
  63#define LCD_CMD_SET_CGRAM_ADDR  0x40    /* Set char generator RAM address */
  64
  65#define LCD_CMD_SET_DDRAM_ADDR  0x80    /* Set display data RAM address */
  66
  67#define LCD_ESCAPE_LEN          24      /* Max chars for LCD escape command */
  68#define LCD_ESCAPE_CHAR         27      /* Use char 27 for escape command */
  69
  70struct charlcd_priv {
  71        struct charlcd lcd;
  72
  73        struct delayed_work bl_work;
  74        struct mutex bl_tempo_lock;     /* Protects access to bl_tempo */
  75        bool bl_tempo;
  76
  77        bool must_clear;
  78
  79        /* contains the LCD config state */
  80        unsigned long int flags;
  81
  82        /* Contains the LCD X and Y offset */
  83        struct {
  84                unsigned long int x;
  85                unsigned long int y;
  86        } addr;
  87
  88        /* Current escape sequence and it's length or -1 if outside */
  89        struct {
  90                char buf[LCD_ESCAPE_LEN + 1];
  91                int len;
  92        } esc_seq;
  93
  94        unsigned long long drvdata[0];
  95};
  96
  97#define to_priv(p)      container_of(p, struct charlcd_priv, lcd)
  98
  99/* Device single-open policy control */
 100static atomic_t charlcd_available = ATOMIC_INIT(1);
 101
 102/* sleeps that many milliseconds with a reschedule */
 103static void long_sleep(int ms)
 104{
 105        if (in_interrupt())
 106                mdelay(ms);
 107        else
 108                schedule_timeout_interruptible(msecs_to_jiffies(ms));
 109}
 110
 111/* turn the backlight on or off */
 112static void charlcd_backlight(struct charlcd *lcd, int on)
 113{
 114        struct charlcd_priv *priv = to_priv(lcd);
 115
 116        if (!lcd->ops->backlight)
 117                return;
 118
 119        mutex_lock(&priv->bl_tempo_lock);
 120        if (!priv->bl_tempo)
 121                lcd->ops->backlight(lcd, on);
 122        mutex_unlock(&priv->bl_tempo_lock);
 123}
 124
 125static void charlcd_bl_off(struct work_struct *work)
 126{
 127        struct delayed_work *dwork = to_delayed_work(work);
 128        struct charlcd_priv *priv =
 129                container_of(dwork, struct charlcd_priv, bl_work);
 130
 131        mutex_lock(&priv->bl_tempo_lock);
 132        if (priv->bl_tempo) {
 133                priv->bl_tempo = false;
 134                if (!(priv->flags & LCD_FLAG_L))
 135                        priv->lcd.ops->backlight(&priv->lcd, 0);
 136        }
 137        mutex_unlock(&priv->bl_tempo_lock);
 138}
 139
 140/* turn the backlight on for a little while */
 141void charlcd_poke(struct charlcd *lcd)
 142{
 143        struct charlcd_priv *priv = to_priv(lcd);
 144
 145        if (!lcd->ops->backlight)
 146                return;
 147
 148        cancel_delayed_work_sync(&priv->bl_work);
 149
 150        mutex_lock(&priv->bl_tempo_lock);
 151        if (!priv->bl_tempo && !(priv->flags & LCD_FLAG_L))
 152                lcd->ops->backlight(lcd, 1);
 153        priv->bl_tempo = true;
 154        schedule_delayed_work(&priv->bl_work, LCD_BL_TEMPO_PERIOD * HZ);
 155        mutex_unlock(&priv->bl_tempo_lock);
 156}
 157EXPORT_SYMBOL_GPL(charlcd_poke);
 158
 159static void charlcd_gotoxy(struct charlcd *lcd)
 160{
 161        struct charlcd_priv *priv = to_priv(lcd);
 162        unsigned int addr;
 163
 164        /*
 165         * we force the cursor to stay at the end of the
 166         * line if it wants to go farther
 167         */
 168        addr = priv->addr.x < lcd->bwidth ? priv->addr.x & (lcd->hwidth - 1)
 169                                          : lcd->bwidth - 1;
 170        if (priv->addr.y & 1)
 171                addr += lcd->hwidth;
 172        if (priv->addr.y & 2)
 173                addr += lcd->bwidth;
 174        lcd->ops->write_cmd(lcd, LCD_CMD_SET_DDRAM_ADDR | addr);
 175}
 176
 177static void charlcd_home(struct charlcd *lcd)
 178{
 179        struct charlcd_priv *priv = to_priv(lcd);
 180
 181        priv->addr.x = 0;
 182        priv->addr.y = 0;
 183        charlcd_gotoxy(lcd);
 184}
 185
 186static void charlcd_print(struct charlcd *lcd, char c)
 187{
 188        struct charlcd_priv *priv = to_priv(lcd);
 189
 190        if (priv->addr.x < lcd->bwidth) {
 191                if (lcd->char_conv)
 192                        c = lcd->char_conv[(unsigned char)c];
 193                lcd->ops->write_data(lcd, c);
 194                priv->addr.x++;
 195        }
 196        /* prevents the cursor from wrapping onto the next line */
 197        if (priv->addr.x == lcd->bwidth)
 198                charlcd_gotoxy(lcd);
 199}
 200
 201static void charlcd_clear_fast(struct charlcd *lcd)
 202{
 203        int pos;
 204
 205        charlcd_home(lcd);
 206
 207        if (lcd->ops->clear_fast)
 208                lcd->ops->clear_fast(lcd);
 209        else
 210                for (pos = 0; pos < min(2, lcd->height) * lcd->hwidth; pos++)
 211                        lcd->ops->write_data(lcd, ' ');
 212
 213        charlcd_home(lcd);
 214}
 215
 216/* clears the display and resets X/Y */
 217static void charlcd_clear_display(struct charlcd *lcd)
 218{
 219        struct charlcd_priv *priv = to_priv(lcd);
 220
 221        lcd->ops->write_cmd(lcd, LCD_CMD_DISPLAY_CLEAR);
 222        priv->addr.x = 0;
 223        priv->addr.y = 0;
 224        /* we must wait a few milliseconds (15) */
 225        long_sleep(15);
 226}
 227
 228static int charlcd_init_display(struct charlcd *lcd)
 229{
 230        void (*write_cmd_raw)(struct charlcd *lcd, int cmd);
 231        struct charlcd_priv *priv = to_priv(lcd);
 232        u8 init;
 233
 234        if (lcd->ifwidth != 4 && lcd->ifwidth != 8)
 235                return -EINVAL;
 236
 237        priv->flags = ((lcd->height > 1) ? LCD_FLAG_N : 0) | LCD_FLAG_D |
 238                      LCD_FLAG_C | LCD_FLAG_B;
 239
 240        long_sleep(20);         /* wait 20 ms after power-up for the paranoid */
 241
 242        /*
 243         * 8-bit mode, 1 line, small fonts; let's do it 3 times, to make sure
 244         * the LCD is in 8-bit mode afterwards
 245         */
 246        init = LCD_CMD_FUNCTION_SET | LCD_CMD_DATA_LEN_8BITS;
 247        if (lcd->ifwidth == 4) {
 248                init >>= 4;
 249                write_cmd_raw = lcd->ops->write_cmd_raw4;
 250        } else {
 251                write_cmd_raw = lcd->ops->write_cmd;
 252        }
 253        write_cmd_raw(lcd, init);
 254        long_sleep(10);
 255        write_cmd_raw(lcd, init);
 256        long_sleep(10);
 257        write_cmd_raw(lcd, init);
 258        long_sleep(10);
 259
 260        if (lcd->ifwidth == 4) {
 261                /* Switch to 4-bit mode, 1 line, small fonts */
 262                lcd->ops->write_cmd_raw4(lcd, LCD_CMD_FUNCTION_SET >> 4);
 263                long_sleep(10);
 264        }
 265
 266        /* set font height and lines number */
 267        lcd->ops->write_cmd(lcd,
 268                LCD_CMD_FUNCTION_SET |
 269                ((lcd->ifwidth == 8) ? LCD_CMD_DATA_LEN_8BITS : 0) |
 270                ((priv->flags & LCD_FLAG_F) ? LCD_CMD_FONT_5X10_DOTS : 0) |
 271                ((priv->flags & LCD_FLAG_N) ? LCD_CMD_TWO_LINES : 0));
 272        long_sleep(10);
 273
 274        /* display off, cursor off, blink off */
 275        lcd->ops->write_cmd(lcd, LCD_CMD_DISPLAY_CTRL);
 276        long_sleep(10);
 277
 278        lcd->ops->write_cmd(lcd,
 279                LCD_CMD_DISPLAY_CTRL |  /* set display mode */
 280                ((priv->flags & LCD_FLAG_D) ? LCD_CMD_DISPLAY_ON : 0) |
 281                ((priv->flags & LCD_FLAG_C) ? LCD_CMD_CURSOR_ON : 0) |
 282                ((priv->flags & LCD_FLAG_B) ? LCD_CMD_BLINK_ON : 0));
 283
 284        charlcd_backlight(lcd, (priv->flags & LCD_FLAG_L) ? 1 : 0);
 285
 286        long_sleep(10);
 287
 288        /* entry mode set : increment, cursor shifting */
 289        lcd->ops->write_cmd(lcd, LCD_CMD_ENTRY_MODE | LCD_CMD_CURSOR_INC);
 290
 291        charlcd_clear_display(lcd);
 292        return 0;
 293}
 294
 295/*
 296 * These are the file operation function for user access to /dev/lcd
 297 * This function can also be called from inside the kernel, by
 298 * setting file and ppos to NULL.
 299 *
 300 */
 301
 302static inline int handle_lcd_special_code(struct charlcd *lcd)
 303{
 304        struct charlcd_priv *priv = to_priv(lcd);
 305
 306        /* LCD special codes */
 307
 308        int processed = 0;
 309
 310        char *esc = priv->esc_seq.buf + 2;
 311        int oldflags = priv->flags;
 312
 313        /* check for display mode flags */
 314        switch (*esc) {
 315        case 'D':       /* Display ON */
 316                priv->flags |= LCD_FLAG_D;
 317                processed = 1;
 318                break;
 319        case 'd':       /* Display OFF */
 320                priv->flags &= ~LCD_FLAG_D;
 321                processed = 1;
 322                break;
 323        case 'C':       /* Cursor ON */
 324                priv->flags |= LCD_FLAG_C;
 325                processed = 1;
 326                break;
 327        case 'c':       /* Cursor OFF */
 328                priv->flags &= ~LCD_FLAG_C;
 329                processed = 1;
 330                break;
 331        case 'B':       /* Blink ON */
 332                priv->flags |= LCD_FLAG_B;
 333                processed = 1;
 334                break;
 335        case 'b':       /* Blink OFF */
 336                priv->flags &= ~LCD_FLAG_B;
 337                processed = 1;
 338                break;
 339        case '+':       /* Back light ON */
 340                priv->flags |= LCD_FLAG_L;
 341                processed = 1;
 342                break;
 343        case '-':       /* Back light OFF */
 344                priv->flags &= ~LCD_FLAG_L;
 345                processed = 1;
 346                break;
 347        case '*':       /* Flash back light */
 348                charlcd_poke(lcd);
 349                processed = 1;
 350                break;
 351        case 'f':       /* Small Font */
 352                priv->flags &= ~LCD_FLAG_F;
 353                processed = 1;
 354                break;
 355        case 'F':       /* Large Font */
 356                priv->flags |= LCD_FLAG_F;
 357                processed = 1;
 358                break;
 359        case 'n':       /* One Line */
 360                priv->flags &= ~LCD_FLAG_N;
 361                processed = 1;
 362                break;
 363        case 'N':       /* Two Lines */
 364                priv->flags |= LCD_FLAG_N;
 365                break;
 366        case 'l':       /* Shift Cursor Left */
 367                if (priv->addr.x > 0) {
 368                        /* back one char if not at end of line */
 369                        if (priv->addr.x < lcd->bwidth)
 370                                lcd->ops->write_cmd(lcd, LCD_CMD_SHIFT);
 371                        priv->addr.x--;
 372                }
 373                processed = 1;
 374                break;
 375        case 'r':       /* shift cursor right */
 376                if (priv->addr.x < lcd->width) {
 377                        /* allow the cursor to pass the end of the line */
 378                        if (priv->addr.x < (lcd->bwidth - 1))
 379                                lcd->ops->write_cmd(lcd,
 380                                        LCD_CMD_SHIFT | LCD_CMD_SHIFT_RIGHT);
 381                        priv->addr.x++;
 382                }
 383                processed = 1;
 384                break;
 385        case 'L':       /* shift display left */
 386                lcd->ops->write_cmd(lcd, LCD_CMD_SHIFT | LCD_CMD_DISPLAY_SHIFT);
 387                processed = 1;
 388                break;
 389        case 'R':       /* shift display right */
 390                lcd->ops->write_cmd(lcd,
 391                                    LCD_CMD_SHIFT | LCD_CMD_DISPLAY_SHIFT |
 392                                    LCD_CMD_SHIFT_RIGHT);
 393                processed = 1;
 394                break;
 395        case 'k': {     /* kill end of line */
 396                int x;
 397
 398                for (x = priv->addr.x; x < lcd->bwidth; x++)
 399                        lcd->ops->write_data(lcd, ' ');
 400
 401                /* restore cursor position */
 402                charlcd_gotoxy(lcd);
 403                processed = 1;
 404                break;
 405        }
 406        case 'I':       /* reinitialize display */
 407                charlcd_init_display(lcd);
 408                processed = 1;
 409                break;
 410        case 'G': {
 411                /* Generator : LGcxxxxx...xx; must have <c> between '0'
 412                 * and '7', representing the numerical ASCII code of the
 413                 * redefined character, and <xx...xx> a sequence of 16
 414                 * hex digits representing 8 bytes for each character.
 415                 * Most LCDs will only use 5 lower bits of the 7 first
 416                 * bytes.
 417                 */
 418
 419                unsigned char cgbytes[8];
 420                unsigned char cgaddr;
 421                int cgoffset;
 422                int shift;
 423                char value;
 424                int addr;
 425
 426                if (!strchr(esc, ';'))
 427                        break;
 428
 429                esc++;
 430
 431                cgaddr = *(esc++) - '0';
 432                if (cgaddr > 7) {
 433                        processed = 1;
 434                        break;
 435                }
 436
 437                cgoffset = 0;
 438                shift = 0;
 439                value = 0;
 440                while (*esc && cgoffset < 8) {
 441                        shift ^= 4;
 442                        if (*esc >= '0' && *esc <= '9') {
 443                                value |= (*esc - '0') << shift;
 444                        } else if (*esc >= 'A' && *esc <= 'Z') {
 445                                value |= (*esc - 'A' + 10) << shift;
 446                        } else if (*esc >= 'a' && *esc <= 'z') {
 447                                value |= (*esc - 'a' + 10) << shift;
 448                        } else {
 449                                esc++;
 450                                continue;
 451                        }
 452
 453                        if (shift == 0) {
 454                                cgbytes[cgoffset++] = value;
 455                                value = 0;
 456                        }
 457
 458                        esc++;
 459                }
 460
 461                lcd->ops->write_cmd(lcd, LCD_CMD_SET_CGRAM_ADDR | (cgaddr * 8));
 462                for (addr = 0; addr < cgoffset; addr++)
 463                        lcd->ops->write_data(lcd, cgbytes[addr]);
 464
 465                /* ensures that we stop writing to CGRAM */
 466                charlcd_gotoxy(lcd);
 467                processed = 1;
 468                break;
 469        }
 470        case 'x':       /* gotoxy : LxXXX[yYYY]; */
 471        case 'y':       /* gotoxy : LyYYY[xXXX]; */
 472                if (!strchr(esc, ';'))
 473                        break;
 474
 475                while (*esc) {
 476                        if (*esc == 'x') {
 477                                esc++;
 478                                if (kstrtoul(esc, 10, &priv->addr.x) < 0)
 479                                        break;
 480                        } else if (*esc == 'y') {
 481                                esc++;
 482                                if (kstrtoul(esc, 10, &priv->addr.y) < 0)
 483                                        break;
 484                        } else {
 485                                break;
 486                        }
 487                }
 488
 489                charlcd_gotoxy(lcd);
 490                processed = 1;
 491                break;
 492        }
 493
 494        /* TODO: This indent party here got ugly, clean it! */
 495        /* Check whether one flag was changed */
 496        if (oldflags == priv->flags)
 497                return processed;
 498
 499        /* check whether one of B,C,D flags were changed */
 500        if ((oldflags ^ priv->flags) &
 501            (LCD_FLAG_B | LCD_FLAG_C | LCD_FLAG_D))
 502                /* set display mode */
 503                lcd->ops->write_cmd(lcd,
 504                        LCD_CMD_DISPLAY_CTRL |
 505                        ((priv->flags & LCD_FLAG_D) ? LCD_CMD_DISPLAY_ON : 0) |
 506                        ((priv->flags & LCD_FLAG_C) ? LCD_CMD_CURSOR_ON : 0) |
 507                        ((priv->flags & LCD_FLAG_B) ? LCD_CMD_BLINK_ON : 0));
 508        /* check whether one of F,N flags was changed */
 509        else if ((oldflags ^ priv->flags) & (LCD_FLAG_F | LCD_FLAG_N))
 510                lcd->ops->write_cmd(lcd,
 511                        LCD_CMD_FUNCTION_SET |
 512                        ((lcd->ifwidth == 8) ? LCD_CMD_DATA_LEN_8BITS : 0) |
 513                        ((priv->flags & LCD_FLAG_F) ? LCD_CMD_FONT_5X10_DOTS : 0) |
 514                        ((priv->flags & LCD_FLAG_N) ? LCD_CMD_TWO_LINES : 0));
 515        /* check whether L flag was changed */
 516        else if ((oldflags ^ priv->flags) & LCD_FLAG_L)
 517                charlcd_backlight(lcd, !!(priv->flags & LCD_FLAG_L));
 518
 519        return processed;
 520}
 521
 522static void charlcd_write_char(struct charlcd *lcd, char c)
 523{
 524        struct charlcd_priv *priv = to_priv(lcd);
 525
 526        /* first, we'll test if we're in escape mode */
 527        if ((c != '\n') && priv->esc_seq.len >= 0) {
 528                /* yes, let's add this char to the buffer */
 529                priv->esc_seq.buf[priv->esc_seq.len++] = c;
 530                priv->esc_seq.buf[priv->esc_seq.len] = 0;
 531        } else {
 532                /* aborts any previous escape sequence */
 533                priv->esc_seq.len = -1;
 534
 535                switch (c) {
 536                case LCD_ESCAPE_CHAR:
 537                        /* start of an escape sequence */
 538                        priv->esc_seq.len = 0;
 539                        priv->esc_seq.buf[priv->esc_seq.len] = 0;
 540                        break;
 541                case '\b':
 542                        /* go back one char and clear it */
 543                        if (priv->addr.x > 0) {
 544                                /*
 545                                 * check if we're not at the
 546                                 * end of the line
 547                                 */
 548                                if (priv->addr.x < lcd->bwidth)
 549                                        /* back one char */
 550                                        lcd->ops->write_cmd(lcd, LCD_CMD_SHIFT);
 551                                priv->addr.x--;
 552                        }
 553                        /* replace with a space */
 554                        lcd->ops->write_data(lcd, ' ');
 555                        /* back one char again */
 556                        lcd->ops->write_cmd(lcd, LCD_CMD_SHIFT);
 557                        break;
 558                case '\014':
 559                        /* quickly clear the display */
 560                        charlcd_clear_fast(lcd);
 561                        break;
 562                case '\n':
 563                        /*
 564                         * flush the remainder of the current line and
 565                         * go to the beginning of the next line
 566                         */
 567                        for (; priv->addr.x < lcd->bwidth; priv->addr.x++)
 568                                lcd->ops->write_data(lcd, ' ');
 569                        priv->addr.x = 0;
 570                        priv->addr.y = (priv->addr.y + 1) % lcd->height;
 571                        charlcd_gotoxy(lcd);
 572                        break;
 573                case '\r':
 574                        /* go to the beginning of the same line */
 575                        priv->addr.x = 0;
 576                        charlcd_gotoxy(lcd);
 577                        break;
 578                case '\t':
 579                        /* print a space instead of the tab */
 580                        charlcd_print(lcd, ' ');
 581                        break;
 582                default:
 583                        /* simply print this char */
 584                        charlcd_print(lcd, c);
 585                        break;
 586                }
 587        }
 588
 589        /*
 590         * now we'll see if we're in an escape mode and if the current
 591         * escape sequence can be understood.
 592         */
 593        if (priv->esc_seq.len >= 2) {
 594                int processed = 0;
 595
 596                if (!strcmp(priv->esc_seq.buf, "[2J")) {
 597                        /* clear the display */
 598                        charlcd_clear_fast(lcd);
 599                        processed = 1;
 600                } else if (!strcmp(priv->esc_seq.buf, "[H")) {
 601                        /* cursor to home */
 602                        charlcd_home(lcd);
 603                        processed = 1;
 604                }
 605                /* codes starting with ^[[L */
 606                else if ((priv->esc_seq.len >= 3) &&
 607                         (priv->esc_seq.buf[0] == '[') &&
 608                         (priv->esc_seq.buf[1] == 'L')) {
 609                        processed = handle_lcd_special_code(lcd);
 610                }
 611
 612                /* LCD special escape codes */
 613                /*
 614                 * flush the escape sequence if it's been processed
 615                 * or if it is getting too long.
 616                 */
 617                if (processed || (priv->esc_seq.len >= LCD_ESCAPE_LEN))
 618                        priv->esc_seq.len = -1;
 619        } /* escape codes */
 620}
 621
 622static struct charlcd *the_charlcd;
 623
 624static ssize_t charlcd_write(struct file *file, const char __user *buf,
 625                             size_t count, loff_t *ppos)
 626{
 627        const char __user *tmp = buf;
 628        char c;
 629
 630        for (; count-- > 0; (*ppos)++, tmp++) {
 631                if (!in_interrupt() && (((count + 1) & 0x1f) == 0))
 632                        /*
 633                         * let's be a little nice with other processes
 634                         * that need some CPU
 635                         */
 636                        schedule();
 637
 638                if (get_user(c, tmp))
 639                        return -EFAULT;
 640
 641                charlcd_write_char(the_charlcd, c);
 642        }
 643
 644        return tmp - buf;
 645}
 646
 647static int charlcd_open(struct inode *inode, struct file *file)
 648{
 649        struct charlcd_priv *priv = to_priv(the_charlcd);
 650
 651        if (!atomic_dec_and_test(&charlcd_available))
 652                return -EBUSY;  /* open only once at a time */
 653
 654        if (file->f_mode & FMODE_READ)  /* device is write-only */
 655                return -EPERM;
 656
 657        if (priv->must_clear) {
 658                charlcd_clear_display(&priv->lcd);
 659                priv->must_clear = false;
 660        }
 661        return nonseekable_open(inode, file);
 662}
 663
 664static int charlcd_release(struct inode *inode, struct file *file)
 665{
 666        atomic_inc(&charlcd_available);
 667        return 0;
 668}
 669
 670static const struct file_operations charlcd_fops = {
 671        .write   = charlcd_write,
 672        .open    = charlcd_open,
 673        .release = charlcd_release,
 674        .llseek  = no_llseek,
 675};
 676
 677static struct miscdevice charlcd_dev = {
 678        .minor  = LCD_MINOR,
 679        .name   = "lcd",
 680        .fops   = &charlcd_fops,
 681};
 682
 683static void charlcd_puts(struct charlcd *lcd, const char *s)
 684{
 685        const char *tmp = s;
 686        int count = strlen(s);
 687
 688        for (; count-- > 0; tmp++) {
 689                if (!in_interrupt() && (((count + 1) & 0x1f) == 0))
 690                        /*
 691                         * let's be a little nice with other processes
 692                         * that need some CPU
 693                         */
 694                        schedule();
 695
 696                charlcd_write_char(lcd, *tmp);
 697        }
 698}
 699
 700/* initialize the LCD driver */
 701static int charlcd_init(struct charlcd *lcd)
 702{
 703        struct charlcd_priv *priv = to_priv(lcd);
 704        int ret;
 705
 706        if (lcd->ops->backlight) {
 707                mutex_init(&priv->bl_tempo_lock);
 708                INIT_DELAYED_WORK(&priv->bl_work, charlcd_bl_off);
 709        }
 710
 711        /*
 712         * before this line, we must NOT send anything to the display.
 713         * Since charlcd_init_display() needs to write data, we have to
 714         * enable mark the LCD initialized just before.
 715         */
 716        ret = charlcd_init_display(lcd);
 717        if (ret)
 718                return ret;
 719
 720        /* display a short message */
 721#ifdef CONFIG_PANEL_CHANGE_MESSAGE
 722#ifdef CONFIG_PANEL_BOOT_MESSAGE
 723        charlcd_puts(lcd, "\x1b[Lc\x1b[Lb\x1b[L*" CONFIG_PANEL_BOOT_MESSAGE);
 724#endif
 725#else
 726        charlcd_puts(lcd, "\x1b[Lc\x1b[Lb\x1b[L*Linux-" UTS_RELEASE "\n");
 727#endif
 728        /* clear the display on the next device opening */
 729        priv->must_clear = true;
 730        charlcd_home(lcd);
 731        return 0;
 732}
 733
 734struct charlcd *charlcd_alloc(unsigned int drvdata_size)
 735{
 736        struct charlcd_priv *priv;
 737        struct charlcd *lcd;
 738
 739        priv = kzalloc(sizeof(*priv) + drvdata_size, GFP_KERNEL);
 740        if (!priv)
 741                return NULL;
 742
 743        priv->esc_seq.len = -1;
 744
 745        lcd = &priv->lcd;
 746        lcd->ifwidth = 8;
 747        lcd->bwidth = DEFAULT_LCD_BWIDTH;
 748        lcd->hwidth = DEFAULT_LCD_HWIDTH;
 749        lcd->drvdata = priv->drvdata;
 750
 751        return lcd;
 752}
 753EXPORT_SYMBOL_GPL(charlcd_alloc);
 754
 755static int panel_notify_sys(struct notifier_block *this, unsigned long code,
 756                            void *unused)
 757{
 758        struct charlcd *lcd = the_charlcd;
 759
 760        switch (code) {
 761        case SYS_DOWN:
 762                charlcd_puts(lcd,
 763                             "\x0cReloading\nSystem...\x1b[Lc\x1b[Lb\x1b[L+");
 764                break;
 765        case SYS_HALT:
 766                charlcd_puts(lcd, "\x0cSystem Halted.\x1b[Lc\x1b[Lb\x1b[L+");
 767                break;
 768        case SYS_POWER_OFF:
 769                charlcd_puts(lcd, "\x0cPower off.\x1b[Lc\x1b[Lb\x1b[L+");
 770                break;
 771        default:
 772                break;
 773        }
 774        return NOTIFY_DONE;
 775}
 776
 777static struct notifier_block panel_notifier = {
 778        panel_notify_sys,
 779        NULL,
 780        0
 781};
 782
 783int charlcd_register(struct charlcd *lcd)
 784{
 785        int ret;
 786
 787        ret = charlcd_init(lcd);
 788        if (ret)
 789                return ret;
 790
 791        ret = misc_register(&charlcd_dev);
 792        if (ret)
 793                return ret;
 794
 795        the_charlcd = lcd;
 796        register_reboot_notifier(&panel_notifier);
 797        return 0;
 798}
 799EXPORT_SYMBOL_GPL(charlcd_register);
 800
 801int charlcd_unregister(struct charlcd *lcd)
 802{
 803        struct charlcd_priv *priv = to_priv(lcd);
 804
 805        unregister_reboot_notifier(&panel_notifier);
 806        charlcd_puts(lcd, "\x0cLCD driver unloaded.\x1b[Lc\x1b[Lb\x1b[L-");
 807        misc_deregister(&charlcd_dev);
 808        the_charlcd = NULL;
 809        if (lcd->ops->backlight) {
 810                cancel_delayed_work_sync(&priv->bl_work);
 811                priv->lcd.ops->backlight(&priv->lcd, 0);
 812        }
 813
 814        return 0;
 815}
 816EXPORT_SYMBOL_GPL(charlcd_unregister);
 817
 818MODULE_LICENSE("GPL");
 819