diff options
Diffstat (limited to 'drivers/staging/easycap/easycap_sound.c')
-rw-r--r-- | drivers/staging/easycap/easycap_sound.c | 1717 |
1 files changed, 669 insertions, 1048 deletions
diff --git a/drivers/staging/easycap/easycap_sound.c b/drivers/staging/easycap/easycap_sound.c index 24d8bb4e449e..a3402b00a8be 100644 --- a/drivers/staging/easycap/easycap_sound.c +++ b/drivers/staging/easycap/easycap_sound.c @@ -29,1009 +29,681 @@ /*****************************************************************************/ #include "easycap.h" -#include "easycap_debug.h" -#include "easycap_sound.h" + +#ifndef CONFIG_EASYCAP_OSS +/*--------------------------------------------------------------------------*/ +/* + * PARAMETERS USED WHEN REGISTERING THE AUDIO INTERFACE + */ +/*--------------------------------------------------------------------------*/ +static const struct snd_pcm_hardware alsa_hardware = { + .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_32000 | SNDRV_PCM_RATE_48000, + .rate_min = 32000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = PAGE_SIZE * + PAGES_PER_AUDIO_FRAGMENT * + AUDIO_FRAGMENT_MANY, + .period_bytes_min = PAGE_SIZE * PAGES_PER_AUDIO_FRAGMENT, + .period_bytes_max = PAGE_SIZE * PAGES_PER_AUDIO_FRAGMENT * 2, + .periods_min = AUDIO_FRAGMENT_MANY, + .periods_max = AUDIO_FRAGMENT_MANY * 2, +}; + /*****************************************************************************/ /*---------------------------------------------------------------------------*/ /* - * ON COMPLETION OF AN AUDIO URB ITS DATA IS COPIED TO THE AUDIO BUFFERS + * ON COMPLETION OF AN AUDIO URB ITS DATA IS COPIED TO THE DAM BUFFER * PROVIDED peasycap->audio_idle IS ZERO. REGARDLESS OF THIS BEING TRUE, * IT IS RESUBMITTED PROVIDED peasycap->audio_isoc_streaming IS NOT ZERO. */ /*---------------------------------------------------------------------------*/ void -easysnd_complete(struct urb *purb) +easycap_alsa_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; + struct easycap *peasycap; + struct snd_pcm_substream *pss; + struct snd_pcm_runtime *prt; + int dma_bytes, fragment_bytes; + int isfragment; + u8 *p1, *p2; + s16 tmp; + int i, j, more, much, rc; +#ifdef 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; + JOT(16, "\n"); -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 (0 != rc) { - if (-ENODEV != rc) - SAM("ERROR: while %i=audio_idle, " \ - "usb_submit_urb() failed with rc:\n", \ - peasycap->audio_idle); - switch (rc) { - case -ENOMEM: { - SAM("-ENOMEM\n"); - break; - } - case -ENODEV: { - break; - } - case -ENXIO: { - SAM("-ENXIO\n"); - break; - } - case -EINVAL: { - SAM("-EINVAL\n"); - break; - } - case -EAGAIN: { - SAM("-EAGAIN\n"); - break; - } - case -EFBIG: { - SAM("-EFBIG\n"); - break; - } - case -EPIPE: { - SAM("-EPIPE\n"); - break; - } - case -EMSGSIZE: { - SAM("-EMSGSIZE\n"); - break; - } - case -ENOSPC: { - SAM("-ENOSPC\n"); - break; - } - default: { - SAM("unknown error: 0x%08X\n", rc); - break; - } - } - } - } -return; -} -/*---------------------------------------------------------------------------*/ -if (purb->status) { - if ((-ESHUTDOWN == purb->status) || (-ENOENT == purb->status)) { - JOM(16, "urb status -ESHUTDOWN or -ENOENT\n"); + if (!purb) { + SAY("ERROR: purb is NULL\n"); return; } - SAM("ERROR: non-zero urb status:\n"); - switch (purb->status) { - case -EINPROGRESS: { - SAM("-EINPROGRESS\n"); - break; - } - case -ENOSR: { - SAM("-ENOSR\n"); - break; - } - case -EPIPE: { - SAM("-EPIPE\n"); - break; - } - case -EOVERFLOW: { - SAM("-EOVERFLOW\n"); - break; - } - case -EPROTO: { - SAM("-EPROTO\n"); - break; - } - case -EILSEQ: { - SAM("-EILSEQ\n"); - break; - } - case -ETIMEDOUT: { - SAM("-ETIMEDOUT\n"); - break; - } - case -EMSGSIZE: { - SAM("-EMSGSIZE\n"); - break; - } - case -EOPNOTSUPP: { - SAM("-EOPNOTSUPP\n"); - break; - } - case -EPFNOSUPPORT: { - SAM("-EPFNOSUPPORT\n"); - break; - } - case -EAFNOSUPPORT: { - SAM("-EAFNOSUPPORT\n"); - break; - } - case -EADDRINUSE: { - SAM("-EADDRINUSE\n"); - break; - } - case -EADDRNOTAVAIL: { - SAM("-EADDRNOTAVAIL\n"); - break; - } - case -ENOBUFS: { - SAM("-ENOBUFS\n"); - break; - } - case -EISCONN: { - SAM("-EISCONN\n"); - break; - } - case -ENOTCONN: { - SAM("-ENOTCONN\n"); - break; - } - case -ESHUTDOWN: { - SAM("-ESHUTDOWN\n"); - break; - } - case -ENOENT: { - SAM("-ENOENT\n"); - break; - } - case -ECONNRESET: { - SAM("-ECONNRESET\n"); - break; - } - case -ENOSPC: { - SAM("ENOSPC\n"); - break; - } - default: { - SAM("unknown error code 0x%08X\n", purb->status); - break; + peasycap = purb->context; + if (!peasycap) { + SAY("ERROR: peasycap is NULL\n"); + return; } + if (memcmp(&peasycap->telltale[0], TELLTALE, strlen(TELLTALE))) { + SAY("ERROR: bad peasycap\n"); + return; } -/*---------------------------------------------------------------------------*/ -/* - * RESUBMIT THIS URB AFTER AN ERROR - * - * (THIS IS DUPLICATE CODE TO REDUCE INDENTATION OF THE NO-ERROR PATH) - */ -/*---------------------------------------------------------------------------*/ - if (peasycap->audio_isoc_streaming) { - rc = usb_submit_urb(purb, GFP_ATOMIC); - if (0 != rc) { - SAM("ERROR: while %i=audio_idle, usb_submit_urb() " - "failed with rc:\n", peasycap->audio_idle); - switch (rc) { - case -ENOMEM: { - SAM("-ENOMEM\n"); - break; - } - case -ENODEV: { - SAM("-ENODEV\n"); - break; - } - case -ENXIO: { - SAM("-ENXIO\n"); - break; - } - case -EINVAL: { - SAM("-EINVAL\n"); - break; - } - case -EAGAIN: { - SAM("-EAGAIN\n"); - break; - } - case -EFBIG: { - SAM("-EFBIG\n"); - break; - } - case -EPIPE: { - SAM("-EPIPE\n"); - break; - } - case -EMSGSIZE: { - SAM("-EMSGSIZE\n"); - break; - } - default: { - SAM("0x%08X\n", rc); break; - } - } + 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) + goto resubmit; + } +/*---------------------------------------------------------------------------*/ + pss = peasycap->psubstream; + if (!pss) + goto resubmit; + prt = pss->runtime; + if (!prt) + goto resubmit; + dma_bytes = (int)prt->dma_bytes; + if (0 == dma_bytes) + goto resubmit; + fragment_bytes = 4 * ((int)prt->period_size); + if (0 == fragment_bytes) + goto resubmit; +/* -------------------------------------------------------------------------*/ + 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; } - return; -} /*---------------------------------------------------------------------------*/ /* * PROCEED HERE WHEN NO ERROR */ /*---------------------------------------------------------------------------*/ -#if defined(UPSAMPLE) -oldaudio = peasycap->oldaudio; + +#ifdef UPSAMPLE + oldaudio = peasycap->oldaudio; #endif /*UPSAMPLE*/ -for (i = 0; i < purb->number_of_packets; i++) { - switch (purb->iso_frame_desc[i].status) { - case 0: { - break; - } - case -ENOENT: { - SAM("-ENOENT\n"); - break; - } - case -EINPROGRESS: { - SAM("-EINPROGRESS\n"); - break; - } - case -EPROTO: { - SAM("-EPROTO\n"); - break; - } - case -EILSEQ: { - SAM("-EILSEQ\n"); - break; - } - case -ETIME: { - SAM("-ETIME\n"); - break; - } - case -ETIMEDOUT: { - SAM("-ETIMEDOUT\n"); - break; - } - case -EPIPE: { - SAM("-EPIPE\n"); - break; - } - case -ECOMM: { - SAM("-ECOMM\n"); - break; - } - case -ENOSR: { - SAM("-ENOSR\n"); - break; - } - case -EOVERFLOW: { - SAM("-EOVERFLOW\n"); - break; - } - case -EREMOTEIO: { - SAM("-EREMOTEIO\n"); - break; - } - case -ENODEV: { - SAM("-ENODEV\n"); - break; - } - case -EXDEV: { - SAM("-EXDEV\n"); - break; - } - case -EINVAL: { - SAM("-EINVAL\n"); - break; - } - case -ECONNRESET: { - SAM("-ECONNRESET\n"); - break; - } - case -ENOSPC: { - SAM("-ENOSPC\n"); - break; - } - case -ESHUTDOWN: { - SAM("-ESHUTDOWN\n"); - break; - } - default: { - SAM("unknown error:0x%08X\n", purb->iso_frame_desc[i].status); - break; - } - } - if (!purb->iso_frame_desc[i].status) { + for (i = 0; i < purb->number_of_packets; i++) { + if (purb->iso_frame_desc[i].status < 0) { + SAM("-%s: %d\n", + strerror(purb->iso_frame_desc[i].status), + purb->iso_frame_desc[i].status); + } + if (purb->iso_frame_desc[i].status) { + JOM(12, "discarding audio samples because " + "%i=purb->iso_frame_desc[i].status\n", + purb->iso_frame_desc[i].status); + continue; + } more = purb->iso_frame_desc[i].actual_length; - -#if defined(TESTTONE) - if (!more) - more = purb->iso_frame_desc[i].length; -#endif - - if (!more) + if (more == 0) { peasycap->audio_mt++; - else { - if (peasycap->audio_mt) { - JOM(16, "%4i empty audio urb frames\n", \ - peasycap->audio_mt); - peasycap->audio_mt = 0; - } + continue; + } + if (0 > more) { + SAM("MISTAKE: more is negative\n"); + return; + } - p1 = (__u8 *)(purb->transfer_buffer + \ - purb->iso_frame_desc[i].offset); + if (peasycap->audio_mt) { + JOM(12, "%4i empty audio urb frames\n", + peasycap->audio_mt); + peasycap->audio_mt = 0; + } - 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("easysnd_complete: MISTAKE: " \ - "more is negative\n"); - return; + p1 = (u8 *)(purb->transfer_buffer + + purb->iso_frame_desc[i].offset); + + /* + * COPY more BYTES FROM ISOC BUFFER + * TO THE DMA BUFFER, CONVERTING + * 8-BIT MONO TO 16-BIT SIGNED + * LITTLE-ENDIAN SAMPLES IF NECESSARY + */ + while (more) { + much = dma_bytes - peasycap->dma_fill; + if (0 > much) { + SAM("MISTAKE: much is negative\n"); + return; + } + if (0 == much) { + peasycap->dma_fill = 0; + peasycap->dma_next = fragment_bytes; + JOM(8, "wrapped dma buffer\n"); + } + if (!peasycap->microphone) { + if (much > more) + much = more; + memcpy(prt->dma_area + peasycap->dma_fill, + p1, much); + p1 += much; + more -= much; + } else { +#ifdef UPSAMPLE + if (much % 16) + JOM(8, "MISTAKE? much" + " is not divisible by 16\n"); + if (much > (16 * more)) + much = 16 * more; + p2 = (u8 *)(prt->dma_area + peasycap->dma_fill); + + for (j = 0; j < (much / 16); j++) { + newaudio = ((int) *p1) - 128; + newaudio = 128 * newaudio; + + delta = (newaudio - oldaudio) / 4; + tmp = oldaudio + delta; + + for (k = 0; k < 4; k++) { + *p2 = (0x00FF & tmp); + *(p2 + 1) = (0xFF00 & tmp) >> 8; + p2 += 2; + *p2 = (0x00FF & tmp); + *(p2 + 1) = (0xFF00 & tmp) >> 8; + p2 += 2; + tmp += delta; + } + p1++; + more--; + oldaudio = tmp; } - if (peasycap->audio_buffer_page_many <= \ - peasycap->audio_fill) { - SAM("ERROR: bad " \ - "peasycap->audio_fill\n"); - return; +#else /*!UPSAMPLE*/ + if (much > (2 * more)) + much = 2 * more; + p2 = (u8 *)(prt->dma_area + peasycap->dma_fill); + + for (j = 0; j < (much / 2); j++) { + tmp = ((int) *p1) - 128; + tmp = 128 * tmp; + *p2 = (0x00FF & tmp); + *(p2 + 1) = (0xFF00 & tmp) >> 8; + p1++; + p2 += 2; + more--; } - - paudio_buffer = &peasycap->audio_buffer\ - [peasycap->audio_fill]; - if (PAGE_SIZE < (paudio_buffer->pto - \ - paudio_buffer->pgo)) { - SAM("ERROR: bad paudio_buffer->pto\n"); +#endif /*UPSAMPLE*/ + } + peasycap->dma_fill += much; + if (peasycap->dma_fill >= peasycap->dma_next) { + isfragment = peasycap->dma_fill / fragment_bytes; + if (0 > isfragment) { + SAM("MISTAKE: isfragment is negative\n"); return; } - if (PAGE_SIZE == (paudio_buffer->pto - \ - paudio_buffer->pgo)) { - -#if defined(TESTTONE) - easysnd_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(12, "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)); - } + peasycap->dma_read = (isfragment - 1) * fragment_bytes; + peasycap->dma_next = (isfragment + 1) * fragment_bytes; + if (dma_bytes < peasycap->dma_next) + peasycap->dma_next = fragment_bytes; + + if (0 <= peasycap->dma_read) { + JOM(8, "snd_pcm_period_elapsed(), %i=" + "isfragment\n", isfragment); + snd_pcm_period_elapsed(pss); } - - 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 - 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; +#ifdef UPSAMPLE + peasycap->oldaudio = oldaudio; #endif /*UPSAMPLE*/ -} + } /*---------------------------------------------------------------------------*/ /* - * RESUBMIT THIS URB AFTER NO ERROR + * RESUBMIT THIS URB */ /*---------------------------------------------------------------------------*/ -if (peasycap->audio_isoc_streaming) { +resubmit: + if (peasycap->audio_isoc_streaming == 0) + return; + rc = usb_submit_urb(purb, GFP_ATOMIC); - if (0 != rc) { - if (-ENODEV != rc) { - SAM("ERROR: while %i=audio_idle, " \ - "usb_submit_urb() failed " \ - "with rc:\n", peasycap->audio_idle); - } - switch (rc) { - case -ENOMEM: { - SAM("-ENOMEM\n"); - break; - } - case -ENODEV: { - break; - } - case -ENXIO: { - SAM("-ENXIO\n"); - break; - } - case -EINVAL: { - SAM("-EINVAL\n"); - break; - } - case -EAGAIN: { - SAM("-EAGAIN\n"); - break; - } - case -EFBIG: { - SAM("-EFBIG\n"); - break; - } - case -EPIPE: { - SAM("-EPIPE\n"); - break; - } - case -EMSGSIZE: { - SAM("-EMSGSIZE\n"); - break; - } - case -ENOSPC: { - SAM("-ENOSPC\n"); - break; - } - default: { - SAM("unknown error: 0x%08X\n", rc); - break; - } + 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); } + if (0 < peasycap->audio_isoc_streaming) + peasycap->audio_isoc_streaming--; } -} -return; + return; } /*****************************************************************************/ -/*---------------------------------------------------------------------------*/ -/* - * THE AUDIO URBS ARE SUBMITTED AT THIS EARLY STAGE SO THAT IT IS POSSIBLE TO - * STREAM FROM /dev/easysnd1 WITH SIMPLE PROGRAMS SUCH AS cat WHICH DO NOT - * HAVE AN IOCTL INTERFACE. - */ -/*---------------------------------------------------------------------------*/ -int -easysnd_open(struct inode *inode, struct file *file) +static int easycap_alsa_open(struct snd_pcm_substream *pss) { -struct usb_interface *pusb_interface; -struct easycap *peasycap; -int subminor, rc; -/*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"); + struct snd_pcm *psnd_pcm; + struct snd_card *psnd_card; + struct easycap *peasycap; + + JOT(4, "\n"); + if (!pss) { + SAY("ERROR: pss is NULL\n"); + return -EFAULT; + } + psnd_pcm = pss->pcm; + if (!psnd_pcm) { + SAY("ERROR: psnd_pcm is NULL\n"); + return -EFAULT; + } + psnd_card = psnd_pcm->card; + if (!psnd_card) { + SAY("ERROR: psnd_card 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; -/*---------------------------------------------------------------------------*/ -/* - * INITIALIZATION - */ -/*---------------------------------------------------------------------------*/ -JOM(4, "starting initialization\n"); + peasycap = psnd_card->private_data; + if (!peasycap) { + SAY("ERROR: peasycap is NULL\n"); + return -EFAULT; + } + if (memcmp(&peasycap->telltale[0], TELLTALE, strlen(TELLTALE))) { + SAY("ERROR: bad peasycap\n"); + return -EFAULT; + } + if (peasycap->psnd_card != psnd_card) { + SAM("ERROR: bad peasycap->psnd_card\n"); + return -EFAULT; + } + if (peasycap->psubstream) { + SAM("ERROR: bad peasycap->psubstream\n"); + return -EFAULT; + } + pss->private_data = peasycap; + peasycap->psubstream = pss; + pss->runtime->hw = peasycap->alsa_hardware; + pss->runtime->private_data = peasycap; + pss->private_data = peasycap; -if ((struct usb_device *)NULL == peasycap->pusb_device) { - SAM("ERROR: peasycap->pusb_device is NULL\n"); - return -ENODEV; + if (0 != easycap_sound_setup(peasycap)) { + JOM(4, "ending unsuccessfully\n"); + return -EFAULT; + } + JOM(4, "ending successfully\n"); + return 0; } -JOM(16, "0x%08lX=peasycap->pusb_device\n", (long int)peasycap->pusb_device); - -rc = audio_setup(peasycap); -if (0 <= rc) - JOM(8, "audio_setup() returned %i\n", rc); -else - JOM(8, "easysnd open(): ERROR: audio_setup() returned %i\n", rc); +/*****************************************************************************/ +static int easycap_alsa_close(struct snd_pcm_substream *pss) +{ + struct easycap *peasycap; -if ((struct usb_device *)NULL == peasycap->pusb_device) { - SAM("ERROR: peasycap->pusb_device has become NULL\n"); - return -ENODEV; -} -/*---------------------------------------------------------------------------*/ -if ((struct usb_device *)NULL == peasycap->pusb_device) { - SAM("ERROR: peasycap->pusb_device has become NULL\n"); - return -ENODEV; + JOT(4, "\n"); + if (!pss) { + SAY("ERROR: pss is NULL\n"); + return -EFAULT; + } + peasycap = snd_pcm_substream_chip(pss); + if (!peasycap) { + SAY("ERROR: peasycap is NULL\n"); + return -EFAULT; + } + if (memcmp(&peasycap->telltale[0], TELLTALE, strlen(TELLTALE))) { + SAY("ERROR: bad peasycap\n"); + return -EFAULT; + } + pss->private_data = NULL; + peasycap->psubstream = NULL; + JOT(4, "ending successfully\n"); + return 0; } -rc = usb_set_interface(peasycap->pusb_device, peasycap->audio_interface, \ - peasycap->audio_altsetting_on); -JOM(8, "usb_set_interface(.,%i,%i) returned %i\n", peasycap->audio_interface, \ - peasycap->audio_altsetting_on, rc); - -rc = wakeup_device(peasycap->pusb_device); -if (0 == rc) - JOM(8, "wakeup_device() returned %i\n", rc); -else - JOM(8, "ERROR: wakeup_device() returned %i\n", rc); - -peasycap->audio_eof = 0; -peasycap->audio_idle = 0; - -peasycap->timeval1.tv_sec = 0; -peasycap->timeval1.tv_usec = 0; - -submit_audio_urbs(peasycap); +/*****************************************************************************/ +static int easycap_alsa_vmalloc(struct snd_pcm_substream *pss, size_t sz) +{ + struct snd_pcm_runtime *prt; + JOT(4, "\n"); -JOM(4, "finished initialization\n"); -return 0; + if (!pss) { + SAY("ERROR: pss is NULL\n"); + return -EFAULT; + } + prt = pss->runtime; + if (!prt) { + SAY("ERROR: substream.runtime is NULL\n"); + return -EFAULT; + } + if (prt->dma_area) { + if (prt->dma_bytes > sz) + return 0; + vfree(prt->dma_area); + } + prt->dma_area = vmalloc(sz); + if (!prt->dma_area) + return -ENOMEM; + prt->dma_bytes = sz; + return 0; } /*****************************************************************************/ -int -easysnd_release(struct inode *inode, struct file *file) +static int easycap_alsa_hw_params(struct snd_pcm_substream *pss, + struct snd_pcm_hw_params *phw) { -struct easycap *peasycap; + int rc; -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; + JOT(4, "%i\n", (params_buffer_bytes(phw))); + if (!pss) { + SAY("ERROR: pss is NULL\n"); + return -EFAULT; + } + rc = easycap_alsa_vmalloc(pss, params_buffer_bytes(phw)); + if (rc) + return rc; + return 0; } /*****************************************************************************/ -ssize_t -easysnd_read(struct file *file, char __user *puserspacebuffer, \ - size_t kount, loff_t *poff) +static int easycap_alsa_hw_free(struct snd_pcm_substream *pss) { -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, "===== easysnd_read(): kount=%i, *poff=%i\n", (int)kount, (int)(*poff)); + struct snd_pcm_runtime *prt; + JOT(4, "\n"); -if (NULL == file) { - SAY("ERROR: file is NULL\n"); - return -ERESTARTSYS; -} -peasycap = file->private_data; -if (NULL == peasycap) { - SAY("ERROR in easysnd_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 in easysnd_read(): peasycap->pusb_device is NULL\n"); - return -EFAULT; -} -kd = isdongle(peasycap); -if (0 <= kd && DONGLE_MANY > kd) { - if (mutex_lock_interruptible(&(easycap_dongle[kd].mutex_audio))) { - SAY("ERROR: cannot lock easycap_dongle[%i].mutex_audio\n", kd); - return -ERESTARTSYS; + if (!pss) { + SAY("ERROR: pss is NULL\n"); + return -EFAULT; } - JOM(4, "locked easycap_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(&easycap_dongle[kd].mutex_audio); - return -ERESTARTSYS; + prt = pss->runtime; + if (!prt) { + SAY("ERROR: substream.runtime is NULL\n"); + return -EFAULT; + } + if (prt->dma_area) { + JOT(8, "prt->dma_area = %p\n", prt->dma_area); + vfree(prt->dma_area); + prt->dma_area = NULL; + } else + JOT(8, "dma_area already freed\n"); + return 0; +} +/*****************************************************************************/ +static int easycap_alsa_prepare(struct snd_pcm_substream *pss) +{ + struct easycap *peasycap; + struct snd_pcm_runtime *prt; + + JOT(4, "\n"); + if (!pss) { + SAY("ERROR: pss is NULL\n"); + return -EFAULT; } - peasycap = file->private_data; - if (NULL == peasycap) { + prt = pss->runtime; + peasycap = snd_pcm_substream_chip(pss); + if (!peasycap) { SAY("ERROR: peasycap is NULL\n"); - mutex_unlock(&easycap_dongle[kd].mutex_audio); - return -ERESTARTSYS; + return -EFAULT; } if (memcmp(&peasycap->telltale[0], TELLTALE, strlen(TELLTALE))) { - SAY("ERROR: bad peasycap: 0x%08lX\n", \ - (unsigned long int) peasycap); - mutex_unlock(&easycap_dongle[kd].mutex_audio); - return -ERESTARTSYS; + SAY("ERROR: bad peasycap\n"); + return -EFAULT; } - if (NULL == peasycap->pusb_device) { - SAM("ERROR: peasycap->pusb_device is NULL\n"); - mutex_unlock(&easycap_dongle[kd].mutex_audio); - return -ERESTARTSYS; + + JOM(16, "ALSA decides %8i Hz=rate\n", pss->runtime->rate); + JOM(16, "ALSA decides %8ld =period_size\n", pss->runtime->period_size); + JOM(16, "ALSA decides %8i =periods\n", pss->runtime->periods); + JOM(16, "ALSA decides %8ld =buffer_size\n", pss->runtime->buffer_size); + JOM(16, "ALSA decides %8zd =dma_bytes\n", pss->runtime->dma_bytes); + JOM(16, "ALSA decides %8ld =boundary\n", pss->runtime->boundary); + JOM(16, "ALSA decides %8i =period_step\n", pss->runtime->period_step); + JOM(16, "ALSA decides %8i =sample_bits\n", pss->runtime->sample_bits); + JOM(16, "ALSA decides %8i =frame_bits\n", pss->runtime->frame_bits); + JOM(16, "ALSA decides %8ld =min_align\n", pss->runtime->min_align); + JOM(12, "ALSA decides %8ld =hw_ptr_base\n", pss->runtime->hw_ptr_base); + JOM(12, "ALSA decides %8ld =hw_ptr_interrupt\n", + pss->runtime->hw_ptr_interrupt); + + if (prt->dma_bytes != 4 * ((int)prt->period_size) * ((int)prt->periods)) { + SAY("MISTAKE: unexpected ALSA parameters\n"); + return -ENOENT; } -} 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; + return 0; } -/*---------------------------------------------------------------------------*/ -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(&easycap_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(&easycap_dongle[kd].mutex_audio); - return -EFAULT; +/*****************************************************************************/ +static int easycap_alsa_ack(struct snd_pcm_substream *pss) +{ + return 0; } -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(&easycap_dongle[kd].mutex_audio); - return -EAGAIN; +/*****************************************************************************/ +static int easycap_alsa_trigger(struct snd_pcm_substream *pss, int cmd) +{ + struct easycap *peasycap; + int retval; + + JOT(4, "%i=cmd cf %i=START %i=STOP\n", cmd, SNDRV_PCM_TRIGGER_START, + SNDRV_PCM_TRIGGER_STOP); + if (!pss) { + SAY("ERROR: pss is NULL\n"); + return -EFAULT; } - 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(&easycap_dongle[kd].mutex_audio); - return -ERESTARTSYS; + peasycap = snd_pcm_substream_chip(pss); + if (!peasycap) { + SAY("ERROR: peasycap is NULL\n"); + return -EFAULT; } - if (peasycap->audio_eof) { - JOM(8, "returning 0 because %i=audio_eof\n", \ - peasycap->audio_eof); - kill_audio_urbs(peasycap); - mutex_unlock(&easycap_dongle[kd].mutex_audio); - return 0; + if (memcmp(&peasycap->telltale[0], TELLTALE, strlen(TELLTALE))) { + SAY("ERROR: bad peasycap\n"); + return -EFAULT; } - if (peasycap->audio_idle) { - JOM(16, "returning 0 because %i=audio_idle\n", \ - peasycap->audio_idle); - mutex_unlock(&easycap_dongle[kd].mutex_audio); - return 0; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: { + peasycap->audio_idle = 0; + break; } - if (!peasycap->audio_isoc_streaming) { - JOM(16, "returning 0 because audio urbs not streaming\n"); - mutex_unlock(&easycap_dongle[kd].mutex_audio); - return 0; + case SNDRV_PCM_TRIGGER_STOP: { + peasycap->audio_idle = 1; + break; } + default: + retval = -EINVAL; + } + 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; -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(&easycap_dongle[kd].mutex_audio); +/*****************************************************************************/ +static snd_pcm_uframes_t easycap_alsa_pointer(struct snd_pcm_substream *pss) +{ + struct easycap *peasycap; + snd_pcm_uframes_t offset; + + JOT(16, "\n"); + if (!pss) { + SAY("ERROR: pss is NULL\n"); return -EFAULT; } - if (NULL == pdata_buffer->pto) { - SAM("ERROR: pdata_buffer->pto is NULL\n"); - mutex_unlock(&easycap_dongle[kd].mutex_audio); + peasycap = snd_pcm_substream_chip(pss); + if (!peasycap) { + SAY("ERROR: peasycap is NULL\n"); return -EFAULT; } - kount1 = PAGE_SIZE - (pdata_buffer->pto - pdata_buffer->pgo); - if (0 > kount1) { - SAM("easysnd_read: MISTAKE: kount1 is negative\n"); - mutex_unlock(&easycap_dongle[kd].mutex_audio); - return -ERESTARTSYS; + if (memcmp(&peasycap->telltale[0], TELLTALE, strlen(TELLTALE))) { + SAY("ERROR: bad peasycap\n"); + return -EFAULT; } - 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(&easycap_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(&easycap_dongle[kd].mutex_audio); - return -EFAULT; - } - if (NULL == pdata_buffer->pgo) { - SAM("ERROR: pdata_buffer->pgo is NULL\n"); - mutex_unlock(&easycap_dongle[kd].mutex_audio); - return -EFAULT; - } - if (NULL == pdata_buffer->pto) { - SAM("ERROR: pdata_buffer->pto is NULL\n"); - mutex_unlock(&easycap_dongle[kd].mutex_audio); - return -EFAULT; - } - kount1 = PAGE_SIZE - (pdata_buffer->pto - pdata_buffer->pgo); + if ((0 != peasycap->audio_eof) || (0 != peasycap->audio_idle)) { + JOM(8, "returning -EIO because " + "%i=audio_idle %i=audio_eof\n", + peasycap->audio_idle, peasycap->audio_eof); + return -EIO; } - 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; +/*---------------------------------------------------------------------------*/ + if (0 > peasycap->dma_read) { + JOM(8, "returning -EBUSY\n"); + return -EBUSY; + } + offset = ((snd_pcm_uframes_t)peasycap->dma_read)/4; + JOM(8, "ALSA decides %8i =hw_ptr_base\n", (int)pss->runtime->hw_ptr_base); + JOM(8, "ALSA decides %8i =hw_ptr_interrupt\n", + (int)pss->runtime->hw_ptr_interrupt); + JOM(8, "%7i=offset %7i=dma_read %7i=dma_next\n", + (int)offset, peasycap->dma_read, peasycap->dma_next); + return offset; +} +/*****************************************************************************/ +static struct page * +easycap_alsa_page(struct snd_pcm_substream *pss, unsigned long offset) +{ + return vmalloc_to_page(pss->runtime->dma_area + offset); +} +/*****************************************************************************/ + +static struct snd_pcm_ops easycap_alsa_pcm_ops = { + .open = easycap_alsa_open, + .close = easycap_alsa_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = easycap_alsa_hw_params, + .hw_free = easycap_alsa_hw_free, + .prepare = easycap_alsa_prepare, + .ack = easycap_alsa_ack, + .trigger = easycap_alsa_trigger, + .pointer = easycap_alsa_pointer, + .page = easycap_alsa_page, +}; +/*****************************************************************************/ /*---------------------------------------------------------------------------*/ /* - * ACCUMULATE DYNAMIC-RANGE INFORMATION - */ + * THE FUNCTION snd_card_create() HAS THIS_MODULE AS AN ARGUMENT. THIS + * MEANS MODULE easycap. BEWARE. +*/ /*---------------------------------------------------------------------------*/ - 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; +int easycap_alsa_probe(struct easycap *peasycap) +{ + int rc; + struct snd_card *psnd_card; + struct snd_pcm *psnd_pcm; + + if (!peasycap) { + SAY("ERROR: peasycap is NULL\n"); + return -ENODEV; } -/*---------------------------------------------------------------------------*/ - rc = copy_to_user(puserspacebuffer, pdata_buffer->pto, more); - if (0 != rc) { - SAM("ERROR: copy_to_user() returned %li\n", rc); - mutex_unlock(&easycap_dongle[kd].mutex_audio); + if (memcmp(&peasycap->telltale[0], TELLTALE, strlen(TELLTALE))) { + SAY("ERROR: bad peasycap\n"); 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); + if (0 > peasycap->minor) { + SAY("ERROR: no minor\n"); + return -ENODEV; + } + + peasycap->alsa_hardware = alsa_hardware; + if (peasycap->microphone) { + peasycap->alsa_hardware.rates = SNDRV_PCM_RATE_32000; + peasycap->alsa_hardware.rate_min = 32000; + peasycap->alsa_hardware.rate_max = 32000; + } else { + peasycap->alsa_hardware.rates = SNDRV_PCM_RATE_48000; + peasycap->alsa_hardware.rate_min = 48000; + peasycap->alsa_hardware.rate_max = 48000; + } + + if (0 != snd_card_create(SNDRV_DEFAULT_IDX1, "easycap_alsa", + THIS_MODULE, 0, &psnd_card)) { + SAY("ERROR: Cannot do ALSA snd_card_create()\n"); + return -EFAULT; + } + + sprintf(&psnd_card->id[0], "EasyALSA%i", peasycap->minor); + strcpy(&psnd_card->driver[0], EASYCAP_DRIVER_DESCRIPTION); + strcpy(&psnd_card->shortname[0], "easycap_alsa"); + sprintf(&psnd_card->longname[0], "%s", &psnd_card->shortname[0]); + + psnd_card->dev = &peasycap->pusb_device->dev; + psnd_card->private_data = peasycap; + peasycap->psnd_card = psnd_card; + + rc = snd_pcm_new(psnd_card, "easycap_pcm", 0, 0, 1, &psnd_pcm); + if (rc) { + SAM("ERROR: Cannot do ALSA snd_pcm_new()\n"); + snd_card_free(psnd_card); + return -EFAULT; + } + + snd_pcm_set_ops(psnd_pcm, SNDRV_PCM_STREAM_CAPTURE, + &easycap_alsa_pcm_ops); + psnd_pcm->info_flags = 0; + strcpy(&psnd_pcm->name[0], &psnd_card->id[0]); + psnd_pcm->private_data = peasycap; + peasycap->psnd_pcm = psnd_pcm; + peasycap->psubstream = NULL; + + rc = snd_card_register(psnd_card); + if (rc) { + SAM("ERROR: Cannot do ALSA snd_card_register()\n"); + snd_card_free(psnd_card); + return -EFAULT; + } + + SAM("registered %s\n", &psnd_card->id[0]); + return 0; } +#endif /*! CONFIG_EASYCAP_OSS */ + +/*****************************************************************************/ +/*****************************************************************************/ +/*****************************************************************************/ +/*****************************************************************************/ +/*****************************************************************************/ +/*****************************************************************************/ /*---------------------------------------------------------------------------*/ /* - * CALCULATE DYNAMIC RANGE FOR (VAPOURWARE) AUTOMATIC VOLUME CONTROL + * COMMON AUDIO INITIALIZATION */ /*---------------------------------------------------------------------------*/ -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 - */ +int +easycap_sound_setup(struct easycap *peasycap) +{ + int rc; + + JOM(4, "starting initialization\n"); + + if (!peasycap) { + SAY("ERROR: peasycap is NULL.\n"); + return -EFAULT; + } + if (!peasycap->pusb_device) { + SAM("ERROR: peasycap->pusb_device is NULL\n"); + return -ENODEV; + } + JOM(16, "0x%08lX=peasycap->pusb_device\n", (long int)peasycap->pusb_device); + + rc = audio_setup(peasycap); + JOM(8, "audio_setup() returned %i\n", rc); + + if (!peasycap->pusb_device) { + SAM("ERROR: peasycap->pusb_device has become NULL\n"); + return -ENODEV; + } /*---------------------------------------------------------------------------*/ -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; + if (!peasycap->pusb_device) { + SAM("ERROR: peasycap->pusb_device has become NULL\n"); + return -ENODEV; + } + rc = usb_set_interface(peasycap->pusb_device, peasycap->audio_interface, + peasycap->audio_altsetting_on); + JOM(8, "usb_set_interface(.,%i,%i) returned %i\n", peasycap->audio_interface, + peasycap->audio_altsetting_on, rc); -JOM(8, "returning %li\n", (long int)szret); -mutex_unlock(&easycap_dongle[kd].mutex_audio); -return szret; + rc = wakeup_device(peasycap->pusb_device); + JOM(8, "wakeup_device() returned %i\n", rc); + + peasycap->audio_eof = 0; + peasycap->audio_idle = 0; + + peasycap->timeval1.tv_sec = 0; + peasycap->timeval1.tv_usec = 0; + + submit_audio_urbs(peasycap); + + JOM(4, "finished initialization\n"); + return 0; } /*****************************************************************************/ /*---------------------------------------------------------------------------*/ @@ -1042,120 +714,75 @@ return szret; int submit_audio_urbs(struct easycap *peasycap) { -struct data_urb *pdata_urb; -struct urb *purb; -struct list_head *plist_head; -int j, isbad, nospc, m, rc; -int isbuf; - -if (NULL == peasycap) { - SAY("ERROR: peasycap is NULL\n"); - return -EFAULT; -} -if ((struct list_head *)NULL == peasycap->purb_audio_head) { - SAM("ERROR: peasycap->urb_audio_head uninitialized\n"); - return -EFAULT; -} -if ((struct usb_device *)NULL == peasycap->pusb_device) { - SAM("ERROR: peasycap->pusb_device is NULL\n"); - return -EFAULT; -} -if (!peasycap->audio_isoc_streaming) { + struct data_urb *pdata_urb; + struct urb *purb; + struct list_head *plist_head; + int j, isbad, nospc, m, rc; + int isbuf; + + if (!peasycap) { + SAY("ERROR: peasycap is NULL\n"); + return -EFAULT; + } + if (!peasycap->purb_audio_head) { + SAM("ERROR: peasycap->urb_audio_head uninitialized\n"); + return -EFAULT; + } + if (!peasycap->pusb_device) { + SAM("ERROR: peasycap->pusb_device is NULL\n"); + return -EFAULT; + } + + if (peasycap->audio_isoc_streaming) { + JOM(4, "already streaming audio urbs\n"); + return 0; + } + JOM(4, "initial submission of all audio urbs\n"); rc = usb_set_interface(peasycap->pusb_device, - peasycap->audio_interface, \ - peasycap->audio_altsetting_on); - JOM(8, "usb_set_interface(.,%i,%i) returned %i\n", \ - peasycap->audio_interface, \ - peasycap->audio_altsetting_on, rc); - - isbad = 0; nospc = 0; m = 0; - list_for_each(plist_head, (peasycap->purb_audio_head)) { + peasycap->audio_interface, + peasycap->audio_altsetting_on); + JOM(8, "usb_set_interface(.,%i,%i) returned %i\n", + peasycap->audio_interface, + peasycap->audio_altsetting_on, rc); + + isbad = 0; + nospc = 0; + m = 0; + list_for_each(plist_head, peasycap->purb_audio_head) { pdata_urb = list_entry(plist_head, struct data_urb, list_head); - if (NULL != pdata_urb) { + if (pdata_urb && pdata_urb->purb) { purb = pdata_urb->purb; - if (NULL != purb) { - isbuf = pdata_urb->isbuf; + isbuf = pdata_urb->isbuf; - purb->interval = 1; - purb->dev = peasycap->pusb_device; - purb->pipe = \ - usb_rcvisocpipe(peasycap->pusb_device,\ + purb->interval = 1; + purb->dev = peasycap->pusb_device; + purb->pipe = usb_rcvisocpipe(peasycap->pusb_device, peasycap->audio_endpointnumber); - purb->transfer_flags = URB_ISO_ASAP; - purb->transfer_buffer = \ - peasycap->audio_isoc_buffer[isbuf].pgo; - purb->transfer_buffer_length = \ - peasycap->audio_isoc_buffer_size; - purb->complete = easysnd_complete; - purb->context = peasycap; - purb->start_frame = 0; - purb->number_of_packets = \ - peasycap->audio_isoc_framesperdesc; - for (j = 0; j < peasycap->\ - audio_isoc_framesperdesc; \ - j++) { - purb->iso_frame_desc[j].offset = j * \ - peasycap->\ - audio_isoc_maxframesize; - purb->iso_frame_desc[j].length = \ - peasycap->\ - audio_isoc_maxframesize; - } + purb->transfer_flags = URB_ISO_ASAP; + purb->transfer_buffer = peasycap->audio_isoc_buffer[isbuf].pgo; + purb->transfer_buffer_length = peasycap->audio_isoc_buffer_size; +#ifdef CONFIG_EASYCAP_OSS + purb->complete = easyoss_complete; +#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; + for (j = 0; j < peasycap->audio_isoc_framesperdesc; j++) { + purb->iso_frame_desc[j].offset = j * peasycap->audio_isoc_maxframesize; + purb->iso_frame_desc[j].length = peasycap->audio_isoc_maxframesize; + } - rc = usb_submit_urb(purb, GFP_KERNEL); - if (0 != rc) { - isbad++; - SAM("ERROR: usb_submit_urb() failed" \ - " for urb with rc:\n"); - switch (rc) { - case -ENOMEM: { - SAM("-ENOMEM\n"); - break; - } - case -ENODEV: { - SAM("-ENODEV\n"); - break; - } - case -ENXIO: { - SAM("-ENXIO\n"); - break; - } - case -EINVAL: { - SAM("-EINVAL\n"); - break; - } - case -EAGAIN: { - SAM("-EAGAIN\n"); - break; - } - case -EFBIG: { - SAM("-EFBIG\n"); - break; - } - case -EPIPE: { - SAM("-EPIPE\n"); - break; - } - case -EMSGSIZE: { - SAM("-EMSGSIZE\n"); - break; - } - case -ENOSPC: { - nospc++; - break; - } - default: { - SAM("unknown error code %i\n",\ - rc); - break; - } - } - } else { - m++; - } - } else { + rc = usb_submit_urb(purb, GFP_KERNEL); + if (rc) { isbad++; + SAM("ERROR: usb_submit_urb() failed" + " for urb with rc: -%s: %d\n", + strerror(rc), rc); + } else { + m++; } } else { isbad++; @@ -1169,23 +796,17 @@ if (!peasycap->audio_isoc_streaming) { if (isbad) { JOM(4, "attempting cleanup instead of submitting\n"); list_for_each(plist_head, (peasycap->purb_audio_head)) { - pdata_urb = list_entry(plist_head, struct data_urb, \ - list_head); - if (NULL != pdata_urb) { - purb = pdata_urb->purb; - if (NULL != purb) - usb_kill_urb(purb); - } + pdata_urb = list_entry(plist_head, struct data_urb, list_head); + if (pdata_urb && pdata_urb->purb) + usb_kill_urb(pdata_urb->purb); } peasycap->audio_isoc_streaming = 0; } else { - peasycap->audio_isoc_streaming = 1; + peasycap->audio_isoc_streaming = m; JOM(4, "submitted %i audio urbs\n", m); } -} else - JOM(4, "already streaming audio urbs\n"); -return 0; + return 0; } /*****************************************************************************/ /*---------------------------------------------------------------------------*/ @@ -1196,38 +817,38 @@ return 0; int kill_audio_urbs(struct easycap *peasycap) { -int m; -struct list_head *plist_head; -struct data_urb *pdata_urb; + int m; + struct list_head *plist_head; + struct data_urb *pdata_urb; -if (NULL == peasycap) { - SAY("ERROR: peasycap is NULL\n"); - return -EFAULT; -} -if (peasycap->audio_isoc_streaming) { - if ((struct list_head *)NULL != peasycap->purb_audio_head) { - peasycap->audio_isoc_streaming = 0; - JOM(4, "killing audio urbs\n"); - m = 0; - list_for_each(plist_head, (peasycap->purb_audio_head)) { - pdata_urb = list_entry(plist_head, struct data_urb, - list_head); - if ((struct data_urb *)NULL != pdata_urb) { - if ((struct urb *)NULL != pdata_urb->purb) { - usb_kill_urb(pdata_urb->purb); - m++; - } - } - } - JOM(4, "%i audio urbs killed\n", m); - } else { + if (!peasycap) { + SAY("ERROR: peasycap is NULL\n"); + return -EFAULT; + } + + if (!peasycap->audio_isoc_streaming) { + JOM(8, "%i=audio_isoc_streaming, no audio urbs killed\n", + peasycap->audio_isoc_streaming); + return 0; + } + + if (!peasycap->purb_audio_head) { SAM("ERROR: peasycap->purb_audio_head is NULL\n"); return -EFAULT; } -} else { - JOM(8, "%i=audio_isoc_streaming, no audio urbs killed\n", \ - peasycap->audio_isoc_streaming); -} -return 0; + + peasycap->audio_isoc_streaming = 0; + JOM(4, "killing audio urbs\n"); + m = 0; + list_for_each(plist_head, (peasycap->purb_audio_head)) { + pdata_urb = list_entry(plist_head, struct data_urb, list_head); + if (pdata_urb && pdata_urb->purb) { + usb_kill_urb(pdata_urb->purb); + m++; + } + } + JOM(4, "%i audio urbs killed\n", m); + + return 0; } /*****************************************************************************/ |