1、DMA的功能和工作原理这里就不多说了,可以查看s3c2440的手册
2、在正式分析DMA驱动之前,我们先来看一下DMA的注册和初始化过程
系统设备:(翻译自源码注释)
系统设备和系统模型有点不同,它不需要动态绑定驱动,不能被探测(probe),不归结为任何的系统总线,所以要区分对待。对待系统设备我们仍然要有设备驱动的观念,因为我们需要对设备进行基本的操作。
定义系统设备,在./arch/arm/mach-s3c2440/s3c244x.c中
view plain copy to clipboard print ?
-
- struct sysdev_class s3c2440_sysclass = {
- .name = "s3c2440-core",
- .suspend = s3c244x_suspend,
- .resume = s3c244x_resume
- };
注册系统设备类,在真正注册设备之前,确保已经注册了初始化了的系统设备类
view plain copy to clipboard print ?
- static int __init s3c2440_core_init(void)
- {
- return sysdev_class_register(&s3c2440_sysclass);
- }
下面就是系统设备类的注册函数,在./drivers/base/sys.c中
view plain copy to clipboard print ?
- int sysdev_class_register(struct sysdev_class *cls)
- {
- int retval;
-
- pr_debug("Registering sysdev class '%s'\n", cls->name);
-
- INIT_LIST_HEAD(&cls->drivers);
- memset(&cls->kset.kobj, 0x00, sizeof(struct kobject));
- cls->kset.kobj.parent = &system_kset->kobj;
- cls->kset.kobj.ktype = &ktype_sysdev_class;
- cls->kset.kobj.kset = system_kset;
-
- retval = kobject_set_name(&cls->kset.kobj, "%s", cls->name);
- if (retval)
- return retval;
-
- retval = kset_register(&cls->kset);
- if (!retval && cls->attrs)
- retval = sysfs_create_files(&cls->kset.kobj,
- (const struct attribute **)cls->attrs);
- return retval;
- }
view plain copy to clipboard print ?
-
- static struct sysdev_driver s3c2440_dma_driver = {
- .add = s3c2440_dma_add,
- };
下面是add函数,就是调用三个函数
view plain copy to clipboard print ?
- static int __init s3c2440_dma_add(struct sys_device *sysdev)
- {
- s3c2410_dma_init();
- s3c24xx_dma_order_set(&s3c2440_dma_order);
- return s3c24xx_dma_init_map(&s3c2440_dma_sel);
- }
注册DMA驱动到系统设备
view plain copy to clipboard print ?
- static int __init s3c2440_dma_init(void)
- {
- return sysdev_driver_register(&s3c2440_sysclass, &s3c2440_dma_driver);
- }
下面就是系统设备驱动的注册函数
view plain copy to clipboard print ?
-
-
-
-
-
-
-
-
-
-
- int sysdev_driver_register(struct sysdev_class *cls, struct sysdev_driver *drv)
- {
- int err = 0;
-
- if (!cls) {
- WARN(1, KERN_WARNING "sysdev: invalid class passed to "
- "sysdev_driver_register!\n");
- return -EINVAL;
- }
-
-
- if (drv->entry.next && !list_empty(&drv->entry))
- WARN(1, KERN_WARNING "sysdev: class %s: driver (%p) has already"
- " been registered to a class, something is wrong, but "
- "will forge on!\n", cls->name, drv);
-
- mutex_lock(&sysdev_drivers_lock);
- if (cls && kset_get(&cls->kset)) {
- list_add_tail(&drv->entry, &cls->drivers);
-
-
- if (drv->add) {
- struct sys_device *dev;
- list_for_each_entry(dev, &cls->kset.list, kobj.entry)
- drv->add(dev);
- }
- } else {
- err = -EINVAL;
- WARN(1, KERN_ERR "%s: invalid device class\n", __func__);
- }
- mutex_unlock(&sysdev_drivers_lock);
- return err;
- }
在./arch/arm/mach-s3c2440/s3c2440.c中定义s3c2440的系统设备和注册
view plain copy to clipboard print ?
- static struct sys_device s3c2440_sysdev = {
- .cls = &s3c2440_sysclass,
- };
-
- int __init s3c2440_init(void)
- {
- printk("S3C2440: Initialising architecture\n");
-
- s3c24xx_gpiocfg_default.set_pull = s3c_gpio_setpull_1up;
- s3c24xx_gpiocfg_default.get_pull = s3c_gpio_getpull_1up;
-
-
-
- s3c_device_wdt.resource[1].start = IRQ_S3C2440_WDT;
- s3c_device_wdt.resource[1].end = IRQ_S3C2440_WDT;
-
-
-
- return sysdev_register(&s3c2440_sysdev);
- }
接下来是系统设备的注册函数
view plain copy to clipboard print ?
-
-
-
-
-
-
- int sysdev_register(struct sys_device *sysdev)
- {
- int error;
- struct sysdev_class *cls = sysdev->cls;
-
- if (!cls)
- return -EINVAL;
-
- pr_debug("Registering sys device of class '%s'\n",
- kobject_name(&cls->kset.kobj));
-
-
- memset(&sysdev->kobj, 0x00, sizeof(struct kobject));
-
-
- sysdev->kobj.kset = &cls->kset;
-
-
- error = kobject_init_and_add(&sysdev->kobj, &ktype_sysdev, NULL,
- "%s%d", kobject_name(&cls->kset.kobj),
- sysdev->id);
-
- if (!error) {
- struct sysdev_driver *drv;
-
- pr_debug("Registering sys device '%s'\n",
- kobject_name(&sysdev->kobj));
-
- mutex_lock(&sysdev_drivers_lock);
-
-
-
-
-
- list_for_each_entry(drv, &cls->drivers, entry) {
- if (drv->add)
- drv->add(sysdev);
- }
- mutex_unlock(&sysdev_drivers_lock);
- kobject_uevent(&sysdev->kobj, KOBJ_ADD);
- }
-
- return error;
- }
那DMA系统设备驱动中的add函数中到底是什么呢?
(1)首先看第一个函数int __init s3c2410_dma_init(void),在./arch/arm/plat-s3c24xx/dma.c
view plain copy to clipboard print ?
- int __init s3c2410_dma_init(void)
- {
- return s3c24xx_dma_init(4, IRQ_DMA0, 0x40);
- }
实际上就是初始化DMA为4通道,设置中断号,设置寄存器的覆盖范围
下面是该函数的实现
view plain copy to clipboard print ?
- int __init s3c24xx_dma_init(unsigned int channels, unsigned int irq,
- unsigned int stride)
- {
- struct s3c2410_dma_chan *cp;
- int channel;
- int ret;
-
- printk("S3C24XX DMA Driver, Copyright 2003-2006 Simtec Electronics\n");
-
- dma_channels = channels;
-
- dma_base = ioremap(S3C24XX_PA_DMA, stride * channels);
- if (dma_base == NULL) {
- printk(KERN_ERR "dma failed to remap register block\n");
- return -ENOMEM;
- }
-
-
- dma_kmem = kmem_cache_create("dma_desc",
- sizeof(struct s3c2410_dma_buf), 0,
- SLAB_HWCACHE_ALIGN,
- s3c2410_dma_cache_ctor);
-
- if (dma_kmem == NULL) {
- printk(KERN_ERR "dma failed to make kmem cache\n");
- ret = -ENOMEM;
- goto err;
- }
-
- for (channel = 0; channel < channels; channel++) {
- cp = &s3c2410_chans[channel];
-
- memset(cp, 0, sizeof(struct s3c2410_dma_chan));
-
-
- cp->number = channel;
- cp->irq = channel + irq;
- cp->regs = dma_base + (channel * stride);
-
-
- cp->stats = &cp->stats_store;
- cp->stats_store.timeout_shortest = LONG_MAX;
-
-
-
- cp->load_timeout = 1<<18;
-
- printk("DMA channel %d at %p, irq %d\n",
- cp->number, cp->regs, cp->irq);
- }
-
- return 0;
-
-
- err:
- kmem_cache_destroy(dma_kmem);
- iounmap(dma_base);
- dma_base = NULL;
- return ret;
- }
(2)然后是函数s3c24xx_dma_order_set(&s3c2440_dma_order);
view plain copy to clipboard print ?
- int __init s3c24xx_dma_order_set(struct s3c24xx_dma_order *ord)
- {
- struct s3c24xx_dma_order *nord = dma_order;
-
- if (nord == NULL)
- nord = kmalloc(sizeof(struct s3c24xx_dma_order), GFP_KERNEL);
-
- if (nord == NULL) {
- printk(KERN_ERR "no memory to store dma channel order\n");
- return -ENOMEM;
- }
-
- dma_order = nord;
- memcpy(nord, ord, sizeof(struct s3c24xx_dma_order));
- return 0;
- }
我们注意到函数中使用了kmalloc给结构体重新分配了内存,这是由于__initdata修饰的变量表示初始化用的变量,初始化完毕后空间自动释放,所以需要将其存储起来。
(3)最后一个函数s3c24xx_dma_init_map(&s3c2440_dma_sel)
该函数功能是建立DMA源与硬件通道的映射图
view plain copy to clipboard print ?
- int __init s3c24xx_dma_init_map(struct s3c24xx_dma_selection *sel)
- {
- struct s3c24xx_dma_map *nmap;
- size_t map_sz = sizeof(*nmap) * sel->map_size;
- int ptr;
-
- nmap = kmalloc(map_sz, GFP_KERNEL);
- if (nmap == NULL)
- return -ENOMEM;
-
- memcpy(nmap, sel->map, map_sz);
- memcpy(&dma_sel, sel, sizeof(*sel));
-
- dma_sel.map = nmap;
-
- for (ptr = 0; ptr < sel->map_size; ptr++)
- s3c24xx_dma_check_entry(nmap+ptr, ptr);
-
- return 0;
- }
这里的kmalloc函数的作用同上面的作用一样。
注:由于内核实在是太深了,这里只是表面上按流程大体了解了子同设备的注册和系统设备驱动的注册以及DMA设备的注册和初始化,函数中有很多细节有待进一步研究。