diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2000-06-19 22:45:37 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2000-06-19 22:45:37 +0000 |
commit | 6d403070f28cd44860fdb3a53be5da0275c65cf4 (patch) | |
tree | 0d0e7fe7b5fb7568d19e11d7d862b77a866ce081 /drivers/ieee1394/guid.c | |
parent | ecf1bf5f6c2e668d03b0a9fb026db7aa41e292e1 (diff) |
Merge with 2.4.0-test1-ac21 + pile of MIPS cleanups to make merging
possible. Chainsawed RM200 kernel to compile again. Jazz machine
status unknown.
Diffstat (limited to 'drivers/ieee1394/guid.c')
-rw-r--r-- | drivers/ieee1394/guid.c | 226 |
1 files changed, 226 insertions, 0 deletions
diff --git a/drivers/ieee1394/guid.c b/drivers/ieee1394/guid.c new file mode 100644 index 000000000..36ac332c5 --- /dev/null +++ b/drivers/ieee1394/guid.c @@ -0,0 +1,226 @@ +/* + * IEEE 1394 for Linux + * + * GUID collection and management + * + * Copyright (C) 2000 Andreas E. Bombe + */ + +#include <linux/kernel.h> +#include <linux/list.h> +#include <linux/slab.h> +#include <asm/byteorder.h> +#include <asm/atomic.h> + +#include "ieee1394_types.h" +#include "ieee1394.h" +#include "hosts.h" +#include "ieee1394_transactions.h" +#include "highlevel.h" +#include "csr.h" + + +static atomic_t outstanding_requests; + +static LIST_HEAD(guid_list); +rwlock_t guid_lock = RW_LOCK_UNLOCKED; + +struct guid_entry { + struct list_head list; + atomic_t refcount; + + u64 guid; + + struct hpsb_host *host; + nodeid_t node_id; + + atomic_t generation; +}; + +struct guid_req { + struct hpsb_packet *pkt; + struct tq_struct tq; +}; + + +static struct guid_entry *create_guid_entry(void) +{ + struct guid_entry *ge; + unsigned long flags; + + ge = kmalloc(sizeof(struct guid_entry), SLAB_ATOMIC); + if (!ge) return NULL; + + INIT_LIST_HEAD(&ge->list); + atomic_set(&ge->refcount, 0); + ge->guid = (u64) -1; + ge->host = NULL; + ge->node_id = 0; + atomic_set(&ge->generation, -1); + + write_lock_irqsave(&guid_lock, flags); + list_add_tail(&ge->list, &guid_list); + write_unlock_irqrestore(&guid_lock, flags); + + return ge; +} + +static struct guid_entry *find_entry(u64 guid) +{ + struct list_head *lh; + struct guid_entry *ge; + + lh = guid_list.next; + while (lh != &guid_list) { + ge = list_entry(lh, struct guid_entry, list); + if (ge->guid == guid) return ge; + lh = lh->next; + } + + return NULL; +} + +static void associate_guid(struct hpsb_host *host, nodeid_t nodeid, u64 guid) +{ + struct guid_entry *ge; + unsigned long flags; + + HPSB_DEBUG("node %d on host 0x%p has GUID 0x%08x%08x", + nodeid & NODE_MASK, host, (unsigned int)(guid >> 32), + (unsigned int)(guid & 0xffffffff)); + + read_lock_irqsave(&guid_lock, flags); + ge = find_entry(guid); + read_unlock_irqrestore(&guid_lock, flags); + + if (!ge) ge = create_guid_entry(); + if (!ge) return; + + ge->host = host; + ge->node_id = nodeid; + ge->guid = guid; + + atomic_set(&ge->generation, get_hpsb_generation()); +} + +static void pkt_complete(struct guid_req *req) +{ + struct hpsb_packet *pkt = req->pkt; + int rcode = (pkt->header[1] >> 12) & 0xf; + + if (pkt->ack_code == ACK_PENDING && rcode == RCODE_COMPLETE) { + if (*(char *)pkt->data > 1) { + associate_guid(pkt->host, pkt->node_id, + ((u64)be32_to_cpu(pkt->data[3]) << 32) + | be32_to_cpu(pkt->data[4])); + } else { + HPSB_DEBUG("minimal ROM on node %d", + pkt->node_id & NODE_MASK); + } + } else { + HPSB_DEBUG("guid transaction error: ack %d, rcode %d", + pkt->ack_code, rcode); + } + + free_tlabel(pkt->host, pkt->node_id, pkt->tlabel); + free_hpsb_packet(pkt); + kfree(req); + + if (atomic_dec_and_test(&outstanding_requests)) { + /* FIXME: free unreferenced and inactive GUID entries. */ + } +} + + +static void host_reset(struct hpsb_host *host) +{ + struct guid_req *greq; + struct hpsb_packet *pkt; + struct selfid *sid = (struct selfid *)host->topology_map; + int nodecount = host->node_count; + nodeid_t nodeid = LOCAL_BUS; + + for (; nodecount; nodecount--, nodeid++, sid++) { + while (sid->extended) sid++; + if (!sid->link_active) continue; + if (nodeid == host->node_id) continue; + + greq = kmalloc(sizeof(struct guid_req), SLAB_ATOMIC); + if (!greq) { + HPSB_ERR("out of memory in GUID processing"); + return; + } + + pkt = hpsb_make_readbpacket(host, nodeid, + CSR_REGISTER_BASE + CSR_CONFIG_ROM, + 20); + if (!pkt) { + kfree(greq); + HPSB_ERR("out of memory in GUID processing"); + return; + } + + greq->tq.next = NULL; + greq->tq.sync = 0; + greq->tq.routine = (void (*)(void*))pkt_complete; + greq->tq.data = greq; + greq->pkt = pkt; + + queue_task(&greq->tq, &pkt->complete_tq); + + if (!hpsb_send_packet(pkt)) { + free_tlabel(pkt->host, pkt->node_id, pkt->tlabel); + free_hpsb_packet(pkt); + kfree(greq); + HPSB_NOTICE("failed to send packet in GUID processing"); + } + + HPSB_INFO("GUID request sent to node %d", nodeid & NODE_MASK); + atomic_inc(&outstanding_requests); + } +} + + +struct guid_entry *hpsb_guid_get_handle(u64 guid) +{ + unsigned long flags; + struct guid_entry *ge; + + read_lock_irqsave(&guid_lock, flags); + ge = find_entry(guid); + if (ge) atomic_inc(&ge->refcount); + read_unlock_irqrestore(&guid_lock, flags); + + return ge; +} + +struct hpsb_host *hpsb_guid_localhost(struct guid_entry *ge) +{ + if (atomic_read(&ge->generation) != get_hpsb_generation()) return NULL; + if (ge->node_id == ge->host->node_id) return ge->host; + return NULL; +} + +int hpsb_guid_fill_packet(struct guid_entry *ge, struct hpsb_packet *pkt) +{ + if (atomic_read(&ge->generation) != get_hpsb_generation()) return 0; + + pkt->host = ge->host; + pkt->node_id = ge->node_id; + pkt->generation = atomic_read(&ge->generation); + return 1; +} + + +static struct hpsb_highlevel_ops guid_ops = { + host_reset: host_reset, +}; + +void init_ieee1394_guid(void) +{ + atomic_set(&outstanding_requests, 0); + + if (!hpsb_register_highlevel("GUID manager", &guid_ops)) { + HPSB_ERR("out of memory during ieee1394 initialization"); + } +} |