// SPDX-License-Identifier: GPL-2.0+ /* * Front panel driver for Linux * Copyright (C) 2000-2008, Willy Tarreau <w@1wt.eu> * Copyright (C) 2016-2017 Glider bvba * * This code drives an LCD module (/dev/lcd), and a keypad (/dev/keypad) * connected to a parallel printer port. * * The LCD module may either be an HD44780-like 8-bit parallel LCD, or a 1-bit * serial module compatible with Samsung's KS0074. The pins may be connected in * any combination, everything is programmable. * * The keypad consists in a matrix of push buttons connecting input pins to * data output pins or to the ground. The combinations have to be hard-coded * in the driver, though several profiles exist and adding new ones is easy. * * Several profiles are provided for commonly found LCD+keypad modules on the * market, such as those found in Nexcom's appliances. * * FIXME: * - the initialization/deinitialization process is very dirty and should * be rewritten. It may even be buggy. * * TODO: * - document 24 keys keyboard (3 rows of 8 cols, 32 diodes + 2 inputs) * - make the LCD a part of a virtual screen of Vx*Vy * - make the inputs list smp-safe * - change the keyboard to a double mapping : signals -> key_id -> values * so that applications can change values without knowing signals *
*/
/* 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)
/* converts an r_str() input to an active high, bits string : 000BAOSE */ #define PNL_PINPUT(a) ((((unsignedchar)(a)) ^ 0x7F) >> 3)
#define PNL_PBUSY 0x80 /* inverted input, active low */ #define PNL_PACK 0x40 /* direct input, active low */ #define PNL_POUTPA 0x20 /* direct input, active high */ #define PNL_PSELECD 0x10 /* direct input, active high */ #define PNL_PERRORP 0x08 /* direct input, active low */
#define PNL_PBIDIR 0x20 /* bi-directional ports */ /* 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 */ #define PNL_PSTROBE 0x01 /* inverted output */
/* 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) (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 */ /* 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;
union { struct { /* valid when type == INPUT_TYPE_STD */ void (*press_fct)(int); void (*release_fct)(int); int press_data; int release_data;
} std; struct { /* valid when type == INPUT_TYPE_KBD */ char press_str[sizeof(void *) + sizeof(int)] __nonstring; char repeat_str[sizeof(void *) + sizeof(int)] __nonstring; char release_str[sizeof(void *) + sizeof(int)] __nonstring;
} kbd;
} u;
};
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. * The 8 lower groups correspond to output bits 0 to 7, and the 9th group * corresponds to the ground. * Within each group, bits are stored in the same order as read on the port : * BAPSE (busy=4, ack=3, paper empty=2, select=1, error=0). * So, each __u64 is represented like this : * 0000000000000000000BAPSEBAPSEBAPSEBAPSEBAPSEBAPSEBAPSEBAPSEBAPSE * <-----unused------><gnd><d07><d06><d05><d04><d03><d02><d01><d00>
*/
/* what has just been read from the I/O ports */ static __u64 phys_read; /* previous phys_read */ static __u64 phys_read_prev; /* stabilized phys_read (phys_read|phys_read_prev) */ static __u64 phys_curr; /* previous phys_curr */ static __u64 phys_prev; /* 0 means that at least one logical signal needs be computed */ staticchar inputs_stable;
/* these variables are specific to the keypad */ staticstruct { bool enabled;
} keypad;
/* TODO: use union here? */ struct { int e; int rs; int rw; int cl; int da; int bl;
} pins;
struct charlcd *charlcd;
} lcd;
/* Needed only for init */ staticint selected_lcd_type = NOT_SET;
/* * Bit masks to convert LCD signals to parallel port outputs. * _d_ are values for data port, _c_ are for control port. * [0] = signal OFF, [1] = signal ON, [2] = mask
*/ #define BIT_CLR 0 #define BIT_SET 1 #define BIT_MSK 2 #define BIT_STATES 3 /* * one entry for each bit on the LCD
*/ #define LCD_BIT_E 0 #define LCD_BIT_RS 1 #define LCD_BIT_RW 2 #define LCD_BIT_BL 3 #define LCD_BIT_CL 4 #define LCD_BIT_DA 5 #define LCD_BITS 6
/* * each bit can be either connected to a DATA or CTRL port
*/ #define LCD_PORT_C 0 #define LCD_PORT_D 1 #define LCD_PORTS 2
/* * These are the parallel port pins the LCD control signals are connected to. * Set this to 0 if the signal is not used. Set it to its opposite value * (negative) if the signal is negated. -MAXINT is used to indicate that the * pin has not been explicitly specified. * * WARNING! no check will be performed about collisions with keypad !
*/
staticint lcd_e_pin = PIN_NOT_SET;
module_param(lcd_e_pin, int, 0000);
MODULE_PARM_DESC(lcd_e_pin, "# of the // port pin connected to LCD 'E' signal, with polarity (-17..17)");
staticint lcd_rs_pin = PIN_NOT_SET;
module_param(lcd_rs_pin, int, 0000);
MODULE_PARM_DESC(lcd_rs_pin, "# of the // port pin connected to LCD 'RS' signal, with polarity (-17..17)");
staticint lcd_rw_pin = PIN_NOT_SET;
module_param(lcd_rw_pin, int, 0000);
MODULE_PARM_DESC(lcd_rw_pin, "# of the // port pin connected to LCD 'RW' signal, with polarity (-17..17)");
staticint lcd_cl_pin = PIN_NOT_SET;
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)");
staticint lcd_da_pin = PIN_NOT_SET;
module_param(lcd_da_pin, int, 0000);
MODULE_PARM_DESC(lcd_da_pin, "# of the // port pin connected to serial LCD 'SDA' signal, with polarity (-17..17)");
staticint lcd_bl_pin = PIN_NOT_SET;
module_param(lcd_bl_pin, int, 0000);
MODULE_PARM_DESC(lcd_bl_pin, "# of the // port pin connected to LCD backlight, with polarity (-17..17)");
/* Deprecated module parameters - consider not using them anymore */
staticvoid lcd_get_bits(unsignedint port, int *val)
{ unsignedint bit, state;
for (bit = 0; bit < LCD_BITS; bit++) {
state = test_bit(bit, bits) ? BIT_SET : BIT_CLR;
*val &= lcd_bits[port][bit][BIT_MSK];
*val |= lcd_bits[port][bit][state];
}
}
/* sets data port bits according to current signals values */ staticint set_data_bits(void)
{ int val;
val = r_dtr(pprt);
lcd_get_bits(LCD_PORT_D, &val);
w_dtr(pprt, val); return val;
}
/* sets ctrl port bits according to current signals values */ staticint set_ctrl_bits(void)
{ int val;
val = r_ctr(pprt);
lcd_get_bits(LCD_PORT_C, &val);
w_ctr(pprt, val); return val;
}
/* sets ctrl & data port bits according to current signals values */ staticvoid panel_set_bits(void)
{
set_data_bits();
set_ctrl_bits();
}
/* * Converts a parallel port pin (from -25 to 25) to data and control ports * masks, and data and control port bits. The signal will be considered * unconnected if it's on pin 0 or an invalid pin (<-25 or >25). * * Result will be used this way : * out(dport, in(dport) & d_val[2] | d_val[signal_state]) * out(cport, in(cport) & c_val[2] | c_val[signal_state])
*/ staticvoid pin_to_bits(int pin, unsignedchar *d_val, unsignedchar *c_val)
{ int d_bit, c_bit, inv;
/* * send a serial byte to the LCD panel. The caller is responsible for locking * if needed.
*/ staticvoid 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.
*/ for (bit = 0; bit < 8; bit++) {
clear_bit(LCD_BIT_CL, bits); /* CLK low */
panel_set_bits(); if (byte & 1) {
set_bit(LCD_BIT_DA, bits);
} else {
clear_bit(LCD_BIT_DA, bits);
}
panel_set_bits();
udelay(2); /* maintain the data during 2 us before CLK up */
set_bit(LCD_BIT_CL, bits); /* CLK high */
panel_set_bits();
udelay(1); /* maintain the strobe during 1 us */
byte >>= 1;
}
}
/* turn the backlight on or off */ staticvoid lcd_backlight(struct charlcd *charlcd, enum charlcd_onoff on)
{ if (lcd.pins.bl == PIN_NONE) return;
/* The backlight is activated by setting the AUTOFEED line to +5V */
spin_lock_irq(&pprt_lock); if (on)
set_bit(LCD_BIT_BL, bits); else
clear_bit(LCD_BIT_BL, bits);
panel_set_bits();
spin_unlock_irq(&pprt_lock);
}
/* send a command to the LCD panel in serial mode */ staticvoid lcd_write_cmd_s(struct hd44780_common *hdc, int cmd)
{
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_irq(&pprt_lock);
}
/* send data to the LCD panel in serial mode */ staticvoid lcd_write_data_s(struct hd44780_common *hdc, int data)
{
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_irq(&pprt_lock);
}
/* send a command to the LCD panel in 8 bits parallel mode */ staticvoid lcd_write_cmd_p8(struct hd44780_common *hdc, int cmd)
{
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(40); /* maintain the strobe during 40 us */
clear_bit(LCD_BIT_E, bits);
set_ctrl_bits();
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 */ staticvoid lcd_write_data_p8(struct hd44780_common *hdc, int data)
{
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(40); /* maintain the strobe during 40 us */
clear_bit(LCD_BIT_E, bits);
set_ctrl_bits();
udelay(45); /* the shortest data takes at least 45 us */
spin_unlock_irq(&pprt_lock);
}
/* send a command to the TI LCD panel */ staticvoid lcd_write_cmd_tilcd(struct hd44780_common *hdc, int cmd)
{
spin_lock_irq(&pprt_lock); /* present the data to the control port */
w_ctr(pprt, cmd);
udelay(60);
spin_unlock_irq(&pprt_lock);
}
/* send data to the TI LCD panel */ staticvoid lcd_write_data_tilcd(struct hd44780_common *hdc, int data)
{
spin_lock_irq(&pprt_lock); /* present the data to the data port */
w_dtr(pprt, data);
udelay(60);
spin_unlock_irq(&pprt_lock);
}
/* Overwrite with module params set on loading */ if (lcd_height != NOT_SET)
charlcd->height = lcd_height; if (lcd_width != NOT_SET)
charlcd->width = lcd_width; if (lcd_bwidth != NOT_SET)
hdc->bwidth = lcd_bwidth; if (lcd_hwidth != NOT_SET)
hdc->hwidth = lcd_hwidth; if (lcd_charset != NOT_SET)
lcd.charset = lcd_charset; if (lcd_proto != NOT_SET)
lcd.proto = lcd_proto; if (lcd_e_pin != PIN_NOT_SET)
lcd.pins.e = lcd_e_pin; if (lcd_rs_pin != PIN_NOT_SET)
lcd.pins.rs = lcd_rs_pin; if (lcd_rw_pin != PIN_NOT_SET)
lcd.pins.rw = lcd_rw_pin; if (lcd_cl_pin != PIN_NOT_SET)
lcd.pins.cl = lcd_cl_pin; if (lcd_da_pin != PIN_NOT_SET)
lcd.pins.da = lcd_da_pin; if (lcd_bl_pin != PIN_NOT_SET)
lcd.pins.bl = lcd_bl_pin;
/* this is used to catch wrong and default values */ if (charlcd->width <= 0)
charlcd->width = DEFAULT_LCD_WIDTH; if (hdc->bwidth <= 0)
hdc->bwidth = DEFAULT_LCD_BWIDTH; if (hdc->hwidth <= 0)
hdc->hwidth = DEFAULT_LCD_HWIDTH; if (charlcd->height <= 0)
charlcd->height = DEFAULT_LCD_HEIGHT;
if (lcd.proto == LCD_PROTO_SERIAL) { /* SERIAL */
charlcd->ops = &charlcd_ops;
hdc->write_data = lcd_write_data_s;
hdc->write_cmd = lcd_write_cmd_s;
if (lcd.pins.cl == PIN_NOT_SET)
lcd.pins.cl = DEFAULT_LCD_PIN_SCL; if (lcd.pins.da == PIN_NOT_SET)
lcd.pins.da = DEFAULT_LCD_PIN_SDA;
staticvoid keypad_send_key(constchar *string, int max_len)
{ /* send the key to the device only if a process is attached to it. */ if (!atomic_read(&keypad_available)) { while (max_len-- && keypad_buflen < KEYPAD_BUFFER && *string) {
keypad_buffer[(keypad_start + keypad_buflen++) %
KEYPAD_BUFFER] = *string++;
}
wake_up_interruptible(&keypad_read_wait);
}
}
/* 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).
*/ staticvoid phys_scan_contacts(void)
{ int bit, bitval; char oldval; char bitmask; char gndmask;
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.
*/ for (bit = 0; bit < 8; bit++) {
bitval = BIT(bit);
if (!(scan_mask_o & bitval)) /* skip unused bits */ continue;
w_dtr(pprt, oldval & ~bitval); /* enable this output */
bitmask = PNL_PINPUT(r_str(pprt)) & ~gndmask;
phys_read |= (__u64)bitmask << (5 * bit);
}
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));
}
staticinlineint input_state_high(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)
input->u.std.press_fct(input->u.std.press_data);
} elseif (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 signal falling down. Let's fall through. */
input->state = INPUT_ST_FALLING;
input->fall_timer = 0;
return 0;
}
staticinlinevoid input_state_falling(struct logical_input *input)
{ #if 0 /* 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;
}
keypressed = 0;
inputs_stable = 1;
list_for_each_entry(input, &logical_inputs, list) { switch (input->state) { 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 ((phys_prev & input->mask) == input->value) break;
input->rise_timer = 0;
input->state = INPUT_ST_RISING;
fallthrough; case INPUT_ST_RISING: if ((phys_curr & input->mask) != input->value) {
input->state = INPUT_ST_LOW; break;
} if (input->rise_timer < input->rise_time) {
inputs_stable = 0;
input->rise_timer++; break;
}
input->high_timer = 0;
input->state = INPUT_ST_HIGH;
fallthrough; case INPUT_ST_HIGH: if (input_state_high(input)) break;
fallthrough; case INPUT_ST_FALLING:
input_state_falling(input);
}
}
}
staticvoid panel_scan_timer(struct timer_list *unused)
{ if (keypad.enabled && keypad_initialized) { if (spin_trylock_irq(&pprt_lock)) {
phys_scan_contacts();
/* no need for the parport anymore */
spin_unlock_irq(&pprt_lock);
}
if (!inputs_stable || phys_curr != phys_prev)
panel_process_inputs();
}
if (keypressed && lcd.enabled && lcd.initialized)
charlcd_poke(lcd.charlcd);
/* 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. * returns 1 if ok, 0 if error (in which case, nothing is written).
*/ static u8 input_name2mask(constchar *name, __u64 *mask, __u64 *value,
u8 *imask, u8 *omask)
{ constchar sigtab[] = "EeSsPpAaBb";
u8 im, om;
__u64 m, v;
om = 0;
im = 0;
m = 0ULL;
v = 0ULL; while (*name) { int in, out, bit, neg; constchar *idx;
idx = strchr(sigtab, *name); if (!idx) return 0; /* input name not found */
in = idx - sigtab;
neg = (in & 1); /* odd (lower) names are negated */
in >>= 1;
im |= BIT(in);
name++; if (*name >= '0' && *name <= '7') {
out = *name - '0';
om |= BIT(out);
} elseif (*name == '-') {
out = 8;
} else { return 0; /* unknown bit name */
}
bit = (out * 5) + in;
m |= 1ULL << bit; if (!neg)
v |= 1ULL << bit;
name++;
}
*mask = m;
*value = v; if (imask)
*imask |= im; if (omask)
*omask |= om; return 1;
}
/* tries to bind a key to the signal name <name>. The key will send the * 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.
*/ staticstruct logical_input *panel_bind_key(constchar *name, constchar *press, constchar *repeat, constchar *release)
{ struct logical_input *key;
key = kzalloc(sizeof(*key), GFP_KERNEL); if (!key) return NULL;
#if 0 /* 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.
*/ staticstruct logical_input *panel_bind_callback(char *name, void (*press_fct)(int), int press_data, void (*release_fct)(int), int release_data)
{ struct logical_input *callback;
callback = kmalloc(sizeof(*callback), GFP_KERNEL); if (!callback) return NULL;
/* take care of an eventual profile */ switch (profile) { case PANEL_PROFILE_CUSTOM: /* custom profile */
selected_keypad_type = DEFAULT_KEYPAD_TYPE;
selected_lcd_type = DEFAULT_LCD_TYPE; break; case PANEL_PROFILE_OLD: /* 8 bits, 2*16, old keypad */
selected_keypad_type = KEYPAD_TYPE_OLD;
selected_lcd_type = LCD_TYPE_OLD;
/* TODO: This two are a little hacky, sort it out later */ if (lcd_width == NOT_SET)
lcd_width = 16; if (lcd_hwidth == NOT_SET)
lcd_hwidth = 16; break; case PANEL_PROFILE_NEW: /* serial, 2*16, new keypad */
selected_keypad_type = KEYPAD_TYPE_NEW;
selected_lcd_type = LCD_TYPE_KS0074; break; case PANEL_PROFILE_HANTRONIX: /* 8 bits, 2*16 hantronix-like, no keypad */
selected_keypad_type = KEYPAD_TYPE_NONE;
selected_lcd_type = LCD_TYPE_HANTRONIX; break; case PANEL_PROFILE_NEXCOM: /* generic 8 bits, 2*16, nexcom keypad, eg. Nexcom. */
selected_keypad_type = KEYPAD_TYPE_NEXCOM;
selected_lcd_type = LCD_TYPE_NEXCOM; break; case PANEL_PROFILE_LARGE: /* 8 bits, 2*40, old keypad */
selected_keypad_type = KEYPAD_TYPE_OLD;
selected_lcd_type = LCD_TYPE_OLD; break;
}
/* * Overwrite selection with module param values (both keypad and lcd), * where the deprecated params have lower prio.
*/ if (keypad_enabled != NOT_SET)
selected_keypad_type = keypad_enabled; if (keypad_type != NOT_SET)
selected_keypad_type = keypad_type;
keypad.enabled = (selected_keypad_type > 0);
if (lcd_enabled != NOT_SET)
selected_lcd_type = lcd_enabled; if (lcd_type != NOT_SET)
selected_lcd_type = lcd_type;
lcd.enabled = (selected_lcd_type > 0);
if (lcd.enabled) { /* * Init lcd struct with load-time values to preserve exact * current functionality (at least for now).
*/
lcd.charset = lcd_charset;
lcd.proto = lcd_proto;
lcd.pins.e = lcd_e_pin;
lcd.pins.rs = lcd_rs_pin;
lcd.pins.rw = lcd_rw_pin;
lcd.pins.cl = lcd_cl_pin;
lcd.pins.da = lcd_da_pin;
lcd.pins.bl = lcd_bl_pin;
}
switch (selected_keypad_type) { case KEYPAD_TYPE_OLD:
keypad_profile = old_keypad_profile; break; case KEYPAD_TYPE_NEW:
keypad_profile = new_keypad_profile; break; case KEYPAD_TYPE_NEXCOM:
keypad_profile = nexcom_keypad_profile; break; default:
keypad_profile = NULL; break;
}
if (!lcd.enabled && !keypad.enabled) { /* no device enabled, let's exit */
pr_err("panel driver disabled.\n"); return;
}
if (parport_claim(pprt)) {
pr_err("could not claim access to parport%d. Aborting.\n",
parport); goto err_unreg_device;
}
/* must init LCD first, just in case an IRQ from the keypad is * generated at keypad init
*/ if (lcd.enabled) {
lcd_init(); if (!lcd.charlcd || charlcd_register(lcd.charlcd)) goto err_unreg_device;
}
if (keypad.enabled) {
keypad_init(); if (misc_register(&keypad_dev)) goto err_lcd_unreg;
} return;
err_lcd_unreg: if (scan_timer.function)
timer_delete_sync(&scan_timer); if (lcd.enabled)
charlcd_unregister(lcd.charlcd);
err_unreg_device:
hd44780_common_free(lcd.charlcd);
lcd.charlcd = NULL;
parport_unregister_device(pprt);
pprt = NULL;
}
staticvoid panel_detach(struct parport *port)
{ if (port->number != parport) return;
if (!pprt) {
pr_err("%s: port->number=%d parport=%d, nothing to unregister.\n",
__func__, port->number, parport); return;
} if (scan_timer.function)
timer_delete_sync(&scan_timer);
if (keypad.enabled) {
misc_deregister(&keypad_dev);
keypad_initialized = 0;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.