📄 em28xx-audio.c
字号:
/* * Empiatech em28x1 audio extension * * Copyright (C) 2006 Markus Rechberger <mrechberger@gmail.com> * * Copyright (C) 2007 Mauro Carvalho Chehab <mchehab@infradead.org> * - Port to work with the in-kernel driver * - Several cleanups * * This driver is based on my previous au600 usb pstn audio driver * and inherits all the copyrights * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */#include <linux/kernel.h>#include <linux/usb.h>#include <linux/init.h>#include <linux/sound.h>#include <linux/spinlock.h>#include <linux/soundcard.h>#include <linux/slab.h>#include <linux/vmalloc.h>#include <linux/proc_fs.h>#include <linux/module.h>#include "compat.h"#include <sound/core.h>#include <sound/pcm.h>#include <sound/pcm_params.h>#include <sound/info.h>#include <sound/initval.h>#include <sound/control.h>#include <media/v4l2-common.h>#include "em28xx.h"static int debug;module_param(debug, int, 0644);MODULE_PARM_DESC(debug, "activates debug info");#define dprintk(fmt, arg...) do { \ if (debug) \ printk(KERN_INFO "em28xx-audio %s: " fmt, \ __func__, ##arg); \ } while (0)static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;static int em28xx_isoc_audio_deinit(struct em28xx *dev){ int i; dprintk("Stopping isoc\n"); for (i = 0; i < EM28XX_AUDIO_BUFS; i++) { usb_kill_urb(dev->adev->urb[i]); usb_free_urb(dev->adev->urb[i]); dev->adev->urb[i] = NULL; } return 0;}#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)static void em28xx_audio_isocirq(struct urb *urb, struct pt_regs *regs)#elsestatic void em28xx_audio_isocirq(struct urb *urb)#endif{ struct em28xx *dev = urb->context; int i; unsigned int oldptr; unsigned long flags; int period_elapsed = 0; int status; unsigned char *cp; unsigned int stride;#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) snd_pcm_substream_t *substream; snd_pcm_runtime_t *runtime;#else struct snd_pcm_substream *substream; struct snd_pcm_runtime *runtime;#endif if (dev->adev->capture_pcm_substream) { substream = dev->adev->capture_pcm_substream; runtime = substream->runtime; stride = runtime->frame_bits >> 3; for (i = 0; i < urb->number_of_packets; i++) { int length = urb->iso_frame_desc[i].actual_length / stride; cp = (unsigned char *)urb->transfer_buffer + urb->iso_frame_desc[i].offset; if (!length) continue; spin_lock_irqsave(&dev->adev->slock, flags); oldptr = dev->adev->hwptr_done_capture; dev->adev->hwptr_done_capture += length; if (dev->adev->hwptr_done_capture >= runtime->buffer_size) dev->adev->hwptr_done_capture -= runtime->buffer_size; dev->adev->capture_transfer_done += length; if (dev->adev->capture_transfer_done >= runtime->period_size) { dev->adev->capture_transfer_done -= runtime->period_size; period_elapsed = 1; } spin_unlock_irqrestore(&dev->adev->slock, flags); if (oldptr + length >= runtime->buffer_size) { unsigned int cnt = runtime->buffer_size - oldptr; memcpy(runtime->dma_area + oldptr * stride, cp, cnt * stride); memcpy(runtime->dma_area, cp + cnt * stride, length * stride - cnt * stride); } else { memcpy(runtime->dma_area + oldptr * stride, cp, length * stride); } } if (period_elapsed) snd_pcm_period_elapsed(substream); } urb->status = 0; if (dev->adev->shutdown) return; status = usb_submit_urb(urb, GFP_ATOMIC); if (status < 0) { em28xx_errdev("resubmit of audio urb failed (error=%i)\n", status); } return;}static int em28xx_init_audio_isoc(struct em28xx *dev){ int i, errCode; const int sb_size = EM28XX_NUM_AUDIO_PACKETS * EM28XX_AUDIO_MAX_PACKET_SIZE; dprintk("Starting isoc transfers\n"); for (i = 0; i < EM28XX_AUDIO_BUFS; i++) { struct urb *urb; int j, k; dev->adev->transfer_buffer[i] = kmalloc(sb_size, GFP_ATOMIC); if (!dev->adev->transfer_buffer[i]) return -ENOMEM; memset(dev->adev->transfer_buffer[i], 0x80, sb_size); urb = usb_alloc_urb(EM28XX_NUM_AUDIO_PACKETS, GFP_ATOMIC); if (!urb) { em28xx_errdev("usb_alloc_urb failed!\n"); for (j = 0; j < i; j++) { usb_free_urb(dev->adev->urb[j]); kfree(dev->adev->transfer_buffer[j]); } return -ENOMEM; } urb->dev = dev->udev; urb->context = dev; urb->pipe = usb_rcvisocpipe(dev->udev, 0x83); urb->transfer_flags = URB_ISO_ASAP; urb->transfer_buffer = dev->adev->transfer_buffer[i]; urb->interval = 1; urb->complete = em28xx_audio_isocirq; urb->number_of_packets = EM28XX_NUM_AUDIO_PACKETS; urb->transfer_buffer_length = sb_size; for (j = k = 0; j < EM28XX_NUM_AUDIO_PACKETS; j++, k += EM28XX_AUDIO_MAX_PACKET_SIZE) { urb->iso_frame_desc[j].offset = k; urb->iso_frame_desc[j].length = EM28XX_AUDIO_MAX_PACKET_SIZE; } dev->adev->urb[i] = urb; } for (i = 0; i < EM28XX_AUDIO_BUFS; i++) { errCode = usb_submit_urb(dev->adev->urb[i], GFP_ATOMIC); if (errCode) { em28xx_isoc_audio_deinit(dev); return errCode; } } return 0;}static int em28xx_cmd(struct em28xx *dev, int cmd, int arg){ dprintk("%s transfer\n", (dev->adev->capture_stream == STREAM_ON)? "stop" : "start"); switch (cmd) { case EM28XX_CAPTURE_STREAM_EN: if (dev->adev->capture_stream == STREAM_OFF && arg == 1) { dev->adev->capture_stream = STREAM_ON; em28xx_init_audio_isoc(dev); } else if (dev->adev->capture_stream == STREAM_ON && arg == 0) { dev->adev->capture_stream = STREAM_OFF; em28xx_isoc_audio_deinit(dev); } else { printk(KERN_ERR "An underrun very likely occurred. " "Ignoring it.\n"); } return 0; default: return -EINVAL; }}#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16)static int snd_pcm_alloc_vmalloc_buffer(snd_pcm_substream_t *subs, size_t size)#elsestatic int snd_pcm_alloc_vmalloc_buffer(struct snd_pcm_substream *subs, size_t size)#endif{#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) snd_pcm_runtime_t *runtime = subs->runtime;#else struct snd_pcm_runtime *runtime = subs->runtime;#endif dprintk("Alocating vbuffer\n"); if (runtime->dma_area) { if (runtime->dma_bytes > size) return 0; vfree(runtime->dma_area); } runtime->dma_area = vmalloc(size); if (!runtime->dma_area) return -ENOMEM; runtime->dma_bytes = size; return 0;}#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16)static snd_pcm_hardware_t snd_em28xx_hw_capture = {#elsestatic struct snd_pcm_hardware snd_em28xx_hw_capture = {#endif .info = SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP_VALID, .formats = SNDRV_PCM_FMTBIT_S16_LE, .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_KNOT, .rate_min = 48000, .rate_max = 48000, .channels_min = 2, .channels_max = 2, .buffer_bytes_max = 62720 * 8, /* just about the value in usbaudio.c */ .period_bytes_min = 64, /* 12544/2, */ .period_bytes_max = 12544, .periods_min = 2, .periods_max = 98, /* 12544, */};#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16)static int snd_em28xx_capture_open(snd_pcm_substream_t *substream)#elsestatic int snd_em28xx_capture_open(struct snd_pcm_substream *substream)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -