summaryrefslogtreecommitdiffstats
path: root/sys/powerpc
diff options
context:
space:
mode:
authornwhitehorn <nwhitehorn@FreeBSD.org>2008-12-06 23:26:02 +0000
committernwhitehorn <nwhitehorn@FreeBSD.org>2008-12-06 23:26:02 +0000
commite6bc656165feaa55eb1bb3b7ba07f1e02c7e307b (patch)
treec72b76b735b5bc154e27235b50a32c00ae045050 /sys/powerpc
parenta689f483835ae8389214b38c9fce209311c1a019 (diff)
downloadFreeBSD-src-e6bc656165feaa55eb1bb3b7ba07f1e02c7e307b.zip
FreeBSD-src-e6bc656165feaa55eb1bb3b7ba07f1e02c7e307b.tar.gz
Fix some nasty race conditions in the VIA-CUDA driver that ended up preventing
my right mouse button and keyboard LEDs from working due to mangled configuration packets. Fixed several other races and associated problems in the main ADB stack that were exposed while fixing this.
Diffstat (limited to 'sys/powerpc')
-rw-r--r--sys/powerpc/powermac/cuda.c229
-rw-r--r--sys/powerpc/powermac/cudavar.h36
2 files changed, 175 insertions, 90 deletions
diff --git a/sys/powerpc/powermac/cuda.c b/sys/powerpc/powermac/cuda.c
index c17ca82..274bb98 100644
--- a/sys/powerpc/powermac/cuda.c
+++ b/sys/powerpc/powermac/cuda.c
@@ -65,10 +65,12 @@ static int cuda_probe(device_t);
static int cuda_attach(device_t);
static int cuda_detach(device_t);
-static u_int cuda_adb_send(device_t dev, u_char command_byte, int len,
+static u_int cuda_adb_send(device_t dev, u_char command_byte, int len,
u_char *data, u_char poll);
-static u_int cuda_adb_autopoll(device_t dev, uint16_t mask);
-static void cuda_poll(device_t dev);
+static u_int cuda_adb_autopoll(device_t dev, uint16_t mask);
+static void cuda_poll(device_t dev);
+static void cuda_send_inbound(struct cuda_softc *sc);
+static void cuda_send_outbound(struct cuda_softc *sc);
static device_method_t cuda_methods[] = {
/* Device interface */
@@ -102,6 +104,8 @@ static devclass_t cuda_devclass;
DRIVER_MODULE(cuda, macio, cuda_driver, cuda_devclass, 0, 0);
DRIVER_MODULE(adb, cuda, adb_driver, adb_devclass, 0, 0);
+MALLOC_DEFINE(M_CUDA, "cuda", "CUDA packet queue");
+
static void cuda_intr(void *arg);
static uint8_t cuda_read_reg(struct cuda_softc *sc, u_int offset);
static void cuda_write_reg(struct cuda_softc *sc, u_int offset, uint8_t value);
@@ -170,9 +174,11 @@ cuda_attach(device_t dev)
sc->sc_waiting = 0;
sc->sc_polling = 0;
sc->sc_state = CUDA_NOTREADY;
- sc->sc_error = 0;
sc->sc_autopoll = 0;
+ STAILQ_INIT(&sc->sc_inq);
+ STAILQ_INIT(&sc->sc_outq);
+
/* Init CUDA */
reg = cuda_read_reg(sc, vDirB);
@@ -335,42 +341,126 @@ cuda_send(void *cookie, int poll, int length, uint8_t *msg)
{
struct cuda_softc *sc = cookie;
device_t dev = sc->sc_dev;
+ struct cuda_packet *pkt;
if (sc->sc_state == CUDA_NOTREADY)
- return -1;
+ return (-1);
mtx_lock(&sc->sc_mutex);
- if (sc->sc_state != CUDA_IDLE) {
- if (sc->sc_waiting == 0) {
- sc->sc_waiting = 1;
- } else {
- mtx_unlock(&sc->sc_mutex);
- return -1;
- }
+ pkt = malloc(sizeof(struct cuda_packet), M_CUDA, M_WAITOK);
+ pkt->len = length - 1;
+ pkt->type = msg[0];
+ memcpy(pkt->data, &msg[1], pkt->len);
+
+ STAILQ_INSERT_TAIL(&sc->sc_outq, pkt, pkt_q);
+
+ /*
+ * If we already are sending a packet, we should bail now that this
+ * one has been added to the queue.
+ */
+
+ if (sc->sc_waiting) {
+ mtx_unlock(&sc->sc_mutex);
+ return (0);
}
- sc->sc_error = 0;
- memcpy(sc->sc_out, msg, length);
- sc->sc_out_length = length;
+ cuda_send_outbound(sc);
+ mtx_unlock(&sc->sc_mutex);
+
+ if (sc->sc_polling || poll || cold)
+ cuda_poll(dev);
+
+ return (0);
+}
+
+static void
+cuda_send_outbound(struct cuda_softc *sc)
+{
+ struct cuda_packet *pkt;
+
+ mtx_assert(&sc->sc_mutex, MA_OWNED);
+
+ pkt = STAILQ_FIRST(&sc->sc_outq);
+ if (pkt == NULL)
+ return;
+
+ sc->sc_out_length = pkt->len + 1;
+ memcpy(sc->sc_out, &pkt->type, pkt->len + 1);
sc->sc_sent = 0;
- if (sc->sc_waiting != 1) {
- DELAY(150);
+ free(pkt, M_CUDA);
+ STAILQ_REMOVE_HEAD(&sc->sc_outq, pkt_q);
+
+ sc->sc_waiting = 1;
+
+ cuda_poll(sc->sc_dev);
+
+ DELAY(150);
+
+ if (sc->sc_state == CUDA_IDLE && !cuda_intr_state(sc)) {
sc->sc_state = CUDA_OUT;
cuda_out(sc);
cuda_write_reg(sc, vSR, sc->sc_out[0]);
cuda_ack_off(sc);
cuda_tip(sc);
}
- sc->sc_waiting = 1;
- mtx_unlock(&sc->sc_mutex);
+}
- if (sc->sc_polling || poll || cold) {
- cuda_poll(dev);
+static void
+cuda_send_inbound(struct cuda_softc *sc)
+{
+ device_t dev;
+ struct cuda_packet *pkt;
+
+ dev = sc->sc_dev;
+
+ mtx_lock(&sc->sc_mutex);
+
+ while ((pkt = STAILQ_FIRST(&sc->sc_inq)) != NULL) {
+ STAILQ_REMOVE_HEAD(&sc->sc_inq, pkt_q);
+
+ mtx_unlock(&sc->sc_mutex);
+
+ /* check if we have a handler for this message */
+ switch (pkt->type) {
+ case CUDA_ADB:
+ if (pkt->len > 2) {
+ adb_receive_raw_packet(sc->adb_bus,
+ pkt->data[0],pkt->data[1],
+ pkt->len - 2,&pkt->data[2]);
+ } else {
+ adb_receive_raw_packet(sc->adb_bus,
+ pkt->data[0],pkt->data[1],0,NULL);
+ }
+ break;
+ case CUDA_PSEUDO:
+ mtx_lock(&sc->sc_mutex);
+ if (pkt->data[0] == CMD_AUTOPOLL)
+ sc->sc_autopoll = 1;
+ mtx_unlock(&sc->sc_mutex);
+ break;
+ case CUDA_ERROR:
+ /*
+ * CUDA will throw errors if we miss a race between
+ * sending and receiving packets. This is already
+ * handled when we abort packet output to handle
+ * this packet in cuda_intr(). Thus, we ignore
+ * these messages.
+ */
+ break;
+ default:
+ device_printf(dev,"unknown CUDA command %d\n",
+ pkt->type);
+ break;
+ }
+
+ free(pkt,M_CUDA);
+
+ mtx_lock(&sc->sc_mutex);
}
- return 0;
+ mtx_unlock(&sc->sc_mutex);
}
static void
@@ -382,8 +472,7 @@ cuda_poll(device_t dev)
!sc->sc_waiting)
return;
- if ((cuda_read_reg(sc, vIFR) & vSR_INT) == vSR_INT)
- cuda_intr(dev);
+ cuda_intr(dev);
}
static void
@@ -392,7 +481,7 @@ cuda_intr(void *arg)
device_t dev;
struct cuda_softc *sc;
- int i, ending, type, restart_send;
+ int i, ending, restart_send, process_inbound;
uint8_t reg;
dev = (device_t)arg;
@@ -401,7 +490,13 @@ cuda_intr(void *arg)
mtx_lock(&sc->sc_mutex);
restart_send = 0;
+ process_inbound = 0;
reg = cuda_read_reg(sc, vIFR);
+ if ((reg & vSR_INT) != vSR_INT) {
+ mtx_unlock(&sc->sc_mutex);
+ return;
+ }
+
cuda_write_reg(sc, vIFR, 0x7f); /* Clear interrupt */
switch_start:
@@ -450,13 +545,6 @@ switch_start:
} else
sc->sc_received++;
- if (sc->sc_received > 3) {
- if ((sc->sc_in[3] == CMD_IIC) &&
- (sc->sc_received > (sc->sc_i2c_read_len + 4))) {
- ending = 1;
- }
- }
-
/* intr off means this is the last byte (end of frame) */
if (cuda_intr_state(sc) == 0) {
ending = 1;
@@ -465,42 +553,24 @@ switch_start:
}
if (ending == 1) { /* end of message? */
- sc->sc_in[0] = sc->sc_received - 1;
+ struct cuda_packet *pkt;
/* reset vars and signal the end of this frame */
cuda_idle(sc);
- /* check if we have a handler for this message */
- type = sc->sc_in[1];
-
- switch (type) {
- case CUDA_ADB:
- if (sc->sc_received > 4) {
- adb_receive_raw_packet(sc->adb_bus,
- sc->sc_in[2],sc->sc_in[3],
- sc->sc_received - 4,&sc->sc_in[4]);
- } else {
- adb_receive_raw_packet(sc->adb_bus,
- sc->sc_in[2],sc->sc_in[3],0,NULL);
- }
- break;
- case CUDA_PSEUDO:
- if (sc->sc_in[3] == CMD_AUTOPOLL)
- sc->sc_autopoll = 1;
- break;
- case CUDA_ERROR:
- device_printf(dev,"CUDA Error\n");
- sc->sc_error = 1;
- break;
- default:
- device_printf(dev,"unknown CUDA command %d\n",
- type);
- break;
- }
+ /* Queue up the packet */
+ pkt = malloc(sizeof(struct cuda_packet), M_CUDA,
+ M_WAITOK);
+
+ pkt->len = sc->sc_received - 2;
+ pkt->type = sc->sc_in[1];
+ memcpy(pkt->data, &sc->sc_in[2], pkt->len);
+
+ STAILQ_INSERT_TAIL(&sc->sc_inq, pkt, pkt_q);
sc->sc_state = CUDA_IDLE;
-
sc->sc_received = 0;
+ process_inbound = 1;
/*
* If there is something waiting to be sent out,
@@ -526,6 +596,7 @@ switch_start:
DELAY(150);
goto switch_start;
}
+
/*
* If we got here, it's ok to start sending
* so load the first byte and tell the chip
@@ -558,7 +629,6 @@ switch_start:
break;
}
if (sc->sc_out_length == sc->sc_sent) { /* check for done */
-
sc->sc_waiting = 0; /* done writing */
sc->sc_state = CUDA_IDLE; /* signal bus is idle */
cuda_in(sc);
@@ -579,14 +649,26 @@ switch_start:
}
mtx_unlock(&sc->sc_mutex);
+
+ if (process_inbound)
+ cuda_send_inbound(sc);
+
+ mtx_lock(&sc->sc_mutex);
+ /* If we have another packet waiting, set it up */
+ if (!sc->sc_waiting && sc->sc_state == CUDA_IDLE)
+ cuda_send_outbound(sc);
+
+ mtx_unlock(&sc->sc_mutex);
+
}
static u_int
-cuda_adb_send(device_t dev, u_char command_byte, int len, u_char *data, u_char poll)
+cuda_adb_send(device_t dev, u_char command_byte, int len, u_char *data,
+ u_char poll)
{
struct cuda_softc *sc = device_get_softc(dev);
- int i;
uint8_t packet[16];
+ int i;
/* construct an ADB command packet and send it */
packet[0] = CUDA_ADB;
@@ -594,15 +676,9 @@ cuda_adb_send(device_t dev, u_char command_byte, int len, u_char *data, u_char p
for (i = 0; i < len; i++)
packet[i + 2] = data[i];
- if (poll)
- cuda_poll(dev);
-
cuda_send(sc, poll, len + 2, packet);
- if (poll)
- cuda_poll(dev);
-
- return 0;
+ return (0);
}
static u_int
@@ -615,17 +691,14 @@ cuda_adb_autopoll(device_t dev, uint16_t mask) {
if (cmd[2] == sc->sc_autopoll) {
mtx_unlock(&sc->sc_mutex);
- return 0;
+ return (0);
}
- while (sc->sc_state != CUDA_IDLE)
- mtx_sleep(dev,&sc->sc_mutex,0,"cuda",1);
-
sc->sc_autopoll = -1;
- cuda_send(sc, 0, 3, cmd);
+ cuda_send(sc, 1, 3, cmd);
mtx_unlock(&sc->sc_mutex);
-
- return 0;
+
+ return (0);
}
diff --git a/sys/powerpc/powermac/cudavar.h b/sys/powerpc/powermac/cudavar.h
index 90f0eae..b6f25fe 100644
--- a/sys/powerpc/powermac/cudavar.h
+++ b/sys/powerpc/powermac/cudavar.h
@@ -61,6 +61,16 @@
#define CUDA_IN 0x4 /* receiving data */
#define CUDA_POLLING 0x5 /* polling - II only */
+struct cuda_packet {
+ uint8_t len;
+ uint8_t type;
+
+ uint8_t data[253];
+ STAILQ_ENTRY(cuda_packet) pkt_q;
+};
+
+STAILQ_HEAD(cuda_pktq, cuda_packet);
+
struct cuda_softc {
device_t sc_dev;
int sc_memrid;
@@ -73,22 +83,24 @@ struct cuda_softc {
device_t adb_bus;
- int sc_node;
- volatile int sc_state;
- int sc_waiting;
- int sc_polling;
- int sc_sent;
- int sc_out_length;
- int sc_received;
- int sc_iic_done;
- int sc_error;
- volatile int sc_autopoll;
+ int sc_node;
+ volatile int sc_state;
+ int sc_waiting;
+ int sc_polling;
+ int sc_iic_done;
+ volatile int sc_autopoll;
int sc_i2c_read_len;
/* internal buffers */
- uint8_t sc_in[256];
- uint8_t sc_out[256];
+ uint8_t sc_in[256];
+ uint8_t sc_out[256];
+ int sc_sent;
+ int sc_out_length;
+ int sc_received;
+
+ struct cuda_pktq sc_inq;
+ struct cuda_pktq sc_outq;
};
#endif /* _POWERPC_CUDAVAR_H_ */
OpenPOWER on IntegriCloud