linux/drivers/auxdisplay/panel.c

// SPDX-License-Identifier: GPL-2.0+
/*
 * Front panel driver for Linux
 * Copyright (C) 2000-2008, Willy Tarreau <[email protected]>
 * 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
 *
 */

#define pr_fmt(fmt)

#include <linux/module.h>

#include <linux/types.h>
#include <linux/errno.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/spinlock.h>
#include <linux/interrupt.h>
#include <linux/miscdevice.h>
#include <linux/slab.h>
#include <linux/ioport.h>
#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/list.h>

#include <linux/io.h>
#include <linux/uaccess.h>

#include "charlcd.h"
#include "hd44780_common.h"

#define LCD_MAXBYTES

#define KEYPAD_BUFFER

/* poll the keyboard this every second */
#define INPUT_POLL_TIME
/* a key starts to repeat after this times INPUT_POLL_TIME */
#define KEYPAD_REP_START
/* a key repeats this times INPUT_POLL_TIME */
#define KEYPAD_REP_DELAY

/* converts an r_str() input to an active high, bits string : 000BAOSE */
#define PNL_PINPUT(a)

#define PNL_PBUSY
#define PNL_PACK
#define PNL_POUTPA
#define PNL_PSELECD
#define PNL_PERRORP

#define PNL_PBIDIR
/* high to read data in or-ed with data out */
#define PNL_PINTEN
#define PNL_PSELECP
#define PNL_PINITP
#define PNL_PAUTOLF
#define PNL_PSTROBE

#define PNL_PD0
#define PNL_PD1
#define PNL_PD2
#define PNL_PD3
#define PNL_PD4
#define PNL_PD5
#define PNL_PD6
#define PNL_PD7

#define PIN_NONE
#define PIN_STROBE
#define PIN_D0
#define PIN_D1
#define PIN_D2
#define PIN_D3
#define PIN_D4
#define PIN_D5
#define PIN_D6
#define PIN_D7
#define PIN_AUTOLF
#define PIN_INITP
#define PIN_SELECP
#define PIN_NOT_SET

#define NOT_SET

/* macros to simplify use of the parallel port */
#define r_ctr(x)
#define r_dtr(x)
#define r_str(x)
#define w_ctr(x, y)
#define w_dtr(x, 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;

enum input_type {};

enum input_state {};

struct logical_input {};

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 */
static char inputs_stable;

/* these variables are specific to the keypad */
static struct {} keypad;

static char keypad_buffer[KEYPAD_BUFFER];
static int keypad_buflen;
static int keypad_start;
static char keypressed;
static wait_queue_head_t keypad_read_wait;

/* lcd-specific variables */
static struct {} lcd;

/* Needed only for init */
static int selected_lcd_type =;

/*
 * 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
#define BIT_SET
#define BIT_MSK
#define BIT_STATES
/*
 * one entry for each bit on the LCD
 */
#define LCD_BIT_E
#define LCD_BIT_RS
#define LCD_BIT_RW
#define LCD_BIT_BL
#define LCD_BIT_CL
#define LCD_BIT_DA
#define LCD_BITS

/*
 * each bit can be either connected to a DATA or CTRL port
 */
#define LCD_PORT_C
#define LCD_PORT_D
#define LCD_PORTS

static unsigned char lcd_bits[LCD_PORTS][LCD_BITS][BIT_STATES];

/*
 * LCD protocols
 */
#define LCD_PROTO_PARALLEL
#define LCD_PROTO_SERIAL
#define LCD_PROTO_TI_DA8XX_LCD

/*
 * LCD character sets
 */
#define LCD_CHARSET_NORMAL
#define LCD_CHARSET_KS0074

/*
 * LCD types
 */
#define LCD_TYPE_NONE
#define LCD_TYPE_CUSTOM
#define LCD_TYPE_OLD
#define LCD_TYPE_KS0074
#define LCD_TYPE_HANTRONIX
#define LCD_TYPE_NEXCOM

/*
 * keypad types
 */
#define KEYPAD_TYPE_NONE
#define KEYPAD_TYPE_OLD
#define KEYPAD_TYPE_NEW
#define KEYPAD_TYPE_NEXCOM

/*
 * panel profiles
 */
#define PANEL_PROFILE_CUSTOM
#define PANEL_PROFILE_OLD
#define PANEL_PROFILE_NEW
#define PANEL_PROFILE_HANTRONIX
#define PANEL_PROFILE_NEXCOM
#define PANEL_PROFILE_LARGE

/*
 * Construct custom config from the kernel's configuration
 */
#define DEFAULT_PARPORT
#define DEFAULT_PROFILE
#define DEFAULT_KEYPAD_TYPE
#define DEFAULT_LCD_TYPE
#define DEFAULT_LCD_HEIGHT
#define DEFAULT_LCD_WIDTH
#define DEFAULT_LCD_CHARSET
#define DEFAULT_LCD_PROTO

#define DEFAULT_LCD_PIN_E
#define DEFAULT_LCD_PIN_RS
#define DEFAULT_LCD_PIN_RW
#define DEFAULT_LCD_PIN_SCL
#define DEFAULT_LCD_PIN_SDA
#define DEFAULT_LCD_PIN_BL

#ifdef CONFIG_PANEL_PARPORT
#undef DEFAULT_PARPORT
#define DEFAULT_PARPORT
#endif

#ifdef CONFIG_PANEL_PROFILE
#undef DEFAULT_PROFILE
#define DEFAULT_PROFILE
#endif

#if DEFAULT_PROFILE == 0	/* custom */
#ifdef CONFIG_PANEL_KEYPAD
#undef DEFAULT_KEYPAD_TYPE
#define DEFAULT_KEYPAD_TYPE
#endif

#ifdef CONFIG_PANEL_LCD
#undef DEFAULT_LCD_TYPE
#define DEFAULT_LCD_TYPE
#endif

#ifdef CONFIG_PANEL_LCD_HEIGHT
#undef DEFAULT_LCD_HEIGHT
#define DEFAULT_LCD_HEIGHT
#endif

#ifdef CONFIG_PANEL_LCD_WIDTH
#undef DEFAULT_LCD_WIDTH
#define DEFAULT_LCD_WIDTH
#endif

#ifdef CONFIG_PANEL_LCD_BWIDTH
#undef DEFAULT_LCD_BWIDTH
#define DEFAULT_LCD_BWIDTH
#endif

#ifdef CONFIG_PANEL_LCD_HWIDTH
#undef DEFAULT_LCD_HWIDTH
#define DEFAULT_LCD_HWIDTH
#endif

#ifdef CONFIG_PANEL_LCD_CHARSET
#undef DEFAULT_LCD_CHARSET
#define DEFAULT_LCD_CHARSET
#endif

#ifdef CONFIG_PANEL_LCD_PROTO
#undef DEFAULT_LCD_PROTO
#define DEFAULT_LCD_PROTO
#endif

#ifdef CONFIG_PANEL_LCD_PIN_E
#undef DEFAULT_LCD_PIN_E
#define DEFAULT_LCD_PIN_E
#endif

#ifdef CONFIG_PANEL_LCD_PIN_RS
#undef DEFAULT_LCD_PIN_RS
#define DEFAULT_LCD_PIN_RS
#endif

#ifdef CONFIG_PANEL_LCD_PIN_RW
#undef DEFAULT_LCD_PIN_RW
#define DEFAULT_LCD_PIN_RW
#endif

#ifdef CONFIG_PANEL_LCD_PIN_SCL
#undef DEFAULT_LCD_PIN_SCL
#define DEFAULT_LCD_PIN_SCL
#endif

#ifdef CONFIG_PANEL_LCD_PIN_SDA
#undef DEFAULT_LCD_PIN_SDA
#define DEFAULT_LCD_PIN_SDA
#endif

#ifdef CONFIG_PANEL_LCD_PIN_BL
#undef DEFAULT_LCD_PIN_BL
#define DEFAULT_LCD_PIN_BL
#endif

#endif /* DEFAULT_PROFILE == 0 */

/* global variables */

/* Device single-open policy control */
static atomic_t keypad_available =;

static struct pardevice *pprt;

static int keypad_initialized;

static DEFINE_SPINLOCK(pprt_lock);
static struct timer_list scan_timer;

MODULE_DESCRIPTION();

static int parport =;
module_param(parport, int, 0000);
MODULE_PARM_DESC();

static int profile =;
module_param(profile, int, 0000);
MODULE_PARM_DESC();

static int keypad_type =;
module_param(keypad_type, int, 0000);
MODULE_PARM_DESC();

static int lcd_type =;
module_param(lcd_type, int, 0000);
MODULE_PARM_DESC();

static int lcd_height =;
module_param(lcd_height, int, 0000);
MODULE_PARM_DESC();

static int lcd_width =;
module_param(lcd_width, int, 0000);
MODULE_PARM_DESC();

static int lcd_bwidth =;	/* internal buffer width (usually 40) */
module_param(lcd_bwidth, int, 0000);
MODULE_PARM_DESC();

static int lcd_hwidth =;	/* hardware buffer width (usually 64) */
module_param(lcd_hwidth, int, 0000);
MODULE_PARM_DESC();

static int lcd_charset =;
module_param(lcd_charset, int, 0000);
MODULE_PARM_DESC();

static int lcd_proto =;
module_param(lcd_proto, int, 0000);
MODULE_PARM_DESC();

/*
 * 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 !
 */

static int lcd_e_pin  =;
module_param(lcd_e_pin, int, 0000);
MODULE_PARM_DESC();

static int lcd_rs_pin =;
module_param(lcd_rs_pin, int, 0000);
MODULE_PARM_DESC();

static int lcd_rw_pin =;
module_param(lcd_rw_pin, int, 0000);
MODULE_PARM_DESC();

static int lcd_cl_pin =;
module_param(lcd_cl_pin, int, 0000);
MODULE_PARM_DESC();

static int lcd_da_pin =;
module_param(lcd_da_pin, int, 0000);
MODULE_PARM_DESC();

static int lcd_bl_pin =;
module_param(lcd_bl_pin, int, 0000);
MODULE_PARM_DESC();

/* Deprecated module parameters - consider not using them anymore */

static int lcd_enabled =;
module_param(lcd_enabled, int, 0000);
MODULE_PARM_DESC();

static int keypad_enabled =;
module_param(keypad_enabled, int, 0000);
MODULE_PARM_DESC();

/* for some LCD drivers (ks0074) we need a charset conversion table. */
static const unsigned char lcd_char_conv_ks0074[256] =;

static const char old_keypad_profile[][4][9] =;

/* signals, press, repeat, release */
static const char new_keypad_profile[][4][9] =;

/* signals, press, repeat, release */
static const char nexcom_keypad_profile[][4][9] =;

static const char (*keypad_profile)[4][9] =;

static DECLARE_BITMAP(bits, LCD_BITS);

static void lcd_get_bits(unsigned int port, int *val)
{}

/* sets data port bits according to current signals values */
static int set_data_bits(void)
{}

/* sets ctrl port bits according to current signals values */
static int set_ctrl_bits(void)
{}

/* sets ctrl & data port bits according to current signals values */
static void panel_set_bits(void)
{}

/*
 * 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])
 */
static void pin_to_bits(int pin, unsigned char *d_val, unsigned char *c_val)
{}

/*
 * send a serial byte to the LCD panel. The caller is responsible for locking
 * if needed.
 */
static void lcd_send_serial(int byte)
{}

/* turn the backlight on or off */
static void lcd_backlight(struct charlcd *charlcd, enum charlcd_onoff on)
{}

/* send a command to the LCD panel in serial mode */
static void lcd_write_cmd_s(struct hd44780_common *hdc, int cmd)
{}

/* send data to the LCD panel in serial mode */
static void lcd_write_data_s(struct hd44780_common *hdc, int data)
{}

/* send a command to the LCD panel in 8 bits parallel mode */
static void lcd_write_cmd_p8(struct hd44780_common *hdc, int cmd)
{}

/* send data to the LCD panel in 8 bits parallel mode */
static void lcd_write_data_p8(struct hd44780_common *hdc, int data)
{}

/* send a command to the TI LCD panel */
static void lcd_write_cmd_tilcd(struct hd44780_common *hdc, int cmd)
{}

/* send data to the TI LCD panel */
static void lcd_write_data_tilcd(struct hd44780_common *hdc, int data)
{}

static const struct charlcd_ops charlcd_ops =;

/* initialize the LCD driver */
static void lcd_init(void)
{}

/*
 * These are the file operation function for user access to /dev/keypad
 */

static ssize_t keypad_read(struct file *file,
			   char __user *buf, size_t count, loff_t *ppos)
{}

static int keypad_open(struct inode *inode, struct file *file)
{}

static int keypad_release(struct inode *inode, struct file *file)
{}

static const struct file_operations keypad_fops =;

static struct miscdevice keypad_dev =;

static void keypad_send_key(const 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".
 *
 * 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)
{}

static inline int input_state_high(struct logical_input *input)
{}

static inline void input_state_falling(struct logical_input *input)
{}

static void panel_process_inputs(void)
{}

static void panel_scan_timer(struct timer_list *unused)
{}

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.
 * returns 1 if ok, 0 if error (in which case, nothing is written).
 */
static u8 input_name2mask(const char *name, __u64 *mask, __u64 *value,
			  u8 *imask, u8 *omask)
{}

/* 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.
 */
static struct logical_input *panel_bind_key(const char *name, const char *press,
					    const char *repeat,
					    const char *release)
{}

#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.
 */
static struct 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;

	memset(callback, 0, sizeof(struct logical_input));
	if (!input_name2mask(name, &callback->mask, &callback->value,
			     &scan_mask_i, &scan_mask_o))
		return NULL;

	callback->type = INPUT_TYPE_STD;
	callback->state = INPUT_ST_LOW;
	callback->rise_time = 1;
	callback->fall_time = 1;
	callback->u.std.press_fct = press_fct;
	callback->u.std.press_data = press_data;
	callback->u.std.release_fct = release_fct;
	callback->u.std.release_data = release_data;
	list_add(&callback->list, &logical_inputs);
	return callback;
}
#endif

static void keypad_init(void)
{}

/**************************************************/
/* device initialization                          */
/**************************************************/

static void panel_attach(struct parport *port)
{}

static void panel_detach(struct parport *port)
{}

static struct parport_driver panel_driver =;
module_parport_driver();

MODULE_AUTHOR();
MODULE_LICENSE();