me8100.c
changeset 4 a90b94337569
parent 3 fc24e3b47731
child 7 ee662c2e14f9
--- 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: 
  */