// SPDX-License-Identifier: GPL-2.0
/*
* DMA memory management for framework level HCD code ( hc_driver )
*
* This implementation plugs in through generic " usb_bus " level methods ,
* and should work with all USB controllers , regardless of bus type .
*
* Released under the GPLv2 only .
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/mm.h>
#include <linux/io.h>
#include <linux/dma-mapping.h>
#include <linux/dmapool.h>
#include <linux/genalloc.h>
#include <linux/usb.h>
#include <linux/usb/hcd.h>
/*
* DMA - Coherent Buffers
*/
/* FIXME tune these based on pool statistics ... */
static size_t pool_max[HCD_BUFFER_POOLS] = {
32 , 128 , 512 , 2048 ,
};
void __init usb_init_pool_max(void )
{
/*
* The pool_max values must never be smaller than
* ARCH_DMA_MINALIGN .
*/
if (ARCH_DMA_MINALIGN <= 32 )
; /* Original value is okay */
else if (ARCH_DMA_MINALIGN <= 64 )
pool_max[0 ] = 64 ;
else if (ARCH_DMA_MINALIGN <= 128 )
pool_max[0 ] = 0 ; /* Don't use this pool */
else
BUILD_BUG(); /* We don't allow this */
}
/* SETUP primitives */
/**
* hcd_buffer_create - initialize buffer pools
* @ hcd : the bus whose buffer pools are to be initialized
*
* Context : task context , might sleep
*
* Call this as part of initializing a host controller that uses the dma
* memory allocators . It initializes some pools of dma - coherent memory that
* will be shared by all drivers using that controller .
*
* Call hcd_buffer_destroy ( ) to clean up after using those pools .
*
* Return : 0 if successful . A negative errno value otherwise .
*/
int hcd_buffer_create(struct usb_hcd *hcd)
{
char name[16 ];
int i, size;
if (hcd->localmem_pool || !hcd_uses_dma(hcd))
return 0 ;
for (i = 0 ; i < HCD_BUFFER_POOLS; i++) {
size = pool_max[i];
if (!size)
continue ;
snprintf(name, sizeof (name), "buffer-%d" , size);
hcd->pool[i] = dma_pool_create(name, hcd->self.sysdev,
size, size, 0 );
if (!hcd->pool[i]) {
hcd_buffer_destroy(hcd);
return -ENOMEM;
}
}
return 0 ;
}
/**
* hcd_buffer_destroy - deallocate buffer pools
* @ hcd : the bus whose buffer pools are to be destroyed
*
* Context : task context , might sleep
*
* This frees the buffer pools created by hcd_buffer_create ( ) .
*/
void hcd_buffer_destroy(struct usb_hcd *hcd)
{
int i;
if (!IS_ENABLED(CONFIG_HAS_DMA))
return ;
for (i = 0 ; i < HCD_BUFFER_POOLS; i++) {
dma_pool_destroy(hcd->pool[i]);
hcd->pool[i] = NULL;
}
}
/* sometimes alloc/free could use kmalloc with GFP_DMA, for
* better sharing and to leverage mm / slab . c intelligence .
*/
void *hcd_buffer_alloc(
struct usb_bus *bus,
size_t size,
gfp_t mem_flags,
dma_addr_t *dma
)
{
struct usb_hcd *hcd = bus_to_hcd(bus);
int i;
if (size == 0 )
return NULL;
if (hcd->localmem_pool)
return gen_pool_dma_alloc(hcd->localmem_pool, size, dma);
/* some USB hosts just use PIO */
if (!hcd_uses_dma(hcd)) {
*dma = ~(dma_addr_t) 0 ;
return kmalloc(size, mem_flags);
}
for (i = 0 ; i < HCD_BUFFER_POOLS; i++) {
if (size <= pool_max[i])
return dma_pool_alloc(hcd->pool[i], mem_flags, dma);
}
return dma_alloc_coherent(hcd->self.sysdev, size, dma, mem_flags);
}
void hcd_buffer_free(
struct usb_bus *bus,
size_t size,
void *addr,
dma_addr_t dma
)
{
struct usb_hcd *hcd = bus_to_hcd(bus);
int i;
if (!addr)
return ;
if (hcd->localmem_pool) {
gen_pool_free(hcd->localmem_pool, (unsigned long )addr, size);
return ;
}
if (!hcd_uses_dma(hcd)) {
kfree(addr);
return ;
}
for (i = 0 ; i < HCD_BUFFER_POOLS; i++) {
if (size <= pool_max[i]) {
dma_pool_free(hcd->pool[i], addr, dma);
return ;
}
}
dma_free_coherent(hcd->self.sysdev, size, addr, dma);
}
void *hcd_buffer_alloc_pages(struct usb_hcd *hcd,
size_t size, gfp_t mem_flags, dma_addr_t *dma)
{
if (size == 0 )
return NULL;
if (hcd->localmem_pool)
return gen_pool_dma_alloc_align(hcd->localmem_pool,
size, dma, PAGE_SIZE);
/* some USB hosts just use PIO */
if (!hcd_uses_dma(hcd)) {
*dma = DMA_MAPPING_ERROR;
return (void *)__get_free_pages(mem_flags,
get_order(size));
}
return dma_alloc_coherent(hcd->self.sysdev,
size, dma, mem_flags);
}
void hcd_buffer_free_pages(struct usb_hcd *hcd,
size_t size, void *addr, dma_addr_t dma)
{
if (!addr)
return ;
if (hcd->localmem_pool) {
gen_pool_free(hcd->localmem_pool,
(unsigned long )addr, size);
return ;
}
if (!hcd_uses_dma(hcd)) {
free_pages((unsigned long )addr, get_order(size));
return ;
}
dma_free_coherent(hcd->self.sysdev, size, addr, dma);
}
Messung V0.5 in Prozent C=88 H=36 G=66
¤ Dauer der Verarbeitung: 0.9 Sekunden
(vorverarbeitet am 2026-09-28)
¤
*© Formatika GbR, Deutschland