#include "myaudio.h"
static int speaker_ioctl(struct inode *inode, struct file *file,
		       unsigned int cmd, unsigned long arg)
{
  int val, ret; /* , *arg; */
  audio_buf_info bufinfo;
  count_info cinfo;
  unsigned long flags;		/* To save the flags for pushf; cli;  */
  val = -EINVAL;

  switch (cmd)
    {
    case OSS_GETVERSION:
#ifdef SPKDBG
      printk("Sound version ioctled");
#endif SPKDBG
      return put_user(SOUND_VERSION, (int *)arg);

    case SNDCTL_DSP_SYNC:   /*reset buffers*/
      sync_output();

#ifdef SPKDBG
      printk("Sync ioctled");
#endif SPKDBG
      return 0;

    case SNDCTL_DSP_POST:  /* Basically means sleep till sync */

#ifdef SPKDBG
      printk("post ioctled");
#endif SPKDBG
      return 0; /* Mandatorily on a test basis- I would have preferred sync_output() */

    case SNDCTL_DSP_RESET: /*reset dsp*/
      sync_output();

#ifdef SPKDBG
      printk("reset ioctled");
#endif SPKDBG
      return 0;

    case SNDCTL_DSP_GETFMTS: /*return in val,dsp format mask */

#ifdef SPKDBG
      printk("format query ioctled");
#endif SPKDBG
      return put_user(AFMT_U8, (int *)arg);


    case SNDCTL_DSP_SETFMT: /*set dsp format mask and return val*/

#ifdef SPKDBG
      printk("set format ioctled");
#endif SPKDBG
      return put_user(AFMT_U8, (int *)arg);

    case SNDCTL_DSP_GETISPACE: /*output in audio_buf_info (include/soundcard.h) amount of unused internal buffer space - invalid for o/p only device*/
      return -EINVAL;

    case SNDCTL_DSP_GETOSPACE: /*same as above but valid */
      if (!(file->f_mode & FMODE_WRITE))
	return -EINVAL;

      spin_lock_irqsave(lock, flags);

      bufinfo.fragsize = buffer_length;
      bufinfo.bytes = buffer_length - (data_ptr - data_buffer);
      bufinfo.fragstotal = 1;
      bufinfo.fragments = (data_ptr == data_buffer) ? 1 : 0;
      spin_unlock_irqrestore(lock, flags);

#ifdef SPKDBG
      printk("o/p space query ioctled");
#endif SPKDBG

      return copy_to_user((void *)arg, &bufinfo, sizeof(bufinfo)) ? -EFAULT : 0; /* :) lifted that gimmick from es1370.c  */

    case SNDCTL_DSP_NONBLOCK: /*set non-blocking flag in the dev structs */
      file->f_flags |= O_NONBLOCK;

#ifdef SPKDBG
      printk("non block flag ioctled");
#endif SPKDBG
      return 0;

    case SNDCTL_DSP_GETCAPS:  /* we have no duplex, real time, trigger or mmapping. So there - return 0 */

#ifdef SPKDBG
      printk("GETCAPS ioctled");
#endif SPKDBG
      return 0;

    case SOUND_PCM_WRITE_RATE:  /* returns speed in val in Hz */

#ifdef SPKDBG
      printk("PCM_WRITE_RATE ioctled");
#endif SPKDBG

      return put_user(22050, (int *)arg);

    case SOUND_PCM_READ_RATE:   /*returns read speed in val. Want to return -EINVAL */

#ifdef SPKDBG
      printk("PCM_READ_RATE ioctled");
#endif SPKDBG

      return -EINVAL;

    case SNDCTL_DSP_STEREO:    /* returns and sets the number of channels aske d for (1,2)*/
      get_user_ret(val, (int *)arg, -EFAULT);
      if (val>0) return -EINVAL;

#ifdef SPKDBG
      printk("DSP_STEREO ioctled");
#endif SPKDBG
      return put_user(0, (int *)arg);

    case SOUND_PCM_WRITE_CHANNELS:  /* returns and sets the number of channels requested for (0,1) */

#ifdef SPKDBG
      printk("PCM_WRITE_CHANNELS ioctled");
#endif SPKDBG

      return put_user(0, (int *)arg);

    case SOUND_PCM_READ_CHANNELS: /* We return 1 (mono) here */

#ifdef SPKDBG
      printk("PCM_READ_CHANNELS ioctled");
#endif SPKDBG
      return put_user(1, (int *)arg);

    case SOUND_PCM_READ_BITS:     /* sets and returns bits per sample */

#ifdef SPKDBG
      printk("PCM_READ_BITS ioctled");
#endif SPKDBG
      return put_user(8, (int *)arg);

    case SNDCTL_DSP_SETDUPLEX:    /* no need to support */

#ifdef SPKDBG
      printk("SETDUPLEX ioctled");
#endif SPKDBG
      return -EINVAL;
    case SNDCTL_DSP_PROFILE:       /* no need to support */

#ifdef SPKDBG
      printk("DSP_PROFILE query ioctled");
#endif SPKDBG
      return -EINVAL;
    case SNDCTL_DSP_GETODELAY:      /* I think that this is simply the time it takes for the damned thing to stop playing when told to stop */
    if (!(file->f_mode & FMODE_WRITE))
	return -EINVAL;
      spin_lock_irqsave(lock, flags);
      val = (data_buffer ==data_ptr) ? 1 : 0; 
      spin_unlock_irqrestore(lock, flags);

#ifdef SPKDBG
      printk("GETODELAY ioctled");
#endif SPKDBG
      return put_user(val, (int *)arg); 

      /********** from dma_ioctl ****************/
    case SNDCTL_DSP_GETIPTR:

#ifdef SPKDBG
      printk("GETIPTR ioctled");
#endif SPKDBG
      return -EINVAL;

    case SNDCTL_DSP_GETOPTR:         /*we need to support this URGENTLY something called a cinfo struct is set */
      if (!(file->f_mode & FMODE_WRITE))

#ifdef SPKDBG
      printk("GETOPTR ioctled");
#endif SPKDBG
	return -EINVAL;
      spin_lock_irqsave(lock, flags);
      cinfo.bytes = data_ptr - data_buffer; /* number of bytes read  */
      cinfo.blocks = 1;	/* number of buffer frags */
      cinfo.ptr = data_ptr;
      spin_unlock_irqrestore(lock, flags);
      return copy_to_user((void *)arg, &cinfo, sizeof(cinfo));

    case SNDCTL_DSP_GETBLKSIZE:
      return put_user(32000 /*(unsigned) buffer_length*/, (int *)arg);
    default:

      return val;
    }
  return put_user(val, (int *)arg);
}

static ssize_t speaker_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
{
  
	if(count < 0) return -EINVAL;


	if (!count)		/* Flush output */
	{
		  sync_output();
		  return 0;
	}

	while(canplay){
	  if(file->f_flags & O_NONBLOCK) return -EAGAIN;
#ifdef SPKDBG
	  printk("<1> sleeping current process");
#endif
	  schedule();
	  /*interruptible_sleep_on(&wait);*/
#ifdef SPKDBG
	  printk("<1> Process awoken");
#endif
	  if(signal_pending(current))  return -ERESTARTSYS;
	}

	if (!canplay)
	  {
	  count = (count <=32000 ? count : 32000);
	  if(copy_from_user((void *)data_buffer, buf, count))
		   return -EFAULT;
	     data_ptr = data_buffer;
	     buffer_length = count;
	     timeout=count*1193180/pit_counter/sampling_factor;
	     canplay = 1;

#ifdef SPKDBG
	     printk("<1> %i bytes written", count);     
#endif

	  return count;
	  }

	return 0;	/* The idea is to return 0 bytes and leave the problem to the client */
}
	  

static ssize_t speaker_read(struct file *file, char *buf, size_t count, loff_t *ppos)
{

#ifdef SPKDBG
  printk("sound device read ");
#endif SPKDBG

  return -EINVAL;
}


static int speaker_open(struct inode *inode, struct file *file)
   {
  /*Check for re-entrancy*/
     if(virgin >= 2) return -EBUSY;
     virgin =1;

#ifdef SPKDBG
      printk("Sound Device opened....");
#endif SPKDBG
     return 0;

   }

static void speaker_close(struct inode *inode,struct file *file)
   {
     virgin = 0;

#ifdef SPKDBG
      printk("Sound device closed");
#endif SPKDBG
    
   }

static int fops_sync_output(struct inode *inode, struct file *file)
{
  sync_output();
  return 0;
}


void sync_output()
{
  canplay = 0;
  data_ptr = data_buffer;
  buffer_length = 0;

}

int helper_get_pit_count(void)
{
  long ntsc=1193180;
  return ntsc/HZ;
}







void helper_set_pit_count(int counter)
{
  cli();
  outb(0x36,0x43);

  outb((char)(counter & 0xff),0x40);

  outb((char)(counter >> 8),0x40);
  sti();
}


int init_module(void)
{
  
  /* Initailize all global variables */
  if((device_major=register_sound_dsp(&speaker_fops, -1))<0)
    {
      printk(KERN_WARNING "Cannot get device dsp device allocation");
      return device_major;
    }


  virgin=0; /*  Just in case you didn't know....... ;)  */
  berio=0; /*set to 8 for 16 bit mono */
  canplay = 0;
  sampling_factor = 1; /* ie; counter = 22Khz / sampling_factor; */
  pit_counter = 54;

  /*allocate room for our 32KB buffer */
  data_buffer = (long) kmalloc(32000, GFP_KERNEL);  /*this is assuming that kmalloc NEVER FAILS */


  timer_idt_ptr = setvect(0x20,(long)myhandler);
  old_pit_counter = helper_get_pit_count();
  temp_pit_counter = compensation_count = old_pit_counter/pit_counter;

  helper_set_pit_count(pit_counter);

  printk("<1>Hopefully, irq0 has been hooked from vector 20 to %p  \n",myhandler); 
  return 0;

}

void cleanup_module(void)
{
  helper_set_pit_count(old_pit_counter);
  setvect(0x20,timer_idt_ptr);

  unregister_sound_dsp(device_major);

  kfree((void *)data_buffer);

  printk("<1>Phew! Graceful exit!!!!! \n irq0 reset to %p \n",(void *)timer_idt_ptr);
}

MODULE_AUTHOR("Cherry George Mathew, berryplumis@yahoo.com");
MODULE_DESCRIPTION("Sound Driver for the pc speaker");







