yiffer的个人空间 https://blog.eetop.cn/edesign [收藏] [复制] [分享] [RSS]

空间首页 动态 记录 日志 相册 主题 分享 留言板 个人资料

日志

WinDriver高级编程 (WinDriver 6.0 用户手册译文)

已有 10935 次阅读| 2009-6-6 23:22

8 chapter WinDriver高级编程
如果你的硬件使用下列芯片,你就不必学习本章了。因为向导已经为你准备好了有关的函数。这些芯片是:PLX/Altera/Marvell/PLDA/AMCC/QuickLogic/Cypress/STMicroelectronics/Texas Instruments and Xilinx。 如果你的芯片不在此列,请你认真学习下面的内容。
8.1 DMA 传输
使用DMA传输有两种方式:连续缓冲DMA 、离散DMA。离散DMA的效率比连续DMA 高,这种方式下Pci设备可以直接从不同的地址拷贝内存块,物理内存存在碎片,虚拟内存连续就可以和用户的缓冲区进行数据的交换。如果你的设备不支持离散DMA ,你就必须分配连续的物理内存给DMA 传输,然后再拷贝到你的数据缓冲区。
不同的PCI设备,DMA 编程也是很不相同的。一般情况下,你要明确PCI 设备的本地地址,主机地址(计算机中的物理内存地址),统计包(要传输的数据块)。给寄存器去传输。
8.1.1 离散DMA
DMA 传输举例
下面的过程介绍了离散DMA 传输,更详细的内容请参考:
\\ WinDriver\\plx\\9054\\lib\\p9054_lib.c
\\WinDriver\\plx\\9080\\lib\\p9080_lib.c
\\WinDriver\\marvell\\gt64\\lib\\gt64_lib.c
// DMA
BOOL DMA_routine(void *startAddress, DWORD transferCount,
BOOL fDirection)
{

WD_DMA dma; //
int i;
BZERO (dma);//清零
dma.pUserAddr = startAddress;
dma.dwBytes = transferCount;
dma.dwOptions = 0;
// lock region in memory
WD_DMALock(hWD,&dma);
if (dma.hDma==0)
return FALSE;
for(i=0;i!=dma.dwPages;i++)
{
// Program the registers for each page of the transfer
My_DMA_Program_Page(dma.Page[i].pPhysicalAddr,
dma.Page[i].dwBytes, fDir);
}
// write to the register that initiates the DMA transfer
My_DMA_Initiate();
// read register that tells when the DMA is done
while(!My_DMA_Done());
WD_DMAUnlock(hWD,&dma);
return TRUE;
}
你需要弄清楚什么?
。My_DMA_Program_page 将设备寄存器设为传输连接表的一部分。
。My_DMA_Initiate 初始化DMA的位。
.My_DMA_DONE 读设备的传输结束位

缓冲大于1M的离散DMA传输
支持256 页。X86 处理器的每页4k,256X4kb=1MB.由于第一页和最后一页不能作为边界,所以256页可以使用1MB-8KB.
如果你要锁定大于1的缓冲,你需要DMA_LARGE_BUFFER 选项。
BOOL DMA_Large_routine(void *startAddress,
DWORD transferCount, BOOL fDirection)
{
DWORD dwPagesNeeded = transferCount / 4096 + 2;
// WD_DMA structure already has space for WD_DMA_PAGES
// number of entries
WD_DMA *pDma=calloc(sizeof(WD_DMA)+sizeof(WD_DMA_PAGE)*
(dwPagesNeeded - WD_DMA_PAGES),1);
pDma->pUserAddr = startAddress;
pDma->dwBytes = transferCount;
pDma->dwOptions = DMA_LARGE_BUFFER;
pDma->dwPages = dwPagesNeeded;
// lock region in memory
WD_DMALock(hWD,pDma);
// the rest is the same as in the DMA_routine()
// free the WD_DMA structure allocated
free (pDma);
}
8.1.2 连续缓冲的DMA传输
更详细的粒子在下列文件中:
 WinDriver\\QuickLogic\\lib\\pbclib.c
 WinDriver\\amcc\\lib\\amcclib.c
读的顺序
下面是从板子读到主机内存的先后顺序。
{
WD_DMA dma;
BZERO (dma);
// allocate the DMA buffer (100000 bytes)
dma.pUserAddr = NULL;
dma.dwBytes = 10000;
dma.dwOptions = DMA_KERNEL_BUFFER_ALLOC;
WD_DMALock(hWD, &dma);
if (dma.hDma==0)
return FALSE;
// transfer data from the card to the buffer
My_Program_DMA_Transfer(dma.Page[0].pPhysicalAddr,
dma.Page[0].dwBytes, fDir);
// Wait for transfer to end
while(!My_Dma_Done());
// now the data is the buffer, and can be used
UseDataReadFromCard(dma.pUserAddr);
// release the buffer
WD_DMAUnlock(hWD,&dma);
}

写的顺序
下面是从主机内存写入板子的过程
{

WD_DMA dma;
BZERO (dma);
//allocate the DMA buffer (100000 bytes)
dma.pUserAddr = NULL;
dma.dwBytes = 10000;
dma.dwOptions = DMA_KERNEL_BUFFER_ALLOC;
WD_DMALock(hWD, &dma);
if (dma.hDma==0)
return FALSE;
// prepare data into buffer
PrepareDataInBuffer(dma.pUserAddr);
// transfer data from the buffer to the card
My_Program_DMA_Transfer(dma.Page[0].pPhysicalAddr,
LocalAddr);
// Wait for transfer to end
while(!My_Dma_Done());
// release the buffer
WD_DMAUnlock(hWD,&dma);
}
8.2 中断处理
向导很容易处理中断,尽可能的自动生成中断处理代码。下面的内容帮助你理解中断处理的机理,你可以按照这个思想写你自己的中断处理代码。
8.2.1 一般的中断处理
1 生成线程来处理引入的中断。
2 线程进入死循环等待中断请求。
3 有中断发生,驱动的中断代码被调用。
4 中断返回,循环继续等待。
WD_IntWait 函数先让线程休眠,有中断发生时,唤醒线程。
在中断的等待过程中没有占用CPU,一旦中断发生,WinDriver 内和首先响应,然后,WD_IntWait 唤醒中断处理线称并返回。
由于中断线程运行在用户模式下,你可以调用任何API 函数。
边缘触发中断例程(常用于ISA/EISA 卡)

// interrupt structure
WD_INTERRUPT Intrp;
DWORD WINAPI wait_interrupt (PVOID pData)
{
printf (\"Waiting for interrupt\");
for (;;)
{
WD_IntWait (hWD, &Intrp);
if (Intrp.fStopped)
break; // WD_IntDisable called by parent

// call your interrupt routine here
printf (\"Got interrupt %d\\n\", Intrp.dwCounter);
}
return 0;
}
void Install_interrupt()
{
BZERO(Intrp);
// put interrupt handle returned by WD_CardRegister
Intrp.hInterrupt = cardReg.Card.Item[0].I.Int.hInterrupt;
// no kernel transfer commands to do upon interrupt
Intrp.Cmd = NULL;
Intrp.dwCmds = 0;
// no special interrupt options
Intrp.dwOptions = 0;
WD_IntEnable(hWD, &Intrp);
if (!Intrp.fEnableOk)
{
printf (\"Failed enabling interrupt\\n\");
return;
}
printf (\"starting interrupt thread\\n\");
thread_handle = CreateThread (0, 0x1000,
wait_interrupt, NULL, 0, &thread_id);
// call your driver code here
WD_IntDisable (hWD, &Intrp);
WaitForSingleObject(thread_handle, INFINITE);
}

简化的中断处理
WinDriver 提供了方便的中断处理函数,InterruptEnable 和InterruptDisable 。下面的代码重写的中断处理函数。
VOID interrupt_handler (PVOID pData)
{
WD_INTERRUPT * pIntrp = (WD_INTERRUPT *) pData;
// do your interrupt routine here
printf (\"Got interrupt %d\\n\", pIntrp->dwCounter);
}

...

int main()
{
HANDLE hWD;
WD_CARD_REGISTER cardReg;
// interrupt structure
WD_INTERRUPT *pIntrp;
HANDLE thread_handle;
...
hWD = WD_Open();
BZERO(cardReg);
cardReg.Card.dwItems = 1;
cardReg.Card.Item[0].item = ITEM_INTERRUPT;
cardReg.Card.Item[0].fNotSharable = TRUE;
cardReg.Card.Item[0].I.Int.dwInterrupt = MY_IRQ;
cardReg.Card.Item[0].I.Int.dwOptions = 0;
...
WD_CardRegister (hWD, &cardReg);
...
pIntrp = malloc(sizeof(WD_INTERRUPT));
BZERO(*pIntrp);
pIntrp->hInterrupt =
cardReg.Card.Item[0].I.Int.hInterrupt;
printf (\"starting interrupt thread\\n\");
// this calls WD_IntEnable() and creates an interrupt
// handler thread

...
dwStatus = InterruptEnable(&thread_handle, hWD, pIntrp,
interrupt_handler, pIntrp))
if (dwStatus)
{
printf (\"failed enabling interrupt Status 0x%x - %s\\n\",
dwStatus, Stat2Str(dwStatus));
}
else
{
// call your driver code here
printf (\"Press Enter to uninstall interrupt\\n\");
fgets(line, sizeof(line), stdin);

// this calls WD_IntDisable()
InterruptDisable(thread_handle);
}
WD_CardUnregister(hWD, &cardReg);
free(pIntrp);
....
}
8.2.2 ISA/EISA PCI 中断
通常情况下, ISA/EISA 的中断是边缘触发,PCI 中断是级优先中断。这按时了重担处理过程的原理。
边缘触发中断 当物理中断信号从低电平转为高电平时,中断产生。操作系统调用内核释放中断等待线程,中断没有回应的项。
级优先中断 当物理中断信号为高电平时,中断产生。如果中断信号电平高于前次中断产生时的电平,操作系统再次调用WinDriver 内核处理中断,挂起CPU .为了防止这种情况的发生, 中断必须得到WinDriver 中断处理的确认。
内核级传输命令 通常PCI 的中断处理需要在内核级别传输命令,来确认中断。在WD_IntWait 返回之前,WinDriver 内核中断处理传输命令,你首先要准备命令数组
(WD_Transfer 结构) ,把它传递给WD_IntEnable函数。
举例如下:
WD_TRANSFER trans[2];
BZERO(trans);
trans[0].cmdTrans = RP_DWORD; // Read Port Dword
// Set address of IO port to write to:
trans[0].dwPort = dwAddr;
trans[1].cmdTrans = WP_DWORD; // Write Port Dword
// address of IO port to write to
trans[1].dwPort = dwAddr;
// the data to write to the IO port
trans[1].Data.Dword = 0;
Intrp.dwCmds = 2;
Intrp.Cmd = trans;
Intrp.dwOptions =
INTERRUPT_LEVEL_SENSITIVE | INTERRUPT_CMD_COPY;
WD_IntEnable(hWD, &Intrp);
这个例子完成从I/O 地址的 DWORD 的读命令,然后给I/O 地址填“0”。
INTERRUPT_CMD_COPY 选项用于再写命令执行以前,恢复读到的值。
当你要先读一个寄存器,后写寄存器的值时,这一点很有用。如果你试图在WD_IntWait之后读寄存器的值,会发现寄存器早已被填0了,因为写操作运行在内核级上。
DWORD WINAPI wait_interrupt(PVOID pData)
{
printf(\"Waiting for interrupt\\n\");
for (;;)
{
WD_TRANSFER trans[2];
Intrp.dwCmds = 2;
Intrp.Cmd = trans;
WD_IntWait(hWD, &Intrp);
if (Intrp.fStopped)
break; // WD_IntDisable called by parent

// call your interrupt routine here
printf(\"Got interrupt %d. Value of register read %x\\n\",
Intrp.dwCounter, trans[0].Data.Dword);
}
return 0;
}
回调函数优化中断处理
VxWorks 支持回调函数,如果用户设置了此项,回调函数回加速中断的确认和处理过程。但常用的操作平台不需要这个例程,因为各自的内核插件增强了中断的处理速度。
使用 windrvr_isr 例程:
1. Include the following declaration in your code:
int (__cdecl *windrvr_isr)(void);
2. Set windrvr_isr to point to the interrupt handler routine that you wish to have performed immediately upon the arrival of an interrupt. For example:
3. int __cdecl my_isr(void)
4. {
5. // Add code here in order to verify that the ISR is called.
6. return TRUE; // If TRUE, continue regular handling of WinDriver; if FALSE, exit ISR.
7. }
8.
9. extern int (__cdecl *windrvr_isr)(void);
10.
11. // after calling drvrInit()
12. windrvr_isr = my_isr;

文章来源:http://bbs.driverdevelop.com/htm_data/13/0304/38573.html


点赞

评论 (0 个评论)

facelist

您需要登录后才可以评论 登录 | 注册

  • 关注TA
  • 加好友
  • 联系TA
  • 0

    周排名
  • 0

    月排名
  • 0

    总排名
  • 0

    关注
  • 3

    粉丝
  • 0

    好友
  • 20

    获赞
  • 69

    评论
  • 3705

    访问数
关闭

站长推荐 上一条 /1 下一条

小黑屋| 关于我们| 联系我们| 在线咨询| 隐私声明| EETOP 创芯网
( 京ICP备:10050787号 京公网安备:11010502037710 )

GMT+8, 2024-4-25 19:44 , Processed in 0.021012 second(s), 6 queries , Gzip On, Redis On.

eetop公众号 创芯大讲堂 创芯人才网
返回顶部