科技行者

行者学院 转型私董会 科技行者专题报道 网红大战科技行者

知识库

知识库 安全导航

至顶网软件频道Linux系统对ISA总线DMA的实现(下) (3)

Linux系统对ISA总线DMA的实现(下) (3)

  • 扫一扫
    分享文章到微信

  • 扫一扫
    关注官方公众号
    至顶头条

DMAC是一种全局的共享资源,为了保证设备驱动程序对它的独占访问,Linux在kernel/dma.c文件中定义了自旋锁dma_spin_lock来保护它(实际上是保护DMAC的I/O端口资源)。任何想要访问DMAC的设备驱动程序都首先必须先持有自旋锁dma_spin_lock。

作者:hehyuan 来源:赛迪网技术社区 2007年11月17日

关键字: 操作系统 实现 ISA Linux

  • 评论
  • 分享微博
  • 分享邮件
 

5 使用DMA的ISA设备驱动程序

DMA虽然是一种硬件机制,但它离不开软件(尤其是设备驱动程序)的配合。任何使用DMA进行数据传输的ISA设备驱动程序都必须遵循一定的框架。

5.1 DMA通道资源的申请与释放

同I/O端口资源类似,设备驱动程序必须在一开始就调用request_dma()函数来向内核申请DMA通道资源的使用权。而且,最好在设备驱动程序的open()方法中完成这个操作,而不是在模块的初始化例程中调用这个函数。因为这在一定程度上可以让多个设备共享DMA通道资源(只要多个设备不同时使用一个DMA通道)。这种共享有点类似于进程对CPU的分时共享:-)

设备使用完DMA通道后,其驱动程序应该记得调用free_dma()函数来释放所占用的DMA通道资源。通常,最好再驱动程序的release()方法中调用该函数,而不是在模块的卸载例程中进行调用。

还需要注意的一个问题是:资源的申请顺序。为了避免死锁(deadlock),驱动程序一定要在申请了中断号资源后才申请DMA通道资源。释放时则要先释放DMA通道,然后再释放中断号资源。

使用DMA的ISA设备驱动程序的open()方法的如下:

int xxx_open(struct inode * inode, struct file * filp)
{
┆
if((err = request_irq(irq,xxx_ISR,SA_INTERRUPT,”YourDeviceName”,NULL))
return err;
if((err = request_dma(dmanr, “YourDeviceName”)){
free_irq(irq, NULL);
return err;
}
┆
return 0;
} 

release()方法的范例代码如下:

void xxx_release(struct inode * inode, struct file * filp)
{
┆
free_dma(dmanr);
free_irq(irq,NULL);
┆
}

5.2 申请DMA缓冲区

由于8237 DMAC只能寻址系统RAM中低16MB物理内存,因此:ISA设备驱动程序在申请DMA缓冲区时,一定要以GFP_DMA标志来调用kmalloc()函数或get_free_pages()函数,以便在系统内存的DMA区中分配物理内存。

5.3 编程DMAC

设备驱动程序可以在他的read()方法、write()方法或ISR中对DMAC进行编程,以便准备启动一个DMA传输事务。一个DMA传输事务有两种典型的过程:(1)用户请求设备进行DMA传输;(2)硬件异步地将外部数据写道系统中。

用户通过I/O请求触发设备进行DMA传输的步骤如下:

1.用户进程通过系统调用read()/write()来调用设备驱动程序的read()方法或write()方法,然后由设备驱动程序read/write方法负责申请DMA缓冲区,对DMAC进行编程,以准备启动一个DMA传输事务,最后正确地设置设备(setup device),并将用户进程投入睡眠。

2.DMAC负责在DMA缓冲区和I/O外设之间进行数据传输,并在结束后触发一个中断。

3.设备的ISR检查DMA传输事务是否成功地结束,并将数据从DMA缓冲区中拷贝到驱动程序的其他内核缓冲区中(对于I/O device to memory的情况)。然后唤醒睡眠的用户进程。

硬件异步地将外部数据写到系统中的步骤如下:

1.外设触发一个中断通知系统有新数据到达。

2.ISR申请一个DMA缓冲区,并对DMAC进行编程,以准备启动一个DMA传输事务,最后正确地设置好外设。

3.硬件将外部数据写到DMA缓冲区中,DMA传输事务结束后,触发一个中断。

4. ISR检查DMA传输事务是否成功地结束,然后将DMA缓冲区中的数据拷贝驱动程序的其他内核缓冲区中,最后唤醒相关的等待进程。

网卡就是上述过程的一个典型例子。

为准备一个DMA传输事务而对DMAC进行编程的典型代码段如下:

  unsigned long flags;
  flags = claim_dma_lock();
  disable_dma(dmanr);
  clear_dma_ff(dmanr);
  set_dma_mode(dmanr,mode);
  set_dma_addr(dmanr, virt_to_bus(buf));
  set_dma_count(dmanr, count);
  enable_dma(dmanr);
  release_dma_lock(flags);

检查一个DMA传输事务是否成功地结束的代码段如下:

int residue;
unsigned long flags = claim_dma_lock();
residue = get_dma_residue(dmanr);
release_dma_lock(flags);
ASSERT(residue == 0);

注:本节大部分内容来自于ldd2。

    • 评论
    • 分享微博
    • 分享邮件
    邮件订阅

    如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。

    重磅专题
    往期文章
    最新文章