/*
 *  Digital Audio (PCM) abstraction layer
 *  Copyright (c) by Jaroslav Kysela <perex@jcu.cz>
 */

#define SND_PCM_DEVICES		4

#define SND_PCM_ZERO_RESERVED	sizeof( struct SND_PCM_HARDWARE )

#define SND_PCM_PLAYBACK	0
#define SND_PCM_RECORD		1

#define SND_PCM_LFLG_NONE	0x0000
#define SND_PCM_LFLG_PLAY	0x0001
#define SND_PCM_LFLG_RECORD	0x0002
#define SND_PCM_LFLG_BOTH	(SND_PCM_LFLG_PLAY|SND_PCM_LFLG_RECORD) 
#define SND_PCM_LFLG_OSS_PLAY	0x0004
#define SND_PCM_LFLG_OSS_RECORD	0x0008
#define SND_PCM_LFLG_OSS	(SND_PCM_LFLG_OSS_PLAY|SND_PCM_LFLG_OSS_RECORD)

#define SND_PCM_DEFAULT_RATE	8000

#define SND_PCM_MODE_MULTI	0x00000010	/* set - multitrack (1-32) operation enabled */
#define SND_PCM_MODE_VALID	0x00000080	/* unset = not valid, set = valid */
#define SND_PCM_MODE_U		0x00000100	/* unset = signed, set = unsigned */
#define SND_PCM_MODE_16		0x00000200	/* unset = 8 bit , set = 16 bit   */
#define SND_PCM_MODE_BIG	0x00000400	/* big endian (16-bit mode) */
#define SND_PCM_MODE_ULAW	0x00000800	/* mu-Law */
#define SND_PCM_MODE_ALAW	0x00001000	/* a-Law */
#define SND_PCM_MODE_ADPCM	0x00002000	/* IMA ADPCM 4:1 */
#define SND_PCM_MODE_MPEG	0x00004000	/* MPEG 1/2 */
#define SND_PCM_MODE_GSM	0x00008000	/* GSM */
#define SND_PCM_MODE_TYPE	0xffffff00	/* bitmask */

#define SND_PCM_FLG_NONE	0x00000000
#define SND_PCM_FLG_ENABLE	0x00000001	/* enable trigger */
#define SND_PCM_FLG_SLEEP	0x00000002	/* channel is in sleep state - need wakeup */
#define SND_PCM_FLG_ABORT	0x00000004	/* user abort */
#define SND_PCM_FLG_NONBLK	0x00000008	/* non block I/O */
#define SND_PCM_FLG_MMAP	0x00000010	/* mmaped access */
#define SND_PCM_FLG_DMAOK	0x00000020	/* DMA is now allocated */
#define SND_PCM_FLG_NEUTRAL	0x00000040	/* erase DMA buffer (fill with neutral byte) */
#define SND_PCM_FLG_BUFFERS	0x00000080	/* compute new sizes for buffers */
#define SND_PCM_FLG_TRIGGER	0x00000100	/* trigger on/off */
#define SND_PCM_FLG_TRIGGER1	0x00000200	/* prepare ok */
#define SND_PCM_FLG_TRIGGERA	0x00000300	/* both above flags */
#define SND_PCM_FLG_SYNC	0x00000400	/* synchronize playback/record */
#define SND_PCM_FLG_TIME	0x00000800	/* time */

#define SND_PCM_HW_BATCH	0x00000001	/* double buffering */
#define SND_PCM_HW_8BITONLY	0x00000002	/* hardware supports only 8-bit DMA, but does conversions from 16-bit to 8-bit */
#define SND_PCM_HW_16BITONLY	0x00000004	/* hardware supports only 16-bit DMA, but does conversion from 8-bit to 16-bit */
#define SND_PCM_HW_COPYMASK	0x00000007	/* flags to copy to info structure */
#define SND_PCM_HW_AUTODMA	0x10000000	/* hardware supports auto dma - good */

#define snd_pcm_lockzero( channel ) \
  ((channel) -> block_lock = -1)
#define snd_pcm_lock( channel, block ) \
  ((channel) -> block_lock = (block))
#define snd_pcm_islock( channel, block ) \
  ((channel) -> block_lock >= 0 && (channel) -> block_lock == (block))
#define snd_pcm_clear_time( channel ) \
  ((channel) -> time.tv_sec = (channel) -> time.tv_usec = 0)

typedef struct snd_stru_pcm_channel snd_pcm_channel_t;

struct snd_stru_pcm_hardware {
  /* -- these values aren't erased -- */
  void *private_data;		/* private structure */
  void (*private_free)( void *private_data );
  /* -- must be filled with low-level driver */
  unsigned int flags;		/* see to SND_PCM_HW_XXXX */
  unsigned int formats;		/* supported formats... */
  unsigned int align;		/* align value... */
  unsigned short min_fragment;	/* minimal fragment... */
  unsigned short min_rate;	/* minimal rate... */
  unsigned short max_rate;	/* maximal rate... */
  unsigned short max_voices;	/* maximal voices... */
  /* -- low-level functions -- */
  int (*open)( snd_pcm_t *pcm );
  void (*close)( snd_pcm_t *pcm );
  void (*compute_rate)( snd_pcm_t *pcm );
  void (*prepare)( snd_pcm_t *pcm, unsigned char *buffer, unsigned int size, unsigned int offset, unsigned int count );
  void (*trigger)( snd_pcm_t *pcm, int up );
  unsigned int (*pointer)( snd_pcm_t *pcm, unsigned int used_size );
  void (*dma)( snd_pcm_t *pcm, unsigned char *buffer, unsigned int offset, unsigned char *user, unsigned int count );
  void (*dma_move)( snd_pcm_t *pcm, unsigned char *buffer, unsigned int dest_offset, unsigned int src_offset, unsigned int count );
  void (*dma_neutral)( snd_pcm_t *pcm, unsigned char *buffer, unsigned offset, unsigned int count, unsigned char neutral_byte );
};

struct snd_stru_pcm_channel {
  /* -- format/buffering -- */
  unsigned short voices;	/* or channels 1-32 */
  unsigned int mode;
  unsigned int format;
  unsigned int rate;
  unsigned int real_rate;
  unsigned int requested_block_size;
  unsigned int requested_blocks;
  unsigned int requested_subdivision;
  volatile unsigned int processed_bytes;
  volatile unsigned int interrupts;
  volatile unsigned int xruns;
  volatile unsigned int total_discarded;	/* discarded blocks... */
  volatile unsigned int total_xruns;		/* under/overruns */
  /* -- physical/flags -- */
  unsigned int flags;
  unsigned int used_size;	/* used size of audio buffer (logical size) */
  unsigned int mmap_size;	/* for OSS */
  unsigned char neutral_byte;
  unsigned int size;		/* real size of audio buffer */
  unsigned char *buffer;	/* pointer to audio buffer */
  int dmanum;			/* dma number */
  /* -- ack callback -- */
  void (*ack)( snd_pcm_t *pcm ); /* acknowledge interrupt to abstract layer */
  /* -- logical blocks -- */
  unsigned short blocks;	/* number of blocks (2-N) */
  unsigned int block_size;	/* size of one block */
  volatile unsigned short used;	/* number of used blocks */
  volatile unsigned int frag_size; /* size of partly used block */
  volatile unsigned short head;	/* fill it... */
  volatile unsigned short tail;	/* remove it... */
  volatile int block_lock;	/* locked block... */
  unsigned int blocks_max;	/* max blocks in queue for wakeup */
  unsigned int blocks_room;	/* min blocks in queue for wakeup */
  unsigned int blocks_min;	/* min blocks in queue for wakeup */
  struct timeval time;		/* time value */
  /* -- /proc interface -- */ 
  void *proc_entry;
  void *proc_private;
  snd_mutex_define( proc );
  /* -- hardware -- */
  struct snd_stru_pcm_hardware hw;
};

struct snd_stru_pcm {
  snd_card_t *card;
  unsigned int device;		/* device number */
  unsigned int flags;
  unsigned short mask;
  unsigned int info_flags;
  char id[32];
  char name[80];
  struct snd_stru_pcm_channel playback;
  struct snd_stru_pcm_channel record;
  void *private_data;
  void (*private_free)( void *private_data );
  snd_info_entry_t *proc_entry;
  snd_sleep_define( playback );
  snd_sleep_define( record );
  snd_mutex_define( open );
  snd_spin_define( playback );
  snd_spin_define( record );
  snd_spin_define( playback_sleep );
  snd_spin_define( record_sleep );
};

extern void snd_pcm_playback_dma( snd_pcm_t *pcm,
			          unsigned char *buffer, unsigned int offset,
			          unsigned char *user, unsigned int count );
extern void snd_pcm_playback_dma_ulaw( snd_pcm_t *pcm,
			               unsigned char *buffer, unsigned int offset,
			               unsigned char *user, unsigned int count );
extern void snd_pcm_playback_dma_neutral( snd_pcm_t *pcm,
				          unsigned char *buffer, unsigned int offset,
				          unsigned int count,
				          unsigned char neutral_byte );
extern void snd_pcm_record_dma( snd_pcm_t *pcm,
			        unsigned char *buffer, unsigned int offset,
			        unsigned char *user, unsigned int count );
extern void snd_pcm_record_dma_ulaw( snd_pcm_t *pcm,
			             unsigned char *buffer, unsigned int offset,
			             unsigned char *user, unsigned int count );
extern void snd_pcm_dma_move( snd_pcm_t *pcm,
                              unsigned char *buffer,
                              unsigned int dest_offset, unsigned int src_offset,
                              unsigned int count );

extern void snd_pcm_clear_channel( snd_pcm_channel_t *pchn );
extern unsigned short snd_pcm_file_flags( unsigned short f_flags );
extern void snd_pcm_fill_with_neutral( snd_pcm_t *pcm, snd_pcm_channel_t *pchn );

extern int snd_pcm_dma_alloc( snd_pcm_t *pcm, int direction, int dmanum, char *ident );
extern int snd_pcm_dma_free( snd_pcm_t *pcm, int direction, int dmanum );

/*
 *  Registering
 */

extern snd_pcm_t *snd_pcm_new_device( snd_card_t *card, char *id );
extern int snd_pcm_free( snd_pcm_t *pcm );
extern int snd_pcm_register( snd_pcm_t *pcm, int pcm_device );
extern int snd_pcm_unregister( snd_pcm_t *pcm );

/*
 *  /proc interface
 */
 
extern void snd_pcm_proc_write( snd_pcm_channel_t *pchn, const void *buffer, unsigned int count );
extern void snd_pcm_proc_init( snd_pcm_t *pcm );
extern void snd_pcm_proc_done( snd_pcm_t *pcm );
