DF-5382 / fix.diff
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 | --- a/sys/bus/u4b/serial/umcs.c +++ b/sys/bus/u4b/serial/umcs.c @@ -138,6 +138,21 @@ uint8_t sc_numports; /* Number of ports (subunits) */ struct umcs7840_softc_oneport sc_ports[UMCS7840_MAX_PORTS]; /* Indexed by PHYSICAL * port number. */ + + /* + * DF-5382: LSR/MSR refresh triggered by interrupt data must not + * run on the shared per-bus USB callback thread. The interrupt + * callback only records the pending ports and defers the + * synchronous control requests to the ucom super taskqueue, where + * synchronous requests are the sanctioned pattern + * (usbd_do_request_proc contract). + */ + uint8_t sc_status_pending; /* mask of PHYSICAL port numbers */ + uint8_t sc_status_isrc[UMCS7840_MAX_PORTS]; /* captured ISR bytes */ + struct umcs7840_status_msg { + struct usb_proc_msg hdr; + struct umcs7840_softc *sc; + } sc_status_msg[2]; }; /* prototypes */ @@ -184,6 +199,9 @@ static void umcs7840_read_callbackN(struct usb_xfer *, usb_error_t, uint8_t); static void umcs7840_write_callbackN(struct usb_xfer *, usb_error_t, uint8_t); + static void umcs7840_cfg_status_refresh(struct usb_proc_msg *); + static void umcs7840_queue_status_refresh(struct umcs7840_softc *); + /* Indexed by LOGICAL port number (subunit), so two-port device uses 0 & 1 */ static usb_callback_t *umcs7840_rw_callbacks[UMCS7840_MAX_PORTS][UMCS7840_N_TRANSFERS] = { {&umcs7840_read_callback1, &umcs7840_write_callback1}, @@ -748,28 +766,14 @@ if (buf[pn] & MCS7840_UART_ISR_NOPENDING) continue; DPRINTF("Port %d has pending interrupt: %02x (FIFO: %02x)\n", pn, buf[pn] & MCS7840_UART_ISR_INTMASK, buf[pn] & (~MCS7840_UART_ISR_INTMASK)); - switch (buf[pn] & MCS7840_UART_ISR_INTMASK) { - case MCS7840_UART_ISR_RXERR: - case MCS7840_UART_ISR_RXHASDATA: - case MCS7840_UART_ISR_RXTIMEOUT: - /* Read new LSR */ - if (umcs7840_get_UART_reg_sync(sc, pn, MCS7840_UART_REG_LSR, &sc->sc_ports[pn].sc_lsr)) - break; /* Inner switch */ - ucom_status_change(&sc->sc_ucom[subunit]); - /* Inner switch */ - break; - case MCS7840_UART_ISR_TXEMPTY: - /* Do nothing */ - break; /* Inner switch */ - case MCS7840_UART_ISR_MSCHANGE: - /* Read new MSR */ - if (umcs7840_get_UART_reg_sync(sc, pn, MCS7840_UART_REG_MSR, &sc->sc_ports[pn].sc_msr)) - break; /* Inner switch */ - DPRINTF("Port %d: new MSR %02x\n", pn, sc->sc_ports[pn].sc_msr); - ucom_status_change(&sc->sc_ucom[subunit]); - break; - } + /* DF-5382: defer, do not issue synchronous + * control requests from the per-bus USB callback + * thread */ + sc->sc_status_isrc[pn] = buf[pn]; + sc->sc_status_pending |= (1U << pn); } + if (sc->sc_status_pending) + umcs7840_queue_status_refresh(sc); } else device_printf(sc->sc_dev, "Invalid interrupt data length %d", actlen); /* FALLTHROUGH */ @@ -789,6 +793,60 @@ } } +/* + * Runs on sc_super_ucom.sc_tq with sc->sc_lock held -- the same context + * and contract as the other ucom cfg tasks: synchronous control requests + * are allowed here, unlike in the xfer callback on the per-bus thread. + */ +static void +umcs7840_cfg_status_refresh(struct usb_proc_msg *_msg) +{ + struct umcs7840_status_msg *msg = (struct umcs7840_status_msg *)_msg; + struct umcs7840_softc *sc = msg->sc; + int subunit; + + for (subunit = 0; subunit < sc->sc_numports; ++subunit) { + uint8_t pn = sc->sc_ucom[subunit].sc_portno; + + if (!(sc->sc_status_pending & (1U << pn))) + continue; + sc->sc_status_pending &= ~(1U << pn); + switch (sc->sc_status_isrc[pn] & MCS7840_UART_ISR_INTMASK) { + case MCS7840_UART_ISR_RXERR: + case MCS7840_UART_ISR_RXHASDATA: + case MCS7840_UART_ISR_RXTIMEOUT: + /* Read new LSR */ + if (umcs7840_get_UART_reg_sync(sc, pn, + MCS7840_UART_REG_LSR, &sc->sc_ports[pn].sc_lsr) == 0) + ucom_status_change(&sc->sc_ucom[subunit]); + break; + case MCS7840_UART_ISR_TXEMPTY: + /* Do nothing */ + break; + case MCS7840_UART_ISR_MSCHANGE: + /* Read new MSR */ + if (umcs7840_get_UART_reg_sync(sc, pn, + MCS7840_UART_REG_MSR, &sc->sc_ports[pn].sc_msr) == 0) + ucom_status_change(&sc->sc_ucom[subunit]); + break; + } + } +} + +/* called from the interrupt xfer callback with sc->sc_lock held */ +static void +umcs7840_queue_status_refresh(struct umcs7840_softc *sc) +{ + struct umcs7840_status_msg *msg; + + if (usb_proc_is_gone(&sc->sc_super_ucom.sc_tq)) + return; /* detaching */ + msg = usb_proc_msignal(&sc->sc_super_ucom.sc_tq, + &sc->sc_status_msg[0].hdr, &sc->sc_status_msg[1].hdr); + msg->hdr.pm_callback = &umcs7840_cfg_status_refresh; + msg->sc = sc; +} + static void umcs7840_read_callback1(struct usb_xfer *xfer, usb_error_t error) { |