staging/easycap: make OSS compilation optional instead of ALSA

OSS is deprecated yet currently it is reported to be more stable
therefore we keep it but make it optional
Revert the conditional compilation:
	add  CONFIG_EASYCAP_OSS and kill EASYCAP_NEEDS_ALSA
move oss-only code from easycap_sound.c to easycap_sound_oss.c

Cc: Mike Thomas <rmthomas@sciolus.org>
Signed-off-by: Tomas Winkler <tomas.winkler@intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
Tomas Winkler 2011-01-24 17:08:20 +02:00 committed by Greg Kroah-Hartman
parent 5c0c6c395e
commit 0edbc24c5d
7 changed files with 789 additions and 742 deletions

View file

@ -1,6 +1,6 @@
config EASYCAP
tristate "EasyCAP USB ID 05e1:0408 support"
depends on USB && VIDEO_DEV && SND
depends on USB && VIDEO_DEV && SOUND
---help---
This is an integrated audio/video driver for EasyCAP cards with
@ -15,6 +15,18 @@ config EASYCAP
To compile this driver as a module, choose M here: the
module will be called easycap
config EASYCAP_OSS
bool "OSS (DEPRECATED)"
depends on EASYCAP && SOUND_OSS_CORE
---help---
Say 'Y' if you prefer Open Sound System (OSS) interface
This will disable Advanced Linux Sound Architecture (ALSA) binding.
Once binding to ALSA interface will be stable this option will be
removed.
config EASYCAP_DEBUG
bool "Enable EasyCAP driver debugging"
depends on EASYCAP

View file

@ -1,5 +1,10 @@
easycap-objs := easycap_main.o easycap_low.o easycap_sound.o \
easycap_ioctl.o easycap_settings.o easycap_testcard.o
easycap-objs := easycap_main.o
easycap-objs += easycap_low.o
easycap-objs += easycap_ioctl.o
easycap-objs += easycap_settings.o
easycap-objs += easycap_testcard.o
easycap-objs += easycap_sound.o
easycap-$(CONFIG_EASYCAP_OSS) += easycap_sound_oss.o
obj-$(CONFIG_EASYCAP) += easycap.o
@ -8,5 +13,4 @@ ccflags-y += -DEASYCAP_IS_VIDEODEV_CLIENT
ccflags-y += -DEASYCAP_NEEDS_V4L2_DEVICE_H
ccflags-y += -DEASYCAP_NEEDS_V4L2_FOPS
ccflags-y += -DEASYCAP_NEEDS_UNLOCKED_IOCTL
ccflags-y += -DEASYCAP_NEEDS_ALSA

View file

@ -59,9 +59,9 @@
*/
/*---------------------------------------------------------------------------*/
#undef EASYCAP_TESTCARD
#if (!defined(EASYCAP_NEEDS_ALSA))
#ifdef CONFIG_EASYCAP_OSS
#undef EASYCAP_TESTTONE
#endif /*EASYCAP_NEEDS_ALSA*/
#endif /* CONFIG_EASYCAP_OSS */
/*---------------------------------------------------------------------------*/
#include <linux/kernel.h>
#include <linux/errno.h>
@ -81,7 +81,7 @@
#include <linux/delay.h>
#include <linux/types.h>
#if defined(EASYCAP_NEEDS_ALSA)
#ifndef CONFIG_EASYCAP_OSS
#include <linux/vmalloc.h>
#include <linux/sound.h>
#include <sound/core.h>
@ -90,7 +90,7 @@
#include <sound/info.h>
#include <sound/initval.h>
#include <sound/control.h>
#endif /*EASYCAP_NEEDS_ALSA*/
#endif /* !CONFIG_EASYCAP_OSS */
/*vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/
#if defined(EASYCAP_IS_VIDEODEV_CLIENT)
#include <media/v4l2-dev.h>
@ -445,7 +445,7 @@ __s16 oldaudio;
* ALSA
*/
/*---------------------------------------------------------------------------*/
#if defined(EASYCAP_NEEDS_ALSA)
#ifndef CONFIG_EASYCAP_OSS
struct snd_pcm_hardware alsa_hardware;
struct snd_card *psnd_card;
struct snd_pcm *psnd_pcm;
@ -453,7 +453,7 @@ __s16 oldaudio;
int dma_fill;
int dma_next;
int dma_read;
#endif /*EASYCAP_NEEDS_ALSA*/
#endif /* !CONFIG_EASYCAP_OSS */
/*---------------------------------------------------------------------------*/
/*
* SOUND PROPERTIES
@ -537,7 +537,7 @@ int adjust_volume(struct easycap *, int);
* AUDIO FUNCTION PROTOTYPES
*/
/*---------------------------------------------------------------------------*/
#if defined(EASYCAP_NEEDS_ALSA)
#ifndef CONFIG_EASYCAP_OSS
int easycap_alsa_probe(struct easycap *);
void easycap_alsa_complete(struct urb *);
@ -553,7 +553,7 @@ int easycap_alsa_trigger(struct snd_pcm_substream *, int);
snd_pcm_uframes_t easycap_alsa_pointer(struct snd_pcm_substream *);
struct page *easycap_alsa_page(struct snd_pcm_substream *, unsigned long);
#else
#else /* CONFIG_EASYCAP_OSS */
void easyoss_complete(struct urb *);
ssize_t easyoss_read(struct file *, char __user *, size_t, loff_t *);
int easyoss_open(struct inode *, struct file *);
@ -564,7 +564,8 @@ int easyoss_ioctl(struct inode *, struct file *, unsigned int,
unsigned long);
unsigned int easyoss_poll(struct file *, poll_table *);
void easyoss_delete(struct kref *);
#endif /*EASYCAP_NEEDS_ALSA*/
#endif /* !CONFIG_EASYCAP_OSS */
int easycap_sound_setup(struct easycap *);
int submit_audio_urbs(struct easycap *);
int kill_audio_urbs(struct easycap *);
@ -715,13 +716,13 @@ extern struct easycap_format easycap_format[];
extern struct v4l2_queryctrl easycap_control[];
extern struct usb_driver easycap_usb_driver;
extern struct easycap_dongle easycapdc60_dongle[];
#if defined(EASYCAP_NEEDS_ALSA)
#ifndef CONFIG_EASYCAP_OSS
extern struct snd_pcm_ops easycap_alsa_ops;
extern struct snd_pcm_hardware easycap_pcm_hardware;
extern struct snd_card *psnd_card;
#else
#else /* CONFIG_EASYCAP_OSS */
extern struct usb_class_driver easyoss_class;
extern const struct file_operations easyoss_fops;
#endif /*EASYCAP_NEEDS_ALSA*/
#endif /* !CONFIG_EASYCAP_OSS */
#endif /* !__EASYCAP_H__ */

View file

@ -2517,7 +2517,7 @@ JOM(4, "unlocked easycapdc60_dongle[%i].mutex_video\n", kd);
return 0;
}
/*****************************************************************************/
#if !defined(EASYCAP_NEEDS_ALSA)
#ifdef CONFIG_EASYCAP_OSS
/*vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/
#if ((defined(EASYCAP_IS_VIDEODEV_CLIENT)) || \
(defined(EASYCAP_NEEDS_UNLOCKED_IOCTL)))
@ -2821,6 +2821,6 @@ default: {
mutex_unlock(&easycapdc60_dongle[kd].mutex_audio);
return 0;
}
#endif /*EASYCAP_NEEDS_ALSA*/
#endif /* CONFIG_EASYCAP_OSS */
/*****************************************************************************/

View file

@ -962,7 +962,7 @@ for (k = 0; k < AUDIO_ISOC_BUFFER_MANY; k++) {
JOM(4, "easyoss_delete(): isoc audio buffers freed: %i pages\n",
m * (0x01 << AUDIO_ISOC_ORDER));
/*---------------------------------------------------------------------------*/
#if !defined(EASYCAP_NEEDS_ALSA)
#ifdef CONFIG_EASYCAP_OSS
JOM(4, "freeing audio buffers.\n");
gone = 0;
for (k = 0; k < peasycap->audio_buffer_page_many; k++) {
@ -974,7 +974,7 @@ for (k = 0; k < peasycap->audio_buffer_page_many; k++) {
}
}
JOM(4, "easyoss_delete(): audio buffers freed: %i pages\n", gone);
#endif /*!EASYCAP_NEEDS_ALSA*/
#endif /* CONFIG_EASYCAP_OSS */
/*---------------------------------------------------------------------------*/
JOM(4, "freeing easycap structure.\n");
allocation_video_urb = peasycap->allocation_video_urb;
@ -4350,7 +4350,7 @@ case 2: {
INIT_LIST_HEAD(&(peasycap->urb_audio_head));
peasycap->purb_audio_head = &(peasycap->urb_audio_head);
#if !defined(EASYCAP_NEEDS_ALSA)
#ifdef CONFIG_EASYCAP_OSS
JOM(4, "allocating an audio buffer\n");
JOM(4, ".... scattered over %i pages\n",
peasycap->audio_buffer_page_many);
@ -4375,7 +4375,7 @@ case 2: {
peasycap->audio_fill = 0;
peasycap->audio_read = 0;
JOM(4, "allocation of audio buffer done: %i pages\n", k);
#endif /*!EASYCAP_NEEDS_ALSA*/
#endif /* CONFIG_EASYCAP_OSS */
/*---------------------------------------------------------------------------*/
JOM(4, "allocating %i isoc audio buffers of size %i\n",
AUDIO_ISOC_BUFFER_MANY, peasycap->audio_isoc_buffer_size);
@ -4450,11 +4450,11 @@ case 2: {
"peasycap->audio_isoc_buffer[.].pgo;\n");
JOM(4, " purb->transfer_buffer_length = %i;\n",
peasycap->audio_isoc_buffer_size);
#if defined(EASYCAP_NEEDS_ALSA)
JOM(4, " purb->complete = easycap_alsa_complete;\n");
#else
#ifdef CONFIG_EASYCAP_OSS
JOM(4, " purb->complete = easyoss_complete;\n");
#endif /*EASYCAP_NEEDS_ALSA*/
#else /* CONFIG_EASYCAP_OSS */
JOM(4, " purb->complete = easycap_alsa_complete;\n");
#endif /* CONFIG_EASYCAP_OSS */
JOM(4, " purb->context = peasycap;\n");
JOM(4, " purb->start_frame = 0;\n");
JOM(4, " purb->number_of_packets = %i;\n",
@ -4477,11 +4477,11 @@ case 2: {
purb->transfer_buffer = peasycap->audio_isoc_buffer[k].pgo;
purb->transfer_buffer_length =
peasycap->audio_isoc_buffer_size;
#if defined(EASYCAP_NEEDS_ALSA)
purb->complete = easycap_alsa_complete;
#else
#ifdef CONFIG_EASYCAP_OSS
purb->complete = easyoss_complete;
#endif /*EASYCAP_NEEDS_ALSA*/
#else /* CONFIG_EASYCAP_OSS */
purb->complete = easycap_alsa_complete;
#endif /* CONFIG_EASYCAP_OSS */
purb->context = peasycap;
purb->start_frame = 0;
purb->number_of_packets = peasycap->audio_isoc_framesperdesc;
@ -4504,7 +4504,7 @@ case 2: {
* THE AUDIO DEVICE CAN BE REGISTERED NOW, AS IT IS READY.
*/
/*---------------------------------------------------------------------------*/
#if defined(EASYCAP_NEEDS_ALSA)
#ifndef CONFIG_EASYCAP_OSS
JOM(4, "initializing ALSA card\n");
rc = easycap_alsa_probe(peasycap);
@ -4518,7 +4518,7 @@ case 2: {
(peasycap->registered_audio)++;
}
#else /*EASYCAP_NEEDS_ALSA*/
#else /* CONFIG_EASYCAP_OSS */
rc = usb_register_dev(pusb_interface, &easyoss_class);
if (0 != rc) {
SAY("ERROR: usb_register_dev() failed\n");
@ -4536,7 +4536,7 @@ case 2: {
*/
/*---------------------------------------------------------------------------*/
SAM("easyoss attached to minor #%d\n", pusb_interface->minor);
#endif /*EASYCAP_NEEDS_ALSA*/
#endif /* CONFIG_EASYCAP_OSS */
break;
}
@ -4774,7 +4774,7 @@ case 2: {
JOM(4, "locked easycapdc60_dongle[%i].mutex_audio\n", kd);
} else
SAY("ERROR: %i=kd is bad: cannot lock dongle\n", kd);
#if defined(EASYCAP_NEEDS_ALSA)
#ifndef CONFIG_EASYCAP_OSS
@ -4786,12 +4786,12 @@ case 2: {
}
#else /*EASYCAP_NEEDS_ALSA*/
#else /* CONFIG_EASYCAP_OSS */
usb_deregister_dev(pusb_interface, &easyoss_class);
(peasycap->registered_audio)--;
JOM(4, "intf[%i]: usb_deregister_dev()\n", bInterfaceNumber);
SAM("easyoss detached from minor #%d\n", minor);
#endif /*EASYCAP_NEEDS_ALSA*/
#endif /* CONFIG_EASYCAP_OSS */
if (0 <= kd && DONGLE_MANY > kd) {
mutex_unlock(&easycapdc60_dongle[kd].mutex_audio);

View file

@ -30,7 +30,7 @@
#include "easycap.h"
#if defined(EASYCAP_NEEDS_ALSA)
#ifndef CONFIG_EASYCAP_OSS
/*--------------------------------------------------------------------------*/
/*
* PARAMETERS USED WHEN REGISTERING THE AUDIO INTERFACE
@ -669,707 +669,7 @@ return vmalloc_to_page(pss->runtime->dma_area + offset);
}
/*****************************************************************************/
#else /*!EASYCAP_NEEDS_ALSA*/
/*****************************************************************************/
/**************************** **************************/
/**************************** Open Sound System **************************/
/**************************** **************************/
/*****************************************************************************/
/*--------------------------------------------------------------------------*/
/*
* PARAMETERS USED WHEN REGISTERING THE AUDIO INTERFACE
*/
/*--------------------------------------------------------------------------*/
const struct file_operations easyoss_fops = {
.owner = THIS_MODULE,
.open = easyoss_open,
.release = easyoss_release,
#if defined(EASYCAP_NEEDS_UNLOCKED_IOCTL)
.unlocked_ioctl = easyoss_ioctl_noinode,
#else
.ioctl = easyoss_ioctl,
#endif /*EASYCAP_NEEDS_UNLOCKED_IOCTL*/
.read = easyoss_read,
.llseek = no_llseek,
};
struct usb_class_driver easyoss_class = {
.name = "usb/easyoss%d",
.fops = &easyoss_fops,
.minor_base = USB_SKEL_MINOR_BASE,
};
/*****************************************************************************/
/*---------------------------------------------------------------------------*/
/*
* ON COMPLETION OF AN AUDIO URB ITS DATA IS COPIED TO THE AUDIO BUFFERS
* PROVIDED peasycap->audio_idle IS ZERO. REGARDLESS OF THIS BEING TRUE,
* IT IS RESUBMITTED PROVIDED peasycap->audio_isoc_streaming IS NOT ZERO.
*/
/*---------------------------------------------------------------------------*/
void
easyoss_complete(struct urb *purb)
{
struct easycap *peasycap;
struct data_buffer *paudio_buffer;
__u8 *p1, *p2;
__s16 s16;
int i, j, more, much, leap, rc;
#if defined(UPSAMPLE)
int k;
__s16 oldaudio, newaudio, delta;
#endif /*UPSAMPLE*/
JOT(16, "\n");
if (NULL == purb) {
SAY("ERROR: purb is NULL\n");
return;
}
peasycap = purb->context;
if (NULL == peasycap) {
SAY("ERROR: peasycap is NULL\n");
return;
}
if (memcmp(&peasycap->telltale[0], TELLTALE, strlen(TELLTALE))) {
SAY("ERROR: bad peasycap\n");
return;
}
much = 0;
if (peasycap->audio_idle) {
JOM(16, "%i=audio_idle %i=audio_isoc_streaming\n",
peasycap->audio_idle, peasycap->audio_isoc_streaming);
if (peasycap->audio_isoc_streaming) {
rc = usb_submit_urb(purb, GFP_ATOMIC);
if (rc) {
if (-ENODEV != rc && -ENOENT != rc) {
SAM("ERROR: while %i=audio_idle, "
"usb_submit_urb() failed with rc: -%s: %d\n",
peasycap->audio_idle,
strerror(rc), rc);
}
}
}
return;
}
/*---------------------------------------------------------------------------*/
if (purb->status) {
if ((-ESHUTDOWN == purb->status) || (-ENOENT == purb->status)) {
JOM(16, "urb status -ESHUTDOWN or -ENOENT\n");
return;
}
SAM("ERROR: non-zero urb status: -%s: %d\n",
strerror(purb->status), purb->status);
goto resubmit;
}
/*---------------------------------------------------------------------------*/
/*
* PROCEED HERE WHEN NO ERROR
*/
/*---------------------------------------------------------------------------*/
#if defined(UPSAMPLE)
oldaudio = peasycap->oldaudio;
#endif /*UPSAMPLE*/
for (i = 0; i < purb->number_of_packets; i++) {
if (!purb->iso_frame_desc[i].status) {
SAM("-%s\n", strerror(purb->iso_frame_desc[i].status));
more = purb->iso_frame_desc[i].actual_length;
#if defined(TESTTONE)
if (!more)
more = purb->iso_frame_desc[i].length;
#endif
if (!more)
peasycap->audio_mt++;
else {
if (peasycap->audio_mt) {
JOM(12, "%4i empty audio urb frames\n",
peasycap->audio_mt);
peasycap->audio_mt = 0;
}
p1 = (__u8 *)(purb->transfer_buffer +
purb->iso_frame_desc[i].offset);
leap = 0;
p1 += leap;
more -= leap;
/*---------------------------------------------------------------------------*/
/*
* COPY more BYTES FROM ISOC BUFFER TO AUDIO BUFFER,
* CONVERTING 8-BIT MONO TO 16-BIT SIGNED LITTLE-ENDIAN SAMPLES IF NECESSARY
*/
/*---------------------------------------------------------------------------*/
while (more) {
if (0 > more) {
SAM("MISTAKE: more is negative\n");
return;
}
if (peasycap->audio_buffer_page_many <=
peasycap->audio_fill) {
SAM("ERROR: bad "
"peasycap->audio_fill\n");
return;
}
paudio_buffer = &peasycap->audio_buffer
[peasycap->audio_fill];
if (PAGE_SIZE < (paudio_buffer->pto -
paudio_buffer->pgo)) {
SAM("ERROR: bad paudio_buffer->pto\n");
return;
}
if (PAGE_SIZE == (paudio_buffer->pto -
paudio_buffer->pgo)) {
#if defined(TESTTONE)
easyoss_testtone(peasycap,
peasycap->audio_fill);
#endif /*TESTTONE*/
paudio_buffer->pto =
paudio_buffer->pgo;
(peasycap->audio_fill)++;
if (peasycap->
audio_buffer_page_many <=
peasycap->audio_fill)
peasycap->audio_fill = 0;
JOM(8, "bumped peasycap->"
"audio_fill to %i\n",
peasycap->audio_fill);
paudio_buffer = &peasycap->
audio_buffer
[peasycap->audio_fill];
paudio_buffer->pto =
paudio_buffer->pgo;
if (!(peasycap->audio_fill %
peasycap->
audio_pages_per_fragment)) {
JOM(12, "wakeup call on wq_"
"audio, %i=frag reading %i"
"=fragment fill\n",
(peasycap->audio_read /
peasycap->
audio_pages_per_fragment),
(peasycap->audio_fill /
peasycap->
audio_pages_per_fragment));
wake_up_interruptible
(&(peasycap->wq_audio));
}
}
much = PAGE_SIZE - (int)(paudio_buffer->pto -
paudio_buffer->pgo);
if (false == peasycap->microphone) {
if (much > more)
much = more;
memcpy(paudio_buffer->pto, p1, much);
p1 += much;
more -= much;
} else {
#if defined(UPSAMPLE)
if (much % 16)
JOM(8, "MISTAKE? much"
" is not divisible by 16\n");
if (much > (16 *
more))
much = 16 *
more;
p2 = (__u8 *)paudio_buffer->pto;
for (j = 0; j < (much/16); j++) {
newaudio = ((int) *p1) - 128;
newaudio = 128 *
newaudio;
delta = (newaudio - oldaudio)
/ 4;
s16 = oldaudio + delta;
for (k = 0; k < 4; k++) {
*p2 = (0x00FF & s16);
*(p2 + 1) = (0xFF00 &
s16) >> 8;
p2 += 2;
*p2 = (0x00FF & s16);
*(p2 + 1) = (0xFF00 &
s16) >> 8;
p2 += 2;
s16 += delta;
}
p1++;
more--;
oldaudio = s16;
}
#else /*!UPSAMPLE*/
if (much > (2 * more))
much = 2 * more;
p2 = (__u8 *)paudio_buffer->pto;
for (j = 0; j < (much / 2); j++) {
s16 = ((int) *p1) - 128;
s16 = 128 *
s16;
*p2 = (0x00FF & s16);
*(p2 + 1) = (0xFF00 & s16) >>
8;
p1++; p2 += 2;
more--;
}
#endif /*UPSAMPLE*/
}
(paudio_buffer->pto) += much;
}
}
} else {
JOM(12, "discarding audio samples because "
"%i=purb->iso_frame_desc[i].status\n",
purb->iso_frame_desc[i].status);
}
#if defined(UPSAMPLE)
peasycap->oldaudio = oldaudio;
#endif /*UPSAMPLE*/
}
/*---------------------------------------------------------------------------*/
/*
* RESUBMIT THIS URB
*/
/*---------------------------------------------------------------------------*/
resubmit:
if (peasycap->audio_isoc_streaming) {
rc = usb_submit_urb(purb, GFP_ATOMIC);
if (0 != rc) {
if (-ENODEV != rc && -ENOENT != rc) {
SAM("ERROR: while %i=audio_idle, "
"usb_submit_urb() failed "
"with rc: -%s: %d\n", peasycap->audio_idle,
strerror(rc), rc);
}
}
}
return;
}
/*****************************************************************************/
/*---------------------------------------------------------------------------*/
/*
* THE AUDIO URBS ARE SUBMITTED AT THIS EARLY STAGE SO THAT IT IS POSSIBLE TO
* STREAM FROM /dev/easyoss1 WITH SIMPLE PROGRAMS SUCH AS cat WHICH DO NOT
* HAVE AN IOCTL INTERFACE.
*/
/*---------------------------------------------------------------------------*/
int
easyoss_open(struct inode *inode, struct file *file)
{
struct usb_interface *pusb_interface;
struct easycap *peasycap;
int subminor;
/*vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/
#if defined(EASYCAP_IS_VIDEODEV_CLIENT)
#if defined(EASYCAP_NEEDS_V4L2_DEVICE_H)
struct v4l2_device *pv4l2_device;
#endif /*EASYCAP_NEEDS_V4L2_DEVICE_H*/
#endif /*EASYCAP_IS_VIDEODEV_CLIENT*/
/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
JOT(4, "begins\n");
subminor = iminor(inode);
pusb_interface = usb_find_interface(&easycap_usb_driver, subminor);
if (NULL == pusb_interface) {
SAY("ERROR: pusb_interface is NULL\n");
SAY("ending unsuccessfully\n");
return -1;
}
peasycap = usb_get_intfdata(pusb_interface);
if (NULL == peasycap) {
SAY("ERROR: peasycap is NULL\n");
SAY("ending unsuccessfully\n");
return -1;
}
/*---------------------------------------------------------------------------*/
#if (!defined(EASYCAP_IS_VIDEODEV_CLIENT))
#
/*vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/
#else
#if defined(EASYCAP_NEEDS_V4L2_DEVICE_H)
/*---------------------------------------------------------------------------*/
/*
* SOME VERSIONS OF THE videodev MODULE OVERWRITE THE DATA WHICH HAS
* BEEN WRITTEN BY THE CALL TO usb_set_intfdata() IN easycap_usb_probe(),
* REPLACING IT WITH A POINTER TO THE EMBEDDED v4l2_device STRUCTURE.
* TO DETECT THIS, THE STRING IN THE easycap.telltale[] BUFFER IS CHECKED.
*/
/*---------------------------------------------------------------------------*/
if (memcmp(&peasycap->telltale[0], TELLTALE, strlen(TELLTALE))) {
pv4l2_device = usb_get_intfdata(pusb_interface);
if ((struct v4l2_device *)NULL == pv4l2_device) {
SAY("ERROR: pv4l2_device is NULL\n");
return -EFAULT;
}
peasycap = (struct easycap *)
container_of(pv4l2_device, struct easycap, v4l2_device);
}
#endif /*EASYCAP_NEEDS_V4L2_DEVICE_H*/
#
#endif /*EASYCAP_IS_VIDEODEV_CLIENT*/
/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
/*---------------------------------------------------------------------------*/
if (memcmp(&peasycap->telltale[0], TELLTALE, strlen(TELLTALE))) {
SAY("ERROR: bad peasycap: 0x%08lX\n", (unsigned long int) peasycap);
return -EFAULT;
}
/*---------------------------------------------------------------------------*/
file->private_data = peasycap;
if (0 != easycap_sound_setup(peasycap)) {
;
;
}
return 0;
}
/*****************************************************************************/
int
easyoss_release(struct inode *inode, struct file *file)
{
struct easycap *peasycap;
JOT(4, "begins\n");
peasycap = file->private_data;
if (NULL == peasycap) {
SAY("ERROR: peasycap is NULL.\n");
return -EFAULT;
}
if (memcmp(&peasycap->telltale[0], TELLTALE, strlen(TELLTALE))) {
SAY("ERROR: bad peasycap: 0x%08lX\n", (unsigned long int) peasycap);
return -EFAULT;
}
if (0 != kill_audio_urbs(peasycap)) {
SAM("ERROR: kill_audio_urbs() failed\n");
return -EFAULT;
}
JOM(4, "ending successfully\n");
return 0;
}
/*****************************************************************************/
ssize_t
easyoss_read(struct file *file, char __user *puserspacebuffer,
size_t kount, loff_t *poff)
{
struct timeval timeval;
long long int above, below, mean;
struct signed_div_result sdr;
unsigned char *p0;
long int kount1, more, rc, l0, lm;
int fragment, kd;
struct easycap *peasycap;
struct data_buffer *pdata_buffer;
size_t szret;
/*---------------------------------------------------------------------------*/
/*
* DO A BLOCKING READ TO TRANSFER DATA TO USER SPACE.
*
******************************************************************************
***** N.B. IF THIS FUNCTION RETURNS 0, NOTHING IS SEEN IN USER SPACE. ******
***** THIS CONDITION SIGNIFIES END-OF-FILE. ******
******************************************************************************
*/
/*---------------------------------------------------------------------------*/
JOT(8, "%5i=kount %5i=*poff\n", (int)kount, (int)(*poff));
if (NULL == file) {
SAY("ERROR: file is NULL\n");
return -ERESTARTSYS;
}
peasycap = file->private_data;
if (NULL == peasycap) {
SAY("ERROR in easyoss_read(): peasycap is NULL\n");
return -EFAULT;
}
if (memcmp(&peasycap->telltale[0], TELLTALE, strlen(TELLTALE))) {
SAY("ERROR: bad peasycap: 0x%08lX\n", (unsigned long int) peasycap);
return -EFAULT;
}
if (NULL == peasycap->pusb_device) {
SAY("ERROR: peasycap->pusb_device is NULL\n");
return -EFAULT;
}
kd = isdongle(peasycap);
if (0 <= kd && DONGLE_MANY > kd) {
if (mutex_lock_interruptible(&(easycapdc60_dongle[kd].mutex_audio))) {
SAY("ERROR: "
"cannot lock easycapdc60_dongle[%i].mutex_audio\n", kd);
return -ERESTARTSYS;
}
JOM(4, "locked easycapdc60_dongle[%i].mutex_audio\n", kd);
/*---------------------------------------------------------------------------*/
/*
* MEANWHILE, easycap_usb_disconnect() MAY HAVE FREED POINTER peasycap,
* IN WHICH CASE A REPEAT CALL TO isdongle() WILL FAIL.
* IF NECESSARY, BAIL OUT.
*/
/*---------------------------------------------------------------------------*/
if (kd != isdongle(peasycap))
return -ERESTARTSYS;
if (NULL == file) {
SAY("ERROR: file is NULL\n");
mutex_unlock(&easycapdc60_dongle[kd].mutex_audio);
return -ERESTARTSYS;
}
peasycap = file->private_data;
if (NULL == peasycap) {
SAY("ERROR: peasycap is NULL\n");
mutex_unlock(&easycapdc60_dongle[kd].mutex_audio);
return -ERESTARTSYS;
}
if (memcmp(&peasycap->telltale[0], TELLTALE, strlen(TELLTALE))) {
SAY("ERROR: bad peasycap: 0x%08lX\n",
(unsigned long int) peasycap);
mutex_unlock(&easycapdc60_dongle[kd].mutex_audio);
return -ERESTARTSYS;
}
if (NULL == peasycap->pusb_device) {
SAM("ERROR: peasycap->pusb_device is NULL\n");
mutex_unlock(&easycapdc60_dongle[kd].mutex_audio);
return -ERESTARTSYS;
}
} else {
/*---------------------------------------------------------------------------*/
/*
* IF easycap_usb_disconnect() HAS ALREADY FREED POINTER peasycap BEFORE THE
* ATTEMPT TO ACQUIRE THE SEMAPHORE, isdongle() WILL HAVE FAILED. BAIL OUT.
*/
/*---------------------------------------------------------------------------*/
return -ERESTARTSYS;
}
/*---------------------------------------------------------------------------*/
if (file->f_flags & O_NONBLOCK)
JOT(16, "NONBLOCK kount=%i, *poff=%i\n", (int)kount, (int)(*poff));
else
JOT(8, "BLOCKING kount=%i, *poff=%i\n", (int)kount, (int)(*poff));
if ((0 > peasycap->audio_read) ||
(peasycap->audio_buffer_page_many <= peasycap->audio_read)) {
SAM("ERROR: peasycap->audio_read out of range\n");
mutex_unlock(&easycapdc60_dongle[kd].mutex_audio);
return -EFAULT;
}
pdata_buffer = &peasycap->audio_buffer[peasycap->audio_read];
if ((struct data_buffer *)NULL == pdata_buffer) {
SAM("ERROR: pdata_buffer is NULL\n");
mutex_unlock(&easycapdc60_dongle[kd].mutex_audio);
return -EFAULT;
}
JOM(12, "before wait, %i=frag read %i=frag fill\n",
(peasycap->audio_read / peasycap->audio_pages_per_fragment),
(peasycap->audio_fill / peasycap->audio_pages_per_fragment));
fragment = (peasycap->audio_read / peasycap->audio_pages_per_fragment);
while ((fragment == (peasycap->audio_fill /
peasycap->audio_pages_per_fragment)) ||
(0 == (PAGE_SIZE - (pdata_buffer->pto - pdata_buffer->pgo)))) {
if (file->f_flags & O_NONBLOCK) {
JOM(16, "returning -EAGAIN as instructed\n");
mutex_unlock(&easycapdc60_dongle[kd].mutex_audio);
return -EAGAIN;
}
rc = wait_event_interruptible(peasycap->wq_audio,
(peasycap->audio_idle || peasycap->audio_eof ||
((fragment != (peasycap->audio_fill /
peasycap->audio_pages_per_fragment)) &&
(0 < (PAGE_SIZE - (pdata_buffer->pto - pdata_buffer->pgo))))));
if (0 != rc) {
SAM("aborted by signal\n");
mutex_unlock(&easycapdc60_dongle[kd].mutex_audio);
return -ERESTARTSYS;
}
if (peasycap->audio_eof) {
JOM(8, "returning 0 because %i=audio_eof\n",
peasycap->audio_eof);
kill_audio_urbs(peasycap);
mutex_unlock(&easycapdc60_dongle[kd].mutex_audio);
return 0;
}
if (peasycap->audio_idle) {
JOM(16, "returning 0 because %i=audio_idle\n",
peasycap->audio_idle);
mutex_unlock(&easycapdc60_dongle[kd].mutex_audio);
return 0;
}
if (!peasycap->audio_isoc_streaming) {
JOM(16, "returning 0 because audio urbs not streaming\n");
mutex_unlock(&easycapdc60_dongle[kd].mutex_audio);
return 0;
}
}
JOM(12, "after wait, %i=frag read %i=frag fill\n",
(peasycap->audio_read / peasycap->audio_pages_per_fragment),
(peasycap->audio_fill / peasycap->audio_pages_per_fragment));
szret = (size_t)0;
fragment = (peasycap->audio_read / peasycap->audio_pages_per_fragment);
while (fragment == (peasycap->audio_read /
peasycap->audio_pages_per_fragment)) {
if (NULL == pdata_buffer->pgo) {
SAM("ERROR: pdata_buffer->pgo is NULL\n");
mutex_unlock(&easycapdc60_dongle[kd].mutex_audio);
return -EFAULT;
}
if (NULL == pdata_buffer->pto) {
SAM("ERROR: pdata_buffer->pto is NULL\n");
mutex_unlock(&easycapdc60_dongle[kd].mutex_audio);
return -EFAULT;
}
kount1 = PAGE_SIZE - (pdata_buffer->pto - pdata_buffer->pgo);
if (0 > kount1) {
SAM("MISTAKE: kount1 is negative\n");
mutex_unlock(&easycapdc60_dongle[kd].mutex_audio);
return -ERESTARTSYS;
}
if (!kount1) {
(peasycap->audio_read)++;
if (peasycap->audio_buffer_page_many <= peasycap->audio_read)
peasycap->audio_read = 0;
JOM(12, "bumped peasycap->audio_read to %i\n",
peasycap->audio_read);
if (fragment != (peasycap->audio_read /
peasycap->audio_pages_per_fragment))
break;
if ((0 > peasycap->audio_read) ||
(peasycap->audio_buffer_page_many <=
peasycap->audio_read)) {
SAM("ERROR: peasycap->audio_read out of range\n");
mutex_unlock(&easycapdc60_dongle[kd].mutex_audio);
return -EFAULT;
}
pdata_buffer = &peasycap->audio_buffer[peasycap->audio_read];
if ((struct data_buffer *)NULL == pdata_buffer) {
SAM("ERROR: pdata_buffer is NULL\n");
mutex_unlock(&easycapdc60_dongle[kd].mutex_audio);
return -EFAULT;
}
if (NULL == pdata_buffer->pgo) {
SAM("ERROR: pdata_buffer->pgo is NULL\n");
mutex_unlock(&easycapdc60_dongle[kd].mutex_audio);
return -EFAULT;
}
if (NULL == pdata_buffer->pto) {
SAM("ERROR: pdata_buffer->pto is NULL\n");
mutex_unlock(&easycapdc60_dongle[kd].mutex_audio);
return -EFAULT;
}
kount1 = PAGE_SIZE - (pdata_buffer->pto - pdata_buffer->pgo);
}
JOM(12, "ready to send %li bytes\n", (long int) kount1);
JOM(12, "still to send %li bytes\n", (long int) kount);
more = kount1;
if (more > kount)
more = kount;
JOM(12, "agreed to send %li bytes from page %i\n",
more, peasycap->audio_read);
if (!more)
break;
/*---------------------------------------------------------------------------*/
/*
* ACCUMULATE DYNAMIC-RANGE INFORMATION
*/
/*---------------------------------------------------------------------------*/
p0 = (unsigned char *)pdata_buffer->pgo; l0 = 0; lm = more/2;
while (l0 < lm) {
SUMMER(p0, &peasycap->audio_sample, &peasycap->audio_niveau,
&peasycap->audio_square); l0++; p0 += 2;
}
/*---------------------------------------------------------------------------*/
rc = copy_to_user(puserspacebuffer, pdata_buffer->pto, more);
if (0 != rc) {
SAM("ERROR: copy_to_user() returned %li\n", rc);
mutex_unlock(&easycapdc60_dongle[kd].mutex_audio);
return -EFAULT;
}
*poff += (loff_t)more;
szret += (size_t)more;
pdata_buffer->pto += more;
puserspacebuffer += more;
kount -= (size_t)more;
}
JOM(12, "after read, %i=frag read %i=frag fill\n",
(peasycap->audio_read / peasycap->audio_pages_per_fragment),
(peasycap->audio_fill / peasycap->audio_pages_per_fragment));
if (kount < 0) {
SAM("MISTAKE: %li=kount %li=szret\n",
(long int)kount, (long int)szret);
}
/*---------------------------------------------------------------------------*/
/*
* CALCULATE DYNAMIC RANGE FOR (VAPOURWARE) AUTOMATIC VOLUME CONTROL
*/
/*---------------------------------------------------------------------------*/
if (peasycap->audio_sample) {
below = peasycap->audio_sample;
above = peasycap->audio_square;
sdr = signed_div(above, below);
above = sdr.quotient;
mean = peasycap->audio_niveau;
sdr = signed_div(mean, peasycap->audio_sample);
JOM(8, "%8lli=mean %8lli=meansquare after %lli samples, =>\n",
sdr.quotient, above, peasycap->audio_sample);
sdr = signed_div(above, 32768);
JOM(8, "audio dynamic range is roughly %lli\n", sdr.quotient);
}
/*---------------------------------------------------------------------------*/
/*
* UPDATE THE AUDIO CLOCK
*/
/*---------------------------------------------------------------------------*/
do_gettimeofday(&timeval);
if (!peasycap->timeval1.tv_sec) {
peasycap->audio_bytes = 0;
peasycap->timeval3 = timeval;
peasycap->timeval1 = peasycap->timeval3;
sdr.quotient = 192000;
} else {
peasycap->audio_bytes += (long long int) szret;
below = ((long long int)(1000000)) *
((long long int)(timeval.tv_sec -
peasycap->timeval3.tv_sec)) +
(long long int)(timeval.tv_usec - peasycap->timeval3.tv_usec);
above = 1000000 * ((long long int) peasycap->audio_bytes);
if (below)
sdr = signed_div(above, below);
else
sdr.quotient = 192000;
}
JOM(8, "audio streaming at %lli bytes/second\n", sdr.quotient);
peasycap->dnbydt = sdr.quotient;
mutex_unlock(&easycapdc60_dongle[kd].mutex_audio);
JOM(4, "unlocked easycapdc60_dongle[%i].mutex_audio\n", kd);
JOM(8, "returning %li\n", (long int)szret);
return szret;
}
/*****************************************************************************/
#endif /*!EASYCAP_NEEDS_ALSA*/
#endif /*! CONFIG_EASYCAP_OSS */
/*****************************************************************************/
/*****************************************************************************/
@ -1484,11 +784,11 @@ if (!peasycap->audio_isoc_streaming) {
peasycap->audio_isoc_buffer[isbuf].pgo;
purb->transfer_buffer_length =
peasycap->audio_isoc_buffer_size;
#if defined(EASYCAP_NEEDS_ALSA)
purb->complete = easycap_alsa_complete;
#else
#ifdef CONFIG_EASYCAP_OSS
purb->complete = easyoss_complete;
#endif /*EASYCAP_NEEDS_ALSA*/
#else /* CONFIG_EASYCAP_OSS */
purb->complete = easycap_alsa_complete;
#endif /* CONFIG_EASYCAP_OSS */
purb->context = peasycap;
purb->start_frame = 0;
purb->number_of_packets =

View file

@ -0,0 +1,730 @@
/******************************************************************************
* *
* easycap_sound.c *
* *
* Audio driver for EasyCAP USB2.0 Video Capture Device DC60 *
* *
* *
******************************************************************************/
/*
*
* Copyright (C) 2010 R.M. Thomas <rmthomas@sciolus.org>
*
*
* This 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.
*
* The software 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 software; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
/*****************************************************************************/
#include "easycap.h"
/*****************************************************************************/
/**************************** **************************/
/**************************** Open Sound System **************************/
/**************************** **************************/
/*****************************************************************************/
/*--------------------------------------------------------------------------*/
/*
* PARAMETERS USED WHEN REGISTERING THE AUDIO INTERFACE
*/
/*--------------------------------------------------------------------------*/
const struct file_operations easyoss_fops = {
.owner = THIS_MODULE,
.open = easyoss_open,
.release = easyoss_release,
#if defined(EASYCAP_NEEDS_UNLOCKED_IOCTL)
.unlocked_ioctl = easyoss_ioctl_noinode,
#else
.ioctl = easyoss_ioctl,
#endif /*EASYCAP_NEEDS_UNLOCKED_IOCTL*/
.read = easyoss_read,
.llseek = no_llseek,
};
struct usb_class_driver easyoss_class = {
.name = "usb/easyoss%d",
.fops = &easyoss_fops,
.minor_base = USB_SKEL_MINOR_BASE,
};
/*****************************************************************************/
/*---------------------------------------------------------------------------*/
/*
* ON COMPLETION OF AN AUDIO URB ITS DATA IS COPIED TO THE AUDIO BUFFERS
* PROVIDED peasycap->audio_idle IS ZERO. REGARDLESS OF THIS BEING TRUE,
* IT IS RESUBMITTED PROVIDED peasycap->audio_isoc_streaming IS NOT ZERO.
*/
/*---------------------------------------------------------------------------*/
void
easyoss_complete(struct urb *purb)
{
struct easycap *peasycap;
struct data_buffer *paudio_buffer;
__u8 *p1, *p2;
__s16 s16;
int i, j, more, much, leap, rc;
#if defined(UPSAMPLE)
int k;
__s16 oldaudio, newaudio, delta;
#endif /*UPSAMPLE*/
JOT(16, "\n");
if (NULL == purb) {
SAY("ERROR: purb is NULL\n");
return;
}
peasycap = purb->context;
if (NULL == peasycap) {
SAY("ERROR: peasycap is NULL\n");
return;
}
if (memcmp(&peasycap->telltale[0], TELLTALE, strlen(TELLTALE))) {
SAY("ERROR: bad peasycap\n");
return;
}
much = 0;
if (peasycap->audio_idle) {
JOM(16, "%i=audio_idle %i=audio_isoc_streaming\n",
peasycap->audio_idle, peasycap->audio_isoc_streaming);
if (peasycap->audio_isoc_streaming) {
rc = usb_submit_urb(purb, GFP_ATOMIC);
if (rc) {
if (-ENODEV != rc && -ENOENT != rc) {
SAM("ERROR: while %i=audio_idle, "
"usb_submit_urb() failed with rc: -%s: %d\n",
peasycap->audio_idle,
strerror(rc), rc);
}
}
}
return;
}
/*---------------------------------------------------------------------------*/
if (purb->status) {
if ((-ESHUTDOWN == purb->status) || (-ENOENT == purb->status)) {
JOM(16, "urb status -ESHUTDOWN or -ENOENT\n");
return;
}
SAM("ERROR: non-zero urb status: -%s: %d\n",
strerror(purb->status), purb->status);
goto resubmit;
}
/*---------------------------------------------------------------------------*/
/*
* PROCEED HERE WHEN NO ERROR
*/
/*---------------------------------------------------------------------------*/
#if defined(UPSAMPLE)
oldaudio = peasycap->oldaudio;
#endif /*UPSAMPLE*/
for (i = 0; i < purb->number_of_packets; i++) {
if (!purb->iso_frame_desc[i].status) {
SAM("-%s\n", strerror(purb->iso_frame_desc[i].status));
more = purb->iso_frame_desc[i].actual_length;
#if defined(TESTTONE)
if (!more)
more = purb->iso_frame_desc[i].length;
#endif
if (!more)
peasycap->audio_mt++;
else {
if (peasycap->audio_mt) {
JOM(12, "%4i empty audio urb frames\n",
peasycap->audio_mt);
peasycap->audio_mt = 0;
}
p1 = (__u8 *)(purb->transfer_buffer +
purb->iso_frame_desc[i].offset);
leap = 0;
p1 += leap;
more -= leap;
/*---------------------------------------------------------------------------*/
/*
* COPY more BYTES FROM ISOC BUFFER TO AUDIO BUFFER,
* CONVERTING 8-BIT MONO TO 16-BIT SIGNED LITTLE-ENDIAN SAMPLES IF NECESSARY
*/
/*---------------------------------------------------------------------------*/
while (more) {
if (0 > more) {
SAM("MISTAKE: more is negative\n");
return;
}
if (peasycap->audio_buffer_page_many <=
peasycap->audio_fill) {
SAM("ERROR: bad "
"peasycap->audio_fill\n");
return;
}
paudio_buffer = &peasycap->audio_buffer
[peasycap->audio_fill];
if (PAGE_SIZE < (paudio_buffer->pto -
paudio_buffer->pgo)) {
SAM("ERROR: bad paudio_buffer->pto\n");
return;
}
if (PAGE_SIZE == (paudio_buffer->pto -
paudio_buffer->pgo)) {
#if defined(TESTTONE)
easyoss_testtone(peasycap,
peasycap->audio_fill);
#endif /*TESTTONE*/
paudio_buffer->pto =
paudio_buffer->pgo;
(peasycap->audio_fill)++;
if (peasycap->
audio_buffer_page_many <=
peasycap->audio_fill)
peasycap->audio_fill = 0;
JOM(8, "bumped peasycap->"
"audio_fill to %i\n",
peasycap->audio_fill);
paudio_buffer = &peasycap->
audio_buffer
[peasycap->audio_fill];
paudio_buffer->pto =
paudio_buffer->pgo;
if (!(peasycap->audio_fill %
peasycap->
audio_pages_per_fragment)) {
JOM(12, "wakeup call on wq_"
"audio, %i=frag reading %i"
"=fragment fill\n",
(peasycap->audio_read /
peasycap->
audio_pages_per_fragment),
(peasycap->audio_fill /
peasycap->
audio_pages_per_fragment));
wake_up_interruptible
(&(peasycap->wq_audio));
}
}
much = PAGE_SIZE - (int)(paudio_buffer->pto -
paudio_buffer->pgo);
if (false == peasycap->microphone) {
if (much > more)
much = more;
memcpy(paudio_buffer->pto, p1, much);
p1 += much;
more -= much;
} else {
#if defined(UPSAMPLE)
if (much % 16)
JOM(8, "MISTAKE? much"
" is not divisible by 16\n");
if (much > (16 *
more))
much = 16 *
more;
p2 = (__u8 *)paudio_buffer->pto;
for (j = 0; j < (much/16); j++) {
newaudio = ((int) *p1) - 128;
newaudio = 128 *
newaudio;
delta = (newaudio - oldaudio)
/ 4;
s16 = oldaudio + delta;
for (k = 0; k < 4; k++) {
*p2 = (0x00FF & s16);
*(p2 + 1) = (0xFF00 &
s16) >> 8;
p2 += 2;
*p2 = (0x00FF & s16);
*(p2 + 1) = (0xFF00 &
s16) >> 8;
p2 += 2;
s16 += delta;
}
p1++;
more--;
oldaudio = s16;
}
#else /*!UPSAMPLE*/
if (much > (2 * more))
much = 2 * more;
p2 = (__u8 *)paudio_buffer->pto;
for (j = 0; j < (much / 2); j++) {
s16 = ((int) *p1) - 128;
s16 = 128 *
s16;
*p2 = (0x00FF & s16);
*(p2 + 1) = (0xFF00 & s16) >>
8;
p1++; p2 += 2;
more--;
}
#endif /*UPSAMPLE*/
}
(paudio_buffer->pto) += much;
}
}
} else {
JOM(12, "discarding audio samples because "
"%i=purb->iso_frame_desc[i].status\n",
purb->iso_frame_desc[i].status);
}
#if defined(UPSAMPLE)
peasycap->oldaudio = oldaudio;
#endif /*UPSAMPLE*/
}
/*---------------------------------------------------------------------------*/
/*
* RESUBMIT THIS URB
*/
/*---------------------------------------------------------------------------*/
resubmit:
if (peasycap->audio_isoc_streaming) {
rc = usb_submit_urb(purb, GFP_ATOMIC);
if (0 != rc) {
if (-ENODEV != rc && -ENOENT != rc) {
SAM("ERROR: while %i=audio_idle, "
"usb_submit_urb() failed "
"with rc: -%s: %d\n", peasycap->audio_idle,
strerror(rc), rc);
}
}
}
return;
}
/*****************************************************************************/
/*---------------------------------------------------------------------------*/
/*
* THE AUDIO URBS ARE SUBMITTED AT THIS EARLY STAGE SO THAT IT IS POSSIBLE TO
* STREAM FROM /dev/easyoss1 WITH SIMPLE PROGRAMS SUCH AS cat WHICH DO NOT
* HAVE AN IOCTL INTERFACE.
*/
/*---------------------------------------------------------------------------*/
int
easyoss_open(struct inode *inode, struct file *file)
{
struct usb_interface *pusb_interface;
struct easycap *peasycap;
int subminor;
/*vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/
#if defined(EASYCAP_IS_VIDEODEV_CLIENT)
#if defined(EASYCAP_NEEDS_V4L2_DEVICE_H)
struct v4l2_device *pv4l2_device;
#endif /*EASYCAP_NEEDS_V4L2_DEVICE_H*/
#endif /*EASYCAP_IS_VIDEODEV_CLIENT*/
/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
JOT(4, "begins\n");
subminor = iminor(inode);
pusb_interface = usb_find_interface(&easycap_usb_driver, subminor);
if (NULL == pusb_interface) {
SAY("ERROR: pusb_interface is NULL\n");
SAY("ending unsuccessfully\n");
return -1;
}
peasycap = usb_get_intfdata(pusb_interface);
if (NULL == peasycap) {
SAY("ERROR: peasycap is NULL\n");
SAY("ending unsuccessfully\n");
return -1;
}
/*---------------------------------------------------------------------------*/
#if (!defined(EASYCAP_IS_VIDEODEV_CLIENT))
#
/*vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/
#else
#if defined(EASYCAP_NEEDS_V4L2_DEVICE_H)
/*---------------------------------------------------------------------------*/
/*
* SOME VERSIONS OF THE videodev MODULE OVERWRITE THE DATA WHICH HAS
* BEEN WRITTEN BY THE CALL TO usb_set_intfdata() IN easycap_usb_probe(),
* REPLACING IT WITH A POINTER TO THE EMBEDDED v4l2_device STRUCTURE.
* TO DETECT THIS, THE STRING IN THE easycap.telltale[] BUFFER IS CHECKED.
*/
/*---------------------------------------------------------------------------*/
if (memcmp(&peasycap->telltale[0], TELLTALE, strlen(TELLTALE))) {
pv4l2_device = usb_get_intfdata(pusb_interface);
if ((struct v4l2_device *)NULL == pv4l2_device) {
SAY("ERROR: pv4l2_device is NULL\n");
return -EFAULT;
}
peasycap = (struct easycap *)
container_of(pv4l2_device, struct easycap, v4l2_device);
}
#endif /*EASYCAP_NEEDS_V4L2_DEVICE_H*/
#
#endif /*EASYCAP_IS_VIDEODEV_CLIENT*/
/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
/*---------------------------------------------------------------------------*/
if (memcmp(&peasycap->telltale[0], TELLTALE, strlen(TELLTALE))) {
SAY("ERROR: bad peasycap: 0x%08lX\n", (unsigned long int) peasycap);
return -EFAULT;
}
/*---------------------------------------------------------------------------*/
file->private_data = peasycap;
if (0 != easycap_sound_setup(peasycap)) {
;
;
}
return 0;
}
/*****************************************************************************/
int
easyoss_release(struct inode *inode, struct file *file)
{
struct easycap *peasycap;
JOT(4, "begins\n");
peasycap = file->private_data;
if (NULL == peasycap) {
SAY("ERROR: peasycap is NULL.\n");
return -EFAULT;
}
if (memcmp(&peasycap->telltale[0], TELLTALE, strlen(TELLTALE))) {
SAY("ERROR: bad peasycap: 0x%08lX\n", (unsigned long int) peasycap);
return -EFAULT;
}
if (0 != kill_audio_urbs(peasycap)) {
SAM("ERROR: kill_audio_urbs() failed\n");
return -EFAULT;
}
JOM(4, "ending successfully\n");
return 0;
}
/*****************************************************************************/
ssize_t
easyoss_read(struct file *file, char __user *puserspacebuffer,
size_t kount, loff_t *poff)
{
struct timeval timeval;
long long int above, below, mean;
struct signed_div_result sdr;
unsigned char *p0;
long int kount1, more, rc, l0, lm;
int fragment, kd;
struct easycap *peasycap;
struct data_buffer *pdata_buffer;
size_t szret;
/*---------------------------------------------------------------------------*/
/*
* DO A BLOCKING READ TO TRANSFER DATA TO USER SPACE.
*
******************************************************************************
***** N.B. IF THIS FUNCTION RETURNS 0, NOTHING IS SEEN IN USER SPACE. ******
***** THIS CONDITION SIGNIFIES END-OF-FILE. ******
******************************************************************************
*/
/*---------------------------------------------------------------------------*/
JOT(8, "%5i=kount %5i=*poff\n", (int)kount, (int)(*poff));
if (NULL == file) {
SAY("ERROR: file is NULL\n");
return -ERESTARTSYS;
}
peasycap = file->private_data;
if (NULL == peasycap) {
SAY("ERROR in easyoss_read(): peasycap is NULL\n");
return -EFAULT;
}
if (memcmp(&peasycap->telltale[0], TELLTALE, strlen(TELLTALE))) {
SAY("ERROR: bad peasycap: 0x%08lX\n", (unsigned long int) peasycap);
return -EFAULT;
}
if (NULL == peasycap->pusb_device) {
SAY("ERROR: peasycap->pusb_device is NULL\n");
return -EFAULT;
}
kd = isdongle(peasycap);
if (0 <= kd && DONGLE_MANY > kd) {
if (mutex_lock_interruptible(&(easycapdc60_dongle[kd].mutex_audio))) {
SAY("ERROR: "
"cannot lock easycapdc60_dongle[%i].mutex_audio\n", kd);
return -ERESTARTSYS;
}
JOM(4, "locked easycapdc60_dongle[%i].mutex_audio\n", kd);
/*---------------------------------------------------------------------------*/
/*
* MEANWHILE, easycap_usb_disconnect() MAY HAVE FREED POINTER peasycap,
* IN WHICH CASE A REPEAT CALL TO isdongle() WILL FAIL.
* IF NECESSARY, BAIL OUT.
*/
/*---------------------------------------------------------------------------*/
if (kd != isdongle(peasycap))
return -ERESTARTSYS;
if (NULL == file) {
SAY("ERROR: file is NULL\n");
mutex_unlock(&easycapdc60_dongle[kd].mutex_audio);
return -ERESTARTSYS;
}
peasycap = file->private_data;
if (NULL == peasycap) {
SAY("ERROR: peasycap is NULL\n");
mutex_unlock(&easycapdc60_dongle[kd].mutex_audio);
return -ERESTARTSYS;
}
if (memcmp(&peasycap->telltale[0], TELLTALE, strlen(TELLTALE))) {
SAY("ERROR: bad peasycap: 0x%08lX\n",
(unsigned long int) peasycap);
mutex_unlock(&easycapdc60_dongle[kd].mutex_audio);
return -ERESTARTSYS;
}
if (NULL == peasycap->pusb_device) {
SAM("ERROR: peasycap->pusb_device is NULL\n");
mutex_unlock(&easycapdc60_dongle[kd].mutex_audio);
return -ERESTARTSYS;
}
} else {
/*---------------------------------------------------------------------------*/
/*
* IF easycap_usb_disconnect() HAS ALREADY FREED POINTER peasycap BEFORE THE
* ATTEMPT TO ACQUIRE THE SEMAPHORE, isdongle() WILL HAVE FAILED. BAIL OUT.
*/
/*---------------------------------------------------------------------------*/
return -ERESTARTSYS;
}
/*---------------------------------------------------------------------------*/
if (file->f_flags & O_NONBLOCK)
JOT(16, "NONBLOCK kount=%i, *poff=%i\n", (int)kount, (int)(*poff));
else
JOT(8, "BLOCKING kount=%i, *poff=%i\n", (int)kount, (int)(*poff));
if ((0 > peasycap->audio_read) ||
(peasycap->audio_buffer_page_many <= peasycap->audio_read)) {
SAM("ERROR: peasycap->audio_read out of range\n");
mutex_unlock(&easycapdc60_dongle[kd].mutex_audio);
return -EFAULT;
}
pdata_buffer = &peasycap->audio_buffer[peasycap->audio_read];
if ((struct data_buffer *)NULL == pdata_buffer) {
SAM("ERROR: pdata_buffer is NULL\n");
mutex_unlock(&easycapdc60_dongle[kd].mutex_audio);
return -EFAULT;
}
JOM(12, "before wait, %i=frag read %i=frag fill\n",
(peasycap->audio_read / peasycap->audio_pages_per_fragment),
(peasycap->audio_fill / peasycap->audio_pages_per_fragment));
fragment = (peasycap->audio_read / peasycap->audio_pages_per_fragment);
while ((fragment == (peasycap->audio_fill /
peasycap->audio_pages_per_fragment)) ||
(0 == (PAGE_SIZE - (pdata_buffer->pto - pdata_buffer->pgo)))) {
if (file->f_flags & O_NONBLOCK) {
JOM(16, "returning -EAGAIN as instructed\n");
mutex_unlock(&easycapdc60_dongle[kd].mutex_audio);
return -EAGAIN;
}
rc = wait_event_interruptible(peasycap->wq_audio,
(peasycap->audio_idle || peasycap->audio_eof ||
((fragment != (peasycap->audio_fill /
peasycap->audio_pages_per_fragment)) &&
(0 < (PAGE_SIZE - (pdata_buffer->pto - pdata_buffer->pgo))))));
if (0 != rc) {
SAM("aborted by signal\n");
mutex_unlock(&easycapdc60_dongle[kd].mutex_audio);
return -ERESTARTSYS;
}
if (peasycap->audio_eof) {
JOM(8, "returning 0 because %i=audio_eof\n",
peasycap->audio_eof);
kill_audio_urbs(peasycap);
mutex_unlock(&easycapdc60_dongle[kd].mutex_audio);
return 0;
}
if (peasycap->audio_idle) {
JOM(16, "returning 0 because %i=audio_idle\n",
peasycap->audio_idle);
mutex_unlock(&easycapdc60_dongle[kd].mutex_audio);
return 0;
}
if (!peasycap->audio_isoc_streaming) {
JOM(16, "returning 0 because audio urbs not streaming\n");
mutex_unlock(&easycapdc60_dongle[kd].mutex_audio);
return 0;
}
}
JOM(12, "after wait, %i=frag read %i=frag fill\n",
(peasycap->audio_read / peasycap->audio_pages_per_fragment),
(peasycap->audio_fill / peasycap->audio_pages_per_fragment));
szret = (size_t)0;
fragment = (peasycap->audio_read / peasycap->audio_pages_per_fragment);
while (fragment == (peasycap->audio_read /
peasycap->audio_pages_per_fragment)) {
if (NULL == pdata_buffer->pgo) {
SAM("ERROR: pdata_buffer->pgo is NULL\n");
mutex_unlock(&easycapdc60_dongle[kd].mutex_audio);
return -EFAULT;
}
if (NULL == pdata_buffer->pto) {
SAM("ERROR: pdata_buffer->pto is NULL\n");
mutex_unlock(&easycapdc60_dongle[kd].mutex_audio);
return -EFAULT;
}
kount1 = PAGE_SIZE - (pdata_buffer->pto - pdata_buffer->pgo);
if (0 > kount1) {
SAM("MISTAKE: kount1 is negative\n");
mutex_unlock(&easycapdc60_dongle[kd].mutex_audio);
return -ERESTARTSYS;
}
if (!kount1) {
(peasycap->audio_read)++;
if (peasycap->audio_buffer_page_many <= peasycap->audio_read)
peasycap->audio_read = 0;
JOM(12, "bumped peasycap->audio_read to %i\n",
peasycap->audio_read);
if (fragment != (peasycap->audio_read /
peasycap->audio_pages_per_fragment))
break;
if ((0 > peasycap->audio_read) ||
(peasycap->audio_buffer_page_many <=
peasycap->audio_read)) {
SAM("ERROR: peasycap->audio_read out of range\n");
mutex_unlock(&easycapdc60_dongle[kd].mutex_audio);
return -EFAULT;
}
pdata_buffer = &peasycap->audio_buffer[peasycap->audio_read];
if ((struct data_buffer *)NULL == pdata_buffer) {
SAM("ERROR: pdata_buffer is NULL\n");
mutex_unlock(&easycapdc60_dongle[kd].mutex_audio);
return -EFAULT;
}
if (NULL == pdata_buffer->pgo) {
SAM("ERROR: pdata_buffer->pgo is NULL\n");
mutex_unlock(&easycapdc60_dongle[kd].mutex_audio);
return -EFAULT;
}
if (NULL == pdata_buffer->pto) {
SAM("ERROR: pdata_buffer->pto is NULL\n");
mutex_unlock(&easycapdc60_dongle[kd].mutex_audio);
return -EFAULT;
}
kount1 = PAGE_SIZE - (pdata_buffer->pto - pdata_buffer->pgo);
}
JOM(12, "ready to send %li bytes\n", (long int) kount1);
JOM(12, "still to send %li bytes\n", (long int) kount);
more = kount1;
if (more > kount)
more = kount;
JOM(12, "agreed to send %li bytes from page %i\n",
more, peasycap->audio_read);
if (!more)
break;
/*---------------------------------------------------------------------------*/
/*
* ACCUMULATE DYNAMIC-RANGE INFORMATION
*/
/*---------------------------------------------------------------------------*/
p0 = (unsigned char *)pdata_buffer->pgo; l0 = 0; lm = more/2;
while (l0 < lm) {
SUMMER(p0, &peasycap->audio_sample, &peasycap->audio_niveau,
&peasycap->audio_square); l0++; p0 += 2;
}
/*---------------------------------------------------------------------------*/
rc = copy_to_user(puserspacebuffer, pdata_buffer->pto, more);
if (0 != rc) {
SAM("ERROR: copy_to_user() returned %li\n", rc);
mutex_unlock(&easycapdc60_dongle[kd].mutex_audio);
return -EFAULT;
}
*poff += (loff_t)more;
szret += (size_t)more;
pdata_buffer->pto += more;
puserspacebuffer += more;
kount -= (size_t)more;
}
JOM(12, "after read, %i=frag read %i=frag fill\n",
(peasycap->audio_read / peasycap->audio_pages_per_fragment),
(peasycap->audio_fill / peasycap->audio_pages_per_fragment));
if (kount < 0) {
SAM("MISTAKE: %li=kount %li=szret\n",
(long int)kount, (long int)szret);
}
/*---------------------------------------------------------------------------*/
/*
* CALCULATE DYNAMIC RANGE FOR (VAPOURWARE) AUTOMATIC VOLUME CONTROL
*/
/*---------------------------------------------------------------------------*/
if (peasycap->audio_sample) {
below = peasycap->audio_sample;
above = peasycap->audio_square;
sdr = signed_div(above, below);
above = sdr.quotient;
mean = peasycap->audio_niveau;
sdr = signed_div(mean, peasycap->audio_sample);
JOM(8, "%8lli=mean %8lli=meansquare after %lli samples, =>\n",
sdr.quotient, above, peasycap->audio_sample);
sdr = signed_div(above, 32768);
JOM(8, "audio dynamic range is roughly %lli\n", sdr.quotient);
}
/*---------------------------------------------------------------------------*/
/*
* UPDATE THE AUDIO CLOCK
*/
/*---------------------------------------------------------------------------*/
do_gettimeofday(&timeval);
if (!peasycap->timeval1.tv_sec) {
peasycap->audio_bytes = 0;
peasycap->timeval3 = timeval;
peasycap->timeval1 = peasycap->timeval3;
sdr.quotient = 192000;
} else {
peasycap->audio_bytes += (long long int) szret;
below = ((long long int)(1000000)) *
((long long int)(timeval.tv_sec -
peasycap->timeval3.tv_sec)) +
(long long int)(timeval.tv_usec - peasycap->timeval3.tv_usec);
above = 1000000 * ((long long int) peasycap->audio_bytes);
if (below)
sdr = signed_div(above, below);
else
sdr.quotient = 192000;
}
JOM(8, "audio streaming at %lli bytes/second\n", sdr.quotient);
peasycap->dnbydt = sdr.quotient;
mutex_unlock(&easycapdc60_dongle[kd].mutex_audio);
JOM(4, "unlocked easycapdc60_dongle[%i].mutex_audio\n", kd);
JOM(8, "returning %li\n", (long int)szret);
return szret;
}
/*****************************************************************************/