1#include <linux/kernel.h> 2#include <linux/usb.h> 3#include <linux/init.h> 4#include <linux/sound.h> 5#include <linux/spinlock.h> 6#include <linux/soundcard.h> 7#include <linux/vmalloc.h> 8#include <linux/proc_fs.h> 9#include <linux/module.h> 10#include <linux/gfp.h> 11#include <sound/core.h> 12#include <sound/pcm.h> 13#include <sound/pcm_params.h> 14#include <sound/info.h> 15#include <sound/initval.h> 16#include <sound/control.h> 17#include <media/v4l2-common.h> 18#include "pd-common.h" 19#include "vendorcmds.h" 20 21static void complete_handler_audio(struct urb *urb); 22#define AUDIO_EP (0x83) 23#define AUDIO_BUF_SIZE (512) 24#define PERIOD_SIZE (1024 * 8) 25#define PERIOD_MIN (4) 26#define PERIOD_MAX PERIOD_MIN 27 28static struct snd_pcm_hardware snd_pd_hw_capture = { 29 .info = SNDRV_PCM_INFO_BLOCK_TRANSFER | 30 SNDRV_PCM_INFO_MMAP | 31 SNDRV_PCM_INFO_INTERLEAVED | 32 SNDRV_PCM_INFO_MMAP_VALID, 33 34 .formats = SNDRV_PCM_FMTBIT_S16_LE, 35 .rates = SNDRV_PCM_RATE_48000, 36 37 .rate_min = 48000, 38 .rate_max = 48000, 39 .channels_min = 2, 40 .channels_max = 2, 41 .buffer_bytes_max = PERIOD_SIZE * PERIOD_MIN, 42 .period_bytes_min = PERIOD_SIZE, 43 .period_bytes_max = PERIOD_SIZE, 44 .periods_min = PERIOD_MIN, 45 .periods_max = PERIOD_MAX, 46 /* 47 .buffer_bytes_max = 62720 * 8, 48 .period_bytes_min = 64, 49 .period_bytes_max = 12544, 50 .periods_min = 2, 51 .periods_max = 98 52 */ 53}; 54 55static int snd_pd_capture_open(struct snd_pcm_substream *substream) 56{ 57 struct poseidon *p = snd_pcm_substream_chip(substream); 58 struct poseidon_audio *pa = &p->audio; 59 struct snd_pcm_runtime *runtime = substream->runtime; 60 61 if (!p) 62 return -ENODEV; 63 pa->users++; 64 pa->card_close = 0; 65 pa->capture_pcm_substream = substream; 66 runtime->private_data = p; 67 68 runtime->hw = snd_pd_hw_capture; 69 snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); 70 usb_autopm_get_interface(p->interface); 71 kref_get(&p->kref); 72 return 0; 73} 74 75static int snd_pd_pcm_close(struct snd_pcm_substream *substream) 76{ 77 struct poseidon *p = snd_pcm_substream_chip(substream); 78 struct poseidon_audio *pa = &p->audio; 79 80 pa->users--; 81 pa->card_close = 1; 82 usb_autopm_put_interface(p->interface); 83 kref_put(&p->kref, poseidon_delete); 84 return 0; 85} 86 87static int snd_pd_hw_capture_params(struct snd_pcm_substream *substream, 88 struct snd_pcm_hw_params *hw_params) 89{ 90 struct snd_pcm_runtime *runtime = substream->runtime; 91 unsigned int size; 92 93 size = params_buffer_bytes(hw_params); 94 if (runtime->dma_area) { 95 if (runtime->dma_bytes > size) 96 return 0; 97 vfree(runtime->dma_area); 98 } 99 runtime->dma_area = vmalloc(size); 100 if (!runtime->dma_area) 101 return -ENOMEM; 102 else 103 runtime->dma_bytes = size; 104 return 0; 105} 106 107static int audio_buf_free(struct poseidon *p) 108{ 109 struct poseidon_audio *pa = &p->audio; 110 int i; 111 112 for (i = 0; i < AUDIO_BUFS; i++) 113 if (pa->urb_array[i]) 114 usb_kill_urb(pa->urb_array[i]); 115 free_all_urb_generic(pa->urb_array, AUDIO_BUFS); 116 logpm(); 117 return 0; 118} 119 120static int snd_pd_hw_capture_free(struct snd_pcm_substream *substream) 121{ 122 struct poseidon *p = snd_pcm_substream_chip(substream); 123 124 logpm(); 125 audio_buf_free(p); 126 return 0; 127} 128 129static int snd_pd_prepare(struct snd_pcm_substream *substream) 130{ 131 return 0; 132} 133 134#define AUDIO_TRAILER_SIZE (16) 135static inline void handle_audio_data(struct urb *urb, int *period_elapsed) 136{ 137 struct poseidon_audio *pa = urb->context; 138 struct snd_pcm_runtime *runtime = pa->capture_pcm_substream->runtime; 139 140 int stride = runtime->frame_bits >> 3; 141 int len = urb->actual_length / stride; 142 unsigned char *cp = urb->transfer_buffer; 143 unsigned int oldptr = pa->rcv_position; 144 145 if (urb->actual_length == AUDIO_BUF_SIZE - 4) 146 len -= (AUDIO_TRAILER_SIZE / stride); 147 148 /* do the copy */ 149 if (oldptr + len >= runtime->buffer_size) { 150 unsigned int cnt = runtime->buffer_size - oldptr; 151 152 memcpy(runtime->dma_area + oldptr * stride, cp, cnt * stride); 153 memcpy(runtime->dma_area, (cp + cnt * stride), 154 (len * stride - cnt * stride)); 155 } else 156 memcpy(runtime->dma_area + oldptr * stride, cp, len * stride); 157 158 /* update the statas */ 159 snd_pcm_stream_lock(pa->capture_pcm_substream); 160 pa->rcv_position += len; 161 if (pa->rcv_position >= runtime->buffer_size) 162 pa->rcv_position -= runtime->buffer_size; 163 164 pa->copied_position += (len); 165 if (pa->copied_position >= runtime->period_size) { 166 pa->copied_position -= runtime->period_size; 167 *period_elapsed = 1; 168 } 169 snd_pcm_stream_unlock(pa->capture_pcm_substream); 170} 171 172static void complete_handler_audio(struct urb *urb) 173{ 174 struct poseidon_audio *pa = urb->context; 175 struct snd_pcm_substream *substream = pa->capture_pcm_substream; 176 int period_elapsed = 0; 177 int ret; 178 179 if (1 == pa->card_close || pa->capture_stream != STREAM_ON) 180 return; 181 182 if (urb->status != 0) { 183 /*if (urb->status == -ESHUTDOWN)*/ 184 return; 185 } 186 187 if (substream) { 188 if (urb->actual_length) { 189 handle_audio_data(urb, &period_elapsed); 190 if (period_elapsed) 191 snd_pcm_period_elapsed(substream); 192 } 193 } 194 195 ret = usb_submit_urb(urb, GFP_ATOMIC); 196 if (ret < 0) 197 log("audio urb failed (errcod = %i)", ret); 198 return; 199} 200 201static int fire_audio_urb(struct poseidon *p) 202{ 203 int i, ret = 0; 204 struct poseidon_audio *pa = &p->audio; 205 206 alloc_bulk_urbs_generic(pa->urb_array, AUDIO_BUFS, 207 p->udev, AUDIO_EP, 208 AUDIO_BUF_SIZE, GFP_ATOMIC, 209 complete_handler_audio, pa); 210 211 for (i = 0; i < AUDIO_BUFS; i++) { 212 ret = usb_submit_urb(pa->urb_array[i], GFP_KERNEL); 213 if (ret) 214 log("urb err : %d", ret); 215 } 216 log(); 217 return ret; 218} 219 220static int snd_pd_capture_trigger(struct snd_pcm_substream *substream, int cmd) 221{ 222 struct poseidon *p = snd_pcm_substream_chip(substream); 223 struct poseidon_audio *pa = &p->audio; 224 225 if (debug_mode) 226 log("cmd %d, audio stat : %d\n", cmd, pa->capture_stream); 227 228 switch (cmd) { 229 case SNDRV_PCM_TRIGGER_RESUME: 230 case SNDRV_PCM_TRIGGER_START: 231 if (pa->capture_stream == STREAM_ON) 232 return 0; 233 234 pa->rcv_position = pa->copied_position = 0; 235 pa->capture_stream = STREAM_ON; 236 237 if (in_hibernation(p)) 238 return 0; 239 fire_audio_urb(p); 240 return 0; 241 242 case SNDRV_PCM_TRIGGER_SUSPEND: 243 pa->capture_stream = STREAM_SUSPEND; 244 return 0; 245 case SNDRV_PCM_TRIGGER_STOP: 246 pa->capture_stream = STREAM_OFF; 247 return 0; 248 default: 249 return -EINVAL; 250 } 251} 252 253static snd_pcm_uframes_t 254snd_pd_capture_pointer(struct snd_pcm_substream *substream) 255{ 256 struct poseidon *p = snd_pcm_substream_chip(substream); 257 struct poseidon_audio *pa = &p->audio; 258 return pa->rcv_position; 259} 260 261static struct page *snd_pcm_pd_get_page(struct snd_pcm_substream *subs, 262 unsigned long offset) 263{ 264 void *pageptr = subs->runtime->dma_area + offset; 265 return vmalloc_to_page(pageptr); 266} 267 268static struct snd_pcm_ops pcm_capture_ops = { 269 .open = snd_pd_capture_open, 270 .close = snd_pd_pcm_close, 271 .ioctl = snd_pcm_lib_ioctl, 272 .hw_params = snd_pd_hw_capture_params, 273 .hw_free = snd_pd_hw_capture_free, 274 .prepare = snd_pd_prepare, 275 .trigger = snd_pd_capture_trigger, 276 .pointer = snd_pd_capture_pointer, 277 .page = snd_pcm_pd_get_page, 278}; 279 280#ifdef CONFIG_PM 281int pm_alsa_suspend(struct poseidon *p) 282{ 283 logpm(p); 284 audio_buf_free(p); 285 return 0; 286} 287 288int pm_alsa_resume(struct poseidon *p) 289{ 290 logpm(p); 291 fire_audio_urb(p); 292 return 0; 293} 294#endif 295 296int poseidon_audio_init(struct poseidon *p) 297{ 298 struct poseidon_audio *pa = &p->audio; 299 struct snd_card *card; 300 struct snd_pcm *pcm; 301 int ret; 302 303 ret = snd_card_new(&p->interface->dev, -1, "Telegent", 304 THIS_MODULE, 0, &card); 305 if (ret != 0) 306 return ret; 307 308 ret = snd_pcm_new(card, "poseidon audio", 0, 0, 1, &pcm); 309 if (ret < 0) { 310 snd_card_free(card); 311 return ret; 312 } 313 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_capture_ops); 314 pcm->info_flags = 0; 315 pcm->private_data = p; 316 strcpy(pcm->name, "poseidon audio capture"); 317 318 strcpy(card->driver, "ALSA driver"); 319 strcpy(card->shortname, "poseidon Audio"); 320 strcpy(card->longname, "poseidon ALSA Audio"); 321 322 if (snd_card_register(card)) { 323 snd_card_free(card); 324 return -ENOMEM; 325 } 326 pa->card = card; 327 return 0; 328} 329 330int poseidon_audio_free(struct poseidon *p) 331{ 332 struct poseidon_audio *pa = &p->audio; 333 334 if (pa->card) 335 snd_card_free(pa->card); 336 return 0; 337} 338