diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2000-06-16 23:00:36 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2000-06-16 23:00:36 +0000 |
commit | 14dd2ec093cfabda3ae7efeeaf0e23c66ebaccc0 (patch) | |
tree | 9a9ce5cff6ef92faa6e07a82785b9a6d6838f7e4 /drivers/usb | |
parent | 847290510f811c572cc2aa80c1f02a04721410b1 (diff) |
Merge with 2.4.0-test1.
Diffstat (limited to 'drivers/usb')
-rw-r--r-- | drivers/usb/audio.c | 165 | ||||
-rw-r--r-- | drivers/usb/ibmcam.c | 80 | ||||
-rw-r--r-- | drivers/usb/ov511.c | 424 |
3 files changed, 436 insertions, 233 deletions
diff --git a/drivers/usb/audio.c b/drivers/usb/audio.c index 40903a9c2..ad94fef55 100644 --- a/drivers/usb/audio.c +++ b/drivers/usb/audio.c @@ -52,14 +52,17 @@ * decent headphones! * "Let's make things better" -> but please Philips start with your * own stuff!!!! - * 1999-11-02: It takes the Philips boxes several seconds to acquire synchronisation + * 1999-11-02: Thomas Sailer + * It takes the Philips boxes several seconds to acquire synchronisation * that means they won't play short sounds. Should probably maintain * the ISO datastream even if there's nothing to play. * Fix counting the total_bytes counter, RealPlayer G2 depends on it. - * 1999-12-20: Fix bad bug in conversion to per interface probing. + * 1999-12-20: Thomas Sailer + * Fix bad bug in conversion to per interface probing. * disconnect was called multiple times for the audio device, * leading to a premature freeing of the audio structures - * 2000-05-13: I don't remember who changed the find_format routine, + * 2000-05-13: Thomas Sailer + * I don't remember who changed the find_format routine, * but the change was completely broken for the Dallas * chip. Anyway taking sampling rate into account in find_format * is bad and should not be done unless there are devices with @@ -72,8 +75,20 @@ * for the Dallas chip. * Also fix a rather long standing problem with applications that * use "small" writes producing no sound at all. - * 2000-05-15: My fears came true, the Philips camera indeed has pretty stupid + * 2000-05-15: Thomas Sailer + * My fears came true, the Philips camera indeed has pretty stupid * audio descriptors. + * 2000-05-17: Thomas Sailer + * Nemsoft spotted my stupid last minute change, thanks + * 2000-05-19: Thomas Sailer + * Fixed FEATURE_UNIT thinkos found thanks to the KC Technology + * Xtend device. Basically the driver treated FEATURE_UNIT's sourced + * by mono terminals as stereo. + * 2000-05-20: Thomas Sailer + * SELECTOR support (and thus selecting record channels from the mixer). + * Somewhat peculiar due to OSS interface limitations. Only works + * for channels where a "slider" is already in front of it (i.e. + * a MIXER unit or a FEATURE unit with volume capability). * */ @@ -211,6 +226,7 @@ struct mixerchannel { __u16 value; __u16 osschannel; /* number of the OSS channel */ __s16 minval, maxval; + __u16 slctunitid; __u8 unitid; __u8 selector; __u8 chnum; @@ -988,7 +1004,7 @@ static int usbin_start(struct usb_audiodev *as) } spin_lock_irqsave(&as->lock, flags); } - if (u->dma.count <= 0 && !u->dma.mapped) + if (u->dma.count >= u->dma.dmasize && !u->dma.mapped) return 0; u->flags |= FLG_RUNNING; if (!(u->flags & FLG_URB0RUNNING)) { @@ -1719,7 +1735,7 @@ static int wrmixer(struct usb_mixerdev *ms, unsigned mixch, unsigned value) if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT, (ch->selector << 8) | ch->chnum, ms->iface | (ch->unitid << 8), data, 2, HZ) < 0) goto err; - if (ch->chnum == 0) + if (!(ch->flags & (MIXFLG_STEREOIN | MIXFLG_STEREOOUT))) return 0; data[0] = v2; data[1] = v2 >> 8; @@ -1735,7 +1751,7 @@ static int wrmixer(struct usb_mixerdev *ms, unsigned mixch, unsigned value) if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT, (ch->selector << 8) | ch->chnum, ms->iface | (ch->unitid << 8), data, 1, HZ) < 0) goto err; - if (ch->chnum == 0) + if (!(ch->flags & (MIXFLG_STEREOIN | MIXFLG_STEREOOUT))) return 0; data[0] = v2 >> 8; if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT, @@ -1754,6 +1770,89 @@ static int wrmixer(struct usb_mixerdev *ms, unsigned mixch, unsigned value) return -1; } +static int get_rec_src(struct usb_mixerdev *ms) +{ + struct usb_device *dev = ms->state->usbdev; + unsigned int mask = 0, retmask = 0; + unsigned int i, j; + unsigned char buf; + int err = 0; + + for (i = 0; i < ms->numch; i++) { + if (!ms->ch[i].slctunitid || (mask & (1 << i))) + continue; + if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, + 0, ms->iface | (ms->ch[i].slctunitid << 8), &buf, 1, HZ) < 0) { + err = -EIO; + printk(KERN_ERR "usbaudio: selector read request device %u if %u unit %u failed\n", + dev->devnum, ms->iface, ms->ch[i].slctunitid & 0xff); + continue; + } + for (j = i; j < ms->numch; i++) { + if ((ms->ch[i].slctunitid ^ ms->ch[j].slctunitid) & 0xff) + continue; + mask |= 1 << j; + if (buf == (ms->ch[j].slctunitid >> 8)) + retmask |= 1 << ms->ch[j].osschannel; + } + } + if (err) + return -EIO; + return retmask; +} + +static int set_rec_src(struct usb_mixerdev *ms, int srcmask) +{ + struct usb_device *dev = ms->state->usbdev; + unsigned int mask = 0, smask, bmask; + unsigned int i, j; + unsigned char buf; + int err = 0; + + for (i = 0; i < ms->numch; i++) { + if (!ms->ch[i].slctunitid || (mask & (1 << i))) + continue; + if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, + 0, ms->iface | (ms->ch[i].slctunitid << 8), &buf, 1, HZ) < 0) { + err = -EIO; + printk(KERN_ERR "usbaudio: selector read request device %u if %u unit %u failed\n", + dev->devnum, ms->iface, ms->ch[i].slctunitid & 0xff); + continue; + } + /* first generate smask */ + smask = bmask = 0; + for (j = i; j < ms->numch; i++) { + if ((ms->ch[i].slctunitid ^ ms->ch[j].slctunitid) & 0xff) + continue; + smask |= 1 << ms->ch[j].osschannel; + if (buf == (ms->ch[j].slctunitid >> 8)) + bmask |= 1 << ms->ch[j].osschannel; + mask |= 1 << j; + } + /* check for multiple set sources */ + j = hweight32(srcmask & smask); + if (j == 0) + continue; + if (j > 1) + srcmask &= ~bmask; + for (j = i; j < ms->numch; i++) { + if ((ms->ch[i].slctunitid ^ ms->ch[j].slctunitid) & 0xff) + continue; + if (!(srcmask & (1 << ms->ch[j].osschannel))) + continue; + buf = ms->ch[j].slctunitid >> 8; + if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT, + 0, ms->iface | (ms->ch[j].slctunitid << 8), &buf, 1, HZ) < 0) { + err = -EIO; + printk(KERN_ERR "usbaudio: selector write request device %u if %u unit %u failed\n", + dev->devnum, ms->iface, ms->ch[j].slctunitid & 0xff); + continue; + } + } + } + return err ? -EIO : 0; +} + /* --------------------------------------------------------------------- */ /* @@ -1886,8 +1985,10 @@ static int usb_audio_ioctl_mixdev(struct inode *inode, struct file *file, unsign if (_IOC_DIR(cmd) == _IOC_READ) { switch (_IOC_NR(cmd)) { case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ - /* don't know how to handle this yet */ - return put_user(0, (int *)arg); + val = get_rec_src(ms); + if (val < 0) + return val; + return put_user(val, (int *)arg); case SOUND_MIXER_DEVMASK: /* Arg contains a bit for each supported device */ for (val = i = 0; i < ms->numch; i++) @@ -1895,9 +1996,11 @@ static int usb_audio_ioctl_mixdev(struct inode *inode, struct file *file, unsign return put_user(val, (int *)arg); case SOUND_MIXER_RECMASK: /* Arg contains a bit for each supported recording source */ - /* don't know how to handle this yet */ - return put_user(0, (int *)arg); - + for (val = i = 0; i < ms->numch; i++) + if (ms->ch[i].slctunitid) + val |= 1 << ms->ch[i].osschannel; + return put_user(val, (int *)arg); + case SOUND_MIXER_STEREODEVS: /* Mixer channels supporting stereo */ for (val = i = 0; i < ms->numch; i++) if (ms->ch[i].flags & (MIXFLG_STEREOIN | MIXFLG_STEREOOUT)) @@ -1905,7 +2008,7 @@ static int usb_audio_ioctl_mixdev(struct inode *inode, struct file *file, unsign return put_user(val, (int *)arg); case SOUND_MIXER_CAPS: - return put_user(0, (int *)arg); + return put_user(SOUND_CAP_EXCL_INPUT, (int *)arg); default: i = _IOC_NR(cmd); @@ -1925,8 +2028,7 @@ static int usb_audio_ioctl_mixdev(struct inode *inode, struct file *file, unsign switch (_IOC_NR(cmd)) { case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ get_user_ret(val, (int *)arg, -EFAULT); - /* set recording source: val */ - return 0; + return set_rec_src(ms, val); default: i = _IOC_NR(cmd); @@ -2852,7 +2954,7 @@ static unsigned int getvolchannel(struct consmixstate *state) { unsigned int u; - if ((state->termtype & 0xff00) == 0x0000 && !(state->mixchmask & SOUND_MASK_VOLUME)) + if ((state->termtype & 0xff00) == 0x0000 && (state->mixchmask & SOUND_MASK_VOLUME)) return SOUND_MIXER_VOLUME; if ((state->termtype & 0xff00) == 0x0100) { if (state->mixchmask & SOUND_MASK_PCM) @@ -2864,8 +2966,6 @@ static unsigned int getvolchannel(struct consmixstate *state) return SOUND_MIXER_MIC; if ((state->termtype & 0xff00) == 0x0300 && (state->mixchmask & SOUND_MASK_SPEAKER)) return SOUND_MIXER_SPEAKER; - if ((state->termtype & 0xff00) == 0x0300 && (state->mixchmask & SOUND_MASK_SPEAKER)) - return SOUND_MIXER_SPEAKER; if ((state->termtype & 0xff00) == 0x0500) { if (state->mixchmask & SOUND_MASK_PHONEIN) return SOUND_MIXER_PHONEIN; @@ -2950,7 +3050,7 @@ static void prepmixch(struct consmixstate *state) if (v3 > 100) v3 = 100; ch->value = v3; - if (ch->chnum != 0) { + if (ch->flags & (MIXFLG_STEREOIN | MIXFLG_STEREOOUT)) { if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, (ch->selector << 8) | (ch->chnum + 1), state->ctrlif | (ch->unitid << 8), buf, 2, HZ) < 0) goto err; @@ -2986,7 +3086,7 @@ static void prepmixch(struct consmixstate *state) if (v3 > 100) v3 = 100; ch->value = v3; - if (ch->chnum != 0) { + if (ch->flags & (MIXFLG_STEREOIN | MIXFLG_STEREOOUT)) { if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, (ch->selector << 8) | (ch->chnum + 1), state->ctrlif | (ch->unitid << 8), buf, 1, HZ) < 0) goto err; @@ -3095,15 +3195,24 @@ static void usb_audio_mixerunit(struct consmixstate *state, unsigned char *mixer static void usb_audio_selectorunit(struct consmixstate *state, unsigned char *selector) { - unsigned int chnum, i; + unsigned int chnum, i, mixch; + struct mixerchannel *mch; if (!selector[4]) { printk(KERN_ERR "usbaudio: unit %u invalid SELECTOR_UNIT descriptor\n", selector[3]); return; } + mixch = state->nrmixch; usb_audio_recurseunit(state, selector[5]); + if (state->nrmixch != mixch) { + mch = &state->mixch[state->nrmixch-1]; + mch->slctunitid = selector[5] | (1 << 8); + } else { + printk(KERN_INFO "usbaudio: selector unit %u: ignoring channel 1\n", selector[3]); + } chnum = state->nrchannels; for (i = 1; i < selector[4]; i++) { + mixch = state->nrmixch; usb_audio_recurseunit(state, selector[5+i]); if (chnum != state->nrchannels) { printk(KERN_ERR "usbaudio: selector unit %u: input pins with varying channel numbers\n", selector[3]); @@ -3112,6 +3221,12 @@ static void usb_audio_selectorunit(struct consmixstate *state, unsigned char *se state->nrchannels = 0; return; } + if (state->nrmixch != mixch) { + mch = &state->mixch[state->nrmixch-1]; + mch->slctunitid = selector[5] | ((i + 1) << 8); + } else { + printk(KERN_INFO "usbaudio: selector unit %u: ignoring channel %u\n", selector[3], i+1); + } } state->termtype = 0; state->chconfig = 0; @@ -3167,7 +3282,7 @@ static void usb_audio_featureunit(struct consmixstate *state, unsigned char *ftr ch->unitid = ftr[3]; ch->selector = VOLUME_CONTROL; ch->chnum = 1; - ch->flags = MIXFLG_STEREOIN | MIXFLG_STEREOOUT; + ch->flags = (state->nrchannels > 1) ? (MIXFLG_STEREOIN | MIXFLG_STEREOOUT) : 0; prepmixch(state); } } else if (mchftr & 2) { @@ -3187,7 +3302,7 @@ static void usb_audio_featureunit(struct consmixstate *state, unsigned char *ftr ch->unitid = ftr[3]; ch->selector = BASS_CONTROL; ch->chnum = 1; - ch->flags = MIXFLG_STEREOIN | MIXFLG_STEREOOUT; + ch->flags = (state->nrchannels > 1) ? (MIXFLG_STEREOIN | MIXFLG_STEREOOUT) : 0; prepmixch(state); } } else if (mchftr & 4) { @@ -3207,7 +3322,7 @@ static void usb_audio_featureunit(struct consmixstate *state, unsigned char *ftr ch->unitid = ftr[3]; ch->selector = TREBLE_CONTROL; ch->chnum = 1; - ch->flags = MIXFLG_STEREOIN | MIXFLG_STEREOOUT; + ch->flags = (state->nrchannels > 1) ? (MIXFLG_STEREOIN | MIXFLG_STEREOOUT) : 0; prepmixch(state); } } else if (mchftr & 16) { @@ -3239,7 +3354,7 @@ static void usb_audio_recurseunit(struct consmixstate *state, unsigned char unit unsigned int i, j; if (test_and_set_bit(unitid, &state->unitbitmap)) { - printk(KERN_ERR "usbaudio: mixer path recursion detected, unit %d!\n", unitid); + printk(KERN_INFO "usbaudio: mixer path revisits unit %d\n", unitid); return; } p1 = find_audiocontrol_unit(state->buffer, state->buflen, NULL, unitid, state->ctrlif); diff --git a/drivers/usb/ibmcam.c b/drivers/usb/ibmcam.c index 2deab03fa..f643dee47 100644 --- a/drivers/usb/ibmcam.c +++ b/drivers/usb/ibmcam.c @@ -7,6 +7,22 @@ * * (C) Copyright 1999 Johannes Erdfelt * (C) Copyright 1999 Randy Dunlap + * + * 5/24/00 Removed optional (and unnecessary) locking of the driver while + * the device remains plugged in. Corrected race conditions in ibmcam_open + * and ibmcam_probe() routines using this as a guideline: + * + * (2) The big kernel lock is automatically released when a process sleeps + * in the kernel and is automatically reacquired on reschedule if the + * process had the lock originally. Any code that can be compiled as + * a module and is entered with the big kernel lock held *MUST* + * increment the use count to activate the indirect module protection + * before doing anything that might sleep. + * + * In practice, this means that all routines that live in modules and + * are invoked under the big kernel lock should do MOD_INC_USE_COUNT + * as their very first action. And all failure paths from that + * routine must do MOD_DEC_USE_COUNT before returning. */ #include <linux/kernel.h> @@ -26,21 +42,6 @@ #include "ibmcam.h" -/* - * IBMCAM_LOCKS_DRIVER_WHILE_DEVICE_IS_PLUGGED: This symbol controls - * the locking of the driver. If non-zero, the driver counts the - * probe() call as usage and increments module usage counter; this - * effectively prevents removal of the module (with rmmod) until the - * device is unplugged (then disconnect() callback reduces the module - * usage counter back, and module can be removed). - * - * This behavior may be useful if you prefer to lock the driver in - * memory until device is unplugged. However you can't reload the - * driver if you want to alter some parameters - you'd need to unplug - * the camera first. Therefore, I recommend setting 0. - */ -#define IBMCAM_LOCKS_DRIVER_WHILE_DEVICE_IS_PLUGGED 0 - #define ENABLE_HEXDUMP 0 /* Enable if you need it */ static int debug = 0; @@ -242,7 +243,7 @@ static void *rvmalloc(unsigned long size) size += (PAGE_SIZE - 1); size &= ~(PAGE_SIZE - 1); - mem = vmalloc(size); + mem = vmalloc_32(size); if (!mem) return NULL; @@ -2346,6 +2347,7 @@ static int ibmcam_new_frame(struct usb_ibmcam *ibmcam, int framenum) * camera is also initialized here (once per connect), at * expense of V4L client (it waits on open() call). * 1/27/00 Used IBMCAM_NUMSBUF as number of URB buffers. + * 5/24/00 Corrected to prevent race condition (MOD_xxx_USE_COUNT). */ static int ibmcam_open(struct video_device *dev, int flags) { @@ -2353,6 +2355,7 @@ static int ibmcam_open(struct video_device *dev, int flags) const int sb_size = FRAMES_PER_DESC * ibmcam->iso_packet_len; int i, err = 0; + MOD_INC_USE_COUNT; down(&ibmcam->lock); if (ibmcam->user) @@ -2425,14 +2428,13 @@ static int ibmcam_open(struct video_device *dev, int flags) else err = -EBUSY; } - if (!err) { + if (!err) ibmcam->user++; - MOD_INC_USE_COUNT; - } } } - up(&ibmcam->lock); + if (err) + MOD_DEC_USE_COUNT; return err; } @@ -2446,6 +2448,7 @@ static int ibmcam_open(struct video_device *dev, int flags) * History: * 1/22/00 Moved scratch buffer deallocation here. * 1/27/00 Used IBMCAM_NUMSBUF as number of URB buffers. + * 5/24/00 Moved MOD_DEC_USE_COUNT outside of code that can sleep. */ static void ibmcam_close(struct video_device *dev) { @@ -2462,13 +2465,13 @@ static void ibmcam_close(struct video_device *dev) kfree(ibmcam->sbuf[i].data); ibmcam->user--; - MOD_DEC_USE_COUNT; if (ibmcam->remove_pending) { printk(KERN_INFO "ibmcam_close: Final disconnect.\n"); usb_ibmcam_release(ibmcam); } up(&ibmcam->lock); + MOD_DEC_USE_COUNT; } static int ibmcam_init_done(struct video_device *dev) @@ -2891,7 +2894,7 @@ static void usb_ibmcam_configure_video(struct usb_ibmcam *ibmcam) } /* - * usb_ibmcam_release() + * ibmcam_find_struct() * * This code searches the array of preallocated (static) structures * and returns index of the first one that isn't in use. Returns -1 @@ -2929,6 +2932,7 @@ static int ibmcam_find_struct(void) * History: * 1/22/00 Moved camera init code to ibmcam_open() * 1/27/00 Changed to use static structures, added locking. + * 5/24/00 Corrected to prevent race condition (MOD_xxx_USE_COUNT). */ static void *usb_ibmcam_probe(struct usb_device *dev, unsigned int ifnum) { @@ -2994,10 +2998,14 @@ static void *usb_ibmcam_probe(struct usb_device *dev, unsigned int ifnum) RESTRICT_TO_RANGE(videosize, VIDEOSIZE_176x144, VIDEOSIZE_352x240); } + /* Code below may sleep, need to lock module while we are here */ + MOD_INC_USE_COUNT; + devnum = ibmcam_find_struct(); if (devnum == -1) { printk(KERN_INFO "IBM USB camera driver: Too many devices!\n"); - return NULL; + ibmcam = NULL; /* Do not free, it's preallocated */ + goto probe_done; } ibmcam = &cams[devnum]; @@ -3017,17 +3025,14 @@ static void *usb_ibmcam_probe(struct usb_device *dev, unsigned int ifnum) usb_ibmcam_configure_video(ibmcam); up (&ibmcam->lock); -#if IBMCAM_LOCKS_DRIVER_WHILE_DEVICE_IS_PLUGGED - MOD_INC_USE_COUNT; -#endif - if (video_register_device(&ibmcam->vdev, VFL_TYPE_GRABBER) == -1) { printk(KERN_ERR "video_register_device failed\n"); - return NULL; + ibmcam = NULL; /* Do not free, it's preallocated */ } if (debug > 1) printk(KERN_DEBUG "video_register_device() successful\n"); - +probe_done: + MOD_DEC_USE_COUNT; return ibmcam; } @@ -3055,17 +3060,26 @@ static void usb_ibmcam_release(struct usb_ibmcam *ibmcam) * structure (pointed by 'ptr') and after that driver should be removable * with no ill consequences. * - * TODO: This code behaves badly on surprise removal! + * This code handles surprise removal. The ibmcam->user is a counter which + * increments on open() and decrements on close(). If we see here that + * this counter is not 0 then we have a client who still has us opened. + * We set ibmcam->remove_pending flag as early as possible, and after that + * all access to the camera will gracefully fail. These failures should + * prompt client to (eventually) close the video device, and then - in + * ibmcam_close() - we decrement ibmcam->ibmcam_used and usage counter. * * History: * 1/22/00 Added polling of MOD_IN_USE to delay removal until all users gone. * 1/27/00 Reworked to allow pending disconnects; see ibmcam_close() + * 5/24/00 Corrected to prevent race condition (MOD_xxx_USE_COUNT). */ static void usb_ibmcam_disconnect(struct usb_device *dev, void *ptr) { static const char proc[] = "usb_ibmcam_disconnect"; struct usb_ibmcam *ibmcam = (struct usb_ibmcam *) ptr; + MOD_INC_USE_COUNT; + if (debug > 0) printk(KERN_DEBUG "%s(%p,%p.)\n", proc, dev, ptr); @@ -3077,16 +3091,14 @@ static void usb_ibmcam_disconnect(struct usb_device *dev, void *ptr) ibmcam->dev = NULL; /* USB device is no more */ -#if IBMCAM_LOCKS_DRIVER_WHILE_DEVICE_IS_PLUGGED - MOD_DEC_USE_COUNT; -#endif if (ibmcam->user) printk(KERN_INFO "%s: In use, disconnect pending.\n", proc); else usb_ibmcam_release(ibmcam); up(&ibmcam->lock); - printk(KERN_INFO "IBM USB camera disconnected.\n"); + + MOD_DEC_USE_COUNT; } static struct usb_driver ibmcam_driver = { diff --git a/drivers/usb/ov511.c b/drivers/usb/ov511.c index e97410c32..3ffb6f8eb 100644 --- a/drivers/usb/ov511.c +++ b/drivers/usb/ov511.c @@ -30,7 +30,7 @@ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -static const char version[] = "1.14"; +static const char version[] = "1.15"; #define __NO_VERSION__ @@ -216,7 +216,7 @@ static void *rvmalloc(unsigned long size) size += (PAGE_SIZE - 1); size &= ~(PAGE_SIZE - 1); - mem = vmalloc(size); + mem = vmalloc_32(size); if (!mem) return NULL; @@ -263,9 +263,10 @@ static void rvfree(void *mem, unsigned long size) * Based on the CPiA driver version 0.7.4 -claudio **********************************************************************/ -#ifdef CONFIG_PROC_FS +#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS) + static struct proc_dir_entry *ov511_proc_entry = NULL; -static struct proc_dir_entry *video_proc_entry = NULL; +extern struct proc_dir_entry *video_proc_entry; #define YES_NO(x) ((x) ? "yes" : "no") @@ -343,6 +344,7 @@ static int ov511_read_proc(char *page, char **start, off_t off, len = count; *start = page + off; + return len; } @@ -357,12 +359,11 @@ static void create_proc_ov511_cam (struct usb_ov511 *ov511) char name[7]; struct proc_dir_entry *ent; - PDEBUG (4, "creating /proc/video/ov511/videoX entry"); if (!ov511_proc_entry || !ov511) return; sprintf(name, "video%d", ov511->vdev.minor); - PDEBUG (4, "creating %s", name); + PDEBUG (4, "creating /proc/video/ov511/%s", name); ent = create_proc_entry(name, S_IFREG|S_IRUGO|S_IWUSR, ov511_proc_entry); @@ -372,7 +373,6 @@ static void create_proc_ov511_cam (struct usb_ov511 *ov511) ent->data = ov511; ent->read_proc = ov511_read_proc; ent->write_proc = ov511_write_proc; - ent->size = 3626; /* FIXME */ ov511->proc_entry = ent; } @@ -391,22 +391,13 @@ static void destroy_proc_ov511_cam (struct usb_ov511 *ov511) static void proc_ov511_create(void) { - struct proc_dir_entry *p = NULL; - /* No current standard here. Alan prefers /proc/video/ as it keeps * /proc "less cluttered than /proc/randomcardifoundintheshed/" * -claudio */ - PDEBUG (3, "creating /proc/video"); - video_proc_entry = proc_mkdir("video", p); - if (!video_proc_entry) { - if (!p) { - err("Unable to initialise /proc/video\n"); - return; - } else { /* FIXME - this doesn't work */ - PDEBUG (3, "/proc/video already exists"); - video_proc_entry = p; - } + if (video_proc_entry == NULL) { + err("Unable to initialise /proc/video/ov511"); + return; } ov511_proc_entry = create_proc_entry("ov511", S_IFDIR, video_proc_entry); @@ -414,16 +405,19 @@ static void proc_ov511_create(void) if (ov511_proc_entry) ov511_proc_entry->owner = THIS_MODULE; else - err("Unable to initialise /proc/video/ov511\n"); + err("Unable to initialise /proc/ov511"); } static void proc_ov511_destroy(void) { PDEBUG (3, "removing /proc/video/ov511"); + + if (ov511_proc_entry == NULL) + return; + remove_proc_entry("ov511", video_proc_entry); - remove_proc_entry("video", NULL); } -#endif /* CONFIG_PROC_FS */ +#endif /* CONFIG_PROC_FS && CONFIG_VIDEO_PROC_FS */ /********************************************************************** @@ -852,7 +846,7 @@ ov7610_get_picture(struct usb_ov511 *ov511, struct video_picture *p) return 0; } -/* FIXME: add 176x144, 160x140 */ +/* FIXME: add 400x300, 176x144, 160x140 */ static struct mode_list mlist[] = { { 640, 480, VIDEO_PALETTE_GREY, 0x4f, 0x3d, 0x00, 0x00, 0x4f, 0x3d, 0x00, 0x00, 0x04, 0x03, 0x24, 0x04, 0x9e }, @@ -866,6 +860,14 @@ static struct mode_list mlist[] = { 0x2b, 0x25, 0x00, 0x00, 0x01, 0x03, 0x04, 0x04, 0x1e }, { 352, 288, VIDEO_PALETTE_RGB24,0x2b, 0x25, 0x00, 0x00, 0x2b, 0x25, 0x00, 0x00, 0x01, 0x03, 0x04, 0x04, 0x1e }, + { 384, 288, VIDEO_PALETTE_GREY, 0x2f, 0x25, 0x00, 0x00, + 0x2f, 0x25, 0x00, 0x00, 0x01, 0x03, 0x04, 0x04, 0x1e }, + { 384, 288, VIDEO_PALETTE_RGB24,0x2f, 0x25, 0x00, 0x00, + 0x2f, 0x25, 0x00, 0x00, 0x01, 0x03, 0x04, 0x04, 0x1e }, + { 448, 336, VIDEO_PALETTE_GREY, 0x37, 0x29, 0x00, 0x00, + 0x37, 0x29, 0x00, 0x00, 0x01, 0x03, 0x04, 0x04, 0x1e }, + { 448, 336, VIDEO_PALETTE_RGB24,0x37, 0x29, 0x00, 0x00, + 0x37, 0x29, 0x00, 0x00, 0x01, 0x03, 0x04, 0x04, 0x1e }, { 0, 0 } }; @@ -1302,158 +1304,190 @@ static int ov511_move_data(struct usb_ov511 *ov511, urb_t *urb) int i, totlen = 0; int aPackNum[10]; struct ov511_frame *frame; + unsigned char *pData; + int iPix; - PDEBUG(4, "ov511_move_data"); + PDEBUG (4, "Moving %d packets", urb->number_of_packets); 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; + urb->iso_frame_desc[i].actual_length = 0; urb->iso_frame_desc[i].status = 0; + cdata = urb->transfer_buffer + urb->iso_frame_desc[i].offset; aPackNum[i] = n ? cdata[ov511->packet_size - 1] : -1; - if (!n || ov511->curframe == -1) continue; + if (!n || ov511->curframe == -1) + continue; if (st) PDEBUG(2, "data error: [%d] len=%d, status=%d", i, n, st); frame = &ov511->frame[ov511->curframe]; - /* Can we find a frame end */ + /* SOF/EOF packets have 1st to 8th bytes zeroed and the 9th + * byte non-zero. The EOF packet has image width/height in the + * 10th and 11th packets. The 9th bit is given as follows: + * + * bit 7: EOF + * 6: compression enabled + * 5: 422/420/400 modes + * 4: 422/420/400 modes + * 3: 1 + * 2: snapshot bottom on + * 1: snapshot frame + * 0: even/odd field + */ + + /* Check for SOF/EOF packet */ if ((cdata[0] | cdata[1] | cdata[2] | cdata[3] | - cdata[4] | cdata[5] | cdata[6] | cdata[7]) == 0 && - (cdata[8] & 8) && (cdata[8] & 0x80)) { + cdata[4] | cdata[5] | cdata[6] | cdata[7]) || + (~cdata[8] & 0x08)) + goto check_middle; + + /* Frame end */ + if (cdata[8] & 0x80) { +#if 0 + struct timeval *ts; + + ts = (struct timeval *)(frame->data + MAX_FRAME_SIZE); + do_gettimeofday (ts); +#endif - struct timeval *ts; - ts = (struct timeval *)(frame->data + MAX_FRAME_SIZE); - do_gettimeofday(ts); + PDEBUG(4, "Frame end, curframe = %d, packnum=%d, hw=%d, vw=%d", + ov511->curframe, (int)(cdata[ov511->packet_size - 1]), + (int)(cdata[9]), (int)(cdata[10])); - PDEBUG(4, "Frame End, curframe = %d, packnum=%d, hw=%d, vw=%d", - ov511->curframe, (int)(cdata[ov511->packet_size - 1]), - (int)(cdata[9]), (int)(cdata[10])); + if (frame->scanstate == STATE_LINES) { + int iFrameNext; - if (frame->scanstate == STATE_LINES) { - int iFrameNext; if (fix_rgb_offset) fixFrameRGBoffset(frame); frame->grabstate = FRAME_DONE; - if (waitqueue_active(&frame->wq)) { - frame->grabstate = FRAME_DONE; - wake_up_interruptible(&frame->wq); + + if (waitqueue_active(&frame->wq)) { + frame->grabstate = FRAME_DONE; + wake_up_interruptible(&frame->wq); } - /* If next frame is ready or grabbing, point to it */ + + /* If next frame is ready or grabbing, + * point to it */ iFrameNext = (ov511->curframe + 1) % OV511_NUMFRAMES; - if (ov511->frame[iFrameNext].grabstate== FRAME_READY || - ov511->frame[iFrameNext].grabstate== FRAME_GRABBING) { - ov511->curframe = iFrameNext; - ov511->frame[iFrameNext].scanstate = STATE_SCANNING; + if (ov511->frame[iFrameNext].grabstate == FRAME_READY + || ov511->frame[iFrameNext].grabstate == FRAME_GRABBING) { + ov511->curframe = iFrameNext; + ov511->frame[iFrameNext].scanstate = STATE_SCANNING; } else { - - PDEBUG(4, "Frame not ready? state = %d", - ov511->frame[iFrameNext].grabstate); - - ov511->curframe = -1; + if (frame->grabstate == FRAME_DONE) { + PDEBUG(4, "Frame done! congratulations"); + } else { + PDEBUG(4, "Frame not ready? state = %d", + ov511->frame[iFrameNext].grabstate); + } + + ov511->curframe = -1; } - } - } - - /* Can we find a frame start */ - else if ((cdata[0] | cdata[1] | cdata[2] | cdata[3] | - cdata[4] | cdata[5] | cdata[6] | cdata[7]) == 0 && - (cdata[8] & 8)) { - - PDEBUG(4, "ov511: Found Frame Start!, framenum = %d", - ov511->curframe); + } + /* Image corruption caused by misplaced frame->segment = 0 + * fixed by carlosf@conectiva.com.br + */ + } else { + /* Frame start */ + PDEBUG(4, "Frame start, framenum = %d", ov511->curframe); /* Check to see if it's a snapshot frame */ /* FIXME?? Should the snapshot reset go here? Performance? */ if (cdata[8] & 0x02) { frame->snapshot = 1; - PDEBUG(3, "ov511_move_data: snapshot detected"); + PDEBUG(3, "snapshot detected"); } frame->scanstate = STATE_LINES; frame->segment = 0; } +check_middle: /* Are we in a frame? */ - if (frame->scanstate == STATE_LINES) { - unsigned char * pData; - int iPix; - - /* Deal with leftover from last segment, if any */ - if (frame->segment) { - pData = ov511->scratch; - iPix = - ov511->scratchlen; - memmove(pData + ov511->scratchlen, cdata, - iPix+frame->segsize); - } else { - pData = &cdata[iPix = 9]; - } - - /* Parse the segments */ - while(iPix <= (ov511->packet_size - 1) - frame->segsize && - frame->segment < frame->width * frame->height / 256) { - int iSegY; - int iSegUV; - int iY, jY, iUV, jUV; - int iOutY, iOutUV; - unsigned char * pOut; - - iSegY = iSegUV = frame->segment; - pOut = frame->data; - - frame->segment++; - iPix += frame->segsize; - - if (frame->sub_flag) { - int iSeg1; - iSeg1 = iSegY / (ov511->subw / 32); - iSeg1 *= frame->width / 32; - iSegY = iSeg1 + (iSegY % (ov511->subw / 32)); - if (iSegY >= frame->width * ov511->subh / 256) - break; - - iSeg1 = iSegUV / (ov511->subw / 16); - iSeg1 *= frame->width / 16; - iSegUV = iSeg1 + (iSegUV % (ov511->subw / 16)); - - pOut += (ov511->subx + - ov511->suby * frame->width) * frame->depth; - } - - iY = iSegY / (frame->width / WDIV); - jY = iSegY - iY * (frame->width / WDIV); - iOutY = (iY*HDIV*frame->width + jY*WDIV) * frame->depth; - iUV = iSegUV / (frame->width / WDIV * 2); - jUV = iSegUV - iUV * (frame->width / WDIV * 2); - iOutUV = (iUV*HDIV*2*frame->width + jUV*WDIV/2) * frame->depth; - - if (frame->format == VIDEO_PALETTE_GREY) { - ov511_parse_data_grey(pData, pOut, iOutY, frame->width); - } else if (frame->format == VIDEO_PALETTE_RGB24) { - ov511_parse_data_rgb24(pData, pOut, iOutY, iOutUV, iY & 1, - frame->width); - } - pData = &cdata[iPix]; - } + if (frame->scanstate != STATE_LINES) + continue; - /* Save extra data for next time */ - if (frame->segment < frame->width * frame->height / 256) { - ov511->scratchlen = (ov511->packet_size - 1) - iPix; - if (ov511->scratchlen < frame->segsize) { - memmove(ov511->scratch, pData, ov511->scratchlen); - } else { - ov511->scratchlen = 0; - } + /* Deal with leftover from last segment, if any */ + if (frame->segment) { + pData = ov511->scratch; + iPix = -ov511->scratchlen; + memmove(pData + ov511->scratchlen, cdata, + iPix+frame->segsize); + } else { + pData = &cdata[iPix = 9]; + } + + /* Parse the segments */ + while (iPix <= (ov511->packet_size - 1) - frame->segsize && + frame->segment < frame->width * frame->height / 256) { + int iSegY, iSegUV; + int iY, jY, iUV, jUV; + int iOutY, iOutUV; + unsigned char *pOut; + + iSegY = iSegUV = frame->segment; + pOut = frame->data; + frame->segment++; + iPix += frame->segsize; + + /* Handle subwindow */ + if (frame->sub_flag) { + int iSeg1; + + iSeg1 = iSegY / (ov511->subw / 32); + iSeg1 *= frame->width / 32; + iSegY = iSeg1 + (iSegY % (ov511->subw / 32)); + if (iSegY >= frame->width * ov511->subh / 256) + break; + + iSeg1 = iSegUV / (ov511->subw / 16); + iSeg1 *= frame->width / 16; + iSegUV = iSeg1 + (iSegUV % (ov511->subw / 16)); + + pOut += (ov511->subx + ov511->suby * frame->width) * + (frame->depth >> 3); } + + /* + * iY counts segment lines + * jY counts segment columns + * iOutY is the offset (in bytes) of the segment upper left corner + */ + iY = iSegY / (frame->width / WDIV); + jY = iSegY - iY * (frame->width / WDIV); + iOutY = (iY*HDIV*frame->width + jY*WDIV) * (frame->depth >> 3); + iUV = iSegUV / (frame->width / WDIV * 2); + jUV = iSegUV - iUV * (frame->width / WDIV * 2); + iOutUV = (iUV*HDIV*2*frame->width + jUV*WDIV/2) * (frame->depth >> 3); + + if (frame->format == VIDEO_PALETTE_GREY) + ov511_parse_data_grey (pData, pOut, iOutY, frame->width); + else if (frame->format == VIDEO_PALETTE_RGB24) + ov511_parse_data_rgb24 (pData, pOut, iOutY, iOutUV, + iY & 1, frame->width); + + pData = &cdata[iPix]; } - } + /* Save extra data for next time */ + if (frame->segment < frame->width * frame->height / 256) { + ov511->scratchlen = (ov511->packet_size - 1) - iPix; + if (ov511->scratchlen < frame->segsize) + memmove(ov511->scratch, pData, + ov511->scratchlen); + else + ov511->scratchlen = 0; + } + } - PDEBUG(5, "pn: %d %d %d %d %d %d %d %d %d %d\n", + PDEBUG(5, "pn: %d %d %d %d %d %d %d %d %d %d", aPackNum[0], aPackNum[1], aPackNum[2], aPackNum[3], aPackNum[4], aPackNum[5],aPackNum[6], aPackNum[7], aPackNum[8], aPackNum[9]); @@ -1472,8 +1506,6 @@ static void ov511_isoc_irq(struct urb *urb) if (!ov511->streaming) { PDEBUG(2, "hmmm... not streaming, but got interrupt"); return; - } else { - PDEBUG(5, "streaming. got interrupt"); } sbuf = &ov511->sbuf[ov511->cursbuf]; @@ -1723,7 +1755,7 @@ static void ov511_close(struct video_device *dev) static int ov511_init_done(struct video_device *dev) { -#ifdef CONFIG_PROC_FS +#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS) create_proc_ov511_cam((struct usb_ov511 *)dev); #endif @@ -1749,6 +1781,8 @@ static int ov511_ioctl(struct video_device *vdev, unsigned int cmd, void *arg) { struct video_capability b; + PDEBUG (4, "VIDIOCGCAP"); + strcpy(b.name, "OV511 USB Camera"); b.type = VID_TYPE_CAPTURE | VID_TYPE_SUBCAPTURE; b.channels = 1; @@ -1798,6 +1832,8 @@ static int ov511_ioctl(struct video_device *vdev, unsigned int cmd, void *arg) { struct video_picture p; + PDEBUG (4, "VIDIOCGPICT"); + if (ov7610_get_picture(ov511, &p)) return -EIO; @@ -1809,18 +1845,33 @@ static int ov511_ioctl(struct video_device *vdev, unsigned int cmd, void *arg) case VIDIOCSPICT: { struct video_picture p; + int i; + + PDEBUG (4, "VIDIOCSPICT"); if (copy_from_user(&p, arg, sizeof(p))) return -EFAULT; - + if (ov7610_set_picture(ov511, &p)) return -EIO; + /* FIXME: check validity */ + + PDEBUG(4, "Setting depth=%d, palette=%d", p.depth, p.palette); + for (i = 0; i < OV511_NUMFRAMES; i++) { + ov511->frame[i].depth = p.depth; + ov511->frame[i].format = p.palette; + ov511->frame[i].segsize = GET_SEGSIZE(p.palette); + } + return 0; } case VIDIOCGCAPTURE: { int vf; + + PDEBUG (4, "VIDIOCGCAPTURE"); + if (copy_from_user(&vf, arg, sizeof(vf))) return -EFAULT; ov511->sub_flag = vf; @@ -1836,16 +1887,25 @@ static int ov511_ioctl(struct video_device *vdev, unsigned int cmd, void *arg) return -EINVAL; if (vc.decimation) return -EINVAL; +#if 0 vc.x /= 4; vc.x *= 4; vc.y /= 2; vc.y *= 2; vc.width /= 32; vc.width *= 32; - if (vc.width == 0) vc.width = 32; +#else + vc.x &= ~3L; + vc.y &= ~1L; + vc.y &= ~31L; +#endif + if (vc.width == 0) + vc.width = 32; + vc.height /= 16; vc.height *= 16; - if (vc.height == 0) vc.height = 16; + if (vc.height == 0) + vc.height = 16; ov511->subx = vc.x; ov511->suby = vc.y; @@ -1857,9 +1917,15 @@ static int ov511_ioctl(struct video_device *vdev, unsigned int cmd, void *arg) case VIDIOCSWIN: { struct video_window vw; + int i, result; if (copy_from_user(&vw, arg, sizeof(vw))) return -EFAULT; + + PDEBUG (4, "VIDIOCSWIN: width=%d, height=%d", + vw.width, vw.height); + +#if 0 if (vw.flags) return -EINVAL; if (vw.clipcount) @@ -1868,8 +1934,22 @@ static int ov511_ioctl(struct video_device *vdev, unsigned int cmd, void *arg) return -EINVAL; if (vw.width != DEFAULT_WIDTH) return -EINVAL; +#endif + + /* If we're collecting previous frame wait + before changing modes */ + interruptible_sleep_on(&ov511->wq); + if (signal_pending(current)) return -EINTR; - ov511->compress = 0; + result = ov511_mode_init_regs(ov511, vw.width, vw.height, + ov511->frame[0].format, ov511->sub_flag); + if (result < 0) + return result; + + for (i = 0; i < OV511_NUMFRAMES; i++) { + ov511->frame[i].width = vw.width; + ov511->frame[i].height = vw.height; + } return 0; } @@ -1877,13 +1957,15 @@ static int ov511_ioctl(struct video_device *vdev, unsigned int cmd, void *arg) { struct video_window vw; - vw.x = 0; + vw.x = 0; /* FIXME */ vw.y = 0; - vw.width = DEFAULT_WIDTH; - vw.height = DEFAULT_HEIGHT; + vw.width = ov511->frame[0].width; + vw.height = ov511->frame[0].height; vw.chromakey = 0; vw.flags = 30; + PDEBUG (4, "VIDIOCGWIN: %dx%d", vw.width, vw.height); + if (copy_to_user(arg, &vw, sizeof(vw))) return -EFAULT; @@ -1935,19 +2017,16 @@ static int ov511_ioctl(struct video_device *vdev, unsigned int cmd, void *arg) before changing modes */ interruptible_sleep_on(&ov511->wq); if (signal_pending(current)) return -EINTR; - ov511_mode_init_regs(ov511, - vm.width, vm.height, - vm.format, ov511->sub_flag); + ov511_mode_init_regs(ov511, vm.width, vm.height, + vm.format, ov511->sub_flag); } ov511->frame[vm.frame].width = vm.width; ov511->frame[vm.frame].height = vm.height; ov511->frame[vm.frame].format = vm.format; ov511->frame[vm.frame].sub_flag = ov511->sub_flag; - ov511->frame[vm.frame].segsize = - vm.format == VIDEO_PALETTE_RGB24 ? 384 : 256; - ov511->frame[vm.frame].depth = - vm.format == VIDEO_PALETTE_RGB24 ? 3 : 1; + ov511->frame[vm.frame].segsize = GET_SEGSIZE(vm.format); + ov511->frame[vm.frame].depth = GET_DEPTH(vm.format); /* Mark it as ready */ ov511->frame[vm.frame].grabstate = FRAME_READY; @@ -1965,11 +2044,11 @@ static int ov511_ioctl(struct video_device *vdev, unsigned int cmd, void *arg) ov511->frame[frame].grabstate); switch (ov511->frame[frame].grabstate) { - case FRAME_UNUSED: - return -EINVAL; - case FRAME_READY: - case FRAME_GRABBING: - case FRAME_ERROR: + case FRAME_UNUSED: + return -EINVAL; + case FRAME_READY: + case FRAME_GRABBING: + case FRAME_ERROR: redo: if (!ov511->dev) return -EIO; @@ -1989,23 +2068,21 @@ redo: if ((ret = ov511_new_frame(ov511, frame)) < 0) return ret; goto redo; - } - case FRAME_DONE: - ov511->frame[frame].grabstate = FRAME_UNUSED; - break; - } - - ov511->frame[frame].grabstate = FRAME_UNUSED; - - /* Reset the hardware snapshot button */ - /* FIXME - Is this the best place for this? */ - if ((ov511->snap_enabled) && - (ov511->frame[frame].snapshot)) { - ov511->frame[frame].snapshot = 0; - ov511_reg_write(ov511->dev, 0x52, 0x01); - ov511_reg_write(ov511->dev, 0x52, 0x03); - ov511_reg_write(ov511->dev, 0x52, 0x01); - } + } + case FRAME_DONE: + ov511->frame[frame].grabstate = FRAME_UNUSED; + + /* Reset the hardware snapshot button */ + /* FIXME - Is this the best place for this? */ + if ((ov511->snap_enabled) && + (ov511->frame[frame].snapshot)) { + ov511->frame[frame].snapshot = 0; + ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_SNAPSHOT, 0x01); + ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_SNAPSHOT, 0x03); + ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_SNAPSHOT, 0x01); + } + break; + } /* end switch */ return 0; } @@ -2038,7 +2115,7 @@ redo: return -EINVAL; default: return -ENOIOCTLCMD; - } /* End switch(cmd) */ + } /* end switch */ return 0; } @@ -2610,7 +2687,7 @@ static void ov511_disconnect(struct usb_device *dev, void *ptr) ov511->sbuf[0].urb = NULL; } -#ifdef CONFIG_PROC_FS +#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS) destroy_proc_ov511_cam(ov511); #endif @@ -2637,8 +2714,7 @@ static struct usb_driver ov511_driver = { static int __init usb_ov511_init(void) { -#ifdef CONFIG_PROC_FS - PDEBUG(3, "creating /proc/ov511"); +#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS) proc_ov511_create(); #endif @@ -2655,7 +2731,7 @@ static void __exit usb_ov511_exit(void) usb_deregister(&ov511_driver); info("ov511 driver deregistered"); -#ifdef CONFIG_PROC_FS +#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS) proc_ov511_destroy(); #endif } |