diff -r -u -P linux/Documentation/Configure.help linux-2.2.10-rele/Documentation/Configure.help --- linux/Documentation/Configure.help Mon Jun 14 05:54:06 1999 +++ linux-2.2.10-rele/Documentation/Configure.help Wed Jan 5 12:03:38 2000 @@ -8254,6 +8254,10 @@ status indication when you read from it (for example, with `cat /dev/lp1'). To use this feature, say Y here. +Parallel port relay card support +CONFIG_RELE + Paraller port relay card driver. See http://www.tuug.fi/~isaarine/rele + Mouse Support (not serial mice) CONFIG_MOUSE This is for machines with a bus mouse or a PS/2 mouse as opposed to diff -r -u -P linux/drivers/char/Config.in linux-2.2.10-rele/drivers/char/Config.in --- linux/drivers/char/Config.in Fri May 7 21:05:30 1999 +++ linux-2.2.10-rele/drivers/char/Config.in Wed Jan 5 12:06:23 2000 @@ -54,6 +54,7 @@ if [ "$CONFIG_PRINTER" != "n" ]; then bool ' Support IEEE1284 status readback' CONFIG_PRINTER_READBACK fi + dep_tristate 'Paraller port relay card support' CONFIG_RELE $CONFIG_PARPORT fi bool 'Mouse Support (not serial mice)' CONFIG_MOUSE diff -r -u -P linux/drivers/char/Makefile linux-2.2.10-rele/drivers/char/Makefile --- linux/drivers/char/Makefile Fri May 7 21:05:30 1999 +++ linux-2.2.10-rele/drivers/char/Makefile Wed Jan 5 12:05:26 2000 @@ -180,6 +180,14 @@ endif endif +ifeq ($(CONFIG_RELE),y) +L_OBJS += rele.o +else + ifeq ($(CONFIG_RELE),m) + M_OBJS += rele.o + endif +endif + ifeq ($(CONFIG_JOYSTICK),y) L_OBJS += joystick/js.o SUB_DIRS += joystick diff -r -u -P linux/drivers/char/mem.c linux-2.2.10-rele/drivers/char/mem.c --- linux/drivers/char/mem.c Mon May 10 20:18:34 1999 +++ linux-2.2.10-rele/drivers/char/mem.c Wed Jan 5 12:04:32 2000 @@ -632,6 +632,9 @@ #ifdef CONFIG_PRINTER lp_init(); #endif +#ifdef CONFIG_RELE + rele_init(); +#endif #ifdef CONFIG_M68K_PRINTER lp_m68k_init(); #endif diff -r -u -P linux/drivers/char/rele.c linux-2.2.10-rele/drivers/char/rele.c --- linux/drivers/char/rele.c Thu Jan 1 02:00:00 1970 +++ linux-2.2.10-rele/drivers/char/rele.c Wed Jan 5 12:06:49 2000 @@ -0,0 +1,654 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + +/* Proc filesystem */ +void rele_proc_clean(void); +int rele_proc_register(struct rele_struct *); +int rele_proc_unregister(struct rele_struct *); +static struct proc_dir_entry *new_proc_entry (const char *, mode_t, + struct proc_dir_entry *, + unsigned short, + struct rele_struct *); +static void rele_destroy_proc_tree(struct rele_struct *); +static inline void rele_destroy_proc_entry(struct proc_dir_entry *, + struct proc_dir_entry **); +int rele_proc_init(void); +void rele_proc_clean(void); +static int rele_stat_read_proc(char *, char **, off_t, int, int *, void *); +static int rele_stat_write_proc(struct file *, const char *, + unsigned long, void *); +static int rele_data_read_proc(char *, char **, off_t, int, int *, void *); +static int rele_data_write_proc(struct file *, const char *, + unsigned long, void *); + + +/* Hardware access */ +int rele_check_hardware(unsigned int); +int rele_write_data(unsigned int, unsigned char); +int rele_read_data(unsigned int); + + + + + +/* Max number of rele devices */ +#define RELE_NO 3 + +struct rele_struct rele_table[RELE_NO] = +{ + [0 ... RELE_NO - 1] = {NULL, 0, 0, 0, 0, NULL, {0}, {0}} +}; + + + + + +struct proc_dir_entry *rele_base = NULL; + + +/* Read rele info */ +static int rele_stat_read_proc(char *page, char **start, + off_t off, int count, + int *eof, void *data) +{ + int len = 1; + struct rele_struct *rele = (struct rele_struct *)data; + if(rele_check_hardware(rele->minor)) + len = sprintf(page, "%s : Connected\n", rele->name); + else + len = sprintf(page, "%s : Disconnected\n", rele->name); + *start = 0; + *eof = 1; + return(len); +} + + + +/* Write rele info (nop) */ +static int rele_stat_write_proc(struct file *file, const char *buffer, + unsigned long count, void *data) +{ + return(1); +} + + + + +/* Read rele data */ +static int rele_data_read_proc(char *page, char **start, + off_t off, int count, + int *eof, void *data) +{ + int len, i; + char t[16], bits[9] = { "00000000\0" }; + struct rele_struct *rele = (struct rele_struct *)data; + /* Make binary string from data */ + for(i = 0; i < 8; i++) + if(rele->data & (1 << i)) bits[(7 - i)] = '1'; + len = sprintf(t, "%d (%s)\n", rele->data, bits); + strncpy(page, t, count); + *start = 0; + *eof = 1; + return(len); +} + + + +/* Write rele data */ +static int rele_data_write_proc(struct file *file, const char *buffer, + unsigned long count, void *data) +{ + struct rele_struct *rele = (struct rele_struct *)data; + if(rele->count) + return(0); + copy_from_user(&rele->data, buffer, sizeof(rele->data)); + rele_write_data(rele->minor, rele->data); + return(sizeof(rele->data)); +} + + + + +/* Destroy proc fs entry */ +static inline void rele_destroy_proc_entry(struct proc_dir_entry *root, + struct proc_dir_entry **d) +{ + proc_unregister(root, (*d)->low_ino); + kfree(*d); + *d = NULL; +} + + + + + +/* Add proc fs entry for rele device */ +int rele_proc_register(struct rele_struct *rele) +{ + memset(&rele->rdir, 0, sizeof(struct rele_dir)); + if (rele_base == NULL) { + printk(KERN_CRIT "rele_proc not initialised.\n"); + return 1; + } + strncpy(rele->rdir.name, rele->name + strlen("rele"), + sizeof(rele->rdir.name)); + rele->rdir.entry = new_proc_entry(rele->rdir.name, S_IFDIR, + rele_base, 0, rele); + if (rele->rdir.entry == NULL) + goto out_fail; + rele->rdir.stat = new_proc_entry("status", S_IFREG | S_IRUGO | S_IWUSR, + rele->rdir.entry, 0, rele); + if (rele->rdir.stat == NULL) + goto out_fail; + rele->rdir.stat->read_proc = rele_stat_read_proc; + rele->rdir.stat->write_proc = rele_stat_write_proc; + rele->rdir.stat->data = rele; + + rele->rdir.data = new_proc_entry("data", S_IFREG | S_IRUGO | S_IWUSR, + rele->rdir.entry, 0, rele); + if (rele->rdir.data == NULL) + goto out_fail; + rele->rdir.data->read_proc = rele_data_read_proc; + rele->rdir.data->write_proc = rele_data_write_proc; + rele->rdir.data->data = rele; + return 0; + + out_fail: + printk(KERN_CRIT "%s: failure registering /proc/ entry.\n", + rele->name); + rele_destroy_proc_tree(rele); + return 1; +} + + + +/* Destroy proc fs entry */ +static void rele_destroy_proc_tree(struct rele_struct *rele) { + if (rele->rdir.entry) { + if (rele->rdir.stat) + rele_destroy_proc_entry(rele->rdir.entry, + &rele->rdir.stat); + if (rele->rdir.data) + rele_destroy_proc_entry(rele->rdir.entry, + &rele->rdir.data); + rele_destroy_proc_entry(rele_base, &rele->rdir.entry); + } +} + + + +/* */ +/* +int rele_proc_unregister(struct rele_struct *rele) +{ + rele_destroy_proc_tree(rele); + return 0; +} +*/ + + +/* Make new proc dir entry */ +static struct proc_dir_entry *new_proc_entry(const char *name, mode_t mode, + struct proc_dir_entry *parent, + unsigned short ino, + struct rele_struct *rele) +{ + struct proc_dir_entry *ent; + ent = kmalloc(sizeof(struct proc_dir_entry), GFP_KERNEL); + if (!ent) + return NULL; + memset(ent, 0, sizeof(struct proc_dir_entry)); + if (mode == S_IFDIR) + mode |= S_IRUGO | S_IXUGO; + else if (mode == 0) + mode = S_IFREG | S_IRUGO; + ent->low_ino = ino; + ent->name = name; + ent->namelen = strlen(name); + ent->mode = mode; + if (S_ISDIR(mode)) + ent->nlink = 2; + else + ent->nlink = 1; + proc_register(parent, ent); + return(ent); +} + + + + + + + +/* Initialize proc fs */ +int rele_proc_init(void) +{ + rele_base = new_proc_entry("rele", S_IFDIR, &proc_root, + PROC_SELF, NULL); + if(rele_base == NULL){ + printk(KERN_ERR "Unable to initialise /proc/rele.\n"); + return(0); + } + return(1); +} + + + + + +/* */ +void rele_proc_clean(void) +{ + if(rele_base){ + proc_unregister(&proc_root, rele_base->low_ino); + kfree(rele_base); + rele_base = NULL; + } +} + + + + + + +#define rele_parport_release(x) do { parport_release(rele_table[(x)].dev); } while (0); +#define rele_parport_claim(x) do { parport_claim_or_block(rele_table[(x)].dev); } while (0); + + + + + + +/* Check if rele card exists at the port */ +int rele_check_hardware(unsigned int minor){ + int retv = 1, i; + unsigned int sd, sc; + struct parport *port = rele_table[minor].dev->port; + unsigned int ps = 0; + sc = parport_read_control(port); + sd = parport_read_data(port); + rele_table[minor].flags |= RELE_ON; + parport_write_control(port, RELE_CHECK); + /* Upper 4 bits of data : 0001 - echo lower 4 bits to status bits 6 - 3 */ + /* and set status bit 7 high */ + i = 16; + while(i < 32){ + parport_write_data(port, i); + ps = parport_read_status(port); + if((!(ps & 0x80)) || (((ps >> 3) & 0x0f) != (i & 0x0f))) break; + i+= 2; + } + if(i < 32){ + retv = 0; + rele_table[minor].flags |= ~RELE_ON; + } + parport_write_control(port, sc); + parport_write_data(port, sd); + return(retv); +} + + + +/* Write data to rele card at the port */ +int rele_write_data(unsigned int minor, unsigned char data){ + unsigned int sc, sd; + struct parport *port = rele_table[minor].dev->port; + if(rele_table[minor].flags & RELE_ON){ + sc = parport_read_control(port); + sd = parport_read_data(port); + parport_write_data(port, data); + parport_write_control(port, RELE_DATA_SET); + parport_write_control(port, RELE_DATA_NORM); + rele_table[minor].data = data; + parport_write_control(port, sc); + parport_write_data(port, sd); + return(1); + } else { + return(0); + } +} + + + + +/* Read data from rele card */ +int rele_read_data(unsigned int minor){ + unsigned char d; + unsigned int sd, sc; + struct parport *port = rele_table[minor].dev->port; + sd = parport_read_data(port); + sc = parport_read_control(port); + parport_write_control(port, RELE_CHECK); + + /* Read low 4 bits */ + parport_write_data(port, 32); + d = 0; + d = parport_read_status(port); + rele_table[minor].data = (d >> 3) & 0x0f; + + /* Read high 4 bits */ + parport_write_data(port, 64); + d = 0; + d = parport_read_status(port); + rele_table[minor].data += (d << 1) & 0xf0; + + parport_write_data(port, sd); + parport_write_control(port, sc); + return(1); +} + + + + + +static __inline__ void rele_yield (int minor) +{ + if(!parport_yield_blocking (rele_table[minor].dev)) + if(current->need_resched) schedule(); +} + + + +static int rele_write_char(unsigned int minor, unsigned char data) +{ + struct rele_struct *rele = &rele_table[minor]; + if(minor >= RELE_NO) + return(-ENXIO); + if(rele->dev == NULL) + return(-ENXIO); + if(signal_pending(current)) + return(0); + for(;;){ + rele_yield(minor); + if(rele_check_hardware(minor)) + break; + } + + return(rele_write_data(minor, rele->data)); +} + + + + +/* Write to device */ +static ssize_t rele_write(struct file *file, const char *buf, + size_t count, loff_t *ppos) +{ + unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev); + ssize_t retv = 0; + copy_from_user(&rele_table[minor].data, buf, sizeof(rele_table[minor].data)); + /* This works too ... */ + /* + rele_table[minor].data = buf[0]; + */ + rele_parport_claim(minor); + retv = rele_write_char(minor, rele_table[minor].data); + rele_parport_release(minor); + return(retv); +} + + + +static long long rele_lseek(struct file * file, long long offset, int origin) +{ + return(-ESPIPE); +} + + + +/* Read from device */ +static ssize_t rele_read(struct file *file, char *buf, + size_t length, loff_t *ppos) +{ + ssize_t len, retv = 0; + char tmp[2]; + unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev); + if(length == 0) + return(0); + + rele_parport_claim(minor); + len = rele_read_data(minor); + rele_parport_release(minor); + + len = 1; + if(len){ + sprintf(tmp, "%c", rele_table[minor].data); + copy_to_user(buf, &tmp, strlen(tmp)); + retv = sizeof(unsigned char); + } else { + copy_to_user(buf, '\0', sizeof(char)); + retv = 0; + } + + /* This works too ... */ + /* + buf[0] = rele_table[minor].data; + */ + + return(retv); +} + + +/* Open device */ +static int rele_open(struct inode *inode, struct file *file) +{ + unsigned int minor = MINOR(inode->i_rdev); + if(minor >= RELE_NO) + return(-ENXIO); + if(rele_table[minor].count) + return(-EBUSY); + if(!rele_check_hardware(minor)) + return(-ENODEV); + rele_table[minor].count++; + MOD_INC_USE_COUNT; + return(0); +} + + +/* Close device */ +static int rele_close(struct inode *inode, struct file *file) +{ + unsigned int minor = MINOR(inode->i_rdev); + if(rele_table[minor].count) + rele_table[minor].count--; + MOD_DEC_USE_COUNT; + return(0); +} + + +/* Device fileops */ +static struct file_operations rele_fops = { + rele_lseek, + rele_read, + rele_write, + NULL, + NULL, + NULL, + NULL, + rele_open, + NULL, + rele_close +}; + + + + + +#ifdef MODULE + +static int parport_n[RELE_NO] = { [0 ... RELE_NO - 1] = RELE_UNSPEC}; +static char *parport[RELE_NO] = {NULL, }; + +MODULE_PARM(parport, "1-" __MODULE_STRING(RELE_NO) "s"); + +#else + +static int parport_n[RELE_NO] __initdata = { [0 ... RELE_NO - 1] = RELE_UNSPEC }; +static int parport_count = 0; + +__initfunc(void rele_setup(char *str, int *ints)) +{ + if(!str){ + if(ints[0] == 0 || ints[1] == 0) + parport_n[0] = RELE_OFF; + }else if(!strncmp(str, "parport", 7)){ + int n = simple_strtoul(str + 7, NULL, 10); + if(parport_count < RELE_NO) + parport_n[parport_count++] = n; + else + printk(KERN_INFO "rele : too many ports. Ignored (%s)\n", str); + }else if(!strcmp(str, "auto")){ + parport_n[0] = RELE_AUTO; + }else if(!strcmp(str, "none")){ + parport_n[parport_count++] = RELE_NONE; + } +} + +#endif /* else MODULE */ + + + +/* Needed by parport_register_device. See function rele_register. */ +static int rele_preempt(void *handle) +{ + struct rele_struct *rele = (struct rele_struct *)handle; + if(waitqueue_active(&rele->wait_q)) + wake_up_interruptible(&rele->wait_q); + return(1); +} + + +/* Register rele device */ +int rele_register(int n, struct parport *port) +{ + rele_table[n].dev = parport_register_device(port, + "rele", + rele_preempt, + NULL, + NULL, + 0, + (void *) &rele_table[n]); + if(rele_table[n].dev == NULL) + return(1); + memset(rele_table[n].name, 0, sizeof(rele_table[n].name)); + sprintf(rele_table[n].name, "rele%d", n); + rele_proc_register(&rele_table[n]); + rele_table[n].count = 0; + rele_table[n].minor = n; + if(rele_check_hardware(n)){ + rele_write_data(n, 0); + rele_table[n].flags |= RELE_EXISTS; + printk(KERN_INFO "rele%d : using %s.\n", n, port->name); + } else { + printk(KERN_INFO "rele%d : using %s (External hardware not found).\n", n, port->name); + } + return(0); +} + + + +/* Init rele */ +int rele_init(void) +{ + unsigned int count = 0; + unsigned int i; + struct parport *port; + rele_proc_init(); + switch(parport_n[0]){ + case RELE_OFF: + return(0); + case RELE_UNSPEC: + case RELE_AUTO: + for(port = parport_enumerate(); port; port = port->next){ + if(parport_n[0] == RELE_AUTO && + port->probe_info.class != PARPORT_CLASS_PRINTER) + continue; + if(!rele_register(count, port)) + if(++count == RELE_NO) + break; + } + break; + default: + for(i = 0; i < RELE_NO; i++){ + for(port = parport_enumerate(); port; port = port->next){ + if(port->number == parport_n[i]){ + if(!rele_register(i, port)) + count++; + break; + } + } + } + break; + } + + if(count){ + if(register_chrdev(RELE_MAJOR, "rele", &rele_fops)){ + printk("rele : unable to get major %d\n", RELE_MAJOR); + return(-EIO); + } + return(0); + }else{ + return(0); + } +} + + + + +#ifdef MODULE + +int init_module(void) +{ + if(parport[0]){ + if(!strncmp(parport[0], "auto", 4)){ + parport_n[0] = RELE_AUTO; + }else{ + int n; + for(n = 0; n < RELE_NO && parport[n]; n++){ + if(!strncmp(parport[n], "none", 4)){ + parport_n[n] = RELE_NONE; + }else{ + char *p; + unsigned long r = simple_strtoul(parport[n], &p, 0); + if(p != parport[n]){ + parport_n[n] = r; + }else{ + printk(KERN_ERR "rele : invalid port '%s'\n", parport[n]); + return(-ENODEV); + } + } + } + } + } + return(rele_init()); +} + + + + +void cleanup_module(void) +{ + unsigned int n; + unregister_chrdev(RELE_MAJOR, "rele"); + for(n = 0; n < RELE_NO; n++){ + if(rele_table[n].dev != NULL){ + rele_write_data(n, 0); + parport_unregister_device(rele_table[n].dev); + } + } + rele_proc_clean(); +} + + +#endif diff -r -u -P linux/include/linux/proc_fs.h linux-2.2.10-rele/include/linux/proc_fs.h --- linux/include/linux/proc_fs.h Tue May 11 20:36:09 1999 +++ linux-2.2.10-rele/include/linux/proc_fs.h Wed Jan 5 12:08:17 2000 @@ -52,6 +52,7 @@ PROC_STRAM, PROC_SOUND, PROC_MTRR, /* whether enabled or not */ + PROC_RELE, PROC_FS }; diff -r -u -P linux/include/linux/rele.h linux-2.2.10-rele/include/linux/rele.h --- linux/include/linux/rele.h Thu Jan 1 02:00:00 1970 +++ linux-2.2.10-rele/include/linux/rele.h Wed Jan 5 12:07:07 2000 @@ -0,0 +1,56 @@ +#include + + +#ifndef _RELE_H +#define _RELE_H + + +#ifdef __KERNEL__ + +#define RELE_NORM ((PARPORT_CONTROL_SELECT | PARPORT_CONTROL_STROBE)^0x0b) +#define RELE_CHECK ((PARPORT_CONTROL_SELECT)^0x0b) + +#define RELE_DATA_NORM ((PARPORT_CONTROL_SELECT | PARPORT_CONTROL_AUTOFD | PARPORT_CONTROL_STROBE)^0x0b) +#define RELE_DATA_SET ((PARPORT_CONTROL_SELECT | PARPORT_CONTROL_AUTOFD)^0x0b) + +#define RELE_CHECK_A (PARPORT_STATUS_BUSY | PARPORT_STATUS_PAPEROUT | 7) +#define RELE_CHECK_B (PARPORT_STATUS_BUSY | PARPORT_STATUS_PAPEROUT | PARPORT_STATUS_ERROR | 7) + +#define RELE_DATA_READ_H ((9 << 4) + 10) /* 10011010 */ +#define RELE_DATA_READ_L ((10 << 4) + 10) /* 10101010 */ + +#define RELE_UNSPEC -4 +#define RELE_AUTO -3 +#define RELE_OFF -2 +#define RELE_NONE -1 + +#define RELE_EXISTS 1 +#define RELE_ON 2 + +/* This should be somewhere else ... */ +#define RELE_MAJOR 100 + +/* Proc fs entry */ +struct rele_dir { + struct proc_dir_entry *entry; + struct proc_dir_entry *stat; + struct proc_dir_entry *data; + char name[6]; +}; + +/* Rele device */ +struct rele_struct { + struct pardevice *dev; + unsigned int flags; + unsigned int count; + unsigned int minor; + unsigned char data; + struct wait_queue *wait_q; + char name[6]; + struct rele_dir rdir; +}; + + +#endif + +#endif diff -r -u -P linux/init/main.c linux-2.2.10-rele/init/main.c --- linux/init/main.c Tue May 11 19:57:14 1999 +++ linux-2.2.10-rele/init/main.c Wed Jan 5 12:09:02 2000 @@ -106,6 +106,9 @@ #ifdef CONFIG_PRINTER extern void lp_setup(char *str, int *ints); #endif +#ifdef CONFIG_RELE +extern void rele_setup(char *str, int *ints); +#endif #ifdef CONFIG_JOY_AMIGA extern void js_am_setup(char *str, int *ints); #endif @@ -604,6 +607,9 @@ #endif #ifdef CONFIG_PRINTER { "lp=", lp_setup }, +#endif +#ifdef CONFIG_RELE + { "rele=", rele_setup }, #endif #ifdef CONFIG_JOY_AMIGA { "js_am=", js_am_setup },