RFC: Small additions to the atusb driver code

Werner Almesberger werner at almesberger.net
Thu Jun 23 05:26:40 EDT 2011

Richard Sharpe wrote:
> I will have to go back and look at the documentation, but you are
> probably correct.

A quick update on how atusb presents itself on USB now:

- two interfaces: #0 is the good old atusb. This is what any atusb
  application should use. #1 is to prepare switching into DFU mode,
  for use by dfu-util.

- two endpoints (on interface #0): EP0  is the usual control
  endpoint, EP 1 is a bulk input, used to signal interrupts.

EP 1 should eventually become an interrupt endpoint, but that seems
to be more of a cosmetical change than a functional one.

EP 1 currently works like this: if there's a transceiver interrupt,
the AVR reads IRQ_STATUS and enqueues this byte on EP 1, clearing
the interrupt. If another interrupt occurs before the previous
interrupt data has been retrieved, is is ignored, with the idea
being that the application/kernel reads IRQ_STATUS when waiting for

This approach doesn't quite seem to work, not even in the sheltered
world of my user-space applications. I think I'll remove reading
IRQ_STATUS in the MCU, so that the host only receives a zero byte
indicating that an interrupt has happened, but not trying to tell
it which.

This still leaves a few races if we try to use the transceiver as
if it wasn't on USB. E.g., if we receive a frame just an instant
before switching the transceiver from receiving to sending, we have
a pending send/receive interrupt (transmit and receive interrupts
are shared, TRX_END). Once the switch is done, there can be no

- transceiver is in state RX_ON (receive any incoming frame)

- frame arrives, setting TRX_END

- MCU enqueues an interrupt notification on EP 1

- not yet aware of the new frame, host decides to transmit a frame

- host commands a transition from RX_ON to PLL_ON (PLL_ON means
  that we're ready to send or receive but presently do neither)

- host waits until all interrupts already pending at the host are
  processed (so that we're sure anything that happened before we
  stopped receiving has been processed). Since we haven't seen the
  byte waiting at the MCU in EP 1 yet, we do nothing.

- USB gets around to fetch the data at EP 1

- host learns of the new interrupt, at a time no TRX_END
  interrupts are possible

- host gets confused

That's the conundrum I'm currently banging my head on.

> 2. Decide on a git repository for committing code ...

How about qi-hardware's qi-kernel ? This is where I keep my branch.
Richard already has commit access.

> 3. Perhaps have weekly IRC meetings where we go over what we plan to
> do in the following week ...

If your duty cycle isn't very low, just staying on IRC and
responding to things as you see them appear / when you return may
be faster. We could start a channel separate from #qi-hardware if
noise becomes a problem.

> 4. Perhaps finish of this driver to expose all the relevant registers
> via /sys/class/xxx/etc

Hmm, if things keep on going in the direction of this just being
an SPI device, the driver wouldn't actually know what registers
are. All it would see are certain patterns of SPI transfers that
it translates into the three ATUSB_SPI_* request types.

The ATUSB_SPI_* requests we have:

- ATUSB_SPI_WRITE: send >= 2 bytes on SPI, without receiving anything.
  The first two bytes are passed in wValue and wIndex, the rest in the
  data phase. If we only have two bytes (e.g., in a register write),
  there's no data phase.

  This is for register writes, frame buffer writes, and SRAM writes.

- ATUSB_SPI_READ1: send one byte (wValue), then receive any number of
  bytes in the data phase.

  This is for register reads and frame buffer reads. 

- ATUSB_SPI_READ2: send two bytes (wValue and wIndex), then receive
  any number of bytes in the data phase.

  This is for SRAM reads.

An SPI driver for atusb would simply look at the requests it gets and
see if it can fit them into any of these patterns. If not, it would
complain and return an error.

Note that I'm not sure yet if this is a good direction. If there's no
other solution to the USB interrupt problem than adapting the
driver's control flow, then we can just use the old
ATUSB_{REG,BUF,SRAM}_* requests.

In terms of efficiency, the old requests and the SPI ones are
equivalent, with the exception of ATUSB_BUF_READ, which makes use of
being able to adjust the size immediately after reading the PHR,
without having to end the SPI transfer.

- Werner

More information about the discussion mailing list