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

Reply via email to