summaryrefslogtreecommitdiff
path: root/linux/drivers/media/video/em28xx/em28xx-video.c
diff options
context:
space:
mode:
authorMauro Carvalho Chehab <mchehab@infradead.org>2008-01-05 18:53:47 -0200
committerMauro Carvalho Chehab <mchehab@infradead.org>2008-01-05 18:53:47 -0200
commitfcb54f0fe6b8b57da03ef141b114f7c7be226dfd (patch)
treecac1c5245486aecf953562a7ab4bf1184f4a5bcd /linux/drivers/media/video/em28xx/em28xx-video.c
parentf6446935a2e144e7f845a951e80a9bf2d313ffda (diff)
parent76a1e5c0858e7b49c63708f044276f5142534b4b (diff)
downloadmediapointer-dvb-s2-fcb54f0fe6b8b57da03ef141b114f7c7be226dfd.tar.gz
mediapointer-dvb-s2-fcb54f0fe6b8b57da03ef141b114f7c7be226dfd.tar.bz2
merge: http://linuxtv.org/hg/~mkrufky/tda18271c2
From: Mauro Carvalho Chehab <mchehab@infradead.org> Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
Diffstat (limited to 'linux/drivers/media/video/em28xx/em28xx-video.c')
-rw-r--r--linux/drivers/media/video/em28xx/em28xx-video.c451
1 files changed, 365 insertions, 86 deletions
diff --git a/linux/drivers/media/video/em28xx/em28xx-video.c b/linux/drivers/media/video/em28xx/em28xx-video.c
index 44621bafc..77d547f81 100644
--- a/linux/drivers/media/video/em28xx/em28xx-video.c
+++ b/linux/drivers/media/video/em28xx/em28xx-video.c
@@ -66,26 +66,32 @@ static LIST_HEAD(em28xx_devlist);
static unsigned int card[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET };
static unsigned int video_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET };
-static unsigned int vbi_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET };
+static unsigned int vbi_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET };
+static unsigned int radio_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET };
+
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
-MODULE_PARM(card,"1-" __stringify(EM28XX_MAXBOARDS) "i");
-MODULE_PARM(video_nr,"1-" __stringify(EM28XX_MAXBOARDS) "i");
-MODULE_PARM(vbi_nr,"1-" __stringify(EM28XX_MAXBOARDS) "i");
+MODULE_PARM(card, "1-" __stringify(EM28XX_MAXBOARDS) "i");
+MODULE_PARM(video_nr, "1-" __stringify(EM28XX_MAXBOARDS) "i");
+MODULE_PARM(vbi_nr, "1-" __stringify(EM28XX_MAXBOARDS) "i");
+MODULE_PARM(radio_nr, "1-" __stringify(EM28XX_MAXBOARDS) "i");
#else
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10)
static int dummy;
module_param_array(card, int, dummy, 0444);
module_param_array(video_nr, int, dummy, 0444);
module_param_array(vbi_nr, int, dummy, 0444);
+module_param_array(radio_nr, int, dummy, 0444);
#else
module_param_array(card, int, NULL, 0444);
module_param_array(video_nr, int, NULL, 0444);
module_param_array(vbi_nr, int, NULL, 0444);
+module_param_array(radio_nr, int, NULL, 0444);
#endif
#endif
-MODULE_PARM_DESC(card,"card type");
-MODULE_PARM_DESC(video_nr,"video device numbers");
-MODULE_PARM_DESC(vbi_nr,"vbi device numbers");
+MODULE_PARM_DESC(card, "card type");
+MODULE_PARM_DESC(video_nr, "video device numbers");
+MODULE_PARM_DESC(vbi_nr, "vbi device numbers");
+MODULE_PARM_DESC(radio_nr, "radio device numbers");
static unsigned int video_debug = 0;
module_param(video_debug,int,0644);
@@ -142,11 +148,9 @@ static int em28xx_config(struct em28xx *dev)
em28xx_write_regs_req(dev,0x00,0x11,"\x51",1);
#endif
- em28xx_audio_usb_mute(dev, 1);
dev->mute = 1; /* maybe not the right place... */
dev->volume = 0x1f;
- em28xx_audio_analog_set(dev);
- em28xx_audio_analog_setup(dev);
+
em28xx_outfmt_set_yuv422(dev);
em28xx_colorlevels_set_default(dev);
em28xx_compression_disable(dev);
@@ -188,7 +192,6 @@ static void em28xx_empty_framequeues(struct em28xx *dev)
static void video_mux(struct em28xx *dev, int index)
{
- int ainput;
struct v4l2_routing route;
route.input = INPUT(index)->vmux;
@@ -205,18 +208,9 @@ static void video_mux(struct em28xx *dev, int index)
route.output = MSP_OUTPUT(MSP_SC_IN_DSP_SCART1);
/* Note: this is msp3400 specific */
em28xx_i2c_call_clients(dev, VIDIOC_INT_S_AUDIO_ROUTING, &route);
- ainput = EM28XX_AUDIO_SRC_TUNER;
- em28xx_audio_source(dev, ainput);
- } else {
- switch (dev->ctl_ainput) {
- case 0:
- ainput = EM28XX_AUDIO_SRC_TUNER;
- break;
- default:
- ainput = EM28XX_AUDIO_SRC_LINE;
- }
- em28xx_audio_source(dev, ainput);
}
+
+ em28xx_set_audio_source(dev);
}
/* Usage lock check functions */
@@ -312,7 +306,6 @@ static int em28xx_set_ctrl(struct em28xx *dev, const struct v4l2_control *ctrl)
case V4L2_CID_AUDIO_MUTE:
if (ctrl->value != dev->mute) {
dev->mute = ctrl->value;
- em28xx_audio_usb_mute(dev, ctrl->value);
return em28xx_audio_analog_set(dev);
}
return 0;
@@ -824,7 +817,7 @@ static int vidioc_g_frequency(struct file *file, void *priv,
struct em28xx_fh *fh = priv;
struct em28xx *dev = fh->dev;
- f->type = V4L2_TUNER_ANALOG_TV;
+ f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
f->frequency = dev->ctl_freq;
return 0;
@@ -844,7 +837,9 @@ static int vidioc_s_frequency(struct file *file, void *priv,
if (0 != f->tuner)
return -EINVAL;
- if (V4L2_TUNER_ANALOG_TV != f->type)
+ if (unlikely(0 == fh->radio && f->type != V4L2_TUNER_ANALOG_TV))
+ return -EINVAL;
+ if (unlikely(1 == fh->radio && f->type != V4L2_TUNER_RADIO))
return -EINVAL;
mutex_lock(&dev->lock);
@@ -1218,6 +1213,102 @@ static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)
return 0;
}
+/* ----------------------------------------------------------- */
+/* RADIO ESPECIFIC IOCTLS */
+/* ----------------------------------------------------------- */
+
+static int radio_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct em28xx *dev = ((struct em28xx_fh *)priv)->dev;
+
+ strlcpy(cap->driver, "em28xx", sizeof(cap->driver));
+ strlcpy(cap->card, em28xx_boards[dev->model].name, sizeof(cap->card));
+ strlcpy(cap->bus_info, dev->udev->dev.bus_id, sizeof(cap->bus_info));
+
+ cap->version = EM28XX_VERSION_CODE;
+ cap->capabilities = V4L2_CAP_TUNER;
+ return 0;
+}
+
+static int radio_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct em28xx *dev = ((struct em28xx_fh *)priv)->dev;
+
+ if (unlikely(t->index > 0))
+ return -EINVAL;
+
+ strcpy(t->name, "Radio");
+ t->type = V4L2_TUNER_RADIO;
+
+ em28xx_i2c_call_clients(dev, VIDIOC_G_TUNER, t);
+ return 0;
+}
+
+static int radio_enum_input(struct file *file, void *priv,
+ struct v4l2_input *i)
+{
+ if (i->index != 0)
+ return -EINVAL;
+ strcpy(i->name, "Radio");
+ i->type = V4L2_INPUT_TYPE_TUNER;
+
+ return 0;
+}
+
+static int radio_g_audio(struct file *file, void *priv, struct v4l2_audio *a)
+{
+ if (unlikely(a->index))
+ return -EINVAL;
+
+ strcpy(a->name, "Radio");
+ return 0;
+}
+
+static int radio_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct em28xx *dev = ((struct em28xx_fh *)priv)->dev;
+
+ if (0 != t->index)
+ return -EINVAL;
+
+ em28xx_i2c_call_clients(dev, VIDIOC_S_TUNER, t);
+
+ return 0;
+}
+
+static int radio_s_audio(struct file *file, void *fh,
+ struct v4l2_audio *a)
+{
+ return 0;
+}
+
+static int radio_s_input(struct file *file, void *fh, unsigned int i)
+{
+ return 0;
+}
+
+static int radio_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *qc)
+{
+ int i;
+
+ if (qc->id < V4L2_CID_BASE ||
+ qc->id >= V4L2_CID_LASTP1)
+ return -EINVAL;
+
+ for (i = 0; i < ARRAY_SIZE(em28xx_qctrl); i++) {
+ if (qc->id && qc->id == em28xx_qctrl[i].id) {
+ memcpy(qc, &(em28xx_qctrl[i]), sizeof(*qc));
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
/*
* em28xx_v4l2_open()
* inits the device and starts isoc transfer
@@ -1225,7 +1316,7 @@ static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)
static int em28xx_v4l2_open(struct inode *inode, struct file *filp)
{
int minor = iminor(inode);
- int errCode = 0;
+ int errCode = 0, radio = 0;
struct em28xx *h,*dev = NULL;
struct em28xx_fh *fh;
@@ -1238,6 +1329,11 @@ static int em28xx_v4l2_open(struct inode *inode, struct file *filp)
dev = h;
dev->type = V4L2_BUF_TYPE_VBI_CAPTURE;
}
+ if (h->radio_dev &&
+ h->radio_dev->minor == minor) {
+ radio = 1;
+ dev = h;
+ }
}
if (NULL == dev)
return -ENODEV;
@@ -1253,6 +1349,7 @@ static int em28xx_v4l2_open(struct inode *inode, struct file *filp)
}
mutex_lock(&dev->lock);
fh->dev = dev;
+ fh->radio = radio;
filp->private_data = fh;
if (dev->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && dev->users == 0) {
@@ -1281,6 +1378,13 @@ static int em28xx_v4l2_open(struct inode *inode, struct file *filp)
em28xx_empty_framequeues(dev);
}
+ if (fh->radio) {
+ em28xx_videodbg("video_open: setting radio device\n");
+#if 0
+ em28xx_start_radio(dev);
+#endif
+ em28xx_i2c_call_clients(dev, AUDC_SET_RADIO, NULL);
+ }
dev->users++;
@@ -1303,16 +1407,30 @@ static void em28xx_release_resources(struct em28xx *dev)
dev->vdev->minor-MINOR_VFL_TYPE_GRABBER_MIN,
dev->vbi_dev->minor-MINOR_VFL_TYPE_VBI_MIN);
list_del(&dev->devlist);
- video_unregister_device(dev->vdev);
- video_unregister_device(dev->vbi_dev);
+ if (dev->radio_dev) {
+ if (-1 != dev->radio_dev->minor)
+ video_unregister_device(dev->radio_dev);
+ else
+ video_device_release(dev->radio_dev);
+ dev->radio_dev = NULL;
+ }
+ if (dev->vbi_dev) {
+ if (-1 != dev->vbi_dev->minor)
+ video_unregister_device(dev->vbi_dev);
+ else
+ video_device_release(dev->vbi_dev);
+ dev->vbi_dev = NULL;
+ }
+ if (dev->vdev) {
+ if (-1 != dev->vdev->minor)
+ video_unregister_device(dev->vdev);
+ else
+ video_device_release(dev->vdev);
+ dev->vdev = NULL;
+ }
em28xx_i2c_unregister(dev);
usb_put_dev(dev->udev);
-#if 0 /* Fixme: disallocating these generates kernel hang */
- kfree (dev->vdev);
- kfree (dev->vbi_dev);
-#endif
-
/* Mark device as unused */
em28xx_devused&=~(1<<dev->devno);
}
@@ -1635,6 +1753,17 @@ static const struct file_operations em28xx_v4l_fops = {
#endif
};
+static const struct file_operations radio_fops = {
+ .owner = THIS_MODULE,
+ .open = em28xx_v4l2_open,
+ .release = em28xx_v4l2_close,
+ .ioctl = video_ioctl2,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 11)
+ .compat_ioctl = v4l_compat_ioctl32,
+#endif
+ .llseek = no_llseek,
+};
+
static const struct video_device em28xx_video_template = {
.fops = &em28xx_v4l_fops,
.release = video_device_release,
@@ -1680,9 +1809,92 @@ static const struct video_device em28xx_video_template = {
.current_norm = V4L2_STD_PAL,
};
+static struct video_device em28xx_radio_template = {
+ .name = "em28xx-radio",
+ .type = VID_TYPE_TUNER,
+ .fops = &radio_fops,
+ .minor = -1,
+ .vidioc_querycap = radio_querycap,
+ .vidioc_g_tuner = radio_g_tuner,
+ .vidioc_enum_input = radio_enum_input,
+ .vidioc_g_audio = radio_g_audio,
+ .vidioc_s_tuner = radio_s_tuner,
+ .vidioc_s_audio = radio_s_audio,
+ .vidioc_s_input = radio_s_input,
+ .vidioc_queryctrl = radio_queryctrl,
+ .vidioc_g_ctrl = vidioc_g_ctrl,
+ .vidioc_s_ctrl = vidioc_s_ctrl,
+ .vidioc_g_frequency = vidioc_g_frequency,
+ .vidioc_s_frequency = vidioc_s_frequency,
+};
/******************************** usb interface *****************************************/
+
+static LIST_HEAD(em28xx_extension_devlist);
+static DEFINE_MUTEX(em28xx_extension_devlist_lock);
+
+int em28xx_register_extension(struct em28xx_ops *ops)
+{
+ struct em28xx *h, *dev = NULL;
+
+ list_for_each_entry(h, &em28xx_devlist, devlist)
+ dev = h;
+
+ mutex_lock(&em28xx_extension_devlist_lock);
+ list_add_tail(&ops->next, &em28xx_extension_devlist);
+ if (dev)
+ ops->init(dev);
+
+ printk(KERN_INFO "Em28xx: Initialized (%s) extension\n", ops->name);
+ mutex_unlock(&em28xx_extension_devlist_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL(em28xx_register_extension);
+
+void em28xx_unregister_extension(struct em28xx_ops *ops)
+{
+ struct em28xx *h, *dev = NULL;
+
+ list_for_each_entry(h, &em28xx_devlist, devlist)
+ dev = h;
+
+ if (dev)
+ ops->fini(dev);
+
+ mutex_lock(&em28xx_extension_devlist_lock);
+ printk(KERN_INFO "Em28xx: Removed (%s) extension\n", ops->name);
+ list_del(&ops->next);
+ mutex_unlock(&em28xx_extension_devlist_lock);
+}
+EXPORT_SYMBOL(em28xx_unregister_extension);
+
+struct video_device *em28xx_vdev_init(struct em28xx *dev,
+ const struct video_device *template,
+ const int type,
+ const char *type_name)
+{
+ struct video_device *vfd;
+
+ vfd = video_device_alloc();
+ if (NULL == vfd)
+ return NULL;
+ *vfd = *template;
+ vfd->minor = -1;
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)
+ vfd->dev = &dev->udev->dev;
+ vfd->release = video_device_release;
+#endif
+ vfd->type = type;
+
+ snprintf(vfd->name, sizeof(vfd->name), "%s %s",
+ dev->name, type_name);
+
+ return vfd;
+}
+
+
/*
* em28xx_init_dev()
* allocates and inits the device structs, registers i2c bus and v4l device
@@ -1690,6 +1902,7 @@ static const struct video_device em28xx_video_template = {
static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
int minor)
{
+ struct em28xx_ops *ops = NULL;
struct em28xx *dev = *devhandle;
int retval = -ENOMEM;
int errCode;
@@ -1709,6 +1922,10 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
dev->em28xx_read_reg_req = em28xx_read_reg_req;
dev->is_em2800 = em28xx_boards[dev->model].is_em2800;
+ errCode = em28xx_read_reg(dev, CHIPID_REG);
+ if (errCode >= 0)
+ em28xx_info("em28xx chip ID = %d\n", errCode);
+
em28xx_pre_card_setup(dev);
errCode = em28xx_config(dev);
@@ -1725,6 +1942,9 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
/* Do board specific init and eeprom reading */
em28xx_card_setup(dev);
+ /* Configure audio */
+ em28xx_audio_analog_set(dev);
+
/* configure the device */
em28xx_config_i2c(dev);
@@ -1748,40 +1968,55 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
errCode = em28xx_config(dev);
+ list_add_tail(&dev->devlist, &em28xx_devlist);
+
/* allocate and fill video video_device struct */
- dev->vdev = video_device_alloc();
+ dev->vdev = em28xx_vdev_init(dev, &em28xx_video_template,
+ VID_TYPE_CAPTURE, "video");
if (NULL == dev->vdev) {
em28xx_errdev("cannot allocate video_device.\n");
- em28xx_devused&=~(1<<dev->devno);
- kfree(dev);
- return -ENOMEM;
+ goto fail_unreg;
}
- memcpy(dev->vdev, &em28xx_video_template,
- sizeof(em28xx_video_template));
- dev->vdev->type = VID_TYPE_CAPTURE;
if (dev->has_tuner)
dev->vdev->type |= VID_TYPE_TUNER;
- dev->vdev->dev = &dev->udev->dev;
- snprintf(dev->vdev->name, sizeof(dev->vbi_dev->name),
- "%s#%d %s", "em28xx", dev->devno, "video");
+
+ /* register v4l2 video video_device */
+ retval = video_register_device(dev->vdev, VFL_TYPE_GRABBER,
+ video_nr[dev->devno]);
+ if (retval) {
+ em28xx_errdev("unable to register video device (error=%i).\n",
+ retval);
+ goto fail_unreg;
+ }
/* Allocate and fill vbi video_device struct */
- dev->vbi_dev = video_device_alloc();
- if (NULL == dev->vbi_dev) {
- em28xx_errdev("cannot allocate video_device.\n");
- kfree(dev->vdev);
- em28xx_devused&=~(1<<dev->devno);
- kfree(dev);
- return -ENOMEM;
+ dev->vbi_dev = em28xx_vdev_init(dev, &em28xx_video_template,
+ VFL_TYPE_VBI, "vbi");
+ /* register v4l2 vbi video_device */
+ if (video_register_device(dev->vbi_dev, VFL_TYPE_VBI,
+ vbi_nr[dev->devno]) < 0) {
+ em28xx_errdev("unable to register vbi device\n");
+ retval = -ENODEV;
+ goto fail_unreg;
+ }
+
+ if (em28xx_boards[dev->model].radio.type == EM28XX_RADIO) {
+ dev->radio_dev = em28xx_vdev_init(dev, &em28xx_radio_template,
+ VFL_TYPE_RADIO, "radio");
+ if (NULL == dev->radio_dev) {
+ em28xx_errdev("cannot allocate video_device.\n");
+ goto fail_unreg;
+ }
+ retval = video_register_device(dev->radio_dev, VFL_TYPE_RADIO,
+ radio_nr[dev->devno]);
+ if (retval < 0) {
+ em28xx_errdev("can't register radio device\n");
+ goto fail_unreg;
+ }
+ em28xx_info("Registered radio device as /dev/radio%d\n",
+ dev->radio_dev->minor & 0x1f);
}
- memcpy(dev->vbi_dev, &em28xx_video_template,
- sizeof(em28xx_video_template));
- dev->vbi_dev->type = VFL_TYPE_VBI;
- dev->vbi_dev->dev = &dev->udev->dev;
- snprintf(dev->vbi_dev->name, sizeof(dev->vbi_dev->name),
- "%s#%d %s", "em28xx", dev->devno, "vbi");
- list_add_tail(&dev->devlist,&em28xx_devlist);
#if 0
video_set_drvdata(dev->vbi_dev, dev);
#endif
@@ -1796,39 +2031,62 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
video_mux(dev, 0);
- /* register v4l2 video video_device */
- if ((retval = video_register_device(dev->vdev, VFL_TYPE_GRABBER,
- video_nr[dev->devno]))) {
- em28xx_errdev("unable to register video device (error=%i).\n",
- retval);
- mutex_unlock(&dev->lock);
- list_del(&dev->devlist);
- video_device_release(dev->vdev);
- em28xx_devused&=~(1<<dev->devno);
- kfree(dev);
- return -ENODEV;
- }
-
- /* register v4l2 vbi video_device */
- if (video_register_device(dev->vbi_dev, VFL_TYPE_VBI,
- vbi_nr[dev->devno]) < 0) {
- printk("unable to register vbi device\n");
- mutex_unlock(&dev->lock);
- list_del(&dev->devlist);
- video_device_release(dev->vbi_dev);
- video_device_release(dev->vdev);
- em28xx_devused&=~(1<<dev->devno);
- kfree(dev);
- return -ENODEV;
- }
-
em28xx_info("V4L2 device registered as /dev/video%d and /dev/vbi%d\n",
dev->vdev->minor-MINOR_VFL_TYPE_GRABBER_MIN,
dev->vbi_dev->minor-MINOR_VFL_TYPE_VBI_MIN);
+ mutex_lock(&em28xx_extension_devlist_lock);
+ if (!list_empty(&em28xx_extension_devlist)) {
+ list_for_each_entry(ops, &em28xx_extension_devlist, next) {
+ if (ops->id)
+ ops->init(dev);
+ }
+ }
+ mutex_unlock(&em28xx_extension_devlist_lock);
+
return 0;
+
+fail_unreg:
+ em28xx_release_resources(dev);
+ mutex_unlock(&dev->lock);
+ kfree(dev);
+ return retval;
}
+#if defined(CONFIG_MODULES) && defined(MODULE)
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
+static void request_module_async(void *ptr)
+{
+ struct em28xx *dev = (struct em28xx *)ptr;
+#else
+static void request_module_async(struct work_struct *work)
+{
+ struct em28xx *dev = container_of(work,
+ struct em28xx, request_module_wk);
+#endif
+
+ if (!dev->has_audio_class)
+ request_module("em28xx-alsa");
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0)
+#define request_modules(dev)
+#else
+static void request_modules(struct em28xx *dev)
+{
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
+ INIT_WORK(&dev->request_module_wk, request_module_async,
+ (void *)dev);
+#else
+ INIT_WORK(&dev->request_module_wk, request_module_async);
+#endif
+ schedule_work(&dev->request_module_wk);
+}
+#endif
+#else
+#define request_modules(dev)
+#endif /* CONFIG_MODULES */
+
/*
* em28xx_usb_probe()
* checks for supported devices
@@ -1899,6 +2157,18 @@ static int em28xx_usb_probe(struct usb_interface *interface,
dev->devno = nr;
dev->model = id->driver_info;
+ /* Checks if audio is provided by some interface */
+ for (i = 0; i < udev->config->desc.bNumInterfaces; i++) {
+ uif = udev->config->interface[i];
+ if (uif->altsetting[0].desc.bInterfaceClass == USB_CLASS_AUDIO) {
+ dev->has_audio_class = 1;
+ break;
+ }
+ }
+
+ printk(KERN_INFO DRIVER_NAME " %s usb audio class\n",
+ dev->has_audio_class ? "Has" : "Doesn't have");
+
/* compute alternate max packet sizes */
uif = udev->actconfig->interface[0];
@@ -1935,6 +2205,9 @@ static int em28xx_usb_probe(struct usb_interface *interface,
/* save our data pointer in this interface device */
usb_set_intfdata(interface, dev);
+
+ request_modules(dev);
+
return 0;
}
@@ -1946,6 +2219,7 @@ static int em28xx_usb_probe(struct usb_interface *interface,
static void em28xx_usb_disconnect(struct usb_interface *interface)
{
struct em28xx *dev;
+ struct em28xx_ops *ops = NULL;
dev = usb_get_intfdata(interface);
usb_set_intfdata(interface, NULL);
@@ -1975,15 +2249,20 @@ static void em28xx_usb_disconnect(struct usb_interface *interface)
dev->state |= DEV_DISCONNECTED;
em28xx_release_resources(dev);
}
-
-
mutex_unlock(&dev->lock);
+ mutex_lock(&em28xx_extension_devlist_lock);
+ if (!list_empty(&em28xx_extension_devlist)) {
+ list_for_each_entry(ops, &em28xx_extension_devlist, next) {
+ ops->fini(dev);
+ }
+ }
+ mutex_unlock(&em28xx_extension_devlist_lock);
+
if (!dev->users) {
kfree(dev->alt_max_pkt_size);
kfree(dev);
}
-
}
static struct usb_driver em28xx_usb_driver = {