日期:2014-05-16  浏览次数:20759 次

自娱自乐9之Linux DMA使用1(三星平台DMA分析)

和以前一样,我不说dma基础知识,你可以看看ldd3


这次我说的是三星平台的dma,不是三星的某款芯片的dma使用。这主要得益于三星公司统一了接口。比如我后有的例子是在s3c2440上做的但是我是参考s3c64xx的spi驱动。
当然内核还是linux-3.2.36,我们看dma-ops.h

/* arch/arm/plat-samsung/include/plat/dma-ops.h
 *
 * Copyright (c) 2011 Samsung Electronics Co., Ltd.
 *        http://www.samsung.com
 *
 * Samsung DMA support
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */

#ifndef __SAMSUNG_DMA_OPS_H_
#define __SAMSUNG_DMA_OPS_H_ __FILE__

#include <linux/dmaengine.h>

struct samsung_dma_prep_info {
    enum dma_transaction_type cap;//dma处理类型
    enum dma_data_direction direction;//dma传输方向
    dma_addr_t buf;//内存地址
    unsigned long period;//
    unsigned long len;//buf长度,sizeof(buf) * width,width在下面struct samsung_dma_info
    /*
    .c中调用
    int len = (info->cap == DMA_CYCLIC) ? info->period : info->len;
    ...
    我不是太清楚period和len区别
    */
    void (*fp)(void *data);//dma中断时会调用,一般作为dma传输完成的接口
    void *fp_param;//传入上面fp的参数
};

struct samsung_dma_info {
    enum dma_transaction_type cap;//dma处理类型
    /*
            if (info->cap == DMA_CYCLIC)
                s3c2410_dma_setflags(dma_ch, S3C2410_DMAF_CIRCULAR);//chan->flags设置
    这个可能和芯片有点关系
    我的plat-s3c24xx中,通道请求函数
        if (chan->flags & S3C2410_DMAF_AUTOSTART) {//如果设置为自动运行,就调用start函数
            s3c2410_dma_ctrl(chan->number | DMACH_LOW_LEVEL,
                     S3C2410_DMAOP_START);
        }
    没有判断S3C2410_DMAF_CIRCULAR标志
    */

    enum dma_data_direction direction;//dma传输方向
    enum dma_slave_buswidth width;//要传输的数据宽度,就是(字节、半字、字)
    dma_addr_t fifo;//外设地址
    struct s3c2410_dma_client *client;
    /*
    struct s3c2410_dma_client {
        char                *name;
    };
    就是一个name,你申请通道时命名就可以了,主要dma中断注册是用、free通道时判断
    是不是正确通道
    */
};

struct samsung_dma_ops {
    unsigned (*request)(enum dma_ch ch, struct samsung_dma_info *info);//请求会有些限制
    //1.总是认为我们的外围设备是一个固定的地址
    //2.总是认为我们的内存地址增加
    //3.硬件触发
    //4.所有传输完成产生中断
    //上面这个从数据手册上看是可以设的,但是三星写的驱动代码没有选的可能
    int (*release)(unsigned ch, struct s3c2410_dma_client *client);//释放
    int (*prepare)(unsigned ch, struct samsung_dma_prep_info *info);//准备
    //准备会把内存数据加入链表中,如果有数据在传输,会打开再加载开关
    int (*trigger)(unsigned ch);//触发
    int (*started)(unsigned ch);//再次开始,实际就是再次载入数据
    int (*flush)(unsigned ch);//清除通道数据
    int (*stop)(unsigned ch);//停止
};

extern void *samsung_dmadev_get_ops(void);
extern void *s3c_dma_get_ops(void);

//去获取一个struct samsung_dma_ops全局变量,
//然后调用驱动,这个倒是给我们提供了一种驱动之间调用的方法
static inline void *__samsung_dma_get_ops(void)
{
    if (samsung_dma_is_dmadev())
        return samsung_dmadev_get_ops();
    else
        return s3c_dma_get_ops();
}

/*
 * samsung_dma_get_ops
 * get the set of samsung dma operations
 */
//在驱动中调用这个
#define samsung_dma_get_ops() __samsung_dma_get_ops()

#endif /* __SAMSUNG_DMA_OPS_H_ */

如果你只是想看看应用接口,你可以在此打住,直接看《自娱自乐10》中dma使用的例子,增加你的理解。

如果你和我一样死脑筋,你可以看看下面的源码分析:主要有三个文件

下面这个文件,看的时候不要纠结,主要是为第二个通道使用的,知道就行。

/* linux/arch/arm/plat-samsung/dma.c
 *
 * Copyright (c) 2003-2009 Simtec Electronics
 *	Ben Dooks <ben@simtec.co.uk>
 *	http://armlinux.simtec.co.uk/
 *
 * S3C DMA core
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
*/

struct s3c2410_dma_buf;
/*
struct s3c2410_dma_buf {
        struct s3c2410_dma_buf  *next;
        int                      magic;         // magic 
        int                      size;          // buffer size in bytes 
        dma_addr_t               data;          // start of DMA data 
        dma_addr_t               ptr;           // where the DMA got to [1] 
        void