Re: page address from user buffer

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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 ecartis@xxxxxxxxxxxx
Please read the FAQ at http://kernelnewbies.org/FAQ


[Index of Archives]     [Newbies FAQ]     [Linux Kernel Mentors]     [Linux Kernel Development]     [IETF Annouce]     [Git]     [Networking]     [Security]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux RAID]     [Linux SCSI]     [Linux ACPI]
  Powered by Linux