// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2001-2004 by David Brownell */ /* this file is part of ehci-hcd.c */ /*-------------------------------------------------------------------------*/ /* * EHCI hardware queue manipulation ... the core. QH/QTD manipulation. * * Control, bulk, and interrupt traffic all use "qh" lists. They list "qtd" * entries describing USB transactions, max 16-20kB/entry (with 4kB-aligned * buffers needed for the larger number). We use one QH per endpoint, queue * multiple urbs (all three types) per endpoint. URBs may need several qtds. * * ISO traffic uses "ISO TD" (itd, and sitd) records, and (along with * interrupts) needs careful scheduling. Performance improvements can be * an ongoing challenge. That's in "ehci-sched.c". * * USB 1.1 devices are handled (a) by "companion" OHCI or UHCI root hubs, * or otherwise through transaction translators (TTs) in USB 2.0 hubs using * (b) special fields in qh entries or (c) split iso entries. TTs will * buffer low/full speed data so the host collects it at high speed. */ /*-------------------------------------------------------------------------*/ /* fill a qtd, returning how much of the buffer we were able to queue up */ static unsigned int qtd_fill(struct ehci_hcd *ehci, struct ehci_qtd *qtd, dma_addr_t buf, size_t len, int token, int maxpacket) { … } /*-------------------------------------------------------------------------*/ static inline void qh_update (struct ehci_hcd *ehci, struct ehci_qh *qh, struct ehci_qtd *qtd) { … } /* if it weren't for a common silicon quirk (writing the dummy into the qh * overlay, so qh->hw_token wrongly becomes inactive/halted), only fault * recovery (including urb dequeue) would need software changes to a QH... */ static void qh_refresh (struct ehci_hcd *ehci, struct ehci_qh *qh) { … } /*-------------------------------------------------------------------------*/ static void qh_link_async(struct ehci_hcd *ehci, struct ehci_qh *qh); static void ehci_clear_tt_buffer_complete(struct usb_hcd *hcd, struct usb_host_endpoint *ep) { … } static void ehci_clear_tt_buffer(struct ehci_hcd *ehci, struct ehci_qh *qh, struct urb *urb, u32 token) { … } static int qtd_copy_status ( struct ehci_hcd *ehci, struct urb *urb, size_t length, u32 token ) { … } static void ehci_urb_done(struct ehci_hcd *ehci, struct urb *urb, int status) { … } static int qh_schedule (struct ehci_hcd *ehci, struct ehci_qh *qh); /* * Process and free completed qtds for a qh, returning URBs to drivers. * Chases up to qh->hw_current. Returns nonzero if the caller should * unlink qh. */ static unsigned qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh) { … } /*-------------------------------------------------------------------------*/ /* * reverse of qh_urb_transaction: free a list of TDs. * used for cleanup after errors, before HC sees an URB's TDs. */ static void qtd_list_free ( struct ehci_hcd *ehci, struct urb *urb, struct list_head *qtd_list ) { … } /* * create a list of filled qtds for this URB; won't link into qh. */ static struct list_head * qh_urb_transaction ( struct ehci_hcd *ehci, struct urb *urb, struct list_head *head, gfp_t flags ) { … } /*-------------------------------------------------------------------------*/ // Would be best to create all qh's from config descriptors, // when each interface/altsetting is established. Unlink // any previous qh and cancel its urbs first; endpoints are // implicitly reset then (data toggle too). // That'd mean updating how usbcore talks to HCDs. (2.7?) /* * Each QH holds a qtd list; a QH is used for everything except iso. * * For interrupt urbs, the scheduler must set the microframe scheduling * mask(s) each time the QH gets scheduled. For highspeed, that's * just one microframe in the s-mask. For split interrupt transactions * there are additional complications: c-mask, maybe FSTNs. */ static struct ehci_qh * qh_make ( struct ehci_hcd *ehci, struct urb *urb, gfp_t flags ) { … } /*-------------------------------------------------------------------------*/ static void enable_async(struct ehci_hcd *ehci) { … } static void disable_async(struct ehci_hcd *ehci) { … } /* move qh (and its qtds) onto async queue; maybe enable queue. */ static void qh_link_async (struct ehci_hcd *ehci, struct ehci_qh *qh) { … } /*-------------------------------------------------------------------------*/ /* * For control/bulk/interrupt, return QH with these TDs appended. * Allocates and initializes the QH if necessary. * Returns null if it can't allocate a QH it needs to. * If the QH has TDs (urbs) already, that's great. */ static struct ehci_qh *qh_append_tds ( struct ehci_hcd *ehci, struct urb *urb, struct list_head *qtd_list, int epnum, void **ptr ) { … } /*-------------------------------------------------------------------------*/ static int submit_async ( struct ehci_hcd *ehci, struct urb *urb, struct list_head *qtd_list, gfp_t mem_flags ) { … } /*-------------------------------------------------------------------------*/ #ifdef CONFIG_USB_HCD_TEST_MODE /* * This function creates the qtds and submits them for the * SINGLE_STEP_SET_FEATURE Test. * This is done in two parts: first SETUP req for GetDesc is sent then * 15 seconds later, the IN stage for GetDesc starts to req data from dev * * is_setup : i/p argument decides which of the two stage needs to be * performed; TRUE - SETUP and FALSE - IN+STATUS * Returns 0 if success */ static int ehci_submit_single_step_set_feature( struct usb_hcd *hcd, struct urb *urb, int is_setup ) { … } #endif /* CONFIG_USB_HCD_TEST_MODE */ /*-------------------------------------------------------------------------*/ static void single_unlink_async(struct ehci_hcd *ehci, struct ehci_qh *qh) { … } static void start_iaa_cycle(struct ehci_hcd *ehci) { … } static void end_iaa_cycle(struct ehci_hcd *ehci) { … } /* See if the async qh for the qtds being unlinked are now gone from the HC */ static void end_unlink_async(struct ehci_hcd *ehci) { … } static void start_unlink_async(struct ehci_hcd *ehci, struct ehci_qh *qh); static void unlink_empty_async(struct ehci_hcd *ehci) { … } #ifdef CONFIG_PM /* The root hub is suspended; unlink all the async QHs */ static void unlink_empty_async_suspended(struct ehci_hcd *ehci) { … } #endif /* makes sure the async qh will become idle */ /* caller must own ehci->lock */ static void start_unlink_async(struct ehci_hcd *ehci, struct ehci_qh *qh) { … } /*-------------------------------------------------------------------------*/ static void scan_async (struct ehci_hcd *ehci) { … }