Commit a270c7b7 authored by Jaroslav Kysela's avatar Jaroslav Kysela Committed by Jaroslav Kysela

[PATCH] ALSA update [10/12] - 2002/09/16

  - OSS mixer emulation
    - save the current volume values permanently
  - PCM midlevel
    - fixed 64bit division on non-ix86 32bit architectures
    - exported snd_pcm_new_stream()
  - PCI DMA allocation
    - fixes and updates
  - PCM Scatter-Gather code
    - don't set runtime->dma_bytes if runtime is null
  - CMI8330 - fix nor non-IsaPnP build
  - CMIPCI - added "Mic As Center/LFE" switch for model 039 or later
  - more initialization of structs in C99 style
  - intel8x0 - fixes for nvidia nforce
  - Maestro3 - added quirk for CF72 toughbook
  - VIA82xx - reset codec only when it's not ready
  - USB Audio
    - fixed oops at unloading if non-intialized substream exists
    - show interface number in proc files
    - clean up and fixed the parser of audio streams
parent 04c401f6
......@@ -46,6 +46,7 @@ struct _snd_oss_mixer_slot {
unsigned long private_value;
void *private_data;
void (*private_free)(snd_mixer_oss_slot_t *slot);
int volume[2];
};
struct _snd_oss_mixer {
......@@ -65,7 +66,6 @@ struct _snd_oss_mixer {
};
struct _snd_oss_file {
int volume[32][2];
snd_card_t *card;
snd_mixer_oss_t *mixer;
};
......
......@@ -460,6 +460,7 @@ void snd_pcm_lock(int unlock);
int snd_pcm_new(snd_card_t * card, char *id, int device,
int playback_count, int capture_count,
snd_pcm_t **rpcm);
int snd_pcm_new_stream(snd_pcm_t *pcm, int stream, int substream_count);
int snd_pcm_notify(snd_pcm_notify_t *notify, int nfree);
......@@ -526,7 +527,7 @@ static inline void divl(u_int32_t high, u_int32_t low,
int c = 32;
while (n > 0xffffffffU) {
q1 <<= 1;
if (n > d) {
if (n >= d) {
n -= d;
q1 |= 1;
}
......
/* include/version.h. Generated automatically by configure. */
#define CONFIG_SND_VERSION "0.9.0rc3"
#define CONFIG_SND_DATE " (Wed Sep 11 18:36:14 2002 UTC)"
#define CONFIG_SND_DATE " (Mon Sep 16 18:05:43 2002 UTC)"
......@@ -538,43 +538,3 @@ int copy_from_user_toio(unsigned long dst, const void *src, size_t count)
return 0;
#endif
}
#ifdef HACK_PCI_ALLOC_CONSISTENT
/*
* A dirty hack... when the kernel code is fixed this should be removed.
*
* since pci_alloc_consistent always tries GFP_DMA when the requested
* pci memory region is below 32bit, it happens quite often that even
* 2 order of pages cannot be allocated.
*
* so in the following, we allocate at first without dma_mask, so that
* allocation will be done without GFP_DMA. if the area doesn't match
* with the requested region, then realloate with the original dma_mask
* again.
*/
#undef pci_alloc_consistent
void *snd_pci_hack_alloc_consistent(struct pci_dev *hwdev, size_t size,
dma_addr_t *dma_handle)
{
void *ret;
u64 dma_mask;
unsigned long rmask;
if (hwdev == NULL)
return pci_alloc_consistent(hwdev, size, dma_handle);
dma_mask = hwdev->dma_mask;
rmask = ~((unsigned long)dma_mask);
hwdev->dma_mask = 0xffffffff; /* do without masking */
ret = pci_alloc_consistent(hwdev, size, dma_handle);
if (ret && ((*dma_handle + size - 1) & rmask)) {
pci_free_consistent(hwdev, size, ret, *dma_handle);
ret = 0;
}
hwdev->dma_mask = dma_mask; /* restore */
if (! ret)
ret = pci_alloc_consistent(hwdev, size, dma_handle);
return ret;
}
#endif /* hack */
......@@ -253,8 +253,8 @@ static int snd_mixer_oss_get_volume(snd_mixer_oss_file_t *fmixer, int slot)
if (mixer == NULL || slot > 30)
return -EIO;
pslot = &mixer->slots[slot];
left = fmixer->volume[slot][0];
right = fmixer->volume[slot][1];
left = pslot->volume[0];
right = pslot->volume[1];
if (pslot->get_volume)
result = pslot->get_volume(fmixer, pslot, &left, &right);
if (!pslot->stereo)
......@@ -262,8 +262,8 @@ static int snd_mixer_oss_get_volume(snd_mixer_oss_file_t *fmixer, int slot)
snd_assert(left >= 0 && left <= 100, return -EIO);
snd_assert(right >= 0 && right <= 100, return -EIO);
if (result >= 0) {
fmixer->volume[slot][0] = left;
fmixer->volume[slot][1] = right;
pslot->volume[0] = left;
pslot->volume[1] = right;
result = (left & 0xff) | ((right & 0xff) << 8);
}
return result;
......@@ -284,13 +284,13 @@ static int snd_mixer_oss_set_volume(snd_mixer_oss_file_t *fmixer,
if (right > 100)
right = 100;
if (!pslot->stereo)
left = right = left;
right = left;
if (pslot->put_volume)
result = pslot->put_volume(fmixer, pslot, left, right);
if (result < 0)
return result;
fmixer->volume[slot][0] = left;
fmixer->volume[slot][1] = right;
pslot->volume[0] = left;
pslot->volume[1] = right;
return (left & 0xff) | ((right & 0xff) << 8);
}
......@@ -409,6 +409,7 @@ static long snd_mixer_oss_conv(long val, long omin, long omax, long nmin, long n
return ((nrange * (val - omin)) + (orange / 2)) / orange + nmin;
}
/* convert from alsa native to oss values (0-100) */
static long snd_mixer_oss_conv1(long val, long min, long max, int *old)
{
if (val == snd_mixer_oss_conv(*old, 0, 100, min, max))
......@@ -416,6 +417,7 @@ static long snd_mixer_oss_conv1(long val, long min, long max, int *old)
return snd_mixer_oss_conv(val, min, max, 0, 100);
}
/* convert from oss to alsa native values */
static long snd_mixer_oss_conv2(long val, long min, long max)
{
return snd_mixer_oss_conv(val, 0, 100, min, max);
......@@ -502,9 +504,9 @@ static void snd_mixer_oss_get_volume1_vol(snd_mixer_oss_file_t *fmixer,
snd_runtime_check(!kctl->info(kctl, &uinfo), return);
snd_runtime_check(!kctl->get(kctl, &uctl), return);
snd_runtime_check(uinfo.type != SNDRV_CTL_ELEM_TYPE_BOOLEAN || uinfo.value.integer.min != 0 || uinfo.value.integer.max != 1, return);
*left = snd_mixer_oss_conv1(uctl.value.integer.value[0], uinfo.value.integer.min, uinfo.value.integer.max, &fmixer->volume[pslot->number][0]);
*left = snd_mixer_oss_conv1(uctl.value.integer.value[0], uinfo.value.integer.min, uinfo.value.integer.max, &pslot->volume[0]);
if (uinfo.count > 1)
*right = snd_mixer_oss_conv1(uctl.value.integer.value[1], uinfo.value.integer.min, uinfo.value.integer.max, &fmixer->volume[pslot->number][1]);
*right = snd_mixer_oss_conv1(uctl.value.integer.value[1], uinfo.value.integer.min, uinfo.value.integer.max, &pslot->volume[1]);
}
static void snd_mixer_oss_get_volume1_sw(snd_mixer_oss_file_t *fmixer,
......
......@@ -541,12 +541,10 @@ static int snd_pcm_substream_proc_done(snd_pcm_substream_t *substream)
return 0;
}
static int snd_pcm_new_stream(snd_pcm_t *pcm,
snd_pcm_str_t *pstr,
int substream_count,
int stream)
int snd_pcm_new_stream(snd_pcm_t *pcm, int stream, int substream_count)
{
int idx, err;
snd_pcm_str_t *pstr = &pcm->streams[stream];
snd_pcm_substream_t *substream, *prev;
#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
......@@ -614,11 +612,11 @@ int snd_pcm_new(snd_card_t * card, char *id, int device,
if (id) {
strncpy(pcm->id, id, sizeof(pcm->id) - 1);
}
if ((err = snd_pcm_new_stream(pcm, &pcm->streams[SNDRV_PCM_STREAM_PLAYBACK], playback_count, SNDRV_PCM_STREAM_PLAYBACK)) < 0) {
if ((err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_PLAYBACK, playback_count)) < 0) {
snd_pcm_free(pcm);
return err;
}
if ((err = snd_pcm_new_stream(pcm, &pcm->streams[SNDRV_PCM_STREAM_CAPTURE], capture_count, SNDRV_PCM_STREAM_CAPTURE)) < 0) {
if ((err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_CAPTURE, capture_count)) < 0) {
snd_pcm_free(pcm);
return err;
}
......@@ -974,6 +972,7 @@ module_exit(alsa_pcm_exit)
EXPORT_SYMBOL(snd_pcm_lock);
EXPORT_SYMBOL(snd_pcm_devices);
EXPORT_SYMBOL(snd_pcm_new);
EXPORT_SYMBOL(snd_pcm_new_stream);
EXPORT_SYMBOL(snd_pcm_notify);
EXPORT_SYMBOL(snd_pcm_open_substream);
EXPORT_SYMBOL(snd_pcm_release_substream);
......
......@@ -110,6 +110,7 @@ int snd_pcm_sgbuf_alloc(snd_pcm_substream_t *substream, size_t size)
if (pages < sgbuf->pages) {
/* release unsed pages */
sgbuf_shrink(sgbuf, pages);
if (substream->runtime)
substream->runtime->dma_bytes = size;
return 1; /* changed */
} else if (pages > sgbuf->tblsize) {
......@@ -136,6 +137,7 @@ int snd_pcm_sgbuf_alloc(snd_pcm_substream_t *substream, size_t size)
changed = 1;
}
sgbuf->size = size;
if (substream->runtime)
substream->runtime->dma_bytes = size;
return changed;
}
......
......@@ -59,3 +59,49 @@ void snd_wrapper_vfree(void *obj)
vfree(obj);
}
#endif
/* check the condition in <sound/core.h> !! */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 0)
#if defined(__i386__) || defined(__ppc__) || defined(__x86_64__)
#include <linux/pci.h>
/*
* A dirty hack... when the kernel code is fixed this should be removed.
*
* since pci_alloc_consistent always tries GFP_DMA when the requested
* pci memory region is below 32bit, it happens quite often that even
* 2 order of pages cannot be allocated.
*
* so in the following, we allocate at first without dma_mask, so that
* allocation will be done without GFP_DMA. if the area doesn't match
* with the requested region, then realloate with the original dma_mask
* again.
*/
void *snd_pci_hack_alloc_consistent(struct pci_dev *hwdev, size_t size,
dma_addr_t *dma_handle)
{
void *ret;
u64 dma_mask;
unsigned long rmask;
if (hwdev == NULL)
return pci_alloc_consistent(hwdev, size, dma_handle);
dma_mask = hwdev->dma_mask;
rmask = ~((unsigned long)dma_mask);
hwdev->dma_mask = 0xffffffff; /* do without masking */
ret = pci_alloc_consistent(hwdev, size, dma_handle);
if (ret && ((*dma_handle + size - 1) & rmask)) {
pci_free_consistent(hwdev, size, ret, *dma_handle);
ret = 0;
}
hwdev->dma_mask = dma_mask; /* restore */
if (! ret)
ret = pci_alloc_consistent(hwdev, size, dma_handle);
return ret;
}
#endif
#endif
......@@ -46,6 +46,11 @@
#include <sound/driver.h>
#include <linux/init.h>
#include <linux/slab.h>
#ifndef LINUX_ISAPNP_H
#include <linux/isapnp.h>
#define isapnp_card pci_bus
#define isapnp_dev pci_dev
#endif
#include <sound/core.h>
#include <sound/ad1848.h>
#include <sound/sb.h>
......
......@@ -257,6 +257,7 @@ MODULE_PARM_SYNTAX(snd_fm_port, SNDRV_ENABLED ",allows:{{-1},{0x388},{0x3c8},{0x
#define CM_REG_MISC 0x27
#define CM_XGPO1 0x20
// #define CM_XGPBIO 0x04
#define CM_MIC_CENTER_LFE 0x04 /* mic as center/lfe out? (model 039 or later?) */
#define CM_SPDIF_INVERSE 0x04 /* spdif input phase inverse (model 037) */
#define CM_SPDVALID 0x02 /* spdif input valid check */
#define CM_DMAUTO 0x01
......@@ -2257,6 +2258,7 @@ static snd_kcontrol_new_t snd_cmipci_extra_mixer_switches[] __devinitdata = {
DEFINE_MIXER_SWITCH("Line-In As Bass", line_bass),
DEFINE_MIXER_SWITCH("IEC958 In Select", spdif_in_sel2),
DEFINE_MIXER_SWITCH("IEC958 In Phase Inverse", spdi_phase2),
DEFINE_MIXER_SWITCH("Mic As Center/LFE", spdi_phase), /* same bit as spdi_phase */
};
/* card control switches */
......
......@@ -692,20 +692,20 @@ static int snd_emu10k1_fx8010_playback_copy(snd_pcm_substream_t *substream,
static snd_pcm_hardware_t snd_emu10k1_fx8010_playback =
{
info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
/* SNDRV_PCM_INFO_MMAP_VALID | */ SNDRV_PCM_INFO_PAUSE),
formats: SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
rates: SNDRV_PCM_RATE_48000,
rate_min: 48000,
rate_max: 48000,
channels_min: 1,
channels_max: 1,
buffer_bytes_max: (128*1024),
period_bytes_min: 1024,
period_bytes_max: (128*1024),
periods_min: 1,
periods_max: 1024,
fifo_size: 0,
.formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
.rates = SNDRV_PCM_RATE_48000,
.rate_min = 48000,
.rate_max = 48000,
.channels_min = 1,
.channels_max = 1,
.buffer_bytes_max = (128*1024),
.period_bytes_min = 1024,
.period_bytes_max = (128*1024),
.periods_min = 1,
.periods_max = 1024,
.fifo_size = 0,
};
static int snd_emu10k1_fx8010_playback_open(snd_pcm_substream_t * substream)
......
......@@ -1363,45 +1363,45 @@ static snd_pcm_uframes_t snd_es1968_pcm_pointer(snd_pcm_substream_t *substream)
}
static snd_pcm_hardware_t snd_es1968_playback = {
info: (SNDRV_PCM_INFO_MMAP |
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
/*SNDRV_PCM_INFO_PAUSE |*/
SNDRV_PCM_INFO_RESUME),
formats: SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
rates: SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
rate_min: 4000,
rate_max: 48000,
channels_min: 1,
channels_max: 2,
buffer_bytes_max: 65536,
period_bytes_min: 256,
period_bytes_max: 65536,
periods_min: 1,
periods_max: 1024,
fifo_size: 0,
.formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
.rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
.rate_min = 4000,
.rate_max = 48000,
.channels_min = 1,
.channels_max = 2,
.buffer_bytes_max = 65536,
.period_bytes_min = 256,
.period_bytes_max = 65536,
.periods_min = 1,
.periods_max = 1024,
.fifo_size = 0,
};
static snd_pcm_hardware_t snd_es1968_capture = {
info: (SNDRV_PCM_INFO_NONINTERLEAVED |
.info = (SNDRV_PCM_INFO_NONINTERLEAVED |
SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
/*SNDRV_PCM_INFO_PAUSE |*/
SNDRV_PCM_INFO_RESUME),
formats: /*SNDRV_PCM_FMTBIT_U8 |*/ SNDRV_PCM_FMTBIT_S16_LE,
rates: SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
rate_min: 4000,
rate_max: 48000,
channels_min: 1,
channels_max: 2,
buffer_bytes_max: 65536,
period_bytes_min: 256,
period_bytes_max: 65536,
periods_min: 1,
periods_max: 1024,
fifo_size: 0,
.formats = /*SNDRV_PCM_FMTBIT_U8 |*/ SNDRV_PCM_FMTBIT_S16_LE,
.rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
.rate_min = 4000,
.rate_max = 48000,
.channels_min = 1,
.channels_max = 2,
.buffer_bytes_max = 65536,
.period_bytes_min = 256,
.period_bytes_max = 65536,
.periods_min = 1,
.periods_max = 1024,
.fifo_size = 0,
};
/* *************************
......
......@@ -1186,7 +1186,7 @@ static void snd_ice1712_set_pro_rate(ice1712_t *ice, snd_pcm_substream_t *substr
{
unsigned long flags;
unsigned int rate;
unsigned char val, tmp;
unsigned char val, tmp, tmp2;
spin_lock_irqsave(&ice->reg_lock, flags);
if (inb(ICEMT(ice, PLAYBACK_CONTROL)) & (ICE1712_CAPTURE_START_SHADOW|
......@@ -1224,17 +1224,25 @@ static void snd_ice1712_set_pro_rate(ice1712_t *ice, snd_pcm_substream_t *substr
case ICE1712_SUBDEVICE_DELTA44:
case ICE1712_SUBDEVICE_AUDIOPHILE:
spin_unlock_irqrestore(&ice->reg_lock, flags);
down(&ice->gpio_mutex);
tmp = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA);
up(&ice->gpio_mutex);
tmp2 = tmp;
tmp2 &= ~ICE1712_DELTA_DFS;
if (val == 15 || val == 11 || val == 7)
tmp2 |= ICE1712_DELTA_DFS;
if (tmp != tmp2) {
snd_ice1712_ak4524_reset(ice, 1);
down(&ice->gpio_mutex);
tmp = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA);
if (val == 15 || val == 11 || val == 7) {
if (val == 15 || val == 11 || val == 7)
tmp |= ICE1712_DELTA_DFS;
} else {
else
tmp &= ~ICE1712_DELTA_DFS;
}
snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
up(&ice->gpio_mutex);
snd_ice1712_ak4524_reset(ice, 0);
}
return;
}
__end:
......
......@@ -2147,7 +2147,8 @@ static int __devinit snd_intel8x0_create(snd_card_t * card,
chip->pci = pci;
chip->irq = -1;
snd_intel8x0_proc_init(chip);
if (pci_resource_flags(pci, 2) & IORESOURCE_MEM) { /* ICH4 and higher */
if (chip->device_type == DEVICE_INTEL_ICH4 &&
(pci_resource_flags(pci, 2) & IORESOURCE_MEM)) { /* ICH4 and higher */
chip->mmio = chip->bm_mmio = 1;
chip->addr = pci_resource_start(pci, 2);
sprintf(chip->ac97_name, "%s - AC'97", card->shortname);
......
......@@ -779,6 +779,7 @@ typedef struct snd_m3 m3_t;
/* quirk lists */
struct m3_quirk {
const char *name; /* device name */
u16 vendor, device; /* subsystem ids */
int amp_gpio; /* gpio pin # for external amp, -1 = default */
int irda_workaround; /* non-zero if avoid to touch 0x10 on GPIO_DIRECTION
......@@ -918,23 +919,33 @@ MODULE_DEVICE_TABLE(pci, snd_m3_ids);
static struct m3_quirk m3_quirk_list[] = {
/* panasonic CF-28 "toughbook" */
{
vendor: 0x10f7,
device: 0x833e,
amp_gpio: 0x0d,
.name = "Panasonic CF-28",
.vendor = 0x10f7,
.device = 0x833e,
.amp_gpio = 0x0d,
},
/* panasonic CF-72 "toughbook" */
{
.name = "Panasonic CF-72",
.vendor = 0x10f7,
.device = 0x833d,
.amp_gpio = 0x0d,
},
/* Dell Inspiron 4000 */
{
vendor: 0x1028,
device: 0x00b0,
amp_gpio: -1,
irda_workaround: 1,
.name = "Dell Inspiron 4000",
.vendor = 0x1028,
.device = 0x00b0,
.amp_gpio = -1,
.irda_workaround = 1,
},
/* Dell Inspiron 8000 */
{
vendor: 0x1028,
device: 0x00a4,
amp_gpio: -1,
irda_workaround: 1,
.name = "Dell Insprion 8000",
.vendor = 0x1028,
.device = 0x00a4,
.amp_gpio = -1,
.irda_workaround = 1,
},
/* FIXME: Inspiron 8100 and 8200 ids should be here, too */
/* END */
......@@ -1581,44 +1592,44 @@ snd_m3_interrupt(int irq, void *dev_id, struct pt_regs *regs)
static snd_pcm_hardware_t snd_m3_playback =
{
info: (SNDRV_PCM_INFO_MMAP |
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
/*SNDRV_PCM_INFO_PAUSE |*/
SNDRV_PCM_INFO_RESUME),
formats: SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
rates: SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
rate_min: 8000,
rate_max: 48000,
channels_min: 1,
channels_max: 2,
buffer_bytes_max: (512*1024),
period_bytes_min: 64,
period_bytes_max: (512*1024),
periods_min: 1,
periods_max: 1024,
.formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
.rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
.rate_min = 8000,
.rate_max = 48000,
.channels_min = 1,
.channels_max = 2,
.buffer_bytes_max = (512*1024),
.period_bytes_min = 64,
.period_bytes_max = (512*1024),
.periods_min = 1,
.periods_max = 1024,
};
static snd_pcm_hardware_t snd_m3_capture =
{
info: (SNDRV_PCM_INFO_MMAP |
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
/*SNDRV_PCM_INFO_PAUSE |*/
SNDRV_PCM_INFO_RESUME),
formats: SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
rates: SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
rate_min: 8000,
rate_max: 48000,
channels_min: 1,
channels_max: 2,
buffer_bytes_max: (512*1024),
period_bytes_min: 64,
period_bytes_max: (512*1024),
periods_min: 1,
periods_max: 1024,
.formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
.rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
.rate_min = 8000,
.rate_max = 48000,
.channels_min = 1,
.channels_max = 2,
.buffer_bytes_max = (512*1024),
.period_bytes_min = 64,
.period_bytes_max = (512*1024),
.periods_min = 1,
.periods_max = 1024,
};
......@@ -2547,6 +2558,7 @@ snd_m3_create(snd_card_t *card, struct pci_dev *pci,
for (quirk = m3_quirk_list; quirk->vendor; quirk++) {
if (subsystem_vendor == quirk->vendor &&
subsystem_device == quirk->device) {
printk(KERN_INFO "maestro3: enabled hack for '%s'\n", quirk->name);
chip->quirk = quirk;
break;
}
......
......@@ -751,46 +751,46 @@ snd_nm256_capture_update(nm256_t *chip)
*/
static snd_pcm_hardware_t snd_nm256_playback =
{
info:
.info =
#ifdef __i386__
SNDRV_PCM_INFO_MMAP|SNDRV_PCM_INFO_MMAP_VALID|
#endif
SNDRV_PCM_INFO_INTERLEAVED |
/*SNDRV_PCM_INFO_PAUSE |*/
SNDRV_PCM_INFO_RESUME,
formats: SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
rates: SNDRV_PCM_RATE_KNOT/*24k*/ | SNDRV_PCM_RATE_8000_48000,
rate_min: 8000,
rate_max: 48000,
channels_min: 1,
channels_max: 2,
periods_min: 2,
periods_max: 1024,
buffer_bytes_max: 128 * 1024,
period_bytes_min: 256,
period_bytes_max: 128 * 1024,
.formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
.rates = SNDRV_PCM_RATE_KNOT/*24k*/ | SNDRV_PCM_RATE_8000_48000,
.rate_min = 8000,
.rate_max = 48000,
.channels_min = 1,
.channels_max = 2,
.periods_min = 2,
.periods_max = 1024,
.buffer_bytes_max = 128 * 1024,
.period_bytes_min = 256,
.period_bytes_max = 128 * 1024,
};
static snd_pcm_hardware_t snd_nm256_capture =
{
info:
.info =
#ifdef __i386__
SNDRV_PCM_INFO_MMAP|SNDRV_PCM_INFO_MMAP_VALID|
#endif
SNDRV_PCM_INFO_INTERLEAVED |
/*SNDRV_PCM_INFO_PAUSE |*/
SNDRV_PCM_INFO_RESUME,
formats: SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
rates: SNDRV_PCM_RATE_KNOT/*24k*/ | SNDRV_PCM_RATE_8000_48000,
rate_min: 8000,
rate_max: 48000,
channels_min: 1,
channels_max: 2,
periods_min: 2,
periods_max: 1024,
buffer_bytes_max: 128 * 1024,
period_bytes_min: 256,
period_bytes_max: 128 * 1024,
.formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
.rates = SNDRV_PCM_RATE_KNOT/*24k*/ | SNDRV_PCM_RATE_8000_48000,
.rate_min = 8000,
.rate_max = 48000,
.channels_min = 1,
.channels_max = 2,
.periods_min = 2,
.periods_max = 1024,
.buffer_bytes_max = 128 * 1024,
.period_bytes_min = 256,
.period_bytes_max = 128 * 1024,
};
......
......@@ -25,7 +25,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
$Id: hammerfall_mem.c,v 1.2 2002/06/19 08:52:11 perex Exp $
$Id: hammerfall_mem.c,v 1.3 2002/09/12 09:03:28 tiwai Exp $
Tue Oct 17 2000 Jaroslav Kysela <perex@suse.cz>
......@@ -93,11 +93,6 @@ static hammerfall_buf_t hammerfall_buffers[NBUFS];
undesirable.
*/
/* remove hack for pci_alloc_consistent to avoid dependecy on snd module */
#ifdef HACK_PCI_ALLOC_CONSISTENT
#undef pci_alloc_consistent
#endif
static void *hammerfall_malloc_pages(struct pci_dev *pci,
unsigned long size,
dma_addr_t *dmaaddr)
......
......@@ -977,7 +977,8 @@ static int __devinit snd_via82xx_chip_init(via82xx_t *chip)
/* disable all legacy ports */
pci_write_config_byte(chip->pci, 0x42, 0);
#endif
pci_read_config_byte(chip->pci, 0x40, &pval);
if (! (pval & 0x01)) { /* codec not ready? */
/* deassert ACLink reset, force SYNC */
pci_write_config_byte(chip->pci, 0x41, 0xe0);
udelay(100);
......@@ -991,6 +992,7 @@ static int __devinit snd_via82xx_chip_init(via82xx_t *chip)
/* note - FM data out has trouble with non VRA codecs !! */
pci_write_config_byte(chip->pci, 0x41, 0xcc);
udelay(100);
}
/* Make sure VRA is enabled, in case we didn't do a
* complete codec reset, above */
......
......@@ -105,6 +105,7 @@ struct audioformat {
struct list_head list;
snd_pcm_format_t format; /* format type */
int channels; /* # channels */
int iface; /* interface number */
unsigned char altsetting; /* corresponding alternate setting */
unsigned char altset_idx; /* array index of altenate setting */
unsigned char attributes; /* corresponding attributes of cs endpoint */
......@@ -136,7 +137,9 @@ struct snd_usb_substream {
snd_usb_stream_t *stream;
struct usb_device *dev;
snd_pcm_substream_t *pcm_substream;
int interface; /* Interface number, -1 means not used */
int direction; /* playback or capture */
int interface; /* current interface */
int endpoint; /* assigned endpoint */
unsigned int format; /* USB data format */
unsigned int datapipe; /* the data i/o pipe */
unsigned int syncpipe; /* 1 - async out or adaptive in */
......@@ -491,6 +494,23 @@ static int retire_playback_urb(snd_usb_substream_t *subs,
}
/*
*/
static struct snd_urb_ops audio_urb_ops[2] = {
{
.prepare = prepare_playback_urb,
.retire = retire_playback_urb,
.prepare_sync = prepare_playback_sync_urb,
.retire_sync = retire_playback_sync_urb,
},
{
.prepare = prepare_capture_urb,
.retire = retire_capture_urb,
.prepare_sync = prepare_capture_sync_urb,
.retire_sync = retire_capture_sync_urb,
},
};
/*
* complete callback from data urb
*/
......@@ -738,7 +758,7 @@ static void release_substream_urbs(snd_usb_substream_t *subs)
static int init_substream_urbs(snd_usb_substream_t *subs, snd_pcm_runtime_t *runtime)
{
int maxsize, n, i;
int is_playback = subs->pcm_substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
int is_playback = subs->direction == SNDRV_PCM_STREAM_PLAYBACK;
int npacks[MAX_URBS], total_packs;
/* calculate the frequency in 10.14 format */
......@@ -907,7 +927,7 @@ static int set_format(snd_usb_substream_t *subs, snd_pcm_runtime_t *runtime)
struct audioformat *fmt;
unsigned int ep, attr;
unsigned char data[3];
int is_playback = subs->pcm_substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
int is_playback = subs->direction == SNDRV_PCM_STREAM_PLAYBACK;
int err;
fmt = find_format(subs, runtime);
......@@ -917,7 +937,7 @@ static int set_format(snd_usb_substream_t *subs, snd_pcm_runtime_t *runtime)
return -EINVAL;
}
iface = &config->interface[subs->interface];
iface = &config->interface[fmt->iface];
alts = &iface->altsetting[fmt->altset_idx];
snd_assert(alts->bAlternateSetting == fmt->altsetting, return -EINVAL);
......@@ -927,6 +947,7 @@ static int set_format(snd_usb_substream_t *subs, snd_pcm_runtime_t *runtime)
subs->datapipe = usb_sndisocpipe(dev, ep);
else
subs->datapipe = usb_rcvisocpipe(dev, ep);
subs->interface = fmt->iface;
subs->format = fmt->altset_idx;
subs->syncpipe = subs->syncinterval = 0;
subs->maxpacksize = alts->endpoint[0].wMaxPacketSize;
......@@ -1126,6 +1147,7 @@ static int snd_usb_pcm_open(snd_pcm_substream_t *substream, int direction,
snd_pcm_runtime_t *runtime = substream->runtime;
snd_usb_substream_t *subs = &as->substream[direction];
subs->interface = -1;
runtime->hw = *hw;
runtime->private_data = subs;
subs->pcm_substream = substream;
......@@ -1139,6 +1161,7 @@ static int snd_usb_pcm_close(snd_pcm_substream_t *substream, int direction)
snd_usb_stream_t *as = snd_pcm_substream_chip(substream);
snd_usb_substream_t *subs = &as->substream[direction];
release_substream_urbs(subs);
if (subs->interface >= 0)
usb_set_interface(subs->dev, subs->interface, 0);
subs->pcm_substream = NULL;
return 0;
......@@ -1285,290 +1308,6 @@ static struct usb_driver usb_audio_driver = {
};
/*
* intialize the substream instance.
*/
static void init_substream(snd_usb_stream_t *stream, snd_usb_substream_t *subs,
int iface_no, int is_input,
unsigned char *buffer, int buflen)
{
struct usb_device *dev;
struct usb_config_descriptor *config;
struct usb_interface *iface;
struct usb_interface_descriptor *alts;
int i, pcm_format, altno;
int format, channels, format_type, nr_rates;
struct audioformat *fp;
unsigned char *fmt, *csep;
dev = stream->chip->dev;
config = dev->actconfig;
subs->stream = stream;
subs->dev = dev;
subs->interface = iface_no;
INIT_LIST_HEAD(&subs->fmt_list);
spin_lock_init(&subs->lock);
if (is_input) {
subs->ops.prepare = prepare_capture_urb;
subs->ops.retire = retire_capture_urb;
subs->ops.prepare_sync = prepare_capture_sync_urb;
subs->ops.retire_sync = retire_capture_sync_urb;
} else {
subs->ops.prepare = prepare_playback_urb;
subs->ops.retire = retire_playback_urb;
subs->ops.prepare_sync = prepare_playback_sync_urb;
subs->ops.retire_sync = retire_playback_sync_urb;
}
if (iface_no < 0)
return;
/* parse the interface's altsettings */
iface = &config->interface[iface_no];
for (i = 0; i < iface->num_altsetting; i++) {
alts = &iface->altsetting[i];
/* skip invalid one */
if (alts->bInterfaceClass != USB_CLASS_AUDIO ||
alts->bInterfaceSubClass != USB_SUBCLASS_AUDIO_STREAMING ||
alts->bNumEndpoints < 1)
continue;
/* must be isochronous */
if ((alts->endpoint[0].bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) !=
USB_ENDPOINT_XFER_ISOC)
continue;
/* check direction */
if (alts->endpoint[0].bEndpointAddress & USB_DIR_IN) {
if (! is_input)
continue;
} else {
if (is_input)
continue;
}
altno = alts->bAlternateSetting;
/* get audio formats */
fmt = snd_usb_find_csint_desc(buffer, buflen, NULL, AS_GENERAL, iface_no, altno);
if (!fmt) {
snd_printk(KERN_ERR "%d:%u:%d : AS_GENERAL descriptor not found\n",
dev->devnum, iface_no, altno);
continue;
}
if (fmt[0] < 7) {
snd_printk(KERN_ERR "%d:%u:%d : invalid AS_GENERAL desc\n",
dev->devnum, iface_no, altno);
continue;
}
format = (fmt[6] << 8) | fmt[5]; /* remember the format value */
/* get format type */
fmt = snd_usb_find_csint_desc(buffer, buflen, NULL, FORMAT_TYPE, iface_no, altno);
if (!fmt) {
snd_printk(KERN_ERR "%d:%u:%d : no FORMAT_TYPE desc\n",
dev->devnum, iface_no, altno);
continue;
}
if (fmt[0] < 8) {
snd_printk(KERN_ERR "%d:%u:%d : invalid FORMAT_TYPE desc\n",
dev->devnum, iface_no, altno);
continue;
}
format_type = fmt[3];
/* FIXME: needed support for TYPE II and III */
if (format_type != USB_FORMAT_TYPE_I) {
snd_printd(KERN_INFO "%d:%u:%d : format type %d is not supported yet\n",
dev->devnum, iface_no, altno, format_type);
continue;
}
nr_rates = fmt[7];
if (fmt[0] < 8 + 3 * (nr_rates ? nr_rates : 2)) {
snd_printk(KERN_ERR "%d:%u:%d : invalid FORMAT_TYPE desc\n",
dev->devnum, iface_no, altno);
continue;
}
/* FIXME: correct endianess and sign? */
pcm_format = -1;
switch (format) {
case USB_AUDIO_FORMAT_PCM:
/* check the format byte size */
switch (fmt[6]) {
case 8:
subs->formats |= SNDRV_PCM_FMTBIT_U8;
pcm_format = SNDRV_PCM_FORMAT_U8;
break;
case 16:
subs->formats |= SNDRV_PCM_FMTBIT_S16_LE;
pcm_format = SNDRV_PCM_FORMAT_S16_LE;
break;
case 18:
case 20:
if (fmt[5] == 3) {
subs->formats |= SNDRV_PCM_FMTBIT_S24_3LE;
pcm_format = SNDRV_PCM_FORMAT_S24_3LE;
} else {
snd_printk(KERN_ERR "%d:%u:%d : non-supported sample bit %d in %d bytes\n",
dev->devnum, iface_no, altno, fmt[6], fmt[5]);
}
break;
case 24:
if (fmt[5] == 4) {
/* FIXME: correct? or S32_LE? */
subs->formats |= SNDRV_PCM_FMTBIT_S24_LE;
pcm_format = SNDRV_PCM_FORMAT_S24_LE;
} else if (fmt[5] == 3) {
subs->formats |= SNDRV_PCM_FMTBIT_S24_3LE;
pcm_format = SNDRV_PCM_FORMAT_S24_3LE;
} else {
snd_printk(KERN_ERR "%d:%u:%d : non-supported sample bit %d in %d bytes\n",
dev->devnum, iface_no, altno, format, fmt[5]);
}
break;
case 32:
subs->formats |= SNDRV_PCM_FMTBIT_S32_LE;
pcm_format = SNDRV_PCM_FORMAT_S32_LE;
break;
default:
snd_printk(KERN_INFO "%d:%u:%d : unsupported sample bitwidth %d in %d bytes\n",
dev->devnum, iface_no, altno, fmt[6], fmt[5]);
break;
}
break;
case USB_AUDIO_FORMAT_PCM8:
/* Dallas DS4201 workaround */
if (dev->descriptor.idVendor == 0x04fa && dev->descriptor.idProduct == 0x4201) {
subs->formats |= ~SNDRV_PCM_FMTBIT_S8;
pcm_format = SNDRV_PCM_FORMAT_S8;
} else {
subs->formats |= SNDRV_PCM_FMTBIT_U8;
pcm_format = SNDRV_PCM_FORMAT_U8;
}
break;
case USB_AUDIO_FORMAT_IEEE_FLOAT:
subs->formats |= SNDRV_PCM_FMTBIT_FLOAT_LE;
pcm_format = SNDRV_PCM_FORMAT_FLOAT_LE;
break;
case USB_AUDIO_FORMAT_ALAW:
subs->formats |= SNDRV_PCM_FMTBIT_A_LAW;
pcm_format = SNDRV_PCM_FORMAT_A_LAW;
break;
case USB_AUDIO_FORMAT_MU_LAW:
subs->formats |= SNDRV_PCM_FMTBIT_MU_LAW;
pcm_format = SNDRV_PCM_FORMAT_MU_LAW;
break;
default:
snd_printk(KERN_INFO "%d:%u:%d : unsupported format type %d\n",
dev->devnum, iface_no, altno, format);
break;
}
if (pcm_format < 0)
continue;
channels = fmt[4];
if (channels < 1) {
snd_printk(KERN_ERR "%d:%u:%d : invalid channels %d\n",
dev->devnum, iface_no, altno, channels);
continue;
}
csep = snd_usb_find_desc(buffer, buflen, NULL, USB_DT_CS_ENDPOINT, iface_no, altno);
if (!csep || csep[0] < 7 || csep[2] != EP_GENERAL) {
snd_printk(KERN_ERR "%d:%u:%d : no or invalid class specific endpoint descriptor\n",
dev->devnum, iface_no, altno);
continue;
}
fp = kmalloc(sizeof(*fp), GFP_KERNEL);
if (! fp) {
snd_printk(KERN_ERR "cannot malloc\n");
break;
}
memset(fp, 0, sizeof(*fp));
fp->format = pcm_format;
fp->altsetting = altno;
fp->altset_idx = i;
fp->endpoint = alts->endpoint[0].bEndpointAddress;
fp->ep_attr = alts->endpoint[0].bmAttributes;
fp->channels = channels;
fp->attributes = csep[3];
if (nr_rates) {
/*
* build the rate table and bitmap flags
*/
int r, idx, c;
/* this table corresponds to the SNDRV_PCM_RATE_XXX bit */
static int conv_rates[] = {
5512, 8000, 11025, 16000, 22050, 32000, 44100, 48000,
64000, 88200, 96000, 176400, 192000
};
fp->rate_table = kmalloc(sizeof(int) * nr_rates, GFP_KERNEL);
if (fp->rate_table == NULL) {
snd_printk(KERN_ERR "cannot malloc\n");
kfree(fp);
break;
}
fp->nr_rates = nr_rates;
fp->rate_min = fp->rate_max = combine_triple(&fmt[8]);
for (r = 0, idx = 8; r < nr_rates; r++, idx += 3) {
int rate = fp->rate_table[r] = combine_triple(&fmt[idx]);
if (rate < fp->rate_min)
fp->rate_min = rate;
else if (rate > fp->rate_max)
fp->rate_max = rate;
for (c = 0; c < 13; c++) {
if (rate == conv_rates[c]) {
fp->rates |= (1 << c);
break;
}
}
#if 0 // FIXME - we need to define constraint
if (c >= 13)
fp->rates |= SNDRV_PCM_KNOT; /* unconventional rate */
#endif
}
} else {
/* continuous rates */
fp->rates = SNDRV_PCM_RATE_CONTINUOUS;
fp->rate_min = combine_triple(&fmt[8]);
fp->rate_max = combine_triple(&fmt[11]);
}
list_add_tail(&fp->list, &subs->fmt_list);
subs->num_formats++;
}
}
/*
* free a substream
*/
static void free_substream(snd_usb_substream_t *subs)
{
struct list_head *p, *n;
if (subs->interface < 0)
return;
list_for_each_safe(p, n, &subs->fmt_list) {
struct audioformat *fp = list_entry(p, struct audioformat, list);
if (fp->rate_table)
kfree(fp->rate_table);
kfree(fp);
}
}
/*
* proc interface for list the supported pcm formats
*/
......@@ -1582,6 +1321,7 @@ static void proc_dump_substream_formats(snd_usb_substream_t *subs, snd_info_buff
list_for_each(p, &subs->fmt_list) {
struct audioformat *fp;
fp = list_entry(p, struct audioformat, list);
snd_iprintf(buffer, " Interface %d\n", fp->iface);
snd_iprintf(buffer, " Altset %d\n", fp->altset_idx);
snd_iprintf(buffer, " Format: %s\n", snd_pcm_format_name(fp->format));
snd_iprintf(buffer, " Channels: %d\n", fp->channels);
......@@ -1610,6 +1350,7 @@ static void proc_dump_substream_status(snd_usb_substream_t *subs, snd_info_buffe
if (subs->running) {
int i;
snd_iprintf(buffer, " Status: Running\n");
snd_iprintf(buffer, " Interface = %d\n", subs->interface);
snd_iprintf(buffer, " Altset = %d\n", subs->format);
snd_iprintf(buffer, " URBs = %d [ ", subs->nurbs);
for (i = 0; i < subs->nurbs; i++)
......@@ -1665,12 +1406,58 @@ static void proc_pcm_format_add(snd_usb_stream_t *stream)
/*
* free a usb stream instance
* intialize the substream instance.
*/
static void snd_usb_audio_stream_free(snd_usb_stream_t *stream)
static void init_substream(snd_usb_stream_t *as, int stream, struct audioformat *fp)
{
if (stream->proc_entry) {
snd_info_unregister(stream->proc_entry);
snd_usb_substream_t *subs = &as->substream[stream];
INIT_LIST_HEAD(&subs->fmt_list);
spin_lock_init(&subs->lock);
subs->stream = as;
subs->direction = stream;
subs->dev = as->chip->dev;
subs->ops = audio_urb_ops[stream];
snd_pcm_lib_preallocate_pages(as->pcm->streams[stream].substream,
64 * 1024, 128 * 1024, GFP_ATOMIC);
snd_pcm_set_ops(as->pcm, stream,
stream == SNDRV_PCM_STREAM_PLAYBACK ?
&snd_usb_playback_ops : &snd_usb_capture_ops);
list_add_tail(&fp->list, &subs->fmt_list);
subs->formats |= 1ULL << fp->format;
subs->endpoint = fp->endpoint;
subs->num_formats++;
}
/*
* free a substream
*/
static void free_substream(snd_usb_substream_t *subs)
{
struct list_head *p, *n;
if (! subs->num_formats)
return; /* not initialized */
list_for_each_safe(p, n, &subs->fmt_list) {
struct audioformat *fp = list_entry(p, struct audioformat, list);
if (fp->rate_table)
kfree(fp->rate_table);
kfree(fp);
}
}
/*
* free a usb stream instance
*/
static void snd_usb_audio_stream_free(snd_usb_stream_t *stream)
{
if (stream->proc_entry) {
snd_info_unregister(stream->proc_entry);
stream->proc_entry = NULL;
}
free_substream(&stream->substream[0]);
......@@ -1689,57 +1476,71 @@ static void snd_usb_audio_pcm_free(snd_pcm_t *pcm)
}
}
static int snd_usb_audio_stream_new(snd_usb_audio_t *chip, unsigned char *buffer, int buflen, int asifin, int asifout)
/*
* add this endpoint to the chip instance.
* if a stream with the same endpoint already exists, append to it.
* if not, create a new pcm stream.
*/
static int add_audio_endpoint(snd_usb_audio_t *chip, int stream, struct audioformat *fp)
{
struct list_head *p;
snd_usb_stream_t *as;
snd_usb_substream_t *subs;
snd_pcm_t *pcm;
char name[32];
int err;
as = snd_magic_kmalloc(snd_usb_stream_t, 0, GFP_KERNEL);
if (as == NULL) {
snd_printk(KERN_ERR "cannot malloc\n");
return -ENOMEM;
list_for_each(p, &chip->pcm_list) {
as = list_entry(p, snd_usb_stream_t, list);
subs = &as->substream[stream];
if (! subs->endpoint)
break;
if (subs->endpoint == fp->endpoint) {
list_add_tail(&fp->list, &subs->fmt_list);
subs->num_formats++;
subs->formats |= 1ULL << fp->format;
return 0;
}
memset(as, 0, sizeof(*as));
as->chip = chip;
INIT_LIST_HEAD(&as->list);
init_substream(as, &as->substream[SNDRV_PCM_STREAM_PLAYBACK], asifout, 0, buffer, buflen);
init_substream(as, &as->substream[SNDRV_PCM_STREAM_CAPTURE], asifin, 1, buffer, buflen);
if (as->substream[0].num_formats == 0 && as->substream[1].num_formats == 0) {
snd_usb_audio_stream_free(as);
}
/* look for an empty stream */
list_for_each(p, &chip->pcm_list) {
as = list_entry(p, snd_usb_stream_t, list);
subs = &as->substream[stream];
if (subs->endpoint)
continue;
err = snd_pcm_new_stream(as->pcm, stream, 1);
if (err < 0)
return err;
init_substream(as, stream, fp);
return 0;
}
if (chip->pcm_devs > 0)
sprintf(name, "USB Audio #%d", chip->pcm_devs);
else
strcpy(name, "USB Audio");
/* create a new pcm */
as = snd_magic_kmalloc(snd_usb_stream_t, 0, GFP_KERNEL);
if (! as)
return -ENOMEM;
memset(as, 0, sizeof(*as));
as->pcm_index = chip->pcm_devs;
as->chip = chip;
err = snd_pcm_new(chip->card, "USB Audio", chip->pcm_devs,
as->substream[SNDRV_PCM_STREAM_PLAYBACK].num_formats ? 1 : 0,
as->substream[SNDRV_PCM_STREAM_CAPTURE].num_formats ? 1 : 0,
stream == SNDRV_PCM_STREAM_PLAYBACK ? 1 : 0,
stream == SNDRV_PCM_STREAM_PLAYBACK ? 0 : 1,
&pcm);
if (err < 0) {
snd_usb_audio_stream_free(as);
snd_magic_kfree(as);
return err;
}
as->pcm = pcm;
as->pcm_index = chip->pcm_devs;
if (as->substream[SNDRV_PCM_STREAM_PLAYBACK].num_formats)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_usb_playback_ops);
if (as->substream[SNDRV_PCM_STREAM_CAPTURE].num_formats)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_usb_capture_ops);
pcm->private_data = as;
pcm->private_free = snd_usb_audio_pcm_free;
pcm->info_flags = 0;
if (chip->pcm_devs > 0)
sprintf(pcm->name, "USB Audio #%d", chip->pcm_devs);
else
strcpy(pcm->name, "USB Audio");
strcpy(pcm->name, name);
init_substream(as, stream, fp);
snd_pcm_lib_preallocate_pages_for_all(pcm, 64*1024, 128*1024, GFP_ATOMIC);
list_add(&as->list, &chip->pcm_list);
chip->pcm_devs++;
......@@ -1749,6 +1550,247 @@ static int snd_usb_audio_stream_new(snd_usb_audio_t *chip, unsigned char *buffer
}
/*
* parse the audio format type descriptor
* and returns the corresponding pcm format
*/
static int parse_audio_format_type(struct usb_device *dev, int iface_no, int altno,
int format, unsigned char *fmt)
{
int format_type = fmt[3];
int pcm_format;
/* FIXME: needed support for TYPE II and III */
if (format_type != USB_FORMAT_TYPE_I) {
snd_printd(KERN_INFO "%d:%u:%d : format type %d is not supported yet\n",
dev->devnum, iface_no, altno, format_type);
return -1;
}
/* FIXME: correct endianess and sign? */
pcm_format = -1;
switch (format) {
case USB_AUDIO_FORMAT_PCM:
/* check the format byte size */
switch (fmt[6]) {
case 8:
pcm_format = SNDRV_PCM_FORMAT_U8;
break;
case 16:
pcm_format = SNDRV_PCM_FORMAT_S16_LE;
break;
case 18:
case 20:
if (fmt[5] == 3)
pcm_format = SNDRV_PCM_FORMAT_S24_3LE;
else
snd_printk(KERN_ERR "%d:%u:%d : non-supported sample bit %d in %d bytes\n",
dev->devnum, iface_no, altno, fmt[6], fmt[5]);
break;
case 24:
if (fmt[5] == 4)
/* FIXME: correct? or S32_LE? */
pcm_format = SNDRV_PCM_FORMAT_S24_LE;
else if (fmt[5] == 3)
pcm_format = SNDRV_PCM_FORMAT_S24_3LE;
else
snd_printk(KERN_ERR "%d:%u:%d : non-supported sample bit %d in %d bytes\n",
dev->devnum, iface_no, altno, format, fmt[5]);
break;
case 32:
pcm_format = SNDRV_PCM_FORMAT_S32_LE;
break;
default:
snd_printk(KERN_INFO "%d:%u:%d : unsupported sample bitwidth %d in %d bytes\n",
dev->devnum, iface_no, altno, fmt[6], fmt[5]);
break;
}
break;
case USB_AUDIO_FORMAT_PCM8:
/* Dallas DS4201 workaround */
if (dev->descriptor.idVendor == 0x04fa && dev->descriptor.idProduct == 0x4201)
pcm_format = SNDRV_PCM_FORMAT_S8;
else
pcm_format = SNDRV_PCM_FORMAT_U8;
break;
case USB_AUDIO_FORMAT_IEEE_FLOAT:
pcm_format = SNDRV_PCM_FORMAT_FLOAT_LE;
break;
case USB_AUDIO_FORMAT_ALAW:
pcm_format = SNDRV_PCM_FORMAT_A_LAW;
break;
case USB_AUDIO_FORMAT_MU_LAW:
pcm_format = SNDRV_PCM_FORMAT_MU_LAW;
break;
default:
snd_printk(KERN_INFO "%d:%u:%d : unsupported format type %d\n",
dev->devnum, iface_no, altno, format);
break;
}
return pcm_format;
}
static int parse_audio_endpoints(snd_usb_audio_t *chip, unsigned char *buffer, int buflen, int iface_no)
{
struct usb_device *dev;
struct usb_config_descriptor *config;
struct usb_interface *iface;
struct usb_interface_descriptor *alts;
int i, altno, err, stream;
int channels, nr_rates, pcm_format, format;
struct audioformat *fp;
unsigned char *fmt, *csep;
dev = chip->dev;
config = dev->actconfig;
/* parse the interface's altsettings */
iface = &config->interface[iface_no];
for (i = 0; i < iface->num_altsetting; i++) {
alts = &iface->altsetting[i];
/* skip invalid one */
if (alts->bInterfaceClass != USB_CLASS_AUDIO ||
alts->bInterfaceSubClass != USB_SUBCLASS_AUDIO_STREAMING ||
alts->bNumEndpoints < 1)
continue;
/* must be isochronous */
if ((alts->endpoint[0].bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) !=
USB_ENDPOINT_XFER_ISOC)
continue;
/* check direction */
stream = (alts->endpoint[0].bEndpointAddress & USB_DIR_IN) ?
SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK;
altno = alts->bAlternateSetting;
/* get audio formats */
fmt = snd_usb_find_csint_desc(buffer, buflen, NULL, AS_GENERAL, iface_no, altno);
if (!fmt) {
snd_printk(KERN_ERR "%d:%u:%d : AS_GENERAL descriptor not found\n",
dev->devnum, iface_no, altno);
continue;
}
if (fmt[0] < 7) {
snd_printk(KERN_ERR "%d:%u:%d : invalid AS_GENERAL desc\n",
dev->devnum, iface_no, altno);
continue;
}
format = (fmt[6] << 8) | fmt[5]; /* remember the format value */
/* get format type */
fmt = snd_usb_find_csint_desc(buffer, buflen, NULL, FORMAT_TYPE, iface_no, altno);
if (!fmt) {
snd_printk(KERN_ERR "%d:%u:%d : no FORMAT_TYPE desc\n",
dev->devnum, iface_no, altno);
continue;
}
if (fmt[0] < 8) {
snd_printk(KERN_ERR "%d:%u:%d : invalid FORMAT_TYPE desc\n",
dev->devnum, iface_no, altno);
continue;
}
pcm_format = parse_audio_format_type(dev, iface_no, altno, format, fmt);
if (pcm_format < 0)
continue;
channels = fmt[4];
if (channels < 1) {
snd_printk(KERN_ERR "%d:%u:%d : invalid channels %d\n",
dev->devnum, iface_no, altno, channels);
continue;
}
nr_rates = fmt[7];
if (fmt[0] < 8 + 3 * (nr_rates ? nr_rates : 2)) {
snd_printk(KERN_ERR "%d:%u:%d : invalid FORMAT_TYPE desc\n",
dev->devnum, iface_no, altno);
continue;
}
csep = snd_usb_find_desc(buffer, buflen, NULL, USB_DT_CS_ENDPOINT, iface_no, altno);
if (!csep || csep[0] < 7 || csep[2] != EP_GENERAL) {
snd_printk(KERN_ERR "%d:%u:%d : no or invalid class specific endpoint descriptor\n",
dev->devnum, iface_no, altno);
continue;
}
fp = kmalloc(sizeof(*fp), GFP_KERNEL);
if (! fp) {
snd_printk(KERN_ERR "cannot malloc\n");
return -ENOMEM;
}
memset(fp, 0, sizeof(*fp));
fp->iface = iface_no;
fp->altsetting = altno;
fp->altset_idx = i;
fp->format = pcm_format;
fp->endpoint = alts->endpoint[0].bEndpointAddress;
fp->ep_attr = alts->endpoint[0].bmAttributes;
fp->channels = channels;
fp->attributes = csep[3];
if (nr_rates) {
/*
* build the rate table and bitmap flags
*/
int r, idx, c;
/* this table corresponds to the SNDRV_PCM_RATE_XXX bit */
static int conv_rates[] = {
5512, 8000, 11025, 16000, 22050, 32000, 44100, 48000,
64000, 88200, 96000, 176400, 192000
};
fp->rate_table = kmalloc(sizeof(int) * nr_rates, GFP_KERNEL);
if (fp->rate_table == NULL) {
snd_printk(KERN_ERR "cannot malloc\n");
kfree(fp);
break;
}
fp->nr_rates = nr_rates;
fp->rate_min = fp->rate_max = combine_triple(&fmt[8]);
for (r = 0, idx = 8; r < nr_rates; r++, idx += 3) {
int rate = fp->rate_table[r] = combine_triple(&fmt[idx]);
if (rate < fp->rate_min)
fp->rate_min = rate;
else if (rate > fp->rate_max)
fp->rate_max = rate;
for (c = 0; c < 13; c++) {
if (rate == conv_rates[c]) {
fp->rates |= (1 << c);
break;
}
}
#if 0 // FIXME - we need to define constraint
if (c >= 13)
fp->rates |= SNDRV_PCM_KNOT; /* unconventional rate */
#endif
}
} else {
/* continuous rates */
fp->rates = SNDRV_PCM_RATE_CONTINUOUS;
fp->rate_min = combine_triple(&fmt[8]);
fp->rate_max = combine_triple(&fmt[11]);
}
snd_printdd(KERN_INFO "%d:%u:%d: add audio endpoint 0x%x\n", dev->devnum, iface_no, i, fp->endpoint);
err = add_audio_endpoint(chip, stream, fp);
if (err < 0) {
if (fp->rate_table)
kfree(fp->rate_table);
kfree(fp);
return err;
}
}
return 0;
}
/*
* parse audio control descriptor and create pcm/midi streams
*/
......@@ -1763,9 +1805,7 @@ static int snd_usb_create_streams(snd_usb_audio_t *chip, int ctrlif,
struct usb_config_descriptor *config;
struct usb_interface *iface;
unsigned char *p1;
unsigned char ifin[USB_MAXINTERFACES], ifout[USB_MAXINTERFACES];
int numifin = 0, numifout = 0;
int i, j, k;
int i, j;
/* find audiocontrol interface */
if (!(p1 = snd_usb_find_csint_desc(buffer, buflen, NULL, HEADER, ctrlif, -1))) {
......@@ -1804,52 +1844,9 @@ static int snd_usb_create_streams(snd_usb_audio_t *chip, int ctrlif,
/* skip non-supported classes */
continue;
}
if (iface->num_altsetting < 2) {
snd_printdd(KERN_ERR "%d:%u:%d: skipping - no valid interface.\n",
dev->devnum, ctrlif, j);
continue;
}
if (iface->altsetting[0].bNumEndpoints > 0) {
/* Check all endpoints; should they all have a bandwidth of 0 ? */
for (k = 0; k < iface->altsetting[0].bNumEndpoints; k++) {
if (iface->altsetting[0].endpoint[k].wMaxPacketSize > 0) {
snd_printk(KERN_ERR "%d:%u:%d ep%d : have no bandwith at alt[0]\n", dev->devnum, ctrlif, j, k);
break;
}
}
if (k < iface->altsetting[0].bNumEndpoints)
continue;
}
if (iface->altsetting[1].bNumEndpoints < 1) {
snd_printk(KERN_ERR "%d:%u:%d : has no endpoint\n",
dev->devnum, ctrlif, j);
continue;
}
/* note: this requires the data endpoint to be ep0 and
* the optional sync ep to be ep1, which seems to be the case
*/
if (iface->altsetting[1].endpoint[0].bEndpointAddress & USB_DIR_IN) {
if (numifin < USB_MAXINTERFACES) {
snd_printdd(KERN_INFO "adding an input interface %d:%u:%d\n", dev->devnum, ctrlif, j);
ifin[numifin++] = j;
parse_audio_endpoints(chip, buffer, buflen, j);
usb_driver_claim_interface(&usb_audio_driver, iface, (void *)-1);
}
} else {
if (numifout < USB_MAXINTERFACES) {
snd_printdd(KERN_INFO "adding an output interface %d:%u:%d\n", dev->devnum, ctrlif, j);
ifout[numifout++] = j;
usb_driver_claim_interface(&usb_audio_driver, iface, (void *)-1);
}
}
}
/* all endpoints are parsed. now create pcm streams */
for (i = 0; i < numifin && i < numifout; i++)
snd_usb_audio_stream_new(chip, buffer, buflen, ifin[i], ifout[i]);
for (j = i; j < numifin; j++)
snd_usb_audio_stream_new(chip, buffer, buflen, ifin[i], -1);
for (j = i; j < numifout; j++)
snd_usb_audio_stream_new(chip, buffer, buflen, -1, ifout[i]);
return 0;
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment