Commit a810a3da authored by Jaroslav Kysela's avatar Jaroslav Kysela

ALSA update (0.9.2)

  - created snd-page-alloc module
    - moved all page allocation code there
    - preserves preallocated DMA buffers for devices
  - USB audio driver updated
  - AC'97 - better modem initialization code
  - timer API - enhanced (added pause and more event notifications)
  - splitted ice1724 code from ice1712 to own module
  - general
    - timerstamp cleanups (timeval -> timespec)
    - C99-like cleanups
  - trident driver
    - more workaround for wrong IRQ acks
  - OSS sequencer emulation
    - fixed OOPS (wrong free order)
    - more compatible with level 1 of sequencer (/dev/sequencer)
  - CS46xx driver updated
  - intel8x0 driver updated
  - emu10k1 driver updated
parent 74521715
...@@ -520,13 +520,19 @@ Module parameters ...@@ -520,13 +520,19 @@ Module parameters
Module for Envy24 (ICE1712) based PCI soundcards. Module for Envy24 (ICE1712) based PCI soundcards.
* MidiMan M Audio Delta 1010 * MidiMan M Audio Delta 1010
* MidiMan M Audio Delta 1010LT
* MidiMan M Audio Delta DiO 2496 * MidiMan M Audio Delta DiO 2496
* MidiMan M Audio Delta 66 * MidiMan M Audio Delta 66
* MidiMan M Audio Delta 44 * MidiMan M Audio Delta 44
* MidiMan M Audio Delta 410
* MidiMan M Audio Audiophile 2496 * MidiMan M Audio Audiophile 2496
* TerraTec EWS 88MT * TerraTec EWS 88MT
* TerraTec EWS 88D * TerraTec EWS 88D
* TerraTec EWX 24/96 * TerraTec EWX 24/96
* TerraTec DMX 6Fire
* Hoontech SoundTrack DSP 24
* Hoontech SoundTrack DSP 24 Value
* Hoontech SoundTrack DSP 24 Media 7.1
omni - Omni I/O support for MidiMan M-Audio Delta44/66 omni - Omni I/O support for MidiMan M-Audio Delta44/66
...@@ -534,6 +540,15 @@ Module parameters ...@@ -534,6 +540,15 @@ Module parameters
is not used with all Envy24 based cards (for example in the MidiMan Delta is not used with all Envy24 based cards (for example in the MidiMan Delta
serie). serie).
Module snd-ice1724
------------------
Module for Envy24HT (VT/ICE1724) based PCI soundcards.
* MidiMan M Audio Revolution 7.1
* AMP Ltd AUDIO2000
Module supports up to 8 cards and autoprobe.
Module snd-intel8x0 Module snd-intel8x0
------------------- -------------------
...@@ -659,7 +674,7 @@ Module parameters ...@@ -659,7 +674,7 @@ Module parameters
Module supports autoprobe and multiple chips (max 8). Module supports autoprobe and multiple chips (max 8).
Note: on some notebooks the buffer address cannot be detected Note: on some notebooks the buffer address cannot be detected
automatically, or causes hang-up during initialization. automatically, or causes hang-up during initialization.
In such a case, specify the buffer top address explicitly via In such a case, specify the buffer top address explicity via
buffer_top option. buffer_top option.
For example, For example,
Sony F250: buffer_top=0x25a800 Sony F250: buffer_top=0x25a800
...@@ -968,6 +983,19 @@ Module parameters ...@@ -968,6 +983,19 @@ Module parameters
The power-management is supported. The power-management is supported.
Configuring Non-ISAPNP Cards
============================
When the kernel is configured with ISA-PnP support, the modules
supporting the isapnp cards will have module options "isapnp".
If this option is set, *only* the ISA-PnP devices will be probed.
For probing the non ISA-PnP cards, you have to pass "isapnp=0" option
together with the proper i/o and irq configuration.
When the kernel is configured without ISA-PnP support, isapnp option
will be not built in.
modprobe/kmod support modprobe/kmod support
===================== =====================
......
...@@ -80,3 +80,5 @@ IEC958 (S/PDIF) interface: ...@@ -80,3 +80,5 @@ IEC958 (S/PDIF) interface:
IEC958 [...] [Playback|Capture] Con Mask /* consumer mask */ IEC958 [...] [Playback|Capture] Con Mask /* consumer mask */
IEC958 [...] [Playback|Capture] Pro Mask /* professional mask */ IEC958 [...] [Playback|Capture] Pro Mask /* professional mask */
IEC958 [...] [Playback|Capture] PCM Stream /* the settings assigned to a PCM stream */ IEC958 [...] [Playback|Capture] PCM Stream /* the settings assigned to a PCM stream */
IEC958 Q-subcode [Playback|Capture] Default /* Q-subcode bits */
IEC958 Preamble [Playback|Capture] Default /* burst preamble words (4*16bits) */
...@@ -48,6 +48,8 @@ ...@@ -48,6 +48,8 @@
<sect1><title>Memory Management Helpers</title> <sect1><title>Memory Management Helpers</title>
!Esound/core/memory.c !Esound/core/memory.c
!Iinclude/sound/sndmagic.h !Iinclude/sound/sndmagic.h
!Esound/core/memalloc.c
!Esound/core/sgbuf.c
</sect1> </sect1>
</chapter> </chapter>
<chapter><title>PCM API</title> <chapter><title>PCM API</title>
...@@ -62,9 +64,6 @@ ...@@ -62,9 +64,6 @@
<sect1><title>PCM Memory Managment</title> <sect1><title>PCM Memory Managment</title>
!Esound/core/pcm_memory.c !Esound/core/pcm_memory.c
</sect1> </sect1>
<sect1><title>SG-Buffer Helpers</title>
!Esound/core/pcm_sgbuf.c
</sect1>
</chapter> </chapter>
<chapter><title>Control/Mixer API</title> <chapter><title>Control/Mixer API</title>
<sect1><title>General Control Interface</title> <sect1><title>General Control Interface</title>
......
...@@ -437,7 +437,7 @@ ...@@ -437,7 +437,7 @@
// (see "Management of Cards and Components") // (see "Management of Cards and Components")
static int __devinit snd_mychip_create(snd_card_t *card, static int __devinit snd_mychip_create(snd_card_t *card,
struct pci_device *pci, struct pci_device *pci,
mychip_t *rchip) mychip_t **rchip)
{ {
mychip_t *chip; mychip_t *chip;
int err; int err;
...@@ -1666,7 +1666,7 @@ ...@@ -1666,7 +1666,7 @@
chip->iobase_phys = pci_resource_start(pci, 0); chip->iobase_phys = pci_resource_start(pci, 0);
chip->iobase_virt = (unsigned long) chip->iobase_virt = (unsigned long)
ioremap_nocache(chip->iobase_phys, 512); ioremap_nocache(chip->iobase_phys, 512);
if ((chip->res_port = request_mem_region(chip->port, 512, if ((chip->res_port = request_mem_region(chip->iobase_phys, 512,
"My Chip")) == NULL) { "My Chip")) == NULL) {
printk(KERN_ERR "cannot allocate the memory region\n"); printk(KERN_ERR "cannot allocate the memory region\n");
snd_mychip_free(chip); snd_mychip_free(chip);
...@@ -2820,7 +2820,7 @@ ...@@ -2820,7 +2820,7 @@
</para> </para>
<para> <para>
If you acquire a spinlock in the interrupt handler, and the If you aquire a spinlock in the interrupt handler, and the
lock is used in other pcm callbacks, too, then you have to lock is used in other pcm callbacks, too, then you have to
release the lock before calling release the lock before calling
<function>snd_pcm_period_elapsed()</function>, because <function>snd_pcm_period_elapsed()</function>, because
...@@ -4480,8 +4480,7 @@ ...@@ -4480,8 +4480,7 @@
<informalexample> <informalexample>
<programlisting> <programlisting>
<![CDATA[ <![CDATA[
snd_pcm_sgbuf_t *sgbuf = snd_magic_cast(snd_pcm_sgbuf_t, snd_pcm_sgbuf_t *sgbuf = (snd_pcm_sgbuf_t*)substream->dma_private;
substream->dma_private, return -EINVAL);
]]> ]]>
</programlisting> </programlisting>
</informalexample> </informalexample>
......
...@@ -156,12 +156,30 @@ ...@@ -156,12 +156,30 @@
/* extended modem ID bit defines */ /* extended modem ID bit defines */
#define AC97_MEI_LINE1 0x0001 /* Line1 present */ #define AC97_MEI_LINE1 0x0001 /* Line1 present */
#define AC97_MEI_LINE2 0x0002 /* Line2 present */ #define AC97_MEI_LINE2 0x0002 /* Line2 present */
#define AC97_MEI_HEADSET 0x0004 /* Headset present */ #define AC97_MEI_HANDSET 0x0004 /* Handset present */
#define AC97_MEI_CID1 0x0008 /* caller ID decode for Line1 is supported */ #define AC97_MEI_CID1 0x0008 /* caller ID decode for Line1 is supported */
#define AC97_MEI_CID2 0x0010 /* caller ID decode for Line2 is supported */ #define AC97_MEI_CID2 0x0010 /* caller ID decode for Line2 is supported */
#define AC97_MEI_ADDR_MASK 0xc000 /* physical codec ID (address) */ #define AC97_MEI_ADDR_MASK 0xc000 /* physical codec ID (address) */
#define AC97_MEI_ADDR_SHIFT 14 #define AC97_MEI_ADDR_SHIFT 14
/* extended modem status and control bit defines */
#define AC97_MEA_GPIO 0x0001 /* GPIO is ready (ro) */
#define AC97_MEA_MREF 0x0002 /* Vref is up to nominal level (ro) */
#define AC97_MEA_ADC1 0x0004 /* ADC1 operational (ro) */
#define AC97_MEA_DAC1 0x0008 /* DAC1 operational (ro) */
#define AC97_MEA_ADC2 0x0010 /* ADC2 operational (ro) */
#define AC97_MEA_DAC2 0x0020 /* DAC2 operational (ro) */
#define AC97_MEA_HADC 0x0040 /* HADC operational (ro) */
#define AC97_MEA_HDAC 0x0080 /* HDAC operational (ro) */
#define AC97_MEA_PRA 0x0100 /* GPIO power down (high) */
#define AC97_MEA_PRB 0x0200 /* reserved */
#define AC97_MEA_PRC 0x0400 /* ADC1 power down (high) */
#define AC97_MEA_PRD 0x0800 /* DAC1 power down (high) */
#define AC97_MEA_PRE 0x1000 /* ADC2 power down (high) */
#define AC97_MEA_PRF 0x2000 /* DAC2 power down (high) */
#define AC97_MEA_PRG 0x4000 /* HADC power down (high) */
#define AC97_MEA_PRH 0x8000 /* HDAC power down (high) */
/* specific - SigmaTel */ /* specific - SigmaTel */
#define AC97_SIGMATEL_ANALOG 0x6c /* Analog Special */ #define AC97_SIGMATEL_ANALOG 0x6c /* Analog Special */
#define AC97_SIGMATEL_DAC2INVERT 0x6e #define AC97_SIGMATEL_DAC2INVERT 0x6e
......
/* /*
* Advanced Linux Sound Architecture - ALSA - Driver * Advanced Linux Sound Architecture - ALSA - Driver
* Copyright (c) 1994-2000 by Jaroslav Kysela <perex@suse.cz>, * Copyright (c) 1994-2003 by Jaroslav Kysela <perex@suse.cz>,
* Abramo Bagnara <abramo@alsa-project.org> * Abramo Bagnara <abramo@alsa-project.org>
* *
* *
...@@ -150,7 +150,7 @@ enum { ...@@ -150,7 +150,7 @@ enum {
* * * *
*****************************************************************************/ *****************************************************************************/
#define SNDRV_PCM_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 4) #define SNDRV_PCM_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 5)
typedef unsigned long sndrv_pcm_uframes_t; typedef unsigned long sndrv_pcm_uframes_t;
typedef long sndrv_pcm_sframes_t; typedef long sndrv_pcm_sframes_t;
...@@ -398,8 +398,8 @@ struct sndrv_pcm_channel_info { ...@@ -398,8 +398,8 @@ struct sndrv_pcm_channel_info {
struct sndrv_pcm_status { struct sndrv_pcm_status {
enum sndrv_pcm_state state; /* stream state */ enum sndrv_pcm_state state; /* stream state */
struct timeval trigger_tstamp; /* time when stream was started/stopped/paused */ struct timespec trigger_tstamp; /* time when stream was started/stopped/paused */
struct timeval tstamp; /* reference timestamp */ struct timespec tstamp; /* reference timestamp */
sndrv_pcm_uframes_t appl_ptr; /* appl ptr */ sndrv_pcm_uframes_t appl_ptr; /* appl ptr */
sndrv_pcm_uframes_t hw_ptr; /* hw ptr */ sndrv_pcm_uframes_t hw_ptr; /* hw ptr */
sndrv_pcm_sframes_t delay; /* current delay in frames */ sndrv_pcm_sframes_t delay; /* current delay in frames */
...@@ -414,7 +414,7 @@ struct sndrv_pcm_mmap_status { ...@@ -414,7 +414,7 @@ struct sndrv_pcm_mmap_status {
enum sndrv_pcm_state state; /* RO: state - SNDRV_PCM_STATE_XXXX */ enum sndrv_pcm_state state; /* RO: state - SNDRV_PCM_STATE_XXXX */
int pad1; /* Needed for 64 bit alignment */ int pad1; /* Needed for 64 bit alignment */
sndrv_pcm_uframes_t hw_ptr; /* RO: hw ptr (0...boundary-1) */ sndrv_pcm_uframes_t hw_ptr; /* RO: hw ptr (0...boundary-1) */
struct timeval tstamp; /* Timestamp */ struct timespec tstamp; /* Timestamp */
enum sndrv_pcm_state suspended_state; /* RO: suspended stream state */ enum sndrv_pcm_state suspended_state; /* RO: suspended stream state */
}; };
...@@ -438,6 +438,7 @@ struct sndrv_xfern { ...@@ -438,6 +438,7 @@ struct sndrv_xfern {
enum { enum {
SNDRV_PCM_IOCTL_PVERSION = _IOR('A', 0x00, int), SNDRV_PCM_IOCTL_PVERSION = _IOR('A', 0x00, int),
SNDRV_PCM_IOCTL_INFO = _IOR('A', 0x01, struct sndrv_pcm_info), SNDRV_PCM_IOCTL_INFO = _IOR('A', 0x01, struct sndrv_pcm_info),
SNDRV_PCM_IOCTL_TSTAMP = _IOW('A', 0x02, int),
SNDRV_PCM_IOCTL_HW_REFINE = _IOWR('A', 0x10, struct sndrv_pcm_hw_params), SNDRV_PCM_IOCTL_HW_REFINE = _IOWR('A', 0x10, struct sndrv_pcm_hw_params),
SNDRV_PCM_IOCTL_HW_PARAMS = _IOWR('A', 0x11, struct sndrv_pcm_hw_params), SNDRV_PCM_IOCTL_HW_PARAMS = _IOWR('A', 0x11, struct sndrv_pcm_hw_params),
SNDRV_PCM_IOCTL_HW_FREE = _IO('A', 0x12), SNDRV_PCM_IOCTL_HW_FREE = _IO('A', 0x12),
...@@ -513,7 +514,7 @@ struct sndrv_rawmidi_params { ...@@ -513,7 +514,7 @@ struct sndrv_rawmidi_params {
struct sndrv_rawmidi_status { struct sndrv_rawmidi_status {
enum sndrv_rawmidi_stream stream; enum sndrv_rawmidi_stream stream;
struct timeval tstamp; /* Timestamp */ struct timespec tstamp; /* Timestamp */
size_t avail; /* available bytes */ size_t avail; /* available bytes */
size_t xruns; /* count of overruns since last status (in bytes) */ size_t xruns; /* count of overruns since last status (in bytes) */
unsigned char reserved[16]; /* reserved for future use */ unsigned char reserved[16]; /* reserved for future use */
...@@ -532,7 +533,7 @@ enum { ...@@ -532,7 +533,7 @@ enum {
* Timer section - /dev/snd/timer * Timer section - /dev/snd/timer
*/ */
#define SNDRV_TIMER_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 0) #define SNDRV_TIMER_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 1)
enum sndrv_timer_class { enum sndrv_timer_class {
SNDRV_TIMER_CLASS_NONE = -1, SNDRV_TIMER_CLASS_NONE = -1,
...@@ -556,6 +557,9 @@ enum sndrv_timer_slave_class { ...@@ -556,6 +557,9 @@ enum sndrv_timer_slave_class {
#define SNDRV_TIMER_GLOBAL_SYSTEM 0 #define SNDRV_TIMER_GLOBAL_SYSTEM 0
#define SNDRV_TIMER_GLOBAL_RTC 1 #define SNDRV_TIMER_GLOBAL_RTC 1
/* info flags */
#define SNDRV_TIMER_FLG_SLAVE (1<<0) /* cannot be controlled */
struct sndrv_timer_id { struct sndrv_timer_id {
enum sndrv_timer_class dev_class; enum sndrv_timer_class dev_class;
enum sndrv_timer_slave_class dev_sclass; enum sndrv_timer_slave_class dev_sclass;
...@@ -564,36 +568,65 @@ struct sndrv_timer_id { ...@@ -564,36 +568,65 @@ struct sndrv_timer_id {
int subdevice; int subdevice;
}; };
struct sndrv_timer_ginfo {
struct sndrv_timer_id tid; /* requested timer ID */
unsigned int flags; /* timer flags - SNDRV_TIMER_FLG_* */
int card; /* card number */
unsigned char id[64]; /* timer identification */
unsigned char name[80]; /* timer name */
unsigned long reserved0; /* reserved for future use */
unsigned long resolution; /* average period resolution in ns */
unsigned long resolution_min; /* minimal period resolution in ns */
unsigned long resolution_max; /* maximal period resolution in ns */
unsigned int clients; /* active timer clients */
unsigned char reserved[32];
};
struct sndrv_timer_gparams {
struct sndrv_timer_id tid; /* requested timer ID */
unsigned long period_num; /* requested precise period duration (in seconds) - numerator */
unsigned long period_den; /* requested precise period duration (in seconds) - denominator */
unsigned char reserved[32];
};
struct sndrv_timer_gstatus {
struct sndrv_timer_id tid; /* requested timer ID */
unsigned long resolution; /* current period resolution in ns */
unsigned long resolution_num; /* precise current period resolution (in seconds) - numerator */
unsigned long resolution_den; /* precise current period resolution (in seconds) - denominator */
unsigned char reserved[32];
};
struct sndrv_timer_select { struct sndrv_timer_select {
struct sndrv_timer_id id; /* bind to timer ID */ struct sndrv_timer_id id; /* bind to timer ID */
unsigned char reserved[32]; /* reserved */ unsigned char reserved[32]; /* reserved */
}; };
#define SNDRV_TIMER_FLG_SLAVE (1<<0) /* cannot be controlled */
struct sndrv_timer_info { struct sndrv_timer_info {
unsigned int flags; /* timer flags - SNDRV_TIMER_FLG_* */ unsigned int flags; /* timer flags - SNDRV_TIMER_FLG_* */
int card; /* R: card number */ int card; /* card number */
unsigned char id[64]; /* timer identificator */ unsigned char id[64]; /* timer identificator */
unsigned char name[80]; /* timer name */ unsigned char name[80]; /* timer name */
unsigned long ticks; /* maximum ticks */ unsigned long reserved0; /* reserved for future use */
unsigned long resolution; /* average resolution */ unsigned long resolution; /* average period resolution in ns */
unsigned char reserved[64]; /* reserved */ unsigned char reserved[64]; /* reserved */
}; };
#define SNDRV_TIMER_PSFLG_AUTO (1<<0) /* supports auto start */ #define SNDRV_TIMER_PSFLG_AUTO (1<<0) /* auto start, otherwise one-shot */
#define SNDRV_TIMER_PSFLG_EXCLUSIVE (1<<1) /* exclusive use, precise start/stop/pause/continue */
struct sndrv_timer_params { struct sndrv_timer_params {
unsigned int flags; /* flags - SNDRV_MIXER_PSFLG_* */ unsigned int flags; /* flags - SNDRV_MIXER_PSFLG_* */
unsigned int ticks; /* requested resolution in ticks */ unsigned int ticks; /* requested resolution in ticks */
unsigned int queue_size; /* total size of queue (32-1024) */ unsigned int queue_size; /* total size of queue (32-1024) */
unsigned int reserved0; /* reserved, was: failure locations */ unsigned int reserved0; /* reserved, was: failure locations */
unsigned char reserved[64]; /* reserved */ unsigned int filter; /* event filter (bitmask of SNDRV_TIMER_EVENT_*) */
unsigned char reserved[60]; /* reserved */
}; };
struct sndrv_timer_status { struct sndrv_timer_status {
struct timeval tstamp; /* Timestamp */ struct timespec tstamp; /* Timestamp - last update */
unsigned int resolution; /* current resolution */ unsigned int resolution; /* current period resolution in ns */
unsigned int lost; /* counter of master tick lost */ unsigned int lost; /* counter of master tick lost */
unsigned int overrun; /* count of read queue overruns */ unsigned int overrun; /* count of read queue overruns */
unsigned int queue; /* used queue size */ unsigned int queue; /* used queue size */
...@@ -603,13 +636,18 @@ struct sndrv_timer_status { ...@@ -603,13 +636,18 @@ struct sndrv_timer_status {
enum { enum {
SNDRV_TIMER_IOCTL_PVERSION = _IOR('T', 0x00, int), SNDRV_TIMER_IOCTL_PVERSION = _IOR('T', 0x00, int),
SNDRV_TIMER_IOCTL_NEXT_DEVICE = _IOWR('T', 0x01, struct sndrv_timer_id), SNDRV_TIMER_IOCTL_NEXT_DEVICE = _IOWR('T', 0x01, struct sndrv_timer_id),
SNDRV_TIMER_IOCTL_TREAD = _IOW('T', 0x02, int),
SNDRV_TIMER_IOCTL_GINFO = _IOWR('T', 0x03, struct sndrv_timer_ginfo),
SNDRV_TIMER_IOCTL_GPARAMS = _IOW('T', 0x04, struct sndrv_timer_gparams),
SNDRV_TIMER_IOCTL_GSTATUS = _IOWR('T', 0x05, struct sndrv_timer_gstatus),
SNDRV_TIMER_IOCTL_SELECT = _IOW('T', 0x10, struct sndrv_timer_select), SNDRV_TIMER_IOCTL_SELECT = _IOW('T', 0x10, struct sndrv_timer_select),
SNDRV_TIMER_IOCTL_INFO = _IOR('T', 0x11, struct sndrv_timer_info), SNDRV_TIMER_IOCTL_INFO = _IOR('T', 0x11, struct sndrv_timer_info),
SNDRV_TIMER_IOCTL_PARAMS = _IOW('T', 0x12, struct sndrv_timer_params), SNDRV_TIMER_IOCTL_PARAMS = _IOW('T', 0x12, struct sndrv_timer_params),
SNDRV_TIMER_IOCTL_STATUS = _IOW('T', 0x14, struct sndrv_timer_status), SNDRV_TIMER_IOCTL_STATUS = _IOR('T', 0x14, struct sndrv_timer_status),
SNDRV_TIMER_IOCTL_START = _IO('T', 0x20), SNDRV_TIMER_IOCTL_START = _IO('T', 0x20),
SNDRV_TIMER_IOCTL_STOP = _IO('T', 0x21), SNDRV_TIMER_IOCTL_STOP = _IO('T', 0x21),
SNDRV_TIMER_IOCTL_CONTINUE = _IO('T', 0x22), SNDRV_TIMER_IOCTL_CONTINUE = _IO('T', 0x22),
SNDRV_TIMER_IOCTL_PAUSE = _IO('T', 0x23),
}; };
struct sndrv_timer_read { struct sndrv_timer_read {
...@@ -617,13 +655,33 @@ struct sndrv_timer_read { ...@@ -617,13 +655,33 @@ struct sndrv_timer_read {
unsigned int ticks; unsigned int ticks;
}; };
enum sndrv_timer_event {
SNDRV_TIMER_EVENT_RESOLUTION = 0, /* val = resolution in ns */
SNDRV_TIMER_EVENT_TICK, /* val = ticks */
SNDRV_TIMER_EVENT_START, /* val = resolution in ns */
SNDRV_TIMER_EVENT_STOP, /* val = 0 */
SNDRV_TIMER_EVENT_CONTINUE, /* val = resolution in ns */
SNDRV_TIMER_EVENT_PAUSE, /* val = 0 */
/* master timer events for slave timer instances */
SNDRV_TIMER_EVENT_MSTART = SNDRV_TIMER_EVENT_START + 10,
SNDRV_TIMER_EVENT_MSTOP = SNDRV_TIMER_EVENT_STOP + 10,
SNDRV_TIMER_EVENT_MCONTINUE = SNDRV_TIMER_EVENT_CONTINUE + 10,
SNDRV_TIMER_EVENT_MPAUSE = SNDRV_TIMER_EVENT_PAUSE + 10,
};
struct sndrv_timer_tread {
enum sndrv_timer_event event;
struct timespec tstamp;
unsigned int val;
};
/**************************************************************************** /****************************************************************************
* * * *
* Section for driver control interface - /dev/snd/control? * * Section for driver control interface - /dev/snd/control? *
* * * *
****************************************************************************/ ****************************************************************************/
#define SNDRV_CTL_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 0) #define SNDRV_CTL_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 1)
struct sndrv_ctl_card_info { struct sndrv_ctl_card_info {
int card; /* card number */ int card; /* card number */
...@@ -664,6 +722,7 @@ enum sndrv_ctl_elem_iface { ...@@ -664,6 +722,7 @@ enum sndrv_ctl_elem_iface {
#define SNDRV_CTL_ELEM_ACCESS_WRITE (1<<1) #define SNDRV_CTL_ELEM_ACCESS_WRITE (1<<1)
#define SNDRV_CTL_ELEM_ACCESS_READWRITE (SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE) #define SNDRV_CTL_ELEM_ACCESS_READWRITE (SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE)
#define SNDRV_CTL_ELEM_ACCESS_VOLATILE (1<<2) /* control value may be changed without a notification */ #define SNDRV_CTL_ELEM_ACCESS_VOLATILE (1<<2) /* control value may be changed without a notification */
#define SNDRV_CTL_ELEM_ACCESS_TIMESTAMP (1<<2) /* when was control changed */
#define SNDRV_CTL_ELEM_ACCESS_INACTIVE (1<<8) /* control does actually nothing, but may be updated */ #define SNDRV_CTL_ELEM_ACCESS_INACTIVE (1<<8) /* control does actually nothing, but may be updated */
#define SNDRV_CTL_ELEM_ACCESS_LOCK (1<<9) /* write lock */ #define SNDRV_CTL_ELEM_ACCESS_LOCK (1<<9) /* write lock */
#define SNDRV_CTL_ELEM_ACCESS_OWNER (1<<10) /* write lock owner */ #define SNDRV_CTL_ELEM_ACCESS_OWNER (1<<10) /* write lock owner */
...@@ -744,7 +803,8 @@ struct sndrv_ctl_elem_value { ...@@ -744,7 +803,8 @@ struct sndrv_ctl_elem_value {
} bytes; } bytes;
struct sndrv_aes_iec958 iec958; struct sndrv_aes_iec958 iec958;
} value; /* RO */ } value; /* RO */
unsigned char reserved[128]; struct timespec tstamp;
unsigned char reserved[128-sizeof(struct timespec)];
}; };
enum { enum {
......
...@@ -27,7 +27,7 @@ ...@@ -27,7 +27,7 @@
#include <linux/rwsem.h> /* struct rw_semaphore */ #include <linux/rwsem.h> /* struct rw_semaphore */
/* Typedef's */ /* Typedef's */
typedef struct timeval snd_timestamp_t; typedef struct timespec snd_timestamp_t;
typedef struct sndrv_interval snd_interval_t; typedef struct sndrv_interval snd_interval_t;
typedef enum sndrv_card_type snd_card_type; typedef enum sndrv_card_type snd_card_type;
typedef struct sndrv_xferi snd_xferi_t; typedef struct sndrv_xferi snd_xferi_t;
...@@ -275,32 +275,6 @@ void snd_hidden_vfree(void *obj); ...@@ -275,32 +275,6 @@ void snd_hidden_vfree(void *obj);
#endif #endif
void *snd_kcalloc(size_t size, int flags); void *snd_kcalloc(size_t size, int flags);
char *snd_kmalloc_strdup(const char *string, int flags); char *snd_kmalloc_strdup(const char *string, int flags);
void *snd_malloc_pages(unsigned long size, unsigned int dma_flags);
void *snd_malloc_pages_fallback(unsigned long size, unsigned int dma_flags, unsigned long *res_size);
void snd_free_pages(void *ptr, unsigned long size);
#ifdef CONFIG_PCI
void *snd_malloc_pci_pages(struct pci_dev *pci, unsigned long size, dma_addr_t *dma_addr);
void *snd_malloc_pci_pages_fallback(struct pci_dev *pci, unsigned long size, dma_addr_t *dma_addr, unsigned long *res_size);
void snd_free_pci_pages(struct pci_dev *pci, unsigned long size, void *ptr, dma_addr_t dma_addr);
void *snd_malloc_pci_page(struct pci_dev *pci, dma_addr_t *dma_addr);
#define snd_free_pci_page(pci,ptr,addr) snd_free_pci_pages(pci,PAGE_SIZE,ptr,addr)
#endif
#ifdef CONFIG_SBUS
void *snd_malloc_sbus_pages(struct sbus_dev *sdev, unsigned long size, dma_addr_t *dma_addr);
void *snd_malloc_sbus_pages_fallback(struct sbus_dev *sdev, unsigned long size, dma_addr_t *dma_addr, unsigned long *res_size);
void snd_free_sbus_pages(struct sbus_dev *sdev, unsigned long size, void *ptr, dma_addr_t dma_addr);
#endif
#ifdef CONFIG_ISA
#ifdef CONFIG_PCI
#define snd_malloc_isa_pages(size, dma_addr) snd_malloc_pci_pages(NULL, size, dma_addr)
#define snd_malloc_isa_pages_fallback(size, dma_addr, res_size) snd_malloc_pci_pages_fallback(NULL, size, dma_addr, res_size)
#define snd_free_isa_pages(size, ptr, dma_addr) snd_free_pci_pages(NULL, size, ptr, dma_addr)
#else /* !CONFIG_PCI */
void *snd_malloc_isa_pages(unsigned long size, dma_addr_t *dma_addr);
void *snd_malloc_isa_pages_fallback(unsigned long size, dma_addr_t *dma_addr, unsigned long *res_size);
#define snd_free_isa_pages(size, ptr, dma_addr) snd_free_pages(ptr, size)
#endif /* CONFIG_PCI */
#endif /* CONFIG_ISA */
int copy_to_user_fromio(void *dst, unsigned long src, size_t count); int copy_to_user_fromio(void *dst, unsigned long src, size_t count);
int copy_from_user_toio(unsigned long dst, const void *src, size_t count); int copy_from_user_toio(unsigned long dst, const void *src, size_t count);
...@@ -450,9 +424,27 @@ void snd_verbose_printd(const char *file, int line, const char *format, ...); ...@@ -450,9 +424,27 @@ void snd_verbose_printd(const char *file, int line, const char *format, ...);
#define snd_BUG() snd_assert(0, ) #define snd_BUG() snd_assert(0, )
#define snd_timestamp_now(tstamp) do_gettimeofday(tstamp) static inline void snd_timestamp_now(struct timespec *tstamp, int timespec)
#define snd_timestamp_zero(tstamp) do { (tstamp)->tv_sec = 0; (tstamp)->tv_usec = 0; } while (0) {
#define snd_timestamp_null(tstamp) ((tstamp)->tv_sec == 0 && (tstamp)->tv_usec ==0) struct timeval val;
/* FIXME: use a linear time source */
do_gettimeofday(&val);
tstamp->tv_sec = val.tv_sec;
tstamp->tv_nsec = val.tv_usec;
if (timespec)
tstamp->tv_nsec *= 1000L;
}
static inline void snd_timestamp_zero(struct timespec *tstamp)
{
tstamp->tv_sec = 0;
tstamp->tv_nsec = 0;
}
static inline int snd_timestamp_null(struct timespec *tstamp)
{
return tstamp->tv_sec == 0 && tstamp->tv_nsec == 0;
}
#define SNDRV_OSS_VERSION ((3<<16)|(8<<8)|(1<<4)|(0)) /* 3.8.1a */ #define SNDRV_OSS_VERSION ((3<<16)|(8<<8)|(1<<4)|(0)) /* 3.8.1a */
......
...@@ -49,20 +49,6 @@ ...@@ -49,20 +49,6 @@
* ========================================================================== * ==========================================================================
*/ */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 0)
#if defined(__i386__) || defined(__ppc__) || defined(__x86_64__)
/*
* Here a dirty hack for 2.4 kernels.. See sound/core/memory.c.
*/
#define HACK_PCI_ALLOC_CONSISTENT
#include <linux/pci.h>
void *snd_pci_hack_alloc_consistent(struct pci_dev *hwdev, size_t size,
dma_addr_t *dma_handle);
#undef pci_alloc_consistent
#define pci_alloc_consistent snd_pci_hack_alloc_consistent
#endif /* i386 or ppc */
#endif /* 2.4.0 */
#ifdef CONFIG_SND_DEBUG_MEMORY #ifdef CONFIG_SND_DEBUG_MEMORY
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/vmalloc.h> #include <linux/vmalloc.h>
......
...@@ -49,6 +49,8 @@ ...@@ -49,6 +49,8 @@
#define NUM_G 64 /* use all channels */ #define NUM_G 64 /* use all channels */
#define NUM_FXSENDS 4 #define NUM_FXSENDS 4
#define EMU10K1_DMA_MASK 0x1fffffffUL
#define AUDIGY_DMA_MASK 0xffffffffUL
#define TMEMSIZE 256*1024 #define TMEMSIZE 256*1024
#define TMEMSIZEREG 4 #define TMEMSIZEREG 4
...@@ -232,6 +234,8 @@ ...@@ -232,6 +234,8 @@
#define A_GPINPUT_MASK 0xff00 #define A_GPINPUT_MASK 0xff00
#define A_GPOUTPUT_MASK 0x00ff #define A_GPOUTPUT_MASK 0x00ff
#define A_IOCFG_GPOUT0 0x0044 /* analog/digital? */ #define A_IOCFG_GPOUT0 0x0044 /* analog/digital? */
#define A_IOCFG_GPOUT1 0x0002 /* IR */
#define A_IOCFG_GPOUT2 0x0001 /* IR */
#define TIMER 0x1a /* Timer terminal count register */ #define TIMER 0x1a /* Timer terminal count register */
/* NOTE: After the rate is changed, a maximum */ /* NOTE: After the rate is changed, a maximum */
...@@ -936,6 +940,7 @@ struct _snd_emu10k1 { ...@@ -936,6 +940,7 @@ struct _snd_emu10k1 {
unsigned short model; /* subsystem id */ unsigned short model; /* subsystem id */
unsigned int card_type; /* EMU10K1_CARD_* */ unsigned int card_type; /* EMU10K1_CARD_* */
unsigned int ecard_ctrl; /* ecard control bits */ unsigned int ecard_ctrl; /* ecard control bits */
unsigned long dma_mask; /* PCI DMA mask */
int max_cache_pages; /* max memory size / PAGE_SIZE */ int max_cache_pages; /* max memory size / PAGE_SIZE */
void *silent_page; /* silent page */ void *silent_page; /* silent page */
dma_addr_t silent_page_dmaaddr; dma_addr_t silent_page_dmaaddr;
......
/*
* Copyright (c) by Jaroslav Kysela <perex@suse.cz>
* Takashi Iwai <tiwai@suse.de>
*
* Generic memory allocators
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef __SOUND_MEMALLOC_H
#define __SOUND_MEMALLOC_H
#include <linux/pci.h>
#ifdef CONFIG_SBUS
#include <asm/sbus.h>
#endif
/*
* buffer device info
*/
struct snd_dma_device {
int type; /* SNDRV_MEM_TYPE_XXX */
union {
struct pci_dev *pci; /* for PCI and PCI-SG types */
unsigned int flags; /* GFP_XXX for continous and ISA types */
#ifdef CONFIG_SBUS
struct sbus_dev *sbus; /* for SBUS type */
#endif
} dev;
unsigned int id; /* a unique ID */
};
/*
* buffer types
*/
#define SNDRV_DMA_TYPE_UNKNOWN 0 /* not defined */
#define SNDRV_DMA_TYPE_CONTINUOUS 1 /* continuous no-DMA memory */
#define SNDRV_DMA_TYPE_ISA 2 /* ISA continuous */
#define SNDRV_DMA_TYPE_PCI 3 /* PCI continuous */
#define SNDRV_DMA_TYPE_SBUS 4 /* SBUS continuous */
#define SNDRV_DMA_TYPE_PCI_SG 5 /* PCI SG-buffer */
#ifdef CONFIG_PCI
/*
* compose a snd_dma_device struct for the PCI device
*/
static inline void snd_dma_device_pci(struct snd_dma_device *dev, struct pci_dev *pci, unsigned int id)
{
memset(dev, 0, sizeof(*dev));
dev->type = SNDRV_DMA_TYPE_PCI;
dev->dev.pci = pci;
dev->id = id;
}
#endif
/*
* info for buffer allocation
*/
struct snd_dma_buffer {
unsigned char *area; /* virtual pointer */
dma_addr_t addr; /* physical address */
size_t bytes; /* buffer size in bytes */
void *private_data; /* private for allocator; don't touch */
};
/* allocate/release a buffer */
int snd_dma_alloc_pages(const struct snd_dma_device *dev, size_t size, struct snd_dma_buffer *dmab);
void snd_dma_free_pages(const struct snd_dma_device *dev, struct snd_dma_buffer *dmab);
/* buffer-preservation managements */
size_t snd_dma_get_reserved(const struct snd_dma_device *dev, struct snd_dma_buffer *dmab);
int snd_dma_free_reserved(const struct snd_dma_device *dev);
int snd_dma_set_reserved(const struct snd_dma_device *dev, struct snd_dma_buffer *dmab);
/*
* Generic memory allocators
*/
/*
* continuous pages
*/
void *snd_malloc_pages(size_t size, unsigned int gfp_flags);
void *snd_malloc_pages_fallback(size_t size, unsigned int gfp_flags, size_t *res_size);
void snd_free_pages(void *ptr, size_t size);
#ifdef CONFIG_PCI
/*
* PCI continuous pages
*/
void *snd_malloc_pci_pages(struct pci_dev *pci, size_t size, dma_addr_t *dma_addr);
void *snd_malloc_pci_pages_fallback(struct pci_dev *pci, size_t size, dma_addr_t *dma_addr, size_t *res_size);
void snd_free_pci_pages(struct pci_dev *pci, size_t size, void *ptr, dma_addr_t dma_addr);
/* one page allocation */
void *snd_malloc_pci_page(struct pci_dev *pci, dma_addr_t *dma_addr);
#define snd_free_pci_page(pci,ptr,addr) snd_free_pci_pages(pci,PAGE_SIZE,ptr,addr)
#endif
#ifdef CONFIG_SBUS
/*
* SBUS continuous pages
*/
void *snd_malloc_sbus_pages(struct sbus_dev *sdev, size_t size, dma_addr_t *dma_addr);
void *snd_malloc_sbus_pages_fallback(struct sbus_dev *sdev, size_t size, dma_addr_t *dma_addr, size_t *res_size);
void snd_free_sbus_pages(struct sbus_dev *sdev, size_t size, void *ptr, dma_addr_t dma_addr);
#endif
#ifdef CONFIG_ISA
/*
* ISA continuous pages
*/
void *snd_malloc_isa_pages(size_t size, dma_addr_t *dma_addr);
void *snd_malloc_isa_pages_fallback(size_t size, dma_addr_t *dma_addr, size_t *res_size);
void snd_free_isa_pages(size_t size, void *ptr, dma_addr_t addr);
#ifdef CONFIG_PCI
#define snd_malloc_isa_pages(size, dma_addr) snd_malloc_pci_pages(NULL, size, dma_addr)
#define snd_malloc_isa_pages_fallback(size, dma_addr, res_size) snd_malloc_pci_pages_fallback(NULL, size, dma_addr, res_size)
#define snd_free_isa_pages(size, ptr, dma_addr) snd_free_pci_pages(NULL, size, ptr, dma_addr)
#else /* !CONFIG_PCI */
#define snd_free_isa_pages(size, ptr, dma_addr) snd_free_pages(ptr, size)
#endif /* CONFIG_PCI */
#endif /* CONFIG_ISA */
#ifdef CONFIG_PCI
/*
* Scatter-Gather PCI pages
*/
struct snd_sg_page {
void *buf;
dma_addr_t addr;
};
struct snd_sg_buf {
int size; /* allocated byte size */
int pages; /* allocated pages */
int tblsize; /* allocated table size */
struct snd_sg_page *table; /* address table */
struct page **page_table; /* page table (for vmap/vunmap) */
struct pci_dev *pci;
};
void *snd_malloc_sgbuf_pages(struct pci_dev *pci, size_t size, struct snd_dma_buffer *dmab);
int snd_free_sgbuf_pages(struct snd_dma_buffer *dmab);
/*
* return the pages matching with the given byte size
*/
static inline unsigned int snd_sgbuf_aligned_pages(size_t size)
{
return (size + PAGE_SIZE - 1) >> PAGE_SHIFT;
}
/*
* return the physical address at the corresponding offset
*/
static inline dma_addr_t snd_sgbuf_get_addr(struct snd_sg_buf *sgbuf, size_t offset)
{
return sgbuf->table[offset >> PAGE_SHIFT].addr + offset % PAGE_SIZE;
}
#endif /* CONFIG_PCI */
/*
* wrappers
*/
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 0)
#ifdef CONFIG_PCI
#if defined(__i386__) || defined(__ppc__) || defined(__x86_64__)
#define HACK_PCI_ALLOC_CONSISTENT
/* a hack for 2.4/5 kernels for better allocation of large buffers */
void *snd_pci_hack_alloc_consistent(struct pci_dev *hwdev, size_t size,
dma_addr_t *dma_handle);
#endif /* arch */
#endif /* CONFIG_PCI */
#endif /* LINUX >= 2.4.0 */
#endif /* __SOUND_MEMALLOC_H */
...@@ -42,6 +42,7 @@ ...@@ -42,6 +42,7 @@
#define MPU401_HW_ALS4000 16 /* Avance Logic ALS4000 */ #define MPU401_HW_ALS4000 16 /* Avance Logic ALS4000 */
#define MPU401_HW_INTEL8X0 17 /* Intel8x0 driver */ #define MPU401_HW_INTEL8X0 17 /* Intel8x0 driver */
#define MPU401_HW_PC98II 18 /* Roland PC98II */ #define MPU401_HW_PC98II 18 /* Roland PC98II */
#define MPU401_HW_AUREAL 19 /* Aureal Vortex */
#define MPU401_MODE_BIT_INPUT 0 #define MPU401_MODE_BIT_INPUT 0
#define MPU401_MODE_BIT_OUTPUT 1 #define MPU401_MODE_BIT_OUTPUT 1
...@@ -87,6 +88,9 @@ struct _snd_mpu401 { ...@@ -87,6 +88,9 @@ struct _snd_mpu401 {
spinlock_t timer_lock; spinlock_t timer_lock;
struct timer_list timer; struct timer_list timer;
void (*write) (mpu401_t * mpu, unsigned char data, unsigned long addr);
unsigned char (*read) (mpu401_t * mpu, unsigned long addr);
}; };
/* I/O ports */ /* I/O ports */
......
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
*/ */
#include <sound/asound.h> #include <sound/asound.h>
#include <sound/memalloc.h>
#include <linux/poll.h> #include <linux/poll.h>
#include <linux/bitops.h> #include <linux/bitops.h>
...@@ -49,6 +50,7 @@ typedef struct sndrv_pcm_status snd_pcm_status_t; ...@@ -49,6 +50,7 @@ typedef struct sndrv_pcm_status snd_pcm_status_t;
typedef struct sndrv_pcm_mmap_status snd_pcm_mmap_status_t; typedef struct sndrv_pcm_mmap_status snd_pcm_mmap_status_t;
typedef struct sndrv_pcm_mmap_control snd_pcm_mmap_control_t; typedef struct sndrv_pcm_mmap_control snd_pcm_mmap_control_t;
typedef struct sndrv_mask snd_mask_t; typedef struct sndrv_mask snd_mask_t;
typedef struct snd_sg_buf snd_pcm_sgbuf_t;
#define _snd_pcm_substream_chip(substream) ((substream)->private_data) #define _snd_pcm_substream_chip(substream) ((substream)->private_data)
#define snd_pcm_substream_chip(substream) snd_magic_cast1(chip_t, _snd_pcm_substream_chip(substream), return -ENXIO) #define snd_pcm_substream_chip(substream) snd_magic_cast1(chip_t, _snd_pcm_substream_chip(substream), return -ENXIO)
...@@ -121,13 +123,6 @@ typedef struct _snd_pcm_ops { ...@@ -121,13 +123,6 @@ typedef struct _snd_pcm_ops {
#define SNDRV_PCM_TRIGGER_SUSPEND 5 #define SNDRV_PCM_TRIGGER_SUSPEND 5
#define SNDRV_PCM_TRIGGER_RESUME 6 #define SNDRV_PCM_TRIGGER_RESUME 6
#define SNDRV_PCM_DMA_TYPE_UNKNOWN 0 /* not defined */
#define SNDRV_PCM_DMA_TYPE_CONTINUOUS 1 /* continuous no-DMA memory */
#define SNDRV_PCM_DMA_TYPE_ISA 2 /* ISA continuous */
#define SNDRV_PCM_DMA_TYPE_PCI 3 /* PCI continuous */
#define SNDRV_PCM_DMA_TYPE_SBUS 4 /* SBUS continuous */
#define SNDRV_PCM_DMA_TYPE_PCI_SG 5 /* PCI SG-buffer */
/* If you change this don't forget to change rates[] table in pcm_native.c */ /* If you change this don't forget to change rates[] table in pcm_native.c */
#define SNDRV_PCM_RATE_5512 (1<<0) /* 5512Hz */ #define SNDRV_PCM_RATE_5512 (1<<0) /* 5512Hz */
#define SNDRV_PCM_RATE_8000 (1<<1) /* 8000Hz */ #define SNDRV_PCM_RATE_8000 (1<<1) /* 8000Hz */
...@@ -282,13 +277,6 @@ typedef struct { ...@@ -282,13 +277,6 @@ typedef struct {
unsigned int mask; unsigned int mask;
} snd_pcm_hw_constraint_list_t; } snd_pcm_hw_constraint_list_t;
struct snd_pcm_dma_buffer {
unsigned char *area;
dma_addr_t addr;
unsigned long bytes;
void *private_data; /* for allocator */
};
struct _snd_pcm_runtime { struct _snd_pcm_runtime {
/* -- Status -- */ /* -- Status -- */
snd_pcm_substream_t *trigger_master; snd_pcm_substream_t *trigger_master;
...@@ -317,6 +305,7 @@ struct _snd_pcm_runtime { ...@@ -317,6 +305,7 @@ struct _snd_pcm_runtime {
unsigned int rate_den; unsigned int rate_den;
/* -- SW params -- */ /* -- SW params -- */
int tstamp_timespec; /* use timeval (0) or timespec (1) */
snd_pcm_tstamp_t tstamp_mode; /* mmap timestamp is updated */ snd_pcm_tstamp_t tstamp_mode; /* mmap timestamp is updated */
unsigned int period_step; unsigned int period_step;
unsigned int sleep_min; /* min ticks to sleep */ unsigned int sleep_min; /* min ticks to sleep */
...@@ -362,7 +351,7 @@ struct _snd_pcm_runtime { ...@@ -362,7 +351,7 @@ struct _snd_pcm_runtime {
/* -- DMA -- */ /* -- DMA -- */
unsigned char *dma_area; /* DMA area */ unsigned char *dma_area; /* DMA area */
dma_addr_t dma_addr; /* physical bus address (not accessible from main CPU) */ dma_addr_t dma_addr; /* physical bus address (not accessible from main CPU) */
unsigned long dma_bytes; /* size of DMA area */ size_t dma_bytes; /* size of DMA area */
void *dma_private; /* private DMA data for the memory allocator */ void *dma_private; /* private DMA data for the memory allocator */
#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE) #if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
...@@ -379,17 +368,17 @@ struct _snd_pcm_substream { ...@@ -379,17 +368,17 @@ struct _snd_pcm_substream {
char name[32]; /* substream name */ char name[32]; /* substream name */
int stream; /* stream (direction) */ int stream; /* stream (direction) */
size_t buffer_bytes_max; /* limit ring buffer size */ size_t buffer_bytes_max; /* limit ring buffer size */
int dma_type; struct snd_dma_device dma_device;
struct snd_pcm_dma_buffer dma_buffer; struct snd_dma_buffer dma_buffer;
size_t dma_max; size_t dma_max;
void *dma_private;
/* -- hardware operations -- */ /* -- hardware operations -- */
unsigned int open_flag: 1; /* lowlevel device has been opened */
snd_pcm_ops_t *ops; snd_pcm_ops_t *ops;
/* -- runtime information -- */ /* -- runtime information -- */
snd_pcm_runtime_t *runtime; snd_pcm_runtime_t *runtime;
/* -- timer section -- */ /* -- timer section -- */
snd_timer_t *timer; /* timer */ snd_timer_t *timer; /* timer */
int timer_running; /* time is running */ int timer_running: 1; /* time is running */
spinlock_t timer_lock; spinlock_t timer_lock;
/* -- next substream -- */ /* -- next substream -- */
snd_pcm_substream_t *next; snd_pcm_substream_t *next;
...@@ -880,7 +869,18 @@ int snd_pcm_lib_preallocate_pci_pages_for_all(struct pci_dev *pci, ...@@ -880,7 +869,18 @@ int snd_pcm_lib_preallocate_pci_pages_for_all(struct pci_dev *pci,
snd_pcm_t *pcm, snd_pcm_t *pcm,
size_t size, size_t size,
size_t max); size_t max);
int snd_pcm_lib_preallocate_sg_pages(struct pci_dev *pci,
snd_pcm_substream_t *substream,
size_t size, size_t max);
int snd_pcm_lib_preallocate_sg_pages_for_all(struct pci_dev *pci,
snd_pcm_t *pcm,
size_t size, size_t max);
#define snd_pcm_substream_sgbuf(substream) ((substream)->runtime->dma_private)
#define snd_pcm_sgbuf_pages(size) snd_sgbuf_aligned_pages(size)
#define snd_pcm_sgbuf_get_addr(sgbuf,ofs) snd_sgbuf_get_addr(sgbuf,ofs)
struct page *snd_pcm_sgbuf_ops_page(snd_pcm_substream_t *substream, unsigned long offset);
#endif #endif
#ifdef CONFIG_SBUS #ifdef CONFIG_SBUS
int snd_pcm_lib_preallocate_sbus_pages(struct sbus_dev *sdev, int snd_pcm_lib_preallocate_sbus_pages(struct sbus_dev *sdev,
snd_pcm_substream_t *substream, snd_pcm_substream_t *substream,
......
#ifndef __SOUND_PCM_SGBUF_H
#define __SOUND_PCM_SGBUF_H
/*
* Scatter-Gather PCM access
*
* Copyright (c) by Takashi Iwai <tiwai@suse.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
struct snd_sg_page {
void *buf;
dma_addr_t addr;
};
struct snd_sg_buf {
int size; /* allocated byte size (= runtime->dma_bytes) */
int pages; /* allocated pages */
int tblsize; /* allocated table size */
struct snd_sg_page *table;
struct page **page_table;
struct pci_dev *pci;
};
typedef struct snd_sg_buf snd_pcm_sgbuf_t; /* for magic cast */
/*
* return the pages matching with the given byte size
*/
static inline unsigned int snd_pcm_sgbuf_pages(size_t size)
{
return (size + PAGE_SIZE - 1) >> PAGE_SHIFT;
}
/*
* return the physical address at the corresponding offset
*/
static inline dma_addr_t snd_pcm_sgbuf_get_addr(struct snd_sg_buf *sgbuf, size_t offset)
{
return sgbuf->table[offset >> PAGE_SHIFT].addr + offset % PAGE_SIZE;
}
void *snd_pcm_sgbuf_alloc_pages(struct pci_dev *pci, size_t size, struct snd_pcm_dma_buffer *dmab);
int snd_pcm_sgbuf_free_pages(struct snd_pcm_dma_buffer *dmab);
int snd_pcm_lib_preallocate_sg_pages(struct pci_dev *pci, snd_pcm_substream_t *substream, size_t size, size_t max);
int snd_pcm_lib_preallocate_sg_pages_for_all(struct pci_dev *pci, snd_pcm_t *pcm, size_t size, size_t max);
#define _snd_pcm_substream_sgbuf(substream) ((substream)->runtime->dma_private)
#define snd_pcm_substream_sgbuf(substream) snd_magic_cast(snd_pcm_sgbuf_t, _snd_pcm_substream_sgbuf(substream), return -ENXIO)
struct page *snd_pcm_sgbuf_ops_page(snd_pcm_substream_t *substream, unsigned long offset);
#endif /* __SOUND_PCM_SGBUF_H */
...@@ -174,7 +174,7 @@ snd_seq_port_callback_t *snd_port_alloc_callback(void); ...@@ -174,7 +174,7 @@ snd_seq_port_callback_t *snd_port_alloc_callback(void);
/* port attach/detach */ /* port attach/detach */
int snd_seq_event_port_attach(int client, snd_seq_port_callback_t *pcbp, int snd_seq_event_port_attach(int client, snd_seq_port_callback_t *pcbp,
int cap, int type, int midi_channels, char *portname); int cap, int type, int midi_channels, int midi_voices, char *portname);
int snd_seq_event_port_detach(int client, int port); int snd_seq_event_port_detach(int client, int port);
#endif /* __SOUND_SEQ_KERNEL_H */ #endif /* __SOUND_SEQ_KERNEL_H */
...@@ -108,7 +108,7 @@ static inline int _snd_magic_bad(void *obj, unsigned long magic) ...@@ -108,7 +108,7 @@ static inline int _snd_magic_bad(void *obj, unsigned long magic)
#define snd_pcm_proc_private_t_magic 0xa15a0104 #define snd_pcm_proc_private_t_magic 0xa15a0104
#define snd_pcm_oss_file_t_magic 0xa15a0105 #define snd_pcm_oss_file_t_magic 0xa15a0105
#define snd_mixer_oss_t_magic 0xa15a0106 #define snd_mixer_oss_t_magic 0xa15a0106
#define snd_pcm_sgbuf_t_magic 0xa15a0107 // #define snd_pcm_sgbuf_t_magic 0xa15a0107
#define snd_info_private_data_t_magic 0xa15a0201 #define snd_info_private_data_t_magic 0xa15a0201
#define snd_info_entry_t_magic 0xa15a0202 #define snd_info_entry_t_magic 0xa15a0202
...@@ -174,6 +174,7 @@ static inline int _snd_magic_bad(void *obj, unsigned long magic) ...@@ -174,6 +174,7 @@ static inline int _snd_magic_bad(void *obj, unsigned long magic)
#define m3_dma_t_magic 0xa15a3202 #define m3_dma_t_magic 0xa15a3202
#define nm256_t_magic 0xa15a3301 #define nm256_t_magic 0xa15a3301
#define nm256_dma_t_magic 0xa15a3302 #define nm256_dma_t_magic 0xa15a3302
#define sam9407_t_magic 0xa15a3401
#define pmac_t_magic 0xa15a3501 #define pmac_t_magic 0xa15a3501
#define ali_t_magic 0xa15a3601 #define ali_t_magic 0xa15a3601
#define mtpav_t_magic 0xa15a3701 #define mtpav_t_magic 0xa15a3701
...@@ -190,6 +191,8 @@ static inline int _snd_magic_bad(void *obj, unsigned long magic) ...@@ -190,6 +191,8 @@ static inline int _snd_magic_bad(void *obj, unsigned long magic)
#define snd_usb_midi_t_magic 0xa15a3f01 #define snd_usb_midi_t_magic 0xa15a3f01
#define snd_usb_midi_out_endpoint_t_magic 0xa15a3f02 #define snd_usb_midi_out_endpoint_t_magic 0xa15a3f02
#define snd_usb_midi_in_endpoint_t_magic 0xa15a3f03 #define snd_usb_midi_in_endpoint_t_magic 0xa15a3f03
#define ak4117_t_magic 0xa15a4000
#define psic_t_magic 0xa15a4100
#else #else
......
...@@ -3,7 +3,8 @@ ...@@ -3,7 +3,8 @@
/* /*
* Timer abstract layer * Timer abstract layer
* Copyright (c) by Jaroslav Kysela <perex@suse.cz> * Copyright (c) by Jaroslav Kysela <perex@suse.cz>,
* Abramo Bagnara <abramo@alsa-project.org>
* *
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
...@@ -29,11 +30,15 @@ typedef enum sndrv_timer_class snd_timer_class_t; ...@@ -29,11 +30,15 @@ typedef enum sndrv_timer_class snd_timer_class_t;
typedef enum sndrv_timer_slave_class snd_timer_slave_class_t; typedef enum sndrv_timer_slave_class snd_timer_slave_class_t;
typedef enum sndrv_timer_global snd_timer_global_t; typedef enum sndrv_timer_global snd_timer_global_t;
typedef struct sndrv_timer_id snd_timer_id_t; typedef struct sndrv_timer_id snd_timer_id_t;
typedef struct sndrv_timer_ginfo snd_timer_ginfo_t;
typedef struct sndrv_timer_gparams snd_timer_gparams_t;
typedef struct sndrv_timer_gstatus snd_timer_gstatus_t;
typedef struct sndrv_timer_select snd_timer_select_t; typedef struct sndrv_timer_select snd_timer_select_t;
typedef struct sndrv_timer_info snd_timer_info_t; typedef struct sndrv_timer_info snd_timer_info_t;
typedef struct sndrv_timer_params snd_timer_params_t; typedef struct sndrv_timer_params snd_timer_params_t;
typedef struct sndrv_timer_status snd_timer_status_t; typedef struct sndrv_timer_status snd_timer_status_t;
typedef struct sndrv_timer_read snd_timer_read_t; typedef struct sndrv_timer_read snd_timer_read_t;
typedef struct sndrv_timer_tread snd_timer_tread_t;
#define _snd_timer_chip(timer) ((timer)->private_data) #define _snd_timer_chip(timer) ((timer)->private_data)
#define snd_timer_chip(timer) snd_magic_cast1(chip_t, _snd_timer_chip(timer), return -ENXIO) #define snd_timer_chip(timer) snd_magic_cast1(chip_t, _snd_timer_chip(timer), return -ENXIO)
...@@ -54,16 +59,21 @@ typedef struct sndrv_timer_read snd_timer_read_t; ...@@ -54,16 +59,21 @@ typedef struct sndrv_timer_read snd_timer_read_t;
#define SNDRV_TIMER_IFLG_AUTO 0x00000008 /* auto restart */ #define SNDRV_TIMER_IFLG_AUTO 0x00000008 /* auto restart */
#define SNDRV_TIMER_IFLG_FAST 0x00000010 /* fast callback (do not use tasklet) */ #define SNDRV_TIMER_IFLG_FAST 0x00000010 /* fast callback (do not use tasklet) */
#define SNDRV_TIMER_IFLG_CALLBACK 0x00000020 /* timer callback is active */ #define SNDRV_TIMER_IFLG_CALLBACK 0x00000020 /* timer callback is active */
#define SNDRV_TIMER_IFLG_EXCLUSIVE 0x00000040 /* exclusive owner - no more instances */
#define SNDRV_TIMER_FLG_CHANGE 0x00000001 #define SNDRV_TIMER_FLG_CHANGE 0x00000001
#define SNDRV_TIMER_FLG_RESCHED 0x00000002 /* need reschedule */ #define SNDRV_TIMER_FLG_RESCHED 0x00000002 /* need reschedule */
typedef void (*snd_timer_callback_t) (snd_timer_instance_t * timeri, unsigned long ticks, unsigned long resolution); typedef void (*snd_timer_callback_t) (snd_timer_instance_t * timeri, unsigned long ticks, unsigned long resolution);
typedef void (*snd_timer_ccallback_t) (snd_timer_instance_t * timeri, enum sndrv_timer_event event,
struct timespec * tstamp, unsigned long resolution);
struct _snd_timer_hardware { struct _snd_timer_hardware {
/* -- must be filled with low-level driver */ /* -- must be filled with low-level driver */
unsigned int flags; /* various flags */ unsigned int flags; /* various flags */
unsigned long resolution; /* average timer resolution for one tick in nsec */ unsigned long resolution; /* average timer resolution for one tick in nsec */
unsigned long resolution_min; /* minimal resolution */
unsigned long resolution_max; /* maximal resolution */
unsigned long ticks; /* max timer ticks per interrupt */ unsigned long ticks; /* max timer ticks per interrupt */
/* -- low-level functions -- */ /* -- low-level functions -- */
int (*open) (snd_timer_t * timer); int (*open) (snd_timer_t * timer);
...@@ -71,6 +81,8 @@ struct _snd_timer_hardware { ...@@ -71,6 +81,8 @@ struct _snd_timer_hardware {
unsigned long (*c_resolution) (snd_timer_t * timer); unsigned long (*c_resolution) (snd_timer_t * timer);
int (*start) (snd_timer_t * timer); int (*start) (snd_timer_t * timer);
int (*stop) (snd_timer_t * timer); int (*stop) (snd_timer_t * timer);
int (*set_period) (snd_timer_t * timer, unsigned long period_num, unsigned long period_den);
int (*precise_resolution) (snd_timer_t * timer, unsigned long *num, unsigned long *den);
}; };
struct _snd_timer { struct _snd_timer {
...@@ -102,6 +114,7 @@ struct _snd_timer_instance { ...@@ -102,6 +114,7 @@ struct _snd_timer_instance {
void *private_data; void *private_data;
void (*private_free) (snd_timer_instance_t *ti); void (*private_free) (snd_timer_instance_t *ti);
snd_timer_callback_t callback; snd_timer_callback_t callback;
snd_timer_ccallback_t ccallback;
void *callback_data; void *callback_data;
unsigned long ticks; /* auto-load ticks when expired */ unsigned long ticks; /* auto-load ticks when expired */
unsigned long cticks; /* current ticks */ unsigned long cticks; /* current ticks */
...@@ -123,20 +136,19 @@ struct _snd_timer_instance { ...@@ -123,20 +136,19 @@ struct _snd_timer_instance {
*/ */
extern int snd_timer_new(snd_card_t *card, char *id, snd_timer_id_t *tid, snd_timer_t ** rtimer); extern int snd_timer_new(snd_card_t *card, char *id, snd_timer_id_t *tid, snd_timer_t ** rtimer);
extern void snd_timer_notify(snd_timer_t *timer, enum sndrv_timer_event event, struct timespec *tstamp);
extern int snd_timer_global_new(char *id, int device, snd_timer_t **rtimer); extern int snd_timer_global_new(char *id, int device, snd_timer_t **rtimer);
extern int snd_timer_global_free(snd_timer_t *timer); extern int snd_timer_global_free(snd_timer_t *timer);
extern int snd_timer_global_register(snd_timer_t *timer); extern int snd_timer_global_register(snd_timer_t *timer);
extern int snd_timer_global_unregister(snd_timer_t *timer); extern int snd_timer_global_unregister(snd_timer_t *timer);
extern snd_timer_instance_t *snd_timer_open(char *owner, snd_timer_id_t *tid, unsigned int slave_id); extern int snd_timer_open(snd_timer_instance_t ** ti, char *owner, snd_timer_id_t *tid, unsigned int slave_id);
extern int snd_timer_close(snd_timer_instance_t * timeri); extern int snd_timer_close(snd_timer_instance_t * timeri);
extern int snd_timer_set_owner(snd_timer_instance_t * timeri, pid_t pid, gid_t gid);
extern int snd_timer_reset_owner(snd_timer_instance_t * timeri);
extern int snd_timer_set_resolution(snd_timer_instance_t * timeri, unsigned long resolution);
extern unsigned long snd_timer_resolution(snd_timer_instance_t * timeri); extern unsigned long snd_timer_resolution(snd_timer_instance_t * timeri);
extern int snd_timer_start(snd_timer_instance_t * timeri, unsigned int ticks); extern int snd_timer_start(snd_timer_instance_t * timeri, unsigned int ticks);
extern int snd_timer_stop(snd_timer_instance_t * timeri); extern int snd_timer_stop(snd_timer_instance_t * timeri);
extern int snd_timer_continue(snd_timer_instance_t * timeri); extern int snd_timer_continue(snd_timer_instance_t * timeri);
extern int snd_timer_pause(snd_timer_instance_t * timeri);
extern void snd_timer_interrupt(snd_timer_t * timer, unsigned long ticks_left); extern void snd_timer_interrupt(snd_timer_t * timer, unsigned long ticks_left);
......
...@@ -365,10 +365,16 @@ struct _snd_trident_voice { ...@@ -365,10 +365,16 @@ struct _snd_trident_voice {
int running: 1, int running: 1,
capture: 1, capture: 1,
spdif: 1, spdif: 1,
foldback: 1; foldback: 1,
isync: 1,
isync2: 1,
isync3: 1;
int foldback_chan; /* foldback subdevice number */ int foldback_chan; /* foldback subdevice number */
unsigned int stimer; /* global sample timer (to detect spurious interrupts) */ unsigned int stimer; /* global sample timer (to detect spurious interrupts) */
unsigned int spurious_threshold; /* spurious threshold */ unsigned int spurious_threshold; /* spurious threshold */
unsigned int isync_mark;
unsigned int isync_max;
unsigned int isync_ESO;
/* --- */ /* --- */
...@@ -448,6 +454,7 @@ struct _snd_trident { ...@@ -448,6 +454,7 @@ struct _snd_trident {
snd_seq_device_t *seq_dev; snd_seq_device_t *seq_dev;
ac97_t *ac97; ac97_t *ac97;
ac97_t *ac97_sec;
unsigned int musicvol_wavevol; unsigned int musicvol_wavevol;
snd_trident_pcm_mixer_t pcm_mixer[32]; snd_trident_pcm_mixer_t pcm_mixer[32];
......
/* include/version.h. Generated by configure. */ /* include/version.h. Generated by configure. */
#define CONFIG_SND_VERSION "0.9.0rc7" #define CONFIG_SND_VERSION "0.9.2"
#define CONFIG_SND_DATE " (Tue Feb 25 13:00:09 2003 UTC)" #define CONFIG_SND_DATE " (Thu Mar 20 13:31:57 2003 UTC)"
...@@ -14,8 +14,10 @@ endif ...@@ -14,8 +14,10 @@ endif
snd-pcm-objs := pcm.o pcm_native.o pcm_lib.o pcm_timer.o pcm_misc.o \ snd-pcm-objs := pcm.o pcm_native.o pcm_lib.o pcm_timer.o pcm_misc.o \
pcm_memory.o pcm_memory.o
snd-page-alloc-objs := memalloc.o
ifeq ($(CONFIG_PCI),y) ifeq ($(CONFIG_PCI),y)
snd-pcm-objs += pcm_sgbuf.o snd-page-alloc-objs += sgbuf.o memory_wrapper.o
endif endif
snd-rawmidi-objs := rawmidi.o snd-rawmidi-objs := rawmidi.o
...@@ -31,72 +33,72 @@ endif ...@@ -31,72 +33,72 @@ endif
obj-$(CONFIG_SND_HWDEP) += snd-hwdep.o obj-$(CONFIG_SND_HWDEP) += snd-hwdep.o
obj-$(CONFIG_SND_MIXER_OSS) += oss/ obj-$(CONFIG_SND_MIXER_OSS) += oss/
obj-$(CONFIG_SND_PCM_OSS) += snd-pcm.o snd-timer.o oss/ obj-$(CONFIG_SND_PCM_OSS) += snd-pcm.o snd-timer.o snd-page-alloc.o oss/
obj-$(CONFIG_SND_SEQUENCER) += snd-timer.o seq/ obj-$(CONFIG_SND_SEQUENCER) += snd-timer.o seq/
obj-$(CONFIG_SND_BIT32_EMUL) += ioctl32/ obj-$(CONFIG_SND_BIT32_EMUL) += ioctl32/
# Toplevel Module Dependency # Toplevel Module Dependency
obj-$(CONFIG_SND_DUMMY) += snd-pcm.o snd-timer.o snd.o obj-$(CONFIG_SND_DUMMY) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o
obj-$(CONFIG_SND_VIRMIDI) += snd-rawmidi.o snd.o snd-timer.o obj-$(CONFIG_SND_VIRMIDI) += snd-rawmidi.o snd.o snd-timer.o
obj-$(CONFIG_SND_SERIAL_U16550) += snd-rawmidi.o snd.o snd-timer.o obj-$(CONFIG_SND_SERIAL_U16550) += snd-rawmidi.o snd.o snd-timer.o
obj-$(CONFIG_SND_MTPAV) += snd-rawmidi.o snd.o snd-timer.o obj-$(CONFIG_SND_MTPAV) += snd-rawmidi.o snd.o snd-timer.o
obj-$(CONFIG_SND_MPU401) += snd-rawmidi.o snd.o snd-timer.o obj-$(CONFIG_SND_MPU401) += snd-rawmidi.o snd.o snd-timer.o
obj-$(CONFIG_SND_ALS100) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o obj-$(CONFIG_SND_ALS100) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o
obj-$(CONFIG_SND_AZT2320) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o obj-$(CONFIG_SND_AZT2320) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o
obj-$(CONFIG_SND_CMI8330) += snd-pcm.o snd-timer.o snd.o obj-$(CONFIG_SND_CMI8330) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o
obj-$(CONFIG_SND_DT019X) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o obj-$(CONFIG_SND_DT019X) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o
obj-$(CONFIG_SND_ES18XX) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o obj-$(CONFIG_SND_ES18XX) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o
obj-$(CONFIG_SND_OPL3SA2) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o obj-$(CONFIG_SND_OPL3SA2) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o
obj-$(CONFIG_SND_SGALAXY) += snd-pcm.o snd-timer.o snd.o obj-$(CONFIG_SND_SGALAXY) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o
obj-$(CONFIG_SND_AD1816A) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o obj-$(CONFIG_SND_AD1816A) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o
obj-$(CONFIG_SND_AD1848) += snd-pcm.o snd-timer.o snd.o obj-$(CONFIG_SND_AD1848) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o
obj-$(CONFIG_SND_CS4231) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o obj-$(CONFIG_SND_CS4231) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o
obj-$(CONFIG_SND_CS4232) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o obj-$(CONFIG_SND_CS4232) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o
obj-$(CONFIG_SND_CS4236) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o obj-$(CONFIG_SND_CS4236) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o
obj-$(CONFIG_SND_ES1688) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o obj-$(CONFIG_SND_ES1688) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o
obj-$(CONFIG_SND_GUSCLASSIC) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o obj-$(CONFIG_SND_GUSCLASSIC) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o
obj-$(CONFIG_SND_GUSMAX) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o obj-$(CONFIG_SND_GUSMAX) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o
obj-$(CONFIG_SND_GUSEXTREME) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o obj-$(CONFIG_SND_GUSEXTREME) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o
obj-$(CONFIG_SND_INTERWAVE) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o obj-$(CONFIG_SND_INTERWAVE) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o
obj-$(CONFIG_SND_INTERWAVE_STB) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o obj-$(CONFIG_SND_INTERWAVE_STB) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o
obj-$(CONFIG_SND_OPTI92X_AD1848) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o obj-$(CONFIG_SND_OPTI92X_AD1848) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o
obj-$(CONFIG_SND_OPTI92X_CS4231) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o obj-$(CONFIG_SND_OPTI92X_CS4231) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o
obj-$(CONFIG_SND_OPTI93X) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o obj-$(CONFIG_SND_OPTI93X) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o
obj-$(CONFIG_SND_SB8) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o obj-$(CONFIG_SND_SB8) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o
obj-$(CONFIG_SND_SB16) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o obj-$(CONFIG_SND_SB16) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o
obj-$(CONFIG_SND_SBAWE) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o obj-$(CONFIG_SND_SBAWE) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o
obj-$(CONFIG_SND_ES968) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o obj-$(CONFIG_SND_ES968) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o
obj-$(CONFIG_SND_WAVEFRONT) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o obj-$(CONFIG_SND_WAVEFRONT) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o
obj-$(CONFIG_SND_ALS4000) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o obj-$(CONFIG_SND_ALS4000) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o
obj-$(CONFIG_SND_CMIPCI) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o obj-$(CONFIG_SND_CMIPCI) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o
obj-$(CONFIG_SND_CS4281) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o obj-$(CONFIG_SND_CS4281) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o
obj-$(CONFIG_SND_ENS1370) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o obj-$(CONFIG_SND_ENS1370) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o
obj-$(CONFIG_SND_ENS1371) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o obj-$(CONFIG_SND_ENS1371) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o
obj-$(CONFIG_SND_ES1938) += snd-pcm.o snd-timer.o snd.o snd-hwdep.o snd-rawmidi.o obj-$(CONFIG_SND_ES1938) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-hwdep.o snd-rawmidi.o
obj-$(CONFIG_SND_ES1968) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o obj-$(CONFIG_SND_ES1968) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o
obj-$(CONFIG_SND_FM801) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o obj-$(CONFIG_SND_FM801) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o
obj-$(CONFIG_SND_ICE1712) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o obj-$(CONFIG_SND_ICE1712) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o
obj-$(CONFIG_SND_INTEL8X0) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o obj-$(CONFIG_SND_INTEL8X0) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o
obj-$(CONFIG_SND_MAESTRO3) += snd-pcm.o snd-timer.o snd.o obj-$(CONFIG_SND_MAESTRO3) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o
obj-$(CONFIG_SND_RME32) += snd-pcm.o snd-timer.o snd.o obj-$(CONFIG_SND_RME32) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o
obj-$(CONFIG_SND_RME96) += snd-pcm.o snd-timer.o snd.o obj-$(CONFIG_SND_RME96) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o
obj-$(CONFIG_SND_SONICVIBES) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o obj-$(CONFIG_SND_SONICVIBES) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o
obj-$(CONFIG_SND_VIA82XX) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o obj-$(CONFIG_SND_VIA82XX) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o
obj-$(CONFIG_SND_ALI5451) += snd.o snd-rawmidi.o snd-timer.o snd-pcm.o obj-$(CONFIG_SND_ALI5451) += snd.o snd-rawmidi.o snd-timer.o snd-page-alloc.o snd-pcm.o
obj-$(CONFIG_SND_CS46XX) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o obj-$(CONFIG_SND_CS46XX) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o
obj-$(CONFIG_SND_EMU10K1) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o obj-$(CONFIG_SND_EMU10K1) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o
obj-$(CONFIG_SND_KORG1212) += snd-pcm.o snd-timer.o snd.o obj-$(CONFIG_SND_KORG1212) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o
obj-$(CONFIG_SND_NM256) += snd-pcm.o snd-timer.o snd.o obj-$(CONFIG_SND_NM256) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o
obj-$(CONFIG_SND_RME9652) += snd-pcm.o snd-timer.o snd.o obj-$(CONFIG_SND_RME9652) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o
obj-$(CONFIG_SND_HDSP) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o obj-$(CONFIG_SND_HDSP) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o
obj-$(CONFIG_SND_TRIDENT) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o obj-$(CONFIG_SND_TRIDENT) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o
obj-$(CONFIG_SND_YMFPCI) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o obj-$(CONFIG_SND_YMFPCI) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o
obj-$(CONFIG_SND_POWERMAC) += snd-pcm.o snd-timer.o snd.o obj-$(CONFIG_SND_POWERMAC) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o
obj-$(CONFIG_SND_SA11XX_UDA1341) += snd-pcm.o snd-timer.o snd.o obj-$(CONFIG_SND_SA11XX_UDA1341) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o
ifeq ($(CONFIG_SND_SB16_CSP),y) ifeq ($(CONFIG_SND_SB16_CSP),y)
obj-$(CONFIG_SND_SB16) += snd-hwdep.o obj-$(CONFIG_SND_SB16) += snd-hwdep.o
obj-$(CONFIG_SND_SBAWE) += snd-hwdep.o obj-$(CONFIG_SND_SBAWE) += snd-hwdep.o
endif endif
obj-$(CONFIG_SND_USB_AUDIO) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o obj-$(CONFIG_SND_USB_AUDIO) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o
obj-m := $(sort $(obj-m)) obj-m := $(sort $(obj-m))
...@@ -714,14 +714,14 @@ static int snd_ctl_ioctl(struct inode *inode, struct file *file, ...@@ -714,14 +714,14 @@ static int snd_ctl_ioctl(struct inode *inode, struct file *file,
return -EFAULT; return -EFAULT;
if (!capable(CAP_SYS_ADMIN)) if (!capable(CAP_SYS_ADMIN))
return -EPERM; return -EPERM;
err = -ENOPROTOOPT;
#ifdef CONFIG_PM #ifdef CONFIG_PM
if (card->set_power_state) { if (card->set_power_state) {
snd_power_lock(card); snd_power_lock(card);
err = card->set_power_state(card, err); err = card->set_power_state(card, err);
snd_power_unlock(card); snd_power_unlock(card);
} } else
#endif #endif
err = -ENOPROTOOPT;
return err; return err;
case SNDRV_CTL_IOCTL_POWER_STATE: case SNDRV_CTL_IOCTL_POWER_STATE:
#ifdef CONFIG_PM #ifdef CONFIG_PM
......
...@@ -220,7 +220,7 @@ static ssize_t snd_info_entry_read(struct file *file, char *buffer, ...@@ -220,7 +220,7 @@ static ssize_t snd_info_entry_read(struct file *file, char *buffer,
buf = data->rbuffer; buf = data->rbuffer;
if (buf == NULL) if (buf == NULL)
return -EIO; return -EIO;
if (file->f_pos >= buf->size) if (file->f_pos >= (long)buf->size)
return 0; return 0;
size = buf->size < count ? buf->size : count; size = buf->size < count ? buf->size : count;
size1 = buf->size - file->f_pos; size1 = buf->size - file->f_pos;
...@@ -260,7 +260,7 @@ static ssize_t snd_info_entry_write(struct file *file, const char *buffer, ...@@ -260,7 +260,7 @@ static ssize_t snd_info_entry_write(struct file *file, const char *buffer,
return -EIO; return -EIO;
if (file->f_pos < 0) if (file->f_pos < 0)
return -EINVAL; return -EINVAL;
if (file->f_pos >= buf->len) if (file->f_pos >= (long)buf->len)
return -ENOMEM; return -ENOMEM;
size = buf->len < count ? buf->len : count; size = buf->len < count ? buf->len : count;
size1 = buf->len - file->f_pos; size1 = buf->len - file->f_pos;
...@@ -268,7 +268,7 @@ static ssize_t snd_info_entry_write(struct file *file, const char *buffer, ...@@ -268,7 +268,7 @@ static ssize_t snd_info_entry_write(struct file *file, const char *buffer,
size = size1; size = size1;
if (copy_from_user(buf->buffer + file->f_pos, buffer, size)) if (copy_from_user(buf->buffer + file->f_pos, buffer, size))
return -EFAULT; return -EFAULT;
if (buf->size < file->f_pos + size) if ((long)buf->size < file->f_pos + size)
buf->size = file->f_pos + size; buf->size = file->f_pos + size;
file->f_pos += size; file->f_pos += size;
break; break;
......
...@@ -495,6 +495,22 @@ void snd_card_info_read_oss(snd_info_buffer_t * buffer) ...@@ -495,6 +495,22 @@ void snd_card_info_read_oss(snd_info_buffer_t * buffer)
#endif #endif
#ifdef MODULE
static snd_info_entry_t *snd_card_module_info_entry;
static void snd_card_module_info_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer)
{
int idx;
snd_card_t *card;
for (idx = 0; idx < SNDRV_CARDS; idx++) {
read_lock(&snd_card_rwlock);
if ((card = snd_cards[idx]) != NULL)
snd_iprintf(buffer, "%i %s\n", idx, card->module->name);
read_unlock(&snd_card_rwlock);
}
}
#endif
int __init snd_card_info_init(void) int __init snd_card_info_init(void)
{ {
snd_info_entry_t *entry; snd_info_entry_t *entry;
...@@ -509,6 +525,20 @@ int __init snd_card_info_init(void) ...@@ -509,6 +525,20 @@ int __init snd_card_info_init(void)
return -ENOMEM; return -ENOMEM;
} }
snd_card_info_entry = entry; snd_card_info_entry = entry;
#ifdef MODULE
entry = snd_info_create_module_entry(THIS_MODULE, "modules", NULL);
if (entry) {
entry->content = SNDRV_INFO_CONTENT_TEXT;
entry->c.text.read_size = PAGE_SIZE;
entry->c.text.read = snd_card_module_info_read;
if (snd_info_register(entry) < 0)
snd_info_free_entry(entry);
else
snd_card_module_info_entry = entry;
}
#endif
return 0; return 0;
} }
...@@ -516,6 +546,10 @@ int __exit snd_card_info_done(void) ...@@ -516,6 +546,10 @@ int __exit snd_card_info_done(void)
{ {
if (snd_card_info_entry) if (snd_card_info_entry)
snd_info_unregister(snd_card_info_entry); snd_info_unregister(snd_card_info_entry);
#ifdef MODULE
if (snd_card_module_info_entry)
snd_info_unregister(snd_card_module_info_entry);
#endif
return 0; return 0;
} }
......
...@@ -31,6 +31,7 @@ struct sndrv_hwdep_dsp_image32 { ...@@ -31,6 +31,7 @@ struct sndrv_hwdep_dsp_image32 {
unsigned char name[64]; unsigned char name[64];
u32 image; /* pointer */ u32 image; /* pointer */
u32 length; u32 length;
u32 driver_data;
} /* don't set packed attribute here */; } /* don't set packed attribute here */;
static int _snd_ioctl32_hwdep_dsp_image(unsigned int fd, unsigned int cmd, unsigned long arg, struct file *file, unsigned int native_ctl) static int _snd_ioctl32_hwdep_dsp_image(unsigned int fd, unsigned int cmd, unsigned long arg, struct file *file, unsigned int native_ctl)
...@@ -47,6 +48,7 @@ static int _snd_ioctl32_hwdep_dsp_image(unsigned int fd, unsigned int cmd, unsig ...@@ -47,6 +48,7 @@ static int _snd_ioctl32_hwdep_dsp_image(unsigned int fd, unsigned int cmd, unsig
memcpy(data.name, data32.name, sizeof(data.name)); memcpy(data.name, data32.name, sizeof(data.name));
data.image = A(data32.image); data.image = A(data32.image);
data.length = data32.length; data.length = data32.length;
data.driver_data = data32.driver_data;
oldseg = get_fs(); oldseg = get_fs();
set_fs(KERNEL_DS); set_fs(KERNEL_DS);
err = file->f_op->ioctl(file->f_dentry->d_inode, file, native_ctl, (unsigned long)&data); err = file->f_op->ioctl(file->f_dentry->d_inode, file, native_ctl, (unsigned long)&data);
......
...@@ -96,5 +96,8 @@ unsigned int snd_dma_pointer(unsigned long dma, unsigned int size) ...@@ -96,5 +96,8 @@ unsigned int snd_dma_pointer(unsigned long dma, unsigned int size)
if (result > size) if (result > size)
snd_printk(KERN_ERR "pointer (0x%x) for DMA #%ld is greater than transfer size (0x%x)\n", result, dma, size); snd_printk(KERN_ERR "pointer (0x%x) for DMA #%ld is greater than transfer size (0x%x)\n", result, dma, size);
#endif #endif
return result >= size ? 0 : size - result; if (result >= size || result == 0)
return 0;
else
return size - result;
} }
This diff is collapsed.
This diff is collapsed.
/*
* Copyright (c) by Jaroslav Kysela <perex@suse.cz>
* Takashi Iwai <tiwai@suse.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <linux/config.h>
#include <linux/version.h>
#include <linux/pci.h>
#include <sound/memalloc.h>
#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.
*/
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);
hwdev->dma_mask = dma_mask; /* restore */
if (ret) {
/* obtained address is out of range? */
if (((unsigned long)*dma_handle + size - 1) & rmask) {
/* reallocate with the proper mask */
pci_free_consistent(hwdev, size, ret, *dma_handle);
ret = pci_alloc_consistent(hwdev, size, dma_handle);
}
} else {
/* wish to success now with the proper mask... */
if (dma_mask != 0xffffffff)
ret = pci_alloc_consistent(hwdev, size, dma_handle);
}
return ret;
}
#endif /* HACK_PCI_ALLOC_CONSISTENT */
This diff is collapsed.
...@@ -386,10 +386,10 @@ static void snd_pcm_substream_proc_status_read(snd_info_entry_t *entry, snd_info ...@@ -386,10 +386,10 @@ static void snd_pcm_substream_proc_status_read(snd_info_entry_t *entry, snd_info
return; return;
} }
snd_iprintf(buffer, "state: %s\n", snd_pcm_state_name(status.state)); snd_iprintf(buffer, "state: %s\n", snd_pcm_state_name(status.state));
snd_iprintf(buffer, "trigger_time: %ld.%06ld\n", snd_iprintf(buffer, "trigger_time: %ld.%09ld\n",
status.trigger_tstamp.tv_sec, status.trigger_tstamp.tv_usec); status.trigger_tstamp.tv_sec, status.trigger_tstamp.tv_nsec);
snd_iprintf(buffer, "tstamp : %ld.%06ld\n", snd_iprintf(buffer, "tstamp : %ld.%09ld\n",
status.tstamp.tv_sec, status.tstamp.tv_usec); status.tstamp.tv_sec, status.tstamp.tv_nsec);
snd_iprintf(buffer, "delay : %ld\n", status.delay); snd_iprintf(buffer, "delay : %ld\n", status.delay);
snd_iprintf(buffer, "avail : %ld\n", status.avail); snd_iprintf(buffer, "avail : %ld\n", status.avail);
snd_iprintf(buffer, "avail_max : %ld\n", status.avail_max); snd_iprintf(buffer, "avail_max : %ld\n", status.avail_max);
...@@ -595,8 +595,6 @@ int snd_pcm_new_stream(snd_pcm_t *pcm, int stream, int substream_count) ...@@ -595,8 +595,6 @@ int snd_pcm_new_stream(snd_pcm_t *pcm, int stream, int substream_count)
snd_magic_kfree(substream); snd_magic_kfree(substream);
return err; return err;
} }
substream->dma_type = SNDRV_PCM_DMA_TYPE_UNKNOWN;
substream->dma_private = NULL;
spin_lock_init(&substream->timer_lock); spin_lock_init(&substream->timer_lock);
prev = substream; prev = substream;
} }
......
...@@ -137,7 +137,7 @@ int snd_pcm_update_hw_ptr_interrupt(snd_pcm_substream_t *substream) ...@@ -137,7 +137,7 @@ int snd_pcm_update_hw_ptr_interrupt(snd_pcm_substream_t *substream)
old_hw_ptr = runtime->status->hw_ptr; old_hw_ptr = runtime->status->hw_ptr;
pos = substream->ops->pointer(substream); pos = substream->ops->pointer(substream);
if (runtime->tstamp_mode & SNDRV_PCM_TSTAMP_MMAP) if (runtime->tstamp_mode & SNDRV_PCM_TSTAMP_MMAP)
snd_timestamp_now((snd_timestamp_t*)&runtime->status->tstamp); snd_timestamp_now((snd_timestamp_t*)&runtime->status->tstamp, runtime->tstamp_timespec);
#ifdef CONFIG_SND_DEBUG #ifdef CONFIG_SND_DEBUG
if (pos >= runtime->buffer_size) { if (pos >= runtime->buffer_size) {
snd_printk(KERN_ERR "BUG: stream = %i, pos = 0x%lx, buffer size = 0x%lx, period size = 0x%lx\n", substream->stream, pos, runtime->buffer_size, runtime->period_size); snd_printk(KERN_ERR "BUG: stream = %i, pos = 0x%lx, buffer size = 0x%lx, period size = 0x%lx\n", substream->stream, pos, runtime->buffer_size, runtime->period_size);
...@@ -198,7 +198,7 @@ int snd_pcm_update_hw_ptr(snd_pcm_substream_t *substream) ...@@ -198,7 +198,7 @@ int snd_pcm_update_hw_ptr(snd_pcm_substream_t *substream)
old_hw_ptr = runtime->status->hw_ptr; old_hw_ptr = runtime->status->hw_ptr;
pos = substream->ops->pointer(substream); pos = substream->ops->pointer(substream);
if (runtime->tstamp_mode & SNDRV_PCM_TSTAMP_MMAP) if (runtime->tstamp_mode & SNDRV_PCM_TSTAMP_MMAP)
snd_timestamp_now((snd_timestamp_t*)&runtime->status->tstamp); snd_timestamp_now((snd_timestamp_t*)&runtime->status->tstamp, runtime->tstamp_timespec);
#ifdef CONFIG_SND_DEBUG #ifdef CONFIG_SND_DEBUG
if (pos >= runtime->buffer_size) { if (pos >= runtime->buffer_size) {
snd_printk(KERN_ERR "BUG: stream = %i, pos = 0x%lx, buffer size = 0x%lx, period size = 0x%lx\n", substream->stream, pos, runtime->buffer_size, runtime->period_size); snd_printk(KERN_ERR "BUG: stream = %i, pos = 0x%lx, buffer size = 0x%lx, period size = 0x%lx\n", substream->stream, pos, runtime->buffer_size, runtime->period_size);
...@@ -2658,6 +2658,9 @@ EXPORT_SYMBOL(snd_pcm_lib_preallocate_isa_pages_for_all); ...@@ -2658,6 +2658,9 @@ EXPORT_SYMBOL(snd_pcm_lib_preallocate_isa_pages_for_all);
#ifdef CONFIG_PCI #ifdef CONFIG_PCI
EXPORT_SYMBOL(snd_pcm_lib_preallocate_pci_pages); EXPORT_SYMBOL(snd_pcm_lib_preallocate_pci_pages);
EXPORT_SYMBOL(snd_pcm_lib_preallocate_pci_pages_for_all); EXPORT_SYMBOL(snd_pcm_lib_preallocate_pci_pages_for_all);
EXPORT_SYMBOL(snd_pcm_lib_preallocate_sg_pages);
EXPORT_SYMBOL(snd_pcm_lib_preallocate_sg_pages_for_all);
EXPORT_SYMBOL(snd_pcm_sgbuf_ops_page);
#endif #endif
#ifdef CONFIG_SBUS #ifdef CONFIG_SBUS
EXPORT_SYMBOL(snd_pcm_lib_preallocate_sbus_pages); EXPORT_SYMBOL(snd_pcm_lib_preallocate_sbus_pages);
......
This diff is collapsed.
...@@ -30,6 +30,7 @@ ...@@ -30,6 +30,7 @@
#include <sound/info.h> #include <sound/info.h>
#include <sound/pcm.h> #include <sound/pcm.h>
#include <sound/pcm_params.h> #include <sound/pcm_params.h>
#include <sound/timer.h>
#include <sound/minors.h> #include <sound/minors.h>
/* /*
...@@ -514,9 +515,9 @@ int snd_pcm_status(snd_pcm_substream_t *substream, ...@@ -514,9 +515,9 @@ int snd_pcm_status(snd_pcm_substream_t *substream,
if (runtime->tstamp_mode & SNDRV_PCM_TSTAMP_MMAP) if (runtime->tstamp_mode & SNDRV_PCM_TSTAMP_MMAP)
status->tstamp = runtime->status->tstamp; status->tstamp = runtime->status->tstamp;
else else
snd_timestamp_now(&status->tstamp); snd_timestamp_now(&status->tstamp, runtime->tstamp_timespec);
} else } else
snd_timestamp_now(&status->tstamp); snd_timestamp_now(&status->tstamp, runtime->tstamp_timespec);
status->appl_ptr = runtime->control->appl_ptr; status->appl_ptr = runtime->control->appl_ptr;
status->hw_ptr = runtime->status->hw_ptr; status->hw_ptr = runtime->status->hw_ptr;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
...@@ -585,15 +586,15 @@ static int snd_pcm_channel_info(snd_pcm_substream_t * substream, snd_pcm_channel ...@@ -585,15 +586,15 @@ static int snd_pcm_channel_info(snd_pcm_substream_t * substream, snd_pcm_channel
return 0; return 0;
} }
static void snd_pcm_trigger_time(snd_pcm_substream_t *substream) static void snd_pcm_trigger_tstamp(snd_pcm_substream_t *substream)
{ {
snd_pcm_runtime_t *runtime = substream->runtime; snd_pcm_runtime_t *runtime = substream->runtime;
if (runtime->trigger_master == NULL) if (runtime->trigger_master == NULL)
return; return;
if (runtime->trigger_master == substream) { if (runtime->trigger_master == substream) {
snd_timestamp_now(&runtime->trigger_tstamp); snd_timestamp_now(&runtime->trigger_tstamp, runtime->tstamp_timespec);
} else { } else {
snd_pcm_trigger_time(runtime->trigger_master); snd_pcm_trigger_tstamp(runtime->trigger_master);
runtime->trigger_tstamp = runtime->trigger_master->runtime->trigger_tstamp; runtime->trigger_tstamp = runtime->trigger_master->runtime->trigger_tstamp;
} }
runtime->trigger_master = NULL; runtime->trigger_master = NULL;
...@@ -668,13 +669,15 @@ static inline int snd_pcm_do_start(snd_pcm_substream_t *substream, int state) ...@@ -668,13 +669,15 @@ static inline int snd_pcm_do_start(snd_pcm_substream_t *substream, int state)
static inline void snd_pcm_post_start(snd_pcm_substream_t *substream, int state) static inline void snd_pcm_post_start(snd_pcm_substream_t *substream, int state)
{ {
snd_pcm_runtime_t *runtime = substream->runtime; snd_pcm_runtime_t *runtime = substream->runtime;
snd_pcm_trigger_time(substream); snd_pcm_trigger_tstamp(substream);
runtime->status->state = SNDRV_PCM_STATE_RUNNING; runtime->status->state = SNDRV_PCM_STATE_RUNNING;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
runtime->silence_size > 0) runtime->silence_size > 0)
snd_pcm_playback_silence(substream, ULONG_MAX); snd_pcm_playback_silence(substream, ULONG_MAX);
if (runtime->sleep_min) if (runtime->sleep_min)
snd_pcm_tick_prepare(substream); snd_pcm_tick_prepare(substream);
if (substream->timer)
snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MSTART, &runtime->trigger_tstamp);
} }
/** /**
...@@ -703,7 +706,9 @@ static inline int snd_pcm_do_stop(snd_pcm_substream_t *substream, int state) ...@@ -703,7 +706,9 @@ static inline int snd_pcm_do_stop(snd_pcm_substream_t *substream, int state)
static inline void snd_pcm_post_stop(snd_pcm_substream_t *substream, int state) static inline void snd_pcm_post_stop(snd_pcm_substream_t *substream, int state)
{ {
snd_pcm_runtime_t *runtime = substream->runtime; snd_pcm_runtime_t *runtime = substream->runtime;
snd_pcm_trigger_time(substream); snd_pcm_trigger_tstamp(substream);
if (substream->timer)
snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MSTOP, &runtime->trigger_tstamp);
runtime->status->state = state; runtime->status->state = state;
snd_pcm_tick_set(substream, 0); snd_pcm_tick_set(substream, 0);
wake_up(&runtime->sleep); wake_up(&runtime->sleep);
...@@ -741,15 +746,19 @@ static inline int snd_pcm_do_pause(snd_pcm_substream_t *substream, int push) ...@@ -741,15 +746,19 @@ static inline int snd_pcm_do_pause(snd_pcm_substream_t *substream, int push)
static inline void snd_pcm_post_pause(snd_pcm_substream_t *substream, int push) static inline void snd_pcm_post_pause(snd_pcm_substream_t *substream, int push)
{ {
snd_pcm_runtime_t *runtime = substream->runtime; snd_pcm_runtime_t *runtime = substream->runtime;
snd_pcm_trigger_time(substream); snd_pcm_trigger_tstamp(substream);
if (push) { if (push) {
runtime->status->state = SNDRV_PCM_STATE_PAUSED; runtime->status->state = SNDRV_PCM_STATE_PAUSED;
if (substream->timer)
snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MPAUSE, &runtime->trigger_tstamp);
snd_pcm_tick_set(substream, 0); snd_pcm_tick_set(substream, 0);
wake_up(&runtime->sleep); wake_up(&runtime->sleep);
} else { } else {
runtime->status->state = SNDRV_PCM_STATE_RUNNING; runtime->status->state = SNDRV_PCM_STATE_RUNNING;
if (runtime->sleep_min) if (runtime->sleep_min)
snd_pcm_tick_prepare(substream); snd_pcm_tick_prepare(substream);
if (substream->timer)
snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MCONTINUE, &runtime->trigger_tstamp);
} }
} }
...@@ -782,7 +791,9 @@ static inline int snd_pcm_do_suspend(snd_pcm_substream_t *substream, int state) ...@@ -782,7 +791,9 @@ static inline int snd_pcm_do_suspend(snd_pcm_substream_t *substream, int state)
static inline void snd_pcm_post_suspend(snd_pcm_substream_t *substream, int state) static inline void snd_pcm_post_suspend(snd_pcm_substream_t *substream, int state)
{ {
snd_pcm_runtime_t *runtime = substream->runtime; snd_pcm_runtime_t *runtime = substream->runtime;
snd_pcm_trigger_time(substream); snd_pcm_trigger_tstamp(substream);
if (substream->timer)
snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MPAUSE, &runtime->trigger_tstamp);
runtime->status->state = SNDRV_PCM_STATE_SUSPENDED; runtime->status->state = SNDRV_PCM_STATE_SUSPENDED;
snd_pcm_tick_set(substream, 0); snd_pcm_tick_set(substream, 0);
wake_up(&runtime->sleep); wake_up(&runtime->sleep);
...@@ -847,7 +858,9 @@ static inline int snd_pcm_do_resume(snd_pcm_substream_t *substream, int state) ...@@ -847,7 +858,9 @@ static inline int snd_pcm_do_resume(snd_pcm_substream_t *substream, int state)
static inline void snd_pcm_post_resume(snd_pcm_substream_t *substream, int state) static inline void snd_pcm_post_resume(snd_pcm_substream_t *substream, int state)
{ {
snd_pcm_runtime_t *runtime = substream->runtime; snd_pcm_runtime_t *runtime = substream->runtime;
snd_pcm_trigger_time(substream); snd_pcm_trigger_tstamp(substream);
if (substream->timer)
snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MCONTINUE, &runtime->trigger_tstamp);
runtime->status->state = runtime->status->suspended_state; runtime->status->state = runtime->status->suspended_state;
if (runtime->sleep_min) if (runtime->sleep_min)
snd_pcm_tick_prepare(substream); snd_pcm_tick_prepare(substream);
...@@ -1733,9 +1746,12 @@ static int snd_pcm_release_file(snd_pcm_file_t * pcm_file) ...@@ -1733,9 +1746,12 @@ static int snd_pcm_release_file(snd_pcm_file_t * pcm_file)
runtime = substream->runtime; runtime = substream->runtime;
str = substream->pstr; str = substream->pstr;
snd_pcm_unlink(substream); snd_pcm_unlink(substream);
if (substream->ops->hw_free != NULL) if (substream->open_flag) {
substream->ops->hw_free(substream); if (substream->ops->hw_free != NULL)
substream->ops->close(substream); substream->ops->hw_free(substream);
substream->ops->close(substream);
substream->open_flag = 0;
}
substream->ffile = NULL; substream->ffile = NULL;
snd_pcm_remove_file(str, pcm_file); snd_pcm_remove_file(str, pcm_file);
snd_pcm_release_substream(substream); snd_pcm_release_substream(substream);
...@@ -1784,6 +1800,7 @@ static int snd_pcm_open_file(struct file *file, ...@@ -1784,6 +1800,7 @@ static int snd_pcm_open_file(struct file *file,
snd_pcm_release_file(pcm_file); snd_pcm_release_file(pcm_file);
return err; return err;
} }
substream->open_flag = 1;
err = snd_pcm_hw_constraints_complete(substream); err = snd_pcm_hw_constraints_complete(substream);
if (err < 0) { if (err < 0) {
...@@ -2020,7 +2037,7 @@ snd_pcm_sframes_t snd_pcm_playback_forward(snd_pcm_substream_t *substream, snd_p ...@@ -2020,7 +2037,7 @@ snd_pcm_sframes_t snd_pcm_playback_forward(snd_pcm_substream_t *substream, snd_p
else else
frames -= frames % runtime->xfer_align; frames -= frames % runtime->xfer_align;
appl_ptr = runtime->control->appl_ptr + frames; appl_ptr = runtime->control->appl_ptr + frames;
if (appl_ptr >= runtime->boundary) if (appl_ptr >= (snd_pcm_sframes_t)runtime->boundary)
appl_ptr -= runtime->boundary; appl_ptr -= runtime->boundary;
runtime->control->appl_ptr = appl_ptr; runtime->control->appl_ptr = appl_ptr;
if (runtime->status->state == SNDRV_PCM_STATE_RUNNING && if (runtime->status->state == SNDRV_PCM_STATE_RUNNING &&
...@@ -2070,7 +2087,7 @@ snd_pcm_sframes_t snd_pcm_capture_forward(snd_pcm_substream_t *substream, snd_pc ...@@ -2070,7 +2087,7 @@ snd_pcm_sframes_t snd_pcm_capture_forward(snd_pcm_substream_t *substream, snd_pc
else else
frames -= frames % runtime->xfer_align; frames -= frames % runtime->xfer_align;
appl_ptr = runtime->control->appl_ptr + frames; appl_ptr = runtime->control->appl_ptr + frames;
if (appl_ptr >= runtime->boundary) if (appl_ptr >= (snd_pcm_sframes_t)runtime->boundary)
appl_ptr -= runtime->boundary; appl_ptr -= runtime->boundary;
runtime->control->appl_ptr = appl_ptr; runtime->control->appl_ptr = appl_ptr;
if (runtime->status->state == SNDRV_PCM_STATE_RUNNING && if (runtime->status->state == SNDRV_PCM_STATE_RUNNING &&
...@@ -2165,6 +2182,14 @@ static int snd_pcm_common_ioctl1(snd_pcm_substream_t *substream, ...@@ -2165,6 +2182,14 @@ static int snd_pcm_common_ioctl1(snd_pcm_substream_t *substream,
return put_user(SNDRV_PCM_VERSION, (int *)arg) ? -EFAULT : 0; return put_user(SNDRV_PCM_VERSION, (int *)arg) ? -EFAULT : 0;
case SNDRV_PCM_IOCTL_INFO: case SNDRV_PCM_IOCTL_INFO:
return snd_pcm_info_user(substream, (snd_pcm_info_t *) arg); return snd_pcm_info_user(substream, (snd_pcm_info_t *) arg);
case SNDRV_PCM_IOCTL_TSTAMP:
{
int xarg;
if (get_user(xarg, (int *) arg))
return -EFAULT;
substream->runtime->tstamp_timespec = xarg ? 1 : 0;
return 0;
}
case SNDRV_PCM_IOCTL_HW_REFINE: case SNDRV_PCM_IOCTL_HW_REFINE:
return snd_pcm_hw_refine_user(substream, (snd_pcm_hw_params_t *) arg); return snd_pcm_hw_refine_user(substream, (snd_pcm_hw_params_t *) arg);
case SNDRV_PCM_IOCTL_HW_PARAMS: case SNDRV_PCM_IOCTL_HW_PARAMS:
......
/*
* Scatter-Gather PCM access
*
* Copyright (c) by Takashi Iwai <tiwai@suse.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <sound/driver.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_sgbuf.h>
/* table entries are align to 32 */
#define SGBUF_TBL_ALIGN 32
#define sgbuf_align_table(tbl) ((((tbl) + SGBUF_TBL_ALIGN - 1) / SGBUF_TBL_ALIGN) * SGBUF_TBL_ALIGN)
/**
* snd_pcm_sgbuf_alloc_pages - allocate the pages for the SG buffer
* @pci: the pci device pointer
* @size: the requested buffer size in bytes
* @dmab: the dma-buffer record to store
*
* Initializes the SG-buffer table and allocates the buffer pages
* for the given size.
* The pages are mapped to the virtually continuous memory.
*
* This function is usually called from snd_pcm_lib_malloc_pages().
*
* Returns the mapped virtual address of the buffer if allocation was
* successful, or NULL at error.
*/
void *snd_pcm_sgbuf_alloc_pages(struct pci_dev *pci, size_t size, struct snd_pcm_dma_buffer *dmab)
{
struct snd_sg_buf *sgbuf;
unsigned int i, pages;
dmab->area = NULL;
dmab->addr = 0;
dmab->private_data = sgbuf = snd_magic_kcalloc(snd_pcm_sgbuf_t, 0, GFP_KERNEL);
if (! sgbuf)
return NULL;
sgbuf->pci = pci;
pages = snd_pcm_sgbuf_pages(size);
sgbuf->tblsize = sgbuf_align_table(pages);
sgbuf->table = snd_kcalloc(sizeof(*sgbuf->table) * sgbuf->tblsize, GFP_KERNEL);
if (! sgbuf->table)
goto _failed;
sgbuf->page_table = snd_kcalloc(sizeof(*sgbuf->page_table) * sgbuf->tblsize, GFP_KERNEL);
if (! sgbuf->page_table)
goto _failed;
/* allocate each page */
for (i = 0; i < pages; i++) {
void *ptr;
dma_addr_t addr;
ptr = snd_malloc_pci_page(sgbuf->pci, &addr);
if (! ptr)
goto _failed;
sgbuf->table[i].buf = ptr;
sgbuf->table[i].addr = addr;
sgbuf->page_table[i] = virt_to_page(ptr);
sgbuf->pages++;
}
sgbuf->size = size;
dmab->area = vmap(sgbuf->page_table, sgbuf->pages);
if (! dmab->area)
goto _failed;
return dmab->area;
_failed:
snd_pcm_sgbuf_free_pages(dmab); /* free the table */
return NULL;
}
/**
* snd_pcm_sgbuf_free_pages - free the sg buffer
* @dmab: dma buffer record
*
* Releases the pages and the SG-buffer table.
*
* This function is called usually from snd_pcm_lib_free_pages().
*
* Returns zero if successful, or a negative error code on failure.
*/
int snd_pcm_sgbuf_free_pages(struct snd_pcm_dma_buffer *dmab)
{
struct snd_sg_buf *sgbuf = snd_magic_cast(snd_pcm_sgbuf_t, dmab->private_data, return -EINVAL);
int i;
for (i = 0; i < sgbuf->pages; i++)
snd_free_pci_page(sgbuf->pci, sgbuf->table[i].buf, sgbuf->table[i].addr);
if (dmab->area)
vunmap(dmab->area);
dmab->area = NULL;
if (sgbuf->table)
kfree(sgbuf->table);
if (sgbuf->page_table)
kfree(sgbuf->page_table);
snd_magic_kfree(sgbuf);
dmab->private_data = NULL;
return 0;
}
/**
* snd_pcm_sgbuf_ops_page - get the page struct at the given offset
* @substream: the pcm substream instance
* @offset: the buffer offset
*
* Returns the page struct at the given buffer offset.
* Used as the page callback of PCM ops.
*/
struct page *snd_pcm_sgbuf_ops_page(snd_pcm_substream_t *substream, unsigned long offset)
{
struct snd_sg_buf *sgbuf = snd_magic_cast(snd_pcm_sgbuf_t, _snd_pcm_substream_sgbuf(substream), return NULL);
unsigned int idx = offset >> PAGE_SHIFT;
if (idx >= (unsigned int)sgbuf->pages)
return NULL;
return sgbuf->page_table[idx];
}
/*
* Exported symbols
*/
EXPORT_SYMBOL(snd_pcm_lib_preallocate_sg_pages);
EXPORT_SYMBOL(snd_pcm_lib_preallocate_sg_pages_for_all);
EXPORT_SYMBOL(snd_pcm_sgbuf_ops_page);
...@@ -79,7 +79,8 @@ static inline int snd_rawmidi_ready(snd_rawmidi_substream_t * substream) ...@@ -79,7 +79,8 @@ static inline int snd_rawmidi_ready(snd_rawmidi_substream_t * substream)
static inline int snd_rawmidi_ready_append(snd_rawmidi_substream_t * substream, size_t count) static inline int snd_rawmidi_ready_append(snd_rawmidi_substream_t * substream, size_t count)
{ {
snd_rawmidi_runtime_t *runtime = substream->runtime; snd_rawmidi_runtime_t *runtime = substream->runtime;
return runtime->avail >= runtime->avail_min && runtime->avail >= count; return runtime->avail >= runtime->avail_min &&
(!substream->append || runtime->avail >= count);
} }
static int snd_rawmidi_init(snd_rawmidi_substream_t *substream) static int snd_rawmidi_init(snd_rawmidi_substream_t *substream)
......
...@@ -51,7 +51,7 @@ static int translate_mode(struct file *file); ...@@ -51,7 +51,7 @@ static int translate_mode(struct file *file);
static int create_port(seq_oss_devinfo_t *dp); static int create_port(seq_oss_devinfo_t *dp);
static int delete_port(seq_oss_devinfo_t *dp); static int delete_port(seq_oss_devinfo_t *dp);
static int alloc_seq_queue(seq_oss_devinfo_t *dp); static int alloc_seq_queue(seq_oss_devinfo_t *dp);
static int delete_seq_queue(seq_oss_devinfo_t *dp); static int delete_seq_queue(int queue);
static void free_devinfo(void *private); static void free_devinfo(void *private);
#define call_ctl(type,rec) snd_seq_kernel_client_ctl(system_client, type, rec) #define call_ctl(type,rec) snd_seq_kernel_client_ctl(system_client, type, rec)
...@@ -186,6 +186,7 @@ snd_seq_oss_open(struct file *file, int level) ...@@ -186,6 +186,7 @@ snd_seq_oss_open(struct file *file, int level)
snd_printk(KERN_ERR "can't malloc device info\n"); snd_printk(KERN_ERR "can't malloc device info\n");
return -ENOMEM; return -ENOMEM;
} }
debug_printk(("oss_open: dp = %p\n", dp));
for (i = 0; i < SNDRV_SEQ_OSS_MAX_CLIENTS; i++) { for (i = 0; i < SNDRV_SEQ_OSS_MAX_CLIENTS; i++) {
if (client_table[i] == NULL) if (client_table[i] == NULL)
...@@ -193,6 +194,7 @@ snd_seq_oss_open(struct file *file, int level) ...@@ -193,6 +194,7 @@ snd_seq_oss_open(struct file *file, int level)
} }
if (i >= SNDRV_SEQ_OSS_MAX_CLIENTS) { if (i >= SNDRV_SEQ_OSS_MAX_CLIENTS) {
snd_printk(KERN_ERR "too many applications\n"); snd_printk(KERN_ERR "too many applications\n");
kfree(dp);
return -ENOMEM; return -ENOMEM;
} }
...@@ -209,22 +211,21 @@ snd_seq_oss_open(struct file *file, int level) ...@@ -209,22 +211,21 @@ snd_seq_oss_open(struct file *file, int level)
if (dp->synth_opened == 0 && dp->max_mididev == 0) { if (dp->synth_opened == 0 && dp->max_mididev == 0) {
snd_printk(KERN_ERR "no device found\n"); snd_printk(KERN_ERR "no device found\n");
kfree(dp); rc = -ENODEV;
return -ENODEV; goto _error;
} }
/* create port */ /* create port */
debug_printk(("create new port\n"));
if ((rc = create_port(dp)) < 0) { if ((rc = create_port(dp)) < 0) {
snd_printk(KERN_ERR "can't create port\n"); snd_printk(KERN_ERR "can't create port\n");
free_devinfo(dp); goto _error;
return rc;
} }
/* allocate queue */ /* allocate queue */
if ((rc = alloc_seq_queue(dp)) < 0) { debug_printk(("allocate queue\n"));
delete_port(dp); if ((rc = alloc_seq_queue(dp)) < 0)
return rc; goto _error;
}
/* set address */ /* set address */
dp->addr.client = dp->cseq; dp->addr.client = dp->cseq;
...@@ -238,31 +239,32 @@ snd_seq_oss_open(struct file *file, int level) ...@@ -238,31 +239,32 @@ snd_seq_oss_open(struct file *file, int level)
dp->file_mode = translate_mode(file); dp->file_mode = translate_mode(file);
/* initialize read queue */ /* initialize read queue */
debug_printk(("initialize read queue\n"));
if (is_read_mode(dp->file_mode)) { if (is_read_mode(dp->file_mode)) {
if ((dp->readq = snd_seq_oss_readq_new(dp, maxqlen)) == NULL) { if ((dp->readq = snd_seq_oss_readq_new(dp, maxqlen)) == NULL) {
delete_seq_queue(dp); rc = -ENOMEM;
delete_port(dp); goto _error;
return -ENOMEM;
} }
} }
/* initialize write queue */ /* initialize write queue */
debug_printk(("initialize write queue\n"));
if (is_write_mode(dp->file_mode)) { if (is_write_mode(dp->file_mode)) {
dp->writeq = snd_seq_oss_writeq_new(dp, maxqlen); dp->writeq = snd_seq_oss_writeq_new(dp, maxqlen);
if (dp->writeq == NULL) { if (dp->writeq == NULL) {
delete_seq_queue(dp); rc = -ENOMEM;
delete_port(dp); goto _error;
return -ENOMEM;
} }
} }
/* initialize timer */ /* initialize timer */
debug_printk(("initialize timer\n"));
if ((dp->timer = snd_seq_oss_timer_new(dp)) == NULL) { if ((dp->timer = snd_seq_oss_timer_new(dp)) == NULL) {
snd_printk(KERN_ERR "can't alloc timer\n"); snd_printk(KERN_ERR "can't alloc timer\n");
delete_seq_queue(dp); rc = -ENOMEM;
delete_port(dp); goto _error;
return -ENOMEM;
} }
debug_printk(("timer initialized\n"));
/* set private data pointer */ /* set private data pointer */
file->private_data = dp; file->private_data = dp;
...@@ -277,8 +279,16 @@ snd_seq_oss_open(struct file *file, int level) ...@@ -277,8 +279,16 @@ snd_seq_oss_open(struct file *file, int level)
num_clients++; num_clients++;
debug_printk(("open done\n")); debug_printk(("open done\n"));
return 0; return 0;
_error:
snd_seq_oss_synth_cleanup(dp);
snd_seq_oss_midi_cleanup(dp);
i = dp->queue;
delete_port(dp);
delete_seq_queue(i);
return rc;
} }
/* /*
...@@ -344,6 +354,7 @@ delete_port(seq_oss_devinfo_t *dp) ...@@ -344,6 +354,7 @@ delete_port(seq_oss_devinfo_t *dp)
if (dp->port < 0) if (dp->port < 0)
return 0; return 0;
debug_printk(("delete_port %i\n", dp->port));
memset(&port_info, 0, sizeof(port_info)); memset(&port_info, 0, sizeof(port_info));
port_info.addr.client = dp->cseq; port_info.addr.client = dp->cseq;
port_info.addr.port = dp->port; port_info.addr.port = dp->port;
...@@ -375,15 +386,19 @@ alloc_seq_queue(seq_oss_devinfo_t *dp) ...@@ -375,15 +386,19 @@ alloc_seq_queue(seq_oss_devinfo_t *dp)
* release queue * release queue
*/ */
static int static int
delete_seq_queue(seq_oss_devinfo_t *dp) delete_seq_queue(int queue)
{ {
snd_seq_queue_info_t qinfo; snd_seq_queue_info_t qinfo;
int rc;
if (dp->queue < 0) if (queue < 0)
return 0; return 0;
memset(&qinfo, 0, sizeof(qinfo)); memset(&qinfo, 0, sizeof(qinfo));
qinfo.queue = dp->queue; qinfo.queue = queue;
return call_ctl(SNDRV_SEQ_IOCTL_DELETE_QUEUE, &qinfo); rc = call_ctl(SNDRV_SEQ_IOCTL_DELETE_QUEUE, &qinfo);
if (rc < 0)
printk(KERN_ERR "seq-oss: unable to delete queue %d (%d)\n", queue, rc);
return rc;
} }
...@@ -414,6 +429,8 @@ free_devinfo(void *private) ...@@ -414,6 +429,8 @@ free_devinfo(void *private)
void void
snd_seq_oss_release(seq_oss_devinfo_t *dp) snd_seq_oss_release(seq_oss_devinfo_t *dp)
{ {
int queue;
client_table[dp->index] = NULL; client_table[dp->index] = NULL;
num_clients--; num_clients--;
...@@ -426,10 +443,10 @@ snd_seq_oss_release(seq_oss_devinfo_t *dp) ...@@ -426,10 +443,10 @@ snd_seq_oss_release(seq_oss_devinfo_t *dp)
/* clear slot */ /* clear slot */
debug_printk(("releasing resource..\n")); debug_printk(("releasing resource..\n"));
queue = dp->queue;
if (dp->port >= 0) if (dp->port >= 0)
delete_port(dp); delete_port(dp);
if (dp->queue >= 0) delete_seq_queue(queue);
delete_seq_queue(dp);
debug_printk(("release done\n")); debug_printk(("release done\n"));
} }
......
...@@ -206,9 +206,9 @@ snd_seq_oss_midi_check_new_port(snd_seq_port_info_t *pinfo) ...@@ -206,9 +206,9 @@ snd_seq_oss_midi_check_new_port(snd_seq_port_info_t *pinfo)
} }
if (i >= max_midi_devs) { if (i >= max_midi_devs) {
if (max_midi_devs >= SNDRV_SEQ_OSS_MAX_MIDI_DEVS) { if (max_midi_devs >= SNDRV_SEQ_OSS_MAX_MIDI_DEVS) {
spin_unlock_irqrestore(&register_lock, flags);
snd_midi_event_free(mdev->coder); snd_midi_event_free(mdev->coder);
kfree(mdev); kfree(mdev);
spin_unlock_irqrestore(&register_lock, flags);
return -ENOMEM; return -ENOMEM;
} }
max_midi_devs++; max_midi_devs++;
...@@ -372,6 +372,8 @@ snd_seq_oss_midi_open(seq_oss_devinfo_t *dp, int dev, int fmode) ...@@ -372,6 +372,8 @@ snd_seq_oss_midi_open(seq_oss_devinfo_t *dp, int dev, int fmode)
subs.sender.client = mdev->client; subs.sender.client = mdev->client;
subs.sender.port = mdev->port; subs.sender.port = mdev->port;
subs.dest = dp->addr; subs.dest = dp->addr;
subs.flags = SNDRV_SEQ_PORT_SUBS_TIMESTAMP;
subs.queue = dp->queue; /* queue for timestamps */
if (snd_seq_kernel_client_ctl(dp->cseq, SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, &subs) >= 0) if (snd_seq_kernel_client_ctl(dp->cseq, SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, &subs) >= 0)
mdev->opened |= PERM_READ; mdev->opened |= PERM_READ;
} }
...@@ -462,8 +464,7 @@ snd_seq_oss_midi_reset(seq_oss_devinfo_t *dp, int dev) ...@@ -462,8 +464,7 @@ snd_seq_oss_midi_reset(seq_oss_devinfo_t *dp, int dev)
return; return;
} }
if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC && if (mdev->opened & PERM_WRITE) {
(mdev->opened & PERM_WRITE)) {
snd_seq_event_t ev; snd_seq_event_t ev;
int c; int c;
...@@ -473,16 +474,22 @@ snd_seq_oss_midi_reset(seq_oss_devinfo_t *dp, int dev) ...@@ -473,16 +474,22 @@ snd_seq_oss_midi_reset(seq_oss_devinfo_t *dp, int dev)
ev.dest.port = mdev->port; ev.dest.port = mdev->port;
ev.queue = dp->queue; ev.queue = dp->queue;
ev.source.port = dp->port; ev.source.port = dp->port;
if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_SYNTH) {
ev.type = SNDRV_SEQ_EVENT_SENSING;
snd_seq_oss_dispatch(dp, &ev, 0, 0); /* active sensing */
}
for (c = 0; c < 16; c++) { for (c = 0; c < 16; c++) {
ev.type = SNDRV_SEQ_EVENT_CONTROLLER; ev.type = SNDRV_SEQ_EVENT_CONTROLLER;
ev.data.control.channel = c; ev.data.control.channel = c;
ev.data.control.param = 123; ev.data.control.param = 123;
snd_seq_oss_dispatch(dp, &ev, 0, 0); /* all notes off */ snd_seq_oss_dispatch(dp, &ev, 0, 0); /* all notes off */
ev.data.control.param = 121; if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC) {
snd_seq_oss_dispatch(dp, &ev, 0, 0); /* reset all controllers */ ev.data.control.param = 121;
ev.type = SNDRV_SEQ_EVENT_PITCHBEND; snd_seq_oss_dispatch(dp, &ev, 0, 0); /* reset all controllers */
ev.data.control.value = 0; ev.type = SNDRV_SEQ_EVENT_PITCHBEND;
snd_seq_oss_dispatch(dp, &ev, 0, 0); /* bender off */ ev.data.control.value = 0;
snd_seq_oss_dispatch(dp, &ev, 0, 0); /* bender off */
}
} }
} }
snd_seq_oss_midi_close(dp, dev); snd_seq_oss_midi_close(dp, dev);
...@@ -597,10 +604,12 @@ send_synth_event(seq_oss_devinfo_t *dp, snd_seq_event_t *ev, int dev) ...@@ -597,10 +604,12 @@ send_synth_event(seq_oss_devinfo_t *dp, snd_seq_event_t *ev, int dev)
static int static int
send_midi_event(seq_oss_devinfo_t *dp, snd_seq_event_t *ev, seq_oss_midi_t *mdev) send_midi_event(seq_oss_devinfo_t *dp, snd_seq_event_t *ev, seq_oss_midi_t *mdev)
{ {
char msg[32]; /* enough except for sysex? */ char msg[32];
int len; int len;
snd_seq_oss_readq_put_timestamp(dp->readq, snd_seq_oss_timer_cur_tick(dp->timer), dp->seq_mode); snd_seq_oss_readq_put_timestamp(dp->readq, ev->time.tick, dp->seq_mode);
if (!dp->timer->running)
len = snd_seq_oss_timer_start(dp->timer);
if (ev->type == SNDRV_SEQ_EVENT_SYSEX) { if (ev->type == SNDRV_SEQ_EVENT_SYSEX) {
if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) == SNDRV_SEQ_EVENT_LENGTH_VARIABLE) if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) == SNDRV_SEQ_EVENT_LENGTH_VARIABLE)
snd_seq_oss_readq_puts(dp->readq, mdev->seq_device, snd_seq_oss_readq_puts(dp->readq, mdev->seq_device,
......
...@@ -302,29 +302,36 @@ snd_seq_oss_synth_cleanup(seq_oss_devinfo_t *dp) ...@@ -302,29 +302,36 @@ snd_seq_oss_synth_cleanup(seq_oss_devinfo_t *dp)
seq_oss_synth_t *rec; seq_oss_synth_t *rec;
seq_oss_synthinfo_t *info; seq_oss_synthinfo_t *info;
snd_assert(dp->max_synthdev <= SNDRV_SEQ_OSS_MAX_SYNTH_DEVS, return);
for (i = 0; i < dp->max_synthdev; i++) { for (i = 0; i < dp->max_synthdev; i++) {
info = &dp->synths[i]; info = &dp->synths[i];
if (! info->opened) if (! info->opened)
continue; continue;
if (info->is_midi) { if (info->is_midi) {
snd_seq_oss_midi_close(dp, info->midi_mapped); if (midi_synth_dev.opened > 0) {
midi_synth_dev.opened--; snd_seq_oss_midi_close(dp, info->midi_mapped);
midi_synth_dev.opened--;
}
} else { } else {
rec = get_sdev(i); rec = get_sdev(i);
if (rec == NULL) if (rec == NULL)
continue; continue;
if (rec->opened) { if (rec->opened > 0) {
debug_printk(("synth %d closed\n", i)); debug_printk(("synth %d closed\n", i));
rec->oper.close(&info->arg); rec->oper.close(&info->arg);
module_put(rec->oper.owner); module_put(rec->oper.owner);
rec->opened--; rec->opened = 0;
} }
snd_use_lock_free(&rec->use_lock); snd_use_lock_free(&rec->use_lock);
} }
if (info->sysex) if (info->sysex) {
kfree(info->sysex); kfree(info->sysex);
if (info->ch) info->sysex = NULL;
}
if (info->ch) {
kfree(info->ch); kfree(info->ch);
info->ch = NULL;
}
} }
dp->synth_opened = 0; dp->synth_opened = 0;
dp->max_synthdev = 0; dp->max_synthdev = 0;
...@@ -401,15 +408,21 @@ snd_seq_oss_synth_reset(seq_oss_devinfo_t *dp, int dev) ...@@ -401,15 +408,21 @@ snd_seq_oss_synth_reset(seq_oss_devinfo_t *dp, int dev)
info->sysex->len = 0; /* reset sysex */ info->sysex->len = 0; /* reset sysex */
reset_channels(info); reset_channels(info);
if (info->is_midi) { if (info->is_midi) {
if (midi_synth_dev.opened <= 0)
return;
snd_seq_oss_midi_reset(dp, info->midi_mapped); snd_seq_oss_midi_reset(dp, info->midi_mapped);
if (snd_seq_oss_midi_open(dp, info->midi_mapped, if (snd_seq_oss_midi_open(dp, info->midi_mapped,
dp->file_mode) < 0) { dp->file_mode) < 0) {
midi_synth_dev.opened--; midi_synth_dev.opened--;
info->opened = 0; info->opened = 0;
if (info->sysex) if (info->sysex) {
kfree(info->sysex); kfree(info->sysex);
if (info->ch) info->sysex = NULL;
}
if (info->ch) {
kfree(info->ch); kfree(info->ch);
info->ch = NULL;
}
} }
return; return;
} }
......
...@@ -272,6 +272,21 @@ static void snd_seq_midisynth_delete(seq_midisynth_t *msynth) ...@@ -272,6 +272,21 @@ static void snd_seq_midisynth_delete(seq_midisynth_t *msynth)
snd_midi_event_free(msynth->parser); snd_midi_event_free(msynth->parser);
} }
/* set our client name */
static int set_client_name(seq_midisynth_client_t *client, snd_card_t *card,
snd_rawmidi_info_t *rmidi)
{
snd_seq_client_info_t cinfo;
const char *name;
memset(&cinfo, 0, sizeof(cinfo));
cinfo.client = client->seq_client;
cinfo.type = KERNEL_CLIENT;
name = rmidi->name[0] ? (const char *)rmidi->name : "External MIDI";
snprintf(cinfo.name, sizeof(cinfo.name), "Rawmidi %d - %s", card->number, name);
return snd_seq_kernel_client_ctl(client->seq_client, SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, &cinfo);
}
/* register new midi synth port */ /* register new midi synth port */
int int
snd_seq_midisynth_register_port(snd_seq_device_t *dev) snd_seq_midisynth_register_port(snd_seq_device_t *dev)
...@@ -284,7 +299,6 @@ snd_seq_midisynth_register_port(snd_seq_device_t *dev) ...@@ -284,7 +299,6 @@ snd_seq_midisynth_register_port(snd_seq_device_t *dev)
unsigned int p, ports; unsigned int p, ports;
snd_seq_client_callback_t callbacks; snd_seq_client_callback_t callbacks;
snd_seq_port_callback_t pcallbacks; snd_seq_port_callback_t pcallbacks;
snd_seq_client_info_t inf;
snd_card_t *card = dev->card; snd_card_t *card = dev->card;
int device = dev->device; int device = dev->device;
unsigned int input_count = 0, output_count = 0; unsigned int input_count = 0, output_count = 0;
...@@ -325,13 +339,9 @@ snd_seq_midisynth_register_port(snd_seq_device_t *dev) ...@@ -325,13 +339,9 @@ snd_seq_midisynth_register_port(snd_seq_device_t *dev)
up(&register_mutex); up(&register_mutex);
return -ENOMEM; return -ENOMEM;
} }
/* set our client name */ set_client_name(client, card, &info);
memset(&inf,0,sizeof(snd_seq_client_info_t)); } else if (device == 0)
inf.client = client->seq_client; set_client_name(client, card, &info); /* use the first device's name */
inf.type = KERNEL_CLIENT;
sprintf(inf.name, "External MIDI %i", card->number);
snd_seq_kernel_client_ctl(client->seq_client, SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, &inf);
}
msynth = snd_kcalloc(sizeof(seq_midisynth_t) * ports, GFP_KERNEL); msynth = snd_kcalloc(sizeof(seq_midisynth_t) * ports, GFP_KERNEL);
if (msynth == NULL) if (msynth == NULL)
......
...@@ -351,6 +351,7 @@ int snd_seq_set_port_info(client_port_t * port, snd_seq_port_info_t * info) ...@@ -351,6 +351,7 @@ int snd_seq_set_port_info(client_port_t * port, snd_seq_port_info_t * info)
/* information about supported channels/voices */ /* information about supported channels/voices */
port->midi_channels = info->midi_channels; port->midi_channels = info->midi_channels;
port->midi_voices = info->midi_voices;
port->synth_voices = info->synth_voices; port->synth_voices = info->synth_voices;
return 0; return 0;
...@@ -372,6 +373,7 @@ int snd_seq_get_port_info(client_port_t * port, snd_seq_port_info_t * info) ...@@ -372,6 +373,7 @@ int snd_seq_get_port_info(client_port_t * port, snd_seq_port_info_t * info)
/* information about supported channels/voices */ /* information about supported channels/voices */
info->midi_channels = port->midi_channels; info->midi_channels = port->midi_channels;
info->midi_voices = port->midi_voices;
info->synth_voices = port->synth_voices; info->synth_voices = port->synth_voices;
/* get subscriber counts */ /* get subscriber counts */
...@@ -611,7 +613,7 @@ subscribers_t *snd_seq_port_get_subscription(port_subs_info_t *src_grp, ...@@ -611,7 +613,7 @@ subscribers_t *snd_seq_port_get_subscription(port_subs_info_t *src_grp,
int snd_seq_event_port_attach(int client, int snd_seq_event_port_attach(int client,
snd_seq_port_callback_t *pcbp, snd_seq_port_callback_t *pcbp,
int cap, int type, int midi_channels, int cap, int type, int midi_channels,
char *portname) int midi_voices, char *portname)
{ {
snd_seq_port_info_t portinfo; snd_seq_port_info_t portinfo;
int ret; int ret;
...@@ -628,6 +630,7 @@ int snd_seq_event_port_attach(int client, ...@@ -628,6 +630,7 @@ int snd_seq_event_port_attach(int client,
portinfo.type = type; portinfo.type = type;
portinfo.kernel = pcbp; portinfo.kernel = pcbp;
portinfo.midi_channels = midi_channels; portinfo.midi_channels = midi_channels;
portinfo.midi_voices = midi_voices;
/* Create it */ /* Create it */
ret = snd_seq_kernel_client_ctl(client, ret = snd_seq_kernel_client_ctl(client,
......
...@@ -81,6 +81,7 @@ typedef struct client_port_t { ...@@ -81,6 +81,7 @@ typedef struct client_port_t {
/* supported channels */ /* supported channels */
int midi_channels; int midi_channels;
int midi_voices;
int synth_voices; int synth_voices;
} client_port_t; } client_port_t;
......
...@@ -262,34 +262,33 @@ int snd_seq_timer_open(queue_t *q) ...@@ -262,34 +262,33 @@ int snd_seq_timer_open(queue_t *q)
snd_timer_instance_t *t; snd_timer_instance_t *t;
seq_timer_t *tmr; seq_timer_t *tmr;
char str[32]; char str[32];
int err;
tmr = q->timer; tmr = q->timer;
snd_assert(tmr != NULL, return -EINVAL); snd_assert(tmr != NULL, return -EINVAL);
if (tmr->timeri) if (tmr->timeri)
return -EBUSY; return -EBUSY;
sprintf(str, "sequencer queue %i", q->queue); sprintf(str, "sequencer queue %i", q->queue);
if (tmr->type == SNDRV_SEQ_TIMER_ALSA) { /* standard ALSA timer */ if (tmr->type != SNDRV_SEQ_TIMER_ALSA) /* standard ALSA timer */
if (tmr->alsa_id.dev_class != SNDRV_TIMER_CLASS_SLAVE)
tmr->alsa_id.dev_sclass = SNDRV_TIMER_SCLASS_SEQUENCER;
t = snd_timer_open(str, &tmr->alsa_id, q->queue);
if (t == NULL && tmr->alsa_id.dev_class != SNDRV_TIMER_CLASS_SLAVE) {
if (tmr->alsa_id.dev_class != SNDRV_TIMER_CLASS_GLOBAL ||
tmr->alsa_id.device != SNDRV_TIMER_GLOBAL_SYSTEM) {
snd_timer_id_t tid;
memset(&tid, 0, sizeof(tid));
tid.dev_class = SNDRV_TIMER_CLASS_GLOBAL;
tid.dev_sclass = SNDRV_TIMER_SCLASS_SEQUENCER;
tid.card = -1;
tid.device = SNDRV_TIMER_GLOBAL_SYSTEM;
t = snd_timer_open(str, &tid, q->queue);
}
if (t == NULL) {
snd_printk(KERN_ERR "fatal error: cannot create timer\n");
return -ENODEV;
}
}
} else {
return -EINVAL; return -EINVAL;
if (tmr->alsa_id.dev_class != SNDRV_TIMER_CLASS_SLAVE)
tmr->alsa_id.dev_sclass = SNDRV_TIMER_SCLASS_SEQUENCER;
err = snd_timer_open(&t, str, &tmr->alsa_id, q->queue);
if (err < 0 && tmr->alsa_id.dev_class != SNDRV_TIMER_CLASS_SLAVE) {
if (tmr->alsa_id.dev_class != SNDRV_TIMER_CLASS_GLOBAL ||
tmr->alsa_id.device != SNDRV_TIMER_GLOBAL_SYSTEM) {
snd_timer_id_t tid;
memset(&tid, 0, sizeof(tid));
tid.dev_class = SNDRV_TIMER_CLASS_GLOBAL;
tid.dev_sclass = SNDRV_TIMER_SCLASS_SEQUENCER;
tid.card = -1;
tid.device = SNDRV_TIMER_GLOBAL_SYSTEM;
err = snd_timer_open(&t, str, &tid, q->queue);
}
if (err < 0) {
snd_printk(KERN_ERR "seq fatal error: cannot create timer (%i)\n", err);
return err;
}
} }
t->callback = snd_seq_timer_interrupt; t->callback = snd_seq_timer_interrupt;
t->callback_data = q; t->callback_data = q;
...@@ -319,7 +318,7 @@ int snd_seq_timer_stop(seq_timer_t * tmr) ...@@ -319,7 +318,7 @@ int snd_seq_timer_stop(seq_timer_t * tmr)
if (!tmr->running) if (!tmr->running)
return 0; return 0;
tmr->running = 0; tmr->running = 0;
snd_timer_stop(tmr->timeri); snd_timer_pause(tmr->timeri);
return 0; return 0;
} }
......
/*
* Scatter-Gather buffer
*
* Copyright (c) by Takashi Iwai <tiwai@suse.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <linux/config.h>
#include <linux/version.h>
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <sound/memalloc.h>
/* table entries are align to 32 */
#define SGBUF_TBL_ALIGN 32
#define sgbuf_align_table(tbl) ((((tbl) + SGBUF_TBL_ALIGN - 1) / SGBUF_TBL_ALIGN) * SGBUF_TBL_ALIGN)
/**
* snd_malloc_sgbuf_pages - allocate the pages for the PCI SG buffer
* @pci: the pci device pointer
* @size: the requested buffer size in bytes
* @dmab: the buffer record to store
*
* Initializes the SG-buffer table and allocates the buffer pages
* for the given size.
* The pages are mapped to the virtually continuous memory.
*
* This function is usually called from the middle-level functions such as
* snd_pcm_lib_malloc_pages().
*
* Returns the mapped virtual address of the buffer if allocation was
* successful, or NULL at error.
*/
void *snd_malloc_sgbuf_pages(struct pci_dev *pci, size_t size, struct snd_dma_buffer *dmab)
{
struct snd_sg_buf *sgbuf;
unsigned int i, pages;
dmab->area = NULL;
dmab->addr = 0;
dmab->private_data = sgbuf = kmalloc(sizeof(*sgbuf), GFP_KERNEL);
if (! sgbuf)
return NULL;
memset(sgbuf, 0, sizeof(*sgbuf));
sgbuf->pci = pci;
pages = snd_sgbuf_aligned_pages(size);
sgbuf->tblsize = sgbuf_align_table(pages);
sgbuf->table = kmalloc(sizeof(*sgbuf->table) * sgbuf->tblsize, GFP_KERNEL);
if (! sgbuf->table)
goto _failed;
memset(sgbuf->table, 0, sizeof(*sgbuf->table) * sgbuf->tblsize);
sgbuf->page_table = kmalloc(sizeof(*sgbuf->page_table) * sgbuf->tblsize, GFP_KERNEL);
if (! sgbuf->page_table)
goto _failed;
memset(sgbuf->page_table, 0, sizeof(*sgbuf->page_table) * sgbuf->tblsize);
/* allocate each page */
for (i = 0; i < pages; i++) {
void *ptr;
dma_addr_t addr;
ptr = snd_malloc_pci_page(sgbuf->pci, &addr);
if (! ptr)
goto _failed;
sgbuf->table[i].buf = ptr;
sgbuf->table[i].addr = addr;
sgbuf->page_table[i] = virt_to_page(ptr);
sgbuf->pages++;
}
sgbuf->size = size;
dmab->area = vmap(sgbuf->page_table, sgbuf->pages);
if (! dmab->area)
goto _failed;
return dmab->area;
_failed:
snd_free_sgbuf_pages(dmab); /* free the table */
return NULL;
}
/**
* snd_free_sgbuf_pages - free the sg buffer
* @dmab: buffer record
*
* Releases the pages and the SG-buffer table.
*
* This function is called usually from the middle-level function
* such as snd_pcm_lib_free_pages().
*
* Returns zero if successful, or a negative error code on failure.
*/
int snd_free_sgbuf_pages(struct snd_dma_buffer *dmab)
{
struct snd_sg_buf *sgbuf = dmab->private_data;
int i;
if (! sgbuf)
return -EINVAL;
for (i = 0; i < sgbuf->pages; i++)
snd_free_pci_page(sgbuf->pci, sgbuf->table[i].buf, sgbuf->table[i].addr);
if (dmab->area)
vunmap(dmab->area);
dmab->area = NULL;
if (sgbuf->table)
kfree(sgbuf->table);
if (sgbuf->page_table)
kfree(sgbuf->page_table);
kfree(sgbuf);
dmab->private_data = NULL;
return 0;
}
...@@ -432,24 +432,6 @@ EXPORT_SYMBOL(snd_magic_kfree); ...@@ -432,24 +432,6 @@ EXPORT_SYMBOL(snd_magic_kfree);
#endif #endif
EXPORT_SYMBOL(snd_kcalloc); EXPORT_SYMBOL(snd_kcalloc);
EXPORT_SYMBOL(snd_kmalloc_strdup); EXPORT_SYMBOL(snd_kmalloc_strdup);
EXPORT_SYMBOL(snd_malloc_pages);
EXPORT_SYMBOL(snd_malloc_pages_fallback);
EXPORT_SYMBOL(snd_free_pages);
#if defined(CONFIG_ISA) && ! defined(CONFIG_PCI)
EXPORT_SYMBOL(snd_malloc_isa_pages);
EXPORT_SYMBOL(snd_malloc_isa_pages_fallback);
#endif
#ifdef CONFIG_PCI
EXPORT_SYMBOL(snd_malloc_pci_pages);
EXPORT_SYMBOL(snd_malloc_pci_pages_fallback);
EXPORT_SYMBOL(snd_malloc_pci_page);
EXPORT_SYMBOL(snd_free_pci_pages);
#endif
#ifdef CONFIG_SBUS
EXPORT_SYMBOL(snd_malloc_sbus_pages);
EXPORT_SYMBOL(snd_malloc_sbus_pages_fallback);
EXPORT_SYMBOL(snd_free_sbus_pages);
#endif
EXPORT_SYMBOL(copy_to_user_fromio); EXPORT_SYMBOL(copy_to_user_fromio);
EXPORT_SYMBOL(copy_from_user_toio); EXPORT_SYMBOL(copy_from_user_toio);
/* init.c */ /* init.c */
...@@ -529,6 +511,3 @@ EXPORT_SYMBOL(snd_wrapper_kfree); ...@@ -529,6 +511,3 @@ EXPORT_SYMBOL(snd_wrapper_kfree);
EXPORT_SYMBOL(snd_wrapper_vmalloc); EXPORT_SYMBOL(snd_wrapper_vmalloc);
EXPORT_SYMBOL(snd_wrapper_vfree); EXPORT_SYMBOL(snd_wrapper_vfree);
#endif #endif
#ifdef HACK_PCI_ALLOC_CONSISTENT
EXPORT_SYMBOL(snd_pci_hack_alloc_consistent);
#endif
This diff is collapsed.
...@@ -49,59 +49,3 @@ void snd_wrapper_vfree(void *obj) ...@@ -49,59 +49,3 @@ void snd_wrapper_vfree(void *obj)
} }
#endif #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>
/* to be sure... */
#ifdef HACK_PCI_ALLOC_CONSISTENT
#error pci_alloc_consistent hack is already defined!!
#endif
/*
* 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);
hwdev->dma_mask = dma_mask; /* restore */
if (ret) {
/* obtained address is out of range? */
if (((unsigned long)*dma_handle + size - 1) & rmask) {
/* reallocate with the proper mask */
pci_free_consistent(hwdev, size, ret, *dma_handle);
ret = pci_alloc_consistent(hwdev, size, dma_handle);
}
} else {
/* wish to success now with the proper mask... */
if (dma_mask != 0xffffffff)
ret = pci_alloc_consistent(hwdev, size, dma_handle);
}
return ret;
}
#endif
#endif
...@@ -54,6 +54,15 @@ MODULE_DEVICES("{{ALSA,Dummy soundcard}}"); ...@@ -54,6 +54,15 @@ MODULE_DEVICES("{{ALSA,Dummy soundcard}}");
#define USE_PERIODS_MAX 2 #define USE_PERIODS_MAX 2
#endif #endif
#if 0 /* ICE1712 emulation */
#define MAX_BUFFER_SIZE (256 * 1024)
#define USE_FORMATS SNDRV_PCM_FMTBIT_S32_LE
#define USE_CHANNELS_MIN 12
#define USE_CHANNELS_MAX 12
#define USE_PERIODS_MIN 1
#define USE_PERIODS_MAX 1024
#endif
#if 0 /* UDA1341 emulation */ #if 0 /* UDA1341 emulation */
#define MAX_BUFFER_SIZE (16380) #define MAX_BUFFER_SIZE (16380)
#define USE_FORMATS SNDRV_PCM_FMTBIT_S16_LE #define USE_FORMATS SNDRV_PCM_FMTBIT_S16_LE
......
...@@ -21,6 +21,11 @@ ...@@ -21,6 +21,11 @@
* along with this program; if not, write to the Free Software * along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
* *
* 13-03-2003:
* Added support for different kind of hardware I/O. Build in choices
* are port and mmio. For other kind of I/O, set mpu->read and
* mpu->write to your own I/O functions.
*
*/ */
#include <sound/driver.h> #include <sound/driver.h>
...@@ -45,21 +50,43 @@ static void snd_mpu401_uart_output_write(mpu401_t * mpu); ...@@ -45,21 +50,43 @@ static void snd_mpu401_uart_output_write(mpu401_t * mpu);
*/ */
#define snd_mpu401_input_avail(mpu) (!(inb(MPU401C(mpu)) & 0x80)) #define snd_mpu401_input_avail(mpu) (!(mpu->read(mpu, MPU401C(mpu)) & 0x80))
#define snd_mpu401_output_ready(mpu) (!(inb(MPU401C(mpu)) & 0x40)) #define snd_mpu401_output_ready(mpu) (!(mpu->read(mpu, MPU401C(mpu)) & 0x40))
#define MPU401_RESET 0xff #define MPU401_RESET 0xff
#define MPU401_ENTER_UART 0x3f #define MPU401_ENTER_UART 0x3f
#define MPU401_ACK 0xfe #define MPU401_ACK 0xfe
/* Build in lowlevel io */
static void mpu401_write_port(mpu401_t *mpu, unsigned char data, unsigned long addr)
{
outb(data, addr);
}
static unsigned char mpu401_read_port(mpu401_t *mpu, unsigned long addr)
{
return inb(addr);
}
static void mpu401_write_mmio(mpu401_t *mpu, unsigned char data, unsigned long addr)
{
writeb(data, (unsigned long*)addr);
}
static unsigned char mpu401_read_mmio(mpu401_t *mpu, unsigned long addr)
{
return readb((unsigned long*)addr);
}
/* */
static void snd_mpu401_uart_clear_rx(mpu401_t *mpu) static void snd_mpu401_uart_clear_rx(mpu401_t *mpu)
{ {
int timeout = 100000; int timeout = 100000;
for (; timeout > 0 && snd_mpu401_input_avail(mpu); timeout--) for (; timeout > 0 && snd_mpu401_input_avail(mpu); timeout--)
inb(MPU401D(mpu)); mpu->read(mpu, MPU401D(mpu));
#ifdef CONFIG_SND_DEBUG #ifdef CONFIG_SND_DEBUG
if (timeout <= 0) if (timeout <= 0)
snd_printk("cmd: clear rx timeout (status = 0x%x)\n", inb(MPU401C(mpu))); snd_printk("cmd: clear rx timeout (status = 0x%x)\n", mpu->read(mpu, MPU401C(mpu)));
#endif #endif
} }
...@@ -163,7 +190,7 @@ static void snd_mpu401_uart_cmd(mpu401_t * mpu, unsigned char cmd, int ack) ...@@ -163,7 +190,7 @@ static void snd_mpu401_uart_cmd(mpu401_t * mpu, unsigned char cmd, int ack)
spin_lock_irqsave(&mpu->input_lock, flags); spin_lock_irqsave(&mpu->input_lock, flags);
if (mpu->hardware != MPU401_HW_TRID4DWAVE) { if (mpu->hardware != MPU401_HW_TRID4DWAVE) {
outb(0x00, MPU401D(mpu)); mpu->write(mpu, 0x00, MPU401D(mpu));
/*snd_mpu401_uart_clear_rx(mpu);*/ /*snd_mpu401_uart_clear_rx(mpu);*/
} }
/* ok. standard MPU-401 initialization */ /* ok. standard MPU-401 initialization */
...@@ -172,28 +199,28 @@ static void snd_mpu401_uart_cmd(mpu401_t * mpu, unsigned char cmd, int ack) ...@@ -172,28 +199,28 @@ static void snd_mpu401_uart_cmd(mpu401_t * mpu, unsigned char cmd, int ack)
udelay(10); udelay(10);
#ifdef CONFIG_SND_DEBUG #ifdef CONFIG_SND_DEBUG
if (!timeout) if (!timeout)
snd_printk("cmd: tx timeout (status = 0x%x)\n", inb(MPU401C(mpu))); snd_printk("cmd: tx timeout (status = 0x%x)\n", mpu->read(mpu, MPU401C(mpu)));
#endif #endif
} }
outb(cmd, MPU401C(mpu)); mpu->write(mpu, cmd, MPU401C(mpu));
if (ack) { if (ack) {
ok = 0; ok = 0;
timeout = 10000; timeout = 10000;
while (!ok && timeout-- > 0) { while (!ok && timeout-- > 0) {
if (snd_mpu401_input_avail(mpu)) { if (snd_mpu401_input_avail(mpu)) {
if (inb(MPU401D(mpu)) == MPU401_ACK) if (mpu->read(mpu, MPU401D(mpu)) == MPU401_ACK)
ok = 1; ok = 1;
} }
} }
if (!ok && inb(MPU401D(mpu)) == MPU401_ACK) if (!ok && mpu->read(mpu, MPU401D(mpu)) == MPU401_ACK)
ok = 1; ok = 1;
} else { } else {
ok = 1; ok = 1;
} }
spin_unlock_irqrestore(&mpu->input_lock, flags); spin_unlock_irqrestore(&mpu->input_lock, flags);
if (! ok) if (! ok)
snd_printk("cmd: 0x%x failed at 0x%lx (status = 0x%x, data = 0x%x)\n", cmd, mpu->port, inb(MPU401C(mpu)), inb(MPU401D(mpu))); snd_printk("cmd: 0x%x failed at 0x%lx (status = 0x%x, data = 0x%x)\n", cmd, mpu->port, mpu->read(mpu, MPU401C(mpu)), mpu->read(mpu, MPU401D(mpu)));
// snd_printk("cmd: 0x%x at 0x%lx (status = 0x%x, data = 0x%x)\n", cmd, mpu->port, inb(MPU401C(mpu)), inb(MPU401D(mpu))); // snd_printk("cmd: 0x%x at 0x%lx (status = 0x%x, data = 0x%x)\n", cmd, mpu->port, mpu->read(mpu, MPU401C(mpu)), mpu->read(mpu, MPU401D(mpu)));
} }
/* /*
...@@ -275,7 +302,7 @@ static void snd_mpu401_uart_input_trigger(snd_rawmidi_substream_t * substream, i ...@@ -275,7 +302,7 @@ static void snd_mpu401_uart_input_trigger(snd_rawmidi_substream_t * substream, i
if (! test_and_set_bit(MPU401_MODE_BIT_INPUT_TRIGGER, &mpu->mode)) { if (! test_and_set_bit(MPU401_MODE_BIT_INPUT_TRIGGER, &mpu->mode)) {
/* first time - flush FIFO */ /* first time - flush FIFO */
while (max-- > 0) while (max-- > 0)
inb(MPU401D(mpu)); mpu->read(mpu, MPU401D(mpu));
if (mpu->irq < 0) if (mpu->irq < 0)
snd_mpu401_uart_add_timer(mpu, 1); snd_mpu401_uart_add_timer(mpu, 1);
} }
...@@ -306,7 +333,7 @@ static void snd_mpu401_uart_input_read(mpu401_t * mpu) ...@@ -306,7 +333,7 @@ static void snd_mpu401_uart_input_read(mpu401_t * mpu)
while (max-- > 0) { while (max-- > 0) {
if (snd_mpu401_input_avail(mpu)) { if (snd_mpu401_input_avail(mpu)) {
byte = inb(MPU401D(mpu)); byte = mpu->read(mpu, MPU401D(mpu));
if (test_bit(MPU401_MODE_BIT_INPUT_TRIGGER, &mpu->mode)) if (test_bit(MPU401_MODE_BIT_INPUT_TRIGGER, &mpu->mode))
snd_rawmidi_receive(mpu->substream_input, &byte, 1); snd_rawmidi_receive(mpu->substream_input, &byte, 1);
} else { } else {
...@@ -336,7 +363,7 @@ static void snd_mpu401_uart_output_write(mpu401_t * mpu) ...@@ -336,7 +363,7 @@ static void snd_mpu401_uart_output_write(mpu401_t * mpu)
if (snd_rawmidi_transmit_peek(mpu->substream_output, &byte, 1) == 1) { if (snd_rawmidi_transmit_peek(mpu->substream_output, &byte, 1) == 1) {
for (timeout = 100; timeout > 0; timeout--) { for (timeout = 100; timeout > 0; timeout--) {
if (snd_mpu401_output_ready(mpu)) { if (snd_mpu401_output_ready(mpu)) {
outb(byte, MPU401D(mpu)); mpu->write(mpu, byte, MPU401D(mpu));
snd_rawmidi_transmit_ack(mpu->substream_output, 1); snd_rawmidi_transmit_ack(mpu->substream_output, 1);
break; break;
} }
...@@ -460,6 +487,16 @@ int snd_mpu401_uart_new(snd_card_t * card, int device, ...@@ -460,6 +487,16 @@ int snd_mpu401_uart_new(snd_card_t * card, int device,
return -EBUSY; return -EBUSY;
} }
} }
switch (hardware) {
case MPU401_HW_AUREAL:
mpu->write = mpu401_write_mmio;
mpu->read = mpu401_read_mmio;
break;
default:
mpu->write = mpu401_write_port;
mpu->read = mpu401_read_port;
break;
}
mpu->port = port; mpu->port = port;
if (hardware == MPU401_HW_PC98II) if (hardware == MPU401_HW_PC98II)
mpu->cport = port + 2; mpu->cport = port + 2;
...@@ -474,7 +511,10 @@ int snd_mpu401_uart_new(snd_card_t * card, int device, ...@@ -474,7 +511,10 @@ int snd_mpu401_uart_new(snd_card_t * card, int device,
} }
mpu->irq = irq; mpu->irq = irq;
mpu->irq_flags = irq_flags; mpu->irq_flags = irq_flags;
sprintf(rmidi->name, "MPU-401 (UART) %d-%d", card->number, device); if (card->shortname[0])
snprintf(rmidi->name, sizeof(rmidi->name), "%s MPU-401", card->shortname);
else
sprintf(rmidi->name, "MPU-401 (UART) %d-%d", card->number, device);
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_mpu401_uart_output); snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_mpu401_uart_output);
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_mpu401_uart_input); snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_mpu401_uart_input);
rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT |
......
...@@ -101,7 +101,7 @@ static int snd_opl3_oss_create_port(opl3_t * opl3) ...@@ -101,7 +101,7 @@ static int snd_opl3_oss_create_port(opl3_t * opl3)
SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC | SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC |
SNDRV_SEQ_PORT_TYPE_MIDI_GM | SNDRV_SEQ_PORT_TYPE_MIDI_GM |
SNDRV_SEQ_PORT_TYPE_SYNTH, SNDRV_SEQ_PORT_TYPE_SYNTH,
voices, voices, voices,
name); name);
if (opl3->oss_chset->port < 0) { if (opl3->oss_chset->port < 0) {
snd_midi_channel_free_set(opl3->oss_chset); snd_midi_channel_free_set(opl3->oss_chset);
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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