| From 879bc2d27904354b98ca295b6168718e045c4aa2 Mon Sep 17 00:00:00 2001 |
| From: Helge Deller <deller@gmx.de> |
| Date: Mon, 19 Oct 2020 16:57:50 +0200 |
| Subject: hil/parisc: Disable HIL driver when it gets stuck |
| |
| From: Helge Deller <deller@gmx.de> |
| |
| commit 879bc2d27904354b98ca295b6168718e045c4aa2 upstream. |
| |
| When starting a HP machine with HIL driver but without an HIL keyboard |
| or HIL mouse attached, it may happen that data written to the HIL loop |
| gets stuck (e.g. because the transaction queue is full). Usually one |
| will then have to reboot the machine because all you see is and endless |
| output of: |
| Transaction add failed: transaction already queued? |
| |
| In the higher layers hp_sdc_enqueue_transaction() is called to queued up |
| a HIL packet. This function returns an error code, and this patch adds |
| the necessary checks for this return code and disables the HIL driver if |
| further packets can't be sent. |
| |
| Tested on a HP 730 and a HP 715/64 machine. |
| |
| Signed-off-by: Helge Deller <deller@gmx.de> |
| Cc: <stable@vger.kernel.org> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| drivers/input/serio/hil_mlc.c | 21 ++++++++++++++++++--- |
| drivers/input/serio/hp_sdc_mlc.c | 8 ++++---- |
| include/linux/hil_mlc.h | 2 +- |
| 3 files changed, 23 insertions(+), 8 deletions(-) |
| |
| --- a/drivers/input/serio/hil_mlc.c |
| +++ b/drivers/input/serio/hil_mlc.c |
| @@ -74,7 +74,7 @@ EXPORT_SYMBOL(hil_mlc_unregister); |
| static LIST_HEAD(hil_mlcs); |
| static DEFINE_RWLOCK(hil_mlcs_lock); |
| static struct timer_list hil_mlcs_kicker; |
| -static int hil_mlcs_probe; |
| +static int hil_mlcs_probe, hil_mlc_stop; |
| |
| static void hil_mlcs_process(unsigned long unused); |
| static DECLARE_TASKLET_DISABLED_OLD(hil_mlcs_tasklet, hil_mlcs_process); |
| @@ -702,9 +702,13 @@ static int hilse_donode(hil_mlc *mlc) |
| if (!mlc->ostarted) { |
| mlc->ostarted = 1; |
| mlc->opacket = pack; |
| - mlc->out(mlc); |
| + rc = mlc->out(mlc); |
| nextidx = HILSEN_DOZE; |
| write_unlock_irqrestore(&mlc->lock, flags); |
| + if (rc) { |
| + hil_mlc_stop = 1; |
| + return 1; |
| + } |
| break; |
| } |
| mlc->ostarted = 0; |
| @@ -715,8 +719,13 @@ static int hilse_donode(hil_mlc *mlc) |
| |
| case HILSE_CTS: |
| write_lock_irqsave(&mlc->lock, flags); |
| - nextidx = mlc->cts(mlc) ? node->bad : node->good; |
| + rc = mlc->cts(mlc); |
| + nextidx = rc ? node->bad : node->good; |
| write_unlock_irqrestore(&mlc->lock, flags); |
| + if (rc) { |
| + hil_mlc_stop = 1; |
| + return 1; |
| + } |
| break; |
| |
| default: |
| @@ -780,6 +789,12 @@ static void hil_mlcs_process(unsigned lo |
| |
| static void hil_mlcs_timer(struct timer_list *unused) |
| { |
| + if (hil_mlc_stop) { |
| + /* could not send packet - stop immediately. */ |
| + pr_warn(PREFIX "HIL seems stuck - Disabling HIL MLC.\n"); |
| + return; |
| + } |
| + |
| hil_mlcs_probe = 1; |
| tasklet_schedule(&hil_mlcs_tasklet); |
| /* Re-insert the periodic task. */ |
| --- a/drivers/input/serio/hp_sdc_mlc.c |
| +++ b/drivers/input/serio/hp_sdc_mlc.c |
| @@ -210,7 +210,7 @@ static int hp_sdc_mlc_cts(hil_mlc *mlc) |
| priv->tseq[2] = 1; |
| priv->tseq[3] = 0; |
| priv->tseq[4] = 0; |
| - __hp_sdc_enqueue_transaction(&priv->trans); |
| + return __hp_sdc_enqueue_transaction(&priv->trans); |
| busy: |
| return 1; |
| done: |
| @@ -219,7 +219,7 @@ static int hp_sdc_mlc_cts(hil_mlc *mlc) |
| return 0; |
| } |
| |
| -static void hp_sdc_mlc_out(hil_mlc *mlc) |
| +static int hp_sdc_mlc_out(hil_mlc *mlc) |
| { |
| struct hp_sdc_mlc_priv_s *priv; |
| |
| @@ -234,7 +234,7 @@ static void hp_sdc_mlc_out(hil_mlc *mlc) |
| do_data: |
| if (priv->emtestmode) { |
| up(&mlc->osem); |
| - return; |
| + return 0; |
| } |
| /* Shouldn't be sending commands when loop may be busy */ |
| BUG_ON(down_trylock(&mlc->csem)); |
| @@ -296,7 +296,7 @@ static void hp_sdc_mlc_out(hil_mlc *mlc) |
| BUG_ON(down_trylock(&mlc->csem)); |
| } |
| enqueue: |
| - hp_sdc_enqueue_transaction(&priv->trans); |
| + return hp_sdc_enqueue_transaction(&priv->trans); |
| } |
| |
| static int __init hp_sdc_mlc_init(void) |
| --- a/include/linux/hil_mlc.h |
| +++ b/include/linux/hil_mlc.h |
| @@ -103,7 +103,7 @@ struct hilse_node { |
| |
| /* Methods for back-end drivers, e.g. hp_sdc_mlc */ |
| typedef int (hil_mlc_cts) (hil_mlc *mlc); |
| -typedef void (hil_mlc_out) (hil_mlc *mlc); |
| +typedef int (hil_mlc_out) (hil_mlc *mlc); |
| typedef int (hil_mlc_in) (hil_mlc *mlc, suseconds_t timeout); |
| |
| struct hil_mlc_devinfo { |