Part of the WritingPortableDrivers section
As we saw in the example in InternalKernelDataTypes taken from drivers/char/serial.c, you can ask the kernel for a memory page. The size of a memory page is not always 4KB of data (as it is on i386). If you are going to be referencing memory pages, you need to use the PAGE_SHIFT and PAGE_SIZE defines.
PAGE_SHIFT is the number of bits to shift one bit left to get the PAGE_SIZE value. Different architectures define this to different values. The table below shows a short list of some architectures and the values of PAGE_SHIFT and the resulting value for PAGE_SIZE.
Architecture |
PAGE_SHIFT |
PAGE_SIZE |
i386 |
12 |
4K |
MIPS |
12 |
4K |
Alpha |
13 |
8K |
m68k |
12 |
4K |
m68k |
13 |
8K |
ARM |
12 |
4K |
ARM |
14 |
16K |
ARM |
15 |
32K |
IA-64 |
12 |
4K |
IA-64 |
13 |
8K |
IA-64 |
14 |
16K |
IA-64 |
16 |
64K |
Even on the same base architecture type, you can have different page sizes. This depends sometimes on a configuration option (like IA-64) or is due to different variants of the processor type (like on ARM).
The code snippet from drivers/usb/class/audio.c below shows how PAGE_SHIFT and PAGE_SIZE are used when accessing memory directly:
1 static int dmabuf_mmap(...)
2 {
3 size >>= PAGE_SHIFT;
4 for(nr = 0; nr < size; nr++)
5 if (!db->sgbuf[nr])
6 return -EINVAL;
7 db->mapped = 1;
8 for (nr = 0; nr < size; nr++) {
9 unsigned long pfn;
10
11 pfn = virt_to_phys(db->sgbuf[nr]) >> PAGE_SHIFT;
12 if (remap_pfn_range(vma, start, pfn, PAGE_SIZE, prot))
13 return -EAGAIN;
14 start += PAGE_SIZE;
15 }
16 return 0;
17 }