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



更多推荐

uio的mmap的memory是通过dma_alloc_coherent来做映射的