linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Jeff Garzik <jgarzik@mandrakesoft.com>
To: Linux Kernel Mailing List <linux-kernel@vger.kernel.org>
Subject: PATCH 2.4.0.10.6: video4linux API update, bttv mmap rewrite
Date: Sat, 28 Oct 2000 13:02:45 -0400	[thread overview]
Message-ID: <39FB06B5.E52682B9@mandrakesoft.com> (raw)

[-- Attachment #1: Type: text/plain, Size: 924 bytes --]

Thought some people here might be interested in this too...  Description
quoted below.
-- 
Jeff Garzik             | "Mind if I drive?"  -Sam
Building 1024           | "Not if you don't mind me clawing at the
MandrakeSoft            |  dash and screaming like a cheerleader."
                        |      -Max


> This patch, against 2.4.0-test10-pre6, updates bttv to use the new and
> groovy method of supporting mmap.
> 
> Advantages:
> * Code more simple.
> * mmap now only limited to number of free pages in the system (busts vmalloc limits)
> * mmap no longer requires vmalloc, or remap_page_range.
> 
> Disadvantages:
> * Requires videodev API update (new member: mmap_vma).
> * KNOWN BUG: If your gbufsize causes a frame to cross a page boundary,
> you lose.  The code doesn't support this, and doesn't check for it
> either.  Boom.  (this is fixable though)
> * Totally untested.  I don't own any bttv hardware.
>

[-- Attachment #2: bttv-mmap.patch --]
[-- Type: text/plain, Size: 10777 bytes --]

Index: include/linux/videodev.h
===================================================================
RCS file: /cvsroot/gkernel/linux_2_4/include/linux/videodev.h,v
retrieving revision 1.1.1.1
diff -u -r1.1.1.1 videodev.h
--- include/linux/videodev.h	2000/10/22 19:36:11	1.1.1.1
+++ include/linux/videodev.h	2000/10/28 07:31:20
@@ -32,6 +32,7 @@
 	int busy;
 	int minor;
 	devfs_handle_t devfs_handle;
+	int (*mmap_vma)(struct file *, struct vm_area_struct *, struct video_device *);
 };
 
 extern int videodev_init(void);
Index: drivers/media/video/bttv-driver.c
===================================================================
RCS file: /cvsroot/gkernel/linux_2_4/drivers/media/video/bttv-driver.c,v
retrieving revision 1.1.1.2
diff -u -r1.1.1.2 bttv-driver.c
--- drivers/media/video/bttv-driver.c	2000/10/22 22:02:34	1.1.1.2
+++ drivers/media/video/bttv-driver.c	2000/10/28 07:31:24
@@ -142,16 +142,6 @@
 	return ret;
 }
 
-static inline unsigned long uvirt_to_bus(unsigned long adr) 
-{
-        unsigned long kva, ret;
-
-        kva = uvirt_to_kva(pgd_offset(current->mm, adr), adr);
-	ret = virt_to_bus((void *)kva);
-        MDEBUG(printk("uv2b(%lx-->%lx)", adr, ret));
-        return ret;
-}
-
 static inline unsigned long kvirt_to_bus(unsigned long adr) 
 {
         unsigned long va, kva, ret;
@@ -163,79 +153,60 @@
         return ret;
 }
 
-/* Here we want the physical address of the memory.
- * This is used when initializing the contents of the
- * area and marking the pages as reserved.
+/*
+ *	Alloc and free DMA pages for mmap(2)
  */
-static inline unsigned long kvirt_to_pa(unsigned long adr) 
-{
-        unsigned long va, kva, ret;
-
-        va = VMALLOC_VMADDR(adr);
-        kva = uvirt_to_kva(pgd_offset_k(va), va);
-	ret = __pa(kva);
-        MDEBUG(printk("kv2pa(%lx-->%lx)", adr, ret));
-        return ret;
-}
-
-static void * rvmalloc(signed long size)
+ 
+static void free_dmabuffers(struct bttv *btv)
 {
-	void * mem;
-	unsigned long adr, page;
+	unsigned int i;
 
-	mem=vmalloc_32(size);
-	if (mem) 
-	{
-		memset(mem, 0, size); /* Clear the ram out, no junk to the user */
-	        adr=(unsigned long) mem;
-		while (size > 0) 
-                {
-	                page = kvirt_to_pa(adr);
-			mem_map_reserve(virt_to_page(__va(page)));
-			adr+=PAGE_SIZE;
-			size-=PAGE_SIZE;
-		}
+	if (btv->page) {
+		for (i = 0; i < btv->n_pages; i++)
+			if (btv->page[i].cpuaddr)
+				pci_free_consistent (btv->dev, PAGE_SIZE,
+						     btv->page[i].cpuaddr,
+						     btv->page[i].handle);
+		memset(btv->page, 0, sizeof(btv->page) * btv->n_pages);
+		btv->n_pages = 0;
+		kfree(btv->page);
+		btv->page = NULL;
 	}
-	return mem;
 }
 
-static void rvfree(void * mem, signed long size)
+static int alloc_dmabuffers(struct bttv *btv)
 {
-        unsigned long adr, page;
-        
-	if (mem) 
-	{
-	        adr=(unsigned long) mem;
-		while (size > 0) 
-                {
-	                page = kvirt_to_pa(adr);
-			mem_map_unreserve(virt_to_page(__va(page)));
-			adr+=PAGE_SIZE;
-			size-=PAGE_SIZE;
-		}
-		vfree(mem);
-	}
-}
+	unsigned int i;
 
+	if (btv->page) {
+		printk(KERN_ERR "bttv%d: Double alloc of DMA pages!\n",	btv->nr);
+		return 0;
+	}
 
+	btv->n_pages = (gbuffers * gbufsize) >> PAGE_SHIFT;
+	if ((gbuffers * gbufsize) % PAGE_SIZE)
+		btv->n_pages++;
 
-/*
- *	Create the giant waste of buffer space we need for now
- *	until we get DMA to user space sorted out (probably 2.3.x)
- *
- *	We only create this as and when someone uses mmap
- */
- 
-static int fbuffer_alloc(struct bttv *btv)
-{
-	if(!btv->fbuffer)
-		btv->fbuffer=(unsigned char *) rvmalloc(gbuffers*gbufsize);
-	else
-		printk(KERN_ERR "bttv%d: Double alloc of fbuffer!\n",
-			btv->nr);
-	if(!btv->fbuffer)
-		return -ENOBUFS;
+	btv->page = kmalloc (sizeof(btv->page) * btv->n_pages, GFP_KERNEL);
+	if (!btv->page) {
+		btv->n_pages = 0;
+		return -ENOMEM;
+	}
+	memset (btv->page, 0, sizeof(btv->page) * btv->n_pages);
+		
+	for (i = 0; i < btv->n_pages; i++) {
+		btv->page[i].cpuaddr = pci_alloc_consistent (
+			btv->dev, PAGE_SIZE, &btv->page[i].handle);
+		if (!btv->page[i].cpuaddr)
+			goto err_out;
+		memset(btv->page[i].cpuaddr, 0, PAGE_SIZE);
+	}
+	
 	return 0;
+
+err_out:
+	free_dmabuffers(btv);
+	return -ENOMEM;
 }
 
 
@@ -1265,13 +1236,13 @@
 static int vgrab(struct bttv *btv, struct video_mmap *mp)
 {
 	unsigned int *ro, *re;
-	unsigned int *vbuf;
+	unsigned int *vbuf, page, page_ofs;
 	unsigned long flags;
 	
-	if(btv->fbuffer==NULL)
+	if (btv->page == NULL)
 	{
-		if(fbuffer_alloc(btv))
-			return -ENOBUFS;
+		int rc = alloc_dmabuffers(btv);
+		if (rc) return rc;
 	}
 
 	if(mp->frame >= gbuffers || mp->frame < 0)
@@ -1294,7 +1265,9 @@
 	 *	Ok load up the BT848
 	 */
 	 
-	vbuf=(unsigned int *)(btv->fbuffer+gbufsize*mp->frame);
+	page = (gbufsize * mp->frame) >> PAGE_SHIFT;
+	page_ofs = (gbufsize * mp->frame) % PAGE_SIZE;
+	vbuf = (unsigned int *) (btv->page[page].cpuaddr + page_ofs);
 	ro=btv->gbuf[mp->frame].risc;
 	re=ro+2048;
         make_vrisctab(btv, ro, re, vbuf, mp->width, mp->height, mp->format);
@@ -1433,9 +1406,8 @@
 	if (btv->user)
 		goto out_unlock;
 	
-	btv->fbuffer=(unsigned char *) rvmalloc(gbuffers*gbufsize);
-	ret = -ENOMEM;
-	if (!btv->fbuffer)
+	ret = alloc_dmabuffers(btv);
+	if (ret)
 		goto out_unlock;
 
         btv->gq_in = 0;
@@ -1495,9 +1467,8 @@
 	 *	We have allowed it to drain.
 	 */
 
-	if(btv->fbuffer)
-		rvfree((void *) btv->fbuffer, gbuffers*gbufsize);
-	btv->fbuffer=0;
+	if (btv->page)
+		free_dmabuffers(btv);
 	up(&btv->lock);
 	MOD_DEC_USE_COUNT;  
 }
@@ -2166,45 +2137,81 @@
 	return 0;
 }
 
+static struct page * bttv_mm_nopage (struct vm_area_struct * vma,
+				     unsigned long address, int write_access)
+{
+	struct bttv *btv = vma->vm_private_data;
+	struct page *dmapage;
+	unsigned long pgoff;
+
+        if (address > vma->vm_end) return NOPAGE_SIGBUS; /* Disallow mremap */
+        if (!btv) return NOPAGE_OOM;	/* Nothing allocated */
+
+	pgoff = vma->vm_pgoff + ((address - vma->vm_start) >> PAGE_SHIFT);
+	if (pgoff > btv->n_pages) return NOPAGE_SIGBUS;
+
+	dmapage = virt_to_page (btv->page[pgoff].cpuaddr);
+	get_page (dmapage);
+	return dmapage;
+}
+
+#ifndef VM_RESERVE
+static int bttv_mm_swapout (struct page *page, struct file *filp)
+{
+	return 0;
+}
+#endif /* VM_RESERVE */
+
+struct vm_operations_struct bttv_mm_ops = {
+	nopage:		bttv_mm_nopage,
+#ifndef VM_RESERVE
+	swapout:	bttv_mm_swapout,
+#endif
+};
+
 /*
  *	This maps the vmalloced and reserved fbuffer to user space.
- *
- *  FIXME: 
- *  - PAGE_READONLY should suffice!?
- *  - remap_page_range is kind of inefficient for page by page remapping.
- *    But e.g. pte_alloc() does not work in modules ... :-(
  */
 
-static int do_bttv_mmap(struct bttv *btv, const char *adr, unsigned long size)
+static int do_bttv_mmap (struct file *file, struct vm_area_struct *vma,
+			 struct bttv *btv)
 {
-        unsigned long start=(unsigned long) adr;
-        unsigned long page,pos;
+	int rc = -EINVAL;
+	unsigned long max_size, size, start, offset;
 
-        if (size>gbuffers*gbufsize)
-                return -EINVAL;
-        if (!btv->fbuffer) {
-                if(fbuffer_alloc(btv))
-                        return -EINVAL;
-        }
-        pos=(unsigned long) btv->fbuffer;
-        while (size > 0) {
-                page = kvirt_to_pa(pos);
-                if (remap_page_range(start, page, PAGE_SIZE, PAGE_SHARED))
-                        return -EAGAIN;
-                start+=PAGE_SIZE;
-                pos+=PAGE_SIZE;
-                size-=PAGE_SIZE;
-        }
-        return 0;
+	max_size = (gbuffers * gbufsize);
+
+	start = vma->vm_start;
+	offset = (vma->vm_pgoff << PAGE_SHIFT);
+	size = vma->vm_end - vma->vm_start;
+
+	/* some basic size/offset sanity checks */
+	if (size > max_size)
+		goto out;
+	if (offset > max_size - size)
+		goto out;
+
+	vma->vm_ops = &bttv_mm_ops;
+	vma->vm_private_data = btv;
+
+#ifdef VM_RESERVE
+	vma->vm_flags |= VM_RESERVE;
+#endif
+
+	rc = 0;
+
+out:
+	return rc;
 }
 
-static int bttv_mmap(struct video_device *dev, const char *adr, unsigned long size)
+static int bttv_mmap_vma(struct file *file, struct vm_area_struct *vma,
+			 struct video_device *dev)
 {
         struct bttv *btv=(struct bttv *)dev;
         int r;
 
         down(&btv->lock);
-        r=do_bttv_mmap(btv, adr, size);
+        r=do_bttv_mmap(file, vma, btv);
         up(&btv->lock);
         return r;
 }
@@ -2212,20 +2219,18 @@
 
 static struct video_device bttv_template=
 {
-	"UNSET",
-	VID_TYPE_TUNER|VID_TYPE_CAPTURE|VID_TYPE_OVERLAY|VID_TYPE_TELETEXT,
-	VID_HARDWARE_BT848,
-	bttv_open,
-	bttv_close,
-	bttv_read,
-	bttv_write,
-	NULL,
-	bttv_ioctl,
-	bttv_mmap,
-	bttv_init_done,
-	NULL,
-	0,
-	-1
+	name:		"UNSET",
+	type:		VID_TYPE_TUNER|VID_TYPE_CAPTURE|
+			VID_TYPE_OVERLAY|VID_TYPE_TELETEXT,
+	hardware:	VID_HARDWARE_BT848,
+	open:		bttv_open,
+	close:		bttv_close,
+	read:		bttv_read,
+	write:		bttv_write,
+	ioctl:		bttv_ioctl,
+	mmap_vma:	bttv_mmap_vma,
+	initialize:	bttv_init_done,
+	minor:		-1,
 };
 
 
@@ -2753,7 +2758,8 @@
 	memset(btv->vbibuf, 0, VBIBUF_SIZE); /* We don't want to return random
 	                                        memory to the user */
 
-	btv->fbuffer=NULL;
+	btv->page = NULL;
+	btv->n_pages = 0;
 
 	bt848_muxsel(btv, 1);
 	bt848_set_winsize(btv);
Index: drivers/media/video/bttv.h
===================================================================
RCS file: /cvsroot/gkernel/linux_2_4/drivers/media/video/bttv.h,v
retrieving revision 1.1.1.2
diff -u -r1.1.1.2 bttv.h
--- drivers/media/video/bttv.h	2000/10/22 22:02:34	1.1.1.2
+++ drivers/media/video/bttv.h	2000/10/28 07:31:24
@@ -251,6 +251,13 @@
 	unsigned long re;
 };
 
+
+struct bttv_dma {
+	void *cpuaddr;
+	dma_addr_t handle;
+};
+
+
 struct bttv {
 	struct video_device video_dev;
 	struct video_device radio_dev;
@@ -312,7 +319,9 @@
 	struct bttv_gbuf *gbuf;
 	int gqueue[MAX_GBUFFERS];
 	int gq_in,gq_out,gq_grab,gq_start;
-        char *fbuffer;
+	
+	struct bttv_dma *page;
+	unsigned int n_pages;
 
 	struct bttv_pll_info pll;
 	unsigned int Fsc;
Index: drivers/media/video/videodev.c
===================================================================
RCS file: /cvsroot/gkernel/linux_2_4/drivers/media/video/videodev.c,v
retrieving revision 1.1.1.1
diff -u -r1.1.1.1 videodev.c
--- drivers/media/video/videodev.c	2000/10/22 21:28:40	1.1.1.1
+++ drivers/media/video/videodev.c	2000/10/28 07:31:24
@@ -227,7 +227,9 @@
 {
 	int ret = -EINVAL;
 	struct video_device *vfl=video_device[MINOR(file->f_dentry->d_inode->i_rdev)];
-	if(vfl->mmap) {
+	if (vfl->mmap_vma)
+		return vfl->mmap_vma (file, vma, vfl);
+	if (vfl->mmap) {
 		lock_kernel();
 		ret = vfl->mmap(vfl, (char *)vma->vm_start, 
 				(unsigned long)(vma->vm_end-vma->vm_start));

                 reply	other threads:[~2000-10-28 17:03 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=39FB06B5.E52682B9@mandrakesoft.com \
    --to=jgarzik@mandrakesoft.com \
    --cc=linux-kernel@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).