我實際上正在處理完全相同的事情,現在我要去ioctl()
路線。總體思路是爲用戶空間分配將用於DMA傳輸的緩衝區,並使用ioctl()
將此緩衝區的大小和地址傳遞給設備驅動程序。然後,驅動程序將使用分散 - 收集列表以及流式DMA API來直接傳輸數據到設備和用戶空間緩衝區,以及從設備和用戶空間緩衝區直接傳輸數據。
我正在使用的實現策略是,驅動程序中的ioctl()
進入一個循環,即DMA的用戶空間緩衝區大小爲256k(這是硬件對可處理多少個分散/收集條目的限制)。這被隔離在一個阻塞直到每個傳輸完成的函數中(見下文)。當所有的字節被轉移或增量傳遞函數返回一個錯誤的ioctl()
退出並返回到用戶空間
僞代碼增量傳遞函數ioctl()
/*serialize all DMA transfers to/from the device*/
if (mutex_lock_interruptible(&device_ptr->mtx))
return -EINTR;
chunk_data = (unsigned long) user_space_addr;
while(*transferred < total_bytes && !ret) {
chunk_bytes = total_bytes - *transferred;
if (chunk_bytes > HW_DMA_MAX)
chunk_bytes = HW_DMA_MAX; /* 256kb limit imposed by my device */
ret = transfer_chunk(device_ptr, chunk_data, chunk_bytes, transferred);
chunk_data += chunk_bytes;
chunk_offset += chunk_bytes;
}
mutex_unlock(&device_ptr->mtx);
僞代碼:
/*Assuming the userspace pointer is passed as an unsigned long, */
/*calculate the first,last, and number of pages being transferred via*/
first_page = (udata & PAGE_MASK) >> PAGE_SHIFT;
last_page = ((udata+nbytes-1) & PAGE_MASK) >> PAGE_SHIFT;
first_page_offset = udata & PAGE_MASK;
npages = last_page - first_page + 1;
/* Ensure that all userspace pages are locked in memory for the */
/* duration of the DMA transfer */
down_read(¤t->mm->mmap_sem);
ret = get_user_pages(current,
current->mm,
udata,
npages,
is_writing_to_userspace,
0,
&pages_array,
NULL);
up_read(¤t->mm->mmap_sem);
/* Map a scatter-gather list to point at the userspace pages */
/*first*/
sg_set_page(&sglist[0], pages_array[0], PAGE_SIZE - fp_offset, fp_offset);
/*middle*/
for(i=1; i < npages-1; i++)
sg_set_page(&sglist[i], pages_array[i], PAGE_SIZE, 0);
/*last*/
if (npages > 1) {
sg_set_page(&sglist[npages-1], pages_array[npages-1],
nbytes - (PAGE_SIZE - fp_offset) - ((npages-2)*PAGE_SIZE), 0);
}
/* Do the hardware specific thing to give it the scatter-gather list
and tell it to start the DMA transfer */
/* Wait for the DMA transfer to complete */
ret = wait_event_interruptible_timeout(&device_ptr->dma_wait,
&device_ptr->flag_dma_done, HZ*2);
if (ret == 0)
/* DMA operation timed out */
else if (ret == -ERESTARTSYS)
/* DMA operation interrupted by signal */
else {
/* DMA success */
*transferred += nbytes;
return 0;
}
中斷處理程序是非常簡單的:
/* Do hardware specific thing to make the device happy */
/* Wake the thread waiting for this DMA operation to complete */
device_ptr->flag_dma_done = 1;
wake_up_interruptible(device_ptr->dma_wait);
請注意,這只是一個一般的方法,我一直工作在這個驅動程序在過去幾個星期了,還沒有實際測試它......所以,請不要把這個僞代碼爲福音,是一定要仔細檢查所有的邏輯和參數;-)。
以來,它一直在Linux下我最後的驅動程序開發的年齡,但我一直保留DMA可用內存,然後被映射到用戶頁面。在過去,並非所有的地址都可以用於DMA傳輸。現在可能並非如此。另一個提示可能是查看Linux驅動程序的視頻,尤其是涉及BT848/BT878芯片的視頻。我希望你能在那裏找到有用的東西。 – jdehaan 2011-04-04 13:51:37
我知道這個問題是類似的:http://stackoverflow.com/q/3333959/119790 – 2011-04-04 14:08:45
我正在處理類似的問題。我很想知道你最終走了哪條路。 – mksuth 2011-06-30 14:47:25