[cpia] USB - First Contact (2/2)

Jochen Scharrlach Jochen.Scharrlach@schwaben.de
Wed, 26 Jan 2000 22:09:03 +0100 (CET)


 	if(cam->open_count == 0) {
 		DBG("unregistering video\n");
 		video_unregister_device(&cam->vdev);
 	} else {
 		LOG("/dev/video%d removed while open, "
 		    "deferring video_unregister_device\n", cam->vdev.minor);
+		DBG("camera open -- setting ops to NULL\n");
+		cam->ops = NULL;
 	}
 	
-	/* Need a lock when adding/removing cameras.  This doesn't happen
-	 * often and doesn't take very long, so grabbing the kernel lock
-	 * should be OK. */
-	lock_kernel();
-	
 #ifdef CONFIG_PROC_FS
 	DBG("destroying /proc/cpia/video%d\n", cam->vdev.minor);
 	destroy_proc_cpia_cam(cam);
 #endif	
 	if(cam->open_count == 0) {
 		DBG("freeing camera\n");
-		kfree(camera[camnr]);
-		camera[camnr] = NULL;
-	} else {
-		DBG("camera open -- setting ops to NULL\n");
-		cam->ops = NULL;
+		kfree(cam);
 	}
-	unlock_kernel();
 }
 
 /****************************************************************************
--- cpia-0.6.0/module/cpia.h	Sat Jan 22 18:43:05 2000
+++ cpia-usb/module/cpia.h	Sun Jan 23 21:25:04 2000
@@ -26,12 +26,33 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+#include <linux/config.h>
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/proc_fs.h>
+#include <linux/ctype.h>
+#include <linux/videodev.h>
+#include <linux/pagemap.h>
+#include <asm/io.h>
+#include <asm/semaphore.h>
+#include <linux/wrapper.h>
+
+#ifdef CONFIG_KMOD
+#include <linux/kmod.h>
+#endif
+
 #define CPIA_MAJ_VER	0
-#define CPIA_MIN_VER    6
+#define CPIA_MIN_VER    5
 #define CPIA_PATCH_VER	0
 
 #define CPIA_PP_MAJ_VER       0
-#define CPIA_PP_MIN_VER       6
+#define CPIA_PP_MIN_VER       5
 #define CPIA_PP_PATCH_VER     0
 
 #define CPIA_MAX_FRAME_SIZE_UNALIGNED	(352 * 288 * 4)   /* CIF at RGB32 */
@@ -46,7 +67,7 @@
 	/* open sets privdata to point to structure for this camera.
          * Returns negative value on error, otherwise 0.
 	 */
-	int (*open)(int camnr, void **privdata);
+	int (*open)(void *privdata);
 	
 	/* Registers callback function cb to be called with cbdata
 	 * when an image is ready.  If cb is NULL, only single image grabs
@@ -88,13 +109,184 @@
 	int (*close)(void *privdata);
 };
 
+struct cpia_frame {
+	u8 *data;
+	int count;
+	int width;
+	int height;
+	volatile int state;
+};
+
+struct cam_params {
+	struct {
+		u8 firmwareVersion;
+		u8 firmwareRevision;
+		u8 vcVersion;
+		u8 vcRevision;
+	} version;
+	struct {
+		u16 vendor;
+		u16 product;
+		u16 deviceRevision;
+	} pnpID;
+	struct {
+		u8 vpVersion;
+		u8 vpRevision;
+		u16 cameraHeadID;
+	} vpVersion;
+	struct {
+		u8 systemState;
+		u8 grabState;
+		u8 streamState;
+		u8 fatalError;
+		u8 cmdError;
+		u8 debugFlags;
+		u8 vpStatus;
+		u8 errorCode;
+	} status;
+	struct {
+		u8 brightness;
+		u8 contrast;
+		u8 saturation;
+	} colourParams;
+	struct {
+		u8 gainMode;
+		u8 expMode;
+		u8 compMode;
+		u8 centreWeight;
+		u8 gain;
+		u8 fineExp;
+		u8 coarseExpLo;
+		u8 coarseExpHi;
+		u8 redComp;
+		u8 green1Comp;
+		u8 green2Comp;
+		u8 blueComp;
+	} exposure;
+	struct {
+		u8 balanceMode;
+		u8 redGain;
+		u8 greenGain;
+		u8 blueGain;
+	} colourBalance;
+	struct {
+		u8 divisor;
+		u8 baserate;
+	} sensorFps;
+	struct {
+		u8 gain1;
+		u8 gain2;
+		u8 gain4;
+		u8 gain8;
+	} apcor;
+	struct {
+		u8 flickerMode;
+		u8 coarseJump;
+		u8 allowableOverExposure;
+	} flickerControl;
+	struct {
+		u8 gain1;
+		u8 gain2;
+		u8 gain4;
+		u8 gain8;
+	} vlOffset;
+	struct {
+		u8 mode;
+		u8 decimation;
+	} compression;
+	struct {
+		u8 frTargeting;
+		u8 targetFR;
+		u8 targetQ;
+	} compressionTarget;
+	struct {
+		u8 yThreshold;
+		u8 uvThreshold;
+	} yuvThreshold;
+	struct {
+		u8 hysteresis;
+		u8 threshMax;
+		u8 smallStep;
+		u8 largeStep;
+		u8 decimationHysteresis;
+		u8 frDiffStepThresh;
+		u8 qDiffStepThresh;
+		u8 decimationThreshMod;
+	} compressionParams;
+	struct {
+		u8 videoSize;		/* CIF/QCIF */
+		u8 subSample;
+		u8 yuvOrder;
+	} format;
+	struct {
+		u8 colStart;		/* skip first 8*colStart pixels */
+		u8 colEnd;		/* finish at 8*colEnd pixels */
+		u8 rowStart;		/* skip first 4*rowStart lines */
+		u8 rowEnd;		/* finish at 4*rowEnd lines */
+	} roi;
+	u8 ecpTiming;
+	u8 streamStartLine;
+};
+
+enum v4l_camstates {
+	CPIA_V4L_IDLE = 0,
+	CPIA_V4L_ERROR,
+	CPIA_V4L_COMMAND,
+	CPIA_V4L_GRABBING,
+	CPIA_V4L_STREAMING,
+	CPIA_V4L_STREAMING_PAUSED,
+};
+
+#define FRAME_NUM	2	/* double buffering for now */
+
+struct cam_data {
+  struct cam_data **previous;
+  struct cam_data *next;
+
+        struct semaphore busy_lock;     /* guard against SMP multithreading */
+	struct cpia_camera_ops *ops;	/* lowlevel driver operations */
+	void *lowlevel_data;		/* private data for lowlevel driver */
+	u8 *raw_image;			/* buffer for raw image data */
+	struct cpia_frame decompressed_frame;
+                                        /* buffer to hold decompressed frame */
+	int image_size;		        /* sizeof last decompressed image */
+	int open_count;			/* # of process that have camera open */
+
+				/* camera status */
+	int fps;			/* actual fps reported by the camera */
+	int transfer_rate;		/* transfer rate from camera in kB/s */
+	u8 mainsFreq;			/* for flicker control */
+
+				/* proc interface */
+	struct semaphore param_lock;	/* params lock for this camera */
+	struct cam_params params;	/* camera settings */
+	struct proc_dir_entry *proc_entry;	/* /proc/cpia/videoX */
+	
+					/* v4l */
+	int video_size;			/* VIDEO_SIZE_ */
+	volatile enum v4l_camstates camstate;	/* v4l layer status */
+	struct video_device vdev;	/* v4l videodev */
+	struct video_picture vp;	/* v4l camera settings */
+	struct video_window vw;		/* v4l capture area */
+
+				/* mmap interface */
+	int curframe;			/* the current frame to grab into */
+	u8 *frame_buf;			/* frame buffer data */
+        struct cpia_frame frame[FRAME_NUM];
+				/* FRAME_NUM-buffering, so we need a array */
+
+	int first_frame;
+	int mmap_kludge;		/* 'wrong' byte order for mmap */
+	volatile u32 cmd_queue;		/* queued commands */
+};
+
 /* cpia_register_camera is called by low level driver for each camera.
  * A unique camera number is returned, or a negative value on error */
-int cpia_register_camera(struct cpia_camera_ops *ops);
+struct cam_data *cpia_register_camera(struct cpia_camera_ops *ops, void *lowlevel);
 
 /* cpia_unregister_camera is called by low level driver when a camera
  * is removed.  This must not fail. */
-void cpia_unregister_camera(int camnr);
+void cpia_unregister_camera(struct cam_data *cam);
 
 #define CPIA_MAXCAMS		4
 
@@ -134,7 +326,6 @@
 #define DECIMATION_ENAB	1
 #define EOI		0xff	/* End Of Image */
 #define EOL		0xfd	/* End Of Line */
-#define FRAME_HEADER_SIZE	64
 
 /* Image grab modes */
 #define CPIA_GRAB_SINGLE	0
@@ -216,6 +407,38 @@
   DBG("%1d %1d %1d %1d %1d %1d %1d %1d \n",\
       (p)&0x80?1:0, (p)&0x40?1:0, (p)&0x20?1:0, (p)&0x10?1:0,\
         (p)&0x08?1:0, (p)&0x04?1:0, (p)&0x02?1:0, (p)&0x01?1:0);
+
+#define ADD_TO_LIST(l, drv) \
+  {\
+    lock_kernel();\
+    (drv)->next = l;\
+    (drv)->previous = &(l);\
+    (l) = drv;\
+    unlock_kernel();\
+  } while(0)
+
+#define REMOVE_FROM_LIST(drv) \
+  {\
+    if ((drv)->previous != NULL) {\
+      lock_kernel();\
+      if ((drv)->next != NULL)\
+        (drv)->next->previous = (drv)->previous;\
+      *((drv)->previous) = (drv)->next;\
+      (drv)->previous = NULL;\
+      (drv)->next = NULL;\
+      unlock_kernel();\
+    }\
+  } while (0)
+
+#define SEARCH_LIST(l, attr, i, ret) \
+  {\
+    lock_kernel();\
+    for (ret=l; ret != NULL && ret->lowlevel_data->attr != i; \
+	 ret = ret->next); /* empty body */ \
+    unlock_kernel();\
+  } while (0)
+  
+
 
 #endif /* __KERNEL__ */
 

--Multipart_Wed_Jan_26_22:09:03_2000-1
Content-Type: application/octet-stream
Content-Disposition: attachment; filename="cpia_usb.c"
Content-Transfer-Encoding: 7bit

#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/list.h>
#include <linux/spinlock.h>

#include "cpia.h"
#include "/usr/src/linux/drivers/usb/usb.h"

#define USB_REQ_CPIA_GRAB_FRAME			0xC1
#define USB_REQ_CPIA_UPLOAD_FRAME		0xC2
#define  WAIT_FOR_NEXT_FRAME			0
#define  FORCE_FRAME_UPLOAD			1

#define FRAMES_PER_DESC		10
#define FRAME_SIZE_PER_DESC	960	/* Shouldn't be hardcoded */
#define CPIA_NUMSBUF	2
#define STREAM_BUF_SIZE		(PAGE_SIZE * 4)
#define SCRATCH_BUF_SIZE	(STREAM_BUF_SIZE * 2)

struct cpia_sbuf {
	char *data;
	urb_t *urb;
};

#define FRAMEBUF_LEN (CPIA_MAX_FRAME_SIZE+100)
enum framebuf_status {
  FRAME_EMPTY,
  FRAME_READING,
  FRAME_READY,
};

struct framebuf 
{
  int length;
  enum framebuf_status status;
  u8 data[FRAMEBUF_LEN];
  struct framebuf *next;
};


struct usb_cpia {
  /* Device structure */
  struct usb_device *dev;

  unsigned char iface;
  wait_queue_head_t wq_stream;

  int cursbuf;		/* Current receiving sbuf */
  struct cpia_sbuf sbuf[CPIA_NUMSBUF];		/* Double buffering */
/*   u8 *buffer; */
/*   int buffer_length; */
/*   int buffer_maxlength; */

/*   unsigned char scratch[SCRATCH_BUF_SIZE]; */
/*   int scratchlen; */
  int streaming;
  struct framebuf *buffers[3];
  struct framebuf *curbuff, *workbuff;
};

static int cpia_usb_open(void *privdata);
static int cpia_usb_registerCallback(void *privdata, void (*cb) (void *cbdata),
			             void *cbdata);
static int cpia_usb_transferCmd(void *privdata, u8 *command, u8 *data);
static int cpia_usb_streamStart(void *privdata);
static int cpia_usb_streamStop(void *privdata);
static int cpia_usb_streamRead(void *privdata, u8 *frame, int noblock);
static int cpia_usb_close(void *privdata);

#define ABOUT "USB driver for Vision CPiA based cameras"

static struct cpia_camera_ops cpia_usb_ops = 
{
  cpia_usb_open,
  cpia_usb_registerCallback,
  cpia_usb_transferCmd,
  cpia_usb_streamStart,
  cpia_usb_streamStop,
  cpia_usb_streamRead,
  cpia_usb_close
};

static struct cam_data *cam_list;

static void cpia_usb_complete(struct urb *urb)
{
  int i;
  char *cdata;
  
  struct usb_cpia *cpia = (struct usb_cpia *) urb->context;

  if (cpia->workbuff->status == FRAME_EMPTY)
    {
      cpia->workbuff->status = FRAME_READING;
      cpia->workbuff->length = 0;
    }
  		  
  for (i = 0; i < urb->number_of_packets; i++) {
    int n = urb->iso_frame_desc[i].actual_length;
    int st = urb->iso_frame_desc[i].status;
	    
    cdata = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
	    
    if (st)
      printk(KERN_DEBUG "cpia data error: [%d] len=%d, status=%X\n",
	     i, n, st);

    if (FRAMEBUF_LEN < cpia->workbuff->length+n) {
      printk(KERN_DEBUG "cpia: scratch buf overflow!scr_len: %d, n: %d\n",cpia->workbuff->length, n );
      return;
    }
	    
    if (n) {
      if (cpia->workbuff->length >0 || 
	  (0x19 == cdata[0] && 0x68 == cdata[1]))
	{
	  memcpy(cpia->workbuff->data+cpia->workbuff->length, cdata, n);
	  cpia->workbuff->length += n;
	}
      else
	DBG ("Ignoring packet!\n");
    } else {
      if (cpia->workbuff->length > 4 &&
	  0xff == cpia->workbuff->data[cpia->workbuff->length-1] &&
	  0xff == cpia->workbuff->data[cpia->workbuff->length-2] &&
	  0xff == cpia->workbuff->data[cpia->workbuff->length-3] &&
	  0xff == cpia->workbuff->data[cpia->workbuff->length-4])
	{
	  cpia->workbuff->status = FRAME_READY;
	  cpia->curbuff = cpia->workbuff;
	  cpia->workbuff = cpia->workbuff->next;
	  cpia->workbuff->status = FRAME_EMPTY;
	  cpia->workbuff->length = 0;
		  
	  if (waitqueue_active(&cpia->wq_stream)) {
	    wake_up_interruptible(&cpia->wq_stream);
	  }
	}
    }
  }
}

static int cpia_usb_open(void *privdata)
{
  struct usb_cpia *cpia = (struct usb_cpia *) privdata;
  urb_t *urb;
  int fx, err;
  
  if (cpia == NULL)
    return -EINVAL;

	cpia->sbuf[0].data = kmalloc (FRAMES_PER_DESC * FRAME_SIZE_PER_DESC, GFP_KERNEL);
	if (!cpia->sbuf[0].data)
	  return -EINVAL;

	cpia->sbuf[1].data = kmalloc (FRAMES_PER_DESC * FRAME_SIZE_PER_DESC, GFP_KERNEL);
	if (!cpia->sbuf[1].data)
	  {
	    kfree (cpia->sbuf[0].data);
	    return -EINVAL;
	  }
	
  if (usb_set_interface(cpia->dev, cpia->iface, 3) < 0) {
    printk(KERN_ERR "usb_set_interface error\n");
    kfree (cpia->sbuf[0].data);
    kfree (cpia->sbuf[1].data);
    return -EBUSY;
  }

  cpia->buffers[0]->status = FRAME_EMPTY;
  cpia->buffers[0]->length = 0;
  cpia->buffers[1]->status = FRAME_EMPTY;
  cpia->buffers[1]->length = 0;
  cpia->buffers[2]->status = FRAME_EMPTY;
  cpia->buffers[2]->length = 0;
  cpia->curbuff = cpia->buffers[0];
  cpia->workbuff = cpia->buffers[1];
  

  /* We double buffer the Iso lists */
  //	err = usb_init_isoc(dev, usb_rcvisocpipe(dev, 1), FRAMES_PER_DESC, cpia, &cpia->sbuf[0].isodesc);
  urb = usb_alloc_urb(FRAMES_PER_DESC);
	
  if (!urb) {
    printk(KERN_ERR "cpia_init_isoc: usb_init_isoc ret %d\n",
	   0);
    kfree (cpia->sbuf[0].data);
    kfree (cpia->sbuf[1].data);
    return -ENOMEM;
  }
  cpia->sbuf[0].urb = urb;
  urb->dev = cpia->dev;
  urb->context = cpia;
  urb->pipe = usb_rcvisocpipe(cpia->dev, 1);
  urb->transfer_flags = USB_ISO_ASAP;
  urb->transfer_buffer = cpia->sbuf[0].data;
  urb->complete = cpia_usb_complete;
  urb->number_of_packets = FRAMES_PER_DESC;
  urb->transfer_buffer_length = FRAME_SIZE_PER_DESC * FRAMES_PER_DESC;
  for (fx = 0; fx < FRAMES_PER_DESC; fx++) {
    urb->iso_frame_desc[fx].offset = FRAME_SIZE_PER_DESC * fx;
    urb->iso_frame_desc[fx].length = FRAME_SIZE_PER_DESC;
  }
  urb = usb_alloc_urb(FRAMES_PER_DESC);
  if (!urb) {
    printk(KERN_ERR "cpia_init_isoc: usb_init_isoc ret %d\n",
	   0);
    kfree (cpia->sbuf[0].data);
    kfree (cpia->sbuf[1].data);
    return -ENOMEM;
  }
  cpia->sbuf[1].urb = urb;
  urb->dev = cpia->dev;
  urb->context = cpia;
  urb->pipe = usb_rcvisocpipe(cpia->dev, 1);
  urb->transfer_flags = USB_ISO_ASAP;
  urb->transfer_buffer = cpia->sbuf[1].data;
  urb->complete = cpia_usb_complete;
  urb->number_of_packets = FRAMES_PER_DESC;
  urb->transfer_buffer_length = FRAME_SIZE_PER_DESC * FRAMES_PER_DESC;
  for (fx = 0; fx < FRAMES_PER_DESC; fx++) {
    urb->iso_frame_desc[fx].offset = FRAME_SIZE_PER_DESC * fx;
    urb->iso_frame_desc[fx].length = FRAME_SIZE_PER_DESC;
  }

  cpia->sbuf[1].urb->next = cpia->sbuf[0].urb;
  cpia->sbuf[0].urb->next = cpia->sbuf[1].urb;
	
  err = usb_submit_urb(cpia->sbuf[0].urb);
  if (err)
    printk(KERN_ERR "cpia_init_isoc: usb_run_isoc(0) ret %d\n",
	   err);
  err = usb_submit_urb(cpia->sbuf[1].urb);
  if (err)
    printk(KERN_ERR "cpia_init_isoc: usb_run_isoc(1) ret %d\n",
	   err);

  cpia->streaming = 1;
  return 0;
}

//
// convenience functions
//

/****************************************************************************
 *
 *  WritePacket
 *
 ***************************************************************************/
static int WritePacket(struct usb_device *cam, const u8 *packet)
{
  if (packet == NULL) {
    return -EINVAL;
  }

  return usb_control_msg(cam, usb_sndctrlpipe(cam, 0),
			 packet[1] + (packet[0] << 8),
			 USB_TYPE_VENDOR | USB_RECIP_DEVICE,
			 packet[2] + (packet[3] << 8), 
			 packet[4] + (packet[5] << 8), NULL, 0, HZ);
}

/****************************************************************************
 *
 *  ReadPacket
 *
 ***************************************************************************/
static int ReadPacket(struct usb_device *cam, u8 *packet, u8 *buf, size_t size)
{
  if (packet == NULL || size <= 0) {
    return -EINVAL;
  }

  return usb_control_msg(cam, usb_rcvctrlpipe(cam, 0),
			 packet[1] + (packet[0] << 8),
			 USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
			 packet[2] + (packet[3] << 8), 
			 packet[4] + (packet[5] << 8), buf, size, HZ);
}


static int cpia_usb_transferCmd(void *privdata, u8 *command, u8 *data)
{
  int err=0;
  int databytes;
  struct usb_device *cam = ((struct usb_cpia*)privdata)->dev;
  if(cam == NULL) {
    DBG("Internal driver error: cam is NULL\n");
    return -EINVAL;
  }
  if(command == NULL) {
    DBG("Internal driver error: command is NULL\n");
    return -EINVAL;
  }
  databytes = (((int)command[7])<<8) | command[6];

  if(command[0] == DATA_IN) {
    u8 buffer[8];
    if(data == NULL) {
      DBG("Internal driver error: data is NULL\n");
      return -EINVAL;
    }
    err = ReadPacket(cam, command, buffer, 8);
    if(err < 0) {
      return err;
    }
    memcpy(data, buffer, databytes);
  } else if(command[0] == DATA_OUT) {
    WritePacket(cam, command);
  } else {
    DBG("Unexpected first byte of command: %x\n", command[0]);
    err = -EINVAL;
  }
  return 0;
}

static int cpia_usb_registerCallback(void *privdata, void (*cb) (void *cbdata),
  void *cbdata)
{
  return -ENODEV;
}

static int cpia_usb_streamStart(void *privdata)
{
  return -ENODEV;
}

static int cpia_usb_streamStop(void *privdata)
{
  return -ENODEV;
}

static int cpia_usb_streamRead(void *privdata, u8 *frame, int noblock)
{
  struct usb_cpia *cpia = (struct usb_cpia *) privdata;
  struct framebuf *mybuff;
  
  if (cpia->curbuff->status != FRAME_READY) {
    interruptible_sleep_on(&cpia->wq_stream);
  } else
    DBG ("Frame already waiting!\n");

  mybuff = cpia->curbuff;
  
  if (mybuff->status != FRAME_READY || mybuff->length < 4) {
    DBG ("Something went wrong!\n");
    return -1;
  }

  memcpy (frame, mybuff->data, mybuff->length);
  mybuff->status = FRAME_EMPTY;
  
/*   DBG("read done, %d bytes, Header: %x/%x, Footer: %x%x%x%x\n",  */
/*       mybuff->length, frame[0], frame[1], */
/*       frame[mybuff->length-4], frame[mybuff->length-3],  */
/*       frame[mybuff->length-2], frame[mybuff->length-1]); */

  return mybuff->length;
}

static int cpia_usb_close(void *privdata)
{
  struct usb_cpia *cpia = (struct usb_cpia *) privdata;
  if (!cpia->streaming)
    return 0;

  /* Set packet size to 0 */
  if (usb_set_interface(cpia->dev, cpia->iface, 0) < 0) {
    printk(KERN_ERR "usb_set_interface error\n");
    return -EINVAL;
  }

  /* Unschedule all of the iso td's */
  usb_unlink_urb(cpia->sbuf[1].urb);
  usb_unlink_urb(cpia->sbuf[0].urb);

  cpia->streaming = 0;

  /* Delete them all */
  usb_free_urb(cpia->sbuf[1].urb);
  usb_free_urb(cpia->sbuf[0].urb);
  return 0;
}

int cpia_usb_init(void)
{
  /*   return -ENODEV; */
  return 0;
}

//
// Probing and initializing
//

static void * cpia_probe(struct usb_device *dev, unsigned int ifnum)
{
  struct usb_interface_descriptor *interface;
  struct usb_cpia *cpia;
  struct cam_data *cam;
  
  /* We don't handle multi-config cameras */
  if (dev->descriptor.bNumConfigurations != 1)
    return NULL;

  interface = &dev->actconfig->interface[ifnum].altsetting[0];

  /* Is it a CPiA? */
  if (dev->descriptor.idVendor != 0x0553)
    return NULL;
  if (dev->descriptor.idProduct != 0x0002)
    return NULL;

  /* Checking vendor/product should be enough, but what the hell */
  if (interface->bInterfaceClass != 0xFF)
    return NULL;
  if (interface->bInterfaceSubClass != 0x00)
    return NULL;

  /* We found a CPiA */
  printk(KERN_INFO "USB CPiA camera found\n");

  if ((cpia = kmalloc(sizeof(*cpia), GFP_KERNEL)) == NULL) {
    printk(KERN_ERR "couldn't kmalloc cpia struct\n");
    return NULL;
  }

  memset(cpia, 0, sizeof(*cpia));

  cpia->dev = dev;
  cpia->iface = interface->bInterfaceNumber;
  init_waitqueue_head(&cpia->wq_stream);

  cpia->buffers[0] = vmalloc(sizeof(struct framebuf));
  cpia->buffers[1] = vmalloc(sizeof(struct framebuf));
  cpia->buffers[2] = vmalloc(sizeof(struct framebuf));
  if (cpia->buffers[0] == NULL ||
      cpia->buffers[1] == NULL ||
      cpia->buffers[2] == NULL)
    {
      printk(KERN_ERR "couldn't kmalloc frame buffers\n");
      return NULL;
    }
      
  cpia->buffers[0]->next = cpia->buffers[1];
  cpia->buffers[1]->next = cpia->buffers[2];
  cpia->buffers[2]->next = cpia->buffers[0];
  
  if (usb_set_interface(dev, interface->bInterfaceNumber, 0) < 0) {
    printk(KERN_ERR "usb_set_interface error\n");
    return NULL;
  }

  if((cam=cpia_register_camera(&cpia_usb_ops, cpia))==NULL) {
    LOG("failed to cpia_register_camera\n");
    return NULL;
  }
  ADD_TO_LIST(cam_list, cam);
  
  return cam;
}

static void cpia_disconnect(struct usb_device *dev, void *ptr);

static struct usb_driver cpia_driver = {
  "cpia",
  cpia_probe,
  cpia_disconnect,
  { NULL, NULL }
};

/* don't use dev, it may be NULL! (see usb_cpia_cleanup) */
static void cpia_disconnect(struct usb_device *dev, void *ptr)
{
  struct cam_data *cpia = (struct cam_data *) ptr;
  struct usb_cpia *cam = (struct usb_cpia *) cpia->lowlevel_data;
  
  REMOVE_FROM_LIST(cpia);

  cpia_unregister_camera(cpia);

  usb_driver_release_interface(&cpia_driver,
			       &cam->dev->actconfig->interface[0]);

  /* Free the memory */
  kfree(cam);
}

int usb_cpia_init(void)
{
  cam_list = NULL;
  return usb_register(&cpia_driver);
}

void usb_cpia_cleanup(void)
{
  struct cam_data *cam;
  while((cam = cam_list) != NULL)
    cpia_disconnect(NULL, cam);
  usb_deregister(&cpia_driver);
}

#ifdef MODULE
int init_module(void)
{
  return usb_cpia_init();
}

void cleanup_module(void)
{
  usb_cpia_cleanup();
}

#else /* !MODULE */

__initfunc(void cpia_usb_setup(char *str, int *ints))
{
}

#endif /* !MODULE */

--Multipart_Wed_Jan_26_22:09:03_2000-1--