[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [cdi-devel] [PATCH 2/5] ehci: Implement interrupt transfers
On Tue, Dec 29, 2015 at 01:00:51AM +0100, Max Reitz wrote:
> Signed-off-by: Max Reitz <max@xxxxxxxxxx>
> ---
> ehci/ehci.c | 265 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
> ehci/ehci.h | 20 +++++
> ehci/main.c | 11 +--
> 3 files changed, 278 insertions(+), 18 deletions(-)
>
> diff --git a/ehci/ehci.c b/ehci/ehci.c
> index addec00..4b548ae 100644
> --- a/ehci/ehci.c
> +++ b/ehci/ehci.c
> @@ -40,6 +40,13 @@
>
> static void bios_handoff(struct cdi_pci_device *pci_dev, struct ehci *hc);
> static void irq_handler(struct cdi_device *dev);
> +static void init_periodic_pseudo_qhs(struct ehci *hc);
> +
> +
> +static inline bool is_power_of_two(unsigned value)
> +{
> + return !(value & (value - 1));
> +}
>
>
> struct cdi_device *ehci_init_device(struct cdi_bus_data *bus_data)
> @@ -111,6 +118,18 @@ struct cdi_device *ehci_init_device(struct cdi_bus_data *bus_data)
> }
> hc->regs->periodiclistbase = hc->periodic_list_mem->paddr.items[0].start;
>
> + hc->periodic_qhs = calloc(1024, sizeof(*hc->periodic_qhs));
> + // 10 = |0..10| = |ld(1)..ld(1024)|
I gues you mean "// 11 = ..."?
> + hc->periodic_pseudo_qhs_mem =
> + cdi_mem_alloc(11 * sizeof(hc->periodic_pseudo_qhs[0]),
> + 6
> + | CDI_MEM_PHYS_CONTIGUOUS
> + | CDI_MEM_DMA_4G
> + | CDI_MEM_NOINIT);
> + hc->periodic_pseudo_qhs = hc->periodic_pseudo_qhs_mem->vaddr;
> +
> + init_periodic_pseudo_qhs(hc);
> +
> // Create anchor
> struct cdi_mem_area *anchor_qh;
> anchor_qh = cdi_mem_alloc(sizeof(ehci_fat_qh_t), 6
> @@ -323,7 +442,28 @@ static void enter_async_qh(struct ehci *hc, ehci_fat_qh_t *qh)
> }
>
>
> -static void retire_qh(struct ehci *hc, ehci_fat_qh_t *qh)
> +static void enter_interrupt_qh(struct ehci *hc, ehci_fat_qh_t *qh,
> + int frame_interval_shift)
> +{
> + qh->next = hc->periodic_functional_qhs;
This value...
> + hc->periodic_functional_qhs = qh;
> +
> + ehci_fat_qh_t *pseudo_qh = &hc->periodic_pseudo_qhs[frame_interval_shift];
> +
> + qh->next = pseudo_qh->next;
...is immediately overwritten here.
As I don't understand yet what functional and pseudo QHs are and the
header doesn't tell anything about it, I can't say how bad this is, but
we seem to throw away all of the QHs in hc->periodic_functional_qhs and
replace them by qh.
My guess is that the first assignment should be qh->next_periodic.
> + qh->previous = pseudo_qh;
> +
> + pseudo_qh->next = qh;
> + if (qh->next) {
> + qh->next->previous = qh;
> + }
> +
> + qh->qh.next_qh = pseudo_qh->qh.next_qh;
> + pseudo_qh->qh.next_qh = qh->paddr | EHCI_QH;
> +}
> +
> +
> +static void retire_async_qh(struct ehci *hc, ehci_fat_qh_t *qh)
> {
> qh->previous->qh.next_qh = qh->qh.next_qh;
>
> @@ -336,11 +476,29 @@ static void retire_qh(struct ehci *hc, ehci_fat_qh_t *qh)
> }
>
>
> -static void retire_first_qtd(ehci_fat_qh_t *qh)
> +static void retire_periodic_qh(struct ehci *hc, ehci_fat_qh_t *qh)
> {
> - ehci_fat_qtd_t *qtd = qh->first_qtd;
> + qh->previous->qh.next_qh = qh->qh.next_qh;
>
> - assert(qtd);
> + qh->previous->next = qh->next;
> + if (qh->next) {
> + qh->next->previous = qh->previous;
> + }
> +
> + ehci_fat_qh_t **func_periodic_ptr = &hc->periodic_functional_qhs;
> + while (*func_periodic_ptr && *func_periodic_ptr != qh) {
> + func_periodic_ptr = &(*func_periodic_ptr)->next_periodic;
> + }
> + assert(*func_periodic_ptr == qh);
> + *func_periodic_ptr = qh->next_periodic;
> +
> + cdi_list_push(hc->retired_qhs, qh);
I think this will only be processed when the next async QH is retired.
Is this good enough?
> +}
> +
> +
> +static void qtd_write_back(ehci_fat_qtd_t *qtd)
> +{
> + ehci_fat_qh_t *qh = qtd->qh;
>
> uint32_t err_mask = (qh->qh.ep_state[0] & EHCI_QHES0_EPS_MASK)
> == EHCI_QHES0_EPS_HS
> diff --git a/ehci/ehci.h b/ehci/ehci.h
> index fb8dd9e..4b55391 100644
> --- a/ehci/ehci.h
> +++ b/ehci/ehci.h
> @@ -87,8 +87,14 @@ struct ehci_fat_qh {
> struct cdi_mem_area *cdi_mem_area;
> uintptr_t paddr;
>
> + cdi_usb_hc_interrupt_cb_t cb;
> + void *cb_opaque;
Maybe comment that cb != NULL if, and only if, this is a periodic QH?
> +
> ehci_fat_qh_t *next, *previous;
> + ehci_fat_qh_t *next_periodic;
> ehci_fat_qtd_t *first_qtd, *last_qtd;
> +
> + bool is_pseudo_qh;
> } __attribute__((aligned(64)));
>
> static_assert(!(sizeof(struct ehci_fat_qh) % 64),
> @@ -122,9 +128,17 @@ struct ehci {
>
> struct cdi_mem_area *periodic_list_mem;
> volatile uint32_t *periodic_list;
> + ehci_fat_qh_t **periodic_qhs;
> +
> + struct cdi_mem_area *periodic_pseudo_qhs_mem;
> + ehci_fat_qh_t *periodic_pseudo_qhs;
> + ehci_fat_qh_t *periodic_functional_qhs;
As I mentioned above, a comment explaining these wouldn't hurt either.
After reading the rest of the patch I have the impression that the job
of the pseudo QHs is just to sit there permanently so that the
(transient) functional QHs have a place to attach to. I'm not sure,
though.
> ehci_fat_qh_t *async_start;
> cdi_list_t retired_qhs;
> +
> + // For a bit of load balancing
> + int periodic_start_microframe;
> };
>
> #define PCI_CLASS_SERIAL_BUS_CONTROLLER 0x0c
Kevin