2008年9月21日 星期日

Implement mmap( ) in driver

其實實作mmap很簡單, 只要先知道幾個structure:task_struct, mm_struct, vm_area_struct. 然後想想mmap應該會做些什麼事情.
首先, 要map一個file必須先打開一個file, 然後想把某一段在這個device可控管的memory給user用到, 要填page table的對應, 其實只要知道user的virtual address, 及要被map的physical address, size就可以做到 ; 而這也就是remap_pfn_range()所做的事.
然而physical address 要怎麼得到呢?
這就要看要被map給user的memory是怎麼得來的, 而且這一段實體位置也必須是連續的, 所以當memory是不連續的時候就不能用remap_pfn_range()了.
kmalloc, get_free_page要到的都是連續的logical address, 所以可以用virt_to_phys來轉換拿到physical address. 而vmalloc則不適用.
IO的話, 其實在driver知道要access的實體位置, 所以也是用這個function時做就可以了.
例子 :
我有一個character device, 掌管4kbtyes的資料, 要給user去寫.
struct simple_dev{
unsigned int* data;
struct cdev scdev;
};
我在initialize的時候跟kernel要memory:
SimpleDevs[0].data = kmalloc(4096, GFP_KERNEL);
memset(SimpleDevs[0].data, 0, 4096);
然後open的時候, 把simple_dev填到file的private data:
dev = container_of(inode->i_cdev, struct simple_dev, scdev);
filp->private_data = dev;
mmap時, 就可以從file descriptor拿到device的data了, 把physical address轉換出來當remap_pfn_range的參數, 就可以map給user了:
dev = filp->private_data;
vma->vm_pgoff = (virt_to_phys(dev->data)) >> PAGE_SHIFT;

if (remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
vma->vm_end - vma->vm_start,
vma->vm_page_prot))
return -EAGAIN;

vma->vm_private_data = filp->private_data;
vma->vm_ops = &simple_remap_vm_ops;
simple_vma_open(vma);