On Fri, 2009-03-06 at 12:19 +0530, Vishal Thanki wrote:
> Hello,
>
> I have implemented a write() call for my character driver. I need to get
> the page address of the "__user" buffer passed in write() call from user
> space. The idea is to get the actual physical address corresponding to
> user address and then do DMA operations using that physical address. But
> for that I will require page address first. Is there any way I can get
> the corresponding page address of a "__user" buffer??
Hi Vishal,
Below is the code snippet which is doing something similar you want.
Key function you want is "page_address". I don't think you can directly
do DMA on user space buffers. You will have to map them in kernel space
first. (experts correct me if I am wrong).
/*
* Get your caller's mm to hold lock on its vm object and map the pages
* to kernel space.
*/
mm = current->mm;
/*
* Calculate the pages fom the buffer size given by user. The
get_user_pages
* needs the count in pages
*/
pgcount = ((unsigned long)buf+count+PAGE_SIZE-1)/PAGE_SIZE - (unsigned
long)buf/PAGE_SIZE;
if (pgcount >= MAX_SPI_TRANSFERS) {
printk(KERN_ERR "spidev: page count more than spi
transfers!\n");
kfree(list);
return -EFBIG;
}
/* allocate mapped pages list. */
maplist = kmalloc (pgcount * sizeof (struct page *), GFP_KERNEL);
if (!maplist) {
up(&gReadWriteLock);
printk(KERN_ERR "spidev: cannot allocate maplist!\n");
kfree(list);
return -ENOMEM;
}
flush_cache_all();
/*
* acquire the semaphore for mm structure of process. get_user_pages
* *thinks* we have acquired this semaphore. We are not sleeping
* interruptible. Once we are in sleep user won't be able to kill us.
*/
down_read(&mm->mmap_sem);
/*
* map the pages: They are mapped and locked, so we can directly hand
the
* address over to DMA for direct transfers
*/
err= get_user_pages(current, mm, (unsigned long)buf, pgcount, 1, 0,
maplist, NULL);
/* release */
up_read(&mm->mmap_sem);
if (err < 0) {
printk(KERN_ERR "spidev: Can't get user pages!\n");
/* do you clean ups and return */
}
pgcount = err;
/* What I am preparing here is a spi transfer list. This list has
physical page
* addresses for which DMA controller can be programmed.
*/
for (i = 0; i < pgcount; i++) {
flush_dcache_page(maplist[i]);
/* Your page address */
list->tx[i] = list->rx[i] = page_address(maplist[i]) + ofs;
list->txlen[i] = list->rxlen[i] = pagelen;
ofs = 0; /* all subsequent transfers start at beginning of a
page */
count = count - pagelen;
pagelen = (count < PAGE_SIZE) ? count : PAGE_SIZE;
}
list->nr_transfers = pgcount;
/* doing some other SPI transfer inits */
......
<snip>
/*
* release each page one by one. Note: we are not marking the pages as
dirty.
* Because the data is transferred and we don't need to swap it now
*/
while (pgcount--) {
page_cache_release (maplist[pgcount]);
}
------------
Hope this would help.
Regards
Chauhan
--
To unsubscribe from this list: send an email with
"unsubscribe kernelnewbies" to [email protected]
Please read the FAQ at http://kernelnewbies.org/FAQ