diff options
Diffstat (limited to 'drivers/media/radio')
-rw-r--r-- | drivers/media/radio/radio-si470x.c | 84 |
1 files changed, 41 insertions, 43 deletions
diff --git a/drivers/media/radio/radio-si470x.c b/drivers/media/radio/radio-si470x.c index 5dd33197bbdd..707988edc1b1 100644 --- a/drivers/media/radio/radio-si470x.c +++ b/drivers/media/radio/radio-si470x.c | |||
@@ -85,7 +85,7 @@ | |||
85 | * Oliver Neukum <oliver@neukum.org> | 85 | * Oliver Neukum <oliver@neukum.org> |
86 | * Version 1.0.7 | 86 | * Version 1.0.7 |
87 | * - usb autosuspend support | 87 | * - usb autosuspend support |
88 | * - unplugging fixed | 88 | * - unplugging fixed |
89 | * | 89 | * |
90 | * ToDo: | 90 | * ToDo: |
91 | * - add seeking support | 91 | * - add seeking support |
@@ -425,7 +425,8 @@ struct si470x_device { | |||
425 | 425 | ||
426 | /* driver management */ | 426 | /* driver management */ |
427 | unsigned int users; | 427 | unsigned int users; |
428 | unsigned char disconnected; | 428 | unsigned char disconnected; |
429 | struct mutex disconnect_lock; | ||
429 | 430 | ||
430 | /* Silabs internal registers (0..15) */ | 431 | /* Silabs internal registers (0..15) */ |
431 | unsigned short registers[RADIO_REGISTER_NUM]; | 432 | unsigned short registers[RADIO_REGISTER_NUM]; |
@@ -442,12 +443,6 @@ struct si470x_device { | |||
442 | 443 | ||
443 | 444 | ||
444 | /* | 445 | /* |
445 | * Lock to prevent kfree of data before all users have releases the device. | ||
446 | */ | ||
447 | static DEFINE_MUTEX(open_close_lock); | ||
448 | |||
449 | |||
450 | /* | ||
451 | * The frequency is set in units of 62.5 Hz when using V4L2_TUNER_CAP_LOW, | 446 | * The frequency is set in units of 62.5 Hz when using V4L2_TUNER_CAP_LOW, |
452 | * 62.5 kHz otherwise. | 447 | * 62.5 kHz otherwise. |
453 | * The tuner is able to have a channel spacing of 50, 100 or 200 kHz. | 448 | * The tuner is able to have a channel spacing of 50, 100 or 200 kHz. |
@@ -582,7 +577,7 @@ static int si470x_get_rds_registers(struct si470x_device *radio) | |||
582 | usb_rcvintpipe(radio->usbdev, 1), | 577 | usb_rcvintpipe(radio->usbdev, 1), |
583 | (void *) &buf, sizeof(buf), &size, usb_timeout); | 578 | (void *) &buf, sizeof(buf), &size, usb_timeout); |
584 | if (size != sizeof(buf)) | 579 | if (size != sizeof(buf)) |
585 | printk(KERN_WARNING DRIVER_NAME ": si470x_get_rds_registers: " | 580 | printk(KERN_WARNING DRIVER_NAME ": si470x_get_rds_registers: " |
586 | "return size differs: %d != %zu\n", size, sizeof(buf)); | 581 | "return size differs: %d != %zu\n", size, sizeof(buf)); |
587 | if (retval < 0) | 582 | if (retval < 0) |
588 | printk(KERN_WARNING DRIVER_NAME ": si470x_get_rds_registers: " | 583 | printk(KERN_WARNING DRIVER_NAME ": si470x_get_rds_registers: " |
@@ -591,7 +586,8 @@ static int si470x_get_rds_registers(struct si470x_device *radio) | |||
591 | if (retval >= 0) | 586 | if (retval >= 0) |
592 | for (regnr = 0; regnr < RDS_REGISTER_NUM; regnr++) | 587 | for (regnr = 0; regnr < RDS_REGISTER_NUM; regnr++) |
593 | radio->registers[STATUSRSSI + regnr] = | 588 | radio->registers[STATUSRSSI + regnr] = |
594 | get_unaligned_be16(&buf[regnr * RADIO_REGISTER_SIZE + 1]); | 589 | get_unaligned_be16( |
590 | &buf[regnr * RADIO_REGISTER_SIZE + 1]); | ||
595 | 591 | ||
596 | return (retval < 0) ? -EINVAL : 0; | 592 | return (retval < 0) ? -EINVAL : 0; |
597 | } | 593 | } |
@@ -879,8 +875,8 @@ static void si470x_work(struct work_struct *work) | |||
879 | struct si470x_device *radio = container_of(work, struct si470x_device, | 875 | struct si470x_device *radio = container_of(work, struct si470x_device, |
880 | work.work); | 876 | work.work); |
881 | 877 | ||
882 | if (radio->disconnected) | 878 | if (radio->disconnected) |
883 | return; | 879 | return; |
884 | if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0) | 880 | if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0) |
885 | return; | 881 | return; |
886 | 882 | ||
@@ -1007,20 +1003,20 @@ static int si470x_fops_open(struct inode *inode, struct file *file) | |||
1007 | static int si470x_fops_release(struct inode *inode, struct file *file) | 1003 | static int si470x_fops_release(struct inode *inode, struct file *file) |
1008 | { | 1004 | { |
1009 | struct si470x_device *radio = video_get_drvdata(video_devdata(file)); | 1005 | struct si470x_device *radio = video_get_drvdata(video_devdata(file)); |
1010 | int retval = 0; | 1006 | int retval = 0; |
1011 | 1007 | ||
1012 | if (!radio) | 1008 | if (!radio) |
1013 | return -ENODEV; | 1009 | return -ENODEV; |
1014 | 1010 | ||
1015 | mutex_lock(&open_close_lock); | 1011 | mutex_lock(&radio->disconnect_lock); |
1016 | radio->users--; | 1012 | radio->users--; |
1017 | if (radio->users == 0) { | 1013 | if (radio->users == 0) { |
1018 | if (radio->disconnected) { | 1014 | if (radio->disconnected) { |
1019 | video_unregister_device(radio->videodev); | 1015 | video_unregister_device(radio->videodev); |
1020 | kfree(radio->buffer); | 1016 | kfree(radio->buffer); |
1021 | kfree(radio); | 1017 | kfree(radio); |
1022 | goto done; | 1018 | goto unlock; |
1023 | } | 1019 | } |
1024 | 1020 | ||
1025 | /* stop rds reception */ | 1021 | /* stop rds reception */ |
1026 | cancel_delayed_work_sync(&radio->work); | 1022 | cancel_delayed_work_sync(&radio->work); |
@@ -1032,9 +1028,9 @@ static int si470x_fops_release(struct inode *inode, struct file *file) | |||
1032 | usb_autopm_put_interface(radio->intf); | 1028 | usb_autopm_put_interface(radio->intf); |
1033 | } | 1029 | } |
1034 | 1030 | ||
1035 | done: | 1031 | unlock: |
1036 | mutex_unlock(&open_close_lock); | 1032 | mutex_unlock(&radio->disconnect_lock); |
1037 | return retval; | 1033 | return retval; |
1038 | } | 1034 | } |
1039 | 1035 | ||
1040 | 1036 | ||
@@ -1174,8 +1170,8 @@ static int si470x_vidioc_g_ctrl(struct file *file, void *priv, | |||
1174 | { | 1170 | { |
1175 | struct si470x_device *radio = video_get_drvdata(video_devdata(file)); | 1171 | struct si470x_device *radio = video_get_drvdata(video_devdata(file)); |
1176 | 1172 | ||
1177 | if (radio->disconnected) | 1173 | if (radio->disconnected) |
1178 | return -EIO; | 1174 | return -EIO; |
1179 | 1175 | ||
1180 | switch (ctrl->id) { | 1176 | switch (ctrl->id) { |
1181 | case V4L2_CID_AUDIO_VOLUME: | 1177 | case V4L2_CID_AUDIO_VOLUME: |
@@ -1201,8 +1197,8 @@ static int si470x_vidioc_s_ctrl(struct file *file, void *priv, | |||
1201 | struct si470x_device *radio = video_get_drvdata(video_devdata(file)); | 1197 | struct si470x_device *radio = video_get_drvdata(video_devdata(file)); |
1202 | int retval; | 1198 | int retval; |
1203 | 1199 | ||
1204 | if (radio->disconnected) | 1200 | if (radio->disconnected) |
1205 | return -EIO; | 1201 | return -EIO; |
1206 | 1202 | ||
1207 | switch (ctrl->id) { | 1203 | switch (ctrl->id) { |
1208 | case V4L2_CID_AUDIO_VOLUME: | 1204 | case V4L2_CID_AUDIO_VOLUME: |
@@ -1266,8 +1262,8 @@ static int si470x_vidioc_g_tuner(struct file *file, void *priv, | |||
1266 | struct si470x_device *radio = video_get_drvdata(video_devdata(file)); | 1262 | struct si470x_device *radio = video_get_drvdata(video_devdata(file)); |
1267 | int retval; | 1263 | int retval; |
1268 | 1264 | ||
1269 | if (radio->disconnected) | 1265 | if (radio->disconnected) |
1270 | return -EIO; | 1266 | return -EIO; |
1271 | if (tuner->index > 0) | 1267 | if (tuner->index > 0) |
1272 | return -EINVAL; | 1268 | return -EINVAL; |
1273 | 1269 | ||
@@ -1324,8 +1320,8 @@ static int si470x_vidioc_s_tuner(struct file *file, void *priv, | |||
1324 | struct si470x_device *radio = video_get_drvdata(video_devdata(file)); | 1320 | struct si470x_device *radio = video_get_drvdata(video_devdata(file)); |
1325 | int retval; | 1321 | int retval; |
1326 | 1322 | ||
1327 | if (radio->disconnected) | 1323 | if (radio->disconnected) |
1328 | return -EIO; | 1324 | return -EIO; |
1329 | if (tuner->index > 0) | 1325 | if (tuner->index > 0) |
1330 | return -EINVAL; | 1326 | return -EINVAL; |
1331 | 1327 | ||
@@ -1351,8 +1347,8 @@ static int si470x_vidioc_g_frequency(struct file *file, void *priv, | |||
1351 | { | 1347 | { |
1352 | struct si470x_device *radio = video_get_drvdata(video_devdata(file)); | 1348 | struct si470x_device *radio = video_get_drvdata(video_devdata(file)); |
1353 | 1349 | ||
1354 | if (radio->disconnected) | 1350 | if (radio->disconnected) |
1355 | return -EIO; | 1351 | return -EIO; |
1356 | 1352 | ||
1357 | freq->type = V4L2_TUNER_RADIO; | 1353 | freq->type = V4L2_TUNER_RADIO; |
1358 | freq->frequency = si470x_get_freq(radio); | 1354 | freq->frequency = si470x_get_freq(radio); |
@@ -1370,8 +1366,8 @@ static int si470x_vidioc_s_frequency(struct file *file, void *priv, | |||
1370 | struct si470x_device *radio = video_get_drvdata(video_devdata(file)); | 1366 | struct si470x_device *radio = video_get_drvdata(video_devdata(file)); |
1371 | int retval; | 1367 | int retval; |
1372 | 1368 | ||
1373 | if (radio->disconnected) | 1369 | if (radio->disconnected) |
1374 | return -EIO; | 1370 | return -EIO; |
1375 | if (freq->type != V4L2_TUNER_RADIO) | 1371 | if (freq->type != V4L2_TUNER_RADIO) |
1376 | return -EINVAL; | 1372 | return -EINVAL; |
1377 | 1373 | ||
@@ -1436,8 +1432,10 @@ static int si470x_usb_driver_probe(struct usb_interface *intf, | |||
1436 | memcpy(radio->videodev, &si470x_viddev_template, | 1432 | memcpy(radio->videodev, &si470x_viddev_template, |
1437 | sizeof(si470x_viddev_template)); | 1433 | sizeof(si470x_viddev_template)); |
1438 | radio->users = 0; | 1434 | radio->users = 0; |
1435 | radio->disconnected = 0; | ||
1439 | radio->usbdev = interface_to_usbdev(intf); | 1436 | radio->usbdev = interface_to_usbdev(intf); |
1440 | radio->intf = intf; | 1437 | radio->intf = intf; |
1438 | mutex_init(&radio->disconnect_lock); | ||
1441 | mutex_init(&radio->lock); | 1439 | mutex_init(&radio->lock); |
1442 | video_set_drvdata(radio->videodev, radio); | 1440 | video_set_drvdata(radio->videodev, radio); |
1443 | 1441 | ||
@@ -1542,16 +1540,16 @@ static void si470x_usb_driver_disconnect(struct usb_interface *intf) | |||
1542 | { | 1540 | { |
1543 | struct si470x_device *radio = usb_get_intfdata(intf); | 1541 | struct si470x_device *radio = usb_get_intfdata(intf); |
1544 | 1542 | ||
1545 | mutex_lock(&open_close_lock); | 1543 | mutex_lock(&radio->disconnect_lock); |
1546 | radio->disconnected = 1; | 1544 | radio->disconnected = 1; |
1547 | cancel_delayed_work_sync(&radio->work); | 1545 | cancel_delayed_work_sync(&radio->work); |
1548 | usb_set_intfdata(intf, NULL); | 1546 | usb_set_intfdata(intf, NULL); |
1549 | if (radio->users == 0) { | 1547 | if (radio->users == 0) { |
1550 | video_unregister_device(radio->videodev); | 1548 | video_unregister_device(radio->videodev); |
1551 | kfree(radio->buffer); | 1549 | kfree(radio->buffer); |
1552 | kfree(radio); | 1550 | kfree(radio); |
1553 | } | 1551 | } |
1554 | mutex_unlock(&open_close_lock); | 1552 | mutex_unlock(&radio->disconnect_lock); |
1555 | } | 1553 | } |
1556 | 1554 | ||
1557 | 1555 | ||