/*
 * mcbsp_module.c: Create a serial data communications device on a McBSP port of the BeagleBoard Rev. C
 *
 *  based on chardev.c: Creates a read-only char device that says how many times
 *  you've read from the dev file
 */

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <asm/uaccess.h>        /* for put_user */
#include <linux/cdev.h>         /* for character device struct 'cdev' */
#include <linux/slab.h>			/* for kmalloc */
#include <linux/device.h>

/* for DMA Mapping */
#include <linux/dma-mapping.h>

/* for Beagleboard OMAP McBSP */
#include "../arch/arm/plat-omap/include/plat/mcbsp.h"

/*  
 *  Prototypes - this would normally go in a .h file
 */
int init_module(void);
void cleanup_module(void);
static int device_open(struct inode *, struct file *);
static int device_release(struct inode *, struct file *);
static ssize_t device_read(struct file *, char *, size_t, loff_t *);
static ssize_t device_write(struct file *, const char *, size_t, loff_t *);
static loff_t dev_llseek(struct file *, loff_t, int );
static unsigned int mcbsp_poll(struct file *, struct poll_table_struct *);
static long mcbsp_ioctl (struct file *, unsigned int, unsigned long );
static int get_flags( int );
int configure_mcbsp(void);


#ifndef DEBUG_MCBSP
#define DEBUG_MCBSP 1
#endif

#define SUCCESS 0
#define DEVICE_NAME "mcbsp-module"   /* Dev name as it appears in /proc/devices   */
#define BUF_LEN 128                  /* Max length of the message from the device */

/*
 * Get rid of that "kernel taint" message
 */
MODULE_LICENSE("GPL");

MODULE_SUPPORTED_DEVICE("mcbsp1");
MODULE_AUTHOR("SSL");
MODULE_DESCRIPTION("Opens the mcbsp module as a serial port");

/* 
 * Global variables are declared as static, so are global within the file. 
 */

static int Major;               /* Major number assigned to our device driver */
static int Device_Open = 0;     /* Is device open?  
                                 * Used to prevent multiple access to device */
//static char msg[BUF_LEN];       /* The msg the device will give when asked */

static int msg_start, msg_len;

static struct file_operations my_fops = {
  .owner = THIS_MODULE,
  .llseek = dev_llseek,
  .read = device_read,
  .aio_read = NULL,     
	/* no async reading, will use .read instead */
  .write = device_write,
  .aio_write = NULL,    
	/* no async writing, will use .write instead */
  .readdir = NULL,      
	/* reading directories not valid here */
  .poll = mcbsp_poll,   
	/* should be implemented to enable the kernel to sleep our device when appropriate */
  .unlocked_ioctl = mcbsp_ioctl, 
	/* necessary for full implementation */
  .compat_ioctl = NULL, 
	/* not implemented, needed for old ioctl compatibility on 64-bit systems */
  .mmap = NULL,         
	/* memory mapping not implemented */
  .open = device_open,
  .flush = NULL,        
	/* kernel will ignore requests until implemented. Barrett thinks this should be written later to ensure messages to other devices are written before the BeagleBoard shuts down or the driver exits, etc.  */
  .release = device_release,
  .fsync = NULL,        
	/* will return EINVAL if user calls this */
  .aio_fsync = NULL,    
	/* no asynch stuff */
  .fasync = NULL,       
	/* no asynch notifications */
  .lock = NULL,         
	/* not implemented */
  .sendpage = NULL,     
	/* not implemented */
  .get_unmapped_area = NULL, 
	/* not needed */
  .check_flags = get_flags  
	/* check flags set by fcntl() calls */
};

/* still need to tweak these settings - putting in '0' by default to fill in later*/ 
static struct omap_mcbsp_reg_cfg my_cfg; 


/*
static struct device mcbsp_dev = {
  .name = DEVICE_NAME,
  .fops = &my_fops
};
*/

struct mcbsp_dev {
	struct cdev 	cdev;
	dma_addr_t 		x_dma;
	dma_addr_t		r_dma;
	char			*xbuff;
	char			*rbuff;
	unsigned int 	dma_chan;
	unsigned int	major;
	unsigned int	minor;
	struct device	dev; /* not sure if this will cause bugs alongside cdev */
};

static struct mcbsp_dev my_mcbsp_dev = {
	.major = 99,
	.minor = 0
};

#define RBUFF_LEN 512
#define XBUFF_LEN 512

/* helper functions to twiddle appropriate bits */
inline
u16 bit_on(u16 var, unsigned int bit)
{
	return var | (1 << bit);
}
inline
u16 bit_off(u16 var, unsigned int bit)
{
	return var & ~((u16)1 << bit);
}

/* Set up configuration register control variables. Refer to 
	 * the AM/DM37x Multimedia Device Silicon Revision 1.x (Rev. P) manual
     * for bit definitions and what to set. This was file name "sprugn4p.pdf". 
	 * (Section 21.6)
     */
void setup_config(void)
{

#ifdef DEBUG_MCBSP
	printk(KERN_NOTICE "before XCCR = 0x%X\n", my_cfg.xccr );
#endif 
	/* *************************************** */
	/* Reg - XCCR */
	my_cfg.xccr = 0; // clean slate
	/* EXTCLKGATE - external clock gate */
	my_cfg.xccr = bit_on(my_cfg.xccr, 15);
	/* PPCONNCECT - pair to pair connection */
	my_cfg.xccr = bit_off(my_cfg.xccr, 14);  // DX pin will go high-imp when no frame to send
	/* DXENDLY - data transmit delay - needs DXENA bit to be 1 to matter */
	my_cfg.xccr = my_cfg.xccr | DXENDLY(1); // 18 ns delay, the default
	/* XFULL_CYCLE */
	my_cfg.xccr = bit_on(my_cfg.xccr, 11); 
	/* DLB - digital loopback - bit 5*/
	my_cfg.xccr = bit_on(my_cfg.xccr, 5);
	/* XDMAEN - transmit DMA enable - when zero, will gate the external transmit DMA request */
	my_cfg.xccr = bit_off(my_cfg.xccr, 3);
	/* XDISABLE - transmit disable - stop transmitting at next frame boundary */
	my_cfg.xccr = bit_off(my_cfg.xccr, 0);

#ifdef DEBUG_MCBSP
	printk(KERN_NOTICE "before RCCR = 0x%X\n", my_cfg.rccr );
#endif 

	/* ***************************************** */
	/* Reg - RCCR */
	my_cfg.rccr = 0; // clean slate
	/* RFULL_CYCLE */
	my_cfg.rccr = bit_on(my_cfg.rccr, 11);
	/* RDMAEN - receive DMA enable */
	my_cfg.rccr = bit_on(my_cfg.rccr, 3);
	/* RDISABLE */
	my_cfg.rccr = bit_off(my_cfg.rccr, 0);
	

	/* ***************************************** */
	/* Reg SPCR2 */
	my_cfg.spcr2 = 0; // clean slate
	/* FREE - free run mode, ignore Msuspend input */
	my_cfg.spcr2 = bit_on(my_cfg.spcr2, 9);
	/* SOFT - soft suspend mode enable - bit 8*/
		/* leave disabled - module will stop its activity immediately following Msuspend */
	/* FRST - frame-sync generator reset - bit 7 */
		/* ? leave reset (0) - FSG not generated by the sample-rate generator */
	/* GRST - sample rate generator reset - bit 6 */
		/* ? leave reset */
	/* XINTM - transmit interrupt mode (legacy) - bits 5:4 */
		/* ? */
	/* XSYNCERR - transmit synchronization error - bit 3 */
		/* writing 0 here clears the legacy transmit interrupt if asserted due to XSYNCERROR condition */
	/* XEMPTY - transmit shift register XSR Empty - bit 2 - read only */
	/* XRDY - transmitter ready - bit 1 - read only */
	/* XRST - Transmitter reset - bit 0 */
		/* leave at 0 until configuration is complete */
	

	/* ****************************************** */
	/* Reg SPCR1 */
	my_cfg.spcr1 = 0; // clean slate
	/* RJUST - right justification of data - bits 14:13 */
		/* leave as 0, right-justify and zero-fill MSBs in DRR */
	/* DXENA - DX enabler - bit 7 */
	my_cfg.spcr1 = bit_on(my_cfg.spcr1, 7);
	/* RINTM - receive interrupt mode (legacy) - bits 5:4 */
		/* leave as 0, receive interrupt driven by RRDY (i.e. end of word) and end of frame in A-bis mode */
	/* RSYNCERR - Receive Synchronization Error - bit 3 */
		/* leave alone */
	/* RRST - Receiver reset - bit 0 */
		/* leave as 0 until configuration is complete */


	/* ********************************************** */
	/* Reg - RCR2 */
	my_cfg.rcr2 = 0; // clean slate
	/* RPHASE - Receive phases - bit 15 */
		/* leave 0 - single phase frame */
	/* RFRLEN2 - Receive Frame Length 2 - bits 14:8 */
		/* don't care w/ single phase selected */
	/* RWDLEN2 - Receive word length 2 - bits 7:5 */
		/* 0 for 8 bit; 1 for 12 bit; 2 for 16 bit; 3 for 20 bit; 4 for 24 bit;
	   	   5 for 32 bit; */
	my_cfg.rcr2 = my_cfg.rcr2 | (5 << 5); 
	/* RREVERSE - Receive reverse mode - bit 4:3 */
		/* leave as 0, data transfer starts with MSB first */
		/* RDATDLY - receive data delay - bit 1:0 */
		/* 0 for 0-bit delay; 1 for 1 bit; 2 for 2 bit */
	my_cfg.rcr2 = my_cfg.rcr2 | 1 ; 

	/* ************************************** */
	/* Reg - RCR1 */
	my_cfg.rcr1 = 0; 
	/* RFRLEN1 - Receive frame length 1 - bits 14:8 */
		/* set to one less than the desired number of words per frame */
	my_cfg.rcr1 = my_cfg.rcr1 | (0 << 8); // 1 word per frame
	/* RWDLEN1 - Receive word length 1 - bits 7:5 */
		/* 0 for 8 bit; 1 for 12 bit; 2 for 16 bit; 3 for 20 bit; 4 for 24 bit;
   	 	  5 for 32 bit; */
	my_cfg.rcr1 = my_cfg.rcr1 | (5 << 5); // 32 bit words


	/* *********************************************** */
	/* Reg - XCR2 */
	my_cfg.xcr2 = 0;
	/* XPHASE - transmit phases - bit 15 */
		/* 0 for single phase; 1 for dual-phase */
	/* XFRLEN2 - transmit frame length 2 - bits 14:8 */
		/* don't care for single-phase */
	/* XWDLEN2 - transmit word length 2 - bits 7:5 */
	my_cfg.xcr2 = my_cfg.xcr2 | (5 << 5); // 32 bit wods
	/* XREVERSE - transmit reverse mode - bits 4:3 */
		/* leave at 0, data transfer starts with MSB first */
	/* XDATDLY - transmit data delay - bits 1:0 */
		/* 0 for 0-bit delay; 1 for 1 bit; 2 for 2 bit */
	my_cfg.xcr2 = my_cfg.xcr2 | 1;


	/* ************************************************ */
	/* Reg - XCR1 */
	/* XFRLEN1 - transmit frame length 1 - bits 14:8 */
	/* set to one less than the desired number of words per frame */
	my_cfg.xcr1 = my_cfg.xcr1 | (0 << 8); // 1 word per frame
	/* XWDLEN1 - transmit word length 1 - bits 7:5 */		
	/* 0 for 8 bit; 1 for 12 bit; 2 for 16 bit; 3 for 20 bit; 4 for 24 bit;
   	   5 for 32 bit; */
	my_cfg.xcr1 = my_cfg.xcr1 | (5 << 5);

	/* *********************************************** */
	/* Reg SRGR2 */
	my_cfg.srgr2 = 0;
	/* GSYNC - sample rate generator synchronization - bit 15 */
		/* only used when the external clock (CLKS) drives the SRG clock */
	/* CLKSP - CLKS polarity clock edge select - bit 14 */
		/* 0 - rising; 1 - falling */
	my_cfg.srgr2 = bit_off(my_cfg.srgr2, 14);
	/* CLKSM - SRG clock mode - bit 13 */
		/* 0 and SCLKME=0 - SRG clock derived from CLKS pin */
		/* 0 and SCLKME=1 - SRG clock derived from the CLKR input pin */
		/* 1 and SCLKME=0 - SRG clock derived from the McBSPi_ICLK */
		/* 1 and SCLKME=1 - SRG clock derived from the CLKX input pin */
		/* leave as 0 */
	/* FSGM - sample rate generator transmit frame-sync mode - bit 12 */
	/* used when FSXM=1 in PCR */
	/* 0 - FSX generated when trans-buff not empty */
	/* 1 - trans frame-sync driven by SRG's FSG  */
	my_cfg.srgr2 = bit_on(my_cfg.srgr2, 12); 	
	/* FPER - frame period - bits 11:0 */
	/* this value plus one determines hwen the frame-sync signal becomes active */
	/* range: 1 to 4096 periods */
	/* leave at zero because I don't knwo what we'll need yet. */

	/* *********************************************** */
	/* Reg SRGR1 */
	my_cfg.srgr1 = 0;
	/* FWID - frame width - bits 15:8 */
	/* this value plus one determines the width of the frame-sync pulse, FSG,
		during its active period. Range 1 to 256 CLKG periods 
	*/
	my_cfg.srgr1 = my_cfg.srgr1 | (0 << 8);
	/* CLKGDV - sample rate generator clock divider - bits 7:0 */
	/* value used as divide-down number to generate the required SRG clock frequency */
	my_cfg.srgr1 = my_cfg.srgr1 | 1;

	/* ********************************************** */
	/* Reg MCR2 */
	my_cfg.mcr2 = 0;
	/* XMCME - transmit multichannel mode select - bit 9 */
	/* only applicable if XMCM non-zero */
	/* XPBBLK - transmit partition B block (legacy) - bits 8:7 */
	/* XPABLK - transmit partition A Block (legacy) - bits 6:5 */
	/* XMCM - multichannel enable bits 1:0 */
	/* leave at zero */

	/* ********************************************** */
	/* Reg MCR1 */
	my_cfg.mcr1 = 0;
	/* RMCME - receive multichannel partition mode select - bit 9 */
	/* RPBBLK - receive partition b block - bits 8:7 */
	/* RPABLK - receive partition a block - bits 6:5 */
	/* RMCM - receive multichannel mode enable - bit 0 */
	/* leave as 0 */

	/* ********************************************** */
	/* Reg PCR */
	my_cfg.pcr0 = 0;
	/* IDLE_EN - idle enable (legacy) - bit 14 */
	/* 0 - running; 1 - enable idle (shut off clocks) */
	/* XIOEN - transmit GPIO mode when XRST=0 - bit 13 */
	/* 0 - be a serial port; 1 - DX pin is output, FSX, CLKX are IO */
	/* RIOEN - receive GPIO mode when RRST=0 - bit 12 */
	/* 0 - be a serial port; 1 - DR and CLKS are input, FSR and CLKR are GPIO */
	/* FSXM - transmit frame-sync mode - bit 11 */
	/* 0 - external frame sync */
	/* 1 frame sync determined by SRG frame sync mode bit FSGM in SRGR2 */
	my_cfg.pcr0 = bit_on(my_cfg.pcr0, 11);
	/* FSRM - receive frame-sync mode - bit 10 */
	/* 0 - external frame sync; FSR is an input pin */
	/* 1 frame sync generated internally by SRG; FSR is an output pin except when GSYNC=1 in SRGR */
	my_cfg.pcr0 = bit_on(my_cfg.pcr0, 10);
	/* CLKXM - transmitter clock mode - bit 9 */
	/* 0 - external clock on CLKX pin */
	/* 1 - CLKX is output pin driven by internal SRG */
	my_cfg.pcr0 = bit_on(my_cfg.pcr0, 9);
	/* CLKRM - receive clock mode - bit 8 */
	/* 0 - external clock on CLKR pin */
	/* 1 - CLKR is output pin driven by internal SRG */
	my_cfg.pcr0 = bit_on(my_cfg.pcr0, 8);
	/* SCLKME - input clock select helper - bit 7 */
	/* CLKG frequency = (Input clock frequency) / (CLKGDV + 1 ) */
	/* SCLKME=0:
		CLKSM = 0: Signal on CLKS pin
		CLKSM = 1: McBSPi_ICLK clock
	   SCLKME=1:
		CLKSM = 0: Signal on CLKR pin
		CLKSM = 1: Signal on CLKX pin */
	my_cfg.pcr0 = bit_off(my_cfg.pcr0, 7);
	/* CLKS_STAT - CLKS pin status - read only - bit 6 */
	/* reads the value of CLKS pin when it is configured GPIO */
	/* DX_STAT - DX pin status - bit 5 */
	/* reads/writes DX pin value when configured as GPIO */
	/* DR_STAT - DR pin status - read only - bit 4 */
	/* reads DR pin value when configured as GPIO */
	/* FSXP - transmit frame sync pulse polarity - bit 3 */
	/* 0 - FSX pulse is active high */
	/* 1 - FSX pulse is active low */
	/* FSRP - receive frame sync pulse polarity - bit 2 */
	/* 0 - FSX pulse is active high */
	/* 1 - FSX pulse is active low */
	/* CLKXP - transmit clock polarity - bit 1 */
	/* 0 - data driven on rising edge of CLKX */
	/* 1 - data driven on falling edge of CLKX */
	/* CLKRP - receive clock polirity - bit 0 */
	/* 0 - data sampled on falling edge of CLKR */
	/* 1 - data sampled on rising edge of CLKR */
	
	/* ******************************************************************* */
	/* Reg RCERC - RCERH */
	/* RCER[C-H] - receive channel enable on partition [C-H] - bits 15:0 */
	/* bit n = 0 - disable nth channel on block [C-H] */
	/* bit n = 1 - enable nth channel on block [C-H] */
	/* not necessary if XMCM=0 */
	my_cfg.rcerc = 0x00;
	my_cfg.rcerd = 0x00;
	my_cfg.rcere = 0x00;
	my_cfg.rcerf = 0x00;
	my_cfg.rcerg = 0x00;
	my_cfg.rcerh = 0x00; 

	/* ********************************************************************/
	/* Reg XCERC -> XCERH */
	/* XCER[C-H] - transmit channel enable on partition [C-H] - bits 15:0 */
	/* bit n = 0 - disable nth channel on block [C-H] */
	/* bit n = 1 - enable nth channel on block [C-H] */
	/* not necessary if RMCM=0 */
	my_cfg.xcerc = 0x00; 
	my_cfg.xcerd = 0x00; 
	my_cfg.xcere = 0x00; 
	my_cfg.xcerf = 0x00; 
	my_cfg.xcerg = 0x00; 
	my_cfg.xcerh = 0x00; 

#ifdef DEBUG_MCBSP
	printk(KERN_NOTICE "after XCCR = 0x%X\n", my_cfg.xccr );
	printk(KERN_NOTICE "after RCCR = 0x%X\n", my_cfg.rccr );
	printk(KERN_NOTICE "after SPCR2 = 0x%X\n", my_cfg.spcr2 );
	printk(KERN_NOTICE "after SPCR1 = 0x%X\n", my_cfg.spcr1 );
	printk(KERN_NOTICE "after SRGR2 = 0x%X\n", my_cfg.srgr2 );
	printk(KERN_NOTICE "after SRGR1 = 0x%X\n", my_cfg.srgr1 );
	printk(KERN_NOTICE "after RCR2 = 0x%X\n", my_cfg.rcr2 );
	printk(KERN_NOTICE "after RCR1 = 0x%X\n", my_cfg.rcr1 );
	printk(KERN_NOTICE "after XCR2 = 0x%X\n", my_cfg.xcr2 );
	printk(KERN_NOTICE "after XCR1 = 0x%X\n", my_cfg.xcr1 );
	printk(KERN_NOTICE "after PCR0 = 0x%X\n", my_cfg.pcr0 );
#endif 
}

/*
 *  Configure the BeagleBoard's McBSP port based on sprugn4o.pdf
 */
int configure_mcbsp(void)
{
  int rc = 0;

	my_mcbsp_dev.xbuff = NULL;
  	my_mcbsp_dev.xbuff = kmalloc( XBUFF_LEN, GFP_DMA );
	if ( NULL == my_mcbsp_dev.xbuff ) {
		printk(KERN_ALERT "mcbsp_module configure_mcbsp: failed to allocate xbuff! \n");
		return -1;
	}
	//my_mcbsp_dev.xbuff = malloc( XBUFF_LEN ); /* might not need this in the end */

	my_mcbsp_dev.rbuff = NULL;
	my_mcbsp_dev.rbuff = kmalloc( RBUFF_LEN, GFP_DMA );
	if ( NULL == my_mcbsp_dev.rbuff ) {
		printk(KERN_ALERT "mcbsp_module configure_mcbsp: failed to allocate rbuff! \n");
		return -1;
	}
	//my_mcbsp_dev.rbuff = malloc( RBUFF_LEN ); /* might not need this in the end */

	/* set IO type for the mcbsp dev we want, must be called before requesting it */  
  rc = omap_mcbsp_set_io_type(OMAP_MCBSP1, OMAP_MCBSP_IRQ_IO);
#ifdef DEBUG_MCBSP
  printk(KERN_NOTICE "mcbsp_module: omap_mcbsp_set_io_mode(1,IRQ) returns %d\n", rc);
#endif

  	/* request McBSP */
	rc = omap_mcbsp_request(OMAP_MCBSP1);
#ifdef DEBUG_MCBSP
	printk(KERN_NOTICE "mcbsp_module: omap_mcbsp_request(1) returns %d\n", rc);
#endif

#ifdef DEBUG_MCBSP
	
#endif

	setup_config();


  /* do configure our McBSP device */
#ifdef DEBUG_MCBSP
	printk(KERN_NOTICE "mcbsp_module: calling omap_mcbsp_config()...    \n");
#endif
	omap_mcbsp_config( OMAP_MCBSP1, &my_cfg );
#ifdef DEBUG_MCBSP
	printk(KERN_NOTICE "mcbsp_module:                                   OK!\n ");
#endif
	
	
	/* setup DMA */
/*
	my_mcbsp_dev.x_dma = dma_map_single( &my_mcbsp_dev.dev, 
										my_mcbsp_dev.xbuff,
										XBUFF_LEN,
										DMA_TO_DEVICE );
	if ( NULL == my_mcbsp_dev.x_dma ) {
		printk(KERN_ALERT "mcbsp_module init: failed to map dma for xmit!");
		return -1;
	}
	my_mcbsp_dev.r_dma = dma_map_single( &my_mcbsp_dev.dev, 
										my_mcbsp_dev.rbuff,
										RBUFF_LEN,
										DMA_FROM_DEVICE );
	if ( NULL == my_mcbsp_dev.x_dma ) {
		printk(KERN_ALERT "mcbsp_module init: failed to map dma for read!");
		return -1;
	}
*/

/*
  rc = omap_mcbsp_xmit_buffer( OMAP_MCBSP1, my_mcbsp_dev.x_dma, XBUFF_LEN);
#ifdef DEBUG_MCBSP
  printk(KERN_INFO "mcbsp_module: omap_mcbsp_xmit_buffer() returns %d\n", rc);
#endif 

  rc = omap_mcbsp_recv_buffer( OMAP_MCBSP1, my_mcbsp_dev.r_dma, XBUFF_LEN);
#ifdef DEBUG_MCBSP
  printk(KERN_INFO "mcbsp_module: omap_mcbsp_recv_buffer() returns %d\n", rc);
#endif
*/

#ifdef DEBUG_MCBSP
  /* debug */
  printk(KERN_INFO "mcbsp_module: max_tx_threshold (1) = %d\n",
	 omap_mcbsp_get_max_tx_threshold(OMAP_MCBSP1) );
  printk(KERN_INFO "mcbsp_module: max_tx_threshold (2) = %d\n",
	 omap_mcbsp_get_max_tx_threshold(OMAP_MCBSP1) );

 printk(KERN_INFO "mcbsp_module: max_rx_threshold = %d\n",
	 omap_mcbsp_get_max_rx_threshold(OMAP_MCBSP1) );
  printk(KERN_INFO "mcbsp_module: tx_threshold = %d\n",
	 omap_mcbsp_get_tx_delay(OMAP_MCBSP1) );
  printk(KERN_INFO "mcbsp_module: rx_threshold = %d\n",
	 omap_mcbsp_get_rx_delay(OMAP_MCBSP1) );
  printk(KERN_INFO "mcbsp_module: fifo size = %d\n", 
	 omap_mcbsp_get_fifo_size(OMAP_MCBSP1) );
  printk(KERN_INFO "mcbsp_module: dma_op_mode = %d\n", 
	omap_mcbsp_get_dma_op_mode(OMAP_MCBSP1) );
#endif

  return rc;
}


/*
 * This function is called when the module is loaded
 */
int init_module(void)
{
	int rc;
	int devno = MKDEV( my_mcbsp_dev.major, my_mcbsp_dev.minor+1 ) ;
	my_mcbsp_dev.dma_chan = 0;

	my_mcbsp_dev.cdev.owner = THIS_MODULE;
	cdev_init( &my_mcbsp_dev.cdev, &my_fops );
	my_mcbsp_dev.cdev.ops = &my_fops;


	/* init McBSP driver */
/*	rc = omap_mcbsp_init();
#ifdef DEBUG_MCBSP
	printk(KERN_INFO "mcbsp_module: omap_mcbsp_init() returns %d\n", rc);
#endif
*/



  	/* configure McBSP */
  	configure_mcbsp();


	/* call McBSP start - tx=1 and rx=1 */
#ifdef DEBUG_MCBSP
	printk(KERN_INFO "mcbsp_module: omap_mcbsp_start(1,1,1) called \n");
#endif 
/* hanging on next call ... */
	omap_mcbsp_start( OMAP_MCBSP1, 1, 1);
#ifdef DEBUG_MCBSP
	printk(KERN_INFO "                                             OK.\n");
#endif 

  	/*    Major = register_chrdev(0, DEVICE_NAME, &fops); */
	/*    Major = register_chrdev(0, DEVICE_NAME, &my_fops ); 
    if (Major < 0) {
        printk(KERN_ALERT "Registering char device failed with %d\n", Major);
        return Major;
    }
	*/
	/* the above was the old way. */

	rc = cdev_add( &my_mcbsp_dev.cdev, devno, 1);
	if ( rc < 0 ) {
		printk(KERN_ALERT "mcbsp_module init: Failed to add character device!	\n");
		return -1;
	}
	/* our device is now 'live' to the kernel - better be ready to do work! */

#ifdef DEBUG_MCBSP
    printk(KERN_INFO "I was assigned major number %d. To talk to\n", Major);
    printk(KERN_INFO "the driver, create a dev file with\n");
    printk(KERN_INFO "'mknod /dev/%s c %d 0'.\n", DEVICE_NAME, Major);
    printk(KERN_INFO "Try various minor numbers. Try to cat and echo to\n");
    printk(KERN_INFO "the device file.\n");
    printk(KERN_INFO "Remove the device file and module when done.\n");
#endif

    msg_start= 0;
    msg_len= 0;

    return SUCCESS;
}


/*
 * This function is called when the module is unloaded
 */
void cleanup_module(void)
{
	int tx, rx;
//, rc;
	tx = 1; /* transmit was turned on */
	rx = 1; /* receive was turned on */
	
	/* 
     * Unregister the device 
     */
	cdev_del( &my_mcbsp_dev.cdev ); /* device no longer 'live' */

	/* stop McBSP */
	omap_mcbsp_stop( OMAP_MCBSP1, tx, rx );
	/* not sure about the value of the 'tx' and 'rx' parameters above */
#ifdef DEBUG_MCBSP
	printk(KERN_INFO "mcbsp_module cleanup: omap_mcbsp_stop(1,1,1) called (returns void)\n");
#endif 

	/* free McBSP channel */
	omap_mcbsp_free( OMAP_MCBSP1 );
#ifdef DEBUG_MCBSP
	printk(KERN_INFO "mcbsp_module cleanup: omap_mcbsp_free(1) called (returns void)\n");
#endif

    
    /*int ret = unregister_chrdev(Major, DEVICE_NAME);*/
    /*if (ret < 0)
        printk(KERN_ALERT "Error in unregister_chrdev: %d\n", ret);*/
	/* above is old way. */ 
}

/*
 * Methods
 */

/* 
 * Called when a process tries to open the device file, like
 * "cat /dev/mycharfile"
 */
static int device_open(struct inode *inode, struct file *filp)
{
	struct mcbsp_dev *dev; 
	dev = container_of(inode->i_cdev, struct mcbsp_dev, cdev );
	filp->private_data = dev; /* suggested by pg 59 of O'Reilly's Linux dev driver book */

    if (Device_Open)
        return -EBUSY;
	int rc=0;

    Device_Open++;
    try_module_get(THIS_MODULE);
	
	/* register any interrupts before DMA channel */
	// TODO 

	/* register my MDA channels */
	/*my_mcbsp_dev.dma_chan = -1;
	rc = -1;
	while ( (rc != 0) && (my_mcbsp_dev.dma_chan < MAX_DMA_CHANNELS))
	 {
		++my_mcbsp_dev.dma_chan;
		rc = request_dma( my_mcbsp_dev.dma_chan, "mcbsp");
#ifdef DEBUG_MCBSP
		if ( rc == EINVAL)
			printk(KERN_ERROR "mcbsp_module open: request_dma(%d, ''mcbsp'') says EINVAL!\n", my_DMA_channel);
		if (rc == EBUSY)
			printk(KERN_INFO "mcbsp_module open: request_dma(%d, ''mcbsp'') says EBUSY.\n", my_DMA_channel);
#endif
	}*/

	if (rc !=0) {
		/* undo all the stuff we did to open */
		return -1;
	}

    return SUCCESS;
}

/* 
 * Called when a process closes the device file.
 */
static int device_release(struct inode *inode, struct file *file)
{
    Device_Open--;          /* We're now ready for our next caller */

	dma_unmap_single( &my_mcbsp_dev.dev, my_mcbsp_dev.x_dma, XBUFF_LEN, DMA_TO_DEVICE );
	dma_unmap_single( &my_mcbsp_dev.dev, my_mcbsp_dev.r_dma, RBUFF_LEN, DMA_FROM_DEVICE );
	/* free any interrupts */
	// TODO

    /* 
     * Decrement the usage count, or else once you opened the file, you'll
     * never get get rid of the module. 
     */
    module_put(THIS_MODULE);

    return 0;
}

/* 
 * Called when a process, which already opened the dev file, attempts to
 * read from it.
 */
static ssize_t device_read(struct file *filp,   /* see include/linux/fs.h   */
                           char *buffer,        /* buffer to fill with data */
                           size_t length,       /* length of the buffer     */
                           loff_t * offset)
{
    /*
     * Number of bytes actually written to the buffer 
     */
    int bytes_read = 0;
	int rc = 0;
	msg_start = 0;

  rc = omap_mcbsp_recv_buffer( OMAP_MCBSP1, my_mcbsp_dev.r_dma, XBUFF_LEN);
#ifdef DEBUG_MCBSP
  printk(KERN_INFO "mcbsp_module: omap_mcbsp_recv_buffer() returns %d\n", rc);
#endif


	char *rbuff = (char*)my_mcbsp_dev.r_dma;
    /* 
     * Actually put the data into the buffer 
     */
    while(length && msg_len) {
        /* 
         * The buffer is in the user data segment, not the kernel 
         * segment so "*" assignment won't work.  We have to use 
         * put_user which copies data from the kernel data segment to
         * the user data segment. 
         */
        put_user(rbuff[msg_start], buffer++);

        msg_start= (msg_start + 1) % BUF_LEN;

        length--;
        bytes_read++;
        msg_len--;
    }

    /* 
     * Most read functions return the number of bytes put into the buffer
     */
    return bytes_read;
}

/*  
 * Called when a process writes to dev file: echo "hi" > /dev/hello 
 */
static ssize_t
device_write(struct file *filp, const char *buff, size_t len, loff_t * off)
{
	int rc=0;
    /*
     * Number of bytes actually read from the buffer 
     */
    int bytes_read = 0;
	msg_len = 0;

	char *xbuff = (char*)my_mcbsp_dev.x_dma;

	/* 1. copy userspace data to kernel dma buffer */
    while(len < BUF_LEN && len) {
        get_user( xbuff[msg_len], buff++);

        bytes_read++;
        msg_len++;
        len--;
    }

	/* 2. transmit buffer */
	rc = omap_mcbsp_xmit_buffer( OMAP_MCBSP1, my_mcbsp_dev.x_dma, XBUFF_LEN);
#ifdef DEBUG_MCBSP
  printk(KERN_INFO "mcbsp_module: omap_mcbsp_xmit_buffer() returns %d\n", rc);
#endif 

    return bytes_read;
}

/*
 * Check the flags set by the fcntl(F_SETFL...) call
 */
static int get_flags( int f )
{
  /* TODO */
  return 0;
}
/*
 * This function ensures seeking is not attempted on our device by always returning a negative number
 */
static loff_t dev_llseek(struct file *filp, loff_t off, int n)
{
  return EINVAL;
}

/* 
 *  This function should be implemented to provide the kernel with information that can be used to put the calling process to sleep until I/O becomes possible.
 */
static unsigned int mcbsp_poll(struct file *filp, struct poll_table_struct *wait)
{
  unsigned int mask = 0;

  /* TODO 
   * 
   *    device is readable if userspace output FIFO is not empty
   *    device is writable if userspace input FIFO is not empty
   */

  return mask;
}

/*
 * IO-control necessary for full implementation, can be mostly a dummy function; called with .unlocked_ioctl hook
 */
static long mcbsp_ioctl (struct file *filp, unsigned int cmd, unsigned long arg )
{
  /* TODO */

  return 0; 
}
