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.
This commit is contained in:
Nathan Whitehorn 2008-12-06 23:26:02 +00:00
parent 11d1cade73
commit 582434bd08
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=185724
7 changed files with 213 additions and 140 deletions

View File

@ -68,7 +68,7 @@ uint8_t adb_get_device_type(device_t dev);
uint8_t adb_get_device_handler(device_t dev);
uint8_t adb_set_device_handler(device_t dev, uint8_t newhandler);
uint8_t adb_read_register(device_t dev, u_char reg, size_t *len, void *data);
size_t adb_read_register(device_t dev, u_char reg, void *data);
/* Bits for implementing ADB host bus adapters */
extern devclass_t adb_devclass;

View File

@ -48,7 +48,7 @@ static void adb_bus_enumerate(void *xdev);
static void adb_probe_nomatch(device_t dev, device_t child);
static int adb_print_child(device_t dev, device_t child);
static int adb_send_raw_packet_sync(device_t dev, uint8_t to, uint8_t command, uint8_t reg, int len, u_char *data);
static int adb_send_raw_packet_sync(device_t dev, uint8_t to, uint8_t command, uint8_t reg, int len, u_char *data, u_char *reply);
static char *adb_device_string[] = {
"HOST", "dongle", "keyboard", "mouse", "tablet", "modem", "RESERVED", "misc"
@ -118,8 +118,7 @@ adb_bus_enumerate(void *xdev)
sc->packet_reply = 0;
sc->autopoll_mask = 0;
mtx_init(&sc->sc_sync_mtx,"adbsyn",NULL,MTX_DEF | MTX_RECURSE);
sc->sync_packet = 0xffff;
/* Initialize devinfo */
for (i = 0; i < 16; i++) {
@ -128,7 +127,7 @@ adb_bus_enumerate(void *xdev)
}
/* Reset ADB bus */
adb_send_raw_packet_sync(dev,0,ADB_COMMAND_BUS_RESET,0,0,NULL);
adb_send_raw_packet_sync(dev,0,ADB_COMMAND_BUS_RESET,0,0,NULL,NULL);
DELAY(1500);
/* Enumerate bus */
@ -140,7 +139,7 @@ adb_bus_enumerate(void *xdev)
do {
reply = adb_send_raw_packet_sync(dev,i,
ADB_COMMAND_TALK,3,0,NULL);
ADB_COMMAND_TALK,3,0,NULL,NULL);
if (reply) {
/* If we got a response, relocate to next_free */
@ -150,10 +149,10 @@ adb_bus_enumerate(void *xdev)
r3 |= 0x00fe;
adb_send_raw_packet_sync(dev,i, ADB_COMMAND_LISTEN,3,
sizeof(uint16_t),(u_char *)(&r3));
sizeof(uint16_t),(u_char *)(&r3),NULL);
adb_send_raw_packet_sync(dev,next_free,
ADB_COMMAND_TALK,3,0,NULL);
ADB_COMMAND_TALK,3,0,NULL,NULL);
sc->devinfo[next_free].default_address = i;
if (first_relocated < 0)
@ -169,9 +168,9 @@ adb_bus_enumerate(void *xdev)
adb_send_raw_packet_sync(dev,first_relocated,
ADB_COMMAND_LISTEN,3,
sizeof(uint16_t),(u_char *)(&r3));
sizeof(uint16_t),(u_char *)(&r3),NULL);
adb_send_raw_packet_sync(dev,i,
ADB_COMMAND_TALK,3,0,NULL);
ADB_COMMAND_TALK,3,0,NULL,NULL);
sc->devinfo[i].default_address = i;
sc->devinfo[(int)(first_relocated)].default_address = 0;
@ -194,10 +193,6 @@ adb_bus_enumerate(void *xdev)
static int adb_bus_detach(device_t dev)
{
struct adb_softc *sc = device_get_softc(dev);
mtx_destroy(&sc->sc_sync_mtx);
return (bus_generic_detach(dev));
}
@ -230,6 +225,7 @@ adb_receive_raw_packet(device_t dev, u_char status, u_char command, int len,
if (sc->sync_packet == command) {
memcpy(sc->syncreg,data,(len > 8) ? 8 : len);
atomic_store_rel_int(&sc->packet_reply,len + 1);
wakeup(sc);
}
if (sc->children[addr] != NULL) {
@ -317,12 +313,12 @@ adb_get_device_handler(device_t dev)
static int
adb_send_raw_packet_sync(device_t dev, uint8_t to, uint8_t command,
uint8_t reg, int len, u_char *data)
uint8_t reg, int len, u_char *data, u_char *reply)
{
u_char command_byte = 0;
struct adb_softc *sc;
int result = -1;
int i = 0;
int i = 1;
sc = device_get_softc(dev);
@ -331,7 +327,8 @@ adb_send_raw_packet_sync(device_t dev, uint8_t to, uint8_t command,
command_byte |= reg;
/* Wait if someone else has a synchronous request pending */
mtx_lock(&sc->sc_sync_mtx);
while (!atomic_cmpset_int(&sc->sync_packet, 0xffff, command_byte))
tsleep(sc, 0, "ADB sync", hz/10);
sc->packet_reply = 0;
sc->sync_packet = command_byte;
@ -343,21 +340,27 @@ adb_send_raw_packet_sync(device_t dev, uint8_t to, uint8_t command,
* Maybe the command got lost? Try resending and polling the
* controller.
*/
if (i > 40)
if (i % 40 == 0)
ADB_HB_SEND_RAW_PACKET(sc->parent, command_byte,
len, data, 1);
DELAY(100);
tsleep(sc, 0, "ADB sync", hz/10);
i++;
}
result = sc->packet_reply - 1;
if (reply != NULL && result > 0)
memcpy(reply,sc->syncreg,result);
/* Clear packet sync */
sc->packet_reply = 0;
sc->sync_packet = 0xffff; /* We can't match a 16 bit value */
mtx_unlock(&sc->sc_sync_mtx);
/*
* We can't match a value beyond 8 bits, so set sync_packet to
* 0xffff to avoid collisions.
*/
atomic_set_int(&sc->sync_packet, 0xffff);
return (result);
}
@ -375,37 +378,27 @@ adb_set_device_handler(device_t dev, uint8_t newhandler)
newr3 = dinfo->register3 & 0xff00;
newr3 |= (uint16_t)(newhandler);
adb_send_raw_packet_sync(sc->sc_dev,dinfo->address, ADB_COMMAND_LISTEN,
3, sizeof(uint16_t), (u_char *)(&newr3), NULL);
adb_send_raw_packet_sync(sc->sc_dev,dinfo->address,
ADB_COMMAND_LISTEN, 3, sizeof(uint16_t), (u_char *)(&newr3));
adb_send_raw_packet_sync(sc->sc_dev,dinfo->address,
ADB_COMMAND_TALK, 3, 0, NULL);
ADB_COMMAND_TALK, 3, 0, NULL, NULL);
return (dinfo->handler_id);
}
uint8_t
adb_read_register(device_t dev, u_char reg,
size_t *len, void *data)
size_t
adb_read_register(device_t dev, u_char reg, void *data)
{
struct adb_softc *sc;
struct adb_devinfo *dinfo;
size_t orig_len;
size_t result;
dinfo = device_get_ivars(dev);
sc = device_get_softc(device_get_parent(dev));
orig_len = *len;
result = adb_send_raw_packet_sync(sc->sc_dev,dinfo->address,
ADB_COMMAND_TALK, reg, 0, NULL, data);
mtx_lock(&sc->sc_sync_mtx);
*len = adb_send_raw_packet_sync(sc->sc_dev,dinfo->address,
ADB_COMMAND_TALK, reg, 0, NULL);
if (*len > 0)
memcpy(data,sc->syncreg,*len);
mtx_unlock(&sc->sc_sync_mtx);
return ((*len > 0) ? 0 : -1);
return (result);
}

View File

@ -271,11 +271,12 @@ adb_kbd_attach(device_t dev)
}
#endif
adb_set_autopoll(dev,1);
/* Check (asynchronously) if we can read out the LED state from
/* Check if we can read out the LED state from
this keyboard by reading the key state register */
adb_send_packet(dev,ADB_COMMAND_TALK,2,0,NULL);
if (adb_read_register(dev, 2, NULL) == 2)
sc->have_led_control = 1;
adb_set_autopoll(dev,1);
return (0);
}
@ -323,11 +324,6 @@ adb_kbd_receive_packet(device_t dev, u_char status,
if (command != ADB_COMMAND_TALK)
return 0;
if (reg == 2 && len == 2) {
sc->have_led_control = 1;
return 0;
}
if (reg != 0 || len != 2)
return (0);

View File

@ -181,7 +181,7 @@ adb_mouse_attach(device_t dev)
sc->mode.resolution = 200;
break;
case 4:
adb_read_register(dev,1,&r1_len,r1);
r1_len = adb_read_register(dev,1,r1);
if (r1_len < 8)
break;

View File

@ -40,7 +40,6 @@ struct adb_softc {
device_t parent;
struct intr_config_hook enum_hook;
struct mtx sc_sync_mtx;
volatile int sync_packet;
volatile int packet_reply;

View File

@ -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 @@ cuda_intr(void *arg)
} 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 @@ cuda_intr(void *arg)
}
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];
/* Queue up the packet */
pkt = malloc(sizeof(struct cuda_packet), M_CUDA,
M_WAITOK);
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;
}
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 @@ cuda_intr(void *arg)
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 @@ cuda_intr(void *arg)
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 @@ cuda_intr(void *arg)
}
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);
}

View File

@ -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_ */