V4L/DVB (6951): Integrates em28xx-audio.c into em28xx kernel module

Also fixes the remaining CodingStyle issues that Lindent didn't fix.

Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
This commit is contained in:
Mauro Carvalho Chehab 2008-01-05 09:57:31 -03:00
parent 1a6f11e0e8
commit 6d79468dd8
5 changed files with 368 additions and 263 deletions

View File

@ -11,3 +11,18 @@ config VIDEO_EM28XX
To compile this driver as a module, choose M here: the
module will be called em28xx
config VIDEO_EM28XX_ALSA
depends on VIDEO_EM28XX
tristate "Empia EM28xx ALSA audio module"
---help---
This is an ALSA driver for some Empia 28xx based TV cards.
This is not required for em2800/em2820/em2821 boards. However,
newer em28xx devices uses Vendor Class for audio, instead of
implementing the USB Audio Class. For those chips, this module
will enable digital audio.
To compile this driver as a module, choose M here: the
module will be called em28xx-alsa

View File

@ -1,7 +1,10 @@
em28xx-objs := em28xx-video.o em28xx-i2c.o em28xx-cards.o em28xx-core.o \
em28xx-input.o
em28xx-alsa-objs := em28xx-audio.o
obj-$(CONFIG_VIDEO_EM28XX) += em28xx.o
obj-$(CONFIG_VIDEO_EM28XX_ALSA) += em28xx-alsa.o
EXTRA_CFLAGS += -Idrivers/media/video
EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core

View File

@ -3,6 +3,10 @@
*
* 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
*
@ -30,7 +34,7 @@
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/proc_fs.h>
#include <linux/moduleparam.h>
#include <linux/module.h>
#include <sound/driver.h>
#include <sound/core.h>
#include <sound/pcm.h>
@ -38,222 +42,52 @@
#include <sound/info.h>
#include <sound/initval.h>
#include <sound/control.h>
//#include <linux/video_decoder.h>
//#include <media/tuner.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, \
__FUNCTION__, ##arg); \
} while (0)
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
static int em28xx_cmd(struct em28xx *dev, int cmd, int arg);
static int snd_pcm_alloc_vmalloc_buffer(struct snd_pcm_substream *subs,
size_t size)
static int em28xx_isoc_audio_deinit(struct em28xx *dev)
{
struct snd_pcm_runtime *runtime = subs->runtime;
if (runtime->dma_area) {
if (runtime->dma_bytes > size)
return 0;
vfree(runtime->dma_area);
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;
}
runtime->dma_area = vmalloc(size);
if (!runtime->dma_area)
return -ENOMEM;
runtime->dma_bytes = size;
return 0;
}
static struct snd_pcm_hardware snd_em28xx_hw_capture = {
.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,
};
static int snd_em28xx_capture_open(struct snd_pcm_substream *substream)
{
int ret = 0;
int mode;
struct em28xx *dev = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
printk("opening radio device and trying to acquire exclusive lock\n");
switch (dev->mode) {
case TUNER_STUB_DVBC_TV:
case TUNER_STUB_DVBT_TV:
case TUNER_STUB_ATSC_TV:
/* digital has no support for analog audio */
if (ret != 0) {
printk("device is already in use by DVB-T\n");
return -EINVAL;
} else {
struct v4l2_tuner tuner;
printk("switching device to FM mode\n");
mode = TUNER_STUB_RADIO;
memset(&tuner, 0x0, sizeof(struct v4l2_tuner));
tuner.type = V4L2_TUNER_RADIO;
/* enable GPIO for analog TV */
dev->em28xx_gpio_control(dev, EM28XX_MODE,
(void *)mode);
dev->mode = mode;
/* upload firmware */
tuner_run_cmd(dev->tobj, TUNER_CMD_INIT, (void *)mode);
/* required for devices which have kerneldriver dependencies */
// em28xx_config(dev);
// em28xx_config_i2c(dev);
/* this is moreover to switch the decoder to FM */
em28xx_i2c_call_clients(dev, VIDIOC_S_TUNER, &tuner);
dev->em28xx_write_regs(dev, 0x0f, "\x87", 1);
ret = dev->em28xx_acquire(dev, EM28XX_RADIO, 1);
em28xx_i2c_call_clients(dev, VIDIOC_INT_RESET, 0);
/* TODO switch to FM mode */
printk("em28xx-audio: %d mode\n", mode);
tuner_run_cmd(dev->tobj, TUNER_CMD_G_MODE, &mode);
printk("retrieved mode from tuner: %d\n", mode);
}
break;
case TUNER_STUB_ANALOG_TV:
printk("em28xx-audio: device is currently in analog TV mode\n");
/* unmute by default */
dev->em28xx_write_regs(dev, 0x0f, "\x87", 1);
break;
case TUNER_STUB_RADIO:
/* check current mode and put a hard lock onto it */
printk
("em28xx-audio: device is currently in analogue FM mode\n");
/* unmute by default here */
dev->em28xx_write_regs(dev, 0x0f, "\x87", 1);
ret = dev->em28xx_acquire(dev, EM28XX_RADIO, 1);
if (ret == 0)
printk("device is locked in fmradio mode now\n");
break;
default:
printk("em28xx-audio: unhandled mode %d\n", dev->mode);
}
runtime->hw = snd_em28xx_hw_capture;
if (dev->alt == 0 && dev->adev->users == 0) {
int errCode;
dev->alt = 7;
errCode = usb_set_interface(dev->udev, 0, 7);
printk("changing alternate number to 7\n");
}
dev->adev->users++;
snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
dev->adev->capture_pcm_substream = substream;
runtime->private_data = dev;
return 0;
}
static int snd_em28xx_pcm_close(struct snd_pcm_substream *substream)
{
struct em28xx *dev = snd_pcm_substream_chip(substream);
int amode = 0;
dev->adev->users--;
/* decrease audio reference */
switch (dev->mode) {
case TUNER_STUB_ANALOG_TV:
amode = EM28XX_VIDEO;
break;
case TUNER_STUB_RADIO:
amode = EM28XX_RADIO;
break;
default:
printk("invalid mode: %d\n", dev->mode);
break;
}
dev->em28xx_acquire(dev, amode, 0);
if (dev->adev->users == 0 && dev->adev->shutdown == 1) {
printk("audio users: %d\n", dev->adev->users);
printk("disabling audio stream!\n");
dev->adev->shutdown = 0;
printk("released lock\n");
em28xx_cmd(dev, EM28XX_CAPTURE_STREAM_EN, 0);
}
return 0;
}
static int snd_em28xx_hw_capture_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
unsigned int channels, rate, format;
int ret;
ret =
snd_pcm_alloc_vmalloc_buffer(substream,
params_buffer_bytes(hw_params));
format = params_format(hw_params);
rate = params_rate(hw_params);
channels = params_channels(hw_params);
/* TODO: set up em28xx audio chip to deliver the correct audio format, current default is 48000hz multiplexed => 96000hz mono
which shouldn't matter since analogue TV only supports mono */
return 0;
}
static int snd_em28xx_hw_capture_free(struct snd_pcm_substream *substream)
{
struct em28xx *dev = snd_pcm_substream_chip(substream);
if (dev->adev->capture_stream == STREAM_ON) {
em28xx_cmd(dev, EM28XX_CAPTURE_STREAM_EN, 0);
}
return 0;
}
static int snd_em28xx_prepare(struct snd_pcm_substream *substream)
{
return 0;
}
static int snd_em28xx_capture_trigger(struct snd_pcm_substream *substream,
int cmd)
{
struct em28xx *dev = snd_pcm_substream_chip(substream);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
em28xx_cmd(dev, EM28XX_CAPTURE_STREAM_EN, 1);
return 0;
case SNDRV_PCM_TRIGGER_STOP:
dev->adev->shutdown = 1;
return 0;
default:
return -EINVAL;
}
}
static void em28xx_audio_isocirq(struct urb *urb)
{
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;
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;
struct snd_pcm_substream *substream;
struct snd_pcm_runtime *runtime;
struct snd_pcm_runtime *runtime;
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;
@ -264,6 +98,7 @@ static void em28xx_audio_isocirq(struct urb *urb)
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 >=
@ -278,6 +113,7 @@ static void em28xx_audio_isocirq(struct urb *urb)
runtime->period_size;
period_elapsed = 1;
}
spin_unlock_irqrestore(&dev->adev->slock, flags);
if (oldptr + length >= runtime->buffer_size) {
@ -292,82 +128,79 @@ static void em28xx_audio_isocirq(struct urb *urb)
length * stride);
}
}
if (period_elapsed) {
if (period_elapsed)
snd_pcm_period_elapsed(substream);
}
}
urb->status = 0;
if (dev->adev->shutdown)
return;
if ((status = usb_submit_urb(urb, GFP_ATOMIC))) {
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_isoc_audio_deinit(struct em28xx *dev)
{
int i;
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;
}
static int em28xx_init_audio_isoc(struct em28xx *dev)
{
int i;
int errCode;
const int sb_size =
EM28XX_NUM_AUDIO_PACKETS * EM28XX_AUDIO_MAX_PACKET_SIZE;
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]) {
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) {
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;
} else {
if (!urb)
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) {
@ -377,8 +210,8 @@ static int em28xx_cmd(struct em28xx *dev, int cmd, int arg)
dev->adev->capture_stream = STREAM_OFF;
em28xx_isoc_audio_deinit(dev);
} else {
printk
("An underrun occured very likely... ignoring it\n");
printk(KERN_ERR "An underrun very likely occurred. "
"Ignoring it.\n");
}
return 0;
default:
@ -386,13 +219,167 @@ static int em28xx_cmd(struct em28xx *dev, int cmd, int arg)
}
}
static int snd_pcm_alloc_vmalloc_buffer(struct snd_pcm_substream *subs,
size_t size)
{
struct snd_pcm_runtime *runtime = subs->runtime;
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;
}
static struct snd_pcm_hardware snd_em28xx_hw_capture = {
.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, */
};
static int snd_em28xx_capture_open(struct snd_pcm_substream *substream)
{
struct em28xx *dev = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
int ret = 0;
dprintk("opening device and trying to acquire exclusive lock\n");
/* Sets volume, mute, etc */
dev->mute = 0;
ret = em28xx_audio_analog_set(dev);
if (ret < 0)
goto err;
runtime->hw = snd_em28xx_hw_capture;
if (dev->alt == 0 && dev->adev->users == 0) {
int errCode;
dev->alt = 7;
errCode = usb_set_interface(dev->udev, 0, 7);
dprintk("changing alternate number to 7\n");
}
dev->adev->users++;
snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
dev->adev->capture_pcm_substream = substream;
runtime->private_data = dev;
return 0;
err:
printk(KERN_ERR "Error while configuring em28xx mixer\n");
return ret;
}
static int snd_em28xx_pcm_close(struct snd_pcm_substream *substream)
{
struct em28xx *dev = snd_pcm_substream_chip(substream);
dev->adev->users--;
dprintk("closing device\n");
dev->mute = 1;
em28xx_audio_analog_set(dev);
if (dev->adev->users == 0 && dev->adev->shutdown == 1) {
dprintk("audio users: %d\n", dev->adev->users);
dprintk("disabling audio stream!\n");
dev->adev->shutdown = 0;
dprintk("released lock\n");
em28xx_cmd(dev, EM28XX_CAPTURE_STREAM_EN, 0);
}
return 0;
}
static int snd_em28xx_hw_capture_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
unsigned int channels, rate, format;
int ret;
dprintk("Setting capture parameters\n");
ret = snd_pcm_alloc_vmalloc_buffer(substream,
params_buffer_bytes(hw_params));
format = params_format(hw_params);
rate = params_rate(hw_params);
channels = params_channels(hw_params);
/* TODO: set up em28xx audio chip to deliver the correct audio format,
current default is 48000hz multiplexed => 96000hz mono
which shouldn't matter since analogue TV only supports mono */
return 0;
}
static int snd_em28xx_hw_capture_free(struct snd_pcm_substream *substream)
{
struct em28xx *dev = snd_pcm_substream_chip(substream);
dprintk("Stop capture, if needed\n");
if (dev->adev->capture_stream == STREAM_ON)
em28xx_cmd(dev, EM28XX_CAPTURE_STREAM_EN, 0);
return 0;
}
static int snd_em28xx_prepare(struct snd_pcm_substream *substream)
{
return 0;
}
static int snd_em28xx_capture_trigger(struct snd_pcm_substream *substream,
int cmd)
{
struct em28xx *dev = snd_pcm_substream_chip(substream);
dprintk("Should %s capture\n", (cmd == SNDRV_PCM_TRIGGER_START)?
"start": "stop");
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
em28xx_cmd(dev, EM28XX_CAPTURE_STREAM_EN, 1);
return 0;
case SNDRV_PCM_TRIGGER_STOP:
dev->adev->shutdown = 1;
return 0;
default:
return -EINVAL;
}
}
static snd_pcm_uframes_t snd_em28xx_capture_pointer(struct snd_pcm_substream
*substream)
{
struct em28xx *dev;
snd_pcm_uframes_t hwptr_done;
dev = snd_pcm_substream_chip(substream);
hwptr_done = dev->adev->hwptr_done_capture;
return hwptr_done;
}
@ -400,34 +387,38 @@ static struct page *snd_pcm_get_vmalloc_page(struct snd_pcm_substream *subs,
unsigned long offset)
{
void *pageptr = subs->runtime->dma_area + offset;
return vmalloc_to_page(pageptr);
}
static struct snd_pcm_ops snd_em28xx_pcm_capture = {
.open = snd_em28xx_capture_open,
.close = snd_em28xx_pcm_close,
.ioctl = snd_pcm_lib_ioctl,
.open = snd_em28xx_capture_open,
.close = snd_em28xx_pcm_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_em28xx_hw_capture_params,
.hw_free = snd_em28xx_hw_capture_free,
.prepare = snd_em28xx_prepare,
.trigger = snd_em28xx_capture_trigger,
.pointer = snd_em28xx_capture_pointer,
.page = snd_pcm_get_vmalloc_page,
.hw_free = snd_em28xx_hw_capture_free,
.prepare = snd_em28xx_prepare,
.trigger = snd_em28xx_capture_trigger,
.pointer = snd_em28xx_capture_pointer,
.page = snd_pcm_get_vmalloc_page,
};
static int em28xx_audio_init(struct em28xx *dev)
{
struct em28xx_audio *adev;
struct snd_pcm *pcm;
struct snd_card *card;
static int devnr;
int ret;
int err;
printk("em28xx-audio.c: probing for em28x1 non standard usbaudio\n");
printk("em28xx-audio.c: Copyright (C) 2006 Markus Rechberger\n");
struct snd_pcm *pcm;
struct snd_card *card;
static int devnr;
int ret, err;
printk(KERN_INFO "em28xx-audio.c: probing for em28x1 "
"non standard usbaudio\n");
printk(KERN_INFO "em28xx-audio.c: Copyright (C) 2006 Markus "
"Rechberger\n");
adev = kzalloc(sizeof(*adev), GFP_KERNEL);
if (!adev) {
printk("em28xx-audio.c: out of memory\n");
printk(KERN_ERR "em28xx-audio.c: out of memory\n");
return -1;
}
card = snd_card_new(index[devnr], "Em28xx Audio", THIS_MODULE, 0);
@ -446,13 +437,15 @@ static int em28xx_audio_init(struct em28xx *dev)
strcpy(card->shortname, "Em28xx Audio");
strcpy(card->longname, "Empia Em28xx Audio");
if ((err = snd_card_register(card)) < 0) {
err = snd_card_register(card);
if (err < 0) {
snd_card_free(card);
return -ENOMEM;
}
adev->sndcard = card;
adev->udev = dev->udev;
dev->adev = adev;
return 0;
}
@ -460,16 +453,18 @@ static int em28xx_audio_fini(struct em28xx *dev)
{
if (dev == NULL)
return 0;
if (dev->adev) {
snd_card_free(dev->adev->sndcard);
kfree(dev->adev);
dev->adev = NULL;
}
return 0;
}
static struct em28xx_ops audio_ops = {
.id = EM28XX_AUDIO,
.id = EM28XX_AUDIO,
.name = "Em28xx Audio Extension",
.init = em28xx_audio_init,
.fini = em28xx_audio_fini,
@ -478,7 +473,6 @@ static struct em28xx_ops audio_ops = {
static int __init em28xx_alsa_register(void)
{
request_module("em28xx");
request_module("tuner");
return em28xx_register_extension(&audio_ops);
}
@ -489,6 +483,7 @@ static void __exit em28xx_alsa_unregister(void)
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Markus Rechberger <mrechberger@gmail.com>");
MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>");
MODULE_DESCRIPTION("Em28xx Audio driver");
module_init(em28xx_alsa_register);

View File

@ -1599,9 +1599,48 @@ static const struct video_device em28xx_video_template = {
.current_norm = V4L2_STD_PAL,
};
/******************************** usb interface *****************************************/
static LIST_HEAD(em28xx_extension_devlist);
static DEFINE_MUTEX(em28xx_extension_devlist_lock);
int em28xx_register_extension(struct em28xx_ops *ops)
{
struct em28xx *h, *dev = NULL;
list_for_each_entry(h, &em28xx_devlist, devlist)
dev = h;
mutex_lock(&em28xx_extension_devlist_lock);
list_add_tail(&ops->next, &em28xx_extension_devlist);
if (dev)
ops->init(dev);
printk(KERN_INFO "Em28xx: Initialized (%s) extension\n", ops->name);
mutex_unlock(&em28xx_extension_devlist_lock);
return 0;
}
EXPORT_SYMBOL(em28xx_register_extension);
void em28xx_unregister_extension(struct em28xx_ops *ops)
{
struct em28xx *h, *dev = NULL;
list_for_each_entry(h, &em28xx_devlist, devlist)
dev = h;
if (dev)
ops->fini(dev);
mutex_lock(&em28xx_extension_devlist_lock);
printk(KERN_INFO "Em28xx: Removed (%s) extension\n", ops->name);
list_del(&ops->next);
mutex_unlock(&em28xx_extension_devlist_lock);
}
EXPORT_SYMBOL(em28xx_unregister_extension);
/*
* em28xx_init_dev()
* allocates and inits the device structs, registers i2c bus and v4l device
@ -1609,6 +1648,7 @@ static const struct video_device em28xx_video_template = {
static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
int minor)
{
struct em28xx_ops *ops = NULL;
struct em28xx *dev = *devhandle;
int retval = -ENOMEM;
int errCode;
@ -1742,6 +1782,15 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
dev->vdev->minor-MINOR_VFL_TYPE_GRABBER_MIN,
dev->vbi_dev->minor-MINOR_VFL_TYPE_VBI_MIN);
mutex_lock(&em28xx_extension_devlist_lock);
if (!list_empty(&em28xx_extension_devlist)) {
list_for_each_entry(ops, &em28xx_extension_devlist, next) {
if (ops->id)
ops->init(dev);
}
}
mutex_unlock(&em28xx_extension_devlist_lock);
return 0;
}
@ -1862,6 +1911,7 @@ static int em28xx_usb_probe(struct usb_interface *interface,
static void em28xx_usb_disconnect(struct usb_interface *interface)
{
struct em28xx *dev;
struct em28xx_ops *ops = NULL;
dev = usb_get_intfdata(interface);
usb_set_intfdata(interface, NULL);
@ -1891,15 +1941,20 @@ static void em28xx_usb_disconnect(struct usb_interface *interface)
dev->state |= DEV_DISCONNECTED;
em28xx_release_resources(dev);
}
mutex_unlock(&dev->lock);
mutex_lock(&em28xx_extension_devlist_lock);
if (!list_empty(&em28xx_extension_devlist)) {
list_for_each_entry(ops, &em28xx_extension_devlist, next) {
ops->fini(dev);
}
}
mutex_unlock(&em28xx_extension_devlist_lock);
if (!dev->users) {
kfree(dev->alt_max_pkt_size);
kfree(dev);
}
}
static struct usb_driver em28xx_usb_driver = {

View File

@ -212,6 +212,28 @@ enum em28xx_dev_state {
DEV_MISCONFIGURED = 0x04,
};
#define EM28XX_AUDIO_BUFS 5
#define EM28XX_NUM_AUDIO_PACKETS 64
#define EM28XX_AUDIO_MAX_PACKET_SIZE 196 /* static value */
#define EM28XX_CAPTURE_STREAM_EN 1
#define EM28XX_AUDIO 0x10
struct em28xx_audio {
char name[50];
char *transfer_buffer[EM28XX_AUDIO_BUFS];
struct urb *urb[EM28XX_AUDIO_BUFS];
struct usb_device *udev;
unsigned int capture_transfer_done;
struct snd_pcm_substream *capture_pcm_substream;
unsigned int hwptr_done_capture;
struct snd_card *sndcard;
int users, shutdown;
enum em28xx_stream_state capture_stream;
spinlock_t slock;
};
/* main device struct */
struct em28xx {
/* generic device properties */
@ -266,6 +288,8 @@ struct em28xx {
unsigned long hash; /* eeprom hash - for boards with generic ID */
unsigned long i2c_hash; /* i2c devicelist hash - for boards with generic ID */
struct em28xx_audio *adev;
/* states */
enum em28xx_dev_state state;
enum em28xx_stream_state stream;
@ -302,6 +326,15 @@ struct em28xx {
struct em28xx_fh {
struct em28xx *dev;
unsigned int stream_on:1; /* Locks streams */
int radio;
};
struct em28xx_ops {
struct list_head next;
char *name;
int id;
int (*init)(struct em28xx *);
int (*fini)(struct em28xx *);
};
/* Provided by em28xx-i2c.c */
@ -341,6 +374,10 @@ int em28xx_init_isoc(struct em28xx *dev);
void em28xx_uninit_isoc(struct em28xx *dev);
int em28xx_set_alternate(struct em28xx *dev);
/* Provided by em28xx-video.c */
int em28xx_register_extension(struct em28xx_ops *dev);
void em28xx_unregister_extension(struct em28xx_ops *dev);
/* Provided by em28xx-cards.c */
extern int em2800_variant_detect(struct usb_device* udev,int model);
extern void em28xx_pre_card_setup(struct em28xx *dev);