Merge tag 'r8169-20060920-00' of git://electric-eye.fr.zoreil.com/home/romieu/linux...
[pandora-kernel.git] / drivers / media / video / pwc / pwc-if.c
index 4141829..d470394 100644 (file)
@@ -1,7 +1,7 @@
 /* Linux driver for Philips webcam
    USB and Video4Linux interface part.
    (C) 1999-2004 Nemosoft Unv.
-   (C) 2004      Luc Saillard (luc@saillard.org)
+   (C) 2004-2006 Luc Saillard (luc@saillard.org)
 
    NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx
    driver and thus may have bugs that are not present in the original version.
 #include <linux/poll.h>
 #include <linux/slab.h>
 #include <linux/vmalloc.h>
+#include <linux/version.h>
 #include <asm/io.h>
+#include <linux/moduleparam.h>
 
 #include "pwc.h"
-#include "pwc-ioctl.h"
 #include "pwc-kiara.h"
 #include "pwc-timon.h"
+#include "pwc-dec23.h"
+#include "pwc-dec1.h"
 #include "pwc-uncompress.h"
 
 /* Function prototypes and driver templates */
 
 /* hotplug device table support */
-static struct usb_device_id pwc_device_table [] = {
+static const struct usb_device_id pwc_device_table [] = {
        { USB_DEVICE(0x0471, 0x0302) }, /* Philips models */
        { USB_DEVICE(0x0471, 0x0303) },
        { USB_DEVICE(0x0471, 0x0304) },
@@ -81,9 +84,10 @@ static struct usb_device_id pwc_device_table [] = {
        { USB_DEVICE(0x0471, 0x0308) },
        { USB_DEVICE(0x0471, 0x030C) },
        { USB_DEVICE(0x0471, 0x0310) },
-       { USB_DEVICE(0x0471, 0x0311) },
+       { USB_DEVICE(0x0471, 0x0311) }, /* Philips ToUcam PRO II */
        { USB_DEVICE(0x0471, 0x0312) },
        { USB_DEVICE(0x0471, 0x0313) }, /* the 'new' 720K */
+       { USB_DEVICE(0x0471, 0x0329) }, /* Philips SPC 900NC PC Camera */
        { USB_DEVICE(0x069A, 0x0001) }, /* Askey */
        { USB_DEVICE(0x046D, 0x08B0) }, /* Logitech QuickCam Pro 3000 */
        { USB_DEVICE(0x046D, 0x08B1) }, /* Logitech QuickCam Notebook Pro */
@@ -94,8 +98,9 @@ static struct usb_device_id pwc_device_table [] = {
        { USB_DEVICE(0x046D, 0x08B6) }, /* Logitech (reserved) */
        { USB_DEVICE(0x046D, 0x08B7) }, /* Logitech (reserved) */
        { USB_DEVICE(0x046D, 0x08B8) }, /* Logitech (reserved) */
-       { USB_DEVICE(0x055D, 0x9000) }, /* Samsung */
-       { USB_DEVICE(0x055D, 0x9001) },
+       { USB_DEVICE(0x055D, 0x9000) }, /* Samsung MPC-C10 */
+       { USB_DEVICE(0x055D, 0x9001) }, /* Samsung MPC-C30 */
+       { USB_DEVICE(0x055D, 0x9002) }, /* Samsung SNC-35E (Ver3.0) */
        { USB_DEVICE(0x041E, 0x400C) }, /* Creative Webcam 5 */
        { USB_DEVICE(0x041E, 0x4011) }, /* Creative Webcam Pro Ex */
        { USB_DEVICE(0x04CC, 0x8116) }, /* Afina Eye */
@@ -122,11 +127,13 @@ static struct usb_driver pwc_driver = {
 static int default_size = PSZ_QCIF;
 static int default_fps = 10;
 static int default_fbufs = 3;   /* Default number of frame buffers */
-static int default_mbufs = 2;  /* Default number of mmap() buffers */
-       int pwc_trace = TRACE_MODULE | TRACE_FLOW | TRACE_PWCX;
+       int pwc_mbufs = 2;      /* Default number of mmap() buffers */
+#if CONFIG_PWC_DEBUG
+       int pwc_trace = PWC_DEBUG_LEVEL;
+#endif
 static int power_save = 0;
 static int led_on = 100, led_off = 0; /* defaults to LED that is on while in use */
-static int pwc_preferred_compression = 2; /* 0..3 = uncompressed..high */
+static int pwc_preferred_compression = 1; /* 0..3 = uncompressed..high */
 static struct {
        int type;
        char serial_number[30];
@@ -138,7 +145,7 @@ static struct {
 
 static int pwc_video_open(struct inode *inode, struct file *file);
 static int pwc_video_close(struct inode *inode, struct file *file);
-static ssize_t pwc_video_read(struct file *file, char __user * buf,
+static ssize_t pwc_video_read(struct file *file, char __user *buf,
                          size_t count, loff_t *ppos);
 static unsigned int pwc_video_poll(struct file *file, poll_table *wait);
 static int  pwc_video_ioctl(struct inode *inode, struct file *file,
@@ -203,52 +210,44 @@ static struct video_device pwc_template = {
 /* Here we want the physical address of the memory.
  * This is used when initializing the contents of the area.
  */
-static inline unsigned long kvirt_to_pa(unsigned long adr)
-{
-       unsigned long kva, ret;
 
-       kva = (unsigned long) page_address(vmalloc_to_page((void *)adr));
-       kva |= adr & (PAGE_SIZE-1); /* restore the offset */
-       ret = __pa(kva);
-       return ret;
-}
 
-static void * rvmalloc(unsigned long size)
+
+static void *pwc_rvmalloc(unsigned long size)
 {
        void * mem;
        unsigned long adr;
 
-       size=PAGE_ALIGN(size);
        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)
-               {
-                       SetPageReserved(vmalloc_to_page((void *)adr));
-                       adr+=PAGE_SIZE;
-                       size-=PAGE_SIZE;
-               }
-       }
+       if (!mem)
+               return NULL;
+
+       memset(mem, 0, size); /* Clear the ram out, no junk to the user */
+       adr=(unsigned long) mem;
+       while (size > 0)
+        {
+          SetPageReserved(vmalloc_to_page((void *)adr));
+          adr  += PAGE_SIZE;
+          size -= PAGE_SIZE;
+        }
        return mem;
 }
 
-static void rvfree(void * mem, unsigned long size)
+static void pwc_rvfree(void * mem, unsigned long size)
 {
        unsigned long adr;
 
-       if (mem)
-       {
-               adr=(unsigned long) mem;
-               while ((long) size > 0)
-               {
-                       ClearPageReserved(vmalloc_to_page((void *)adr));
-                       adr+=PAGE_SIZE;
-                       size-=PAGE_SIZE;
-               }
-               vfree(mem);
-       }
+       if (!mem)
+               return;
+
+       adr=(unsigned long) mem;
+       while ((long) size > 0)
+        {
+          ClearPageReserved(vmalloc_to_page((void *)adr));
+          adr  += PAGE_SIZE;
+          size -= PAGE_SIZE;
+        }
+       vfree(mem);
 }
 
 
@@ -256,100 +255,83 @@ static void rvfree(void * mem, unsigned long size)
 
 static int pwc_allocate_buffers(struct pwc_device *pdev)
 {
-       int i;
+       int i, err;
        void *kbuf;
 
-       Trace(TRACE_MEMORY, ">> pwc_allocate_buffers(pdev = 0x%p)\n", pdev);
+       PWC_DEBUG_MEMORY(">> pwc_allocate_buffers(pdev = 0x%p)\n", pdev);
 
        if (pdev == NULL)
                return -ENXIO;
 
-#ifdef PWC_MAGIC
-       if (pdev->magic != PWC_MAGIC) {
-               Err("allocate_buffers(): magic failed.\n");
-               return -ENXIO;
-       }
-#endif
-       /* Allocate Isochronous pipe buffers */
+       /* Allocate Isochronuous pipe buffers */
        for (i = 0; i < MAX_ISO_BUFS; i++) {
                if (pdev->sbuf[i].data == NULL) {
-                       kbuf = kmalloc(ISO_BUFFER_SIZE, GFP_KERNEL);
+                       kbuf = kzalloc(ISO_BUFFER_SIZE, GFP_KERNEL);
                        if (kbuf == NULL) {
-                               Err("Failed to allocate iso buffer %d.\n", i);
+                               PWC_ERROR("Failed to allocate iso buffer %d.\n", i);
                                return -ENOMEM;
                        }
-                       Trace(TRACE_MEMORY, "Allocated iso buffer at %p.\n", kbuf);
+                       PWC_DEBUG_MEMORY("Allocated iso buffer at %p.\n", kbuf);
                        pdev->sbuf[i].data = kbuf;
-                       memset(kbuf, 0, ISO_BUFFER_SIZE);
                }
        }
 
        /* Allocate frame buffer structure */
        if (pdev->fbuf == NULL) {
-               kbuf = kmalloc(default_fbufs * sizeof(struct pwc_frame_buf), GFP_KERNEL);
+               kbuf = kzalloc(default_fbufs * sizeof(struct pwc_frame_buf), GFP_KERNEL);
                if (kbuf == NULL) {
-                       Err("Failed to allocate frame buffer structure.\n");
+                       PWC_ERROR("Failed to allocate frame buffer structure.\n");
                        return -ENOMEM;
                }
-               Trace(TRACE_MEMORY, "Allocated frame buffer structure at %p.\n", kbuf);
+               PWC_DEBUG_MEMORY("Allocated frame buffer structure at %p.\n", kbuf);
                pdev->fbuf = kbuf;
-               memset(kbuf, 0, default_fbufs * sizeof(struct pwc_frame_buf));
        }
+
        /* create frame buffers, and make circular ring */
        for (i = 0; i < default_fbufs; i++) {
                if (pdev->fbuf[i].data == NULL) {
                        kbuf = vmalloc(PWC_FRAME_SIZE); /* need vmalloc since frame buffer > 128K */
                        if (kbuf == NULL) {
-                               Err("Failed to allocate frame buffer %d.\n", i);
+                               PWC_ERROR("Failed to allocate frame buffer %d.\n", i);
                                return -ENOMEM;
                        }
-                       Trace(TRACE_MEMORY, "Allocated frame buffer %d at %p.\n", i, kbuf);
+                       PWC_DEBUG_MEMORY("Allocated frame buffer %d at %p.\n", i, kbuf);
                        pdev->fbuf[i].data = kbuf;
-                       memset(kbuf, 128, PWC_FRAME_SIZE);
+                       memset(kbuf, 0, PWC_FRAME_SIZE);
                }
        }
 
        /* Allocate decompressor table space */
-       kbuf = NULL;
-       switch (pdev->type)
-        {
-         case 675:
-         case 680:
-         case 690:
-         case 720:
-         case 730:
-         case 740:
-         case 750:
-#if 0
-           Trace(TRACE_MEMORY,"private_data(%zu)\n",sizeof(struct pwc_dec23_private));
-           kbuf = kmalloc(sizeof(struct pwc_dec23_private), GFP_KERNEL);       /* Timon & Kiara */
-           break;
-         case 645:
-         case 646:
-           /* TODO & FIXME */
-           kbuf = kmalloc(sizeof(struct pwc_dec23_private), GFP_KERNEL);
-           break;
-#endif
-       ;
-        }
-       pdev->decompress_data = kbuf;
+       if (DEVICE_USE_CODEC1(pdev->type))
+               err = pwc_dec1_alloc(pdev);
+       else
+               err = pwc_dec23_alloc(pdev);
+
+       if (err) {
+               PWC_ERROR("Failed to allocate decompress table.\n");
+               return err;
+       }
 
        /* Allocate image buffer; double buffer for mmap() */
-       kbuf = rvmalloc(default_mbufs * pdev->len_per_image);
+       kbuf = pwc_rvmalloc(pwc_mbufs * pdev->len_per_image);
        if (kbuf == NULL) {
-               Err("Failed to allocate image buffer(s). needed (%d)\n",default_mbufs * pdev->len_per_image);
+               PWC_ERROR("Failed to allocate image buffer(s). needed (%d)\n",
+                               pwc_mbufs * pdev->len_per_image);
                return -ENOMEM;
        }
-       Trace(TRACE_MEMORY, "Allocated image buffer at %p.\n", kbuf);
+       PWC_DEBUG_MEMORY("Allocated image buffer at %p.\n", kbuf);
        pdev->image_data = kbuf;
-       for (i = 0; i < default_mbufs; i++)
-               pdev->image_ptr[i] = kbuf + i * pdev->len_per_image;
-       for (; i < MAX_IMAGES; i++)
-               pdev->image_ptr[i] = NULL;
+       for (i = 0; i < pwc_mbufs; i++) {
+               pdev->images[i].offset = i * pdev->len_per_image;
+               pdev->images[i].vma_use_count = 0;
+       }
+       for (; i < MAX_IMAGES; i++) {
+               pdev->images[i].offset = 0;
+       }
 
        kbuf = NULL;
 
-       Trace(TRACE_MEMORY, "<< pwc_allocate_buffers()\n");
+       PWC_DEBUG_MEMORY("<< pwc_allocate_buffers()\n");
        return 0;
 }
 
@@ -357,21 +339,14 @@ static void pwc_free_buffers(struct pwc_device *pdev)
 {
        int i;
 
-       Trace(TRACE_MEMORY, "Entering free_buffers(%p).\n", pdev);
+       PWC_DEBUG_MEMORY("Entering free_buffers(%p).\n", pdev);
 
        if (pdev == NULL)
                return;
-#ifdef PWC_MAGIC
-       if (pdev->magic != PWC_MAGIC) {
-               Err("free_buffers(): magic failed.\n");
-               return;
-       }
-#endif
-
        /* Release Iso-pipe buffers */
        for (i = 0; i < MAX_ISO_BUFS; i++)
                if (pdev->sbuf[i].data != NULL) {
-                       Trace(TRACE_MEMORY, "Freeing ISO buffer at %p.\n", pdev->sbuf[i].data);
+                       PWC_DEBUG_MEMORY("Freeing ISO buffer at %p.\n", pdev->sbuf[i].data);
                        kfree(pdev->sbuf[i].data);
                        pdev->sbuf[i].data = NULL;
                }
@@ -380,7 +355,7 @@ static void pwc_free_buffers(struct pwc_device *pdev)
        if (pdev->fbuf != NULL) {
                for (i = 0; i < default_fbufs; i++) {
                        if (pdev->fbuf[i].data != NULL) {
-                               Trace(TRACE_MEMORY, "Freeing frame buffer %d at %p.\n", i, pdev->fbuf[i].data);
+                               PWC_DEBUG_MEMORY("Freeing frame buffer %d at %p.\n", i, pdev->fbuf[i].data);
                                vfree(pdev->fbuf[i].data);
                                pdev->fbuf[i].data = NULL;
                        }
@@ -391,20 +366,19 @@ static void pwc_free_buffers(struct pwc_device *pdev)
 
        /* Intermediate decompression buffer & tables */
        if (pdev->decompress_data != NULL) {
-               Trace(TRACE_MEMORY, "Freeing decompression buffer at %p.\n", pdev->decompress_data);
+               PWC_DEBUG_MEMORY("Freeing decompression buffer at %p.\n", pdev->decompress_data);
                kfree(pdev->decompress_data);
                pdev->decompress_data = NULL;
        }
-       pdev->decompressor = NULL;
 
        /* Release image buffers */
        if (pdev->image_data != NULL) {
-               Trace(TRACE_MEMORY, "Freeing image buffer at %p.\n", pdev->image_data);
-               rvfree(pdev->image_data, default_mbufs * pdev->len_per_image);
+               PWC_DEBUG_MEMORY("Freeing image buffer at %p.\n", pdev->image_data);
+               pwc_rvfree(pdev->image_data, pwc_mbufs * pdev->len_per_image);
        }
        pdev->image_data = NULL;
 
-       Trace(TRACE_MEMORY, "Leaving free_buffers().\n");
+       PWC_DEBUG_MEMORY("Leaving free_buffers().\n");
 }
 
 /* The frame & image buffer mess.
@@ -464,7 +438,7 @@ static void pwc_free_buffers(struct pwc_device *pdev)
 /**
   \brief Find next frame buffer to fill. Take from empty or full list, whichever comes first.
  */
-static inline int pwc_next_fill_frame(struct pwc_device *pdev)
+static int pwc_next_fill_frame(struct pwc_device *pdev)
 {
        int ret;
        unsigned long flags;
@@ -489,23 +463,17 @@ static inline int pwc_next_fill_frame(struct pwc_device *pdev)
        }
        else {
                /* Hmm. Take it from the full list */
-#if PWC_DEBUG
                /* sanity check */
                if (pdev->full_frames == NULL) {
-                       Err("Neither empty or full frames available!\n");
+                       PWC_ERROR("Neither empty or full frames available!\n");
                        spin_unlock_irqrestore(&pdev->ptrlock, flags);
                        return -EINVAL;
                }
-#endif
                pdev->fill_frame = pdev->full_frames;
                pdev->full_frames = pdev->full_frames->next;
                ret = 1;
        }
        pdev->fill_frame->next = NULL;
-#if PWC_DEBUG
-       Trace(TRACE_SEQUENCE, "Assigning sequence number %d.\n", pdev->sequence);
-       pdev->fill_frame->sequence = pdev->sequence++;
-#endif
        spin_unlock_irqrestore(&pdev->ptrlock, flags);
        return ret;
 }
@@ -521,6 +489,8 @@ static void pwc_reset_buffers(struct pwc_device *pdev)
        int i;
        unsigned long flags;
 
+       PWC_DEBUG_MEMORY(">> %s __enter__\n", __FUNCTION__);
+
        spin_lock_irqsave(&pdev->ptrlock, flags);
        pdev->full_frames = NULL;
        pdev->full_frames_tail = NULL;
@@ -540,13 +510,15 @@ static void pwc_reset_buffers(struct pwc_device *pdev)
        pdev->image_read_pos = 0;
        pdev->fill_image = 0;
        spin_unlock_irqrestore(&pdev->ptrlock, flags);
+
+       PWC_DEBUG_MEMORY("<< %s __leaving__\n", __FUNCTION__);
 }
 
 
 /**
   \brief Do all the handling for getting one frame: get pointer, decompress, advance pointers.
  */
-static int pwc_handle_frame(struct pwc_device *pdev)
+int pwc_handle_frame(struct pwc_device *pdev)
 {
        int ret = 0;
        unsigned long flags;
@@ -556,41 +528,40 @@ static int pwc_handle_frame(struct pwc_device *pdev)
           we can release the lock after this without problems */
        if (pdev->read_frame != NULL) {
                /* This can't theoretically happen */
-               Err("Huh? Read frame still in use?\n");
+               PWC_ERROR("Huh? Read frame still in use?\n");
+               spin_unlock_irqrestore(&pdev->ptrlock, flags);
+               return ret;
+       }
+
+
+       if (pdev->full_frames == NULL) {
+               PWC_ERROR("Woops. No frames ready.\n");
        }
        else {
-               if (pdev->full_frames == NULL) {
-                       Err("Woops. No frames ready.\n");
+               pdev->read_frame = pdev->full_frames;
+               pdev->full_frames = pdev->full_frames->next;
+               pdev->read_frame->next = NULL;
+       }
+
+       if (pdev->read_frame != NULL) {
+               /* Decompression is a lenghty process, so it's outside of the lock.
+                  This gives the isoc_handler the opportunity to fill more frames
+                  in the mean time.
+               */
+               spin_unlock_irqrestore(&pdev->ptrlock, flags);
+               ret = pwc_decompress(pdev);
+               spin_lock_irqsave(&pdev->ptrlock, flags);
+
+               /* We're done with read_buffer, tack it to the end of the empty buffer list */
+               if (pdev->empty_frames == NULL) {
+                       pdev->empty_frames = pdev->read_frame;
+                       pdev->empty_frames_tail = pdev->empty_frames;
                }
                else {
-                       pdev->read_frame = pdev->full_frames;
-                       pdev->full_frames = pdev->full_frames->next;
-                       pdev->read_frame->next = NULL;
-               }
-
-               if (pdev->read_frame != NULL) {
-#if PWC_DEBUG
-                       Trace(TRACE_SEQUENCE, "Decompressing frame %d\n", pdev->read_frame->sequence);
-#endif
-                       /* Decompression is a lenghty process, so it's outside of the lock.
-                          This gives the isoc_handler the opportunity to fill more frames
-                          in the mean time.
-                       */
-                       spin_unlock_irqrestore(&pdev->ptrlock, flags);
-                       ret = pwc_decompress(pdev);
-                       spin_lock_irqsave(&pdev->ptrlock, flags);
-
-                       /* We're done with read_buffer, tack it to the end of the empty buffer list */
-                       if (pdev->empty_frames == NULL) {
-                               pdev->empty_frames = pdev->read_frame;
-                               pdev->empty_frames_tail = pdev->empty_frames;
-                       }
-                       else {
-                               pdev->empty_frames_tail->next = pdev->read_frame;
-                               pdev->empty_frames_tail = pdev->read_frame;
-                       }
-                       pdev->read_frame = NULL;
+                       pdev->empty_frames_tail->next = pdev->read_frame;
+                       pdev->empty_frames_tail = pdev->read_frame;
                }
+               pdev->read_frame = NULL;
        }
        spin_unlock_irqrestore(&pdev->ptrlock, flags);
        return ret;
@@ -599,12 +570,114 @@ static int pwc_handle_frame(struct pwc_device *pdev)
 /**
   \brief Advance pointers of image buffer (after each user request)
 */
-static inline void pwc_next_image(struct pwc_device *pdev)
+void pwc_next_image(struct pwc_device *pdev)
 {
        pdev->image_used[pdev->fill_image] = 0;
-       pdev->fill_image = (pdev->fill_image + 1) % default_mbufs;
+       pdev->fill_image = (pdev->fill_image + 1) % pwc_mbufs;
 }
 
+/**
+ * Print debug information when a frame is discarded because all of our buffer
+ * is full
+ */
+static void pwc_frame_dumped(struct pwc_device *pdev)
+{
+       pdev->vframes_dumped++;
+       if (pdev->vframe_count < FRAME_LOWMARK)
+               return;
+
+       if (pdev->vframes_dumped < 20)
+               PWC_DEBUG_FLOW("Dumping frame %d\n", pdev->vframe_count);
+       else if (pdev->vframes_dumped == 20)
+               PWC_DEBUG_FLOW("Dumping frame %d (last message)\n",
+                               pdev->vframe_count);
+}
+
+static int pwc_rcv_short_packet(struct pwc_device *pdev, const struct pwc_frame_buf *fbuf)
+{
+       int awake = 0;
+
+       /* The ToUCam Fun CMOS sensor causes the firmware to send 2 or 3 bogus
+          frames on the USB wire after an exposure change. This conditition is
+          however detected  in the cam and a bit is set in the header.
+          */
+       if (pdev->type == 730) {
+               unsigned char *ptr = (unsigned char *)fbuf->data;
+
+               if (ptr[1] == 1 && ptr[0] & 0x10) {
+                       PWC_TRACE("Hyundai CMOS sensor bug. Dropping frame.\n");
+                       pdev->drop_frames += 2;
+                       pdev->vframes_error++;
+               }
+               if ((ptr[0] ^ pdev->vmirror) & 0x01) {
+                       if (ptr[0] & 0x01) {
+                               pdev->snapshot_button_status = 1;
+                               PWC_TRACE("Snapshot button pressed.\n");
+                       }
+                       else {
+                               PWC_TRACE("Snapshot button released.\n");
+                       }
+               }
+               if ((ptr[0] ^ pdev->vmirror) & 0x02) {
+                       if (ptr[0] & 0x02)
+                               PWC_TRACE("Image is mirrored.\n");
+                       else
+                               PWC_TRACE("Image is normal.\n");
+               }
+               pdev->vmirror = ptr[0] & 0x03;
+               /* Sometimes the trailer of the 730 is still sent as a 4 byte packet
+                  after a short frame; this condition is filtered out specifically. A 4 byte
+                  frame doesn't make sense anyway.
+                  So we get either this sequence:
+                  drop_bit set -> 4 byte frame -> short frame -> good frame
+                  Or this one:
+                  drop_bit set -> short frame -> good frame
+                  So we drop either 3 or 2 frames in all!
+                  */
+               if (fbuf->filled == 4)
+                       pdev->drop_frames++;
+       }
+       else if (pdev->type == 740 || pdev->type == 720) {
+               unsigned char *ptr = (unsigned char *)fbuf->data;
+               if ((ptr[0] ^ pdev->vmirror) & 0x01) {
+                       if (ptr[0] & 0x01) {
+                               pdev->snapshot_button_status = 1;
+                               PWC_TRACE("Snapshot button pressed.\n");
+                       }
+                       else
+                               PWC_TRACE("Snapshot button released.\n");
+               }
+               pdev->vmirror = ptr[0] & 0x03;
+       }
+
+       /* In case we were instructed to drop the frame, do so silently.
+          The buffer pointers are not updated either (but the counters are reset below).
+          */
+       if (pdev->drop_frames > 0)
+               pdev->drop_frames--;
+       else {
+               /* Check for underflow first */
+               if (fbuf->filled < pdev->frame_total_size) {
+                       PWC_DEBUG_FLOW("Frame buffer underflow (%d bytes);"
+                                      " discarded.\n", fbuf->filled);
+                       pdev->vframes_error++;
+               }
+               else {
+                       /* Send only once per EOF */
+                       awake = 1; /* delay wake_ups */
+
+                       /* Find our next frame to fill. This will always succeed, since we
+                        * nick a frame from either empty or full list, but if we had to
+                        * take it from the full list, it means a frame got dropped.
+                        */
+                       if (pwc_next_fill_frame(pdev))
+                               pwc_frame_dumped(pdev);
+
+               }
+       } /* !drop_frames */
+       pdev->vframe_count++;
+       return awake;
+}
 
 /* This gets called for the Isochronous pipe (video). This is done in
  * interrupt time, so it has to be fast, not crash, and not stall. Neat.
@@ -620,17 +693,12 @@ static void pwc_isoc_handler(struct urb *urb, struct pt_regs *regs)
        awake = 0;
        pdev = (struct pwc_device *)urb->context;
        if (pdev == NULL) {
-               Err("isoc_handler() called with NULL device?!\n");
-               return;
-       }
-#ifdef PWC_MAGIC
-       if (pdev->magic != PWC_MAGIC) {
-               Err("isoc_handler() called with bad magic!\n");
+               PWC_ERROR("isoc_handler() called with NULL device?!\n");
                return;
        }
-#endif
+
        if (urb->status == -ENOENT || urb->status == -ECONNRESET) {
-               Trace(TRACE_OPEN, "pwc_isoc_handler(): URB (%p) unlinked %ssynchronuously.\n", urb, urb->status == -ENOENT ? "" : "a");
+               PWC_DEBUG_OPEN("URB (%p) unlinked %ssynchronuously.\n", urb, urb->status == -ENOENT ? "" : "a");
                return;
        }
        if (urb->status != -EINPROGRESS && urb->status != 0) {
@@ -645,13 +713,13 @@ static void pwc_isoc_handler(struct urb *urb, struct pt_regs *regs)
                        case -EILSEQ:           errmsg = "CRC/Timeout (could be anything)"; break;
                        case -ETIMEDOUT:        errmsg = "NAK (device does not respond)"; break;
                }
-               Trace(TRACE_FLOW, "pwc_isoc_handler() called with status %d [%s].\n", urb->status, errmsg);
+               PWC_DEBUG_FLOW("pwc_isoc_handler() called with status %d [%s].\n", urb->status, errmsg);
                /* Give up after a number of contiguous errors on the USB bus.
                   Appearantly something is wrong so we simulate an unplug event.
                 */
                if (++pdev->visoc_errors > MAX_ISOC_ERRORS)
                {
-                       Info("Too many ISOC errors, bailing out.\n");
+                       PWC_INFO("Too many ISOC errors, bailing out.\n");
                        pdev->error_status = EIO;
                        awake = 1;
                        wake_up_interruptible(&pdev->frameq);
@@ -661,7 +729,7 @@ static void pwc_isoc_handler(struct urb *urb, struct pt_regs *regs)
 
        fbuf = pdev->fill_frame;
        if (fbuf == NULL) {
-               Err("pwc_isoc_handler without valid fill frame.\n");
+               PWC_ERROR("pwc_isoc_handler without valid fill frame.\n");
                awake = 1;
                goto handler_end;
        }
@@ -688,7 +756,7 @@ static void pwc_isoc_handler(struct urb *urb, struct pt_regs *regs)
 
                                        /* ...copy data to frame buffer, if possible */
                                        if (flen + fbuf->filled > pdev->frame_total_size) {
-                                               Trace(TRACE_FLOW, "Frame buffer overflow (flen = %d, frame_total_size = %d).\n", flen, pdev->frame_total_size);
+                                               PWC_DEBUG_FLOW("Frame buffer overflow (flen = %d, frame_total_size = %d).\n", flen, pdev->frame_total_size);
                                                pdev->vsync = 0; /* Hmm, let's wait for an EOF (end-of-frame) */
                                                pdev->vframes_error++;
                                        }
@@ -704,96 +772,28 @@ static void pwc_isoc_handler(struct urb *urb, struct pt_regs *regs)
                                /* Shorter packet... We probably have the end of an image-frame;
                                   wake up read() process and let select()/poll() do something.
                                   Decompression is done in user time over there.
-                                */
+                                  */
                                if (pdev->vsync == 2) {
-                                       /* The ToUCam Fun CMOS sensor causes the firmware to send 2 or 3 bogus
-                                          frames on the USB wire after an exposure change. This conditition is
-                                          however detected  in the cam and a bit is set in the header.
-                                        */
-                                       if (pdev->type == 730) {
-                                               unsigned char *ptr = (unsigned char *)fbuf->data;
-
-                                               if (ptr[1] == 1 && ptr[0] & 0x10) {
-#if PWC_DEBUG
-                                                       Debug("Hyundai CMOS sensor bug. Dropping frame %d.\n", fbuf->sequence);
-#endif
-                                                       pdev->drop_frames += 2;
-                                                       pdev->vframes_error++;
-                                               }
-                                               if ((ptr[0] ^ pdev->vmirror) & 0x01) {
-                                                       if (ptr[0] & 0x01)
-                                                               Info("Snapshot button pressed.\n");
-                                                       else
-                                                               Info("Snapshot button released.\n");
-                                               }
-                                               if ((ptr[0] ^ pdev->vmirror) & 0x02) {
-                                                       if (ptr[0] & 0x02)
-                                                               Info("Image is mirrored.\n");
-                                                       else
-                                                               Info("Image is normal.\n");
-                                               }
-                                               pdev->vmirror = ptr[0] & 0x03;
-                                               /* Sometimes the trailer of the 730 is still sent as a 4 byte packet
-                                                  after a short frame; this condition is filtered out specifically. A 4 byte
-                                                  frame doesn't make sense anyway.
-                                                  So we get either this sequence:
-                                                       drop_bit set -> 4 byte frame -> short frame -> good frame
-                                                  Or this one:
-                                                       drop_bit set -> short frame -> good frame
-                                                  So we drop either 3 or 2 frames in all!
-                                                */
-                                               if (fbuf->filled == 4)
-                                                       pdev->drop_frames++;
+                                       if (pwc_rcv_short_packet(pdev, fbuf)) {
+                                               awake = 1;
+                                               fbuf = pdev->fill_frame;
                                        }
-
-                                       /* In case we were instructed to drop the frame, do so silently.
-                                          The buffer pointers are not updated either (but the counters are reset below).
-                                        */
-                                       if (pdev->drop_frames > 0)
-                                               pdev->drop_frames--;
-                                       else {
-                                               /* Check for underflow first */
-                                               if (fbuf->filled < pdev->frame_total_size) {
-                                                       Trace(TRACE_FLOW, "Frame buffer underflow (%d bytes); discarded.\n", fbuf->filled);
-                                                       pdev->vframes_error++;
-                                               }
-                                               else {
-                                                       /* Send only once per EOF */
-                                                       awake = 1; /* delay wake_ups */
-
-                                                       /* Find our next frame to fill. This will always succeed, since we
-                                                        * nick a frame from either empty or full list, but if we had to
-                                                        * take it from the full list, it means a frame got dropped.
-                                                        */
-                                                       if (pwc_next_fill_frame(pdev)) {
-                                                               pdev->vframes_dumped++;
-                                                               if ((pdev->vframe_count > FRAME_LOWMARK) && (pwc_trace & TRACE_FLOW)) {
-                                                                       if (pdev->vframes_dumped < 20)
-                                                                               Trace(TRACE_FLOW, "Dumping frame %d.\n", pdev->vframe_count);
-                                                                       if (pdev->vframes_dumped == 20)
-                                                                               Trace(TRACE_FLOW, "Dumping frame %d (last message).\n", pdev->vframe_count);
-                                                               }
-                                                       }
-                                                       fbuf = pdev->fill_frame;
-                                               }
-                                       } /* !drop_frames */
-                                       pdev->vframe_count++;
                                }
                                fbuf->filled = 0;
                                fillptr = fbuf->data;
                                pdev->vsync = 1;
-                       } /* .. flen < last_packet_size */
+                       }
+
                        pdev->vlast_packet_size = flen;
                } /* ..status == 0 */
-#if PWC_DEBUG
-               /* This is normally not interesting to the user, unless you are really debugging something */
                else {
+                       /* This is normally not interesting to the user, unless
+                        * you are really debugging something */
                        static int iso_error = 0;
                        iso_error++;
                        if (iso_error < 20)
-                               Trace(TRACE_FLOW, "Iso frame %d of USB has error %d\n", i, fst);
+                               PWC_DEBUG_FLOW("Iso frame %d of USB has error %d\n", i, fst);
                }
-#endif
        }
 
 handler_end:
@@ -803,11 +803,11 @@ handler_end:
        urb->dev = pdev->udev;
        i = usb_submit_urb(urb, GFP_ATOMIC);
        if (i != 0)
-               Err("Error (%d) re-submitting urb in pwc_isoc_handler.\n", i);
+               PWC_ERROR("Error (%d) re-submitting urb in pwc_isoc_handler.\n", i);
 }
 
 
-static int pwc_isoc_init(struct pwc_device *pdev)
+int pwc_isoc_init(struct pwc_device *pdev)
 {
        struct usb_device *udev;
        struct urb *urb;
@@ -826,7 +826,6 @@ static int pwc_isoc_init(struct pwc_device *pdev)
        /* Get the current alternate interface, adjust packet size */
        if (!udev->actconfig)
                return -EFAULT;
-
        intf = usb_ifnum_to_if(udev, 0);
        if (intf)
                idesc = usb_altnum_to_altsetting(intf, pdev->valternate);
@@ -836,20 +835,21 @@ static int pwc_isoc_init(struct pwc_device *pdev)
 
        /* Search video endpoint */
        pdev->vmax_packet_size = -1;
-       for (i = 0; i < idesc->desc.bNumEndpoints; i++)
+       for (i = 0; i < idesc->desc.bNumEndpoints; i++) {
                if ((idesc->endpoint[i].desc.bEndpointAddress & 0xF) == pdev->vendpoint) {
                        pdev->vmax_packet_size = le16_to_cpu(idesc->endpoint[i].desc.wMaxPacketSize);
                        break;
                }
+       }
 
        if (pdev->vmax_packet_size < 0 || pdev->vmax_packet_size > ISO_MAX_FRAME_SIZE) {
-               Err("Failed to find packet size for video endpoint in current alternate setting.\n");
+               PWC_ERROR("Failed to find packet size for video endpoint in current alternate setting.\n");
                return -ENFILE; /* Odd error, that should be noticeable */
        }
 
        /* Set alternate interface */
        ret = 0;
-       Trace(TRACE_OPEN, "Setting alternate interface %d\n", pdev->valternate);
+       PWC_DEBUG_OPEN("Setting alternate interface %d\n", pdev->valternate);
        ret = usb_set_interface(pdev->udev, 0, pdev->valternate);
        if (ret < 0)
                return ret;
@@ -857,12 +857,12 @@ static int pwc_isoc_init(struct pwc_device *pdev)
        for (i = 0; i < MAX_ISO_BUFS; i++) {
                urb = usb_alloc_urb(ISO_FRAMES_PER_DESC, GFP_KERNEL);
                if (urb == NULL) {
-                       Err("Failed to allocate urb %d\n", i);
+                       PWC_ERROR("Failed to allocate urb %d\n", i);
                        ret = -ENOMEM;
                        break;
                }
                pdev->sbuf[i].urb = urb;
-               Trace(TRACE_MEMORY, "Allocated URB at 0x%p\n", urb);
+               PWC_DEBUG_MEMORY("Allocated URB at 0x%p\n", urb);
        }
        if (ret) {
                /* De-allocate in reverse order */
@@ -899,24 +899,26 @@ static int pwc_isoc_init(struct pwc_device *pdev)
        for (i = 0; i < MAX_ISO_BUFS; i++) {
                ret = usb_submit_urb(pdev->sbuf[i].urb, GFP_KERNEL);
                if (ret)
-                       Err("isoc_init() submit_urb %d failed with error %d\n", i, ret);
+                       PWC_ERROR("isoc_init() submit_urb %d failed with error %d\n", i, ret);
                else
-                       Trace(TRACE_MEMORY, "URB 0x%p submitted.\n", pdev->sbuf[i].urb);
+                       PWC_DEBUG_MEMORY("URB 0x%p submitted.\n", pdev->sbuf[i].urb);
        }
 
        /* All is done... */
        pdev->iso_init = 1;
-       Trace(TRACE_OPEN, "<< pwc_isoc_init()\n");
+       PWC_DEBUG_OPEN("<< pwc_isoc_init()\n");
        return 0;
 }
 
-static void pwc_isoc_cleanup(struct pwc_device *pdev)
+void pwc_isoc_cleanup(struct pwc_device *pdev)
 {
        int i;
 
-       Trace(TRACE_OPEN, ">> pwc_isoc_cleanup()\n");
+       PWC_DEBUG_OPEN(">> pwc_isoc_cleanup()\n");
        if (pdev == NULL)
                return;
+       if (pdev->iso_init == 0)
+               return;
 
        /* Unlinking ISOC buffers one by one */
        for (i = 0; i < MAX_ISO_BUFS; i++) {
@@ -925,10 +927,10 @@ static void pwc_isoc_cleanup(struct pwc_device *pdev)
                urb = pdev->sbuf[i].urb;
                if (urb != 0) {
                        if (pdev->iso_init) {
-                               Trace(TRACE_MEMORY, "Unlinking URB %p\n", urb);
+                               PWC_DEBUG_MEMORY("Unlinking URB %p\n", urb);
                                usb_kill_urb(urb);
                        }
-                       Trace(TRACE_MEMORY, "Freeing URB\n");
+                       PWC_DEBUG_MEMORY("Freeing URB\n");
                        usb_free_urb(urb);
                        pdev->sbuf[i].urb = NULL;
                }
@@ -938,12 +940,12 @@ static void pwc_isoc_cleanup(struct pwc_device *pdev)
           is signalled by EPIPE)
         */
        if (pdev->error_status && pdev->error_status != EPIPE) {
-               Trace(TRACE_OPEN, "Setting alternate interface 0.\n");
+               PWC_DEBUG_OPEN("Setting alternate interface 0.\n");
                usb_set_interface(pdev->udev, 0, 0);
        }
 
        pdev->iso_init = 0;
-       Trace(TRACE_OPEN, "<< pwc_isoc_cleanup()\n");
+       PWC_DEBUG_OPEN("<< pwc_isoc_cleanup()\n");
 }
 
 int pwc_try_video_mode(struct pwc_device *pdev, int width, int height, int new_fps, int new_compression, int new_snapshot)
@@ -957,18 +959,18 @@ int pwc_try_video_mode(struct pwc_device *pdev, int width, int height, int new_f
        /* Try to set video mode... */
        start = ret = pwc_set_video_mode(pdev, width, height, new_fps, new_compression, new_snapshot);
        if (ret) {
-               Trace(TRACE_FLOW, "pwc_set_video_mode attempt 1 failed.\n");
+               PWC_DEBUG_FLOW("pwc_set_video_mode attempt 1 failed.\n");
                /* That failed... restore old mode (we know that worked) */
                start = pwc_set_video_mode(pdev, pdev->view.x, pdev->view.y, pdev->vframes, pdev->vcompression, pdev->vsnapshot);
                if (start) {
-                       Trace(TRACE_FLOW, "pwc_set_video_mode attempt 2 failed.\n");
+                       PWC_DEBUG_FLOW("pwc_set_video_mode attempt 2 failed.\n");
                }
        }
        if (start == 0)
        {
                if (pwc_isoc_init(pdev) < 0)
                {
-                       Info("Failed to restart ISOC transfers in pwc_try_video_mode.\n");
+                       PWC_WARNING("Failed to restart ISOC transfers in pwc_try_video_mode.\n");
                        ret = -EAGAIN; /* let's try again, who knows if it works a second time */
                }
        }
@@ -976,54 +978,129 @@ int pwc_try_video_mode(struct pwc_device *pdev, int width, int height, int new_f
        return ret; /* Return original error code */
 }
 
+/*********
+ * sysfs
+ *********/
+static struct pwc_device *cd_to_pwc(struct class_device *cd)
+{
+       struct video_device *vdev = to_video_device(cd);
+       return video_get_drvdata(vdev);
+}
+
+static ssize_t show_pan_tilt(struct class_device *class_dev, char *buf)
+{
+       struct pwc_device *pdev = cd_to_pwc(class_dev);
+       return sprintf(buf, "%d %d\n", pdev->pan_angle, pdev->tilt_angle);
+}
+
+static ssize_t store_pan_tilt(struct class_device *class_dev, const char *buf,
+                        size_t count)
+{
+       struct pwc_device *pdev = cd_to_pwc(class_dev);
+       int pan, tilt;
+       int ret = -EINVAL;
+
+       if (strncmp(buf, "reset", 5) == 0)
+               ret = pwc_mpt_reset(pdev, 0x3);
+
+       else if (sscanf(buf, "%d %d", &pan, &tilt) > 0)
+               ret = pwc_mpt_set_angle(pdev, pan, tilt);
+
+       if (ret < 0)
+               return ret;
+       return strlen(buf);
+}
+static CLASS_DEVICE_ATTR(pan_tilt, S_IRUGO | S_IWUSR, show_pan_tilt,
+                        store_pan_tilt);
+
+static ssize_t show_snapshot_button_status(struct class_device *class_dev, char *buf)
+{
+       struct pwc_device *pdev = cd_to_pwc(class_dev);
+       int status = pdev->snapshot_button_status;
+       pdev->snapshot_button_status = 0;
+       return sprintf(buf, "%d\n", status);
+}
+
+static CLASS_DEVICE_ATTR(button, S_IRUGO | S_IWUSR, show_snapshot_button_status,
+                        NULL);
+
+static void pwc_create_sysfs_files(struct video_device *vdev)
+{
+       struct pwc_device *pdev = video_get_drvdata(vdev);
+       if (pdev->features & FEATURE_MOTOR_PANTILT)
+               video_device_create_file(vdev, &class_device_attr_pan_tilt);
+       video_device_create_file(vdev, &class_device_attr_button);
+}
+
+static void pwc_remove_sysfs_files(struct video_device *vdev)
+{
+       struct pwc_device *pdev = video_get_drvdata(vdev);
+       if (pdev->features & FEATURE_MOTOR_PANTILT)
+               video_device_remove_file(vdev, &class_device_attr_pan_tilt);
+       video_device_remove_file(vdev, &class_device_attr_button);
+}
+
+#if CONFIG_PWC_DEBUG
+static const char *pwc_sensor_type_to_string(unsigned int sensor_type)
+{
+       switch(sensor_type) {
+               case 0x00:
+                       return "Hyundai CMOS sensor";
+               case 0x20:
+                       return "Sony CCD sensor + TDA8787";
+               case 0x2E:
+                       return "Sony CCD sensor + Exas 98L59";
+               case 0x2F:
+                       return "Sony CCD sensor + ADI 9804";
+               case 0x30:
+                       return "Sharp CCD sensor + TDA8787";
+               case 0x3E:
+                       return "Sharp CCD sensor + Exas 98L59";
+               case 0x3F:
+                       return "Sharp CCD sensor + ADI 9804";
+               case 0x40:
+                       return "UPA 1021 sensor";
+               case 0x100:
+                       return "VGA sensor";
+               case 0x101:
+                       return "PAL MR sensor";
+               default:
+                       return "unknown type of sensor";
+       }
+}
+#endif
 
 /***************************************************************************/
 /* Video4Linux functions */
 
 static int pwc_video_open(struct inode *inode, struct file *file)
 {
-       int i;
+       int i, ret;
        struct video_device *vdev = video_devdata(file);
        struct pwc_device *pdev;
 
-       Trace(TRACE_OPEN, ">> video_open called(vdev = 0x%p).\n", vdev);
+       PWC_DEBUG_OPEN(">> video_open called(vdev = 0x%p).\n", vdev);
 
        pdev = (struct pwc_device *)vdev->priv;
        if (pdev == NULL)
                BUG();
-       if (pdev->vopen)
+       if (pdev->vopen) {
+               PWC_DEBUG_OPEN("I'm busy, someone is using the device.\n");
                return -EBUSY;
+       }
 
        down(&pdev->modlock);
        if (!pdev->usb_init) {
-               Trace(TRACE_OPEN, "Doing first time initialization.\n");
+               PWC_DEBUG_OPEN("Doing first time initialization.\n");
                pdev->usb_init = 1;
 
-               if (pwc_trace & TRACE_OPEN)
+               /* Query sensor type */
+               ret = pwc_get_cmos_sensor(pdev, &i);
+               if (ret >= 0)
                {
-                       /* Query sensor type */
-                       const char *sensor_type = NULL;
-                       int ret;
-
-                       ret = pwc_get_cmos_sensor(pdev, &i);
-                       if (ret >= 0)
-                       {
-                               switch(i) {
-                               case 0x00:  sensor_type = "Hyundai CMOS sensor"; break;
-                               case 0x20:  sensor_type = "Sony CCD sensor + TDA8787"; break;
-                               case 0x2E:  sensor_type = "Sony CCD sensor + Exas 98L59"; break;
-                               case 0x2F:  sensor_type = "Sony CCD sensor + ADI 9804"; break;
-                               case 0x30:  sensor_type = "Sharp CCD sensor + TDA8787"; break;
-                               case 0x3E:  sensor_type = "Sharp CCD sensor + Exas 98L59"; break;
-                               case 0x3F:  sensor_type = "Sharp CCD sensor + ADI 9804"; break;
-                               case 0x40:  sensor_type = "UPA 1021 sensor"; break;
-                               case 0x100: sensor_type = "VGA sensor"; break;
-                               case 0x101: sensor_type = "PAL MR sensor"; break;
-                               default:    sensor_type = "unknown type of sensor"; break;
-                               }
-                       }
-                       if (sensor_type != NULL)
-                               Info("This %s camera is equipped with a %s (%d).\n", pdev->vdev->name, sensor_type, i);
+                       PWC_DEBUG_OPEN("This %s camera is equipped with a %s (%d).\n",
+                                       pdev->vdev->name,
+                                       pwc_sensor_type_to_string(i), i);
                }
        }
 
@@ -1031,34 +1108,32 @@ static int pwc_video_open(struct inode *inode, struct file *file)
        if (power_save) {
                i = pwc_camera_power(pdev, 1);
                if (i < 0)
-                       Info("Failed to restore power to the camera! (%d)\n", i);
+                       PWC_DEBUG_OPEN("Failed to restore power to the camera! (%d)\n", i);
        }
        /* Set LED on/off time */
        if (pwc_set_leds(pdev, led_on, led_off) < 0)
-               Info("Failed to set LED on/off time.\n");
+               PWC_DEBUG_OPEN("Failed to set LED on/off time.\n");
 
        pwc_construct(pdev); /* set min/max sizes correct */
 
        /* So far, so good. Allocate memory. */
        i = pwc_allocate_buffers(pdev);
        if (i < 0) {
-               Trace(TRACE_OPEN, "Failed to allocate buffer memory.\n");
+               PWC_DEBUG_OPEN("Failed to allocate buffers memory.\n");
+               pwc_free_buffers(pdev);
                up(&pdev->modlock);
                return i;
        }
 
        /* Reset buffers & parameters */
        pwc_reset_buffers(pdev);
-       for (i = 0; i < default_mbufs; i++)
+       for (i = 0; i < pwc_mbufs; i++)
                pdev->image_used[i] = 0;
        pdev->vframe_count = 0;
        pdev->vframes_dumped = 0;
        pdev->vframes_error = 0;
        pdev->visoc_errors = 0;
        pdev->error_status = 0;
-#if PWC_DEBUG
-       pdev->sequence = 0;
-#endif
        pwc_construct(pdev); /* set min/max sizes correct */
 
        /* Set some defaults */
@@ -1070,29 +1145,44 @@ static int pwc_video_open(struct inode *inode, struct file *file)
         */
        i = pwc_set_video_mode(pdev, pwc_image_sizes[pdev->vsize].x, pwc_image_sizes[pdev->vsize].y, pdev->vframes, pdev->vcompression, 0);
        if (i)  {
-               Trace(TRACE_OPEN, "First attempt at set_video_mode failed.\n");
-               if (pdev->type == 730 || pdev->type == 740 || pdev->type == 750)
-                       i = pwc_set_video_mode(pdev, pwc_image_sizes[PSZ_QSIF].x, pwc_image_sizes[PSZ_QSIF].y, 10, pdev->vcompression, 0);
+               unsigned int default_resolution;
+               PWC_DEBUG_OPEN("First attempt at set_video_mode failed.\n");
+               if (pdev->type>= 730)
+                       default_resolution = PSZ_QSIF;
                else
-                       i = pwc_set_video_mode(pdev, pwc_image_sizes[PSZ_QCIF].x, pwc_image_sizes[PSZ_QCIF].y, 10, pdev->vcompression, 0);
+                       default_resolution = PSZ_QCIF;
+
+               i = pwc_set_video_mode(pdev,
+                                      pwc_image_sizes[default_resolution].x,
+                                      pwc_image_sizes[default_resolution].y,
+                                      10,
+                                      pdev->vcompression,
+                                      0);
        }
        if (i) {
-               Trace(TRACE_OPEN, "Second attempt at set_video_mode failed.\n");
+               PWC_DEBUG_OPEN("Second attempt at set_video_mode failed.\n");
+               pwc_free_buffers(pdev);
                up(&pdev->modlock);
                return i;
        }
 
        i = pwc_isoc_init(pdev);
        if (i) {
-               Trace(TRACE_OPEN, "Failed to init ISOC stuff = %d.\n", i);
+               PWC_DEBUG_OPEN("Failed to init ISOC stuff = %d.\n", i);
+               pwc_isoc_cleanup(pdev);
+               pwc_free_buffers(pdev);
                up(&pdev->modlock);
                return i;
        }
 
+       /* Initialize the webcam to sane value */
+       pwc_set_brightness(pdev, 0x7fff);
+       pwc_set_agc(pdev, 1, 0);
+
        pdev->vopen++;
        file->private_data = vdev;
        up(&pdev->modlock);
-       Trace(TRACE_OPEN, "<< video_open() returns 0.\n");
+       PWC_DEBUG_OPEN("<< video_open() returns 0.\n");
        return 0;
 }
 
@@ -1103,35 +1193,23 @@ static int pwc_video_close(struct inode *inode, struct file *file)
        struct pwc_device *pdev;
        int i;
 
-       Trace(TRACE_OPEN, ">> video_close called(vdev = 0x%p).\n", vdev);
+       PWC_DEBUG_OPEN(">> video_close called(vdev = 0x%p).\n", vdev);
 
        pdev = (struct pwc_device *)vdev->priv;
        if (pdev->vopen == 0)
-               Info("video_close() called on closed device?\n");
+               PWC_DEBUG_MODULE("video_close() called on closed device?\n");
 
        /* Dump statistics, but only if a reasonable amount of frames were
           processed (to prevent endless log-entries in case of snap-shot
           programs)
         */
        if (pdev->vframe_count > 20)
-               Info("Closing video device: %d frames received, dumped %d frames, %d frames with errors.\n", pdev->vframe_count, pdev->vframes_dumped, pdev->vframes_error);
+               PWC_DEBUG_MODULE("Closing video device: %d frames received, dumped %d frames, %d frames with errors.\n", pdev->vframe_count, pdev->vframes_dumped, pdev->vframes_error);
 
-       switch (pdev->type)
-        {
-         case 675:
-         case 680:
-         case 690:
-         case 720:
-         case 730:
-         case 740:
-         case 750:
-/*         pwc_dec23_exit();   *//* Timon & Kiara */
-           break;
-         case 645:
-         case 646:
-/*         pwc_dec1_exit(); */
-           break;
-        }
+       if (DEVICE_USE_CODEC1(pdev->type))
+           pwc_dec1_exit();
+       else
+           pwc_dec23_exit();
 
        pwc_isoc_cleanup(pdev);
        pwc_free_buffers(pdev);
@@ -1140,15 +1218,15 @@ static int pwc_video_close(struct inode *inode, struct file *file)
        if (pdev->error_status != EPIPE) {
                /* Turn LEDs off */
                if (pwc_set_leds(pdev, 0, 0) < 0)
-                       Info("Failed to set LED on/off time.\n");
+                       PWC_DEBUG_MODULE("Failed to set LED on/off time.\n");
                if (power_save) {
                        i = pwc_camera_power(pdev, 0);
                        if (i < 0)
-                               Err("Failed to power down camera (%d)\n", i);
+                               PWC_ERROR("Failed to power down camera (%d)\n", i);
                }
        }
-       pdev->vopen = 0;
-       Trace(TRACE_OPEN, "<< video_close()\n");
+       pdev->vopen--;
+       PWC_DEBUG_OPEN("<< video_close() vopen=%d\n", pdev->vopen);
        return 0;
 }
 
@@ -1164,7 +1242,7 @@ static int pwc_video_close(struct inode *inode, struct file *file)
                device is tricky anyhow.
  */
 
-static ssize_t pwc_video_read(struct file *file, char __user * buf,
+static ssize_t pwc_video_read(struct file *file, char __user *buf,
                          size_t count, loff_t *ppos)
 {
        struct video_device *vdev = file->private_data;
@@ -1172,8 +1250,10 @@ static ssize_t pwc_video_read(struct file *file, char __user * buf,
        int noblock = file->f_flags & O_NONBLOCK;
        DECLARE_WAITQUEUE(wait, current);
        int bytes_to_read;
+       void *image_buffer_addr;
 
-       Trace(TRACE_READ, "video_read(0x%p, %p, %zu) called.\n", vdev, buf, count);
+       PWC_DEBUG_READ("pwc_video_read(vdev=0x%p, buf=%p, count=%zd) called.\n",
+                       vdev, buf, count);
        if (vdev == NULL)
                return -EFAULT;
        pdev = vdev->priv;
@@ -1214,16 +1294,19 @@ static ssize_t pwc_video_read(struct file *file, char __user * buf,
                        return -EFAULT;
        }
 
-       Trace(TRACE_READ, "Copying data to user space.\n");
+       PWC_DEBUG_READ("Copying data to user space.\n");
        if (pdev->vpalette == VIDEO_PALETTE_RAW)
-               bytes_to_read = pdev->frame_size;
+               bytes_to_read = pdev->frame_size + sizeof(struct pwc_raw_frame);
        else
                bytes_to_read = pdev->view.size;
 
        /* copy bytes to user space; we allow for partial reads */
        if (count + pdev->image_read_pos > bytes_to_read)
                count = bytes_to_read - pdev->image_read_pos;
-       if (copy_to_user(buf, pdev->image_ptr[pdev->fill_image] + pdev->image_read_pos, count))
+       image_buffer_addr = pdev->image_data;
+       image_buffer_addr += pdev->images[pdev->fill_image].offset;
+       image_buffer_addr += pdev->image_read_pos;
+       if (copy_to_user(buf, image_buffer_addr, count))
                return -EFAULT;
        pdev->image_read_pos += count;
        if (pdev->image_read_pos >= bytes_to_read) { /* All data has been read */
@@ -1253,370 +1336,56 @@ static unsigned int pwc_video_poll(struct file *file, poll_table *wait)
        return 0;
 }
 
-static int pwc_video_do_ioctl(struct inode *inode, struct file *file,
-                             unsigned int cmd, void *arg)
-{
-       struct video_device *vdev = file->private_data;
-       struct pwc_device *pdev;
-       DECLARE_WAITQUEUE(wait, current);
-
-       if (vdev == NULL)
-               return -EFAULT;
-       pdev = vdev->priv;
-       if (pdev == NULL)
-               return -EFAULT;
-
-       switch (cmd) {
-               /* Query cabapilities */
-               case VIDIOCGCAP:
-               {
-                       struct video_capability *caps = arg;
-
-                       strcpy(caps->name, vdev->name);
-                       caps->type = VID_TYPE_CAPTURE;
-                       caps->channels = 1;
-                       caps->audios = 1;
-                       caps->minwidth  = pdev->view_min.x;
-                       caps->minheight = pdev->view_min.y;
-                       caps->maxwidth  = pdev->view_max.x;
-                       caps->maxheight = pdev->view_max.y;
-                       break;
-               }
-
-               /* Channel functions (simulate 1 channel) */
-               case VIDIOCGCHAN:
-               {
-                       struct video_channel *v = arg;
-
-                       if (v->channel != 0)
-                               return -EINVAL;
-                       v->flags = 0;
-                       v->tuners = 0;
-                       v->type = VIDEO_TYPE_CAMERA;
-                       strcpy(v->name, "Webcam");
-                       return 0;
-               }
-
-               case VIDIOCSCHAN:
-               {
-                       /* The spec says the argument is an integer, but
-                          the bttv driver uses a video_channel arg, which
-                          makes sense becasue it also has the norm flag.
-                        */
-                       struct video_channel *v = arg;
-                       if (v->channel != 0)
-                               return -EINVAL;
-                       return 0;
-               }
-
-
-               /* Picture functions; contrast etc. */
-               case VIDIOCGPICT:
-               {
-                       struct video_picture *p = arg;
-                       int val;
-
-                       val = pwc_get_brightness(pdev);
-                       if (val >= 0)
-                               p->brightness = val;
-                       else
-                               p->brightness = 0xffff;
-                       val = pwc_get_contrast(pdev);
-                       if (val >= 0)
-                               p->contrast = val;
-                       else
-                               p->contrast = 0xffff;
-                       /* Gamma, Whiteness, what's the difference? :) */
-                       val = pwc_get_gamma(pdev);
-                       if (val >= 0)
-                               p->whiteness = val;
-                       else
-                               p->whiteness = 0xffff;
-                       val = pwc_get_saturation(pdev);
-                       if (val >= 0)
-                               p->colour = val;
-                       else
-                               p->colour = 0xffff;
-                       p->depth = 24;
-                       p->palette = pdev->vpalette;
-                       p->hue = 0xFFFF; /* N/A */
-                       break;
-               }
-
-               case VIDIOCSPICT:
-               {
-                       struct video_picture *p = arg;
-                       /*
-                        *      FIXME:  Suppose we are mid read
-                               ANSWER: No problem: the firmware of the camera
-                                       can handle brightness/contrast/etc
-                                       changes at _any_ time, and the palette
-                                       is used exactly once in the uncompress
-                                       routine.
-                        */
-                       pwc_set_brightness(pdev, p->brightness);
-                       pwc_set_contrast(pdev, p->contrast);
-                       pwc_set_gamma(pdev, p->whiteness);
-                       pwc_set_saturation(pdev, p->colour);
-                       if (p->palette && p->palette != pdev->vpalette) {
-                               switch (p->palette) {
-                                       case VIDEO_PALETTE_YUV420P:
-                                       case VIDEO_PALETTE_RAW:
-                                               pdev->vpalette = p->palette;
-                                               return pwc_try_video_mode(pdev, pdev->image.x, pdev->image.y, pdev->vframes, pdev->vcompression, pdev->vsnapshot);
-                                               break;
-                                       default:
-                                               return -EINVAL;
-                                               break;
-                               }
-                       }
-                       break;
-               }
-
-               /* Window/size parameters */
-               case VIDIOCGWIN:
-               {
-                       struct video_window *vw = arg;
-
-                       vw->x = 0;
-                       vw->y = 0;
-                       vw->width = pdev->view.x;
-                       vw->height = pdev->view.y;
-                       vw->chromakey = 0;
-                       vw->flags = (pdev->vframes << PWC_FPS_SHIFT) |
-                                  (pdev->vsnapshot ? PWC_FPS_SNAPSHOT : 0);
-                       break;
-               }
-
-               case VIDIOCSWIN:
-               {
-                       struct video_window *vw = arg;
-                       int fps, snapshot, ret;
-
-                       fps = (vw->flags & PWC_FPS_FRMASK) >> PWC_FPS_SHIFT;
-                       snapshot = vw->flags & PWC_FPS_SNAPSHOT;
-                       if (fps == 0)
-                               fps = pdev->vframes;
-                       if (pdev->view.x == vw->width && pdev->view.y && fps == pdev->vframes && snapshot == pdev->vsnapshot)
-                               return 0;
-                       ret = pwc_try_video_mode(pdev, vw->width, vw->height, fps, pdev->vcompression, snapshot);
-                       if (ret)
-                               return ret;
-                       break;
-               }
-
-               /* We don't have overlay support (yet) */
-               case VIDIOCGFBUF:
-               {
-                       struct video_buffer *vb = arg;
-
-                       memset(vb,0,sizeof(*vb));
-                       break;
-               }
-
-               /* mmap() functions */
-               case VIDIOCGMBUF:
-               {
-                       /* Tell the user program how much memory is needed for a mmap() */
-                       struct video_mbuf *vm = arg;
-                       int i;
-
-                       memset(vm, 0, sizeof(*vm));
-                       vm->size = default_mbufs * pdev->len_per_image;
-                       vm->frames = default_mbufs; /* double buffering should be enough for most applications */
-                       for (i = 0; i < default_mbufs; i++)
-                               vm->offsets[i] = i * pdev->len_per_image;
-                       break;
-               }
-
-               case VIDIOCMCAPTURE:
-               {
-                       /* Start capture into a given image buffer (called 'frame' in video_mmap structure) */
-                       struct video_mmap *vm = arg;
-
-                       Trace(TRACE_READ, "VIDIOCMCAPTURE: %dx%d, frame %d, format %d\n", vm->width, vm->height, vm->frame, vm->format);
-                       if (vm->frame < 0 || vm->frame >= default_mbufs)
-                               return -EINVAL;
-
-                       /* xawtv is nasty. It probes the available palettes
-                          by setting a very small image size and trying
-                          various palettes... The driver doesn't support
-                          such small images, so I'm working around it.
-                        */
-                       if (vm->format)
-                       {
-                               switch (vm->format)
-                               {
-                                       case VIDEO_PALETTE_YUV420P:
-                                       case VIDEO_PALETTE_RAW:
-                                               break;
-                                       default:
-                                               return -EINVAL;
-                                               break;
-                               }
-                       }
-
-                       if ((vm->width != pdev->view.x || vm->height != pdev->view.y) &&
-                           (vm->width >= pdev->view_min.x && vm->height >= pdev->view_min.y)) {
-                               int ret;
-
-                               Trace(TRACE_OPEN, "VIDIOCMCAPTURE: changing size to please xawtv :-(.\n");
-                               ret = pwc_try_video_mode(pdev, vm->width, vm->height, pdev->vframes, pdev->vcompression, pdev->vsnapshot);
-                               if (ret)
-                                       return ret;
-                       } /* ... size mismatch */
-
-                       /* FIXME: should we lock here? */
-                       if (pdev->image_used[vm->frame])
-                               return -EBUSY;  /* buffer wasn't available. Bummer */
-                       pdev->image_used[vm->frame] = 1;
-
-                       /* Okay, we're done here. In the SYNC call we wait until a
-                          frame comes available, then expand image into the given
-                          buffer.
-                          In contrast to the CPiA cam the Philips cams deliver a
-                          constant stream, almost like a grabber card. Also,
-                          we have separate buffers for the rawdata and the image,
-                          meaning we can nearly always expand into the requested buffer.
-                        */
-                       Trace(TRACE_READ, "VIDIOCMCAPTURE done.\n");
-                       break;
-               }
-
-               case VIDIOCSYNC:
-               {
-                       /* The doc says: "Whenever a buffer is used it should
-                          call VIDIOCSYNC to free this frame up and continue."
-
-                          The only odd thing about this whole procedure is
-                          that MCAPTURE flags the buffer as "in use", and
-                          SYNC immediately unmarks it, while it isn't
-                          after SYNC that you know that the buffer actually
-                          got filled! So you better not start a CAPTURE in
-                          the same frame immediately (use double buffering).
-                          This is not a problem for this cam, since it has
-                          extra intermediate buffers, but a hardware
-                          grabber card will then overwrite the buffer
-                          you're working on.
-                        */
-                       int *mbuf = arg;
-                       int ret;
-
-                       Trace(TRACE_READ, "VIDIOCSYNC called (%d).\n", *mbuf);
-
-                       /* bounds check */
-                       if (*mbuf < 0 || *mbuf >= default_mbufs)
-                               return -EINVAL;
-                       /* check if this buffer was requested anyway */
-                       if (pdev->image_used[*mbuf] == 0)
-                               return -EINVAL;
-
-                       /* Add ourselves to the frame wait-queue.
-
-                          FIXME: needs auditing for safety.
-                          QUESTION: In what respect? I think that using the
-                                    frameq is safe now.
-                        */
-                       add_wait_queue(&pdev->frameq, &wait);
-                       while (pdev->full_frames == NULL) {
-                               if (pdev->error_status) {
-                                       remove_wait_queue(&pdev->frameq, &wait);
-                                       set_current_state(TASK_RUNNING);
-                                       return -pdev->error_status;
-                               }
-
-                               if (signal_pending(current)) {
-                                       remove_wait_queue(&pdev->frameq, &wait);
-                                       set_current_state(TASK_RUNNING);
-                                       return -ERESTARTSYS;
-                               }
-                               schedule();
-                               set_current_state(TASK_INTERRUPTIBLE);
-                       }
-                       remove_wait_queue(&pdev->frameq, &wait);
-                       set_current_state(TASK_RUNNING);
-
-                       /* The frame is ready. Expand in the image buffer
-                          requested by the user. I don't care if you
-                          mmap() 5 buffers and request data in this order:
-                          buffer 4 2 3 0 1 2 3 0 4 3 1 . . .
-                          Grabber hardware may not be so forgiving.
-                        */
-                       Trace(TRACE_READ, "VIDIOCSYNC: frame ready.\n");
-                       pdev->fill_image = *mbuf; /* tell in which buffer we want the image to be expanded */
-                       /* Decompress, etc */
-                       ret = pwc_handle_frame(pdev);
-                       pdev->image_used[*mbuf] = 0;
-                       if (ret)
-                               return -EFAULT;
-                       break;
-               }
-
-               case VIDIOCGAUDIO:
-               {
-                       struct video_audio *v = arg;
-
-                       strcpy(v->name, "Microphone");
-                       v->audio = -1; /* unknown audio minor */
-                       v->flags = 0;
-                       v->mode = VIDEO_SOUND_MONO;
-                       v->volume = 0;
-                       v->bass = 0;
-                       v->treble = 0;
-                       v->balance = 0x8000;
-                       v->step = 1;
-                       break;
-               }
-
-               case VIDIOCSAUDIO:
-               {
-                       /* Dummy: nothing can be set */
-                       break;
-               }
-
-               case VIDIOCGUNIT:
-               {
-                       struct video_unit *vu = arg;
-
-                       vu->video = pdev->vdev->minor & 0x3F;
-                       vu->audio = -1; /* not known yet */
-                       vu->vbi = -1;
-                       vu->radio = -1;
-                       vu->teletext = -1;
-                       break;
-               }
-               default:
-                       return pwc_ioctl(pdev, cmd, arg);
-       } /* ..switch */
-       return 0;
-}
-
 static int pwc_video_ioctl(struct inode *inode, struct file *file,
                           unsigned int cmd, unsigned long arg)
 {
        return video_usercopy(inode, file, cmd, arg, pwc_video_do_ioctl);
 }
 
-
 static int pwc_video_mmap(struct file *file, struct vm_area_struct *vma)
 {
        struct video_device *vdev = file->private_data;
        struct pwc_device *pdev;
-       unsigned long start = vma->vm_start;
-       unsigned long size  = vma->vm_end-vma->vm_start;
-       unsigned long page, pos;
+       unsigned long start;
+       unsigned long size;
+       unsigned long page, pos = 0;
+       int index;
 
-       Trace(TRACE_MEMORY, "mmap(0x%p, 0x%lx, %lu) called.\n", vdev, start, size);
+       PWC_DEBUG_MEMORY(">> %s\n", __FUNCTION__);
        pdev = vdev->priv;
+       size = vma->vm_end - vma->vm_start;
+       start = vma->vm_start;
 
-       vma->vm_flags |= VM_IO;
+       /* Find the idx buffer for this mapping */
+       for (index = 0; index < pwc_mbufs; index++) {
+               pos = pdev->images[index].offset;
+               if ((pos>>PAGE_SHIFT) == vma->vm_pgoff)
+                       break;
+       }
+       if (index == MAX_IMAGES)
+               return -EINVAL;
+       if (index == 0) {
+               /*
+                * Special case for v4l1. In v4l1, we map only one big buffer,
+                * but in v4l2 each buffer is mapped
+                */
+               unsigned long total_size;
+               total_size = pwc_mbufs * pdev->len_per_image;
+               if (size != pdev->len_per_image && size != total_size) {
+                       PWC_ERROR("Wrong size (%lu) needed to be len_per_image=%d or total_size=%lu\n",
+                                  size, pdev->len_per_image, total_size);
+                       return -EINVAL;
+               }
+       } else if (size > pdev->len_per_image)
+               return -EINVAL;
 
-       pos = (unsigned long)pdev->image_data;
+       vma->vm_flags |= VM_IO; /* from 2.6.9-acX */
+
+       pos += (unsigned long)pdev->image_data;
        while (size > 0) {
                page = vmalloc_to_pfn((void *)pos);
                if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED))
                        return -EAGAIN;
-
                start += PAGE_SIZE;
                pos += PAGE_SIZE;
                if (size > PAGE_SIZE)
@@ -1624,7 +1393,6 @@ static int pwc_video_mmap(struct file *file, struct vm_area_struct *vma)
                else
                        size = 0;
        }
-
        return 0;
 }
 
@@ -1645,10 +1413,12 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
        int video_nr = -1; /* default: use next available device */
        char serial_number[30], *name;
 
+       vendor_id = le16_to_cpu(udev->descriptor.idVendor);
+       product_id = le16_to_cpu(udev->descriptor.idProduct);
+
        /* Check if we can handle this device */
-       Trace(TRACE_PROBE, "probe() called [%04X %04X], if %d\n",
-               le16_to_cpu(udev->descriptor.idVendor),
-               le16_to_cpu(udev->descriptor.idProduct),
+       PWC_DEBUG_PROBE("probe() called [%04X %04X], if %d\n",
+               vendor_id, product_id,
                intf->altsetting->desc.bInterfaceNumber);
 
        /* the interfaces are probed one by one. We are only interested in the
@@ -1658,61 +1428,63 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
        if (intf->altsetting->desc.bInterfaceNumber > 0)
                return -ENODEV;
 
-       vendor_id = le16_to_cpu(udev->descriptor.idVendor);
-       product_id = le16_to_cpu(udev->descriptor.idProduct);
-
        if (vendor_id == 0x0471) {
                switch (product_id) {
                case 0x0302:
-                       Info("Philips PCA645VC USB webcam detected.\n");
+                       PWC_INFO("Philips PCA645VC USB webcam detected.\n");
                        name = "Philips 645 webcam";
                        type_id = 645;
                        break;
                case 0x0303:
-                       Info("Philips PCA646VC USB webcam detected.\n");
+                       PWC_INFO("Philips PCA646VC USB webcam detected.\n");
                        name = "Philips 646 webcam";
                        type_id = 646;
                        break;
                case 0x0304:
-                       Info("Askey VC010 type 2 USB webcam detected.\n");
+                       PWC_INFO("Askey VC010 type 2 USB webcam detected.\n");
                        name = "Askey VC010 webcam";
                        type_id = 646;
                        break;
                case 0x0307:
-                       Info("Philips PCVC675K (Vesta) USB webcam detected.\n");
+                       PWC_INFO("Philips PCVC675K (Vesta) USB webcam detected.\n");
                        name = "Philips 675 webcam";
                        type_id = 675;
                        break;
                case 0x0308:
-                       Info("Philips PCVC680K (Vesta Pro) USB webcam detected.\n");
+                       PWC_INFO("Philips PCVC680K (Vesta Pro) USB webcam detected.\n");
                        name = "Philips 680 webcam";
                        type_id = 680;
                        break;
                case 0x030C:
-                       Info("Philips PCVC690K (Vesta Pro Scan) USB webcam detected.\n");
+                       PWC_INFO("Philips PCVC690K (Vesta Pro Scan) USB webcam detected.\n");
                        name = "Philips 690 webcam";
                        type_id = 690;
                        break;
                case 0x0310:
-                       Info("Philips PCVC730K (ToUCam Fun)/PCVC830 (ToUCam II) USB webcam detected.\n");
+                       PWC_INFO("Philips PCVC730K (ToUCam Fun)/PCVC830 (ToUCam II) USB webcam detected.\n");
                        name = "Philips 730 webcam";
                        type_id = 730;
                        break;
                case 0x0311:
-                       Info("Philips PCVC740K (ToUCam Pro)/PCVC840 (ToUCam II) USB webcam detected.\n");
+                       PWC_INFO("Philips PCVC740K (ToUCam Pro)/PCVC840 (ToUCam II) USB webcam detected.\n");
                        name = "Philips 740 webcam";
                        type_id = 740;
                        break;
                case 0x0312:
-                       Info("Philips PCVC750K (ToUCam Pro Scan) USB webcam detected.\n");
+                       PWC_INFO("Philips PCVC750K (ToUCam Pro Scan) USB webcam detected.\n");
                        name = "Philips 750 webcam";
                        type_id = 750;
                        break;
                case 0x0313:
-                       Info("Philips PCVC720K/40 (ToUCam XS) USB webcam detected.\n");
+                       PWC_INFO("Philips PCVC720K/40 (ToUCam XS) USB webcam detected.\n");
                        name = "Philips 720K/40 webcam";
                        type_id = 720;
                        break;
+               case 0x0329:
+                       PWC_INFO("Philips SPC 900NC USB webcam detected.\n");
+                       name = "Philips SPC 900NC webcam";
+                       type_id = 720;
+                       break;
                default:
                        return -ENODEV;
                        break;
@@ -1721,7 +1493,7 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
        else if (vendor_id == 0x069A) {
                switch(product_id) {
                case 0x0001:
-                       Info("Askey VC010 type 1 USB webcam detected.\n");
+                       PWC_INFO("Askey VC010 type 1 USB webcam detected.\n");
                        name = "Askey VC010 webcam";
                        type_id = 645;
                        break;
@@ -1733,32 +1505,33 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
        else if (vendor_id == 0x046d) {
                switch(product_id) {
                case 0x08b0:
-                       Info("Logitech QuickCam Pro 3000 USB webcam detected.\n");
+                       PWC_INFO("Logitech QuickCam Pro 3000 USB webcam detected.\n");
                        name = "Logitech QuickCam Pro 3000";
                        type_id = 740; /* CCD sensor */
                        break;
                case 0x08b1:
-                       Info("Logitech QuickCam Notebook Pro USB webcam detected.\n");
+                       PWC_INFO("Logitech QuickCam Notebook Pro USB webcam detected.\n");
                        name = "Logitech QuickCam Notebook Pro";
                        type_id = 740; /* CCD sensor */
                        break;
                case 0x08b2:
-                       Info("Logitech QuickCam 4000 Pro USB webcam detected.\n");
+                       PWC_INFO("Logitech QuickCam 4000 Pro USB webcam detected.\n");
                        name = "Logitech QuickCam Pro 4000";
                        type_id = 740; /* CCD sensor */
                        break;
                case 0x08b3:
-                       Info("Logitech QuickCam Zoom USB webcam detected.\n");
+                       PWC_INFO("Logitech QuickCam Zoom USB webcam detected.\n");
                        name = "Logitech QuickCam Zoom";
                        type_id = 740; /* CCD sensor */
                        break;
                case 0x08B4:
-                       Info("Logitech QuickCam Zoom (new model) USB webcam detected.\n");
+                       PWC_INFO("Logitech QuickCam Zoom (new model) USB webcam detected.\n");
                        name = "Logitech QuickCam Zoom";
                        type_id = 740; /* CCD sensor */
+                       power_save = 1;
                        break;
                case 0x08b5:
-                       Info("Logitech QuickCam Orbit/Sphere USB webcam detected.\n");
+                       PWC_INFO("Logitech QuickCam Orbit/Sphere USB webcam detected.\n");
                        name = "Logitech QuickCam Orbit";
                        type_id = 740; /* CCD sensor */
                        features |= FEATURE_MOTOR_PANTILT;
@@ -1766,7 +1539,7 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
                case 0x08b6:
                case 0x08b7:
                case 0x08b8:
-                       Info("Logitech QuickCam detected (reserved ID).\n");
+                       PWC_INFO("Logitech QuickCam detected (reserved ID).\n");
                        name = "Logitech QuickCam (res.)";
                        type_id = 730; /* Assuming CMOS */
                        break;
@@ -1782,15 +1555,20 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
                 */
                switch(product_id) {
                case 0x9000:
-                       Info("Samsung MPC-C10 USB webcam detected.\n");
+                       PWC_INFO("Samsung MPC-C10 USB webcam detected.\n");
                        name = "Samsung MPC-C10";
                        type_id = 675;
                        break;
                case 0x9001:
-                       Info("Samsung MPC-C30 USB webcam detected.\n");
+                       PWC_INFO("Samsung MPC-C30 USB webcam detected.\n");
                        name = "Samsung MPC-C30";
                        type_id = 675;
                        break;
+               case 0x9002:
+                       PWC_INFO("Samsung SNC-35E (v3.0) USB webcam detected.\n");
+                       name = "Samsung MPC-C30";
+                       type_id = 740;
+                       break;
                default:
                        return -ENODEV;
                        break;
@@ -1799,12 +1577,12 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
        else if (vendor_id == 0x041e) {
                switch(product_id) {
                case 0x400c:
-                       Info("Creative Labs Webcam 5 detected.\n");
+                       PWC_INFO("Creative Labs Webcam 5 detected.\n");
                        name = "Creative Labs Webcam 5";
                        type_id = 730;
                        break;
                case 0x4011:
-                       Info("Creative Labs Webcam Pro Ex detected.\n");
+                       PWC_INFO("Creative Labs Webcam Pro Ex detected.\n");
                        name = "Creative Labs Webcam Pro Ex";
                        type_id = 740;
                        break;
@@ -1816,7 +1594,7 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
        else if (vendor_id == 0x04cc) {
                switch(product_id) {
                case 0x8116:
-                       Info("Sotec Afina Eye USB webcam detected.\n");
+                       PWC_INFO("Sotec Afina Eye USB webcam detected.\n");
                        name = "Sotec Afina Eye";
                        type_id = 730;
                        break;
@@ -1829,7 +1607,7 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
                switch(product_id) {
                case 0x8116:
                        /* This is essentially the same cam as the Sotec Afina Eye */
-                       Info("AME Co. Afina Eye USB webcam detected.\n");
+                       PWC_INFO("AME Co. Afina Eye USB webcam detected.\n");
                        name = "AME Co. Afina Eye";
                        type_id = 750;
                        break;
@@ -1842,12 +1620,12 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
        else if (vendor_id == 0x0d81) {
                switch(product_id) {
                case 0x1900:
-                       Info("Visionite VCS-UC300 USB webcam detected.\n");
+                       PWC_INFO("Visionite VCS-UC300 USB webcam detected.\n");
                        name = "Visionite VCS-UC300";
                        type_id = 740; /* CCD sensor */
                        break;
                case 0x1910:
-                       Info("Visionite VCS-UM100 USB webcam detected.\n");
+                       PWC_INFO("Visionite VCS-UM100 USB webcam detected.\n");
                        name = "Visionite VCS-UM100";
                        type_id = 730; /* CMOS sensor */
                        break;
@@ -1861,15 +1639,15 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
 
        memset(serial_number, 0, 30);
        usb_string(udev, udev->descriptor.iSerialNumber, serial_number, 29);
-       Trace(TRACE_PROBE, "Device serial number is %s\n", serial_number);
+       PWC_DEBUG_PROBE("Device serial number is %s\n", serial_number);
 
        if (udev->descriptor.bNumConfigurations > 1)
-               Info("Warning: more than 1 configuration available.\n");
+               PWC_WARNING("Warning: more than 1 configuration available.\n");
 
        /* Allocate structure, initialize pointers, mutexes, etc. and link it to the usb_device */
        pdev = kzalloc(sizeof(struct pwc_device), GFP_KERNEL);
        if (pdev == NULL) {
-               Err("Oops, could not allocate memory for pwc_device.\n");
+               PWC_ERROR("Oops, could not allocate memory for pwc_device.\n");
                return -ENOMEM;
        }
        pdev->type = type_id;
@@ -1900,17 +1678,18 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
        pdev->vdev = video_device_alloc();
        if (pdev->vdev == 0)
        {
-               Err("Err, cannot allocate video_device struture. Failing probe.");
+               PWC_ERROR("Err, cannot allocate video_device struture. Failing probe.");
                kfree(pdev);
                return -ENOMEM;
        }
        memcpy(pdev->vdev, &pwc_template, sizeof(pwc_template));
+       pdev->vdev->dev = &(udev->dev);
        strcpy(pdev->vdev->name, name);
        pdev->vdev->owner = THIS_MODULE;
        video_set_drvdata(pdev->vdev, pdev);
 
        pdev->release = le16_to_cpu(udev->descriptor.bcdDevice);
-       Trace(TRACE_PROBE, "Release: %04x\n", pdev->release);
+       PWC_DEBUG_PROBE("Release: %04x\n", pdev->release);
 
        /* Now search device_hint[] table for a match, so we can hint a node number. */
        for (hint = 0; hint < MAX_DEV_HINTS; hint++) {
@@ -1918,10 +1697,10 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
                     (device_hint[hint].pdev == NULL)) {
                        /* so far, so good... try serial number */
                        if ((device_hint[hint].serial_number[0] == '*') || !strcmp(device_hint[hint].serial_number, serial_number)) {
-                               /* match! */
-                               video_nr = device_hint[hint].device_node;
-                               Trace(TRACE_PROBE, "Found hint, will try to register as /dev/video%d\n", video_nr);
-                               break;
+                               /* match! */
+                               video_nr = device_hint[hint].device_node;
+                               PWC_DEBUG_PROBE("Found hint, will try to register as /dev/video%d\n", video_nr);
+                               break;
                        }
                }
        }
@@ -1929,21 +1708,27 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
        pdev->vdev->release = video_device_release;
        i = video_register_device(pdev->vdev, VFL_TYPE_GRABBER, video_nr);
        if (i < 0) {
-               Err("Failed to register as video device (%d).\n", i);
+               PWC_ERROR("Failed to register as video device (%d).\n", i);
                video_device_release(pdev->vdev); /* Drip... drip... drip... */
                kfree(pdev); /* Oops, no memory leaks please */
                return -EIO;
        }
        else {
-               Info("Registered as /dev/video%d.\n", pdev->vdev->minor & 0x3F);
+               PWC_INFO("Registered as /dev/video%d.\n", pdev->vdev->minor & 0x3F);
        }
 
        /* occupy slot */
        if (hint < MAX_DEV_HINTS)
                device_hint[hint].pdev = pdev;
 
-       Trace(TRACE_PROBE, "probe() function returning struct at 0x%p.\n", pdev);
+       PWC_DEBUG_PROBE("probe() function returning struct at 0x%p.\n", pdev);
        usb_set_intfdata (intf, pdev);
+       pwc_create_sysfs_files(pdev->vdev);
+
+       /* Set the leds off */
+       pwc_set_leds(pdev, 0, 0);
+       pwc_camera_power(pdev, 0);
+
        return 0;
 }
 
@@ -1957,27 +1742,21 @@ static void usb_pwc_disconnect(struct usb_interface *intf)
        pdev = usb_get_intfdata (intf);
        usb_set_intfdata (intf, NULL);
        if (pdev == NULL) {
-               Err("pwc_disconnect() Called without private pointer.\n");
+               PWC_ERROR("pwc_disconnect() Called without private pointer.\n");
                goto disconnect_out;
        }
        if (pdev->udev == NULL) {
-               Err("pwc_disconnect() already called for %p\n", pdev);
+               PWC_ERROR("pwc_disconnect() already called for %p\n", pdev);
                goto disconnect_out;
        }
        if (pdev->udev != interface_to_usbdev(intf)) {
-               Err("pwc_disconnect() Woops: pointer mismatch udev/pdev.\n");
-               goto disconnect_out;
-       }
-#ifdef PWC_MAGIC
-       if (pdev->magic != PWC_MAGIC) {
-               Err("pwc_disconnect() Magic number failed. Consult your scrolls and try again.\n");
+               PWC_ERROR("pwc_disconnect() Woops: pointer mismatch udev/pdev.\n");
                goto disconnect_out;
        }
-#endif
 
        /* We got unplugged; this is signalled by an EPIPE error code */
        if (pdev->vopen) {
-               Info("Disconnected while webcam is in use!\n");
+               PWC_INFO("Disconnected while webcam is in use!\n");
                pdev->error_status = EPIPE;
        }
 
@@ -1987,7 +1766,8 @@ static void usb_pwc_disconnect(struct usb_interface *intf)
        while (pdev->vopen)
                schedule();
        /* Device is now closed, so we can safely unregister it */
-       Trace(TRACE_PROBE, "Unregistering video device in disconnect().\n");
+       PWC_DEBUG_PROBE("Unregistering video device in disconnect().\n");
+       pwc_remove_sysfs_files(pdev->vdev);
        video_unregister_device(pdev->vdev);
 
        /* Free memory (don't set pdev to 0 just yet) */
@@ -2021,58 +1801,64 @@ static int pwc_atoi(const char *s)
  * Initialization code & module stuff
  */
 
-static char size[10];
-static int fps = 0;
-static int fbufs = 0;
-static int mbufs = 0;
-static int trace = -1;
+static char *size;
+static int fps;
+static int fbufs;
+static int mbufs;
 static int compression = -1;
 static int leds[2] = { -1, -1 };
-static char *dev_hint[MAX_DEV_HINTS] = { };
+static int leds_nargs;
+static char *dev_hint[MAX_DEV_HINTS];
+static int dev_hint_nargs;
+
+module_param(size, charp, 0444);
+module_param(fps, int, 0444);
+module_param(fbufs, int, 0444);
+module_param(mbufs, int, 0444);
+#if CONFIG_PWC_DEBUG
+module_param_named(trace, pwc_trace, int, 0644);
+#endif
+module_param(power_save, int, 0444);
+module_param(compression, int, 0444);
+module_param_array(leds, int, &leds_nargs, 0444);
+module_param_array(dev_hint, charp, &dev_hint_nargs, 0444);
 
-module_param_string(size, size, sizeof(size), 0);
 MODULE_PARM_DESC(size, "Initial image size. One of sqcif, qsif, qcif, sif, cif, vga");
-module_param(fps, int, 0000);
 MODULE_PARM_DESC(fps, "Initial frames per second. Varies with model, useful range 5-30");
-module_param(fbufs, int, 0000);
 MODULE_PARM_DESC(fbufs, "Number of internal frame buffers to reserve");
-module_param(mbufs, int, 0000);
 MODULE_PARM_DESC(mbufs, "Number of external (mmap()ed) image buffers");
-module_param(trace, int, 0000);
 MODULE_PARM_DESC(trace, "For debugging purposes");
-module_param(power_save, bool, 0000);
 MODULE_PARM_DESC(power_save, "Turn power save feature in camera on or off");
-module_param(compression, int, 0000);
 MODULE_PARM_DESC(compression, "Preferred compression quality. Range 0 (uncompressed) to 3 (high compression)");
-module_param_array(leds, int, NULL, 0000);
 MODULE_PARM_DESC(leds, "LED on,off time in milliseconds");
-module_param_array(dev_hint, charp, NULL, 0000);
 MODULE_PARM_DESC(dev_hint, "Device node hints");
 
 MODULE_DESCRIPTION("Philips & OEM USB webcam driver");
 MODULE_AUTHOR("Luc Saillard <luc@saillard.org>");
 MODULE_LICENSE("GPL");
+MODULE_ALIAS("pwcx");
+MODULE_VERSION( PWC_VERSION );
 
 static int __init usb_pwc_init(void)
 {
        int i, sz;
        char *sizenames[PSZ_MAX] = { "sqcif", "qsif", "qcif", "sif", "cif", "vga" };
 
-       Info("Philips webcam module version " PWC_VERSION " loaded.\n");
-       Info("Supports Philips PCA645/646, PCVC675/680/690, PCVC720[40]/730/740/750 & PCVC830/840.\n");
-       Info("Also supports the Askey VC010, various Logitech Quickcams, Samsung MPC-C10 and MPC-C30,\n");
-       Info("the Creative WebCam 5 & Pro Ex, SOTEC Afina Eye and Visionite VCS-UC300 and VCS-UM100.\n");
+       PWC_INFO("Philips webcam module version " PWC_VERSION " loaded.\n");
+       PWC_INFO("Supports Philips PCA645/646, PCVC675/680/690, PCVC720[40]/730/740/750 & PCVC830/840.\n");
+       PWC_INFO("Also supports the Askey VC010, various Logitech Quickcams, Samsung MPC-C10 and MPC-C30,\n");
+       PWC_INFO("the Creative WebCam 5 & Pro Ex, SOTEC Afina Eye and Visionite VCS-UC300 and VCS-UM100.\n");
 
        if (fps) {
                if (fps < 4 || fps > 30) {
-                       Err("Framerate out of bounds (4-30).\n");
+                       PWC_ERROR("Framerate out of bounds (4-30).\n");
                        return -EINVAL;
                }
                default_fps = fps;
-               Info("Default framerate set to %d.\n", default_fps);
+               PWC_DEBUG_MODULE("Default framerate set to %d.\n", default_fps);
        }
 
-       if (size[0]) {
+       if (size) {
                /* string; try matching with array */
                for (sz = 0; sz < PSZ_MAX; sz++) {
                        if (!strcmp(sizenames[sz], size)) { /* Found! */
@@ -2081,41 +1867,42 @@ static int __init usb_pwc_init(void)
                        }
                }
                if (sz == PSZ_MAX) {
-                       Err("Size not recognized; try size=[sqcif | qsif | qcif | sif | cif | vga].\n");
+                       PWC_ERROR("Size not recognized; try size=[sqcif | qsif | qcif | sif | cif | vga].\n");
                        return -EINVAL;
                }
-               Info("Default image size set to %s [%dx%d].\n", sizenames[default_size], pwc_image_sizes[default_size].x, pwc_image_sizes[default_size].y);
+               PWC_DEBUG_MODULE("Default image size set to %s [%dx%d].\n", sizenames[default_size], pwc_image_sizes[default_size].x, pwc_image_sizes[default_size].y);
        }
        if (mbufs) {
                if (mbufs < 1 || mbufs > MAX_IMAGES) {
-                       Err("Illegal number of mmap() buffers; use a number between 1 and %d.\n", MAX_IMAGES);
+                       PWC_ERROR("Illegal number of mmap() buffers; use a number between 1 and %d.\n", MAX_IMAGES);
                        return -EINVAL;
                }
-               default_mbufs = mbufs;
-               Info("Number of image buffers set to %d.\n", default_mbufs);
+               pwc_mbufs = mbufs;
+               PWC_DEBUG_MODULE("Number of image buffers set to %d.\n", pwc_mbufs);
        }
        if (fbufs) {
                if (fbufs < 2 || fbufs > MAX_FRAMES) {
-                       Err("Illegal number of frame buffers; use a number between 2 and %d.\n", MAX_FRAMES);
+                       PWC_ERROR("Illegal number of frame buffers; use a number between 2 and %d.\n", MAX_FRAMES);
                        return -EINVAL;
                }
                default_fbufs = fbufs;
-               Info("Number of frame buffers set to %d.\n", default_fbufs);
+               PWC_DEBUG_MODULE("Number of frame buffers set to %d.\n", default_fbufs);
        }
-       if (trace >= 0) {
-               Info("Trace options: 0x%04x\n", trace);
-               pwc_trace = trace;
+#if CONFIG_PWC_DEBUG
+       if (pwc_trace >= 0) {
+               PWC_DEBUG_MODULE("Trace options: 0x%04x\n", pwc_trace);
        }
+#endif
        if (compression >= 0) {
                if (compression > 3) {
-                       Err("Invalid compression setting; use a number between 0 (uncompressed) and 3 (high).\n");
+                       PWC_ERROR("Invalid compression setting; use a number between 0 (uncompressed) and 3 (high).\n");
                        return -EINVAL;
                }
                pwc_preferred_compression = compression;
-               Info("Preferred compression set to %d.\n", pwc_preferred_compression);
+               PWC_DEBUG_MODULE("Preferred compression set to %d.\n", pwc_preferred_compression);
        }
        if (power_save)
-               Info("Enabling power save on open/close.\n");
+               PWC_DEBUG_MODULE("Enabling power save on open/close.\n");
        if (leds[0] >= 0)
                led_on = leds[0];
        if (leds[1] >= 0)
@@ -2146,14 +1933,14 @@ static int __init usb_pwc_init(void)
                                dot++;
                        /* Few sanity checks */
                        if (*dot != '\0' && dot > colon) {
-                               Err("Malformed camera hint: the colon must be after the dot.\n");
+                               PWC_ERROR("Malformed camera hint: the colon must be after the dot.\n");
                                return -EINVAL;
                        }
 
                        if (*colon == '\0') {
                                /* No colon */
                                if (*dot != '\0') {
-                                       Err("Malformed camera hint: no colon + device node given.\n");
+                                       PWC_ERROR("Malformed camera hint: no colon + device node given.\n");
                                        return -EINVAL;
                                }
                                else {
@@ -2178,28 +1965,27 @@ static int __init usb_pwc_init(void)
                                        device_hint[i].serial_number[k] = '\0';
                                }
                        }
-#if PWC_DEBUG
-                       Debug("device_hint[%d]:\n", i);
-                       Debug("  type    : %d\n", device_hint[i].type);
-                       Debug("  serial# : %s\n", device_hint[i].serial_number);
-                       Debug("  node    : %d\n", device_hint[i].device_node);
-#endif
+                       PWC_TRACE("device_hint[%d]:\n", i);
+                       PWC_TRACE("  type    : %d\n", device_hint[i].type);
+                       PWC_TRACE("  serial# : %s\n", device_hint[i].serial_number);
+                       PWC_TRACE("  node    : %d\n", device_hint[i].device_node);
                }
                else
                        device_hint[i].type = 0; /* not filled */
        } /* ..for MAX_DEV_HINTS */
 
-       Trace(TRACE_PROBE, "Registering driver at address 0x%p.\n", &pwc_driver);
+       PWC_DEBUG_PROBE("Registering driver at address 0x%p.\n", &pwc_driver);
        return usb_register(&pwc_driver);
 }
 
 static void __exit usb_pwc_exit(void)
 {
-       Trace(TRACE_MODULE, "Deregistering driver.\n");
+       PWC_DEBUG_MODULE("Deregistering driver.\n");
        usb_deregister(&pwc_driver);
-       Info("Philips webcam module removed.\n");
+       PWC_INFO("Philips webcam module removed.\n");
 }
 
 module_init(usb_pwc_init);
 module_exit(usb_pwc_exit);
 
+/* vim: set cino= formatoptions=croql cindent shiftwidth=8 tabstop=8: */