patch-2.4.19 linux-2.4.19/drivers/ieee1394/ieee1394_core.c

Next file: linux-2.4.19/drivers/ieee1394/ieee1394_core.h
Previous file: linux-2.4.19/drivers/ieee1394/ieee1394.h
Back to the patch index
Back to the overall index

diff -urN linux-2.4.18/drivers/ieee1394/ieee1394_core.c linux-2.4.19/drivers/ieee1394/ieee1394_core.c
@@ -18,9 +18,11 @@
 #include <linux/slab.h>
 #include <linux/interrupt.h>
 #include <linux/module.h>
+#include <linux/proc_fs.h>
 #include <asm/bitops.h>
 #include <asm/byteorder.h>
 #include <asm/semaphore.h>
+#include <asm/smplock.h>
 
 #include "ieee1394_types.h"
 #include "ieee1394.h"
@@ -39,6 +41,10 @@
 MODULE_PARM_DESC(disable_nodemgr, "Disable nodemgr functionality.");
 static int disable_nodemgr = 0;
 
+MODULE_PARM(disable_hotplug, "i");
+MODULE_PARM_DESC(disable_hotplug, "Disable hotplug for detected nodes.");
+static int disable_hotplug = 0;
+
 /* We are GPL, so treat us special */
 MODULE_LICENSE("GPL");
 
@@ -106,7 +112,7 @@
                 packet->data_size = data_size;
         }
 
-        INIT_TQ_HEAD(packet->complete_tq);
+        INIT_LIST_HEAD(&packet->complete_tq);
         INIT_LIST_HEAD(&packet->list);
         sema_init(&packet->state_change, 0);
         packet->state = hpsb_unused;
@@ -135,12 +141,8 @@
 
 int hpsb_reset_bus(struct hpsb_host *host, int type)
 {
-        if (!host->initialized) {
-                return 1;
-        }
-
         if (!host->in_bus_reset) {
-                host->template->devctl(host, RESET_BUS, type);
+                host->ops->devctl(host, RESET_BUS, type);
                 return 0;
         } else {
                 return 1;
@@ -151,8 +153,8 @@
 int hpsb_bus_reset(struct hpsb_host *host)
 {
         if (host->in_bus_reset) {
-                HPSB_NOTICE(__FUNCTION__ 
-                            " called while bus reset already in progress");
+                HPSB_NOTICE("%s called while bus reset already in progress",
+			    __FUNCTION__);
                 return 1;
         }
 
@@ -171,14 +173,16 @@
  * Verify num_of_selfids SelfIDs and return number of nodes.  Return zero in
  * case verification failed.
  */
-static int check_selfids(struct hpsb_host *host, unsigned int num_of_selfids)
+static int check_selfids(struct hpsb_host *host)
 {
         int nodeid = -1;
-        int rest_of_selfids = num_of_selfids;
+        int rest_of_selfids = host->selfid_count;
         struct selfid *sid = (struct selfid *)host->topology_map;
         struct ext_selfid *esid;
         int esid_seq = 23;
 
+	host->nodes_active = 0;
+
         while (rest_of_selfids--) {
                 if (!sid->extended) {
                         nodeid++;
@@ -190,9 +194,11 @@
                                 return 0;
                         }
                         
-                        if (sid->contender && sid->link_active) {
-                                host->irm_id = LOCAL_BUS | sid->phy_id;
-                        }
+			if (sid->link_active) {
+				host->nodes_active++;
+				if (sid->contender)
+					host->irm_id = LOCAL_BUS | sid->phy_id;
+			}
                 } else {
                         esid = (struct ext_selfid *)sid;
 
@@ -226,7 +232,8 @@
                         return 0;
         }
 
-        return nodeid + 1;
+	host->node_count = nodeid + 1;
+        return 1;
 }
 
 static void build_speed_map(struct hpsb_host *host, int nodecount)
@@ -318,15 +325,17 @@
 
 void hpsb_selfid_complete(struct hpsb_host *host, int phyid, int isroot)
 {
+	if (!host->in_bus_reset)
+		HPSB_NOTICE("SelfID completion called outside of bus reset!");
+
         host->node_id = LOCAL_BUS | phyid;
-        host->in_bus_reset = 0;
         host->is_root = isroot;
 
-        host->node_count = check_selfids(host, host->selfid_count);
-        if (!host->node_count) {
+        if (!check_selfids(host)) {
                 if (host->reset_retries++ < 20) {
                         /* selfid stage did not complete without error */
                         HPSB_NOTICE("Error in SelfID stage, resetting");
+			host->in_bus_reset = 0;
                         hpsb_reset_bus(host, LONG_RESET);
                         return;
                 } else {
@@ -346,8 +355,9 @@
         }
 
         host->reset_retries = 0;
-        atomic_inc(&host->generation);
-        if (isroot) host->template->devctl(host, ACT_CYCLE_MASTER, 1);
+        if (isroot) host->ops->devctl(host, ACT_CYCLE_MASTER, 1);
+	atomic_inc(&host->generation);
+	host->in_bus_reset = 0;
         highlevel_host_reset(host);
 }
 
@@ -402,7 +412,7 @@
 {
         struct hpsb_host *host = packet->host;
 
-        if (!host->initialized || host->in_bus_reset 
+        if (host->is_shutdown || host->in_bus_reset
             || (packet->generation != get_hpsb_generation(host))) {
                 return 0;
         }
@@ -431,7 +441,7 @@
         }
 #endif
 
-        return host->template->transmit_packet(host, packet);
+        return host->ops->transmit_packet(host, packet);
 }
 
 static void send_packet_nocare(struct hpsb_packet *packet)
@@ -727,7 +737,7 @@
         struct list_head *lh;
         LIST_HEAD(llist);
 
-        host->template->devctl(host, CANCEL_REQUESTS, 0);
+        host->ops->devctl(host, CANCEL_REQUESTS, 0);
 
         spin_lock_irqsave(&host->pending_pkt_lock, flags);
         list_splice(&host->pending_packets, &llist);
@@ -786,14 +796,209 @@
 }
 
 
+/*
+ * character device dispatching (see ieee1394_core.h)
+ * Dan Maas <dmaas@dcine.com>
+ */
+
+static struct {
+	struct file_operations *file_ops;
+	struct module *module;
+} ieee1394_chardevs[16];
+
+static rwlock_t ieee1394_chardevs_lock = RW_LOCK_UNLOCKED;
+
+static int ieee1394_dispatch_open(struct inode *inode, struct file *file);
+
+static struct file_operations ieee1394_chardev_ops = {
+	.owner =THIS_MODULE,
+	.open =	ieee1394_dispatch_open,
+};
+
+devfs_handle_t ieee1394_devfs_handle;
+
+
+/* claim a block of minor numbers */
+int ieee1394_register_chardev(int blocknum,
+			      struct module *module,
+			      struct file_operations *file_ops)
+{
+	int retval;
+	
+	if( (blocknum < 0) || (blocknum > 15) )
+		return -EINVAL;
+
+	write_lock(&ieee1394_chardevs_lock);
+
+	if(ieee1394_chardevs[blocknum].file_ops == NULL) {
+		/* grab the minor block */
+		ieee1394_chardevs[blocknum].file_ops = file_ops;
+		ieee1394_chardevs[blocknum].module = module;
+		
+		retval = 0;
+	} else {
+		/* block already taken */
+		retval = -EBUSY;
+	}
+	
+	write_unlock(&ieee1394_chardevs_lock);
+
+	return retval;
+}
+
+/* release a block of minor numbers */
+void ieee1394_unregister_chardev(int blocknum)
+{
+	if( (blocknum < 0) || (blocknum > 15) )
+		return;
+	
+	write_lock(&ieee1394_chardevs_lock);
+	
+	if(ieee1394_chardevs[blocknum].file_ops) {
+		ieee1394_chardevs[blocknum].file_ops = NULL;
+		ieee1394_chardevs[blocknum].module = NULL;
+	}
+	
+	write_unlock(&ieee1394_chardevs_lock);
+}
+
+/* the point of entry for open() on any ieee1394 character device */
+static int ieee1394_dispatch_open(struct inode *inode, struct file *file)
+{
+	struct file_operations *file_ops;
+	struct module *module;
+	int blocknum;
+	int retval = -ENODEV;
+
+	/*
+	  Maintaining correct module reference counts is tricky here!
+
+	  For Linux v2.4 and later:
+	  
+	  The key thing to remember is that the VFS increments the
+	  reference count of ieee1394 before it calls
+	  ieee1394_dispatch_open().
+
+	  If the open() succeeds, then we need to transfer this extra
+	  reference to the task-specific driver module (e.g. raw1394).
+	  The VFS will deref the driver module automatically when the
+	  file is later released.
+
+	  If the open() fails, then the VFS will drop the
+	  reference count of whatever module file->f_op->owner points
+	  to, immediately after this function returns.
+
+	  The comments below refer to the 2.4 case, since the 2.2
+	  case is trivial.
+	  
+	*/
+
+#define INCREF(mod_) do { struct module *mod = (struct module*) mod_; \
+                          if(mod != NULL) __MOD_INC_USE_COUNT(mod); } while(0)
+#define DECREF(mod_) do { struct module *mod = (struct module*) mod_; \
+                          if(mod != NULL) __MOD_DEC_USE_COUNT(mod); } while(0)
+	
+        /* shift away lower four bits of the minor
+	   to get the index of the ieee1394_driver
+	   we want */
+	
+	blocknum = (minor(inode->i_rdev) >> 4) & 0xF;
+
+	/* printk("ieee1394_dispatch_open(%d)", blocknum); */
+
+	read_lock(&ieee1394_chardevs_lock);
+	module = ieee1394_chardevs[blocknum].module;
+	/* bump the reference count of the driver that
+	   will receive the open() */
+	INCREF(module);
+	file_ops = ieee1394_chardevs[blocknum].file_ops;
+	read_unlock(&ieee1394_chardevs_lock);
+
+	if(file_ops == NULL) {
+		DECREF(module);
+		goto out_fail;
+	}
+
+	/* redirect all subsequent requests to the driver's
+	   own file_operations */
+	file->f_op = file_ops;
+
+	/* at this point BOTH ieee1394 and the task-specific driver have
+	   an extra reference */
+
+	/* follow through with the open() */
+	retval = file_ops->open(inode, file);
+
+	if(retval) {
+		
+		/* if the open() failed, then we need to drop the
+                   extra reference we gave to the task-specific
+                   driver */
+
+		DECREF(module);
+		goto out_fail;
+		
+	} else {
+
+		/* if the open() succeeded, then ieee1394 will be left
+		   with an extra module reference, so we discard it here.*/
+
+		DECREF(THIS_MODULE);
+
+		/* the task-specific driver still has the extra
+		   reference we gave it. This extra reference prevents
+		   the module from unloading while the file is open,
+		   and will be dropped by the VFS when the file is
+		   released. */
+		
+		return 0;
+	}
+	       
+out_fail:
+	/* point the file's f_ops back to ieee1394. The VFS will then
+	   decrement ieee1394's reference count immediately after this
+	   function returns. */
+	
+	file->f_op = &ieee1394_chardev_ops;
+	return retval;
+
+#undef INCREF
+#undef DECREF
+	     
+}
+
+struct proc_dir_entry *ieee1394_procfs_entry;
+
 static int __init ieee1394_init(void)
 {
 	hpsb_packet_cache = kmem_cache_create("hpsb_packet", sizeof(struct hpsb_packet),
 					      0, 0, NULL, NULL);
+
+	ieee1394_devfs_handle = devfs_mk_dir(NULL, "ieee1394", NULL);
+
+	if (register_chrdev(IEEE1394_MAJOR, "ieee1394", &ieee1394_chardev_ops)) {
+		HPSB_ERR("unable to register character device major %d!\n", IEEE1394_MAJOR);
+		devfs_unregister(ieee1394_devfs_handle);
+		return -ENODEV;
+	}
+
+#ifdef CONFIG_PROC_FS
+	/* Must be done before we start everything else, since the drivers
+	 * may use it.  */
+	ieee1394_procfs_entry = proc_mkdir( "ieee1394", proc_bus);
+	if (ieee1394_procfs_entry == NULL) {
+		HPSB_ERR("unable to create /proc/bus/ieee1394\n");
+		unregister_chrdev(IEEE1394_MAJOR, "ieee1394");
+		devfs_unregister(ieee1394_devfs_handle);
+		return -ENOMEM;
+	}
+	ieee1394_procfs_entry->owner = THIS_MODULE;
+#endif
+
 	init_hpsb_highlevel();
 	init_csr();
 	if (!disable_nodemgr)
-		init_ieee1394_nodemgr();
+		init_ieee1394_nodemgr(disable_hotplug);
 	else
 		HPSB_INFO("nodemgr functionality disabled");
 
@@ -807,6 +1012,13 @@
 
 	cleanup_csr();
 	kmem_cache_destroy(hpsb_packet_cache);
+
+	unregister_chrdev(IEEE1394_MAJOR, "ieee1394");
+	
+	/* it's ok to pass a NULL devfs_handle to devfs_unregister */
+	devfs_unregister(ieee1394_devfs_handle);
+	
+	remove_proc_entry("ieee1394", proc_bus);
 }
 
 module_init(ieee1394_init);
@@ -815,9 +1027,11 @@
 /* Exported symbols */
 EXPORT_SYMBOL(hpsb_register_lowlevel);
 EXPORT_SYMBOL(hpsb_unregister_lowlevel);
-EXPORT_SYMBOL(hpsb_get_host);
-EXPORT_SYMBOL(hpsb_inc_host_usage);
-EXPORT_SYMBOL(hpsb_dec_host_usage);
+EXPORT_SYMBOL(hpsb_alloc_host);
+EXPORT_SYMBOL(hpsb_add_host);
+EXPORT_SYMBOL(hpsb_remove_host);
+EXPORT_SYMBOL(hpsb_ref_host);
+EXPORT_SYMBOL(hpsb_unref_host);
 EXPORT_SYMBOL(hpsb_speedto_str);
 
 EXPORT_SYMBOL(alloc_hpsb_packet);
@@ -867,12 +1081,19 @@
 EXPORT_SYMBOL(highlevel_add_host);
 EXPORT_SYMBOL(highlevel_remove_host);
 EXPORT_SYMBOL(highlevel_host_reset);
-EXPORT_SYMBOL(highlevel_add_one_host);
 
 EXPORT_SYMBOL(hpsb_guid_get_entry);
 EXPORT_SYMBOL(hpsb_nodeid_get_entry);
-EXPORT_SYMBOL(hpsb_get_host_by_ne);
-EXPORT_SYMBOL(hpsb_guid_fill_packet);
+EXPORT_SYMBOL(hpsb_node_fill_packet);
+EXPORT_SYMBOL(hpsb_node_read);
+EXPORT_SYMBOL(hpsb_node_write);
+EXPORT_SYMBOL(hpsb_node_lock);
 EXPORT_SYMBOL(hpsb_register_protocol);
 EXPORT_SYMBOL(hpsb_unregister_protocol);
 EXPORT_SYMBOL(hpsb_release_unit_directory);
+
+EXPORT_SYMBOL(ieee1394_register_chardev);
+EXPORT_SYMBOL(ieee1394_unregister_chardev);
+EXPORT_SYMBOL(ieee1394_devfs_handle);
+
+EXPORT_SYMBOL(ieee1394_procfs_entry);

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)