summaryrefslogtreecommitdiffstats
path: root/drivers/ieee1394
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/ieee1394')
-rw-r--r--drivers/ieee1394/Kconfig164
-rw-r--r--drivers/ieee1394/Makefile18
-rw-r--r--drivers/ieee1394/config_roms.c194
-rw-r--r--drivers/ieee1394/config_roms.h19
-rw-r--r--drivers/ieee1394/csr.c843
-rw-r--r--drivers/ieee1394/csr.h99
-rw-r--r--drivers/ieee1394/csr1212.c1482
-rw-r--r--drivers/ieee1394/csr1212.h388
-rw-r--r--drivers/ieee1394/dma.c290
-rw-r--r--drivers/ieee1394/dma.h88
-rw-r--r--drivers/ieee1394/dv1394-private.h587
-rw-r--r--drivers/ieee1394/dv1394.c2596
-rw-r--r--drivers/ieee1394/dv1394.h305
-rw-r--r--drivers/ieee1394/eth1394.c1752
-rw-r--r--drivers/ieee1394/eth1394.h235
-rw-r--r--drivers/ieee1394/highlevel.c690
-rw-r--r--drivers/ieee1394/highlevel.h140
-rw-r--r--drivers/ieee1394/hosts.c249
-rw-r--r--drivers/ieee1394/hosts.h201
-rw-r--r--drivers/ieee1394/ieee1394-ioctl.h106
-rw-r--r--drivers/ieee1394/ieee1394.h219
-rw-r--r--drivers/ieee1394/ieee1394_core.c1379
-rw-r--r--drivers/ieee1394/ieee1394_core.h168
-rw-r--r--drivers/ieee1394/ieee1394_hotplug.h19
-rw-r--r--drivers/ieee1394/ieee1394_transactions.c572
-rw-r--r--drivers/ieee1394/ieee1394_transactions.h38
-rw-r--r--drivers/ieee1394/ieee1394_types.h69
-rw-r--r--drivers/ieee1394/init_ohci1394_dma.c285
-rw-r--r--drivers/ieee1394/iso.c568
-rw-r--r--drivers/ieee1394/iso.h194
-rw-r--r--drivers/ieee1394/nodemgr.c1905
-rw-r--r--drivers/ieee1394/nodemgr.h171
-rw-r--r--drivers/ieee1394/ohci1394.c3582
-rw-r--r--drivers/ieee1394/ohci1394.h453
-rw-r--r--drivers/ieee1394/pcilynx.c1555
-rw-r--r--drivers/ieee1394/pcilynx.h468
-rw-r--r--drivers/ieee1394/raw1394-private.h81
-rw-r--r--drivers/ieee1394/raw1394.c3089
-rw-r--r--drivers/ieee1394/raw1394.h191
-rw-r--r--drivers/ieee1394/sbp2.c2141
-rw-r--r--drivers/ieee1394/sbp2.h340
-rw-r--r--drivers/ieee1394/video1394.c1531
-rw-r--r--drivers/ieee1394/video1394.h67
43 files changed, 29531 insertions, 0 deletions
diff --git a/drivers/ieee1394/Kconfig b/drivers/ieee1394/Kconfig
new file mode 100644
index 0000000..95f45f9
--- /dev/null
+++ b/drivers/ieee1394/Kconfig
@@ -0,0 +1,164 @@
+menu "IEEE 1394 (FireWire) support"
+ depends on PCI || BROKEN
+
+source "drivers/firewire/Kconfig"
+
+config IEEE1394
+ tristate "Stable FireWire stack"
+ depends on PCI || BROKEN
+ help
+ IEEE 1394 describes a high performance serial bus, which is also
+ known as FireWire(tm) or i.Link(tm) and is used for connecting all
+ sorts of devices (most notably digital video cameras) to your
+ computer.
+
+ If you have FireWire hardware and want to use it, say Y here. This
+ is the core support only, you will also need to select a driver for
+ your IEEE 1394 adapter.
+
+ To compile this driver as a module, say M here: the
+ module will be called ieee1394.
+
+config IEEE1394_OHCI1394
+ tristate "OHCI-1394 controllers"
+ depends on PCI && IEEE1394
+ help
+ Enable this driver if you have an IEEE 1394 controller based on the
+ OHCI-1394 specification. The current driver is only tested with OHCI
+ chipsets made by Texas Instruments and NEC. Most third-party vendors
+ use one of these chipsets. It should work with any OHCI-1394
+ compliant card, however.
+
+ To compile this driver as a module, say M here: the
+ module will be called ohci1394.
+
+ NOTE:
+
+ You should only build either ohci1394 or the new firewire-ohci driver,
+ but not both. If you nevertheless want to install both, you should
+ configure them only as modules and blacklist the driver(s) which you
+ don't want to have auto-loaded. Add either
+
+ blacklist firewire-ohci
+ or
+ blacklist ohci1394
+ blacklist video1394
+ blacklist dv1394
+
+ to /etc/modprobe.conf or /etc/modprobe.d/* and update modprobe.conf
+ depending on your distribution. The latter two modules should be
+ blacklisted together with ohci1394 because they depend on ohci1394.
+
+ If you have an old modprobe which doesn't implement the blacklist
+ directive, use "install modulename /bin/true" for the modules to be
+ blacklisted.
+
+comment "PCILynx controller requires I2C"
+ depends on IEEE1394 && I2C=n
+
+config IEEE1394_PCILYNX
+ tristate "PCILynx controller"
+ depends on PCI && IEEE1394 && I2C
+ select I2C_ALGOBIT
+ help
+ Say Y here if you have an IEEE-1394 controller with the Texas
+ Instruments PCILynx chip. Note: this driver is written for revision
+ 2 of this chip and may not work with revision 0.
+
+ To compile this driver as a module, say M here: the
+ module will be called pcilynx.
+
+ Only some old and now very rare PCI and CardBus cards and
+ PowerMacs G3 B&W contain the PCILynx controller. Therefore
+ almost everybody can say N here.
+
+comment "SBP-2 support (for storage devices) requires SCSI"
+ depends on IEEE1394 && SCSI=n
+
+config IEEE1394_SBP2
+ tristate "Storage devices (SBP-2 protocol)"
+ depends on IEEE1394 && SCSI
+ help
+ This option enables you to use SBP-2 devices connected to an IEEE
+ 1394 bus. SBP-2 devices include storage devices like harddisks and
+ DVD drives, also some other FireWire devices like scanners.
+
+ You should also enable support for disks, CD-ROMs, etc. in the SCSI
+ configuration section.
+
+config IEEE1394_SBP2_PHYS_DMA
+ bool "Enable replacement for physical DMA in SBP2"
+ depends on IEEE1394_SBP2 && VIRT_TO_BUS && EXPERIMENTAL
+ help
+ This builds sbp2 for use with non-OHCI host adapters which do not
+ support physical DMA or for when ohci1394 is run with phys_dma=0.
+ Physical DMA is data movement without assistance of the drivers'
+ interrupt handlers. This option includes the interrupt handlers
+ that are required in absence of this hardware feature.
+
+ This option is buggy and currently broken on some architectures.
+ If unsure, say N.
+
+config IEEE1394_ETH1394_ROM_ENTRY
+ depends on IEEE1394
+ bool
+ default n
+
+config IEEE1394_ETH1394
+ tristate "IP over 1394"
+ depends on IEEE1394 && EXPERIMENTAL && INET
+ select IEEE1394_ETH1394_ROM_ENTRY
+ help
+ This driver implements a functional majority of RFC 2734: IPv4 over
+ 1394. It will provide IP connectivity with implementations of RFC
+ 2734 found on other operating systems. It will not communicate with
+ older versions of this driver found in stock kernels prior to 2.6.3.
+ This driver is still considered experimental. It does not yet support
+ MCAP, therefore multicast support is significantly limited.
+
+ The module is called eth1394 although it does not emulate Ethernet.
+
+config IEEE1394_RAWIO
+ tristate "raw1394 userspace interface"
+ depends on IEEE1394
+ help
+ This option adds support for the raw1394 device file which enables
+ direct communication of user programs with IEEE 1394 devices
+ (isochronous and asynchronous). Almost all application programs
+ which access FireWire require this option.
+
+ To compile this driver as a module, say M here: the module will be
+ called raw1394.
+
+config IEEE1394_VIDEO1394
+ tristate "video1394 userspace interface"
+ depends on IEEE1394 && IEEE1394_OHCI1394
+ help
+ This option adds support for the video1394 device files which enable
+ isochronous communication of user programs with IEEE 1394 devices,
+ especially video capture or export. This interface is used by all
+ libdc1394 based programs and by several other programs, in addition to
+ the raw1394 interface. It is generally not required for DV capture.
+
+ To compile this driver as a module, say M here: the module will be
+ called video1394.
+
+config IEEE1394_DV1394
+ tristate "dv1394 userspace interface (deprecated)"
+ depends on IEEE1394 && IEEE1394_OHCI1394
+ help
+ The dv1394 driver is unsupported and may be removed from Linux in a
+ future release. Its functionality is now provided by raw1394 together
+ with libraries such as libiec61883.
+
+config IEEE1394_VERBOSEDEBUG
+ bool "Excessive debugging output"
+ depends on IEEE1394
+ help
+ If you say Y here, you will get very verbose debugging logs from the
+ ieee1394 drivers, including sent and received packet headers. This
+ will quickly result in large amounts of data sent to the system log.
+
+ Say Y if you really need the debugging output. Everyone else says N.
+
+endmenu
diff --git a/drivers/ieee1394/Makefile b/drivers/ieee1394/Makefile
new file mode 100644
index 0000000..1f8153b
--- /dev/null
+++ b/drivers/ieee1394/Makefile
@@ -0,0 +1,18 @@
+#
+# Makefile for the Linux IEEE 1394 implementation
+#
+
+ieee1394-objs := ieee1394_core.o ieee1394_transactions.o hosts.o \
+ highlevel.o csr.o nodemgr.o dma.o iso.o \
+ csr1212.o config_roms.o
+
+obj-$(CONFIG_IEEE1394) += ieee1394.o
+obj-$(CONFIG_IEEE1394_PCILYNX) += pcilynx.o
+obj-$(CONFIG_IEEE1394_OHCI1394) += ohci1394.o
+obj-$(CONFIG_IEEE1394_VIDEO1394) += video1394.o
+obj-$(CONFIG_IEEE1394_RAWIO) += raw1394.o
+obj-$(CONFIG_IEEE1394_SBP2) += sbp2.o
+obj-$(CONFIG_IEEE1394_DV1394) += dv1394.o
+obj-$(CONFIG_IEEE1394_ETH1394) += eth1394.o
+
+obj-$(CONFIG_PROVIDE_OHCI1394_DMA_INIT) += init_ohci1394_dma.o
diff --git a/drivers/ieee1394/config_roms.c b/drivers/ieee1394/config_roms.c
new file mode 100644
index 0000000..1b98120
--- /dev/null
+++ b/drivers/ieee1394/config_roms.c
@@ -0,0 +1,194 @@
+/*
+ * IEEE 1394 for Linux
+ *
+ * ConfigROM entries
+ *
+ * Copyright (C) 2004 Ben Collins
+ *
+ * This code is licensed under the GPL. See the file COPYING in the root
+ * directory of the kernel sources for details.
+ */
+
+#include <linux/types.h>
+
+#include "csr1212.h"
+#include "ieee1394.h"
+#include "ieee1394_types.h"
+#include "hosts.h"
+#include "ieee1394_core.h"
+#include "highlevel.h"
+#include "csr.h"
+#include "config_roms.h"
+
+struct hpsb_config_rom_entry {
+ const char *name;
+
+ /* Base initialization, called at module load */
+ int (*init)(void);
+
+ /* Cleanup called at module exit */
+ void (*cleanup)(void);
+
+ /* The flag added to host->config_roms */
+ unsigned int flag;
+};
+
+/* The default host entry. This must succeed. */
+int hpsb_default_host_entry(struct hpsb_host *host)
+{
+ struct csr1212_keyval *root;
+ struct csr1212_keyval *vend_id = NULL;
+ struct csr1212_keyval *text = NULL;
+ char csr_name[128];
+ int ret;
+
+ sprintf(csr_name, "Linux - %s", host->driver->name);
+ root = host->csr.rom->root_kv;
+
+ vend_id = csr1212_new_immediate(CSR1212_KV_ID_VENDOR, host->csr.guid_hi >> 8);
+ text = csr1212_new_string_descriptor_leaf(csr_name);
+
+ if (!vend_id || !text) {
+ if (vend_id)
+ csr1212_release_keyval(vend_id);
+ if (text)
+ csr1212_release_keyval(text);
+ csr1212_destroy_csr(host->csr.rom);
+ return -ENOMEM;
+ }
+
+ csr1212_associate_keyval(vend_id, text);
+ csr1212_release_keyval(text);
+ ret = csr1212_attach_keyval_to_directory(root, vend_id);
+ csr1212_release_keyval(vend_id);
+ if (ret != CSR1212_SUCCESS) {
+ csr1212_destroy_csr(host->csr.rom);
+ return -ENOMEM;
+ }
+
+ host->update_config_rom = 1;
+
+ return 0;
+}
+
+
+#ifdef CONFIG_IEEE1394_ETH1394_ROM_ENTRY
+#include "eth1394.h"
+
+static struct csr1212_keyval *ip1394_ud;
+
+static int config_rom_ip1394_init(void)
+{
+ struct csr1212_keyval *spec_id = NULL;
+ struct csr1212_keyval *spec_desc = NULL;
+ struct csr1212_keyval *ver = NULL;
+ struct csr1212_keyval *ver_desc = NULL;
+ int ret = -ENOMEM;
+
+ ip1394_ud = csr1212_new_directory(CSR1212_KV_ID_UNIT);
+
+ spec_id = csr1212_new_immediate(CSR1212_KV_ID_SPECIFIER_ID,
+ ETHER1394_GASP_SPECIFIER_ID);
+ spec_desc = csr1212_new_string_descriptor_leaf("IANA");
+ ver = csr1212_new_immediate(CSR1212_KV_ID_VERSION,
+ ETHER1394_GASP_VERSION);
+ ver_desc = csr1212_new_string_descriptor_leaf("IPv4");
+
+ if (!ip1394_ud || !spec_id || !spec_desc || !ver || !ver_desc)
+ goto ip1394_fail;
+
+ csr1212_associate_keyval(spec_id, spec_desc);
+ csr1212_associate_keyval(ver, ver_desc);
+ if (csr1212_attach_keyval_to_directory(ip1394_ud, spec_id)
+ == CSR1212_SUCCESS &&
+ csr1212_attach_keyval_to_directory(ip1394_ud, ver)
+ == CSR1212_SUCCESS)
+ ret = 0;
+
+ip1394_fail:
+ if (ret && ip1394_ud) {
+ csr1212_release_keyval(ip1394_ud);
+ ip1394_ud = NULL;
+ }
+
+ if (spec_id)
+ csr1212_release_keyval(spec_id);
+ if (spec_desc)
+ csr1212_release_keyval(spec_desc);
+ if (ver)
+ csr1212_release_keyval(ver);
+ if (ver_desc)
+ csr1212_release_keyval(ver_desc);
+
+ return ret;
+}
+
+static void config_rom_ip1394_cleanup(void)
+{
+ if (ip1394_ud) {
+ csr1212_release_keyval(ip1394_ud);
+ ip1394_ud = NULL;
+ }
+}
+
+int hpsb_config_rom_ip1394_add(struct hpsb_host *host)
+{
+ if (!ip1394_ud)
+ return -ENODEV;
+
+ if (csr1212_attach_keyval_to_directory(host->csr.rom->root_kv,
+ ip1394_ud) != CSR1212_SUCCESS)
+ return -ENOMEM;
+
+ host->config_roms |= HPSB_CONFIG_ROM_ENTRY_IP1394;
+ host->update_config_rom = 1;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(hpsb_config_rom_ip1394_add);
+
+void hpsb_config_rom_ip1394_remove(struct hpsb_host *host)
+{
+ csr1212_detach_keyval_from_directory(host->csr.rom->root_kv, ip1394_ud);
+ host->config_roms &= ~HPSB_CONFIG_ROM_ENTRY_IP1394;
+ host->update_config_rom = 1;
+}
+EXPORT_SYMBOL_GPL(hpsb_config_rom_ip1394_remove);
+
+static struct hpsb_config_rom_entry ip1394_entry = {
+ .name = "ip1394",
+ .init = config_rom_ip1394_init,
+ .cleanup = config_rom_ip1394_cleanup,
+ .flag = HPSB_CONFIG_ROM_ENTRY_IP1394,
+};
+
+#endif /* CONFIG_IEEE1394_ETH1394_ROM_ENTRY */
+
+static struct hpsb_config_rom_entry *const config_rom_entries[] = {
+#ifdef CONFIG_IEEE1394_ETH1394_ROM_ENTRY
+ &ip1394_entry,
+#endif
+};
+
+/* Initialize all config roms */
+int hpsb_init_config_roms(void)
+{
+ int i, error = 0;
+
+ for (i = 0; i < ARRAY_SIZE(config_rom_entries); i++)
+ if (config_rom_entries[i]->init()) {
+ HPSB_ERR("Failed to initialize config rom entry `%s'",
+ config_rom_entries[i]->name);
+ error = -1;
+ }
+
+ return error;
+}
+
+/* Cleanup all config roms */
+void hpsb_cleanup_config_roms(void)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(config_rom_entries); i++)
+ config_rom_entries[i]->cleanup();
+}
diff --git a/drivers/ieee1394/config_roms.h b/drivers/ieee1394/config_roms.h
new file mode 100644
index 0000000..1f5cd1f
--- /dev/null
+++ b/drivers/ieee1394/config_roms.h
@@ -0,0 +1,19 @@
+#ifndef _IEEE1394_CONFIG_ROMS_H
+#define _IEEE1394_CONFIG_ROMS_H
+
+struct hpsb_host;
+
+int hpsb_default_host_entry(struct hpsb_host *host);
+int hpsb_init_config_roms(void);
+void hpsb_cleanup_config_roms(void);
+
+/* List of flags to check if a host contains a certain extra config rom
+ * entry. Available in the host->config_roms member. */
+#define HPSB_CONFIG_ROM_ENTRY_IP1394 0x00000001
+
+#ifdef CONFIG_IEEE1394_ETH1394_ROM_ENTRY
+int hpsb_config_rom_ip1394_add(struct hpsb_host *host);
+void hpsb_config_rom_ip1394_remove(struct hpsb_host *host);
+#endif
+
+#endif /* _IEEE1394_CONFIG_ROMS_H */
diff --git a/drivers/ieee1394/csr.c b/drivers/ieee1394/csr.c
new file mode 100644
index 0000000..c90be40
--- /dev/null
+++ b/drivers/ieee1394/csr.c
@@ -0,0 +1,843 @@
+/*
+ * IEEE 1394 for Linux
+ *
+ * CSR implementation, iso/bus manager implementation.
+ *
+ * Copyright (C) 1999 Andreas E. Bombe
+ * 2002 Manfred Weihs <weihs@ict.tuwien.ac.at>
+ *
+ * This code is licensed under the GPL. See the file COPYING in the root
+ * directory of the kernel sources for details.
+ *
+ *
+ * Contributions:
+ *
+ * Manfred Weihs <weihs@ict.tuwien.ac.at>
+ * configuration ROM manipulation
+ *
+ */
+
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/param.h>
+#include <linux/spinlock.h>
+#include <linux/string.h>
+
+#include "csr1212.h"
+#include "ieee1394_types.h"
+#include "hosts.h"
+#include "ieee1394.h"
+#include "highlevel.h"
+#include "ieee1394_core.h"
+
+/* Module Parameters */
+/* this module parameter can be used to disable mapping of the FCP registers */
+
+static int fcp = 1;
+module_param(fcp, int, 0444);
+MODULE_PARM_DESC(fcp, "Map FCP registers (default = 1, disable = 0).");
+
+static struct csr1212_keyval *node_cap = NULL;
+
+static void add_host(struct hpsb_host *host);
+static void remove_host(struct hpsb_host *host);
+static void host_reset(struct hpsb_host *host);
+static int read_maps(struct hpsb_host *host, int nodeid, quadlet_t *buffer,
+ u64 addr, size_t length, u16 fl);
+static int write_fcp(struct hpsb_host *host, int nodeid, int dest,
+ quadlet_t *data, u64 addr, size_t length, u16 flags);
+static int read_regs(struct hpsb_host *host, int nodeid, quadlet_t *buf,
+ u64 addr, size_t length, u16 flags);
+static int write_regs(struct hpsb_host *host, int nodeid, int destid,
+ quadlet_t *data, u64 addr, size_t length, u16 flags);
+static int lock_regs(struct hpsb_host *host, int nodeid, quadlet_t *store,
+ u64 addr, quadlet_t data, quadlet_t arg, int extcode, u16 fl);
+static int lock64_regs(struct hpsb_host *host, int nodeid, octlet_t * store,
+ u64 addr, octlet_t data, octlet_t arg, int extcode, u16 fl);
+static int read_config_rom(struct hpsb_host *host, int nodeid, quadlet_t *buffer,
+ u64 addr, size_t length, u16 fl);
+static u64 allocate_addr_range(u64 size, u32 alignment, void *__host);
+static void release_addr_range(u64 addr, void *__host);
+
+static struct hpsb_highlevel csr_highlevel = {
+ .name = "standard registers",
+ .add_host = add_host,
+ .remove_host = remove_host,
+ .host_reset = host_reset,
+};
+
+static struct hpsb_address_ops map_ops = {
+ .read = read_maps,
+};
+
+static struct hpsb_address_ops fcp_ops = {
+ .write = write_fcp,
+};
+
+static struct hpsb_address_ops reg_ops = {
+ .read = read_regs,
+ .write = write_regs,
+ .lock = lock_regs,
+ .lock64 = lock64_regs,
+};
+
+static struct hpsb_address_ops config_rom_ops = {
+ .read = read_config_rom,
+};
+
+struct csr1212_bus_ops csr_bus_ops = {
+ .allocate_addr_range = allocate_addr_range,
+ .release_addr = release_addr_range,
+};
+
+
+static u16 csr_crc16(unsigned *data, int length)
+{
+ int check=0, i;
+ int shift, sum, next=0;
+
+ for (i = length; i; i--) {
+ for (next = check, shift = 28; shift >= 0; shift -= 4 ) {
+ sum = ((next >> 12) ^ (be32_to_cpu(*data) >> shift)) & 0xf;
+ next = (next << 4) ^ (sum << 12) ^ (sum << 5) ^ (sum);
+ }
+ check = next & 0xffff;
+ data++;
+ }
+
+ return check;
+}
+
+static void host_reset(struct hpsb_host *host)
+{
+ host->csr.state &= 0x300;
+
+ host->csr.bus_manager_id = 0x3f;
+ host->csr.bandwidth_available = 4915;
+ host->csr.channels_available_hi = 0xfffffffe; /* pre-alloc ch 31 per 1394a-2000 */
+ host->csr.channels_available_lo = ~0;
+ host->csr.broadcast_channel = 0x80000000 | 31;
+
+ if (host->is_irm) {
+ if (host->driver->hw_csr_reg) {
+ host->driver->hw_csr_reg(host, 2, 0xfffffffe, ~0);
+ }
+ }
+
+ host->csr.node_ids = host->node_id << 16;
+
+ if (!host->is_root) {
+ /* clear cmstr bit */
+ host->csr.state &= ~0x100;
+ }
+
+ be32_add_cpu(&host->csr.topology_map[1], 1);
+ host->csr.topology_map[2] = cpu_to_be32(host->node_count << 16
+ | host->selfid_count);
+ host->csr.topology_map[0] =
+ cpu_to_be32((host->selfid_count + 2) << 16
+ | csr_crc16(host->csr.topology_map + 1,
+ host->selfid_count + 2));
+
+ be32_add_cpu(&host->csr.speed_map[1], 1);
+ host->csr.speed_map[0] = cpu_to_be32(0x3f1 << 16
+ | csr_crc16(host->csr.speed_map+1,
+ 0x3f1));
+}
+
+/*
+ * HI == seconds (bits 0:2)
+ * LO == fractions of a second in units of 125usec (bits 19:31)
+ *
+ * Convert SPLIT_TIMEOUT to jiffies.
+ * The default and minimum as per 1394a-2000 clause 8.3.2.2.6 is 100ms.
+ */
+static inline void calculate_expire(struct csr_control *csr)
+{
+ unsigned int usecs = (csr->split_timeout_hi & 7) * 1000000 +
+ (csr->split_timeout_lo >> 19) * 125;
+
+ csr->expire = usecs_to_jiffies(usecs > 100000 ? usecs : 100000);
+ HPSB_VERBOSE("CSR: setting expire to %lu, HZ=%u", csr->expire, HZ);
+}
+
+
+static void add_host(struct hpsb_host *host)
+{
+ struct csr1212_keyval *root;
+ quadlet_t bus_info[CSR_BUS_INFO_SIZE];
+
+ hpsb_register_addrspace(&csr_highlevel, host, &reg_ops,
+ CSR_REGISTER_BASE,
+ CSR_REGISTER_BASE + CSR_CONFIG_ROM);
+ hpsb_register_addrspace(&csr_highlevel, host, &config_rom_ops,
+ CSR_REGISTER_BASE + CSR_CONFIG_ROM,
+ CSR_REGISTER_BASE + CSR_CONFIG_ROM_END);
+ if (fcp) {
+ hpsb_register_addrspace(&csr_highlevel, host, &fcp_ops,
+ CSR_REGISTER_BASE + CSR_FCP_COMMAND,
+ CSR_REGISTER_BASE + CSR_FCP_END);
+ }
+ hpsb_register_addrspace(&csr_highlevel, host, &map_ops,
+ CSR_REGISTER_BASE + CSR_TOPOLOGY_MAP,
+ CSR_REGISTER_BASE + CSR_TOPOLOGY_MAP_END);
+ hpsb_register_addrspace(&csr_highlevel, host, &map_ops,
+ CSR_REGISTER_BASE + CSR_SPEED_MAP,
+ CSR_REGISTER_BASE + CSR_SPEED_MAP_END);
+
+ spin_lock_init(&host->csr.lock);
+
+ host->csr.state = 0;
+ host->csr.node_ids = 0;
+ host->csr.split_timeout_hi = 0;
+ host->csr.split_timeout_lo = 800 << 19;
+ calculate_expire(&host->csr);
+ host->csr.cycle_time = 0;
+ host->csr.bus_time = 0;
+ host->csr.bus_manager_id = 0x3f;
+ host->csr.bandwidth_available = 4915;
+ host->csr.channels_available_hi = 0xfffffffe; /* pre-alloc ch 31 per 1394a-2000 */
+ host->csr.channels_available_lo = ~0;
+ host->csr.broadcast_channel = 0x80000000 | 31;
+
+ if (host->is_irm) {
+ if (host->driver->hw_csr_reg) {
+ host->driver->hw_csr_reg(host, 2, 0xfffffffe, ~0);
+ }
+ }
+
+ if (host->csr.max_rec >= 9)
+ host->csr.max_rom = 2;
+ else if (host->csr.max_rec >= 5)
+ host->csr.max_rom = 1;
+ else
+ host->csr.max_rom = 0;
+
+ host->csr.generation = 2;
+
+ bus_info[1] = __constant_cpu_to_be32(0x31333934);
+ bus_info[2] = cpu_to_be32((hpsb_disable_irm ? 0 : 1 << CSR_IRMC_SHIFT) |
+ (1 << CSR_CMC_SHIFT) |
+ (1 << CSR_ISC_SHIFT) |
+ (0 << CSR_BMC_SHIFT) |
+ (0 << CSR_PMC_SHIFT) |
+ (host->csr.cyc_clk_acc << CSR_CYC_CLK_ACC_SHIFT) |
+ (host->csr.max_rec << CSR_MAX_REC_SHIFT) |
+ (host->csr.max_rom << CSR_MAX_ROM_SHIFT) |
+ (host->csr.generation << CSR_GENERATION_SHIFT) |
+ host->csr.lnk_spd);
+
+ bus_info[3] = cpu_to_be32(host->csr.guid_hi);
+ bus_info[4] = cpu_to_be32(host->csr.guid_lo);
+
+ /* The hardware copy of the bus info block will be set later when a
+ * bus reset is issued. */
+
+ csr1212_init_local_csr(host->csr.rom, bus_info, host->csr.max_rom);
+
+ root = host->csr.rom->root_kv;
+
+ if(csr1212_attach_keyval_to_directory(root, node_cap) != CSR1212_SUCCESS) {
+ HPSB_ERR("Failed to attach Node Capabilities to root directory");
+ }
+
+ host->update_config_rom = 1;
+}
+
+static void remove_host(struct hpsb_host *host)
+{
+ quadlet_t bus_info[CSR_BUS_INFO_SIZE];
+
+ bus_info[1] = __constant_cpu_to_be32(0x31333934);
+ bus_info[2] = cpu_to_be32((0 << CSR_IRMC_SHIFT) |
+ (0 << CSR_CMC_SHIFT) |
+ (0 << CSR_ISC_SHIFT) |
+ (0 << CSR_BMC_SHIFT) |
+ (0 << CSR_PMC_SHIFT) |
+ (host->csr.cyc_clk_acc << CSR_CYC_CLK_ACC_SHIFT) |
+ (host->csr.max_rec << CSR_MAX_REC_SHIFT) |
+ (0 << CSR_MAX_ROM_SHIFT) |
+ (0 << CSR_GENERATION_SHIFT) |
+ host->csr.lnk_spd);
+
+ bus_info[3] = cpu_to_be32(host->csr.guid_hi);
+ bus_info[4] = cpu_to_be32(host->csr.guid_lo);
+
+ csr1212_detach_keyval_from_directory(host->csr.rom->root_kv, node_cap);
+
+ csr1212_init_local_csr(host->csr.rom, bus_info, 0);
+ host->update_config_rom = 1;
+}
+
+
+int hpsb_update_config_rom(struct hpsb_host *host, const quadlet_t *new_rom,
+ size_t buffersize, unsigned char rom_version)
+{
+ unsigned long flags;
+ int ret;
+
+ HPSB_NOTICE("hpsb_update_config_rom() is deprecated");
+
+ spin_lock_irqsave(&host->csr.lock, flags);
+ if (rom_version != host->csr.generation)
+ ret = -1;
+ else if (buffersize > host->csr.rom->cache_head->size)
+ ret = -2;
+ else {
+ /* Just overwrite the generated ConfigROM image with new data,
+ * it can be regenerated later. */
+ memcpy(host->csr.rom->cache_head->data, new_rom, buffersize);
+ host->csr.rom->cache_head->len = buffersize;
+
+ if (host->driver->set_hw_config_rom)
+ host->driver->set_hw_config_rom(host, host->csr.rom->bus_info_data);
+ /* Increment the generation number to keep some sort of sync
+ * with the newer ConfigROM manipulation method. */
+ host->csr.generation++;
+ if (host->csr.generation > 0xf || host->csr.generation < 2)
+ host->csr.generation = 2;
+ ret=0;
+ }
+ spin_unlock_irqrestore(&host->csr.lock, flags);
+ return ret;
+}
+
+
+/* Read topology / speed maps and configuration ROM */
+static int read_maps(struct hpsb_host *host, int nodeid, quadlet_t *buffer,
+ u64 addr, size_t length, u16 fl)
+{
+ unsigned long flags;
+ int csraddr = addr - CSR_REGISTER_BASE;
+ const char *src;
+
+ spin_lock_irqsave(&host->csr.lock, flags);
+
+ if (csraddr < CSR_SPEED_MAP) {
+ src = ((char *)host->csr.topology_map) + csraddr
+ - CSR_TOPOLOGY_MAP;
+ } else {
+ src = ((char *)host->csr.speed_map) + csraddr - CSR_SPEED_MAP;
+ }
+
+ memcpy(buffer, src, length);
+ spin_unlock_irqrestore(&host->csr.lock, flags);
+ return RCODE_COMPLETE;
+}
+
+
+#define out if (--length == 0) break
+
+static int read_regs(struct hpsb_host *host, int nodeid, quadlet_t *buf,
+ u64 addr, size_t length, u16 flags)
+{
+ int csraddr = addr - CSR_REGISTER_BASE;
+ int oldcycle;
+ quadlet_t ret;
+
+ if ((csraddr | length) & 0x3)
+ return RCODE_TYPE_ERROR;
+
+ length /= 4;
+
+ switch (csraddr) {
+ case CSR_STATE_CLEAR:
+ *(buf++) = cpu_to_be32(host->csr.state);
+ out;
+ case CSR_STATE_SET:
+ *(buf++) = cpu_to_be32(host->csr.state);
+ out;
+ case CSR_NODE_IDS:
+ *(buf++) = cpu_to_be32(host->csr.node_ids);
+ out;
+
+ case CSR_RESET_START:
+ return RCODE_TYPE_ERROR;
+
+ /* address gap - handled by default below */
+
+ case CSR_SPLIT_TIMEOUT_HI:
+ *(buf++) = cpu_to_be32(host->csr.split_timeout_hi);
+ out;
+ case CSR_SPLIT_TIMEOUT_LO:
+ *(buf++) = cpu_to_be32(host->csr.split_timeout_lo);
+ out;
+
+ /* address gap */
+ return RCODE_ADDRESS_ERROR;
+
+ case CSR_CYCLE_TIME:
+ oldcycle = host->csr.cycle_time;
+ host->csr.cycle_time =
+ host->driver->devctl(host, GET_CYCLE_COUNTER, 0);
+
+ if (oldcycle > host->csr.cycle_time) {
+ /* cycle time wrapped around */
+ host->csr.bus_time += 1 << 7;
+ }
+ *(buf++) = cpu_to_be32(host->csr.cycle_time);
+ out;
+ case CSR_BUS_TIME:
+ oldcycle = host->csr.cycle_time;
+ host->csr.cycle_time =
+ host->driver->devctl(host, GET_CYCLE_COUNTER, 0);
+
+ if (oldcycle > host->csr.cycle_time) {
+ /* cycle time wrapped around */
+ host->csr.bus_time += (1 << 7);
+ }
+ *(buf++) = cpu_to_be32(host->csr.bus_time
+ | (host->csr.cycle_time >> 25));
+ out;
+
+ /* address gap */
+ return RCODE_ADDRESS_ERROR;
+
+ case CSR_BUSY_TIMEOUT:
+ /* not yet implemented */
+ return RCODE_ADDRESS_ERROR;
+
+ case CSR_BUS_MANAGER_ID:
+ if (host->driver->hw_csr_reg)
+ ret = host->driver->hw_csr_reg(host, 0, 0, 0);
+ else
+ ret = host->csr.bus_manager_id;
+
+ *(buf++) = cpu_to_be32(ret);
+ out;
+ case CSR_BANDWIDTH_AVAILABLE:
+ if (host->driver->hw_csr_reg)
+ ret = host->driver->hw_csr_reg(host, 1, 0, 0);
+ else
+ ret = host->csr.bandwidth_available;
+
+ *(buf++) = cpu_to_be32(ret);
+ out;
+ case CSR_CHANNELS_AVAILABLE_HI:
+ if (host->driver->hw_csr_reg)
+ ret = host->driver->hw_csr_reg(host, 2, 0, 0);
+ else
+ ret = host->csr.channels_available_hi;
+
+ *(buf++) = cpu_to_be32(ret);
+ out;
+ case CSR_CHANNELS_AVAILABLE_LO:
+ if (host->driver->hw_csr_reg)
+ ret = host->driver->hw_csr_reg(host, 3, 0, 0);
+ else
+ ret = host->csr.channels_available_lo;
+
+ *(buf++) = cpu_to_be32(ret);
+ out;
+
+ case CSR_BROADCAST_CHANNEL:
+ *(buf++) = cpu_to_be32(host->csr.broadcast_channel);
+ out;
+
+ /* address gap to end - fall through to default */
+ default:
+ return RCODE_ADDRESS_ERROR;
+ }
+
+ return RCODE_COMPLETE;
+}
+
+static int write_regs(struct hpsb_host *host, int nodeid, int destid,
+ quadlet_t *data, u64 addr, size_t length, u16 flags)
+{
+ int csraddr = addr - CSR_REGISTER_BASE;
+
+ if ((csraddr | length) & 0x3)
+ return RCODE_TYPE_ERROR;
+
+ length /= 4;
+
+ switch (csraddr) {
+ case CSR_STATE_CLEAR:
+ /* FIXME FIXME FIXME */
+ printk("doh, someone wants to mess with state clear\n");
+ out;
+ case CSR_STATE_SET:
+ printk("doh, someone wants to mess with state set\n");
+ out;
+
+ case CSR_NODE_IDS:
+ host->csr.node_ids &= NODE_MASK << 16;
+ host->csr.node_ids |= be32_to_cpu(*(data++)) & (BUS_MASK << 16);
+ host->node_id = host->csr.node_ids >> 16;
+ host->driver->devctl(host, SET_BUS_ID, host->node_id >> 6);
+ out;
+
+ case CSR_RESET_START:
+ /* FIXME - perform command reset */
+ out;
+
+ /* address gap */
+ return RCODE_ADDRESS_ERROR;
+
+ case CSR_SPLIT_TIMEOUT_HI:
+ host->csr.split_timeout_hi =
+ be32_to_cpu(*(data++)) & 0x00000007;
+ calculate_expire(&host->csr);
+ out;
+ case CSR_SPLIT_TIMEOUT_LO:
+ host->csr.split_timeout_lo =
+ be32_to_cpu(*(data++)) & 0xfff80000;
+ calculate_expire(&host->csr);
+ out;
+
+ /* address gap */
+ return RCODE_ADDRESS_ERROR;
+
+ case CSR_CYCLE_TIME:
+ /* should only be set by cycle start packet, automatically */
+ host->csr.cycle_time = be32_to_cpu(*data);
+ host->driver->devctl(host, SET_CYCLE_COUNTER,
+ be32_to_cpu(*(data++)));
+ out;
+ case CSR_BUS_TIME:
+ host->csr.bus_time = be32_to_cpu(*(data++)) & 0xffffff80;
+ out;
+
+ /* address gap */
+ return RCODE_ADDRESS_ERROR;
+
+ case CSR_BUSY_TIMEOUT:
+ /* not yet implemented */
+ return RCODE_ADDRESS_ERROR;
+
+ case CSR_BUS_MANAGER_ID:
+ case CSR_BANDWIDTH_AVAILABLE:
+ case CSR_CHANNELS_AVAILABLE_HI:
+ case CSR_CHANNELS_AVAILABLE_LO:
+ /* these are not writable, only lockable */
+ return RCODE_TYPE_ERROR;
+
+ case CSR_BROADCAST_CHANNEL:
+ /* only the valid bit can be written */
+ host->csr.broadcast_channel = (host->csr.broadcast_channel & ~0x40000000)
+ | (be32_to_cpu(*data) & 0x40000000);
+ out;
+
+ /* address gap to end - fall through */
+ default:
+ return RCODE_ADDRESS_ERROR;
+ }
+
+ return RCODE_COMPLETE;
+}
+
+#undef out
+
+
+static int lock_regs(struct hpsb_host *host, int nodeid, quadlet_t *store,
+ u64 addr, quadlet_t data, quadlet_t arg, int extcode, u16 fl)
+{
+ int csraddr = addr - CSR_REGISTER_BASE;
+ unsigned long flags;
+ quadlet_t *regptr = NULL;
+
+ if (csraddr & 0x3)
+ return RCODE_TYPE_ERROR;
+
+ if (csraddr < CSR_BUS_MANAGER_ID || csraddr > CSR_CHANNELS_AVAILABLE_LO
+ || extcode != EXTCODE_COMPARE_SWAP)
+ goto unsupported_lockreq;
+
+ data = be32_to_cpu(data);
+ arg = be32_to_cpu(arg);
+
+ /* Is somebody releasing the broadcast_channel on us? */
+ if (csraddr == CSR_CHANNELS_AVAILABLE_HI && (data & 0x1)) {
+ /* Note: this is may not be the right way to handle
+ * the problem, so we should look into the proper way
+ * eventually. */
+ HPSB_WARN("Node [" NODE_BUS_FMT "] wants to release "
+ "broadcast channel 31. Ignoring.",
+ NODE_BUS_ARGS(host, nodeid));
+
+ data &= ~0x1; /* keep broadcast channel allocated */
+ }
+
+ if (host->driver->hw_csr_reg) {
+ quadlet_t old;
+
+ old = host->driver->
+ hw_csr_reg(host, (csraddr - CSR_BUS_MANAGER_ID) >> 2,
+ data, arg);
+
+ *store = cpu_to_be32(old);
+ return RCODE_COMPLETE;
+ }
+
+ spin_lock_irqsave(&host->csr.lock, flags);
+
+ switch (csraddr) {
+ case CSR_BUS_MANAGER_ID:
+ regptr = &host->csr.bus_manager_id;
+ *store = cpu_to_be32(*regptr);
+ if (*regptr == arg)
+ *regptr = data;
+ break;
+
+ case CSR_BANDWIDTH_AVAILABLE:
+ {
+ quadlet_t bandwidth;
+ quadlet_t old;
+ quadlet_t new;
+
+ regptr = &host->csr.bandwidth_available;
+ old = *regptr;
+
+ /* bandwidth available algorithm adapted from IEEE 1394a-2000 spec */
+ if (arg > 0x1fff) {
+ *store = cpu_to_be32(old); /* change nothing */
+ break;
+ }
+ data &= 0x1fff;
+ if (arg >= data) {
+ /* allocate bandwidth */
+ bandwidth = arg - data;
+ if (old >= bandwidth) {
+ new = old - bandwidth;
+ *store = cpu_to_be32(arg);
+ *regptr = new;
+ } else {
+ *store = cpu_to_be32(old);
+ }
+ } else {
+ /* deallocate bandwidth */
+ bandwidth = data - arg;
+ if (old + bandwidth < 0x2000) {
+ new = old + bandwidth;
+ *store = cpu_to_be32(arg);
+ *regptr = new;
+ } else {
+ *store = cpu_to_be32(old);
+ }
+ }
+ break;
+ }
+
+ case CSR_CHANNELS_AVAILABLE_HI:
+ {
+ /* Lock algorithm for CHANNELS_AVAILABLE as recommended by 1394a-2000 */
+ quadlet_t affected_channels = arg ^ data;
+
+ regptr = &host->csr.channels_available_hi;
+
+ if ((arg & affected_channels) == (*regptr & affected_channels)) {
+ *regptr ^= affected_channels;
+ *store = cpu_to_be32(arg);
+ } else {
+ *store = cpu_to_be32(*regptr);
+ }
+
+ break;
+ }
+
+ case CSR_CHANNELS_AVAILABLE_LO:
+ {
+ /* Lock algorithm for CHANNELS_AVAILABLE as recommended by 1394a-2000 */
+ quadlet_t affected_channels = arg ^ data;
+
+ regptr = &host->csr.channels_available_lo;
+
+ if ((arg & affected_channels) == (*regptr & affected_channels)) {
+ *regptr ^= affected_channels;
+ *store = cpu_to_be32(arg);
+ } else {
+ *store = cpu_to_be32(*regptr);
+ }
+ break;
+ }
+ }
+
+ spin_unlock_irqrestore(&host->csr.lock, flags);
+
+ return RCODE_COMPLETE;
+
+ unsupported_lockreq:
+ switch (csraddr) {
+ case CSR_STATE_CLEAR:
+ case CSR_STATE_SET:
+ case CSR_RESET_START:
+ case CSR_NODE_IDS:
+ case CSR_SPLIT_TIMEOUT_HI:
+ case CSR_SPLIT_TIMEOUT_LO:
+ case CSR_CYCLE_TIME:
+ case CSR_BUS_TIME:
+ case CSR_BROADCAST_CHANNEL:
+ return RCODE_TYPE_ERROR;
+
+ case CSR_BUSY_TIMEOUT:
+ /* not yet implemented - fall through */
+ default:
+ return RCODE_ADDRESS_ERROR;
+ }
+}
+
+static int lock64_regs(struct hpsb_host *host, int nodeid, octlet_t * store,
+ u64 addr, octlet_t data, octlet_t arg, int extcode, u16 fl)
+{
+ int csraddr = addr - CSR_REGISTER_BASE;
+ unsigned long flags;
+
+ data = be64_to_cpu(data);
+ arg = be64_to_cpu(arg);
+
+ if (csraddr & 0x3)
+ return RCODE_TYPE_ERROR;
+
+ if (csraddr != CSR_CHANNELS_AVAILABLE
+ || extcode != EXTCODE_COMPARE_SWAP)
+ goto unsupported_lock64req;
+
+ /* Is somebody releasing the broadcast_channel on us? */
+ if (csraddr == CSR_CHANNELS_AVAILABLE_HI && (data & 0x100000000ULL)) {
+ /* Note: this is may not be the right way to handle
+ * the problem, so we should look into the proper way
+ * eventually. */
+ HPSB_WARN("Node [" NODE_BUS_FMT "] wants to release "
+ "broadcast channel 31. Ignoring.",
+ NODE_BUS_ARGS(host, nodeid));
+
+ data &= ~0x100000000ULL; /* keep broadcast channel allocated */
+ }
+
+ if (host->driver->hw_csr_reg) {
+ quadlet_t data_hi, data_lo;
+ quadlet_t arg_hi, arg_lo;
+ quadlet_t old_hi, old_lo;
+
+ data_hi = data >> 32;
+ data_lo = data & 0xFFFFFFFF;
+ arg_hi = arg >> 32;
+ arg_lo = arg & 0xFFFFFFFF;
+
+ old_hi = host->driver->hw_csr_reg(host, (csraddr - CSR_BUS_MANAGER_ID) >> 2,
+ data_hi, arg_hi);
+
+ old_lo = host->driver->hw_csr_reg(host, ((csraddr + 4) - CSR_BUS_MANAGER_ID) >> 2,
+ data_lo, arg_lo);
+
+ *store = cpu_to_be64(((octlet_t)old_hi << 32) | old_lo);
+ } else {
+ octlet_t old;
+ octlet_t affected_channels = arg ^ data;
+
+ spin_lock_irqsave(&host->csr.lock, flags);
+
+ old = ((octlet_t)host->csr.channels_available_hi << 32) | host->csr.channels_available_lo;
+
+ if ((arg & affected_channels) == (old & affected_channels)) {
+ host->csr.channels_available_hi ^= (affected_channels >> 32);
+ host->csr.channels_available_lo ^= (affected_channels & 0xffffffff);
+ *store = cpu_to_be64(arg);
+ } else {
+ *store = cpu_to_be64(old);
+ }
+
+ spin_unlock_irqrestore(&host->csr.lock, flags);
+ }
+
+ /* Is somebody erroneously releasing the broadcast_channel on us? */
+ if (host->csr.channels_available_hi & 0x1)
+ host->csr.channels_available_hi &= ~0x1;
+
+ return RCODE_COMPLETE;
+
+ unsupported_lock64req:
+ switch (csraddr) {
+ case CSR_STATE_CLEAR:
+ case CSR_STATE_SET:
+ case CSR_RESET_START:
+ case CSR_NODE_IDS:
+ case CSR_SPLIT_TIMEOUT_HI:
+ case CSR_SPLIT_TIMEOUT_LO:
+ case CSR_CYCLE_TIME:
+ case CSR_BUS_TIME:
+ case CSR_BUS_MANAGER_ID:
+ case CSR_BROADCAST_CHANNEL:
+ case CSR_BUSY_TIMEOUT:
+ case CSR_BANDWIDTH_AVAILABLE:
+ return RCODE_TYPE_ERROR;
+
+ default:
+ return RCODE_ADDRESS_ERROR;
+ }
+}
+
+static int write_fcp(struct hpsb_host *host, int nodeid, int dest,
+ quadlet_t *data, u64 addr, size_t length, u16 flags)
+{
+ int csraddr = addr - CSR_REGISTER_BASE;
+
+ if (length > 512)
+ return RCODE_TYPE_ERROR;
+
+ switch (csraddr) {
+ case CSR_FCP_COMMAND:
+ highlevel_fcp_request(host, nodeid, 0, (u8 *)data, length);
+ break;
+ case CSR_FCP_RESPONSE:
+ highlevel_fcp_request(host, nodeid, 1, (u8 *)data, length);
+ break;
+ default:
+ return RCODE_TYPE_ERROR;
+ }
+
+ return RCODE_COMPLETE;
+}
+
+static int read_config_rom(struct hpsb_host *host, int nodeid, quadlet_t *buffer,
+ u64 addr, size_t length, u16 fl)
+{
+ u32 offset = addr - CSR1212_REGISTER_SPACE_BASE;
+
+ if (csr1212_read(host->csr.rom, offset, buffer, length) == CSR1212_SUCCESS)
+ return RCODE_COMPLETE;
+ else
+ return RCODE_ADDRESS_ERROR;
+}
+
+static u64 allocate_addr_range(u64 size, u32 alignment, void *__host)
+{
+ struct hpsb_host *host = (struct hpsb_host*)__host;
+
+ return hpsb_allocate_and_register_addrspace(&csr_highlevel,
+ host,
+ &config_rom_ops,
+ size, alignment,
+ CSR1212_UNITS_SPACE_BASE,
+ CSR1212_UNITS_SPACE_END);
+}
+
+static void release_addr_range(u64 addr, void *__host)
+{
+ struct hpsb_host *host = (struct hpsb_host*)__host;
+ hpsb_unregister_addrspace(&csr_highlevel, host, addr);
+}
+
+
+int init_csr(void)
+{
+ node_cap = csr1212_new_immediate(CSR1212_KV_ID_NODE_CAPABILITIES, 0x0083c0);
+ if (!node_cap) {
+ HPSB_ERR("Failed to allocate memory for Node Capabilties ConfigROM entry!");
+ return -ENOMEM;
+ }
+
+ hpsb_register_highlevel(&csr_highlevel);
+
+ return 0;
+}
+
+void cleanup_csr(void)
+{
+ if (node_cap)
+ csr1212_release_keyval(node_cap);
+ hpsb_unregister_highlevel(&csr_highlevel);
+}
diff --git a/drivers/ieee1394/csr.h b/drivers/ieee1394/csr.h
new file mode 100644
index 0000000..f115465
--- /dev/null
+++ b/drivers/ieee1394/csr.h
@@ -0,0 +1,99 @@
+#ifndef _IEEE1394_CSR_H
+#define _IEEE1394_CSR_H
+
+#include <linux/spinlock_types.h>
+
+#include "csr1212.h"
+#include "ieee1394_types.h"
+
+#define CSR_REGISTER_BASE 0xfffff0000000ULL
+
+/* register offsets relative to CSR_REGISTER_BASE */
+#define CSR_STATE_CLEAR 0x0
+#define CSR_STATE_SET 0x4
+#define CSR_NODE_IDS 0x8
+#define CSR_RESET_START 0xc
+#define CSR_SPLIT_TIMEOUT_HI 0x18
+#define CSR_SPLIT_TIMEOUT_LO 0x1c
+#define CSR_CYCLE_TIME 0x200
+#define CSR_BUS_TIME 0x204
+#define CSR_BUSY_TIMEOUT 0x210
+#define CSR_BUS_MANAGER_ID 0x21c
+#define CSR_BANDWIDTH_AVAILABLE 0x220
+#define CSR_CHANNELS_AVAILABLE 0x224
+#define CSR_CHANNELS_AVAILABLE_HI 0x224
+#define CSR_CHANNELS_AVAILABLE_LO 0x228
+#define CSR_BROADCAST_CHANNEL 0x234
+#define CSR_CONFIG_ROM 0x400
+#define CSR_CONFIG_ROM_END 0x800
+#define CSR_FCP_COMMAND 0xB00
+#define CSR_FCP_RESPONSE 0xD00
+#define CSR_FCP_END 0xF00
+#define CSR_TOPOLOGY_MAP 0x1000
+#define CSR_TOPOLOGY_MAP_END 0x1400
+#define CSR_SPEED_MAP 0x2000
+#define CSR_SPEED_MAP_END 0x3000
+
+/* IEEE 1394 bus specific Configuration ROM Key IDs */
+#define IEEE1394_KV_ID_POWER_REQUIREMENTS (0x30)
+
+/* IEEE 1394 Bus Information Block specifics */
+#define CSR_BUS_INFO_SIZE (5 * sizeof(quadlet_t))
+
+#define CSR_IRMC_SHIFT 31
+#define CSR_CMC_SHIFT 30
+#define CSR_ISC_SHIFT 29
+#define CSR_BMC_SHIFT 28
+#define CSR_PMC_SHIFT 27
+#define CSR_CYC_CLK_ACC_SHIFT 16
+#define CSR_MAX_REC_SHIFT 12
+#define CSR_MAX_ROM_SHIFT 8
+#define CSR_GENERATION_SHIFT 4
+
+#define CSR_SET_BUS_INFO_GENERATION(csr, gen) \
+ ((csr)->bus_info_data[2] = \
+ cpu_to_be32((be32_to_cpu((csr)->bus_info_data[2]) & \
+ ~(0xf << CSR_GENERATION_SHIFT)) | \
+ (gen) << CSR_GENERATION_SHIFT))
+
+struct csr_control {
+ spinlock_t lock;
+
+ quadlet_t state;
+ quadlet_t node_ids;
+ quadlet_t split_timeout_hi, split_timeout_lo;
+ unsigned long expire; /* Calculated from split_timeout */
+ quadlet_t cycle_time;
+ quadlet_t bus_time;
+ quadlet_t bus_manager_id;
+ quadlet_t bandwidth_available;
+ quadlet_t channels_available_hi, channels_available_lo;
+ quadlet_t broadcast_channel;
+
+ /* Bus Info */
+ quadlet_t guid_hi, guid_lo;
+ u8 cyc_clk_acc;
+ u8 max_rec;
+ u8 max_rom;
+ u8 generation; /* Only use values between 0x2 and 0xf */
+ u8 lnk_spd;
+
+ unsigned long gen_timestamp[16];
+
+ struct csr1212_csr *rom;
+
+ quadlet_t topology_map[256];
+ quadlet_t speed_map[1024];
+};
+
+extern struct csr1212_bus_ops csr_bus_ops;
+
+int init_csr(void);
+void cleanup_csr(void);
+
+/* hpsb_update_config_rom() is deprecated */
+struct hpsb_host;
+int hpsb_update_config_rom(struct hpsb_host *host, const quadlet_t *new_rom,
+ size_t size, unsigned char rom_version);
+
+#endif /* _IEEE1394_CSR_H */
diff --git a/drivers/ieee1394/csr1212.c b/drivers/ieee1394/csr1212.c
new file mode 100644
index 0000000..5e38a68
--- /dev/null
+++ b/drivers/ieee1394/csr1212.c
@@ -0,0 +1,1482 @@
+/*
+ * csr1212.c -- IEEE 1212 Control and Status Register support for Linux
+ *
+ * Copyright (C) 2003 Francois Retief <fgretief@sun.ac.za>
+ * Steve Kinneberg <kinnebergsteve@acmsystems.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/* TODO List:
+ * - Verify interface consistency: i.e., public functions that take a size
+ * parameter expect size to be in bytes.
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <asm/bug.h>
+#include <asm/byteorder.h>
+
+#include "csr1212.h"
+
+
+/* Permitted key type for each key id */
+#define __I (1 << CSR1212_KV_TYPE_IMMEDIATE)
+#define __C (1 << CSR1212_KV_TYPE_CSR_OFFSET)
+#define __D (1 << CSR1212_KV_TYPE_DIRECTORY)
+#define __L (1 << CSR1212_KV_TYPE_LEAF)
+static const u8 csr1212_key_id_type_map[0x30] = {
+ __C, /* used by Apple iSight */
+ __D | __L, /* Descriptor */
+ __I | __D | __L, /* Bus_Dependent_Info */
+ __I | __D | __L, /* Vendor */
+ __I, /* Hardware_Version */
+ 0, 0, /* Reserved */
+ __D | __L | __I, /* Module */
+ __I, 0, 0, 0, /* used by Apple iSight, Reserved */
+ __I, /* Node_Capabilities */
+ __L, /* EUI_64 */
+ 0, 0, 0, /* Reserved */
+ __D, /* Unit */
+ __I, /* Specifier_ID */
+ __I, /* Version */
+ __I | __C | __D | __L, /* Dependent_Info */
+ __L, /* Unit_Location */
+ 0, /* Reserved */
+ __I, /* Model */
+ __D, /* Instance */
+ __L, /* Keyword */
+ __D, /* Feature */
+ __L, /* Extended_ROM */
+ __I, /* Extended_Key_Specifier_ID */
+ __I, /* Extended_Key */
+ __I | __C | __D | __L, /* Extended_Data */
+ __L, /* Modifiable_Descriptor */
+ __I, /* Directory_ID */
+ __I, /* Revision */
+};
+#undef __I
+#undef __C
+#undef __D
+#undef __L
+
+
+#define quads_to_bytes(_q) ((_q) * sizeof(u32))
+#define bytes_to_quads(_b) DIV_ROUND_UP(_b, sizeof(u32))
+
+static void free_keyval(struct csr1212_keyval *kv)
+{
+ if ((kv->key.type == CSR1212_KV_TYPE_LEAF) &&
+ (kv->key.id != CSR1212_KV_ID_EXTENDED_ROM))
+ CSR1212_FREE(kv->value.leaf.data);
+
+ CSR1212_FREE(kv);
+}
+
+static u16 csr1212_crc16(const u32 *buffer, size_t length)
+{
+ int shift;
+ u32 data;
+ u16 sum, crc = 0;
+
+ for (; length; length--) {
+ data = be32_to_cpu(*buffer);
+ buffer++;
+ for (shift = 28; shift >= 0; shift -= 4 ) {
+ sum = ((crc >> 12) ^ (data >> shift)) & 0xf;
+ crc = (crc << 4) ^ (sum << 12) ^ (sum << 5) ^ (sum);
+ }
+ crc &= 0xffff;
+ }
+
+ return cpu_to_be16(crc);
+}
+
+/* Microsoft computes the CRC with the bytes in reverse order. */
+static u16 csr1212_msft_crc16(const u32 *buffer, size_t length)
+{
+ int shift;
+ u32 data;
+ u16 sum, crc = 0;
+
+ for (; length; length--) {
+ data = le32_to_cpu(*buffer);
+ buffer++;
+ for (shift = 28; shift >= 0; shift -= 4 ) {
+ sum = ((crc >> 12) ^ (data >> shift)) & 0xf;
+ crc = (crc << 4) ^ (sum << 12) ^ (sum << 5) ^ (sum);
+ }
+ crc &= 0xffff;
+ }
+
+ return cpu_to_be16(crc);
+}
+
+static struct csr1212_dentry *
+csr1212_find_keyval(struct csr1212_keyval *dir, struct csr1212_keyval *kv)
+{
+ struct csr1212_dentry *pos;
+
+ for (pos = dir->value.directory.dentries_head;
+ pos != NULL; pos = pos->next)
+ if (pos->kv == kv)
+ return pos;
+ return NULL;
+}
+
+static struct csr1212_keyval *
+csr1212_find_keyval_offset(struct csr1212_keyval *kv_list, u32 offset)
+{
+ struct csr1212_keyval *kv;
+
+ for (kv = kv_list->next; kv && (kv != kv_list); kv = kv->next)
+ if (kv->offset == offset)
+ return kv;
+ return NULL;
+}
+
+
+/* Creation Routines */
+
+struct csr1212_csr *csr1212_create_csr(struct csr1212_bus_ops *ops,
+ size_t bus_info_size, void *private)
+{
+ struct csr1212_csr *csr;
+
+ csr = CSR1212_MALLOC(sizeof(*csr));
+ if (!csr)
+ return NULL;
+
+ csr->cache_head =
+ csr1212_rom_cache_malloc(CSR1212_CONFIG_ROM_SPACE_OFFSET,
+ CSR1212_CONFIG_ROM_SPACE_SIZE);
+ if (!csr->cache_head) {
+ CSR1212_FREE(csr);
+ return NULL;
+ }
+
+ /* The keyval key id is not used for the root node, but a valid key id
+ * that can be used for a directory needs to be passed to
+ * csr1212_new_directory(). */
+ csr->root_kv = csr1212_new_directory(CSR1212_KV_ID_VENDOR);
+ if (!csr->root_kv) {
+ CSR1212_FREE(csr->cache_head);
+ CSR1212_FREE(csr);
+ return NULL;
+ }
+
+ csr->bus_info_data = csr->cache_head->data;
+ csr->bus_info_len = bus_info_size;
+ csr->crc_len = bus_info_size;
+ csr->ops = ops;
+ csr->private = private;
+ csr->cache_tail = csr->cache_head;
+
+ return csr;
+}
+
+void csr1212_init_local_csr(struct csr1212_csr *csr,
+ const u32 *bus_info_data, int max_rom)
+{
+ static const int mr_map[] = { 4, 64, 1024, 0 };
+
+ BUG_ON(max_rom & ~0x3);
+ csr->max_rom = mr_map[max_rom];
+ memcpy(csr->bus_info_data, bus_info_data, csr->bus_info_len);
+}
+
+static struct csr1212_keyval *csr1212_new_keyval(u8 type, u8 key)
+{
+ struct csr1212_keyval *kv;
+
+ if (key < 0x30 && ((csr1212_key_id_type_map[key] & (1 << type)) == 0))
+ return NULL;
+
+ kv = CSR1212_MALLOC(sizeof(*kv));
+ if (!kv)
+ return NULL;
+
+ atomic_set(&kv->refcnt, 1);
+ kv->key.type = type;
+ kv->key.id = key;
+ kv->associate = NULL;
+ kv->next = NULL;
+ kv->prev = NULL;
+ kv->offset = 0;
+ kv->valid = 0;
+ return kv;
+}
+
+struct csr1212_keyval *csr1212_new_immediate(u8 key, u32 value)
+{
+ struct csr1212_keyval *kv;
+
+ kv = csr1212_new_keyval(CSR1212_KV_TYPE_IMMEDIATE, key);
+ if (!kv)
+ return NULL;
+
+ kv->value.immediate = value;
+ kv->valid = 1;
+ return kv;
+}
+
+static struct csr1212_keyval *
+csr1212_new_leaf(u8 key, const void *data, size_t data_len)
+{
+ struct csr1212_keyval *kv;
+
+ kv = csr1212_new_keyval(CSR1212_KV_TYPE_LEAF, key);
+ if (!kv)
+ return NULL;
+
+ if (data_len > 0) {
+ kv->value.leaf.data = CSR1212_MALLOC(data_len);
+ if (!kv->value.leaf.data) {
+ CSR1212_FREE(kv);
+ return NULL;
+ }
+
+ if (data)
+ memcpy(kv->value.leaf.data, data, data_len);
+ } else {
+ kv->value.leaf.data = NULL;
+ }
+
+ kv->value.leaf.len = bytes_to_quads(data_len);
+ kv->offset = 0;
+ kv->valid = 1;
+
+ return kv;
+}
+
+static struct csr1212_keyval *
+csr1212_new_csr_offset(u8 key, u32 csr_offset)
+{
+ struct csr1212_keyval *kv;
+
+ kv = csr1212_new_keyval(CSR1212_KV_TYPE_CSR_OFFSET, key);
+ if (!kv)
+ return NULL;
+
+ kv->value.csr_offset = csr_offset;
+
+ kv->offset = 0;
+ kv->valid = 1;
+ return kv;
+}
+
+struct csr1212_keyval *csr1212_new_directory(u8 key)
+{
+ struct csr1212_keyval *kv;
+
+ kv = csr1212_new_keyval(CSR1212_KV_TYPE_DIRECTORY, key);
+ if (!kv)
+ return NULL;
+
+ kv->value.directory.len = 0;
+ kv->offset = 0;
+ kv->value.directory.dentries_head = NULL;
+ kv->value.directory.dentries_tail = NULL;
+ kv->valid = 1;
+ return kv;
+}
+
+void csr1212_associate_keyval(struct csr1212_keyval *kv,
+ struct csr1212_keyval *associate)
+{
+ BUG_ON(!kv || !associate || kv->key.id == CSR1212_KV_ID_DESCRIPTOR ||
+ (associate->key.id != CSR1212_KV_ID_DESCRIPTOR &&
+ associate->key.id != CSR1212_KV_ID_DEPENDENT_INFO &&
+ associate->key.id != CSR1212_KV_ID_EXTENDED_KEY &&
+ associate->key.id != CSR1212_KV_ID_EXTENDED_DATA &&
+ associate->key.id < 0x30) ||
+ (kv->key.id == CSR1212_KV_ID_EXTENDED_KEY_SPECIFIER_ID &&
+ associate->key.id != CSR1212_KV_ID_EXTENDED_KEY) ||
+ (kv->key.id == CSR1212_KV_ID_EXTENDED_KEY &&
+ associate->key.id != CSR1212_KV_ID_EXTENDED_DATA) ||
+ (associate->key.id == CSR1212_KV_ID_EXTENDED_KEY &&
+ kv->key.id != CSR1212_KV_ID_EXTENDED_KEY_SPECIFIER_ID) ||
+ (associate->key.id == CSR1212_KV_ID_EXTENDED_DATA &&
+ kv->key.id != CSR1212_KV_ID_EXTENDED_KEY));
+
+ if (kv->associate)
+ csr1212_release_keyval(kv->associate);
+
+ csr1212_keep_keyval(associate);
+ kv->associate = associate;
+}
+
+static int __csr1212_attach_keyval_to_directory(struct csr1212_keyval *dir,
+ struct csr1212_keyval *kv,
+ bool keep_keyval)
+{
+ struct csr1212_dentry *dentry;
+
+ BUG_ON(!kv || !dir || dir->key.type != CSR1212_KV_TYPE_DIRECTORY);
+
+ dentry = CSR1212_MALLOC(sizeof(*dentry));
+ if (!dentry)
+ return -ENOMEM;
+
+ if (keep_keyval)
+ csr1212_keep_keyval(kv);
+ dentry->kv = kv;
+
+ dentry->next = NULL;
+ dentry->prev = dir->value.directory.dentries_tail;
+
+ if (!dir->value.directory.dentries_head)
+ dir->value.directory.dentries_head = dentry;
+
+ if (dir->value.directory.dentries_tail)
+ dir->value.directory.dentries_tail->next = dentry;
+ dir->value.directory.dentries_tail = dentry;
+
+ return CSR1212_SUCCESS;
+}
+
+int csr1212_attach_keyval_to_directory(struct csr1212_keyval *dir,
+ struct csr1212_keyval *kv)
+{
+ return __csr1212_attach_keyval_to_directory(dir, kv, true);
+}
+
+#define CSR1212_DESCRIPTOR_LEAF_DATA(kv) \
+ (&((kv)->value.leaf.data[1]))
+
+#define CSR1212_DESCRIPTOR_LEAF_SET_TYPE(kv, type) \
+ ((kv)->value.leaf.data[0] = \
+ cpu_to_be32(CSR1212_DESCRIPTOR_LEAF_SPECIFIER_ID(kv) | \
+ ((type) << CSR1212_DESCRIPTOR_LEAF_TYPE_SHIFT)))
+#define CSR1212_DESCRIPTOR_LEAF_SET_SPECIFIER_ID(kv, spec_id) \
+ ((kv)->value.leaf.data[0] = \
+ cpu_to_be32((CSR1212_DESCRIPTOR_LEAF_TYPE(kv) << \
+ CSR1212_DESCRIPTOR_LEAF_TYPE_SHIFT) | \
+ ((spec_id) & CSR1212_DESCRIPTOR_LEAF_SPECIFIER_ID_MASK)))
+
+static struct csr1212_keyval *
+csr1212_new_descriptor_leaf(u8 dtype, u32 specifier_id,
+ const void *data, size_t data_len)
+{
+ struct csr1212_keyval *kv;
+
+ kv = csr1212_new_leaf(CSR1212_KV_ID_DESCRIPTOR, NULL,
+ data_len + CSR1212_DESCRIPTOR_LEAF_OVERHEAD);
+ if (!kv)
+ return NULL;
+
+ CSR1212_DESCRIPTOR_LEAF_SET_TYPE(kv, dtype);
+ CSR1212_DESCRIPTOR_LEAF_SET_SPECIFIER_ID(kv, specifier_id);
+
+ if (data)
+ memcpy(CSR1212_DESCRIPTOR_LEAF_DATA(kv), data, data_len);
+
+ return kv;
+}
+
+/* Check if string conforms to minimal ASCII as per IEEE 1212 clause 7.4 */
+static int csr1212_check_minimal_ascii(const char *s)
+{
+ static const char minimal_ascii_table[] = {
+ /* 1 2 4 8 16 32 64 128 */
+ 128, /* --, --, --, --, --, --, --, 07, */
+ 4 + 16 + 32, /* --, --, 0a, --, 0C, 0D, --, --, */
+ 0, /* --, --, --, --, --, --, --, --, */
+ 0, /* --, --, --, --, --, --, --, --, */
+ 255 - 8 - 16, /* 20, 21, 22, --, --, 25, 26, 27, */
+ 255, /* 28, 29, 2a, 2b, 2c, 2d, 2e, 2f, */
+ 255, /* 30, 31, 32, 33, 34, 35, 36, 37, */
+ 255, /* 38, 39, 3a, 3b, 3c, 3d, 3e, 3f, */
+ 255, /* 40, 41, 42, 43, 44, 45, 46, 47, */
+ 255, /* 48, 49, 4a, 4b, 4c, 4d, 4e, 4f, */
+ 255, /* 50, 51, 52, 53, 54, 55, 56, 57, */
+ 1 + 2 + 4 + 128, /* 58, 59, 5a, --, --, --, --, 5f, */
+ 255 - 1, /* --, 61, 62, 63, 64, 65, 66, 67, */
+ 255, /* 68, 69, 6a, 6b, 6c, 6d, 6e, 6f, */
+ 255, /* 70, 71, 72, 73, 74, 75, 76, 77, */
+ 1 + 2 + 4, /* 78, 79, 7a, --, --, --, --, --, */
+ };
+ int i, j;
+
+ for (; *s; s++) {
+ i = *s >> 3; /* i = *s / 8; */
+ j = 1 << (*s & 3); /* j = 1 << (*s % 8); */
+
+ if (i >= ARRAY_SIZE(minimal_ascii_table) ||
+ !(minimal_ascii_table[i] & j))
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/* IEEE 1212 clause 7.5.4.1 textual descriptors (English, minimal ASCII) */
+struct csr1212_keyval *csr1212_new_string_descriptor_leaf(const char *s)
+{
+ struct csr1212_keyval *kv;
+ u32 *text;
+ size_t str_len, quads;
+
+ if (!s || !*s || csr1212_check_minimal_ascii(s))
+ return NULL;
+
+ str_len = strlen(s);
+ quads = bytes_to_quads(str_len);
+ kv = csr1212_new_descriptor_leaf(0, 0, NULL, quads_to_bytes(quads) +
+ CSR1212_TEXTUAL_DESCRIPTOR_LEAF_OVERHEAD);
+ if (!kv)
+ return NULL;
+
+ kv->value.leaf.data[1] = 0; /* width, character_set, language */
+ text = CSR1212_TEXTUAL_DESCRIPTOR_LEAF_DATA(kv);
+ text[quads - 1] = 0; /* padding */
+ memcpy(text, s, str_len);
+
+ return kv;
+}
+
+
+/* Destruction Routines */
+
+void csr1212_detach_keyval_from_directory(struct csr1212_keyval *dir,
+ struct csr1212_keyval *kv)
+{
+ struct csr1212_dentry *dentry;
+
+ if (!kv || !dir || dir->key.type != CSR1212_KV_TYPE_DIRECTORY)
+ return;
+
+ dentry = csr1212_find_keyval(dir, kv);
+
+ if (!dentry)
+ return;
+
+ if (dentry->prev)
+ dentry->prev->next = dentry->next;
+ if (dentry->next)
+ dentry->next->prev = dentry->prev;
+ if (dir->value.directory.dentries_head == dentry)
+ dir->value.directory.dentries_head = dentry->next;
+ if (dir->value.directory.dentries_tail == dentry)
+ dir->value.directory.dentries_tail = dentry->prev;
+
+ CSR1212_FREE(dentry);
+
+ csr1212_release_keyval(kv);
+}
+
+/* This function is used to free the memory taken by a keyval. If the given
+ * keyval is a directory type, then any keyvals contained in that directory
+ * will be destroyed as well if noone holds a reference on them. By means of
+ * list manipulation, this routine will descend a directory structure in a
+ * non-recursive manner. */
+void csr1212_release_keyval(struct csr1212_keyval *kv)
+{
+ struct csr1212_keyval *k, *a;
+ struct csr1212_dentry dentry;
+ struct csr1212_dentry *head, *tail;
+
+ if (!atomic_dec_and_test(&kv->refcnt))
+ return;
+
+ dentry.kv = kv;
+ dentry.next = NULL;
+ dentry.prev = NULL;
+
+ head = &dentry;
+ tail = head;
+
+ while (head) {
+ k = head->kv;
+
+ while (k) {
+ /* must not dec_and_test kv->refcnt again */
+ if (k != kv && !atomic_dec_and_test(&k->refcnt))
+ break;
+
+ a = k->associate;
+
+ if (k->key.type == CSR1212_KV_TYPE_DIRECTORY) {
+ /* If the current entry is a directory, move all
+ * the entries to the destruction list. */
+ if (k->value.directory.dentries_head) {
+ tail->next =
+ k->value.directory.dentries_head;
+ k->value.directory.dentries_head->prev =
+ tail;
+ tail = k->value.directory.dentries_tail;
+ }
+ }
+ free_keyval(k);
+ k = a;
+ }
+
+ head = head->next;
+ if (head) {
+ if (head->prev && head->prev != &dentry)
+ CSR1212_FREE(head->prev);
+ head->prev = NULL;
+ } else if (tail != &dentry) {
+ CSR1212_FREE(tail);
+ }
+ }
+}
+
+void csr1212_destroy_csr(struct csr1212_csr *csr)
+{
+ struct csr1212_csr_rom_cache *c, *oc;
+ struct csr1212_cache_region *cr, *ocr;
+
+ csr1212_release_keyval(csr->root_kv);
+
+ c = csr->cache_head;
+ while (c) {
+ oc = c;
+ cr = c->filled_head;
+ while (cr) {
+ ocr = cr;
+ cr = cr->next;
+ CSR1212_FREE(ocr);
+ }
+ c = c->next;
+ CSR1212_FREE(oc);
+ }
+
+ CSR1212_FREE(csr);
+}
+
+
+/* CSR Image Creation */
+
+static int csr1212_append_new_cache(struct csr1212_csr *csr, size_t romsize)
+{
+ struct csr1212_csr_rom_cache *cache;
+ u64 csr_addr;
+
+ BUG_ON(!csr || !csr->ops || !csr->ops->allocate_addr_range ||
+ !csr->ops->release_addr || csr->max_rom < 1);
+
+ /* ROM size must be a multiple of csr->max_rom */
+ romsize = (romsize + (csr->max_rom - 1)) & ~(csr->max_rom - 1);
+
+ csr_addr = csr->ops->allocate_addr_range(romsize, csr->max_rom,
+ csr->private);
+ if (csr_addr == CSR1212_INVALID_ADDR_SPACE)
+ return -ENOMEM;
+
+ if (csr_addr < CSR1212_REGISTER_SPACE_BASE) {
+ /* Invalid address returned from allocate_addr_range(). */
+ csr->ops->release_addr(csr_addr, csr->private);
+ return -ENOMEM;
+ }
+
+ cache = csr1212_rom_cache_malloc(csr_addr - CSR1212_REGISTER_SPACE_BASE,
+ romsize);
+ if (!cache) {
+ csr->ops->release_addr(csr_addr, csr->private);
+ return -ENOMEM;
+ }
+
+ cache->ext_rom = csr1212_new_keyval(CSR1212_KV_TYPE_LEAF,
+ CSR1212_KV_ID_EXTENDED_ROM);
+ if (!cache->ext_rom) {
+ csr->ops->release_addr(csr_addr, csr->private);
+ CSR1212_FREE(cache);
+ return -ENOMEM;
+ }
+
+ if (csr1212_attach_keyval_to_directory(csr->root_kv, cache->ext_rom) !=
+ CSR1212_SUCCESS) {
+ csr1212_release_keyval(cache->ext_rom);
+ csr->ops->release_addr(csr_addr, csr->private);
+ CSR1212_FREE(cache);
+ return -ENOMEM;
+ }
+ cache->ext_rom->offset = csr_addr - CSR1212_REGISTER_SPACE_BASE;
+ cache->ext_rom->value.leaf.len = -1;
+ cache->ext_rom->value.leaf.data = cache->data;
+
+ /* Add cache to tail of cache list */
+ cache->prev = csr->cache_tail;
+ csr->cache_tail->next = cache;
+ csr->cache_tail = cache;
+ return CSR1212_SUCCESS;
+}
+
+static void csr1212_remove_cache(struct csr1212_csr *csr,
+ struct csr1212_csr_rom_cache *cache)
+{
+ if (csr->cache_head == cache)
+ csr->cache_head = cache->next;
+ if (csr->cache_tail == cache)
+ csr->cache_tail = cache->prev;
+
+ if (cache->prev)
+ cache->prev->next = cache->next;
+ if (cache->next)
+ cache->next->prev = cache->prev;
+
+ if (cache->ext_rom) {
+ csr1212_detach_keyval_from_directory(csr->root_kv,
+ cache->ext_rom);
+ csr1212_release_keyval(cache->ext_rom);
+ }
+
+ CSR1212_FREE(cache);
+}
+
+static int csr1212_generate_layout_subdir(struct csr1212_keyval *dir,
+ struct csr1212_keyval **layout_tail)
+{
+ struct csr1212_dentry *dentry;
+ struct csr1212_keyval *dkv;
+ struct csr1212_keyval *last_extkey_spec = NULL;
+ struct csr1212_keyval *last_extkey = NULL;
+ int num_entries = 0;
+
+ for (dentry = dir->value.directory.dentries_head; dentry;
+ dentry = dentry->next) {
+ for (dkv = dentry->kv; dkv; dkv = dkv->associate) {
+ /* Special Case: Extended Key Specifier_ID */
+ if (dkv->key.id ==
+ CSR1212_KV_ID_EXTENDED_KEY_SPECIFIER_ID) {
+ if (last_extkey_spec == NULL)
+ last_extkey_spec = dkv;
+ else if (dkv->value.immediate !=
+ last_extkey_spec->value.immediate)
+ last_extkey_spec = dkv;
+ else
+ continue;
+ /* Special Case: Extended Key */
+ } else if (dkv->key.id == CSR1212_KV_ID_EXTENDED_KEY) {
+ if (last_extkey == NULL)
+ last_extkey = dkv;
+ else if (dkv->value.immediate !=
+ last_extkey->value.immediate)
+ last_extkey = dkv;
+ else
+ continue;
+ }
+
+ num_entries += 1;
+
+ switch (dkv->key.type) {
+ default:
+ case CSR1212_KV_TYPE_IMMEDIATE:
+ case CSR1212_KV_TYPE_CSR_OFFSET:
+ break;
+ case CSR1212_KV_TYPE_LEAF:
+ case CSR1212_KV_TYPE_DIRECTORY:
+ /* Remove from list */
+ if (dkv->prev && (dkv->prev->next == dkv))
+ dkv->prev->next = dkv->next;
+ if (dkv->next && (dkv->next->prev == dkv))
+ dkv->next->prev = dkv->prev;
+ //if (dkv == *layout_tail)
+ // *layout_tail = dkv->prev;
+
+ /* Special case: Extended ROM leafs */
+ if (dkv->key.id == CSR1212_KV_ID_EXTENDED_ROM) {
+ dkv->value.leaf.len = -1;
+ /* Don't add Extended ROM leafs in the
+ * layout list, they are handled
+ * differently. */
+ break;
+ }
+
+ /* Add to tail of list */
+ dkv->next = NULL;
+ dkv->prev = *layout_tail;
+ (*layout_tail)->next = dkv;
+ *layout_tail = dkv;
+ break;
+ }
+ }
+ }
+ return num_entries;
+}
+
+static size_t csr1212_generate_layout_order(struct csr1212_keyval *kv)
+{
+ struct csr1212_keyval *ltail = kv;
+ size_t agg_size = 0;
+
+ while (kv) {
+ switch (kv->key.type) {
+ case CSR1212_KV_TYPE_LEAF:
+ /* Add 1 quadlet for crc/len field */
+ agg_size += kv->value.leaf.len + 1;
+ break;
+
+ case CSR1212_KV_TYPE_DIRECTORY:
+ kv->value.directory.len =
+ csr1212_generate_layout_subdir(kv, &ltail);
+ /* Add 1 quadlet for crc/len field */
+ agg_size += kv->value.directory.len + 1;
+ break;
+ }
+ kv = kv->next;
+ }
+ return quads_to_bytes(agg_size);
+}
+
+static struct csr1212_keyval *
+csr1212_generate_positions(struct csr1212_csr_rom_cache *cache,
+ struct csr1212_keyval *start_kv, int start_pos)
+{
+ struct csr1212_keyval *kv = start_kv;
+ struct csr1212_keyval *okv = start_kv;
+ int pos = start_pos;
+ int kv_len = 0, okv_len = 0;
+
+ cache->layout_head = kv;
+
+ while (kv && pos < cache->size) {
+ /* Special case: Extended ROM leafs */
+ if (kv->key.id != CSR1212_KV_ID_EXTENDED_ROM)
+ kv->offset = cache->offset + pos;
+
+ switch (kv->key.type) {
+ case CSR1212_KV_TYPE_LEAF:
+ kv_len = kv->value.leaf.len;
+ break;
+
+ case CSR1212_KV_TYPE_DIRECTORY:
+ kv_len = kv->value.directory.len;
+ break;
+
+ default:
+ /* Should never get here */
+ WARN_ON(1);
+ break;
+ }
+
+ pos += quads_to_bytes(kv_len + 1);
+
+ if (pos <= cache->size) {
+ okv = kv;
+ okv_len = kv_len;
+ kv = kv->next;
+ }
+ }
+
+ cache->layout_tail = okv;
+ cache->len = okv->offset - cache->offset + quads_to_bytes(okv_len + 1);
+
+ return kv;
+}
+
+#define CSR1212_KV_KEY_SHIFT 24
+#define CSR1212_KV_KEY_TYPE_SHIFT 6
+#define CSR1212_KV_KEY_ID_MASK 0x3f
+#define CSR1212_KV_KEY_TYPE_MASK 0x3 /* after shift */
+
+static void
+csr1212_generate_tree_subdir(struct csr1212_keyval *dir, u32 *data_buffer)
+{
+ struct csr1212_dentry *dentry;
+ struct csr1212_keyval *last_extkey_spec = NULL;
+ struct csr1212_keyval *last_extkey = NULL;
+ int index = 0;
+
+ for (dentry = dir->value.directory.dentries_head;
+ dentry;
+ dentry = dentry->next) {
+ struct csr1212_keyval *a;
+
+ for (a = dentry->kv; a; a = a->associate) {
+ u32 value = 0;
+
+ /* Special Case: Extended Key Specifier_ID */
+ if (a->key.id ==
+ CSR1212_KV_ID_EXTENDED_KEY_SPECIFIER_ID) {
+ if (last_extkey_spec == NULL)
+ last_extkey_spec = a;
+ else if (a->value.immediate !=
+ last_extkey_spec->value.immediate)
+ last_extkey_spec = a;
+ else
+ continue;
+
+ /* Special Case: Extended Key */
+ } else if (a->key.id == CSR1212_KV_ID_EXTENDED_KEY) {
+ if (last_extkey == NULL)
+ last_extkey = a;
+ else if (a->value.immediate !=
+ last_extkey->value.immediate)
+ last_extkey = a;
+ else
+ continue;
+ }
+
+ switch (a->key.type) {
+ case CSR1212_KV_TYPE_IMMEDIATE:
+ value = a->value.immediate;
+ break;
+ case CSR1212_KV_TYPE_CSR_OFFSET:
+ value = a->value.csr_offset;
+ break;
+ case CSR1212_KV_TYPE_LEAF:
+ value = a->offset;
+ value -= dir->offset + quads_to_bytes(1+index);
+ value = bytes_to_quads(value);
+ break;
+ case CSR1212_KV_TYPE_DIRECTORY:
+ value = a->offset;
+ value -= dir->offset + quads_to_bytes(1+index);
+ value = bytes_to_quads(value);
+ break;
+ default:
+ /* Should never get here */
+ WARN_ON(1);
+ break;
+ }
+
+ value |= (a->key.id & CSR1212_KV_KEY_ID_MASK) <<
+ CSR1212_KV_KEY_SHIFT;
+ value |= (a->key.type & CSR1212_KV_KEY_TYPE_MASK) <<
+ (CSR1212_KV_KEY_SHIFT +
+ CSR1212_KV_KEY_TYPE_SHIFT);
+ data_buffer[index] = cpu_to_be32(value);
+ index++;
+ }
+ }
+}
+
+struct csr1212_keyval_img {
+ u16 length;
+ u16 crc;
+
+ /* Must be last */
+ u32 data[0]; /* older gcc can't handle [] which is standard */
+};
+
+static void csr1212_fill_cache(struct csr1212_csr_rom_cache *cache)
+{
+ struct csr1212_keyval *kv, *nkv;
+ struct csr1212_keyval_img *kvi;
+
+ for (kv = cache->layout_head;
+ kv != cache->layout_tail->next;
+ kv = nkv) {
+ kvi = (struct csr1212_keyval_img *)(cache->data +
+ bytes_to_quads(kv->offset - cache->offset));
+ switch (kv->key.type) {
+ default:
+ case CSR1212_KV_TYPE_IMMEDIATE:
+ case CSR1212_KV_TYPE_CSR_OFFSET:
+ /* Should never get here */
+ WARN_ON(1);
+ break;
+
+ case CSR1212_KV_TYPE_LEAF:
+ /* Don't copy over Extended ROM areas, they are
+ * already filled out! */
+ if (kv->key.id != CSR1212_KV_ID_EXTENDED_ROM)
+ memcpy(kvi->data, kv->value.leaf.data,
+ quads_to_bytes(kv->value.leaf.len));
+
+ kvi->length = cpu_to_be16(kv->value.leaf.len);
+ kvi->crc = csr1212_crc16(kvi->data, kv->value.leaf.len);
+ break;
+
+ case CSR1212_KV_TYPE_DIRECTORY:
+ csr1212_generate_tree_subdir(kv, kvi->data);
+
+ kvi->length = cpu_to_be16(kv->value.directory.len);
+ kvi->crc = csr1212_crc16(kvi->data,
+ kv->value.directory.len);
+ break;
+ }
+
+ nkv = kv->next;
+ if (kv->prev)
+ kv->prev->next = NULL;
+ if (kv->next)
+ kv->next->prev = NULL;
+ kv->prev = NULL;
+ kv->next = NULL;
+ }
+}
+
+/* This size is arbitrarily chosen.
+ * The struct overhead is subtracted for more economic allocations. */
+#define CSR1212_EXTENDED_ROM_SIZE (2048 - sizeof(struct csr1212_csr_rom_cache))
+
+int csr1212_generate_csr_image(struct csr1212_csr *csr)
+{
+ struct csr1212_bus_info_block_img *bi;
+ struct csr1212_csr_rom_cache *cache;
+ struct csr1212_keyval *kv;
+ size_t agg_size;
+ int ret;
+ int init_offset;
+
+ BUG_ON(!csr);
+
+ cache = csr->cache_head;
+
+ bi = (struct csr1212_bus_info_block_img*)cache->data;
+
+ bi->length = bytes_to_quads(csr->bus_info_len) - 1;
+ bi->crc_length = bi->length;
+ bi->crc = csr1212_crc16(bi->data, bi->crc_length);
+
+ csr->root_kv->next = NULL;
+ csr->root_kv->prev = NULL;
+
+ agg_size = csr1212_generate_layout_order(csr->root_kv);
+
+ init_offset = csr->bus_info_len;
+
+ for (kv = csr->root_kv, cache = csr->cache_head;
+ kv;
+ cache = cache->next) {
+ if (!cache) {
+ /* Estimate approximate number of additional cache
+ * regions needed (it assumes that the cache holding
+ * the first 1K Config ROM space always exists). */
+ int est_c = agg_size / (CSR1212_EXTENDED_ROM_SIZE -
+ (2 * sizeof(u32))) + 1;
+
+ /* Add additional cache regions, extras will be
+ * removed later */
+ for (; est_c; est_c--) {
+ ret = csr1212_append_new_cache(csr,
+ CSR1212_EXTENDED_ROM_SIZE);
+ if (ret != CSR1212_SUCCESS)
+ return ret;
+ }
+ /* Need to re-layout for additional cache regions */
+ agg_size = csr1212_generate_layout_order(csr->root_kv);
+ kv = csr->root_kv;
+ cache = csr->cache_head;
+ init_offset = csr->bus_info_len;
+ }
+ kv = csr1212_generate_positions(cache, kv, init_offset);
+ agg_size -= cache->len;
+ init_offset = sizeof(u32);
+ }
+
+ /* Remove unused, excess cache regions */
+ while (cache) {
+ struct csr1212_csr_rom_cache *oc = cache;
+
+ cache = cache->next;
+ csr1212_remove_cache(csr, oc);
+ }
+
+ /* Go through the list backward so that when done, the correct CRC
+ * will be calculated for the Extended ROM areas. */
+ for (cache = csr->cache_tail; cache; cache = cache->prev) {
+ /* Only Extended ROM caches should have this set. */
+ if (cache->ext_rom) {
+ int leaf_size;
+
+ /* Make sure the Extended ROM leaf is a multiple of
+ * max_rom in size. */
+ BUG_ON(csr->max_rom < 1);
+ leaf_size = (cache->len + (csr->max_rom - 1)) &
+ ~(csr->max_rom - 1);
+
+ /* Zero out the unused ROM region */
+ memset(cache->data + bytes_to_quads(cache->len), 0x00,
+ leaf_size - cache->len);
+
+ /* Subtract leaf header */
+ leaf_size -= sizeof(u32);
+
+ /* Update the Extended ROM leaf length */
+ cache->ext_rom->value.leaf.len =
+ bytes_to_quads(leaf_size);
+ } else {
+ /* Zero out the unused ROM region */
+ memset(cache->data + bytes_to_quads(cache->len), 0x00,
+ cache->size - cache->len);
+ }
+
+ /* Copy the data into the cache buffer */
+ csr1212_fill_cache(cache);
+
+ if (cache != csr->cache_head) {
+ /* Set the length and CRC of the extended ROM. */
+ struct csr1212_keyval_img *kvi =
+ (struct csr1212_keyval_img*)cache->data;
+ u16 len = bytes_to_quads(cache->len) - 1;
+
+ kvi->length = cpu_to_be16(len);
+ kvi->crc = csr1212_crc16(kvi->data, len);
+ }
+ }
+
+ return CSR1212_SUCCESS;
+}
+
+int csr1212_read(struct csr1212_csr *csr, u32 offset, void *buffer, u32 len)
+{
+ struct csr1212_csr_rom_cache *cache;
+
+ for (cache = csr->cache_head; cache; cache = cache->next)
+ if (offset >= cache->offset &&
+ (offset + len) <= (cache->offset + cache->size)) {
+ memcpy(buffer, &cache->data[
+ bytes_to_quads(offset - cache->offset)],
+ len);
+ return CSR1212_SUCCESS;
+ }
+
+ return -ENOENT;
+}
+
+/*
+ * Apparently there are many different wrong implementations of the CRC
+ * algorithm. We don't fail, we just warn... approximately once per GUID.
+ */
+static void
+csr1212_check_crc(const u32 *buffer, size_t length, u16 crc, __be32 *guid)
+{
+ static u64 last_bad_eui64;
+ u64 eui64 = ((u64)be32_to_cpu(guid[0]) << 32) | be32_to_cpu(guid[1]);
+
+ if (csr1212_crc16(buffer, length) == crc ||
+ csr1212_msft_crc16(buffer, length) == crc ||
+ eui64 == last_bad_eui64)
+ return;
+
+ printk(KERN_DEBUG "ieee1394: config ROM CRC error\n");
+ last_bad_eui64 = eui64;
+}
+
+/* Parse a chunk of data as a Config ROM */
+
+static int csr1212_parse_bus_info_block(struct csr1212_csr *csr)
+{
+ struct csr1212_bus_info_block_img *bi;
+ struct csr1212_cache_region *cr;
+ int i;
+ int ret;
+
+ /* IEEE 1212 says that the entire bus info block should be readable in
+ * a single transaction regardless of the max_rom value.
+ * Unfortunately, many IEEE 1394 devices do not abide by that, so the
+ * bus info block will be read 1 quadlet at a time. The rest of the
+ * ConfigROM will be read according to the max_rom field. */
+ for (i = 0; i < csr->bus_info_len; i += sizeof(u32)) {
+ ret = csr->ops->bus_read(csr, CSR1212_CONFIG_ROM_SPACE_BASE + i,
+ sizeof(u32), &csr->cache_head->data[bytes_to_quads(i)],
+ csr->private);
+ if (ret != CSR1212_SUCCESS)
+ return ret;
+
+ /* check ROM header's info_length */
+ if (i == 0 &&
+ be32_to_cpu(csr->cache_head->data[0]) >> 24 !=
+ bytes_to_quads(csr->bus_info_len) - 1)
+ return -EINVAL;
+ }
+
+ bi = (struct csr1212_bus_info_block_img*)csr->cache_head->data;
+ csr->crc_len = quads_to_bytes(bi->crc_length);
+
+ /* IEEE 1212 recommends that crc_len be equal to bus_info_len, but that
+ * is not always the case, so read the rest of the crc area 1 quadlet at
+ * a time. */
+ for (i = csr->bus_info_len; i <= csr->crc_len; i += sizeof(u32)) {
+ ret = csr->ops->bus_read(csr, CSR1212_CONFIG_ROM_SPACE_BASE + i,
+ sizeof(u32), &csr->cache_head->data[bytes_to_quads(i)],
+ csr->private);
+ if (ret != CSR1212_SUCCESS)
+ return ret;
+ }
+
+ csr1212_check_crc(bi->data, bi->crc_length, bi->crc,
+ &csr->bus_info_data[3]);
+
+ cr = CSR1212_MALLOC(sizeof(*cr));
+ if (!cr)
+ return -ENOMEM;
+
+ cr->next = NULL;
+ cr->prev = NULL;
+ cr->offset_start = 0;
+ cr->offset_end = csr->crc_len + 4;
+
+ csr->cache_head->filled_head = cr;
+ csr->cache_head->filled_tail = cr;
+
+ return CSR1212_SUCCESS;
+}
+
+#define CSR1212_KV_KEY(q) (be32_to_cpu(q) >> CSR1212_KV_KEY_SHIFT)
+#define CSR1212_KV_KEY_TYPE(q) (CSR1212_KV_KEY(q) >> CSR1212_KV_KEY_TYPE_SHIFT)
+#define CSR1212_KV_KEY_ID(q) (CSR1212_KV_KEY(q) & CSR1212_KV_KEY_ID_MASK)
+#define CSR1212_KV_VAL_MASK 0xffffff
+#define CSR1212_KV_VAL(q) (be32_to_cpu(q) & CSR1212_KV_VAL_MASK)
+
+static int
+csr1212_parse_dir_entry(struct csr1212_keyval *dir, u32 ki, u32 kv_pos)
+{
+ int ret = CSR1212_SUCCESS;
+ struct csr1212_keyval *k = NULL;
+ u32 offset;
+ bool keep_keyval = true;
+
+ switch (CSR1212_KV_KEY_TYPE(ki)) {
+ case CSR1212_KV_TYPE_IMMEDIATE:
+ k = csr1212_new_immediate(CSR1212_KV_KEY_ID(ki),
+ CSR1212_KV_VAL(ki));
+ if (!k) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ /* Don't keep local reference when parsing. */
+ keep_keyval = false;
+ break;
+
+ case CSR1212_KV_TYPE_CSR_OFFSET:
+ k = csr1212_new_csr_offset(CSR1212_KV_KEY_ID(ki),
+ CSR1212_KV_VAL(ki));
+ if (!k) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ /* Don't keep local reference when parsing. */
+ keep_keyval = false;
+ break;
+
+ default:
+ /* Compute the offset from 0xffff f000 0000. */
+ offset = quads_to_bytes(CSR1212_KV_VAL(ki)) + kv_pos;
+ if (offset == kv_pos) {
+ /* Uh-oh. Can't have a relative offset of 0 for Leaves
+ * or Directories. The Config ROM image is most likely
+ * messed up, so we'll just abort here. */
+ ret = -EIO;
+ goto out;
+ }
+
+ k = csr1212_find_keyval_offset(dir, offset);
+
+ if (k)
+ break; /* Found it. */
+
+ if (CSR1212_KV_KEY_TYPE(ki) == CSR1212_KV_TYPE_DIRECTORY)
+ k = csr1212_new_directory(CSR1212_KV_KEY_ID(ki));
+ else
+ k = csr1212_new_leaf(CSR1212_KV_KEY_ID(ki), NULL, 0);
+
+ if (!k) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ /* Don't keep local reference when parsing. */
+ keep_keyval = false;
+ /* Contents not read yet so it's not valid. */
+ k->valid = 0;
+ k->offset = offset;
+
+ k->prev = dir;
+ k->next = dir->next;
+ dir->next->prev = k;
+ dir->next = k;
+ }
+ ret = __csr1212_attach_keyval_to_directory(dir, k, keep_keyval);
+out:
+ if (ret != CSR1212_SUCCESS && k != NULL)
+ free_keyval(k);
+ return ret;
+}
+
+int csr1212_parse_keyval(struct csr1212_keyval *kv,
+ struct csr1212_csr_rom_cache *cache)
+{
+ struct csr1212_keyval_img *kvi;
+ int i;
+ int ret = CSR1212_SUCCESS;
+ int kvi_len;
+
+ kvi = (struct csr1212_keyval_img*)
+ &cache->data[bytes_to_quads(kv->offset - cache->offset)];
+ kvi_len = be16_to_cpu(kvi->length);
+
+ /* GUID is wrong in here in case of extended ROM. We don't care. */
+ csr1212_check_crc(kvi->data, kvi_len, kvi->crc, &cache->data[3]);
+
+ switch (kv->key.type) {
+ case CSR1212_KV_TYPE_DIRECTORY:
+ for (i = 0; i < kvi_len; i++) {
+ u32 ki = kvi->data[i];
+
+ /* Some devices put null entries in their unit
+ * directories. If we come across such an entry,
+ * then skip it. */
+ if (ki == 0x0)
+ continue;
+ ret = csr1212_parse_dir_entry(kv, ki,
+ kv->offset + quads_to_bytes(i + 1));
+ }
+ kv->value.directory.len = kvi_len;
+ break;
+
+ case CSR1212_KV_TYPE_LEAF:
+ if (kv->key.id != CSR1212_KV_ID_EXTENDED_ROM) {
+ size_t size = quads_to_bytes(kvi_len);
+
+ kv->value.leaf.data = CSR1212_MALLOC(size);
+ if (!kv->value.leaf.data) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ kv->value.leaf.len = kvi_len;
+ memcpy(kv->value.leaf.data, kvi->data, size);
+ }
+ break;
+ }
+
+ kv->valid = 1;
+out:
+ return ret;
+}
+
+static int
+csr1212_read_keyval(struct csr1212_csr *csr, struct csr1212_keyval *kv)
+{
+ struct csr1212_cache_region *cr, *ncr, *newcr = NULL;
+ struct csr1212_keyval_img *kvi = NULL;
+ struct csr1212_csr_rom_cache *cache;
+ int cache_index;
+ u64 addr;
+ u32 *cache_ptr;
+ u16 kv_len = 0;
+
+ BUG_ON(!csr || !kv || csr->max_rom < 1);
+
+ /* First find which cache the data should be in (or go in if not read
+ * yet). */
+ for (cache = csr->cache_head; cache; cache = cache->next)
+ if (kv->offset >= cache->offset &&
+ kv->offset < (cache->offset + cache->size))
+ break;
+
+ if (!cache) {
+ u32 q, cache_size;
+
+ /* Only create a new cache for Extended ROM leaves. */
+ if (kv->key.id != CSR1212_KV_ID_EXTENDED_ROM)
+ return -EINVAL;
+
+ if (csr->ops->bus_read(csr,
+ CSR1212_REGISTER_SPACE_BASE + kv->offset,
+ sizeof(u32), &q, csr->private))
+ return -EIO;
+
+ kv->value.leaf.len = be32_to_cpu(q) >> 16;
+
+ cache_size = (quads_to_bytes(kv->value.leaf.len + 1) +
+ (csr->max_rom - 1)) & ~(csr->max_rom - 1);
+
+ cache = csr1212_rom_cache_malloc(kv->offset, cache_size);
+ if (!cache)
+ return -ENOMEM;
+
+ kv->value.leaf.data = &cache->data[1];
+ csr->cache_tail->next = cache;
+ cache->prev = csr->cache_tail;
+ cache->next = NULL;
+ csr->cache_tail = cache;
+ cache->filled_head =
+ CSR1212_MALLOC(sizeof(*cache->filled_head));
+ if (!cache->filled_head)
+ return -ENOMEM;
+
+ cache->filled_head->offset_start = 0;
+ cache->filled_head->offset_end = sizeof(u32);
+ cache->filled_tail = cache->filled_head;
+ cache->filled_head->next = NULL;
+ cache->filled_head->prev = NULL;
+ cache->data[0] = q;
+
+ /* Don't read the entire extended ROM now. Pieces of it will
+ * be read when entries inside it are read. */
+ return csr1212_parse_keyval(kv, cache);
+ }
+
+ cache_index = kv->offset - cache->offset;
+
+ /* Now seach read portions of the cache to see if it is there. */
+ for (cr = cache->filled_head; cr; cr = cr->next) {
+ if (cache_index < cr->offset_start) {
+ newcr = CSR1212_MALLOC(sizeof(*newcr));
+ if (!newcr)
+ return -ENOMEM;
+
+ newcr->offset_start = cache_index & ~(csr->max_rom - 1);
+ newcr->offset_end = newcr->offset_start;
+ newcr->next = cr;
+ newcr->prev = cr->prev;
+ cr->prev = newcr;
+ cr = newcr;
+ break;
+ } else if ((cache_index >= cr->offset_start) &&
+ (cache_index < cr->offset_end)) {
+ kvi = (struct csr1212_keyval_img*)
+ (&cache->data[bytes_to_quads(cache_index)]);
+ kv_len = quads_to_bytes(be16_to_cpu(kvi->length) + 1);
+ break;
+ } else if (cache_index == cr->offset_end) {
+ break;
+ }
+ }
+
+ if (!cr) {
+ cr = cache->filled_tail;
+ newcr = CSR1212_MALLOC(sizeof(*newcr));
+ if (!newcr)
+ return -ENOMEM;
+
+ newcr->offset_start = cache_index & ~(csr->max_rom - 1);
+ newcr->offset_end = newcr->offset_start;
+ newcr->prev = cr;
+ newcr->next = cr->next;
+ cr->next = newcr;
+ cr = newcr;
+ cache->filled_tail = newcr;
+ }
+
+ while(!kvi || cr->offset_end < cache_index + kv_len) {
+ cache_ptr = &cache->data[bytes_to_quads(cr->offset_end &
+ ~(csr->max_rom - 1))];
+
+ addr = (CSR1212_CSR_ARCH_REG_SPACE_BASE + cache->offset +
+ cr->offset_end) & ~(csr->max_rom - 1);
+
+ if (csr->ops->bus_read(csr, addr, csr->max_rom, cache_ptr,
+ csr->private)) {
+ if (csr->max_rom == 4)
+ /* We've got problems! */
+ return -EIO;
+
+ /* Apperently the max_rom value was a lie, set it to
+ * do quadlet reads and try again. */
+ csr->max_rom = 4;
+ continue;
+ }
+
+ cr->offset_end += csr->max_rom - (cr->offset_end &
+ (csr->max_rom - 1));
+
+ if (!kvi && (cr->offset_end > cache_index)) {
+ kvi = (struct csr1212_keyval_img*)
+ (&cache->data[bytes_to_quads(cache_index)]);
+ kv_len = quads_to_bytes(be16_to_cpu(kvi->length) + 1);
+ }
+
+ if ((kv_len + (kv->offset - cache->offset)) > cache->size) {
+ /* The Leaf or Directory claims its length extends
+ * beyond the ConfigROM image region and thus beyond the
+ * end of our cache region. Therefore, we abort now
+ * rather than seg faulting later. */
+ return -EIO;
+ }
+
+ ncr = cr->next;
+
+ if (ncr && (cr->offset_end >= ncr->offset_start)) {
+ /* consolidate region entries */
+ ncr->offset_start = cr->offset_start;
+
+ if (cr->prev)
+ cr->prev->next = cr->next;
+ ncr->prev = cr->prev;
+ if (cache->filled_head == cr)
+ cache->filled_head = ncr;
+ CSR1212_FREE(cr);
+ cr = ncr;
+ }
+ }
+
+ return csr1212_parse_keyval(kv, cache);
+}
+
+struct csr1212_keyval *
+csr1212_get_keyval(struct csr1212_csr *csr, struct csr1212_keyval *kv)
+{
+ if (!kv)
+ return NULL;
+ if (!kv->valid)
+ if (csr1212_read_keyval(csr, kv) != CSR1212_SUCCESS)
+ return NULL;
+ return kv;
+}
+
+int csr1212_parse_csr(struct csr1212_csr *csr)
+{
+ static const int mr_map[] = { 4, 64, 1024, 0 };
+ struct csr1212_dentry *dentry;
+ int ret;
+
+ BUG_ON(!csr || !csr->ops || !csr->ops->bus_read);
+
+ ret = csr1212_parse_bus_info_block(csr);
+ if (ret != CSR1212_SUCCESS)
+ return ret;
+
+ if (!csr->ops->get_max_rom) {
+ csr->max_rom = mr_map[0]; /* default value */
+ } else {
+ int i = csr->ops->get_max_rom(csr->bus_info_data,
+ csr->private);
+ if (i & ~0x3)
+ return -EINVAL;
+ csr->max_rom = mr_map[i];
+ }
+
+ csr->cache_head->layout_head = csr->root_kv;
+ csr->cache_head->layout_tail = csr->root_kv;
+
+ csr->root_kv->offset = (CSR1212_CONFIG_ROM_SPACE_BASE & 0xffff) +
+ csr->bus_info_len;
+
+ csr->root_kv->valid = 0;
+ csr->root_kv->next = csr->root_kv;
+ csr->root_kv->prev = csr->root_kv;
+ ret = csr1212_read_keyval(csr, csr->root_kv);
+ if (ret != CSR1212_SUCCESS)
+ return ret;
+
+ /* Scan through the Root directory finding all extended ROM regions
+ * and make cache regions for them */
+ for (dentry = csr->root_kv->value.directory.dentries_head;
+ dentry; dentry = dentry->next) {
+ if (dentry->kv->key.id == CSR1212_KV_ID_EXTENDED_ROM &&
+ !dentry->kv->valid) {
+ ret = csr1212_read_keyval(csr, dentry->kv);
+ if (ret != CSR1212_SUCCESS)
+ return ret;
+ }
+ }
+
+ return CSR1212_SUCCESS;
+}
diff --git a/drivers/ieee1394/csr1212.h b/drivers/ieee1394/csr1212.h
new file mode 100644
index 0000000..043039f
--- /dev/null
+++ b/drivers/ieee1394/csr1212.h
@@ -0,0 +1,388 @@
+/*
+ * csr1212.h -- IEEE 1212 Control and Status Register support for Linux
+ *
+ * Copyright (C) 2003 Francois Retief <fgretief@sun.ac.za>
+ * Steve Kinneberg <kinnebergsteve@acmsystems.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __CSR1212_H__
+#define __CSR1212_H__
+
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <asm/atomic.h>
+
+#define CSR1212_MALLOC(size) kmalloc((size), GFP_KERNEL)
+#define CSR1212_FREE(ptr) kfree(ptr)
+
+#define CSR1212_SUCCESS (0)
+
+
+/* CSR 1212 key types */
+#define CSR1212_KV_TYPE_IMMEDIATE 0
+#define CSR1212_KV_TYPE_CSR_OFFSET 1
+#define CSR1212_KV_TYPE_LEAF 2
+#define CSR1212_KV_TYPE_DIRECTORY 3
+
+
+/* CSR 1212 key ids */
+#define CSR1212_KV_ID_DESCRIPTOR 0x01
+#define CSR1212_KV_ID_BUS_DEPENDENT_INFO 0x02
+#define CSR1212_KV_ID_VENDOR 0x03
+#define CSR1212_KV_ID_HARDWARE_VERSION 0x04
+#define CSR1212_KV_ID_MODULE 0x07
+#define CSR1212_KV_ID_NODE_CAPABILITIES 0x0C
+#define CSR1212_KV_ID_EUI_64 0x0D
+#define CSR1212_KV_ID_UNIT 0x11
+#define CSR1212_KV_ID_SPECIFIER_ID 0x12
+#define CSR1212_KV_ID_VERSION 0x13
+#define CSR1212_KV_ID_DEPENDENT_INFO 0x14
+#define CSR1212_KV_ID_UNIT_LOCATION 0x15
+#define CSR1212_KV_ID_MODEL 0x17
+#define CSR1212_KV_ID_INSTANCE 0x18
+#define CSR1212_KV_ID_KEYWORD 0x19
+#define CSR1212_KV_ID_FEATURE 0x1A
+#define CSR1212_KV_ID_EXTENDED_ROM 0x1B
+#define CSR1212_KV_ID_EXTENDED_KEY_SPECIFIER_ID 0x1C
+#define CSR1212_KV_ID_EXTENDED_KEY 0x1D
+#define CSR1212_KV_ID_EXTENDED_DATA 0x1E
+#define CSR1212_KV_ID_MODIFIABLE_DESCRIPTOR 0x1F
+#define CSR1212_KV_ID_DIRECTORY_ID 0x20
+#define CSR1212_KV_ID_REVISION 0x21
+
+
+/* IEEE 1212 Address space map */
+#define CSR1212_ALL_SPACE_BASE (0x000000000000ULL)
+#define CSR1212_ALL_SPACE_SIZE (1ULL << 48)
+#define CSR1212_ALL_SPACE_END (CSR1212_ALL_SPACE_BASE + CSR1212_ALL_SPACE_SIZE)
+
+#define CSR1212_MEMORY_SPACE_BASE (0x000000000000ULL)
+#define CSR1212_MEMORY_SPACE_SIZE ((256ULL * (1ULL << 40)) - (512ULL * (1ULL << 20)))
+#define CSR1212_MEMORY_SPACE_END (CSR1212_MEMORY_SPACE_BASE + CSR1212_MEMORY_SPACE_SIZE)
+
+#define CSR1212_PRIVATE_SPACE_BASE (0xffffe0000000ULL)
+#define CSR1212_PRIVATE_SPACE_SIZE (256ULL * (1ULL << 20))
+#define CSR1212_PRIVATE_SPACE_END (CSR1212_PRIVATE_SPACE_BASE + CSR1212_PRIVATE_SPACE_SIZE)
+
+#define CSR1212_REGISTER_SPACE_BASE (0xfffff0000000ULL)
+#define CSR1212_REGISTER_SPACE_SIZE (256ULL * (1ULL << 20))
+#define CSR1212_REGISTER_SPACE_END (CSR1212_REGISTER_SPACE_BASE + CSR1212_REGISTER_SPACE_SIZE)
+
+#define CSR1212_CSR_ARCH_REG_SPACE_BASE (0xfffff0000000ULL)
+#define CSR1212_CSR_ARCH_REG_SPACE_SIZE (512)
+#define CSR1212_CSR_ARCH_REG_SPACE_END (CSR1212_CSR_ARCH_REG_SPACE_BASE + CSR1212_CSR_ARCH_REG_SPACE_SIZE)
+#define CSR1212_CSR_ARCH_REG_SPACE_OFFSET (CSR1212_CSR_ARCH_REG_SPACE_BASE - CSR1212_REGISTER_SPACE_BASE)
+
+#define CSR1212_CSR_BUS_DEP_REG_SPACE_BASE (0xfffff0000200ULL)
+#define CSR1212_CSR_BUS_DEP_REG_SPACE_SIZE (512)
+#define CSR1212_CSR_BUS_DEP_REG_SPACE_END (CSR1212_CSR_BUS_DEP_REG_SPACE_BASE + CSR1212_CSR_BUS_DEP_REG_SPACE_SIZE)
+#define CSR1212_CSR_BUS_DEP_REG_SPACE_OFFSET (CSR1212_CSR_BUS_DEP_REG_SPACE_BASE - CSR1212_REGISTER_SPACE_BASE)
+
+#define CSR1212_CONFIG_ROM_SPACE_BASE (0xfffff0000400ULL)
+#define CSR1212_CONFIG_ROM_SPACE_SIZE (1024)
+#define CSR1212_CONFIG_ROM_SPACE_END (CSR1212_CONFIG_ROM_SPACE_BASE + CSR1212_CONFIG_ROM_SPACE_SIZE)
+#define CSR1212_CONFIG_ROM_SPACE_OFFSET (CSR1212_CONFIG_ROM_SPACE_BASE - CSR1212_REGISTER_SPACE_BASE)
+
+#define CSR1212_UNITS_SPACE_BASE (0xfffff0000800ULL)
+#define CSR1212_UNITS_SPACE_SIZE ((256ULL * (1ULL << 20)) - 2048)
+#define CSR1212_UNITS_SPACE_END (CSR1212_UNITS_SPACE_BASE + CSR1212_UNITS_SPACE_SIZE)
+#define CSR1212_UNITS_SPACE_OFFSET (CSR1212_UNITS_SPACE_BASE - CSR1212_REGISTER_SPACE_BASE)
+
+#define CSR1212_INVALID_ADDR_SPACE -1
+
+
+/* Config ROM image structures */
+struct csr1212_bus_info_block_img {
+ u8 length;
+ u8 crc_length;
+ u16 crc;
+
+ /* Must be last */
+ u32 data[0]; /* older gcc can't handle [] which is standard */
+};
+
+struct csr1212_leaf {
+ int len;
+ u32 *data;
+};
+
+struct csr1212_dentry {
+ struct csr1212_dentry *next, *prev;
+ struct csr1212_keyval *kv;
+};
+
+struct csr1212_directory {
+ int len;
+ struct csr1212_dentry *dentries_head, *dentries_tail;
+};
+
+struct csr1212_keyval {
+ struct {
+ u8 type;
+ u8 id;
+ } key;
+ union {
+ u32 immediate;
+ u32 csr_offset;
+ struct csr1212_leaf leaf;
+ struct csr1212_directory directory;
+ } value;
+ struct csr1212_keyval *associate;
+ atomic_t refcnt;
+
+ /* used in generating and/or parsing CSR image */
+ struct csr1212_keyval *next, *prev; /* flat list of CSR elements */
+ u32 offset; /* position in CSR from 0xffff f000 0000 */
+ u8 valid; /* flag indicating keyval has valid data*/
+};
+
+
+struct csr1212_cache_region {
+ struct csr1212_cache_region *next, *prev;
+ u32 offset_start; /* inclusive */
+ u32 offset_end; /* exclusive */
+};
+
+struct csr1212_csr_rom_cache {
+ struct csr1212_csr_rom_cache *next, *prev;
+ struct csr1212_cache_region *filled_head, *filled_tail;
+ struct csr1212_keyval *layout_head, *layout_tail;
+ size_t size;
+ u32 offset;
+ struct csr1212_keyval *ext_rom;
+ size_t len;
+
+ /* Must be last */
+ u32 data[0]; /* older gcc can't handle [] which is standard */
+};
+
+struct csr1212_csr {
+ size_t bus_info_len; /* bus info block length in bytes */
+ size_t crc_len; /* crc length in bytes */
+ u32 *bus_info_data; /* bus info data incl bus name and EUI */
+
+ void *private; /* private, bus specific data */
+ struct csr1212_bus_ops *ops;
+
+ struct csr1212_keyval *root_kv;
+
+ int max_rom; /* max bytes readable in Config ROM region */
+
+ /* Items below used for image parsing and generation */
+ struct csr1212_csr_rom_cache *cache_head, *cache_tail;
+};
+
+struct csr1212_bus_ops {
+ /* This function is used by csr1212 to read additional information
+ * from remote nodes when parsing a Config ROM (i.e., read Config ROM
+ * entries located in the Units Space. Must return 0 on success
+ * anything else indicates an error. */
+ int (*bus_read) (struct csr1212_csr *csr, u64 addr,
+ u16 length, void *buffer, void *private);
+
+ /* This function is used by csr1212 to allocate a region in units space
+ * in the event that Config ROM entries don't all fit in the predefined
+ * 1K region. The void *private parameter is private member of struct
+ * csr1212_csr. */
+ u64 (*allocate_addr_range) (u64 size, u32 alignment, void *private);
+
+ /* This function is used by csr1212 to release a region in units space
+ * that is no longer needed. */
+ void (*release_addr) (u64 addr, void *private);
+
+ /* This function is used by csr1212 to determine the max read request
+ * supported by a remote node when reading the ConfigROM space. Must
+ * return 0, 1, or 2 per IEEE 1212. */
+ int (*get_max_rom) (u32 *bus_info, void *private);
+};
+
+
+/* Descriptor Leaf manipulation macros */
+#define CSR1212_DESCRIPTOR_LEAF_TYPE_SHIFT 24
+#define CSR1212_DESCRIPTOR_LEAF_SPECIFIER_ID_MASK 0xffffff
+#define CSR1212_DESCRIPTOR_LEAF_OVERHEAD (1 * sizeof(u32))
+
+#define CSR1212_DESCRIPTOR_LEAF_TYPE(kv) \
+ (be32_to_cpu((kv)->value.leaf.data[0]) >> \
+ CSR1212_DESCRIPTOR_LEAF_TYPE_SHIFT)
+#define CSR1212_DESCRIPTOR_LEAF_SPECIFIER_ID(kv) \
+ (be32_to_cpu((kv)->value.leaf.data[0]) & \
+ CSR1212_DESCRIPTOR_LEAF_SPECIFIER_ID_MASK)
+
+
+/* Text Descriptor Leaf manipulation macros */
+#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH_SHIFT 28
+#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH_MASK 0xf /* after shift */
+#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET_SHIFT 16
+#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET_MASK 0xfff /* after shift */
+#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_LANGUAGE_MASK 0xffff
+#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_OVERHEAD (1 * sizeof(u32))
+
+#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH(kv) \
+ (be32_to_cpu((kv)->value.leaf.data[1]) >> \
+ CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH_SHIFT)
+#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET(kv) \
+ ((be32_to_cpu((kv)->value.leaf.data[1]) >> \
+ CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET_SHIFT) & \
+ CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET_MASK)
+#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_LANGUAGE(kv) \
+ (be32_to_cpu((kv)->value.leaf.data[1]) & \
+ CSR1212_TEXTUAL_DESCRIPTOR_LEAF_LANGUAGE_MASK)
+#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_DATA(kv) \
+ (&((kv)->value.leaf.data[2]))
+
+
+/* The following 2 function are for creating new Configuration ROM trees. The
+ * first function is used for both creating local trees and parsing remote
+ * trees. The second function adds pertinent information to local Configuration
+ * ROM trees - namely data for the bus information block. */
+extern struct csr1212_csr *csr1212_create_csr(struct csr1212_bus_ops *ops,
+ size_t bus_info_size,
+ void *private);
+extern void csr1212_init_local_csr(struct csr1212_csr *csr,
+ const u32 *bus_info_data, int max_rom);
+
+
+/* Destroy a Configuration ROM tree and release all memory taken by the tree. */
+extern void csr1212_destroy_csr(struct csr1212_csr *csr);
+
+
+/* The following set of functions are fore creating new keyvals for placement in
+ * a Configuration ROM tree. Code that creates new keyvals with these functions
+ * must release those keyvals with csr1212_release_keyval() when they are no
+ * longer needed. */
+extern struct csr1212_keyval *csr1212_new_immediate(u8 key, u32 value);
+extern struct csr1212_keyval *csr1212_new_directory(u8 key);
+extern struct csr1212_keyval *csr1212_new_string_descriptor_leaf(const char *s);
+
+
+/* The following function manages association between keyvals. Typically,
+ * Descriptor Leaves and Directories will be associated with another keyval and
+ * it is desirable for the Descriptor keyval to be place immediately after the
+ * keyval that it is associated with.
+ * Take care with subsequent ROM modifications: There is no function to remove
+ * previously specified associations.
+ */
+extern void csr1212_associate_keyval(struct csr1212_keyval *kv,
+ struct csr1212_keyval *associate);
+
+
+/* The following functions manage the association of a keyval and directories.
+ * A keyval may be attached to more than one directory. */
+extern int csr1212_attach_keyval_to_directory(struct csr1212_keyval *dir,
+ struct csr1212_keyval *kv);
+extern void csr1212_detach_keyval_from_directory(struct csr1212_keyval *dir,
+ struct csr1212_keyval *kv);
+
+
+/* Creates a complete Configuration ROM image in the list of caches available
+ * via csr->cache_head. */
+extern int csr1212_generate_csr_image(struct csr1212_csr *csr);
+
+
+/* This is a convience function for reading a block of data out of one of the
+ * caches in the csr->cache_head list. */
+extern int csr1212_read(struct csr1212_csr *csr, u32 offset, void *buffer,
+ u32 len);
+
+
+/* The following functions are in place for parsing Configuration ROM images.
+ * csr1212_parse_keyval() is used should there be a need to directly parse a
+ * Configuration ROM directly. */
+extern int csr1212_parse_keyval(struct csr1212_keyval *kv,
+ struct csr1212_csr_rom_cache *cache);
+extern int csr1212_parse_csr(struct csr1212_csr *csr);
+
+
+/* This function allocates a new cache which may be used for either parsing or
+ * generating sub-sets of Configuration ROM images. */
+static inline struct csr1212_csr_rom_cache *
+csr1212_rom_cache_malloc(u32 offset, size_t size)
+{
+ struct csr1212_csr_rom_cache *cache;
+
+ cache = CSR1212_MALLOC(sizeof(*cache) + size);
+ if (!cache)
+ return NULL;
+
+ cache->next = NULL;
+ cache->prev = NULL;
+ cache->filled_head = NULL;
+ cache->filled_tail = NULL;
+ cache->layout_head = NULL;
+ cache->layout_tail = NULL;
+ cache->offset = offset;
+ cache->size = size;
+ cache->ext_rom = NULL;
+
+ return cache;
+}
+
+
+/* This function ensures that a keyval contains data when referencing a keyval
+ * created by parsing a Configuration ROM. */
+extern struct csr1212_keyval *
+csr1212_get_keyval(struct csr1212_csr *csr, struct csr1212_keyval *kv);
+
+
+/* This function increments the reference count for a keyval should there be a
+ * need for code to retain a keyval that has been parsed. */
+static inline void csr1212_keep_keyval(struct csr1212_keyval *kv)
+{
+ atomic_inc(&kv->refcnt);
+ smp_mb__after_atomic_inc();
+}
+
+
+/* This function decrements a keyval's reference count and will destroy the
+ * keyval when there are no more users of the keyval. This should be called by
+ * any code that calls csr1212_keep_keyval() or any of the keyval creation
+ * routines csr1212_new_*(). */
+extern void csr1212_release_keyval(struct csr1212_keyval *kv);
+
+
+/*
+ * This macro allows for looping over the keyval entries in a directory and it
+ * ensures that keyvals from remote ConfigROMs are parsed properly.
+ *
+ * struct csr1212_csr *_csr points to the CSR associated with dir.
+ * struct csr1212_keyval *_kv points to the current keyval (loop index).
+ * struct csr1212_keyval *_dir points to the directory to be looped.
+ * struct csr1212_dentry *_pos is used internally for indexing.
+ *
+ * kv will be NULL upon exit of the loop.
+ */
+#define csr1212_for_each_dir_entry(_csr, _kv, _dir, _pos) \
+ for (csr1212_get_keyval((_csr), (_dir)), \
+ _pos = (_dir)->value.directory.dentries_head, \
+ _kv = (_pos) ? csr1212_get_keyval((_csr), _pos->kv) : NULL;\
+ (_kv) && (_pos); \
+ (_kv->associate == NULL) ? \
+ ((_pos = _pos->next), (_kv = (_pos) ? \
+ csr1212_get_keyval((_csr), _pos->kv) : \
+ NULL)) : \
+ (_kv = csr1212_get_keyval((_csr), _kv->associate)))
+
+#endif /* __CSR1212_H__ */
diff --git a/drivers/ieee1394/dma.c b/drivers/ieee1394/dma.c
new file mode 100644
index 0000000..1aba8c1
--- /dev/null
+++ b/drivers/ieee1394/dma.c
@@ -0,0 +1,290 @@
+/*
+ * DMA region bookkeeping routines
+ *
+ * Copyright (C) 2002 Maas Digital LLC
+ *
+ * This code is licensed under the GPL. See the file COPYING in the root
+ * directory of the kernel sources for details.
+ */
+
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/scatterlist.h>
+
+#include "dma.h"
+
+/* dma_prog_region */
+
+void dma_prog_region_init(struct dma_prog_region *prog)
+{
+ prog->kvirt = NULL;
+ prog->dev = NULL;
+ prog->n_pages = 0;
+ prog->bus_addr = 0;
+}
+
+int dma_prog_region_alloc(struct dma_prog_region *prog, unsigned long n_bytes,
+ struct pci_dev *dev)
+{
+ /* round up to page size */
+ n_bytes = PAGE_ALIGN(n_bytes);
+
+ prog->n_pages = n_bytes >> PAGE_SHIFT;
+
+ prog->kvirt = pci_alloc_consistent(dev, n_bytes, &prog->bus_addr);
+ if (!prog->kvirt) {
+ printk(KERN_ERR
+ "dma_prog_region_alloc: pci_alloc_consistent() failed\n");
+ dma_prog_region_free(prog);
+ return -ENOMEM;
+ }
+
+ prog->dev = dev;
+
+ return 0;
+}
+
+void dma_prog_region_free(struct dma_prog_region *prog)
+{
+ if (prog->kvirt) {
+ pci_free_consistent(prog->dev, prog->n_pages << PAGE_SHIFT,
+ prog->kvirt, prog->bus_addr);
+ }
+
+ prog->kvirt = NULL;
+ prog->dev = NULL;
+ prog->n_pages = 0;
+ prog->bus_addr = 0;
+}
+
+/* dma_region */
+
+/**
+ * dma_region_init - clear out all fields but do not allocate anything
+ */
+void dma_region_init(struct dma_region *dma)
+{
+ dma->kvirt = NULL;
+ dma->dev = NULL;
+ dma->n_pages = 0;
+ dma->n_dma_pages = 0;
+ dma->sglist = NULL;
+}
+
+/**
+ * dma_region_alloc - allocate the buffer and map it to the IOMMU
+ */
+int dma_region_alloc(struct dma_region *dma, unsigned long n_bytes,
+ struct pci_dev *dev, int direction)
+{
+ unsigned int i;
+
+ /* round up to page size */
+ n_bytes = PAGE_ALIGN(n_bytes);
+
+ dma->n_pages = n_bytes >> PAGE_SHIFT;
+
+ dma->kvirt = vmalloc_32(n_bytes);
+ if (!dma->kvirt) {
+ printk(KERN_ERR "dma_region_alloc: vmalloc_32() failed\n");
+ goto err;
+ }
+
+ /* Clear the ram out, no junk to the user */
+ memset(dma->kvirt, 0, n_bytes);
+
+ /* allocate scatter/gather list */
+ dma->sglist = vmalloc(dma->n_pages * sizeof(*dma->sglist));
+ if (!dma->sglist) {
+ printk(KERN_ERR "dma_region_alloc: vmalloc(sglist) failed\n");
+ goto err;
+ }
+
+ sg_init_table(dma->sglist, dma->n_pages);
+
+ /* fill scatter/gather list with pages */
+ for (i = 0; i < dma->n_pages; i++) {
+ unsigned long va =
+ (unsigned long)dma->kvirt + (i << PAGE_SHIFT);
+
+ sg_set_page(&dma->sglist[i], vmalloc_to_page((void *)va),
+ PAGE_SIZE, 0);
+ }
+
+ /* map sglist to the IOMMU */
+ dma->n_dma_pages =
+ pci_map_sg(dev, dma->sglist, dma->n_pages, direction);
+
+ if (dma->n_dma_pages == 0) {
+ printk(KERN_ERR "dma_region_alloc: pci_map_sg() failed\n");
+ goto err;
+ }
+
+ dma->dev = dev;
+ dma->direction = direction;
+
+ return 0;
+
+ err:
+ dma_region_free(dma);
+ return -ENOMEM;
+}
+
+/**
+ * dma_region_free - unmap and free the buffer
+ */
+void dma_region_free(struct dma_region *dma)
+{
+ if (dma->n_dma_pages) {
+ pci_unmap_sg(dma->dev, dma->sglist, dma->n_pages,
+ dma->direction);
+ dma->n_dma_pages = 0;
+ dma->dev = NULL;
+ }
+
+ vfree(dma->sglist);
+ dma->sglist = NULL;
+
+ vfree(dma->kvirt);
+ dma->kvirt = NULL;
+ dma->n_pages = 0;
+}
+
+/* find the scatterlist index and remaining offset corresponding to a
+ given offset from the beginning of the buffer */
+static inline int dma_region_find(struct dma_region *dma, unsigned long offset,
+ unsigned int start, unsigned long *rem)
+{
+ int i;
+ unsigned long off = offset;
+
+ for (i = start; i < dma->n_dma_pages; i++) {
+ if (off < sg_dma_len(&dma->sglist[i])) {
+ *rem = off;
+ break;
+ }
+
+ off -= sg_dma_len(&dma->sglist[i]);
+ }
+
+ BUG_ON(i >= dma->n_dma_pages);
+
+ return i;
+}
+
+/**
+ * dma_region_offset_to_bus - get bus address of an offset within a DMA region
+ *
+ * Returns the DMA bus address of the byte with the given @offset relative to
+ * the beginning of the @dma.
+ */
+dma_addr_t dma_region_offset_to_bus(struct dma_region * dma,
+ unsigned long offset)
+{
+ unsigned long rem = 0;
+
+ struct scatterlist *sg =
+ &dma->sglist[dma_region_find(dma, offset, 0, &rem)];
+ return sg_dma_address(sg) + rem;
+}
+
+/**
+ * dma_region_sync_for_cpu - sync the CPU's view of the buffer
+ */
+void dma_region_sync_for_cpu(struct dma_region *dma, unsigned long offset,
+ unsigned long len)
+{
+ int first, last;
+ unsigned long rem = 0;
+
+ if (!len)
+ len = 1;
+
+ first = dma_region_find(dma, offset, 0, &rem);
+ last = dma_region_find(dma, rem + len - 1, first, &rem);
+
+ pci_dma_sync_sg_for_cpu(dma->dev, &dma->sglist[first], last - first + 1,
+ dma->direction);
+}
+
+/**
+ * dma_region_sync_for_device - sync the IO bus' view of the buffer
+ */
+void dma_region_sync_for_device(struct dma_region *dma, unsigned long offset,
+ unsigned long len)
+{
+ int first, last;
+ unsigned long rem = 0;
+
+ if (!len)
+ len = 1;
+
+ first = dma_region_find(dma, offset, 0, &rem);
+ last = dma_region_find(dma, rem + len - 1, first, &rem);
+
+ pci_dma_sync_sg_for_device(dma->dev, &dma->sglist[first],
+ last - first + 1, dma->direction);
+}
+
+#ifdef CONFIG_MMU
+
+static int dma_region_pagefault(struct vm_area_struct *vma,
+ struct vm_fault *vmf)
+{
+ struct dma_region *dma = (struct dma_region *)vma->vm_private_data;
+
+ if (!dma->kvirt)
+ return VM_FAULT_SIGBUS;
+
+ if (vmf->pgoff >= dma->n_pages)
+ return VM_FAULT_SIGBUS;
+
+ vmf->page = vmalloc_to_page(dma->kvirt + (vmf->pgoff << PAGE_SHIFT));
+ get_page(vmf->page);
+ return 0;
+}
+
+static struct vm_operations_struct dma_region_vm_ops = {
+ .fault = dma_region_pagefault,
+};
+
+/**
+ * dma_region_mmap - map the buffer into a user space process
+ */
+int dma_region_mmap(struct dma_region *dma, struct file *file,
+ struct vm_area_struct *vma)
+{
+ unsigned long size;
+
+ if (!dma->kvirt)
+ return -EINVAL;
+
+ /* must be page-aligned (XXX: comment is wrong, we could allow pgoff) */
+ if (vma->vm_pgoff != 0)
+ return -EINVAL;
+
+ /* check the length */
+ size = vma->vm_end - vma->vm_start;
+ if (size > (dma->n_pages << PAGE_SHIFT))
+ return -EINVAL;
+
+ vma->vm_ops = &dma_region_vm_ops;
+ vma->vm_private_data = dma;
+ vma->vm_file = file;
+ vma->vm_flags |= VM_RESERVED | VM_ALWAYSDUMP;
+
+ return 0;
+}
+
+#else /* CONFIG_MMU */
+
+int dma_region_mmap(struct dma_region *dma, struct file *file,
+ struct vm_area_struct *vma)
+{
+ return -EINVAL;
+}
+
+#endif /* CONFIG_MMU */
diff --git a/drivers/ieee1394/dma.h b/drivers/ieee1394/dma.h
new file mode 100644
index 0000000..2727bcd
--- /dev/null
+++ b/drivers/ieee1394/dma.h
@@ -0,0 +1,88 @@
+/*
+ * DMA region bookkeeping routines
+ *
+ * Copyright (C) 2002 Maas Digital LLC
+ *
+ * This code is licensed under the GPL. See the file COPYING in the root
+ * directory of the kernel sources for details.
+ */
+
+#ifndef IEEE1394_DMA_H
+#define IEEE1394_DMA_H
+
+#include <asm/types.h>
+
+struct pci_dev;
+struct scatterlist;
+struct vm_area_struct;
+
+/**
+ * struct dma_prog_region - small contiguous DMA buffer
+ * @kvirt: kernel virtual address
+ * @dev: PCI device
+ * @n_pages: number of kernel pages
+ * @bus_addr: base bus address
+ *
+ * a small, physically contiguous DMA buffer with random-access, synchronous
+ * usage characteristics
+ */
+struct dma_prog_region {
+ unsigned char *kvirt;
+ struct pci_dev *dev;
+ unsigned int n_pages;
+ dma_addr_t bus_addr;
+};
+
+/* clear out all fields but do not allocate any memory */
+void dma_prog_region_init(struct dma_prog_region *prog);
+int dma_prog_region_alloc(struct dma_prog_region *prog, unsigned long n_bytes,
+ struct pci_dev *dev);
+void dma_prog_region_free(struct dma_prog_region *prog);
+
+static inline dma_addr_t dma_prog_region_offset_to_bus(
+ struct dma_prog_region *prog, unsigned long offset)
+{
+ return prog->bus_addr + offset;
+}
+
+/**
+ * struct dma_region - large non-contiguous DMA buffer
+ * @virt: kernel virtual address
+ * @dev: PCI device
+ * @n_pages: number of kernel pages
+ * @n_dma_pages: number of IOMMU pages
+ * @sglist: IOMMU mapping
+ * @direction: PCI_DMA_TODEVICE, etc.
+ *
+ * a large, non-physically-contiguous DMA buffer with streaming, asynchronous
+ * usage characteristics
+ */
+struct dma_region {
+ unsigned char *kvirt;
+ struct pci_dev *dev;
+ unsigned int n_pages;
+ unsigned int n_dma_pages;
+ struct scatterlist *sglist;
+ int direction;
+};
+
+void dma_region_init(struct dma_region *dma);
+int dma_region_alloc(struct dma_region *dma, unsigned long n_bytes,
+ struct pci_dev *dev, int direction);
+void dma_region_free(struct dma_region *dma);
+void dma_region_sync_for_cpu(struct dma_region *dma, unsigned long offset,
+ unsigned long len);
+void dma_region_sync_for_device(struct dma_region *dma, unsigned long offset,
+ unsigned long len);
+int dma_region_mmap(struct dma_region *dma, struct file *file,
+ struct vm_area_struct *vma);
+dma_addr_t dma_region_offset_to_bus(struct dma_region *dma,
+ unsigned long offset);
+
+/**
+ * dma_region_i - macro to index into a DMA region (or dma_prog_region)
+ */
+#define dma_region_i(_dma, _type, _index) \
+ ( ((_type*) ((_dma)->kvirt)) + (_index) )
+
+#endif /* IEEE1394_DMA_H */
diff --git a/drivers/ieee1394/dv1394-private.h b/drivers/ieee1394/dv1394-private.h
new file mode 100644
index 0000000..7d1d284
--- /dev/null
+++ b/drivers/ieee1394/dv1394-private.h
@@ -0,0 +1,587 @@
+/*
+ * dv1394-private.h - DV input/output over IEEE 1394 on OHCI chips
+ * Copyright (C)2001 Daniel Maas <dmaas@dcine.com>
+ * receive by Dan Dennedy <dan@dennedy.org>
+ *
+ * based on:
+ * video1394.h - driver for OHCI 1394 boards
+ * Copyright (C)1999,2000 Sebastien Rougeaux <sebastien.rougeaux@anu.edu.au>
+ * Peter Schlaile <udbz@rz.uni-karlsruhe.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _DV_1394_PRIVATE_H
+#define _DV_1394_PRIVATE_H
+
+#include "ieee1394.h"
+#include "ohci1394.h"
+#include "dma.h"
+
+/* data structures private to the dv1394 driver */
+/* none of this is exposed to user-space */
+
+
+/*
+ the 8-byte CIP (Common Isochronous Packet) header that precedes
+ each packet of DV data.
+
+ See the IEC 61883 standard.
+*/
+
+struct CIP_header { unsigned char b[8]; };
+
+static inline void fill_cip_header(struct CIP_header *cip,
+ unsigned char source_node_id,
+ unsigned long counter,
+ enum pal_or_ntsc format,
+ unsigned long timestamp)
+{
+ cip->b[0] = source_node_id;
+ cip->b[1] = 0x78; /* packet size in quadlets (480/4) - even for empty packets! */
+ cip->b[2] = 0x00;
+ cip->b[3] = counter;
+
+ cip->b[4] = 0x80; /* const */
+
+ switch(format) {
+ case DV1394_PAL:
+ cip->b[5] = 0x80;
+ break;
+ case DV1394_NTSC:
+ cip->b[5] = 0x00;
+ break;
+ }
+
+ cip->b[6] = timestamp >> 8;
+ cip->b[7] = timestamp & 0xFF;
+}
+
+
+
+/*
+ DMA commands used to program the OHCI's DMA engine
+
+ See the Texas Instruments OHCI 1394 chipset documentation.
+*/
+
+struct output_more_immediate { u32 q[8]; };
+struct output_more { u32 q[4]; };
+struct output_last { u32 q[4]; };
+struct input_more { u32 q[4]; };
+struct input_last { u32 q[4]; };
+
+/* outputs */
+
+static inline void fill_output_more_immediate(struct output_more_immediate *omi,
+ unsigned char tag,
+ unsigned char channel,
+ unsigned char sync_tag,
+ unsigned int payload_size)
+{
+ omi->q[0] = cpu_to_le32(0x02000000 | 8); /* OUTPUT_MORE_IMMEDIATE; 8 is the size of the IT header */
+ omi->q[1] = 0;
+ omi->q[2] = 0;
+ omi->q[3] = 0;
+
+ /* IT packet header */
+ omi->q[4] = cpu_to_le32( (0x0 << 16) /* IEEE1394_SPEED_100 */
+ | (tag << 14)
+ | (channel << 8)
+ | (TCODE_ISO_DATA << 4)
+ | (sync_tag) );
+
+ /* reserved field; mimic behavior of my Sony DSR-40 */
+ omi->q[5] = cpu_to_le32((payload_size << 16) | (0x7F << 8) | 0xA0);
+
+ omi->q[6] = 0;
+ omi->q[7] = 0;
+}
+
+static inline void fill_output_more(struct output_more *om,
+ unsigned int data_size,
+ unsigned long data_phys_addr)
+{
+ om->q[0] = cpu_to_le32(data_size);
+ om->q[1] = cpu_to_le32(data_phys_addr);
+ om->q[2] = 0;
+ om->q[3] = 0;
+}
+
+static inline void fill_output_last(struct output_last *ol,
+ int want_timestamp,
+ int want_interrupt,
+ unsigned int data_size,
+ unsigned long data_phys_addr)
+{
+ u32 temp = 0;
+ temp |= 1 << 28; /* OUTPUT_LAST */
+
+ if (want_timestamp) /* controller will update timestamp at DMA time */
+ temp |= 1 << 27;
+
+ if (want_interrupt)
+ temp |= 3 << 20;
+
+ temp |= 3 << 18; /* must take branch */
+ temp |= data_size;
+
+ ol->q[0] = cpu_to_le32(temp);
+ ol->q[1] = cpu_to_le32(data_phys_addr);
+ ol->q[2] = 0;
+ ol->q[3] = 0;
+}
+
+/* inputs */
+
+static inline void fill_input_more(struct input_more *im,
+ int want_interrupt,
+ unsigned int data_size,
+ unsigned long data_phys_addr)
+{
+ u32 temp = 2 << 28; /* INPUT_MORE */
+ temp |= 8 << 24; /* s = 1, update xferStatus and resCount */
+ if (want_interrupt)
+ temp |= 0 << 20; /* interrupts, i=0 in packet-per-buffer mode */
+ temp |= 0x0 << 16; /* disable branch to address for packet-per-buffer mode */
+ /* disable wait on sync field, not used in DV :-( */
+ temp |= data_size;
+
+ im->q[0] = cpu_to_le32(temp);
+ im->q[1] = cpu_to_le32(data_phys_addr);
+ im->q[2] = 0; /* branchAddress and Z not use in packet-per-buffer mode */
+ im->q[3] = 0; /* xferStatus & resCount, resCount must be initialize to data_size */
+}
+
+static inline void fill_input_last(struct input_last *il,
+ int want_interrupt,
+ unsigned int data_size,
+ unsigned long data_phys_addr)
+{
+ u32 temp = 3 << 28; /* INPUT_LAST */
+ temp |= 8 << 24; /* s = 1, update xferStatus and resCount */
+ if (want_interrupt)
+ temp |= 3 << 20; /* enable interrupts */
+ temp |= 0xC << 16; /* enable branch to address */
+ /* disable wait on sync field, not used in DV :-( */
+ temp |= data_size;
+
+ il->q[0] = cpu_to_le32(temp);
+ il->q[1] = cpu_to_le32(data_phys_addr);
+ il->q[2] = cpu_to_le32(1); /* branchAddress (filled in later) and Z = 1 descriptor in next block */
+ il->q[3] = cpu_to_le32(data_size); /* xferStatus & resCount, resCount must be initialize to data_size */
+}
+
+
+
+/*
+ A "DMA descriptor block" consists of several contiguous DMA commands.
+ struct DMA_descriptor_block encapsulates all of the commands necessary
+ to send one packet of DV data.
+
+ There are three different types of these blocks:
+
+ 1) command to send an empty packet (CIP header only, no DV data):
+
+ OUTPUT_MORE-Immediate <-- contains the iso header in-line
+ OUTPUT_LAST <-- points to the CIP header
+
+ 2) command to send a full packet when the DV data payload does NOT
+ cross a page boundary:
+
+ OUTPUT_MORE-Immediate <-- contains the iso header in-line
+ OUTPUT_MORE <-- points to the CIP header
+ OUTPUT_LAST <-- points to entire DV data payload
+
+ 3) command to send a full packet when the DV payload DOES cross
+ a page boundary:
+
+ OUTPUT_MORE-Immediate <-- contains the iso header in-line
+ OUTPUT_MORE <-- points to the CIP header
+ OUTPUT_MORE <-- points to first part of DV data payload
+ OUTPUT_LAST <-- points to second part of DV data payload
+
+ This struct describes all three block types using unions.
+
+ !!! It is vital that an even number of these descriptor blocks fit on one
+ page of memory, since a block cannot cross a page boundary !!!
+
+ */
+
+struct DMA_descriptor_block {
+
+ union {
+ struct {
+ /* iso header, common to all output block types */
+ struct output_more_immediate omi;
+
+ union {
+ /* empty packet */
+ struct {
+ struct output_last ol; /* CIP header */
+ } empty;
+
+ /* full packet */
+ struct {
+ struct output_more om; /* CIP header */
+
+ union {
+ /* payload does not cross page boundary */
+ struct {
+ struct output_last ol; /* data payload */
+ } nocross;
+
+ /* payload crosses page boundary */
+ struct {
+ struct output_more om; /* data payload */
+ struct output_last ol; /* data payload */
+ } cross;
+ } u;
+
+ } full;
+ } u;
+ } out;
+
+ struct {
+ struct input_last il;
+ } in;
+
+ } u;
+
+ /* ensure that PAGE_SIZE % sizeof(struct DMA_descriptor_block) == 0
+ by padding out to 128 bytes */
+ u32 __pad__[12];
+};
+
+
+/* struct frame contains all data associated with one frame in the
+ ringbuffer these are allocated when the DMA context is initialized
+ do_dv1394_init(). They are re-used after the card finishes
+ transmitting the frame. */
+
+struct video_card; /* forward declaration */
+
+struct frame {
+
+ /* points to the struct video_card that owns this frame */
+ struct video_card *video;
+
+ /* index of this frame in video_card->frames[] */
+ unsigned int frame_num;
+
+ /* FRAME_CLEAR - DMA program not set up, waiting for data
+ FRAME_READY - DMA program written, ready to transmit
+
+ Changes to these should be locked against the interrupt
+ */
+ enum {
+ FRAME_CLEAR = 0,
+ FRAME_READY
+ } state;
+
+ /* whether this frame has been DMA'ed already; used only from
+ the IRQ handler to determine whether the frame can be reset */
+ int done;
+
+
+ /* kernel virtual pointer to the start of this frame's data in
+ the user ringbuffer. Use only for CPU access; to get the DMA
+ bus address you must go through the video->user_dma mapping */
+ unsigned long data;
+
+ /* Max # of packets per frame */
+#define MAX_PACKETS 500
+
+
+ /* a PAGE_SIZE memory pool for allocating CIP headers
+ !header_pool must be aligned to PAGE_SIZE! */
+ struct CIP_header *header_pool;
+ dma_addr_t header_pool_dma;
+
+
+ /* a physically contiguous memory pool for allocating DMA
+ descriptor blocks; usually around 64KB in size
+ !descriptor_pool must be aligned to PAGE_SIZE! */
+ struct DMA_descriptor_block *descriptor_pool;
+ dma_addr_t descriptor_pool_dma;
+ unsigned long descriptor_pool_size;
+
+
+ /* # of packets allocated for this frame */
+ unsigned int n_packets;
+
+
+ /* below are several pointers (kernel virtual addresses, not
+ DMA bus addresses) to parts of the DMA program. These are
+ set each time the DMA program is written in
+ frame_prepare(). They are used later on, e.g. from the
+ interrupt handler, to check the status of the frame */
+
+ /* points to status/timestamp field of first DMA packet */
+ /* (we'll check it later to monitor timestamp accuracy) */
+ u32 *frame_begin_timestamp;
+
+ /* the timestamp we assigned to the first packet in the frame */
+ u32 assigned_timestamp;
+
+ /* pointer to the first packet's CIP header (where the timestamp goes) */
+ struct CIP_header *cip_syt1;
+
+ /* pointer to the second packet's CIP header
+ (only set if the first packet was empty) */
+ struct CIP_header *cip_syt2;
+
+ /* in order to figure out what caused an interrupt,
+ store pointers to the status fields of the two packets
+ that can cause interrupts. We'll check these from the
+ interrupt handler.
+ */
+ u32 *mid_frame_timestamp;
+ u32 *frame_end_timestamp;
+
+ /* branch address field of final packet. This is effectively
+ the "tail" in the chain of DMA descriptor blocks.
+ We will fill it with the address of the first DMA descriptor
+ block in the subsequent frame, once it is ready.
+ */
+ u32 *frame_end_branch;
+
+ /* the number of descriptors in the first descriptor block
+ of the frame. Needed to start DMA */
+ int first_n_descriptors;
+};
+
+
+struct packet {
+ u16 timestamp;
+ u16 invalid;
+ u16 iso_header;
+ u16 data_length;
+ u32 cip_h1;
+ u32 cip_h2;
+ unsigned char data[480];
+ unsigned char padding[16]; /* force struct size =512 for page alignment */
+};
+
+
+/* allocate/free a frame */
+static struct frame* frame_new(unsigned int frame_num, struct video_card *video);
+static void frame_delete(struct frame *f);
+
+/* reset f so that it can be used again */
+static void frame_reset(struct frame *f);
+
+/* struct video_card contains all data associated with one instance
+ of the dv1394 driver
+*/
+enum modes {
+ MODE_RECEIVE,
+ MODE_TRANSMIT
+};
+
+struct video_card {
+
+ /* ohci card to which this instance corresponds */
+ struct ti_ohci *ohci;
+
+ /* OHCI card id; the link between the VFS inode and a specific video_card
+ (essentially the device minor number) */
+ int id;
+
+ /* entry in dv1394_cards */
+ struct list_head list;
+
+ /* OHCI card IT DMA context number, -1 if not in use */
+ int ohci_it_ctx;
+ struct ohci1394_iso_tasklet it_tasklet;
+
+ /* register offsets for current IT DMA context, 0 if not in use */
+ u32 ohci_IsoXmitContextControlSet;
+ u32 ohci_IsoXmitContextControlClear;
+ u32 ohci_IsoXmitCommandPtr;
+
+ /* OHCI card IR DMA context number, -1 if not in use */
+ struct ohci1394_iso_tasklet ir_tasklet;
+ int ohci_ir_ctx;
+
+ /* register offsets for current IR DMA context, 0 if not in use */
+ u32 ohci_IsoRcvContextControlSet;
+ u32 ohci_IsoRcvContextControlClear;
+ u32 ohci_IsoRcvCommandPtr;
+ u32 ohci_IsoRcvContextMatch;
+
+
+ /* CONCURRENCY CONTROL */
+
+ /* there are THREE levels of locking associated with video_card. */
+
+ /*
+ 1) the 'open' flag - this prevents more than one process from
+ opening the device. (the driver currently assumes only one opener).
+ This is a regular int, but use test_and_set_bit() (on bit zero)
+ for atomicity.
+ */
+ unsigned long open;
+
+ /*
+ 2) the spinlock - this provides mutual exclusion between the interrupt
+ handler and process-context operations. Generally you must take the
+ spinlock under the following conditions:
+ 1) DMA (and hence the interrupt handler) may be running
+ AND
+ 2) you need to operate on the video_card, especially active_frame
+
+ It is OK to play with video_card without taking the spinlock if
+ you are certain that DMA is not running. Even if DMA is running,
+ it is OK to *read* active_frame with the lock, then drop it
+ immediately. This is safe because the interrupt handler will never
+ advance active_frame onto a frame that is not READY (and the spinlock
+ must be held while marking a frame READY).
+
+ spinlock is also used to protect ohci_it_ctx and ohci_ir_ctx,
+ which can be accessed from both process and interrupt context
+ */
+ spinlock_t spinlock;
+
+ /* flag to prevent spurious interrupts (which OHCI seems to
+ generate a lot :) from accessing the struct */
+ int dma_running;
+
+ /*
+ 3) the sleeping mutex 'mtx' - this is used from process context only,
+ to serialize various operations on the video_card. Even though only one
+ open() is allowed, we still need to prevent multiple threads of execution
+ from entering calls like read, write, ioctl, etc.
+
+ I honestly can't think of a good reason to use dv1394 from several threads
+ at once, but we need to serialize anyway to prevent oopses =).
+
+ NOTE: if you need both spinlock and mtx, take mtx first to avoid deadlock!
+ */
+ struct mutex mtx;
+
+ /* people waiting for buffer space, please form a line here... */
+ wait_queue_head_t waitq;
+
+ /* support asynchronous I/O signals (SIGIO) */
+ struct fasync_struct *fasync;
+
+ /* the large, non-contiguous (rvmalloc()) ringbuffer for DV
+ data, exposed to user-space via mmap() */
+ unsigned long dv_buf_size;
+ struct dma_region dv_buf;
+
+ /* next byte in the ringbuffer that a write() call will fill */
+ size_t write_off;
+
+ struct frame *frames[DV1394_MAX_FRAMES];
+
+ /* n_frames also serves as an indicator that this struct video_card is
+ initialized and ready to run DMA buffers */
+
+ int n_frames;
+
+ /* this is the frame that is currently "owned" by the OHCI DMA controller
+ (set to -1 iff DMA is not running)
+
+ ! must lock against the interrupt handler when accessing it !
+
+ RULES:
+
+ Only the interrupt handler may change active_frame if DMA
+ is running; if not, process may change it
+
+ If the next frame is READY, the interrupt handler will advance
+ active_frame when the current frame is finished.
+
+ If the next frame is CLEAR, the interrupt handler will re-transmit
+ the current frame, and the dropped_frames counter will be incremented.
+
+ The interrupt handler will NEVER advance active_frame to a
+ frame that is not READY.
+ */
+ int active_frame;
+ int first_run;
+
+ /* the same locking rules apply to these three fields also: */
+
+ /* altered ONLY from process context. Must check first_clear_frame->state;
+ if it's READY, that means the ringbuffer is full with READY frames;
+ if it's CLEAR, that means one or more ringbuffer frames are CLEAR */
+ unsigned int first_clear_frame;
+
+ /* altered both by process and interrupt */
+ unsigned int n_clear_frames;
+
+ /* only altered by the interrupt */
+ unsigned int dropped_frames;
+
+
+
+ /* the CIP accumulator and continuity counter are properties
+ of the DMA stream as a whole (not a single frame), so they
+ are stored here in the video_card */
+
+ unsigned long cip_accum;
+ unsigned long cip_n, cip_d;
+ unsigned int syt_offset;
+ unsigned int continuity_counter;
+
+ enum pal_or_ntsc pal_or_ntsc;
+
+ /* redundant, but simplifies the code somewhat */
+ unsigned int frame_size; /* in bytes */
+
+ /* the isochronous channel to use, -1 if video card is inactive */
+ int channel;
+
+
+ /* physically contiguous packet ringbuffer for receive */
+ struct dma_region packet_buf;
+ unsigned long packet_buf_size;
+
+ unsigned int current_packet;
+ int first_frame; /* received first start frame marker? */
+ enum modes mode;
+};
+
+/*
+ if the video_card is not initialized, then the ONLY fields that are valid are:
+ ohci
+ open
+ n_frames
+*/
+
+static inline int video_card_initialized(struct video_card *v)
+{
+ return v->n_frames > 0;
+}
+
+static int do_dv1394_init(struct video_card *video, struct dv1394_init *init);
+static int do_dv1394_init_default(struct video_card *video);
+static void do_dv1394_shutdown(struct video_card *video, int free_user_buf);
+
+
+/* NTSC empty packet rate accurate to within 0.01%,
+ calibrated against a Sony DSR-40 DVCAM deck */
+
+#define CIP_N_NTSC 68000000
+#define CIP_D_NTSC 1068000000
+
+#define CIP_N_PAL 1
+#define CIP_D_PAL 16
+
+#endif /* _DV_1394_PRIVATE_H */
+
diff --git a/drivers/ieee1394/dv1394.c b/drivers/ieee1394/dv1394.c
new file mode 100644
index 0000000..c19f232
--- /dev/null
+++ b/drivers/ieee1394/dv1394.c
@@ -0,0 +1,2596 @@
+/*
+ * dv1394.c - DV input/output over IEEE 1394 on OHCI chips
+ * Copyright (C)2001 Daniel Maas <dmaas@dcine.com>
+ * receive by Dan Dennedy <dan@dennedy.org>
+ *
+ * based on:
+ * video1394.c - video driver for OHCI 1394 boards
+ * Copyright (C)1999,2000 Sebastien Rougeaux <sebastien.rougeaux@anu.edu.au>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*
+ OVERVIEW
+
+ I designed dv1394 as a "pipe" that you can use to shoot DV onto a
+ FireWire bus. In transmission mode, dv1394 does the following:
+
+ 1. accepts contiguous frames of DV data from user-space, via write()
+ or mmap() (see dv1394.h for the complete API)
+ 2. wraps IEC 61883 packets around the DV data, inserting
+ empty synchronization packets as necessary
+ 3. assigns accurate SYT timestamps to the outgoing packets
+ 4. shoots them out using the OHCI card's IT DMA engine
+
+ Thanks to Dan Dennedy, we now have a receive mode that does the following:
+
+ 1. accepts raw IEC 61883 packets from the OHCI card
+ 2. re-assembles the DV data payloads into contiguous frames,
+ discarding empty packets
+ 3. sends the DV data to user-space via read() or mmap()
+*/
+
+/*
+ TODO:
+
+ - tunable frame-drop behavior: either loop last frame, or halt transmission
+
+ - use a scatter/gather buffer for DMA programs (f->descriptor_pool)
+ so that we don't rely on allocating 64KB of contiguous kernel memory
+ via pci_alloc_consistent()
+
+ DONE:
+ - during reception, better handling of dropped frames and continuity errors
+ - during reception, prevent DMA from bypassing the irq tasklets
+ - reduce irq rate during reception (1/250 packets).
+ - add many more internal buffers during reception with scatter/gather dma.
+ - add dbc (continuity) checking on receive, increment status.dropped_frames
+ if not continuous.
+ - restart IT DMA after a bus reset
+ - safely obtain and release ISO Tx channels in cooperation with OHCI driver
+ - map received DIF blocks to their proper location in DV frame (ensure
+ recovery if dropped packet)
+ - handle bus resets gracefully (OHCI card seems to take care of this itself(!))
+ - do not allow resizing the user_buf once allocated; eliminate nuke_buffer_mappings
+ - eliminated #ifdef DV1394_DEBUG_LEVEL by inventing macros debug_printk and irq_printk
+ - added wmb() and mb() to places where PCI read/write ordering needs to be enforced
+ - set video->id correctly
+ - store video_cards in an array indexed by OHCI card ID, rather than a list
+ - implement DMA context allocation to cooperate with other users of the OHCI
+ - fix all XXX showstoppers
+ - disable IR/IT DMA interrupts on shutdown
+ - flush pci writes to the card by issuing a read
+ - character device dispatching
+ - switch over to the new kernel DMA API (pci_map_*()) (* needs testing on platforms with IOMMU!)
+ - keep all video_cards in a list (for open() via chardev), set file->private_data = video
+ - dv1394_poll should indicate POLLIN when receiving buffers are available
+ - add proc fs interface to set cip_n, cip_d, syt_offset, and video signal
+ - expose xmit and recv as separate devices (not exclusive)
+ - expose NTSC and PAL as separate devices (can be overridden)
+
+*/
+
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/wait.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/fs.h>
+#include <linux/poll.h>
+#include <linux/mutex.h>
+#include <linux/bitops.h>
+#include <asm/byteorder.h>
+#include <asm/atomic.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <linux/delay.h>
+#include <asm/pgtable.h>
+#include <asm/page.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/vmalloc.h>
+#include <linux/string.h>
+#include <linux/compat.h>
+#include <linux/cdev.h>
+
+#include "dv1394.h"
+#include "dv1394-private.h"
+#include "highlevel.h"
+#include "hosts.h"
+#include "ieee1394.h"
+#include "ieee1394_core.h"
+#include "ieee1394_hotplug.h"
+#include "ieee1394_types.h"
+#include "nodemgr.h"
+#include "ohci1394.h"
+
+/* DEBUG LEVELS:
+ 0 - no debugging messages
+ 1 - some debugging messages, but none during DMA frame transmission
+ 2 - lots of messages, including during DMA frame transmission
+ (will cause undeflows if your machine is too slow!)
+*/
+
+#define DV1394_DEBUG_LEVEL 0
+
+/* for debugging use ONLY: allow more than one open() of the device */
+/* #define DV1394_ALLOW_MORE_THAN_ONE_OPEN 1 */
+
+#if DV1394_DEBUG_LEVEL >= 2
+#define irq_printk( args... ) printk( args )
+#else
+#define irq_printk( args... ) do {} while (0)
+#endif
+
+#if DV1394_DEBUG_LEVEL >= 1
+#define debug_printk( args... ) printk( args)
+#else
+#define debug_printk( args... ) do {} while (0)
+#endif
+
+/* issue a dummy PCI read to force the preceding write
+ to be posted to the PCI bus immediately */
+
+static inline void flush_pci_write(struct ti_ohci *ohci)
+{
+ mb();
+ reg_read(ohci, OHCI1394_IsochronousCycleTimer);
+}
+
+static void it_tasklet_func(unsigned long data);
+static void ir_tasklet_func(unsigned long data);
+
+#ifdef CONFIG_COMPAT
+static long dv1394_compat_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg);
+#endif
+
+/* GLOBAL DATA */
+
+/* list of all video_cards */
+static LIST_HEAD(dv1394_cards);
+static DEFINE_SPINLOCK(dv1394_cards_lock);
+
+/* translate from a struct file* to the corresponding struct video_card* */
+
+static inline struct video_card* file_to_video_card(struct file *file)
+{
+ return (struct video_card*) file->private_data;
+}
+
+/*** FRAME METHODS *********************************************************/
+
+static void frame_reset(struct frame *f)
+{
+ f->state = FRAME_CLEAR;
+ f->done = 0;
+ f->n_packets = 0;
+ f->frame_begin_timestamp = NULL;
+ f->assigned_timestamp = 0;
+ f->cip_syt1 = NULL;
+ f->cip_syt2 = NULL;
+ f->mid_frame_timestamp = NULL;
+ f->frame_end_timestamp = NULL;
+ f->frame_end_branch = NULL;
+}
+
+static struct frame* frame_new(unsigned int frame_num, struct video_card *video)
+{
+ struct frame *f = kmalloc(sizeof(*f), GFP_KERNEL);
+ if (!f)
+ return NULL;
+
+ f->video = video;
+ f->frame_num = frame_num;
+
+ f->header_pool = pci_alloc_consistent(f->video->ohci->dev, PAGE_SIZE, &f->header_pool_dma);
+ if (!f->header_pool) {
+ printk(KERN_ERR "dv1394: failed to allocate CIP header pool\n");
+ kfree(f);
+ return NULL;
+ }
+
+ debug_printk("dv1394: frame_new: allocated CIP header pool at virt 0x%08lx (contig) dma 0x%08lx size %ld\n",
+ (unsigned long) f->header_pool, (unsigned long) f->header_pool_dma, PAGE_SIZE);
+
+ f->descriptor_pool_size = MAX_PACKETS * sizeof(struct DMA_descriptor_block);
+ /* make it an even # of pages */
+ f->descriptor_pool_size += PAGE_SIZE - (f->descriptor_pool_size%PAGE_SIZE);
+
+ f->descriptor_pool = pci_alloc_consistent(f->video->ohci->dev,
+ f->descriptor_pool_size,
+ &f->descriptor_pool_dma);
+ if (!f->descriptor_pool) {
+ pci_free_consistent(f->video->ohci->dev, PAGE_SIZE, f->header_pool, f->header_pool_dma);
+ kfree(f);
+ return NULL;
+ }
+
+ debug_printk("dv1394: frame_new: allocated DMA program memory at virt 0x%08lx (contig) dma 0x%08lx size %ld\n",
+ (unsigned long) f->descriptor_pool, (unsigned long) f->descriptor_pool_dma, f->descriptor_pool_size);
+
+ f->data = 0;
+ frame_reset(f);
+
+ return f;
+}
+
+static void frame_delete(struct frame *f)
+{
+ pci_free_consistent(f->video->ohci->dev, PAGE_SIZE, f->header_pool, f->header_pool_dma);
+ pci_free_consistent(f->video->ohci->dev, f->descriptor_pool_size, f->descriptor_pool, f->descriptor_pool_dma);
+ kfree(f);
+}
+
+
+
+
+/*
+ frame_prepare() - build the DMA program for transmitting
+
+ Frame_prepare() must be called OUTSIDE the video->spinlock.
+ However, frame_prepare() must still be serialized, so
+ it should be called WITH the video->mtx taken.
+ */
+
+static void frame_prepare(struct video_card *video, unsigned int this_frame)
+{
+ struct frame *f = video->frames[this_frame];
+ int last_frame;
+
+ struct DMA_descriptor_block *block;
+ dma_addr_t block_dma;
+ struct CIP_header *cip;
+ dma_addr_t cip_dma;
+
+ unsigned int n_descriptors, full_packets, packets_per_frame, payload_size;
+
+ /* these flags denote packets that need special attention */
+ int empty_packet, first_packet, last_packet, mid_packet;
+
+ u32 *branch_address, *last_branch_address = NULL;
+ unsigned long data_p;
+ int first_packet_empty = 0;
+ u32 cycleTimer, ct_sec, ct_cyc, ct_off;
+ unsigned long irq_flags;
+
+ irq_printk("frame_prepare( %d ) ---------------------\n", this_frame);
+
+ full_packets = 0;
+
+
+
+ if (video->pal_or_ntsc == DV1394_PAL)
+ packets_per_frame = DV1394_PAL_PACKETS_PER_FRAME;
+ else
+ packets_per_frame = DV1394_NTSC_PACKETS_PER_FRAME;
+
+ while ( full_packets < packets_per_frame ) {
+ empty_packet = first_packet = last_packet = mid_packet = 0;
+
+ data_p = f->data + full_packets * 480;
+
+ /************************************************/
+ /* allocate a descriptor block and a CIP header */
+ /************************************************/
+
+ /* note: these should NOT cross a page boundary (DMA restriction) */
+
+ if (f->n_packets >= MAX_PACKETS) {
+ printk(KERN_ERR "dv1394: FATAL ERROR: max packet count exceeded\n");
+ return;
+ }
+
+ /* the block surely won't cross a page boundary,
+ since an even number of descriptor_blocks fit on a page */
+ block = &(f->descriptor_pool[f->n_packets]);
+
+ /* DMA address of the block = offset of block relative
+ to the kernel base address of the descriptor pool
+ + DMA base address of the descriptor pool */
+ block_dma = ((unsigned long) block - (unsigned long) f->descriptor_pool) + f->descriptor_pool_dma;
+
+
+ /* the whole CIP pool fits on one page, so no worries about boundaries */
+ if ( ((unsigned long) &(f->header_pool[f->n_packets]) - (unsigned long) f->header_pool)
+ > PAGE_SIZE) {
+ printk(KERN_ERR "dv1394: FATAL ERROR: no room to allocate CIP header\n");
+ return;
+ }
+
+ cip = &(f->header_pool[f->n_packets]);
+
+ /* DMA address of the CIP header = offset of cip
+ relative to kernel base address of the header pool
+ + DMA base address of the header pool */
+ cip_dma = (unsigned long) cip % PAGE_SIZE + f->header_pool_dma;
+
+ /* is this an empty packet? */
+
+ if (video->cip_accum > (video->cip_d - video->cip_n)) {
+ empty_packet = 1;
+ payload_size = 8;
+ video->cip_accum -= (video->cip_d - video->cip_n);
+ } else {
+ payload_size = 488;
+ video->cip_accum += video->cip_n;
+ }
+
+ /* there are three important packets each frame:
+
+ the first packet in the frame - we ask the card to record the timestamp when
+ this packet is actually sent, so we can monitor
+ how accurate our timestamps are. Also, the first
+ packet serves as a semaphore to let us know that
+ it's OK to free the *previous* frame's DMA buffer
+
+ the last packet in the frame - this packet is used to detect buffer underflows.
+ if this is the last ready frame, the last DMA block
+ will have a branch back to the beginning of the frame
+ (so that the card will re-send the frame on underflow).
+ if this branch gets taken, we know that at least one
+ frame has been dropped. When the next frame is ready,
+ the branch is pointed to its first packet, and the
+ semaphore is disabled.
+
+ a "mid" packet slightly before the end of the frame - this packet should trigger
+ an interrupt so we can go and assign a timestamp to the first packet
+ in the next frame. We don't use the very last packet in the frame
+ for this purpose, because that would leave very little time to set
+ the timestamp before DMA starts on the next frame.
+ */
+
+ if (f->n_packets == 0) {
+ first_packet = 1;
+ } else if ( full_packets == (packets_per_frame-1) ) {
+ last_packet = 1;
+ } else if (f->n_packets == packets_per_frame) {
+ mid_packet = 1;
+ }
+
+
+ /********************/
+ /* setup CIP header */
+ /********************/
+
+ /* the timestamp will be written later from the
+ mid-frame interrupt handler. For now we just
+ store the address of the CIP header(s) that
+ need a timestamp. */
+
+ /* first packet in the frame needs a timestamp */
+ if (first_packet) {
+ f->cip_syt1 = cip;
+ if (empty_packet)
+ first_packet_empty = 1;
+
+ } else if (first_packet_empty && (f->n_packets == 1) ) {
+ /* if the first packet was empty, the second
+ packet's CIP header also needs a timestamp */
+ f->cip_syt2 = cip;
+ }
+
+ fill_cip_header(cip,
+ /* the node ID number of the OHCI card */
+ reg_read(video->ohci, OHCI1394_NodeID) & 0x3F,
+ video->continuity_counter,
+ video->pal_or_ntsc,
+ 0xFFFF /* the timestamp is filled in later */);
+
+ /* advance counter, only for full packets */
+ if ( ! empty_packet )
+ video->continuity_counter++;
+
+ /******************************/
+ /* setup DMA descriptor block */
+ /******************************/
+
+ /* first descriptor - OUTPUT_MORE_IMMEDIATE, for the controller's IT header */
+ fill_output_more_immediate( &(block->u.out.omi), 1, video->channel, 0, payload_size);
+
+ if (empty_packet) {
+ /* second descriptor - OUTPUT_LAST for CIP header */
+ fill_output_last( &(block->u.out.u.empty.ol),
+
+ /* want completion status on all interesting packets */
+ (first_packet || mid_packet || last_packet) ? 1 : 0,
+
+ /* want interrupts on all interesting packets */
+ (first_packet || mid_packet || last_packet) ? 1 : 0,
+
+ sizeof(struct CIP_header), /* data size */
+ cip_dma);
+
+ if (first_packet)
+ f->frame_begin_timestamp = &(block->u.out.u.empty.ol.q[3]);
+ else if (mid_packet)
+ f->mid_frame_timestamp = &(block->u.out.u.empty.ol.q[3]);
+ else if (last_packet) {
+ f->frame_end_timestamp = &(block->u.out.u.empty.ol.q[3]);
+ f->frame_end_branch = &(block->u.out.u.empty.ol.q[2]);
+ }
+
+ branch_address = &(block->u.out.u.empty.ol.q[2]);
+ n_descriptors = 3;
+ if (first_packet)
+ f->first_n_descriptors = n_descriptors;
+
+ } else { /* full packet */
+
+ /* second descriptor - OUTPUT_MORE for CIP header */
+ fill_output_more( &(block->u.out.u.full.om),
+ sizeof(struct CIP_header), /* data size */
+ cip_dma);
+
+
+ /* third (and possibly fourth) descriptor - for DV data */
+ /* the 480-byte payload can cross a page boundary; if so,
+ we need to split it into two DMA descriptors */
+
+ /* does the 480-byte data payload cross a page boundary? */
+ if ( (PAGE_SIZE- ((unsigned long)data_p % PAGE_SIZE) ) < 480 ) {
+
+ /* page boundary crossed */
+
+ fill_output_more( &(block->u.out.u.full.u.cross.om),
+ /* data size - how much of data_p fits on the first page */
+ PAGE_SIZE - (data_p % PAGE_SIZE),
+
+ /* DMA address of data_p */
+ dma_region_offset_to_bus(&video->dv_buf,
+ data_p - (unsigned long) video->dv_buf.kvirt));
+
+ fill_output_last( &(block->u.out.u.full.u.cross.ol),
+
+ /* want completion status on all interesting packets */
+ (first_packet || mid_packet || last_packet) ? 1 : 0,
+
+ /* want interrupt on all interesting packets */
+ (first_packet || mid_packet || last_packet) ? 1 : 0,
+
+ /* data size - remaining portion of data_p */
+ 480 - (PAGE_SIZE - (data_p % PAGE_SIZE)),
+
+ /* DMA address of data_p + PAGE_SIZE - (data_p % PAGE_SIZE) */
+ dma_region_offset_to_bus(&video->dv_buf,
+ data_p + PAGE_SIZE - (data_p % PAGE_SIZE) - (unsigned long) video->dv_buf.kvirt));
+
+ if (first_packet)
+ f->frame_begin_timestamp = &(block->u.out.u.full.u.cross.ol.q[3]);
+ else if (mid_packet)
+ f->mid_frame_timestamp = &(block->u.out.u.full.u.cross.ol.q[3]);
+ else if (last_packet) {
+ f->frame_end_timestamp = &(block->u.out.u.full.u.cross.ol.q[3]);
+ f->frame_end_branch = &(block->u.out.u.full.u.cross.ol.q[2]);
+ }
+
+ branch_address = &(block->u.out.u.full.u.cross.ol.q[2]);
+
+ n_descriptors = 5;
+ if (first_packet)
+ f->first_n_descriptors = n_descriptors;
+
+ full_packets++;
+
+ } else {
+ /* fits on one page */
+
+ fill_output_last( &(block->u.out.u.full.u.nocross.ol),
+
+ /* want completion status on all interesting packets */
+ (first_packet || mid_packet || last_packet) ? 1 : 0,
+
+ /* want interrupt on all interesting packets */
+ (first_packet || mid_packet || last_packet) ? 1 : 0,
+
+ 480, /* data size (480 bytes of DV data) */
+
+
+ /* DMA address of data_p */
+ dma_region_offset_to_bus(&video->dv_buf,
+ data_p - (unsigned long) video->dv_buf.kvirt));
+
+ if (first_packet)
+ f->frame_begin_timestamp = &(block->u.out.u.full.u.nocross.ol.q[3]);
+ else if (mid_packet)
+ f->mid_frame_timestamp = &(block->u.out.u.full.u.nocross.ol.q[3]);
+ else if (last_packet) {
+ f->frame_end_timestamp = &(block->u.out.u.full.u.nocross.ol.q[3]);
+ f->frame_end_branch = &(block->u.out.u.full.u.nocross.ol.q[2]);
+ }
+
+ branch_address = &(block->u.out.u.full.u.nocross.ol.q[2]);
+
+ n_descriptors = 4;
+ if (first_packet)
+ f->first_n_descriptors = n_descriptors;
+
+ full_packets++;
+ }
+ }
+
+ /* link this descriptor block into the DMA program by filling in
+ the branch address of the previous block */
+
+ /* note: we are not linked into the active DMA chain yet */
+
+ if (last_branch_address) {
+ *(last_branch_address) = cpu_to_le32(block_dma | n_descriptors);
+ }
+
+ last_branch_address = branch_address;
+
+
+ f->n_packets++;
+
+ }
+
+ /* when we first assemble a new frame, set the final branch
+ to loop back up to the top */
+ *(f->frame_end_branch) = cpu_to_le32(f->descriptor_pool_dma | f->first_n_descriptors);
+
+ /* make the latest version of this frame visible to the PCI card */
+ dma_region_sync_for_device(&video->dv_buf, f->data - (unsigned long) video->dv_buf.kvirt, video->frame_size);
+
+ /* lock against DMA interrupt */
+ spin_lock_irqsave(&video->spinlock, irq_flags);
+
+ f->state = FRAME_READY;
+
+ video->n_clear_frames--;
+
+ last_frame = video->first_clear_frame - 1;
+ if (last_frame == -1)
+ last_frame = video->n_frames-1;
+
+ video->first_clear_frame = (video->first_clear_frame + 1) % video->n_frames;
+
+ irq_printk(" frame %d prepared, active_frame = %d, n_clear_frames = %d, first_clear_frame = %d\n last=%d\n",
+ this_frame, video->active_frame, video->n_clear_frames, video->first_clear_frame, last_frame);
+
+ irq_printk(" begin_ts %08lx mid_ts %08lx end_ts %08lx end_br %08lx\n",
+ (unsigned long) f->frame_begin_timestamp,
+ (unsigned long) f->mid_frame_timestamp,
+ (unsigned long) f->frame_end_timestamp,
+ (unsigned long) f->frame_end_branch);
+
+ if (video->active_frame != -1) {
+
+ /* if DMA is already active, we are almost done */
+ /* just link us onto the active DMA chain */
+ if (video->frames[last_frame]->frame_end_branch) {
+ u32 temp;
+
+ /* point the previous frame's tail to this frame's head */
+ *(video->frames[last_frame]->frame_end_branch) = cpu_to_le32(f->descriptor_pool_dma | f->first_n_descriptors);
+
+ /* this write MUST precede the next one, or we could silently drop frames */
+ wmb();
+
+ /* disable the want_status semaphore on the last packet */
+ temp = le32_to_cpu(*(video->frames[last_frame]->frame_end_branch - 2));
+ temp &= 0xF7CFFFFF;
+ *(video->frames[last_frame]->frame_end_branch - 2) = cpu_to_le32(temp);
+
+ /* flush these writes to memory ASAP */
+ flush_pci_write(video->ohci);
+
+ /* NOTE:
+ ideally the writes should be "atomic": if
+ the OHCI card reads the want_status flag in
+ between them, we'll falsely report a
+ dropped frame. Hopefully this window is too
+ small to really matter, and the consequence
+ is rather harmless. */
+
+
+ irq_printk(" new frame %d linked onto DMA chain\n", this_frame);
+
+ } else {
+ printk(KERN_ERR "dv1394: last frame not ready???\n");
+ }
+
+ } else {
+
+ u32 transmit_sec, transmit_cyc;
+ u32 ts_cyc, ts_off;
+
+ /* DMA is stopped, so this is the very first frame */
+ video->active_frame = this_frame;
+
+ /* set CommandPtr to address and size of first descriptor block */
+ reg_write(video->ohci, video->ohci_IsoXmitCommandPtr,
+ video->frames[video->active_frame]->descriptor_pool_dma |
+ f->first_n_descriptors);
+
+ /* assign a timestamp based on the current cycle time...
+ We'll tell the card to begin DMA 100 cycles from now,
+ and assign a timestamp 103 cycles from now */
+
+ cycleTimer = reg_read(video->ohci, OHCI1394_IsochronousCycleTimer);
+
+ ct_sec = cycleTimer >> 25;
+ ct_cyc = (cycleTimer >> 12) & 0x1FFF;
+ ct_off = cycleTimer & 0xFFF;
+
+ transmit_sec = ct_sec;
+ transmit_cyc = ct_cyc + 100;
+
+ transmit_sec += transmit_cyc/8000;
+ transmit_cyc %= 8000;
+
+ ts_off = ct_off;
+ ts_cyc = transmit_cyc + 3;
+ ts_cyc %= 8000;
+
+ f->assigned_timestamp = (ts_cyc&0xF) << 12;
+
+ /* now actually write the timestamp into the appropriate CIP headers */
+ if (f->cip_syt1) {
+ f->cip_syt1->b[6] = f->assigned_timestamp >> 8;
+ f->cip_syt1->b[7] = f->assigned_timestamp & 0xFF;
+ }
+ if (f->cip_syt2) {
+ f->cip_syt2->b[6] = f->assigned_timestamp >> 8;
+ f->cip_syt2->b[7] = f->assigned_timestamp & 0xFF;
+ }
+
+ /* --- start DMA --- */
+
+ /* clear all bits in ContextControl register */
+
+ reg_write(video->ohci, video->ohci_IsoXmitContextControlClear, 0xFFFFFFFF);
+ wmb();
+
+ /* the OHCI card has the ability to start ISO transmission on a
+ particular cycle (start-on-cycle). This way we can ensure that
+ the first DV frame will have an accurate timestamp.
+
+ However, start-on-cycle only appears to work if the OHCI card
+ is cycle master! Since the consequences of messing up the first
+ timestamp are minimal*, just disable start-on-cycle for now.
+
+ * my DV deck drops the first few frames before it "locks in;"
+ so the first frame having an incorrect timestamp is inconsequential.
+ */
+
+#if 0
+ reg_write(video->ohci, video->ohci_IsoXmitContextControlSet,
+ (1 << 31) /* enable start-on-cycle */
+ | ( (transmit_sec & 0x3) << 29)
+ | (transmit_cyc << 16));
+ wmb();
+#endif
+
+ video->dma_running = 1;
+
+ /* set the 'run' bit */
+ reg_write(video->ohci, video->ohci_IsoXmitContextControlSet, 0x8000);
+ flush_pci_write(video->ohci);
+
+ /* --- DMA should be running now --- */
+
+ debug_printk(" Cycle = %4u ContextControl = %08x CmdPtr = %08x\n",
+ (reg_read(video->ohci, OHCI1394_IsochronousCycleTimer) >> 12) & 0x1FFF,
+ reg_read(video->ohci, video->ohci_IsoXmitContextControlSet),
+ reg_read(video->ohci, video->ohci_IsoXmitCommandPtr));
+
+ debug_printk(" DMA start - current cycle %4u, transmit cycle %4u (%2u), assigning ts cycle %2u\n",
+ ct_cyc, transmit_cyc, transmit_cyc & 0xF, ts_cyc & 0xF);
+
+#if DV1394_DEBUG_LEVEL >= 2
+ {
+ /* check if DMA is really running */
+ int i = 0;
+ while (i < 20) {
+ mb();
+ mdelay(1);
+ if (reg_read(video->ohci, video->ohci_IsoXmitContextControlSet) & (1 << 10)) {
+ printk("DMA ACTIVE after %d msec\n", i);
+ break;
+ }
+ i++;
+ }
+
+ printk("set = %08x, cmdPtr = %08x\n",
+ reg_read(video->ohci, video->ohci_IsoXmitContextControlSet),
+ reg_read(video->ohci, video->ohci_IsoXmitCommandPtr)
+ );
+
+ if ( ! (reg_read(video->ohci, video->ohci_IsoXmitContextControlSet) & (1 << 10)) ) {
+ printk("DMA did NOT go active after 20ms, event = %x\n",
+ reg_read(video->ohci, video->ohci_IsoXmitContextControlSet) & 0x1F);
+ } else
+ printk("DMA is RUNNING!\n");
+ }
+#endif
+
+ }
+
+
+ spin_unlock_irqrestore(&video->spinlock, irq_flags);
+}
+
+
+
+/*** RECEIVE FUNCTIONS *****************************************************/
+
+/*
+ frame method put_packet
+
+ map and copy the packet data to its location in the frame
+ based upon DIF section and sequence
+*/
+
+static void inline
+frame_put_packet (struct frame *f, struct packet *p)
+{
+ int section_type = p->data[0] >> 5; /* section type is in bits 5 - 7 */
+ int dif_sequence = p->data[1] >> 4; /* dif sequence number is in bits 4 - 7 */
+ int dif_block = p->data[2];
+
+ /* sanity check */
+ if (dif_sequence > 11 || dif_block > 149) return;
+
+ switch (section_type) {
+ case 0: /* 1 Header block */
+ memcpy( (void *) f->data + dif_sequence * 150 * 80, p->data, 480);
+ break;
+
+ case 1: /* 2 Subcode blocks */
+ memcpy( (void *) f->data + dif_sequence * 150 * 80 + (1 + dif_block) * 80, p->data, 480);
+ break;
+
+ case 2: /* 3 VAUX blocks */
+ memcpy( (void *) f->data + dif_sequence * 150 * 80 + (3 + dif_block) * 80, p->data, 480);
+ break;
+
+ case 3: /* 9 Audio blocks interleaved with video */
+ memcpy( (void *) f->data + dif_sequence * 150 * 80 + (6 + dif_block * 16) * 80, p->data, 480);
+ break;
+
+ case 4: /* 135 Video blocks interleaved with audio */
+ memcpy( (void *) f->data + dif_sequence * 150 * 80 + (7 + (dif_block / 15) + dif_block) * 80, p->data, 480);
+ break;
+
+ default: /* we can not handle any other data */
+ break;
+ }
+}
+
+
+static void start_dma_receive(struct video_card *video)
+{
+ if (video->first_run == 1) {
+ video->first_run = 0;
+
+ /* start DMA once all of the frames are READY */
+ video->n_clear_frames = 0;
+ video->first_clear_frame = -1;
+ video->current_packet = 0;
+ video->active_frame = 0;
+
+ /* reset iso recv control register */
+ reg_write(video->ohci, video->ohci_IsoRcvContextControlClear, 0xFFFFFFFF);
+ wmb();
+
+ /* clear bufferFill, set isochHeader and speed (0=100) */
+ reg_write(video->ohci, video->ohci_IsoRcvContextControlSet, 0x40000000);
+
+ /* match on all tags, listen on channel */
+ reg_write(video->ohci, video->ohci_IsoRcvContextMatch, 0xf0000000 | video->channel);
+
+ /* address and first descriptor block + Z=1 */
+ reg_write(video->ohci, video->ohci_IsoRcvCommandPtr,
+ video->frames[0]->descriptor_pool_dma | 1); /* Z=1 */
+ wmb();
+
+ video->dma_running = 1;
+
+ /* run */
+ reg_write(video->ohci, video->ohci_IsoRcvContextControlSet, 0x8000);
+ flush_pci_write(video->ohci);
+
+ debug_printk("dv1394: DMA started\n");
+
+#if DV1394_DEBUG_LEVEL >= 2
+ {
+ int i;
+
+ for (i = 0; i < 1000; ++i) {
+ mdelay(1);
+ if (reg_read(video->ohci, video->ohci_IsoRcvContextControlSet) & (1 << 10)) {
+ printk("DMA ACTIVE after %d msec\n", i);
+ break;
+ }
+ }
+ if ( reg_read(video->ohci, video->ohci_IsoRcvContextControlSet) & (1 << 11) ) {
+ printk("DEAD, event = %x\n",
+ reg_read(video->ohci, video->ohci_IsoRcvContextControlSet) & 0x1F);
+ } else
+ printk("RUNNING!\n");
+ }
+#endif
+ } else if ( reg_read(video->ohci, video->ohci_IsoRcvContextControlSet) & (1 << 11) ) {
+ debug_printk("DEAD, event = %x\n",
+ reg_read(video->ohci, video->ohci_IsoRcvContextControlSet) & 0x1F);
+
+ /* wake */
+ reg_write(video->ohci, video->ohci_IsoRcvContextControlSet, (1 << 12));
+ }
+}
+
+
+/*
+ receive_packets() - build the DMA program for receiving
+*/
+
+static void receive_packets(struct video_card *video)
+{
+ struct DMA_descriptor_block *block = NULL;
+ dma_addr_t block_dma = 0;
+ struct packet *data = NULL;
+ dma_addr_t data_dma = 0;
+ u32 *last_branch_address = NULL;
+ unsigned long irq_flags;
+ int want_interrupt = 0;
+ struct frame *f = NULL;
+ int i, j;
+
+ spin_lock_irqsave(&video->spinlock, irq_flags);
+
+ for (j = 0; j < video->n_frames; j++) {
+
+ /* connect frames */
+ if (j > 0 && f != NULL && f->frame_end_branch != NULL)
+ *(f->frame_end_branch) = cpu_to_le32(video->frames[j]->descriptor_pool_dma | 1); /* set Z=1 */
+
+ f = video->frames[j];
+
+ for (i = 0; i < MAX_PACKETS; i++) {
+ /* locate a descriptor block and packet from the buffer */
+ block = &(f->descriptor_pool[i]);
+ block_dma = ((unsigned long) block - (unsigned long) f->descriptor_pool) + f->descriptor_pool_dma;
+
+ data = ((struct packet*)video->packet_buf.kvirt) + f->frame_num * MAX_PACKETS + i;
+ data_dma = dma_region_offset_to_bus( &video->packet_buf,
+ ((unsigned long) data - (unsigned long) video->packet_buf.kvirt) );
+
+ /* setup DMA descriptor block */
+ want_interrupt = ((i % (MAX_PACKETS/2)) == 0 || i == (MAX_PACKETS-1));
+ fill_input_last( &(block->u.in.il), want_interrupt, 512, data_dma);
+
+ /* link descriptors */
+ last_branch_address = f->frame_end_branch;
+
+ if (last_branch_address != NULL)
+ *(last_branch_address) = cpu_to_le32(block_dma | 1); /* set Z=1 */
+
+ f->frame_end_branch = &(block->u.in.il.q[2]);
+ }
+
+ } /* next j */
+
+ spin_unlock_irqrestore(&video->spinlock, irq_flags);
+
+}
+
+
+
+/*** MANAGEMENT FUNCTIONS **************************************************/
+
+static int do_dv1394_init(struct video_card *video, struct dv1394_init *init)
+{
+ unsigned long flags, new_buf_size;
+ int i;
+ u64 chan_mask;
+ int retval = -EINVAL;
+
+ debug_printk("dv1394: initialising %d\n", video->id);
+ if (init->api_version != DV1394_API_VERSION)
+ return -EINVAL;
+
+ /* first sanitize all the parameters */
+ if ( (init->n_frames < 2) || (init->n_frames > DV1394_MAX_FRAMES) )
+ return -EINVAL;
+
+ if ( (init->format != DV1394_NTSC) && (init->format != DV1394_PAL) )
+ return -EINVAL;
+
+ if ( (init->syt_offset == 0) || (init->syt_offset > 50) )
+ /* default SYT offset is 3 cycles */
+ init->syt_offset = 3;
+
+ if (init->channel > 63)
+ init->channel = 63;
+
+ chan_mask = (u64)1 << init->channel;
+
+ /* calculate what size DMA buffer is needed */
+ if (init->format == DV1394_NTSC)
+ new_buf_size = DV1394_NTSC_FRAME_SIZE * init->n_frames;
+ else
+ new_buf_size = DV1394_PAL_FRAME_SIZE * init->n_frames;
+
+ /* round up to PAGE_SIZE */
+ if (new_buf_size % PAGE_SIZE) new_buf_size += PAGE_SIZE - (new_buf_size % PAGE_SIZE);
+
+ /* don't allow the user to allocate the DMA buffer more than once */
+ if (video->dv_buf.kvirt && video->dv_buf_size != new_buf_size) {
+ printk("dv1394: re-sizing the DMA buffer is not allowed\n");
+ return -EINVAL;
+ }
+
+ /* shutdown the card if it's currently active */
+ /* (the card should not be reset if the parameters are screwy) */
+
+ do_dv1394_shutdown(video, 0);
+
+ /* try to claim the ISO channel */
+ spin_lock_irqsave(&video->ohci->IR_channel_lock, flags);
+ if (video->ohci->ISO_channel_usage & chan_mask) {
+ spin_unlock_irqrestore(&video->ohci->IR_channel_lock, flags);
+ retval = -EBUSY;
+ goto err;
+ }
+ video->ohci->ISO_channel_usage |= chan_mask;
+ spin_unlock_irqrestore(&video->ohci->IR_channel_lock, flags);
+
+ video->channel = init->channel;
+
+ /* initialize misc. fields of video */
+ video->n_frames = init->n_frames;
+ video->pal_or_ntsc = init->format;
+
+ video->cip_accum = 0;
+ video->continuity_counter = 0;
+
+ video->active_frame = -1;
+ video->first_clear_frame = 0;
+ video->n_clear_frames = video->n_frames;
+ video->dropped_frames = 0;
+
+ video->write_off = 0;
+
+ video->first_run = 1;
+ video->current_packet = -1;
+ video->first_frame = 0;
+
+ if (video->pal_or_ntsc == DV1394_NTSC) {
+ video->cip_n = init->cip_n != 0 ? init->cip_n : CIP_N_NTSC;
+ video->cip_d = init->cip_d != 0 ? init->cip_d : CIP_D_NTSC;
+ video->frame_size = DV1394_NTSC_FRAME_SIZE;
+ } else {
+ video->cip_n = init->cip_n != 0 ? init->cip_n : CIP_N_PAL;
+ video->cip_d = init->cip_d != 0 ? init->cip_d : CIP_D_PAL;
+ video->frame_size = DV1394_PAL_FRAME_SIZE;
+ }
+
+ video->syt_offset = init->syt_offset;
+
+ /* find and claim DMA contexts on the OHCI card */
+
+ if (video->ohci_it_ctx == -1) {
+ ohci1394_init_iso_tasklet(&video->it_tasklet, OHCI_ISO_TRANSMIT,
+ it_tasklet_func, (unsigned long) video);
+
+ if (ohci1394_register_iso_tasklet(video->ohci, &video->it_tasklet) < 0) {
+ printk(KERN_ERR "dv1394: could not find an available IT DMA context\n");
+ retval = -EBUSY;
+ goto err;
+ }
+
+ video->ohci_it_ctx = video->it_tasklet.context;
+ debug_printk("dv1394: claimed IT DMA context %d\n", video->ohci_it_ctx);
+ }
+
+ if (video->ohci_ir_ctx == -1) {
+ ohci1394_init_iso_tasklet(&video->ir_tasklet, OHCI_ISO_RECEIVE,
+ ir_tasklet_func, (unsigned long) video);
+
+ if (ohci1394_register_iso_tasklet(video->ohci, &video->ir_tasklet) < 0) {
+ printk(KERN_ERR "dv1394: could not find an available IR DMA context\n");
+ retval = -EBUSY;
+ goto err;
+ }
+ video->ohci_ir_ctx = video->ir_tasklet.context;
+ debug_printk("dv1394: claimed IR DMA context %d\n", video->ohci_ir_ctx);
+ }
+
+ /* allocate struct frames */
+ for (i = 0; i < init->n_frames; i++) {
+ video->frames[i] = frame_new(i, video);
+
+ if (!video->frames[i]) {
+ printk(KERN_ERR "dv1394: Cannot allocate frame structs\n");
+ retval = -ENOMEM;
+ goto err;
+ }
+ }
+
+ if (!video->dv_buf.kvirt) {
+ /* allocate the ringbuffer */
+ retval = dma_region_alloc(&video->dv_buf, new_buf_size, video->ohci->dev, PCI_DMA_TODEVICE);
+ if (retval)
+ goto err;
+
+ video->dv_buf_size = new_buf_size;
+
+ debug_printk("dv1394: Allocated %d frame buffers, total %u pages (%u DMA pages), %lu bytes\n",
+ video->n_frames, video->dv_buf.n_pages,
+ video->dv_buf.n_dma_pages, video->dv_buf_size);
+ }
+
+ /* set up the frame->data pointers */
+ for (i = 0; i < video->n_frames; i++)
+ video->frames[i]->data = (unsigned long) video->dv_buf.kvirt + i * video->frame_size;
+
+ if (!video->packet_buf.kvirt) {
+ /* allocate packet buffer */
+ video->packet_buf_size = sizeof(struct packet) * video->n_frames * MAX_PACKETS;
+ if (video->packet_buf_size % PAGE_SIZE)
+ video->packet_buf_size += PAGE_SIZE - (video->packet_buf_size % PAGE_SIZE);
+
+ retval = dma_region_alloc(&video->packet_buf, video->packet_buf_size,
+ video->ohci->dev, PCI_DMA_FROMDEVICE);
+ if (retval)
+ goto err;
+
+ debug_printk("dv1394: Allocated %d packets in buffer, total %u pages (%u DMA pages), %lu bytes\n",
+ video->n_frames*MAX_PACKETS, video->packet_buf.n_pages,
+ video->packet_buf.n_dma_pages, video->packet_buf_size);
+ }
+
+ /* set up register offsets for IT context */
+ /* IT DMA context registers are spaced 16 bytes apart */
+ video->ohci_IsoXmitContextControlSet = OHCI1394_IsoXmitContextControlSet+16*video->ohci_it_ctx;
+ video->ohci_IsoXmitContextControlClear = OHCI1394_IsoXmitContextControlClear+16*video->ohci_it_ctx;
+ video->ohci_IsoXmitCommandPtr = OHCI1394_IsoXmitCommandPtr+16*video->ohci_it_ctx;
+
+ /* enable interrupts for IT context */
+ reg_write(video->ohci, OHCI1394_IsoXmitIntMaskSet, (1 << video->ohci_it_ctx));
+ debug_printk("dv1394: interrupts enabled for IT context %d\n", video->ohci_it_ctx);
+
+ /* set up register offsets for IR context */
+ /* IR DMA context registers are spaced 32 bytes apart */
+ video->ohci_IsoRcvContextControlSet = OHCI1394_IsoRcvContextControlSet+32*video->ohci_ir_ctx;
+ video->ohci_IsoRcvContextControlClear = OHCI1394_IsoRcvContextControlClear+32*video->ohci_ir_ctx;
+ video->ohci_IsoRcvCommandPtr = OHCI1394_IsoRcvCommandPtr+32*video->ohci_ir_ctx;
+ video->ohci_IsoRcvContextMatch = OHCI1394_IsoRcvContextMatch+32*video->ohci_ir_ctx;
+
+ /* enable interrupts for IR context */
+ reg_write(video->ohci, OHCI1394_IsoRecvIntMaskSet, (1 << video->ohci_ir_ctx) );
+ debug_printk("dv1394: interrupts enabled for IR context %d\n", video->ohci_ir_ctx);
+
+ return 0;
+
+err:
+ do_dv1394_shutdown(video, 1);
+ return retval;
+}
+
+/* if the user doesn't bother to call ioctl(INIT) before starting
+ mmap() or read()/write(), just give him some default values */
+
+static int do_dv1394_init_default(struct video_card *video)
+{
+ struct dv1394_init init;
+
+ init.api_version = DV1394_API_VERSION;
+ init.n_frames = DV1394_MAX_FRAMES / 4;
+ init.channel = video->channel;
+ init.format = video->pal_or_ntsc;
+ init.cip_n = video->cip_n;
+ init.cip_d = video->cip_d;
+ init.syt_offset = video->syt_offset;
+
+ return do_dv1394_init(video, &init);
+}
+
+/* do NOT call from interrupt context */
+static void stop_dma(struct video_card *video)
+{
+ unsigned long flags;
+ int i;
+
+ /* no interrupts */
+ spin_lock_irqsave(&video->spinlock, flags);
+
+ video->dma_running = 0;
+
+ if ( (video->ohci_it_ctx == -1) && (video->ohci_ir_ctx == -1) )
+ goto out;
+
+ /* stop DMA if in progress */
+ if ( (video->active_frame != -1) ||
+ (reg_read(video->ohci, video->ohci_IsoXmitContextControlClear) & (1 << 10)) ||
+ (reg_read(video->ohci, video->ohci_IsoRcvContextControlClear) & (1 << 10)) ) {
+
+ /* clear the .run bits */
+ reg_write(video->ohci, video->ohci_IsoXmitContextControlClear, (1 << 15));
+ reg_write(video->ohci, video->ohci_IsoRcvContextControlClear, (1 << 15));
+ flush_pci_write(video->ohci);
+
+ video->active_frame = -1;
+ video->first_run = 1;
+
+ /* wait until DMA really stops */
+ i = 0;
+ while (i < 1000) {
+
+ /* wait 0.1 millisecond */
+ udelay(100);
+
+ if ( (reg_read(video->ohci, video->ohci_IsoXmitContextControlClear) & (1 << 10)) ||
+ (reg_read(video->ohci, video->ohci_IsoRcvContextControlClear) & (1 << 10)) ) {
+ /* still active */
+ debug_printk("dv1394: stop_dma: DMA not stopped yet\n" );
+ mb();
+ } else {
+ debug_printk("dv1394: stop_dma: DMA stopped safely after %d ms\n", i/10);
+ break;
+ }
+
+ i++;
+ }
+
+ if (i == 1000) {
+ printk(KERN_ERR "dv1394: stop_dma: DMA still going after %d ms!\n", i/10);
+ }
+ }
+ else
+ debug_printk("dv1394: stop_dma: already stopped.\n");
+
+out:
+ spin_unlock_irqrestore(&video->spinlock, flags);
+}
+
+
+
+static void do_dv1394_shutdown(struct video_card *video, int free_dv_buf)
+{
+ int i;
+
+ debug_printk("dv1394: shutdown...\n");
+
+ /* stop DMA if in progress */
+ stop_dma(video);
+
+ /* release the DMA contexts */
+ if (video->ohci_it_ctx != -1) {
+ video->ohci_IsoXmitContextControlSet = 0;
+ video->ohci_IsoXmitContextControlClear = 0;
+ video->ohci_IsoXmitCommandPtr = 0;
+
+ /* disable interrupts for IT context */
+ reg_write(video->ohci, OHCI1394_IsoXmitIntMaskClear, (1 << video->ohci_it_ctx));
+
+ /* remove tasklet */
+ ohci1394_unregister_iso_tasklet(video->ohci, &video->it_tasklet);
+ debug_printk("dv1394: IT context %d released\n", video->ohci_it_ctx);
+ video->ohci_it_ctx = -1;
+ }
+
+ if (video->ohci_ir_ctx != -1) {
+ video->ohci_IsoRcvContextControlSet = 0;
+ video->ohci_IsoRcvContextControlClear = 0;
+ video->ohci_IsoRcvCommandPtr = 0;
+ video->ohci_IsoRcvContextMatch = 0;
+
+ /* disable interrupts for IR context */
+ reg_write(video->ohci, OHCI1394_IsoRecvIntMaskClear, (1 << video->ohci_ir_ctx));
+
+ /* remove tasklet */
+ ohci1394_unregister_iso_tasklet(video->ohci, &video->ir_tasklet);
+ debug_printk("dv1394: IR context %d released\n", video->ohci_ir_ctx);
+ video->ohci_ir_ctx = -1;
+ }
+
+ /* release the ISO channel */
+ if (video->channel != -1) {
+ u64 chan_mask;
+ unsigned long flags;
+
+ chan_mask = (u64)1 << video->channel;
+
+ spin_lock_irqsave(&video->ohci->IR_channel_lock, flags);
+ video->ohci->ISO_channel_usage &= ~(chan_mask);
+ spin_unlock_irqrestore(&video->ohci->IR_channel_lock, flags);
+
+ video->channel = -1;
+ }
+
+ /* free the frame structs */
+ for (i = 0; i < DV1394_MAX_FRAMES; i++) {
+ if (video->frames[i])
+ frame_delete(video->frames[i]);
+ video->frames[i] = NULL;
+ }
+
+ video->n_frames = 0;
+
+ /* we can't free the DMA buffer unless it is guaranteed that
+ no more user-space mappings exist */
+
+ if (free_dv_buf) {
+ dma_region_free(&video->dv_buf);
+ video->dv_buf_size = 0;
+ }
+
+ /* free packet buffer */
+ dma_region_free(&video->packet_buf);
+ video->packet_buf_size = 0;
+
+ debug_printk("dv1394: shutdown OK\n");
+}
+
+/*
+ **********************************
+ *** MMAP() THEORY OF OPERATION ***
+ **********************************
+
+ The ringbuffer cannot be re-allocated or freed while
+ a user program maintains a mapping of it. (note that a mapping
+ can persist even after the device fd is closed!)
+
+ So, only let the user process allocate the DMA buffer once.
+ To resize or deallocate it, you must close the device file
+ and open it again.
+
+ Previously Dan M. hacked out a scheme that allowed the DMA
+ buffer to change by forcefully unmapping it from the user's
+ address space. It was prone to error because it's very hard to
+ track all the places the buffer could have been mapped (we
+ would have had to walk the vma list of every process in the
+ system to be sure we found all the mappings!). Instead, we
+ force the user to choose one buffer size and stick with
+ it. This small sacrifice is worth the huge reduction in
+ error-prone code in dv1394.
+*/
+
+static int dv1394_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct video_card *video = file_to_video_card(file);
+ int retval = -EINVAL;
+
+ /*
+ * We cannot use the blocking variant mutex_lock here because .mmap
+ * is called with mmap_sem held, while .ioctl, .read, .write acquire
+ * video->mtx and subsequently call copy_to/from_user which will
+ * grab mmap_sem in case of a page fault.
+ */
+ if (!mutex_trylock(&video->mtx))
+ return -EAGAIN;
+
+ if ( ! video_card_initialized(video) ) {
+ retval = do_dv1394_init_default(video);
+ if (retval)
+ goto out;
+ }
+
+ retval = dma_region_mmap(&video->dv_buf, file, vma);
+out:
+ mutex_unlock(&video->mtx);
+ return retval;
+}
+
+/*** DEVICE FILE INTERFACE *************************************************/
+
+/* no need to serialize, multiple threads OK */
+static unsigned int dv1394_poll(struct file *file, struct poll_table_struct *wait)
+{
+ struct video_card *video = file_to_video_card(file);
+ unsigned int mask = 0;
+ unsigned long flags;
+
+ poll_wait(file, &video->waitq, wait);
+
+ spin_lock_irqsave(&video->spinlock, flags);
+ if ( video->n_frames == 0 ) {
+
+ } else if ( video->active_frame == -1 ) {
+ /* nothing going on */
+ mask |= POLLOUT;
+ } else {
+ /* any clear/ready buffers? */
+ if (video->n_clear_frames >0)
+ mask |= POLLOUT | POLLIN;
+ }
+ spin_unlock_irqrestore(&video->spinlock, flags);
+
+ return mask;
+}
+
+static int dv1394_fasync(int fd, struct file *file, int on)
+{
+ /* I just copied this code verbatim from Alan Cox's mouse driver example
+ (Documentation/DocBook/) */
+
+ struct video_card *video = file_to_video_card(file);
+
+ int retval = fasync_helper(fd, file, on, &video->fasync);
+
+ if (retval < 0)
+ return retval;
+ return 0;
+}
+
+static ssize_t dv1394_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
+{
+ struct video_card *video = file_to_video_card(file);
+ DECLARE_WAITQUEUE(wait, current);
+ ssize_t ret;
+ size_t cnt;
+ unsigned long flags;
+ int target_frame;
+
+ /* serialize this to prevent multi-threaded mayhem */
+ if (file->f_flags & O_NONBLOCK) {
+ if (!mutex_trylock(&video->mtx))
+ return -EAGAIN;
+ } else {
+ if (mutex_lock_interruptible(&video->mtx))
+ return -ERESTARTSYS;
+ }
+
+ if ( !video_card_initialized(video) ) {
+ ret = do_dv1394_init_default(video);
+ if (ret) {
+ mutex_unlock(&video->mtx);
+ return ret;
+ }
+ }
+
+ ret = 0;
+ add_wait_queue(&video->waitq, &wait);
+
+ while (count > 0) {
+
+ /* must set TASK_INTERRUPTIBLE *before* checking for free
+ buffers; otherwise we could miss a wakeup if the interrupt
+ fires between the check and the schedule() */
+
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ spin_lock_irqsave(&video->spinlock, flags);
+
+ target_frame = video->first_clear_frame;
+
+ spin_unlock_irqrestore(&video->spinlock, flags);
+
+ if (video->frames[target_frame]->state == FRAME_CLEAR) {
+
+ /* how much room is left in the target frame buffer */
+ cnt = video->frame_size - (video->write_off - target_frame * video->frame_size);
+
+ } else {
+ /* buffer is already used */
+ cnt = 0;
+ }
+
+ if (cnt > count)
+ cnt = count;
+
+ if (cnt <= 0) {
+ /* no room left, gotta wait */
+ if (file->f_flags & O_NONBLOCK) {
+ if (!ret)
+ ret = -EAGAIN;
+ break;
+ }
+ if (signal_pending(current)) {
+ if (!ret)
+ ret = -ERESTARTSYS;
+ break;
+ }
+
+ schedule();
+
+ continue; /* start over from 'while(count > 0)...' */
+ }
+
+ if (copy_from_user(video->dv_buf.kvirt + video->write_off, buffer, cnt)) {
+ if (!ret)
+ ret = -EFAULT;
+ break;
+ }
+
+ video->write_off = (video->write_off + cnt) % (video->n_frames * video->frame_size);
+
+ count -= cnt;
+ buffer += cnt;
+ ret += cnt;
+
+ if (video->write_off == video->frame_size * ((target_frame + 1) % video->n_frames))
+ frame_prepare(video, target_frame);
+ }
+
+ remove_wait_queue(&video->waitq, &wait);
+ set_current_state(TASK_RUNNING);
+ mutex_unlock(&video->mtx);
+ return ret;
+}
+
+
+static ssize_t dv1394_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
+{
+ struct video_card *video = file_to_video_card(file);
+ DECLARE_WAITQUEUE(wait, current);
+ ssize_t ret;
+ size_t cnt;
+ unsigned long flags;
+ int target_frame;
+
+ /* serialize this to prevent multi-threaded mayhem */
+ if (file->f_flags & O_NONBLOCK) {
+ if (!mutex_trylock(&video->mtx))
+ return -EAGAIN;
+ } else {
+ if (mutex_lock_interruptible(&video->mtx))
+ return -ERESTARTSYS;
+ }
+
+ if ( !video_card_initialized(video) ) {
+ ret = do_dv1394_init_default(video);
+ if (ret) {
+ mutex_unlock(&video->mtx);
+ return ret;
+ }
+ video->continuity_counter = -1;
+
+ receive_packets(video);
+
+ start_dma_receive(video);
+ }
+
+ ret = 0;
+ add_wait_queue(&video->waitq, &wait);
+
+ while (count > 0) {
+
+ /* must set TASK_INTERRUPTIBLE *before* checking for free
+ buffers; otherwise we could miss a wakeup if the interrupt
+ fires between the check and the schedule() */
+
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ spin_lock_irqsave(&video->spinlock, flags);
+
+ target_frame = video->first_clear_frame;
+
+ spin_unlock_irqrestore(&video->spinlock, flags);
+
+ if (target_frame >= 0 &&
+ video->n_clear_frames > 0 &&
+ video->frames[target_frame]->state == FRAME_CLEAR) {
+
+ /* how much room is left in the target frame buffer */
+ cnt = video->frame_size - (video->write_off - target_frame * video->frame_size);
+
+ } else {
+ /* buffer is already used */
+ cnt = 0;
+ }
+
+ if (cnt > count)
+ cnt = count;
+
+ if (cnt <= 0) {
+ /* no room left, gotta wait */
+ if (file->f_flags & O_NONBLOCK) {
+ if (!ret)
+ ret = -EAGAIN;
+ break;
+ }
+ if (signal_pending(current)) {
+ if (!ret)
+ ret = -ERESTARTSYS;
+ break;
+ }
+
+ schedule();
+
+ continue; /* start over from 'while(count > 0)...' */
+ }
+
+ if (copy_to_user(buffer, video->dv_buf.kvirt + video->write_off, cnt)) {
+ if (!ret)
+ ret = -EFAULT;
+ break;
+ }
+
+ video->write_off = (video->write_off + cnt) % (video->n_frames * video->frame_size);
+
+ count -= cnt;
+ buffer += cnt;
+ ret += cnt;
+
+ if (video->write_off == video->frame_size * ((target_frame + 1) % video->n_frames)) {
+ spin_lock_irqsave(&video->spinlock, flags);
+ video->n_clear_frames--;
+ video->first_clear_frame = (video->first_clear_frame + 1) % video->n_frames;
+ spin_unlock_irqrestore(&video->spinlock, flags);
+ }
+ }
+
+ remove_wait_queue(&video->waitq, &wait);
+ set_current_state(TASK_RUNNING);
+ mutex_unlock(&video->mtx);
+ return ret;
+}
+
+
+/*** DEVICE IOCTL INTERFACE ************************************************/
+
+static long dv1394_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct video_card *video = file_to_video_card(file);
+ unsigned long flags;
+ int ret = -EINVAL;
+ void __user *argp = (void __user *)arg;
+
+ DECLARE_WAITQUEUE(wait, current);
+
+ /* serialize this to prevent multi-threaded mayhem */
+ if (file->f_flags & O_NONBLOCK) {
+ if (!mutex_trylock(&video->mtx))
+ return -EAGAIN;
+ } else {
+ if (mutex_lock_interruptible(&video->mtx))
+ return -ERESTARTSYS;
+ }
+
+ switch(cmd)
+ {
+ case DV1394_IOC_SUBMIT_FRAMES: {
+ unsigned int n_submit;
+
+ if ( !video_card_initialized(video) ) {
+ ret = do_dv1394_init_default(video);
+ if (ret)
+ goto out;
+ }
+
+ n_submit = (unsigned int) arg;
+
+ if (n_submit > video->n_frames) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ while (n_submit > 0) {
+
+ add_wait_queue(&video->waitq, &wait);
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ spin_lock_irqsave(&video->spinlock, flags);
+
+ /* wait until video->first_clear_frame is really CLEAR */
+ while (video->frames[video->first_clear_frame]->state != FRAME_CLEAR) {
+
+ spin_unlock_irqrestore(&video->spinlock, flags);
+
+ if (signal_pending(current)) {
+ remove_wait_queue(&video->waitq, &wait);
+ set_current_state(TASK_RUNNING);
+ ret = -EINTR;
+ goto out;
+ }
+
+ schedule();
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ spin_lock_irqsave(&video->spinlock, flags);
+ }
+ spin_unlock_irqrestore(&video->spinlock, flags);
+
+ remove_wait_queue(&video->waitq, &wait);
+ set_current_state(TASK_RUNNING);
+
+ frame_prepare(video, video->first_clear_frame);
+
+ n_submit--;
+ }
+
+ ret = 0;
+ break;
+ }
+
+ case DV1394_IOC_WAIT_FRAMES: {
+ unsigned int n_wait;
+
+ if ( !video_card_initialized(video) ) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ n_wait = (unsigned int) arg;
+
+ /* since we re-run the last frame on underflow, we will
+ never actually have n_frames clear frames; at most only
+ n_frames - 1 */
+
+ if (n_wait > (video->n_frames-1) ) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ add_wait_queue(&video->waitq, &wait);
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ spin_lock_irqsave(&video->spinlock, flags);
+
+ while (video->n_clear_frames < n_wait) {
+
+ spin_unlock_irqrestore(&video->spinlock, flags);
+
+ if (signal_pending(current)) {
+ remove_wait_queue(&video->waitq, &wait);
+ set_current_state(TASK_RUNNING);
+ ret = -EINTR;
+ goto out;
+ }
+
+ schedule();
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ spin_lock_irqsave(&video->spinlock, flags);
+ }
+
+ spin_unlock_irqrestore(&video->spinlock, flags);
+
+ remove_wait_queue(&video->waitq, &wait);
+ set_current_state(TASK_RUNNING);
+ ret = 0;
+ break;
+ }
+
+ case DV1394_IOC_RECEIVE_FRAMES: {
+ unsigned int n_recv;
+
+ if ( !video_card_initialized(video) ) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ n_recv = (unsigned int) arg;
+
+ /* at least one frame must be active */
+ if (n_recv > (video->n_frames-1) ) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ spin_lock_irqsave(&video->spinlock, flags);
+
+ /* release the clear frames */
+ video->n_clear_frames -= n_recv;
+
+ /* advance the clear frame cursor */
+ video->first_clear_frame = (video->first_clear_frame + n_recv) % video->n_frames;
+
+ /* reset dropped_frames */
+ video->dropped_frames = 0;
+
+ spin_unlock_irqrestore(&video->spinlock, flags);
+
+ ret = 0;
+ break;
+ }
+
+ case DV1394_IOC_START_RECEIVE: {
+ if ( !video_card_initialized(video) ) {
+ ret = do_dv1394_init_default(video);
+ if (ret)
+ goto out;
+ }
+
+ video->continuity_counter = -1;
+
+ receive_packets(video);
+
+ start_dma_receive(video);
+
+ ret = 0;
+ break;
+ }
+
+ case DV1394_IOC_INIT: {
+ struct dv1394_init init;
+ if (!argp) {
+ ret = do_dv1394_init_default(video);
+ } else {
+ if (copy_from_user(&init, argp, sizeof(init))) {
+ ret = -EFAULT;
+ goto out;
+ }
+ ret = do_dv1394_init(video, &init);
+ }
+ break;
+ }
+
+ case DV1394_IOC_SHUTDOWN:
+ do_dv1394_shutdown(video, 0);
+ ret = 0;
+ break;
+
+
+ case DV1394_IOC_GET_STATUS: {
+ struct dv1394_status status;
+
+ if ( !video_card_initialized(video) ) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ status.init.api_version = DV1394_API_VERSION;
+ status.init.channel = video->channel;
+ status.init.n_frames = video->n_frames;
+ status.init.format = video->pal_or_ntsc;
+ status.init.cip_n = video->cip_n;
+ status.init.cip_d = video->cip_d;
+ status.init.syt_offset = video->syt_offset;
+
+ status.first_clear_frame = video->first_clear_frame;
+
+ /* the rest of the fields need to be locked against the interrupt */
+ spin_lock_irqsave(&video->spinlock, flags);
+
+ status.active_frame = video->active_frame;
+ status.n_clear_frames = video->n_clear_frames;
+
+ status.dropped_frames = video->dropped_frames;
+
+ /* reset dropped_frames */
+ video->dropped_frames = 0;
+
+ spin_unlock_irqrestore(&video->spinlock, flags);
+
+ if (copy_to_user(argp, &status, sizeof(status))) {
+ ret = -EFAULT;
+ goto out;
+ }
+
+ ret = 0;
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ out:
+ mutex_unlock(&video->mtx);
+ return ret;
+}
+
+/*** DEVICE FILE INTERFACE CONTINUED ***************************************/
+
+static int dv1394_open(struct inode *inode, struct file *file)
+{
+ struct video_card *video = NULL;
+
+ if (file->private_data) {
+ video = (struct video_card*) file->private_data;
+
+ } else {
+ /* look up the card by ID */
+ unsigned long flags;
+
+ spin_lock_irqsave(&dv1394_cards_lock, flags);
+ if (!list_empty(&dv1394_cards)) {
+ struct video_card *p;
+ list_for_each_entry(p, &dv1394_cards, list) {
+ if ((p->id) == ieee1394_file_to_instance(file)) {
+ video = p;
+ break;
+ }
+ }
+ }
+ spin_unlock_irqrestore(&dv1394_cards_lock, flags);
+
+ if (!video) {
+ debug_printk("dv1394: OHCI card %d not found", ieee1394_file_to_instance(file));
+ return -ENODEV;
+ }
+
+ file->private_data = (void*) video;
+ }
+
+#ifndef DV1394_ALLOW_MORE_THAN_ONE_OPEN
+
+ if ( test_and_set_bit(0, &video->open) ) {
+ /* video is already open by someone else */
+ return -EBUSY;
+ }
+
+#endif
+
+ return 0;
+}
+
+
+static int dv1394_release(struct inode *inode, struct file *file)
+{
+ struct video_card *video = file_to_video_card(file);
+
+ /* OK to free the DMA buffer, no more mappings can exist */
+ do_dv1394_shutdown(video, 1);
+
+ /* give someone else a turn */
+ clear_bit(0, &video->open);
+
+ return 0;
+}
+
+
+/*** DEVICE DRIVER HANDLERS ************************************************/
+
+static void it_tasklet_func(unsigned long data)
+{
+ int wake = 0;
+ struct video_card *video = (struct video_card*) data;
+
+ spin_lock(&video->spinlock);
+
+ if (!video->dma_running)
+ goto out;
+
+ irq_printk("ContextControl = %08x, CommandPtr = %08x\n",
+ reg_read(video->ohci, video->ohci_IsoXmitContextControlSet),
+ reg_read(video->ohci, video->ohci_IsoXmitCommandPtr)
+ );
+
+
+ if ( (video->ohci_it_ctx != -1) &&
+ (reg_read(video->ohci, video->ohci_IsoXmitContextControlSet) & (1 << 10)) ) {
+
+ struct frame *f;
+ unsigned int frame, i;
+
+
+ if (video->active_frame == -1)
+ frame = 0;
+ else
+ frame = video->active_frame;
+
+ /* check all the DMA-able frames */
+ for (i = 0; i < video->n_frames; i++, frame = (frame+1) % video->n_frames) {
+
+ irq_printk("IRQ checking frame %d...", frame);
+ f = video->frames[frame];
+ if (f->state != FRAME_READY) {
+ irq_printk("clear, skipping\n");
+ /* we don't own this frame */
+ continue;
+ }
+
+ irq_printk("DMA\n");
+
+ /* check the frame begin semaphore to see if we can free the previous frame */
+ if ( *(f->frame_begin_timestamp) ) {
+ int prev_frame;
+ struct frame *prev_f;
+
+
+
+ /* don't reset, need this later *(f->frame_begin_timestamp) = 0; */
+ irq_printk(" BEGIN\n");
+
+ prev_frame = frame - 1;
+ if (prev_frame == -1)
+ prev_frame += video->n_frames;
+ prev_f = video->frames[prev_frame];
+
+ /* make sure we can actually garbage collect
+ this frame */
+ if ( (prev_f->state == FRAME_READY) &&
+ prev_f->done && (!f->done) )
+ {
+ frame_reset(prev_f);
+ video->n_clear_frames++;
+ wake = 1;
+ video->active_frame = frame;
+
+ irq_printk(" BEGIN - freeing previous frame %d, new active frame is %d\n", prev_frame, frame);
+ } else {
+ irq_printk(" BEGIN - can't free yet\n");
+ }
+
+ f->done = 1;
+ }
+
+
+ /* see if we need to set the timestamp for the next frame */
+ if ( *(f->mid_frame_timestamp) ) {
+ struct frame *next_frame;
+ u32 begin_ts, ts_cyc, ts_off;
+
+ *(f->mid_frame_timestamp) = 0;
+
+ begin_ts = le32_to_cpu(*(f->frame_begin_timestamp));
+
+ irq_printk(" MIDDLE - first packet was sent at cycle %4u (%2u), assigned timestamp was (%2u) %4u\n",
+ begin_ts & 0x1FFF, begin_ts & 0xF,
+ f->assigned_timestamp >> 12, f->assigned_timestamp & 0xFFF);
+
+ /* prepare next frame and assign timestamp */
+ next_frame = video->frames[ (frame+1) % video->n_frames ];
+
+ if (next_frame->state == FRAME_READY) {
+ irq_printk(" MIDDLE - next frame is ready, good\n");
+ } else {
+ debug_printk("dv1394: Underflow! At least one frame has been dropped.\n");
+ next_frame = f;
+ }
+
+ /* set the timestamp to the timestamp of the last frame sent,
+ plus the length of the last frame sent, plus the syt latency */
+ ts_cyc = begin_ts & 0xF;
+ /* advance one frame, plus syt latency (typically 2-3) */
+ ts_cyc += f->n_packets + video->syt_offset ;
+
+ ts_off = 0;
+
+ ts_cyc += ts_off/3072;
+ ts_off %= 3072;
+
+ next_frame->assigned_timestamp = ((ts_cyc&0xF) << 12) + ts_off;
+ if (next_frame->cip_syt1) {
+ next_frame->cip_syt1->b[6] = next_frame->assigned_timestamp >> 8;
+ next_frame->cip_syt1->b[7] = next_frame->assigned_timestamp & 0xFF;
+ }
+ if (next_frame->cip_syt2) {
+ next_frame->cip_syt2->b[6] = next_frame->assigned_timestamp >> 8;
+ next_frame->cip_syt2->b[7] = next_frame->assigned_timestamp & 0xFF;
+ }
+
+ }
+
+ /* see if the frame looped */
+ if ( *(f->frame_end_timestamp) ) {
+
+ *(f->frame_end_timestamp) = 0;
+
+ debug_printk(" END - the frame looped at least once\n");
+
+ video->dropped_frames++;
+ }
+
+ } /* for (each frame) */
+ }
+
+ if (wake) {
+ kill_fasync(&video->fasync, SIGIO, POLL_OUT);
+
+ /* wake readers/writers/ioctl'ers */
+ wake_up_interruptible(&video->waitq);
+ }
+
+out:
+ spin_unlock(&video->spinlock);
+}
+
+static void ir_tasklet_func(unsigned long data)
+{
+ int wake = 0;
+ struct video_card *video = (struct video_card*) data;
+
+ spin_lock(&video->spinlock);
+
+ if (!video->dma_running)
+ goto out;
+
+ if ( (video->ohci_ir_ctx != -1) &&
+ (reg_read(video->ohci, video->ohci_IsoRcvContextControlSet) & (1 << 10)) ) {
+
+ int sof=0; /* start-of-frame flag */
+ struct frame *f;
+ u16 packet_length, packet_time;
+ int i, dbc=0;
+ struct DMA_descriptor_block *block = NULL;
+ u16 xferstatus;
+
+ int next_i, prev_i;
+ struct DMA_descriptor_block *next = NULL;
+ dma_addr_t next_dma = 0;
+ struct DMA_descriptor_block *prev = NULL;
+
+ /* loop over all descriptors in all frames */
+ for (i = 0; i < video->n_frames*MAX_PACKETS; i++) {
+ struct packet *p = dma_region_i(&video->packet_buf, struct packet, video->current_packet);
+
+ /* make sure we are seeing the latest changes to p */
+ dma_region_sync_for_cpu(&video->packet_buf,
+ (unsigned long) p - (unsigned long) video->packet_buf.kvirt,
+ sizeof(struct packet));
+
+ packet_length = le16_to_cpu(p->data_length);
+ packet_time = le16_to_cpu(p->timestamp);
+
+ irq_printk("received packet %02d, timestamp=%04x, length=%04x, sof=%02x%02x\n", video->current_packet,
+ packet_time, packet_length,
+ p->data[0], p->data[1]);
+
+ /* get the descriptor based on packet_buffer cursor */
+ f = video->frames[video->current_packet / MAX_PACKETS];
+ block = &(f->descriptor_pool[video->current_packet % MAX_PACKETS]);
+ xferstatus = le32_to_cpu(block->u.in.il.q[3]) >> 16;
+ xferstatus &= 0x1F;
+ irq_printk("ir_tasklet_func: xferStatus/resCount [%d] = 0x%08x\n", i, le32_to_cpu(block->u.in.il.q[3]) );
+
+ /* get the current frame */
+ f = video->frames[video->active_frame];
+
+ /* exclude empty packet */
+ if (packet_length > 8 && xferstatus == 0x11) {
+ /* check for start of frame */
+ /* DRD> Changed to check section type ([0]>>5==0)
+ and dif sequence ([1]>>4==0) */
+ sof = ( (p->data[0] >> 5) == 0 && (p->data[1] >> 4) == 0);
+
+ dbc = (int) (p->cip_h1 >> 24);
+ if ( video->continuity_counter != -1 && dbc > ((video->continuity_counter + 1) % 256) )
+ {
+ printk(KERN_WARNING "dv1394: discontinuity detected, dropping all frames\n" );
+ video->dropped_frames += video->n_clear_frames + 1;
+ video->first_frame = 0;
+ video->n_clear_frames = 0;
+ video->first_clear_frame = -1;
+ }
+ video->continuity_counter = dbc;
+
+ if (!video->first_frame) {
+ if (sof) {
+ video->first_frame = 1;
+ }
+
+ } else if (sof) {
+ /* close current frame */
+ frame_reset(f); /* f->state = STATE_CLEAR */
+ video->n_clear_frames++;
+ if (video->n_clear_frames > video->n_frames) {
+ video->dropped_frames++;
+ printk(KERN_WARNING "dv1394: dropped a frame during reception\n" );
+ video->n_clear_frames = video->n_frames-1;
+ video->first_clear_frame = (video->first_clear_frame + 1) % video->n_frames;
+ }
+ if (video->first_clear_frame == -1)
+ video->first_clear_frame = video->active_frame;
+
+ /* get the next frame */
+ video->active_frame = (video->active_frame + 1) % video->n_frames;
+ f = video->frames[video->active_frame];
+ irq_printk(" frame received, active_frame = %d, n_clear_frames = %d, first_clear_frame = %d\n",
+ video->active_frame, video->n_clear_frames, video->first_clear_frame);
+ }
+ if (video->first_frame) {
+ if (sof) {
+ /* open next frame */
+ f->state = FRAME_READY;
+ }
+
+ /* copy to buffer */
+ if (f->n_packets > (video->frame_size / 480)) {
+ printk(KERN_ERR "frame buffer overflow during receive\n");
+ }
+
+ frame_put_packet(f, p);
+
+ } /* first_frame */
+ }
+
+ /* stop, end of ready packets */
+ else if (xferstatus == 0) {
+ break;
+ }
+
+ /* reset xferStatus & resCount */
+ block->u.in.il.q[3] = cpu_to_le32(512);
+
+ /* terminate dma chain at this (next) packet */
+ next_i = video->current_packet;
+ f = video->frames[next_i / MAX_PACKETS];
+ next = &(f->descriptor_pool[next_i % MAX_PACKETS]);
+ next_dma = ((unsigned long) block - (unsigned long) f->descriptor_pool) + f->descriptor_pool_dma;
+ next->u.in.il.q[0] |= 3 << 20; /* enable interrupt */
+ next->u.in.il.q[2] = 0; /* disable branch */
+
+ /* link previous to next */
+ prev_i = (next_i == 0) ? (MAX_PACKETS * video->n_frames - 1) : (next_i - 1);
+ f = video->frames[prev_i / MAX_PACKETS];
+ prev = &(f->descriptor_pool[prev_i % MAX_PACKETS]);
+ if (prev_i % (MAX_PACKETS/2)) {
+ prev->u.in.il.q[0] &= ~(3 << 20); /* no interrupt */
+ } else {
+ prev->u.in.il.q[0] |= 3 << 20; /* enable interrupt */
+ }
+ prev->u.in.il.q[2] = cpu_to_le32(next_dma | 1); /* set Z=1 */
+ wmb();
+
+ /* wake up DMA in case it fell asleep */
+ reg_write(video->ohci, video->ohci_IsoRcvContextControlSet, (1 << 12));
+
+ /* advance packet_buffer cursor */
+ video->current_packet = (video->current_packet + 1) % (MAX_PACKETS * video->n_frames);
+
+ } /* for all packets */
+
+ wake = 1; /* why the hell not? */
+
+ } /* receive interrupt */
+
+ if (wake) {
+ kill_fasync(&video->fasync, SIGIO, POLL_IN);
+
+ /* wake readers/writers/ioctl'ers */
+ wake_up_interruptible(&video->waitq);
+ }
+
+out:
+ spin_unlock(&video->spinlock);
+}
+
+static struct cdev dv1394_cdev;
+static const struct file_operations dv1394_fops=
+{
+ .owner = THIS_MODULE,
+ .poll = dv1394_poll,
+ .unlocked_ioctl = dv1394_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = dv1394_compat_ioctl,
+#endif
+ .mmap = dv1394_mmap,
+ .open = dv1394_open,
+ .write = dv1394_write,
+ .read = dv1394_read,
+ .release = dv1394_release,
+ .fasync = dv1394_fasync,
+};
+
+
+/*** HOTPLUG STUFF **********************************************************/
+/*
+ * Export information about protocols/devices supported by this driver.
+ */
+#ifdef MODULE
+static struct ieee1394_device_id dv1394_id_table[] = {
+ {
+ .match_flags = IEEE1394_MATCH_SPECIFIER_ID | IEEE1394_MATCH_VERSION,
+ .specifier_id = AVC_UNIT_SPEC_ID_ENTRY & 0xffffff,
+ .version = AVC_SW_VERSION_ENTRY & 0xffffff
+ },
+ { }
+};
+
+MODULE_DEVICE_TABLE(ieee1394, dv1394_id_table);
+#endif /* MODULE */
+
+static struct hpsb_protocol_driver dv1394_driver = {
+ .name = "dv1394",
+};
+
+
+/*** IEEE1394 HPSB CALLBACKS ***********************************************/
+
+static int dv1394_init(struct ti_ohci *ohci, enum pal_or_ntsc format, enum modes mode)
+{
+ struct video_card *video;
+ unsigned long flags;
+ int i;
+
+ video = kzalloc(sizeof(*video), GFP_KERNEL);
+ if (!video) {
+ printk(KERN_ERR "dv1394: cannot allocate video_card\n");
+ return -1;
+ }
+
+ video->ohci = ohci;
+ /* lower 2 bits of id indicate which of four "plugs"
+ per host */
+ video->id = ohci->host->id << 2;
+ if (format == DV1394_NTSC)
+ video->id |= mode;
+ else
+ video->id |= 2 + mode;
+
+ video->ohci_it_ctx = -1;
+ video->ohci_ir_ctx = -1;
+
+ video->ohci_IsoXmitContextControlSet = 0;
+ video->ohci_IsoXmitContextControlClear = 0;
+ video->ohci_IsoXmitCommandPtr = 0;
+
+ video->ohci_IsoRcvContextControlSet = 0;
+ video->ohci_IsoRcvContextControlClear = 0;
+ video->ohci_IsoRcvCommandPtr = 0;
+ video->ohci_IsoRcvContextMatch = 0;
+
+ video->n_frames = 0; /* flag that video is not initialized */
+ video->channel = 63; /* default to broadcast channel */
+ video->active_frame = -1;
+
+ /* initialize the following */
+ video->pal_or_ntsc = format;
+ video->cip_n = 0; /* 0 = use builtin default */
+ video->cip_d = 0;
+ video->syt_offset = 0;
+ video->mode = mode;
+
+ for (i = 0; i < DV1394_MAX_FRAMES; i++)
+ video->frames[i] = NULL;
+
+ dma_region_init(&video->dv_buf);
+ video->dv_buf_size = 0;
+ dma_region_init(&video->packet_buf);
+ video->packet_buf_size = 0;
+
+ clear_bit(0, &video->open);
+ spin_lock_init(&video->spinlock);
+ video->dma_running = 0;
+ mutex_init(&video->mtx);
+ init_waitqueue_head(&video->waitq);
+ video->fasync = NULL;
+
+ spin_lock_irqsave(&dv1394_cards_lock, flags);
+ INIT_LIST_HEAD(&video->list);
+ list_add_tail(&video->list, &dv1394_cards);
+ spin_unlock_irqrestore(&dv1394_cards_lock, flags);
+
+ debug_printk("dv1394: dv1394_init() OK on ID %d\n", video->id);
+ return 0;
+}
+
+static void dv1394_remove_host(struct hpsb_host *host)
+{
+ struct video_card *video, *tmp_video;
+ unsigned long flags;
+ int found_ohci_card = 0;
+
+ do {
+ video = NULL;
+ spin_lock_irqsave(&dv1394_cards_lock, flags);
+ list_for_each_entry(tmp_video, &dv1394_cards, list) {
+ if ((tmp_video->id >> 2) == host->id) {
+ list_del(&tmp_video->list);
+ video = tmp_video;
+ found_ohci_card = 1;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&dv1394_cards_lock, flags);
+
+ if (video) {
+ do_dv1394_shutdown(video, 1);
+ kfree(video);
+ }
+ } while (video);
+
+ if (found_ohci_card)
+ device_destroy(hpsb_protocol_class, MKDEV(IEEE1394_MAJOR,
+ IEEE1394_MINOR_BLOCK_DV1394 * 16 + (host->id << 2)));
+}
+
+static void dv1394_add_host(struct hpsb_host *host)
+{
+ struct ti_ohci *ohci;
+ int id = host->id;
+
+ /* We only work with the OHCI-1394 driver */
+ if (strcmp(host->driver->name, OHCI1394_DRIVER_NAME))
+ return;
+
+ ohci = (struct ti_ohci *)host->hostdata;
+
+ device_create(hpsb_protocol_class, NULL,
+ MKDEV(IEEE1394_MAJOR,
+ IEEE1394_MINOR_BLOCK_DV1394 * 16 + (id<<2)),
+ NULL, "dv1394-%d", id);
+
+ dv1394_init(ohci, DV1394_NTSC, MODE_RECEIVE);
+ dv1394_init(ohci, DV1394_NTSC, MODE_TRANSMIT);
+ dv1394_init(ohci, DV1394_PAL, MODE_RECEIVE);
+ dv1394_init(ohci, DV1394_PAL, MODE_TRANSMIT);
+}
+
+
+/* Bus reset handler. In the event of a bus reset, we may need to
+ re-start the DMA contexts - otherwise the user program would
+ end up waiting forever.
+*/
+
+static void dv1394_host_reset(struct hpsb_host *host)
+{
+ struct ti_ohci *ohci;
+ struct video_card *video = NULL, *tmp_vid;
+ unsigned long flags;
+
+ /* We only work with the OHCI-1394 driver */
+ if (strcmp(host->driver->name, OHCI1394_DRIVER_NAME))
+ return;
+
+ ohci = (struct ti_ohci *)host->hostdata;
+
+
+ /* find the corresponding video_cards */
+ spin_lock_irqsave(&dv1394_cards_lock, flags);
+ list_for_each_entry(tmp_vid, &dv1394_cards, list) {
+ if ((tmp_vid->id >> 2) == host->id) {
+ video = tmp_vid;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&dv1394_cards_lock, flags);
+
+ if (!video)
+ return;
+
+
+ spin_lock_irqsave(&video->spinlock, flags);
+
+ if (!video->dma_running)
+ goto out;
+
+ /* check IT context */
+ if (video->ohci_it_ctx != -1) {
+ u32 ctx;
+
+ ctx = reg_read(video->ohci, video->ohci_IsoXmitContextControlSet);
+
+ /* if (RUN but not ACTIVE) */
+ if ( (ctx & (1<<15)) &&
+ !(ctx & (1<<10)) ) {
+
+ debug_printk("dv1394: IT context stopped due to bus reset; waking it up\n");
+
+ /* to be safe, assume a frame has been dropped. User-space programs
+ should handle this condition like an underflow. */
+ video->dropped_frames++;
+
+ /* for some reason you must clear, then re-set the RUN bit to restart DMA */
+
+ /* clear RUN */
+ reg_write(video->ohci, video->ohci_IsoXmitContextControlClear, (1 << 15));
+ flush_pci_write(video->ohci);
+
+ /* set RUN */
+ reg_write(video->ohci, video->ohci_IsoXmitContextControlSet, (1 << 15));
+ flush_pci_write(video->ohci);
+
+ /* set the WAKE bit (just in case; this isn't strictly necessary) */
+ reg_write(video->ohci, video->ohci_IsoXmitContextControlSet, (1 << 12));
+ flush_pci_write(video->ohci);
+
+ irq_printk("dv1394: AFTER IT restart ctx 0x%08x ptr 0x%08x\n",
+ reg_read(video->ohci, video->ohci_IsoXmitContextControlSet),
+ reg_read(video->ohci, video->ohci_IsoXmitCommandPtr));
+ }
+ }
+
+ /* check IR context */
+ if (video->ohci_ir_ctx != -1) {
+ u32 ctx;
+
+ ctx = reg_read(video->ohci, video->ohci_IsoRcvContextControlSet);
+
+ /* if (RUN but not ACTIVE) */
+ if ( (ctx & (1<<15)) &&
+ !(ctx & (1<<10)) ) {
+
+ debug_printk("dv1394: IR context stopped due to bus reset; waking it up\n");
+
+ /* to be safe, assume a frame has been dropped. User-space programs
+ should handle this condition like an overflow. */
+ video->dropped_frames++;
+
+ /* for some reason you must clear, then re-set the RUN bit to restart DMA */
+ /* XXX this doesn't work for me, I can't get IR DMA to restart :[ */
+
+ /* clear RUN */
+ reg_write(video->ohci, video->ohci_IsoRcvContextControlClear, (1 << 15));
+ flush_pci_write(video->ohci);
+
+ /* set RUN */
+ reg_write(video->ohci, video->ohci_IsoRcvContextControlSet, (1 << 15));
+ flush_pci_write(video->ohci);
+
+ /* set the WAKE bit (just in case; this isn't strictly necessary) */
+ reg_write(video->ohci, video->ohci_IsoRcvContextControlSet, (1 << 12));
+ flush_pci_write(video->ohci);
+
+ irq_printk("dv1394: AFTER IR restart ctx 0x%08x ptr 0x%08x\n",
+ reg_read(video->ohci, video->ohci_IsoRcvContextControlSet),
+ reg_read(video->ohci, video->ohci_IsoRcvCommandPtr));
+ }
+ }
+
+out:
+ spin_unlock_irqrestore(&video->spinlock, flags);
+
+ /* wake readers/writers/ioctl'ers */
+ wake_up_interruptible(&video->waitq);
+}
+
+static struct hpsb_highlevel dv1394_highlevel = {
+ .name = "dv1394",
+ .add_host = dv1394_add_host,
+ .remove_host = dv1394_remove_host,
+ .host_reset = dv1394_host_reset,
+};
+
+#ifdef CONFIG_COMPAT
+
+#define DV1394_IOC32_INIT _IOW('#', 0x06, struct dv1394_init32)
+#define DV1394_IOC32_GET_STATUS _IOR('#', 0x0c, struct dv1394_status32)
+
+struct dv1394_init32 {
+ u32 api_version;
+ u32 channel;
+ u32 n_frames;
+ u32 format;
+ u32 cip_n;
+ u32 cip_d;
+ u32 syt_offset;
+};
+
+struct dv1394_status32 {
+ struct dv1394_init32 init;
+ s32 active_frame;
+ u32 first_clear_frame;
+ u32 n_clear_frames;
+ u32 dropped_frames;
+};
+
+/* RED-PEN: this should use compat_alloc_userspace instead */
+
+static int handle_dv1394_init(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct dv1394_init32 dv32;
+ struct dv1394_init dv;
+ mm_segment_t old_fs;
+ int ret;
+
+ if (file->f_op->unlocked_ioctl != dv1394_ioctl)
+ return -EFAULT;
+
+ if (copy_from_user(&dv32, (void __user *)arg, sizeof(dv32)))
+ return -EFAULT;
+
+ dv.api_version = dv32.api_version;
+ dv.channel = dv32.channel;
+ dv.n_frames = dv32.n_frames;
+ dv.format = dv32.format;
+ dv.cip_n = (unsigned long)dv32.cip_n;
+ dv.cip_d = (unsigned long)dv32.cip_d;
+ dv.syt_offset = dv32.syt_offset;
+
+ old_fs = get_fs();
+ set_fs(KERNEL_DS);
+ ret = dv1394_ioctl(file, DV1394_IOC_INIT, (unsigned long)&dv);
+ set_fs(old_fs);
+
+ return ret;
+}
+
+static int handle_dv1394_get_status(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct dv1394_status32 dv32;
+ struct dv1394_status dv;
+ mm_segment_t old_fs;
+ int ret;
+
+ if (file->f_op->unlocked_ioctl != dv1394_ioctl)
+ return -EFAULT;
+
+ old_fs = get_fs();
+ set_fs(KERNEL_DS);
+ ret = dv1394_ioctl(file, DV1394_IOC_GET_STATUS, (unsigned long)&dv);
+ set_fs(old_fs);
+
+ if (!ret) {
+ dv32.init.api_version = dv.init.api_version;
+ dv32.init.channel = dv.init.channel;
+ dv32.init.n_frames = dv.init.n_frames;
+ dv32.init.format = dv.init.format;
+ dv32.init.cip_n = (u32)dv.init.cip_n;
+ dv32.init.cip_d = (u32)dv.init.cip_d;
+ dv32.init.syt_offset = dv.init.syt_offset;
+ dv32.active_frame = dv.active_frame;
+ dv32.first_clear_frame = dv.first_clear_frame;
+ dv32.n_clear_frames = dv.n_clear_frames;
+ dv32.dropped_frames = dv.dropped_frames;
+
+ if (copy_to_user((struct dv1394_status32 __user *)arg, &dv32, sizeof(dv32)))
+ ret = -EFAULT;
+ }
+
+ return ret;
+}
+
+
+
+static long dv1394_compat_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ switch (cmd) {
+ case DV1394_IOC_SHUTDOWN:
+ case DV1394_IOC_SUBMIT_FRAMES:
+ case DV1394_IOC_WAIT_FRAMES:
+ case DV1394_IOC_RECEIVE_FRAMES:
+ case DV1394_IOC_START_RECEIVE:
+ return dv1394_ioctl(file, cmd, arg);
+
+ case DV1394_IOC32_INIT:
+ return handle_dv1394_init(file, cmd, arg);
+ case DV1394_IOC32_GET_STATUS:
+ return handle_dv1394_get_status(file, cmd, arg);
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+#endif /* CONFIG_COMPAT */
+
+
+/*** KERNEL MODULE HANDLERS ************************************************/
+
+MODULE_AUTHOR("Dan Maas <dmaas@dcine.com>, Dan Dennedy <dan@dennedy.org>");
+MODULE_DESCRIPTION("driver for DV input/output on OHCI board");
+MODULE_SUPPORTED_DEVICE("dv1394");
+MODULE_LICENSE("GPL");
+
+static void __exit dv1394_exit_module(void)
+{
+ hpsb_unregister_protocol(&dv1394_driver);
+ hpsb_unregister_highlevel(&dv1394_highlevel);
+ cdev_del(&dv1394_cdev);
+}
+
+static int __init dv1394_init_module(void)
+{
+ int ret;
+
+ printk(KERN_WARNING
+ "NOTE: The dv1394 driver is unsupported and may be removed in a "
+ "future Linux release. Use raw1394 instead.\n");
+
+ cdev_init(&dv1394_cdev, &dv1394_fops);
+ dv1394_cdev.owner = THIS_MODULE;
+ ret = cdev_add(&dv1394_cdev, IEEE1394_DV1394_DEV, 16);
+ if (ret) {
+ printk(KERN_ERR "dv1394: unable to register character device\n");
+ return ret;
+ }
+
+ hpsb_register_highlevel(&dv1394_highlevel);
+
+ ret = hpsb_register_protocol(&dv1394_driver);
+ if (ret) {
+ printk(KERN_ERR "dv1394: failed to register protocol\n");
+ hpsb_unregister_highlevel(&dv1394_highlevel);
+ cdev_del(&dv1394_cdev);
+ return ret;
+ }
+
+ return 0;
+}
+
+module_init(dv1394_init_module);
+module_exit(dv1394_exit_module);
diff --git a/drivers/ieee1394/dv1394.h b/drivers/ieee1394/dv1394.h
new file mode 100644
index 0000000..5807f52
--- /dev/null
+++ b/drivers/ieee1394/dv1394.h
@@ -0,0 +1,305 @@
+/*
+ * dv1394.h - DV input/output over IEEE 1394 on OHCI chips
+ * Copyright (C)2001 Daniel Maas <dmaas@dcine.com>
+ * receive by Dan Dennedy <dan@dennedy.org>
+ *
+ * based on:
+ * video1394.h - driver for OHCI 1394 boards
+ * Copyright (C)1999,2000 Sebastien Rougeaux <sebastien.rougeaux@anu.edu.au>
+ * Peter Schlaile <udbz@rz.uni-karlsruhe.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _DV_1394_H
+#define _DV_1394_H
+
+/* This is the public user-space interface. Try not to break it. */
+
+#define DV1394_API_VERSION 0x20011127
+
+/* ********************
+ ** **
+ ** DV1394 API **
+ ** **
+ ********************
+
+ There are two methods of operating the DV1394 DV output device.
+
+ 1)
+
+ The simplest is an interface based on write(): simply write
+ full DV frames of data to the device, and they will be transmitted
+ as quickly as possible. The FD may be set for non-blocking I/O,
+ in which case you can use select() or poll() to wait for output
+ buffer space.
+
+ To set the DV output parameters (e.g. whether you want NTSC or PAL
+ video), use the DV1394_INIT ioctl, passing in the parameters you
+ want in a struct dv1394_init.
+
+ Example 1:
+ To play a raw .DV file: cat foo.DV > /dev/dv1394
+ (cat will use write() internally)
+
+ Example 2:
+ static struct dv1394_init init = {
+ 0x63, (broadcast channel)
+ 4, (four-frame ringbuffer)
+ DV1394_NTSC, (send NTSC video)
+ 0, 0 (default empty packet rate)
+ }
+
+ ioctl(fd, DV1394_INIT, &init);
+
+ while (1) {
+ read( <a raw DV file>, buf, DV1394_NTSC_FRAME_SIZE );
+ write( <the dv1394 FD>, buf, DV1394_NTSC_FRAME_SIZE );
+ }
+
+ 2)
+
+ For more control over buffering, and to avoid unnecessary copies
+ of the DV data, you can use the more sophisticated the mmap() interface.
+ First, call the DV1394_INIT ioctl to specify your parameters,
+ including the number of frames in the ringbuffer. Then, calling mmap()
+ on the dv1394 device will give you direct access to the ringbuffer
+ from which the DV card reads your frame data.
+
+ The ringbuffer is simply one large, contiguous region of memory
+ containing two or more frames of packed DV data. Each frame of DV data
+ is 120000 bytes (NTSC) or 144000 bytes (PAL).
+
+ Fill one or more frames in the ringbuffer, then use the DV1394_SUBMIT_FRAMES
+ ioctl to begin I/O. You can use either the DV1394_WAIT_FRAMES ioctl
+ or select()/poll() to wait until the frames are transmitted. Next, you'll
+ need to call the DV1394_GET_STATUS ioctl to determine which ringbuffer
+ frames are clear (ready to be filled with new DV data). Finally, use
+ DV1394_SUBMIT_FRAMES again to send the new data to the DV output.
+
+
+ Example: here is what a four-frame ringbuffer might look like
+ during DV transmission:
+
+
+ frame 0 frame 1 frame 2 frame 3
+
+ *--------------------------------------*
+ | CLEAR | DV data | DV data | CLEAR |
+ *--------------------------------------*
+ <ACTIVE>
+
+ transmission goes in this direction --->>>
+
+
+ The DV hardware is currently transmitting the data in frame 1.
+ Once frame 1 is finished, it will automatically transmit frame 2.
+ (if frame 2 finishes before frame 3 is submitted, the device
+ will continue to transmit frame 2, and will increase the dropped_frames
+ counter each time it repeats the transmission).
+
+
+ If you called DV1394_GET_STATUS at this instant, you would
+ receive the following values:
+
+ n_frames = 4
+ active_frame = 1
+ first_clear_frame = 3
+ n_clear_frames = 2
+
+ At this point, you should write new DV data into frame 3 and optionally
+ frame 0. Then call DV1394_SUBMIT_FRAMES to inform the device that
+ it may transmit the new frames.
+
+ ERROR HANDLING
+
+ An error (buffer underflow/overflow or a break in the DV stream due
+ to a 1394 bus reset) can be detected by checking the dropped_frames
+ field of struct dv1394_status (obtained through the
+ DV1394_GET_STATUS ioctl).
+
+ The best way to recover from such an error is to re-initialize
+ dv1394, either by using the DV1394_INIT ioctl call, or closing the
+ file descriptor and opening it again. (note that you must unmap all
+ ringbuffer mappings when closing the file descriptor, or else
+ dv1394 will still be considered 'in use').
+
+ MAIN LOOP
+
+ For maximum efficiency and robustness against bus errors, you are
+ advised to model the main loop of your application after the
+ following pseudo-code example:
+
+ (checks of system call return values omitted for brevity; always
+ check return values in your code!)
+
+ while ( frames left ) {
+
+ struct pollfd *pfd = ...;
+
+ pfd->fd = dv1394_fd;
+ pfd->revents = 0;
+ pfd->events = POLLOUT | POLLIN; (OUT for transmit, IN for receive)
+
+ (add other sources of I/O here)
+
+ poll(pfd, 1, -1); (or select(); add a timeout if you want)
+
+ if (pfd->revents) {
+ struct dv1394_status status;
+
+ ioctl(dv1394_fd, DV1394_GET_STATUS, &status);
+
+ if (status.dropped_frames > 0) {
+ reset_dv1394();
+ } else {
+ for (int i = 0; i < status.n_clear_frames; i++) {
+ copy_DV_frame();
+ }
+ }
+ }
+ }
+
+ where copy_DV_frame() reads or writes on the dv1394 file descriptor
+ (read/write mode) or copies data to/from the mmap ringbuffer and
+ then calls ioctl(DV1394_SUBMIT_FRAMES) to notify dv1394 that new
+ frames are availble (mmap mode).
+
+ reset_dv1394() is called in the event of a buffer
+ underflow/overflow or a halt in the DV stream (e.g. due to a 1394
+ bus reset). To guarantee recovery from the error, this function
+ should close the dv1394 file descriptor (and munmap() all
+ ringbuffer mappings, if you are using them), then re-open the
+ dv1394 device (and re-map the ringbuffer).
+
+*/
+
+
+/* maximum number of frames in the ringbuffer */
+#define DV1394_MAX_FRAMES 32
+
+/* number of *full* isochronous packets per DV frame */
+#define DV1394_NTSC_PACKETS_PER_FRAME 250
+#define DV1394_PAL_PACKETS_PER_FRAME 300
+
+/* size of one frame's worth of DV data, in bytes */
+#define DV1394_NTSC_FRAME_SIZE (480 * DV1394_NTSC_PACKETS_PER_FRAME)
+#define DV1394_PAL_FRAME_SIZE (480 * DV1394_PAL_PACKETS_PER_FRAME)
+
+
+/* ioctl() commands */
+#include "ieee1394-ioctl.h"
+
+
+enum pal_or_ntsc {
+ DV1394_NTSC = 0,
+ DV1394_PAL
+};
+
+
+
+
+/* this is the argument to DV1394_INIT */
+struct dv1394_init {
+ /* DV1394_API_VERSION */
+ unsigned int api_version;
+
+ /* isochronous transmission channel to use */
+ unsigned int channel;
+
+ /* number of frames in the ringbuffer. Must be at least 2
+ and at most DV1394_MAX_FRAMES. */
+ unsigned int n_frames;
+
+ /* send/receive PAL or NTSC video format */
+ enum pal_or_ntsc format;
+
+ /* the following are used only for transmission */
+
+ /* set these to zero unless you want a
+ non-default empty packet rate (see below) */
+ unsigned long cip_n;
+ unsigned long cip_d;
+
+ /* set this to zero unless you want a
+ non-default SYT cycle offset (default = 3 cycles) */
+ unsigned int syt_offset;
+};
+
+/* NOTE: you may only allocate the DV frame ringbuffer once each time
+ you open the dv1394 device. DV1394_INIT will fail if you call it a
+ second time with different 'n_frames' or 'format' arguments (which
+ would imply a different size for the ringbuffer). If you need a
+ different buffer size, simply close and re-open the device, then
+ initialize it with your new settings. */
+
+/* Q: What are cip_n and cip_d? */
+
+/*
+ A: DV video streams do not utilize 100% of the potential bandwidth offered
+ by IEEE 1394 (FireWire). To achieve the correct rate of data transmission,
+ DV devices must periodically insert empty packets into the 1394 data stream.
+ Typically there is one empty packet per 14-16 data-carrying packets.
+
+ Some DV devices will accept a wide range of empty packet rates, while others
+ require a precise rate. If the dv1394 driver produces empty packets at
+ a rate that your device does not accept, you may see ugly patterns on the
+ DV output, or even no output at all.
+
+ The default empty packet insertion rate seems to work for many people; if
+ your DV output is stable, you can simply ignore this discussion. However,
+ we have exposed the empty packet rate as a parameter to support devices that
+ do not work with the default rate.
+
+ The decision to insert an empty packet is made with a numerator/denominator
+ algorithm. Empty packets are produced at an average rate of CIP_N / CIP_D.
+ You can alter the empty packet rate by passing non-zero values for cip_n
+ and cip_d to the INIT ioctl.
+
+ */
+
+
+
+struct dv1394_status {
+ /* this embedded init struct returns the current dv1394
+ parameters in use */
+ struct dv1394_init init;
+
+ /* the ringbuffer frame that is currently being
+ displayed. (-1 if the device is not transmitting anything) */
+ int active_frame;
+
+ /* index of the first buffer (ahead of active_frame) that
+ is ready to be filled with data */
+ unsigned int first_clear_frame;
+
+ /* how many buffers, including first_clear_buffer, are
+ ready to be filled with data */
+ unsigned int n_clear_frames;
+
+ /* how many times the DV stream has underflowed, overflowed,
+ or otherwise encountered an error, since the previous call
+ to DV1394_GET_STATUS */
+ unsigned int dropped_frames;
+
+ /* N.B. The dropped_frames counter is only a lower bound on the actual
+ number of dropped frames, with the special case that if dropped_frames
+ is zero, then it is guaranteed that NO frames have been dropped
+ since the last call to DV1394_GET_STATUS.
+ */
+};
+
+
+#endif /* _DV_1394_H */
diff --git a/drivers/ieee1394/eth1394.c b/drivers/ieee1394/eth1394.c
new file mode 100644
index 0000000..2012869
--- /dev/null
+++ b/drivers/ieee1394/eth1394.c
@@ -0,0 +1,1752 @@
+/*
+ * eth1394.c -- IPv4 driver for Linux IEEE-1394 Subsystem
+ *
+ * Copyright (C) 2001-2003 Ben Collins <bcollins@debian.org>
+ * 2000 Bonin Franck <boninf@free.fr>
+ * 2003 Steve Kinneberg <kinnebergsteve@acmsystems.com>
+ *
+ * Mainly based on work by Emanuel Pirker and Andreas E. Bombe
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * This driver intends to support RFC 2734, which describes a method for
+ * transporting IPv4 datagrams over IEEE-1394 serial busses.
+ *
+ * TODO:
+ * RFC 2734 related:
+ * - Add MCAP. Limited Multicast exists only to 224.0.0.1 and 224.0.0.2.
+ *
+ * Non-RFC 2734 related:
+ * - Handle fragmented skb's coming from the networking layer.
+ * - Move generic GASP reception to core 1394 code
+ * - Convert kmalloc/kfree for link fragments to use kmem_cache_* instead
+ * - Stability improvements
+ * - Performance enhancements
+ * - Consider garbage collecting old partial datagrams after X amount of time
+ */
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/workqueue.h>
+
+#include <linux/netdevice.h>
+#include <linux/inetdevice.h>
+#include <linux/if_arp.h>
+#include <linux/if_ether.h>
+#include <linux/ip.h>
+#include <linux/in.h>
+#include <linux/tcp.h>
+#include <linux/skbuff.h>
+#include <linux/bitops.h>
+#include <linux/ethtool.h>
+#include <asm/uaccess.h>
+#include <asm/delay.h>
+#include <asm/unaligned.h>
+#include <net/arp.h>
+
+#include "config_roms.h"
+#include "csr1212.h"
+#include "eth1394.h"
+#include "highlevel.h"
+#include "ieee1394.h"
+#include "ieee1394_core.h"
+#include "ieee1394_hotplug.h"
+#include "ieee1394_transactions.h"
+#include "ieee1394_types.h"
+#include "iso.h"
+#include "nodemgr.h"
+
+#define ETH1394_PRINT_G(level, fmt, args...) \
+ printk(level "%s: " fmt, driver_name, ## args)
+
+#define ETH1394_PRINT(level, dev_name, fmt, args...) \
+ printk(level "%s: %s: " fmt, driver_name, dev_name, ## args)
+
+struct fragment_info {
+ struct list_head list;
+ int offset;
+ int len;
+};
+
+struct partial_datagram {
+ struct list_head list;
+ u16 dgl;
+ u16 dg_size;
+ u16 ether_type;
+ struct sk_buff *skb;
+ char *pbuf;
+ struct list_head frag_info;
+};
+
+struct pdg_list {
+ struct list_head list; /* partial datagram list per node */
+ unsigned int sz; /* partial datagram list size per node */
+ spinlock_t lock; /* partial datagram lock */
+};
+
+struct eth1394_host_info {
+ struct hpsb_host *host;
+ struct net_device *dev;
+};
+
+struct eth1394_node_ref {
+ struct unit_directory *ud;
+ struct list_head list;
+};
+
+struct eth1394_node_info {
+ u16 maxpayload; /* max payload */
+ u8 sspd; /* max speed */
+ u64 fifo; /* FIFO address */
+ struct pdg_list pdg; /* partial RX datagram lists */
+ int dgl; /* outgoing datagram label */
+};
+
+static const char driver_name[] = "eth1394";
+
+static struct kmem_cache *packet_task_cache;
+
+static struct hpsb_highlevel eth1394_highlevel;
+
+/* Use common.lf to determine header len */
+static const int hdr_type_len[] = {
+ sizeof(struct eth1394_uf_hdr),
+ sizeof(struct eth1394_ff_hdr),
+ sizeof(struct eth1394_sf_hdr),
+ sizeof(struct eth1394_sf_hdr)
+};
+
+static const u16 eth1394_speedto_maxpayload[] = {
+/* S100, S200, S400, S800, S1600, S3200 */
+ 512, 1024, 2048, 4096, 4096, 4096
+};
+
+MODULE_AUTHOR("Ben Collins (bcollins@debian.org)");
+MODULE_DESCRIPTION("IEEE 1394 IPv4 Driver (IPv4-over-1394 as per RFC 2734)");
+MODULE_LICENSE("GPL");
+
+/*
+ * The max_partial_datagrams parameter is the maximum number of fragmented
+ * datagrams per node that eth1394 will keep in memory. Providing an upper
+ * bound allows us to limit the amount of memory that partial datagrams
+ * consume in the event that some partial datagrams are never completed.
+ */
+static int max_partial_datagrams = 25;
+module_param(max_partial_datagrams, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(max_partial_datagrams,
+ "Maximum number of partially received fragmented datagrams "
+ "(default = 25).");
+
+
+static int ether1394_header(struct sk_buff *skb, struct net_device *dev,
+ unsigned short type, const void *daddr,
+ const void *saddr, unsigned len);
+static int ether1394_rebuild_header(struct sk_buff *skb);
+static int ether1394_header_parse(const struct sk_buff *skb,
+ unsigned char *haddr);
+static int ether1394_header_cache(const struct neighbour *neigh,
+ struct hh_cache *hh);
+static void ether1394_header_cache_update(struct hh_cache *hh,
+ const struct net_device *dev,
+ const unsigned char *haddr);
+static int ether1394_tx(struct sk_buff *skb, struct net_device *dev);
+static void ether1394_iso(struct hpsb_iso *iso);
+
+static struct ethtool_ops ethtool_ops;
+
+static int ether1394_write(struct hpsb_host *host, int srcid, int destid,
+ quadlet_t *data, u64 addr, size_t len, u16 flags);
+static void ether1394_add_host(struct hpsb_host *host);
+static void ether1394_remove_host(struct hpsb_host *host);
+static void ether1394_host_reset(struct hpsb_host *host);
+
+/* Function for incoming 1394 packets */
+static struct hpsb_address_ops addr_ops = {
+ .write = ether1394_write,
+};
+
+/* Ieee1394 highlevel driver functions */
+static struct hpsb_highlevel eth1394_highlevel = {
+ .name = driver_name,
+ .add_host = ether1394_add_host,
+ .remove_host = ether1394_remove_host,
+ .host_reset = ether1394_host_reset,
+};
+
+static int ether1394_recv_init(struct eth1394_priv *priv)
+{
+ unsigned int iso_buf_size;
+
+ /* FIXME: rawiso limits us to PAGE_SIZE */
+ iso_buf_size = min((unsigned int)PAGE_SIZE,
+ 2 * (1U << (priv->host->csr.max_rec + 1)));
+
+ priv->iso = hpsb_iso_recv_init(priv->host,
+ ETHER1394_GASP_BUFFERS * iso_buf_size,
+ ETHER1394_GASP_BUFFERS,
+ priv->broadcast_channel,
+ HPSB_ISO_DMA_PACKET_PER_BUFFER,
+ 1, ether1394_iso);
+ if (priv->iso == NULL) {
+ ETH1394_PRINT_G(KERN_ERR, "Failed to allocate IR context\n");
+ priv->bc_state = ETHER1394_BC_ERROR;
+ return -EAGAIN;
+ }
+
+ if (hpsb_iso_recv_start(priv->iso, -1, (1 << 3), -1) < 0)
+ priv->bc_state = ETHER1394_BC_STOPPED;
+ else
+ priv->bc_state = ETHER1394_BC_RUNNING;
+ return 0;
+}
+
+/* This is called after an "ifup" */
+static int ether1394_open(struct net_device *dev)
+{
+ struct eth1394_priv *priv = netdev_priv(dev);
+ int ret;
+
+ if (priv->bc_state == ETHER1394_BC_ERROR) {
+ ret = ether1394_recv_init(priv);
+ if (ret)
+ return ret;
+ }
+ netif_start_queue(dev);
+ return 0;
+}
+
+/* This is called after an "ifdown" */
+static int ether1394_stop(struct net_device *dev)
+{
+ /* flush priv->wake */
+ flush_scheduled_work();
+
+ netif_stop_queue(dev);
+ return 0;
+}
+
+/* Return statistics to the caller */
+static struct net_device_stats *ether1394_stats(struct net_device *dev)
+{
+ return &(((struct eth1394_priv *)netdev_priv(dev))->stats);
+}
+
+/* FIXME: What to do if we timeout? I think a host reset is probably in order,
+ * so that's what we do. Should we increment the stat counters too? */
+static void ether1394_tx_timeout(struct net_device *dev)
+{
+ struct hpsb_host *host =
+ ((struct eth1394_priv *)netdev_priv(dev))->host;
+
+ ETH1394_PRINT(KERN_ERR, dev->name, "Timeout, resetting host\n");
+ ether1394_host_reset(host);
+}
+
+static inline int ether1394_max_mtu(struct hpsb_host* host)
+{
+ return (1 << (host->csr.max_rec + 1))
+ - sizeof(union eth1394_hdr) - ETHER1394_GASP_OVERHEAD;
+}
+
+static int ether1394_change_mtu(struct net_device *dev, int new_mtu)
+{
+ int max_mtu;
+
+ if (new_mtu < 68)
+ return -EINVAL;
+
+ max_mtu = ether1394_max_mtu(
+ ((struct eth1394_priv *)netdev_priv(dev))->host);
+ if (new_mtu > max_mtu) {
+ ETH1394_PRINT(KERN_INFO, dev->name,
+ "Local node constrains MTU to %d\n", max_mtu);
+ return -ERANGE;
+ }
+
+ dev->mtu = new_mtu;
+ return 0;
+}
+
+static void purge_partial_datagram(struct list_head *old)
+{
+ struct partial_datagram *pd;
+ struct list_head *lh, *n;
+ struct fragment_info *fi;
+
+ pd = list_entry(old, struct partial_datagram, list);
+
+ list_for_each_safe(lh, n, &pd->frag_info) {
+ fi = list_entry(lh, struct fragment_info, list);
+ list_del(lh);
+ kfree(fi);
+ }
+ list_del(old);
+ kfree_skb(pd->skb);
+ kfree(pd);
+}
+
+/******************************************
+ * 1394 bus activity functions
+ ******************************************/
+
+static struct eth1394_node_ref *eth1394_find_node(struct list_head *inl,
+ struct unit_directory *ud)
+{
+ struct eth1394_node_ref *node;
+
+ list_for_each_entry(node, inl, list)
+ if (node->ud == ud)
+ return node;
+
+ return NULL;
+}
+
+static struct eth1394_node_ref *eth1394_find_node_guid(struct list_head *inl,
+ u64 guid)
+{
+ struct eth1394_node_ref *node;
+
+ list_for_each_entry(node, inl, list)
+ if (node->ud->ne->guid == guid)
+ return node;
+
+ return NULL;
+}
+
+static struct eth1394_node_ref *eth1394_find_node_nodeid(struct list_head *inl,
+ nodeid_t nodeid)
+{
+ struct eth1394_node_ref *node;
+
+ list_for_each_entry(node, inl, list)
+ if (node->ud->ne->nodeid == nodeid)
+ return node;
+
+ return NULL;
+}
+
+static int eth1394_new_node(struct eth1394_host_info *hi,
+ struct unit_directory *ud)
+{
+ struct eth1394_priv *priv;
+ struct eth1394_node_ref *new_node;
+ struct eth1394_node_info *node_info;
+
+ new_node = kmalloc(sizeof(*new_node), GFP_KERNEL);
+ if (!new_node)
+ return -ENOMEM;
+
+ node_info = kmalloc(sizeof(*node_info), GFP_KERNEL);
+ if (!node_info) {
+ kfree(new_node);
+ return -ENOMEM;
+ }
+
+ spin_lock_init(&node_info->pdg.lock);
+ INIT_LIST_HEAD(&node_info->pdg.list);
+ node_info->pdg.sz = 0;
+ node_info->fifo = CSR1212_INVALID_ADDR_SPACE;
+
+ ud->device.driver_data = node_info;
+ new_node->ud = ud;
+
+ priv = netdev_priv(hi->dev);
+ list_add_tail(&new_node->list, &priv->ip_node_list);
+ return 0;
+}
+
+static int eth1394_probe(struct device *dev)
+{
+ struct unit_directory *ud;
+ struct eth1394_host_info *hi;
+
+ ud = container_of(dev, struct unit_directory, device);
+ hi = hpsb_get_hostinfo(&eth1394_highlevel, ud->ne->host);
+ if (!hi)
+ return -ENOENT;
+
+ return eth1394_new_node(hi, ud);
+}
+
+static int eth1394_remove(struct device *dev)
+{
+ struct unit_directory *ud;
+ struct eth1394_host_info *hi;
+ struct eth1394_priv *priv;
+ struct eth1394_node_ref *old_node;
+ struct eth1394_node_info *node_info;
+ struct list_head *lh, *n;
+ unsigned long flags;
+
+ ud = container_of(dev, struct unit_directory, device);
+ hi = hpsb_get_hostinfo(&eth1394_highlevel, ud->ne->host);
+ if (!hi)
+ return -ENOENT;
+
+ priv = netdev_priv(hi->dev);
+
+ old_node = eth1394_find_node(&priv->ip_node_list, ud);
+ if (!old_node)
+ return 0;
+
+ list_del(&old_node->list);
+ kfree(old_node);
+
+ node_info = (struct eth1394_node_info*)ud->device.driver_data;
+
+ spin_lock_irqsave(&node_info->pdg.lock, flags);
+ /* The partial datagram list should be empty, but we'll just
+ * make sure anyway... */
+ list_for_each_safe(lh, n, &node_info->pdg.list)
+ purge_partial_datagram(lh);
+ spin_unlock_irqrestore(&node_info->pdg.lock, flags);
+
+ kfree(node_info);
+ ud->device.driver_data = NULL;
+ return 0;
+}
+
+static int eth1394_update(struct unit_directory *ud)
+{
+ struct eth1394_host_info *hi;
+ struct eth1394_priv *priv;
+ struct eth1394_node_ref *node;
+
+ hi = hpsb_get_hostinfo(&eth1394_highlevel, ud->ne->host);
+ if (!hi)
+ return -ENOENT;
+
+ priv = netdev_priv(hi->dev);
+ node = eth1394_find_node(&priv->ip_node_list, ud);
+ if (node)
+ return 0;
+
+ return eth1394_new_node(hi, ud);
+}
+
+static struct ieee1394_device_id eth1394_id_table[] = {
+ {
+ .match_flags = (IEEE1394_MATCH_SPECIFIER_ID |
+ IEEE1394_MATCH_VERSION),
+ .specifier_id = ETHER1394_GASP_SPECIFIER_ID,
+ .version = ETHER1394_GASP_VERSION,
+ },
+ {}
+};
+
+MODULE_DEVICE_TABLE(ieee1394, eth1394_id_table);
+
+static struct hpsb_protocol_driver eth1394_proto_driver = {
+ .name = driver_name,
+ .id_table = eth1394_id_table,
+ .update = eth1394_update,
+ .driver = {
+ .probe = eth1394_probe,
+ .remove = eth1394_remove,
+ },
+};
+
+static void ether1394_reset_priv(struct net_device *dev, int set_mtu)
+{
+ unsigned long flags;
+ int i;
+ struct eth1394_priv *priv = netdev_priv(dev);
+ struct hpsb_host *host = priv->host;
+ u64 guid = get_unaligned((u64 *)&(host->csr.rom->bus_info_data[3]));
+ int max_speed = IEEE1394_SPEED_MAX;
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ memset(priv->ud_list, 0, sizeof(priv->ud_list));
+ priv->bc_maxpayload = 512;
+
+ /* Determine speed limit */
+ /* FIXME: This is broken for nodes with link speed < PHY speed,
+ * and it is suboptimal for S200B...S800B hardware.
+ * The result of nodemgr's speed probe should be used somehow. */
+ for (i = 0; i < host->node_count; i++) {
+ /* take care of S100B...S400B PHY ports */
+ if (host->speed[i] == SELFID_SPEED_UNKNOWN) {
+ max_speed = IEEE1394_SPEED_100;
+ break;
+ }
+ if (max_speed > host->speed[i])
+ max_speed = host->speed[i];
+ }
+ priv->bc_sspd = max_speed;
+
+ if (set_mtu) {
+ /* Use the RFC 2734 default 1500 octets or the maximum payload
+ * as initial MTU */
+ dev->mtu = min(1500, ether1394_max_mtu(host));
+
+ /* Set our hardware address while we're at it */
+ memcpy(dev->dev_addr, &guid, sizeof(u64));
+ memset(dev->broadcast, 0xff, sizeof(u64));
+ }
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+}
+
+static const struct header_ops ether1394_header_ops = {
+ .create = ether1394_header,
+ .rebuild = ether1394_rebuild_header,
+ .cache = ether1394_header_cache,
+ .cache_update = ether1394_header_cache_update,
+ .parse = ether1394_header_parse,
+};
+
+static void ether1394_init_dev(struct net_device *dev)
+{
+ dev->open = ether1394_open;
+ dev->stop = ether1394_stop;
+ dev->hard_start_xmit = ether1394_tx;
+ dev->get_stats = ether1394_stats;
+ dev->tx_timeout = ether1394_tx_timeout;
+ dev->change_mtu = ether1394_change_mtu;
+
+ dev->header_ops = &ether1394_header_ops;
+
+ SET_ETHTOOL_OPS(dev, &ethtool_ops);
+
+ dev->watchdog_timeo = ETHER1394_TIMEOUT;
+ dev->flags = IFF_BROADCAST | IFF_MULTICAST;
+ dev->features = NETIF_F_HIGHDMA;
+ dev->addr_len = ETH1394_ALEN;
+ dev->hard_header_len = ETH1394_HLEN;
+ dev->type = ARPHRD_IEEE1394;
+
+ /* FIXME: This value was copied from ether_setup(). Is it too much? */
+ dev->tx_queue_len = 1000;
+}
+
+/*
+ * Wake the queue up after commonly encountered transmit failure conditions are
+ * hopefully over. Currently only tlabel exhaustion is accounted for.
+ */
+static void ether1394_wake_queue(struct work_struct *work)
+{
+ struct eth1394_priv *priv;
+ struct hpsb_packet *packet;
+
+ priv = container_of(work, struct eth1394_priv, wake);
+ packet = hpsb_alloc_packet(0);
+
+ /* This is really bad, but unjam the queue anyway. */
+ if (!packet)
+ goto out;
+
+ packet->host = priv->host;
+ packet->node_id = priv->wake_node;
+ /*
+ * A transaction label is all we really want. If we get one, it almost
+ * always means we can get a lot more because the ieee1394 core recycled
+ * a whole batch of tlabels, at last.
+ */
+ if (hpsb_get_tlabel(packet) == 0)
+ hpsb_free_tlabel(packet);
+
+ hpsb_free_packet(packet);
+out:
+ netif_wake_queue(priv->wake_dev);
+}
+
+/*
+ * This function is called every time a card is found. It is generally called
+ * when the module is installed. This is where we add all of our ethernet
+ * devices. One for each host.
+ */
+static void ether1394_add_host(struct hpsb_host *host)
+{
+ struct eth1394_host_info *hi = NULL;
+ struct net_device *dev = NULL;
+ struct eth1394_priv *priv;
+ u64 fifo_addr;
+
+ if (hpsb_config_rom_ip1394_add(host) != 0) {
+ ETH1394_PRINT_G(KERN_ERR, "Can't add IP-over-1394 ROM entry\n");
+ return;
+ }
+
+ fifo_addr = hpsb_allocate_and_register_addrspace(
+ &eth1394_highlevel, host, &addr_ops,
+ ETHER1394_REGION_ADDR_LEN, ETHER1394_REGION_ADDR_LEN,
+ CSR1212_INVALID_ADDR_SPACE, CSR1212_INVALID_ADDR_SPACE);
+ if (fifo_addr == CSR1212_INVALID_ADDR_SPACE) {
+ ETH1394_PRINT_G(KERN_ERR, "Cannot register CSR space\n");
+ hpsb_config_rom_ip1394_remove(host);
+ return;
+ }
+
+ dev = alloc_netdev(sizeof(*priv), "eth%d", ether1394_init_dev);
+ if (dev == NULL) {
+ ETH1394_PRINT_G(KERN_ERR, "Out of memory\n");
+ goto out;
+ }
+
+ SET_NETDEV_DEV(dev, &host->device);
+
+ priv = netdev_priv(dev);
+ INIT_LIST_HEAD(&priv->ip_node_list);
+ spin_lock_init(&priv->lock);
+ priv->host = host;
+ priv->local_fifo = fifo_addr;
+ INIT_WORK(&priv->wake, ether1394_wake_queue);
+ priv->wake_dev = dev;
+
+ hi = hpsb_create_hostinfo(&eth1394_highlevel, host, sizeof(*hi));
+ if (hi == NULL) {
+ ETH1394_PRINT_G(KERN_ERR, "Out of memory\n");
+ goto out;
+ }
+
+ ether1394_reset_priv(dev, 1);
+
+ if (register_netdev(dev)) {
+ ETH1394_PRINT_G(KERN_ERR, "Cannot register the driver\n");
+ goto out;
+ }
+
+ ETH1394_PRINT(KERN_INFO, dev->name, "IPv4 over IEEE 1394 (fw-host%d)\n",
+ host->id);
+
+ hi->host = host;
+ hi->dev = dev;
+
+ /* Ignore validity in hopes that it will be set in the future. It'll
+ * be checked when the eth device is opened. */
+ priv->broadcast_channel = host->csr.broadcast_channel & 0x3f;
+
+ ether1394_recv_init(priv);
+ return;
+out:
+ if (dev)
+ free_netdev(dev);
+ if (hi)
+ hpsb_destroy_hostinfo(&eth1394_highlevel, host);
+ hpsb_unregister_addrspace(&eth1394_highlevel, host, fifo_addr);
+ hpsb_config_rom_ip1394_remove(host);
+}
+
+/* Remove a card from our list */
+static void ether1394_remove_host(struct hpsb_host *host)
+{
+ struct eth1394_host_info *hi;
+ struct eth1394_priv *priv;
+
+ hi = hpsb_get_hostinfo(&eth1394_highlevel, host);
+ if (!hi)
+ return;
+ priv = netdev_priv(hi->dev);
+ hpsb_unregister_addrspace(&eth1394_highlevel, host, priv->local_fifo);
+ hpsb_config_rom_ip1394_remove(host);
+ if (priv->iso)
+ hpsb_iso_shutdown(priv->iso);
+ unregister_netdev(hi->dev);
+ free_netdev(hi->dev);
+}
+
+/* A bus reset happened */
+static void ether1394_host_reset(struct hpsb_host *host)
+{
+ struct eth1394_host_info *hi;
+ struct eth1394_priv *priv;
+ struct net_device *dev;
+ struct list_head *lh, *n;
+ struct eth1394_node_ref *node;
+ struct eth1394_node_info *node_info;
+ unsigned long flags;
+
+ hi = hpsb_get_hostinfo(&eth1394_highlevel, host);
+
+ /* This can happen for hosts that we don't use */
+ if (!hi)
+ return;
+
+ dev = hi->dev;
+ priv = netdev_priv(dev);
+
+ /* Reset our private host data, but not our MTU */
+ netif_stop_queue(dev);
+ ether1394_reset_priv(dev, 0);
+
+ list_for_each_entry(node, &priv->ip_node_list, list) {
+ node_info = node->ud->device.driver_data;
+
+ spin_lock_irqsave(&node_info->pdg.lock, flags);
+
+ list_for_each_safe(lh, n, &node_info->pdg.list)
+ purge_partial_datagram(lh);
+
+ INIT_LIST_HEAD(&(node_info->pdg.list));
+ node_info->pdg.sz = 0;
+
+ spin_unlock_irqrestore(&node_info->pdg.lock, flags);
+ }
+
+ netif_wake_queue(dev);
+}
+
+/******************************************
+ * HW Header net device functions
+ ******************************************/
+/* These functions have been adapted from net/ethernet/eth.c */
+
+/* Create a fake MAC header for an arbitrary protocol layer.
+ * saddr=NULL means use device source address
+ * daddr=NULL means leave destination address (eg unresolved arp). */
+static int ether1394_header(struct sk_buff *skb, struct net_device *dev,
+ unsigned short type, const void *daddr,
+ const void *saddr, unsigned len)
+{
+ struct eth1394hdr *eth =
+ (struct eth1394hdr *)skb_push(skb, ETH1394_HLEN);
+
+ eth->h_proto = htons(type);
+
+ if (dev->flags & (IFF_LOOPBACK | IFF_NOARP)) {
+ memset(eth->h_dest, 0, dev->addr_len);
+ return dev->hard_header_len;
+ }
+
+ if (daddr) {
+ memcpy(eth->h_dest, daddr, dev->addr_len);
+ return dev->hard_header_len;
+ }
+
+ return -dev->hard_header_len;
+}
+
+/* Rebuild the faked MAC header. This is called after an ARP
+ * (or in future other address resolution) has completed on this
+ * sk_buff. We now let ARP fill in the other fields.
+ *
+ * This routine CANNOT use cached dst->neigh!
+ * Really, it is used only when dst->neigh is wrong.
+ */
+static int ether1394_rebuild_header(struct sk_buff *skb)
+{
+ struct eth1394hdr *eth = (struct eth1394hdr *)skb->data;
+
+ if (eth->h_proto == htons(ETH_P_IP))
+ return arp_find((unsigned char *)&eth->h_dest, skb);
+
+ ETH1394_PRINT(KERN_DEBUG, skb->dev->name,
+ "unable to resolve type %04x addresses\n",
+ ntohs(eth->h_proto));
+ return 0;
+}
+
+static int ether1394_header_parse(const struct sk_buff *skb,
+ unsigned char *haddr)
+{
+ memcpy(haddr, skb->dev->dev_addr, ETH1394_ALEN);
+ return ETH1394_ALEN;
+}
+
+static int ether1394_header_cache(const struct neighbour *neigh,
+ struct hh_cache *hh)
+{
+ unsigned short type = hh->hh_type;
+ struct net_device *dev = neigh->dev;
+ struct eth1394hdr *eth =
+ (struct eth1394hdr *)((u8 *)hh->hh_data + 16 - ETH1394_HLEN);
+
+ if (type == htons(ETH_P_802_3))
+ return -1;
+
+ eth->h_proto = type;
+ memcpy(eth->h_dest, neigh->ha, dev->addr_len);
+
+ hh->hh_len = ETH1394_HLEN;
+ return 0;
+}
+
+/* Called by Address Resolution module to notify changes in address. */
+static void ether1394_header_cache_update(struct hh_cache *hh,
+ const struct net_device *dev,
+ const unsigned char * haddr)
+{
+ memcpy((u8 *)hh->hh_data + 16 - ETH1394_HLEN, haddr, dev->addr_len);
+}
+
+/******************************************
+ * Datagram reception code
+ ******************************************/
+
+/* Copied from net/ethernet/eth.c */
+static u16 ether1394_type_trans(struct sk_buff *skb, struct net_device *dev)
+{
+ struct eth1394hdr *eth;
+ unsigned char *rawp;
+
+ skb_reset_mac_header(skb);
+ skb_pull(skb, ETH1394_HLEN);
+ eth = eth1394_hdr(skb);
+
+ if (*eth->h_dest & 1) {
+ if (memcmp(eth->h_dest, dev->broadcast, dev->addr_len) == 0)
+ skb->pkt_type = PACKET_BROADCAST;
+#if 0
+ else
+ skb->pkt_type = PACKET_MULTICAST;
+#endif
+ } else {
+ if (memcmp(eth->h_dest, dev->dev_addr, dev->addr_len))
+ skb->pkt_type = PACKET_OTHERHOST;
+ }
+
+ if (ntohs(eth->h_proto) >= 1536)
+ return eth->h_proto;
+
+ rawp = skb->data;
+
+ if (*(unsigned short *)rawp == 0xFFFF)
+ return htons(ETH_P_802_3);
+
+ return htons(ETH_P_802_2);
+}
+
+/* Parse an encapsulated IP1394 header into an ethernet frame packet.
+ * We also perform ARP translation here, if need be. */
+static u16 ether1394_parse_encap(struct sk_buff *skb, struct net_device *dev,
+ nodeid_t srcid, nodeid_t destid,
+ u16 ether_type)
+{
+ struct eth1394_priv *priv = netdev_priv(dev);
+ u64 dest_hw;
+ unsigned short ret = 0;
+
+ /* Setup our hw addresses. We use these to build the ethernet header. */
+ if (destid == (LOCAL_BUS | ALL_NODES))
+ dest_hw = ~0ULL; /* broadcast */
+ else
+ dest_hw = cpu_to_be64((u64)priv->host->csr.guid_hi << 32 |
+ priv->host->csr.guid_lo);
+
+ /* If this is an ARP packet, convert it. First, we want to make
+ * use of some of the fields, since they tell us a little bit
+ * about the sending machine. */
+ if (ether_type == htons(ETH_P_ARP)) {
+ struct eth1394_arp *arp1394 = (struct eth1394_arp *)skb->data;
+ struct arphdr *arp = (struct arphdr *)skb->data;
+ unsigned char *arp_ptr = (unsigned char *)(arp + 1);
+ u64 fifo_addr = (u64)ntohs(arp1394->fifo_hi) << 32 |
+ ntohl(arp1394->fifo_lo);
+ u8 max_rec = min(priv->host->csr.max_rec,
+ (u8)(arp1394->max_rec));
+ int sspd = arp1394->sspd;
+ u16 maxpayload;
+ struct eth1394_node_ref *node;
+ struct eth1394_node_info *node_info;
+ __be64 guid;
+
+ /* Sanity check. MacOSX seems to be sending us 131 in this
+ * field (atleast on my Panther G5). Not sure why. */
+ if (sspd > 5 || sspd < 0)
+ sspd = 0;
+
+ maxpayload = min(eth1394_speedto_maxpayload[sspd],
+ (u16)(1 << (max_rec + 1)));
+
+ guid = get_unaligned(&arp1394->s_uniq_id);
+ node = eth1394_find_node_guid(&priv->ip_node_list,
+ be64_to_cpu(guid));
+ if (!node)
+ return 0;
+
+ node_info =
+ (struct eth1394_node_info *)node->ud->device.driver_data;
+
+ /* Update our speed/payload/fifo_offset table */
+ node_info->maxpayload = maxpayload;
+ node_info->sspd = sspd;
+ node_info->fifo = fifo_addr;
+
+ /* Now that we're done with the 1394 specific stuff, we'll
+ * need to alter some of the data. Believe it or not, all
+ * that needs to be done is sender_IP_address needs to be
+ * moved, the destination hardware address get stuffed
+ * in and the hardware address length set to 8.
+ *
+ * IMPORTANT: The code below overwrites 1394 specific data
+ * needed above so keep the munging of the data for the
+ * higher level IP stack last. */
+
+ arp->ar_hln = 8;
+ arp_ptr += arp->ar_hln; /* skip over sender unique id */
+ *(u32 *)arp_ptr = arp1394->sip; /* move sender IP addr */
+ arp_ptr += arp->ar_pln; /* skip over sender IP addr */
+
+ if (arp->ar_op == htons(ARPOP_REQUEST))
+ memset(arp_ptr, 0, sizeof(u64));
+ else
+ memcpy(arp_ptr, dev->dev_addr, sizeof(u64));
+ }
+
+ /* Now add the ethernet header. */
+ if (dev_hard_header(skb, dev, ntohs(ether_type), &dest_hw, NULL,
+ skb->len) >= 0)
+ ret = ether1394_type_trans(skb, dev);
+
+ return ret;
+}
+
+static int fragment_overlap(struct list_head *frag_list, int offset, int len)
+{
+ struct fragment_info *fi;
+ int end = offset + len;
+
+ list_for_each_entry(fi, frag_list, list)
+ if (offset < fi->offset + fi->len && end > fi->offset)
+ return 1;
+
+ return 0;
+}
+
+static struct list_head *find_partial_datagram(struct list_head *pdgl, int dgl)
+{
+ struct partial_datagram *pd;
+
+ list_for_each_entry(pd, pdgl, list)
+ if (pd->dgl == dgl)
+ return &pd->list;
+
+ return NULL;
+}
+
+/* Assumes that new fragment does not overlap any existing fragments */
+static int new_fragment(struct list_head *frag_info, int offset, int len)
+{
+ struct list_head *lh;
+ struct fragment_info *fi, *fi2, *new;
+
+ list_for_each(lh, frag_info) {
+ fi = list_entry(lh, struct fragment_info, list);
+ if (fi->offset + fi->len == offset) {
+ /* The new fragment can be tacked on to the end */
+ fi->len += len;
+ /* Did the new fragment plug a hole? */
+ fi2 = list_entry(lh->next, struct fragment_info, list);
+ if (fi->offset + fi->len == fi2->offset) {
+ /* glue fragments together */
+ fi->len += fi2->len;
+ list_del(lh->next);
+ kfree(fi2);
+ }
+ return 0;
+ } else if (offset + len == fi->offset) {
+ /* The new fragment can be tacked on to the beginning */
+ fi->offset = offset;
+ fi->len += len;
+ /* Did the new fragment plug a hole? */
+ fi2 = list_entry(lh->prev, struct fragment_info, list);
+ if (fi2->offset + fi2->len == fi->offset) {
+ /* glue fragments together */
+ fi2->len += fi->len;
+ list_del(lh);
+ kfree(fi);
+ }
+ return 0;
+ } else if (offset > fi->offset + fi->len) {
+ break;
+ } else if (offset + len < fi->offset) {
+ lh = lh->prev;
+ break;
+ }
+ }
+
+ new = kmalloc(sizeof(*new), GFP_ATOMIC);
+ if (!new)
+ return -ENOMEM;
+
+ new->offset = offset;
+ new->len = len;
+
+ list_add(&new->list, lh);
+ return 0;
+}
+
+static int new_partial_datagram(struct net_device *dev, struct list_head *pdgl,
+ int dgl, int dg_size, char *frag_buf,
+ int frag_off, int frag_len)
+{
+ struct partial_datagram *new;
+
+ new = kmalloc(sizeof(*new), GFP_ATOMIC);
+ if (!new)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&new->frag_info);
+
+ if (new_fragment(&new->frag_info, frag_off, frag_len) < 0) {
+ kfree(new);
+ return -ENOMEM;
+ }
+
+ new->dgl = dgl;
+ new->dg_size = dg_size;
+
+ new->skb = dev_alloc_skb(dg_size + dev->hard_header_len + 15);
+ if (!new->skb) {
+ struct fragment_info *fi = list_entry(new->frag_info.next,
+ struct fragment_info,
+ list);
+ kfree(fi);
+ kfree(new);
+ return -ENOMEM;
+ }
+
+ skb_reserve(new->skb, (dev->hard_header_len + 15) & ~15);
+ new->pbuf = skb_put(new->skb, dg_size);
+ memcpy(new->pbuf + frag_off, frag_buf, frag_len);
+
+ list_add(&new->list, pdgl);
+ return 0;
+}
+
+static int update_partial_datagram(struct list_head *pdgl, struct list_head *lh,
+ char *frag_buf, int frag_off, int frag_len)
+{
+ struct partial_datagram *pd =
+ list_entry(lh, struct partial_datagram, list);
+
+ if (new_fragment(&pd->frag_info, frag_off, frag_len) < 0)
+ return -ENOMEM;
+
+ memcpy(pd->pbuf + frag_off, frag_buf, frag_len);
+
+ /* Move list entry to beginnig of list so that oldest partial
+ * datagrams percolate to the end of the list */
+ list_move(lh, pdgl);
+ return 0;
+}
+
+static int is_datagram_complete(struct list_head *lh, int dg_size)
+{
+ struct partial_datagram *pd;
+ struct fragment_info *fi;
+
+ pd = list_entry(lh, struct partial_datagram, list);
+ fi = list_entry(pd->frag_info.next, struct fragment_info, list);
+
+ return (fi->len == dg_size);
+}
+
+/* Packet reception. We convert the IP1394 encapsulation header to an
+ * ethernet header, and fill it with some of our other fields. This is
+ * an incoming packet from the 1394 bus. */
+static int ether1394_data_handler(struct net_device *dev, int srcid, int destid,
+ char *buf, int len)
+{
+ struct sk_buff *skb;
+ unsigned long flags;
+ struct eth1394_priv *priv = netdev_priv(dev);
+ union eth1394_hdr *hdr = (union eth1394_hdr *)buf;
+ u16 ether_type = 0; /* initialized to clear warning */
+ int hdr_len;
+ struct unit_directory *ud = priv->ud_list[NODEID_TO_NODE(srcid)];
+ struct eth1394_node_info *node_info;
+
+ if (!ud) {
+ struct eth1394_node_ref *node;
+ node = eth1394_find_node_nodeid(&priv->ip_node_list, srcid);
+ if (unlikely(!node)) {
+ HPSB_PRINT(KERN_ERR, "ether1394 rx: sender nodeid "
+ "lookup failure: " NODE_BUS_FMT,
+ NODE_BUS_ARGS(priv->host, srcid));
+ priv->stats.rx_dropped++;
+ return -1;
+ }
+ ud = node->ud;
+
+ priv->ud_list[NODEID_TO_NODE(srcid)] = ud;
+ }
+
+ node_info = (struct eth1394_node_info *)ud->device.driver_data;
+
+ /* First, did we receive a fragmented or unfragmented datagram? */
+ hdr->words.word1 = ntohs(hdr->words.word1);
+
+ hdr_len = hdr_type_len[hdr->common.lf];
+
+ if (hdr->common.lf == ETH1394_HDR_LF_UF) {
+ /* An unfragmented datagram has been received by the ieee1394
+ * bus. Build an skbuff around it so we can pass it to the
+ * high level network layer. */
+
+ skb = dev_alloc_skb(len + dev->hard_header_len + 15);
+ if (unlikely(!skb)) {
+ ETH1394_PRINT_G(KERN_ERR, "Out of memory\n");
+ priv->stats.rx_dropped++;
+ return -1;
+ }
+ skb_reserve(skb, (dev->hard_header_len + 15) & ~15);
+ memcpy(skb_put(skb, len - hdr_len), buf + hdr_len,
+ len - hdr_len);
+ ether_type = hdr->uf.ether_type;
+ } else {
+ /* A datagram fragment has been received, now the fun begins. */
+
+ struct list_head *pdgl, *lh;
+ struct partial_datagram *pd;
+ int fg_off;
+ int fg_len = len - hdr_len;
+ int dg_size;
+ int dgl;
+ int retval;
+ struct pdg_list *pdg = &(node_info->pdg);
+
+ hdr->words.word3 = ntohs(hdr->words.word3);
+ /* The 4th header word is reserved so no need to do ntohs() */
+
+ if (hdr->common.lf == ETH1394_HDR_LF_FF) {
+ ether_type = hdr->ff.ether_type;
+ dgl = hdr->ff.dgl;
+ dg_size = hdr->ff.dg_size + 1;
+ fg_off = 0;
+ } else {
+ hdr->words.word2 = ntohs(hdr->words.word2);
+ dgl = hdr->sf.dgl;
+ dg_size = hdr->sf.dg_size + 1;
+ fg_off = hdr->sf.fg_off;
+ }
+ spin_lock_irqsave(&pdg->lock, flags);
+
+ pdgl = &(pdg->list);
+ lh = find_partial_datagram(pdgl, dgl);
+
+ if (lh == NULL) {
+ while (pdg->sz >= max_partial_datagrams) {
+ /* remove the oldest */
+ purge_partial_datagram(pdgl->prev);
+ pdg->sz--;
+ }
+
+ retval = new_partial_datagram(dev, pdgl, dgl, dg_size,
+ buf + hdr_len, fg_off,
+ fg_len);
+ if (retval < 0) {
+ spin_unlock_irqrestore(&pdg->lock, flags);
+ goto bad_proto;
+ }
+ pdg->sz++;
+ lh = find_partial_datagram(pdgl, dgl);
+ } else {
+ pd = list_entry(lh, struct partial_datagram, list);
+
+ if (fragment_overlap(&pd->frag_info, fg_off, fg_len)) {
+ /* Overlapping fragments, obliterate old
+ * datagram and start new one. */
+ purge_partial_datagram(lh);
+ retval = new_partial_datagram(dev, pdgl, dgl,
+ dg_size,
+ buf + hdr_len,
+ fg_off, fg_len);
+ if (retval < 0) {
+ pdg->sz--;
+ spin_unlock_irqrestore(&pdg->lock, flags);
+ goto bad_proto;
+ }
+ } else {
+ retval = update_partial_datagram(pdgl, lh,
+ buf + hdr_len,
+ fg_off, fg_len);
+ if (retval < 0) {
+ /* Couldn't save off fragment anyway
+ * so might as well obliterate the
+ * datagram now. */
+ purge_partial_datagram(lh);
+ pdg->sz--;
+ spin_unlock_irqrestore(&pdg->lock, flags);
+ goto bad_proto;
+ }
+ } /* fragment overlap */
+ } /* new datagram or add to existing one */
+
+ pd = list_entry(lh, struct partial_datagram, list);
+
+ if (hdr->common.lf == ETH1394_HDR_LF_FF)
+ pd->ether_type = ether_type;
+
+ if (is_datagram_complete(lh, dg_size)) {
+ ether_type = pd->ether_type;
+ pdg->sz--;
+ skb = skb_get(pd->skb);
+ purge_partial_datagram(lh);
+ spin_unlock_irqrestore(&pdg->lock, flags);
+ } else {
+ /* Datagram is not complete, we're done for the
+ * moment. */
+ spin_unlock_irqrestore(&pdg->lock, flags);
+ return 0;
+ }
+ } /* unframgented datagram or fragmented one */
+
+ /* Write metadata, and then pass to the receive level */
+ skb->dev = dev;
+ skb->ip_summed = CHECKSUM_UNNECESSARY; /* don't check it */
+
+ /* Parse the encapsulation header. This actually does the job of
+ * converting to an ethernet frame header, aswell as arp
+ * conversion if needed. ARP conversion is easier in this
+ * direction, since we are using ethernet as our backend. */
+ skb->protocol = ether1394_parse_encap(skb, dev, srcid, destid,
+ ether_type);
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ if (!skb->protocol) {
+ priv->stats.rx_errors++;
+ priv->stats.rx_dropped++;
+ dev_kfree_skb_any(skb);
+ } else if (netif_rx(skb) == NET_RX_DROP) {
+ priv->stats.rx_errors++;
+ priv->stats.rx_dropped++;
+ } else {
+ priv->stats.rx_packets++;
+ priv->stats.rx_bytes += skb->len;
+ }
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+bad_proto:
+ if (netif_queue_stopped(dev))
+ netif_wake_queue(dev);
+
+ dev->last_rx = jiffies;
+
+ return 0;
+}
+
+static int ether1394_write(struct hpsb_host *host, int srcid, int destid,
+ quadlet_t *data, u64 addr, size_t len, u16 flags)
+{
+ struct eth1394_host_info *hi;
+
+ hi = hpsb_get_hostinfo(&eth1394_highlevel, host);
+ if (unlikely(!hi)) {
+ ETH1394_PRINT_G(KERN_ERR, "No net device at fw-host%d\n",
+ host->id);
+ return RCODE_ADDRESS_ERROR;
+ }
+
+ if (ether1394_data_handler(hi->dev, srcid, destid, (char*)data, len))
+ return RCODE_ADDRESS_ERROR;
+ else
+ return RCODE_COMPLETE;
+}
+
+static void ether1394_iso(struct hpsb_iso *iso)
+{
+ quadlet_t *data;
+ char *buf;
+ struct eth1394_host_info *hi;
+ struct net_device *dev;
+ struct eth1394_priv *priv;
+ unsigned int len;
+ u32 specifier_id;
+ u16 source_id;
+ int i;
+ int nready;
+
+ hi = hpsb_get_hostinfo(&eth1394_highlevel, iso->host);
+ if (unlikely(!hi)) {
+ ETH1394_PRINT_G(KERN_ERR, "No net device at fw-host%d\n",
+ iso->host->id);
+ return;
+ }
+
+ dev = hi->dev;
+
+ nready = hpsb_iso_n_ready(iso);
+ for (i = 0; i < nready; i++) {
+ struct hpsb_iso_packet_info *info =
+ &iso->infos[(iso->first_packet + i) % iso->buf_packets];
+ data = (quadlet_t *)(iso->data_buf.kvirt + info->offset);
+
+ /* skip over GASP header */
+ buf = (char *)data + 8;
+ len = info->len - 8;
+
+ specifier_id = (be32_to_cpu(data[0]) & 0xffff) << 8 |
+ (be32_to_cpu(data[1]) & 0xff000000) >> 24;
+ source_id = be32_to_cpu(data[0]) >> 16;
+
+ priv = netdev_priv(dev);
+
+ if (info->channel != (iso->host->csr.broadcast_channel & 0x3f)
+ || specifier_id != ETHER1394_GASP_SPECIFIER_ID) {
+ /* This packet is not for us */
+ continue;
+ }
+ ether1394_data_handler(dev, source_id, LOCAL_BUS | ALL_NODES,
+ buf, len);
+ }
+
+ hpsb_iso_recv_release_packets(iso, i);
+
+ dev->last_rx = jiffies;
+}
+
+/******************************************
+ * Datagram transmission code
+ ******************************************/
+
+/* Convert a standard ARP packet to 1394 ARP. The first 8 bytes (the entire
+ * arphdr) is the same format as the ip1394 header, so they overlap. The rest
+ * needs to be munged a bit. The remainder of the arphdr is formatted based
+ * on hwaddr len and ipaddr len. We know what they'll be, so it's easy to
+ * judge.
+ *
+ * Now that the EUI is used for the hardware address all we need to do to make
+ * this work for 1394 is to insert 2 quadlets that contain max_rec size,
+ * speed, and unicast FIFO address information between the sender_unique_id
+ * and the IP addresses.
+ */
+static void ether1394_arp_to_1394arp(struct sk_buff *skb,
+ struct net_device *dev)
+{
+ struct eth1394_priv *priv = netdev_priv(dev);
+ struct arphdr *arp = (struct arphdr *)skb->data;
+ unsigned char *arp_ptr = (unsigned char *)(arp + 1);
+ struct eth1394_arp *arp1394 = (struct eth1394_arp *)skb->data;
+
+ arp1394->hw_addr_len = 16;
+ arp1394->sip = *(u32*)(arp_ptr + ETH1394_ALEN);
+ arp1394->max_rec = priv->host->csr.max_rec;
+ arp1394->sspd = priv->host->csr.lnk_spd;
+ arp1394->fifo_hi = htons(priv->local_fifo >> 32);
+ arp1394->fifo_lo = htonl(priv->local_fifo & ~0x0);
+}
+
+/* We need to encapsulate the standard header with our own. We use the
+ * ethernet header's proto for our own. */
+static unsigned int ether1394_encapsulate_prep(unsigned int max_payload,
+ __be16 proto,
+ union eth1394_hdr *hdr,
+ u16 dg_size, u16 dgl)
+{
+ unsigned int adj_max_payload =
+ max_payload - hdr_type_len[ETH1394_HDR_LF_UF];
+
+ /* Does it all fit in one packet? */
+ if (dg_size <= adj_max_payload) {
+ hdr->uf.lf = ETH1394_HDR_LF_UF;
+ hdr->uf.ether_type = proto;
+ } else {
+ hdr->ff.lf = ETH1394_HDR_LF_FF;
+ hdr->ff.ether_type = proto;
+ hdr->ff.dg_size = dg_size - 1;
+ hdr->ff.dgl = dgl;
+ adj_max_payload = max_payload - hdr_type_len[ETH1394_HDR_LF_FF];
+ }
+ return DIV_ROUND_UP(dg_size, adj_max_payload);
+}
+
+static unsigned int ether1394_encapsulate(struct sk_buff *skb,
+ unsigned int max_payload,
+ union eth1394_hdr *hdr)
+{
+ union eth1394_hdr *bufhdr;
+ int ftype = hdr->common.lf;
+ int hdrsz = hdr_type_len[ftype];
+ unsigned int adj_max_payload = max_payload - hdrsz;
+
+ switch (ftype) {
+ case ETH1394_HDR_LF_UF:
+ bufhdr = (union eth1394_hdr *)skb_push(skb, hdrsz);
+ bufhdr->words.word1 = htons(hdr->words.word1);
+ bufhdr->words.word2 = hdr->words.word2;
+ break;
+
+ case ETH1394_HDR_LF_FF:
+ bufhdr = (union eth1394_hdr *)skb_push(skb, hdrsz);
+ bufhdr->words.word1 = htons(hdr->words.word1);
+ bufhdr->words.word2 = hdr->words.word2;
+ bufhdr->words.word3 = htons(hdr->words.word3);
+ bufhdr->words.word4 = 0;
+
+ /* Set frag type here for future interior fragments */
+ hdr->common.lf = ETH1394_HDR_LF_IF;
+ hdr->sf.fg_off = 0;
+ break;
+
+ default:
+ hdr->sf.fg_off += adj_max_payload;
+ bufhdr = (union eth1394_hdr *)skb_pull(skb, adj_max_payload);
+ if (max_payload >= skb->len)
+ hdr->common.lf = ETH1394_HDR_LF_LF;
+ bufhdr->words.word1 = htons(hdr->words.word1);
+ bufhdr->words.word2 = htons(hdr->words.word2);
+ bufhdr->words.word3 = htons(hdr->words.word3);
+ bufhdr->words.word4 = 0;
+ }
+ return min(max_payload, skb->len);
+}
+
+static struct hpsb_packet *ether1394_alloc_common_packet(struct hpsb_host *host)
+{
+ struct hpsb_packet *p;
+
+ p = hpsb_alloc_packet(0);
+ if (p) {
+ p->host = host;
+ p->generation = get_hpsb_generation(host);
+ p->type = hpsb_async;
+ }
+ return p;
+}
+
+static int ether1394_prep_write_packet(struct hpsb_packet *p,
+ struct hpsb_host *host, nodeid_t node,
+ u64 addr, void *data, int tx_len)
+{
+ p->node_id = node;
+
+ if (hpsb_get_tlabel(p))
+ return -EAGAIN;
+
+ p->tcode = TCODE_WRITEB;
+ p->header_size = 16;
+ p->expect_response = 1;
+ p->header[0] =
+ p->node_id << 16 | p->tlabel << 10 | 1 << 8 | TCODE_WRITEB << 4;
+ p->header[1] = host->node_id << 16 | addr >> 32;
+ p->header[2] = addr & 0xffffffff;
+ p->header[3] = tx_len << 16;
+ p->data_size = (tx_len + 3) & ~3;
+ p->data = data;
+
+ return 0;
+}
+
+static void ether1394_prep_gasp_packet(struct hpsb_packet *p,
+ struct eth1394_priv *priv,
+ struct sk_buff *skb, int length)
+{
+ p->header_size = 4;
+ p->tcode = TCODE_STREAM_DATA;
+
+ p->header[0] = length << 16 | 3 << 14 | priv->broadcast_channel << 8 |
+ TCODE_STREAM_DATA << 4;
+ p->data_size = length;
+ p->data = (quadlet_t *)skb->data - 2;
+ p->data[0] = cpu_to_be32(priv->host->node_id << 16 |
+ ETHER1394_GASP_SPECIFIER_ID_HI);
+ p->data[1] = cpu_to_be32(ETHER1394_GASP_SPECIFIER_ID_LO << 24 |
+ ETHER1394_GASP_VERSION);
+
+ p->speed_code = priv->bc_sspd;
+
+ /* prevent hpsb_send_packet() from overriding our speed code */
+ p->node_id = LOCAL_BUS | ALL_NODES;
+}
+
+static void ether1394_free_packet(struct hpsb_packet *packet)
+{
+ if (packet->tcode != TCODE_STREAM_DATA)
+ hpsb_free_tlabel(packet);
+ hpsb_free_packet(packet);
+}
+
+static void ether1394_complete_cb(void *__ptask);
+
+static int ether1394_send_packet(struct packet_task *ptask, unsigned int tx_len)
+{
+ struct eth1394_priv *priv = ptask->priv;
+ struct hpsb_packet *packet = NULL;
+
+ packet = ether1394_alloc_common_packet(priv->host);
+ if (!packet)
+ return -ENOMEM;
+
+ if (ptask->tx_type == ETH1394_GASP) {
+ int length = tx_len + 2 * sizeof(quadlet_t);
+
+ ether1394_prep_gasp_packet(packet, priv, ptask->skb, length);
+ } else if (ether1394_prep_write_packet(packet, priv->host,
+ ptask->dest_node,
+ ptask->addr, ptask->skb->data,
+ tx_len)) {
+ hpsb_free_packet(packet);
+ return -EAGAIN;
+ }
+
+ ptask->packet = packet;
+ hpsb_set_packet_complete_task(ptask->packet, ether1394_complete_cb,
+ ptask);
+
+ if (hpsb_send_packet(packet) < 0) {
+ ether1394_free_packet(packet);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/* Task function to be run when a datagram transmission is completed */
+static void ether1394_dg_complete(struct packet_task *ptask, int fail)
+{
+ struct sk_buff *skb = ptask->skb;
+ struct eth1394_priv *priv = netdev_priv(skb->dev);
+ unsigned long flags;
+
+ /* Statistics */
+ spin_lock_irqsave(&priv->lock, flags);
+ if (fail) {
+ priv->stats.tx_dropped++;
+ priv->stats.tx_errors++;
+ } else {
+ priv->stats.tx_bytes += skb->len;
+ priv->stats.tx_packets++;
+ }
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ dev_kfree_skb_any(skb);
+ kmem_cache_free(packet_task_cache, ptask);
+}
+
+/* Callback for when a packet has been sent and the status of that packet is
+ * known */
+static void ether1394_complete_cb(void *__ptask)
+{
+ struct packet_task *ptask = (struct packet_task *)__ptask;
+ struct hpsb_packet *packet = ptask->packet;
+ int fail = 0;
+
+ if (packet->tcode != TCODE_STREAM_DATA)
+ fail = hpsb_packet_success(packet);
+
+ ether1394_free_packet(packet);
+
+ ptask->outstanding_pkts--;
+ if (ptask->outstanding_pkts > 0 && !fail) {
+ int tx_len, err;
+
+ /* Add the encapsulation header to the fragment */
+ tx_len = ether1394_encapsulate(ptask->skb, ptask->max_payload,
+ &ptask->hdr);
+ err = ether1394_send_packet(ptask, tx_len);
+ if (err) {
+ if (err == -EAGAIN)
+ ETH1394_PRINT_G(KERN_ERR, "Out of tlabels\n");
+
+ ether1394_dg_complete(ptask, 1);
+ }
+ } else {
+ ether1394_dg_complete(ptask, fail);
+ }
+}
+
+/* Transmit a packet (called by kernel) */
+static int ether1394_tx(struct sk_buff *skb, struct net_device *dev)
+{
+ struct eth1394hdr hdr_buf;
+ struct eth1394_priv *priv = netdev_priv(dev);
+ __be16 proto;
+ unsigned long flags;
+ nodeid_t dest_node;
+ eth1394_tx_type tx_type;
+ unsigned int tx_len;
+ unsigned int max_payload;
+ u16 dg_size;
+ u16 dgl;
+ struct packet_task *ptask;
+ struct eth1394_node_ref *node;
+ struct eth1394_node_info *node_info = NULL;
+
+ ptask = kmem_cache_alloc(packet_task_cache, GFP_ATOMIC);
+ if (ptask == NULL)
+ goto fail;
+
+ /* XXX Ignore this for now. Noticed that when MacOSX is the IRM,
+ * it does not set our validity bit. We need to compensate for
+ * that somewhere else, but not in eth1394. */
+#if 0
+ if ((priv->host->csr.broadcast_channel & 0xc0000000) != 0xc0000000)
+ goto fail;
+#endif
+
+ skb = skb_share_check(skb, GFP_ATOMIC);
+ if (!skb)
+ goto fail;
+
+ /* Get rid of the fake eth1394 header, but first make a copy.
+ * We might need to rebuild the header on tx failure. */
+ memcpy(&hdr_buf, skb->data, sizeof(hdr_buf));
+ skb_pull(skb, ETH1394_HLEN);
+
+ proto = hdr_buf.h_proto;
+ dg_size = skb->len;
+
+ /* Set the transmission type for the packet. ARP packets and IP
+ * broadcast packets are sent via GASP. */
+ if (memcmp(hdr_buf.h_dest, dev->broadcast, ETH1394_ALEN) == 0 ||
+ proto == htons(ETH_P_ARP) ||
+ (proto == htons(ETH_P_IP) &&
+ IN_MULTICAST(ntohl(ip_hdr(skb)->daddr)))) {
+ tx_type = ETH1394_GASP;
+ dest_node = LOCAL_BUS | ALL_NODES;
+ max_payload = priv->bc_maxpayload - ETHER1394_GASP_OVERHEAD;
+ BUG_ON(max_payload < 512 - ETHER1394_GASP_OVERHEAD);
+ dgl = priv->bc_dgl;
+ if (max_payload < dg_size + hdr_type_len[ETH1394_HDR_LF_UF])
+ priv->bc_dgl++;
+ } else {
+ __be64 guid = get_unaligned((u64 *)hdr_buf.h_dest);
+
+ node = eth1394_find_node_guid(&priv->ip_node_list,
+ be64_to_cpu(guid));
+ if (!node)
+ goto fail;
+
+ node_info =
+ (struct eth1394_node_info *)node->ud->device.driver_data;
+ if (node_info->fifo == CSR1212_INVALID_ADDR_SPACE)
+ goto fail;
+
+ dest_node = node->ud->ne->nodeid;
+ max_payload = node_info->maxpayload;
+ BUG_ON(max_payload < 512 - ETHER1394_GASP_OVERHEAD);
+
+ dgl = node_info->dgl;
+ if (max_payload < dg_size + hdr_type_len[ETH1394_HDR_LF_UF])
+ node_info->dgl++;
+ tx_type = ETH1394_WRREQ;
+ }
+
+ /* If this is an ARP packet, convert it */
+ if (proto == htons(ETH_P_ARP))
+ ether1394_arp_to_1394arp(skb, dev);
+
+ ptask->hdr.words.word1 = 0;
+ ptask->hdr.words.word2 = 0;
+ ptask->hdr.words.word3 = 0;
+ ptask->hdr.words.word4 = 0;
+ ptask->skb = skb;
+ ptask->priv = priv;
+ ptask->tx_type = tx_type;
+
+ if (tx_type != ETH1394_GASP) {
+ u64 addr;
+
+ spin_lock_irqsave(&priv->lock, flags);
+ addr = node_info->fifo;
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ ptask->addr = addr;
+ ptask->dest_node = dest_node;
+ }
+
+ ptask->tx_type = tx_type;
+ ptask->max_payload = max_payload;
+ ptask->outstanding_pkts = ether1394_encapsulate_prep(max_payload,
+ proto, &ptask->hdr, dg_size, dgl);
+
+ /* Add the encapsulation header to the fragment */
+ tx_len = ether1394_encapsulate(skb, max_payload, &ptask->hdr);
+ dev->trans_start = jiffies;
+ if (ether1394_send_packet(ptask, tx_len)) {
+ if (dest_node == (LOCAL_BUS | ALL_NODES))
+ goto fail;
+
+ /* At this point we want to restore the packet. When we return
+ * here with NETDEV_TX_BUSY we will get another entrance in this
+ * routine with the same skb and we need it to look the same.
+ * So we pull 4 more bytes, then build the header again. */
+ skb_pull(skb, 4);
+ ether1394_header(skb, dev, ntohs(hdr_buf.h_proto),
+ hdr_buf.h_dest, NULL, 0);
+
+ /* Most failures of ether1394_send_packet are recoverable. */
+ netif_stop_queue(dev);
+ priv->wake_node = dest_node;
+ schedule_work(&priv->wake);
+ kmem_cache_free(packet_task_cache, ptask);
+ return NETDEV_TX_BUSY;
+ }
+
+ return NETDEV_TX_OK;
+fail:
+ if (ptask)
+ kmem_cache_free(packet_task_cache, ptask);
+
+ if (skb != NULL)
+ dev_kfree_skb(skb);
+
+ spin_lock_irqsave(&priv->lock, flags);
+ priv->stats.tx_dropped++;
+ priv->stats.tx_errors++;
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ /*
+ * FIXME: According to a patch from 2003-02-26, "returning non-zero
+ * causes serious problems" here, allegedly. Before that patch,
+ * -ERRNO was returned which is not appropriate under Linux 2.6.
+ * Perhaps more needs to be done? Stop the queue in serious
+ * conditions and restart it elsewhere?
+ */
+ /* return NETDEV_TX_BUSY; */
+ return NETDEV_TX_OK;
+}
+
+static void ether1394_get_drvinfo(struct net_device *dev,
+ struct ethtool_drvinfo *info)
+{
+ strcpy(info->driver, driver_name);
+ strcpy(info->bus_info, "ieee1394"); /* FIXME provide more detail? */
+}
+
+static struct ethtool_ops ethtool_ops = {
+ .get_drvinfo = ether1394_get_drvinfo
+};
+
+static int __init ether1394_init_module(void)
+{
+ int err;
+
+ packet_task_cache = kmem_cache_create("packet_task",
+ sizeof(struct packet_task),
+ 0, 0, NULL);
+ if (!packet_task_cache)
+ return -ENOMEM;
+
+ hpsb_register_highlevel(&eth1394_highlevel);
+ err = hpsb_register_protocol(&eth1394_proto_driver);
+ if (err) {
+ hpsb_unregister_highlevel(&eth1394_highlevel);
+ kmem_cache_destroy(packet_task_cache);
+ }
+ return err;
+}
+
+static void __exit ether1394_exit_module(void)
+{
+ hpsb_unregister_protocol(&eth1394_proto_driver);
+ hpsb_unregister_highlevel(&eth1394_highlevel);
+ kmem_cache_destroy(packet_task_cache);
+}
+
+module_init(ether1394_init_module);
+module_exit(ether1394_exit_module);
diff --git a/drivers/ieee1394/eth1394.h b/drivers/ieee1394/eth1394.h
new file mode 100644
index 0000000..4f3e2dd
--- /dev/null
+++ b/drivers/ieee1394/eth1394.h
@@ -0,0 +1,235 @@
+/*
+ * eth1394.h -- Ethernet driver for Linux IEEE-1394 Subsystem
+ *
+ * Copyright (C) 2000 Bonin Franck <boninf@free.fr>
+ * (C) 2001 Ben Collins <bcollins@debian.org>
+ *
+ * Mainly based on work by Emanuel Pirker and Andreas E. Bombe
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __ETH1394_H
+#define __ETH1394_H
+
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <asm/byteorder.h>
+
+#include "ieee1394.h"
+#include "ieee1394_types.h"
+
+/* Register for incoming packets. This is 4096 bytes, which supports up to
+ * S3200 (per Table 16-3 of IEEE 1394b-2002). */
+#define ETHER1394_REGION_ADDR_LEN 4096
+
+/* GASP identifier numbers for IPv4 over IEEE 1394 */
+#define ETHER1394_GASP_SPECIFIER_ID 0x00005E
+#define ETHER1394_GASP_SPECIFIER_ID_HI ((0x00005E >> 8) & 0xffff)
+#define ETHER1394_GASP_SPECIFIER_ID_LO (0x00005E & 0xff)
+#define ETHER1394_GASP_VERSION 1
+
+#define ETHER1394_GASP_OVERHEAD (2 * sizeof(quadlet_t)) /* for GASP header */
+
+#define ETHER1394_GASP_BUFFERS 16
+
+#define NODE_SET (ALL_NODES + 1) /* Node set == 64 */
+
+enum eth1394_bc_states { ETHER1394_BC_ERROR,
+ ETHER1394_BC_RUNNING,
+ ETHER1394_BC_STOPPED };
+
+
+/* Private structure for our ethernet driver */
+struct eth1394_priv {
+ struct net_device_stats stats; /* Device stats */
+ struct hpsb_host *host; /* The card for this dev */
+ u16 bc_maxpayload; /* Max broadcast payload */
+ u8 bc_sspd; /* Max broadcast speed */
+ u64 local_fifo; /* Local FIFO Address */
+ spinlock_t lock; /* Private lock */
+ int broadcast_channel; /* Async stream Broadcast Channel */
+ enum eth1394_bc_states bc_state; /* broadcast channel state */
+ struct hpsb_iso *iso; /* Async stream recv handle */
+ int bc_dgl; /* Outgoing broadcast datagram label */
+ struct list_head ip_node_list; /* List of IP capable nodes */
+ struct unit_directory *ud_list[ALL_NODES]; /* Cached unit dir list */
+
+ struct work_struct wake; /* Wake up after xmit failure */
+ struct net_device *wake_dev; /* Stupid backlink for .wake */
+ nodeid_t wake_node; /* Destination of failed xmit */
+};
+
+
+/* Define a fake hardware header format for the networking core. Note that
+ * header size cannot exceed 16 bytes as that is the size of the header cache.
+ * Also, we do not need the source address in the header so we omit it and
+ * keep the header to under 16 bytes */
+#define ETH1394_ALEN (8)
+#define ETH1394_HLEN (10)
+
+struct eth1394hdr {
+ unsigned char h_dest[ETH1394_ALEN]; /* destination eth1394 addr */
+ unsigned short h_proto; /* packet type ID field */
+} __attribute__((packed));
+
+static inline struct eth1394hdr *eth1394_hdr(const struct sk_buff *skb)
+{
+ return (struct eth1394hdr *)skb_mac_header(skb);
+}
+
+typedef enum {ETH1394_GASP, ETH1394_WRREQ} eth1394_tx_type;
+
+/* IP1394 headers */
+
+/* Unfragmented */
+#if defined __BIG_ENDIAN_BITFIELD
+struct eth1394_uf_hdr {
+ u16 lf:2;
+ u16 res:14;
+ u16 ether_type; /* Ethernet packet type */
+} __attribute__((packed));
+#elif defined __LITTLE_ENDIAN_BITFIELD
+struct eth1394_uf_hdr {
+ u16 res:14;
+ u16 lf:2;
+ u16 ether_type;
+} __attribute__((packed));
+#else
+#error Unknown bit field type
+#endif
+
+/* First fragment */
+#if defined __BIG_ENDIAN_BITFIELD
+struct eth1394_ff_hdr {
+ u16 lf:2;
+ u16 res1:2;
+ u16 dg_size:12; /* Datagram size */
+ u16 ether_type; /* Ethernet packet type */
+ u16 dgl; /* Datagram label */
+ u16 res2;
+} __attribute__((packed));
+#elif defined __LITTLE_ENDIAN_BITFIELD
+struct eth1394_ff_hdr {
+ u16 dg_size:12;
+ u16 res1:2;
+ u16 lf:2;
+ u16 ether_type;
+ u16 dgl;
+ u16 res2;
+} __attribute__((packed));
+#else
+#error Unknown bit field type
+#endif
+
+/* XXX: Subsequent fragments, including last */
+#if defined __BIG_ENDIAN_BITFIELD
+struct eth1394_sf_hdr {
+ u16 lf:2;
+ u16 res1:2;
+ u16 dg_size:12; /* Datagram size */
+ u16 res2:4;
+ u16 fg_off:12; /* Fragment offset */
+ u16 dgl; /* Datagram label */
+ u16 res3;
+} __attribute__((packed));
+#elif defined __LITTLE_ENDIAN_BITFIELD
+struct eth1394_sf_hdr {
+ u16 dg_size:12;
+ u16 res1:2;
+ u16 lf:2;
+ u16 fg_off:12;
+ u16 res2:4;
+ u16 dgl;
+ u16 res3;
+} __attribute__((packed));
+#else
+#error Unknown bit field type
+#endif
+
+#if defined __BIG_ENDIAN_BITFIELD
+struct eth1394_common_hdr {
+ u16 lf:2;
+ u16 pad1:14;
+} __attribute__((packed));
+#elif defined __LITTLE_ENDIAN_BITFIELD
+struct eth1394_common_hdr {
+ u16 pad1:14;
+ u16 lf:2;
+} __attribute__((packed));
+#else
+#error Unknown bit field type
+#endif
+
+struct eth1394_hdr_words {
+ u16 word1;
+ u16 word2;
+ u16 word3;
+ u16 word4;
+};
+
+union eth1394_hdr {
+ struct eth1394_common_hdr common;
+ struct eth1394_uf_hdr uf;
+ struct eth1394_ff_hdr ff;
+ struct eth1394_sf_hdr sf;
+ struct eth1394_hdr_words words;
+};
+
+/* End of IP1394 headers */
+
+/* Fragment types */
+#define ETH1394_HDR_LF_UF 0 /* unfragmented */
+#define ETH1394_HDR_LF_FF 1 /* first fragment */
+#define ETH1394_HDR_LF_LF 2 /* last fragment */
+#define ETH1394_HDR_LF_IF 3 /* interior fragment */
+
+#define IP1394_HW_ADDR_LEN 16 /* As per RFC */
+
+/* Our arp packet (ARPHRD_IEEE1394) */
+struct eth1394_arp {
+ u16 hw_type; /* 0x0018 */
+ u16 proto_type; /* 0x0806 */
+ u8 hw_addr_len; /* 16 */
+ u8 ip_addr_len; /* 4 */
+ u16 opcode; /* ARP Opcode */
+ /* Above is exactly the same format as struct arphdr */
+
+ u64 s_uniq_id; /* Sender's 64bit EUI */
+ u8 max_rec; /* Sender's max packet size */
+ u8 sspd; /* Sender's max speed */
+ u16 fifo_hi; /* hi 16bits of sender's FIFO addr */
+ u32 fifo_lo; /* lo 32bits of sender's FIFO addr */
+ u32 sip; /* Sender's IP Address */
+ u32 tip; /* IP Address of requested hw addr */
+};
+
+/* Network timeout */
+#define ETHER1394_TIMEOUT 100000
+
+/* This is our task struct. It's used for the packet complete callback. */
+struct packet_task {
+ struct sk_buff *skb;
+ int outstanding_pkts;
+ eth1394_tx_type tx_type;
+ int max_payload;
+ struct hpsb_packet *packet;
+ struct eth1394_priv *priv;
+ union eth1394_hdr hdr;
+ u64 addr;
+ u16 dest_node;
+};
+
+#endif /* __ETH1394_H */
diff --git a/drivers/ieee1394/highlevel.c b/drivers/ieee1394/highlevel.c
new file mode 100644
index 0000000..272543a
--- /dev/null
+++ b/drivers/ieee1394/highlevel.c
@@ -0,0 +1,690 @@
+/*
+ * IEEE 1394 for Linux
+ *
+ * Copyright (C) 1999 Andreas E. Bombe
+ *
+ * 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/slab.h>
+#include <linux/list.h>
+#include <linux/bitops.h>
+
+#include "ieee1394.h"
+#include "ieee1394_types.h"
+#include "hosts.h"
+#include "ieee1394_core.h"
+#include "highlevel.h"
+#include "nodemgr.h"
+
+
+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 DECLARE_RWSEM(hl_drivers_sem);
+
+static LIST_HEAD(hl_irqs);
+static DEFINE_RWLOCK(hl_irqs_lock);
+
+static DEFINE_RWLOCK(addr_space_lock);
+
+
+static struct hl_host_info *hl_get_hostinfo(struct hpsb_highlevel *hl,
+ struct hpsb_host *host)
+{
+ struct hl_host_info *hi = NULL;
+
+ if (!hl || !host)
+ return NULL;
+
+ read_lock(&hl->host_info_lock);
+ list_for_each_entry(hi, &hl->host_info_list, list) {
+ if (hi->host == host) {
+ read_unlock(&hl->host_info_lock);
+ return hi;
+ }
+ }
+ read_unlock(&hl->host_info_lock);
+ return NULL;
+}
+
+/**
+ * hpsb_get_hostinfo - retrieve a hostinfo pointer bound to this driver/host
+ *
+ * Returns a per @host and @hl 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);
+
+ return hi ? hi->data : NULL;
+}
+
+/**
+ * hpsb_create_hostinfo - allocate a hostinfo pointer bound to this driver/host
+ *
+ * Allocate a hostinfo pointer backed by memory with @data_size and bind it to
+ * to this @hl driver and @host. If @data_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 = kzalloc(sizeof(*hi) + data_size, GFP_ATOMIC);
+ if (!hi)
+ return NULL;
+
+ 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;
+}
+
+/**
+ * hpsb_set_hostinfo - set the hostinfo pointer to something useful
+ *
+ * Usually follows a call to hpsb_create_hostinfo, where the size is 0.
+ */
+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;
+}
+
+/**
+ * hpsb_destroy_hostinfo - free and remove a hostinfo pointer
+ *
+ * Free and remove the hostinfo pointer bound to this @hl driver and @host.
+ */
+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;
+}
+
+/**
+ * hpsb_set_hostinfo_key - set an alternate lookup key for an hostinfo
+ *
+ * Sets an alternate lookup key for the hostinfo bound to this @hl driver and
+ * @host.
+ */
+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;
+}
+
+/**
+ * hpsb_get_hostinfo_bykey - retrieve a hostinfo pointer by its alternate key
+ */
+void *hpsb_get_hostinfo_bykey(struct hpsb_highlevel *hl, unsigned long key)
+{
+ struct hl_host_info *hi;
+ void *data = NULL;
+
+ if (!hl)
+ return NULL;
+
+ read_lock(&hl->host_info_lock);
+ list_for_each_entry(hi, &hl->host_info_list, list) {
+ if (hi->key == key) {
+ data = hi->data;
+ break;
+ }
+ }
+ read_unlock(&hl->host_info_lock);
+ return data;
+}
+
+static int highlevel_for_each_host_reg(struct hpsb_host *host, void *__data)
+{
+ struct hpsb_highlevel *hl = __data;
+
+ hl->add_host(host);
+
+ if (host->update_config_rom && hpsb_update_config_rom_image(host) < 0)
+ HPSB_ERR("Failed to generate Configuration ROM image for host "
+ "%s-%d", hl->name, host->id);
+ return 0;
+}
+
+/**
+ * hpsb_register_highlevel - register highlevel driver
+ *
+ * The name pointer in @hl has to stay valid at all times because the string is
+ * not copied.
+ */
+void hpsb_register_highlevel(struct hpsb_highlevel *hl)
+{
+ unsigned long flags;
+
+ hpsb_init_highlevel(hl);
+ INIT_LIST_HEAD(&hl->addr_list);
+
+ down_write(&hl_drivers_sem);
+ list_add_tail(&hl->hl_list, &hl_drivers);
+ up_write(&hl_drivers_sem);
+
+ write_lock_irqsave(&hl_irqs_lock, flags);
+ list_add_tail(&hl->irq_list, &hl_irqs);
+ write_unlock_irqrestore(&hl_irqs_lock, flags);
+
+ if (hl->add_host)
+ nodemgr_for_each_host(hl, highlevel_for_each_host_reg);
+ return;
+}
+
+static void __delete_addr(struct hpsb_address_serve *as)
+{
+ list_del(&as->host_list);
+ list_del(&as->hl_list);
+ kfree(as);
+}
+
+static void __unregister_host(struct hpsb_highlevel *hl, struct hpsb_host *host,
+ int update_cr)
+{
+ unsigned long flags;
+ struct list_head *lh, *next;
+ struct hpsb_address_serve *as;
+
+ /* First, let the highlevel driver unreg */
+ if (hl->remove_host)
+ hl->remove_host(host);
+
+ /* Remove any addresses that are matched for this highlevel driver
+ * and this particular host. */
+ write_lock_irqsave(&addr_space_lock, flags);
+ list_for_each_safe (lh, next, &hl->addr_list) {
+ as = list_entry(lh, struct hpsb_address_serve, hl_list);
+ if (as->host == host)
+ __delete_addr(as);
+ }
+ write_unlock_irqrestore(&addr_space_lock, flags);
+
+ /* Now update the config-rom to reflect anything removed by the
+ * highlevel driver. */
+ if (update_cr && host->update_config_rom &&
+ hpsb_update_config_rom_image(host) < 0)
+ HPSB_ERR("Failed to generate Configuration ROM image for host "
+ "%s-%d", hl->name, host->id);
+
+ /* Finally remove all the host info associated between these two. */
+ hpsb_destroy_hostinfo(hl, host);
+}
+
+static int highlevel_for_each_host_unreg(struct hpsb_host *host, void *__data)
+{
+ struct hpsb_highlevel *hl = __data;
+
+ __unregister_host(hl, host, 1);
+ return 0;
+}
+
+/**
+ * hpsb_unregister_highlevel - unregister highlevel driver
+ */
+void hpsb_unregister_highlevel(struct hpsb_highlevel *hl)
+{
+ unsigned long flags;
+
+ write_lock_irqsave(&hl_irqs_lock, flags);
+ list_del(&hl->irq_list);
+ write_unlock_irqrestore(&hl_irqs_lock, flags);
+
+ down_write(&hl_drivers_sem);
+ list_del(&hl->hl_list);
+ up_write(&hl_drivers_sem);
+
+ nodemgr_for_each_host(hl, highlevel_for_each_host_unreg);
+}
+
+/**
+ * hpsb_allocate_and_register_addrspace - alloc' and reg' a host address space
+ *
+ * @start and @end are 48 bit pointers and have to be quadlet aligned.
+ * @end points to the first address behind the handled addresses. This
+ * function can be called multiple times for a single hpsb_highlevel @hl to
+ * implement sparse register sets. The requested region must not overlap any
+ * previously allocated region, otherwise registering will fail.
+ *
+ * It returns true for successful allocation. Address spaces can be
+ * unregistered with hpsb_unregister_addrspace. All remaining address spaces
+ * are automatically deallocated together with the hpsb_highlevel @hl.
+ */
+u64 hpsb_allocate_and_register_addrspace(struct hpsb_highlevel *hl,
+ struct hpsb_host *host,
+ struct hpsb_address_ops *ops,
+ u64 size, u64 alignment,
+ u64 start, u64 end)
+{
+ struct hpsb_address_serve *as, *a1, *a2;
+ struct list_head *entry;
+ u64 retval = CSR1212_INVALID_ADDR_SPACE;
+ unsigned long flags;
+ u64 align_mask = ~(alignment - 1);
+
+ if ((alignment & 3) || (alignment > 0x800000000000ULL) ||
+ (hweight64(alignment) != 1)) {
+ HPSB_ERR("%s called with invalid alignment: 0x%048llx",
+ __func__, (unsigned long long)alignment);
+ return retval;
+ }
+
+ /* default range,
+ * avoids controller's posted write area (see OHCI 1.1 clause 1.5) */
+ if (start == CSR1212_INVALID_ADDR_SPACE &&
+ end == CSR1212_INVALID_ADDR_SPACE) {
+ start = host->middle_addr_space;
+ end = CSR1212_ALL_SPACE_END;
+ }
+
+ if (((start|end) & ~align_mask) || (start >= end) ||
+ (end > CSR1212_ALL_SPACE_END)) {
+ HPSB_ERR("%s called with invalid addresses "
+ "(start = %012Lx end = %012Lx)", __func__,
+ (unsigned long long)start,(unsigned long long)end);
+ return retval;
+ }
+
+ as = kmalloc(sizeof(*as), GFP_KERNEL);
+ if (!as)
+ return retval;
+
+ INIT_LIST_HEAD(&as->host_list);
+ INIT_LIST_HEAD(&as->hl_list);
+ as->op = ops;
+ as->host = host;
+
+ write_lock_irqsave(&addr_space_lock, flags);
+ list_for_each(entry, &host->addr_space) {
+ u64 a1sa, a1ea;
+ u64 a2sa, a2ea;
+
+ a1 = list_entry(entry, struct hpsb_address_serve, host_list);
+ a2 = list_entry(entry->next, struct hpsb_address_serve,
+ host_list);
+
+ a1sa = a1->start & align_mask;
+ a1ea = (a1->end + alignment -1) & align_mask;
+ a2sa = a2->start & align_mask;
+ a2ea = (a2->end + alignment -1) & align_mask;
+
+ if ((a2sa - a1ea >= size) && (a2sa - start >= size) &&
+ (a2sa > start)) {
+ as->start = max(start, a1ea);
+ as->end = as->start + size;
+ list_add(&as->host_list, entry);
+ list_add_tail(&as->hl_list, &hl->addr_list);
+ retval = as->start;
+ break;
+ }
+ }
+ write_unlock_irqrestore(&addr_space_lock, flags);
+
+ if (retval == CSR1212_INVALID_ADDR_SPACE)
+ kfree(as);
+ return retval;
+}
+
+/**
+ * hpsb_register_addrspace - register a host address space
+ *
+ * @start and @end are 48 bit pointers and have to be quadlet aligned.
+ * @end points to the first address behind the handled addresses. This
+ * function can be called multiple times for a single hpsb_highlevel @hl to
+ * implement sparse register sets. The requested region must not overlap any
+ * previously allocated region, otherwise registering will fail.
+ *
+ * It returns true for successful allocation. Address spaces can be
+ * unregistered with hpsb_unregister_addrspace. All remaining address spaces
+ * are automatically deallocated together with the hpsb_highlevel @hl.
+ */
+int hpsb_register_addrspace(struct hpsb_highlevel *hl, struct hpsb_host *host,
+ struct hpsb_address_ops *ops, u64 start, u64 end)
+{
+ struct hpsb_address_serve *as;
+ struct list_head *lh;
+ int retval = 0;
+ unsigned long flags;
+
+ if (((start|end) & 3) || (start >= end) ||
+ (end > CSR1212_ALL_SPACE_END)) {
+ HPSB_ERR("%s called with invalid addresses", __func__);
+ return 0;
+ }
+
+ as = kmalloc(sizeof(*as), GFP_ATOMIC);
+ if (!as)
+ return 0;
+
+ INIT_LIST_HEAD(&as->host_list);
+ INIT_LIST_HEAD(&as->hl_list);
+ as->op = ops;
+ as->start = start;
+ as->end = end;
+ as->host = host;
+
+ write_lock_irqsave(&addr_space_lock, flags);
+ list_for_each(lh, &host->addr_space) {
+ struct hpsb_address_serve *as_this =
+ list_entry(lh, struct hpsb_address_serve, host_list);
+ struct hpsb_address_serve *as_next =
+ list_entry(lh->next, struct hpsb_address_serve,
+ host_list);
+
+ if (as_this->end > as->start)
+ break;
+
+ if (as_next->start >= as->end) {
+ list_add(&as->host_list, lh);
+ list_add_tail(&as->hl_list, &hl->addr_list);
+ retval = 1;
+ break;
+ }
+ }
+ write_unlock_irqrestore(&addr_space_lock, flags);
+
+ if (retval == 0)
+ kfree(as);
+ return retval;
+}
+
+int hpsb_unregister_addrspace(struct hpsb_highlevel *hl, struct hpsb_host *host,
+ u64 start)
+{
+ int retval = 0;
+ struct hpsb_address_serve *as;
+ struct list_head *lh, *next;
+ unsigned long flags;
+
+ write_lock_irqsave(&addr_space_lock, flags);
+ list_for_each_safe (lh, next, &hl->addr_list) {
+ as = list_entry(lh, struct hpsb_address_serve, hl_list);
+ if (as->start == start && as->host == host) {
+ __delete_addr(as);
+ retval = 1;
+ break;
+ }
+ }
+ write_unlock_irqrestore(&addr_space_lock, flags);
+ return retval;
+}
+
+static struct hpsb_address_ops dummy_ops;
+
+/* dummy address spaces as lower and upper bounds of the host's a.s. list */
+static void init_hpsb_highlevel(struct hpsb_host *host)
+{
+ INIT_LIST_HEAD(&host->dummy_zero_addr.host_list);
+ INIT_LIST_HEAD(&host->dummy_zero_addr.hl_list);
+ INIT_LIST_HEAD(&host->dummy_max_addr.host_list);
+ INIT_LIST_HEAD(&host->dummy_max_addr.hl_list);
+
+ host->dummy_zero_addr.op = host->dummy_max_addr.op = &dummy_ops;
+
+ host->dummy_zero_addr.start = host->dummy_zero_addr.end = 0;
+ host->dummy_max_addr.start = host->dummy_max_addr.end = ((u64) 1) << 48;
+
+ list_add_tail(&host->dummy_zero_addr.host_list, &host->addr_space);
+ list_add_tail(&host->dummy_max_addr.host_list, &host->addr_space);
+}
+
+void highlevel_add_host(struct hpsb_host *host)
+{
+ struct hpsb_highlevel *hl;
+
+ init_hpsb_highlevel(host);
+
+ down_read(&hl_drivers_sem);
+ list_for_each_entry(hl, &hl_drivers, hl_list) {
+ if (hl->add_host)
+ hl->add_host(host);
+ }
+ up_read(&hl_drivers_sem);
+ if (host->update_config_rom && hpsb_update_config_rom_image(host) < 0)
+ HPSB_ERR("Failed to generate Configuration ROM image for host "
+ "%s-%d", hl->name, host->id);
+}
+
+void highlevel_remove_host(struct hpsb_host *host)
+{
+ struct hpsb_highlevel *hl;
+
+ down_read(&hl_drivers_sem);
+ list_for_each_entry(hl, &hl_drivers, hl_list)
+ __unregister_host(hl, host, 0);
+ up_read(&hl_drivers_sem);
+}
+
+void highlevel_host_reset(struct hpsb_host *host)
+{
+ unsigned long flags;
+ struct hpsb_highlevel *hl;
+
+ read_lock_irqsave(&hl_irqs_lock, flags);
+ list_for_each_entry(hl, &hl_irqs, irq_list) {
+ if (hl->host_reset)
+ hl->host_reset(host);
+ }
+ read_unlock_irqrestore(&hl_irqs_lock, flags);
+}
+
+void highlevel_fcp_request(struct hpsb_host *host, int nodeid, int direction,
+ void *data, size_t length)
+{
+ unsigned long flags;
+ struct hpsb_highlevel *hl;
+ int cts = ((quadlet_t *)data)[0] >> 4;
+
+ read_lock_irqsave(&hl_irqs_lock, flags);
+ list_for_each_entry(hl, &hl_irqs, irq_list) {
+ if (hl->fcp_request)
+ hl->fcp_request(host, nodeid, direction, cts, data,
+ length);
+ }
+ read_unlock_irqrestore(&hl_irqs_lock, flags);
+}
+
+/*
+ * highlevel_read, highlevel_write, highlevel_lock, highlevel_lock64:
+ *
+ * These functions are called to handle transactions. They are called when a
+ * packet arrives. The flags argument contains the second word of the first
+ * header quadlet of the incoming packet (containing transaction label, retry
+ * code, transaction code and priority). These functions either return a
+ * response code or a negative number. In the first case a response will be
+ * generated. In the latter case, no response will be sent and the driver which
+ * handled the request will send the response itself.
+ */
+int highlevel_read(struct hpsb_host *host, int nodeid, void *data, u64 addr,
+ unsigned int length, u16 flags)
+{
+ struct hpsb_address_serve *as;
+ unsigned int partlength;
+ int rcode = RCODE_ADDRESS_ERROR;
+
+ read_lock(&addr_space_lock);
+ list_for_each_entry(as, &host->addr_space, host_list) {
+ if (as->start > addr)
+ break;
+
+ if (as->end > addr) {
+ partlength = min(as->end - addr, (u64) length);
+
+ if (as->op->read)
+ rcode = as->op->read(host, nodeid, data,
+ addr, partlength, flags);
+ else
+ rcode = RCODE_TYPE_ERROR;
+
+ data += partlength;
+ length -= partlength;
+ addr += partlength;
+
+ if ((rcode != RCODE_COMPLETE) || !length)
+ break;
+ }
+ }
+ read_unlock(&addr_space_lock);
+
+ if (length && (rcode == RCODE_COMPLETE))
+ rcode = RCODE_ADDRESS_ERROR;
+ return rcode;
+}
+
+int highlevel_write(struct hpsb_host *host, int nodeid, int destid, void *data,
+ u64 addr, unsigned int length, u16 flags)
+{
+ struct hpsb_address_serve *as;
+ unsigned int partlength;
+ int rcode = RCODE_ADDRESS_ERROR;
+
+ read_lock(&addr_space_lock);
+ list_for_each_entry(as, &host->addr_space, host_list) {
+ if (as->start > addr)
+ break;
+
+ if (as->end > addr) {
+ partlength = min(as->end - addr, (u64) length);
+
+ if (as->op->write)
+ rcode = as->op->write(host, nodeid, destid,
+ data, addr, partlength,
+ flags);
+ else
+ rcode = RCODE_TYPE_ERROR;
+
+ data += partlength;
+ length -= partlength;
+ addr += partlength;
+
+ if ((rcode != RCODE_COMPLETE) || !length)
+ break;
+ }
+ }
+ read_unlock(&addr_space_lock);
+
+ if (length && (rcode == RCODE_COMPLETE))
+ rcode = RCODE_ADDRESS_ERROR;
+ return rcode;
+}
+
+int highlevel_lock(struct hpsb_host *host, int nodeid, quadlet_t *store,
+ u64 addr, quadlet_t data, quadlet_t arg, int ext_tcode,
+ u16 flags)
+{
+ struct hpsb_address_serve *as;
+ int rcode = RCODE_ADDRESS_ERROR;
+
+ read_lock(&addr_space_lock);
+ list_for_each_entry(as, &host->addr_space, host_list) {
+ if (as->start > addr)
+ break;
+
+ if (as->end > addr) {
+ if (as->op->lock)
+ rcode = as->op->lock(host, nodeid, store, addr,
+ data, arg, ext_tcode,
+ flags);
+ else
+ rcode = RCODE_TYPE_ERROR;
+ break;
+ }
+ }
+ read_unlock(&addr_space_lock);
+ return rcode;
+}
+
+int highlevel_lock64(struct hpsb_host *host, int nodeid, octlet_t *store,
+ u64 addr, octlet_t data, octlet_t arg, int ext_tcode,
+ u16 flags)
+{
+ struct hpsb_address_serve *as;
+ int rcode = RCODE_ADDRESS_ERROR;
+
+ read_lock(&addr_space_lock);
+
+ list_for_each_entry(as, &host->addr_space, host_list) {
+ if (as->start > addr)
+ break;
+
+ if (as->end > addr) {
+ if (as->op->lock64)
+ rcode = as->op->lock64(host, nodeid, store,
+ addr, data, arg,
+ ext_tcode, flags);
+ else
+ rcode = RCODE_TYPE_ERROR;
+ break;
+ }
+ }
+ read_unlock(&addr_space_lock);
+ return rcode;
+}
diff --git a/drivers/ieee1394/highlevel.h b/drivers/ieee1394/highlevel.h
new file mode 100644
index 0000000..bc5d085
--- /dev/null
+++ b/drivers/ieee1394/highlevel.h
@@ -0,0 +1,140 @@
+#ifndef IEEE1394_HIGHLEVEL_H
+#define IEEE1394_HIGHLEVEL_H
+
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+
+struct module;
+
+#include "ieee1394_types.h"
+
+struct hpsb_host;
+
+/* internal to ieee1394 core */
+struct hpsb_address_serve {
+ struct list_head host_list; /* per host list */
+ struct list_head hl_list; /* hpsb_highlevel list */
+ struct hpsb_address_ops *op;
+ struct hpsb_host *host;
+ u64 start; /* first address handled, quadlet aligned */
+ u64 end; /* first address behind, quadlet aligned */
+};
+
+/* Only the following structures are of interest to actual highlevel drivers. */
+
+struct hpsb_highlevel {
+ const char *name;
+
+ /* Any of the following pointers can legally be NULL. */
+
+ /* New host initialized. Will also be called during
+ * hpsb_register_highlevel for all hosts already installed. */
+ void (*add_host)(struct hpsb_host *host);
+
+ /* Host about to be removed. Will also be called during
+ * hpsb_unregister_highlevel once for each host. */
+ void (*remove_host)(struct hpsb_host *host);
+
+ /* Host experienced bus reset with possible configuration changes.
+ * Note that this one may occur during interrupt/bottom half handling.
+ * You can not expect to be able to do stock hpsb_reads. */
+ void (*host_reset)(struct hpsb_host *host);
+
+ /* A write request was received on either the FCP_COMMAND (direction =
+ * 0) or the FCP_RESPONSE (direction = 1) register. The cts arg
+ * contains the cts field (first byte of data). */
+ void (*fcp_request)(struct hpsb_host *host, int nodeid, int direction,
+ int cts, u8 *data, size_t length);
+
+ /* These are initialized by the subsystem when the
+ * hpsb_higlevel is registered. */
+ struct list_head hl_list;
+ struct list_head irq_list;
+ struct list_head addr_list;
+
+ struct list_head host_info_list;
+ rwlock_t host_info_lock;
+};
+
+struct hpsb_address_ops {
+ /*
+ * Null function pointers will make the respective operation complete
+ * with RCODE_TYPE_ERROR. Makes for easy to implement read-only
+ * registers (just leave everything but read NULL).
+ *
+ * All functions shall return appropriate IEEE 1394 rcodes.
+ */
+
+ /* These functions have to implement block reads for themselves.
+ *
+ * These functions either return a response code or a negative number.
+ * In the first case a response will be generated. In the latter case,
+ * no response will be sent and the driver which handled the request
+ * will send the response itself. */
+ int (*read)(struct hpsb_host *host, int nodeid, quadlet_t *buffer,
+ u64 addr, size_t length, u16 flags);
+ int (*write)(struct hpsb_host *host, int nodeid, int destid,
+ quadlet_t *data, u64 addr, size_t length, u16 flags);
+
+ /* Lock transactions: write results of ext_tcode operation into
+ * *store. */
+ int (*lock)(struct hpsb_host *host, int nodeid, quadlet_t *store,
+ u64 addr, quadlet_t data, quadlet_t arg, int ext_tcode,
+ u16 flags);
+ int (*lock64)(struct hpsb_host *host, int nodeid, octlet_t *store,
+ u64 addr, octlet_t data, octlet_t arg, int ext_tcode,
+ u16 flags);
+};
+
+void highlevel_add_host(struct hpsb_host *host);
+void highlevel_remove_host(struct hpsb_host *host);
+void highlevel_host_reset(struct hpsb_host *host);
+int highlevel_read(struct hpsb_host *host, int nodeid, void *data, u64 addr,
+ unsigned int length, u16 flags);
+int highlevel_write(struct hpsb_host *host, int nodeid, int destid, void *data,
+ u64 addr, unsigned int length, u16 flags);
+int highlevel_lock(struct hpsb_host *host, int nodeid, quadlet_t *store,
+ u64 addr, quadlet_t data, quadlet_t arg, int ext_tcode,
+ u16 flags);
+int highlevel_lock64(struct hpsb_host *host, int nodeid, octlet_t *store,
+ u64 addr, octlet_t data, octlet_t arg, int ext_tcode,
+ u16 flags);
+void highlevel_fcp_request(struct hpsb_host *host, int nodeid, int direction,
+ void *data, size_t length);
+
+/**
+ * hpsb_init_highlevel - initialize a struct hpsb_highlevel
+ *
+ * This is only necessary if hpsb_get_hostinfo_bykey can be called
+ * before hpsb_register_highlevel.
+ */
+static inline void hpsb_init_highlevel(struct hpsb_highlevel *hl)
+{
+ rwlock_init(&hl->host_info_lock);
+ INIT_LIST_HEAD(&hl->host_info_list);
+}
+void hpsb_register_highlevel(struct hpsb_highlevel *hl);
+void hpsb_unregister_highlevel(struct hpsb_highlevel *hl);
+
+u64 hpsb_allocate_and_register_addrspace(struct hpsb_highlevel *hl,
+ struct hpsb_host *host,
+ struct hpsb_address_ops *ops,
+ u64 size, u64 alignment,
+ u64 start, u64 end);
+int hpsb_register_addrspace(struct hpsb_highlevel *hl, struct hpsb_host *host,
+ struct hpsb_address_ops *ops, u64 start, u64 end);
+int hpsb_unregister_addrspace(struct hpsb_highlevel *hl, struct hpsb_host *host,
+ u64 start);
+
+void *hpsb_get_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host);
+void *hpsb_create_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host,
+ size_t data_size);
+void hpsb_destroy_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host);
+void hpsb_set_hostinfo_key(struct hpsb_highlevel *hl, struct hpsb_host *host,
+ unsigned long key);
+void *hpsb_get_hostinfo_bykey(struct hpsb_highlevel *hl, unsigned long key);
+int hpsb_set_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host,
+ void *data);
+
+#endif /* IEEE1394_HIGHLEVEL_H */
diff --git a/drivers/ieee1394/hosts.c b/drivers/ieee1394/hosts.c
new file mode 100644
index 0000000..237d0c9
--- /dev/null
+++ b/drivers/ieee1394/hosts.c
@@ -0,0 +1,249 @@
+/*
+ * IEEE 1394 for Linux
+ *
+ * Low level (host adapter) management.
+ *
+ * Copyright (C) 1999 Andreas E. Bombe
+ * Copyright (C) 1999 Emanuel Pirker
+ *
+ * This code is licensed under the GPL. See the file COPYING in the root
+ * directory of the kernel sources for details.
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/timer.h>
+#include <linux/jiffies.h>
+#include <linux/mutex.h>
+
+#include "csr1212.h"
+#include "ieee1394.h"
+#include "ieee1394_types.h"
+#include "hosts.h"
+#include "ieee1394_core.h"
+#include "highlevel.h"
+#include "nodemgr.h"
+#include "csr.h"
+#include "config_roms.h"
+
+
+static void delayed_reset_bus(struct work_struct *work)
+{
+ struct hpsb_host *host =
+ container_of(work, struct hpsb_host, delayed_reset.work);
+ int generation = host->csr.generation + 1;
+
+ /* The generation field rolls over to 2 rather than 0 per IEEE
+ * 1394a-2000. */
+ if (generation > 0xf || generation < 2)
+ generation = 2;
+
+ CSR_SET_BUS_INFO_GENERATION(host->csr.rom, generation);
+ if (csr1212_generate_csr_image(host->csr.rom) != CSR1212_SUCCESS) {
+ /* CSR image creation failed.
+ * Reset generation field and do not issue a bus reset. */
+ CSR_SET_BUS_INFO_GENERATION(host->csr.rom,
+ host->csr.generation);
+ return;
+ }
+
+ host->csr.generation = generation;
+
+ host->update_config_rom = 0;
+ if (host->driver->set_hw_config_rom)
+ host->driver->set_hw_config_rom(host,
+ host->csr.rom->bus_info_data);
+
+ host->csr.gen_timestamp[host->csr.generation] = jiffies;
+ hpsb_reset_bus(host, SHORT_RESET);
+}
+
+static int dummy_transmit_packet(struct hpsb_host *h, struct hpsb_packet *p)
+{
+ return 0;
+}
+
+static int dummy_devctl(struct hpsb_host *h, enum devctl_cmd c, int arg)
+{
+ return -1;
+}
+
+static int dummy_isoctl(struct hpsb_iso *iso, enum isoctl_cmd command,
+ unsigned long arg)
+{
+ return -1;
+}
+
+static struct hpsb_host_driver dummy_driver = {
+ .transmit_packet = dummy_transmit_packet,
+ .devctl = dummy_devctl,
+ .isoctl = dummy_isoctl
+};
+
+static int alloc_hostnum_cb(struct hpsb_host *host, void *__data)
+{
+ int *hostnum = __data;
+
+ if (host->id == *hostnum)
+ return 1;
+
+ return 0;
+}
+
+static DEFINE_MUTEX(host_num_alloc);
+
+/**
+ * hpsb_alloc_host - allocate a new host controller.
+ * @drv: the driver that will manage the host controller
+ * @extra: number of extra bytes to allocate for the driver
+ *
+ * Allocate a &hpsb_host and initialize the general subsystem specific
+ * fields. If the driver needs to store per host data, as drivers
+ * usually do, the amount of memory required can be specified by the
+ * @extra parameter. Once allocated, the driver should initialize the
+ * driver specific parts, enable the controller and make it available
+ * to the general subsystem using hpsb_add_host().
+ *
+ * Return Value: a pointer to the &hpsb_host if successful, %NULL if
+ * no memory was available.
+ */
+struct hpsb_host *hpsb_alloc_host(struct hpsb_host_driver *drv, size_t extra,
+ struct device *dev)
+{
+ struct hpsb_host *h;
+ int i;
+ int hostnum = 0;
+
+ h = kzalloc(sizeof(*h) + extra, GFP_KERNEL);
+ if (!h)
+ return NULL;
+
+ h->csr.rom = csr1212_create_csr(&csr_bus_ops, CSR_BUS_INFO_SIZE, h);
+ if (!h->csr.rom)
+ goto fail;
+
+ h->hostdata = h + 1;
+ h->driver = drv;
+
+ INIT_LIST_HEAD(&h->pending_packets);
+ INIT_LIST_HEAD(&h->addr_space);
+
+ for (i = 2; i < 16; i++)
+ h->csr.gen_timestamp[i] = jiffies - 60 * HZ;
+
+ atomic_set(&h->generation, 0);
+
+ INIT_DELAYED_WORK(&h->delayed_reset, delayed_reset_bus);
+
+ init_timer(&h->timeout);
+ h->timeout.data = (unsigned long) h;
+ h->timeout.function = abort_timedouts;
+ h->timeout_interval = HZ / 20; /* 50ms, half of minimum SPLIT_TIMEOUT */
+
+ h->topology_map = h->csr.topology_map + 3;
+ h->speed_map = (u8 *)(h->csr.speed_map + 2);
+
+ mutex_lock(&host_num_alloc);
+ while (nodemgr_for_each_host(&hostnum, alloc_hostnum_cb))
+ hostnum++;
+ mutex_unlock(&host_num_alloc);
+ h->id = hostnum;
+
+ memcpy(&h->device, &nodemgr_dev_template_host, sizeof(h->device));
+ h->device.parent = dev;
+ set_dev_node(&h->device, dev_to_node(dev));
+ dev_set_name(&h->device, "fw-host%d", h->id);
+
+ h->host_dev.parent = &h->device;
+ h->host_dev.class = &hpsb_host_class;
+ dev_set_name(&h->host_dev, "fw-host%d", h->id);
+
+ if (device_register(&h->device))
+ goto fail;
+ if (device_register(&h->host_dev)) {
+ device_unregister(&h->device);
+ goto fail;
+ }
+ get_device(&h->device);
+
+ return h;
+
+fail:
+ kfree(h);
+ return NULL;
+}
+
+int hpsb_add_host(struct hpsb_host *host)
+{
+ if (hpsb_default_host_entry(host))
+ return -ENOMEM;
+
+ highlevel_add_host(host);
+ return 0;
+}
+
+void hpsb_resume_host(struct hpsb_host *host)
+{
+ if (host->driver->set_hw_config_rom)
+ host->driver->set_hw_config_rom(host,
+ host->csr.rom->bus_info_data);
+ host->driver->devctl(host, RESET_BUS, SHORT_RESET);
+}
+
+void hpsb_remove_host(struct hpsb_host *host)
+{
+ host->is_shutdown = 1;
+
+ cancel_delayed_work(&host->delayed_reset);
+ flush_scheduled_work();
+
+ host->driver = &dummy_driver;
+ highlevel_remove_host(host);
+
+ device_unregister(&host->host_dev);
+ device_unregister(&host->device);
+}
+
+/**
+ * hpsb_update_config_rom_image - updates configuration ROM image of a host
+ *
+ * Updates the configuration ROM image of a host. rom_version must be the
+ * current version, otherwise it will fail with return value -1. If this
+ * host does not support config-rom-update, it will return -%EINVAL.
+ * Return value 0 indicates success.
+ */
+int hpsb_update_config_rom_image(struct hpsb_host *host)
+{
+ unsigned long reset_delay;
+ int next_gen = host->csr.generation + 1;
+
+ if (!host->update_config_rom)
+ return -EINVAL;
+
+ if (next_gen > 0xf)
+ next_gen = 2;
+
+ /* Stop the delayed interrupt, we're about to change the config rom and
+ * it would be a waste to do a bus reset twice. */
+ cancel_delayed_work(&host->delayed_reset);
+
+ /* IEEE 1394a-2000 prohibits using the same generation number
+ * twice in a 60 second period. */
+ if (time_before(jiffies, host->csr.gen_timestamp[next_gen] + 60 * HZ))
+ /* Wait 60 seconds from the last time this generation number was
+ * used. */
+ reset_delay =
+ (60 * HZ) + host->csr.gen_timestamp[next_gen] - jiffies;
+ else
+ /* Wait 1 second in case some other code wants to change the
+ * Config ROM in the near future. */
+ reset_delay = HZ;
+
+ PREPARE_DELAYED_WORK(&host->delayed_reset, delayed_reset_bus);
+ schedule_delayed_work(&host->delayed_reset, reset_delay);
+
+ return 0;
+}
diff --git a/drivers/ieee1394/hosts.h b/drivers/ieee1394/hosts.h
new file mode 100644
index 0000000..dd22995
--- /dev/null
+++ b/drivers/ieee1394/hosts.h
@@ -0,0 +1,201 @@
+#ifndef _IEEE1394_HOSTS_H
+#define _IEEE1394_HOSTS_H
+
+#include <linux/device.h>
+#include <linux/list.h>
+#include <linux/timer.h>
+#include <linux/types.h>
+#include <linux/workqueue.h>
+#include <asm/atomic.h>
+
+struct pci_dev;
+struct module;
+
+#include "ieee1394_types.h"
+#include "csr.h"
+#include "highlevel.h"
+
+struct hpsb_packet;
+struct hpsb_iso;
+
+struct hpsb_host {
+ struct list_head host_list;
+
+ void *hostdata;
+
+ atomic_t generation;
+
+ struct list_head pending_packets;
+ struct timer_list timeout;
+ unsigned long timeout_interval;
+
+ int node_count; /* number of identified nodes on this bus */
+ int selfid_count; /* total number of SelfIDs received */
+ int nodes_active; /* number of nodes with active link layer */
+
+ nodeid_t node_id; /* node ID of this host */
+ nodeid_t irm_id; /* ID of this bus' isochronous resource manager */
+ nodeid_t busmgr_id; /* ID of this bus' bus manager */
+
+ /* this nodes state */
+ unsigned in_bus_reset:1;
+ unsigned is_shutdown:1;
+ unsigned resume_packet_sent:1;
+
+ /* this nodes' duties on the bus */
+ unsigned is_root:1;
+ unsigned is_cycmst:1;
+ unsigned is_irm:1;
+ unsigned is_busmgr:1;
+
+ int reset_retries;
+ quadlet_t *topology_map;
+ u8 *speed_map;
+
+ int id;
+ struct hpsb_host_driver *driver;
+ struct pci_dev *pdev;
+ struct device device;
+ struct device host_dev;
+
+ struct delayed_work delayed_reset;
+ unsigned config_roms:31;
+ unsigned update_config_rom:1;
+
+ struct list_head addr_space;
+ u64 low_addr_space; /* upper bound of physical DMA area */
+ u64 middle_addr_space; /* upper bound of posted write area */
+
+ u8 speed[ALL_NODES]; /* speed between each node and local node */
+
+ /* per node tlabel allocation */
+ u8 next_tl[ALL_NODES];
+ struct { DECLARE_BITMAP(map, 64); } tl_pool[ALL_NODES];
+
+ struct csr_control csr;
+
+ struct hpsb_address_serve dummy_zero_addr;
+ struct hpsb_address_serve dummy_max_addr;
+};
+
+enum devctl_cmd {
+ /* Host is requested to reset its bus and cancel all outstanding async
+ * requests. If arg == 1, it shall also attempt to become root on the
+ * bus. Return void. */
+ RESET_BUS,
+
+ /* Arg is void, return value is the hardware cycle counter value. */
+ GET_CYCLE_COUNTER,
+
+ /* Set the hardware cycle counter to the value in arg, return void.
+ * FIXME - setting is probably not required. */
+ SET_CYCLE_COUNTER,
+
+ /* Configure hardware for new bus ID in arg, return void. */
+ SET_BUS_ID,
+
+ /* If arg true, start sending cycle start packets, stop if arg == 0.
+ * Return void. */
+ ACT_CYCLE_MASTER,
+
+ /* Cancel all outstanding async requests without resetting the bus.
+ * Return void. */
+ CANCEL_REQUESTS,
+};
+
+enum isoctl_cmd {
+ /* rawiso API - see iso.h for the meanings of these commands
+ * (they correspond exactly to the hpsb_iso_* API functions)
+ * INIT = allocate resources
+ * START = begin transmission/reception
+ * STOP = halt transmission/reception
+ * QUEUE/RELEASE = produce/consume packets
+ * SHUTDOWN = deallocate resources
+ */
+
+ XMIT_INIT,
+ XMIT_START,
+ XMIT_STOP,
+ XMIT_QUEUE,
+ XMIT_SHUTDOWN,
+
+ RECV_INIT,
+ RECV_LISTEN_CHANNEL, /* multi-channel only */
+ RECV_UNLISTEN_CHANNEL, /* multi-channel only */
+ RECV_SET_CHANNEL_MASK, /* multi-channel only; arg is a *u64 */
+ RECV_START,
+ RECV_STOP,
+ RECV_RELEASE,
+ RECV_SHUTDOWN,
+ RECV_FLUSH
+};
+
+enum reset_types {
+ /* 166 microsecond reset -- only type of reset available on
+ non-1394a capable controllers */
+ LONG_RESET,
+
+ /* Short (arbitrated) reset -- only available on 1394a capable
+ controllers */
+ SHORT_RESET,
+
+ /* Variants that set force_root before issueing the bus reset */
+ LONG_RESET_FORCE_ROOT, SHORT_RESET_FORCE_ROOT,
+
+ /* Variants that clear force_root before issueing the bus reset */
+ LONG_RESET_NO_FORCE_ROOT, SHORT_RESET_NO_FORCE_ROOT
+};
+
+struct hpsb_host_driver {
+ struct module *owner;
+ const char *name;
+
+ /* The hardware driver may optionally support a function that is used
+ * to set the hardware ConfigROM if the hardware supports handling
+ * reads to the ConfigROM on its own. */
+ void (*set_hw_config_rom)(struct hpsb_host *host,
+ quadlet_t *config_rom);
+
+ /* This function shall implement packet transmission based on
+ * packet->type. It shall CRC both parts of the packet (unless
+ * packet->type == raw) and do byte-swapping as necessary or instruct
+ * the hardware to do so. It can return immediately after the packet
+ * was queued for sending. After sending, hpsb_sent_packet() has to be
+ * called. Return 0 on success, negative errno on failure.
+ * NOTE: The function must be callable in interrupt context.
+ */
+ int (*transmit_packet)(struct hpsb_host *host,
+ struct hpsb_packet *packet);
+
+ /* This function requests miscellanous services from the driver, see
+ * above for command codes and expected actions. Return -1 for unknown
+ * command, though that should never happen.
+ */
+ int (*devctl)(struct hpsb_host *host, enum devctl_cmd command, int arg);
+
+ /* ISO transmission/reception functions. Return 0 on success, -1
+ * (or -EXXX errno code) on failure. If the low-level driver does not
+ * support the new ISO API, set isoctl to NULL.
+ */
+ int (*isoctl)(struct hpsb_iso *iso, enum isoctl_cmd command,
+ unsigned long arg);
+
+ /* This function is mainly to redirect local CSR reads/locks to the iso
+ * management registers (bus manager id, bandwidth available, channels
+ * available) to the hardware registers in OHCI. reg is 0,1,2,3 for bus
+ * mgr, bwdth avail, ch avail hi, ch avail lo respectively (the same ids
+ * as OHCI uses). data and compare are the new data and expected data
+ * respectively, return value is the old value.
+ */
+ quadlet_t (*hw_csr_reg) (struct hpsb_host *host, int reg,
+ quadlet_t data, quadlet_t compare);
+};
+
+struct hpsb_host *hpsb_alloc_host(struct hpsb_host_driver *drv, size_t extra,
+ struct device *dev);
+int hpsb_add_host(struct hpsb_host *host);
+void hpsb_resume_host(struct hpsb_host *host);
+void hpsb_remove_host(struct hpsb_host *host);
+int hpsb_update_config_rom_image(struct hpsb_host *host);
+
+#endif /* _IEEE1394_HOSTS_H */
diff --git a/drivers/ieee1394/ieee1394-ioctl.h b/drivers/ieee1394/ieee1394-ioctl.h
new file mode 100644
index 0000000..46878fe
--- /dev/null
+++ b/drivers/ieee1394/ieee1394-ioctl.h
@@ -0,0 +1,106 @@
+/*
+ * Base file for all ieee1394 ioctl's.
+ * Linux-1394 has allocated base '#' with a range of 0x00-0x3f.
+ */
+
+#ifndef __IEEE1394_IOCTL_H
+#define __IEEE1394_IOCTL_H
+
+#include <linux/ioctl.h>
+#include <linux/types.h>
+
+/* DV1394 Gets 10 */
+
+/* Get the driver ready to transmit video. pass a struct dv1394_init* as
+ * the parameter (see below), or NULL to get default parameters */
+#define DV1394_IOC_INIT _IOW('#', 0x06, struct dv1394_init)
+
+/* Stop transmitting video and free the ringbuffer */
+#define DV1394_IOC_SHUTDOWN _IO ('#', 0x07)
+
+/* Submit N new frames to be transmitted, where the index of the first new
+ * frame is first_clear_buffer, and the index of the last new frame is
+ * (first_clear_buffer + N) % n_frames */
+#define DV1394_IOC_SUBMIT_FRAMES _IO ('#', 0x08)
+
+/* Block until N buffers are clear (pass N as the parameter) Because we
+ * re-transmit the last frame on underrun, there will at most be n_frames
+ * - 1 clear frames at any time */
+#define DV1394_IOC_WAIT_FRAMES _IO ('#', 0x09)
+
+/* Capture new frames that have been received, where the index of the
+ * first new frame is first_clear_buffer, and the index of the last new
+ * frame is (first_clear_buffer + N) % n_frames */
+#define DV1394_IOC_RECEIVE_FRAMES _IO ('#', 0x0a)
+
+/* Tell card to start receiving DMA */
+#define DV1394_IOC_START_RECEIVE _IO ('#', 0x0b)
+
+/* Pass a struct dv1394_status* as the parameter */
+#define DV1394_IOC_GET_STATUS _IOR('#', 0x0c, struct dv1394_status)
+
+
+/* Video1394 Gets 10 */
+
+#define VIDEO1394_IOC_LISTEN_CHANNEL \
+ _IOWR('#', 0x10, struct video1394_mmap)
+#define VIDEO1394_IOC_UNLISTEN_CHANNEL \
+ _IOW ('#', 0x11, int)
+#define VIDEO1394_IOC_LISTEN_QUEUE_BUFFER \
+ _IOW ('#', 0x12, struct video1394_wait)
+#define VIDEO1394_IOC_LISTEN_WAIT_BUFFER \
+ _IOWR('#', 0x13, struct video1394_wait)
+#define VIDEO1394_IOC_TALK_CHANNEL \
+ _IOWR('#', 0x14, struct video1394_mmap)
+#define VIDEO1394_IOC_UNTALK_CHANNEL \
+ _IOW ('#', 0x15, int)
+/*
+ * This one is broken: it really wanted
+ * "sizeof (struct video1394_wait) + sizeof (struct video1394_queue_variable)"
+ * but got just a "size_t"
+ */
+#define VIDEO1394_IOC_TALK_QUEUE_BUFFER \
+ _IOW ('#', 0x16, size_t)
+#define VIDEO1394_IOC_TALK_WAIT_BUFFER \
+ _IOW ('#', 0x17, struct video1394_wait)
+#define VIDEO1394_IOC_LISTEN_POLL_BUFFER \
+ _IOWR('#', 0x18, struct video1394_wait)
+
+
+/* Raw1394's ISO interface */
+#define RAW1394_IOC_ISO_XMIT_INIT \
+ _IOW ('#', 0x1a, struct raw1394_iso_status)
+#define RAW1394_IOC_ISO_RECV_INIT \
+ _IOWR('#', 0x1b, struct raw1394_iso_status)
+#define RAW1394_IOC_ISO_RECV_START \
+ _IOC (_IOC_WRITE, '#', 0x1c, sizeof(int) * 3)
+#define RAW1394_IOC_ISO_XMIT_START \
+ _IOC (_IOC_WRITE, '#', 0x1d, sizeof(int) * 2)
+#define RAW1394_IOC_ISO_XMIT_RECV_STOP \
+ _IO ('#', 0x1e)
+#define RAW1394_IOC_ISO_GET_STATUS \
+ _IOR ('#', 0x1f, struct raw1394_iso_status)
+#define RAW1394_IOC_ISO_SHUTDOWN \
+ _IO ('#', 0x20)
+#define RAW1394_IOC_ISO_QUEUE_ACTIVITY \
+ _IO ('#', 0x21)
+#define RAW1394_IOC_ISO_RECV_LISTEN_CHANNEL \
+ _IOW ('#', 0x22, unsigned char)
+#define RAW1394_IOC_ISO_RECV_UNLISTEN_CHANNEL \
+ _IOW ('#', 0x23, unsigned char)
+#define RAW1394_IOC_ISO_RECV_SET_CHANNEL_MASK \
+ _IOW ('#', 0x24, __u64)
+#define RAW1394_IOC_ISO_RECV_PACKETS \
+ _IOW ('#', 0x25, struct raw1394_iso_packets)
+#define RAW1394_IOC_ISO_RECV_RELEASE_PACKETS \
+ _IOW ('#', 0x26, unsigned int)
+#define RAW1394_IOC_ISO_XMIT_PACKETS \
+ _IOW ('#', 0x27, struct raw1394_iso_packets)
+#define RAW1394_IOC_ISO_XMIT_SYNC \
+ _IO ('#', 0x28)
+#define RAW1394_IOC_ISO_RECV_FLUSH \
+ _IO ('#', 0x29)
+#define RAW1394_IOC_GET_CYCLE_TIMER \
+ _IOR ('#', 0x30, struct raw1394_cycle_timer)
+
+#endif /* __IEEE1394_IOCTL_H */
diff --git a/drivers/ieee1394/ieee1394.h b/drivers/ieee1394/ieee1394.h
new file mode 100644
index 0000000..4049207
--- /dev/null
+++ b/drivers/ieee1394/ieee1394.h
@@ -0,0 +1,219 @@
+/*
+ * Generic IEEE 1394 definitions
+ */
+
+#ifndef _IEEE1394_IEEE1394_H
+#define _IEEE1394_IEEE1394_H
+
+#define TCODE_WRITEQ 0x0
+#define TCODE_WRITEB 0x1
+#define TCODE_WRITE_RESPONSE 0x2
+#define TCODE_READQ 0x4
+#define TCODE_READB 0x5
+#define TCODE_READQ_RESPONSE 0x6
+#define TCODE_READB_RESPONSE 0x7
+#define TCODE_CYCLE_START 0x8
+#define TCODE_LOCK_REQUEST 0x9
+#define TCODE_ISO_DATA 0xa
+#define TCODE_STREAM_DATA 0xa
+#define TCODE_LOCK_RESPONSE 0xb
+
+#define RCODE_COMPLETE 0x0
+#define RCODE_CONFLICT_ERROR 0x4
+#define RCODE_DATA_ERROR 0x5
+#define RCODE_TYPE_ERROR 0x6
+#define RCODE_ADDRESS_ERROR 0x7
+
+#define EXTCODE_MASK_SWAP 0x1
+#define EXTCODE_COMPARE_SWAP 0x2
+#define EXTCODE_FETCH_ADD 0x3
+#define EXTCODE_LITTLE_ADD 0x4
+#define EXTCODE_BOUNDED_ADD 0x5
+#define EXTCODE_WRAP_ADD 0x6
+
+#define ACK_COMPLETE 0x1
+#define ACK_PENDING 0x2
+#define ACK_BUSY_X 0x4
+#define ACK_BUSY_A 0x5
+#define ACK_BUSY_B 0x6
+#define ACK_TARDY 0xb
+#define ACK_CONFLICT_ERROR 0xc
+#define ACK_DATA_ERROR 0xd
+#define ACK_TYPE_ERROR 0xe
+#define ACK_ADDRESS_ERROR 0xf
+
+/* Non-standard "ACK codes" for internal use */
+#define ACKX_NONE (-1)
+#define ACKX_SEND_ERROR (-2)
+#define ACKX_ABORTED (-3)
+#define ACKX_TIMEOUT (-4)
+
+#define IEEE1394_SPEED_100 0x00
+#define IEEE1394_SPEED_200 0x01
+#define IEEE1394_SPEED_400 0x02
+#define IEEE1394_SPEED_800 0x03
+#define IEEE1394_SPEED_1600 0x04
+#define IEEE1394_SPEED_3200 0x05
+
+/* The current highest tested speed supported by the subsystem */
+#define IEEE1394_SPEED_MAX IEEE1394_SPEED_800
+
+/* Maps speed values above to a string representation */
+extern const char *hpsb_speedto_str[];
+
+/* 1394a cable PHY packets */
+#define SELFID_PWRCL_NO_POWER 0x0
+#define SELFID_PWRCL_PROVIDE_15W 0x1
+#define SELFID_PWRCL_PROVIDE_30W 0x2
+#define SELFID_PWRCL_PROVIDE_45W 0x3
+#define SELFID_PWRCL_USE_1W 0x4
+#define SELFID_PWRCL_USE_3W 0x5
+#define SELFID_PWRCL_USE_6W 0x6
+#define SELFID_PWRCL_USE_10W 0x7
+
+#define SELFID_PORT_CHILD 0x3
+#define SELFID_PORT_PARENT 0x2
+#define SELFID_PORT_NCONN 0x1
+#define SELFID_PORT_NONE 0x0
+
+#define SELFID_SPEED_UNKNOWN 0x3 /* 1394b PHY */
+
+#define PHYPACKET_LINKON 0x40000000
+#define PHYPACKET_PHYCONFIG_R 0x00800000
+#define PHYPACKET_PHYCONFIG_T 0x00400000
+#define EXTPHYPACKET_TYPE_PING 0x00000000
+#define EXTPHYPACKET_TYPE_REMOTEACCESS_BASE 0x00040000
+#define EXTPHYPACKET_TYPE_REMOTEACCESS_PAGED 0x00140000
+#define EXTPHYPACKET_TYPE_REMOTEREPLY_BASE 0x000C0000
+#define EXTPHYPACKET_TYPE_REMOTEREPLY_PAGED 0x001C0000
+#define EXTPHYPACKET_TYPE_REMOTECOMMAND 0x00200000
+#define EXTPHYPACKET_TYPE_REMOTECONFIRMATION 0x00280000
+#define EXTPHYPACKET_TYPE_RESUME 0x003C0000
+
+#define EXTPHYPACKET_TYPEMASK 0xC0FC0000
+
+#define PHYPACKET_PORT_SHIFT 24
+#define PHYPACKET_GAPCOUNT_SHIFT 16
+
+/* 1394a PHY register map bitmasks */
+#define PHY_00_PHYSICAL_ID 0xFC
+#define PHY_00_R 0x02 /* Root */
+#define PHY_00_PS 0x01 /* Power Status*/
+#define PHY_01_RHB 0x80 /* Root Hold-Off */
+#define PHY_01_IBR 0x80 /* Initiate Bus Reset */
+#define PHY_01_GAP_COUNT 0x3F
+#define PHY_02_EXTENDED 0xE0 /* 0x7 for 1394a-compliant PHY */
+#define PHY_02_TOTAL_PORTS 0x1F
+#define PHY_03_MAX_SPEED 0xE0
+#define PHY_03_DELAY 0x0F
+#define PHY_04_LCTRL 0x80 /* Link Active Report Control */
+#define PHY_04_CONTENDER 0x40
+#define PHY_04_JITTER 0x38
+#define PHY_04_PWR_CLASS 0x07 /* Power Class */
+#define PHY_05_WATCHDOG 0x80
+#define PHY_05_ISBR 0x40 /* Initiate Short Bus Reset */
+#define PHY_05_LOOP 0x20 /* Loop Detect */
+#define PHY_05_PWR_FAIL 0x10 /* Cable Power Failure Detect */
+#define PHY_05_TIMEOUT 0x08 /* Arbitration State Machine Timeout */
+#define PHY_05_PORT_EVENT 0x04 /* Port Event Detect */
+#define PHY_05_ENAB_ACCEL 0x02 /* Enable Arbitration Acceleration */
+#define PHY_05_ENAB_MULTI 0x01 /* Ena. Multispeed Packet Concatenation */
+
+#include <asm/byteorder.h>
+
+#ifdef __BIG_ENDIAN_BITFIELD
+
+struct selfid {
+ u32 packet_identifier:2; /* always binary 10 */
+ u32 phy_id:6;
+ /* byte */
+ u32 extended:1; /* if true is struct ext_selfid */
+ u32 link_active:1;
+ u32 gap_count:6;
+ /* byte */
+ u32 speed:2;
+ u32 phy_delay:2;
+ u32 contender:1;
+ u32 power_class:3;
+ /* byte */
+ u32 port0:2;
+ u32 port1:2;
+ u32 port2:2;
+ u32 initiated_reset:1;
+ u32 more_packets:1;
+} __attribute__((packed));
+
+struct ext_selfid {
+ u32 packet_identifier:2; /* always binary 10 */
+ u32 phy_id:6;
+ /* byte */
+ u32 extended:1; /* if false is struct selfid */
+ u32 seq_nr:3;
+ u32 reserved:2;
+ u32 porta:2;
+ /* byte */
+ u32 portb:2;
+ u32 portc:2;
+ u32 portd:2;
+ u32 porte:2;
+ /* byte */
+ u32 portf:2;
+ u32 portg:2;
+ u32 porth:2;
+ u32 reserved2:1;
+ u32 more_packets:1;
+} __attribute__((packed));
+
+#elif defined __LITTLE_ENDIAN_BITFIELD /* __BIG_ENDIAN_BITFIELD */
+
+/*
+ * Note: these mean to be bit fields of a big endian SelfID as seen on a little
+ * endian machine. Without swapping.
+ */
+
+struct selfid {
+ u32 phy_id:6;
+ u32 packet_identifier:2; /* always binary 10 */
+ /* byte */
+ u32 gap_count:6;
+ u32 link_active:1;
+ u32 extended:1; /* if true is struct ext_selfid */
+ /* byte */
+ u32 power_class:3;
+ u32 contender:1;
+ u32 phy_delay:2;
+ u32 speed:2;
+ /* byte */
+ u32 more_packets:1;
+ u32 initiated_reset:1;
+ u32 port2:2;
+ u32 port1:2;
+ u32 port0:2;
+} __attribute__((packed));
+
+struct ext_selfid {
+ u32 phy_id:6;
+ u32 packet_identifier:2; /* always binary 10 */
+ /* byte */
+ u32 porta:2;
+ u32 reserved:2;
+ u32 seq_nr:3;
+ u32 extended:1; /* if false is struct selfid */
+ /* byte */
+ u32 porte:2;
+ u32 portd:2;
+ u32 portc:2;
+ u32 portb:2;
+ /* byte */
+ u32 more_packets:1;
+ u32 reserved2:1;
+ u32 porth:2;
+ u32 portg:2;
+ u32 portf:2;
+} __attribute__((packed));
+
+#else
+#error What? PDP endian?
+#endif /* __BIG_ENDIAN_BITFIELD */
+
+#endif /* _IEEE1394_IEEE1394_H */
diff --git a/drivers/ieee1394/ieee1394_core.c b/drivers/ieee1394/ieee1394_core.c
new file mode 100644
index 0000000..dcdb71a
--- /dev/null
+++ b/drivers/ieee1394/ieee1394_core.c
@@ -0,0 +1,1379 @@
+/*
+ * IEEE 1394 for Linux
+ *
+ * Core support: hpsb_packet management, packet handling and forwarding to
+ * highlevel or lowlevel code
+ *
+ * Copyright (C) 1999, 2000 Andreas E. Bombe
+ * 2002 Manfred Weihs <weihs@ict.tuwien.ac.at>
+ *
+ * This code is licensed under the GPL. See the file COPYING in the root
+ * directory of the kernel sources for details.
+ *
+ *
+ * Contributions:
+ *
+ * Manfred Weihs <weihs@ict.tuwien.ac.at>
+ * loopback functionality in hpsb_send_packet
+ * allow highlevel drivers to disable automatic response generation
+ * and to generate responses themselves (deferred)
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/bitops.h>
+#include <linux/kdev_t.h>
+#include <linux/freezer.h>
+#include <linux/suspend.h>
+#include <linux/kthread.h>
+#include <linux/preempt.h>
+#include <linux/time.h>
+
+#include <asm/system.h>
+#include <asm/byteorder.h>
+
+#include "ieee1394_types.h"
+#include "ieee1394.h"
+#include "hosts.h"
+#include "ieee1394_core.h"
+#include "highlevel.h"
+#include "ieee1394_transactions.h"
+#include "csr.h"
+#include "nodemgr.h"
+#include "dma.h"
+#include "iso.h"
+#include "config_roms.h"
+
+/*
+ * Disable the nodemgr detection and config rom reading functionality.
+ */
+static int disable_nodemgr;
+module_param(disable_nodemgr, int, 0444);
+MODULE_PARM_DESC(disable_nodemgr, "Disable nodemgr functionality.");
+
+/* Disable Isochronous Resource Manager functionality */
+int hpsb_disable_irm = 0;
+module_param_named(disable_irm, hpsb_disable_irm, bool, 0444);
+MODULE_PARM_DESC(disable_irm,
+ "Disable Isochronous Resource Manager functionality.");
+
+/* We are GPL, so treat us special */
+MODULE_LICENSE("GPL");
+
+/* Some globals used */
+const char *hpsb_speedto_str[] = { "S100", "S200", "S400", "S800", "S1600", "S3200" };
+struct class *hpsb_protocol_class;
+
+#ifdef CONFIG_IEEE1394_VERBOSEDEBUG
+static void dump_packet(const char *text, quadlet_t *data, int size, int speed)
+{
+ int i;
+
+ size /= 4;
+ size = (size > 4 ? 4 : size);
+
+ printk(KERN_DEBUG "ieee1394: %s", text);
+ if (speed > -1 && speed < 6)
+ printk(" at %s", hpsb_speedto_str[speed]);
+ printk(":");
+ for (i = 0; i < size; i++)
+ printk(" %08x", data[i]);
+ printk("\n");
+}
+#else
+#define dump_packet(a,b,c,d) do {} while (0)
+#endif
+
+static void abort_requests(struct hpsb_host *host);
+static void queue_packet_complete(struct hpsb_packet *packet);
+
+
+/**
+ * hpsb_set_packet_complete_task - set task that runs when a packet completes
+ * @packet: the packet whose completion we want the task added to
+ * @routine: function to call
+ * @data: data (if any) to pass to the above function
+ *
+ * Set the task that runs when a packet completes. You cannot call this more
+ * than once on a single packet before it is sent.
+ *
+ * Typically, the complete @routine is responsible to call hpsb_free_packet().
+ */
+void hpsb_set_packet_complete_task(struct hpsb_packet *packet,
+ void (*routine)(void *), void *data)
+{
+ WARN_ON(packet->complete_routine != NULL);
+ packet->complete_routine = routine;
+ packet->complete_data = data;
+ return;
+}
+
+/**
+ * hpsb_alloc_packet - allocate new packet structure
+ * @data_size: size of the data block to be allocated, in bytes
+ *
+ * This function allocates, initializes and returns a new &struct hpsb_packet.
+ * It can be used in interrupt context. A header block is always included and
+ * initialized with zeros. Its size is big enough to contain all possible 1394
+ * headers. The data block is only allocated if @data_size is not zero.
+ *
+ * For packets for which responses will be received the @data_size has to be big
+ * enough to contain the response's data block since no further allocation
+ * occurs at response matching time.
+ *
+ * The packet's generation value will be set to the current generation number
+ * for ease of use. Remember to overwrite it with your own recorded generation
+ * number if you can not be sure that your code will not race with a bus reset.
+ *
+ * Return value: A pointer to a &struct hpsb_packet or NULL on allocation
+ * failure.
+ */
+struct hpsb_packet *hpsb_alloc_packet(size_t data_size)
+{
+ struct hpsb_packet *packet;
+
+ data_size = ((data_size + 3) & ~3);
+
+ packet = kzalloc(sizeof(*packet) + data_size, GFP_ATOMIC);
+ if (!packet)
+ return NULL;
+
+ packet->state = hpsb_unused;
+ packet->generation = -1;
+ INIT_LIST_HEAD(&packet->driver_list);
+ INIT_LIST_HEAD(&packet->queue);
+ atomic_set(&packet->refcnt, 1);
+
+ if (data_size) {
+ packet->data = packet->embedded_data;
+ packet->allocated_data_size = data_size;
+ }
+ return packet;
+}
+
+/**
+ * hpsb_free_packet - free packet and data associated with it
+ * @packet: packet to free (is NULL safe)
+ *
+ * Frees @packet->data only if it was allocated through hpsb_alloc_packet().
+ */
+void hpsb_free_packet(struct hpsb_packet *packet)
+{
+ if (packet && atomic_dec_and_test(&packet->refcnt)) {
+ BUG_ON(!list_empty(&packet->driver_list) ||
+ !list_empty(&packet->queue));
+ kfree(packet);
+ }
+}
+
+/**
+ * hpsb_reset_bus - initiate bus reset on the given host
+ * @host: host controller whose bus to reset
+ * @type: one of enum reset_types
+ *
+ * Returns 1 if bus reset already in progress, 0 otherwise.
+ */
+int hpsb_reset_bus(struct hpsb_host *host, int type)
+{
+ if (!host->in_bus_reset) {
+ host->driver->devctl(host, RESET_BUS, type);
+ return 0;
+ } else {
+ return 1;
+ }
+}
+
+/**
+ * hpsb_read_cycle_timer - read cycle timer register and system time
+ * @host: host whose isochronous cycle timer register is read
+ * @cycle_timer: address of bitfield to return the register contents
+ * @local_time: address to return the system time
+ *
+ * The format of * @cycle_timer, is described in OHCI 1.1 clause 5.13. This
+ * format is also read from non-OHCI controllers. * @local_time contains the
+ * system time in microseconds since the Epoch, read at the moment when the
+ * cycle timer was read.
+ *
+ * Return value: 0 for success or error number otherwise.
+ */
+int hpsb_read_cycle_timer(struct hpsb_host *host, u32 *cycle_timer,
+ u64 *local_time)
+{
+ int ctr;
+ struct timeval tv;
+ unsigned long flags;
+
+ if (!host || !cycle_timer || !local_time)
+ return -EINVAL;
+
+ preempt_disable();
+ local_irq_save(flags);
+
+ ctr = host->driver->devctl(host, GET_CYCLE_COUNTER, 0);
+ if (ctr)
+ do_gettimeofday(&tv);
+
+ local_irq_restore(flags);
+ preempt_enable();
+
+ if (!ctr)
+ return -EIO;
+ *cycle_timer = ctr;
+ *local_time = tv.tv_sec * 1000000ULL + tv.tv_usec;
+ return 0;
+}
+
+/**
+ * hpsb_bus_reset - notify a bus reset to the core
+ *
+ * For host driver module usage. Safe to use in interrupt context, although
+ * quite complex; so you may want to run it in the bottom rather than top half.
+ *
+ * Returns 1 if bus reset already in progress, 0 otherwise.
+ */
+int hpsb_bus_reset(struct hpsb_host *host)
+{
+ if (host->in_bus_reset) {
+ HPSB_NOTICE("%s called while bus reset already in progress",
+ __func__);
+ return 1;
+ }
+
+ abort_requests(host);
+ host->in_bus_reset = 1;
+ host->irm_id = -1;
+ host->is_irm = 0;
+ host->busmgr_id = -1;
+ host->is_busmgr = 0;
+ host->is_cycmst = 0;
+ host->node_count = 0;
+ host->selfid_count = 0;
+
+ return 0;
+}
+
+
+/*
+ * Verify num_of_selfids SelfIDs and return number of nodes. Return zero in
+ * case verification failed.
+ */
+static int check_selfids(struct hpsb_host *host)
+{
+ int nodeid = -1;
+ int rest_of_selfids = host->selfid_count;
+ struct selfid *sid = (struct selfid *)host->topology_map;
+ struct ext_selfid *esid;
+ int esid_seq = 23;
+
+ host->nodes_active = 0;
+
+ while (rest_of_selfids--) {
+ if (!sid->extended) {
+ nodeid++;
+ esid_seq = 0;
+
+ if (sid->phy_id != nodeid) {
+ HPSB_INFO("SelfIDs failed monotony check with "
+ "%d", sid->phy_id);
+ return 0;
+ }
+
+ if (sid->link_active) {
+ host->nodes_active++;
+ if (sid->contender)
+ host->irm_id = LOCAL_BUS | sid->phy_id;
+ }
+ } else {
+ esid = (struct ext_selfid *)sid;
+
+ if ((esid->phy_id != nodeid)
+ || (esid->seq_nr != esid_seq)) {
+ HPSB_INFO("SelfIDs failed monotony check with "
+ "%d/%d", esid->phy_id, esid->seq_nr);
+ return 0;
+ }
+ esid_seq++;
+ }
+ sid++;
+ }
+
+ esid = (struct ext_selfid *)(sid - 1);
+ while (esid->extended) {
+ if ((esid->porta == SELFID_PORT_PARENT) ||
+ (esid->portb == SELFID_PORT_PARENT) ||
+ (esid->portc == SELFID_PORT_PARENT) ||
+ (esid->portd == SELFID_PORT_PARENT) ||
+ (esid->porte == SELFID_PORT_PARENT) ||
+ (esid->portf == SELFID_PORT_PARENT) ||
+ (esid->portg == SELFID_PORT_PARENT) ||
+ (esid->porth == SELFID_PORT_PARENT)) {
+ HPSB_INFO("SelfIDs failed root check on "
+ "extended SelfID");
+ return 0;
+ }
+ esid--;
+ }
+
+ sid = (struct selfid *)esid;
+ if ((sid->port0 == SELFID_PORT_PARENT) ||
+ (sid->port1 == SELFID_PORT_PARENT) ||
+ (sid->port2 == SELFID_PORT_PARENT)) {
+ HPSB_INFO("SelfIDs failed root check");
+ return 0;
+ }
+
+ host->node_count = nodeid + 1;
+ return 1;
+}
+
+static void build_speed_map(struct hpsb_host *host, int nodecount)
+{
+ u8 cldcnt[nodecount];
+ u8 *map = host->speed_map;
+ u8 *speedcap = host->speed;
+ struct selfid *sid;
+ struct ext_selfid *esid;
+ int i, j, n;
+
+ for (i = 0; i < (nodecount * 64); i += 64) {
+ for (j = 0; j < nodecount; j++) {
+ map[i+j] = IEEE1394_SPEED_MAX;
+ }
+ }
+
+ for (i = 0; i < nodecount; i++) {
+ cldcnt[i] = 0;
+ }
+
+ /* find direct children count and speed */
+ for (sid = (struct selfid *)&host->topology_map[host->selfid_count-1],
+ n = nodecount - 1;
+ (void *)sid >= (void *)host->topology_map; sid--) {
+ if (sid->extended) {
+ esid = (struct ext_selfid *)sid;
+
+ if (esid->porta == SELFID_PORT_CHILD) cldcnt[n]++;
+ if (esid->portb == SELFID_PORT_CHILD) cldcnt[n]++;
+ if (esid->portc == SELFID_PORT_CHILD) cldcnt[n]++;
+ if (esid->portd == SELFID_PORT_CHILD) cldcnt[n]++;
+ if (esid->porte == SELFID_PORT_CHILD) cldcnt[n]++;
+ if (esid->portf == SELFID_PORT_CHILD) cldcnt[n]++;
+ if (esid->portg == SELFID_PORT_CHILD) cldcnt[n]++;
+ if (esid->porth == SELFID_PORT_CHILD) cldcnt[n]++;
+ } else {
+ if (sid->port0 == SELFID_PORT_CHILD) cldcnt[n]++;
+ if (sid->port1 == SELFID_PORT_CHILD) cldcnt[n]++;
+ if (sid->port2 == SELFID_PORT_CHILD) cldcnt[n]++;
+
+ speedcap[n] = sid->speed;
+ if (speedcap[n] > host->csr.lnk_spd)
+ speedcap[n] = host->csr.lnk_spd;
+ n--;
+ }
+ }
+
+ /* set self mapping */
+ for (i = 0; i < nodecount; i++) {
+ map[64*i + i] = speedcap[i];
+ }
+
+ /* fix up direct children count to total children count;
+ * also fix up speedcaps for sibling and parent communication */
+ for (i = 1; i < nodecount; i++) {
+ for (j = cldcnt[i], n = i - 1; j > 0; j--) {
+ cldcnt[i] += cldcnt[n];
+ speedcap[n] = min(speedcap[n], speedcap[i]);
+ n -= cldcnt[n] + 1;
+ }
+ }
+
+ for (n = 0; n < nodecount; n++) {
+ for (i = n - cldcnt[n]; i <= n; i++) {
+ for (j = 0; j < (n - cldcnt[n]); j++) {
+ map[j*64 + i] = map[i*64 + j] =
+ min(map[i*64 + j], speedcap[n]);
+ }
+ for (j = n + 1; j < nodecount; j++) {
+ map[j*64 + i] = map[i*64 + j] =
+ min(map[i*64 + j], speedcap[n]);
+ }
+ }
+ }
+
+#if SELFID_SPEED_UNKNOWN != IEEE1394_SPEED_MAX
+ /* assume maximum speed for 1394b PHYs, nodemgr will correct it */
+ for (n = 0; n < nodecount; n++)
+ if (speedcap[n] == SELFID_SPEED_UNKNOWN)
+ speedcap[n] = IEEE1394_SPEED_MAX;
+#endif
+}
+
+
+/**
+ * hpsb_selfid_received - hand over received selfid packet to the core
+ *
+ * For host driver module usage. Safe to use in interrupt context.
+ *
+ * The host driver should have done a successful complement check (second
+ * quadlet is complement of first) beforehand.
+ */
+void hpsb_selfid_received(struct hpsb_host *host, quadlet_t sid)
+{
+ if (host->in_bus_reset) {
+ HPSB_VERBOSE("Including SelfID 0x%x", sid);
+ host->topology_map[host->selfid_count++] = sid;
+ } else {
+ HPSB_NOTICE("Spurious SelfID packet (0x%08x) received from bus %d",
+ sid, NODEID_TO_BUS(host->node_id));
+ }
+}
+
+/**
+ * hpsb_selfid_complete - notify completion of SelfID stage to the core
+ *
+ * For host driver module usage. Safe to use in interrupt context, although
+ * quite complex; so you may want to run it in the bottom rather than top half.
+ *
+ * Notify completion of SelfID stage to the core and report new physical ID
+ * and whether host is root now.
+ */
+void hpsb_selfid_complete(struct hpsb_host *host, int phyid, int isroot)
+{
+ if (!host->in_bus_reset)
+ HPSB_NOTICE("SelfID completion called outside of bus reset!");
+
+ host->node_id = LOCAL_BUS | phyid;
+ host->is_root = isroot;
+
+ if (!check_selfids(host)) {
+ if (host->reset_retries++ < 20) {
+ /* selfid stage did not complete without error */
+ HPSB_NOTICE("Error in SelfID stage, resetting");
+ host->in_bus_reset = 0;
+ /* this should work from ohci1394 now... */
+ hpsb_reset_bus(host, LONG_RESET);
+ return;
+ } else {
+ HPSB_NOTICE("Stopping out-of-control reset loop");
+ HPSB_NOTICE("Warning - topology map and speed map will not be valid");
+ host->reset_retries = 0;
+ }
+ } else {
+ host->reset_retries = 0;
+ build_speed_map(host, host->node_count);
+ }
+
+ HPSB_VERBOSE("selfid_complete called with successful SelfID stage "
+ "... irm_id: 0x%X node_id: 0x%X",host->irm_id,host->node_id);
+
+ /* irm_id is kept up to date by check_selfids() */
+ if (host->irm_id == host->node_id) {
+ host->is_irm = 1;
+ } else {
+ host->is_busmgr = 0;
+ host->is_irm = 0;
+ }
+
+ if (isroot) {
+ host->driver->devctl(host, ACT_CYCLE_MASTER, 1);
+ host->is_cycmst = 1;
+ }
+ atomic_inc(&host->generation);
+ host->in_bus_reset = 0;
+ highlevel_host_reset(host);
+}
+
+static DEFINE_SPINLOCK(pending_packets_lock);
+
+/**
+ * hpsb_packet_sent - notify core of sending a packet
+ *
+ * For host driver module usage. Safe to call from within a transmit packet
+ * routine.
+ *
+ * Notify core of sending a packet. Ackcode is the ack code returned for async
+ * transmits or ACKX_SEND_ERROR if the transmission failed completely; ACKX_NONE
+ * for other cases (internal errors that don't justify a panic).
+ */
+void hpsb_packet_sent(struct hpsb_host *host, struct hpsb_packet *packet,
+ int ackcode)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&pending_packets_lock, flags);
+
+ packet->ack_code = ackcode;
+
+ if (packet->no_waiter || packet->state == hpsb_complete) {
+ /* if packet->no_waiter, must not have a tlabel allocated */
+ spin_unlock_irqrestore(&pending_packets_lock, flags);
+ hpsb_free_packet(packet);
+ return;
+ }
+
+ atomic_dec(&packet->refcnt); /* drop HC's reference */
+ /* here the packet must be on the host->pending_packets queue */
+
+ if (ackcode != ACK_PENDING || !packet->expect_response) {
+ packet->state = hpsb_complete;
+ list_del_init(&packet->queue);
+ spin_unlock_irqrestore(&pending_packets_lock, flags);
+ queue_packet_complete(packet);
+ return;
+ }
+
+ packet->state = hpsb_pending;
+ packet->sendtime = jiffies;
+
+ spin_unlock_irqrestore(&pending_packets_lock, flags);
+
+ mod_timer(&host->timeout, jiffies + host->timeout_interval);
+}
+
+/**
+ * hpsb_send_phy_config - transmit a PHY configuration packet on the bus
+ * @host: host that PHY config packet gets sent through
+ * @rootid: root whose force_root bit should get set (-1 = don't set force_root)
+ * @gapcnt: gap count value to set (-1 = don't set gap count)
+ *
+ * This function sends a PHY config packet on the bus through the specified
+ * host.
+ *
+ * Return value: 0 for success or negative error number otherwise.
+ */
+int hpsb_send_phy_config(struct hpsb_host *host, int rootid, int gapcnt)
+{
+ struct hpsb_packet *packet;
+ quadlet_t d = 0;
+ int retval = 0;
+
+ if (rootid >= ALL_NODES || rootid < -1 || gapcnt > 0x3f || gapcnt < -1 ||
+ (rootid == -1 && gapcnt == -1)) {
+ HPSB_DEBUG("Invalid Parameter: rootid = %d gapcnt = %d",
+ rootid, gapcnt);
+ return -EINVAL;
+ }
+
+ if (rootid != -1)
+ d |= PHYPACKET_PHYCONFIG_R | rootid << PHYPACKET_PORT_SHIFT;
+ if (gapcnt != -1)
+ d |= PHYPACKET_PHYCONFIG_T | gapcnt << PHYPACKET_GAPCOUNT_SHIFT;
+
+ packet = hpsb_make_phypacket(host, d);
+ if (!packet)
+ return -ENOMEM;
+
+ packet->generation = get_hpsb_generation(host);
+ retval = hpsb_send_packet_and_wait(packet);
+ hpsb_free_packet(packet);
+
+ return retval;
+}
+
+/**
+ * hpsb_send_packet - transmit a packet on the bus
+ * @packet: packet to send
+ *
+ * The packet is sent through the host specified in the packet->host field.
+ * Before sending, the packet's transmit speed is automatically determined
+ * using the local speed map when it is an async, non-broadcast packet.
+ *
+ * Possibilities for failure are that host is either not initialized, in bus
+ * reset, the packet's generation number doesn't match the current generation
+ * number or the host reports a transmit error.
+ *
+ * Return value: 0 on success, negative errno on failure.
+ */
+int hpsb_send_packet(struct hpsb_packet *packet)
+{
+ struct hpsb_host *host = packet->host;
+
+ if (host->is_shutdown)
+ return -EINVAL;
+ if (host->in_bus_reset ||
+ (packet->generation != get_hpsb_generation(host)))
+ return -EAGAIN;
+
+ packet->state = hpsb_queued;
+
+ /* This just seems silly to me */
+ WARN_ON(packet->no_waiter && packet->expect_response);
+
+ if (!packet->no_waiter || packet->expect_response) {
+ unsigned long flags;
+
+ atomic_inc(&packet->refcnt);
+ /* Set the initial "sendtime" to 10 seconds from now, to
+ prevent premature expiry. If a packet takes more than
+ 10 seconds to hit the wire, we have bigger problems :) */
+ packet->sendtime = jiffies + 10 * HZ;
+ spin_lock_irqsave(&pending_packets_lock, flags);
+ list_add_tail(&packet->queue, &host->pending_packets);
+ spin_unlock_irqrestore(&pending_packets_lock, flags);
+ }
+
+ if (packet->node_id == host->node_id) {
+ /* it is a local request, so handle it locally */
+
+ quadlet_t *data;
+ size_t size = packet->data_size + packet->header_size;
+
+ data = kmalloc(size, GFP_ATOMIC);
+ if (!data) {
+ HPSB_ERR("unable to allocate memory for concatenating header and data");
+ return -ENOMEM;
+ }
+
+ memcpy(data, packet->header, packet->header_size);
+
+ if (packet->data_size)
+ memcpy(((u8*)data) + packet->header_size, packet->data, packet->data_size);
+
+ dump_packet("send packet local", packet->header, packet->header_size, -1);
+
+ hpsb_packet_sent(host, packet, packet->expect_response ? ACK_PENDING : ACK_COMPLETE);
+ hpsb_packet_received(host, data, size, 0);
+
+ kfree(data);
+
+ return 0;
+ }
+
+ if (packet->type == hpsb_async &&
+ NODEID_TO_NODE(packet->node_id) != ALL_NODES)
+ packet->speed_code =
+ host->speed[NODEID_TO_NODE(packet->node_id)];
+
+ dump_packet("send packet", packet->header, packet->header_size, packet->speed_code);
+
+ return host->driver->transmit_packet(host, packet);
+}
+
+/* We could just use complete() directly as the packet complete
+ * callback, but this is more typesafe, in the sense that we get a
+ * compiler error if the prototype for complete() changes. */
+
+static void complete_packet(void *data)
+{
+ complete((struct completion *) data);
+}
+
+/**
+ * hpsb_send_packet_and_wait - enqueue packet, block until transaction completes
+ * @packet: packet to send
+ *
+ * Return value: 0 on success, negative errno on failure.
+ */
+int hpsb_send_packet_and_wait(struct hpsb_packet *packet)
+{
+ struct completion done;
+ int retval;
+
+ init_completion(&done);
+ hpsb_set_packet_complete_task(packet, complete_packet, &done);
+ retval = hpsb_send_packet(packet);
+ if (retval == 0)
+ wait_for_completion(&done);
+
+ return retval;
+}
+
+static void send_packet_nocare(struct hpsb_packet *packet)
+{
+ if (hpsb_send_packet(packet) < 0) {
+ hpsb_free_packet(packet);
+ }
+}
+
+static size_t packet_size_to_data_size(size_t packet_size, size_t header_size,
+ size_t buffer_size, int tcode)
+{
+ size_t ret = packet_size <= header_size ? 0 : packet_size - header_size;
+
+ if (unlikely(ret > buffer_size))
+ ret = buffer_size;
+
+ if (unlikely(ret + header_size != packet_size))
+ HPSB_ERR("unexpected packet size %zd (tcode %d), bug?",
+ packet_size, tcode);
+ return ret;
+}
+
+static void handle_packet_response(struct hpsb_host *host, int tcode,
+ quadlet_t *data, size_t size)
+{
+ struct hpsb_packet *packet;
+ int tlabel = (data[0] >> 10) & 0x3f;
+ size_t header_size;
+ unsigned long flags;
+
+ spin_lock_irqsave(&pending_packets_lock, flags);
+
+ list_for_each_entry(packet, &host->pending_packets, queue)
+ if (packet->tlabel == tlabel &&
+ packet->node_id == (data[1] >> 16))
+ goto found;
+
+ spin_unlock_irqrestore(&pending_packets_lock, flags);
+ HPSB_DEBUG("unsolicited response packet received - %s",
+ "no tlabel match");
+ dump_packet("contents", data, 16, -1);
+ return;
+
+found:
+ switch (packet->tcode) {
+ case TCODE_WRITEQ:
+ case TCODE_WRITEB:
+ if (unlikely(tcode != TCODE_WRITE_RESPONSE))
+ break;
+ header_size = 12;
+ size = 0;
+ goto dequeue;
+
+ case TCODE_READQ:
+ if (unlikely(tcode != TCODE_READQ_RESPONSE))
+ break;
+ header_size = 16;
+ size = 0;
+ goto dequeue;
+
+ case TCODE_READB:
+ if (unlikely(tcode != TCODE_READB_RESPONSE))
+ break;
+ header_size = 16;
+ size = packet_size_to_data_size(size, header_size,
+ packet->allocated_data_size,
+ tcode);
+ goto dequeue;
+
+ case TCODE_LOCK_REQUEST:
+ if (unlikely(tcode != TCODE_LOCK_RESPONSE))
+ break;
+ header_size = 16;
+ size = packet_size_to_data_size(min(size, (size_t)(16 + 8)),
+ header_size,
+ packet->allocated_data_size,
+ tcode);
+ goto dequeue;
+ }
+
+ spin_unlock_irqrestore(&pending_packets_lock, flags);
+ HPSB_DEBUG("unsolicited response packet received - %s",
+ "tcode mismatch");
+ dump_packet("contents", data, 16, -1);
+ return;
+
+dequeue:
+ list_del_init(&packet->queue);
+ spin_unlock_irqrestore(&pending_packets_lock, flags);
+
+ if (packet->state == hpsb_queued) {
+ packet->sendtime = jiffies;
+ packet->ack_code = ACK_PENDING;
+ }
+ packet->state = hpsb_complete;
+
+ memcpy(packet->header, data, header_size);
+ if (size)
+ memcpy(packet->data, data + 4, size);
+
+ queue_packet_complete(packet);
+}
+
+
+static struct hpsb_packet *create_reply_packet(struct hpsb_host *host,
+ quadlet_t *data, size_t dsize)
+{
+ struct hpsb_packet *p;
+
+ p = hpsb_alloc_packet(dsize);
+ if (unlikely(p == NULL)) {
+ /* FIXME - send data_error response */
+ HPSB_ERR("out of memory, cannot send response packet");
+ return NULL;
+ }
+
+ p->type = hpsb_async;
+ p->state = hpsb_unused;
+ p->host = host;
+ p->node_id = data[1] >> 16;
+ p->tlabel = (data[0] >> 10) & 0x3f;
+ p->no_waiter = 1;
+
+ p->generation = get_hpsb_generation(host);
+
+ if (dsize % 4)
+ p->data[dsize / 4] = 0;
+
+ return p;
+}
+
+#define PREP_ASYNC_HEAD_RCODE(tc) \
+ packet->tcode = tc; \
+ packet->header[0] = (packet->node_id << 16) | (packet->tlabel << 10) \
+ | (1 << 8) | (tc << 4); \
+ packet->header[1] = (packet->host->node_id << 16) | (rcode << 12); \
+ packet->header[2] = 0
+
+static void fill_async_readquad_resp(struct hpsb_packet *packet, int rcode,
+ quadlet_t data)
+{
+ PREP_ASYNC_HEAD_RCODE(TCODE_READQ_RESPONSE);
+ packet->header[3] = data;
+ packet->header_size = 16;
+ packet->data_size = 0;
+}
+
+static void fill_async_readblock_resp(struct hpsb_packet *packet, int rcode,
+ int length)
+{
+ if (rcode != RCODE_COMPLETE)
+ length = 0;
+
+ PREP_ASYNC_HEAD_RCODE(TCODE_READB_RESPONSE);
+ packet->header[3] = length << 16;
+ packet->header_size = 16;
+ packet->data_size = length + (length % 4 ? 4 - (length % 4) : 0);
+}
+
+static void fill_async_write_resp(struct hpsb_packet *packet, int rcode)
+{
+ PREP_ASYNC_HEAD_RCODE(TCODE_WRITE_RESPONSE);
+ packet->header_size = 12;
+ packet->data_size = 0;
+}
+
+static void fill_async_lock_resp(struct hpsb_packet *packet, int rcode, int extcode,
+ int length)
+{
+ if (rcode != RCODE_COMPLETE)
+ length = 0;
+
+ PREP_ASYNC_HEAD_RCODE(TCODE_LOCK_RESPONSE);
+ packet->header[3] = (length << 16) | extcode;
+ packet->header_size = 16;
+ packet->data_size = length;
+}
+
+static void handle_incoming_packet(struct hpsb_host *host, int tcode,
+ quadlet_t *data, size_t size,
+ int write_acked)
+{
+ struct hpsb_packet *packet;
+ int length, rcode, extcode;
+ quadlet_t buffer;
+ nodeid_t source = data[1] >> 16;
+ nodeid_t dest = data[0] >> 16;
+ u16 flags = (u16) data[0];
+ u64 addr;
+
+ /* FIXME?
+ * Out-of-bounds lengths are left for highlevel_read|write to cap. */
+
+ switch (tcode) {
+ case TCODE_WRITEQ:
+ addr = (((u64)(data[1] & 0xffff)) << 32) | data[2];
+ rcode = highlevel_write(host, source, dest, data + 3,
+ addr, 4, flags);
+ goto handle_write_request;
+
+ case TCODE_WRITEB:
+ addr = (((u64)(data[1] & 0xffff)) << 32) | data[2];
+ rcode = highlevel_write(host, source, dest, data + 4,
+ addr, data[3] >> 16, flags);
+handle_write_request:
+ if (rcode < 0 || write_acked ||
+ NODEID_TO_NODE(data[0] >> 16) == NODE_MASK)
+ return;
+ /* not a broadcast write, reply */
+ packet = create_reply_packet(host, data, 0);
+ if (packet) {
+ fill_async_write_resp(packet, rcode);
+ send_packet_nocare(packet);
+ }
+ return;
+
+ case TCODE_READQ:
+ addr = (((u64)(data[1] & 0xffff)) << 32) | data[2];
+ rcode = highlevel_read(host, source, &buffer, addr, 4, flags);
+ if (rcode < 0)
+ return;
+
+ packet = create_reply_packet(host, data, 0);
+ if (packet) {
+ fill_async_readquad_resp(packet, rcode, buffer);
+ send_packet_nocare(packet);
+ }
+ return;
+
+ case TCODE_READB:
+ length = data[3] >> 16;
+ packet = create_reply_packet(host, data, length);
+ if (!packet)
+ return;
+
+ addr = (((u64)(data[1] & 0xffff)) << 32) | data[2];
+ rcode = highlevel_read(host, source, packet->data, addr,
+ length, flags);
+ if (rcode < 0) {
+ hpsb_free_packet(packet);
+ return;
+ }
+ fill_async_readblock_resp(packet, rcode, length);
+ send_packet_nocare(packet);
+ return;
+
+ case TCODE_LOCK_REQUEST:
+ length = data[3] >> 16;
+ extcode = data[3] & 0xffff;
+ addr = (((u64)(data[1] & 0xffff)) << 32) | data[2];
+
+ packet = create_reply_packet(host, data, 8);
+ if (!packet)
+ return;
+
+ if (extcode == 0 || extcode >= 7) {
+ /* let switch default handle error */
+ length = 0;
+ }
+
+ switch (length) {
+ case 4:
+ rcode = highlevel_lock(host, source, packet->data, addr,
+ data[4], 0, extcode, flags);
+ fill_async_lock_resp(packet, rcode, extcode, 4);
+ break;
+ case 8:
+ if (extcode != EXTCODE_FETCH_ADD &&
+ extcode != EXTCODE_LITTLE_ADD) {
+ rcode = highlevel_lock(host, source,
+ packet->data, addr,
+ data[5], data[4],
+ extcode, flags);
+ fill_async_lock_resp(packet, rcode, extcode, 4);
+ } else {
+ rcode = highlevel_lock64(host, source,
+ (octlet_t *)packet->data, addr,
+ *(octlet_t *)(data + 4), 0ULL,
+ extcode, flags);
+ fill_async_lock_resp(packet, rcode, extcode, 8);
+ }
+ break;
+ case 16:
+ rcode = highlevel_lock64(host, source,
+ (octlet_t *)packet->data, addr,
+ *(octlet_t *)(data + 6),
+ *(octlet_t *)(data + 4),
+ extcode, flags);
+ fill_async_lock_resp(packet, rcode, extcode, 8);
+ break;
+ default:
+ rcode = RCODE_TYPE_ERROR;
+ fill_async_lock_resp(packet, rcode, extcode, 0);
+ }
+
+ if (rcode < 0)
+ hpsb_free_packet(packet);
+ else
+ send_packet_nocare(packet);
+ return;
+ }
+}
+
+/**
+ * hpsb_packet_received - hand over received packet to the core
+ *
+ * For host driver module usage.
+ *
+ * The contents of data are expected to be the full packet but with the CRCs
+ * left out (data block follows header immediately), with the header (i.e. the
+ * first four quadlets) in machine byte order and the data block in big endian.
+ * *@data can be safely overwritten after this call.
+ *
+ * If the packet is a write request, @write_acked is to be set to true if it was
+ * ack_complete'd already, false otherwise. This argument is ignored for any
+ * other packet type.
+ */
+void hpsb_packet_received(struct hpsb_host *host, quadlet_t *data, size_t size,
+ int write_acked)
+{
+ int tcode;
+
+ if (unlikely(host->in_bus_reset)) {
+ HPSB_DEBUG("received packet during reset; ignoring");
+ return;
+ }
+
+ dump_packet("received packet", data, size, -1);
+
+ tcode = (data[0] >> 4) & 0xf;
+
+ switch (tcode) {
+ case TCODE_WRITE_RESPONSE:
+ case TCODE_READQ_RESPONSE:
+ case TCODE_READB_RESPONSE:
+ case TCODE_LOCK_RESPONSE:
+ handle_packet_response(host, tcode, data, size);
+ break;
+
+ case TCODE_WRITEQ:
+ case TCODE_WRITEB:
+ case TCODE_READQ:
+ case TCODE_READB:
+ case TCODE_LOCK_REQUEST:
+ handle_incoming_packet(host, tcode, data, size, write_acked);
+ break;
+
+ case TCODE_CYCLE_START:
+ /* simply ignore this packet if it is passed on */
+ break;
+
+ default:
+ HPSB_DEBUG("received packet with bogus transaction code %d",
+ tcode);
+ break;
+ }
+}
+
+static void abort_requests(struct hpsb_host *host)
+{
+ struct hpsb_packet *packet, *p;
+ struct list_head tmp;
+ unsigned long flags;
+
+ host->driver->devctl(host, CANCEL_REQUESTS, 0);
+
+ INIT_LIST_HEAD(&tmp);
+ spin_lock_irqsave(&pending_packets_lock, flags);
+ list_splice_init(&host->pending_packets, &tmp);
+ spin_unlock_irqrestore(&pending_packets_lock, flags);
+
+ list_for_each_entry_safe(packet, p, &tmp, queue) {
+ list_del_init(&packet->queue);
+ packet->state = hpsb_complete;
+ packet->ack_code = ACKX_ABORTED;
+ queue_packet_complete(packet);
+ }
+}
+
+void abort_timedouts(unsigned long __opaque)
+{
+ struct hpsb_host *host = (struct hpsb_host *)__opaque;
+ struct hpsb_packet *packet, *p;
+ struct list_head tmp;
+ unsigned long flags, expire, j;
+
+ spin_lock_irqsave(&host->csr.lock, flags);
+ expire = host->csr.expire;
+ spin_unlock_irqrestore(&host->csr.lock, flags);
+
+ j = jiffies;
+ INIT_LIST_HEAD(&tmp);
+ spin_lock_irqsave(&pending_packets_lock, flags);
+
+ list_for_each_entry_safe(packet, p, &host->pending_packets, queue) {
+ if (time_before(packet->sendtime + expire, j))
+ list_move_tail(&packet->queue, &tmp);
+ else
+ /* Since packets are added to the tail, the oldest
+ * ones are first, always. When we get to one that
+ * isn't timed out, the rest aren't either. */
+ break;
+ }
+ if (!list_empty(&host->pending_packets))
+ mod_timer(&host->timeout, j + host->timeout_interval);
+
+ spin_unlock_irqrestore(&pending_packets_lock, flags);
+
+ list_for_each_entry_safe(packet, p, &tmp, queue) {
+ list_del_init(&packet->queue);
+ packet->state = hpsb_complete;
+ packet->ack_code = ACKX_TIMEOUT;
+ queue_packet_complete(packet);
+ }
+}
+
+static struct task_struct *khpsbpkt_thread;
+static LIST_HEAD(hpsbpkt_queue);
+
+static void queue_packet_complete(struct hpsb_packet *packet)
+{
+ unsigned long flags;
+
+ if (packet->no_waiter) {
+ hpsb_free_packet(packet);
+ return;
+ }
+ if (packet->complete_routine != NULL) {
+ spin_lock_irqsave(&pending_packets_lock, flags);
+ list_add_tail(&packet->queue, &hpsbpkt_queue);
+ spin_unlock_irqrestore(&pending_packets_lock, flags);
+ wake_up_process(khpsbpkt_thread);
+ }
+ return;
+}
+
+/*
+ * Kernel thread which handles packets that are completed. This way the
+ * packet's "complete" function is asynchronously run in process context.
+ * Only packets which have a "complete" function may be sent here.
+ */
+static int hpsbpkt_thread(void *__hi)
+{
+ struct hpsb_packet *packet, *p;
+ struct list_head tmp;
+ int may_schedule;
+
+ while (!kthread_should_stop()) {
+
+ INIT_LIST_HEAD(&tmp);
+ spin_lock_irq(&pending_packets_lock);
+ list_splice_init(&hpsbpkt_queue, &tmp);
+ spin_unlock_irq(&pending_packets_lock);
+
+ list_for_each_entry_safe(packet, p, &tmp, queue) {
+ list_del_init(&packet->queue);
+ packet->complete_routine(packet->complete_data);
+ }
+
+ set_current_state(TASK_INTERRUPTIBLE);
+ spin_lock_irq(&pending_packets_lock);
+ may_schedule = list_empty(&hpsbpkt_queue);
+ spin_unlock_irq(&pending_packets_lock);
+ if (may_schedule)
+ schedule();
+ __set_current_state(TASK_RUNNING);
+ }
+ return 0;
+}
+
+static int __init ieee1394_init(void)
+{
+ int i, ret;
+
+ /* non-fatal error */
+ if (hpsb_init_config_roms()) {
+ HPSB_ERR("Failed to initialize some config rom entries.\n");
+ HPSB_ERR("Some features may not be available\n");
+ }
+
+ khpsbpkt_thread = kthread_run(hpsbpkt_thread, NULL, "khpsbpkt");
+ if (IS_ERR(khpsbpkt_thread)) {
+ HPSB_ERR("Failed to start hpsbpkt thread!\n");
+ ret = PTR_ERR(khpsbpkt_thread);
+ goto exit_cleanup_config_roms;
+ }
+
+ if (register_chrdev_region(IEEE1394_CORE_DEV, 256, "ieee1394")) {
+ HPSB_ERR("unable to register character device major %d!\n", IEEE1394_MAJOR);
+ ret = -ENODEV;
+ goto exit_release_kernel_thread;
+ }
+
+ ret = bus_register(&ieee1394_bus_type);
+ if (ret < 0) {
+ HPSB_INFO("bus register failed");
+ goto release_chrdev;
+ }
+
+ for (i = 0; fw_bus_attrs[i]; i++) {
+ ret = bus_create_file(&ieee1394_bus_type, fw_bus_attrs[i]);
+ if (ret < 0) {
+ while (i >= 0) {
+ bus_remove_file(&ieee1394_bus_type,
+ fw_bus_attrs[i--]);
+ }
+ bus_unregister(&ieee1394_bus_type);
+ goto release_chrdev;
+ }
+ }
+
+ ret = class_register(&hpsb_host_class);
+ if (ret < 0)
+ goto release_all_bus;
+
+ hpsb_protocol_class = class_create(THIS_MODULE, "ieee1394_protocol");
+ if (IS_ERR(hpsb_protocol_class)) {
+ ret = PTR_ERR(hpsb_protocol_class);
+ goto release_class_host;
+ }
+
+ ret = init_csr();
+ if (ret) {
+ HPSB_INFO("init csr failed");
+ ret = -ENOMEM;
+ goto release_class_protocol;
+ }
+
+ if (disable_nodemgr) {
+ HPSB_INFO("nodemgr and IRM functionality disabled");
+ /* We shouldn't contend for IRM with nodemgr disabled, since
+ nodemgr implements functionality required of ieee1394a-2000
+ IRMs */
+ hpsb_disable_irm = 1;
+
+ return 0;
+ }
+
+ if (hpsb_disable_irm) {
+ HPSB_INFO("IRM functionality disabled");
+ }
+
+ ret = init_ieee1394_nodemgr();
+ if (ret < 0) {
+ HPSB_INFO("init nodemgr failed");
+ goto cleanup_csr;
+ }
+
+ return 0;
+
+cleanup_csr:
+ cleanup_csr();
+release_class_protocol:
+ class_destroy(hpsb_protocol_class);
+release_class_host:
+ class_unregister(&hpsb_host_class);
+release_all_bus:
+ for (i = 0; fw_bus_attrs[i]; i++)
+ bus_remove_file(&ieee1394_bus_type, fw_bus_attrs[i]);
+ bus_unregister(&ieee1394_bus_type);
+release_chrdev:
+ unregister_chrdev_region(IEEE1394_CORE_DEV, 256);
+exit_release_kernel_thread:
+ kthread_stop(khpsbpkt_thread);
+exit_cleanup_config_roms:
+ hpsb_cleanup_config_roms();
+ return ret;
+}
+
+static void __exit ieee1394_cleanup(void)
+{
+ int i;
+
+ if (!disable_nodemgr)
+ cleanup_ieee1394_nodemgr();
+
+ cleanup_csr();
+
+ class_destroy(hpsb_protocol_class);
+ class_unregister(&hpsb_host_class);
+ for (i = 0; fw_bus_attrs[i]; i++)
+ bus_remove_file(&ieee1394_bus_type, fw_bus_attrs[i]);
+ bus_unregister(&ieee1394_bus_type);
+
+ kthread_stop(khpsbpkt_thread);
+
+ hpsb_cleanup_config_roms();
+
+ unregister_chrdev_region(IEEE1394_CORE_DEV, 256);
+}
+
+module_init(ieee1394_init);
+module_exit(ieee1394_cleanup);
+
+/* Exported symbols */
+
+/** hosts.c **/
+EXPORT_SYMBOL(hpsb_alloc_host);
+EXPORT_SYMBOL(hpsb_add_host);
+EXPORT_SYMBOL(hpsb_resume_host);
+EXPORT_SYMBOL(hpsb_remove_host);
+EXPORT_SYMBOL(hpsb_update_config_rom_image);
+
+/** ieee1394_core.c **/
+EXPORT_SYMBOL(hpsb_speedto_str);
+EXPORT_SYMBOL(hpsb_protocol_class);
+EXPORT_SYMBOL(hpsb_set_packet_complete_task);
+EXPORT_SYMBOL(hpsb_alloc_packet);
+EXPORT_SYMBOL(hpsb_free_packet);
+EXPORT_SYMBOL(hpsb_send_packet);
+EXPORT_SYMBOL(hpsb_reset_bus);
+EXPORT_SYMBOL(hpsb_read_cycle_timer);
+EXPORT_SYMBOL(hpsb_bus_reset);
+EXPORT_SYMBOL(hpsb_selfid_received);
+EXPORT_SYMBOL(hpsb_selfid_complete);
+EXPORT_SYMBOL(hpsb_packet_sent);
+EXPORT_SYMBOL(hpsb_packet_received);
+EXPORT_SYMBOL_GPL(hpsb_disable_irm);
+
+/** ieee1394_transactions.c **/
+EXPORT_SYMBOL(hpsb_get_tlabel);
+EXPORT_SYMBOL(hpsb_free_tlabel);
+EXPORT_SYMBOL(hpsb_make_readpacket);
+EXPORT_SYMBOL(hpsb_make_writepacket);
+EXPORT_SYMBOL(hpsb_make_streampacket);
+EXPORT_SYMBOL(hpsb_make_lockpacket);
+EXPORT_SYMBOL(hpsb_make_lock64packet);
+EXPORT_SYMBOL(hpsb_make_phypacket);
+EXPORT_SYMBOL(hpsb_read);
+EXPORT_SYMBOL(hpsb_write);
+EXPORT_SYMBOL(hpsb_packet_success);
+
+/** highlevel.c **/
+EXPORT_SYMBOL(hpsb_register_highlevel);
+EXPORT_SYMBOL(hpsb_unregister_highlevel);
+EXPORT_SYMBOL(hpsb_register_addrspace);
+EXPORT_SYMBOL(hpsb_unregister_addrspace);
+EXPORT_SYMBOL(hpsb_allocate_and_register_addrspace);
+EXPORT_SYMBOL(hpsb_get_hostinfo);
+EXPORT_SYMBOL(hpsb_create_hostinfo);
+EXPORT_SYMBOL(hpsb_destroy_hostinfo);
+EXPORT_SYMBOL(hpsb_set_hostinfo_key);
+EXPORT_SYMBOL(hpsb_get_hostinfo_bykey);
+EXPORT_SYMBOL(hpsb_set_hostinfo);
+
+/** nodemgr.c **/
+EXPORT_SYMBOL(hpsb_node_fill_packet);
+EXPORT_SYMBOL(hpsb_node_write);
+EXPORT_SYMBOL(__hpsb_register_protocol);
+EXPORT_SYMBOL(hpsb_unregister_protocol);
+
+/** csr.c **/
+EXPORT_SYMBOL(hpsb_update_config_rom);
+
+/** dma.c **/
+EXPORT_SYMBOL(dma_prog_region_init);
+EXPORT_SYMBOL(dma_prog_region_alloc);
+EXPORT_SYMBOL(dma_prog_region_free);
+EXPORT_SYMBOL(dma_region_init);
+EXPORT_SYMBOL(dma_region_alloc);
+EXPORT_SYMBOL(dma_region_free);
+EXPORT_SYMBOL(dma_region_sync_for_cpu);
+EXPORT_SYMBOL(dma_region_sync_for_device);
+EXPORT_SYMBOL(dma_region_mmap);
+EXPORT_SYMBOL(dma_region_offset_to_bus);
+
+/** iso.c **/
+EXPORT_SYMBOL(hpsb_iso_xmit_init);
+EXPORT_SYMBOL(hpsb_iso_recv_init);
+EXPORT_SYMBOL(hpsb_iso_xmit_start);
+EXPORT_SYMBOL(hpsb_iso_recv_start);
+EXPORT_SYMBOL(hpsb_iso_recv_listen_channel);
+EXPORT_SYMBOL(hpsb_iso_recv_unlisten_channel);
+EXPORT_SYMBOL(hpsb_iso_recv_set_channel_mask);
+EXPORT_SYMBOL(hpsb_iso_stop);
+EXPORT_SYMBOL(hpsb_iso_shutdown);
+EXPORT_SYMBOL(hpsb_iso_xmit_queue_packet);
+EXPORT_SYMBOL(hpsb_iso_xmit_sync);
+EXPORT_SYMBOL(hpsb_iso_recv_release_packets);
+EXPORT_SYMBOL(hpsb_iso_n_ready);
+EXPORT_SYMBOL(hpsb_iso_packet_sent);
+EXPORT_SYMBOL(hpsb_iso_packet_received);
+EXPORT_SYMBOL(hpsb_iso_wake);
+EXPORT_SYMBOL(hpsb_iso_recv_flush);
+
+/** csr1212.c **/
+EXPORT_SYMBOL(csr1212_attach_keyval_to_directory);
+EXPORT_SYMBOL(csr1212_detach_keyval_from_directory);
+EXPORT_SYMBOL(csr1212_get_keyval);
+EXPORT_SYMBOL(csr1212_new_directory);
+EXPORT_SYMBOL(csr1212_parse_keyval);
+EXPORT_SYMBOL(csr1212_read);
+EXPORT_SYMBOL(csr1212_release_keyval);
diff --git a/drivers/ieee1394/ieee1394_core.h b/drivers/ieee1394/ieee1394_core.h
new file mode 100644
index 0000000..21d50f7
--- /dev/null
+++ b/drivers/ieee1394/ieee1394_core.h
@@ -0,0 +1,168 @@
+#ifndef _IEEE1394_CORE_H
+#define _IEEE1394_CORE_H
+
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/list.h>
+#include <linux/types.h>
+#include <asm/atomic.h>
+
+#include "hosts.h"
+#include "ieee1394_types.h"
+
+struct hpsb_packet {
+ /* This struct is basically read-only for hosts with the exception of
+ * the data buffer contents and driver_list. */
+
+ /* This can be used for host driver internal linking.
+ *
+ * NOTE: This must be left in init state when the driver is done
+ * with it (e.g. by using list_del_init()), since the core does
+ * some sanity checks to make sure the packet is not on a
+ * driver_list when free'ing it. */
+ struct list_head driver_list;
+
+ nodeid_t node_id;
+
+ /* hpsb_raw = send as-is, do not CRC (but still byte-swap it) */
+ enum { hpsb_async, hpsb_raw } __attribute__((packed)) type;
+
+ /* Okay, this is core internal and a no care for hosts.
+ * queued = queued for sending
+ * pending = sent, waiting for response
+ * complete = processing completed, successful or not
+ */
+ enum {
+ hpsb_unused, hpsb_queued, hpsb_pending, hpsb_complete
+ } __attribute__((packed)) state;
+
+ /* These are core-internal. */
+ signed char tlabel;
+ signed char ack_code;
+ unsigned char tcode;
+
+ unsigned expect_response:1;
+ unsigned no_waiter:1;
+
+ /* Speed to transmit with: 0 = 100Mbps, 1 = 200Mbps, 2 = 400Mbps */
+ unsigned speed_code:2;
+
+ struct hpsb_host *host;
+ unsigned int generation;
+
+ atomic_t refcnt;
+ struct list_head queue;
+
+ /* Function (and possible data to pass to it) to call when this
+ * packet is completed. */
+ void (*complete_routine)(void *);
+ void *complete_data;
+
+ /* Store jiffies for implementing bus timeouts. */
+ unsigned long sendtime;
+
+ /* Core-internal. */
+ size_t allocated_data_size; /* as allocated */
+
+ /* Sizes are in bytes. To be set by caller of hpsb_alloc_packet. */
+ size_t data_size; /* as filled in */
+ size_t header_size; /* as filled in, not counting the CRC */
+
+ /* Buffers */
+ quadlet_t *data; /* can be DMA-mapped */
+ quadlet_t header[5];
+ quadlet_t embedded_data[0]; /* keep as last member */
+};
+
+void hpsb_set_packet_complete_task(struct hpsb_packet *packet,
+ void (*routine)(void *), void *data);
+static inline struct hpsb_packet *driver_packet(struct list_head *l)
+{
+ return list_entry(l, struct hpsb_packet, driver_list);
+}
+void abort_timedouts(unsigned long __opaque);
+struct hpsb_packet *hpsb_alloc_packet(size_t data_size);
+void hpsb_free_packet(struct hpsb_packet *packet);
+
+/**
+ * get_hpsb_generation - generation counter for the complete 1394 subsystem
+ *
+ * Generation gets incremented on every change in the subsystem (notably on bus
+ * resets). Use the functions, not the variable.
+ */
+static inline unsigned int get_hpsb_generation(struct hpsb_host *host)
+{
+ return atomic_read(&host->generation);
+}
+
+int hpsb_send_phy_config(struct hpsb_host *host, int rootid, int gapcnt);
+int hpsb_send_packet(struct hpsb_packet *packet);
+int hpsb_send_packet_and_wait(struct hpsb_packet *packet);
+int hpsb_reset_bus(struct hpsb_host *host, int type);
+int hpsb_read_cycle_timer(struct hpsb_host *host, u32 *cycle_timer,
+ u64 *local_time);
+
+int hpsb_bus_reset(struct hpsb_host *host);
+void hpsb_selfid_received(struct hpsb_host *host, quadlet_t sid);
+void hpsb_selfid_complete(struct hpsb_host *host, int phyid, int isroot);
+void hpsb_packet_sent(struct hpsb_host *host, struct hpsb_packet *packet,
+ int ackcode);
+void hpsb_packet_received(struct hpsb_host *host, quadlet_t *data, size_t size,
+ int write_acked);
+
+/*
+ * CHARACTER DEVICE DISPATCHING
+ *
+ * All ieee1394 character device drivers share the same major number
+ * (major 171). The 256 minor numbers are allocated to the various
+ * task-specific interfaces (raw1394, video1394, dv1394, etc) in
+ * blocks of 16.
+ *
+ * The core ieee1394.o module allocates the device number region
+ * 171:0-255, the various drivers must then cdev_add() their cdev
+ * objects to handle their respective sub-regions.
+ *
+ * Minor device number block allocations:
+ *
+ * Block 0 ( 0- 15) raw1394
+ * Block 1 ( 16- 31) video1394
+ * Block 2 ( 32- 47) dv1394
+ *
+ * Blocks 3-14 free for future allocation
+ *
+ * Block 15 (240-255) reserved for drivers under development, etc.
+ */
+
+#define IEEE1394_MAJOR 171
+
+#define IEEE1394_MINOR_BLOCK_RAW1394 0
+#define IEEE1394_MINOR_BLOCK_VIDEO1394 1
+#define IEEE1394_MINOR_BLOCK_DV1394 2
+#define IEEE1394_MINOR_BLOCK_EXPERIMENTAL 15
+
+#define IEEE1394_CORE_DEV MKDEV(IEEE1394_MAJOR, 0)
+#define IEEE1394_RAW1394_DEV MKDEV(IEEE1394_MAJOR, \
+ IEEE1394_MINOR_BLOCK_RAW1394 * 16)
+#define IEEE1394_VIDEO1394_DEV MKDEV(IEEE1394_MAJOR, \
+ IEEE1394_MINOR_BLOCK_VIDEO1394 * 16)
+#define IEEE1394_DV1394_DEV MKDEV(IEEE1394_MAJOR, \
+ IEEE1394_MINOR_BLOCK_DV1394 * 16)
+#define IEEE1394_EXPERIMENTAL_DEV MKDEV(IEEE1394_MAJOR, \
+ IEEE1394_MINOR_BLOCK_EXPERIMENTAL * 16)
+
+/**
+ * ieee1394_file_to_instance - get the index within a minor number block
+ */
+static inline unsigned char ieee1394_file_to_instance(struct file *file)
+{
+ return file->f_path.dentry->d_inode->i_cindex;
+}
+
+extern int hpsb_disable_irm;
+
+/* Our sysfs bus entry */
+extern struct bus_type ieee1394_bus_type;
+extern struct class hpsb_host_class;
+extern struct class *hpsb_protocol_class;
+
+#endif /* _IEEE1394_CORE_H */
diff --git a/drivers/ieee1394/ieee1394_hotplug.h b/drivers/ieee1394/ieee1394_hotplug.h
new file mode 100644
index 0000000..dd5500e
--- /dev/null
+++ b/drivers/ieee1394/ieee1394_hotplug.h
@@ -0,0 +1,19 @@
+#ifndef _IEEE1394_HOTPLUG_H
+#define _IEEE1394_HOTPLUG_H
+
+/* Unit spec id and sw version entry for some protocols */
+#define AVC_UNIT_SPEC_ID_ENTRY 0x0000A02D
+#define AVC_SW_VERSION_ENTRY 0x00010001
+#define CAMERA_UNIT_SPEC_ID_ENTRY 0x0000A02D
+#define CAMERA_SW_VERSION_ENTRY 0x00000100
+
+/* /include/linux/mod_devicetable.h defines:
+ * IEEE1394_MATCH_VENDOR_ID
+ * IEEE1394_MATCH_MODEL_ID
+ * IEEE1394_MATCH_SPECIFIER_ID
+ * IEEE1394_MATCH_VERSION
+ * struct ieee1394_device_id
+ */
+#include <linux/mod_devicetable.h>
+
+#endif /* _IEEE1394_HOTPLUG_H */
diff --git a/drivers/ieee1394/ieee1394_transactions.c b/drivers/ieee1394/ieee1394_transactions.c
new file mode 100644
index 0000000..10c3d9f
--- /dev/null
+++ b/drivers/ieee1394/ieee1394_transactions.c
@@ -0,0 +1,572 @@
+/*
+ * IEEE 1394 for Linux
+ *
+ * Transaction support.
+ *
+ * Copyright (C) 1999 Andreas E. Bombe
+ *
+ * This code is licensed under the GPL. See the file COPYING in the root
+ * directory of the kernel sources for details.
+ */
+
+#include <linux/bitops.h>
+#include <linux/compiler.h>
+#include <linux/hardirq.h>
+#include <linux/spinlock.h>
+#include <linux/string.h>
+#include <linux/sched.h> /* because linux/wait.h is broken if CONFIG_SMP=n */
+#include <linux/wait.h>
+
+#include <asm/bug.h>
+#include <asm/errno.h>
+#include <asm/system.h>
+
+#include "ieee1394.h"
+#include "ieee1394_types.h"
+#include "hosts.h"
+#include "ieee1394_core.h"
+#include "ieee1394_transactions.h"
+
+#define PREP_ASYNC_HEAD_ADDRESS(tc) \
+ packet->tcode = tc; \
+ packet->header[0] = (packet->node_id << 16) | (packet->tlabel << 10) \
+ | (1 << 8) | (tc << 4); \
+ packet->header[1] = (packet->host->node_id << 16) | (addr >> 32); \
+ packet->header[2] = addr & 0xffffffff
+
+#ifndef HPSB_DEBUG_TLABELS
+static
+#endif
+DEFINE_SPINLOCK(hpsb_tlabel_lock);
+
+static DECLARE_WAIT_QUEUE_HEAD(tlabel_wq);
+
+static void fill_async_readquad(struct hpsb_packet *packet, u64 addr)
+{
+ PREP_ASYNC_HEAD_ADDRESS(TCODE_READQ);
+ packet->header_size = 12;
+ packet->data_size = 0;
+ packet->expect_response = 1;
+}
+
+static void fill_async_readblock(struct hpsb_packet *packet, u64 addr,
+ int length)
+{
+ PREP_ASYNC_HEAD_ADDRESS(TCODE_READB);
+ packet->header[3] = length << 16;
+ packet->header_size = 16;
+ packet->data_size = 0;
+ packet->expect_response = 1;
+}
+
+static void fill_async_writequad(struct hpsb_packet *packet, u64 addr,
+ quadlet_t data)
+{
+ PREP_ASYNC_HEAD_ADDRESS(TCODE_WRITEQ);
+ packet->header[3] = data;
+ packet->header_size = 16;
+ packet->data_size = 0;
+ packet->expect_response = 1;
+}
+
+static void fill_async_writeblock(struct hpsb_packet *packet, u64 addr,
+ int length)
+{
+ PREP_ASYNC_HEAD_ADDRESS(TCODE_WRITEB);
+ packet->header[3] = length << 16;
+ packet->header_size = 16;
+ packet->expect_response = 1;
+ packet->data_size = length + (length % 4 ? 4 - (length % 4) : 0);
+}
+
+static void fill_async_lock(struct hpsb_packet *packet, u64 addr, int extcode,
+ int length)
+{
+ PREP_ASYNC_HEAD_ADDRESS(TCODE_LOCK_REQUEST);
+ packet->header[3] = (length << 16) | extcode;
+ packet->header_size = 16;
+ packet->data_size = length;
+ packet->expect_response = 1;
+}
+
+static void fill_phy_packet(struct hpsb_packet *packet, quadlet_t data)
+{
+ packet->header[0] = data;
+ packet->header[1] = ~data;
+ packet->header_size = 8;
+ packet->data_size = 0;
+ packet->expect_response = 0;
+ packet->type = hpsb_raw; /* No CRC added */
+ packet->speed_code = IEEE1394_SPEED_100; /* Force speed to be 100Mbps */
+}
+
+static void fill_async_stream_packet(struct hpsb_packet *packet, int length,
+ int channel, int tag, int sync)
+{
+ packet->header[0] = (length << 16) | (tag << 14) | (channel << 8)
+ | (TCODE_STREAM_DATA << 4) | sync;
+
+ packet->header_size = 4;
+ packet->data_size = length;
+ packet->type = hpsb_async;
+ packet->tcode = TCODE_ISO_DATA;
+}
+
+/* same as hpsb_get_tlabel, except that it returns immediately */
+static int hpsb_get_tlabel_atomic(struct hpsb_packet *packet)
+{
+ unsigned long flags, *tp;
+ u8 *next;
+ int tlabel, n = NODEID_TO_NODE(packet->node_id);
+
+ /* Broadcast transactions are complete once the request has been sent.
+ * Use the same transaction label for all broadcast transactions. */
+ if (unlikely(n == ALL_NODES)) {
+ packet->tlabel = 0;
+ return 0;
+ }
+ tp = packet->host->tl_pool[n].map;
+ next = &packet->host->next_tl[n];
+
+ spin_lock_irqsave(&hpsb_tlabel_lock, flags);
+ tlabel = find_next_zero_bit(tp, 64, *next);
+ if (tlabel > 63)
+ tlabel = find_first_zero_bit(tp, 64);
+ if (tlabel > 63) {
+ spin_unlock_irqrestore(&hpsb_tlabel_lock, flags);
+ return -EAGAIN;
+ }
+ __set_bit(tlabel, tp);
+ *next = (tlabel + 1) & 63;
+ spin_unlock_irqrestore(&hpsb_tlabel_lock, flags);
+
+ packet->tlabel = tlabel;
+ return 0;
+}
+
+/**
+ * hpsb_get_tlabel - allocate a transaction label
+ * @packet: the packet whose tlabel and tl_pool we set
+ *
+ * Every asynchronous transaction on the 1394 bus needs a transaction
+ * label to match the response to the request. This label has to be
+ * different from any other transaction label in an outstanding request to
+ * the same node to make matching possible without ambiguity.
+ *
+ * There are 64 different tlabels, so an allocated tlabel has to be freed
+ * with hpsb_free_tlabel() after the transaction is complete (unless it's
+ * reused again for the same target node).
+ *
+ * Return value: Zero on success, otherwise non-zero. A non-zero return
+ * generally means there are no available tlabels. If this is called out
+ * of interrupt or atomic context, then it will sleep until can return a
+ * tlabel or a signal is received.
+ */
+int hpsb_get_tlabel(struct hpsb_packet *packet)
+{
+ if (irqs_disabled() || in_atomic())
+ return hpsb_get_tlabel_atomic(packet);
+
+ /* NB: The macro wait_event_interruptible() is called with a condition
+ * argument with side effect. This is only possible because the side
+ * effect does not occur until the condition became true, and
+ * wait_event_interruptible() won't evaluate the condition again after
+ * that. */
+ return wait_event_interruptible(tlabel_wq,
+ !hpsb_get_tlabel_atomic(packet));
+}
+
+/**
+ * hpsb_free_tlabel - free an allocated transaction label
+ * @packet: packet whose tlabel and tl_pool needs to be cleared
+ *
+ * Frees the transaction label allocated with hpsb_get_tlabel(). The
+ * tlabel has to be freed after the transaction is complete (i.e. response
+ * was received for a split transaction or packet was sent for a unified
+ * transaction).
+ *
+ * A tlabel must not be freed twice.
+ */
+void hpsb_free_tlabel(struct hpsb_packet *packet)
+{
+ unsigned long flags, *tp;
+ int tlabel, n = NODEID_TO_NODE(packet->node_id);
+
+ if (unlikely(n == ALL_NODES))
+ return;
+ tp = packet->host->tl_pool[n].map;
+ tlabel = packet->tlabel;
+ BUG_ON(tlabel > 63 || tlabel < 0);
+
+ spin_lock_irqsave(&hpsb_tlabel_lock, flags);
+ BUG_ON(!__test_and_clear_bit(tlabel, tp));
+ spin_unlock_irqrestore(&hpsb_tlabel_lock, flags);
+
+ wake_up_interruptible(&tlabel_wq);
+}
+
+/**
+ * hpsb_packet_success - Make sense of the ack and reply codes
+ *
+ * Make sense of the ack and reply codes and return more convenient error codes:
+ * 0 = success. -%EBUSY = node is busy, try again. -%EAGAIN = error which can
+ * probably resolved by retry. -%EREMOTEIO = node suffers from an internal
+ * error. -%EACCES = this transaction is not allowed on requested address.
+ * -%EINVAL = invalid address at node.
+ */
+int hpsb_packet_success(struct hpsb_packet *packet)
+{
+ switch (packet->ack_code) {
+ case ACK_PENDING:
+ switch ((packet->header[1] >> 12) & 0xf) {
+ case RCODE_COMPLETE:
+ return 0;
+ case RCODE_CONFLICT_ERROR:
+ return -EAGAIN;
+ case RCODE_DATA_ERROR:
+ return -EREMOTEIO;
+ case RCODE_TYPE_ERROR:
+ return -EACCES;
+ case RCODE_ADDRESS_ERROR:
+ return -EINVAL;
+ default:
+ HPSB_ERR("received reserved rcode %d from node %d",
+ (packet->header[1] >> 12) & 0xf,
+ packet->node_id);
+ return -EAGAIN;
+ }
+
+ case ACK_BUSY_X:
+ case ACK_BUSY_A:
+ case ACK_BUSY_B:
+ return -EBUSY;
+
+ case ACK_TYPE_ERROR:
+ return -EACCES;
+
+ case ACK_COMPLETE:
+ if (packet->tcode == TCODE_WRITEQ
+ || packet->tcode == TCODE_WRITEB) {
+ return 0;
+ } else {
+ HPSB_ERR("impossible ack_complete from node %d "
+ "(tcode %d)", packet->node_id, packet->tcode);
+ return -EAGAIN;
+ }
+
+ case ACK_DATA_ERROR:
+ if (packet->tcode == TCODE_WRITEB
+ || packet->tcode == TCODE_LOCK_REQUEST) {
+ return -EAGAIN;
+ } else {
+ HPSB_ERR("impossible ack_data_error from node %d "
+ "(tcode %d)", packet->node_id, packet->tcode);
+ return -EAGAIN;
+ }
+
+ case ACK_ADDRESS_ERROR:
+ return -EINVAL;
+
+ case ACK_TARDY:
+ case ACK_CONFLICT_ERROR:
+ case ACKX_NONE:
+ case ACKX_SEND_ERROR:
+ case ACKX_ABORTED:
+ case ACKX_TIMEOUT:
+ /* error while sending */
+ return -EAGAIN;
+
+ default:
+ HPSB_ERR("got invalid ack %d from node %d (tcode %d)",
+ packet->ack_code, packet->node_id, packet->tcode);
+ return -EAGAIN;
+ }
+}
+
+struct hpsb_packet *hpsb_make_readpacket(struct hpsb_host *host, nodeid_t node,
+ u64 addr, size_t length)
+{
+ struct hpsb_packet *packet;
+
+ if (length == 0)
+ return NULL;
+
+ packet = hpsb_alloc_packet(length);
+ if (!packet)
+ return NULL;
+
+ packet->host = host;
+ packet->node_id = node;
+
+ if (hpsb_get_tlabel(packet)) {
+ hpsb_free_packet(packet);
+ return NULL;
+ }
+
+ if (length == 4)
+ fill_async_readquad(packet, addr);
+ else
+ fill_async_readblock(packet, addr, length);
+
+ return packet;
+}
+
+struct hpsb_packet *hpsb_make_writepacket(struct hpsb_host *host, nodeid_t node,
+ u64 addr, quadlet_t * buffer,
+ size_t length)
+{
+ struct hpsb_packet *packet;
+
+ if (length == 0)
+ return NULL;
+
+ packet = hpsb_alloc_packet(length);
+ if (!packet)
+ return NULL;
+
+ if (length % 4) { /* zero padding bytes */
+ packet->data[length >> 2] = 0;
+ }
+ packet->host = host;
+ packet->node_id = node;
+
+ if (hpsb_get_tlabel(packet)) {
+ hpsb_free_packet(packet);
+ return NULL;
+ }
+
+ if (length == 4) {
+ fill_async_writequad(packet, addr, buffer ? *buffer : 0);
+ } else {
+ fill_async_writeblock(packet, addr, length);
+ if (buffer)
+ memcpy(packet->data, buffer, length);
+ }
+
+ return packet;
+}
+
+struct hpsb_packet *hpsb_make_streampacket(struct hpsb_host *host, u8 * buffer,
+ int length, int channel, int tag,
+ int sync)
+{
+ struct hpsb_packet *packet;
+
+ if (length == 0)
+ return NULL;
+
+ packet = hpsb_alloc_packet(length);
+ if (!packet)
+ return NULL;
+
+ if (length % 4) { /* zero padding bytes */
+ packet->data[length >> 2] = 0;
+ }
+ packet->host = host;
+
+ /* Because it is too difficult to determine all PHY speeds and link
+ * speeds here, we use S100... */
+ packet->speed_code = IEEE1394_SPEED_100;
+
+ /* ...and prevent hpsb_send_packet() from overriding it. */
+ packet->node_id = LOCAL_BUS | ALL_NODES;
+
+ if (hpsb_get_tlabel(packet)) {
+ hpsb_free_packet(packet);
+ return NULL;
+ }
+
+ fill_async_stream_packet(packet, length, channel, tag, sync);
+ if (buffer)
+ memcpy(packet->data, buffer, length);
+
+ return packet;
+}
+
+struct hpsb_packet *hpsb_make_lockpacket(struct hpsb_host *host, nodeid_t node,
+ u64 addr, int extcode,
+ quadlet_t * data, quadlet_t arg)
+{
+ struct hpsb_packet *p;
+ u32 length;
+
+ p = hpsb_alloc_packet(8);
+ if (!p)
+ return NULL;
+
+ p->host = host;
+ p->node_id = node;
+ if (hpsb_get_tlabel(p)) {
+ hpsb_free_packet(p);
+ return NULL;
+ }
+
+ switch (extcode) {
+ case EXTCODE_FETCH_ADD:
+ case EXTCODE_LITTLE_ADD:
+ length = 4;
+ if (data)
+ p->data[0] = *data;
+ break;
+ default:
+ length = 8;
+ if (data) {
+ p->data[0] = arg;
+ p->data[1] = *data;
+ }
+ break;
+ }
+ fill_async_lock(p, addr, extcode, length);
+
+ return p;
+}
+
+struct hpsb_packet *hpsb_make_lock64packet(struct hpsb_host *host,
+ nodeid_t node, u64 addr, int extcode,
+ octlet_t * data, octlet_t arg)
+{
+ struct hpsb_packet *p;
+ u32 length;
+
+ p = hpsb_alloc_packet(16);
+ if (!p)
+ return NULL;
+
+ p->host = host;
+ p->node_id = node;
+ if (hpsb_get_tlabel(p)) {
+ hpsb_free_packet(p);
+ return NULL;
+ }
+
+ switch (extcode) {
+ case EXTCODE_FETCH_ADD:
+ case EXTCODE_LITTLE_ADD:
+ length = 8;
+ if (data) {
+ p->data[0] = *data >> 32;
+ p->data[1] = *data & 0xffffffff;
+ }
+ break;
+ default:
+ length = 16;
+ if (data) {
+ p->data[0] = arg >> 32;
+ p->data[1] = arg & 0xffffffff;
+ p->data[2] = *data >> 32;
+ p->data[3] = *data & 0xffffffff;
+ }
+ break;
+ }
+ fill_async_lock(p, addr, extcode, length);
+
+ return p;
+}
+
+struct hpsb_packet *hpsb_make_phypacket(struct hpsb_host *host, quadlet_t data)
+{
+ struct hpsb_packet *p;
+
+ p = hpsb_alloc_packet(0);
+ if (!p)
+ return NULL;
+
+ p->host = host;
+ fill_phy_packet(p, data);
+
+ return p;
+}
+
+/*
+ * FIXME - these functions should probably read from / write to user space to
+ * avoid in kernel buffers for user space callers
+ */
+
+/**
+ * hpsb_read - generic read function
+ *
+ * Recognizes the local node ID and act accordingly. Automatically uses a
+ * quadlet read request if @length == 4 and and a block read request otherwise.
+ * It does not yet support lengths that are not a multiple of 4.
+ *
+ * You must explicitly specifiy the @generation for which the node ID is valid,
+ * to avoid sending packets to the wrong nodes when we race with a bus reset.
+ */
+int hpsb_read(struct hpsb_host *host, nodeid_t node, unsigned int generation,
+ u64 addr, quadlet_t * buffer, size_t length)
+{
+ struct hpsb_packet *packet;
+ int retval = 0;
+
+ if (length == 0)
+ return -EINVAL;
+
+ BUG_ON(in_interrupt()); // We can't be called in an interrupt, yet
+
+ packet = hpsb_make_readpacket(host, node, addr, length);
+
+ if (!packet) {
+ return -ENOMEM;
+ }
+
+ packet->generation = generation;
+ retval = hpsb_send_packet_and_wait(packet);
+ if (retval < 0)
+ goto hpsb_read_fail;
+
+ retval = hpsb_packet_success(packet);
+
+ if (retval == 0) {
+ if (length == 4) {
+ *buffer = packet->header[3];
+ } else {
+ memcpy(buffer, packet->data, length);
+ }
+ }
+
+ hpsb_read_fail:
+ hpsb_free_tlabel(packet);
+ hpsb_free_packet(packet);
+
+ return retval;
+}
+
+/**
+ * hpsb_write - generic write function
+ *
+ * Recognizes the local node ID and act accordingly. Automatically uses a
+ * quadlet write request if @length == 4 and and a block write request
+ * otherwise. It does not yet support lengths that are not a multiple of 4.
+ *
+ * You must explicitly specifiy the @generation for which the node ID is valid,
+ * to avoid sending packets to the wrong nodes when we race with a bus reset.
+ */
+int hpsb_write(struct hpsb_host *host, nodeid_t node, unsigned int generation,
+ u64 addr, quadlet_t * buffer, size_t length)
+{
+ struct hpsb_packet *packet;
+ int retval;
+
+ if (length == 0)
+ return -EINVAL;
+
+ BUG_ON(in_interrupt()); // We can't be called in an interrupt, yet
+
+ packet = hpsb_make_writepacket(host, node, addr, buffer, length);
+
+ if (!packet)
+ return -ENOMEM;
+
+ packet->generation = generation;
+ retval = hpsb_send_packet_and_wait(packet);
+ if (retval < 0)
+ goto hpsb_write_fail;
+
+ retval = hpsb_packet_success(packet);
+
+ hpsb_write_fail:
+ hpsb_free_tlabel(packet);
+ hpsb_free_packet(packet);
+
+ return retval;
+}
diff --git a/drivers/ieee1394/ieee1394_transactions.h b/drivers/ieee1394/ieee1394_transactions.h
new file mode 100644
index 0000000..d2d5bc3
--- /dev/null
+++ b/drivers/ieee1394/ieee1394_transactions.h
@@ -0,0 +1,38 @@
+#ifndef _IEEE1394_TRANSACTIONS_H
+#define _IEEE1394_TRANSACTIONS_H
+
+#include <linux/types.h>
+
+#include "ieee1394_types.h"
+
+struct hpsb_packet;
+struct hpsb_host;
+
+int hpsb_get_tlabel(struct hpsb_packet *packet);
+void hpsb_free_tlabel(struct hpsb_packet *packet);
+struct hpsb_packet *hpsb_make_readpacket(struct hpsb_host *host, nodeid_t node,
+ u64 addr, size_t length);
+struct hpsb_packet *hpsb_make_lockpacket(struct hpsb_host *host, nodeid_t node,
+ u64 addr, int extcode, quadlet_t *data,
+ quadlet_t arg);
+struct hpsb_packet *hpsb_make_lock64packet(struct hpsb_host *host,
+ nodeid_t node, u64 addr, int extcode,
+ octlet_t *data, octlet_t arg);
+struct hpsb_packet *hpsb_make_phypacket(struct hpsb_host *host, quadlet_t data);
+struct hpsb_packet *hpsb_make_writepacket(struct hpsb_host *host,
+ nodeid_t node, u64 addr,
+ quadlet_t *buffer, size_t length);
+struct hpsb_packet *hpsb_make_streampacket(struct hpsb_host *host, u8 *buffer,
+ int length, int channel, int tag,
+ int sync);
+int hpsb_packet_success(struct hpsb_packet *packet);
+int hpsb_read(struct hpsb_host *host, nodeid_t node, unsigned int generation,
+ u64 addr, quadlet_t *buffer, size_t length);
+int hpsb_write(struct hpsb_host *host, nodeid_t node, unsigned int generation,
+ u64 addr, quadlet_t *buffer, size_t length);
+
+#ifdef HPSB_DEBUG_TLABELS
+extern spinlock_t hpsb_tlabel_lock;
+#endif
+
+#endif /* _IEEE1394_TRANSACTIONS_H */
diff --git a/drivers/ieee1394/ieee1394_types.h b/drivers/ieee1394/ieee1394_types.h
new file mode 100644
index 0000000..9803aaa
--- /dev/null
+++ b/drivers/ieee1394/ieee1394_types.h
@@ -0,0 +1,69 @@
+#ifndef _IEEE1394_TYPES_H
+#define _IEEE1394_TYPES_H
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <asm/byteorder.h>
+
+typedef u32 quadlet_t;
+typedef u64 octlet_t;
+typedef u16 nodeid_t;
+
+typedef u8 byte_t;
+typedef u64 nodeaddr_t;
+typedef u16 arm_length_t;
+
+#define BUS_MASK 0xffc0
+#define BUS_SHIFT 6
+#define NODE_MASK 0x003f
+#define LOCAL_BUS 0xffc0
+#define ALL_NODES 0x003f
+
+#define NODEID_TO_BUS(nodeid) ((nodeid & BUS_MASK) >> BUS_SHIFT)
+#define NODEID_TO_NODE(nodeid) (nodeid & NODE_MASK)
+
+/* Can be used to consistently print a node/bus ID. */
+#define NODE_BUS_FMT "%d-%02d:%04d"
+#define NODE_BUS_ARGS(__host, __nodeid) \
+ __host->id, NODEID_TO_NODE(__nodeid), NODEID_TO_BUS(__nodeid)
+
+#define HPSB_PRINT(level, fmt, args...) \
+ printk(level "ieee1394: " fmt "\n" , ## args)
+
+#define HPSB_DEBUG(fmt, args...) HPSB_PRINT(KERN_DEBUG, fmt , ## args)
+#define HPSB_INFO(fmt, args...) HPSB_PRINT(KERN_INFO, fmt , ## args)
+#define HPSB_NOTICE(fmt, args...) HPSB_PRINT(KERN_NOTICE, fmt , ## args)
+#define HPSB_WARN(fmt, args...) HPSB_PRINT(KERN_WARNING, fmt , ## args)
+#define HPSB_ERR(fmt, args...) HPSB_PRINT(KERN_ERR, fmt , ## args)
+
+#ifdef CONFIG_IEEE1394_VERBOSEDEBUG
+#define HPSB_VERBOSE(fmt, args...) HPSB_PRINT(KERN_DEBUG, fmt , ## args)
+#define HPSB_DEBUG_TLABELS
+#else
+#define HPSB_VERBOSE(fmt, args...) do {} while (0)
+#endif
+
+#ifdef __BIG_ENDIAN
+
+static inline void *memcpy_le32(u32 *dest, const u32 *__src, size_t count)
+{
+ void *tmp = dest;
+ u32 *src = (u32 *)__src;
+
+ count /= 4;
+ while (count--)
+ *dest++ = swab32p(src++);
+ return tmp;
+}
+
+#else
+
+static __inline__ void *memcpy_le32(u32 *dest, const u32 *src, size_t count)
+{
+ return memcpy(dest, src, count);
+}
+
+#endif /* __BIG_ENDIAN */
+
+#endif /* _IEEE1394_TYPES_H */
diff --git a/drivers/ieee1394/init_ohci1394_dma.c b/drivers/ieee1394/init_ohci1394_dma.c
new file mode 100644
index 0000000..ddaab6e
--- /dev/null
+++ b/drivers/ieee1394/init_ohci1394_dma.c
@@ -0,0 +1,285 @@
+/*
+ * init_ohci1394_dma.c - Initializes physical DMA on all OHCI 1394 controllers
+ *
+ * Copyright (C) 2006-2007 Bernhard Kaindl <bk@suse.de>
+ *
+ * Derived from drivers/ieee1394/ohci1394.c and arch/x86/kernel/early-quirks.c
+ * this file has functions to:
+ * - scan the PCI very early on boot for all OHCI 1394-compliant controllers
+ * - reset and initialize them and make them join the IEEE1394 bus and
+ * - enable physical DMA on them to allow remote debugging
+ *
+ * All code and data is marked as __init and __initdata, respective as
+ * during boot, all OHCI1394 controllers may be claimed by the firewire
+ * stack and at this point, this code should not touch them anymore.
+ *
+ * To use physical DMA after the initialization of the firewire stack,
+ * be sure that the stack enables it and (re-)attach after the bus reset
+ * which may be caused by the firewire stack initialization.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <linux/interrupt.h> /* for ohci1394.h */
+#include <linux/delay.h>
+#include <linux/pci.h> /* for PCI defines */
+#include <linux/init_ohci1394_dma.h>
+#include <asm/pci-direct.h> /* for direct PCI config space access */
+#include <asm/fixmap.h>
+
+#include "ieee1394_types.h"
+#include "ohci1394.h"
+
+int __initdata init_ohci1394_dma_early;
+
+/* Reads a PHY register of an OHCI-1394 controller */
+static inline u8 __init get_phy_reg(struct ti_ohci *ohci, u8 addr)
+{
+ int i;
+ quadlet_t r;
+
+ reg_write(ohci, OHCI1394_PhyControl, (addr << 8) | 0x00008000);
+
+ for (i = 0; i < OHCI_LOOP_COUNT; i++) {
+ if (reg_read(ohci, OHCI1394_PhyControl) & 0x80000000)
+ break;
+ mdelay(1);
+ }
+ r = reg_read(ohci, OHCI1394_PhyControl);
+
+ return (r & 0x00ff0000) >> 16;
+}
+
+/* Writes to a PHY register of an OHCI-1394 controller */
+static inline void __init set_phy_reg(struct ti_ohci *ohci, u8 addr, u8 data)
+{
+ int i;
+
+ reg_write(ohci, OHCI1394_PhyControl, (addr << 8) | data | 0x00004000);
+
+ for (i = 0; i < OHCI_LOOP_COUNT; i++) {
+ u32 r = reg_read(ohci, OHCI1394_PhyControl);
+ if (!(r & 0x00004000))
+ break;
+ mdelay(1);
+ }
+}
+
+/* Resets an OHCI-1394 controller (for sane state before initialization) */
+static inline void __init init_ohci1394_soft_reset(struct ti_ohci *ohci) {
+ int i;
+
+ reg_write(ohci, OHCI1394_HCControlSet, OHCI1394_HCControl_softReset);
+
+ for (i = 0; i < OHCI_LOOP_COUNT; i++) {
+ if (!(reg_read(ohci, OHCI1394_HCControlSet)
+ & OHCI1394_HCControl_softReset))
+ break;
+ mdelay(1);
+ }
+}
+
+/* Basic OHCI-1394 register and port inititalization */
+static inline void __init init_ohci1394_initialize(struct ti_ohci *ohci)
+{
+ quadlet_t bus_options;
+ int num_ports, i;
+
+ /* Put some defaults to these undefined bus options */
+ bus_options = reg_read(ohci, OHCI1394_BusOptions);
+ bus_options |= 0x60000000; /* Enable CMC and ISC */
+ bus_options &= ~0x00ff0000; /* XXX: Set cyc_clk_acc to zero for now */
+ bus_options &= ~0x18000000; /* Disable PMC and BMC */
+ reg_write(ohci, OHCI1394_BusOptions, bus_options);
+
+ /* Set the bus number */
+ reg_write(ohci, OHCI1394_NodeID, 0x0000ffc0);
+
+ /* Enable posted writes */
+ reg_write(ohci, OHCI1394_HCControlSet,
+ OHCI1394_HCControl_postedWriteEnable);
+
+ /* Clear link control register */
+ reg_write(ohci, OHCI1394_LinkControlClear, 0xffffffff);
+
+ /* enable phys */
+ reg_write(ohci, OHCI1394_LinkControlSet,
+ OHCI1394_LinkControl_RcvPhyPkt);
+
+ /* Don't accept phy packets into AR request context */
+ reg_write(ohci, OHCI1394_LinkControlClear, 0x00000400);
+
+ /* Clear the Isochonouys interrupt masks */
+ reg_write(ohci, OHCI1394_IsoRecvIntMaskClear, 0xffffffff);
+ reg_write(ohci, OHCI1394_IsoRecvIntEventClear, 0xffffffff);
+ reg_write(ohci, OHCI1394_IsoXmitIntMaskClear, 0xffffffff);
+ reg_write(ohci, OHCI1394_IsoXmitIntEventClear, 0xffffffff);
+
+ /* Accept asyncronous transfer requests from all nodes for now */
+ reg_write(ohci,OHCI1394_AsReqFilterHiSet, 0x80000000);
+
+ /* Specify asyncronous transfer retries */
+ reg_write(ohci, OHCI1394_ATRetries,
+ OHCI1394_MAX_AT_REQ_RETRIES |
+ (OHCI1394_MAX_AT_RESP_RETRIES<<4) |
+ (OHCI1394_MAX_PHYS_RESP_RETRIES<<8));
+
+ /* We don't want hardware swapping */
+ reg_write(ohci, OHCI1394_HCControlClear, OHCI1394_HCControl_noByteSwap);
+
+ /* Enable link */
+ reg_write(ohci, OHCI1394_HCControlSet, OHCI1394_HCControl_linkEnable);
+
+ /* If anything is connected to a port, make sure it is enabled */
+ num_ports = get_phy_reg(ohci, 2) & 0xf;
+ for (i = 0; i < num_ports; i++) {
+ unsigned int status;
+
+ set_phy_reg(ohci, 7, i);
+ status = get_phy_reg(ohci, 8);
+
+ if (status & 0x20)
+ set_phy_reg(ohci, 8, status & ~1);
+ }
+}
+
+/**
+ * init_ohci1394_wait_for_busresets - wait until bus resets are completed
+ *
+ * OHCI1394 initialization itself and any device going on- or offline
+ * and any cable issue cause a IEEE1394 bus reset. The OHCI1394 spec
+ * specifies that physical DMA is disabled on each bus reset and it
+ * has to be enabled after each bus reset when needed. We resort
+ * to polling here because on early boot, we have no interrupts.
+ */
+static inline void __init init_ohci1394_wait_for_busresets(struct ti_ohci *ohci)
+{
+ int i, events;
+
+ for (i=0; i < 9; i++) {
+ mdelay(200);
+ events = reg_read(ohci, OHCI1394_IntEventSet);
+ if (events & OHCI1394_busReset)
+ reg_write(ohci, OHCI1394_IntEventClear,
+ OHCI1394_busReset);
+ }
+}
+
+/**
+ * init_ohci1394_enable_physical_dma - Enable physical DMA for remote debugging
+ * This enables remote DMA access over IEEE1394 from every host for the low
+ * 4GB of address space. DMA accesses above 4GB are not available currently.
+ */
+static inline void __init init_ohci1394_enable_physical_dma(struct ti_ohci *hci)
+{
+ reg_write(hci, OHCI1394_PhyReqFilterHiSet, 0xffffffff);
+ reg_write(hci, OHCI1394_PhyReqFilterLoSet, 0xffffffff);
+ reg_write(hci, OHCI1394_PhyUpperBound, 0xffff0000);
+}
+
+/**
+ * init_ohci1394_reset_and_init_dma - init controller and enable DMA
+ * This initializes the given controller and enables physical DMA engine in it.
+ */
+static inline void __init init_ohci1394_reset_and_init_dma(struct ti_ohci *ohci)
+{
+ /* Start off with a soft reset, clears everything to a sane state. */
+ init_ohci1394_soft_reset(ohci);
+
+ /* Accessing some registers without LPS enabled may cause lock up */
+ reg_write(ohci, OHCI1394_HCControlSet, OHCI1394_HCControl_LPS);
+
+ /* Disable and clear interrupts */
+ reg_write(ohci, OHCI1394_IntEventClear, 0xffffffff);
+ reg_write(ohci, OHCI1394_IntMaskClear, 0xffffffff);
+
+ mdelay(50); /* Wait 50msec to make sure we have full link enabled */
+
+ init_ohci1394_initialize(ohci);
+ /*
+ * The initialization causes at least one IEEE1394 bus reset. Enabling
+ * physical DMA only works *after* *all* bus resets have calmed down:
+ */
+ init_ohci1394_wait_for_busresets(ohci);
+
+ /* We had to wait and do this now if we want to debug early problems */
+ init_ohci1394_enable_physical_dma(ohci);
+}
+
+/**
+ * init_ohci1394_controller - Map the registers of the controller and init DMA
+ * This maps the registers of the specified controller and initializes it
+ */
+static inline void __init init_ohci1394_controller(int num, int slot, int func)
+{
+ unsigned long ohci_base;
+ struct ti_ohci ohci;
+
+ printk(KERN_INFO "init_ohci1394_dma: initializing OHCI-1394"
+ " at %02x:%02x.%x\n", num, slot, func);
+
+ ohci_base = read_pci_config(num, slot, func, PCI_BASE_ADDRESS_0+(0<<2))
+ & PCI_BASE_ADDRESS_MEM_MASK;
+
+ set_fixmap_nocache(FIX_OHCI1394_BASE, ohci_base);
+
+ ohci.registers = (void *)fix_to_virt(FIX_OHCI1394_BASE);
+
+ init_ohci1394_reset_and_init_dma(&ohci);
+}
+
+/**
+ * debug_init_ohci1394_dma - scan for OHCI1394 controllers and init DMA on them
+ * Scans the whole PCI space for OHCI1394 controllers and inits DMA on them
+ */
+void __init init_ohci1394_dma_on_all_controllers(void)
+{
+ int num, slot, func;
+
+ if (!early_pci_allowed())
+ return;
+
+ /* Poor man's PCI discovery, the only thing we can do at early boot */
+ for (num = 0; num < 32; num++) {
+ for (slot = 0; slot < 32; slot++) {
+ for (func = 0; func < 8; func++) {
+ u32 class = read_pci_config(num,slot,func,
+ PCI_CLASS_REVISION);
+ if ((class == 0xffffffff))
+ continue; /* No device at this func */
+
+ if (class>>8 != PCI_CLASS_SERIAL_FIREWIRE_OHCI)
+ continue; /* Not an OHCI-1394 device */
+
+ init_ohci1394_controller(num, slot, func);
+ break; /* Assume one controller per device */
+ }
+ }
+ }
+ printk(KERN_INFO "init_ohci1394_dma: finished initializing OHCI DMA\n");
+}
+
+/**
+ * setup_init_ohci1394_early - enables early OHCI1394 DMA initialization
+ */
+static int __init setup_ohci1394_dma(char *opt)
+{
+ if (!strcmp(opt, "early"))
+ init_ohci1394_dma_early = 1;
+ return 0;
+}
+
+/* passing ohci1394_dma=early on boot causes early OHCI1394 DMA initialization */
+early_param("ohci1394_dma", setup_ohci1394_dma);
diff --git a/drivers/ieee1394/iso.c b/drivers/ieee1394/iso.c
new file mode 100644
index 0000000..1cf6487
--- /dev/null
+++ b/drivers/ieee1394/iso.c
@@ -0,0 +1,568 @@
+/*
+ * IEEE 1394 for Linux
+ *
+ * kernel ISO transmission/reception
+ *
+ * Copyright (C) 2002 Maas Digital LLC
+ *
+ * This code is licensed under the GPL. See the file COPYING in the root
+ * directory of the kernel sources for details.
+ */
+
+#include <linux/pci.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+
+#include "hosts.h"
+#include "iso.h"
+
+/**
+ * hpsb_iso_stop - stop DMA
+ */
+void hpsb_iso_stop(struct hpsb_iso *iso)
+{
+ if (!(iso->flags & HPSB_ISO_DRIVER_STARTED))
+ return;
+
+ iso->host->driver->isoctl(iso, iso->type == HPSB_ISO_XMIT ?
+ XMIT_STOP : RECV_STOP, 0);
+ iso->flags &= ~HPSB_ISO_DRIVER_STARTED;
+}
+
+/**
+ * hpsb_iso_shutdown - deallocate buffer and DMA context
+ */
+void hpsb_iso_shutdown(struct hpsb_iso *iso)
+{
+ if (iso->flags & HPSB_ISO_DRIVER_INIT) {
+ hpsb_iso_stop(iso);
+ iso->host->driver->isoctl(iso, iso->type == HPSB_ISO_XMIT ?
+ XMIT_SHUTDOWN : RECV_SHUTDOWN, 0);
+ iso->flags &= ~HPSB_ISO_DRIVER_INIT;
+ }
+
+ dma_region_free(&iso->data_buf);
+ kfree(iso);
+}
+
+static struct hpsb_iso *hpsb_iso_common_init(struct hpsb_host *host,
+ enum hpsb_iso_type type,
+ unsigned int data_buf_size,
+ unsigned int buf_packets,
+ int channel, int dma_mode,
+ int irq_interval,
+ void (*callback) (struct hpsb_iso
+ *))
+{
+ struct hpsb_iso *iso;
+ int dma_direction;
+
+ /* make sure driver supports the ISO API */
+ if (!host->driver->isoctl) {
+ printk(KERN_INFO
+ "ieee1394: host driver '%s' does not support the rawiso API\n",
+ host->driver->name);
+ return NULL;
+ }
+
+ /* sanitize parameters */
+
+ if (buf_packets < 2)
+ buf_packets = 2;
+
+ if ((dma_mode < HPSB_ISO_DMA_DEFAULT)
+ || (dma_mode > HPSB_ISO_DMA_PACKET_PER_BUFFER))
+ dma_mode = HPSB_ISO_DMA_DEFAULT;
+
+ if ((irq_interval < 0) || (irq_interval > buf_packets / 4))
+ irq_interval = buf_packets / 4;
+ if (irq_interval == 0) /* really interrupt for each packet */
+ irq_interval = 1;
+
+ if (channel < -1 || channel >= 64)
+ return NULL;
+
+ /* channel = -1 is OK for multi-channel recv but not for xmit */
+ if (type == HPSB_ISO_XMIT && channel < 0)
+ return NULL;
+
+ /* allocate and write the struct hpsb_iso */
+
+ iso =
+ kmalloc(sizeof(*iso) +
+ buf_packets * sizeof(struct hpsb_iso_packet_info),
+ GFP_KERNEL);
+ if (!iso)
+ return NULL;
+
+ iso->infos = (struct hpsb_iso_packet_info *)(iso + 1);
+
+ iso->type = type;
+ iso->host = host;
+ iso->hostdata = NULL;
+ iso->callback = callback;
+ init_waitqueue_head(&iso->waitq);
+ iso->channel = channel;
+ iso->irq_interval = irq_interval;
+ iso->dma_mode = dma_mode;
+ dma_region_init(&iso->data_buf);
+ iso->buf_size = PAGE_ALIGN(data_buf_size);
+ iso->buf_packets = buf_packets;
+ iso->pkt_dma = 0;
+ iso->first_packet = 0;
+ spin_lock_init(&iso->lock);
+
+ if (iso->type == HPSB_ISO_XMIT) {
+ iso->n_ready_packets = iso->buf_packets;
+ dma_direction = PCI_DMA_TODEVICE;
+ } else {
+ iso->n_ready_packets = 0;
+ dma_direction = PCI_DMA_FROMDEVICE;
+ }
+
+ atomic_set(&iso->overflows, 0);
+ iso->bytes_discarded = 0;
+ iso->flags = 0;
+ iso->prebuffer = 0;
+
+ /* allocate the packet buffer */
+ if (dma_region_alloc
+ (&iso->data_buf, iso->buf_size, host->pdev, dma_direction))
+ goto err;
+
+ return iso;
+
+ err:
+ hpsb_iso_shutdown(iso);
+ return NULL;
+}
+
+/**
+ * hpsb_iso_n_ready - returns number of packets ready to send or receive
+ */
+int hpsb_iso_n_ready(struct hpsb_iso *iso)
+{
+ unsigned long flags;
+ int val;
+
+ spin_lock_irqsave(&iso->lock, flags);
+ val = iso->n_ready_packets;
+ spin_unlock_irqrestore(&iso->lock, flags);
+
+ return val;
+}
+
+/**
+ * hpsb_iso_xmit_init - allocate the buffer and DMA context
+ */
+struct hpsb_iso *hpsb_iso_xmit_init(struct hpsb_host *host,
+ unsigned int data_buf_size,
+ unsigned int buf_packets,
+ int channel,
+ int speed,
+ int irq_interval,
+ void (*callback) (struct hpsb_iso *))
+{
+ struct hpsb_iso *iso = hpsb_iso_common_init(host, HPSB_ISO_XMIT,
+ data_buf_size, buf_packets,
+ channel,
+ HPSB_ISO_DMA_DEFAULT,
+ irq_interval, callback);
+ if (!iso)
+ return NULL;
+
+ iso->speed = speed;
+
+ /* tell the driver to start working */
+ if (host->driver->isoctl(iso, XMIT_INIT, 0))
+ goto err;
+
+ iso->flags |= HPSB_ISO_DRIVER_INIT;
+ return iso;
+
+ err:
+ hpsb_iso_shutdown(iso);
+ return NULL;
+}
+
+/**
+ * hpsb_iso_recv_init - allocate the buffer and DMA context
+ *
+ * Note, if channel = -1, multi-channel receive is enabled.
+ */
+struct hpsb_iso *hpsb_iso_recv_init(struct hpsb_host *host,
+ unsigned int data_buf_size,
+ unsigned int buf_packets,
+ int channel,
+ int dma_mode,
+ int irq_interval,
+ void (*callback) (struct hpsb_iso *))
+{
+ struct hpsb_iso *iso = hpsb_iso_common_init(host, HPSB_ISO_RECV,
+ data_buf_size, buf_packets,
+ channel, dma_mode,
+ irq_interval, callback);
+ if (!iso)
+ return NULL;
+
+ /* tell the driver to start working */
+ if (host->driver->isoctl(iso, RECV_INIT, 0))
+ goto err;
+
+ iso->flags |= HPSB_ISO_DRIVER_INIT;
+ return iso;
+
+ err:
+ hpsb_iso_shutdown(iso);
+ return NULL;
+}
+
+/**
+ * hpsb_iso_recv_listen_channel
+ *
+ * multi-channel only
+ */
+int hpsb_iso_recv_listen_channel(struct hpsb_iso *iso, unsigned char channel)
+{
+ if (iso->type != HPSB_ISO_RECV || iso->channel != -1 || channel >= 64)
+ return -EINVAL;
+ return iso->host->driver->isoctl(iso, RECV_LISTEN_CHANNEL, channel);
+}
+
+/**
+ * hpsb_iso_recv_unlisten_channel
+ *
+ * multi-channel only
+ */
+int hpsb_iso_recv_unlisten_channel(struct hpsb_iso *iso, unsigned char channel)
+{
+ if (iso->type != HPSB_ISO_RECV || iso->channel != -1 || channel >= 64)
+ return -EINVAL;
+ return iso->host->driver->isoctl(iso, RECV_UNLISTEN_CHANNEL, channel);
+}
+
+/**
+ * hpsb_iso_recv_set_channel_mask
+ *
+ * multi-channel only
+ */
+int hpsb_iso_recv_set_channel_mask(struct hpsb_iso *iso, u64 mask)
+{
+ if (iso->type != HPSB_ISO_RECV || iso->channel != -1)
+ return -EINVAL;
+ return iso->host->driver->isoctl(iso, RECV_SET_CHANNEL_MASK,
+ (unsigned long)&mask);
+}
+
+/**
+ * hpsb_iso_recv_flush - check for arrival of new packets
+ *
+ * check for arrival of new packets immediately (even if irq_interval
+ * has not yet been reached)
+ */
+int hpsb_iso_recv_flush(struct hpsb_iso *iso)
+{
+ if (iso->type != HPSB_ISO_RECV)
+ return -EINVAL;
+ return iso->host->driver->isoctl(iso, RECV_FLUSH, 0);
+}
+
+static int do_iso_xmit_start(struct hpsb_iso *iso, int cycle)
+{
+ int retval = iso->host->driver->isoctl(iso, XMIT_START, cycle);
+ if (retval)
+ return retval;
+
+ iso->flags |= HPSB_ISO_DRIVER_STARTED;
+ return retval;
+}
+
+/**
+ * hpsb_iso_xmit_start - start DMA
+ */
+int hpsb_iso_xmit_start(struct hpsb_iso *iso, int cycle, int prebuffer)
+{
+ if (iso->type != HPSB_ISO_XMIT)
+ return -1;
+
+ if (iso->flags & HPSB_ISO_DRIVER_STARTED)
+ return 0;
+
+ if (cycle < -1)
+ cycle = -1;
+ else if (cycle >= 8000)
+ cycle %= 8000;
+
+ iso->xmit_cycle = cycle;
+
+ if (prebuffer < 0)
+ prebuffer = iso->buf_packets - 1;
+ else if (prebuffer == 0)
+ prebuffer = 1;
+
+ if (prebuffer >= iso->buf_packets)
+ prebuffer = iso->buf_packets - 1;
+
+ iso->prebuffer = prebuffer;
+
+ /* remember the starting cycle; DMA will commence from xmit_queue_packets()
+ once enough packets have been buffered */
+ iso->start_cycle = cycle;
+
+ return 0;
+}
+
+/**
+ * hpsb_iso_recv_start - start DMA
+ */
+int hpsb_iso_recv_start(struct hpsb_iso *iso, int cycle, int tag_mask, int sync)
+{
+ int retval = 0;
+ int isoctl_args[3];
+
+ if (iso->type != HPSB_ISO_RECV)
+ return -1;
+
+ if (iso->flags & HPSB_ISO_DRIVER_STARTED)
+ return 0;
+
+ if (cycle < -1)
+ cycle = -1;
+ else if (cycle >= 8000)
+ cycle %= 8000;
+
+ isoctl_args[0] = cycle;
+
+ if (tag_mask < 0)
+ /* match all tags */
+ tag_mask = 0xF;
+ isoctl_args[1] = tag_mask;
+
+ isoctl_args[2] = sync;
+
+ retval =
+ iso->host->driver->isoctl(iso, RECV_START,
+ (unsigned long)&isoctl_args[0]);
+ if (retval)
+ return retval;
+
+ iso->flags |= HPSB_ISO_DRIVER_STARTED;
+ return retval;
+}
+
+/* check to make sure the user has not supplied bogus values of offset/len
+ * that would cause the kernel to access memory outside the buffer */
+static int hpsb_iso_check_offset_len(struct hpsb_iso *iso,
+ unsigned int offset, unsigned short len,
+ unsigned int *out_offset,
+ unsigned short *out_len)
+{
+ if (offset >= iso->buf_size)
+ return -EFAULT;
+
+ /* make sure the packet does not go beyond the end of the buffer */
+ if (offset + len > iso->buf_size)
+ return -EFAULT;
+
+ /* check for wrap-around */
+ if (offset + len < offset)
+ return -EFAULT;
+
+ /* now we can trust 'offset' and 'length' */
+ *out_offset = offset;
+ *out_len = len;
+
+ return 0;
+}
+
+/**
+ * hpsb_iso_xmit_queue_packet - queue a packet for transmission.
+ *
+ * @offset is relative to the beginning of the DMA buffer, where the packet's
+ * data payload should already have been placed.
+ */
+int hpsb_iso_xmit_queue_packet(struct hpsb_iso *iso, u32 offset, u16 len,
+ u8 tag, u8 sy)
+{
+ struct hpsb_iso_packet_info *info;
+ unsigned long flags;
+ int rv;
+
+ if (iso->type != HPSB_ISO_XMIT)
+ return -EINVAL;
+
+ /* is there space in the buffer? */
+ if (iso->n_ready_packets <= 0) {
+ return -EBUSY;
+ }
+
+ info = &iso->infos[iso->first_packet];
+
+ /* check for bogus offset/length */
+ if (hpsb_iso_check_offset_len
+ (iso, offset, len, &info->offset, &info->len))
+ return -EFAULT;
+
+ info->tag = tag;
+ info->sy = sy;
+
+ spin_lock_irqsave(&iso->lock, flags);
+
+ rv = iso->host->driver->isoctl(iso, XMIT_QUEUE, (unsigned long)info);
+ if (rv)
+ goto out;
+
+ /* increment cursors */
+ iso->first_packet = (iso->first_packet + 1) % iso->buf_packets;
+ iso->xmit_cycle = (iso->xmit_cycle + 1) % 8000;
+ iso->n_ready_packets--;
+
+ if (iso->prebuffer != 0) {
+ iso->prebuffer--;
+ if (iso->prebuffer <= 0) {
+ iso->prebuffer = 0;
+ rv = do_iso_xmit_start(iso, iso->start_cycle);
+ }
+ }
+
+ out:
+ spin_unlock_irqrestore(&iso->lock, flags);
+ return rv;
+}
+
+/**
+ * hpsb_iso_xmit_sync - wait until all queued packets have been transmitted
+ */
+int hpsb_iso_xmit_sync(struct hpsb_iso *iso)
+{
+ if (iso->type != HPSB_ISO_XMIT)
+ return -EINVAL;
+
+ return wait_event_interruptible(iso->waitq,
+ hpsb_iso_n_ready(iso) ==
+ iso->buf_packets);
+}
+
+/**
+ * hpsb_iso_packet_sent
+ *
+ * Available to low-level drivers.
+ *
+ * Call after a packet has been transmitted to the bus (interrupt context is
+ * OK). @cycle is the _exact_ cycle the packet was sent on. @error should be
+ * non-zero if some sort of error occurred when sending the packet.
+ */
+void hpsb_iso_packet_sent(struct hpsb_iso *iso, int cycle, int error)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&iso->lock, flags);
+
+ /* predict the cycle of the next packet to be queued */
+
+ /* jump ahead by the number of packets that are already buffered */
+ cycle += iso->buf_packets - iso->n_ready_packets;
+ cycle %= 8000;
+
+ iso->xmit_cycle = cycle;
+ iso->n_ready_packets++;
+ iso->pkt_dma = (iso->pkt_dma + 1) % iso->buf_packets;
+
+ if (iso->n_ready_packets == iso->buf_packets || error != 0) {
+ /* the buffer has run empty! */
+ atomic_inc(&iso->overflows);
+ }
+
+ spin_unlock_irqrestore(&iso->lock, flags);
+}
+
+/**
+ * hpsb_iso_packet_received
+ *
+ * Available to low-level drivers.
+ *
+ * Call after a packet has been received (interrupt context is OK).
+ */
+void hpsb_iso_packet_received(struct hpsb_iso *iso, u32 offset, u16 len,
+ u16 total_len, u16 cycle, u8 channel, u8 tag,
+ u8 sy)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&iso->lock, flags);
+
+ if (iso->n_ready_packets == iso->buf_packets) {
+ /* overflow! */
+ atomic_inc(&iso->overflows);
+ /* Record size of this discarded packet */
+ iso->bytes_discarded += total_len;
+ } else {
+ struct hpsb_iso_packet_info *info = &iso->infos[iso->pkt_dma];
+ info->offset = offset;
+ info->len = len;
+ info->total_len = total_len;
+ info->cycle = cycle;
+ info->channel = channel;
+ info->tag = tag;
+ info->sy = sy;
+
+ iso->pkt_dma = (iso->pkt_dma + 1) % iso->buf_packets;
+ iso->n_ready_packets++;
+ }
+
+ spin_unlock_irqrestore(&iso->lock, flags);
+}
+
+/**
+ * hpsb_iso_recv_release_packets - release packets, reuse buffer
+ *
+ * @n_packets have been read out of the buffer, re-use the buffer space
+ */
+int hpsb_iso_recv_release_packets(struct hpsb_iso *iso, unsigned int n_packets)
+{
+ unsigned long flags;
+ unsigned int i;
+ int rv = 0;
+
+ if (iso->type != HPSB_ISO_RECV)
+ return -1;
+
+ spin_lock_irqsave(&iso->lock, flags);
+ for (i = 0; i < n_packets; i++) {
+ rv = iso->host->driver->isoctl(iso, RECV_RELEASE,
+ (unsigned long)&iso->infos[iso->
+ first_packet]);
+ if (rv)
+ break;
+
+ iso->first_packet = (iso->first_packet + 1) % iso->buf_packets;
+ iso->n_ready_packets--;
+
+ /* release memory from packets discarded when queue was full */
+ if (iso->n_ready_packets == 0) { /* Release only after all prior packets handled */
+ if (iso->bytes_discarded != 0) {
+ struct hpsb_iso_packet_info inf;
+ inf.total_len = iso->bytes_discarded;
+ iso->host->driver->isoctl(iso, RECV_RELEASE,
+ (unsigned long)&inf);
+ iso->bytes_discarded = 0;
+ }
+ }
+ }
+ spin_unlock_irqrestore(&iso->lock, flags);
+ return rv;
+}
+
+/**
+ * hpsb_iso_wake
+ *
+ * Available to low-level drivers.
+ *
+ * Call to wake waiting processes after buffer space has opened up.
+ */
+void hpsb_iso_wake(struct hpsb_iso *iso)
+{
+ wake_up_interruptible(&iso->waitq);
+
+ if (iso->callback)
+ iso->callback(iso);
+}
diff --git a/drivers/ieee1394/iso.h b/drivers/ieee1394/iso.h
new file mode 100644
index 0000000..b5de5f2
--- /dev/null
+++ b/drivers/ieee1394/iso.h
@@ -0,0 +1,194 @@
+/*
+ * IEEE 1394 for Linux
+ *
+ * kernel ISO transmission/reception
+ *
+ * Copyright (C) 2002 Maas Digital LLC
+ *
+ * This code is licensed under the GPL. See the file COPYING in the root
+ * directory of the kernel sources for details.
+ */
+
+#ifndef IEEE1394_ISO_H
+#define IEEE1394_ISO_H
+
+#include <linux/spinlock_types.h>
+#include <asm/atomic.h>
+#include <asm/types.h>
+
+#include "dma.h"
+
+struct hpsb_host;
+
+/* high-level ISO interface */
+
+/*
+ * This API sends and receives isochronous packets on a large,
+ * virtually-contiguous kernel memory buffer. The buffer may be mapped
+ * into a user-space process for zero-copy transmission and reception.
+ *
+ * There are no explicit boundaries between packets in the buffer. A
+ * packet may be transmitted or received at any location. However,
+ * low-level drivers may impose certain restrictions on alignment or
+ * size of packets. (e.g. in OHCI no packet may cross a page boundary,
+ * and packets should be quadlet-aligned)
+ */
+
+/* Packet descriptor - the API maintains a ring buffer of these packet
+ * descriptors in kernel memory (hpsb_iso.infos[]). */
+struct hpsb_iso_packet_info {
+ /* offset of data payload relative to the first byte of the buffer */
+ __u32 offset;
+
+ /* length of the data payload, in bytes (not including the isochronous
+ * header) */
+ __u16 len;
+
+ /* (recv only) the cycle number (mod 8000) on which the packet was
+ * received */
+ __u16 cycle;
+
+ /* (recv only) channel on which the packet was received */
+ __u8 channel;
+
+ /* 2-bit 'tag' and 4-bit 'sy' fields of the isochronous header */
+ __u8 tag;
+ __u8 sy;
+
+ /* length in bytes of the packet including header/trailer.
+ * MUST be at structure end, since the first part of this structure is
+ * also defined in raw1394.h (i.e. struct raw1394_iso_packet_info), is
+ * copied to userspace and is accessed there through libraw1394. */
+ __u16 total_len;
+};
+
+enum hpsb_iso_type { HPSB_ISO_RECV = 0, HPSB_ISO_XMIT = 1 };
+
+/* The mode of the dma when receiving iso data. Must be supported by chip */
+enum raw1394_iso_dma_recv_mode {
+ HPSB_ISO_DMA_DEFAULT = -1,
+ HPSB_ISO_DMA_OLD_ABI = 0,
+ HPSB_ISO_DMA_BUFFERFILL = 1,
+ HPSB_ISO_DMA_PACKET_PER_BUFFER = 2
+};
+
+struct hpsb_iso {
+ enum hpsb_iso_type type;
+
+ /* pointer to low-level driver and its private data */
+ struct hpsb_host *host;
+ void *hostdata;
+
+ /* a function to be called (from interrupt context) after
+ * outgoing packets have been sent, or incoming packets have
+ * arrived */
+ void (*callback)(struct hpsb_iso*);
+
+ /* wait for buffer space */
+ wait_queue_head_t waitq;
+
+ int speed; /* IEEE1394_SPEED_100, 200, or 400 */
+ int channel; /* -1 if multichannel */
+ int dma_mode; /* dma receive mode */
+
+
+ /* greatest # of packets between interrupts - controls
+ * the maximum latency of the buffer */
+ int irq_interval;
+
+ /* the buffer for packet data payloads */
+ struct dma_region data_buf;
+
+ /* size of data_buf, in bytes (always a multiple of PAGE_SIZE) */
+ unsigned int buf_size;
+
+ /* # of packets in the ringbuffer */
+ unsigned int buf_packets;
+
+ /* protects packet cursors */
+ spinlock_t lock;
+
+ /* the index of the next packet that will be produced
+ or consumed by the user */
+ int first_packet;
+
+ /* the index of the next packet that will be transmitted
+ or received by the 1394 hardware */
+ int pkt_dma;
+
+ /* how many packets, starting at first_packet:
+ * (transmit) are ready to be filled with data
+ * (receive) contain received data */
+ int n_ready_packets;
+
+ /* how many times the buffer has overflowed or underflowed */
+ atomic_t overflows;
+ /* how many cycles were skipped for a given context */
+ atomic_t skips;
+
+ /* Current number of bytes lost in discarded packets */
+ int bytes_discarded;
+
+ /* private flags to track initialization progress */
+#define HPSB_ISO_DRIVER_INIT (1<<0)
+#define HPSB_ISO_DRIVER_STARTED (1<<1)
+ unsigned int flags;
+
+ /* # of packets left to prebuffer (xmit only) */
+ int prebuffer;
+
+ /* starting cycle for DMA (xmit only) */
+ int start_cycle;
+
+ /* cycle at which next packet will be transmitted,
+ * -1 if not known */
+ int xmit_cycle;
+
+ /* ringbuffer of packet descriptors in regular kernel memory
+ * XXX Keep this last, since we use over-allocated memory from
+ * this entry to fill this field. */
+ struct hpsb_iso_packet_info *infos;
+};
+
+/* functions available to high-level drivers (e.g. raw1394) */
+
+struct hpsb_iso* hpsb_iso_xmit_init(struct hpsb_host *host,
+ unsigned int data_buf_size,
+ unsigned int buf_packets,
+ int channel,
+ int speed,
+ int irq_interval,
+ void (*callback)(struct hpsb_iso*));
+struct hpsb_iso* hpsb_iso_recv_init(struct hpsb_host *host,
+ unsigned int data_buf_size,
+ unsigned int buf_packets,
+ int channel,
+ int dma_mode,
+ int irq_interval,
+ void (*callback)(struct hpsb_iso*));
+int hpsb_iso_recv_listen_channel(struct hpsb_iso *iso, unsigned char channel);
+int hpsb_iso_recv_unlisten_channel(struct hpsb_iso *iso, unsigned char channel);
+int hpsb_iso_recv_set_channel_mask(struct hpsb_iso *iso, u64 mask);
+int hpsb_iso_xmit_start(struct hpsb_iso *iso, int start_on_cycle,
+ int prebuffer);
+int hpsb_iso_recv_start(struct hpsb_iso *iso, int start_on_cycle,
+ int tag_mask, int sync);
+void hpsb_iso_stop(struct hpsb_iso *iso);
+void hpsb_iso_shutdown(struct hpsb_iso *iso);
+int hpsb_iso_xmit_queue_packet(struct hpsb_iso *iso, u32 offset, u16 len,
+ u8 tag, u8 sy);
+int hpsb_iso_xmit_sync(struct hpsb_iso *iso);
+int hpsb_iso_recv_release_packets(struct hpsb_iso *recv,
+ unsigned int n_packets);
+int hpsb_iso_recv_flush(struct hpsb_iso *iso);
+int hpsb_iso_n_ready(struct hpsb_iso *iso);
+
+/* the following are callbacks available to low-level drivers */
+
+void hpsb_iso_packet_sent(struct hpsb_iso *iso, int cycle, int error);
+void hpsb_iso_packet_received(struct hpsb_iso *iso, u32 offset, u16 len,
+ u16 total_len, u16 cycle, u8 channel, u8 tag,
+ u8 sy);
+void hpsb_iso_wake(struct hpsb_iso *iso);
+
+#endif /* IEEE1394_ISO_H */
diff --git a/drivers/ieee1394/nodemgr.c b/drivers/ieee1394/nodemgr.c
new file mode 100644
index 0000000..79ef5fd
--- /dev/null
+++ b/drivers/ieee1394/nodemgr.c
@@ -0,0 +1,1905 @@
+/*
+ * Node information (ConfigROM) collection and management.
+ *
+ * Copyright (C) 2000 Andreas E. Bombe
+ * 2001-2003 Ben Collins <bcollins@debian.net>
+ *
+ * This code is licensed under the GPL. See the file COPYING in the root
+ * directory of the kernel sources for details.
+ */
+
+#include <linux/bitmap.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/kthread.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/mutex.h>
+#include <linux/freezer.h>
+#include <linux/semaphore.h>
+#include <asm/atomic.h>
+
+#include "csr.h"
+#include "highlevel.h"
+#include "hosts.h"
+#include "ieee1394.h"
+#include "ieee1394_core.h"
+#include "ieee1394_hotplug.h"
+#include "ieee1394_types.h"
+#include "ieee1394_transactions.h"
+#include "nodemgr.h"
+
+static int ignore_drivers;
+module_param(ignore_drivers, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(ignore_drivers, "Disable automatic probing for drivers.");
+
+struct nodemgr_csr_info {
+ struct hpsb_host *host;
+ nodeid_t nodeid;
+ unsigned int generation;
+ unsigned int speed_unverified:1;
+};
+
+
+/*
+ * Correct the speed map entry. This is necessary
+ * - for nodes with link speed < phy speed,
+ * - for 1394b nodes with negotiated phy port speed < IEEE1394_SPEED_MAX.
+ * A possible speed is determined by trial and error, using quadlet reads.
+ */
+static int nodemgr_check_speed(struct nodemgr_csr_info *ci, u64 addr,
+ quadlet_t *buffer)
+{
+ quadlet_t q;
+ u8 i, *speed, old_speed, good_speed;
+ int error;
+
+ speed = &(ci->host->speed[NODEID_TO_NODE(ci->nodeid)]);
+ old_speed = *speed;
+ good_speed = IEEE1394_SPEED_MAX + 1;
+
+ /* Try every speed from S100 to old_speed.
+ * If we did it the other way around, a too low speed could be caught
+ * if the retry succeeded for some other reason, e.g. because the link
+ * just finished its initialization. */
+ for (i = IEEE1394_SPEED_100; i <= old_speed; i++) {
+ *speed = i;
+ error = hpsb_read(ci->host, ci->nodeid, ci->generation, addr,
+ &q, sizeof(quadlet_t));
+ if (error)
+ break;
+ *buffer = q;
+ good_speed = i;
+ }
+ if (good_speed <= IEEE1394_SPEED_MAX) {
+ HPSB_DEBUG("Speed probe of node " NODE_BUS_FMT " yields %s",
+ NODE_BUS_ARGS(ci->host, ci->nodeid),
+ hpsb_speedto_str[good_speed]);
+ *speed = good_speed;
+ ci->speed_unverified = 0;
+ return 0;
+ }
+ *speed = old_speed;
+ return error;
+}
+
+static int nodemgr_bus_read(struct csr1212_csr *csr, u64 addr, u16 length,
+ void *buffer, void *__ci)
+{
+ struct nodemgr_csr_info *ci = (struct nodemgr_csr_info*)__ci;
+ int i, error;
+
+ for (i = 1; ; i++) {
+ error = hpsb_read(ci->host, ci->nodeid, ci->generation, addr,
+ buffer, length);
+ if (!error) {
+ ci->speed_unverified = 0;
+ break;
+ }
+ /* Give up after 3rd failure. */
+ if (i == 3)
+ break;
+
+ /* The ieee1394_core guessed the node's speed capability from
+ * the self ID. Check whether a lower speed works. */
+ if (ci->speed_unverified && length == sizeof(quadlet_t)) {
+ error = nodemgr_check_speed(ci, addr, buffer);
+ if (!error)
+ break;
+ }
+ if (msleep_interruptible(334))
+ return -EINTR;
+ }
+ return error;
+}
+
+#define OUI_FREECOM_TECHNOLOGIES_GMBH 0x0001db
+
+static int nodemgr_get_max_rom(quadlet_t *bus_info_data, void *__ci)
+{
+ /* Freecom FireWire Hard Drive firmware bug */
+ if (be32_to_cpu(bus_info_data[3]) >> 8 == OUI_FREECOM_TECHNOLOGIES_GMBH)
+ return 0;
+
+ return (be32_to_cpu(bus_info_data[2]) >> 8) & 0x3;
+}
+
+static struct csr1212_bus_ops nodemgr_csr_ops = {
+ .bus_read = nodemgr_bus_read,
+ .get_max_rom = nodemgr_get_max_rom
+};
+
+
+/*
+ * Basically what we do here is start off retrieving the bus_info block.
+ * From there will fill in some info about the node, verify it is of IEEE
+ * 1394 type, and that the crc checks out ok. After that we start off with
+ * the root directory, and subdirectories. To do this, we retrieve the
+ * quadlet header for a directory, find out the length, and retrieve the
+ * complete directory entry (be it a leaf or a directory). We then process
+ * it and add the info to our structure for that particular node.
+ *
+ * We verify CRC's along the way for each directory/block/leaf. The entire
+ * node structure is generic, and simply stores the information in a way
+ * that's easy to parse by the protocol interface.
+ */
+
+/*
+ * The nodemgr relies heavily on the Driver Model for device callbacks and
+ * driver/device mappings. The old nodemgr used to handle all this itself,
+ * but now we are much simpler because of the LDM.
+ */
+
+struct host_info {
+ struct hpsb_host *host;
+ struct list_head list;
+ struct task_struct *thread;
+};
+
+static int nodemgr_bus_match(struct device * dev, struct device_driver * drv);
+static int nodemgr_uevent(struct device *dev, struct kobj_uevent_env *env);
+
+struct bus_type ieee1394_bus_type = {
+ .name = "ieee1394",
+ .match = nodemgr_bus_match,
+};
+
+static void host_cls_release(struct device *dev)
+{
+ put_device(&container_of((dev), struct hpsb_host, host_dev)->device);
+}
+
+struct class hpsb_host_class = {
+ .name = "ieee1394_host",
+ .dev_release = host_cls_release,
+};
+
+static void ne_cls_release(struct device *dev)
+{
+ put_device(&container_of((dev), struct node_entry, node_dev)->device);
+}
+
+static struct class nodemgr_ne_class = {
+ .name = "ieee1394_node",
+ .dev_release = ne_cls_release,
+};
+
+static void ud_cls_release(struct device *dev)
+{
+ put_device(&container_of((dev), struct unit_directory, unit_dev)->device);
+}
+
+/* The name here is only so that unit directory hotplug works with old
+ * style hotplug, which only ever did unit directories anyway.
+ */
+static struct class nodemgr_ud_class = {
+ .name = "ieee1394",
+ .dev_release = ud_cls_release,
+ .dev_uevent = nodemgr_uevent,
+};
+
+static struct hpsb_highlevel nodemgr_highlevel;
+
+
+static void nodemgr_release_ud(struct device *dev)
+{
+ struct unit_directory *ud = container_of(dev, struct unit_directory, device);
+
+ if (ud->vendor_name_kv)
+ csr1212_release_keyval(ud->vendor_name_kv);
+ if (ud->model_name_kv)
+ csr1212_release_keyval(ud->model_name_kv);
+
+ kfree(ud);
+}
+
+static void nodemgr_release_ne(struct device *dev)
+{
+ struct node_entry *ne = container_of(dev, struct node_entry, device);
+
+ if (ne->vendor_name_kv)
+ csr1212_release_keyval(ne->vendor_name_kv);
+
+ kfree(ne);
+}
+
+
+static void nodemgr_release_host(struct device *dev)
+{
+ struct hpsb_host *host = container_of(dev, struct hpsb_host, device);
+
+ csr1212_destroy_csr(host->csr.rom);
+
+ kfree(host);
+}
+
+static int nodemgr_ud_platform_data;
+
+static struct device nodemgr_dev_template_ud = {
+ .bus = &ieee1394_bus_type,
+ .release = nodemgr_release_ud,
+ .platform_data = &nodemgr_ud_platform_data,
+};
+
+static struct device nodemgr_dev_template_ne = {
+ .bus = &ieee1394_bus_type,
+ .release = nodemgr_release_ne,
+};
+
+/* This dummy driver prevents the host devices from being scanned. We have no
+ * useful drivers for them yet, and there would be a deadlock possible if the
+ * driver core scans the host device while the host's low-level driver (i.e.
+ * the host's parent device) is being removed. */
+static struct device_driver nodemgr_mid_layer_driver = {
+ .bus = &ieee1394_bus_type,
+ .name = "nodemgr",
+ .owner = THIS_MODULE,
+};
+
+struct device nodemgr_dev_template_host = {
+ .bus = &ieee1394_bus_type,
+ .release = nodemgr_release_host,
+};
+
+
+#define fw_attr(class, class_type, field, type, format_string) \
+static ssize_t fw_show_##class##_##field (struct device *dev, struct device_attribute *attr, char *buf)\
+{ \
+ class_type *class; \
+ class = container_of(dev, class_type, device); \
+ return sprintf(buf, format_string, (type)class->field); \
+} \
+static struct device_attribute dev_attr_##class##_##field = { \
+ .attr = {.name = __stringify(field), .mode = S_IRUGO }, \
+ .show = fw_show_##class##_##field, \
+};
+
+#define fw_attr_td(class, class_type, td_kv) \
+static ssize_t fw_show_##class##_##td_kv (struct device *dev, struct device_attribute *attr, char *buf)\
+{ \
+ int len; \
+ class_type *class = container_of(dev, class_type, device); \
+ len = (class->td_kv->value.leaf.len - 2) * sizeof(quadlet_t); \
+ memcpy(buf, \
+ CSR1212_TEXTUAL_DESCRIPTOR_LEAF_DATA(class->td_kv), \
+ len); \
+ while (buf[len - 1] == '\0') \
+ len--; \
+ buf[len++] = '\n'; \
+ buf[len] = '\0'; \
+ return len; \
+} \
+static struct device_attribute dev_attr_##class##_##td_kv = { \
+ .attr = {.name = __stringify(td_kv), .mode = S_IRUGO }, \
+ .show = fw_show_##class##_##td_kv, \
+};
+
+
+#define fw_drv_attr(field, type, format_string) \
+static ssize_t fw_drv_show_##field (struct device_driver *drv, char *buf) \
+{ \
+ struct hpsb_protocol_driver *driver; \
+ driver = container_of(drv, struct hpsb_protocol_driver, driver); \
+ return sprintf(buf, format_string, (type)driver->field);\
+} \
+static struct driver_attribute driver_attr_drv_##field = { \
+ .attr = {.name = __stringify(field), .mode = S_IRUGO }, \
+ .show = fw_drv_show_##field, \
+};
+
+
+static ssize_t fw_show_ne_bus_options(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct node_entry *ne = container_of(dev, struct node_entry, device);
+
+ return sprintf(buf, "IRMC(%d) CMC(%d) ISC(%d) BMC(%d) PMC(%d) GEN(%d) "
+ "LSPD(%d) MAX_REC(%d) MAX_ROM(%d) CYC_CLK_ACC(%d)\n",
+ ne->busopt.irmc,
+ ne->busopt.cmc, ne->busopt.isc, ne->busopt.bmc,
+ ne->busopt.pmc, ne->busopt.generation, ne->busopt.lnkspd,
+ ne->busopt.max_rec,
+ ne->busopt.max_rom,
+ ne->busopt.cyc_clk_acc);
+}
+static DEVICE_ATTR(bus_options,S_IRUGO,fw_show_ne_bus_options,NULL);
+
+
+#ifdef HPSB_DEBUG_TLABELS
+static ssize_t fw_show_ne_tlabels_free(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct node_entry *ne = container_of(dev, struct node_entry, device);
+ unsigned long flags;
+ unsigned long *tp = ne->host->tl_pool[NODEID_TO_NODE(ne->nodeid)].map;
+ int tf;
+
+ spin_lock_irqsave(&hpsb_tlabel_lock, flags);
+ tf = 64 - bitmap_weight(tp, 64);
+ spin_unlock_irqrestore(&hpsb_tlabel_lock, flags);
+
+ return sprintf(buf, "%d\n", tf);
+}
+static DEVICE_ATTR(tlabels_free,S_IRUGO,fw_show_ne_tlabels_free,NULL);
+
+
+static ssize_t fw_show_ne_tlabels_mask(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct node_entry *ne = container_of(dev, struct node_entry, device);
+ unsigned long flags;
+ unsigned long *tp = ne->host->tl_pool[NODEID_TO_NODE(ne->nodeid)].map;
+ u64 tm;
+
+ spin_lock_irqsave(&hpsb_tlabel_lock, flags);
+#if (BITS_PER_LONG <= 32)
+ tm = ((u64)tp[0] << 32) + tp[1];
+#else
+ tm = tp[0];
+#endif
+ spin_unlock_irqrestore(&hpsb_tlabel_lock, flags);
+
+ return sprintf(buf, "0x%016llx\n", (unsigned long long)tm);
+}
+static DEVICE_ATTR(tlabels_mask, S_IRUGO, fw_show_ne_tlabels_mask, NULL);
+#endif /* HPSB_DEBUG_TLABELS */
+
+
+static ssize_t fw_set_ignore_driver(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct unit_directory *ud = container_of(dev, struct unit_directory, device);
+ int state = simple_strtoul(buf, NULL, 10);
+
+ if (state == 1) {
+ ud->ignore_driver = 1;
+ device_release_driver(dev);
+ } else if (state == 0)
+ ud->ignore_driver = 0;
+
+ return count;
+}
+static ssize_t fw_get_ignore_driver(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct unit_directory *ud = container_of(dev, struct unit_directory, device);
+
+ return sprintf(buf, "%d\n", ud->ignore_driver);
+}
+static DEVICE_ATTR(ignore_driver, S_IWUSR | S_IRUGO, fw_get_ignore_driver, fw_set_ignore_driver);
+
+
+static ssize_t fw_set_rescan(struct bus_type *bus, const char *buf,
+ size_t count)
+{
+ int error = 0;
+
+ if (simple_strtoul(buf, NULL, 10) == 1)
+ error = bus_rescan_devices(&ieee1394_bus_type);
+ return error ? error : count;
+}
+static ssize_t fw_get_rescan(struct bus_type *bus, char *buf)
+{
+ return sprintf(buf, "You can force a rescan of the bus for "
+ "drivers by writing a 1 to this file\n");
+}
+static BUS_ATTR(rescan, S_IWUSR | S_IRUGO, fw_get_rescan, fw_set_rescan);
+
+
+static ssize_t fw_set_ignore_drivers(struct bus_type *bus, const char *buf, size_t count)
+{
+ int state = simple_strtoul(buf, NULL, 10);
+
+ if (state == 1)
+ ignore_drivers = 1;
+ else if (state == 0)
+ ignore_drivers = 0;
+
+ return count;
+}
+static ssize_t fw_get_ignore_drivers(struct bus_type *bus, char *buf)
+{
+ return sprintf(buf, "%d\n", ignore_drivers);
+}
+static BUS_ATTR(ignore_drivers, S_IWUSR | S_IRUGO, fw_get_ignore_drivers, fw_set_ignore_drivers);
+
+
+struct bus_attribute *const fw_bus_attrs[] = {
+ &bus_attr_rescan,
+ &bus_attr_ignore_drivers,
+ NULL
+};
+
+
+fw_attr(ne, struct node_entry, capabilities, unsigned int, "0x%06x\n")
+fw_attr(ne, struct node_entry, nodeid, unsigned int, "0x%04x\n")
+
+fw_attr(ne, struct node_entry, vendor_id, unsigned int, "0x%06x\n")
+fw_attr_td(ne, struct node_entry, vendor_name_kv)
+
+fw_attr(ne, struct node_entry, guid, unsigned long long, "0x%016Lx\n")
+fw_attr(ne, struct node_entry, guid_vendor_id, unsigned int, "0x%06x\n")
+fw_attr(ne, struct node_entry, in_limbo, int, "%d\n");
+
+static struct device_attribute *const fw_ne_attrs[] = {
+ &dev_attr_ne_guid,
+ &dev_attr_ne_guid_vendor_id,
+ &dev_attr_ne_capabilities,
+ &dev_attr_ne_vendor_id,
+ &dev_attr_ne_nodeid,
+ &dev_attr_bus_options,
+#ifdef HPSB_DEBUG_TLABELS
+ &dev_attr_tlabels_free,
+ &dev_attr_tlabels_mask,
+#endif
+};
+
+
+
+fw_attr(ud, struct unit_directory, address, unsigned long long, "0x%016Lx\n")
+fw_attr(ud, struct unit_directory, length, int, "%d\n")
+/* These are all dependent on the value being provided */
+fw_attr(ud, struct unit_directory, vendor_id, unsigned int, "0x%06x\n")
+fw_attr(ud, struct unit_directory, model_id, unsigned int, "0x%06x\n")
+fw_attr(ud, struct unit_directory, specifier_id, unsigned int, "0x%06x\n")
+fw_attr(ud, struct unit_directory, version, unsigned int, "0x%06x\n")
+fw_attr_td(ud, struct unit_directory, vendor_name_kv)
+fw_attr_td(ud, struct unit_directory, model_name_kv)
+
+static struct device_attribute *const fw_ud_attrs[] = {
+ &dev_attr_ud_address,
+ &dev_attr_ud_length,
+ &dev_attr_ignore_driver,
+};
+
+
+fw_attr(host, struct hpsb_host, node_count, int, "%d\n")
+fw_attr(host, struct hpsb_host, selfid_count, int, "%d\n")
+fw_attr(host, struct hpsb_host, nodes_active, int, "%d\n")
+fw_attr(host, struct hpsb_host, in_bus_reset, int, "%d\n")
+fw_attr(host, struct hpsb_host, is_root, int, "%d\n")
+fw_attr(host, struct hpsb_host, is_cycmst, int, "%d\n")
+fw_attr(host, struct hpsb_host, is_irm, int, "%d\n")
+fw_attr(host, struct hpsb_host, is_busmgr, int, "%d\n")
+
+static struct device_attribute *const fw_host_attrs[] = {
+ &dev_attr_host_node_count,
+ &dev_attr_host_selfid_count,
+ &dev_attr_host_nodes_active,
+ &dev_attr_host_in_bus_reset,
+ &dev_attr_host_is_root,
+ &dev_attr_host_is_cycmst,
+ &dev_attr_host_is_irm,
+ &dev_attr_host_is_busmgr,
+};
+
+
+static ssize_t fw_show_drv_device_ids(struct device_driver *drv, char *buf)
+{
+ struct hpsb_protocol_driver *driver;
+ struct ieee1394_device_id *id;
+ int length = 0;
+ char *scratch = buf;
+
+ driver = container_of(drv, struct hpsb_protocol_driver, driver);
+ id = driver->id_table;
+ if (!id)
+ return 0;
+
+ for (; id->match_flags != 0; id++) {
+ int need_coma = 0;
+
+ if (id->match_flags & IEEE1394_MATCH_VENDOR_ID) {
+ length += sprintf(scratch, "vendor_id=0x%06x", id->vendor_id);
+ scratch = buf + length;
+ need_coma++;
+ }
+
+ if (id->match_flags & IEEE1394_MATCH_MODEL_ID) {
+ length += sprintf(scratch, "%smodel_id=0x%06x",
+ need_coma++ ? "," : "",
+ id->model_id);
+ scratch = buf + length;
+ }
+
+ if (id->match_flags & IEEE1394_MATCH_SPECIFIER_ID) {
+ length += sprintf(scratch, "%sspecifier_id=0x%06x",
+ need_coma++ ? "," : "",
+ id->specifier_id);
+ scratch = buf + length;
+ }
+
+ if (id->match_flags & IEEE1394_MATCH_VERSION) {
+ length += sprintf(scratch, "%sversion=0x%06x",
+ need_coma++ ? "," : "",
+ id->version);
+ scratch = buf + length;
+ }
+
+ if (need_coma) {
+ *scratch++ = '\n';
+ length++;
+ }
+ }
+
+ return length;
+}
+static DRIVER_ATTR(device_ids,S_IRUGO,fw_show_drv_device_ids,NULL);
+
+
+fw_drv_attr(name, const char *, "%s\n")
+
+static struct driver_attribute *const fw_drv_attrs[] = {
+ &driver_attr_drv_name,
+ &driver_attr_device_ids,
+};
+
+
+static void nodemgr_create_drv_files(struct hpsb_protocol_driver *driver)
+{
+ struct device_driver *drv = &driver->driver;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(fw_drv_attrs); i++)
+ if (driver_create_file(drv, fw_drv_attrs[i]))
+ goto fail;
+ return;
+fail:
+ HPSB_ERR("Failed to add sysfs attribute");
+}
+
+
+static void nodemgr_remove_drv_files(struct hpsb_protocol_driver *driver)
+{
+ struct device_driver *drv = &driver->driver;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(fw_drv_attrs); i++)
+ driver_remove_file(drv, fw_drv_attrs[i]);
+}
+
+
+static void nodemgr_create_ne_dev_files(struct node_entry *ne)
+{
+ struct device *dev = &ne->device;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(fw_ne_attrs); i++)
+ if (device_create_file(dev, fw_ne_attrs[i]))
+ goto fail;
+ return;
+fail:
+ HPSB_ERR("Failed to add sysfs attribute");
+}
+
+
+static void nodemgr_create_host_dev_files(struct hpsb_host *host)
+{
+ struct device *dev = &host->device;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(fw_host_attrs); i++)
+ if (device_create_file(dev, fw_host_attrs[i]))
+ goto fail;
+ return;
+fail:
+ HPSB_ERR("Failed to add sysfs attribute");
+}
+
+
+static struct node_entry *find_entry_by_nodeid(struct hpsb_host *host,
+ nodeid_t nodeid);
+
+static void nodemgr_update_host_dev_links(struct hpsb_host *host)
+{
+ struct device *dev = &host->device;
+ struct node_entry *ne;
+
+ sysfs_remove_link(&dev->kobj, "irm_id");
+ sysfs_remove_link(&dev->kobj, "busmgr_id");
+ sysfs_remove_link(&dev->kobj, "host_id");
+
+ if ((ne = find_entry_by_nodeid(host, host->irm_id)) &&
+ sysfs_create_link(&dev->kobj, &ne->device.kobj, "irm_id"))
+ goto fail;
+ if ((ne = find_entry_by_nodeid(host, host->busmgr_id)) &&
+ sysfs_create_link(&dev->kobj, &ne->device.kobj, "busmgr_id"))
+ goto fail;
+ if ((ne = find_entry_by_nodeid(host, host->node_id)) &&
+ sysfs_create_link(&dev->kobj, &ne->device.kobj, "host_id"))
+ goto fail;
+ return;
+fail:
+ HPSB_ERR("Failed to update sysfs attributes for host %d", host->id);
+}
+
+static void nodemgr_create_ud_dev_files(struct unit_directory *ud)
+{
+ struct device *dev = &ud->device;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(fw_ud_attrs); i++)
+ if (device_create_file(dev, fw_ud_attrs[i]))
+ goto fail;
+ if (ud->flags & UNIT_DIRECTORY_SPECIFIER_ID)
+ if (device_create_file(dev, &dev_attr_ud_specifier_id))
+ goto fail;
+ if (ud->flags & UNIT_DIRECTORY_VERSION)
+ if (device_create_file(dev, &dev_attr_ud_version))
+ goto fail;
+ if (ud->flags & UNIT_DIRECTORY_VENDOR_ID) {
+ if (device_create_file(dev, &dev_attr_ud_vendor_id))
+ goto fail;
+ if (ud->vendor_name_kv &&
+ device_create_file(dev, &dev_attr_ud_vendor_name_kv))
+ goto fail;
+ }
+ if (ud->flags & UNIT_DIRECTORY_MODEL_ID) {
+ if (device_create_file(dev, &dev_attr_ud_model_id))
+ goto fail;
+ if (ud->model_name_kv &&
+ device_create_file(dev, &dev_attr_ud_model_name_kv))
+ goto fail;
+ }
+ return;
+fail:
+ HPSB_ERR("Failed to add sysfs attribute");
+}
+
+
+static int nodemgr_bus_match(struct device * dev, struct device_driver * drv)
+{
+ struct hpsb_protocol_driver *driver;
+ struct unit_directory *ud;
+ struct ieee1394_device_id *id;
+
+ /* We only match unit directories */
+ if (dev->platform_data != &nodemgr_ud_platform_data)
+ return 0;
+
+ ud = container_of(dev, struct unit_directory, device);
+ if (ud->ne->in_limbo || ud->ignore_driver)
+ return 0;
+
+ /* We only match drivers of type hpsb_protocol_driver */
+ if (drv == &nodemgr_mid_layer_driver)
+ return 0;
+
+ driver = container_of(drv, struct hpsb_protocol_driver, driver);
+ id = driver->id_table;
+ if (!id)
+ return 0;
+
+ for (; id->match_flags != 0; id++) {
+ if ((id->match_flags & IEEE1394_MATCH_VENDOR_ID) &&
+ id->vendor_id != ud->vendor_id)
+ continue;
+
+ if ((id->match_flags & IEEE1394_MATCH_MODEL_ID) &&
+ id->model_id != ud->model_id)
+ continue;
+
+ if ((id->match_flags & IEEE1394_MATCH_SPECIFIER_ID) &&
+ id->specifier_id != ud->specifier_id)
+ continue;
+
+ if ((id->match_flags & IEEE1394_MATCH_VERSION) &&
+ id->version != ud->version)
+ continue;
+
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static DEFINE_MUTEX(nodemgr_serialize_remove_uds);
+
+static int match_ne(struct device *dev, void *data)
+{
+ struct unit_directory *ud;
+ struct node_entry *ne = data;
+
+ ud = container_of(dev, struct unit_directory, unit_dev);
+ return ud->ne == ne;
+}
+
+static void nodemgr_remove_uds(struct node_entry *ne)
+{
+ struct device *dev;
+ struct unit_directory *ud;
+
+ /* Use class_find device to iterate the devices. Since this code
+ * may be called from other contexts besides the knodemgrds,
+ * protect it by nodemgr_serialize_remove_uds.
+ */
+ mutex_lock(&nodemgr_serialize_remove_uds);
+ for (;;) {
+ dev = class_find_device(&nodemgr_ud_class, NULL, ne, match_ne);
+ if (!dev)
+ break;
+ ud = container_of(dev, struct unit_directory, unit_dev);
+ put_device(dev);
+ device_unregister(&ud->unit_dev);
+ device_unregister(&ud->device);
+ }
+ mutex_unlock(&nodemgr_serialize_remove_uds);
+}
+
+
+static void nodemgr_remove_ne(struct node_entry *ne)
+{
+ struct device *dev;
+
+ dev = get_device(&ne->device);
+ if (!dev)
+ return;
+
+ HPSB_DEBUG("Node removed: ID:BUS[" NODE_BUS_FMT "] GUID[%016Lx]",
+ NODE_BUS_ARGS(ne->host, ne->nodeid), (unsigned long long)ne->guid);
+ nodemgr_remove_uds(ne);
+
+ device_unregister(&ne->node_dev);
+ device_unregister(dev);
+
+ put_device(dev);
+}
+
+static int remove_host_dev(struct device *dev, void *data)
+{
+ if (dev->bus == &ieee1394_bus_type)
+ nodemgr_remove_ne(container_of(dev, struct node_entry,
+ device));
+ return 0;
+}
+
+static void nodemgr_remove_host_dev(struct device *dev)
+{
+ device_for_each_child(dev, NULL, remove_host_dev);
+ sysfs_remove_link(&dev->kobj, "irm_id");
+ sysfs_remove_link(&dev->kobj, "busmgr_id");
+ sysfs_remove_link(&dev->kobj, "host_id");
+}
+
+
+static void nodemgr_update_bus_options(struct node_entry *ne)
+{
+#ifdef CONFIG_IEEE1394_VERBOSEDEBUG
+ static const u16 mr[] = { 4, 64, 1024, 0};
+#endif
+ quadlet_t busoptions = be32_to_cpu(ne->csr->bus_info_data[2]);
+
+ ne->busopt.irmc = (busoptions >> 31) & 1;
+ ne->busopt.cmc = (busoptions >> 30) & 1;
+ ne->busopt.isc = (busoptions >> 29) & 1;
+ ne->busopt.bmc = (busoptions >> 28) & 1;
+ ne->busopt.pmc = (busoptions >> 27) & 1;
+ ne->busopt.cyc_clk_acc = (busoptions >> 16) & 0xff;
+ ne->busopt.max_rec = 1 << (((busoptions >> 12) & 0xf) + 1);
+ ne->busopt.max_rom = (busoptions >> 8) & 0x3;
+ ne->busopt.generation = (busoptions >> 4) & 0xf;
+ ne->busopt.lnkspd = busoptions & 0x7;
+
+ HPSB_VERBOSE("NodeMgr: raw=0x%08x irmc=%d cmc=%d isc=%d bmc=%d pmc=%d "
+ "cyc_clk_acc=%d max_rec=%d max_rom=%d gen=%d lspd=%d",
+ busoptions, ne->busopt.irmc, ne->busopt.cmc,
+ ne->busopt.isc, ne->busopt.bmc, ne->busopt.pmc,
+ ne->busopt.cyc_clk_acc, ne->busopt.max_rec,
+ mr[ne->busopt.max_rom],
+ ne->busopt.generation, ne->busopt.lnkspd);
+}
+
+
+static struct node_entry *nodemgr_create_node(octlet_t guid,
+ struct csr1212_csr *csr, struct hpsb_host *host,
+ nodeid_t nodeid, unsigned int generation)
+{
+ struct node_entry *ne;
+
+ ne = kzalloc(sizeof(*ne), GFP_KERNEL);
+ if (!ne)
+ goto fail_alloc;
+
+ ne->host = host;
+ ne->nodeid = nodeid;
+ ne->generation = generation;
+ ne->needs_probe = true;
+
+ ne->guid = guid;
+ ne->guid_vendor_id = (guid >> 40) & 0xffffff;
+ ne->csr = csr;
+
+ memcpy(&ne->device, &nodemgr_dev_template_ne,
+ sizeof(ne->device));
+ ne->device.parent = &host->device;
+ dev_set_name(&ne->device, "%016Lx", (unsigned long long)(ne->guid));
+
+ ne->node_dev.parent = &ne->device;
+ ne->node_dev.class = &nodemgr_ne_class;
+ dev_set_name(&ne->node_dev, "%016Lx", (unsigned long long)(ne->guid));
+
+ if (device_register(&ne->device))
+ goto fail_devreg;
+ if (device_register(&ne->node_dev))
+ goto fail_classdevreg;
+ get_device(&ne->device);
+
+ nodemgr_create_ne_dev_files(ne);
+
+ nodemgr_update_bus_options(ne);
+
+ HPSB_DEBUG("%s added: ID:BUS[" NODE_BUS_FMT "] GUID[%016Lx]",
+ (host->node_id == nodeid) ? "Host" : "Node",
+ NODE_BUS_ARGS(host, nodeid), (unsigned long long)guid);
+
+ return ne;
+
+fail_classdevreg:
+ device_unregister(&ne->device);
+fail_devreg:
+ kfree(ne);
+fail_alloc:
+ HPSB_ERR("Failed to create node ID:BUS[" NODE_BUS_FMT "] GUID[%016Lx]",
+ NODE_BUS_ARGS(host, nodeid), (unsigned long long)guid);
+
+ return NULL;
+}
+
+static int match_ne_guid(struct device *dev, void *data)
+{
+ struct node_entry *ne;
+ u64 *guid = data;
+
+ ne = container_of(dev, struct node_entry, node_dev);
+ return ne->guid == *guid;
+}
+
+static struct node_entry *find_entry_by_guid(u64 guid)
+{
+ struct device *dev;
+ struct node_entry *ne;
+
+ dev = class_find_device(&nodemgr_ne_class, NULL, &guid, match_ne_guid);
+ if (!dev)
+ return NULL;
+ ne = container_of(dev, struct node_entry, node_dev);
+ put_device(dev);
+
+ return ne;
+}
+
+struct match_nodeid_parameter {
+ struct hpsb_host *host;
+ nodeid_t nodeid;
+};
+
+static int match_ne_nodeid(struct device *dev, void *data)
+{
+ int found = 0;
+ struct node_entry *ne;
+ struct match_nodeid_parameter *p = data;
+
+ if (!dev)
+ goto ret;
+ ne = container_of(dev, struct node_entry, node_dev);
+ if (ne->host == p->host && ne->nodeid == p->nodeid)
+ found = 1;
+ret:
+ return found;
+}
+
+static struct node_entry *find_entry_by_nodeid(struct hpsb_host *host,
+ nodeid_t nodeid)
+{
+ struct device *dev;
+ struct node_entry *ne;
+ struct match_nodeid_parameter p;
+
+ p.host = host;
+ p.nodeid = nodeid;
+
+ dev = class_find_device(&nodemgr_ne_class, NULL, &p, match_ne_nodeid);
+ if (!dev)
+ return NULL;
+ ne = container_of(dev, struct node_entry, node_dev);
+ put_device(dev);
+
+ return ne;
+}
+
+
+static void nodemgr_register_device(struct node_entry *ne,
+ struct unit_directory *ud, struct device *parent)
+{
+ memcpy(&ud->device, &nodemgr_dev_template_ud,
+ sizeof(ud->device));
+
+ ud->device.parent = parent;
+
+ dev_set_name(&ud->device, "%s-%u", dev_name(&ne->device), ud->id);
+
+ ud->unit_dev.parent = &ud->device;
+ ud->unit_dev.class = &nodemgr_ud_class;
+ dev_set_name(&ud->unit_dev, "%s-%u", dev_name(&ne->device), ud->id);
+
+ if (device_register(&ud->device))
+ goto fail_devreg;
+ if (device_register(&ud->unit_dev))
+ goto fail_classdevreg;
+ get_device(&ud->device);
+
+ nodemgr_create_ud_dev_files(ud);
+
+ return;
+
+fail_classdevreg:
+ device_unregister(&ud->device);
+fail_devreg:
+ HPSB_ERR("Failed to create unit %s", dev_name(&ud->device));
+}
+
+
+/* This implementation currently only scans the config rom and its
+ * immediate unit directories looking for software_id and
+ * software_version entries, in order to get driver autoloading working. */
+static struct unit_directory *nodemgr_process_unit_directory
+ (struct node_entry *ne, struct csr1212_keyval *ud_kv,
+ unsigned int *id, struct unit_directory *parent)
+{
+ struct unit_directory *ud;
+ struct unit_directory *ud_child = NULL;
+ struct csr1212_dentry *dentry;
+ struct csr1212_keyval *kv;
+ u8 last_key_id = 0;
+
+ ud = kzalloc(sizeof(*ud), GFP_KERNEL);
+ if (!ud)
+ goto unit_directory_error;
+
+ ud->ne = ne;
+ ud->ignore_driver = ignore_drivers;
+ ud->address = ud_kv->offset + CSR1212_REGISTER_SPACE_BASE;
+ ud->directory_id = ud->address & 0xffffff;
+ ud->ud_kv = ud_kv;
+ ud->id = (*id)++;
+
+ csr1212_for_each_dir_entry(ne->csr, kv, ud_kv, dentry) {
+ switch (kv->key.id) {
+ case CSR1212_KV_ID_VENDOR:
+ if (kv->key.type == CSR1212_KV_TYPE_IMMEDIATE) {
+ ud->vendor_id = kv->value.immediate;
+ ud->flags |= UNIT_DIRECTORY_VENDOR_ID;
+ }
+ break;
+
+ case CSR1212_KV_ID_MODEL:
+ ud->model_id = kv->value.immediate;
+ ud->flags |= UNIT_DIRECTORY_MODEL_ID;
+ break;
+
+ case CSR1212_KV_ID_SPECIFIER_ID:
+ ud->specifier_id = kv->value.immediate;
+ ud->flags |= UNIT_DIRECTORY_SPECIFIER_ID;
+ break;
+
+ case CSR1212_KV_ID_VERSION:
+ ud->version = kv->value.immediate;
+ ud->flags |= UNIT_DIRECTORY_VERSION;
+ break;
+
+ case CSR1212_KV_ID_DESCRIPTOR:
+ if (kv->key.type == CSR1212_KV_TYPE_LEAF &&
+ CSR1212_DESCRIPTOR_LEAF_TYPE(kv) == 0 &&
+ CSR1212_DESCRIPTOR_LEAF_SPECIFIER_ID(kv) == 0 &&
+ CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH(kv) == 0 &&
+ CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET(kv) == 0 &&
+ CSR1212_TEXTUAL_DESCRIPTOR_LEAF_LANGUAGE(kv) == 0) {
+ switch (last_key_id) {
+ case CSR1212_KV_ID_VENDOR:
+ csr1212_keep_keyval(kv);
+ ud->vendor_name_kv = kv;
+ break;
+
+ case CSR1212_KV_ID_MODEL:
+ csr1212_keep_keyval(kv);
+ ud->model_name_kv = kv;
+ break;
+
+ }
+ } /* else if (kv->key.type == CSR1212_KV_TYPE_DIRECTORY) ... */
+ break;
+
+ case CSR1212_KV_ID_DEPENDENT_INFO:
+ /* Logical Unit Number */
+ if (kv->key.type == CSR1212_KV_TYPE_IMMEDIATE) {
+ if (ud->flags & UNIT_DIRECTORY_HAS_LUN) {
+ ud_child = kmemdup(ud, sizeof(*ud_child), GFP_KERNEL);
+ if (!ud_child)
+ goto unit_directory_error;
+ nodemgr_register_device(ne, ud_child, &ne->device);
+ ud_child = NULL;
+
+ ud->id = (*id)++;
+ }
+ ud->lun = kv->value.immediate;
+ ud->flags |= UNIT_DIRECTORY_HAS_LUN;
+
+ /* Logical Unit Directory */
+ } else if (kv->key.type == CSR1212_KV_TYPE_DIRECTORY) {
+ /* This should really be done in SBP2 as this is
+ * doing SBP2 specific parsing.
+ */
+
+ /* first register the parent unit */
+ ud->flags |= UNIT_DIRECTORY_HAS_LUN_DIRECTORY;
+ if (ud->device.bus != &ieee1394_bus_type)
+ nodemgr_register_device(ne, ud, &ne->device);
+
+ /* process the child unit */
+ ud_child = nodemgr_process_unit_directory(ne, kv, id, ud);
+
+ if (ud_child == NULL)
+ break;
+
+ /* inherit unspecified values, the driver core picks it up */
+ if ((ud->flags & UNIT_DIRECTORY_MODEL_ID) &&
+ !(ud_child->flags & UNIT_DIRECTORY_MODEL_ID))
+ {
+ ud_child->flags |= UNIT_DIRECTORY_MODEL_ID;
+ ud_child->model_id = ud->model_id;
+ }
+ if ((ud->flags & UNIT_DIRECTORY_SPECIFIER_ID) &&
+ !(ud_child->flags & UNIT_DIRECTORY_SPECIFIER_ID))
+ {
+ ud_child->flags |= UNIT_DIRECTORY_SPECIFIER_ID;
+ ud_child->specifier_id = ud->specifier_id;
+ }
+ if ((ud->flags & UNIT_DIRECTORY_VERSION) &&
+ !(ud_child->flags & UNIT_DIRECTORY_VERSION))
+ {
+ ud_child->flags |= UNIT_DIRECTORY_VERSION;
+ ud_child->version = ud->version;
+ }
+
+ /* register the child unit */
+ ud_child->flags |= UNIT_DIRECTORY_LUN_DIRECTORY;
+ nodemgr_register_device(ne, ud_child, &ud->device);
+ }
+
+ break;
+
+ case CSR1212_KV_ID_DIRECTORY_ID:
+ ud->directory_id = kv->value.immediate;
+ break;
+
+ default:
+ break;
+ }
+ last_key_id = kv->key.id;
+ }
+
+ /* do not process child units here and only if not already registered */
+ if (!parent && ud->device.bus != &ieee1394_bus_type)
+ nodemgr_register_device(ne, ud, &ne->device);
+
+ return ud;
+
+unit_directory_error:
+ kfree(ud);
+ return NULL;
+}
+
+
+static void nodemgr_process_root_directory(struct node_entry *ne)
+{
+ unsigned int ud_id = 0;
+ struct csr1212_dentry *dentry;
+ struct csr1212_keyval *kv, *vendor_name_kv = NULL;
+ u8 last_key_id = 0;
+
+ ne->needs_probe = false;
+
+ csr1212_for_each_dir_entry(ne->csr, kv, ne->csr->root_kv, dentry) {
+ switch (kv->key.id) {
+ case CSR1212_KV_ID_VENDOR:
+ ne->vendor_id = kv->value.immediate;
+ break;
+
+ case CSR1212_KV_ID_NODE_CAPABILITIES:
+ ne->capabilities = kv->value.immediate;
+ break;
+
+ case CSR1212_KV_ID_UNIT:
+ nodemgr_process_unit_directory(ne, kv, &ud_id, NULL);
+ break;
+
+ case CSR1212_KV_ID_DESCRIPTOR:
+ if (last_key_id == CSR1212_KV_ID_VENDOR) {
+ if (kv->key.type == CSR1212_KV_TYPE_LEAF &&
+ CSR1212_DESCRIPTOR_LEAF_TYPE(kv) == 0 &&
+ CSR1212_DESCRIPTOR_LEAF_SPECIFIER_ID(kv) == 0 &&
+ CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH(kv) == 0 &&
+ CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET(kv) == 0 &&
+ CSR1212_TEXTUAL_DESCRIPTOR_LEAF_LANGUAGE(kv) == 0) {
+ csr1212_keep_keyval(kv);
+ vendor_name_kv = kv;
+ }
+ }
+ break;
+ }
+ last_key_id = kv->key.id;
+ }
+
+ if (ne->vendor_name_kv) {
+ kv = ne->vendor_name_kv;
+ ne->vendor_name_kv = vendor_name_kv;
+ csr1212_release_keyval(kv);
+ } else if (vendor_name_kv) {
+ ne->vendor_name_kv = vendor_name_kv;
+ if (device_create_file(&ne->device,
+ &dev_attr_ne_vendor_name_kv) != 0)
+ HPSB_ERR("Failed to add sysfs attribute");
+ }
+}
+
+#ifdef CONFIG_HOTPLUG
+
+static int nodemgr_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+ struct unit_directory *ud;
+ int retval = 0;
+ /* ieee1394:venNmoNspNverN */
+ char buf[8 + 1 + 3 + 8 + 2 + 8 + 2 + 8 + 3 + 8 + 1];
+
+ if (!dev)
+ return -ENODEV;
+
+ ud = container_of(dev, struct unit_directory, unit_dev);
+
+ if (ud->ne->in_limbo || ud->ignore_driver)
+ return -ENODEV;
+
+#define PUT_ENVP(fmt,val) \
+do { \
+ retval = add_uevent_var(env, fmt, val); \
+ if (retval) \
+ return retval; \
+} while (0)
+
+ PUT_ENVP("VENDOR_ID=%06x", ud->vendor_id);
+ PUT_ENVP("MODEL_ID=%06x", ud->model_id);
+ PUT_ENVP("GUID=%016Lx", (unsigned long long)ud->ne->guid);
+ PUT_ENVP("SPECIFIER_ID=%06x", ud->specifier_id);
+ PUT_ENVP("VERSION=%06x", ud->version);
+ snprintf(buf, sizeof(buf), "ieee1394:ven%08Xmo%08Xsp%08Xver%08X",
+ ud->vendor_id,
+ ud->model_id,
+ ud->specifier_id,
+ ud->version);
+ PUT_ENVP("MODALIAS=%s", buf);
+
+#undef PUT_ENVP
+
+ return 0;
+}
+
+#else
+
+static int nodemgr_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+ return -ENODEV;
+}
+
+#endif /* CONFIG_HOTPLUG */
+
+
+int __hpsb_register_protocol(struct hpsb_protocol_driver *drv,
+ struct module *owner)
+{
+ int error;
+
+ drv->driver.bus = &ieee1394_bus_type;
+ drv->driver.owner = owner;
+ drv->driver.name = drv->name;
+
+ /* This will cause a probe for devices */
+ error = driver_register(&drv->driver);
+ if (!error)
+ nodemgr_create_drv_files(drv);
+ return error;
+}
+
+void hpsb_unregister_protocol(struct hpsb_protocol_driver *driver)
+{
+ nodemgr_remove_drv_files(driver);
+ /* This will subsequently disconnect all devices that our driver
+ * is attached to. */
+ driver_unregister(&driver->driver);
+}
+
+
+/*
+ * This function updates nodes that were present on the bus before the
+ * reset and still are after the reset. The nodeid and the config rom
+ * may have changed, and the drivers managing this device must be
+ * informed that this device just went through a bus reset, to allow
+ * the to take whatever actions required.
+ */
+static void nodemgr_update_node(struct node_entry *ne, struct csr1212_csr *csr,
+ nodeid_t nodeid, unsigned int generation)
+{
+ if (ne->nodeid != nodeid) {
+ HPSB_DEBUG("Node changed: " NODE_BUS_FMT " -> " NODE_BUS_FMT,
+ NODE_BUS_ARGS(ne->host, ne->nodeid),
+ NODE_BUS_ARGS(ne->host, nodeid));
+ ne->nodeid = nodeid;
+ }
+
+ if (ne->busopt.generation != ((be32_to_cpu(csr->bus_info_data[2]) >> 4) & 0xf)) {
+ kfree(ne->csr->private);
+ csr1212_destroy_csr(ne->csr);
+ ne->csr = csr;
+
+ /* If the node's configrom generation has changed, we
+ * unregister all the unit directories. */
+ nodemgr_remove_uds(ne);
+
+ nodemgr_update_bus_options(ne);
+
+ /* Mark the node as new, so it gets re-probed */
+ ne->needs_probe = true;
+ } else {
+ /* old cache is valid, so update its generation */
+ struct nodemgr_csr_info *ci = ne->csr->private;
+ ci->generation = generation;
+ /* free the partially filled now unneeded new cache */
+ kfree(csr->private);
+ csr1212_destroy_csr(csr);
+ }
+
+ /* Mark the node current */
+ ne->generation = generation;
+
+ if (ne->in_limbo) {
+ device_remove_file(&ne->device, &dev_attr_ne_in_limbo);
+ ne->in_limbo = false;
+
+ HPSB_DEBUG("Node reactivated: "
+ "ID:BUS[" NODE_BUS_FMT "] GUID[%016Lx]",
+ NODE_BUS_ARGS(ne->host, ne->nodeid),
+ (unsigned long long)ne->guid);
+ }
+}
+
+static void nodemgr_node_scan_one(struct hpsb_host *host,
+ nodeid_t nodeid, int generation)
+{
+ struct node_entry *ne;
+ octlet_t guid;
+ struct csr1212_csr *csr;
+ struct nodemgr_csr_info *ci;
+ u8 *speed;
+
+ ci = kmalloc(sizeof(*ci), GFP_KERNEL);
+ if (!ci)
+ return;
+
+ ci->host = host;
+ ci->nodeid = nodeid;
+ ci->generation = generation;
+
+ /* Prepare for speed probe which occurs when reading the ROM */
+ speed = &(host->speed[NODEID_TO_NODE(nodeid)]);
+ if (*speed > host->csr.lnk_spd)
+ *speed = host->csr.lnk_spd;
+ ci->speed_unverified = *speed > IEEE1394_SPEED_100;
+
+ /* We need to detect when the ConfigROM's generation has changed,
+ * so we only update the node's info when it needs to be. */
+
+ csr = csr1212_create_csr(&nodemgr_csr_ops, 5 * sizeof(quadlet_t), ci);
+ if (!csr || csr1212_parse_csr(csr) != CSR1212_SUCCESS) {
+ HPSB_ERR("Error parsing configrom for node " NODE_BUS_FMT,
+ NODE_BUS_ARGS(host, nodeid));
+ if (csr)
+ csr1212_destroy_csr(csr);
+ kfree(ci);
+ return;
+ }
+
+ if (csr->bus_info_data[1] != IEEE1394_BUSID_MAGIC) {
+ /* This isn't a 1394 device, but we let it slide. There
+ * was a report of a device with broken firmware which
+ * reported '2394' instead of '1394', which is obviously a
+ * mistake. One would hope that a non-1394 device never
+ * gets connected to Firewire bus. If someone does, we
+ * shouldn't be held responsible, so we'll allow it with a
+ * warning. */
+ HPSB_WARN("Node " NODE_BUS_FMT " has invalid busID magic [0x%08x]",
+ NODE_BUS_ARGS(host, nodeid), csr->bus_info_data[1]);
+ }
+
+ guid = ((u64)be32_to_cpu(csr->bus_info_data[3]) << 32) | be32_to_cpu(csr->bus_info_data[4]);
+ ne = find_entry_by_guid(guid);
+
+ if (ne && ne->host != host && ne->in_limbo) {
+ /* Must have moved this device from one host to another */
+ nodemgr_remove_ne(ne);
+ ne = NULL;
+ }
+
+ if (!ne)
+ nodemgr_create_node(guid, csr, host, nodeid, generation);
+ else
+ nodemgr_update_node(ne, csr, nodeid, generation);
+}
+
+
+static void nodemgr_node_scan(struct hpsb_host *host, int generation)
+{
+ int count;
+ struct selfid *sid = (struct selfid *)host->topology_map;
+ nodeid_t nodeid = LOCAL_BUS;
+
+ /* Scan each node on the bus */
+ for (count = host->selfid_count; count; count--, sid++) {
+ if (sid->extended)
+ continue;
+
+ if (!sid->link_active) {
+ nodeid++;
+ continue;
+ }
+ nodemgr_node_scan_one(host, nodeid++, generation);
+ }
+}
+
+static void nodemgr_pause_ne(struct node_entry *ne)
+{
+ HPSB_DEBUG("Node paused: ID:BUS[" NODE_BUS_FMT "] GUID[%016Lx]",
+ NODE_BUS_ARGS(ne->host, ne->nodeid),
+ (unsigned long long)ne->guid);
+
+ ne->in_limbo = true;
+ WARN_ON(device_create_file(&ne->device, &dev_attr_ne_in_limbo));
+}
+
+static int update_pdrv(struct device *dev, void *data)
+{
+ struct unit_directory *ud;
+ struct device_driver *drv;
+ struct hpsb_protocol_driver *pdrv;
+ struct node_entry *ne = data;
+ int error;
+
+ ud = container_of(dev, struct unit_directory, unit_dev);
+ if (ud->ne == ne) {
+ drv = get_driver(ud->device.driver);
+ if (drv) {
+ error = 0;
+ pdrv = container_of(drv, struct hpsb_protocol_driver,
+ driver);
+ if (pdrv->update) {
+ down(&ud->device.sem);
+ error = pdrv->update(ud);
+ up(&ud->device.sem);
+ }
+ if (error)
+ device_release_driver(&ud->device);
+ put_driver(drv);
+ }
+ }
+
+ return 0;
+}
+
+static void nodemgr_update_pdrv(struct node_entry *ne)
+{
+ class_for_each_device(&nodemgr_ud_class, NULL, ne, update_pdrv);
+}
+
+/* Write the BROADCAST_CHANNEL as per IEEE1394a 8.3.2.3.11 and 8.4.2.3. This
+ * seems like an optional service but in the end it is practically mandatory
+ * as a consequence of these clauses.
+ *
+ * Note that we cannot do a broadcast write to all nodes at once because some
+ * pre-1394a devices would hang. */
+static void nodemgr_irm_write_bc(struct node_entry *ne, int generation)
+{
+ const u64 bc_addr = (CSR_REGISTER_BASE | CSR_BROADCAST_CHANNEL);
+ quadlet_t bc_remote, bc_local;
+ int error;
+
+ if (!ne->host->is_irm || ne->generation != generation ||
+ ne->nodeid == ne->host->node_id)
+ return;
+
+ bc_local = cpu_to_be32(ne->host->csr.broadcast_channel);
+
+ /* Check if the register is implemented and 1394a compliant. */
+ error = hpsb_read(ne->host, ne->nodeid, generation, bc_addr, &bc_remote,
+ sizeof(bc_remote));
+ if (!error && bc_remote & cpu_to_be32(0x80000000) &&
+ bc_remote != bc_local)
+ hpsb_node_write(ne, bc_addr, &bc_local, sizeof(bc_local));
+}
+
+
+static void nodemgr_probe_ne(struct hpsb_host *host, struct node_entry *ne,
+ int generation)
+{
+ struct device *dev;
+
+ if (ne->host != host || ne->in_limbo)
+ return;
+
+ dev = get_device(&ne->device);
+ if (!dev)
+ return;
+
+ nodemgr_irm_write_bc(ne, generation);
+
+ /* If "needs_probe", then this is either a new or changed node we
+ * rescan totally. If the generation matches for an existing node
+ * (one that existed prior to the bus reset) we send update calls
+ * down to the drivers. Otherwise, this is a dead node and we
+ * suspend it. */
+ if (ne->needs_probe)
+ nodemgr_process_root_directory(ne);
+ else if (ne->generation == generation)
+ nodemgr_update_pdrv(ne);
+ else
+ nodemgr_pause_ne(ne);
+
+ put_device(dev);
+}
+
+struct node_probe_parameter {
+ struct hpsb_host *host;
+ int generation;
+ bool probe_now;
+};
+
+static int node_probe(struct device *dev, void *data)
+{
+ struct node_probe_parameter *p = data;
+ struct node_entry *ne;
+
+ if (p->generation != get_hpsb_generation(p->host))
+ return -EAGAIN;
+
+ ne = container_of(dev, struct node_entry, node_dev);
+ if (ne->needs_probe == p->probe_now)
+ nodemgr_probe_ne(p->host, ne, p->generation);
+ return 0;
+}
+
+static int nodemgr_node_probe(struct hpsb_host *host, int generation)
+{
+ struct node_probe_parameter p;
+
+ p.host = host;
+ p.generation = generation;
+ /*
+ * Do some processing of the nodes we've probed. This pulls them
+ * into the sysfs layer if needed, and can result in processing of
+ * unit-directories, or just updating the node and it's
+ * unit-directories.
+ *
+ * Run updates before probes. Usually, updates are time-critical
+ * while probes are time-consuming.
+ *
+ * Meanwhile, another bus reset may have happened. In this case we
+ * skip everything here and let the next bus scan handle it.
+ * Otherwise we may prematurely remove nodes which are still there.
+ */
+ p.probe_now = false;
+ if (class_for_each_device(&nodemgr_ne_class, NULL, &p, node_probe) != 0)
+ return 0;
+
+ p.probe_now = true;
+ if (class_for_each_device(&nodemgr_ne_class, NULL, &p, node_probe) != 0)
+ return 0;
+ /*
+ * Now let's tell the bus to rescan our devices. This may seem
+ * like overhead, but the driver-model core will only scan a
+ * device for a driver when either the device is added, or when a
+ * new driver is added. A bus reset is a good reason to rescan
+ * devices that were there before. For example, an sbp2 device
+ * may become available for login, if the host that held it was
+ * just removed.
+ */
+ if (bus_rescan_devices(&ieee1394_bus_type) != 0)
+ HPSB_DEBUG("bus_rescan_devices had an error");
+
+ return 1;
+}
+
+static int remove_nodes_in_limbo(struct device *dev, void *data)
+{
+ struct node_entry *ne;
+
+ if (dev->bus != &ieee1394_bus_type)
+ return 0;
+
+ ne = container_of(dev, struct node_entry, device);
+ if (ne->in_limbo)
+ nodemgr_remove_ne(ne);
+
+ return 0;
+}
+
+static void nodemgr_remove_nodes_in_limbo(struct hpsb_host *host)
+{
+ device_for_each_child(&host->device, NULL, remove_nodes_in_limbo);
+}
+
+static int nodemgr_send_resume_packet(struct hpsb_host *host)
+{
+ struct hpsb_packet *packet;
+ int error = -ENOMEM;
+
+ packet = hpsb_make_phypacket(host,
+ EXTPHYPACKET_TYPE_RESUME |
+ NODEID_TO_NODE(host->node_id) << PHYPACKET_PORT_SHIFT);
+ if (packet) {
+ packet->no_waiter = 1;
+ packet->generation = get_hpsb_generation(host);
+ error = hpsb_send_packet(packet);
+ }
+ if (error)
+ HPSB_WARN("fw-host%d: Failed to broadcast resume packet",
+ host->id);
+ return error;
+}
+
+/* Perform a few high-level IRM responsibilities. */
+static int nodemgr_do_irm_duties(struct hpsb_host *host, int cycles)
+{
+ quadlet_t bc;
+
+ /* if irm_id == -1 then there is no IRM on this bus */
+ if (!host->is_irm || host->irm_id == (nodeid_t)-1)
+ return 1;
+
+ /* We are a 1394a-2000 compliant IRM. Set the validity bit. */
+ host->csr.broadcast_channel |= 0x40000000;
+
+ /* If there is no bus manager then we should set the root node's
+ * force_root bit to promote bus stability per the 1394
+ * spec. (8.4.2.6) */
+ if (host->busmgr_id == 0xffff && host->node_count > 1)
+ {
+ u16 root_node = host->node_count - 1;
+
+ /* get cycle master capability flag from root node */
+ if (host->is_cycmst ||
+ (!hpsb_read(host, LOCAL_BUS | root_node, get_hpsb_generation(host),
+ (CSR_REGISTER_BASE + CSR_CONFIG_ROM + 2 * sizeof(quadlet_t)),
+ &bc, sizeof(quadlet_t)) &&
+ be32_to_cpu(bc) & 1 << CSR_CMC_SHIFT))
+ hpsb_send_phy_config(host, root_node, -1);
+ else {
+ HPSB_DEBUG("The root node is not cycle master capable; "
+ "selecting a new root node and resetting...");
+
+ if (cycles >= 5) {
+ /* Oh screw it! Just leave the bus as it is */
+ HPSB_DEBUG("Stopping reset loop for IRM sanity");
+ return 1;
+ }
+
+ hpsb_send_phy_config(host, NODEID_TO_NODE(host->node_id), -1);
+ hpsb_reset_bus(host, LONG_RESET_FORCE_ROOT);
+
+ return 0;
+ }
+ }
+
+ /* Some devices suspend their ports while being connected to an inactive
+ * host adapter, i.e. if connected before the low-level driver is
+ * loaded. They become visible either when physically unplugged and
+ * replugged, or when receiving a resume packet. Send one once. */
+ if (!host->resume_packet_sent && !nodemgr_send_resume_packet(host))
+ host->resume_packet_sent = 1;
+
+ return 1;
+}
+
+/* We need to ensure that if we are not the IRM, that the IRM node is capable of
+ * everything we can do, otherwise issue a bus reset and try to become the IRM
+ * ourselves. */
+static int nodemgr_check_irm_capability(struct hpsb_host *host, int cycles)
+{
+ quadlet_t bc;
+ int status;
+
+ if (hpsb_disable_irm || host->is_irm)
+ return 1;
+
+ status = hpsb_read(host, LOCAL_BUS | (host->irm_id),
+ get_hpsb_generation(host),
+ (CSR_REGISTER_BASE | CSR_BROADCAST_CHANNEL),
+ &bc, sizeof(quadlet_t));
+
+ if (status < 0 || !(be32_to_cpu(bc) & 0x80000000)) {
+ /* The current irm node does not have a valid BROADCAST_CHANNEL
+ * register and we do, so reset the bus with force_root set */
+ HPSB_DEBUG("Current remote IRM is not 1394a-2000 compliant, resetting...");
+
+ if (cycles >= 5) {
+ /* Oh screw it! Just leave the bus as it is */
+ HPSB_DEBUG("Stopping reset loop for IRM sanity");
+ return 1;
+ }
+
+ hpsb_send_phy_config(host, NODEID_TO_NODE(host->node_id), -1);
+ hpsb_reset_bus(host, LONG_RESET_FORCE_ROOT);
+
+ return 0;
+ }
+
+ return 1;
+}
+
+static int nodemgr_host_thread(void *data)
+{
+ struct hpsb_host *host = data;
+ unsigned int g, generation = 0;
+ int i, reset_cycles = 0;
+
+ set_freezable();
+ /* Setup our device-model entries */
+ nodemgr_create_host_dev_files(host);
+
+ for (;;) {
+ /* Sleep until next bus reset */
+ set_current_state(TASK_INTERRUPTIBLE);
+ if (get_hpsb_generation(host) == generation &&
+ !kthread_should_stop())
+ schedule();
+ __set_current_state(TASK_RUNNING);
+
+ /* Thread may have been woken up to freeze or to exit */
+ if (try_to_freeze())
+ continue;
+ if (kthread_should_stop())
+ goto exit;
+
+ /* Pause for 1/4 second in 1/16 second intervals,
+ * to make sure things settle down. */
+ g = get_hpsb_generation(host);
+ for (i = 0; i < 4 ; i++) {
+ msleep_interruptible(63);
+ try_to_freeze();
+ if (kthread_should_stop())
+ goto exit;
+
+ /* Now get the generation in which the node ID's we collect
+ * are valid. During the bus scan we will use this generation
+ * for the read transactions, so that if another reset occurs
+ * during the scan the transactions will fail instead of
+ * returning bogus data. */
+ generation = get_hpsb_generation(host);
+
+ /* If we get a reset before we are done waiting, then
+ * start the waiting over again */
+ if (generation != g)
+ g = generation, i = 0;
+ }
+
+ if (!nodemgr_check_irm_capability(host, reset_cycles) ||
+ !nodemgr_do_irm_duties(host, reset_cycles)) {
+ reset_cycles++;
+ continue;
+ }
+ reset_cycles = 0;
+
+ /* Scan our nodes to get the bus options and create node
+ * entries. This does not do the sysfs stuff, since that
+ * would trigger uevents and such, which is a bad idea at
+ * this point. */
+ nodemgr_node_scan(host, generation);
+
+ /* This actually does the full probe, with sysfs
+ * registration. */
+ if (!nodemgr_node_probe(host, generation))
+ continue;
+
+ /* Update some of our sysfs symlinks */
+ nodemgr_update_host_dev_links(host);
+
+ /* Sleep 3 seconds */
+ for (i = 3000/200; i; i--) {
+ msleep_interruptible(200);
+ try_to_freeze();
+ if (kthread_should_stop())
+ goto exit;
+
+ if (generation != get_hpsb_generation(host))
+ break;
+ }
+ /* Remove nodes which are gone, unless a bus reset happened */
+ if (!i)
+ nodemgr_remove_nodes_in_limbo(host);
+ }
+exit:
+ HPSB_VERBOSE("NodeMgr: Exiting thread");
+ return 0;
+}
+
+struct per_host_parameter {
+ void *data;
+ int (*cb)(struct hpsb_host *, void *);
+};
+
+static int per_host(struct device *dev, void *data)
+{
+ struct hpsb_host *host;
+ struct per_host_parameter *p = data;
+
+ host = container_of(dev, struct hpsb_host, host_dev);
+ return p->cb(host, p->data);
+}
+
+/**
+ * nodemgr_for_each_host - call a function for each IEEE 1394 host
+ * @data: an address to supply to the callback
+ * @cb: function to call for each host
+ *
+ * Iterate the hosts, calling a given function with supplied data for each host.
+ * If the callback fails on a host, i.e. if it returns a non-zero value, the
+ * iteration is stopped.
+ *
+ * Return value: 0 on success, non-zero on failure (same as returned by last run
+ * of the callback).
+ */
+int nodemgr_for_each_host(void *data, int (*cb)(struct hpsb_host *, void *))
+{
+ struct per_host_parameter p;
+
+ p.cb = cb;
+ p.data = data;
+ return class_for_each_device(&hpsb_host_class, NULL, &p, per_host);
+}
+
+/* The following two convenience functions use a struct node_entry
+ * for addressing a node on the bus. They are intended for use by any
+ * process context, not just the nodemgr thread, so we need to be a
+ * little careful when reading out the node ID and generation. The
+ * thing that can go wrong is that we get the node ID, then a bus
+ * reset occurs, and then we read the generation. The node ID is
+ * possibly invalid, but the generation is current, and we end up
+ * sending a packet to a the wrong node.
+ *
+ * The solution is to make sure we read the generation first, so that
+ * if a reset occurs in the process, we end up with a stale generation
+ * and the transactions will fail instead of silently using wrong node
+ * ID's.
+ */
+
+/**
+ * hpsb_node_fill_packet - fill some destination information into a packet
+ * @ne: destination node
+ * @packet: packet to fill in
+ *
+ * This will fill in the given, pre-initialised hpsb_packet with the current
+ * information from the node entry (host, node ID, bus generation number).
+ */
+void hpsb_node_fill_packet(struct node_entry *ne, struct hpsb_packet *packet)
+{
+ packet->host = ne->host;
+ packet->generation = ne->generation;
+ barrier();
+ packet->node_id = ne->nodeid;
+}
+
+int hpsb_node_write(struct node_entry *ne, u64 addr,
+ quadlet_t *buffer, size_t length)
+{
+ unsigned int generation = ne->generation;
+
+ barrier();
+ return hpsb_write(ne->host, ne->nodeid, generation,
+ addr, buffer, length);
+}
+
+static void nodemgr_add_host(struct hpsb_host *host)
+{
+ struct host_info *hi;
+
+ hi = hpsb_create_hostinfo(&nodemgr_highlevel, host, sizeof(*hi));
+ if (!hi) {
+ HPSB_ERR("NodeMgr: out of memory in add host");
+ return;
+ }
+ hi->host = host;
+ hi->thread = kthread_run(nodemgr_host_thread, host, "knodemgrd_%d",
+ host->id);
+ if (IS_ERR(hi->thread)) {
+ HPSB_ERR("NodeMgr: cannot start thread for host %d", host->id);
+ hpsb_destroy_hostinfo(&nodemgr_highlevel, host);
+ }
+}
+
+static void nodemgr_host_reset(struct hpsb_host *host)
+{
+ struct host_info *hi = hpsb_get_hostinfo(&nodemgr_highlevel, host);
+
+ if (hi) {
+ HPSB_VERBOSE("NodeMgr: Processing reset for host %d", host->id);
+ wake_up_process(hi->thread);
+ }
+}
+
+static void nodemgr_remove_host(struct hpsb_host *host)
+{
+ struct host_info *hi = hpsb_get_hostinfo(&nodemgr_highlevel, host);
+
+ if (hi) {
+ kthread_stop(hi->thread);
+ nodemgr_remove_host_dev(&host->device);
+ }
+}
+
+static struct hpsb_highlevel nodemgr_highlevel = {
+ .name = "Node manager",
+ .add_host = nodemgr_add_host,
+ .host_reset = nodemgr_host_reset,
+ .remove_host = nodemgr_remove_host,
+};
+
+int init_ieee1394_nodemgr(void)
+{
+ int error;
+
+ error = class_register(&nodemgr_ne_class);
+ if (error)
+ goto fail_ne;
+ error = class_register(&nodemgr_ud_class);
+ if (error)
+ goto fail_ud;
+ error = driver_register(&nodemgr_mid_layer_driver);
+ if (error)
+ goto fail_ml;
+ /* This driver is not used if nodemgr is off (disable_nodemgr=1). */
+ nodemgr_dev_template_host.driver = &nodemgr_mid_layer_driver;
+
+ hpsb_register_highlevel(&nodemgr_highlevel);
+ return 0;
+
+fail_ml:
+ class_unregister(&nodemgr_ud_class);
+fail_ud:
+ class_unregister(&nodemgr_ne_class);
+fail_ne:
+ return error;
+}
+
+void cleanup_ieee1394_nodemgr(void)
+{
+ hpsb_unregister_highlevel(&nodemgr_highlevel);
+ driver_unregister(&nodemgr_mid_layer_driver);
+ class_unregister(&nodemgr_ud_class);
+ class_unregister(&nodemgr_ne_class);
+}
diff --git a/drivers/ieee1394/nodemgr.h b/drivers/ieee1394/nodemgr.h
new file mode 100644
index 0000000..4f287a3
--- /dev/null
+++ b/drivers/ieee1394/nodemgr.h
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2000 Andreas E. Bombe
+ * 2001 Ben Collins <bcollins@debian.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _IEEE1394_NODEMGR_H
+#define _IEEE1394_NODEMGR_H
+
+#include <linux/device.h>
+#include <asm/types.h>
+
+#include "ieee1394_core.h"
+#include "ieee1394_types.h"
+
+struct csr1212_csr;
+struct csr1212_keyval;
+struct hpsb_host;
+struct ieee1394_device_id;
+
+/* '1' '3' '9' '4' in ASCII */
+#define IEEE1394_BUSID_MAGIC __constant_cpu_to_be32(0x31333934)
+
+/* This is the start of a Node entry structure. It should be a stable API
+ * for which to gather info from the Node Manager about devices attached
+ * to the bus. */
+struct bus_options {
+ u8 irmc; /* Iso Resource Manager Capable */
+ u8 cmc; /* Cycle Master Capable */
+ u8 isc; /* Iso Capable */
+ u8 bmc; /* Bus Master Capable */
+ u8 pmc; /* Power Manager Capable (PNP spec) */
+ u8 cyc_clk_acc; /* Cycle clock accuracy */
+ u8 max_rom; /* Maximum block read supported in the CSR */
+ u8 generation; /* Incremented when configrom changes */
+ u8 lnkspd; /* Link speed */
+ u16 max_rec; /* Maximum packet size node can receive */
+};
+
+#define UNIT_DIRECTORY_VENDOR_ID 0x01
+#define UNIT_DIRECTORY_MODEL_ID 0x02
+#define UNIT_DIRECTORY_SPECIFIER_ID 0x04
+#define UNIT_DIRECTORY_VERSION 0x08
+#define UNIT_DIRECTORY_HAS_LUN_DIRECTORY 0x10
+#define UNIT_DIRECTORY_LUN_DIRECTORY 0x20
+#define UNIT_DIRECTORY_HAS_LUN 0x40
+
+/*
+ * A unit directory corresponds to a protocol supported by the
+ * node. If a node supports eg. IP/1394 and AV/C, its config rom has a
+ * unit directory for each of these protocols.
+ */
+struct unit_directory {
+ struct node_entry *ne; /* The node which this directory belongs to */
+ octlet_t address; /* Address of the unit directory on the node */
+ u8 flags; /* Indicates which entries were read */
+
+ quadlet_t vendor_id;
+ struct csr1212_keyval *vendor_name_kv;
+
+ quadlet_t model_id;
+ struct csr1212_keyval *model_name_kv;
+ quadlet_t specifier_id;
+ quadlet_t version;
+ quadlet_t directory_id;
+
+ unsigned int id;
+
+ int ignore_driver;
+
+ int length; /* Number of quadlets */
+
+ struct device device;
+ struct device unit_dev;
+
+ struct csr1212_keyval *ud_kv;
+ u32 lun; /* logical unit number immediate value */
+};
+
+struct node_entry {
+ u64 guid; /* GUID of this node */
+ u32 guid_vendor_id; /* Top 24bits of guid */
+
+ struct hpsb_host *host; /* Host this node is attached to */
+ nodeid_t nodeid; /* NodeID */
+ struct bus_options busopt; /* Bus Options */
+ bool needs_probe;
+ unsigned int generation; /* Synced with hpsb generation */
+
+ /* The following is read from the config rom */
+ u32 vendor_id;
+ struct csr1212_keyval *vendor_name_kv;
+
+ u32 capabilities;
+
+ struct device device;
+ struct device node_dev;
+
+ /* Means this node is not attached anymore */
+ bool in_limbo;
+
+ struct csr1212_csr *csr;
+};
+
+struct hpsb_protocol_driver {
+ /* The name of the driver, e.g. SBP2 or IP1394 */
+ const char *name;
+
+ /*
+ * The device id table describing the protocols and/or devices
+ * supported by this driver. This is used by the nodemgr to
+ * decide if a driver could support a given node, but the
+ * probe function below can implement further protocol
+ * dependent or vendor dependent checking.
+ */
+ struct ieee1394_device_id *id_table;
+
+ /*
+ * The update function is called when the node has just
+ * survived a bus reset, i.e. it is still present on the bus.
+ * However, it may be necessary to reestablish the connection
+ * or login into the node again, depending on the protocol. If the
+ * probe fails (returns non-zero), we unbind the driver from this
+ * device.
+ */
+ int (*update)(struct unit_directory *ud);
+
+ /* Our LDM structure */
+ struct device_driver driver;
+};
+
+int __hpsb_register_protocol(struct hpsb_protocol_driver *, struct module *);
+static inline int hpsb_register_protocol(struct hpsb_protocol_driver *driver)
+{
+ return __hpsb_register_protocol(driver, THIS_MODULE);
+}
+
+void hpsb_unregister_protocol(struct hpsb_protocol_driver *driver);
+
+static inline int hpsb_node_entry_valid(struct node_entry *ne)
+{
+ return ne->generation == get_hpsb_generation(ne->host);
+}
+void hpsb_node_fill_packet(struct node_entry *ne, struct hpsb_packet *packet);
+int hpsb_node_write(struct node_entry *ne, u64 addr,
+ quadlet_t *buffer, size_t length);
+int nodemgr_for_each_host(void *data, int (*cb)(struct hpsb_host *, void *));
+
+int init_ieee1394_nodemgr(void);
+void cleanup_ieee1394_nodemgr(void);
+
+/* The template for a host device */
+extern struct device nodemgr_dev_template_host;
+
+/* Bus attributes we export */
+extern struct bus_attribute *const fw_bus_attrs[];
+
+#endif /* _IEEE1394_NODEMGR_H */
diff --git a/drivers/ieee1394/ohci1394.c b/drivers/ieee1394/ohci1394.c
new file mode 100644
index 0000000..e509e13
--- /dev/null
+++ b/drivers/ieee1394/ohci1394.c
@@ -0,0 +1,3582 @@
+/*
+ * ohci1394.c - driver for OHCI 1394 boards
+ * Copyright (C)1999,2000 Sebastien Rougeaux <sebastien.rougeaux@anu.edu.au>
+ * Gord Peters <GordPeters@smarttech.com>
+ * 2001 Ben Collins <bcollins@debian.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * Things known to be working:
+ * . Async Request Transmit
+ * . Async Response Receive
+ * . Async Request Receive
+ * . Async Response Transmit
+ * . Iso Receive
+ * . DMA mmap for iso receive
+ * . Config ROM generation
+ *
+ * Things implemented, but still in test phase:
+ * . Iso Transmit
+ * . Async Stream Packets Transmit (Receive done via Iso interface)
+ *
+ * Things not implemented:
+ * . DMA error recovery
+ *
+ * Known bugs:
+ * . devctl BUS_RESET arg confusion (reset type or root holdoff?)
+ * added LONG_RESET_ROOT and SHORT_RESET_ROOT for root holdoff --kk
+ */
+
+/*
+ * Acknowledgments:
+ *
+ * Adam J Richter <adam@yggdrasil.com>
+ * . Use of pci_class to find device
+ *
+ * Emilie Chung <emilie.chung@axis.com>
+ * . Tip on Async Request Filter
+ *
+ * Pascal Drolet <pascal.drolet@informission.ca>
+ * . Various tips for optimization and functionnalities
+ *
+ * Robert Ficklin <rficklin@westengineering.com>
+ * . Loop in irq_handler
+ *
+ * James Goodwin <jamesg@Filanet.com>
+ * . Various tips on initialization, self-id reception, etc.
+ *
+ * Albrecht Dress <ad@mpifr-bonn.mpg.de>
+ * . Apple PowerBook detection
+ *
+ * Daniel Kobras <daniel.kobras@student.uni-tuebingen.de>
+ * . Reset the board properly before leaving + misc cleanups
+ *
+ * Leon van Stuivenberg <leonvs@iae.nl>
+ * . Bug fixes
+ *
+ * Ben Collins <bcollins@debian.org>
+ * . Working big-endian support
+ * . Updated to 2.4.x module scheme (PCI aswell)
+ * . Config ROM generation
+ *
+ * Manfred Weihs <weihs@ict.tuwien.ac.at>
+ * . Reworked code for initiating bus resets
+ * (long, short, with or without hold-off)
+ *
+ * Nandu Santhi <contactnandu@users.sourceforge.net>
+ * . Added support for nVidia nForce2 onboard Firewire chipset
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/wait.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/pci.h>
+#include <linux/fs.h>
+#include <linux/poll.h>
+#include <asm/byteorder.h>
+#include <asm/atomic.h>
+#include <asm/uaccess.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+
+#include <asm/pgtable.h>
+#include <asm/page.h>
+#include <asm/irq.h>
+#include <linux/types.h>
+#include <linux/vmalloc.h>
+#include <linux/init.h>
+
+#ifdef CONFIG_PPC_PMAC
+#include <asm/machdep.h>
+#include <asm/pmac_feature.h>
+#include <asm/prom.h>
+#include <asm/pci-bridge.h>
+#endif
+
+#include "csr1212.h"
+#include "ieee1394.h"
+#include "ieee1394_types.h"
+#include "hosts.h"
+#include "dma.h"
+#include "iso.h"
+#include "ieee1394_core.h"
+#include "highlevel.h"
+#include "ohci1394.h"
+
+#ifdef CONFIG_IEEE1394_VERBOSEDEBUG
+#define OHCI1394_DEBUG
+#endif
+
+#ifdef DBGMSG
+#undef DBGMSG
+#endif
+
+#ifdef OHCI1394_DEBUG
+#define DBGMSG(fmt, args...) \
+printk(KERN_INFO "%s: fw-host%d: " fmt "\n" , OHCI1394_DRIVER_NAME, ohci->host->id , ## args)
+#else
+#define DBGMSG(fmt, args...) do {} while (0)
+#endif
+
+/* print general (card independent) information */
+#define PRINT_G(level, fmt, args...) \
+printk(level "%s: " fmt "\n" , OHCI1394_DRIVER_NAME , ## args)
+
+/* print card specific information */
+#define PRINT(level, fmt, args...) \
+printk(level "%s: fw-host%d: " fmt "\n" , OHCI1394_DRIVER_NAME, ohci->host->id , ## args)
+
+/* Module Parameters */
+static int phys_dma = 1;
+module_param(phys_dma, int, 0444);
+MODULE_PARM_DESC(phys_dma, "Enable physical DMA (default = 1).");
+
+static void dma_trm_tasklet(unsigned long data);
+static void dma_trm_reset(struct dma_trm_ctx *d);
+
+static int alloc_dma_rcv_ctx(struct ti_ohci *ohci, struct dma_rcv_ctx *d,
+ enum context_type type, int ctx, int num_desc,
+ int buf_size, int split_buf_size, int context_base);
+static void free_dma_rcv_ctx(struct dma_rcv_ctx *d);
+
+static int alloc_dma_trm_ctx(struct ti_ohci *ohci, struct dma_trm_ctx *d,
+ enum context_type type, int ctx, int num_desc,
+ int context_base);
+
+static void ohci1394_pci_remove(struct pci_dev *pdev);
+
+#ifndef __LITTLE_ENDIAN
+static const size_t hdr_sizes[] = {
+ 3, /* TCODE_WRITEQ */
+ 4, /* TCODE_WRITEB */
+ 3, /* TCODE_WRITE_RESPONSE */
+ 0, /* reserved */
+ 3, /* TCODE_READQ */
+ 4, /* TCODE_READB */
+ 3, /* TCODE_READQ_RESPONSE */
+ 4, /* TCODE_READB_RESPONSE */
+ 1, /* TCODE_CYCLE_START */
+ 4, /* TCODE_LOCK_REQUEST */
+ 2, /* TCODE_ISO_DATA */
+ 4, /* TCODE_LOCK_RESPONSE */
+ /* rest is reserved or link-internal */
+};
+
+static inline void header_le32_to_cpu(quadlet_t *data, unsigned char tcode)
+{
+ size_t size;
+
+ if (unlikely(tcode >= ARRAY_SIZE(hdr_sizes)))
+ return;
+
+ size = hdr_sizes[tcode];
+ while (size--)
+ data[size] = le32_to_cpu(data[size]);
+}
+#else
+#define header_le32_to_cpu(w,x) do {} while (0)
+#endif /* !LITTLE_ENDIAN */
+
+/***********************************
+ * IEEE-1394 functionality section *
+ ***********************************/
+
+static u8 get_phy_reg(struct ti_ohci *ohci, u8 addr)
+{
+ int i;
+ unsigned long flags;
+ quadlet_t r;
+
+ spin_lock_irqsave (&ohci->phy_reg_lock, flags);
+
+ reg_write(ohci, OHCI1394_PhyControl, (addr << 8) | 0x00008000);
+
+ for (i = 0; i < OHCI_LOOP_COUNT; i++) {
+ if (reg_read(ohci, OHCI1394_PhyControl) & 0x80000000)
+ break;
+
+ mdelay(1);
+ }
+
+ r = reg_read(ohci, OHCI1394_PhyControl);
+
+ if (i >= OHCI_LOOP_COUNT)
+ PRINT (KERN_ERR, "Get PHY Reg timeout [0x%08x/0x%08x/%d]",
+ r, r & 0x80000000, i);
+
+ spin_unlock_irqrestore (&ohci->phy_reg_lock, flags);
+
+ return (r & 0x00ff0000) >> 16;
+}
+
+static void set_phy_reg(struct ti_ohci *ohci, u8 addr, u8 data)
+{
+ int i;
+ unsigned long flags;
+ u32 r = 0;
+
+ spin_lock_irqsave (&ohci->phy_reg_lock, flags);
+
+ reg_write(ohci, OHCI1394_PhyControl, (addr << 8) | data | 0x00004000);
+
+ for (i = 0; i < OHCI_LOOP_COUNT; i++) {
+ r = reg_read(ohci, OHCI1394_PhyControl);
+ if (!(r & 0x00004000))
+ break;
+
+ mdelay(1);
+ }
+
+ if (i == OHCI_LOOP_COUNT)
+ PRINT (KERN_ERR, "Set PHY Reg timeout [0x%08x/0x%08x/%d]",
+ r, r & 0x00004000, i);
+
+ spin_unlock_irqrestore (&ohci->phy_reg_lock, flags);
+
+ return;
+}
+
+/* Or's our value into the current value */
+static void set_phy_reg_mask(struct ti_ohci *ohci, u8 addr, u8 data)
+{
+ u8 old;
+
+ old = get_phy_reg (ohci, addr);
+ old |= data;
+ set_phy_reg (ohci, addr, old);
+
+ return;
+}
+
+static void handle_selfid(struct ti_ohci *ohci, struct hpsb_host *host,
+ int phyid, int isroot)
+{
+ quadlet_t *q = ohci->selfid_buf_cpu;
+ quadlet_t self_id_count=reg_read(ohci, OHCI1394_SelfIDCount);
+ size_t size;
+ quadlet_t q0, q1;
+
+ /* Check status of self-id reception */
+
+ if (ohci->selfid_swap)
+ q0 = le32_to_cpu(q[0]);
+ else
+ q0 = q[0];
+
+ if ((self_id_count & 0x80000000) ||
+ ((self_id_count & 0x00FF0000) != (q0 & 0x00FF0000))) {
+ PRINT(KERN_ERR,
+ "Error in reception of SelfID packets [0x%08x/0x%08x] (count: %d)",
+ self_id_count, q0, ohci->self_id_errors);
+
+ /* Tip by James Goodwin <jamesg@Filanet.com>:
+ * We had an error, generate another bus reset in response. */
+ if (ohci->self_id_errors<OHCI1394_MAX_SELF_ID_ERRORS) {
+ set_phy_reg_mask (ohci, 1, 0x40);
+ ohci->self_id_errors++;
+ } else {
+ PRINT(KERN_ERR,
+ "Too many errors on SelfID error reception, giving up!");
+ }
+ return;
+ }
+
+ /* SelfID Ok, reset error counter. */
+ ohci->self_id_errors = 0;
+
+ size = ((self_id_count & 0x00001FFC) >> 2) - 1;
+ q++;
+
+ while (size > 0) {
+ if (ohci->selfid_swap) {
+ q0 = le32_to_cpu(q[0]);
+ q1 = le32_to_cpu(q[1]);
+ } else {
+ q0 = q[0];
+ q1 = q[1];
+ }
+
+ if (q0 == ~q1) {
+ DBGMSG ("SelfID packet 0x%x received", q0);
+ hpsb_selfid_received(host, cpu_to_be32(q0));
+ if (((q0 & 0x3f000000) >> 24) == phyid)
+ DBGMSG ("SelfID for this node is 0x%08x", q0);
+ } else {
+ PRINT(KERN_ERR,
+ "SelfID is inconsistent [0x%08x/0x%08x]", q0, q1);
+ }
+ q += 2;
+ size -= 2;
+ }
+
+ DBGMSG("SelfID complete");
+
+ return;
+}
+
+static void ohci_soft_reset(struct ti_ohci *ohci) {
+ int i;
+
+ reg_write(ohci, OHCI1394_HCControlSet, OHCI1394_HCControl_softReset);
+
+ for (i = 0; i < OHCI_LOOP_COUNT; i++) {
+ if (!(reg_read(ohci, OHCI1394_HCControlSet) & OHCI1394_HCControl_softReset))
+ break;
+ mdelay(1);
+ }
+ DBGMSG ("Soft reset finished");
+}
+
+
+/* Generate the dma receive prgs and start the context */
+static void initialize_dma_rcv_ctx(struct dma_rcv_ctx *d, int generate_irq)
+{
+ struct ti_ohci *ohci = (struct ti_ohci*)(d->ohci);
+ int i;
+
+ ohci1394_stop_context(ohci, d->ctrlClear, NULL);
+
+ for (i=0; i<d->num_desc; i++) {
+ u32 c;
+
+ c = DMA_CTL_INPUT_MORE | DMA_CTL_UPDATE | DMA_CTL_BRANCH;
+ if (generate_irq)
+ c |= DMA_CTL_IRQ;
+
+ d->prg_cpu[i]->control = cpu_to_le32(c | d->buf_size);
+
+ /* End of descriptor list? */
+ if (i + 1 < d->num_desc) {
+ d->prg_cpu[i]->branchAddress =
+ cpu_to_le32((d->prg_bus[i+1] & 0xfffffff0) | 0x1);
+ } else {
+ d->prg_cpu[i]->branchAddress =
+ cpu_to_le32((d->prg_bus[0] & 0xfffffff0));
+ }
+
+ d->prg_cpu[i]->address = cpu_to_le32(d->buf_bus[i]);
+ d->prg_cpu[i]->status = cpu_to_le32(d->buf_size);
+ }
+
+ d->buf_ind = 0;
+ d->buf_offset = 0;
+
+ if (d->type == DMA_CTX_ISO) {
+ /* Clear contextControl */
+ reg_write(ohci, d->ctrlClear, 0xffffffff);
+
+ /* Set bufferFill, isochHeader, multichannel for IR context */
+ reg_write(ohci, d->ctrlSet, 0xd0000000);
+
+ /* Set the context match register to match on all tags */
+ reg_write(ohci, d->ctxtMatch, 0xf0000000);
+
+ /* Clear the multi channel mask high and low registers */
+ reg_write(ohci, OHCI1394_IRMultiChanMaskHiClear, 0xffffffff);
+ reg_write(ohci, OHCI1394_IRMultiChanMaskLoClear, 0xffffffff);
+
+ /* Set up isoRecvIntMask to generate interrupts */
+ reg_write(ohci, OHCI1394_IsoRecvIntMaskSet, 1 << d->ctx);
+ }
+
+ /* Tell the controller where the first AR program is */
+ reg_write(ohci, d->cmdPtr, d->prg_bus[0] | 0x1);
+
+ /* Run context */
+ reg_write(ohci, d->ctrlSet, 0x00008000);
+
+ DBGMSG("Receive DMA ctx=%d initialized", d->ctx);
+}
+
+/* Initialize the dma transmit context */
+static void initialize_dma_trm_ctx(struct dma_trm_ctx *d)
+{
+ struct ti_ohci *ohci = (struct ti_ohci*)(d->ohci);
+
+ /* Stop the context */
+ ohci1394_stop_context(ohci, d->ctrlClear, NULL);
+
+ d->prg_ind = 0;
+ d->sent_ind = 0;
+ d->free_prgs = d->num_desc;
+ d->branchAddrPtr = NULL;
+ INIT_LIST_HEAD(&d->fifo_list);
+ INIT_LIST_HEAD(&d->pending_list);
+
+ if (d->type == DMA_CTX_ISO) {
+ /* enable interrupts */
+ reg_write(ohci, OHCI1394_IsoXmitIntMaskSet, 1 << d->ctx);
+ }
+
+ DBGMSG("Transmit DMA ctx=%d initialized", d->ctx);
+}
+
+/* Count the number of available iso contexts */
+static int get_nb_iso_ctx(struct ti_ohci *ohci, int reg)
+{
+ int i,ctx=0;
+ u32 tmp;
+
+ reg_write(ohci, reg, 0xffffffff);
+ tmp = reg_read(ohci, reg);
+
+ DBGMSG("Iso contexts reg: %08x implemented: %08x", reg, tmp);
+
+ /* Count the number of contexts */
+ for (i=0; i<32; i++) {
+ if (tmp & 1) ctx++;
+ tmp >>= 1;
+ }
+ return ctx;
+}
+
+/* Global initialization */
+static void ohci_initialize(struct ti_ohci *ohci)
+{
+ quadlet_t buf;
+ int num_ports, i;
+
+ spin_lock_init(&ohci->phy_reg_lock);
+
+ /* Put some defaults to these undefined bus options */
+ buf = reg_read(ohci, OHCI1394_BusOptions);
+ buf |= 0x60000000; /* Enable CMC and ISC */
+ if (hpsb_disable_irm)
+ buf &= ~0x80000000;
+ else
+ buf |= 0x80000000; /* Enable IRMC */
+ buf &= ~0x00ff0000; /* XXX: Set cyc_clk_acc to zero for now */
+ buf &= ~0x18000000; /* Disable PMC and BMC */
+ reg_write(ohci, OHCI1394_BusOptions, buf);
+
+ /* Set the bus number */
+ reg_write(ohci, OHCI1394_NodeID, 0x0000ffc0);
+
+ /* Enable posted writes */
+ reg_write(ohci, OHCI1394_HCControlSet, OHCI1394_HCControl_postedWriteEnable);
+
+ /* Clear link control register */
+ reg_write(ohci, OHCI1394_LinkControlClear, 0xffffffff);
+
+ /* Enable cycle timer and cycle master and set the IRM
+ * contender bit in our self ID packets if appropriate. */
+ reg_write(ohci, OHCI1394_LinkControlSet,
+ OHCI1394_LinkControl_CycleTimerEnable |
+ OHCI1394_LinkControl_CycleMaster);
+ i = get_phy_reg(ohci, 4) | PHY_04_LCTRL;
+ if (hpsb_disable_irm)
+ i &= ~PHY_04_CONTENDER;
+ else
+ i |= PHY_04_CONTENDER;
+ set_phy_reg(ohci, 4, i);
+
+ /* Set up self-id dma buffer */
+ reg_write(ohci, OHCI1394_SelfIDBuffer, ohci->selfid_buf_bus);
+
+ /* enable self-id */
+ reg_write(ohci, OHCI1394_LinkControlSet, OHCI1394_LinkControl_RcvSelfID);
+
+ /* Set the Config ROM mapping register */
+ reg_write(ohci, OHCI1394_ConfigROMmap, ohci->csr_config_rom_bus);
+
+ /* Now get our max packet size */
+ ohci->max_packet_size =
+ 1<<(((reg_read(ohci, OHCI1394_BusOptions)>>12)&0xf)+1);
+
+ /* Clear the interrupt mask */
+ reg_write(ohci, OHCI1394_IsoRecvIntMaskClear, 0xffffffff);
+ reg_write(ohci, OHCI1394_IsoRecvIntEventClear, 0xffffffff);
+
+ /* Clear the interrupt mask */
+ reg_write(ohci, OHCI1394_IsoXmitIntMaskClear, 0xffffffff);
+ reg_write(ohci, OHCI1394_IsoXmitIntEventClear, 0xffffffff);
+
+ /* Initialize AR dma */
+ initialize_dma_rcv_ctx(&ohci->ar_req_context, 0);
+ initialize_dma_rcv_ctx(&ohci->ar_resp_context, 0);
+
+ /* Initialize AT dma */
+ initialize_dma_trm_ctx(&ohci->at_req_context);
+ initialize_dma_trm_ctx(&ohci->at_resp_context);
+
+ /* Accept AR requests from all nodes */
+ reg_write(ohci, OHCI1394_AsReqFilterHiSet, 0x80000000);
+
+ /* Set the address range of the physical response unit.
+ * Most controllers do not implement it as a writable register though.
+ * They will keep a hardwired offset of 0x00010000 and show 0x0 as
+ * register content.
+ * To actually enable physical responses is the job of our interrupt
+ * handler which programs the physical request filter. */
+ reg_write(ohci, OHCI1394_PhyUpperBound,
+ OHCI1394_PHYS_UPPER_BOUND_PROGRAMMED >> 16);
+
+ DBGMSG("physUpperBoundOffset=%08x",
+ reg_read(ohci, OHCI1394_PhyUpperBound));
+
+ /* Specify AT retries */
+ reg_write(ohci, OHCI1394_ATRetries,
+ OHCI1394_MAX_AT_REQ_RETRIES |
+ (OHCI1394_MAX_AT_RESP_RETRIES<<4) |
+ (OHCI1394_MAX_PHYS_RESP_RETRIES<<8));
+
+ /* We don't want hardware swapping */
+ reg_write(ohci, OHCI1394_HCControlClear, OHCI1394_HCControl_noByteSwap);
+
+ /* Enable interrupts */
+ reg_write(ohci, OHCI1394_IntMaskSet,
+ OHCI1394_unrecoverableError |
+ OHCI1394_masterIntEnable |
+ OHCI1394_busReset |
+ OHCI1394_selfIDComplete |
+ OHCI1394_RSPkt |
+ OHCI1394_RQPkt |
+ OHCI1394_respTxComplete |
+ OHCI1394_reqTxComplete |
+ OHCI1394_isochRx |
+ OHCI1394_isochTx |
+ OHCI1394_postedWriteErr |
+ OHCI1394_cycleTooLong |
+ OHCI1394_cycleInconsistent);
+
+ /* Enable link */
+ reg_write(ohci, OHCI1394_HCControlSet, OHCI1394_HCControl_linkEnable);
+
+ buf = reg_read(ohci, OHCI1394_Version);
+ PRINT(KERN_INFO, "OHCI-1394 %d.%d (PCI): IRQ=[%d] "
+ "MMIO=[%llx-%llx] Max Packet=[%d] IR/IT contexts=[%d/%d]",
+ ((((buf) >> 16) & 0xf) + (((buf) >> 20) & 0xf) * 10),
+ ((((buf) >> 4) & 0xf) + ((buf) & 0xf) * 10), ohci->dev->irq,
+ (unsigned long long)pci_resource_start(ohci->dev, 0),
+ (unsigned long long)pci_resource_start(ohci->dev, 0) + OHCI1394_REGISTER_SIZE - 1,
+ ohci->max_packet_size,
+ ohci->nb_iso_rcv_ctx, ohci->nb_iso_xmit_ctx);
+
+ /* Check all of our ports to make sure that if anything is
+ * connected, we enable that port. */
+ num_ports = get_phy_reg(ohci, 2) & 0xf;
+ for (i = 0; i < num_ports; i++) {
+ unsigned int status;
+
+ set_phy_reg(ohci, 7, i);
+ status = get_phy_reg(ohci, 8);
+
+ if (status & 0x20)
+ set_phy_reg(ohci, 8, status & ~1);
+ }
+
+ /* Serial EEPROM Sanity check. */
+ if ((ohci->max_packet_size < 512) ||
+ (ohci->max_packet_size > 4096)) {
+ /* Serial EEPROM contents are suspect, set a sane max packet
+ * size and print the raw contents for bug reports if verbose
+ * debug is enabled. */
+#ifdef CONFIG_IEEE1394_VERBOSEDEBUG
+ int i;
+#endif
+
+ PRINT(KERN_DEBUG, "Serial EEPROM has suspicious values, "
+ "attempting to set max_packet_size to 512 bytes");
+ reg_write(ohci, OHCI1394_BusOptions,
+ (reg_read(ohci, OHCI1394_BusOptions) & 0xf007) | 0x8002);
+ ohci->max_packet_size = 512;
+#ifdef CONFIG_IEEE1394_VERBOSEDEBUG
+ PRINT(KERN_DEBUG, " EEPROM Present: %d",
+ (reg_read(ohci, OHCI1394_Version) >> 24) & 0x1);
+ reg_write(ohci, OHCI1394_GUID_ROM, 0x80000000);
+
+ for (i = 0;
+ ((i < 1000) &&
+ (reg_read(ohci, OHCI1394_GUID_ROM) & 0x80000000)); i++)
+ udelay(10);
+
+ for (i = 0; i < 0x20; i++) {
+ reg_write(ohci, OHCI1394_GUID_ROM, 0x02000000);
+ PRINT(KERN_DEBUG, " EEPROM %02x: %02x", i,
+ (reg_read(ohci, OHCI1394_GUID_ROM) >> 16) & 0xff);
+ }
+#endif
+ }
+}
+
+/*
+ * Insert a packet in the DMA fifo and generate the DMA prg
+ * FIXME: rewrite the program in order to accept packets crossing
+ * page boundaries.
+ * check also that a single dma descriptor doesn't cross a
+ * page boundary.
+ */
+static void insert_packet(struct ti_ohci *ohci,
+ struct dma_trm_ctx *d, struct hpsb_packet *packet)
+{
+ u32 cycleTimer;
+ int idx = d->prg_ind;
+
+ DBGMSG("Inserting packet for node " NODE_BUS_FMT
+ ", tlabel=%d, tcode=0x%x, speed=%d",
+ NODE_BUS_ARGS(ohci->host, packet->node_id), packet->tlabel,
+ packet->tcode, packet->speed_code);
+
+ d->prg_cpu[idx]->begin.address = 0;
+ d->prg_cpu[idx]->begin.branchAddress = 0;
+
+ if (d->type == DMA_CTX_ASYNC_RESP) {
+ /*
+ * For response packets, we need to put a timeout value in
+ * the 16 lower bits of the status... let's try 1 sec timeout
+ */
+ cycleTimer = reg_read(ohci, OHCI1394_IsochronousCycleTimer);
+ d->prg_cpu[idx]->begin.status = cpu_to_le32(
+ (((((cycleTimer>>25)&0x7)+1)&0x7)<<13) |
+ ((cycleTimer&0x01fff000)>>12));
+
+ DBGMSG("cycleTimer: %08x timeStamp: %08x",
+ cycleTimer, d->prg_cpu[idx]->begin.status);
+ } else
+ d->prg_cpu[idx]->begin.status = 0;
+
+ if ( (packet->type == hpsb_async) || (packet->type == hpsb_raw) ) {
+
+ if (packet->type == hpsb_raw) {
+ d->prg_cpu[idx]->data[0] = cpu_to_le32(OHCI1394_TCODE_PHY<<4);
+ d->prg_cpu[idx]->data[1] = cpu_to_le32(packet->header[0]);
+ d->prg_cpu[idx]->data[2] = cpu_to_le32(packet->header[1]);
+ } else {
+ d->prg_cpu[idx]->data[0] = packet->speed_code<<16 |
+ (packet->header[0] & 0xFFFF);
+
+ if (packet->tcode == TCODE_ISO_DATA) {
+ /* Sending an async stream packet */
+ d->prg_cpu[idx]->data[1] = packet->header[0] & 0xFFFF0000;
+ } else {
+ /* Sending a normal async request or response */
+ d->prg_cpu[idx]->data[1] =
+ (packet->header[1] & 0xFFFF) |
+ (packet->header[0] & 0xFFFF0000);
+ d->prg_cpu[idx]->data[2] = packet->header[2];
+ d->prg_cpu[idx]->data[3] = packet->header[3];
+ }
+ header_le32_to_cpu(d->prg_cpu[idx]->data, packet->tcode);
+ }
+
+ if (packet->data_size) { /* block transmit */
+ if (packet->tcode == TCODE_STREAM_DATA){
+ d->prg_cpu[idx]->begin.control =
+ cpu_to_le32(DMA_CTL_OUTPUT_MORE |
+ DMA_CTL_IMMEDIATE | 0x8);
+ } else {
+ d->prg_cpu[idx]->begin.control =
+ cpu_to_le32(DMA_CTL_OUTPUT_MORE |
+ DMA_CTL_IMMEDIATE | 0x10);
+ }
+ d->prg_cpu[idx]->end.control =
+ cpu_to_le32(DMA_CTL_OUTPUT_LAST |
+ DMA_CTL_IRQ |
+ DMA_CTL_BRANCH |
+ packet->data_size);
+ /*
+ * Check that the packet data buffer
+ * does not cross a page boundary.
+ *
+ * XXX Fix this some day. eth1394 seems to trigger
+ * it, but ignoring it doesn't seem to cause a
+ * problem.
+ */
+#if 0
+ if (cross_bound((unsigned long)packet->data,
+ packet->data_size)>0) {
+ /* FIXME: do something about it */
+ PRINT(KERN_ERR,
+ "%s: packet data addr: %p size %Zd bytes "
+ "cross page boundary", __func__,
+ packet->data, packet->data_size);
+ }
+#endif
+ d->prg_cpu[idx]->end.address = cpu_to_le32(
+ pci_map_single(ohci->dev, packet->data,
+ packet->data_size,
+ PCI_DMA_TODEVICE));
+
+ d->prg_cpu[idx]->end.branchAddress = 0;
+ d->prg_cpu[idx]->end.status = 0;
+ if (d->branchAddrPtr)
+ *(d->branchAddrPtr) =
+ cpu_to_le32(d->prg_bus[idx] | 0x3);
+ d->branchAddrPtr =
+ &(d->prg_cpu[idx]->end.branchAddress);
+ } else { /* quadlet transmit */
+ if (packet->type == hpsb_raw)
+ d->prg_cpu[idx]->begin.control =
+ cpu_to_le32(DMA_CTL_OUTPUT_LAST |
+ DMA_CTL_IMMEDIATE |
+ DMA_CTL_IRQ |
+ DMA_CTL_BRANCH |
+ (packet->header_size + 4));
+ else
+ d->prg_cpu[idx]->begin.control =
+ cpu_to_le32(DMA_CTL_OUTPUT_LAST |
+ DMA_CTL_IMMEDIATE |
+ DMA_CTL_IRQ |
+ DMA_CTL_BRANCH |
+ packet->header_size);
+
+ if (d->branchAddrPtr)
+ *(d->branchAddrPtr) =
+ cpu_to_le32(d->prg_bus[idx] | 0x2);
+ d->branchAddrPtr =
+ &(d->prg_cpu[idx]->begin.branchAddress);
+ }
+
+ } else { /* iso packet */
+ d->prg_cpu[idx]->data[0] = packet->speed_code<<16 |
+ (packet->header[0] & 0xFFFF);
+ d->prg_cpu[idx]->data[1] = packet->header[0] & 0xFFFF0000;
+ header_le32_to_cpu(d->prg_cpu[idx]->data, packet->tcode);
+
+ d->prg_cpu[idx]->begin.control =
+ cpu_to_le32(DMA_CTL_OUTPUT_MORE |
+ DMA_CTL_IMMEDIATE | 0x8);
+ d->prg_cpu[idx]->end.control =
+ cpu_to_le32(DMA_CTL_OUTPUT_LAST |
+ DMA_CTL_UPDATE |
+ DMA_CTL_IRQ |
+ DMA_CTL_BRANCH |
+ packet->data_size);
+ d->prg_cpu[idx]->end.address = cpu_to_le32(
+ pci_map_single(ohci->dev, packet->data,
+ packet->data_size, PCI_DMA_TODEVICE));
+
+ d->prg_cpu[idx]->end.branchAddress = 0;
+ d->prg_cpu[idx]->end.status = 0;
+ DBGMSG("Iso xmit context info: header[%08x %08x]\n"
+ " begin=%08x %08x %08x %08x\n"
+ " %08x %08x %08x %08x\n"
+ " end =%08x %08x %08x %08x",
+ d->prg_cpu[idx]->data[0], d->prg_cpu[idx]->data[1],
+ d->prg_cpu[idx]->begin.control,
+ d->prg_cpu[idx]->begin.address,
+ d->prg_cpu[idx]->begin.branchAddress,
+ d->prg_cpu[idx]->begin.status,
+ d->prg_cpu[idx]->data[0],
+ d->prg_cpu[idx]->data[1],
+ d->prg_cpu[idx]->data[2],
+ d->prg_cpu[idx]->data[3],
+ d->prg_cpu[idx]->end.control,
+ d->prg_cpu[idx]->end.address,
+ d->prg_cpu[idx]->end.branchAddress,
+ d->prg_cpu[idx]->end.status);
+ if (d->branchAddrPtr)
+ *(d->branchAddrPtr) = cpu_to_le32(d->prg_bus[idx] | 0x3);
+ d->branchAddrPtr = &(d->prg_cpu[idx]->end.branchAddress);
+ }
+ d->free_prgs--;
+
+ /* queue the packet in the appropriate context queue */
+ list_add_tail(&packet->driver_list, &d->fifo_list);
+ d->prg_ind = (d->prg_ind + 1) % d->num_desc;
+}
+
+/*
+ * This function fills the FIFO with the (eventual) pending packets
+ * and runs or wakes up the DMA prg if necessary.
+ *
+ * The function MUST be called with the d->lock held.
+ */
+static void dma_trm_flush(struct ti_ohci *ohci, struct dma_trm_ctx *d)
+{
+ struct hpsb_packet *packet, *ptmp;
+ int idx = d->prg_ind;
+ int z = 0;
+
+ /* insert the packets into the dma fifo */
+ list_for_each_entry_safe(packet, ptmp, &d->pending_list, driver_list) {
+ if (!d->free_prgs)
+ break;
+
+ /* For the first packet only */
+ if (!z)
+ z = (packet->data_size) ? 3 : 2;
+
+ /* Insert the packet */
+ list_del_init(&packet->driver_list);
+ insert_packet(ohci, d, packet);
+ }
+
+ /* Nothing must have been done, either no free_prgs or no packets */
+ if (z == 0)
+ return;
+
+ /* Is the context running ? (should be unless it is
+ the first packet to be sent in this context) */
+ if (!(reg_read(ohci, d->ctrlSet) & 0x8000)) {
+ u32 nodeId = reg_read(ohci, OHCI1394_NodeID);
+
+ DBGMSG("Starting transmit DMA ctx=%d",d->ctx);
+ reg_write(ohci, d->cmdPtr, d->prg_bus[idx] | z);
+
+ /* Check that the node id is valid, and not 63 */
+ if (!(nodeId & 0x80000000) || (nodeId & 0x3f) == 63)
+ PRINT(KERN_ERR, "Running dma failed because Node ID is not valid");
+ else
+ reg_write(ohci, d->ctrlSet, 0x8000);
+ } else {
+ /* Wake up the dma context if necessary */
+ if (!(reg_read(ohci, d->ctrlSet) & 0x400))
+ DBGMSG("Waking transmit DMA ctx=%d",d->ctx);
+
+ /* do this always, to avoid race condition */
+ reg_write(ohci, d->ctrlSet, 0x1000);
+ }
+
+ return;
+}
+
+/* Transmission of an async or iso packet */
+static int ohci_transmit(struct hpsb_host *host, struct hpsb_packet *packet)
+{
+ struct ti_ohci *ohci = host->hostdata;
+ struct dma_trm_ctx *d;
+ unsigned long flags;
+
+ if (packet->data_size > ohci->max_packet_size) {
+ PRINT(KERN_ERR,
+ "Transmit packet size %Zd is too big",
+ packet->data_size);
+ return -EOVERFLOW;
+ }
+
+ if (packet->type == hpsb_raw)
+ d = &ohci->at_req_context;
+ else if ((packet->tcode & 0x02) && (packet->tcode != TCODE_ISO_DATA))
+ d = &ohci->at_resp_context;
+ else
+ d = &ohci->at_req_context;
+
+ spin_lock_irqsave(&d->lock,flags);
+
+ list_add_tail(&packet->driver_list, &d->pending_list);
+
+ dma_trm_flush(ohci, d);
+
+ spin_unlock_irqrestore(&d->lock,flags);
+
+ return 0;
+}
+
+static int ohci_devctl(struct hpsb_host *host, enum devctl_cmd cmd, int arg)
+{
+ struct ti_ohci *ohci = host->hostdata;
+ int retval = 0, phy_reg;
+
+ switch (cmd) {
+ case RESET_BUS:
+ switch (arg) {
+ case SHORT_RESET:
+ phy_reg = get_phy_reg(ohci, 5);
+ phy_reg |= 0x40;
+ set_phy_reg(ohci, 5, phy_reg); /* set ISBR */
+ break;
+ case LONG_RESET:
+ phy_reg = get_phy_reg(ohci, 1);
+ phy_reg |= 0x40;
+ set_phy_reg(ohci, 1, phy_reg); /* set IBR */
+ break;
+ case SHORT_RESET_NO_FORCE_ROOT:
+ phy_reg = get_phy_reg(ohci, 1);
+ if (phy_reg & 0x80) {
+ phy_reg &= ~0x80;
+ set_phy_reg(ohci, 1, phy_reg); /* clear RHB */
+ }
+
+ phy_reg = get_phy_reg(ohci, 5);
+ phy_reg |= 0x40;
+ set_phy_reg(ohci, 5, phy_reg); /* set ISBR */
+ break;
+ case LONG_RESET_NO_FORCE_ROOT:
+ phy_reg = get_phy_reg(ohci, 1);
+ phy_reg &= ~0x80;
+ phy_reg |= 0x40;
+ set_phy_reg(ohci, 1, phy_reg); /* clear RHB, set IBR */
+ break;
+ case SHORT_RESET_FORCE_ROOT:
+ phy_reg = get_phy_reg(ohci, 1);
+ if (!(phy_reg & 0x80)) {
+ phy_reg |= 0x80;
+ set_phy_reg(ohci, 1, phy_reg); /* set RHB */
+ }
+
+ phy_reg = get_phy_reg(ohci, 5);
+ phy_reg |= 0x40;
+ set_phy_reg(ohci, 5, phy_reg); /* set ISBR */
+ break;
+ case LONG_RESET_FORCE_ROOT:
+ phy_reg = get_phy_reg(ohci, 1);
+ phy_reg |= 0xc0;
+ set_phy_reg(ohci, 1, phy_reg); /* set RHB and IBR */
+ break;
+ default:
+ retval = -1;
+ }
+ break;
+
+ case GET_CYCLE_COUNTER:
+ retval = reg_read(ohci, OHCI1394_IsochronousCycleTimer);
+ break;
+
+ case SET_CYCLE_COUNTER:
+ reg_write(ohci, OHCI1394_IsochronousCycleTimer, arg);
+ break;
+
+ case SET_BUS_ID:
+ PRINT(KERN_ERR, "devctl command SET_BUS_ID err");
+ break;
+
+ case ACT_CYCLE_MASTER:
+ if (arg) {
+ /* check if we are root and other nodes are present */
+ u32 nodeId = reg_read(ohci, OHCI1394_NodeID);
+ if ((nodeId & (1<<30)) && (nodeId & 0x3f)) {
+ /*
+ * enable cycleTimer, cycleMaster
+ */
+ DBGMSG("Cycle master enabled");
+ reg_write(ohci, OHCI1394_LinkControlSet,
+ OHCI1394_LinkControl_CycleTimerEnable |
+ OHCI1394_LinkControl_CycleMaster);
+ }
+ } else {
+ /* disable cycleTimer, cycleMaster, cycleSource */
+ reg_write(ohci, OHCI1394_LinkControlClear,
+ OHCI1394_LinkControl_CycleTimerEnable |
+ OHCI1394_LinkControl_CycleMaster |
+ OHCI1394_LinkControl_CycleSource);
+ }
+ break;
+
+ case CANCEL_REQUESTS:
+ DBGMSG("Cancel request received");
+ dma_trm_reset(&ohci->at_req_context);
+ dma_trm_reset(&ohci->at_resp_context);
+ break;
+
+ default:
+ PRINT_G(KERN_ERR, "ohci_devctl cmd %d not implemented yet",
+ cmd);
+ break;
+ }
+ return retval;
+}
+
+/***********************************
+ * rawiso ISO reception *
+ ***********************************/
+
+/*
+ We use either buffer-fill or packet-per-buffer DMA mode. The DMA
+ buffer is split into "blocks" (regions described by one DMA
+ descriptor). Each block must be one page or less in size, and
+ must not cross a page boundary.
+
+ There is one little wrinkle with buffer-fill mode: a packet that
+ starts in the final block may wrap around into the first block. But
+ the user API expects all packets to be contiguous. Our solution is
+ to keep the very last page of the DMA buffer in reserve - if a
+ packet spans the gap, we copy its tail into this page.
+*/
+
+struct ohci_iso_recv {
+ struct ti_ohci *ohci;
+
+ struct ohci1394_iso_tasklet task;
+ int task_active;
+
+ enum { BUFFER_FILL_MODE = 0,
+ PACKET_PER_BUFFER_MODE = 1 } dma_mode;
+
+ /* memory and PCI mapping for the DMA descriptors */
+ struct dma_prog_region prog;
+ struct dma_cmd *block; /* = (struct dma_cmd*) prog.virt */
+
+ /* how many DMA blocks fit in the buffer */
+ unsigned int nblocks;
+
+ /* stride of DMA blocks */
+ unsigned int buf_stride;
+
+ /* number of blocks to batch between interrupts */
+ int block_irq_interval;
+
+ /* block that DMA will finish next */
+ int block_dma;
+
+ /* (buffer-fill only) block that the reader will release next */
+ int block_reader;
+
+ /* (buffer-fill only) bytes of buffer the reader has released,
+ less than one block */
+ int released_bytes;
+
+ /* (buffer-fill only) buffer offset at which the next packet will appear */
+ int dma_offset;
+
+ /* OHCI DMA context control registers */
+ u32 ContextControlSet;
+ u32 ContextControlClear;
+ u32 CommandPtr;
+ u32 ContextMatch;
+};
+
+static void ohci_iso_recv_task(unsigned long data);
+static void ohci_iso_recv_stop(struct hpsb_iso *iso);
+static void ohci_iso_recv_shutdown(struct hpsb_iso *iso);
+static int ohci_iso_recv_start(struct hpsb_iso *iso, int cycle, int tag_mask, int sync);
+static void ohci_iso_recv_program(struct hpsb_iso *iso);
+
+static int ohci_iso_recv_init(struct hpsb_iso *iso)
+{
+ struct ti_ohci *ohci = iso->host->hostdata;
+ struct ohci_iso_recv *recv;
+ int ctx;
+ int ret = -ENOMEM;
+
+ recv = kmalloc(sizeof(*recv), GFP_KERNEL);
+ if (!recv)
+ return -ENOMEM;
+
+ iso->hostdata = recv;
+ recv->ohci = ohci;
+ recv->task_active = 0;
+ dma_prog_region_init(&recv->prog);
+ recv->block = NULL;
+
+ /* use buffer-fill mode, unless irq_interval is 1
+ (note: multichannel requires buffer-fill) */
+
+ if (((iso->irq_interval == 1 && iso->dma_mode == HPSB_ISO_DMA_OLD_ABI) ||
+ iso->dma_mode == HPSB_ISO_DMA_PACKET_PER_BUFFER) && iso->channel != -1) {
+ recv->dma_mode = PACKET_PER_BUFFER_MODE;
+ } else {
+ recv->dma_mode = BUFFER_FILL_MODE;
+ }
+
+ /* set nblocks, buf_stride, block_irq_interval */
+
+ if (recv->dma_mode == BUFFER_FILL_MODE) {
+ recv->buf_stride = PAGE_SIZE;
+
+ /* one block per page of data in the DMA buffer, minus the final guard page */
+ recv->nblocks = iso->buf_size/PAGE_SIZE - 1;
+ if (recv->nblocks < 3) {
+ DBGMSG("ohci_iso_recv_init: DMA buffer too small");
+ goto err;
+ }
+
+ /* iso->irq_interval is in packets - translate that to blocks */
+ if (iso->irq_interval == 1)
+ recv->block_irq_interval = 1;
+ else
+ recv->block_irq_interval = iso->irq_interval *
+ ((recv->nblocks+1)/iso->buf_packets);
+ if (recv->block_irq_interval*4 > recv->nblocks)
+ recv->block_irq_interval = recv->nblocks/4;
+ if (recv->block_irq_interval < 1)
+ recv->block_irq_interval = 1;
+
+ } else {
+ int max_packet_size;
+
+ recv->nblocks = iso->buf_packets;
+ recv->block_irq_interval = iso->irq_interval;
+ if (recv->block_irq_interval * 4 > iso->buf_packets)
+ recv->block_irq_interval = iso->buf_packets / 4;
+ if (recv->block_irq_interval < 1)
+ recv->block_irq_interval = 1;
+
+ /* choose a buffer stride */
+ /* must be a power of 2, and <= PAGE_SIZE */
+
+ max_packet_size = iso->buf_size / iso->buf_packets;
+
+ for (recv->buf_stride = 8; recv->buf_stride < max_packet_size;
+ recv->buf_stride *= 2);
+
+ if (recv->buf_stride*iso->buf_packets > iso->buf_size ||
+ recv->buf_stride > PAGE_SIZE) {
+ /* this shouldn't happen, but anyway... */
+ DBGMSG("ohci_iso_recv_init: problem choosing a buffer stride");
+ goto err;
+ }
+ }
+
+ recv->block_reader = 0;
+ recv->released_bytes = 0;
+ recv->block_dma = 0;
+ recv->dma_offset = 0;
+
+ /* size of DMA program = one descriptor per block */
+ if (dma_prog_region_alloc(&recv->prog,
+ sizeof(struct dma_cmd) * recv->nblocks,
+ recv->ohci->dev))
+ goto err;
+
+ recv->block = (struct dma_cmd*) recv->prog.kvirt;
+
+ ohci1394_init_iso_tasklet(&recv->task,
+ iso->channel == -1 ? OHCI_ISO_MULTICHANNEL_RECEIVE :
+ OHCI_ISO_RECEIVE,
+ ohci_iso_recv_task, (unsigned long) iso);
+
+ if (ohci1394_register_iso_tasklet(recv->ohci, &recv->task) < 0) {
+ ret = -EBUSY;
+ goto err;
+ }
+
+ recv->task_active = 1;
+
+ /* recv context registers are spaced 32 bytes apart */
+ ctx = recv->task.context;
+ recv->ContextControlSet = OHCI1394_IsoRcvContextControlSet + 32 * ctx;
+ recv->ContextControlClear = OHCI1394_IsoRcvContextControlClear + 32 * ctx;
+ recv->CommandPtr = OHCI1394_IsoRcvCommandPtr + 32 * ctx;
+ recv->ContextMatch = OHCI1394_IsoRcvContextMatch + 32 * ctx;
+
+ if (iso->channel == -1) {
+ /* clear multi-channel selection mask */
+ reg_write(recv->ohci, OHCI1394_IRMultiChanMaskHiClear, 0xFFFFFFFF);
+ reg_write(recv->ohci, OHCI1394_IRMultiChanMaskLoClear, 0xFFFFFFFF);
+ }
+
+ /* write the DMA program */
+ ohci_iso_recv_program(iso);
+
+ DBGMSG("ohci_iso_recv_init: %s mode, DMA buffer is %lu pages"
+ " (%u bytes), using %u blocks, buf_stride %u, block_irq_interval %d",
+ recv->dma_mode == BUFFER_FILL_MODE ?
+ "buffer-fill" : "packet-per-buffer",
+ iso->buf_size/PAGE_SIZE, iso->buf_size,
+ recv->nblocks, recv->buf_stride, recv->block_irq_interval);
+
+ return 0;
+
+err:
+ ohci_iso_recv_shutdown(iso);
+ return ret;
+}
+
+static void ohci_iso_recv_stop(struct hpsb_iso *iso)
+{
+ struct ohci_iso_recv *recv = iso->hostdata;
+
+ /* disable interrupts */
+ reg_write(recv->ohci, OHCI1394_IsoRecvIntMaskClear, 1 << recv->task.context);
+
+ /* halt DMA */
+ ohci1394_stop_context(recv->ohci, recv->ContextControlClear, NULL);
+}
+
+static void ohci_iso_recv_shutdown(struct hpsb_iso *iso)
+{
+ struct ohci_iso_recv *recv = iso->hostdata;
+
+ if (recv->task_active) {
+ ohci_iso_recv_stop(iso);
+ ohci1394_unregister_iso_tasklet(recv->ohci, &recv->task);
+ recv->task_active = 0;
+ }
+
+ dma_prog_region_free(&recv->prog);
+ kfree(recv);
+ iso->hostdata = NULL;
+}
+
+/* set up a "gapped" ring buffer DMA program */
+static void ohci_iso_recv_program(struct hpsb_iso *iso)
+{
+ struct ohci_iso_recv *recv = iso->hostdata;
+ int blk;
+
+ /* address of 'branch' field in previous DMA descriptor */
+ u32 *prev_branch = NULL;
+
+ for (blk = 0; blk < recv->nblocks; blk++) {
+ u32 control;
+
+ /* the DMA descriptor */
+ struct dma_cmd *cmd = &recv->block[blk];
+
+ /* offset of the DMA descriptor relative to the DMA prog buffer */
+ unsigned long prog_offset = blk * sizeof(struct dma_cmd);
+
+ /* offset of this packet's data within the DMA buffer */
+ unsigned long buf_offset = blk * recv->buf_stride;
+
+ if (recv->dma_mode == BUFFER_FILL_MODE) {
+ control = 2 << 28; /* INPUT_MORE */
+ } else {
+ control = 3 << 28; /* INPUT_LAST */
+ }
+
+ control |= 8 << 24; /* s = 1, update xferStatus and resCount */
+
+ /* interrupt on last block, and at intervals */
+ if (blk == recv->nblocks-1 || (blk % recv->block_irq_interval) == 0) {
+ control |= 3 << 20; /* want interrupt */
+ }
+
+ control |= 3 << 18; /* enable branch to address */
+ control |= recv->buf_stride;
+
+ cmd->control = cpu_to_le32(control);
+ cmd->address = cpu_to_le32(dma_region_offset_to_bus(&iso->data_buf, buf_offset));
+ cmd->branchAddress = 0; /* filled in on next loop */
+ cmd->status = cpu_to_le32(recv->buf_stride);
+
+ /* link the previous descriptor to this one */
+ if (prev_branch) {
+ *prev_branch = cpu_to_le32(dma_prog_region_offset_to_bus(&recv->prog, prog_offset) | 1);
+ }
+
+ prev_branch = &cmd->branchAddress;
+ }
+
+ /* the final descriptor's branch address and Z should be left at 0 */
+}
+
+/* listen or unlisten to a specific channel (multi-channel mode only) */
+static void ohci_iso_recv_change_channel(struct hpsb_iso *iso, unsigned char channel, int listen)
+{
+ struct ohci_iso_recv *recv = iso->hostdata;
+ int reg, i;
+
+ if (channel < 32) {
+ reg = listen ? OHCI1394_IRMultiChanMaskLoSet : OHCI1394_IRMultiChanMaskLoClear;
+ i = channel;
+ } else {
+ reg = listen ? OHCI1394_IRMultiChanMaskHiSet : OHCI1394_IRMultiChanMaskHiClear;
+ i = channel - 32;
+ }
+
+ reg_write(recv->ohci, reg, (1 << i));
+
+ /* issue a dummy read to force all PCI writes to be posted immediately */
+ mb();
+ reg_read(recv->ohci, OHCI1394_IsochronousCycleTimer);
+}
+
+static void ohci_iso_recv_set_channel_mask(struct hpsb_iso *iso, u64 mask)
+{
+ struct ohci_iso_recv *recv = iso->hostdata;
+ int i;
+
+ for (i = 0; i < 64; i++) {
+ if (mask & (1ULL << i)) {
+ if (i < 32)
+ reg_write(recv->ohci, OHCI1394_IRMultiChanMaskLoSet, (1 << i));
+ else
+ reg_write(recv->ohci, OHCI1394_IRMultiChanMaskHiSet, (1 << (i-32)));
+ } else {
+ if (i < 32)
+ reg_write(recv->ohci, OHCI1394_IRMultiChanMaskLoClear, (1 << i));
+ else
+ reg_write(recv->ohci, OHCI1394_IRMultiChanMaskHiClear, (1 << (i-32)));
+ }
+ }
+
+ /* issue a dummy read to force all PCI writes to be posted immediately */
+ mb();
+ reg_read(recv->ohci, OHCI1394_IsochronousCycleTimer);
+}
+
+static int ohci_iso_recv_start(struct hpsb_iso *iso, int cycle, int tag_mask, int sync)
+{
+ struct ohci_iso_recv *recv = iso->hostdata;
+ struct ti_ohci *ohci = recv->ohci;
+ u32 command, contextMatch;
+
+ reg_write(recv->ohci, recv->ContextControlClear, 0xFFFFFFFF);
+ wmb();
+
+ /* always keep ISO headers */
+ command = (1 << 30);
+
+ if (recv->dma_mode == BUFFER_FILL_MODE)
+ command |= (1 << 31);
+
+ reg_write(recv->ohci, recv->ContextControlSet, command);
+
+ /* match on specified tags */
+ contextMatch = tag_mask << 28;
+
+ if (iso->channel == -1) {
+ /* enable multichannel reception */
+ reg_write(recv->ohci, recv->ContextControlSet, (1 << 28));
+ } else {
+ /* listen on channel */
+ contextMatch |= iso->channel;
+ }
+
+ if (cycle != -1) {
+ u32 seconds;
+
+ /* enable cycleMatch */
+ reg_write(recv->ohci, recv->ContextControlSet, (1 << 29));
+
+ /* set starting cycle */
+ cycle &= 0x1FFF;
+
+ /* 'cycle' is only mod 8000, but we also need two 'seconds' bits -
+ just snarf them from the current time */
+ seconds = reg_read(recv->ohci, OHCI1394_IsochronousCycleTimer) >> 25;
+
+ /* advance one second to give some extra time for DMA to start */
+ seconds += 1;
+
+ cycle |= (seconds & 3) << 13;
+
+ contextMatch |= cycle << 12;
+ }
+
+ if (sync != -1) {
+ /* set sync flag on first DMA descriptor */
+ struct dma_cmd *cmd = &recv->block[recv->block_dma];
+ cmd->control |= cpu_to_le32(DMA_CTL_WAIT);
+
+ /* match sync field */
+ contextMatch |= (sync&0xf)<<8;
+ }
+
+ reg_write(recv->ohci, recv->ContextMatch, contextMatch);
+
+ /* address of first descriptor block */
+ command = dma_prog_region_offset_to_bus(&recv->prog,
+ recv->block_dma * sizeof(struct dma_cmd));
+ command |= 1; /* Z=1 */
+
+ reg_write(recv->ohci, recv->CommandPtr, command);
+
+ /* enable interrupts */
+ reg_write(recv->ohci, OHCI1394_IsoRecvIntMaskSet, 1 << recv->task.context);
+
+ wmb();
+
+ /* run */
+ reg_write(recv->ohci, recv->ContextControlSet, 0x8000);
+
+ /* issue a dummy read of the cycle timer register to force
+ all PCI writes to be posted immediately */
+ mb();
+ reg_read(recv->ohci, OHCI1394_IsochronousCycleTimer);
+
+ /* check RUN */
+ if (!(reg_read(recv->ohci, recv->ContextControlSet) & 0x8000)) {
+ PRINT(KERN_ERR,
+ "Error starting IR DMA (ContextControl 0x%08x)\n",
+ reg_read(recv->ohci, recv->ContextControlSet));
+ return -1;
+ }
+
+ return 0;
+}
+
+static void ohci_iso_recv_release_block(struct ohci_iso_recv *recv, int block)
+{
+ /* re-use the DMA descriptor for the block */
+ /* by linking the previous descriptor to it */
+
+ int next_i = block;
+ int prev_i = (next_i == 0) ? (recv->nblocks - 1) : (next_i - 1);
+
+ struct dma_cmd *next = &recv->block[next_i];
+ struct dma_cmd *prev = &recv->block[prev_i];
+
+ /* ignore out-of-range requests */
+ if ((block < 0) || (block > recv->nblocks))
+ return;
+
+ /* 'next' becomes the new end of the DMA chain,
+ so disable branch and enable interrupt */
+ next->branchAddress = 0;
+ next->control |= cpu_to_le32(3 << 20);
+ next->status = cpu_to_le32(recv->buf_stride);
+
+ /* link prev to next */
+ prev->branchAddress = cpu_to_le32(dma_prog_region_offset_to_bus(&recv->prog,
+ sizeof(struct dma_cmd) * next_i)
+ | 1); /* Z=1 */
+
+ /* disable interrupt on previous DMA descriptor, except at intervals */
+ if ((prev_i % recv->block_irq_interval) == 0) {
+ prev->control |= cpu_to_le32(3 << 20); /* enable interrupt */
+ } else {
+ prev->control &= cpu_to_le32(~(3<<20)); /* disable interrupt */
+ }
+ wmb();
+
+ /* wake up DMA in case it fell asleep */
+ reg_write(recv->ohci, recv->ContextControlSet, (1 << 12));
+}
+
+static void ohci_iso_recv_bufferfill_release(struct ohci_iso_recv *recv,
+ struct hpsb_iso_packet_info *info)
+{
+ /* release the memory where the packet was */
+ recv->released_bytes += info->total_len;
+
+ /* have we released enough memory for one block? */
+ while (recv->released_bytes > recv->buf_stride) {
+ ohci_iso_recv_release_block(recv, recv->block_reader);
+ recv->block_reader = (recv->block_reader + 1) % recv->nblocks;
+ recv->released_bytes -= recv->buf_stride;
+ }
+}
+
+static inline void ohci_iso_recv_release(struct hpsb_iso *iso, struct hpsb_iso_packet_info *info)
+{
+ struct ohci_iso_recv *recv = iso->hostdata;
+ if (recv->dma_mode == BUFFER_FILL_MODE) {
+ ohci_iso_recv_bufferfill_release(recv, info);
+ } else {
+ ohci_iso_recv_release_block(recv, info - iso->infos);
+ }
+}
+
+/* parse all packets from blocks that have been fully received */
+static void ohci_iso_recv_bufferfill_parse(struct hpsb_iso *iso, struct ohci_iso_recv *recv)
+{
+ int wake = 0;
+ int runaway = 0;
+ struct ti_ohci *ohci = recv->ohci;
+
+ while (1) {
+ /* we expect the next parsable packet to begin at recv->dma_offset */
+ /* note: packet layout is as shown in section 10.6.1.1 of the OHCI spec */
+
+ unsigned int offset;
+ unsigned short len, cycle, total_len;
+ unsigned char channel, tag, sy;
+
+ unsigned char *p = iso->data_buf.kvirt;
+
+ unsigned int this_block = recv->dma_offset/recv->buf_stride;
+
+ /* don't loop indefinitely */
+ if (runaway++ > 100000) {
+ atomic_inc(&iso->overflows);
+ PRINT(KERN_ERR,
+ "IR DMA error - Runaway during buffer parsing!\n");
+ break;
+ }
+
+ /* stop parsing once we arrive at block_dma (i.e. don't get ahead of DMA) */
+ if (this_block == recv->block_dma)
+ break;
+
+ wake = 1;
+
+ /* parse data length, tag, channel, and sy */
+
+ /* note: we keep our own local copies of 'len' and 'offset'
+ so the user can't mess with them by poking in the mmap area */
+
+ len = p[recv->dma_offset+2] | (p[recv->dma_offset+3] << 8);
+
+ if (len > 4096) {
+ PRINT(KERN_ERR,
+ "IR DMA error - bogus 'len' value %u\n", len);
+ }
+
+ channel = p[recv->dma_offset+1] & 0x3F;
+ tag = p[recv->dma_offset+1] >> 6;
+ sy = p[recv->dma_offset+0] & 0xF;
+
+ /* advance to data payload */
+ recv->dma_offset += 4;
+
+ /* check for wrap-around */
+ if (recv->dma_offset >= recv->buf_stride*recv->nblocks) {
+ recv->dma_offset -= recv->buf_stride*recv->nblocks;
+ }
+
+ /* dma_offset now points to the first byte of the data payload */
+ offset = recv->dma_offset;
+
+ /* advance to xferStatus/timeStamp */
+ recv->dma_offset += len;
+
+ total_len = len + 8; /* 8 bytes header+trailer in OHCI packet */
+ /* payload is padded to 4 bytes */
+ if (len % 4) {
+ recv->dma_offset += 4 - (len%4);
+ total_len += 4 - (len%4);
+ }
+
+ /* check for wrap-around */
+ if (recv->dma_offset >= recv->buf_stride*recv->nblocks) {
+ /* uh oh, the packet data wraps from the last
+ to the first DMA block - make the packet
+ contiguous by copying its "tail" into the
+ guard page */
+
+ int guard_off = recv->buf_stride*recv->nblocks;
+ int tail_len = len - (guard_off - offset);
+
+ if (tail_len > 0 && tail_len < recv->buf_stride) {
+ memcpy(iso->data_buf.kvirt + guard_off,
+ iso->data_buf.kvirt,
+ tail_len);
+ }
+
+ recv->dma_offset -= recv->buf_stride*recv->nblocks;
+ }
+
+ /* parse timestamp */
+ cycle = p[recv->dma_offset+0] | (p[recv->dma_offset+1]<<8);
+ cycle &= 0x1FFF;
+
+ /* advance to next packet */
+ recv->dma_offset += 4;
+
+ /* check for wrap-around */
+ if (recv->dma_offset >= recv->buf_stride*recv->nblocks) {
+ recv->dma_offset -= recv->buf_stride*recv->nblocks;
+ }
+
+ hpsb_iso_packet_received(iso, offset, len, total_len, cycle, channel, tag, sy);
+ }
+
+ if (wake)
+ hpsb_iso_wake(iso);
+}
+
+static void ohci_iso_recv_bufferfill_task(struct hpsb_iso *iso, struct ohci_iso_recv *recv)
+{
+ int loop;
+ struct ti_ohci *ohci = recv->ohci;
+
+ /* loop over all blocks */
+ for (loop = 0; loop < recv->nblocks; loop++) {
+
+ /* check block_dma to see if it's done */
+ struct dma_cmd *im = &recv->block[recv->block_dma];
+
+ /* check the DMA descriptor for new writes to xferStatus */
+ u16 xferstatus = le32_to_cpu(im->status) >> 16;
+
+ /* rescount is the number of bytes *remaining to be written* in the block */
+ u16 rescount = le32_to_cpu(im->status) & 0xFFFF;
+
+ unsigned char event = xferstatus & 0x1F;
+
+ if (!event) {
+ /* nothing has happened to this block yet */
+ break;
+ }
+
+ if (event != 0x11) {
+ atomic_inc(&iso->overflows);
+ PRINT(KERN_ERR,
+ "IR DMA error - OHCI error code 0x%02x\n", event);
+ }
+
+ if (rescount != 0) {
+ /* the card is still writing to this block;
+ we can't touch it until it's done */
+ break;
+ }
+
+ /* OK, the block is finished... */
+
+ /* sync our view of the block */
+ dma_region_sync_for_cpu(&iso->data_buf, recv->block_dma*recv->buf_stride, recv->buf_stride);
+
+ /* reset the DMA descriptor */
+ im->status = recv->buf_stride;
+
+ /* advance block_dma */
+ recv->block_dma = (recv->block_dma + 1) % recv->nblocks;
+
+ if ((recv->block_dma+1) % recv->nblocks == recv->block_reader) {
+ atomic_inc(&iso->overflows);
+ DBGMSG("ISO reception overflow - "
+ "ran out of DMA blocks");
+ }
+ }
+
+ /* parse any packets that have arrived */
+ ohci_iso_recv_bufferfill_parse(iso, recv);
+}
+
+static void ohci_iso_recv_packetperbuf_task(struct hpsb_iso *iso, struct ohci_iso_recv *recv)
+{
+ int count;
+ int wake = 0;
+ struct ti_ohci *ohci = recv->ohci;
+
+ /* loop over the entire buffer */
+ for (count = 0; count < recv->nblocks; count++) {
+ u32 packet_len = 0;
+
+ /* pointer to the DMA descriptor */
+ struct dma_cmd *il = ((struct dma_cmd*) recv->prog.kvirt) + iso->pkt_dma;
+
+ /* check the DMA descriptor for new writes to xferStatus */
+ u16 xferstatus = le32_to_cpu(il->status) >> 16;
+ u16 rescount = le32_to_cpu(il->status) & 0xFFFF;
+
+ unsigned char event = xferstatus & 0x1F;
+
+ if (!event) {
+ /* this packet hasn't come in yet; we are done for now */
+ goto out;
+ }
+
+ if (event == 0x11) {
+ /* packet received successfully! */
+
+ /* rescount is the number of bytes *remaining* in the packet buffer,
+ after the packet was written */
+ packet_len = recv->buf_stride - rescount;
+
+ } else if (event == 0x02) {
+ PRINT(KERN_ERR, "IR DMA error - packet too long for buffer\n");
+ } else if (event) {
+ PRINT(KERN_ERR, "IR DMA error - OHCI error code 0x%02x\n", event);
+ }
+
+ /* sync our view of the buffer */
+ dma_region_sync_for_cpu(&iso->data_buf, iso->pkt_dma * recv->buf_stride, recv->buf_stride);
+
+ /* record the per-packet info */
+ {
+ /* iso header is 8 bytes ahead of the data payload */
+ unsigned char *hdr;
+
+ unsigned int offset;
+ unsigned short cycle;
+ unsigned char channel, tag, sy;
+
+ offset = iso->pkt_dma * recv->buf_stride;
+ hdr = iso->data_buf.kvirt + offset;
+
+ /* skip iso header */
+ offset += 8;
+ packet_len -= 8;
+
+ cycle = (hdr[0] | (hdr[1] << 8)) & 0x1FFF;
+ channel = hdr[5] & 0x3F;
+ tag = hdr[5] >> 6;
+ sy = hdr[4] & 0xF;
+
+ hpsb_iso_packet_received(iso, offset, packet_len,
+ recv->buf_stride, cycle, channel, tag, sy);
+ }
+
+ /* reset the DMA descriptor */
+ il->status = recv->buf_stride;
+
+ wake = 1;
+ recv->block_dma = iso->pkt_dma;
+ }
+
+out:
+ if (wake)
+ hpsb_iso_wake(iso);
+}
+
+static void ohci_iso_recv_task(unsigned long data)
+{
+ struct hpsb_iso *iso = (struct hpsb_iso*) data;
+ struct ohci_iso_recv *recv = iso->hostdata;
+
+ if (recv->dma_mode == BUFFER_FILL_MODE)
+ ohci_iso_recv_bufferfill_task(iso, recv);
+ else
+ ohci_iso_recv_packetperbuf_task(iso, recv);
+}
+
+/***********************************
+ * rawiso ISO transmission *
+ ***********************************/
+
+struct ohci_iso_xmit {
+ struct ti_ohci *ohci;
+ struct dma_prog_region prog;
+ struct ohci1394_iso_tasklet task;
+ int task_active;
+ int last_cycle;
+ atomic_t skips;
+
+ u32 ContextControlSet;
+ u32 ContextControlClear;
+ u32 CommandPtr;
+};
+
+/* transmission DMA program:
+ one OUTPUT_MORE_IMMEDIATE for the IT header
+ one OUTPUT_LAST for the buffer data */
+
+struct iso_xmit_cmd {
+ struct dma_cmd output_more_immediate;
+ u8 iso_hdr[8];
+ u32 unused[2];
+ struct dma_cmd output_last;
+};
+
+static int ohci_iso_xmit_init(struct hpsb_iso *iso);
+static int ohci_iso_xmit_start(struct hpsb_iso *iso, int cycle);
+static void ohci_iso_xmit_shutdown(struct hpsb_iso *iso);
+static void ohci_iso_xmit_task(unsigned long data);
+
+static int ohci_iso_xmit_init(struct hpsb_iso *iso)
+{
+ struct ohci_iso_xmit *xmit;
+ unsigned int prog_size;
+ int ctx;
+ int ret = -ENOMEM;
+
+ xmit = kmalloc(sizeof(*xmit), GFP_KERNEL);
+ if (!xmit)
+ return -ENOMEM;
+
+ iso->hostdata = xmit;
+ xmit->ohci = iso->host->hostdata;
+ xmit->task_active = 0;
+ xmit->last_cycle = -1;
+ atomic_set(&iso->skips, 0);
+
+ dma_prog_region_init(&xmit->prog);
+
+ prog_size = sizeof(struct iso_xmit_cmd) * iso->buf_packets;
+
+ if (dma_prog_region_alloc(&xmit->prog, prog_size, xmit->ohci->dev))
+ goto err;
+
+ ohci1394_init_iso_tasklet(&xmit->task, OHCI_ISO_TRANSMIT,
+ ohci_iso_xmit_task, (unsigned long) iso);
+
+ if (ohci1394_register_iso_tasklet(xmit->ohci, &xmit->task) < 0) {
+ ret = -EBUSY;
+ goto err;
+ }
+
+ xmit->task_active = 1;
+
+ /* xmit context registers are spaced 16 bytes apart */
+ ctx = xmit->task.context;
+ xmit->ContextControlSet = OHCI1394_IsoXmitContextControlSet + 16 * ctx;
+ xmit->ContextControlClear = OHCI1394_IsoXmitContextControlClear + 16 * ctx;
+ xmit->CommandPtr = OHCI1394_IsoXmitCommandPtr + 16 * ctx;
+
+ return 0;
+
+err:
+ ohci_iso_xmit_shutdown(iso);
+ return ret;
+}
+
+static void ohci_iso_xmit_stop(struct hpsb_iso *iso)
+{
+ struct ohci_iso_xmit *xmit = iso->hostdata;
+ struct ti_ohci *ohci = xmit->ohci;
+
+ /* disable interrupts */
+ reg_write(xmit->ohci, OHCI1394_IsoXmitIntMaskClear, 1 << xmit->task.context);
+
+ /* halt DMA */
+ if (ohci1394_stop_context(xmit->ohci, xmit->ContextControlClear, NULL)) {
+ /* XXX the DMA context will lock up if you try to send too much data! */
+ PRINT(KERN_ERR,
+ "you probably exceeded the OHCI card's bandwidth limit - "
+ "reload the module and reduce xmit bandwidth");
+ }
+}
+
+static void ohci_iso_xmit_shutdown(struct hpsb_iso *iso)
+{
+ struct ohci_iso_xmit *xmit = iso->hostdata;
+
+ if (xmit->task_active) {
+ ohci_iso_xmit_stop(iso);
+ ohci1394_unregister_iso_tasklet(xmit->ohci, &xmit->task);
+ xmit->task_active = 0;
+ }
+
+ dma_prog_region_free(&xmit->prog);
+ kfree(xmit);
+ iso->hostdata = NULL;
+}
+
+static void ohci_iso_xmit_task(unsigned long data)
+{
+ struct hpsb_iso *iso = (struct hpsb_iso*) data;
+ struct ohci_iso_xmit *xmit = iso->hostdata;
+ struct ti_ohci *ohci = xmit->ohci;
+ int wake = 0;
+ int count;
+
+ /* check the whole buffer if necessary, starting at pkt_dma */
+ for (count = 0; count < iso->buf_packets; count++) {
+ int cycle;
+
+ /* DMA descriptor */
+ struct iso_xmit_cmd *cmd = dma_region_i(&xmit->prog, struct iso_xmit_cmd, iso->pkt_dma);
+
+ /* check for new writes to xferStatus */
+ u16 xferstatus = le32_to_cpu(cmd->output_last.status) >> 16;
+ u8 event = xferstatus & 0x1F;
+
+ if (!event) {
+ /* packet hasn't been sent yet; we are done for now */
+ break;
+ }
+
+ if (event != 0x11)
+ PRINT(KERN_ERR,
+ "IT DMA error - OHCI error code 0x%02x\n", event);
+
+ /* at least one packet went out, so wake up the writer */
+ wake = 1;
+
+ /* parse cycle */
+ cycle = le32_to_cpu(cmd->output_last.status) & 0x1FFF;
+
+ if (xmit->last_cycle > -1) {
+ int cycle_diff = cycle - xmit->last_cycle;
+ int skip;
+
+ /* unwrap */
+ if (cycle_diff < 0) {
+ cycle_diff += 8000;
+ if (cycle_diff < 0)
+ PRINT(KERN_ERR, "bogus cycle diff %d\n",
+ cycle_diff);
+ }
+
+ skip = cycle_diff - 1;
+ if (skip > 0) {
+ DBGMSG("skipped %d cycles without packet loss", skip);
+ atomic_add(skip, &iso->skips);
+ }
+ }
+ xmit->last_cycle = cycle;
+
+ /* tell the subsystem the packet has gone out */
+ hpsb_iso_packet_sent(iso, cycle, event != 0x11);
+
+ /* reset the DMA descriptor for next time */
+ cmd->output_last.status = 0;
+ }
+
+ if (wake)
+ hpsb_iso_wake(iso);
+}
+
+static int ohci_iso_xmit_queue(struct hpsb_iso *iso, struct hpsb_iso_packet_info *info)
+{
+ struct ohci_iso_xmit *xmit = iso->hostdata;
+ struct ti_ohci *ohci = xmit->ohci;
+
+ int next_i, prev_i;
+ struct iso_xmit_cmd *next, *prev;
+
+ unsigned int offset;
+ unsigned short len;
+ unsigned char tag, sy;
+
+ /* check that the packet doesn't cross a page boundary
+ (we could allow this if we added OUTPUT_MORE descriptor support) */
+ if (cross_bound(info->offset, info->len)) {
+ PRINT(KERN_ERR,
+ "rawiso xmit: packet %u crosses a page boundary",
+ iso->first_packet);
+ return -EINVAL;
+ }
+
+ offset = info->offset;
+ len = info->len;
+ tag = info->tag;
+ sy = info->sy;
+
+ /* sync up the card's view of the buffer */
+ dma_region_sync_for_device(&iso->data_buf, offset, len);
+
+ /* append first_packet to the DMA chain */
+ /* by linking the previous descriptor to it */
+ /* (next will become the new end of the DMA chain) */
+
+ next_i = iso->first_packet;
+ prev_i = (next_i == 0) ? (iso->buf_packets - 1) : (next_i - 1);
+
+ next = dma_region_i(&xmit->prog, struct iso_xmit_cmd, next_i);
+ prev = dma_region_i(&xmit->prog, struct iso_xmit_cmd, prev_i);
+
+ /* set up the OUTPUT_MORE_IMMEDIATE descriptor */
+ memset(next, 0, sizeof(struct iso_xmit_cmd));
+ next->output_more_immediate.control = cpu_to_le32(0x02000008);
+
+ /* ISO packet header is embedded in the OUTPUT_MORE_IMMEDIATE */
+
+ /* tcode = 0xA, and sy */
+ next->iso_hdr[0] = 0xA0 | (sy & 0xF);
+
+ /* tag and channel number */
+ next->iso_hdr[1] = (tag << 6) | (iso->channel & 0x3F);
+
+ /* transmission speed */
+ next->iso_hdr[2] = iso->speed & 0x7;
+
+ /* payload size */
+ next->iso_hdr[6] = len & 0xFF;
+ next->iso_hdr[7] = len >> 8;
+
+ /* set up the OUTPUT_LAST */
+ next->output_last.control = cpu_to_le32(1 << 28);
+ next->output_last.control |= cpu_to_le32(1 << 27); /* update timeStamp */
+ next->output_last.control |= cpu_to_le32(3 << 20); /* want interrupt */
+ next->output_last.control |= cpu_to_le32(3 << 18); /* enable branch */
+ next->output_last.control |= cpu_to_le32(len);
+
+ /* payload bus address */
+ next->output_last.address = cpu_to_le32(dma_region_offset_to_bus(&iso->data_buf, offset));
+
+ /* leave branchAddress at zero for now */
+
+ /* re-write the previous DMA descriptor to chain to this one */
+
+ /* set prev branch address to point to next (Z=3) */
+ prev->output_last.branchAddress = cpu_to_le32(
+ dma_prog_region_offset_to_bus(&xmit->prog, sizeof(struct iso_xmit_cmd) * next_i) | 3);
+
+ /*
+ * Link the skip address to this descriptor itself. This causes a
+ * context to skip a cycle whenever lost cycles or FIFO overruns occur,
+ * without dropping the data at that point the application should then
+ * decide whether this is an error condition or not. Some protocols
+ * can deal with this by dropping some rate-matching padding packets.
+ */
+ next->output_more_immediate.branchAddress =
+ prev->output_last.branchAddress;
+
+ /* disable interrupt, unless required by the IRQ interval */
+ if (prev_i % iso->irq_interval) {
+ prev->output_last.control &= cpu_to_le32(~(3 << 20)); /* no interrupt */
+ } else {
+ prev->output_last.control |= cpu_to_le32(3 << 20); /* enable interrupt */
+ }
+
+ wmb();
+
+ /* wake DMA in case it is sleeping */
+ reg_write(xmit->ohci, xmit->ContextControlSet, 1 << 12);
+
+ /* issue a dummy read of the cycle timer to force all PCI
+ writes to be posted immediately */
+ mb();
+ reg_read(xmit->ohci, OHCI1394_IsochronousCycleTimer);
+
+ return 0;
+}
+
+static int ohci_iso_xmit_start(struct hpsb_iso *iso, int cycle)
+{
+ struct ohci_iso_xmit *xmit = iso->hostdata;
+ struct ti_ohci *ohci = xmit->ohci;
+
+ /* clear out the control register */
+ reg_write(xmit->ohci, xmit->ContextControlClear, 0xFFFFFFFF);
+ wmb();
+
+ /* address and length of first descriptor block (Z=3) */
+ reg_write(xmit->ohci, xmit->CommandPtr,
+ dma_prog_region_offset_to_bus(&xmit->prog, iso->pkt_dma * sizeof(struct iso_xmit_cmd)) | 3);
+
+ /* cycle match */
+ if (cycle != -1) {
+ u32 start = cycle & 0x1FFF;
+
+ /* 'cycle' is only mod 8000, but we also need two 'seconds' bits -
+ just snarf them from the current time */
+ u32 seconds = reg_read(xmit->ohci, OHCI1394_IsochronousCycleTimer) >> 25;
+
+ /* advance one second to give some extra time for DMA to start */
+ seconds += 1;
+
+ start |= (seconds & 3) << 13;
+
+ reg_write(xmit->ohci, xmit->ContextControlSet, 0x80000000 | (start << 16));
+ }
+
+ /* enable interrupts */
+ reg_write(xmit->ohci, OHCI1394_IsoXmitIntMaskSet, 1 << xmit->task.context);
+
+ /* run */
+ reg_write(xmit->ohci, xmit->ContextControlSet, 0x8000);
+ mb();
+
+ /* wait 100 usec to give the card time to go active */
+ udelay(100);
+
+ /* check the RUN bit */
+ if (!(reg_read(xmit->ohci, xmit->ContextControlSet) & 0x8000)) {
+ PRINT(KERN_ERR, "Error starting IT DMA (ContextControl 0x%08x)\n",
+ reg_read(xmit->ohci, xmit->ContextControlSet));
+ return -1;
+ }
+
+ return 0;
+}
+
+static int ohci_isoctl(struct hpsb_iso *iso, enum isoctl_cmd cmd, unsigned long arg)
+{
+
+ switch(cmd) {
+ case XMIT_INIT:
+ return ohci_iso_xmit_init(iso);
+ case XMIT_START:
+ return ohci_iso_xmit_start(iso, arg);
+ case XMIT_STOP:
+ ohci_iso_xmit_stop(iso);
+ return 0;
+ case XMIT_QUEUE:
+ return ohci_iso_xmit_queue(iso, (struct hpsb_iso_packet_info*) arg);
+ case XMIT_SHUTDOWN:
+ ohci_iso_xmit_shutdown(iso);
+ return 0;
+
+ case RECV_INIT:
+ return ohci_iso_recv_init(iso);
+ case RECV_START: {
+ int *args = (int*) arg;
+ return ohci_iso_recv_start(iso, args[0], args[1], args[2]);
+ }
+ case RECV_STOP:
+ ohci_iso_recv_stop(iso);
+ return 0;
+ case RECV_RELEASE:
+ ohci_iso_recv_release(iso, (struct hpsb_iso_packet_info*) arg);
+ return 0;
+ case RECV_FLUSH:
+ ohci_iso_recv_task((unsigned long) iso);
+ return 0;
+ case RECV_SHUTDOWN:
+ ohci_iso_recv_shutdown(iso);
+ return 0;
+ case RECV_LISTEN_CHANNEL:
+ ohci_iso_recv_change_channel(iso, arg, 1);
+ return 0;
+ case RECV_UNLISTEN_CHANNEL:
+ ohci_iso_recv_change_channel(iso, arg, 0);
+ return 0;
+ case RECV_SET_CHANNEL_MASK:
+ ohci_iso_recv_set_channel_mask(iso, *((u64*) arg));
+ return 0;
+
+ default:
+ PRINT_G(KERN_ERR, "ohci_isoctl cmd %d not implemented yet",
+ cmd);
+ break;
+ }
+ return -EINVAL;
+}
+
+/***************************************
+ * IEEE-1394 functionality section END *
+ ***************************************/
+
+
+/********************************************************
+ * Global stuff (interrupt handler, init/shutdown code) *
+ ********************************************************/
+
+static void dma_trm_reset(struct dma_trm_ctx *d)
+{
+ unsigned long flags;
+ LIST_HEAD(packet_list);
+ struct ti_ohci *ohci = d->ohci;
+ struct hpsb_packet *packet, *ptmp;
+
+ ohci1394_stop_context(ohci, d->ctrlClear, NULL);
+
+ /* Lock the context, reset it and release it. Move the packets
+ * that were pending in the context to packet_list and free
+ * them after releasing the lock. */
+
+ spin_lock_irqsave(&d->lock, flags);
+
+ list_splice_init(&d->fifo_list, &packet_list);
+ list_splice_init(&d->pending_list, &packet_list);
+
+ d->branchAddrPtr = NULL;
+ d->sent_ind = d->prg_ind;
+ d->free_prgs = d->num_desc;
+
+ spin_unlock_irqrestore(&d->lock, flags);
+
+ if (list_empty(&packet_list))
+ return;
+
+ PRINT(KERN_INFO, "AT dma reset ctx=%d, aborting transmission", d->ctx);
+
+ /* Now process subsystem callbacks for the packets from this
+ * context. */
+ list_for_each_entry_safe(packet, ptmp, &packet_list, driver_list) {
+ list_del_init(&packet->driver_list);
+ hpsb_packet_sent(ohci->host, packet, ACKX_ABORTED);
+ }
+}
+
+static void ohci_schedule_iso_tasklets(struct ti_ohci *ohci,
+ quadlet_t rx_event,
+ quadlet_t tx_event)
+{
+ struct ohci1394_iso_tasklet *t;
+ unsigned long mask;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ohci->iso_tasklet_list_lock, flags);
+
+ list_for_each_entry(t, &ohci->iso_tasklet_list, link) {
+ mask = 1 << t->context;
+
+ if (t->type == OHCI_ISO_TRANSMIT) {
+ if (tx_event & mask)
+ tasklet_schedule(&t->tasklet);
+ } else {
+ /* OHCI_ISO_RECEIVE or OHCI_ISO_MULTICHANNEL_RECEIVE */
+ if (rx_event & mask)
+ tasklet_schedule(&t->tasklet);
+ }
+ }
+
+ spin_unlock_irqrestore(&ohci->iso_tasklet_list_lock, flags);
+}
+
+static irqreturn_t ohci_irq_handler(int irq, void *dev_id)
+{
+ quadlet_t event, node_id;
+ struct ti_ohci *ohci = (struct ti_ohci *)dev_id;
+ struct hpsb_host *host = ohci->host;
+ int phyid = -1, isroot = 0;
+ unsigned long flags;
+
+ /* Read and clear the interrupt event register. Don't clear
+ * the busReset event, though. This is done when we get the
+ * selfIDComplete interrupt. */
+ spin_lock_irqsave(&ohci->event_lock, flags);
+ event = reg_read(ohci, OHCI1394_IntEventClear);
+ reg_write(ohci, OHCI1394_IntEventClear, event & ~OHCI1394_busReset);
+ spin_unlock_irqrestore(&ohci->event_lock, flags);
+
+ if (!event)
+ return IRQ_NONE;
+
+ /* If event is ~(u32)0 cardbus card was ejected. In this case
+ * we just return, and clean up in the ohci1394_pci_remove
+ * function. */
+ if (event == ~(u32) 0) {
+ DBGMSG("Device removed.");
+ return IRQ_NONE;
+ }
+
+ DBGMSG("IntEvent: %08x", event);
+
+ if (event & OHCI1394_unrecoverableError) {
+ int ctx;
+ PRINT(KERN_ERR, "Unrecoverable error!");
+
+ if (reg_read(ohci, OHCI1394_AsReqTrContextControlSet) & 0x800)
+ PRINT(KERN_ERR, "Async Req Tx Context died: "
+ "ctrl[%08x] cmdptr[%08x]",
+ reg_read(ohci, OHCI1394_AsReqTrContextControlSet),
+ reg_read(ohci, OHCI1394_AsReqTrCommandPtr));
+
+ if (reg_read(ohci, OHCI1394_AsRspTrContextControlSet) & 0x800)
+ PRINT(KERN_ERR, "Async Rsp Tx Context died: "
+ "ctrl[%08x] cmdptr[%08x]",
+ reg_read(ohci, OHCI1394_AsRspTrContextControlSet),
+ reg_read(ohci, OHCI1394_AsRspTrCommandPtr));
+
+ if (reg_read(ohci, OHCI1394_AsReqRcvContextControlSet) & 0x800)
+ PRINT(KERN_ERR, "Async Req Rcv Context died: "
+ "ctrl[%08x] cmdptr[%08x]",
+ reg_read(ohci, OHCI1394_AsReqRcvContextControlSet),
+ reg_read(ohci, OHCI1394_AsReqRcvCommandPtr));
+
+ if (reg_read(ohci, OHCI1394_AsRspRcvContextControlSet) & 0x800)
+ PRINT(KERN_ERR, "Async Rsp Rcv Context died: "
+ "ctrl[%08x] cmdptr[%08x]",
+ reg_read(ohci, OHCI1394_AsRspRcvContextControlSet),
+ reg_read(ohci, OHCI1394_AsRspRcvCommandPtr));
+
+ for (ctx = 0; ctx < ohci->nb_iso_xmit_ctx; ctx++) {
+ if (reg_read(ohci, OHCI1394_IsoXmitContextControlSet + (16 * ctx)) & 0x800)
+ PRINT(KERN_ERR, "Iso Xmit %d Context died: "
+ "ctrl[%08x] cmdptr[%08x]", ctx,
+ reg_read(ohci, OHCI1394_IsoXmitContextControlSet + (16 * ctx)),
+ reg_read(ohci, OHCI1394_IsoXmitCommandPtr + (16 * ctx)));
+ }
+
+ for (ctx = 0; ctx < ohci->nb_iso_rcv_ctx; ctx++) {
+ if (reg_read(ohci, OHCI1394_IsoRcvContextControlSet + (32 * ctx)) & 0x800)
+ PRINT(KERN_ERR, "Iso Recv %d Context died: "
+ "ctrl[%08x] cmdptr[%08x] match[%08x]", ctx,
+ reg_read(ohci, OHCI1394_IsoRcvContextControlSet + (32 * ctx)),
+ reg_read(ohci, OHCI1394_IsoRcvCommandPtr + (32 * ctx)),
+ reg_read(ohci, OHCI1394_IsoRcvContextMatch + (32 * ctx)));
+ }
+
+ event &= ~OHCI1394_unrecoverableError;
+ }
+ if (event & OHCI1394_postedWriteErr) {
+ PRINT(KERN_ERR, "physical posted write error");
+ /* no recovery strategy yet, had to involve protocol drivers */
+ event &= ~OHCI1394_postedWriteErr;
+ }
+ if (event & OHCI1394_cycleTooLong) {
+ if(printk_ratelimit())
+ PRINT(KERN_WARNING, "isochronous cycle too long");
+ else
+ DBGMSG("OHCI1394_cycleTooLong");
+ reg_write(ohci, OHCI1394_LinkControlSet,
+ OHCI1394_LinkControl_CycleMaster);
+ event &= ~OHCI1394_cycleTooLong;
+ }
+ if (event & OHCI1394_cycleInconsistent) {
+ /* We subscribe to the cycleInconsistent event only to
+ * clear the corresponding event bit... otherwise,
+ * isochronous cycleMatch DMA won't work. */
+ DBGMSG("OHCI1394_cycleInconsistent");
+ event &= ~OHCI1394_cycleInconsistent;
+ }
+ if (event & OHCI1394_busReset) {
+ /* The busReset event bit can't be cleared during the
+ * selfID phase, so we disable busReset interrupts, to
+ * avoid burying the cpu in interrupt requests. */
+ spin_lock_irqsave(&ohci->event_lock, flags);
+ reg_write(ohci, OHCI1394_IntMaskClear, OHCI1394_busReset);
+
+ if (ohci->check_busreset) {
+ int loop_count = 0;
+
+ udelay(10);
+
+ while (reg_read(ohci, OHCI1394_IntEventSet) & OHCI1394_busReset) {
+ reg_write(ohci, OHCI1394_IntEventClear, OHCI1394_busReset);
+
+ spin_unlock_irqrestore(&ohci->event_lock, flags);
+ udelay(10);
+ spin_lock_irqsave(&ohci->event_lock, flags);
+
+ /* The loop counter check is to prevent the driver
+ * from remaining in this state forever. For the
+ * initial bus reset, the loop continues for ever
+ * and the system hangs, until some device is plugged-in
+ * or out manually into a port! The forced reset seems
+ * to solve this problem. This mainly effects nForce2. */
+ if (loop_count > 10000) {
+ ohci_devctl(host, RESET_BUS, LONG_RESET);
+ DBGMSG("Detected bus-reset loop. Forced a bus reset!");
+ loop_count = 0;
+ }
+
+ loop_count++;
+ }
+ }
+ spin_unlock_irqrestore(&ohci->event_lock, flags);
+ if (!host->in_bus_reset) {
+ DBGMSG("irq_handler: Bus reset requested");
+
+ /* Subsystem call */
+ hpsb_bus_reset(ohci->host);
+ }
+ event &= ~OHCI1394_busReset;
+ }
+ if (event & OHCI1394_reqTxComplete) {
+ struct dma_trm_ctx *d = &ohci->at_req_context;
+ DBGMSG("Got reqTxComplete interrupt "
+ "status=0x%08X", reg_read(ohci, d->ctrlSet));
+ if (reg_read(ohci, d->ctrlSet) & 0x800)
+ ohci1394_stop_context(ohci, d->ctrlClear,
+ "reqTxComplete");
+ else
+ dma_trm_tasklet((unsigned long)d);
+ //tasklet_schedule(&d->task);
+ event &= ~OHCI1394_reqTxComplete;
+ }
+ if (event & OHCI1394_respTxComplete) {
+ struct dma_trm_ctx *d = &ohci->at_resp_context;
+ DBGMSG("Got respTxComplete interrupt "
+ "status=0x%08X", reg_read(ohci, d->ctrlSet));
+ if (reg_read(ohci, d->ctrlSet) & 0x800)
+ ohci1394_stop_context(ohci, d->ctrlClear,
+ "respTxComplete");
+ else
+ tasklet_schedule(&d->task);
+ event &= ~OHCI1394_respTxComplete;
+ }
+ if (event & OHCI1394_RQPkt) {
+ struct dma_rcv_ctx *d = &ohci->ar_req_context;
+ DBGMSG("Got RQPkt interrupt status=0x%08X",
+ reg_read(ohci, d->ctrlSet));
+ if (reg_read(ohci, d->ctrlSet) & 0x800)
+ ohci1394_stop_context(ohci, d->ctrlClear, "RQPkt");
+ else
+ tasklet_schedule(&d->task);
+ event &= ~OHCI1394_RQPkt;
+ }
+ if (event & OHCI1394_RSPkt) {
+ struct dma_rcv_ctx *d = &ohci->ar_resp_context;
+ DBGMSG("Got RSPkt interrupt status=0x%08X",
+ reg_read(ohci, d->ctrlSet));
+ if (reg_read(ohci, d->ctrlSet) & 0x800)
+ ohci1394_stop_context(ohci, d->ctrlClear, "RSPkt");
+ else
+ tasklet_schedule(&d->task);
+ event &= ~OHCI1394_RSPkt;
+ }
+ if (event & OHCI1394_isochRx) {
+ quadlet_t rx_event;
+
+ rx_event = reg_read(ohci, OHCI1394_IsoRecvIntEventSet);
+ reg_write(ohci, OHCI1394_IsoRecvIntEventClear, rx_event);
+ ohci_schedule_iso_tasklets(ohci, rx_event, 0);
+ event &= ~OHCI1394_isochRx;
+ }
+ if (event & OHCI1394_isochTx) {
+ quadlet_t tx_event;
+
+ tx_event = reg_read(ohci, OHCI1394_IsoXmitIntEventSet);
+ reg_write(ohci, OHCI1394_IsoXmitIntEventClear, tx_event);
+ ohci_schedule_iso_tasklets(ohci, 0, tx_event);
+ event &= ~OHCI1394_isochTx;
+ }
+ if (event & OHCI1394_selfIDComplete) {
+ if (host->in_bus_reset) {
+ node_id = reg_read(ohci, OHCI1394_NodeID);
+
+ if (!(node_id & 0x80000000)) {
+ PRINT(KERN_ERR,
+ "SelfID received, but NodeID invalid "
+ "(probably new bus reset occurred): %08X",
+ node_id);
+ goto selfid_not_valid;
+ }
+
+ phyid = node_id & 0x0000003f;
+ isroot = (node_id & 0x40000000) != 0;
+
+ DBGMSG("SelfID interrupt received "
+ "(phyid %d, %s)", phyid,
+ (isroot ? "root" : "not root"));
+
+ handle_selfid(ohci, host, phyid, isroot);
+
+ /* Clear the bus reset event and re-enable the
+ * busReset interrupt. */
+ spin_lock_irqsave(&ohci->event_lock, flags);
+ reg_write(ohci, OHCI1394_IntEventClear, OHCI1394_busReset);
+ reg_write(ohci, OHCI1394_IntMaskSet, OHCI1394_busReset);
+ spin_unlock_irqrestore(&ohci->event_lock, flags);
+
+ /* Turn on phys dma reception.
+ *
+ * TODO: Enable some sort of filtering management.
+ */
+ if (phys_dma) {
+ reg_write(ohci, OHCI1394_PhyReqFilterHiSet,
+ 0xffffffff);
+ reg_write(ohci, OHCI1394_PhyReqFilterLoSet,
+ 0xffffffff);
+ }
+
+ DBGMSG("PhyReqFilter=%08x%08x",
+ reg_read(ohci, OHCI1394_PhyReqFilterHiSet),
+ reg_read(ohci, OHCI1394_PhyReqFilterLoSet));
+
+ hpsb_selfid_complete(host, phyid, isroot);
+ } else
+ PRINT(KERN_ERR,
+ "SelfID received outside of bus reset sequence");
+
+selfid_not_valid:
+ event &= ~OHCI1394_selfIDComplete;
+ }
+
+ /* Make sure we handle everything, just in case we accidentally
+ * enabled an interrupt that we didn't write a handler for. */
+ if (event)
+ PRINT(KERN_ERR, "Unhandled interrupt(s) 0x%08x",
+ event);
+
+ return IRQ_HANDLED;
+}
+
+/* Put the buffer back into the dma context */
+static void insert_dma_buffer(struct dma_rcv_ctx *d, int idx)
+{
+ struct ti_ohci *ohci = (struct ti_ohci*)(d->ohci);
+ DBGMSG("Inserting dma buf ctx=%d idx=%d", d->ctx, idx);
+
+ d->prg_cpu[idx]->status = cpu_to_le32(d->buf_size);
+ d->prg_cpu[idx]->branchAddress &= le32_to_cpu(0xfffffff0);
+ idx = (idx + d->num_desc - 1 ) % d->num_desc;
+ d->prg_cpu[idx]->branchAddress |= le32_to_cpu(0x00000001);
+
+ /* To avoid a race, ensure 1394 interface hardware sees the inserted
+ * context program descriptors before it sees the wakeup bit set. */
+ wmb();
+
+ /* wake up the dma context if necessary */
+ if (!(reg_read(ohci, d->ctrlSet) & 0x400)) {
+ PRINT(KERN_INFO,
+ "Waking dma ctx=%d ... processing is probably too slow",
+ d->ctx);
+ }
+
+ /* do this always, to avoid race condition */
+ reg_write(ohci, d->ctrlSet, 0x1000);
+}
+
+#define cond_le32_to_cpu(data, noswap) \
+ (noswap ? data : le32_to_cpu(data))
+
+static const int TCODE_SIZE[16] = {20, 0, 16, -1, 16, 20, 20, 0,
+ -1, 0, -1, 0, -1, -1, 16, -1};
+
+/*
+ * Determine the length of a packet in the buffer
+ * Optimization suggested by Pascal Drolet <pascal.drolet@informission.ca>
+ */
+static inline int packet_length(struct dma_rcv_ctx *d, int idx,
+ quadlet_t *buf_ptr, int offset,
+ unsigned char tcode, int noswap)
+{
+ int length = -1;
+
+ if (d->type == DMA_CTX_ASYNC_REQ || d->type == DMA_CTX_ASYNC_RESP) {
+ length = TCODE_SIZE[tcode];
+ if (length == 0) {
+ if (offset + 12 >= d->buf_size) {
+ length = (cond_le32_to_cpu(d->buf_cpu[(idx + 1) % d->num_desc]
+ [3 - ((d->buf_size - offset) >> 2)], noswap) >> 16);
+ } else {
+ length = (cond_le32_to_cpu(buf_ptr[3], noswap) >> 16);
+ }
+ length += 20;
+ }
+ } else if (d->type == DMA_CTX_ISO) {
+ /* Assumption: buffer fill mode with header/trailer */
+ length = (cond_le32_to_cpu(buf_ptr[0], noswap) >> 16) + 8;
+ }
+
+ if (length > 0 && length % 4)
+ length += 4 - (length % 4);
+
+ return length;
+}
+
+/* Tasklet that processes dma receive buffers */
+static void dma_rcv_tasklet (unsigned long data)
+{
+ struct dma_rcv_ctx *d = (struct dma_rcv_ctx*)data;
+ struct ti_ohci *ohci = (struct ti_ohci*)(d->ohci);
+ unsigned int split_left, idx, offset, rescount;
+ unsigned char tcode;
+ int length, bytes_left, ack;
+ unsigned long flags;
+ quadlet_t *buf_ptr;
+ char *split_ptr;
+ char msg[256];
+
+ spin_lock_irqsave(&d->lock, flags);
+
+ idx = d->buf_ind;
+ offset = d->buf_offset;
+ buf_ptr = d->buf_cpu[idx] + offset/4;
+
+ rescount = le32_to_cpu(d->prg_cpu[idx]->status) & 0xffff;
+ bytes_left = d->buf_size - rescount - offset;
+
+ while (bytes_left > 0) {
+ tcode = (cond_le32_to_cpu(buf_ptr[0], ohci->no_swap_incoming) >> 4) & 0xf;
+
+ /* packet_length() will return < 4 for an error */
+ length = packet_length(d, idx, buf_ptr, offset, tcode, ohci->no_swap_incoming);
+
+ if (length < 4) { /* something is wrong */
+ sprintf(msg,"Unexpected tcode 0x%x(0x%08x) in AR ctx=%d, length=%d",
+ tcode, cond_le32_to_cpu(buf_ptr[0], ohci->no_swap_incoming),
+ d->ctx, length);
+ ohci1394_stop_context(ohci, d->ctrlClear, msg);
+ spin_unlock_irqrestore(&d->lock, flags);
+ return;
+ }
+
+ /* The first case is where we have a packet that crosses
+ * over more than one descriptor. The next case is where
+ * it's all in the first descriptor. */
+ if ((offset + length) > d->buf_size) {
+ DBGMSG("Split packet rcv'd");
+ if (length > d->split_buf_size) {
+ ohci1394_stop_context(ohci, d->ctrlClear,
+ "Split packet size exceeded");
+ d->buf_ind = idx;
+ d->buf_offset = offset;
+ spin_unlock_irqrestore(&d->lock, flags);
+ return;
+ }
+
+ if (le32_to_cpu(d->prg_cpu[(idx+1)%d->num_desc]->status)
+ == d->buf_size) {
+ /* Other part of packet not written yet.
+ * this should never happen I think
+ * anyway we'll get it on the next call. */
+ PRINT(KERN_INFO,
+ "Got only half a packet!");
+ d->buf_ind = idx;
+ d->buf_offset = offset;
+ spin_unlock_irqrestore(&d->lock, flags);
+ return;
+ }
+
+ split_left = length;
+ split_ptr = (char *)d->spb;
+ memcpy(split_ptr,buf_ptr,d->buf_size-offset);
+ split_left -= d->buf_size-offset;
+ split_ptr += d->buf_size-offset;
+ insert_dma_buffer(d, idx);
+ idx = (idx+1) % d->num_desc;
+ buf_ptr = d->buf_cpu[idx];
+ offset=0;
+
+ while (split_left >= d->buf_size) {
+ memcpy(split_ptr,buf_ptr,d->buf_size);
+ split_ptr += d->buf_size;
+ split_left -= d->buf_size;
+ insert_dma_buffer(d, idx);
+ idx = (idx+1) % d->num_desc;
+ buf_ptr = d->buf_cpu[idx];
+ }
+
+ if (split_left > 0) {
+ memcpy(split_ptr, buf_ptr, split_left);
+ offset = split_left;
+ buf_ptr += offset/4;
+ }
+ } else {
+ DBGMSG("Single packet rcv'd");
+ memcpy(d->spb, buf_ptr, length);
+ offset += length;
+ buf_ptr += length/4;
+ if (offset==d->buf_size) {
+ insert_dma_buffer(d, idx);
+ idx = (idx+1) % d->num_desc;
+ buf_ptr = d->buf_cpu[idx];
+ offset=0;
+ }
+ }
+
+ /* We get one phy packet to the async descriptor for each
+ * bus reset. We always ignore it. */
+ if (tcode != OHCI1394_TCODE_PHY) {
+ if (!ohci->no_swap_incoming)
+ header_le32_to_cpu(d->spb, tcode);
+ DBGMSG("Packet received from node"
+ " %d ack=0x%02X spd=%d tcode=0x%X"
+ " length=%d ctx=%d tlabel=%d",
+ (d->spb[1]>>16)&0x3f,
+ (cond_le32_to_cpu(d->spb[length/4-1], ohci->no_swap_incoming)>>16)&0x1f,
+ (cond_le32_to_cpu(d->spb[length/4-1], ohci->no_swap_incoming)>>21)&0x3,
+ tcode, length, d->ctx,
+ (d->spb[0]>>10)&0x3f);
+
+ ack = (((cond_le32_to_cpu(d->spb[length/4-1], ohci->no_swap_incoming)>>16)&0x1f)
+ == 0x11) ? 1 : 0;
+
+ hpsb_packet_received(ohci->host, d->spb,
+ length-4, ack);
+ }
+#ifdef OHCI1394_DEBUG
+ else
+ PRINT (KERN_DEBUG, "Got phy packet ctx=%d ... discarded",
+ d->ctx);
+#endif
+
+ rescount = le32_to_cpu(d->prg_cpu[idx]->status) & 0xffff;
+
+ bytes_left = d->buf_size - rescount - offset;
+
+ }
+
+ d->buf_ind = idx;
+ d->buf_offset = offset;
+
+ spin_unlock_irqrestore(&d->lock, flags);
+}
+
+/* Bottom half that processes sent packets */
+static void dma_trm_tasklet (unsigned long data)
+{
+ struct dma_trm_ctx *d = (struct dma_trm_ctx*)data;
+ struct ti_ohci *ohci = (struct ti_ohci*)(d->ohci);
+ struct hpsb_packet *packet, *ptmp;
+ unsigned long flags;
+ u32 status, ack;
+ size_t datasize;
+
+ spin_lock_irqsave(&d->lock, flags);
+
+ list_for_each_entry_safe(packet, ptmp, &d->fifo_list, driver_list) {
+ datasize = packet->data_size;
+ if (datasize && packet->type != hpsb_raw)
+ status = le32_to_cpu(
+ d->prg_cpu[d->sent_ind]->end.status) >> 16;
+ else
+ status = le32_to_cpu(
+ d->prg_cpu[d->sent_ind]->begin.status) >> 16;
+
+ if (status == 0)
+ /* this packet hasn't been sent yet*/
+ break;
+
+#ifdef OHCI1394_DEBUG
+ if (datasize)
+ if (((le32_to_cpu(d->prg_cpu[d->sent_ind]->data[0])>>4)&0xf) == 0xa)
+ DBGMSG("Stream packet sent to channel %d tcode=0x%X "
+ "ack=0x%X spd=%d dataLength=%d ctx=%d",
+ (le32_to_cpu(d->prg_cpu[d->sent_ind]->data[0])>>8)&0x3f,
+ (le32_to_cpu(d->prg_cpu[d->sent_ind]->data[0])>>4)&0xf,
+ status&0x1f, (status>>5)&0x3,
+ le32_to_cpu(d->prg_cpu[d->sent_ind]->data[1])>>16,
+ d->ctx);
+ else
+ DBGMSG("Packet sent to node %d tcode=0x%X tLabel="
+ "%d ack=0x%X spd=%d dataLength=%d ctx=%d",
+ (le32_to_cpu(d->prg_cpu[d->sent_ind]->data[1])>>16)&0x3f,
+ (le32_to_cpu(d->prg_cpu[d->sent_ind]->data[0])>>4)&0xf,
+ (le32_to_cpu(d->prg_cpu[d->sent_ind]->data[0])>>10)&0x3f,
+ status&0x1f, (status>>5)&0x3,
+ le32_to_cpu(d->prg_cpu[d->sent_ind]->data[3])>>16,
+ d->ctx);
+ else
+ DBGMSG("Packet sent to node %d tcode=0x%X tLabel="
+ "%d ack=0x%X spd=%d data=0x%08X ctx=%d",
+ (le32_to_cpu(d->prg_cpu[d->sent_ind]->data[1])
+ >>16)&0x3f,
+ (le32_to_cpu(d->prg_cpu[d->sent_ind]->data[0])
+ >>4)&0xf,
+ (le32_to_cpu(d->prg_cpu[d->sent_ind]->data[0])
+ >>10)&0x3f,
+ status&0x1f, (status>>5)&0x3,
+ le32_to_cpu(d->prg_cpu[d->sent_ind]->data[3]),
+ d->ctx);
+#endif
+
+ if (status & 0x10) {
+ ack = status & 0xf;
+ } else {
+ switch (status & 0x1f) {
+ case EVT_NO_STATUS: /* that should never happen */
+ case EVT_RESERVED_A: /* that should never happen */
+ case EVT_LONG_PACKET: /* that should never happen */
+ PRINT(KERN_WARNING, "Received OHCI evt_* error 0x%x", status & 0x1f);
+ ack = ACKX_SEND_ERROR;
+ break;
+ case EVT_MISSING_ACK:
+ ack = ACKX_TIMEOUT;
+ break;
+ case EVT_UNDERRUN:
+ ack = ACKX_SEND_ERROR;
+ break;
+ case EVT_OVERRUN: /* that should never happen */
+ PRINT(KERN_WARNING, "Received OHCI evt_* error 0x%x", status & 0x1f);
+ ack = ACKX_SEND_ERROR;
+ break;
+ case EVT_DESCRIPTOR_READ:
+ case EVT_DATA_READ:
+ case EVT_DATA_WRITE:
+ ack = ACKX_SEND_ERROR;
+ break;
+ case EVT_BUS_RESET: /* that should never happen */
+ PRINT(KERN_WARNING, "Received OHCI evt_* error 0x%x", status & 0x1f);
+ ack = ACKX_SEND_ERROR;
+ break;
+ case EVT_TIMEOUT:
+ ack = ACKX_TIMEOUT;
+ break;
+ case EVT_TCODE_ERR:
+ ack = ACKX_SEND_ERROR;
+ break;
+ case EVT_RESERVED_B: /* that should never happen */
+ case EVT_RESERVED_C: /* that should never happen */
+ PRINT(KERN_WARNING, "Received OHCI evt_* error 0x%x", status & 0x1f);
+ ack = ACKX_SEND_ERROR;
+ break;
+ case EVT_UNKNOWN:
+ case EVT_FLUSHED:
+ ack = ACKX_SEND_ERROR;
+ break;
+ default:
+ PRINT(KERN_ERR, "Unhandled OHCI evt_* error 0x%x", status & 0x1f);
+ ack = ACKX_SEND_ERROR;
+ BUG();
+ }
+ }
+
+ list_del_init(&packet->driver_list);
+ hpsb_packet_sent(ohci->host, packet, ack);
+
+ if (datasize)
+ pci_unmap_single(ohci->dev,
+ cpu_to_le32(d->prg_cpu[d->sent_ind]->end.address),
+ datasize, PCI_DMA_TODEVICE);
+
+ d->sent_ind = (d->sent_ind+1)%d->num_desc;
+ d->free_prgs++;
+ }
+
+ dma_trm_flush(ohci, d);
+
+ spin_unlock_irqrestore(&d->lock, flags);
+}
+
+static void free_dma_rcv_ctx(struct dma_rcv_ctx *d)
+{
+ int i;
+ struct ti_ohci *ohci = d->ohci;
+
+ if (ohci == NULL)
+ return;
+
+ DBGMSG("Freeing dma_rcv_ctx %d", d->ctx);
+
+ if (d->buf_cpu) {
+ for (i=0; i<d->num_desc; i++)
+ if (d->buf_cpu[i] && d->buf_bus[i])
+ pci_free_consistent(
+ ohci->dev, d->buf_size,
+ d->buf_cpu[i], d->buf_bus[i]);
+ kfree(d->buf_cpu);
+ kfree(d->buf_bus);
+ }
+ if (d->prg_cpu) {
+ for (i=0; i<d->num_desc; i++)
+ if (d->prg_cpu[i] && d->prg_bus[i])
+ pci_pool_free(d->prg_pool, d->prg_cpu[i],
+ d->prg_bus[i]);
+ pci_pool_destroy(d->prg_pool);
+ kfree(d->prg_cpu);
+ kfree(d->prg_bus);
+ }
+ kfree(d->spb);
+
+ /* Mark this context as freed. */
+ d->ohci = NULL;
+}
+
+static int
+alloc_dma_rcv_ctx(struct ti_ohci *ohci, struct dma_rcv_ctx *d,
+ enum context_type type, int ctx, int num_desc,
+ int buf_size, int split_buf_size, int context_base)
+{
+ int i, len;
+ static int num_allocs;
+ static char pool_name[20];
+
+ d->ohci = ohci;
+ d->type = type;
+ d->ctx = ctx;
+
+ d->num_desc = num_desc;
+ d->buf_size = buf_size;
+ d->split_buf_size = split_buf_size;
+
+ d->ctrlSet = 0;
+ d->ctrlClear = 0;
+ d->cmdPtr = 0;
+
+ d->buf_cpu = kzalloc(d->num_desc * sizeof(*d->buf_cpu), GFP_ATOMIC);
+ d->buf_bus = kzalloc(d->num_desc * sizeof(*d->buf_bus), GFP_ATOMIC);
+
+ if (d->buf_cpu == NULL || d->buf_bus == NULL) {
+ PRINT(KERN_ERR, "Failed to allocate %s", "DMA buffer");
+ free_dma_rcv_ctx(d);
+ return -ENOMEM;
+ }
+
+ d->prg_cpu = kzalloc(d->num_desc * sizeof(*d->prg_cpu), GFP_ATOMIC);
+ d->prg_bus = kzalloc(d->num_desc * sizeof(*d->prg_bus), GFP_ATOMIC);
+
+ if (d->prg_cpu == NULL || d->prg_bus == NULL) {
+ PRINT(KERN_ERR, "Failed to allocate %s", "DMA prg");
+ free_dma_rcv_ctx(d);
+ return -ENOMEM;
+ }
+
+ d->spb = kmalloc(d->split_buf_size, GFP_ATOMIC);
+
+ if (d->spb == NULL) {
+ PRINT(KERN_ERR, "Failed to allocate %s", "split buffer");
+ free_dma_rcv_ctx(d);
+ return -ENOMEM;
+ }
+
+ len = sprintf(pool_name, "ohci1394_rcv_prg");
+ sprintf(pool_name+len, "%d", num_allocs);
+ d->prg_pool = pci_pool_create(pool_name, ohci->dev,
+ sizeof(struct dma_cmd), 4, 0);
+ if(d->prg_pool == NULL)
+ {
+ PRINT(KERN_ERR, "pci_pool_create failed for %s", pool_name);
+ free_dma_rcv_ctx(d);
+ return -ENOMEM;
+ }
+ num_allocs++;
+
+ for (i=0; i<d->num_desc; i++) {
+ d->buf_cpu[i] = pci_alloc_consistent(ohci->dev,
+ d->buf_size,
+ d->buf_bus+i);
+
+ if (d->buf_cpu[i] != NULL) {
+ memset(d->buf_cpu[i], 0, d->buf_size);
+ } else {
+ PRINT(KERN_ERR,
+ "Failed to allocate %s", "DMA buffer");
+ free_dma_rcv_ctx(d);
+ return -ENOMEM;
+ }
+
+ d->prg_cpu[i] = pci_pool_alloc(d->prg_pool, GFP_KERNEL, d->prg_bus+i);
+
+ if (d->prg_cpu[i] != NULL) {
+ memset(d->prg_cpu[i], 0, sizeof(struct dma_cmd));
+ } else {
+ PRINT(KERN_ERR,
+ "Failed to allocate %s", "DMA prg");
+ free_dma_rcv_ctx(d);
+ return -ENOMEM;
+ }
+ }
+
+ spin_lock_init(&d->lock);
+
+ d->ctrlSet = context_base + OHCI1394_ContextControlSet;
+ d->ctrlClear = context_base + OHCI1394_ContextControlClear;
+ d->cmdPtr = context_base + OHCI1394_ContextCommandPtr;
+
+ tasklet_init(&d->task, dma_rcv_tasklet, (unsigned long) d);
+ return 0;
+}
+
+static void free_dma_trm_ctx(struct dma_trm_ctx *d)
+{
+ int i;
+ struct ti_ohci *ohci = d->ohci;
+
+ if (ohci == NULL)
+ return;
+
+ DBGMSG("Freeing dma_trm_ctx %d", d->ctx);
+
+ if (d->prg_cpu) {
+ for (i=0; i<d->num_desc; i++)
+ if (d->prg_cpu[i] && d->prg_bus[i])
+ pci_pool_free(d->prg_pool, d->prg_cpu[i],
+ d->prg_bus[i]);
+ pci_pool_destroy(d->prg_pool);
+ kfree(d->prg_cpu);
+ kfree(d->prg_bus);
+ }
+
+ /* Mark this context as freed. */
+ d->ohci = NULL;
+}
+
+static int
+alloc_dma_trm_ctx(struct ti_ohci *ohci, struct dma_trm_ctx *d,
+ enum context_type type, int ctx, int num_desc,
+ int context_base)
+{
+ int i, len;
+ static char pool_name[20];
+ static int num_allocs=0;
+
+ d->ohci = ohci;
+ d->type = type;
+ d->ctx = ctx;
+ d->num_desc = num_desc;
+ d->ctrlSet = 0;
+ d->ctrlClear = 0;
+ d->cmdPtr = 0;
+
+ d->prg_cpu = kzalloc(d->num_desc * sizeof(*d->prg_cpu), GFP_KERNEL);
+ d->prg_bus = kzalloc(d->num_desc * sizeof(*d->prg_bus), GFP_KERNEL);
+
+ if (d->prg_cpu == NULL || d->prg_bus == NULL) {
+ PRINT(KERN_ERR, "Failed to allocate %s", "AT DMA prg");
+ free_dma_trm_ctx(d);
+ return -ENOMEM;
+ }
+
+ len = sprintf(pool_name, "ohci1394_trm_prg");
+ sprintf(pool_name+len, "%d", num_allocs);
+ d->prg_pool = pci_pool_create(pool_name, ohci->dev,
+ sizeof(struct at_dma_prg), 4, 0);
+ if (d->prg_pool == NULL) {
+ PRINT(KERN_ERR, "pci_pool_create failed for %s", pool_name);
+ free_dma_trm_ctx(d);
+ return -ENOMEM;
+ }
+ num_allocs++;
+
+ for (i = 0; i < d->num_desc; i++) {
+ d->prg_cpu[i] = pci_pool_alloc(d->prg_pool, GFP_KERNEL, d->prg_bus+i);
+
+ if (d->prg_cpu[i] != NULL) {
+ memset(d->prg_cpu[i], 0, sizeof(struct at_dma_prg));
+ } else {
+ PRINT(KERN_ERR,
+ "Failed to allocate %s", "AT DMA prg");
+ free_dma_trm_ctx(d);
+ return -ENOMEM;
+ }
+ }
+
+ spin_lock_init(&d->lock);
+
+ /* initialize tasklet */
+ d->ctrlSet = context_base + OHCI1394_ContextControlSet;
+ d->ctrlClear = context_base + OHCI1394_ContextControlClear;
+ d->cmdPtr = context_base + OHCI1394_ContextCommandPtr;
+ tasklet_init(&d->task, dma_trm_tasklet, (unsigned long)d);
+ return 0;
+}
+
+static void ohci_set_hw_config_rom(struct hpsb_host *host, quadlet_t *config_rom)
+{
+ struct ti_ohci *ohci = host->hostdata;
+
+ reg_write(ohci, OHCI1394_ConfigROMhdr, be32_to_cpu(config_rom[0]));
+ reg_write(ohci, OHCI1394_BusOptions, be32_to_cpu(config_rom[2]));
+
+ memcpy(ohci->csr_config_rom_cpu, config_rom, OHCI_CONFIG_ROM_LEN);
+}
+
+
+static quadlet_t ohci_hw_csr_reg(struct hpsb_host *host, int reg,
+ quadlet_t data, quadlet_t compare)
+{
+ struct ti_ohci *ohci = host->hostdata;
+ int i;
+
+ reg_write(ohci, OHCI1394_CSRData, data);
+ reg_write(ohci, OHCI1394_CSRCompareData, compare);
+ reg_write(ohci, OHCI1394_CSRControl, reg & 0x3);
+
+ for (i = 0; i < OHCI_LOOP_COUNT; i++) {
+ if (reg_read(ohci, OHCI1394_CSRControl) & 0x80000000)
+ break;
+
+ mdelay(1);
+ }
+
+ return reg_read(ohci, OHCI1394_CSRData);
+}
+
+static struct hpsb_host_driver ohci1394_driver = {
+ .owner = THIS_MODULE,
+ .name = OHCI1394_DRIVER_NAME,
+ .set_hw_config_rom = ohci_set_hw_config_rom,
+ .transmit_packet = ohci_transmit,
+ .devctl = ohci_devctl,
+ .isoctl = ohci_isoctl,
+ .hw_csr_reg = ohci_hw_csr_reg,
+};
+
+/***********************************
+ * PCI Driver Interface functions *
+ ***********************************/
+
+#ifdef CONFIG_PPC_PMAC
+static void ohci1394_pmac_on(struct pci_dev *dev)
+{
+ if (machine_is(powermac)) {
+ struct device_node *ofn = pci_device_to_OF_node(dev);
+
+ if (ofn) {
+ pmac_call_feature(PMAC_FTR_1394_CABLE_POWER, ofn, 0, 1);
+ pmac_call_feature(PMAC_FTR_1394_ENABLE, ofn, 0, 1);
+ }
+ }
+}
+
+static void ohci1394_pmac_off(struct pci_dev *dev)
+{
+ if (machine_is(powermac)) {
+ struct device_node *ofn = pci_device_to_OF_node(dev);
+
+ if (ofn) {
+ pmac_call_feature(PMAC_FTR_1394_ENABLE, ofn, 0, 0);
+ pmac_call_feature(PMAC_FTR_1394_CABLE_POWER, ofn, 0, 0);
+ }
+ }
+}
+#else
+#define ohci1394_pmac_on(dev)
+#define ohci1394_pmac_off(dev)
+#endif /* CONFIG_PPC_PMAC */
+
+static int __devinit ohci1394_pci_probe(struct pci_dev *dev,
+ const struct pci_device_id *ent)
+{
+ struct hpsb_host *host;
+ struct ti_ohci *ohci; /* shortcut to currently handled device */
+ resource_size_t ohci_base;
+ int err = -ENOMEM;
+
+ ohci1394_pmac_on(dev);
+ if (pci_enable_device(dev)) {
+ PRINT_G(KERN_ERR, "Failed to enable OHCI hardware");
+ err = -ENXIO;
+ goto err;
+ }
+ pci_set_master(dev);
+
+ host = hpsb_alloc_host(&ohci1394_driver, sizeof(struct ti_ohci), &dev->dev);
+ if (!host) {
+ PRINT_G(KERN_ERR, "Failed to allocate %s", "host structure");
+ goto err;
+ }
+ ohci = host->hostdata;
+ ohci->dev = dev;
+ ohci->host = host;
+ ohci->init_state = OHCI_INIT_ALLOC_HOST;
+ host->pdev = dev;
+ pci_set_drvdata(dev, ohci);
+
+ /* We don't want hardware swapping */
+ pci_write_config_dword(dev, OHCI1394_PCI_HCI_Control, 0);
+
+ /* Some oddball Apple controllers do not order the selfid
+ * properly, so we make up for it here. */
+#ifndef __LITTLE_ENDIAN
+ /* XXX: Need a better way to check this. I'm wondering if we can
+ * read the values of the OHCI1394_PCI_HCI_Control and the
+ * noByteSwapData registers to see if they were not cleared to
+ * zero. Should this work? Obviously it's not defined what these
+ * registers will read when they aren't supported. Bleh! */
+ if (dev->vendor == PCI_VENDOR_ID_APPLE &&
+ dev->device == PCI_DEVICE_ID_APPLE_UNI_N_FW) {
+ ohci->no_swap_incoming = 1;
+ ohci->selfid_swap = 0;
+ } else
+ ohci->selfid_swap = 1;
+#endif
+
+
+#ifndef PCI_DEVICE_ID_NVIDIA_NFORCE2_FW
+#define PCI_DEVICE_ID_NVIDIA_NFORCE2_FW 0x006e
+#endif
+
+ /* These chipsets require a bit of extra care when checking after
+ * a busreset. */
+ if ((dev->vendor == PCI_VENDOR_ID_APPLE &&
+ dev->device == PCI_DEVICE_ID_APPLE_UNI_N_FW) ||
+ (dev->vendor == PCI_VENDOR_ID_NVIDIA &&
+ dev->device == PCI_DEVICE_ID_NVIDIA_NFORCE2_FW))
+ ohci->check_busreset = 1;
+
+ /* We hardwire the MMIO length, since some CardBus adaptors
+ * fail to report the right length. Anyway, the ohci spec
+ * clearly says it's 2kb, so this shouldn't be a problem. */
+ ohci_base = pci_resource_start(dev, 0);
+ if (pci_resource_len(dev, 0) < OHCI1394_REGISTER_SIZE)
+ PRINT(KERN_WARNING, "PCI resource length of 0x%llx too small!",
+ (unsigned long long)pci_resource_len(dev, 0));
+
+ if (!request_mem_region(ohci_base, OHCI1394_REGISTER_SIZE,
+ OHCI1394_DRIVER_NAME)) {
+ PRINT_G(KERN_ERR, "MMIO resource (0x%llx - 0x%llx) unavailable",
+ (unsigned long long)ohci_base,
+ (unsigned long long)ohci_base + OHCI1394_REGISTER_SIZE);
+ goto err;
+ }
+ ohci->init_state = OHCI_INIT_HAVE_MEM_REGION;
+
+ ohci->registers = ioremap(ohci_base, OHCI1394_REGISTER_SIZE);
+ if (ohci->registers == NULL) {
+ PRINT_G(KERN_ERR, "Failed to remap registers");
+ err = -ENXIO;
+ goto err;
+ }
+ ohci->init_state = OHCI_INIT_HAVE_IOMAPPING;
+ DBGMSG("Remapped memory spaces reg 0x%p", ohci->registers);
+
+ /* csr_config rom allocation */
+ ohci->csr_config_rom_cpu =
+ pci_alloc_consistent(ohci->dev, OHCI_CONFIG_ROM_LEN,
+ &ohci->csr_config_rom_bus);
+ if (ohci->csr_config_rom_cpu == NULL) {
+ PRINT_G(KERN_ERR, "Failed to allocate %s", "buffer config rom");
+ goto err;
+ }
+ ohci->init_state = OHCI_INIT_HAVE_CONFIG_ROM_BUFFER;
+
+ /* self-id dma buffer allocation */
+ ohci->selfid_buf_cpu =
+ pci_alloc_consistent(ohci->dev, OHCI1394_SI_DMA_BUF_SIZE,
+ &ohci->selfid_buf_bus);
+ if (ohci->selfid_buf_cpu == NULL) {
+ PRINT_G(KERN_ERR, "Failed to allocate %s", "self-ID buffer");
+ goto err;
+ }
+ ohci->init_state = OHCI_INIT_HAVE_SELFID_BUFFER;
+
+ if ((unsigned long)ohci->selfid_buf_cpu & 0x1fff)
+ PRINT(KERN_INFO, "SelfID buffer %p is not aligned on "
+ "8Kb boundary... may cause problems on some CXD3222 chip",
+ ohci->selfid_buf_cpu);
+
+ /* No self-id errors at startup */
+ ohci->self_id_errors = 0;
+
+ ohci->init_state = OHCI_INIT_HAVE_TXRX_BUFFERS__MAYBE;
+ /* AR DMA request context allocation */
+ if (alloc_dma_rcv_ctx(ohci, &ohci->ar_req_context,
+ DMA_CTX_ASYNC_REQ, 0, AR_REQ_NUM_DESC,
+ AR_REQ_BUF_SIZE, AR_REQ_SPLIT_BUF_SIZE,
+ OHCI1394_AsReqRcvContextBase) < 0) {
+ PRINT_G(KERN_ERR, "Failed to allocate %s", "AR Req context");
+ goto err;
+ }
+ /* AR DMA response context allocation */
+ if (alloc_dma_rcv_ctx(ohci, &ohci->ar_resp_context,
+ DMA_CTX_ASYNC_RESP, 0, AR_RESP_NUM_DESC,
+ AR_RESP_BUF_SIZE, AR_RESP_SPLIT_BUF_SIZE,
+ OHCI1394_AsRspRcvContextBase) < 0) {
+ PRINT_G(KERN_ERR, "Failed to allocate %s", "AR Resp context");
+ goto err;
+ }
+ /* AT DMA request context */
+ if (alloc_dma_trm_ctx(ohci, &ohci->at_req_context,
+ DMA_CTX_ASYNC_REQ, 0, AT_REQ_NUM_DESC,
+ OHCI1394_AsReqTrContextBase) < 0) {
+ PRINT_G(KERN_ERR, "Failed to allocate %s", "AT Req context");
+ goto err;
+ }
+ /* AT DMA response context */
+ if (alloc_dma_trm_ctx(ohci, &ohci->at_resp_context,
+ DMA_CTX_ASYNC_RESP, 1, AT_RESP_NUM_DESC,
+ OHCI1394_AsRspTrContextBase) < 0) {
+ PRINT_G(KERN_ERR, "Failed to allocate %s", "AT Resp context");
+ goto err;
+ }
+ /* Start off with a soft reset, to clear everything to a sane
+ * state. */
+ ohci_soft_reset(ohci);
+
+ /* Now enable LPS, which we need in order to start accessing
+ * most of the registers. In fact, on some cards (ALI M5251),
+ * accessing registers in the SClk domain without LPS enabled
+ * will lock up the machine. Wait 50msec to make sure we have
+ * full link enabled. */
+ reg_write(ohci, OHCI1394_HCControlSet, OHCI1394_HCControl_LPS);
+
+ /* Disable and clear interrupts */
+ reg_write(ohci, OHCI1394_IntEventClear, 0xffffffff);
+ reg_write(ohci, OHCI1394_IntMaskClear, 0xffffffff);
+
+ mdelay(50);
+
+ /* Determine the number of available IR and IT contexts. */
+ ohci->nb_iso_rcv_ctx =
+ get_nb_iso_ctx(ohci, OHCI1394_IsoRecvIntMaskSet);
+ ohci->nb_iso_xmit_ctx =
+ get_nb_iso_ctx(ohci, OHCI1394_IsoXmitIntMaskSet);
+
+ /* Set the usage bits for non-existent contexts so they can't
+ * be allocated */
+ ohci->ir_ctx_usage = ~0 << ohci->nb_iso_rcv_ctx;
+ ohci->it_ctx_usage = ~0 << ohci->nb_iso_xmit_ctx;
+
+ INIT_LIST_HEAD(&ohci->iso_tasklet_list);
+ spin_lock_init(&ohci->iso_tasklet_list_lock);
+ ohci->ISO_channel_usage = 0;
+ spin_lock_init(&ohci->IR_channel_lock);
+
+ spin_lock_init(&ohci->event_lock);
+
+ /*
+ * interrupts are disabled, all right, but... due to IRQF_SHARED we
+ * might get called anyway. We'll see no event, of course, but
+ * we need to get to that "no event", so enough should be initialized
+ * by that point.
+ */
+ if (request_irq(dev->irq, ohci_irq_handler, IRQF_SHARED,
+ OHCI1394_DRIVER_NAME, ohci)) {
+ PRINT_G(KERN_ERR, "Failed to allocate interrupt %d", dev->irq);
+ goto err;
+ }
+ ohci->init_state = OHCI_INIT_HAVE_IRQ;
+ ohci_initialize(ohci);
+
+ /* Set certain csr values */
+ host->csr.guid_hi = reg_read(ohci, OHCI1394_GUIDHi);
+ host->csr.guid_lo = reg_read(ohci, OHCI1394_GUIDLo);
+ host->csr.cyc_clk_acc = 100; /* how do we determine clk accuracy? */
+ host->csr.max_rec = (reg_read(ohci, OHCI1394_BusOptions) >> 12) & 0xf;
+ host->csr.lnk_spd = reg_read(ohci, OHCI1394_BusOptions) & 0x7;
+
+ if (phys_dma) {
+ host->low_addr_space =
+ (u64) reg_read(ohci, OHCI1394_PhyUpperBound) << 16;
+ if (!host->low_addr_space)
+ host->low_addr_space = OHCI1394_PHYS_UPPER_BOUND_FIXED;
+ }
+ host->middle_addr_space = OHCI1394_MIDDLE_ADDRESS_SPACE;
+
+ /* Tell the highlevel this host is ready */
+ if (hpsb_add_host(host)) {
+ PRINT_G(KERN_ERR, "Failed to register host with highlevel");
+ goto err;
+ }
+ ohci->init_state = OHCI_INIT_DONE;
+
+ return 0;
+err:
+ ohci1394_pci_remove(dev);
+ return err;
+}
+
+static void ohci1394_pci_remove(struct pci_dev *dev)
+{
+ struct ti_ohci *ohci;
+ struct device *device;
+
+ ohci = pci_get_drvdata(dev);
+ if (!ohci)
+ goto out;
+
+ device = get_device(&ohci->host->device);
+
+ switch (ohci->init_state) {
+ case OHCI_INIT_DONE:
+ hpsb_remove_host(ohci->host);
+
+ /* Clear out BUS Options */
+ reg_write(ohci, OHCI1394_ConfigROMhdr, 0);
+ reg_write(ohci, OHCI1394_BusOptions,
+ (reg_read(ohci, OHCI1394_BusOptions) & 0x0000f007) |
+ 0x00ff0000);
+ memset(ohci->csr_config_rom_cpu, 0, OHCI_CONFIG_ROM_LEN);
+
+ case OHCI_INIT_HAVE_IRQ:
+ /* Clear interrupt registers */
+ reg_write(ohci, OHCI1394_IntMaskClear, 0xffffffff);
+ reg_write(ohci, OHCI1394_IntEventClear, 0xffffffff);
+ reg_write(ohci, OHCI1394_IsoXmitIntMaskClear, 0xffffffff);
+ reg_write(ohci, OHCI1394_IsoXmitIntEventClear, 0xffffffff);
+ reg_write(ohci, OHCI1394_IsoRecvIntMaskClear, 0xffffffff);
+ reg_write(ohci, OHCI1394_IsoRecvIntEventClear, 0xffffffff);
+
+ /* Disable IRM Contender */
+ set_phy_reg(ohci, 4, ~0xc0 & get_phy_reg(ohci, 4));
+
+ /* Clear link control register */
+ reg_write(ohci, OHCI1394_LinkControlClear, 0xffffffff);
+
+ /* Let all other nodes know to ignore us */
+ ohci_devctl(ohci->host, RESET_BUS, LONG_RESET_NO_FORCE_ROOT);
+
+ /* Soft reset before we start - this disables
+ * interrupts and clears linkEnable and LPS. */
+ ohci_soft_reset(ohci);
+ free_irq(dev->irq, ohci);
+
+ case OHCI_INIT_HAVE_TXRX_BUFFERS__MAYBE:
+ /* The ohci_soft_reset() stops all DMA contexts, so we
+ * dont need to do this. */
+ free_dma_rcv_ctx(&ohci->ar_req_context);
+ free_dma_rcv_ctx(&ohci->ar_resp_context);
+ free_dma_trm_ctx(&ohci->at_req_context);
+ free_dma_trm_ctx(&ohci->at_resp_context);
+
+ case OHCI_INIT_HAVE_SELFID_BUFFER:
+ pci_free_consistent(dev, OHCI1394_SI_DMA_BUF_SIZE,
+ ohci->selfid_buf_cpu,
+ ohci->selfid_buf_bus);
+
+ case OHCI_INIT_HAVE_CONFIG_ROM_BUFFER:
+ pci_free_consistent(dev, OHCI_CONFIG_ROM_LEN,
+ ohci->csr_config_rom_cpu,
+ ohci->csr_config_rom_bus);
+
+ case OHCI_INIT_HAVE_IOMAPPING:
+ iounmap(ohci->registers);
+
+ case OHCI_INIT_HAVE_MEM_REGION:
+ release_mem_region(pci_resource_start(dev, 0),
+ OHCI1394_REGISTER_SIZE);
+
+ case OHCI_INIT_ALLOC_HOST:
+ pci_set_drvdata(dev, NULL);
+ }
+
+ if (device)
+ put_device(device);
+out:
+ ohci1394_pmac_off(dev);
+}
+
+#ifdef CONFIG_PM
+static int ohci1394_pci_suspend(struct pci_dev *dev, pm_message_t state)
+{
+ int err;
+ struct ti_ohci *ohci = pci_get_drvdata(dev);
+
+ if (!ohci) {
+ printk(KERN_ERR "%s: tried to suspend nonexisting host\n",
+ OHCI1394_DRIVER_NAME);
+ return -ENXIO;
+ }
+ DBGMSG("suspend called");
+
+ /* Clear the async DMA contexts and stop using the controller */
+ hpsb_bus_reset(ohci->host);
+
+ /* See ohci1394_pci_remove() for comments on this sequence */
+ reg_write(ohci, OHCI1394_ConfigROMhdr, 0);
+ reg_write(ohci, OHCI1394_BusOptions,
+ (reg_read(ohci, OHCI1394_BusOptions) & 0x0000f007) |
+ 0x00ff0000);
+ reg_write(ohci, OHCI1394_IntMaskClear, 0xffffffff);
+ reg_write(ohci, OHCI1394_IntEventClear, 0xffffffff);
+ reg_write(ohci, OHCI1394_IsoXmitIntMaskClear, 0xffffffff);
+ reg_write(ohci, OHCI1394_IsoXmitIntEventClear, 0xffffffff);
+ reg_write(ohci, OHCI1394_IsoRecvIntMaskClear, 0xffffffff);
+ reg_write(ohci, OHCI1394_IsoRecvIntEventClear, 0xffffffff);
+ set_phy_reg(ohci, 4, ~0xc0 & get_phy_reg(ohci, 4));
+ reg_write(ohci, OHCI1394_LinkControlClear, 0xffffffff);
+ ohci_devctl(ohci->host, RESET_BUS, LONG_RESET_NO_FORCE_ROOT);
+ ohci_soft_reset(ohci);
+
+ err = pci_save_state(dev);
+ if (err) {
+ PRINT(KERN_ERR, "pci_save_state failed with %d", err);
+ return err;
+ }
+ err = pci_set_power_state(dev, pci_choose_state(dev, state));
+ if (err)
+ DBGMSG("pci_set_power_state failed with %d", err);
+ ohci1394_pmac_off(dev);
+
+ return 0;
+}
+
+static int ohci1394_pci_resume(struct pci_dev *dev)
+{
+ int err;
+ struct ti_ohci *ohci = pci_get_drvdata(dev);
+
+ if (!ohci) {
+ printk(KERN_ERR "%s: tried to resume nonexisting host\n",
+ OHCI1394_DRIVER_NAME);
+ return -ENXIO;
+ }
+ DBGMSG("resume called");
+
+ ohci1394_pmac_on(dev);
+ pci_set_power_state(dev, PCI_D0);
+ pci_restore_state(dev);
+ err = pci_enable_device(dev);
+ if (err) {
+ PRINT(KERN_ERR, "pci_enable_device failed with %d", err);
+ return err;
+ }
+
+ /* See ohci1394_pci_probe() for comments on this sequence */
+ ohci_soft_reset(ohci);
+ reg_write(ohci, OHCI1394_HCControlSet, OHCI1394_HCControl_LPS);
+ reg_write(ohci, OHCI1394_IntEventClear, 0xffffffff);
+ reg_write(ohci, OHCI1394_IntMaskClear, 0xffffffff);
+ mdelay(50);
+ ohci_initialize(ohci);
+
+ hpsb_resume_host(ohci->host);
+ return 0;
+}
+#endif /* CONFIG_PM */
+
+static struct pci_device_id ohci1394_pci_tbl[] = {
+ {
+ .class = PCI_CLASS_SERIAL_FIREWIRE_OHCI,
+ .class_mask = PCI_ANY_ID,
+ .vendor = PCI_ANY_ID,
+ .device = PCI_ANY_ID,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ },
+ { 0, },
+};
+
+MODULE_DEVICE_TABLE(pci, ohci1394_pci_tbl);
+
+static struct pci_driver ohci1394_pci_driver = {
+ .name = OHCI1394_DRIVER_NAME,
+ .id_table = ohci1394_pci_tbl,
+ .probe = ohci1394_pci_probe,
+ .remove = ohci1394_pci_remove,
+#ifdef CONFIG_PM
+ .resume = ohci1394_pci_resume,
+ .suspend = ohci1394_pci_suspend,
+#endif
+};
+
+/***********************************
+ * OHCI1394 Video Interface *
+ ***********************************/
+
+/* essentially the only purpose of this code is to allow another
+ module to hook into ohci's interrupt handler */
+
+/* returns zero if successful, one if DMA context is locked up */
+int ohci1394_stop_context(struct ti_ohci *ohci, int reg, char *msg)
+{
+ int i=0;
+
+ /* stop the channel program if it's still running */
+ reg_write(ohci, reg, 0x8000);
+
+ /* Wait until it effectively stops */
+ while (reg_read(ohci, reg) & 0x400) {
+ i++;
+ if (i>5000) {
+ PRINT(KERN_ERR,
+ "Runaway loop while stopping context: %s...", msg ? msg : "");
+ return 1;
+ }
+
+ mb();
+ udelay(10);
+ }
+ if (msg) PRINT(KERN_ERR, "%s: dma prg stopped", msg);
+ return 0;
+}
+
+void ohci1394_init_iso_tasklet(struct ohci1394_iso_tasklet *tasklet, int type,
+ void (*func)(unsigned long), unsigned long data)
+{
+ tasklet_init(&tasklet->tasklet, func, data);
+ tasklet->type = type;
+ /* We init the tasklet->link field, so we can list_del() it
+ * without worrying whether it was added to the list or not. */
+ INIT_LIST_HEAD(&tasklet->link);
+}
+
+int ohci1394_register_iso_tasklet(struct ti_ohci *ohci,
+ struct ohci1394_iso_tasklet *tasklet)
+{
+ unsigned long flags, *usage;
+ int n, i, r = -EBUSY;
+
+ if (tasklet->type == OHCI_ISO_TRANSMIT) {
+ n = ohci->nb_iso_xmit_ctx;
+ usage = &ohci->it_ctx_usage;
+ }
+ else {
+ n = ohci->nb_iso_rcv_ctx;
+ usage = &ohci->ir_ctx_usage;
+
+ /* only one receive context can be multichannel (OHCI sec 10.4.1) */
+ if (tasklet->type == OHCI_ISO_MULTICHANNEL_RECEIVE) {
+ if (test_and_set_bit(0, &ohci->ir_multichannel_used)) {
+ return r;
+ }
+ }
+ }
+
+ spin_lock_irqsave(&ohci->iso_tasklet_list_lock, flags);
+
+ for (i = 0; i < n; i++)
+ if (!test_and_set_bit(i, usage)) {
+ tasklet->context = i;
+ list_add_tail(&tasklet->link, &ohci->iso_tasklet_list);
+ r = 0;
+ break;
+ }
+
+ spin_unlock_irqrestore(&ohci->iso_tasklet_list_lock, flags);
+
+ return r;
+}
+
+void ohci1394_unregister_iso_tasklet(struct ti_ohci *ohci,
+ struct ohci1394_iso_tasklet *tasklet)
+{
+ unsigned long flags;
+
+ tasklet_kill(&tasklet->tasklet);
+
+ spin_lock_irqsave(&ohci->iso_tasklet_list_lock, flags);
+
+ if (tasklet->type == OHCI_ISO_TRANSMIT)
+ clear_bit(tasklet->context, &ohci->it_ctx_usage);
+ else {
+ clear_bit(tasklet->context, &ohci->ir_ctx_usage);
+
+ if (tasklet->type == OHCI_ISO_MULTICHANNEL_RECEIVE) {
+ clear_bit(0, &ohci->ir_multichannel_used);
+ }
+ }
+
+ list_del(&tasklet->link);
+
+ spin_unlock_irqrestore(&ohci->iso_tasklet_list_lock, flags);
+}
+
+EXPORT_SYMBOL(ohci1394_stop_context);
+EXPORT_SYMBOL(ohci1394_init_iso_tasklet);
+EXPORT_SYMBOL(ohci1394_register_iso_tasklet);
+EXPORT_SYMBOL(ohci1394_unregister_iso_tasklet);
+
+/***********************************
+ * General module initialization *
+ ***********************************/
+
+MODULE_AUTHOR("Sebastien Rougeaux <sebastien.rougeaux@anu.edu.au>");
+MODULE_DESCRIPTION("Driver for PCI OHCI IEEE-1394 controllers");
+MODULE_LICENSE("GPL");
+
+static void __exit ohci1394_cleanup (void)
+{
+ pci_unregister_driver(&ohci1394_pci_driver);
+}
+
+static int __init ohci1394_init(void)
+{
+ return pci_register_driver(&ohci1394_pci_driver);
+}
+
+module_init(ohci1394_init);
+module_exit(ohci1394_cleanup);
diff --git a/drivers/ieee1394/ohci1394.h b/drivers/ieee1394/ohci1394.h
new file mode 100644
index 0000000..7fb8ab9
--- /dev/null
+++ b/drivers/ieee1394/ohci1394.h
@@ -0,0 +1,453 @@
+/*
+ * ohci1394.h - driver for OHCI 1394 boards
+ * Copyright (C)1999,2000 Sebastien Rougeaux <sebastien.rougeaux@anu.edu.au>
+ * Gord Peters <GordPeters@smarttech.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _OHCI1394_H
+#define _OHCI1394_H
+
+#include "ieee1394_types.h"
+#include <asm/io.h>
+
+#define OHCI1394_DRIVER_NAME "ohci1394"
+
+#define OHCI1394_MAX_AT_REQ_RETRIES 0xf
+#define OHCI1394_MAX_AT_RESP_RETRIES 0x2
+#define OHCI1394_MAX_PHYS_RESP_RETRIES 0x8
+#define OHCI1394_MAX_SELF_ID_ERRORS 16
+
+#define AR_REQ_NUM_DESC 4 /* number of AR req descriptors */
+#define AR_REQ_BUF_SIZE PAGE_SIZE /* size of AR req buffers */
+#define AR_REQ_SPLIT_BUF_SIZE PAGE_SIZE /* split packet buffer */
+
+#define AR_RESP_NUM_DESC 4 /* number of AR resp descriptors */
+#define AR_RESP_BUF_SIZE PAGE_SIZE /* size of AR resp buffers */
+#define AR_RESP_SPLIT_BUF_SIZE PAGE_SIZE /* split packet buffer */
+
+#define IR_NUM_DESC 16 /* number of IR descriptors */
+#define IR_BUF_SIZE PAGE_SIZE /* 4096 bytes/buffer */
+#define IR_SPLIT_BUF_SIZE PAGE_SIZE /* split packet buffer */
+
+#define IT_NUM_DESC 16 /* number of IT descriptors */
+
+#define AT_REQ_NUM_DESC 32 /* number of AT req descriptors */
+#define AT_RESP_NUM_DESC 32 /* number of AT resp descriptors */
+
+#define OHCI_LOOP_COUNT 100 /* Number of loops for reg read waits */
+
+#define OHCI_CONFIG_ROM_LEN 1024 /* Length of the mapped configrom space */
+
+#define OHCI1394_SI_DMA_BUF_SIZE 8192 /* length of the selfid buffer */
+
+/* PCI configuration space addresses */
+#define OHCI1394_PCI_HCI_Control 0x40
+
+struct dma_cmd {
+ u32 control;
+ u32 address;
+ u32 branchAddress;
+ u32 status;
+};
+
+/*
+ * FIXME:
+ * It is important that a single at_dma_prg does not cross a page boundary
+ * The proper way to do it would be to do the check dynamically as the
+ * programs are inserted into the AT fifo.
+ */
+struct at_dma_prg {
+ struct dma_cmd begin;
+ quadlet_t data[4];
+ struct dma_cmd end;
+ quadlet_t pad[4]; /* FIXME: quick hack for memory alignment */
+};
+
+/* identify whether a DMA context is asynchronous or isochronous */
+enum context_type { DMA_CTX_ASYNC_REQ, DMA_CTX_ASYNC_RESP, DMA_CTX_ISO };
+
+/* DMA receive context */
+struct dma_rcv_ctx {
+ struct ti_ohci *ohci;
+ enum context_type type;
+ int ctx;
+ unsigned int num_desc;
+
+ unsigned int buf_size;
+ unsigned int split_buf_size;
+
+ /* dma block descriptors */
+ struct dma_cmd **prg_cpu;
+ dma_addr_t *prg_bus;
+ struct pci_pool *prg_pool;
+
+ /* dma buffers */
+ quadlet_t **buf_cpu;
+ dma_addr_t *buf_bus;
+
+ unsigned int buf_ind;
+ unsigned int buf_offset;
+ quadlet_t *spb;
+ spinlock_t lock;
+ struct tasklet_struct task;
+ int ctrlClear;
+ int ctrlSet;
+ int cmdPtr;
+ int ctxtMatch;
+};
+
+/* DMA transmit context */
+struct dma_trm_ctx {
+ struct ti_ohci *ohci;
+ enum context_type type;
+ int ctx;
+ unsigned int num_desc;
+
+ /* dma block descriptors */
+ struct at_dma_prg **prg_cpu;
+ dma_addr_t *prg_bus;
+ struct pci_pool *prg_pool;
+
+ unsigned int prg_ind;
+ unsigned int sent_ind;
+ int free_prgs;
+ quadlet_t *branchAddrPtr;
+
+ /* list of packets inserted in the AT FIFO */
+ struct list_head fifo_list;
+
+ /* list of pending packets to be inserted in the AT FIFO */
+ struct list_head pending_list;
+
+ spinlock_t lock;
+ struct tasklet_struct task;
+ int ctrlClear;
+ int ctrlSet;
+ int cmdPtr;
+};
+
+struct ohci1394_iso_tasklet {
+ struct tasklet_struct tasklet;
+ struct list_head link;
+ int context;
+ enum { OHCI_ISO_TRANSMIT, OHCI_ISO_RECEIVE,
+ OHCI_ISO_MULTICHANNEL_RECEIVE } type;
+};
+
+struct ti_ohci {
+ struct pci_dev *dev;
+
+ enum {
+ OHCI_INIT_ALLOC_HOST,
+ OHCI_INIT_HAVE_MEM_REGION,
+ OHCI_INIT_HAVE_IOMAPPING,
+ OHCI_INIT_HAVE_CONFIG_ROM_BUFFER,
+ OHCI_INIT_HAVE_SELFID_BUFFER,
+ OHCI_INIT_HAVE_TXRX_BUFFERS__MAYBE,
+ OHCI_INIT_HAVE_IRQ,
+ OHCI_INIT_DONE,
+ } init_state;
+
+ /* remapped memory spaces */
+ void __iomem *registers;
+
+ /* dma buffer for self-id packets */
+ quadlet_t *selfid_buf_cpu;
+ dma_addr_t selfid_buf_bus;
+
+ /* buffer for csr config rom */
+ quadlet_t *csr_config_rom_cpu;
+ dma_addr_t csr_config_rom_bus;
+ int csr_config_rom_length;
+
+ unsigned int max_packet_size;
+
+ /* async receive */
+ struct dma_rcv_ctx ar_resp_context;
+ struct dma_rcv_ctx ar_req_context;
+
+ /* async transmit */
+ struct dma_trm_ctx at_resp_context;
+ struct dma_trm_ctx at_req_context;
+
+ /* iso receive */
+ int nb_iso_rcv_ctx;
+ unsigned long ir_ctx_usage; /* use test_and_set_bit() for atomicity */
+ unsigned long ir_multichannel_used; /* ditto */
+ spinlock_t IR_channel_lock;
+
+ /* iso transmit */
+ int nb_iso_xmit_ctx;
+ unsigned long it_ctx_usage; /* use test_and_set_bit() for atomicity */
+
+ u64 ISO_channel_usage;
+
+ /* IEEE-1394 part follows */
+ struct hpsb_host *host;
+
+ int phyid, isroot;
+
+ spinlock_t phy_reg_lock;
+ spinlock_t event_lock;
+
+ int self_id_errors;
+
+ /* Tasklets for iso receive and transmit, used by video1394
+ * and dv1394 */
+ struct list_head iso_tasklet_list;
+ spinlock_t iso_tasklet_list_lock;
+
+ /* Swap the selfid buffer? */
+ unsigned int selfid_swap:1;
+ /* Some Apple chipset seem to swap incoming headers for us */
+ unsigned int no_swap_incoming:1;
+
+ /* Force extra paranoia checking on bus-reset handling */
+ unsigned int check_busreset:1;
+};
+
+static inline int cross_bound(unsigned long addr, unsigned int size)
+{
+ if (size == 0)
+ return 0;
+
+ if (size > PAGE_SIZE)
+ return 1;
+
+ if (addr >> PAGE_SHIFT != (addr + size - 1) >> PAGE_SHIFT)
+ return 1;
+
+ return 0;
+}
+
+/*
+ * Register read and write helper functions.
+ */
+static inline void reg_write(const struct ti_ohci *ohci, int offset, u32 data)
+{
+ writel(data, ohci->registers + offset);
+}
+
+static inline u32 reg_read(const struct ti_ohci *ohci, int offset)
+{
+ return readl(ohci->registers + offset);
+}
+
+
+/* 2 KiloBytes of register space */
+#define OHCI1394_REGISTER_SIZE 0x800
+
+/* Offsets relative to context bases defined below */
+
+#define OHCI1394_ContextControlSet 0x000
+#define OHCI1394_ContextControlClear 0x004
+#define OHCI1394_ContextCommandPtr 0x00C
+
+/* register map */
+#define OHCI1394_Version 0x000
+#define OHCI1394_GUID_ROM 0x004
+#define OHCI1394_ATRetries 0x008
+#define OHCI1394_CSRData 0x00C
+#define OHCI1394_CSRCompareData 0x010
+#define OHCI1394_CSRControl 0x014
+#define OHCI1394_ConfigROMhdr 0x018
+#define OHCI1394_BusID 0x01C
+#define OHCI1394_BusOptions 0x020
+#define OHCI1394_GUIDHi 0x024
+#define OHCI1394_GUIDLo 0x028
+#define OHCI1394_ConfigROMmap 0x034
+#define OHCI1394_PostedWriteAddressLo 0x038
+#define OHCI1394_PostedWriteAddressHi 0x03C
+#define OHCI1394_VendorID 0x040
+#define OHCI1394_HCControlSet 0x050
+#define OHCI1394_HCControlClear 0x054
+#define OHCI1394_HCControl_noByteSwap 0x40000000
+#define OHCI1394_HCControl_programPhyEnable 0x00800000
+#define OHCI1394_HCControl_aPhyEnhanceEnable 0x00400000
+#define OHCI1394_HCControl_LPS 0x00080000
+#define OHCI1394_HCControl_postedWriteEnable 0x00040000
+#define OHCI1394_HCControl_linkEnable 0x00020000
+#define OHCI1394_HCControl_softReset 0x00010000
+#define OHCI1394_SelfIDBuffer 0x064
+#define OHCI1394_SelfIDCount 0x068
+#define OHCI1394_IRMultiChanMaskHiSet 0x070
+#define OHCI1394_IRMultiChanMaskHiClear 0x074
+#define OHCI1394_IRMultiChanMaskLoSet 0x078
+#define OHCI1394_IRMultiChanMaskLoClear 0x07C
+#define OHCI1394_IntEventSet 0x080
+#define OHCI1394_IntEventClear 0x084
+#define OHCI1394_IntMaskSet 0x088
+#define OHCI1394_IntMaskClear 0x08C
+#define OHCI1394_IsoXmitIntEventSet 0x090
+#define OHCI1394_IsoXmitIntEventClear 0x094
+#define OHCI1394_IsoXmitIntMaskSet 0x098
+#define OHCI1394_IsoXmitIntMaskClear 0x09C
+#define OHCI1394_IsoRecvIntEventSet 0x0A0
+#define OHCI1394_IsoRecvIntEventClear 0x0A4
+#define OHCI1394_IsoRecvIntMaskSet 0x0A8
+#define OHCI1394_IsoRecvIntMaskClear 0x0AC
+#define OHCI1394_InitialBandwidthAvailable 0x0B0
+#define OHCI1394_InitialChannelsAvailableHi 0x0B4
+#define OHCI1394_InitialChannelsAvailableLo 0x0B8
+#define OHCI1394_FairnessControl 0x0DC
+#define OHCI1394_LinkControlSet 0x0E0
+#define OHCI1394_LinkControlClear 0x0E4
+#define OHCI1394_LinkControl_RcvSelfID 0x00000200
+#define OHCI1394_LinkControl_RcvPhyPkt 0x00000400
+#define OHCI1394_LinkControl_CycleTimerEnable 0x00100000
+#define OHCI1394_LinkControl_CycleMaster 0x00200000
+#define OHCI1394_LinkControl_CycleSource 0x00400000
+#define OHCI1394_NodeID 0x0E8
+#define OHCI1394_PhyControl 0x0EC
+#define OHCI1394_IsochronousCycleTimer 0x0F0
+#define OHCI1394_AsReqFilterHiSet 0x100
+#define OHCI1394_AsReqFilterHiClear 0x104
+#define OHCI1394_AsReqFilterLoSet 0x108
+#define OHCI1394_AsReqFilterLoClear 0x10C
+#define OHCI1394_PhyReqFilterHiSet 0x110
+#define OHCI1394_PhyReqFilterHiClear 0x114
+#define OHCI1394_PhyReqFilterLoSet 0x118
+#define OHCI1394_PhyReqFilterLoClear 0x11C
+#define OHCI1394_PhyUpperBound 0x120
+
+#define OHCI1394_AsReqTrContextBase 0x180
+#define OHCI1394_AsReqTrContextControlSet 0x180
+#define OHCI1394_AsReqTrContextControlClear 0x184
+#define OHCI1394_AsReqTrCommandPtr 0x18C
+
+#define OHCI1394_AsRspTrContextBase 0x1A0
+#define OHCI1394_AsRspTrContextControlSet 0x1A0
+#define OHCI1394_AsRspTrContextControlClear 0x1A4
+#define OHCI1394_AsRspTrCommandPtr 0x1AC
+
+#define OHCI1394_AsReqRcvContextBase 0x1C0
+#define OHCI1394_AsReqRcvContextControlSet 0x1C0
+#define OHCI1394_AsReqRcvContextControlClear 0x1C4
+#define OHCI1394_AsReqRcvCommandPtr 0x1CC
+
+#define OHCI1394_AsRspRcvContextBase 0x1E0
+#define OHCI1394_AsRspRcvContextControlSet 0x1E0
+#define OHCI1394_AsRspRcvContextControlClear 0x1E4
+#define OHCI1394_AsRspRcvCommandPtr 0x1EC
+
+/* Isochronous transmit registers */
+/* Add (16 * n) for context n */
+#define OHCI1394_IsoXmitContextBase 0x200
+#define OHCI1394_IsoXmitContextControlSet 0x200
+#define OHCI1394_IsoXmitContextControlClear 0x204
+#define OHCI1394_IsoXmitCommandPtr 0x20C
+
+/* Isochronous receive registers */
+/* Add (32 * n) for context n */
+#define OHCI1394_IsoRcvContextBase 0x400
+#define OHCI1394_IsoRcvContextControlSet 0x400
+#define OHCI1394_IsoRcvContextControlClear 0x404
+#define OHCI1394_IsoRcvCommandPtr 0x40C
+#define OHCI1394_IsoRcvContextMatch 0x410
+
+/* Interrupts Mask/Events */
+
+#define OHCI1394_reqTxComplete 0x00000001
+#define OHCI1394_respTxComplete 0x00000002
+#define OHCI1394_ARRQ 0x00000004
+#define OHCI1394_ARRS 0x00000008
+#define OHCI1394_RQPkt 0x00000010
+#define OHCI1394_RSPkt 0x00000020
+#define OHCI1394_isochTx 0x00000040
+#define OHCI1394_isochRx 0x00000080
+#define OHCI1394_postedWriteErr 0x00000100
+#define OHCI1394_lockRespErr 0x00000200
+#define OHCI1394_selfIDComplete 0x00010000
+#define OHCI1394_busReset 0x00020000
+#define OHCI1394_phy 0x00080000
+#define OHCI1394_cycleSynch 0x00100000
+#define OHCI1394_cycle64Seconds 0x00200000
+#define OHCI1394_cycleLost 0x00400000
+#define OHCI1394_cycleInconsistent 0x00800000
+#define OHCI1394_unrecoverableError 0x01000000
+#define OHCI1394_cycleTooLong 0x02000000
+#define OHCI1394_phyRegRcvd 0x04000000
+#define OHCI1394_masterIntEnable 0x80000000
+
+/* DMA Control flags */
+#define DMA_CTL_OUTPUT_MORE 0x00000000
+#define DMA_CTL_OUTPUT_LAST 0x10000000
+#define DMA_CTL_INPUT_MORE 0x20000000
+#define DMA_CTL_INPUT_LAST 0x30000000
+#define DMA_CTL_UPDATE 0x08000000
+#define DMA_CTL_IMMEDIATE 0x02000000
+#define DMA_CTL_IRQ 0x00300000
+#define DMA_CTL_BRANCH 0x000c0000
+#define DMA_CTL_WAIT 0x00030000
+
+/* OHCI evt_* error types, table 3-2 of the OHCI 1.1 spec. */
+#define EVT_NO_STATUS 0x0 /* No event status */
+#define EVT_RESERVED_A 0x1 /* Reserved, not used !!! */
+#define EVT_LONG_PACKET 0x2 /* The revc data was longer than the buf */
+#define EVT_MISSING_ACK 0x3 /* A subaction gap was detected before an ack
+ arrived, or recv'd ack had a parity error */
+#define EVT_UNDERRUN 0x4 /* Underrun on corresponding FIFO, packet
+ truncated */
+#define EVT_OVERRUN 0x5 /* A recv FIFO overflowed on reception of ISO
+ packet */
+#define EVT_DESCRIPTOR_READ 0x6 /* An unrecoverable error occurred while host was
+ reading a descriptor block */
+#define EVT_DATA_READ 0x7 /* An error occurred while host controller was
+ attempting to read from host memory in the data
+ stage of descriptor processing */
+#define EVT_DATA_WRITE 0x8 /* An error occurred while host controller was
+ attempting to write either during the data stage
+ of descriptor processing, or when processing a single
+ 16-bit host memory write */
+#define EVT_BUS_RESET 0x9 /* Identifies a PHY packet in the recv buffer as
+ being a synthesized bus reset packet */
+#define EVT_TIMEOUT 0xa /* Indicates that the asynchronous transmit response
+ packet expired and was not transmitted, or that an
+ IT DMA context experienced a skip processing overflow */
+#define EVT_TCODE_ERR 0xb /* A bad tCode is associated with this packet.
+ The packet was flushed */
+#define EVT_RESERVED_B 0xc /* Reserved, not used !!! */
+#define EVT_RESERVED_C 0xd /* Reserved, not used !!! */
+#define EVT_UNKNOWN 0xe /* An error condition has occurred that cannot be
+ represented by any other event codes defined herein. */
+#define EVT_FLUSHED 0xf /* Send by the link side of output FIFO when asynchronous
+ packets are being flushed due to a bus reset. */
+
+#define OHCI1394_TCODE_PHY 0xE
+
+/* Node offset map (phys DMA area, posted write area).
+ * The value of OHCI1394_PHYS_UPPER_BOUND_PROGRAMMED may be modified but must
+ * be lower than OHCI1394_MIDDLE_ADDRESS_SPACE.
+ * OHCI1394_PHYS_UPPER_BOUND_FIXED and OHCI1394_MIDDLE_ADDRESS_SPACE are
+ * constants given by the OHCI spec.
+ */
+#define OHCI1394_PHYS_UPPER_BOUND_FIXED 0x000100000000ULL /* 4 GB */
+#define OHCI1394_PHYS_UPPER_BOUND_PROGRAMMED 0x010000000000ULL /* 1 TB */
+#define OHCI1394_MIDDLE_ADDRESS_SPACE 0xffff00000000ULL
+
+void ohci1394_init_iso_tasklet(struct ohci1394_iso_tasklet *tasklet,
+ int type,
+ void (*func)(unsigned long),
+ unsigned long data);
+int ohci1394_register_iso_tasklet(struct ti_ohci *ohci,
+ struct ohci1394_iso_tasklet *tasklet);
+void ohci1394_unregister_iso_tasklet(struct ti_ohci *ohci,
+ struct ohci1394_iso_tasklet *tasklet);
+int ohci1394_stop_context(struct ti_ohci *ohci, int reg, char *msg);
+struct ti_ohci *ohci1394_get_struct(int card_num);
+
+#endif
diff --git a/drivers/ieee1394/pcilynx.c b/drivers/ieee1394/pcilynx.c
new file mode 100644
index 0000000..7aee1ac
--- /dev/null
+++ b/drivers/ieee1394/pcilynx.c
@@ -0,0 +1,1555 @@
+/*
+ * pcilynx.c - Texas Instruments PCILynx driver
+ * Copyright (C) 1999,2000 Andreas Bombe <andreas.bombe@munich.netsurf.de>,
+ * Stephan Linz <linz@mazet.de>
+ * Manfred Weihs <weihs@ict.tuwien.ac.at>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * Contributions:
+ *
+ * Manfred Weihs <weihs@ict.tuwien.ac.at>
+ * reading bus info block (containing GUID) from serial
+ * eeprom via i2c and storing it in config ROM
+ * Reworked code for initiating bus resets
+ * (long, short, with or without hold-off)
+ * Enhancements in async and iso send code
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/wait.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/fs.h>
+#include <linux/poll.h>
+#include <linux/kdev_t.h>
+#include <linux/dma-mapping.h>
+#include <asm/byteorder.h>
+#include <asm/atomic.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <asm/irq.h>
+
+#include "csr1212.h"
+#include "ieee1394.h"
+#include "ieee1394_types.h"
+#include "hosts.h"
+#include "ieee1394_core.h"
+#include "highlevel.h"
+#include "pcilynx.h"
+
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+
+/* print general (card independent) information */
+#define PRINT_G(level, fmt, args...) printk(level "pcilynx: " fmt "\n" , ## args)
+/* print card specific information */
+#define PRINT(level, card, fmt, args...) printk(level "pcilynx%d: " fmt "\n" , card , ## args)
+
+#ifdef CONFIG_IEEE1394_VERBOSEDEBUG
+#define PRINT_GD(level, fmt, args...) printk(level "pcilynx: " fmt "\n" , ## args)
+#define PRINTD(level, card, fmt, args...) printk(level "pcilynx%d: " fmt "\n" , card , ## args)
+#else
+#define PRINT_GD(level, fmt, args...) do {} while (0)
+#define PRINTD(level, card, fmt, args...) do {} while (0)
+#endif
+
+
+/* Module Parameters */
+static int skip_eeprom;
+module_param(skip_eeprom, int, 0444);
+MODULE_PARM_DESC(skip_eeprom, "Use generic bus info block instead of serial eeprom (default = 0).");
+
+
+static struct hpsb_host_driver lynx_driver;
+static unsigned int card_id;
+
+
+
+/*
+ * I2C stuff
+ */
+
+/* the i2c stuff was inspired by i2c-philips-par.c */
+
+static void bit_setscl(void *data, int state)
+{
+ if (state) {
+ ((struct ti_lynx *) data)->i2c_driven_state |= 0x00000040;
+ } else {
+ ((struct ti_lynx *) data)->i2c_driven_state &= ~0x00000040;
+ }
+ reg_write((struct ti_lynx *) data, SERIAL_EEPROM_CONTROL, ((struct ti_lynx *) data)->i2c_driven_state);
+}
+
+static void bit_setsda(void *data, int state)
+{
+ if (state) {
+ ((struct ti_lynx *) data)->i2c_driven_state |= 0x00000010;
+ } else {
+ ((struct ti_lynx *) data)->i2c_driven_state &= ~0x00000010;
+ }
+ reg_write((struct ti_lynx *) data, SERIAL_EEPROM_CONTROL, ((struct ti_lynx *) data)->i2c_driven_state);
+}
+
+static int bit_getscl(void *data)
+{
+ return reg_read((struct ti_lynx *) data, SERIAL_EEPROM_CONTROL) & 0x00000040;
+}
+
+static int bit_getsda(void *data)
+{
+ return reg_read((struct ti_lynx *) data, SERIAL_EEPROM_CONTROL) & 0x00000010;
+}
+
+static struct i2c_algo_bit_data bit_data = {
+ .setsda = bit_setsda,
+ .setscl = bit_setscl,
+ .getsda = bit_getsda,
+ .getscl = bit_getscl,
+ .udelay = 5,
+ .timeout = 100,
+};
+
+
+/*
+ * PCL handling functions.
+ */
+
+static pcl_t alloc_pcl(struct ti_lynx *lynx)
+{
+ u8 m;
+ int i, j;
+
+ spin_lock(&lynx->lock);
+ /* FIXME - use ffz() to make this readable */
+ for (i = 0; i < (LOCALRAM_SIZE / 1024); i++) {
+ m = lynx->pcl_bmap[i];
+ for (j = 0; j < 8; j++) {
+ if (m & 1<<j) {
+ continue;
+ }
+ m |= 1<<j;
+ lynx->pcl_bmap[i] = m;
+ spin_unlock(&lynx->lock);
+ return 8 * i + j;
+ }
+ }
+ spin_unlock(&lynx->lock);
+
+ return -1;
+}
+
+
+#if 0
+static void free_pcl(struct ti_lynx *lynx, pcl_t pclid)
+{
+ int off, bit;
+
+ off = pclid / 8;
+ bit = pclid % 8;
+
+ if (pclid < 0) {
+ return;
+ }
+
+ spin_lock(&lynx->lock);
+ if (lynx->pcl_bmap[off] & 1<<bit) {
+ lynx->pcl_bmap[off] &= ~(1<<bit);
+ } else {
+ PRINT(KERN_ERR, lynx->id,
+ "attempted to free unallocated PCL %d", pclid);
+ }
+ spin_unlock(&lynx->lock);
+}
+
+/* functions useful for debugging */
+static void pretty_print_pcl(const struct ti_pcl *pcl)
+{
+ int i;
+
+ printk("PCL next %08x, userdata %08x, status %08x, remtrans %08x, nextbuf %08x\n",
+ pcl->next, pcl->user_data, pcl->pcl_status,
+ pcl->remaining_transfer_count, pcl->next_data_buffer);
+
+ printk("PCL");
+ for (i=0; i<13; i++) {
+ printk(" c%x:%08x d%x:%08x",
+ i, pcl->buffer[i].control, i, pcl->buffer[i].pointer);
+ if (!(i & 0x3) && (i != 12)) printk("\nPCL");
+ }
+ printk("\n");
+}
+
+static void print_pcl(const struct ti_lynx *lynx, pcl_t pclid)
+{
+ struct ti_pcl pcl;
+
+ get_pcl(lynx, pclid, &pcl);
+ pretty_print_pcl(&pcl);
+}
+#endif
+
+
+
+/***********************************
+ * IEEE-1394 functionality section *
+ ***********************************/
+
+
+static int get_phy_reg(struct ti_lynx *lynx, int addr)
+{
+ int retval;
+ int i = 0;
+
+ unsigned long flags;
+
+ if (addr > 15) {
+ PRINT(KERN_ERR, lynx->id,
+ "%s: PHY register address %d out of range",
+ __func__, addr);
+ return -1;
+ }
+
+ spin_lock_irqsave(&lynx->phy_reg_lock, flags);
+
+ reg_write(lynx, LINK_PHY, LINK_PHY_READ | LINK_PHY_ADDR(addr));
+ do {
+ retval = reg_read(lynx, LINK_PHY);
+
+ if (i > 10000) {
+ PRINT(KERN_ERR, lynx->id, "%s: runaway loop, aborting",
+ __func__);
+ retval = -1;
+ break;
+ }
+ i++;
+ } while ((retval & 0xf00) != LINK_PHY_RADDR(addr));
+
+ reg_write(lynx, LINK_INT_STATUS, LINK_INT_PHY_REG_RCVD);
+ spin_unlock_irqrestore(&lynx->phy_reg_lock, flags);
+
+ if (retval != -1) {
+ return retval & 0xff;
+ } else {
+ return -1;
+ }
+}
+
+static int set_phy_reg(struct ti_lynx *lynx, int addr, int val)
+{
+ unsigned long flags;
+
+ if (addr > 15) {
+ PRINT(KERN_ERR, lynx->id,
+ "%s: PHY register address %d out of range", __func__, addr);
+ return -1;
+ }
+
+ if (val > 0xff) {
+ PRINT(KERN_ERR, lynx->id,
+ "%s: PHY register value %d out of range", __func__, val);
+ return -1;
+ }
+
+ spin_lock_irqsave(&lynx->phy_reg_lock, flags);
+
+ reg_write(lynx, LINK_PHY, LINK_PHY_WRITE | LINK_PHY_ADDR(addr)
+ | LINK_PHY_WDATA(val));
+
+ spin_unlock_irqrestore(&lynx->phy_reg_lock, flags);
+
+ return 0;
+}
+
+static int sel_phy_reg_page(struct ti_lynx *lynx, int page)
+{
+ int reg;
+
+ if (page > 7) {
+ PRINT(KERN_ERR, lynx->id,
+ "%s: PHY page %d out of range", __func__, page);
+ return -1;
+ }
+
+ reg = get_phy_reg(lynx, 7);
+ if (reg != -1) {
+ reg &= 0x1f;
+ reg |= (page << 5);
+ set_phy_reg(lynx, 7, reg);
+ return 0;
+ } else {
+ return -1;
+ }
+}
+
+#if 0 /* not needed at this time */
+static int sel_phy_reg_port(struct ti_lynx *lynx, int port)
+{
+ int reg;
+
+ if (port > 15) {
+ PRINT(KERN_ERR, lynx->id,
+ "%s: PHY port %d out of range", __func__, port);
+ return -1;
+ }
+
+ reg = get_phy_reg(lynx, 7);
+ if (reg != -1) {
+ reg &= 0xf0;
+ reg |= port;
+ set_phy_reg(lynx, 7, reg);
+ return 0;
+ } else {
+ return -1;
+ }
+}
+#endif
+
+static u32 get_phy_vendorid(struct ti_lynx *lynx)
+{
+ u32 pvid = 0;
+ sel_phy_reg_page(lynx, 1);
+ pvid |= (get_phy_reg(lynx, 10) << 16);
+ pvid |= (get_phy_reg(lynx, 11) << 8);
+ pvid |= get_phy_reg(lynx, 12);
+ PRINT(KERN_INFO, lynx->id, "PHY vendor id 0x%06x", pvid);
+ return pvid;
+}
+
+static u32 get_phy_productid(struct ti_lynx *lynx)
+{
+ u32 id = 0;
+ sel_phy_reg_page(lynx, 1);
+ id |= (get_phy_reg(lynx, 13) << 16);
+ id |= (get_phy_reg(lynx, 14) << 8);
+ id |= get_phy_reg(lynx, 15);
+ PRINT(KERN_INFO, lynx->id, "PHY product id 0x%06x", id);
+ return id;
+}
+
+static quadlet_t generate_own_selfid(struct ti_lynx *lynx,
+ struct hpsb_host *host)
+{
+ quadlet_t lsid;
+ char phyreg[7];
+ int i;
+
+ phyreg[0] = lynx->phy_reg0;
+ for (i = 1; i < 7; i++) {
+ phyreg[i] = get_phy_reg(lynx, i);
+ }
+
+ /* FIXME? We assume a TSB21LV03A phy here. This code doesn't support
+ more than 3 ports on the PHY anyway. */
+
+ lsid = 0x80400000 | ((phyreg[0] & 0xfc) << 22);
+ lsid |= (phyreg[1] & 0x3f) << 16; /* gap count */
+ lsid |= (phyreg[2] & 0xc0) << 8; /* max speed */
+ if (!hpsb_disable_irm)
+ lsid |= (phyreg[6] & 0x01) << 11; /* contender (phy dependent) */
+ /* lsid |= 1 << 11; *//* set contender (hack) */
+ lsid |= (phyreg[6] & 0x10) >> 3; /* initiated reset */
+
+ for (i = 0; i < (phyreg[2] & 0xf); i++) { /* ports */
+ if (phyreg[3 + i] & 0x4) {
+ lsid |= (((phyreg[3 + i] & 0x8) | 0x10) >> 3)
+ << (6 - i*2);
+ } else {
+ lsid |= 1 << (6 - i*2);
+ }
+ }
+
+ cpu_to_be32s(&lsid);
+ PRINT(KERN_DEBUG, lynx->id, "generated own selfid 0x%x", lsid);
+ return lsid;
+}
+
+static void handle_selfid(struct ti_lynx *lynx, struct hpsb_host *host)
+{
+ quadlet_t *q = lynx->rcv_page;
+ int phyid, isroot, size;
+ quadlet_t lsid = 0;
+ int i;
+
+ if (lynx->phy_reg0 == -1 || lynx->selfid_size == -1) return;
+
+ size = lynx->selfid_size;
+ phyid = lynx->phy_reg0;
+
+ i = (size > 16 ? 16 : size) / 4 - 1;
+ while (i >= 0) {
+ cpu_to_be32s(&q[i]);
+ i--;
+ }
+
+ if (!lynx->phyic.reg_1394a) {
+ lsid = generate_own_selfid(lynx, host);
+ }
+
+ isroot = (phyid & 2) != 0;
+ phyid >>= 2;
+ PRINT(KERN_INFO, lynx->id, "SelfID process finished (phyid %d, %s)",
+ phyid, (isroot ? "root" : "not root"));
+ reg_write(lynx, LINK_ID, (0xffc0 | phyid) << 16);
+
+ if (!lynx->phyic.reg_1394a && !size) {
+ hpsb_selfid_received(host, lsid);
+ }
+
+ while (size > 0) {
+ struct selfid *sid = (struct selfid *)q;
+
+ if (!lynx->phyic.reg_1394a && !sid->extended
+ && (sid->phy_id == (phyid + 1))) {
+ hpsb_selfid_received(host, lsid);
+ }
+
+ if (q[0] == ~q[1]) {
+ PRINT(KERN_DEBUG, lynx->id, "SelfID packet 0x%x rcvd",
+ q[0]);
+ hpsb_selfid_received(host, q[0]);
+ } else {
+ PRINT(KERN_INFO, lynx->id,
+ "inconsistent selfid 0x%x/0x%x", q[0], q[1]);
+ }
+ q += 2;
+ size -= 8;
+ }
+
+ if (!lynx->phyic.reg_1394a && isroot && phyid != 0) {
+ hpsb_selfid_received(host, lsid);
+ }
+
+ hpsb_selfid_complete(host, phyid, isroot);
+
+ if (host->in_bus_reset) return; /* in bus reset again */
+
+ if (isroot) reg_set_bits(lynx, LINK_CONTROL, LINK_CONTROL_CYCMASTER); //FIXME: I do not think, we need this here
+ reg_set_bits(lynx, LINK_CONTROL,
+ LINK_CONTROL_RCV_CMP_VALID | LINK_CONTROL_TX_ASYNC_EN
+ | LINK_CONTROL_RX_ASYNC_EN | LINK_CONTROL_CYCTIMEREN);
+}
+
+
+
+/* This must be called with the respective queue_lock held. */
+static void send_next(struct ti_lynx *lynx, int what)
+{
+ struct ti_pcl pcl;
+ struct lynx_send_data *d;
+ struct hpsb_packet *packet;
+
+#if 0 /* has been removed from ieee1394 core */
+ d = (what == hpsb_iso ? &lynx->iso_send : &lynx->async);
+#else
+ d = &lynx->async;
+#endif
+ if (!list_empty(&d->pcl_queue)) {
+ PRINT(KERN_ERR, lynx->id, "trying to queue a new packet in nonempty fifo");
+ BUG();
+ }
+
+ packet = driver_packet(d->queue.next);
+ list_move_tail(&packet->driver_list, &d->pcl_queue);
+
+ d->header_dma = pci_map_single(lynx->dev, packet->header,
+ packet->header_size, PCI_DMA_TODEVICE);
+ if (packet->data_size) {
+ d->data_dma = pci_map_single(lynx->dev, packet->data,
+ packet->data_size,
+ PCI_DMA_TODEVICE);
+ } else {
+ d->data_dma = 0;
+ }
+
+ pcl.next = PCL_NEXT_INVALID;
+ pcl.async_error_next = PCL_NEXT_INVALID;
+ pcl.pcl_status = 0;
+ pcl.buffer[0].control = packet->speed_code << 14 | packet->header_size;
+#ifndef __BIG_ENDIAN
+ pcl.buffer[0].control |= PCL_BIGENDIAN;
+#endif
+ pcl.buffer[0].pointer = d->header_dma;
+ pcl.buffer[1].control = PCL_LAST_BUFF | packet->data_size;
+ pcl.buffer[1].pointer = d->data_dma;
+
+ switch (packet->type) {
+ case hpsb_async:
+ pcl.buffer[0].control |= PCL_CMD_XMT;
+ break;
+#if 0 /* has been removed from ieee1394 core */
+ case hpsb_iso:
+ pcl.buffer[0].control |= PCL_CMD_XMT | PCL_ISOMODE;
+ break;
+#endif
+ case hpsb_raw:
+ pcl.buffer[0].control |= PCL_CMD_UNFXMT;
+ break;
+ }
+
+ put_pcl(lynx, d->pcl, &pcl);
+ run_pcl(lynx, d->pcl_start, d->channel);
+}
+
+
+/* called from subsystem core */
+static int lynx_transmit(struct hpsb_host *host, struct hpsb_packet *packet)
+{
+ struct ti_lynx *lynx = host->hostdata;
+ struct lynx_send_data *d;
+ unsigned long flags;
+
+ if (packet->data_size >= 4096) {
+ PRINT(KERN_ERR, lynx->id, "transmit packet data too big (%Zd)",
+ packet->data_size);
+ return -EOVERFLOW;
+ }
+
+ switch (packet->type) {
+ case hpsb_async:
+ case hpsb_raw:
+ d = &lynx->async;
+ break;
+#if 0 /* has been removed from ieee1394 core */
+ case hpsb_iso:
+ d = &lynx->iso_send;
+ break;
+#endif
+ default:
+ PRINT(KERN_ERR, lynx->id, "invalid packet type %d",
+ packet->type);
+ return -EINVAL;
+ }
+
+ if (packet->tcode == TCODE_WRITEQ
+ || packet->tcode == TCODE_READQ_RESPONSE) {
+ cpu_to_be32s(&packet->header[3]);
+ }
+
+ spin_lock_irqsave(&d->queue_lock, flags);
+
+ list_add_tail(&packet->driver_list, &d->queue);
+ if (list_empty(&d->pcl_queue))
+ send_next(lynx, packet->type);
+
+ spin_unlock_irqrestore(&d->queue_lock, flags);
+
+ return 0;
+}
+
+
+/* called from subsystem core */
+static int lynx_devctl(struct hpsb_host *host, enum devctl_cmd cmd, int arg)
+{
+ struct ti_lynx *lynx = host->hostdata;
+ int retval = 0;
+ struct hpsb_packet *packet;
+ LIST_HEAD(packet_list);
+ unsigned long flags;
+ int phy_reg;
+
+ switch (cmd) {
+ case RESET_BUS:
+ if (reg_read(lynx, LINK_INT_STATUS) & LINK_INT_PHY_BUSRESET) {
+ retval = 0;
+ break;
+ }
+
+ switch (arg) {
+ case SHORT_RESET:
+ if (lynx->phyic.reg_1394a) {
+ phy_reg = get_phy_reg(lynx, 5);
+ if (phy_reg == -1) {
+ PRINT(KERN_ERR, lynx->id, "cannot reset bus, because read phy reg failed");
+ retval = -1;
+ break;
+ }
+ phy_reg |= 0x40;
+
+ PRINT(KERN_INFO, lynx->id, "resetting bus (short bus reset) on request");
+
+ lynx->selfid_size = -1;
+ lynx->phy_reg0 = -1;
+ set_phy_reg(lynx, 5, phy_reg); /* set ISBR */
+ break;
+ } else {
+ PRINT(KERN_INFO, lynx->id, "cannot do short bus reset, because of old phy");
+ /* fall through to long bus reset */
+ }
+ case LONG_RESET:
+ phy_reg = get_phy_reg(lynx, 1);
+ if (phy_reg == -1) {
+ PRINT(KERN_ERR, lynx->id, "cannot reset bus, because read phy reg failed");
+ retval = -1;
+ break;
+ }
+ phy_reg |= 0x40;
+
+ PRINT(KERN_INFO, lynx->id, "resetting bus (long bus reset) on request");
+
+ lynx->selfid_size = -1;
+ lynx->phy_reg0 = -1;
+ set_phy_reg(lynx, 1, phy_reg); /* clear RHB, set IBR */
+ break;
+ case SHORT_RESET_NO_FORCE_ROOT:
+ if (lynx->phyic.reg_1394a) {
+ phy_reg = get_phy_reg(lynx, 1);
+ if (phy_reg == -1) {
+ PRINT(KERN_ERR, lynx->id, "cannot reset bus, because read phy reg failed");
+ retval = -1;
+ break;
+ }
+ if (phy_reg & 0x80) {
+ phy_reg &= ~0x80;
+ set_phy_reg(lynx, 1, phy_reg); /* clear RHB */
+ }
+
+ phy_reg = get_phy_reg(lynx, 5);
+ if (phy_reg == -1) {
+ PRINT(KERN_ERR, lynx->id, "cannot reset bus, because read phy reg failed");
+ retval = -1;
+ break;
+ }
+ phy_reg |= 0x40;
+
+ PRINT(KERN_INFO, lynx->id, "resetting bus (short bus reset, no force_root) on request");
+
+ lynx->selfid_size = -1;
+ lynx->phy_reg0 = -1;
+ set_phy_reg(lynx, 5, phy_reg); /* set ISBR */
+ break;
+ } else {
+ PRINT(KERN_INFO, lynx->id, "cannot do short bus reset, because of old phy");
+ /* fall through to long bus reset */
+ }
+ case LONG_RESET_NO_FORCE_ROOT:
+ phy_reg = get_phy_reg(lynx, 1);
+ if (phy_reg == -1) {
+ PRINT(KERN_ERR, lynx->id, "cannot reset bus, because read phy reg failed");
+ retval = -1;
+ break;
+ }
+ phy_reg &= ~0x80;
+ phy_reg |= 0x40;
+
+ PRINT(KERN_INFO, lynx->id, "resetting bus (long bus reset, no force_root) on request");
+
+ lynx->selfid_size = -1;
+ lynx->phy_reg0 = -1;
+ set_phy_reg(lynx, 1, phy_reg); /* clear RHB, set IBR */
+ break;
+ case SHORT_RESET_FORCE_ROOT:
+ if (lynx->phyic.reg_1394a) {
+ phy_reg = get_phy_reg(lynx, 1);
+ if (phy_reg == -1) {
+ PRINT(KERN_ERR, lynx->id, "cannot reset bus, because read phy reg failed");
+ retval = -1;
+ break;
+ }
+ if (!(phy_reg & 0x80)) {
+ phy_reg |= 0x80;
+ set_phy_reg(lynx, 1, phy_reg); /* set RHB */
+ }
+
+ phy_reg = get_phy_reg(lynx, 5);
+ if (phy_reg == -1) {
+ PRINT(KERN_ERR, lynx->id, "cannot reset bus, because read phy reg failed");
+ retval = -1;
+ break;
+ }
+ phy_reg |= 0x40;
+
+ PRINT(KERN_INFO, lynx->id, "resetting bus (short bus reset, force_root set) on request");
+
+ lynx->selfid_size = -1;
+ lynx->phy_reg0 = -1;
+ set_phy_reg(lynx, 5, phy_reg); /* set ISBR */
+ break;
+ } else {
+ PRINT(KERN_INFO, lynx->id, "cannot do short bus reset, because of old phy");
+ /* fall through to long bus reset */
+ }
+ case LONG_RESET_FORCE_ROOT:
+ phy_reg = get_phy_reg(lynx, 1);
+ if (phy_reg == -1) {
+ PRINT(KERN_ERR, lynx->id, "cannot reset bus, because read phy reg failed");
+ retval = -1;
+ break;
+ }
+ phy_reg |= 0xc0;
+
+ PRINT(KERN_INFO, lynx->id, "resetting bus (long bus reset, force_root set) on request");
+
+ lynx->selfid_size = -1;
+ lynx->phy_reg0 = -1;
+ set_phy_reg(lynx, 1, phy_reg); /* set IBR and RHB */
+ break;
+ default:
+ PRINT(KERN_ERR, lynx->id, "unknown argument for reset_bus command %d", arg);
+ retval = -1;
+ }
+
+ break;
+
+ case GET_CYCLE_COUNTER:
+ retval = reg_read(lynx, CYCLE_TIMER);
+ break;
+
+ case SET_CYCLE_COUNTER:
+ reg_write(lynx, CYCLE_TIMER, arg);
+ break;
+
+ case SET_BUS_ID:
+ reg_write(lynx, LINK_ID,
+ (arg << 22) | (reg_read(lynx, LINK_ID) & 0x003f0000));
+ break;
+
+ case ACT_CYCLE_MASTER:
+ if (arg) {
+ reg_set_bits(lynx, LINK_CONTROL,
+ LINK_CONTROL_CYCMASTER);
+ } else {
+ reg_clear_bits(lynx, LINK_CONTROL,
+ LINK_CONTROL_CYCMASTER);
+ }
+ break;
+
+ case CANCEL_REQUESTS:
+ spin_lock_irqsave(&lynx->async.queue_lock, flags);
+
+ reg_write(lynx, DMA_CHAN_CTRL(CHANNEL_ASYNC_SEND), 0);
+ list_splice_init(&lynx->async.queue, &packet_list);
+
+ if (list_empty(&lynx->async.pcl_queue)) {
+ spin_unlock_irqrestore(&lynx->async.queue_lock, flags);
+ PRINTD(KERN_DEBUG, lynx->id, "no async packet in PCL to cancel");
+ } else {
+ struct ti_pcl pcl;
+ u32 ack;
+
+ PRINT(KERN_INFO, lynx->id, "cancelling async packet, that was already in PCL");
+
+ get_pcl(lynx, lynx->async.pcl, &pcl);
+
+ packet = driver_packet(lynx->async.pcl_queue.next);
+ list_del_init(&packet->driver_list);
+
+ pci_unmap_single(lynx->dev, lynx->async.header_dma,
+ packet->header_size, PCI_DMA_TODEVICE);
+ if (packet->data_size) {
+ pci_unmap_single(lynx->dev, lynx->async.data_dma,
+ packet->data_size, PCI_DMA_TODEVICE);
+ }
+
+ spin_unlock_irqrestore(&lynx->async.queue_lock, flags);
+
+ if (pcl.pcl_status & DMA_CHAN_STAT_PKTCMPL) {
+ if (pcl.pcl_status & DMA_CHAN_STAT_SPECIALACK) {
+ ack = (pcl.pcl_status >> 15) & 0xf;
+ PRINTD(KERN_INFO, lynx->id, "special ack %d", ack);
+ ack = (ack == 1 ? ACKX_TIMEOUT : ACKX_SEND_ERROR);
+ } else {
+ ack = (pcl.pcl_status >> 15) & 0xf;
+ }
+ } else {
+ PRINT(KERN_INFO, lynx->id, "async packet was not completed");
+ ack = ACKX_ABORTED;
+ }
+ hpsb_packet_sent(host, packet, ack);
+ }
+
+ while (!list_empty(&packet_list)) {
+ packet = driver_packet(packet_list.next);
+ list_del_init(&packet->driver_list);
+ hpsb_packet_sent(host, packet, ACKX_ABORTED);
+ }
+
+ break;
+#if 0 /* has been removed from ieee1394 core */
+ case ISO_LISTEN_CHANNEL:
+ spin_lock_irqsave(&lynx->iso_rcv.lock, flags);
+
+ if (lynx->iso_rcv.chan_count++ == 0) {
+ reg_write(lynx, DMA_WORD1_CMP_ENABLE(CHANNEL_ISO_RCV),
+ DMA_WORD1_CMP_ENABLE_MASTER);
+ }
+
+ spin_unlock_irqrestore(&lynx->iso_rcv.lock, flags);
+ break;
+
+ case ISO_UNLISTEN_CHANNEL:
+ spin_lock_irqsave(&lynx->iso_rcv.lock, flags);
+
+ if (--lynx->iso_rcv.chan_count == 0) {
+ reg_write(lynx, DMA_WORD1_CMP_ENABLE(CHANNEL_ISO_RCV),
+ 0);
+ }
+
+ spin_unlock_irqrestore(&lynx->iso_rcv.lock, flags);
+ break;
+#endif
+ default:
+ PRINT(KERN_ERR, lynx->id, "unknown devctl command %d", cmd);
+ retval = -1;
+ }
+
+ return retval;
+}
+
+
+/***************************************
+ * IEEE-1394 functionality section END *
+ ***************************************/
+
+
+/********************************************************
+ * Global stuff (interrupt handler, init/shutdown code) *
+ ********************************************************/
+
+
+static irqreturn_t lynx_irq_handler(int irq, void *dev_id)
+{
+ struct ti_lynx *lynx = (struct ti_lynx *)dev_id;
+ struct hpsb_host *host = lynx->host;
+ u32 intmask;
+ u32 linkint;
+
+ linkint = reg_read(lynx, LINK_INT_STATUS);
+ intmask = reg_read(lynx, PCI_INT_STATUS);
+
+ if (!(intmask & PCI_INT_INT_PEND))
+ return IRQ_NONE;
+
+ PRINTD(KERN_DEBUG, lynx->id, "interrupt: 0x%08x / 0x%08x", intmask,
+ linkint);
+
+ reg_write(lynx, LINK_INT_STATUS, linkint);
+ reg_write(lynx, PCI_INT_STATUS, intmask);
+
+ if (intmask & PCI_INT_1394) {
+ if (linkint & LINK_INT_PHY_TIMEOUT) {
+ PRINT(KERN_INFO, lynx->id, "PHY timeout occurred");
+ }
+ if (linkint & LINK_INT_PHY_BUSRESET) {
+ PRINT(KERN_INFO, lynx->id, "bus reset interrupt");
+ lynx->selfid_size = -1;
+ lynx->phy_reg0 = -1;
+ if (!host->in_bus_reset)
+ hpsb_bus_reset(host);
+ }
+ if (linkint & LINK_INT_PHY_REG_RCVD) {
+ u32 reg;
+
+ spin_lock(&lynx->phy_reg_lock);
+ reg = reg_read(lynx, LINK_PHY);
+ spin_unlock(&lynx->phy_reg_lock);
+
+ if (!host->in_bus_reset) {
+ PRINT(KERN_INFO, lynx->id,
+ "phy reg received without reset");
+ } else if (reg & 0xf00) {
+ PRINT(KERN_INFO, lynx->id,
+ "unsolicited phy reg %d received",
+ (reg >> 8) & 0xf);
+ } else {
+ lynx->phy_reg0 = reg & 0xff;
+ handle_selfid(lynx, host);
+ }
+ }
+ if (linkint & LINK_INT_ISO_STUCK) {
+ PRINT(KERN_INFO, lynx->id, "isochronous transmitter stuck");
+ }
+ if (linkint & LINK_INT_ASYNC_STUCK) {
+ PRINT(KERN_INFO, lynx->id, "asynchronous transmitter stuck");
+ }
+ if (linkint & LINK_INT_SENT_REJECT) {
+ PRINT(KERN_INFO, lynx->id, "sent reject");
+ }
+ if (linkint & LINK_INT_TX_INVALID_TC) {
+ PRINT(KERN_INFO, lynx->id, "invalid transaction code");
+ }
+ if (linkint & LINK_INT_GRF_OVERFLOW) {
+ /* flush FIFO if overflow happens during reset */
+ if (host->in_bus_reset)
+ reg_write(lynx, FIFO_CONTROL,
+ FIFO_CONTROL_GRF_FLUSH);
+ PRINT(KERN_INFO, lynx->id, "GRF overflow");
+ }
+ if (linkint & LINK_INT_ITF_UNDERFLOW) {
+ PRINT(KERN_INFO, lynx->id, "ITF underflow");
+ }
+ if (linkint & LINK_INT_ATF_UNDERFLOW) {
+ PRINT(KERN_INFO, lynx->id, "ATF underflow");
+ }
+ }
+
+ if (intmask & PCI_INT_DMA_HLT(CHANNEL_ISO_RCV)) {
+ PRINTD(KERN_DEBUG, lynx->id, "iso receive");
+
+ spin_lock(&lynx->iso_rcv.lock);
+
+ lynx->iso_rcv.stat[lynx->iso_rcv.next] =
+ reg_read(lynx, DMA_CHAN_STAT(CHANNEL_ISO_RCV));
+
+ lynx->iso_rcv.used++;
+ lynx->iso_rcv.next = (lynx->iso_rcv.next + 1) % NUM_ISORCV_PCL;
+
+ if ((lynx->iso_rcv.next == lynx->iso_rcv.last)
+ || !lynx->iso_rcv.chan_count) {
+ PRINTD(KERN_DEBUG, lynx->id, "stopped");
+ reg_write(lynx, DMA_WORD1_CMP_ENABLE(CHANNEL_ISO_RCV), 0);
+ }
+
+ run_sub_pcl(lynx, lynx->iso_rcv.pcl_start, lynx->iso_rcv.next,
+ CHANNEL_ISO_RCV);
+
+ spin_unlock(&lynx->iso_rcv.lock);
+
+ tasklet_schedule(&lynx->iso_rcv.tq);
+ }
+
+ if (intmask & PCI_INT_DMA_HLT(CHANNEL_ASYNC_SEND)) {
+ PRINTD(KERN_DEBUG, lynx->id, "async sent");
+ spin_lock(&lynx->async.queue_lock);
+
+ if (list_empty(&lynx->async.pcl_queue)) {
+ spin_unlock(&lynx->async.queue_lock);
+ PRINT(KERN_WARNING, lynx->id, "async dma halted, but no queued packet (maybe it was cancelled)");
+ } else {
+ struct ti_pcl pcl;
+ u32 ack;
+ struct hpsb_packet *packet;
+
+ get_pcl(lynx, lynx->async.pcl, &pcl);
+
+ packet = driver_packet(lynx->async.pcl_queue.next);
+ list_del_init(&packet->driver_list);
+
+ pci_unmap_single(lynx->dev, lynx->async.header_dma,
+ packet->header_size, PCI_DMA_TODEVICE);
+ if (packet->data_size) {
+ pci_unmap_single(lynx->dev, lynx->async.data_dma,
+ packet->data_size, PCI_DMA_TODEVICE);
+ }
+
+ if (!list_empty(&lynx->async.queue)) {
+ send_next(lynx, hpsb_async);
+ }
+
+ spin_unlock(&lynx->async.queue_lock);
+
+ if (pcl.pcl_status & DMA_CHAN_STAT_PKTCMPL) {
+ if (pcl.pcl_status & DMA_CHAN_STAT_SPECIALACK) {
+ ack = (pcl.pcl_status >> 15) & 0xf;
+ PRINTD(KERN_INFO, lynx->id, "special ack %d", ack);
+ ack = (ack == 1 ? ACKX_TIMEOUT : ACKX_SEND_ERROR);
+ } else {
+ ack = (pcl.pcl_status >> 15) & 0xf;
+ }
+ } else {
+ PRINT(KERN_INFO, lynx->id, "async packet was not completed");
+ ack = ACKX_SEND_ERROR;
+ }
+ hpsb_packet_sent(host, packet, ack);
+ }
+ }
+
+ if (intmask & PCI_INT_DMA_HLT(CHANNEL_ISO_SEND)) {
+ PRINTD(KERN_DEBUG, lynx->id, "iso sent");
+ spin_lock(&lynx->iso_send.queue_lock);
+
+ if (list_empty(&lynx->iso_send.pcl_queue)) {
+ spin_unlock(&lynx->iso_send.queue_lock);
+ PRINT(KERN_ERR, lynx->id, "iso send dma halted, but no queued packet");
+ } else {
+ struct ti_pcl pcl;
+ u32 ack;
+ struct hpsb_packet *packet;
+
+ get_pcl(lynx, lynx->iso_send.pcl, &pcl);
+
+ packet = driver_packet(lynx->iso_send.pcl_queue.next);
+ list_del_init(&packet->driver_list);
+
+ pci_unmap_single(lynx->dev, lynx->iso_send.header_dma,
+ packet->header_size, PCI_DMA_TODEVICE);
+ if (packet->data_size) {
+ pci_unmap_single(lynx->dev, lynx->iso_send.data_dma,
+ packet->data_size, PCI_DMA_TODEVICE);
+ }
+#if 0 /* has been removed from ieee1394 core */
+ if (!list_empty(&lynx->iso_send.queue)) {
+ send_next(lynx, hpsb_iso);
+ }
+#endif
+ spin_unlock(&lynx->iso_send.queue_lock);
+
+ if (pcl.pcl_status & DMA_CHAN_STAT_PKTCMPL) {
+ if (pcl.pcl_status & DMA_CHAN_STAT_SPECIALACK) {
+ ack = (pcl.pcl_status >> 15) & 0xf;
+ PRINTD(KERN_INFO, lynx->id, "special ack %d", ack);
+ ack = (ack == 1 ? ACKX_TIMEOUT : ACKX_SEND_ERROR);
+ } else {
+ ack = (pcl.pcl_status >> 15) & 0xf;
+ }
+ } else {
+ PRINT(KERN_INFO, lynx->id, "iso send packet was not completed");
+ ack = ACKX_SEND_ERROR;
+ }
+
+ hpsb_packet_sent(host, packet, ack); //FIXME: maybe we should just use ACK_COMPLETE and ACKX_SEND_ERROR
+ }
+ }
+
+ if (intmask & PCI_INT_DMA_HLT(CHANNEL_ASYNC_RCV)) {
+ /* general receive DMA completed */
+ int stat = reg_read(lynx, DMA_CHAN_STAT(CHANNEL_ASYNC_RCV));
+
+ PRINTD(KERN_DEBUG, lynx->id, "received packet size %d",
+ stat & 0x1fff);
+
+ if (stat & DMA_CHAN_STAT_SELFID) {
+ lynx->selfid_size = stat & 0x1fff;
+ handle_selfid(lynx, host);
+ } else {
+ quadlet_t *q_data = lynx->rcv_page;
+ if ((*q_data >> 4 & 0xf) == TCODE_READQ_RESPONSE
+ || (*q_data >> 4 & 0xf) == TCODE_WRITEQ) {
+ cpu_to_be32s(q_data + 3);
+ }
+ hpsb_packet_received(host, q_data, stat & 0x1fff, 0);
+ }
+
+ run_pcl(lynx, lynx->rcv_pcl_start, CHANNEL_ASYNC_RCV);
+ }
+
+ return IRQ_HANDLED;
+}
+
+
+static void iso_rcv_bh(struct ti_lynx *lynx)
+{
+ unsigned int idx;
+ quadlet_t *data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&lynx->iso_rcv.lock, flags);
+
+ while (lynx->iso_rcv.used) {
+ idx = lynx->iso_rcv.last;
+ spin_unlock_irqrestore(&lynx->iso_rcv.lock, flags);
+
+ data = lynx->iso_rcv.page[idx / ISORCV_PER_PAGE]
+ + (idx % ISORCV_PER_PAGE) * MAX_ISORCV_SIZE;
+
+ if ((*data >> 16) + 4 != (lynx->iso_rcv.stat[idx] & 0x1fff)) {
+ PRINT(KERN_ERR, lynx->id,
+ "iso length mismatch 0x%08x/0x%08x", *data,
+ lynx->iso_rcv.stat[idx]);
+ }
+
+ if (lynx->iso_rcv.stat[idx]
+ & (DMA_CHAN_STAT_PCIERR | DMA_CHAN_STAT_PKTERR)) {
+ PRINT(KERN_INFO, lynx->id,
+ "iso receive error on %d to 0x%p", idx, data);
+ } else {
+ hpsb_packet_received(lynx->host, data,
+ lynx->iso_rcv.stat[idx] & 0x1fff,
+ 0);
+ }
+
+ spin_lock_irqsave(&lynx->iso_rcv.lock, flags);
+ lynx->iso_rcv.last = (idx + 1) % NUM_ISORCV_PCL;
+ lynx->iso_rcv.used--;
+ }
+
+ if (lynx->iso_rcv.chan_count) {
+ reg_write(lynx, DMA_WORD1_CMP_ENABLE(CHANNEL_ISO_RCV),
+ DMA_WORD1_CMP_ENABLE_MASTER);
+ }
+ spin_unlock_irqrestore(&lynx->iso_rcv.lock, flags);
+}
+
+
+static void remove_card(struct pci_dev *dev)
+{
+ struct ti_lynx *lynx;
+ struct device *lynx_dev;
+ int i;
+
+ lynx = pci_get_drvdata(dev);
+ if (!lynx) return;
+ pci_set_drvdata(dev, NULL);
+
+ lynx_dev = get_device(&lynx->host->device);
+
+ switch (lynx->state) {
+ case is_host:
+ reg_write(lynx, PCI_INT_ENABLE, 0);
+ hpsb_remove_host(lynx->host);
+ case have_intr:
+ reg_write(lynx, PCI_INT_ENABLE, 0);
+ free_irq(lynx->dev->irq, lynx);
+
+ /* Disable IRM Contender and LCtrl */
+ if (lynx->phyic.reg_1394a)
+ set_phy_reg(lynx, 4, ~0xc0 & get_phy_reg(lynx, 4));
+
+ /* Let all other nodes know to ignore us */
+ lynx_devctl(lynx->host, RESET_BUS, LONG_RESET_NO_FORCE_ROOT);
+
+ case have_iomappings:
+ reg_set_bits(lynx, MISC_CONTROL, MISC_CONTROL_SWRESET);
+ /* Fix buggy cards with autoboot pin not tied low: */
+ reg_write(lynx, DMA0_CHAN_CTRL, 0);
+ iounmap(lynx->registers);
+ iounmap(lynx->local_rom);
+ iounmap(lynx->local_ram);
+ iounmap(lynx->aux_port);
+ case have_1394_buffers:
+ for (i = 0; i < ISORCV_PAGES; i++) {
+ if (lynx->iso_rcv.page[i]) {
+ pci_free_consistent(lynx->dev, PAGE_SIZE,
+ lynx->iso_rcv.page[i],
+ lynx->iso_rcv.page_dma[i]);
+ }
+ }
+ pci_free_consistent(lynx->dev, PAGE_SIZE, lynx->rcv_page,
+ lynx->rcv_page_dma);
+ case have_aux_buf:
+ case have_pcl_mem:
+ pci_free_consistent(lynx->dev, LOCALRAM_SIZE, lynx->pcl_mem,
+ lynx->pcl_mem_dma);
+ case clear:
+ /* do nothing - already freed */
+ ;
+ }
+
+ tasklet_kill(&lynx->iso_rcv.tq);
+
+ if (lynx_dev)
+ put_device(lynx_dev);
+}
+
+
+static int __devinit add_card(struct pci_dev *dev,
+ const struct pci_device_id *devid_is_unused)
+{
+#define FAIL(fmt, args...) do { \
+ PRINT_G(KERN_ERR, fmt , ## args); \
+ remove_card(dev); \
+ return error; \
+ } while (0)
+
+ char irq_buf[16];
+ struct hpsb_host *host;
+ struct ti_lynx *lynx; /* shortcut to currently handled device */
+ struct ti_pcl pcl;
+ u32 *pcli;
+ int i;
+ int error;
+
+ error = -ENXIO;
+
+ if (pci_set_dma_mask(dev, DMA_32BIT_MASK))
+ FAIL("DMA address limits not supported for PCILynx hardware");
+ if (pci_enable_device(dev))
+ FAIL("failed to enable PCILynx hardware");
+ pci_set_master(dev);
+
+ error = -ENOMEM;
+
+ host = hpsb_alloc_host(&lynx_driver, sizeof(struct ti_lynx), &dev->dev);
+ if (!host) FAIL("failed to allocate control structure memory");
+
+ lynx = host->hostdata;
+ lynx->id = card_id++;
+ lynx->dev = dev;
+ lynx->state = clear;
+ lynx->host = host;
+ host->pdev = dev;
+ pci_set_drvdata(dev, lynx);
+
+ spin_lock_init(&lynx->lock);
+ spin_lock_init(&lynx->phy_reg_lock);
+
+ lynx->pcl_mem = pci_alloc_consistent(dev, LOCALRAM_SIZE,
+ &lynx->pcl_mem_dma);
+
+ if (lynx->pcl_mem != NULL) {
+ lynx->state = have_pcl_mem;
+ PRINT(KERN_INFO, lynx->id,
+ "allocated PCL memory %d Bytes @ 0x%p", LOCALRAM_SIZE,
+ lynx->pcl_mem);
+ } else {
+ FAIL("failed to allocate PCL memory area");
+ }
+
+ lynx->rcv_page = pci_alloc_consistent(dev, PAGE_SIZE,
+ &lynx->rcv_page_dma);
+ if (lynx->rcv_page == NULL) {
+ FAIL("failed to allocate receive buffer");
+ }
+ lynx->state = have_1394_buffers;
+
+ for (i = 0; i < ISORCV_PAGES; i++) {
+ lynx->iso_rcv.page[i] =
+ pci_alloc_consistent(dev, PAGE_SIZE,
+ &lynx->iso_rcv.page_dma[i]);
+ if (lynx->iso_rcv.page[i] == NULL) {
+ FAIL("failed to allocate iso receive buffers");
+ }
+ }
+
+ lynx->registers = ioremap_nocache(pci_resource_start(dev,0),
+ PCILYNX_MAX_REGISTER);
+ lynx->local_ram = ioremap(pci_resource_start(dev,1), PCILYNX_MAX_MEMORY);
+ lynx->aux_port = ioremap(pci_resource_start(dev,2), PCILYNX_MAX_MEMORY);
+ lynx->local_rom = ioremap(pci_resource_start(dev,PCI_ROM_RESOURCE),
+ PCILYNX_MAX_MEMORY);
+ lynx->state = have_iomappings;
+
+ if (lynx->registers == NULL) {
+ FAIL("failed to remap registers - card not accessible");
+ }
+
+ reg_set_bits(lynx, MISC_CONTROL, MISC_CONTROL_SWRESET);
+ /* Fix buggy cards with autoboot pin not tied low: */
+ reg_write(lynx, DMA0_CHAN_CTRL, 0);
+
+ sprintf (irq_buf, "%d", dev->irq);
+
+ if (!request_irq(dev->irq, lynx_irq_handler, IRQF_SHARED,
+ PCILYNX_DRIVER_NAME, lynx)) {
+ PRINT(KERN_INFO, lynx->id, "allocated interrupt %s", irq_buf);
+ lynx->state = have_intr;
+ } else {
+ FAIL("failed to allocate shared interrupt %s", irq_buf);
+ }
+
+ /* alloc_pcl return values are not checked, it is expected that the
+ * provided PCL space is sufficient for the initial allocations */
+ lynx->rcv_pcl = alloc_pcl(lynx);
+ lynx->rcv_pcl_start = alloc_pcl(lynx);
+ lynx->async.pcl = alloc_pcl(lynx);
+ lynx->async.pcl_start = alloc_pcl(lynx);
+ lynx->iso_send.pcl = alloc_pcl(lynx);
+ lynx->iso_send.pcl_start = alloc_pcl(lynx);
+
+ for (i = 0; i < NUM_ISORCV_PCL; i++) {
+ lynx->iso_rcv.pcl[i] = alloc_pcl(lynx);
+ }
+ lynx->iso_rcv.pcl_start = alloc_pcl(lynx);
+
+ /* all allocations successful - simple init stuff follows */
+
+ reg_write(lynx, PCI_INT_ENABLE, PCI_INT_DMA_ALL);
+
+ tasklet_init(&lynx->iso_rcv.tq, (void (*)(unsigned long))iso_rcv_bh,
+ (unsigned long)lynx);
+
+ spin_lock_init(&lynx->iso_rcv.lock);
+
+ spin_lock_init(&lynx->async.queue_lock);
+ lynx->async.channel = CHANNEL_ASYNC_SEND;
+ spin_lock_init(&lynx->iso_send.queue_lock);
+ lynx->iso_send.channel = CHANNEL_ISO_SEND;
+
+ PRINT(KERN_INFO, lynx->id, "remapped memory spaces reg 0x%p, rom 0x%p, "
+ "ram 0x%p, aux 0x%p", lynx->registers, lynx->local_rom,
+ lynx->local_ram, lynx->aux_port);
+
+ /* now, looking for PHY register set */
+ if ((get_phy_reg(lynx, 2) & 0xe0) == 0xe0) {
+ lynx->phyic.reg_1394a = 1;
+ PRINT(KERN_INFO, lynx->id,
+ "found 1394a conform PHY (using extended register set)");
+ lynx->phyic.vendor = get_phy_vendorid(lynx);
+ lynx->phyic.product = get_phy_productid(lynx);
+ } else {
+ lynx->phyic.reg_1394a = 0;
+ PRINT(KERN_INFO, lynx->id, "found old 1394 PHY");
+ }
+
+ lynx->selfid_size = -1;
+ lynx->phy_reg0 = -1;
+
+ INIT_LIST_HEAD(&lynx->async.queue);
+ INIT_LIST_HEAD(&lynx->async.pcl_queue);
+ INIT_LIST_HEAD(&lynx->iso_send.queue);
+ INIT_LIST_HEAD(&lynx->iso_send.pcl_queue);
+
+ pcl.next = pcl_bus(lynx, lynx->rcv_pcl);
+ put_pcl(lynx, lynx->rcv_pcl_start, &pcl);
+
+ pcl.next = PCL_NEXT_INVALID;
+ pcl.async_error_next = PCL_NEXT_INVALID;
+
+ pcl.buffer[0].control = PCL_CMD_RCV | 16;
+#ifndef __BIG_ENDIAN
+ pcl.buffer[0].control |= PCL_BIGENDIAN;
+#endif
+ pcl.buffer[1].control = PCL_LAST_BUFF | 4080;
+
+ pcl.buffer[0].pointer = lynx->rcv_page_dma;
+ pcl.buffer[1].pointer = lynx->rcv_page_dma + 16;
+ put_pcl(lynx, lynx->rcv_pcl, &pcl);
+
+ pcl.next = pcl_bus(lynx, lynx->async.pcl);
+ pcl.async_error_next = pcl_bus(lynx, lynx->async.pcl);
+ put_pcl(lynx, lynx->async.pcl_start, &pcl);
+
+ pcl.next = pcl_bus(lynx, lynx->iso_send.pcl);
+ pcl.async_error_next = PCL_NEXT_INVALID;
+ put_pcl(lynx, lynx->iso_send.pcl_start, &pcl);
+
+ pcl.next = PCL_NEXT_INVALID;
+ pcl.async_error_next = PCL_NEXT_INVALID;
+ pcl.buffer[0].control = PCL_CMD_RCV | 4;
+#ifndef __BIG_ENDIAN
+ pcl.buffer[0].control |= PCL_BIGENDIAN;
+#endif
+ pcl.buffer[1].control = PCL_LAST_BUFF | 2044;
+
+ for (i = 0; i < NUM_ISORCV_PCL; i++) {
+ int page = i / ISORCV_PER_PAGE;
+ int sec = i % ISORCV_PER_PAGE;
+
+ pcl.buffer[0].pointer = lynx->iso_rcv.page_dma[page]
+ + sec * MAX_ISORCV_SIZE;
+ pcl.buffer[1].pointer = pcl.buffer[0].pointer + 4;
+ put_pcl(lynx, lynx->iso_rcv.pcl[i], &pcl);
+ }
+
+ pcli = (u32 *)&pcl;
+ for (i = 0; i < NUM_ISORCV_PCL; i++) {
+ pcli[i] = pcl_bus(lynx, lynx->iso_rcv.pcl[i]);
+ }
+ put_pcl(lynx, lynx->iso_rcv.pcl_start, &pcl);
+
+ /* FIFO sizes from left to right: ITF=48 ATF=48 GRF=160 */
+ reg_write(lynx, FIFO_SIZES, 0x003030a0);
+ /* 20 byte threshold before triggering PCI transfer */
+ reg_write(lynx, DMA_GLOBAL_REGISTER, 0x2<<24);
+ /* threshold on both send FIFOs before transmitting:
+ FIFO size - cache line size - 1 */
+ i = reg_read(lynx, PCI_LATENCY_CACHELINE) & 0xff;
+ i = 0x30 - i - 1;
+ reg_write(lynx, FIFO_XMIT_THRESHOLD, (i << 8) | i);
+
+ reg_set_bits(lynx, PCI_INT_ENABLE, PCI_INT_1394);
+
+ reg_write(lynx, LINK_INT_ENABLE, LINK_INT_PHY_TIMEOUT
+ | LINK_INT_PHY_REG_RCVD | LINK_INT_PHY_BUSRESET
+ | LINK_INT_ISO_STUCK | LINK_INT_ASYNC_STUCK
+ | LINK_INT_SENT_REJECT | LINK_INT_TX_INVALID_TC
+ | LINK_INT_GRF_OVERFLOW | LINK_INT_ITF_UNDERFLOW
+ | LINK_INT_ATF_UNDERFLOW);
+
+ reg_write(lynx, DMA_WORD0_CMP_VALUE(CHANNEL_ASYNC_RCV), 0);
+ reg_write(lynx, DMA_WORD0_CMP_ENABLE(CHANNEL_ASYNC_RCV), 0xa<<4);
+ reg_write(lynx, DMA_WORD1_CMP_VALUE(CHANNEL_ASYNC_RCV), 0);
+ reg_write(lynx, DMA_WORD1_CMP_ENABLE(CHANNEL_ASYNC_RCV),
+ DMA_WORD1_CMP_MATCH_LOCAL_NODE | DMA_WORD1_CMP_MATCH_BROADCAST
+ | DMA_WORD1_CMP_MATCH_EXACT | DMA_WORD1_CMP_MATCH_BUS_BCAST
+ | DMA_WORD1_CMP_ENABLE_SELF_ID | DMA_WORD1_CMP_ENABLE_MASTER);
+
+ run_pcl(lynx, lynx->rcv_pcl_start, CHANNEL_ASYNC_RCV);
+
+ reg_write(lynx, DMA_WORD0_CMP_VALUE(CHANNEL_ISO_RCV), 0);
+ reg_write(lynx, DMA_WORD0_CMP_ENABLE(CHANNEL_ISO_RCV), 0x9<<4);
+ reg_write(lynx, DMA_WORD1_CMP_VALUE(CHANNEL_ISO_RCV), 0);
+ reg_write(lynx, DMA_WORD1_CMP_ENABLE(CHANNEL_ISO_RCV), 0);
+
+ run_sub_pcl(lynx, lynx->iso_rcv.pcl_start, 0, CHANNEL_ISO_RCV);
+
+ reg_write(lynx, LINK_CONTROL, LINK_CONTROL_RCV_CMP_VALID
+ | LINK_CONTROL_TX_ISO_EN | LINK_CONTROL_RX_ISO_EN
+ | LINK_CONTROL_TX_ASYNC_EN | LINK_CONTROL_RX_ASYNC_EN
+ | LINK_CONTROL_RESET_TX | LINK_CONTROL_RESET_RX);
+
+ if (!lynx->phyic.reg_1394a) {
+ if (!hpsb_disable_irm) {
+ /* attempt to enable contender bit -FIXME- would this
+ * work elsewhere? */
+ reg_set_bits(lynx, GPIO_CTRL_A, 0x1);
+ reg_write(lynx, GPIO_DATA_BASE + 0x3c, 0x1);
+ }
+ } else {
+ /* set the contender (if appropriate) and LCtrl bit in the
+ * extended PHY register set. (Should check that PHY_02_EXTENDED
+ * is set in register 2?)
+ */
+ i = get_phy_reg(lynx, 4);
+ i |= PHY_04_LCTRL;
+ if (hpsb_disable_irm)
+ i &= ~PHY_04_CONTENDER;
+ else
+ i |= PHY_04_CONTENDER;
+ if (i != -1) set_phy_reg(lynx, 4, i);
+ }
+
+ if (!skip_eeprom)
+ {
+ /* needed for i2c communication with serial eeprom */
+ struct i2c_adapter *i2c_ad;
+ struct i2c_algo_bit_data i2c_adapter_data;
+
+ error = -ENOMEM;
+ i2c_ad = kzalloc(sizeof(*i2c_ad), GFP_KERNEL);
+ if (!i2c_ad) FAIL("failed to allocate I2C adapter memory");
+
+ i2c_ad->id = I2C_HW_B_PCILYNX;
+ strlcpy(i2c_ad->name, "PCILynx I2C", sizeof(i2c_ad->name));
+ i2c_adapter_data = bit_data;
+ i2c_ad->algo_data = &i2c_adapter_data;
+ i2c_adapter_data.data = lynx;
+ i2c_ad->dev.parent = &dev->dev;
+
+ PRINTD(KERN_DEBUG, lynx->id,"original eeprom control: %d",
+ reg_read(lynx, SERIAL_EEPROM_CONTROL));
+
+ /* reset hardware to sane state */
+ lynx->i2c_driven_state = 0x00000070;
+ reg_write(lynx, SERIAL_EEPROM_CONTROL, lynx->i2c_driven_state);
+
+ if (i2c_bit_add_bus(i2c_ad) < 0)
+ {
+ kfree(i2c_ad);
+ error = -ENXIO;
+ FAIL("unable to register i2c");
+ }
+ else
+ {
+ /* do i2c stuff */
+ unsigned char i2c_cmd = 0x10;
+ struct i2c_msg msg[2] = { { 0x50, 0, 1, &i2c_cmd },
+ { 0x50, I2C_M_RD, 20, (unsigned char*) lynx->bus_info_block }
+ };
+
+ /* we use i2c_transfer because we have no i2c_client
+ at hand */
+ if (i2c_transfer(i2c_ad, msg, 2) < 0) {
+ PRINT(KERN_ERR, lynx->id, "unable to read bus info block from i2c");
+ } else {
+ PRINT(KERN_INFO, lynx->id, "got bus info block from serial eeprom");
+ /* FIXME: probably we shoud rewrite the max_rec, max_ROM(1394a),
+ * generation(1394a) and link_spd(1394a) field and recalculate
+ * the CRC */
+
+ for (i = 0; i < 5 ; i++)
+ PRINTD(KERN_DEBUG, lynx->id, "Businfo block quadlet %i: %08x",
+ i, be32_to_cpu(lynx->bus_info_block[i]));
+
+ /* info_length, crc_length and 1394 magic number to check, if it is really a bus info block */
+ if (((be32_to_cpu(lynx->bus_info_block[0]) & 0xffff0000) == 0x04040000) &&
+ (lynx->bus_info_block[1] == __constant_cpu_to_be32(0x31333934)))
+ {
+ PRINT(KERN_DEBUG, lynx->id, "read a valid bus info block from");
+ } else {
+ kfree(i2c_ad);
+ error = -ENXIO;
+ FAIL("read something from serial eeprom, but it does not seem to be a valid bus info block");
+ }
+
+ }
+
+ i2c_del_adapter(i2c_ad);
+ kfree(i2c_ad);
+ }
+ }
+
+ host->csr.guid_hi = be32_to_cpu(lynx->bus_info_block[3]);
+ host->csr.guid_lo = be32_to_cpu(lynx->bus_info_block[4]);
+ host->csr.cyc_clk_acc = (be32_to_cpu(lynx->bus_info_block[2]) >> 16) & 0xff;
+ host->csr.max_rec = (be32_to_cpu(lynx->bus_info_block[2]) >> 12) & 0xf;
+ if (!lynx->phyic.reg_1394a)
+ host->csr.lnk_spd = (get_phy_reg(lynx, 2) & 0xc0) >> 6;
+ else
+ host->csr.lnk_spd = be32_to_cpu(lynx->bus_info_block[2]) & 0x7;
+
+ if (hpsb_add_host(host)) {
+ error = -ENOMEM;
+ FAIL("Failed to register host with highlevel");
+ }
+
+ lynx->state = is_host;
+
+ return 0;
+#undef FAIL
+}
+
+
+static struct pci_device_id pci_table[] = {
+ {
+ .vendor = PCI_VENDOR_ID_TI,
+ .device = PCI_DEVICE_ID_TI_PCILYNX,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ },
+ { } /* Terminating entry */
+};
+
+static struct pci_driver lynx_pci_driver = {
+ .name = PCILYNX_DRIVER_NAME,
+ .id_table = pci_table,
+ .probe = add_card,
+ .remove = remove_card,
+};
+
+static struct hpsb_host_driver lynx_driver = {
+ .owner = THIS_MODULE,
+ .name = PCILYNX_DRIVER_NAME,
+ .set_hw_config_rom = NULL,
+ .transmit_packet = lynx_transmit,
+ .devctl = lynx_devctl,
+ .isoctl = NULL,
+};
+
+MODULE_AUTHOR("Andreas E. Bombe <andreas.bombe@munich.netsurf.de>");
+MODULE_DESCRIPTION("driver for Texas Instruments PCI Lynx IEEE-1394 controller");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("pcilynx");
+MODULE_DEVICE_TABLE(pci, pci_table);
+
+static int __init pcilynx_init(void)
+{
+ int ret;
+
+ ret = pci_register_driver(&lynx_pci_driver);
+ if (ret < 0) {
+ PRINT_G(KERN_ERR, "PCI module init failed");
+ return ret;
+ }
+
+ return 0;
+}
+
+static void __exit pcilynx_cleanup(void)
+{
+ pci_unregister_driver(&lynx_pci_driver);
+}
+
+
+module_init(pcilynx_init);
+module_exit(pcilynx_cleanup);
diff --git a/drivers/ieee1394/pcilynx.h b/drivers/ieee1394/pcilynx.h
new file mode 100644
index 0000000..ec27321
--- /dev/null
+++ b/drivers/ieee1394/pcilynx.h
@@ -0,0 +1,468 @@
+#ifndef __PCILYNX_H__
+#define __PCILYNX_H__
+
+
+#define PCILYNX_DRIVER_NAME "pcilynx"
+#define PCILYNX_MAJOR 177
+
+#define PCILYNX_MINOR_AUX_START 0
+#define PCILYNX_MINOR_ROM_START 16
+#define PCILYNX_MINOR_RAM_START 32
+
+#define PCILYNX_MAX_REGISTER 0xfff
+#define PCILYNX_MAX_MEMORY 0xffff
+
+#define PCI_DEVICE_ID_TI_PCILYNX 0x8000
+#define MAX_PCILYNX_CARDS 4
+#define LOCALRAM_SIZE 4096
+
+#define NUM_ISORCV_PCL 4
+#define MAX_ISORCV_SIZE 2048
+#define ISORCV_PER_PAGE (PAGE_SIZE / MAX_ISORCV_SIZE)
+#define ISORCV_PAGES (NUM_ISORCV_PCL / ISORCV_PER_PAGE)
+
+#define CHANNEL_LOCALBUS 0
+#define CHANNEL_ASYNC_RCV 1
+#define CHANNEL_ISO_RCV 2
+#define CHANNEL_ASYNC_SEND 3
+#define CHANNEL_ISO_SEND 4
+
+#define PCILYNX_CONFIG_ROM_LENGTH 1024
+
+typedef int pcl_t;
+
+struct ti_lynx {
+ int id; /* sequential card number */
+
+ spinlock_t lock;
+
+ struct pci_dev *dev;
+
+ struct {
+ unsigned reg_1394a:1;
+ u32 vendor;
+ u32 product;
+ } phyic;
+
+ enum { clear, have_intr, have_aux_buf, have_pcl_mem,
+ have_1394_buffers, have_iomappings, is_host } state;
+
+ /* remapped memory spaces */
+ void __iomem *registers;
+ void __iomem *local_rom;
+ void __iomem *local_ram;
+ void __iomem *aux_port;
+ quadlet_t bus_info_block[5];
+
+ /*
+ * use local RAM of LOCALRAM_SIZE bytes for PCLs, which allows for
+ * LOCALRAM_SIZE * 8 PCLs (each sized 128 bytes);
+ * the following is an allocation bitmap
+ */
+ u8 pcl_bmap[LOCALRAM_SIZE / 1024];
+
+ /* point to PCLs memory area if needed */
+ void *pcl_mem;
+ dma_addr_t pcl_mem_dma;
+
+ /* PCLs for local mem / aux transfers */
+ pcl_t dmem_pcl;
+
+ /* IEEE-1394 part follows */
+ struct hpsb_host *host;
+
+ int phyid, isroot;
+ int selfid_size;
+ int phy_reg0;
+
+ spinlock_t phy_reg_lock;
+
+ pcl_t rcv_pcl_start, rcv_pcl;
+ void *rcv_page;
+ dma_addr_t rcv_page_dma;
+ int rcv_active;
+
+ struct lynx_send_data {
+ pcl_t pcl_start, pcl;
+ struct list_head queue;
+ struct list_head pcl_queue; /* this queue contains at most one packet */
+ spinlock_t queue_lock;
+ dma_addr_t header_dma, data_dma;
+ int channel;
+ } async, iso_send;
+
+ struct {
+ pcl_t pcl[NUM_ISORCV_PCL];
+ u32 stat[NUM_ISORCV_PCL];
+ void *page[ISORCV_PAGES];
+ dma_addr_t page_dma[ISORCV_PAGES];
+ pcl_t pcl_start;
+ int chan_count;
+ int next, last, used, running;
+ struct tasklet_struct tq;
+ spinlock_t lock;
+ } iso_rcv;
+
+ u32 i2c_driven_state; /* the state we currently drive the Serial EEPROM Control register */
+};
+
+/* the per-file data structure for mem space access */
+struct memdata {
+ struct ti_lynx *lynx;
+ int cid;
+ atomic_t aux_intr_last_seen;
+ /* enum values are the same as LBUS_ADDR_SEL_* values below */
+ enum { rom = 0x10000, aux = 0x20000, ram = 0 } type;
+};
+
+
+
+/*
+ * Register read and write helper functions.
+ */
+static inline void reg_write(const struct ti_lynx *lynx, int offset, u32 data)
+{
+ writel(data, lynx->registers + offset);
+}
+
+static inline u32 reg_read(const struct ti_lynx *lynx, int offset)
+{
+ return readl(lynx->registers + offset);
+}
+
+static inline void reg_set_bits(const struct ti_lynx *lynx, int offset,
+ u32 mask)
+{
+ reg_write(lynx, offset, (reg_read(lynx, offset) | mask));
+}
+
+static inline void reg_clear_bits(const struct ti_lynx *lynx, int offset,
+ u32 mask)
+{
+ reg_write(lynx, offset, (reg_read(lynx, offset) & ~mask));
+}
+
+
+
+/* chip register definitions follow */
+
+#define PCI_LATENCY_CACHELINE 0x0c
+
+#define MISC_CONTROL 0x40
+#define MISC_CONTROL_SWRESET (1<<0)
+
+#define SERIAL_EEPROM_CONTROL 0x44
+
+#define PCI_INT_STATUS 0x48
+#define PCI_INT_ENABLE 0x4c
+/* status and enable have identical bit numbers */
+#define PCI_INT_INT_PEND (1<<31)
+#define PCI_INT_FORCED_INT (1<<30)
+#define PCI_INT_SLV_ADR_PERR (1<<28)
+#define PCI_INT_SLV_DAT_PERR (1<<27)
+#define PCI_INT_MST_DAT_PERR (1<<26)
+#define PCI_INT_MST_DEV_TIMEOUT (1<<25)
+#define PCI_INT_INTERNAL_SLV_TIMEOUT (1<<23)
+#define PCI_INT_AUX_TIMEOUT (1<<18)
+#define PCI_INT_AUX_INT (1<<17)
+#define PCI_INT_1394 (1<<16)
+#define PCI_INT_DMA4_PCL (1<<9)
+#define PCI_INT_DMA4_HLT (1<<8)
+#define PCI_INT_DMA3_PCL (1<<7)
+#define PCI_INT_DMA3_HLT (1<<6)
+#define PCI_INT_DMA2_PCL (1<<5)
+#define PCI_INT_DMA2_HLT (1<<4)
+#define PCI_INT_DMA1_PCL (1<<3)
+#define PCI_INT_DMA1_HLT (1<<2)
+#define PCI_INT_DMA0_PCL (1<<1)
+#define PCI_INT_DMA0_HLT (1<<0)
+/* all DMA interrupts combined: */
+#define PCI_INT_DMA_ALL 0x3ff
+
+#define PCI_INT_DMA_HLT(chan) (1 << (chan * 2))
+#define PCI_INT_DMA_PCL(chan) (1 << (chan * 2 + 1))
+
+#define LBUS_ADDR 0xb4
+#define LBUS_ADDR_SEL_RAM (0x0<<16)
+#define LBUS_ADDR_SEL_ROM (0x1<<16)
+#define LBUS_ADDR_SEL_AUX (0x2<<16)
+#define LBUS_ADDR_SEL_ZV (0x3<<16)
+
+#define GPIO_CTRL_A 0xb8
+#define GPIO_CTRL_B 0xbc
+#define GPIO_DATA_BASE 0xc0
+
+#define DMA_BREG(base, chan) (base + chan * 0x20)
+#define DMA_SREG(base, chan) (base + chan * 0x10)
+
+#define DMA0_PREV_PCL 0x100
+#define DMA1_PREV_PCL 0x120
+#define DMA2_PREV_PCL 0x140
+#define DMA3_PREV_PCL 0x160
+#define DMA4_PREV_PCL 0x180
+#define DMA_PREV_PCL(chan) (DMA_BREG(DMA0_PREV_PCL, chan))
+
+#define DMA0_CURRENT_PCL 0x104
+#define DMA1_CURRENT_PCL 0x124
+#define DMA2_CURRENT_PCL 0x144
+#define DMA3_CURRENT_PCL 0x164
+#define DMA4_CURRENT_PCL 0x184
+#define DMA_CURRENT_PCL(chan) (DMA_BREG(DMA0_CURRENT_PCL, chan))
+
+#define DMA0_CHAN_STAT 0x10c
+#define DMA1_CHAN_STAT 0x12c
+#define DMA2_CHAN_STAT 0x14c
+#define DMA3_CHAN_STAT 0x16c
+#define DMA4_CHAN_STAT 0x18c
+#define DMA_CHAN_STAT(chan) (DMA_BREG(DMA0_CHAN_STAT, chan))
+/* CHAN_STATUS registers share bits */
+#define DMA_CHAN_STAT_SELFID (1<<31)
+#define DMA_CHAN_STAT_ISOPKT (1<<30)
+#define DMA_CHAN_STAT_PCIERR (1<<29)
+#define DMA_CHAN_STAT_PKTERR (1<<28)
+#define DMA_CHAN_STAT_PKTCMPL (1<<27)
+#define DMA_CHAN_STAT_SPECIALACK (1<<14)
+
+
+#define DMA0_CHAN_CTRL 0x110
+#define DMA1_CHAN_CTRL 0x130
+#define DMA2_CHAN_CTRL 0x150
+#define DMA3_CHAN_CTRL 0x170
+#define DMA4_CHAN_CTRL 0x190
+#define DMA_CHAN_CTRL(chan) (DMA_BREG(DMA0_CHAN_CTRL, chan))
+/* CHAN_CTRL registers share bits */
+#define DMA_CHAN_CTRL_ENABLE (1<<31)
+#define DMA_CHAN_CTRL_BUSY (1<<30)
+#define DMA_CHAN_CTRL_LINK (1<<29)
+
+#define DMA0_READY 0x114
+#define DMA1_READY 0x134
+#define DMA2_READY 0x154
+#define DMA3_READY 0x174
+#define DMA4_READY 0x194
+#define DMA_READY(chan) (DMA_BREG(DMA0_READY, chan))
+
+#define DMA_GLOBAL_REGISTER 0x908
+
+#define FIFO_SIZES 0xa00
+
+#define FIFO_CONTROL 0xa10
+#define FIFO_CONTROL_GRF_FLUSH (1<<4)
+#define FIFO_CONTROL_ITF_FLUSH (1<<3)
+#define FIFO_CONTROL_ATF_FLUSH (1<<2)
+
+#define FIFO_XMIT_THRESHOLD 0xa14
+
+#define DMA0_WORD0_CMP_VALUE 0xb00
+#define DMA1_WORD0_CMP_VALUE 0xb10
+#define DMA2_WORD0_CMP_VALUE 0xb20
+#define DMA3_WORD0_CMP_VALUE 0xb30
+#define DMA4_WORD0_CMP_VALUE 0xb40
+#define DMA_WORD0_CMP_VALUE(chan) (DMA_SREG(DMA0_WORD0_CMP_VALUE, chan))
+
+#define DMA0_WORD0_CMP_ENABLE 0xb04
+#define DMA1_WORD0_CMP_ENABLE 0xb14
+#define DMA2_WORD0_CMP_ENABLE 0xb24
+#define DMA3_WORD0_CMP_ENABLE 0xb34
+#define DMA4_WORD0_CMP_ENABLE 0xb44
+#define DMA_WORD0_CMP_ENABLE(chan) (DMA_SREG(DMA0_WORD0_CMP_ENABLE,chan))
+
+#define DMA0_WORD1_CMP_VALUE 0xb08
+#define DMA1_WORD1_CMP_VALUE 0xb18
+#define DMA2_WORD1_CMP_VALUE 0xb28
+#define DMA3_WORD1_CMP_VALUE 0xb38
+#define DMA4_WORD1_CMP_VALUE 0xb48
+#define DMA_WORD1_CMP_VALUE(chan) (DMA_SREG(DMA0_WORD1_CMP_VALUE, chan))
+
+#define DMA0_WORD1_CMP_ENABLE 0xb0c
+#define DMA1_WORD1_CMP_ENABLE 0xb1c
+#define DMA2_WORD1_CMP_ENABLE 0xb2c
+#define DMA3_WORD1_CMP_ENABLE 0xb3c
+#define DMA4_WORD1_CMP_ENABLE 0xb4c
+#define DMA_WORD1_CMP_ENABLE(chan) (DMA_SREG(DMA0_WORD1_CMP_ENABLE,chan))
+/* word 1 compare enable flags */
+#define DMA_WORD1_CMP_MATCH_OTHERBUS (1<<15)
+#define DMA_WORD1_CMP_MATCH_BROADCAST (1<<14)
+#define DMA_WORD1_CMP_MATCH_BUS_BCAST (1<<13)
+#define DMA_WORD1_CMP_MATCH_LOCAL_NODE (1<<12)
+#define DMA_WORD1_CMP_MATCH_EXACT (1<<11)
+#define DMA_WORD1_CMP_ENABLE_SELF_ID (1<<10)
+#define DMA_WORD1_CMP_ENABLE_MASTER (1<<8)
+
+#define LINK_ID 0xf00
+#define LINK_ID_BUS(id) (id<<22)
+#define LINK_ID_NODE(id) (id<<16)
+
+#define LINK_CONTROL 0xf04
+#define LINK_CONTROL_BUSY (1<<29)
+#define LINK_CONTROL_TX_ISO_EN (1<<26)
+#define LINK_CONTROL_RX_ISO_EN (1<<25)
+#define LINK_CONTROL_TX_ASYNC_EN (1<<24)
+#define LINK_CONTROL_RX_ASYNC_EN (1<<23)
+#define LINK_CONTROL_RESET_TX (1<<21)
+#define LINK_CONTROL_RESET_RX (1<<20)
+#define LINK_CONTROL_CYCMASTER (1<<11)
+#define LINK_CONTROL_CYCSOURCE (1<<10)
+#define LINK_CONTROL_CYCTIMEREN (1<<9)
+#define LINK_CONTROL_RCV_CMP_VALID (1<<7)
+#define LINK_CONTROL_SNOOP_ENABLE (1<<6)
+
+#define CYCLE_TIMER 0xf08
+
+#define LINK_PHY 0xf0c
+#define LINK_PHY_READ (1<<31)
+#define LINK_PHY_WRITE (1<<30)
+#define LINK_PHY_ADDR(addr) (addr<<24)
+#define LINK_PHY_WDATA(data) (data<<16)
+#define LINK_PHY_RADDR(addr) (addr<<8)
+
+
+#define LINK_INT_STATUS 0xf14
+#define LINK_INT_ENABLE 0xf18
+/* status and enable have identical bit numbers */
+#define LINK_INT_LINK_INT (1<<31)
+#define LINK_INT_PHY_TIMEOUT (1<<30)
+#define LINK_INT_PHY_REG_RCVD (1<<29)
+#define LINK_INT_PHY_BUSRESET (1<<28)
+#define LINK_INT_TX_RDY (1<<26)
+#define LINK_INT_RX_DATA_RDY (1<<25)
+#define LINK_INT_ISO_STUCK (1<<20)
+#define LINK_INT_ASYNC_STUCK (1<<19)
+#define LINK_INT_SENT_REJECT (1<<17)
+#define LINK_INT_HDR_ERR (1<<16)
+#define LINK_INT_TX_INVALID_TC (1<<15)
+#define LINK_INT_CYC_SECOND (1<<11)
+#define LINK_INT_CYC_START (1<<10)
+#define LINK_INT_CYC_DONE (1<<9)
+#define LINK_INT_CYC_PENDING (1<<8)
+#define LINK_INT_CYC_LOST (1<<7)
+#define LINK_INT_CYC_ARB_FAILED (1<<6)
+#define LINK_INT_GRF_OVERFLOW (1<<5)
+#define LINK_INT_ITF_UNDERFLOW (1<<4)
+#define LINK_INT_ATF_UNDERFLOW (1<<3)
+#define LINK_INT_ISOARB_FAILED (1<<0)
+
+/* PHY specifics */
+#define PHY_VENDORID_TI 0x800028
+#define PHY_PRODUCTID_TSB41LV03 0x000000
+
+
+/* this is the physical layout of a PCL, its size is 128 bytes */
+struct ti_pcl {
+ u32 next;
+ u32 async_error_next;
+ u32 user_data;
+ u32 pcl_status;
+ u32 remaining_transfer_count;
+ u32 next_data_buffer;
+ struct {
+ u32 control;
+ u32 pointer;
+ } buffer[13] __attribute__ ((packed));
+} __attribute__ ((packed));
+
+#include <linux/stddef.h>
+#define pcloffs(MEMBER) (offsetof(struct ti_pcl, MEMBER))
+
+
+static inline void put_pcl(const struct ti_lynx *lynx, pcl_t pclid,
+ const struct ti_pcl *pcl)
+{
+ memcpy_le32((u32 *)(lynx->pcl_mem + pclid * sizeof(struct ti_pcl)),
+ (u32 *)pcl, sizeof(struct ti_pcl));
+}
+
+static inline void get_pcl(const struct ti_lynx *lynx, pcl_t pclid,
+ struct ti_pcl *pcl)
+{
+ memcpy_le32((u32 *)pcl,
+ (u32 *)(lynx->pcl_mem + pclid * sizeof(struct ti_pcl)),
+ sizeof(struct ti_pcl));
+}
+
+static inline u32 pcl_bus(const struct ti_lynx *lynx, pcl_t pclid)
+{
+ return lynx->pcl_mem_dma + pclid * sizeof(struct ti_pcl);
+}
+
+
+#if defined (__BIG_ENDIAN)
+typedef struct ti_pcl pcltmp_t;
+
+static inline struct ti_pcl *edit_pcl(const struct ti_lynx *lynx, pcl_t pclid,
+ pcltmp_t *tmp)
+{
+ get_pcl(lynx, pclid, tmp);
+ return tmp;
+}
+
+static inline void commit_pcl(const struct ti_lynx *lynx, pcl_t pclid,
+ pcltmp_t *tmp)
+{
+ put_pcl(lynx, pclid, tmp);
+}
+
+#else
+typedef int pcltmp_t; /* just a dummy */
+
+static inline struct ti_pcl *edit_pcl(const struct ti_lynx *lynx, pcl_t pclid,
+ pcltmp_t *tmp)
+{
+ return lynx->pcl_mem + pclid * sizeof(struct ti_pcl);
+}
+
+static inline void commit_pcl(const struct ti_lynx *lynx, pcl_t pclid,
+ pcltmp_t *tmp)
+{
+}
+#endif
+
+
+static inline void run_sub_pcl(const struct ti_lynx *lynx, pcl_t pclid, int idx,
+ int dmachan)
+{
+ reg_write(lynx, DMA0_CURRENT_PCL + dmachan * 0x20,
+ pcl_bus(lynx, pclid) + idx * 4);
+ reg_write(lynx, DMA0_CHAN_CTRL + dmachan * 0x20,
+ DMA_CHAN_CTRL_ENABLE | DMA_CHAN_CTRL_LINK);
+}
+
+static inline void run_pcl(const struct ti_lynx *lynx, pcl_t pclid, int dmachan)
+{
+ run_sub_pcl(lynx, pclid, 0, dmachan);
+}
+
+#define PCL_NEXT_INVALID (1<<0)
+
+/* transfer commands */
+#define PCL_CMD_RCV (0x1<<24)
+#define PCL_CMD_RCV_AND_UPDATE (0xa<<24)
+#define PCL_CMD_XMT (0x2<<24)
+#define PCL_CMD_UNFXMT (0xc<<24)
+#define PCL_CMD_PCI_TO_LBUS (0x8<<24)
+#define PCL_CMD_LBUS_TO_PCI (0x9<<24)
+
+/* aux commands */
+#define PCL_CMD_NOP (0x0<<24)
+#define PCL_CMD_LOAD (0x3<<24)
+#define PCL_CMD_STOREQ (0x4<<24)
+#define PCL_CMD_STORED (0xb<<24)
+#define PCL_CMD_STORE0 (0x5<<24)
+#define PCL_CMD_STORE1 (0x6<<24)
+#define PCL_CMD_COMPARE (0xe<<24)
+#define PCL_CMD_SWAP_COMPARE (0xf<<24)
+#define PCL_CMD_ADD (0xd<<24)
+#define PCL_CMD_BRANCH (0x7<<24)
+
+/* BRANCH condition codes */
+#define PCL_COND_DMARDY_SET (0x1<<20)
+#define PCL_COND_DMARDY_CLEAR (0x2<<20)
+
+#define PCL_GEN_INTR (1<<19)
+#define PCL_LAST_BUFF (1<<18)
+#define PCL_LAST_CMD (PCL_LAST_BUFF)
+#define PCL_WAITSTAT (1<<17)
+#define PCL_BIGENDIAN (1<<16)
+#define PCL_ISOMODE (1<<12)
+
+#endif
diff --git a/drivers/ieee1394/raw1394-private.h b/drivers/ieee1394/raw1394-private.h
new file mode 100644
index 0000000..7a225a4
--- /dev/null
+++ b/drivers/ieee1394/raw1394-private.h
@@ -0,0 +1,81 @@
+#ifndef IEEE1394_RAW1394_PRIVATE_H
+#define IEEE1394_RAW1394_PRIVATE_H
+
+/* header for definitions that are private to the raw1394 driver
+ and not visible to user-space */
+
+#define RAW1394_DEVICE_MAJOR 171
+#define RAW1394_DEVICE_NAME "raw1394"
+
+#define RAW1394_MAX_USER_CSR_DIRS 16
+
+struct iso_block_store {
+ atomic_t refcount;
+ size_t data_size;
+ quadlet_t data[0];
+};
+
+enum raw1394_iso_state { RAW1394_ISO_INACTIVE = 0,
+ RAW1394_ISO_RECV = 1,
+ RAW1394_ISO_XMIT = 2 };
+
+struct file_info {
+ struct list_head list;
+
+ struct mutex state_mutex;
+ enum { opened, initialized, connected } state;
+ unsigned int protocol_version;
+
+ struct hpsb_host *host;
+
+ struct list_head req_pending; /* protected by reqlists_lock */
+ struct list_head req_complete; /* protected by reqlists_lock */
+ spinlock_t reqlists_lock;
+ wait_queue_head_t wait_complete;
+
+ struct list_head addr_list; /* protected by host_info_lock */
+
+ u8 __user *fcp_buffer;
+
+ u8 notification; /* (busreset-notification) RAW1394_NOTIFY_OFF/ON */
+
+ /* new rawiso API */
+ enum raw1394_iso_state iso_state;
+ struct hpsb_iso *iso_handle;
+
+ /* User space's CSR1212 dynamic ConfigROM directories */
+ struct csr1212_keyval *csr1212_dirs[RAW1394_MAX_USER_CSR_DIRS];
+
+ /* Legacy ConfigROM update flag */
+ u8 cfgrom_upd;
+};
+
+struct arm_addr {
+ struct list_head addr_list; /* file_info list */
+ u64 start, end;
+ u64 arm_tag;
+ u8 access_rights;
+ u8 notification_options;
+ u8 client_transactions;
+ u64 recvb;
+ u16 rec_length;
+ u8 *addr_space_buffer; /* accessed by read/write/lock requests */
+};
+
+struct pending_request {
+ struct list_head list;
+ struct file_info *file_info;
+ struct hpsb_packet *packet;
+ struct iso_block_store *ibs;
+ quadlet_t *data;
+ int free_data;
+ struct raw1394_request req;
+};
+
+struct host_info {
+ struct list_head list;
+ struct hpsb_host *host;
+ struct list_head file_info_list; /* protected by host_info_lock */
+};
+
+#endif /* IEEE1394_RAW1394_PRIVATE_H */
diff --git a/drivers/ieee1394/raw1394.c b/drivers/ieee1394/raw1394.c
new file mode 100644
index 0000000..bf7e761
--- /dev/null
+++ b/drivers/ieee1394/raw1394.c
@@ -0,0 +1,3089 @@
+/*
+ * IEEE 1394 for Linux
+ *
+ * Raw interface to the bus
+ *
+ * Copyright (C) 1999, 2000 Andreas E. Bombe
+ * 2001, 2002 Manfred Weihs <weihs@ict.tuwien.ac.at>
+ * 2002 Christian Toegel <christian.toegel@gmx.at>
+ *
+ * This code is licensed under the GPL. See the file COPYING in the root
+ * directory of the kernel sources for details.
+ *
+ *
+ * Contributions:
+ *
+ * Manfred Weihs <weihs@ict.tuwien.ac.at>
+ * configuration ROM manipulation
+ * address range mapping
+ * adaptation for new (transparent) loopback mechanism
+ * sending of arbitrary async packets
+ * Christian Toegel <christian.toegel@gmx.at>
+ * address range mapping
+ * lock64 request
+ * transmit physical packet
+ * busreset notification control (switch on/off)
+ * busreset with selection of type (short/long)
+ * request_reply
+ */
+
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/poll.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/vmalloc.h>
+#include <linux/cdev.h>
+#include <asm/uaccess.h>
+#include <asm/atomic.h>
+#include <linux/compat.h>
+
+#include "csr1212.h"
+#include "highlevel.h"
+#include "hosts.h"
+#include "ieee1394.h"
+#include "ieee1394_core.h"
+#include "ieee1394_hotplug.h"
+#include "ieee1394_transactions.h"
+#include "ieee1394_types.h"
+#include "iso.h"
+#include "nodemgr.h"
+#include "raw1394.h"
+#include "raw1394-private.h"
+
+#define int2ptr(x) ((void __user *)(unsigned long)x)
+#define ptr2int(x) ((u64)(unsigned long)(void __user *)x)
+
+#ifdef CONFIG_IEEE1394_VERBOSEDEBUG
+#define RAW1394_DEBUG
+#endif
+
+#ifdef RAW1394_DEBUG
+#define DBGMSG(fmt, args...) \
+printk(KERN_INFO "raw1394:" fmt "\n" , ## args)
+#else
+#define DBGMSG(fmt, args...) do {} while (0)
+#endif
+
+static LIST_HEAD(host_info_list);
+static int host_count;
+static DEFINE_SPINLOCK(host_info_lock);
+static atomic_t internal_generation = ATOMIC_INIT(0);
+
+static atomic_t iso_buffer_size;
+static const int iso_buffer_max = 4 * 1024 * 1024; /* 4 MB */
+
+static struct hpsb_highlevel raw1394_highlevel;
+
+static int arm_read(struct hpsb_host *host, int nodeid, quadlet_t * buffer,
+ u64 addr, size_t length, u16 flags);
+static int arm_write(struct hpsb_host *host, int nodeid, int destid,
+ quadlet_t * data, u64 addr, size_t length, u16 flags);
+static int arm_lock(struct hpsb_host *host, int nodeid, quadlet_t * store,
+ u64 addr, quadlet_t data, quadlet_t arg, int ext_tcode,
+ u16 flags);
+static int arm_lock64(struct hpsb_host *host, int nodeid, octlet_t * store,
+ u64 addr, octlet_t data, octlet_t arg, int ext_tcode,
+ u16 flags);
+static struct hpsb_address_ops arm_ops = {
+ .read = arm_read,
+ .write = arm_write,
+ .lock = arm_lock,
+ .lock64 = arm_lock64,
+};
+
+static void queue_complete_cb(struct pending_request *req);
+
+static struct pending_request *__alloc_pending_request(gfp_t flags)
+{
+ struct pending_request *req;
+
+ req = kzalloc(sizeof(*req), flags);
+ if (req)
+ INIT_LIST_HEAD(&req->list);
+
+ return req;
+}
+
+static inline struct pending_request *alloc_pending_request(void)
+{
+ return __alloc_pending_request(GFP_KERNEL);
+}
+
+static void free_pending_request(struct pending_request *req)
+{
+ if (req->ibs) {
+ if (atomic_dec_and_test(&req->ibs->refcount)) {
+ atomic_sub(req->ibs->data_size, &iso_buffer_size);
+ kfree(req->ibs);
+ }
+ } else if (req->free_data) {
+ kfree(req->data);
+ }
+ hpsb_free_packet(req->packet);
+ kfree(req);
+}
+
+/* fi->reqlists_lock must be taken */
+static void __queue_complete_req(struct pending_request *req)
+{
+ struct file_info *fi = req->file_info;
+
+ list_move_tail(&req->list, &fi->req_complete);
+ wake_up(&fi->wait_complete);
+}
+
+static void queue_complete_req(struct pending_request *req)
+{
+ unsigned long flags;
+ struct file_info *fi = req->file_info;
+
+ spin_lock_irqsave(&fi->reqlists_lock, flags);
+ __queue_complete_req(req);
+ spin_unlock_irqrestore(&fi->reqlists_lock, flags);
+}
+
+static void queue_complete_cb(struct pending_request *req)
+{
+ struct hpsb_packet *packet = req->packet;
+ int rcode = (packet->header[1] >> 12) & 0xf;
+
+ switch (packet->ack_code) {
+ case ACKX_NONE:
+ case ACKX_SEND_ERROR:
+ req->req.error = RAW1394_ERROR_SEND_ERROR;
+ break;
+ case ACKX_ABORTED:
+ req->req.error = RAW1394_ERROR_ABORTED;
+ break;
+ case ACKX_TIMEOUT:
+ req->req.error = RAW1394_ERROR_TIMEOUT;
+ break;
+ default:
+ req->req.error = (packet->ack_code << 16) | rcode;
+ break;
+ }
+
+ if (!((packet->ack_code == ACK_PENDING) && (rcode == RCODE_COMPLETE))) {
+ req->req.length = 0;
+ }
+
+ if ((req->req.type == RAW1394_REQ_ASYNC_READ) ||
+ (req->req.type == RAW1394_REQ_ASYNC_WRITE) ||
+ (req->req.type == RAW1394_REQ_ASYNC_STREAM) ||
+ (req->req.type == RAW1394_REQ_LOCK) ||
+ (req->req.type == RAW1394_REQ_LOCK64))
+ hpsb_free_tlabel(packet);
+
+ queue_complete_req(req);
+}
+
+static void add_host(struct hpsb_host *host)
+{
+ struct host_info *hi;
+ unsigned long flags;
+
+ hi = kmalloc(sizeof(*hi), GFP_KERNEL);
+
+ if (hi) {
+ INIT_LIST_HEAD(&hi->list);
+ hi->host = host;
+ INIT_LIST_HEAD(&hi->file_info_list);
+
+ spin_lock_irqsave(&host_info_lock, flags);
+ list_add_tail(&hi->list, &host_info_list);
+ host_count++;
+ spin_unlock_irqrestore(&host_info_lock, flags);
+ }
+
+ atomic_inc(&internal_generation);
+}
+
+static struct host_info *find_host_info(struct hpsb_host *host)
+{
+ struct host_info *hi;
+
+ list_for_each_entry(hi, &host_info_list, list)
+ if (hi->host == host)
+ return hi;
+
+ return NULL;
+}
+
+static void remove_host(struct hpsb_host *host)
+{
+ struct host_info *hi;
+ unsigned long flags;
+
+ spin_lock_irqsave(&host_info_lock, flags);
+ hi = find_host_info(host);
+
+ if (hi != NULL) {
+ list_del(&hi->list);
+ host_count--;
+ /*
+ FIXME: address ranges should be removed
+ and fileinfo states should be initialized
+ (including setting generation to
+ internal-generation ...)
+ */
+ }
+ spin_unlock_irqrestore(&host_info_lock, flags);
+
+ if (hi == NULL) {
+ printk(KERN_ERR "raw1394: attempt to remove unknown host "
+ "0x%p\n", host);
+ return;
+ }
+
+ kfree(hi);
+
+ atomic_inc(&internal_generation);
+}
+
+static void host_reset(struct hpsb_host *host)
+{
+ unsigned long flags;
+ struct host_info *hi;
+ struct file_info *fi;
+ struct pending_request *req;
+
+ spin_lock_irqsave(&host_info_lock, flags);
+ hi = find_host_info(host);
+
+ if (hi != NULL) {
+ list_for_each_entry(fi, &hi->file_info_list, list) {
+ if (fi->notification == RAW1394_NOTIFY_ON) {
+ req = __alloc_pending_request(GFP_ATOMIC);
+
+ if (req != NULL) {
+ req->file_info = fi;
+ req->req.type = RAW1394_REQ_BUS_RESET;
+ req->req.generation =
+ get_hpsb_generation(host);
+ req->req.misc = (host->node_id << 16)
+ | host->node_count;
+ if (fi->protocol_version > 3) {
+ req->req.misc |=
+ (NODEID_TO_NODE
+ (host->irm_id)
+ << 8);
+ }
+
+ queue_complete_req(req);
+ }
+ }
+ }
+ }
+ spin_unlock_irqrestore(&host_info_lock, flags);
+}
+
+static void fcp_request(struct hpsb_host *host, int nodeid, int direction,
+ int cts, u8 * data, size_t length)
+{
+ unsigned long flags;
+ struct host_info *hi;
+ struct file_info *fi;
+ struct pending_request *req, *req_next;
+ struct iso_block_store *ibs = NULL;
+ LIST_HEAD(reqs);
+
+ if ((atomic_read(&iso_buffer_size) + length) > iso_buffer_max) {
+ HPSB_INFO("dropped fcp request");
+ return;
+ }
+
+ spin_lock_irqsave(&host_info_lock, flags);
+ hi = find_host_info(host);
+
+ if (hi != NULL) {
+ list_for_each_entry(fi, &hi->file_info_list, list) {
+ if (!fi->fcp_buffer)
+ continue;
+
+ req = __alloc_pending_request(GFP_ATOMIC);
+ if (!req)
+ break;
+
+ if (!ibs) {
+ ibs = kmalloc(sizeof(*ibs) + length,
+ GFP_ATOMIC);
+ if (!ibs) {
+ kfree(req);
+ break;
+ }
+
+ atomic_add(length, &iso_buffer_size);
+ atomic_set(&ibs->refcount, 0);
+ ibs->data_size = length;
+ memcpy(ibs->data, data, length);
+ }
+
+ atomic_inc(&ibs->refcount);
+
+ req->file_info = fi;
+ req->ibs = ibs;
+ req->data = ibs->data;
+ req->req.type = RAW1394_REQ_FCP_REQUEST;
+ req->req.generation = get_hpsb_generation(host);
+ req->req.misc = nodeid | (direction << 16);
+ req->req.recvb = ptr2int(fi->fcp_buffer);
+ req->req.length = length;
+
+ list_add_tail(&req->list, &reqs);
+ }
+ }
+ spin_unlock_irqrestore(&host_info_lock, flags);
+
+ list_for_each_entry_safe(req, req_next, &reqs, list)
+ queue_complete_req(req);
+}
+
+#ifdef CONFIG_COMPAT
+struct compat_raw1394_req {
+ __u32 type;
+ __s32 error;
+ __u32 misc;
+
+ __u32 generation;
+ __u32 length;
+
+ __u64 address;
+
+ __u64 tag;
+
+ __u64 sendb;
+ __u64 recvb;
+}
+#if defined(CONFIG_X86_64) || defined(CONFIG_IA64)
+__attribute__((packed))
+#endif
+;
+
+static const char __user *raw1394_compat_write(const char __user *buf)
+{
+ struct compat_raw1394_req __user *cr = (typeof(cr)) buf;
+ struct raw1394_request __user *r;
+ r = compat_alloc_user_space(sizeof(struct raw1394_request));
+
+#define C(x) __copy_in_user(&r->x, &cr->x, sizeof(r->x))
+
+ if (copy_in_user(r, cr, sizeof(struct compat_raw1394_req)) ||
+ C(address) ||
+ C(tag) ||
+ C(sendb) ||
+ C(recvb))
+ return ERR_PTR(-EFAULT);
+ return (const char __user *)r;
+}
+#undef C
+
+#define P(x) __put_user(r->x, &cr->x)
+
+static int
+raw1394_compat_read(const char __user *buf, struct raw1394_request *r)
+{
+ struct compat_raw1394_req __user *cr = (typeof(cr)) buf;
+ if (!access_ok(VERIFY_WRITE, cr, sizeof(struct compat_raw1394_req)) ||
+ P(type) ||
+ P(error) ||
+ P(misc) ||
+ P(generation) ||
+ P(length) ||
+ P(address) ||
+ P(tag) ||
+ P(sendb) ||
+ P(recvb))
+ return -EFAULT;
+ return sizeof(struct compat_raw1394_req);
+}
+#undef P
+
+#endif
+
+/* get next completed request (caller must hold fi->reqlists_lock) */
+static inline struct pending_request *__next_complete_req(struct file_info *fi)
+{
+ struct list_head *lh;
+ struct pending_request *req = NULL;
+
+ if (!list_empty(&fi->req_complete)) {
+ lh = fi->req_complete.next;
+ list_del(lh);
+ req = list_entry(lh, struct pending_request, list);
+ }
+ return req;
+}
+
+/* atomically get next completed request */
+static struct pending_request *next_complete_req(struct file_info *fi)
+{
+ unsigned long flags;
+ struct pending_request *req;
+
+ spin_lock_irqsave(&fi->reqlists_lock, flags);
+ req = __next_complete_req(fi);
+ spin_unlock_irqrestore(&fi->reqlists_lock, flags);
+ return req;
+}
+
+static ssize_t raw1394_read(struct file *file, char __user * buffer,
+ size_t count, loff_t * offset_is_ignored)
+{
+ struct file_info *fi = (struct file_info *)file->private_data;
+ struct pending_request *req;
+ ssize_t ret;
+
+#ifdef CONFIG_COMPAT
+ if (count == sizeof(struct compat_raw1394_req)) {
+ /* ok */
+ } else
+#endif
+ if (count != sizeof(struct raw1394_request)) {
+ return -EINVAL;
+ }
+
+ if (!access_ok(VERIFY_WRITE, buffer, count)) {
+ return -EFAULT;
+ }
+
+ if (file->f_flags & O_NONBLOCK) {
+ if (!(req = next_complete_req(fi)))
+ return -EAGAIN;
+ } else {
+ /*
+ * NB: We call the macro wait_event_interruptible() with a
+ * condition argument with side effect. This is only possible
+ * because the side effect does not occur until the condition
+ * became true, and wait_event_interruptible() won't evaluate
+ * the condition again after that.
+ */
+ if (wait_event_interruptible(fi->wait_complete,
+ (req = next_complete_req(fi))))
+ return -ERESTARTSYS;
+ }
+
+ if (req->req.length) {
+ if (copy_to_user(int2ptr(req->req.recvb), req->data,
+ req->req.length)) {
+ req->req.error = RAW1394_ERROR_MEMFAULT;
+ }
+ }
+
+#ifdef CONFIG_COMPAT
+ if (count == sizeof(struct compat_raw1394_req) &&
+ sizeof(struct compat_raw1394_req) !=
+ sizeof(struct raw1394_request)) {
+ ret = raw1394_compat_read(buffer, &req->req);
+ } else
+#endif
+ {
+ if (copy_to_user(buffer, &req->req, sizeof(req->req))) {
+ ret = -EFAULT;
+ goto out;
+ }
+ ret = (ssize_t) sizeof(struct raw1394_request);
+ }
+ out:
+ free_pending_request(req);
+ return ret;
+}
+
+static int state_opened(struct file_info *fi, struct pending_request *req)
+{
+ if (req->req.type == RAW1394_REQ_INITIALIZE) {
+ switch (req->req.misc) {
+ case RAW1394_KERNELAPI_VERSION:
+ case 3:
+ fi->state = initialized;
+ fi->protocol_version = req->req.misc;
+ req->req.error = RAW1394_ERROR_NONE;
+ req->req.generation = atomic_read(&internal_generation);
+ break;
+
+ default:
+ req->req.error = RAW1394_ERROR_COMPAT;
+ req->req.misc = RAW1394_KERNELAPI_VERSION;
+ }
+ } else {
+ req->req.error = RAW1394_ERROR_STATE_ORDER;
+ }
+
+ req->req.length = 0;
+ queue_complete_req(req);
+ return 0;
+}
+
+static int state_initialized(struct file_info *fi, struct pending_request *req)
+{
+ unsigned long flags;
+ struct host_info *hi;
+ struct raw1394_khost_list *khl;
+
+ if (req->req.generation != atomic_read(&internal_generation)) {
+ req->req.error = RAW1394_ERROR_GENERATION;
+ req->req.generation = atomic_read(&internal_generation);
+ req->req.length = 0;
+ queue_complete_req(req);
+ return 0;
+ }
+
+ switch (req->req.type) {
+ case RAW1394_REQ_LIST_CARDS:
+ spin_lock_irqsave(&host_info_lock, flags);
+ khl = kmalloc(sizeof(*khl) * host_count, GFP_ATOMIC);
+
+ if (khl) {
+ req->req.misc = host_count;
+ req->data = (quadlet_t *) khl;
+
+ list_for_each_entry(hi, &host_info_list, list) {
+ khl->nodes = hi->host->node_count;
+ strcpy(khl->name, hi->host->driver->name);
+ khl++;
+ }
+ }
+ spin_unlock_irqrestore(&host_info_lock, flags);
+
+ if (khl) {
+ req->req.error = RAW1394_ERROR_NONE;
+ req->req.length = min(req->req.length,
+ (u32) (sizeof
+ (struct raw1394_khost_list)
+ * req->req.misc));
+ req->free_data = 1;
+ } else {
+ return -ENOMEM;
+ }
+ break;
+
+ case RAW1394_REQ_SET_CARD:
+ spin_lock_irqsave(&host_info_lock, flags);
+ if (req->req.misc >= host_count) {
+ req->req.error = RAW1394_ERROR_INVALID_ARG;
+ goto out_set_card;
+ }
+ list_for_each_entry(hi, &host_info_list, list)
+ if (!req->req.misc--)
+ break;
+ get_device(&hi->host->device); /* FIXME handle failure case */
+ list_add_tail(&fi->list, &hi->file_info_list);
+
+ /* prevent unloading of the host's low-level driver */
+ if (!try_module_get(hi->host->driver->owner)) {
+ req->req.error = RAW1394_ERROR_ABORTED;
+ goto out_set_card;
+ }
+ WARN_ON(fi->host);
+ fi->host = hi->host;
+ fi->state = connected;
+
+ req->req.error = RAW1394_ERROR_NONE;
+ req->req.generation = get_hpsb_generation(fi->host);
+ req->req.misc = (fi->host->node_id << 16)
+ | fi->host->node_count;
+ if (fi->protocol_version > 3)
+ req->req.misc |= NODEID_TO_NODE(fi->host->irm_id) << 8;
+out_set_card:
+ spin_unlock_irqrestore(&host_info_lock, flags);
+
+ req->req.length = 0;
+ break;
+
+ default:
+ req->req.error = RAW1394_ERROR_STATE_ORDER;
+ req->req.length = 0;
+ break;
+ }
+
+ queue_complete_req(req);
+ return 0;
+}
+
+static void handle_fcp_listen(struct file_info *fi, struct pending_request *req)
+{
+ if (req->req.misc) {
+ if (fi->fcp_buffer) {
+ req->req.error = RAW1394_ERROR_ALREADY;
+ } else {
+ fi->fcp_buffer = int2ptr(req->req.recvb);
+ }
+ } else {
+ if (!fi->fcp_buffer) {
+ req->req.error = RAW1394_ERROR_ALREADY;
+ } else {
+ fi->fcp_buffer = NULL;
+ }
+ }
+
+ req->req.length = 0;
+ queue_complete_req(req);
+}
+
+static int handle_async_request(struct file_info *fi,
+ struct pending_request *req, int node)
+{
+ unsigned long flags;
+ struct hpsb_packet *packet = NULL;
+ u64 addr = req->req.address & 0xffffffffffffULL;
+
+ switch (req->req.type) {
+ case RAW1394_REQ_ASYNC_READ:
+ DBGMSG("read_request called");
+ packet =
+ hpsb_make_readpacket(fi->host, node, addr, req->req.length);
+
+ if (!packet)
+ return -ENOMEM;
+
+ if (req->req.length == 4)
+ req->data = &packet->header[3];
+ else
+ req->data = packet->data;
+
+ break;
+
+ case RAW1394_REQ_ASYNC_WRITE:
+ DBGMSG("write_request called");
+
+ packet = hpsb_make_writepacket(fi->host, node, addr, NULL,
+ req->req.length);
+ if (!packet)
+ return -ENOMEM;
+
+ if (req->req.length == 4) {
+ if (copy_from_user
+ (&packet->header[3], int2ptr(req->req.sendb),
+ req->req.length))
+ req->req.error = RAW1394_ERROR_MEMFAULT;
+ } else {
+ if (copy_from_user
+ (packet->data, int2ptr(req->req.sendb),
+ req->req.length))
+ req->req.error = RAW1394_ERROR_MEMFAULT;
+ }
+
+ req->req.length = 0;
+ break;
+
+ case RAW1394_REQ_ASYNC_STREAM:
+ DBGMSG("stream_request called");
+
+ packet =
+ hpsb_make_streampacket(fi->host, NULL, req->req.length,
+ node & 0x3f /*channel */ ,
+ (req->req.misc >> 16) & 0x3,
+ req->req.misc & 0xf);
+ if (!packet)
+ return -ENOMEM;
+
+ if (copy_from_user(packet->data, int2ptr(req->req.sendb),
+ req->req.length))
+ req->req.error = RAW1394_ERROR_MEMFAULT;
+
+ req->req.length = 0;
+ break;
+
+ case RAW1394_REQ_LOCK:
+ DBGMSG("lock_request called");
+ if ((req->req.misc == EXTCODE_FETCH_ADD)
+ || (req->req.misc == EXTCODE_LITTLE_ADD)) {
+ if (req->req.length != 4) {
+ req->req.error = RAW1394_ERROR_INVALID_ARG;
+ break;
+ }
+ } else {
+ if (req->req.length != 8) {
+ req->req.error = RAW1394_ERROR_INVALID_ARG;
+ break;
+ }
+ }
+
+ packet = hpsb_make_lockpacket(fi->host, node, addr,
+ req->req.misc, NULL, 0);
+ if (!packet)
+ return -ENOMEM;
+
+ if (copy_from_user(packet->data, int2ptr(req->req.sendb),
+ req->req.length)) {
+ req->req.error = RAW1394_ERROR_MEMFAULT;
+ break;
+ }
+
+ req->data = packet->data;
+ req->req.length = 4;
+ break;
+
+ case RAW1394_REQ_LOCK64:
+ DBGMSG("lock64_request called");
+ if ((req->req.misc == EXTCODE_FETCH_ADD)
+ || (req->req.misc == EXTCODE_LITTLE_ADD)) {
+ if (req->req.length != 8) {
+ req->req.error = RAW1394_ERROR_INVALID_ARG;
+ break;
+ }
+ } else {
+ if (req->req.length != 16) {
+ req->req.error = RAW1394_ERROR_INVALID_ARG;
+ break;
+ }
+ }
+ packet = hpsb_make_lock64packet(fi->host, node, addr,
+ req->req.misc, NULL, 0);
+ if (!packet)
+ return -ENOMEM;
+
+ if (copy_from_user(packet->data, int2ptr(req->req.sendb),
+ req->req.length)) {
+ req->req.error = RAW1394_ERROR_MEMFAULT;
+ break;
+ }
+
+ req->data = packet->data;
+ req->req.length = 8;
+ break;
+
+ default:
+ req->req.error = RAW1394_ERROR_STATE_ORDER;
+ }
+
+ req->packet = packet;
+
+ if (req->req.error) {
+ req->req.length = 0;
+ queue_complete_req(req);
+ return 0;
+ }
+
+ hpsb_set_packet_complete_task(packet,
+ (void (*)(void *))queue_complete_cb, req);
+
+ spin_lock_irqsave(&fi->reqlists_lock, flags);
+ list_add_tail(&req->list, &fi->req_pending);
+ spin_unlock_irqrestore(&fi->reqlists_lock, flags);
+
+ packet->generation = req->req.generation;
+
+ if (hpsb_send_packet(packet) < 0) {
+ req->req.error = RAW1394_ERROR_SEND_ERROR;
+ req->req.length = 0;
+ hpsb_free_tlabel(packet);
+ queue_complete_req(req);
+ }
+ return 0;
+}
+
+static int handle_async_send(struct file_info *fi, struct pending_request *req)
+{
+ unsigned long flags;
+ struct hpsb_packet *packet;
+ int header_length = req->req.misc & 0xffff;
+ int expect_response = req->req.misc >> 16;
+ size_t data_size;
+
+ if (header_length > req->req.length || header_length < 12 ||
+ header_length > FIELD_SIZEOF(struct hpsb_packet, header)) {
+ req->req.error = RAW1394_ERROR_INVALID_ARG;
+ req->req.length = 0;
+ queue_complete_req(req);
+ return 0;
+ }
+
+ data_size = req->req.length - header_length;
+ packet = hpsb_alloc_packet(data_size);
+ req->packet = packet;
+ if (!packet)
+ return -ENOMEM;
+
+ if (copy_from_user(packet->header, int2ptr(req->req.sendb),
+ header_length)) {
+ req->req.error = RAW1394_ERROR_MEMFAULT;
+ req->req.length = 0;
+ queue_complete_req(req);
+ return 0;
+ }
+
+ if (copy_from_user
+ (packet->data, int2ptr(req->req.sendb) + header_length,
+ data_size)) {
+ req->req.error = RAW1394_ERROR_MEMFAULT;
+ req->req.length = 0;
+ queue_complete_req(req);
+ return 0;
+ }
+
+ packet->type = hpsb_async;
+ packet->node_id = packet->header[0] >> 16;
+ packet->tcode = (packet->header[0] >> 4) & 0xf;
+ packet->tlabel = (packet->header[0] >> 10) & 0x3f;
+ packet->host = fi->host;
+ packet->expect_response = expect_response;
+ packet->header_size = header_length;
+ packet->data_size = data_size;
+
+ req->req.length = 0;
+ hpsb_set_packet_complete_task(packet,
+ (void (*)(void *))queue_complete_cb, req);
+
+ spin_lock_irqsave(&fi->reqlists_lock, flags);
+ list_add_tail(&req->list, &fi->req_pending);
+ spin_unlock_irqrestore(&fi->reqlists_lock, flags);
+
+ /* Update the generation of the packet just before sending. */
+ packet->generation = req->req.generation;
+
+ if (hpsb_send_packet(packet) < 0) {
+ req->req.error = RAW1394_ERROR_SEND_ERROR;
+ queue_complete_req(req);
+ }
+
+ return 0;
+}
+
+static int arm_read(struct hpsb_host *host, int nodeid, quadlet_t * buffer,
+ u64 addr, size_t length, u16 flags)
+{
+ unsigned long irqflags;
+ struct pending_request *req;
+ struct host_info *hi;
+ struct file_info *fi = NULL;
+ struct list_head *entry;
+ struct arm_addr *arm_addr = NULL;
+ struct arm_request *arm_req = NULL;
+ struct arm_response *arm_resp = NULL;
+ int found = 0, size = 0, rcode = -1;
+ struct arm_request_response *arm_req_resp = NULL;
+
+ DBGMSG("arm_read called by node: %X "
+ "addr: %4.4x %8.8x length: %Zu", nodeid,
+ (u16) ((addr >> 32) & 0xFFFF), (u32) (addr & 0xFFFFFFFF),
+ length);
+ spin_lock_irqsave(&host_info_lock, irqflags);
+ hi = find_host_info(host); /* search address-entry */
+ if (hi != NULL) {
+ list_for_each_entry(fi, &hi->file_info_list, list) {
+ entry = fi->addr_list.next;
+ while (entry != &(fi->addr_list)) {
+ arm_addr =
+ list_entry(entry, struct arm_addr,
+ addr_list);
+ if (((arm_addr->start) <= (addr))
+ && ((arm_addr->end) >= (addr + length))) {
+ found = 1;
+ break;
+ }
+ entry = entry->next;
+ }
+ if (found) {
+ break;
+ }
+ }
+ }
+ rcode = -1;
+ if (!found) {
+ printk(KERN_ERR "raw1394: arm_read FAILED addr_entry not found"
+ " -> rcode_address_error\n");
+ spin_unlock_irqrestore(&host_info_lock, irqflags);
+ return (RCODE_ADDRESS_ERROR);
+ } else {
+ DBGMSG("arm_read addr_entry FOUND");
+ }
+ if (arm_addr->rec_length < length) {
+ DBGMSG("arm_read blocklength too big -> rcode_data_error");
+ rcode = RCODE_DATA_ERROR; /* hardware error, data is unavailable */
+ }
+ if (rcode == -1) {
+ if (arm_addr->access_rights & ARM_READ) {
+ if (!(arm_addr->client_transactions & ARM_READ)) {
+ memcpy(buffer,
+ (arm_addr->addr_space_buffer) + (addr -
+ (arm_addr->
+ start)),
+ length);
+ DBGMSG("arm_read -> (rcode_complete)");
+ rcode = RCODE_COMPLETE;
+ }
+ } else {
+ rcode = RCODE_TYPE_ERROR; /* function not allowed */
+ DBGMSG("arm_read -> rcode_type_error (access denied)");
+ }
+ }
+ if (arm_addr->notification_options & ARM_READ) {
+ DBGMSG("arm_read -> entering notification-section");
+ req = __alloc_pending_request(GFP_ATOMIC);
+ if (!req) {
+ DBGMSG("arm_read -> rcode_conflict_error");
+ spin_unlock_irqrestore(&host_info_lock, irqflags);
+ return (RCODE_CONFLICT_ERROR); /* A resource conflict was detected.
+ The request may be retried */
+ }
+ if (rcode == RCODE_COMPLETE) {
+ size =
+ sizeof(struct arm_request) +
+ sizeof(struct arm_response) +
+ length * sizeof(byte_t) +
+ sizeof(struct arm_request_response);
+ } else {
+ size =
+ sizeof(struct arm_request) +
+ sizeof(struct arm_response) +
+ sizeof(struct arm_request_response);
+ }
+ req->data = kmalloc(size, GFP_ATOMIC);
+ if (!(req->data)) {
+ free_pending_request(req);
+ DBGMSG("arm_read -> rcode_conflict_error");
+ spin_unlock_irqrestore(&host_info_lock, irqflags);
+ return (RCODE_CONFLICT_ERROR); /* A resource conflict was detected.
+ The request may be retried */
+ }
+ req->free_data = 1;
+ req->file_info = fi;
+ req->req.type = RAW1394_REQ_ARM;
+ req->req.generation = get_hpsb_generation(host);
+ req->req.misc =
+ (((length << 16) & (0xFFFF0000)) | (ARM_READ & 0xFF));
+ req->req.tag = arm_addr->arm_tag;
+ req->req.recvb = arm_addr->recvb;
+ req->req.length = size;
+ arm_req_resp = (struct arm_request_response *)(req->data);
+ arm_req = (struct arm_request *)((byte_t *) (req->data) +
+ (sizeof
+ (struct
+ arm_request_response)));
+ arm_resp =
+ (struct arm_response *)((byte_t *) (arm_req) +
+ (sizeof(struct arm_request)));
+ arm_req->buffer = NULL;
+ arm_resp->buffer = NULL;
+ if (rcode == RCODE_COMPLETE) {
+ byte_t *buf =
+ (byte_t *) arm_resp + sizeof(struct arm_response);
+ memcpy(buf,
+ (arm_addr->addr_space_buffer) + (addr -
+ (arm_addr->
+ start)),
+ length);
+ arm_resp->buffer =
+ int2ptr((arm_addr->recvb) +
+ sizeof(struct arm_request_response) +
+ sizeof(struct arm_request) +
+ sizeof(struct arm_response));
+ }
+ arm_resp->buffer_length =
+ (rcode == RCODE_COMPLETE) ? length : 0;
+ arm_resp->response_code = rcode;
+ arm_req->buffer_length = 0;
+ arm_req->generation = req->req.generation;
+ arm_req->extended_transaction_code = 0;
+ arm_req->destination_offset = addr;
+ arm_req->source_nodeid = nodeid;
+ arm_req->destination_nodeid = host->node_id;
+ arm_req->tlabel = (flags >> 10) & 0x3f;
+ arm_req->tcode = (flags >> 4) & 0x0f;
+ arm_req_resp->request = int2ptr((arm_addr->recvb) +
+ sizeof(struct
+ arm_request_response));
+ arm_req_resp->response =
+ int2ptr((arm_addr->recvb) +
+ sizeof(struct arm_request_response) +
+ sizeof(struct arm_request));
+ queue_complete_req(req);
+ }
+ spin_unlock_irqrestore(&host_info_lock, irqflags);
+ return (rcode);
+}
+
+static int arm_write(struct hpsb_host *host, int nodeid, int destid,
+ quadlet_t * data, u64 addr, size_t length, u16 flags)
+{
+ unsigned long irqflags;
+ struct pending_request *req;
+ struct host_info *hi;
+ struct file_info *fi = NULL;
+ struct list_head *entry;
+ struct arm_addr *arm_addr = NULL;
+ struct arm_request *arm_req = NULL;
+ struct arm_response *arm_resp = NULL;
+ int found = 0, size = 0, rcode = -1, length_conflict = 0;
+ struct arm_request_response *arm_req_resp = NULL;
+
+ DBGMSG("arm_write called by node: %X "
+ "addr: %4.4x %8.8x length: %Zu", nodeid,
+ (u16) ((addr >> 32) & 0xFFFF), (u32) (addr & 0xFFFFFFFF),
+ length);
+ spin_lock_irqsave(&host_info_lock, irqflags);
+ hi = find_host_info(host); /* search address-entry */
+ if (hi != NULL) {
+ list_for_each_entry(fi, &hi->file_info_list, list) {
+ entry = fi->addr_list.next;
+ while (entry != &(fi->addr_list)) {
+ arm_addr =
+ list_entry(entry, struct arm_addr,
+ addr_list);
+ if (((arm_addr->start) <= (addr))
+ && ((arm_addr->end) >= (addr + length))) {
+ found = 1;
+ break;
+ }
+ entry = entry->next;
+ }
+ if (found) {
+ break;
+ }
+ }
+ }
+ rcode = -1;
+ if (!found) {
+ printk(KERN_ERR "raw1394: arm_write FAILED addr_entry not found"
+ " -> rcode_address_error\n");
+ spin_unlock_irqrestore(&host_info_lock, irqflags);
+ return (RCODE_ADDRESS_ERROR);
+ } else {
+ DBGMSG("arm_write addr_entry FOUND");
+ }
+ if (arm_addr->rec_length < length) {
+ DBGMSG("arm_write blocklength too big -> rcode_data_error");
+ length_conflict = 1;
+ rcode = RCODE_DATA_ERROR; /* hardware error, data is unavailable */
+ }
+ if (rcode == -1) {
+ if (arm_addr->access_rights & ARM_WRITE) {
+ if (!(arm_addr->client_transactions & ARM_WRITE)) {
+ memcpy((arm_addr->addr_space_buffer) +
+ (addr - (arm_addr->start)), data,
+ length);
+ DBGMSG("arm_write -> (rcode_complete)");
+ rcode = RCODE_COMPLETE;
+ }
+ } else {
+ rcode = RCODE_TYPE_ERROR; /* function not allowed */
+ DBGMSG("arm_write -> rcode_type_error (access denied)");
+ }
+ }
+ if (arm_addr->notification_options & ARM_WRITE) {
+ DBGMSG("arm_write -> entering notification-section");
+ req = __alloc_pending_request(GFP_ATOMIC);
+ if (!req) {
+ DBGMSG("arm_write -> rcode_conflict_error");
+ spin_unlock_irqrestore(&host_info_lock, irqflags);
+ return (RCODE_CONFLICT_ERROR); /* A resource conflict was detected.
+ The request my be retried */
+ }
+ size =
+ sizeof(struct arm_request) + sizeof(struct arm_response) +
+ (length) * sizeof(byte_t) +
+ sizeof(struct arm_request_response);
+ req->data = kmalloc(size, GFP_ATOMIC);
+ if (!(req->data)) {
+ free_pending_request(req);
+ DBGMSG("arm_write -> rcode_conflict_error");
+ spin_unlock_irqrestore(&host_info_lock, irqflags);
+ return (RCODE_CONFLICT_ERROR); /* A resource conflict was detected.
+ The request may be retried */
+ }
+ req->free_data = 1;
+ req->file_info = fi;
+ req->req.type = RAW1394_REQ_ARM;
+ req->req.generation = get_hpsb_generation(host);
+ req->req.misc =
+ (((length << 16) & (0xFFFF0000)) | (ARM_WRITE & 0xFF));
+ req->req.tag = arm_addr->arm_tag;
+ req->req.recvb = arm_addr->recvb;
+ req->req.length = size;
+ arm_req_resp = (struct arm_request_response *)(req->data);
+ arm_req = (struct arm_request *)((byte_t *) (req->data) +
+ (sizeof
+ (struct
+ arm_request_response)));
+ arm_resp =
+ (struct arm_response *)((byte_t *) (arm_req) +
+ (sizeof(struct arm_request)));
+ arm_resp->buffer = NULL;
+ memcpy((byte_t *) arm_resp + sizeof(struct arm_response),
+ data, length);
+ arm_req->buffer = int2ptr((arm_addr->recvb) +
+ sizeof(struct arm_request_response) +
+ sizeof(struct arm_request) +
+ sizeof(struct arm_response));
+ arm_req->buffer_length = length;
+ arm_req->generation = req->req.generation;
+ arm_req->extended_transaction_code = 0;
+ arm_req->destination_offset = addr;
+ arm_req->source_nodeid = nodeid;
+ arm_req->destination_nodeid = destid;
+ arm_req->tlabel = (flags >> 10) & 0x3f;
+ arm_req->tcode = (flags >> 4) & 0x0f;
+ arm_resp->buffer_length = 0;
+ arm_resp->response_code = rcode;
+ arm_req_resp->request = int2ptr((arm_addr->recvb) +
+ sizeof(struct
+ arm_request_response));
+ arm_req_resp->response =
+ int2ptr((arm_addr->recvb) +
+ sizeof(struct arm_request_response) +
+ sizeof(struct arm_request));
+ queue_complete_req(req);
+ }
+ spin_unlock_irqrestore(&host_info_lock, irqflags);
+ return (rcode);
+}
+
+static int arm_lock(struct hpsb_host *host, int nodeid, quadlet_t * store,
+ u64 addr, quadlet_t data, quadlet_t arg, int ext_tcode,
+ u16 flags)
+{
+ unsigned long irqflags;
+ struct pending_request *req;
+ struct host_info *hi;
+ struct file_info *fi = NULL;
+ struct list_head *entry;
+ struct arm_addr *arm_addr = NULL;
+ struct arm_request *arm_req = NULL;
+ struct arm_response *arm_resp = NULL;
+ int found = 0, size = 0, rcode = -1;
+ quadlet_t old, new;
+ struct arm_request_response *arm_req_resp = NULL;
+
+ if (((ext_tcode & 0xFF) == EXTCODE_FETCH_ADD) ||
+ ((ext_tcode & 0xFF) == EXTCODE_LITTLE_ADD)) {
+ DBGMSG("arm_lock called by node: %X "
+ "addr: %4.4x %8.8x extcode: %2.2X data: %8.8X",
+ nodeid, (u16) ((addr >> 32) & 0xFFFF),
+ (u32) (addr & 0xFFFFFFFF), ext_tcode & 0xFF,
+ be32_to_cpu(data));
+ } else {
+ DBGMSG("arm_lock called by node: %X "
+ "addr: %4.4x %8.8x extcode: %2.2X data: %8.8X arg: %8.8X",
+ nodeid, (u16) ((addr >> 32) & 0xFFFF),
+ (u32) (addr & 0xFFFFFFFF), ext_tcode & 0xFF,
+ be32_to_cpu(data), be32_to_cpu(arg));
+ }
+ spin_lock_irqsave(&host_info_lock, irqflags);
+ hi = find_host_info(host); /* search address-entry */
+ if (hi != NULL) {
+ list_for_each_entry(fi, &hi->file_info_list, list) {
+ entry = fi->addr_list.next;
+ while (entry != &(fi->addr_list)) {
+ arm_addr =
+ list_entry(entry, struct arm_addr,
+ addr_list);
+ if (((arm_addr->start) <= (addr))
+ && ((arm_addr->end) >=
+ (addr + sizeof(*store)))) {
+ found = 1;
+ break;
+ }
+ entry = entry->next;
+ }
+ if (found) {
+ break;
+ }
+ }
+ }
+ rcode = -1;
+ if (!found) {
+ printk(KERN_ERR "raw1394: arm_lock FAILED addr_entry not found"
+ " -> rcode_address_error\n");
+ spin_unlock_irqrestore(&host_info_lock, irqflags);
+ return (RCODE_ADDRESS_ERROR);
+ } else {
+ DBGMSG("arm_lock addr_entry FOUND");
+ }
+ if (rcode == -1) {
+ if (arm_addr->access_rights & ARM_LOCK) {
+ if (!(arm_addr->client_transactions & ARM_LOCK)) {
+ memcpy(&old,
+ (arm_addr->addr_space_buffer) + (addr -
+ (arm_addr->
+ start)),
+ sizeof(old));
+ switch (ext_tcode) {
+ case (EXTCODE_MASK_SWAP):
+ new = data | (old & ~arg);
+ break;
+ case (EXTCODE_COMPARE_SWAP):
+ if (old == arg) {
+ new = data;
+ } else {
+ new = old;
+ }
+ break;
+ case (EXTCODE_FETCH_ADD):
+ new =
+ cpu_to_be32(be32_to_cpu(data) +
+ be32_to_cpu(old));
+ break;
+ case (EXTCODE_LITTLE_ADD):
+ new =
+ cpu_to_le32(le32_to_cpu(data) +
+ le32_to_cpu(old));
+ break;
+ case (EXTCODE_BOUNDED_ADD):
+ if (old != arg) {
+ new =
+ cpu_to_be32(be32_to_cpu
+ (data) +
+ be32_to_cpu
+ (old));
+ } else {
+ new = old;
+ }
+ break;
+ case (EXTCODE_WRAP_ADD):
+ if (old != arg) {
+ new =
+ cpu_to_be32(be32_to_cpu
+ (data) +
+ be32_to_cpu
+ (old));
+ } else {
+ new = data;
+ }
+ break;
+ default:
+ rcode = RCODE_TYPE_ERROR; /* function not allowed */
+ printk(KERN_ERR
+ "raw1394: arm_lock FAILED "
+ "ext_tcode not allowed -> rcode_type_error\n");
+ break;
+ } /*switch */
+ if (rcode == -1) {
+ DBGMSG("arm_lock -> (rcode_complete)");
+ rcode = RCODE_COMPLETE;
+ memcpy(store, &old, sizeof(*store));
+ memcpy((arm_addr->addr_space_buffer) +
+ (addr - (arm_addr->start)),
+ &new, sizeof(*store));
+ }
+ }
+ } else {
+ rcode = RCODE_TYPE_ERROR; /* function not allowed */
+ DBGMSG("arm_lock -> rcode_type_error (access denied)");
+ }
+ }
+ if (arm_addr->notification_options & ARM_LOCK) {
+ byte_t *buf1, *buf2;
+ DBGMSG("arm_lock -> entering notification-section");
+ req = __alloc_pending_request(GFP_ATOMIC);
+ if (!req) {
+ DBGMSG("arm_lock -> rcode_conflict_error");
+ spin_unlock_irqrestore(&host_info_lock, irqflags);
+ return (RCODE_CONFLICT_ERROR); /* A resource conflict was detected.
+ The request may be retried */
+ }
+ size = sizeof(struct arm_request) + sizeof(struct arm_response) + 3 * sizeof(*store) + sizeof(struct arm_request_response); /* maximum */
+ req->data = kmalloc(size, GFP_ATOMIC);
+ if (!(req->data)) {
+ free_pending_request(req);
+ DBGMSG("arm_lock -> rcode_conflict_error");
+ spin_unlock_irqrestore(&host_info_lock, irqflags);
+ return (RCODE_CONFLICT_ERROR); /* A resource conflict was detected.
+ The request may be retried */
+ }
+ req->free_data = 1;
+ arm_req_resp = (struct arm_request_response *)(req->data);
+ arm_req = (struct arm_request *)((byte_t *) (req->data) +
+ (sizeof
+ (struct
+ arm_request_response)));
+ arm_resp =
+ (struct arm_response *)((byte_t *) (arm_req) +
+ (sizeof(struct arm_request)));
+ buf1 = (byte_t *) arm_resp + sizeof(struct arm_response);
+ buf2 = buf1 + 2 * sizeof(*store);
+ if ((ext_tcode == EXTCODE_FETCH_ADD) ||
+ (ext_tcode == EXTCODE_LITTLE_ADD)) {
+ arm_req->buffer_length = sizeof(*store);
+ memcpy(buf1, &data, sizeof(*store));
+
+ } else {
+ arm_req->buffer_length = 2 * sizeof(*store);
+ memcpy(buf1, &arg, sizeof(*store));
+ memcpy(buf1 + sizeof(*store), &data, sizeof(*store));
+ }
+ if (rcode == RCODE_COMPLETE) {
+ arm_resp->buffer_length = sizeof(*store);
+ memcpy(buf2, &old, sizeof(*store));
+ } else {
+ arm_resp->buffer_length = 0;
+ }
+ req->file_info = fi;
+ req->req.type = RAW1394_REQ_ARM;
+ req->req.generation = get_hpsb_generation(host);
+ req->req.misc = ((((sizeof(*store)) << 16) & (0xFFFF0000)) |
+ (ARM_LOCK & 0xFF));
+ req->req.tag = arm_addr->arm_tag;
+ req->req.recvb = arm_addr->recvb;
+ req->req.length = size;
+ arm_req->generation = req->req.generation;
+ arm_req->extended_transaction_code = ext_tcode;
+ arm_req->destination_offset = addr;
+ arm_req->source_nodeid = nodeid;
+ arm_req->destination_nodeid = host->node_id;
+ arm_req->tlabel = (flags >> 10) & 0x3f;
+ arm_req->tcode = (flags >> 4) & 0x0f;
+ arm_resp->response_code = rcode;
+ arm_req_resp->request = int2ptr((arm_addr->recvb) +
+ sizeof(struct
+ arm_request_response));
+ arm_req_resp->response =
+ int2ptr((arm_addr->recvb) +
+ sizeof(struct arm_request_response) +
+ sizeof(struct arm_request));
+ arm_req->buffer =
+ int2ptr((arm_addr->recvb) +
+ sizeof(struct arm_request_response) +
+ sizeof(struct arm_request) +
+ sizeof(struct arm_response));
+ arm_resp->buffer =
+ int2ptr((arm_addr->recvb) +
+ sizeof(struct arm_request_response) +
+ sizeof(struct arm_request) +
+ sizeof(struct arm_response) + 2 * sizeof(*store));
+ queue_complete_req(req);
+ }
+ spin_unlock_irqrestore(&host_info_lock, irqflags);
+ return (rcode);
+}
+
+static int arm_lock64(struct hpsb_host *host, int nodeid, octlet_t * store,
+ u64 addr, octlet_t data, octlet_t arg, int ext_tcode,
+ u16 flags)
+{
+ unsigned long irqflags;
+ struct pending_request *req;
+ struct host_info *hi;
+ struct file_info *fi = NULL;
+ struct list_head *entry;
+ struct arm_addr *arm_addr = NULL;
+ struct arm_request *arm_req = NULL;
+ struct arm_response *arm_resp = NULL;
+ int found = 0, size = 0, rcode = -1;
+ octlet_t old, new;
+ struct arm_request_response *arm_req_resp = NULL;
+
+ if (((ext_tcode & 0xFF) == EXTCODE_FETCH_ADD) ||
+ ((ext_tcode & 0xFF) == EXTCODE_LITTLE_ADD)) {
+ DBGMSG("arm_lock64 called by node: %X "
+ "addr: %4.4x %8.8x extcode: %2.2X data: %8.8X %8.8X ",
+ nodeid, (u16) ((addr >> 32) & 0xFFFF),
+ (u32) (addr & 0xFFFFFFFF),
+ ext_tcode & 0xFF,
+ (u32) ((be64_to_cpu(data) >> 32) & 0xFFFFFFFF),
+ (u32) (be64_to_cpu(data) & 0xFFFFFFFF));
+ } else {
+ DBGMSG("arm_lock64 called by node: %X "
+ "addr: %4.4x %8.8x extcode: %2.2X data: %8.8X %8.8X arg: "
+ "%8.8X %8.8X ",
+ nodeid, (u16) ((addr >> 32) & 0xFFFF),
+ (u32) (addr & 0xFFFFFFFF),
+ ext_tcode & 0xFF,
+ (u32) ((be64_to_cpu(data) >> 32) & 0xFFFFFFFF),
+ (u32) (be64_to_cpu(data) & 0xFFFFFFFF),
+ (u32) ((be64_to_cpu(arg) >> 32) & 0xFFFFFFFF),
+ (u32) (be64_to_cpu(arg) & 0xFFFFFFFF));
+ }
+ spin_lock_irqsave(&host_info_lock, irqflags);
+ hi = find_host_info(host); /* search addressentry in file_info's for host */
+ if (hi != NULL) {
+ list_for_each_entry(fi, &hi->file_info_list, list) {
+ entry = fi->addr_list.next;
+ while (entry != &(fi->addr_list)) {
+ arm_addr =
+ list_entry(entry, struct arm_addr,
+ addr_list);
+ if (((arm_addr->start) <= (addr))
+ && ((arm_addr->end) >=
+ (addr + sizeof(*store)))) {
+ found = 1;
+ break;
+ }
+ entry = entry->next;
+ }
+ if (found) {
+ break;
+ }
+ }
+ }
+ rcode = -1;
+ if (!found) {
+ printk(KERN_ERR
+ "raw1394: arm_lock64 FAILED addr_entry not found"
+ " -> rcode_address_error\n");
+ spin_unlock_irqrestore(&host_info_lock, irqflags);
+ return (RCODE_ADDRESS_ERROR);
+ } else {
+ DBGMSG("arm_lock64 addr_entry FOUND");
+ }
+ if (rcode == -1) {
+ if (arm_addr->access_rights & ARM_LOCK) {
+ if (!(arm_addr->client_transactions & ARM_LOCK)) {
+ memcpy(&old,
+ (arm_addr->addr_space_buffer) + (addr -
+ (arm_addr->
+ start)),
+ sizeof(old));
+ switch (ext_tcode) {
+ case (EXTCODE_MASK_SWAP):
+ new = data | (old & ~arg);
+ break;
+ case (EXTCODE_COMPARE_SWAP):
+ if (old == arg) {
+ new = data;
+ } else {
+ new = old;
+ }
+ break;
+ case (EXTCODE_FETCH_ADD):
+ new =
+ cpu_to_be64(be64_to_cpu(data) +
+ be64_to_cpu(old));
+ break;
+ case (EXTCODE_LITTLE_ADD):
+ new =
+ cpu_to_le64(le64_to_cpu(data) +
+ le64_to_cpu(old));
+ break;
+ case (EXTCODE_BOUNDED_ADD):
+ if (old != arg) {
+ new =
+ cpu_to_be64(be64_to_cpu
+ (data) +
+ be64_to_cpu
+ (old));
+ } else {
+ new = old;
+ }
+ break;
+ case (EXTCODE_WRAP_ADD):
+ if (old != arg) {
+ new =
+ cpu_to_be64(be64_to_cpu
+ (data) +
+ be64_to_cpu
+ (old));
+ } else {
+ new = data;
+ }
+ break;
+ default:
+ printk(KERN_ERR
+ "raw1394: arm_lock64 FAILED "
+ "ext_tcode not allowed -> rcode_type_error\n");
+ rcode = RCODE_TYPE_ERROR; /* function not allowed */
+ break;
+ } /*switch */
+ if (rcode == -1) {
+ DBGMSG
+ ("arm_lock64 -> (rcode_complete)");
+ rcode = RCODE_COMPLETE;
+ memcpy(store, &old, sizeof(*store));
+ memcpy((arm_addr->addr_space_buffer) +
+ (addr - (arm_addr->start)),
+ &new, sizeof(*store));
+ }
+ }
+ } else {
+ rcode = RCODE_TYPE_ERROR; /* function not allowed */
+ DBGMSG
+ ("arm_lock64 -> rcode_type_error (access denied)");
+ }
+ }
+ if (arm_addr->notification_options & ARM_LOCK) {
+ byte_t *buf1, *buf2;
+ DBGMSG("arm_lock64 -> entering notification-section");
+ req = __alloc_pending_request(GFP_ATOMIC);
+ if (!req) {
+ spin_unlock_irqrestore(&host_info_lock, irqflags);
+ DBGMSG("arm_lock64 -> rcode_conflict_error");
+ return (RCODE_CONFLICT_ERROR); /* A resource conflict was detected.
+ The request may be retried */
+ }
+ size = sizeof(struct arm_request) + sizeof(struct arm_response) + 3 * sizeof(*store) + sizeof(struct arm_request_response); /* maximum */
+ req->data = kmalloc(size, GFP_ATOMIC);
+ if (!(req->data)) {
+ free_pending_request(req);
+ spin_unlock_irqrestore(&host_info_lock, irqflags);
+ DBGMSG("arm_lock64 -> rcode_conflict_error");
+ return (RCODE_CONFLICT_ERROR); /* A resource conflict was detected.
+ The request may be retried */
+ }
+ req->free_data = 1;
+ arm_req_resp = (struct arm_request_response *)(req->data);
+ arm_req = (struct arm_request *)((byte_t *) (req->data) +
+ (sizeof
+ (struct
+ arm_request_response)));
+ arm_resp =
+ (struct arm_response *)((byte_t *) (arm_req) +
+ (sizeof(struct arm_request)));
+ buf1 = (byte_t *) arm_resp + sizeof(struct arm_response);
+ buf2 = buf1 + 2 * sizeof(*store);
+ if ((ext_tcode == EXTCODE_FETCH_ADD) ||
+ (ext_tcode == EXTCODE_LITTLE_ADD)) {
+ arm_req->buffer_length = sizeof(*store);
+ memcpy(buf1, &data, sizeof(*store));
+
+ } else {
+ arm_req->buffer_length = 2 * sizeof(*store);
+ memcpy(buf1, &arg, sizeof(*store));
+ memcpy(buf1 + sizeof(*store), &data, sizeof(*store));
+ }
+ if (rcode == RCODE_COMPLETE) {
+ arm_resp->buffer_length = sizeof(*store);
+ memcpy(buf2, &old, sizeof(*store));
+ } else {
+ arm_resp->buffer_length = 0;
+ }
+ req->file_info = fi;
+ req->req.type = RAW1394_REQ_ARM;
+ req->req.generation = get_hpsb_generation(host);
+ req->req.misc = ((((sizeof(*store)) << 16) & (0xFFFF0000)) |
+ (ARM_LOCK & 0xFF));
+ req->req.tag = arm_addr->arm_tag;
+ req->req.recvb = arm_addr->recvb;
+ req->req.length = size;
+ arm_req->generation = req->req.generation;
+ arm_req->extended_transaction_code = ext_tcode;
+ arm_req->destination_offset = addr;
+ arm_req->source_nodeid = nodeid;
+ arm_req->destination_nodeid = host->node_id;
+ arm_req->tlabel = (flags >> 10) & 0x3f;
+ arm_req->tcode = (flags >> 4) & 0x0f;
+ arm_resp->response_code = rcode;
+ arm_req_resp->request = int2ptr((arm_addr->recvb) +
+ sizeof(struct
+ arm_request_response));
+ arm_req_resp->response =
+ int2ptr((arm_addr->recvb) +
+ sizeof(struct arm_request_response) +
+ sizeof(struct arm_request));
+ arm_req->buffer =
+ int2ptr((arm_addr->recvb) +
+ sizeof(struct arm_request_response) +
+ sizeof(struct arm_request) +
+ sizeof(struct arm_response));
+ arm_resp->buffer =
+ int2ptr((arm_addr->recvb) +
+ sizeof(struct arm_request_response) +
+ sizeof(struct arm_request) +
+ sizeof(struct arm_response) + 2 * sizeof(*store));
+ queue_complete_req(req);
+ }
+ spin_unlock_irqrestore(&host_info_lock, irqflags);
+ return (rcode);
+}
+
+static int arm_register(struct file_info *fi, struct pending_request *req)
+{
+ int retval;
+ struct arm_addr *addr;
+ struct host_info *hi;
+ struct file_info *fi_hlp = NULL;
+ struct list_head *entry;
+ struct arm_addr *arm_addr = NULL;
+ int same_host, another_host;
+ unsigned long flags;
+
+ DBGMSG("arm_register called "
+ "addr(Offset): %8.8x %8.8x length: %u "
+ "rights: %2.2X notify: %2.2X "
+ "max_blk_len: %4.4X",
+ (u32) ((req->req.address >> 32) & 0xFFFF),
+ (u32) (req->req.address & 0xFFFFFFFF),
+ req->req.length, ((req->req.misc >> 8) & 0xFF),
+ (req->req.misc & 0xFF), ((req->req.misc >> 16) & 0xFFFF));
+ /* check addressrange */
+ if ((((req->req.address) & ~(0xFFFFFFFFFFFFULL)) != 0) ||
+ (((req->req.address + req->req.length) & ~(0xFFFFFFFFFFFFULL)) !=
+ 0)) {
+ req->req.length = 0;
+ return (-EINVAL);
+ }
+ /* addr-list-entry for fileinfo */
+ addr = kmalloc(sizeof(*addr), GFP_KERNEL);
+ if (!addr) {
+ req->req.length = 0;
+ return (-ENOMEM);
+ }
+ /* allocation of addr_space_buffer */
+ addr->addr_space_buffer = vmalloc(req->req.length);
+ if (!(addr->addr_space_buffer)) {
+ kfree(addr);
+ req->req.length = 0;
+ return (-ENOMEM);
+ }
+ /* initialization of addr_space_buffer */
+ if ((req->req.sendb) == (unsigned long)NULL) {
+ /* init: set 0 */
+ memset(addr->addr_space_buffer, 0, req->req.length);
+ } else {
+ /* init: user -> kernel */
+ if (copy_from_user
+ (addr->addr_space_buffer, int2ptr(req->req.sendb),
+ req->req.length)) {
+ vfree(addr->addr_space_buffer);
+ kfree(addr);
+ return (-EFAULT);
+ }
+ }
+ INIT_LIST_HEAD(&addr->addr_list);
+ addr->arm_tag = req->req.tag;
+ addr->start = req->req.address;
+ addr->end = req->req.address + req->req.length;
+ addr->access_rights = (u8) (req->req.misc & 0x0F);
+ addr->notification_options = (u8) ((req->req.misc >> 4) & 0x0F);
+ addr->client_transactions = (u8) ((req->req.misc >> 8) & 0x0F);
+ addr->access_rights |= addr->client_transactions;
+ addr->notification_options |= addr->client_transactions;
+ addr->recvb = req->req.recvb;
+ addr->rec_length = (u16) ((req->req.misc >> 16) & 0xFFFF);
+
+ spin_lock_irqsave(&host_info_lock, flags);
+ hi = find_host_info(fi->host);
+ same_host = 0;
+ another_host = 0;
+ /* same host with address-entry containing same addressrange ? */
+ list_for_each_entry(fi_hlp, &hi->file_info_list, list) {
+ entry = fi_hlp->addr_list.next;
+ while (entry != &(fi_hlp->addr_list)) {
+ arm_addr =
+ list_entry(entry, struct arm_addr, addr_list);
+ if ((arm_addr->start == addr->start)
+ && (arm_addr->end == addr->end)) {
+ DBGMSG("same host ownes same "
+ "addressrange -> EALREADY");
+ same_host = 1;
+ break;
+ }
+ entry = entry->next;
+ }
+ if (same_host) {
+ break;
+ }
+ }
+ if (same_host) {
+ /* addressrange occupied by same host */
+ spin_unlock_irqrestore(&host_info_lock, flags);
+ vfree(addr->addr_space_buffer);
+ kfree(addr);
+ return (-EALREADY);
+ }
+ /* another host with valid address-entry containing same addressrange */
+ list_for_each_entry(hi, &host_info_list, list) {
+ if (hi->host != fi->host) {
+ list_for_each_entry(fi_hlp, &hi->file_info_list, list) {
+ entry = fi_hlp->addr_list.next;
+ while (entry != &(fi_hlp->addr_list)) {
+ arm_addr =
+ list_entry(entry, struct arm_addr,
+ addr_list);
+ if ((arm_addr->start == addr->start)
+ && (arm_addr->end == addr->end)) {
+ DBGMSG
+ ("another host ownes same "
+ "addressrange");
+ another_host = 1;
+ break;
+ }
+ entry = entry->next;
+ }
+ if (another_host) {
+ break;
+ }
+ }
+ }
+ }
+ spin_unlock_irqrestore(&host_info_lock, flags);
+
+ if (another_host) {
+ DBGMSG("another hosts entry is valid -> SUCCESS");
+ if (copy_to_user(int2ptr(req->req.recvb),
+ &addr->start, sizeof(u64))) {
+ printk(KERN_ERR "raw1394: arm_register failed "
+ " address-range-entry is invalid -> EFAULT !!!\n");
+ vfree(addr->addr_space_buffer);
+ kfree(addr);
+ return (-EFAULT);
+ }
+ free_pending_request(req); /* immediate success or fail */
+ /* INSERT ENTRY */
+ spin_lock_irqsave(&host_info_lock, flags);
+ list_add_tail(&addr->addr_list, &fi->addr_list);
+ spin_unlock_irqrestore(&host_info_lock, flags);
+ return 0;
+ }
+ retval =
+ hpsb_register_addrspace(&raw1394_highlevel, fi->host, &arm_ops,
+ req->req.address,
+ req->req.address + req->req.length);
+ if (retval) {
+ /* INSERT ENTRY */
+ spin_lock_irqsave(&host_info_lock, flags);
+ list_add_tail(&addr->addr_list, &fi->addr_list);
+ spin_unlock_irqrestore(&host_info_lock, flags);
+ } else {
+ DBGMSG("arm_register failed errno: %d \n", retval);
+ vfree(addr->addr_space_buffer);
+ kfree(addr);
+ return (-EALREADY);
+ }
+ free_pending_request(req); /* immediate success or fail */
+ return 0;
+}
+
+static int arm_unregister(struct file_info *fi, struct pending_request *req)
+{
+ int found = 0;
+ int retval = 0;
+ struct list_head *entry;
+ struct arm_addr *addr = NULL;
+ struct host_info *hi;
+ struct file_info *fi_hlp = NULL;
+ struct arm_addr *arm_addr = NULL;
+ int another_host;
+ unsigned long flags;
+
+ DBGMSG("arm_Unregister called addr(Offset): "
+ "%8.8x %8.8x",
+ (u32) ((req->req.address >> 32) & 0xFFFF),
+ (u32) (req->req.address & 0xFFFFFFFF));
+ spin_lock_irqsave(&host_info_lock, flags);
+ /* get addr */
+ entry = fi->addr_list.next;
+ while (entry != &(fi->addr_list)) {
+ addr = list_entry(entry, struct arm_addr, addr_list);
+ if (addr->start == req->req.address) {
+ found = 1;
+ break;
+ }
+ entry = entry->next;
+ }
+ if (!found) {
+ DBGMSG("arm_Unregister addr not found");
+ spin_unlock_irqrestore(&host_info_lock, flags);
+ return (-EINVAL);
+ }
+ DBGMSG("arm_Unregister addr found");
+ another_host = 0;
+ /* another host with valid address-entry containing
+ same addressrange */
+ list_for_each_entry(hi, &host_info_list, list) {
+ if (hi->host != fi->host) {
+ list_for_each_entry(fi_hlp, &hi->file_info_list, list) {
+ entry = fi_hlp->addr_list.next;
+ while (entry != &(fi_hlp->addr_list)) {
+ arm_addr = list_entry(entry,
+ struct arm_addr,
+ addr_list);
+ if (arm_addr->start == addr->start) {
+ DBGMSG("another host ownes "
+ "same addressrange");
+ another_host = 1;
+ break;
+ }
+ entry = entry->next;
+ }
+ if (another_host) {
+ break;
+ }
+ }
+ }
+ }
+ if (another_host) {
+ DBGMSG("delete entry from list -> success");
+ list_del(&addr->addr_list);
+ spin_unlock_irqrestore(&host_info_lock, flags);
+ vfree(addr->addr_space_buffer);
+ kfree(addr);
+ free_pending_request(req); /* immediate success or fail */
+ return 0;
+ }
+ retval =
+ hpsb_unregister_addrspace(&raw1394_highlevel, fi->host,
+ addr->start);
+ if (!retval) {
+ printk(KERN_ERR "raw1394: arm_Unregister failed -> EINVAL\n");
+ spin_unlock_irqrestore(&host_info_lock, flags);
+ return (-EINVAL);
+ }
+ DBGMSG("delete entry from list -> success");
+ list_del(&addr->addr_list);
+ spin_unlock_irqrestore(&host_info_lock, flags);
+ vfree(addr->addr_space_buffer);
+ kfree(addr);
+ free_pending_request(req); /* immediate success or fail */
+ return 0;
+}
+
+/* Copy data from ARM buffer(s) to user buffer. */
+static int arm_get_buf(struct file_info *fi, struct pending_request *req)
+{
+ struct arm_addr *arm_addr = NULL;
+ unsigned long flags;
+ unsigned long offset;
+
+ struct list_head *entry;
+
+ DBGMSG("arm_get_buf "
+ "addr(Offset): %04X %08X length: %u",
+ (u32) ((req->req.address >> 32) & 0xFFFF),
+ (u32) (req->req.address & 0xFFFFFFFF), (u32) req->req.length);
+
+ spin_lock_irqsave(&host_info_lock, flags);
+ entry = fi->addr_list.next;
+ while (entry != &(fi->addr_list)) {
+ arm_addr = list_entry(entry, struct arm_addr, addr_list);
+ if ((arm_addr->start <= req->req.address) &&
+ (arm_addr->end > req->req.address)) {
+ if (req->req.address + req->req.length <= arm_addr->end) {
+ offset = req->req.address - arm_addr->start;
+ spin_unlock_irqrestore(&host_info_lock, flags);
+
+ DBGMSG
+ ("arm_get_buf copy_to_user( %08X, %p, %u )",
+ (u32) req->req.recvb,
+ arm_addr->addr_space_buffer + offset,
+ (u32) req->req.length);
+ if (copy_to_user
+ (int2ptr(req->req.recvb),
+ arm_addr->addr_space_buffer + offset,
+ req->req.length))
+ return (-EFAULT);
+
+ /* We have to free the request, because we
+ * queue no response, and therefore nobody
+ * will free it. */
+ free_pending_request(req);
+ return 0;
+ } else {
+ DBGMSG("arm_get_buf request exceeded mapping");
+ spin_unlock_irqrestore(&host_info_lock, flags);
+ return (-EINVAL);
+ }
+ }
+ entry = entry->next;
+ }
+ spin_unlock_irqrestore(&host_info_lock, flags);
+ return (-EINVAL);
+}
+
+/* Copy data from user buffer to ARM buffer(s). */
+static int arm_set_buf(struct file_info *fi, struct pending_request *req)
+{
+ struct arm_addr *arm_addr = NULL;
+ unsigned long flags;
+ unsigned long offset;
+
+ struct list_head *entry;
+
+ DBGMSG("arm_set_buf "
+ "addr(Offset): %04X %08X length: %u",
+ (u32) ((req->req.address >> 32) & 0xFFFF),
+ (u32) (req->req.address & 0xFFFFFFFF), (u32) req->req.length);
+
+ spin_lock_irqsave(&host_info_lock, flags);
+ entry = fi->addr_list.next;
+ while (entry != &(fi->addr_list)) {
+ arm_addr = list_entry(entry, struct arm_addr, addr_list);
+ if ((arm_addr->start <= req->req.address) &&
+ (arm_addr->end > req->req.address)) {
+ if (req->req.address + req->req.length <= arm_addr->end) {
+ offset = req->req.address - arm_addr->start;
+ spin_unlock_irqrestore(&host_info_lock, flags);
+
+ DBGMSG
+ ("arm_set_buf copy_from_user( %p, %08X, %u )",
+ arm_addr->addr_space_buffer + offset,
+ (u32) req->req.sendb,
+ (u32) req->req.length);
+ if (copy_from_user
+ (arm_addr->addr_space_buffer + offset,
+ int2ptr(req->req.sendb),
+ req->req.length))
+ return (-EFAULT);
+
+ /* We have to free the request, because we
+ * queue no response, and therefore nobody
+ * will free it. */
+ free_pending_request(req);
+ return 0;
+ } else {
+ DBGMSG("arm_set_buf request exceeded mapping");
+ spin_unlock_irqrestore(&host_info_lock, flags);
+ return (-EINVAL);
+ }
+ }
+ entry = entry->next;
+ }
+ spin_unlock_irqrestore(&host_info_lock, flags);
+ return (-EINVAL);
+}
+
+static int reset_notification(struct file_info *fi, struct pending_request *req)
+{
+ DBGMSG("reset_notification called - switch %s ",
+ (req->req.misc == RAW1394_NOTIFY_OFF) ? "OFF" : "ON");
+ if ((req->req.misc == RAW1394_NOTIFY_OFF) ||
+ (req->req.misc == RAW1394_NOTIFY_ON)) {
+ fi->notification = (u8) req->req.misc;
+ free_pending_request(req); /* we have to free the request, because we queue no response, and therefore nobody will free it */
+ return 0;
+ }
+ /* error EINVAL (22) invalid argument */
+ return (-EINVAL);
+}
+
+static int write_phypacket(struct file_info *fi, struct pending_request *req)
+{
+ struct hpsb_packet *packet = NULL;
+ int retval = 0;
+ quadlet_t data;
+ unsigned long flags;
+
+ data = be32_to_cpu((u32) req->req.sendb);
+ DBGMSG("write_phypacket called - quadlet 0x%8.8x ", data);
+ packet = hpsb_make_phypacket(fi->host, data);
+ if (!packet)
+ return -ENOMEM;
+ req->req.length = 0;
+ req->packet = packet;
+ hpsb_set_packet_complete_task(packet,
+ (void (*)(void *))queue_complete_cb, req);
+ spin_lock_irqsave(&fi->reqlists_lock, flags);
+ list_add_tail(&req->list, &fi->req_pending);
+ spin_unlock_irqrestore(&fi->reqlists_lock, flags);
+ packet->generation = req->req.generation;
+ retval = hpsb_send_packet(packet);
+ DBGMSG("write_phypacket send_packet called => retval: %d ", retval);
+ if (retval < 0) {
+ req->req.error = RAW1394_ERROR_SEND_ERROR;
+ req->req.length = 0;
+ queue_complete_req(req);
+ }
+ return 0;
+}
+
+static int get_config_rom(struct file_info *fi, struct pending_request *req)
+{
+ int ret = 0;
+ quadlet_t *data = kmalloc(req->req.length, GFP_KERNEL);
+ int status;
+
+ if (!data)
+ return -ENOMEM;
+
+ status =
+ csr1212_read(fi->host->csr.rom, CSR1212_CONFIG_ROM_SPACE_OFFSET,
+ data, req->req.length);
+ if (copy_to_user(int2ptr(req->req.recvb), data, req->req.length))
+ ret = -EFAULT;
+ if (copy_to_user
+ (int2ptr(req->req.tag), &fi->host->csr.rom->cache_head->len,
+ sizeof(fi->host->csr.rom->cache_head->len)))
+ ret = -EFAULT;
+ if (copy_to_user(int2ptr(req->req.address), &fi->host->csr.generation,
+ sizeof(fi->host->csr.generation)))
+ ret = -EFAULT;
+ if (copy_to_user(int2ptr(req->req.sendb), &status, sizeof(status)))
+ ret = -EFAULT;
+ kfree(data);
+ if (ret >= 0) {
+ free_pending_request(req); /* we have to free the request, because we queue no response, and therefore nobody will free it */
+ }
+ return ret;
+}
+
+static int update_config_rom(struct file_info *fi, struct pending_request *req)
+{
+ int ret = 0;
+ quadlet_t *data = kmalloc(req->req.length, GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+ if (copy_from_user(data, int2ptr(req->req.sendb), req->req.length)) {
+ ret = -EFAULT;
+ } else {
+ int status = hpsb_update_config_rom(fi->host,
+ data, req->req.length,
+ (unsigned char)req->req.
+ misc);
+ if (copy_to_user
+ (int2ptr(req->req.recvb), &status, sizeof(status)))
+ ret = -ENOMEM;
+ }
+ kfree(data);
+ if (ret >= 0) {
+ free_pending_request(req); /* we have to free the request, because we queue no response, and therefore nobody will free it */
+ fi->cfgrom_upd = 1;
+ }
+ return ret;
+}
+
+static int modify_config_rom(struct file_info *fi, struct pending_request *req)
+{
+ struct csr1212_keyval *kv;
+ struct csr1212_csr_rom_cache *cache;
+ struct csr1212_dentry *dentry;
+ u32 dr;
+ int ret = 0;
+
+ if (req->req.misc == ~0) {
+ if (req->req.length == 0)
+ return -EINVAL;
+
+ /* Find an unused slot */
+ for (dr = 0;
+ dr < RAW1394_MAX_USER_CSR_DIRS && fi->csr1212_dirs[dr];
+ dr++) ;
+
+ if (dr == RAW1394_MAX_USER_CSR_DIRS)
+ return -ENOMEM;
+
+ fi->csr1212_dirs[dr] =
+ csr1212_new_directory(CSR1212_KV_ID_VENDOR);
+ if (!fi->csr1212_dirs[dr])
+ return -ENOMEM;
+ } else {
+ dr = req->req.misc;
+ if (!fi->csr1212_dirs[dr])
+ return -EINVAL;
+
+ /* Delete old stuff */
+ for (dentry =
+ fi->csr1212_dirs[dr]->value.directory.dentries_head;
+ dentry; dentry = dentry->next) {
+ csr1212_detach_keyval_from_directory(fi->host->csr.rom->
+ root_kv,
+ dentry->kv);
+ }
+
+ if (req->req.length == 0) {
+ csr1212_release_keyval(fi->csr1212_dirs[dr]);
+ fi->csr1212_dirs[dr] = NULL;
+
+ hpsb_update_config_rom_image(fi->host);
+ free_pending_request(req);
+ return 0;
+ }
+ }
+
+ cache = csr1212_rom_cache_malloc(0, req->req.length);
+ if (!cache) {
+ csr1212_release_keyval(fi->csr1212_dirs[dr]);
+ fi->csr1212_dirs[dr] = NULL;
+ return -ENOMEM;
+ }
+
+ cache->filled_head = kmalloc(sizeof(*cache->filled_head), GFP_KERNEL);
+ if (!cache->filled_head) {
+ csr1212_release_keyval(fi->csr1212_dirs[dr]);
+ fi->csr1212_dirs[dr] = NULL;
+ CSR1212_FREE(cache);
+ return -ENOMEM;
+ }
+ cache->filled_tail = cache->filled_head;
+
+ if (copy_from_user(cache->data, int2ptr(req->req.sendb),
+ req->req.length)) {
+ csr1212_release_keyval(fi->csr1212_dirs[dr]);
+ fi->csr1212_dirs[dr] = NULL;
+ ret = -EFAULT;
+ } else {
+ cache->len = req->req.length;
+ cache->filled_head->offset_start = 0;
+ cache->filled_head->offset_end = cache->size - 1;
+
+ cache->layout_head = cache->layout_tail = fi->csr1212_dirs[dr];
+
+ ret = CSR1212_SUCCESS;
+ /* parse all the items */
+ for (kv = cache->layout_head; ret == CSR1212_SUCCESS && kv;
+ kv = kv->next) {
+ ret = csr1212_parse_keyval(kv, cache);
+ }
+
+ /* attach top level items to the root directory */
+ for (dentry =
+ fi->csr1212_dirs[dr]->value.directory.dentries_head;
+ ret == CSR1212_SUCCESS && dentry; dentry = dentry->next) {
+ ret =
+ csr1212_attach_keyval_to_directory(fi->host->csr.
+ rom->root_kv,
+ dentry->kv);
+ }
+
+ if (ret == CSR1212_SUCCESS) {
+ ret = hpsb_update_config_rom_image(fi->host);
+
+ if (ret >= 0 && copy_to_user(int2ptr(req->req.recvb),
+ &dr, sizeof(dr))) {
+ ret = -ENOMEM;
+ }
+ }
+ }
+ kfree(cache->filled_head);
+ CSR1212_FREE(cache);
+
+ if (ret >= 0) {
+ /* we have to free the request, because we queue no response,
+ * and therefore nobody will free it */
+ free_pending_request(req);
+ return 0;
+ } else {
+ for (dentry =
+ fi->csr1212_dirs[dr]->value.directory.dentries_head;
+ dentry; dentry = dentry->next) {
+ csr1212_detach_keyval_from_directory(fi->host->csr.rom->
+ root_kv,
+ dentry->kv);
+ }
+ csr1212_release_keyval(fi->csr1212_dirs[dr]);
+ fi->csr1212_dirs[dr] = NULL;
+ return ret;
+ }
+}
+
+static int state_connected(struct file_info *fi, struct pending_request *req)
+{
+ int node = req->req.address >> 48;
+
+ req->req.error = RAW1394_ERROR_NONE;
+
+ switch (req->req.type) {
+
+ case RAW1394_REQ_ECHO:
+ queue_complete_req(req);
+ return 0;
+
+ case RAW1394_REQ_ARM_REGISTER:
+ return arm_register(fi, req);
+
+ case RAW1394_REQ_ARM_UNREGISTER:
+ return arm_unregister(fi, req);
+
+ case RAW1394_REQ_ARM_SET_BUF:
+ return arm_set_buf(fi, req);
+
+ case RAW1394_REQ_ARM_GET_BUF:
+ return arm_get_buf(fi, req);
+
+ case RAW1394_REQ_RESET_NOTIFY:
+ return reset_notification(fi, req);
+
+ case RAW1394_REQ_ISO_SEND:
+ case RAW1394_REQ_ISO_LISTEN:
+ printk(KERN_DEBUG "raw1394: old iso ABI has been removed\n");
+ req->req.error = RAW1394_ERROR_COMPAT;
+ req->req.misc = RAW1394_KERNELAPI_VERSION;
+ queue_complete_req(req);
+ return 0;
+
+ case RAW1394_REQ_FCP_LISTEN:
+ handle_fcp_listen(fi, req);
+ return 0;
+
+ case RAW1394_REQ_RESET_BUS:
+ if (req->req.misc == RAW1394_LONG_RESET) {
+ DBGMSG("busreset called (type: LONG)");
+ hpsb_reset_bus(fi->host, LONG_RESET);
+ free_pending_request(req); /* we have to free the request, because we queue no response, and therefore nobody will free it */
+ return 0;
+ }
+ if (req->req.misc == RAW1394_SHORT_RESET) {
+ DBGMSG("busreset called (type: SHORT)");
+ hpsb_reset_bus(fi->host, SHORT_RESET);
+ free_pending_request(req); /* we have to free the request, because we queue no response, and therefore nobody will free it */
+ return 0;
+ }
+ /* error EINVAL (22) invalid argument */
+ return (-EINVAL);
+ case RAW1394_REQ_GET_ROM:
+ return get_config_rom(fi, req);
+
+ case RAW1394_REQ_UPDATE_ROM:
+ return update_config_rom(fi, req);
+
+ case RAW1394_REQ_MODIFY_ROM:
+ return modify_config_rom(fi, req);
+ }
+
+ if (req->req.generation != get_hpsb_generation(fi->host)) {
+ req->req.error = RAW1394_ERROR_GENERATION;
+ req->req.generation = get_hpsb_generation(fi->host);
+ req->req.length = 0;
+ queue_complete_req(req);
+ return 0;
+ }
+
+ switch (req->req.type) {
+ case RAW1394_REQ_PHYPACKET:
+ return write_phypacket(fi, req);
+ case RAW1394_REQ_ASYNC_SEND:
+ return handle_async_send(fi, req);
+ }
+
+ if (req->req.length == 0) {
+ req->req.error = RAW1394_ERROR_INVALID_ARG;
+ queue_complete_req(req);
+ return 0;
+ }
+
+ return handle_async_request(fi, req, node);
+}
+
+static ssize_t raw1394_write(struct file *file, const char __user * buffer,
+ size_t count, loff_t * offset_is_ignored)
+{
+ struct file_info *fi = (struct file_info *)file->private_data;
+ struct pending_request *req;
+ ssize_t retval = -EBADFD;
+
+#ifdef CONFIG_COMPAT
+ if (count == sizeof(struct compat_raw1394_req) &&
+ sizeof(struct compat_raw1394_req) !=
+ sizeof(struct raw1394_request)) {
+ buffer = raw1394_compat_write(buffer);
+ if (IS_ERR(buffer))
+ return PTR_ERR(buffer);
+ } else
+#endif
+ if (count != sizeof(struct raw1394_request)) {
+ return -EINVAL;
+ }
+
+ req = alloc_pending_request();
+ if (req == NULL) {
+ return -ENOMEM;
+ }
+ req->file_info = fi;
+
+ if (copy_from_user(&req->req, buffer, sizeof(struct raw1394_request))) {
+ free_pending_request(req);
+ return -EFAULT;
+ }
+
+ if (!mutex_trylock(&fi->state_mutex))
+ return -EAGAIN;
+
+ switch (fi->state) {
+ case opened:
+ retval = state_opened(fi, req);
+ break;
+
+ case initialized:
+ retval = state_initialized(fi, req);
+ break;
+
+ case connected:
+ retval = state_connected(fi, req);
+ break;
+ }
+
+ mutex_unlock(&fi->state_mutex);
+
+ if (retval < 0) {
+ free_pending_request(req);
+ } else {
+ BUG_ON(retval);
+ retval = count;
+ }
+
+ return retval;
+}
+
+/* rawiso operations */
+
+/* check if any RAW1394_REQ_RAWISO_ACTIVITY event is already in the
+ * completion queue (reqlists_lock must be taken) */
+static inline int __rawiso_event_in_queue(struct file_info *fi)
+{
+ struct pending_request *req;
+
+ list_for_each_entry(req, &fi->req_complete, list)
+ if (req->req.type == RAW1394_REQ_RAWISO_ACTIVITY)
+ return 1;
+
+ return 0;
+}
+
+/* put a RAWISO_ACTIVITY event in the queue, if one isn't there already */
+static void queue_rawiso_event(struct file_info *fi)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&fi->reqlists_lock, flags);
+
+ /* only one ISO activity event may be in the queue */
+ if (!__rawiso_event_in_queue(fi)) {
+ struct pending_request *req =
+ __alloc_pending_request(GFP_ATOMIC);
+
+ if (req) {
+ req->file_info = fi;
+ req->req.type = RAW1394_REQ_RAWISO_ACTIVITY;
+ req->req.generation = get_hpsb_generation(fi->host);
+ __queue_complete_req(req);
+ } else {
+ /* on allocation failure, signal an overflow */
+ if (fi->iso_handle) {
+ atomic_inc(&fi->iso_handle->overflows);
+ }
+ }
+ }
+ spin_unlock_irqrestore(&fi->reqlists_lock, flags);
+}
+
+static void rawiso_activity_cb(struct hpsb_iso *iso)
+{
+ unsigned long flags;
+ struct host_info *hi;
+ struct file_info *fi;
+
+ spin_lock_irqsave(&host_info_lock, flags);
+ hi = find_host_info(iso->host);
+
+ if (hi != NULL) {
+ list_for_each_entry(fi, &hi->file_info_list, list) {
+ if (fi->iso_handle == iso)
+ queue_rawiso_event(fi);
+ }
+ }
+
+ spin_unlock_irqrestore(&host_info_lock, flags);
+}
+
+/* helper function - gather all the kernel iso status bits for returning to user-space */
+static void raw1394_iso_fill_status(struct hpsb_iso *iso,
+ struct raw1394_iso_status *stat)
+{
+ int overflows = atomic_read(&iso->overflows);
+ int skips = atomic_read(&iso->skips);
+
+ stat->config.data_buf_size = iso->buf_size;
+ stat->config.buf_packets = iso->buf_packets;
+ stat->config.channel = iso->channel;
+ stat->config.speed = iso->speed;
+ stat->config.irq_interval = iso->irq_interval;
+ stat->n_packets = hpsb_iso_n_ready(iso);
+ stat->overflows = ((skips & 0xFFFF) << 16) | ((overflows & 0xFFFF));
+ stat->xmit_cycle = iso->xmit_cycle;
+}
+
+static int raw1394_iso_xmit_init(struct file_info *fi, void __user * uaddr)
+{
+ struct raw1394_iso_status stat;
+
+ if (!fi->host)
+ return -EINVAL;
+
+ if (copy_from_user(&stat, uaddr, sizeof(stat)))
+ return -EFAULT;
+
+ fi->iso_handle = hpsb_iso_xmit_init(fi->host,
+ stat.config.data_buf_size,
+ stat.config.buf_packets,
+ stat.config.channel,
+ stat.config.speed,
+ stat.config.irq_interval,
+ rawiso_activity_cb);
+ if (!fi->iso_handle)
+ return -ENOMEM;
+
+ fi->iso_state = RAW1394_ISO_XMIT;
+
+ raw1394_iso_fill_status(fi->iso_handle, &stat);
+ if (copy_to_user(uaddr, &stat, sizeof(stat)))
+ return -EFAULT;
+
+ /* queue an event to get things started */
+ rawiso_activity_cb(fi->iso_handle);
+
+ return 0;
+}
+
+static int raw1394_iso_recv_init(struct file_info *fi, void __user * uaddr)
+{
+ struct raw1394_iso_status stat;
+
+ if (!fi->host)
+ return -EINVAL;
+
+ if (copy_from_user(&stat, uaddr, sizeof(stat)))
+ return -EFAULT;
+
+ fi->iso_handle = hpsb_iso_recv_init(fi->host,
+ stat.config.data_buf_size,
+ stat.config.buf_packets,
+ stat.config.channel,
+ stat.config.dma_mode,
+ stat.config.irq_interval,
+ rawiso_activity_cb);
+ if (!fi->iso_handle)
+ return -ENOMEM;
+
+ fi->iso_state = RAW1394_ISO_RECV;
+
+ raw1394_iso_fill_status(fi->iso_handle, &stat);
+ if (copy_to_user(uaddr, &stat, sizeof(stat)))
+ return -EFAULT;
+ return 0;
+}
+
+static int raw1394_iso_get_status(struct file_info *fi, void __user * uaddr)
+{
+ struct raw1394_iso_status stat;
+ struct hpsb_iso *iso = fi->iso_handle;
+
+ raw1394_iso_fill_status(fi->iso_handle, &stat);
+ if (copy_to_user(uaddr, &stat, sizeof(stat)))
+ return -EFAULT;
+
+ /* reset overflow counter */
+ atomic_set(&iso->overflows, 0);
+ /* reset skip counter */
+ atomic_set(&iso->skips, 0);
+
+ return 0;
+}
+
+/* copy N packet_infos out of the ringbuffer into user-supplied array */
+static int raw1394_iso_recv_packets(struct file_info *fi, void __user * uaddr)
+{
+ struct raw1394_iso_packets upackets;
+ unsigned int packet = fi->iso_handle->first_packet;
+ int i;
+
+ if (copy_from_user(&upackets, uaddr, sizeof(upackets)))
+ return -EFAULT;
+
+ if (upackets.n_packets > hpsb_iso_n_ready(fi->iso_handle))
+ return -EINVAL;
+
+ /* ensure user-supplied buffer is accessible and big enough */
+ if (!access_ok(VERIFY_WRITE, upackets.infos,
+ upackets.n_packets *
+ sizeof(struct raw1394_iso_packet_info)))
+ return -EFAULT;
+
+ /* copy the packet_infos out */
+ for (i = 0; i < upackets.n_packets; i++) {
+ if (__copy_to_user(&upackets.infos[i],
+ &fi->iso_handle->infos[packet],
+ sizeof(struct raw1394_iso_packet_info)))
+ return -EFAULT;
+
+ packet = (packet + 1) % fi->iso_handle->buf_packets;
+ }
+
+ return 0;
+}
+
+/* copy N packet_infos from user to ringbuffer, and queue them for transmission */
+static int raw1394_iso_send_packets(struct file_info *fi, void __user * uaddr)
+{
+ struct raw1394_iso_packets upackets;
+ int i, rv;
+
+ if (copy_from_user(&upackets, uaddr, sizeof(upackets)))
+ return -EFAULT;
+
+ if (upackets.n_packets >= fi->iso_handle->buf_packets)
+ return -EINVAL;
+
+ if (upackets.n_packets >= hpsb_iso_n_ready(fi->iso_handle))
+ return -EAGAIN;
+
+ /* ensure user-supplied buffer is accessible and big enough */
+ if (!access_ok(VERIFY_READ, upackets.infos,
+ upackets.n_packets *
+ sizeof(struct raw1394_iso_packet_info)))
+ return -EFAULT;
+
+ /* copy the infos structs in and queue the packets */
+ for (i = 0; i < upackets.n_packets; i++) {
+ struct raw1394_iso_packet_info info;
+
+ if (__copy_from_user(&info, &upackets.infos[i],
+ sizeof(struct raw1394_iso_packet_info)))
+ return -EFAULT;
+
+ rv = hpsb_iso_xmit_queue_packet(fi->iso_handle, info.offset,
+ info.len, info.tag, info.sy);
+ if (rv)
+ return rv;
+ }
+
+ return 0;
+}
+
+static void raw1394_iso_shutdown(struct file_info *fi)
+{
+ if (fi->iso_handle)
+ hpsb_iso_shutdown(fi->iso_handle);
+
+ fi->iso_handle = NULL;
+ fi->iso_state = RAW1394_ISO_INACTIVE;
+}
+
+static int raw1394_read_cycle_timer(struct file_info *fi, void __user * uaddr)
+{
+ struct raw1394_cycle_timer ct;
+ int err;
+
+ err = hpsb_read_cycle_timer(fi->host, &ct.cycle_timer, &ct.local_time);
+ if (!err)
+ if (copy_to_user(uaddr, &ct, sizeof(ct)))
+ err = -EFAULT;
+ return err;
+}
+
+/* mmap the rawiso xmit/recv buffer */
+static int raw1394_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct file_info *fi = file->private_data;
+ int ret;
+
+ if (!mutex_trylock(&fi->state_mutex))
+ return -EAGAIN;
+
+ if (fi->iso_state == RAW1394_ISO_INACTIVE)
+ ret = -EINVAL;
+ else
+ ret = dma_region_mmap(&fi->iso_handle->data_buf, file, vma);
+
+ mutex_unlock(&fi->state_mutex);
+
+ return ret;
+}
+
+static long raw1394_ioctl_inactive(struct file_info *fi, unsigned int cmd,
+ void __user *argp)
+{
+ switch (cmd) {
+ case RAW1394_IOC_ISO_XMIT_INIT:
+ return raw1394_iso_xmit_init(fi, argp);
+ case RAW1394_IOC_ISO_RECV_INIT:
+ return raw1394_iso_recv_init(fi, argp);
+ default:
+ return -EINVAL;
+ }
+}
+
+static long raw1394_ioctl_recv(struct file_info *fi, unsigned int cmd,
+ unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+
+ switch (cmd) {
+ case RAW1394_IOC_ISO_RECV_START:{
+ int args[3];
+
+ if (copy_from_user(&args[0], argp, sizeof(args)))
+ return -EFAULT;
+ return hpsb_iso_recv_start(fi->iso_handle,
+ args[0], args[1], args[2]);
+ }
+ case RAW1394_IOC_ISO_XMIT_RECV_STOP:
+ hpsb_iso_stop(fi->iso_handle);
+ return 0;
+ case RAW1394_IOC_ISO_RECV_LISTEN_CHANNEL:
+ return hpsb_iso_recv_listen_channel(fi->iso_handle, arg);
+ case RAW1394_IOC_ISO_RECV_UNLISTEN_CHANNEL:
+ return hpsb_iso_recv_unlisten_channel(fi->iso_handle, arg);
+ case RAW1394_IOC_ISO_RECV_SET_CHANNEL_MASK:{
+ u64 mask;
+
+ if (copy_from_user(&mask, argp, sizeof(mask)))
+ return -EFAULT;
+ return hpsb_iso_recv_set_channel_mask(fi->iso_handle,
+ mask);
+ }
+ case RAW1394_IOC_ISO_GET_STATUS:
+ return raw1394_iso_get_status(fi, argp);
+ case RAW1394_IOC_ISO_RECV_PACKETS:
+ return raw1394_iso_recv_packets(fi, argp);
+ case RAW1394_IOC_ISO_RECV_RELEASE_PACKETS:
+ return hpsb_iso_recv_release_packets(fi->iso_handle, arg);
+ case RAW1394_IOC_ISO_RECV_FLUSH:
+ return hpsb_iso_recv_flush(fi->iso_handle);
+ case RAW1394_IOC_ISO_SHUTDOWN:
+ raw1394_iso_shutdown(fi);
+ return 0;
+ case RAW1394_IOC_ISO_QUEUE_ACTIVITY:
+ queue_rawiso_event(fi);
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static long raw1394_ioctl_xmit(struct file_info *fi, unsigned int cmd,
+ void __user *argp)
+{
+ switch (cmd) {
+ case RAW1394_IOC_ISO_XMIT_START:{
+ int args[2];
+
+ if (copy_from_user(&args[0], argp, sizeof(args)))
+ return -EFAULT;
+ return hpsb_iso_xmit_start(fi->iso_handle,
+ args[0], args[1]);
+ }
+ case RAW1394_IOC_ISO_XMIT_SYNC:
+ return hpsb_iso_xmit_sync(fi->iso_handle);
+ case RAW1394_IOC_ISO_XMIT_RECV_STOP:
+ hpsb_iso_stop(fi->iso_handle);
+ return 0;
+ case RAW1394_IOC_ISO_GET_STATUS:
+ return raw1394_iso_get_status(fi, argp);
+ case RAW1394_IOC_ISO_XMIT_PACKETS:
+ return raw1394_iso_send_packets(fi, argp);
+ case RAW1394_IOC_ISO_SHUTDOWN:
+ raw1394_iso_shutdown(fi);
+ return 0;
+ case RAW1394_IOC_ISO_QUEUE_ACTIVITY:
+ queue_rawiso_event(fi);
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+/* ioctl is only used for rawiso operations */
+static long raw1394_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct file_info *fi = file->private_data;
+ void __user *argp = (void __user *)arg;
+ long ret;
+
+ /* state-independent commands */
+ switch(cmd) {
+ case RAW1394_IOC_GET_CYCLE_TIMER:
+ return raw1394_read_cycle_timer(fi, argp);
+ default:
+ break;
+ }
+
+ if (!mutex_trylock(&fi->state_mutex))
+ return -EAGAIN;
+
+ switch (fi->iso_state) {
+ case RAW1394_ISO_INACTIVE:
+ ret = raw1394_ioctl_inactive(fi, cmd, argp);
+ break;
+ case RAW1394_ISO_RECV:
+ ret = raw1394_ioctl_recv(fi, cmd, arg);
+ break;
+ case RAW1394_ISO_XMIT:
+ ret = raw1394_ioctl_xmit(fi, cmd, argp);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ mutex_unlock(&fi->state_mutex);
+
+ return ret;
+}
+
+#ifdef CONFIG_COMPAT
+struct raw1394_iso_packets32 {
+ __u32 n_packets;
+ compat_uptr_t infos;
+} __attribute__((packed));
+
+struct raw1394_cycle_timer32 {
+ __u32 cycle_timer;
+ __u64 local_time;
+}
+#if defined(CONFIG_X86_64) || defined(CONFIG_IA64)
+__attribute__((packed))
+#endif
+;
+
+#define RAW1394_IOC_ISO_RECV_PACKETS32 \
+ _IOW ('#', 0x25, struct raw1394_iso_packets32)
+#define RAW1394_IOC_ISO_XMIT_PACKETS32 \
+ _IOW ('#', 0x27, struct raw1394_iso_packets32)
+#define RAW1394_IOC_GET_CYCLE_TIMER32 \
+ _IOR ('#', 0x30, struct raw1394_cycle_timer32)
+
+static long raw1394_iso_xmit_recv_packets32(struct file *file, unsigned int cmd,
+ struct raw1394_iso_packets32 __user *arg)
+{
+ compat_uptr_t infos32;
+ void __user *infos;
+ long err = -EFAULT;
+ struct raw1394_iso_packets __user *dst = compat_alloc_user_space(sizeof(struct raw1394_iso_packets));
+
+ if (!copy_in_user(&dst->n_packets, &arg->n_packets, sizeof arg->n_packets) &&
+ !copy_from_user(&infos32, &arg->infos, sizeof infos32)) {
+ infos = compat_ptr(infos32);
+ if (!copy_to_user(&dst->infos, &infos, sizeof infos))
+ err = raw1394_ioctl(file, cmd, (unsigned long)dst);
+ }
+ return err;
+}
+
+static long raw1394_read_cycle_timer32(struct file_info *fi, void __user * uaddr)
+{
+ struct raw1394_cycle_timer32 ct;
+ int err;
+
+ err = hpsb_read_cycle_timer(fi->host, &ct.cycle_timer, &ct.local_time);
+ if (!err)
+ if (copy_to_user(uaddr, &ct, sizeof(ct)))
+ err = -EFAULT;
+ return err;
+}
+
+static long raw1394_compat_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct file_info *fi = file->private_data;
+ void __user *argp = (void __user *)arg;
+ long err;
+
+ switch (cmd) {
+ /* These requests have same format as long as 'int' has same size. */
+ case RAW1394_IOC_ISO_RECV_INIT:
+ case RAW1394_IOC_ISO_RECV_START:
+ case RAW1394_IOC_ISO_RECV_LISTEN_CHANNEL:
+ case RAW1394_IOC_ISO_RECV_UNLISTEN_CHANNEL:
+ case RAW1394_IOC_ISO_RECV_SET_CHANNEL_MASK:
+ case RAW1394_IOC_ISO_RECV_RELEASE_PACKETS:
+ case RAW1394_IOC_ISO_RECV_FLUSH:
+ case RAW1394_IOC_ISO_XMIT_RECV_STOP:
+ case RAW1394_IOC_ISO_XMIT_INIT:
+ case RAW1394_IOC_ISO_XMIT_START:
+ case RAW1394_IOC_ISO_XMIT_SYNC:
+ case RAW1394_IOC_ISO_GET_STATUS:
+ case RAW1394_IOC_ISO_SHUTDOWN:
+ case RAW1394_IOC_ISO_QUEUE_ACTIVITY:
+ err = raw1394_ioctl(file, cmd, arg);
+ break;
+ /* These request have different format. */
+ case RAW1394_IOC_ISO_RECV_PACKETS32:
+ err = raw1394_iso_xmit_recv_packets32(file, RAW1394_IOC_ISO_RECV_PACKETS, argp);
+ break;
+ case RAW1394_IOC_ISO_XMIT_PACKETS32:
+ err = raw1394_iso_xmit_recv_packets32(file, RAW1394_IOC_ISO_XMIT_PACKETS, argp);
+ break;
+ case RAW1394_IOC_GET_CYCLE_TIMER32:
+ err = raw1394_read_cycle_timer32(fi, argp);
+ break;
+ default:
+ err = -EINVAL;
+ break;
+ }
+
+ return err;
+}
+#endif
+
+static unsigned int raw1394_poll(struct file *file, poll_table * pt)
+{
+ struct file_info *fi = file->private_data;
+ unsigned int mask = POLLOUT | POLLWRNORM;
+ unsigned long flags;
+
+ poll_wait(file, &fi->wait_complete, pt);
+
+ spin_lock_irqsave(&fi->reqlists_lock, flags);
+ if (!list_empty(&fi->req_complete)) {
+ mask |= POLLIN | POLLRDNORM;
+ }
+ spin_unlock_irqrestore(&fi->reqlists_lock, flags);
+
+ return mask;
+}
+
+static int raw1394_open(struct inode *inode, struct file *file)
+{
+ struct file_info *fi;
+
+ fi = kzalloc(sizeof(*fi), GFP_KERNEL);
+ if (!fi)
+ return -ENOMEM;
+
+ fi->notification = (u8) RAW1394_NOTIFY_ON; /* busreset notification */
+
+ INIT_LIST_HEAD(&fi->list);
+ mutex_init(&fi->state_mutex);
+ fi->state = opened;
+ INIT_LIST_HEAD(&fi->req_pending);
+ INIT_LIST_HEAD(&fi->req_complete);
+ spin_lock_init(&fi->reqlists_lock);
+ init_waitqueue_head(&fi->wait_complete);
+ INIT_LIST_HEAD(&fi->addr_list);
+
+ file->private_data = fi;
+
+ return 0;
+}
+
+static int raw1394_release(struct inode *inode, struct file *file)
+{
+ struct file_info *fi = file->private_data;
+ struct list_head *lh;
+ struct pending_request *req;
+ int i, fail;
+ int retval = 0;
+ struct list_head *entry;
+ struct arm_addr *addr = NULL;
+ struct host_info *hi;
+ struct file_info *fi_hlp = NULL;
+ struct arm_addr *arm_addr = NULL;
+ int another_host;
+ int csr_mod = 0;
+ unsigned long flags;
+
+ if (fi->iso_state != RAW1394_ISO_INACTIVE)
+ raw1394_iso_shutdown(fi);
+
+ spin_lock_irqsave(&host_info_lock, flags);
+
+ fail = 0;
+ /* set address-entries invalid */
+
+ while (!list_empty(&fi->addr_list)) {
+ another_host = 0;
+ lh = fi->addr_list.next;
+ addr = list_entry(lh, struct arm_addr, addr_list);
+ /* another host with valid address-entry containing
+ same addressrange? */
+ list_for_each_entry(hi, &host_info_list, list) {
+ if (hi->host != fi->host) {
+ list_for_each_entry(fi_hlp, &hi->file_info_list,
+ list) {
+ entry = fi_hlp->addr_list.next;
+ while (entry != &(fi_hlp->addr_list)) {
+ arm_addr = list_entry(entry, struct
+ arm_addr,
+ addr_list);
+ if (arm_addr->start ==
+ addr->start) {
+ DBGMSG
+ ("raw1394_release: "
+ "another host ownes "
+ "same addressrange");
+ another_host = 1;
+ break;
+ }
+ entry = entry->next;
+ }
+ if (another_host) {
+ break;
+ }
+ }
+ }
+ }
+ if (!another_host) {
+ DBGMSG("raw1394_release: call hpsb_arm_unregister");
+ retval =
+ hpsb_unregister_addrspace(&raw1394_highlevel,
+ fi->host, addr->start);
+ if (!retval) {
+ ++fail;
+ printk(KERN_ERR
+ "raw1394_release arm_Unregister failed\n");
+ }
+ }
+ DBGMSG("raw1394_release: delete addr_entry from list");
+ list_del(&addr->addr_list);
+ vfree(addr->addr_space_buffer);
+ kfree(addr);
+ } /* while */
+ spin_unlock_irqrestore(&host_info_lock, flags);
+ if (fail > 0) {
+ printk(KERN_ERR "raw1394: during addr_list-release "
+ "error(s) occurred \n");
+ }
+
+ for (;;) {
+ /* This locked section guarantees that neither
+ * complete nor pending requests exist once i!=0 */
+ spin_lock_irqsave(&fi->reqlists_lock, flags);
+ while ((req = __next_complete_req(fi)))
+ free_pending_request(req);
+
+ i = list_empty(&fi->req_pending);
+ spin_unlock_irqrestore(&fi->reqlists_lock, flags);
+
+ if (i)
+ break;
+ /*
+ * Sleep until more requests can be freed.
+ *
+ * NB: We call the macro wait_event() with a condition argument
+ * with side effect. This is only possible because the side
+ * effect does not occur until the condition became true, and
+ * wait_event() won't evaluate the condition again after that.
+ */
+ wait_event(fi->wait_complete, (req = next_complete_req(fi)));
+ free_pending_request(req);
+ }
+
+ /* Remove any sub-trees left by user space programs */
+ for (i = 0; i < RAW1394_MAX_USER_CSR_DIRS; i++) {
+ struct csr1212_dentry *dentry;
+ if (!fi->csr1212_dirs[i])
+ continue;
+ for (dentry =
+ fi->csr1212_dirs[i]->value.directory.dentries_head; dentry;
+ dentry = dentry->next) {
+ csr1212_detach_keyval_from_directory(fi->host->csr.rom->
+ root_kv,
+ dentry->kv);
+ }
+ csr1212_release_keyval(fi->csr1212_dirs[i]);
+ fi->csr1212_dirs[i] = NULL;
+ csr_mod = 1;
+ }
+
+ if ((csr_mod || fi->cfgrom_upd)
+ && hpsb_update_config_rom_image(fi->host) < 0)
+ HPSB_ERR
+ ("Failed to generate Configuration ROM image for host %d",
+ fi->host->id);
+
+ if (fi->state == connected) {
+ spin_lock_irqsave(&host_info_lock, flags);
+ list_del(&fi->list);
+ spin_unlock_irqrestore(&host_info_lock, flags);
+
+ put_device(&fi->host->device);
+ }
+
+ spin_lock_irqsave(&host_info_lock, flags);
+ if (fi->host)
+ module_put(fi->host->driver->owner);
+ spin_unlock_irqrestore(&host_info_lock, flags);
+
+ kfree(fi);
+
+ return 0;
+}
+
+/*** HOTPLUG STUFF **********************************************************/
+/*
+ * Export information about protocols/devices supported by this driver.
+ */
+#ifdef MODULE
+static struct ieee1394_device_id raw1394_id_table[] = {
+ {
+ .match_flags = IEEE1394_MATCH_SPECIFIER_ID | IEEE1394_MATCH_VERSION,
+ .specifier_id = AVC_UNIT_SPEC_ID_ENTRY & 0xffffff,
+ .version = AVC_SW_VERSION_ENTRY & 0xffffff},
+ {
+ .match_flags = IEEE1394_MATCH_SPECIFIER_ID | IEEE1394_MATCH_VERSION,
+ .specifier_id = CAMERA_UNIT_SPEC_ID_ENTRY & 0xffffff,
+ .version = CAMERA_SW_VERSION_ENTRY & 0xffffff},
+ {
+ .match_flags = IEEE1394_MATCH_SPECIFIER_ID | IEEE1394_MATCH_VERSION,
+ .specifier_id = CAMERA_UNIT_SPEC_ID_ENTRY & 0xffffff,
+ .version = (CAMERA_SW_VERSION_ENTRY + 1) & 0xffffff},
+ {
+ .match_flags = IEEE1394_MATCH_SPECIFIER_ID | IEEE1394_MATCH_VERSION,
+ .specifier_id = CAMERA_UNIT_SPEC_ID_ENTRY & 0xffffff,
+ .version = (CAMERA_SW_VERSION_ENTRY + 2) & 0xffffff},
+ {}
+};
+
+MODULE_DEVICE_TABLE(ieee1394, raw1394_id_table);
+#endif /* MODULE */
+
+static struct hpsb_protocol_driver raw1394_driver = {
+ .name = "raw1394",
+};
+
+/******************************************************************************/
+
+static struct hpsb_highlevel raw1394_highlevel = {
+ .name = RAW1394_DEVICE_NAME,
+ .add_host = add_host,
+ .remove_host = remove_host,
+ .host_reset = host_reset,
+ .fcp_request = fcp_request,
+};
+
+static struct cdev raw1394_cdev;
+static const struct file_operations raw1394_fops = {
+ .owner = THIS_MODULE,
+ .read = raw1394_read,
+ .write = raw1394_write,
+ .mmap = raw1394_mmap,
+ .unlocked_ioctl = raw1394_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = raw1394_compat_ioctl,
+#endif
+ .poll = raw1394_poll,
+ .open = raw1394_open,
+ .release = raw1394_release,
+};
+
+static int __init init_raw1394(void)
+{
+ int ret = 0;
+
+ hpsb_register_highlevel(&raw1394_highlevel);
+
+ if (IS_ERR
+ (device_create(hpsb_protocol_class, NULL,
+ MKDEV(IEEE1394_MAJOR,
+ IEEE1394_MINOR_BLOCK_RAW1394 * 16),
+ NULL, RAW1394_DEVICE_NAME))) {
+ ret = -EFAULT;
+ goto out_unreg;
+ }
+
+ cdev_init(&raw1394_cdev, &raw1394_fops);
+ raw1394_cdev.owner = THIS_MODULE;
+ ret = cdev_add(&raw1394_cdev, IEEE1394_RAW1394_DEV, 1);
+ if (ret) {
+ HPSB_ERR("raw1394 failed to register minor device block");
+ goto out_dev;
+ }
+
+ HPSB_INFO("raw1394: /dev/%s device initialized", RAW1394_DEVICE_NAME);
+
+ ret = hpsb_register_protocol(&raw1394_driver);
+ if (ret) {
+ HPSB_ERR("raw1394: failed to register protocol");
+ cdev_del(&raw1394_cdev);
+ goto out_dev;
+ }
+
+ goto out;
+
+ out_dev:
+ device_destroy(hpsb_protocol_class,
+ MKDEV(IEEE1394_MAJOR,
+ IEEE1394_MINOR_BLOCK_RAW1394 * 16));
+ out_unreg:
+ hpsb_unregister_highlevel(&raw1394_highlevel);
+ out:
+ return ret;
+}
+
+static void __exit cleanup_raw1394(void)
+{
+ device_destroy(hpsb_protocol_class,
+ MKDEV(IEEE1394_MAJOR,
+ IEEE1394_MINOR_BLOCK_RAW1394 * 16));
+ cdev_del(&raw1394_cdev);
+ hpsb_unregister_highlevel(&raw1394_highlevel);
+ hpsb_unregister_protocol(&raw1394_driver);
+}
+
+module_init(init_raw1394);
+module_exit(cleanup_raw1394);
+MODULE_LICENSE("GPL");
diff --git a/drivers/ieee1394/raw1394.h b/drivers/ieee1394/raw1394.h
new file mode 100644
index 0000000..963ac20
--- /dev/null
+++ b/drivers/ieee1394/raw1394.h
@@ -0,0 +1,191 @@
+#ifndef IEEE1394_RAW1394_H
+#define IEEE1394_RAW1394_H
+
+/* header for the raw1394 API that is exported to user-space */
+
+#define RAW1394_KERNELAPI_VERSION 4
+
+/* state: opened */
+#define RAW1394_REQ_INITIALIZE 1
+
+/* state: initialized */
+#define RAW1394_REQ_LIST_CARDS 2
+#define RAW1394_REQ_SET_CARD 3
+
+/* state: connected */
+#define RAW1394_REQ_ASYNC_READ 100
+#define RAW1394_REQ_ASYNC_WRITE 101
+#define RAW1394_REQ_LOCK 102
+#define RAW1394_REQ_LOCK64 103
+#define RAW1394_REQ_ISO_SEND 104 /* removed ABI, now a no-op */
+#define RAW1394_REQ_ASYNC_SEND 105
+#define RAW1394_REQ_ASYNC_STREAM 106
+
+#define RAW1394_REQ_ISO_LISTEN 200 /* removed ABI, now a no-op */
+#define RAW1394_REQ_FCP_LISTEN 201
+#define RAW1394_REQ_RESET_BUS 202
+#define RAW1394_REQ_GET_ROM 203
+#define RAW1394_REQ_UPDATE_ROM 204
+#define RAW1394_REQ_ECHO 205
+#define RAW1394_REQ_MODIFY_ROM 206
+
+#define RAW1394_REQ_ARM_REGISTER 300
+#define RAW1394_REQ_ARM_UNREGISTER 301
+#define RAW1394_REQ_ARM_SET_BUF 302
+#define RAW1394_REQ_ARM_GET_BUF 303
+
+#define RAW1394_REQ_RESET_NOTIFY 400
+
+#define RAW1394_REQ_PHYPACKET 500
+
+/* kernel to user */
+#define RAW1394_REQ_BUS_RESET 10000
+#define RAW1394_REQ_ISO_RECEIVE 10001
+#define RAW1394_REQ_FCP_REQUEST 10002
+#define RAW1394_REQ_ARM 10003
+#define RAW1394_REQ_RAWISO_ACTIVITY 10004
+
+/* error codes */
+#define RAW1394_ERROR_NONE 0
+#define RAW1394_ERROR_COMPAT (-1001)
+#define RAW1394_ERROR_STATE_ORDER (-1002)
+#define RAW1394_ERROR_GENERATION (-1003)
+#define RAW1394_ERROR_INVALID_ARG (-1004)
+#define RAW1394_ERROR_MEMFAULT (-1005)
+#define RAW1394_ERROR_ALREADY (-1006)
+
+#define RAW1394_ERROR_EXCESSIVE (-1020)
+#define RAW1394_ERROR_UNTIDY_LEN (-1021)
+
+#define RAW1394_ERROR_SEND_ERROR (-1100)
+#define RAW1394_ERROR_ABORTED (-1101)
+#define RAW1394_ERROR_TIMEOUT (-1102)
+
+/* arm_codes */
+#define ARM_READ 1
+#define ARM_WRITE 2
+#define ARM_LOCK 4
+
+#define RAW1394_LONG_RESET 0
+#define RAW1394_SHORT_RESET 1
+
+/* busresetnotify ... */
+#define RAW1394_NOTIFY_OFF 0
+#define RAW1394_NOTIFY_ON 1
+
+#include <asm/types.h>
+
+struct raw1394_request {
+ __u32 type;
+ __s32 error;
+ __u32 misc;
+
+ __u32 generation;
+ __u32 length;
+
+ __u64 address;
+
+ __u64 tag;
+
+ __u64 sendb;
+ __u64 recvb;
+};
+
+struct raw1394_khost_list {
+ __u32 nodes;
+ __u8 name[32];
+};
+
+typedef struct arm_request {
+ __u16 destination_nodeid;
+ __u16 source_nodeid;
+ __u64 destination_offset;
+ __u8 tlabel;
+ __u8 tcode;
+ __u8 extended_transaction_code;
+ __u32 generation;
+ __u16 buffer_length;
+ __u8 __user *buffer;
+} *arm_request_t;
+
+typedef struct arm_response {
+ __s32 response_code;
+ __u16 buffer_length;
+ __u8 __user *buffer;
+} *arm_response_t;
+
+typedef struct arm_request_response {
+ struct arm_request __user *request;
+ struct arm_response __user *response;
+} *arm_request_response_t;
+
+/* rawiso API */
+#include "ieee1394-ioctl.h"
+
+/* per-packet metadata embedded in the ringbuffer */
+/* must be identical to hpsb_iso_packet_info in iso.h! */
+struct raw1394_iso_packet_info {
+ __u32 offset;
+ __u16 len;
+ __u16 cycle; /* recv only */
+ __u8 channel; /* recv only */
+ __u8 tag;
+ __u8 sy;
+};
+
+/* argument for RAW1394_ISO_RECV/XMIT_PACKETS ioctls */
+struct raw1394_iso_packets {
+ __u32 n_packets;
+ struct raw1394_iso_packet_info __user *infos;
+};
+
+struct raw1394_iso_config {
+ /* size of packet data buffer, in bytes (will be rounded up to PAGE_SIZE) */
+ __u32 data_buf_size;
+
+ /* # of packets to buffer */
+ __u32 buf_packets;
+
+ /* iso channel (set to -1 for multi-channel recv) */
+ __s32 channel;
+
+ /* xmit only - iso transmission speed */
+ __u8 speed;
+
+ /* The mode of the dma when receiving iso data. Must be supported by chip */
+ __u8 dma_mode;
+
+ /* max. latency of buffer, in packets (-1 if you don't care) */
+ __s32 irq_interval;
+};
+
+/* argument to RAW1394_ISO_XMIT/RECV_INIT and RAW1394_ISO_GET_STATUS */
+struct raw1394_iso_status {
+ /* current settings */
+ struct raw1394_iso_config config;
+
+ /* number of packets waiting to be filled with data (ISO transmission)
+ or containing data received (ISO reception) */
+ __u32 n_packets;
+
+ /* approximate number of packets dropped due to overflow or
+ underflow of the packet buffer (a value of zero guarantees
+ that no packets have been dropped) */
+ __u32 overflows;
+
+ /* cycle number at which next packet will be transmitted;
+ -1 if not known */
+ __s16 xmit_cycle;
+};
+
+/* argument to RAW1394_IOC_GET_CYCLE_TIMER ioctl */
+struct raw1394_cycle_timer {
+ /* contents of Isochronous Cycle Timer register,
+ as in OHCI 1.1 clause 5.13 (also with non-OHCI hosts) */
+ __u32 cycle_timer;
+
+ /* local time in microseconds since Epoch,
+ simultaneously read with cycle timer */
+ __u64 local_time;
+};
+#endif /* IEEE1394_RAW1394_H */
diff --git a/drivers/ieee1394/sbp2.c b/drivers/ieee1394/sbp2.c
new file mode 100644
index 0000000..a01b43e
--- /dev/null
+++ b/drivers/ieee1394/sbp2.c
@@ -0,0 +1,2141 @@
+/*
+ * sbp2.c - SBP-2 protocol driver for IEEE-1394
+ *
+ * Copyright (C) 2000 James Goodwin, Filanet Corporation (www.filanet.com)
+ * jamesg@filanet.com (JSG)
+ *
+ * Copyright (C) 2003 Ben Collins <bcollins@debian.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * Brief Description:
+ *
+ * This driver implements the Serial Bus Protocol 2 (SBP-2) over IEEE-1394
+ * under Linux. The SBP-2 driver is implemented as an IEEE-1394 high-level
+ * driver. It also registers as a SCSI lower-level driver in order to accept
+ * SCSI commands for transport using SBP-2.
+ *
+ * You may access any attached SBP-2 (usually storage devices) as regular
+ * SCSI devices. E.g. mount /dev/sda1, fdisk, mkfs, etc..
+ *
+ * See http://www.t10.org/drafts.htm#sbp2 for the final draft of the SBP-2
+ * specification and for where to purchase the official standard.
+ *
+ * TODO:
+ * - look into possible improvements of the SCSI error handlers
+ * - handle Unit_Characteristics.mgt_ORB_timeout and .ORB_size
+ * - handle Logical_Unit_Number.ordered
+ * - handle src == 1 in status blocks
+ * - reimplement the DMA mapping in absence of physical DMA so that
+ * bus_to_virt is no longer required
+ * - debug the handling of absent physical DMA
+ * - replace CONFIG_IEEE1394_SBP2_PHYS_DMA by automatic detection
+ * (this is easy but depends on the previous two TODO items)
+ * - make the parameter serialize_io configurable per device
+ * - move all requests to fetch agent registers into non-atomic context,
+ * replace all usages of sbp2util_node_write_no_wait by true transactions
+ * Grep for inline FIXME comments below.
+ */
+
+#include <linux/blkdev.h>
+#include <linux/compiler.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/gfp.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/stat.h>
+#include <linux/string.h>
+#include <linux/stringify.h>
+#include <linux/types.h>
+#include <linux/wait.h>
+#include <linux/workqueue.h>
+#include <linux/scatterlist.h>
+
+#include <asm/byteorder.h>
+#include <asm/errno.h>
+#include <asm/param.h>
+#include <asm/system.h>
+#include <asm/types.h>
+
+#ifdef CONFIG_IEEE1394_SBP2_PHYS_DMA
+#include <asm/io.h> /* for bus_to_virt */
+#endif
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_dbg.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_host.h>
+
+#include "csr1212.h"
+#include "highlevel.h"
+#include "hosts.h"
+#include "ieee1394.h"
+#include "ieee1394_core.h"
+#include "ieee1394_hotplug.h"
+#include "ieee1394_transactions.h"
+#include "ieee1394_types.h"
+#include "nodemgr.h"
+#include "sbp2.h"
+
+/*
+ * Module load parameter definitions
+ */
+
+/*
+ * Change max_speed on module load if you have a bad IEEE-1394
+ * controller that has trouble running 2KB packets at 400mb.
+ *
+ * NOTE: On certain OHCI parts I have seen short packets on async transmit
+ * (probably due to PCI latency/throughput issues with the part). You can
+ * bump down the speed if you are running into problems.
+ */
+static int sbp2_max_speed = IEEE1394_SPEED_MAX;
+module_param_named(max_speed, sbp2_max_speed, int, 0644);
+MODULE_PARM_DESC(max_speed, "Force max speed "
+ "(3 = 800Mb/s, 2 = 400Mb/s, 1 = 200Mb/s, 0 = 100Mb/s)");
+
+/*
+ * Set serialize_io to 0 or N to use dynamically appended lists of command ORBs.
+ * This is and always has been buggy in multiple subtle ways. See above TODOs.
+ */
+static int sbp2_serialize_io = 1;
+module_param_named(serialize_io, sbp2_serialize_io, bool, 0444);
+MODULE_PARM_DESC(serialize_io, "Serialize requests coming from SCSI drivers "
+ "(default = Y, faster but buggy = N)");
+
+/*
+ * Adjust max_sectors if you'd like to influence how many sectors each SCSI
+ * command can transfer at most. Please note that some older SBP-2 bridge
+ * chips are broken for transfers greater or equal to 128KB, therefore
+ * max_sectors used to be a safe 255 sectors for many years. We now have a
+ * default of 0 here which means that we let the SCSI stack choose a limit.
+ *
+ * The SBP2_WORKAROUND_128K_MAX_TRANS flag, if set either in the workarounds
+ * module parameter or in the sbp2_workarounds_table[], will override the
+ * value of max_sectors. We should use sbp2_workarounds_table[] to cover any
+ * bridge chip which becomes known to need the 255 sectors limit.
+ */
+static int sbp2_max_sectors;
+module_param_named(max_sectors, sbp2_max_sectors, int, 0444);
+MODULE_PARM_DESC(max_sectors, "Change max sectors per I/O supported "
+ "(default = 0 = use SCSI stack's default)");
+
+/*
+ * Exclusive login to sbp2 device? In most cases, the sbp2 driver should
+ * do an exclusive login, as it's generally unsafe to have two hosts
+ * talking to a single sbp2 device at the same time (filesystem coherency,
+ * etc.). If you're running an sbp2 device that supports multiple logins,
+ * and you're either running read-only filesystems or some sort of special
+ * filesystem supporting multiple hosts, e.g. OpenGFS, Oracle Cluster
+ * File System, or Lustre, then set exclusive_login to zero.
+ *
+ * So far only bridges from Oxford Semiconductor are known to support
+ * concurrent logins. Depending on firmware, four or two concurrent logins
+ * are possible on OXFW911 and newer Oxsemi bridges.
+ */
+static int sbp2_exclusive_login = 1;
+module_param_named(exclusive_login, sbp2_exclusive_login, bool, 0644);
+MODULE_PARM_DESC(exclusive_login, "Exclusive login to sbp2 device "
+ "(default = Y, use N for concurrent initiators)");
+
+/*
+ * If any of the following workarounds is required for your device to work,
+ * please submit the kernel messages logged by sbp2 to the linux1394-devel
+ * mailing list.
+ *
+ * - 128kB max transfer
+ * Limit transfer size. Necessary for some old bridges.
+ *
+ * - 36 byte inquiry
+ * When scsi_mod probes the device, let the inquiry command look like that
+ * from MS Windows.
+ *
+ * - skip mode page 8
+ * Suppress sending of mode_sense for mode page 8 if the device pretends to
+ * support the SCSI Primary Block commands instead of Reduced Block Commands.
+ *
+ * - fix capacity
+ * Tell sd_mod to correct the last sector number reported by read_capacity.
+ * Avoids access beyond actual disk limits on devices with an off-by-one bug.
+ * Don't use this with devices which don't have this bug.
+ *
+ * - delay inquiry
+ * Wait extra SBP2_INQUIRY_DELAY seconds after login before SCSI inquiry.
+ *
+ * - power condition
+ * Set the power condition field in the START STOP UNIT commands sent by
+ * sd_mod on suspend, resume, and shutdown (if manage_start_stop is on).
+ * Some disks need this to spin down or to resume properly.
+ *
+ * - override internal blacklist
+ * Instead of adding to the built-in blacklist, use only the workarounds
+ * specified in the module load parameter.
+ * Useful if a blacklist entry interfered with a non-broken device.
+ */
+static int sbp2_default_workarounds;
+module_param_named(workarounds, sbp2_default_workarounds, int, 0644);
+MODULE_PARM_DESC(workarounds, "Work around device bugs (default = 0"
+ ", 128kB max transfer = " __stringify(SBP2_WORKAROUND_128K_MAX_TRANS)
+ ", 36 byte inquiry = " __stringify(SBP2_WORKAROUND_INQUIRY_36)
+ ", skip mode page 8 = " __stringify(SBP2_WORKAROUND_MODE_SENSE_8)
+ ", fix capacity = " __stringify(SBP2_WORKAROUND_FIX_CAPACITY)
+ ", delay inquiry = " __stringify(SBP2_WORKAROUND_DELAY_INQUIRY)
+ ", set power condition in start stop unit = "
+ __stringify(SBP2_WORKAROUND_POWER_CONDITION)
+ ", override internal blacklist = " __stringify(SBP2_WORKAROUND_OVERRIDE)
+ ", or a combination)");
+
+/*
+ * This influences the format of the sysfs attribute
+ * /sys/bus/scsi/devices/.../ieee1394_id.
+ *
+ * The default format is like in older kernels: %016Lx:%d:%d
+ * It contains the target's EUI-64, a number given to the logical unit by
+ * the ieee1394 driver's nodemgr (starting at 0), and the LUN.
+ *
+ * The long format is: %016Lx:%06x:%04x
+ * It contains the target's EUI-64, the unit directory's directory_ID as per
+ * IEEE 1212 clause 7.7.19, and the LUN. This format comes closest to the
+ * format of SBP(-3) target port and logical unit identifier as per SAM (SCSI
+ * Architecture Model) rev.2 to 4 annex A. Therefore and because it is
+ * independent of the implementation of the ieee1394 nodemgr, the longer format
+ * is recommended for future use.
+ */
+static int sbp2_long_sysfs_ieee1394_id;
+module_param_named(long_ieee1394_id, sbp2_long_sysfs_ieee1394_id, bool, 0644);
+MODULE_PARM_DESC(long_ieee1394_id, "8+3+2 bytes format of ieee1394_id in sysfs "
+ "(default = backwards-compatible = N, SAM-conforming = Y)");
+
+
+#define SBP2_INFO(fmt, args...) HPSB_INFO("sbp2: "fmt, ## args)
+#define SBP2_ERR(fmt, args...) HPSB_ERR("sbp2: "fmt, ## args)
+
+/*
+ * Globals
+ */
+static void sbp2scsi_complete_all_commands(struct sbp2_lu *, u32);
+static void sbp2scsi_complete_command(struct sbp2_lu *, u32, struct scsi_cmnd *,
+ void (*)(struct scsi_cmnd *));
+static struct sbp2_lu *sbp2_alloc_device(struct unit_directory *);
+static int sbp2_start_device(struct sbp2_lu *);
+static void sbp2_remove_device(struct sbp2_lu *);
+static int sbp2_login_device(struct sbp2_lu *);
+static int sbp2_reconnect_device(struct sbp2_lu *);
+static int sbp2_logout_device(struct sbp2_lu *);
+static void sbp2_host_reset(struct hpsb_host *);
+static int sbp2_handle_status_write(struct hpsb_host *, int, int, quadlet_t *,
+ u64, size_t, u16);
+static int sbp2_agent_reset(struct sbp2_lu *, int);
+static void sbp2_parse_unit_directory(struct sbp2_lu *,
+ struct unit_directory *);
+static int sbp2_set_busy_timeout(struct sbp2_lu *);
+static int sbp2_max_speed_and_size(struct sbp2_lu *);
+
+
+static const u8 sbp2_speedto_max_payload[] = { 0x7, 0x8, 0x9, 0xA, 0xB, 0xC };
+
+static DEFINE_RWLOCK(sbp2_hi_logical_units_lock);
+
+static struct hpsb_highlevel sbp2_highlevel = {
+ .name = SBP2_DEVICE_NAME,
+ .host_reset = sbp2_host_reset,
+};
+
+static struct hpsb_address_ops sbp2_ops = {
+ .write = sbp2_handle_status_write
+};
+
+#ifdef CONFIG_IEEE1394_SBP2_PHYS_DMA
+static int sbp2_handle_physdma_write(struct hpsb_host *, int, int, quadlet_t *,
+ u64, size_t, u16);
+static int sbp2_handle_physdma_read(struct hpsb_host *, int, quadlet_t *, u64,
+ size_t, u16);
+
+static struct hpsb_address_ops sbp2_physdma_ops = {
+ .read = sbp2_handle_physdma_read,
+ .write = sbp2_handle_physdma_write,
+};
+#endif
+
+
+/*
+ * Interface to driver core and IEEE 1394 core
+ */
+static struct ieee1394_device_id sbp2_id_table[] = {
+ {
+ .match_flags = IEEE1394_MATCH_SPECIFIER_ID | IEEE1394_MATCH_VERSION,
+ .specifier_id = SBP2_UNIT_SPEC_ID_ENTRY & 0xffffff,
+ .version = SBP2_SW_VERSION_ENTRY & 0xffffff},
+ {}
+};
+MODULE_DEVICE_TABLE(ieee1394, sbp2_id_table);
+
+static int sbp2_probe(struct device *);
+static int sbp2_remove(struct device *);
+static int sbp2_update(struct unit_directory *);
+
+static struct hpsb_protocol_driver sbp2_driver = {
+ .name = SBP2_DEVICE_NAME,
+ .id_table = sbp2_id_table,
+ .update = sbp2_update,
+ .driver = {
+ .probe = sbp2_probe,
+ .remove = sbp2_remove,
+ },
+};
+
+
+/*
+ * Interface to SCSI core
+ */
+static int sbp2scsi_queuecommand(struct scsi_cmnd *,
+ void (*)(struct scsi_cmnd *));
+static int sbp2scsi_abort(struct scsi_cmnd *);
+static int sbp2scsi_reset(struct scsi_cmnd *);
+static int sbp2scsi_slave_alloc(struct scsi_device *);
+static int sbp2scsi_slave_configure(struct scsi_device *);
+static void sbp2scsi_slave_destroy(struct scsi_device *);
+static ssize_t sbp2_sysfs_ieee1394_id_show(struct device *,
+ struct device_attribute *, char *);
+
+static DEVICE_ATTR(ieee1394_id, S_IRUGO, sbp2_sysfs_ieee1394_id_show, NULL);
+
+static struct device_attribute *sbp2_sysfs_sdev_attrs[] = {
+ &dev_attr_ieee1394_id,
+ NULL
+};
+
+static struct scsi_host_template sbp2_shost_template = {
+ .module = THIS_MODULE,
+ .name = "SBP-2 IEEE-1394",
+ .proc_name = SBP2_DEVICE_NAME,
+ .queuecommand = sbp2scsi_queuecommand,
+ .eh_abort_handler = sbp2scsi_abort,
+ .eh_device_reset_handler = sbp2scsi_reset,
+ .slave_alloc = sbp2scsi_slave_alloc,
+ .slave_configure = sbp2scsi_slave_configure,
+ .slave_destroy = sbp2scsi_slave_destroy,
+ .this_id = -1,
+ .sg_tablesize = SG_ALL,
+ .use_clustering = ENABLE_CLUSTERING,
+ .cmd_per_lun = SBP2_MAX_CMDS,
+ .can_queue = SBP2_MAX_CMDS,
+ .sdev_attrs = sbp2_sysfs_sdev_attrs,
+};
+
+/* for match-all entries in sbp2_workarounds_table */
+#define SBP2_ROM_VALUE_WILDCARD 0x1000000
+
+/*
+ * List of devices with known bugs.
+ *
+ * The firmware_revision field, masked with 0xffff00, is the best indicator
+ * for the type of bridge chip of a device. It yields a few false positives
+ * but this did not break correctly behaving devices so far.
+ */
+static const struct {
+ u32 firmware_revision;
+ u32 model_id;
+ unsigned workarounds;
+} sbp2_workarounds_table[] = {
+ /* DViCO Momobay CX-1 with TSB42AA9 bridge */ {
+ .firmware_revision = 0x002800,
+ .model_id = 0x001010,
+ .workarounds = SBP2_WORKAROUND_INQUIRY_36 |
+ SBP2_WORKAROUND_MODE_SENSE_8 |
+ SBP2_WORKAROUND_POWER_CONDITION,
+ },
+ /* DViCO Momobay FX-3A with TSB42AA9A bridge */ {
+ .firmware_revision = 0x002800,
+ .model_id = 0x000000,
+ .workarounds = SBP2_WORKAROUND_DELAY_INQUIRY |
+ SBP2_WORKAROUND_POWER_CONDITION,
+ },
+ /* Initio bridges, actually only needed for some older ones */ {
+ .firmware_revision = 0x000200,
+ .model_id = SBP2_ROM_VALUE_WILDCARD,
+ .workarounds = SBP2_WORKAROUND_INQUIRY_36,
+ },
+ /* PL-3507 bridge with Prolific firmware */ {
+ .firmware_revision = 0x012800,
+ .model_id = SBP2_ROM_VALUE_WILDCARD,
+ .workarounds = SBP2_WORKAROUND_POWER_CONDITION,
+ },
+ /* Symbios bridge */ {
+ .firmware_revision = 0xa0b800,
+ .model_id = SBP2_ROM_VALUE_WILDCARD,
+ .workarounds = SBP2_WORKAROUND_128K_MAX_TRANS,
+ },
+ /* Datafab MD2-FW2 with Symbios/LSILogic SYM13FW500 bridge */ {
+ .firmware_revision = 0x002600,
+ .model_id = SBP2_ROM_VALUE_WILDCARD,
+ .workarounds = SBP2_WORKAROUND_128K_MAX_TRANS,
+ },
+ /*
+ * iPod 2nd generation: needs 128k max transfer size workaround
+ * iPod 3rd generation: needs fix capacity workaround
+ */
+ {
+ .firmware_revision = 0x0a2700,
+ .model_id = 0x000000,
+ .workarounds = SBP2_WORKAROUND_128K_MAX_TRANS |
+ SBP2_WORKAROUND_FIX_CAPACITY,
+ },
+ /* iPod 4th generation */ {
+ .firmware_revision = 0x0a2700,
+ .model_id = 0x000021,
+ .workarounds = SBP2_WORKAROUND_FIX_CAPACITY,
+ },
+ /* iPod mini */ {
+ .firmware_revision = 0x0a2700,
+ .model_id = 0x000022,
+ .workarounds = SBP2_WORKAROUND_FIX_CAPACITY,
+ },
+ /* iPod mini */ {
+ .firmware_revision = 0x0a2700,
+ .model_id = 0x000023,
+ .workarounds = SBP2_WORKAROUND_FIX_CAPACITY,
+ },
+ /* iPod Photo */ {
+ .firmware_revision = 0x0a2700,
+ .model_id = 0x00007e,
+ .workarounds = SBP2_WORKAROUND_FIX_CAPACITY,
+ }
+};
+
+/**************************************
+ * General utility functions
+ **************************************/
+
+#ifndef __BIG_ENDIAN
+/*
+ * Converts a buffer from be32 to cpu byte ordering. Length is in bytes.
+ */
+static inline void sbp2util_be32_to_cpu_buffer(void *buffer, int length)
+{
+ u32 *temp = buffer;
+
+ for (length = (length >> 2); length--; )
+ temp[length] = be32_to_cpu(temp[length]);
+}
+
+/*
+ * Converts a buffer from cpu to be32 byte ordering. Length is in bytes.
+ */
+static inline void sbp2util_cpu_to_be32_buffer(void *buffer, int length)
+{
+ u32 *temp = buffer;
+
+ for (length = (length >> 2); length--; )
+ temp[length] = cpu_to_be32(temp[length]);
+}
+#else /* BIG_ENDIAN */
+/* Why waste the cpu cycles? */
+#define sbp2util_be32_to_cpu_buffer(x,y) do {} while (0)
+#define sbp2util_cpu_to_be32_buffer(x,y) do {} while (0)
+#endif
+
+static DECLARE_WAIT_QUEUE_HEAD(sbp2_access_wq);
+
+/*
+ * Waits for completion of an SBP-2 access request.
+ * Returns nonzero if timed out or prematurely interrupted.
+ */
+static int sbp2util_access_timeout(struct sbp2_lu *lu, int timeout)
+{
+ long leftover;
+
+ leftover = wait_event_interruptible_timeout(
+ sbp2_access_wq, lu->access_complete, timeout);
+ lu->access_complete = 0;
+ return leftover <= 0;
+}
+
+static void sbp2_free_packet(void *packet)
+{
+ hpsb_free_tlabel(packet);
+ hpsb_free_packet(packet);
+}
+
+/*
+ * This is much like hpsb_node_write(), except it ignores the response
+ * subaction and returns immediately. Can be used from atomic context.
+ */
+static int sbp2util_node_write_no_wait(struct node_entry *ne, u64 addr,
+ quadlet_t *buf, size_t len)
+{
+ struct hpsb_packet *packet;
+
+ packet = hpsb_make_writepacket(ne->host, ne->nodeid, addr, buf, len);
+ if (!packet)
+ return -ENOMEM;
+
+ hpsb_set_packet_complete_task(packet, sbp2_free_packet, packet);
+ hpsb_node_fill_packet(ne, packet);
+ if (hpsb_send_packet(packet) < 0) {
+ sbp2_free_packet(packet);
+ return -EIO;
+ }
+ return 0;
+}
+
+static void sbp2util_notify_fetch_agent(struct sbp2_lu *lu, u64 offset,
+ quadlet_t *data, size_t len)
+{
+ /* There is a small window after a bus reset within which the node
+ * entry's generation is current but the reconnect wasn't completed. */
+ if (unlikely(atomic_read(&lu->state) == SBP2LU_STATE_IN_RESET))
+ return;
+
+ if (hpsb_node_write(lu->ne, lu->command_block_agent_addr + offset,
+ data, len))
+ SBP2_ERR("sbp2util_notify_fetch_agent failed.");
+
+ /* Now accept new SCSI commands, unless a bus reset happended during
+ * hpsb_node_write. */
+ if (likely(atomic_read(&lu->state) != SBP2LU_STATE_IN_RESET))
+ scsi_unblock_requests(lu->shost);
+}
+
+static void sbp2util_write_orb_pointer(struct work_struct *work)
+{
+ struct sbp2_lu *lu = container_of(work, struct sbp2_lu, protocol_work);
+ quadlet_t data[2];
+
+ data[0] = ORB_SET_NODE_ID(lu->hi->host->node_id);
+ data[1] = lu->last_orb_dma;
+ sbp2util_cpu_to_be32_buffer(data, 8);
+ sbp2util_notify_fetch_agent(lu, SBP2_ORB_POINTER_OFFSET, data, 8);
+}
+
+static void sbp2util_write_doorbell(struct work_struct *work)
+{
+ struct sbp2_lu *lu = container_of(work, struct sbp2_lu, protocol_work);
+
+ sbp2util_notify_fetch_agent(lu, SBP2_DOORBELL_OFFSET, NULL, 4);
+}
+
+static int sbp2util_create_command_orb_pool(struct sbp2_lu *lu)
+{
+ struct sbp2_command_info *cmd;
+ struct device *dmadev = lu->hi->host->device.parent;
+ int i, orbs = sbp2_serialize_io ? 2 : SBP2_MAX_CMDS;
+
+ for (i = 0; i < orbs; i++) {
+ cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+ if (!cmd)
+ goto failed_alloc;
+
+ cmd->command_orb_dma =
+ dma_map_single(dmadev, &cmd->command_orb,
+ sizeof(struct sbp2_command_orb),
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(dmadev, cmd->command_orb_dma))
+ goto failed_orb;
+
+ cmd->sge_dma =
+ dma_map_single(dmadev, &cmd->scatter_gather_element,
+ sizeof(cmd->scatter_gather_element),
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(dmadev, cmd->sge_dma))
+ goto failed_sge;
+
+ INIT_LIST_HEAD(&cmd->list);
+ list_add_tail(&cmd->list, &lu->cmd_orb_completed);
+ }
+ return 0;
+
+failed_sge:
+ dma_unmap_single(dmadev, cmd->command_orb_dma,
+ sizeof(struct sbp2_command_orb), DMA_TO_DEVICE);
+failed_orb:
+ kfree(cmd);
+failed_alloc:
+ return -ENOMEM;
+}
+
+static void sbp2util_remove_command_orb_pool(struct sbp2_lu *lu,
+ struct hpsb_host *host)
+{
+ struct list_head *lh, *next;
+ struct sbp2_command_info *cmd;
+ unsigned long flags;
+
+ spin_lock_irqsave(&lu->cmd_orb_lock, flags);
+ if (!list_empty(&lu->cmd_orb_completed))
+ list_for_each_safe(lh, next, &lu->cmd_orb_completed) {
+ cmd = list_entry(lh, struct sbp2_command_info, list);
+ dma_unmap_single(host->device.parent,
+ cmd->command_orb_dma,
+ sizeof(struct sbp2_command_orb),
+ DMA_TO_DEVICE);
+ dma_unmap_single(host->device.parent, cmd->sge_dma,
+ sizeof(cmd->scatter_gather_element),
+ DMA_TO_DEVICE);
+ kfree(cmd);
+ }
+ spin_unlock_irqrestore(&lu->cmd_orb_lock, flags);
+ return;
+}
+
+/*
+ * Finds the sbp2_command for a given outstanding command ORB.
+ * Only looks at the in-use list.
+ */
+static struct sbp2_command_info *sbp2util_find_command_for_orb(
+ struct sbp2_lu *lu, dma_addr_t orb)
+{
+ struct sbp2_command_info *cmd;
+ unsigned long flags;
+
+ spin_lock_irqsave(&lu->cmd_orb_lock, flags);
+ if (!list_empty(&lu->cmd_orb_inuse))
+ list_for_each_entry(cmd, &lu->cmd_orb_inuse, list)
+ if (cmd->command_orb_dma == orb) {
+ spin_unlock_irqrestore(
+ &lu->cmd_orb_lock, flags);
+ return cmd;
+ }
+ spin_unlock_irqrestore(&lu->cmd_orb_lock, flags);
+ return NULL;
+}
+
+/*
+ * Finds the sbp2_command for a given outstanding SCpnt.
+ * Only looks at the in-use list.
+ * Must be called with lu->cmd_orb_lock held.
+ */
+static struct sbp2_command_info *sbp2util_find_command_for_SCpnt(
+ struct sbp2_lu *lu, void *SCpnt)
+{
+ struct sbp2_command_info *cmd;
+
+ if (!list_empty(&lu->cmd_orb_inuse))
+ list_for_each_entry(cmd, &lu->cmd_orb_inuse, list)
+ if (cmd->Current_SCpnt == SCpnt)
+ return cmd;
+ return NULL;
+}
+
+static struct sbp2_command_info *sbp2util_allocate_command_orb(
+ struct sbp2_lu *lu,
+ struct scsi_cmnd *Current_SCpnt,
+ void (*Current_done)(struct scsi_cmnd *))
+{
+ struct list_head *lh;
+ struct sbp2_command_info *cmd = NULL;
+ unsigned long flags;
+
+ spin_lock_irqsave(&lu->cmd_orb_lock, flags);
+ if (!list_empty(&lu->cmd_orb_completed)) {
+ lh = lu->cmd_orb_completed.next;
+ list_del(lh);
+ cmd = list_entry(lh, struct sbp2_command_info, list);
+ cmd->Current_done = Current_done;
+ cmd->Current_SCpnt = Current_SCpnt;
+ list_add_tail(&cmd->list, &lu->cmd_orb_inuse);
+ } else
+ SBP2_ERR("%s: no orbs available", __func__);
+ spin_unlock_irqrestore(&lu->cmd_orb_lock, flags);
+ return cmd;
+}
+
+/*
+ * Unmaps the DMAs of a command and moves the command to the completed ORB list.
+ * Must be called with lu->cmd_orb_lock held.
+ */
+static void sbp2util_mark_command_completed(struct sbp2_lu *lu,
+ struct sbp2_command_info *cmd)
+{
+ if (scsi_sg_count(cmd->Current_SCpnt))
+ dma_unmap_sg(lu->ud->ne->host->device.parent,
+ scsi_sglist(cmd->Current_SCpnt),
+ scsi_sg_count(cmd->Current_SCpnt),
+ cmd->Current_SCpnt->sc_data_direction);
+ list_move_tail(&cmd->list, &lu->cmd_orb_completed);
+}
+
+/*
+ * Is lu valid? Is the 1394 node still present?
+ */
+static inline int sbp2util_node_is_available(struct sbp2_lu *lu)
+{
+ return lu && lu->ne && !lu->ne->in_limbo;
+}
+
+/*********************************************
+ * IEEE-1394 core driver stack related section
+ *********************************************/
+
+static int sbp2_probe(struct device *dev)
+{
+ struct unit_directory *ud;
+ struct sbp2_lu *lu;
+
+ ud = container_of(dev, struct unit_directory, device);
+
+ /* Don't probe UD's that have the LUN flag. We'll probe the LUN(s)
+ * instead. */
+ if (ud->flags & UNIT_DIRECTORY_HAS_LUN_DIRECTORY)
+ return -ENODEV;
+
+ lu = sbp2_alloc_device(ud);
+ if (!lu)
+ return -ENOMEM;
+
+ sbp2_parse_unit_directory(lu, ud);
+ return sbp2_start_device(lu);
+}
+
+static int sbp2_remove(struct device *dev)
+{
+ struct unit_directory *ud;
+ struct sbp2_lu *lu;
+ struct scsi_device *sdev;
+
+ ud = container_of(dev, struct unit_directory, device);
+ lu = ud->device.driver_data;
+ if (!lu)
+ return 0;
+
+ if (lu->shost) {
+ /* Get rid of enqueued commands if there is no chance to
+ * send them. */
+ if (!sbp2util_node_is_available(lu))
+ sbp2scsi_complete_all_commands(lu, DID_NO_CONNECT);
+ /* scsi_remove_device() may trigger shutdown functions of SCSI
+ * highlevel drivers which would deadlock if blocked. */
+ atomic_set(&lu->state, SBP2LU_STATE_IN_SHUTDOWN);
+ scsi_unblock_requests(lu->shost);
+ }
+ sdev = lu->sdev;
+ if (sdev) {
+ lu->sdev = NULL;
+ scsi_remove_device(sdev);
+ }
+
+ sbp2_logout_device(lu);
+ sbp2_remove_device(lu);
+
+ return 0;
+}
+
+static int sbp2_update(struct unit_directory *ud)
+{
+ struct sbp2_lu *lu = ud->device.driver_data;
+
+ if (sbp2_reconnect_device(lu) != 0) {
+ /*
+ * Reconnect failed. If another bus reset happened,
+ * let nodemgr proceed and call sbp2_update again later
+ * (or sbp2_remove if this node went away).
+ */
+ if (!hpsb_node_entry_valid(lu->ne))
+ return 0;
+ /*
+ * Or the target rejected the reconnect because we weren't
+ * fast enough. Try a regular login, but first log out
+ * just in case of any weirdness.
+ */
+ sbp2_logout_device(lu);
+
+ if (sbp2_login_device(lu) != 0) {
+ if (!hpsb_node_entry_valid(lu->ne))
+ return 0;
+
+ /* Maybe another initiator won the login. */
+ SBP2_ERR("Failed to reconnect to sbp2 device!");
+ return -EBUSY;
+ }
+ }
+
+ sbp2_set_busy_timeout(lu);
+ sbp2_agent_reset(lu, 1);
+ sbp2_max_speed_and_size(lu);
+
+ /* Complete any pending commands with busy (so they get retried)
+ * and remove them from our queue. */
+ sbp2scsi_complete_all_commands(lu, DID_BUS_BUSY);
+
+ /* Accept new commands unless there was another bus reset in the
+ * meantime. */
+ if (hpsb_node_entry_valid(lu->ne)) {
+ atomic_set(&lu->state, SBP2LU_STATE_RUNNING);
+ scsi_unblock_requests(lu->shost);
+ }
+ return 0;
+}
+
+static struct sbp2_lu *sbp2_alloc_device(struct unit_directory *ud)
+{
+ struct sbp2_fwhost_info *hi;
+ struct Scsi_Host *shost = NULL;
+ struct sbp2_lu *lu = NULL;
+ unsigned long flags;
+
+ lu = kzalloc(sizeof(*lu), GFP_KERNEL);
+ if (!lu) {
+ SBP2_ERR("failed to create lu");
+ goto failed_alloc;
+ }
+
+ lu->ne = ud->ne;
+ lu->ud = ud;
+ lu->speed_code = IEEE1394_SPEED_100;
+ lu->max_payload_size = sbp2_speedto_max_payload[IEEE1394_SPEED_100];
+ lu->status_fifo_addr = CSR1212_INVALID_ADDR_SPACE;
+ INIT_LIST_HEAD(&lu->cmd_orb_inuse);
+ INIT_LIST_HEAD(&lu->cmd_orb_completed);
+ INIT_LIST_HEAD(&lu->lu_list);
+ spin_lock_init(&lu->cmd_orb_lock);
+ atomic_set(&lu->state, SBP2LU_STATE_RUNNING);
+ INIT_WORK(&lu->protocol_work, NULL);
+
+ ud->device.driver_data = lu;
+
+ hi = hpsb_get_hostinfo(&sbp2_highlevel, ud->ne->host);
+ if (!hi) {
+ hi = hpsb_create_hostinfo(&sbp2_highlevel, ud->ne->host,
+ sizeof(*hi));
+ if (!hi) {
+ SBP2_ERR("failed to allocate hostinfo");
+ goto failed_alloc;
+ }
+ hi->host = ud->ne->host;
+ INIT_LIST_HEAD(&hi->logical_units);
+
+#ifdef CONFIG_IEEE1394_SBP2_PHYS_DMA
+ /* Handle data movement if physical dma is not
+ * enabled or not supported on host controller */
+ if (!hpsb_register_addrspace(&sbp2_highlevel, ud->ne->host,
+ &sbp2_physdma_ops,
+ 0x0ULL, 0xfffffffcULL)) {
+ SBP2_ERR("failed to register lower 4GB address range");
+ goto failed_alloc;
+ }
+#endif
+ }
+
+ if (dma_get_max_seg_size(hi->host->device.parent) > SBP2_MAX_SEG_SIZE)
+ BUG_ON(dma_set_max_seg_size(hi->host->device.parent,
+ SBP2_MAX_SEG_SIZE));
+
+ /* Prevent unloading of the 1394 host */
+ if (!try_module_get(hi->host->driver->owner)) {
+ SBP2_ERR("failed to get a reference on 1394 host driver");
+ goto failed_alloc;
+ }
+
+ lu->hi = hi;
+
+ write_lock_irqsave(&sbp2_hi_logical_units_lock, flags);
+ list_add_tail(&lu->lu_list, &hi->logical_units);
+ write_unlock_irqrestore(&sbp2_hi_logical_units_lock, flags);
+
+ /* Register the status FIFO address range. We could use the same FIFO
+ * for targets at different nodes. However we need different FIFOs per
+ * target in order to support multi-unit devices.
+ * The FIFO is located out of the local host controller's physical range
+ * but, if possible, within the posted write area. Status writes will
+ * then be performed as unified transactions. This slightly reduces
+ * bandwidth usage, and some Prolific based devices seem to require it.
+ */
+ lu->status_fifo_addr = hpsb_allocate_and_register_addrspace(
+ &sbp2_highlevel, ud->ne->host, &sbp2_ops,
+ sizeof(struct sbp2_status_block), sizeof(quadlet_t),
+ ud->ne->host->low_addr_space, CSR1212_ALL_SPACE_END);
+ if (lu->status_fifo_addr == CSR1212_INVALID_ADDR_SPACE) {
+ SBP2_ERR("failed to allocate status FIFO address range");
+ goto failed_alloc;
+ }
+
+ shost = scsi_host_alloc(&sbp2_shost_template, sizeof(unsigned long));
+ if (!shost) {
+ SBP2_ERR("failed to register scsi host");
+ goto failed_alloc;
+ }
+
+ shost->hostdata[0] = (unsigned long)lu;
+
+ if (!scsi_add_host(shost, &ud->device)) {
+ lu->shost = shost;
+ return lu;
+ }
+
+ SBP2_ERR("failed to add scsi host");
+ scsi_host_put(shost);
+
+failed_alloc:
+ sbp2_remove_device(lu);
+ return NULL;
+}
+
+static void sbp2_host_reset(struct hpsb_host *host)
+{
+ struct sbp2_fwhost_info *hi;
+ struct sbp2_lu *lu;
+ unsigned long flags;
+
+ hi = hpsb_get_hostinfo(&sbp2_highlevel, host);
+ if (!hi)
+ return;
+
+ read_lock_irqsave(&sbp2_hi_logical_units_lock, flags);
+
+ list_for_each_entry(lu, &hi->logical_units, lu_list)
+ if (atomic_cmpxchg(&lu->state,
+ SBP2LU_STATE_RUNNING, SBP2LU_STATE_IN_RESET)
+ == SBP2LU_STATE_RUNNING)
+ scsi_block_requests(lu->shost);
+
+ read_unlock_irqrestore(&sbp2_hi_logical_units_lock, flags);
+}
+
+static int sbp2_start_device(struct sbp2_lu *lu)
+{
+ struct sbp2_fwhost_info *hi = lu->hi;
+ int error;
+
+ lu->login_response = dma_alloc_coherent(hi->host->device.parent,
+ sizeof(struct sbp2_login_response),
+ &lu->login_response_dma, GFP_KERNEL);
+ if (!lu->login_response)
+ goto alloc_fail;
+
+ lu->query_logins_orb = dma_alloc_coherent(hi->host->device.parent,
+ sizeof(struct sbp2_query_logins_orb),
+ &lu->query_logins_orb_dma, GFP_KERNEL);
+ if (!lu->query_logins_orb)
+ goto alloc_fail;
+
+ lu->query_logins_response = dma_alloc_coherent(hi->host->device.parent,
+ sizeof(struct sbp2_query_logins_response),
+ &lu->query_logins_response_dma, GFP_KERNEL);
+ if (!lu->query_logins_response)
+ goto alloc_fail;
+
+ lu->reconnect_orb = dma_alloc_coherent(hi->host->device.parent,
+ sizeof(struct sbp2_reconnect_orb),
+ &lu->reconnect_orb_dma, GFP_KERNEL);
+ if (!lu->reconnect_orb)
+ goto alloc_fail;
+
+ lu->logout_orb = dma_alloc_coherent(hi->host->device.parent,
+ sizeof(struct sbp2_logout_orb),
+ &lu->logout_orb_dma, GFP_KERNEL);
+ if (!lu->logout_orb)
+ goto alloc_fail;
+
+ lu->login_orb = dma_alloc_coherent(hi->host->device.parent,
+ sizeof(struct sbp2_login_orb),
+ &lu->login_orb_dma, GFP_KERNEL);
+ if (!lu->login_orb)
+ goto alloc_fail;
+
+ if (sbp2util_create_command_orb_pool(lu))
+ goto alloc_fail;
+
+ /* Wait a second before trying to log in. Previously logged in
+ * initiators need a chance to reconnect. */
+ if (msleep_interruptible(1000)) {
+ sbp2_remove_device(lu);
+ return -EINTR;
+ }
+
+ if (sbp2_login_device(lu)) {
+ sbp2_remove_device(lu);
+ return -EBUSY;
+ }
+
+ sbp2_set_busy_timeout(lu);
+ sbp2_agent_reset(lu, 1);
+ sbp2_max_speed_and_size(lu);
+
+ if (lu->workarounds & SBP2_WORKAROUND_DELAY_INQUIRY)
+ ssleep(SBP2_INQUIRY_DELAY);
+
+ error = scsi_add_device(lu->shost, 0, lu->ud->id, 0);
+ if (error) {
+ SBP2_ERR("scsi_add_device failed");
+ sbp2_logout_device(lu);
+ sbp2_remove_device(lu);
+ return error;
+ }
+
+ return 0;
+
+alloc_fail:
+ SBP2_ERR("Could not allocate memory for lu");
+ sbp2_remove_device(lu);
+ return -ENOMEM;
+}
+
+static void sbp2_remove_device(struct sbp2_lu *lu)
+{
+ struct sbp2_fwhost_info *hi;
+ unsigned long flags;
+
+ if (!lu)
+ return;
+ hi = lu->hi;
+ if (!hi)
+ goto no_hi;
+
+ if (lu->shost) {
+ scsi_remove_host(lu->shost);
+ scsi_host_put(lu->shost);
+ }
+ flush_scheduled_work();
+ sbp2util_remove_command_orb_pool(lu, hi->host);
+
+ write_lock_irqsave(&sbp2_hi_logical_units_lock, flags);
+ list_del(&lu->lu_list);
+ write_unlock_irqrestore(&sbp2_hi_logical_units_lock, flags);
+
+ if (lu->login_response)
+ dma_free_coherent(hi->host->device.parent,
+ sizeof(struct sbp2_login_response),
+ lu->login_response,
+ lu->login_response_dma);
+ if (lu->login_orb)
+ dma_free_coherent(hi->host->device.parent,
+ sizeof(struct sbp2_login_orb),
+ lu->login_orb,
+ lu->login_orb_dma);
+ if (lu->reconnect_orb)
+ dma_free_coherent(hi->host->device.parent,
+ sizeof(struct sbp2_reconnect_orb),
+ lu->reconnect_orb,
+ lu->reconnect_orb_dma);
+ if (lu->logout_orb)
+ dma_free_coherent(hi->host->device.parent,
+ sizeof(struct sbp2_logout_orb),
+ lu->logout_orb,
+ lu->logout_orb_dma);
+ if (lu->query_logins_orb)
+ dma_free_coherent(hi->host->device.parent,
+ sizeof(struct sbp2_query_logins_orb),
+ lu->query_logins_orb,
+ lu->query_logins_orb_dma);
+ if (lu->query_logins_response)
+ dma_free_coherent(hi->host->device.parent,
+ sizeof(struct sbp2_query_logins_response),
+ lu->query_logins_response,
+ lu->query_logins_response_dma);
+
+ if (lu->status_fifo_addr != CSR1212_INVALID_ADDR_SPACE)
+ hpsb_unregister_addrspace(&sbp2_highlevel, hi->host,
+ lu->status_fifo_addr);
+
+ lu->ud->device.driver_data = NULL;
+
+ module_put(hi->host->driver->owner);
+no_hi:
+ kfree(lu);
+}
+
+#ifdef CONFIG_IEEE1394_SBP2_PHYS_DMA
+/*
+ * Deal with write requests on adapters which do not support physical DMA or
+ * have it switched off.
+ */
+static int sbp2_handle_physdma_write(struct hpsb_host *host, int nodeid,
+ int destid, quadlet_t *data, u64 addr,
+ size_t length, u16 flags)
+{
+ memcpy(bus_to_virt((u32) addr), data, length);
+ return RCODE_COMPLETE;
+}
+
+/*
+ * Deal with read requests on adapters which do not support physical DMA or
+ * have it switched off.
+ */
+static int sbp2_handle_physdma_read(struct hpsb_host *host, int nodeid,
+ quadlet_t *data, u64 addr, size_t length,
+ u16 flags)
+{
+ memcpy(data, bus_to_virt((u32) addr), length);
+ return RCODE_COMPLETE;
+}
+#endif
+
+/**************************************
+ * SBP-2 protocol related section
+ **************************************/
+
+static int sbp2_query_logins(struct sbp2_lu *lu)
+{
+ struct sbp2_fwhost_info *hi = lu->hi;
+ quadlet_t data[2];
+ int max_logins;
+ int active_logins;
+
+ lu->query_logins_orb->reserved1 = 0x0;
+ lu->query_logins_orb->reserved2 = 0x0;
+
+ lu->query_logins_orb->query_response_lo = lu->query_logins_response_dma;
+ lu->query_logins_orb->query_response_hi =
+ ORB_SET_NODE_ID(hi->host->node_id);
+ lu->query_logins_orb->lun_misc =
+ ORB_SET_FUNCTION(SBP2_QUERY_LOGINS_REQUEST);
+ lu->query_logins_orb->lun_misc |= ORB_SET_NOTIFY(1);
+ lu->query_logins_orb->lun_misc |= ORB_SET_LUN(lu->lun);
+
+ lu->query_logins_orb->reserved_resp_length =
+ ORB_SET_QUERY_LOGINS_RESP_LENGTH(
+ sizeof(struct sbp2_query_logins_response));
+
+ lu->query_logins_orb->status_fifo_hi =
+ ORB_SET_STATUS_FIFO_HI(lu->status_fifo_addr, hi->host->node_id);
+ lu->query_logins_orb->status_fifo_lo =
+ ORB_SET_STATUS_FIFO_LO(lu->status_fifo_addr);
+
+ sbp2util_cpu_to_be32_buffer(lu->query_logins_orb,
+ sizeof(struct sbp2_query_logins_orb));
+
+ memset(lu->query_logins_response, 0,
+ sizeof(struct sbp2_query_logins_response));
+
+ data[0] = ORB_SET_NODE_ID(hi->host->node_id);
+ data[1] = lu->query_logins_orb_dma;
+ sbp2util_cpu_to_be32_buffer(data, 8);
+
+ hpsb_node_write(lu->ne, lu->management_agent_addr, data, 8);
+
+ if (sbp2util_access_timeout(lu, 2*HZ)) {
+ SBP2_INFO("Error querying logins to SBP-2 device - timed out");
+ return -EIO;
+ }
+
+ if (lu->status_block.ORB_offset_lo != lu->query_logins_orb_dma) {
+ SBP2_INFO("Error querying logins to SBP-2 device - timed out");
+ return -EIO;
+ }
+
+ if (STATUS_TEST_RDS(lu->status_block.ORB_offset_hi_misc)) {
+ SBP2_INFO("Error querying logins to SBP-2 device - failed");
+ return -EIO;
+ }
+
+ sbp2util_cpu_to_be32_buffer(lu->query_logins_response,
+ sizeof(struct sbp2_query_logins_response));
+
+ max_logins = RESPONSE_GET_MAX_LOGINS(
+ lu->query_logins_response->length_max_logins);
+ SBP2_INFO("Maximum concurrent logins supported: %d", max_logins);
+
+ active_logins = RESPONSE_GET_ACTIVE_LOGINS(
+ lu->query_logins_response->length_max_logins);
+ SBP2_INFO("Number of active logins: %d", active_logins);
+
+ if (active_logins >= max_logins) {
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int sbp2_login_device(struct sbp2_lu *lu)
+{
+ struct sbp2_fwhost_info *hi = lu->hi;
+ quadlet_t data[2];
+
+ if (!lu->login_orb)
+ return -EIO;
+
+ if (!sbp2_exclusive_login && sbp2_query_logins(lu)) {
+ SBP2_INFO("Device does not support any more concurrent logins");
+ return -EIO;
+ }
+
+ /* assume no password */
+ lu->login_orb->password_hi = 0;
+ lu->login_orb->password_lo = 0;
+
+ lu->login_orb->login_response_lo = lu->login_response_dma;
+ lu->login_orb->login_response_hi = ORB_SET_NODE_ID(hi->host->node_id);
+ lu->login_orb->lun_misc = ORB_SET_FUNCTION(SBP2_LOGIN_REQUEST);
+
+ /* one second reconnect time */
+ lu->login_orb->lun_misc |= ORB_SET_RECONNECT(0);
+ lu->login_orb->lun_misc |= ORB_SET_EXCLUSIVE(sbp2_exclusive_login);
+ lu->login_orb->lun_misc |= ORB_SET_NOTIFY(1);
+ lu->login_orb->lun_misc |= ORB_SET_LUN(lu->lun);
+
+ lu->login_orb->passwd_resp_lengths =
+ ORB_SET_LOGIN_RESP_LENGTH(sizeof(struct sbp2_login_response));
+
+ lu->login_orb->status_fifo_hi =
+ ORB_SET_STATUS_FIFO_HI(lu->status_fifo_addr, hi->host->node_id);
+ lu->login_orb->status_fifo_lo =
+ ORB_SET_STATUS_FIFO_LO(lu->status_fifo_addr);
+
+ sbp2util_cpu_to_be32_buffer(lu->login_orb,
+ sizeof(struct sbp2_login_orb));
+
+ memset(lu->login_response, 0, sizeof(struct sbp2_login_response));
+
+ data[0] = ORB_SET_NODE_ID(hi->host->node_id);
+ data[1] = lu->login_orb_dma;
+ sbp2util_cpu_to_be32_buffer(data, 8);
+
+ hpsb_node_write(lu->ne, lu->management_agent_addr, data, 8);
+
+ /* wait up to 20 seconds for login status */
+ if (sbp2util_access_timeout(lu, 20*HZ)) {
+ SBP2_ERR("Error logging into SBP-2 device - timed out");
+ return -EIO;
+ }
+
+ /* make sure that the returned status matches the login ORB */
+ if (lu->status_block.ORB_offset_lo != lu->login_orb_dma) {
+ SBP2_ERR("Error logging into SBP-2 device - timed out");
+ return -EIO;
+ }
+
+ if (STATUS_TEST_RDS(lu->status_block.ORB_offset_hi_misc)) {
+ SBP2_ERR("Error logging into SBP-2 device - failed");
+ return -EIO;
+ }
+
+ sbp2util_cpu_to_be32_buffer(lu->login_response,
+ sizeof(struct sbp2_login_response));
+ lu->command_block_agent_addr =
+ ((u64)lu->login_response->command_block_agent_hi) << 32;
+ lu->command_block_agent_addr |=
+ ((u64)lu->login_response->command_block_agent_lo);
+ lu->command_block_agent_addr &= 0x0000ffffffffffffULL;
+
+ SBP2_INFO("Logged into SBP-2 device");
+ return 0;
+}
+
+static int sbp2_logout_device(struct sbp2_lu *lu)
+{
+ struct sbp2_fwhost_info *hi = lu->hi;
+ quadlet_t data[2];
+ int error;
+
+ lu->logout_orb->reserved1 = 0x0;
+ lu->logout_orb->reserved2 = 0x0;
+ lu->logout_orb->reserved3 = 0x0;
+ lu->logout_orb->reserved4 = 0x0;
+
+ lu->logout_orb->login_ID_misc = ORB_SET_FUNCTION(SBP2_LOGOUT_REQUEST);
+ lu->logout_orb->login_ID_misc |=
+ ORB_SET_LOGIN_ID(lu->login_response->length_login_ID);
+ lu->logout_orb->login_ID_misc |= ORB_SET_NOTIFY(1);
+
+ lu->logout_orb->reserved5 = 0x0;
+ lu->logout_orb->status_fifo_hi =
+ ORB_SET_STATUS_FIFO_HI(lu->status_fifo_addr, hi->host->node_id);
+ lu->logout_orb->status_fifo_lo =
+ ORB_SET_STATUS_FIFO_LO(lu->status_fifo_addr);
+
+ sbp2util_cpu_to_be32_buffer(lu->logout_orb,
+ sizeof(struct sbp2_logout_orb));
+
+ data[0] = ORB_SET_NODE_ID(hi->host->node_id);
+ data[1] = lu->logout_orb_dma;
+ sbp2util_cpu_to_be32_buffer(data, 8);
+
+ error = hpsb_node_write(lu->ne, lu->management_agent_addr, data, 8);
+ if (error)
+ return error;
+
+ /* wait up to 1 second for the device to complete logout */
+ if (sbp2util_access_timeout(lu, HZ))
+ return -EIO;
+
+ SBP2_INFO("Logged out of SBP-2 device");
+ return 0;
+}
+
+static int sbp2_reconnect_device(struct sbp2_lu *lu)
+{
+ struct sbp2_fwhost_info *hi = lu->hi;
+ quadlet_t data[2];
+ int error;
+
+ lu->reconnect_orb->reserved1 = 0x0;
+ lu->reconnect_orb->reserved2 = 0x0;
+ lu->reconnect_orb->reserved3 = 0x0;
+ lu->reconnect_orb->reserved4 = 0x0;
+
+ lu->reconnect_orb->login_ID_misc =
+ ORB_SET_FUNCTION(SBP2_RECONNECT_REQUEST);
+ lu->reconnect_orb->login_ID_misc |=
+ ORB_SET_LOGIN_ID(lu->login_response->length_login_ID);
+ lu->reconnect_orb->login_ID_misc |= ORB_SET_NOTIFY(1);
+
+ lu->reconnect_orb->reserved5 = 0x0;
+ lu->reconnect_orb->status_fifo_hi =
+ ORB_SET_STATUS_FIFO_HI(lu->status_fifo_addr, hi->host->node_id);
+ lu->reconnect_orb->status_fifo_lo =
+ ORB_SET_STATUS_FIFO_LO(lu->status_fifo_addr);
+
+ sbp2util_cpu_to_be32_buffer(lu->reconnect_orb,
+ sizeof(struct sbp2_reconnect_orb));
+
+ data[0] = ORB_SET_NODE_ID(hi->host->node_id);
+ data[1] = lu->reconnect_orb_dma;
+ sbp2util_cpu_to_be32_buffer(data, 8);
+
+ error = hpsb_node_write(lu->ne, lu->management_agent_addr, data, 8);
+ if (error)
+ return error;
+
+ /* wait up to 1 second for reconnect status */
+ if (sbp2util_access_timeout(lu, HZ)) {
+ SBP2_ERR("Error reconnecting to SBP-2 device - timed out");
+ return -EIO;
+ }
+
+ /* make sure that the returned status matches the reconnect ORB */
+ if (lu->status_block.ORB_offset_lo != lu->reconnect_orb_dma) {
+ SBP2_ERR("Error reconnecting to SBP-2 device - timed out");
+ return -EIO;
+ }
+
+ if (STATUS_TEST_RDS(lu->status_block.ORB_offset_hi_misc)) {
+ SBP2_ERR("Error reconnecting to SBP-2 device - failed");
+ return -EIO;
+ }
+
+ SBP2_INFO("Reconnected to SBP-2 device");
+ return 0;
+}
+
+/*
+ * Set the target node's Single Phase Retry limit. Affects the target's retry
+ * behaviour if our node is too busy to accept requests.
+ */
+static int sbp2_set_busy_timeout(struct sbp2_lu *lu)
+{
+ quadlet_t data;
+
+ data = cpu_to_be32(SBP2_BUSY_TIMEOUT_VALUE);
+ if (hpsb_node_write(lu->ne, SBP2_BUSY_TIMEOUT_ADDRESS, &data, 4))
+ SBP2_ERR("%s error", __func__);
+ return 0;
+}
+
+static void sbp2_parse_unit_directory(struct sbp2_lu *lu,
+ struct unit_directory *ud)
+{
+ struct csr1212_keyval *kv;
+ struct csr1212_dentry *dentry;
+ u64 management_agent_addr;
+ u32 unit_characteristics, firmware_revision;
+ unsigned workarounds;
+ int i;
+
+ management_agent_addr = 0;
+ unit_characteristics = 0;
+ firmware_revision = 0;
+
+ csr1212_for_each_dir_entry(ud->ne->csr, kv, ud->ud_kv, dentry) {
+ switch (kv->key.id) {
+ case CSR1212_KV_ID_DEPENDENT_INFO:
+ if (kv->key.type == CSR1212_KV_TYPE_CSR_OFFSET)
+ management_agent_addr =
+ CSR1212_REGISTER_SPACE_BASE +
+ (kv->value.csr_offset << 2);
+
+ else if (kv->key.type == CSR1212_KV_TYPE_IMMEDIATE)
+ lu->lun = ORB_SET_LUN(kv->value.immediate);
+ break;
+
+ case SBP2_UNIT_CHARACTERISTICS_KEY:
+ /* FIXME: This is ignored so far.
+ * See SBP-2 clause 7.4.8. */
+ unit_characteristics = kv->value.immediate;
+ break;
+
+ case SBP2_FIRMWARE_REVISION_KEY:
+ firmware_revision = kv->value.immediate;
+ break;
+
+ default:
+ /* FIXME: Check for SBP2_DEVICE_TYPE_AND_LUN_KEY.
+ * Its "ordered" bit has consequences for command ORB
+ * list handling. See SBP-2 clauses 4.6, 7.4.11, 10.2 */
+ break;
+ }
+ }
+
+ workarounds = sbp2_default_workarounds;
+
+ if (!(workarounds & SBP2_WORKAROUND_OVERRIDE))
+ for (i = 0; i < ARRAY_SIZE(sbp2_workarounds_table); i++) {
+ if (sbp2_workarounds_table[i].firmware_revision !=
+ SBP2_ROM_VALUE_WILDCARD &&
+ sbp2_workarounds_table[i].firmware_revision !=
+ (firmware_revision & 0xffff00))
+ continue;
+ if (sbp2_workarounds_table[i].model_id !=
+ SBP2_ROM_VALUE_WILDCARD &&
+ sbp2_workarounds_table[i].model_id != ud->model_id)
+ continue;
+ workarounds |= sbp2_workarounds_table[i].workarounds;
+ break;
+ }
+
+ if (workarounds)
+ SBP2_INFO("Workarounds for node " NODE_BUS_FMT ": 0x%x "
+ "(firmware_revision 0x%06x, vendor_id 0x%06x,"
+ " model_id 0x%06x)",
+ NODE_BUS_ARGS(ud->ne->host, ud->ne->nodeid),
+ workarounds, firmware_revision,
+ ud->vendor_id ? ud->vendor_id : ud->ne->vendor_id,
+ ud->model_id);
+
+ /* We would need one SCSI host template for each target to adjust
+ * max_sectors on the fly, therefore warn only. */
+ if (workarounds & SBP2_WORKAROUND_128K_MAX_TRANS &&
+ (sbp2_max_sectors * 512) > (128 * 1024))
+ SBP2_INFO("Node " NODE_BUS_FMT ": Bridge only supports 128KB "
+ "max transfer size. WARNING: Current max_sectors "
+ "setting is larger than 128KB (%d sectors)",
+ NODE_BUS_ARGS(ud->ne->host, ud->ne->nodeid),
+ sbp2_max_sectors);
+
+ /* If this is a logical unit directory entry, process the parent
+ * to get the values. */
+ if (ud->flags & UNIT_DIRECTORY_LUN_DIRECTORY) {
+ struct unit_directory *parent_ud = container_of(
+ ud->device.parent, struct unit_directory, device);
+ sbp2_parse_unit_directory(lu, parent_ud);
+ } else {
+ lu->management_agent_addr = management_agent_addr;
+ lu->workarounds = workarounds;
+ if (ud->flags & UNIT_DIRECTORY_HAS_LUN)
+ lu->lun = ORB_SET_LUN(ud->lun);
+ }
+}
+
+#define SBP2_PAYLOAD_TO_BYTES(p) (1 << ((p) + 2))
+
+/*
+ * This function is called in order to determine the max speed and packet
+ * size we can use in our ORBs. Note, that we (the driver and host) only
+ * initiate the transaction. The SBP-2 device actually transfers the data
+ * (by reading from the DMA area we tell it). This means that the SBP-2
+ * device decides the actual maximum data it can transfer. We just tell it
+ * the speed that it needs to use, and the max_rec the host supports, and
+ * it takes care of the rest.
+ */
+static int sbp2_max_speed_and_size(struct sbp2_lu *lu)
+{
+ struct sbp2_fwhost_info *hi = lu->hi;
+ u8 payload;
+
+ lu->speed_code = hi->host->speed[NODEID_TO_NODE(lu->ne->nodeid)];
+
+ if (lu->speed_code > sbp2_max_speed) {
+ lu->speed_code = sbp2_max_speed;
+ SBP2_INFO("Reducing speed to %s",
+ hpsb_speedto_str[sbp2_max_speed]);
+ }
+
+ /* Payload size is the lesser of what our speed supports and what
+ * our host supports. */
+ payload = min(sbp2_speedto_max_payload[lu->speed_code],
+ (u8) (hi->host->csr.max_rec - 1));
+
+ /* If physical DMA is off, work around limitation in ohci1394:
+ * packet size must not exceed PAGE_SIZE */
+ if (lu->ne->host->low_addr_space < (1ULL << 32))
+ while (SBP2_PAYLOAD_TO_BYTES(payload) + 24 > PAGE_SIZE &&
+ payload)
+ payload--;
+
+ SBP2_INFO("Node " NODE_BUS_FMT ": Max speed [%s] - Max payload [%u]",
+ NODE_BUS_ARGS(hi->host, lu->ne->nodeid),
+ hpsb_speedto_str[lu->speed_code],
+ SBP2_PAYLOAD_TO_BYTES(payload));
+
+ lu->max_payload_size = payload;
+ return 0;
+}
+
+static int sbp2_agent_reset(struct sbp2_lu *lu, int wait)
+{
+ quadlet_t data;
+ u64 addr;
+ int retval;
+ unsigned long flags;
+
+ /* flush lu->protocol_work */
+ if (wait)
+ flush_scheduled_work();
+
+ data = ntohl(SBP2_AGENT_RESET_DATA);
+ addr = lu->command_block_agent_addr + SBP2_AGENT_RESET_OFFSET;
+
+ if (wait)
+ retval = hpsb_node_write(lu->ne, addr, &data, 4);
+ else
+ retval = sbp2util_node_write_no_wait(lu->ne, addr, &data, 4);
+
+ if (retval < 0) {
+ SBP2_ERR("hpsb_node_write failed.\n");
+ return -EIO;
+ }
+
+ /* make sure that the ORB_POINTER is written on next command */
+ spin_lock_irqsave(&lu->cmd_orb_lock, flags);
+ lu->last_orb = NULL;
+ spin_unlock_irqrestore(&lu->cmd_orb_lock, flags);
+
+ return 0;
+}
+
+static int sbp2_prep_command_orb_sg(struct sbp2_command_orb *orb,
+ struct sbp2_fwhost_info *hi,
+ struct sbp2_command_info *cmd,
+ unsigned int sg_count,
+ struct scatterlist *sg,
+ u32 orb_direction,
+ enum dma_data_direction dma_dir)
+{
+ struct device *dmadev = hi->host->device.parent;
+ struct sbp2_unrestricted_page_table *pt;
+ int i, n;
+
+ n = dma_map_sg(dmadev, sg, sg_count, dma_dir);
+ if (n == 0)
+ return -ENOMEM;
+
+ orb->data_descriptor_hi = ORB_SET_NODE_ID(hi->host->node_id);
+ orb->misc |= ORB_SET_DIRECTION(orb_direction);
+
+ /* special case if only one element (and less than 64KB in size) */
+ if (n == 1) {
+ orb->misc |= ORB_SET_DATA_SIZE(sg_dma_len(sg));
+ orb->data_descriptor_lo = sg_dma_address(sg);
+ } else {
+ pt = &cmd->scatter_gather_element[0];
+
+ dma_sync_single_for_cpu(dmadev, cmd->sge_dma,
+ sizeof(cmd->scatter_gather_element),
+ DMA_TO_DEVICE);
+
+ for_each_sg(sg, sg, n, i) {
+ pt[i].high = cpu_to_be32(sg_dma_len(sg) << 16);
+ pt[i].low = cpu_to_be32(sg_dma_address(sg));
+ }
+
+ orb->misc |= ORB_SET_PAGE_TABLE_PRESENT(0x1) |
+ ORB_SET_DATA_SIZE(n);
+ orb->data_descriptor_lo = cmd->sge_dma;
+
+ dma_sync_single_for_device(dmadev, cmd->sge_dma,
+ sizeof(cmd->scatter_gather_element),
+ DMA_TO_DEVICE);
+ }
+ return 0;
+}
+
+static int sbp2_create_command_orb(struct sbp2_lu *lu,
+ struct sbp2_command_info *cmd,
+ struct scsi_cmnd *SCpnt)
+{
+ struct device *dmadev = lu->hi->host->device.parent;
+ struct sbp2_command_orb *orb = &cmd->command_orb;
+ unsigned int scsi_request_bufflen = scsi_bufflen(SCpnt);
+ enum dma_data_direction dma_dir = SCpnt->sc_data_direction;
+ u32 orb_direction;
+ int ret;
+
+ dma_sync_single_for_cpu(dmadev, cmd->command_orb_dma,
+ sizeof(struct sbp2_command_orb), DMA_TO_DEVICE);
+ /*
+ * Set-up our command ORB.
+ *
+ * NOTE: We're doing unrestricted page tables (s/g), as this is
+ * best performance (at least with the devices I have). This means
+ * that data_size becomes the number of s/g elements, and
+ * page_size should be zero (for unrestricted).
+ */
+ orb->next_ORB_hi = ORB_SET_NULL_PTR(1);
+ orb->next_ORB_lo = 0x0;
+ orb->misc = ORB_SET_MAX_PAYLOAD(lu->max_payload_size);
+ orb->misc |= ORB_SET_SPEED(lu->speed_code);
+ orb->misc |= ORB_SET_NOTIFY(1);
+
+ if (dma_dir == DMA_NONE)
+ orb_direction = ORB_DIRECTION_NO_DATA_TRANSFER;
+ else if (dma_dir == DMA_TO_DEVICE && scsi_request_bufflen)
+ orb_direction = ORB_DIRECTION_WRITE_TO_MEDIA;
+ else if (dma_dir == DMA_FROM_DEVICE && scsi_request_bufflen)
+ orb_direction = ORB_DIRECTION_READ_FROM_MEDIA;
+ else {
+ SBP2_INFO("Falling back to DMA_NONE");
+ orb_direction = ORB_DIRECTION_NO_DATA_TRANSFER;
+ }
+
+ /* set up our page table stuff */
+ if (orb_direction == ORB_DIRECTION_NO_DATA_TRANSFER) {
+ orb->data_descriptor_hi = 0x0;
+ orb->data_descriptor_lo = 0x0;
+ orb->misc |= ORB_SET_DIRECTION(1);
+ ret = 0;
+ } else {
+ ret = sbp2_prep_command_orb_sg(orb, lu->hi, cmd,
+ scsi_sg_count(SCpnt),
+ scsi_sglist(SCpnt),
+ orb_direction, dma_dir);
+ }
+ sbp2util_cpu_to_be32_buffer(orb, sizeof(*orb));
+
+ memset(orb->cdb, 0, sizeof(orb->cdb));
+ memcpy(orb->cdb, SCpnt->cmnd, SCpnt->cmd_len);
+
+ dma_sync_single_for_device(dmadev, cmd->command_orb_dma,
+ sizeof(struct sbp2_command_orb), DMA_TO_DEVICE);
+ return ret;
+}
+
+static void sbp2_link_orb_command(struct sbp2_lu *lu,
+ struct sbp2_command_info *cmd)
+{
+ struct sbp2_fwhost_info *hi = lu->hi;
+ struct sbp2_command_orb *last_orb;
+ dma_addr_t last_orb_dma;
+ u64 addr = lu->command_block_agent_addr;
+ quadlet_t data[2];
+ size_t length;
+ unsigned long flags;
+
+ /* check to see if there are any previous orbs to use */
+ spin_lock_irqsave(&lu->cmd_orb_lock, flags);
+ last_orb = lu->last_orb;
+ last_orb_dma = lu->last_orb_dma;
+ if (!last_orb) {
+ /*
+ * last_orb == NULL means: We know that the target's fetch agent
+ * is not active right now.
+ */
+ addr += SBP2_ORB_POINTER_OFFSET;
+ data[0] = ORB_SET_NODE_ID(hi->host->node_id);
+ data[1] = cmd->command_orb_dma;
+ sbp2util_cpu_to_be32_buffer(data, 8);
+ length = 8;
+ } else {
+ /*
+ * last_orb != NULL means: We know that the target's fetch agent
+ * is (very probably) not dead or in reset state right now.
+ * We have an ORB already sent that we can append a new one to.
+ * The target's fetch agent may or may not have read this
+ * previous ORB yet.
+ */
+ dma_sync_single_for_cpu(hi->host->device.parent, last_orb_dma,
+ sizeof(struct sbp2_command_orb),
+ DMA_TO_DEVICE);
+ last_orb->next_ORB_lo = cpu_to_be32(cmd->command_orb_dma);
+ wmb();
+ /* Tells hardware that this pointer is valid */
+ last_orb->next_ORB_hi = 0;
+ dma_sync_single_for_device(hi->host->device.parent,
+ last_orb_dma,
+ sizeof(struct sbp2_command_orb),
+ DMA_TO_DEVICE);
+ addr += SBP2_DOORBELL_OFFSET;
+ data[0] = 0;
+ length = 4;
+ }
+ lu->last_orb = &cmd->command_orb;
+ lu->last_orb_dma = cmd->command_orb_dma;
+ spin_unlock_irqrestore(&lu->cmd_orb_lock, flags);
+
+ if (sbp2util_node_write_no_wait(lu->ne, addr, data, length)) {
+ /*
+ * sbp2util_node_write_no_wait failed. We certainly ran out
+ * of transaction labels, perhaps just because there were no
+ * context switches which gave khpsbpkt a chance to collect
+ * free tlabels. Try again in non-atomic context. If necessary,
+ * the workqueue job will sleep to guaranteedly get a tlabel.
+ * We do not accept new commands until the job is over.
+ */
+ scsi_block_requests(lu->shost);
+ PREPARE_WORK(&lu->protocol_work,
+ last_orb ? sbp2util_write_doorbell:
+ sbp2util_write_orb_pointer);
+ schedule_work(&lu->protocol_work);
+ }
+}
+
+static int sbp2_send_command(struct sbp2_lu *lu, struct scsi_cmnd *SCpnt,
+ void (*done)(struct scsi_cmnd *))
+{
+ struct sbp2_command_info *cmd;
+
+ cmd = sbp2util_allocate_command_orb(lu, SCpnt, done);
+ if (!cmd)
+ return -EIO;
+
+ if (sbp2_create_command_orb(lu, cmd, SCpnt))
+ return -ENOMEM;
+
+ sbp2_link_orb_command(lu, cmd);
+ return 0;
+}
+
+/*
+ * Translates SBP-2 status into SCSI sense data for check conditions
+ */
+static unsigned int sbp2_status_to_sense_data(unchar *sbp2_status,
+ unchar *sense_data)
+{
+ /* OK, it's pretty ugly... ;-) */
+ sense_data[0] = 0x70;
+ sense_data[1] = 0x0;
+ sense_data[2] = sbp2_status[9];
+ sense_data[3] = sbp2_status[12];
+ sense_data[4] = sbp2_status[13];
+ sense_data[5] = sbp2_status[14];
+ sense_data[6] = sbp2_status[15];
+ sense_data[7] = 10;
+ sense_data[8] = sbp2_status[16];
+ sense_data[9] = sbp2_status[17];
+ sense_data[10] = sbp2_status[18];
+ sense_data[11] = sbp2_status[19];
+ sense_data[12] = sbp2_status[10];
+ sense_data[13] = sbp2_status[11];
+ sense_data[14] = sbp2_status[20];
+ sense_data[15] = sbp2_status[21];
+
+ return sbp2_status[8] & 0x3f;
+}
+
+static int sbp2_handle_status_write(struct hpsb_host *host, int nodeid,
+ int destid, quadlet_t *data, u64 addr,
+ size_t length, u16 fl)
+{
+ struct sbp2_fwhost_info *hi;
+ struct sbp2_lu *lu = NULL, *lu_tmp;
+ struct scsi_cmnd *SCpnt = NULL;
+ struct sbp2_status_block *sb;
+ u32 scsi_status = SBP2_SCSI_STATUS_GOOD;
+ struct sbp2_command_info *cmd;
+ unsigned long flags;
+
+ if (unlikely(length < 8 || length > sizeof(struct sbp2_status_block))) {
+ SBP2_ERR("Wrong size of status block");
+ return RCODE_ADDRESS_ERROR;
+ }
+ if (unlikely(!host)) {
+ SBP2_ERR("host is NULL - this is bad!");
+ return RCODE_ADDRESS_ERROR;
+ }
+ hi = hpsb_get_hostinfo(&sbp2_highlevel, host);
+ if (unlikely(!hi)) {
+ SBP2_ERR("host info is NULL - this is bad!");
+ return RCODE_ADDRESS_ERROR;
+ }
+
+ /* Find the unit which wrote the status. */
+ read_lock_irqsave(&sbp2_hi_logical_units_lock, flags);
+ list_for_each_entry(lu_tmp, &hi->logical_units, lu_list) {
+ if (lu_tmp->ne->nodeid == nodeid &&
+ lu_tmp->status_fifo_addr == addr) {
+ lu = lu_tmp;
+ break;
+ }
+ }
+ read_unlock_irqrestore(&sbp2_hi_logical_units_lock, flags);
+
+ if (unlikely(!lu)) {
+ SBP2_ERR("lu is NULL - device is gone?");
+ return RCODE_ADDRESS_ERROR;
+ }
+
+ /* Put response into lu status fifo buffer. The first two bytes
+ * come in big endian bit order. Often the target writes only a
+ * truncated status block, minimally the first two quadlets. The rest
+ * is implied to be zeros. */
+ sb = &lu->status_block;
+ memset(sb->command_set_dependent, 0, sizeof(sb->command_set_dependent));
+ memcpy(sb, data, length);
+ sbp2util_be32_to_cpu_buffer(sb, 8);
+
+ /* Ignore unsolicited status. Handle command ORB status. */
+ if (unlikely(STATUS_GET_SRC(sb->ORB_offset_hi_misc) == 2))
+ cmd = NULL;
+ else
+ cmd = sbp2util_find_command_for_orb(lu, sb->ORB_offset_lo);
+ if (cmd) {
+ /* Grab SCSI command pointers and check status. */
+ /*
+ * FIXME: If the src field in the status is 1, the ORB DMA must
+ * not be reused until status for a subsequent ORB is received.
+ */
+ SCpnt = cmd->Current_SCpnt;
+ spin_lock_irqsave(&lu->cmd_orb_lock, flags);
+ sbp2util_mark_command_completed(lu, cmd);
+ spin_unlock_irqrestore(&lu->cmd_orb_lock, flags);
+
+ if (SCpnt) {
+ u32 h = sb->ORB_offset_hi_misc;
+ u32 r = STATUS_GET_RESP(h);
+
+ if (r != RESP_STATUS_REQUEST_COMPLETE) {
+ SBP2_INFO("resp 0x%x, sbp_status 0x%x",
+ r, STATUS_GET_SBP_STATUS(h));
+ scsi_status =
+ r == RESP_STATUS_TRANSPORT_FAILURE ?
+ SBP2_SCSI_STATUS_BUSY :
+ SBP2_SCSI_STATUS_COMMAND_TERMINATED;
+ }
+
+ if (STATUS_GET_LEN(h) > 1)
+ scsi_status = sbp2_status_to_sense_data(
+ (unchar *)sb, SCpnt->sense_buffer);
+
+ if (STATUS_TEST_DEAD(h))
+ sbp2_agent_reset(lu, 0);
+ }
+
+ /* Check here to see if there are no commands in-use. If there
+ * are none, we know that the fetch agent left the active state
+ * _and_ that we did not reactivate it yet. Therefore clear
+ * last_orb so that next time we write directly to the
+ * ORB_POINTER register. That way the fetch agent does not need
+ * to refetch the next_ORB. */
+ spin_lock_irqsave(&lu->cmd_orb_lock, flags);
+ if (list_empty(&lu->cmd_orb_inuse))
+ lu->last_orb = NULL;
+ spin_unlock_irqrestore(&lu->cmd_orb_lock, flags);
+
+ } else {
+ /* It's probably status after a management request. */
+ if ((sb->ORB_offset_lo == lu->reconnect_orb_dma) ||
+ (sb->ORB_offset_lo == lu->login_orb_dma) ||
+ (sb->ORB_offset_lo == lu->query_logins_orb_dma) ||
+ (sb->ORB_offset_lo == lu->logout_orb_dma)) {
+ lu->access_complete = 1;
+ wake_up_interruptible(&sbp2_access_wq);
+ }
+ }
+
+ if (SCpnt)
+ sbp2scsi_complete_command(lu, scsi_status, SCpnt,
+ cmd->Current_done);
+ return RCODE_COMPLETE;
+}
+
+/**************************************
+ * SCSI interface related section
+ **************************************/
+
+static int sbp2scsi_queuecommand(struct scsi_cmnd *SCpnt,
+ void (*done)(struct scsi_cmnd *))
+{
+ struct sbp2_lu *lu = (struct sbp2_lu *)SCpnt->device->host->hostdata[0];
+ struct sbp2_fwhost_info *hi;
+ int result = DID_NO_CONNECT << 16;
+
+ if (unlikely(!sbp2util_node_is_available(lu)))
+ goto done;
+
+ hi = lu->hi;
+
+ if (unlikely(!hi)) {
+ SBP2_ERR("sbp2_fwhost_info is NULL - this is bad!");
+ goto done;
+ }
+
+ /* Multiple units are currently represented to the SCSI core as separate
+ * targets, not as one target with multiple LUs. Therefore return
+ * selection time-out to any IO directed at non-zero LUNs. */
+ if (unlikely(SCpnt->device->lun))
+ goto done;
+
+ if (unlikely(!hpsb_node_entry_valid(lu->ne))) {
+ SBP2_ERR("Bus reset in progress - rejecting command");
+ result = DID_BUS_BUSY << 16;
+ goto done;
+ }
+
+ /* Bidirectional commands are not yet implemented,
+ * and unknown transfer direction not handled. */
+ if (unlikely(SCpnt->sc_data_direction == DMA_BIDIRECTIONAL)) {
+ SBP2_ERR("Cannot handle DMA_BIDIRECTIONAL - rejecting command");
+ result = DID_ERROR << 16;
+ goto done;
+ }
+
+ if (sbp2_send_command(lu, SCpnt, done)) {
+ SBP2_ERR("Error sending SCSI command");
+ sbp2scsi_complete_command(lu,
+ SBP2_SCSI_STATUS_SELECTION_TIMEOUT,
+ SCpnt, done);
+ }
+ return 0;
+
+done:
+ SCpnt->result = result;
+ done(SCpnt);
+ return 0;
+}
+
+static void sbp2scsi_complete_all_commands(struct sbp2_lu *lu, u32 status)
+{
+ struct list_head *lh;
+ struct sbp2_command_info *cmd;
+ unsigned long flags;
+
+ spin_lock_irqsave(&lu->cmd_orb_lock, flags);
+ while (!list_empty(&lu->cmd_orb_inuse)) {
+ lh = lu->cmd_orb_inuse.next;
+ cmd = list_entry(lh, struct sbp2_command_info, list);
+ sbp2util_mark_command_completed(lu, cmd);
+ if (cmd->Current_SCpnt) {
+ cmd->Current_SCpnt->result = status << 16;
+ cmd->Current_done(cmd->Current_SCpnt);
+ }
+ }
+ spin_unlock_irqrestore(&lu->cmd_orb_lock, flags);
+
+ return;
+}
+
+/*
+ * Complete a regular SCSI command. Can be called in atomic context.
+ */
+static void sbp2scsi_complete_command(struct sbp2_lu *lu, u32 scsi_status,
+ struct scsi_cmnd *SCpnt,
+ void (*done)(struct scsi_cmnd *))
+{
+ if (!SCpnt) {
+ SBP2_ERR("SCpnt is NULL");
+ return;
+ }
+
+ switch (scsi_status) {
+ case SBP2_SCSI_STATUS_GOOD:
+ SCpnt->result = DID_OK << 16;
+ break;
+
+ case SBP2_SCSI_STATUS_BUSY:
+ SBP2_ERR("SBP2_SCSI_STATUS_BUSY");
+ SCpnt->result = DID_BUS_BUSY << 16;
+ break;
+
+ case SBP2_SCSI_STATUS_CHECK_CONDITION:
+ SCpnt->result = CHECK_CONDITION << 1 | DID_OK << 16;
+ break;
+
+ case SBP2_SCSI_STATUS_SELECTION_TIMEOUT:
+ SBP2_ERR("SBP2_SCSI_STATUS_SELECTION_TIMEOUT");
+ SCpnt->result = DID_NO_CONNECT << 16;
+ scsi_print_command(SCpnt);
+ break;
+
+ case SBP2_SCSI_STATUS_CONDITION_MET:
+ case SBP2_SCSI_STATUS_RESERVATION_CONFLICT:
+ case SBP2_SCSI_STATUS_COMMAND_TERMINATED:
+ SBP2_ERR("Bad SCSI status = %x", scsi_status);
+ SCpnt->result = DID_ERROR << 16;
+ scsi_print_command(SCpnt);
+ break;
+
+ default:
+ SBP2_ERR("Unsupported SCSI status = %x", scsi_status);
+ SCpnt->result = DID_ERROR << 16;
+ }
+
+ /* If a bus reset is in progress and there was an error, complete
+ * the command as busy so that it will get retried. */
+ if (!hpsb_node_entry_valid(lu->ne)
+ && (scsi_status != SBP2_SCSI_STATUS_GOOD)) {
+ SBP2_ERR("Completing command with busy (bus reset)");
+ SCpnt->result = DID_BUS_BUSY << 16;
+ }
+
+ /* Tell the SCSI stack that we're done with this command. */
+ done(SCpnt);
+}
+
+static int sbp2scsi_slave_alloc(struct scsi_device *sdev)
+{
+ struct sbp2_lu *lu = (struct sbp2_lu *)sdev->host->hostdata[0];
+
+ if (sdev->lun != 0 || sdev->id != lu->ud->id || sdev->channel != 0)
+ return -ENODEV;
+
+ lu->sdev = sdev;
+ sdev->allow_restart = 1;
+
+ /* SBP-2 requires quadlet alignment of the data buffers. */
+ blk_queue_update_dma_alignment(sdev->request_queue, 4 - 1);
+
+ if (lu->workarounds & SBP2_WORKAROUND_INQUIRY_36)
+ sdev->inquiry_len = 36;
+ return 0;
+}
+
+static int sbp2scsi_slave_configure(struct scsi_device *sdev)
+{
+ struct sbp2_lu *lu = (struct sbp2_lu *)sdev->host->hostdata[0];
+
+ sdev->use_10_for_rw = 1;
+
+ if (sbp2_exclusive_login)
+ sdev->manage_start_stop = 1;
+ if (sdev->type == TYPE_ROM)
+ sdev->use_10_for_ms = 1;
+ if (sdev->type == TYPE_DISK &&
+ lu->workarounds & SBP2_WORKAROUND_MODE_SENSE_8)
+ sdev->skip_ms_page_8 = 1;
+ if (lu->workarounds & SBP2_WORKAROUND_FIX_CAPACITY)
+ sdev->fix_capacity = 1;
+ if (lu->workarounds & SBP2_WORKAROUND_POWER_CONDITION)
+ sdev->start_stop_pwr_cond = 1;
+ if (lu->workarounds & SBP2_WORKAROUND_128K_MAX_TRANS)
+ blk_queue_max_sectors(sdev->request_queue, 128 * 1024 / 512);
+
+ blk_queue_max_segment_size(sdev->request_queue, SBP2_MAX_SEG_SIZE);
+ return 0;
+}
+
+static void sbp2scsi_slave_destroy(struct scsi_device *sdev)
+{
+ ((struct sbp2_lu *)sdev->host->hostdata[0])->sdev = NULL;
+ return;
+}
+
+/*
+ * Called by scsi stack when something has really gone wrong.
+ * Usually called when a command has timed-out for some reason.
+ */
+static int sbp2scsi_abort(struct scsi_cmnd *SCpnt)
+{
+ struct sbp2_lu *lu = (struct sbp2_lu *)SCpnt->device->host->hostdata[0];
+ struct sbp2_command_info *cmd;
+ unsigned long flags;
+
+ SBP2_INFO("aborting sbp2 command");
+ scsi_print_command(SCpnt);
+
+ if (sbp2util_node_is_available(lu)) {
+ sbp2_agent_reset(lu, 1);
+
+ /* Return a matching command structure to the free pool. */
+ spin_lock_irqsave(&lu->cmd_orb_lock, flags);
+ cmd = sbp2util_find_command_for_SCpnt(lu, SCpnt);
+ if (cmd) {
+ sbp2util_mark_command_completed(lu, cmd);
+ if (cmd->Current_SCpnt) {
+ cmd->Current_SCpnt->result = DID_ABORT << 16;
+ cmd->Current_done(cmd->Current_SCpnt);
+ }
+ }
+ spin_unlock_irqrestore(&lu->cmd_orb_lock, flags);
+
+ sbp2scsi_complete_all_commands(lu, DID_BUS_BUSY);
+ }
+
+ return SUCCESS;
+}
+
+/*
+ * Called by scsi stack when something has really gone wrong.
+ */
+static int sbp2scsi_reset(struct scsi_cmnd *SCpnt)
+{
+ struct sbp2_lu *lu = (struct sbp2_lu *)SCpnt->device->host->hostdata[0];
+
+ SBP2_INFO("reset requested");
+
+ if (sbp2util_node_is_available(lu)) {
+ SBP2_INFO("generating sbp2 fetch agent reset");
+ sbp2_agent_reset(lu, 1);
+ }
+
+ return SUCCESS;
+}
+
+static ssize_t sbp2_sysfs_ieee1394_id_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct scsi_device *sdev;
+ struct sbp2_lu *lu;
+
+ if (!(sdev = to_scsi_device(dev)))
+ return 0;
+
+ if (!(lu = (struct sbp2_lu *)sdev->host->hostdata[0]))
+ return 0;
+
+ if (sbp2_long_sysfs_ieee1394_id)
+ return sprintf(buf, "%016Lx:%06x:%04x\n",
+ (unsigned long long)lu->ne->guid,
+ lu->ud->directory_id, ORB_SET_LUN(lu->lun));
+ else
+ return sprintf(buf, "%016Lx:%d:%d\n",
+ (unsigned long long)lu->ne->guid,
+ lu->ud->id, ORB_SET_LUN(lu->lun));
+}
+
+MODULE_AUTHOR("Ben Collins <bcollins@debian.org>");
+MODULE_DESCRIPTION("IEEE-1394 SBP-2 protocol driver");
+MODULE_SUPPORTED_DEVICE(SBP2_DEVICE_NAME);
+MODULE_LICENSE("GPL");
+
+static int sbp2_module_init(void)
+{
+ int ret;
+
+ if (sbp2_serialize_io) {
+ sbp2_shost_template.can_queue = 1;
+ sbp2_shost_template.cmd_per_lun = 1;
+ }
+
+ sbp2_shost_template.max_sectors = sbp2_max_sectors;
+
+ hpsb_register_highlevel(&sbp2_highlevel);
+ ret = hpsb_register_protocol(&sbp2_driver);
+ if (ret) {
+ SBP2_ERR("Failed to register protocol");
+ hpsb_unregister_highlevel(&sbp2_highlevel);
+ return ret;
+ }
+ return 0;
+}
+
+static void __exit sbp2_module_exit(void)
+{
+ hpsb_unregister_protocol(&sbp2_driver);
+ hpsb_unregister_highlevel(&sbp2_highlevel);
+}
+
+module_init(sbp2_module_init);
+module_exit(sbp2_module_exit);
diff --git a/drivers/ieee1394/sbp2.h b/drivers/ieee1394/sbp2.h
new file mode 100644
index 0000000..c5036f1
--- /dev/null
+++ b/drivers/ieee1394/sbp2.h
@@ -0,0 +1,340 @@
+/*
+ * sbp2.h - Defines and prototypes for sbp2.c
+ *
+ * Copyright (C) 2000 James Goodwin, Filanet Corporation (www.filanet.com)
+ * jamesg@filanet.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef SBP2_H
+#define SBP2_H
+
+#define SBP2_DEVICE_NAME "sbp2"
+
+/*
+ * SBP-2 specific definitions
+ */
+
+#define ORB_DIRECTION_WRITE_TO_MEDIA 0x0
+#define ORB_DIRECTION_READ_FROM_MEDIA 0x1
+#define ORB_DIRECTION_NO_DATA_TRANSFER 0x2
+
+#define ORB_SET_NULL_PTR(v) (((v) & 0x1) << 31)
+#define ORB_SET_NOTIFY(v) (((v) & 0x1) << 31)
+#define ORB_SET_RQ_FMT(v) (((v) & 0x3) << 29)
+#define ORB_SET_NODE_ID(v) (((v) & 0xffff) << 16)
+#define ORB_SET_STATUS_FIFO_HI(v, id) ((v) >> 32 | ORB_SET_NODE_ID(id))
+#define ORB_SET_STATUS_FIFO_LO(v) ((v) & 0xffffffff)
+#define ORB_SET_DATA_SIZE(v) ((v) & 0xffff)
+#define ORB_SET_PAGE_SIZE(v) (((v) & 0x7) << 16)
+#define ORB_SET_PAGE_TABLE_PRESENT(v) (((v) & 0x1) << 19)
+#define ORB_SET_MAX_PAYLOAD(v) (((v) & 0xf) << 20)
+#define ORB_SET_SPEED(v) (((v) & 0x7) << 24)
+#define ORB_SET_DIRECTION(v) (((v) & 0x1) << 27)
+
+struct sbp2_command_orb {
+ u32 next_ORB_hi;
+ u32 next_ORB_lo;
+ u32 data_descriptor_hi;
+ u32 data_descriptor_lo;
+ u32 misc;
+ u8 cdb[12];
+} __attribute__((packed));
+
+#define SBP2_LOGIN_REQUEST 0x0
+#define SBP2_QUERY_LOGINS_REQUEST 0x1
+#define SBP2_RECONNECT_REQUEST 0x3
+#define SBP2_SET_PASSWORD_REQUEST 0x4
+#define SBP2_LOGOUT_REQUEST 0x7
+#define SBP2_ABORT_TASK_REQUEST 0xb
+#define SBP2_ABORT_TASK_SET 0xc
+#define SBP2_LOGICAL_UNIT_RESET 0xe
+#define SBP2_TARGET_RESET_REQUEST 0xf
+
+#define ORB_SET_LUN(v) ((v) & 0xffff)
+#define ORB_SET_FUNCTION(v) (((v) & 0xf) << 16)
+#define ORB_SET_RECONNECT(v) (((v) & 0xf) << 20)
+#define ORB_SET_EXCLUSIVE(v) ((v) ? 1 << 28 : 0)
+#define ORB_SET_LOGIN_RESP_LENGTH(v) ((v) & 0xffff)
+#define ORB_SET_PASSWD_LENGTH(v) (((v) & 0xffff) << 16)
+
+struct sbp2_login_orb {
+ u32 password_hi;
+ u32 password_lo;
+ u32 login_response_hi;
+ u32 login_response_lo;
+ u32 lun_misc;
+ u32 passwd_resp_lengths;
+ u32 status_fifo_hi;
+ u32 status_fifo_lo;
+} __attribute__((packed));
+
+#define RESPONSE_GET_LOGIN_ID(v) ((v) & 0xffff)
+#define RESPONSE_GET_LENGTH(v) (((v) >> 16) & 0xffff)
+#define RESPONSE_GET_RECONNECT_HOLD(v) ((v) & 0xffff)
+
+struct sbp2_login_response {
+ u32 length_login_ID;
+ u32 command_block_agent_hi;
+ u32 command_block_agent_lo;
+ u32 reconnect_hold;
+} __attribute__((packed));
+
+#define ORB_SET_LOGIN_ID(v) ((v) & 0xffff)
+#define ORB_SET_QUERY_LOGINS_RESP_LENGTH(v) ((v) & 0xffff)
+
+struct sbp2_query_logins_orb {
+ u32 reserved1;
+ u32 reserved2;
+ u32 query_response_hi;
+ u32 query_response_lo;
+ u32 lun_misc;
+ u32 reserved_resp_length;
+ u32 status_fifo_hi;
+ u32 status_fifo_lo;
+} __attribute__((packed));
+
+#define RESPONSE_GET_MAX_LOGINS(v) ((v) & 0xffff)
+#define RESPONSE_GET_ACTIVE_LOGINS(v) ((RESPONSE_GET_LENGTH((v)) - 4) / 12)
+
+struct sbp2_query_logins_response {
+ u32 length_max_logins;
+ u32 misc_IDs;
+ u32 initiator_misc_hi;
+ u32 initiator_misc_lo;
+} __attribute__((packed));
+
+struct sbp2_reconnect_orb {
+ u32 reserved1;
+ u32 reserved2;
+ u32 reserved3;
+ u32 reserved4;
+ u32 login_ID_misc;
+ u32 reserved5;
+ u32 status_fifo_hi;
+ u32 status_fifo_lo;
+} __attribute__((packed));
+
+struct sbp2_logout_orb {
+ u32 reserved1;
+ u32 reserved2;
+ u32 reserved3;
+ u32 reserved4;
+ u32 login_ID_misc;
+ u32 reserved5;
+ u32 status_fifo_hi;
+ u32 status_fifo_lo;
+} __attribute__((packed));
+
+struct sbp2_unrestricted_page_table {
+ __be32 high;
+ __be32 low;
+};
+
+#define RESP_STATUS_REQUEST_COMPLETE 0x0
+#define RESP_STATUS_TRANSPORT_FAILURE 0x1
+#define RESP_STATUS_ILLEGAL_REQUEST 0x2
+#define RESP_STATUS_VENDOR_DEPENDENT 0x3
+
+#define SBP2_STATUS_NO_ADDITIONAL_INFO 0x0
+#define SBP2_STATUS_REQ_TYPE_NOT_SUPPORTED 0x1
+#define SBP2_STATUS_SPEED_NOT_SUPPORTED 0x2
+#define SBP2_STATUS_PAGE_SIZE_NOT_SUPPORTED 0x3
+#define SBP2_STATUS_ACCESS_DENIED 0x4
+#define SBP2_STATUS_LU_NOT_SUPPORTED 0x5
+#define SBP2_STATUS_MAX_PAYLOAD_TOO_SMALL 0x6
+#define SBP2_STATUS_RESOURCES_UNAVAILABLE 0x8
+#define SBP2_STATUS_FUNCTION_REJECTED 0x9
+#define SBP2_STATUS_LOGIN_ID_NOT_RECOGNIZED 0xa
+#define SBP2_STATUS_DUMMY_ORB_COMPLETED 0xb
+#define SBP2_STATUS_REQUEST_ABORTED 0xc
+#define SBP2_STATUS_UNSPECIFIED_ERROR 0xff
+
+#define SFMT_CURRENT_ERROR 0x0
+#define SFMT_DEFERRED_ERROR 0x1
+#define SFMT_VENDOR_DEPENDENT_STATUS 0x3
+
+#define STATUS_GET_SRC(v) (((v) >> 30) & 0x3)
+#define STATUS_GET_RESP(v) (((v) >> 28) & 0x3)
+#define STATUS_GET_LEN(v) (((v) >> 24) & 0x7)
+#define STATUS_GET_SBP_STATUS(v) (((v) >> 16) & 0xff)
+#define STATUS_GET_ORB_OFFSET_HI(v) ((v) & 0x0000ffff)
+#define STATUS_TEST_DEAD(v) ((v) & 0x08000000)
+/* test 'resp' | 'dead' | 'sbp2_status' */
+#define STATUS_TEST_RDS(v) ((v) & 0x38ff0000)
+
+struct sbp2_status_block {
+ u32 ORB_offset_hi_misc;
+ u32 ORB_offset_lo;
+ u8 command_set_dependent[24];
+} __attribute__((packed));
+
+
+/*
+ * SBP2 related configuration ROM definitions
+ */
+
+#define SBP2_UNIT_DIRECTORY_OFFSET_KEY 0xd1
+#define SBP2_CSR_OFFSET_KEY 0x54
+#define SBP2_UNIT_SPEC_ID_KEY 0x12
+#define SBP2_UNIT_SW_VERSION_KEY 0x13
+#define SBP2_COMMAND_SET_SPEC_ID_KEY 0x38
+#define SBP2_COMMAND_SET_KEY 0x39
+#define SBP2_UNIT_CHARACTERISTICS_KEY 0x3a
+#define SBP2_DEVICE_TYPE_AND_LUN_KEY 0x14
+#define SBP2_FIRMWARE_REVISION_KEY 0x3c
+
+#define SBP2_AGENT_STATE_OFFSET 0x00ULL
+#define SBP2_AGENT_RESET_OFFSET 0x04ULL
+#define SBP2_ORB_POINTER_OFFSET 0x08ULL
+#define SBP2_DOORBELL_OFFSET 0x10ULL
+#define SBP2_UNSOLICITED_STATUS_ENABLE_OFFSET 0x14ULL
+#define SBP2_UNSOLICITED_STATUS_VALUE 0xf
+
+#define SBP2_BUSY_TIMEOUT_ADDRESS 0xfffff0000210ULL
+/* biggest possible value for Single Phase Retry count is 0xf */
+#define SBP2_BUSY_TIMEOUT_VALUE 0xf
+
+#define SBP2_AGENT_RESET_DATA 0xf
+
+#define SBP2_UNIT_SPEC_ID_ENTRY 0x0000609e
+#define SBP2_SW_VERSION_ENTRY 0x00010483
+
+/*
+ * The default maximum s/g segment size of a FireWire controller is
+ * usually 0x10000, but SBP-2 only allows 0xffff. Since buffers have to
+ * be quadlet-aligned, we set the length limit to 0xffff & ~3.
+ */
+#define SBP2_MAX_SEG_SIZE 0xfffc
+
+/*
+ * There is no real limitation of the queue depth (i.e. length of the linked
+ * list of command ORBs) at the target. The chosen depth is merely an
+ * implementation detail of the sbp2 driver.
+ */
+#define SBP2_MAX_CMDS 8
+
+#define SBP2_SCSI_STATUS_GOOD 0x0
+#define SBP2_SCSI_STATUS_CHECK_CONDITION 0x2
+#define SBP2_SCSI_STATUS_CONDITION_MET 0x4
+#define SBP2_SCSI_STATUS_BUSY 0x8
+#define SBP2_SCSI_STATUS_RESERVATION_CONFLICT 0x18
+#define SBP2_SCSI_STATUS_COMMAND_TERMINATED 0x22
+#define SBP2_SCSI_STATUS_SELECTION_TIMEOUT 0xff
+
+
+/*
+ * Representations of commands and devices
+ */
+
+/* Per SCSI command */
+struct sbp2_command_info {
+ struct list_head list;
+ struct sbp2_command_orb command_orb;
+ dma_addr_t command_orb_dma;
+ struct scsi_cmnd *Current_SCpnt;
+ void (*Current_done)(struct scsi_cmnd *);
+
+ /* Also need s/g structure for each sbp2 command */
+ struct sbp2_unrestricted_page_table
+ scatter_gather_element[SG_ALL] __attribute__((aligned(8)));
+ dma_addr_t sge_dma;
+};
+
+/* Per FireWire host */
+struct sbp2_fwhost_info {
+ struct hpsb_host *host;
+ struct list_head logical_units;
+};
+
+/* Per logical unit */
+struct sbp2_lu {
+ /* Operation request blocks */
+ struct sbp2_command_orb *last_orb;
+ dma_addr_t last_orb_dma;
+ struct sbp2_login_orb *login_orb;
+ dma_addr_t login_orb_dma;
+ struct sbp2_login_response *login_response;
+ dma_addr_t login_response_dma;
+ struct sbp2_query_logins_orb *query_logins_orb;
+ dma_addr_t query_logins_orb_dma;
+ struct sbp2_query_logins_response *query_logins_response;
+ dma_addr_t query_logins_response_dma;
+ struct sbp2_reconnect_orb *reconnect_orb;
+ dma_addr_t reconnect_orb_dma;
+ struct sbp2_logout_orb *logout_orb;
+ dma_addr_t logout_orb_dma;
+ struct sbp2_status_block status_block;
+
+ /* How to talk to the unit */
+ u64 management_agent_addr;
+ u64 command_block_agent_addr;
+ u32 speed_code;
+ u32 max_payload_size;
+ u16 lun;
+
+ /* Address for the unit to write status blocks to */
+ u64 status_fifo_addr;
+
+ /* Waitqueue flag for logins, reconnects, logouts, query logins */
+ unsigned int access_complete:1;
+
+ /* Pool of command ORBs for this logical unit */
+ spinlock_t cmd_orb_lock;
+ struct list_head cmd_orb_inuse;
+ struct list_head cmd_orb_completed;
+
+ /* Backlink to FireWire host; list of units attached to the host */
+ struct sbp2_fwhost_info *hi;
+ struct list_head lu_list;
+
+ /* IEEE 1394 core's device representations */
+ struct node_entry *ne;
+ struct unit_directory *ud;
+
+ /* SCSI core's device representations */
+ struct scsi_device *sdev;
+ struct Scsi_Host *shost;
+
+ /* Device specific workarounds/brokeness */
+ unsigned workarounds;
+
+ /* Connection state */
+ atomic_t state;
+
+ /* For deferred requests to the fetch agent */
+ struct work_struct protocol_work;
+};
+
+/* For use in sbp2_lu.state */
+enum sbp2lu_state_types {
+ SBP2LU_STATE_RUNNING, /* all normal */
+ SBP2LU_STATE_IN_RESET, /* between bus reset and reconnect */
+ SBP2LU_STATE_IN_SHUTDOWN /* when sbp2_remove was called */
+};
+
+/* For use in sbp2_lu.workarounds and in the corresponding
+ * module load parameter */
+#define SBP2_WORKAROUND_128K_MAX_TRANS 0x1
+#define SBP2_WORKAROUND_INQUIRY_36 0x2
+#define SBP2_WORKAROUND_MODE_SENSE_8 0x4
+#define SBP2_WORKAROUND_FIX_CAPACITY 0x8
+#define SBP2_WORKAROUND_DELAY_INQUIRY 0x10
+#define SBP2_INQUIRY_DELAY 12
+#define SBP2_WORKAROUND_POWER_CONDITION 0x20
+#define SBP2_WORKAROUND_OVERRIDE 0x100
+
+#endif /* SBP2_H */
diff --git a/drivers/ieee1394/video1394.c b/drivers/ieee1394/video1394.c
new file mode 100644
index 0000000..679a918
--- /dev/null
+++ b/drivers/ieee1394/video1394.c
@@ -0,0 +1,1531 @@
+/*
+ * video1394.c - video driver for OHCI 1394 boards
+ * Copyright (C)1999,2000 Sebastien Rougeaux <sebastien.rougeaux@anu.edu.au>
+ * Peter Schlaile <udbz@rz.uni-karlsruhe.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * NOTES:
+ *
+ * ioctl return codes:
+ * EFAULT is only for invalid address for the argp
+ * EINVAL for out of range values
+ * EBUSY when trying to use an already used resource
+ * ESRCH when trying to free/stop a not used resource
+ * EAGAIN for resource allocation failure that could perhaps succeed later
+ * ENOTTY for unsupported ioctl request
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/wait.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/fs.h>
+#include <linux/poll.h>
+#include <linux/delay.h>
+#include <linux/bitops.h>
+#include <linux/types.h>
+#include <linux/vmalloc.h>
+#include <linux/timex.h>
+#include <linux/mm.h>
+#include <linux/compat.h>
+#include <linux/cdev.h>
+
+#include "dma.h"
+#include "highlevel.h"
+#include "hosts.h"
+#include "ieee1394.h"
+#include "ieee1394_core.h"
+#include "ieee1394_hotplug.h"
+#include "ieee1394_types.h"
+#include "nodemgr.h"
+#include "ohci1394.h"
+#include "video1394.h"
+
+#define ISO_CHANNELS 64
+
+struct it_dma_prg {
+ struct dma_cmd begin;
+ quadlet_t data[4];
+ struct dma_cmd end;
+ quadlet_t pad[4]; /* FIXME: quick hack for memory alignment */
+};
+
+struct dma_iso_ctx {
+ struct ti_ohci *ohci;
+ int type; /* OHCI_ISO_TRANSMIT or OHCI_ISO_RECEIVE */
+ struct ohci1394_iso_tasklet iso_tasklet;
+ int channel;
+ int ctx;
+ int last_buffer;
+ int * next_buffer; /* For ISO Transmit of video packets
+ to write the correct SYT field
+ into the next block */
+ unsigned int num_desc;
+ unsigned int buf_size;
+ unsigned int frame_size;
+ unsigned int packet_size;
+ unsigned int left_size;
+ unsigned int nb_cmd;
+
+ struct dma_region dma;
+
+ struct dma_prog_region *prg_reg;
+
+ struct dma_cmd **ir_prg;
+ struct it_dma_prg **it_prg;
+
+ unsigned int *buffer_status;
+ unsigned int *buffer_prg_assignment;
+ struct timeval *buffer_time; /* time when the buffer was received */
+ unsigned int *last_used_cmd; /* For ISO Transmit with
+ variable sized packets only ! */
+ int ctrlClear;
+ int ctrlSet;
+ int cmdPtr;
+ int ctxMatch;
+ wait_queue_head_t waitq;
+ spinlock_t lock;
+ unsigned int syt_offset;
+ int flags;
+
+ struct list_head link;
+};
+
+
+struct file_ctx {
+ struct ti_ohci *ohci;
+ struct list_head context_list;
+ struct dma_iso_ctx *current_ctx;
+};
+
+#ifdef CONFIG_IEEE1394_VERBOSEDEBUG
+#define VIDEO1394_DEBUG
+#endif
+
+#ifdef DBGMSG
+#undef DBGMSG
+#endif
+
+#ifdef VIDEO1394_DEBUG
+#define DBGMSG(card, fmt, args...) \
+printk(KERN_INFO "video1394_%d: " fmt "\n" , card , ## args)
+#else
+#define DBGMSG(card, fmt, args...) do {} while (0)
+#endif
+
+/* print general (card independent) information */
+#define PRINT_G(level, fmt, args...) \
+printk(level "video1394: " fmt "\n" , ## args)
+
+/* print card specific information */
+#define PRINT(level, card, fmt, args...) \
+printk(level "video1394_%d: " fmt "\n" , card , ## args)
+
+static void wakeup_dma_ir_ctx(unsigned long l);
+static void wakeup_dma_it_ctx(unsigned long l);
+
+static struct hpsb_highlevel video1394_highlevel;
+
+static int free_dma_iso_ctx(struct dma_iso_ctx *d)
+{
+ int i;
+
+ DBGMSG(d->ohci->host->id, "Freeing dma_iso_ctx %d", d->ctx);
+
+ ohci1394_stop_context(d->ohci, d->ctrlClear, NULL);
+ if (d->iso_tasklet.link.next != NULL)
+ ohci1394_unregister_iso_tasklet(d->ohci, &d->iso_tasklet);
+
+ dma_region_free(&d->dma);
+
+ if (d->prg_reg) {
+ for (i = 0; i < d->num_desc; i++)
+ dma_prog_region_free(&d->prg_reg[i]);
+ kfree(d->prg_reg);
+ }
+
+ kfree(d->ir_prg);
+ kfree(d->it_prg);
+ kfree(d->buffer_status);
+ kfree(d->buffer_prg_assignment);
+ kfree(d->buffer_time);
+ kfree(d->last_used_cmd);
+ kfree(d->next_buffer);
+ list_del(&d->link);
+ kfree(d);
+
+ return 0;
+}
+
+static struct dma_iso_ctx *
+alloc_dma_iso_ctx(struct ti_ohci *ohci, int type, int num_desc,
+ int buf_size, int channel, unsigned int packet_size)
+{
+ struct dma_iso_ctx *d;
+ int i;
+
+ d = kzalloc(sizeof(*d), GFP_KERNEL);
+ if (!d) {
+ PRINT(KERN_ERR, ohci->host->id, "Failed to allocate dma_iso_ctx");
+ return NULL;
+ }
+
+ d->ohci = ohci;
+ d->type = type;
+ d->channel = channel;
+ d->num_desc = num_desc;
+ d->frame_size = buf_size;
+ d->buf_size = PAGE_ALIGN(buf_size);
+ d->last_buffer = -1;
+ INIT_LIST_HEAD(&d->link);
+ init_waitqueue_head(&d->waitq);
+
+ /* Init the regions for easy cleanup */
+ dma_region_init(&d->dma);
+
+ if (dma_region_alloc(&d->dma, (d->num_desc - 1) * d->buf_size, ohci->dev,
+ PCI_DMA_BIDIRECTIONAL)) {
+ PRINT(KERN_ERR, ohci->host->id, "Failed to allocate dma buffer");
+ free_dma_iso_ctx(d);
+ return NULL;
+ }
+
+ if (type == OHCI_ISO_RECEIVE)
+ ohci1394_init_iso_tasklet(&d->iso_tasklet, type,
+ wakeup_dma_ir_ctx,
+ (unsigned long) d);
+ else
+ ohci1394_init_iso_tasklet(&d->iso_tasklet, type,
+ wakeup_dma_it_ctx,
+ (unsigned long) d);
+
+ if (ohci1394_register_iso_tasklet(ohci, &d->iso_tasklet) < 0) {
+ PRINT(KERN_ERR, ohci->host->id, "no free iso %s contexts",
+ type == OHCI_ISO_RECEIVE ? "receive" : "transmit");
+ free_dma_iso_ctx(d);
+ return NULL;
+ }
+ d->ctx = d->iso_tasklet.context;
+
+ d->prg_reg = kmalloc(d->num_desc * sizeof(*d->prg_reg), GFP_KERNEL);
+ if (!d->prg_reg) {
+ PRINT(KERN_ERR, ohci->host->id, "Failed to allocate ir prg regs");
+ free_dma_iso_ctx(d);
+ return NULL;
+ }
+ /* Makes for easier cleanup */
+ for (i = 0; i < d->num_desc; i++)
+ dma_prog_region_init(&d->prg_reg[i]);
+
+ if (type == OHCI_ISO_RECEIVE) {
+ d->ctrlSet = OHCI1394_IsoRcvContextControlSet+32*d->ctx;
+ d->ctrlClear = OHCI1394_IsoRcvContextControlClear+32*d->ctx;
+ d->cmdPtr = OHCI1394_IsoRcvCommandPtr+32*d->ctx;
+ d->ctxMatch = OHCI1394_IsoRcvContextMatch+32*d->ctx;
+
+ d->ir_prg = kzalloc(d->num_desc * sizeof(*d->ir_prg),
+ GFP_KERNEL);
+
+ if (!d->ir_prg) {
+ PRINT(KERN_ERR, ohci->host->id, "Failed to allocate dma ir prg");
+ free_dma_iso_ctx(d);
+ return NULL;
+ }
+
+ d->nb_cmd = d->buf_size / PAGE_SIZE + 1;
+ d->left_size = (d->frame_size % PAGE_SIZE) ?
+ d->frame_size % PAGE_SIZE : PAGE_SIZE;
+
+ for (i = 0;i < d->num_desc; i++) {
+ if (dma_prog_region_alloc(&d->prg_reg[i], d->nb_cmd *
+ sizeof(struct dma_cmd), ohci->dev)) {
+ PRINT(KERN_ERR, ohci->host->id, "Failed to allocate dma ir prg");
+ free_dma_iso_ctx(d);
+ return NULL;
+ }
+ d->ir_prg[i] = (struct dma_cmd *)d->prg_reg[i].kvirt;
+ }
+
+ } else { /* OHCI_ISO_TRANSMIT */
+ d->ctrlSet = OHCI1394_IsoXmitContextControlSet+16*d->ctx;
+ d->ctrlClear = OHCI1394_IsoXmitContextControlClear+16*d->ctx;
+ d->cmdPtr = OHCI1394_IsoXmitCommandPtr+16*d->ctx;
+
+ d->it_prg = kzalloc(d->num_desc * sizeof(*d->it_prg),
+ GFP_KERNEL);
+
+ if (!d->it_prg) {
+ PRINT(KERN_ERR, ohci->host->id,
+ "Failed to allocate dma it prg");
+ free_dma_iso_ctx(d);
+ return NULL;
+ }
+
+ d->packet_size = packet_size;
+
+ if (PAGE_SIZE % packet_size || packet_size>4096) {
+ PRINT(KERN_ERR, ohci->host->id,
+ "Packet size %d (page_size: %ld) "
+ "not yet supported\n",
+ packet_size, PAGE_SIZE);
+ free_dma_iso_ctx(d);
+ return NULL;
+ }
+
+ d->nb_cmd = d->frame_size / d->packet_size;
+ if (d->frame_size % d->packet_size) {
+ d->nb_cmd++;
+ d->left_size = d->frame_size % d->packet_size;
+ } else
+ d->left_size = d->packet_size;
+
+ for (i = 0; i < d->num_desc; i++) {
+ if (dma_prog_region_alloc(&d->prg_reg[i], d->nb_cmd *
+ sizeof(struct it_dma_prg), ohci->dev)) {
+ PRINT(KERN_ERR, ohci->host->id, "Failed to allocate dma it prg");
+ free_dma_iso_ctx(d);
+ return NULL;
+ }
+ d->it_prg[i] = (struct it_dma_prg *)d->prg_reg[i].kvirt;
+ }
+ }
+
+ d->buffer_status =
+ kzalloc(d->num_desc * sizeof(*d->buffer_status), GFP_KERNEL);
+ d->buffer_prg_assignment =
+ kzalloc(d->num_desc * sizeof(*d->buffer_prg_assignment), GFP_KERNEL);
+ d->buffer_time =
+ kzalloc(d->num_desc * sizeof(*d->buffer_time), GFP_KERNEL);
+ d->last_used_cmd =
+ kzalloc(d->num_desc * sizeof(*d->last_used_cmd), GFP_KERNEL);
+ d->next_buffer =
+ kzalloc(d->num_desc * sizeof(*d->next_buffer), GFP_KERNEL);
+
+ if (!d->buffer_status || !d->buffer_prg_assignment || !d->buffer_time ||
+ !d->last_used_cmd || !d->next_buffer) {
+ PRINT(KERN_ERR, ohci->host->id,
+ "Failed to allocate dma_iso_ctx member");
+ free_dma_iso_ctx(d);
+ return NULL;
+ }
+
+ spin_lock_init(&d->lock);
+
+ DBGMSG(ohci->host->id, "Iso %s DMA: %d buffers "
+ "of size %d allocated for a frame size %d, each with %d prgs",
+ (type == OHCI_ISO_RECEIVE) ? "receive" : "transmit",
+ d->num_desc - 1, d->buf_size, d->frame_size, d->nb_cmd);
+
+ return d;
+}
+
+static void reset_ir_status(struct dma_iso_ctx *d, int n)
+{
+ int i;
+ d->ir_prg[n][0].status = cpu_to_le32(4);
+ d->ir_prg[n][1].status = cpu_to_le32(PAGE_SIZE-4);
+ for (i = 2; i < d->nb_cmd - 1; i++)
+ d->ir_prg[n][i].status = cpu_to_le32(PAGE_SIZE);
+ d->ir_prg[n][i].status = cpu_to_le32(d->left_size);
+}
+
+static void reprogram_dma_ir_prg(struct dma_iso_ctx *d, int n, int buffer, int flags)
+{
+ struct dma_cmd *ir_prg = d->ir_prg[n];
+ unsigned long buf = (unsigned long)d->dma.kvirt + buffer * d->buf_size;
+ int i;
+
+ d->buffer_prg_assignment[n] = buffer;
+
+ ir_prg[0].address = cpu_to_le32(dma_region_offset_to_bus(&d->dma, buf -
+ (unsigned long)d->dma.kvirt));
+ ir_prg[1].address = cpu_to_le32(dma_region_offset_to_bus(&d->dma,
+ (buf + 4) - (unsigned long)d->dma.kvirt));
+
+ for (i=2;i<d->nb_cmd-1;i++) {
+ ir_prg[i].address = cpu_to_le32(dma_region_offset_to_bus(&d->dma,
+ (buf+(i-1)*PAGE_SIZE) -
+ (unsigned long)d->dma.kvirt));
+ }
+
+ ir_prg[i].control = cpu_to_le32(DMA_CTL_INPUT_MORE | DMA_CTL_UPDATE |
+ DMA_CTL_IRQ | DMA_CTL_BRANCH | d->left_size);
+ ir_prg[i].address = cpu_to_le32(dma_region_offset_to_bus(&d->dma,
+ (buf+(i-1)*PAGE_SIZE) - (unsigned long)d->dma.kvirt));
+}
+
+static void initialize_dma_ir_prg(struct dma_iso_ctx *d, int n, int flags)
+{
+ struct dma_cmd *ir_prg = d->ir_prg[n];
+ struct dma_prog_region *ir_reg = &d->prg_reg[n];
+ unsigned long buf = (unsigned long)d->dma.kvirt;
+ int i;
+
+ /* the first descriptor will read only 4 bytes */
+ ir_prg[0].control = cpu_to_le32(DMA_CTL_INPUT_MORE | DMA_CTL_UPDATE |
+ DMA_CTL_BRANCH | 4);
+
+ /* set the sync flag */
+ if (flags & VIDEO1394_SYNC_FRAMES)
+ ir_prg[0].control |= cpu_to_le32(DMA_CTL_WAIT);
+
+ ir_prg[0].address = cpu_to_le32(dma_region_offset_to_bus(&d->dma, buf -
+ (unsigned long)d->dma.kvirt));
+ ir_prg[0].branchAddress = cpu_to_le32((dma_prog_region_offset_to_bus(ir_reg,
+ 1 * sizeof(struct dma_cmd)) & 0xfffffff0) | 0x1);
+
+ /* If there is *not* only one DMA page per frame (hence, d->nb_cmd==2) */
+ if (d->nb_cmd > 2) {
+ /* The second descriptor will read PAGE_SIZE-4 bytes */
+ ir_prg[1].control = cpu_to_le32(DMA_CTL_INPUT_MORE | DMA_CTL_UPDATE |
+ DMA_CTL_BRANCH | (PAGE_SIZE-4));
+ ir_prg[1].address = cpu_to_le32(dma_region_offset_to_bus(&d->dma, (buf + 4) -
+ (unsigned long)d->dma.kvirt));
+ ir_prg[1].branchAddress = cpu_to_le32((dma_prog_region_offset_to_bus(ir_reg,
+ 2 * sizeof(struct dma_cmd)) & 0xfffffff0) | 0x1);
+
+ for (i = 2; i < d->nb_cmd - 1; i++) {
+ ir_prg[i].control = cpu_to_le32(DMA_CTL_INPUT_MORE | DMA_CTL_UPDATE |
+ DMA_CTL_BRANCH | PAGE_SIZE);
+ ir_prg[i].address = cpu_to_le32(dma_region_offset_to_bus(&d->dma,
+ (buf+(i-1)*PAGE_SIZE) -
+ (unsigned long)d->dma.kvirt));
+
+ ir_prg[i].branchAddress =
+ cpu_to_le32((dma_prog_region_offset_to_bus(ir_reg,
+ (i + 1) * sizeof(struct dma_cmd)) & 0xfffffff0) | 0x1);
+ }
+
+ /* The last descriptor will generate an interrupt */
+ ir_prg[i].control = cpu_to_le32(DMA_CTL_INPUT_MORE | DMA_CTL_UPDATE |
+ DMA_CTL_IRQ | DMA_CTL_BRANCH | d->left_size);
+ ir_prg[i].address = cpu_to_le32(dma_region_offset_to_bus(&d->dma,
+ (buf+(i-1)*PAGE_SIZE) -
+ (unsigned long)d->dma.kvirt));
+ } else {
+ /* Only one DMA page is used. Read d->left_size immediately and */
+ /* generate an interrupt as this is also the last page. */
+ ir_prg[1].control = cpu_to_le32(DMA_CTL_INPUT_MORE | DMA_CTL_UPDATE |
+ DMA_CTL_IRQ | DMA_CTL_BRANCH | (d->left_size-4));
+ ir_prg[1].address = cpu_to_le32(dma_region_offset_to_bus(&d->dma,
+ (buf + 4) - (unsigned long)d->dma.kvirt));
+ }
+}
+
+static void initialize_dma_ir_ctx(struct dma_iso_ctx *d, int tag, int flags)
+{
+ struct ti_ohci *ohci = (struct ti_ohci *)d->ohci;
+ int i;
+
+ d->flags = flags;
+
+ ohci1394_stop_context(ohci, d->ctrlClear, NULL);
+
+ for (i=0;i<d->num_desc;i++) {
+ initialize_dma_ir_prg(d, i, flags);
+ reset_ir_status(d, i);
+ }
+
+ /* reset the ctrl register */
+ reg_write(ohci, d->ctrlClear, 0xf0000000);
+
+ /* Set bufferFill */
+ reg_write(ohci, d->ctrlSet, 0x80000000);
+
+ /* Set isoch header */
+ if (flags & VIDEO1394_INCLUDE_ISO_HEADERS)
+ reg_write(ohci, d->ctrlSet, 0x40000000);
+
+ /* Set the context match register to match on all tags,
+ sync for sync tag, and listen to d->channel */
+ reg_write(ohci, d->ctxMatch, 0xf0000000|((tag&0xf)<<8)|d->channel);
+
+ /* Set up isoRecvIntMask to generate interrupts */
+ reg_write(ohci, OHCI1394_IsoRecvIntMaskSet, 1<<d->ctx);
+}
+
+/* find which context is listening to this channel */
+static struct dma_iso_ctx *
+find_ctx(struct list_head *list, int type, int channel)
+{
+ struct dma_iso_ctx *ctx;
+
+ list_for_each_entry(ctx, list, link) {
+ if (ctx->type == type && ctx->channel == channel)
+ return ctx;
+ }
+
+ return NULL;
+}
+
+static void wakeup_dma_ir_ctx(unsigned long l)
+{
+ struct dma_iso_ctx *d = (struct dma_iso_ctx *) l;
+ int i;
+
+ spin_lock(&d->lock);
+
+ for (i = 0; i < d->num_desc; i++) {
+ if (d->ir_prg[i][d->nb_cmd-1].status & cpu_to_le32(0xFFFF0000)) {
+ reset_ir_status(d, i);
+ d->buffer_status[d->buffer_prg_assignment[i]] = VIDEO1394_BUFFER_READY;
+ do_gettimeofday(&d->buffer_time[d->buffer_prg_assignment[i]]);
+ dma_region_sync_for_cpu(&d->dma,
+ d->buffer_prg_assignment[i] * d->buf_size,
+ d->buf_size);
+ }
+ }
+
+ spin_unlock(&d->lock);
+
+ if (waitqueue_active(&d->waitq))
+ wake_up_interruptible(&d->waitq);
+}
+
+static inline void put_timestamp(struct ti_ohci *ohci, struct dma_iso_ctx * d,
+ int n)
+{
+ unsigned char* buf = d->dma.kvirt + n * d->buf_size;
+ u32 cycleTimer;
+ u32 timeStamp;
+
+ if (n == -1) {
+ return;
+ }
+
+ cycleTimer = reg_read(ohci, OHCI1394_IsochronousCycleTimer);
+
+ timeStamp = ((cycleTimer & 0x0fff) + d->syt_offset); /* 11059 = 450 us */
+ timeStamp = (timeStamp % 3072 + ((timeStamp / 3072) << 12)
+ + (cycleTimer & 0xf000)) & 0xffff;
+
+ buf[6] = timeStamp >> 8;
+ buf[7] = timeStamp & 0xff;
+
+ /* if first packet is empty packet, then put timestamp into the next full one too */
+ if ( (le32_to_cpu(d->it_prg[n][0].data[1]) >>16) == 0x008) {
+ buf += d->packet_size;
+ buf[6] = timeStamp >> 8;
+ buf[7] = timeStamp & 0xff;
+ }
+
+ /* do the next buffer frame too in case of irq latency */
+ n = d->next_buffer[n];
+ if (n == -1) {
+ return;
+ }
+ buf = d->dma.kvirt + n * d->buf_size;
+
+ timeStamp += (d->last_used_cmd[n] << 12) & 0xffff;
+
+ buf[6] = timeStamp >> 8;
+ buf[7] = timeStamp & 0xff;
+
+ /* if first packet is empty packet, then put timestamp into the next full one too */
+ if ( (le32_to_cpu(d->it_prg[n][0].data[1]) >>16) == 0x008) {
+ buf += d->packet_size;
+ buf[6] = timeStamp >> 8;
+ buf[7] = timeStamp & 0xff;
+ }
+
+#if 0
+ printk("curr: %d, next: %d, cycleTimer: %08x timeStamp: %08x\n",
+ curr, n, cycleTimer, timeStamp);
+#endif
+}
+
+static void wakeup_dma_it_ctx(unsigned long l)
+{
+ struct dma_iso_ctx *d = (struct dma_iso_ctx *) l;
+ struct ti_ohci *ohci = d->ohci;
+ int i;
+
+ spin_lock(&d->lock);
+
+ for (i = 0; i < d->num_desc; i++) {
+ if (d->it_prg[i][d->last_used_cmd[i]].end.status &
+ cpu_to_le32(0xFFFF0000)) {
+ int next = d->next_buffer[i];
+ put_timestamp(ohci, d, next);
+ d->it_prg[i][d->last_used_cmd[i]].end.status = 0;
+ d->buffer_status[d->buffer_prg_assignment[i]] = VIDEO1394_BUFFER_READY;
+ }
+ }
+
+ spin_unlock(&d->lock);
+
+ if (waitqueue_active(&d->waitq))
+ wake_up_interruptible(&d->waitq);
+}
+
+static void reprogram_dma_it_prg(struct dma_iso_ctx *d, int n, int buffer)
+{
+ struct it_dma_prg *it_prg = d->it_prg[n];
+ unsigned long buf = (unsigned long)d->dma.kvirt + buffer * d->buf_size;
+ int i;
+
+ d->buffer_prg_assignment[n] = buffer;
+ for (i=0;i<d->nb_cmd;i++) {
+ it_prg[i].end.address =
+ cpu_to_le32(dma_region_offset_to_bus(&d->dma,
+ (buf+i*d->packet_size) - (unsigned long)d->dma.kvirt));
+ }
+}
+
+static void initialize_dma_it_prg(struct dma_iso_ctx *d, int n, int sync_tag)
+{
+ struct it_dma_prg *it_prg = d->it_prg[n];
+ struct dma_prog_region *it_reg = &d->prg_reg[n];
+ unsigned long buf = (unsigned long)d->dma.kvirt;
+ int i;
+ d->last_used_cmd[n] = d->nb_cmd - 1;
+ for (i=0;i<d->nb_cmd;i++) {
+
+ it_prg[i].begin.control = cpu_to_le32(DMA_CTL_OUTPUT_MORE |
+ DMA_CTL_IMMEDIATE | 8) ;
+ it_prg[i].begin.address = 0;
+
+ it_prg[i].begin.status = 0;
+
+ it_prg[i].data[0] = cpu_to_le32(
+ (IEEE1394_SPEED_100 << 16)
+ | (/* tag */ 1 << 14)
+ | (d->channel << 8)
+ | (TCODE_ISO_DATA << 4));
+ if (i==0) it_prg[i].data[0] |= cpu_to_le32(sync_tag);
+ it_prg[i].data[1] = cpu_to_le32(d->packet_size << 16);
+ it_prg[i].data[2] = 0;
+ it_prg[i].data[3] = 0;
+
+ it_prg[i].end.control = cpu_to_le32(DMA_CTL_OUTPUT_LAST |
+ DMA_CTL_BRANCH);
+ it_prg[i].end.address =
+ cpu_to_le32(dma_region_offset_to_bus(&d->dma, (buf+i*d->packet_size) -
+ (unsigned long)d->dma.kvirt));
+
+ if (i<d->nb_cmd-1) {
+ it_prg[i].end.control |= cpu_to_le32(d->packet_size);
+ it_prg[i].begin.branchAddress =
+ cpu_to_le32((dma_prog_region_offset_to_bus(it_reg, (i + 1) *
+ sizeof(struct it_dma_prg)) & 0xfffffff0) | 0x3);
+ it_prg[i].end.branchAddress =
+ cpu_to_le32((dma_prog_region_offset_to_bus(it_reg, (i + 1) *
+ sizeof(struct it_dma_prg)) & 0xfffffff0) | 0x3);
+ } else {
+ /* the last prg generates an interrupt */
+ it_prg[i].end.control |= cpu_to_le32(DMA_CTL_UPDATE |
+ DMA_CTL_IRQ | d->left_size);
+ /* the last prg doesn't branch */
+ it_prg[i].begin.branchAddress = 0;
+ it_prg[i].end.branchAddress = 0;
+ }
+ it_prg[i].end.status = 0;
+ }
+}
+
+static void initialize_dma_it_prg_var_packet_queue(
+ struct dma_iso_ctx *d, int n, unsigned int * packet_sizes,
+ struct ti_ohci *ohci)
+{
+ struct it_dma_prg *it_prg = d->it_prg[n];
+ struct dma_prog_region *it_reg = &d->prg_reg[n];
+ int i;
+
+#if 0
+ if (n != -1) {
+ put_timestamp(ohci, d, n);
+ }
+#endif
+ d->last_used_cmd[n] = d->nb_cmd - 1;
+
+ for (i = 0; i < d->nb_cmd; i++) {
+ unsigned int size;
+ if (packet_sizes[i] > d->packet_size) {
+ size = d->packet_size;
+ } else {
+ size = packet_sizes[i];
+ }
+ it_prg[i].data[1] = cpu_to_le32(size << 16);
+ it_prg[i].end.control = cpu_to_le32(DMA_CTL_OUTPUT_LAST | DMA_CTL_BRANCH);
+
+ if (i < d->nb_cmd-1 && packet_sizes[i+1] != 0) {
+ it_prg[i].end.control |= cpu_to_le32(size);
+ it_prg[i].begin.branchAddress =
+ cpu_to_le32((dma_prog_region_offset_to_bus(it_reg, (i + 1) *
+ sizeof(struct it_dma_prg)) & 0xfffffff0) | 0x3);
+ it_prg[i].end.branchAddress =
+ cpu_to_le32((dma_prog_region_offset_to_bus(it_reg, (i + 1) *
+ sizeof(struct it_dma_prg)) & 0xfffffff0) | 0x3);
+ } else {
+ /* the last prg generates an interrupt */
+ it_prg[i].end.control |= cpu_to_le32(DMA_CTL_UPDATE |
+ DMA_CTL_IRQ | size);
+ /* the last prg doesn't branch */
+ it_prg[i].begin.branchAddress = 0;
+ it_prg[i].end.branchAddress = 0;
+ d->last_used_cmd[n] = i;
+ break;
+ }
+ }
+}
+
+static void initialize_dma_it_ctx(struct dma_iso_ctx *d, int sync_tag,
+ unsigned int syt_offset, int flags)
+{
+ struct ti_ohci *ohci = (struct ti_ohci *)d->ohci;
+ int i;
+
+ d->flags = flags;
+ d->syt_offset = (syt_offset == 0 ? 11000 : syt_offset);
+
+ ohci1394_stop_context(ohci, d->ctrlClear, NULL);
+
+ for (i=0;i<d->num_desc;i++)
+ initialize_dma_it_prg(d, i, sync_tag);
+
+ /* Set up isoRecvIntMask to generate interrupts */
+ reg_write(ohci, OHCI1394_IsoXmitIntMaskSet, 1<<d->ctx);
+}
+
+static inline unsigned video1394_buffer_state(struct dma_iso_ctx *d,
+ unsigned int buffer)
+{
+ unsigned long flags;
+ unsigned int ret;
+ spin_lock_irqsave(&d->lock, flags);
+ ret = d->buffer_status[buffer];
+ spin_unlock_irqrestore(&d->lock, flags);
+ return ret;
+}
+
+static long video1394_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct file_ctx *ctx = (struct file_ctx *)file->private_data;
+ struct ti_ohci *ohci = ctx->ohci;
+ unsigned long flags;
+ void __user *argp = (void __user *)arg;
+
+ switch(cmd)
+ {
+ case VIDEO1394_IOC_LISTEN_CHANNEL:
+ case VIDEO1394_IOC_TALK_CHANNEL:
+ {
+ struct video1394_mmap v;
+ u64 mask;
+ struct dma_iso_ctx *d;
+ int i;
+
+ if (copy_from_user(&v, argp, sizeof(v)))
+ return -EFAULT;
+
+ /* if channel < 0, find lowest available one */
+ if (v.channel < 0) {
+ mask = (u64)0x1;
+ for (i=0; ; i++) {
+ if (i == ISO_CHANNELS) {
+ PRINT(KERN_ERR, ohci->host->id,
+ "No free channel found");
+ return -EAGAIN;
+ }
+ if (!(ohci->ISO_channel_usage & mask)) {
+ v.channel = i;
+ PRINT(KERN_INFO, ohci->host->id, "Found free channel %d", i);
+ break;
+ }
+ mask = mask << 1;
+ }
+ } else if (v.channel >= ISO_CHANNELS) {
+ PRINT(KERN_ERR, ohci->host->id,
+ "Iso channel %d out of bounds", v.channel);
+ return -EINVAL;
+ } else {
+ mask = (u64)0x1<<v.channel;
+ }
+ DBGMSG(ohci->host->id, "mask: %08X%08X usage: %08X%08X\n",
+ (u32)(mask>>32),(u32)(mask&0xffffffff),
+ (u32)(ohci->ISO_channel_usage>>32),
+ (u32)(ohci->ISO_channel_usage&0xffffffff));
+ if (ohci->ISO_channel_usage & mask) {
+ PRINT(KERN_ERR, ohci->host->id,
+ "Channel %d is already taken", v.channel);
+ return -EBUSY;
+ }
+
+ if (v.buf_size == 0 || v.buf_size > VIDEO1394_MAX_SIZE) {
+ PRINT(KERN_ERR, ohci->host->id,
+ "Invalid %d length buffer requested",v.buf_size);
+ return -EINVAL;
+ }
+
+ if (v.nb_buffers == 0 || v.nb_buffers > VIDEO1394_MAX_SIZE) {
+ PRINT(KERN_ERR, ohci->host->id,
+ "Invalid %d buffers requested",v.nb_buffers);
+ return -EINVAL;
+ }
+
+ if (v.nb_buffers * v.buf_size > VIDEO1394_MAX_SIZE) {
+ PRINT(KERN_ERR, ohci->host->id,
+ "%d buffers of size %d bytes is too big",
+ v.nb_buffers, v.buf_size);
+ return -EINVAL;
+ }
+
+ if (cmd == VIDEO1394_IOC_LISTEN_CHANNEL) {
+ d = alloc_dma_iso_ctx(ohci, OHCI_ISO_RECEIVE,
+ v.nb_buffers + 1, v.buf_size,
+ v.channel, 0);
+
+ if (d == NULL) {
+ PRINT(KERN_ERR, ohci->host->id,
+ "Couldn't allocate ir context");
+ return -EAGAIN;
+ }
+ initialize_dma_ir_ctx(d, v.sync_tag, v.flags);
+
+ ctx->current_ctx = d;
+
+ v.buf_size = d->buf_size;
+ list_add_tail(&d->link, &ctx->context_list);
+
+ DBGMSG(ohci->host->id,
+ "iso context %d listen on channel %d",
+ d->ctx, v.channel);
+ }
+ else {
+ d = alloc_dma_iso_ctx(ohci, OHCI_ISO_TRANSMIT,
+ v.nb_buffers + 1, v.buf_size,
+ v.channel, v.packet_size);
+
+ if (d == NULL) {
+ PRINT(KERN_ERR, ohci->host->id,
+ "Couldn't allocate it context");
+ return -EAGAIN;
+ }
+ initialize_dma_it_ctx(d, v.sync_tag,
+ v.syt_offset, v.flags);
+
+ ctx->current_ctx = d;
+
+ v.buf_size = d->buf_size;
+
+ list_add_tail(&d->link, &ctx->context_list);
+
+ DBGMSG(ohci->host->id,
+ "Iso context %d talk on channel %d", d->ctx,
+ v.channel);
+ }
+
+ if (copy_to_user(argp, &v, sizeof(v))) {
+ /* FIXME : free allocated dma resources */
+ return -EFAULT;
+ }
+
+ ohci->ISO_channel_usage |= mask;
+
+ return 0;
+ }
+ case VIDEO1394_IOC_UNLISTEN_CHANNEL:
+ case VIDEO1394_IOC_UNTALK_CHANNEL:
+ {
+ int channel;
+ u64 mask;
+ struct dma_iso_ctx *d;
+
+ if (copy_from_user(&channel, argp, sizeof(int)))
+ return -EFAULT;
+
+ if (channel < 0 || channel >= ISO_CHANNELS) {
+ PRINT(KERN_ERR, ohci->host->id,
+ "Iso channel %d out of bound", channel);
+ return -EINVAL;
+ }
+ mask = (u64)0x1<<channel;
+ if (!(ohci->ISO_channel_usage & mask)) {
+ PRINT(KERN_ERR, ohci->host->id,
+ "Channel %d is not being used", channel);
+ return -ESRCH;
+ }
+
+ /* Mark this channel as unused */
+ ohci->ISO_channel_usage &= ~mask;
+
+ if (cmd == VIDEO1394_IOC_UNLISTEN_CHANNEL)
+ d = find_ctx(&ctx->context_list, OHCI_ISO_RECEIVE, channel);
+ else
+ d = find_ctx(&ctx->context_list, OHCI_ISO_TRANSMIT, channel);
+
+ if (d == NULL) return -ESRCH;
+ DBGMSG(ohci->host->id, "Iso context %d "
+ "stop talking on channel %d", d->ctx, channel);
+ free_dma_iso_ctx(d);
+
+ return 0;
+ }
+ case VIDEO1394_IOC_LISTEN_QUEUE_BUFFER:
+ {
+ struct video1394_wait v;
+ struct dma_iso_ctx *d;
+ int next_prg;
+
+ if (unlikely(copy_from_user(&v, argp, sizeof(v))))
+ return -EFAULT;
+
+ d = find_ctx(&ctx->context_list, OHCI_ISO_RECEIVE, v.channel);
+ if (unlikely(d == NULL))
+ return -EFAULT;
+
+ if (unlikely(v.buffer >= d->num_desc - 1)) {
+ PRINT(KERN_ERR, ohci->host->id,
+ "Buffer %d out of range",v.buffer);
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&d->lock,flags);
+
+ if (unlikely(d->buffer_status[v.buffer]==VIDEO1394_BUFFER_QUEUED)) {
+ PRINT(KERN_ERR, ohci->host->id,
+ "Buffer %d is already used",v.buffer);
+ spin_unlock_irqrestore(&d->lock,flags);
+ return -EBUSY;
+ }
+
+ d->buffer_status[v.buffer]=VIDEO1394_BUFFER_QUEUED;
+
+ next_prg = (d->last_buffer + 1) % d->num_desc;
+ if (d->last_buffer>=0)
+ d->ir_prg[d->last_buffer][d->nb_cmd-1].branchAddress =
+ cpu_to_le32((dma_prog_region_offset_to_bus(&d->prg_reg[next_prg], 0)
+ & 0xfffffff0) | 0x1);
+
+ d->last_buffer = next_prg;
+ reprogram_dma_ir_prg(d, d->last_buffer, v.buffer, d->flags);
+
+ d->ir_prg[d->last_buffer][d->nb_cmd-1].branchAddress = 0;
+
+ spin_unlock_irqrestore(&d->lock,flags);
+
+ if (!(reg_read(ohci, d->ctrlSet) & 0x8000))
+ {
+ DBGMSG(ohci->host->id, "Starting iso DMA ctx=%d",d->ctx);
+
+ /* Tell the controller where the first program is */
+ reg_write(ohci, d->cmdPtr,
+ dma_prog_region_offset_to_bus(&d->prg_reg[d->last_buffer], 0) | 0x1);
+
+ /* Run IR context */
+ reg_write(ohci, d->ctrlSet, 0x8000);
+ }
+ else {
+ /* Wake up dma context if necessary */
+ if (!(reg_read(ohci, d->ctrlSet) & 0x400)) {
+ DBGMSG(ohci->host->id,
+ "Waking up iso dma ctx=%d", d->ctx);
+ reg_write(ohci, d->ctrlSet, 0x1000);
+ }
+ }
+ return 0;
+
+ }
+ case VIDEO1394_IOC_LISTEN_WAIT_BUFFER:
+ case VIDEO1394_IOC_LISTEN_POLL_BUFFER:
+ {
+ struct video1394_wait v;
+ struct dma_iso_ctx *d;
+ int i = 0;
+
+ if (unlikely(copy_from_user(&v, argp, sizeof(v))))
+ return -EFAULT;
+
+ d = find_ctx(&ctx->context_list, OHCI_ISO_RECEIVE, v.channel);
+ if (unlikely(d == NULL))
+ return -EFAULT;
+
+ if (unlikely(v.buffer > d->num_desc - 1)) {
+ PRINT(KERN_ERR, ohci->host->id,
+ "Buffer %d out of range",v.buffer);
+ return -EINVAL;
+ }
+
+ /*
+ * I change the way it works so that it returns
+ * the last received frame.
+ */
+ spin_lock_irqsave(&d->lock, flags);
+ switch(d->buffer_status[v.buffer]) {
+ case VIDEO1394_BUFFER_READY:
+ d->buffer_status[v.buffer]=VIDEO1394_BUFFER_FREE;
+ break;
+ case VIDEO1394_BUFFER_QUEUED:
+ if (cmd == VIDEO1394_IOC_LISTEN_POLL_BUFFER) {
+ /* for polling, return error code EINTR */
+ spin_unlock_irqrestore(&d->lock, flags);
+ return -EINTR;
+ }
+
+ spin_unlock_irqrestore(&d->lock, flags);
+ wait_event_interruptible(d->waitq,
+ video1394_buffer_state(d, v.buffer) ==
+ VIDEO1394_BUFFER_READY);
+ if (signal_pending(current))
+ return -EINTR;
+ spin_lock_irqsave(&d->lock, flags);
+ d->buffer_status[v.buffer]=VIDEO1394_BUFFER_FREE;
+ break;
+ default:
+ PRINT(KERN_ERR, ohci->host->id,
+ "Buffer %d is not queued",v.buffer);
+ spin_unlock_irqrestore(&d->lock, flags);
+ return -ESRCH;
+ }
+
+ /* set time of buffer */
+ v.filltime = d->buffer_time[v.buffer];
+
+ /*
+ * Look ahead to see how many more buffers have been received
+ */
+ i=0;
+ while (d->buffer_status[(v.buffer+1)%(d->num_desc - 1)]==
+ VIDEO1394_BUFFER_READY) {
+ v.buffer=(v.buffer+1)%(d->num_desc - 1);
+ i++;
+ }
+ spin_unlock_irqrestore(&d->lock, flags);
+
+ v.buffer=i;
+ if (unlikely(copy_to_user(argp, &v, sizeof(v))))
+ return -EFAULT;
+
+ return 0;
+ }
+ case VIDEO1394_IOC_TALK_QUEUE_BUFFER:
+ {
+ struct video1394_wait v;
+ unsigned int *psizes = NULL;
+ struct dma_iso_ctx *d;
+ int next_prg;
+
+ if (copy_from_user(&v, argp, sizeof(v)))
+ return -EFAULT;
+
+ d = find_ctx(&ctx->context_list, OHCI_ISO_TRANSMIT, v.channel);
+ if (d == NULL) return -EFAULT;
+
+ if (v.buffer >= d->num_desc - 1) {
+ PRINT(KERN_ERR, ohci->host->id,
+ "Buffer %d out of range",v.buffer);
+ return -EINVAL;
+ }
+
+ if (d->flags & VIDEO1394_VARIABLE_PACKET_SIZE) {
+ int buf_size = d->nb_cmd * sizeof(*psizes);
+ struct video1394_queue_variable __user *p = argp;
+ unsigned int __user *qv;
+
+ if (get_user(qv, &p->packet_sizes))
+ return -EFAULT;
+
+ psizes = kmalloc(buf_size, GFP_KERNEL);
+ if (!psizes)
+ return -ENOMEM;
+
+ if (copy_from_user(psizes, qv, buf_size)) {
+ kfree(psizes);
+ return -EFAULT;
+ }
+ }
+
+ spin_lock_irqsave(&d->lock,flags);
+
+ /* last_buffer is last_prg */
+ next_prg = (d->last_buffer + 1) % d->num_desc;
+ if (d->buffer_status[v.buffer]!=VIDEO1394_BUFFER_FREE) {
+ PRINT(KERN_ERR, ohci->host->id,
+ "Buffer %d is already used",v.buffer);
+ spin_unlock_irqrestore(&d->lock,flags);
+ kfree(psizes);
+ return -EBUSY;
+ }
+
+ if (d->flags & VIDEO1394_VARIABLE_PACKET_SIZE) {
+ initialize_dma_it_prg_var_packet_queue(
+ d, next_prg, psizes, ohci);
+ }
+
+ d->buffer_status[v.buffer]=VIDEO1394_BUFFER_QUEUED;
+
+ if (d->last_buffer >= 0) {
+ d->it_prg[d->last_buffer]
+ [ d->last_used_cmd[d->last_buffer] ].end.branchAddress =
+ cpu_to_le32((dma_prog_region_offset_to_bus(&d->prg_reg[next_prg],
+ 0) & 0xfffffff0) | 0x3);
+
+ d->it_prg[d->last_buffer]
+ [ d->last_used_cmd[d->last_buffer] ].begin.branchAddress =
+ cpu_to_le32((dma_prog_region_offset_to_bus(&d->prg_reg[next_prg],
+ 0) & 0xfffffff0) | 0x3);
+ d->next_buffer[d->last_buffer] = (v.buffer + 1) % (d->num_desc - 1);
+ }
+ d->last_buffer = next_prg;
+ reprogram_dma_it_prg(d, d->last_buffer, v.buffer);
+ d->next_buffer[d->last_buffer] = -1;
+
+ d->it_prg[d->last_buffer][d->last_used_cmd[d->last_buffer]].end.branchAddress = 0;
+
+ spin_unlock_irqrestore(&d->lock,flags);
+
+ if (!(reg_read(ohci, d->ctrlSet) & 0x8000))
+ {
+ DBGMSG(ohci->host->id, "Starting iso transmit DMA ctx=%d",
+ d->ctx);
+ put_timestamp(ohci, d, d->last_buffer);
+ dma_region_sync_for_device(&d->dma,
+ v.buffer * d->buf_size, d->buf_size);
+
+ /* Tell the controller where the first program is */
+ reg_write(ohci, d->cmdPtr,
+ dma_prog_region_offset_to_bus(&d->prg_reg[next_prg], 0) | 0x3);
+
+ /* Run IT context */
+ reg_write(ohci, d->ctrlSet, 0x8000);
+ }
+ else {
+ /* Wake up dma context if necessary */
+ if (!(reg_read(ohci, d->ctrlSet) & 0x400)) {
+ DBGMSG(ohci->host->id,
+ "Waking up iso transmit dma ctx=%d",
+ d->ctx);
+ put_timestamp(ohci, d, d->last_buffer);
+ dma_region_sync_for_device(&d->dma,
+ v.buffer * d->buf_size, d->buf_size);
+
+ reg_write(ohci, d->ctrlSet, 0x1000);
+ }
+ }
+
+ kfree(psizes);
+ return 0;
+
+ }
+ case VIDEO1394_IOC_TALK_WAIT_BUFFER:
+ {
+ struct video1394_wait v;
+ struct dma_iso_ctx *d;
+
+ if (copy_from_user(&v, argp, sizeof(v)))
+ return -EFAULT;
+
+ d = find_ctx(&ctx->context_list, OHCI_ISO_TRANSMIT, v.channel);
+ if (d == NULL) return -EFAULT;
+
+ if (v.buffer >= d->num_desc - 1) {
+ PRINT(KERN_ERR, ohci->host->id,
+ "Buffer %d out of range",v.buffer);
+ return -EINVAL;
+ }
+
+ switch(d->buffer_status[v.buffer]) {
+ case VIDEO1394_BUFFER_READY:
+ d->buffer_status[v.buffer]=VIDEO1394_BUFFER_FREE;
+ return 0;
+ case VIDEO1394_BUFFER_QUEUED:
+ wait_event_interruptible(d->waitq,
+ (d->buffer_status[v.buffer] == VIDEO1394_BUFFER_READY));
+ if (signal_pending(current))
+ return -EINTR;
+ d->buffer_status[v.buffer]=VIDEO1394_BUFFER_FREE;
+ return 0;
+ default:
+ PRINT(KERN_ERR, ohci->host->id,
+ "Buffer %d is not queued",v.buffer);
+ return -ESRCH;
+ }
+ }
+ default:
+ return -ENOTTY;
+ }
+}
+
+/*
+ * This maps the vmalloced and reserved buffer to user space.
+ *
+ * FIXME:
+ * - PAGE_READONLY should suffice!?
+ * - remap_pfn_range is kind of inefficient for page by page remapping.
+ * But e.g. pte_alloc() does not work in modules ... :-(
+ */
+
+static int video1394_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct file_ctx *ctx = (struct file_ctx *)file->private_data;
+
+ if (ctx->current_ctx == NULL) {
+ PRINT(KERN_ERR, ctx->ohci->host->id,
+ "Current iso context not set");
+ return -EINVAL;
+ }
+
+ return dma_region_mmap(&ctx->current_ctx->dma, file, vma);
+}
+
+static unsigned int video1394_poll(struct file *file, poll_table *pt)
+{
+ struct file_ctx *ctx;
+ unsigned int mask = 0;
+ unsigned long flags;
+ struct dma_iso_ctx *d;
+ int i;
+
+ ctx = file->private_data;
+ d = ctx->current_ctx;
+ if (d == NULL) {
+ PRINT(KERN_ERR, ctx->ohci->host->id,
+ "Current iso context not set");
+ return POLLERR;
+ }
+
+ poll_wait(file, &d->waitq, pt);
+
+ spin_lock_irqsave(&d->lock, flags);
+ for (i = 0; i < d->num_desc; i++) {
+ if (d->buffer_status[i] == VIDEO1394_BUFFER_READY) {
+ mask |= POLLIN | POLLRDNORM;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&d->lock, flags);
+
+ return mask;
+}
+
+static int video1394_open(struct inode *inode, struct file *file)
+{
+ int i = ieee1394_file_to_instance(file);
+ struct ti_ohci *ohci;
+ struct file_ctx *ctx;
+
+ ohci = hpsb_get_hostinfo_bykey(&video1394_highlevel, i);
+ if (ohci == NULL)
+ return -EIO;
+
+ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+ if (!ctx) {
+ PRINT(KERN_ERR, ohci->host->id, "Cannot malloc file_ctx");
+ return -ENOMEM;
+ }
+
+ ctx->ohci = ohci;
+ INIT_LIST_HEAD(&ctx->context_list);
+ ctx->current_ctx = NULL;
+ file->private_data = ctx;
+
+ return 0;
+}
+
+static int video1394_release(struct inode *inode, struct file *file)
+{
+ struct file_ctx *ctx = (struct file_ctx *)file->private_data;
+ struct ti_ohci *ohci = ctx->ohci;
+ struct list_head *lh, *next;
+ u64 mask;
+
+ list_for_each_safe(lh, next, &ctx->context_list) {
+ struct dma_iso_ctx *d;
+ d = list_entry(lh, struct dma_iso_ctx, link);
+ mask = (u64) 1 << d->channel;
+
+ if (!(ohci->ISO_channel_usage & mask))
+ PRINT(KERN_ERR, ohci->host->id, "On release: Channel %d "
+ "is not being used", d->channel);
+ else
+ ohci->ISO_channel_usage &= ~mask;
+ DBGMSG(ohci->host->id, "On release: Iso %s context "
+ "%d stop listening on channel %d",
+ d->type == OHCI_ISO_RECEIVE ? "receive" : "transmit",
+ d->ctx, d->channel);
+ free_dma_iso_ctx(d);
+ }
+
+ kfree(ctx);
+ file->private_data = NULL;
+
+ return 0;
+}
+
+#ifdef CONFIG_COMPAT
+static long video1394_compat_ioctl(struct file *f, unsigned cmd, unsigned long arg);
+#endif
+
+static struct cdev video1394_cdev;
+static const struct file_operations video1394_fops=
+{
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = video1394_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = video1394_compat_ioctl,
+#endif
+ .poll = video1394_poll,
+ .mmap = video1394_mmap,
+ .open = video1394_open,
+ .release = video1394_release
+};
+
+/*** HOTPLUG STUFF **********************************************************/
+/*
+ * Export information about protocols/devices supported by this driver.
+ */
+#ifdef MODULE
+static struct ieee1394_device_id video1394_id_table[] = {
+ {
+ .match_flags = IEEE1394_MATCH_SPECIFIER_ID | IEEE1394_MATCH_VERSION,
+ .specifier_id = CAMERA_UNIT_SPEC_ID_ENTRY & 0xffffff,
+ .version = CAMERA_SW_VERSION_ENTRY & 0xffffff
+ },
+ {
+ .match_flags = IEEE1394_MATCH_SPECIFIER_ID | IEEE1394_MATCH_VERSION,
+ .specifier_id = CAMERA_UNIT_SPEC_ID_ENTRY & 0xffffff,
+ .version = (CAMERA_SW_VERSION_ENTRY + 1) & 0xffffff
+ },
+ {
+ .match_flags = IEEE1394_MATCH_SPECIFIER_ID | IEEE1394_MATCH_VERSION,
+ .specifier_id = CAMERA_UNIT_SPEC_ID_ENTRY & 0xffffff,
+ .version = (CAMERA_SW_VERSION_ENTRY + 2) & 0xffffff
+ },
+ { }
+};
+
+MODULE_DEVICE_TABLE(ieee1394, video1394_id_table);
+#endif /* MODULE */
+
+static struct hpsb_protocol_driver video1394_driver = {
+ .name = VIDEO1394_DRIVER_NAME,
+};
+
+
+static void video1394_add_host (struct hpsb_host *host)
+{
+ struct ti_ohci *ohci;
+ int minor;
+
+ /* We only work with the OHCI-1394 driver */
+ if (strcmp(host->driver->name, OHCI1394_DRIVER_NAME))
+ return;
+
+ ohci = (struct ti_ohci *)host->hostdata;
+
+ if (!hpsb_create_hostinfo(&video1394_highlevel, host, 0)) {
+ PRINT(KERN_ERR, ohci->host->id, "Cannot allocate hostinfo");
+ return;
+ }
+
+ hpsb_set_hostinfo(&video1394_highlevel, host, ohci);
+ hpsb_set_hostinfo_key(&video1394_highlevel, host, ohci->host->id);
+
+ minor = IEEE1394_MINOR_BLOCK_VIDEO1394 * 16 + ohci->host->id;
+ device_create(hpsb_protocol_class, NULL, MKDEV(IEEE1394_MAJOR, minor),
+ NULL, "%s-%d", VIDEO1394_DRIVER_NAME, ohci->host->id);
+}
+
+
+static void video1394_remove_host (struct hpsb_host *host)
+{
+ struct ti_ohci *ohci = hpsb_get_hostinfo(&video1394_highlevel, host);
+
+ if (ohci)
+ device_destroy(hpsb_protocol_class, MKDEV(IEEE1394_MAJOR,
+ IEEE1394_MINOR_BLOCK_VIDEO1394 * 16 + ohci->host->id));
+ return;
+}
+
+
+static struct hpsb_highlevel video1394_highlevel = {
+ .name = VIDEO1394_DRIVER_NAME,
+ .add_host = video1394_add_host,
+ .remove_host = video1394_remove_host,
+};
+
+MODULE_AUTHOR("Sebastien Rougeaux <sebastien.rougeaux@anu.edu.au>");
+MODULE_DESCRIPTION("driver for digital video on OHCI board");
+MODULE_SUPPORTED_DEVICE(VIDEO1394_DRIVER_NAME);
+MODULE_LICENSE("GPL");
+
+#ifdef CONFIG_COMPAT
+
+#define VIDEO1394_IOC32_LISTEN_QUEUE_BUFFER \
+ _IOW ('#', 0x12, struct video1394_wait32)
+#define VIDEO1394_IOC32_LISTEN_WAIT_BUFFER \
+ _IOWR('#', 0x13, struct video1394_wait32)
+#define VIDEO1394_IOC32_TALK_WAIT_BUFFER \
+ _IOW ('#', 0x17, struct video1394_wait32)
+#define VIDEO1394_IOC32_LISTEN_POLL_BUFFER \
+ _IOWR('#', 0x18, struct video1394_wait32)
+
+struct video1394_wait32 {
+ u32 channel;
+ u32 buffer;
+ struct compat_timeval filltime;
+};
+
+static int video1394_wr_wait32(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct video1394_wait32 __user *argp = (void __user *)arg;
+ struct video1394_wait32 wait32;
+ struct video1394_wait wait;
+ mm_segment_t old_fs;
+ int ret;
+
+ if (copy_from_user(&wait32, argp, sizeof(wait32)))
+ return -EFAULT;
+
+ wait.channel = wait32.channel;
+ wait.buffer = wait32.buffer;
+ wait.filltime.tv_sec = (time_t)wait32.filltime.tv_sec;
+ wait.filltime.tv_usec = (suseconds_t)wait32.filltime.tv_usec;
+
+ old_fs = get_fs();
+ set_fs(KERNEL_DS);
+ if (cmd == VIDEO1394_IOC32_LISTEN_WAIT_BUFFER)
+ ret = video1394_ioctl(file,
+ VIDEO1394_IOC_LISTEN_WAIT_BUFFER,
+ (unsigned long) &wait);
+ else
+ ret = video1394_ioctl(file,
+ VIDEO1394_IOC_LISTEN_POLL_BUFFER,
+ (unsigned long) &wait);
+ set_fs(old_fs);
+
+ if (!ret) {
+ wait32.channel = wait.channel;
+ wait32.buffer = wait.buffer;
+ wait32.filltime.tv_sec = (int)wait.filltime.tv_sec;
+ wait32.filltime.tv_usec = (int)wait.filltime.tv_usec;
+
+ if (copy_to_user(argp, &wait32, sizeof(wait32)))
+ ret = -EFAULT;
+ }
+
+ return ret;
+}
+
+static int video1394_w_wait32(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct video1394_wait32 wait32;
+ struct video1394_wait wait;
+ mm_segment_t old_fs;
+ int ret;
+
+ if (copy_from_user(&wait32, (void __user *)arg, sizeof(wait32)))
+ return -EFAULT;
+
+ wait.channel = wait32.channel;
+ wait.buffer = wait32.buffer;
+ wait.filltime.tv_sec = (time_t)wait32.filltime.tv_sec;
+ wait.filltime.tv_usec = (suseconds_t)wait32.filltime.tv_usec;
+
+ old_fs = get_fs();
+ set_fs(KERNEL_DS);
+ if (cmd == VIDEO1394_IOC32_LISTEN_QUEUE_BUFFER)
+ ret = video1394_ioctl(file,
+ VIDEO1394_IOC_LISTEN_QUEUE_BUFFER,
+ (unsigned long) &wait);
+ else
+ ret = video1394_ioctl(file,
+ VIDEO1394_IOC_TALK_WAIT_BUFFER,
+ (unsigned long) &wait);
+ set_fs(old_fs);
+
+ return ret;
+}
+
+static int video1394_queue_buf32(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ return -EFAULT; /* ??? was there before. */
+
+ return video1394_ioctl(file,
+ VIDEO1394_IOC_TALK_QUEUE_BUFFER, arg);
+}
+
+static long video1394_compat_ioctl(struct file *f, unsigned cmd, unsigned long arg)
+{
+ switch (cmd) {
+ case VIDEO1394_IOC_LISTEN_CHANNEL:
+ case VIDEO1394_IOC_UNLISTEN_CHANNEL:
+ case VIDEO1394_IOC_TALK_CHANNEL:
+ case VIDEO1394_IOC_UNTALK_CHANNEL:
+ return video1394_ioctl(f, cmd, arg);
+
+ case VIDEO1394_IOC32_LISTEN_QUEUE_BUFFER:
+ return video1394_w_wait32(f, cmd, arg);
+ case VIDEO1394_IOC32_LISTEN_WAIT_BUFFER:
+ return video1394_wr_wait32(f, cmd, arg);
+ case VIDEO1394_IOC_TALK_QUEUE_BUFFER:
+ return video1394_queue_buf32(f, cmd, arg);
+ case VIDEO1394_IOC32_TALK_WAIT_BUFFER:
+ return video1394_w_wait32(f, cmd, arg);
+ case VIDEO1394_IOC32_LISTEN_POLL_BUFFER:
+ return video1394_wr_wait32(f, cmd, arg);
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+#endif /* CONFIG_COMPAT */
+
+static void __exit video1394_exit_module (void)
+{
+ hpsb_unregister_protocol(&video1394_driver);
+ hpsb_unregister_highlevel(&video1394_highlevel);
+ cdev_del(&video1394_cdev);
+ PRINT_G(KERN_INFO, "Removed " VIDEO1394_DRIVER_NAME " module");
+}
+
+static int __init video1394_init_module (void)
+{
+ int ret;
+
+ hpsb_init_highlevel(&video1394_highlevel);
+
+ cdev_init(&video1394_cdev, &video1394_fops);
+ video1394_cdev.owner = THIS_MODULE;
+ ret = cdev_add(&video1394_cdev, IEEE1394_VIDEO1394_DEV, 16);
+ if (ret) {
+ PRINT_G(KERN_ERR, "video1394: unable to get minor device block");
+ return ret;
+ }
+
+ hpsb_register_highlevel(&video1394_highlevel);
+
+ ret = hpsb_register_protocol(&video1394_driver);
+ if (ret) {
+ PRINT_G(KERN_ERR, "video1394: failed to register protocol");
+ hpsb_unregister_highlevel(&video1394_highlevel);
+ cdev_del(&video1394_cdev);
+ return ret;
+ }
+
+ PRINT_G(KERN_INFO, "Installed " VIDEO1394_DRIVER_NAME " module");
+ return 0;
+}
+
+
+module_init(video1394_init_module);
+module_exit(video1394_exit_module);
diff --git a/drivers/ieee1394/video1394.h b/drivers/ieee1394/video1394.h
new file mode 100644
index 0000000..9a89d9c
--- /dev/null
+++ b/drivers/ieee1394/video1394.h
@@ -0,0 +1,67 @@
+/*
+ * video1394.h - driver for OHCI 1394 boards
+ * Copyright (C)1999,2000 Sebastien Rougeaux <sebastien.rougeaux@anu.edu.au>
+ * Peter Schlaile <udbz@rz.uni-karlsruhe.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _VIDEO_1394_H
+#define _VIDEO_1394_H
+
+#include "ieee1394-ioctl.h"
+
+#define VIDEO1394_DRIVER_NAME "video1394"
+
+#define VIDEO1394_MAX_SIZE 0x4000000
+
+enum {
+ VIDEO1394_BUFFER_FREE = 0,
+ VIDEO1394_BUFFER_QUEUED,
+ VIDEO1394_BUFFER_READY
+};
+
+#define VIDEO1394_SYNC_FRAMES 0x00000001
+#define VIDEO1394_INCLUDE_ISO_HEADERS 0x00000002
+#define VIDEO1394_VARIABLE_PACKET_SIZE 0x00000004
+
+struct video1394_mmap {
+ int channel; /* -1 to find an open channel in LISTEN/TALK */
+ unsigned int sync_tag;
+ unsigned int nb_buffers;
+ unsigned int buf_size;
+ unsigned int packet_size; /* For VARIABLE_PACKET_SIZE:
+ Maximum packet size */
+ unsigned int fps;
+ unsigned int syt_offset;
+ unsigned int flags;
+};
+
+/* For TALK_QUEUE_BUFFER with VIDEO1394_VARIABLE_PACKET_SIZE use */
+struct video1394_queue_variable {
+ unsigned int channel;
+ unsigned int buffer;
+ unsigned int __user * packet_sizes; /* Buffer of size:
+ buf_size / packet_size */
+};
+
+struct video1394_wait {
+ unsigned int channel;
+ unsigned int buffer;
+ struct timeval filltime; /* time of buffer full */
+};
+
+
+#endif
OpenPOWER on IntegriCloud