handle_ep0(9)

πŸ“¦ version 7.3.1, in nixpkgs unstable

πŸ’… handle_ep0(9) β€” bestie edition

okay so this is handle_ep0(9) from the Kernel API Manual and honestly? it's a whole moment. sit down, grab a strawberry drink πŸ“, we're learning how the control endpoint moves data around.


✨ NAME ✨

handle_ep0 β€” literally the function that handles control endpoint data transfers πŸŽ€


🌸 SYNOPSIS 🌸

void handle_ep0 (struct pxa_udc *udc , int fifo_irq , int opc_irq );

like, that's the whole signature, no notes πŸ’…


πŸ¦‹ ARGUMENTS πŸ¦‹

  • udc β€” the udc device itself, the main character of this whole operation πŸ‘‘
  • fifo_irq β€” 1 if we got triggered by a fifo service type irq
  • opc_irq β€” 1 if we got triggered by an output packet complete type irq

πŸ’œ CONTEXT πŸ’œ

interrupt handler ✨ (yes, interrupt handler, we're in the fast lane, no sleeping on the job)


πŸ”₯ DESCRIPTION πŸ”₯

okay so what does it actually do? it tries to transfer all pending request data into the endpoint and/or transfer all pending data in the endpoint into usb requests. both directions, she's flexible like that 🫢. and it handles the states of the ep0 automata β€” the state machine, the vibe check enforcer.

so, PXA27x hardware is out here handling several standard usb control requests without even notifying the driver. she's independent, we love that for her. the requests fully handled by hardware are:

SET_ADDRESS, SET_FEATURE, CLEAR_FEATURE, GET_CONFIGURATION, GET_INTERFACE, GET_STATUS

then there's the requests handled by hardware, but with irq notification:

SYNCH_FRAME, SET_CONFIGURATION, SET_INTERFACE

and the remaining standard requests really handled by handle_ep0 are:

GET_DESCRIPTOR, SET_DESCRIPTOR, specific requests.

requests standardized outside of USB 2.0 chapter 9 are handled more uniformly, by gadget drivers. iykyk πŸ“š

🚨 real talk: the control endpoint state machine is _not_ USB spec compliant. it's even hardly compliant with Intel PXA270 developers guide. like, not even close, bestie 😭

the key points which inferred this state machine are:

  • on every setup token, bit UDCCSR0_SA is raised and held until cleared by software.
  • on every OUT packet received, UDCCSR0_OPC is raised and held until cleared by software.
  • clearing UDCCSR0_OPC always flushes ep0. If in setup stage, never do it before reading ep0. This is true only for PXA27x. This is not true anymore for PXA3xx family (check Back-to-Back setup packet in developers guide). ⚠️
  • irq can be called on a "packet complete" event (opc_irq=1), while UDCCSR0_OPC is not yet raised (delta can be as big as 100ms from experimentation). yeah. 100ms. we timed it. πŸ’€
  • as UDCCSR0_SA can be activated while in irq handling, and clearing UDCCSR0_OPC would flush the setup data, we almost never clear UDCCSR0_OPC => we never actually read the "status stage" packet of an IN data stage => this is not documented in Intel documentation. iconic behaviour honestly πŸ™ˆ
  • hardware as no idea of STATUS STAGE, it only handle SETUP STAGE and DATA STAGE. The driver add STATUS STAGE to send last zero length packet in OUT_STATUS_STAGE.
  • special attention was needed for IN_STATUS_STAGE. If a packet complete event is detected, we terminate the status stage without ackowledging the packet (not to risk to loose a potential SETUP packet) ⭐

🌈 SEE ALSO 🌈

Kernel file ./drivers/usb/gadget/udc/pxa27x_udc.c is where the whole friend group lives, and it's also is_match_usb_pxa(9), find_pxa_ep(9), update_pxa_ep_matches(9), pio_irq_enable(9), pio_irq_disable(9), udc_set_mask_UDCCR(9), udc_clear_mask_UDCCR(9), ep_write_UDCCSR(9), ep_count_bytes_remain(9), ep_is_empty(9), ep_is_full(9), epout_has_pkt(9), set_ep0state(9), ep0_idle(9), inc_ep_stats_reqs(9), inc_ep_stats_bytes(9), pxa_ep_setup(9), pxa_eps_setup(9), pxa_ep_alloc_request(9), pxa_ep_free_request(9), ep_add_request(9), ep_del_request(9), req_done(9), ep_end_out_req(9), ep0_end_out_req(9), ep_end_in_req(9), ep0_end_in_req(9), nuke(9), read_packet(9), write_packet(9), read_fifo(9), write_fifo(9), read_ep0_fifo(9), write_ep0_fifo(9), pxa_ep_queue(9), pxa_ep_dequeue(9), pxa_ep_set_halt(9), pxa_ep_fifo_status(9), pxa_ep_fifo_flush(9), pxa_ep_enable(9), pxa_ep_disable(9), dplus_pullup(9), pxa_udc_get_frame(9), pxa_udc_wakeup(9), should_enable_udc(9),

should_disable_udc(9), pxa_udc_pullup(9), pxa_udc_vbus_session(9), pxa_udc_vbus_draw(9), pxa_udc_phy_event(9), udc_disable(9), udc_init_data(9), udc_enable(9), pxa27x_udc_start(9), stop_activity(9), pxa27x_udc_stop(9), handle_ep0_ctrl_req(9), handle_ep(9), pxa27x_change_configuration(9), pxa27x_change_interface(9), irq_udc_suspend(9), irq_udc_resume(9), irq_udc_reconfig(9), irq_udc_reset(9), pxa_udc_irq(9), pxa_udc_probe(9), pxa_udc_remove(9), pxa_udc_suspend(9), pxa_udc_resume(9)


that's the whole page bestie, go read the source, slay πŸ’–βœ¨

sign-off time πŸŽ€ β€” this gorgeous little doc lives under ./drivers/usb/gadget/udc, dated October 2026, and it's serving handle_ep0(9) energy only. periodt. πŸ’…

πŸ“¦ where this page comes from