diff options
| author | Hans de Goede <hdegoede@redhat.com> | 2011-06-06 13:43:39 -0400 |
|---|---|---|
| committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2011-06-21 12:31:12 -0400 |
| commit | 9a7b2d1f0eb0a6b674726c9a9d77ce83fd0b27fe (patch) | |
| tree | f2fd1bad0dcfd8600cccdd9e5619a4a3a6c4d5ae | |
| parent | e76e4706cf9051e4db12c3d4418fcfbb053fc463 (diff) | |
[media] pwc: better usb disconnect handling
Unplugging a pwc cam while an app has the /dev/video# node open leads
to an oops in pwc_video_close when the app closes the node, because
the disconnect handler has free-ed the pdev struct pwc_video_close
tries to use. Instead of adding some sort of bandaid for this.
fix it properly using the v4l2 core's new(ish) behavior of keeping the
v4l2_dev structure around until both unregister has been called, and
all file handles referring to it have been closed:
Embed the v4l2_dev structure in the pdev structure and define a v4l2 dev
release callback releasing the pdev structure (and thus also the embedded
v4l2 dev structure.
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Cc: stable@kernel.org
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
| -rw-r--r-- | drivers/media/video/pwc/pwc-ctrl.c | 2 | ||||
| -rw-r--r-- | drivers/media/video/pwc/pwc-if.c | 152 | ||||
| -rw-r--r-- | drivers/media/video/pwc/pwc.h | 4 |
3 files changed, 50 insertions, 108 deletions
diff --git a/drivers/media/video/pwc/pwc-ctrl.c b/drivers/media/video/pwc/pwc-ctrl.c index 1593f8deb810..760b4de13adf 100644 --- a/drivers/media/video/pwc/pwc-ctrl.c +++ b/drivers/media/video/pwc/pwc-ctrl.c | |||
| @@ -1414,7 +1414,7 @@ long pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg) | |||
| 1414 | { | 1414 | { |
| 1415 | ARG_DEF(struct pwc_probe, probe) | 1415 | ARG_DEF(struct pwc_probe, probe) |
| 1416 | 1416 | ||
| 1417 | strcpy(ARGR(probe).name, pdev->vdev->name); | 1417 | strcpy(ARGR(probe).name, pdev->vdev.name); |
| 1418 | ARGR(probe).type = pdev->type; | 1418 | ARGR(probe).type = pdev->type; |
| 1419 | ARG_OUT(probe) | 1419 | ARG_OUT(probe) |
| 1420 | break; | 1420 | break; |
diff --git a/drivers/media/video/pwc/pwc-if.c b/drivers/media/video/pwc/pwc-if.c index 356cd42b593b..b0bde5a87c8a 100644 --- a/drivers/media/video/pwc/pwc-if.c +++ b/drivers/media/video/pwc/pwc-if.c | |||
| @@ -40,7 +40,7 @@ | |||
| 40 | Oh yes, convention: to disctinguish between all the various pointers to | 40 | Oh yes, convention: to disctinguish between all the various pointers to |
| 41 | device-structures, I use these names for the pointer variables: | 41 | device-structures, I use these names for the pointer variables: |
| 42 | udev: struct usb_device * | 42 | udev: struct usb_device * |
| 43 | vdev: struct video_device * | 43 | vdev: struct video_device (member of pwc_dev) |
| 44 | pdev: struct pwc_devive * | 44 | pdev: struct pwc_devive * |
| 45 | */ | 45 | */ |
| 46 | 46 | ||
| @@ -152,6 +152,7 @@ static ssize_t pwc_video_read(struct file *file, char __user *buf, | |||
| 152 | size_t count, loff_t *ppos); | 152 | size_t count, loff_t *ppos); |
| 153 | static unsigned int pwc_video_poll(struct file *file, poll_table *wait); | 153 | static unsigned int pwc_video_poll(struct file *file, poll_table *wait); |
| 154 | static int pwc_video_mmap(struct file *file, struct vm_area_struct *vma); | 154 | static int pwc_video_mmap(struct file *file, struct vm_area_struct *vma); |
| 155 | static void pwc_video_release(struct video_device *vfd); | ||
| 155 | 156 | ||
| 156 | static const struct v4l2_file_operations pwc_fops = { | 157 | static const struct v4l2_file_operations pwc_fops = { |
| 157 | .owner = THIS_MODULE, | 158 | .owner = THIS_MODULE, |
| @@ -164,42 +165,12 @@ static const struct v4l2_file_operations pwc_fops = { | |||
| 164 | }; | 165 | }; |
| 165 | static struct video_device pwc_template = { | 166 | static struct video_device pwc_template = { |
| 166 | .name = "Philips Webcam", /* Filled in later */ | 167 | .name = "Philips Webcam", /* Filled in later */ |
| 167 | .release = video_device_release, | 168 | .release = pwc_video_release, |
| 168 | .fops = &pwc_fops, | 169 | .fops = &pwc_fops, |
| 170 | .ioctl_ops = &pwc_ioctl_ops, | ||
| 169 | }; | 171 | }; |
| 170 | 172 | ||
| 171 | /***************************************************************************/ | 173 | /***************************************************************************/ |
| 172 | |||
| 173 | /* Okay, this is some magic that I worked out and the reasoning behind it... | ||
| 174 | |||
| 175 | The biggest problem with any USB device is of course: "what to do | ||
| 176 | when the user unplugs the device while it is in use by an application?" | ||
| 177 | We have several options: | ||
| 178 | 1) Curse them with the 7 plagues when they do (requires divine intervention) | ||
| 179 | 2) Tell them not to (won't work: they'll do it anyway) | ||
| 180 | 3) Oops the kernel (this will have a negative effect on a user's uptime) | ||
| 181 | 4) Do something sensible. | ||
| 182 | |||
| 183 | Of course, we go for option 4. | ||
| 184 | |||
| 185 | It happens that this device will be linked to two times, once from | ||
| 186 | usb_device and once from the video_device in their respective 'private' | ||
| 187 | pointers. This is done when the device is probed() and all initialization | ||
| 188 | succeeded. The pwc_device struct links back to both structures. | ||
| 189 | |||
| 190 | When a device is unplugged while in use it will be removed from the | ||
| 191 | list of known USB devices; I also de-register it as a V4L device, but | ||
| 192 | unfortunately I can't free the memory since the struct is still in use | ||
| 193 | by the file descriptor. This free-ing is then deferend until the first | ||
| 194 | opportunity. Crude, but it works. | ||
| 195 | |||
| 196 | A small 'advantage' is that if a user unplugs the cam and plugs it back | ||
| 197 | in, it should get assigned the same video device minor, but unfortunately | ||
| 198 | it's non-trivial to re-link the cam back to the video device... (that | ||
| 199 | would surely be magic! :)) | ||
| 200 | */ | ||
| 201 | |||
| 202 | /***************************************************************************/ | ||
| 203 | /* Private functions */ | 174 | /* Private functions */ |
| 204 | 175 | ||
| 205 | /* Here we want the physical address of the memory. | 176 | /* Here we want the physical address of the memory. |
| @@ -1016,16 +987,15 @@ static ssize_t show_snapshot_button_status(struct device *class_dev, | |||
| 1016 | static DEVICE_ATTR(button, S_IRUGO | S_IWUSR, show_snapshot_button_status, | 987 | static DEVICE_ATTR(button, S_IRUGO | S_IWUSR, show_snapshot_button_status, |
| 1017 | NULL); | 988 | NULL); |
| 1018 | 989 | ||
| 1019 | static int pwc_create_sysfs_files(struct video_device *vdev) | 990 | static int pwc_create_sysfs_files(struct pwc_device *pdev) |
| 1020 | { | 991 | { |
| 1021 | struct pwc_device *pdev = video_get_drvdata(vdev); | ||
| 1022 | int rc; | 992 | int rc; |
| 1023 | 993 | ||
| 1024 | rc = device_create_file(&vdev->dev, &dev_attr_button); | 994 | rc = device_create_file(&pdev->vdev.dev, &dev_attr_button); |
| 1025 | if (rc) | 995 | if (rc) |
| 1026 | goto err; | 996 | goto err; |
| 1027 | if (pdev->features & FEATURE_MOTOR_PANTILT) { | 997 | if (pdev->features & FEATURE_MOTOR_PANTILT) { |
| 1028 | rc = device_create_file(&vdev->dev, &dev_attr_pan_tilt); | 998 | rc = device_create_file(&pdev->vdev.dev, &dev_attr_pan_tilt); |
| 1029 | if (rc) | 999 | if (rc) |
| 1030 | goto err_button; | 1000 | goto err_button; |
| 1031 | } | 1001 | } |
| @@ -1033,19 +1003,17 @@ static int pwc_create_sysfs_files(struct video_device *vdev) | |||
| 1033 | return 0; | 1003 | return 0; |
| 1034 | 1004 | ||
| 1035 | err_button: | 1005 | err_button: |
| 1036 | device_remove_file(&vdev->dev, &dev_attr_button); | 1006 | device_remove_file(&pdev->vdev.dev, &dev_attr_button); |
| 1037 | err: | 1007 | err: |
| 1038 | PWC_ERROR("Could not create sysfs files.\n"); | 1008 | PWC_ERROR("Could not create sysfs files.\n"); |
| 1039 | return rc; | 1009 | return rc; |
| 1040 | } | 1010 | } |
| 1041 | 1011 | ||
| 1042 | static void pwc_remove_sysfs_files(struct video_device *vdev) | 1012 | static void pwc_remove_sysfs_files(struct pwc_device *pdev) |
| 1043 | { | 1013 | { |
| 1044 | struct pwc_device *pdev = video_get_drvdata(vdev); | ||
| 1045 | |||
| 1046 | if (pdev->features & FEATURE_MOTOR_PANTILT) | 1014 | if (pdev->features & FEATURE_MOTOR_PANTILT) |
| 1047 | device_remove_file(&vdev->dev, &dev_attr_pan_tilt); | 1015 | device_remove_file(&pdev->vdev.dev, &dev_attr_pan_tilt); |
| 1048 | device_remove_file(&vdev->dev, &dev_attr_button); | 1016 | device_remove_file(&pdev->vdev.dev, &dev_attr_button); |
| 1049 | } | 1017 | } |
| 1050 | 1018 | ||
| 1051 | #ifdef CONFIG_USB_PWC_DEBUG | 1019 | #ifdef CONFIG_USB_PWC_DEBUG |
| @@ -1106,7 +1074,7 @@ static int pwc_video_open(struct file *file) | |||
| 1106 | if (ret >= 0) | 1074 | if (ret >= 0) |
| 1107 | { | 1075 | { |
| 1108 | PWC_DEBUG_OPEN("This %s camera is equipped with a %s (%d).\n", | 1076 | PWC_DEBUG_OPEN("This %s camera is equipped with a %s (%d).\n", |
| 1109 | pdev->vdev->name, | 1077 | pdev->vdev.name, |
| 1110 | pwc_sensor_type_to_string(i), i); | 1078 | pwc_sensor_type_to_string(i), i); |
| 1111 | } | 1079 | } |
| 1112 | } | 1080 | } |
| @@ -1180,16 +1148,15 @@ static int pwc_video_open(struct file *file) | |||
| 1180 | return 0; | 1148 | return 0; |
| 1181 | } | 1149 | } |
| 1182 | 1150 | ||
| 1183 | 1151 | static void pwc_video_release(struct video_device *vfd) | |
| 1184 | static void pwc_cleanup(struct pwc_device *pdev) | ||
| 1185 | { | 1152 | { |
| 1186 | pwc_remove_sysfs_files(pdev->vdev); | 1153 | struct pwc_device *pdev = container_of(vfd, struct pwc_device, vdev); |
| 1187 | video_unregister_device(pdev->vdev); | 1154 | int hint; |
| 1188 | 1155 | ||
| 1189 | #ifdef CONFIG_USB_PWC_INPUT_EVDEV | 1156 | /* search device_hint[] table if we occupy a slot, by any chance */ |
| 1190 | if (pdev->button_dev) | 1157 | for (hint = 0; hint < MAX_DEV_HINTS; hint++) |
| 1191 | input_unregister_device(pdev->button_dev); | 1158 | if (device_hint[hint].pdev == pdev) |
| 1192 | #endif | 1159 | device_hint[hint].pdev = NULL; |
| 1193 | 1160 | ||
| 1194 | kfree(pdev); | 1161 | kfree(pdev); |
