lkml.org 
[lkml]   [2023]   [May]   [11]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 6/7] Input: libps2 - introduce common interrupt handler
    Date
    Instead of exposing inner workings of libps2 to drivers such as atkbd and
    psmouse, have them define pre-receive and receive callbacks, and provide a
    common handler that can be used with underlying serio port.

    While at this add kerneldoc to the module.

    Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
    ---
    drivers/input/keyboard/atkbd.c | 73 +++++-----
    drivers/input/mouse/psmouse-base.c | 53 +++----
    drivers/input/serio/libps2.c | 226 ++++++++++++++++++++---------
    include/linux/libps2.h | 61 +++++---
    4 files changed, 259 insertions(+), 154 deletions(-)

    diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c
    index 2fb2ad73e796..8ef663a589b3 100644
    --- a/drivers/input/keyboard/atkbd.c
    +++ b/drivers/input/keyboard/atkbd.c
    @@ -398,47 +398,49 @@ static unsigned int atkbd_compat_scancode(struct atkbd *atkbd, unsigned int code
    return code;
    }

    -/*
    - * atkbd_interrupt(). Here takes place processing of data received from
    - * the keyboard into events.
    - */
    -
    -static irqreturn_t atkbd_interrupt(struct serio *serio, unsigned char data,
    - unsigned int flags)
    +static enum ps2_disposition atkbd_pre_receive_byte(struct ps2dev *ps2dev,
    + u8 data, unsigned int flags)
    {
    - struct atkbd *atkbd = atkbd_from_serio(serio);
    - struct input_dev *dev = atkbd->dev;
    - unsigned int code = data;
    - int scroll = 0, hscroll = 0, click = -1;
    - int value;
    - unsigned short keycode;
    + struct serio *serio = ps2dev->serio;

    dev_dbg(&serio->dev, "Received %02x flags %02x\n", data, flags);

    #if !defined(__i386__) && !defined (__x86_64__)
    - if ((flags & (SERIO_FRAME | SERIO_PARITY)) && (~flags & SERIO_TIMEOUT) && !atkbd->resend && atkbd->write) {
    - dev_warn(&serio->dev, "Frame/parity error: %02x\n", flags);
    - serio_write(serio, ATKBD_CMD_RESEND);
    - atkbd->resend = true;
    - goto out;
    + if ((flags & (SERIO_FRAME | SERIO_PARITY)) &&
    + (~flags & SERIO_TIMEOUT)) {
    + struct atkbd *atkbd = container_of(ps2dev, struct atkbd,
    + ps2dev);
    +
    + if (!atkbd->resend && atkbd->write) {
    + dev_warn(&serio->dev,
    + "Frame/parity error: %02x\n", flags);
    + serio_write(serio, ATKBD_CMD_RESEND);
    + atkbd->resend = true;
    + return PS2_IGNORE;
    + }
    }

    if (!flags && data == ATKBD_RET_ACK)
    atkbd->resend = false;
    #endif

    - if (unlikely(atkbd->ps2dev.flags & PS2_FLAG_ACK))
    - if (ps2_handle_ack(&atkbd->ps2dev, data))
    - goto out;
    + return PS2_PROCESS;
    +}

    - if (unlikely(atkbd->ps2dev.flags & PS2_FLAG_CMD))
    - if (ps2_handle_response(&atkbd->ps2dev, data))
    - goto out;
    +static void atkbd_receive_byte(struct ps2dev *ps2dev, u8 data)
    +{
    + struct serio *serio = ps2dev->serio;
    + struct atkbd *atkbd = container_of(ps2dev, struct atkbd, ps2dev);
    + struct input_dev *dev = atkbd->dev;
    + unsigned int code = data;
    + int scroll = 0, hscroll = 0, click = -1;
    + int value;
    + unsigned short keycode;

    pm_wakeup_event(&serio->dev, 0);

    if (!atkbd->enabled)
    - goto out;
    + return;

    input_event(dev, EV_MSC, MSC_RAW, code);

    @@ -460,16 +462,16 @@ static irqreturn_t atkbd_interrupt(struct serio *serio, unsigned char data,
    case ATKBD_RET_BAT:
    atkbd->enabled = false;
    serio_reconnect(atkbd->ps2dev.serio);
    - goto out;
    + return;
    case ATKBD_RET_EMUL0:
    atkbd->emul = 1;
    - goto out;
    + return;
    case ATKBD_RET_EMUL1:
    atkbd->emul = 2;
    - goto out;
    + return;
    case ATKBD_RET_RELEASE:
    atkbd->release = true;
    - goto out;
    + return;
    case ATKBD_RET_ACK:
    case ATKBD_RET_NAK:
    if (printk_ratelimit())
    @@ -477,18 +479,18 @@ static irqreturn_t atkbd_interrupt(struct serio *serio, unsigned char data,
    "Spurious %s on %s. "
    "Some program might be trying to access hardware directly.\n",
    data == ATKBD_RET_ACK ? "ACK" : "NAK", serio->phys);
    - goto out;
    + return;
    case ATKBD_RET_ERR:
    atkbd->err_count++;
    dev_dbg(&serio->dev, "Keyboard on %s reports too many keys pressed.\n",
    serio->phys);
    - goto out;
    + return;
    }

    code = atkbd_compat_scancode(atkbd, code);

    if (atkbd->emul && --atkbd->emul)
    - goto out;
    + return;

    keycode = atkbd->keycode[code];

    @@ -564,8 +566,6 @@ static irqreturn_t atkbd_interrupt(struct serio *serio, unsigned char data,
    }

    atkbd->release = false;
    -out:
    - return IRQ_HANDLED;
    }

    static int atkbd_set_repeat_rate(struct atkbd *atkbd)
    @@ -1229,7 +1229,8 @@ static int atkbd_connect(struct serio *serio, struct serio_driver *drv)
    goto fail1;

    atkbd->dev = dev;
    - ps2_init(&atkbd->ps2dev, serio);
    + ps2_init(&atkbd->ps2dev, serio,
    + atkbd_pre_receive_byte, atkbd_receive_byte);
    INIT_DELAYED_WORK(&atkbd->event_work, atkbd_event_work);
    mutex_init(&atkbd->mutex);

    @@ -1385,7 +1386,7 @@ static struct serio_driver atkbd_drv = {
    },
    .description = DRIVER_DESC,
    .id_table = atkbd_serio_ids,
    - .interrupt = atkbd_interrupt,
    + .interrupt = ps2_interrupt,
    .connect = atkbd_connect,
    .reconnect = atkbd_reconnect,
    .disconnect = atkbd_disconnect,
    diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c
    index ed5376099fba..a0aac76b1e41 100644
    --- a/drivers/input/mouse/psmouse-base.c
    +++ b/drivers/input/mouse/psmouse-base.c
    @@ -336,17 +336,14 @@ static void psmouse_handle_oob_data(struct psmouse *psmouse, u8 data)
    }
    }

    -/*
    - * psmouse_interrupt() handles incoming characters, either passing them
    - * for normal processing or gathering them as command response.
    - */
    -static irqreturn_t psmouse_interrupt(struct serio *serio,
    - u8 data, unsigned int flags)
    +static enum ps2_disposition psmouse_pre_receive_byte(struct ps2dev *ps2dev,
    + u8 data,
    + unsigned int flags)
    {
    - struct psmouse *psmouse = psmouse_from_serio(serio);
    + struct psmouse *psmouse = container_of(ps2dev, struct psmouse, ps2dev);

    if (psmouse->state == PSMOUSE_IGNORE)
    - goto out;
    + return PS2_IGNORE;

    if (unlikely((flags & SERIO_TIMEOUT) ||
    ((flags & SERIO_PARITY) &&
    @@ -357,27 +354,25 @@ static irqreturn_t psmouse_interrupt(struct serio *serio,
    "bad data from KBC -%s%s\n",
    flags & SERIO_TIMEOUT ? " timeout" : "",
    flags & SERIO_PARITY ? " bad parity" : "");
    - ps2_cmd_aborted(&psmouse->ps2dev);
    - goto out;
    + return PS2_ERROR;
    }

    if (flags & SERIO_OOB_DATA) {
    psmouse_handle_oob_data(psmouse, data);
    - goto out;
    + return PS2_IGNORE;
    }

    - if (unlikely(psmouse->ps2dev.flags & PS2_FLAG_ACK))
    - if (ps2_handle_ack(&psmouse->ps2dev, data))
    - goto out;
    + return PS2_PROCESS;
    +}

    - if (unlikely(psmouse->ps2dev.flags & PS2_FLAG_CMD))
    - if (ps2_handle_response(&psmouse->ps2dev, data))
    - goto out;
    +static void psmouse_receive_byte(struct ps2dev *ps2dev, u8 data)
    +{
    + struct psmouse *psmouse = container_of(ps2dev, struct psmouse, ps2dev);

    - pm_wakeup_event(&serio->dev, 0);
    + pm_wakeup_event(&ps2dev->serio->dev, 0);

    if (psmouse->state <= PSMOUSE_RESYNCING)
    - goto out;
    + return;

    if (psmouse->state == PSMOUSE_ACTIVATED &&
    psmouse->pktcnt && time_after(jiffies, psmouse->last + HZ/2)) {
    @@ -386,7 +381,7 @@ static irqreturn_t psmouse_interrupt(struct serio *serio,
    psmouse->badbyte = psmouse->packet[0];
    __psmouse_set_state(psmouse, PSMOUSE_RESYNCING);
    psmouse_queue_work(psmouse, &psmouse->resync_work, 0);
    - goto out;
    + return;
    }

    psmouse->packet[psmouse->pktcnt++] = data;
    @@ -395,21 +390,21 @@ static irqreturn_t psmouse_interrupt(struct serio *serio,
    if (unlikely(psmouse->packet[0] == PSMOUSE_RET_BAT && psmouse->pktcnt <= 2)) {
    if (psmouse->pktcnt == 1) {
    psmouse->last = jiffies;
    - goto out;
    + return;
    }

    if (psmouse->packet[1] == PSMOUSE_RET_ID ||
    (psmouse->protocol->type == PSMOUSE_HGPK &&
    psmouse->packet[1] == PSMOUSE_RET_BAT)) {
    __psmouse_set_state(psmouse, PSMOUSE_IGNORE);
    - serio_reconnect(serio);
    - goto out;
    + serio_reconnect(ps2dev->serio);
    + return;
    }

    /* Not a new device, try processing first byte normally */
    psmouse->pktcnt = 1;
    if (psmouse_handle_byte(psmouse))
    - goto out;
    + return;

    psmouse->packet[psmouse->pktcnt++] = data;
    }
    @@ -424,14 +419,11 @@ static irqreturn_t psmouse_interrupt(struct serio *serio,
    psmouse->badbyte = psmouse->packet[0];
    __psmouse_set_state(psmouse, PSMOUSE_RESYNCING);
    psmouse_queue_work(psmouse, &psmouse->resync_work, 0);
    - goto out;
    + return;
    }

    psmouse->last = jiffies;
    psmouse_handle_byte(psmouse);
    -
    - out:
    - return IRQ_HANDLED;
    }

    /*
    @@ -1604,7 +1596,8 @@ static int psmouse_connect(struct serio *serio, struct serio_driver *drv)
    if (!psmouse || !input_dev)
    goto err_free;

    - ps2_init(&psmouse->ps2dev, serio);
    + ps2_init(&psmouse->ps2dev, serio,
    + psmouse_pre_receive_byte, psmouse_receive_byte);
    INIT_DELAYED_WORK(&psmouse->resync_work, psmouse_resync);
    psmouse->dev = input_dev;
    snprintf(psmouse->phys, sizeof(psmouse->phys), "%s/input0", serio->phys);
    @@ -1786,7 +1779,7 @@ static struct serio_driver psmouse_drv = {
    },
    .description = DRIVER_DESC,
    .id_table = psmouse_serio_ids,
    - .interrupt = psmouse_interrupt,
    + .interrupt = ps2_interrupt,
    .connect = psmouse_connect,
    .reconnect = psmouse_reconnect,
    .fast_reconnect = psmouse_fast_reconnect,
    diff --git a/drivers/input/serio/libps2.c b/drivers/input/serio/libps2.c
    index 09eb605364bb..7c5fc853072a 100644
    --- a/drivers/input/serio/libps2.c
    +++ b/drivers/input/serio/libps2.c
    @@ -19,9 +19,22 @@

    #define DRIVER_DESC "PS/2 driver library"

    -MODULE_AUTHOR("Dmitry Torokhov <dtor@mail.ru>");
    -MODULE_DESCRIPTION("PS/2 driver library");
    -MODULE_LICENSE("GPL");
    +#define PS2_CMD_SETSCALE11 0x00e6
    +#define PS2_CMD_SETRES 0x10e8
    +#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_RET_ERR 0xfc
    +
    +#define PS2_FLAG_ACK BIT(0) /* Waiting for ACK/NAK */
    +#define PS2_FLAG_CMD BIT(1) /* Waiting for a command to finish */
    +#define PS2_FLAG_CMD1 BIT(2) /* Waiting for the first byte of command response */
    +#define PS2_FLAG_WAITID BIT(3) /* Command executing is GET ID */
    +#define PS2_FLAG_NAK BIT(4) /* Last transmission was NAKed */

    static int ps2_do_sendbyte(struct ps2dev *ps2dev, u8 byte,
    unsigned int timeout, unsigned int max_attempts)
    @@ -76,14 +89,17 @@ static int ps2_do_sendbyte(struct ps2dev *ps2dev, u8 byte,
    return error;
    }

    -/*
    - * ps2_sendbyte() sends a byte to the device and waits for acknowledge.
    - * It doesn't handle retransmission, the caller is expected to handle
    +/**
    + * ps2_sendbyte - sends a byte to the device and wait for acknowledgement
    + * @ps2dev: a PS/2 device to send the data to
    + * @byte: data to be sent to the device
    + * @timeout: timeout for sending the data and receiving an acknowledge
    + *
    + * The function doesn't handle retransmission, the caller is expected to handle
    * it when needed.
    *
    * ps2_sendbyte() can only be called from a process context.
    */
    -
    int ps2_sendbyte(struct ps2dev *ps2dev, u8 byte, unsigned int timeout)
    {
    int retval;
    @@ -99,6 +115,13 @@ int ps2_sendbyte(struct ps2dev *ps2dev, u8 byte, unsigned int timeout)
    }
    EXPORT_SYMBOL(ps2_sendbyte);

    +/**
    + * ps2_begin_command - mark beginning of execution of a complex command
    + * @ps2dev: a PS/2 device executing the command
    + *
    + * Serializes a complex/compound command. Once command is finished
    + * ps2_end_command() should be called.
    + */
    void ps2_begin_command(struct ps2dev *ps2dev)
    {
    struct mutex *m = ps2dev->serio->ps2_cmd_mutex ?: &ps2dev->cmd_mutex;
    @@ -107,6 +130,10 @@ void ps2_begin_command(struct ps2dev *ps2dev)
    }
    EXPORT_SYMBOL(ps2_begin_command);

    +/**
    + * ps2_end_command - mark end of execution of a complex command
    + * @ps2dev: a PS/2 device executing the command
    + */
    void ps2_end_command(struct ps2dev *ps2dev)
    {
    struct mutex *m = ps2dev->serio->ps2_cmd_mutex ?: &ps2dev->cmd_mutex;
    @@ -115,11 +142,13 @@ void ps2_end_command(struct ps2dev *ps2dev)
    }
    EXPORT_SYMBOL(ps2_end_command);

    -/*
    - * ps2_drain() waits for device to transmit requested number of bytes
    - * and discards them.
    +/**
    + * ps2_drain - waits for device to transmit requested number of bytes
    + * and discards them
    + * @ps2dev: the PS/2 device that should be drained
    + * @maxbytes: maximum number of bytes to be drained
    + * @timeout: time to drain the device
    */
    -
    void ps2_drain(struct ps2dev *ps2dev, size_t maxbytes, unsigned int timeout)
    {
    if (maxbytes > sizeof(ps2dev->cmdbuf)) {
    @@ -142,11 +171,11 @@ void ps2_drain(struct ps2dev *ps2dev, size_t maxbytes, unsigned int timeout)
    }
    EXPORT_SYMBOL(ps2_drain);

    -/*
    - * ps2_is_keyboard_id() checks received ID byte against the list of
    - * known keyboard IDs.
    +/**
    + * ps2_is_keyboard_id - checks received ID byte against the list of
    + * known keyboard IDs
    + * @id_byte: data byte that should be checked
    */
    -
    bool ps2_is_keyboard_id(u8 id_byte)
    {
    static const u8 keyboard_ids[] = {
    @@ -167,7 +196,6 @@ EXPORT_SYMBOL(ps2_is_keyboard_id);
    * response and tries to reduce remaining timeout to speed up command
    * completion.
    */
    -
    static int ps2_adjust_timeout(struct ps2dev *ps2dev,
    unsigned int command, unsigned int timeout)
    {
    @@ -217,13 +245,19 @@ static int ps2_adjust_timeout(struct ps2dev *ps2dev,
    return timeout;
    }

    -/*
    - * 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 - send a command to PS/2 device
    + * @ps2dev: the PS/2 device that should execute the command
    + * @param: a buffer containing parameters to be sent along with the command,
    + * or place where the results of the command execution will be deposited,
    + * or both
    + * @command: command word that encodes the command itself, as well as number of
    + * additional parameter bytes that should be sent to the device and expected
    + * length of the command response
    *
    - * ps2_command() can only be called from a process context
    + * Not serialized. Callers should use ps2_begin_command() and ps2_end_command()
    + * to ensure proper serialization for complex commands.
    */
    -
    int __ps2_command(struct ps2dev *ps2dev, u8 *param, unsigned int command)
    {
    unsigned int timeout;
    @@ -327,6 +361,20 @@ int __ps2_command(struct ps2dev *ps2dev, u8 *param, unsigned int command)
    }
    EXPORT_SYMBOL(__ps2_command);

    +/**
    + * ps2_command - send a command to PS/2 device
    + * @ps2dev: the PS/2 device that should execute the command
    + * @param: a buffer containing parameters to be sent along with the command,
    + * or place where the results of the command execution will be deposited,
    + * or both
    + * @command: command word that encodes the command itself, as well as number of
    + * additional parameter bytes that should be sent to the device and expected
    + * length of the command response
    + *
    + * Note: ps2_command() serializes the command execution so that only one
    + * command can be executed at a time for either individual port or the entire
    + * 8042 controller.
    + */
    int ps2_command(struct ps2dev *ps2dev, u8 *param, unsigned int command)
    {
    int rc;
    @@ -339,14 +387,16 @@ int ps2_command(struct ps2dev *ps2dev, u8 *param, unsigned int command)
    }
    EXPORT_SYMBOL(ps2_command);

    -/*
    - * ps2_sliced_command() sends an extended PS/2 command to the mouse
    - * using sliced syntax, understood by advanced devices, such as Logitech
    - * or Synaptics touchpads. The command is encoded as:
    +/**
    + * ps2_sliced_command - sends an extended PS/2 command to a mouse
    + * @ps2dev: the PS/2 device that should execute the command
    + * @command: command byte
    + *
    + * The command is sent using "sliced" syntax understood by advanced devices,
    + * such as Logitech or Synaptics touchpads. The command is encoded as:
    * 0xE6 0xE8 rr 0xE8 ss 0xE8 tt 0xE8 uu where (rr*64)+(ss*16)+(tt*4)+uu
    * is the command.
    */
    -
    int ps2_sliced_command(struct ps2dev *ps2dev, u8 command)
    {
    int i;
    @@ -372,12 +422,22 @@ int ps2_sliced_command(struct ps2dev *ps2dev, u8 command)
    }
    EXPORT_SYMBOL(ps2_sliced_command);

    -/*
    - * ps2_init() initializes ps2dev structure
    +/**
    + * ps2_init - initializes ps2dev structure
    + * @ps2dev: structure to be initialized
    + * @serio: serio port associated with the PS/2 device
    + * @pre_receive_handler: validation handler to check basic communication state
    + * @receive_handler: main protocol handler
    + *
    + * Prepares ps2dev structure for use in drivers for PS/2 devices.
    */
    -
    -void ps2_init(struct ps2dev *ps2dev, struct serio *serio)
    +void ps2_init(struct ps2dev *ps2dev, struct serio *serio,
    + ps2_pre_receive_handler_t pre_receive_handler,
    + ps2_receive_handler_t receive_handler)
    {
    + ps2dev->pre_receive_handler = pre_receive_handler;
    + ps2dev->receive_handler = receive_handler;
    +
    mutex_init(&ps2dev->cmd_mutex);
    lockdep_set_subclass(&ps2dev->cmd_mutex, serio->depth);
    init_waitqueue_head(&ps2dev->wait);
    @@ -387,11 +447,35 @@ void ps2_init(struct ps2dev *ps2dev, struct serio *serio)
    EXPORT_SYMBOL(ps2_init);

    /*
    - * ps2_handle_ack() is supposed to be used in interrupt handler
    - * to properly process ACK/NAK of a command from a PS/2 device.
    + * ps2_handle_response() stores device's response to a command and notifies
    + * the process waiting for completion of the command. Note that there is a
    + * distinction between waiting for the first byte of the response, and
    + * waiting for subsequent bytes. It is done so that callers could shorten
    + * timeouts once first byte of response is received.
    */
    +static void ps2_handle_response(struct ps2dev *ps2dev, u8 data)
    +{
    + if (ps2dev->cmdcnt)
    + ps2dev->cmdbuf[--ps2dev->cmdcnt] = data;

    -bool ps2_handle_ack(struct ps2dev *ps2dev, u8 data)
    + if (ps2dev->flags & PS2_FLAG_CMD1) {
    + ps2dev->flags &= ~PS2_FLAG_CMD1;
    + if (ps2dev->cmdcnt)
    + wake_up(&ps2dev->wait);
    + }
    +
    + if (!ps2dev->cmdcnt) {
    + ps2dev->flags &= ~PS2_FLAG_CMD;
    + wake_up(&ps2dev->wait);
    + }
    +}
    +
    +/*
    + * ps2_handle_ack() processes ACK/NAK of a command from a PS/2 device,
    + * possibly applying workarounds for mice not acknowledging the "get ID"
    + * command.
    + */
    +static void ps2_handle_ack(struct ps2dev *ps2dev, u8 data)
    {
    switch (data) {
    case PS2_RET_ACK:
    @@ -436,53 +520,25 @@ bool ps2_handle_ack(struct ps2dev *ps2dev, u8 data)
    */
    dev_dbg(&ps2dev->serio->dev, "unexpected %#02x\n", data);
    ps2dev->flags &= ~PS2_FLAG_WAITID;
    - return true;
    + return;
    }

    if (!ps2dev->nak)
    ps2dev->flags &= ~PS2_FLAG_NAK;

    ps2dev->flags &= ~PS2_FLAG_ACK;
    - wake_up(&ps2dev->wait);

    if (!ps2dev->nak && data != PS2_RET_ACK)
    ps2_handle_response(ps2dev, data);
    -
    - return true;
    -}
    -EXPORT_SYMBOL(ps2_handle_ack);
    -
    -/*
    - * ps2_handle_response() is supposed to be used in interrupt handler
    - * to properly store device's response to a command and notify process
    - * waiting for completion of the command.
    - */
    -
    -bool ps2_handle_response(struct ps2dev *ps2dev, u8 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(&ps2dev->wait);
    - }
    -
    - if (!ps2dev->cmdcnt) {
    - ps2dev->flags &= ~PS2_FLAG_CMD;
    + else
    wake_up(&ps2dev->wait);
    - }
    -
    - return true;
    }
    -EXPORT_SYMBOL(ps2_handle_response);

    /*
    * Clears state of PS/2 device after communication error by resetting majority
    * of flags and waking up waiters, if any.
    */
    -void ps2_cmd_aborted(struct ps2dev *ps2dev)
    +static void ps2_cleanup(struct ps2dev *ps2dev)
    {
    unsigned long old_flags = ps2dev->flags;

    @@ -494,6 +550,46 @@ void ps2_cmd_aborted(struct ps2dev *ps2dev)

    if (old_flags & (PS2_FLAG_ACK | PS2_FLAG_CMD))
    wake_up(&ps2dev->wait);
    +}

    +/**
    + * ps2_interrupt - common interrupt handler for PS/2 devices
    + * @serio: serio port for the device
    + * @data: a data byte received from the device
    + * @flags: flags such as %SERIO_PARITY or %SERIO_TIMEOUT indicating state of
    + * the data transfer
    + *
    + * ps2_interrupt() invokes pre-receive handler, optionally handles command
    + * acknowledgement and response from the device, and finally passes the data
    + * to the main protocol handler for future processing.
    + */
    +irqreturn_t ps2_interrupt(struct serio *serio, u8 data, unsigned int flags) {
    + struct ps2dev *ps2dev = serio_get_drvdata(serio);
    + enum ps2_disposition rc;
    +
    + rc = ps2dev->pre_receive_handler(ps2dev, data, flags);
    + switch (rc) {
    + case PS2_ERROR:
    + ps2_cleanup(ps2dev);
    + break;
    +
    + case PS2_IGNORE:
    + break;
    +
    + case PS2_PROCESS:
    + if (ps2dev->flags & PS2_FLAG_ACK)
    + ps2_handle_ack(ps2dev, data);
    + else if (ps2dev->flags & PS2_FLAG_CMD)
    + ps2_handle_response(ps2dev, data);
    + else
    + ps2dev->receive_handler(ps2dev, data);
    + break;
    + }
    +
    + return IRQ_HANDLED;
    }
    -EXPORT_SYMBOL(ps2_cmd_aborted);
    +EXPORT_SYMBOL(ps2_interrupt);
    +
    +MODULE_AUTHOR("Dmitry Torokhov <dtor@mail.ru>");
    +MODULE_DESCRIPTION("PS/2 driver library");
    +MODULE_LICENSE("GPL");
    diff --git a/include/linux/libps2.h b/include/linux/libps2.h
    index 193dd53ad18b..9ca9ce4e6e64 100644
    --- a/include/linux/libps2.h
    +++ b/include/linux/libps2.h
    @@ -8,43 +8,59 @@
    */

    #include <linux/bitops.h>
    +#include <linux/interrupt.h>
    #include <linux/mutex.h>
    #include <linux/types.h>
    #include <linux/wait.h>

    -#define PS2_CMD_SETSCALE11 0x00e6
    -#define PS2_CMD_SETRES 0x10e8
    -#define PS2_CMD_GETID 0x02f2
    -#define PS2_CMD_RESET_BAT 0x02ff
    +struct ps2dev;

    -#define PS2_RET_BAT 0xaa
    -#define PS2_RET_ID 0x00
    -#define PS2_RET_ACK 0xfa
    -#define PS2_RET_NAK 0xfe
    -#define PS2_RET_ERR 0xfc
    +/**
    + * enum ps2_disposition - indicates how received byte should be handled
    + * @PS2_PROCESS: pass to the main protocol handler, process normally
    + * @PS2_IGNORE: skip the byte
    + * @PS2_ERROR: do not process the byte, abort command in progress
    + */
    +enum ps2_disposition {
    + PS2_PROCESS,
    + PS2_IGNORE,
    + PS2_ERROR,
    +};

    -#define PS2_FLAG_ACK BIT(0) /* Waiting for ACK/NAK */
    -#define PS2_FLAG_CMD BIT(1) /* Waiting for a command to finish */
    -#define PS2_FLAG_CMD1 BIT(2) /* Waiting for the first byte of command response */
    -#define PS2_FLAG_WAITID BIT(3) /* Command executing is GET ID */
    -#define PS2_FLAG_NAK BIT(4) /* Last transmission was NAKed */
    +typedef enum ps2_disposition (*ps2_pre_receive_handler_t)(struct ps2dev *, u8,
    + unsigned int);
    +typedef void (*ps2_receive_handler_t)(struct ps2dev *, u8);

    +/**
    + * struct ps2dev - represents a device using PS/2 protocol
    + * @serio: a serio port used by the PS/2 device
    + * @cmd_mutex: a mutex ensuring that only one command is executing at a time
    + * @wait: a waitqueue used to signal completion from the serio interrupt handler
    + * @flags: various internal flags indicating stages of PS/2 command execution
    + * @cmdbuf: buffer holding command response
    + * @cmdcnt: outstanding number of bytes of the command response
    + * @nak: a byte transmitted by the device when it refuses command
    + * @pre_receive_handler: checks communication errors and returns disposition
    + * (&enum ps2_disposition) of the received data byte
    + * @receive_handler: main handler of particular PS/2 protocol, such as keyboard
    + * or mouse protocol
    + */
    struct ps2dev {
    struct serio *serio;
    -
    - /* Ensures that only one command is executing at a time */
    struct mutex cmd_mutex;
    -
    - /* Used to signal completion from interrupt handler */
    wait_queue_head_t wait;
    -
    unsigned long flags;
    u8 cmdbuf[8];
    u8 cmdcnt;
    u8 nak;
    +
    + ps2_pre_receive_handler_t pre_receive_handler;
    + ps2_receive_handler_t receive_handler;
    };

    -void ps2_init(struct ps2dev *ps2dev, struct serio *serio);
    +void ps2_init(struct ps2dev *ps2dev, struct serio *serio,
    + ps2_pre_receive_handler_t pre_receive_handler,
    + ps2_receive_handler_t receive_handler);
    int ps2_sendbyte(struct ps2dev *ps2dev, u8 byte, unsigned int timeout);
    void ps2_drain(struct ps2dev *ps2dev, size_t maxbytes, unsigned int timeout);
    void ps2_begin_command(struct ps2dev *ps2dev);
    @@ -52,9 +68,8 @@ void ps2_end_command(struct ps2dev *ps2dev);
    int __ps2_command(struct ps2dev *ps2dev, u8 *param, unsigned int command);
    int ps2_command(struct ps2dev *ps2dev, u8 *param, unsigned int command);
    int ps2_sliced_command(struct ps2dev *ps2dev, u8 command);
    -bool ps2_handle_ack(struct ps2dev *ps2dev, u8 data);
    -bool ps2_handle_response(struct ps2dev *ps2dev, u8 data);
    -void ps2_cmd_aborted(struct ps2dev *ps2dev);
    bool ps2_is_keyboard_id(u8 id);

    +irqreturn_t ps2_interrupt(struct serio *serio, u8 data, unsigned int flags);
    +
    #endif /* _LIBPS2_H */
    --
    2.40.1.606.ga4b1b128d6-goog
    \
     
     \ /
      Last update: 2023-05-11 20:54    [W:2.468 / U:0.824 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site