> Date: Mon, 5 Jul 2021 00:04:24 +0200
> From: Patrick Wildt <[email protected]>
> 
> Hi,
> 
> I had trouble interfacing with a machine's IPMI through dwiic(4).  What
> I saw was that when sending 'bigger' commands, it would never receive
> the STOP bit interrupt.
> 
> The trouble is, as can be seen in the log, that we want to send (it
> says read, but it's a write OP, so it's send) 20 bytes, but the tx
> limit says 14.
> 
> What we should do is send 14 bytes, then wait for it to send us the
> tx empty interrupt (like we do when we first enable the controller),
> and then re-read the tx limit.  The last line in the log is some
> debug print I added for myself, but is not part of the diff.
> 
> With this, I was finally able to change the IPMI password and regain
> access to the web interface after updating the BMC's firmware...
> 
> dwiic0: dwiic_i2c_exec: op 7, addr 0x10, cmdlen 2, len 3, flags 0x00
> dwiic0: dwiic_i2c_exec: need to read 3 bytes, can send 14 read reqs
> dwiic0: dwiic_i2c_exec: op 5, addr 0x10, cmdlen 1, len 33, flags 0x00
> dwiic0: dwiic_i2c_exec: need to read 33 bytes, can send 15 read reqs
> dwiic0: dwiic_i2c_exec: op 7, addr 0x10, cmdlen 2, len 20, flags 0x00
> dwiic0: dwiic_i2c_exec: need to read 20 bytes, can send 14 read reqs
> dwiic0: new tx limit 8
> 
> Opinions? ok?

I think you're on to something.  But this needs to handle I2C_F_POLL.

> diff --git a/sys/dev/ic/dwiic.c b/sys/dev/ic/dwiic.c
> index 84d97b8645b..d04a7b03979 100644
> --- a/sys/dev/ic/dwiic.c
> +++ b/sys/dev/ic/dwiic.c
> @@ -416,6 +416,21 @@ dwiic_i2c_exec(void *cookie, i2c_op_t op, i2c_addr_t 
> addr, const void *cmdbuf,
>                       tx_limit = sc->tx_fifo_depth -
>                           dwiic_read(sc, DW_IC_TXFLR);
>               }
> +
> +             if (I2C_OP_WRITE_P(op) && tx_limit == 0 && x < len) {
> +                     s = splbio();
> +                     dwiic_read(sc, DW_IC_CLR_INTR);
> +                     dwiic_write(sc, DW_IC_INTR_MASK, DW_IC_INTR_TX_EMPTY);
> +
> +                     if (tsleep_nsec(&sc->sc_writewait, PRIBIO, "dwiic",
> +                         MSEC_TO_NSEC(500)) != 0)
> +                             printf("%s: timed out waiting for tx_empty "
> +                                 "intr\n", sc->sc_dev.dv_xname);
> +                     splx(s);
> +
> +                     tx_limit = sc->tx_fifo_depth -
> +                         dwiic_read(sc, DW_IC_TXFLR);
> +             }
>       }
>  
>       if (I2C_OP_STOP_P(op) && I2C_OP_WRITE_P(op)) {
> 
> 

Reply via email to