write() works if the device was open()ed for writing.
ENIO remains on on close().
.cvsignore files added
--- a/.cvsignore Wed Jan 16 15:54:58 2002 +0100
+++ b/.cvsignore Wed Jan 16 20:29:27 2002 +0100
@@ -1,6 +1,1 @@
-me8100_test_counter/me8100_test_counter
-me8100_test_dio/me8100_test_dio
-me8100_test_dio/wtest
-me8100_test_dio/rtest
-me8100_test_int/me8100_test_int
-me8100_test_int/test
+*.o
--- a/README.hs Wed Jan 16 15:54:58 2002 +0100
+++ b/README.hs Wed Jan 16 20:29:27 2002 +0100
@@ -17,3 +17,10 @@
* read(): Beim ersten Lesen den aktuellen Zustand, und dann blockieren, bis
Interrupt kommt und weiterlesen... (?)
+
+* close(): Sollen die digital-Ausgänge dann hochohmig werden?
+ pro: scheint logisch zu sein.
+ con: echo -n 'A' >/dev/me8100_0a
+ geht dann nicht, weil ja sofort wieder close() aufgerufen wird.
+
+ Lösung: ein IOCTL für "shutdown on close" oder so ähnlich
--- a/me8100.c Wed Jan 16 15:54:58 2002 +0100
+++ b/me8100.c Wed Jan 16 20:29:27 2002 +0100
@@ -134,6 +134,7 @@
static int me8100_fasync(int, struct file *, int);
static void me8100_isr(int, void *, struct pt_regs *);
static ssize_t me8100_read(struct file *, char *, size_t, loff_t *);
+static ssize_t me8100_write(struct file *, const char *, size_t, loff_t *);
static int me8100_init_board(me8100_info_type *, struct pci_dev *);
static int me8100_reset_board(me8100_info_type *);
@@ -172,8 +173,8 @@
static int me8100_get_board_info(me8100_info_type *, me8100_info_type *);
static int me8100_get_int_count(me8100_int_occur_type *, me8100_info_type *);
-static inline int DEVICE(int i) { return MINOR(i) & 0x0f; }
-static inline int SUBDEVICE(int i) { return MINOR(i) >> 4; }
+static inline int DEVICE(int i) { return i & 0x0f; }
+static inline int SUBDEVICE(int i) { return i >> 4; }
/* File operations provided by the driver */
@@ -183,7 +184,7 @@
#endif
llseek: NULL, /* lseek() */
read: me8100_read, /* read() */
- write: NULL, /* write() */
+ write: me8100_write, /* write() */
readdir: NULL, /* readdir() */
poll: NULL, /* poll() */
ioctl: me8100_ioctl, /* ioctl() */
@@ -435,6 +436,9 @@
info->me8100_regbase_size = ME8100_BASE_SIZE;
info->me8100_regbase = me8100_regbase_tmp & PCI_BASE_ADDRESS_IO_MASK;
PDEBUG("me8100_init_board():IO at 0x%04X\n", info->me8100_regbase);
+
+ info->subinfo[0].regbase = info->me8100_regbase;
+ info->subinfo[1].regbase = info->me8100_regbase + 0x0C;
/*--------------------------- init device info ----------------------------*/
@@ -572,30 +576,40 @@
* when compiling for SMP.
*/
static int me8100_open(struct inode *inode_ptr, struct file *file_ptr){
- int minor = 0;
+ int device,subdevice;
me8100_info_type *info;
+ struct me8100_subinfo *subinfo;
- PDEBUG("*** me8100_open() is executed for pid: %d\n", file_ptr->f_owner.pid);
+ PDEBUG("*** me8100_open() is executed\n");
- minor = DEVICE(inode_ptr->i_rdev);
+ device = DEVICE(MINOR(inode_ptr->i_rdev));
+ subdevice = SUBDEVICE(MINOR(inode_ptr->i_rdev));
+ info = &info_vec[device];
+ subinfo = &info->subinfo[subdevice];
- PDEBUG("*** device: %d, subdevice %d\n", DEVICE(inode_ptr->i_rdev),
- SUBDEVICE(inode_ptr->i_rdev));
+ PDEBUG("*** device: %d, subdevice %d\n", device, subdevice);
- if(minor >= me8100_board_count){
- printk(KERN_ERR"ME8100:me8100_open():Board %d doesn't exist\n", minor);
+ if(device >= me8100_board_count){
+ printk(KERN_ERR"ME8100:me8100_open():Board %d doesn't exist\n", device);
return -ENODEV;
}
- info = &info_vec[minor];
spin_lock(&info->use_lock);
if(info->board_in_use){
- printk(KERN_ERR "WARNING: ME8100:me8100_open():Board %d already in use\n", minor);
+ printk(KERN_ERR "WARNING: ME8100:me8100_open():Board %d already in use\n", device);
}
++info->board_in_use;
spin_unlock(&info->use_lock);
- /* info->file_ptr = file_ptr; */ /* müssen wir uns nicht hier merken! */
+ if (file_ptr->f_mode & FMODE_WRITE) {
+ PDEBUG("*** open for writing\n");
+
+ if (0 == subinfo->num_writer++) {
+ subinfo->ctrl_reg |= ME8100_CTRL_ENIO | ME8100_CTRL_SOURCE;
+ PDEBUG("*** setting ENIO+SOURCE mode: ctrl: 0x%x\n", subinfo->ctrl_reg);
+ outw(subinfo->ctrl_reg, subinfo->regbase + ME8100_CTRL_REG);
+ }
+ }
MOD_INC_USE_COUNT;
@@ -631,15 +645,27 @@
* Modification:
*/
static int me8100_release(struct inode *inode_ptr, struct file *file_ptr){
- int minor;
+ int device;
int err;
me8100_info_type *info;
+ struct me8100_subinfo* subinfo;
PDEBUG("me8100_release() is executed\n");
- minor = DEVICE(inode_ptr->i_rdev);
- info = &info_vec[minor];
+ device = DEVICE(MINOR(inode_ptr->i_rdev));
+ info = &info_vec[device];
+ subinfo = &info->subinfo[SUBDEVICE(MINOR(inode_ptr->i_rdev))];
+
+ /* Resetting write mode if last writer is gone */
+ if (file_ptr->f_mode & FMODE_WRITE) {
+ PDEBUG("*** writer closes\n");
+ if (0 == --subinfo->num_writer) {
+ subinfo->ctrl_reg &= !ME8100_CTRL_ENIO;
+ // PDEBUG("*** resetting ENIO mode ctrl: 0x%x\n", subinfo->ctrl_reg);
+ // outw(subinfo->ctrl_reg, subinfo->regbase + ME8100_CTRL_REG);
+ }
+ }
/* resetting the board on last close */
@@ -694,11 +720,12 @@
unsigned int service,
unsigned long arg){
- int minor = 0;
+ int minor, device;
PDEBUG("me8100_ioctl() is executed\n");
- minor = DEVICE(inode_ptr->i_rdev);
+ minor = MINOR(inode_ptr->i_rdev);
+ device = DEVICE(minor);
if(_IOC_TYPE(service) != ME8100_MAGIC){
printk(KERN_ERR"ME8100:Invalid ME8100_MAGIC\n");
@@ -711,65 +738,65 @@
switch(service){
case ME8100_READ_ID_A:
- return me8100_read_id_a((unsigned short *) arg, &info_vec[minor]);
+ return me8100_read_id_a((unsigned short *) arg, &info_vec[device]);
case ME8100_WRITE_CTRL_A:
- return me8100_write_ctrl_a((unsigned short *) arg, &info_vec[minor]);
+ return me8100_write_ctrl_a((unsigned short *) arg, &info_vec[device]);
case ME8100_RES_INT_A:
- return me8100_res_int_a(&info_vec[minor]);
+ return me8100_res_int_a(&info_vec[device]);
case ME8100_READ_DI_A:
- return me8100_read_di_a((unsigned short *) arg, &info_vec[minor]);
+ return me8100_read_di_a((unsigned short *) arg, &info_vec[device]);
case ME8100_WRITE_DO_A:
- return me8100_write_do_a((unsigned short *) arg, &info_vec[minor]);
+ return me8100_write_do_a((unsigned short *) arg, &info_vec[device]);
case ME8100_WRITE_PATTERN_A:
- return me8100_write_pattern_a((unsigned short *) arg, &info_vec[minor]);
+ return me8100_write_pattern_a((unsigned short *) arg, &info_vec[device]);
case ME8100_WRITE_MASK_A:
- return me8100_write_mask_a((unsigned short *) arg, &info_vec[minor]);
+ return me8100_write_mask_a((unsigned short *) arg, &info_vec[device]);
case ME8100_READ_INT_DI_A:
- return me8100_read_int_di_a((unsigned short *) arg, &info_vec[minor]);
+ return me8100_read_int_di_a((unsigned short *) arg, &info_vec[device]);
case ME8100_READ_ID_B:
- return me8100_read_id_b((unsigned short *) arg, &info_vec[minor]);
+ return me8100_read_id_b((unsigned short *) arg, &info_vec[device]);
case ME8100_WRITE_CTRL_B:
- return me8100_write_ctrl_b((unsigned short *) arg, &info_vec[minor]);
+ return me8100_write_ctrl_b((unsigned short *) arg, &info_vec[device]);
case ME8100_RES_INT_B:
- return me8100_res_int_b(&info_vec[minor]);
+ return me8100_res_int_b(&info_vec[device]);
case ME8100_READ_DI_B:
- return me8100_read_di_b((unsigned short *) arg, &info_vec[minor]);
+ return me8100_read_di_b((unsigned short *) arg, &info_vec[device]);
case ME8100_WRITE_DO_B:
- return me8100_write_do_b((unsigned short *) arg, &info_vec[minor]);
+ return me8100_write_do_b((unsigned short *) arg, &info_vec[device]);
case ME8100_WRITE_PATTERN_B:
- return me8100_write_pattern_b((unsigned short *) arg, &info_vec[minor]);
+ return me8100_write_pattern_b((unsigned short *) arg, &info_vec[device]);
case ME8100_WRITE_MASK_B:
- return me8100_write_mask_b((unsigned short *) arg, &info_vec[minor]);
+ return me8100_write_mask_b((unsigned short *) arg, &info_vec[device]);
case ME8100_READ_INT_DI_B:
- return me8100_read_int_di_b((unsigned short *) arg, &info_vec[minor]);
+ return me8100_read_int_di_b((unsigned short *) arg, &info_vec[device]);
case ME8100_WRITE_COUNTER_0:
- return me8100_write_counter_0((unsigned char *) arg, &info_vec[minor]);
+ return me8100_write_counter_0((unsigned char *) arg, &info_vec[device]);
case ME8100_WRITE_COUNTER_1:
- return me8100_write_counter_1((unsigned char *) arg, &info_vec[minor]);
+ return me8100_write_counter_1((unsigned char *) arg, &info_vec[device]);
case ME8100_WRITE_COUNTER_2:
- return me8100_write_counter_2((unsigned char *) arg, &info_vec[minor]);
+ return me8100_write_counter_2((unsigned char *) arg, &info_vec[device]);
case ME8100_READ_COUNTER_0:
- return me8100_read_counter_0((unsigned char *) arg, &info_vec[minor]);
+ return me8100_read_counter_0((unsigned char *) arg, &info_vec[device]);
case ME8100_READ_COUNTER_1:
- return me8100_read_counter_1((unsigned char *) arg, &info_vec[minor]);
+ return me8100_read_counter_1((unsigned char *) arg, &info_vec[device]);
case ME8100_READ_COUNTER_2:
- return me8100_read_counter_2((unsigned char *) arg, &info_vec[minor]);
+ return me8100_read_counter_2((unsigned char *) arg, &info_vec[device]);
case ME8100_SETUP_COUNTER:
- return me8100_setup_counter((unsigned char *) arg, &info_vec[minor]);
+ return me8100_setup_counter((unsigned char *) arg, &info_vec[device]);
case ME8100_GET_SERIAL:
- return me8100_get_serial((unsigned int *) arg, &info_vec[minor]);
+ return me8100_get_serial((unsigned int *) arg, &info_vec[device]);
case ME8100_GET_NAME:
- return me8100_get_name((me8100_version_enum_type *) arg, &info_vec[minor]);
+ return me8100_get_name((me8100_version_enum_type *) arg, &info_vec[device]);
case ME8100_INT_OCCUR:
- return me8100_int_occur((me8100_int_occur_type *) arg, &info_vec[minor]);
+ return me8100_int_occur((me8100_int_occur_type *) arg, &info_vec[device]);
case ME8100_SETUP_ICSR:
- return me8100_setup_icsr((unsigned char *) arg, &info_vec[minor]);
+ return me8100_setup_icsr((unsigned char *) arg, &info_vec[device]);
case ME8100_READ_ICSR:
- return me8100_read_icsr((unsigned char *) arg, &info_vec[minor]);
+ return me8100_read_icsr((unsigned char *) arg, &info_vec[device]);
case ME8100_GET_BOARD_INFO:
- return me8100_get_board_info((me8100_info_type *)arg, &info_vec[minor]);
+ return me8100_get_board_info((me8100_info_type *)arg, &info_vec[device]);
case ME8100_GET_INT_COUNT:
- return me8100_get_int_count((me8100_int_occur_type *)arg,&info_vec[minor]);
+ return me8100_get_int_count((me8100_int_occur_type *)arg,&info_vec[device]);
default:
return -EINVAL;
}
@@ -807,11 +834,11 @@
*/
static int me8100_fasync(int fd, struct file *file_ptr, int mode){
int val;
- int minor;
+ int device;
me8100_info_type *info;
- minor = DEVICE(file_ptr->f_dentry->d_inode->i_rdev);
- info = &info_vec[minor];
+ device = DEVICE(MINOR(file_ptr->f_dentry->d_inode->i_rdev));
+ info = &info_vec[device];
PDEBUG("me8100_fasync() is executed\n");
PDEBUG("** fasync_ptr: %p\n", info->fasync_ptr);
@@ -1037,8 +1064,6 @@
return 0;
}
-
-
/*
* Routine:
* me8100_write_pattern_a
@@ -2067,14 +2092,14 @@
*/
void cleanup_module(void){
extern unsigned int major;
- int minor;
+ int device;
int err;
PDEBUG("cleanup_module() is executed\n");
- for (minor = me8100_board_count - 1; minor > -1; --minor) {
- free_irq(info_vec[minor].int_line, (void *) &info_vec[minor]);
+ for (device = me8100_board_count - 1; device > -1; --device) {
+ free_irq(info_vec[device].int_line, (void *) &info_vec[device]);
}
if(major){
@@ -2084,11 +2109,46 @@
}
}
-ssize_t me8100_read(struct file * file_ptr, char *buffer, size_t len, loff_t *offset) {
- PDEBUG("me8100_read() called\n");
+ssize_t me8100_read(struct file * file_ptr, char *buffer, size_t len, loff_t *offset)
+{
+ PDEBUG("me8100_read(%d) called\n", len);
return -EINVAL;
}
+/* Writing: we do only write one word (an unsigned short) and return immediatly. Yes,
+ * we could loop over the complete buffer, but it's not expected to get more data
+ * than one word. If there's more output, the responsibility is transferred back
+ * to the C library(?).
+ */
+ssize_t me8100_write(struct file * file_ptr, const char *buffer, size_t len, loff_t *offset)
+{
+ int err;
+ unsigned short val;
+ int minor;
+
+ minor = MINOR(file_ptr->f_dentry->d_inode->i_rdev);
+ PDEBUG("me8100_write(%d) called\n", len);
+
+ if (len == 0) return 0; /* do we need this? */
+ if (len < 0) return -EINVAL; /* do we need this? */
+
+ /* Take care of "short" writes! */
+ if (len >= sizeof(unsigned short)) {
+ err = get_user(val, (unsigned short*) buffer);
+ len = sizeof(unsigned short);
+ } else {
+ err = get_user(val, (char*) buffer);
+ val &= 0xff;
+ }
+
+ if (err) return err;
+
+ PDEBUG("*** me8100_write: val=0x%04x\n", val);
+ outw(val, info_vec[DEVICE(minor)].subinfo[SUBDEVICE(minor)].regbase + ME8100_DO_REG);
+
+ return len;
+}
+
/*
vim:sts=2 sw=2 aw ai sm:
*/
--- a/me8100.h Wed Jan 16 15:54:58 2002 +0100
+++ b/me8100.h Wed Jan 16 20:29:27 2002 +0100
@@ -127,12 +127,15 @@
/*********************************************************/
/* Some definitions to be written to the CTRL register */
-#define ME8100_CTL_ENIO 0x80
-#define ME8100_CTL_SOURCE 0x10
-#define ME8100_CTL_SINK 0x00
+#define ME8100_CTRL_ENIO 0x80
+#define ME8100_CTRL_SOURCE 0x10
+#define ME8100_CTRL_SINK 0x00
-#define ME8100_CTL_IRQ_PATTERN 0x40
-#define ME8100_CTL_IRQ_MASK 0x60
+#define ME8100_CTRL_IRQ_PATTERN 0x40
+#define ME8100_CTRL_IRQ_MASK 0x60
+
+#define ME8100_CTRL_REG 0x00 //( ,w)
+#define ME8100_DO_REG 0x06 //( ,w)
/* ME8100 Register Set A */
#define ME8100_ID_REG_A 0x00 //(r, )
@@ -187,6 +190,12 @@
#ifdef __KERNEL__
+struct me8100_subinfo {
+ unsigned int regbase;
+ unsigned short ctrl_reg;
+ int num_writer;
+};
+
typedef struct{
int board_count; /* index of the board after detection */
me8100_version_enum_type version; /* sort of board */
@@ -210,12 +219,13 @@
spinlock_t use_lock; /* Guards board in use */
struct file *file_ptr; /* Pointer to file structure of path */
struct fasync_struct *fasync_ptr; /* .hs */
+ struct me8100_subinfo subinfo[2]; /* .hs */
} me8100_info_type;
#endif /* __KERNEL__ */
/* ME8100 IOCTL's */
-#define ME8100_IOCTL_MAXNR 29
+#define ME8100_IOCTL_MAXNR 30
#define ME8100_MAGIC 'o'
#define ME8100_READ_ID_A _IOR(ME8100_MAGIC, 0, unsigned short)
#define ME8100_WRITE_CTRL_A _IOW(ME8100_MAGIC, 1, unsigned short)
@@ -251,3 +261,8 @@
#define ME8100_GET_BOARD_INFO _IOR(ME8100_MAGIC, 28, me8100_info_type)
#define ME8100_GET_INT_COUNT _IOR(ME8100_MAGIC, 29, me8100_int_occur_type)
+#define ME8100_WRITE_DO _IOR(ME8100_MAGIC, 30, unsigned short)
+
+/*
+ * vim:sts=2 sw=2 aw ai sm:
+ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/me8100_test_counter/.cvsignore Wed Jan 16 20:29:27 2002 +0100
@@ -0,0 +1,1 @@
+me8100_test_counter
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/me8100_test_dio/.cvsignore Wed Jan 16 20:29:27 2002 +0100
@@ -0,0 +1,3 @@
+me8100_test_dio
+wtest
+rtest
--- a/me8100_test_dio/Makefile Wed Jan 16 15:54:58 2002 +0100
+++ b/me8100_test_dio/Makefile Wed Jan 16 20:29:27 2002 +0100
@@ -9,7 +9,7 @@
CPPFLAGS += -I.. -I$(KERNEL_DIR)/include
CFLAGS += -Wall -O
-bin_PROGRAMS = me8100_test_dio wtest rtest
+bin_PROGRAMS = me8100_test_dio wtest rtest
.PHONY: all clean
--- a/me8100_test_dio/rtest.c Wed Jan 16 15:54:58 2002 +0100
+++ b/me8100_test_dio/rtest.c Wed Jan 16 20:29:27 2002 +0100
@@ -30,7 +30,7 @@
unsigned short value_a;
printf("Read test, PID: %d\n", getpid());
- file_handle = open("/dev/me8100_0", O_RDWR, 0);
+ file_handle = open("/dev/me8100_0", O_RDONLY, 0);
if(file_handle < 0){
printf("Cannot open path !\n");
--- a/me8100_test_dio/wtest.c Wed Jan 16 15:54:58 2002 +0100
+++ b/me8100_test_dio/wtest.c Wed Jan 16 20:29:27 2002 +0100
@@ -23,35 +23,43 @@
#include "me8100.h"
+#define USE_WRITE
+
int main(void){
int err = 0;
static int file_handle = -1;
+#ifndef USE_WRITE
unsigned short ctrl_a;
+#endif
unsigned short value_a;
printf("Write test, PID: %d\n", getpid());
- file_handle = open("/dev/me8100_0", O_RDWR, 0);
+#ifdef USE_WRITE
+ printf("Using write()\n");
+#endif
+ file_handle = open("/dev/me8100_0", O_WRONLY, 0);
if(file_handle < 0){
printf("Cannot open path !\n");
return 1;
}
- /* Write.
- * HACK: The driver itself should remember the status
- * of the IRQ bits in its control register
- */
- ctrl_a = ME8100_CTL_ENIO | ME8100_CTL_SOURCE | ME8100_CTL_IRQ_MASK;
+#ifndef USE_WRITE
+ ctrl_a = ME8100_CTRL_ENIO | ME8100_CTRL_SOURCE | ME8100_CTRL_IRQ_MASK;
err = ioctl(file_handle, ME8100_WRITE_CTRL_A, &ctrl_a);
if (err) {
fprintf(stderr, "Can't setup output to port A\n");
return 1;
}
+#endif
- value_a = 0x00;
- for (value_a = 0x00; value_a < 0xffff; ++value_a) {
+ for (value_a = 0x01; value_a < 0xffff; ++value_a) {
+#ifndef USE_WRITE
ioctl(file_handle, ME8100_WRITE_DO_A, &value_a);
+#else
+ write(file_handle, &value_a, sizeof(value_a));
+#endif
printf("Wrote %04x\n", value_a);
sleep(3);
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/me8100_test_int/.cvsignore Wed Jan 16 20:29:27 2002 +0100
@@ -0,0 +1,2 @@
+me8100_test_int
+test
--- a/me8100_test_int/test.c Wed Jan 16 15:54:58 2002 +0100
+++ b/me8100_test_int/test.c Wed Jan 16 20:29:27 2002 +0100
@@ -96,7 +96,7 @@
}
/* Enable interrupt signalling by bit mask for port a */
- ctrl_a = ME8100_CTL_ENIO | ME8100_CTL_SOURCE | ME8100_CTL_IRQ_MASK;
+ ctrl_a = ME8100_CTRL_ENIO | ME8100_CTRL_SOURCE | ME8100_CTRL_IRQ_MASK;
err = ioctl(file_handle, ME8100_WRITE_CTRL_A, &ctrl_a);
if(err){
printf("Cannot write ctrl a\n");