aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/video/pwc/pwc-if.c
diff options
context:
space:
mode:
authorOliver Neukum <oliver@neukum.org>2007-09-26 09:19:01 -0400
committerMauro Carvalho Chehab <mchehab@infradead.org>2007-10-09 21:14:49 -0400
commit0b67f5c568c545cb36f88e9f418af2df1cc58589 (patch)
tree71fddc45a7e9ea0d5b58c06577e155824b4d7a09 /drivers/media/video/pwc/pwc-if.c
parent23869e236846657415654e8f5fbda9faec8d19e4 (diff)
V4L/DVB (6237): Oops in pwc v4l driver
The pwc driver is defficient in locking, which can trigger an oops when disconnecting. Signed-off-by: Oliver Neukum <oneukum@suse.de> CC: Luc Saillard <luc@saillard.org> Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
Diffstat (limited to 'drivers/media/video/pwc/pwc-if.c')
-rw-r--r--drivers/media/video/pwc/pwc-if.c96
1 files changed, 72 insertions, 24 deletions
diff --git a/drivers/media/video/pwc/pwc-if.c b/drivers/media/video/pwc/pwc-if.c
index 1088ebf5744f..0ff5718bf9b9 100644
--- a/drivers/media/video/pwc/pwc-if.c
+++ b/drivers/media/video/pwc/pwc-if.c
@@ -907,31 +907,49 @@ int pwc_isoc_init(struct pwc_device *pdev)
907 return 0; 907 return 0;
908} 908}
909 909
910void pwc_isoc_cleanup(struct pwc_device *pdev) 910static void pwc_iso_stop(struct pwc_device *pdev)
911{ 911{
912 int i; 912 int i;
913 913
914 PWC_DEBUG_OPEN(">> pwc_isoc_cleanup()\n");
915 if (pdev == NULL)
916 return;
917 if (pdev->iso_init == 0)
918 return;
919
920 /* Unlinking ISOC buffers one by one */ 914 /* Unlinking ISOC buffers one by one */
921 for (i = 0; i < MAX_ISO_BUFS; i++) { 915 for (i = 0; i < MAX_ISO_BUFS; i++) {
922 struct urb *urb; 916 struct urb *urb;
923 917
924 urb = pdev->sbuf[i].urb; 918 urb = pdev->sbuf[i].urb;
925 if (urb != 0) { 919 if (urb != 0) {
926 if (pdev->iso_init) { 920 PWC_DEBUG_MEMORY("Unlinking URB %p\n", urb);
927 PWC_DEBUG_MEMORY("Unlinking URB %p\n", urb); 921 usb_kill_urb(urb);
928 usb_kill_urb(urb); 922 }
929 } 923 }
924}
925
926static void pwc_iso_free(struct pwc_device *pdev)
927{
928 int i;
929
930 /* Freeing ISOC buffers one by one */
931 for (i = 0; i < MAX_ISO_BUFS; i++) {
932 struct urb *urb;
933
934 urb = pdev->sbuf[i].urb;
935 if (urb != 0) {
930 PWC_DEBUG_MEMORY("Freeing URB\n"); 936 PWC_DEBUG_MEMORY("Freeing URB\n");
931 usb_free_urb(urb); 937 usb_free_urb(urb);
932 pdev->sbuf[i].urb = NULL; 938 pdev->sbuf[i].urb = NULL;
933 } 939 }
934 } 940 }
941}
942
943void pwc_isoc_cleanup(struct pwc_device *pdev)
944{
945 PWC_DEBUG_OPEN(">> pwc_isoc_cleanup()\n");
946 if (pdev == NULL)
947 return;
948 if (pdev->iso_init == 0)
949 return;
950
951 pwc_iso_stop(pdev);
952 pwc_iso_free(pdev);
935 953
936 /* Stop camera, but only if we are sure the camera is still there (unplug 954 /* Stop camera, but only if we are sure the camera is still there (unplug
937 is signalled by EPIPE) 955 is signalled by EPIPE)
@@ -1211,6 +1229,7 @@ static int pwc_video_close(struct inode *inode, struct file *file)
1211 1229
1212 PWC_DEBUG_OPEN(">> video_close called(vdev = 0x%p).\n", vdev); 1230 PWC_DEBUG_OPEN(">> video_close called(vdev = 0x%p).\n", vdev);
1213 1231
1232 lock_kernel();
1214 pdev = (struct pwc_device *)vdev->priv; 1233 pdev = (struct pwc_device *)vdev->priv;
1215 if (pdev->vopen == 0) 1234 if (pdev->vopen == 0)
1216 PWC_DEBUG_MODULE("video_close() called on closed device?\n"); 1235 PWC_DEBUG_MODULE("video_close() called on closed device?\n");
@@ -1230,7 +1249,6 @@ static int pwc_video_close(struct inode *inode, struct file *file)
1230 pwc_isoc_cleanup(pdev); 1249 pwc_isoc_cleanup(pdev);
1231 pwc_free_buffers(pdev); 1250 pwc_free_buffers(pdev);
1232 1251
1233 lock_kernel();
1234 /* Turn off LEDS and power down camera, but only when not unplugged */ 1252 /* Turn off LEDS and power down camera, but only when not unplugged */
1235 if (!pdev->unplugged) { 1253 if (!pdev->unplugged) {
1236 /* Turn LEDs off */ 1254 /* Turn LEDs off */
@@ -1276,7 +1294,7 @@ static ssize_t pwc_video_read(struct file *file, char __user *buf,
1276 struct pwc_device *pdev; 1294 struct pwc_device *pdev;
1277 int noblock = file->f_flags & O_NONBLOCK; 1295 int noblock = file->f_flags & O_NONBLOCK;
1278 DECLARE_WAITQUEUE(wait, current); 1296 DECLARE_WAITQUEUE(wait, current);
1279 int bytes_to_read; 1297 int bytes_to_read, rv = 0;
1280 void *image_buffer_addr; 1298 void *image_buffer_addr;
1281 1299
1282 PWC_DEBUG_READ("pwc_video_read(vdev=0x%p, buf=%p, count=%zd) called.\n", 1300 PWC_DEBUG_READ("pwc_video_read(vdev=0x%p, buf=%p, count=%zd) called.\n",
@@ -1286,8 +1304,12 @@ static ssize_t pwc_video_read(struct file *file, char __user *buf,
1286 pdev = vdev->priv; 1304 pdev = vdev->priv;
1287 if (pdev == NULL) 1305 if (pdev == NULL)
1288 return -EFAULT; 1306 return -EFAULT;
1289 if (pdev->error_status) 1307
1290 return -pdev->error_status; /* Something happened, report what. */ 1308 mutex_lock(&pdev->modlock);
1309 if (pdev->error_status) {
1310 rv = -pdev->error_status; /* Something happened, report what. */
1311 goto err_out;
1312 }
1291 1313
1292 /* In case we're doing partial reads, we don't have to wait for a frame */ 1314 /* In case we're doing partial reads, we don't have to wait for a frame */
1293 if (pdev->image_read_pos == 0) { 1315 if (pdev->image_read_pos == 0) {
@@ -1298,17 +1320,20 @@ static ssize_t pwc_video_read(struct file *file, char __user *buf,
1298 if (pdev->error_status) { 1320 if (pdev->error_status) {
1299 remove_wait_queue(&pdev->frameq, &wait); 1321 remove_wait_queue(&pdev->frameq, &wait);
1300 set_current_state(TASK_RUNNING); 1322 set_current_state(TASK_RUNNING);
1301 return -pdev->error_status ; 1323 rv = -pdev->error_status ;
1324 goto err_out;
1302 } 1325 }
1303 if (noblock) { 1326 if (noblock) {
1304 remove_wait_queue(&pdev->frameq, &wait); 1327 remove_wait_queue(&pdev->frameq, &wait);
1305 set_current_state(TASK_RUNNING); 1328 set_current_state(TASK_RUNNING);
1306 return -EWOULDBLOCK; 1329 rv = -EWOULDBLOCK;
1330 goto err_out;
1307 } 1331 }
1308 if (signal_pending(current)) { 1332 if (signal_pending(current)) {
1309 remove_wait_queue(&pdev->frameq, &wait); 1333 remove_wait_queue(&pdev->frameq, &wait);
1310 set_current_state(TASK_RUNNING); 1334 set_current_state(TASK_RUNNING);
1311 return -ERESTARTSYS; 1335 rv = -ERESTARTSYS;
1336 goto err_out;
1312 } 1337 }
1313 schedule(); 1338 schedule();
1314 set_current_state(TASK_INTERRUPTIBLE); 1339 set_current_state(TASK_INTERRUPTIBLE);
@@ -1317,8 +1342,10 @@ static ssize_t pwc_video_read(struct file *file, char __user *buf,
1317 set_current_state(TASK_RUNNING); 1342 set_current_state(TASK_RUNNING);
1318 1343
1319 /* Decompress and release frame */ 1344 /* Decompress and release frame */
1320 if (pwc_handle_frame(pdev)) 1345 if (pwc_handle_frame(pdev)) {
1321 return -EFAULT; 1346 rv = -EFAULT;
1347 goto err_out;
1348 }
1322 } 1349 }
1323 1350
1324 PWC_DEBUG_READ("Copying data to user space.\n"); 1351 PWC_DEBUG_READ("Copying data to user space.\n");
@@ -1333,14 +1360,20 @@ static ssize_t pwc_video_read(struct file *file, char __user *buf,
1333 image_buffer_addr = pdev->image_data; 1360 image_buffer_addr = pdev->image_data;
1334 image_buffer_addr += pdev->images[pdev->fill_image].offset; 1361 image_buffer_addr += pdev->images[pdev->fill_image].offset;
1335 image_buffer_addr += pdev->image_read_pos; 1362 image_buffer_addr += pdev->image_read_pos;
1336 if (copy_to_user(buf, image_buffer_addr, count)) 1363 if (copy_to_user(buf, image_buffer_addr, count)) {
1337 return -EFAULT; 1364 rv = -EFAULT;
1365 goto err_out;
1366 }
1338 pdev->image_read_pos += count; 1367 pdev->image_read_pos += count;
1339 if (pdev->image_read_pos >= bytes_to_read) { /* All data has been read */ 1368 if (pdev->image_read_pos >= bytes_to_read) { /* All data has been read */
1340 pdev->image_read_pos = 0; 1369 pdev->image_read_pos = 0;
1341 pwc_next_image(pdev); 1370 pwc_next_image(pdev);
1342 } 1371 }
1372 mutex_unlock(&pdev->modlock);
1343 return count; 1373 return count;
1374err_out:
1375 mutex_unlock(&pdev->modlock);
1376 return rv;
1344} 1377}
1345 1378
1346static unsigned int pwc_video_poll(struct file *file, poll_table *wait) 1379static unsigned int pwc_video_poll(struct file *file, poll_table *wait)
@@ -1366,7 +1399,20 @@ static unsigned int pwc_video_poll(struct file *file, poll_table *wait)
1366static int pwc_video_ioctl(struct inode *inode, struct file *file, 1399static int pwc_video_ioctl(struct inode *inode, struct file *file,
1367 unsigned int cmd, unsigned long arg) 1400 unsigned int cmd, unsigned long arg)
1368{ 1401{
1369 return video_usercopy(inode, file, cmd, arg, pwc_video_do_ioctl); 1402 struct video_device *vdev = file->private_data;
1403 struct pwc_device *pdev;
1404 int r = -ENODEV;
1405
1406 if (!vdev)
1407 goto out;
1408 pdev = vdev->priv;
1409
1410 mutex_lock(&pdev->modlock);
1411 if (!pdev->unplugged)
1412 r = video_usercopy(inode, file, cmd, arg, pwc_video_do_ioctl);
1413 mutex_unlock(&pdev->modlock);
1414out:
1415 return r;
1370} 1416}
1371 1417
1372static int pwc_video_mmap(struct file *file, struct vm_area_struct *vma) 1418static int pwc_video_mmap(struct file *file, struct vm_area_struct *vma)
@@ -1809,7 +1855,10 @@ static void usb_pwc_disconnect(struct usb_interface *intf)
1809 wake_up_interruptible(&pdev->frameq); 1855 wake_up_interruptible(&pdev->frameq);
1810 /* Wait until device is closed */ 1856 /* Wait until device is closed */
1811 if(pdev->vopen) { 1857 if(pdev->vopen) {
1858 mutex_lock(&pdev->modlock);
1812 pdev->unplugged = 1; 1859 pdev->unplugged = 1;
1860 mutex_unlock(&pdev->modlock);
1861 pwc_iso_stop(pdev);
1813 } else { 1862 } else {
1814 /* Device is closed, so we can safely unregister it */ 1863 /* Device is closed, so we can safely unregister it */
1815 PWC_DEBUG_PROBE("Unregistering video device in disconnect().\n"); 1864 PWC_DEBUG_PROBE("Unregistering video device in disconnect().\n");
@@ -1827,7 +1876,6 @@ disconnect_out:
1827 unlock_kernel(); 1876 unlock_kernel();
1828} 1877}
1829 1878
1830
1831/* *grunt* We have to do atoi ourselves :-( */ 1879/* *grunt* We have to do atoi ourselves :-( */
1832static int pwc_atoi(const char *s) 1880static int pwc_atoi(const char *s)
1833{ 1881{