aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/video/em28xx
diff options
context:
space:
mode:
authorMauro Carvalho Chehab <mchehab@redhat.com>2010-10-09 14:53:58 -0400
committerMauro Carvalho Chehab <mchehab@redhat.com>2010-10-20 23:17:50 -0400
commit39a96b4cf592e79aefd1b4f2b136c20ec7bf10a7 (patch)
tree010b6910c34bbe9bb2b513ccde537f4bdc152dd0 /drivers/media/video/em28xx
parent643800d5c806cf46b0cd184b595a14cce83224e4 (diff)
[media] em28xx-audio: fix some locking issues
Those locking issues affect tvtime, causing a kernel oops/panic, due to a race condition. Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media/video/em28xx')
-rw-r--r--drivers/media/video/em28xx/em28xx-audio.c75
-rw-r--r--drivers/media/video/em28xx/em28xx.h17
2 files changed, 42 insertions, 50 deletions
diff --git a/drivers/media/video/em28xx/em28xx-audio.c b/drivers/media/video/em28xx/em28xx-audio.c
index e182abf476c..3c48a72eb7d 100644
--- a/drivers/media/video/em28xx/em28xx-audio.c
+++ b/drivers/media/video/em28xx/em28xx-audio.c
@@ -102,6 +102,9 @@ static void em28xx_audio_isocirq(struct urb *urb)
102 break; 102 break;
103 } 103 }
104 104
105 if (atomic_read(&dev->stream_started) == 0)
106 return;
107
105 if (dev->adev.capture_pcm_substream) { 108 if (dev->adev.capture_pcm_substream) {
106 substream = dev->adev.capture_pcm_substream; 109 substream = dev->adev.capture_pcm_substream;
107 runtime = substream->runtime; 110 runtime = substream->runtime;
@@ -217,31 +220,6 @@ static int em28xx_init_audio_isoc(struct em28xx *dev)
217 return 0; 220 return 0;
218} 221}
219 222
220static int em28xx_cmd(struct em28xx *dev, int cmd, int arg)
221{
222 dprintk("%s transfer\n", (dev->adev.capture_stream == STREAM_ON) ?
223 "stop" : "start");
224
225 switch (cmd) {
226 case EM28XX_CAPTURE_STREAM_EN:
227 if (dev->adev.capture_stream == STREAM_OFF &&
228 arg == EM28XX_START_AUDIO) {
229 dev->adev.capture_stream = STREAM_ON;
230 em28xx_init_audio_isoc(dev);
231 } else if (dev->adev.capture_stream == STREAM_ON &&
232 arg == EM28XX_STOP_AUDIO) {
233 dev->adev.capture_stream = STREAM_OFF;
234 em28xx_deinit_isoc_audio(dev);
235 } else {
236 em28xx_errdev("An underrun very likely occurred. "
237 "Ignoring it.\n");
238 }
239 return 0;
240 default:
241 return -EINVAL;
242 }
243}
244
245static int snd_pcm_alloc_vmalloc_buffer(struct snd_pcm_substream *subs, 223static int snd_pcm_alloc_vmalloc_buffer(struct snd_pcm_substream *subs,
246 size_t size) 224 size_t size)
247{ 225{
@@ -303,7 +281,6 @@ static int snd_em28xx_capture_open(struct snd_pcm_substream *substream)
303 dev->mute = 0; 281 dev->mute = 0;
304 mutex_lock(&dev->lock); 282 mutex_lock(&dev->lock);
305 ret = em28xx_audio_analog_set(dev); 283 ret = em28xx_audio_analog_set(dev);
306 mutex_unlock(&dev->lock);
307 if (ret < 0) 284 if (ret < 0)
308 goto err; 285 goto err;
309 286
@@ -311,11 +288,10 @@ static int snd_em28xx_capture_open(struct snd_pcm_substream *substream)
311 if (dev->alt == 0 && dev->adev.users == 0) { 288 if (dev->alt == 0 && dev->adev.users == 0) {
312 int errCode; 289 int errCode;
313 dev->alt = 7; 290 dev->alt = 7;
314 errCode = usb_set_interface(dev->udev, 0, 7);
315 dprintk("changing alternate number to 7\n"); 291 dprintk("changing alternate number to 7\n");
292 errCode = usb_set_interface(dev->udev, 0, 7);
316 } 293 }
317 294
318 mutex_lock(&dev->lock);
319 dev->adev.users++; 295 dev->adev.users++;
320 mutex_unlock(&dev->lock); 296 mutex_unlock(&dev->lock);
321 297
@@ -325,6 +301,8 @@ static int snd_em28xx_capture_open(struct snd_pcm_substream *substream)
325 301
326 return 0; 302 return 0;
327err: 303err:
304 mutex_unlock(&dev->lock);
305
328 em28xx_err("Error while configuring em28xx mixer\n"); 306 em28xx_err("Error while configuring em28xx mixer\n");
329 return ret; 307 return ret;
330} 308}
@@ -338,6 +316,11 @@ static int snd_em28xx_pcm_close(struct snd_pcm_substream *substream)
338 dev->mute = 1; 316 dev->mute = 1;
339 mutex_lock(&dev->lock); 317 mutex_lock(&dev->lock);
340 dev->adev.users--; 318 dev->adev.users--;
319 if (atomic_read(&dev->stream_started) > 0) {
320 atomic_set(&dev->stream_started, 0);
321 schedule_work(&dev->wq_trigger);
322 }
323
341 em28xx_audio_analog_set(dev); 324 em28xx_audio_analog_set(dev);
342 if (substream->runtime->dma_area) { 325 if (substream->runtime->dma_area) {
343 dprintk("freeing\n"); 326 dprintk("freeing\n");
@@ -375,8 +358,10 @@ static int snd_em28xx_hw_capture_free(struct snd_pcm_substream *substream)
375 358
376 dprintk("Stop capture, if needed\n"); 359 dprintk("Stop capture, if needed\n");
377 360
378 if (dev->adev.capture_stream == STREAM_ON) 361 if (atomic_read(&dev->stream_started) > 0) {
379 em28xx_cmd(dev, EM28XX_CAPTURE_STREAM_EN, EM28XX_STOP_AUDIO); 362 atomic_set(&dev->stream_started, 0);
363 schedule_work(&dev->wq_trigger);
364 }
380 365
381 return 0; 366 return 0;
382} 367}
@@ -391,31 +376,37 @@ static int snd_em28xx_prepare(struct snd_pcm_substream *substream)
391 return 0; 376 return 0;
392} 377}
393 378
379static void audio_trigger(struct work_struct *work)
380{
381 struct em28xx *dev = container_of(work, struct em28xx, wq_trigger);
382
383 if (atomic_read(&dev->stream_started)) {
384 dprintk("starting capture");
385 em28xx_init_audio_isoc(dev);
386 } else {
387 dprintk("stopping capture");
388 em28xx_deinit_isoc_audio(dev);
389 }
390}
391
394static int snd_em28xx_capture_trigger(struct snd_pcm_substream *substream, 392static int snd_em28xx_capture_trigger(struct snd_pcm_substream *substream,
395 int cmd) 393 int cmd)
396{ 394{
397 struct em28xx *dev = snd_pcm_substream_chip(substream); 395 struct em28xx *dev = snd_pcm_substream_chip(substream);
398 int retval; 396 int retval;
399 397
400 dprintk("Should %s capture\n", (cmd == SNDRV_PCM_TRIGGER_START) ?
401 "start" : "stop");
402
403 spin_lock(&dev->adev.slock);
404 switch (cmd) { 398 switch (cmd) {
405 case SNDRV_PCM_TRIGGER_START: 399 case SNDRV_PCM_TRIGGER_START:
406 em28xx_cmd(dev, EM28XX_CAPTURE_STREAM_EN, EM28XX_START_AUDIO); 400 atomic_set(&dev->stream_started, 1);
407 retval = 0;
408 break; 401 break;
409 case SNDRV_PCM_TRIGGER_STOP: 402 case SNDRV_PCM_TRIGGER_STOP:
410 em28xx_cmd(dev, EM28XX_CAPTURE_STREAM_EN, EM28XX_STOP_AUDIO); 403 atomic_set(&dev->stream_started, 1);
411 retval = 0;
412 break; 404 break;
413 default: 405 default:
414 retval = -EINVAL; 406 retval = -EINVAL;
415 } 407 }
416 408 schedule_work(&dev->wq_trigger);
417 spin_unlock(&dev->adev.slock); 409 return 0;
418 return retval;
419} 410}
420 411
421static snd_pcm_uframes_t snd_em28xx_capture_pointer(struct snd_pcm_substream 412static snd_pcm_uframes_t snd_em28xx_capture_pointer(struct snd_pcm_substream
@@ -495,6 +486,8 @@ static int em28xx_audio_init(struct em28xx *dev)
495 strcpy(card->shortname, "Em28xx Audio"); 486 strcpy(card->shortname, "Em28xx Audio");
496 strcpy(card->longname, "Empia Em28xx Audio"); 487 strcpy(card->longname, "Empia Em28xx Audio");
497 488
489 INIT_WORK(&dev->wq_trigger, audio_trigger);
490
498 err = snd_card_register(card); 491 err = snd_card_register(card);
499 if (err < 0) { 492 if (err < 0) {
500 snd_card_free(card); 493 snd_card_free(card);
diff --git a/drivers/media/video/em28xx/em28xx.h b/drivers/media/video/em28xx/em28xx.h
index 1c61a6b65d2..adb20eb6d90 100644
--- a/drivers/media/video/em28xx/em28xx.h
+++ b/drivers/media/video/em28xx/em28xx.h
@@ -25,12 +25,13 @@
25#ifndef _EM28XX_H 25#ifndef _EM28XX_H
26#define _EM28XX_H 26#define _EM28XX_H
27 27
28#include <linux/workqueue.h>
29#include <linux/i2c.h>
30#include <linux/mutex.h>
28#include <linux/videodev2.h> 31#include <linux/videodev2.h>
32
29#include <media/videobuf-vmalloc.h> 33#include <media/videobuf-vmalloc.h>
30#include <media/v4l2-device.h> 34#include <media/v4l2-device.h>
31
32#include <linux/i2c.h>
33#include <linux/mutex.h>
34#include <media/ir-kbd-i2c.h> 35#include <media/ir-kbd-i2c.h>
35#include <media/ir-core.h> 36#include <media/ir-core.h>
36#if defined(CONFIG_VIDEO_EM28XX_DVB) || defined(CONFIG_VIDEO_EM28XX_DVB_MODULE) 37#if defined(CONFIG_VIDEO_EM28XX_DVB) || defined(CONFIG_VIDEO_EM28XX_DVB_MODULE)
@@ -184,11 +185,6 @@ enum em28xx_mode {
184 EM28XX_DIGITAL_MODE, 185 EM28XX_DIGITAL_MODE,
185}; 186};
186 187
187enum em28xx_stream_state {
188 STREAM_OFF,
189 STREAM_INTERRUPT,
190 STREAM_ON,
191};
192 188
193struct em28xx; 189struct em28xx;
194 190
@@ -463,7 +459,6 @@ struct em28xx_audio {
463 struct snd_card *sndcard; 459 struct snd_card *sndcard;
464 460
465 int users; 461 int users;
466 enum em28xx_stream_state capture_stream;
467 spinlock_t slock; 462 spinlock_t slock;
468}; 463};
469 464
@@ -505,6 +500,10 @@ struct em28xx {
505 unsigned int has_audio_class:1; 500 unsigned int has_audio_class:1;
506 unsigned int has_alsa_audio:1; 501 unsigned int has_alsa_audio:1;
507 502
503 /* Controls audio streaming */
504 struct work_struct wq_trigger; /* Trigger to start/stop audio for alsa module */
505 atomic_t stream_started; /* stream should be running if true */
506
508 struct em28xx_fmt *format; 507 struct em28xx_fmt *format;
509 508
510 struct em28xx_IR *ir; 509 struct em28xx_IR *ir;