This is the mail archive of the
gcc-bugs@gcc.gnu.org
mailing list for the GCC project.
Possible Bug
- To: gcc-bugs at gcc dot gnu dot org
- Subject: Possible Bug
- From: Jim Bean <Jim-bean at worldnet dot att dot net>
- Date: Tue, 28 Mar 2000 15:39:53 -0500
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().