Am 09.05.2014 11:17, schrieb Hans Verkuil: > Some comments for future improvements: > > On 03/24/2014 08:33 PM, Frank Schäfer wrote: >> Signed-off-by: Frank Schäfer <fschaefer.oss@xxxxxxxxxxxxxx> >> --- >> drivers/media/usb/em28xx/em28xx-camera.c | 4 +- >> drivers/media/usb/em28xx/em28xx-video.c | 160 +++++++++++++++++++++---------- >> drivers/media/usb/em28xx/em28xx.h | 8 +- >> 3 files changed, 116 insertions(+), 56 deletions(-) >> >> diff --git a/drivers/media/usb/em28xx/em28xx-camera.c b/drivers/media/usb/em28xx/em28xx-camera.c >> index 505e050..daebef3 100644 >> --- a/drivers/media/usb/em28xx/em28xx-camera.c >> +++ b/drivers/media/usb/em28xx/em28xx-camera.c >> @@ -365,7 +365,7 @@ int em28xx_init_camera(struct em28xx *dev) >> dev->sensor_xtal = 4300000; >> pdata.xtal = dev->sensor_xtal; >> if (NULL == >> - v4l2_i2c_new_subdev_board(&dev->v4l2_dev, adap, >> + v4l2_i2c_new_subdev_board(&dev->v4l2->v4l2_dev, adap, >> &mt9v011_info, NULL)) { >> ret = -ENODEV; >> break; >> @@ -422,7 +422,7 @@ int em28xx_init_camera(struct em28xx *dev) >> dev->sensor_yres = 480; >> >> subdev = >> - v4l2_i2c_new_subdev_board(&dev->v4l2_dev, adap, >> + v4l2_i2c_new_subdev_board(&dev->v4l2->v4l2_dev, adap, >> &ov2640_info, NULL); >> if (NULL == subdev) { >> ret = -ENODEV; >> diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c >> index 45ad471..89947db 100644 >> --- a/drivers/media/usb/em28xx/em28xx-video.c >> +++ b/drivers/media/usb/em28xx/em28xx-video.c >> @@ -189,10 +189,11 @@ static int em28xx_vbi_supported(struct em28xx *dev) >> */ >> static void em28xx_wake_i2c(struct em28xx *dev) >> { >> - v4l2_device_call_all(&dev->v4l2_dev, 0, core, reset, 0); >> - v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_routing, >> + struct v4l2_device *v4l2_dev = &dev->v4l2->v4l2_dev; >> + v4l2_device_call_all(v4l2_dev, 0, core, reset, 0); >> + v4l2_device_call_all(v4l2_dev, 0, video, s_routing, >> INPUT(dev->ctl_input)->vmux, 0, 0); >> - v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 0); >> + v4l2_device_call_all(v4l2_dev, 0, video, s_stream, 0); >> } >> >> static int em28xx_colorlevels_set_default(struct em28xx *dev) >> @@ -952,7 +953,8 @@ int em28xx_start_analog_streaming(struct vb2_queue *vq, unsigned int count) >> f.type = V4L2_TUNER_RADIO; >> else >> f.type = V4L2_TUNER_ANALOG_TV; >> - v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, &f); >> + v4l2_device_call_all(&dev->v4l2->v4l2_dev, >> + 0, tuner, s_frequency, &f); >> } >> >> dev->streaming_users++; >> @@ -1083,6 +1085,7 @@ static int em28xx_vb2_setup(struct em28xx *dev) >> >> static void video_mux(struct em28xx *dev, int index) >> { >> + struct v4l2_device *v4l2_dev = &dev->v4l2->v4l2_dev; >> dev->ctl_input = index; >> dev->ctl_ainput = INPUT(index)->amux; >> dev->ctl_aoutput = INPUT(index)->aout; >> @@ -1090,21 +1093,21 @@ static void video_mux(struct em28xx *dev, int index) >> if (!dev->ctl_aoutput) >> dev->ctl_aoutput = EM28XX_AOUT_MASTER; >> >> - v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_routing, >> + v4l2_device_call_all(v4l2_dev, 0, video, s_routing, >> INPUT(index)->vmux, 0, 0); >> >> if (dev->board.has_msp34xx) { >> if (dev->i2s_speed) { >> - v4l2_device_call_all(&dev->v4l2_dev, 0, audio, >> + v4l2_device_call_all(v4l2_dev, 0, audio, >> s_i2s_clock_freq, dev->i2s_speed); >> } >> /* Note: this is msp3400 specific */ >> - v4l2_device_call_all(&dev->v4l2_dev, 0, audio, s_routing, >> + v4l2_device_call_all(v4l2_dev, 0, audio, s_routing, >> dev->ctl_ainput, MSP_OUTPUT(MSP_SC_IN_DSP_SCART1), 0); >> } >> >> if (dev->board.adecoder != EM28XX_NOADECODER) { >> - v4l2_device_call_all(&dev->v4l2_dev, 0, audio, s_routing, >> + v4l2_device_call_all(v4l2_dev, 0, audio, s_routing, >> dev->ctl_ainput, dev->ctl_aoutput, 0); >> } >> >> @@ -1344,7 +1347,7 @@ static int vidioc_querystd(struct file *file, void *priv, v4l2_std_id *norm) >> struct em28xx_fh *fh = priv; >> struct em28xx *dev = fh->dev; >> >> - v4l2_device_call_all(&dev->v4l2_dev, 0, video, querystd, norm); >> + v4l2_device_call_all(&dev->v4l2->v4l2_dev, 0, video, querystd, norm); >> >> return 0; >> } >> @@ -1374,7 +1377,7 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id norm) >> size_to_scale(dev, dev->width, dev->height, &dev->hscale, &dev->vscale); >> >> em28xx_resolution_set(dev); >> - v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, dev->norm); >> + v4l2_device_call_all(&dev->v4l2->v4l2_dev, 0, core, s_std, dev->norm); >> >> return 0; >> } >> @@ -1388,7 +1391,7 @@ static int vidioc_g_parm(struct file *file, void *priv, >> >> p->parm.capture.readbuffers = EM28XX_MIN_BUF; >> if (dev->board.is_webcam) >> - rc = v4l2_device_call_until_err(&dev->v4l2_dev, 0, >> + rc = v4l2_device_call_until_err(&dev->v4l2->v4l2_dev, 0, >> video, g_parm, p); >> else >> v4l2_video_std_frame_period(dev->norm, >> @@ -1404,7 +1407,8 @@ static int vidioc_s_parm(struct file *file, void *priv, >> struct em28xx *dev = fh->dev; >> >> p->parm.capture.readbuffers = EM28XX_MIN_BUF; >> - return v4l2_device_call_until_err(&dev->v4l2_dev, 0, video, s_parm, p); >> + return v4l2_device_call_until_err(&dev->v4l2->v4l2_dev, >> + 0, video, s_parm, p); >> } >> >> static const char *iname[] = { >> @@ -1543,7 +1547,7 @@ static int vidioc_g_tuner(struct file *file, void *priv, >> >> strcpy(t->name, "Tuner"); >> >> - v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_tuner, t); >> + v4l2_device_call_all(&dev->v4l2->v4l2_dev, 0, tuner, g_tuner, t); >> return 0; >> } >> >> @@ -1556,7 +1560,7 @@ static int vidioc_s_tuner(struct file *file, void *priv, >> if (0 != t->index) >> return -EINVAL; >> >> - v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_tuner, t); >> + v4l2_device_call_all(&dev->v4l2->v4l2_dev, 0, tuner, s_tuner, t); >> return 0; >> } >> >> @@ -1576,15 +1580,16 @@ static int vidioc_g_frequency(struct file *file, void *priv, >> static int vidioc_s_frequency(struct file *file, void *priv, >> const struct v4l2_frequency *f) >> { >> - struct v4l2_frequency new_freq = *f; >> - struct em28xx_fh *fh = priv; >> - struct em28xx *dev = fh->dev; >> + struct v4l2_frequency new_freq = *f; >> + struct em28xx_fh *fh = priv; >> + struct em28xx *dev = fh->dev; >> + struct em28xx_v4l2 *v4l2 = dev->v4l2; >> >> if (0 != f->tuner) >> return -EINVAL; >> >> - v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, f); >> - v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_frequency, &new_freq); >> + v4l2_device_call_all(&v4l2->v4l2_dev, 0, tuner, s_frequency, f); >> + v4l2_device_call_all(&v4l2->v4l2_dev, 0, tuner, g_frequency, &new_freq); >> dev->ctl_freq = new_freq.frequency; >> >> return 0; >> @@ -1602,7 +1607,8 @@ static int vidioc_g_chip_info(struct file *file, void *priv, >> if (chip->match.addr == 1) >> strlcpy(chip->name, "ac97", sizeof(chip->name)); >> else >> - strlcpy(chip->name, dev->v4l2_dev.name, sizeof(chip->name)); >> + strlcpy(chip->name, >> + dev->v4l2->v4l2_dev.name, sizeof(chip->name)); >> return 0; >> } >> >> @@ -1814,7 +1820,7 @@ static int radio_g_tuner(struct file *file, void *priv, >> >> strcpy(t->name, "Radio"); >> >> - v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_tuner, t); >> + v4l2_device_call_all(&dev->v4l2->v4l2_dev, 0, tuner, g_tuner, t); >> >> return 0; >> } >> @@ -1827,12 +1833,26 @@ static int radio_s_tuner(struct file *file, void *priv, >> if (0 != t->index) >> return -EINVAL; >> >> - v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_tuner, t); >> + v4l2_device_call_all(&dev->v4l2->v4l2_dev, 0, tuner, s_tuner, t); >> >> return 0; >> } >> >> /* >> + * em28xx_free_v4l2() - Free struct em28xx_v4l2 >> + * >> + * @ref: struct kref for struct em28xx_v4l2 >> + * >> + * Called when all users of struct em28xx_v4l2 are gone >> + */ >> +void em28xx_free_v4l2(struct kref *ref) >> +{ >> + struct em28xx_v4l2 *v4l2 = container_of(ref, struct em28xx_v4l2, ref); >> + >> + kfree(v4l2); >> +} >> + >> +/* >> * em28xx_v4l2_open() >> * inits the device and starts isoc transfer >> */ >> @@ -1840,6 +1860,7 @@ static int em28xx_v4l2_open(struct file *filp) >> { >> struct video_device *vdev = video_devdata(filp); >> struct em28xx *dev = video_drvdata(filp); >> + struct em28xx_v4l2 *v4l2 = dev->v4l2; >> enum v4l2_buf_type fh_type = 0; >> struct em28xx_fh *fh; >> >> @@ -1888,10 +1909,11 @@ static int em28xx_v4l2_open(struct file *filp) >> >> if (vdev->vfl_type == VFL_TYPE_RADIO) { >> em28xx_videodbg("video_open: setting radio device\n"); >> - v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_radio); >> + v4l2_device_call_all(&v4l2->v4l2_dev, 0, tuner, s_radio); >> } >> >> kref_get(&dev->ref); >> + kref_get(&v4l2->ref); > I never like these kref things. Especially for usb devices I strongly recommend > using the release() callback from v4l2_device instead: this callback will only > be called once all references to video_device nodes have been closed. In other > words, only once all filehandles to /dev/videoX (and radio, vbi etc) are closed > will the release callback be called. > > As such it is a perfect place to put the final cleanup, and there is no more need > to mess around with krefs. The v4l2 submodule data struct can not be cleared before 1) the submodule is unloaded/unregistered AND 2) all users of all device nodes are gone. Using a kref is much easier (and also safer) than dealing with non-trivial case checks in em28xx_v4l2_fini() and the v4l2_device release() callbacks. What we could do is to call kref_get() only one time at the first open() of a device node and kref_put() only at the last close(). But it seems that this would just complicate the code without any real benefit. >> dev->users++; > The same for these user counters. You can use v4l2_fh_is_singular_file() to check > if the file open is the first file. However, this function assumes that v4l2_fh_add > has been called first. > > So for this driver it might be easier if we add a v4l2_fh_is_empty() to v4l2-fh.c > so we can call this before v4l2_fh_add. > > For that matter, you can almost certainly remove struct em28xx_fh altogether. > The type field of that struct can be determined by vdev->vfl_type and 'dev' can be > obtained via video_get_drvdata(). Yes, fields "dev" and "type" can definitly be removed from struct em28xx_fh. Then struct v4l2_fh fh is the last member, but I didn't have the time yet to take a deeper look at it. At a first glance its usage seems to be incomplete/broken. There are no v4l2_fh_del() and v4l2_fh_exit() calls and I wonder who deallocates the structs memory !? Regards, Frank > > Regards, > > Hans > >> >> mutex_unlock(&dev->lock); -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html