Commit 2ef6e58b authored by Dmitry Torokhov's avatar Dmitry Torokhov Committed by Vojtech Pavlik

Input: pull common code from psmouse and atkbd into libps2 module

Signed-off-by: default avatarDmitry Torokhov <dtor@mail.ru>
parent 9e61cbe0
......@@ -16,6 +16,7 @@ config KEYBOARD_ATKBD
default y
depends on INPUT && INPUT_KEYBOARD
select SERIO
select SERIO_LIBPS2
select SERIO_I8042 if PC
select SERIO_GSCPS2 if GSC
help
......
This diff is collapsed.
......@@ -16,6 +16,7 @@ config MOUSE_PS2
default y
depends on INPUT && INPUT_MOUSE
select SERIO
select SERIO_LIBPS2
select SERIO_I8042 if PC
select SERIO_GSCPS2 if GSC
---help---
......
......@@ -15,6 +15,7 @@
#include <linux/input.h>
#include <linux/serio.h>
#include <linux/libps2.h>
#include "psmouse.h"
#include "alps.h"
......@@ -187,6 +188,7 @@ static psmouse_ret_t alps_process_byte(struct psmouse *psmouse, struct pt_regs *
int alps_get_model(struct psmouse *psmouse)
{
struct ps2dev *ps2dev = &psmouse->ps2dev;
unsigned char param[4];
int i;
......@@ -195,14 +197,14 @@ int alps_get_model(struct psmouse *psmouse)
* ALPS should return 0x00,0x00,0x0a or 0x00,0x00,0x64
*/
param[0] = 0;
if (psmouse_command(psmouse, param, PSMOUSE_CMD_SETRES) ||
psmouse_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE11) ||
psmouse_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE11) ||
psmouse_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE11))
if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES) ||
ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) ||
ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) ||
ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11))
return -1;
param[0] = param[1] = param[2] = 0xff;
if (psmouse_command(psmouse, param, PSMOUSE_CMD_GETINFO))
if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO))
return -1;
dbg("E6 report: %2.2x %2.2x %2.2x", param[0], param[1], param[2]);
......@@ -212,14 +214,14 @@ int alps_get_model(struct psmouse *psmouse)
/* Now try "E7 report". ALPS should return 0x33 in byte 1 */
param[0] = 0;
if (psmouse_command(psmouse, param, PSMOUSE_CMD_SETRES) ||
psmouse_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE21) ||
psmouse_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE21) ||
psmouse_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE21))
if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES) ||
ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) ||
ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) ||
ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21))
return -1;
param[0] = param[1] = param[2] = 0xff;
if (psmouse_command(psmouse, param, PSMOUSE_CMD_GETINFO))
if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO))
return -1;
dbg("E7 report: %2.2x %2.2x %2.2x", param[0], param[1], param[2]);
......@@ -238,36 +240,39 @@ int alps_get_model(struct psmouse *psmouse)
*/
static int alps_passthrough_mode(struct psmouse *psmouse, int enable)
{
struct ps2dev *ps2dev = &psmouse->ps2dev;
unsigned char param[3];
int cmd = enable ? PSMOUSE_CMD_SETSCALE21 : PSMOUSE_CMD_SETSCALE11;
if (psmouse_command(psmouse, NULL, cmd) ||
psmouse_command(psmouse, NULL, cmd) ||
psmouse_command(psmouse, NULL, cmd) ||
psmouse_command(psmouse, NULL, PSMOUSE_CMD_DISABLE))
if (ps2_command(ps2dev, NULL, cmd) ||
ps2_command(ps2dev, NULL, cmd) ||
ps2_command(ps2dev, NULL, cmd) ||
ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE))
return -1;
/* we may get 3 more bytes, just ignore them */
psmouse_command(psmouse, param, 0x0300);
ps2_command(ps2dev, param, 0x0300);
return 0;
}
static int alps_magic_knock(struct psmouse *psmouse)
{
struct ps2dev *ps2dev = &psmouse->ps2dev;
/* Try ALPS magic knock - 4 disable before enable */
if (psmouse_command(psmouse, NULL, PSMOUSE_CMD_DISABLE) ||
psmouse_command(psmouse, NULL, PSMOUSE_CMD_DISABLE) ||
psmouse_command(psmouse, NULL, PSMOUSE_CMD_DISABLE) ||
psmouse_command(psmouse, NULL, PSMOUSE_CMD_DISABLE) ||
psmouse_command(psmouse, NULL, PSMOUSE_CMD_ENABLE))
if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) ||
ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) ||
ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) ||
ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) ||
ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE))
return -1;
return 0;
}
static int alps_absolute_mode(struct psmouse *psmouse)
{
if (psmouse_command(psmouse, NULL, PSMOUSE_CMD_RESET_DIS))
if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS))
return -1;
if (alps_passthrough_mode(psmouse, 1))
......@@ -286,16 +291,18 @@ static int alps_absolute_mode(struct psmouse *psmouse)
* Switch mouse to poll (remote) mode so motion data will not
* get in our way
*/
return psmouse_command(psmouse, NULL, PSMOUSE_CMD_SETPOLL);
return ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETPOLL);
}
static int alps_get_status(struct psmouse *psmouse, char *param)
{
struct ps2dev *ps2dev = &psmouse->ps2dev;
/* Get status: 0xF5 0xF5 0xF5 0xE9 */
if (psmouse_command(psmouse, NULL, PSMOUSE_CMD_DISABLE) ||
psmouse_command(psmouse, NULL, PSMOUSE_CMD_DISABLE) ||
psmouse_command(psmouse, NULL, PSMOUSE_CMD_DISABLE) ||
psmouse_command(psmouse, param, PSMOUSE_CMD_GETINFO))
if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) ||
ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) ||
ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) ||
ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO))
return -1;
dbg("Status: %2.2x %2.2x %2.2x", param[0], param[1], param[2]);
......@@ -315,6 +322,7 @@ static int alps_get_status(struct psmouse *psmouse, char *param)
static int alps_tap_mode(struct psmouse *psmouse, int model, int enable)
{
int rc = 0;
struct ps2dev *ps2dev = &psmouse->ps2dev;
int cmd = enable ? PSMOUSE_CMD_SETRATE : PSMOUSE_CMD_SETRES;
unsigned char tap_arg = enable ? 0x0A : 0x00;
unsigned char param[4];
......@@ -322,10 +330,10 @@ static int alps_tap_mode(struct psmouse *psmouse, int model, int enable)
if (model == ALPS_MODEL_DUALPOINT && alps_passthrough_mode(psmouse, 1))
return -1;
if (psmouse_command(psmouse, param, PSMOUSE_CMD_GETINFO) ||
psmouse_command(psmouse, NULL, PSMOUSE_CMD_DISABLE) ||
psmouse_command(psmouse, NULL, PSMOUSE_CMD_DISABLE) ||
psmouse_command(psmouse, &tap_arg, cmd))
if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO) ||
ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) ||
ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) ||
ps2_command(ps2dev, &tap_arg, cmd))
rc = -1;
if (model == ALPS_MODEL_DUALPOINT && alps_passthrough_mode(psmouse, 0))
......
......@@ -11,6 +11,7 @@
#include <linux/input.h>
#include <linux/serio.h>
#include <linux/libps2.h>
#include "psmouse.h"
#include "logips2pp.h"
......@@ -97,7 +98,7 @@ static int ps2pp_cmd(struct psmouse *psmouse, unsigned char *param, unsigned cha
if (psmouse_sliced_command(psmouse, command))
return -1;
if (psmouse_command(psmouse, param, PSMOUSE_CMD_POLL))
if (ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_POLL))
return -1;
return 0;
......@@ -113,19 +114,20 @@ static int ps2pp_cmd(struct psmouse *psmouse, unsigned char *param, unsigned cha
static void ps2pp_set_smartscroll(struct psmouse *psmouse)
{
struct ps2dev *ps2dev = &psmouse->ps2dev;
unsigned char param[4];
ps2pp_cmd(psmouse, param, 0x32);
param[0] = 0;
psmouse_command(psmouse, param, PSMOUSE_CMD_SETRES);
psmouse_command(psmouse, param, PSMOUSE_CMD_SETRES);
psmouse_command(psmouse, param, PSMOUSE_CMD_SETRES);
ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
if (psmouse_smartscroll < 2) {
/* 0 - disabled, 1 - enabled */
param[0] = psmouse_smartscroll;
psmouse_command(psmouse, param, PSMOUSE_CMD_SETRES);
ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
}
}
......@@ -137,11 +139,13 @@ static void ps2pp_set_smartscroll(struct psmouse *psmouse)
void ps2pp_set_800dpi(struct psmouse *psmouse)
{
struct ps2dev *ps2dev = &psmouse->ps2dev;
unsigned char param = 3;
psmouse_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE11);
psmouse_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE11);
psmouse_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE11);
psmouse_command(psmouse, &param, PSMOUSE_CMD_SETRES);
ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11);
ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11);
ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11);
ps2_command(ps2dev, &param, PSMOUSE_CMD_SETRES);
}
static struct ps2pp_info *get_model_info(unsigned char model)
......@@ -238,18 +242,19 @@ static void ps2pp_set_model_properties(struct psmouse *psmouse, struct ps2pp_inf
int ps2pp_init(struct psmouse *psmouse, int set_properties)
{
struct ps2dev *ps2dev = &psmouse->ps2dev;
unsigned char param[4];
unsigned char protocol = PSMOUSE_PS2;
unsigned char model, buttons;
struct ps2pp_info *model_info;
param[0] = 0;
psmouse_command(psmouse, param, PSMOUSE_CMD_SETRES);
psmouse_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE11);
psmouse_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE11);
psmouse_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE11);
ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11);
ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11);
ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11);
param[1] = 0;
psmouse_command(psmouse, param, PSMOUSE_CMD_GETINFO);
ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO);
if (param[1] != 0) {
model = ((param[0] >> 4) & 0x07) | ((param[0] << 3) & 0x78);
......@@ -263,16 +268,16 @@ int ps2pp_init(struct psmouse *psmouse, int set_properties)
/* Unprotect RAM */
param[0] = 0x11; param[1] = 0x04; param[2] = 0x68;
psmouse_command(psmouse, param, 0x30d1);
ps2_command(ps2dev, param, 0x30d1);
/* Enable features */
param[0] = 0x11; param[1] = 0x05; param[2] = 0x0b;
psmouse_command(psmouse, param, 0x30d1);
ps2_command(ps2dev, param, 0x30d1);
/* Enable PS2++ */
param[0] = 0x11; param[1] = 0x09; param[2] = 0xc3;
psmouse_command(psmouse, param, 0x30d1);
ps2_command(ps2dev, param, 0x30d1);
param[0] = 0;
if (!psmouse_command(psmouse, param, 0x13d1) &&
if (!ps2_command(ps2dev, param, 0x13d1) &&
param[0] == 0x06 && param[1] == 0x00 && param[2] == 0x14) {
protocol = PSMOUSE_PS2TPP;
}
......
This diff is collapsed.
......@@ -20,11 +20,6 @@
#define PSMOUSE_RET_ACK 0xfa
#define PSMOUSE_RET_NAK 0xfe
#define PSMOUSE_FLAG_ACK 0 /* Waiting for ACK/NAK */
#define PSMOUSE_FLAG_CMD 1 /* Waiting for command to finish */
#define PSMOUSE_FLAG_CMD1 2 /* Waiting for the first byte of command response */
#define PSMOUSE_FLAG_WAITID 3 /* Command execiting is GET ID */
enum psmouse_state {
PSMOUSE_IGNORE,
PSMOUSE_INITIALIZING,
......@@ -42,26 +37,18 @@ typedef enum {
struct psmouse {
void *private;
struct input_dev dev;
struct serio *serio;
struct ps2dev ps2dev;
char *vendor;
char *name;
unsigned char cmdbuf[8];
unsigned char packet[8];
unsigned char cmdcnt;
unsigned char pktcnt;
unsigned char type;
unsigned char model;
unsigned long last;
unsigned long out_of_sync;
enum psmouse_state state;
unsigned char nak;
char error;
char devname[64];
char phys[32];
unsigned long flags;
/* Used to signal completion from interrupt handler */
wait_queue_head_t wait;
psmouse_ret_t (*protocol_handler)(struct psmouse *psmouse, struct pt_regs *regs);
int (*reconnect)(struct psmouse *psmouse);
......@@ -84,7 +71,6 @@ enum psmouse_type {
PSMOUSE_ALPS,
};
int psmouse_command(struct psmouse *psmouse, unsigned char *param, int command);
int psmouse_sliced_command(struct psmouse *psmouse, unsigned char command);
int psmouse_reset(struct psmouse *psmouse);
......
......@@ -26,6 +26,7 @@
#include <linux/module.h>
#include <linux/input.h>
#include <linux/serio.h>
#include <linux/libps2.h>
#include "psmouse.h"
#include "synaptics.h"
......@@ -50,7 +51,7 @@ static int synaptics_send_cmd(struct psmouse *psmouse, unsigned char c, unsigned
{
if (psmouse_sliced_command(psmouse, c))
return -1;
if (psmouse_command(psmouse, param, PSMOUSE_CMD_GETINFO))
if (ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETINFO))
return -1;
return 0;
}
......@@ -65,7 +66,7 @@ static int synaptics_mode_cmd(struct psmouse *psmouse, unsigned char mode)
if (psmouse_sliced_command(psmouse, mode))
return -1;
param[0] = SYN_PS_SET_MODE2;
if (psmouse_command(psmouse, param, PSMOUSE_CMD_SETRATE))
if (ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_SETRATE))
return -1;
return 0;
}
......@@ -219,7 +220,7 @@ static int synaptics_pt_write(struct serio *serio, unsigned char c)
if (psmouse_sliced_command(parent, c))
return -1;
if (psmouse_command(parent, &rate_param, PSMOUSE_CMD_SETRATE))
if (ps2_command(&parent->ps2dev, &rate_param, PSMOUSE_CMD_SETRATE))
return -1;
return 0;
}
......@@ -245,7 +246,7 @@ static void synaptics_pass_pt_packet(struct serio *ptport, unsigned char *packet
static void synaptics_pt_activate(struct psmouse *psmouse)
{
struct psmouse *child = psmouse->serio->child->private;
struct psmouse *child = psmouse->ps2dev.serio->child->private;
/* adjust the touchpad to child's choice of protocol */
if (child && child->type >= PSMOUSE_GENPS) {
......@@ -270,11 +271,11 @@ static void synaptics_pt_create(struct psmouse *psmouse)
strlcpy(serio->name, "Synaptics pass-through", sizeof(serio->name));
strlcpy(serio->phys, "synaptics-pt/serio0", sizeof(serio->name));
serio->write = synaptics_pt_write;
serio->parent = psmouse->serio;
serio->parent = psmouse->ps2dev.serio;
psmouse->pt_activate = synaptics_pt_activate;
psmouse->serio->child = serio;
psmouse->ps2dev.serio->child = serio;
}
/*****************************************************************************
......@@ -470,8 +471,8 @@ static psmouse_ret_t synaptics_process_byte(struct psmouse *psmouse, struct pt_r
priv->pkt_type = synaptics_detect_pkt_type(psmouse);
if (SYN_CAP_PASS_THROUGH(priv->capabilities) && synaptics_is_pt_packet(psmouse->packet)) {
if (psmouse->serio->child)
synaptics_pass_pt_packet(psmouse->serio->child, psmouse->packet);
if (psmouse->ps2dev.serio->child)
synaptics_pass_pt_packet(psmouse->ps2dev.serio->child, psmouse->packet);
} else
synaptics_process_packet(psmouse);
......@@ -561,15 +562,16 @@ static int synaptics_reconnect(struct psmouse *psmouse)
int synaptics_detect(struct psmouse *psmouse)
{
struct ps2dev *ps2dev = &psmouse->ps2dev;
unsigned char param[4];
param[0] = 0;
psmouse_command(psmouse, param, PSMOUSE_CMD_SETRES);
psmouse_command(psmouse, param, PSMOUSE_CMD_SETRES);
psmouse_command(psmouse, param, PSMOUSE_CMD_SETRES);
psmouse_command(psmouse, param, PSMOUSE_CMD_SETRES);
psmouse_command(psmouse, param, PSMOUSE_CMD_GETINFO);
ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO);
return param[1] == 0x47;
}
......
......@@ -131,6 +131,16 @@ config SERIO_MACEPS2
To compile this driver as a module, choose M here: the
module will be called maceps2.
config SERIO_LIBPS2
tristate "PS/2 driver library"
depends on SERIO
help
Say Y here if you are using a driver for device connected
to a PS/2 port, such as PS/2 mouse or standard AT keyboard.
To compile this driver as a module, choose M here: the
module will be called libps2.
config SERIO_RAW
tristate "Raw access to serio ports"
depends on SERIO
......
......@@ -17,4 +17,5 @@ obj-$(CONFIG_SERIO_98KBD) += 98kbd-io.o
obj-$(CONFIG_SERIO_GSCPS2) += gscps2.o
obj-$(CONFIG_SERIO_PCIPS2) += pcips2.o
obj-$(CONFIG_SERIO_MACEPS2) += maceps2.o
obj-$(CONFIG_SERIO_LIBPS2) += libps2.o
obj-$(CONFIG_SERIO_RAW) += serio_raw.o
/*
* PS/2 driver library
*
* Copyright (c) 1999-2002 Vojtech Pavlik
* Copyright (c) 2004 Dmitry Torokhov
*/
/*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*/
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/input.h>
#include <linux/serio.h>
#include <linux/init.h>
#include <linux/libps2.h>
#define DRIVER_DESC "PS/2 driver library"
MODULE_AUTHOR("Dmitry Torokhov <dtor@mail.ru>");
MODULE_DESCRIPTION("PS/2 driver library");
MODULE_LICENSE("GPL");
EXPORT_SYMBOL(ps2_init);
EXPORT_SYMBOL(ps2_sendbyte);
EXPORT_SYMBOL(ps2_command);
EXPORT_SYMBOL(ps2_schedule_command);
EXPORT_SYMBOL(ps2_handle_ack);
EXPORT_SYMBOL(ps2_handle_response);
EXPORT_SYMBOL(ps2_cmd_aborted);
/* Work structure to schedule execution of a command */
struct ps2work {
struct work_struct work;
struct ps2dev *ps2dev;
int command;
unsigned char param[0];
};
/*
* ps2_sendbyte() sends a byte to the mouse, and waits for acknowledge.
* It doesn't handle retransmission, though it could - because when there would
* be need for retransmissions, the mouse has to be replaced anyway.
*
* ps2_sendbyte() can only be called from a process context
*/
int ps2_sendbyte(struct ps2dev *ps2dev, unsigned char byte)
{
serio_pause_rx(ps2dev->serio);
ps2dev->nak = 1;
ps2dev->flags |= PS2_FLAG_ACK;
serio_continue_rx(ps2dev->serio);
if (serio_write(ps2dev->serio, byte) == 0)
wait_event_interruptible_timeout(ps2dev->wait,
!(ps2dev->flags & PS2_FLAG_ACK),
msecs_to_jiffies(200));
serio_pause_rx(ps2dev->serio);
ps2dev->flags &= ~PS2_FLAG_ACK;
serio_continue_rx(ps2dev->serio);
return -ps2dev->nak;
}
/*
* ps2_command() sends a command and its parameters to the mouse,
* then waits for the response and puts it in the param array.
*
* ps2_command() can only be called from a process context
*/
int ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command)
{
int timeout;
int send = (command >> 12) & 0xf;
int receive = (command >> 8) & 0xf;
int rc = -1;
int i;
down(&ps2dev->cmd_sem);
timeout = msecs_to_jiffies(command == PS2_CMD_RESET_BAT ? 4000 : 500);
serio_pause_rx(ps2dev->serio);
ps2dev->flags = command == PS2_CMD_GETID ? PS2_FLAG_WAITID : 0;
ps2dev->cmdcnt = receive;
if (receive && param)
for (i = 0; i < receive; i++)
ps2dev->cmdbuf[(receive - 1) - i] = param[i];
serio_continue_rx(ps2dev->serio);
if (command & 0xff)
if (ps2_sendbyte(ps2dev, command & 0xff))
goto out;
for (i = 0; i < send; i++)
if (ps2_sendbyte(ps2dev, param[i]))
goto out;
timeout = wait_event_interruptible_timeout(ps2dev->wait,
!(ps2dev->flags & PS2_FLAG_CMD1), timeout);
if (ps2dev->cmdcnt && timeout > 0) {
if (command == PS2_CMD_RESET_BAT && jiffies_to_msecs(timeout) > 100)
timeout = msecs_to_jiffies(100);
if (command == PS2_CMD_GETID &&
ps2dev->cmdbuf[receive - 1] != 0xab && ps2dev->cmdbuf[receive - 1] != 0xac) {
/*
* Device behind the port is not a keyboard
* so we don't need to wait for the 2nd byte
* of ID response.
*/
serio_pause_rx(ps2dev->serio);
ps2dev->flags = ps2dev->cmdcnt = 0;
serio_continue_rx(ps2dev->serio);
}
wait_event_interruptible_timeout(ps2dev->wait,
!(ps2dev->flags & PS2_FLAG_CMD), timeout);
}
if (param)
for (i = 0; i < receive; i++)
param[i] = ps2dev->cmdbuf[(receive - 1) - i];
if (ps2dev->cmdcnt && (command != PS2_CMD_RESET_BAT || ps2dev->cmdcnt != 1))
goto out;
rc = 0;
out:
serio_pause_rx(ps2dev->serio);
ps2dev->flags = 0;
serio_continue_rx(ps2dev->serio);
up(&ps2dev->cmd_sem);
return rc;
}
/*
* ps2_execute_scheduled_command() sends a command, previously scheduled by
* ps2_schedule_command(), to a PS/2 device (keyboard, mouse, etc.)
*/
static void ps2_execute_scheduled_command(void *data)
{
struct ps2work *ps2work = data;
ps2_command(ps2work->ps2dev, ps2work->param, ps2work->command);
kfree(ps2work);
}
/*
* ps2_schedule_command() allows to schedule delayed execution of a PS/2
* command and can be used to issue a command from an interrupt or softirq
* context.
*/
int ps2_schedule_command(struct ps2dev *ps2dev, unsigned char *param, int command)
{
struct ps2work *ps2work;
int send = (command >> 12) & 0xf;
int receive = (command >> 8) & 0xf;
if (!(ps2work = kmalloc(sizeof(struct ps2work) + max(send, receive), GFP_ATOMIC)))
return -1;
memset(ps2work, 0, sizeof(struct ps2work));
ps2work->ps2dev = ps2dev;
ps2work->command = command;
memcpy(ps2work->param, param, send);
INIT_WORK(&ps2work->work, ps2_execute_scheduled_command, ps2work);
if (!schedule_work(&ps2work->work)) {
kfree(ps2work);
return -1;
}
return 0;
}
/*
* ps2_init() initializes ps2dev structure
*/
void ps2_init(struct ps2dev *ps2dev, struct serio *serio)
{
init_MUTEX(&ps2dev->cmd_sem);
init_waitqueue_head(&ps2dev->wait);
ps2dev->serio = serio;
}
/*
* ps2_handle_ack()
*/
int ps2_handle_ack(struct ps2dev *ps2dev, unsigned char data)
{
switch (data) {
case PS2_RET_ACK:
ps2dev->nak = 0;
break;
case PS2_RET_NAK:
ps2dev->nak = 1;
break;
/*
* Workaround for mice which don't ACK the Get ID command.
* These are valid mouse IDs that we recognize.
*/
case 0x00:
case 0x03:
case 0x04:
if (ps2dev->flags & PS2_FLAG_WAITID) {
ps2dev->nak = 0;
break;
}
/* Fall through */
default:
return 1;
}
if (!ps2dev->nak && ps2dev->cmdcnt)
ps2dev->flags |= PS2_FLAG_CMD | PS2_FLAG_CMD1;
ps2dev->flags &= ~PS2_FLAG_ACK;
wake_up_interruptible(&ps2dev->wait);
return data == PS2_RET_ACK || data == PS2_RET_NAK;
}
int ps2_handle_response(struct ps2dev *ps2dev, unsigned char data)
{
if (ps2dev->cmdcnt)
ps2dev->cmdbuf[--ps2dev->cmdcnt] = data;
if (ps2dev->flags & PS2_FLAG_CMD1) {
ps2dev->flags &= ~PS2_FLAG_CMD1;
if (ps2dev->cmdcnt)
wake_up_interruptible(&ps2dev->wait);
}
if (!ps2dev->cmdcnt) {
ps2dev->flags &= ~PS2_FLAG_CMD;
wake_up_interruptible(&ps2dev->wait);
}
return 1;
}
void ps2_cmd_aborted(struct ps2dev *ps2dev)
{
if (ps2dev->flags & PS2_FLAG_ACK)
ps2dev->nak = 1;
if (ps2dev->flags & (PS2_FLAG_ACK | PS2_FLAG_CMD))
wake_up_interruptible(&ps2dev->wait);
ps2dev->flags = 0;
}
#ifndef _LIBPS2_H
#define _LIBPS2_H
/*
* Copyright (C) 1999-2002 Vojtech Pavlik
* Copyright (C) 2004 Dmitry Torokhov
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*/
#define PS2_CMD_GETID 0x02f2
#define PS2_CMD_RESET_BAT 0x02ff
#define PS2_RET_BAT 0xaa
#define PS2_RET_ID 0x00
#define PS2_RET_ACK 0xfa
#define PS2_RET_NAK 0xfe
#define PS2_FLAG_ACK 1 /* Waiting for ACK/NAK */
#define PS2_FLAG_CMD 2 /* Waiting for command to finish */
#define PS2_FLAG_CMD1 4 /* Waiting for the first byte of command response */
#define PS2_FLAG_WAITID 8 /* Command execiting is GET ID */
struct ps2dev {
struct serio *serio;
/* Ensures that only one command is executing at a time */
struct semaphore cmd_sem;
/* Used to signal completion from interrupt handler */
wait_queue_head_t wait;
unsigned long flags;
unsigned char cmdbuf[6];
unsigned char cmdcnt;
unsigned char nak;
};
void ps2_init(struct ps2dev *ps2dev, struct serio *serio);
int ps2_sendbyte(struct ps2dev *ps2dev, unsigned char byte);
int ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command);
int ps2_schedule_command(struct ps2dev *ps2dev, unsigned char *param, int command);
int ps2_handle_ack(struct ps2dev *ps2dev, unsigned char data);
int ps2_handle_response(struct ps2dev *ps2dev, unsigned char data);
void ps2_cmd_aborted(struct ps2dev *ps2dev);
#endif /* _LIBPS2_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment