--- /dev/null
+#include <comedi.h>
+#include <math.h>
+#include <stdio.h>
+#include "../comedi/drivers/8253.h"
+
+#if NOT
+/* this function is meant to replace i8253_cascade_ns_to_timer_2div
+ * (which is completely broken at the moment)
+ */
+static inline void cascade_new(int i8253_osc_base, unsigned int *d1, unsigned int *d2, unsigned int *nanosec, int round_mode)
+{
+ unsigned int divider;
+ unsigned int div1, div2;
+ unsigned int div1_glb, div2_glb, ns_glb;
+ unsigned int div1_lub, div2_lub, ns_lub;
+ unsigned int ns;
+ unsigned int start;
+ unsigned int ns_low, ns_high;
+
+ divider = *nanosec / i8253_osc_base;
+
+ div1_lub = div2_lub = 0;
+ div1_glb = div2_glb = 0;
+
+ ns_glb = 0;
+ ns_lub = 0xffffffff;
+
+ div2 = 0x10000;
+ start = divider / div2;
+ if(start < 2) start = 2;
+ for (div1 = start; div1 <= divider / div1 + 1; div1++) {
+ for(div2 = divider / div1; div1 * div2 <= divider + div1 + 1; div2++) {
+ ns = i8253_osc_base * div1 * div2;
+ if (ns <= *nanosec && ns > ns_glb) {
+ ns_glb = ns;
+ div1_glb = div1;
+ div2_glb = div2;
+ }
+ if (div2 <= 0x10000) {
+ ns = i8253_osc_base * div1 * div2;
+ if (ns >= *nanosec && ns < ns_lub) {
+ ns_lub = ns;
+ div1_lub = div1;
+ div2_lub = div2;
+ }
+ }
+ }
+ }
+
+ switch (round_mode) {
+ case TRIG_ROUND_NEAREST:
+ default:
+ ns_high = div1_lub * div2_lub * i8253_osc_base;
+ ns_low = div1_glb * div2_glb * i8253_osc_base;
+ if( ns_high - *nanosec < *nanosec - ns_low) {
+ div1 = div1_lub;
+ div2 = div2_lub;
+ } else {
+ div1 = div1_glb;
+ div2 = div2_glb;
+ }
+ break;
+ case TRIG_ROUND_UP:
+ div1 = div1_lub;
+ div2 = div2_lub;
+ break;
+ case TRIG_ROUND_DOWN:
+ div1 = div1_glb;
+ div2 = div2_glb;
+ break;
+ }
+
+ *nanosec = div1 * div2 * i8253_osc_base;
+ *d1 = div1;
+ *d2 = div2;
+ return;
+}
+#endif
+
+#ifdef ROGI
+int main(int argc,vhar *argv[])
+{
+ int osc_base = 1000;
+ int round_mode = TRIG_ROUND_NEAREST;
+ int i;
+ int div1_old, div2_old, ns_old, err_old;
+ int div1_new, div2_new, ns_new, err_new;
+ int div1_pow, div2_pow, ns_pow, err_pow;
+ int err = 0;
+
+ /* loop over desired nanosecond timings to test */
+ for(i = 10000; i < 100000; i++)
+ {
+ ns_old = ns_new = ns_pow = i;
+
+ i8253_cascade_ns_to_timer_2div(osc_base, &div1_old, &div2_old, &ns_old, round_mode);
+ err_old = ns_old - i;
+
+ i8253_cascade_ns_to_timer_power(osc_base, &div1_pow, &div2_pow, &ns_pow, round_mode);
+ err_pow = ns_pow - i;
+
+ cascade_new(osc_base, &div1_new, &div2_new, &ns_new, round_mode);
+ err_new = ns_new - i;
+
+ /* print results on this condition */
+ if(abs(err_new) > abs(err_pow))
+ printf("nanosec %i\terr_new %i\tdiv1_new %i\tdiv2_new %i\n"
+ "\t\terr_pow %i\tdiv1_pow %i\tdiv2_pow %i\n",
+ i, err_new, div1_new, div2_new, err_pow, div1_pow, div2_pow);
+
+ /* consistency checks */
+ if(div1_old * div2_old * osc_base != ns_old) err = 1;
+ if(div1_pow * div2_pow * osc_base != ns_pow) err = 2;
+ if(div1_new * div2_new * osc_base != ns_new) err = 3;
+ if(err)
+ {
+ printf("err %i\n", err);
+ err=0;
+ }
+ }
+ return 0;
+}
+#endif
+
+int main(int argc,char *argv[])
+{
+ int osc_base = 1000;
+ unsigned int ns;
+ unsigned int div1,div2;
+ int i=0;
+
+ for(ns = 4*osc_base; ns<0x80000000;ns++){
+ i8253_cascade_ns_to_timer_2div
+ (osc_base, &div1, &div2, &ns, TRIG_ROUND_UP);
+
+ i++;
+ if(!(i&0xff))
+ printf("ns=%d div1=%d div2=%d\n",ns,div1,div2);
+ if(i==100000)exit(0);
+ }
+
+ return 0;
+}
+
--- /dev/null
+diff -ur comedi-0.7.53/comedi/comedi_fops.c comedi-0.7.53-patched/comedi/comedi_fops.c
+--- comedi-0.7.53/comedi/comedi_fops.c Fri Dec 1 13:06:29 2000
++++ comedi-0.7.53-patched/comedi/comedi_fops.c Sun Jan 7 21:30:20 2001
+@@ -60,9 +60,12 @@
+ static int do_cancel_ioctl(comedi_device *dev,unsigned int arg,void *file);
+ static int do_cmdtest_ioctl(comedi_device *dev,void *arg,void *file);
+ static int do_insnlist_ioctl(comedi_device *dev,void *arg,void *file);
++static int do_bufconfig_ioctl(comedi_device *dev,void *arg);
+
+ static void do_become_nonbusy(comedi_device *dev,comedi_subdevice *s);
+
++void* resize_buf(comedi_device *dev,comedi_subdevice *s);
++
+ static int comedi_ioctl(struct inode * inode,struct file * file,unsigned int cmd,unsigned long arg)
+ {
+ kdev_t minor=MINOR(inode->i_rdev);
+@@ -96,11 +99,43 @@
+ return do_cmdtest_ioctl(dev,(void *)arg,file);
+ case COMEDI_INSNLIST:
+ return do_insnlist_ioctl(dev,(void *)arg,file);
++ case COMEDI_BUFCONFIG:
++ return do_bufconfig_ioctl(dev,(void*)arg);
+ default:
+ return -EIO;
+ }
+ }
+
++/*
++ COMEDI_BUFCONFIG
++ buffer configuration ioctl
++
++ arg:
++ pointer to bufconfig structure
++
++ reads:
++ bufconfig at arg
++
++ writes:
++ modified bufconfig at arg
++*/
++static int do_bufconfig_ioctl(comedi_device *dev,void *arg)
++{
++ comedi_bufconfig bc;
++
++ if(!suser())
++ return -EPERM;
++
++ if(copy_from_user(&bc,arg,sizeof(comedi_bufconfig)))
++ return -EFAULT;
++
++ dev->subdev_bufsz = bc.size;
++
++ if(copy_to_user(arg,&bc,sizeof(comedi_bufconfig)))
++ return -EFAULT;
++
++ return 0;
++}
+
+ /*
+ COMEDI_DEVCONFIG
+@@ -529,6 +564,8 @@
+ goto cleanup;
+ }
+
++ /* make sure preallocated buffer is the correct size */
++ resize_buf(dev, s);
+ if(!s->prealloc_buf){
+ printk("comedi: bug: s->prealloc_buf=NULL\n");
+ }
+@@ -818,12 +855,15 @@
+ goto cleanup;
+ }
+
+- if(!s->prealloc_bufsz){
++ /* make sure preallocated buffer is the correct size */
++ resize_buf(dev, s);
++ if(!s->prealloc_buf){
+ ret=-ENOMEM;
+ DPRINTK("no buffer (?)\n");
+ goto cleanup;
+ }
+ s->cmd.data_len=s->prealloc_bufsz;
++ s->cmd.data=s->prealloc_buf;
+
+ #ifdef CONFIG_COMEDI_MODE_CORE
+ s->cur_trig.data=s->prealloc_buf; /* XXX */
+@@ -1102,6 +1143,28 @@
+ do_become_nonbusy(dev,s);
+
+ return ret;
++}
++
++/* utility function that resizes the prealloc_buf for
++ * a subdevice according to the value of dev->subdev_bufsz
++ * Frank Mori Hess 2001-01-07
++ */
++void* resize_buf(comedi_device *dev, comedi_subdevice *s)
++{
++ unsigned int size;
++
++ // make sure buffer is an integral number of pages (we round up)
++ size = ((dev->subdev_bufsz + PAGE_SIZE - 1) / PAGE_SIZE) * PAGE_SIZE;
++
++ if(s->prealloc_buf && s->prealloc_bufsz)
++ {
++ if(s->prealloc_bufsz == size)
++ return s->prealloc_buf;
++ else
++ rvfree(s->prealloc_buf, s->prealloc_bufsz);
++ }
++ s->prealloc_bufsz = size;
++ return s->prealloc_buf = rvmalloc(s->prealloc_bufsz);
+ }
+
+ #ifdef LINUX_V22
+Only in comedi-0.7.53-patched/comedi: comedi_fops.c.orig
+diff -ur comedi-0.7.53/comedi/drivers.c comedi-0.7.53-patched/comedi/drivers.c
+--- comedi-0.7.53/comedi/drivers.c Tue Nov 7 15:48:16 2000
++++ comedi-0.7.53-patched/comedi/drivers.c Sun Jan 7 21:30:20 2001
+@@ -201,6 +201,10 @@
+ int have_trig;
+ comedi_subdevice *s;
+
++ // set the default buffer size if appropriate
++ if(dev->subdev_bufsz == 0)
++ dev->subdev_bufsz = PAGE_SIZE * 32;
++
+ for(i=0;i<dev->n_subdevices;i++){
+ s=dev->subdevices+i;
+
+@@ -221,7 +225,7 @@
+ s->trig[4]=command_trig;
+ }
+ if(s->do_cmd || have_trig){
+- s->prealloc_bufsz=1024*128;
++ s->prealloc_bufsz=dev->subdev_bufsz;
+ }else{
+ s->prealloc_bufsz=0;
+ }
+diff -ur comedi-0.7.53/include/linux/comedi.h comedi-0.7.53-patched/include/linux/comedi.h
+--- comedi-0.7.53/include/linux/comedi.h Tue Nov 7 14:47:37 2000
++++ comedi-0.7.53-patched/include/linux/comedi.h Sun Jan 7 21:30:20 2001
+@@ -170,6 +170,7 @@
+ #define COMEDI_CMDTEST _IOR(CIO,10,comedi_cmd)
+ #define COMEDI_INSNLIST _IOR(CIO,11,comedi_insnlist)
+ #define COMEDI_INSN _IOR(CIO,12,comedi_insn)
++#define COMEDI_BUFCONFIG _IOR(CIO,13,comedi_bufconfig)
+
+
+
+@@ -185,6 +186,7 @@
+ typedef struct comedi_devconfig_struct comedi_devconfig;
+ typedef struct comedi_rangeinfo_struct comedi_rangeinfo;
+ typedef struct comedi_krange_struct comedi_krange;
++typedef struct comedi_bufconfig_struct comedi_bufconfig;
+
+ struct comedi_trig_struct{
+ unsigned int subdev; /* subdevice */
+@@ -283,6 +285,11 @@
+ struct comedi_devconfig_struct{
+ char board_name[COMEDI_NAMELEN];
+ int options[COMEDI_NDEVCONFOPTS];
++};
++
++struct comedi_bufconfig_struct{
++ unsigned int size; /* buffer size in bytes */
++ int unused[5];
+ };
+
+
+diff -ur comedi-0.7.53/include/linux/comedidev.h comedi-0.7.53-patched/include/linux/comedidev.h
+--- comedi-0.7.53/include/linux/comedidev.h Fri Nov 24 18:10:34 2000
++++ comedi-0.7.53-patched/include/linux/comedidev.h Sun Jan 7 21:30:20 2001
+@@ -148,6 +148,8 @@
+ int n_subdevices;
+ comedi_subdevice *subdevices;
+ int options[COMEDI_NDEVCONFOPTS];
++ // size in bytes subdevices should use for prealloc_buf
++ unsigned int subdev_bufsz;
+
+ /* dumb */
+ int iobase;
--- /dev/null
+? patch
+? comedi/drivers/patch
+? comedi/drivers/pcmad.c
+? comedi/drivers/diff_ni-E.c
+? include/comedi
+? include/modbuild
+Index: comedi/Config.in
+===================================================================
+RCS file: /d/ds/cvsroot/comedi/comedi/Config.in,v
+retrieving revision 1.20
+diff -u -r1.20 Config.in
+--- comedi/Config.in 2000/08/09 20:44:20 1.20
++++ comedi/Config.in 2000/09/03 01:54:35
+@@ -87,6 +87,7 @@
+ if [ "$CONFIG_PCI" = "y" ];then
+ dep_tristate 'IOtech DaqBoard/2000' CONFIG_COMEDI_DAQBOARD2000 $CONFIG_COMEDI
+ fi
++dep_tristate 'Winsystems PCM-A/D' CONFIG_COMEDI_PCMAD $CONFIG_COMEDI
+ dep_tristate 'Skeleton driver' CONFIG_COMEDI_SKEL $CONFIG_COMEDI
+
+ if [ "$CONFIG_COMEDI_RT" = "y" ];then
+Index: comedi/drivers/Makefile
+===================================================================
+RCS file: /d/ds/cvsroot/comedi/comedi/drivers/Makefile,v
+retrieving revision 1.14
+diff -u -r1.14 Makefile
+--- comedi/drivers/Makefile 2000/07/24 19:43:29 1.14
++++ comedi/drivers/Makefile 2000/09/03 01:54:36
+@@ -39,6 +39,8 @@
+
+ obj-$(CONFIG_COMEDI_II_PCI20KC) += ii_pci20kc.o
+
++obj-$(CONFIG_COMEDI_PCMAD) += pcmad.o
++
+ obj-$(CONFIG_COMEDI_MULTIQ3) += multiq3.o
+
+ obj-$(CONFIG_COMEDI_NI_ATMIO) += ni_atmio.o
--- /dev/null
+Index: comedi/drivers/ni_mio_common.c
+===================================================================
+RCS file: /var/cvs/comedi/comedi/drivers/ni_mio_common.c,v
+retrieving revision 1.43
+diff -u -r1.43 ni_mio_common.c
+--- comedi/drivers/ni_mio_common.c 2001/02/05 02:58:40 1.43
++++ comedi/drivers/ni_mio_common.c 2001/02/05 02:59:48
+@@ -161,6 +161,11 @@
+ static int ni_dio(comedi_device *dev,comedi_subdevice *s,comedi_trig *it);
+ #endif
+
++static int ni_serial_insn_config(comedi_device *dev,comedi_subdevice *s,
++ comedi_insn *insn,lsampl_t *data);
++static int ni_serial_insn_bits(comedi_device *dev,comedi_subdevice *s,
++ comedi_insn *insn,lsampl_t *data);
++
+ #ifdef USE_TRIG
+ static int ni_eeprom(comedi_device *dev,comedi_subdevice *s,comedi_trig *it);
+ #endif
+@@ -203,6 +208,11 @@
+ #define AIMODE_SCAN 2
+ #define AIMODE_SAMPLE 3
+
++/* supported serial clock intervals */
++#define SERIAL_DISABLED 0
++#define SERIAL_600NS 600
++#define SERIAL_1_2US 1200
++#define SERIAL_10US 10000
+
+ static void ni_E_interrupt(int irq,void *d,struct pt_regs * regs)
+ {
+@@ -1991,6 +2001,203 @@
+ return 2;
+ }
+
++static int ni_serial_insn_config(comedi_device *dev,comedi_subdevice *s,
++ comedi_insn *insn,lsampl_t *data)
++{
++#ifdef DEBUG_DIO
++ printk("SPI serial I/O Config %d\n", data[0]);
++#endif
++
++ if(insn->n!=1)return -EINVAL;
++ devpriv->serial_interval_ns = data[0];
++ devpriv->serial_hw_mode = 1;
++ devpriv->dio_control |= DIO_HW_Serial_Enable;
++ switch(data[0]) {
++ default:
++ case SERIAL_DISABLED:
++ /* Disable (or use software serial) */
++ devpriv->serial_hw_mode = 0;
++ devpriv->dio_control &= ~(DIO_HW_Serial_Enable |
++ DIO_Software_Serial_Control);
++ break;
++ case SERIAL_600NS:
++ /* Warning: this clock speed is too fast to reliably
++ control SCXI. */
++ devpriv->dio_control &= ~DIO_HW_Serial_Timebase;
++ devpriv->clock_and_fout |= Slow_Internal_Timebase;
++ devpriv->clock_and_fout &= ~DIO_Serial_Out_Divide_By_2;
++ break;
++ case SERIAL_1_2US:
++ devpriv->dio_control &= ~DIO_HW_Serial_Timebase;
++ devpriv->clock_and_fout |= Slow_Internal_Timebase |
++ DIO_Serial_Out_Divide_By_2;
++ break;
++ case SERIAL_10US:
++ devpriv->dio_control |= DIO_HW_Serial_Timebase;
++ devpriv->clock_and_fout |= Slow_Internal_Timebase |
++ DIO_Serial_Out_Divide_By_2;
++ /* Note: DIO_Serial_Out_Divide_By_2 only affects
++ 600ns/1.2us. If you turn divide_by_2 off with the
++ slow clock, you will still get 10us, except then
++ all your delays are wrong. */
++ break;
++ }
++ win_out(devpriv->dio_control,DIO_Control_Register);
++ win_out(devpriv->clock_and_fout,Clock_and_FOUT_Register);
++ return 1;
++}
++
++static int ni_serial_hw_readwrite8(comedi_device *dev,comedi_subdevice *s,
++ unsigned char data_out,
++ unsigned char *data_in)
++{
++ unsigned int status1;
++ int err = 0, count = 20;
++
++#ifdef DEBUG_DIO
++ printk("ni_serial_hw_readwrite8: outputting 0x%x\n", data_out);
++#endif
++
++ if(devpriv->serial_interval_ns == 0) {
++ err = -EINVAL;
++ goto Error;
++ }
++
++ devpriv->dio_output &= ~DIO_Serial_Data_Mask;
++ devpriv->dio_output |= DIO_Serial_Data_Out(data_out);
++ win_out(devpriv->dio_output,DIO_Output_Register);
++
++ status1 = win_in(Joint_Status_1_Register);
++ if(status1 & DIO_Serial_IO_In_Progress_St) {
++ err = -EBUSY;
++ goto Error;
++ }
++
++ devpriv->dio_control |= DIO_HW_Serial_Start;
++ win_out(devpriv->dio_control,DIO_Control_Register);
++ devpriv->dio_control &= ~DIO_HW_Serial_Start;
++
++ /* Wait until STC says we're done, but don't loop infinitely. Also,
++ we don't have to keep updating the window address for this. */
++ ni_writew(Joint_Status_1_Register,Window_Address);
++ while((status1 = ni_readw(Window_Data)) & DIO_Serial_IO_In_Progress_St) {
++ /* Delay one bit per loop */
++ nanodelay(devpriv->serial_interval_ns);
++ if(--count < 0) {
++ printk("ni_serial_hw_readwrite8: SPI serial I/O didn't finish in time!\n");
++ err = -ETIME;
++ goto Error;
++ }
++ }
++
++ /* Delay for last bit. This delay is absolutely necessary, because
++ DIO_Serial_IO_In_Progress_St goes high one bit too early. */
++ nanodelay(devpriv->serial_interval_ns);
++
++ if(data_in != NULL) {
++ *data_in = win_in(DIO_Serial_Input_Register);
++#ifdef DEBUG_DIO
++ printk("ni_serial_hw_readwrite8: inputted 0x%x\n", *data_in);
++#endif
++ }
++
++ Error:
++ win_out(devpriv->dio_control,DIO_Control_Register);
++
++ return err;
++}
++
++static int ni_serial_sw_readwrite8(comedi_device *dev,comedi_subdevice *s,
++ unsigned char data_out,
++ unsigned char *data_in)
++{
++ unsigned char mask, input = 0;
++
++#ifdef DEBUG_DIO
++ printk("ni_serial_sw_readwrite8: outputting 0x%x\n", data_out);
++#endif
++
++ /* Wait for one bit before transfer */
++ nanodelay(devpriv->serial_interval_ns);
++
++ for(mask = 0x80; mask; mask >>= 1) {
++ /* Output current bit; note that we cannot touch s->state
++ because it is a per-subdevice field, and serial is
++ a separate subdevice from DIO. */
++ devpriv->dio_output &= ~DIO_SDOUT;
++ if(data_out & mask) {
++ devpriv->dio_output |= DIO_SDOUT;
++ }
++ win_out(devpriv->dio_output,DIO_Output_Register);
++
++ /* Assert SDCLK (active low, inverted), wait for half of
++ the delay, deassert SDCLK, and wait for the other half. */
++ devpriv->dio_control |= DIO_Software_Serial_Control;
++ win_out(devpriv->dio_control,DIO_Control_Register);
++
++ nanodelay(devpriv->serial_interval_ns / 2);
++
++ devpriv->dio_control &= ~DIO_Software_Serial_Control;
++ win_out(devpriv->dio_control,DIO_Control_Register);
++
++ nanodelay(devpriv->serial_interval_ns / 2);
++
++ /* Input current bit */
++ if(ni_readw(DIO_Parallel_Input) & DIO_SDIN) {
++ input |= mask;
++ }
++ }
++#ifdef DEBUG_DIO
++ printk("ni_serial_sw_readwrite8: inputted 0x%x\n", input);
++#endif
++ if(data_in) *data_in = input;
++
++ return 0;
++}
++
++static int ni_serial_insn_bits(comedi_device *dev,comedi_subdevice *s,
++ comedi_insn *insn,lsampl_t *data)
++{
++ int err = insn->n;
++ lsampl_t data_out, data_in, num_bits;
++ unsigned char byteOut, byteIn;
++
++#ifdef DEBUG_DIO
++ printk("ni_serial_insn_bits: num_bits=0x%x data_out=0x%x\n", data[0],
++ data[1]);
++#endif
++
++ if(insn->n!=2) return -EINVAL;
++
++ num_bits = data[0];
++
++ if((num_bits % 8) != 0) return -EINVAL;
++
++ data_out = data[1];
++ data_in = 0;
++ while(num_bits > 0) {
++ /* Read from MSB to LSB */
++ data_in <<= 8;
++
++ byteOut = (data_out >> (num_bits - 8)) & 0xff;
++ if(devpriv->serial_hw_mode) {
++ err = ni_serial_hw_readwrite8(dev,s,byteOut,&byteIn);
++ } else if(devpriv->serial_interval_ns > 0) {
++ err = ni_serial_sw_readwrite8(dev,s,byteOut,&byteIn);
++ } else {
++ printk("ni_serial_insn_bits: serial disabled!\n");
++ return -EINVAL;
++ }
++ if(err < 0) return err;
++ data_in |= byteIn;
++
++ /* Write from MSB to LSB */
++ num_bits -= 8;
++ }
++ data[1] = data_in;
++ return insn->n;
++}
++
+ /*
+ HACK!
+
+@@ -2031,7 +2238,7 @@
+ {
+ comedi_subdevice *s;
+
+- dev->n_subdevices=7;
++ dev->n_subdevices=8;
+
+ if(alloc_subdevices(dev)<0)
+ return -ENOMEM;
+@@ -2144,11 +2351,24 @@
+ #ifdef USE_TRIG
+ s->trig[0]=ni_eeprom;
+ #endif
+-
++
++ /* SPI serial I/O */
++ s=dev->subdevices+7;
++ s->type=COMEDI_SUBD_SERIAL;
++ s->subdev_flags=SDF_READABLE|SDF_WRITEABLE|SDF_INTERNAL;
++ s->n_chan=1;
++ s->maxdata=0xff;
++ s->insn_bits=ni_serial_insn_bits;
++ s->insn_config=ni_serial_insn_config;
++
++ /* serial configuration */
++ devpriv->serial_interval_ns = 0;
++ devpriv->serial_hw_mode = 0;
++
+ /* ai configuration */
+ ni_ai_reset(dev,dev->subdevices+0);
+- win_out(0x1ba0,Clock_and_FOUT_Register);
+-
++ devpriv->clock_and_fout = 0x1ba0;
++ win_out(devpriv->clock_and_fout,Clock_and_FOUT_Register);
+
+ /* analog output configuration */
+
+Index: include/linux/comedi.h
+===================================================================
+RCS file: /var/cvs/comedi/include/linux/comedi.h,v
+retrieving revision 1.5
+diff -u -r1.5 comedi.h
+--- include/linux/comedi.h 2001/01/30 20:19:39 1.5
++++ include/linux/comedi.h 2001/02/05 02:59:50
+@@ -148,7 +148,7 @@
+ #define COMEDI_SUBD_MEMORY 8 /* memory, EEPROM, DPRAM */
+ #define COMEDI_SUBD_CALIB 9 /* calibration DACs */
+ #define COMEDI_SUBD_PROC 10 /* processor, DSP */
+-
++#define COMEDI_SUBD_SERIAL 11 /* SPI serial I/O */
+
+ #define COMEDI_INPUT 0
+ #define COMEDI_OUTPUT 1
+Index: include/linux/comedidev.h
+===================================================================
+RCS file: /var/cvs/comedi/include/linux/comedidev.h,v
+retrieving revision 1.8
+diff -u -r1.8 comedidev.h
+--- include/linux/comedidev.h 2001/02/05 02:09:19 1.8
++++ include/linux/comedidev.h 2001/02/05 02:59:51
+@@ -276,6 +276,18 @@
+ return 0;
+ }
+
++static inline int nanodelay(unsigned long ns)
++{
++ /* We round up, so the result should always be longer than the
++ * specified time. It's probably not much more precise than
++ * using udelay(). Hopefully, one day Linux will have an
++ * nanodelay() function. */
++ unsigned long loops_per_us = (loops_per_sec + 999999) / 1000000;
++ unsigned long loops = ((ns * loops_per_us) + 999) / 1000;
++ /* printk("nanodelay: ns=%ld loops=%ld\n", ns, loops); */
++ __delay(loops);
++}
++
+
+ #ifdef LINUX_V20
+ extern struct symbol_table comedi_syms;
--- /dev/null
+/*
+ module/pcmad
+ hardware driver for Winsystems PCM-A/D12 and PCM-A/D16
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 2000 David A. Schleef <ds@stm.lbl.gov>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/timex.h>
+#include <linux/timer.h>
+#include <asm/io.h>
+#include <comedi_module.h>
+
+
+#define PCMAD_SIZE 4
+
+#define PCMAD_STATUS 0
+#define PCMAD_LSB 1
+#define PCMAD_MSB 2
+#define PCMAD_CONVERT 1
+
+static int pcmad_attach(comedi_device *dev,comedi_devconfig *it);
+static int pcmad_detach(comedi_device *dev);
+static int pcmad_recognize(char *name);
+comedi_driver driver_pcmad={
+ driver_name: "pcmad",
+ module: THIS_MODULE,
+ attach: pcmad_attach,
+ detach: pcmad_detach,
+ recognize: pcmad_recognize,
+};
+
+struct pcmad_board_struct{
+ char *name;
+ int n_ai_bits;
+};
+struct pcmad_board_struct pcmad_boards[]={
+ {
+ name: "pcmad12",
+ n_ai_bits: 12,
+ },
+ {
+ name: "pcmad16",
+ n_ai_bits: 16,
+ },
+};
+#define this_board ((struct pcmad_board_struct *)(dev->board_ptr))
+static int n_pcmad_boards=(sizeof(pcmad_boards)/sizeof(pcmad_boards[0]));
+
+struct pcmad_priv_struct{
+ int differential;
+ int twos_comp;
+};
+#define devpriv ((struct pcmad_priv_struct *)dev->private)
+
+
+#define TIMEOUT 100
+
+static int pcmad_ai_mode0(comedi_device *dev,comedi_subdevice *s,comedi_trig *it)
+{
+ int i,msb,lsb;
+ int chan;
+ int data;
+
+ chan=CR_CHAN(it->chanlist[0]);
+
+ outb(chan,dev->iobase+PCMAD_CONVERT);
+
+ for(i=0;i<TIMEOUT;i++){
+ if((inb(dev->iobase+PCMAD_STATUS)&0x3) == 0x3)
+ break;
+ }
+ lsb=inb(dev->iobase+PCMAD_LSB);
+ msb=inb(dev->iobase+PCMAD_MSB);
+
+ data=(msb<<8)|(lsb);
+
+ if(devpriv->twos_comp){
+ data ^= (1<<(this_board->n_ai_bits-1));
+ }
+ it->data[0]=data;
+
+ return 1;
+}
+
+static int pcmad_recognize(char *name)
+{
+ int i;
+
+ for(i=0;i<n_pcmad_boards;i++){
+ if(!strcmp(pcmad_boards[i].name,name))
+ return i;
+ }
+
+ return -1;
+}
+
+/*
+ * options:
+ * 0 i/o base
+ * 1 unused
+ * 2 0=single ended 1=differential
+ * 3 0=straight binary 1=two's comp
+ */
+static int pcmad_attach(comedi_device *dev,comedi_devconfig *it)
+{
+ int ret;
+ comedi_subdevice *s;
+
+ dev->iobase=it->options[0];
+ printk("comedi%d: pcmad: 0x%04x ",dev->minor,dev->iobase);
+ if(check_region(dev->iobase,PCMAD_SIZE)<0){
+ printk("I/O port conflict\n");
+ return -EIO;
+ }
+ request_region(dev->iobase,PCMAD_SIZE,"pcmad");
+ dev->iobase=dev->iobase;
+ dev->iosize=PCMAD_SIZE;
+
+ dev->n_subdevices=1;
+ if((ret=alloc_subdevices(dev))<0)
+ return ret;
+ if((ret=alloc_private(dev,sizeof(struct pcmad_priv_struct)))<0)
+ return ret;
+
+ dev->board_ptr = pcmad_boards+dev->board;
+
+ s=dev->subdevices+0;
+ s->type=COMEDI_SUBD_AI;
+ s->subdev_flags=SDF_READABLE;
+ s->n_chan=16; /* XXX */
+ s->len_chanlist=1;
+ s->trig[0]=pcmad_ai_mode0;
+ s->maxdata=(1<<this_board->n_ai_bits)-1;
+ s->range_table=&range_unknown;
+
+ return 0;
+}
+
+
+static int pcmad_detach(comedi_device *dev)
+{
+ printk("comedi%d: pcmad: remove\n",dev->minor);
+
+ if(dev->irq){
+ free_irq(dev->irq,dev);
+ }
+ release_region(dev->iobase,dev->iosize);
+
+ return 0;
+}
+
+
+COMEDI_INITCLEANUP(driver_pcmad);
+