/* phoenix.c -- Phoenix driver for 2.4.x & 2.6.x kernels 25-Feb-2005 BPA To compile under 2.4.x kernels gcc -Wall -O2 -c -DMODULE -D__KERNEL__ -I/usr/src/linux/include -o ph.o ph.c Note: '/usr/src/linux' should have kernel source for compilation last modified on 19-JUL-05 */ // Uncommend the line below, for using parallel port interrupt IRQ7 #define USE_IRQ #include #include #include #include #include #include #include #include #include /* for put_user */ #include /* for I/O */ #include "phdriver.h" // Has info to be shared with user programs MODULE_LICENSE ("GPL"); MODULE_AUTHOR ("Ajith Kumar, Nuclear Science Centre"); #define DATA 0x378 /* printer port I/O addresses */ #define STAT 0x379 #define CTL 0x37A #define ADCLO 0 // Phoenix Hardware address information #define ADCHI 1 // Offset for different functions #define ADADR 2 #define ADSTRT 3 #define DAC 4 #define SM 5 #define DOUT 6 #define DIN 7 #define ENBIT 8 /* to avoid decoder spikes */ #define ADEOC 128 /* connected to S7 of status byte */ #define ERR_BREAK {error=TRUE; break;} // Just to save some typing u8 waitForHigh (int b); // Internal function prototypes u8 waitForLow (int b); u8 markRising (int b, int index); u8 markFalling (int b, int index); void outPort (u8 data, u8 port); void pulseOut (int pin, int th, int tl, int np); void invPulseOut (int pin, int th, int tl, int np); u8 timedReadADC (int index); void rotateMotor (int nsteps, int dir); void user_delay(u32 del); int motorDelay = 10; // 10 msecs for Stepper Motor u8 ADCtimeout = FALSE; int ADCdelay = 120; // Conversion time for ADC0809 chip static int maxWait = 2000000; // nearly 2 seconds timeout struct timeval begin; // used by user_delay() and ADC READBLOCK drvdata *data; // storage to be allocated dynamically #ifdef USE_IRQ #define IRQBIT 0x40 #define IRQENBIT 0x10 static int irq = 7; static int irq_count = 0; #define NCHAN 4096 static int waiting_for_irq; static int hist[NCHAN]; void readadc(void); irqreturn_t ppisr (int irqnum, void *device, struct pt_regs *regs) { if(irqnum != 7) return IRQ_NONE; if(!waiting_for_irq) { printk("unexpected Interrupt from %d\n",irq); return IRQ_HANDLED; } readadc(); outb(7, DATA); // clear busy flag outb(0, DATA); return IRQ_HANDLED; } #endif static int device_ioctl (struct inode *inode, struct file *file, unsigned int ioctl_num, unsigned long ioctl_param) { /* IOCTL calls from user programs land here. Arguments: 'ioctl_num' is the command send by the user. 'ioctl_param' is a pointer to some memory block in user space. Driver gets/puts data from there according to the requirement. */ static u8 dataval, ctlval; // current data & ctlval on the PCB u32 *up = (void *) ioctl_param; // our pointer to user space u8 error, tmp8, chan; u32 tmpi, k, buf[4]; error = FALSE; switch (ioctl_num) { case READ_HIST: if(copy_to_user(up, hist, sizeof(hist))) return -EFAULT; break; case CLEAR_HIST: for(k=0; k < NCHAN; ++k) hist[k] = 0; // Clear Histogram break; case START_HIST: outb(7, DATA); /* clear busy flag */ outb(0, DATA); waiting_for_irq = 1; outb(IRQENBIT, CTL); // printk("START: IRQ enabled...\n"); break; case STOP_HIST: outb(0, CTL); waiting_for_irq = 0; // printk("STOP: IRQ disabled...%d\n",sizeof(hist)); break; case OUTDATA: copy_from_user (&tmp8, up, sizeof (u8)); dataval = tmp8; outb (tmp8, DATA); break; case OUTCTL: copy_from_user (&tmp8, up, sizeof (u8)); tmp8 ^= 0x0b; // Parallel port inverts C0, C1 and C3 inside ctlval = tmp8; outb (tmp8, CTL); break; case INSTAT: tmp8 = inb(STAT); copy_to_user (up, &tmp8, sizeof (u8)); break; case PULSEDATA: copy_from_user (&tmp8, up, sizeof (u8)); tmp8 ^= dataval; for(k = 0; k < 1024; ++k) { outb(tmp8, DATA); outb(dataval, DATA); } break; case PULSECTL: copy_from_user (&tmp8, up, sizeof (u8)); tmp8 ^= ctlval; for(k = 0; k < 1024; ++k) { outb(tmp8, CTL); outb(ctlval, CTL); } break; #ifdef USE_IRQ case ENBIRQ: irq_count = 0; outb(DIN, CTL); outb(ENBIT+DIN, CTL); outb(ENBIT+DIN+IRQENBIT, CTL); printk("IRQ Enabled\n"); break; case IRQCOUNT: copy_to_user (up, &irq_count, sizeof (u32)); outb(DIN, CTL); printk("IRQ Disabled. Count = %d\n", irq_count); break; #endif case TIMESTAMP: do_gettimeofday (&data[0].t); copy_to_user (up, data, sizeof (drvdata)); break; case DIGIN: // Reads the 4 bit digital input word outb (DIN, CTL); outb (ENBIT + DIN, CTL); tmp8 = (inb (STAT) >> 3) & 15; outb (DIN, CTL); copy_to_user (up, &tmp8, sizeof (u8)); break; case DIGOUT: // Writes a value, 0 to 255, to the digital output copy_from_user (&tmp8, up, sizeof (u8)); outPort(tmp8, DOUT); break; case SETDAC: // Accepts a number , 0 to 255, and sets the DAC to that value. copy_from_user (&tmp8, up, sizeof (u8)); outb (tmp8, DATA); /* DAC goes from -5 to 5 for 0 to 255 */ outb (DAC, CTL); outb (ENBIT + DAC, CTL); outb (DAC, CTL); break; case MOTORCW: // Rotate motor clockwise by 'tmpi' steps copy_from_user (&tmpi, up, sizeof (u32)); rotateMotor (tmpi, 1); break; case MOTORCCW: // counter clockwise copy_from_user (&tmpi, up, sizeof (u32)); rotateMotor (tmpi, 0); break; case SETMOTOR: // Set any desired value (0 to 15) copy_from_user (&tmp8, up, sizeof (u8)); outPort (tmp8, SM); break; case SELECTADC: // Select the ADC channel to be used. (0 to 7) copy_from_user (&tmp8, up, sizeof (u8)); if (tmp8 > 7) ERR_BREAK; outb (tmp8, DATA); outb (ADADR, CTL); outb (ENBIT + ADADR, CTL); outb (ADADR, CTL); break; case READADC: // Retuns ADC value with time stamp ADCtimeout = FALSE; data[0].adval[0] = timedReadADC (0) & 0xff; if (ADCtimeout) ERR_BREAK; copy_to_user (up, data, sizeof (drvdata)); break; case READBLOCK: /* Digitize a waveform from the channel kept selected before and returns the values in data[k].adval[0] and time stamps in data[k].t Arguments: 1. Number of samples 2. Delay between samples in usecs */ copy_from_user (buf, up, 2 * sizeof (u32)); if ((buf[0] > MAXPOINTS) || (buf[1] > MAXDELAY*1000) ) ERR_BREAK; if(buf[1] < ADCdelay) buf[1] = ADCdelay; ADCtimeout = FALSE; for (k = 0; k < buf[0]; ++k) { if(!k) do_gettimeofday (&begin); // mark starttime else user_delay(k * buf[1]); data[k].adval[0] = timedReadADC (k) & 0xff; if (ADCtimeout) ERR_BREAK; } if (error) break; copy_to_user (up, data, buf[0] * sizeof (drvdata)); break; case MULTIREADBLOCK: /* Digitizes waveforms on multiple channels sampled simultaneously, starting with channel zero. Arguments: 1. Number of samples 2. Number of channels to be read (1 to 4) 3. Delay between samples */ copy_from_user (buf, up, 3 * sizeof (u32)); if ((buf[0] > MAXPOINTS) || (buf[1] > 4) || (buf[2] > MAXDELAY)) ERR_BREAK; ADCtimeout = FALSE; for (k = 0; k < buf[0]; ++k) { if(!k) do_gettimeofday (&begin); // mark starttime else user_delay(k * buf[2]); for (chan = 0; chan < buf[1]; ++chan) { if (chan == 0) outb (0, DATA); // Channel 0 with fresh sampling else outb (chan + 4, DATA); // rest with the stored charge outb (ADADR, CTL); outb (ENBIT + ADADR, CTL); // Select Channel outb (ADADR, CTL); data[k].adval[chan] = timedReadADC (k); if (ADCtimeout) ERR_BREAK; // get out of the inner for loop } if (error) break; // get out of the outer for loop } if (error) break; copy_to_user (up, data, buf[0] * sizeof (drvdata)); break; case TRIGREADBLOCK: /* Digitize a block after detecting a Falling Edge on a Digital Input Pin Arguments: 1. Digital Input pin number to which trigger is applied 2. Number of points to be digitized 3. Deadtime introduced from trigger to first digitization. 4. Delay between samples afterwards. 5. trigger polarity (rising edge or falling edge) */ copy_from_user (buf, up, 5 * sizeof (u32)); if ((buf[0] > 3) || (buf[1] > MAXPOINTS) || (buf[2] > MAXDELAY) || ( buf[3] > MAXDELAY) || (buf[4] > 1) ) ERR_BREAK; if(buf[4] == 0) // Rising Edge Triggering { if (!waitForLow(buf[0])) ERR_BREAK; if (!markRising (buf[0], 0)) ERR_BREAK; } else { if (!waitForHigh (buf[0])) ERR_BREAK; if (!markFalling (buf[0], 0)) ERR_BREAK; } udelay (buf[2]); // deadtime before starting digitization ADCtimeout = FALSE; for (k = 0; k < buf[1]; ++k) // store ADC data and time stamps { if(!k) do_gettimeofday (&begin); // mark starttime else user_delay(k * buf[3]); // buf[3] is delay between samples data[k].adval[0] = timedReadADC (k); if (ADCtimeout) ERR_BREAK; } if (error) break; copy_to_user (up, data, buf[1] * sizeof (drvdata)); //buf[1] = npoints break; case PERIOD: /* Returns the times for specified number of rising edges on an input pin data[0] has the fist timestamp and data[1] has the last stamp */ copy_from_user (buf, up, 2 * sizeof (u32)); if ((buf[0] > 3) || (buf[1] > MAXPOINTS)) ERR_BREAK; if (!waitForLow (buf[0])) ERR_BREAK; if (!markRising (buf[0], 0)) // timestamp first edge to data[0] ERR_BREAK; for (k = 0; k < buf[1]; ++k) { if (!waitForLow (buf[0])) ERR_BREAK; if (!markRising (buf[0], 1)) // timestamp edges to data[1] ERR_BREAK; } if (error) break; copy_to_user (up, data, 2 * sizeof (drvdata)); break; case R2RTIME: /* Measures the time between two rising edges on digital inputs pins. Arguments: 1. Pin number to wait for raising edge 2. Pin number to wait for the falling edge values range from 0 to 3. */ copy_from_user (buf, up, 2 * sizeof (u32)); if ((buf[0] > 3) || (buf[1] > 3) ) ERR_BREAK; // Pin should be LOW first to give a rising edge if (!waitForLow (buf[0])) ERR_BREAK; if (!markRising (buf[0], 0)) ERR_BREAK; if (!waitForLow (buf[1])) ERR_BREAK; if (!markRising (buf[1], 1)) ERR_BREAK; copy_to_user (up, data, 2 * sizeof (drvdata)); // copy two timestamps break; case R2FTIME: /* Measures the time from rising edge to a falling edge on digital inputs pins. Arguments: 1. Pin number to wait for raising edge 2. Pin number to wait for the falling edge values range from 0 to 3 and they could be the same */ copy_from_user (buf, up, 2 * sizeof (u32)); if ((buf[0] > 3) || (buf[1] > 3)) ERR_BREAK; // Pin should be LOW first to give a rising edge if (!waitForLow (buf[0])) ERR_BREAK; if (!markRising (buf[0], 0)) ERR_BREAK; if (!markFalling (buf[1], 1)) ERR_BREAK; copy_to_user (up, data, 2 * sizeof (drvdata)); // copy two timestamps break; case F2RTIME: /* Measures the time from falling edge to a rising edge on digital inputs pins. Arguments: 1. Pin number to wait for falling edge 2. Pin number to wait for the rising edge values range from 0 to 3 and they could be the same */ copy_from_user (buf, up, 2 * sizeof (u32)); if ((buf[0] > 3) || (buf[1] > 3)) ERR_BREAK; // Pin should be HIGH first to give a falling edge if (!waitForHigh (buf[0])) ERR_BREAK; if (!markFalling (buf[0], 0)) ERR_BREAK; if (!markRising (buf[1], 1)) ERR_BREAK; copy_to_user (up, data, 2 * sizeof (drvdata)); // copy two timestamps break; case F2FTIME: /* Measures the time from falling edge to a falling edge on digital inputs pins. Arguments: 1. Pin number to wait for falling edge 2. Pin number to wait for the rising edge values range from 0 to 3. */ copy_from_user (buf, up, 2 * sizeof (u32)); if ((buf[0] > 3) || (buf[1] > 3) ) ERR_BREAK; // Pin should be HIGH first to give a falling edge if (!waitForHigh (buf[0])) ERR_BREAK; if (!markFalling (buf[0], 0)) ERR_BREAK; if (!waitForHigh (buf[1])) ERR_BREAK; if (!markFalling (buf[1], 1)) ERR_BREAK; copy_to_user (up, data, 2 * sizeof (drvdata)); // copy two timestamps break; case SET2RTIME: /* Returns time between setting an output to the rising edge of an input Arguments: 1. Outut Pin number. Digital outputs(0 to 7). 8 to 11 for SM outputs 0 to 3. 2. Input Pin (0 to 3) */ copy_from_user (buf, up, 2 * sizeof (u32)); if ((buf[0] > 11) || (buf[1] > 3)) ERR_BREAK; do_gettimeofday (&data[0].t); // mark start time if (buf[0] < 8) // On a digital output pin outPort (1 << buf[0], DOUT); else outPort (1 << (buf[0] - 8), SM); if (!markRising (buf[1], 1)) ERR_BREAK; copy_to_user (up, data, 2 * sizeof (drvdata)); // return 2 timestamps break; case SET2FTIME: /* Returns time between setting an output to the Falling edge of an input Arguments: 1. Outut Pin number. Digital outputs(0 to 7). 8 to 11 for SM outputs 0 to 3. 2. Input Pin (0 to 3) */ copy_from_user (buf, up, 2 * sizeof (u32)); if ((buf[0] > 11) || (buf[1] > 3)) ERR_BREAK; do_gettimeofday (&data[0].t); // mark start time if (buf[0] < 8) // On a digital output pin outPort (1 << buf[0], DOUT); else outPort (1 << (buf[0] - 8), SM); if (!markFalling (buf[1], 1)) ERR_BREAK; copy_to_user (up, data, 2 * sizeof (drvdata)); // return 2 timestamps break; case CLR2RTIME: /* Returns time between clearing an output bit to the Falling edge of an input Arguments: 1. Outut Pin number. Digital outputs(0 to 7). 8 to 11 for SM outputs 0 to 3. 2. Input Pin (0 to 3) */ copy_from_user (buf, up, 2 * sizeof (u32)); if ((buf[0] > 11) || (buf[1] > 3)) ERR_BREAK; do_gettimeofday (&data[0].t); // mark start time if (buf[0] < 8) // On a digital output pin outPort ( ~(1 << buf[0]), DOUT); else outPort ( ~( 1 << (buf[0] - 8) ), SM); if (!markRising (buf[1], 1)) ERR_BREAK; copy_to_user (up, data, 2 * sizeof (drvdata)); // return 2 timestamps break; case CLR2FTIME: /* Returns time between clearing an output bit to the Falling edge of an input Arguments: 1. Outut Pin number. Digital outputs(0 to 7). 8 to 11 for SM outputs 0 to 3. 2. Input Pin (0 to 3) */ copy_from_user (buf, up, 2 * sizeof (u32)); if ((buf[0] > 11) || (buf[1] > 3)) ERR_BREAK; do_gettimeofday (&data[0].t); // mark start time if (buf[0] < 8) // On a digital output pin outPort ( ~(1 << buf[0]), DOUT); else outPort ( ~( 1 << (buf[0] - 8) ), SM); if (!markFalling (buf[1], 1)) ERR_BREAK; copy_to_user (up, data, 2 * sizeof (drvdata)); // return 2 timestamps break; case PULSE2RTIME: /* Generate single pulse on any digital output pins or stepper motor outputs and then waits for rising edge on any of the Input lines Arguments: 1. Pin1: From 0 to 7 means digital output and 8 to 11 means SM outputs 0 to 3. 2. Pin2: From 0 to 3 3. Pulse Width in microseconds (upto 1000 usecs) 4. Deadtime after sending the pulse and looking for the response 5. Polarity of pulse */ copy_from_user (buf, up, 5 * sizeof (u32)); if ((buf[0] > 11) || (buf[1] > 3) || (buf[2] > MAXHP) || (buf[3] > MAXDELAY) || (buf[4] > 1) ) ERR_BREAK; do_gettimeofday (&data[0].t); // mark start time if(buf[4] == 0) // TTL HIGH TRUE pulseOut (buf[0], buf[2], 0, 1); else // TTL LOW TRUE invPulseOut (buf[0], buf[2], 0, 1); udelay(buf[3]); // Dead time. if (!markRising (buf[1], 1)) ERR_BREAK; copy_to_user (up, data, 2 * sizeof (drvdata)); // return 2 timestamps break; case PULSE2FTIME: /* Generate single pulse on any digital output pins or stepper motor outputs and then waits for Falling edge on any of the Input lines Arguments: 1. Pin1: From 0 to 7 means digital output and 8 to 11 means SM outputs 0 to 3. 2. Pin2: From 0 to 3 3. Pulse Width in microseconds (upto 1000 usecs) 4. Deadtime after sending the pulse and looking for the response 5. Polarity of pulse */ copy_from_user (buf, up, 5 * sizeof (u32)); if ((buf[0] > 11) || (buf[1] > 3) || (buf[2] > MAXHP) || (buf[3] > MAXDELAY) || (buf[4] > 1) ) ERR_BREAK; do_gettimeofday (&data[0].t); // mark start time if(buf[4] == 0) // TTL HIGH TRUE pulseOut (buf[0], buf[2], 0, 1); else // TTL LOW TRUE invPulseOut (buf[0], buf[2], 0, 1); udelay(buf[3]); // Dead time. if (!markFalling (buf[1], 1)) ERR_BREAK; copy_to_user (up, data, 2 * sizeof (drvdata)); // return 2 timestamps break; case PULSEOUT: /* Generates pulses on any digital output pins or stepper motor outputs. Arguments: 1. Pin number. 0 to 7 means digital output and 8 to 11 means SM outputs 0 to 3. 2. HITIME in microseconds (upto MAXHP usecs) 3. LOWTIME in microseconds (upto MAXHP usecs) 4. Number of pulses to be generated. 5. Polarity. 0 for positive TTL ( _|^|_ ) and 1 for negative TTL ( |_| ) */ copy_from_user (buf, up, 5 * sizeof (u32)); if ((buf[0] > 11) || (buf[1] > MAXHP) || (buf[2] > MAXHP) || (buf[3] > MAXPULSES) || (buf[4] > 1) ) ERR_BREAK; do_gettimeofday (&data[0].t); // mark start time if(buf[4] == 0) pulseOut (buf[0], buf[1], buf[2], buf[3]); else invPulseOut (buf[0], buf[1], buf[2], buf[3]); do_gettimeofday (&data[1].t); // mark end time copy_to_user (up, data, 2 * sizeof (drvdata)); // return 2 timestamps break; case SETMAXWAIT: /* The time measurement calls wait inside the driver and freezes the system for that period. It is essential to provide maximum limits for waiting. The default is around 1000000 microseconds ( 1 second). User program can change this to any value between 5 msecs & 5 secs. */ copy_from_user (buf, up, sizeof (u32)); if ((buf[0] < 5000) || (buf[0] > 5000000)) // 5 msec to 5 sec ERR_BREAK; maxWait = buf[0]; break; default: ERR_BREAK; } if (error) return -EIO; return 0; } //--------------------------------------------------------------------------- int init_module (void); void cleanup_module (void); static int device_open (struct inode *, struct file *); static int device_release (struct inode *, struct file *); #define DEVICE_NAME "phoenix" /* Dev name as it appears in /proc/devices */ static int Major; /* Major number assigned to our device driver */ static int Device_Open = 0; /* Is device open */ static struct file_operations fops = { .open = device_open, .ioctl = device_ioctl, .release = device_release }; int init_module (void) { Major = register_chrdev (0, DEVICE_NAME, &fops); if (Major < 0) { printk ("Registering the character device failed with %d\n", Major); return Major; } //data = (drvdata *) kmalloc(MAXPOINTS*sizeof(drvdata), GFP_KERNEL); data = (drvdata *) vmalloc(MAXPOINTS*sizeof(drvdata)); if(!data) { printk ("Phoenix: memory allocation failed.\n"); unregister_chrdev (Major, DEVICE_NAME); return -1; } #ifdef USE_IRQ { int status; outb(0, CTL); // Clear Interrupt BIT of parallel port status = request_irq (irq, (void *) ppisr, SA_INTERRUPT, DEVICE_NAME, NULL); if (status < 0) { printk ("IRQ number allocation failed. %d\n",status); unregister_chrdev (Major, DEVICE_NAME); return status; } } #endif return 0; } void cleanup_module (void) { int ret; vfree(data); #ifdef USE_IRQ free_irq (irq, NULL); #endif ret = unregister_chrdev (Major, DEVICE_NAME); if (ret < 0) printk ("Error in unregister_phoenix: %d\n", ret); } static int device_open (struct inode *inode, struct file *file) { if (Device_Open) // We may remove this.. { printk ("Device Busy....\n"); return -EBUSY; } Device_Open++; #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0) try_module_get (THIS_MODULE); #endif return 0; } static int device_release (struct inode *inode, struct file *file) { Device_Open--; /* We're now ready for our next caller */ #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0) module_put (THIS_MODULE); #endif return 0; } //------------------------------------------------------------------------- u8 timedReadADC (int k) /* returns 0 to 255. */ { u8 lo, hi, dat; volatile int timer = 50; outb (ADSTRT, CTL); outb (ENBIT + ADSTRT, CTL); // sample mode do_gettimeofday (&data[k].t); // mark time udelay (1); outb (ADSTRT, CTL); // Start Conversion udelay (100); // better not disturb the PCB for 100 microseconds while (timer) { if (~inb (STAT) & ADEOC) break; // EOC bit inverted by Pport --timer; } if (!timer) { ADCtimeout = TRUE; // set error flag return 0; } outb (ADCLO, CTL); outb (ENBIT + ADCLO, CTL); lo = (inb (STAT) >> 3) & 15; outb (ADCLO, CTL); outb (ADCHI, CTL); outb (ENBIT + ADCHI, CTL); hi = inb (STAT) >> 3; outb (ADCHI, CTL); dat = (hi << 4) | lo; return dat; } void rotateMotor (int nsteps, int dir) { static u8 pos = 0; static u8 seq[4] = { 12, 6, 3, 9 }; int i; for (i = 0; i < nsteps; ++i) { if (dir) if (pos == 3) pos = 0; else ++pos; else if (pos == 0) pos = 3; else --pos; outb (seq[pos], DATA); outb (SM, CTL); outb (SM + ENBIT, CTL); outb (SM, CTL); outb (0, CTL); mdelay (motorDelay); } } u8 waitForHigh (int b) { u8 mask = 1 << (b + 3); // Printer Status port data is from S3 to S7 volatile int timer = 0; outb (DIN, CTL); outb (ENBIT + DIN, CTL); while (timer++ < maxWait) if (inb (STAT) & mask) break; outb (DIN, CTL); //printk("WFH: timer %d\n", timer); if (timer >= maxWait) return FALSE; return TRUE; } u8 waitForLow (int b) { u8 mask = 1 << (b + 3); // Printer Status port data is from S3 to S7 volatile int timer = 0; outb (DIN, CTL); outb (ENBIT + DIN, CTL); while (timer++ < maxWait) if (~inb (STAT) & mask) break; outb (DIN, CTL); //printk("WFL: timer %d\n", timer); if (timer >= maxWait) return FALSE; return TRUE; } u8 markRising (int b, int index) { u8 mask = 1 << (b + 3); // Printer Status port data is from S3 to S7 volatile int timer = 0; outb (DIN, CTL); outb (ENBIT + DIN, CTL); while (timer++ < maxWait) if (inb (STAT) & mask) break; do_gettimeofday (&data[index].t); // Store the time stamp outb (DIN, CTL); // printk("MR: timer %d\n", timer); if (timer >= maxWait) return FALSE; return TRUE; } u8 markFalling (int b, int index) { u8 mask = 1 << (b + 3); // Printer Status port data is from S3 to S7 volatile int timer = 0; outb (DIN, CTL); outb (ENBIT + DIN, CTL); while (timer++ < maxWait) if (~inb (STAT) & mask) break; do_gettimeofday (&data[index].t); // Store the time stamp outb (DIN, CTL); //printk("MF: timer %d\n", timer); if (timer >= maxWait) return FALSE; return TRUE; } void pulseOut (int pin, int th, int tl, int np) { int k; u8 port; if (pin < 8) port = DOUT; else port = SM; for (k = 0; k < np; ++k) { outb (1 << pin, DATA); outb (ENBIT + port, CTL); outb (port, CTL); if (th > 3) // 3 usecs goes for the 'outb' instruction udelay (th - 3); outb (0, DATA); outb (ENBIT + port, CTL); outb (port, CTL); if (tl > 3) udelay (tl - 3); } } void invPulseOut (int pin, int th, int tl, int np) { int k; u8 port; if (pin < 8) port = DOUT; else port = SM; for (k = 0; k < np; ++k) { outb (0, DATA); outb (ENBIT + port, CTL); outb (port, CTL); if (th > 3) // 3 usecs goes for the 'outb' instruction udelay (th - 3); outb (1 << pin, DATA); outb (ENBIT + port, CTL); outb (port, CTL); if (tl > 3) udelay (tl - 3); } } void outPort (u8 data, u8 port) { outb (data, DATA); outb (port, CTL); outb (ENBIT + port, CTL); outb (port, CTL); } void user_delay(u32 del) { struct timeval now; u32 ds, dus; while(1) { do_gettimeofday (&now); if(now.tv_usec < begin.tv_usec) { now.tv_usec += 1000000; now.tv_sec -= 1; } dus = now.tv_usec - begin.tv_usec; ds = now.tv_sec - begin.tv_sec; if( (ds * 1000000 + dus) > del) break; } } void readadc(void) { unsigned char n2, n3, n4; unsigned short data; outb(1, DATA); n2 = inb(STAT); n2 = (n2 >> 3) & 0x17; n2 |= (n2 & 16) >> 1; n2 = (n2 & 15); outb(2, DATA); n3 = inb(STAT); n3 = (n3 >> 3) & 0x17; n3 |= (n3 & 16) >> 1; n3 = (n3 & 15); outb(3, DATA); n4 = inb(STAT); n4 = (n4 >> 3) & 0x17; n4 |= (n4 & 16) >> 1; n4 = (n4 & 15); data = (n4 << 8) | (n3 << 4) | n2; ++hist[data]; //printk("%x %x %x\n",data, hist[191], hist[192]); }