首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

DMA过程详解(2)

DMA过程详解(2)

以下是write系统调用内核态处理函数的路径:
经过一系列处理,write系统调用处理结束后,若需要写磁盘数据最终会经过以下路径:
scsi_scan_target(scsi扫面函数)——》__scsi_scan_target ——》scsi_sequential_lun_scan ——》scsi_probe_and_add_lun ——>scsi_alloc_sdev ——》scsi_alloc_queue(scsi分配队列),从这里分开,一条路径是设置DMA并发送命令到DMA控制器(路径一),另一条是初始化函数路径(路径二)。
路径一:scsi_request_fn——>scsi_dispatch_cmd——》scsi_log_send——》(.queuecommand           =ata_scsi_queuecmd,)ata_scsi_queuecmd——》__ata_scsi_queuecmd——》ata_scsi_translate——》ata_qc_issue——》ata_bmdma_qc_issue——》(bfin_bmdma_setup:设置DMA寄存器/ bfin_bmdma_start:开始DMA)
路径二:scsi_prep_fn——>scsi_setup_blk_pc_cmnd ——》scsi_init_io ——》scsi_init_sgtable ——》blk_rq_map_sg(该函数的参数request这个结构体封装了bio结构体).
以下主要分析bfin_bmdma_setup和bfin_bmdma_start函数,即DMA操作过程:
(1)    软件准备好一个PRD Table放在内存中,每个8字节,对齐到4字节边界。
(2)    软件把PRD table的起始地址设置好,同时通过设置读/写控制位设置数据和传输方向,清除状态寄存器中的中断位和错误位。
(3)    软件发出DMA传送指令到disk设备。
(4)    向总线控制器IDE命令寄存器的对应通道中写入1,使能总线控制器。
(5)    DMA从IDE设备中请求控制器传送数据到/从内存中
(6)    传送结束,IDE设备发出中断
(7)    接收到中断后,软件设置命令寄存器的开始/结束位,然后先后读控制器状态、驱动状态,进而确定是否传送成功。
代码如下:
/**
*     bfin_bmdma_setup- Set up IDE DMA transaction
*     @qc:Info associated with this ATA transaction.
*
*     Note:Original code is ata_bmdma_setup().
*/

static void bfin_bmdma_setup(struct ata_queued_cmd *qc)
{
         struct ata_port *ap = qc->ap;
         /*下面结构体就是scatterlist 结构体的封装,其指向所有scatterlist 在内存中的位置,对应以上步骤(2)*/
         struct dma_desc_array *dma_desc_cpu =(struct dma_desc_array *)ap->bmdma_prd;
         void __iomem *base = (void __iomem*)ap->ioaddr.ctl_addr;
         /*DMA配置*/
         unsigned short config = DMAFLOW_ARRAY |NDSIZE_5 | RESTART | WDSIZE_16 | DMAEN;
         struct scatterlist *sg;
         unsigned int si;
         unsigned int channel;
         unsigned int dir;
         unsigned int size = 0;
         dev_dbg(qc->ap->dev, "inatapi dma setup\n");
         /* Program the ATA_CTRL register withdir */
         /*设置ATA控制寄存器,与DMA控制寄存器无关*/
         if (qc->tf.flags &ATA_TFLAG_WRITE) {
                  channel = CH_ATAPI_TX;
                  dir = DMA_TO_DEVICE;
         } else {
                  channel = CH_ATAPI_RX;
                  dir = DMA_FROM_DEVICE;
                  config |= WNR;
         }
dma_map_sg(ap->dev,qc->sg, qc->n_elem, dir);
         /* fill the ATAPI DMA controller *//挨个填写sg结构体,sg结构体用来指向每个要传输的内存块,对应以上步骤(1)
         for_each_sg(qc->sg, sg,qc->n_elem, si) {
                  dma_desc_cpu[si].start_addr =sg_dma_address(sg);
                  dma_desc_cpu[si].cfg = config;
                  dma_desc_cpu[si].x_count =sg_dma_len(sg) >> 1;
                  dma_desc_cpu[si].x_modify = 2;
                  size += sg_dma_len(sg);
         }


         /* Send ATA DMA command */
         /*这里要注意,虽然是发送DMA命令,但是真正的DMA操作还没开始;
         *该函数中有设置各种ATA设备寄存器,并等待设置结束后返回
         */
         bfin_exec_command(ap, &qc->tf);
//根据初始化时设置的IO操作方向,确定DMA方向,对应步骤(2)
         if (qc->tf.flags &ATA_TFLAG_WRITE) {
                  /*set ATA DMA write direction */
                  ATAPI_SET_CONTROL(base,(ATAPI_GET_CONTROL(base)
                          | XFER_DIR));
         } else {
                  /* set ATA DMA read direction*/
                  ATAPI_SET_CONTROL(base,(ATAPI_GET_CONTROL(base)
                          & ~XFER_DIR));
         }

         /* Reset all transfer count */
         ATAPI_SET_CONTROL(base,ATAPI_GET_CONTROL(base) | TFRCNT_RST);

         /* Set ATAPI state machine contorl interminate sequence */
         ATAPI_SET_CONTROL(base,ATAPI_GET_CONTROL(base) | END_ON_TERM);

         /* Set transfer length to the totalsize of sg buffers */
         ATAPI_SET_XFER_LEN(base, size >>1);
/**
*     bfin_bmdma_start- Start an IDE DMA transaction
*     @qc:Info associated with this ATA transaction.
*
*     Note:Original code is ata_bmdma_start().
*/ static void bfin_bmdma_start(structata_queued_cmd *qc)
{
         struct ata_port *ap = qc->ap;
         void __iomem *base = (void __iomem*)ap->ioaddr.ctl_addr;

         dev_dbg(qc->ap->dev, "inatapi dma start\n");

         if (!(ap->udma_mask ||ap->mwdma_mask))
                  return;

         /* start ATAPI transfer*/
         if (ap->udma_mask)
                  ATAPI_SET_CONTROL(base, ATAPI_GET_CONTROL(base)
                          | ULTRA_START);
         else
                  ATAPI_SET_CONTROL(base,ATAPI_GET_CONTROL(base)
                          | MULTI_START);
}
返回列表