diff options
Diffstat (limited to 'drivers/staging/panel/panel.c')
| -rw-r--r-- | drivers/staging/panel/panel.c | 1371 |
1 files changed, 751 insertions, 620 deletions
diff --git a/drivers/staging/panel/panel.c b/drivers/staging/panel/panel.c index 95c93e82cce..4e9229363c3 100644 --- a/drivers/staging/panel/panel.c +++ b/drivers/staging/panel/panel.c @@ -34,6 +34,8 @@ * */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/types.h> @@ -41,7 +43,6 @@ #include <linux/signal.h> #include <linux/sched.h> #include <linux/spinlock.h> -#include <linux/smp_lock.h> #include <linux/interrupt.h> #include <linux/miscdevice.h> #include <linux/slab.h> @@ -49,17 +50,16 @@ #include <linux/fcntl.h> #include <linux/init.h> #include <linux/delay.h> +#include <linux/kernel.h> #include <linux/ctype.h> #include <linux/parport.h> -#include <linux/version.h> #include <linux/list.h> #include <linux/notifier.h> #include <linux/reboot.h> #include <generated/utsrelease.h> #include <linux/io.h> -#include <asm/uaccess.h> -#include <asm/system.h> +#include <linux/uaccess.h> #define LCD_MINOR 156 #define KEYPAD_MINOR 185 @@ -69,11 +69,16 @@ #define LCD_MAXBYTES 256 /* max burst write */ #define KEYPAD_BUFFER 64 -#define INPUT_POLL_TIME (HZ/50) /* poll the keyboard this every second */ -#define KEYPAD_REP_START (10) /* a key starts to repeat after this times INPUT_POLL_TIME */ -#define KEYPAD_REP_DELAY (2) /* a key repeats this times INPUT_POLL_TIME */ -#define FLASH_LIGHT_TEMPO (200) /* keep the light on this times INPUT_POLL_TIME for each flash */ +/* poll the keyboard this every second */ +#define INPUT_POLL_TIME (HZ/50) +/* a key starts to repeat after this times INPUT_POLL_TIME */ +#define KEYPAD_REP_START (10) +/* a key repeats this times INPUT_POLL_TIME */ +#define KEYPAD_REP_DELAY (2) + +/* keep the light on this times INPUT_POLL_TIME for each flash */ +#define FLASH_LIGHT_TEMPO (200) /* converts an r_str() input to an active high, bits string : 000BAOSE */ #define PNL_PINPUT(a) ((((unsigned char)(a)) ^ 0x7F) >> 3) @@ -85,7 +90,8 @@ #define PNL_PERRORP 0x08 /* direct input, active low */ #define PNL_PBIDIR 0x20 /* bi-directional ports */ -#define PNL_PINTEN 0x10 /* high to read data in or-ed with data out */ +/* high to read data in or-ed with data out */ +#define PNL_PINTEN 0x10 #define PNL_PSELECP 0x08 /* inverted output, active low */ #define PNL_PINITP 0x04 /* direct output, active low */ #define PNL_PAUTOLF 0x02 /* inverted output, active low */ @@ -124,19 +130,21 @@ #define LCD_FLAG_N 0x0040 /* 2-rows mode */ #define LCD_FLAG_L 0x0080 /* backlight enabled */ -#define LCD_ESCAPE_LEN 24 /* 24 chars max for an LCD escape command */ +#define LCD_ESCAPE_LEN 24 /* max chars for LCD escape command */ #define LCD_ESCAPE_CHAR 27 /* use char 27 for escape command */ /* macros to simplify use of the parallel port */ #define r_ctr(x) (parport_read_control((x)->port)) #define r_dtr(x) (parport_read_data((x)->port)) #define r_str(x) (parport_read_status((x)->port)) -#define w_ctr(x, y) do { parport_write_control((x)->port, (y)); } while (0) -#define w_dtr(x, y) do { parport_write_data((x)->port, (y)); } while (0) +#define w_ctr(x, y) (parport_write_control((x)->port, (y))) +#define w_dtr(x, y) (parport_write_data((x)->port, (y))) /* this defines which bits are to be used and which ones to be ignored */ -static __u8 scan_mask_o; /* logical or of the output bits involved in the scan matrix */ -static __u8 scan_mask_i; /* logical or of the input bits involved in the scan matrix */ +/* logical or of the output bits involved in the scan matrix */ +static __u8 scan_mask_o; +/* logical or of the input bits involved in the scan matrix */ +static __u8 scan_mask_i; typedef __u64 pmask_t; @@ -162,14 +170,14 @@ struct logical_input { __u8 rise_timer, fall_timer, high_timer; union { - struct { /* this structure is valid when type == INPUT_TYPE_STD */ - void (*press_fct) (int); - void (*release_fct) (int); + struct { /* valid when type == INPUT_TYPE_STD */ + void (*press_fct)(int); + void (*release_fct)(int); int press_data; int release_data; } std; - struct { /* this structure is valid when type == INPUT_TYPE_KBD */ - /* strings can be full-length (ie. non null-terminated) */ + struct { /* valid when type == INPUT_TYPE_KBD */ + /* strings can be non null-terminated */ char press_str[sizeof(void *) + sizeof(int)]; char repeat_str[sizeof(void *) + sizeof(int)]; char release_str[sizeof(void *) + sizeof(int)]; @@ -177,7 +185,7 @@ struct logical_input { } u; }; -LIST_HEAD(logical_inputs); /* list of all defined logical inputs */ +static LIST_HEAD(logical_inputs); /* list of all defined logical inputs */ /* physical contacts history * Physical contacts are a 45 bits string of 9 groups of 5 bits each. @@ -189,11 +197,17 @@ LIST_HEAD(logical_inputs); /* list of all defined logical inputs */ * 0000000000000000000BAPSEBAPSEBAPSEBAPSEBAPSEBAPSEBAPSEBAPSEBAPSE * <-----unused------><gnd><d07><d06><d05><d04><d03><d02><d01><d00> */ -static pmask_t phys_read; /* what has just been read from the I/O ports */ -static pmask_t phys_read_prev; /* previous phys_read */ -static pmask_t phys_curr; /* stabilized phys_read (phys_read|phys_read_prev) */ -static pmask_t phys_prev; /* previous phys_curr */ -static char inputs_stable; /* 0 means that at least one logical signal needs be computed */ + +/* what has just been read from the I/O ports */ +static pmask_t phys_read; +/* previous phys_read */ +static pmask_t phys_read_prev; +/* stabilized phys_read (phys_read|phys_read_prev) */ +static pmask_t phys_curr; +/* previous phys_curr */ +static pmask_t phys_prev; +/* 0 means that at least one logical signal needs be computed */ +static char inputs_stable; /* these variables are specific to the keypad */ static char keypad_buffer[KEYPAD_BUFFER]; @@ -203,11 +217,17 @@ static char keypressed; static wait_queue_head_t keypad_read_wait; /* lcd-specific variables */ -static unsigned long int lcd_flags; /* contains the LCD config state */ -static unsigned long int lcd_addr_x; /* contains the LCD X offset */ -static unsigned long int lcd_addr_y; /* contains the LCD Y offset */ -static char lcd_escape[LCD_ESCAPE_LEN + 1]; /* current escape sequence, 0 terminated */ -static int lcd_escape_len = -1; /* not in escape state. >=0 = escape cmd len */ + +/* contains the LCD config state */ +static unsigned long int lcd_flags; +/* contains the LCD X offset */ +static unsigned long int lcd_addr_x; +/* contains the LCD Y offset */ +static unsigned long int lcd_addr_y; +/* current escape sequence, 0 terminated */ +static char lcd_escape[LCD_ESCAPE_LEN + 1]; +/* not in escape state. >=0 = escape cmd len */ +static int lcd_escape_len = -1; /* * Bit masks to convert LCD signals to parallel port outputs. @@ -397,9 +417,9 @@ static char lcd_must_clear; static char lcd_left_shift; static char init_in_progress; -static void (*lcd_write_cmd) (int); -static void (*lcd_write_data) (int); -static void (*lcd_clear_fast) (void); +static void (*lcd_write_cmd)(int); +static void (*lcd_write_data)(int); +static void (*lcd_clear_fast)(void); static DEFINE_SPINLOCK(pprt_lock); static struct timer_list scan_timer; @@ -441,8 +461,8 @@ MODULE_PARM_DESC(lcd_type, static int lcd_proto = -1; module_param(lcd_proto, int, 0000); -MODULE_PARM_DESC(lcd_proto, "LCD communication: 0=parallel (//), 1=serial," - "2=TI LCD Interface"); +MODULE_PARM_DESC(lcd_proto, + "LCD communication: 0=parallel (//), 1=serial, 2=TI LCD Interface"); static int lcd_charset = -1; module_param(lcd_charset, int, 0000); @@ -456,7 +476,8 @@ MODULE_PARM_DESC(keypad_type, static int profile = DEFAULT_PROFILE; module_param(profile, int, 0000); MODULE_PARM_DESC(profile, - "1=16x2 old kp; 2=serial 16x2, new kp; 3=16x2 hantronix; 4=16x2 nexcom; default=40x2, old kp"); + "1=16x2 old kp; 2=serial 16x2, new kp; 3=16x2 hantronix; " + "4=16x2 nexcom; default=40x2, old kp"); /* * These are the parallel port pins the LCD control signals are connected to. @@ -497,10 +518,10 @@ module_param(lcd_cl_pin, int, 0000); MODULE_PARM_DESC(lcd_cl_pin, "# of the // port pin connected to serial LCD 'SCL' signal, with polarity (-17..17)"); -static unsigned char *lcd_char_conv; +static const unsigned char *lcd_char_conv; /* for some LCD drivers (ks0074) we need a charset conversion table. */ -static unsigned char lcd_char_conv_ks0074[256] = { +static const unsigned char lcd_char_conv_ks0074[256] = { /* 0|8 1|9 2|A 3|B 4|C 5|D 6|E 7|F */ /* 0x00 */ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x08 */ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, @@ -536,7 +557,7 @@ static unsigned char lcd_char_conv_ks0074[256] = { /* 0xF8 */ 0xac, 0xa6, 0xea, 0xef, 0x7e, 0xeb, 0xb2, 0x79, }; -char old_keypad_profile[][4][9] = { +static const char old_keypad_profile[][4][9] = { {"S0", "Left\n", "Left\n", ""}, {"S1", "Down\n", "Down\n", ""}, {"S2", "Up\n", "Up\n", ""}, @@ -547,7 +568,7 @@ char old_keypad_profile[][4][9] = { }; /* signals, press, repeat, release */ -char new_keypad_profile[][4][9] = { +static const char new_keypad_profile[][4][9] = { {"S0", "Left\n", "Left\n", ""}, {"S1", "Down\n", "Down\n", ""}, {"S2", "Up\n", "Up\n", ""}, @@ -560,7 +581,7 @@ char new_keypad_profile[][4][9] = { }; /* signals, press, repeat, release */ -char nexcom_keypad_profile[][4][9] = { +static const char nexcom_keypad_profile[][4][9] = { {"a-p-e-", "Down\n", "Down\n", ""}, {"a-p-E-", "Ret\n", "Ret\n", ""}, {"a-P-E-", "Esc\n", "Esc\n", ""}, @@ -569,16 +590,16 @@ char nexcom_keypad_profile[][4][9] = { {"", "", "", ""} }; -static char (*keypad_profile)[4][9] = old_keypad_profile; +static const char (*keypad_profile)[4][9] = old_keypad_profile; /* FIXME: this should be converted to a bit array containing signals states */ static struct { - unsigned char e; /* parallel LCD E (data latch on falling edge) */ - unsigned char rs; /* parallel LCD RS (0 = cmd, 1 = data) */ - unsigned char rw; /* parallel LCD R/W (0 = W, 1 = R) */ - unsigned char bl; /* parallel LCD backlight (0 = off, 1 = on) */ - unsigned char cl; /* serial LCD clock (latch on rising edge) */ - unsigned char da; /* serial LCD data */ + unsigned char e; /* parallel LCD E (data latch on falling edge) */ + unsigned char rs; /* parallel LCD RS (0 = cmd, 1 = data) */ + unsigned char rw; /* parallel LCD R/W (0 = W, 1 = R) */ + unsigned char bl; /* parallel LCD backlight (0 = off, 1 = on) */ + unsigned char cl; /* serial LCD clock (latch on rising edge) */ + unsigned char da; /* serial LCD data */ } bits; static void init_scan_timer(void); @@ -639,12 +660,16 @@ static void panel_set_bits(void) * out(dport, in(dport) & d_val[2] | d_val[signal_state]) * out(cport, in(cport) & c_val[2] | c_val[signal_state]) */ -void pin_to_bits(int pin, unsigned char *d_val, unsigned char *c_val) +static void pin_to_bits(int pin, unsigned char *d_val, unsigned char *c_val) { int d_bit, c_bit, inv; - d_val[0] = c_val[0] = d_val[1] = c_val[1] = 0; - d_val[2] = c_val[2] = 0xFF; + d_val[0] = 0; + c_val[0] = 0; + d_val[1] = 0; + c_val[1] = 0; + d_val[2] = 0xFF; + c_val[2] = 0xFF; if (pin == 0) return; @@ -653,7 +678,8 @@ void pin_to_bits(int pin, unsigned char *d_val, unsigned char *c_val) if (inv) pin = -pin; - d_bit = c_bit = 0; + d_bit = 0; + c_bit = 0; switch (pin) { case PIN_STROBE: /* strobe, inverted */ @@ -667,7 +693,7 @@ void pin_to_bits(int pin, unsigned char *d_val, unsigned char *c_val) c_bit = PNL_PAUTOLF; inv = !inv; break; - case PIN_INITP: /* init, direct */ + case PIN_INITP: /* init, direct */ c_bit = PNL_PINITP; break; case PIN_SELECP: /* select_in, inverted */ @@ -690,32 +716,31 @@ void pin_to_bits(int pin, unsigned char *d_val, unsigned char *c_val) /* sleeps that many milliseconds with a reschedule */ static void long_sleep(int ms) { - - if (in_interrupt()) + if (in_interrupt()) { mdelay(ms); - else { + } else { current->state = TASK_INTERRUPTIBLE; schedule_timeout((ms * HZ + 999) / 1000); } } -/* send a serial byte to the LCD panel. The caller is responsible for locking if needed. */ +/* send a serial byte to the LCD panel. The caller is responsible for locking + if needed. */ static void lcd_send_serial(int byte) { int bit; /* the data bit is set on D0, and the clock on STROBE. - * LCD reads D0 on STROBE's rising edge. - */ + * LCD reads D0 on STROBE's rising edge. */ for (bit = 0; bit < 8; bit++) { bits.cl = BIT_CLR; /* CLK low */ panel_set_bits(); bits.da = byte & 1; panel_set_bits(); - udelay(2); /* maintain the data during 2 us before CLK up */ + udelay(2); /* maintain the data during 2 us before CLK up */ bits.cl = BIT_SET; /* CLK high */ panel_set_bits(); - udelay(1); /* maintain the strobe during 1 us */ + udelay(1); /* maintain the strobe during 1 us */ byte >>= 1; } } @@ -726,104 +751,105 @@ static void lcd_backlight(int on) if (lcd_bl_pin == PIN_NONE) return; - /* The backlight is activated by seting the AUTOFEED line to +5V */ - spin_lock(&pprt_lock); + /* The backlight is activated by setting the AUTOFEED line to +5V */ + spin_lock_irq(&pprt_lock); bits.bl = on; panel_set_bits(); - spin_unlock(&pprt_lock); + spin_unlock_irq(&pprt_lock); } /* send a command to the LCD panel in serial mode */ static void lcd_write_cmd_s(int cmd) { - spin_lock(&pprt_lock); + spin_lock_irq(&pprt_lock); lcd_send_serial(0x1F); /* R/W=W, RS=0 */ lcd_send_serial(cmd & 0x0F); lcd_send_serial((cmd >> 4) & 0x0F); udelay(40); /* the shortest command takes at least 40 us */ - spin_unlock(&pprt_lock); + spin_unlock_irq(&pprt_lock); } /* send data to the LCD panel in serial mode */ static void lcd_write_data_s(int data) { - spin_lock(&pprt_lock); + spin_lock_irq(&pprt_lock); lcd_send_serial(0x5F); /* R/W=W, RS=1 */ lcd_send_serial(data & 0x0F); lcd_send_serial((data >> 4) & 0x0F); udelay(40); /* the shortest data takes at least 40 us */ - spin_unlock(&pprt_lock); + spin_unlock_irq(&pprt_lock); } /* send a command to the LCD panel in 8 bits parallel mode */ static void lcd_write_cmd_p8(int cmd) { - spin_lock(&pprt_lock); + spin_lock_irq(&pprt_lock); /* present the data to the data port */ w_dtr(pprt, cmd); - udelay(20); /* maintain the data during 20 us before the strobe */ + udelay(20); /* maintain the data during 20 us before the strobe */ bits.e = BIT_SET; bits.rs = BIT_CLR; bits.rw = BIT_CLR; set_ctrl_bits(); - udelay(40); /* maintain the strobe during 40 us */ + udelay(40); /* maintain the strobe during 40 us */ bits.e = BIT_CLR; set_ctrl_bits(); - udelay(120); /* the shortest command takes at least 120 us */ - spin_unlock(&pprt_lock); + udelay(120); /* the shortest command takes at least 120 us */ + spin_unlock_irq(&pprt_lock); } /* send data to the LCD panel in 8 bits parallel mode */ static void lcd_write_data_p8(int data) { - spin_lock(&pprt_lock); + spin_lock_irq(&pprt_lock); /* present the data to the data port */ w_dtr(pprt, data); - udelay(20); /* maintain the data during 20 us before the strobe */ + udelay(20); /* maintain the data during 20 us before the strobe */ bits.e = BIT_SET; bits.rs = BIT_SET; bits.rw = BIT_CLR; set_ctrl_bits(); - udelay(40); /* maintain the strobe during 40 us */ + udelay(40); /* maintain the strobe during 40 us */ bits.e = BIT_CLR; set_ctrl_bits(); - udelay(45); /* the shortest data takes at least 45 us */ - spin_unlock(&pprt_lock); + udelay(45); /* the shortest data takes at least 45 us */ + spin_unlock_irq(&pprt_lock); } /* send a command to the TI LCD panel */ static void lcd_write_cmd_tilcd(int cmd) { - spin_lock(&pprt_lock); + spin_lock_irq(&pprt_lock); /* present the data to the control port */ w_ctr(pprt, cmd); udelay(60); - spin_unlock(&pprt_lock); + spin_unlock_irq(&pprt_lock); } /* send data to the TI LCD panel */ static void lcd_write_data_tilcd(int data) { - spin_lock(&pprt_lock); + spin_lock_irq(&pprt_lock); /* present the data to the data port */ w_dtr(pprt, data); udelay(60); - spin_unlock(&pprt_lock); + spin_unlock_irq(&pprt_lock); } static void lcd_gotoxy(void) { lcd_write_cmd(0x80 /* set DDRAM address */ | (lcd_addr_y ? lcd_hwidth : 0) - /* we force the cursor to stay at the end of the line if it wants to go farther */ + /* we force the cursor to stay at the end of the + line if it wants to go farther */ | ((lcd_addr_x < lcd_bwidth) ? lcd_addr_x & (lcd_hwidth - 1) : lcd_bwidth - 1)); } @@ -845,19 +871,22 @@ static void lcd_print(char c) static void lcd_clear_fast_s(void) { int pos; - lcd_addr_x = lcd_addr_y = 0; + + lcd_addr_x = 0; + lcd_addr_y = 0; lcd_gotoxy(); - spin_lock(&pprt_lock); + spin_lock_irq(&pprt_lock); for (pos = 0; pos < lcd_height * lcd_hwidth; pos++) { lcd_send_serial(0x5F); /* R/W=W, RS=1 */ lcd_send_serial(' ' & 0x0F); lcd_send_serial((' ' >> 4) & 0x0F); udelay(40); /* the shortest data takes at least 40 us */ } - spin_unlock(&pprt_lock); + spin_unlock_irq(&pprt_lock); - lcd_addr_x = lcd_addr_y = 0; + lcd_addr_x = 0; + lcd_addr_y = 0; lcd_gotoxy(); } @@ -865,30 +894,37 @@ static void lcd_clear_fast_s(void) static void lcd_clear_fast_p8(void) { int pos; - lcd_addr_x = lcd_addr_y = 0; + + lcd_addr_x = 0; + lcd_addr_y = 0; lcd_gotoxy(); - spin_lock(&pprt_lock); + spin_lock_irq(&pprt_lock); for (pos = 0; pos < lcd_height * lcd_hwidth; pos++) { /* present the data to the data port */ w_dtr(pprt, ' '); - udelay(20); /* maintain the data during 20 us before the strobe */ + + /* maintain the data during 20 us before the strobe */ + udelay(20); bits.e = BIT_SET; bits.rs = BIT_SET; bits.rw = BIT_CLR; set_ctrl_bits(); - udelay(40); /* maintain the strobe during 40 us */ + /* maintain the strobe during 40 us */ + udelay(40); bits.e = BIT_CLR; set_ctrl_bits(); - udelay(45); /* the shortest data takes at least 45 us */ + /* the shortest data takes at least 45 us */ + udelay(45); } - spin_unlock(&pprt_lock); + spin_unlock_irq(&pprt_lock); - lcd_addr_x = lcd_addr_y = 0; + lcd_addr_x = 0; + lcd_addr_y = 0; lcd_gotoxy(); } @@ -896,19 +932,22 @@ static void lcd_clear_fast_p8(void) static void lcd_clear_fast_tilcd(void) { int pos; - lcd_addr_x = lcd_addr_y = 0; + + lcd_addr_x = 0; + lcd_addr_y = 0; lcd_gotoxy(); - spin_lock(&pprt_lock); + spin_lock_irq(&pprt_lock); for (pos = 0; pos < lcd_height * lcd_hwidth; pos++) { /* present the data to the data port */ w_dtr(pprt, ' '); udelay(60); } - spin_unlock(&pprt_lock); + spin_unlock_irq(&pprt_lock); - lcd_addr_x = lcd_addr_y = 0; + lcd_addr_x = 0; + lcd_addr_y = 0; lcd_gotoxy(); } @@ -916,14 +955,14 @@ static void lcd_clear_fast_tilcd(void) static void lcd_clear_display(void) { lcd_write_cmd(0x01); /* clear display */ - lcd_addr_x = lcd_addr_y = 0; + lcd_addr_x = 0; + lcd_addr_y = 0; /* we must wait a few milliseconds (15) */ long_sleep(15); } static void lcd_init_display(void) { - lcd_flags = ((lcd_height > 1) ? LCD_FLAG_N : 0) | LCD_FLAG_D | LCD_FLAG_C | LCD_FLAG_B; @@ -955,7 +994,8 @@ static void lcd_init_display(void) long_sleep(10); - lcd_write_cmd(0x06); /* entry mode set : increment, cursor shifting */ + /* entry mode set : increment, cursor shifting */ + lcd_write_cmd(0x06); lcd_clear_display(); } @@ -967,317 +1007,339 @@ static void lcd_init_display(void) * */ -static ssize_t lcd_write(struct file *file, - const char *buf, size_t count, loff_t *ppos) +static inline int handle_lcd_special_code(void) { + /* LCD special codes */ - const char *tmp = buf; - char c; + int processed = 0; - for (; count-- > 0; (ppos ? (*ppos)++ : 0), ++tmp) { - if (!in_interrupt() && (((count + 1) & 0x1f) == 0)) - schedule(); /* let's be a little nice with other processes that need some CPU */ + char *esc = lcd_escape + 2; + int oldflags = lcd_flags; - if (ppos == NULL && file == NULL) - c = *tmp; /* let's not use get_user() from the kernel ! */ - else if (get_user(c, tmp)) - return -EFAULT; + /* check for display mode flags */ + switch (*esc) { + case 'D': /* Display ON */ + lcd_flags |= LCD_FLAG_D; + processed = 1; + break; + case 'd': /* Display OFF */ + lcd_flags &= ~LCD_FLAG_D; + processed = 1; + break; + case 'C': /* Cursor ON */ + lcd_flags |= LCD_FLAG_C; + processed = 1; + break; + case 'c': /* Cursor OFF */ + lcd_flags &= ~LCD_FLAG_C; + processed = 1; + break; + case 'B': /* Blink ON */ + lcd_flags |= LCD_FLAG_B; + processed = 1; + break; + case 'b': /* Blink OFF */ + lcd_flags &= ~LCD_FLAG_B; + processed = 1; + break; + case '+': /* Back light ON */ + lcd_flags |= LCD_FLAG_L; + processed = 1; + break; + case '-': /* Back light OFF */ + lcd_flags &= ~LCD_FLAG_L; + processed = 1; + break; + case '*': + /* flash back light using the keypad timer */ + if (scan_timer.function != NULL) { + if (light_tempo == 0 && ((lcd_flags & LCD_FLAG_L) == 0)) + lcd_backlight(1); + light_tempo = FLASH_LIGHT_TEMPO; + } + processed = 1; + break; + case 'f': /* Small Font */ + lcd_flags &= ~LCD_FLAG_F; + processed = 1; + break; + case 'F': /* Large Font */ + lcd_flags |= LCD_FLAG_F; + processed = 1; + break; + case 'n': /* One Line */ + lcd_flags &= ~LCD_FLAG_N; + processed = 1; + break; + case 'N': /* Two Lines */ + lcd_flags |= LCD_FLAG_N; + break; + case 'l': /* Shift Cursor Left */ + if (lcd_addr_x > 0) { + /* back one char if not at end of line */ + if (lcd_addr_x < lcd_bwidth) + lcd_write_cmd(0x10); + lcd_addr_x--; + } + processed = 1; + break; + case 'r': /* shift cursor right */ + if (lcd_addr_x < lcd_width) { + /* allow the cursor to pass the end of the line */ + if (lcd_addr_x < + (lcd_bwidth - 1)) + lcd_write_cmd(0x14); + lcd_addr_x++; + } + processed = 1; + break; + case 'L': /* shift display left */ + lcd_left_shift++; + lcd_write_cmd(0x18); + processed = 1; + break; + case 'R': /* shift display right */ + lcd_left_shift--; + lcd_write_cmd(0x1C); + processed = 1; + break; + case 'k': { /* kill end of line */ + int x; - /* first, we'll test if we're in escape mode */ - if ((c != '\n') && lcd_escape_len >= 0) { /* yes, let's add this char to the buffer */ - lcd_escape[lcd_escape_len++] = c; - lcd_escape[lcd_escape_len] = 0; - } else { - lcd_escape_len = -1; /* aborts any previous escape sequence */ + for (x = lcd_addr_x; x < lcd_bwidth; x++) + lcd_write_data(' '); - switch (c) { - case LCD_ESCAPE_CHAR: /* start of an escape sequence */ - lcd_escape_len = 0; - lcd_escape[lcd_escape_len] = 0; - break; - case '\b': /* go back one char and clear it */ - if (lcd_addr_x > 0) { - if (lcd_addr_x < lcd_bwidth) /* check if we're not at the end of the line */ - lcd_write_cmd(0x10); /* back one char */ - lcd_addr_x--; - } - lcd_write_data(' '); /* replace with a space */ - lcd_write_cmd(0x10); /* back one char again */ - break; - case '\014': /* quickly clear the display */ - lcd_clear_fast(); - break; - case '\n': /* flush the remainder of the current line and go to the - beginning of the next line */ - for (; lcd_addr_x < lcd_bwidth; lcd_addr_x++) - lcd_write_data(' '); - lcd_addr_x = 0; - lcd_addr_y = (lcd_addr_y + 1) % lcd_height; - lcd_gotoxy(); - break; - case '\r': /* go to the beginning of the same line */ - lcd_addr_x = 0; - lcd_gotoxy(); - break; - case '\t': /* print a space instead of the tab */ - lcd_print(' '); - break; - default: /* simply print this char */ - lcd_print(c); - break; - } + /* restore cursor position */ + lcd_gotoxy(); + processed = 1; + break; + } + case 'I': /* reinitialize display */ + lcd_init_display(); + lcd_left_shift = 0; + processed = 1; + break; + case 'G': { + /* Generator : LGcxxxxx...xx; must have <c> between '0' + * and '7', representing the numerical ASCII code of the + * redefined character, and <xx...xx> a sequence of 16 + * hex digits representing 8 bytes for each character. + * Most LCDs will only use 5 lower bits of the 7 first + * bytes. + */ + + unsigned char cgbytes[8]; + unsigned char cgaddr; + int cgoffset; + int shift; + char value; + int addr; + + if (strchr(esc, ';') == NULL) + break; + + esc++; + + cgaddr = *(esc++) - '0'; + if (cgaddr > 7) { + processed = 1; + break; } - /* now we'll see if we're in an escape mode and if the current - escape sequence can be understood. - */ - if (lcd_escape_len >= 2) { /* minimal length for an escape command */ - int processed = 0; /* 1 means the command has been processed */ - - if (!strcmp(lcd_escape, "[2J")) { /* Clear the display */ - lcd_clear_fast(); /* clear display */ - processed = 1; - } else if (!strcmp(lcd_escape, "[H")) { /* Cursor to home */ - lcd_addr_x = lcd_addr_y = 0; - lcd_gotoxy(); - processed = 1; + cgoffset = 0; + shift = 0; + value = 0; + while (*esc && cgoffset < 8) { + shift ^= 4; + if (*esc >= '0' && *esc <= '9') { + value |= (*esc - '0') << shift; + } else if (*esc >= 'A' && *esc <= 'Z') { + value |= (*esc - 'A' + 10) << shift; + } else if (*esc >= 'a' && *esc <= 'z') { + value |= (*esc - 'a' + 10) << shift; + } else { + esc++; + continue; } - /* codes starting with ^[[L */ - else if ((lcd_escape_len >= 3) && - (lcd_escape[0] == '[') && (lcd_escape[1] == 'L')) { /* LCD special codes */ - - char *esc = lcd_escape + 2; - int oldflags = lcd_flags; - - /* check for display mode flags */ - switch (*esc) { - case 'D': /* Display ON */ - lcd_flags |= LCD_FLAG_D; - processed = 1; - break; - case 'd': /* Display OFF */ - lcd_flags &= ~LCD_FLAG_D; - processed = 1; - break; - case 'C': /* Cursor ON */ - lcd_flags |= LCD_FLAG_C; - processed = 1; - break; - case 'c': /* Cursor OFF */ - lcd_flags &= ~LCD_FLAG_C; - processed = 1; - break; - case 'B': /* Blink ON */ - lcd_flags |= LCD_FLAG_B; - processed = 1; - break; - case 'b': /* Blink OFF */ - lcd_flags &= ~LCD_FLAG_B; - processed = 1; - break; - case '+': /* Back light ON */ - lcd_flags |= LCD_FLAG_L; - processed = 1; - break; - case '-': /* Back light OFF */ - lcd_flags &= ~LCD_FLAG_L; - processed = 1; - break; - case '*': /* flash back light using the keypad timer */ - if (scan_timer.function != NULL) { - if (light_tempo == 0 - && ((lcd_flags & LCD_FLAG_L) - == 0)) - lcd_backlight(1); - light_tempo = FLASH_LIGHT_TEMPO; - } - processed = 1; - break; - case 'f': /* Small Font */ - lcd_flags &= ~LCD_FLAG_F; - processed = 1; - break; - case 'F': /* Large Font */ - lcd_flags |= LCD_FLAG_F; - processed = 1; - break; - case 'n': /* One Line */ - lcd_flags &= ~LCD_FLAG_N; - processed = 1; - break; - case 'N': /* Two Lines */ - lcd_flags |= LCD_FLAG_N; - break; - case 'l': /* Shift Cursor Left */ - if (lcd_addr_x > 0) { - if (lcd_addr_x < lcd_bwidth) - lcd_write_cmd(0x10); /* back one char if not at end of line */ - lcd_addr_x--; - } - processed = 1; - break; + if (shift == 0) { + cgbytes[cgoffset++] = value; + value = 0; + } - case 'r': /* shift cursor right */ - if (lcd_addr_x < lcd_width) { - if (lcd_addr_x < (lcd_bwidth - 1)) - lcd_write_cmd(0x14); /* allow the cursor to pass the end of the line */ - lcd_addr_x++; - } - processed = 1; - break; + esc++; + } - case 'L': /* shift display left */ - lcd_left_shift++; - lcd_write_cmd(0x18); - processed = 1; - break; + lcd_write_cmd(0x40 | (cgaddr * 8)); + for (addr = 0; addr < cgoffset; addr++) + lcd_write_data(cgbytes[addr]); - case 'R': /* shift display right */ - lcd_left_shift--; - lcd_write_cmd(0x1C); - processed = 1; - break; + /* ensures that we stop writing to CGRAM */ + lcd_gotoxy(); + processed = 1; + break; + } + case 'x': /* gotoxy : LxXXX[yYYY]; */ + case 'y': /* gotoxy : LyYYY[xXXX]; */ + if (strchr(esc, ';') == NULL) + break; - case 'k':{ /* kill end of line */ - int x; - for (x = lcd_addr_x; x < lcd_bwidth; x++) - lcd_write_data(' '); - lcd_gotoxy(); /* restore cursor position */ - processed = 1; - break; - } - case 'I': /* reinitialize display */ - lcd_init_display(); - lcd_left_shift = 0; - processed = 1; + while (*esc) { + if (*esc == 'x') { + esc++; + if (kstrtoul(esc, 10, &lcd_addr_x) < 0) break; - - case 'G': /* Generator : LGcxxxxx...xx; */ { - /* must have <c> between '0' and '7', representing the numerical - * ASCII code of the redefined character, and <xx...xx> a sequence - * of 16 hex digits representing 8 bytes for each character. Most - * LCDs will only use 5 lower bits of the 7 first bytes. - */ - - unsigned char cgbytes[8]; - unsigned char cgaddr; - int cgoffset; - int shift; - char value; - int addr; - - if (strchr(esc, ';') == NULL) - break; - - esc++; - - cgaddr = *(esc++) - '0'; - if (cgaddr > 7) { - processed = 1; - break; - } - - cgoffset = 0; - shift = 0; - value = 0; - while (*esc && cgoffset < 8) { - shift ^= 4; - if (*esc >= '0' && *esc <= '9') - value |= (*esc - '0') << shift; - else if (*esc >= 'A' && *esc <= 'Z') - value |= (*esc - 'A' + 10) << shift; - else if (*esc >= 'a' && *esc <= 'z') - value |= (*esc - 'a' + 10) << shift; - else { - esc++; - continue; - } - - if (shift == 0) { - cgbytes[cgoffset++] = value; - value = 0; - } - - esc++; - } - - lcd_write_cmd(0x40 | (cgaddr * 8)); - for (addr = 0; addr < cgoffset; addr++) - lcd_write_data(cgbytes[addr]); - - lcd_gotoxy(); /* ensures that we stop writing to CGRAM */ - processed = 1; - break; - } - case 'x': /* gotoxy : LxXXX[yYYY]; */ - case 'y': /* gotoxy : LyYYY[xXXX]; */ - if (strchr(esc, ';') == NULL) - break; - - while (*esc) { - if (*esc == 'x') { - esc++; - lcd_addr_x = 0; - while (isdigit(*esc)) { - lcd_addr_x = - lcd_addr_x * - 10 + (*esc - - '0'); - esc++; - } - } else if (*esc == 'y') { - esc++; - lcd_addr_y = 0; - while (isdigit(*esc)) { - lcd_addr_y = - lcd_addr_y * - 10 + (*esc - - '0'); - esc++; - } - } else - break; - } - - lcd_gotoxy(); - processed = 1; + } else if (*esc == 'y') { + esc++; + if (kstrtoul(esc, 10, &lcd_addr_y) < 0) break; - } /* end of switch */ - - /* Check wether one flag was changed */ - if (oldflags != lcd_flags) { - /* check wether one of B,C,D flags was changed */ - if ((oldflags ^ lcd_flags) & - (LCD_FLAG_B | LCD_FLAG_C | LCD_FLAG_D)) - /* set display mode */ - lcd_write_cmd(0x08 | - ((lcd_flags & LCD_FLAG_D) ? 4 : 0) | - ((lcd_flags & LCD_FLAG_C) ? 2 : 0) | - ((lcd_flags & LCD_FLAG_B) ? 1 : 0)); - /* check wether one of F,N flags was changed */ - else if ((oldflags ^ lcd_flags) & - (LCD_FLAG_F | LCD_FLAG_N)) - lcd_write_cmd(0x30 | - ((lcd_flags & LCD_FLAG_F) ? 4 : 0) | - ((lcd_flags & LCD_FLAG_N) ? 8 : 0)); - /* check wether L flag was changed */ - else if ((oldflags ^ lcd_flags) & - (LCD_FLAG_L)) { - if (lcd_flags & (LCD_FLAG_L)) - lcd_backlight(1); - else if (light_tempo == 0) /* switch off the light only when the tempo lighting is gone */ - lcd_backlight(0); - } - } + } else { + break; } + } - /* LCD special escape codes */ - /* flush the escape sequence if it's been processed or if it is - getting too long. */ - if (processed || (lcd_escape_len >= LCD_ESCAPE_LEN)) - lcd_escape_len = -1; - } /* escape codes */ + lcd_gotoxy(); + processed = 1; + break; + } + + /* Check whether one flag was changed */ + if (oldflags != lcd_flags) { + /* check whether one of B,C,D flags were changed */ + if ((oldflags ^ lcd_flags) & + (LCD_FLAG_B | LCD_FLAG_C | LCD_FLAG_D)) + /* set display mode */ + lcd_write_cmd(0x08 + | ((lcd_flags & LCD_FLAG_D) ? 4 : 0) + | ((lcd_flags & LCD_FLAG_C) ? 2 : 0) + | ((lcd_flags & LCD_FLAG_B) ? 1 : 0)); + /* check whether one of F,N flags was changed */ + else if ((oldflags ^ lcd_flags) & (LCD_FLAG_F | LCD_FLAG_N)) + lcd_write_cmd(0x30 + | ((lcd_flags & LCD_FLAG_F) ? 4 : 0) + | ((lcd_flags & LCD_FLAG_N) ? 8 : 0)); + /* check whether L flag was changed */ + else if ((oldflags ^ lcd_flags) & (LCD_FLAG_L)) { + if (lcd_flags & (LCD_FLAG_L)) + lcd_backlight(1); + else if (light_tempo == 0) + /* switch off the light only when the tempo + lighting is gone */ + lcd_backlight(0); + } + } + + return processed; +} + +static void lcd_write_char(char c) +{ + /* first, we'll test if we're in escape mode */ + if ((c != '\n') && lcd_escape_len >= 0) { + /* yes, let's add this char to the buffer */ + lcd_escape[lcd_escape_len++] = c; + lcd_escape[lcd_escape_len] = 0; + } else { + /* aborts any previous escape sequence */ + lcd_escape_len = -1; + + switch (c) { + case LCD_ESCAPE_CHAR: + /* start of an escape sequence */ + lcd_escape_len = 0; + lcd_escape[lcd_escape_len] = 0; + break; + case '\b': + /* go back one char and clear it */ + if (lcd_addr_x > 0) { + /* check if we're not at the + end of the line */ + if (lcd_addr_x < lcd_bwidth) + /* back one char */ + lcd_write_cmd(0x10); + lcd_addr_x--; + } + /* replace with a space */ + lcd_write_data(' '); + /* back one char again */ + lcd_write_cmd(0x10); + break; + case '\014': + /* quickly clear the display */ + lcd_clear_fast(); + break; + case '\n': + /* flush the remainder of the current line and + go to the beginning of the next line */ + for (; lcd_addr_x < lcd_bwidth; lcd_addr_x++) + lcd_write_data(' '); + lcd_addr_x = 0; + lcd_addr_y = (lcd_addr_y + 1) % lcd_height; + lcd_gotoxy(); + break; + case '\r': + /* go to the beginning of the same line */ + lcd_addr_x = 0; + lcd_gotoxy(); + break; + case '\t': + /* print a space instead of the tab */ + lcd_print(' '); + break; + default: + /* simply print this char */ + lcd_print(c); + break; + } + } + + /* now we'll see if we're in an escape mode and if the current + escape sequence can be understood. */ + if (lcd_escape_len >= 2) { + int processed = 0; + + if (!strcmp(lcd_escape, "[2J")) { + /* clear the display */ + lcd_clear_fast(); + processed = 1; + } else if (!strcmp(lcd_escape, "[H")) { + /* cursor to home */ + lcd_addr_x = 0; + lcd_addr_y = 0; + lcd_gotoxy(); + processed = 1; + } + /* codes starting with ^[[L */ + else if ((lcd_escape_len >= 3) && + (lcd_escape[0] == '[') && + (lcd_escape[1] == 'L')) { + processed = handle_lcd_special_code(); + } + + /* LCD special escape codes */ + /* flush the escape sequence if it's been processed + or if it is getting too long. */ + if (processed || (lcd_escape_len >= LCD_ESCAPE_LEN)) + lcd_escape_len = -1; + } /* escape codes */ +} + +static ssize_t lcd_write(struct file *file, + const char __user *buf, size_t count, loff_t *ppos) +{ + const char __user *tmp = buf; + char c; + + for (; count-- > 0; (*ppos)++, tmp++) { + if (!in_interrupt() && (((count + 1) & 0x1f) == 0)) + /* let's be a little nice with other processes + that need some CPU */ + schedule(); + + if (get_user(c, tmp)) + return -EFAULT; + + lcd_write_char(c); } return tmp - buf; @@ -1296,7 +1358,7 @@ static int lcd_open(struct inode *inode, struct file *file) lcd_must_clear = 0; } lcd_open_cnt++; - return 0; + return nonseekable_open(inode, file); } static int lcd_release(struct inode *inode, struct file *file) @@ -1305,10 +1367,11 @@ static int lcd_release(struct inode *inode, struct file *file) return 0; } -static struct file_operations lcd_fops = { +static const struct file_operations lcd_fops = { .write = lcd_write, .open = lcd_open, .release = lcd_release, + .llseek = no_llseek, }; static struct miscdevice lcd_dev = { @@ -1318,17 +1381,29 @@ static struct miscdevice lcd_dev = { }; /* public function usable from the kernel for any purpose */ -void panel_lcd_print(char *s) +static void panel_lcd_print(const char *s) { - if (lcd_enabled && lcd_initialized) - lcd_write(NULL, s, strlen(s), NULL); + const char *tmp = s; + int count = strlen(s); + + if (lcd_enabled && lcd_initialized) { + for (; count-- > 0; tmp++) { + if (!in_interrupt() && (((count + 1) & 0x1f) == 0)) + /* let's be a little nice with other processes + that need some CPU */ + schedule(); + + lcd_write_char(*tmp); + } + } } /* initialize the LCD driver */ -void lcd_init(void) +static void lcd_init(void) { switch (lcd_type) { - case LCD_TYPE_OLD: /* parallel mode, 8 bits */ + case LCD_TYPE_OLD: + /* parallel mode, 8 bits */ if (lcd_proto < 0) lcd_proto = LCD_PROTO_PARALLEL; if (lcd_charset < 0) @@ -1347,7 +1422,8 @@ void lcd_init(void) if (lcd_height < 0) lcd_height = 2; break; - case LCD_TYPE_KS0074: /* serial mode, ks0074 */ + case LCD_TYPE_KS0074: + /* serial mode, ks0074 */ if (lcd_proto < 0) lcd_proto = LCD_PROTO_SERIAL; if (lcd_charset < 0) @@ -1368,7 +1444,8 @@ void lcd_init(void) if (lcd_height < 0) lcd_height = 2; break; - case LCD_TYPE_NEXCOM: /* parallel mode, 8 bits, generic */ + case LCD_TYPE_NEXCOM: + /* parallel mode, 8 bits, generic */ if (lcd_proto < 0) lcd_proto = LCD_PROTO_PARALLEL; if (lcd_charset < 0) @@ -1389,14 +1466,16 @@ void lcd_init(void) if (lcd_height < 0) lcd_height = 2; break; - case LCD_TYPE_CUSTOM: /* customer-defined */ + case LCD_TYPE_CUSTOM: + /* customer-defined */ if (lcd_proto < 0) lcd_proto = DEFAULT_LCD_PROTO; if (lcd_charset < 0) lcd_charset = DEFAULT_LCD_CHARSET; /* default geometry will be set later */ break; - case LCD_TYPE_HANTRONIX: /* parallel mode, 8 bits, hantronix-like */ + case LCD_TYPE_HANTRONIX: + /* parallel mode, 8 bits, hantronix-like */ default: if (lcd_proto < 0) lcd_proto = LCD_PROTO_PARALLEL; @@ -1497,8 +1576,7 @@ void lcd_init(void) /* before this line, we must NOT send anything to the display. * Since lcd_init_display() needs to write data, we have to - * enable mark the LCD initialized just before. - */ + * enable mark the LCD initialized just before. */ lcd_initialized = 1; lcd_init_display(); @@ -1511,8 +1589,10 @@ void lcd_init(void) panel_lcd_print("\x1b[Lc\x1b[Lb\x1b[L*Linux-" UTS_RELEASE "\nPanel-" PANEL_VERSION); #endif - lcd_addr_x = lcd_addr_y = 0; - lcd_must_clear = 1; /* clear the display on the next device opening */ + lcd_addr_x = 0; + lcd_addr_y = 0; + /* clear the display on the next device opening */ + lcd_must_clear = 1; lcd_gotoxy(); } @@ -1521,22 +1601,22 @@ void lcd_init(void) */ static ssize_t keypad_read(struct file *file, - char *buf, size_t count, loff_t *ppos) + char __user *buf, size_t count, loff_t *ppos) { - unsigned i = *ppos; - char *tmp = buf; + char __user *tmp = buf; if (keypad_buflen == 0) { if (file->f_flags & O_NONBLOCK) return -EAGAIN; - interruptible_sleep_on(&keypad_read_wait); - if (signal_pending(current)) + if (wait_event_interruptible(keypad_read_wait, + keypad_buflen != 0)) return -EINTR; } - for (; count-- > 0 && (keypad_buflen > 0); ++i, ++tmp, --keypad_buflen) { + for (; count-- > 0 && (keypad_buflen > 0); + ++i, ++tmp, --keypad_buflen) { put_user(keypad_buffer[keypad_start], tmp); keypad_start = (keypad_start + 1) % KEYPAD_BUFFER; } @@ -1547,7 +1627,6 @@ static ssize_t keypad_read(struct file *file, static int keypad_open(struct inode *inode, struct file *file) { - if (keypad_open_cnt) return -EBUSY; /* open only once at a time */ @@ -1565,10 +1644,11 @@ static int keypad_release(struct inode *inode, struct file *file) return 0; } -static struct file_operations keypad_fops = { +static const struct file_operations keypad_fops = { .read = keypad_read, /* read */ .open = keypad_open, /* open */ .release = keypad_release, /* close */ + .llseek = default_llseek, }; static struct miscdevice keypad_dev = { @@ -1577,7 +1657,7 @@ static struct miscdevice keypad_dev = { &keypad_fops }; -static void keypad_send_key(char *string, int max_len) +static void keypad_send_key(const char *string, int max_len) { if (init_in_progress) return; @@ -1592,14 +1672,15 @@ static void keypad_send_key(char *string, int max_len) } } -/* this function scans all the bits involving at least one logical signal, and puts the - * results in the bitfield "phys_read" (one bit per established contact), and sets - * "phys_read_prev" to "phys_read". +/* this function scans all the bits involving at least one logical signal, + * and puts the results in the bitfield "phys_read" (one bit per established + * contact), and sets "phys_read_prev" to "phys_read". * - * Note: to debounce input signals, we will only consider as switched a signal which is - * stable across 2 measures. Signals which are different between two reads will be kept - * as they previously were in their logical form (phys_prev). A signal which has just - * switched will have a 1 in (phys_read ^ phys_read_prev). + * Note: to debounce input signals, we will only consider as switched a signal + * which is stable across 2 measures. Signals which are different between two + * reads will be kept as they previously were in their logical form (phys_prev). + * A signal which has just switched will have a 1 in + * (phys_read ^ phys_read_prev). */ static void phys_scan_contacts(void) { @@ -1612,21 +1693,30 @@ static void phys_scan_contacts(void) phys_read_prev = phys_read; phys_read = 0; /* flush all signals */ - oldval = r_dtr(pprt) | scan_mask_o; /* keep track of old value, with all outputs disabled */ - w_dtr(pprt, oldval & ~scan_mask_o); /* activate all keyboard outputs (active low) */ - bitmask = PNL_PINPUT(r_str(pprt)) & scan_mask_i; /* will have a 1 for each bit set to gnd */ - w_dtr(pprt, oldval); /* disable all matrix signals */ + /* keep track of old value, with all outputs disabled */ + oldval = r_dtr(pprt) | scan_mask_o; + /* activate all keyboard outputs (active low) */ + w_dtr(pprt, oldval & ~scan_mask_o); + + /* will have a 1 for each bit set to gnd */ + bitmask = PNL_PINPUT(r_str(pprt)) & scan_mask_i; + /* disable all matrix signals */ + w_dtr(pprt, oldval); /* now that all outputs are cleared, the only active input bits are * directly connected to the ground */ - gndmask = PNL_PINPUT(r_str(pprt)) & scan_mask_i; /* 1 for each grounded input */ - phys_read |= (pmask_t) gndmask << 40; /* grounded inputs are signals 40-44 */ + /* 1 for each grounded input */ + gndmask = PNL_PINPUT(r_str(pprt)) & scan_mask_i; + + /* grounded inputs are signals 40-44 */ + phys_read |= (pmask_t) gndmask << 40; if (bitmask != gndmask) { - /* since clearing the outputs changed some inputs, we know that some - * input signals are currently tied to some outputs. So we'll scan them. + /* since clearing the outputs changed some inputs, we know + * that some input signals are currently tied to some outputs. + * So we'll scan them. */ for (bit = 0; bit < 8; bit++) { bitval = 1 << bit; @@ -1640,24 +1730,146 @@ static void phys_scan_contacts(void) } w_dtr(pprt, oldval); /* disable all outputs */ } - /* this is easy: use old bits when they are flapping, use new ones when stable */ - phys_curr = - (phys_prev & (phys_read ^ phys_read_prev)) | (phys_read & - ~(phys_read ^ - phys_read_prev)); + /* this is easy: use old bits when they are flapping, + * use new ones when stable */ + phys_curr = (phys_prev & (phys_read ^ phys_read_prev)) | + (phys_read & ~(phys_read ^ phys_read_prev)); } -static void panel_process_inputs(void) +static inline int input_state_high(struct logical_input *input) { - struct list_head *item; - struct logical_input *input; +#if 0 + /* FIXME: + * this is an invalid test. It tries to catch + * transitions from single-key to multiple-key, but + * doesn't take into account the contacts polarity. + * The only solution to the problem is to parse keys + * from the most complex to the simplest combinations, + * and mark them as 'caught' once a combination + * matches, then unmatch it for all other ones. + */ + + /* try to catch dangerous transitions cases : + * someone adds a bit, so this signal was a false + * positive resulting from a transition. We should + * invalidate the signal immediately and not call the + * release function. + * eg: 0 -(press A)-> A -(press B)-> AB : don't match A's release. + */ + if (((phys_prev & input->mask) == input->value) && + ((phys_curr & input->mask) > input->value)) { + input->state = INPUT_ST_LOW; /* invalidate */ + return 1; + } +#endif + + if ((phys_curr & input->mask) == input->value) { + if ((input->type == INPUT_TYPE_STD) && + (input->high_timer == 0)) { + input->high_timer++; + if (input->u.std.press_fct != NULL) + input->u.std.press_fct(input->u.std.press_data); + } else if (input->type == INPUT_TYPE_KBD) { + /* will turn on the light */ + keypressed = 1; + + if (input->high_timer == 0) { + char *press_str = input->u.kbd.press_str; + if (press_str[0]) { + int s = sizeof(input->u.kbd.press_str); + + keypad_send_key(press_str, s); + } + } + + if (input->u.kbd.repeat_str[0]) { + char *repeat_str = input->u.kbd.repeat_str; + + if (input->high_timer >= KEYPAD_REP_START) { + int s = sizeof(input->u.kbd.repeat_str); + + input->high_timer -= KEYPAD_REP_DELAY; + keypad_send_key(repeat_str, s); + } + /* we will need to come back here soon */ + inputs_stable = 0; + } + + if (input->high_timer < 255) + input->high_timer++; + } + return 1; + } else { + /* else signal falling down. Let's fall through. */ + input->state = INPUT_ST_FALLING; + input->fall_timer = 0; + } + return 0; +} + +static inline void input_state_falling(struct logical_input *input) +{ #if 0 - printk(KERN_DEBUG - "entering panel_process_inputs with pp=%016Lx & pc=%016Lx\n", - phys_prev, phys_curr); + /* FIXME !!! same comment as in input_state_high */ + if (((phys_prev & input->mask) == input->value) && + ((phys_curr & input->mask) > input->value)) { + input->state = INPUT_ST_LOW; /* invalidate */ + return; + } #endif + if ((phys_curr & input->mask) == input->value) { + if (input->type == INPUT_TYPE_KBD) { + /* will turn on the light */ + keypressed = 1; + + if (input->u.kbd.repeat_str[0]) { + char *repeat_str = input->u.kbd.repeat_str; + + if (input->high_timer >= KEYPAD_REP_START) { + int s = sizeof(input->u.kbd.repeat_str); + + input->high_timer -= KEYPAD_REP_DELAY; + keypad_send_key(repeat_str, s); + } + /* we will need to come back here soon */ + inputs_stable = 0; + } + + if (input->high_timer < 255) + input->high_timer++; + } + input->state = INPUT_ST_HIGH; + } else if (input->fall_timer >= input->fall_time) { + /* call release event */ + if (input->type == INPUT_TYPE_STD) { + void (*release_fct)(int) = input->u.std.release_fct; + + if (release_fct != NULL) + release_fct(input->u.std.release_data); + } else if (input->type == INPUT_TYPE_KBD) { + char *release_str = input->u.kbd.release_str; + + if (release_str[0]) { + int s = sizeof(input->u.kbd.release_str); + + keypad_send_key(release_str, s); + } + } + + input->state = INPUT_ST_LOW; + } else { + input->fall_timer++; + inputs_stable = 0; + } +} + +static void panel_process_inputs(void) +{ + struct list_head *item; + struct logical_input *input; + keypressed = 0; inputs_stable = 1; list_for_each(item, &logical_inputs) { @@ -1667,10 +1879,12 @@ static void panel_process_inputs(void) case INPUT_ST_LOW: if ((phys_curr & input->mask) != input->value) break; - /* if all needed ones were already set previously, this means that - * this logical signal has been activated by the releasing of - * another combined signal, so we don't want to match. - * eg: AB -(release B)-> A -(release A)-> 0 : don't match A. + /* if all needed ones were already set previously, + * this means that this logical signal has been + * activated by the releasing of another combined + * signal, so we don't want to match. + * eg: AB -(release B)-> A -(release A)-> 0 : + * don't match A. */ if ((phys_prev & input->mask) == input->value) break; @@ -1691,122 +1905,11 @@ static void panel_process_inputs(void) input->state = INPUT_ST_HIGH; /* no break here, fall through */ case INPUT_ST_HIGH: -#if 0 - /* FIXME: - * this is an invalid test. It tries to catch transitions from single-key - * to multiple-key, but doesn't take into account the contacts polarity. - * The only solution to the problem is to parse keys from the most complex - * to the simplest combinations, and mark them as 'caught' once a combination - * matches, then unmatch it for all other ones. - */ - - /* try to catch dangerous transitions cases : - * someone adds a bit, so this signal was a false - * positive resulting from a transition. We should invalidate - * the signal immediately and not call the release function. - * eg: 0 -(press A)-> A -(press B)-> AB : don't match A's release. - */ - if (((phys_prev & input->mask) == input->value) - && ((phys_curr & input->mask) > input->value)) { - input->state = INPUT_ST_LOW; /* invalidate */ + if (input_state_high(input)) break; - } -#endif - - if ((phys_curr & input->mask) == input->value) { - if ((input->type == INPUT_TYPE_STD) - && (input->high_timer == 0)) { - input->high_timer++; - if (input->u.std.press_fct != NULL) - input->u.std.press_fct(input->u. - std. - press_data); - } else if (input->type == INPUT_TYPE_KBD) { - keypressed = 1; /* will turn on the light */ - - if (input->high_timer == 0) { - if (input->u.kbd.press_str[0]) - keypad_send_key(input-> - u.kbd. - press_str, - sizeof - (input-> - u.kbd. - press_str)); - } - - if (input->u.kbd.repeat_str[0]) { - if (input->high_timer >= - KEYPAD_REP_START) { - input->high_timer -= - KEYPAD_REP_DELAY; - keypad_send_key(input-> - u.kbd. - repeat_str, - sizeof - (input-> - u.kbd. - repeat_str)); - } - inputs_stable = 0; /* we will need to come back here soon */ - } - - if (input->high_timer < 255) - input->high_timer++; - } - break; - } else { - /* else signal falling down. Let's fall through. */ - input->state = INPUT_ST_FALLING; - input->fall_timer = 0; - } /* no break here, fall through */ case INPUT_ST_FALLING: -#if 0 - /* FIXME !!! same comment as above */ - if (((phys_prev & input->mask) == input->value) - && ((phys_curr & input->mask) > input->value)) { - input->state = INPUT_ST_LOW; /* invalidate */ - break; - } -#endif - - if ((phys_curr & input->mask) == input->value) { - if (input->type == INPUT_TYPE_KBD) { - keypressed = 1; /* will turn on the light */ - - if (input->u.kbd.repeat_str[0]) { - if (input->high_timer >= KEYPAD_REP_START) - input->high_timer -= KEYPAD_REP_DELAY; - keypad_send_key(input->u.kbd.repeat_str, - sizeof(input->u.kbd.repeat_str)); - inputs_stable = 0; /* we will need to come back here soon */ - } - - if (input->high_timer < 255) - input->high_timer++; - } - input->state = INPUT_ST_HIGH; - break; - } else if (input->fall_timer >= input->fall_time) { - /* call release event */ - if (input->type == INPUT_TYPE_STD) { - if (input->u.std.release_fct != NULL) - input->u.std.release_fct(input->u.std.release_data); - - } else if (input->type == INPUT_TYPE_KBD) { - if (input->u.kbd.release_str[0]) - keypad_send_key(input->u.kbd.release_str, - sizeof(input->u.kbd.release_str)); - } - - input->state = INPUT_ST_LOW; - break; - } else { - input->fall_timer++; - inputs_stable = 0; - break; - } + input_state_falling(input); } } } @@ -1814,9 +1917,11 @@ static void panel_process_inputs(void) static void panel_scan_timer(void) { if (keypad_enabled && keypad_initialized) { - if (spin_trylock(&pprt_lock)) { + if (spin_trylock_irq(&pprt_lock)) { phys_scan_contacts(); - spin_unlock(&pprt_lock); /* no need for the parport anymore */ + + /* no need for the parport anymore */ + spin_unlock_irq(&pprt_lock); } if (!inputs_stable || phys_curr != phys_prev) @@ -1851,22 +1956,28 @@ static void init_scan_timer(void) } /* converts a name of the form "({BbAaPpSsEe}{01234567-})*" to a series of bits. - * if <omask> or <imask> are non-null, they will be or'ed with the bits corresponding - * to out and in bits respectively. + * if <omask> or <imask> are non-null, they will be or'ed with the bits + * corresponding to out and in bits respectively. * returns 1 if ok, 0 if error (in which case, nothing is written). */ -static int input_name2mask(char *name, pmask_t *mask, pmask_t *value, +static int input_name2mask(const char *name, pmask_t *mask, pmask_t *value, char *imask, char *omask) { static char sigtab[10] = "EeSsPpAaBb"; char im, om; pmask_t m, v; - om = im = m = v = 0ULL; + om = 0ULL; + im = 0ULL; + m = 0ULL; + v = 0ULL; while (*name) { int in, out, bit, neg; - for (in = 0; (in < sizeof(sigtab)) && (sigtab[in] != *name); in++) + + for (in = 0; (in < sizeof(sigtab)) && (sigtab[in] != *name); + in++) ; + if (in >= sizeof(sigtab)) return 0; /* input name not found */ neg = (in & 1); /* odd (lower) names are negated */ @@ -1877,10 +1988,11 @@ static int input_name2mask(char *name, pmask_t *mask, pmask_t *value, if (isdigit(*name)) { out = *name - '0'; om |= (1 << out); - } else if (*name == '-') + } else if (*name == '-') { out = 8; - else + } else { return 0; /* unknown bit name */ + } bit = (out * 5) + in; @@ -1902,30 +2014,27 @@ static int input_name2mask(char *name, pmask_t *mask, pmask_t *value, * strings <press>, <repeat>, <release> for these respective events. * Returns the pointer to the new key if ok, NULL if the key could not be bound. */ -static struct logical_input *panel_bind_key(char *name, char *press, - char *repeat, char *release) +static struct logical_input *panel_bind_key(const char *name, const char *press, + const char *repeat, + const char *release) { struct logical_input *key; - key = kmalloc(sizeof(struct logical_input), GFP_KERNEL); - if (!key) { - printk(KERN_ERR "panel: not enough memory\n"); + key = kzalloc(sizeof(*key), GFP_KERNEL); + if (!key) return NULL; - } - memset(key, 0, sizeof(struct logical_input)); + if (!input_name2mask(name, &key->mask, &key->value, &scan_mask_i, - &scan_mask_o)) + &scan_mask_o)) { + kfree(key); return NULL; + } key->type = INPUT_TYPE_KBD; key->state = INPUT_ST_LOW; key->rise_time = 1; key->fall_time = 1; -#if 0 - printk(KERN_DEBUG "bind: <%s> : m=%016Lx v=%016Lx\n", name, key->mask, - key->value); -#endif strncpy(key->u.kbd.press_str, press, sizeof(key->u.kbd.press_str)); strncpy(key->u.kbd.repeat_str, repeat, sizeof(key->u.kbd.repeat_str)); strncpy(key->u.kbd.release_str, release, @@ -1938,21 +2047,21 @@ static struct logical_input *panel_bind_key(char *name, char *press, /* tries to bind a callback function to the signal name <name>. The function * <press_fct> will be called with the <press_data> arg when the signal is * activated, and so on for <release_fct>/<release_data> - * Returns the pointer to the new signal if ok, NULL if the signal could not be bound. + * Returns the pointer to the new signal if ok, NULL if the signal could not + * be bound. */ static struct logical_input *panel_bind_callback(char *name, - void (*press_fct) (int), + void (*press_fct)(int), int press_data, - void (*release_fct) (int), + void (*release_fct)(int), int release_data) { struct logical_input *callback; - callback = kmalloc(sizeof(struct logical_input), GFP_KERNEL); - if (!callback) { - printk(KERN_ERR "panel: not enough memory\n"); + callback = kmalloc(sizeof(*callback), GFP_KERNEL); + if (!callback) return NULL; - } + memset(callback, 0, sizeof(struct logical_input)); if (!input_name2mask(name, &callback->mask, &callback->value, &scan_mask_i, &scan_mask_o)) @@ -1974,6 +2083,7 @@ static struct logical_input *panel_bind_callback(char *name, static void keypad_init(void) { int keynum; + init_waitqueue_head(&keypad_read_wait); keypad_buflen = 0; /* flushes any eventual noisy keystroke */ @@ -2029,34 +2139,49 @@ static void panel_attach(struct parport *port) return; if (pprt) { - printk(KERN_ERR - "panel_attach(): port->number=%d parport=%d, already registered !\n", - port->number, parport); + pr_err("%s: port->number=%d parport=%d, already registered!\n", + __func__, port->number, parport); return; } - pprt = parport_register_device(port, "panel", NULL, NULL, /* pf, kf */ + pprt = parport_register_device(port, "panel", NULL, NULL, /* pf, kf */ NULL, /*PARPORT_DEV_EXCL */ 0, (void *)&pprt); + if (pprt == NULL) { + pr_err("%s: port->number=%d parport=%d, parport_register_device() failed\n", + __func__, port->number, parport); + return; + } if (parport_claim(pprt)) { - printk(KERN_ERR - "Panel: could not claim access to parport%d. Aborting.\n", + pr_err("could not claim access to parport%d. Aborting.\n", parport); - return; + goto err_unreg_device; } - /* must init LCD first, just in case an IRQ from the keypad is generated at keypad init */ + /* must init LCD first, just in case an IRQ from the keypad is + * generated at keypad init + */ if (lcd_enabled) { lcd_init(); - misc_register(&lcd_dev); + if (misc_register(&lcd_dev)) + goto err_unreg_device; } if (keypad_enabled) { keypad_init(); - misc_register(&keypad_dev); + if (misc_register(&keypad_dev)) + goto err_lcd_unreg; } + return; + +err_lcd_unreg: + if (lcd_enabled) + misc_deregister(&lcd_dev); +err_unreg_device: + parport_unregister_device(pprt); + pprt = NULL; } static void panel_detach(struct parport *port) @@ -2065,9 +2190,8 @@ static void panel_detach(struct parport *port) return; if (!pprt) { - printk(KERN_ERR - "panel_detach(): port->number=%d parport=%d, nothing to unregister.\n", - port->number, parport); + pr_err("%s: port->number=%d parport=%d, nothing to unregister.\n", + __func__, port->number, parport); return; } @@ -2093,7 +2217,7 @@ static struct parport_driver panel_driver = { }; /* init function */ -int panel_init(void) +static int panel_init(void) { /* for backwards compatibility */ if (keypad_type < 0) @@ -2107,13 +2231,15 @@ int panel_init(void) /* take care of an eventual profile */ switch (profile) { - case PANEL_PROFILE_CUSTOM: /* custom profile */ + case PANEL_PROFILE_CUSTOM: + /* custom profile */ if (keypad_type < 0) keypad_type = DEFAULT_KEYPAD; if (lcd_type < 0) lcd_type = DEFAULT_LCD; break; - case PANEL_PROFILE_OLD: /* 8 bits, 2*16, old keypad */ + case PANEL_PROFILE_OLD: + /* 8 bits, 2*16, old keypad */ if (keypad_type < 0) keypad_type = KEYPAD_TYPE_OLD; if (lcd_type < 0) @@ -2123,25 +2249,29 @@ int panel_init(void) if (lcd_hwidth < 0) lcd_hwidth = 16; break; - case PANEL_PROFILE_NEW: /* serial, 2*16, new keypad */ + case PANEL_PROFILE_NEW: + /* serial, 2*16, new keypad */ if (keypad_type < 0) keypad_type = KEYPAD_TYPE_NEW; if (lcd_type < 0) lcd_type = LCD_TYPE_KS0074; break; - case PANEL_PROFILE_HANTRONIX: /* 8 bits, 2*16 hantronix-like, no keypad */ + case PANEL_PROFILE_HANTRONIX: + /* 8 bits, 2*16 hantronix-like, no keypad */ if (keypad_type < 0) keypad_type = KEYPAD_TYPE_NONE; if (lcd_type < 0) lcd_type = LCD_TYPE_HANTRONIX; break; - case PANEL_PROFILE_NEXCOM: /* generic 8 bits, 2*16, nexcom keypad, eg. Nexcom. */ + case PANEL_PROFILE_NEXCOM: + /* generic 8 bits, 2*16, nexcom keypad, eg. Nexcom. */ if (keypad_type < 0) keypad_type = KEYPAD_TYPE_NEXCOM; if (lcd_type < 0) lcd_type = LCD_TYPE_NEXCOM; break; - case PANEL_PROFILE_LARGE: /* 8 bits, 2*40, old keypad */ + case PANEL_PROFILE_LARGE: + /* 8 bits, 2*40, old keypad */ if (keypad_type < 0) keypad_type = KEYPAD_TYPE_OLD; if (lcd_type < 0) @@ -2171,8 +2301,7 @@ int panel_init(void) init_in_progress = 1; if (parport_register_driver(&panel_driver)) { - printk(KERN_ERR - "Panel: could not register with parport. Aborting.\n"); + pr_err("could not register with parport. Aborting.\n"); return -EIO; } @@ -2181,23 +2310,24 @@ int panel_init(void) if (pprt) { parport_release(pprt); parport_unregister_device(pprt); + pprt = NULL; } parport_unregister_driver(&panel_driver); - printk(KERN_ERR "Panel driver version " PANEL_VERSION - " disabled.\n"); + pr_err("driver version " PANEL_VERSION " disabled.\n"); return -ENODEV; } register_reboot_notifier(&panel_notifier); if (pprt) - printk(KERN_INFO "Panel driver version " PANEL_VERSION - " registered on parport%d (io=0x%lx).\n", parport, - pprt->port->base); + pr_info("driver version " PANEL_VERSION + " registered on parport%d (io=0x%lx).\n", parport, + pprt->port->base); else - printk(KERN_INFO "Panel driver version " PANEL_VERSION - " not yet registered\n"); - /* tells various subsystems about the fact that initialization is finished */ + pr_info("driver version " PANEL_VERSION + " not yet registered\n"); + /* tells various subsystems about the fact that initialization + is finished */ init_in_progress = 0; return 0; } @@ -2212,7 +2342,7 @@ static void __exit panel_cleanup_module(void) unregister_reboot_notifier(&panel_notifier); if (scan_timer.function != NULL) - del_timer(&scan_timer); + del_timer_sync(&scan_timer); if (pprt != NULL) { if (keypad_enabled) { @@ -2230,6 +2360,7 @@ static void __exit panel_cleanup_module(void) /* TODO: free all input signals */ parport_release(pprt); parport_unregister_device(pprt); + pprt = NULL; } parport_unregister_driver(&panel_driver); } |
