summaryrefslogtreecommitdiff
path: root/target/linux/ep93xx/patches-2.6.30/002-lcd-linux-hd44780.patch
diff options
context:
space:
mode:
Diffstat (limited to 'target/linux/ep93xx/patches-2.6.30/002-lcd-linux-hd44780.patch')
-rw-r--r--target/linux/ep93xx/patches-2.6.30/002-lcd-linux-hd44780.patch4342
1 files changed, 4342 insertions, 0 deletions
diff --git a/target/linux/ep93xx/patches-2.6.30/002-lcd-linux-hd44780.patch b/target/linux/ep93xx/patches-2.6.30/002-lcd-linux-hd44780.patch
new file mode 100644
index 0000000000..7480b28117
--- /dev/null
+++ b/target/linux/ep93xx/patches-2.6.30/002-lcd-linux-hd44780.patch
@@ -0,0 +1,4342 @@
+Index: linux-2.6.30.9/arch/arm/Kconfig
+===================================================================
+--- linux-2.6.30.9.orig/arch/arm/Kconfig 2009-10-05 17:38:08.000000000 +0200
++++ linux-2.6.30.9/arch/arm/Kconfig 2009-11-24 02:01:42.000000000 +0100
+@@ -1407,6 +1407,8 @@
+
+ source "drivers/accessibility/Kconfig"
+
++source "drivers/lcd-linux/Kconfig"
++
+ source "drivers/leds/Kconfig"
+
+ source "drivers/rtc/Kconfig"
+Index: linux-2.6.30.9/drivers/Makefile
+===================================================================
+--- linux-2.6.30.9.orig/drivers/Makefile 2009-10-05 17:38:08.000000000 +0200
++++ linux-2.6.30.9/drivers/Makefile 2009-11-24 02:01:42.000000000 +0100
+@@ -106,4 +106,5 @@
+ obj-$(CONFIG_SSB) += ssb/
+ obj-$(CONFIG_VIRTIO) += virtio/
+ obj-$(CONFIG_STAGING) += staging/
++obj-$(CONFIG_LCD_LINUX) += lcd-linux/
+ obj-y += platform/
+Index: linux-2.6.30.9/drivers/lcd-linux/Config.in
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.30.9/drivers/lcd-linux/Config.in 2009-11-24 02:01:42.000000000 +0100
+@@ -0,0 +1,8 @@
++if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
++ mainmenu_option next_comment
++ comment 'LCD support'
++
++ tristate 'LCD-Linux layer' CONFIG_LCD_LINUX
++ dep_tristate ' HD44780 controller' CONFIG_LCD_HD44780 $CONFIG_LCD_LINUX
++ endmenu
++fi
+Index: linux-2.6.30.9/drivers/lcd-linux/Kconfig
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.30.9/drivers/lcd-linux/Kconfig 2009-11-24 02:01:42.000000000 +0100
+@@ -0,0 +1,33 @@
++menu "LCD-Linux support"
++ depends on EXPERIMENTAL
++
++config LCD_LINUX
++ tristate "LCD-Linux layer"
++ default m
++ help
++ LCD-Linux provides an easy way to drive LCD displays under
++ Linux by creating a character which can be read or written.
++ It features complete VT102 emulation and recognizes
++ many escape sequences. If you want to use it you must also
++ choose an appropriate driver, otherwise it will not be
++ very useful. For more information see
++ http://lcd-linux.sourceforge.net/
++
++ To compile LCD-Linux as a module, choose M here:
++ the module will be called lcd-linux.
++
++config LCD_HD44780
++ tristate "HD44780 controller"
++ depends on LCD_LINUX && MACH_SIM_ONE
++ default m
++ help
++ This is a LCD-Linux driver for LCD displays based on the
++ Hitachi HD44780 (and compatible) controllers connected
++ to the SimOne LCD port.
++
++ To compile this driver as a module, choose M here:
++ the module will be called hd44780.
++
++ If unsure, say N.
++
++endmenu
+Index: linux-2.6.30.9/drivers/lcd-linux/Makefile
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.30.9/drivers/lcd-linux/Makefile 2009-11-24 02:01:42.000000000 +0100
+@@ -0,0 +1,8 @@
++#
++#
++# Standard Makefile to statically compile LCD-Linux into the kernel
++# Linux 2.6
++
++obj-$(CONFIG_LCD_LINUX) += lcd-linux.o
++obj-$(CONFIG_LCD_HD44780) += hd44780.o
++
+Index: linux-2.6.30.9/drivers/lcd-linux/cgram/default.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.30.9/drivers/lcd-linux/cgram/default.h 2009-11-24 02:01:42.000000000 +0100
+@@ -0,0 +1,37 @@
++/* default.h
++ *
++ *
++ *
++ * Default user defined characters for lcdmod.
++ *
++ * Copyright (C) by Michael McLellan (mikey@cs.auckland.ac.nz)
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ *
++ *
++ */
++
++static void init_charmap(void)
++{
++}
++
++static unsigned char cg0[] = { 0x1f, 0x1f, 0x11, 0x0f, 0x11, 0x1e, 0x01, 0x1f };
++static unsigned char cg1[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f };
++static unsigned char cg2[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x1f };
++static unsigned char cg3[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x1f, 0x1f };
++static unsigned char cg4[] = { 0x00, 0x00, 0x00, 0x00, 0x1f, 0x1f, 0x1f, 0x1f };
++static unsigned char cg5[] = { 0x00, 0x00, 0x00, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f };
++static unsigned char cg6[] = { 0x00, 0x00, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f };
++static unsigned char cg7[] = { 0x00, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f };
+Index: linux-2.6.30.9/drivers/lcd-linux/charmap.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.30.9/drivers/lcd-linux/charmap.h 2009-11-24 02:01:42.000000000 +0100
+@@ -0,0 +1,79 @@
++/* charmap.h
++ *
++ *
++ *
++ * Character mapping for HD44780 devices by Mark Haemmerling <mail@markh.de>.
++ *
++ * Translates ISO 8859-1 to HD44780 charset.
++ * HD44780 charset reference: http://markh.de/hd44780-charset.png
++ *
++ * Initial table taken from lcd.o Linux kernel driver by
++ * Nils Faerber <nilsf@users.sourceforge.net>. Thanks!
++ *
++ * This file is released under the GNU General Public License. Refer to the
++ * COPYING file distributed with this package.
++ *
++ * Following translations are being performed:
++ * - maps umlaut accent characters to the corresponding umlaut characters
++ * - maps other accent characters to the characters without accents
++ * - maps beta (=ringel-S), micro and Yen
++ *
++ * Alternative mappings:
++ * - #112 ("p") -> #240 (large "p"), orig. mapped -> #112
++ * - #113 ("q") -> #241 (large "q"), orig. mapped -> #113
++ *
++ * HD44780 misses backslash
++ *
++ */
++
++static unsigned char charmap[] = {
++
++/* 0 - 31 */
++ 0, 1, 2, 3, 4, 5, 6, 7,
++ 8, 9, 10, 11, 12, 13, 14, 15,
++ 16, 17, 18, 19, 20, 21, 22, 23,
++ 24, 25, 26, 27, 28, 29, 30, 31,
++
++/* 32 - 63 */
++ 32, 33, 34, 35, 36, 37, 38, 39,
++ 40, 41, 42, 43, 44, 45, 46, 47,
++ 48, 49, 50, 51, 52, 53, 54, 55,
++ 56, 57, 58, 59, 60, 61, 62, 63,
++
++/* 64 - 95 */
++ 64, 65, 66, 67, 68, 69, 70, 71,
++ 72, 73, 74, 75, 76, 77, 78, 79,
++ 80, 81, 82, 83, 84, 85, 86, 87,
++ 88, 89, 90, 91, 47, 93, 94, 95,
++
++/* 96 - 127 */
++ 96, 97, 98, 99, 100, 101, 102, 103,
++104, 105, 106, 107, 108, 109, 110, 111,
++112, 113, 114, 115, 116, 117, 118, 119,
++120, 121, 122, 123, 124, 125, 126, 127,
++
++/* 128 - 159 */
++128, 129, 130, 131, 132, 133, 134, 135,
++136, 137, 138, 139, 140, 141, 142, 143,
++144, 145, 146, 147, 148, 149, 150, 151,
++152, 153, 154, 155, 156, 157, 158, 159,
++
++/* 160 - 191 */
++160, 33, 236, 237, 164, 92, 124, 167,
++ 34, 169, 170, 171, 172, 173, 174, 175,
++223, 177, 178, 179, 39, 249, 247, 165,
++ 44, 185, 186, 187, 188, 189, 190, 63,
++
++/* 192 - 223 */
++ 65, 65, 65, 65, 225, 65, 65, 67,
++ 69, 69, 69, 69, 73, 73, 73, 73,
++ 68, 78, 79, 79, 79, 79, 239, 120,
++ 48, 85, 85, 85, 245, 89, 240, 226,
++
++/* 224 - 255 */
++ 97, 97, 97, 97, 225, 97, 97, 99,
++101, 101, 101, 101, 105, 105, 105, 105,
++111, 110, 111, 111, 111, 111, 239, 253,
++ 48, 117, 117, 117, 245, 121, 240, 255
++
++};
+Index: linux-2.6.30.9/drivers/lcd-linux/commands.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.30.9/drivers/lcd-linux/commands.h 2009-11-24 02:01:42.000000000 +0100
+@@ -0,0 +1,77 @@
++/* commands.h
++ *
++ *
++ *
++ * LCD-Linux:
++ * Driver for HD44780 compatible displays connected to the parallel port.
++ *
++ * HD44780 commands.
++ *
++ * Copyright (C) 2004 - 2007 Mattia Jona-Lasinio (mjona@users.sourceforge.net)
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ *
++ */
++
++#ifndef HD44780_COMMANDS_H
++#define HD44780_COMMANDS_H
++
++/*** HD44780 Command Set ***/
++
++/* Clear Display*/
++#define CLR_DISP 0x01 /* Clear entire display; cursor at row 0, column 0 */
++
++/* Return Home */
++#define RET_HOME 0x02 /* Cursor at row 0, column 0; display content doesn't change */
++
++/* Entry Mode Set */
++#define ENTRY_MODE_SET 0x04
++#define DISP_SHIFT_ON (ENTRY_MODE_SET | 0x01) /* Shift display, not cursor after data write */
++#define DISP_SHIFT_OFF (ENTRY_MODE_SET | 0x00) /* Shift cursor, not display after data write */
++#define CURS_INC (ENTRY_MODE_SET | 0x02) /* Shift on the right after data read/write */
++#define CURS_DEC (ENTRY_MODE_SET | 0x00) /* Shift on the left after data read/write */
++
++/* Display on/off Control */
++#define DISP_ONOFF_CNTR 0x08
++#define BLINK_ON (DISP_ONOFF_CNTR | 0x01) /* Cursor blinking on */
++#define BLINK_OFF (DISP_ONOFF_CNTR | 0x00) /* Cursor blinking off */
++#define CURS_ON (DISP_ONOFF_CNTR | 0x02) /* Display Cursor */
++#define CURS_OFF (DISP_ONOFF_CNTR | 0x00) /* Hide Cursor */
++#define DISP_ON (DISP_ONOFF_CNTR | 0x04) /* Turn on display updating */
++#define DISP_OFF (DISP_ONOFF_CNTR | 0x00) /* Freeze display content */
++
++/* Cursor or Display Shift */
++#define CURS_DISP_SHIFT 0x10
++#define SHIFT_RIGHT (CURS_DISP_SHIFT | 0x04) /* Shift on the right */
++#define SHIFT_LEFT (CURS_DISP_SHIFT | 0x00) /* Shift on the left */
++#define SHIFT_DISP (CURS_DISP_SHIFT | 0x08) /* Shift display */
++#define SHIFT_CURS (CURS_DISP_SHIFT | 0x00) /* Shift cursor */
++
++/* Function Set */
++#define FUNC_SET 0x20
++#define FONT_5X10 (FUNC_SET | 0x04) /* Select 5x10 dots font */
++#define FONT_5X8 (FUNC_SET | 0x00) /* Select 5x8 dots font */
++#define DISP_2_LINES (FUNC_SET | 0x08) /* Select 2 lines display (only 5x8 font allowed) */
++#define DISP_1_LINE (FUNC_SET | 0x00) /* Select 1 line display */
++#define BUS_8_BITS (FUNC_SET | 0x10) /* Set 8 data bits */
++#define BUS_4_BITS (FUNC_SET | 0x00) /* Set 4 data bits */
++
++/* Set CGRAM Address */
++#define CGRAM_IO 0x40 /* Base CGRAM address */
++
++/* Set DDRAM Address */
++#define DDRAM_IO 0x80 /* Base DDRAM address */
++
++#endif /* commands included */
+Index: linux-2.6.30.9/drivers/lcd-linux/config.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.30.9/drivers/lcd-linux/config.h 2009-11-24 02:01:42.000000000 +0100
+@@ -0,0 +1,73 @@
++/* config.h
++ *
++ *
++ *
++ * Configure file for LCD-Linux. Here you must specify your hardware setup and
++ * timings constants. The default values will probably be right for you.
++ *
++ * Copyright (C) 2005 - 2007 Mattia Jona-Lasinio (mjona@users.sourceforge.net)
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ *
++ *
++ */
++
++/* Setup the default user defined characters in CGRAM */
++#include "cgram/default.h"
++
++/* Don't modify the default timing constants
++ * unless you know what you are doing.
++ */
++
++/* Execution times (in microseconds) */
++#define T_READ 60 /* Read execution time (min 43 us) */
++#define T_WRITE 60 /* Write execution time (min 43 us) */
++#define T_BF 2 /* Busy flag polling time (min 1 us) */
++
++/* Timings in nanoseconds */
++#define T_AS 200 /* Address set-up time (min 140 ns) */
++#define T_EH 500 /* Enable high time (min 450 ns) */
++#define T_EL 600 /* Enable low time (min 500 ns) */
++
++/* Various constants */
++#define DFLT_NUM_CNTR 1 /* Default number of controllers the display has */
++#define DFLT_CNTR_ROWS 2 /* Default number of rows per controller */
++#define DFLT_CNTR_COLS 16 /* Default number of columns the display has */
++#define DFLT_VS_ROWS 25 /* Default number of rows for the virtual screen */
++#define DFLT_VS_COLS 80 /* Default number of columns for the virtual screen */
++#define DFLT_TABSTOP 3 /* Default length of tabs */
++#define DFLT_FLAGS (HD44780_CHECK_BF | HD44780_4BITS_BUS ) /* Default flags */
++
++#define MAX_CNTR_ROWS 4 /* The HD44780 supports up to 4 lines as a fake 2 lines mode */
++#define MAX_CNTR_COLS 80 /* The HD44780 supports up to 80 characters (1*80; 2*40; etc) */
++
++#define SETUP 4
++#define HIGH_NIBBLE_WRITE(x) (((x) >> (4-SETUP)) & (0x0f << SETUP))
++#define LOW_NIBBLE_WRITE(x) (((x) << SETUP) & (0x0f << SETUP))
++#define HIGH_NIBBLE_READ(x) (((x) & (0x0f << SETUP)) << (4-SETUP))
++#define LOW_NIBBLE_READ(x) (((x) & (0x0f << SETUP)) >> SETUP)
++
++
++#define SIMONE_LCD_RS EP93XX_GPIO_LINE_A(2) /* OUT */
++#define SIMONE_LCD_RD EP93XX_GPIO_LINE_A(3) /* OUT */
++#define SIMONE_LCD_EN EP93XX_GPIO_LINE_B(4) /* EGPIO12 OUT */
++#define SIMONE_LCD_BCKLIGHT EP93XX_GPIO_LINE_B(5) /* EGPIO13 OUT */
++
++#define SIMONE_LCD_DATA0 EP93XX_GPIO_LINE_A(4)
++#define SIMONE_LCD_DATA1 EP93XX_GPIO_LINE_A(5)
++#define SIMONE_LCD_DATA2 EP93XX_GPIO_LINE_A(6)
++#define SIMONE_LCD_DATA3 EP93XX_GPIO_LINE_A(7)
++
++
+Index: linux-2.6.30.9/drivers/lcd-linux/hd44780.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.30.9/drivers/lcd-linux/hd44780.c 2009-11-24 02:05:29.000000000 +0100
+@@ -0,0 +1,860 @@
++/* hd44780.c
++ *
++ *
++ *
++ * LCD-Linux:
++ * Driver for HD44780 compatible displays connected to the parallel port.
++ *
++ * Copyright (C) 2005 - 2007 Mattia Jona-Lasinio (mjona@users.sourceforge.net)
++ * Adapted to Sim.One Hardware by Nuccio Raciti (raciti.nuccio@gmail.com)
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ *
++ */
++
++
++#include <linux/autoconf.h>
++
++
++#ifdef CONFIG_PROC_FS
++#define USE_PROC
++#else
++#undef USE_PROC
++#endif
++
++#include <linux/bitops.h>
++#include <linux/kernel.h>
++#include <linux/module.h>
++
++#include <linux/delay.h>
++#include <linux/fs.h>
++
++#include <asm/uaccess.h>
++#include <linux/init.h>
++
++#include <asm/io.h>
++#include <linux/ioport.h>
++#include <asm/gpio.h>
++
++#ifdef USE_PROC
++#include <linux/proc_fs.h>
++#endif
++
++#define LCD_LINUX_MAIN
++#include <linux/hd44780.h>
++
++#include "charmap.h"
++#include "commands.h"
++#include "config.h"
++
++/** Function prototypes **/
++static void read_display(unsigned char *byte, unsigned char bitmask);
++static void write_display(unsigned char byte, unsigned char bitmask);
++
++/* Initialization */
++static int hd44780_validate_driver(void);
++static int hd44780_init_port(void);
++static int hd44780_cleanup_port(void);
++static int hd44780_init_display(void);
++static int hd44780_cleanup_display(void);
++
++/* Write */
++static void hd44780_address_mode(int);
++static void hd44780_clear_display(void);
++static void hd44780_write_char(unsigned int, unsigned short);
++static void hd44780_write_cgram_char(unsigned char, unsigned char *);
++
++/* Read */
++static void check_bf(unsigned char);
++static void hd44780_read_char(unsigned int, unsigned short *);
++static void hd44780_read_cgram_char(unsigned char, unsigned char *);
++
++/* Input handling */
++static int hd44780_handle_custom_char(unsigned int);
++static int hd44780_handle_custom_ioctl(unsigned int,unsigned long , unsigned int);
++
++/* Proc operations */
++#ifdef USE_PROC
++static void create_proc_entries(void);
++static void remove_proc_entries(void);
++#endif
++
++/* hd44780 access */
++#define ACCESS_TO_READ 0
++#define ACCESS_TO_WRITE 1
++#define ACCESS_TO_DATA 2
++
++/* hd44780_flags */
++#define _CHECK_BF 0 /* Do busy-flag checking */
++#define _4BITS_BUS 1 /* The bus is 4 bits long */
++#define _5X10_FONT 2 /* Use 5x10 font */
++#define CURSOR_BLINK 3 /* Make the cursor blinking */
++#define SHOW_CURSOR 4 /* Make the cursor visible */
++#define DISPLAY_ON 5 /* Display status: on or off */
++#define INC_ADDR 6 /* Increment address after data read/write */
++#define BACKLIGHT 7 /* Display backlight: on or off */
++#define CGRAM_STATE 9 /* Controller status bitmask (bits 9->15): DDRAM or CGRAM access */
++#define ESC_MASK 0x00ff0000
++#define PROC_MASK 0x0f000000
++
++#define SET_STATE(state, mask) (hd44780_flags = (hd44780_flags & ~(mask)) | ((state) & (mask)))
++#define SET_ESC_STATE(state) SET_STATE((state) << 16, ESC_MASK)
++#define SET_PROC_LEVEL(level) SET_STATE((level) << 24, PROC_MASK)
++#define ESC_STATE ((hd44780_flags & ESC_MASK) >> 16)
++#define PROC_LEVEL ((hd44780_flags & PROC_MASK) >> 24)
++
++/* globals */
++static unsigned int disp_size; /* Display size (rows*columns) */
++static unsigned int disp_offset[1]; /* Physical cursor position on the display */
++static unsigned long hd44780_flags; /* Driver flags for internal use only */
++
++static struct lcd_parameters par = {
++ .name = HD44780_STRING,
++ .minor = HD44780_MINOR,
++ .flags = DFLT_FLAGS,
++ .tabstop = DFLT_TABSTOP,
++ .num_cntr = 1,
++ .cntr_rows = DFLT_CNTR_ROWS,
++ .cntr_cols = DFLT_CNTR_COLS,
++ .vs_rows = DFLT_VS_ROWS,
++ .vs_cols = DFLT_VS_COLS,
++ .cgram_chars = 8,
++ .cgram_bytes = 8,
++ .cgram_char0 = 0,
++};
++/* End of globals */
++
++#ifdef MODULE
++#include <linux/device.h>
++MODULE_ALIAS_CHARDEV(LCD_MAJOR, HD44780_MINOR);
++#include <linux/kmod.h>
++
++static unsigned short flags = DFLT_FLAGS;
++static unsigned short tabstop = DFLT_TABSTOP;
++static unsigned short cntr_rows = DFLT_CNTR_ROWS;
++static unsigned short cntr_cols = DFLT_CNTR_COLS;
++static unsigned short vs_rows = DFLT_VS_ROWS;
++static unsigned short vs_cols = DFLT_VS_COLS;
++static unsigned short minor = HD44780_MINOR;
++
++MODULE_DESCRIPTION("LCD SimOne driver for HD44780 compatible controllers.");
++MODULE_AUTHOR("Nuccio Raciti (raciti.nuccio@gmail.com)");
++#ifdef MODULE_LICENSE
++MODULE_LICENSE("GPL");
++#endif
++module_param(flags, ushort, 0444);
++module_param(cntr_rows, ushort, 0444);
++module_param(cntr_cols, ushort, 0444);
++module_param(vs_rows, ushort, 0444);
++module_param(vs_cols, ushort, 0444);
++module_param(tabstop, ushort, 0444);
++module_param(minor, ushort, 0444);
++
++MODULE_PARM_DESC(flags, "Various flags (see Documentation)");
++MODULE_PARM_DESC(cntr_rows, "Number of rows per controller on the LCD: 1, 2, 4 (default: " string(DFLT_CNTR_ROWS) ")");
++MODULE_PARM_DESC(cntr_cols, "Number of columns on the LCD (default: " string(DFLT_CNTR_COLS) ", max: " string(MAX_CNTR_COLS) ")");
++MODULE_PARM_DESC(vs_rows, "Number of rows of the virtual screen (default: " string(DFLT_VS_ROWS) ")");
++MODULE_PARM_DESC(vs_cols, "Number of columns of the virtual screen (default: " string(DFLT_VS_COLS) ")");
++MODULE_PARM_DESC(tabstop, "Tab character length (default: " string(DFLT_TABSTOP) ")");
++MODULE_PARM_DESC(minor, "Assigned minor number (default: " string(HD44780_MINOR) ")");
++#else
++
++/*
++ * Parse boot command line
++ *
++ * hd44780=cntr_rows,cntr_cols,vs_rows,vs_cols,flags,minor,tabstop
++ */
++static int __init hd44780_boot_init(char *cmdline)
++{
++ char *str = cmdline;
++ int idx = 0;
++ unsigned short *args[] = {
++ &par.cntr_rows,
++ &par.cntr_cols,
++ &par.vs_rows,
++ &par.vs_cols,
++ &par.flags,
++ &par.num_cntr,
++ &par.minor,
++ &par.tabstop,
++ };
++
++ while (*cmdline && idx < (sizeof(args)/sizeof(unsigned short *))) {
++ switch (*str) {
++ case ',':
++ *str++ = 0;
++ case 0:
++ if (strlen(cmdline))
++ *args[idx] = simple_strtoul(cmdline, NULL, 0);
++ ++idx;
++ cmdline = str;
++ break;
++ default:
++ ++str;
++ break;
++ }
++ }
++
++ return (1);
++}
++
++__setup("hd44780=", hd44780_boot_init);
++#endif /* MODULE */
++
++/* Macros for iterator handling */
++static inline unsigned int iterator_inc_(unsigned int iterator, const unsigned int module)
++{
++ return ((++iterator)%module);
++}
++
++static inline unsigned int iterator_dec_(unsigned int iterator, const unsigned int module)
++{
++ return (iterator ? --iterator : module-1);
++}
++
++#define iterator_inc(iterator, module) (iterator = iterator_inc_(iterator, module))
++#define iterator_dec(iterator, module) (iterator = iterator_dec_(iterator, module))
++
++static inline void set_lines(unsigned char bitmask)
++{
++ gpio_set_value(SIMONE_LCD_EN, 0); /* Disable */
++
++ if(bitmask & ACCESS_TO_WRITE ) {
++ gpio_direction_output (SIMONE_LCD_DATA0 , 0);
++ gpio_direction_output (SIMONE_LCD_DATA1 , 0);
++ gpio_direction_output (SIMONE_LCD_DATA2 , 0);
++ gpio_direction_output (SIMONE_LCD_DATA3 , 0);
++ gpio_set_value(SIMONE_LCD_RD, 0); /* Write */
++ } else {
++
++ gpio_direction_input (SIMONE_LCD_DATA0);
++ gpio_direction_input (SIMONE_LCD_DATA1);
++ gpio_direction_input (SIMONE_LCD_DATA2);
++ gpio_direction_input (SIMONE_LCD_DATA3);
++ gpio_set_value(SIMONE_LCD_RD, 1); /* Read */
++ }
++
++ if(bitmask & ACCESS_TO_DATA )
++ gpio_set_value(SIMONE_LCD_RS, 1); /* Data */
++ else
++ gpio_set_value(SIMONE_LCD_RS, 0); /* Cmds*/
++
++ if(test_bit(BACKLIGHT, &hd44780_flags))
++ gpio_set_value(SIMONE_LCD_BCKLIGHT, 1);
++ else
++ gpio_set_value(SIMONE_LCD_BCKLIGHT, 0);
++}
++
++
++/* Low level read from the display */
++static inline unsigned char __read_display(unsigned char bitmask)
++{
++ unsigned char byte;
++
++ set_lines (bitmask);
++
++ ndelay(T_AS); /* Address set-up time */
++ gpio_set_value(SIMONE_LCD_EN, 1); /* Enable */
++ ndelay(T_EH);/* Enable high time */
++
++ byte = (gpio_get_value(SIMONE_LCD_DATA0) << 4);
++ byte |= (gpio_get_value(SIMONE_LCD_DATA1) << 5);
++ byte |= (gpio_get_value(SIMONE_LCD_DATA2) << 6);
++ byte |= (gpio_get_value(SIMONE_LCD_DATA3) << 7);
++
++ gpio_set_value(SIMONE_LCD_EN, 0); /* Disable */
++
++ ndelay(T_EL); /* Enable low time */
++
++
++ return (byte);
++
++}
++
++/* Low level write to the display */
++static inline void __write_display(unsigned char data, unsigned char bitmask)
++{
++ set_lines(bitmask);
++
++ if(data & 0x10)
++ gpio_set_value(SIMONE_LCD_DATA0, 1);
++ else
++ gpio_set_value(SIMONE_LCD_DATA0, 0);
++
++ if(data & 0x20)
++ gpio_set_value(SIMONE_LCD_DATA1, 1);
++ else
++ gpio_set_value(SIMONE_LCD_DATA1, 0);
++
++ if(data & 0x40)
++ gpio_set_value(SIMONE_LCD_DATA2, 1);
++ else
++ gpio_set_value(SIMONE_LCD_DATA2, 0);
++
++ if(data & 0x80)
++ gpio_set_value(SIMONE_LCD_DATA3, 1);
++ else
++ gpio_set_value(SIMONE_LCD_DATA3, 0);
++
++ ndelay(T_AS); /* Address set-up time */
++ gpio_set_value(SIMONE_LCD_EN, 1);
++ ndelay(T_EH); /* Enable high time */
++
++ gpio_set_value(SIMONE_LCD_EN, 0); /* Disable */
++ ndelay(T_EL); /* Enable low time */
++}
++
++
++/* Read data from the display (4 bit bus, check busy flag) */
++static void read_display (unsigned char *byte,unsigned char bitmask)
++{
++ if(bitmask) check_bf(bitmask);
++ *byte = HIGH_NIBBLE_READ(__read_display(bitmask));
++ if(bitmask) check_bf(bitmask);
++ *byte |= LOW_NIBBLE_READ(__read_display(bitmask));
++}
++
++
++/* Output commands or data to the display (4 bit bus, check busy flag) */
++static void write_display(unsigned char byte,unsigned char bitmask)
++{
++ check_bf(bitmask);
++ __write_display(HIGH_NIBBLE_WRITE(byte),bitmask);
++ check_bf(bitmask);
++ __write_display(LOW_NIBBLE_WRITE(byte),bitmask);
++}
++
++
++/* Read Address Counter AC from the display */
++static unsigned char read_ac(unsigned char bitmask)
++{
++ unsigned char byte;
++
++ read_display(&byte, bitmask);
++
++ return (byte);
++}
++
++
++/* Do busy-flag check */
++static void check_bf(unsigned char bitmask)
++{
++ unsigned int timeout = 20;
++ static unsigned char do_check_bf = 5;
++ gpio_set_value(SIMONE_LCD_EN, 0); /* Disable */
++
++ gpio_direction_input (SIMONE_LCD_DATA0);
++ gpio_direction_input (SIMONE_LCD_DATA1);
++ gpio_direction_input (SIMONE_LCD_DATA2);
++ gpio_direction_input (SIMONE_LCD_DATA3);
++
++ gpio_set_value(SIMONE_LCD_RD, 1); /* Read */
++ gpio_set_value(SIMONE_LCD_RS, 0); /* Instru */
++
++ ndelay(T_AS); /* Address set-up time */
++ gpio_set_value(SIMONE_LCD_EN, 1); /* Enable */
++ ndelay(T_EH);/* Enable high time */
++
++ do{
++ udelay(T_BF);
++ } while (gpio_get_value(SIMONE_LCD_DATA3) && --timeout);
++
++ if (! timeout) {
++ if (! --do_check_bf) {
++ printk(KERN_NOTICE "hd44780 error: is the LCD connected?\n");
++ }
++ }
++
++ gpio_set_value(SIMONE_LCD_EN, 0); /* Disable */
++
++ ndelay(T_EL); /* Enable low time */
++}
++
++/* Send commands to the display */
++static void write_command(unsigned char command)
++{
++ write_display(command, ACCESS_TO_WRITE);
++
++ if (command <= 0x03)
++ mdelay(2);
++}
++
++static inline void set_cursor(unsigned int offset)
++{
++ unsigned int disp_number = offset/disp_size;
++ unsigned int local_offset = offset%disp_size;
++
++ if (disp_offset[disp_number] != local_offset || test_bit(CGRAM_STATE+disp_number, &hd44780_flags)) {
++ unsigned int disp_row = local_offset/par.cntr_cols;
++ unsigned int disp_column = local_offset%par.cntr_cols;
++
++ write_command(DDRAM_IO | ((disp_row%2)*0x40) | (((disp_row >= 2)*par.cntr_cols)+disp_column));
++ clear_bit(CGRAM_STATE+disp_number, &hd44780_flags);
++ disp_offset[disp_number] = local_offset;
++ }
++}
++
++/* HD44780 DDRAM addresses are consecutive only when
++ * the cursor moves on the same row of the display.
++ * Every time the row of the cursor changes we invalidate
++ * the cursor position to force hardware cursor repositioning.
++ */
++static inline void mov_cursor(unsigned int disp_number)
++{
++ if (test_bit(INC_ADDR, &hd44780_flags)) {
++ iterator_inc(disp_offset[disp_number], disp_size);
++ if (disp_offset[disp_number]%par.cntr_cols == 0)
++ disp_offset[disp_number] = disp_size;
++ } else {
++ iterator_dec(disp_offset[disp_number], disp_size);
++ if (disp_offset[disp_number]%par.cntr_cols == par.cntr_cols-1)
++ disp_offset[disp_number] = disp_size;
++ }
++}
++
++static struct lcd_driver hd44780 = {
++ .read_char = hd44780_read_char,
++ .read_cgram_char = hd44780_read_cgram_char,
++ .write_char = hd44780_write_char,
++ .write_cgram_char = hd44780_write_cgram_char,
++ .address_mode = hd44780_address_mode,
++ .clear_display = hd44780_clear_display,
++ .validate_driver = hd44780_validate_driver,
++ .init_display = hd44780_init_display,
++ .cleanup_display = hd44780_cleanup_display,
++ .init_port = hd44780_init_port,
++ .cleanup_port = hd44780_cleanup_port,
++ .handle_custom_char = hd44780_handle_custom_char,
++ .handle_custom_ioctl = hd44780_handle_custom_ioctl,
++
++ .charmap = charmap,
++};
++
++static void hd44780_read_char(unsigned int offset, unsigned short *data)
++{
++ unsigned int disp_number = offset/disp_size;
++ unsigned char tmp;
++
++ set_cursor(offset);
++ read_display(&tmp, ACCESS_TO_DATA);
++ *data = tmp;
++ mov_cursor(disp_number);
++}
++
++static void hd44780_read_cgram_char(unsigned char index, unsigned char *pixels)
++{
++ unsigned int i;
++
++ write_command(CGRAM_IO | (index << 3));
++ set_bit(CGRAM_STATE+0, &hd44780_flags);
++
++ for (i = 0; i < 8; ++i) {
++ read_display(pixels+i, ACCESS_TO_DATA );
++ pixels[i] &= 0x1f;
++ }
++}
++
++static void hd44780_write_char(unsigned int offset, unsigned short data)
++{
++ unsigned int disp_number = offset/disp_size;
++
++ set_cursor(offset);
++ write_display(data & 0xff, ACCESS_TO_WRITE | ACCESS_TO_DATA );
++ mov_cursor(disp_number);
++}
++
++static void hd44780_write_cgram_char(unsigned char index, unsigned char *pixels)
++{
++ unsigned int i;
++
++ /* Move address pointer to index in CGRAM */
++ write_command(CGRAM_IO | (index << 3));
++
++ set_bit(CGRAM_STATE+0, &hd44780_flags);
++
++ for (i = 0; i < 8; ++i) {
++ pixels[i] &= 0x1f;
++ write_display(pixels[i], ACCESS_TO_WRITE | ACCESS_TO_DATA );
++ }
++}
++
++/* Increment/decrement address mode after a data read/write */
++static void hd44780_address_mode(int mode)
++{
++ if (mode > 0 && ! test_bit(INC_ADDR, &hd44780_flags)) {
++ write_command(CURS_INC | DISP_SHIFT_OFF);
++ set_bit(INC_ADDR, &hd44780_flags);
++ } else if (mode < 0 && test_bit(INC_ADDR, &hd44780_flags)) {
++ write_command(CURS_DEC | DISP_SHIFT_OFF);
++ clear_bit(INC_ADDR, &hd44780_flags);
++ }
++}
++
++static void hd44780_clear_display(void)
++{
++ write_command(CLR_DISP);
++ if (! test_bit(INC_ADDR, &hd44780_flags))
++ write_command(CURS_DEC | DISP_SHIFT_OFF);
++ memset(disp_offset, 0, sizeof(disp_offset));
++}
++
++static int hd44780_validate_driver(void)
++{
++ if (par.cntr_rows != 1 && par.cntr_rows != 2 && par.cntr_rows != 4)
++ par.cntr_rows = DFLT_CNTR_ROWS;
++
++ if (par.cntr_rows != 1)
++ par.flags &= ~HD44780_5X10_FONT;
++
++
++ if (! par.cntr_cols || par.cntr_cols > MAX_CNTR_COLS/par.cntr_rows)
++ par.cntr_cols = MAX_CNTR_COLS/par.cntr_rows;
++
++ disp_size = par.cntr_rows*par.cntr_cols;
++
++ /* These parameters depend on the hardware and cannot be changed */
++ par.cgram_chars = 8;
++ par.cgram_bytes = 8;
++ par.cgram_char0 = 0;
++
++ return (0);
++}
++
++/* Send init commands to the display */
++static void write_init_command(void)
++{
++ unsigned char command = 0x30;
++
++ __write_display(HIGH_NIBBLE_WRITE(command), ACCESS_TO_WRITE);
++ mdelay(20); /* Wait more than 4.1 ms */
++ __write_display(HIGH_NIBBLE_WRITE(command), ACCESS_TO_WRITE);
++ udelay(200); /* Wait more than 100 us */
++ __write_display(HIGH_NIBBLE_WRITE(command), ACCESS_TO_WRITE);
++ udelay(200); /* Wait more than 100 us */
++ command = BUS_4_BITS;
++ command |= ((par.cntr_rows == 1) ? DISP_1_LINE : DISP_2_LINES);
++ command |= (test_bit(_5X10_FONT, &hd44780_flags) ? FONT_5X10 : FONT_5X8);
++ write_command(command);
++ /* set_bit(BACKLIGHT, &hd44780_flags); */
++}
++
++static int hd44780_init_display(void)
++{
++ if (par.flags & HD44780_CHECK_BF)
++ set_bit(_CHECK_BF, &hd44780_flags);
++ else
++ clear_bit(_CHECK_BF, &hd44780_flags);
++
++ if (par.flags & HD44780_4BITS_BUS)
++ set_bit(_4BITS_BUS, &hd44780_flags);
++ else
++ clear_bit(_4BITS_BUS, &hd44780_flags);
++
++ if (par.flags & HD44780_5X10_FONT)
++ set_bit(_5X10_FONT, &hd44780_flags);
++ else
++ clear_bit(_5X10_FONT, &hd44780_flags);
++
++ write_init_command();
++
++ hd44780_clear_display();
++ hd44780_address_mode(1);
++ write_command(DISP_ON | CURS_OFF | BLINK_OFF);
++ set_bit(DISPLAY_ON, &hd44780_flags);
++ clear_bit(SHOW_CURSOR, &hd44780_flags);
++ clear_bit(CURSOR_BLINK, &hd44780_flags);
++
++ /* Set the CGRAM to default values */
++ hd44780_write_cgram_char(0, cg0);
++ hd44780_write_cgram_char(1, cg1);
++ hd44780_write_cgram_char(2, cg2);
++ hd44780_write_cgram_char(3, cg3);
++ hd44780_write_cgram_char(4, cg4);
++ hd44780_write_cgram_char(5, cg5);
++ hd44780_write_cgram_char(6, cg6);
++ hd44780_write_cgram_char(7, cg7);
++ init_charmap();
++
++ return (0);
++}
++
++static int hd44780_cleanup_display(void)
++{
++ hd44780_clear_display();
++
++ return (0);
++}
++
++static int hd44780_init_port(void)
++{
++ gpio_direction_output (SIMONE_LCD_BCKLIGHT, 0);
++ gpio_direction_output (SIMONE_LCD_RD , 0);
++ gpio_direction_output (SIMONE_LCD_RS , 0);
++ gpio_direction_output (SIMONE_LCD_EN , 0);
++ gpio_set_value(SIMONE_LCD_EN, 0); /* Disable */
++
++ return (0);
++}
++
++static int hd44780_cleanup_port(void)
++{
++
++ gpio_direction_input (SIMONE_LCD_BCKLIGHT);
++ gpio_direction_input (SIMONE_LCD_RD);
++ gpio_direction_input (SIMONE_LCD_RS);
++ gpio_direction_input (SIMONE_LCD_EN);
++ gpio_direction_input (SIMONE_LCD_DATA0);
++ gpio_direction_input (SIMONE_LCD_DATA1);
++ gpio_direction_input (SIMONE_LCD_DATA2);
++ gpio_direction_input (SIMONE_LCD_DATA3);
++ return (0);
++}
++
++static void display_attr(unsigned char input)
++{
++ unsigned char command;
++
++ switch (ESC_STATE) {
++ case 'a': /* Turn on/off the display cursor */
++ if (input == '1')
++ set_bit(SHOW_CURSOR, &hd44780_flags);
++ else if (input == '0')
++ clear_bit(SHOW_CURSOR, &hd44780_flags);
++ break;
++ case 'b': /* Turn on/off the display cursor blinking */
++ if (input == '1')
++ set_bit(CURSOR_BLINK, &hd44780_flags);
++ else if (input == '0')
++ clear_bit(CURSOR_BLINK, &hd44780_flags);
++ break;
++ case 'h': /* Turn on/off the display */
++ if (input == '1')
++ set_bit(DISPLAY_ON, &hd44780_flags);
++ else if (input == '0')
++ clear_bit(DISPLAY_ON, &hd44780_flags);
++ break;
++ }
++
++ command = (test_bit(DISPLAY_ON, &hd44780_flags) ? DISP_ON : DISP_OFF);
++ command |= (test_bit(SHOW_CURSOR, &hd44780_flags) ? CURS_ON : CURS_OFF);
++ command |= (test_bit(CURSOR_BLINK, &hd44780_flags) ? BLINK_ON : BLINK_OFF);
++
++ if (ESC_STATE == 'h')
++ write_command(command);
++}
++
++static int hd44780_handle_custom_char(unsigned int _input)
++{
++ unsigned char input = _input & 0xff;
++
++ if (_input & (~0xff)) {
++ switch (ESC_STATE) {
++ case 'a': /* Turn on/off the display cursor */
++ case 'b': /* Turn on/off the display cursor blinking */
++ case 'h': /* Turn on/off the the display */
++ display_attr(input);
++ return (0);
++ case 'l': /* Turn on/off the backlight */
++ if (input == '1')
++ set_bit(BACKLIGHT, &hd44780_flags);
++ else if (input == '0')
++ clear_bit(BACKLIGHT, &hd44780_flags);
++ read_ac(ACCESS_TO_READ);
++ return (0);
++ }
++ }
++
++ switch (input) {
++ case 'a': /* Turn on/off the display cursor */
++ case 'b': /* Turn on/off the display cursor blinking */
++ case 'h': /* Turn on/off the display */
++ case 'l': /* Turn on/off the backlight */
++ SET_ESC_STATE(input);
++ return (1);
++ case 'd': /* Shift display cursor Right */
++ write_command(SHIFT_CURS | SHIFT_RIGHT);
++ return (0);
++ case 'e': /* Shift display cursor Left */
++ write_command(SHIFT_CURS | SHIFT_LEFT);
++ return (0);
++ case 'f': /* Shift display Right */
++ write_command(SHIFT_DISP | SHIFT_RIGHT);
++ return (0);
++ case 'g': /* Shift display Left */
++ write_command(SHIFT_DISP | SHIFT_LEFT);
++ return (0);
++ }
++
++ return (-1);
++}
++
++static int hd44780_handle_custom_ioctl(unsigned int num, unsigned long arg, unsigned int user_space)
++{
++ unsigned char *buffer = (unsigned char *)arg;
++
++ if (num != HD44780_READ_AC)
++ return (-ENOIOCTLCMD);
++
++ if (user_space)
++ put_user(read_ac(ACCESS_TO_READ), buffer);
++ else
++ buffer[0] = read_ac(ACCESS_TO_READ);
++
++ return (0);
++}
++
++#ifdef USE_PROC
++static int hd44780_proc_status(char *buffer, char **start, off_t offset, int size, int *eof, void *data)
++{
++ char *temp = buffer;
++
++ /* Print display configuration */
++ temp += sprintf(temp,
++ "Interface:\t%u bits\n"
++ "Display rows:\t%d\n"
++ "Display cols:\t%d\n"
++ "Screen rows:\t%d\n"
++ "Screen cols:\t%d\n"
++ "Read:\t\t%sabled\n"
++ "Busy flag chk:\t%sabled\n"
++ "Assigned minor:\t%u\n",
++ (test_bit(_4BITS_BUS, &hd44780_flags) ? 4 : 8),
++ par.cntr_rows, par.cntr_cols,
++ par.vs_rows, par.vs_cols,
++ (hd44780.read_char ? "En" : "Dis"),
++ (test_bit(_CHECK_BF, &hd44780_flags) ? "En" : "Dis"),
++ par.minor);
++
++ return (temp-buffer);
++}
++
++static int hd44780_proc_cgram(char *buffer, char **start, off_t offset, int size, int *eof, void *data)
++{
++ char *temp = buffer;
++ unsigned int i;
++
++ temp += sprintf(temp, "static void init_charmap(void)\n{\n"
++ "\t/*\n"
++ "\t * charmap[char mapped to cg0] = 0;\n"
++ "\t * charmap[char mapped to cg1] = 1;\n"
++ "\t * charmap[char mapped to cg2] = 2;\n"
++ "\t * charmap[char mapped to cg3] = 3;\n"
++ "\t * charmap[char mapped to cg4] = 4;\n"
++ "\t * charmap[char mapped to cg5] = 5;\n"
++ "\t * charmap[char mapped to cg6] = 6;\n"
++ "\t * charmap[char mapped to cg7] = 7;\n"
++ "\t */\n"
++ "}\n\n");
++
++ for (i = 0; i < 8; ++i) {
++ unsigned char cgram_buffer[8];
++ unsigned int j;
++
++ temp += sprintf(temp, "static unsigned char cg%u[] = { ", i);
++ hd44780_read_cgram_char(i, cgram_buffer);
++ for (j = 0; j < 8; ++j)
++ temp += sprintf(temp, "0x%.2x%s", cgram_buffer[j], (j == 7 ? " };\n" : ", "));
++ }
++
++ return (temp-buffer);
++}
++
++static void create_proc_entries(void)
++{
++ int count = 0;
++
++ SET_PROC_LEVEL(count);
++ if (create_proc_read_entry("status", 0, hd44780.driver_proc_root, hd44780_proc_status, NULL) == NULL) {
++ printk(KERN_ERR "hd44780: cannot create /proc/lcd/%s/status\n", par.name);
++ return;
++ }
++ SET_PROC_LEVEL(++count);
++ if (hd44780.read_cgram_char) {
++ if (create_proc_read_entry("cgram.h", 0, hd44780.driver_proc_root, hd44780_proc_cgram, NULL) == NULL) {
++ printk(KERN_ERR "hd44780: cannot create /proc/lcd/%s/cgram.h\n", par.name);
++ return;
++ }
++ SET_PROC_LEVEL(++count);
++ }
++}
++
++static void remove_proc_entries(void)
++{
++ switch (PROC_LEVEL) {
++ case 2:
++ remove_proc_entry("cgram.h", hd44780.driver_proc_root);
++ case 1:
++ remove_proc_entry("status", hd44780.driver_proc_root);
++ }
++ SET_PROC_LEVEL(0);
++}
++#endif
++
++/* Initialization */
++static int __init hd44780_init_module(void)
++{
++ int ret;
++
++#ifdef MODULE
++#ifdef NOT_DEF
++ if ((ret = request_module("lcd-linux"))) {
++ if (ret != -ENOSYS) {
++ printk(KERN_ERR "hd44780: failure while loading module lcd-linux\n");
++ return (ret);
++ }
++ printk(KERN_ERR "hd44780: your kernel does not have kmod or kerneld support;\n");
++ printk(KERN_ERR "hd44780: remember to load the lcd-linux module before\n");
++ printk(KERN_ERR "hd44780: loading the hd44780 module\n");
++ }
++#endif
++ if (flags != DFLT_FLAGS) par.flags = flags;
++ if (tabstop != DFLT_TABSTOP) par.tabstop = tabstop;
++ if (cntr_rows != DFLT_CNTR_ROWS) par.cntr_rows = cntr_rows;
++ if (cntr_cols != DFLT_CNTR_COLS) par.cntr_cols = cntr_cols;
++ if (vs_rows != DFLT_VS_ROWS) par.vs_rows = vs_rows;
++ if (vs_cols != DFLT_VS_COLS) par.vs_cols = vs_cols;
++ if (minor != HD44780_MINOR) par.minor = minor;
++#endif
++
++ lcd_driver_setup(&hd44780);
++ if ((ret = lcd_register_driver(&hd44780, &par)))
++ return (ret);
++
++#ifdef USE_PROC
++ if (hd44780.driver_proc_root)
++ create_proc_entries();
++#endif
++
++ printk(KERN_INFO "HD44780 driver (LCD-Linux" HD44780_VERSION ")\n");
++ printk(KERN_INFO "Nuccio Raciti <raciti.nuccio@gmail.com>\n");
++
++
++ return (0);
++}
++
++/* Cleanup */
++
++static void __exit hd44780_cleanup_module(void)
++{
++#ifdef USE_PROC
++ if (hd44780.driver_proc_root)
++ remove_proc_entries();
++#endif
++
++ lcd_unregister_driver(&hd44780, &par);
++}
++
++module_init(hd44780_init_module)
++module_exit(hd44780_cleanup_module)
+Index: linux-2.6.30.9/drivers/lcd-linux/lcd-linux.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.30.9/drivers/lcd-linux/lcd-linux.c 2009-11-24 02:01:42.000000000 +0100
+@@ -0,0 +1,2892 @@
++/* lcd-linux.c
++ *
++ *
++ *
++ * Software layer to drive LCD displays under Linux.
++ *
++ * Copyright (C) 2005 - 2007 Mattia Jona-Lasinio (mjona@users.sourceforge.net)
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ *
++ */
++
++#include <linux/version.h>
++
++#ifndef KERNEL_VERSION
++#define KERNEL_VERSION(a, b, c) (((a) << 16) + ((b) << 8) + (c))
++#endif
++
++#ifndef LINUX_VERSION_CODE
++#error - LINUX_VERSION_CODE undefined in 'linux/version.h'
++#endif
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
++#include <linux/autoconf.h>
++#else
++#include <linux/config.h>
++#endif
++#ifdef CONFIG_PROC_FS
++#define USE_PROC
++#else
++#undef USE_PROC
++#endif
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26)
++#include <linux/semaphore.h>
++#else
++#include <asm/semaphore.h>
++#endif
++#include <linux/bitops.h>
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/sched.h>
++
++#include <linux/fs.h>
++
++#include <asm/uaccess.h>
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
++#include <linux/device.h>
++#endif
++#include <linux/init.h>
++#include <linux/list.h>
++#include <linux/slab.h>
++#include <linux/selection.h>
++#include <linux/vmalloc.h>
++
++#ifdef USE_PROC
++#include <linux/proc_fs.h>
++#endif
++
++#define LCD_LINUX_MAIN
++#include <linux/lcd-linux.h>
++
++/*** struct_flags ***/
++#define NEED_WRAP 0 /* Next char will trigger a newline */
++#define DECIM 1 /* Insert mode */
++#define DECAWM 2 /* Autowrap */
++#define DECSCNM 3 /* Inverted screen */
++#define CRLF 4 /* Follow lf, vt, ff, with a cr */
++#define INC_CURS_POS 5 /* Increment cursor position after data read/write */
++#define QUES 6 /* CSI Esc sequence contains a question mark */
++#define USER_SPACE 7 /* If set, the buffer pointed by arg in do_lcd_ioctl() is
++ * assumed to be in user space otherwise it is in kernel space */
++#define NULL_CHARMAP 8 /* The driver doesn't provide a charmap so the
++ * lcd-linux layer provides one*/
++#define CAN_DO_COLOR 9 /* The display is color capable */
++#define WITH_ATTR 10 /* If set, the void * buffer in do_lcd_read/write() contains
++ * attributes and therefore is an unsigned short * otherwise it
++ * is an unsigned char *
++ */
++
++/*** attributes ***/
++#define I_MASK 0x03 /* Intensity (0 = low, 1 = normal, 2 = bright) */
++#define ULINE 0x04 /* Underlined text */
++#define REVERSE 0x08 /* Reversed video text */
++#define BLINK 0x80 /* Blinking text */
++
++/*** Color attributes ***/
++#define FG_MASK 0x0f /* Foreground color */
++#define BG_MASK 0xf0 /* Background color */
++
++/* input states */
++#define NORMAL 0x00001000 /* Normal mode */
++#define RAW 0x00002000 /* Raw mode (console emulation disabled) */
++#define SYN 0x00003000 /* Synchronous Idle mode */
++#define ESC 0x00004000 /* Escape mode */
++#define CSI 0x00005000 /* CSI escape mode */
++#define ESC_G0 0x00006000 /* G0 character set */
++#define ESC_G1 0x00007000 /* G1 character set */
++#define ESC_HASH 0x00008000 /* ESC # escape sequence */
++#define ESC_PERCENT 0x00009000 /* ESC % escape sequence */
++#define ARG 0x0000a000 /* Waiting for arguments for the lcd-linux layer */
++#define ARG_DRIVER 0x0000b000 /* Waiting for arguments for the display driver */
++#define INPUT_MASK 0x0000f000
++#define ESC_MASK 0x00ff0000
++#define INIT_MASK 0x0f000000
++#define PROC_MASK 0xf0000000
++
++#define SET_STATE(p, state, mask) ((p)->struct_flags = ((p)->struct_flags & ~(mask)) | ((state) & (mask)))
++#define SET_INPUT_STATE(p, state) SET_STATE(p, state, INPUT_MASK)
++#define SET_ESC_STATE(p, state) SET_STATE(p, (state) << 16, ESC_MASK)
++#define SET_INIT_LEVEL(p, level) SET_STATE(p, (level) << 24, INIT_MASK)
++#define SET_PROC_LEVEL(p, level) SET_STATE(p, (level) << 28, PROC_MASK)
++#define INPUT_STATE(p) ((p)->struct_flags & INPUT_MASK)
++#define ESC_STATE(p) (((p)->struct_flags & ESC_MASK) >> 16)
++#define INIT_LEVEL(p) (((p)->struct_flags & INIT_MASK) >> 24)
++#define PROC_LEVEL(p) (((p)->struct_flags & PROC_MASK) >> 28)
++
++#define NPAR 16 /* Max number of parameters in CSI escape sequence */
++#define FLIP_BUF_SIZE (1 << 6) /* Flip buffer size (64 bytes) */
++
++struct lcd_struct {
++ struct list_head lcd_list; /* Doubly linked list */
++ struct semaphore lcd_sem; /* Locks this structure */
++ struct lcd_driver *driver; /* The driver associated to this struct */
++ struct lcd_parameters *par; /* The parameters associated to this struct */
++ unsigned long struct_flags; /* Flags for internal use only */
++ unsigned int refcount; /* Number of references to this struct */
++
++ unsigned short *display; /* The display buffer */
++
++ unsigned short *fb; /* The virtual screen framebuffer */
++ unsigned int fb_size; /* Size of the framebuffer */
++ unsigned int frame_base; /* Offset of row 0, column 0 of a frame in fb */
++ unsigned int frame_size; /* Size of the frame */
++
++ unsigned int row; /* Current row in virtual screen */
++ unsigned int col; /* Current column in virtual screen */
++ unsigned int s_offset; /* Saved cursor position in virtual screen */
++
++ unsigned int top; /* Top scroll row in virtual screen */
++ unsigned int bot; /* Bottom scroll row in virtual screen */
++
++ int esc_args; /* Number of arguments for a normal escape sequence */
++ unsigned int csi_args[NPAR]; /* CSI parameters */
++ unsigned int index; /* Index in csi_args and counter for cgram characters generation */
++ unsigned char cgram_index; /* Index of the cgram character to be created */
++ unsigned char *cgram_buffer; /* Buffer for cgram operations in this driver */
++
++ unsigned short erase_char; /* Character to be used when erasing */
++ unsigned char attr; /* Current attributes */
++ unsigned char color; /* Color for normal intensity mode */
++ unsigned char s_color; /* Saved color for normal intensity mode */
++ unsigned char defcolor; /* Default color for normal intensity mode */
++ unsigned char ulcolor; /* Color for underline mode */
++ unsigned char halfcolor; /* Color for low intensity mode */
++ unsigned char attributes; /* Packed attributes */
++ unsigned char s_attributes; /* Saved packed attributes */
++
++ unsigned char *s_charmap; /* Saved character map for this driver */
++ unsigned char *flip_buf; /* High speed flip buffer */
++};
++
++/** Function prototypes **/
++
++/* Init/Cleanup the driver */
++static int init_driver(struct lcd_struct *);
++static int cleanup_driver(struct lcd_struct *);
++
++/* Read from/Write to the driver */
++static void read_data(struct lcd_struct *, unsigned short *);
++static void read_cgram(struct lcd_struct *, unsigned char, unsigned char *);
++static void write_data(struct lcd_struct *, unsigned short);
++static void write_cgram(struct lcd_struct *, unsigned char, unsigned char *);
++
++/* Input handlers */
++static void cr(struct lcd_struct *);
++static void lf(struct lcd_struct *);
++static void control_char(struct lcd_struct *, unsigned char);
++static void handle_csi(struct lcd_struct *, unsigned char);
++static int handle_custom_esc(struct lcd_struct *, unsigned int);
++static int handle_esc(struct lcd_struct *, unsigned char);
++static void handle_input(struct lcd_struct *, unsigned short);
++
++/* Low level file operations */
++static ssize_t do_lcd_read(struct lcd_struct *, void *, size_t);
++static ssize_t do_lcd_write(struct lcd_struct *, const void *, size_t);
++static int do_lcd_open(struct lcd_struct *);
++static int do_lcd_release(struct lcd_struct *);
++static int do_lcd_ioctl(struct lcd_struct *, unsigned int, unsigned long);
++
++/* Proc functions */
++#ifdef USE_PROC
++static void create_driver_proc_entries(struct lcd_struct *);
++static void remove_driver_proc_entries(struct lcd_struct *);
++#endif
++
++/* globals */
++static unsigned int major = LCD_MAJOR; /* Major number for LCD-Linux device */
++static unsigned short minors = LCD_MINORS; /* Minor numbers allocated for LCD-Linux */
++static LIST_HEAD(lcd_drivers); /* Registered lcd drivers */
++static struct semaphore drivers_sem; /* Locks the lcd_drivers list */
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 13)
++static struct class *lcd_linux_class;
++#endif
++#ifdef USE_PROC
++static struct proc_dir_entry *lcd_proc_root;
++#endif
++/* End of globals */
++
++#ifdef MODULE
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
++#include <linux/device.h>
++MODULE_ALIAS_CHARDEV_MAJOR(LCD_MAJOR);
++#endif
++MODULE_DESCRIPTION("Software layer to drive LCD displays under Linux.");
++MODULE_AUTHOR("Mattia Jona-Lasinio <mjona@users.sourceforge.net>");
++#ifdef MODULE_LICENSE
++MODULE_LICENSE("GPL");
++#endif
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
++module_param(minors, ushort, 0444);
++#else
++MODULE_PARM(minors, "h");
++#endif
++MODULE_PARM_DESC(minors, "Minor numbers allocated for LCD-Linux (default: " string(LCD_MINORS) ")");
++#else
++
++/*
++ * Parse boot command line
++ *
++ * lcd=minors
++ */
++static int __init lcd_linux_boot_init(char *cmdline)
++{
++ unsigned short args;
++
++ if ((args = simple_strtoul(cmdline, NULL, 0)))
++ minors = args;
++
++ return (1);
++}
++
++__setup("lcd=", lcd_linux_boot_init);
++#endif /* MODULE */
++
++/* Macros for iterator handling */
++static inline unsigned int iterator_inc_(unsigned int iterator, const unsigned int module)
++{
++ return ((++iterator)%module);
++}
++
++static inline unsigned int iterator_dec_(unsigned int iterator, const unsigned int module)
++{
++ return (iterator ? --iterator : module-1);
++}
++
++#define iterator_inc(iterator, module) (iterator = iterator_inc_(iterator, module))
++#define iterator_dec(iterator, module) (iterator = iterator_dec_(iterator, module))
++
++/* Uncomment the following two lines
++ * for non-atomic set_bit and clear_bit
++#define set_bit __set_bit
++#define clear_bit __clear_bit
++*/
++
++/************************************
++ * Low level routines and utilities *
++ ************************************/
++/*
++ * Set whether the address counter should be incremented
++ * or decremented after a Read/Write
++ */
++static void address_mode(struct lcd_struct *p, int mode)
++{
++ struct lcd_driver *driver = p->driver;
++
++ if (mode > 0 && ! test_bit(INC_CURS_POS, &p->struct_flags)) {
++ if (driver->address_mode)
++ driver->address_mode(mode);
++ set_bit(INC_CURS_POS, &p->struct_flags);
++ } else if (mode < 0 && test_bit(INC_CURS_POS, &p->struct_flags)) {
++ if (driver->address_mode)
++ driver->address_mode(mode);
++ clear_bit(INC_CURS_POS, &p->struct_flags);
++ }
++}
++
++/* WARNING!! This function returns an int because if iterator is not
++ * within the visible area of the frame it returns -1
++ */
++static inline int vs_to_frame_(struct lcd_struct *p, unsigned int iterator)
++{
++ unsigned int vs_cols = p->par->vs_cols;
++ unsigned int row = iterator/vs_cols;
++ unsigned int col = iterator%vs_cols;
++ unsigned int frame_base_row = p->frame_base/vs_cols;
++ unsigned int frame_base_col = p->frame_base%vs_cols;
++ unsigned int frame_rows = p->par->cntr_rows*p->par->num_cntr;
++ unsigned int frame_cols = p->par->cntr_cols;
++
++ if (row < frame_base_row || row >= frame_base_row+frame_rows)
++ return (-1);
++ if (col < frame_base_col || col >= frame_base_col+frame_cols)
++ return (-1);
++
++ return ((row-frame_base_row)*frame_cols+(col-frame_base_col));
++}
++
++/* Given 'iterator' in vs, returns the offset in vs corresponding to the nearest
++ * visible offset in vs, or returns 'iterator' if it is already visible.
++ */
++static unsigned int round_vs_(struct lcd_struct *p, unsigned int iterator)
++{
++ unsigned int vs_cols = p->par->vs_cols;
++ unsigned int row = iterator/vs_cols;
++ unsigned int col = iterator%vs_cols;
++ unsigned int frame_base_row = p->frame_base/vs_cols;
++ unsigned int frame_base_col = p->frame_base%vs_cols;
++ unsigned int frame_rows = p->par->cntr_rows*p->par->num_cntr;
++ unsigned int frame_cols = p->par->cntr_cols;
++
++ if (row < frame_base_row)
++ row = frame_base_row;
++ else if (row >= frame_base_row+frame_rows)
++ row = frame_base_row+(frame_rows-1);
++
++ if (col < frame_base_col)
++ col = frame_base_col;
++ else if (col >= frame_base_col+frame_cols)
++ col = frame_base_col+(frame_cols-1);
++
++ return ((row*vs_cols)+col);
++}
++
++#define round_vs(p, iterator) (iterator = round_vs_(p, iterator))
++
++/*
++ * Sync the frame area starting at offset s, ending at offset e with fb content.
++ */
++static void redraw_screen(struct lcd_struct *p, unsigned int s, unsigned int e)
++{
++ unsigned int len;
++ unsigned int row = p->row, col = p->col;
++ unsigned int inc_set = test_bit(INC_CURS_POS, &p->struct_flags);
++ unsigned int frame_cols = p->par->cntr_cols;
++ unsigned int vs_cols = p->par->vs_cols;
++ unsigned long flags;
++
++ if (s >= p->fb_size || e >= p->fb_size || e < s || e < p->frame_base)
++ return;
++
++ round_vs(p, s);
++ round_vs(p, e);
++
++ len = 1+e-s;
++
++ if (! inc_set)
++ s = e;
++
++ p->row = s/vs_cols;
++ p->col = s%vs_cols;
++
++ flags = p->struct_flags;
++ clear_bit(NEED_WRAP, &p->struct_flags);
++ clear_bit(DECIM, &p->struct_flags);
++ set_bit(DECAWM, &p->struct_flags);
++ SET_INPUT_STATE(p, RAW);
++ if (inc_set)
++ while (len--)
++ if (vs_to_frame_(p, (p->row*vs_cols)+p->col) < 0) {
++ s += vs_cols-frame_cols;
++ len -= vs_cols-frame_cols-1;
++ p->row = s/vs_cols;
++ p->col = s%vs_cols;
++ } else {
++ write_data(p, p->fb[s++]);
++ if (test_bit(NEED_WRAP, &p->struct_flags)) {
++ cr(p);
++ lf(p);
++ }
++ }
++ else
++ while (len--)
++ if (vs_to_frame_(p, (p->row*vs_cols)+p->col) < 0) {
++ s -= vs_cols-frame_cols;
++ len -= vs_cols-frame_cols-1;
++ p->row = s/vs_cols;
++ p->col = s%vs_cols;
++ } else {
++ write_data(p, p->fb[s--]);
++ if (test_bit(NEED_WRAP, &p->struct_flags)) {
++ cr(p);
++ lf(p);
++ }
++ }
++ p->struct_flags = flags;
++
++ p->row = row; p->col = col;
++}
++
++static int make_cursor_visible(struct lcd_struct *p)
++{
++ unsigned int vs_rows = p->par->vs_rows;
++ unsigned int vs_cols = p->par->vs_cols;
++ unsigned int frame_base, frame_base_row, frame_base_col;
++ unsigned int frame_rows = p->par->cntr_rows*p->par->num_cntr;
++ unsigned int frame_cols = p->par->cntr_cols;
++ unsigned int tmp = frame_cols/2;
++
++ if (test_bit(INC_CURS_POS, &p->struct_flags)) {
++ /* cursor always on the lowest row of the display */
++ frame_base_row = 0;
++ frame_base_col = 0;
++ if (p->row >= frame_rows)
++ frame_base_row = p->row-(frame_rows-1);
++ if (p->col >= frame_cols) {
++ frame_base_col = p->col-(frame_cols-1);
++ if (tmp) {
++ tmp = (tmp-(frame_base_col%tmp))%tmp;
++ if (frame_base_col+tmp <= vs_cols-frame_cols)
++ frame_base_col += tmp;
++ }
++ }
++ } else {
++ /* cursor always on the uppermost row of the display */
++ frame_base_row = vs_rows-frame_rows;
++ frame_base_col = vs_cols-frame_cols;
++ if (p->row < vs_rows-frame_rows)
++ frame_base_row = p->row;
++ if (p->col < vs_cols-frame_cols) {
++ frame_base_col = p->col;
++ if (tmp) {
++ tmp = frame_base_col%tmp;
++ if (frame_base_col >= tmp)
++ frame_base_col -= tmp;
++ }
++ }
++ }
++
++ frame_base = p->frame_base;
++ p->frame_base = (frame_base_row*vs_cols)+frame_base_col;
++
++ return (frame_base != p->frame_base);
++}
++
++/*
++ * Move the visible screen area at user's wish
++ */
++static void browse_screen(struct lcd_struct *p, unsigned char dir)
++{
++ unsigned int vs_rows = p->par->vs_rows;
++ unsigned int vs_cols = p->par->vs_cols;
++ unsigned int frame_base_row = p->frame_base/vs_cols;
++ unsigned int frame_base_col = p->frame_base%vs_cols;
++ unsigned int frame_rows = p->par->cntr_rows*p->par->num_cntr;
++ unsigned int frame_cols = p->par->cntr_cols;
++
++ switch (dir) {
++ case '1': /* Up */
++ if (! frame_base_row)
++ return;
++ --frame_base_row;
++ break;
++ case '2': /* Down */
++ if (frame_base_row >= vs_rows-frame_rows)
++ return;
++ ++frame_base_row;
++ break;
++ case '3': /* Left */
++ if (! frame_base_col)
++ return;
++ --frame_base_col;
++ break;
++ case '4': /* Right */
++ if (frame_base_col >= vs_cols-frame_cols)
++ return;
++ ++frame_base_col;
++ break;
++ default:
++ return;
++ }
++
++ p->frame_base = (frame_base_row*vs_cols)+frame_base_col;
++ redraw_screen(p, 0, p->fb_size-1);
++}
++
++static inline void __memset_short(unsigned short *buf, unsigned short c, unsigned int len)
++{
++ while (len--)
++ *buf++ = c;
++}
++
++/*
++ * A memset implementation writing to LCD instead of memory locations.
++ */
++static void lcd_memset(struct lcd_struct *p, unsigned int d, unsigned short c, unsigned int len)
++{
++ unsigned int inc_set = test_bit(INC_CURS_POS, &p->struct_flags);
++
++ if (! len || d >= p->fb_size)
++ return;
++
++ if (inc_set && d+len > p->fb_size)
++ len = p->fb_size-d;
++ else if (! inc_set && len > d+1)
++ len = d+1;
++
++ if (! inc_set)
++ d -= len-1;
++ __memset_short(p->fb+d, c, len);
++
++ if (make_cursor_visible(p))
++ redraw_screen(p, 0, p->fb_size-1);
++ else
++ redraw_screen(p, d, d+(len-1));
++}
++
++static inline void __memcpy_short(unsigned short *d, unsigned short *s, unsigned int len, int dir)
++{
++ if (dir > 0)
++ while (len--)
++ *d++ = *s++;
++ else
++ while (len--)
++ *d-- = *s--;
++}
++
++/*
++ * A memmove implementation writing to LCD instead of memory locations.
++ * Copy is done in a non destructive way. Display regions may overlap.
++ */
++static void lcd_memmove(struct lcd_struct *p, unsigned int d, unsigned int s, unsigned int len)
++{
++ if (! len || d == s || d >= p->fb_size || s >= p->fb_size)
++ return;
++
++ if (d < s) {
++ if (test_bit(INC_CURS_POS, &p->struct_flags)) {
++ if (s+len > p->fb_size)
++ len = p->fb_size-s;
++ } else {
++ if (len > d+1)
++ len = d+1;
++ d -= len-1;
++ s -= len-1;
++ }
++ __memcpy_short(p->fb+d, p->fb+s, len, 1);
++ if (make_cursor_visible(p))
++ redraw_screen(p, 0, p->fb_size-1);
++ else
++ redraw_screen(p, d, d+(len-1));
++ } else {
++ if (test_bit(INC_CURS_POS, &p->struct_flags)) {
++ if (d+len > p->fb_size)
++ len = p->fb_size-d;
++ d += len-1;
++ s += len-1;
++ } else {
++ if (len > s+1)
++ len = s+1;
++ }
++ __memcpy_short(p->fb+d, p->fb+s, len, -1);
++ if (make_cursor_visible(p))
++ redraw_screen(p, 0, p->fb_size-1);
++ else
++ redraw_screen(p, d-(len-1), d);
++ }
++}
++
++static void scrup(struct lcd_struct *p, unsigned int t, unsigned int b, unsigned int nr)
++{
++ unsigned int vs_rows = p->par->vs_rows;
++ unsigned int vs_cols = p->par->vs_cols;
++ unsigned int d, s;
++
++ if (t+nr >= b)
++ nr = b-t-1;
++ if (b > vs_rows || t >= b || nr < 1)
++ return;
++ d = t*vs_cols;
++ s = (t+nr)*vs_cols;
++ if (test_bit(INC_CURS_POS, &p->struct_flags)) {
++ lcd_memmove(p, d, s, (b-t-nr)*vs_cols);
++ lcd_memset(p, d+(b-t-nr)*vs_cols, p->erase_char, nr*vs_cols);
++ } else {
++ lcd_memmove(p, d+(b-t-nr)*vs_cols-1, s+(b-t-nr)*vs_cols-1, (b-t-nr)*vs_cols);
++ lcd_memset(p, d+(b-t)*vs_cols-1, p->erase_char, nr*vs_cols);
++ }
++}
++
++static void scrdown(struct lcd_struct *p, unsigned int t, unsigned int b, unsigned int nr)
++{
++ unsigned int vs_rows = p->par->vs_rows;
++ unsigned int vs_cols = p->par->vs_cols;
++ unsigned int d, s;
++
++ if (t+nr >= b)
++ nr = b-t-1;
++ if (b > vs_rows || t >= b || nr < 1)
++ return;
++ s = t*vs_cols;
++ d = (t+nr)*vs_cols;
++ if (test_bit(INC_CURS_POS, &p->struct_flags)) {
++ lcd_memmove(p, d, s, (b-t-nr)*vs_cols);
++ lcd_memset(p, s, p->erase_char, nr*vs_cols);
++ } else {
++ lcd_memmove(p, d+(b-t-nr)*vs_cols-1, s+(b-t-nr)*vs_cols-1, (b-t-nr)*vs_cols);
++ lcd_memset(p, s+nr*vs_cols-1, p->erase_char, nr*vs_cols);
++ }
++}
++
++static void lcd_insert_char(struct lcd_struct *p, unsigned int nr)
++{
++ unsigned int vs_cols = p->par->vs_cols;
++ unsigned int pos = (p->row*vs_cols)+p->col;
++
++ clear_bit(NEED_WRAP, &p->struct_flags);
++ if (test_bit(INC_CURS_POS, &p->struct_flags))
++ lcd_memmove(p, pos+nr, pos, vs_cols-p->col-nr);
++ else
++ lcd_memmove(p, pos-nr, pos, p->col-(nr-1));
++ lcd_memset(p, pos, p->erase_char, nr);
++}
++
++static void lcd_delete_char(struct lcd_struct *p, unsigned int nr)
++{
++ unsigned int vs_cols = p->par->vs_cols;
++ unsigned int pos = (p->row*vs_cols)+p->col;
++
++ clear_bit(NEED_WRAP, &p->struct_flags);
++ if (test_bit(INC_CURS_POS, &p->struct_flags)) {
++ lcd_memmove(p, pos, pos+nr, vs_cols-(p->col+nr));
++ lcd_memset(p, (p->row+1)*vs_cols-nr, p->erase_char, nr);
++ } else {
++ lcd_memmove(p, pos, pos-nr, p->col-(nr-1));
++ lcd_memset(p, (p->row*vs_cols)+(nr-1), p->erase_char, nr);
++ }
++}
++
++
++
++
++
++/******************************************************************************
++ ************************* VT 102 Emulation *************************
++ ******************************************************************************/
++
++/**********************
++ * Control characters *
++ **********************/
++static void bs(struct lcd_struct *p)
++{
++ clear_bit(NEED_WRAP, &p->struct_flags);
++ if (test_bit(INC_CURS_POS, &p->struct_flags)) {
++ if (p->col)
++ --p->col;
++ } else {
++ if (p->col+1 < p->par->vs_cols)
++ ++p->col;
++ }
++}
++
++static void cr(struct lcd_struct *p)
++{
++ clear_bit(NEED_WRAP, &p->struct_flags);
++ p->col = (test_bit(INC_CURS_POS, &p->struct_flags) ? 0 : p->par->vs_cols-1);
++}
++
++static void lf(struct lcd_struct *p)
++{
++ clear_bit(NEED_WRAP, &p->struct_flags);
++ if (test_bit(INC_CURS_POS, &p->struct_flags)) {
++ if (p->row+1 == p->bot && INPUT_STATE(p) != RAW) {
++ make_cursor_visible(p);
++ scrup(p, p->top, p->bot, 1);
++ } else if (p->row+1 < p->par->vs_rows)
++ ++p->row;
++ } else {
++ if (p->row == p->top && INPUT_STATE(p) != RAW) {
++ make_cursor_visible(p);
++ scrdown(p, p->top, p->bot, 1);
++ } else if (p->row)
++ --p->row;
++ }
++}
++
++static void ri(struct lcd_struct *p)
++{
++ clear_bit(NEED_WRAP, &p->struct_flags);
++ if (test_bit(INC_CURS_POS, &p->struct_flags)) {
++ if (p->row == p->top) {
++ make_cursor_visible(p);
++ scrdown(p, p->top, p->bot, 1);
++ } else if (p->row)
++ --p->row;
++ } else {
++ if (p->row+1 == p->bot) {
++ make_cursor_visible(p);
++ scrup(p, p->top, p->bot, 1);
++ } else if (p->row+1 < p->par->vs_rows)
++ ++p->row;
++ }
++}
++
++static void ff(struct lcd_struct *p)
++{
++ unsigned int vs_rows = p->par->vs_rows;
++ unsigned int vs_cols = p->par->vs_cols;
++
++ clear_bit(NEED_WRAP, &p->struct_flags);
++ if (p->driver->clear_display) {
++ p->driver->clear_display();
++ __memset_short(p->fb, p->erase_char, p->fb_size);
++ __memset_short(p->display, p->erase_char, p->frame_size);
++ p->frame_base = 0;
++ } else if (test_bit(INC_CURS_POS, &p->struct_flags))
++ lcd_memset(p, 0, p->erase_char, p->fb_size);
++ else
++ lcd_memset(p, p->fb_size-1, p->erase_char, p->fb_size);
++
++ if (test_bit(INC_CURS_POS, &p->struct_flags))
++ p->row = p->col = 0;
++ else {
++ p->row = vs_rows-1;
++ p->col = vs_cols-1;
++ }
++}
++
++static void tab(struct lcd_struct *p)
++{
++ struct lcd_parameters *par = p->par;
++ unsigned int i, vs_cols = par->vs_cols;
++
++ clear_bit(NEED_WRAP, &p->struct_flags);
++
++ if (! par->tabstop)
++ return;
++
++ if (test_bit(INC_CURS_POS, &p->struct_flags)) {
++ i = par->tabstop-(p->col%par->tabstop);
++ if (p->col+i < vs_cols)
++ p->col += i;
++ } else {
++ i = p->col%par->tabstop;
++ i = (i == 0 ? par->tabstop : i);
++ if (p->col >= i)
++ p->col -= i;
++ }
++}
++
++/*
++ * Control character handler.
++ */
++static void control_char(struct lcd_struct *p, unsigned char val)
++{
++ switch (val) {
++ case 0x08: /* BS: Back Space (^H) */
++ case 0x7f: /* DEL: Delete */
++ bs(p);
++ return;
++
++ case 0x09: /* HT: Horizontal Tab (^I) */
++ tab(p);
++ return;
++
++ case 0x0c: /* FF: Form Feed (^L) */
++ ff(p);
++ return;
++
++ case 0x0a: /* LF: Line Feed (^J) */
++ case 0x0b: /* VT: Vertical Tab (^K) */
++ lf(p);
++ if (! test_bit(CRLF, &p->struct_flags))
++ return;
++
++ case 0x0d: /* CR: Carriage Return (^M) */
++ cr(p);
++ return;
++
++ case 0x16: /* SYN: Synchronous Idle (^V) */
++ SET_INPUT_STATE(p, SYN);
++ return;
++
++ case 0x1b: /* ESC: Start of escape sequence */
++ SET_INPUT_STATE(p, ESC);
++ return;
++
++ case 0x9b: /* CSI: Start of CSI escape sequence */
++ memset(p->csi_args, 0, sizeof(p->csi_args));
++ p->index = 0;
++ SET_INPUT_STATE(p, CSI);
++ return;
++ }
++}
++
++static void gotoxy(struct lcd_struct *p, int new_col, int new_row)
++{
++ unsigned int vs_rows = p->par->vs_rows;
++ unsigned int vs_cols = p->par->vs_cols;
++
++ clear_bit(NEED_WRAP, &p->struct_flags);
++ if (new_row < 0)
++ p->row = 0;
++ else if (new_row >= vs_rows)
++ p->row = vs_rows-1;
++ else
++ p->row = new_row;
++
++ if (new_col < 0)
++ p->col = 0;
++ else if (new_col >= vs_cols)
++ p->col = vs_cols-1;
++ else
++ p->col = new_col;
++
++ if (make_cursor_visible(p))
++ redraw_screen(p, 0, p->fb_size-1);
++}
++
++
++/******************************
++ * ECMA-48 CSI ESC- sequences *
++ ******************************/
++static void csi_at(struct lcd_struct *p, unsigned int nr)
++{
++ unsigned int vs_cols = p->par->vs_cols;
++
++ if (p->col+nr > vs_cols)
++ nr = vs_cols-p->col;
++ else if (! nr)
++ ++nr;
++ lcd_insert_char(p, nr);
++}
++
++static void csi_J(struct lcd_struct *p, unsigned int action)
++{
++ unsigned int vs_cols = p->par->vs_cols;
++ unsigned int pos = (p->row*vs_cols)+p->col;
++
++ clear_bit(NEED_WRAP, &p->struct_flags);
++ switch (action) {
++ case 0: /* From cursor to end of display */
++ lcd_memset(p, pos, p->erase_char, p->fb_size-pos);
++ return;
++
++ case 1: /* From start of display to cursor */
++ lcd_memset(p, 0, p->erase_char, pos+1);
++ return;
++
++ case 2: /* Whole display */
++ lcd_memset(p, 0, p->erase_char, p->fb_size);
++ return;
++ }
++}
++
++static void csi_K(struct lcd_struct *p, unsigned int action)
++{
++ unsigned int vs_cols = p->par->vs_cols;
++ unsigned int row_start = p->row*vs_cols;
++
++ clear_bit(NEED_WRAP, &p->struct_flags);
++ switch (action) {
++ case 0: /* From cursor to end of line */
++ lcd_memset(p, row_start+p->col, p->erase_char, vs_cols-p->col);
++ return;
++
++ case 1: /* From start of line to cursor */
++ lcd_memset(p, row_start, p->erase_char, p->col+1);
++ return;
++
++ case 2: /* Whole line */
++ lcd_memset(p, row_start, p->erase_char, vs_cols);
++ return;
++ }
++}
++
++static void csi_L(struct lcd_struct *p, unsigned int nr)
++{
++ unsigned int vs_rows = p->par->vs_rows;
++ unsigned int vs_cols = p->par->vs_cols;
++
++ clear_bit(NEED_WRAP, &p->struct_flags);
++ if (p->row+nr > vs_rows)
++ nr = vs_rows-p->row;
++ else if (! nr)
++ ++nr;;
++ lcd_memmove(p, (p->row+nr)*vs_cols, p->row*vs_cols, (vs_rows-p->row-nr)*vs_cols);
++ lcd_memset(p, p->row*vs_cols, p->erase_char, nr*vs_cols);
++}
++
++static void csi_M(struct lcd_struct *p, unsigned int nr)
++{
++ unsigned int vs_rows = p->par->vs_rows;
++ unsigned int vs_cols = p->par->vs_cols;
++
++ clear_bit(NEED_WRAP, &p->struct_flags);
++ if (p->row+nr > vs_rows)
++ nr = vs_rows-p->row;
++ else if (! nr)
++ ++nr;;
++ lcd_memmove(p, p->row*vs_cols, (p->row+nr)*vs_cols, (vs_rows-p->row-nr)*vs_cols);
++ lcd_memset(p, (vs_rows-nr)*vs_cols, p->erase_char, nr*vs_cols);
++}
++
++static void csi_P(struct lcd_struct *p, unsigned int nr)
++{
++ unsigned int vs_cols = p->par->vs_cols;
++
++ if (p->col+nr > vs_cols)
++ nr = vs_cols-p->col;
++ else if (! nr)
++ ++nr;
++ lcd_delete_char(p, nr);
++}
++
++static void csi_X(struct lcd_struct *p, unsigned int nr)
++{
++ unsigned int vs_cols = p->par->vs_cols;
++
++ clear_bit(NEED_WRAP, &p->struct_flags);
++ if (p->col+nr > vs_cols)
++ nr = vs_cols-p->col;
++ else if (! nr)
++ ++nr;
++ lcd_memset(p, (p->row*vs_cols)+p->col, p->erase_char, nr);
++}
++
++static void csi_su(struct lcd_struct *p, unsigned char input)
++{
++ unsigned int vs_cols = p->par->vs_cols;
++
++ clear_bit(NEED_WRAP, &p->struct_flags);
++ if (input == 'u') {
++ p->row = p->s_offset/vs_cols;
++ p->col = p->s_offset%vs_cols;
++ p->color = p->s_color;
++ p->attributes = p->s_attributes;
++ return;
++ }
++ p->s_offset = (p->row*vs_cols)+p->col;
++ p->s_color = p->color;
++ p->s_attributes = p->attributes;
++}
++
++static unsigned char build_attr(struct lcd_struct *p, unsigned char color, unsigned char intensity,
++ unsigned char blink, unsigned char underline, unsigned char reverse)
++{
++ unsigned char attr;
++
++ if (test_bit(CAN_DO_COLOR, &p->struct_flags)) {
++ attr = color;
++ if (underline)
++ attr = (attr & BG_MASK) | p->ulcolor;
++ else if (intensity == 0)
++ attr = (attr & BG_MASK) | p->halfcolor;
++ if (reverse)
++ attr = (attr & 0x88) | ((attr & 0x70) >> 4) | ((attr & 0x07) << 4);
++ if (blink)
++ attr ^= 0x80;
++ if (intensity == 2)
++ attr ^= 0x08;
++ } else {
++ attr = intensity;
++ attr |= (underline ? ULINE : 0x00);
++ attr |= (reverse ? REVERSE : 0x00);
++ attr |= (blink ? BLINK : 0x00);
++ }
++
++ return (attr);
++}
++
++static void update_attr(struct lcd_struct *p)
++{
++ unsigned char intensity = p->attributes & 0x03;
++ unsigned char underline = (p->attributes >> 2) & 0x01;
++ unsigned char reverse = (p->attributes >> 3) & 0x01;
++ unsigned char blink = (p->attributes >> 7) & 0x01;
++ unsigned char decscnm = (p->struct_flags >> DECSCNM) & 0x01;
++
++ p->attr = build_attr(p, p->color, intensity, blink, underline, reverse^decscnm);
++ p->erase_char = (build_attr(p, p->color, 1, blink, 0, decscnm) << 8) | ' ';
++}
++
++static void default_attr(struct lcd_struct *p)
++{
++ p->attributes = 0x01;
++ p->color = p->defcolor;
++}
++
++static void lcd_invert_screen(struct lcd_struct *p, unsigned int offset, unsigned int len)
++{
++ if (test_bit(CAN_DO_COLOR, &p->struct_flags))
++ while (len--) {
++ p->fb[offset] = (p->fb[offset] & 0x88ff) | ((p->fb[offset] & 0x7000) >> 4) | ((p->fb[offset] & 0x0700) << 4);
++ ++offset;
++ }
++ else
++ while (len--) {
++ p->fb[offset] ^= 0x0800;
++ ++offset;
++ }
++}
++
++static void csi_m(struct lcd_struct *p, unsigned int n)
++{
++ int i, arg;
++
++ for (i = 0; i <= n; ++i)
++ switch ((arg = p->csi_args[i]))
++ {
++ case 0:
++ default_attr(p);
++ break;
++
++ case 1:
++ p->attributes = (p->attributes & ~I_MASK) | 2;
++ break;
++
++ case 2:
++ p->attributes = (p->attributes & ~I_MASK) | 0;
++ break;
++
++ case 4:
++ p->attributes |= ULINE;
++ break;
++
++ case 5:
++ p->attributes |= BLINK;
++ break;
++
++ case 7:
++ p->attributes |= REVERSE;
++ break;
++
++ case 21: case 22:
++ p->attributes = (p->attributes & ~I_MASK) | 1;
++ break;
++
++ case 24:
++ p->attributes &= ~ULINE;
++ break;
++
++ case 25:
++ p->attributes &= ~BLINK;
++ break;
++
++ case 27:
++ p->attributes &= ~REVERSE;
++ break;
++
++ case 38:
++ p->attributes |= ULINE;
++ p->color = (p->color & BG_MASK) | (p->defcolor & FG_MASK);
++ break;
++
++ case 39:
++ p->attributes &= ~ULINE;
++ p->color = (p->color & BG_MASK) | (p->defcolor & FG_MASK);
++ break;
++
++ case 49:
++ p->color = (p->defcolor & BG_MASK) | (p->color & FG_MASK);
++ break;
++
++ default:
++ if (arg >= 30 && arg <= 37)
++ p->color = (p->color & BG_MASK) | color_table[arg-30];
++ else if (arg >= 40 && arg <= 47)
++ p->color = (p->color & FG_MASK) | (color_table[arg-40] << 4);
++ break;
++ }
++
++ update_attr(p);
++}
++
++static void csi_h(struct lcd_struct *p, unsigned char n)
++{
++ switch (n) {
++ case 4: /* Set insert mode */
++ set_bit(DECIM, &p->struct_flags);
++ return;
++
++ case 5: /* Inverted screen mode */
++ if (test_bit(QUES, &p->struct_flags) && ! test_bit(DECSCNM, &p->struct_flags)) {
++ set_bit(DECSCNM, &p->struct_flags);
++ lcd_invert_screen(p, 0, p->fb_size);
++ update_attr(p);
++ redraw_screen(p, 0, p->fb_size-1);
++ }
++ return;
++
++ case 7: /* Set autowrap */
++ if (test_bit(QUES, &p->struct_flags))
++ set_bit(DECAWM, &p->struct_flags);
++ return;
++
++ case 20: /* Set cr lf */
++ set_bit(CRLF, &p->struct_flags);
++ return;
++ }
++}
++
++static void csi_l(struct lcd_struct *p, unsigned char n)
++{
++ switch (n) {
++ case 4: /* Reset insert mode */
++ clear_bit(DECIM, &p->struct_flags);
++ return;
++
++ case 5: /* Normal screen mode */
++ if (test_bit(QUES, &p->struct_flags) && test_bit(DECSCNM, &p->struct_flags)) {
++ clear_bit(DECSCNM, &p->struct_flags);
++ lcd_invert_screen(p, 0, p->fb_size);
++ update_attr(p);
++ redraw_screen(p, 0, p->fb_size-1);
++ }
++ return;
++
++ case 7: /* Reset autowrap */
++ if (test_bit(QUES, &p->struct_flags))
++ clear_bit(DECAWM, &p->struct_flags);
++ return;
++
++ case 20: /* Reset cr lf */
++ clear_bit(CRLF, &p->struct_flags);
++ return;
++ }
++}
++
++static void csi_linux(struct lcd_struct *p)
++{
++ switch (p->csi_args[0]) {
++ case 1:
++ if (test_bit(CAN_DO_COLOR, &p->struct_flags) && p->csi_args[1] < 16) {
++ p->ulcolor = color_table[p->csi_args[1]];
++ if (p->attributes & ULINE)
++ update_attr(p);
++ }
++ return;
++
++ case 2:
++ if (test_bit(CAN_DO_COLOR, &p->struct_flags) && p->csi_args[1] < 16) {
++ p->halfcolor = color_table[p->csi_args[1]];
++ if ((p->attributes & I_MASK) == 0)
++ update_attr(p);
++ }
++ return;
++
++ case 8:
++ p->defcolor = p->color;
++ default_attr(p);
++ update_attr(p);
++ return;
++ }
++}
++
++static void csi_r(struct lcd_struct *p, unsigned int top, unsigned int bot)
++{
++ /* Minimum allowed region is 2 lines */
++ if (top < bot) {
++ p->top = top-1;
++ p->bot = bot;
++ gotoxy(p, 0, 0);
++ }
++}
++
++/*
++ * ECMA-48 CSI ESC- sequence handler.
++ */
++static void handle_csi(struct lcd_struct *p, unsigned char input)
++{
++ if (p->index >= NPAR) {
++ SET_INPUT_STATE(p, NORMAL);
++ printk(KERN_NOTICE "LCD: too many parameters in CSI escape sequence\n");
++ } else if (input == '?') {
++ set_bit(QUES, &p->struct_flags);
++ } else if (input == ';') {
++ ++p->index;
++ } else if (input >= '0' && input <= '9') {
++ p->csi_args[p->index] = (p->csi_args[p->index]*10)+(input-'0');
++ } else {
++ SET_INPUT_STATE(p, NORMAL);
++ if (! test_bit(INC_CURS_POS, &p->struct_flags))
++ return;
++ switch (input) {
++ case 'h': /* DECSET sequences and mode switches */
++ csi_h(p, p->csi_args[0]);
++ clear_bit(QUES, &p->struct_flags);
++ return;
++
++ case 'l': /* DECRST sequences and mode switches */
++ csi_l(p, p->csi_args[0]);
++ clear_bit(QUES, &p->struct_flags);
++ return;
++ }
++ clear_bit(QUES, &p->struct_flags);
++ switch (input) {
++ case '@': /* Insert # Blank character */
++ csi_at(p, p->csi_args[0]);
++ return;
++
++ case 'G': case '`': /* Cursor to indicated column in current row */
++ if (p->csi_args[0])
++ --p->csi_args[0];
++ gotoxy(p, p->csi_args[0], p->row);
++ return;
++
++ case 'A': /* Cursor # rows Up */
++ if (! p->csi_args[0])
++ ++p->csi_args[0];
++ gotoxy(p, p->col, p->row-p->csi_args[0]);
++ return;
++
++ case 'B': case 'e': /* Cursor # rows Down */
++ if (! p->csi_args[0])
++ ++p->csi_args[0];
++ gotoxy(p, p->col, p->row+p->csi_args[0]);
++ return;
++
++ case 'C': case 'a': /* Cursor # columns Right */
++ if (! p->csi_args[0])
++ ++p->csi_args[0];
++ gotoxy(p, p->col+p->csi_args[0], p->row);
++ return;
++
++ case 'D': /* Cursor # columns Left */
++ if (! p->csi_args[0])
++ ++p->csi_args[0];
++ gotoxy(p, p->col-p->csi_args[0], p->row);
++ return;
++
++ case 'E': /* Cursor # rows Down, column 1 */
++ if (! p->csi_args[0])
++ ++p->csi_args[0];
++ gotoxy(p, 0, p->row+p->csi_args[0]);
++ return;
++
++ case 'F': /* Cursor # rows Up, column 1 */
++ if (! p->csi_args[0])
++ ++p->csi_args[0];
++ gotoxy(p, 0, p->row-p->csi_args[0]);
++ return;
++
++ case 'd': /* Cursor to indicated row in current column */
++ if (p->csi_args[0])
++ --p->csi_args[0];
++ gotoxy(p, p->col, p->csi_args[0]);
++ return;
++
++ case 'H': case 'f': /* Cursor to indicated row, column (origin 1, 1) */
++ if (p->csi_args[0])
++ --p->csi_args[0];
++ if (p->csi_args[1])
++ --p->csi_args[1];
++ gotoxy(p, p->csi_args[1], p->csi_args[0]);
++ return;
++
++ case 'J': /* Erase display */
++ csi_J(p, p->csi_args[0]);
++ return;
++
++ case 'K': /* Erase line */
++ csi_K(p, p->csi_args[0]);
++ return;
++
++ case 'L': /* Insert # blank lines */
++ csi_L(p, p->csi_args[0]);
++ return;
++
++ case 'M': /* Delete # blank lines */
++ csi_M(p, p->csi_args[0]);
++ return;
++
++ case 'P': /* Delete # characters on the current line */
++ csi_P(p, p->csi_args[0]);
++ return;
++
++ case 'X': /* Erase # characters on the current line */
++ csi_X(p, p->csi_args[0]);
++ return;
++
++ case 'm': /* Set video attributes */
++ csi_m(p, p->index);
++ return;
++
++ case 's': /* Save cursor position */
++ case 'u': /* Restore cursor position */
++ csi_su(p, input);
++ return;
++
++ case ']': /* Linux private ESC [ ] sequence */
++ csi_linux(p);
++ return;
++
++ case 'r': /* Set the scrolling region */
++ if (! p->csi_args[0])
++ ++p->csi_args[0];
++ if (! p->csi_args[1] || p->csi_args[1] > p->par->vs_rows)
++ p->csi_args[1] = p->par->vs_rows;
++ csi_r(p, p->csi_args[0], p->csi_args[1]);
++ return;
++
++ /* Ignored escape sequences */
++ case 'c':
++ case 'g':
++ case 'n':
++ case 'q':
++ return;
++
++ default:
++ printk(KERN_NOTICE "LCD: unrecognized CSI escape sequence: ESC [ %u\n", input);
++ return;
++ }
++ }
++}
++
++/*
++ * Custom ESC- sequence handler.
++ */
++static int handle_custom_esc(struct lcd_struct *p, unsigned int _input)
++{
++ unsigned char input = _input & 0xff;
++ struct lcd_parameters *par = p->par;
++
++ if (_input & (~0xff)) {
++ switch (ESC_STATE(p)) {
++ case 's':
++ if (p->index++) {
++ unsigned char *buf = p->cgram_buffer+(p->cgram_index-par->cgram_char0)*par->cgram_bytes;
++
++ buf[p->index-2] = input;
++ if (p->index == par->cgram_bytes+1)
++ write_cgram(p, p->cgram_index, buf);
++ } else {
++ if (! p->driver->write_cgram_char) {
++ printk(KERN_ERR "LCD: %s: missing function to write to CGRAM\n", p->par->name);
++ return (-1);
++ }
++ if (input >= par->cgram_char0 && input < par->cgram_char0+par->cgram_chars)
++ p->cgram_index = input;
++ else {
++ printk(KERN_NOTICE "LCD: bad CGRAM index\n");
++ return (-1);
++ }
++ }
++ return (0);
++
++ case 'G':
++ if (input >= par->cgram_char0 && input < par->cgram_char0+par->cgram_chars)
++ write_data(p, (p->attr << 8) | p->driver->charmap[input]);
++ else {
++ SET_INPUT_STATE(p, NORMAL);
++ handle_input(p, (p->attr << 8) | input);
++ }
++ return (0);
++
++ case 'r':
++ if (input == '1')
++ address_mode(p, -1);
++ else if (input == '0')
++ address_mode(p, 1);
++ return (0);
++
++ case 'A':
++ scrup(p, p->top, p->bot, input);
++ return (0);
++
++ case 'B':
++ scrdown(p, p->top, p->bot, input);
++ return (0);
++
++ case 'C':
++ browse_screen(p, input);
++ return (0);
++ }
++ }
++
++ /* These are the custom ESC- sequences */
++ switch (input) {
++ case 's': /* CGRAM select */
++ if (p->cgram_buffer) {
++ SET_ESC_STATE(p, input);
++ p->index = 0;
++ return (par->cgram_bytes+1);
++ } else {
++ printk(KERN_NOTICE "LCD: driver %s does not support CGRAM chars\n", par->name);
++ return (0);
++ }
++
++ case 'A': /* Scroll up */
++ case 'B': /* Scroll down */
++ case 'C': /* Browse screen */
++ case 'G': /* Enter cgram mode */
++ case 'r': /* Decrement counter after data read/write */
++ SET_ESC_STATE(p, input);
++ return (1);
++ }
++
++ return (-1);
++}
++
++/*
++ * ESC- but not CSI sequence handler.
++ */
++static int handle_esc(struct lcd_struct *p, unsigned char input)
++{
++ int ret;
++
++ SET_INPUT_STATE(p, NORMAL);
++ switch (input) {
++ case 'c': /* Reset */
++ set_bit(DECAWM, &p->struct_flags);
++ set_bit(INC_CURS_POS, &p->struct_flags);
++ ff(p);
++ return (0);
++
++ case 'D': /* Line Feed */
++ lf(p);
++ return (0);
++
++ case 'E': /* New Line */
++ cr(p);
++ lf(p);
++ return (0);
++
++ case 'M': /* Reverse Line Feed */
++ ri(p);
++ return (0);
++
++ case '7':
++ case '8':
++ csi_su(p, (input == '7' ? 's' : 'u'));
++ return (0);
++
++ /* CSI: Start of CSI escape sequence */
++ case '[':
++ memset(p->csi_args, 0, sizeof(p->csi_args));
++ p->index = 0;
++ SET_INPUT_STATE(p, CSI);
++ return (0);
++
++ /* Ignored escape sequences */
++ case '(':
++ SET_INPUT_STATE(p, ESC_G0);
++ return (1);
++
++ case ')':
++ SET_INPUT_STATE(p, ESC_G1);
++ return (1);
++
++ case '#':
++ SET_INPUT_STATE(p, ESC_HASH);
++ return (1);
++
++ case '%':
++ SET_INPUT_STATE(p, ESC_PERCENT);
++ return (1);
++
++ case 'H':
++ case 'Z':
++ case '>':
++ case '=':
++ case ']':
++ return (0);
++ }
++
++ /* These are the custom ESC- sequences */
++ if ((ret = handle_custom_esc(p, input)) > 0) {
++ SET_INPUT_STATE(p, ARG);
++ return (ret);
++ }
++
++ if (ret < 0 && p->driver->handle_custom_char)
++ if ((ret = p->driver->handle_custom_char(input)) > 0) {
++ SET_INPUT_STATE(p, ARG_DRIVER);
++ return (ret);
++ }
++
++ if (ret < 0)
++ printk(KERN_NOTICE "LCD: unrecognized escape sequence: ESC %u\n", input);
++
++ return (0);
++}
++
++/*
++ * Main input handler.
++ */
++static void handle_input(struct lcd_struct *p, unsigned short _input)
++{
++ unsigned char input = _input & 0xff;
++ struct lcd_driver *driver = p->driver;
++
++ switch (INPUT_STATE(p)) {
++ case NORMAL:
++ if (input < 0x20 || input == 0x9b)
++ control_char(p, input);
++ else
++ write_data(p, (_input & 0xff00) | driver->charmap[input]);
++ return;
++
++ case RAW:
++ write_data(p, (_input & 0xff00) | driver->charmap[input]);
++ return;
++
++ case SYN:
++ write_data(p, _input);
++ SET_INPUT_STATE(p, NORMAL);
++ return;
++
++ case ESC:
++ p->esc_args = handle_esc(p, input);
++ return;
++
++ case ESC_G0:
++ case ESC_G1:
++ case ESC_HASH:
++ case ESC_PERCENT:
++ if (! --p->esc_args)
++ SET_INPUT_STATE(p, NORMAL);
++ return;
++
++ case CSI:
++ handle_csi(p, input);
++ return;
++
++ case ARG:
++ if (handle_custom_esc(p, 0x100 | input) || ! --p->esc_args)
++ SET_INPUT_STATE(p, NORMAL);
++ return;
++
++ case ARG_DRIVER:
++ if (driver->handle_custom_char(0x100 | input) || ! --p->esc_args)
++ SET_INPUT_STATE(p, NORMAL);
++ return;
++ }
++}
++
++
++
++
++
++/***************************************
++ * Read from/Write to display routines *
++ ***************************************/
++
++/*
++ * Write character data to the display.
++ */
++static void write_data(struct lcd_struct *p, unsigned short data)
++{
++ unsigned int vs_cols = p->par->vs_cols;
++ unsigned int pos;
++ int frame_pos;
++
++ if (test_bit(NEED_WRAP, &p->struct_flags)) {
++ cr(p);
++ lf(p);
++ }
++
++ if (test_bit(DECIM, &p->struct_flags))
++ lcd_insert_char(p, 1);
++
++ pos = (p->row*vs_cols)+p->col;
++ if ((frame_pos = vs_to_frame_(p, pos)) < 0) {
++ make_cursor_visible(p);
++ redraw_screen(p, 0, p->fb_size-1);
++ frame_pos = vs_to_frame_(p, pos);
++ }
++
++ if (p->display[frame_pos] != data) {
++ p->driver->write_char(frame_pos, data);
++ p->display[frame_pos] = data;
++ }
++
++ p->fb[pos] = data;
++
++ if (test_bit(INC_CURS_POS, &p->struct_flags)) {
++ if (p->col+1 < vs_cols)
++ iterator_inc(p->col, vs_cols);
++ else if (test_bit(DECAWM, &p->struct_flags))
++ set_bit(NEED_WRAP, &p->struct_flags);
++ } else {
++ if (p->col)
++ iterator_dec(p->col, vs_cols);
++ else if (test_bit(DECAWM, &p->struct_flags))
++ set_bit(NEED_WRAP, &p->struct_flags);
++ }
++}
++
++/*
++ * Write an entire CGRAM character to the display.
++ */
++static void write_cgram(struct lcd_struct *p, unsigned char index, unsigned char *pixels)
++{
++ unsigned int inc_set = test_bit(INC_CURS_POS, &p->struct_flags);
++
++ if (! inc_set)
++ address_mode(p, 1);
++
++ p->driver->write_cgram_char(index, pixels);
++
++ if (! inc_set)
++ address_mode(p, -1);
++}
++
++/*
++ * Read character data from the display.
++ */
++static void read_data(struct lcd_struct *p, unsigned short *data)
++{
++ unsigned int vs_rows = p->par->vs_rows;
++ unsigned int vs_cols = p->par->vs_cols;
++ unsigned int pos = (p->row*vs_cols)+p->col;
++ int frame_pos;
++
++ if ((frame_pos = vs_to_frame_(p, pos)) < 0) {
++ make_cursor_visible(p);
++ redraw_screen(p, 0, p->fb_size-1);
++ frame_pos = vs_to_frame_(p, pos);
++ }
++
++ p->driver->read_char(frame_pos, data);
++
++ if (test_bit(INC_CURS_POS, &p->struct_flags)) {
++ iterator_inc(p->col, vs_cols);
++ if (! p->col) {
++ if (p->row+1 < vs_rows)
++ ++p->row;
++ }
++ } else {
++ iterator_dec(p->col, vs_cols);
++ if (p->col+1 == vs_cols) {
++ if (p->row)
++ --p->row;
++ }
++ }
++}
++
++/*
++ * Read an entire CGRAM character from the display.
++ */
++static void read_cgram(struct lcd_struct *p, unsigned char index, unsigned char *pixels)
++{
++ unsigned int inc_set = test_bit(INC_CURS_POS, &p->struct_flags);
++
++ if (! inc_set)
++ address_mode(p, 1);
++
++ p->driver->read_cgram_char(index, pixels);
++
++ if (! inc_set)
++ address_mode(p, -1);
++}
++
++
++
++
++
++/****************************
++ * Proc filesystem routines *
++ ****************************/
++#ifdef USE_PROC
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 0)
++/* create_proc_read_entry is missing in 2.2.x kernels */
++static struct proc_dir_entry *create_proc_read_entry(const char *name, mode_t mode,
++ struct proc_dir_entry *parent, read_proc_t *read_proc, void *data)
++{
++ struct proc_dir_entry *res = create_proc_entry(name, mode, parent);
++
++ if (res) {
++ res->read_proc = read_proc;
++ res->data = data;
++ }
++
++ return (res);
++}
++#endif
++
++static int proc_fb_read(char *buffer, char **start, off_t offset, int size, int *eof, void *data)
++{
++ char *temp = buffer;
++ struct lcd_struct *p = (struct lcd_struct *)data;
++ unsigned int vs_cols;
++ static unsigned int nr, need_wrap;
++ static off_t _offset;
++
++ down(&p->lcd_sem);
++ if (! offset)
++ _offset = 0;
++ if ((*eof = (_offset >= p->fb_size))) {
++ up(&p->lcd_sem);
++ return (0);
++ }
++ vs_cols = p->par->vs_cols;
++ if (size && need_wrap) {
++ need_wrap = 0;
++ temp += sprintf(temp, "\n");
++ --size;
++ }
++ if (! nr)
++ nr = vs_cols;
++ *start = (char *)0;
++ while (size && nr) {
++ unsigned char c = (p->fb[_offset] & 0xff);
++
++ temp += sprintf(temp, "%c", (c < 0x20 ? '·' : c));
++ --size;
++ --nr;
++ ++*start;
++ ++_offset;
++ }
++ if (! nr) {
++ if (size) {
++ temp += sprintf(temp, "\n");
++ --size;
++ } else
++ need_wrap = 1;
++ }
++ up(&p->lcd_sem);
++
++ return (temp-buffer);
++}
++
++static int proc_display_read(char *buffer, char **start, off_t offset, int size, int *eof, void *data)
++{
++ char *temp = buffer;
++ struct lcd_struct *p = (struct lcd_struct *)data;
++ unsigned int i, frame_cols;
++ int frame_pos;
++
++ down(&p->lcd_sem);
++ frame_cols = p->par->cntr_cols;
++ frame_pos = vs_to_frame_(p, (p->row*p->par->vs_cols)+p->col);
++ temp += sprintf(temp, " ");
++ for (i = 2; i <= frame_cols; i += 2)
++ temp += sprintf(temp, " %d", i%10);
++ temp += sprintf(temp, "\n");
++
++ temp += sprintf(temp, " +");
++ for (i = 0; i < frame_cols; ++i)
++ temp += sprintf(temp, "-");
++ temp += sprintf(temp, "+\n");
++
++ for (i = 0; i < p->frame_size; ++i) {
++ unsigned char c = (p->display[i] & 0xff);
++
++ if (! (i%frame_cols))
++ temp += sprintf(temp, "%2d |", 1+i/frame_cols);
++ if (frame_pos--)
++ temp += sprintf(temp, "%c", (c < 0x20 ? '·' : c));
++ else
++ temp += sprintf(temp, "_");
++ if (! ((i+1)%frame_cols))
++ temp += sprintf(temp, "|\n");
++ }
++
++ temp += sprintf(temp, " +");
++ for (i = 0; i < frame_cols; ++i)
++ temp += sprintf(temp, "-");
++ temp += sprintf(temp, "+\n");
++ up(&p->lcd_sem);
++
++ return (temp-buffer);
++}
++
++static int proc_charmap_read(char *buffer, char **start, off_t offset, int size, int *eof, void *data)
++{
++ char *temp = buffer;
++ struct lcd_struct *p = (struct lcd_struct *)data;
++ unsigned char *charmap;
++ unsigned int i;
++
++ down(&p->lcd_sem);
++ charmap = p->driver->charmap;
++ temp += sprintf(temp, "static unsigned char charmap[] = {");
++ for (i = 0; i < 255; ++i) {
++ if (! (i & 7)) {
++ temp += sprintf(temp, "\n");
++ if (! (i & 31))
++ temp += sprintf(temp, "\n/* %d - %d */\n", i, i+31);
++ }
++ temp += sprintf(temp, "0x%.2x, ", *charmap++);
++ }
++ temp += sprintf(temp, "0x%.2x\n\n};\n", *charmap);
++ up(&p->lcd_sem);
++
++ return (temp-buffer);
++}
++
++static int proc_registered_drivers(char *buffer, char **start, off_t offset, int size, int *eof, void *data)
++{
++ char *temp = buffer;
++ struct list_head *entry;
++
++ down(&drivers_sem);
++ temp += sprintf(temp, "Registered drivers:\n");
++ list_for_each(entry, &lcd_drivers) {
++ struct lcd_struct *p = list_entry(entry, struct lcd_struct, lcd_list);
++
++ down(&p->lcd_sem);
++ temp += sprintf(temp, "%3d %s\n", p->par->minor, p->par->name);
++ up(&p->lcd_sem);
++ }
++ up(&drivers_sem);
++
++ return (temp-buffer);
++}
++
++static void create_driver_proc_entries(struct lcd_struct *p)
++{
++ struct proc_dir_entry *driver_proc_root = p->driver->driver_proc_root;
++ int proc_level = 0;
++
++ SET_PROC_LEVEL(p, proc_level);
++ if (create_proc_read_entry("framebuffer", 0, driver_proc_root, proc_fb_read, p) == NULL) {
++ printk(KERN_ERR "LCD: cannot create /proc/lcd/%s/framebuffer\n", p->par->name);
++ return;
++ }
++ SET_PROC_LEVEL(p, ++proc_level);
++ if (create_proc_read_entry("display", 0, driver_proc_root, proc_display_read, p) == NULL) {
++ printk(KERN_ERR "LCD: cannot create /proc/lcd/%s/display\n", p->par->name);
++ return;
++ }
++ SET_PROC_LEVEL(p, ++proc_level);
++ if (create_proc_read_entry("charmap.h", 0, driver_proc_root, proc_charmap_read, p) == NULL) {
++ printk(KERN_ERR "LCD: cannot create /proc/lcd/%s/charmap.h\n", p->par->name);
++ return;
++ }
++ SET_PROC_LEVEL(p, ++proc_level);
++}
++
++static void remove_driver_proc_entries(struct lcd_struct *p)
++{
++ struct proc_dir_entry *driver_proc_root = p->driver->driver_proc_root;
++
++ switch (PROC_LEVEL(p)) {
++ case 3:
++ remove_proc_entry("charmap.h", driver_proc_root);
++ case 2:
++ remove_proc_entry("display", driver_proc_root);
++ case 1:
++ remove_proc_entry("framebuffer", driver_proc_root);
++ }
++ SET_PROC_LEVEL(p, 0);
++}
++#endif
++
++
++
++
++
++/*****************************
++ * Low level file operations *
++ *****************************/
++static ssize_t do_lcd_read(struct lcd_struct *p, void *buffer, size_t length)
++{
++ unsigned int i;
++ unsigned short tmp;
++
++ if (! p->refcount)
++ return (-ENXIO);
++
++ if (! p->driver->read_char) {
++ printk(KERN_NOTICE "LCD: driver %s doesn't support reading\n", p->par->name);
++ return (-ENOSYS);
++ }
++
++ if (test_bit(WITH_ATTR, &p->struct_flags))
++ for (i = 0; i < length; ++i) {
++ read_data(p, &tmp);
++ ((unsigned short *)buffer)[i] = tmp;
++ }
++ else
++ for (i = 0; i < length; ++i) {
++ read_data(p, &tmp);
++ ((unsigned char *)buffer)[i] = tmp & 0xff;
++ }
++
++ return (length);
++}
++
++static ssize_t do_lcd_write(struct lcd_struct *p, const void *buffer, size_t length)
++{
++ unsigned int i;
++
++ if (! p->refcount)
++ return (-ENXIO);
++
++ if (test_bit(WITH_ATTR, &p->struct_flags))
++ for (i = 0; i < length; ++i)
++ handle_input(p, ((const unsigned short *)buffer)[i]);
++ else
++ for (i = 0; i < length; ++i)
++ handle_input(p, (p->attr << 8) | ((const unsigned char *)buffer)[i]);
++
++ return (length);
++}
++
++static int do_lcd_open(struct lcd_struct *p)
++{
++ if (! p->refcount) {
++ if (p->driver->driver_module) {
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
++ if (! try_module_get(p->driver->driver_module))
++ return (-EBUSY);
++#else
++ if (__MOD_IN_USE(p->driver->driver_module))
++ return (-EBUSY);
++
++ __MOD_INC_USE_COUNT(p->driver->driver_module);
++#endif
++ }
++ }
++
++ ++p->refcount;
++
++ return (0);
++}
++
++static int do_lcd_release(struct lcd_struct *p)
++{
++ if (! p->refcount)
++ return (0);
++
++ if (p->refcount == 1) {
++ if (p->driver->driver_module)
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
++ module_put(p->driver->driver_module);
++#else
++ __MOD_DEC_USE_COUNT(p->driver->driver_module);
++#endif
++ }
++
++ --p->refcount;
++
++ return (0);
++}
++
++static int cgram_ioctl(struct lcd_struct *p, unsigned int cmd, unsigned char *argp)
++{
++ struct lcd_parameters *par = p->par;
++ unsigned int length = par->cgram_bytes;
++ unsigned char index = argp[0];
++ unsigned char *buffer = argp+1;
++ unsigned char *cgram_buffer = p->cgram_buffer+(index-par->cgram_char0)*length;
++
++ if (index < par->cgram_char0 || index >= par->cgram_char0+par->cgram_chars)
++ return (-EINVAL);
++
++ if (cmd == LCDL_SET_CGRAM_CHAR) {
++ if (! p->driver->write_cgram_char) {
++ printk(KERN_ERR "LCD: %s: missing function to write to CGRAM\n", p->par->name);
++ return (-ENOSYS);
++ }
++ if (test_bit(USER_SPACE, &p->struct_flags)) {
++ if (copy_from_user(cgram_buffer, buffer, length))
++ return (-EFAULT);
++ } else
++ memcpy(cgram_buffer, buffer, length);
++ write_cgram(p, index, cgram_buffer);
++ } else {
++ if (! p->driver->read_cgram_char) {
++ printk(KERN_ERR "LCD: %s: missing function to read from CGRAM or unable to read\n", p->par->name);
++ return (-ENOSYS);
++ }
++ read_cgram(p, index, cgram_buffer);
++ if (test_bit(USER_SPACE, &p->struct_flags)) {
++ if (copy_to_user(buffer, cgram_buffer, length))
++ return (-EFAULT);
++ } else
++ memcpy(buffer, cgram_buffer, length);
++ }
++
++ return (0);
++}
++
++static int do_lcd_ioctl(struct lcd_struct *p, unsigned int cmd, unsigned long arg)
++{
++ int i;
++ struct lcd_driver *driver = p->driver;
++ struct lcd_parameters *par = p->par;
++ unsigned char *argp = (unsigned char *)arg;
++
++ if (! p->refcount)
++ return (-ENXIO);
++
++ switch (cmd) {
++ case LCDL_SET_PARAM:
++ if ((i = cleanup_driver(p)))
++ return (i);
++ i = par->minor;
++ if (test_bit(USER_SPACE, &p->struct_flags)) {
++ if (copy_from_user(par, argp, sizeof(struct lcd_parameters)))
++ return (-EFAULT);
++ } else
++ memcpy(par, argp, sizeof(struct lcd_parameters));
++ par->minor = i;
++ return (init_driver(p));
++
++ case LCDL_GET_PARAM:
++ if (test_bit(USER_SPACE, &p->struct_flags)) {
++ if (copy_to_user(argp, par, sizeof(struct lcd_parameters)))
++ return (-EFAULT);
++ } else
++ memcpy(argp, par, sizeof(struct lcd_parameters));
++ return (0);
++
++ case LCDL_RESET_CHARMAP:
++ for (i = 0; i < 256; ++i)
++ driver->charmap[i] = i;
++ return (0);
++
++ case LCDL_CHARSUBST:
++ if (test_bit(USER_SPACE, &p->struct_flags)) {
++ get_user(i, argp);
++ get_user(driver->charmap[i], argp+1);
++ } else {
++ i = argp[0];
++ driver->charmap[i] = argp[1];
++ }
++ return (0);
++
++ case LCDL_SAVE_CHARMAP:
++ memcpy(p->s_charmap, driver->charmap, 256);
++ return (0);
++
++ case LCDL_RESTORE_CHARMAP:
++ memcpy(driver->charmap, p->s_charmap, 256);
++ return (0);
++
++ case LCDL_SWAP_CHARMAP:
++ {
++ unsigned char *tmp;
++
++ tmp = driver->charmap;
++ driver->charmap = p->s_charmap;
++ p->s_charmap = tmp;
++ }
++ return (0);
++
++ case LCDL_RAW_MODE:
++ if (arg) {
++ clear_bit(NEED_WRAP, &p->struct_flags);
++ clear_bit(DECIM, &p->struct_flags);
++ clear_bit(DECAWM, &p->struct_flags);
++ SET_INPUT_STATE(p, RAW);
++ } else {
++ set_bit(DECAWM, &p->struct_flags);
++ SET_INPUT_STATE(p, NORMAL);
++ }
++ return (0);
++
++ case LCDL_CLEAR_DISP:
++ ff(p);
++ return (0);
++
++ case LCDL_SET_CGRAM_CHAR:
++ case LCDL_GET_CGRAM_CHAR:
++ if (p->cgram_buffer)
++ return (cgram_ioctl(p, cmd, argp));
++ else
++ printk(KERN_NOTICE "LCD: driver %s does not support CGRAM chars\n", par->name);
++ return (0);
++
++ case LCDL_SET_CHARMAP:
++ if (test_bit(USER_SPACE, &p->struct_flags)) {
++ if (copy_from_user(driver->charmap, argp, 256))
++ return (-EFAULT);
++ } else
++ memcpy(driver->charmap, argp, 256);
++ return (0);
++
++ case LCDL_GET_CHARMAP:
++ if (test_bit(USER_SPACE, &p->struct_flags)) {
++ if (copy_to_user(argp, driver->charmap, 256))
++ return (-EFAULT);
++ } else
++ memcpy(argp, driver->charmap, 256);
++ return (0);
++
++ case LCDL_MEMSET:
++ case LCDL_MEMMOVE:
++ {
++ int buf[3];
++
++ if (test_bit(USER_SPACE, &p->struct_flags)) {
++ if (copy_from_user(buf, argp, sizeof(buf)))
++ return (-EFAULT);
++ } else
++ memcpy(buf, argp, sizeof(buf));
++
++ if (cmd == LCDL_MEMSET)
++ lcd_memset(p, buf[0], buf[1], buf[2]);
++ else
++ lcd_memmove(p, buf[0], buf[1], buf[2]);
++
++ return (0);
++ }
++
++ default:
++ if (driver->handle_custom_ioctl)
++ return (driver->handle_custom_ioctl(cmd, arg, test_bit(USER_SPACE, &p->struct_flags)));
++ }
++
++ return (-ENOIOCTLCMD);
++}
++
++
++
++
++
++/**************************************************
++ * Kernel register/unregister lcd driver routines *
++ **************************************************/
++/*
++ * Find a driver in lcd_drivers linked list
++ */
++static struct lcd_struct *find_lcd_struct(unsigned short minor)
++{
++ struct list_head *entry;
++
++ list_for_each(entry, &lcd_drivers) {
++ struct lcd_struct *p = list_entry(entry, struct lcd_struct, lcd_list);
++
++ if (p->par->minor == minor)
++ return (p);
++ }
++
++ return (NULL);
++}
++
++static void list_add_sorted(struct list_head *new)
++{
++ struct list_head *entry;
++ unsigned short new_minor = (list_entry(new, struct lcd_struct, lcd_list))->par->minor;
++
++ list_for_each(entry, &lcd_drivers) {
++ struct lcd_struct *p = list_entry(entry, struct lcd_struct, lcd_list);
++
++ if (p->par->minor > new_minor)
++ break;
++ }
++ list_add_tail(new, entry);
++}
++
++/* Exported function */
++int lcd_register_driver(struct lcd_driver *driver, struct lcd_parameters *par)
++{
++ int ret;
++ struct lcd_struct *p;
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
++ struct device *lcd_device;
++#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 13)
++ struct class_device *lcd_class_device;
++#endif
++
++ if (! driver || ! par || par->minor >= minors)
++ return (-EINVAL);
++ if (! driver->write_char || ! driver->init_port || ! driver->cleanup_port) {
++ printk(KERN_ERR "LCD: missing functions\n");
++ return (-EINVAL);
++ }
++
++ down(&drivers_sem);
++
++ if (find_lcd_struct(par->minor)) {
++ up(&drivers_sem);
++ return (-EBUSY);
++ }
++
++ if ((p = (struct lcd_struct *)kmalloc(sizeof(struct lcd_struct), GFP_KERNEL)) == NULL) {
++ printk(KERN_ERR "LCD: memory allocation failed (kmalloc)\n");
++ up(&drivers_sem);
++ return (-ENOMEM);
++ }
++ memset(p, 0, sizeof(struct lcd_struct));
++
++ p->driver = driver;
++ p->par = par;
++ p->refcount = 0;
++ SET_INIT_LEVEL(p, 0);
++ SET_INPUT_STATE(p, NORMAL);
++ set_bit(DECAWM, &p->struct_flags);
++ set_bit(INC_CURS_POS, &p->struct_flags);
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
++ lcd_device = device_create(lcd_linux_class, NULL, MKDEV(major, par->minor), "%s", par->name);
++ if (IS_ERR(lcd_device)) {
++ kfree(p);
++ up(&drivers_sem);
++ return (PTR_ERR(lcd_device));
++ }
++#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 15)
++ lcd_class_device = class_device_create(lcd_linux_class, NULL, MKDEV(major, par->minor), NULL, "%s", par->name);
++ if (IS_ERR(lcd_class_device)) {
++ kfree(p);
++ up(&drivers_sem);
++ return (PTR_ERR(lcd_class_device));
++ }
++#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 13)
++ lcd_class_device = class_device_create(lcd_linux_class, MKDEV(major, par->minor), NULL, "%s", par->name);
++ if (IS_ERR(lcd_class_device)) {
++ kfree(p);
++ up(&drivers_sem);
++ return (PTR_ERR(lcd_class_device));
++ }
++#endif
++
++#ifdef USE_PROC
++ if (lcd_proc_root && (driver->driver_proc_root = proc_mkdir(par->name, lcd_proc_root)) == NULL)
++ printk(KERN_ERR "LCD: cannot create /proc/lcd/%s/\n", par->name);
++#endif
++
++ if ((ret = init_driver(p))) {
++#ifdef USE_PROC
++ if (driver->driver_proc_root)
++ remove_proc_entry(p->par->name, lcd_proc_root);
++#endif
++ kfree(p);
++ up(&drivers_sem);
++ return (ret);
++ }
++
++ init_MUTEX(&p->lcd_sem);
++
++ list_add_sorted(&p->lcd_list);
++
++ up(&drivers_sem);
++
++#ifdef MODULE
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
++ try_module_get(THIS_MODULE);
++#else
++ MOD_INC_USE_COUNT;
++#endif
++#endif
++
++ return (0);
++}
++EXPORT_SYMBOL(lcd_register_driver);
++
++/* Exported function */
++int lcd_unregister_driver(struct lcd_driver *driver, struct lcd_parameters *par)
++{
++ int ret;
++ struct lcd_struct *p;
++
++ if (! driver || ! par || par->minor >= minors)
++ return (-EINVAL);
++
++ down(&drivers_sem);
++
++ if ((p = find_lcd_struct(par->minor)) == NULL || p->driver != driver) {
++ printk(KERN_ERR "LCD: driver not found; lcd_unregister_driver failed\n");
++ up(&drivers_sem);
++ return (-ENODEV);
++ }
++
++ down(&p->lcd_sem);
++
++ if (p->refcount) {
++ printk(KERN_ERR "LCD: driver busy; lcd_unregister_driver failed\n");
++ up(&p->lcd_sem);
++ up(&drivers_sem);
++ return (-EBUSY);
++ }
++
++ if ((ret = cleanup_driver(p))) {
++ up(&p->lcd_sem);
++ up(&drivers_sem);
++ return (ret);
++ }
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
++ device_destroy(lcd_linux_class, MKDEV(major, par->minor));
++#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 13)
++ class_device_destroy(lcd_linux_class, MKDEV(major, par->minor));
++#endif
++
++#ifdef USE_PROC
++ if (p->driver->driver_proc_root)
++ remove_proc_entry(p->par->name, lcd_proc_root);
++#endif
++
++ list_del(&p->lcd_list);
++ kfree(p);
++
++ up(&drivers_sem);
++
++#ifdef MODULE
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
++ module_put(THIS_MODULE);
++#else
++ MOD_DEC_USE_COUNT;
++#endif
++#endif
++
++ return (0);
++}
++EXPORT_SYMBOL(lcd_unregister_driver);
++
++
++
++
++
++/************************
++ * Kernel I/O interface *
++ ************************/
++/* Exported function */
++int lcd_open(unsigned short minor, struct lcd_struct **pp)
++{
++ int ret;
++ struct lcd_struct *p;
++
++ down(&drivers_sem);
++
++ if (minor >= minors || (*pp = p = find_lcd_struct(minor)) == NULL) {
++ printk(KERN_ERR "LCD: lcd_open failed. Device not found.\n");
++ up(&drivers_sem);
++ return (-ENODEV);
++ }
++
++ down(&p->lcd_sem);
++ up(&drivers_sem);
++
++ ret = do_lcd_open(p);
++
++ up(&p->lcd_sem);
++
++ return (ret);
++}
++EXPORT_SYMBOL(lcd_open);
++
++/* Exported function */
++int lcd_close(struct lcd_struct **pp)
++{
++ int ret;
++ struct lcd_struct *p;
++
++ if (! pp || ! (p = *pp)) {
++ printk(KERN_ERR "LCD: NULL pointer in lcd_close\n");
++ return (-ENODEV);
++ }
++
++ down(&p->lcd_sem);
++
++ if (! (ret = do_lcd_release(p)))
++ *pp = NULL;
++
++ up(&p->lcd_sem);
++
++ return (ret);
++}
++EXPORT_SYMBOL(lcd_close);
++
++static inline loff_t offset_to_row_col(struct lcd_struct *p, loff_t offset)
++{
++ unsigned long _offset = offset;
++ unsigned int vs_cols = p->par->vs_cols;
++
++ gotoxy(p, _offset%vs_cols, _offset/vs_cols);
++
++ return ((p->row*vs_cols)+p->col);
++}
++
++/* Exported function */
++ssize_t lcd_read(struct lcd_struct *p, void *bufp, size_t length, loff_t offset, unsigned int with_attr)
++{
++ ssize_t ret = 0;
++
++ if (! p) {
++ printk(KERN_ERR "LCD: NULL pointer in lcd_read\n");
++ return (-ENODEV);
++ }
++ if (! bufp)
++ return (-EFAULT);
++ if (offset < 0 || offset >= p->fb_size)
++ return (-EINVAL);
++
++ if (length+offset > p->fb_size)
++ length = p->fb_size-offset;
++
++ if (with_attr)
++ set_bit(WITH_ATTR, &p->struct_flags);
++
++ offset_to_row_col(p, offset);
++ ret = do_lcd_read(p, bufp, length);
++
++ if (with_attr)
++ clear_bit(WITH_ATTR, &p->struct_flags);
++
++ return (ret);
++}
++EXPORT_SYMBOL(lcd_read);
++
++/* Exported function */
++ssize_t lcd_write(struct lcd_struct *p, const void *bufp, size_t length, loff_t offset, unsigned int with_attr)
++{
++ ssize_t ret;
++
++ if (! p) {
++ printk(KERN_ERR "LCD: NULL pointer in lcd_write\n");
++ return (-ENODEV);
++ }
++ if (! bufp)
++ return (-EFAULT);
++ if (offset < 0 || offset >= p->fb_size)
++ return (-EINVAL);
++
++ if (with_attr)
++ set_bit(WITH_ATTR, &p->struct_flags);
++
++ offset_to_row_col(p, offset);
++ ret = do_lcd_write(p, bufp, length);
++
++ if (with_attr)
++ clear_bit(WITH_ATTR, &p->struct_flags);
++
++ return (ret);
++}
++EXPORT_SYMBOL(lcd_write);
++
++/* Exported function */
++int lcd_ioctl(struct lcd_struct *p, unsigned int cmd, ...)
++{
++ int ret;
++ unsigned long arg;
++ va_list ap;
++
++ if (! p) {
++ printk(KERN_ERR "LCD: NULL pointer in lcd_ioctl\n");
++ return (-ENODEV);
++ }
++
++ down(&p->lcd_sem);
++ va_start(ap, cmd);
++ arg = va_arg(ap, unsigned long);
++ ret = do_lcd_ioctl(p, cmd, arg);
++ va_end(ap);
++ up(&p->lcd_sem);
++
++ return (ret);
++}
++EXPORT_SYMBOL(lcd_ioctl);
++
++
++
++
++
++/*******************
++ * File operations *
++ *******************/
++static loff_t lcd_fops_llseek(struct file *filp, loff_t offset, int orig)
++{
++ struct lcd_struct *p;
++
++ if (! (p = filp->private_data))
++ return (-ENODEV);
++
++ down(&p->lcd_sem);
++
++ switch (orig) {
++ case 0:
++ filp->f_pos = offset;
++ break;
++
++ case 1:
++ filp->f_pos += offset;
++ break;
++
++ default:
++ up(&p->lcd_sem);
++ return (-EINVAL); /* SEEK_END not supported */
++ }
++
++ filp->f_pos = offset_to_row_col(p, filp->f_pos);
++
++ up(&p->lcd_sem);
++
++ return (filp->f_pos);
++}
++
++static ssize_t lcd_fops_read(struct file *filp, char *buffer, size_t length, loff_t *offp)
++{
++ ssize_t ret = 0;
++ char *bufp = buffer;
++ struct lcd_struct *p;
++
++ if (! bufp)
++ return (-EFAULT);
++ if (! (p = filp->private_data))
++ return (-ENODEV);
++
++ down(&p->lcd_sem);
++
++ if (*offp < 0 || *offp >= p->fb_size) {
++ up(&p->lcd_sem);
++ return (-EINVAL);
++ }
++
++ if (length+(*offp) > p->fb_size)
++ length = p->fb_size-(*offp);
++
++ while (length) {
++ ret = (length > FLIP_BUF_SIZE ? FLIP_BUF_SIZE : length);
++ if ((ret = do_lcd_read(p, p->flip_buf, ret)) < 0)
++ break;
++ *offp = (p->row*p->par->vs_cols)+p->col;
++ if (copy_to_user(bufp, p->flip_buf, ret)) {
++ ret = -EFAULT;
++ break;
++ }
++ length -= ret;
++ bufp += ret;
++ ret = bufp-buffer;
++ if (length)
++ schedule();
++ }
++
++ up(&p->lcd_sem);
++
++ return (ret);
++}
++
++static ssize_t lcd_fops_write(struct file *filp, const char *buffer, size_t length, loff_t *offp)
++{
++ ssize_t ret = 0;
++ const char *bufp = buffer;
++ struct lcd_struct *p;
++
++ if (! bufp)
++ return (-EFAULT);
++ if (! (p = filp->private_data))
++ return (-ENODEV);
++
++ down(&p->lcd_sem);
++
++ if (*offp < 0 || *offp >= p->fb_size) {
++ up(&p->lcd_sem);
++ return (-EINVAL);
++ }
++
++ while (length) {
++ ret = (length > FLIP_BUF_SIZE ? FLIP_BUF_SIZE : length);
++ if (copy_from_user(p->flip_buf, bufp, ret)) {
++ ret = -EFAULT;
++ break;
++ }
++ if ((ret = do_lcd_write(p, p->flip_buf, ret)) < 0)
++ break;
++ *offp = (p->row*p->par->vs_cols)+p->col;
++ length -= ret;
++ bufp += ret;
++ ret = bufp-buffer;
++ if (length)
++ schedule();
++ }
++
++ up(&p->lcd_sem);
++
++ return (ret);
++}
++
++static int lcd_fops_open(struct inode *inop, struct file *filp)
++{
++ unsigned short minor;
++ int ret;
++ struct lcd_struct *p;
++
++ down(&drivers_sem);
++
++ if ((minor = MINOR(inop->i_rdev)) >= minors || (filp->private_data = p = find_lcd_struct(minor)) == NULL) {
++ up(&drivers_sem);
++ return (-ENODEV);
++ }
++
++ down(&p->lcd_sem);
++ up(&drivers_sem);
++
++ ret = do_lcd_open(p);
++
++ up(&p->lcd_sem);
++
++ return (ret);
++}
++
++static int lcd_fops_release(struct inode *inop, struct file *filp)
++{
++ struct lcd_struct *p;
++ int ret;
++
++ if (! (p = filp->private_data))
++ return (-ENODEV);
++
++ down(&p->lcd_sem);
++
++ if (! (ret = do_lcd_release(p)))
++ filp->private_data = NULL;
++
++ up(&p->lcd_sem);
++
++ return (ret);
++}
++
++static int lcd_fops_ioctl(struct inode *inop, struct file *filp, unsigned int cmd, unsigned long arg)
++{
++ struct lcd_struct *p;
++ int ret;
++
++ if (! (p = filp->private_data))
++ return (-ENODEV);
++
++ down(&p->lcd_sem);
++
++ set_bit(USER_SPACE, &p->struct_flags);
++ ret = do_lcd_ioctl(p, cmd, arg);
++ clear_bit(USER_SPACE, &p->struct_flags);
++
++ up(&p->lcd_sem);
++
++ return (ret);
++}
++
++static struct file_operations lcd_linux_fops = {
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 0)
++ .owner = THIS_MODULE,
++#endif
++ .llseek = lcd_fops_llseek,
++ .read = lcd_fops_read,
++ .write = lcd_fops_write,
++ .open = lcd_fops_open,
++ .release = lcd_fops_release,
++ .ioctl = lcd_fops_ioctl,
++};
++
++
++
++
++
++/********************************
++ * Init/Cleanup driver routines *
++ ********************************/
++static int do_init_driver(struct lcd_struct *p)
++{
++ int ret, init_level;
++ struct lcd_driver *driver = p->driver;
++ struct lcd_parameters *par = p->par;
++ unsigned int frame_rows = par->cntr_rows*par->num_cntr;
++ unsigned int frame_cols = par->cntr_cols;
++
++ switch ((init_level = INIT_LEVEL(p))) {
++ case 0:
++ if (frame_rows == 0 || frame_cols == 0 || ! par->name) {
++ printk(KERN_ERR "LCD: wrong lcd parameters\n");
++ return (-EINVAL);
++ }
++ if (driver->validate_driver) {
++ if ((ret = driver->validate_driver()) < 0) {
++ printk(KERN_ERR "LCD: validate_driver failed\n");
++ return (-EINVAL);
++ } else if (ret > 0) {
++ set_bit(CAN_DO_COLOR, &p->struct_flags);
++ p->defcolor = 0x07;
++ p->ulcolor = 0x0f;
++ p->halfcolor = 0x08;
++ }
++ }
++ default_attr(p);
++ update_attr(p);
++ p->frame_size = frame_rows*frame_cols;
++ if (par->vs_rows < frame_rows)
++ par->vs_rows = frame_rows;
++ if (par->vs_cols < frame_cols)
++ par->vs_cols = frame_cols;
++ p->fb_size = par->vs_rows*par->vs_cols;
++
++ ret = sizeof(short)*p->fb_size;
++ ret += sizeof(short)*p->frame_size;
++ ret += FLIP_BUF_SIZE;
++ ret += (p->driver->charmap ? 256 : 512);
++ ret += par->cgram_chars*par->cgram_bytes;
++ if ((p->fb = (unsigned short *)vmalloc(ret)) == NULL) {
++ printk(KERN_ERR "LCD: memory allocation failed (vmalloc)\n");
++ return (-ENOMEM);
++ }
++ __memset_short(p->fb, p->erase_char, p->fb_size+p->frame_size);
++
++ p->display = p->fb+p->fb_size;
++ p->flip_buf = (unsigned char *)(p->display+p->frame_size);
++
++ if (! p->driver->charmap) {
++ set_bit(NULL_CHARMAP, &p->struct_flags);
++ p->driver->charmap = p->flip_buf+FLIP_BUF_SIZE;
++ for (ret = 0; ret < 256; ++ret)
++ p->driver->charmap[ret] = ret;
++ p->s_charmap = p->driver->charmap+256;
++ } else
++ p->s_charmap = p->flip_buf+FLIP_BUF_SIZE;
++ memset(p->s_charmap, 0, 256);
++
++ if (par->cgram_chars*par->cgram_bytes) {
++ p->cgram_buffer = p->s_charmap+256;
++ memset(p->cgram_buffer, 0, par->cgram_chars*par->cgram_bytes);
++ } else
++ p->cgram_buffer = NULL;
++
++ p->frame_base = 0;
++ p->row = p->col = 0;
++ p->top = 0;
++ p->bot = par->vs_rows;
++ SET_INIT_LEVEL(p, ++init_level);
++
++ case 1:
++ /* Initialize the communication port */
++ if ((ret = driver->init_port())) {
++ printk(KERN_ERR "LCD: failure while initializing the communication port\n");
++ return (ret);
++ }
++ SET_INIT_LEVEL(p, ++init_level);
++
++ case 2:
++ /* Initialize LCD display */
++ if (driver->init_display && (ret = driver->init_display())) {
++ printk(KERN_ERR "LCD: failure while initializing the display\n");
++ return (ret);
++ }
++
++#ifdef USE_PROC
++ /* Create entries in /proc/lcd/"driver" */
++ if (driver->driver_proc_root)
++ create_driver_proc_entries(p);
++#endif
++ SET_INIT_LEVEL(p, ++init_level);
++ }
++
++ return (0);
++}
++
++static int do_cleanup_driver(struct lcd_struct *p)
++{
++ int ret, init_level;
++ struct lcd_driver *driver = p->driver;
++
++ switch ((init_level = INIT_LEVEL(p))) {
++ case 3:
++#ifdef USE_PROC
++ if (driver->driver_proc_root)
++ remove_driver_proc_entries(p);
++#endif
++ if (driver->cleanup_display && (ret = driver->cleanup_display())) {
++ printk(KERN_ERR "LCD: failure while cleaning the display\n");
++ return (ret);
++ }
++ SET_INIT_LEVEL(p, --init_level);
++
++ case 2:
++ if ((ret = driver->cleanup_port())) {
++ printk(KERN_ERR "LCD: failure while cleaning the communication port\n");
++ return (ret);
++ }
++ SET_INIT_LEVEL(p, --init_level);
++
++ case 1:
++ if (test_bit(NULL_CHARMAP, &p->struct_flags)) {
++ p->driver->charmap = NULL;
++ clear_bit(NULL_CHARMAP, &p->struct_flags);
++ }
++ vfree(p->fb);
++ p->fb = NULL;
++ SET_INIT_LEVEL(p, --init_level);
++ }
++
++ return (0);
++}
++
++static int init_driver(struct lcd_struct *p)
++{
++ int ret;
++
++ if ((ret = do_init_driver(p))) {
++ do_cleanup_driver(p);
++ printk(KERN_ERR "LCD: init_driver failed\n");
++ }
++
++ return (ret);
++}
++
++static int cleanup_driver(struct lcd_struct *p)
++{
++ int ret;
++
++ if ((ret = do_cleanup_driver(p))) {
++ do_init_driver(p);
++ printk(KERN_ERR "LCD: cleanup_driver failed\n");
++ }
++
++ return (ret);
++}
++
++
++
++
++
++/********************************
++ * Init/Cleanup module routines *
++ ********************************/
++static int __init lcd_linux_init_module(void)
++{
++ int ret;
++
++ if (! minors || minors > 256)
++ minors = LCD_MINORS;
++
++ init_MUTEX(&drivers_sem);
++
++ if ((ret = register_chrdev(major, LCD_LINUX_STRING, &lcd_linux_fops)) < 0) {
++ printk(KERN_ERR "LCD: register_chrdev failed\n");
++ return (ret);
++ }
++ if (major == 0)
++ major = ret;
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 13)
++ if (IS_ERR((lcd_linux_class = class_create(THIS_MODULE, "lcd")))) {
++ ret = PTR_ERR(lcd_linux_class);
++ unregister_chrdev(major, LCD_LINUX_STRING);
++ return (ret);
++ }
++#endif
++
++#ifdef USE_PROC
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26)
++ if ((lcd_proc_root = proc_mkdir("lcd", NULL)) == NULL)
++#else
++ if ((lcd_proc_root = proc_mkdir("lcd", &proc_root)) == NULL)
++#endif
++ printk(KERN_ERR "LCD: cannot create /proc/lcd/\n");
++ else if (create_proc_read_entry("drivers", 0, lcd_proc_root, proc_registered_drivers, NULL) == NULL)
++ printk(KERN_ERR "LCD: cannot create /proc/lcd/drivers\n");
++#endif
++
++ printk(KERN_INFO "LCD: --> LCD-Linux " LCD_LINUX_VERSION " <--\n");
++ printk(KERN_INFO "LCD: --> Mattia Jona-Lasinio <mjona@users.sourceforge.net> <--\n" );
++
++
++ return (0);
++}
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 0)
++static void __exit lcd_linux_cleanup_module(void)
++#else
++/* __exit is not defined in 2.2.x kernels */
++static void lcd_linux_cleanup_module(void)
++#endif
++{
++#ifdef USE_PROC
++ if (lcd_proc_root) {
++ remove_proc_entry("drivers", lcd_proc_root);
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26)
++ remove_proc_entry("lcd", NULL);
++#else
++ remove_proc_entry("lcd", &proc_root);
++#endif
++ }
++#endif
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 13)
++ class_destroy(lcd_linux_class);
++#endif
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23)
++ unregister_chrdev(major, LCD_LINUX_STRING);
++#else
++ if (unregister_chrdev(major, LCD_LINUX_STRING))
++ printk(KERN_ERR "LCD: unregister_chrdev failed\n");
++#endif
++}
++
++module_init(lcd_linux_init_module)
++module_exit(lcd_linux_cleanup_module)
+Index: linux-2.6.30.9/include/linux/hd44780.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.30.9/include/linux/hd44780.h 2009-11-24 02:01:42.000000000 +0100
+@@ -0,0 +1,46 @@
++/* hd44780.h
++ *
++ *
++ * LCD-Linux:
++ * Driver for HD44780 compatible displays connected to the parallel port.
++ *
++ * HD44780 header file.
++ *
++ * Copyright (C) 2004 - 2007 Mattia Jona-Lasinio (mjona@users.sourceforge.net)
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ *
++ */
++
++#ifndef HD44780_H
++#define HD44780_H
++
++#include <linux/lcd-linux.h>
++
++#define HD44780_VERSION LCD_LINUX_VERSION /* Version number */
++#define HD44780_STRING "hd44780"
++
++#define HD44780_MINOR 0 /* Minor number for the hd44780 driver */
++
++
++/* flags */
++#define HD44780_CHECK_BF 0x00000001 /* Do busy flag checking */
++#define HD44780_4BITS_BUS 0x00000002 /* Set the bus length to 4 bits */
++#define HD44780_5X10_FONT 0x00000004 /* Use 5x10 dots fonts */
++
++/* IOCTLs */
++#define HD44780_READ_AC _IOR(LCD_MAJOR, 0x00, unsigned char *)
++
++#endif /* HD44780 included */
+Index: linux-2.6.30.9/include/linux/lcd-linux.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.30.9/include/linux/lcd-linux.h 2009-11-24 02:01:42.000000000 +0100
+@@ -0,0 +1,151 @@
++/* lcd-linux.h
++ *
++ *
++ * Software layer to drive LCD displays under Linux.
++ *
++ * External interface header file.
++ *
++ * Copyright (C) 2005 - 2007 Mattia Jona-Lasinio (mjona@users.sourceforge.net)
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ *
++ */
++
++#ifndef LCD_LINUX_H
++#define LCD_LINUX_H
++
++#ifndef LCD_LINUX_MAIN
++#warning
++#warning LCD-Linux is still in development stage and
++#warning aims at speed and optimization. For these
++#warning reasons there is no guarantee of backward
++#warning compatibility between different LCD-Linux
++#warning versions. Be sure to use the lcd-linux.h
++#warning file of the same version as the module.
++#warning "http://lcd-linux.sourceforge.net/"
++#warning
++#endif
++
++#define LCD_LINUX_VERSION "0.13.6" /* Version number */
++#define LCD_LINUX_STRING "lcd"
++
++#define LCD_MAJOR 120 /* Major number for this device
++ * Set this to 0 for dynamic allocation
++ */
++#define LCD_MINORS 8 /* Minors allocated for LCD-Linux*/
++
++#include <linux/types.h>
++
++#define str(s) #s
++#define string(s) str(s)
++
++struct lcd_parameters {
++ const char *name; /* Driver's name */
++ unsigned long flags; /* Flags (see documentation) */
++ unsigned short minor; /* Minor number of the char device */
++ unsigned short tabstop; /* Tab character length */
++ unsigned short num_cntr; /* Controllers to drive */
++ unsigned short cntr_rows; /* Rows per controller */
++ unsigned short cntr_cols; /* Display columns */
++ unsigned short vs_rows; /* Virtual screen rows */
++ unsigned short vs_cols; /* Virtual screen columns */
++ unsigned short cgram_chars; /* Number of user definable characters */
++ unsigned short cgram_bytes; /* Number of bytes required to define a
++ * user definable character */
++ unsigned char cgram_char0; /* Ascii of first user definable character */
++};
++
++/* IOCTLs */
++#include <asm/ioctl.h>
++#define LCDL_SET_PARAM _IOW(LCD_MAJOR, 0x80, struct lcd_parameters *)
++#define LCDL_GET_PARAM _IOR(LCD_MAJOR, 0x81, struct lcd_parameters *)
++#define LCDL_CHARSUBST _IOW(LCD_MAJOR, 0x82, unsigned char *)
++#define LCDL_RAW_MODE _IOW(LCD_MAJOR, 0x83, unsigned int)
++#define LCDL_RESET_CHARMAP _IO(LCD_MAJOR, 0x84)
++#define LCDL_SAVE_CHARMAP _IO(LCD_MAJOR, 0x85)
++#define LCDL_RESTORE_CHARMAP _IO(LCD_MAJOR, 0x86)
++#define LCDL_SWAP_CHARMAP _IO(LCD_MAJOR, 0x87)
++#define LCDL_CLEAR_DISP _IO(LCD_MAJOR, 0x88)
++#define LCDL_SET_CGRAM_CHAR _IOW(LCD_MAJOR, 0x89, unsigned char *)
++#define LCDL_GET_CGRAM_CHAR _IOR(LCD_MAJOR, 0x8a, unsigned char *)
++#define LCDL_SET_CHARMAP _IOW(LCD_MAJOR, 0x8b, unsigned char *)
++#define LCDL_GET_CHARMAP _IOR(LCD_MAJOR, 0x8c, unsigned char *)
++#define LCDL_MEMSET _IOW(LCD_MAJOR, 0x8d, unsigned int *)
++#define LCDL_MEMMOVE _IOW(LCD_MAJOR, 0x8e, unsigned int *)
++
++
++
++#ifdef __KERNEL__ /* The rest is for kernel only */
++
++#include <linux/kernel.h>
++#include <linux/module.h>
++
++
++struct lcd_driver {
++ void (*read_char)(unsigned int offset, unsigned short *data);
++ void (*read_cgram_char)(unsigned char index, unsigned char *pixmap);
++ void (*write_char)(unsigned int offset, unsigned short data);
++ void (*write_cgram_char)(unsigned char index, unsigned char *pixmap);
++ void (*clear_display)(void);
++ void (*address_mode)(int mode);
++ int (*validate_driver)(void);
++ int (*init_display)(void);
++ int (*cleanup_display)(void);
++ int (*init_port)(void);
++ int (*cleanup_port)(void);
++ int (*handle_custom_char)(unsigned int data);
++ int (*handle_custom_ioctl)(unsigned int cmd, unsigned long arg, unsigned int arg_in_userspace);
++
++ /* The character map to be used */
++ unsigned char *charmap;
++
++ /* The root where the driver can create its own proc files.
++ * Will be filled by the lcd-linux layer.
++ */
++ struct proc_dir_entry *driver_proc_root;
++
++ /* Set this field to 'driver_module_init' or call lcd_driver_setup
++ * just before registering the driver with lcd_register_driver.
++ */
++ struct module *driver_module;
++};
++
++#ifdef MODULE
++#define driver_module_init THIS_MODULE
++#else
++#define driver_module_init NULL
++#endif
++
++/* Always call lcd_driver_setup just before registering the driver
++ * with lcd_register_driver.
++ */
++static inline void lcd_driver_setup(struct lcd_driver *p)
++{
++ p->driver_module = driver_module_init;
++}
++
++/* External interface */
++struct lcd_struct;
++int lcd_register_driver(struct lcd_driver *drv, struct lcd_parameters *par);
++int lcd_unregister_driver(struct lcd_driver *drv, struct lcd_parameters *par);
++int lcd_open(unsigned short minor, struct lcd_struct **lcd);
++int lcd_close(struct lcd_struct **lcd);
++int lcd_ioctl(struct lcd_struct *lcd, unsigned int cmd, ...);
++ssize_t lcd_write(struct lcd_struct *lcd, const void *buffer, size_t length, loff_t offset, unsigned int);
++ssize_t lcd_read(struct lcd_struct *lcd, void *buffer, size_t length, loff_t offset, unsigned int);
++
++#endif /* __KERNEL__ */
++
++#endif /* External interface included */