/* Very simple dummy ALSA driver.
   Copyright (C) 2007 Trent Piepho <xyzzy@speakeasy.org> */

#include <linux/version.h>
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
#include <linux/config.h>
#endif
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/sched.h>

#include <sound/driver.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/initval.h>

MODULE_LICENSE("GPL");

#include <linux/calc64.h>
#define MHZ	1544.426
#define INT	(unsigned int)(MHZ * (1<<12) + 0.5)
#define FRAC	(unsigned int)(MHZ * (1<<21) / 1000.0 + 0.5)
#define timestamp(str, args...)	{ u64 _time; u32 _rem; rdtscll(_time); \
	_time = (_time - chip->starttime)<<12; \
	_rem = do_div(_time, INT); \
	printk("%s - %u.%03u us " str "\n", __FUNCTION__, \
		(unsigned int)_time, (_rem << 9) / FRAC , ## args); }

struct snd_as {
	u64	starttime;
};

static struct snd_pcm_hardware as_hw = {
	.info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
	        SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_INTERLEAVED,
	.formats = SNDRV_PCM_FMTBIT_U8,
	.rates = SNDRV_PCM_RATE_8000_48000,
	.rate_min = 8000,
	.rate_max = 48000,
	.channels_min = 1,
	.channels_max = 1,
	.buffer_bytes_max = (128 * 1024),
	.period_bytes_min = 1024,
	.period_bytes_max = (128 * 1024),
	.periods_min = 1,
	.periods_max = 128,
};

static int as_open(struct snd_pcm_substream *substream)
{
	struct snd_as *chip = snd_pcm_substream_chip(substream);
	struct snd_pcm_runtime *runtime = substream->runtime;
	int err;

	runtime->hw = as_hw;
	rdtscll(chip->starttime);
	err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
	timestamp("");
	return err;
}

static int as_close(struct snd_pcm_substream *substream)
{
	struct snd_as *chip = snd_pcm_substream_chip(substream);

	timestamp("");
	return 0;
}

static int as_hw_params(struct snd_pcm_substream *substream, 
			struct snd_pcm_hw_params *hw_params)
{
	struct snd_as *chip = snd_pcm_substream_chip(substream);

	timestamp("periods %d, period size %d", params_periods(hw_params), 
	          params_period_size(hw_params));
	return 0;
}

static int as_prepare(struct snd_pcm_substream *substream)
{
	struct snd_as *chip = snd_pcm_substream_chip(substream);
	struct snd_pcm_runtime *runtime = substream->runtime;

	timestamp("periods %d, period size %lu", runtime->periods, runtime->period_size);
	return 0;
}

static int as_trigger(struct snd_pcm_substream *substream, int cmd)
{
	struct snd_as *chip = snd_pcm_substream_chip(substream);

	timestamp("cmd %d", cmd);
	return 0;
}

static snd_pcm_uframes_t as_pointer(struct snd_pcm_substream *substream)
{
	struct snd_as *chip = snd_pcm_substream_chip(substream);

	timestamp("");
	return 0;
}

static struct snd_pcm_ops as_pcm_ops = {
	.open = as_open,
	.close = as_close,
	.ioctl = snd_pcm_lib_ioctl,
	.hw_params = as_hw_params,
	.prepare = as_prepare,
	.trigger = as_trigger,
	.pointer = as_pointer,
};

static struct snd_card *card;

static int __init init(void)
{
	struct snd_as *chip;
	struct snd_pcm *pcm;
	int err;

	card = snd_card_new(-1, SNDRV_DEFAULT_STR1, THIS_MODULE, sizeof(*chip));
	chip = card->private_data;
	snd_pcm_new(card, "Test", 0, 1, 1, &pcm);
	pcm->private_data = chip;
	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &as_pcm_ops);
	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &as_pcm_ops);

	err = snd_card_register(card);
	return err;
}
module_init(init);

static void __exit exit(void)
{
        snd_card_free(card);
	return;
}
module_exit(exit);
