How to do it...

Let's see how to do it by following these steps:

  1. Again, as in previous sections, the first step is adding our new mmap() method to the driver's struct file_operations:
static const struct file_operations chrdev_fops = {
.owner = THIS_MODULE,
.mmap = chrdev_mmap,
.unlocked_ioctl = chrdev_ioctl,
.llseek = chrdev_llseek,
.read = chrdev_read,
.write = chrdev_write,
.open = chrdev_open,
.release = chrdev_release
};
  1. Then, we add the chrdev_mmap() implementation, as explained in the previous section and reported in the following:
static int chrdev_mmap(struct file *filp, struct vm_area_struct *vma)
{
struct chrdev_device *chrdev = filp->private_data;
size_t size = vma->vm_end - vma->vm_start;
phys_addr_t offset = (phys_addr_t) vma->vm_pgoff << PAGE_SHIFT;
unsigned long pfn;

/* Does it even fit in phys_addr_t? */
if (offset >> PAGE_SHIFT != vma->vm_pgoff)
return -EINVAL;

/* We cannot mmap too big areas */
if ((offset > BUF_LEN) || (size > BUF_LEN - offset))
return -EINVAL;
  1. Then, we must get the physical address of the buf buffer:
    /* Get the physical address belong the virtual kernel address */
pfn = virt_to_phys(chrdev->buf) >> PAGE_SHIFT;
Note that this step won't be needed if we simply wanted to remap the physical address on which our peripheral is mapped.
  1. Finally, we can do the remap:
    /* Remap-pfn-range will mark the range VM_IO */
if (remap_pfn_range(vma, vma->vm_start,
pfn, size,
vma->vm_page_prot))
return -EAGAIN;

return 0;
}