File Index
Files and directories
Sign in
FILE

xhci-sideband.c

File preview with syntax highlighting.

linux-6.19.14/drivers/usb/host/xhci-sideband.c C
  1// SPDX-License-Identifier: GPL-2.0
  2
  3/*
  4 * xHCI host controller sideband support
  5 *
  6 * Copyright (c) 2023-2025, Intel Corporation.
  7 *
  8 * Author: Mathias Nyman
  9 */
 10
 11#include <linux/usb/xhci-sideband.h>
 12#include <linux/dma-direct.h>
 13
 14#include "xhci.h"
 15
 16/* sideband internal helpers */
 17static struct sg_table *
 18xhci_ring_to_sgtable(struct xhci_sideband *sb, struct xhci_ring *ring)
 19{
 20	struct xhci_segment *seg;
 21	struct sg_table	*sgt;
 22	unsigned int n_pages;
 23	struct page **pages;
 24	struct device *dev;
 25	size_t sz;
 26	int i;
 27
 28	dev = xhci_to_hcd(sb->xhci)->self.sysdev;
 29	sz = ring->num_segs * TRB_SEGMENT_SIZE;
 30	n_pages = PAGE_ALIGN(sz) >> PAGE_SHIFT;
 31	pages = kvmalloc_array(n_pages, sizeof(struct page *), GFP_KERNEL);
 32	if (!pages)
 33		return NULL;
 34
 35	sgt = kzalloc(sizeof(*sgt), GFP_KERNEL);
 36	if (!sgt) {
 37		kvfree(pages);
 38		return NULL;
 39	}
 40
 41	seg = ring->first_seg;
 42	if (!seg)
 43		goto err;
 44	/*
 45	 * Rings can potentially have multiple segments, create an array that
 46	 * carries page references to allocated segments.  Utilize the
 47	 * sg_alloc_table_from_pages() to create the sg table, and to ensure
 48	 * that page links are created.
 49	 */
 50	for (i = 0; i < ring->num_segs; i++) {
 51		dma_get_sgtable(dev, sgt, seg->trbs, seg->dma,
 52				TRB_SEGMENT_SIZE);
 53		pages[i] = sg_page(sgt->sgl);
 54		sg_free_table(sgt);
 55		seg = seg->next;
 56	}
 57
 58	if (sg_alloc_table_from_pages(sgt, pages, n_pages, 0, sz, GFP_KERNEL))
 59		goto err;
 60
 61	/*
 62	 * Save first segment dma address to sg dma_address field for the sideband
 63	 * client to have access to the IOVA of the ring.
 64	 */
 65	sg_dma_address(sgt->sgl) = ring->first_seg->dma;
 66
 67	return sgt;
 68
 69err:
 70	kvfree(pages);
 71	kfree(sgt);
 72
 73	return NULL;
 74}
 75
 76/* Caller must hold sb->mutex */
 77static void
 78__xhci_sideband_remove_endpoint(struct xhci_sideband *sb, struct xhci_virt_ep *ep)
 79{
 80	lockdep_assert_held(&sb->mutex);
 81
 82	/*
 83	 * Issue a stop endpoint command when an endpoint is removed.
 84	 * The stop ep cmd handler will handle the ring cleanup.
 85	 */
 86	xhci_stop_endpoint_sync(sb->xhci, ep, 0, GFP_KERNEL);
 87
 88	ep->sideband = NULL;
 89	sb->eps[ep->ep_index] = NULL;
 90}
 91
 92/* Caller must hold sb->mutex */
 93static void
 94__xhci_sideband_remove_interrupter(struct xhci_sideband *sb)
 95{
 96	lockdep_assert_held(&sb->mutex);
 97
 98	if (!sb->ir)
 99		return;
100
101	xhci_remove_secondary_interrupter(xhci_to_hcd(sb->xhci), sb->ir);
102	sb->ir = NULL;
103}
104
105/* sideband api functions */
106
107/**
108 * xhci_sideband_notify_ep_ring_free - notify client of xfer ring free
109 * @sb: sideband instance for this usb device
110 * @ep_index: usb endpoint index
111 *
112 * Notifies the xHCI sideband client driver of a xHCI transfer ring free
113 * routine.  This will allow for the client to ensure that all transfers
114 * are completed.
115 *
116 * The callback should be synchronous, as the ring free happens after.
117 */
118void xhci_sideband_notify_ep_ring_free(struct xhci_sideband *sb,
119				       unsigned int ep_index)
120{
121	struct xhci_sideband_event evt;
122
123	evt.type = XHCI_SIDEBAND_XFER_RING_FREE;
124	evt.evt_data = &ep_index;
125
126	if (sb->notify_client)
127		sb->notify_client(sb->intf, &evt);
128}
129EXPORT_SYMBOL_GPL(xhci_sideband_notify_ep_ring_free);
130
131/**
132 * xhci_sideband_add_endpoint - add endpoint to sideband access list
133 * @sb: sideband instance for this usb device
134 * @host_ep: usb host endpoint
135 *
136 * Adds an endpoint to the list of sideband accessed endpoints for this usb
137 * device.
138 * After an endpoint is added the sideband client can get the endpoint transfer
139 * ring buffer by calling xhci_sideband_endpoint_buffer()
140 *
141 * Return: 0 on success, negative error otherwise.
142 */
143int
144xhci_sideband_add_endpoint(struct xhci_sideband *sb,
145			   struct usb_host_endpoint *host_ep)
146{
147	struct xhci_virt_ep *ep;
148	unsigned int ep_index;
149
150	guard(mutex)(&sb->mutex);
151
152	if (!sb->vdev)
153		return -ENODEV;
154
155	ep_index = xhci_get_endpoint_index(&host_ep->desc);
156	ep = &sb->vdev->eps[ep_index];
157
158	if (ep->ep_state & EP_HAS_STREAMS)
159		return -EINVAL;
160
161	/*
162	 * Note, we don't know the DMA mask of the audio DSP device, if its
163	 * smaller than for xhci it won't be able to access the endpoint ring
164	 * buffer. This could be solved by not allowing the audio class driver
165	 * to add the endpoint the normal way, but instead offload it immediately,
166	 * and let this function add the endpoint and allocate the ring buffer
167	 * with the smallest common DMA mask
168	 */
169	if (sb->eps[ep_index] || ep->sideband)
170		return -EBUSY;
171
172	ep->sideband = sb;
173	sb->eps[ep_index] = ep;
174
175	return 0;
176}
177EXPORT_SYMBOL_GPL(xhci_sideband_add_endpoint);
178
179/**
180 * xhci_sideband_remove_endpoint - remove endpoint from sideband access list
181 * @sb: sideband instance for this usb device
182 * @host_ep: usb host endpoint
183 *
184 * Removes an endpoint from the list of sideband accessed endpoints for this usb
185 * device.
186 * sideband client should no longer touch the endpoint transfer buffer after
187 * calling this.
188 *
189 * Return: 0 on success, negative error otherwise.
190 */
191int
192xhci_sideband_remove_endpoint(struct xhci_sideband *sb,
193			      struct usb_host_endpoint *host_ep)
194{
195	struct xhci_virt_ep *ep;
196	unsigned int ep_index;
197
198	guard(mutex)(&sb->mutex);
199
200	ep_index = xhci_get_endpoint_index(&host_ep->desc);
201	ep = sb->eps[ep_index];
202
203	if (!ep || !ep->sideband || ep->sideband != sb)
204		return -ENODEV;
205
206	__xhci_sideband_remove_endpoint(sb, ep);
207
208	return 0;
209}
210EXPORT_SYMBOL_GPL(xhci_sideband_remove_endpoint);
211
212int
213xhci_sideband_stop_endpoint(struct xhci_sideband *sb,
214			    struct usb_host_endpoint *host_ep)
215{
216	struct xhci_virt_ep *ep;
217	unsigned int ep_index;
218
219	ep_index = xhci_get_endpoint_index(&host_ep->desc);
220	ep = sb->eps[ep_index];
221
222	if (!ep || !ep->sideband || ep->sideband != sb)
223		return -EINVAL;
224
225	return xhci_stop_endpoint_sync(sb->xhci, ep, 0, GFP_KERNEL);
226}
227EXPORT_SYMBOL_GPL(xhci_sideband_stop_endpoint);
228
229/**
230 * xhci_sideband_get_endpoint_buffer - gets the endpoint transfer buffer address
231 * @sb: sideband instance for this usb device
232 * @host_ep: usb host endpoint
233 *
234 * Returns the address of the endpoint buffer where xHC controller reads queued
235 * transfer TRBs from. This is the starting address of the ringbuffer where the
236 * sideband client should write TRBs to.
237 *
238 * Caller needs to free the returned sg_table
239 *
240 * Return: struct sg_table * if successful. NULL otherwise.
241 */
242struct sg_table *
243xhci_sideband_get_endpoint_buffer(struct xhci_sideband *sb,
244				  struct usb_host_endpoint *host_ep)
245{
246	struct xhci_virt_ep *ep;
247	unsigned int ep_index;
248
249	ep_index = xhci_get_endpoint_index(&host_ep->desc);
250	ep = sb->eps[ep_index];
251
252	if (!ep || !ep->ring || !ep->sideband || ep->sideband != sb)
253		return NULL;
254
255	return xhci_ring_to_sgtable(sb, ep->ring);
256}
257EXPORT_SYMBOL_GPL(xhci_sideband_get_endpoint_buffer);
258
259/**
260 * xhci_sideband_get_event_buffer - return the event buffer for this device
261 * @sb: sideband instance for this usb device
262 *
263 * If a secondary xhci interupter is set up for this usb device then this
264 * function returns the address of the event buffer where xHC writes
265 * the transfer completion events.
266 *
267 * Caller needs to free the returned sg_table
268 *
269 * Return: struct sg_table * if successful. NULL otherwise.
270 */
271struct sg_table *
272xhci_sideband_get_event_buffer(struct xhci_sideband *sb)
273{
274	if (!sb || !sb->ir)
275		return NULL;
276
277	return xhci_ring_to_sgtable(sb, sb->ir->event_ring);
278}
279EXPORT_SYMBOL_GPL(xhci_sideband_get_event_buffer);
280
281/**
282 * xhci_sideband_check - check the existence of active sidebands
283 * @hcd: the host controller driver associated with the target host controller
284 *
285 * Allow other drivers, such as usb controller driver, to check if there are
286 * any sideband activity on the host controller. This information could be used
287 * for power management or other forms of resource management. The caller should
288 * ensure downstream usb devices are all marked as "offload_pm_locked" to ensure
289 * the correctness of the return value.
290 *
291 * Returns true on any active sideband existence, false otherwise.
292 */
293bool xhci_sideband_check(struct usb_hcd *hcd)
294{
295	struct usb_device *udev = hcd->self.root_hub;
296	bool active;
297
298	usb_lock_device(udev);
299	active = usb_offload_check(udev);
300	usb_unlock_device(udev);
301
302	return active;
303}
304EXPORT_SYMBOL_GPL(xhci_sideband_check);
305
306/**
307 * xhci_sideband_create_interrupter - creates a new interrupter for this sideband
308 * @sb: sideband instance for this usb device
309 * @num_seg: number of event ring segments to allocate
310 * @ip_autoclear: IP autoclearing support such as MSI implemented
311 *
312 * Sets up a xhci interrupter that can be used for this sideband accessed usb
313 * device. Transfer events for this device can be routed to this interrupters
314 * event ring by setting the 'Interrupter Target' field correctly when queueing
315 * the transfer TRBs.
316 * Once this interrupter is created the interrupter target ID can be obtained
317 * by calling xhci_sideband_interrupter_id()
318 *
319 * Returns 0 on success, negative error otherwise
320 */
321int
322xhci_sideband_create_interrupter(struct xhci_sideband *sb, int num_seg,
323				 bool ip_autoclear, u32 imod_interval, int intr_num)
324{
325	if (!sb || !sb->xhci)
326		return -ENODEV;
327
328	guard(mutex)(&sb->mutex);
329
330	if (!sb->vdev)
331		return -ENODEV;
332
333	if (sb->ir)
334		return -EBUSY;
335
336	sb->ir = xhci_create_secondary_interrupter(xhci_to_hcd(sb->xhci),
337						   num_seg, imod_interval,
338						   intr_num);
339	if (!sb->ir)
340		return -ENOMEM;
341
342	sb->ir->ip_autoclear = ip_autoclear;
343
344	return 0;
345}
346EXPORT_SYMBOL_GPL(xhci_sideband_create_interrupter);
347
348/**
349 * xhci_sideband_remove_interrupter - remove the interrupter from a sideband
350 * @sb: sideband instance for this usb device
351 *
352 * Removes a registered interrupt for a sideband.  This would allow for other
353 * sideband users to utilize this interrupter.
354 */
355void
356xhci_sideband_remove_interrupter(struct xhci_sideband *sb)
357{
358	if (!sb)
359		return;
360
361	guard(mutex)(&sb->mutex);
362
363	__xhci_sideband_remove_interrupter(sb);
364}
365EXPORT_SYMBOL_GPL(xhci_sideband_remove_interrupter);
366
367/**
368 * xhci_sideband_interrupter_id - return the interrupter target id
369 * @sb: sideband instance for this usb device
370 *
371 * If a secondary xhci interrupter is set up for this usb device then this
372 * function returns the ID used by the interrupter. The sideband client
373 * needs to write this ID to the 'Interrupter Target' field of the transfer TRBs
374 * it queues on the endpoints transfer ring to ensure transfer completion event
375 * are written by xHC to the correct interrupter event ring.
376 *
377 * Returns interrupter id on success, negative error othgerwise
378 */
379int
380xhci_sideband_interrupter_id(struct xhci_sideband *sb)
381{
382	if (!sb || !sb->ir)
383		return -ENODEV;
384
385	return sb->ir->intr_num;
386}
387EXPORT_SYMBOL_GPL(xhci_sideband_interrupter_id);
388
389/**
390 * xhci_sideband_register - register a sideband for a usb device
391 * @intf: usb interface associated with the sideband device
392 *
393 * Allows for clients to utilize XHCI interrupters and fetch transfer and event
394 * ring parameters for executing data transfers.
395 *
396 * Return: pointer to a new xhci_sideband instance if successful. NULL otherwise.
397 */
398struct xhci_sideband *
399xhci_sideband_register(struct usb_interface *intf, enum xhci_sideband_type type,
400		       int (*notify_client)(struct usb_interface *intf,
401				    struct xhci_sideband_event *evt))
402{
403	struct usb_device *udev = interface_to_usbdev(intf);
404	struct usb_hcd *hcd = bus_to_hcd(udev->bus);
405	struct xhci_hcd *xhci = hcd_to_xhci(hcd);
406	struct xhci_virt_device *vdev;
407	struct xhci_sideband *sb;
408
409	/*
410	 * Make sure the usb device is connected to a xhci controller.  Fail
411	 * registration if the type is anything other than  XHCI_SIDEBAND_VENDOR,
412	 * as this is the only type that is currently supported by xhci-sideband.
413	 */
414	if (!udev->slot_id || type != XHCI_SIDEBAND_VENDOR)
415		return NULL;
416
417	sb = kzalloc_node(sizeof(*sb), GFP_KERNEL, dev_to_node(hcd->self.sysdev));
418	if (!sb)
419		return NULL;
420
421	mutex_init(&sb->mutex);
422
423	/* check this device isn't already controlled via sideband */
424	spin_lock_irq(&xhci->lock);
425
426	vdev = xhci->devs[udev->slot_id];
427
428	if (!vdev || vdev->sideband) {
429		xhci_warn(xhci, "XHCI sideband for slot %d already in use\n",
430			  udev->slot_id);
431		spin_unlock_irq(&xhci->lock);
432		kfree(sb);
433		return NULL;
434	}
435
436	sb->xhci = xhci;
437	sb->vdev = vdev;
438	sb->intf = intf;
439	sb->type = type;
440	sb->notify_client = notify_client;
441	vdev->sideband = sb;
442
443	spin_unlock_irq(&xhci->lock);
444
445	return sb;
446}
447EXPORT_SYMBOL_GPL(xhci_sideband_register);
448
449/**
450 * xhci_sideband_unregister - unregister sideband access to a usb device
451 * @sb: sideband instance to be unregistered
452 *
453 * Unregisters sideband access to a usb device and frees the sideband
454 * instance.
455 * After this the endpoint and interrupter event buffers should no longer
456 * be accessed via sideband. The xhci driver can now take over handling
457 * the buffers.
458 */
459void
460xhci_sideband_unregister(struct xhci_sideband *sb)
461{
462	struct xhci_virt_device *vdev;
463	struct xhci_hcd *xhci;
464	int i;
465
466	if (!sb)
467		return;
468
469	xhci = sb->xhci;
470
471	scoped_guard(mutex, &sb->mutex) {
472		vdev = sb->vdev;
473		if (!vdev)
474			return;
475
476		for (i = 0; i < EP_CTX_PER_DEV; i++)
477			if (sb->eps[i])
478				__xhci_sideband_remove_endpoint(sb, sb->eps[i]);
479
480		__xhci_sideband_remove_interrupter(sb);
481
482		sb->vdev = NULL;
483	}
484
485	spin_lock_irq(&xhci->lock);
486	sb->xhci = NULL;
487	vdev->sideband = NULL;
488	spin_unlock_irq(&xhci->lock);
489
490	kfree(sb);
491}
492EXPORT_SYMBOL_GPL(xhci_sideband_unregister);
493MODULE_DESCRIPTION("xHCI sideband driver for secondary interrupter management");
494MODULE_LICENSE("GPL");
~/ Read-only file preview.