summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sys/dev/ic/pckbc.c36
1 files changed, 27 insertions, 9 deletions
diff --git a/sys/dev/ic/pckbc.c b/sys/dev/ic/pckbc.c
index 131192585fc..210dc6df406 100644
--- a/sys/dev/ic/pckbc.c
+++ b/sys/dev/ic/pckbc.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: pckbc.c,v 1.51 2019/01/29 18:25:26 mglocker Exp $ */
+/* $OpenBSD: pckbc.c,v 1.52 2019/06/03 16:46:49 anton Exp $ */
/* $NetBSD: pckbc.c,v 1.5 2000/06/09 04:58:35 soda Exp $ */
/*
@@ -59,6 +59,7 @@ struct pckbc_devcmd {
int flags;
#define KBC_CMDFLAG_SYNC 1 /* give descriptor back to caller */
#define KBC_CMDFLAG_SLOW 2
+#define KBC_CMDFLAG_QUEUED 4 /* descriptor on cmdqueue */
u_char cmd[4];
int cmdlen, cmdidx, retries;
u_char response[4];
@@ -699,12 +700,21 @@ pckbc_cleanqueue(struct pckbc_slotdata *q)
while ((cmd = TAILQ_FIRST(&q->cmdqueue))) {
TAILQ_REMOVE(&q->cmdqueue, cmd, next);
+ cmd->flags &= ~KBC_CMDFLAG_QUEUED;
#ifdef PCKBCDEBUG
printf("pckbc_cleanqueue: removing");
for (i = 0; i < cmd->cmdlen; i++)
printf(" %02x", cmd->cmd[i]);
printf("\n");
#endif
+ /*
+ * A synchronous command on the cmdqueue is currently owned by a
+ * sleeping proc. The same proc is responsible for putting it
+ * back on the freequeue once awake.
+ */
+ if (cmd->flags & KBC_CMDFLAG_SYNC)
+ continue;
+
TAILQ_INSERT_TAIL(&q->freequeue, cmd, next);
}
}
@@ -793,15 +803,15 @@ pckbc_start(struct pckbc_internal *t, pckbc_slot_t slot)
if (cmd->status)
printf("pckbc_start: command error\n");
+ TAILQ_REMOVE(&q->cmdqueue, cmd, next);
+ cmd->flags &= ~KBC_CMDFLAG_QUEUED;
if (cmd->flags & KBC_CMDFLAG_SYNC) {
wakeup(cmd);
- cmd = TAILQ_NEXT(cmd, next);
} else {
- TAILQ_REMOVE(&q->cmdqueue, cmd, next);
timeout_del(&t->t_cleanup);
TAILQ_INSERT_TAIL(&q->freequeue, cmd, next);
- cmd = TAILQ_FIRST(&q->cmdqueue);
}
+ cmd = TAILQ_FIRST(&q->cmdqueue);
} while (cmd);
return;
}
@@ -856,15 +866,15 @@ pckbc_cmdresponse(struct pckbc_internal *t, pckbc_slot_t slot, u_char data)
return (0);
/* dequeue: */
+ TAILQ_REMOVE(&q->cmdqueue, cmd, next);
+ cmd->flags &= ~KBC_CMDFLAG_QUEUED;
if (cmd->flags & KBC_CMDFLAG_SYNC) {
wakeup(cmd);
- cmd = TAILQ_NEXT(cmd, next);
} else {
- TAILQ_REMOVE(&q->cmdqueue, cmd, next);
timeout_del(&t->t_cleanup);
TAILQ_INSERT_TAIL(&q->freequeue, cmd, next);
- cmd = TAILQ_FIRST(&q->cmdqueue);
}
+ cmd = TAILQ_FIRST(&q->cmdqueue);
if (cmd == NULL)
return (1);
restart:
@@ -913,6 +923,7 @@ pckbc_enqueue_cmd(pckbc_tag_t self, pckbc_slot_t slot, u_char *cmd, int len,
}
isactive = CMD_IN_QUEUE(q);
+ nc->flags |= KBC_CMDFLAG_QUEUED;
TAILQ_INSERT_TAIL(&q->cmdqueue, nc, next);
if (!isactive)
pckbc_start(t, slot);
@@ -921,10 +932,17 @@ pckbc_enqueue_cmd(pckbc_tag_t self, pckbc_slot_t slot, u_char *cmd, int len,
res = (sync ? nc->status : 0);
else if (sync) {
if ((res = tsleep(nc, 0, "kbccmd", 1*hz))) {
- TAILQ_REMOVE(&q->cmdqueue, nc, next);
pckbc_cleanup(t);
} else {
- TAILQ_REMOVE(&q->cmdqueue, nc, next);
+ /*
+ * Under certain circumstances, such as during suspend,
+ * tsleep() becomes a no-op and the command is left on
+ * the cmdqueue.
+ */
+ if (nc->flags & KBC_CMDFLAG_QUEUED) {
+ TAILQ_REMOVE(&q->cmdqueue, nc, next);
+ nc->flags &= ~KBC_CMDFLAG_QUEUED;
+ }
res = nc->status;
}
} else