uio的全称是Userspace I/O。uio一般分为kernel part和user part。kernel part主要是调用uio_register_device 来注册一个字符设备,这样就在/dev/uiox 设备。假如只有一个uio设备的话。
则就会存在/dev/uio0 和 /sys/class/uio/uio0/maps0/addr /sys/class/uio/uio0/maps0/size.这样在用户态就可以通过下面的code read来得到addr和size,然后再通过mmap将这段空间map到user space,然后就可以想操作user space的memory一样来读写addr和size表示的空间。如果是中端的话一般通过select 来polling
从driver/uio的kconfig中可以看出只有config_uio 是架构相关的,其他都是具体的uio driver。
obj-$(CONFIG_UIO) += uio.o
obj-$(CONFIG_UIO_CIF) += uio_cif.o
obj-$(CONFIG_UIO_PDRV_GENIRQ) += uio_pdrv_genirq.o
obj-$(CONFIG_UIO_DMEM_GENIRQ) += uio_dmem_genirq.o
obj-$(CONFIG_UIO_AEC) += uio_aec.o
obj-$(CONFIG_UIO_SERCOS3) += uio_sercos3.o
obj-$(CONFIG_UIO_PCI_GENERIC) += uio_pci_generic.o
obj-$(CONFIG_UIO_NETX) += uio_netx.o
obj-$(CONFIG_UIO_PRUSS) += uio_pruss.o
obj-$(CONFIG_UIO_MF624) += uio_mf624.o
obj-$(CONFIG_UIO_FSL_ELBC_GPCM) += uio_fsl_elbc_gpcm.o
当定我们定义了CONFIG_UIO_DMEM_GENIRQ,因此在driver/uio 中就可以看到uio.o和uio_dmem_genirq.o
static struct platform_driver uio_dmem_genirq = {
.probe = uio_dmem_genirq_probe,
.remove = uio_dmem_genirq_remove,
.driver = {
.name = DRIVER_NAME,
.pm = &uio_dmem_genirq_dev_pm_ops,
.of_match_table = of_match_ptr(uio_of_genirq_match),
},
};
module_platform_driver(uio_dmem_genirq);
UIO_DMEM_GENIRQ 代表uio的kernel part,最终要的就是注册uio_register_device,因此在uio_dmem_genirq_probe中
static int uio_dmem_genirq_probe(struct platform_device *pdev)
{
struct uio_dmem_genirq_pdata *pdata = dev_get_platdata(&pdev->dev);
struct uio_info *uioinfo = &pdata->uioinfo;
struct uio_dmem_genirq_platdata *priv;
struct uio_mem *uiomem;
int ret = -EINVAL;
int i;
//apci mode所以pdev->dev.of_node 为null
if (pdev->dev.of_node) {
int irq;
/* alloc uioinfo for one device */
uioinfo = kzalloc(sizeof(*uioinfo), GFP_KERNEL);
if (!uioinfo) {
ret = -ENOMEM;
dev_err(&pdev->dev, "unable to kmalloc\n");
goto bad2;
}
uioinfo->name = pdev->dev.of_node->name;
uioinfo->version = "devicetree";
/* Multiple IRQs are not supported */
irq = platform_get_irq(pdev, 0);
if (irq == -ENXIO)
uioinfo->irq = UIO_IRQ_NONE;
else
uioinfo->irq = irq;
}
// 看来来在platform_data 中必须设定uioinfo中制定name和version
if (!uioinfo || !uioinfo->name || !uioinfo->version) {
dev_err(&pdev->dev, "missing platform_data\n");
goto bad0;
}
if (uioinfo->handler || uioinfo->irqcontrol ||
uioinfo->irq_flags & IRQF_SHARED) {
dev_err(&pdev->dev, "interrupt configuration error\n");
goto bad0;
}
// 申请一个uio_dmem_genirq_platdata 作为driver data,最后通过platform_set_drvdata(pdev, priv);设定driver data.
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv) {
ret = -ENOMEM;
dev_err(&pdev->dev, "unable to kmalloc\n");
goto bad0;
}
//将dma_mask 设定成32 ,所以uio driver只能用4G以内的memory
dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
priv->uioinfo = uioinfo;
spin_lock_init(&priv->lock);
priv->flags = 0; /* interrupt is enabled to begin with */
priv->pdev = pdev;
mutex_init(&priv->alloc_lock);
if (!uioinfo->irq) {
ret = platform_get_irq(pdev, 0);
if (ret < 0) {
dev_err(&pdev->dev, "failed to get IRQ\n");
goto bad1;
}
uioinfo->irq = ret;
}
uiomem = &uioinfo->mem[0];
//使用uio_mem 表示可以map的memory,最多只能map 5个#define MAX_UIO_MAPS 5
for (i = 0; i < pdev->num_resources; ++i) {
struct resource *r = &pdev->resource[i];
if (r->flags != IORESOURCE_MEM)
continue;
if (uiomem >= &uioinfo->mem[MAX_UIO_MAPS]) {
dev_warn(&pdev->dev, "device has more than "
__stringify(MAX_UIO_MAPS)
" I/O memory resources.\n");
break;
}
uiomem->memtype = UIO_MEM_PHYS;
uiomem->addr = r->start;
uiomem->size = resource_size(r);
++uiomem;
}
priv->dmem_region_start = uiomem - &uioinfo->mem[0];
priv->num_dmem_regions = pdata->num_dynamic_regions;
for (i = 0; i < pdata->num_dynamic_regions; ++i) {
if (uiomem >= &uioinfo->mem[MAX_UIO_MAPS]) {
dev_warn(&pdev->dev, "device has more than "
__stringify(MAX_UIO_MAPS)
" dynamic and fixed memory regions.\n");
break;
}
uiomem->memtype = UIO_MEM_PHYS;
uiomem->addr = DMEM_MAP_ERROR;
uiomem->size = pdata->dynamic_region_sizes[i];
++uiomem;
}
while (uiomem < &uioinfo->mem[MAX_UIO_MAPS]) {
uiomem->size = 0;
++uiomem;
}
/* This driver requires no hardware specific kernel code to handle
* interrupts. Instead, the interrupt handler simply disables the
* interrupt in the interrupt controller. User space is responsible
* for performing hardware specific acknowledge and re-enabling of
* the interrupt in the interrupt controller.
*
* Interrupt sharing is not supported.
*/
//给uioinfo 赋值。注册uio_register_device 最重要的结构,赋值后就调用uio_register_device 注册/dev/uioX
uioinfo->handler = uio_dmem_genirq_handler;
uioinfo->irqcontrol = uio_dmem_genirq_irqcontrol;
uioinfo->open = uio_dmem_genirq_open;
uioinfo->release = uio_dmem_genirq_release;
uioinfo->priv = priv;
/* Enable Runtime PM for this device:
* The device starts in suspended state to allow the hardware to be
* turned off by default. The Runtime PM bus code should power on the
* hardware and enable clocks at open().
*/
pm_runtime_enable(&pdev->dev);
ret = uio_register_device(&pdev->dev, priv->uioinfo);
if (ret) {
dev_err(&pdev->dev, "unable to register uio device\n");
pm_runtime_disable(&pdev->dev);
goto bad1;
}
platform_set_drvdata(pdev, priv);
return 0;
bad1:
kfree(priv);
bad0:
/* kfree uioinfo for OF */
if (pdev->dev.of_node)
kfree(uioinfo);
bad2:
return ret;
}
总结一下,在uioinfo->priv = priv;而priv的类型是uio_dmem_genirq_platdata,这样通过下面的code给uiomem 赋值
uiomem = &uioinfo->mem[0];
for (i = 0; i < pdev->num_resources; ++i) {
struct resource *r = &pdev->resource[i];
if (r->flags != IORESOURCE_MEM)
continue;
if (uiomem >= &uioinfo->mem[MAX_UIO_MAPS]) {
dev_warn(&pdev->dev, "device has more than "
__stringify(MAX_UIO_MAPS)
" I/O memory resources.\n");
break;
}
uiomem->memtype = UIO_MEM_PHYS;
uiomem->addr = r->start;
uiomem->size = resource_size(r);
++uiomem;
}
priv->dmem_region_start = uiomem - &uioinfo->mem[0];
这样在用户态通过open函数打开/dev/uioX 的时候会调用uioinfo->open = uio_dmem_genirq_open;中
static int uio_dmem_genirq_open(struct uio_info *info, struct inode *inode)
{
struct uio_dmem_genirq_platdata *priv = info->priv;
struct uio_mem *uiomem;
int ret = 0;
int dmem_region = priv->dmem_region_start;
uiomem = &priv->uioinfo->mem[priv->dmem_region_start];
mutex_lock(&priv->alloc_lock);
while (!priv->refcnt && uiomem < &priv->uioinfo->mem[MAX_UIO_MAPS]) {
void *addr;
if (!uiomem->size)
break;
addr = dma_alloc_coherent(&priv->pdev->dev, uiomem->size,
(dma_addr_t *)&uiomem->addr, GFP_KERNEL);
if (!addr) {
uiomem->addr = DMEM_MAP_ERROR;
}
priv->dmem_region_vaddr[dmem_region++] = addr;
++uiomem;
}
priv->refcnt++;
mutex_unlock(&priv->alloc_lock);
/* Wait until the Runtime PM code has woken up the device */
pm_runtime_get_sync(&priv->pdev->dev);
return ret;
}
通过uiomem = &priv->uioinfo->mem[priv->dmem_region_start]; 来得到uiomem,再通过dma_alloc_coherent将uio表示的memory做dma mapping