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