This is the mail archive of the gcc-bugs@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

Possible Bug


hi,
I started getting reports of a driver failing after folks upgraded their
systems.  They were all
now using gcc version  2.91.66  (RH 6.0 and SUSE 6.1).  I verified this
locally.    I haven't
sent you much documentation as this may be a known/fixed problem.  If
not I will gladly send
a more complete package.
The problem occurs when reading a hardware register from an addin A/D
converter board using inb_p.  Some bits in the register are tied
directly to hardware switches.  These bits can not change during
execution.  The register is read in two places and the bits appear to
change.  The register is also written to.  It seems as if the register
value is being cashe'd.
I have tried optimization levels O1, O2 and none, and get the same
result.
I will try using the latest version of GCC if necessary, but the machine
that shows the failure is not "netable" so this would not be easy to
do.  Enclosed are the driver C code and a dmesg dump showing the result
of the two reads.
regards
Jim Bean
/*
 * Copyright (C) 1995  Sam Moore, Warren Jasper  (CIO-DAS16/330)
 * Copyright (C) 1995  Ricky Beam, Rob Cox       (CIO-DAS1600)
 * Copyright (C) 1996  Joerg Weule
 * Copyright (C) 1998  Jim Bean                  (CIO-DAS1400)
 * All rights reserved.
 *
 * This software may be freely copied, modified, and redistributed
 * provided that this copyright notice is preserved on all copies.
 *
 * You may not distribute this software, in whole or in part, as part of
 * any commercial product without the express consent of the authors.
 *
 * There is no warranty or other guarantee of fitness of this software
 * for any purpose.  It is provided solely "as is".
 *
 */

/* version 1.0  Original (libc only)                       Tested RH 4.2
   version 1.1  Matching sti added to function StartSoft.
                Includes were cleaned up for glibc access. Tested RH 4.2, 5.2
   version 1.2  Use count macros added so that the module
                will work properly when autoloaded.        Tested RH 5.2
   version 1.3  Support for use with 4.2.x kernels added.
                Documentation improved. Tested RH 5.2 (kernels 2.0.36,2.2.5)
   version 1.3a Patched up fix for gcc/egcs incompatability.
   Tested RH 5.2 (kernels 2.0.36,2.2.5,2.3.43), RH 6.0 kernel 2.2.5*/

/***************************************************************************
 *
 * das1400.c
 *
 ***************************************************************************/

#include <linux/autoconf.h>
#if defined(CONFIG_MODVERSIONS) && !defined(MODVERSIONS)
#   define MODVERSIONS /* force it on */
#endif

#ifdef MODVERSIONS
#  include <linux/modversions.h>
#endif

#include <linux/module.h>
#include <linux/version.h>
#include <linux/mm.h>
#include <linux/malloc.h>
#include <asm/dma.h>
#include <asm/io.h>
#include <asm/system.h>

#include "adc.h"
#include "das1400.h"

#if LINUX_VERSION_CODE < 0x020100
#include <asm/segment.h>
#else
#include <asm/uaccess.h>
#endif


void copy_in (WORD *p, const void *x, unsigned long n){
#if LINUX_VERSION_CODE < 0x020100
   memcpy_tofs (p, x, n);
#else
   unsigned long ret;
   ret = copy_to_user (p, x, n);
#endif
}

/***************************************************************************
 *
 * Global data.
 *
 ***************************************************************************/

/* Interrupt context data. Controls interrupt service handler */

static BoardRec BoardData;                  /* Board specific information */

/* Values kept. */

static ChanRec Chan;                    /* Channel specific information   */

static int MajorNumber = DEFAULT_MAJOR_DEV; /* Major number compiled in   */

#if LINUX_VERSION_CODE >= 0x020300
static DECLARE_WAIT_QUEUE_HEAD(das0_wait);
#else
struct wait_queue  *das0_wait = NULL;              /* wait semaphore     */
#endif

/***************************************************************************
 *
 * Prototype of public and private functions.
 *
 ***************************************************************************/

static int  PacerReadBlocked(WORD*KernBuff,int WordCount);
static int  SoftReadBlocked (WORD*KernBuff,int WordCount);
static void SetGains(BYTE gain);
static int  SetPacerFreq(BoardRec *board);
static void StopPacer();
static void StartPacer();
static void StartSoft();
static void SetChannelMux(BYTE lowChan, BYTE highChan);
static void LoadPacer(BoardRec *board);
static void LoadCounter0( LONG arg );

#if LINUX_VERSION_CODE < 0x020100
static int das_read (struct inode *iNode, struct file *filePtr,
                                     char *buf, int count);
static int das_write (struct inode *iNode, struct file *filePtr,
		                     const char *buf, int count);
static void das_close (struct inode *iNode, struct file *filePtr);
#else
static ssize_t das_read(struct file * filePtr, char * buf,
			    size_t count, loff_t *ppos);
static ssize_t das_write(struct file * filePtr, const char * buf,
			    size_t count, loff_t *ppos);
static int  das_close (struct inode *iNode, struct file *filePtr);
#endif

static int das_open(struct inode *iNode, struct file *filePtr);
static int das_ioctl(struct inode *iNode, struct file *filePtr,
		     unsigned int cmd, LONG arg);
static void Int_Handler(int);
static void doDMA(void);
static void buf_check_len(void);

int init_module(void);
void cleanup_module(void);

/***************************************************************************
 *
 * Code for DMAbuffer
 *
 ***************************************************************************/

/*
 * Copyright (C) 1996 Dr. Joerg Weule
 * All rights reserved.
 *
 * This software may be freely copied, modified, and redistributed
 * provided that this copyright notice is preserved on all copies.
 *
 * You may not distribute this software, in whole or in part, as part of
 * any commercial product without the express consent of the authors.
 *
 * There is no warranty or other guarantee of fitness of this software
 * for any purpose.  It is provided solely "as is".
 *
 */

/***************************************************************************
 *
 * Buffer to use DMA.
 *
 ***************************************************************************/

/* The buffer is used as a ring of cells, each is filled with DMA. After
 * a transfer is complete, the next cell is installed for DMA.
 *
 * This idea needs the length of the buffer to be a multiple of the length of
 * DMA, which is a multiple of the length of the number of channels used.
 *
 * We call these length buf_len and buf_dma_len respectively.
 *
 * To be able to see if the buffer is full or empty, the buffer may not
 * be completely full.
 *
 * Buffer with data:
 *        -------------------------------------------------------------
 *       |..............ddddddddddddddddddddddddddd....................|
 *        -------------------------------------------------------------
 *                      ^                          ^
 *                   buf_a                       buf_b
 *
 * The concept and coding of the buffer was done by Dr. J"org Weule
 *                                                  weule@uni-duesseldorf.de
 */

#define BUF_RETRY_LIMIT 3
#define BUF_SIZE ( 16 * 1024 )

static WORD *DMAbuf;                        /* static DMA buffer space   */
static WORD *buf_p;                         /* DMAbuf + buf_b */
static int buf_dma_len = 1024 ;             /* length up to an interrupt */
static volatile int buf_a = 0 ;             /* DMS uses the Buffer as a ring */
static volatile int buf_b = 0 ;
static volatile int buf_len = BUF_SIZE ;
static volatile int buf_wait;               /* driver is sleeping */
static volatile int Clock_10MHz;            /* Xtal is 10 MHz (or 1 MHz) */

/***************************************************************************
 *
 *Set buf_len and check all the buf_..._len variables
 *
 ***************************************************************************/

static inline void buf_check_len(){

    int nScan = Chan.highScan - Chan.lowScan + 1 ;
    if ( nScan < 1 ) nScan = 1 ;
#ifdef DEBUG
    printk("%s: buf_check_len(): nScan        = %d\n", ADAPTER_ID,(int)nScan);
#endif

    if ( buf_dma_len > ( BUF_SIZE >> 2 )) buf_dma_len = BUF_SIZE  >> 2 ;

    buf_dma_len /= nScan ;
    if ( buf_dma_len == 0 ) buf_dma_len = 1 ;
    buf_dma_len *= nScan ;

    buf_len = BUF_SIZE ;
    buf_len /= buf_dma_len ;
    buf_len *= buf_dma_len ;

#ifdef DEBUG
    printk("%s: buf_check_len(): buf_dma_len  = %d\n", ADAPTER_ID,(int)buf_dma_len);
#endif
}

/***************************************************************************
 *
 * Read Data
 *
 ***************************************************************************/

static inline int buf_read(WORD*p,int n){

#ifdef DEBUG
   printk("%s: R: Entering buf_read(),buf_a=%d,buf_b=%d.\n",ADAPTER_ID,buf_a,buf_b);
#endif

   /*
    * Look for latest samples.
    * Copy the samples out.
    */
   if ( buf_a != buf_b ){
      int a = buf_a ;
      int b = buf_b ;
      int k ;
      if ( b > a ) {
         k = b - a ;
         if ( k > n ) k = n ;
         copy_in (p,DMAbuf+a,k*sizeof(WORD));
         a += k ;
         if ( a >= buf_len ) a = 0 ;
         buf_a = a ;
         return k ;
      } else {
         k = buf_len - a ;
         if ( k >= n ) {
            copy_in (p,DMAbuf+a,n*sizeof(WORD));
            a += n ;
            if ( a >= buf_len ) a = 0 ;
            buf_a = a ;
            return n ;
         } else {
            /* fist use count k, than l, return k + l */
            int l = b ;
            copy_in (p,DMAbuf+a,k*sizeof(WORD));
            p += k ;
            n -= k ;
            if ( l > n ) l = n ;
            copy_in (p,DMAbuf,l*sizeof(WORD));
            a = l ;
            buf_a = l ;
            return k + l ;
         }
      }
   }
   return 0;
}

/***************************************************************************
 *
 * Clear DMA area
 *
 ***************************************************************************/

static inline void buf_clear(void){
   int i = buf_dma_len ;
   WORD*p = buf_p ;
#ifdef DEBUG
   printk("%s: ?: buf_clear()\n", ADAPTER_ID);
#endif
   do *p++ = 0x3aaf ; while ( --i );
}

/***************************************************************************
 *
 * DMA post processing
 *
 ***************************************************************************/

static inline void buf_dma_postprocessing(void){
   int a , b , n ;
#ifdef DEBUG
   printk("%s: I: buf_dma_postprocessing()\n", ADAPTER_ID);
#endif
   a = buf_a ;
   b = buf_b ;
   buf_b = b = ( b + buf_dma_len ) % buf_len ;
   buf_p = DMAbuf+b;
   n = ( b - a ) % buf_len ;
   if ( n > ( buf_len - 2 * buf_dma_len )) {
      a = buf_a ;
      printk("%s: Buffer overrun !!!!!!!!!!!!!!!!!!\n",ADAPTER_ID);
      a = ( a + buf_dma_len ) % buf_len ;
      buf_a = a ;
   }
}

/***************************************************************************
 *
 * Read Data
 *
 ***************************************************************************/

static int inline PacerReadBlocked(WORD*KernBuff,int n){
  int i , r ;

#ifdef DEBUG
  printk("%s: R: PacerReadBlocked(%d) ---v---\n",ADAPTER_ID,n);
#endif
  if ( n <= 0 ) return 0 ;
  r = 0 ;
  do {
    i = buf_read(KernBuff,n);
    (WORD*)KernBuff += i ;
    r += i ;
    n -= i ;
    if ( n <= 0 ) break ;
    cli();
#ifdef DEBUG
    printk("%s: pacer sleep_on\n",ADAPTER_ID);
#endif
    buf_wait = TRUE ;
    interruptible_sleep_on(&das0_wait);

    /* Interrupted ?     */
    if ( buf_wait == TRUE ) break ;
  } while ( 1 ) ;
#ifdef DEBUG
  printk("%s: R: PacerReadBlocked(%d) ---^---\n",ADAPTER_ID,n);
#endif
  return r ;

}  /* PacerReadBlocked */

/*************************************************************************/

static int inline SoftReadBlocked(WORD*KernBuff,int n){
  int i , r , Sanity;
#ifdef DEBUG
  printk("%s: R: SoftReadBlocked(%d) ---v---\n",ADAPTER_ID,n);
#endif

  if ( n <= 0 ) return 0 ;

  r = 0 ;
  Sanity = n;
  do {
    Sanity--;
    /* Use DMA to get one dataset, nscan values. */
    buf_wait = TRUE ;
    doDMA ();

    cli();
    /* Sometimes the interrupt is completed before we have time to sleep. */
    if (buf_wait == TRUE){
#ifdef DEBUG
      printk("%s: R: soft sleep_on\n",ADAPTER_ID);
#endif
      interruptible_sleep_on(&das0_wait);
    }

    i = buf_read(KernBuff,Chan.nScan);
    (WORD*)KernBuff += i ;
    r += i ;
    n -= i ;
    if ( n <= 0 ) break ;

  } while ( Sanity > 0 ) ;
#ifdef DEBUG
  printk("%s: R: SoftReadBlocked(%d) ---^---\n",ADAPTER_ID,n);
#endif
  return r ;

}  /* SoftReadBlocked */

/***************************************************************************
 *
 * read() service function
 *
 ***************************************************************************/

#if LINUX_VERSION_CODE < 0x020100
static int das_read (struct inode *iNode, struct file *filePtr,
		     char *buf, int count){
  int stat;
#else
static ssize_t das_read(struct file * filePtr, char * buf,
			size_t count, loff_t *ppos){
#endif

  int n ;
  int Words;
  
#ifdef DEBUG
  printk("%s: R: das_read(): Entering das_read(%d) vvvvvvvvvv\n",ADAPTER_ID,count);
  if (count < 0) {
     printk("%s: R: das_read(): Negative count\n",ADAPTER_ID);
     return -1;
  }
#endif

  Words = count * Chan.nScan;
  
#if LINUX_VERSION_CODE < 0x020100
  /* Check that data can be written to file. */
  if ((stat = verify_area(VERIFY_WRITE, buf,
          sizeof(WORD) * Words)) != 0) {
    printk("%s: R: das_read: Failed VERIFY_WRITE\n",ADAPTER_ID);
    return -1;
  }
  /* Area is secure... */
#endif

  /* Read */
  switch (Chan.mode) {
    case ADC_PACER_CLOCK :

      if ( Chan.pacer == FALSE ) StartPacer();
    
      n = PacerReadBlocked ((WORD*)buf,Words);
      break;

    case ADC_SOFT_TRIGGER :

      n = SoftReadBlocked ((WORD*)buf,Words);
      break;

    case ADC_EXTERNAL_TRIGGER :
    default :
      n = 0;
      break;
  }
  Chan.total += n ;
  n *= sizeof(WORD);

#ifdef DEBUG
  printk("%s: R: das_read(): Leaving das_read() ^^^^^^^^^^^^\n",ADAPTER_ID);
#endif
  
  return(n);
}

/***************************************************************************
 *
 * write() service function
 *
 ***************************************************************************/
#if LINUX_VERSION_CODE < 0x020100
static int das_write(struct inode *iNode, struct file *filePtr,
                     const char *buf, int count)
#else
static ssize_t das_write(struct file * filePtr, const char * buf,
		       size_t count, loff_t *ppos)
#endif
{
  return(-EINVAL);
}


/***************************************************************************
 *
 * open() service handler
 *
 ***************************************************************************/

static int das_open(struct inode *iNode, struct file *filePtr)
{
  volatile int cntrl_reg = CNTRL_REG;
  volatile int status_reg = STATUS_REG;
  volatile int misc_reg = MISC_REG;

  /* Check if device is already open: only one process may read from a
     port at a time.  There is still the possibility of two processes 
     reading from two different channels messing things up. However,
     the overhead to check for this may not be worth it.     */
  
  if ( Chan.open == TRUE ) {
    return -EBUSY;
  }

  Chan.open = TRUE;             /* The device is open */
  Chan.pacer = FALSE;           /* Pacer and DMA not active */
  Chan.gain = BP_10_00V;	/* +/- 10V */
  Chan.mode = filePtr->f_flags; /* set trigger mode */
  Chan.highScan = (BYTE) 0;
  Chan.lowScan = (BYTE) 0;
  Chan.nScan = (int) 0;
  Chan.total = (long) 0;
  
  SetChannelMux(0,0);

  BoardData.pacerReg = 0x0;

  outb_p(0x0, status_reg);	/* Clear INT bit */
  outb_p(0x0, misc_reg);	/* Put in compatibility mode */
  {
    volatile BYTE bReg ;
    bReg = inb_p(cntrl_reg);
    bReg &= ~(CNTL_INTE | CNTL_DMA);
    outb_p(bReg, cntrl_reg);
  }
   disable_dma(DAS_DMA);
   buf_a = buf_b = 0;
   buf_dma_len = 1024;
   buf_len = BUF_SIZE;
   buf_wait = FALSE;
 
     
   /*   #ifdef DEBUG */
     printk("%s: das_open() end ...\n",ADAPTER_ID);
   /*   #endif */
  
  MOD_INC_USE_COUNT;

  return 0;   
}  /* open */

/***************************************************************************
 *
 * close() service handler
 *
 ***************************************************************************/

#if LINUX_VERSION_CODE >= 0x020200
static int das_close(struct inode *iNode, struct file *filePtr)
#else
static void das_close(struct inode *iNode, struct file *filePtr)
#endif
{
  volatile int status_reg = STATUS_REG;

  outb_p(0x00, status_reg);	/* Clear INT bit */

  StopPacer() ;
  /* #ifdef DEBUG */
  printk("%s: Words read: %ld.\n",ADAPTER_ID,(long)Chan.total);
  printk("%s: close().\n",ADAPTER_ID);
  /* #endif */
  
  Chan.open = FALSE;

  MOD_DEC_USE_COUNT;
#if LINUX_VERSION_CODE >= 0x020200
  return 0;
#endif
}

/***************************************************************************
 *
 * ioctl() service handler
 *
 ***************************************************************************/

/* Note:
   Remember that FIOCLEX, FIONCLEX, FIONBIO, and FIOASYN
   are reserved ioctl cmd numbers
   */

static int das_ioctl(struct inode *iNode, struct file *filePtr,
                     unsigned int cmd, LONG arg) {
  volatile int dio_reg = DIO_REG;
  volatile int pacer_clock_reg = PACER_CLOCK_REG;

  switch (cmd) {
    case ADC_START_PACER:
      StartPacer();
    break;

    case ADC_SET_GAINS:
      Chan.gain = (BYTE)arg;
#ifdef DEBUG
      printk("%s: ioctl(ADC_SET_GAINS) %#x\n", ADAPTER_ID, (int) arg);
#endif
      break;

    case ADC_SET_DMA_LEN:

#ifdef DEBUG
      printk("%s: ioctl(ADC_SET_DMA_LEN) %d\n", ADAPTER_ID, (int) buf_dma_len);
#endif
      if ( arg > 0 )  buf_dma_len = arg ;
    break ;

    case ADC_GET_DMA_LEN:
      buf_check_len();
      cli();
      put_user(buf_dma_len, (long*) arg);
      sti();
    break;
    
    case ADC_SET_PACER_FREQ:
#ifdef DEBUG
      printk("%s: ioctl(ADC_SET_PACER_FREQ) %ld\n", ADAPTER_ID, (long) arg);
#endif
      if ( BoardData.freq > MAX_FREQ  ) {
        printk("%s: ioctl:  BoardData.freq = %ld out of range.\n",ADAPTER_ID,
	     BoardData.freq);
        return -1;
      } else {
        BoardData.freq = (LONG)arg;
        SetPacerFreq(&BoardData);
        LoadPacer(&BoardData);	/* load the board frequency now */
        outb_p(BoardData.pacerReg, pacer_clock_reg);  
      }
    break;
    
    case ADC_GET_PACER_FREQ:
      cli();
      put_user(BoardData.freq, (long*) arg);
      sti();
    break;
    
    case ADC_STOP_PACER:
      StopPacer();
    break;
    
    case CTR0:
      if ( arg == 1 ) {
        BoardData.pacerReg |= 0x2;
      } else {
        BoardData.pacerReg &= ~(0x2);
      }
      outb_p(BoardData.pacerReg, pacer_clock_reg);  
    break;
    
    case COUNTER0:
      LoadCounter0( (LONG) arg );
    break;

    case ADC_GET_GAINS:
      cli();
      put_user(Chan.gain, (long*) arg);
      sti();
    break;

    case ADC_GET_RANGE:
      cli();
      put_user(Chan.Unipolar, (long*) arg);
      sti();
    break;

    case ADC_START_SOFT_TRIG:
       StartSoft();
    break;

    case ADC_DIO_OUT:
      outb_p((BYTE) arg, dio_reg);
    break;

    case ADC_DIO_IN:
      {volatile BYTE bReg ;
      cli();
      bReg = inb_p(dio_reg);
      put_user(bReg, (long*) arg);
      sti();
      }
    break;

    case ADC_SETMUX_LIMITS:
      Chan.highScan = (arg & 0xf);
      Chan.lowScan = (arg & 0xf0) >> 4 ;
      SetChannelMux(Chan.lowScan,Chan.highScan);
      if (Chan.highScan > AD_CHANNELS) {
        Chan.highScan = 0;
        BoardData.pacerReg &= 0x3;
        return(-EINVAL);
      }
      Chan.nScan = Chan.highScan - Chan.lowScan ;
      BoardData.pacerReg |= Chan.nScan << 4 ;
      Chan.nScan += 1 ;
      outb_p(BoardData.pacerReg, pacer_clock_reg);  
#ifdef DEBUG
    printk("%s: ioctl(ADC_SETMUX_LIMITS):  value = %#x\n",ADAPTER_ID, (int)arg);
    printk("%s: ioctl(ADC_SETMUX_LIMITS):  low = %#x  high = %#x\n",
        ADAPTER_ID, (int)Chan.lowScan,(int)Chan.highScan);
#endif
    break;
    
    case ADC_SET_MODE:
      Chan.mode = (BYTE) arg;
      BoardData.pacerReg &= ~(0x1);
    break;

    default:
      return(-EINVAL);
    break;
  }

  return 0;
} /* das_ioc */

/***************************************************************************
 *
 *
 ***************************************************************************/

static struct file_operations das_fops = {
  NULL,				/* seek    */
  das_read,			/* read    */
  das_write,			/* write   */
  NULL,				/* readdir */
  NULL,				/* select  */
  das_ioctl,			/* ioctl   */
  NULL,				/* select  */
  das_open,			/* open    */
#if LINUX_VERSION_CODE >= 0x020200
  NULL,
#endif
  das_close,			/* close   */
  NULL				/* fsync   */
};

/***************************************************************************
 *
 * Loads driver. Called when "insmod adc.o" is invoked on the command line.
 *               The board is set to IRQ, 
 *
 ***************************************************************************/

int init_module(void) {
  volatile int misc_reg = MISC_REG;
  volatile int cntrl_reg = CNTRL_REG;
  volatile int status_reg = STATUS_REG;
  volatile int burst_stat_reg = BURST_STAT_REG;
  volatile int burst_mode_reg = BURST_MODE_REG;
  volatile int mode_enable_reg = MODE_ENABLE_REG;
  volatile int convert_dis_reg = CONVERT_DIS_REG;
  register int i, retry;
  volatile BYTE bReg;
#ifdef DEBUG
  register int j;
#endif

  retry = 0;
  do {
    DMAbuf = (WORD*) kmalloc(BUF_SIZE * sizeof(WORD), GFP_DMA);
  } while ((DMAbuf == NULL) && (++retry < BUF_RETRY_LIMIT));

  if (!DMAbuf ) {
    printk("%s: Unable to allocate DMA buffers of %d bytes\n",
      ADAPTER_ID, BUF_SIZE * sizeof(WORD));
    return -EIO;
  }

  /* Register as a device with kernel.  */

  if (register_chrdev(MajorNumber, ADAPTER_ID, &das_fops)) {
    printk("%s: Unable to register character device.\n", ADAPTER_ID);
    return -EIO;
  } else {
    printk("%s: BASE=%#x IRQ=%d DMA=%d DMABufsiz= %d words\n",
      ADAPTER_ID, BASE_REG, DAS_IRQ, DAS_DMA, BUF_SIZE);
    printk("%s: DMABufsiz=%#x [%p-%p]\n",
      ADAPTER_ID, BUF_SIZE * sizeof(WORD), DMAbuf, DMAbuf+BUF_SIZE);
    printk("%s:  4/27/95 wjasper@tx.ncsu.edu sam@tx.ncsu.edu\n",ADAPTER_ID);
    printk("%s: 10/13/95 jfbeam@tx.ncsu.edu rncox@tx.ncsu.edu\n",ADAPTER_ID);
    printk("%s: 01/04/96 weule@uni-duesseldorf.de\n",ADAPTER_ID);
    printk("%s: 08/04/98 Jim-bean@worldnet.att.net\n",ADAPTER_ID);
  }

  cli();

  /* Turn out interrupt before enabling it */ 
  /* Set interrupt level on board */
  bReg = DAS_IRQ;
  bReg <<= 4;			/* Tell ADC which IRQ to use */
  outb_p(bReg, (int)cntrl_reg);	/* ... clearing INTE bit in control reg */

  /* Register interrupt handler. */
  if (request_irq(DAS_IRQ, (void*)Int_Handler, SA_INTERRUPT, ADAPTER_ID,NULL)) {
    sti();
    printk("%s: Can't request IRQ %d\n", ADAPTER_ID, DAS_IRQ);
    return -(EIO);
  }

  /* Register DMA. */
  if (request_dma(DAS_DMA, ADAPTER_ID)) {
    sti();
    printk("%s: Can't request DMA %d\n", ADAPTER_ID, DAS_DMA);
    free_irq(DAS_IRQ,NULL);
    return -(EIO);
  }

  /* Set all channel structures to show nothing active/open */
  for (i = 0; i < AD_CHANNELS; i++) {
    Chan.open = FALSE;
    Chan.adc_dio_pretrig = FALSE;
  }

#ifdef DEBUG
  for (i = 0, j = 0; i < BUF_SIZE; i++) {
    DMAbuf[i] = (WORD) j++;
  }

  printk("%s: DMAbuf == %p  \n",ADAPTER_ID, DMAbuf);
#endif

  /*  das0_wait = NULL; */
  {volatile int sReg;
  sReg = inb_p(status_reg);
  printk("%s Init: Status Reg == %x  \n",ADAPTER_ID, sReg);

  /* Note the setting of the Uni/Bi switch */
  /*Chan.Unipolar = sReg & ADSR_UB; */

  Chan.Unipolar = UNIPOLAR;

  /* Note the setting of the 16ch/8ch switch */
  /* Chan.Is_16_Ch = sReg & ADSR_MUX; */

  Chan.Is_16_Ch = (MUX == 16);
  }
  bReg = inb_p(burst_stat_reg);

  /* See if the board is set for 10Mhz */
  Clock_10MHz = bReg&DAS1400_CLK;

  printk("%s:     Burst Mode %s\n", ADAPTER_ID,
	 bReg&DAS1400_BME?"Enabled":"Disabled");
  printk("%s:     DAS1400 Mode %s\n", ADAPTER_ID,
	 bReg&DAS1400_ME?"Enabled":"Disabled");
  printk("%s:     Conversions %s\n", ADAPTER_ID,
	 bReg&DAS1400_CD?"Allowed":"Blocked");
  printk("%s:     WaitState %s\n", ADAPTER_ID,
	 bReg&DAS1400_WS?"Enabled":"Disabled");
  printk("%s:     Voltage range %s\n", ADAPTER_ID,
	 Chan.Unipolar?"Unipolar":"Bipolar");
  printk("%s:     MUX Switch    %s\n", ADAPTER_ID,
	 Chan.Is_16_Ch?"16 Ch Single Ended":"8 Ch Differential");
  printk("%s:     Using %dMHz Clocks\n", ADAPTER_ID,
	 Clock_10MHz?10:1);

  BoardData.freq = DEFAULT_FREQ; /* Set default pacer clock frequency */
  SetPacerFreq(&BoardData);
  outb_p(0x0, misc_reg);	 /* Put in compatibility mode */

  outb_p(0x40, mode_enable_reg);	/* DAS1400 Functions ENABLED */
  outb_p(0x00, burst_mode_reg);		/* Burst Mode DISABLED        */
  outb_p(0x00, convert_dis_reg);	/* Conversions are ENABLED   */

  sti();

#ifdef DEBUG
  printk("%s: Setup complete. IRQ set to %d\n", ADAPTER_ID, DAS_IRQ);
#endif

  return 0;
}   /* init_module */

/***************************************************************************
 *
 * Remove driver. Called when "rmmod adc" is run on the command line.
 *
 ***************************************************************************/

void cleanup_module(void) {

  if (MOD_IN_USE) {
    printk("%s: device busy, remove delayed\n", ADAPTER_ID);
    return;
  }

  /* Release the DMA channel and the IRQ */
  free_dma(DAS_DMA);
  free_irq(DAS_IRQ,NULL);

  /* Return the DMA buffers to free memery */
  if ( DMAbuf ) kfree(DMAbuf);

  if (unregister_chrdev(MajorNumber, ADAPTER_ID) != 0) {
    printk("%s: Unable to unregister character device.\n", ADAPTER_ID);
  } else {
    printk("%s: module removed.\n\n", ADAPTER_ID);
  }    
}

/***************************************************************************
 *
 * Interrupt handler used to service enhanced mode(interrupt) read().
 *
 ***************************************************************************/

void Int_Handler(int irq) {
  volatile int cntrl_reg = CNTRL_REG;
  volatile int status_reg = STATUS_REG;

  cli();

  { volatile BYTE bReg ;
    bReg = inb_p(cntrl_reg);
    if ( (bReg & CNTL_INTE) == 0) return;

#ifdef DEBUG
  printk("%s: I: Int_Handler(%d) v v v v v v v v v v \n",ADAPTER_ID,irq);
#endif

    bReg = inb_p(status_reg);
    if ( (bReg & ADSR_INT ) == 0 ){
    printk("%s: I: ERR: Int_Handler(%d) why ? Status Reg = %#x\n",
        ADAPTER_ID,irq,bReg);
      return ;
    }
    printk("%s Intr: Status Reg == %x  \n",ADAPTER_ID, bReg);
  }
  if ( *buf_p == 0x3aaf ) {
    printk("%s: I: ERR: Int_Handler(%d) why ? DMAbuf[buf_b] = %#x\n",
      ADAPTER_ID,irq,*buf_p);
    return ;
  }

  if ( Chan.mode == ADC_SOFT_TRIGGER ) {
      /* Now we have to set up the variables for the next DMA transfer.   */
      buf_dma_postprocessing();
      buf_wait = FALSE ;
#ifdef DEBUG
      printk("%s: I: soft wakeup \n",ADAPTER_ID);
#endif
      wake_up_interruptible(&das0_wait);

  } else {

    if ( Chan.pacer == TRUE ) {

      /* Now we have to set up the variables for the next DMA transfer.   */
      buf_dma_postprocessing();
      if ( buf_wait == TRUE ) {
        buf_wait = FALSE ;
#ifdef DEBUG
        printk("%s: I: pacer wakeup \n",ADAPTER_ID);
#endif
        wake_up_interruptible(&das0_wait);
      }
    }
    /*
     * Maybe we turn off interrupt and DMA but one is still in the system.
     * Chan.pacer is FALSE then.
     * We test this case now.  */

    if ( Chan.pacer == TRUE ) doDMA();
  }
#ifdef DEBUG
  printk("%s: I: Int_Handler(%d) ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ \n",ADAPTER_ID,irq);
#endif
  sti();
}  /* int_handler */

/***************************************************************************
 *
 * Set voltage gains.
 *
 ***************************************************************************/

static void SetGains( BYTE gain ) {
  volatile int misc_reg = MISC_REG;
  volatile BYTE mask;
  volatile BYTE test;
  
  mask = inb_p(misc_reg);	/* read the Analog Input Range Register */
  mask &= 0xf0;			/* mask off last 4 bits                 */
  mask |= gain;			/* set gain                             */
  outb_p(mask, misc_reg);
  test = inb_p(misc_reg);
#ifdef DEBUG
  printk("%s:     Gain Setting:  %#x \n", ADAPTER_ID, gain); 
#endif
  return;
}

/****************************************************************************
 *   Set Pacer Clock Frequency
 * 
 *   Description: 
 *       Set the counters so that the pacer clock runs at the
 *       desired frequency.  The frequency is generated by dividing
 *       down a 10 MHz clock, so all frequencies can not be generated.
 *       This routine calculated the divisor to generate a frequency
 *       as near as possible to the requested one.  It then calculates
 *       the real frequency and returns it .
 ****************************************************************************/

static int SetPacerFreq(BoardRec *board)
{
  short unsigned ctr1, ctr2;
  LONG product, error;

  /* divide 10MHz or 1MHz by frequency */

  if ( Clock_10MHz )
     product =  10000000.0 / board->freq + 0.5;
  else
     product =   1000000.0 / board->freq + 0.5;

  /*
     Now the job is to find two 16 bit numbers, that when multiplied
     together are approximately equal to product.  Start by setting
     one of them, ctr1 to 2 (minimum settable value) and increment until
     the error is minimized and ctr2 is less than 32768.
     
     NOTE: In Mode 2, a value of 1 is illegal! Therefore, crt1 and crt2
     can never be 1.
     */

  ctr1 = product / 32768;
  if ( ctr1 < 2 ) ctr1 = 2;
  ctr2 = product / ctr1;
  error = abs(product - ctr2*ctr1);

  while ( error && ctr1 < 32768  && ctr2 > 1 ) {
    ctr1++;
    ctr2 = product / ctr1;
    error = abs(product - ctr2*ctr1);
  }

  /* the frequency is prime, add 1 to it */

  if ( error ) {
    product++;
    ctr1 = product / 32768;
    if ( ctr1 < 2 ) ctr1 = 2;
    ctr2 = product / ctr1;
    error = abs(product - ctr2*ctr1);

    while ( error  && ctr1 < 32768 && ctr2 > 1) {
      ctr1++;
      ctr2 = product / ctr1;
      error = abs(product - ctr2*ctr1);
    }
  }

  /* we can't have ctr2 equal to 1, or system hangs */

  if ( ctr2 == 1 ) {
    ctr2++;
    ctr1 /= 2;
  }

  board->ctr1 = ctr1 ;
  board->ctr2 = ctr2 ;
  if ( Clock_10MHz )
     board->freq = 10000000.0/(ctr1*ctr2) + 0.5;
  else
     board->freq =  1000000.0/(ctr1*ctr2) + 0.5;

#ifdef DEBUG
  printk("%s: SetPacerFreq: A ctr1 %d ctr2 %d\n",ADAPTER_ID,
    board->ctr1, board->ctr2);
#endif

#ifdef DEBUG
  printk("%s: SetPacerFreq: Pacer Register set to %#x\n",
    ADAPTER_ID, BoardData.pacerReg);
#endif

  return 0;
}

/***************************************************************************
 *
 * Load two part frequency to pacer counter chip.
 *
 ***************************************************************************/

static void LoadPacer(BoardRec *board)
{
  volatile BYTE mask;
  volatile BYTE bData;

  /* Write the values of ctr1 and ctr2 into counter A1 and A2 */

  cli();

#ifdef DEBUG
  printk("%s: LoadPacer: load values: A ctr1 %#x ctr2 %#x\n",ADAPTER_ID,
    board->ctr1, board->ctr2);
#endif

  mask = C2 + MODE2 + LSBFIRST;
  outb_p(mask, COUNTERA_CONTROL); 

  bData = (BYTE) (board->ctr2 & 0xff);
  outb_p(bData, COUNTERA_2_DATA); 

  bData = (BYTE) (board->ctr2 >> 8);
  outb_p(bData, COUNTERA_2_DATA); 

  mask = C1 + MODE2 + LSBFIRST;
  outb_p(mask, COUNTERA_CONTROL); 

  bData = (board->ctr1 & 0xff);
  outb_p(bData, COUNTERA_1_DATA); 

  bData = (board->ctr1 >> 8) & 0xff;
  outb_p(bData, COUNTERA_1_DATA); 

  sti();
}

/***************************************************************************
 *
 * Load value into Counter 0  XXXX    Mode    MSB     LSB
 *                            Byte 3  Byte 2  Byte 1  Byte 0
 *
 ***************************************************************************/

static void LoadCounter0( LONG value ) {
  volatile BYTE mask;
  volatile BYTE bData;

  cli();

  /* Write the value into Counter 0 Mode 2 */
  
#ifdef DEBUG
  printk("%s: LoadCounter0: load value %#lx into Counter 0.\n",ADAPTER_ID,
    value);
#endif

  /* the mode is in the third byte */

  mask = 0xff & (value >> 16);
  mask += C0 + LSBFIRST;
  outb_p(mask, COUNTERA_CONTROL); 

  if ( value & 0xff000000 ) {
    /* load control word only */
    sti();
    return;
  } else {
    /* LSB in the first byte of value */

    bData = (BYTE) (value & 0xff);
    outb_p(bData, COUNTERA_0_DATA); 

    /* MSB in the second byte of value */

    bData = (BYTE) ((value >> 8) & 0xff);
    outb_p(bData, COUNTERA_0_DATA); 
  }
  sti();
}

/***************************************************************************
 *
 * Set which channels read() is interested in.
 *
 ***************************************************************************/

static void SetChannelMux(BYTE lowChan, BYTE highChan)
{
  volatile int mux_scan_limits = MUX_SCAN_LIMITS;
  volatile BYTE channelMask;

  channelMask = (highChan << 4) | lowChan;
  outb_p(channelMask, mux_scan_limits);
  
#ifdef DEBUG
  printk("%s: SetChannelMux: lowChan = %d  highChan = %d\n",
    ADAPTER_ID, lowChan, highChan);
#endif

  return;
}

/***************************************************************************
 *
 * Turn off pacer timer chip.
 *
 ***************************************************************************/

static void StopPacer() {
  volatile int cntrl_reg = CNTRL_REG;
  volatile BYTE bReg;

  cli();

#ifdef DEBUG
  printk("%s: C: StopPacer \n", ADAPTER_ID);
#endif

  /* Zero words passed to this routine is used to shutdown the DMA
       controller for the card.                                           */

  /* Note that this routine lets the card sending an interrupt.
   * We set Chan.pacer = FALSE to inform Int_Handler.
   */
  bReg = inb_p(cntrl_reg);
  bReg &= ~(CNTL_INTE | CNTL_DMA );
  outb_p(bReg, cntrl_reg);
  disable_dma(DAS_DMA);

#ifdef DEBUG
  printk("%s: C: cntrl_reg: %#2x \n", ADAPTER_ID, (int)bReg);
#endif

  /* The clock may run forever */

  Chan.pacer = FALSE ;

  sti();
}

/***************************************************************************/

static void StartSoft () {
  volatile int cntrl_reg = CNTRL_REG;
  volatile int mux_scan_limits = MUX_SCAN_LIMITS;
  volatile int burst_mode_reg = BURST_MODE_REG;
  volatile int convert_dis_reg = CONVERT_DIS_REG;
#ifdef DEBUG
  printk("%s: C: StartSoft \n", ADAPTER_ID);
#endif

  buf_b = buf_a = 0 ;
  buf_p = DMAbuf ;
  buf_check_len();
  {
    volatile BYTE bReg = 0x0 ;

    SetGains((BYTE)Chan.gain);
    SetChannelMux(Chan.lowScan, Chan.highScan);
    Chan.nScan = Chan.highScan - Chan.lowScan + 1 ;
    Chan.mode = ADC_SOFT_TRIGGER;
    /* Disable pacer if in soft trigger mode */
    Chan.pacer = FALSE;
    /* software triggered A/D only */ 
    bReg = DAS_IRQ << 4 ;
    outb_p(bReg,cntrl_reg);

    outb_p(0x40,convert_dis_reg); /* Disable Conversions */
    outb_p(0x40,burst_mode_reg);  /* Turns Burst Mode on */

    cli();
    bReg= (BYTE) inb_p(mux_scan_limits); /* Read/Write clears FIFO */
#ifdef DEBUG
    printk("%s: C: start_soft:  MUXval = %#x\n",ADAPTER_ID, (int)bReg);
#endif
    outb_p(bReg,mux_scan_limits);
    outb_p(0x0,convert_dis_reg); /* Enable Conversions  to the A/D */

    /* With the DMA controller now setup and active, we can tell the card
       to use the DMA transfer mode.                                      */
    bReg = DAS_IRQ << 4 ;
    bReg |= ( CNTL_DMA | CNTL_INTE );
    outb_p(bReg, cntrl_reg);

#ifdef DEBUG
  printk("%s: C: start_soft: cntrl_reg: %#2x \n", ADAPTER_ID, (int)bReg);
#endif

    sti();
    return ;
  }
}  /* StartSoft */

/***************************************************************************/

static void StartPacer() {
  volatile int cntrl_reg = CNTRL_REG;
  volatile int burst_stat_reg = BURST_STAT_REG;
  volatile int burst_mode_reg = BURST_MODE_REG;
  volatile int mux_scan_limits = MUX_SCAN_LIMITS;
  volatile int pacer_clock_reg = PACER_CLOCK_REG;
  volatile int convert_dis_reg = CONVERT_DIS_REG;
#ifdef DEBUG
  printk("%s: C: StartPacer \n", ADAPTER_ID);
#endif

  buf_b = buf_a = 0 ;
  buf_p = DMAbuf ;
  buf_check_len();
  {
    volatile BYTE bReg = 0x0 ;

    SetGains((BYTE)Chan.gain);
    SetChannelMux(Chan.lowScan, Chan.highScan);
    Chan.nScan = Chan.highScan - Chan.lowScan + 1 ;
    Chan.mode = ADC_PACER_CLOCK ;
    Chan.pacer = TRUE ;
    /* Disable pacer if in pacer mode */
    /* software triggered A/D only */ 
    bReg = DAS_IRQ << 4 ;
    outb_p(bReg,cntrl_reg);

    outb_p(0x40,convert_dis_reg); /* Disable Conversions */
    outb_p(0x40,burst_mode_reg);  /* Turns Burst Mode on */
    bReg = inb_p(burst_stat_reg);
    LoadPacer(&BoardData);
    BoardData.pacerReg |= 0x1 ;
#ifdef DEBUG
    printk("%s: C: start_pacer:  PacerREg = %#x\n",ADAPTER_ID,
        (int)BoardData.pacerReg);
#endif
    outb_p(BoardData.pacerReg,pacer_clock_reg);
    cli();
    bReg= (BYTE) inb_p(mux_scan_limits); /* Read/Write clears FIFO */
#ifdef DEBUG
    printk("%s: C: start_pacer:  MUXval = %#x\n",ADAPTER_ID, (int)bReg);
#endif
    outb_p(bReg,mux_scan_limits);
    outb_p(0x0,convert_dis_reg); /* Enable Conversions  to the A/D */

    /* With the DMA controller now setup and active, we can tell the card
       to use the DMA transfer mode.                                      */
    bReg = DAS_IRQ << 4 ;
    bReg |= ( (DAS_IRQ << 4) | CNTL_DMA | CNTL_INTE| CNTL_TS0 | CNTL_TS1 );
    outb_p(bReg, cntrl_reg);

#ifdef DEBUG
  printk("%s: C: start_pacer: cntrl_reg: %#2x \n", ADAPTER_ID, (int)bReg);
#endif

    doDMA();

#ifdef DEBUG
  bReg = inb_p(cntrl_reg);
  printk("%s: C: start_pacer: cntrl_reg: %#2x \n", ADAPTER_ID, (int)bReg);
#endif

    return ;
  }
}  /* StartPacer */

/***************************************************************************
 *
 * Block copy of data samples from data register to kernel memory.
 *
 ***************************************************************************/

static void doDMA() {
  volatile int status_reg = STATUS_REG;
  unsigned long flags;

    /* Ok, we have been asked to grab a block of data with the DMA */

    save_flags(flags);
    cli();

    /* The DMA controller has to be setup before the DAS-1400 is told
       to use DMA transfer mode.  So, setup the DMA first.  DMA controller
       must be off before it can be setup.                                 */
    buf_clear();

#ifdef DEBUG
    printk("%s: ?: doDma(): buf_p = %p, Count = %d\n", ADAPTER_ID,
        buf_p, buf_dma_len * sizeof(WORD));
#endif
    disable_dma  (DAS_DMA);
    clear_dma_ff (DAS_DMA);
    set_dma_mode (DAS_DMA, DMA_MODE_READ);
    set_dma_addr (DAS_DMA, (long) buf_p);
    set_dma_count(DAS_DMA, buf_dma_len * sizeof(WORD));

    outb_p (0xFF, status_reg);  /* Clear status flag. */

    if ( Chan.mode == ADC_SOFT_TRIGGER )
        /* Trigger transfer */
        outb_p (0xFF, LSB_AND_CHNLS);
    enable_dma(DAS_DMA);

#ifdef DEBUG
    printk("%s: ?: doDMA(): DMA triggered and INT is enabled\n",ADAPTER_ID);
#endif

    restore_flags(flags);
  return;
}
Linux version 2.2.5-15 (root@porky.devel.redhat.com) (gcc version egcs-2.91.66 19990314/Linux (egcs-1.1.2 release)) #1 Mon Apr 19 21:39:28 EDT 1999
Console: colour VGA+ 80x25
Calibrating delay loop... 66.56 BogoMIPS
Memory: 14396k/16384k available (1060k kernel code, 412k reserved, 456k data, 60k init)
Checking if this processor honours the WP bit even in supervisor mode... Ok.
VFS: Diskquotas version dquot_6.4.0 initialized
CPU: AMD Am5x86-WB stepping 04
Checking 386/387 coupling... OK, FPU using exception 16 error reporting.
Checking 'hlt' instruction... OK.
Checking for popad bug... OK.
POSIX conformance testing by UNIFIX
PCI: PCI BIOS revision 2.10 entry at 0xfba50
PCI: Using configuration type 1
PCI: Probing PCI hardware
Linux NET4.0 for Linux 2.2
Based upon Swansea University Computer Society NET3.039
NET4: Unix domain sockets 1.0 for Linux NET4.0.
NET4: Linux TCP/IP 1.0 for NET4.0
IP Protocols: ICMP, UDP, TCP, IGMP
Initializing RT netlink socket
Starting kswapd v 1.5 
Serial driver version 4.27 with MANY_PORTS MULTIPORT SHARE_IRQ enabled
ttyS00 at 0x03f8 (irq = 4) is a 16550A
ttyS01 at 0x02f8 (irq = 3) is a 16550A
pty: 256 Unix98 ptys configured
apm: BIOS version 1.1 Flags 0x07 (Driver version 1.9)
Real Time Clock Driver v1.09
RAM disk driver initialized:  16 RAM disks of 4096K size
hda: ST3630A, ATA DISK drive
hdd: ATAPI 36X CDROM DRIVE, ATAPI CDROM drive
ide0 at 0x1f0-0x1f7,0x3f6 on irq 14
ide1 at 0x170-0x177,0x376 on irq 15
hda: ST3630A, 601MB w/120kB Cache, CHS=611/32/63
hdd: ATAPI 40X CD-ROM drive, 128kB Cache
Uniform CDROM driver Revision: 2.54
Floppy drive(s): fd0 is 1.44M
FDC 0 is an 8272A
md driver 0.90.0 MAX_MD_DEVS=256, MAX_REAL=12
raid5: measuring checksumming speed
   8regs     :   102.489 MB/sec
   32regs    :    80.391 MB/sec
using fastest function: 8regs (102.489 MB/sec)
scsi : 0 hosts.
scsi : detected total.
md.c: sizeof(mdp_super_t) = 4096
Partition check:
 hda: hda1 hda2 hda3
autodetecting RAID arrays
autorun ...
... autorun DONE.
VFS: Mounted root (ext2 filesystem) readonly.
Freeing unused kernel memory: 60k freed
Adding Swap: 17132k swap-space (priority -1)
CIO/DAS-1400: BASE=0x300 IRQ=7 DMA=1 DMABufsiz= 16384 words
CIO/DAS-1400: DMABufsiz=0x8000 [c0d90000-c0d98000]
CIO/DAS-1400:  4/27/95 wjasper@tx.ncsu.edu sam@tx.ncsu.edu
CIO/DAS-1400: 10/13/95 jfbeam@tx.ncsu.edu rncox@tx.ncsu.edu
CIO/DAS-1400: 01/04/96 weule@uni-duesseldorf.de
CIO/DAS-1400: 08/04/98 Jim-bean@worldnet.att.net
CIO/DAS-1400 Init: Status Reg == 0  
CIO/DAS-1400:     Burst Mode Enabled
CIO/DAS-1400:     DAS1400 Mode Enabled
CIO/DAS-1400:     Conversions Allowed
CIO/DAS-1400:     WaitState Disabled
CIO/DAS-1400:     Voltage range Unipolar
CIO/DAS-1400:     MUX Switch    16 Ch Single Ended
CIO/DAS-1400:     Using 1MHz Clocks
CIO/DAS-1400: das_open() end ...
CIO/DAS-1400 Intr: Status Reg == 70  
CIO/DAS-1400: Words read: 4.
CIO/DAS-1400: close().

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]