~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/sound/usb/line6/capture.c

Version: ~ [ linux-5.9.1 ] ~ [ linux-5.8.16 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.72 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.152 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.202 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.240 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.240 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.19.8 ] ~ [ linux-3.18.140 ] ~ [ linux-3.17.8 ] ~ [ linux-3.16.85 ] ~ [ linux-3.15.10 ] ~ [ linux-3.14.79 ] ~ [ linux-3.13.11 ] ~ [ linux-3.12.74 ] ~ [ linux-3.11.10 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.5 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 /*
  2  * Line 6 Linux USB driver
  3  *
  4  * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
  5  *
  6  *      This program is free software; you can redistribute it and/or
  7  *      modify it under the terms of the GNU General Public License as
  8  *      published by the Free Software Foundation, version 2.
  9  *
 10  */
 11 
 12 #include <linux/slab.h>
 13 #include <sound/core.h>
 14 #include <sound/pcm.h>
 15 #include <sound/pcm_params.h>
 16 
 17 #include "capture.h"
 18 #include "driver.h"
 19 #include "pcm.h"
 20 
 21 /*
 22         Find a free URB and submit it.
 23         must be called in line6pcm->in.lock context
 24 */
 25 static int submit_audio_in_urb(struct snd_line6_pcm *line6pcm)
 26 {
 27         int index;
 28         int i, urb_size;
 29         int ret;
 30         struct urb *urb_in;
 31 
 32         index = find_first_zero_bit(&line6pcm->in.active_urbs,
 33                                     line6pcm->line6->iso_buffers);
 34 
 35         if (index < 0 || index >= line6pcm->line6->iso_buffers) {
 36                 dev_err(line6pcm->line6->ifcdev, "no free URB found\n");
 37                 return -EINVAL;
 38         }
 39 
 40         urb_in = line6pcm->in.urbs[index];
 41         urb_size = 0;
 42 
 43         for (i = 0; i < LINE6_ISO_PACKETS; ++i) {
 44                 struct usb_iso_packet_descriptor *fin =
 45                     &urb_in->iso_frame_desc[i];
 46                 fin->offset = urb_size;
 47                 fin->length = line6pcm->max_packet_size_in;
 48                 urb_size += line6pcm->max_packet_size_in;
 49         }
 50 
 51         urb_in->transfer_buffer =
 52             line6pcm->in.buffer +
 53             index * LINE6_ISO_PACKETS * line6pcm->max_packet_size_in;
 54         urb_in->transfer_buffer_length = urb_size;
 55         urb_in->context = line6pcm;
 56 
 57         ret = usb_submit_urb(urb_in, GFP_ATOMIC);
 58 
 59         if (ret == 0)
 60                 set_bit(index, &line6pcm->in.active_urbs);
 61         else
 62                 dev_err(line6pcm->line6->ifcdev,
 63                         "URB in #%d submission failed (%d)\n", index, ret);
 64 
 65         return 0;
 66 }
 67 
 68 /*
 69         Submit all currently available capture URBs.
 70         must be called in line6pcm->in.lock context
 71 */
 72 int line6_submit_audio_in_all_urbs(struct snd_line6_pcm *line6pcm)
 73 {
 74         int ret = 0, i;
 75 
 76         for (i = 0; i < line6pcm->line6->iso_buffers; ++i) {
 77                 ret = submit_audio_in_urb(line6pcm);
 78                 if (ret < 0)
 79                         break;
 80         }
 81 
 82         return ret;
 83 }
 84 
 85 /*
 86         Copy data into ALSA capture buffer.
 87 */
 88 void line6_capture_copy(struct snd_line6_pcm *line6pcm, char *fbuf, int fsize)
 89 {
 90         struct snd_pcm_substream *substream =
 91             get_substream(line6pcm, SNDRV_PCM_STREAM_CAPTURE);
 92         struct snd_pcm_runtime *runtime = substream->runtime;
 93         const int bytes_per_frame =
 94                 line6pcm->properties->bytes_per_channel *
 95                 line6pcm->properties->capture_hw.channels_max;
 96         int frames = fsize / bytes_per_frame;
 97 
 98         if (runtime == NULL)
 99                 return;
100 
101         if (line6pcm->in.pos_done + frames > runtime->buffer_size) {
102                 /*
103                    The transferred area goes over buffer boundary,
104                    copy two separate chunks.
105                  */
106                 int len;
107 
108                 len = runtime->buffer_size - line6pcm->in.pos_done;
109 
110                 if (len > 0) {
111                         memcpy(runtime->dma_area +
112                                line6pcm->in.pos_done * bytes_per_frame, fbuf,
113                                len * bytes_per_frame);
114                         memcpy(runtime->dma_area, fbuf + len * bytes_per_frame,
115                                (frames - len) * bytes_per_frame);
116                 } else {
117                         /* this is somewhat paranoid */
118                         dev_err(line6pcm->line6->ifcdev,
119                                 "driver bug: len = %d\n", len);
120                 }
121         } else {
122                 /* copy single chunk */
123                 memcpy(runtime->dma_area +
124                        line6pcm->in.pos_done * bytes_per_frame, fbuf, fsize);
125         }
126 
127         line6pcm->in.pos_done += frames;
128         if (line6pcm->in.pos_done >= runtime->buffer_size)
129                 line6pcm->in.pos_done -= runtime->buffer_size;
130 }
131 
132 void line6_capture_check_period(struct snd_line6_pcm *line6pcm, int length)
133 {
134         struct snd_pcm_substream *substream =
135             get_substream(line6pcm, SNDRV_PCM_STREAM_CAPTURE);
136 
137         line6pcm->in.bytes += length;
138         if (line6pcm->in.bytes >= line6pcm->in.period) {
139                 line6pcm->in.bytes %= line6pcm->in.period;
140                 spin_unlock(&line6pcm->in.lock);
141                 snd_pcm_period_elapsed(substream);
142                 spin_lock(&line6pcm->in.lock);
143         }
144 }
145 
146 /*
147  * Callback for completed capture URB.
148  */
149 static void audio_in_callback(struct urb *urb)
150 {
151         int i, index, length = 0, shutdown = 0;
152         unsigned long flags;
153 
154         struct snd_line6_pcm *line6pcm = (struct snd_line6_pcm *)urb->context;
155 
156         line6pcm->in.last_frame = urb->start_frame;
157 
158         /* find index of URB */
159         for (index = 0; index < line6pcm->line6->iso_buffers; ++index)
160                 if (urb == line6pcm->in.urbs[index])
161                         break;
162 
163         spin_lock_irqsave(&line6pcm->in.lock, flags);
164 
165         for (i = 0; i < LINE6_ISO_PACKETS; ++i) {
166                 char *fbuf;
167                 int fsize;
168                 struct usb_iso_packet_descriptor *fin = &urb->iso_frame_desc[i];
169 
170                 if (fin->status == -EXDEV) {
171                         shutdown = 1;
172                         break;
173                 }
174 
175                 fbuf = urb->transfer_buffer + fin->offset;
176                 fsize = fin->actual_length;
177 
178                 if (fsize > line6pcm->max_packet_size_in) {
179                         dev_err(line6pcm->line6->ifcdev,
180                                 "driver and/or device bug: packet too large (%d > %d)\n",
181                                 fsize, line6pcm->max_packet_size_in);
182                 }
183 
184                 length += fsize;
185 
186                 BUILD_BUG_ON_MSG(LINE6_ISO_PACKETS != 1,
187                         "The following code assumes LINE6_ISO_PACKETS == 1");
188                 /* TODO:
189                  * Also, if iso_buffers != 2, the prev frame is almost random at
190                  * playback side.
191                  * This needs to be redesigned. It should be "stable", but we may
192                  * experience sync problems on such high-speed configs.
193                  */
194 
195                 line6pcm->prev_fbuf = fbuf;
196                 line6pcm->prev_fsize = fsize /
197                         (line6pcm->properties->bytes_per_channel *
198                         line6pcm->properties->capture_hw.channels_max);
199 
200                 if (!test_bit(LINE6_STREAM_IMPULSE, &line6pcm->in.running) &&
201                     test_bit(LINE6_STREAM_PCM, &line6pcm->in.running) &&
202                     fsize > 0)
203                         line6_capture_copy(line6pcm, fbuf, fsize);
204         }
205 
206         clear_bit(index, &line6pcm->in.active_urbs);
207 
208         if (test_and_clear_bit(index, &line6pcm->in.unlink_urbs))
209                 shutdown = 1;
210 
211         if (!shutdown) {
212                 submit_audio_in_urb(line6pcm);
213 
214                 if (!test_bit(LINE6_STREAM_IMPULSE, &line6pcm->in.running) &&
215                     test_bit(LINE6_STREAM_PCM, &line6pcm->in.running))
216                         line6_capture_check_period(line6pcm, length);
217         }
218 
219         spin_unlock_irqrestore(&line6pcm->in.lock, flags);
220 }
221 
222 /* open capture callback */
223 static int snd_line6_capture_open(struct snd_pcm_substream *substream)
224 {
225         int err;
226         struct snd_pcm_runtime *runtime = substream->runtime;
227         struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream);
228 
229         err = snd_pcm_hw_constraint_ratdens(runtime, 0,
230                                             SNDRV_PCM_HW_PARAM_RATE,
231                                             &line6pcm->properties->rates);
232         if (err < 0)
233                 return err;
234 
235         line6_pcm_acquire(line6pcm, LINE6_STREAM_CAPTURE_HELPER, false);
236 
237         runtime->hw = line6pcm->properties->capture_hw;
238         return 0;
239 }
240 
241 /* close capture callback */
242 static int snd_line6_capture_close(struct snd_pcm_substream *substream)
243 {
244         struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream);
245 
246         line6_pcm_release(line6pcm, LINE6_STREAM_CAPTURE_HELPER);
247         return 0;
248 }
249 
250 /* capture operators */
251 const struct snd_pcm_ops snd_line6_capture_ops = {
252         .open = snd_line6_capture_open,
253         .close = snd_line6_capture_close,
254         .ioctl = snd_pcm_lib_ioctl,
255         .hw_params = snd_line6_hw_params,
256         .hw_free = snd_line6_hw_free,
257         .prepare = snd_line6_prepare,
258         .trigger = snd_line6_trigger,
259         .pointer = snd_line6_pointer,
260 };
261 
262 int line6_create_audio_in_urbs(struct snd_line6_pcm *line6pcm)
263 {
264         struct usb_line6 *line6 = line6pcm->line6;
265         int i;
266 
267         line6pcm->in.urbs = kzalloc(
268                 sizeof(struct urb *) * line6->iso_buffers, GFP_KERNEL);
269         if (line6pcm->in.urbs == NULL)
270                 return -ENOMEM;
271 
272         /* create audio URBs and fill in constant values: */
273         for (i = 0; i < line6->iso_buffers; ++i) {
274                 struct urb *urb;
275 
276                 /* URB for audio in: */
277                 urb = line6pcm->in.urbs[i] =
278                     usb_alloc_urb(LINE6_ISO_PACKETS, GFP_KERNEL);
279 
280                 if (urb == NULL)
281                         return -ENOMEM;
282 
283                 urb->dev = line6->usbdev;
284                 urb->pipe =
285                     usb_rcvisocpipe(line6->usbdev,
286                                     line6->properties->ep_audio_r &
287                                     USB_ENDPOINT_NUMBER_MASK);
288                 urb->transfer_flags = URB_ISO_ASAP;
289                 urb->start_frame = -1;
290                 urb->number_of_packets = LINE6_ISO_PACKETS;
291                 urb->interval = LINE6_ISO_INTERVAL;
292                 urb->error_count = 0;
293                 urb->complete = audio_in_callback;
294         }
295 
296         return 0;
297 }
298 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

osdn.jp