patch-2.4.21 linux-2.4.21/drivers/ieee1394/highlevel.c

Next file: linux-2.4.21/drivers/ieee1394/highlevel.h
Previous file: linux-2.4.21/drivers/ieee1394/eth1394.h
Back to the patch index
Back to the overall index

diff -urN linux-2.4.20/drivers/ieee1394/highlevel.c linux-2.4.21/drivers/ieee1394/highlevel.c
@@ -5,10 +5,21 @@
  *
  * This code is licensed under the GPL.  See the file COPYING in the root
  * directory of the kernel sources for details.
+ *
+ *
+ * Contributions:
+ *
+ * Christian Toegel <christian.toegel@gmx.at>
+ *        unregister address space
+ *
+ * Manfred Weihs <weihs@ict.tuwien.ac.at>
+ *        unregister address space
+ *
  */
 
 #include <linux/config.h>
 #include <linux/slab.h>
+#include <linux/list.h>
 
 #include "ieee1394.h"
 #include "ieee1394_types.h"
@@ -17,69 +28,237 @@
 #include "highlevel.h"
 
 
-LIST_HEAD(hl_drivers);
-rwlock_t hl_drivers_lock = RW_LOCK_UNLOCKED;
+struct hl_host_info {
+	struct list_head list;
+	struct hpsb_host *host;
+	size_t size;
+	unsigned long key;
+	void *data;
+};
+
+
+static LIST_HEAD(hl_drivers);
+static rwlock_t hl_drivers_lock = RW_LOCK_UNLOCKED;
 
-LIST_HEAD(addr_space);
-rwlock_t addr_space_lock = RW_LOCK_UNLOCKED;
+static LIST_HEAD(addr_space);
+static rwlock_t addr_space_lock = RW_LOCK_UNLOCKED;
 
 /* addr_space list will have zero and max already included as bounds */
 static struct hpsb_address_ops dummy_ops = { NULL, NULL, NULL, NULL };
 static struct hpsb_address_serve dummy_zero_addr, dummy_max_addr;
 
-struct hpsb_highlevel *hpsb_register_highlevel(const char *name,
-                                               struct hpsb_highlevel_ops *ops)
+
+static struct hl_host_info *hl_get_hostinfo(struct hpsb_highlevel *hl,
+					      struct hpsb_host *host)
 {
-        struct hpsb_highlevel *hl;
+	struct hl_host_info *hi = NULL;
+	struct list_head *lh;
 
-        hl = (struct hpsb_highlevel *)kmalloc(sizeof(struct hpsb_highlevel),
-                                              GFP_KERNEL);
-        if (hl == NULL) {
-                return NULL;
-        }
+	if (!hl || !host)
+		return NULL;
+
+	read_lock(&hl->host_info_lock);
+	list_for_each (lh, &hl->host_info_list) {
+		hi = list_entry(lh, struct hl_host_info, list);
+		if (hi->host == host)
+			break;
+		hi = NULL;
+	}
+	read_unlock(&hl->host_info_lock);
+
+	return hi;
+}
+
+
+/* Returns a per host/driver data structure that was previously stored by
+ * hpsb_create_hostinfo. */
+void *hpsb_get_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host)
+{
+	struct hl_host_info *hi = hl_get_hostinfo(hl, host);
+
+	if (hi)
+		return hi->data;
+
+	return NULL;
+}
+
+
+/* If size is zero, then the return here is only valid for error checking */
+void *hpsb_create_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host,
+			   size_t data_size)
+{
+	struct hl_host_info *hi;
+	void *data;
+	unsigned long flags;
+
+	hi = hl_get_hostinfo(hl, host);
+	if (hi) {
+		HPSB_ERR("%s called hpsb_create_hostinfo when hostinfo already exists",
+			 hl->name);
+		return NULL;
+	}
+
+	hi = kmalloc(sizeof(*hi) + data_size, GFP_KERNEL);
+	if (!hi)
+		return NULL;
+
+	memset(hi, 0, sizeof(*hi) + data_size);
+
+	if (data_size) {
+		data = hi->data = hi + 1;
+		hi->size = data_size;
+	} else
+		data = hi;
+
+	hi->host = host;
+
+	write_lock_irqsave(&hl->host_info_lock, flags);
+	list_add_tail(&hi->list, &hl->host_info_list);
+	write_unlock_irqrestore(&hl->host_info_lock, flags);
+
+	return data;
+}
+
+
+int hpsb_set_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host,
+		      void *data)
+{
+	struct hl_host_info *hi;
+
+	hi = hl_get_hostinfo(hl, host);
+	if (hi) {
+		if (!hi->size && !hi->data) {
+			hi->data = data;
+			return 0;
+		} else
+			HPSB_ERR("%s called hpsb_set_hostinfo when hostinfo already has data",
+				 hl->name);
+	} else
+		HPSB_ERR("%s called hpsb_set_hostinfo when no hostinfo exists",
+			 hl->name);
+
+	return -EINVAL;
+}
+
+
+void hpsb_destroy_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host)
+{
+	struct hl_host_info *hi;
+
+	hi = hl_get_hostinfo(hl, host);
+	if (hi) {
+		unsigned long flags;
+		write_lock_irqsave(&hl->host_info_lock, flags);
+		list_del(&hi->list);
+		write_unlock_irqrestore(&hl->host_info_lock, flags);
+		kfree(hi);
+	}
+
+	return;
+}
+
+
+void hpsb_set_hostinfo_key(struct hpsb_highlevel *hl, struct hpsb_host *host, unsigned long key)
+{
+	struct hl_host_info *hi;
+
+	hi = hl_get_hostinfo(hl, host);
+	if (hi)
+		hi->key = key;
+
+	return;
+}
+
+
+unsigned long hpsb_get_hostinfo_key(struct hpsb_highlevel *hl, struct hpsb_host *host)
+{
+	struct hl_host_info *hi;
+
+	hi = hl_get_hostinfo(hl, host);
+	if (hi)
+		return hi->key;
+
+	return 0;
+}
+
+
+void *hpsb_get_hostinfo_bykey(struct hpsb_highlevel *hl, unsigned long key)
+{
+	struct list_head *lh;
+	struct hl_host_info *hi;
+	void *data = NULL;
+
+	if (!hl)
+		return NULL;
+
+	read_lock(&hl->host_info_lock);
+	list_for_each (lh, &hl->host_info_list) {
+		hi = list_entry(lh, struct hl_host_info, list);
+		if (hi->key == key) {
+			data = hi->data;
+			break;
+		}
+	}
+	read_unlock(&hl->host_info_lock);
+
+	return data;
+}
+
+
+void hpsb_register_highlevel(struct hpsb_highlevel *hl)
+{
+	struct list_head *lh;
+	unsigned long flags;
 
-        INIT_LIST_HEAD(&hl->hl_list);
         INIT_LIST_HEAD(&hl->addr_list);
-        hl->name = name;
-        hl->op = ops;
+	INIT_LIST_HEAD(&hl->host_info_list);
+
+	rwlock_init(&hl->host_info_lock);
 
-        write_lock_irq(&hl_drivers_lock);
+	write_lock_irqsave(&hl_drivers_lock, flags);
         list_add_tail(&hl->hl_list, &hl_drivers);
-        write_unlock_irq(&hl_drivers_lock);
+	write_unlock_irqrestore(&hl_drivers_lock, flags);
 
-        hl_all_hosts(hl->op->add_host);
+	if (hl->add_host) {
+		down(&hpsb_hosts_lock);
+		list_for_each (lh, &hpsb_hosts) {
+			struct hpsb_host *host = list_entry(lh, struct hpsb_host, host_list);
+			hl->add_host(host);
+		}
+		up(&hpsb_hosts_lock);
+	}
 
-        return hl;
+        return;
 }
 
 void hpsb_unregister_highlevel(struct hpsb_highlevel *hl)
 {
-        struct list_head *entry;
+        struct list_head *lh, *next;
         struct hpsb_address_serve *as;
+	unsigned long flags;
 
-        if (hl == NULL) {
-                return;
-        }
-
-        write_lock_irq(&addr_space_lock);
-        entry = hl->addr_list.next;
-
-        while (entry != &hl->addr_list) {
-                as = list_entry(entry, struct hpsb_address_serve, addr_list);
+	write_lock_irqsave(&addr_space_lock, flags);
+	list_for_each_safe (lh, next, &hl->addr_list) {
+                as = list_entry(lh, struct hpsb_address_serve, addr_list);
                 list_del(&as->as_list);
-                entry = entry->next;
                 kfree(as);
         }
-        write_unlock_irq(&addr_space_lock);
+	write_unlock_irqrestore(&addr_space_lock, flags);
 
-        write_lock_irq(&hl_drivers_lock);
+	write_lock_irqsave(&hl_drivers_lock, flags);
         list_del(&hl->hl_list);
-        write_unlock_irq(&hl_drivers_lock);
-
-        if (hl->op->remove_host)
-		hl_all_hosts(hl->op->remove_host);
+	write_unlock_irqrestore(&hl_drivers_lock, flags);
 
-        kfree(hl);
+        if (hl->remove_host) {
+		down(&hpsb_hosts_lock);
+		list_for_each(lh, &hpsb_hosts) {
+			struct hpsb_host *host = list_entry(lh, struct hpsb_host, host_list);
+
+			hl->remove_host(host);
+			hpsb_destroy_hostinfo(hl, host);
+		}
+		up(&hpsb_hosts_lock);
+	}
 }
 
 int hpsb_register_addrspace(struct hpsb_highlevel *hl,
@@ -88,6 +267,7 @@
         struct hpsb_address_serve *as;
         struct list_head *entry;
         int retval = 0;
+        unsigned long flags;
 
         if (((start|end) & 3) || (start >= end) || (end > 0x1000000000000ULL)) {
                 HPSB_ERR("%s called with invalid addresses", __FUNCTION__);
@@ -106,7 +286,7 @@
         as->start = start;
         as->end = end;
 
-        write_lock_irq(&addr_space_lock);
+        write_lock_irqsave(&addr_space_lock, flags);
         entry = addr_space.next;
 
         while (list_entry(entry, struct hpsb_address_serve, as_list)->end
@@ -120,7 +300,7 @@
                 }
                 entry = entry->next;
         }
-        write_unlock_irq(&addr_space_lock);
+        write_unlock_irqrestore(&addr_space_lock, flags);
 
         if (retval == 0) {
                 kfree(as);
@@ -129,18 +309,47 @@
         return retval;
 }
 
+int hpsb_unregister_addrspace(struct hpsb_highlevel *hl, u64 start)
+{
+        int retval = 0;
+        struct hpsb_address_serve *as;
+        struct list_head *entry;
+        unsigned long flags;
+
+        write_lock_irqsave(&addr_space_lock, flags);
+
+        entry = hl->addr_list.next;
+
+        while (entry != &hl->addr_list) {
+                as = list_entry(entry, struct hpsb_address_serve, addr_list);
+                entry = entry->next;
+                if (as->start == start) {
+                        list_del(&as->as_list);
+                        list_del(&as->addr_list);
+                        kfree(as);
+                        retval = 1;
+                        break;
+                }
+        }
+
+        write_unlock_irqrestore(&addr_space_lock, flags);
+
+        return retval;
+}
 
-void hpsb_listen_channel(struct hpsb_highlevel *hl, struct hpsb_host *host,
+int hpsb_listen_channel(struct hpsb_highlevel *hl, struct hpsb_host *host,
                          unsigned int channel)
 {
         if (channel > 63) {
                 HPSB_ERR("%s called with invalid channel", __FUNCTION__);
-                return;
+                return -EINVAL;
         }
 
         if (host->iso_listen_count[channel]++ == 0) {
-                host->driver->devctl(host, ISO_LISTEN_CHANNEL, channel);
+                return host->driver->devctl(host, ISO_LISTEN_CHANNEL, channel);
         }
+
+	return 0;
 }
 
 void hpsb_unlisten_channel(struct hpsb_highlevel *hl, struct hpsb_host *host, 
@@ -165,8 +374,8 @@
         read_lock(&hl_drivers_lock);
         list_for_each(entry, &hl_drivers) {
                 hl = list_entry(entry, struct hpsb_highlevel, hl_list);
-
-		hl->op->add_host(host);
+		if (hl->add_host)
+			hl->add_host(host);
         }
         read_unlock(&hl_drivers_lock);
 }
@@ -176,14 +385,16 @@
         struct list_head *entry;
         struct hpsb_highlevel *hl;
 
-        write_lock_irq(&hl_drivers_lock);
+	read_lock(&hl_drivers_lock);
 	list_for_each(entry, &hl_drivers) {
                 hl = list_entry(entry, struct hpsb_highlevel, hl_list);
 
-		if (hl->op->remove_host)
-			hl->op->remove_host(host);
+		if (hl->remove_host) {
+			hl->remove_host(host);
+			hpsb_destroy_hostinfo(hl, host);
+		}
         }
-        write_unlock_irq(&hl_drivers_lock);
+	read_unlock(&hl_drivers_lock);
 }
 
 void highlevel_host_reset(struct hpsb_host *host)
@@ -195,26 +406,26 @@
 	list_for_each(entry, &hl_drivers) {
                 hl = list_entry(entry, struct hpsb_highlevel, hl_list);
 
-                if (hl->op->host_reset)
-                        hl->op->host_reset(host);
+                if (hl->host_reset)
+                        hl->host_reset(host);
         }
 	read_unlock(&hl_drivers_lock);
 }
 
-void highlevel_iso_receive(struct hpsb_host *host, quadlet_t *data,
+void highlevel_iso_receive(struct hpsb_host *host, void *data,
                            unsigned int length)
 {
         struct list_head *entry;
         struct hpsb_highlevel *hl;
-        int channel = (data[0] >> 8) & 0x3f;
+        int channel = (((quadlet_t *)data)[0] >> 8) & 0x3f;
 
         read_lock(&hl_drivers_lock);
         entry = hl_drivers.next;
 
         while (entry != &hl_drivers) {
                 hl = list_entry(entry, struct hpsb_highlevel, hl_list);
-                if (hl->op->iso_receive) {
-                        hl->op->iso_receive(host, channel, data, length);
+                if (hl->iso_receive) {
+                        hl->iso_receive(host, channel, data, length);
                 }
                 entry = entry->next;
         }
@@ -222,19 +433,19 @@
 }
 
 void highlevel_fcp_request(struct hpsb_host *host, int nodeid, int direction,
-                           u8 *data, unsigned int length)
+                           void *data, unsigned int length)
 {
         struct list_head *entry;
         struct hpsb_highlevel *hl;
-        int cts = data[0] >> 4;
+        int cts = ((quadlet_t *)data)[0] >> 4;
 
         read_lock(&hl_drivers_lock);
         entry = hl_drivers.next;
 
         while (entry != &hl_drivers) {
                 hl = list_entry(entry, struct hpsb_highlevel, hl_list);
-                if (hl->op->fcp_request) {
-                        hl->op->fcp_request(host, nodeid, direction, cts, data,
+                if (hl->fcp_request) {
+                        hl->fcp_request(host, nodeid, direction, cts, data,
                                             length);
                 }
                 entry = entry->next;
@@ -242,8 +453,8 @@
         read_unlock(&hl_drivers_lock);
 }
 
-int highlevel_read(struct hpsb_host *host, int nodeid, quadlet_t *buffer,
-                   u64 addr, unsigned int length)
+int highlevel_read(struct hpsb_host *host, int nodeid, void *data,
+                   u64 addr, unsigned int length, u16 flags)
 {
         struct hpsb_address_serve *as;
         struct list_head *entry;
@@ -259,13 +470,14 @@
                 if (as->end > addr) {
                         partlength = min(as->end - addr, (u64) length);
 
-                        if (as->op->read != NULL) {
-                                rcode = as->op->read(host, nodeid, buffer,
-						     addr, partlength);
+                        if (as->op->read) {
+                                rcode = as->op->read(host, nodeid, data,
+						     addr, partlength, flags);
                         } else {
                                 rcode = RCODE_TYPE_ERROR;
                         }
 
+			(u8 *)data += partlength;
                         length -= partlength;
                         addr += partlength;
 
@@ -288,7 +500,7 @@
 }
 
 int highlevel_write(struct hpsb_host *host, int nodeid, int destid,
-		    quadlet_t *data, u64 addr, unsigned int length)
+		    void *data, u64 addr, unsigned int length, u16 flags)
 {
         struct hpsb_address_serve *as;
         struct list_head *entry;
@@ -304,13 +516,14 @@
                 if (as->end > addr) {
                         partlength = min(as->end - addr, (u64) length);
 
-                        if (as->op->write != NULL) {
+                        if (as->op->write) {
                                 rcode = as->op->write(host, nodeid, destid,
-						      data, addr, partlength);
+						      data, addr, partlength, flags);
                         } else {
                                 rcode = RCODE_TYPE_ERROR;
                         }
 
+			(u8 *)data += partlength;
                         length -= partlength;
                         addr += partlength;
 
@@ -334,7 +547,7 @@
 
 
 int highlevel_lock(struct hpsb_host *host, int nodeid, quadlet_t *store,
-                   u64 addr, quadlet_t data, quadlet_t arg, int ext_tcode)
+                   u64 addr, quadlet_t data, quadlet_t arg, int ext_tcode, u16 flags)
 {
         struct hpsb_address_serve *as;
         struct list_head *entry;
@@ -347,9 +560,9 @@
 
         while (as->start <= addr) {
                 if (as->end > addr) {
-                        if (as->op->lock != NULL) {
+                        if (as->op->lock) {
                                 rcode = as->op->lock(host, nodeid, store, addr,
-                                                     data, arg, ext_tcode);
+                                                     data, arg, ext_tcode, flags);
                         } else {
                                 rcode = RCODE_TYPE_ERROR;
                         }
@@ -367,7 +580,7 @@
 }
 
 int highlevel_lock64(struct hpsb_host *host, int nodeid, octlet_t *store,
-                     u64 addr, octlet_t data, octlet_t arg, int ext_tcode)
+                     u64 addr, octlet_t data, octlet_t arg, int ext_tcode, u16 flags)
 {
         struct hpsb_address_serve *as;
         struct list_head *entry;
@@ -380,10 +593,10 @@
 
         while (as->start <= addr) {
                 if (as->end > addr) {
-                        if (as->op->lock64 != NULL) {
+                        if (as->op->lock64) {
                                 rcode = as->op->lock64(host, nodeid, store,
                                                        addr, data, arg,
-                                                       ext_tcode);
+                                                       ext_tcode, flags);
                         } else {
                                 rcode = RCODE_TYPE_ERROR;
                         }

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