Commit e631dac6 authored by Gerd Knorr's avatar Gerd Knorr Committed by Linus Torvalds

[PATCH] v4l: pwc webcam update

This patch adapts the philips webcam driver to the videodev changes.
Also has the unplug race fix.
parent 8721ad19
...@@ -1084,62 +1084,52 @@ int pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg) ...@@ -1084,62 +1084,52 @@ int pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg)
case VIDIOCPWCSCQUAL: case VIDIOCPWCSCQUAL:
{ {
int qual, ret; int *qual = arg;
int ret;
if (copy_from_user(&qual, arg, sizeof(int))) if (*qual < 0 || *qual > 3)
return -EFAULT;
if (qual < 0 || qual > 3)
return -EINVAL; return -EINVAL;
ret = pwc_try_video_mode(pdev, pdev->view.x, pdev->view.y, pdev->vframes, qual, pdev->vsnapshot); ret = pwc_try_video_mode(pdev, pdev->view.x, pdev->view.y, pdev->vframes, *qual, pdev->vsnapshot);
if (ret < 0) if (ret < 0)
return ret; return ret;
pdev->vcompression = qual; pdev->vcompression = *qual;
break; break;
} }
case VIDIOCPWCGCQUAL: case VIDIOCPWCGCQUAL:
{ {
if (copy_to_user(arg, &pdev->vcompression, sizeof(int))) int *qual = arg;
return -EFAULT;
*qual = pdev->vcompression;
break; break;
} }
case VIDIOCPWCSAGC: case VIDIOCPWCSAGC:
{ {
int agc; int *agc = arg;
if (copy_from_user(&agc, arg, sizeof(agc))) if (pwc_set_agc(pdev, *agc < 0 ? 1 : 0, *agc))
return -EFAULT; return -EINVAL;
else {
if (pwc_set_agc(pdev, agc < 0 ? 1 : 0, agc))
return -EINVAL;
}
break; break;
} }
case VIDIOCPWCGAGC: case VIDIOCPWCGAGC:
{ {
int agc; int *agc = arg;
if (pwc_get_agc(pdev, &agc)) if (pwc_get_agc(pdev, agc))
return -EINVAL; return -EINVAL;
if (copy_to_user(arg, &agc, sizeof(agc)))
return -EFAULT;
break; break;
} }
case VIDIOCPWCSSHUTTER: case VIDIOCPWCSSHUTTER:
{ {
int shutter_speed, ret; int *shutter_speed = arg;
int ret;
if (copy_from_user(&shutter_speed, arg, sizeof(shutter_speed))) ret = pwc_set_shutter_speed(pdev, *shutter_speed < 0 ? 1 : 0, *shutter_speed);
return -EFAULT; if (ret < 0)
else { return ret;
ret = pwc_set_shutter_speed(pdev, shutter_speed < 0 ? 1 : 0, shutter_speed);
if (ret < 0)
return ret;
}
break; break;
} }
...@@ -1149,48 +1139,40 @@ int pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg) ...@@ -1149,48 +1139,40 @@ int pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg)
case VIDIOCPWCSAWB: case VIDIOCPWCSAWB:
{ {
struct pwc_whitebalance wb; struct pwc_whitebalance *wb = arg;
int ret; int ret;
if (copy_from_user(&wb, arg, sizeof(wb))) ret = pwc_set_awb(pdev, wb->mode);
return -EFAULT; if (ret >= 0 && wb->mode == PWC_WB_MANUAL) {
pwc_set_red_gain(pdev, wb->manual_red);
ret = pwc_set_awb(pdev, wb.mode); pwc_set_blue_gain(pdev, wb->manual_blue);
if (ret >= 0 && wb.mode == PWC_WB_MANUAL) {
pwc_set_red_gain(pdev, wb.manual_red);
pwc_set_blue_gain(pdev, wb.manual_blue);
} }
break; break;
} }
case VIDIOCPWCGAWB: case VIDIOCPWCGAWB:
{ {
struct pwc_whitebalance wb; struct pwc_whitebalance *wb = arg;
memset(&wb, 0, sizeof(wb)); memset(wb, 0, sizeof(*wb));
wb.mode = pwc_get_awb(pdev); wb->mode = pwc_get_awb(pdev);
if (wb.mode < 0) if (wb->mode < 0)
return -EINVAL; return -EINVAL;
wb.manual_red = pwc_get_red_gain(pdev); wb->manual_red = pwc_get_red_gain(pdev);
wb.manual_blue = pwc_get_blue_gain(pdev); wb->manual_blue = pwc_get_blue_gain(pdev);
if (wb.mode == PWC_WB_AUTO) { if (wb->mode == PWC_WB_AUTO) {
wb.read_red = pwc_read_red_gain(pdev); wb->read_red = pwc_read_red_gain(pdev);
wb.read_blue = pwc_read_blue_gain(pdev); wb->read_blue = pwc_read_blue_gain(pdev);
} }
if (copy_to_user(arg, &wb, sizeof(wb)))
return -EFAULT;
break; break;
} }
case VIDIOCPWCSLED: case VIDIOCPWCSLED:
{ {
int ret; int ret;
struct pwc_leds leds; struct pwc_leds *leds = arg;
if (copy_from_user(&leds, arg, sizeof(leds)))
return -EFAULT;
ret = pwc_set_leds(pdev, leds.led_on, leds.led_off); ret = pwc_set_leds(pdev, leds->led_on, leds->led_off);
if (ret<0) if (ret<0)
return ret; return ret;
break; break;
...@@ -1201,13 +1183,11 @@ int pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg) ...@@ -1201,13 +1183,11 @@ int pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg)
case VIDIOCPWCGLED: case VIDIOCPWCGLED:
{ {
int led; int led;
struct pwc_leds leds; struct pwc_leds *leds = arg;
led = pwc_get_leds(pdev, &leds.led_on, &leds.led_off); led = pwc_get_leds(pdev, &leds->led_on, &leds->led_off);
if (led < 0) if (led < 0)
return -EINVAL; return -EINVAL;
if (copy_to_user(arg, &leds, sizeof(leds)))
return -EFAULT;
break; break;
} }
......
...@@ -120,28 +120,32 @@ static void *mem_leak = NULL; /* For delayed kfree()s. See below */ ...@@ -120,28 +120,32 @@ static void *mem_leak = NULL; /* For delayed kfree()s. See below */
/***/ /***/
static int pwc_video_open(struct video_device *vdev, int mode); static int pwc_video_open(struct inode *inode, struct file *file);
static void pwc_video_close(struct video_device *vdev); static int pwc_video_close(struct inode *inode, struct file *file);
static long pwc_video_read(struct video_device *vdev, char *buf, unsigned long count, int noblock); static int pwc_video_read(struct file *file, char *buf,
static long pwc_video_write(struct video_device *vdev, const char *buf, unsigned long count, int noblock); size_t count, loff_t *ppos);
static unsigned int pwc_video_poll(struct video_device *vdev, struct file *file, poll_table *wait); static unsigned int pwc_video_poll(struct file *file, poll_table *wait);
static int pwc_video_ioctl(struct video_device *vdev, unsigned int cmd, void *arg); static int pwc_video_ioctl(struct inode *inode, struct file *file,
static int pwc_video_mmap(struct vm_area_struct *vma, struct video_device *dev, const char *adr, unsigned long size); unsigned int ioctlnr, void *arg);
static int pwc_video_mmap(struct file *file, struct vm_area_struct *vma);
static struct video_device pwc_template = {
static struct file_operations pwc_fops = {
owner: THIS_MODULE, owner: THIS_MODULE,
name: "Philips Webcam", /* Filled in later */
type: VID_TYPE_CAPTURE,
hardware: VID_HARDWARE_PWC,
open: pwc_video_open, open: pwc_video_open,
close: pwc_video_close, release: pwc_video_close,
read: pwc_video_read, read: pwc_video_read,
write: pwc_video_write,
poll: pwc_video_poll, poll: pwc_video_poll,
ioctl: pwc_video_ioctl,
mmap: pwc_video_mmap, mmap: pwc_video_mmap,
initialize: NULL, /* initialize */ ioctl: video_generic_ioctl,
minor: 0 /* minor */ llseek: no_llseek,
};
static struct video_device pwc_template = {
owner: THIS_MODULE,
name: "Philips Webcam", /* Filled in later */
type: VID_TYPE_CAPTURE,
hardware: VID_HARDWARE_PWC,
fops: &pwc_fops,
kernel_ioctl: pwc_video_ioctl,
}; };
/***************************************************************************/ /***************************************************************************/
...@@ -909,18 +913,19 @@ static inline void free_mem_leak(void) ...@@ -909,18 +913,19 @@ static inline void free_mem_leak(void)
/***************************************************************************/ /***************************************************************************/
/* Video4Linux functions */ /* Video4Linux functions */
static int pwc_video_open(struct video_device *vdev, int mode) static int pwc_video_open(struct inode *inode, struct file *file)
{ {
int i; int i;
struct video_device *vdev = video_devdata(file);
struct pwc_device *pdev; struct pwc_device *pdev;
Trace(TRACE_OPEN, "video_open called(0x%p, 0%o).\n", vdev, mode); Trace(TRACE_OPEN, "video_open called(0x%p).\n", vdev);
if (vdev == NULL)
BUG();
pdev = (struct pwc_device *)vdev->priv; pdev = (struct pwc_device *)vdev->priv;
if (pdev == NULL) if (pdev == NULL)
BUG(); BUG();
if (pdev->vopen)
return -EBUSY;
down(&pdev->modlock); down(&pdev->modlock);
if (!pdev->usb_init) { if (!pdev->usb_init) {
...@@ -1008,6 +1013,7 @@ static int pwc_video_open(struct video_device *vdev, int mode) ...@@ -1008,6 +1013,7 @@ static int pwc_video_open(struct video_device *vdev, int mode)
} }
pdev->vopen++; pdev->vopen++;
file->private_data = vdev;
/* lock decompressor; this has a small race condition, since we /* lock decompressor; this has a small race condition, since we
could in theory unload pwcx.o between pwc_find_decompressor() could in theory unload pwcx.o between pwc_find_decompressor()
above and this call. I doubt it's ever going to be a problem. above and this call. I doubt it's ever going to be a problem.
...@@ -1020,8 +1026,9 @@ static int pwc_video_open(struct video_device *vdev, int mode) ...@@ -1020,8 +1026,9 @@ static int pwc_video_open(struct video_device *vdev, int mode)
} }
/* Note that all cleanup is done in the reverse order as in _open */ /* Note that all cleanup is done in the reverse order as in _open */
static void pwc_video_close(struct video_device *vdev) static int pwc_video_close(struct inode *inode, struct file *file)
{ {
struct video_device *vdev = file->private_data;
struct pwc_device *pdev; struct pwc_device *pdev;
int i; int i;
...@@ -1041,13 +1048,7 @@ static void pwc_video_close(struct video_device *vdev) ...@@ -1041,13 +1048,7 @@ static void pwc_video_close(struct video_device *vdev)
if (pdev->vframe_count > 20) 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); Info("Closing video device: %d frames received, dumped %d frames, %d frames with errors.\n", pdev->vframe_count, pdev->vframes_dumped, pdev->vframes_error);
if (pdev->unplugged) { if (!pdev->unplugged) {
/* The device was unplugged or some other error occured */
/* We unregister the video_device */
Trace(TRACE_OPEN, "Delayed video device unregistered.\n");
video_unregister_device(pdev->vdev);
}
else {
/* Normal close: stop isochronuous and interrupt endpoint */ /* Normal close: stop isochronuous and interrupt endpoint */
Trace(TRACE_OPEN, "Normal close(): setting interface to 0.\n"); Trace(TRACE_OPEN, "Normal close(): setting interface to 0.\n");
usb_set_interface(pdev->udev, 0, 0); usb_set_interface(pdev->udev, 0, 0);
...@@ -1073,6 +1074,8 @@ static void pwc_video_close(struct video_device *vdev) ...@@ -1073,6 +1074,8 @@ static void pwc_video_close(struct video_device *vdev)
/* wake up _disconnect() routine */ /* wake up _disconnect() routine */
if (pdev->unplugged) if (pdev->unplugged)
wake_up(&pdev->remove_ok); wake_up(&pdev->remove_ok);
file->private_data = NULL;
return 0;
} }
/* /*
...@@ -1087,12 +1090,15 @@ static void pwc_video_close(struct video_device *vdev) ...@@ -1087,12 +1090,15 @@ static void pwc_video_close(struct video_device *vdev)
device is tricky anyhow. device is tricky anyhow.
*/ */
static long pwc_video_read(struct video_device *vdev, char *buf, unsigned long count, int noblock) static int pwc_video_read(struct file *file, char *buf,
size_t count, loff_t *ppos)
{ {
struct video_device *vdev = file->private_data;
struct pwc_device *pdev; struct pwc_device *pdev;
int noblock = file->f_flags & O_NONBLOCK;
DECLARE_WAITQUEUE(wait, current); DECLARE_WAITQUEUE(wait, current);
Trace(TRACE_READ, "video_read(0x%p, %p, %ld, %d) called.\n", vdev, buf, count, noblock); Trace(TRACE_READ, "video_read(0x%p, %p, %d) called.\n", vdev, buf, count);
if (vdev == NULL) if (vdev == NULL)
return -EFAULT; return -EFAULT;
pdev = vdev->priv; pdev = vdev->priv;
...@@ -1143,14 +1149,9 @@ static long pwc_video_read(struct video_device *vdev, char *buf, unsigned long c ...@@ -1143,14 +1149,9 @@ static long pwc_video_read(struct video_device *vdev, char *buf, unsigned long c
return count; return count;
} }
static unsigned int pwc_video_poll(struct file *file, poll_table *wait)
static long pwc_video_write(struct video_device *vdev, const char *buf, unsigned long count, int noblock)
{
return -EINVAL;
}
static unsigned int pwc_video_poll(struct video_device *vdev, struct file *file, poll_table *wait)
{ {
struct video_device *vdev = file->private_data;
struct pwc_device *pdev; struct pwc_device *pdev;
if (vdev == NULL) if (vdev == NULL)
...@@ -1170,8 +1171,10 @@ static unsigned int pwc_video_poll(struct video_device *vdev, struct file *file, ...@@ -1170,8 +1171,10 @@ static unsigned int pwc_video_poll(struct video_device *vdev, struct file *file,
return 0; return 0;
} }
static int pwc_video_ioctl(struct video_device *vdev, unsigned int cmd, void *arg) static int pwc_video_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, void *arg)
{ {
struct video_device *vdev = file->private_data;
struct pwc_device *pdev; struct pwc_device *pdev;
DECLARE_WAITQUEUE(wait, current); DECLARE_WAITQUEUE(wait, current);
...@@ -1185,39 +1188,30 @@ static int pwc_video_ioctl(struct video_device *vdev, unsigned int cmd, void *ar ...@@ -1185,39 +1188,30 @@ static int pwc_video_ioctl(struct video_device *vdev, unsigned int cmd, void *ar
/* Query cabapilities */ /* Query cabapilities */
case VIDIOCGCAP: case VIDIOCGCAP:
{ {
struct video_capability caps; struct video_capability *caps = arg;
strcpy(caps.name, vdev->name); strcpy(caps->name, vdev->name);
caps.type = VID_TYPE_CAPTURE; caps->type = VID_TYPE_CAPTURE;
caps.channels = 1; caps->channels = 1;
caps.audios = 1; caps->audios = 1;
caps.minwidth = pdev->view_min.x; caps->minwidth = pdev->view_min.x;
caps.minheight = pdev->view_min.y; caps->minheight = pdev->view_min.y;
caps.maxwidth = pdev->view_max.x; caps->maxwidth = pdev->view_max.x;
caps.maxheight = pdev->view_max.y; caps->maxheight = pdev->view_max.y;
if (copy_to_user(arg, &caps, sizeof(caps)))
return -EFAULT;
break; break;
} }
/* Channel functions (simulate 1 channel) */ /* Channel functions (simulate 1 channel) */
case VIDIOCGCHAN: case VIDIOCGCHAN:
{ {
struct video_channel v; struct video_channel *v = arg;
if (copy_from_user(&v, arg, sizeof(v))) if (v->channel != 0)
return -EFAULT;
if (v.channel != 0)
return -EINVAL; return -EINVAL;
v->flags = 0;
v.flags = 0; v->tuners = 0;
v.tuners = 0; v->type = VIDEO_TYPE_CAMERA;
v.type = VIDEO_TYPE_CAMERA; strcpy(v->name, "Webcam");
strcpy(v.name, "Webcam");
if (copy_to_user(arg, &v, sizeof(v)))
return -EFAULT;
return 0; return 0;
} }
...@@ -1227,14 +1221,9 @@ static int pwc_video_ioctl(struct video_device *vdev, unsigned int cmd, void *ar ...@@ -1227,14 +1221,9 @@ static int pwc_video_ioctl(struct video_device *vdev, unsigned int cmd, void *ar
the bttv driver uses a video_channel arg, which the bttv driver uses a video_channel arg, which
makes sense becasue it also has the norm flag. makes sense becasue it also has the norm flag.
*/ */
struct video_channel v; struct video_channel *v = arg;
if (v->channel != 0)
if (copy_from_user(&v, arg, sizeof(v)))
return -EFAULT;
if (v.channel != 0)
return -EINVAL; return -EINVAL;
return 0; return 0;
} }
...@@ -1242,48 +1231,41 @@ static int pwc_video_ioctl(struct video_device *vdev, unsigned int cmd, void *ar ...@@ -1242,48 +1231,41 @@ static int pwc_video_ioctl(struct video_device *vdev, unsigned int cmd, void *ar
/* Picture functions; contrast etc. */ /* Picture functions; contrast etc. */
case VIDIOCGPICT: case VIDIOCGPICT:
{ {
struct video_picture p; struct video_picture *p = arg;
int val; int val;
p.colour = 0x8000; p->colour = 0x8000;
p.hue = 0x8000; p->hue = 0x8000;
val = pwc_get_brightness(pdev); val = pwc_get_brightness(pdev);
if (val >= 0) if (val >= 0)
p.brightness = val; p->brightness = val;
else else
p.brightness = 0xffff; p->brightness = 0xffff;
val = pwc_get_contrast(pdev); val = pwc_get_contrast(pdev);
if (val >= 0) if (val >= 0)
p.contrast = val; p->contrast = val;
else else
p.contrast = 0xffff; p->contrast = 0xffff;
/* Gamma, Whiteness, what's the difference? :) */ /* Gamma, Whiteness, what's the difference? :) */
val = pwc_get_gamma(pdev); val = pwc_get_gamma(pdev);
if (val >= 0) if (val >= 0)
p.whiteness = val; p->whiteness = val;
else else
p.whiteness = 0xffff; p->whiteness = 0xffff;
val = pwc_get_saturation(pdev); val = pwc_get_saturation(pdev);
if (val >= 0) if (val >= 0)
p.colour = val; p->colour = val;
else else
p.colour = 0xffff; p->colour = 0xffff;
p.depth = 24; p->depth = 24;
p.palette = pdev->vpalette; p->palette = pdev->vpalette;
p.hue = 0xFFFF; /* N/A */ p->hue = 0xFFFF; /* N/A */
if (copy_to_user(arg, &p, sizeof(p)))
return -EFAULT;
break; break;
} }
case VIDIOCSPICT: case VIDIOCSPICT:
{ {
struct video_picture p; struct video_picture *p = arg;
if (copy_from_user(&p, arg, sizeof(p)))
return -EFAULT;
/* /*
* FIXME: Suppose we are mid read * FIXME: Suppose we are mid read
ANSWER: No problem: the firmware of the camera ANSWER: No problem: the firmware of the camera
...@@ -1292,50 +1274,44 @@ static int pwc_video_ioctl(struct video_device *vdev, unsigned int cmd, void *ar ...@@ -1292,50 +1274,44 @@ static int pwc_video_ioctl(struct video_device *vdev, unsigned int cmd, void *ar
is used exactly once in the uncompress is used exactly once in the uncompress
routine. routine.
*/ */
pwc_set_brightness(pdev, p.brightness); if (p->palette && p->palette != pdev->vpalette) {
pwc_set_contrast(pdev, p.contrast); if (pwc_set_palette(pdev, p->palette) < 0)
pwc_set_gamma(pdev, p.whiteness);
pwc_set_saturation(pdev, p.colour);
if (p.palette && p.palette != pdev->vpalette) {
if (pwc_set_palette(pdev, p.palette) < 0)
return -EINVAL; return -EINVAL;
} }
pwc_set_brightness(pdev, p->brightness);
pwc_set_contrast(pdev, p->contrast);
pwc_set_gamma(pdev, p->whiteness);
pwc_set_saturation(pdev, p->colour);
break; break;
} }
/* Window/size parameters */ /* Window/size parameters */
case VIDIOCGWIN: case VIDIOCGWIN:
{ {
struct video_window vw; struct video_window *vw = arg;
vw.x = 0; vw->x = 0;
vw.y = 0; vw->y = 0;
vw.width = pdev->view.x; vw->width = pdev->view.x;
vw.height = pdev->view.y; vw->height = pdev->view.y;
vw.chromakey = 0; vw->chromakey = 0;
vw.flags = (pdev->vframes << PWC_FPS_SHIFT) | vw->flags = (pdev->vframes << PWC_FPS_SHIFT) |
(pdev->vsnapshot ? PWC_FPS_SNAPSHOT : 0); (pdev->vsnapshot ? PWC_FPS_SNAPSHOT : 0);
if (copy_to_user(arg, &vw, sizeof(vw)))
return -EFAULT;
break; break;
} }
case VIDIOCSWIN: case VIDIOCSWIN:
{ {
struct video_window vw; struct video_window *vw = arg;
int fps, snapshot, ret; int fps, snapshot, ret;
if (copy_from_user(&vw, arg, sizeof(vw)))
return -EFAULT;
fps = (vw.flags & PWC_FPS_FRMASK) >> PWC_FPS_SHIFT; fps = (vw->flags & PWC_FPS_FRMASK) >> PWC_FPS_SHIFT;
snapshot = vw.flags & PWC_FPS_SNAPSHOT; snapshot = vw->flags & PWC_FPS_SNAPSHOT;
if (fps == 0) if (fps == 0)
fps = pdev->vframes; fps = pdev->vframes;
if (pdev->view.x == vw.width && pdev->view.y && fps == pdev->vframes && snapshot == pdev->vsnapshot) if (pdev->view.x == vw->width && pdev->view.y && fps == pdev->vframes && snapshot == pdev->vsnapshot)
return 0; return 0;
ret = pwc_try_video_mode(pdev, vw.width, vw.height, fps, pdev->vcompression, snapshot); ret = pwc_try_video_mode(pdev, vw->width, vw->height, fps, pdev->vcompression, snapshot);
if (ret) if (ret)
return ret; return ret;
break; break;
...@@ -1344,16 +1320,9 @@ static int pwc_video_ioctl(struct video_device *vdev, unsigned int cmd, void *ar ...@@ -1344,16 +1320,9 @@ static int pwc_video_ioctl(struct video_device *vdev, unsigned int cmd, void *ar
/* We don't have overlay support (yet) */ /* We don't have overlay support (yet) */
case VIDIOCGFBUF: case VIDIOCGFBUF:
{ {
struct video_buffer vb; struct video_buffer *vb = arg;
vb.base = NULL; memset(vb,0,sizeof(*vb));
vb.height = 0;
vb.width = 0;
vb.depth = 0;
vb.bytesperline = 0;
if (copy_to_user((void *)arg, (void *)&vb, sizeof(vb)))
return -EFAULT;
break; break;
} }
...@@ -1361,29 +1330,24 @@ static int pwc_video_ioctl(struct video_device *vdev, unsigned int cmd, void *ar ...@@ -1361,29 +1330,24 @@ static int pwc_video_ioctl(struct video_device *vdev, unsigned int cmd, void *ar
case VIDIOCGMBUF: case VIDIOCGMBUF:
{ {
/* Tell the user program how much memory is needed for a mmap() */ /* Tell the user program how much memory is needed for a mmap() */
struct video_mbuf vm; struct video_mbuf *vm = arg;
int i; int i;
memset(&vm, 0, sizeof(vm)); memset(vm, 0, sizeof(*vm));
vm.size = default_mbufs * pdev->len_per_image; vm->size = default_mbufs * pdev->len_per_image;
vm.frames = default_mbufs; /* double buffering should be enough for most applications */ vm->frames = default_mbufs; /* double buffering should be enough for most applications */
for (i = 0; i < default_mbufs; i++) for (i = 0; i < default_mbufs; i++)
vm.offsets[i] = i * pdev->len_per_image; vm->offsets[i] = i * pdev->len_per_image;
if (copy_to_user((void *)arg, (void *)&vm, sizeof(vm)))
return -EFAULT;
break; break;
} }
case VIDIOCMCAPTURE: case VIDIOCMCAPTURE:
{ {
/* Start capture into a given image buffer (called 'frame' in video_mmap structure) */ /* Start capture into a given image buffer (called 'frame' in video_mmap structure) */
struct video_mmap vm; struct video_mmap *vm = arg;
if (copy_from_user((void *)&vm, (void *)arg, sizeof(vm))) Trace(TRACE_READ, "VIDIOCMCAPTURE: %dx%d, frame %d, format %d\n", vm->width, vm->height, vm->frame, vm->format);
return -EFAULT; if (vm->frame < 0 || vm->frame >= default_mbufs)
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; return -EINVAL;
/* xawtv is nasty. It probes the available palettes /* xawtv is nasty. It probes the available palettes
...@@ -1391,24 +1355,24 @@ static int pwc_video_ioctl(struct video_device *vdev, unsigned int cmd, void *ar ...@@ -1391,24 +1355,24 @@ static int pwc_video_ioctl(struct video_device *vdev, unsigned int cmd, void *ar
various palettes... The driver doesn't support various palettes... The driver doesn't support
such small images, so I'm working around it. such small images, so I'm working around it.
*/ */
if (vm.format && vm.format != pdev->vpalette) if (vm->format && vm->format != pdev->vpalette)
if (pwc_set_palette(pdev, vm.format) < 0) if (pwc_set_palette(pdev, vm->format) < 0)
return -EINVAL; return -EINVAL;
if ((vm.width != pdev->view.x || vm.height != pdev->view.y) && if ((vm->width != pdev->view.x || vm->height != pdev->view.y) &&
(vm.width >= pdev->view_min.x && vm.height >= pdev->view_min.y)) { (vm->width >= pdev->view_min.x && vm->height >= pdev->view_min.y)) {
int ret; int ret;
Trace(TRACE_OPEN, "VIDIOCMCAPTURE: changing size to please xawtv :-(.\n"); 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); ret = pwc_try_video_mode(pdev, vm->width, vm->height, pdev->vframes, pdev->vcompression, pdev->vsnapshot);
if (ret) if (ret)
return ret; return ret;
} /* ... size mismatch */ } /* ... size mismatch */
/* FIXME: should we lock here? */ /* FIXME: should we lock here? */
if (pdev->image_used[vm.frame]) if (pdev->image_used[vm->frame])
return -EBUSY; /* buffer wasn't available. Bummer */ return -EBUSY; /* buffer wasn't available. Bummer */
pdev->image_used[vm.frame] = 1; pdev->image_used[vm->frame] = 1;
/* Okay, we're done here. In the SYNC call we wait until a /* Okay, we're done here. In the SYNC call we wait until a
frame comes available, then expand image into the given frame comes available, then expand image into the given
...@@ -1438,18 +1402,16 @@ static int pwc_video_ioctl(struct video_device *vdev, unsigned int cmd, void *ar ...@@ -1438,18 +1402,16 @@ static int pwc_video_ioctl(struct video_device *vdev, unsigned int cmd, void *ar
grabber card will then overwrite the buffer grabber card will then overwrite the buffer
you're working on. you're working on.
*/ */
int mbuf, ret; int *mbuf = arg;
int ret;
if (copy_from_user((void *)&mbuf, arg, sizeof(int))) Trace(TRACE_READ, "VIDIOCSYNC called (%d).\n", *mbuf);
return -EFAULT;
Trace(TRACE_READ, "VIDIOCSYNC called (%d).\n", mbuf);
/* bounds check */ /* bounds check */
if (mbuf < 0 || mbuf >= default_mbufs) if (*mbuf < 0 || *mbuf >= default_mbufs)
return -EINVAL; return -EINVAL;
/* check if this buffer was requested anyway */ /* check if this buffer was requested anyway */
if (pdev->image_used[mbuf] == 0) if (pdev->image_used[*mbuf] == 0)
return -EINVAL; return -EINVAL;
/* Add ourselves to the frame wait-queue. /* Add ourselves to the frame wait-queue.
...@@ -1471,8 +1433,8 @@ static int pwc_video_ioctl(struct video_device *vdev, unsigned int cmd, void *ar ...@@ -1471,8 +1433,8 @@ static int pwc_video_ioctl(struct video_device *vdev, unsigned int cmd, void *ar
set_current_state(TASK_RUNNING); set_current_state(TASK_RUNNING);
return -ERESTARTSYS; return -ERESTARTSYS;
} }
schedule();
set_current_state(TASK_INTERRUPTIBLE); set_current_state(TASK_INTERRUPTIBLE);
schedule();
} }
remove_wait_queue(&pdev->frameq, &wait); remove_wait_queue(&pdev->frameq, &wait);
set_current_state(TASK_RUNNING); set_current_state(TASK_RUNNING);
...@@ -1484,10 +1446,10 @@ static int pwc_video_ioctl(struct video_device *vdev, unsigned int cmd, void *ar ...@@ -1484,10 +1446,10 @@ static int pwc_video_ioctl(struct video_device *vdev, unsigned int cmd, void *ar
Grabber hardware may not be so forgiving. Grabber hardware may not be so forgiving.
*/ */
Trace(TRACE_READ, "VIDIOCSYNC: frame ready.\n"); Trace(TRACE_READ, "VIDIOCSYNC: frame ready.\n");
pdev->fill_image = mbuf; /* tell in which buffer we want the image to be expanded */ pdev->fill_image = *mbuf; /* tell in which buffer we want the image to be expanded */
/* Decompress, etc */ /* Decompress, etc */
ret = pwc_handle_frame(pdev); ret = pwc_handle_frame(pdev);
pdev->image_used[mbuf] = 0; pdev->image_used[*mbuf] = 0;
if (ret) if (ret)
return -EFAULT; return -EFAULT;
break; break;
...@@ -1495,44 +1457,35 @@ static int pwc_video_ioctl(struct video_device *vdev, unsigned int cmd, void *ar ...@@ -1495,44 +1457,35 @@ static int pwc_video_ioctl(struct video_device *vdev, unsigned int cmd, void *ar
case VIDIOCGAUDIO: case VIDIOCGAUDIO:
{ {
struct video_audio v; 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;
if (copy_to_user(arg, &v, sizeof(v))) strcpy(v->name, "Microphone");
return -EFAULT; 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; break;
} }
case VIDIOCSAUDIO: case VIDIOCSAUDIO:
{ {
struct video_audio v;
if (copy_from_user(&v, arg, sizeof(v)))
return -EFAULT;
/* Dummy: nothing can be set */ /* Dummy: nothing can be set */
break; break;
} }
case VIDIOCGUNIT: case VIDIOCGUNIT:
{ {
struct video_unit vu; struct video_unit *vu = arg;
vu.video = pdev->vdev->minor & 0x3F; vu->video = pdev->vdev->minor & 0x3F;
vu.audio = -1; /* not known yet */ vu->audio = -1; /* not known yet */
vu.vbi = -1; vu->vbi = -1;
vu.radio = -1; vu->radio = -1;
vu.teletext = -1; vu->teletext = -1;
if (copy_to_user(arg, &vu, sizeof(vu)))
return -EFAULT;
break; break;
} }
default: default:
...@@ -1541,13 +1494,15 @@ static int pwc_video_ioctl(struct video_device *vdev, unsigned int cmd, void *ar ...@@ -1541,13 +1494,15 @@ static int pwc_video_ioctl(struct video_device *vdev, unsigned int cmd, void *ar
return 0; return 0;
} }
static int pwc_video_mmap(struct vm_area_struct *vma, struct video_device *vdev, const char *adr, unsigned long size) static int pwc_video_mmap(struct file *file, struct vm_area_struct *vma)
{ {
struct video_device *vdev = file->private_data;
struct pwc_device *pdev; struct pwc_device *pdev;
unsigned long start = (unsigned long)adr; unsigned long start = vma->vm_start;
unsigned long size = vma->vm_end-vma->vm_start;
unsigned long page, pos; unsigned long page, pos;
Trace(TRACE_MEMORY, "mmap(0x%p, 0x%p, %lu) called.\n", vdev, adr, size); Trace(TRACE_MEMORY, "mmap(0x%p, 0x%lx, %lu) called.\n", vdev, start, size);
pdev = vdev->priv; pdev = vdev->priv;
/* FIXME - audit mmap during a read */ /* FIXME - audit mmap during a read */
...@@ -1811,6 +1766,7 @@ static void usb_pwc_disconnect(struct usb_device *udev, void *ptr) ...@@ -1811,6 +1766,7 @@ static void usb_pwc_disconnect(struct usb_device *udev, void *ptr)
pdev->unplugged = 1; pdev->unplugged = 1;
if (pdev->vdev != NULL) { if (pdev->vdev != NULL) {
video_unregister_device(pdev->vdev);
if (pdev->vopen) { if (pdev->vopen) {
Info("Disconnected while device/video is open!\n"); Info("Disconnected while device/video is open!\n");
...@@ -1839,7 +1795,6 @@ static void usb_pwc_disconnect(struct usb_device *udev, void *ptr) ...@@ -1839,7 +1795,6 @@ static void usb_pwc_disconnect(struct usb_device *udev, void *ptr)
else { else {
/* Normal disconnect; remove from available devices */ /* Normal disconnect; remove from available devices */
Trace(TRACE_PROBE, "Unregistering video device normally.\n"); Trace(TRACE_PROBE, "Unregistering video device normally.\n");
video_unregister_device(pdev->vdev);
kfree(pdev->vdev); kfree(pdev->vdev);
pdev->vdev = NULL; pdev->vdev = NULL;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment