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

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

diff -u --recursive --new-file v2.3.39/linux/drivers/ieee1394/highlevel.c linux/drivers/ieee1394/highlevel.c
@@ -0,0 +1,402 @@
+/*
+ * IEEE 1394 for Linux
+ *
+ * Copyright (C) 1999 Andreas E. Bombe
+ */
+
+#include <linux/config.h>
+#include <linux/slab.h>
+
+#include "ieee1394.h"
+#include "ieee1394_types.h"
+#include "hosts.h"
+#include "ieee1394_core.h"
+#include "highlevel.h"
+
+
+LIST_HEAD(hl_drivers);
+rwlock_t hl_drivers_lock = RW_LOCK_UNLOCKED;
+
+LIST_HEAD(addr_space);
+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)
+{
+        struct hpsb_highlevel *hl;
+
+        hl = (struct hpsb_highlevel *)kmalloc(sizeof(struct hpsb_highlevel),
+                                              GFP_KERNEL);
+        if (hl == NULL) {
+                return NULL;
+        }
+
+        INIT_LIST_HEAD(&hl->hl_list);
+        INIT_LIST_HEAD(&hl->addr_list);
+        hl->name = name;
+        hl->op = ops;
+
+        write_lock_irq(&hl_drivers_lock);
+        hl_all_hosts(hl, 1);
+        list_add_tail(&hl->hl_list, &hl_drivers);
+        write_unlock_irq(&hl_drivers_lock);
+
+        return hl;
+}
+
+void hpsb_unregister_highlevel(struct hpsb_highlevel *hl)
+{
+        struct list_head *entry;
+        struct hpsb_address_serve *as;
+
+        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);
+                list_del(&as->as_list);
+                entry = entry->next;
+                kfree(as);
+        }
+        write_unlock_irq(&addr_space_lock);
+
+        write_lock_irq(&hl_drivers_lock);
+        list_del(&hl->hl_list);
+        hl_all_hosts(hl, 0);
+        write_unlock_irq(&hl_drivers_lock);
+
+        kfree(hl);
+}
+
+int hpsb_register_addrspace(struct hpsb_highlevel *hl,
+                            struct hpsb_address_ops *ops, u64 start, u64 end)
+{
+        struct hpsb_address_serve *as;
+        struct list_head *entry;
+        int retval = 0;
+
+        if (((start|end) & 3) || (start >= end) || (end > 0x1000000000000ULL)) {
+                HPSB_ERR(__FUNCTION__ " called with invalid addresses");
+                return 0;
+        }
+
+        as = (struct hpsb_address_serve *)
+                kmalloc(sizeof(struct hpsb_address_serve), GFP_KERNEL);
+        if (as == NULL) {
+                return 0;
+        }
+
+        INIT_LIST_HEAD(&as->as_list);
+        INIT_LIST_HEAD(&as->addr_list);
+        as->op = ops;
+        as->start = start;
+        as->end = end;
+
+        write_lock_irq(&addr_space_lock);
+        entry = addr_space.next;
+
+        while (list_entry(entry, struct hpsb_address_serve, as_list)->end
+               <= start) {
+                if (list_entry(entry->next, struct hpsb_address_serve, as_list)
+                    ->start >= end) {
+                        list_add(&as->as_list, entry);
+                        list_add_tail(&as->addr_list, &hl->addr_list);
+                        retval = 1;
+                        break;
+                }
+                entry = entry->next;
+        }
+        write_unlock_irq(&addr_space_lock);
+
+        if (retval == 0) {
+                kfree(as);
+        }
+
+        return retval;
+}
+
+
+void hpsb_listen_channel(struct hpsb_highlevel *hl, struct hpsb_host *host,
+                         unsigned int channel)
+{
+        if (channel > 63) {
+                HPSB_ERR(__FUNCTION__ " called with invalid channel");
+                return;
+        }
+
+        if (host->iso_listen_count[channel]++ == 0) {
+                host->template->devctl(host, ISO_LISTEN_CHANNEL, channel);
+        }
+}
+
+void hpsb_unlisten_channel(struct hpsb_highlevel *hl, struct hpsb_host *host, 
+                           unsigned int channel)
+{
+        if (channel > 63) {
+                HPSB_ERR(__FUNCTION__ " called with invalid channel");
+                return;
+        }
+
+        if (--host->iso_listen_count[channel] == 0) {
+                host->template->devctl(host, ISO_UNLISTEN_CHANNEL, channel);
+        }
+}
+
+
+#define DEFINE_MULTIPLEXER(Function) \
+void highlevel_##Function(struct hpsb_host *host) \
+{ \
+        struct list_head *entry; \
+        void (*funcptr)(struct hpsb_host*); \
+        read_lock(&hl_drivers_lock); \
+        entry = hl_drivers.next; \
+        while (entry != &hl_drivers) { \
+                funcptr = list_entry(entry, struct hpsb_highlevel, hl_list) \
+                          ->op->Function; \
+                if (funcptr) funcptr(host); \
+                entry = entry->next; \
+        } \
+        read_unlock(&hl_drivers_lock); \
+}
+
+DEFINE_MULTIPLEXER(add_host)
+DEFINE_MULTIPLEXER(remove_host)
+DEFINE_MULTIPLEXER(host_reset)
+#undef DEFINE_MULTIPLEXER
+
+void highlevel_iso_receive(struct hpsb_host *host, quadlet_t *data,
+                           unsigned int length)
+{
+        struct list_head *entry;
+        struct hpsb_highlevel *hl;
+        int channel = (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);
+                }
+                entry = entry->next;
+        }
+        read_unlock(&hl_drivers_lock);
+}
+
+
+int highlevel_read(struct hpsb_host *host, quadlet_t *buffer, u64 addr,
+                   unsigned int length)
+{
+        struct hpsb_address_serve *as;
+        struct list_head *entry;
+        unsigned int partlength;
+        int rcode = RCODE_ADDRESS_ERROR;
+
+        if ((addr | length) & 0x3) {
+                /* Addresses or lengths not a multiple of a quadlet pose a big
+                 * problem on little endian machines because we always do this
+                 * in arch endian and swapping would mess it all up.  So we 
+                 * simply don't allow this at all. */
+                return RCODE_TYPE_ERROR;
+        }
+
+        read_lock(&addr_space_lock);
+
+        entry = addr_space.next;
+        as = list_entry(entry, struct hpsb_address_serve, as_list);
+
+        while (as->start <= addr) {
+                if (as->end > addr) {
+                        partlength = MIN((unsigned int)(as->end - addr),
+                                         length);
+
+                        if (as->op->read != NULL) {
+                                rcode = as->op->read(host, buffer, addr,
+                                                     partlength);
+                        } else {
+                                rcode = RCODE_TYPE_ERROR;
+                        }
+
+                        length -= partlength;
+                        addr += partlength;
+
+                        if ((rcode != RCODE_COMPLETE) || !length) {
+                                break;
+                        }
+                }
+
+                entry = entry->next;
+                as = list_entry(entry, struct hpsb_address_serve, as_list);
+        }
+
+        read_unlock(&addr_space_lock);
+
+        if (length && (rcode == RCODE_COMPLETE)) {
+                rcode = RCODE_ADDRESS_ERROR;
+        }
+
+        return rcode;
+}
+
+int highlevel_write(struct hpsb_host *host, quadlet_t *data, u64 addr,
+                    unsigned int length)
+{
+        struct hpsb_address_serve *as;
+        struct list_head *entry;
+        unsigned int partlength;
+        int rcode = RCODE_ADDRESS_ERROR;
+
+        if ((addr | length) & 0x3) {
+                return RCODE_TYPE_ERROR;
+        }
+
+        read_lock(&addr_space_lock);
+
+        entry = addr_space.next;
+        as = list_entry(entry, struct hpsb_address_serve, as_list);
+
+        while (as->start <= addr) {
+                if (as->end > addr) {
+                        partlength = MIN((unsigned int)(as->end - addr),
+                                         length);
+
+                        if (as->op->write != NULL) {
+                                rcode = as->op->write(host, data, addr,
+                                                      partlength);
+                        } else {
+                                rcode = RCODE_TYPE_ERROR;
+                        }
+
+                        length -= partlength;
+                        addr += partlength;
+
+                        if ((rcode != RCODE_COMPLETE) || !length) {
+                                break;
+                        }
+                }
+
+                entry = entry->next;
+                as = list_entry(entry, struct hpsb_address_serve, as_list);
+        }
+
+        read_unlock(&addr_space_lock);
+
+        if (length && (rcode == RCODE_COMPLETE)) {
+                rcode = RCODE_ADDRESS_ERROR;
+        }
+
+        return rcode;
+}
+
+
+int highlevel_lock(struct hpsb_host *host, quadlet_t *store, u64 addr,
+                   quadlet_t data, quadlet_t arg, int ext_tcode)
+{
+        struct hpsb_address_serve *as;
+        struct list_head *entry;
+        int rcode = RCODE_ADDRESS_ERROR;
+
+        read_lock(&addr_space_lock);
+
+        entry = addr_space.next;
+        as = list_entry(entry, struct hpsb_address_serve, as_list);
+
+        while (as->start <= addr) {
+                if (as->end > addr) {
+                        if (as->op->lock != NULL) {
+                                rcode = as->op->lock(host, store, addr, data, 
+                                                     arg, ext_tcode);
+                        } else {
+                                rcode = RCODE_TYPE_ERROR;
+                        }
+
+                        break;
+                }
+
+                entry = entry->next;
+                as = list_entry(entry, struct hpsb_address_serve, as_list);
+        }
+
+        read_unlock(&addr_space_lock);
+
+        return rcode;
+}
+
+int highlevel_lock64(struct hpsb_host *host, octlet_t *store, u64 addr,
+                     octlet_t data, octlet_t arg, int ext_tcode)
+{
+        struct hpsb_address_serve *as;
+        struct list_head *entry;
+        int rcode = RCODE_ADDRESS_ERROR;
+
+        read_lock(&addr_space_lock);
+
+        entry = addr_space.next;
+        as = list_entry(entry, struct hpsb_address_serve, as_list);
+
+        while (as->start <= addr) {
+                if (as->end > addr) {
+                        if (as->op->lock64 != NULL) {
+                                rcode = as->op->lock64(host, store, addr, data, 
+                                                       arg, ext_tcode);
+                        } else {
+                                rcode = RCODE_TYPE_ERROR;
+                        }
+
+                        break;
+                }
+
+                entry = entry->next;
+                as = list_entry(entry, struct hpsb_address_serve, as_list);
+        }
+
+        read_unlock(&addr_space_lock);
+
+        return rcode;
+}
+
+
+
+#ifndef MODULE
+
+void register_builtin_highlevels(void)
+{
+#ifdef CONFIG_IEEE1394_RAWIO
+        {
+                int init_raw1394(void);
+                init_raw1394();
+        }
+#endif
+}
+
+#endif /* !MODULE */
+
+
+void init_hpsb_highlevel(void)
+{
+        INIT_LIST_HEAD(&dummy_zero_addr.as_list);
+        INIT_LIST_HEAD(&dummy_zero_addr.addr_list);
+        INIT_LIST_HEAD(&dummy_max_addr.as_list);
+        INIT_LIST_HEAD(&dummy_max_addr.addr_list);
+
+        dummy_zero_addr.op = dummy_max_addr.op = &dummy_ops;
+
+        dummy_zero_addr.start = dummy_zero_addr.end = 0;
+        dummy_max_addr.start = dummy_max_addr.end = ((u64) 1) << 48;
+
+        list_add_tail(&dummy_zero_addr.as_list, &addr_space);
+        list_add_tail(&dummy_max_addr.as_list, &addr_space);
+
+#ifndef MODULE
+        register_builtin_highlevels();
+#endif
+}

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