/***************************************************************************** pxlc - Parallel Christmas Lighting Controller Kernel Loadable Module To send data to the data port you send a byte to address 378 hex. To send data to the control port you send a byte to address 37A hex. base+0 data base+1 status base+2 control The pins that the data port 378 controls are as follows: PINS 9 8 7 6 5 4 3 2 ie. if you execute: out 378,5 (binary 00000101) then pins 4 and 2 change from 0v to 5v. The pins that the control port (37A) controls are as follows: PINS - - - - 17 16 14 1 The pxlc uses : 1 - Clock the databus (C0 inverted) 14 - Strobe input on the 138 (secondary latch) (C1 inverted) The control bits are a little bit more complicated than the data bits since three of them are inverted. This means that if you want the signal to go high (+5V), you must send a zero (0V) to the bit and it will automatically be inverted (notted) and output a 1 (+5V). You are also using a slightly different base address to access the control bits. The parallel port base address for the first port is typically 0x378 (in hex) and the control address is base+2. *****************************************************************************/ #include #include #include #include #include #include #include #include #include "pxlc_if.h" #define PXLC_MAJOR 39 #define PXLC_NAME "pxlc" #define PARALLEL_PORT_INTERRUPT 7 #define PAR_DATA 0x378 #define PAR_CONTROL 0x37A #define PAR_ECONTROL 0x77C #define ECR_SPP 00 #define ECR_MODE_MASK 0xe0 static unsigned char pxlc_byte [PXLC_BYTE_COUNT]; static int parport_ECR_present(); // open function - called when the "file" /dev/pxlc is opened in userspace static int pxlc_open (struct inode *inode, struct file *file) { // printk("pxlc_open\n"); // we could do some checking on the flags supplied by "open" // i.e. O_NONBLOCK // -> set some flag to disable interruptible_sleep_on in pxlc_read return 0; } // close function - called when the "file" /dev/pxlc is closed in userspace static int pxlc_release (struct inode *inode, struct file *file) { // printk("pxlc_release\n"); return 0; } // read function called when from /dev/pxlc is read static ssize_t pxlc_read (struct file *file, char *buf, size_t count, loff_t *ppos) { int err; // The only allowable read is to read all the bytes. if (count != PXLC_BYTE_COUNT) { return -EINVAL; } err = copy_to_user(buf, pxlc_byte, count); if (err != 0) return -EFAULT; return count; } static void brief_pause() { static int ignore; ignore++; } static inline void set_byte(unsigned char data, int bytenum) { // set_box function by Hill Robertson // // This function sends the 6 writes for each parallel port instruction // to control (currently) 40 8-circuit Christmas light SSR controller // boxes. The box and bank data are selected and then the write sequence // begins. int bank; // Upper address lines that select a 74138 int bankbox; // 3 lower bits that drive the 74138 int base = PAR_DATA; int control = PAR_CONTROL; // if (box>=1&&box<=8) { bank=8; bankbox=box-1; } // if (box>=9&&box<=16) { bank=16; bankbox=box-9; } // Reduced to the following since pxlc only has two banks if (bytenum < 8) { bank=8; bankbox=bytenum; } else { bank=16; bankbox=bytenum - 8; } // base=0x378 for first parallel port base address // control=base+2 for first parallel port control address outb(data, base); // Write 1: // Outputs data byte (D0-D7) on pins 2-9 // of parallel port brief_pause(); outb(0, control); // Write 2: // Outputs a 1 (high) on C0 and C1 // (pins 1 and 14) since they are inverted // without changing any states on the data pins // This will latch data into the first 74374. // Not needed since the data is now latched. // outb(1, control); // Write 3: // Outputs a 0 (low) on C0 and a 1 (high) on // C1 since they are inverted. Again, not // changing any states on the data pins brief_pause(); outb(bankbox+bank, base); // Write 4: // Outputs the steering (addressing) data on // the data pins. bankbox selects a 74138 // and bank select a line on that 74138. brief_pause(); outb(3, control); // Write 5: // Outputs a 0 (low) on both C0 and C1 // since they are inverted. This will patch // a high-to-low transition through to the // selected 74374 data byte. // It also resets the 74374 latch for the // next cycle. brief_pause(); outb(1, control); // Write 6: // Outputs a 0 (low) on C0 and a 1 (high) on // C1 since they are inverted. // The low-to-high transition on C1 will // latch the databyte from the data 74374 // into the selected 74374 data byte. } // write function called when to /dev/pxlc is written // must always write two 32-bit words: an opcode and a parameter static ssize_t pxlc_write (struct file *file, const char *buf, size_t count, loff_t *ppos) { int err; int i; int bytenum, bitnum; uint32_t tmpbuf[2]; static const unsigned char on_mask[8] = {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01}; static const unsigned char off_mask[8] = {0x7f, 0xbf, 0xdf, 0xef, 0xf7, 0xfb, 0xfd, 0xfe}; // The only allowable write is two words if (count != 8) { return -EINVAL; } err = copy_from_user(tmpbuf, buf, count); if (err != 0) return -EFAULT; bytenum = tmpbuf[1] / 8; if (bytenum > PXLC_BYTE_COUNT) { return -EINVAL; } bitnum = tmpbuf[1] % 8; if (tmpbuf[0] == PXLC_OP_SETBIT) { pxlc_byte[bytenum] |= on_mask[bitnum]; set_byte(pxlc_byte[bytenum], bytenum); return count; } if (tmpbuf[0] == PXLC_OP_CLRBIT) { pxlc_byte[bytenum] &= off_mask[bitnum]; set_byte(pxlc_byte[bytenum], bytenum); return count; } if (tmpbuf[0] == PXLC_OP_RESET) { // Initialize C0 to low (1) and C1 to high (0). This preps // The 74374 data latch at C0 for the low to high transition, // and holds the outputs of all 74138s to high. outb(1, PAR_CONTROL); for (i=0; i < PXLC_BYTE_COUNT; i++) { pxlc_byte[i] = 0; set_byte(pxlc_byte[i], i); } return count; } return -ENOENT; } // interrupt handler static int interrupt_handler(void) { printk(">>> PXLC INT HANDLED\n"); return 0; } // ioctl - I/O control static int pxlc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { int retval = 0; switch ( cmd ) { case 111: retval = (int) interrupt_handler; break; default: retval = -EINVAL; } return retval; } // define which file operations are supported struct file_operations pxlc_fops = { .owner = THIS_MODULE, .llseek = NULL, .read = pxlc_read, .write = pxlc_write, .readdir = NULL, .poll = NULL, .ioctl = pxlc_ioctl, .mmap = NULL, .open = pxlc_open, .flush = NULL, .release = pxlc_release, .fsync = NULL, .fasync = NULL, .lock = NULL, .readv = NULL, .writev = NULL, }; // initialize module (and interrupt) static int __init pxlc_init_module (void) { int i; printk("initializing pxlc module\n"); i = register_chrdev (PXLC_MAJOR, PXLC_NAME, &pxlc_fops); if (i != 0) return - EIO; parport_ECR_present(); return 0; } // close and cleanup module static void __exit pxlc_cleanup_module (void) { printk("cleaning up pxlc module\n"); unregister_chrdev (PXLC_MAJOR, PXLC_NAME); } /************************************************************************* The following code is lifted from parport_pc.c. It is just a dumb way to get the parallel port into SPP mode. The pc control block has been ripped out. **************************************************************************/ /* frob_control, but for ECR */ static void frob_econtrol (unsigned char m, unsigned char v) { unsigned char ectr = 0; if (m != 0xff) ectr = inb (PAR_ECONTROL); outb ((ectr & ~m) ^ v, PAR_ECONTROL); } static void __inline__ frob_set_mode (int mode) { frob_econtrol (ECR_MODE_MASK, mode << 5); } #define ECR_WRITE(v) frob_econtrol(0xff,(v)) /* Check for ECR * * Old style XT ports alias io ports every 0x400, hence accessing ECR * on these cards actually accesses the CTR. * * Modern cards don't do this but reading from ECR will return 0xff * regardless of what is written here if the card does NOT support * ECP. * * We first check to see if ECR is the same as CTR. If not, the low * two bits of ECR aren't writable, so we check by writing ECR and * reading it back to see if it's what we expect. */ static int parport_ECR_present() { unsigned char r = 0xc; outb (r, PAR_CONTROL); if ((inb (PAR_ECONTROL) & 0x3) == (r & 0x3)) { outb (r ^ 0x2, PAR_CONTROL); /* Toggle bit 1 */ r = inb (PAR_CONTROL); if ((inb (PAR_ECONTROL) & 0x2) == (r & 0x2)) goto no_reg; /* Sure that no ECR register exists */ } if ((inb (PAR_ECONTROL) & 0x3 ) != 0x1) goto no_reg; ECR_WRITE (0x34); if (inb (PAR_ECONTROL) != 0x35) goto no_reg; outb (0xc, PAR_CONTROL); /* Go to mode 000 */ frob_set_mode (ECR_SPP); return 1; no_reg: outb (0xc, PAR_CONTROL); return 0; } module_init(pxlc_init_module); module_exit(pxlc_cleanup_module); MODULE_AUTHOR("Don Law"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Parallel Christmas Lighting Controller Driver");