diff options
author | Mauro Carvalho Chehab <mchehab@redhat.com> | 2010-10-09 14:53:58 -0400 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2010-10-20 23:17:50 -0400 |
commit | 39a96b4cf592e79aefd1b4f2b136c20ec7bf10a7 (patch) | |
tree | 010b6910c34bbe9bb2b513ccde537f4bdc152dd0 /drivers/media/video/em28xx | |
parent | 643800d5c806cf46b0cd184b595a14cce83224e4 (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.c | 75 | ||||
-rw-r--r-- | drivers/media/video/em28xx/em28xx.h | 17 |
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 | ||
220 | static 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 | |||
245 | static int snd_pcm_alloc_vmalloc_buffer(struct snd_pcm_substream *subs, | 223 | static 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; |
327 | err: | 303 | err: |
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 | ||
379 | static 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 | |||
394 | static int snd_em28xx_capture_trigger(struct snd_pcm_substream *substream, | 392 | static 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 | ||
421 | static snd_pcm_uframes_t snd_em28xx_capture_pointer(struct snd_pcm_substream | 412 | static 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 | ||
187 | enum em28xx_stream_state { | ||
188 | STREAM_OFF, | ||
189 | STREAM_INTERRUPT, | ||
190 | STREAM_ON, | ||
191 | }; | ||
192 | 188 | ||
193 | struct em28xx; | 189 | struct 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; |