diff -c /home/riastradh/netbsd/current/src-cvs/sys/dev/usb/umcs.c /tmp/riastradh/buffer-content-3288MbP --- /home/riastradh/netbsd/current/src-cvs/sys/dev/usb/umcs.c 2014-03-23 01:45:47.000000000 +0000 +++ /tmp/riastradh/buffer-content-3288MbP 2014-03-23 15:45:30.000000000 +0000 @@ -78,9 +78,6 @@ */ struct umcs7840_softc_oneport { device_t sc_port_ucom; /* ucom subdevice */ - volatile uint32_t /* changes for this port have */ - sc_port_changed; /* been signaled, - call ucom_status_change() */ unsigned int sc_port_phys; /* physical port number */ uint8_t sc_port_lcr; /* local line control register */ uint8_t sc_port_mcr; /* local modem control register */ @@ -94,6 +91,7 @@ uint8_t *sc_intr_buf; /* buffer for interrupt xfer */ unsigned int sc_intr_buflen; /* size of buffer */ struct usb_task sc_change_task; /* async status changes */ + volatile uint32_t sc_change_mask; /* mask of port changes */ struct umcs7840_softc_oneport sc_ports[UMCS7840_MAX_PORTS]; /* data for each port */ uint8_t sc_numports; /* number of ports (subunits) */ @@ -866,6 +864,16 @@ return; } +static uint32_t +atomic_or_32_ov(volatile uint32_t *p, uint32_t v) +{ + uint32_t ov; + + do ov = *p; while (atomic_cas_32(p, ov, (ov | v)) != ov); + + return ov; +} + static void umcs7840_intr(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status) @@ -889,7 +897,7 @@ usbd_get_xfer_status(xfer, NULL, NULL, &actlen, NULL); if (actlen == 5 || actlen == 13) { - usb_rem_task(sc->sc_udev, &sc->sc_change_task); + uint32_t change_mask; /* Check status of all ports */ for (subunit = 0; subunit < sc->sc_numports; subunit++) { uint8_t pn = sc->sc_ports[subunit].sc_port_phys; @@ -904,7 +912,7 @@ case MCS7840_UART_ISR_RXHASDATA: case MCS7840_UART_ISR_RXTIMEOUT: case MCS7840_UART_ISR_MSCHANGE: - sc->sc_ports[subunit].sc_port_changed = 1; + change_mask |= (1U << subunit); break; default: /* Do nothing */ @@ -912,9 +920,9 @@ } } - membar_exit(); - usb_add_task(sc->sc_udev, &sc->sc_change_task, - USB_TASKQ_DRIVER); + if (atomic_or_32_ov(&sc->sc_change_mask, change_mask) != 0) + usb_add_task(sc->sc_udev, &sc->sc_change_task, + USB_TASKQ_DRIVER); } else { aprint_error_dev(sc->sc_dev, "Invalid interrupt data length %d", actlen); @@ -925,14 +933,13 @@ umcs7840_change_task(void *arg) { struct umcs7840_softc *sc = arg; + uint32_t change_mask; int i; + change_mask = atomic_swap_32(&sc->sc_change_mask, 0); for (i = 0; i < sc->sc_numports; i++) { - if (sc->sc_ports[i].sc_port_changed) { - sc->sc_ports[i].sc_port_changed = 0; - membar_exit(); + if (ISSET(change_mask, (1U << i))) ucom_status_change(device_private( sc->sc_ports[i].sc_port_ucom)); - } } } Diff finished. Sun Mar 23 15:45:30 2014