diff options
author | Daniel Mack <daniel@caiaq.de> | 2008-04-14 09:39:14 -0400 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2008-04-24 06:00:35 -0400 |
commit | 8d048841e822f745187246a036d03f2793739b7f (patch) | |
tree | 0ea6afcd5cdb817c40c9877b5f9a19982ca7afd2 /sound/usb/caiaq/caiaq-audio.c | |
parent | f57ab97e767d293132a29a43ca3ecb0f73f1d5bb (diff) |
[ALSA] snd_usb_caiaq: fix potential lockups locking
This patch fixes potential lockups in snd_usb_caiaq by refining the
locking mechanims and by using usb_kill_urb() in favor to
usb_unlink_urb().
Signed-off-by: Daniel Mack <daniel@caiaq.de>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/usb/caiaq/caiaq-audio.c')
-rw-r--r-- | sound/usb/caiaq/caiaq-audio.c | 71 |
1 files changed, 29 insertions, 42 deletions
diff --git a/sound/usb/caiaq/caiaq-audio.c b/sound/usb/caiaq/caiaq-audio.c index 9cc4cd8283f9..1aa927942cc6 100644 --- a/sound/usb/caiaq/caiaq-audio.c +++ b/sound/usb/caiaq/caiaq-audio.c | |||
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | * Copyright (c) 2006,2007 Daniel Mack, Karsten Wiese | 2 | * Copyright (c) 2006-2008 Daniel Mack, Karsten Wiese |
3 | * | 3 | * |
4 | * This program is free software; you can redistribute it and/or modify | 4 | * This program is free software; you can redistribute it and/or modify |
5 | * it under the terms of the GNU General Public License as published by | 5 | * it under the terms of the GNU General Public License as published by |
@@ -77,10 +77,15 @@ static void | |||
77 | deactivate_substream(struct snd_usb_caiaqdev *dev, | 77 | deactivate_substream(struct snd_usb_caiaqdev *dev, |
78 | struct snd_pcm_substream *sub) | 78 | struct snd_pcm_substream *sub) |
79 | { | 79 | { |
80 | unsigned long flags; | ||
81 | spin_lock_irqsave(&dev->spinlock, flags); | ||
82 | |||
80 | if (sub->stream == SNDRV_PCM_STREAM_PLAYBACK) | 83 | if (sub->stream == SNDRV_PCM_STREAM_PLAYBACK) |
81 | dev->sub_playback[sub->number] = NULL; | 84 | dev->sub_playback[sub->number] = NULL; |
82 | else | 85 | else |
83 | dev->sub_capture[sub->number] = NULL; | 86 | dev->sub_capture[sub->number] = NULL; |
87 | |||
88 | spin_unlock_irqrestore(&dev->spinlock, flags); | ||
84 | } | 89 | } |
85 | 90 | ||
86 | static int | 91 | static int |
@@ -97,13 +102,13 @@ static int stream_start(struct snd_usb_caiaqdev *dev) | |||
97 | { | 102 | { |
98 | int i, ret; | 103 | int i, ret; |
99 | 104 | ||
100 | debug("stream_start(%p)\n", dev); | 105 | debug("%s(%p)\n", __func__, dev); |
101 | spin_lock_irq(&dev->spinlock); | 106 | |
102 | if (dev->streaming) { | 107 | if (dev->streaming) |
103 | spin_unlock_irq(&dev->spinlock); | ||
104 | return -EINVAL; | 108 | return -EINVAL; |
105 | } | ||
106 | 109 | ||
110 | memset(dev->sub_playback, 0, sizeof(dev->sub_playback)); | ||
111 | memset(dev->sub_capture, 0, sizeof(dev->sub_capture)); | ||
107 | dev->input_panic = 0; | 112 | dev->input_panic = 0; |
108 | dev->output_panic = 0; | 113 | dev->output_panic = 0; |
109 | dev->first_packet = 1; | 114 | dev->first_packet = 1; |
@@ -112,37 +117,35 @@ static int stream_start(struct snd_usb_caiaqdev *dev) | |||
112 | for (i = 0; i < N_URBS; i++) { | 117 | for (i = 0; i < N_URBS; i++) { |
113 | ret = usb_submit_urb(dev->data_urbs_in[i], GFP_ATOMIC); | 118 | ret = usb_submit_urb(dev->data_urbs_in[i], GFP_ATOMIC); |
114 | if (ret) { | 119 | if (ret) { |
115 | log("unable to trigger initial read #%d! (ret = %d)\n", | 120 | log("unable to trigger read #%d! (ret %d)\n", i, ret); |
116 | i, ret); | ||
117 | dev->streaming = 0; | 121 | dev->streaming = 0; |
118 | spin_unlock_irq(&dev->spinlock); | ||
119 | return -EPIPE; | 122 | return -EPIPE; |
120 | } | 123 | } |
121 | } | 124 | } |
122 | 125 | ||
123 | spin_unlock_irq(&dev->spinlock); | ||
124 | return 0; | 126 | return 0; |
125 | } | 127 | } |
126 | 128 | ||
127 | static void stream_stop(struct snd_usb_caiaqdev *dev) | 129 | static void stream_stop(struct snd_usb_caiaqdev *dev) |
128 | { | 130 | { |
129 | int i; | 131 | int i; |
130 | 132 | ||
131 | debug("stream_stop(%p)\n", dev); | 133 | debug("%s(%p)\n", __func__, dev); |
132 | if (!dev->streaming) | 134 | if (!dev->streaming) |
133 | return; | 135 | return; |
134 | 136 | ||
135 | dev->streaming = 0; | 137 | dev->streaming = 0; |
138 | |||
136 | for (i = 0; i < N_URBS; i++) { | 139 | for (i = 0; i < N_URBS; i++) { |
137 | usb_unlink_urb(dev->data_urbs_in[i]); | 140 | usb_kill_urb(dev->data_urbs_in[i]); |
138 | usb_unlink_urb(dev->data_urbs_out[i]); | 141 | usb_kill_urb(dev->data_urbs_out[i]); |
139 | } | 142 | } |
140 | } | 143 | } |
141 | 144 | ||
142 | static int snd_usb_caiaq_substream_open(struct snd_pcm_substream *substream) | 145 | static int snd_usb_caiaq_substream_open(struct snd_pcm_substream *substream) |
143 | { | 146 | { |
144 | struct snd_usb_caiaqdev *dev = snd_pcm_substream_chip(substream); | 147 | struct snd_usb_caiaqdev *dev = snd_pcm_substream_chip(substream); |
145 | debug("snd_usb_caiaq_substream_open(%p)\n", substream); | 148 | debug("%s(%p)\n", __func__, substream); |
146 | substream->runtime->hw = dev->pcm_info; | 149 | substream->runtime->hw = dev->pcm_info; |
147 | snd_pcm_limit_hw_rates(substream->runtime); | 150 | snd_pcm_limit_hw_rates(substream->runtime); |
148 | return 0; | 151 | return 0; |
@@ -152,7 +155,7 @@ static int snd_usb_caiaq_substream_close(struct snd_pcm_substream *substream) | |||
152 | { | 155 | { |
153 | struct snd_usb_caiaqdev *dev = snd_pcm_substream_chip(substream); | 156 | struct snd_usb_caiaqdev *dev = snd_pcm_substream_chip(substream); |
154 | 157 | ||
155 | debug("snd_usb_caiaq_substream_close(%p)\n", substream); | 158 | debug("%s(%p)\n", __func__, substream); |
156 | if (all_substreams_zero(dev->sub_playback) && | 159 | if (all_substreams_zero(dev->sub_playback) && |
157 | all_substreams_zero(dev->sub_capture)) { | 160 | all_substreams_zero(dev->sub_capture)) { |
158 | /* when the last client has stopped streaming, | 161 | /* when the last client has stopped streaming, |
@@ -160,24 +163,22 @@ static int snd_usb_caiaq_substream_close(struct snd_pcm_substream *substream) | |||
160 | stream_stop(dev); | 163 | stream_stop(dev); |
161 | dev->pcm_info.rates = dev->samplerates; | 164 | dev->pcm_info.rates = dev->samplerates; |
162 | } | 165 | } |
163 | 166 | ||
164 | return 0; | 167 | return 0; |
165 | } | 168 | } |
166 | 169 | ||
167 | static int snd_usb_caiaq_pcm_hw_params(struct snd_pcm_substream *sub, | 170 | static int snd_usb_caiaq_pcm_hw_params(struct snd_pcm_substream *sub, |
168 | struct snd_pcm_hw_params *hw_params) | 171 | struct snd_pcm_hw_params *hw_params) |
169 | { | 172 | { |
170 | debug("snd_usb_caiaq_pcm_hw_params(%p)\n", sub); | 173 | debug("%s(%p)\n", __func__, sub); |
171 | return snd_pcm_lib_malloc_pages(sub, params_buffer_bytes(hw_params)); | 174 | return snd_pcm_lib_malloc_pages(sub, params_buffer_bytes(hw_params)); |
172 | } | 175 | } |
173 | 176 | ||
174 | static int snd_usb_caiaq_pcm_hw_free(struct snd_pcm_substream *sub) | 177 | static int snd_usb_caiaq_pcm_hw_free(struct snd_pcm_substream *sub) |
175 | { | 178 | { |
176 | struct snd_usb_caiaqdev *dev = snd_pcm_substream_chip(sub); | 179 | struct snd_usb_caiaqdev *dev = snd_pcm_substream_chip(sub); |
177 | debug("snd_usb_caiaq_pcm_hw_free(%p)\n", sub); | 180 | debug("%s(%p)\n", __func__, sub); |
178 | spin_lock_irq(&dev->spinlock); | ||
179 | deactivate_substream(dev, sub); | 181 | deactivate_substream(dev, sub); |
180 | spin_unlock_irq(&dev->spinlock); | ||
181 | return snd_pcm_lib_free_pages(sub); | 182 | return snd_pcm_lib_free_pages(sub); |
182 | } | 183 | } |
183 | 184 | ||
@@ -196,7 +197,7 @@ static int snd_usb_caiaq_pcm_prepare(struct snd_pcm_substream *substream) | |||
196 | struct snd_usb_caiaqdev *dev = snd_pcm_substream_chip(substream); | 197 | struct snd_usb_caiaqdev *dev = snd_pcm_substream_chip(substream); |
197 | struct snd_pcm_runtime *runtime = substream->runtime; | 198 | struct snd_pcm_runtime *runtime = substream->runtime; |
198 | 199 | ||
199 | debug("snd_usb_caiaq_pcm_prepare(%p)\n", substream); | 200 | debug("%s(%p)\n", __func__, substream); |
200 | 201 | ||
201 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | 202 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) |
202 | dev->audio_out_buf_pos[index] = BYTES_PER_SAMPLE + 1; | 203 | dev->audio_out_buf_pos[index] = BYTES_PER_SAMPLE + 1; |
@@ -247,15 +248,11 @@ static int snd_usb_caiaq_pcm_trigger(struct snd_pcm_substream *sub, int cmd) | |||
247 | switch (cmd) { | 248 | switch (cmd) { |
248 | case SNDRV_PCM_TRIGGER_START: | 249 | case SNDRV_PCM_TRIGGER_START: |
249 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | 250 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: |
250 | spin_lock(&dev->spinlock); | ||
251 | activate_substream(dev, sub); | 251 | activate_substream(dev, sub); |
252 | spin_unlock(&dev->spinlock); | ||
253 | break; | 252 | break; |
254 | case SNDRV_PCM_TRIGGER_STOP: | 253 | case SNDRV_PCM_TRIGGER_STOP: |
255 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | 254 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: |
256 | spin_lock(&dev->spinlock); | ||
257 | deactivate_substream(dev, sub); | 255 | deactivate_substream(dev, sub); |
258 | spin_unlock(&dev->spinlock); | ||
259 | break; | 256 | break; |
260 | default: | 257 | default: |
261 | return -EINVAL; | 258 | return -EINVAL; |
@@ -328,8 +325,6 @@ static void read_in_urb_mode0(struct snd_usb_caiaqdev *dev, | |||
328 | if (all_substreams_zero(dev->sub_capture)) | 325 | if (all_substreams_zero(dev->sub_capture)) |
329 | return; | 326 | return; |
330 | 327 | ||
331 | spin_lock(&dev->spinlock); | ||
332 | |||
333 | for (i = 0; i < iso->actual_length;) { | 328 | for (i = 0; i < iso->actual_length;) { |
334 | for (stream = 0; stream < dev->n_streams; stream++, i++) { | 329 | for (stream = 0; stream < dev->n_streams; stream++, i++) { |
335 | sub = dev->sub_capture[stream]; | 330 | sub = dev->sub_capture[stream]; |
@@ -345,8 +340,6 @@ static void read_in_urb_mode0(struct snd_usb_caiaqdev *dev, | |||
345 | } | 340 | } |
346 | } | 341 | } |
347 | } | 342 | } |
348 | |||
349 | spin_unlock(&dev->spinlock); | ||
350 | } | 343 | } |
351 | 344 | ||
352 | static void read_in_urb_mode2(struct snd_usb_caiaqdev *dev, | 345 | static void read_in_urb_mode2(struct snd_usb_caiaqdev *dev, |
@@ -358,8 +351,6 @@ static void read_in_urb_mode2(struct snd_usb_caiaqdev *dev, | |||
358 | struct snd_pcm_substream *sub; | 351 | struct snd_pcm_substream *sub; |
359 | int stream, i; | 352 | int stream, i; |
360 | 353 | ||
361 | spin_lock(&dev->spinlock); | ||
362 | |||
363 | for (i = 0; i < iso->actual_length;) { | 354 | for (i = 0; i < iso->actual_length;) { |
364 | if (i % (dev->n_streams * BYTES_PER_SAMPLE_USB) == 0) { | 355 | if (i % (dev->n_streams * BYTES_PER_SAMPLE_USB) == 0) { |
365 | for (stream = 0; | 356 | for (stream = 0; |
@@ -393,8 +384,6 @@ static void read_in_urb_mode2(struct snd_usb_caiaqdev *dev, | |||
393 | } | 384 | } |
394 | } | 385 | } |
395 | } | 386 | } |
396 | |||
397 | spin_unlock(&dev->spinlock); | ||
398 | } | 387 | } |
399 | 388 | ||
400 | static void read_in_urb(struct snd_usb_caiaqdev *dev, | 389 | static void read_in_urb(struct snd_usb_caiaqdev *dev, |
@@ -418,8 +407,6 @@ static void read_in_urb(struct snd_usb_caiaqdev *dev, | |||
418 | dev->input_panic ? "(input)" : "", | 407 | dev->input_panic ? "(input)" : "", |
419 | dev->output_panic ? "(output)" : ""); | 408 | dev->output_panic ? "(output)" : ""); |
420 | } | 409 | } |
421 | |||
422 | check_for_elapsed_periods(dev, dev->sub_capture); | ||
423 | } | 410 | } |
424 | 411 | ||
425 | static void fill_out_urb(struct snd_usb_caiaqdev *dev, | 412 | static void fill_out_urb(struct snd_usb_caiaqdev *dev, |
@@ -429,8 +416,6 @@ static void fill_out_urb(struct snd_usb_caiaqdev *dev, | |||
429 | unsigned char *usb_buf = urb->transfer_buffer + iso->offset; | 416 | unsigned char *usb_buf = urb->transfer_buffer + iso->offset; |
430 | struct snd_pcm_substream *sub; | 417 | struct snd_pcm_substream *sub; |
431 | int stream, i; | 418 | int stream, i; |
432 | |||
433 | spin_lock(&dev->spinlock); | ||
434 | 419 | ||
435 | for (i = 0; i < iso->length;) { | 420 | for (i = 0; i < iso->length;) { |
436 | for (stream = 0; stream < dev->n_streams; stream++, i++) { | 421 | for (stream = 0; stream < dev->n_streams; stream++, i++) { |
@@ -456,9 +441,6 @@ static void fill_out_urb(struct snd_usb_caiaqdev *dev, | |||
456 | for (stream = 0; stream < dev->n_streams; stream++, i++) | 441 | for (stream = 0; stream < dev->n_streams; stream++, i++) |
457 | usb_buf[i] = MAKE_CHECKBYTE(dev, stream, i); | 442 | usb_buf[i] = MAKE_CHECKBYTE(dev, stream, i); |
458 | } | 443 | } |
459 | |||
460 | spin_unlock(&dev->spinlock); | ||
461 | check_for_elapsed_periods(dev, dev->sub_playback); | ||
462 | } | 444 | } |
463 | 445 | ||
464 | static void read_completed(struct urb *urb) | 446 | static void read_completed(struct urb *urb) |
@@ -472,6 +454,7 @@ static void read_completed(struct urb *urb) | |||
472 | return; | 454 | return; |
473 | 455 | ||
474 | dev = info->dev; | 456 | dev = info->dev; |
457 | |||
475 | if (!dev->streaming) | 458 | if (!dev->streaming) |
476 | return; | 459 | return; |
477 | 460 | ||
@@ -489,8 +472,12 @@ static void read_completed(struct urb *urb) | |||
489 | out->iso_frame_desc[outframe].offset = BYTES_PER_FRAME * frame; | 472 | out->iso_frame_desc[outframe].offset = BYTES_PER_FRAME * frame; |
490 | 473 | ||
491 | if (len > 0) { | 474 | if (len > 0) { |
475 | spin_lock(&dev->spinlock); | ||
492 | fill_out_urb(dev, out, &out->iso_frame_desc[outframe]); | 476 | fill_out_urb(dev, out, &out->iso_frame_desc[outframe]); |
493 | read_in_urb(dev, urb, &urb->iso_frame_desc[frame]); | 477 | read_in_urb(dev, urb, &urb->iso_frame_desc[frame]); |
478 | spin_unlock(&dev->spinlock); | ||
479 | check_for_elapsed_periods(dev, dev->sub_playback); | ||
480 | check_for_elapsed_periods(dev, dev->sub_capture); | ||
494 | send_it = 1; | 481 | send_it = 1; |
495 | } | 482 | } |
496 | 483 | ||
@@ -696,7 +683,7 @@ int snd_usb_caiaq_audio_init(struct snd_usb_caiaqdev *dev) | |||
696 | 683 | ||
697 | void snd_usb_caiaq_audio_free(struct snd_usb_caiaqdev *dev) | 684 | void snd_usb_caiaq_audio_free(struct snd_usb_caiaqdev *dev) |
698 | { | 685 | { |
699 | debug("snd_usb_caiaq_audio_free (%p)\n", dev); | 686 | debug("%s(%p)\n", __func__, dev); |
700 | stream_stop(dev); | 687 | stream_stop(dev); |
701 | free_urbs(dev->data_urbs_in); | 688 | free_urbs(dev->data_urbs_in); |
702 | free_urbs(dev->data_urbs_out); | 689 | free_urbs(dev->data_urbs_out); |