/* * daq.c - test driver for "daq" device * * Created by chopping down the "short.c" device driver from the book * "Linux Device Drivers" by Alessandro Rubini and Jonathan Corbet, * published by O'Reilly & Associates, and then adding in code specific * to the DIY Electronics Kit 93 PC Data Acquisition UNIT (DAQ) device. * * --Ted Baker, 24 July 2003 * * $Id: daq.c,v 1.4 2003/07/29 18:31:03 developer Exp developer $ */ #ifndef __KERNEL__ # define __KERNEL__ #endif #ifndef MODULE # define MODULE #endif #include #include #include /* printk() */ #include /* error codes */ #include #include #include #include /* udelay() */ #include /* readb(), writeb() */ #include /* copy_to/from_user() */ #include #include "daq.h" #define LOGPRIO KERN_DEBUG #define DAQ_NR_PORTS 4 /* use 4 ports by default */ #define STROBE 0x01 /* strobe bit for digital output port */ #define ENABLE 0x02 /* strobe bit for analog output port */ #define PLOAD 0x01 /* parallel load bit */ #define CLK 0x02 /* clock bit */ #define PE 0x20 /* data bit */ /* The DAQ documentation says: * "...when talking to the port signals should remain stable for * at least 5 to 10 microseconds..." * In our experiments, it seems that we can get by with zero * delay, at least for the Digital Output */ #define DAQ_AOUT_DELAY_US 20 #define DAQ_DOUT_DELAY_US 0 #define DAQ_DIN_DELAY_US 0 #ifdef DAQ_DEBUG #define trace(msg) printk(LOGPRIO msg "\n"); #else #define trace(msg) do {} while (0); #endif /* * all of the parameters have no "daq_" prefix, to save typing when * specifying them at load time */ static int major = 0; /* dynamic by default */ MODULE_PARM(major, "i"); /* the default PC parallel port base address */ static unsigned long base = 0x378; unsigned long daq_base = 0; /* allow the default base address to be overriden by a module parameter */ MODULE_PARM(base, "l"); MODULE_AUTHOR ("Allessandro Rubini/Ted Baker"); MODULE_LICENSE("Dual BSD/GPL"); int daq_open (struct inode *inode, struct file *filp) { trace("daq_open complete"); return 0; } int daq_release (struct inode *inode, struct file *filp) { trace("daq_release complete"); return 0; } union digital_input { unsigned int a; unsigned char x[2]; }; int get_digital_input(void) { union digital_input data; unsigned long address = daq_base; unsigned char temp; unsigned int bits; int i; outb(PLOAD, address); /* "PL" high, "CLK" low */ udelay(DAQ_DIN_DELAY_US); outb(0, address); /* "PL & CLK" both low */ udelay(DAQ_DIN_DELAY_US); outb(CLK, address); /* "PL" low, "CLK" high */ /* loads inputs into shift registers. */ udelay(DAQ_DIN_DELAY_US); outb(PLOAD, address); /* "PL" high, "CLK" low */ udelay(DAQ_DIN_DELAY_US); /* read digital inputs */ data.a = 0; for (i = 0; i < 15; i++) { data.a=data.a<<1; /* shift next bit out of shift registers */ outb(PLOAD|CLK, address); udelay(DAQ_DIN_DELAY_US); outb(PLOAD, address); udelay(DAQ_DIN_DELAY_US); /* read in data bits */ bits = inb(address+1); /* shift in a 1-bit if bit is set */ if (bits & PE) data.a |= 1; } /* swap upper & lower bytes in the 16-bit data */ temp=data.x[0]; data.x[0]=data.x[1]; data.x[1]=temp; return data.a; } ssize_t daq_read(struct file *filp, char *buf, size_t count, loff_t *f_pos) { union digital_input data; if (count != 2) return -EINVAL; if (*f_pos != 0) return -EINVAL; data.a = get_digital_input(); if (copy_to_user(buf, &data, 2)) return -EFAULT; return 2; } ssize_t daq_write(struct file *filp, const char *buf, size_t count, loff_t *f_pos) { struct inode *inode = filp->f_dentry->d_inode; unsigned long address = daq_base + (MINOR(inode->i_rdev)&0x0f); unsigned char data; unsigned char status; if (*f_pos != 0) return -EINVAL; trace("daq_write"); if (count == 2) { if (copy_from_user(&data, buf, 1)) return -EFAULT; /* write to the analog output */ status = inb(address+2); /* save control port status */ data = (unsigned char) ((((unsigned) data)*255)/10); trace("daq_write"); outb(data, address); /* set up data */ udelay(DAQ_AOUT_DELAY_US); outb(status & ~ENABLE, address+2); /* "CE" high */ udelay(DAQ_AOUT_DELAY_US); outb(status | ENABLE, address+2); /* "CE" low */ udelay(DAQ_AOUT_DELAY_US); outb(status & ~ENABLE, address+2); /* "CE" high */ } else { /* write to the digital outputs */ if (count != 1) return -EINVAL; if (copy_from_user(&data, buf, 1)) return -EFAULT; status = inb(address+2); /* save control port status */ outb(data, address); /* set up data */ udelay(DAQ_DOUT_DELAY_US); outb(status & ~STROBE, address+2); /* strobe high */ udelay(DAQ_DOUT_DELAY_US); outb(status | STROBE, address+2); /* strobe low */ udelay(DAQ_DOUT_DELAY_US); outb(status & ~STROBE, address+2); /* strobe high */ } return count; } int scull_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { /* no ioctls implemented */ return -ENOTTY; } struct file_operations daq_fops = { .owner = THIS_MODULE, .read = daq_read, .write = daq_write, .ioctl = scull_ioctl, .open = daq_open, .release = daq_release, }; unsigned long now, before; unsigned long now_jiffies, before_jiffies; int daq_init(void) { struct resource* req_region; int result; daq_base = base; req_region = request_region(daq_base, DAQ_NR_PORTS, "daq"); if (!req_region) { printk(KERN_ERR "daq: can't get I/O port address 0x%lx\n", daq_base); return -1; } result = register_chrdev(major, "daq", &daq_fops); if (result < 0) { printk(KERN_ERR "daq: can't get major number\n"); release_region(daq_base,DAQ_NR_PORTS); return result; } if (major == 0) major = result; /* dynamic */ rdtscl(before); before_jiffies = jiffies; trace("daq_init complete"); return 0; } void daq_cleanup(void) { trace("daq_cleanup started"); unregister_chrdev(major, "daq"); release_region(daq_base, DAQ_NR_PORTS); trace("daq_cleanup complete"); } module_init(daq_init); module_exit(daq_cleanup);