diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2000-02-16 01:07:24 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2000-02-16 01:07:24 +0000 |
commit | 95db6b748fc86297827fbd9c9ef174d491c9ad89 (patch) | |
tree | 27a92a942821cde1edda9a1b088718d436b3efe4 /drivers/ieee1394/highlevel.c | |
parent | 45b27b0a0652331d104c953a5b192d843fff88f8 (diff) |
Merge with Linux 2.3.40.
Diffstat (limited to 'drivers/ieee1394/highlevel.c')
-rw-r--r-- | drivers/ieee1394/highlevel.c | 402 |
1 files changed, 402 insertions, 0 deletions
diff --git a/drivers/ieee1394/highlevel.c b/drivers/ieee1394/highlevel.c new file mode 100644 index 000000000..3b4ac40f5 --- /dev/null +++ b/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 +} |