summaryrefslogtreecommitdiffstats
path: root/drivers/ieee1394/hosts.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/ieee1394/hosts.c')
-rw-r--r--drivers/ieee1394/hosts.c344
1 files changed, 344 insertions, 0 deletions
diff --git a/drivers/ieee1394/hosts.c b/drivers/ieee1394/hosts.c
new file mode 100644
index 000000000..1ac6a9595
--- /dev/null
+++ b/drivers/ieee1394/hosts.c
@@ -0,0 +1,344 @@
+/*
+ * IEEE 1394 for Linux
+ *
+ * Low level (host adapter) management.
+ *
+ * Copyright (C) 1999 Andreas E. Bombe
+ * Copyright (C) 1999 Emanuel Pirker
+ */
+
+#include <linux/config.h>
+
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/vmalloc.h>
+#include <linux/wait.h>
+
+#include "ieee1394_types.h"
+#include "hosts.h"
+#include "ieee1394_core.h"
+#include "highlevel.h"
+
+
+static struct hpsb_host_template *templates = NULL;
+spinlock_t templates_lock = SPIN_LOCK_UNLOCKED;
+
+
+/*
+ * The following function is exported for module usage. It will
+ * be called from high-level drivers such as the raw driver.
+ */
+int hpsb_get_host_list(struct hpsb_host *list[], int list_size)
+{
+ struct hpsb_host *host, **ptr;
+ struct hpsb_host_template *tmpl;
+ int count=0;
+
+ ptr = list;
+
+ for (tmpl = templates ; tmpl != NULL; tmpl = tmpl->next) {
+ for (host = tmpl->hosts; (host != NULL) && (count < list_size);
+ host = host->next) {
+ *ptr = host;
+ ptr++;
+ count++;
+ }
+ }
+
+ return count;
+}
+
+/*
+ * This function calls the add_host/remove_host hooks for every host currently
+ * registered. Init == TRUE means add_host.
+ */
+void hl_all_hosts(struct hpsb_highlevel *hl, int init)
+{
+ struct hpsb_host_template *tmpl;
+ struct hpsb_host *host;
+
+ spin_lock(&templates_lock);
+
+ for (tmpl = templates; tmpl != NULL; tmpl = tmpl->next) {
+ for (host = tmpl->hosts; host != NULL; host = host->next) {
+ if (host->initialized) {
+ if (init) {
+ if (hl->op->add_host) {
+ hl->op->add_host(host);
+ }
+ } else {
+ if (hl->op->remove_host) {
+ hl->op->remove_host(host);
+ }
+ }
+ }
+ }
+ }
+
+ spin_unlock(&templates_lock);
+}
+
+int hpsb_inc_host_usage(struct hpsb_host *host)
+{
+ struct hpsb_host_template *tmpl;
+ struct hpsb_host *h;
+ int retval = 0;
+
+ spin_lock(&templates_lock);
+
+ for (tmpl = templates; (tmpl != NULL) && !retval; tmpl = tmpl->next) {
+ for (h = tmpl->hosts; h != NULL; h = h->next) {
+ if (h == host) {
+ tmpl->devctl(h, MODIFY_USAGE, 1);
+ retval = 1;
+ break;
+ }
+ }
+ }
+
+ spin_unlock(&templates_lock);
+
+ return retval;
+}
+
+void hpsb_dec_host_usage(struct hpsb_host *host)
+{
+ host->template->devctl(host, MODIFY_USAGE, 0);
+}
+
+/*
+ * The following function is exported for module usage. It will be called from
+ * the detect function of a adapter driver.
+ */
+struct hpsb_host *hpsb_get_host(struct hpsb_host_template *tmpl,
+ size_t hd_size)
+{
+ struct hpsb_host *h;
+
+ h = vmalloc(sizeof(struct hpsb_host) + hd_size);
+ if (h == NULL) {
+ return NULL;
+ }
+
+ memset(h, 0, sizeof(struct hpsb_host) + hd_size);
+ h->tlabel_count = 64;
+ INIT_LIST_HEAD(&h->pending_packets);
+ spin_lock_init(&h->pending_pkt_lock);
+ spin_lock_init(&h->tlabel_lock);
+ init_waitqueue_head(&h->tlabel_wait);
+
+ h->timeout_tq.routine = (void (*)(void*))abort_timedouts;
+ h->timeout_tq.data = h;
+
+ h->topology_map = h->csr.topology_map + 3;
+ h->speed_map = h->csr.speed_map + 2;
+
+ h->template = tmpl;
+ if (hd_size) {
+ h->hostdata = &h->embedded_hostdata[0];
+ }
+
+ if (tmpl->hosts == NULL) {
+ tmpl->hosts = h;
+ } else {
+ struct hpsb_host *last = tmpl->hosts;
+
+ while (last->next != NULL) {
+ last = last->next;
+ }
+ last->next = h;
+ }
+
+ return h;
+}
+
+static void free_all_hosts(struct hpsb_host_template *tmpl)
+{
+ struct hpsb_host *next, *host = tmpl->hosts;
+
+ while (host) {
+ next = host->next;
+ vfree(host);
+ host = next;
+ }
+}
+
+
+static void init_hosts(struct hpsb_host_template *tmpl)
+{
+ int count;
+ struct hpsb_host *host;
+
+ count = tmpl->detect_hosts(tmpl);
+
+ for (host = tmpl->hosts; host != NULL; host = host->next) {
+ if (tmpl->initialize_host(host)) {
+ host->initialized = 1;
+
+ highlevel_add_host(host);
+ reset_host_bus(host);
+
+ //kernel_thread(hpsb_host_thread, host,
+ // CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
+ }
+ }
+
+ tmpl->number_of_hosts = count;
+ HPSB_INFO("detected %d %s adapter%c", count, tmpl->name,
+ (count != 1 ? 's' : ' '));
+}
+
+static void shutdown_hosts(struct hpsb_host_template *tmpl)
+{
+ struct hpsb_host *host;
+
+ for (host = tmpl->hosts; host != NULL; host = host->next) {
+ if (host->initialized) {
+ host->initialized = 0;
+ abort_requests(host);
+
+ highlevel_remove_host(host);
+
+ tmpl->release_host(host);
+ while (test_bit(0, &host->timeout_tq.sync)) {
+ schedule();
+ }
+ }
+ }
+ free_all_hosts(tmpl);
+ tmpl->release_host(NULL);
+
+ tmpl->number_of_hosts = 0;
+}
+
+
+static int add_template(struct hpsb_host_template *new)
+{
+ new->next = NULL;
+ new->hosts = NULL;
+ new->number_of_hosts = 0;
+
+ spin_lock(&templates_lock);
+ if (templates == NULL) {
+ templates = new;
+ } else {
+ struct hpsb_host_template *last = templates;
+ while (last->next != NULL) {
+ last = last->next;
+ }
+ last->next = new;
+ }
+ spin_unlock(&templates_lock);
+
+ return 0;
+}
+
+static int remove_template(struct hpsb_host_template *tmpl)
+{
+ int retval = 0;
+
+ if (tmpl->number_of_hosts) {
+ HPSB_ERR("attempted to remove busy host template "
+ "of %s at address 0x%p", tmpl->name, tmpl);
+ return 1;
+ }
+
+ spin_lock(&templates_lock);
+ if (templates == tmpl) {
+ templates = tmpl->next;
+ } else {
+ struct hpsb_host_template *t;
+
+ t = templates;
+ while (t->next != tmpl && t->next != NULL) {
+ t = t->next;
+ }
+
+ if (t->next == NULL) {
+ HPSB_ERR("attempted to remove unregistered host template "
+ "of %s at address 0x%p", tmpl->name, tmpl);
+ retval = -1;
+ } else {
+ t->next = tmpl->next;
+ }
+ }
+ spin_unlock(&templates_lock);
+
+ inc_hpsb_generation();
+ return retval;
+}
+
+
+/*
+ * The following two functions are exported symbols for module usage.
+ */
+int hpsb_register_lowlevel(struct hpsb_host_template *tmpl)
+{
+ add_template(tmpl);
+ HPSB_INFO("registered %s driver, initializing now", tmpl->name);
+ init_hosts(tmpl);
+
+ return 0;
+}
+
+void hpsb_unregister_lowlevel(struct hpsb_host_template *tmpl)
+{
+ shutdown_hosts(tmpl);
+
+ if (remove_template(tmpl)) {
+ HPSB_PANIC("remove_template failed on %s", tmpl->name);
+ }
+}
+
+
+
+#ifndef MODULE
+
+/*
+ * This is the init function for builtin lowlevel drivers. To add new drivers
+ * put their setup code (get and register template) here. Module only
+ * drivers don't need to touch this.
+ */
+
+#define SETUP_TEMPLATE(name, visname) \
+do { \
+ extern struct hpsb_host_template *get_ ## name ## _template(void); \
+ t = get_ ## name ## _template(); \
+ \
+ if (t != NULL) { \
+ if(!hpsb_register_lowlevel(t)) { \
+ count++; \
+ } \
+ } else { \
+ HPSB_WARN(visname " driver returned no host template"); \
+ } \
+} while (0)
+
+void __init register_builtin_lowlevels()
+{
+ struct hpsb_host_template *t;
+ int count = 0;
+
+ /* Touch t to avoid warning if no drivers are configured to
+ * be built directly into the kernel. */
+ t = NULL;
+
+#ifdef CONFIG_IEEE1394_PCILYNX
+ SETUP_TEMPLATE(lynx, "Lynx");
+#endif
+
+#ifdef CONFIG_IEEE1394_AIC5800
+ SETUP_TEMPLATE(aic, "AIC-5800");
+#endif
+
+#ifdef CONFIG_IEEE1394_OHCI1394
+ SETUP_TEMPLATE(ohci, "OHCI-1394");
+#endif
+
+ HPSB_INFO("%d host adapter%s initialized", count,
+ (count != 1 ? "s" : ""));
+}
+
+#undef SETUP_TEMPLATE
+
+#endif /* !MODULE */