diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/ieee1394 | |
download | kernel_samsung_smdk4412-1da177e4c3f41524e886b7f1b8a0c1fc7321cac2.zip kernel_samsung_smdk4412-1da177e4c3f41524e886b7f1b8a0c1fc7321cac2.tar.gz kernel_samsung_smdk4412-1da177e4c3f41524e886b7f1b8a0c1fc7321cac2.tar.bz2 |
Linux-2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/ieee1394')
48 files changed, 39925 insertions, 0 deletions
diff --git a/drivers/ieee1394/Kconfig b/drivers/ieee1394/Kconfig new file mode 100644 index 0000000..78b201f --- /dev/null +++ b/drivers/ieee1394/Kconfig @@ -0,0 +1,188 @@ +# -*- shell-script -*- + +menu "IEEE 1394 (FireWire) support" + +config IEEE1394 + tristate "IEEE 1394 (FireWire) support" + depends on PCI || BROKEN + select NET + 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. + +comment "Subsystem Options" + depends on IEEE1394 + +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 subsystem which includes a dump of the header of every sent + and received packet. This can amount to a high amount of data + collected in a very short time which is usually also saved to + disk by the system logging daemons. + + Say Y if you really want or need the debugging output, everyone + else says N. + +config IEEE1394_OUI_DB + bool "OUI Database built-in" + depends on IEEE1394 + help + If you say Y here, then an OUI list (vendor unique ID's) will be + compiled into the ieee1394 module. This doesn't really do much + except being able to display the vendor of a hardware node. The + downside is that it adds about 300k to the size of the module, + or kernel (depending on whether you compile ieee1394 as a + module, or static in the kernel). + + This option is not needed for userspace programs like gscanbus + to show this information. + +config IEEE1394_EXTRA_CONFIG_ROMS + bool "Build in extra config rom entries for certain functionality" + depends on IEEE1394 + help + Some IEEE1394 functionality depends on extra config rom entries + being available in the host adapters CSR. These options will + allow you to choose which ones. + +config IEEE1394_CONFIG_ROM_IP1394 + bool "IP-1394 Entry" + depends on IEEE1394_EXTRA_CONFIG_ROMS && IEEE1394 + help + Adds an entry for using IP-over-1394. If you want to use your + IEEE1394 bus as a network for IP systems (including interacting + with MacOSX and WinXP IP-over-1394), enable this option and the + eth1394 option below. + +comment "Device Drivers" + depends on IEEE1394 + +comment "Texas Instruments PCILynx requires I2C" + depends on IEEE1394 && I2C=n + +config IEEE1394_PCILYNX + tristate "Texas Instruments PCILynx support" + 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. + +# Non-maintained pcilynx options +# if [ "$CONFIG_IEEE1394_PCILYNX" != "n" ]; then +# bool ' Use PCILynx local RAM' CONFIG_IEEE1394_PCILYNX_LOCALRAM +# bool ' Support for non-IEEE1394 local ports' CONFIG_IEEE1394_PCILYNX_PORTS +# fi +config IEEE1394_OHCI1394 + tristate "OHCI-1394 support" + 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. + +comment "Protocol Drivers" + depends on IEEE1394 + +config IEEE1394_VIDEO1394 + tristate "OHCI-1394 Video support" + depends on IEEE1394 && IEEE1394_OHCI1394 + help + This option enables video device usage for OHCI-1394 cards. Enable + this option only if you have an IEEE 1394 video device connected to + an OHCI-1394 card. + +config IEEE1394_SBP2 + tristate "SBP-2 support (Harddisks etc.)" + depends on IEEE1394 && SCSI && (PCI || BROKEN) + help + This option enables you to use SBP-2 devices connected to your IEEE + 1394 bus. SBP-2 devices include harddrives and DVD devices. + +config IEEE1394_SBP2_PHYS_DMA + bool "Enable Phys DMA support for SBP2 (Debug)" + depends on IEEE1394 && IEEE1394_SBP2 + +config IEEE1394_ETH1394 + tristate "Ethernet over 1394" + depends on IEEE1394 && EXPERIMENTAL && INET + select IEEE1394_CONFIG_ROM_IP1394 + select IEEE1394_EXTRA_CONFIG_ROMS + 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. + +config IEEE1394_DV1394 + tristate "OHCI-DV I/O support" + depends on IEEE1394 && IEEE1394_OHCI1394 + help + This driver allows you to transmit and receive DV (digital video) + streams on an OHCI-1394 card using a simple frame-oriented + interface. + + The user-space API for dv1394 is documented in dv1394.h. + + To compile this driver as a module, say M here: the + module will be called dv1394. + +config IEEE1394_RAWIO + tristate "Raw IEEE1394 I/O support" + depends on IEEE1394 + help + Say Y here if you want support for the raw device. This is generally + a good idea, so you should say Y here. The raw device enables + direct communication of user programs with the IEEE 1394 bus and + thus with the attached peripherals. + + To compile this driver as a module, say M here: the + module will be called raw1394. + +config IEEE1394_CMP + tristate "IEC61883-1 Plug support" + depends on IEEE1394 + help + This option enables the Connection Management Procedures + (IEC61883-1) driver, which implements input and output plugs. + + To compile this driver as a module, say M here: the + module will be called cmp. + +config IEEE1394_AMDTP + tristate "IEC61883-6 (Audio transmission) support" + depends on IEEE1394 && IEEE1394_OHCI1394 && IEEE1394_CMP + help + This option enables the Audio & Music Data Transmission Protocol + (IEC61883-6) driver, which implements audio transmission over + IEEE1394. + + The userspace interface is documented in amdtp.h. + + To compile this driver as a module, say M here: the + module will be called amdtp. + +endmenu diff --git a/drivers/ieee1394/Makefile b/drivers/ieee1394/Makefile new file mode 100644 index 0000000..e8b4d48 --- /dev/null +++ b/drivers/ieee1394/Makefile @@ -0,0 +1,26 @@ +# +# Makefile for the Linux IEEE 1394 implementation +# + +ieee1394-objs := ieee1394_core.o ieee1394_transactions.o hosts.o \ + highlevel.o csr.o nodemgr.o oui.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_IEEE1394_AMDTP) += amdtp.o +obj-$(CONFIG_IEEE1394_CMP) += cmp.o + +quiet_cmd_oui2c = OUI2C $@ + cmd_oui2c = $(CONFIG_SHELL) $(srctree)/$(src)/oui2c.sh < $< > $@ + +targets := oui.c +$(obj)/oui.o: $(obj)/oui.c +$(obj)/oui.c: $(src)/oui.db $(src)/oui2c.sh FORCE + $(call if_changed,oui2c) diff --git a/drivers/ieee1394/amdtp.c b/drivers/ieee1394/amdtp.c new file mode 100644 index 0000000..84ae027 --- /dev/null +++ b/drivers/ieee1394/amdtp.c @@ -0,0 +1,1300 @@ +/* -*- c-basic-offset: 8 -*- + * + * amdtp.c - Audio and Music Data Transmission Protocol Driver + * Copyright (C) 2001 Kristian Høgsberg + * + * 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 + * -------- + * + * The AMDTP driver is designed to expose the IEEE1394 bus as a + * regular OSS soundcard, i.e. you can link /dev/dsp to /dev/amdtp and + * then your favourite MP3 player, game or whatever sound program will + * output to an IEEE1394 isochronous channel. The signal destination + * could be a set of IEEE1394 loudspeakers (if and when such things + * become available) or an amplifier with IEEE1394 input (like the + * Sony STR-LSA1). The driver only handles the actual streaming, some + * connection management is also required for this to actually work. + * That is outside the scope of this driver, and furthermore it is not + * really standardized yet. + * + * The Audio and Music Data Tranmission Protocol is available at + * + * http://www.1394ta.org/Download/Technology/Specifications/2001/AM20Final-jf2.pdf + * + * + * TODO + * ---- + * + * - We should be able to change input sample format between LE/BE, as + * we already shift the bytes around when we construct the iso + * packets. + * + * - Fix DMA stop after bus reset! + * + * - Clean up iso context handling in ohci1394. + * + * + * MAYBE TODO + * ---------- + * + * - Receive data for local playback or recording. Playback requires + * soft syncing with the sound card. + * + * - Signal processing, i.e. receive packets, do some processing, and + * transmit them again using the same packet structure and timestamps + * offset by processing time. + * + * - Maybe make an ALSA interface, that is, create a file_ops + * implementation that recognizes ALSA ioctls and uses defaults for + * things that can't be controlled through ALSA (iso channel). + * + * Changes: + * + * - Audit copy_from_user in amdtp_write. + * Daniele Bellucci <bellucda@tiscali.it> + * + */ + +#include <linux/module.h> +#include <linux/list.h> +#include <linux/sched.h> +#include <linux/types.h> +#include <linux/fs.h> +#include <linux/ioctl.h> +#include <linux/wait.h> +#include <linux/pci.h> +#include <linux/interrupt.h> +#include <linux/poll.h> +#include <linux/ioctl32.h> +#include <linux/compat.h> +#include <linux/cdev.h> +#include <asm/uaccess.h> +#include <asm/atomic.h> + +#include "hosts.h" +#include "highlevel.h" +#include "ieee1394.h" +#include "ieee1394_core.h" +#include "ohci1394.h" + +#include "amdtp.h" +#include "cmp.h" + +#define FMT_AMDTP 0x10 +#define FDF_AM824 0x00 +#define FDF_SFC_32KHZ 0x00 +#define FDF_SFC_44K1HZ 0x01 +#define FDF_SFC_48KHZ 0x02 +#define FDF_SFC_88K2HZ 0x03 +#define FDF_SFC_96KHZ 0x04 +#define FDF_SFC_176K4HZ 0x05 +#define FDF_SFC_192KHZ 0x06 + +struct descriptor_block { + struct output_more_immediate { + u32 control; + u32 pad0; + u32 skip; + u32 pad1; + u32 header[4]; + } header_desc; + + struct output_last { + u32 control; + u32 data_address; + u32 branch; + u32 status; + } payload_desc; +}; + +struct packet { + struct descriptor_block *db; + dma_addr_t db_bus; + struct iso_packet *payload; + dma_addr_t payload_bus; +}; + +#include <asm/byteorder.h> + +#if defined __BIG_ENDIAN_BITFIELD + +struct iso_packet { + /* First quadlet */ + unsigned int dbs : 8; + unsigned int eoh0 : 2; + unsigned int sid : 6; + + unsigned int dbc : 8; + unsigned int fn : 2; + unsigned int qpc : 3; + unsigned int sph : 1; + unsigned int reserved : 2; + + /* Second quadlet */ + unsigned int fdf : 8; + unsigned int eoh1 : 2; + unsigned int fmt : 6; + + unsigned int syt : 16; + + quadlet_t data[0]; +}; + +#elif defined __LITTLE_ENDIAN_BITFIELD + +struct iso_packet { + /* First quadlet */ + unsigned int sid : 6; + unsigned int eoh0 : 2; + unsigned int dbs : 8; + + unsigned int reserved : 2; + unsigned int sph : 1; + unsigned int qpc : 3; + unsigned int fn : 2; + unsigned int dbc : 8; + + /* Second quadlet */ + unsigned int fmt : 6; + unsigned int eoh1 : 2; + unsigned int fdf : 8; + + unsigned int syt : 16; + + quadlet_t data[0]; +}; + +#else + +#error Unknown bitfield type + +#endif + +struct fraction { + int integer; + int numerator; + int denominator; +}; + +#define PACKET_LIST_SIZE 256 +#define MAX_PACKET_LISTS 4 + +struct packet_list { + struct list_head link; + int last_cycle_count; + struct packet packets[PACKET_LIST_SIZE]; +}; + +#define BUFFER_SIZE 128 + +/* This implements a circular buffer for incoming samples. */ + +struct buffer { + size_t head, tail, length, size; + unsigned char data[0]; +}; + +struct stream { + int iso_channel; + int format; + int rate; + int dimension; + int fdf; + int mode; + int sample_format; + struct cmp_pcr *opcr; + + /* Input samples are copied here. */ + struct buffer *input; + + /* ISO Packer state */ + unsigned char dbc; + struct packet_list *current_packet_list; + int current_packet; + struct fraction ready_samples, samples_per_cycle; + + /* We use these to generate control bits when we are packing + * iec958 data. + */ + int iec958_frame_count; + int iec958_rate_code; + + /* The cycle_count and cycle_offset fields are used for the + * synchronization timestamps (syt) in the cip header. They + * are incremented by at least a cycle every time we put a + * time stamp in a packet. As we don't time stamp all + * packages, cycle_count isn't updated in every cycle, and + * sometimes it's incremented by 2. Thus, we have + * cycle_count2, which is simply incremented by one with each + * packet, so we can compare it to the transmission time + * written back in the dma programs. + */ + atomic_t cycle_count, cycle_count2; + struct fraction cycle_offset, ticks_per_syt_offset; + int syt_interval; + int stale_count; + + /* Theses fields control the sample output to the DMA engine. + * The dma_packet_lists list holds packet lists currently + * queued for dma; the head of the list is currently being + * processed. The last program in a packet list generates an + * interrupt, which removes the head from dma_packet_lists and + * puts it back on the free list. + */ + struct list_head dma_packet_lists; + struct list_head free_packet_lists; + wait_queue_head_t packet_list_wait; + spinlock_t packet_list_lock; + struct ohci1394_iso_tasklet iso_tasklet; + struct pci_pool *descriptor_pool, *packet_pool; + + /* Streams at a host controller are chained through this field. */ + struct list_head link; + struct amdtp_host *host; +}; + +struct amdtp_host { + struct hpsb_host *host; + struct ti_ohci *ohci; + struct list_head stream_list; + spinlock_t stream_list_lock; +}; + +static struct hpsb_highlevel amdtp_highlevel; + + +/* FIXME: This doesn't belong here... */ + +#define OHCI1394_CONTEXT_CYCLE_MATCH 0x80000000 +#define OHCI1394_CONTEXT_RUN 0x00008000 +#define OHCI1394_CONTEXT_WAKE 0x00001000 +#define OHCI1394_CONTEXT_DEAD 0x00000800 +#define OHCI1394_CONTEXT_ACTIVE 0x00000400 + +static void ohci1394_start_it_ctx(struct ti_ohci *ohci, int ctx, + dma_addr_t first_cmd, int z, int cycle_match) +{ + reg_write(ohci, OHCI1394_IsoXmitIntMaskSet, 1 << ctx); + reg_write(ohci, OHCI1394_IsoXmitCommandPtr + ctx * 16, first_cmd | z); + reg_write(ohci, OHCI1394_IsoXmitContextControlClear + ctx * 16, ~0); + wmb(); + reg_write(ohci, OHCI1394_IsoXmitContextControlSet + ctx * 16, + OHCI1394_CONTEXT_CYCLE_MATCH | (cycle_match << 16) | + OHCI1394_CONTEXT_RUN); +} + +static void ohci1394_wake_it_ctx(struct ti_ohci *ohci, int ctx) +{ + reg_write(ohci, OHCI1394_IsoXmitContextControlSet + ctx * 16, + OHCI1394_CONTEXT_WAKE); +} + +static void ohci1394_stop_it_ctx(struct ti_ohci *ohci, int ctx, int synchronous) +{ + u32 control; + int wait; + + reg_write(ohci, OHCI1394_IsoXmitIntMaskClear, 1 << ctx); + reg_write(ohci, OHCI1394_IsoXmitContextControlClear + ctx * 16, + OHCI1394_CONTEXT_RUN); + wmb(); + + if (synchronous) { + for (wait = 0; wait < 5; wait++) { + control = reg_read(ohci, OHCI1394_IsoXmitContextControlSet + ctx * 16); + if ((control & OHCI1394_CONTEXT_ACTIVE) == 0) + break; + + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(1); + } + } +} + +/* Note: we can test if free_packet_lists is empty without aquiring + * the packet_list_lock. The interrupt handler only adds to the free + * list, there is no race condition between testing the list non-empty + * and acquiring the lock. + */ + +static struct packet_list *stream_get_free_packet_list(struct stream *s) +{ + struct packet_list *pl; + unsigned long flags; + + if (list_empty(&s->free_packet_lists)) + return NULL; + + spin_lock_irqsave(&s->packet_list_lock, flags); + pl = list_entry(s->free_packet_lists.next, struct packet_list, link); + list_del(&pl->link); + spin_unlock_irqrestore(&s->packet_list_lock, flags); + + return pl; +} + +static void stream_start_dma(struct stream *s, struct packet_list *pl) +{ + u32 syt_cycle, cycle_count, start_cycle; + + cycle_count = reg_read(s->host->ohci, + OHCI1394_IsochronousCycleTimer) >> 12; + syt_cycle = (pl->last_cycle_count - PACKET_LIST_SIZE + 1) & 0x0f; + + /* We program the DMA controller to start transmission at + * least 17 cycles from now - this happens when the lower four + * bits of cycle_count is 0x0f and syt_cycle is 0, in this + * case the start cycle is cycle_count - 15 + 32. */ + start_cycle = (cycle_count & ~0x0f) + 32 + syt_cycle; + if ((start_cycle & 0x1fff) >= 8000) + start_cycle = start_cycle - 8000 + 0x2000; + + ohci1394_start_it_ctx(s->host->ohci, s->iso_tasklet.context, + pl->packets[0].db_bus, 3, + start_cycle & 0x7fff); +} + +static void stream_put_dma_packet_list(struct stream *s, + struct packet_list *pl) +{ + unsigned long flags; + struct packet_list *prev; + + /* Remember the cycle_count used for timestamping the last packet. */ + pl->last_cycle_count = atomic_read(&s->cycle_count2) - 1; + pl->packets[PACKET_LIST_SIZE - 1].db->payload_desc.branch = 0; + + spin_lock_irqsave(&s->packet_list_lock, flags); + list_add_tail(&pl->link, &s->dma_packet_lists); + spin_unlock_irqrestore(&s->packet_list_lock, flags); + + prev = list_entry(pl->link.prev, struct packet_list, link); + if (pl->link.prev != &s->dma_packet_lists) { + struct packet *last = &prev->packets[PACKET_LIST_SIZE - 1]; + last->db->payload_desc.branch = pl->packets[0].db_bus | 3; + last->db->header_desc.skip = pl->packets[0].db_bus | 3; + ohci1394_wake_it_ctx(s->host->ohci, s->iso_tasklet.context); + } + else + stream_start_dma(s, pl); +} + +static void stream_shift_packet_lists(unsigned long l) +{ + struct stream *s = (struct stream *) l; + struct packet_list *pl; + struct packet *last; + int diff; + + if (list_empty(&s->dma_packet_lists)) { + HPSB_ERR("empty dma_packet_lists in %s", __FUNCTION__); + return; + } + + /* Now that we know the list is non-empty, we can get the head + * of the list without locking, because the process context + * only adds to the tail. + */ + pl = list_entry(s->dma_packet_lists.next, struct packet_list, link); + last = &pl->packets[PACKET_LIST_SIZE - 1]; + + /* This is weird... if we stop dma processing in the middle of + * a packet list, the dma context immediately generates an + * interrupt if we enable it again later. This only happens + * when amdtp_release is interrupted while waiting for dma to + * complete, though. Anyway, we detect this by seeing that + * the status of the dma descriptor that we expected an + * interrupt from is still 0. + */ + if (last->db->payload_desc.status == 0) { + HPSB_INFO("weird interrupt..."); + return; + } + + /* If the last descriptor block does not specify a branch + * address, we have a sample underflow. + */ + if (last->db->payload_desc.branch == 0) + HPSB_INFO("FIXME: sample underflow..."); + + /* Here we check when (which cycle) the last packet was sent + * and compare it to what the iso packer was using at the + * time. If there is a mismatch, we adjust the cycle count in + * the iso packer. However, there are still up to + * MAX_PACKET_LISTS packet lists queued with bad time stamps, + * so we disable time stamp monitoring for the next + * MAX_PACKET_LISTS packet lists. + */ + diff = (last->db->payload_desc.status - pl->last_cycle_count) & 0xf; + if (diff > 0 && s->stale_count == 0) { + atomic_add(diff, &s->cycle_count); + atomic_add(diff, &s->cycle_count2); + s->stale_count = MAX_PACKET_LISTS; + } + + if (s->stale_count > 0) + s->stale_count--; + + /* Finally, we move the packet list that was just processed + * back to the free list, and notify any waiters. + */ + spin_lock(&s->packet_list_lock); + list_del(&pl->link); + list_add_tail(&pl->link, &s->free_packet_lists); + spin_unlock(&s->packet_list_lock); + + wake_up_interruptible(&s->packet_list_wait); +} + +static struct packet *stream_current_packet(struct stream *s) +{ + if (s->current_packet_list == NULL && + (s->current_packet_list = stream_get_free_packet_list(s)) == NULL) + return NULL; + + return &s->current_packet_list->packets[s->current_packet]; +} + +static void stream_queue_packet(struct stream *s) +{ + s->current_packet++; + if (s->current_packet == PACKET_LIST_SIZE) { + stream_put_dma_packet_list(s, s->current_packet_list); + s->current_packet_list = NULL; + s->current_packet = 0; + } +} + +/* Integer fractional math. When we transmit a 44k1Hz signal we must + * send 5 41/80 samples per isochronous cycle, as these occur 8000 + * times a second. Of course, we must send an integral number of + * samples in a packet, so we use the integer math to alternate + * between sending 5 and 6 samples per packet. + */ + +static void fraction_init(struct fraction *f, int numerator, int denominator) +{ + f->integer = numerator / denominator; + f->numerator = numerator % denominator; + f->denominator = denominator; +} + +static __inline__ void fraction_add(struct fraction *dst, + struct fraction *src1, + struct fraction *src2) +{ + /* assert: src1->denominator == src2->denominator */ + + int sum, denom; + + /* We use these two local variables to allow gcc to optimize + * the division and the modulo into only one division. */ + + sum = src1->numerator + src2->numerator; + denom = src1->denominator; + dst->integer = src1->integer + src2->integer + sum / denom; + dst->numerator = sum % denom; + dst->denominator = denom; +} + +static __inline__ void fraction_sub_int(struct fraction *dst, + struct fraction *src, int integer) +{ + dst->integer = src->integer - integer; + dst->numerator = src->numerator; + dst->denominator = src->denominator; +} + +static __inline__ int fraction_floor(struct fraction *frac) +{ + return frac->integer; +} + +static __inline__ int fraction_ceil(struct fraction *frac) +{ + return frac->integer + (frac->numerator > 0 ? 1 : 0); +} + +static void packet_initialize(struct packet *p, struct packet *next) +{ + /* Here we initialize the dma descriptor block for + * transferring one iso packet. We use two descriptors per + * packet: an OUTPUT_MORE_IMMMEDIATE descriptor for the + * IEEE1394 iso packet header and an OUTPUT_LAST descriptor + * for the payload. + */ + + p->db->header_desc.control = + DMA_CTL_OUTPUT_MORE | DMA_CTL_IMMEDIATE | 8; + + if (next) { + p->db->payload_desc.control = + DMA_CTL_OUTPUT_LAST | DMA_CTL_BRANCH; + p->db->payload_desc.branch = next->db_bus | 3; + p->db->header_desc.skip = next->db_bus | 3; + } + else { + p->db->payload_desc.control = + DMA_CTL_OUTPUT_LAST | DMA_CTL_BRANCH | + DMA_CTL_UPDATE | DMA_CTL_IRQ; + p->db->payload_desc.branch = 0; + p->db->header_desc.skip = 0; + } + p->db->payload_desc.data_address = p->payload_bus; + p->db->payload_desc.status = 0; +} + +static struct packet_list *packet_list_alloc(struct stream *s) +{ + int i; + struct packet_list *pl; + struct packet *next; + + pl = kmalloc(sizeof *pl, SLAB_KERNEL); + if (pl == NULL) + return NULL; + + for (i = 0; i < PACKET_LIST_SIZE; i++) { + struct packet *p = &pl->packets[i]; + p->db = pci_pool_alloc(s->descriptor_pool, SLAB_KERNEL, + &p->db_bus); + p->payload = pci_pool_alloc(s->packet_pool, SLAB_KERNEL, + &p->payload_bus); + } + + for (i = 0; i < PACKET_LIST_SIZE; i++) { + if (i < PACKET_LIST_SIZE - 1) + next = &pl->packets[i + 1]; + else + next = NULL; + packet_initialize(&pl->packets[i], next); + } + + return pl; +} + +static void packet_list_free(struct packet_list *pl, struct stream *s) +{ + int i; + + for (i = 0; i < PACKET_LIST_SIZE; i++) { + struct packet *p = &pl->packets[i]; + pci_pool_free(s->descriptor_pool, p->db, p->db_bus); + pci_pool_free(s->packet_pool, p->payload, p->payload_bus); + } + kfree(pl); +} + +static struct buffer *buffer_alloc(int size) +{ + struct buffer *b; + + b = kmalloc(sizeof *b + size, SLAB_KERNEL); + if (b == NULL) + return NULL; + b->head = 0; + b->tail = 0; + b->length = 0; + b->size = size; + + return b; +} + +static unsigned char *buffer_get_bytes(struct buffer *buffer, int size) +{ + unsigned char *p; + + if (buffer->head + size > buffer->size) + BUG(); + + p = &buffer->data[buffer->head]; + buffer->head += size; + if (buffer->head == buffer->size) + buffer->head = 0; + buffer->length -= size; + + return p; +} + +static unsigned char *buffer_put_bytes(struct buffer *buffer, + size_t max, size_t *actual) +{ + size_t length; + unsigned char *p; + + p = &buffer->data[buffer->tail]; + length = min(buffer->size - buffer->length, max); + if (buffer->tail + length < buffer->size) { + *actual = length; + buffer->tail += length; + } + else { + *actual = buffer->size - buffer->tail; + buffer->tail = 0; + } + + buffer->length += *actual; + return p; +} + +static u32 get_iec958_header_bits(struct stream *s, int sub_frame, u32 sample) +{ + int csi, parity, shift; + int block_start; + u32 bits; + + switch (s->iec958_frame_count) { + case 1: + csi = s->format == AMDTP_FORMAT_IEC958_AC3; + break; + case 2: + case 9: + csi = 1; + break; + case 24 ... 27: + csi = (s->iec958_rate_code >> (27 - s->iec958_frame_count)) & 0x01; + break; + default: + csi = 0; + break; + } + + block_start = (s->iec958_frame_count == 0 && sub_frame == 0); + + /* The parity bit is the xor of the sample bits and the + * channel status info bit. */ + for (shift = 16, parity = sample ^ csi; shift > 0; shift >>= 1) + parity ^= (parity >> shift); + + bits = (block_start << 5) | /* Block start bit */ + ((sub_frame == 0) << 4) | /* Subframe bit */ + ((parity & 1) << 3) | /* Parity bit */ + (csi << 2); /* Channel status info bit */ + + return bits; +} + +static u32 get_header_bits(struct stream *s, int sub_frame, u32 sample) +{ + switch (s->format) { + case AMDTP_FORMAT_IEC958_PCM: + case AMDTP_FORMAT_IEC958_AC3: + return get_iec958_header_bits(s, sub_frame, sample); + + case AMDTP_FORMAT_RAW: + return 0x40; + + default: + return 0; + } +} + +static void fill_payload_le16(struct stream *s, quadlet_t *data, int nevents) +{ + quadlet_t *event, sample, bits; + unsigned char *p; + int i, j; + + for (i = 0, event = data; i < nevents; i++) { + + for (j = 0; j < s->dimension; j++) { + p = buffer_get_bytes(s->input, 2); + sample = (p[1] << 16) | (p[0] << 8); + bits = get_header_bits(s, j, sample); + event[j] = cpu_to_be32((bits << 24) | sample); + } + + event += s->dimension; + if (++s->iec958_frame_count == 192) + s->iec958_frame_count = 0; + } +} + +static void fill_packet(struct stream *s, struct packet *packet, int nevents) +{ + int syt_index, syt, size; + u32 control; + + size = (nevents * s->dimension + 2) * sizeof(quadlet_t); + + /* Update DMA descriptors */ + packet->db->payload_desc.status = 0; + control = packet->db->payload_desc.control & 0xffff0000; + packet->db->payload_desc.control = control | size; + + /* Fill IEEE1394 headers */ + packet->db->header_desc.header[0] = + (IEEE1394_SPEED_100 << 16) | (0x01 << 14) | + (s->iso_channel << 8) | (TCODE_ISO_DATA << 4); + packet->db->header_desc.header[1] = size << 16; + + /* Calculate synchronization timestamp (syt). First we + * determine syt_index, that is, the index in the packet of + * the sample for which the timestamp is valid. */ + syt_index = (s->syt_interval - s->dbc) & (s->syt_interval - 1); + if (syt_index < nevents) { + syt = ((atomic_read(&s->cycle_count) << 12) | + s->cycle_offset.integer) & 0xffff; + fraction_add(&s->cycle_offset, + &s->cycle_offset, &s->ticks_per_syt_offset); + + /* This next addition should be modulo 8000 (0x1f40), + * but we only use the lower 4 bits of cycle_count, so + * we don't need the modulo. */ + atomic_add(s->cycle_offset.integer / 3072, &s->cycle_count); + s->cycle_offset.integer %= 3072; + } + else + syt = 0xffff; + + atomic_inc(&s->cycle_count2); + + /* Fill cip header */ + packet->payload->eoh0 = 0; + packet->payload->sid = s->host->host->node_id & 0x3f; + packet->payload->dbs = s->dimension; + packet->payload->fn = 0; + packet->payload->qpc = 0; + packet->payload->sph = 0; + packet->payload->reserved = 0; + packet->payload->dbc = s->dbc; + packet->payload->eoh1 = 2; + packet->payload->fmt = FMT_AMDTP; + packet->payload->fdf = s->fdf; + packet->payload->syt = cpu_to_be16(syt); + + switch (s->sample_format) { + case AMDTP_INPUT_LE16: + fill_payload_le16(s, packet->payload->data, nevents); + break; + } + + s->dbc += nevents; +} + +static void stream_flush(struct stream *s) +{ + struct packet *p; + int nevents; + struct fraction next; + + /* The AMDTP specifies two transmission modes: blocking and + * non-blocking. In blocking mode you always transfer + * syt_interval or zero samples, whereas in non-blocking mode + * you send as many samples as you have available at transfer + * time. + * + * The fraction samples_per_cycle specifies the number of + * samples that become available per cycle. We add this to + * the fraction ready_samples, which specifies the number of + * leftover samples from the previous transmission. The sum, + * stored in the fraction next, specifies the number of + * samples available for transmission, and from this we + * determine the number of samples to actually transmit. + */ + + while (1) { + fraction_add(&next, &s->ready_samples, &s->samples_per_cycle); + if (s->mode == AMDTP_MODE_BLOCKING) { + if (fraction_floor(&next) >= s->syt_interval) + nevents = s->syt_interval; + else + nevents = 0; + } + else + nevents = fraction_floor(&next); + + p = stream_current_packet(s); + if (s->input->length < nevents * s->dimension * 2 || p == NULL) + break; + + fill_packet(s, p, nevents); + stream_queue_packet(s); + + /* Now that we have successfully queued the packet for + * transmission, we update the fraction ready_samples. */ + fraction_sub_int(&s->ready_samples, &next, nevents); + } +} + +static int stream_alloc_packet_lists(struct stream *s) +{ + int max_nevents, max_packet_size, i; + + if (s->mode == AMDTP_MODE_BLOCKING) + max_nevents = s->syt_interval; + else + max_nevents = fraction_ceil(&s->samples_per_cycle); + + max_packet_size = max_nevents * s->dimension * 4 + 8; + s->packet_pool = pci_pool_create("packet pool", s->host->ohci->dev, + max_packet_size, 0, 0); + + if (s->packet_pool == NULL) + return -1; + + INIT_LIST_HEAD(&s->free_packet_lists); + INIT_LIST_HEAD(&s->dma_packet_lists); + for (i = 0; i < MAX_PACKET_LISTS; i++) { + struct packet_list *pl = packet_list_alloc(s); + if (pl == NULL) + break; + list_add_tail(&pl->link, &s->free_packet_lists); + } + + return i < MAX_PACKET_LISTS ? -1 : 0; +} + +static void stream_free_packet_lists(struct stream *s) +{ + struct packet_list *packet_l, *packet_l_next; + + if (s->current_packet_list != NULL) + packet_list_free(s->current_packet_list, s); + list_for_each_entry_safe(packet_l, packet_l_next, &s->dma_packet_lists, link) + packet_list_free(packet_l, s); + list_for_each_entry_safe(packet_l, packet_l_next, &s->free_packet_lists, link) + packet_list_free(packet_l, s); + if (s->packet_pool != NULL) + pci_pool_destroy(s->packet_pool); + + s->current_packet_list = NULL; + INIT_LIST_HEAD(&s->free_packet_lists); + INIT_LIST_HEAD(&s->dma_packet_lists); + s->packet_pool = NULL; +} + +static void plug_update(struct cmp_pcr *plug, void *data) +{ + struct stream *s = data; + + HPSB_INFO("plug update: p2p_count=%d, channel=%d", + plug->p2p_count, plug->channel); + s->iso_channel = plug->channel; + if (plug->p2p_count > 0) { + struct packet_list *pl; + + pl = list_entry(s->dma_packet_lists.next, struct packet_list, link); + stream_start_dma(s, pl); + } + else { + ohci1394_stop_it_ctx(s->host->ohci, s->iso_tasklet.context, 0); + } +} + +static int stream_configure(struct stream *s, int cmd, struct amdtp_ioctl *cfg) +{ + const int transfer_delay = 9000; + + if (cfg->format <= AMDTP_FORMAT_IEC958_AC3) + s->format = cfg->format; + else + return -EINVAL; + + switch (cfg->rate) { + case 32000: + s->syt_interval = 8; + s->fdf = FDF_SFC_32KHZ; + s->iec958_rate_code = 0x0c; + break; + case 44100: + s->syt_interval = 8; + s->fdf = FDF_SFC_44K1HZ; + s->iec958_rate_code = 0x00; + break; + case 48000: + s->syt_interval = 8; + s->fdf = FDF_SFC_48KHZ; + s->iec958_rate_code = 0x04; + break; + case 88200: + s->syt_interval = 16; + s->fdf = FDF_SFC_88K2HZ; + s->iec958_rate_code = 0x00; + break; + case 96000: + s->syt_interval = 16; + s->fdf = FDF_SFC_96KHZ; + s->iec958_rate_code = 0x00; + break; + case 176400: + s->syt_interval = 32; + s->fdf = FDF_SFC_176K4HZ; + s->iec958_rate_code = 0x00; + break; + case 192000: + s->syt_interval = 32; + s->fdf = FDF_SFC_192KHZ; + s->iec958_rate_code = 0x00; + break; + + default: + return -EINVAL; + } + + s->rate = cfg->rate; + fraction_init(&s->samples_per_cycle, s->rate, 8000); + fraction_init(&s->ready_samples, 0, 8000); + + /* The ticks_per_syt_offset is initialized to the number of + * ticks between syt_interval events. The number of ticks per + * second is 24.576e6, so the number of ticks between + * syt_interval events is 24.576e6 * syt_interval / rate. + */ + fraction_init(&s->ticks_per_syt_offset, + 24576000 * s->syt_interval, s->rate); + fraction_init(&s->cycle_offset, (transfer_delay % 3072) * s->rate, s->rate); + atomic_set(&s->cycle_count, transfer_delay / 3072); + atomic_set(&s->cycle_count2, 0); + + s->mode = cfg->mode; + s->sample_format = AMDTP_INPUT_LE16; + + /* When using the AM824 raw subformat we can stream signals of + * any dimension. The IEC958 subformat, however, only + * supports 2 channels. + */ + if (s->format == AMDTP_FORMAT_RAW || cfg->dimension == 2) + s->dimension = cfg->dimension; + else + return -EINVAL; + + if (s->opcr != NULL) { + cmp_unregister_opcr(s->host->host, s->opcr); + s->opcr = NULL; + } + + switch(cmd) { + case AMDTP_IOC_PLUG: + s->opcr = cmp_register_opcr(s->host->host, cfg->u.plug, + /*payload*/ 12, plug_update, s); + if (s->opcr == NULL) + return -EINVAL; + s->iso_channel = s->opcr->channel; + break; + + case AMDTP_IOC_CHANNEL: + if (cfg->u.channel >= 0 && cfg->u.channel < 64) + s->iso_channel = cfg->u.channel; + else + return -EINVAL; + break; + } + + /* The ioctl settings were all valid, so we realloc the packet + * lists to make sure the packet size is big enough. + */ + if (s->packet_pool != NULL) + stream_free_packet_lists(s); + + if (stream_alloc_packet_lists(s) < 0) { + stream_free_packet_lists(s); + return -ENOMEM; + } + + return 0; +} + +static struct stream *stream_alloc(struct amdtp_host *host) +{ + struct stream *s; + unsigned long flags; + + s = kmalloc(sizeof(struct stream), SLAB_KERNEL); + if (s == NULL) + return NULL; + + memset(s, 0, sizeof(struct stream)); + s->host = host; + + s->input = buffer_alloc(BUFFER_SIZE); + if (s->input == NULL) { + kfree(s); + return NULL; + } + + s->descriptor_pool = pci_pool_create("descriptor pool", host->ohci->dev, + sizeof(struct descriptor_block), + 16, 0); + + if (s->descriptor_pool == NULL) { + kfree(s->input); + kfree(s); + return NULL; + } + + INIT_LIST_HEAD(&s->free_packet_lists); + INIT_LIST_HEAD(&s->dma_packet_lists); + + init_waitqueue_head(&s->packet_list_wait); + spin_lock_init(&s->packet_list_lock); + + ohci1394_init_iso_tasklet(&s->iso_tasklet, OHCI_ISO_TRANSMIT, + stream_shift_packet_lists, + (unsigned long) s); + + if (ohci1394_register_iso_tasklet(host->ohci, &s->iso_tasklet) < 0) { + pci_pool_destroy(s->descriptor_pool); + kfree(s->input); + kfree(s); + return NULL; + } + + spin_lock_irqsave(&host->stream_list_lock, flags); + list_add_tail(&s->link, &host->stream_list); + spin_unlock_irqrestore(&host->stream_list_lock, flags); + + return s; +} + +static void stream_free(struct stream *s) +{ + unsigned long flags; + + /* Stop the DMA. We wait for the dma packet list to become + * empty and let the dma controller run out of programs. This + * seems to be more reliable than stopping it directly, since + * that sometimes generates an it transmit interrupt if we + * later re-enable the context. + */ + wait_event_interruptible(s->packet_list_wait, + list_empty(&s->dma_packet_lists)); + + ohci1394_stop_it_ctx(s->host->ohci, s->iso_tasklet.context, 1); + ohci1394_unregister_iso_tasklet(s->host->ohci, &s->iso_tasklet); + + if (s->opcr != NULL) + cmp_unregister_opcr(s->host->host, s->opcr); + + spin_lock_irqsave(&s->host->stream_list_lock, flags); + list_del(&s->link); + spin_unlock_irqrestore(&s->host->stream_list_lock, flags); + + kfree(s->input); + + stream_free_packet_lists(s); + pci_pool_destroy(s->descriptor_pool); + + kfree(s); +} + +/* File operations */ + +static ssize_t amdtp_write(struct file *file, const char __user *buffer, size_t count, + loff_t *offset_is_ignored) +{ + struct stream *s = file->private_data; + unsigned char *p; + int i; + size_t length; + + if (s->packet_pool == NULL) + return -EBADFD; + + /* Fill the circular buffer from the input buffer and call the + * iso packer when the buffer is full. The iso packer may + * leave bytes in the buffer for two reasons: either the + * remaining bytes wasn't enough to build a new packet, or + * there were no free packet lists. In the first case we + * re-fill the buffer and call the iso packer again or return + * if we used all the data from userspace. In the second + * case, the wait_event_interruptible will block until the irq + * handler frees a packet list. + */ + + for (i = 0; i < count; i += length) { + p = buffer_put_bytes(s->input, count - i, &length); + if (copy_from_user(p, buffer + i, length)) + return -EFAULT; + if (s->input->length < s->input->size) + continue; + + stream_flush(s); + + if (s->current_packet_list != NULL) + continue; + + if (file->f_flags & O_NONBLOCK) + return i + length > 0 ? i + length : -EAGAIN; + + if (wait_event_interruptible(s->packet_list_wait, + !list_empty(&s->free_packet_lists))) + return -EINTR; + } + + return count; +} + +static long amdtp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct stream *s = file->private_data; + struct amdtp_ioctl cfg; + int err; + lock_kernel(); + switch(cmd) + { + case AMDTP_IOC_PLUG: + case AMDTP_IOC_CHANNEL: + if (copy_from_user(&cfg, (struct amdtp_ioctl __user *) arg, sizeof cfg)) + err = -EFAULT; + else + err = stream_configure(s, cmd, &cfg); + break; + + default: + err = -EINVAL; + break; + } + unlock_kernel(); + return err; +} + +static unsigned int amdtp_poll(struct file *file, poll_table *pt) +{ + struct stream *s = file->private_data; + + poll_wait(file, &s->packet_list_wait, pt); + + if (!list_empty(&s->free_packet_lists)) + return POLLOUT | POLLWRNORM; + else + return 0; +} + +static int amdtp_open(struct inode *inode, struct file *file) +{ + struct amdtp_host *host; + int i = ieee1394_file_to_instance(file); + + host = hpsb_get_hostinfo_bykey(&amdtp_highlevel, i); + if (host == NULL) + return -ENODEV; + + file->private_data = stream_alloc(host); + if (file->private_data == NULL) + return -ENOMEM; + + return 0; +} + +static int amdtp_release(struct inode *inode, struct file *file) +{ + struct stream *s = file->private_data; + + stream_free(s); + + return 0; +} + +static struct cdev amdtp_cdev; +static struct file_operations amdtp_fops = +{ + .owner = THIS_MODULE, + .write = amdtp_write, + .poll = amdtp_poll, + .unlocked_ioctl = amdtp_ioctl, + .compat_ioctl = amdtp_ioctl, /* All amdtp ioctls are compatible */ + .open = amdtp_open, + .release = amdtp_release +}; + +/* IEEE1394 Subsystem functions */ + +static void amdtp_add_host(struct hpsb_host *host) +{ + struct amdtp_host *ah; + int minor; + + if (strcmp(host->driver->name, OHCI1394_DRIVER_NAME) != 0) + return; + + ah = hpsb_create_hostinfo(&amdtp_highlevel, host, sizeof(*ah)); + if (!ah) { + HPSB_ERR("amdtp: Unable able to alloc hostinfo"); + return; + } + + ah->host = host; + ah->ohci = host->hostdata; + + hpsb_set_hostinfo_key(&amdtp_highlevel, host, ah->host->id); + + minor = IEEE1394_MINOR_BLOCK_AMDTP * 16 + ah->host->id; + + INIT_LIST_HEAD(&ah->stream_list); + spin_lock_init(&ah->stream_list_lock); + + devfs_mk_cdev(MKDEV(IEEE1394_MAJOR, minor), + S_IFCHR|S_IRUSR|S_IWUSR, "amdtp/%d", ah->host->id); +} + +static void amdtp_remove_host(struct hpsb_host *host) +{ + struct amdtp_host *ah = hpsb_get_hostinfo(&amdtp_highlevel, host); + + if (ah) + devfs_remove("amdtp/%d", ah->host->id); + + return; +} + +static struct hpsb_highlevel amdtp_highlevel = { + .name = "amdtp", + .add_host = amdtp_add_host, + .remove_host = amdtp_remove_host, +}; + +/* Module interface */ + +MODULE_AUTHOR("Kristian Hogsberg <hogsberg@users.sf.net>"); +MODULE_DESCRIPTION("Driver for Audio & Music Data Transmission Protocol " + "on OHCI boards."); +MODULE_SUPPORTED_DEVICE("amdtp"); +MODULE_LICENSE("GPL"); + +static int __init amdtp_init_module (void) +{ + cdev_init(&amdtp_cdev, &amdtp_fops); + amdtp_cdev.owner = THIS_MODULE; + kobject_set_name(&amdtp_cdev.kobj, "amdtp"); + if (cdev_add(&amdtp_cdev, IEEE1394_AMDTP_DEV, 16)) { + HPSB_ERR("amdtp: unable to add char device"); + return -EIO; + } + + devfs_mk_dir("amdtp"); + + hpsb_register_highlevel(&amdtp_highlevel); + + HPSB_INFO("Loaded AMDTP driver"); + + return 0; +} + +static void __exit amdtp_exit_module (void) +{ + hpsb_unregister_highlevel(&amdtp_highlevel); + devfs_remove("amdtp"); + cdev_del(&amdtp_cdev); + + HPSB_INFO("Unloaded AMDTP driver"); +} + +module_init(amdtp_init_module); +module_exit(amdtp_exit_module); +MODULE_ALIAS_CHARDEV(IEEE1394_MAJOR, IEEE1394_MINOR_BLOCK_AMDTP * 16); diff --git a/drivers/ieee1394/amdtp.h b/drivers/ieee1394/amdtp.h new file mode 100644 index 0000000..531f28e --- /dev/null +++ b/drivers/ieee1394/amdtp.h @@ -0,0 +1,84 @@ +/* -*- c-basic-offset: 8 -*- */ + +#ifndef __AMDTP_H +#define __AMDTP_H + +#include <asm/types.h> +#include "ieee1394-ioctl.h" + +/* The userspace interface for the Audio & Music Data Transmission + * Protocol driver is really simple. First, open /dev/amdtp, use the + * ioctl to configure format, rate, dimension and either plug or + * channel, then start writing samples. + * + * The formats supported by the driver are listed below. + * AMDTP_FORMAT_RAW corresponds to the AM824 raw format, which can + * carry any number of channels, so use this if you're streaming + * multichannel audio. The AMDTP_FORMAT_IEC958_PCM corresponds to the + * AM824 IEC958 encapsulation without the IEC958 data bit set, using + * AMDTP_FORMAT_IEC958_AC3 will transmit the samples with the data bit + * set, suitable for transmitting compressed AC-3 audio. + * + * The rate field specifies the transmission rate; supported values + * are 32000, 44100, 48000, 88200, 96000, 176400 and 192000. + * + * The dimension field specifies the dimension of the signal, that is, + * the number of audio channels. Only AMDTP_FORMAT_RAW supports + * settings greater than 2. + * + * The mode field specifies which transmission mode to use. The AMDTP + * specifies two different transmission modes: blocking and + * non-blocking. The blocking transmission mode always send a fixed + * number of samples, typically 8, 16 or 32. To exactly match the + * transmission rate, the driver alternates between sending empty and + * non-empty packets. In non-blocking mode, the driver transmits as + * small packets as possible. For example, for a transmission rate of + * 44100Hz, the driver should send 5 41/80 samples in every cycle, but + * this is not possible so instead the driver alternates between + * sending 5 and 6 samples. + * + * The last thing to specify is either the isochronous channel to use + * or the output plug to connect to. If you know what channel the + * destination device will listen on, you can specify the channel + * directly and use the AMDTP_IOC_CHANNEL ioctl. However, if the + * destination device chooses the channel and uses the IEC61883-1 plug + * mechanism, you can specify an output plug to connect to. The + * driver will pick up the channel number from the plug once the + * destination device locks the output plug control register. In this + * case set the plug field and use the AMDTP_IOC_PLUG ioctl. + * + * Having configured the interface, the driver now accepts writes of + * regular 16 bit signed little endian samples, with the channels + * interleaved. For example, 4 channels would look like: + * + * | sample 0 | sample 1 ... + * | ch. 0 | ch. 1 | ch. 2 | ch. 3 | ch. 0 | ... + * | lsb | msb | lsb | msb | lsb | msb | lsb | msb | lsb | msb | ... + * + */ + +enum { + AMDTP_FORMAT_RAW, + AMDTP_FORMAT_IEC958_PCM, + AMDTP_FORMAT_IEC958_AC3 +}; + +enum { + AMDTP_MODE_BLOCKING, + AMDTP_MODE_NON_BLOCKING, +}; + +enum { + AMDTP_INPUT_LE16, + AMDTP_INPUT_BE16, +}; + +struct amdtp_ioctl { + __u32 format; + __u32 rate; + __u32 dimension; + __u32 mode; + union { __u32 channel; __u32 plug; } u; +}; + +#endif /* __AMDTP_H */ diff --git a/drivers/ieee1394/cmp.c b/drivers/ieee1394/cmp.c new file mode 100644 index 0000000..69aed26 --- /dev/null +++ b/drivers/ieee1394/cmp.c @@ -0,0 +1,311 @@ +/* -*- c-basic-offset: 8 -*- + * + * cmp.c - Connection Management Procedures + * Copyright (C) 2001 Kristian Høgsberg + * + * 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. + */ + +/* TODO + * ---- + * + * - Implement IEC61883-1 output plugs and connection management. + * This should probably be part of the general subsystem, as it could + * be shared with dv1394. + * + * - Add IEC61883 unit directory when loading this module. This + * requires a run-time changeable config rom. + */ + +#include <linux/module.h> +#include <linux/list.h> +#include <linux/sched.h> +#include <linux/types.h> +#include <linux/wait.h> +#include <linux/interrupt.h> + +#include "hosts.h" +#include "highlevel.h" +#include "ieee1394.h" +#include "ieee1394_core.h" +#include "cmp.h" + +struct plug { + union { + struct cmp_pcr pcr; + quadlet_t quadlet; + } u; + void (*update)(struct cmp_pcr *plug, void *data); + void *data; +}; + +struct cmp_host { + struct hpsb_host *host; + + union { + struct cmp_mpr ompr; + quadlet_t ompr_quadlet; + } u; + struct plug opcr[2]; + + union { + struct cmp_mpr impr; + quadlet_t impr_quadlet; + } v; + struct plug ipcr[2]; +}; + +enum { + CMP_P2P_CONNECTION, + CMP_BC_CONNECTION +}; + +#define CSR_PCR_MAP 0x900 +#define CSR_PCR_MAP_END 0x9fc + +static struct hpsb_highlevel cmp_highlevel; + +static void cmp_add_host(struct hpsb_host *host); +static void cmp_host_reset(struct hpsb_host *host); +static int pcr_read(struct hpsb_host *host, int nodeid, quadlet_t *buf, + u64 addr, size_t length, u16 flags); +static int pcr_lock(struct hpsb_host *host, int nodeid, quadlet_t *store, + u64 addr, quadlet_t data, quadlet_t arg, int extcode, u16 flags); + +static struct hpsb_highlevel cmp_highlevel = { + .name = "cmp", + .add_host = cmp_add_host, + .host_reset = cmp_host_reset, +}; + +static struct hpsb_address_ops pcr_ops = { + .read = pcr_read, + .lock = pcr_lock, +}; + + +struct cmp_pcr * +cmp_register_opcr(struct hpsb_host *host, int opcr_number, int payload, + void (*update)(struct cmp_pcr *pcr, void *data), + void *data) +{ + struct cmp_host *ch; + struct plug *plug; + + ch = hpsb_get_hostinfo(&cmp_highlevel, host); + + if (opcr_number >= ch->u.ompr.nplugs || + ch->opcr[opcr_number].update != NULL) + return NULL; + + plug = &ch->opcr[opcr_number]; + plug->u.pcr.online = 1; + plug->u.pcr.bcast_count = 0; + plug->u.pcr.p2p_count = 0; + plug->u.pcr.overhead = 0; + plug->u.pcr.payload = payload; + plug->update = update; + plug->data = data; + + return &plug->u.pcr; +} + +void cmp_unregister_opcr(struct hpsb_host *host, struct cmp_pcr *opcr) +{ + struct cmp_host *ch; + struct plug *plug; + + ch = hpsb_get_hostinfo(&cmp_highlevel, host); + plug = (struct plug *)opcr; + if (plug - ch->opcr >= ch->u.ompr.nplugs) BUG(); + + plug->u.pcr.online = 0; + plug->update = NULL; +} + +static void reset_plugs(struct cmp_host *ch) +{ + int i; + + ch->u.ompr.non_persistent_ext = 0xff; + for (i = 0; i < ch->u.ompr.nplugs; i++) { + ch->opcr[i].u.pcr.bcast_count = 0; + ch->opcr[i].u.pcr.p2p_count = 0; + ch->opcr[i].u.pcr.overhead = 0; + } +} + +static void cmp_add_host(struct hpsb_host *host) +{ + struct cmp_host *ch = hpsb_create_hostinfo(&cmp_highlevel, host, sizeof (*ch)); + + if (ch == NULL) { + HPSB_ERR("Failed to allocate cmp_host"); + return; + } + + hpsb_register_addrspace(&cmp_highlevel, host, &pcr_ops, + CSR_REGISTER_BASE + CSR_PCR_MAP, + CSR_REGISTER_BASE + CSR_PCR_MAP_END); + + ch->host = host; + ch->u.ompr.rate = IEEE1394_SPEED_100; + ch->u.ompr.bcast_channel_base = 63; + ch->u.ompr.nplugs = 2; + + reset_plugs(ch); +} + +static void cmp_host_reset(struct hpsb_host *host) +{ + struct cmp_host *ch; + + ch = hpsb_get_hostinfo(&cmp_highlevel, host); + if (ch == NULL) { + HPSB_ERR("cmp: Tried to reset unknown host"); + return; + } + + reset_plugs(ch); +} + +static int pcr_read(struct hpsb_host *host, int nodeid, quadlet_t *buf, + u64 addr, size_t length, u16 flags) +{ + int csraddr = addr - CSR_REGISTER_BASE; + int plug; + struct cmp_host *ch; + + if (length != 4) + return RCODE_TYPE_ERROR; + + ch = hpsb_get_hostinfo(&cmp_highlevel, host); + if (csraddr == 0x900) { + *buf = cpu_to_be32(ch->u.ompr_quadlet); + return RCODE_COMPLETE; + } + else if (csraddr < 0x904 + ch->u.ompr.nplugs * 4) { + plug = (csraddr - 0x904) / 4; + *buf = cpu_to_be32(ch->opcr[plug].u.quadlet); + return RCODE_COMPLETE; + } + else if (csraddr < 0x980) { + return RCODE_ADDRESS_ERROR; + } + else if (csraddr == 0x980) { + *buf = cpu_to_be32(ch->v.impr_quadlet); + return RCODE_COMPLETE; + } + else if (csraddr < 0x984 + ch->v.impr.nplugs * 4) { + plug = (csraddr - 0x984) / 4; + *buf = cpu_to_be32(ch->ipcr[plug].u.quadlet); + return RCODE_COMPLETE; + } + else + return RCODE_ADDRESS_ERROR; +} + +static int pcr_lock(struct hpsb_host *host, int nodeid, quadlet_t *store, + u64 addr, quadlet_t data, quadlet_t arg, int extcode, u16 flags) +{ + int csraddr = addr - CSR_REGISTER_BASE; + int plug; + struct cmp_host *ch; + + ch = hpsb_get_hostinfo(&cmp_highlevel, host); + + if (extcode != EXTCODE_COMPARE_SWAP) + return RCODE_TYPE_ERROR; + + if (csraddr == 0x900) { + /* FIXME: Ignore writes to bits 30-31 and 0-7 */ + *store = cpu_to_be32(ch->u.ompr_quadlet); + if (arg == cpu_to_be32(ch->u.ompr_quadlet)) + ch->u.ompr_quadlet = be32_to_cpu(data); + + return RCODE_COMPLETE; + } + if (csraddr < 0x904 + ch->u.ompr.nplugs * 4) { + plug = (csraddr - 0x904) / 4; + *store = cpu_to_be32(ch->opcr[plug].u.quadlet); + + if (arg == *store) + ch->opcr[plug].u.quadlet = be32_to_cpu(data); + + if (be32_to_cpu(*store) != ch->opcr[plug].u.quadlet && + ch->opcr[plug].update != NULL) + ch->opcr[plug].update(&ch->opcr[plug].u.pcr, + ch->opcr[plug].data); + + return RCODE_COMPLETE; + } + else if (csraddr < 0x980) { + return RCODE_ADDRESS_ERROR; + } + else if (csraddr == 0x980) { + /* FIXME: Ignore writes to bits 24-31 and 0-7 */ + *store = cpu_to_be32(ch->u.ompr_quadlet); + if (arg == cpu_to_be32(ch->u.ompr_quadlet)) + ch->u.ompr_quadlet = be32_to_cpu(data); + + return RCODE_COMPLETE; + } + else if (csraddr < 0x984 + ch->v.impr.nplugs * 4) { + plug = (csraddr - 0x984) / 4; + *store = cpu_to_be32(ch->ipcr[plug].u.quadlet); + + if (arg == *store) + ch->ipcr[plug].u.quadlet = be32_to_cpu(data); + + if (be32_to_cpu(*store) != ch->ipcr[plug].u.quadlet && + ch->ipcr[plug].update != NULL) + ch->ipcr[plug].update(&ch->ipcr[plug].u.pcr, + ch->ipcr[plug].data); + + return RCODE_COMPLETE; + } + else + return RCODE_ADDRESS_ERROR; +} + + +/* Module interface */ + +MODULE_AUTHOR("Kristian Hogsberg <hogsberg@users.sf.net>"); +MODULE_DESCRIPTION("Connection Management Procedures (CMP)"); +MODULE_SUPPORTED_DEVICE("cmp"); +MODULE_LICENSE("GPL"); + +EXPORT_SYMBOL(cmp_register_opcr); +EXPORT_SYMBOL(cmp_unregister_opcr); + +static int __init cmp_init_module (void) +{ + hpsb_register_highlevel (&cmp_highlevel); + + HPSB_INFO("Loaded CMP driver"); + + return 0; +} + +static void __exit cmp_exit_module (void) +{ + hpsb_unregister_highlevel(&cmp_highlevel); + + HPSB_INFO("Unloaded CMP driver"); +} + +module_init(cmp_init_module); +module_exit(cmp_exit_module); diff --git a/drivers/ieee1394/cmp.h b/drivers/ieee1394/cmp.h new file mode 100644 index 0000000..f9288bf --- /dev/null +++ b/drivers/ieee1394/cmp.h @@ -0,0 +1,31 @@ +#ifndef __CMP_H +#define __CMP_H + +struct cmp_mpr { + u32 nplugs:5; + u32 reserved:3; + u32 persistent_ext:8; + u32 non_persistent_ext:8; + u32 bcast_channel_base:6; + u32 rate:2; +} __attribute__((packed)); + +struct cmp_pcr { + u32 payload:10; + u32 overhead:4; + u32 speed:2; + u32 channel:6; + u32 reserved:2; + u32 p2p_count:6; + u32 bcast_count:1; + u32 online:1; +} __attribute__((packed)); + +struct cmp_pcr *cmp_register_opcr(struct hpsb_host *host, int plug, + int payload, + void (*update)(struct cmp_pcr *plug, + void *data), + void *data); +void cmp_unregister_opcr(struct hpsb_host *host, struct cmp_pcr *plug); + +#endif /* __CMP_H */ diff --git a/drivers/ieee1394/config_roms.c b/drivers/ieee1394/config_roms.c new file mode 100644 index 0000000..1017fd7 --- /dev/null +++ b/drivers/ieee1394/config_roms.c @@ -0,0 +1,236 @@ +/* + * 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/config.h> +#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); + + /* Add entry to specified host */ + int (*add)(struct hpsb_host *host); + + /* Remove entry from specified host */ + void (*remove)(struct hpsb_host *host); + + /* Cleanup called at module exit */ + void (*cleanup)(void); + + /* The flag added to host->config_roms */ + unsigned int flag; +}; + + +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; + } + + ret = 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_CONFIG_ROM_IP1394 +#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; + + if (csr1212_associate_keyval(spec_id, spec_desc) == CSR1212_SUCCESS && + csr1212_associate_keyval(ver, ver_desc) == CSR1212_SUCCESS && + 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; + } +} + +static int 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; + + return 0; +} + +static void config_rom_ip1394_remove(struct hpsb_host *host) +{ + csr1212_detach_keyval_from_directory(host->csr.rom->root_kv, ip1394_ud); +} + +static struct hpsb_config_rom_entry ip1394_entry = { + .name = "ip1394", + .init = config_rom_ip1394_init, + .add = config_rom_ip1394_add, + .remove = config_rom_ip1394_remove, + .cleanup = config_rom_ip1394_cleanup, + .flag = HPSB_CONFIG_ROM_ENTRY_IP1394, +}; +#endif /* CONFIG_IEEE1394_CONFIG_ROM_IP1394 */ + + +static struct hpsb_config_rom_entry *const config_rom_entries[] = { +#ifdef CONFIG_IEEE1394_CONFIG_ROM_IP1394 + &ip1394_entry, +#endif + NULL, +}; + + +int hpsb_init_config_roms(void) +{ + int i, error = 0; + + for (i = 0; config_rom_entries[i]; i++) { + if (!config_rom_entries[i]->init) + continue; + + if (config_rom_entries[i]->init()) { + HPSB_ERR("Failed to initialize config rom entry `%s'", + config_rom_entries[i]->name); + error = -1; + } else + HPSB_DEBUG("Initialized config rom entry `%s'", + config_rom_entries[i]->name); + } + + return error; +} + +void hpsb_cleanup_config_roms(void) +{ + int i; + + for (i = 0; config_rom_entries[i]; i++) { + if (config_rom_entries[i]->cleanup) + config_rom_entries[i]->cleanup(); + } +} + +int hpsb_add_extra_config_roms(struct hpsb_host *host) +{ + int i, error = 0; + + for (i = 0; config_rom_entries[i]; i++) { + if (config_rom_entries[i]->add(host)) { + HPSB_ERR("fw-host%d: Failed to attach config rom entry `%s'", + host->id, config_rom_entries[i]->name); + error = -1; + } else { + host->config_roms |= config_rom_entries[i]->flag; + host->update_config_rom = 1; + } + } + + return error; +} + +void hpsb_remove_extra_config_roms(struct hpsb_host *host) +{ + int i; + + for (i = 0; config_rom_entries[i]; i++) { + if (!(host->config_roms & config_rom_entries[i]->flag)) + continue; + + config_rom_entries[i]->remove(host); + + host->config_roms &= ~config_rom_entries[i]->flag; + host->update_config_rom = 1; + } +} diff --git a/drivers/ieee1394/config_roms.h b/drivers/ieee1394/config_roms.h new file mode 100644 index 0000000..0a70544 --- /dev/null +++ b/drivers/ieee1394/config_roms.h @@ -0,0 +1,27 @@ +#ifndef _IEEE1394_CONFIG_ROMS_H +#define _IEEE1394_CONFIG_ROMS_H + +#include "ieee1394_types.h" +#include "hosts.h" + +/* The default host entry. This must succeed. */ +int hpsb_default_host_entry(struct hpsb_host *host); + +/* Initialize all config roms */ +int hpsb_init_config_roms(void); + +/* Cleanup all config roms */ +void hpsb_cleanup_config_roms(void); + +/* Add extra config roms to specified host */ +int hpsb_add_extra_config_roms(struct hpsb_host *host); + +/* Remove extra config roms from specified host */ +void hpsb_remove_extra_config_roms(struct hpsb_host *host); + + +/* 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 + +#endif /* _IEEE1394_CONFIG_ROMS_H */ diff --git a/drivers/ieee1394/csr.c b/drivers/ieee1394/csr.c new file mode 100644 index 0000000..1b98684 --- /dev/null +++ b/drivers/ieee1394/csr.c @@ -0,0 +1,857 @@ +/* + * 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/string.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/param.h> +#include <linux/spinlock.h> + +#include "csr1212.h" +#include "ieee1394_types.h" +#include "hosts.h" +#include "ieee1394.h" +#include "highlevel.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; + } + + host->csr.topology_map[1] = + cpu_to_be32(be32_to_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)); + + host->csr.speed_map[1] = + cpu_to_be32(be32_to_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 == fraction units of 1/8000 of a second, as per 1394 (bits 19:31) + * + * Convert to units and then to HZ, for comparison to jiffies. + * + * By default this will end up being 800 units, or 100ms (125usec per + * unit). + * + * NOTE: The spec says 1/8000, but also says we can compute based on 1/8192 + * like CSR specifies. Should make our math less complex. + */ +static inline void calculate_expire(struct csr_control *csr) +{ + unsigned long units; + + /* Take the seconds, and convert to units */ + units = (unsigned long)(csr->split_timeout_hi & 0x07) << 13; + + /* Add in the fractional units */ + units += (unsigned long)(csr->split_timeout_lo >> 19); + + /* Convert to jiffies */ + csr->expire = (unsigned long)(units * HZ) >> 13UL; + + /* Just to keep from rounding low */ + csr->expire++; + + 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, ®_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((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..ea9aa4f --- /dev/null +++ b/drivers/ieee1394/csr.h @@ -0,0 +1,96 @@ + +#ifndef _IEEE1394_CSR_H +#define _IEEE1394_CSR_H + +#ifdef CONFIG_PREEMPT +#include <linux/sched.h> +#endif + +#include "csr1212.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 Inforamation 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); + +#endif /* _IEEE1394_CSR_H */ diff --git a/drivers/ieee1394/csr1212.c b/drivers/ieee1394/csr1212.c new file mode 100644 index 0000000..7c4330e --- /dev/null +++ b/drivers/ieee1394/csr1212.c @@ -0,0 +1,1612 @@ +/* + * 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. + * - Convenience functions for reading a block of data from a given offset. + */ + +#ifndef __KERNEL__ +#include <string.h> +#endif + +#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 u_int8_t csr1212_key_id_type_map[0x30] = { + 0, /* Reserved */ + __D | __L, /* Descriptor */ + __I | __D | __L, /* Bus_Dependent_Info */ + __I | __D | __L, /* Vendor */ + __I, /* Hardware_Version */ + 0, 0, /* Reserved */ + __D | __L, /* Module */ + 0, 0, 0, 0, /* 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(u_int32_t)) +#define bytes_to_quads(_b) (((_b) + sizeof(u_int32_t) - 1) / sizeof(u_int32_t)) + +static inline 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 u_int16_t csr1212_crc16(const u_int32_t *buffer, size_t length) +{ + int shift; + u_int32_t data; + u_int16_t sum, crc = 0; + + for (; length; length--) { + data = CSR1212_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 CSR1212_CPU_TO_BE16(crc); +} + +#if 0 +/* Microsoft computes the CRC with the bytes in reverse order. Therefore we + * have a special version of the CRC algorithm to account for their buggy + * software. */ +static u_int16_t csr1212_msft_crc16(const u_int32_t *buffer, size_t length) +{ + int shift; + u_int32_t data; + u_int16_t sum, crc = 0; + + for (; length; length--) { + data = CSR1212_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 CSR1212_CPU_TO_BE16(crc); +} +#endif + +static inline 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 inline struct csr1212_keyval *csr1212_find_keyval_offset(struct csr1212_keyval *kv_list, + u_int32_t 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 u_int32_t *bus_info_data, int max_rom) +{ + static const int mr_map[] = { 4, 64, 1024, 0 }; + + 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(u_int8_t type, u_int8_t 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; + + kv->key.type = type; + kv->key.id = key; + + kv->associate = NULL; + kv->refcnt = 1; + + kv->next = NULL; + kv->prev = NULL; + kv->offset = 0; + kv->valid = 0; + return kv; +} + +struct csr1212_keyval *csr1212_new_immediate(u_int8_t key, u_int32_t value) +{ + struct csr1212_keyval *kv = csr1212_new_keyval(CSR1212_KV_TYPE_IMMEDIATE, key); + + if (!kv) + return NULL; + + kv->value.immediate = value; + kv->valid = 1; + return kv; +} + +struct csr1212_keyval *csr1212_new_leaf(u_int8_t key, const void *data, size_t data_len) +{ + struct csr1212_keyval *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; +} + +struct csr1212_keyval *csr1212_new_csr_offset(u_int8_t key, u_int32_t csr_offset) +{ + struct csr1212_keyval *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(u_int8_t key) +{ + struct csr1212_keyval *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; +} + +int csr1212_associate_keyval(struct csr1212_keyval *kv, + struct csr1212_keyval *associate) +{ + if (!kv || !associate) + return CSR1212_EINVAL; + + if (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)) + return CSR1212_EINVAL; + + if (kv->key.id == CSR1212_KV_ID_EXTENDED_KEY_SPECIFIER_ID && + associate->key.id != CSR1212_KV_ID_EXTENDED_KEY) + return CSR1212_EINVAL; + + if (kv->key.id == CSR1212_KV_ID_EXTENDED_KEY && + associate->key.id != CSR1212_KV_ID_EXTENDED_DATA) + return CSR1212_EINVAL; + + if (associate->key.id == CSR1212_KV_ID_EXTENDED_KEY && + kv->key.id != CSR1212_KV_ID_EXTENDED_KEY_SPECIFIER_ID) + return CSR1212_EINVAL; + + if (associate->key.id == CSR1212_KV_ID_EXTENDED_DATA && + kv->key.id != CSR1212_KV_ID_EXTENDED_KEY) + return CSR1212_EINVAL; + + if (kv->associate) + csr1212_release_keyval(kv->associate); + + associate->refcnt++; + kv->associate = associate; + + return CSR1212_SUCCESS; +} + +int csr1212_attach_keyval_to_directory(struct csr1212_keyval *dir, + struct csr1212_keyval *kv) +{ + struct csr1212_dentry *dentry; + + if (!kv || !dir || dir->key.type != CSR1212_KV_TYPE_DIRECTORY) + return CSR1212_EINVAL; + + dentry = CSR1212_MALLOC(sizeof(*dentry)); + if (!dentry) + return CSR1212_ENOMEM; + + dentry->kv = kv; + + kv->refcnt++; + + 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; +} + +struct csr1212_keyval *csr1212_new_extended_immediate(u_int32_t spec, u_int32_t key, + u_int32_t value) +{ + struct csr1212_keyval *kvs, *kvk, *kvv; + + kvs = csr1212_new_immediate(CSR1212_KV_ID_EXTENDED_KEY_SPECIFIER_ID, spec); + kvk = csr1212_new_immediate(CSR1212_KV_ID_EXTENDED_KEY, key); + kvv = csr1212_new_immediate(CSR1212_KV_ID_EXTENDED_DATA, value); + + if (!kvs || !kvk || !kvv) { + if (kvs) + free_keyval(kvs); + if (kvk) + free_keyval(kvk); + if (kvv) + free_keyval(kvv); + return NULL; + } + + /* Don't keep a local reference to the extended key or value. */ + kvk->refcnt = 0; + kvv->refcnt = 0; + + csr1212_associate_keyval(kvk, kvv); + csr1212_associate_keyval(kvs, kvk); + + return kvs; +} + +struct csr1212_keyval *csr1212_new_extended_leaf(u_int32_t spec, u_int32_t key, + const void *data, size_t data_len) +{ + struct csr1212_keyval *kvs, *kvk, *kvv; + + kvs = csr1212_new_immediate(CSR1212_KV_ID_EXTENDED_KEY_SPECIFIER_ID, spec); + kvk = csr1212_new_immediate(CSR1212_KV_ID_EXTENDED_KEY, key); + kvv = csr1212_new_leaf(CSR1212_KV_ID_EXTENDED_DATA, data, data_len); + + if (!kvs || !kvk || !kvv) { + if (kvs) + free_keyval(kvs); + if (kvk) + free_keyval(kvk); + if (kvv) + free_keyval(kvv); + return NULL; + } + + /* Don't keep a local reference to the extended key or value. */ + kvk->refcnt = 0; + kvv->refcnt = 0; + + csr1212_associate_keyval(kvk, kvv); + csr1212_associate_keyval(kvs, kvk); + + return kvs; +} + +struct csr1212_keyval *csr1212_new_descriptor_leaf(u_int8_t dtype, u_int32_t 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; +} + + +struct csr1212_keyval *csr1212_new_textual_descriptor_leaf(u_int8_t cwidth, + u_int16_t cset, + u_int16_t language, + const void *data, + size_t data_len) +{ + struct csr1212_keyval *kv; + char *lstr; + + kv = csr1212_new_descriptor_leaf(0, 0, NULL, data_len + + CSR1212_TEXTUAL_DESCRIPTOR_LEAF_OVERHEAD); + if (!kv) + return NULL; + + CSR1212_TEXTUAL_DESCRIPTOR_LEAF_SET_WIDTH(kv, cwidth); + CSR1212_TEXTUAL_DESCRIPTOR_LEAF_SET_CHAR_SET(kv, cset); + CSR1212_TEXTUAL_DESCRIPTOR_LEAF_SET_LANGUAGE(kv, language); + + lstr = (char*)CSR1212_TEXTUAL_DESCRIPTOR_LEAF_DATA(kv); + + /* make sure last quadlet is zeroed out */ + *((u_int32_t*)&(lstr[(data_len - 1) & ~0x3])) = 0; + + /* don't copy the NUL terminator */ + memcpy(lstr, data, data_len); + + return kv; +} + +static int csr1212_check_minimal_ascii(const char *s) +{ + static const char minimal_ascii_table[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, + 0x00, 0x00, 0x0a, 0x00, 0x0C, 0x0D, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x20, 0x21, 0x22, 0x00, 0x00, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x5a, 0x00, 0x00, 0x00, 0x00, 0x5f, + 0x00, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7a, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + for (; *s; s++) { + if (minimal_ascii_table[*s & 0x7F] != *s) + return -1; /* failed */ + } + /* String conforms to minimal-ascii, as specified by IEEE 1212, + * par. 7.4 */ + return 0; +} + +struct csr1212_keyval *csr1212_new_string_descriptor_leaf(const char *s) +{ + /* Check if string conform to minimal_ascii format */ + if (csr1212_check_minimal_ascii(s)) + return NULL; + + /* IEEE 1212, par. 7.5.4.1 Textual descriptors (minimal ASCII) */ + return csr1212_new_textual_descriptor_leaf(0, 0, 0, s, strlen(s)); +} + +struct csr1212_keyval *csr1212_new_icon_descriptor_leaf(u_int32_t version, + u_int8_t palette_depth, + u_int8_t color_space, + u_int16_t language, + u_int16_t hscan, + u_int16_t vscan, + u_int32_t *palette, + u_int32_t *pixels) +{ + static const int pd[4] = { 0, 4, 16, 256 }; + static const int cs[16] = { 4, 2 }; + struct csr1212_keyval *kv; + int palette_size = pd[palette_depth] * cs[color_space]; + int pixel_size = (hscan * vscan + 3) & ~0x3; + + if ((palette_depth && !palette) || !pixels) + return NULL; + + kv = csr1212_new_descriptor_leaf(1, 0, NULL, + palette_size + pixel_size + + CSR1212_ICON_DESCRIPTOR_LEAF_OVERHEAD); + if (!kv) + return NULL; + + CSR1212_ICON_DESCRIPTOR_LEAF_SET_VERSION(kv, version); + CSR1212_ICON_DESCRIPTOR_LEAF_SET_PALETTE_DEPTH(kv, palette_depth); + CSR1212_ICON_DESCRIPTOR_LEAF_SET_COLOR_SPACE(kv, color_space); + CSR1212_ICON_DESCRIPTOR_LEAF_SET_LANGUAGE(kv, language); + CSR1212_ICON_DESCRIPTOR_LEAF_SET_HSCAN(kv, hscan); + CSR1212_ICON_DESCRIPTOR_LEAF_SET_VSCAN(kv, vscan); + + if (palette_size) + memcpy(CSR1212_ICON_DESCRIPTOR_LEAF_PALETTE(kv), palette, + palette_size); + + memcpy(CSR1212_ICON_DESCRIPTOR_LEAF_PIXELS(kv), pixels, pixel_size); + + return kv; +} + +struct csr1212_keyval *csr1212_new_modifiable_descriptor_leaf(u_int16_t max_size, + u_int64_t address) +{ + struct csr1212_keyval *kv; + + /* IEEE 1212, par. 7.5.4.3 Modifiable descriptors */ + kv = csr1212_new_leaf(CSR1212_KV_ID_MODIFIABLE_DESCRIPTOR, NULL, sizeof(u_int64_t)); + if(!kv) + return NULL; + + CSR1212_MODIFIABLE_DESCRIPTOR_SET_MAX_SIZE(kv, max_size); + CSR1212_MODIFIABLE_DESCRIPTOR_SET_ADDRESS_HI(kv, address); + CSR1212_MODIFIABLE_DESCRIPTOR_SET_ADDRESS_LO(kv, address); + + return kv; +} + +static int csr1212_check_keyword(const char *s) +{ + for (; *s; s++) { + + if (('A' <= *s) && (*s <= 'Z')) + continue; + if (('0' <= *s) && (*s <= '9')) + continue; + if (*s == '-') + continue; + + return -1; /* failed */ + } + /* String conforms to keyword, as specified by IEEE 1212, + * par. 7.6.5 */ + return CSR1212_SUCCESS; +} + +struct csr1212_keyval *csr1212_new_keyword_leaf(int strc, const char *strv[]) +{ + struct csr1212_keyval *kv; + char *buffer; + int i, data_len = 0; + + /* Check all keywords to see if they conform to restrictions: + * Only the following characters is allowed ['A'..'Z','0'..'9','-'] + * Each word is zero-terminated. + * Also calculate the total length of the keywords. + */ + for (i = 0; i < strc; i++) { + if (!strv[i] || csr1212_check_keyword(strv[i])) { + return NULL; + } + data_len += strlen(strv[i]) + 1; /* Add zero-termination char. */ + } + + /* IEEE 1212, par. 7.6.5 Keyword leaves */ + kv = csr1212_new_leaf(CSR1212_KV_ID_KEYWORD, NULL, data_len); + if (!kv) + return NULL; + + buffer = (char *)kv->value.leaf.data; + + /* make sure last quadlet is zeroed out */ + *((u_int32_t*)&(buffer[(data_len - 1) & ~0x3])) = 0; + + /* Copy keyword(s) into leaf data buffer */ + for (i = 0; i < strc; i++) { + int len = strlen(strv[i]) + 1; + memcpy(buffer, strv[i], len); + buffer += 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); +} + + +void csr1212_disassociate_keyval(struct csr1212_keyval *kv) +{ + if (kv->associate) { + csr1212_release_keyval(kv->associate); + } + + kv->associate = NULL; +} + + +/* 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 their respective refcnts are 0. By means of + * list manipulation, this routine will descend a directory structure in a + * non-recursive manner. */ +void _csr1212_destroy_keyval(struct csr1212_keyval *kv) +{ + struct csr1212_keyval *k, *a; + struct csr1212_dentry dentry; + struct csr1212_dentry *head, *tail; + + dentry.kv = kv; + dentry.next = NULL; + dentry.prev = NULL; + + head = &dentry; + tail = head; + + while (head) { + k = head->kv; + + while (k) { + k->refcnt--; + + if (k->refcnt > 0) + break; + + a = k->associate; + + if (k->key.type == CSR1212_KV_TYPE_DIRECTORY) { + /* If the current entry is a directory, then 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; + u_int64_t csr_addr; + + if (!csr || !csr->ops->allocate_addr_range || + !csr->ops->release_addr) + return CSR1212_ENOMEM; + + /* 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 == ~0ULL) { + return CSR1212_ENOMEM; + } + if (csr_addr < CSR1212_REGISTER_SPACE_BASE) { + /* Invalid address returned from allocate_addr_range(). */ + csr->ops->release_addr(csr_addr, csr->private); + return CSR1212_ENOMEM; + } + + cache = csr1212_rom_cache_malloc(csr_addr - CSR1212_REGISTER_SPACE_BASE, romsize); + if (!cache) { + csr->ops->release_addr(csr_addr, csr->private); + return CSR1212_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 CSR1212_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 CSR1212_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 inline 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; +} + +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, <ail); + /* Add 1 quadlet for crc/len field */ + agg_size += kv->value.directory.len + 1; + break; + } + kv = kv->next; + } + return quads_to_bytes(agg_size); +} + +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 */ + 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; +} + +static void csr1212_generate_tree_subdir(struct csr1212_keyval *dir, + u_int32_t *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) { + u_int32_t 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 */ + break; /* GDB breakpoint */ + } + + 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] = CSR1212_CPU_TO_BE32(value); + index++; + } + } +} + +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 */ + break; /* GDB breakpoint */ + + 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 = CSR1212_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 = CSR1212_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; + } +} + +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; + + if (!csr) + return CSR1212_EINVAL; + + 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(u_int32_t))) + 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(u_int32_t); + } + + /* 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. */ + 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(u_int32_t); + + /* 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; + + kvi->length = CSR1212_CPU_TO_BE16(bytes_to_quads(cache->len) - 1); + kvi->crc = csr1212_crc16(kvi->data, + bytes_to_quads(cache->len) - 1); + + } + } + + return CSR1212_SUCCESS; +} + +int csr1212_read(struct csr1212_csr *csr, u_int32_t offset, void *buffer, u_int32_t 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 CSR1212_ENOENT; +} + + + +/* 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(csr1212_quad_t)) { + ret = csr->ops->bus_read(csr, CSR1212_CONFIG_ROM_SPACE_BASE + i, + sizeof(csr1212_quad_t), + &csr->cache_head->data[bytes_to_quads(i)], + csr->private); + if (ret != CSR1212_SUCCESS) + return ret; + } + + 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(csr1212_quad_t)) { + ret = csr->ops->bus_read(csr, CSR1212_CONFIG_ROM_SPACE_BASE + i, + sizeof(csr1212_quad_t), + &csr->cache_head->data[bytes_to_quads(i)], + csr->private); + if (ret != CSR1212_SUCCESS) + return ret; + } + + if (bytes_to_quads(csr->bus_info_len - sizeof(csr1212_quad_t)) != bi->length) + return CSR1212_EINVAL; + +#if 0 + /* Apparently there are too many differnt wrong implementations of the + * CRC algorithm that verifying them is moot. */ + if ((csr1212_crc16(bi->data, bi->crc_length) != bi->crc) && + (csr1212_msft_crc16(bi->data, bi->crc_length) != bi->crc)) + return CSR1212_EINVAL; +#endif + + cr = CSR1212_MALLOC(sizeof(struct csr1212_cache_region)); + if (!cr) + return CSR1212_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; +} + +static int csr1212_parse_dir_entry(struct csr1212_keyval *dir, + csr1212_quad_t ki, + u_int32_t kv_pos) +{ + int ret = CSR1212_SUCCESS; + struct csr1212_keyval *k = NULL; + u_int32_t offset; + + 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 = CSR1212_ENOMEM; + goto fail; + } + + k->refcnt = 0; /* Don't keep local reference when parsing. */ + break; + + case CSR1212_KV_TYPE_CSR_OFFSET: + k = csr1212_new_csr_offset(CSR1212_KV_KEY_ID(ki), + CSR1212_KV_VAL(ki)); + if (!k) { + ret = CSR1212_ENOMEM; + goto fail; + } + k->refcnt = 0; /* Don't keep local reference when parsing. */ + 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 = CSR1212_EIO; + goto fail; + } + + 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 = CSR1212_ENOMEM; + goto fail; + } + k->refcnt = 0; /* Don't keep local reference when parsing. */ + k->valid = 0; /* Contents not read yet so it's not valid. */ + 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); + +fail: + if (ret != CSR1212_SUCCESS) { + if (k) + 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 = CSR1212_BE16_TO_CPU(kvi->length); + +#if 0 + /* Apparently there are too many differnt wrong implementations of the + * CRC algorithm that verifying them is moot. */ + if ((csr1212_crc16(kvi->data, kvi_len) != kvi->crc) && + (csr1212_msft_crc16(kvi->data, kvi_len) != kvi->crc)) { + ret = CSR1212_EINVAL; + goto fail; + } +#endif + + switch(kv->key.type) { + case CSR1212_KV_TYPE_DIRECTORY: + for (i = 0; i < kvi_len; i++) { + csr1212_quad_t 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) { + kv->value.leaf.data = CSR1212_MALLOC(quads_to_bytes(kvi_len)); + if (!kv->value.leaf.data) + { + ret = CSR1212_ENOMEM; + goto fail; + } + + kv->value.leaf.len = kvi_len; + memcpy(kv->value.leaf.data, kvi->data, quads_to_bytes(kvi_len)); + } + break; + } + + kv->valid = 1; + +fail: + return ret; +} + + +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; + u_int64_t addr; + u_int32_t *cache_ptr; + u_int16_t kv_len = 0; + + if (!csr || !kv) + return CSR1212_EINVAL; + + /* 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) { + csr1212_quad_t q; + u_int32_t cache_size; + + /* Only create a new cache for Extended ROM leaves. */ + if (kv->key.id != CSR1212_KV_ID_EXTENDED_ROM) + return CSR1212_EINVAL; + + if (csr->ops->bus_read(csr, + CSR1212_REGISTER_SPACE_BASE + kv->offset, + sizeof(csr1212_quad_t), &q, csr->private)) { + return CSR1212_EIO; + } + + kv->value.leaf.len = CSR1212_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 CSR1212_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(struct csr1212_cache_region)); + if (!cache->filled_head) { + return CSR1212_ENOMEM; + } + + cache->filled_head->offset_start = 0; + cache->filled_head->offset_end = sizeof(csr1212_quad_t); + 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(struct csr1212_cache_region)); + if (!newcr) + return CSR1212_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(CSR1212_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(struct csr1212_cache_region)); + if (!newcr) + return CSR1212_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 CSR1212_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(CSR1212_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 CSR1212_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); +} + + + +int csr1212_parse_csr(struct csr1212_csr *csr) +{ + static const int mr_map[] = { 4, 64, 1024, 0 }; + struct csr1212_dentry *dentry; + int ret; + + if (!csr || !csr->ops->bus_read) + return CSR1212_EINVAL; + + 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 + csr->max_rom = mr_map[csr->ops->get_max_rom(csr->bus_info_data, + csr->private)]; + + 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; + csr1212_get_keyval(csr, csr->root_kv); + + /* 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) { + csr1212_get_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..e673426 --- /dev/null +++ b/drivers/ieee1394/csr1212.h @@ -0,0 +1,727 @@ +/* + * 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__ + + +/* Compatibility layer */ +#ifdef __KERNEL__ + +#include <linux/types.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/sched.h> +#include <linux/vmalloc.h> +#include <asm/pgalloc.h> + +#define CSR1212_MALLOC(size) vmalloc((size)) +#define CSR1212_FREE(ptr) vfree(ptr) +#define CSR1212_BE16_TO_CPU(quad) be16_to_cpu(quad) +#define CSR1212_CPU_TO_BE16(quad) cpu_to_be16(quad) +#define CSR1212_BE32_TO_CPU(quad) be32_to_cpu(quad) +#define CSR1212_CPU_TO_BE32(quad) cpu_to_be32(quad) +#define CSR1212_BE64_TO_CPU(quad) be64_to_cpu(quad) +#define CSR1212_CPU_TO_BE64(quad) cpu_to_be64(quad) + +#define CSR1212_LE16_TO_CPU(quad) le16_to_cpu(quad) +#define CSR1212_CPU_TO_LE16(quad) cpu_to_le16(quad) +#define CSR1212_LE32_TO_CPU(quad) le32_to_cpu(quad) +#define CSR1212_CPU_TO_LE32(quad) cpu_to_le32(quad) +#define CSR1212_LE64_TO_CPU(quad) le64_to_cpu(quad) +#define CSR1212_CPU_TO_LE64(quad) cpu_to_le64(quad) + +#include <linux/errno.h> +#define CSR1212_SUCCESS (0) +#define CSR1212_EINVAL (-EINVAL) +#define CSR1212_ENOMEM (-ENOMEM) +#define CSR1212_ENOENT (-ENOENT) +#define CSR1212_EIO (-EIO) +#define CSR1212_EBUSY (-EBUSY) + +#else /* Userspace */ + +#include <sys/types.h> +#include <malloc.h> +#define CSR1212_MALLOC(size) malloc(size) +#define CSR1212_FREE(ptr) free(ptr) +#include <endian.h> +#if __BYTE_ORDER == __LITTLE_ENDIAN +#include <byteswap.h> +#define CSR1212_BE16_TO_CPU(quad) bswap_16(quad) +#define CSR1212_CPU_TO_BE16(quad) bswap_16(quad) +#define CSR1212_BE32_TO_CPU(quad) bswap_32(quad) +#define CSR1212_CPU_TO_BE32(quad) bswap_32(quad) +#define CSR1212_BE64_TO_CPU(quad) bswap_64(quad) +#define CSR1212_CPU_TO_BE64(quad) bswap_64(quad) + +#define CSR1212_LE16_TO_CPU(quad) (quad) +#define CSR1212_CPU_TO_LE16(quad) (quad) +#define CSR1212_LE32_TO_CPU(quad) (quad) +#define CSR1212_CPU_TO_LE32(quad) (quad) +#define CSR1212_LE64_TO_CPU(quad) (quad) +#define CSR1212_CPU_TO_LE64(quad) (quad) +#else +#define CSR1212_BE16_TO_CPU(quad) (quad) +#define CSR1212_CPU_TO_BE16(quad) (quad) +#define CSR1212_BE32_TO_CPU(quad) (quad) +#define CSR1212_CPU_TO_BE32(quad) (quad) +#define CSR1212_BE64_TO_CPU(quad) (quad) +#define CSR1212_CPU_TO_BE64(quad) (quad) + +#define CSR1212_LE16_TO_CPU(quad) bswap_16(quad) +#define CSR1212_CPU_TO_LE16(quad) bswap_16(quad) +#define CSR1212_LE32_TO_CPU(quad) bswap_32(quad) +#define CSR1212_CPU_TO_LE32(quad) bswap_32(quad) +#define CSR1212_LE64_TO_CPU(quad) bswap_64(quad) +#define CSR1212_CPU_TO_LE64(quad) bswap_64(quad) +#endif + +#include <errno.h> +#define CSR1212_SUCCESS (0) +#define CSR1212_EINVAL (EINVAL) +#define CSR1212_ENOMEM (ENOMEM) +#define CSR1212_ENOENT (ENOENT) +#define CSR1212_EIO (EIO) +#define CSR1212_EBUSY (EBUSY) + +#endif + + +#define CSR1212_KV_VAL_MASK 0xffffff +#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 */ + + +/* 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_EXTENDED_ROM_SIZE (0x10000 * sizeof(u_int32_t)) + + +/* Config ROM image structures */ +struct csr1212_bus_info_block_img { + u_int8_t length; + u_int8_t crc_length; + u_int16_t crc; + + /* Must be last */ + u_int32_t data[0]; /* older gcc can't handle [] which is standard */ +}; + +#define CSR1212_KV_KEY(quad) (CSR1212_BE32_TO_CPU(quad) >> CSR1212_KV_KEY_SHIFT) +#define CSR1212_KV_KEY_TYPE(quad) (CSR1212_KV_KEY(quad) >> CSR1212_KV_KEY_TYPE_SHIFT) +#define CSR1212_KV_KEY_ID(quad) (CSR1212_KV_KEY(quad) & CSR1212_KV_KEY_ID_MASK) +#define CSR1212_KV_VAL(quad) (CSR1212_BE32_TO_CPU(quad) & CSR1212_KV_VAL_MASK) + +#define CSR1212_SET_KV_KEY(quad, key) ((quad) = \ + CSR1212_CPU_TO_BE32(CSR1212_KV_VAL(quad) | ((key) << CSR1212_KV_KEY_SHIFT))) +#define CSR1212_SET_KV_VAL(quad, val) ((quad) = \ + CSR1212_CPU_TO_BE32((CSR1212_KV_KEY(quad) << CSR1212_KV_KEY_SHIFT) | (val))) +#define CSR1212_SET_KV_TYPEID(quad, type, id) ((quad) = \ + CSR1212_CPU_TO_BE32(CSR1212_KV_VAL(quad) | \ + (((((type) & CSR1212_KV_KEY_TYPE_MASK) << CSR1212_KV_KEY_TYPE_SHIFT) | \ + ((id) & CSR1212_KV_KEY_ID_MASK)) << CSR1212_KV_KEY_SHIFT))) + +typedef u_int32_t csr1212_quad_t; + + +struct csr1212_keyval_img { + u_int16_t length; + u_int16_t crc; + + /* Must be last */ + csr1212_quad_t data[0]; /* older gcc can't handle [] which is standard */ +}; + +struct csr1212_leaf { + int len; + u_int32_t *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 { + u_int8_t type; + u_int8_t id; + } key; + union { + u_int32_t immediate; + u_int32_t csr_offset; + struct csr1212_leaf leaf; + struct csr1212_directory directory; + } value; + struct csr1212_keyval *associate; + int refcnt; + + /* used in generating and/or parsing CSR image */ + struct csr1212_keyval *next, *prev; /* flat list of CSR elements */ + u_int32_t offset; /* position in CSR from 0xffff f000 0000 */ + u_int8_t valid; /* flag indicating keyval has valid data*/ +}; + + +struct csr1212_cache_region { + struct csr1212_cache_region *next, *prev; + u_int32_t offset_start; /* inclusive */ + u_int32_t 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; + u_int32_t offset; + struct csr1212_keyval *ext_rom; + size_t len; + + /* Must be last */ + u_int32_t 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 */ + u_int32_t *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, u_int64_t addr, + u_int16_t 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. */ + u_int64_t (*allocate_addr_range) (u_int64_t size, u_int32_t alignment, + void *private); + + + /* This function is used by csr1212 to release a region in units space + * that is no longer needed. */ + void (*release_addr) (u_int64_t 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) (u_int32_t *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(u_int32_t)) + +#define CSR1212_DESCRIPTOR_LEAF_TYPE(kv) \ + (CSR1212_BE32_TO_CPU((kv)->value.leaf.data[0]) >> CSR1212_DESCRIPTOR_LEAF_TYPE_SHIFT) +#define CSR1212_DESCRIPTOR_LEAF_SPECIFIER_ID(kv) \ + (CSR1212_BE32_TO_CPU((kv)->value.leaf.data[0]) & \ + CSR1212_DESCRIPTOR_LEAF_SPECIFIER_ID_MASK) +#define CSR1212_DESCRIPTOR_LEAF_DATA(kv) \ + (&((kv)->value.leaf.data[1])) + +#define CSR1212_DESCRIPTOR_LEAF_SET_TYPE(kv, type) \ + ((kv)->value.leaf.data[0] = \ + CSR1212_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] = \ + CSR1212_CPU_TO_BE32((CSR1212_DESCRIPTOR_LEAF_TYPE(kv) << \ + CSR1212_DESCRIPTOR_LEAF_TYPE_SHIFT) | \ + ((spec_id) & 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(u_int32_t)) + +#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH(kv) \ + (CSR1212_BE32_TO_CPU((kv)->value.leaf.data[1]) >> \ + CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH_SHIFT) +#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET(kv) \ + ((CSR1212_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) \ + (CSR1212_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])) + +#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_SET_WIDTH(kv, width) \ + ((kv)->value.leaf.data[1] = \ + ((kv)->value.leaf.data[1] & \ + CSR1212_CPU_TO_BE32(~(CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH_MASK << \ + CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH_SHIFT))) | \ + CSR1212_CPU_TO_BE32(((width) & \ + CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH_MASK) << \ + CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH_SHIFT)) +#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_SET_CHAR_SET(kv, char_set) \ + ((kv)->value.leaf.data[1] = \ + ((kv)->value.leaf.data[1] & \ + CSR1212_CPU_TO_BE32(~(CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET_MASK << \ + CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET_SHIFT))) | \ + CSR1212_CPU_TO_BE32(((char_set) & \ + CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET_MASK) << \ + CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET_SHIFT)) +#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_SET_LANGUAGE(kv, language) \ + ((kv)->value.leaf.data[1] = \ + ((kv)->value.leaf.data[1] & \ + CSR1212_CPU_TO_BE32(~(CSR1212_TEXTUAL_DESCRIPTOR_LEAF_LANGUAGE_MASK))) | \ + CSR1212_CPU_TO_BE32(((language) & \ + CSR1212_TEXTUAL_DESCRIPTOR_LEAF_LANGUAGE_MASK))) + + +/* Icon Descriptor Leaf manipulation macros */ +#define CSR1212_ICON_DESCRIPTOR_LEAF_VERSION_MASK 0xffffff +#define CSR1212_ICON_DESCRIPTOR_LEAF_PALETTE_DEPTH_SHIFT 30 +#define CSR1212_ICON_DESCRIPTOR_LEAF_PALETTE_DEPTH_MASK 0x3 /* after shift */ +#define CSR1212_ICON_DESCRIPTOR_LEAF_COLOR_SPACE_SHIFT 16 +#define CSR1212_ICON_DESCRIPTOR_LEAF_COLOR_SPACE_MASK 0xf /* after shift */ +#define CSR1212_ICON_DESCRIPTOR_LEAF_LANGUAGE_MASK 0xffff +#define CSR1212_ICON_DESCRIPTOR_LEAF_HSCAN_SHIFT 16 +#define CSR1212_ICON_DESCRIPTOR_LEAF_HSCAN_MASK 0xffff /* after shift */ +#define CSR1212_ICON_DESCRIPTOR_LEAF_VSCAN_MASK 0xffff +#define CSR1212_ICON_DESCRIPTOR_LEAF_OVERHEAD (3 * sizeof(u_int32_t)) + +#define CSR1212_ICON_DESCRIPTOR_LEAF_VERSION(kv) \ + (CSR1212_BE32_TO_CPU((kv)->value.leaf.data[2]) & \ + CSR1212_ICON_DESCRIPTOR_LEAF_VERSION_MASK) + +#define CSR1212_ICON_DESCRIPTOR_LEAF_PALETTE_DEPTH(kv) \ + (CSR1212_BE32_TO_CPU((kv)->value.leaf.data[3]) >> \ + CSR1212_ICON_DESCRIPTOR_LEAF_PALETTE_DEPTH_SHIFT) + +#define CSR1212_ICON_DESCRIPTOR_LEAF_COLOR_SPACE(kv) \ + ((CSR1212_BE32_TO_CPU((kv)->value.leaf.data[3]) >> \ + CSR1212_ICON_DESCRIPTOR_LEAF_COLOR_SPACE_SHIFT) & \ + CSR1212_ICON_DESCRIPTOR_LEAF_COLOR_SPACE_MASK) + +#define CSR1212_ICON_DESCRIPTOR_LEAF_LANGUAGE(kv) \ + (CSR1212_BE32_TO_CPU((kv)->value.leaf.data[3]) & \ + CSR1212_ICON_DESCRIPTOR_LEAF_LANGUAGE_MASK) + +#define CSR1212_ICON_DESCRIPTOR_LEAF_HSCAN(kv) \ + ((CSR1212_BE32_TO_CPU((kv)->value.leaf.data[4]) >> \ + CSR1212_ICON_DESCRIPTOR_LEAF_COLOR_HSCAN_SHIFT) & \ + CSR1212_ICON_DESCRIPTOR_LEAF_COLOR_HSCAN_MASK) + +#define CSR1212_ICON_DESCRIPTOR_LEAF_VSCAN(kv) \ + (CSR1212_BE32_TO_CPU((kv)->value.leaf.data[4]) & \ + CSR1212_ICON_DESCRIPTOR_LEAF_VSCAN_MASK) + +#define CSR1212_ICON_DESCRIPTOR_LEAF_PALETTE(kv) \ + (&((kv)->value.leaf.data[5])) + +static inline u_int32_t *CSR1212_ICON_DESCRIPTOR_LEAF_PIXELS(struct csr1212_keyval *kv) +{ + static const int pd[4] = { 0, 4, 16, 256 }; + static const int cs[16] = { 4, 2 }; + int ps = pd[CSR1212_ICON_DESCRIPTOR_LEAF_PALETTE_DEPTH(kv)]; + + return &kv->value.leaf.data[5 + + (ps * cs[CSR1212_ICON_DESCRIPTOR_LEAF_COLOR_SPACE(kv)]) / + sizeof(u_int32_t)]; +} + +#define CSR1212_ICON_DESCRIPTOR_LEAF_SET_VERSION(kv, version) \ + ((kv)->value.leaf.data[2] = \ + ((kv)->value.leaf.data[2] & \ + CSR1212_CPU_TO_BE32(~(CSR1212_ICON_DESCRIPTOR_LEAF_VERSION_MASK))) | \ + CSR1212_CPU_TO_BE32(((version) & \ + CSR1212_ICON_DESCRIPTOR_LEAF_VERSION_MASK))) + +#define CSR1212_ICON_DESCRIPTOR_LEAF_SET_PALETTE_DEPTH(kv, palette_depth) \ + ((kv)->value.leaf.data[3] = \ + ((kv)->value.leaf.data[3] & \ + CSR1212_CPU_TO_BE32(~(CSR1212_ICON_DESCRIPTOR_LEAF_PALETTE_DEPTH_MASK << \ + CSR1212_ICON_DESCRIPTOR_LEAF_PALETTE_DEPTH_SHIFT))) | \ + CSR1212_CPU_TO_BE32(((palette_depth) & \ + CSR1212_ICON_DESCRIPTOR_LEAF_PALETTE_DEPTH_MASK) << \ + CSR1212_ICON_DESCRIPTOR_LEAF_PALETTE_DEPTH_SHIFT)) + +#define CSR1212_ICON_DESCRIPTOR_LEAF_SET_COLOR_SPACE(kv, color_space) \ + ((kv)->value.leaf.data[3] = \ + ((kv)->value.leaf.data[3] & \ + CSR1212_CPU_TO_BE32(~(CSR1212_ICON_DESCRIPTOR_LEAF_COLOR_SPACE_MASK << \ + CSR1212_ICON_DESCRIPTOR_LEAF_COLOR_SPACE_SHIFT))) | \ + CSR1212_CPU_TO_BE32(((color_space) & \ + CSR1212_ICON_DESCRIPTOR_LEAF_COLOR_SPACE_MASK) << \ + CSR1212_ICON_DESCRIPTOR_LEAF_COLOR_SPACE_SHIFT)) + +#define CSR1212_ICON_DESCRIPTOR_LEAF_SET_LANGUAGE(kv, language) \ + ((kv)->value.leaf.data[3] = \ + ((kv)->value.leaf.data[3] & \ + CSR1212_CPU_TO_BE32(~(CSR1212_ICON_DESCRIPTOR_LEAF_LANGUAGE_MASK))) | \ + CSR1212_CPU_TO_BE32(((language) & \ + CSR1212_ICON_DESCRIPTOR_LEAF_LANGUAGE_MASK))) + +#define CSR1212_ICON_DESCRIPTOR_LEAF_SET_HSCAN(kv, hscan) \ + ((kv)->value.leaf.data[4] = \ + ((kv)->value.leaf.data[4] & \ + CSR1212_CPU_TO_BE32(~(CSR1212_ICON_DESCRIPTOR_LEAF_HSCAN_MASK << \ + CSR1212_ICON_DESCRIPTOR_LEAF_HSCAN_SHIFT))) | \ + CSR1212_CPU_TO_BE32(((hscan) & \ + CSR1212_ICON_DESCRIPTOR_LEAF_HSCAN_MASK) << \ + CSR1212_ICON_DESCRIPTOR_LEAF_HSCAN_SHIFT)) + +#define CSR1212_ICON_DESCRIPTOR_LEAF_SET_VSCAN(kv, vscan) \ + ((kv)->value.leaf.data[4] = \ + (((kv)->value.leaf.data[4] & \ + CSR1212_CPU_TO_BE32(~CSR1212_ICON_DESCRIPTOR_LEAF_VSCAN_MASK))) | \ + CSR1212_CPU_TO_BE32(((vscan) & \ + CSR1212_ICON_DESCRIPTOR_LEAF_VSCAN_MASK))) + + +/* Modifiable Descriptor Leaf manipulation macros */ +#define CSR1212_MODIFIABLE_DESCRIPTOR_LEAF_MAX_SIZE_SHIFT 16 +#define CSR1212_MODIFIABLE_DESCRIPTOR_LEAF_MAX_SIZE_MASK 0xffff +#define CSR1212_MODIFIABLE_DESCRIPTOR_LEAF_ADDR_HI_SHIFT 32 +#define CSR1212_MODIFIABLE_DESCRIPTOR_LEAF_ADDR_HI_MASK 0xffff +#define CSR1212_MODIFIABLE_DESCRIPTOR_LEAF_ADDR_LO_MASK 0xffffffffULL + +#define CSR1212_MODIFIABLE_DESCRIPTOR_MAX_SIZE(kv) \ + CSR1212_BE16_TO_CPU((kv)->value.leaf.data[0] >> CSR1212_MODIFIABLE_DESCRIPTOR_MAX_SIZE_SHIFT) + +#define CSR1212_MODIFIABLE_DESCRIPTOR_ADDRESS(kv) \ + (CSR1212_BE16_TO_CPU(((u_int64_t)((kv)->value.leaf.data[0])) << \ + CSR1212_MODIFIABLE_DESCRIPTOR_ADDR_HI_SHIFT) | \ + CSR1212_BE32_TO_CPU((kv)->value.leaf.data[1])) + +#define CSR1212_MODIFIABLE_DESCRIPTOR_SET_MAX_SIZE(kv, size) \ + ((kv)->value.leaf.data[0] = \ + ((kv)->value.leaf.data[0] & \ + CSR1212_CPU_TO_BE32(~(CSR1212_MODIFIABLE_DESCRIPTOR_LEAF_MAX_SIZE_MASK << \ + CSR1212_MODIFIABLE_DESCRIPTOR_LEAF_MAX_SIZE_SHIFT))) | \ + CSR1212_CPU_TO_BE32(((size) & \ + CSR1212_MODIFIABLE_DESCRIPTOR_LEAF_MAX_SIZE_MASK) << \ + CSR1212_MODIFIABLE_DESCRIPTOR_LEAF_MAX_SIZE_SHIFT)) + +#define CSR1212_MODIFIABLE_DESCRIPTOR_SET_ADDRESS_HI(kv, addr) \ + ((kv)->value.leaf.data[0] = \ + ((kv)->value.leaf.data[0] & \ + CSR1212_CPU_TO_BE32(~(CSR1212_MODIFIABLE_DESCRIPTOR_LEAF_ADDR_HI_MASK))) | \ + CSR1212_CPU_TO_BE32(((addr) & \ + CSR1212_MODIFIABLE_DESCRIPTOR_LEAF_ADDR_HI_MASK))) + +#define CSR1212_MODIFIABLE_DESCRIPTOR_SET_ADDRESS_LO(kv, addr) \ + ((kv)->value.leaf.data[1] = \ + CSR1212_CPU_TO_BE32(addr & CSR1212_MODIFIABLE_DESCRIPTOR_LEAF_ADDR_LO_MASK)) + + + +/* 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 u_int32_t *bus_info_data, int max_rom); + + +/* The following function destroys 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(u_int8_t key, u_int32_t value); +extern struct csr1212_keyval *csr1212_new_leaf(u_int8_t key, const void *data, + size_t data_len); +extern struct csr1212_keyval *csr1212_new_csr_offset(u_int8_t key, + u_int32_t csr_offset); +extern struct csr1212_keyval *csr1212_new_directory(u_int8_t key); +extern struct csr1212_keyval *csr1212_new_extended_immediate(u_int32_t spec, + u_int32_t key, + u_int32_t value); +extern struct csr1212_keyval *csr1212_new_extended_leaf(u_int32_t spec, + u_int32_t key, + const void *data, + size_t data_len); +extern struct csr1212_keyval *csr1212_new_descriptor_leaf(u_int8_t dtype, + u_int32_t specifier_id, + const void *data, + size_t data_len); +extern struct csr1212_keyval *csr1212_new_textual_descriptor_leaf(u_int8_t cwidth, + u_int16_t cset, + u_int16_t language, + const void *data, + size_t data_len); +extern struct csr1212_keyval *csr1212_new_string_descriptor_leaf(const char *s); +extern struct csr1212_keyval *csr1212_new_icon_descriptor_leaf(u_int32_t version, + u_int8_t palette_depth, + u_int8_t color_space, + u_int16_t language, + u_int16_t hscan, + u_int16_t vscan, + u_int32_t *palette, + u_int32_t *pixels); +extern struct csr1212_keyval *csr1212_new_modifiable_descriptor_leaf(u_int16_t max_size, + u_int64_t address); +extern struct csr1212_keyval *csr1212_new_keyword_leaf(int strc, + const char *strv[]); + + +/* The following functions manage 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.*/ +extern int csr1212_associate_keyval(struct csr1212_keyval *kv, + struct csr1212_keyval *associate); +extern void csr1212_disassociate_keyval(struct csr1212_keyval *kv); + + +/* 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); + + +/* The following functions create a Configuration ROM image from the tree of + * keyvals provided. csr1212_generate_csr_image() creates a complete image in + * the list of caches available via csr->cache_head. The other functions are + * provided should there be a need to create a flat image without restrictions + * placed by IEEE 1212. */ +extern struct csr1212_keyval *csr1212_generate_positions(struct csr1212_csr_rom_cache *cache, + struct csr1212_keyval *start_kv, + int start_pos); +extern size_t csr1212_generate_layout_order(struct csr1212_keyval *kv); +extern void csr1212_fill_cache(struct csr1212_csr_rom_cache *cache); +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, u_int32_t offset, void *buffer, + u_int32_t 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); + +/* These are internal functions referenced by inline functions below. */ +extern int _csr1212_read_keyval(struct csr1212_csr *csr, struct csr1212_keyval *kv); +extern void _csr1212_destroy_keyval(struct csr1212_keyval *kv); + + +/* 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(u_int32_t offset, + size_t size) +{ + struct csr1212_csr_rom_cache *cache; + + cache = CSR1212_MALLOC(sizeof(struct csr1212_csr_rom_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. */ +static inline 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; +} + + +/* 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) +{ + kv->refcnt++; +} + + +/* 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_*(). */ +static inline void csr1212_release_keyval(struct csr1212_keyval *kv) +{ + if (kv->refcnt > 1) + kv->refcnt--; + else + _csr1212_destroy_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. + * + * _csr is a struct csr1212_csr * that points to CSR associated with dir. + * _kv is a struct csr1212_keyval * that'll point to the current keyval (loop index). + * _dir is a struct csr1212_keyval * that points to the directory to be looped. + * _pos is a struct csr1212_dentry * that 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..758819d --- /dev/null +++ b/drivers/ieee1394/dma.c @@ -0,0 +1,260 @@ +/* + * 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/module.h> +#include <linux/vmalloc.h> +#include <linux/slab.h> +#include <linux/mm.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 */ + +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; +} + +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; + } + + /* just to be safe - this will become unnecessary once sglist->address goes away */ + memset(dma->sglist, 0, dma->n_pages * sizeof(*dma->sglist)); + + /* fill scatter/gather list with pages */ + for (i = 0; i < dma->n_pages; i++) { + unsigned long va = (unsigned long) dma->kvirt + (i << PAGE_SHIFT); + + dma->sglist[i].page = vmalloc_to_page((void *)va); + dma->sglist[i].length = PAGE_SIZE; + } + + /* 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; +} + +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 long *rem) +{ + int i; + unsigned long off = offset; + + for (i = 0; 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_addr_t dma_region_offset_to_bus(struct dma_region *dma, unsigned long offset) +{ + unsigned long rem; + + struct scatterlist *sg = &dma->sglist[dma_region_find(dma, offset, &rem)]; + return sg_dma_address(sg) + rem; +} + +void dma_region_sync_for_cpu(struct dma_region *dma, unsigned long offset, unsigned long len) +{ + int first, last; + unsigned long rem; + + if (!len) + len = 1; + + first = dma_region_find(dma, offset, &rem); + last = dma_region_find(dma, offset + len - 1, &rem); + + pci_dma_sync_sg_for_cpu(dma->dev, &dma->sglist[first], last - first + 1, dma->direction); +} + +void dma_region_sync_for_device(struct dma_region *dma, unsigned long offset, unsigned long len) +{ + int first, last; + unsigned long rem; + + if (!len) + len = 1; + + first = dma_region_find(dma, offset, &rem); + last = dma_region_find(dma, offset + len - 1, &rem); + + pci_dma_sync_sg_for_device(dma->dev, &dma->sglist[first], last - first + 1, dma->direction); +} + +#ifdef CONFIG_MMU + +/* nopage() handler for mmap access */ + +static struct page* +dma_region_pagefault(struct vm_area_struct *area, unsigned long address, int *type) +{ + unsigned long offset; + unsigned long kernel_virt_addr; + struct page *ret = NOPAGE_SIGBUS; + + struct dma_region *dma = (struct dma_region*) area->vm_private_data; + + if (!dma->kvirt) + goto out; + + if ( (address < (unsigned long) area->vm_start) || + (address > (unsigned long) area->vm_start + (dma->n_pages << PAGE_SHIFT)) ) + goto out; + + if (type) + *type = VM_FAULT_MINOR; + offset = address - area->vm_start; + kernel_virt_addr = (unsigned long) dma->kvirt + offset; + ret = vmalloc_to_page((void*) kernel_virt_addr); + get_page(ret); +out: + return ret; +} + +static struct vm_operations_struct dma_region_vm_ops = { + .nopage = dma_region_pagefault, +}; + +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 */ + 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; + + 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..061550a --- /dev/null +++ b/drivers/ieee1394/dma.h @@ -0,0 +1,78 @@ +/* + * 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 <linux/pci.h> +#include <asm/scatterlist.h> + +/* struct dma_prog_region + + a small, physically-contiguous DMA buffer with random-access, + synchronous usage characteristics +*/ + +struct dma_prog_region { + unsigned char *kvirt; /* kernel virtual address */ + struct pci_dev *dev; /* PCI device */ + unsigned int n_pages; /* # of kernel pages */ + dma_addr_t bus_addr; /* base bus address */ +}; + +/* 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 + + a large, non-physically-contiguous DMA buffer with streaming, + asynchronous usage characteristics +*/ + +struct dma_region { + unsigned char *kvirt; /* kernel virtual address */ + struct pci_dev *dev; /* PCI device */ + unsigned int n_pages; /* # of kernel pages */ + unsigned int n_dma_pages; /* # of IOMMU pages */ + struct scatterlist *sglist; /* IOMMU mapping */ + int direction; /* PCI_DMA_TODEVICE, etc */ +}; + +/* clear out all fields but do not allocate anything */ +void dma_region_init(struct dma_region *dma); + +/* 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); + +/* unmap and free the buffer */ +void dma_region_free(struct dma_region *dma); + +/* sync the CPU's view of the buffer */ +void dma_region_sync_for_cpu(struct dma_region *dma, unsigned long offset, unsigned long len); +/* sync the IO bus' view of the buffer */ +void dma_region_sync_for_device(struct dma_region *dma, unsigned long offset, unsigned long len); + +/* map the buffer into a user space process */ +int dma_region_mmap(struct dma_region *dma, struct file *file, struct vm_area_struct *vma); + +/* macro to index into a DMA region (or dma_prog_region) */ +#define dma_region_i(_dma, _type, _index) ( ((_type*) ((_dma)->kvirt)) + (_index) ) + +/* return the DMA bus address of the byte with the given offset + relative to the beginning of the dma_region */ +dma_addr_t dma_region_offset_to_bus(struct dma_region *dma, unsigned long offset); + +#endif /* IEEE1394_DMA_H */ diff --git a/drivers/ieee1394/dv1394-private.h b/drivers/ieee1394/dv1394-private.h new file mode 100644 index 0000000..80b5ac7 --- /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 semaphore 'sem' - 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 sem, take sem first to avoid deadlock! + */ + struct semaphore sem; + + /* 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..68c7a5f --- /dev/null +++ b/drivers/ieee1394/dv1394.c @@ -0,0 +1,2663 @@ +/* + * 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 + - devfs and character device dispatching (* needs testing with Linux 2.2.x) + - 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/config.h> +#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/smp_lock.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/ioctl32.h> +#include <linux/compat.h> +#include <linux/cdev.h> + +#include "ieee1394.h" +#include "ieee1394_types.h" +#include "nodemgr.h" +#include "hosts.h" +#include "ieee1394_core.h" +#include "highlevel.h" +#include "dv1394.h" +#include "dv1394-private.h" + +#include "ohci1394.h" + +#ifndef virt_to_page +#define virt_to_page(x) MAP_NR(x) +#endif + +#ifndef vmalloc_32 +#define vmalloc_32(x) vmalloc(x) +#endif + + +/* 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... ) +#endif + +#if DV1394_DEBUG_LEVEL >= 1 +#define debug_printk( args... ) printk( args) +#else +#define debug_printk( args... ) +#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->sem 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 < 0) ) + 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; + /* the following are now set via devfs */ + 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; + + /* serialize mmap */ + down(&video->sem); + + 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: + up(&video->sem); + 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 (down_trylock(&video->sem)) + return -EAGAIN; + } else { + if (down_interruptible(&video->sem)) + return -ERESTARTSYS; + } + + if ( !video_card_initialized(video) ) { + ret = do_dv1394_init_default(video); + if (ret) { + up(&video->sem); + 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); + up(&video->sem); + 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 (down_trylock(&video->sem)) + return -EAGAIN; + } else { + if (down_interruptible(&video->sem)) + return -ERESTARTSYS; + } + + if ( !video_card_initialized(video) ) { + ret = do_dv1394_init_default(video); + if (ret) { + up(&video->sem); + 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); + up(&video->sem); + return ret; +} + + +/*** DEVICE IOCTL INTERFACE ************************************************/ + +static long dv1394_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct video_card *video; + unsigned long flags; + int ret = -EINVAL; + void __user *argp = (void __user *)arg; + + DECLARE_WAITQUEUE(wait, current); + + lock_kernel(); + video = file_to_video_card(file); + + /* serialize this to prevent multi-threaded mayhem */ + if (file->f_flags & O_NONBLOCK) { + if (down_trylock(&video->sem)) { + unlock_kernel(); + return -EAGAIN; + } + } else { + if (down_interruptible(&video->sem)) { + unlock_kernel(); + 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: + up(&video->sem); + unlock_kernel(); + return ret; +} + +/*** DEVICE FILE INTERFACE CONTINUED ***************************************/ + +static int dv1394_open(struct inode *inode, struct file *file) +{ + struct video_card *video = NULL; + + /* if the device was opened through devfs, then file->private_data + has already been set to video by devfs */ + 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); + + /* clean up async I/O users */ + dv1394_fasync(-1, file, 0); + + /* 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 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. + */ +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); + +static struct hpsb_protocol_driver dv1394_driver = { + .name = "DV/1394 Driver", + .id_table = dv1394_id_table, + .driver = { + .name = "dv1394", + .bus = &ieee1394_bus_type, + }, +}; + + +/*** 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 = kmalloc(sizeof(struct video_card), GFP_KERNEL); + if (!video) { + printk(KERN_ERR "dv1394: cannot allocate video_card\n"); + goto err; + } + + memset(video, 0, sizeof(struct video_card)); + + 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; + init_MUTEX(&video->sem); + 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); + + if (devfs_mk_cdev(MKDEV(IEEE1394_MAJOR, + IEEE1394_MINOR_BLOCK_DV1394*16 + video->id), + S_IFCHR|S_IRUGO|S_IWUGO, + "ieee1394/dv/host%d/%s/%s", + (video->id>>2), + (video->pal_or_ntsc == DV1394_NTSC ? "NTSC" : "PAL"), + (video->mode == MODE_RECEIVE ? "in" : "out")) < 0) + goto err_free; + + debug_printk("dv1394: dv1394_init() OK on ID %d\n", video->id); + + return 0; + + err_free: + kfree(video); + err: + return -1; +} + +static void dv1394_un_init(struct video_card *video) +{ + char buf[32]; + + /* obviously nobody has the driver open at this point */ + do_dv1394_shutdown(video, 1); + snprintf(buf, sizeof(buf), "dv/host%d/%s/%s", (video->id >> 2), + (video->pal_or_ntsc == DV1394_NTSC ? "NTSC" : "PAL"), + (video->mode == MODE_RECEIVE ? "in" : "out") + ); + + devfs_remove("ieee1394/%s", buf); + kfree(video); +} + + +static void dv1394_remove_host (struct hpsb_host *host) +{ + struct video_card *video; + unsigned long flags; + int id = host->id; + + /* We only work with the OHCI-1394 driver */ + if (strcmp(host->driver->name, OHCI1394_DRIVER_NAME)) + return; + + /* find the corresponding video_cards */ + do { + struct video_card *tmp_vid; + + video = NULL; + + spin_lock_irqsave(&dv1394_cards_lock, flags); + list_for_each_entry(tmp_vid, &dv1394_cards, list) { + if ((tmp_vid->id >> 2) == id) { + list_del(&tmp_vid->list); + video = tmp_vid; + break; + } + } + spin_unlock_irqrestore(&dv1394_cards_lock, flags); + + if (video) + dv1394_un_init(video); + } while (video != NULL); + + class_simple_device_remove(MKDEV( + IEEE1394_MAJOR, IEEE1394_MINOR_BLOCK_DV1394 * 16 + (id<<2))); + devfs_remove("ieee1394/dv/host%d/NTSC", id); + devfs_remove("ieee1394/dv/host%d/PAL", id); + devfs_remove("ieee1394/dv/host%d", id); +} + +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; + + class_simple_device_add(hpsb_protocol_class, MKDEV( + IEEE1394_MAJOR, IEEE1394_MINOR_BLOCK_DV1394 * 16 + (id<<2)), + NULL, "dv1394-%d", id); + devfs_mk_dir("ieee1394/dv/host%d", id); + devfs_mk_dir("ieee1394/dv/host%d/NTSC", id); + devfs_mk_dir("ieee1394/dv/host%d/PAL", 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); + devfs_remove("ieee1394/dv"); +} + +static int __init dv1394_init_module(void) +{ + int ret; + + cdev_init(&dv1394_cdev, &dv1394_fops); + dv1394_cdev.owner = THIS_MODULE; + kobject_set_name(&dv1394_cdev.kobj, "dv1394"); + ret = cdev_add(&dv1394_cdev, IEEE1394_DV1394_DEV, 16); + if (ret) { + printk(KERN_ERR "dv1394: unable to register character device\n"); + return ret; + } + + devfs_mk_dir("ieee1394/dv"); + + 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); + devfs_remove("ieee1394/dv"); + cdev_del(&dv1394_cdev); + return ret; + } + + return 0; +} + +module_init(dv1394_init_module); +module_exit(dv1394_exit_module); +MODULE_ALIAS_CHARDEV(IEEE1394_MAJOR, IEEE1394_MINOR_BLOCK_DV1394 * 16); 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..654da76 --- /dev/null +++ b/drivers/ieee1394/eth1394.c @@ -0,0 +1,1801 @@ +/* + * eth1394.c -- Ethernet 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. This driver + * will ultimately support that method, but currently falls short in + * several areas. + * + * 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/sched.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/netdevice.h> +#include <linux/inetdevice.h> +#include <linux/etherdevice.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/semaphore.h> +#include <net/arp.h> + +#include "csr1212.h" +#include "ieee1394_types.h" +#include "ieee1394_core.h" +#include "ieee1394_transactions.h" +#include "ieee1394.h" +#include "highlevel.h" +#include "iso.h" +#include "nodemgr.h" +#include "eth1394.h" +#include "config_roms.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) + +#define DEBUG(fmt, args...) \ + printk(KERN_ERR "%s:%s[%d]: " fmt "\n", driver_name, __FUNCTION__, __LINE__, ## args) +#define TRACE() printk(KERN_ERR "%s:%s[%d] ---- TRACE\n", driver_name, __FUNCTION__, __LINE__) + +static char version[] __devinitdata = + "$Rev: 1247 $ Ben Collins <bcollins@debian.org>"; + +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 */ +}; + +/* Our ieee1394 highlevel driver */ +#define ETH1394_DRIVER_NAME "eth1394" +static const char driver_name[] = ETH1394_DRIVER_NAME; + +static kmem_cache_t *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) +}; + +/* Change this to IEEE1394_SPEED_S100 to make testing easier */ +#define ETH1394_SPEED_DEF IEEE1394_SPEED_MAX + +/* For now, this needs to be 1500, so that XP works with us */ +#define ETH1394_DATA_LEN ETH_DATA_LEN + +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, void *daddr, void *saddr, + unsigned len); +static int ether1394_rebuild_header(struct sk_buff *skb); +static int ether1394_header_parse(struct sk_buff *skb, unsigned char *haddr); +static int ether1394_header_cache(struct neighbour *neigh, struct hh_cache *hh); +static void ether1394_header_cache_update(struct hh_cache *hh, + struct net_device *dev, + unsigned char * haddr); +static int ether1394_mac_addr(struct net_device *dev, void *p); + +static void purge_partial_datagram(struct list_head *old); +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, +}; + + +/* This is called after an "ifup" */ +static int ether1394_open (struct net_device *dev) +{ + struct eth1394_priv *priv = netdev_priv(dev); + int ret = 0; + + /* Something bad happened, don't even try */ + if (priv->bc_state == ETHER1394_BC_ERROR) { + /* we'll try again */ + priv->iso = hpsb_iso_recv_init(priv->host, + ETHER1394_GASP_BUFFERS * 2 * + (1 << (priv->host->csr.max_rec + + 1)), + ETHER1394_GASP_BUFFERS, + priv->broadcast_channel, + HPSB_ISO_DMA_PACKET_PER_BUFFER, + 1, ether1394_iso); + if (priv->iso == NULL) { + ETH1394_PRINT(KERN_ERR, dev->name, + "Could not allocate isochronous receive " + "context for the broadcast channel\n"); + priv->bc_state = ETHER1394_BC_ERROR; + ret = -EAGAIN; + } else { + 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; + } + } + + if (ret) + return ret; + + netif_start_queue (dev); + return 0; +} + +/* This is called after an "ifdown" */ +static int ether1394_stop (struct net_device *dev) +{ + 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); +} + +/* 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) +{ + ETH1394_PRINT (KERN_ERR, dev->name, "Timeout, resetting host %s\n", + ((struct eth1394_priv *)netdev_priv(dev))->host->driver->name); + + highlevel_host_reset (((struct eth1394_priv *)netdev_priv(dev))->host); + + netif_wake_queue (dev); +} + +static int ether1394_change_mtu(struct net_device *dev, int new_mtu) +{ + struct eth1394_priv *priv = netdev_priv(dev); + + if ((new_mtu < 68) || + (new_mtu > min(ETH1394_DATA_LEN, + (int)((1 << (priv->host->csr.max_rec + 1)) - + (sizeof(union eth1394_hdr) + + ETHER1394_GASP_OVERHEAD))))) + return -EINVAL; + dev->mtu = new_mtu; + return 0; +} + +static void purge_partial_datagram(struct list_head *old) +{ + struct partial_datagram *pd = list_entry(old, struct partial_datagram, list); + struct list_head *lh, *n; + + list_for_each_safe(lh, n, &pd->frag_info) { + struct fragment_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_probe(struct device *dev) +{ + struct unit_directory *ud; + struct eth1394_host_info *hi; + struct eth1394_priv *priv; + struct eth1394_node_ref *new_node; + struct eth1394_node_info *node_info; + + ud = container_of(dev, struct unit_directory, device); + + hi = hpsb_get_hostinfo(ð1394_highlevel, ud->ne->host); + if (!hi) + return -ENOENT; + + new_node = kmalloc(sizeof(struct eth1394_node_ref), + in_interrupt() ? GFP_ATOMIC : GFP_KERNEL); + if (!new_node) + return -ENOMEM; + + node_info = kmalloc(sizeof(struct eth1394_node_info), + in_interrupt() ? GFP_ATOMIC : 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 = ETHER1394_INVALID_ADDR; + + 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_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(ð1394_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) { + 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; + struct eth1394_node_info *node_info; + + hi = hpsb_get_hostinfo(ð1394_highlevel, ud->ne->host); + if (!hi) + return -ENOENT; + + priv = netdev_priv(hi->dev); + + node = eth1394_find_node(&priv->ip_node_list, ud); + + if (!node) { + node = kmalloc(sizeof(struct eth1394_node_ref), + in_interrupt() ? GFP_ATOMIC : GFP_KERNEL); + if (!node) + return -ENOMEM; + + node_info = kmalloc(sizeof(struct eth1394_node_info), + in_interrupt() ? GFP_ATOMIC : GFP_KERNEL); + if (!node_info) { + kfree(node); + return -ENOMEM; + } + + spin_lock_init(&node_info->pdg.lock); + INIT_LIST_HEAD(&node_info->pdg.list); + node_info->pdg.sz = 0; + + ud->device.driver_data = node_info; + node->ud = ud; + + priv = netdev_priv(hi->dev); + list_add_tail(&node->list, &priv->ip_node_list); + } + + return 0; +} + + +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 = "IPv4 over 1394 Driver", + .id_table = eth1394_id_table, + .update = eth1394_update, + .driver = { + .name = ETH1394_DRIVER_NAME, + .bus = &ieee1394_bus_type, + .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 = *((u64*)&(host->csr.rom->bus_info_data[3])); + u16 maxpayload = 1 << (host->csr.max_rec + 1); + int max_speed = IEEE1394_SPEED_MAX; + + spin_lock_irqsave (&priv->lock, flags); + + memset(priv->ud_list, 0, sizeof(struct node_entry*) * ALL_NODES); + priv->bc_maxpayload = 512; + + /* Determine speed limit */ + for (i = 0; i < host->node_count; i++) + if (max_speed > host->speed_map[NODEID_TO_NODE(host->node_id) * + 64 + i]) + max_speed = host->speed_map[NODEID_TO_NODE(host->node_id) * + 64 + i]; + priv->bc_sspd = max_speed; + + /* We'll use our maxpayload as the default mtu */ + if (set_mtu) { + dev->mtu = min(ETH1394_DATA_LEN, + (int)(maxpayload - + (sizeof(union eth1394_hdr) + + ETHER1394_GASP_OVERHEAD))); + + /* Set our hardware address while we're at it */ + *(u64*)dev->dev_addr = guid; + *(u64*)dev->broadcast = ~0x0ULL; + } + + spin_unlock_irqrestore (&priv->lock, flags); +} + +/* This function is called right before register_netdev */ +static void ether1394_init_dev (struct net_device *dev) +{ + /* Our functions */ + 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->hard_header = ether1394_header; + dev->rebuild_header = ether1394_rebuild_header; + dev->hard_header_cache = ether1394_header_cache; + dev->header_cache_update= ether1394_header_cache_update; + dev->hard_header_parse = ether1394_header_parse; + dev->set_mac_address = ether1394_mac_addr; + SET_ETHTOOL_OPS(dev, ðtool_ops); + + /* Some constants */ + 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; + + ether1394_reset_priv (dev, 1); +} + +/* + * 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; + static int version_printed = 0; + u64 fifo_addr; + + if (!(host->config_roms & HPSB_CONFIG_ROM_ENTRY_IP1394)) + return; + + fifo_addr = hpsb_allocate_and_register_addrspace(ð1394_highlevel, + host, + &addr_ops, + ETHER1394_REGION_ADDR_LEN, + ETHER1394_REGION_ADDR_LEN, + -1, -1); + if (fifo_addr == ~0ULL) + goto out; + + if (version_printed++ == 0) + ETH1394_PRINT_G (KERN_INFO, "%s\n", version); + + /* We should really have our own alloc_hpsbdev() function in + * net_init.c instead of calling the one for ethernet then hijacking + * it for ourselves. That way we'd be a real networking device. */ + dev = alloc_etherdev(sizeof (struct eth1394_priv)); + + if (dev == NULL) { + ETH1394_PRINT_G (KERN_ERR, "Out of memory trying to allocate " + "etherdevice for IEEE 1394 device %s-%d\n", + host->driver->name, host->id); + goto out; + } + + SET_MODULE_OWNER(dev); + 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; + + hi = hpsb_create_hostinfo(ð1394_highlevel, host, sizeof(*hi)); + + if (hi == NULL) { + ETH1394_PRINT_G (KERN_ERR, "Out of memory trying to create " + "hostinfo for IEEE 1394 device %s-%d\n", + host->driver->name, host->id); + goto out; + } + + ether1394_init_dev(dev); + + if (register_netdev (dev)) { + ETH1394_PRINT (KERN_ERR, dev->name, "Error registering network driver\n"); + goto out; + } + + ETH1394_PRINT (KERN_INFO, dev->name, "IEEE-1394 IPv4 over 1394 Ethernet (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; + + priv->iso = hpsb_iso_recv_init(host, (ETHER1394_GASP_BUFFERS * 2 * + (1 << (host->csr.max_rec + 1))), + ETHER1394_GASP_BUFFERS, + priv->broadcast_channel, + HPSB_ISO_DMA_PACKET_PER_BUFFER, + 1, ether1394_iso); + if (priv->iso == NULL) { + ETH1394_PRINT(KERN_ERR, dev->name, + "Could not allocate isochronous receive context " + "for the broadcast channel\n"); + priv->bc_state = ETHER1394_BC_ERROR; + } else { + 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; + +out: + if (dev != NULL) + free_netdev(dev); + if (hi) + hpsb_destroy_hostinfo(ð1394_highlevel, host); + + return; +} + +/* Remove a card from our list */ +static void ether1394_remove_host (struct hpsb_host *host) +{ + struct eth1394_host_info *hi; + + hi = hpsb_get_hostinfo(ð1394_highlevel, host); + if (hi != NULL) { + struct eth1394_priv *priv = netdev_priv(hi->dev); + + hpsb_unregister_addrspace(ð1394_highlevel, host, + priv->local_fifo); + + if (priv->iso != NULL) + hpsb_iso_shutdown(priv->iso); + + if (hi->dev) { + unregister_netdev (hi->dev); + free_netdev(hi->dev); + } + } + + return; +} + +/* A reset has just arisen */ +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(ð1394_highlevel, host); + + /* This can happen for hosts that we don't use */ + if (hi == NULL) + 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 = (struct eth1394_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, void *daddr, 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; + struct net_device *dev = skb->dev; + + switch (eth->h_proto) { + +#ifdef CONFIG_INET + case __constant_htons(ETH_P_IP): + return arp_find((unsigned char*)ð->h_dest, skb); +#endif + default: + ETH1394_PRINT(KERN_DEBUG, dev->name, + "unable to resolve type %04x addresses.\n", + eth->h_proto); + break; + } + + return 0; +} + +static int ether1394_header_parse(struct sk_buff *skb, unsigned char *haddr) +{ + struct net_device *dev = skb->dev; + memcpy(haddr, dev->dev_addr, ETH1394_ALEN); + return ETH1394_ALEN; +} + + +static int ether1394_header_cache(struct neighbour *neigh, struct hh_cache *hh) +{ + unsigned short type = hh->hh_type; + struct eth1394hdr *eth = (struct eth1394hdr*)(((u8*)hh->hh_data) + + (16 - ETH1394_HLEN)); + struct net_device *dev = neigh->dev; + + if (type == __constant_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, + struct net_device *dev, + unsigned char * haddr) +{ + memcpy(((u8*)hh->hh_data) + (16 - ETH1394_HLEN), haddr, dev->addr_len); +} + +static int ether1394_mac_addr(struct net_device *dev, void *p) +{ + if (netif_running(dev)) + return -EBUSY; + + /* Not going to allow setting the MAC address, we really need to use + * the real one supplied by the hardware */ + return -EINVAL; + } + + + +/****************************************** + * Datagram reception code + ******************************************/ + +/* Copied from net/ethernet/eth.c */ +static inline u16 ether1394_type_trans(struct sk_buff *skb, + struct net_device *dev) +{ + struct eth1394hdr *eth; + unsigned char *rawp; + + skb->mac.raw = skb->data; + 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 inline 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 == __constant_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; + + /* 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))); + + node = eth1394_find_node_guid(&priv->ip_node_list, + be64_to_cpu(arp1394->s_uniq_id)); + 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 == 1) + /* just set ARP req target unique ID to 0 */ + *((u64*)arp_ptr) = 0; + else + *((u64*)arp_ptr) = *((u64*)dev->dev_addr); + } + + /* Now add the ethernet header. */ + if (dev->hard_header (skb, dev, __constant_ntohs (ether_type), + &dest_hw, NULL, skb->len) >= 0) + ret = ether1394_type_trans(skb, dev); + + return ret; +} + +static inline int fragment_overlap(struct list_head *frag_list, int offset, int len) +{ + struct fragment_info *fi; + + list_for_each_entry(fi, frag_list, list) { + if ( ! ((offset > (fi->offset + fi->len - 1)) || + ((offset + len - 1) < fi->offset))) + return 1; + } + return 0; +} + +static inline 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 inline 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(struct fragment_info), GFP_ATOMIC); + if (!new) + return -ENOMEM; + + new->offset = offset; + new->len = len; + + list_add(&new->list, lh); + + return 0; +} + +static inline 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(struct partial_datagram), 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 inline 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_del(lh); + list_add(lh, pdgl); + + return 0; +} + +static inline int is_datagram_complete(struct list_head *lh, int dg_size) +{ + struct partial_datagram *pd = list_entry(lh, struct partial_datagram, list); + struct fragment_info *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 (!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 (!skb) { + HPSB_PRINT (KERN_ERR, "ether1394 rx: low on mem\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 { + struct partial_datagram *pd; + + 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); + goto bad_proto; + } + + if (netif_rx(skb) == NET_RX_DROP) { + priv->stats.rx_errors++; + priv->stats.rx_dropped++; + goto bad_proto; + } + + /* Statistics */ + priv->stats.rx_packets++; + priv->stats.rx_bytes += skb->len; + +bad_proto: + if (netif_queue_stopped(dev)) + netif_wake_queue(dev); + spin_unlock_irqrestore(&priv->lock, flags); + + 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(ð1394_highlevel, host); + if (hi == NULL) { + ETH1394_PRINT_G(KERN_ERR, "Could not find net device for host %s\n", + host->driver->name); + 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(ð1394_highlevel, iso->host); + if (hi == NULL) { + ETH1394_PRINT_G(KERN_ERR, "Could not find net device for host %s\n", + iso->host->driver->name); + 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 inline 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; + + /* Believe it or not, all that need to happen is sender IP get moved + * and set hw_addr_len, max_rec, sspd, fifo_hi and fifo_lo. */ + 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); + + return; +} + +/* We need to encapsulate the standard header with our own. We use the + * ethernet header's proto for our own. */ +static inline unsigned int ether1394_encapsulate_prep(unsigned int max_payload, + int 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((dg_size + (adj_max_payload - 1)) / adj_max_payload); +} + +static inline 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 inline 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 inline 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; + p->data = NULL; + + p->tcode = TCODE_WRITEB; + p->header[1] = (host->node_id << 16) | (addr >> 32); + p->header[2] = addr & 0xffffffff; + + p->header_size = 16; + p->expect_response = 1; + + if (hpsb_get_tlabel(p)) { + ETH1394_PRINT_G(KERN_ERR, "No more tlabels left while sending " + "to node " NODE_BUS_FMT "\n", NODE_BUS_ARGS(host, node)); + return -1; + } + p->header[0] = (p->node_id << 16) | (p->tlabel << 10) + | (1 << 8) | (TCODE_WRITEB << 4); + + p->header[3] = tx_len << 16; + p->data_size = (tx_len + 3) & ~3; + p->data = (quadlet_t*)data; + + return 0; +} + +static inline 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] = __constant_cpu_to_be32((ETHER1394_GASP_SPECIFIER_ID_LO << 24) | + ETHER1394_GASP_VERSION); + + /* Setting the node id to ALL_NODES (not LOCAL_BUS | ALL_NODES) + * prevents hpsb_send_packet() from setting the speed to an arbitrary + * value based on packet->node_id if packet->node_id is not set. */ + p->node_id = ALL_NODES; + p->speed_code = priv->bc_sspd; +} + +static inline 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 -1; + + 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 -1; + } + + 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 -1; + } + + return 0; +} + + +/* Task function to be run when a datagram transmission is completed */ +static inline void ether1394_dg_complete(struct packet_task *ptask, int fail) +{ + struct sk_buff *skb = ptask->skb; + struct net_device *dev = skb->dev; + struct eth1394_priv *priv = netdev_priv(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; + + /* Add the encapsulation header to the fragment */ + tx_len = ether1394_encapsulate(ptask->skb, ptask->max_payload, + &ptask->hdr); + if (ether1394_send_packet(ptask, tx_len)) + 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) +{ + int kmflags = in_interrupt() ? GFP_ATOMIC : GFP_KERNEL; + struct eth1394hdr *eth; + struct eth1394_priv *priv = netdev_priv(dev); + int proto; + unsigned long flags; + nodeid_t dest_node; + eth1394_tx_type tx_type; + int ret = 0; + 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, kmflags); + if (ptask == NULL) { + ret = -ENOMEM; + 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) { + ret = -EAGAIN; + goto fail; + } +#endif + + if ((skb = skb_share_check (skb, kmflags)) == NULL) { + ret = -ENOMEM; + goto fail; + } + + /* Get rid of the fake eth1394 header, but save a pointer */ + eth = (struct eth1394hdr*)skb->data; + skb_pull(skb, ETH1394_HLEN); + + proto = eth->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(eth->h_dest, dev->broadcast, ETH1394_ALEN) == 0 || + proto == __constant_htons(ETH_P_ARP) || + (proto == __constant_htons(ETH_P_IP) && + IN_MULTICAST(__constant_ntohl(skb->nh.iph->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 { + node = eth1394_find_node_guid(&priv->ip_node_list, + be64_to_cpu(*(u64*)eth->h_dest)); + if (!node) { + ret = -EAGAIN; + goto fail; + } + node_info = (struct eth1394_node_info*)node->ud->device.driver_data; + if (node_info->fifo == ETHER1394_INVALID_ADDR) { + ret = -EAGAIN; + 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 == __constant_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)) + goto fail; + + netif_wake_queue(dev); + return 0; +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); + + if (netif_queue_stopped(dev)) + netif_wake_queue(dev); + + return 0; /* returning non-zero causes serious problems */ +} + +static void ether1394_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) +{ + strcpy (info->driver, driver_name); + strcpy (info->version, "$Rev: 1247 $"); + /* FIXME XXX provide sane businfo */ + strcpy (info->bus_info, "ieee1394"); +} + +static struct ethtool_ops ethtool_ops = { + .get_drvinfo = ether1394_get_drvinfo +}; + +static int __init ether1394_init_module (void) +{ + packet_task_cache = kmem_cache_create("packet_task", sizeof(struct packet_task), + 0, 0, NULL, NULL); + + /* Register ourselves as a highlevel driver */ + hpsb_register_highlevel(ð1394_highlevel); + + return hpsb_register_protocol(ð1394_proto_driver); +} + +static void __exit ether1394_exit_module (void) +{ + hpsb_unregister_protocol(ð1394_proto_driver); + hpsb_unregister_highlevel(ð1394_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..ed8f1c4 --- /dev/null +++ b/drivers/ieee1394/eth1394.h @@ -0,0 +1,236 @@ +/* + * 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 "ieee1394.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 + +#define ETHER1394_INVALID_ADDR ~0ULL + +/* GASP identifier numbers for IPv4 over IEEE 1394 */ +#define ETHER1394_GASP_SPECIFIER_ID 0x00005E +#define ETHER1394_GASP_SPECIFIER_ID_HI ((ETHER1394_GASP_SPECIFIER_ID >> 8) & 0xffff) +#define ETHER1394_GASP_SPECIFIER_ID_LO (ETHER1394_GASP_SPECIFIER_ID & 0xff) +#define ETHER1394_GASP_VERSION 1 + +#define ETHER1394_GASP_OVERHEAD (2 * sizeof(quadlet_t)) /* GASP header overhead */ + +#define ETHER1394_GASP_BUFFERS 16 + +/* Node set == 64 */ +#define NODE_SET (ALL_NODES + 1) + +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 */ +}; + + +/* 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)); + +#ifdef __KERNEL__ +#include <linux/skbuff.h> + +static inline struct eth1394hdr *eth1394_hdr(const struct sk_buff *skb) +{ + return (struct eth1394hdr *)skb->mac.raw; +} +#endif + +typedef enum {ETH1394_GASP, ETH1394_WRREQ} eth1394_tx_type; + +/* IP1394 headers */ +#include <asm/byteorder.h> + +/* 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..997e1bf --- /dev/null +++ b/drivers/ieee1394/highlevel.c @@ -0,0 +1,704 @@ +/* + * 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/config.h> +#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); + +/* addr_space list will have zero and max already included as bounds */ +static struct hpsb_address_ops dummy_ops = { NULL, NULL, NULL, NULL }; +static struct hpsb_address_serve dummy_zero_addr, dummy_max_addr; + + +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; +} + + +/* Returns a per host/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); + + if (hi) + return hi->data; + + return NULL; +} + + +/* If 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 = kmalloc(sizeof(*hi) + data_size, GFP_ATOMIC); + if (!hi) + return NULL; + + memset(hi, 0, sizeof(*hi) + data_size); + + 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; +} + + +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; +} + + +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; +} + + +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; +} + + +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) { + if (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; +} + +void hpsb_register_highlevel(struct hpsb_highlevel *hl) +{ + INIT_LIST_HEAD(&hl->addr_list); + INIT_LIST_HEAD(&hl->host_info_list); + + rwlock_init(&hl->host_info_lock); + + down_write(&hl_drivers_sem); + list_add_tail(&hl->hl_list, &hl_drivers); + up_write(&hl_drivers_sem); + + write_lock(&hl_irqs_lock); + list_add_tail(&hl->irq_list, &hl_irqs); + write_unlock(&hl_irqs_lock); + + 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) { + if (hpsb_update_config_rom_image(host) < 0) { + HPSB_ERR("Failed to generate Configuration ROM image for host " + "%s-%d", hl->name, host->id); + } + } + + /* And 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; +} + +void hpsb_unregister_highlevel(struct hpsb_highlevel *hl) +{ + write_lock(&hl_irqs_lock); + list_del(&hl->irq_list); + write_unlock(&hl_irqs_lock); + + 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); +} + +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 = ~0ULL; + unsigned long flags; + u64 align_mask = ~(alignment - 1); + + if ((alignment & 3) || (alignment > 0x800000000000ULL) || + ((hweight32(alignment >> 32) + + hweight32(alignment & 0xffffffff) != 1))) { + HPSB_ERR("%s called with invalid alignment: 0x%048llx", + __FUNCTION__, (unsigned long long)alignment); + return retval; + } + + if (start == ~0ULL && end == ~0ULL) { + start = CSR1212_ALL_SPACE_BASE + 0xffff00000000ULL; /* ohci1394.c limit */ + end = CSR1212_ALL_SPACE_END; + } + + if (((start|end) & ~align_mask) || (start >= end) || (end > 0x1000000000000ULL)) { + HPSB_ERR("%s called with invalid addresses (start = %012Lx end = %012Lx)", + __FUNCTION__, (unsigned long long)start, (unsigned long long)end); + return retval; + } + + as = (struct hpsb_address_serve *) + kmalloc(sizeof(struct hpsb_address_serve), GFP_KERNEL); + if (as == NULL) { + 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 == ~0ULL) { + kfree(as); + } + + return retval; +} + +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 > 0x1000000000000ULL)) { + HPSB_ERR("%s called with invalid addresses", __FUNCTION__); + return 0; + } + + as = (struct hpsb_address_serve *) + kmalloc(sizeof(struct hpsb_address_serve), GFP_ATOMIC); + if (as == NULL) { + 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; +} + +int hpsb_listen_channel(struct hpsb_highlevel *hl, struct hpsb_host *host, + unsigned int channel) +{ + if (channel > 63) { + HPSB_ERR("%s called with invalid channel", __FUNCTION__); + return -EINVAL; + } + + if (host->iso_listen_count[channel]++ == 0) { + return host->driver->devctl(host, ISO_LISTEN_CHANNEL, channel); + } + + return 0; +} + +void hpsb_unlisten_channel(struct hpsb_highlevel *hl, struct hpsb_host *host, + unsigned int channel) +{ + if (channel > 63) { + HPSB_ERR("%s called with invalid channel", __FUNCTION__); + return; + } + + if (--host->iso_listen_count[channel] == 0) { + host->driver->devctl(host, ISO_UNLISTEN_CHANNEL, channel); + } +} + +static void init_hpsb_highlevel(struct hpsb_host *host) +{ + INIT_LIST_HEAD(&dummy_zero_addr.host_list); + INIT_LIST_HEAD(&dummy_zero_addr.hl_list); + INIT_LIST_HEAD(&dummy_max_addr.host_list); + INIT_LIST_HEAD(&dummy_max_addr.hl_list); + + dummy_zero_addr.op = dummy_max_addr.op = &dummy_ops; + + dummy_zero_addr.start = dummy_zero_addr.end = 0; + dummy_max_addr.start = dummy_max_addr.end = ((u64) 1) << 48; + + list_add_tail(&dummy_zero_addr.host_list, &host->addr_space); + list_add_tail(&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) { + if (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) +{ + struct hpsb_highlevel *hl; + + read_lock(&hl_irqs_lock); + list_for_each_entry(hl, &hl_irqs, irq_list) { + if (hl->host_reset) + hl->host_reset(host); + } + read_unlock(&hl_irqs_lock); +} + +void highlevel_iso_receive(struct hpsb_host *host, void *data, size_t length) +{ + struct hpsb_highlevel *hl; + int channel = (((quadlet_t *)data)[0] >> 8) & 0x3f; + + read_lock(&hl_irqs_lock); + list_for_each_entry(hl, &hl_irqs, irq_list) { + if (hl->iso_receive) + hl->iso_receive(host, channel, data, length); + } + read_unlock(&hl_irqs_lock); +} + +void highlevel_fcp_request(struct hpsb_host *host, int nodeid, int direction, + void *data, size_t length) +{ + struct hpsb_highlevel *hl; + int cts = ((quadlet_t *)data)[0] >> 4; + + read_lock(&hl_irqs_lock); + list_for_each_entry(hl, &hl_irqs, irq_list) { + if (hl->fcp_request) + hl->fcp_request(host, nodeid, direction, cts, data, + length); + } + read_unlock(&hl_irqs_lock); +} + +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..e119fb8 --- /dev/null +++ b/drivers/ieee1394/highlevel.h @@ -0,0 +1,190 @@ + +#ifndef IEEE1394_HIGHLEVEL_H +#define IEEE1394_HIGHLEVEL_H + + +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; + + /* first address handled and first address behind, quadlet aligned */ + u64 start, end; +}; + + +/* + * The above structs are internal to highlevel driver handling. Only the + * following structures are of interest to actual highlevel drivers. + */ + +struct hpsb_highlevel { + struct module *owner; + const char *name; + + /* Any of the following pointers can legally be NULL, except for + * iso_receive which can only be NULL when you don't request + * channels. */ + + /* 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); + + /* An isochronous packet was received. Channel contains the channel + * number for your convenience, it is also contained in the included + * packet header (first quadlet, CRCs are missing). You may get called + * for channel/host combinations you did not request. */ + void (*iso_receive) (struct hpsb_host *host, int channel, + quadlet_t *data, size_t length); + + /* 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 + later case, no response will be sent and the driver, that 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); + + +/* 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 + later case, no response will be sent and the driver, that 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); +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_iso_receive(struct hpsb_host *host, void *data, + size_t length); +void highlevel_fcp_request(struct hpsb_host *host, int nodeid, int direction, + void *data, size_t length); + + +/* + * Register highlevel driver. The name pointer has to stay valid at all times + * because the string is not copied. + */ +void hpsb_register_highlevel(struct hpsb_highlevel *hl); +void hpsb_unregister_highlevel(struct hpsb_highlevel *hl); + +/* + * Register handlers for host address spaces. 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 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. There is no unregister function, + * all address spaces are deallocated together with the hpsb_highlevel. + */ +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); + +/* + * Enable or disable receving a certain isochronous channel through the + * iso_receive op. + */ +int hpsb_listen_channel(struct hpsb_highlevel *hl, struct hpsb_host *host, + unsigned int channel); +void hpsb_unlisten_channel(struct hpsb_highlevel *hl, struct hpsb_host *host, + unsigned int channel); + + +/* Retrieve a hostinfo pointer bound to this driver/host */ +void *hpsb_get_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host); + +/* Allocate a hostinfo pointer of data_size bound to this driver/host */ +void *hpsb_create_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host, + size_t data_size); + +/* Free and remove the hostinfo pointer bound to this driver/host */ +void hpsb_destroy_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host); + +/* Set an alternate lookup key for the hostinfo bound to this driver/host */ +void hpsb_set_hostinfo_key(struct hpsb_highlevel *hl, struct hpsb_host *host, unsigned long key); + +/* Retrieve the alternate lookup key for the hostinfo bound to this driver/host */ +unsigned long hpsb_get_hostinfo_key(struct hpsb_highlevel *hl, struct hpsb_host *host); + +/* Retrieve a hostinfo pointer bound to this driver using its alternate key */ +void *hpsb_get_hostinfo_bykey(struct hpsb_highlevel *hl, unsigned long key); + +/* 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); + +/* Retrieve hpsb_host using a highlevel handle and a key */ +struct hpsb_host *hpsb_get_host_bykey(struct hpsb_highlevel *hl, unsigned long key); + +#endif /* IEEE1394_HIGHLEVEL_H */ diff --git a/drivers/ieee1394/hosts.c b/drivers/ieee1394/hosts.c new file mode 100644 index 0000000..c502c6e --- /dev/null +++ b/drivers/ieee1394/hosts.c @@ -0,0 +1,233 @@ +/* + * 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/config.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/list.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/pci.h> +#include <linux/timer.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(void * __reset_info) +{ + struct hpsb_host *host = (struct hpsb_host*)__reset_info; + 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; +} + +/** + * 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 succesful, %NULL if + * no memory was available. + */ +static DECLARE_MUTEX(host_num_alloc); + +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 = kmalloc(sizeof(struct hpsb_host) + extra, SLAB_KERNEL); + if (!h) return NULL; + memset(h, 0, sizeof(struct hpsb_host) + extra); + + h->csr.rom = csr1212_create_csr(&csr_bus_ops, CSR_BUS_INFO_SIZE, h); + if (!h->csr.rom) { + kfree(h); + return NULL; + } + + h->hostdata = h + 1; + h->driver = drv; + + skb_queue_head_init(&h->pending_packet_queue); + INIT_LIST_HEAD(&h->addr_space); + + for (i = 2; i < 16; i++) + h->csr.gen_timestamp[i] = jiffies - 60 * HZ; + + for (i = 0; i < ARRAY_SIZE(h->tpool); i++) + HPSB_TPOOL_INIT(&h->tpool[i]); + + atomic_set(&h->generation, 0); + + INIT_WORK(&h->delayed_reset, delayed_reset_bus, h); + + init_timer(&h->timeout); + h->timeout.data = (unsigned long) h; + h->timeout.function = abort_timedouts; + h->timeout_interval = HZ / 20; // 50ms by default + + h->topology_map = h->csr.topology_map + 3; + h->speed_map = (u8 *)(h->csr.speed_map + 2); + + down(&host_num_alloc); + + while (nodemgr_for_each_host(&hostnum, alloc_hostnum_cb)) + hostnum++; + + h->id = hostnum; + + memcpy(&h->device, &nodemgr_dev_template_host, sizeof(h->device)); + h->device.parent = dev; + snprintf(h->device.bus_id, BUS_ID_SIZE, "fw-host%d", h->id); + + h->class_dev.dev = &h->device; + h->class_dev.class = &hpsb_host_class; + snprintf(h->class_dev.class_id, BUS_ID_SIZE, "fw-host%d", h->id); + + device_register(&h->device); + class_device_register(&h->class_dev); + get_device(&h->device); + + up(&host_num_alloc); + + return h; +} + +int hpsb_add_host(struct hpsb_host *host) +{ + if (hpsb_default_host_entry(host)) + return -ENOMEM; + + hpsb_add_extra_config_roms(host); + + highlevel_add_host(host); + + return 0; +} + +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); + + hpsb_remove_extra_config_roms(host); + + class_device_unregister(&host->class_dev); + device_unregister(&host->device); +} + +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 (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_WORK(&host->delayed_reset, delayed_reset_bus, host); + 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..739e768 --- /dev/null +++ b/drivers/ieee1394/hosts.h @@ -0,0 +1,215 @@ +#ifndef _IEEE1394_HOSTS_H +#define _IEEE1394_HOSTS_H + +#include <linux/device.h> +#include <linux/wait.h> +#include <linux/list.h> +#include <linux/timer.h> +#include <linux/skbuff.h> + +#include <asm/semaphore.h> + +#include "ieee1394_types.h" +#include "csr.h" + + +struct hpsb_packet; +struct hpsb_iso; + +struct hpsb_host { + struct list_head host_list; + + void *hostdata; + + atomic_t generation; + + struct sk_buff_head pending_packet_queue; + + struct timer_list timeout; + unsigned long timeout_interval; + + unsigned char iso_listen_count[64]; + + int node_count; /* number of identified nodes on this bus */ + int selfid_count; /* total number of SelfIDs received */ + int nodes_active; /* number of nodes that are actually active */ + + 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; + + /* 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; + struct csr_control csr; + + /* Per node tlabel pool allocation */ + struct hpsb_tlabel_pool tpool[64]; + + struct hpsb_host_driver *driver; + + struct pci_dev *pdev; + + int id; + + struct device device; + struct class_device class_dev; + + int update_config_rom; + struct work_struct delayed_reset; + + unsigned int config_roms; + + struct list_head addr_space; +}; + + + +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, + + /* Start or stop receiving isochronous channel in arg. Return void. + * This acts as an optimization hint, hosts are not required not to + * listen on unrequested channels. */ + ISO_LISTEN_CHANNEL, + ISO_UNLISTEN_CHANNEL +}; + +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 IEEE 1394 controllers */ + LONG_RESET, + + /* Short (arbitrated) reset -- only available on 1394a capable + IEEE 1394 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_remove_host(struct hpsb_host *h); + +/* The following 2 functions are deprecated and will be removed when the + * raw1394/libraw1394 update is complete. */ +int hpsb_update_config_rom(struct hpsb_host *host, + const quadlet_t *new_rom, size_t size, unsigned char rom_version); +int hpsb_get_config_rom(struct hpsb_host *host, quadlet_t *buffer, + size_t buffersize, size_t *rom_size, unsigned char *rom_version); + +/* 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); + +#endif /* _IEEE1394_HOSTS_H */ diff --git a/drivers/ieee1394/ieee1394-ioctl.h b/drivers/ieee1394/ieee1394-ioctl.h new file mode 100644 index 0000000..f92b566 --- /dev/null +++ b/drivers/ieee1394/ieee1394-ioctl.h @@ -0,0 +1,111 @@ +/* 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> + + +/* AMDTP Gets 6 */ +#define AMDTP_IOC_CHANNEL _IOW('#', 0x00, struct amdtp_ioctl) +#define AMDTP_IOC_PLUG _IOW('#', 0x01, struct amdtp_ioctl) +#define AMDTP_IOC_PING _IOW('#', 0x02, struct amdtp_ioctl) +#define AMDTP_IOC_ZAP _IO ('#', 0x03) + + +/* 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) + + +#endif /* __IEEE1394_IOCTL_H */ diff --git a/drivers/ieee1394/ieee1394.h b/drivers/ieee1394/ieee1394.h new file mode 100644 index 0000000..b634a9b --- /dev/null +++ b/drivers/ieee1394/ieee1394.h @@ -0,0 +1,202 @@ +/* + * 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[]; + + +#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 + + +/* 1394a PHY 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..1c5845f --- /dev/null +++ b/drivers/ieee1394/ieee1394_core.c @@ -0,0 +1,1330 @@ +/* + * 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/config.h> +#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/skbuff.h> +#include <linux/suspend.h> + +#include <asm/byteorder.h> +#include <asm/semaphore.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 = 0; +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, 0); +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_simple *hpsb_protocol_class; + +#ifdef CONFIG_IEEE1394_VERBOSEDEBUG +static void dump_packet(const char *text, quadlet_t *data, int size) +{ + int i; + + size /= 4; + size = (size > 4 ? 4 : size); + + printk(KERN_DEBUG "ieee1394: %s", text); + for (i = 0; i < size; i++) + printk(" %08x", data[i]); + printk("\n"); +} +#else +#define dump_packet(x,y,z) +#endif + +static void abort_requests(struct hpsb_host *host); +static void queue_packet_complete(struct hpsb_packet *packet); + + +/** + * hpsb_set_packet_complete_task - set the task that runs when a packet + * completes. You cannot call this more than once on a single packet + * before it is sent. + * + * @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 + */ +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 + * + * This function allocates, initializes and returns a new &struct hpsb_packet. + * It can be used in interrupt context. A header block is always included, its + * size is big enough to contain all possible 1394 headers. The data block is + * only allocated when @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 = NULL; + struct sk_buff *skb; + + data_size = ((data_size + 3) & ~3); + + skb = alloc_skb(data_size + sizeof(*packet), GFP_ATOMIC); + if (skb == NULL) + return NULL; + + memset(skb->data, 0, data_size + sizeof(*packet)); + + packet = (struct hpsb_packet *)skb->data; + packet->skb = skb; + + packet->header = packet->embedded_header; + packet->state = hpsb_unused; + packet->generation = -1; + INIT_LIST_HEAD(&packet->driver_list); + atomic_set(&packet->refcnt, 1); + + if (data_size) { + packet->data = (quadlet_t *)(skb->data + sizeof(*packet)); + packet->data_size = data_size; + } + + return packet; +} + + +/** + * hpsb_free_packet - free packet and data associated with it + * @packet: packet to free (is NULL safe) + * + * This function will free packet->data and finally the packet itself. + */ +void hpsb_free_packet(struct hpsb_packet *packet) +{ + if (packet && atomic_dec_and_test(&packet->refcnt)) { + BUG_ON(!list_empty(&packet->driver_list)); + kfree_skb(packet->skb); + } +} + + +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; + } +} + + +int hpsb_bus_reset(struct hpsb_host *host) +{ + if (host->in_bus_reset) { + HPSB_NOTICE("%s called while bus reset already in progress", + __FUNCTION__); + 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 == 0x2) || (esid->portb == 0x2) + || (esid->portc == 0x2) || (esid->portd == 0x2) + || (esid->porte == 0x2) || (esid->portf == 0x2) + || (esid->portg == 0x2) || (esid->porth == 0x2)) { + HPSB_INFO("SelfIDs failed root check on " + "extended SelfID"); + return 0; + } + esid--; + } + + sid = (struct selfid *)esid; + if ((sid->port0 == 0x2) || (sid->port1 == 0x2) || (sid->port2 == 0x2)) { + 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 speedcap[nodecount]; + u8 cldcnt[nodecount]; + u8 *map = host->speed_map; + 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 == 0x3) cldcnt[n]++; + if (esid->portb == 0x3) cldcnt[n]++; + if (esid->portc == 0x3) cldcnt[n]++; + if (esid->portd == 0x3) cldcnt[n]++; + if (esid->porte == 0x3) cldcnt[n]++; + if (esid->portf == 0x3) cldcnt[n]++; + if (esid->portg == 0x3) cldcnt[n]++; + if (esid->porth == 0x3) cldcnt[n]++; + } else { + if (sid->port0 == 0x3) cldcnt[n]++; + if (sid->port1 == 0x3) cldcnt[n]++; + if (sid->port2 == 0x3) cldcnt[n]++; + + speedcap[n] = sid->speed; + 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]); + } + } + } +} + + +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)); + } +} + +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); +} + + +void hpsb_packet_sent(struct hpsb_host *host, struct hpsb_packet *packet, + int ackcode) +{ + unsigned long flags; + + spin_lock_irqsave(&host->pending_packet_queue.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(&host->pending_packet_queue.lock, flags); + hpsb_free_packet(packet); + return; + } + + atomic_dec(&packet->refcnt); /* drop HC's reference */ + /* here the packet must be on the host->pending_packet_queue */ + + if (ackcode != ACK_PENDING || !packet->expect_response) { + packet->state = hpsb_complete; + __skb_unlink(packet->skb, &host->pending_packet_queue); + spin_unlock_irqrestore(&host->pending_packet_queue.lock, flags); + queue_packet_complete(packet); + return; + } + + packet->state = hpsb_pending; + packet->sendtime = jiffies; + + spin_unlock_irqrestore(&host->pending_packet_queue.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 error number otherwise. + */ +int hpsb_send_phy_config(struct hpsb_host *host, int rootid, int gapcnt) +{ + struct hpsb_packet *packet; + 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; + } + + packet = hpsb_alloc_packet(0); + if (!packet) + return -ENOMEM; + + packet->host = host; + packet->header_size = 8; + packet->data_size = 0; + packet->expect_response = 0; + packet->no_waiter = 0; + packet->type = hpsb_raw; + packet->header[0] = 0; + if (rootid != -1) + packet->header[0] |= rootid << 24 | 1 << 23; + if (gapcnt != -1) + packet->header[0] |= gapcnt << 16 | 1 << 22; + + packet->header[1] = ~packet->header[0]; + + 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) { + atomic_inc(&packet->refcnt); + packet->sendtime = jiffies; + skb_queue_tail(&host->pending_packet_queue, packet->skb); + } + + 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); + + 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 && packet->node_id != ALL_NODES) { + packet->speed_code = + host->speed_map[NODEID_TO_NODE(host->node_id) * 64 + + NODEID_TO_NODE(packet->node_id)]; + } + +#ifdef CONFIG_IEEE1394_VERBOSEDEBUG + switch (packet->speed_code) { + case 2: + dump_packet("send packet 400:", packet->header, + packet->header_size); + break; + case 1: + dump_packet("send packet 200:", packet->header, + packet->header_size); + break; + default: + dump_packet("send packet 100:", packet->header, + packet->header_size); + } +#endif + + 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); +} + +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 void handle_packet_response(struct hpsb_host *host, int tcode, + quadlet_t *data, size_t size) +{ + struct hpsb_packet *packet = NULL; + struct sk_buff *skb; + int tcode_match = 0; + int tlabel; + unsigned long flags; + + tlabel = (data[0] >> 10) & 0x3f; + + spin_lock_irqsave(&host->pending_packet_queue.lock, flags); + + skb_queue_walk(&host->pending_packet_queue, skb) { + packet = (struct hpsb_packet *)skb->data; + if ((packet->tlabel == tlabel) + && (packet->node_id == (data[1] >> 16))){ + break; + } + + packet = NULL; + } + + if (packet == NULL) { + HPSB_DEBUG("unsolicited response packet received - no tlabel match"); + dump_packet("contents:", data, 16); + spin_unlock_irqrestore(&host->pending_packet_queue.lock, flags); + return; + } + + switch (packet->tcode) { + case TCODE_WRITEQ: + case TCODE_WRITEB: + if (tcode != TCODE_WRITE_RESPONSE) + break; + tcode_match = 1; + memcpy(packet->header, data, 12); + break; + case TCODE_READQ: + if (tcode != TCODE_READQ_RESPONSE) + break; + tcode_match = 1; + memcpy(packet->header, data, 16); + break; + case TCODE_READB: + if (tcode != TCODE_READB_RESPONSE) + break; + tcode_match = 1; + BUG_ON(packet->skb->len - sizeof(*packet) < size - 16); + memcpy(packet->header, data, 16); + memcpy(packet->data, data + 4, size - 16); + break; + case TCODE_LOCK_REQUEST: + if (tcode != TCODE_LOCK_RESPONSE) + break; + tcode_match = 1; + size = min((size - 16), (size_t)8); + BUG_ON(packet->skb->len - sizeof(*packet) < size); + memcpy(packet->header, data, 16); + memcpy(packet->data, data + 4, size); + break; + } + + if (!tcode_match) { + spin_unlock_irqrestore(&host->pending_packet_queue.lock, flags); + HPSB_INFO("unsolicited response packet received - tcode mismatch"); + dump_packet("contents:", data, 16); + return; + } + + __skb_unlink(skb, skb->list); + + if (packet->state == hpsb_queued) { + packet->sendtime = jiffies; + packet->ack_code = ACK_PENDING; + } + + packet->state = hpsb_complete; + spin_unlock_irqrestore(&host->pending_packet_queue.lock, flags); + + 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 */ + 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[2] = 0; + 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; +} + +#define PREP_REPLY_PACKET(length) \ + packet = create_reply_packet(host, data, length); \ + if (packet == NULL) break + +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; + + /* big FIXME - no error checking is done for an out of bounds length */ + + switch (tcode) { + case TCODE_WRITEQ: + addr = (((u64)(data[1] & 0xffff)) << 32) | data[2]; + rcode = highlevel_write(host, source, dest, data+3, + addr, 4, flags); + + if (!write_acked + && (NODEID_TO_NODE(data[0] >> 16) != NODE_MASK) + && (rcode >= 0)) { + /* not a broadcast write, reply */ + PREP_REPLY_PACKET(0); + fill_async_write_resp(packet, rcode); + send_packet_nocare(packet); + } + break; + + case TCODE_WRITEB: + addr = (((u64)(data[1] & 0xffff)) << 32) | data[2]; + rcode = highlevel_write(host, source, dest, data+4, + addr, data[3]>>16, flags); + + if (!write_acked + && (NODEID_TO_NODE(data[0] >> 16) != NODE_MASK) + && (rcode >= 0)) { + /* not a broadcast write, reply */ + PREP_REPLY_PACKET(0); + fill_async_write_resp(packet, rcode); + send_packet_nocare(packet); + } + break; + + case TCODE_READQ: + addr = (((u64)(data[1] & 0xffff)) << 32) | data[2]; + rcode = highlevel_read(host, source, &buffer, addr, 4, flags); + + if (rcode >= 0) { + PREP_REPLY_PACKET(0); + fill_async_readquad_resp(packet, rcode, buffer); + send_packet_nocare(packet); + } + break; + + case TCODE_READB: + length = data[3] >> 16; + PREP_REPLY_PACKET(length); + + addr = (((u64)(data[1] & 0xffff)) << 32) | data[2]; + rcode = highlevel_read(host, source, packet->data, addr, + length, flags); + + if (rcode >= 0) { + fill_async_readblock_resp(packet, rcode, length); + send_packet_nocare(packet); + } else { + hpsb_free_packet(packet); + } + break; + + case TCODE_LOCK_REQUEST: + length = data[3] >> 16; + extcode = data[3] & 0xffff; + addr = (((u64)(data[1] & 0xffff)) << 32) | data[2]; + + PREP_REPLY_PACKET(8); + + 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) { + send_packet_nocare(packet); + } else { + hpsb_free_packet(packet); + } + break; + } + +} +#undef PREP_REPLY_PACKET + + +void hpsb_packet_received(struct hpsb_host *host, quadlet_t *data, size_t size, + int write_acked) +{ + int tcode; + + if (host->in_bus_reset) { + HPSB_INFO("received packet during reset; ignoring"); + return; + } + + dump_packet("received packet:", data, size); + + 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_ISO_DATA: + highlevel_iso_receive(host, data, size); + break; + + case TCODE_CYCLE_START: + /* simply ignore this packet if it is passed on */ + break; + + default: + HPSB_NOTICE("received packet with bogus transaction code %d", + tcode); + break; + } +} + + +static void abort_requests(struct hpsb_host *host) +{ + struct hpsb_packet *packet; + struct sk_buff *skb; + + host->driver->devctl(host, CANCEL_REQUESTS, 0); + + while ((skb = skb_dequeue(&host->pending_packet_queue)) != NULL) { + packet = (struct hpsb_packet *)skb->data; + + 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; + unsigned long flags; + struct hpsb_packet *packet; + struct sk_buff *skb; + unsigned long expire; + + spin_lock_irqsave(&host->csr.lock, flags); + expire = host->csr.expire; + spin_unlock_irqrestore(&host->csr.lock, flags); + + /* Hold the lock around this, since we aren't dequeuing all + * packets, just ones we need. */ + spin_lock_irqsave(&host->pending_packet_queue.lock, flags); + + while (!skb_queue_empty(&host->pending_packet_queue)) { + skb = skb_peek(&host->pending_packet_queue); + + packet = (struct hpsb_packet *)skb->data; + + if (time_before(packet->sendtime + expire, jiffies)) { + __skb_unlink(skb, skb->list); + packet->state = hpsb_complete; + packet->ack_code = ACKX_TIMEOUT; + queue_packet_complete(packet); + } 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 (!skb_queue_empty(&host->pending_packet_queue)) + mod_timer(&host->timeout, jiffies + host->timeout_interval); + + spin_unlock_irqrestore(&host->pending_packet_queue.lock, flags); +} + + +/* Kernel thread and vars, which handles packets that are completed. Only + * packets that have a "complete" function are sent here. This way, the + * completion is run out of kernel context, and doesn't block the rest of + * the stack. */ +static int khpsbpkt_pid = -1, khpsbpkt_kill; +static DECLARE_COMPLETION(khpsbpkt_complete); +static struct sk_buff_head hpsbpkt_queue; +static DECLARE_MUTEX_LOCKED(khpsbpkt_sig); + + +static void queue_packet_complete(struct hpsb_packet *packet) +{ + if (packet->no_waiter) { + hpsb_free_packet(packet); + return; + } + if (packet->complete_routine != NULL) { + skb_queue_tail(&hpsbpkt_queue, packet->skb); + + /* Signal the kernel thread to handle this */ + up(&khpsbpkt_sig); + } + return; +} + +static int hpsbpkt_thread(void *__hi) +{ + struct sk_buff *skb; + struct hpsb_packet *packet; + void (*complete_routine)(void*); + void *complete_data; + + daemonize("khpsbpkt"); + + while (1) { + if (down_interruptible(&khpsbpkt_sig)) { + if (current->flags & PF_FREEZE) { + refrigerator(0); + continue; + } + printk("khpsbpkt: received unexpected signal?!\n" ); + break; + } + + if (khpsbpkt_kill) + break; + + while ((skb = skb_dequeue(&hpsbpkt_queue)) != NULL) { + packet = (struct hpsb_packet *)skb->data; + + complete_routine = packet->complete_routine; + complete_data = packet->complete_data; + + packet->complete_routine = packet->complete_data = NULL; + + complete_routine(complete_data); + } + } + + complete_and_exit(&khpsbpkt_complete, 0); +} + +static int __init ieee1394_init(void) +{ + int i, ret; + + skb_queue_head_init(&hpsbpkt_queue); + + /* 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_pid = kernel_thread(hpsbpkt_thread, NULL, CLONE_KERNEL); + if (khpsbpkt_pid < 0) { + HPSB_ERR("Failed to start hpsbpkt thread!\n"); + ret = -ENOMEM; + 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; + } + + /* actually this is a non-fatal error */ + ret = devfs_mk_dir("ieee1394"); + if (ret < 0) { + HPSB_ERR("unable to make devfs dir for device major %d!\n", IEEE1394_MAJOR); + goto release_chrdev; + } + + ret = bus_register(&ieee1394_bus_type); + if (ret < 0) { + HPSB_INFO("bus register failed"); + goto release_devfs; + } + + 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_devfs; + } + } + + ret = class_register(&hpsb_host_class); + if (ret < 0) + goto release_all_bus; + + hpsb_protocol_class = class_simple_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_simple_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_devfs: + devfs_remove("ieee1394"); +release_chrdev: + unregister_chrdev_region(IEEE1394_CORE_DEV, 256); +exit_release_kernel_thread: + if (khpsbpkt_pid >= 0) { + kill_proc(khpsbpkt_pid, SIGTERM, 1); + wait_for_completion(&khpsbpkt_complete); + } +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_simple_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); + + if (khpsbpkt_pid >= 0) { + khpsbpkt_kill = 1; + mb(); + up(&khpsbpkt_sig); + wait_for_completion(&khpsbpkt_complete); + } + + hpsb_cleanup_config_roms(); + + unregister_chrdev_region(IEEE1394_CORE_DEV, 256); + devfs_remove("ieee1394"); +} + +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_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_phy_config); +EXPORT_SYMBOL(hpsb_send_packet); +EXPORT_SYMBOL(hpsb_send_packet_and_wait); +EXPORT_SYMBOL(hpsb_reset_bus); +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_make_isopacket); +EXPORT_SYMBOL(hpsb_read); +EXPORT_SYMBOL(hpsb_write); +EXPORT_SYMBOL(hpsb_lock); +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_listen_channel); +EXPORT_SYMBOL(hpsb_unlisten_channel); +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); +EXPORT_SYMBOL(highlevel_add_host); +EXPORT_SYMBOL(highlevel_remove_host); +EXPORT_SYMBOL(highlevel_host_reset); + +/** nodemgr.c **/ +EXPORT_SYMBOL(hpsb_node_fill_packet); +EXPORT_SYMBOL(hpsb_node_write); +EXPORT_SYMBOL(hpsb_register_protocol); +EXPORT_SYMBOL(hpsb_unregister_protocol); +EXPORT_SYMBOL(ieee1394_bus_type); +EXPORT_SYMBOL(nodemgr_for_each_host); + +/** 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_create_csr); +EXPORT_SYMBOL(csr1212_init_local_csr); +EXPORT_SYMBOL(csr1212_new_immediate); +EXPORT_SYMBOL(csr1212_new_directory); +EXPORT_SYMBOL(csr1212_associate_keyval); +EXPORT_SYMBOL(csr1212_attach_keyval_to_directory); +EXPORT_SYMBOL(csr1212_new_string_descriptor_leaf); +EXPORT_SYMBOL(csr1212_detach_keyval_from_directory); +EXPORT_SYMBOL(csr1212_release_keyval); +EXPORT_SYMBOL(csr1212_destroy_csr); +EXPORT_SYMBOL(csr1212_read); +EXPORT_SYMBOL(csr1212_generate_csr_image); +EXPORT_SYMBOL(csr1212_parse_keyval); +EXPORT_SYMBOL(csr1212_parse_csr); +EXPORT_SYMBOL(_csr1212_read_keyval); +EXPORT_SYMBOL(_csr1212_destroy_keyval); diff --git a/drivers/ieee1394/ieee1394_core.h b/drivers/ieee1394/ieee1394_core.h new file mode 100644 index 0000000..c4b4408 --- /dev/null +++ b/drivers/ieee1394/ieee1394_core.h @@ -0,0 +1,228 @@ + +#ifndef _IEEE1394_CORE_H +#define _IEEE1394_CORE_H + +#include <linux/slab.h> +#include <linux/devfs_fs_kernel.h> +#include <asm/atomic.h> +#include <asm/semaphore.h> +#include "hosts.h" + + +struct hpsb_packet { + /* This struct is basically read-only for hosts with the exception of + * the data buffer contents and xnext - see below. */ + + /* 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; + + /* Async and Iso types should be clear, raw means send-as-is, do not + * CRC! Byte swapping shall still be done in this case. */ + enum { hpsb_async, hpsb_iso, 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; + char ack_code; + char tcode; + + unsigned expect_response:1; + unsigned no_waiter:1; + + /* Speed to transmit with: 0 = 100Mbps, 1 = 200Mbps, 2 = 400Mbps */ + unsigned speed_code:2; + + /* + * *header and *data are guaranteed to be 32-bit DMAable and may be + * overwritten to allow in-place byte swapping. Neither of these is + * CRCed (the sizes also don't include CRC), but contain space for at + * least one additional quadlet to allow in-place CRCing. The memory is + * also guaranteed to be DMA mappable. + */ + quadlet_t *header; + quadlet_t *data; + size_t header_size; + size_t data_size; + + + struct hpsb_host *host; + unsigned int generation; + + atomic_t refcnt; + + /* Function (and possible data to pass to it) to call when this + * packet is completed. */ + void (*complete_routine)(void *); + void *complete_data; + + /* XXX This is just a hack at the moment */ + struct sk_buff *skb; + + /* Store jiffies for implementing bus timeouts. */ + unsigned long sendtime; + + quadlet_t embedded_header[5]; +}; + +/* Set a task for when a packet completes */ +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); + + +/* + * Generation counter for the complete 1394 subsystem. Generation gets + * incremented on every change in the subsystem (e.g. bus reset). + * + * Use the functions, not the variable. + */ +static inline unsigned int get_hpsb_generation(struct hpsb_host *host) +{ + return atomic_read(&host->generation); +} + +/* + * Send a PHY configuration packet, return 0 on success, negative + * errno on failure. + */ +int hpsb_send_phy_config(struct hpsb_host *host, int rootid, int gapcnt); + +/* + * Queue packet for transmitting, return 0 on success, negative errno + * on failure. + */ +int hpsb_send_packet(struct hpsb_packet *packet); + +/* + * Queue packet for transmitting, and block until the transaction + * completes. Return 0 on success, negative errno on failure. + */ +int hpsb_send_packet_and_wait(struct hpsb_packet *packet); + +/* Initiate bus reset on the given host. Returns 1 if bus reset already in + * progress, 0 otherwise. */ +int hpsb_reset_bus(struct hpsb_host *host, int type); + +/* + * The following functions are exported for host driver module usage. All of + * them are safe to use in interrupt contexts, although some are quite + * complicated so you may want to run them in bottom halves instead of calling + * them directly. + */ + +/* Notify a bus reset to the core. Returns 1 if bus reset already in progress, + * 0 otherwise. */ +int hpsb_bus_reset(struct hpsb_host *host); + +/* + * Hand over received selfid packet to the core. Complement check (second + * quadlet is complement of first) is expected to be done and succesful. + */ +void hpsb_selfid_received(struct hpsb_host *host, quadlet_t sid); + +/* + * 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); + +/* + * 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). Safe to call + * from within a transmit packet routine. + */ +void hpsb_packet_sent(struct hpsb_host *host, struct hpsb_packet *packet, + int ackcode); + +/* + * Hand over received packet to the core. 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 arg is ignored for any other + * packet type. + */ +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_AMDTP 3 +#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_AMDTP_DEV MKDEV(IEEE1394_MAJOR, IEEE1394_MINOR_BLOCK_AMDTP * 16) +#define IEEE1394_EXPERIMENTAL_DEV MKDEV(IEEE1394_MAJOR, IEEE1394_MINOR_BLOCK_EXPERIMENTAL * 16) + +/* return the index (within a minor number block) of a file */ +static inline unsigned char ieee1394_file_to_instance(struct file *file) +{ + return file->f_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_simple *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..5be70d3 --- /dev/null +++ b/drivers/ieee1394/ieee1394_hotplug.h @@ -0,0 +1,33 @@ +#ifndef _IEEE1394_HOTPLUG_H +#define _IEEE1394_HOTPLUG_H + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/mod_devicetable.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 + +/* Check to make sure this all isn't already defined */ +#ifndef IEEE1394_MATCH_VENDOR_ID + +#define IEEE1394_MATCH_VENDOR_ID 0x0001 +#define IEEE1394_MATCH_MODEL_ID 0x0002 +#define IEEE1394_MATCH_SPECIFIER_ID 0x0004 +#define IEEE1394_MATCH_VERSION 0x0008 + +struct ieee1394_device_id { + u32 match_flags; + u32 vendor_id; + u32 model_id; + u32 specifier_id; + u32 version; + void *driver_data; +}; + +#endif + +#endif /* _IEEE1394_HOTPLUG_H */ diff --git a/drivers/ieee1394/ieee1394_transactions.c b/drivers/ieee1394/ieee1394_transactions.c new file mode 100644 index 0000000..09908b9 --- /dev/null +++ b/drivers/ieee1394/ieee1394_transactions.c @@ -0,0 +1,601 @@ +/* + * 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/sched.h> +#include <linux/bitops.h> +#include <linux/smp_lock.h> +#include <linux/interrupt.h> + +#include <asm/errno.h> + +#include "ieee1394.h" +#include "ieee1394_types.h" +#include "hosts.h" +#include "ieee1394_core.h" +#include "highlevel.h" +#include "nodemgr.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 + + +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_iso_packet(struct hpsb_packet *packet, int length, int channel, + int tag, int sync) +{ + packet->header[0] = (length << 16) | (tag << 14) | (channel << 8) + | (TCODE_ISO_DATA << 4) | sync; + + packet->header_size = 4; + packet->data_size = length; + packet->type = hpsb_iso; + packet->tcode = TCODE_ISO_DATA; +} + +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; +} + +/** + * hpsb_get_tlabel - allocate a transaction label + * @packet: the packet who's tlabel/tpool 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. + */ +int hpsb_get_tlabel(struct hpsb_packet *packet) +{ + unsigned long flags; + struct hpsb_tlabel_pool *tp; + + tp = &packet->host->tpool[packet->node_id & NODE_MASK]; + + if (irqs_disabled() || in_atomic()) { + if (down_trylock(&tp->count)) + return 1; + } else { + down(&tp->count); + } + + spin_lock_irqsave(&tp->lock, flags); + + packet->tlabel = find_next_zero_bit(tp->pool, 64, tp->next); + if (packet->tlabel > 63) + packet->tlabel = find_first_zero_bit(tp->pool, 64); + tp->next = (packet->tlabel + 1) % 64; + /* Should _never_ happen */ + BUG_ON(test_and_set_bit(packet->tlabel, tp->pool)); + tp->allocations++; + spin_unlock_irqrestore(&tp->lock, flags); + + return 0; +} + +/** + * hpsb_free_tlabel - free an allocated transaction label + * @packet: packet whos tlabel/tpool 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; + struct hpsb_tlabel_pool *tp; + + tp = &packet->host->tpool[packet->node_id & NODE_MASK]; + + BUG_ON(packet->tlabel > 63 || packet->tlabel < 0); + + spin_lock_irqsave(&tp->lock, flags); + BUG_ON(!test_and_clear_bit(packet->tlabel, tp->pool)); + spin_unlock_irqrestore(&tp->lock, flags); + + up(&tp->count); +} + + + +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; + } + HPSB_PANIC("reached unreachable code 1 in %s", __FUNCTION__); + + 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; + } + + HPSB_PANIC("reached unreachable code 2 in %s", __FUNCTION__); +} + +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; + + 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; +} + +struct hpsb_packet *hpsb_make_isopacket(struct hpsb_host *host, + int length, int channel, + int tag, int sync) +{ + struct hpsb_packet *p; + + p = hpsb_alloc_packet(length); + if (!p) return NULL; + + p->host = host; + fill_iso_packet(p, length, channel, tag, sync); + + p->generation = get_hpsb_generation(host); + + return p; +} + +/* + * FIXME - these functions should probably read from / write to user space to + * avoid in kernel buffers for user space callers + */ + +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; +} + + +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; +} + + +int hpsb_lock(struct hpsb_host *host, nodeid_t node, unsigned int generation, + u64 addr, int extcode, quadlet_t *data, quadlet_t arg) +{ + struct hpsb_packet *packet; + int retval = 0; + + BUG_ON(in_interrupt()); // We can't be called in an interrupt, yet + + packet = hpsb_make_lockpacket(host, node, addr, extcode, data, arg); + if (!packet) + return -ENOMEM; + + packet->generation = generation; + retval = hpsb_send_packet_and_wait(packet); + if (retval < 0) + goto hpsb_lock_fail; + + retval = hpsb_packet_success(packet); + + if (retval == 0) { + *data = packet->data[0]; + } + +hpsb_lock_fail: + hpsb_free_tlabel(packet); + hpsb_free_packet(packet); + + return retval; +} + + +int hpsb_send_gasp(struct hpsb_host *host, int channel, unsigned int generation, + quadlet_t *buffer, size_t length, u32 specifier_id, + unsigned int version) +{ + struct hpsb_packet *packet; + int retval = 0; + u16 specifier_id_hi = (specifier_id & 0x00ffff00) >> 8; + u8 specifier_id_lo = specifier_id & 0xff; + + HPSB_VERBOSE("Send GASP: channel = %d, length = %Zd", channel, length); + + length += 8; + + packet = hpsb_make_streampacket(host, NULL, length, channel, 3, 0); + if (!packet) + return -ENOMEM; + + packet->data[0] = cpu_to_be32((host->node_id << 16) | specifier_id_hi); + packet->data[1] = cpu_to_be32((specifier_id_lo << 24) | (version & 0x00ffffff)); + + memcpy(&(packet->data[2]), buffer, length - 8); + + packet->generation = generation; + + packet->no_waiter = 1; + + retval = hpsb_send_packet(packet); + if (retval < 0) + 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..526a43c --- /dev/null +++ b/drivers/ieee1394/ieee1394_transactions.h @@ -0,0 +1,64 @@ +#ifndef _IEEE1394_TRANSACTIONS_H +#define _IEEE1394_TRANSACTIONS_H + +#include "ieee1394_core.h" + + +/* + * Get and free transaction labels. + */ +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_isopacket(struct hpsb_host *host, + int length, int channel, + int tag, int sync); +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); + +/* + * hpsb_packet_success - 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); + + +/* + * The generic read, write and lock functions. All recognize the local node ID + * and act accordingly. Read and write automatically use quadlet commands if + * length == 4 and and block commands otherwise (however, they do 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); +int hpsb_write(struct hpsb_host *host, nodeid_t node, unsigned int generation, + u64 addr, quadlet_t *buffer, size_t length); +int hpsb_lock(struct hpsb_host *host, nodeid_t node, unsigned int generation, + u64 addr, int extcode, quadlet_t *data, quadlet_t arg); +int hpsb_lock64(struct hpsb_host *host, nodeid_t node, unsigned int generation, + u64 addr, int extcode, octlet_t *data, octlet_t arg); +int hpsb_send_gasp(struct hpsb_host *host, int channel, unsigned int generation, + quadlet_t *buffer, size_t length, u32 specifier_id, + unsigned int version); + +#endif /* _IEEE1394_TRANSACTIONS_H */ diff --git a/drivers/ieee1394/ieee1394_types.h b/drivers/ieee1394/ieee1394_types.h new file mode 100644 index 0000000..3165609 --- /dev/null +++ b/drivers/ieee1394/ieee1394_types.h @@ -0,0 +1,101 @@ + +#ifndef _IEEE1394_TYPES_H +#define _IEEE1394_TYPES_H + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/list.h> +#include <linux/init.h> +#include <linux/spinlock.h> +#include <linux/string.h> + +#include <asm/semaphore.h> +#include <asm/byteorder.h> + + +/* Transaction Label handling */ +struct hpsb_tlabel_pool { + DECLARE_BITMAP(pool, 64); + spinlock_t lock; + u8 next; + u32 allocations; + struct semaphore count; +}; + +#define HPSB_TPOOL_INIT(_tp) \ +do { \ + bitmap_zero((_tp)->pool, 64); \ + spin_lock_init(&(_tp)->lock); \ + (_tp)->next = 0; \ + (_tp)->allocations = 0; \ + sema_init(&(_tp)->count, 63); \ +} while (0) + + +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) +#else +#define HPSB_VERBOSE(fmt, args...) +#endif + +#define HPSB_PANIC(fmt, args...) panic("ieee1394: " fmt "\n" , ## args) + +#define HPSB_TRACE() HPSB_PRINT(KERN_INFO, "TRACE - %s, %s(), line %d", __FILE__, __FUNCTION__, __LINE__) + + +#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/iso.c b/drivers/ieee1394/iso.c new file mode 100644 index 0000000..f057591 --- /dev/null +++ b/drivers/ieee1394/iso.c @@ -0,0 +1,451 @@ +/* + * 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/slab.h> +#include <linux/sched.h> +#include "iso.h" + +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; +} + +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) /* really interrupt for each packet*/ + irq_interval = 1; + else if ((irq_interval < 0) || (irq_interval > buf_packets / 4)) + irq_interval = buf_packets / 4; + + 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->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; +} + +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; +} + + +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; +} + +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; +} + +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); +} + +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); +} + +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); +} + +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; +} + +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; + else if (prebuffer == 0) + prebuffer = 1; + + if (prebuffer > iso->buf_packets) + prebuffer = iso->buf_packets; + + 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; +} + +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; +} + + +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; +} + +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); +} + +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); +} + +void hpsb_iso_packet_received(struct hpsb_iso *iso, u32 offset, u16 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); + } else { + struct hpsb_iso_packet_info *info = &iso->infos[iso->pkt_dma]; + info->offset = offset; + info->len = 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); +} + +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--; + } + spin_unlock_irqrestore(&iso->lock, flags); + return rv; +} + +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..fb654d9 --- /dev/null +++ b/drivers/ieee1394/iso.h @@ -0,0 +1,201 @@ +/* + * 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 "hosts.h" +#include "dma.h" + +/* 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; +}; + +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; + + /* 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) */ + +/* 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*)); + +/* 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*)); + +/* multi-channel only */ +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); + +/* start/stop DMA */ +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); + +/* deallocate buffer and DMA context */ +void hpsb_iso_shutdown(struct hpsb_iso *iso); + +/* 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); + +/* wait until all queued packets have been transmitted to the bus */ +int hpsb_iso_xmit_sync(struct hpsb_iso *iso); + +/* N packets have been read out of the buffer, re-use the buffer space */ +int hpsb_iso_recv_release_packets(struct hpsb_iso *recv, unsigned int n_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); + +/* returns # of packets ready to send or receive */ +int hpsb_iso_n_ready(struct hpsb_iso *iso); + +/* the following are callbacks 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); + +/* call after a packet has been received (interrupt context OK) */ +void hpsb_iso_packet_received(struct hpsb_iso *iso, u32 offset, u16 len, + u16 cycle, u8 channel, u8 tag, u8 sy); + +/* call to wake waiting processes after buffer space has opened up. */ +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..a1e30a6 --- /dev/null +++ b/drivers/ieee1394/nodemgr.c @@ -0,0 +1,1732 @@ +/* + * 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/kernel.h> +#include <linux/config.h> +#include <linux/list.h> +#include <linux/slab.h> +#include <linux/smp_lock.h> +#include <linux/interrupt.h> +#include <linux/kmod.h> +#include <linux/completion.h> +#include <linux/delay.h> +#include <linux/pci.h> +#include <linux/moduleparam.h> +#include <asm/atomic.h> + +#include "ieee1394_types.h" +#include "ieee1394.h" +#include "ieee1394_core.h" +#include "hosts.h" +#include "ieee1394_transactions.h" +#include "highlevel.h" +#include "csr.h" +#include "nodemgr.h" + +static int ignore_drivers = 0; +module_param(ignore_drivers, int, 0444); +MODULE_PARM_DESC(ignore_drivers, "Disable automatic probing for drivers."); + +struct nodemgr_csr_info { + struct hpsb_host *host; + nodeid_t nodeid; + unsigned int generation; +}; + + +static char *nodemgr_find_oui_name(int oui) +{ +#ifdef CONFIG_IEEE1394_OUI_DB + extern struct oui_list_struct { + int oui; + char *name; + } oui_list[]; + int i; + + for (i = 0; oui_list[i].name; i++) + if (oui_list[i].oui == oui) + return oui_list[i].name; +#endif + return NULL; +} + + +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, ret = 0; + + for (i = 0; i < 3; i++) { + ret = hpsb_read(ci->host, ci->nodeid, ci->generation, addr, + buffer, length); + if (!ret) + break; + + if (msleep_interruptible(334)) + return -EINTR; + } + + return ret; +} + +static int nodemgr_get_max_rom(quadlet_t *bus_info_data, void *__ci) +{ + return (CSR1212_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. + */ + +static DECLARE_MUTEX(nodemgr_serialize); + +struct host_info { + struct hpsb_host *host; + struct list_head list; + struct completion exited; + struct semaphore reset_sem; + int pid; + char daemon_name[15]; + int kill_me; +}; + +static int nodemgr_bus_match(struct device * dev, struct device_driver * drv); +static int nodemgr_hotplug(struct class_device *cdev, char **envp, int num_envp, + char *buffer, int buffer_size); +static void nodemgr_resume_ne(struct node_entry *ne); +static void nodemgr_remove_ne(struct node_entry *ne); +static struct node_entry *find_entry_by_guid(u64 guid); + +struct bus_type ieee1394_bus_type = { + .name = "ieee1394", + .match = nodemgr_bus_match, +}; + +static void host_cls_release(struct class_device *class_dev) +{ + put_device(&container_of((class_dev), struct hpsb_host, class_dev)->device); +} + +struct class hpsb_host_class = { + .name = "ieee1394_host", + .release = host_cls_release, +}; + +static void ne_cls_release(struct class_device *class_dev) +{ + put_device(&container_of((class_dev), struct node_entry, class_dev)->device); +} + +static struct class nodemgr_ne_class = { + .name = "ieee1394_node", + .release = ne_cls_release, +}; + +static void ud_cls_release(struct class_device *class_dev) +{ + put_device(&container_of((class_dev), struct unit_directory, class_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", + .release = ud_cls_release, + .hotplug = nodemgr_hotplug, +}; + +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, +}; + +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, 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, 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, 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); + + +static ssize_t fw_show_ne_tlabels_free(struct device *dev, char *buf) +{ + struct node_entry *ne = container_of(dev, struct node_entry, device); + return sprintf(buf, "%d\n", atomic_read(&ne->tpool->count.count) + 1); +} +static DEVICE_ATTR(tlabels_free,S_IRUGO,fw_show_ne_tlabels_free,NULL); + + +static ssize_t fw_show_ne_tlabels_allocations(struct device *dev, char *buf) +{ + struct node_entry *ne = container_of(dev, struct node_entry, device); + return sprintf(buf, "%u\n", ne->tpool->allocations); +} +static DEVICE_ATTR(tlabels_allocations,S_IRUGO,fw_show_ne_tlabels_allocations,NULL); + + +static ssize_t fw_show_ne_tlabels_mask(struct device *dev, char *buf) +{ + struct node_entry *ne = container_of(dev, struct node_entry, device); +#if (BITS_PER_LONG <= 32) + return sprintf(buf, "0x%08lx%08lx\n", ne->tpool->pool[0], ne->tpool->pool[1]); +#else + return sprintf(buf, "0x%016lx\n", ne->tpool->pool[0]); +#endif +} +static DEVICE_ATTR(tlabels_mask, S_IRUGO, fw_show_ne_tlabels_mask, NULL); + + +static ssize_t fw_set_ignore_driver(struct device *dev, 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) { + down_write(&dev->bus->subsys.rwsem); + device_release_driver(dev); + ud->ignore_driver = 1; + up_write(&dev->bus->subsys.rwsem); + } else if (!state) + ud->ignore_driver = 0; + + return count; +} +static ssize_t fw_get_ignore_driver(struct device *dev, 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_destroy_node(struct bus_type *bus, const char *buf, size_t count) +{ + struct node_entry *ne; + u64 guid = (u64)simple_strtoull(buf, NULL, 16); + + ne = find_entry_by_guid(guid); + + if (ne == NULL || !ne->in_limbo) + return -EINVAL; + + nodemgr_remove_ne(ne); + + return count; +} +static ssize_t fw_get_destroy_node(struct bus_type *bus, char *buf) +{ + return sprintf(buf, "You can destroy in_limbo nodes by writing their GUID to this file\n"); +} +static BUS_ATTR(destroy_node, S_IWUSR | S_IRUGO, fw_get_destroy_node, fw_set_destroy_node); + +static int nodemgr_rescan_bus_thread(void *__unused) +{ + /* No userlevel access needed */ + daemonize("kfwrescan"); + + bus_rescan_devices(&ieee1394_bus_type); + + return 0; +} + +static ssize_t fw_set_rescan(struct bus_type *bus, const char *buf, size_t count) +{ + int state = simple_strtoul(buf, NULL, 10); + + /* Don't wait for this, or care about errors. Root could do + * something stupid and spawn this a lot of times, but that's + * root's fault. */ + if (state == 1) + kernel_thread(nodemgr_rescan_bus_thread, NULL, CLONE_KERNEL); + + return 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) + 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_destroy_node, + &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, vendor_oui, const char *, "%s\n") + +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, guid_vendor_oui, const char *, "%s\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, + &dev_attr_tlabels_free, + &dev_attr_tlabels_allocations, + &dev_attr_tlabels_mask, +}; + + + +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(ud, struct unit_directory, vendor_oui, const char *, "%s\n") +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); + + for (id = driver->id_table; 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++) + driver_create_file(drv, fw_drv_attrs[i]); +} + + +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++) + device_create_file(dev, fw_ne_attrs[i]); +} + + +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++) + device_create_file(dev, fw_host_attrs[i]); +} + + +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"); + if ((ne = find_entry_by_nodeid(host, host->busmgr_id))) + sysfs_create_link(&dev->kobj, &ne->device.kobj, "busmgr_id"); + if ((ne = find_entry_by_nodeid(host, host->node_id))) + sysfs_create_link(&dev->kobj, &ne->device.kobj, "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++) + device_create_file(dev, fw_ud_attrs[i]); + + if (ud->flags & UNIT_DIRECTORY_SPECIFIER_ID) + device_create_file(dev, &dev_attr_ud_specifier_id); + + if (ud->flags & UNIT_DIRECTORY_VERSION) + device_create_file(dev, &dev_attr_ud_version); + + if (ud->flags & UNIT_DIRECTORY_VENDOR_ID) { + device_create_file(dev, &dev_attr_ud_vendor_id); + if (ud->vendor_name_kv) + device_create_file(dev, &dev_attr_ud_vendor_name_kv); + } + + if (ud->flags & UNIT_DIRECTORY_MODEL_ID) { + device_create_file(dev, &dev_attr_ud_model_id); + if (ud->model_name_kv) + device_create_file(dev, &dev_attr_ud_model_name_kv); + } +} + + +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); + driver = container_of(drv, struct hpsb_protocol_driver, driver); + + if (ud->ne->in_limbo || ud->ignore_driver) + return 0; + + for (id = driver->id_table; 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 void nodemgr_remove_uds(struct node_entry *ne) +{ + struct class_device *cdev, *next; + struct unit_directory *ud; + + list_for_each_entry_safe(cdev, next, &nodemgr_ud_class.children, node) { + ud = container_of(cdev, struct unit_directory, class_dev); + + if (ud->ne != ne) + continue; + + class_device_unregister(&ud->class_dev); + device_unregister(&ud->device); + } +} + + +static void nodemgr_remove_ne(struct node_entry *ne) +{ + struct device *dev = &ne->device; + + 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); + + class_device_unregister(&ne->class_dev); + device_unregister(dev); + + put_device(dev); +} + + +static void nodemgr_remove_host_dev(struct device *dev) +{ + struct device *ne_dev, *next; + + list_for_each_entry_safe(ne_dev, next, &dev->children, node) + nodemgr_remove_ne(container_of(ne_dev, struct node_entry, device)); + + 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 host_info *hi, nodeid_t nodeid, + unsigned int generation) +{ + struct hpsb_host *host = hi->host; + struct node_entry *ne; + + ne = kmalloc(sizeof(struct node_entry), GFP_KERNEL); + if (!ne) return NULL; + + memset(ne, 0, sizeof(struct node_entry)); + + ne->tpool = &host->tpool[nodeid & NODE_MASK]; + + ne->host = host; + ne->nodeid = nodeid; + ne->generation = generation; + ne->needs_probe = 1; + + ne->guid = guid; + ne->guid_vendor_id = (guid >> 40) & 0xffffff; + ne->guid_vendor_oui = nodemgr_find_oui_name(ne->guid_vendor_id); + ne->csr = csr; + + memcpy(&ne->device, &nodemgr_dev_template_ne, + sizeof(ne->device)); + ne->device.parent = &host->device; + snprintf(ne->device.bus_id, BUS_ID_SIZE, "%016Lx", + (unsigned long long)(ne->guid)); + + ne->class_dev.dev = &ne->device; + ne->class_dev.class = &nodemgr_ne_class; + snprintf(ne->class_dev.class_id, BUS_ID_SIZE, "%016Lx", + (unsigned long long)(ne->guid)); + + device_register(&ne->device); + class_device_register(&ne->class_dev); + get_device(&ne->device); + + if (ne->guid_vendor_oui) + device_create_file(&ne->device, &dev_attr_ne_guid_vendor_oui); + 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; +} + + +static struct node_entry *find_entry_by_guid(u64 guid) +{ + struct class *class = &nodemgr_ne_class; + struct class_device *cdev; + struct node_entry *ne, *ret_ne = NULL; + + down_read(&class->subsys.rwsem); + list_for_each_entry(cdev, &class->children, node) { + ne = container_of(cdev, struct node_entry, class_dev); + + if (ne->guid == guid) { + ret_ne = ne; + break; + } + } + up_read(&class->subsys.rwsem); + + return ret_ne; +} + + +static struct node_entry *find_entry_by_nodeid(struct hpsb_host *host, nodeid_t nodeid) +{ + struct class *class = &nodemgr_ne_class; + struct class_device *cdev; + struct node_entry *ne, *ret_ne = NULL; + + down_read(&class->subsys.rwsem); + list_for_each_entry(cdev, &class->children, node) { + ne = container_of(cdev, struct node_entry, class_dev); + + if (ne->host == host && ne->nodeid == nodeid) { + ret_ne = ne; + break; + } + } + up_read(&class->subsys.rwsem); + + return ret_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; + + snprintf(ud->device.bus_id, BUS_ID_SIZE, "%s-%u", + ne->device.bus_id, ud->id); + + ud->class_dev.dev = &ud->device; + ud->class_dev.class = &nodemgr_ud_class; + snprintf(ud->class_dev.class_id, BUS_ID_SIZE, "%s-%u", + ne->device.bus_id, ud->id); + + device_register(&ud->device); + class_device_register(&ud->class_dev); + get_device(&ud->device); + + if (ud->vendor_oui) + device_create_file(&ud->device, &dev_attr_ud_vendor_oui); + nodemgr_create_ud_dev_files(ud); +} + + +/* 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 host_info *hi, 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 = kmalloc(sizeof(struct unit_directory), GFP_KERNEL); + if (!ud) + goto unit_directory_error; + + memset (ud, 0, sizeof(struct unit_directory)); + + ud->ne = ne; + ud->ignore_driver = ignore_drivers; + ud->address = ud_kv->offset + CSR1212_CONFIG_ROM_SPACE_BASE; + 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; + + if (ud->vendor_id) + ud->vendor_oui = nodemgr_find_oui_name(ud->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: + ud->vendor_name_kv = kv; + csr1212_keep_keyval(kv); + break; + + case CSR1212_KV_ID_MODEL: + ud->model_name_kv = kv; + csr1212_keep_keyval(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 = kmalloc(sizeof(struct unit_directory), GFP_KERNEL); + if (!ud_child) + goto unit_directory_error; + memcpy(ud_child, ud, sizeof(struct unit_directory)); + 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(hi, ne, kv, id, ud); + + if (ud_child == NULL) + break; + + /* inherit unspecified values so hotplug 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; + + 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: + if (ud != NULL) + kfree(ud); + return NULL; +} + + +static void nodemgr_process_root_directory(struct host_info *hi, struct node_entry *ne) +{ + unsigned int ud_id = 0; + struct csr1212_dentry *dentry; + struct csr1212_keyval *kv; + u8 last_key_id = 0; + + ne->needs_probe = 0; + + 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; + + if (ne->vendor_id) + ne->vendor_oui = nodemgr_find_oui_name(ne->vendor_id); + break; + + case CSR1212_KV_ID_NODE_CAPABILITIES: + ne->capabilities = kv->value.immediate; + break; + + case CSR1212_KV_ID_UNIT: + nodemgr_process_unit_directory(hi, 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) { + ne->vendor_name_kv = kv; + csr1212_keep_keyval(kv); + } + } + break; + } + last_key_id = kv->key.id; + } + + if (ne->vendor_oui) + device_create_file(&ne->device, &dev_attr_ne_vendor_oui); + if (ne->vendor_name_kv) + device_create_file(&ne->device, &dev_attr_ne_vendor_name_kv); +} + +#ifdef CONFIG_HOTPLUG + +static int nodemgr_hotplug(struct class_device *cdev, char **envp, int num_envp, + char *buffer, int buffer_size) +{ + struct unit_directory *ud; + int i = 0; + int length = 0; + + if (!cdev) + return -ENODEV; + + ud = container_of(cdev, struct unit_directory, class_dev); + + if (ud->ne->in_limbo || ud->ignore_driver) + return -ENODEV; + +#define PUT_ENVP(fmt,val) \ +do { \ + int printed; \ + envp[i++] = buffer; \ + printed = snprintf(buffer, buffer_size - length, \ + fmt, val); \ + if ((buffer_size - (length+printed) <= 0) || (i >= num_envp)) \ + return -ENOMEM; \ + length += printed+1; \ + buffer += printed+1; \ +} 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); + +#undef PUT_ENVP + + envp[i] = NULL; + + return 0; +} + +#else + +static int nodemgr_hotplug(struct class_device *cdev, char **envp, int num_envp, + char *buffer, int buffer_size) +{ + return -ENODEV; +} + +#endif /* CONFIG_HOTPLUG */ + + +int hpsb_register_protocol(struct hpsb_protocol_driver *driver) +{ + int ret; + + /* This will cause a probe for devices */ + ret = driver_register(&driver->driver); + if (!ret) + nodemgr_create_drv_files(driver); + + return ret; +} + +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, + struct host_info *hi, 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 = 1; + } 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); + } + + if (ne->in_limbo) + nodemgr_resume_ne(ne); + + /* Mark the node current */ + ne->generation = generation; +} + + + +static void nodemgr_node_scan_one(struct host_info *hi, + nodeid_t nodeid, int generation) +{ + struct hpsb_host *host = hi->host; + struct node_entry *ne; + octlet_t guid; + struct csr1212_csr *csr; + struct nodemgr_csr_info *ci; + + ci = kmalloc(sizeof(struct nodemgr_csr_info), GFP_KERNEL); + if (!ci) + return; + + ci->host = host; + ci->nodeid = nodeid; + ci->generation = generation; + + /* 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, hi, nodeid, generation); + else + nodemgr_update_node(ne, csr, hi, nodeid, generation); + + return; +} + + +static void nodemgr_node_scan(struct host_info *hi, int generation) +{ + int count; + struct hpsb_host *host = hi->host; + 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(hi, nodeid++, generation); + } +} + + +static void nodemgr_suspend_ne(struct node_entry *ne) +{ + struct class_device *cdev; + struct unit_directory *ud; + + HPSB_DEBUG("Node suspended: ID:BUS[" NODE_BUS_FMT "] GUID[%016Lx]", + NODE_BUS_ARGS(ne->host, ne->nodeid), (unsigned long long)ne->guid); + + ne->in_limbo = 1; + device_create_file(&ne->device, &dev_attr_ne_in_limbo); + + down_write(&ne->device.bus->subsys.rwsem); + list_for_each_entry(cdev, &nodemgr_ud_class.children, node) { + ud = container_of(cdev, struct unit_directory, class_dev); + + if (ud->ne != ne) + continue; + + if (ud->device.driver && + (!ud->device.driver->suspend || + ud->device.driver->suspend(&ud->device, PMSG_SUSPEND, 0))) + device_release_driver(&ud->device); + } + up_write(&ne->device.bus->subsys.rwsem); +} + + +static void nodemgr_resume_ne(struct node_entry *ne) +{ + struct class_device *cdev; + struct unit_directory *ud; + + ne->in_limbo = 0; + device_remove_file(&ne->device, &dev_attr_ne_in_limbo); + + down_read(&ne->device.bus->subsys.rwsem); + list_for_each_entry(cdev, &nodemgr_ud_class.children, node) { + ud = container_of(cdev, struct unit_directory, class_dev); + + if (ud->ne != ne) + continue; + + if (ud->device.driver && ud->device.driver->resume) + ud->device.driver->resume(&ud->device, 0); + } + up_read(&ne->device.bus->subsys.rwsem); + + HPSB_DEBUG("Node resumed: ID:BUS[" NODE_BUS_FMT "] GUID[%016Lx]", + NODE_BUS_ARGS(ne->host, ne->nodeid), (unsigned long long)ne->guid); +} + + +static void nodemgr_update_pdrv(struct node_entry *ne) +{ + struct unit_directory *ud; + struct hpsb_protocol_driver *pdrv; + struct class *class = &nodemgr_ud_class; + struct class_device *cdev; + + down_read(&class->subsys.rwsem); + list_for_each_entry(cdev, &class->children, node) { + ud = container_of(cdev, struct unit_directory, class_dev); + if (ud->ne != ne || !ud->device.driver) + continue; + + pdrv = container_of(ud->device.driver, struct hpsb_protocol_driver, driver); + + if (pdrv->update && pdrv->update(ud)) { + down_write(&ud->device.bus->subsys.rwsem); + device_release_driver(&ud->device); + up_write(&ud->device.bus->subsys.rwsem); + } + } + up_read(&class->subsys.rwsem); +} + + +static void nodemgr_probe_ne(struct host_info *hi, struct node_entry *ne, int generation) +{ + struct device *dev; + + if (ne->host != hi->host || ne->in_limbo) + return; + + dev = get_device(&ne->device); + if (!dev) + return; + + /* 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(hi, ne); + else if (ne->generation == generation) + nodemgr_update_pdrv(ne); + else + nodemgr_suspend_ne(ne); + + put_device(dev); +} + + +static void nodemgr_node_probe(struct host_info *hi, int generation) +{ + struct hpsb_host *host = hi->host; + struct class *class = &nodemgr_ne_class; + struct class_device *cdev; + + /* 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. */ + down_read(&class->subsys.rwsem); + list_for_each_entry(cdev, &class->children, node) + nodemgr_probe_ne(hi, container_of(cdev, struct node_entry, class_dev), generation); + up_read(&class->subsys.rwsem); + + + /* If we had a bus reset while we were scanning the bus, it is + * possible that we did not probe all nodes. In that case, we + * skip the clean up for now, since we could remove nodes that + * were still on the bus. The bus reset increased hi->reset_sem, + * so there's a bus scan pending which will do the clean up + * eventually. + * + * 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 (generation == get_hpsb_generation(host)) + bus_rescan_devices(&ieee1394_bus_type); + + return; +} + +/* Because we are a 1394a-2000 compliant IRM, we need to inform all the other + * nodes of the broadcast channel. (Really we're only setting the validity + * bit). Other IRM responsibilities go in here as well. */ +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; + + host->csr.broadcast_channel |= 0x40000000; /* set validity bit */ + + bc = cpu_to_be32(host->csr.broadcast_channel); + + hpsb_write(host, LOCAL_BUS | ALL_NODES, get_hpsb_generation(host), + (CSR_REGISTER_BASE | CSR_BROADCAST_CHANNEL), + &bc, sizeof(quadlet_t)); + + /* 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; + struct node_entry *ne = find_entry_by_nodeid(host, root_node | LOCAL_BUS); + + if (ne && ne->busopt.cmc) + 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; + } + } + + 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 *__hi) +{ + struct host_info *hi = (struct host_info *)__hi; + struct hpsb_host *host = hi->host; + int reset_cycles = 0; + + /* No userlevel access needed */ + daemonize(hi->daemon_name); + + /* Setup our device-model entries */ + nodemgr_create_host_dev_files(host); + + /* Sit and wait for a signal to probe the nodes on the bus. This + * happens when we get a bus reset. */ + while (1) { + unsigned int generation = 0; + int i; + + if (down_interruptible(&hi->reset_sem) || + down_interruptible(&nodemgr_serialize)) { + if (try_to_freeze(PF_FREEZE)) + continue; + printk("NodeMgr: received unexpected signal?!\n" ); + break; + } + + if (hi->kill_me) { + up(&nodemgr_serialize); + break; + } + + /* Pause for 1/4 second in 1/16 second intervals, + * to make sure things settle down. */ + for (i = 0; i < 4 ; i++) { + set_current_state(TASK_INTERRUPTIBLE); + if (msleep_interruptible(63)) { + up(&nodemgr_serialize); + goto caught_signal; + } + + /* 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 the waiting over again */ + while (!down_trylock(&hi->reset_sem)) + i = 0; + + /* Check the kill_me again */ + if (hi->kill_me) { + up(&nodemgr_serialize); + goto caught_signal; + } + } + + if (!nodemgr_check_irm_capability(host, reset_cycles)) { + reset_cycles++; + up(&nodemgr_serialize); + continue; + } + + /* Scan our nodes to get the bus options and create node + * entries. This does not do the sysfs stuff, since that + * would trigger hotplug callbacks and such, which is a + * bad idea at this point. */ + nodemgr_node_scan(hi, generation); + if (!nodemgr_do_irm_duties(host, reset_cycles)) { + reset_cycles++; + up(&nodemgr_serialize); + continue; + } + + reset_cycles = 0; + + /* This actually does the full probe, with sysfs + * registration. */ + nodemgr_node_probe(hi, generation); + + /* Update some of our sysfs symlinks */ + nodemgr_update_host_dev_links(host); + + up(&nodemgr_serialize); + } + +caught_signal: + HPSB_VERBOSE("NodeMgr: Exiting thread"); + + complete_and_exit(&hi->exited, 0); +} + +int nodemgr_for_each_host(void *__data, int (*cb)(struct hpsb_host *, void *)) +{ + struct class *class = &hpsb_host_class; + struct class_device *cdev; + struct hpsb_host *host; + int error = 0; + + down_read(&class->subsys.rwsem); + list_for_each_entry(cdev, &class->children, node) { + host = container_of(cdev, struct hpsb_host, class_dev); + + if ((error = cb(host, __data))) + break; + } + up_read(&class->subsys.rwsem); + + return error; +} + +/* The following four 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. + */ + +void hpsb_node_fill_packet(struct node_entry *ne, struct hpsb_packet *pkt) +{ + pkt->host = ne->host; + pkt->generation = ne->generation; + barrier(); + pkt->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; + init_completion(&hi->exited); + sema_init(&hi->reset_sem, 0); + + sprintf(hi->daemon_name, "knodemgrd_%d", host->id); + + hi->pid = kernel_thread(nodemgr_host_thread, hi, CLONE_KERNEL); + + if (hi->pid < 0) { + HPSB_ERR ("NodeMgr: failed to start %s thread for %s", + hi->daemon_name, host->driver->name); + hpsb_destroy_hostinfo(&nodemgr_highlevel, host); + return; + } + + return; +} + +static void nodemgr_host_reset(struct hpsb_host *host) +{ + struct host_info *hi = hpsb_get_hostinfo(&nodemgr_highlevel, host); + + if (hi != NULL) { + HPSB_VERBOSE("NodeMgr: Processing host reset for %s", hi->daemon_name); + up(&hi->reset_sem); + } else + HPSB_ERR ("NodeMgr: could not process reset of unused host"); + + return; +} + +static void nodemgr_remove_host(struct hpsb_host *host) +{ + struct host_info *hi = hpsb_get_hostinfo(&nodemgr_highlevel, host); + + if (hi) { + if (hi->pid >= 0) { + hi->kill_me = 1; + mb(); + up(&hi->reset_sem); + wait_for_completion(&hi->exited); + nodemgr_remove_host_dev(&host->device); + } + } else + HPSB_ERR("NodeMgr: host %s does not exist, cannot remove", + host->driver->name); + + return; +} + +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 ret; + + ret = class_register(&nodemgr_ne_class); + if (ret < 0) + return ret; + + ret = class_register(&nodemgr_ud_class); + if (ret < 0) { + class_unregister(&nodemgr_ne_class); + return ret; + } + + hpsb_register_highlevel(&nodemgr_highlevel); + + return 0; +} + +void cleanup_ieee1394_nodemgr(void) +{ + hpsb_unregister_highlevel(&nodemgr_highlevel); + + 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..3a2f0c0 --- /dev/null +++ b/drivers/ieee1394/nodemgr.h @@ -0,0 +1,207 @@ +/* + * 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 "csr1212.h" +#include "ieee1394_core.h" +#include "ieee1394_hotplug.h" + +/* '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; + const char *vendor_oui; + + quadlet_t model_id; + struct csr1212_keyval *model_name_kv; + quadlet_t specifier_id; + quadlet_t version; + + unsigned int id; + + int ignore_driver; + + int length; /* Number of quadlets */ + + struct device device; + + struct class_device class_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 */ + const char *guid_vendor_oui; /* OUI name of guid vendor id */ + + struct hpsb_host *host; /* Host this node is attached to */ + nodeid_t nodeid; /* NodeID */ + struct bus_options busopt; /* Bus Options */ + int 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; + const char *vendor_oui; + + u32 capabilities; + struct hpsb_tlabel_pool *tpool; + + struct device device; + + struct class_device class_dev; + + /* Means this node is not attached anymore */ + int 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 *driver); +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); +} + +/* + * Returns a node entry (which has its reference count incremented) or NULL if + * the GUID in question is not known. Getting a valid entry does not mean that + * the node with this GUID is currently accessible (might be powered down). + */ +struct node_entry *hpsb_guid_get_entry(u64 guid); + +/* Same as above, but use the nodeid to get an node entry. This is not + * fool-proof by itself, since the nodeid can change. */ +struct node_entry *hpsb_nodeid_get_entry(struct hpsb_host *host, nodeid_t nodeid); + +/* + * If the entry refers to a local host, this function will return the pointer + * to the hpsb_host structure. It will return NULL otherwise. Once you have + * established it is a local host, you can use that knowledge from then on (the + * GUID won't wander to an external node). */ +struct hpsb_host *hpsb_get_host_by_ne(struct node_entry *ne); + +/* + * This will fill in the given, pre-initialised hpsb_packet with the current + * information from the node entry (host, node ID, generation number). It will + * return false if the node owning the GUID is not accessible (and not modify the + * hpsb_packet) and return true otherwise. + * + * Note that packet sending may still fail in hpsb_send_packet if a bus reset + * happens while you are trying to set up the packet (due to obsolete generation + * number). It will at least reliably fail so that you don't accidentally and + * unknowingly send your packet to the wrong node. + */ +void hpsb_node_fill_packet(struct node_entry *ne, struct hpsb_packet *pkt); + +int hpsb_node_read(struct node_entry *ne, u64 addr, + quadlet_t *buffer, size_t length); +int hpsb_node_write(struct node_entry *ne, u64 addr, + quadlet_t *buffer, size_t length); +int hpsb_node_lock(struct node_entry *ne, u64 addr, + int extcode, quadlet_t *data, quadlet_t arg); + + +/* Iterate the hosts, calling a given function with supplied data for each + * host. */ +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..97ff364 --- /dev/null +++ b/drivers/ieee1394/ohci1394.c @@ -0,0 +1,3705 @@ +/* + * 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/config.h> +#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/sched.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...) +#endif + +#ifdef CONFIG_IEEE1394_OHCI_DMA_DEBUG +#define OHCI_DMA_ALLOC(fmt, args...) \ + HPSB_ERR("%s(%s)alloc(%d): "fmt, OHCI1394_DRIVER_NAME, __FUNCTION__, \ + ++global_outstanding_dmas, ## args) +#define OHCI_DMA_FREE(fmt, args...) \ + HPSB_ERR("%s(%s)free(%d): "fmt, OHCI1394_DRIVER_NAME, __FUNCTION__, \ + --global_outstanding_dmas, ## args) +static int global_outstanding_dmas = 0; +#else +#define OHCI_DMA_ALLOC(fmt, args...) +#define OHCI_DMA_FREE(fmt, args...) +#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) + +static char version[] __devinitdata = + "$Rev: 1250 $ Ben Collins <bcollins@debian.org>"; + +/* Module Parameters */ +static int phys_dma = 1; +module_param(phys_dma, int, 0644); +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 stop_dma_rcv_ctx(struct dma_rcv_ctx *d); +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 unsigned hdr_sizes[] = +{ + 3, /* TCODE_WRITEQ */ + 4, /* TCODE_WRITEB */ + 3, /* TCODE_WRITE_RESPONSE */ + 0, /* ??? */ + 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 */ +}; + +/* Swap headers */ +static inline void packet_swab(quadlet_t *data, int tcode) +{ + size_t size = hdr_sizes[tcode]; + + if (tcode > TCODE_LOCK_RESPONSE || hdr_sizes[tcode] == 0) + return; + + while (size--) + data[size] = swab32(data[size]); +} +#else +/* Don't waste cycles on same sex byte swaps */ +#define packet_swab(w,x) +#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) +{ + char irq_buf[16]; + quadlet_t buf; + int num_ports, i; + + spin_lock_init(&ohci->phy_reg_lock); + spin_lock_init(&ohci->event_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; /* 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); + set_phy_reg_mask(ohci, 4, PHY_04_LCTRL | + (hpsb_disable_irm ? 0 : PHY_04_CONTENDER)); + + /* Set up self-id dma buffer */ + reg_write(ohci, OHCI1394_SelfIDBuffer, ohci->selfid_buf_bus); + + /* enable self-id and phys */ + reg_write(ohci, OHCI1394_LinkControlSet, OHCI1394_LinkControl_RcvSelfID | + OHCI1394_LinkControl_RcvPhyPkt); + + /* 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); + + /* Don't accept phy packets into AR request context */ + reg_write(ohci, OHCI1394_LinkControlClear, 0x00000400); + + /* 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); + + /* Initialize IR Legacy DMA */ + ohci->ir_legacy_channels = 0; + initialize_dma_rcv_ctx(&ohci->ir_legacy_context, 1); + DBGMSG("ISO receive legacy context activated"); + + /* + * Accept AT requests from all nodes. This probably + * will have to be controlled from the subsystem + * on a per node basis. + */ + reg_write(ohci,OHCI1394_AsReqFilterHiSet, 0x80000000); + + /* 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_cycleInconsistent); + + /* Enable link */ + reg_write(ohci, OHCI1394_HCControlSet, OHCI1394_HCControl_linkEnable); + + buf = reg_read(ohci, OHCI1394_Version); +#ifndef __sparc__ + sprintf (irq_buf, "%d", ohci->dev->irq); +#else + sprintf (irq_buf, "%s", __irq_itoa(ohci->dev->irq)); +#endif + PRINT(KERN_INFO, "OHCI-1394 %d.%d (PCI): IRQ=[%s] " + "MMIO=[%lx-%lx] Max Packet=[%d]", + ((((buf) >> 16) & 0xf) + (((buf) >> 20) & 0xf) * 10), + ((((buf) >> 4) & 0xf) + ((buf) & 0xf) * 10), irq_buf, + pci_resource_start(ohci->dev, 0), + pci_resource_start(ohci->dev, 0) + OHCI1394_REGISTER_SIZE - 1, + ohci->max_packet_size); + + /* 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 setting 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]; + } + packet_swab(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", __FUNCTION__, + 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)); + OHCI_DMA_ALLOC("single, block transmit packet"); + + 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; + packet_swab(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)); + OHCI_DMA_ALLOC("single, iso transmit packet"); + + 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; + } + + /* Decide whether we have an iso, a request, or a response packet */ + if (packet->type == hpsb_raw) + d = &ohci->at_req_context; + else if ((packet->tcode == TCODE_ISO_DATA) && (packet->type == hpsb_iso)) { + /* The legacy IT DMA context is initialized on first + * use. However, the alloc cannot be run from + * interrupt context, so we bail out if that is the + * case. I don't see anyone sending ISO packets from + * interrupt context anyway... */ + + if (ohci->it_legacy_context.ohci == NULL) { + if (in_interrupt()) { + PRINT(KERN_ERR, + "legacy IT context cannot be initialized during interrupt"); + return -EINVAL; + } + + if (alloc_dma_trm_ctx(ohci, &ohci->it_legacy_context, + DMA_CTX_ISO, 0, IT_NUM_DESC, + OHCI1394_IsoXmitContextBase) < 0) { + PRINT(KERN_ERR, + "error initializing legacy IT context"); + return -ENOMEM; + } + + initialize_dma_trm_ctx(&ohci->it_legacy_context); + } + + d = &ohci->it_legacy_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; + unsigned long flags; + int 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; + + case ISO_LISTEN_CHANNEL: + { + u64 mask; + + if (arg<0 || arg>63) { + PRINT(KERN_ERR, + "%s: IS0 listen channel %d is out of range", + __FUNCTION__, arg); + return -EFAULT; + } + + mask = (u64)0x1<<arg; + + spin_lock_irqsave(&ohci->IR_channel_lock, flags); + + if (ohci->ISO_channel_usage & mask) { + PRINT(KERN_ERR, + "%s: IS0 listen channel %d is already used", + __FUNCTION__, arg); + spin_unlock_irqrestore(&ohci->IR_channel_lock, flags); + return -EFAULT; + } + + ohci->ISO_channel_usage |= mask; + ohci->ir_legacy_channels |= mask; + + if (arg>31) + reg_write(ohci, OHCI1394_IRMultiChanMaskHiSet, + 1<<(arg-32)); + else + reg_write(ohci, OHCI1394_IRMultiChanMaskLoSet, + 1<<arg); + + spin_unlock_irqrestore(&ohci->IR_channel_lock, flags); + DBGMSG("Listening enabled on channel %d", arg); + break; + } + case ISO_UNLISTEN_CHANNEL: + { + u64 mask; + + if (arg<0 || arg>63) { + PRINT(KERN_ERR, + "%s: IS0 unlisten channel %d is out of range", + __FUNCTION__, arg); + return -EFAULT; + } + + mask = (u64)0x1<<arg; + + spin_lock_irqsave(&ohci->IR_channel_lock, flags); + + if (!(ohci->ISO_channel_usage & mask)) { + PRINT(KERN_ERR, + "%s: IS0 unlisten channel %d is not used", + __FUNCTION__, arg); + spin_unlock_irqrestore(&ohci->IR_channel_lock, flags); + return -EFAULT; + } + + ohci->ISO_channel_usage &= ~mask; + ohci->ir_legacy_channels &= ~mask; + + if (arg>31) + reg_write(ohci, OHCI1394_IRMultiChanMaskHiClear, + 1<<(arg-32)); + else + reg_write(ohci, OHCI1394_IRMultiChanMaskLoClear, + 1<<arg); + + spin_unlock_irqrestore(&ohci->IR_channel_lock, flags); + DBGMSG("Listening disabled on channel %d", arg); + 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), SLAB_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) + 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]; + + /* '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) +{ + int len; + + /* release the memory where the packet was */ + len = info->len; + + /* add the wasted space for padding to 4 bytes */ + if (len % 4) + len += 4 - (len % 4); + + /* add 8 bytes for the OHCI DMA data format overhead */ + len += 8; + + recv->released_bytes += 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; + 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; + + /* payload is padded to 4 bytes */ + if (len % 4) { + recv->dma_offset += 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, 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, 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; + + 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), SLAB_KERNEL); + if (!xmit) + return -ENOMEM; + + iso->hostdata = xmit; + xmit->ohci = iso->host->hostdata; + xmit->task_active = 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) + 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; + + /* 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); + + /* 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(&d->fifo_list, &packet_list); + list_splice(&d->pending_list, &packet_list); + INIT_LIST_HEAD(&d->fifo_list); + INIT_LIST_HEAD(&d->pending_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; + + spin_lock(&ohci->iso_tasklet_list_lock); + + list_for_each_entry(t, &ohci->iso_tasklet_list, link) { + mask = 1 << t->context; + + if (t->type == OHCI_ISO_TRANSMIT && tx_event & mask) + tasklet_schedule(&t->tasklet); + else if (rx_event & mask) + tasklet_schedule(&t->tasklet); + } + + spin_unlock(&ohci->iso_tasklet_list_lock); + +} + +static irqreturn_t ohci_irq_handler(int irq, void *dev_id, + struct pt_regs *regs_are_unused) +{ + 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_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); + + /* Accept Physical requests from all nodes. */ + reg_write(ohci,OHCI1394_AsReqFilterHiSet, 0xffffffff); + reg_write(ohci,OHCI1394_AsReqFilterLoSet, 0xffffffff); + + /* 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); + reg_write(ohci,OHCI1394_PhyUpperBound, 0xffff0000); + } else { + reg_write(ohci,OHCI1394_PhyReqFilterHiSet, 0x00000000); + reg_write(ohci,OHCI1394_PhyReqFilterLoSet, 0x00000000); + } + + 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) + packet_swab(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, + (cond_le32_to_cpu(d->spb[length/4-1], ohci->no_swap_incoming)>>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=" + "0x%02X 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=" + "0x%02X 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); + OHCI_DMA_FREE("single Xmit data packet"); + } + + 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 stop_dma_rcv_ctx(struct dma_rcv_ctx *d) +{ + if (d->ctrlClear) { + ohci1394_stop_context(d->ohci, d->ctrlClear, NULL); + + if (d->type == DMA_CTX_ISO) { + /* disable interrupts */ + reg_write(d->ohci, OHCI1394_IsoRecvIntMaskClear, 1 << d->ctx); + ohci1394_unregister_iso_tasklet(d->ohci, &d->ohci->ir_legacy_tasklet); + } else { + tasklet_kill(&d->task); + } + } +} + + +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]); + OHCI_DMA_FREE("consistent dma_rcv buf[%d]", 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]); + OHCI_DMA_FREE("consistent dma_rcv prg[%d]", i); + } + pci_pool_destroy(d->prg_pool); + OHCI_DMA_FREE("dma_rcv prg pool"); + kfree(d->prg_cpu); + kfree(d->prg_bus); + } + if (d->spb) 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 = kmalloc(d->num_desc * sizeof(quadlet_t*), GFP_ATOMIC); + d->buf_bus = kmalloc(d->num_desc * sizeof(dma_addr_t), GFP_ATOMIC); + + if (d->buf_cpu == NULL || d->buf_bus == NULL) { + PRINT(KERN_ERR, "Failed to allocate dma buffer"); + free_dma_rcv_ctx(d); + return -ENOMEM; + } + memset(d->buf_cpu, 0, d->num_desc * sizeof(quadlet_t*)); + memset(d->buf_bus, 0, d->num_desc * sizeof(dma_addr_t)); + + d->prg_cpu = kmalloc(d->num_desc * sizeof(struct dma_cmd*), + GFP_ATOMIC); + d->prg_bus = kmalloc(d->num_desc * sizeof(dma_addr_t), GFP_ATOMIC); + + if (d->prg_cpu == NULL || d->prg_bus == NULL) { + PRINT(KERN_ERR, "Failed to allocate dma prg"); + free_dma_rcv_ctx(d); + return -ENOMEM; + } + memset(d->prg_cpu, 0, d->num_desc * sizeof(struct dma_cmd*)); + memset(d->prg_bus, 0, d->num_desc * sizeof(dma_addr_t)); + + d->spb = kmalloc(d->split_buf_size, GFP_ATOMIC); + + if (d->spb == NULL) { + PRINT(KERN_ERR, "Failed to allocate 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++; + + OHCI_DMA_ALLOC("dma_rcv prg pool"); + + for (i=0; i<d->num_desc; i++) { + d->buf_cpu[i] = pci_alloc_consistent(ohci->dev, + d->buf_size, + d->buf_bus+i); + OHCI_DMA_ALLOC("consistent dma_rcv buf[%d]", i); + + if (d->buf_cpu[i] != NULL) { + memset(d->buf_cpu[i], 0, d->buf_size); + } else { + PRINT(KERN_ERR, + "Failed to allocate dma buffer"); + free_dma_rcv_ctx(d); + return -ENOMEM; + } + + d->prg_cpu[i] = pci_pool_alloc(d->prg_pool, SLAB_KERNEL, d->prg_bus+i); + OHCI_DMA_ALLOC("pool dma_rcv prg[%d]", i); + + if (d->prg_cpu[i] != NULL) { + memset(d->prg_cpu[i], 0, sizeof(struct dma_cmd)); + } else { + PRINT(KERN_ERR, + "Failed to allocate dma prg"); + free_dma_rcv_ctx(d); + return -ENOMEM; + } + } + + spin_lock_init(&d->lock); + + if (type == DMA_CTX_ISO) { + ohci1394_init_iso_tasklet(&ohci->ir_legacy_tasklet, + OHCI_ISO_MULTICHANNEL_RECEIVE, + dma_rcv_tasklet, (unsigned long) d); + if (ohci1394_register_iso_tasklet(ohci, + &ohci->ir_legacy_tasklet) < 0) { + PRINT(KERN_ERR, "No IR DMA context available"); + free_dma_rcv_ctx(d); + return -EBUSY; + } + + /* the IR context can be assigned to any DMA context + * by ohci1394_register_iso_tasklet */ + d->ctx = ohci->ir_legacy_tasklet.context; + d->ctrlSet = OHCI1394_IsoRcvContextControlSet + 32*d->ctx; + d->ctrlClear = OHCI1394_IsoRcvContextControlClear + 32*d->ctx; + d->cmdPtr = OHCI1394_IsoRcvCommandPtr + 32*d->ctx; + d->ctxtMatch = OHCI1394_IsoRcvContextMatch + 32*d->ctx; + } else { + 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]); + OHCI_DMA_FREE("pool dma_trm prg[%d]", i); + } + pci_pool_destroy(d->prg_pool); + OHCI_DMA_FREE("dma_trm 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 = kmalloc(d->num_desc * sizeof(struct at_dma_prg*), + GFP_KERNEL); + d->prg_bus = kmalloc(d->num_desc * sizeof(dma_addr_t), GFP_KERNEL); + + if (d->prg_cpu == NULL || d->prg_bus == NULL) { + PRINT(KERN_ERR, "Failed to allocate at dma prg"); + free_dma_trm_ctx(d); + return -ENOMEM; + } + memset(d->prg_cpu, 0, d->num_desc * sizeof(struct at_dma_prg*)); + memset(d->prg_bus, 0, d->num_desc * sizeof(dma_addr_t)); + + 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++; + + OHCI_DMA_ALLOC("dma_rcv prg pool"); + + for (i = 0; i < d->num_desc; i++) { + d->prg_cpu[i] = pci_pool_alloc(d->prg_pool, SLAB_KERNEL, d->prg_bus+i); + OHCI_DMA_ALLOC("pool dma_trm prg[%d]", 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 at dma prg"); + free_dma_trm_ctx(d); + return -ENOMEM; + } + } + + spin_lock_init(&d->lock); + + /* initialize tasklet */ + if (type == DMA_CTX_ISO) { + ohci1394_init_iso_tasklet(&ohci->it_legacy_tasklet, OHCI_ISO_TRANSMIT, + dma_trm_tasklet, (unsigned long) d); + if (ohci1394_register_iso_tasklet(ohci, + &ohci->it_legacy_tasklet) < 0) { + PRINT(KERN_ERR, "No IT DMA context available"); + free_dma_trm_ctx(d); + return -EBUSY; + } + + /* IT can be assigned to any context by register_iso_tasklet */ + d->ctx = ohci->it_legacy_tasklet.context; + d->ctrlSet = OHCI1394_IsoXmitContextControlSet + 16 * d->ctx; + d->ctrlClear = OHCI1394_IsoXmitContextControlClear + 16 * d->ctx; + d->cmdPtr = OHCI1394_IsoXmitCommandPtr + 16 * d->ctx; + } else { + 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 * + ***********************************/ + +#define FAIL(err, fmt, args...) \ +do { \ + PRINT_G(KERN_ERR, fmt , ## args); \ + ohci1394_pci_remove(dev); \ + return err; \ +} while (0) + +static int __devinit ohci1394_pci_probe(struct pci_dev *dev, + const struct pci_device_id *ent) +{ + static int version_printed = 0; + + struct hpsb_host *host; + struct ti_ohci *ohci; /* shortcut to currently handled device */ + unsigned long ohci_base; + + if (version_printed++ == 0) + PRINT_G(KERN_INFO, "%s", version); + + if (pci_enable_device(dev)) + FAIL(-ENXIO, "Failed to enable OHCI hardware"); + pci_set_master(dev); + + host = hpsb_alloc_host(&ohci1394_driver, sizeof(struct ti_ohci), &dev->dev); + if (!host) FAIL(-ENOMEM, "Failed to allocate host structure"); + + 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, "Unexpected PCI resource length of %lx!", + pci_resource_len(dev, 0)); + + /* Seems PCMCIA handles this internally. Not sure why. Seems + * pretty bogus to force a driver to special case this. */ +#ifndef PCMCIA + if (!request_mem_region (ohci_base, OHCI1394_REGISTER_SIZE, OHCI1394_DRIVER_NAME)) + FAIL(-ENOMEM, "MMIO resource (0x%lx - 0x%lx) unavailable", + ohci_base, ohci_base + OHCI1394_REGISTER_SIZE); +#endif + ohci->init_state = OHCI_INIT_HAVE_MEM_REGION; + + ohci->registers = ioremap(ohci_base, OHCI1394_REGISTER_SIZE); + if (ohci->registers == NULL) + FAIL(-ENXIO, "Failed to remap registers - card not accessible"); + 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); + OHCI_DMA_ALLOC("consistent csr_config_rom"); + if (ohci->csr_config_rom_cpu == NULL) + FAIL(-ENOMEM, "Failed to allocate buffer config rom"); + 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); + OHCI_DMA_ALLOC("consistent selfid_buf"); + + if (ohci->selfid_buf_cpu == NULL) + FAIL(-ENOMEM, "Failed to allocate DMA buffer for self-id packets"); + 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) + FAIL(-ENOMEM, "Failed to allocate AR Req context"); + + /* 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) + FAIL(-ENOMEM, "Failed to allocate AR Resp context"); + + /* 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) + FAIL(-ENOMEM, "Failed to allocate AT Req context"); + + /* 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) + FAIL(-ENOMEM, "Failed to allocate AT Resp context"); + + /* 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); + DBGMSG("%d iso receive contexts available", + ohci->nb_iso_rcv_ctx); + + ohci->nb_iso_xmit_ctx = + get_nb_iso_ctx(ohci, OHCI1394_IsoXmitIntMaskSet); + DBGMSG("%d iso transmit contexts available", + ohci->nb_iso_xmit_ctx); + + /* 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); + + /* Allocate the IR DMA context right here so we don't have + * to do it in interrupt path - note that this doesn't + * waste much memory and avoids the jugglery required to + * allocate it in IRQ path. */ + if (alloc_dma_rcv_ctx(ohci, &ohci->ir_legacy_context, + DMA_CTX_ISO, 0, IR_NUM_DESC, + IR_BUF_SIZE, IR_SPLIT_BUF_SIZE, + OHCI1394_IsoRcvContextBase) < 0) { + FAIL(-ENOMEM, "Cannot allocate IR Legacy DMA context"); + } + + /* We hopefully don't have to pre-allocate IT DMA like we did + * for IR DMA above. Allocate it on-demand and mark inactive. */ + ohci->it_legacy_context.ohci = NULL; + + if (request_irq(dev->irq, ohci_irq_handler, SA_SHIRQ, + OHCI1394_DRIVER_NAME, ohci)) + FAIL(-ENOMEM, "Failed to allocate shared interrupt %d", dev->irq); + + 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; + + /* Tell the highlevel this host is ready */ + if (hpsb_add_host(host)) + FAIL(-ENOMEM, "Failed to register host with highlevel"); + + ohci->init_state = OHCI_INIT_DONE; + + return 0; +#undef FAIL +} + +static void ohci1394_pci_remove(struct pci_dev *pdev) +{ + struct ti_ohci *ohci; + struct device *dev; + + ohci = pci_get_drvdata(pdev); + if (!ohci) + return; + + dev = get_device(&ohci->host->device); + + switch (ohci->init_state) { + case OHCI_INIT_DONE: + stop_dma_rcv_ctx(&ohci->ir_legacy_context); + 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(ohci->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 AR dma */ + free_dma_rcv_ctx(&ohci->ar_req_context); + free_dma_rcv_ctx(&ohci->ar_resp_context); + + /* Free AT dma */ + free_dma_trm_ctx(&ohci->at_req_context); + free_dma_trm_ctx(&ohci->at_resp_context); + + /* Free IR dma */ + free_dma_rcv_ctx(&ohci->ir_legacy_context); + + /* Free IT dma */ + free_dma_trm_ctx(&ohci->it_legacy_context); + + /* Free IR legacy dma */ + free_dma_rcv_ctx(&ohci->ir_legacy_context); + + + case OHCI_INIT_HAVE_SELFID_BUFFER: + pci_free_consistent(ohci->dev, OHCI1394_SI_DMA_BUF_SIZE, + ohci->selfid_buf_cpu, + ohci->selfid_buf_bus); + OHCI_DMA_FREE("consistent selfid_buf"); + + case OHCI_INIT_HAVE_CONFIG_ROM_BUFFER: + pci_free_consistent(ohci->dev, OHCI_CONFIG_ROM_LEN, + ohci->csr_config_rom_cpu, + ohci->csr_config_rom_bus); + OHCI_DMA_FREE("consistent csr_config_rom"); + + case OHCI_INIT_HAVE_IOMAPPING: + iounmap(ohci->registers); + + case OHCI_INIT_HAVE_MEM_REGION: +#ifndef PCMCIA + release_mem_region(pci_resource_start(ohci->dev, 0), + OHCI1394_REGISTER_SIZE); +#endif + +#ifdef CONFIG_PPC_PMAC + /* On UniNorth, power down the cable and turn off the chip + * clock when the module is removed to save power on + * laptops. Turning it back ON is done by the arch code when + * pci_enable_device() is called */ + { + struct device_node* of_node; + + of_node = pci_device_to_OF_node(ohci->dev); + if (of_node) { + pmac_call_feature(PMAC_FTR_1394_ENABLE, of_node, 0, 0); + pmac_call_feature(PMAC_FTR_1394_CABLE_POWER, of_node, 0, 0); + } + } +#endif /* CONFIG_PPC_PMAC */ + + case OHCI_INIT_ALLOC_HOST: + pci_set_drvdata(ohci->dev, NULL); + } + + if (dev) + put_device(dev); +} + + +static int ohci1394_pci_resume (struct pci_dev *pdev) +{ +#ifdef CONFIG_PMAC_PBOOK + { + struct device_node *of_node; + + /* Re-enable 1394 */ + of_node = pci_device_to_OF_node (pdev); + if (of_node) + pmac_call_feature (PMAC_FTR_1394_ENABLE, of_node, 0, 1); + } +#endif + + pci_enable_device(pdev); + + return 0; +} + + +static int ohci1394_pci_suspend (struct pci_dev *pdev, pm_message_t state) +{ +#ifdef CONFIG_PMAC_PBOOK + { + struct device_node *of_node; + + /* Disable 1394 */ + of_node = pci_device_to_OF_node (pdev); + if (of_node) + pmac_call_feature(PMAC_FTR_1394_ENABLE, of_node, 0, 0); + } +#endif + + return 0; +} + + +#define PCI_CLASS_FIREWIRE_OHCI ((PCI_CLASS_SERIAL_FIREWIRE << 8) | 0x10) + +static struct pci_device_id ohci1394_pci_tbl[] = { + { + .class = PCI_CLASS_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, + .resume = ohci1394_pci_resume, + .suspend = ohci1394_pci_suspend, +}; + + + +/*********************************** + * OHCI1394 Video Interface * + ***********************************/ + +/* essentially the only purpose of this code is to allow another + module to hook into ohci's interrupt handler */ + +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..d1758d4 --- /dev/null +++ b/drivers/ieee1394/ohci1394.h @@ -0,0 +1,456 @@ +/* + * 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 0x2 +#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 receive (legacy API) */ + u64 ir_legacy_channels; /* note: this differs from ISO_channel_usage; + it only accounts for channels listened to + by the legacy API, so that we can know when + it is safe to free the legacy API context */ + + struct dma_rcv_ctx ir_legacy_context; + struct ohci1394_iso_tasklet ir_legacy_tasklet; + + /* iso transmit */ + int nb_iso_xmit_ctx; + unsigned long it_ctx_usage; /* use test_and_set_bit() for atomicity */ + + /* iso transmit (legacy API) */ + struct dma_trm_ctx it_legacy_context; + struct ohci1394_iso_tasklet it_legacy_tasklet; + + 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, + * amdtp 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 > 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 + +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); + +/* returns zero if successful, one if DMA context is locked up */ +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/oui.db b/drivers/ieee1394/oui.db new file mode 100644 index 0000000..592c8a6 --- /dev/null +++ b/drivers/ieee1394/oui.db @@ -0,0 +1,7048 @@ +000000 XEROX CORPORATION +000001 XEROX CORPORATION +000002 XEROX CORPORATION +000003 XEROX CORPORATION +000004 XEROX CORPORATION +000005 XEROX CORPORATION +000006 XEROX CORPORATION +000007 XEROX CORPORATION +000008 XEROX CORPORATION +000009 XEROX CORPORATION +00000A OMRON TATEISI ELECTRONICS CO. +00000B MATRIX CORPORATION +00000C CISCO SYSTEMS, INC. +00000D FIBRONICS LTD. +00000E FUJITSU LIMITED +00000F NEXT, INC. +000010 SYTEK INC. +000011 NORMEREL SYSTEMES +000012 INFORMATION TECHNOLOGY LIMITED +000013 CAMEX +000014 NETRONIX +000015 DATAPOINT CORPORATION +000016 DU PONT PIXEL SYSTEMS . +000017 TEKELEC +000018 WEBSTER COMPUTER CORPORATION +000019 APPLIED DYNAMICS INTERNATIONAL +00001A ADVANCED MICRO DEVICES +00001B NOVELL INC. +00001C BELL TECHNOLOGIES +00001D CABLETRON SYSTEMS, INC. +00001E TELSIST INDUSTRIA ELECTRONICA +00001F Telco Systems, Inc. +000020 DATAINDUSTRIER DIAB AB +000021 SUREMAN COMP. & COMMUN. CORP. +000022 VISUAL TECHNOLOGY INC. +000023 ABB INDUSTRIAL SYSTEMS AB +000024 CONNECT AS +000025 RAMTEK CORP. +000026 SHA-KEN CO., LTD. +000027 JAPAN RADIO COMPANY +000028 PRODIGY SYSTEMS CORPORATION +000029 IMC NETWORKS CORP. +00002A TRW - SEDD/INP +00002B CRISP AUTOMATION, INC +00002C AUTOTOTE LIMITED +00002D CHROMATICS INC +00002E SOCIETE EVIRA +00002F TIMEPLEX INC. +000030 VG LABORATORY SYSTEMS LTD +000031 QPSX COMMUNICATIONS PTY LTD +000032 Marconi plc +000033 EGAN MACHINERY COMPANY +000034 NETWORK RESOURCES CORPORATION +000035 SPECTRAGRAPHICS CORPORATION +000036 ATARI CORPORATION +000037 OXFORD METRICS LIMITED +000038 CSS LABS +000039 TOSHIBA CORPORATION +00003A CHYRON CORPORATION +00003B i Controls, Inc. +00003C AUSPEX SYSTEMS INC. +00003D UNISYS +00003E SIMPACT +00003F SYNTREX, INC. +000040 APPLICON, INC. +000041 ICE CORPORATION +000042 METIER MANAGEMENT SYSTEMS LTD. +000043 MICRO TECHNOLOGY +000044 CASTELLE CORPORATION +000045 FORD AEROSPACE & COMM. CORP. +000046 OLIVETTI NORTH AMERICA +000047 NICOLET INSTRUMENTS CORP. +000048 SEIKO EPSON CORPORATION +000049 APRICOT COMPUTERS, LTD +00004A ADC CODENOLL TECHNOLOGY CORP. +00004B ICL DATA OY +00004C NEC CORPORATION +00004D DCI CORPORATION +00004E AMPEX CORPORATION +00004F LOGICRAFT, INC. +000050 RADISYS CORPORATION +000051 HOB ELECTRONIC GMBH & CO. KG +000052 Intrusion.com, Inc. +000053 COMPUCORP +000054 MODICON, INC. +000055 COMMISSARIAT A L`ENERGIE ATOM. +000056 DR. B. STRUCK +000057 SCITEX CORPORATION LTD. +000058 RACORE COMPUTER PRODUCTS INC. +000059 HELLIGE GMBH +00005A SysKonnect GmbH +00005B ELTEC ELEKTRONIK AG +00005C TELEMATICS INTERNATIONAL INC. +00005D CS TELECOM +00005E USC INFORMATION SCIENCES INST +00005F SUMITOMO ELECTRIC IND., LTD. +000060 KONTRON ELEKTRONIK GMBH +000061 GATEWAY COMMUNICATIONS +000062 BULL HN INFORMATION SYSTEMS +000063 DR.ING.SEUFERT GMBH +000064 YOKOGAWA DIGITAL COMPUTER CORP +000065 NETWORK ASSOCIATES, INC. +000066 TALARIS SYSTEMS, INC. +000067 SOFT * RITE, INC. +000068 ROSEMOUNT CONTROLS +000069 CONCORD COMMUNICATIONS INC +00006A COMPUTER CONSOLES INC. +00006B SILICON GRAPHICS INC./MIPS +00006D CRAY COMMUNICATIONS, LTD. +00006E ARTISOFT, INC. +00006F Madge Ltd. +000070 HCL LIMITED +000071 ADRA SYSTEMS INC. +000072 MINIWARE TECHNOLOGY +000073 SIECOR CORPORATION +000074 RICOH COMPANY LTD. +000075 Nortel Networks +000076 ABEKAS VIDEO SYSTEM +000077 INTERPHASE CORPORATION +000078 LABTAM LIMITED +000079 NETWORTH INCORPORATED +00007A DANA COMPUTER INC. +00007B RESEARCH MACHINES +00007C AMPERE INCORPORATED +00007D SUN MICROSYSTEMS, INC. +00007E CLUSTRIX CORPORATION +00007F LINOTYPE-HELL AG +000080 CRAY COMMUNICATIONS A/S +000081 BAY NETWORKS +000082 LECTRA SYSTEMES SA +000083 TADPOLE TECHNOLOGY PLC +000084 SUPERNET +000085 CANON INC. +000086 MEGAHERTZ CORPORATION +000087 HITACHI, LTD. +000088 COMPUTER NETWORK TECH. CORP. +000089 CAYMAN SYSTEMS INC. +00008A DATAHOUSE INFORMATION SYSTEMS +00008B INFOTRON +00008C Alloy Computer Products (Australia) Pty Ltd +00008D VERDIX CORPORATION +00008E SOLBOURNE COMPUTER, INC. +00008F RAYTHEON COMPANY +000090 MICROCOM +000091 ANRITSU CORPORATION +000092 COGENT DATA TECHNOLOGIES +000093 PROTEON INC. +000094 ASANTE TECHNOLOGIES +000095 SONY TEKTRONIX CORP. +000096 MARCONI ELECTRONICS LTD. +000097 EPOCH SYSTEMS +000098 CROSSCOMM CORPORATION +000099 MTX, INC. +00009A RC COMPUTER A/S +00009B INFORMATION INTERNATIONAL, INC +00009C ROLM MIL-SPEC COMPUTERS +00009D LOCUS COMPUTING CORPORATION +00009E MARLI S.A. +00009F AMERISTAR TECHNOLOGIES INC. +0000A0 TOKYO SANYO ELECTRIC CO. LTD. +0000A1 MARQUETTE ELECTRIC CO. +0000A2 BAY NETWORKS +0000A3 NETWORK APPLICATION TECHNOLOGY +0000A4 ACORN COMPUTERS LIMITED +0000A5 COMPATIBLE SYSTEMS CORP. +0000A6 NETWORK GENERAL CORPORATION +0000A7 NETWORK COMPUTING DEVICES INC. +0000A8 STRATUS COMPUTER INC. +0000A9 NETWORK SYSTEMS CORP. +0000AA XEROX CORPORATION +0000AB LOGIC MODELING CORPORATION +0000AC CONWARE COMPUTER CONSULTING +0000AD BRUKER INSTRUMENTS INC. +0000AE DASSAULT ELECTRONIQUE +0000AF NUCLEAR DATA INSTRUMENTATION +0000B0 RND-RAD NETWORK DEVICES +0000B1 ALPHA MICROSYSTEMS INC. +0000B2 TELEVIDEO SYSTEMS, INC. +0000B3 CIMLINC INCORPORATED +0000B4 EDIMAX COMPUTER COMPANY +0000B5 DATABILITY SOFTWARE SYS. INC. +0000B6 MICRO-MATIC RESEARCH +0000B7 DOVE COMPUTER CORPORATION +0000B8 SEIKOSHA CO., LTD. +0000B9 MCDONNELL DOUGLAS COMPUTER SYS +0000BA SIIG, INC. +0000BB TRI-DATA +0000BC ALLEN-BRADLEY CO. INC. +0000BD MITSUBISHI CABLE COMPANY +0000BE THE NTI GROUP +0000BF SYMMETRIC COMPUTER SYSTEMS +0000C0 WESTERN DIGITAL CORPORATION +0000C1 Madge Ltd. +0000C2 INFORMATION PRESENTATION TECH. +0000C3 HARRIS CORP COMPUTER SYS DIV +0000C4 WATERS DIV. OF MILLIPORE +0000C5 FARALLON COMPUTING/NETOPIA +0000C6 EON SYSTEMS +0000C7 ARIX CORPORATION +0000C8 ALTOS COMPUTER SYSTEMS +0000C9 EMULEX CORPORATION +0000CA APPLITEK +0000CB COMPU-SHACK ELECTRONIC GMBH +0000CC DENSAN CO., LTD. +0000CD Allied Telesyn Research Ltd. +0000CE MEGADATA CORP. +0000CF HAYES MICROCOMPUTER PRODUCTS +0000D0 DEVELCON ELECTRONICS LTD. +0000D1 ADAPTEC INCORPORATED +0000D2 SBE, INC. +0000D3 WANG LABORATORIES INC. +0000D4 PURE DATA LTD. +0000D5 MICROGNOSIS INTERNATIONAL +0000D6 PUNCH LINE HOLDING +0000D7 DARTMOUTH COLLEGE +0000D8 NOVELL, INC. +0000D9 NIPPON TELEGRAPH & TELEPHONE +0000DA ATEX +0000DB BRITISH TELECOMMUNICATIONS PLC +0000DC HAYES MICROCOMPUTER PRODUCTS +0000DD TCL INCORPORATED +0000DE CETIA +0000DF BELL & HOWELL PUB SYS DIV +0000E0 QUADRAM CORP. +0000E1 GRID SYSTEMS +0000E2 ACER TECHNOLOGIES CORP. +0000E3 INTEGRATED MICRO PRODUCTS LTD +0000E4 IN2 GROUPE INTERTECHNIQUE +0000E5 SIGMEX LTD. +0000E6 APTOR PRODUITS DE COMM INDUST +0000E7 STAR GATE TECHNOLOGIES +0000E8 ACCTON TECHNOLOGY CORP. +0000E9 ISICAD, INC. +0000EA UPNOD AB +0000EB MATSUSHITA COMM. IND. CO. LTD. +0000EC MICROPROCESS +0000ED APRIL +0000EE NETWORK DESIGNERS, LTD. +0000EF KTI +0000F0 SAMSUNG ELECTRONICS CO., LTD. +0000F1 MAGNA COMPUTER CORPORATION +0000F2 SPIDER COMMUNICATIONS +0000F3 GANDALF DATA LIMITED +0000F4 ALLIED TELESYN INTERNATIONAL +0000F5 DIAMOND SALES LIMITED +0000F6 APPLIED MICROSYSTEMS CORP. +0000F7 YOUTH KEEP ENTERPRISE CO LTD +0000F8 DIGITAL EQUIPMENT CORPORATION +0000F9 QUOTRON SYSTEMS INC. +0000FA MICROSAGE COMPUTER SYSTEMS INC +0000FB RECHNER ZUR KOMMUNIKATION +0000FC MEIKO +0000FD HIGH LEVEL HARDWARE +0000FE ANNAPOLIS MICRO SYSTEMS +0000FF CAMTEC ELECTRONICS LTD. +000100 EQUIP'TRANS +000102 3COM CORPORATION +000103 3COM CORPORATION +000104 DVICO Co., Ltd. +000105 BECKHOFF GmbH +000106 Tews Datentechnik GmbH +000107 Leiser GmbH +000108 AVLAB Technology, Inc. +000109 Nagano Japan Radio Co., Ltd. +00010A CIS TECHNOLOGY INC. +00010B Space CyberLink, Inc. +00010C System Talks Inc. +00010D CORECO, INC. +00010E Bri-Link Technologies Co., Ltd +00010F Nishan Systems, Inc. +000110 Gotham Networks +000111 iDigm Inc. +000112 Shark Multimedia Inc. +000113 OLYMPUS CORPORATION +000114 KANDA TSUSHIN KOGYO CO., LTD. +000115 EXTRATECH CORPORATION +000116 Netspect Technologies, Inc. +000117 CANAL + +000118 EZ Digital Co., Ltd. +000119 Action Controls Pty. Ltd. +00011A EEH DataLink GmbH +00011B Unizone Technologies, Inc. +00011C Universal Talkware Corporation +00011D Centillium Communications +00011E Precidia Technologies, Inc. +00011F RC Networks, Inc. +000120 OSCILLOQUARTZ S.A. +000121 RapidStream Inc. +000122 Trend Communications, Ltd. +000123 DIGITAL ELECTRONICS CORP. +000124 Acer Incorporated +000125 YAESU MUSEN CO., LTD. +000126 PAC Labs +000127 The OPEN Group Limited +000128 EnjoyWeb, Inc. +000129 DFI Inc. +00012A Telematica Sistems Inteligente +00012B TELENET Co., Ltd. +00012C Aravox Technologies, Inc. +00012D Komodo Technology +00012E PC Partner Ltd. +00012F Twinhead International Corp +000130 Extreme Networks +000131 Detection Systems, Inc. +000132 Dranetz - BMI +000133 KYOWA Electronic Instruments C +000134 SIG Positec Systems AG +000135 KDC Corp. +000136 CyberTAN Technology, Inc. +000137 IT Farm Corporation +000138 XAVi Technologies Corp. +000139 Point Multimedia Systems +00013A SHELCAD COMMUNICATIONS, LTD. +00013B BNA SYSTEMS +00013C TIW SYSTEMS +00013D RiscStation Ltd. +00013E Ascom Tateco AB +00013F Neighbor World Co., Ltd. +000140 Sendtek Corporation +000141 CABLE PRINT +000142 Cisco Systems, Inc. +000143 Cisco Systems, Inc. +000144 Cereva Networks, Inc. +000145 WINSYSTEMS, INC. +000146 Tesco Controls, Inc. +000147 Zhone Technologies +000148 X-traWeb Inc. +000149 T.D.T. Transfer Data Test GmbH +00014A SONY COMPUTER SCIENCE LABS., I +00014B Ennovate Networks, Inc. +00014C Berkeley Process Control +00014D Shin Kin Enterprises Co., Ltd +00014E WIN Enterprises, Inc. +00014F LUMINOUS Networks, Inc. +000150 GILAT COMMUNICATIONS, LTD. +000151 Ensemble Communications +000152 CHROMATEK INC. +000153 ARCHTEK TELECOM CORPORATION +000154 G3M Corporation +000155 Promise Technology, Inc. +000156 FIREWIREDIRECT.COM, INC. +000157 SYSWAVE CO., LTD +000158 Electro Industries/Gauge Tech +000159 S1 Corporation +00015A Digital Video Broadcasting +00015B ITALTEL S.p.A/RF-UP-I +00015C CADANT INC. +00015D Sun Microsystems, Inc +00015E BEST TECHNOLOGY CO., LTD. +00015F DIGITAL DESIGN GmbH +000160 ELMEX Co., LTD. +000161 Meta Machine Technology +000162 Cygnet Technologies, Inc. +000163 Cisco Systems, Inc. +000164 Cisco Systems, Inc. +000165 AirSwitch Corporation +000166 TC GROUP A/S +000167 HIOKI E.E. CORPORATION +000168 VITANA CORPORATION +000169 Celestix Networks Pte Ltd. +00016A ALITEC +00016B LightChip, Inc. +00016C FOXCONN +00016D CarrierComm Inc. +00016E Conklin Corporation +00016F HAITAI ELECTRONICS CO., LTD. +000170 ESE Embedded System Engineer'g +000171 Allied Data Technologies +000172 TechnoLand Co., LTD. +000173 JNI Corporation +000174 CyberOptics Corporation +000175 Radiant Communications Corp. +000176 Orient Silver Enterprises +000177 EDSL +000178 MARGI Systems, Inc. +000179 WIRELESS TECHNOLOGY, INC. +00017A Chengdu Maipu Electric Industrial Co., Ltd. +00017B Heidelberger Druckmaschinen AG +00017C AG-E GmbH +00017D ThermoQuest +00017E ADTEK System Science Co., Ltd. +00017F Experience Music Project +000180 AOpen, Inc. +000181 Nortel Networks +000182 DICA TECHNOLOGIES AG +000183 ANITE TELECOMS +000184 SIEB & MEYER AG +000185 Aloka Co., Ltd. +000186 DISCH GmbH +000187 i2SE GmbH +000188 LXCO Technologies ag +000189 Refraction Technology, Inc. +00018A ROI COMPUTER AG +00018B NetLinks Co., Ltd. +00018C Mega Vision +00018D AudeSi Technologies +00018E Logitec Corporation +00018F Kenetec, Inc. +000190 SMK-M +000191 SYRED Data Systems +000192 Texas Digital Systems +000193 Hanbyul Telecom Co., Ltd. +000194 Capital Equipment Corporation +000195 Sena Technologies, Inc. +000196 Cisco Systems, Inc. +000197 Cisco Systems, Inc. +000198 Darim Vision +000199 HeiSei Electronics +00019A LEUNIG GmbH +00019B Kyoto Microcomputer Co., Ltd. +00019C JDS Uniphase Inc. +00019D E-Control Systems, Inc. +00019E ESS Technology, Inc. +00019F Phonex Broadband +0001A0 Infinilink Corporation +0001A1 Mag-Tek, Inc. +0001A2 Logical Co., Ltd. +0001A3 GENESYS LOGIC, INC. +0001A4 Microlink Corporation +0001A5 Nextcomm, Inc. +0001A6 Scientific-Atlanta Arcodan A/S +0001A7 UNEX TECHNOLOGY CORPORATION +0001A8 Welltech Computer Co., Ltd. +0001A9 BMW AG +0001AA Airspan Communications, Ltd. +0001AB Main Street Networks +0001AC Sitara Networks, Inc. +0001AD Coach Master International d.b.a. CMI Worldwide, Inc. +0001AE Trex Enterprises +0001AF Motorola Computer Group +0001B0 Fulltek Technology Co., Ltd. +0001B1 General Bandwidth +0001B2 Digital Processing Systems, Inc. +0001B3 Precision Electronic Manufacturing +0001B4 Wayport, Inc. +0001B5 Turin Networks, Inc. +0001B6 SAEJIN T&M Co., Ltd. +0001B7 Centos, Inc. +0001B8 Netsensity, Inc. +0001B9 SKF Condition Monitoring +0001BA IC-Net, Inc. +0001BB Frequentis +0001BC Brains Corporation +0001BD Peterson Electro-Musical Products, Inc. +0001BE Gigalink Co., Ltd. +0001BF Teleforce Co., Ltd. +0001C0 CompuLab, Ltd. +0001C1 Vitesse Semiconductor Corporation +0001C2 ARK Research Corp. +0001C3 Acromag, Inc. +0001C4 NeoWave, Inc. +0001C5 Simpler Networks +0001C6 Quarry Technologies +0001C7 Cisco Systems, Inc. +0001C8 THOMAS CONRAD CORP. +0001C8 CONRAD CORP. +0001C9 Cisco Systems, Inc. +0001CA Geocast Network Systems, Inc. +0001CB NetGame, Ltd. +0001CC Japan Total Design Communication Co., Ltd. +0001CD ARtem +0001CE Custom Micro Products, Ltd. +0001CF Alpha Data Parallel Systems, Ltd. +0001D0 VitalPoint, Inc. +0001D1 CoNet Communications, Inc. +0001D2 MacPower Peripherals, Ltd. +0001D3 PAXCOMM, Inc. +0001D4 Leisure Time, Inc. +0001D5 HAEDONG INFO & COMM CO., LTD +0001D6 MAN Roland Druckmaschinen AG +0001D7 F5 Networks, Inc. +0001D8 Teltronics, Inc. +0001D9 Sigma, Inc. +0001DA WINCOMM Corporation +0001DB Freecom Technologies GmbH +0001DC Activetelco +0001DD Avail Networks +0001DE Trango Systems, Inc. +0001DF ISDN Communications, Ltd. +0001E0 Fast Systems, Inc. +0001E1 Kinpo Electronics, Inc. +0001E2 Ando Electric Corporation +0001E3 Siemens AG +0001E4 Sitera, Inc. +0001E5 Supernet, Inc. +0001E6 Hewlett-Packard Company +0001E7 Hewlett-Packard Company +0001E8 Force10 Networks, Inc. +0001E9 Litton Marine Systems B.V. +0001EA Cirilium Corp. +0001EB C-COM Corporation +0001EC Ericsson Group +0001ED SETA Corp. +0001EE Comtrol Europe, Ltd. +0001EF Camtel Technology Corp. +0001F0 Tridium, Inc. +0001F1 Innovative Concepts, Inc. +0001F2 Mark of the Unicorn, Inc. +0001F3 QPS, Inc. +0001F4 Enterasys Networks +0001F5 ERIM S.A. +0001F6 Association of Musical Electronics Industry +0001F7 Image Display Systems, Inc. +0001F8 Adherent Systems, Ltd. +0001F9 TeraGlobal Communications Corp. +0001FA HOROSCAS +0001FB DoTop Technology, Inc. +0001FC Keyence Corporation +0001FD Digital Voice Systems, Inc. +0001FE DIGITAL EQUIPMENT CORPORATION +0001FF Data Direct Networks, Inc. +000200 Net & Sys Co., Ltd. +000201 IFM Electronic gmbh +000202 Amino Communications, Ltd. +000203 Woonsang Telecom, Inc. +000204 Bodmann Industries Elektronik GmbH +000205 Hitachi Denshi, Ltd. +000206 Telital R&D Denmark A/S +000207 VisionGlobal Network Corp. +000208 Unify Networks, Inc. +000209 Shenzhen SED Information Technology Co., Ltd. +00020A Gefran Spa +00020B Native Networks, Inc. +00020C Metro-Optix +00020D Micronpc.com +00020E Laurel Networks, Inc. +00020F AATR +000210 Fenecom +000211 Nature Worldwide Technology Corp. +000212 SierraCom +000213 S.D.E.L. +000214 DTVRO +000215 Cotas Computer Technology A/B +000216 Cisco Systems, Inc. +000217 Cisco Systems, Inc. +000218 Advanced Scientific Corp +000219 Paralon Technologies +00021A Zuma Networks +00021B Kollmorgen-Servotronix +00021C Network Elements, Inc. +00021D Data General Communication Ltd. +00021E SIMTEL S.R.L. +00021F Aculab PLC +000220 Canon Aptex, Inc. +000221 DSP Application, Ltd. +000222 Chromisys, Inc. +000223 ClickTV +000224 Lantern Communications, Inc. +000225 Certus Technology, Inc. +000226 XESystems, Inc. +000227 ESD GmbH +000228 Necsom, Ltd. +000229 Adtec Corporation +00022A Asound Electronic +00022B Tamura Electric Works, Ltd. +00022C ABB Bomem, Inc. +00022D Agere Systems +00022E TEAC Corp. R& D +00022F P-Cube, Ltd. +000230 Intersoft Electronics +000231 Ingersoll-Rand +000232 Avision, Inc. +000233 Mantra Communications, Inc. +000234 Imperial Technology, Inc. +000235 Paragon Networks International +000236 INIT GmbH +000237 Cosmo Research Corp. +000238 Serome Technology, Inc. +000239 Visicom +00023A ZSK Stickmaschinen GmbH +00023B Redback Networks +00023C Creative Technology, Ltd. +00023D NuSpeed, Inc. +00023E Selta Telematica S.p.a +00023F Compal Electronics, Inc. +000240 Seedek Co., Ltd. +000241 Amer.com +000242 Videoframe Systems +000243 Raysis Co., Ltd. +000244 SURECOM Technology Co. +000245 Lampus Co, Ltd. +000246 All-Win Tech Co., Ltd. +000247 Great Dragon Information Technology (Group) Co., Ltd. +000248 Pilz GmbH & Co. +000249 Aviv Infocom Co, Ltd. +00024A Cisco Systems, Inc. +00024B Cisco Systems, Inc. +00024C SiByte, Inc. +00024D Mannesman Dematic Colby Pty. Ltd. +00024E Datacard Group +00024F IPM Datacom S.R.L. +000250 Geyser Networks, Inc. +000251 Soma Networks +000252 Carrier Corporation +000253 Televideo, Inc. +000254 WorldGate +000255 IBM Corporation +000256 Alpha Processor, Inc. +000257 Microcom Corp. +000258 Flying Packets Communications +000259 Tsann Kuen China (Shanghai)Enterprise Co., Ltd. IT Group +00025A Catena Networks +00025B Cambridge Silicon Radio +00025C SCI Systems (Kunshan) Co., Ltd. +00025D Calix Networks +00025E High Technology Ltd +00025F Nortel Networks +000260 Accordion Networks, Inc. +000261 i3 Micro Technology AB +000262 Soyo Group Soyo Com Tech Co., Ltd +000263 UPS Manufacturing SRL +000264 AudioRamp.com +000265 Virditech Co. Ltd. +000266 Thermalogic Corporation +000267 NODE RUNNER, INC. +000268 Harris Government Communications +000269 Nadatel Co., Ltd +00026A Cocess Telecom Co., Ltd. +00026B BCM Computers Co., Ltd. +00026C Philips CFT +00026D Adept Telecom +00026E NeGeN Access, Inc. +00026F Senao International Co., Ltd. +000270 Crewave Co., Ltd. +000271 Vpacket Communications +000272 CC&C Technologies, Inc. +000273 Coriolis Networks +000274 Tommy Technologies Corp. +000275 SMART Technologies, Inc. +000276 Primax Electronics Ltd. +000277 Cash Systemes Industrie +000278 Samsung Electro-Mechanics Co., Ltd. +000279 Control Applications, Ltd. +00027A IOI Technology Corporation +00027B Amplify Net, Inc. +00027C Trilithic, Inc. +00027D Cisco Systems, Inc. +00027E Cisco Systems, Inc. +00027F ask-technologies.com +000280 Mu Net, Inc. +000281 Madge Ltd. +000282 ViaClix, Inc. +000283 Spectrum Controls, Inc. +000284 Alstom T&D P&C +000285 Riverstone Networks +000286 Occam Networks +000287 Adapcom +000288 GLOBAL VILLAGE COMMUNICATION +000289 DNE Technologies +00028A Ambit Microsystems Corporation +00028B VDSL Systems OY +00028C Micrel-Synergy Semiconductor +00028D Movita Technologies, Inc. +00028E Rapid 5 Networks, Inc. +00028F Globetek, Inc. +000290 Woorigisool, Inc. +000291 Open Network Co., Ltd. +000292 Logic Innovations, Inc. +000293 Solid Data Systems +000294 Tokyo Sokushin Co., Ltd. +000295 IP.Access Limited +000296 Lectron Co,. Ltd. +000297 C-COR.net +000298 Broadframe Corporation +000299 Apex, Inc. +00029A Storage Apps +00029B Kreatel Communications AB +00029C 3COM +00029D Merix Corp. +00029E Information Equipment Co., Ltd. +00029F L-3 Communication Aviation Recorders +0002A0 Flatstack Ltd. +0002A1 World Wide Packets +0002A2 Hilscher GmbH +0002A3 ABB Power Automation +0002A4 AddPac Technology Co., Ltd. +0002A5 Compaq Computer Corporation +0002A6 Effinet Systems Co., Ltd. +0002A7 Vivace Networks +0002A8 Air Link Technology +0002A9 RACOM, s.r.o. +0002AA PLcom Co., Ltd. +0002AB CTC Union Technologies Co., Ltd. +0002AC 3PAR data +0002AD Pentax Corpotation +0002AE Scannex Electronics Ltd. +0002AF TeleCruz Technology, Inc. +0002B0 Hokubu Communication & Industrial Co., Ltd. +0002B1 Anritsu, Ltd. +0002B2 Cablevision +0002B3 Intel Corporation +0002B4 DAPHNE +0002B5 Avnet, Inc. +0002B6 Acrosser Technology Co., Ltd. +0002B7 Watanabe Electric Industry Co., Ltd. +0002B8 WHI KONSULT AB +0002B9 Cisco Systems, Inc. +0002BA Cisco Systems, Inc. +0002BB Continuous Computing +0002BC LVL 7 Systems, Inc. +0002BD Bionet Co., Ltd. +0002BE Totsu Engineering, Inc. +0002BF dotRocket, Inc. +0002C0 Bencent Tzeng Industry Co., Ltd. +0002C1 Innovative Electronic Designs, Inc. +0002C2 Net Vision Telecom +0002C3 Arelnet Ltd. +0002C4 Vector International BUBA +0002C5 Evertz Microsystems Ltd. +0002C6 Data Track Technology PLC +0002C7 ALPS ELECTRIC Co., Ltd. +0002C8 Technocom Communications Technology (pte) Ltd +0002C9 Mellanox Technologies +0002CA EndPoints, Inc. +0002CB TriState Ltd. +0002CC M.C.C.I +0002CD TeleDream, Inc. +0002CE FoxJet, Inc. +0002CF ZyGate Communications, Inc. +0002D0 Comdial Corporation +0002D1 Vivotek, Inc. +0002D2 Workstation AG +0002D3 NetBotz, Inc. +0002D4 PDA Peripherals, Inc. +0002D5 ACR +0002D6 NICE Systems +0002D7 EMPEG Ltd +0002D8 BRECIS Communications Corporation +0002D9 Reliable Controls +0002DA ExiO Communications, Inc. +0002DB NETSEC +0002DC Fujitsu General Limited +0002DD Bromax Communications, Ltd. +0002DE Astrodesign, Inc. +0002DF Net Com Systems, Inc. +0002E0 ETAS GmbH +0002E1 Integrated Network Corporation +0002E2 NDC Infared Engineering +0002E3 LITE-ON Communications, Inc. +0002E4 JC HYUN Systems, Inc. +0002E5 Timeware Ltd. +0002E6 Gould Instrument Systems, Inc. +0002E7 CAB GmbH & Co KG +0002E8 E.D.&A. +0002E9 CS Systemes De Securite - C3S +0002EA Videonics, Inc. +0002EB Pico Communications +0002EC Maschoff Design Engineering +0002ED DXO Telecom Co., Ltd. +0002EE Nokia Danmark A/S +0002EF CCC Network Systems Group Ltd. +0002F0 AME Optimedia Technology Co., Ltd. +0002F1 Pinetron Co., Ltd. +0002F2 eDevice, Inc. +0002F3 Media Serve Co., Ltd. +0002F4 PCTEL, Inc. +0002F5 VIVE Synergies, Inc. +0002F6 Equipe Communications +0002F7 ARM +0002F8 SEAKR Engineering, Inc. +0002F9 Mimos Semiconductor SDN BHD +0002FA DX Antenna Co., Ltd. +0002FB Baumuller Aulugen-Systemtechnik GmbH +0002FC Cisco Systems, Inc. +0002FD Cisco Systems, Inc. +0002FE Viditec, Inc. +0002FF Handan BroadInfoCom +000300 NetContinuum, Inc. +000301 Avantas Networks Corporation +000302 Oasys Telecom, Inc. +000303 JAMA Electronics Co., Ltd. +000304 Pacific Broadband Communications +000305 Smart Network Devices GmbH +000306 Fusion In Tech Co., Ltd. +000307 Secure Works, Inc. +000308 AM Communications, Inc. +000309 Texcel Technology PLC +00030A Argus Technologies +00030B Hunter Technology, Inc. +00030C Telesoft Technologies Ltd. +00030D Uniwill Computer Corp. +00030E Core Communications Co., Ltd. +00030F Digital China (Shanghai) Networks Ltd. +000310 Link Evolution Corp. +000311 Micro Technology Co., Ltd. +000312 TR-Systemtechnik GmbH +000313 Access Media SPA +000314 Teleware Network Systems +000315 Cidco Incorporated +000316 Nobell Communications, Inc. +000317 Merlin Systems, Inc. +000318 Cyras Systems, Inc. +000319 Infineon AG +00031A Beijing Broad Telecom Ltd., China +00031B Cellvision Systems, Inc. +00031C Svenska Hardvarufabriken AB +00031D Taiwan Commate Computer, Inc. +00031E Optranet, Inc. +00031F Condev Ltd. +000320 Xpeed, Inc. +000321 Reco Research Co., Ltd. +000322 IDIS Co., Ltd. +000323 Cornet Technology, Inc. +000324 SANYO Multimedia Tottori Co., Ltd. +000325 Arima Computer Corp. +000326 Iwasaki Information Systems Co., Ltd. +000327 ACT'L +000328 Mace Group, Inc. +000329 F3, Inc. +00032A UniData Communication Systems, Inc. +00032B GAI Datenfunksysteme GmbH +00032C ABB Industrie AG +00032D IBASE Technology, Inc. +00032E Scope Information Management, Ltd. +00032F Global Sun Technology, Inc. +000330 Imagenics, Co., Ltd. +000331 Cisco Systems, Inc. +000332 Cisco Systems, Inc. +000333 Digitel Co., Ltd. +000334 Newport Electronics +000335 Mirae Technology +000336 Zetes Technologies +000337 Vaone, Inc. +000338 Oak Technology +000339 Eurologic Systems, Ltd. +00033A Silicon Wave, Inc. +00033B TAMI Tech Co., Ltd. +00033C Daiden Co., Ltd. +00033D ILSHin Lab +00033E Tateyama System Laboratory Co., Ltd. +00033F BigBand Networks, Ltd. +000340 Floware Wireless Systems, Ltd. +000341 Axon Digital Design +000342 Nortel Networks +000343 Martin Professional A/S +000344 Tietech.Co., Ltd. +000345 Routrek Networks Corporation +000346 Hitachi Kokusai Electric, Inc. +000347 Intel Corporation +000348 Norscan Instruments, Ltd. +000349 Vidicode Datacommunicatie B.V. +00034A RIAS Corporation +00034B Nortel Networks +00034C Shanghai DigiVision Technology Co., Ltd. +00034D Chiaro Networks, Ltd. +00034E Pos Data Company, Ltd. +00034F Sur-Gard Security +000350 BTICINO SPA +000351 Diebold, Inc. +000352 Colubris Networks +000353 Mitac, Inc. +000354 Fiber Logic Communications +000355 TeraBeam Internet Systems +000356 Wincor Nixdorf GmbH & Co KG +000357 Intervoice-Brite, Inc. +000358 iCable System Co., Ltd. +000359 DigitalSis +00035A Photron Limited +00035B BridgeWave Communications +00035C Saint Song Corp. +00035D Bosung Hi-Net Co., Ltd. +00035E Metropolitan Area Networks, Inc. +00035F Prueftechnik Condition Monitoring GmbH & Co. KG +000360 PAC Interactive Technology, Inc. +000361 Widcomm, Inc. +000362 Vodtel Communications, Inc. +000363 Miraesys Co., Ltd. +000364 Scenix Semiconductor, Inc. +000365 Kira Information & Communications, Ltd. +000366 ASM Pacific Technology +000367 Jasmine Networks, Inc. +000368 Embedone Co., Ltd. +000369 Nippon Antenna Co., Ltd. +00036A Mainnet, Ltd. +00036B Cisco Systems, Inc. +00036C Cisco Systems, Inc. +00036D Runtop, Inc. +00036E Nicon Systems (Pty) Limited +00036F Telsey SPA +000370 NXTV, Inc. +000371 Acomz Networks Corp. +000372 ULAN +000373 Aselsan A.S +000374 Hunter Watertech +000375 NetMedia, Inc. +000376 Graphtec Technology, Inc. +000377 Gigabit Wireless +000378 HUMAX Co., Ltd. +000379 Proscend Communications, Inc. +00037A Taiyo Yuden Co., Ltd. +00037B IDEC IZUMI Corporation +00037C Coax Media +00037D Stellcom +00037E PORTech Communications, Inc. +00037F Atheros Communications, Inc. +000380 SSH Communications Security Corp. +000381 Ingenico International +000382 A-One Co., Ltd. +000383 Metera Networks, Inc. +000384 AETA +000385 Actelis Networks, Inc. +000386 Ho Net, Inc. +000387 Blaze Network Products +000388 Fastfame Technology Co., Ltd. +000389 Plantronics +00038A America Online, Inc. +00038B PLUS-ONE I&T, Inc. +00038C Total Impact +00038D PCS Revenue Control Systems, Inc. +00038E Atoga Systems, Inc. +00038F Weinschel Corporation +000390 Digital Video Communications, Inc. +000392 Hyundai Teletek Co., Ltd. +000393 Apple Computer, Inc. +000394 Connect One +000395 California Amplifier +000396 EZ Cast Co., Ltd. +000397 Watchfront Electronics +000398 WISI +000399 Dongju Informations & Communications Co., Ltd. +00039A nSine, Ltd. +00039B NetChip Technology, Inc. +00039C OptiMight Communications, Inc. +00039D BENQ CORPORATION +00039E Tera System Co., Ltd. +00039F Cisco Systems, Inc. +0003A0 Cisco Systems, Inc. +0003A1 HIPER Information & Communication, Inc. +0003A2 Catapult Communications +0003A3 MAVIX, Ltd. +0003A4 Data Storage and Information Management +0003A5 Medea Corporation +0003A7 Unixtar Technology, Inc. +0003A8 IDOT Computers, Inc. +0003A9 AXCENT Media AG +0003AA Watlow +0003AB Bridge Information Systems +0003AC Fronius Schweissmaschinen +0003AD Emerson Energy Systems AB +0003AE Allied Advanced Manufacturing Pte, Ltd. +0003AF Paragea Communications +0003B0 Xsense Technology Corp. +0003B1 Abbott Laboratories HPD +0003B2 Radware +0003B3 IA Link Systems Co., Ltd. +0003B4 Macrotek International Corp. +0003B5 Entra Technology Co. +0003B6 QSI Corporation +0003B7 ZACCESS Systems +0003B8 NetKit Solutions, LLC +0003B9 Hualong Telecom Co., Ltd. +0003BA Sun Microsystems +0003BB Signal Communications Limited +0003BC COT GmbH +0003BD OmniCluster Technologies, Inc. +0003BE Netility +0003BF Centerpoint Broadband Technologies, Inc. +0003C0 RFTNC Co., Ltd. +0003C1 Packet Dynamics Ltd +0003C2 Solphone K.K. +0003C3 Micronik Multimedia +0003C4 Tomra Systems ASA +0003C5 Mobotix AG +0003C6 ICUE Systems, Inc. +0003C7 hopf Elektronik GmbH +0003C8 CML Emergency Services +0003C9 TECOM Co., Ltd. +0003CA MTS Systems Corp. +0003CB Nippon Systems Development Co., Ltd. +0003CC Momentum Computer, Inc. +0003CD Clovertech, Inc. +0003CE ETEN Technologies, Inc. +0003CF Muxcom, Inc. +0003D0 KOANKEISO Co., Ltd. +0003D1 Takaya Corporation +0003D2 Crossbeam Systems, Inc. +0003D3 Internet Energy Systems, Inc. +0003D4 Alloptic, Inc. +0003D5 Advanced Communications Co., Ltd. +0003D6 RADVision, Ltd. +0003D7 NextNet Wireless, Inc. +0003D8 iMPath Networks, Inc. +0003D9 Secheron SA +0003DA Takamisawa Cybernetics Co., Ltd. +0003DB Apogee Electronics Corp. +0003DC Lexar Media, Inc. +0003DD Comark Corp. +0003DE OTC Wireless +0003DF Desana Systems +0003E0 RadioFrame Networks, Inc. +0003E1 Winmate Communication, Inc. +0003E2 Comspace Corporation +0003E3 Cisco Systems, Inc. +0003E4 Cisco Systems, Inc. +0003E5 Hermstedt SG +0003E6 Entone Technologies, Inc. +0003E7 Logostek Co. Ltd. +0003E8 Wavelength Digital Limited +0003E9 Akara Canada, Inc. +0003EA Mega System Technologies, Inc. +0003EB Atrica +0003EC ICG Research, Inc. +0003ED Shinkawa Electric Co., Ltd. +0003EE MKNet Corporation +0003EF Oneline AG +0003F0 Redfern Broadband Networks +0003F1 Cicada Semiconductor, Inc. +0003F2 Seneca Networks +0003F3 Dazzle Multimedia, Inc. +0003F4 NetBurner +0003F5 Chip2Chip +0003F6 Allegro Networks, Inc. +0003F7 Plast-Control GmbH +0003F8 SanCastle Technologies, Inc. +0003F9 Pleiades Communications, Inc. +0003FA TiMetra Networks +0003FB Toko Seiki Company, Ltd. +0003FC Intertex Data AB +0003FD Cisco Systems, Inc. +0003FE Cisco Systems, Inc. +0003FF Connectix +000400 LEXMARK INTERNATIONAL, INC. +000401 Osaki Electric Co., Ltd. +000402 Nexsan Technologies, Ltd. +000403 Nexsi Corporation +000404 Makino Milling Machine Co., Ltd. +000405 ACN Technologies +000406 Fa. Metabox AG +000407 Topcon Positioning Systems, Inc. +000408 Sanko Electronics Co., Ltd. +000409 Cratos Networks +00040A Sage Systems +00040B 3com Europe Ltd. +00040C KANNO Work's Ltd. +00040D Avaya, Inc. +00040E AVM GmbH +00040F Asus Network Technologies, Inc. +000410 Spinnaker Networks, Inc. +000411 Inkra Networks, Inc. +000412 WaveSmith Networks, Inc. +000413 SNOM Technology AG +000414 Umezawa Musen Denki Co., Ltd. +000415 Rasteme Systems Co., Ltd. +000416 Parks S/A Comunicacoes Digitais +000417 ELAU AG +000418 Teltronic S.A.U. +000419 Fibercycle Networks, Inc. +00041A ines GmbH +00041B Digital Interfaces Ltd. +00041C ipDialog, Inc. +00041D Corega of America +00041E Shikoku Instrumentation Co., Ltd. +00041F Sony Computer Entertainment, Inc. +000420 Slim Devices, Inc. +000421 Ocular Networks +000422 Gordon Kapes, Inc. +000423 Intel Corporation +000424 TMC s.r.l. +000425 Atmel Corporation +000426 Autosys +000427 Cisco Systems, Inc. +000428 Cisco Systems, Inc. +000429 Pixord Corporation +00042A Wireless Networks, Inc. +00042B IT Access Co., Ltd. +00042C Minet, Inc. +00042D Sarian Systems, Ltd. +00042E Netous Technologies, Ltd. +00042F International Communications Products, Inc. +000430 Netgem +000431 GlobalStreams, Inc. +000432 Voyetra Turtle Beach, Inc. +000433 Cyberboard A/S +000434 Accelent Systems, Inc. +000435 Comptek International, Inc. +000436 ELANsat Technologies, Inc. +000437 Powin Information Technology, Inc. +000438 Nortel Networks +000439 Rosco Entertainment Technology, Inc. +00043A Intelligent Telecommunications, Inc. +00043B Lava Computer Mfg., Inc. +00043C SONOS Co., Ltd. +00043D INDEL AG +00043E Telencomm +00043F Electronic Systems Technology, Inc. +000440 cyberPIXIE, Inc. +000441 Half Dome Systems, Inc. +000442 NACT +000443 Agilent Technologies, Inc. +000444 Western Multiplex Corporation +000445 LMS Skalar Instruments GmbH +000446 CYZENTECH Co., Ltd. +000447 Acrowave Systems Co., Ltd. +000448 Polaroid Professional Imaging +000449 Mapletree Networks +00044A iPolicy Networks, Inc. +00044B NVIDIA +00044C JENOPTIK +00044D Cisco Systems, Inc. +00044E Cisco Systems, Inc. +00044F Leukhardt Systemelektronik GmbH +000450 DMD Computers SRL +000451 Medrad, Inc. +000452 RocketLogix, Inc. +000453 YottaYotta, Inc. +000454 Quadriga UK +000455 ANTARA.net +000456 PipingHot Networks +000457 Universal Access Technology, Inc. +000458 Fusion X Co., Ltd. +000459 Veristar Corporation +00045A The Linksys Group, Inc. +00045B Techsan Electronics Co., Ltd. +00045C Mobiwave Pte Ltd +00045D BEKA Elektronik +00045E PolyTrax Information Technology AG +00045F Evalue Technology, Inc. +000460 Knilink Technology, Inc. +000461 EPOX Computer Co., Ltd. +000462 DAKOS Data & Communication Co., Ltd. +000463 Bosch Security Systems +000464 Fantasma Networks, Inc. +000465 i.s.t isdn-support technik GmbH +000466 ARMITEL Co. +000467 Wuhan Research Institute of MII +000468 Vivity, Inc. +000469 Innocom, Inc. +00046A Navini Networks +00046B Palm Wireless, Inc. +00046C Cyber Technology Co., Ltd. +00046D Cisco Systems, Inc. +00046E Cisco Systems, Inc. +00046F Digitel S/A Industria Eletronica +000470 ipUnplugged AB +000471 IPrad +000472 Telelynx, Inc. +000473 Photonex Corporation +000474 LEGRAND +000475 3 Com Corporation +000476 3 Com Corporation +000477 Scalant Systems, Inc. +000478 G. Star Technology Corporation +000479 Radius Co., Ltd. +00047A AXXESSIT ASA +00047B Schlumberger +00047C Skidata AG +00047D Pelco +00047E NKF Electronics +00047F Chr. Mayr GmbH & Co. KG +000480 Foundry Networks, Inc. +000481 Econolite Control Products, Inc. +000482 Medialogic Corp. +000483 Deltron Technology, Inc. +000484 Amann GmbH +000485 PicoLight +000486 ITTC, University of Kansas +000487 Cogency Semiconductor, Inc. +000488 Eurotherm Action Incorporated. +000489 YAFO Networks, Inc. +00048A Temia Vertriebs GmbH +00048B Poscon Corporation +00048C Nayna Networks, Inc. +00048D Tone Commander Systems, Inc. +00048E Ohm Tech Labs, Inc. +00048F TD Systems Corp. +000490 Optical Access +000491 Technovision, Inc. +000492 Hive Internet, Ltd. +000493 Tsinghua Unisplendour Co., Ltd. +000494 Breezecom, Ltd. +000495 Tejas Networks +000496 Extreme Networks +000497 MacroSystem Digital Video AG +000499 Chino Corporation +00049A Cisco Systems, Inc. +00049B Cisco Systems, Inc. +00049C Surgient Networks, Inc. +00049D Ipanema Technologies +00049E Wirelink Co., Ltd. +00049F Metrowerks +0004A0 Verity Instruments, Inc. +0004A1 Pathway Connectivity +0004A2 L.S.I. Japan Co., Ltd. +0004A3 Microchip Technology, Inc. +0004A4 NetEnabled, Inc. +0004A5 Barco Projection Systems NV +0004A6 SAF Tehnika Ltd. +0004A7 FabiaTech Corporation +0004A8 Broadmax Technologies, Inc. +0004A9 SandStream Technologies, Inc. +0004AA Jetstream Communications +0004AB Comverse Network Systems, Inc. +0004AC IBM CORP. +0004AD Malibu Networks +0004AE Liquid Metronics +0004AF Digital Fountain, Inc. +0004B0 ELESIGN Co., Ltd. +0004B1 Signal Technology, Inc. +0004B2 ESSEGI SRL +0004B3 Videotek, Inc. +0004B4 CIAC +0004B5 Equitrac Corporation +0004B6 Stratex Networks, Inc. +0004B7 AMB i.t. Holding +0004B8 Kumahira Co., Ltd. +0004B9 S.I. Soubou, Inc. +0004BA KDD Media Will Corporation +0004BB Bardac Corporation +0004BC Giantec, Inc. +0004BD Motorola BCS +0004BE OptXCon, Inc. +0004BF VersaLogic Corp. +0004C0 Cisco Systems, Inc. +0004C1 Cisco Systems, Inc. +0004C2 Magnipix, Inc. +0004C3 CASTOR Informatique +0004C4 Allen & Heath Limited +0004C5 ASE Technologies, USA +0004C6 Yamaha Motor Co., Ltd. +0004C7 NetMount +0004C8 LIBA Maschinenfabrik GmbH +0004C9 Micro Electron Co., Ltd. +0004CA FreeMs Corp. +0004CB Tdsoft Communication, Ltd. +0004CC Peek Traffic B.V. +0004CD Informedia Research Group +0004CE Patria Ailon +0004CF Seagate Technology +0004D0 Softlink s.r.o. +0004D1 Drew Technologies, Inc. +0004D2 Adcon Telemetry AG +0004D3 Toyokeiki Co., Ltd. +0004D4 Proview Electronics Co., Ltd. +0004D5 Hitachi Communication Systems, Inc. +0004D6 Takagi Industrial Co., Ltd. +0004D7 Omitec Instrumentation Ltd. +0004D8 IPWireless, Inc. +0004D9 Titan Electronics, Inc. +0004DA Relax Technology, Inc. +0004DB Tellus Group Corp. +0004DC Nortel Networks +0004DD Cisco Systems, Inc. +0004DE Cisco Systems, Inc. +0004DF Teracom Telematica Ltda. +0004E0 Procket Networks +0004E1 Infinior Microsystems +0004E2 SMC Networks, Inc. +0004E3 Accton Technology Corp. +0004E4 Daeryung Ind., Inc. +0004E5 Glonet Systems, Inc. +0004E6 Banyan Network Private Limited +0004E7 Lightpointe Communications, Inc +0004E8 IER, Inc. +0004E9 Infiniswitch Corporation +0004EA Hewlett-Packard Company +0004EB Paxonet Communications, Inc. +0004EC Memobox SA +0004ED Billion Electric Co., Ltd. +0004EE Lincoln Electric Company +0004EF Polestar Corp. +0004F0 International Computers, Ltd +0004F1 WhereNet +0004F2 Circa Communications, Ltd. +0004F3 FS FORTH-SYSTEME GmbH +0004F4 Infinite Electronics Inc. +0004F5 SnowShore Networks, Inc. +0004F6 Amphus +0004F7 Omega Band, Inc. +0004F8 QUALICABLE TV Industria E Com., Ltda +0004F9 Xtera Communications, Inc. +0004FA MIST Inc. +0004FB Commtech, Inc. +0004FC Stratus Computer (DE), Inc. +0004FD Japan Control Engineering Co., Ltd. +0004FE Pelago Networks +0004FF Acronet Co., Ltd. +000500 Cisco Systems, Inc. +000501 Cisco Systems, Inc. +000502 APPLE COMPUTER +000503 ICONAG +000504 Naray Information & Communication Enterprise +000505 Systems Integration Solutions, Inc. +000506 Reddo Networks AB +000507 Fine Appliance Corp. +000508 Inetcam, Inc. +000509 AVOC Nishimura Ltd. +00050A ICS Spa +00050B SICOM Systems, Inc. +00050C Network Photonics, Inc. +00050D Midstream Technologies, Inc. +00050E 3ware, Inc. +00050F Tanaka S/S Ltd. +000510 Infinite Shanghai Communication Terminals Ltd. +000511 Complementary Technologies Ltd +000512 MeshNetworks, Inc. +000513 VTLinx Multimedia Systems, Inc. +000514 KDT Systems Co., Ltd. +000515 Nuark Co., Ltd. +000516 SMART Modular Technologies +000517 Shellcomm, Inc. +000518 Jupiters Technology +000519 Siemens Building Technologies AG, +00051A 3Com Europe Ltd. +00051B Magic Control Technology Corporation +00051C Xnet Technology Corp. +00051D Airocon, Inc. +00051E Brocade Communications Systems, Inc. +00051F Taijin Media Co., Ltd. +000520 Smartronix, Inc. +000521 Control Microsystems +000522 LEA*D Corporation, Inc. +000523 AVL List GmbH +000524 BTL System (HK) Limited +000525 Puretek Industrial Co., Ltd. +000526 IPAS GmbH +000527 SJ Tek Co. Ltd +000528 New Focus, Inc. +000529 Shanghai Broadan Communication Technology Co., Ltd +00052A Ikegami Tsushinki Co., Ltd. +00052B HORIBA, Ltd. +00052C Supreme Magic Corporation +00052D Zoltrix International Limited +00052E Cinta Networks +00052F Leviton Voice and Data +000530 Andiamo Systems, Inc. +000531 Cisco Systems, Inc. +000532 Cisco Systems, Inc. +000533 Sanera Systems, Inc. +000534 Northstar Engineering Ltd. +000535 Chip PC Ltd. +000536 Danam Communications, Inc. +000537 Nets Technology Co., Ltd. +000538 Merilus, Inc. +000539 A Brand New World in Sweden AB +00053A Willowglen Services Pte Ltd +00053B Harbour Networks Ltd., Co. Beijing +00053C Xircom +00053D Agere Systems +00053E KID Systeme GmbH +00053F VisionTek, Inc. +000540 FAST Corporation +000541 Advanced Systems Co., Ltd. +000542 Otari, Inc. +000543 IQ Wireless GmbH +000544 Valley Technologies, Inc. +000545 Internet Photonics +000546 K-Solutions Inc. +000547 Starent Networks +000548 Disco Corporation +000549 Salira Optical Network Systems +00054A Ario Data Networks, Inc. +00054B Micro Innovation AG +00054C RF Innovations Pty Ltd +00054D Brans Technologies, Inc. +00054E Philips Components +000550 Digi-Tech Communications Limited +000551 F & S Elektronik Systeme GmbH +000552 Xycotec Computer GmbH +000553 DVC Company, Inc. +000554 Rangestar Wireless +000555 Japan Cash Machine Co., Ltd. +000556 360 Systems +000557 Agile TV Corporation +000558 Synchronous, Inc. +000559 Intracom S.A. +00055A Power Dsine Ltd. +00055B Charles Industries, Ltd. +00055C Kowa Company, Ltd. +00055D D-Link Systems, Inc. +00055E Cisco Systems, Inc. +00055F Cisco Systems, Inc. +000560 LEADER COMM.CO., LTD +000561 nac Image Technology, Inc. +000562 Digital View Limited +000563 J-Works, Inc. +000564 Tsinghua Bitway Co., Ltd. +000565 Tailyn Communication Company Ltd. +000566 Secui.com Corporation +000567 Etymonic Design, Inc. +000568 Piltofish Networks AB +000569 VMWARE, Inc. +00056A Heuft Systemtechnik GmbH +00056B C.P. Technology Co., Ltd. +00056C Hung Chang Co., Ltd. +00056D Pacific Corporation +00056E National Enhance Technology, Inc. +00056F Innomedia Technologies Pvt. Ltd. +000570 Baydel Ltd. +000571 Seiwa Electronics Co. +000572 Deonet Co., Ltd. +000573 Cisco Systems, Inc. +000574 Cisco Systems, Inc. +000575 CDS-Electronics BV +000576 NSM Technology Ltd. +000577 SM Information & Communication +000579 Universal Control Solution Corp. +00057A Hatteras Networks +00057B Chung Nam Electronic Co., Ltd. +00057C RCO Security AB +00057D Sun Communications, Inc. +00057E Eckelmann Steuerungstechnik GmbH +00057F Acqis Technology +000580 Fibrolan Ltd. +000581 Snell & Wilcox Ltd. +000582 ClearCube Technology +000583 ImageCom Limited +000584 AbsoluteValue Systems, Inc. +000585 Juniper Networks, Inc. +000586 Lucent Technologies +000587 Locus, Incorporated +000588 Sensoria Corp. +000589 National Datacomputer +00058A Netcom Co., Ltd. +00058B IPmental, Inc. +00058C Opentech Inc. +00058D Lynx Photonic Networks, Inc. +00058E Flextronics International GmbH & Co. Nfg. KG +00058F CLCsoft co. +000590 Swissvoice Ltd. +000591 Active Silicon Ltd. +000592 Pultek Corp. +000593 Grammar Engine Inc. +000594 IXXAT Automation GmbH +000595 Alesis Corporation +000596 Genotech Co., Ltd. +000597 Eagle Traffic Control Systems +000598 CRONOS S.r.l. +000599 DRS Test and Energy Management or DRS-TEM +00059A Cisco Systems, Inc. +00059B Cisco Systems, Inc. +00059C Kleinknecht GmbH, Ing. Buero +00059D Daniel Computing Systems, Inc. +00059E Zinwell Corporation +00059F Yotta Networks, Inc. +0005A0 MOBILINE Kft. +0005A1 Zenocom +0005A2 CELOX Networks +0005A3 QEI, Inc. +0005A4 Lucid Voice Ltd. +0005A5 KOTT +0005A6 Extron Electronics +0005A7 Hyperchip, Inc. +0005A8 WYLE ELECTRONICS +0005A9 Princeton Networks, Inc. +0005AA Moore Industries International Inc. +0005AB Cyber Fone, Inc. +0005AC Northern Digital, Inc. +0005AD Topspin Communications, Inc. +0005AE Mediaport USA +0005AF InnoScan Computing A/S +0005B0 Korea Computer Technology Co., Ltd. +0005B1 ASB Technology BV +0005B2 Medison Co., Ltd. +0005B3 Asahi-Engineering Co., Ltd. +0005B4 Aceex Corporation +0005B5 Broadcom Technologies +0005B6 INSYS Microelectronics GmbH +0005B7 Arbor Technology Corp. +0005B8 Electronic Design Associates, Inc. +0005B9 Airvana, Inc. +0005BA Area Netwoeks, Inc. +0005BB Myspace AB +0005BC Resorsys Ltd. +0005BD ROAX BV +0005BE Kongsberg Seatex AS +0005BF JustEzy Technology, Inc. +0005C0 Digital Network Alacarte Co., Ltd. +0005C1 A-Kyung Motion, Inc. +0005C2 Soronti, Inc. +0005C3 Pacific Instruments, Inc. +0005C4 Telect, Inc. +0005C5 Flaga HF +0005C6 Triz Communications +0005C7 I/F-COM A/S +0005C8 VERYTECH +0005C9 LG Innotek +0005CA Hitron Technology, Inc. +0005CB ROIS Technologies, Inc. +0005CC Sumtel Communications, Inc. +0005CD Denon, Ltd. +0005CE Prolink Microsystems Corporation +0005CF Thunder River Technologies, Inc. +0005D0 Solinet Systems +0005D1 Metavector Technologies +0005D2 DAP Technologies +0005D3 eProduction Solutions, Inc. +0005D4 FutureSmart Networks, Inc. +0005D5 Speedcom Wireless +0005D6 Titan Wireless +0005D7 Vista Imaging, Inc. +0005D8 Arescom, Inc. +0005D9 Techno Valley, Inc. +0005DA Apex Automationstechnik +0005DB Nentec GmbH +0005DC Cisco Systems, Inc. +0005DD Cisco Systems, Inc. +0005DE Gi Fone Korea, Inc. +0005DF Electronic Innovation, Inc. +0005E0 Empirix Corp. +0005E1 Trellis Photonics, Ltd. +0005E2 Creativ Network Technologies +0005E3 LightSand Communications, Inc. +0005E4 Red Lion Controls L.P. +0005E5 Renishaw PLC +0005E6 Egenera, Inc. +0005E7 Netrake Corp. +0005E8 TurboWave, Inc. +0005E9 Unicess Network, Inc. +0005EA Rednix +0005EB Blue Ridge Networks, Inc. +0005EC Mosaic Systems Inc. +0005ED Technikum Joanneum GmbH +0005EE BEWATOR Group +0005EF ADOIR Digital Technology +0005F0 SATEC +0005F1 Vrcom, Inc. +0005F2 Power R, Inc. +0005F3 Weboyn +0005F4 System Base Co., Ltd. +0005F5 OYO Geospace Corp. +0005F6 Young Chang Co. Ltd. +0005F7 Analog Devices, Inc. +0005F8 Real Time Access, Inc. +0005F9 TOA Corporation +0005FA IPOptical, Inc. +0005FB ShareGate, Inc. +0005FC Schenck Pegasus Corp. +0005FD PacketLight Networks Ltd. +0005FE Traficon N.V. +0005FF SNS Solutions, Inc. +000600 Tokyo Electronic Industry Co., Ltd. +000601 Otanikeiki Co., Ltd. +000602 Cirkitech Electronics Co. +000603 Baker Hughes Inc. +000604 @Track Communications, Inc. +000605 Inncom International, Inc. +000606 RapidWAN, Inc. +000607 Omni Directional Control Technology Inc. +000608 At-Sky SAS +000609 Crossport Systems +00060A Blue2space +00060B Paceline Systems Corporation +00060C Melco Industries, Inc. +00060D Wave7 Optics +00060E IGYS Systems, Inc. +00060F Narad Networks Inc +000610 Abeona Networks Inc +000611 Zeus Wireless, Inc. +000612 Accusys, Inc. +000613 Kawasaki Microelectronics Incorporated +000614 Prism Holdings +000615 Kimoto Electric Co., Ltd. +000616 Tel Net Co., Ltd. +000617 Redswitch Inc. +000618 DigiPower Manufacturing Inc. +000619 Connection Technology Systems +00061A Zetari Inc. +00061B Portable Systems, IBM Japan Co, Ltd +00061C Hoshino Metal Industries, Ltd. +00061D MIP Telecom, Inc. +00061E Maxan Systems +00061F Vision Components GmbH +000620 Serial System Ltd. +000621 Hinox, Co., Ltd. +000622 Chung Fu Chen Yeh Enterprise Corp. +000623 MGE UPS Systems France +000624 Gentner Communications Corp. +000625 The Linksys Group, Inc. +000626 MWE GmbH +000627 Uniwide Technologies, Inc. +000628 Cisco Systems, Inc. +000629 IBM CORPORATION +00062A Cisco Systems, Inc. +00062B INTRASERVER TECHNOLOGY +00062C Network Robots, Inc. +00062D TouchStar Technologies, L.L.C. +00062E Aristos Logic Corp. +00062F Pivotech Systems Inc. +000630 Adtranz Sweden +000631 Optical Solutions, Inc. +000632 Mesco Engineering GmbH +000633 Heimann Biometric Systems GmbH +000634 GTE Airfone Inc. +000635 PacketAir Networks, Inc. +000636 Jedai Broadband Networks +000637 Toptrend-Meta Information (ShenZhen) Inc. +000638 Sungjin C&C Co., Ltd. +000639 Newtec +00063A Dura Micro, Inc. +00063B Arcturus Networks, Inc. +00063C NMI Electronics Ltd +00063D Microwave Data Systems Inc. +00063E Opthos Inc. +00063F Everex Communications Inc. +000640 White Rock Networks +000641 ITCN +000642 Genetel Systems Inc. +000643 SONO Computer Co., Ltd. +000644 NEIX Inc. +000645 Meisei Electric Co. Ltd. +000646 ShenZhen XunBao Network Technology Co Ltd +000647 Etrali S.A. +000648 Seedsware, Inc. +000649 Quante +00064A Honeywell Co., Ltd. (KOREA) +00064B Alexon Co., Ltd. +00064C Invicta Networks, Inc. +00064D Sencore +00064E Broad Net Technology Inc. +00064F PRO-NETS Technology Corporation +000650 Tiburon Networks, Inc. +000651 Aspen Networks Inc. +000652 Cisco Systems, Inc. +000653 Cisco Systems, Inc. +000654 Maxxio Technologies +000655 Yipee, Inc. +000656 Tactel AB +000657 Market Central, Inc. +000658 Helmut Fischer GmbH & Co. KG +000659 EAL (Apeldoorn) B.V. +00065A Strix Systems +00065B Dell Computer Corp. +00065C Malachite Technologies, Inc. +00065D Heidelberg Web Systems +00065E Photuris, Inc. +00065F ECI Telecom - NGTS Ltd. +000660 NADEX Co., Ltd. +000661 NIA Home Technologies Corp. +000662 MBM Technology Ltd. +000663 Human Technology Co., Ltd. +000664 Fostex Corporation +000665 Sunny Giken, Inc. +000666 Roving Networks +000667 Tripp Lite +000668 Vicon Industries Inc. +000669 Datasound Laboratories Ltd +00066A InfiniCon Systems, Inc. +00066B Sysmex Corporation +00066C Robinson Corporation +00066D Compuprint S.P.A. +00066E Delta Electronics, Inc. +00066F Korea Data Systems +000670 Upponetti Oy +000671 Softing AG +000672 Netezza +000673 Optelecom, Inc. +000674 Spectrum Control, Inc. +000675 Banderacom, Inc. +000676 Novra Technologies Inc. +000677 SICK AG +000678 Marantz Japan, Inc. +000679 Konami Corporation +00067A JMP Systems +00067B Toplink C&C Corporation +00067C CISCO SYSTEMS, INC. +00067D Takasago Ltd. +00067E WinCom Systems, Inc. +00067F Rearden Steel Technologies +000680 Card Access, Inc. +000681 Goepel Electronic GmbH +000682 Convedia +000683 Bravara Communications, Inc. +000684 Biacore AB +000685 NetNearU Corporation +000686 ZARDCOM Co., Ltd. +000687 Omnitron Systems Technology, Inc. +000688 Telways Communication Co., Ltd. +000689 yLez Technologies Pte Ltd +00068A NeuronNet Co. Ltd. R&D Center +00068B AirRunner Technologies, Inc. +00068C 3Com Corporation +00068D SANgate Systems +00068E HID Corporation +00068F Telemonitor, Inc. +000690 Euracom Communication GmbH +000691 PT Inovacao +000692 Intruvert Networks, Inc. +000693 Flexus Computer Technology, Inc. +000694 Mobillian Corporation +000695 Ensure Technologies, Inc. +000696 Advent Networks +000697 R & D Center +000698 egnite Software GmbH +000699 Vida Design Co. +00069A e & Tel +00069B AVT Audio Video Technologies GmbH +00069C Transmode Systems AB +00069D Petards Mobile Intelligence +00069E UNIQA, Inc. +00069F Kuokoa Networks +0006A0 Mx Imaging +0006A1 Celsian Technologies, Inc. +0006A2 Microtune, Inc. +0006A3 Bitran Corporation +0006A4 INNOWELL Corp. +0006A5 PINON Corp. +0006A6 Artistic Licence (UK) Ltd +0006A7 Primarion +0006A8 KC Technology, Inc. +0006A9 Universal Instruments Corp. +0006AA Miltope Corporation +0006AB W-Link Systems, Inc. +0006AC Intersoft Co. +0006AD KB Electronics Ltd. +0006AE Himachal Futuristic Communications Ltd +0006B0 Comtech EF Data Corp. +0006B1 Sonicwall +0006B2 Linxtek Co. +0006B3 Diagraph Corporation +0006B4 Vorne Industries, Inc. +0006B5 Luminent, Inc. +0006B6 Nir-Or Israel Ltd. +0006B7 TELEM GmbH +0006B8 Bandspeed Pty Ltd +0006B9 A5TEK Corp. +0006BA Westwave Communications +0006BB ATI Technologies Inc. +0006BC Macrolink, Inc. +0006BD BNTECHNOLOGY Co., Ltd. +0006BE Baumer Optronic GmbH +0006BF Accella Technologies Co., Ltd. +0006C0 United Internetworks, Inc. +0006C1 CISCO SYSTEMS, INC. +0006C2 Smartmatic Corporation +0006C3 Schindler Elevators Ltd. +0006C4 Piolink Inc. +0006C5 INNOVI Technologies Limited +0006C6 lesswire AG +0006C7 RFNET Technologies Pte Ltd (S) +0006C8 Sumitomo Metal Micro Devices, Inc. +0006C9 Technical Marketing Research, Inc. +0006CA American Computer & Digital Components, Inc. (ACDC) +0006CB Jotron Electronics A/S +0006CC JMI Electronics Co., Ltd. +0006CD CreoScitex Corporation Ltd. +0006CE DATENO +0006CF Thales Avionics In-Flight Systems, LLC +0006D0 Elgar Electronics Corp. +0006D1 Tahoe Networks, Inc. +0006D2 Tundra Semiconductor Corp. +0006D3 Alpha Telecom, Inc. U.S.A. +0006D4 Interactive Objects, Inc. +0006D5 Diamond Systems Corp. +0006D6 Cisco Systems, Inc. +0006D7 Cisco Systems, Inc. +0006D8 Maple Optical Systems +0006D9 IPM-Net S.p.A. +0006DA ITRAN Communications Ltd. +0006DB ICHIPS Co., Ltd. +0006DC Syabas Technology (Amquest) +0006DD AT & T Laboratories - Cambridge Ltd +0006DE Flash Technology +0006DF AIDONIC Corporation +0006E0 MAT Co., Ltd. +0006E1 Techno Trade s.a +0006E2 Ceemax Technology Co., Ltd. +0006E3 Quantitative Imaging Corporation +0006E4 Citel Technologies Ltd. +0006E5 Fujian Newland Computer Ltd. Co. +0006E6 DongYang Telecom Co., Ltd. +0006E7 Bit Blitz Communications Inc. +0006E8 Optical Network Testing, Inc. +0006E9 Intime Corp. +0006EA ELZET80 Mikrocomputer GmbH&Co. KG +0006EB Global Data +0006EC M/A COM Private Radio System Inc. +0006ED Inara Networks +0006EE Shenyang Neu-era Information & Technology Stock Co., Ltd +0006EF Maxxan Systems, Inc. +0006F0 Digeo, Inc. +0006F1 Optillion +0006F2 Platys Communications +0006F3 AcceLight Networks +0006F4 Prime Electronics & Satellitics Inc. +0006F9 Mitsui Zosen Systems Research Inc. +0006FA IP SQUARE Co, Ltd. +0006FB Hitachi Printing Solutions, Ltd. +0006FC Fnet Co., Ltd. +0006FD Comjet Information Systems Corp. +0006FE Celion Networks, Inc. +0006FF Sheba Systems Co., Ltd. +000700 Zettamedia Korea +000701 RACAL-DATACOM +000702 Varian Medical Systems +000703 CSEE Transport +000705 Endress & Hauser GmbH & Co +000706 Sanritz Corporation +000707 Interalia Inc. +000708 Bitrage Inc. +000709 Westerstrand Urfabrik AB +00070A Unicom Automation Co., Ltd. +00070B Octal, SA +00070C SVA-Intrusion.com Co. Ltd. +00070D Cisco Systems Inc. +00070E Cisco Systems Inc. +00070F Fujant, Inc. +000710 Adax, Inc. +000711 Acterna +000712 JAL Information Technology +000713 IP One, Inc. +000714 Brightcom +000715 General Research of Electronics, Inc. +000716 J & S Marine Ltd. +000717 Wieland Electric GmbH +000718 iCanTek Co., Ltd. +000719 Mobiis Co., Ltd. +00071A Finedigital Inc. +00071B Position Technology Inc. +00071C AT&T Fixed Wireless Services +00071D Satelsa Sistemas Y Aplicaciones De Telecomunicaciones, S.A. +00071E Tri-M Engineering / Nupak Dev. Corp. +00071F European Systems Integration +000720 Trutzschler GmbH & Co. KG +000721 Formac Elektronik GmbH +000722 Nielsen Media Research +000723 ELCON Systemtechnik GmbH +000724 Telemax Co., Ltd. +000725 Bematech International Corp. +000727 Zi Corporation (HK) Ltd. +000728 Neo Telecom +000729 Kistler Instrumente AG +00072A Innovance Networks +00072B Jung Myung Telecom Co., Ltd. +00072C Fabricom +00072D CNSystems +00072E North Node AB +00072F Instransa, Inc. +000730 Hutchison OPTEL Telecom Technology Co., Ltd. +000731 Spiricon, Inc. +000732 AAEON Technology Inc. +000733 DANCONTROL Engineering +000734 ONStor, Inc. +000735 Flarion Technologies, Inc. +000736 Data Video Technologies Co., Ltd. +000737 Soriya Co. Ltd. +000738 Young Technology Co., Ltd. +000739 Motion Media Technology Ltd. +00073A Inventel Systemes +00073B Tenovis GmbH & Co KG +00073C Telecom Design +00073D Nanjing Postel Telecommunications Co., Ltd. +00073E China Great-Wall Computer Shenzhen Co., Ltd. +00073F Woojyun Systec Co., Ltd. +000740 Melco Inc. +000741 Sierra Automated Systems +000742 Current Technologies +000743 Chelsio Communications +000744 Unico, Inc. +000745 Radlan Computer Communications Ltd. +000746 Interlink BT, LLC +000747 Mecalc +000748 The Imaging Source Europe +000749 CENiX Inc. +00074A Carl Valentin GmbH +00074B Daihen Corporation +00074C Beicom Inc. +00074D Zebra Technologies Corp. +00074E Naughty boy co., Ltd. +00074F Cisco Systems, Inc. +000750 Cisco Systems, Inc. +000751 m.u.t. - GmbH +000752 Rhythm Watch Co., Ltd. +000753 Beijing Qxcomm Technology Co., Ltd. +000754 Xyterra Computing, Inc. +000755 Lafon SA +000756 Juyoung Telecom +000757 Topcall International AG +000758 Dragonwave +000759 Boris Manufacturing Corp. +00075A Air Products and Chemicals, Inc. +00075B Gibson Guitars +00075C ENCAD, Inc. +00075D Celleritas Inc. +00075E Pulsar Technologies, Inc. +00075F VCS Video Communication Systems AG +000760 TOMIS Information & Telecom Corp. +000761 Logitech SA +000762 Group Sense Limited +000763 Sunniwell Cyber Tech. Co., Ltd. +000764 YoungWoo Telecom Co. Ltd. +000765 Jade Quantum Technologies, Inc. +000766 Chou Chin Industrial Co., Ltd. +000767 Yuxing Electronics Company Limited +000768 Danfoss A/S +000769 Italiana Macchi SpA +00076A NEXTEYE Co., Ltd. +00076B Stralfors AB +00076C Daehanet, Inc. +00076D Flexlight Networks +00076E Sinetica Corporation Ltd. +00076F Synoptics Limited +000770 Locusnetworks Corporation +000771 Embedded System Corporation +000772 Alcatel Shanghai Bell Co., Ltd. +000773 Ascom Powerline Communications Ltd. +000774 GuangZhou Thinker Technology Co. Ltd. +000775 Valence Semiconductor, Inc. +000776 Federal APD +000777 Motah Ltd. +000778 GERSTEL GmbH & Co. KG +000779 Sungil Telecom Co., Ltd. +00077A Infoware System Co., Ltd. +00077B Millimetrix Broadband Networks +00077C OnTime Networks +00077E Elrest GmbH +00077F J Communications Co., Ltd. +000780 Bluegiga Technologies OY +000781 Itron Inc. +000782 Nauticus Networks, Inc. +000783 SynCom Network, Inc. +000784 Cisco Systems Inc. +000785 Cisco Systems Inc. +000786 Wireless Networks Inc. +000787 Idea System Co., Ltd. +000788 Clipcomm, Inc. +000789 Eastel Systems Corporation +00078A Mentor Data System Inc. +00078B Wegener Communications, Inc. +00078C Elektronikspecialisten i Borlange AB +00078D NetEngines Ltd. +00078E Garz & Friche GmbH +00078F Emkay Innovative Products +000790 Tri-M Technologies (s) Limited +000791 International Data Communications, Inc. +000792 Suetron Electronic GmbH +000794 Simple Devices, Inc. +000795 Elitegroup Computer System Co. (ECS) +000796 LSI Systems, Inc. +000797 Netpower Co., Ltd. +000798 Selea SRL +000799 Tipping Point Technologies, Inc. +00079A SmartSight Networks Inc. +00079B Aurora Networks +00079C Golden Electronics Technology Co., Ltd. +00079D Musashi Co., Ltd. +00079E Ilinx Co., Ltd. +00079F Action Digital Inc. +0007A0 e-Watch Inc. +0007A1 VIASYS Healthcare GmbH +0007A2 Opteon Corporation +0007A3 Ositis Software, Inc. +0007A4 GN Netcom Ltd. +0007A5 Y.D.K Co. Ltd. +0007A6 Home Automation, Inc. +0007A7 A-Z Inc. +0007A8 Haier Group Technologies Ltd. +0007A9 Novasonics +0007AA Quantum Data Inc. +0007AC Eolring +0007AD Pentacon GmbH Foto-und Feinwerktechnik +0007AE Layer N Networks +0007AF N-Tron Corp. +0007B0 Office Details, Inc. +0007B1 Equator Technologies +0007B2 Transaccess S.A. +0007B3 Cisco Systems Inc. +0007B4 Cisco Systems Inc. +0007B5 Any One Wireless Ltd. +0007B6 Telecom Technology Ltd. +0007B7 Samurai Ind. Prods Eletronicos Ltda +0007B8 American Predator Corp. +0007B9 Ginganet Corporation +0007BA Xebeo Communications, Inc. +0007BB Candera Inc. +0007BC Identix Inc. +0007BD Radionet Ltd. +0007BE DataLogic SpA +0007BF Armillaire Technologies, Inc. +0007C0 NetZerver Inc. +0007C1 Overture Networks, Inc. +0007C2 Netsys Telecom +0007C3 Cirpack +0007C4 JEAN Co. Ltd. +0007C5 Gcom, Inc. +0007C6 VDS Vosskuhler GmbH +0007C7 Synectics Systems Limited +0007C8 Brain21, Inc. +0007C9 Technol Seven Co., Ltd. +0007CA Creatix Polymedia Ges Fur Kommunikaitonssysteme +0007CB Freebox SA +0007CC Kaba Benzing GmbH +0007CD NMTEL Co., Ltd. +0007CE Cabletime Limited +0007CF Anoto AB +0007D0 Automat Engenharia de Automaoa Ltda. +0007D1 Spectrum Signal Processing Inc. +0007D2 Logopak Systeme +0007D3 Stork Digital Imaging B.V. +0007D4 Zhejiang Yutong Network Communication Co Ltd. +0007D5 3e Technologies Int;., Inc. +0007D6 Commil Ltd. +0007D7 Caporis Networks AG +0007D8 Hitron Systems Inc. +0007D9 Splicecom +0007DA Neuro Telecom Co., Ltd. +0007DB Kirana Networks, Inc. +0007DC Atek Co, Ltd. +0007DD Cradle Technologies +0007DE eCopilt AB +0007DF Vbrick Systems Inc. +0007E0 Palm Inc. +0007E1 WIS Communications Co. Ltd. +0007E2 Bitworks, Inc. +0007E3 Navcom Technology, Inc. +0007E4 SoftRadio Co., Ltd. +0007E5 Coup Corporation +0007E6 edgeflow Canada Inc. +0007E7 FreeWave Technologies +0007E8 St. Bernard Software +0007E9 Intel Corporation +0007EA Massana, Inc. +0007EB Cisco Systems Inc. +0007EC Cisco Systems Inc. +0007ED Altera Corporation +0007EE telco Informationssysteme GmbH +0007EF Lockheed Martin Tactical Systems +0007F0 LogiSync Corporation +0007F1 TeraBurst Networks Inc. +0007F2 IOA Corporation +0007F3 Think Engine Networks +0007F4 Eletex Co., Ltd. +0007F5 Bridgeco Co AG +0007F6 Qqest Software Systems +0007F7 Galtronics +0007F8 ITDevices, Inc. +0007F9 Phonetics, Inc. +0007FA ITT Co., Ltd. +0007FB Giga Stream UMTS Technologies GmbH +0007FC Adept Systems Inc. +0007FD LANergy Ltd. +0007FE Rigaku Corporation +0007FF Gluon Networks +000800 MULTITECH SYSTEMS, INC. +000801 HighSpeed Surfing Inc. +000802 Compaq Computer Corporation +000803 Cos Tron +000804 ICA Inc. +000805 Techno-Holon Corporation +000806 Raonet Systems, Inc. +000807 Access Devices Limited +000808 PPT Vision, Inc. +000809 Systemonic AG +00080A Espera-Werke GmbH +00080B Birka BPA Informationssystem AB +00080C VDA elettronica SrL +00080D Toshiba +00080E Motorola, BCS +00080F Proximion Fiber Optics AB +000810 Key Technology, Inc. +000811 VOIX Corporation +000812 GM-2 Corporation +000813 Diskbank, Inc. +000814 TIL Technologies +000815 CATS Co., Ltd. +000816 Bluetags A/S +000817 EmergeCore Networks LLC +000818 Pixelworks, Inc. +000819 Banksys +00081A Sanrad Intelligence Storage Communications (2000) Ltd. +00081B Windigo Systems +00081C @pos.com +00081D Ipsil, Incorporated +00081E Repeatit AB +00081F Pou Yuen Tech Corp. Ltd. +000820 Cisco Systems Inc. +000821 Cisco Systems Inc. +000822 InPro Comm +000823 Texa Corp. +000824 Promatek Industries Ltd. +000825 Acme Packet +000826 Colorado Med Tech +000827 Pirelli Cables & Systems +000828 Koei Engineering Ltd. +000829 Aval Nagasaki Corporation +00082A Powerwallz Network Security +00082B Wooksung Electronics, Inc. +00082C Homag AG +00082D Indus Teqsite Private Limited +00082E Multitone Electronics PLC +00084E DivergeNet, Inc. +00084F Qualstar Corporation +000850 Arizona Instrument Corp. +000851 Canadian Bank Note Company, Ltd. +000852 Davolink Co. Inc. +000853 Schleicher GmbH & Co. Relaiswerke KG +000854 Netronix, Inc. +000855 NASA-Goddard Space Flight Center +000856 Gamatronic Electronic Industries Ltd. +000857 Polaris Networks, Inc. +000858 Novatechnology Inc. +000859 ShenZhen Unitone Electronics Co., Ltd. +00085A IntiGate Inc. +00085B Hanbit Electronics Co., Ltd. +00085C Shanghai Dare Technologies Co. Ltd. +00085D Aastra +00085E PCO AG +00085F Picanol N.V. +000860 LodgeNet Entertainment Corp. +000861 SoftEnergy Co., Ltd. +000862 NEC Eluminant Technologies, Inc. +000863 Entrisphere Inc. +000864 Fasy S.p.A. +000865 JASCOM CO., LTD +000866 DSX Access Systems, Inc. +000867 Uptime Devices +000868 PurOptix +000869 Command-e Technology Co.,Ltd. +00086A Industrie Technik IPS GmbH +00086B MIPSYS +00086C Plasmon LMS +00086D Missouri FreeNet +00086E Hyglo AB +00086F Resources Computer Network Ltd. +000870 Rasvia Systems, Inc. +000871 NORTHDATA Co., Ltd. +000872 Sorenson Technologies, Inc. +000873 DAP Design B.V. +000874 Dell Computer Corp. +000875 Acorp Electronics Corp. +000876 SDSystem +000877 Liebert HIROSS S.p.A. +000878 Benchmark Storage Innovations +000879 CEM Corporation +00087A Wipotec GmbH +00087B RTX Telecom A/S +00087C Cisco Systems, Inc. +00087D Cisco Systems Inc. +00087E Bon Electro-Telecom Inc. +00087F SPAUN electronic GmbH & Co. KG +000880 BroadTel Canada Communications inc. +000881 DIGITAL HANDS CO.,LTD. +000882 SIGMA CORPORATION +000883 Hewlett-Packard Company +000884 Index Braille AB +000885 EMS Dr. Thomas Wuensche +000886 Hansung Teliann, Inc. +000887 Maschinenfabrik Reinhausen GmbH +000888 OULLIM Information Technology Inc,. +000889 Echostar Technologies Corp +00088A Minds@Work +00088B Tropic Networks Inc. +00088C Quanta Network Systems Inc. +00088D Sigma-Links Inc. +00088E Nihon Computer Co., Ltd. +00088F ADVANCED DIGITAL TECHNOLOGY +000890 AVILINKS SA +000891 Lyan Inc. +000892 EM Solutions +000894 InnoVISION Multimedia Ltd. +000895 DIRC Technologie GmbH & Co.KG +000896 Printronix, Inc. +000897 Quake Technologies +000898 Gigabit Optics Corporation +000899 Netbind, Inc. +00089A Alcatel Microelectronics +00089B ICP Electronics Inc. +00089C Elecs Industry Co., Ltd. +00089D UHD-Elektronik +00089E Beijing Enter-Net co.LTD +00089F EFM Networks +0008A0 Stotz Feinmesstechnik GmbH +0008A1 CNet Technology Inc. +0008A2 ADI Engineering, Inc. +0008A3 Cisco Systems +0008A4 Cisco Systems +0008A5 Peninsula Systems Inc. +0008A6 Multiware & Image Co., Ltd. +0008A7 iLogic Inc. +0008A8 Systec Co., Ltd. +0008A9 SangSang Technology, Inc. +0008AA KARAM +0008AB EnerLinx.com, Inc. +0008AD Toyo-Linx Co., Ltd. +0008AE Packetfront +0008AF Novatec Corporation +0008B0 BKtel communications GmbH +0008B1 ProQuent Systems +0008B2 SHENZHEN COMPASS TECHNOLOGY DEVELOPMENT CO.,LTD +0008B3 Fastwel +0008B4 SYSPOL +0008B5 TAI GUEN ENTERPRISE CO., LTD +0008B6 RouteFree, Inc. +0008B7 HIT Incorporated +0008B8 E.F. Johnson +0008B9 KAON MEDIA Co., Ltd. +0008BA Erskine Systems Ltd +0008BB NetExcell +0008BC Ilevo AB +0008BD TEPG-US +0008BE XENPAK MSA Group +0008BF Aptus Elektronik AB +0008C0 ASA SYSTEMS +0008C1 Avistar Communications Corporation +0008C2 Cisco Systems +0008C3 Contex A/S +0008C4 Hikari Co.,Ltd. +0008C5 Liontech Co., Ltd. +0008C6 Philips Consumer Communications +0008C7 COMPAQ COMPUTER CORPORATION +0008C8 Soneticom, Inc. +0008C9 TechniSat Digital GmbH +0008CA TwinHan Technology Co.,Ltd +0008CB Zeta Broadband Inc. +0008CC Remotec, Inc. +0008CD With-Net Inc +0008CF Nippon Koei Power Systems Co., Ltd. +0008D0 Musashi Engineering Co., LTD. +0008D1 KAREL INC. +0008D2 ZOOM Networks Inc. +0008D3 Hercules Technologies S.A. +0008D4 IneoQuest Technologies, Inc +0008D5 Vanguard Managed Solutions +0008D6 HASSNET Inc. +0008D7 HOW CORPORATION +0008D8 Dowkey Microwave +0008D9 Mitadenshi Co.,LTD +0008DA SofaWare Technologies Ltd. +0008DB Corrigent Systems +0008DC Wiznet +0008DD Telena Communications, Inc. +0008DE 3UP Systems +0008DF Alistel Inc. +0008E0 ATO Technology Ltd. +0008E1 Barix AG +0008E2 Cisco Systems +0008E3 Cisco Systems +0008E4 Envenergy Inc +0008E5 IDK Corporation +0008E6 Littlefeet +0008E7 SHI ControlSystems,Ltd. +0008E8 Excel Master Ltd. +0008E9 NextGig +0008EA Motion Control Engineering, Inc +0008EB ROMWin Co.,Ltd. +0008EC Zonu, Inc. +0008ED ST&T Instrument Corp. +0008EE Logic Product Development +0008EF DIBAL,S.A. +0008F0 Next Generation Systems, Inc. +0008F1 Voltaire +0008F2 C&S Technology +0008F3 WANY +0008F4 Bluetake Technology Co., Ltd. +0008F5 YESTECHNOLOGY Co.,Ltd. +0008F6 SUMITOMO ELECTRIC HIGHTECHS.co.,ltd. +0008F7 Hitachi Ltd, Semiconductor & Integrated Circuits Gr +0008F8 Guardall Ltd +0008F9 Padcom, Inc. +0008FA Karl E.Brinkmann GmbH +0008FB SonoSite, Inc. +0008FC Gigaphoton Inc. +0008FD BlueKorea Co., Ltd. +0008FE UNIK C&C Co.,Ltd. +0008FF Trilogy Broadcast (Holdings) Ltd +000900 TMT +000901 Shenzhen Shixuntong Information & Technoligy Co +000902 Redline Communications Inc. +000903 Panasas, Inc +000904 MONDIAL electronic +000905 iTEC Technologies Ltd. +000906 Esteem Networks +000907 Chrysalis Development +000908 VTech Technology Corp. +000909 Telenor Connect A/S +00090A SnedFar Technology Co., Ltd. +00090B MTL Instruments PLC +00090C Mayekawa Mfg. Co. Ltd. +00090D LEADER ELECTRONICS CORP. +00090E Helix Technology Inc. +00090F Fortinet Inc. +000910 Simple Access Inc. +000911 Cisco Systems +000912 Cisco Systems +000914 COMPUTROLS INC. +000915 CAS Corp. +000916 Listman Home Technologies, Inc. +000917 WEM Technology Inc +000918 SAMSUNG TECHWIN CO.,LTD +000919 MDS Gateways +00091A Macat Optics & Electronics Co., Ltd. +00091B Digital Generation Inc. +00091C CacheVision, Inc +00091D Proteam Computer Corporation +00091E Firstech Technology Corp. +00091F A&D Co., Ltd. +000920 EpoX COMPUTER CO.,LTD. +000921 Planmeca Oy +000922 Touchless Sensor Technology AG +000923 Heaman System Co., Ltd +000924 Telebau GmbH +000925 VSN Systemen BV +000926 YODA COMMUNICATIONS, INC. +000927 TOYOKEIKI CO.,LTD. +000928 Telecore Inc +000929 Sanyo Industries (UK) Limited +00092A MYTECS Co.,Ltd. +00092B iQstor Networks, Inc. +00092C Hitpoint Inc. +00092D High Tech Computer, Corp. +00092E B&Tech System Inc. +00092F Akom Technology Corporation +000930 AeroConcierge Inc. +000931 Future Internet, Inc. +000932 Omnilux +000933 OPTOVALLEY Co. Ltd. +000934 Dream-Multimedia-Tv GmbH +000935 Sandvine Incorporated +000936 Ipetronik GmbH & Co.KG +000937 Inventec Appliance Corp +000938 Allot Communications +000939 ShibaSoku Co.,Ltd. +00093A Molex Fiber Optics +00093B HYUNDAI NETWORKS INC. +00093C Jacques Technologies P/L +00093D Newisys,Inc. +00093E C&I Technologies +00093F Double-Win Enterpirse CO., LTD +000940 AGFEO GmbH & Co. KG +000941 Allied Telesis K.K. +000942 CRESCO, LTD. +000943 Cisco Systems +000944 Cisco Systems +000945 Palmmicro Communications Inc +000946 Cluster Labs GmbH +000947 Aztek, Inc. +000948 Vista Control Systems, Corp. +000949 Glyph Technologies Inc. +00094A Homenet Communications +00094B FillFactory NV +00094C Communication Weaver Co.,Ltd. +00094D Braintree Communications Pty Ltd +00094E BARTECH SYSTEMS INTERNATIONAL, INC +00094F elmegt GmbH & Co. KG +000950 Independent Storage Corporation +000951 Apogee Instruments, Inc +000952 Auerswald GmbH & Co. KG +000953 Linkage System Integration Co.Ltd. +000954 AMiT spol. s. r. o. +000955 Young Generation International Corp. +000956 Network Systems Group, Ltd. (NSG) +000957 Supercaller, Inc. +000958 INTELNET S.A. +000959 Sitecsoft +00095A RACEWOOD TECHNOLOGY +00095B Netgear, Inc. +00095C Philips Medical Systems - Cardiac and Monitoring Systems (CM +00095D Dialogue Technology Corp. +00095E Masstech Group Inc. +00095F Telebyte, Inc. +000960 YOZAN Inc. +000961 Switchgear and Instrumentation Ltd +000962 Filetrac AS +000963 Dominion Lasercom Inc. +000964 Hi-Techniques +000966 Thales Navigation +000967 Tachyon, Inc +000968 TECHNOVENTURE, INC. +000969 Meret Optical Communications +00096A Cloverleaf Communications Inc. +00096B IBM Corporation +00096C Imedia Semiconductor Corp. +00096D Powernet Technologies Corp. +00096E GIANT ELECTRONICS LTD. +00096F Beijing Zhongqing Elegant Tech. Corp.,Limited +000970 Vibration Research Corporation +000971 Time Management, Inc. +000972 Securebase,Inc +000973 Lenten Technology Co., Ltd. +000974 Innopia Technologies, Inc. +000975 fSONA Communications Corporation +000976 Datasoft ISDN Systems GmbH +000977 Brunner Elektronik AG +000978 AIJI System Co., Ltd. +000979 Advanced Television Systems Committee, Inc. +00097A Louis Design Labs. +00097B Cisco Systems +00097C Cisco Systems +00097D SecWell Networks Oy +00097E IMI TECHNOLOGY CO., LTD +00097F Vsecure 2000 LTD. +000980 Power Zenith Inc. +000981 Newport Networks +000982 Loewe Opta GmbH +000983 Gvision Incorporated +000984 MyCasa Network Inc. +000985 Auto Telecom Company +000986 Metalink LTD. +000987 NISHI NIPPON ELECTRIC WIRE & CABLE CO.,LTD. +000988 Nudian Electron Co., Ltd. +000989 VividLogic Inc. +00098A EqualLogic Inc +00098B Entropic Communications, Inc. +00098C Possio AB +00098D DCT Ltd (Digital Communication Technologies Ltd) +00098E ipcas GmbH +00098F Cetacean Networks +000990 ACKSYS Communications & systems +000991 GE Fanuc Automation Manufacturing, Inc. +000992 InterEpoch Technology,INC. +000993 Visteon Corporation +000994 Cronyx Engineering +000995 Castle Technology Ltd +000996 RDI +000997 Nortel Networks +000998 Capinfo Company Limited +000999 CP GEORGES RENAULT +00099A ELMO COMPANY, LIMITED +00099B Western Telematic Inc. +00099C Naval Research Laboratory +00099D Haliplex Communications +00099E Testech, Inc. +00099F VIDEX INC. +0009A0 Microtechno Corporation +0009A1 Telewise Communications, Inc. +0009A2 Interface Co., Ltd. +0009A3 Leadfly Techologies Corp. Ltd. +0009A4 HARTEC Corporation +0009A5 HANSUNG ELETRONIC INDUSTRIES DEVELOPMENT CO., LTD +0009A6 Ignis Optics, Inc. +0009A7 Bang & Olufsen A/S +0009A8 Eastmode Pte Ltd +0009A9 Ikanos Communications +0009AA Data Comm for Business, Inc. +0009AB Netcontrol Oy +0009AC LANVOICE +0009AD HYUNDAI SYSCOMM, INC. +0009AE OKANO ELECTRIC CO.,LTD +0009AF e-generis +0009B0 Onkyo Corporation +0009B1 Kanematsu Electronics, Ltd. +0009B2 L&F Inc. +0009B3 MCM Systems Ltd +0009B4 KISAN TELECOM CO., LTD. +0009B5 3J Tech. Co., Ltd. +0009B6 Cisco Systems +0009B7 Cisco Systems +0009B8 Entise Systems +0009B9 Action Imaging Solutions +0009BA MAKU Informationstechik GmbH +0009BB MathStar, Inc. +0009BC Digital Safety Technologies Inc. +0009BD Epygi Technologies, Ltd. +0009BE Mamiya-OP Co.,Ltd. +0009BF Nintendo Co.,Ltd. +0009C0 6WIND +0009C1 PROCES-DATA A/S +0009C3 NETAS +0009C4 Medicore Co., Ltd +0009C5 KINGENE Technology Corporation +0009C6 Visionics Corporation +0009C7 Movistec +0009C8 SINAGAWA TSUSHIN KEISOU SERVICE +0009C9 BlueWINC Co., Ltd. +0009CA iMaxNetworks(Shenzhen)Limited. +0009CB HBrain +0009CC Moog GmbH +0009CD HUDSON SOFT CO.,LTD. +0009CE SpaceBridge Semiconductor Corp. +0009CF iAd GmbH +0009D0 Versatel Networks +0009D1 SERANOA NETWORKS INC +0009D2 Mai Logic Inc. +0009D3 Western DataCom Co., Inc. +0009D4 Transtech Networks +0009D5 Signal Communication, Inc. +0009D6 KNC One GmbH +0009D7 DC Security Products +0009D9 Neoscale Systems, Inc +0009DA Control Module Inc. +0009DB eSpace +0009DC Galaxis Technology AG +0009DD Mavin Technology Inc. +0009DE Samjin Information & Communications Co., Ltd. +0009DF Vestel Komunikasyon Sanayi ve Ticaret A.S. +0009E0 XEMICS S.A. +0009E1 Gemtek Technology Co., Ltd. +0009E2 Sinbon Electronics Co., Ltd. +0009E3 Angel Iglesias S.A. +0009E4 K Tech Infosystem Inc. +0009E5 Hottinger Baldwin Messtechnik GmbH +0009E6 Cyber Switching Inc. +0009E7 ADC Techonology +0009E8 Cisco Systems +0009E9 Cisco Systems +0009EA YEM Inc. +0009EB HuMANDATA LTD. +0009EC Daktronics, Inc. +0009ED CipherOptics +0009EE MEIKYO ELECTRIC CO.,LTD +0009EF Vocera Communications +0009F0 Shimizu Technology Inc. +0009F1 Yamaki Electric Corporation +0009F2 Cohu, Inc., Electronics Division +0009F3 WELL Communication Corp. +0009F4 Alcon Laboratories, Inc. +0009F5 Emerson Network Power Co.,Ltd +0009F6 Shenzhen Eastern Digital Tech Ltd. +0009F7 SED, a division of Calian +0009F8 UNIMO TECHNOLOGY CO., LTD. +0009F9 ART JAPAN CO., LTD. +0009FB Philips Medizinsysteme Boeblingen GmbH +0009FC IPFLEX Inc. +0009FD Ubinetics Limited +0009FE Daisy Technologies, Inc. +0009FF X.net 2000 GmbH +000A00 Mediatek Corp. +000A01 SOHOware, Inc. +000A02 ANNSO CO., LTD. +000A03 ENDESA SERVICIOS, S.L. +000A04 3Com Europe Ltd +000A05 Widax Corp. +000A06 Teledex LLC +000A07 WebWayOne Ltd +000A08 ALPINE ELECTRONICS, INC. +000A09 TaraCom Integrated Products, Inc. +000A0A SUNIX Co., Ltd. +000A0B Sealevel Systems, Inc. +000A0C Scientific Research Corporation +000A0D MergeOptics GmbH +000A0E Invivo Research Inc. +000A0F Ilryung Telesys, Inc +000A10 FAST media integrations AG +000A11 ExPet Technologies, Inc +000A12 Azylex Technology, Inc +000A13 Silent Witness +000A14 TECO a.s. +000A15 Silicon Data, Inc +000A16 Lassen Research +000A17 NESTAR COMMUNICATIONS, INC +000A18 Vichel Inc. +000A19 Valere Power, Inc. +000A1A Imerge Ltd +000A1B Stream Labs +000A1C Bridge Information Co., Ltd. +000A1D Optical Communications Products Inc. +000A1E Red-M (Communications) Limited +000A1F ART WARE Telecommunication Co., Ltd. +000A20 SVA Networks, Inc. +000A21 Integra Telecom Co. Ltd +000A22 Amperion Inc +000A23 Parama Networks Inc +000A24 Octave Communications +000A25 CERAGON NETWORKS +000A26 CEIA S.p.A. +000A27 Apple Computer, Inc. +000A28 Motorola +000A29 Pan Dacom Networking AG +000A2A QSI Systems Inc. +000A2B Etherstuff +000A2C Active Tchnology Corporation +000A2E MAPLE NETWORKS CO., LTD +000A2F Artnix Inc. +000A30 Johnson Controls-ASG +000A31 HCV Wireless +000A32 Xsido Corporation +000A33 Sierra Logic, Inc. +000A34 Identicard Systems Incorporated +000A35 Xilinx +000A36 Synelec Telecom Multimedia +000A37 Procera Networks, Inc. +000A38 Netlock Technologies, Inc. +000A39 LoPA Information Technology +000A3A J-THREE INTERNATIONAL Holding Co., Ltd. +000A3B GCT Semiconductor, Inc +000A3C Enerpoint Ltd. +000A3D Elo Sistemas Eletronicos S.A. +000A3E EADS Telecom +000A3F Data East Corporation +000A40 Crown Audio +000A41 Cisco Systems +000A42 Cisco Systems +000A43 Chunghwa Telecom Co., Ltd. +000A44 Avery Dennison Deutschland GmbH +000A45 Audio-Technica Corp. +000A46 ARO Controls SAS +000A47 Allied Vision Technologies +000A48 Albatron Technology +000A49 Acopia Networks +000A4A Targa Systems Ltd. +000A4B DataPower Technology, Inc. +000A4C Molecular Devices Corporation +000A4D Noritz Corporation +000A4E UNITEK Electronics INC. +000A4F Brain Boxes Limited +000A50 REMOTEK CORPORATION +000A51 GyroSignal Technology Co., Ltd. +000A52 Venitek Co. Ltd. +000A53 Intronics, Incorporated +000A54 Laguna Hills, Inc. +000A55 MARKEM Corporation +000A56 HITACHI Maxell Ltd. +000A57 Hewlett-Packard Company - Standards +000A58 Ingenieur-Buero Freyer & Siegel +000A59 HW server +000A5A GreenNET Technologies Co.,Ltd. +000A5B Power-One as +000A5C Carel s.p.a. +000A5D PUC Founder (MSC) Berhad +000A5E 3COM Corporation +000A5F almedio inc. +000A60 Autostar Technology Pte Ltd +000A61 Cellinx Systems Inc. +000A62 Crinis Networks, Inc. +000A63 DHD GmbH +000A64 Eracom Technologies +000A65 GentechMedia.co.,ltd. +000A66 MITSUBISHI ELECTRIC SYSTEM & SERVICE CO.,LTD. +000A67 OngCorp +000A68 SolarFlare Communications, Inc. +000A69 SUNNY bell Technology Co., Ltd. +000A6A SVM Microwaves s.r.o. +000A6B Tadiran Telecom Business Systems LTD +000A6C Walchem Corporation +000A6D EKS Elektronikservice GmbH +000A6E Broadcast Technology Limited +000A6F ZyTera Technologies Inc. +000A70 MPLS Forum +000A71 Avrio Technologies, Inc +000A72 SimpleTech, Inc. +000A73 Scientific Atlanta +000A74 Manticom Networks Inc. +000A75 Cat Electronics +000A76 Beida Jade Bird Huaguang Technology Co.,Ltd +000A77 Bluewire Technologies LLC +000A78 OLITEC +000A79 corega K.K. +000A7A Kyoritsu Electric Co., Ltd. +000A7B Cornelius Consult +000A7C Tecton Ltd +000A7D Valo, Inc. +000A7E The Advantage Group +000A7F Teradon Industries, Inc +000A80 Telkonet Inc. +000A81 TEIMA Audiotex S.L. +000A82 TATSUTA SYSTEM ELECTRONICS CO.,LTD. +000A83 SALTO SYSTEMS S.L. +000A84 Rainsun Enterprise Co., Ltd. +000A85 PLAT'C2,Inc +000A86 Lenze +000A87 Integrated Micromachines Inc. +000A88 InCypher S.A. +000A89 Creval Systems, Inc. +000A8A Cisco Systems +000A8B Cisco Systems +000A8C Guardware Systems Ltd. +000A8D EUROTHERM LIMITED +000A8E Invacom Ltd +000A8F Aska International Inc. +000A90 Bayside Interactive, Inc. +000A91 HemoCue AB +000A92 Presonus Corporation +000A93 W2 Networks, Inc. +000A94 ShangHai cellink CO., LTD +000A95 Apple Computer, Inc. +000A96 MEWTEL TECHNOLOGY INC. +000A97 SONICblue, Inc. +000A98 M+F Gwinner GmbH & Co +000A99 Dataradio Inc. +000A9A Aiptek International Inc +000A9B Towa Meccs Corporation +000A9C Server Technology, Inc. +000A9D King Young Technology Co. Ltd. +000A9E BroadWeb Corportation +000A9F Pannaway Technologies, Inc. +000AA0 Cedar Point Communications +000AA1 V V S Limited +000AA2 SYSTEK INC. +000AA3 SHIMAFUJI ELECTRIC CO.,LTD. +000AA4 SHANGHAI SURVEILLANCE TECHNOLOGY CO,LTD +000AA5 MAXLINK INDUSTRIES LIMITED +000AA6 Hochiki Corporation +000AA7 FEI Company +000AA8 ePipe Pty. Ltd. +000AA9 Brooks Automation GmbH +000AAA AltiGen Communications Inc. +000AAB TOYOTA MACS, INC. +000AAC TerraTec Electronic GmbH +000AAD Stargames Corporation +000AAE Rosemount Process Analytical +000AAF Pipal Systems +000AB0 LOYTEC electronics GmbH +000AB1 GENETEC Corporation +000AB2 Fresnel Wireless Systems +000AB3 Fa. GIRA +000AB4 ETIC Telecommunications +000AB5 Digital Electronic Network +000AB6 COMPUNETIX, INC +000AB7 Cisco Systems +000AB8 Cisco Systems +000AB9 Astera Technologies Corp. +000ABA Arcon Technology Limited +000ABB Taiwan Secom Co,. Ltd +000ABC Seabridge Ltd. +000ABD Rupprecht & Patashnick Co. +000ABE OPNET Technologies CO., LTD. +000ABF HIROTA SS +000AC0 Fuyoh Video Industry CO., LTD. +000AC1 Futuretel +000AC2 FiberHome Telecommunication Technologies CO.,LTD +000AC3 eM Technics Co., Ltd. +000AC4 Daewoo Teletech Co., Ltd +000AC5 Color Kinetics +000AC7 Unication Group +000AC8 ZPSYS CO.,LTD. (Planning&Management) +000AC9 Zambeel Inc +000ACA YOKOYAMA SHOKAI CO.,Ltd. +000ACB XPAK MSA Group +000ACC Winnow Networks, Inc. +000ACD Sunrich Technology Limited +000ACE RADIANTECH, INC. +000ACF PROVIDEO Multimedia Co. Ltd. +000AD0 Niigata Develoment Center, F.I.T. Co., Ltd. +000AD1 MWS +000AD2 JEPICO Corporation +000AD3 INITECH Co., Ltd +000AD4 CoreBell Systems Inc. +000AD5 Brainchild Electronic Co., Ltd. +000AD6 BeamReach Networks +000AD8 IPCserv Technology Corp. +000AD9 Sony Ericsson Mobile Communications AB +000ADB SkyPilot Network, Inc +000ADC RuggedCom Inc. +000ADD InSciTek Microsystems, Inc. +000ADE Happy Communication Co., Ltd. +000ADF Gennum Corporation +000AE0 Fujitsu Softek +000AE1 EG Technology +000AE2 Binatone Electronics International, Ltd +000AE3 YANG MEI TECHNOLOGY CO., LTD +000AE4 Wistron Corp. +000AE5 ScottCare Corporation +000AE6 Elitegroup Computer System Co. (ECS) +000AE7 ELIOP S.A. +000AE8 Cathay Roxus Information Technology Co. LTD +000AE9 AirVast Technology Inc. +000AEA ADAM ELEKTRONIK LTD.STI. +000AEB Shenzhen Tp-link Technology Co; Ltd. +000AEC Koatsu Gas Kogyo Co., Ltd. +000AED HARTING Vending G.m.b.H. & CO KG +000AEE GCD Hard- & Software GmbH +000AEF OTRUM ASA +000AF0 SHIN-OH ELECTRONICS CO., LTD. R&D +000AF1 Clarity Design, Inc. +000AF2 NeoAxiom Corp. +000AF3 Cisco Systems +000AF4 Cisco Systems +000AF5 Airgo Networks, Inc. +000AF6 Computer Process Controls +000AF7 Broadcom Corp. +000AF8 American Telecare Inc. +000AFA Traverse Technologies Australia +000AFB Ambri Limited +000AFC Core Tec Communications, LLC +000AFD Viking Electronic Services +000AFE NovaPal Ltd +000AFF Kilchherr Elektronik AG +000B00 FUJIAN START COMPUTER EQUIPMENT CO.,LTD +000B01 DAIICHI ELECTRONICS CO., LTD. +000B02 Dallmeier electronic +000B03 Taekwang Industrial Co., Ltd +000B04 Volktek Corporation +000B05 Pacific Broadband Networks +000B06 Motorola BCS +000B07 Voxpath Networks +000B08 Pillar Data Systems +000B09 Ifoundry Systems Singapore +000B0A dBm Optics +000B0B Corrent Corporation +000B0C Agile Systems Inc. +000B0D Air2U, Inc. +000B0E Trapeze Networks +000B0F Nyquist Industrial Control BV +000B10 11wave Technonlogy Co.,Ltd +000B11 HIMEJI ABC TRADING CO.,LTD. +000B13 ZETRON INC +000B14 ViewSonic Corporation +000B15 Platypus Technology +000B16 Communication Machinery Corporation +000B17 MKS Instruments +000B19 Vernier Networks, Inc. +000B1A Teltone Corporation +000B1B Systronix, Inc. +000B1D LayerZero Power Systems, Inc. +000B1E KAPPA opto-electronics GmbH +000B1F I CON Computer Co. +000B20 Hirata corporation +000B21 G-Star Communications Inc. +000B22 Environmental Systems and Services +000B23 Efficient Networks, Inc. +000B24 AirLogic +000B25 Aeluros +000B26 Wetek Corporation +000B27 Scion Corporation +000B28 Quatech Inc. +000B29 LG Industrial Systems Co.,Ltd. +000B2A HOWTEL Co., Ltd. +000B2B HOSTNET CORPORATION +000B2C Eiki Industrial Co. Ltd. +000B2D Danfoss Inc. +000B2E Cal-Comp Electronics (Thailand) Public Company Limited Taipe +000B2F bplan GmbH +000B30 Beijing Gongye Science & Technology Co.,Ltd +000B31 Yantai ZhiYang Scientific and technology industry CO., LTD +000B32 VORMETRIC, INC. +000B33 Vivato +000B34 ShangHai Broadband Technologies CO.LTD +000B35 Quad Bit System co., Ltd. +000B36 Productivity Systems, Inc. +000B37 MANUFACTURE DES MONTRES ROLEX SA +000B38 Knuerr AG +000B39 Keisoku Giken Co.,Ltd. +000B3A Fortel DTV, Inc. +000B3B devolo AG +000B3C Cygnal Integrated Products, Inc. +000B3D CONTAL OK Ltd. +000B3E BittWare, Inc +000B3F Anthology Solutions Inc. +000B40 OpNext Inc. +000B41 Ing. Buero Dr. Beutlhauser +000B42 commax Co., Ltd. +000B43 Microscan Systems, Inc. +000B44 Concord IDea Corp. +000B45 Cisco +000B46 Cisco +000B47 Advanced Energy +000B48 sofrel +000B49 RF-Link System Inc. +000B4A Visimetrics (UK) Ltd +000B4B VISIOWAVE SA +000B4C Clarion (M) Sdn Bhd +000B4D Emuzed +000B4E VertexRSI Antenna Products Division +000B4F Verifone, INC. +000B50 Oxygnet +000B51 Micetek International Inc. +000B52 JOYMAX ELECTRONICS CORP. +000B53 INITIUM Co., Ltd. +000B54 BiTMICRO Networks, Inc. +000B55 ADInstruments +000B56 Cybernetics +000B57 Silicon Laboratories +000B58 Astronautics C.A LTD +000B59 ScriptPro, LLC +000B5A HyperEdge +000B5B Rincon Research Corporation +000B5C Newtech Co.,Ltd +000B5D FUJITSU LIMITED +000B5E ATMAVA Ltd +000B5F Cisco Systems +000B60 Cisco Systems +000B61 Friedrich Lütze GmbH &Co. +000B62 Ingenieurbüro Ingo Mohnen +000B64 Kieback & Peter GmbH & Co KG +000B65 Sy.A.C. srl +000B66 Teralink Communications +000B67 Topview Technology Corporation +000B68 Addvalue Communications Pte Ltd +000B69 Franke Finland Oy +000B6A Asiarock Incorporation +000B6B Wistron Neweb Corp. +000B6C Sychip Inc. +000B6D SOLECTRON JAPAN NAKANIIDA +000B6E Neff Instrument Corp. +000B6F Media Streaming Networks Inc +000B70 Load Technology, Inc. +000B71 Litchfield Communications Inc. +000B72 Lawo AG +000B73 Kodeos Communications +000B74 Kingwave Technology Co., Ltd. +000B75 Iosoft Ltd. +000B76 ET&T Co. Ltd. +000B77 Cogent Systems, Inc. +000B78 TAIFATECH INC. +000B79 X-COM, Inc. +000B7B Test-Um Inc. +000B7C Telex Communications +000B7D SOLOMON EXTREME INTERNATIONAL LTD. +000B7E SAGINOMIYA Seisakusho Inc. +000B7F OmniWerks +000B81 Kaparel Corporation +000B82 Grandstream Networks, Inc. +000B83 DATAWATT B.V. +000B84 BODET +000B85 Airespace, Inc. +000B86 Aruba Networks +000B87 American Reliance Inc. +000B88 Vidisco ltd. +000B89 Top Global Technology, Ltd. +000B8A MITEQ Inc. +000B8B KERAJET, S.A. +000B8C flextronics israel +000B8D Avvio Networks +000B8E Ascent Corporation +000B8F AKITA ELECTRONICS SYSTEMS CO.,LTD. +000B90 Covaro Networks, Inc. +000B91 Aglaia Gesellschaft für Bildverarbeitung und Kommunikation m +000B92 Ascom Danmark A/S +000B93 Barmag Electronic +000B94 Digital Monitoring Products, Inc. +000B95 eBet Gaming Systems Pty Ltd +000B96 Innotrac Diagnostics Oy +000B97 Matsushita Electric Industrial Co.,Ltd. +000B98 NiceTechVision +000B99 SensAble Technologies, Inc. +000B9A Shanghai Ulink Telecom Equipment Co. Ltd. +000B9B Sirius System Co, Ltd. +000B9C TriBeam Technologies, Inc. +000B9D TwinMOS Technologies Inc. +000B9E Yasing Technology Corp. +000B9F Neue ELSA GmbH +000BA0 T&L Information Inc. +000BA1 SYSCOM Ltd. +000BA2 Sumitomo Electric Networks, Inc +000BA3 Siemens AG, I&S +000BA4 Shiron Satellite Communications Ltd. (1996) +000BA5 Quasar Cipta Mandiri, PT +000BA6 Miyakawa Electric Works Ltd. +000BA7 Maranti Networks +000BA8 HANBACK ELECTRONICS CO., LTD. +000BAA Aiphone co.,Ltd +000BAB Advantech Technology (CHINA) Co., Ltd. +000BAC 3Com Europe Ltd. +000BAD PC-PoS Inc. +000BAE Vitals System Inc. +000BB0 Sysnet Telematica srl +000BB1 Super Star Technology Co., Ltd. +000BB2 SMALLBIG TECHNOLOGY +000BB3 RiT technologies Ltd. +000BB4 RDC Semiconductor Inc., +000BB5 nStor Technologies, Inc. +000BB6 Mototech Inc. +000BB7 Micro Systems Co.,Ltd. +000BB8 Kihoku Electronic Co. +000BB9 Imsys AB +000BBA Harmonic Broadband Access Networks +000BBB Etin Systems Co., Ltd +000BBC En Garde Systems, Inc. +000BBD Connexionz Limited +000BBE Cisco Systems +000BBF Cisco Systems +000BC0 China IWNComm Co., Ltd. +000BC1 Bay Microsystems, Inc. +000BC2 Corinex Communication Corp. +000BC3 Multiplex, Inc. +000BC4 BIOTRONIK GmbH & Co +000BC5 SMC Networks, Inc. +000BC6 ISAC, Inc. +000BC7 ICET S.p.A. +000BC8 AirFlow Networks +000BC9 Electroline Equipment +000BCA DATAVAN International Corporation +000BCB Fagor Automation , S. Coop +000BCC JUSAN, S.A. +000BCD Compaq (HP) +000BCE Free2move AB +000BCF AGFA NDT INC. +000BD0 XiMeta Technology Americas Inc. +000BD1 Aeronix, Inc. +000BD2 Remopro Technology Inc. +000BD3 cd3o +000BD4 Beijing Wise Technology & Science Development Co.Ltd +000BD5 Nvergence, Inc. +000BD6 Paxton Access Ltd +000BD7 MBB Gelma GmbH +000BD8 Industrial Scientific Corp. +000BD9 General Hydrogen +000BDA EyeCross Co.,Inc. +000BDB Dell ESG PCBA Test +000BDC AKCP +000BDD TOHOKU RICOH Co., LTD. +000BDF Shenzhen RouterD Networks Limited +000BE0 SercoNet Ltd. +000BE2 Lumenera Corporation +000BE3 Key Stream Co., Ltd. +000BE4 Hosiden Corporation +000BE5 HIMS Korea Co., Ltd. +000BE6 Datel Electronics +000BE7 COMFLUX TECHNOLOGY INC. +000BE8 AOIP +000BEA Zultys Technologies +000BEB Systegra AG +000BEC NIPPON ELECTRIC INSTRUMENT, INC. +000BED ELM Inc. +000BEE inc.jet, Incorporated +000BEF Code Corporation +000BF0 MoTEX Products Co., Ltd. +000BF1 LAP Laser Applikations +000BF2 Chih-Kan Technology Co., Ltd. +000BF3 BAE SYSTEMS +000BF5 Shanghai Sibo Telecom Technology Co.,Ltd +000BF6 Nitgen Co., Ltd +000BF7 NIDEK CO.,LTD +000BF8 Infinera +000BF9 Gemstone communications, Inc. +000BFB D-NET International Corporation +000BFC Cisco Systems +000BFD Cisco Systems +000BFE CASTEL Broadband Limited +000BFF Berkeley Camera Engineering +000C00 BEB Industrie-Elektronik AG +000C01 Abatron AG +000C02 ABB Oy +000C03 HDMI Licensing, LLC +000C04 Tecnova +000C05 RPA Reserch Co., Ltd. +000C06 Nixvue Systems Pte Ltd +000C07 Iftest AG +000C08 HUMEX Technologies Corp. +000C09 Hitachi IE Systems Co., Ltd +000C0A Guangdong Province Electronic Technology Research Institute +000C0B Broadbus Technologies +000C0C APPRO TECHNOLOGY INC. +000C0D Communications & Power Industries / Satcom Division +000C0E XtremeSpectrum, Inc. +000C0F Techno-One Co., Ltd +000C10 PNI Corporation +000C11 NIPPON DEMPA CO.,LTD. +000C12 Micro-Optronic-Messtechnik GmbH +000C13 MediaQ +000C14 Diagnostic Instruments, Inc. +000C15 CyberPower Systems, Inc. +000C16 Concorde Microsystems Inc. +000C17 AJA Video Systems Inc +000C18 Zenisu Keisoku Inc. +000C19 Telio Communications GmbH +000C1A Quest Technical Solutions Inc. +000C1B ORACOM Co, Ltd. +000C1C MicroWeb Co., Ltd. +000C1D Mettler & Fuchs AG +000C1E Global Cache +000C1F Glimmerglass Networks +000C20 Fi WIn, Inc. +000C21 Faculty of Science and Technology, Keio University +000C22 Double D Electronics Ltd +000C23 Beijing Lanchuan Tech. Co., Ltd. +000C25 Allied Telesyn Networks +000C26 Weintek Labs. Inc. +000C27 Sammy Corporation +000C28 RIFATRON +000C29 VMware, Inc. +000C2A OCTTEL Communication Co., Ltd. +000C2B ELIAS Technology, Inc. +000C2C Enwiser Inc. +000C2D FullWave Technology Co., Ltd. +000C2E Openet information technology(shenzhen) Co., Ltd. +000C2F SeorimTechnology Co.,Ltd. +000C30 Cisco +000C31 Cisco +000C32 Avionic Design Development GmbH +000C33 Compucase Enterprise Co. Ltd. +000C34 Vixen Co., Ltd. +000C35 KaVo Dental GmbH & Co. KG +000C36 SHARP TAKAYA ELECTRONICS INDUSTRY CO.,LTD. +000C37 Geomation, Inc. +000C38 TelcoBridges Inc. +000C39 Sentinel Wireless Inc. +000C3A Oxance +000C3B Orion Electric Co., Ltd. +000C3C MediaChorus, Inc. +000C3D Glsystech Co., Ltd. +000C3E Crest Audio +000C3F Cogent Defence & Security Networks, +000C40 Altech Controls +000C41 The Linksys Group, Inc. +000C42 Routerboard.com +000C43 Ralink Technology, Corp. +000C44 Automated Interfaces, Inc. +000C45 Animation Technologies Inc. +000C46 Allied Telesyn Inc. +000C47 SK Teletech(R&D Planning Team) +000C48 QoStek Corporation +000C49 Dangaard Telecom RTC Division A/S +000C4A Cygnus Microsystems Private Limited +000C4B Cheops Elektronik +000C4C Arcor AG&Co. +000C4D ACRA CONTROL +000C4E Winbest Technology CO,LT +000C4F UDTech Japan Corporation +000C50 Seagate Technology +000C51 Scientific Technologies Inc. +000C52 Roll Systems Inc. +000C54 Pedestal Networks, Inc +000C55 Microlink Communications Inc. +000C56 Megatel Computer (1986) Corp. +000C57 MACKIE Engineering Services Belgium BVBA +000C58 M&S Systems +000C59 Indyme Electronics, Inc. +000C5A IBSmm Industrieelektronik Multimedia +000C5B HANWANG TECHNOLOGY CO.,LTD +000C5C GTN Systems B.V. +000C5D CHIC TECHNOLOGY (CHINA) CORP. +000C5F Avtec, Inc. +000C60 ACM Systems +000C61 AC Tech corporation DBA Advanced Digital +000C62 ABB Automation Technology Products AB, Control +000C63 Zenith Electronics Corporation +000C64 X2 MSA Group +000C65 Sunin Telecom +000C66 Pronto Networks Inc +000C67 OYO ELECTRIC CO.,LTD +000C68 Oasis Semiconductor, Inc. +000C69 National Radio Astronomy Observatory +000C6A MBARI +000C6B Kurz Industrie-Elektronik GmbH +000C6C Elgato Systems LLC +000C6D BOC Edwards +000C6E ASUSTEK COMPUTER INC. +000C6F Amtek system co.,LTD. +000C70 ACC GmbH +000C71 Wybron, Inc +000C72 Tempearl Industrial Co., Ltd. +000C73 TELSON ELECTRONICS CO., LTD +000C74 RIVERTEC CORPORATION +000C75 Oriental integrated electronics. LTD +000C76 MICRO-STAR INTERNATIONAL CO., LTD. +000C77 Life Racing Ltd +000C78 In-Tech Electronics Limited +000C79 Extel Communications P/L +000C7A DaTARIUS Technologies GmbH +000C7B ALPHA PROJECT Co.,Ltd. +000C7C Internet Information Image Inc. +000C7D TEIKOKU ELECTRIC MFG. CO., LTD +000C7E Tellium Incorporated +000C7F synertronixx GmbH +000C80 Opelcomm Inc. +000C81 Nulec Industries Pty Ltd +000C82 NETWORK TECHNOLOGIES INC +000C83 Logical Solutions +000C84 Eazix, Inc. +000C85 Cisco Systems +000C86 Cisco Systems +000C87 ATI +000C88 Apache Micro Peripherals, Inc. +000C89 AC Electric Vehicles, Ltd. +000C8A Bose Corporation +000C8B Connect Tech Inc +000C8C KODICOM CO.,LTD. +000C8D MATRIX VISION GmbH +000C8E Mentor Engineering Inc +000C8F Nergal s.r.l. +000C90 Octasic Inc. +000C91 Riverhead Networks Inc. +000C92 WolfVision Gmbh +000C93 Xeline Co., Ltd. +000C94 United Electronic Industries, Inc. +000C95 PrimeNet +000C96 OQO, Inc. +000C97 NV ADB TTV Technologies SA +000C98 LETEK Communications Inc. +000C99 HITEL LINK Co.,Ltd +000C9A Hitech Electronics Corp. +000C9B EE Solutions, Inc +000C9C Chongho information & communications +000C9D AirWalk Communications, Inc. +000C9E MemoryLink Corp. +000C9F NKE Corporation +000CA0 StorCase Technology, Inc. +000CA1 SIGMACOM Co., LTD. +000CA2 Scopus Network Technologies Ltd +000CA3 Rancho Technology, Inc. +000CA4 Prompttec Product Management GmbH +000CA6 Mintera Corporation +000CA7 Metro (Suzhou) Technologies Co., Ltd. +000CA8 Garuda Networks Corporation +000CA9 Ebtron Inc. +000CAA Cubic Transportation Systems Inc +000CAB COMMEND International +000CAC Citizen Watch Co., Ltd. +000CAD BTU International +000CAE Ailocom Oy +000CAF TRI TERM CO.,LTD. +000CB0 Star Semiconductor Corporation +000CB1 Salland Engineering (Europe) BV +000CB2 safei Co., Ltd. +000CB3 ROUND Co.,Ltd. +000CB4 Propagate Networks, Inc +000CB5 Premier Technolgies, Inc +000CB6 NANJING SEU MOBILE & INTERNET TECHNOLOGY CO.,LTD +000CB7 Nanjing Huazhuo Electronics Co., Ltd. +000CB8 MEDION AG +000CB9 LEA +000CBA Jamex +000CBB ISKRAEMECO +000CBC Iscutum +000CBD Interface Masters, Inc +000CBF Holy Stone Ent. Co., Ltd. +000CC0 Genera Oy +000CC1 Cooper Industries Inc. +000CC3 BeWAN systems +000CC4 Tiptel AG +000CC5 Nextlink Co., Ltd. +000CC6 Ka-Ro electronics GmbH +000CC7 Intelligent Computer Solutions Inc. +000CC8 Integrated Digital Systems, Inc. +000CC9 ILWOO DATA & TECHNOLOGY CO.,LTD +000CCA Hitachi Global Storage Technologies +000CCB Design Combus Ltd +000CCC Bluesoft Ltd. +000CCD IEC - TC57 +000CCE Cisco Systems +000CCF Cisco Systems +000CD0 Symetrix +000CD1 SFOM Technology Corp. +000CD2 Schaffner EMV AG +000CD3 Prettl Elektronik Radeberg GmbH +000CD4 Positron Public Safety Systems inc. +000CD5 Passave Inc. +000CD6 PARTNER TECH +000CD7 Nallatech Ltd +000CD8 M. K. Juchheim GmbH & Co +000CD9 Itcare Co., Ltd +000CDA FreeHand Systems, Inc. +000CDB Foundry Networks +000CDC BECS Technology, Inc +000CDD AOS Technologies AG +000CDE ABB STOTZ-KONTAKT GmbH +000CDF PULNiX America, Inc +000CE0 Trek Diagnostics Inc. +000CE1 The Open Group +000CE2 Rolls-Royce +000CE3 Option International N.V. +000CE4 NeuroCom International, Inc. +000CE5 Motorola BCS +000CE6 Meru Networks Inc +000CE7 MediaTek Inc. +000CE8 GuangZhou AnJuBao Co., Ltd +000CE9 BLOOMBERG L.P. +000CEA aphona Kommunikationssysteme +000CEB CNMP Networks, Inc. +000CEC Spectracom Corp. +000CED Real Digital Media +000CEE Q-Networks +000CEF Open Networks Engineering Ltd +000CF0 M & N GmbH +000CF1 Intel Corporation +000CF2 GAMESA EÓLICA +000CF3 CALL IMAGE SA +000CF4 AKATSUKI ELECTRIC MFG.CO.,LTD. +000CF5 InfoExpress +000CF6 Sitecom Europe BV +000CF7 Nortel Networks +000CF8 Nortel Networks +000CF9 ITT Flygt AB +000CFA Digital Systems Corp +000CFB Korea Network Systems +000CFC S2io Technologies Corp +000CFE Grand Electronic Co., Ltd +000CFF MRO-TEK LIMITED +000D00 Seaway Networks Inc. +000D01 P&E Microcomputer Systems, Inc. +000D02 NEC Access Technica,Ltd +000D03 Matrics, Inc. +000D04 Foxboro Eckardt Development GmbH +000D05 cybernet manufacturing inc. +000D06 Compulogic Limited +000D07 Calrec Audio Ltd +000D08 AboveCable, Inc. +000D09 Yuehua(Zhuhai) Electronic CO. LTD +000D0A Projectiondesign as +000D0B Melco Inc. +000D0C MDI Security Systems +000D0D ITSupported, LLC +000D0E Inqnet Systems, Inc. +000D0F Finlux Ltd +000D10 Embedtronics Oy +000D11 DENTSPLY - Gendex +000D12 AXELL Corporation +000D13 Wilhelm Rutenbeck GmbH&Co. +000D14 Vtech Innovation LP dba Advanced American Telephones +000D15 Voipac s.r.o. +000D16 UHS Systems Pty Ltd +000D17 Turbo Networks Co.Ltd +000D18 Sunitec Enterprise Co., Ltd. +000D19 ROBE Show lighting +000D1A Mustek System Inc. +000D1B Kyoto Electronics Manufacturing Co., Ltd. +000D1C I2E TELECOM +000D1D HIGH-TEK HARNESS ENT. CO., LTD. +000D1E Control Techniques +000D1F AV Digital +000D20 ASAHIKASEI TECHNOSYSTEM CO.,LTD. +000D21 WISCORE Inc. +000D22 Unitronics +000D23 Smart Solution, Inc +000D24 SENTEC E&E CO., LTD. +000D25 SANDEN CORPORATION +000D26 Primagraphics Limited +000D27 MICROPLEX Printware AG +000D28 Cisco +000D29 Cisco +000D2A Scanmatic AS +000D2B Racal Instruments +000D2C Patapsco Designs Ltd +000D2D NCT Deutschland GmbH +000D2E Matsushita Avionics Systems Corporation +000D2F AIN Comm.Tech.Co., LTD +000D30 IceFyre Semiconductor +000D31 Compellent Technologies, Inc. +000D32 DispenseSource, Inc. +000D33 Prediwave Corp. +000D34 Shell International Exploration and Production, Inc. +000D35 PAC International Ltd +000D36 Wu Han Routon Electronic Co., Ltd +000D37 WIPLUG +000D38 NISSIN INC. +000D39 Network Electronics +000D3A Microsoft Corp. +000D3B Microelectronics Technology Inc. +000D3C i.Tech Dynamic Ltd +000D3E APLUX Communications Ltd. +000D3F VXI Technology +000D40 Verint Loronix Video Solutions +000D41 Siemens AG ICM MP UC RD IT KLF1 +000D42 Newbest Development Limited +000D43 DRS Tactical Systems Inc. +000D45 Tottori SANYO Electric Co., Ltd. +000D46 Eurotherm Drives, Ltd. +000D47 Collex +000D48 AEWIN Technologies Co., Ltd. +000D49 Triton Systems of Delaware, Inc. +000D4A Steag ETA-Optik +000D4B Roku, LLC +000D4C Outline Electronics Ltd. +000D4D Ninelanes +000D4E NDR Co.,LTD. +000D4F Kenwood Corporation +000D50 Galazar Networks +000D51 DIVR Systems, Inc. +000D52 Comart system +000D53 Beijing 5w Communication Corp. +000D54 3Com Europe Ltd +000D55 SANYCOM Technology Co.,Ltd +000D56 Dell PCBA Test +000D57 Fujitsu I-Network Systems Limited. +000D59 Amity Systems, Inc. +000D5A Tiesse SpA +000D5B Smart Empire Investments Limited +000D5C Robert Bosch GmbH, VT-ATMO +000D5D Raritan Computer, Inc +000D5E NEC CustomTechnica, Ltd. +000D5F Minds Inc +000D60 IBM Corporation +000D61 Giga-Byte Technology Co., Ltd. +000D62 Funkwerk Dabendorf GmbH +000D63 DENT Instruments, Inc. +000D64 COMAG Handels AG +000D65 Cisco Systems +000D66 Cisco Systems +000D67 BelAir Networks Inc. +000D68 Vinci Systems, Inc. +000D69 TMT&D Corporation +000D6A Redwood Technologies LTD +000D6B Mita-Teknik A/S +000D6C M-Audio +000D6D K-Tech Devices Corp. +000D6E K-Patents Oy +000D6F Ember Corporation +000D70 Datamax Corporation +000D71 boca systems +000D72 2Wire, Inc +000D73 Technical Support, Inc. +000D74 Sand Network Systems, Inc. +000D75 Kobian Pte Ltd - Taiwan Branch +000D76 Hokuto Denshi Co,. Ltd. +000D77 FalconStor Software +000D78 Engineering & Security +000D79 Dynamic Solutions Co,.Ltd. +000D7A DiGATTO Asia Pacific Pte Ltd +000D7B Consensys Computers Inc. +000D7C Codian Ltd +000D7D Afco Systems +000D7E Axiowave Networks, Inc. +000D7F MIDAS COMMUNICATION TECHNOLOGIES PTE LTD ( Foreign Branch) +000D80 Online Development Inc +000D81 Pepperl+Fuchs GmbH +000D82 PHS srl +000D83 Sanmina-SCI Hungary Ltd. +000D84 Seodu Inchip, Inc. +000D85 Tapwave, Inc. +000D86 Huber + Suhner AG +000D87 Elitegroup Computer System Co. (ECS) +000D88 D-Link Corporation +000D89 Bils Technology Inc +000D8A Winners Electronics Co., Ltd. +000D8B T&D Corporation +000D8C Shanghai Wedone Digital Ltd. CO. +000D8D ProLinx Communication Gateways, Inc. +000D8E Koden Electronics Co., Ltd. +000D8F King Tsushin Kogyo Co., LTD. +000D90 Factum Electronics AB +000D91 Eclipse (HQ Espana) S.L. +000D92 Arima Communication Corporation +000D93 Apple Computer +000D94 AFAR Communications,Inc +000D96 Vtera Technology Inc. +000D97 Tropos Networks, Inc. +000D98 S.W.A.C. Schmitt-Walter Automation Consult GmbH +000D99 Orbital Sciences Corp.; Launch Systems Group +000D9A INFOTEC LTD +000D9C Elan GmbH & Co KG +000D9D Hewlett Packard +000D9E TOKUDEN OHIZUMI SEISAKUSYO Co.,Ltd. +000D9F RF Micro Devices +000DA0 NEDAP N.V. +000DA1 MIRAE ITS Co.,LTD. +000DA2 Infrant Technologies, Inc. +000DA3 Emerging Technologies Limited +000DA4 DOSCH & AMAND SYSTEMS AG +000DA5 Fabric7 Systems, Inc +000DA6 Universal Switching Corporation +000DA8 Teletronics Technology Corporation +000DA9 T.E.A.M. S.L. +000DAA S.A.Tehnology co.,Ltd. +000DAB Parker Hannifin GmbH Electromechanical Division Europe +000DAC Japan CBM Corporation +000DAD Dataprobe Inc +000DAE SAMSUNG HEAVY INDUSTRIES CO., LTD. +000DAF Plexus Corp (UK) Ltd +000DB0 Olym-tech Co.,Ltd. +000DB1 Japan Network Service Co., Ltd. +000DB2 Ammasso, Inc. +000DB3 SDO Communication Corperation +000DB4 NETASQ +000DB5 GLOBALSAT TECHNOLOGY CORPORATION +000DB6 Teknovus, Inc. +000DB7 SANKO ELECTRIC CO,.LTD +000DB8 SCHILLER AG +000DB9 PC Engines GmbH +000DBA Océ Document Technologies GmbH +000DBB Nippon Dentsu Co.,Ltd. +000DBC Cisco Systems +000DBD Cisco Systems +000DBE Bel Fuse Europe Ltd.,UK +000DBF TekTone Sound & Signal Mfg., Inc. +000DC0 Spagat AS +000DC1 SafeWeb Inc +000DC3 First Communication, Inc. +000DC4 Emcore Corporation +000DC5 EchoStar International Corporation +000DC6 DigiRose Technology Co., Ltd. +000DC7 COSMIC ENGINEERING INC. +000DC8 AirMagnet, Inc +000DC9 THALES Elektronik Systeme GmbH +000DCA Tait Electronics +000DCB Petcomkorea Co., Ltd. +000DCC NEOSMART Corp. +000DCD GROUPE TXCOM +000DCE Dynavac Technology Pte Ltd +000DCF Cidra Corp. +000DD0 TetraTec Instruments GmbH +000DD1 Stryker Corporation +000DD2 Simrad Optronics ASA +000DD3 SAMWOO Telecommunication Co.,Ltd. +000DD4 Revivio Inc. +000DD5 O'RITE TECHNOLOGY CO.,LTD +000DD7 Bright +000DD8 BBN +000DD9 Anton Paar GmbH +000DDA ALLIED TELESIS K.K. +000DDB AIRWAVE TECHNOLOGIES INC. +000DDC VAC +000DDD PROFÝLO TELRA ELEKTRONÝK SANAYÝ VE TÝCARET A.Þ. +000DDE Joyteck Co., Ltd. +000DDF Japan Image & Network Inc. +000DE0 ICPDAS Co.,LTD +000DE1 Control Products, Inc. +000DE2 CMZ Sistemi Elettronici +000DE3 AT Sweden AB +000DE4 DIGINICS, Inc. +000DE5 Samsung Thales +000DE6 YOUNGBO ENGINEERING CO.,LTD +000DE7 Snap-on OEM Group +000DE8 Nasaco Electronics Pte. Ltd +000DE9 Napatech Aps +000DEA Kingtel Telecommunication Corp. +000DEB CompXs Limited +000DEC Cisco Systems +000DED Cisco Systems +000DEF Soc. Coop. Bilanciai +000DF0 QCOM TECHNOLOGY INC. +000DF1 IONIX INC. +000DF3 Asmax Solutions +000DF4 Watertek Co. +000DF5 Teletronics International Inc. +000DF6 Technology Thesaurus Corp. +000DF7 Space Dynamics Lab +000DF8 ORGA Kartensysteme GmbH +000DF9 NDS Limited +000DFA Micro Control Systems Ltd. +000DFB Komax AG +000DFC ITFOR Inc. resarch and development +000DFD Huges Hi-Tech Inc., +000DFE Hauppauge Computer Works, Inc. +000DFF CHENMING MOLD INDUSTRY CORP. +000E01 ASIP Technologies Inc. +000E02 Advantech AMT Inc. +000E03 Aarohi Communications, Inc. +000E05 WIRELESS MATRIX CORP. +000E06 Team Simoco Ltd +000E07 Sony Ericsson Mobile Communications AB +000E08 Sipura Technology, Inc. +000E09 Shenzhen Coship Software Co.,LTD. +000E0B Netac Technology Co., Ltd. +000E0C Intel Corporation +000E0D HESCH Schröder GmbH +000E0E ESA elettronica S.P.A. +000E0F ERMME +000E11 BDT Büro- und Datentechnik GmbH & Co. KG +000E12 Adaptive Micro Systems Inc. +000E13 Accu-Sort Systems inc. +000E14 Visionary Solutions, Inc. +000E15 Tadlys LTD +000E16 SouthWing +000E18 MyA Technology +000E19 LogicaCMG Pty Ltd +000E1B IAV GmbH +000E1C Hach Company +000E1F TCL Networks Equipment Co., Ltd. +000E20 PalmSource, Inc. +000E21 MTU Friedrichshafen GmbH +000E23 Incipient, Inc. +000E25 Hannae Technology Co., Ltd +000E26 Gincom Technology Corp. +000E27 Crere Networks, Inc. +000E28 Dynamic Ratings P/L +000E29 Shester Communications Inc +000E2B Safari Technologies +000E2C Netcodec co. +000E2D Hyundai Digital Technology Co.,Ltd. +000E2E Edimax Technology Co., Ltd. +000E2F Disetronic Medical Systems AG +000E30 AERAS Networks, Inc. +000E31 Olympus BioSystems GmbH +000E32 Kontron Medical +000E33 Shuko Electronics Co.,Ltd +000E34 NexxGenCity +000E35 Intel Corp +000E36 HEINESYS, Inc. +000E37 Harms & Wende GmbH & Co.KG +000E38 Cisco Systems +000E39 Cisco Systems +000E3A Cirrus Logic +000E3B Hawking Technologies, Inc. +000E3C TransAct Technoloiges Inc. +000E3D Televic N.V. +000E3E Sun Optronics Inc +000E3F Soronti, Inc. +000E40 Nortel Networks +000E41 NIHON MECHATRONICS CO.,LTD. +000E42 Motic Incoporation Ltd. +000E43 G-Tek Electronics Sdn. Bhd. +000E44 Digital 5, Inc. +000E45 Beijing Newtry Electronic Technology Ltd +000E46 Niigata Seimitsu Co.,Ltd. +000E47 NCI System Co.,Ltd. +000E48 Lipman TransAction Solutions +000E49 Forsway Scandinavia AB +000E4A Changchun Huayu WEBPAD Co.,LTD +000E4B atrium c and i +000E4C Bermai Inc. +000E4D Numesa Inc. +000E4E Waveplus Technology Co., Ltd. +000E4F Trajet GmbH +000E50 Thomson Multi Media +000E51 tecna elettronica srl +000E52 Optium Corporation +000E53 AV TECH CORPORATION +000E54 AlphaCell Wireless Ltd. +000E55 AUVITRAN +000E56 4G Systems GmbH +000E57 Iworld Networking, Inc. +000E58 Rincon Networks +000E5A TELEFIELD inc. +000E5B ParkerVision - Direct2Data +000E5C Motorola BCS +000E5D Com-X Networks +000E5E Beijing Raisecom Science & Technology Development Co.,Ltd +000E5F activ-net GmbH & Co. KG +000E60 360SUN Digital Broadband Corporation +000E61 MICROTROL LIMITED +000E62 Nortel Networks +000E63 Lemke Diagnostics GmbH +000E64 Elphel, Inc +000E65 TransCore +000E66 Hitachi Advanced Digital, Inc. +000E67 Eltis Microelectronics Ltd. +000E68 E-TOP Network Technology Inc. +000E69 China Electric Power Research Institute +000E6A 3COM EUROPE LTD +000E6B Janitza electronics GmbH +000E6C Device Drivers Limited +000E6D Murata Manufacturing Co., Ltd. +000E6E MICRELEC ELECTRONICS S.A +000E6F IRIS Corporation Berhad +000E70 in2 Networks +000E71 Gemstar Technology Development Ltd. +000E72 CTS electronics +000E73 Tpack A/S +000E74 Solar Telecom. Tech +000E75 New York Air Brake Corp. +000E76 GEMSOC INNOVISION INC. +000E77 Decru, Inc. +000E78 Amtelco +000E79 Ample Communications Inc. +000E7B Toshiba +000E7D Electronics Line 3000 Ltd. +000E7E Comprog Oy +000E7F Hewlett Packard +000E81 Instant802 Networks Inc. +000E82 Commtech Wireless +000E83 Cisco Systems +000E84 Cisco Systems +000E85 Catalyst Enterprises, Inc. +000E86 Alcatel North America +000E87 adp Gauselmann GmbH +000E88 VIDEOTRON CORP. +000E89 CLEMATIC +000E8A Avara Technologies Pty. Ltd. +000E8B Astarte Technology Co, Ltd. +000E8C Siemens AG A&D ET +000E8D Systems in Progress Holding GmbH +000E8E SparkLAN Communications, Inc. +000E8F Sercomm Corp. +000E90 PONICO CORP. +000E92 Millinet Co., Ltd. +000E93 Milénio 3 Sistemas Electrónicos, Lda. +000E94 Maas International BV +000E95 Fujiya Denki Seisakusho Co.,Ltd. +000E96 Cubic Defense Applications, Inc. +000E97 Ultracker Technology CO., Inc +000E98 Vitec CC, INC. +000E99 Spectrum Digital, Inc +000E9A BOE TECHNOLOGY GROUP CO.,LTD +000E9C Pemstar +000E9D Video Networks Ltd +000E9E Topfield Co., Ltd +000E9F TEMIC SDS GmbH +000EA0 NetKlass Technology Inc. +000EA1 Formosa Teletek Corporation +000EA2 CyberGuard Corporation +000EA3 CNCR-IT CO.,LTD,HangZhou P.R.CHINA +000EA4 Certance Inc. +000EA5 BLIP Systems +000EA6 ASUSTEK COMPUTER INC. +000EA7 Endace Inc Ltd. +000EA8 United Technologists Europe Limited +000EA9 Shanghai Xun Shi Communications Equipment Ltd. Co. +000EAC MINTRON ENTERPRISE CO., LTD. +000EAD Metanoia Technologies, Inc. +000EAE GAWELL TECHNOLOGIES CORP. +000EAF CASTEL +000EB0 Solutions Radio BV +000EB1 Newcotech,Ltd +000EB2 Micro-Research Finland Oy +000EB3 LeftHand Networks +000EB4 GUANGZHOU GAOKE COMMUNICATIONS TECHNOLOGY CO.LTD. +000EB5 Ecastle Electronics Co., Ltd. +000EB6 Riverbed Technology, Inc. +000EB7 Knovative, Inc. +000EB8 Iiga co.,Ltd +000EB9 HASHIMOTO Electronics Industry Co.,Ltd. +000EBA HANMI SEMICONDUCTOR CO., LTD. +000EBB Everbee Networks +000EBC Cullmann GmbH +000EBD Burdick, a Quinton Compny +000EBE B&B Electronics Manufacturing Co. +000EC0 Nortel Networks +000EC1 MYNAH Technologies +000EC2 Lowrance Electronics, Inc. +000EC3 Logic Controls, Inc. +000EC4 Iskra Transmission d.d. +000EC6 ASIX ELECTRONICS CORP. +000EC7 Appeal Telecom Co.,Ltd. +000EC8 Zoran Corporation +000EC9 YOKO Technology Corp. +000ECB VineSys Technology +000ECC Tableau +000ECD SKOV A/S +000ECE S.I.T.T.I. S.p.A. +000ECF PROFIBUS Nutzerorganisation e.V. +000ED0 Privaris, Inc. +000ED1 Osaka Micro Computer. +000ED2 Filtronic plc +000ED3 Epicenter, Inc. +000ED4 CRESITT INDUSTRIE +000ED5 COPAN Systems Inc. +000ED6 Cisco Systems +000ED7 Cisco Systems +000ED8 Aktino, Inc. +000ED9 Aksys, Ltd. +000EDA C-TECH UNITED CORP. +000EDB XiNCOM Corp. +000EDC Tellion INC. +000EDD SHURE INCORPORATED +000EDE REMEC, Inc. +000EDF PLX Technology +000EE0 Mcharge +000EE1 ExtremeSpeed Inc. +000EE2 Custom Engineering S.p.A. +000EE3 Chiyu Technology Co.,Ltd +000EE5 bitWallet, Inc. +000EE6 Adimos Systems LTD +000EE7 AAC ELECTRONICS CORP. +000EE8 zioncom +000EE9 WayTech Development, Inc. +000EEA Shadong Luneng Jicheng Electronics,Co.,Ltd +000EEB Sandmartin(zhong shan)Electronics Co.,Ltd +000EEC Orban +000EED Nokia Danmark A/S +000EEE Muco Industrie BV +000EF0 Festo AG & Co. KG +000EF1 EZQUEST INC. +000EF3 Smarthome +000EF4 Shenzhen Kasda Digital Technology Co.,Ltd +000EF5 iPAC Technology Co., Ltd. +000EF6 E-TEN Information Systems Co., Ltd. +000EF7 Vulcan Portals Inc +000EF8 SBC ASI +000EF9 REA Elektronik GmbH +000EFA Optoway Technology Incorporation +000EFB Macey Enterprises +000EFC JTAG Technologies B.V. +000EFD FUJI PHOTO OPTICAL CO., LTD. +000EFE EndRun Technologies LLC +000EFF Megasolution,Inc. +000F00 Legra Systems, Inc. +000F01 DIGITALKS INC +000F02 Digicube Technology Co., Ltd +000F03 COM&C CO., LTD +000F04 cim-usa inc +000F05 3B SYSTEM INC. +000F06 Nortel Networks +000F07 Mangrove Systems, Inc. +000F08 Indagon Oy +000F0B Kentima Technologies AB +000F0C SYNCHRONIC ENGINEERING +000F0D Hunt Electronic Co., Ltd. +000F0E WaveSplitter Technologies, Inc. +000F0F Real ID Technology Co., Ltd. +000F10 RDM Corporation +000F11 Prodrive B.V. +000F12 Panasonic AVC Networks Germany GmbH +000F13 Nisca corporation +000F14 Mindray Co., Ltd. +000F15 Kjaerulff1 A/S +000F16 JAY HOW TECHNOLOGY CO., +000F17 Insta Elektro GmbH +000F18 Industrial Control Systems +000F19 Guidant Corporation +000F1A Gaming Support B.V. +000F1B Ego Systems Inc. +000F1C DigitAll World Co., Ltd +000F1D Cosmo Techs Co., Ltd. +000F1E Chengdu KT Electric Co.of High & New Technology +000F1F WW PCBA Test +000F20 WW Ops +000F21 Scientific Atlanta, Inc +000F22 Helius, Inc. +000F23 Cisco Systems +000F24 Cisco Systems +000F25 AimValley B.V. +000F26 WorldAccxx LLC +000F27 TEAL Electronics, Inc. +000F28 Itronix Corporation +000F29 Augmentix Corporation +000F2A Cableware Electronics +000F2B GREENBELL SYSTEMS +000F2C Uplogix, Inc. +001000 CABLE TELEVISION LABORATORIES, INC. +001001 MCK COMMUNICATIONS +001002 ACTIA +001003 IMATRON, INC. +001004 THE BRANTLEY COILE COMPANY,INC +001005 UEC COMMERCIAL +001006 Thales Contact Solutions Ltd. +001007 CISCO SYSTEMS, INC. +001008 VIENNA SYSTEMS CORPORATION +001009 HORO QUARTZ +00100A WILLIAMS COMMUNICATIONS GROUP +00100B CISCO SYSTEMS, INC. +00100C ITO CO., LTD. +00100D CISCO SYSTEMS, INC. +00100E MICRO LINEAR COPORATION +00100F INDUSTRIAL CPU SYSTEMS +001010 INITIO CORPORATION +001011 CISCO SYSTEMS, INC. +001012 PROCESSOR SYSTEMS (I) PVT LTD +001013 INDUSTRIAL COMPUTER SOURCE +001014 CISCO SYSTEMS, INC. +001015 OOmon Inc. +001016 T.SQWARE +001017 MICOS GmbH +001018 BROADCOM CORPORATION +001019 SIRONA DENTAL SYSTEMS GmbH & Co. KG +00101A PictureTel Corp. +00101B CORNET TECHNOLOGY, INC. +00101C OHM TECHNOLOGIES INTL, LLC +00101D WINBOND ELECTRONICS CORP. +00101E MATSUSHITA ELECTRONIC INSTRUMENTS CORP. +00101F CISCO SYSTEMS, INC. +001020 WELCH ALLYN, DATA COLLECTION +001021 ENCANTO NETWORKS, INC. +001022 SatCom Media Corporation +001023 FLOWWISE NETWORKS, INC. +001024 NAGOYA ELECTRIC WORKS CO., LTD +001025 GRAYHILL INC. +001026 ACCELERATED NETWORKS, INC. +001027 L-3 COMMUNICATIONS EAST +001028 COMPUTER TECHNICA, INC. +001029 CISCO SYSTEMS, INC. +00102A ZF MICROSYSTEMS, INC. +00102B UMAX DATA SYSTEMS, INC. +00102C Lasat Networks A/S +00102D HITACHI SOFTWARE ENGINEERING +00102E NETWORK SYSTEMS & TECHNOLOGIES PVT. LTD. +00102F CISCO SYSTEMS, INC. +001030 Wi-LAN, Inc. +001031 OBJECTIVE COMMUNICATIONS, INC. +001032 ALTA TECHNOLOGY +001033 ACCESSLAN COMMUNICATIONS, INC. +001034 GNP Computers +001035 ELITEGROUP COMPUTER SYSTEMS CO., LTD +001036 INTER-TEL INTEGRATED SYSTEMS +001037 CYQ've Technology Co., Ltd. +001038 MICRO RESEARCH INSTITUTE, INC. +001039 Vectron Systems AG +00103A DIAMOND NETWORK TECH +00103B HIPPI NETWORKING FORUM +00103C IC ENSEMBLE, INC. +00103D PHASECOM, LTD. +00103E NETSCHOOLS CORPORATION +00103F TOLLGRADE COMMUNICATIONS, INC. +001040 INTERMEC CORPORATION +001041 BRISTOL BABCOCK, INC. +001042 AlacriTech +001043 A2 CORPORATION +001044 InnoLabs Corporation +001045 Nortel Networks +001046 ALCORN MCBRIDE INC. +001047 ECHO ELETRIC CO. LTD. +001048 HTRC AUTOMATION, INC. +001049 SHORELINE TELEWORKS, INC. +00104A THE PARVUC CORPORATION +00104B 3COM CORPORATION +00104C COMPUTER ACCESS TECHNOLOGY +00104D SURTEC INDUSTRIES, INC. +00104E CEOLOGIC +00104F STORAGE TECHNOLOGY CORPORATION +001050 RION CO., LTD. +001051 CMICRO CORPORATION +001052 METTLER-TOLEDO (ALBSTADT) GMBH +001053 COMPUTER TECHNOLOGY CORP. +001054 CISCO SYSTEMS, INC. +001055 FUJITSU MICROELECTRONICS, INC. +001056 SODICK CO., LTD. +001057 Rebel.com, Inc. +001058 ArrowPoint Communications +001059 DIABLO RESEARCH CO. LLC +00105A 3COM CORPORATION +00105B NET INSIGHT AB +00105C QUANTUM DESIGNS (H.K.) LTD. +00105D Draeger Medical +00105E HEKIMIAN LABORATORIES, INC. +00105F IN-SNEC +001060 BILLIONTON SYSTEMS, INC. +001061 HOSTLINK CORP. +001062 NX SERVER, ILNC. +001063 STARGUIDE DIGITAL NETWORKS +001064 DIGITAL EQUIPMENT CORP. +001065 RADYNE CORPORATION +001066 ADVANCED CONTROL SYSTEMS, INC. +001067 REDBACK NETWORKS, INC. +001068 COMOS TELECOM +001069 HELIOSS COMMUNICATIONS, INC. +00106A DIGITAL MICROWAVE CORPORATION +00106B SONUS NETWORKS, INC. +00106C INFRATEC PLUS GmbH +00106D INTEGRITY COMMUNICATIONS, INC. +00106E TADIRAN COM. LTD. +00106F TRENTON TECHNOLOGY INC. +001070 CARADON TREND LTD. +001071 ADVANET INC. +001072 GVN TECHNOLOGIES, INC. +001073 TECHNOBOX, INC. +001074 ATEN INTERNATIONAL CO., LTD. +001075 Maxtor Corporation +001076 EUREM GmbH +001077 SAF DRIVE SYSTEMS, LTD. +001078 NUERA COMMUNICATIONS, INC. +001079 CISCO SYSTEMS, INC. +00107A AmbiCom, Inc. +00107B CISCO SYSTEMS, INC. +00107C P-COM, INC. +00107D AURORA COMMUNICATIONS, LTD. +00107E BACHMANN ELECTRONIC GmbH +00107F CRESTRON ELECTRONICS, INC. +001080 METAWAVE COMMUNICATIONS +001081 DPS, INC. +001082 JNA TELECOMMUNICATIONS LIMITED +001083 HEWLETT-PACKARD COMPANY +001084 K-BOT COMMUNICATIONS +001085 POLARIS COMMUNICATIONS, INC. +001086 ATTO TECHNOLOGY, INC. +001087 Xstreamis PLC +001088 AMERICAN NETWORKS INC. +001089 WebSonic +00108A TeraLogic, Inc. +00108B LASERANIMATION SOLLINGER GmbH +00108C FUJITSU TELECOMMUNICATIONS EUROPE, LTD. +00108D JOHNSON CONTROLS, INC. +00108E HUGH SYMONS CONCEPT Technologies Ltd. +00108F RAPTOR SYSTEMS +001090 CIMETRICS, INC. +001091 NO WIRES NEEDED BV +001092 NETCORE INC. +001093 CMS COMPUTERS, LTD. +001094 Performance Analysis Broadband, Spirent plc +001095 Thomson Multimedia, Inc. +001096 TRACEWELL SYSTEMS, INC. +001097 WinNet Metropolitan Communications Systems, Inc. +001098 STARNET TECHNOLOGIES, INC. +001099 InnoMedia, Inc. +00109A NETLINE +00109B VIXEL CORPORATION +00109C M-SYSTEM CO., LTD. +00109D CLARINET SYSTEMS, INC. +00109E AWARE, INC. +00109F PAVO, INC. +0010A0 INNOVEX TECHNOLOGIES, INC. +0010A1 KENDIN SEMICONDUCTOR, INC. +0010A2 TNS +0010A3 OMNITRONIX, INC. +0010A4 XIRCOM +0010A5 OXFORD INSTRUMENTS +0010A6 CISCO SYSTEMS, INC. +0010A7 UNEX TECHNOLOGY CORPORATION +0010A8 RELIANCE COMPUTER CORP. +0010A9 ADHOC TECHNOLOGIES +0010AA MEDIA4, INC. +0010AB KOITO INDUSTRIES, LTD. +0010AC IMCI TECHNOLOGIES +0010AD SOFTRONICS USB, INC. +0010AE SHINKO ELECTRIC INDUSTRIES CO. +0010AF TAC SYSTEMS, INC. +0010B0 MERIDIAN TECHNOLOGY CORP. +0010B1 FOR-A CO., LTD. +0010B2 COACTIVE AESTHETICS +0010B3 NOKIA MULTIMEDIA TERMINALS +0010B4 ATMOSPHERE NETWORKS +0010B5 ACCTON TECHNOLOGY CORPORATION +0010B6 ENTRATA COMMUNICATIONS CORP. +0010B7 COYOTE TECHNOLOGIES, LLC +0010B8 ISHIGAKI COMPUTER SYSTEM CO. +0010B9 MAXTOR CORP. +0010BA MARTINHO-DAVIS SYSTEMS, INC. +0010BB DATA & INFORMATION TECHNOLOGY +0010BC Aastra Telecom +0010BD THE TELECOMMUNICATION TECHNOLOGY COMMITTEE +0010BE TELEXIS CORP. +0010BF InterAir Wireless +0010C0 ARMA, INC. +0010C1 OI ELECTRIC CO., LTD. +0010C2 WILLNET, INC. +0010C3 CSI-CONTROL SYSTEMS +0010C4 MEDIA LINKS CO., LTD. +0010C5 PROTOCOL TECHNOLOGIES, INC. +0010C6 USI +0010C7 DATA TRANSMISSION NETWORK +0010C8 COMMUNICATIONS ELECTRONICS SECURITY GROUP +0010C9 MITSUBISHI ELECTRONICS LOGISTIC SUPPORT CO. +0010CA INTEGRAL ACCESS +0010CB FACIT K.K. +0010CC CLP COMPUTER LOGISTIK PLANUNG GmbH +0010CD INTERFACE CONCEPT +0010CE VOLAMP, LTD. +0010CF FIBERLANE COMMUNICATIONS +0010D0 WITCOM, LTD. +0010D1 Top Layer Networks, Inc. +0010D2 NITTO TSUSHINKI CO., LTD +0010D3 GRIPS ELECTRONIC GMBH +0010D4 STORAGE COMPUTER CORPORATION +0010D5 IMASDE CANARIAS, S.A. +0010D6 ITT - A/CD +0010D7 ARGOSY RESEARCH INC. +0010D8 CALISTA +0010D9 IBM JAPAN, FUJISAWA MT+D +0010DA MOTION ENGINEERING, INC. +0010DB NetScreen Technologies, Inc. +0010DC MICRO-STAR INTERNATIONAL CO., LTD. +0010DD ENABLE SEMICONDUCTOR, INC. +0010DE INTERNATIONAL DATACASTING CORPORATION +0010DF RISE COMPUTER INC. +0010E0 COBALT MICROSERVER, INC. +0010E1 S.I. TECH, INC. +0010E2 ArrayComm, Inc. +0010E3 COMPAQ COMPUTER CORPORATION +0010E4 NSI CORPORATION +0010E5 SOLECTRON TEXAS +0010E6 APPLIED INTELLIGENT SYSTEMS, INC. +0010E7 BreezeCom +0010E8 TELOCITY, INCORPORATED +0010E9 RAIDTEC LTD. +0010EA ADEPT TECHNOLOGY +0010EB SELSIUS SYSTEMS, INC. +0010EC RPCG, LLC +0010ED SUNDANCE TECHNOLOGY, INC. +0010EE CTI PRODUCTS, INC. +0010EF DBTEL INCORPORATED +0010F1 I-O CORPORATION +0010F2 ANTEC +0010F3 Nexcom International Co., Ltd. +0010F4 VERTICAL NETWORKS, INC. +0010F5 AMHERST SYSTEMS, INC. +0010F6 CISCO SYSTEMS, INC. +0010F7 IRIICHI TECHNOLOGIES Inc. +0010F8 KENWOOD TMI CORPORATION +0010F9 UNIQUE SYSTEMS, INC. +0010FA ZAYANTE, INC. +0010FB ZIDA TECHNOLOGIES LIMITED +0010FC BROADBAND NETWORKS, INC. +0010FD COCOM A/S +0010FE DIGITAL EQUIPMENT CORPORATION +0010FF CISCO SYSTEMS, INC. +001C7C PERQ SYSTEMS CORPORATION +002000 LEXMARK INTERNATIONAL, INC. +002001 DSP SOLUTIONS, INC. +002002 SERITECH ENTERPRISE CO., LTD. +002003 PIXEL POWER LTD. +002004 YAMATAKE-HONEYWELL CO., LTD. +002005 SIMPLE TECHNOLOGY +002006 GARRETT COMMUNICATIONS, INC. +002007 SFA, INC. +002008 CABLE & COMPUTER TECHNOLOGY +002009 PACKARD BELL ELEC., INC. +00200A SOURCE-COMM CORP. +00200B OCTAGON SYSTEMS CORP. +00200C ADASTRA SYSTEMS CORP. +00200D CARL ZEISS +00200E SATELLITE TECHNOLOGY MGMT, INC +00200F TANBAC CO., LTD. +002010 JEOL SYSTEM TECHNOLOGY CO. LTD +002011 CANOPUS CO., LTD. +002012 CAMTRONICS MEDICAL SYSTEMS +002013 DIVERSIFIED TECHNOLOGY, INC. +002014 GLOBAL VIEW CO., LTD. +002015 ACTIS COMPUTER SA +002016 SHOWA ELECTRIC WIRE & CABLE CO +002017 ORBOTECH +002018 CIS TECHNOLOGY INC. +002019 OHLER GmbH +00201A N-BASE SWITCH COMMUNICATIONS +00201B NORTHERN TELECOM/NETWORK +00201C EXCEL, INC. +00201D KATANA PRODUCTS +00201E NETQUEST CORPORATION +00201F BEST POWER TECHNOLOGY, INC. +002020 MEGATRON COMPUTER INDUSTRIES PTY, LTD. +002021 ALGORITHMS SOFTWARE PVT. LTD. +002022 TEKNIQUE, INC. +002023 T.C. TECHNOLOGIES PTY. LTD +002024 PACIFIC COMMUNICATION SCIENCES +002025 CONTROL TECHNOLOGY, INC. +002026 AMKLY SYSTEMS, INC. +002027 MING FORTUNE INDUSTRY CO., LTD +002028 WEST EGG SYSTEMS, INC. +002029 TELEPROCESSING PRODUCTS, INC. +00202A N.V. DZINE +00202B ADVANCED TELECOMMUNICATIONS MODULES, LTD. +00202C WELLTRONIX CO., LTD. +00202D TAIYO CORPORATION +00202E DAYSTAR DIGITAL +00202F ZETA COMMUNICATIONS, LTD. +002030 ANALOG & DIGITAL SYSTEMS +002031 ERTEC GmbH +002032 ALCATEL TAISEL +002033 SYNAPSE TECHNOLOGIES, INC. +002034 ROTEC INDUSTRIEAUTOMATION GMBH +002035 IBM CORPORATION +002036 BMC SOFTWARE +002037 SEAGATE TECHNOLOGY +002038 VME MICROSYSTEMS INTERNATIONAL CORPORATION +002039 SCINETS +00203A DIGITAL BI0METRICS INC. +00203B WISDM LTD. +00203C EUROTIME AB +00203D NOVAR ELECTRONICS CORPORATION +00203E LogiCan Technologies, Inc. +00203F JUKI CORPORATION +002040 Motorola Broadband Communications Sector +002041 DATA NET +002042 DATAMETRICS CORP. +002043 NEURON COMPANY LIMITED +002044 GENITECH PTY LTD +002045 ION Networks, Inc. +002046 CIPRICO, INC. +002047 STEINBRECHER CORP. +002048 Marconi Communications +002049 COMTRON, INC. +00204A PRONET GMBH +00204B AUTOCOMPUTER CO., LTD. +00204C MITRON COMPUTER PTE LTD. +00204D INOVIS GMBH +00204E NETWORK SECURITY SYSTEMS, INC. +00204F DEUTSCHE AEROSPACE AG +002050 KOREA COMPUTER INC. +002051 Verilink Corporation +002052 RAGULA SYSTEMS +002053 HUNTSVILLE MICROSYSTEMS, INC. +002054 EASTERN RESEARCH, INC. +002055 ALTECH CO., LTD. +002056 NEOPRODUCTS +002057 TITZE DATENTECHNIK GmbH +002058 ALLIED SIGNAL INC. +002059 MIRO COMPUTER PRODUCTS AG +00205A COMPUTER IDENTICS +00205B SKYLINE TECHNOLOGY +00205C InterNet Systems of Florida, Inc. +00205D NANOMATIC OY +00205E CASTLE ROCK, INC. +00205F GAMMADATA COMPUTER GMBH +002060 ALCATEL ITALIA S.p.A. +002061 DYNATECH COMMUNICATIONS, INC. +002062 SCORPION LOGIC, LTD. +002063 WIPRO INFOTECH LTD. +002064 PROTEC MICROSYSTEMS, INC. +002065 SUPERNET NETWORKING INC. +002066 GENERAL MAGIC, INC. +002068 ISDYNE +002069 ISDN SYSTEMS CORPORATION +00206A OSAKA COMPUTER CORP. +00206B KONICA MINOLTA HOLDINGS, INC. +00206C EVERGREEN TECHNOLOGY CORP. +00206D DATA RACE, INC. +00206E XACT, INC. +00206F FLOWPOINT CORPORATION +002070 HYNET, LTD. +002071 IBR GMBH +002072 WORKLINK INNOVATIONS +002073 FUSION SYSTEMS CORPORATION +002074 SUNGWOON SYSTEMS +002075 MOTOROLA COMMUNICATION ISRAEL +002076 REUDO CORPORATION +002077 KARDIOS SYSTEMS CORP. +002078 RUNTOP, INC. +002079 MIKRON GMBH +00207A WiSE Communications, Inc. +00207B Intel Corporation +00207C AUTEC GmbH +00207D ADVANCED COMPUTER APPLICATIONS +00207E FINECOM Co., Ltd. +00207F KYOEI SANGYO CO., LTD. +002080 SYNERGY (UK) LTD. +002081 TITAN ELECTRONICS +002082 ONEAC CORPORATION +002083 PRESTICOM INCORPORATED +002084 OCE PRINTING SYSTEMS, GMBH +002085 EXIDE ELECTRONICS +002086 MICROTECH ELECTRONICS LIMITED +002087 MEMOTEC COMMUNICATIONS CORP. +002088 GLOBAL VILLAGE COMMUNICATION +002089 T3PLUS NETWORKING, INC. +00208A SONIX COMMUNICATIONS, LTD. +00208B LAPIS TECHNOLOGIES, INC. +00208C GALAXY NETWORKS, INC. +00208D CMD TECHNOLOGY +00208E CHEVIN SOFTWARE ENG. LTD. +00208F ECI TELECOM LTD. +002090 ADVANCED COMPRESSION TECHNOLOGY, INC. +002091 J125, NATIONAL SECURITY AGENCY +002092 CHESS ENGINEERING B.V. +002093 LANDINGS TECHNOLOGY CORP. +002094 CUBIX CORPORATION +002095 RIVA ELECTRONICS +002096 Invensys +002097 APPLIED SIGNAL TECHNOLOGY +002098 HECTRONIC AB +002099 BON ELECTRIC CO., LTD. +00209A THE 3DO COMPANY +00209B ERSAT ELECTRONIC GMBH +00209C PRIMARY ACCESS CORP. +00209D LIPPERT AUTOMATIONSTECHNIK +00209E BROWN'S OPERATING SYSTEM SERVICES, LTD. +00209F MERCURY COMPUTER SYSTEMS, INC. +0020A0 OA LABORATORY CO., LTD. +0020A1 DOVATRON +0020A2 GALCOM NETWORKING LTD. +0020A3 DIVICOM INC. +0020A4 MULTIPOINT NETWORKS +0020A5 API ENGINEERING +0020A6 PROXIM, INC. +0020A7 PAIRGAIN TECHNOLOGIES, INC. +0020A8 SAST TECHNOLOGY CORP. +0020A9 WHITE HORSE INDUSTRIAL +0020AA DIGIMEDIA VISION LTD. +0020AB MICRO INDUSTRIES CORP. +0020AC INTERFLEX DATENSYSTEME GMBH +0020AD LINQ SYSTEMS +0020AE ORNET DATA COMMUNICATION TECH. +0020AF 3COM CORPORATION +0020B0 GATEWAY DEVICES, INC. +0020B1 COMTECH RESEARCH INC. +0020B2 GKD Gesellschaft Fur Kommunikation Und Datentechnik +0020B3 SCLTEC COMMUNICATIONS SYSTEMS +0020B4 TERMA ELEKTRONIK AS +0020B5 YASKAWA ELECTRIC CORPORATION +0020B6 AGILE NETWORKS, INC. +0020B7 NAMAQUA COMPUTERWARE +0020B8 PRIME OPTION, INC. +0020B9 METRICOM, INC. +0020BA CENTER FOR HIGH PERFORMANCE +0020BB ZAX CORPORATION +0020BC JTEC PTY LTD. +0020BD NIOBRARA R & D CORPORATION +0020BE LAN ACCESS CORP. +0020BF AEHR TEST SYSTEMS +0020C0 PULSE ELECTRONICS, INC. +0020C1 TAIKO ELECTRIC WORKS, LTD. +0020C2 TEXAS MEMORY SYSTEMS, INC. +0020C3 COUNTER SOLUTIONS LTD. +0020C4 INET,INC. +0020C5 EAGLE TECHNOLOGY +0020C6 NECTEC +0020C7 AKAI Professional M.I. Corp. +0020C8 LARSCOM INCORPORATED +0020C9 VICTRON BV +0020CA DIGITAL OCEAN +0020CB PRETEC ELECTRONICS CORP. +0020CC DIGITAL SERVICES, LTD. +0020CD HYBRID NETWORKS, INC. +0020CE LOGICAL DESIGN GROUP, INC. +0020CF TEST & MEASUREMENT SYSTEMS INC +0020D0 VERSALYNX CORPORATION +0020D1 MICROCOMPUTER SYSTEMS (M) SDN. +0020D2 RAD DATA COMMUNICATIONS, LTD. +0020D3 OST (OUEST STANDARD TELEMATIQU +0020D4 CABLETRON - ZEITTNET INC. +0020D5 VIPA GMBH +0020D6 BREEZECOM +0020D7 JAPAN MINICOMPUTER SYSTEMS CO., Ltd. +0020D8 Nortel Networks +0020D9 PANASONIC TECHNOLOGIES, INC./MIECO-US +0020DA XYLAN CORPORATION +0020DB XNET TECHNOLOGY, INC. +0020DC DENSITRON TAIWAN LTD. +0020DD Cybertec Pty Ltd +0020DE JAPAN DIGITAL LABORAT'Y CO.LTD +0020DF KYOSAN ELECTRIC MFG. CO., LTD. +0020E0 PREMAX ELECTRONICS, INC. +0020E1 ALAMAR ELECTRONICS +0020E2 INFORMATION RESOURCE ENGINEERING +0020E3 MCD KENCOM CORPORATION +0020E4 HSING TECH ENTERPRISE CO., LTD +0020E5 APEX DATA, INC. +0020E6 LIDKOPING MACHINE TOOLS AB +0020E7 B&W NUCLEAR SERVICE COMPANY +0020E8 DATATREK CORPORATION +0020E9 DANTEL +0020EA EFFICIENT NETWORKS, INC. +0020EB CINCINNATI MICROWAVE, INC. +0020EC TECHWARE SYSTEMS CORP. +0020ED GIGA-BYTE TECHNOLOGY CO., LTD. +0020EE GTECH CORPORATION +0020EF USC CORPORATION +0020F0 UNIVERSAL MICROELECTRONICS CO. +0020F1 ALTOS INDIA LIMITED +0020F2 SUN MICROSYSTEMS, INC. +0020F3 RAYNET CORPORATION +0020F4 SPECTRIX CORPORATION +0020F5 PANDATEL AG +0020F6 NET TEK AND KARLNET, INC. +0020F7 CYBERDATA +0020F8 CARRERA COMPUTERS, INC. +0020F9 PARALINK NETWORKS, INC. +0020FA GDE SYSTEMS, INC. +0020FB OCTEL COMMUNICATIONS CORP. +0020FC MATROX +0020FD ITV TECHNOLOGIES, INC. +0020FE TOPWARE INC. / GRAND COMPUTER +0020FF SYMMETRICAL TECHNOLOGIES +003000 ALLWELL TECHNOLOGY CORP. +003001 SMP +003002 Expand Networks +003003 Phasys Ltd. +003004 LEADTEK RESEARCH INC. +003005 Fujitsu Siemens Computers +003006 SUPERPOWER COMPUTER +003007 OPTI, INC. +003008 AVIO DIGITAL, INC. +003009 Tachion Networks, Inc. +00300A AZTECH SYSTEMS LTD. +00300B mPHASE Technologies, Inc. +00300C CONGRUENCY, LTD. +00300D MMC Technology, Inc. +00300E Klotz Digital AG +00300F IMT - Information Management T +003010 VISIONETICS INTERNATIONAL +003011 HMS FIELDBUS SYSTEMS AB +003012 DIGITAL ENGINEERING LTD. +003013 NEC Corporation +003014 DIVIO, INC. +003015 CP CLARE CORP. +003016 ISHIDA CO., LTD. +003017 TERASTACK LTD. +003018 Jetway Information Co., Ltd. +003019 CISCO SYSTEMS, INC. +00301A SMARTBRIDGES PTE. LTD. +00301B SHUTTLE, INC. +00301C ALTVATER AIRDATA SYSTEMS +00301D SKYSTREAM, INC. +00301E 3COM Europe Ltd. +00301F OPTICAL NETWORKS, INC. +003020 TSI, Inc.. +003021 HSING TECH. ENTERPRISE CO.,LTD +003022 Fong Kai Industrial Co., Ltd. +003023 COGENT COMPUTER SYSTEMS, INC. +003024 CISCO SYSTEMS, INC. +003025 CHECKOUT COMPUTER SYSTEMS, LTD +003026 HEITEL +003027 KERBANGO, INC. +003028 FASE Saldatura srl +003029 OPICOM +00302A SOUTHERN INFORMATION +00302B INALP NETWORKS, INC. +00302C SYLANTRO SYSTEMS CORPORATION +00302D QUANTUM BRIDGE COMMUNICATIONS +00302E Hoft & Wessel AG +00302F Smiths Industries +003030 HARMONIX CORPORATION +003031 LIGHTWAVE COMMUNICATIONS, INC. +003032 MagicRam, Inc. +003033 ORIENT TELECOM CO., LTD. +003036 RMP ELEKTRONIKSYSTEME GMBH +003037 Packard Bell Nec Services +003038 XCP, INC. +003039 SOFTBOOK PRESS +00303A MAATEL +00303B PowerCom Technology +00303C ONNTO CORP. +00303D IVA CORPORATION +00303E Radcom Ltd. +00303F TurboComm Tech Inc. +003040 CISCO SYSTEMS, INC. +003041 SAEJIN T & M CO., LTD. +003042 DeTeWe-Deutsche Telephonwerke +003043 IDREAM TECHNOLOGIES, PTE. LTD. +003044 Portsmith LLC +003045 Village Networks, Inc. (VNI) +003046 Controlled Electronic Manageme +003047 NISSEI ELECTRIC CO., LTD. +003048 Supermicro Computer, Inc. +003049 BRYANT TECHNOLOGY, LTD. +00304A FRAUNHOFER INSTITUTE IMS +00304B ORBACOM SYSTEMS, INC. +00304C APPIAN COMMUNICATIONS, INC. +00304D ESI +00304E BUSTEC PRODUCTION LTD. +00304F PLANET Technology Corporation +003050 Versa Technology +003051 ORBIT AVIONIC & COMMUNICATION +003052 ELASTIC NETWORKS +003053 Basler AG +003054 CASTLENET TECHNOLOGY, INC. +003055 Hitachi Semiconductor America, +003056 Beck IPC GmbH +003057 E-Tel Corporation +003058 API MOTION +003059 DIGITAL-LOGIC AG +00305A TELGEN CORPORATION +00305B MODULE DEPARTMENT +00305C SMAR Laboratories Corp. +00305D DIGITRA SYSTEMS, INC. +00305E Abelko Innovation +00305F IMACON APS +003060 STARMATIX, INC. +003061 MobyTEL +003062 PATH 1 NETWORK TECHNOL'S INC. +003063 SANTERA SYSTEMS, INC. +003064 ADLINK TECHNOLOGY, INC. +003065 APPLE COMPUTER, INC. +003066 DIGITAL WIRELESS CORPORATION +003067 BIOSTAR MICROTECH INT'L CORP. +003068 CYBERNETICS TECH. CO., LTD. +003069 IMPACCT TECHNOLOGY CORP. +00306A PENTA MEDIA CO., LTD. +00306B CMOS SYSTEMS, INC. +00306C Hitex Holding GmbH +00306D LUCENT TECHNOLOGIES +00306E HEWLETT PACKARD +00306F SEYEON TECH. CO., LTD. +003070 1Net Corporation +003071 Cisco Systems, Inc. +003072 INTELLIBYTE INC. +003073 International Microsystems, In +003074 EQUIINET LTD. +003075 ADTECH +003076 Akamba Corporation +003077 ONPREM NETWORKS +003078 Cisco Systems, Inc. +003079 CQOS, INC. +00307A Advanced Technology & Systems +00307B Cisco Systems, Inc. +00307C ADID SA +00307D GRE AMERICA, INC. +00307E Redflex Communication Systems +00307F IRLAN LTD. +003080 CISCO SYSTEMS, INC. +003081 ALTOS C&C +003082 TAIHAN ELECTRIC WIRE CO., LTD. +003083 Ivron Systems +003084 ALLIED TELESYN INTERNAIONAL +003085 CISCO SYSTEMS, INC. +003086 Transistor Devices, Inc. +003087 VEGA GRIESHABER KG +003088 Siara Systems, Inc. +003089 Spectrapoint Wireless, LLC +00308A NICOTRA SISTEMI S.P.A +00308B Brix Networks +00308C ADVANCED DIGITAL INFORMATION +00308D PINNACLE SYSTEMS, INC. +00308E CROSS MATCH TECHNOLOGIES, INC. +00308F MICRILOR, Inc. +003090 CYRA TECHNOLOGIES, INC. +003091 TAIWAN FIRST LINE ELEC. CORP. +003092 ModuNORM GmbH +003093 SONNET TECHNOLOGIES, INC. +003094 Cisco Systems, Inc. +003095 Procomp Informatics, Ltd. +003096 CISCO SYSTEMS, INC. +003097 EXOMATIC AB +003098 Global Converging Technologies +003099 BOENIG UND KALLENBACH OHG +00309A ASTRO TERRA CORP. +00309B Smartware +00309C Timing Applications, Inc. +00309D Nimble Microsystems, Inc. +00309E WORKBIT CORPORATION. +00309F AMBER NETWORKS +0030A0 TYCO SUBMARINE SYSTEMS, LTD. +0030A1 WEBGATE Inc. +0030A2 Lightner Engineering +0030A3 CISCO SYSTEMS, INC. +0030A4 Woodwind Communications System +0030A5 ACTIVE POWER +0030A6 VIANET TECHNOLOGIES, LTD. +0030A7 SCHWEITZER ENGINEERING +0030A8 OL'E COMMUNICATIONS, INC. +0030A9 Netiverse, Inc. +0030AA AXUS MICROSYSTEMS, INC. +0030AB DELTA NETWORKS, INC. +0030AC Systeme Lauer GmbH & Co., Ltd. +0030AD SHANGHAI COMMUNICATION +0030AE Times N System, Inc. +0030AF Honeywell GmbH +0030B0 Convergenet Technologies +0030B1 GOC GESELLSCHAFT FUR OPTISCHE +0030B2 WESCAM - HEALDSBURG +0030B3 San Valley Systems, Inc. +0030B4 INTERSIL CORP. +0030B5 Tadiran Microwave Networks +0030B6 CISCO SYSTEMS, INC. +0030B7 Teletrol Systems, Inc. +0030B8 RiverDelta Networks +0030B9 ECTEL +0030BA AC&T SYSTEM CO., LTD. +0030BB CacheFlow, Inc. +0030BC Optronic AG +0030BD BELKIN COMPONENTS +0030BE City-Net Technology, Inc. +0030BF MULTIDATA GMBH +0030C0 Lara Technology, Inc. +0030C1 HEWLETT-PACKARD +0030C2 COMONE +0030C3 FLUECKIGER ELEKTRONIK AG +0030C4 Niigata Canotec Co., Inc. +0030C5 CADENCE DESIGN SYSTEMS +0030C6 CONTROL SOLUTIONS, INC. +0030C7 MACROMATE CORP. +0030C8 GAD LINE, LTD. +0030C9 LuxN, N +0030CA Discovery Com +0030CB OMNI FLOW COMPUTERS, INC. +0030CC Tenor Networks, Inc. +0030CD CONEXANT SYSTEMS, INC. +0030CE Zaffire +0030CF TWO TECHNOLOGIES, INC. +0030D1 INOVA CORPORATION +0030D2 WIN TECHNOLOGIES, CO., LTD. +0030D3 Agilent Technologies +0030D4 COMTIER +0030D5 DResearch GmbH +0030D6 MSC VERTRIEBS GMBH +0030D7 Innovative Systems, L.L.C. +0030D8 SITEK +0030D9 DATACORE SOFTWARE CORP. +0030DA COMTREND CO. +0030DB Mindready Solutions, Inc. +0030DC RIGHTECH CORPORATION +0030DD INDIGITA CORPORATION +0030DE WAGO Kontakttechnik GmbH +0030DF KB/TEL TELECOMUNICACIONES +0030E0 OXFORD SEMICONDUCTOR LTD. +0030E1 ACROTRON SYSTEMS, INC. +0030E2 GARNET SYSTEMS CO., LTD. +0030E3 SEDONA NETWORKS CORP. +0030E4 CHIYODA SYSTEM RIKEN +0030E5 Amper Datos S.A. +0030E6 SIEMENS MEDICAL SYSTEMS +0030E7 CNF MOBILE SOLUTIONS, INC. +0030E8 ENSIM CORP. +0030E9 GMA COMMUNICATION MANUFACT'G +0030EA TeraForce Technology Corporation +0030EB TURBONET COMMUNICATIONS, INC. +0030EC BORGARDT +0030ED Expert Magnetics Corp. +0030EE DSG Technology, Inc. +0030EF NEON TECHNOLOGY, INC. +0030F0 Uniform Industrial Corp. +0030F1 Accton Technology Corp. +0030F2 CISCO SYSTEMS, INC. +0030F3 At Work Computers +0030F4 STARDOT TECHNOLOGIES +0030F5 Wild Lab. Ltd. +0030F6 SECURELOGIX CORPORATION +0030F7 RAMIX INC. +0030F8 Dynapro Systems, Inc. +0030F9 Sollae Systems Co., Ltd. +0030FA TELICA, INC. +0030FB AZS Technology AG +0030FC Terawave Communications, Inc. +0030FD INTEGRATED SYSTEMS DESIGN +0030FE DSA GmbH +0030FF DATAFAB SYSTEMS, INC. +004000 PCI COMPONENTES DA AMZONIA LTD +004001 ZYXEL COMMUNICATIONS, INC. +004002 PERLE SYSTEMS LIMITED +004003 WESTINGHOUSE PROCESS CONTROL +004004 ICM CO. LTD. +004005 ANI COMMUNICATIONS INC. +004006 SAMPO TECHNOLOGY CORPORATION +004007 TELMAT INFORMATIQUE +004008 A PLUS INFO CORPORATION +004009 TACHIBANA TECTRON CO., LTD. +00400A PIVOTAL TECHNOLOGIES, INC. +00400B CISCO SYSTEMS, INC. +00400C GENERAL MICRO SYSTEMS, INC. +00400D LANNET DATA COMMUNICATIONS,LTD +00400E MEMOTEC COMMUNICATIONS, INC. +00400F DATACOM TECHNOLOGIES +004010 SONIC SYSTEMS, INC. +004011 ANDOVER CONTROLS CORPORATION +004012 WINDATA, INC. +004013 NTT DATA COMM. SYSTEMS CORP. +004014 COMSOFT GMBH +004015 ASCOM INFRASYS AG +004016 HADAX ELECTRONICS, INC. +004017 XCD INC. +004018 ADOBE SYSTEMS, INC. +004019 AEON SYSTEMS, INC. +00401A FUJI ELECTRIC CO., LTD. +00401B PRINTER SYSTEMS CORP. +00401C AST RESEARCH, INC. +00401D INVISIBLE SOFTWARE, INC. +00401E ICC +00401F COLORGRAPH LTD +004020 PINACL COMMUNICATION +004021 RASTER GRAPHICS +004022 KLEVER COMPUTERS, INC. +004023 LOGIC CORPORATION +004024 COMPAC INC. +004025 MOLECULAR DYNAMICS +004026 MELCO, INC. +004027 SMC MASSACHUSETTS, INC. +004028 NETCOMM LIMITED +004029 COMPEX +00402A CANOGA-PERKINS +00402B TRIGEM COMPUTER, INC. +00402C ISIS DISTRIBUTED SYSTEMS, INC. +00402D HARRIS ADACOM CORPORATION +00402E PRECISION SOFTWARE, INC. +00402F XLNT DESIGNS INC. +004030 GK COMPUTER +004031 KOKUSAI ELECTRIC CO., LTD +004032 DIGITAL COMMUNICATIONS +004033 ADDTRON TECHNOLOGY CO., LTD. +004034 BUSTEK CORPORATION +004035 OPCOM +004036 TRIBE COMPUTER WORKS, INC. +004037 SEA-ILAN, INC. +004038 TALENT ELECTRIC INCORPORATED +004039 OPTEC DAIICHI DENKO CO., LTD. +00403A IMPACT TECHNOLOGIES +00403B SYNERJET INTERNATIONAL CORP. +00403C FORKS, INC. +00403D TERADATA +00403E RASTER OPS CORPORATION +00403F SSANGYONG COMPUTER SYSTEMS +004040 RING ACCESS, INC. +004041 FUJIKURA LTD. +004042 N.A.T. GMBH +004043 NOKIA TELECOMMUNICATIONS +004044 QNIX COMPUTER CO., LTD. +004045 TWINHEAD CORPORATION +004046 UDC RESEARCH LIMITED +004047 WIND RIVER SYSTEMS +004048 SMD INFORMATICA S.A. +004049 TEGIMENTA AG +00404A WEST AUSTRALIAN DEPARTMENT +00404B MAPLE COMPUTER SYSTEMS +00404C HYPERTEC PTY LTD. +00404D TELECOMMUNICATIONS TECHNIQUES +00404E FLUENT, INC. +00404F SPACE & NAVAL WARFARE SYSTEMS +004050 IRONICS, INCORPORATED +004051 GRACILIS, INC. +004052 STAR TECHNOLOGIES, INC. +004053 AMPRO COMPUTERS +004054 CONNECTION MACHINES SERVICES +004055 METRONIX GMBH +004056 MCM JAPAN LTD. +004057 LOCKHEED - SANDERS +004058 KRONOS, INC. +004059 YOSHIDA KOGYO K. K. +00405A GOLDSTAR INFORMATION & COMM. +00405B FUNASSET LIMITED +00405C FUTURE SYSTEMS, INC. +00405D STAR-TEK, INC. +00405E NORTH HILLS ISRAEL +00405F AFE COMPUTERS LTD. +004060 COMENDEC LTD +004061 DATATECH ENTERPRISES CO., LTD. +004062 E-SYSTEMS, INC./GARLAND DIV. +004063 VIA TECHNOLOGIES, INC. +004064 KLA INSTRUMENTS CORPORATION +004065 GTE SPACENET +004066 HITACHI CABLE, LTD. +004067 OMNIBYTE CORPORATION +004068 EXTENDED SYSTEMS +004069 LEMCOM SYSTEMS, INC. +00406A KENTEK INFORMATION SYSTEMS,INC +00406B SYSGEN +00406C COPERNIQUE +00406D LANCO, INC. +00406E COROLLARY, INC. +00406F SYNC RESEARCH INC. +004070 INTERWARE CO., LTD. +004071 ATM COMPUTER GMBH +004072 Applied Innovation Inc. +004073 BASS ASSOCIATES +004074 CABLE AND WIRELESS +004075 M-TRADE (UK) LTD +004076 Sun Conversion Technologies +004077 MAXTON TECHNOLOGY CORPORATION +004078 WEARNES AUTOMATION PTE LTD +004079 JUKO MANUFACTURE COMPANY, LTD. +00407A SOCIETE D'EXPLOITATION DU CNIT +00407B SCIENTIFIC ATLANTA +00407C QUME CORPORATION +00407D EXTENSION TECHNOLOGY CORP. +00407E EVERGREEN SYSTEMS, INC. +00407F FLIR Systems +004080 ATHENIX CORPORATION +004081 MANNESMANN SCANGRAPHIC GMBH +004082 LABORATORY EQUIPMENT CORP. +004083 TDA INDUSTRIA DE PRODUTOS +004084 HONEYWELL INC. +004085 SAAB INSTRUMENTS AB +004086 MICHELS & KLEBERHOFF COMPUTER +004087 UBITREX CORPORATION +004088 MOBIUS TECHNOLOGIES, INC. +004089 MEIDENSHA CORPORATION +00408A TPS TELEPROCESSING SYS. GMBH +00408B RAYLAN CORPORATION +00408C AXIS COMMUNICATIONS AB +00408D THE GOODYEAR TIRE & RUBBER CO. +00408E DIGILOG, INC. +00408F WM-DATA MINFO AB +004090 ANSEL COMMUNICATIONS +004091 PROCOMP INDUSTRIA ELETRONICA +004092 ASP COMPUTER PRODUCTS, INC. +004093 PAXDATA NETWORKS LTD. +004094 SHOGRAPHICS, INC. +004095 R.P.T. INTERGROUPS INT'L LTD. +004096 Aironet Wireless Communication +004097 DATEX DIVISION OF +004098 DRESSLER GMBH & CO. +004099 NEWGEN SYSTEMS CORP. +00409A NETWORK EXPRESS, INC. +00409B HAL COMPUTER SYSTEMS INC. +00409C TRANSWARE +00409D DIGIBOARD, INC. +00409E CONCURRENT TECHNOLOGIES LTD. +00409F LANCAST/CASAT TECHNOLOGY, INC. +0040A0 GOLDSTAR CO., LTD. +0040A1 ERGO COMPUTING +0040A2 KINGSTAR TECHNOLOGY INC. +0040A3 MICROUNITY SYSTEMS ENGINEERING +0040A4 ROSE ELECTRONICS +0040A5 CLINICOMP INTL. +0040A6 Cray, Inc. +0040A7 ITAUTEC PHILCO S.A. +0040A8 IMF INTERNATIONAL LTD. +0040A9 DATACOM INC. +0040AA VALMET AUTOMATION INC. +0040AB ROLAND DG CORPORATION +0040AC SUPER WORKSTATION, INC. +0040AD SMA REGELSYSTEME GMBH +0040AE DELTA CONTROLS, INC. +0040AF DIGITAL PRODUCTS, INC. +0040B0 BYTEX CORPORATION, ENGINEERING +0040B1 CODONICS INC. +0040B2 SYSTEMFORSCHUNG +0040B3 PAR MICROSYSTEMS CORPORATION +0040B4 NEXTCOM K.K. +0040B5 VIDEO TECHNOLOGY COMPUTERS LTD +0040B6 COMPUTERM CORPORATION +0040B7 STEALTH COMPUTER SYSTEMS +0040B8 IDEA ASSOCIATES +0040B9 MACQ ELECTRONIQUE SA +0040BA ALLIANT COMPUTER SYSTEMS CORP. +0040BB GOLDSTAR CABLE CO., LTD. +0040BC ALGORITHMICS LTD. +0040BD STARLIGHT NETWORKS, INC. +0040BE BOEING DEFENSE & SPACE +0040BF CHANNEL SYSTEMS INTERN'L INC. +0040C0 VISTA CONTROLS CORPORATION +0040C1 BIZERBA-WERKE WILHEIM KRAUT +0040C2 APPLIED COMPUTING DEVICES +0040C3 FISCHER AND PORTER CO. +0040C4 KINKEI SYSTEM CORPORATION +0040C5 MICOM COMMUNICATIONS INC. +0040C6 FIBERNET RESEARCH, INC. +0040C7 RUBY TECH CORPORATION +0040C8 MILAN TECHNOLOGY CORPORATION +0040C9 NCUBE +0040CA FIRST INTERNAT'L COMPUTER, INC +0040CB LANWAN TECHNOLOGIES +0040CC SILCOM MANUF'G TECHNOLOGY INC. +0040CD TERA MICROSYSTEMS, INC. +0040CE NET-SOURCE, INC. +0040CF STRAWBERRY TREE, INC. +0040D0 MITAC INTERNATIONAL CORP. +0040D1 FUKUDA DENSHI CO., LTD. +0040D2 PAGINE CORPORATION +0040D3 KIMPSION INTERNATIONAL CORP. +0040D4 GAGE TALKER CORP. +0040D5 SARTORIUS AG +0040D6 LOCAMATION B.V. +0040D7 STUDIO GEN INC. +0040D8 OCEAN OFFICE AUTOMATION LTD. +0040D9 AMERICAN MEGATRENDS INC. +0040DA TELSPEC LTD +0040DB ADVANCED TECHNICAL SOLUTIONS +0040DC TRITEC ELECTRONIC GMBH +0040DD HONG TECHNOLOGIES +0040DE ELETTRONICA SAN GIORGIO +0040DF DIGALOG SYSTEMS, INC. +0040E0 ATOMWIDE LTD. +0040E1 MARNER INTERNATIONAL, INC. +0040E2 MESA RIDGE TECHNOLOGIES, INC. +0040E3 QUIN SYSTEMS LTD +0040E4 E-M TECHNOLOGY, INC. +0040E5 SYBUS CORPORATION +0040E6 C.A.E.N. +0040E7 ARNOS INSTRUMENTS & COMPUTER +0040E8 CHARLES RIVER DATA SYSTEMS,INC +0040E9 ACCORD SYSTEMS, INC. +0040EA PLAIN TREE SYSTEMS INC +0040EB MARTIN MARIETTA CORPORATION +0040EC MIKASA SYSTEM ENGINEERING +0040ED NETWORK CONTROLS INT'NATL INC. +0040EE OPTIMEM +0040EF HYPERCOM, INC. +0040F0 MICRO SYSTEMS, INC. +0040F1 CHUO ELECTRONICS CO., LTD. +0040F2 JANICH & KLASS COMPUTERTECHNIK +0040F3 NETCOR +0040F4 CAMEO COMMUNICATIONS, INC. +0040F5 OEM ENGINES +0040F6 KATRON COMPUTERS INC. +0040F7 POLAROID MEDICAL IMAGING SYS. +0040F8 SYSTEMHAUS DISCOM +0040F9 COMBINET +0040FA MICROBOARDS, INC. +0040FB CASCADE COMMUNICATIONS CORP. +0040FC IBR COMPUTER TECHNIK GMBH +0040FD LXE +0040FE SYMPLEX COMMUNICATIONS +0040FF TELEBIT CORPORATION +004252 RLX Technologies +005000 NEXO COMMUNICATIONS, INC. +005001 YAMASHITA SYSTEMS CORP. +005002 OMNISEC AG +005003 GRETAG MACBETH AG +005004 3COM CORPORATION +005006 TAC AB +005007 SIEMENS TELECOMMUNICATION SYSTEMS LIMITED +005008 TIVA MICROCOMPUTER CORP. (TMC) +005009 PHILIPS BROADBAND NETWORKS +00500A IRIS TECHNOLOGIES, INC. +00500B CISCO SYSTEMS, INC. +00500C e-Tek Labs, Inc. +00500D SATORI ELECTORIC CO., LTD. +00500E CHROMATIS NETWORKS, INC. +00500F CISCO SYSTEMS, INC. +005010 NovaNET Learning, Inc. +005012 CBL - GMBH +005013 Chaparral Network Storage +005014 CISCO SYSTEMS, INC. +005015 BRIGHT STAR ENGINEERING +005016 SST/WOODHEAD INDUSTRIES +005017 RSR S.R.L. +005018 ADVANCED MULTIMEDIA INTERNET TECHNOLOGY INC. +005019 SPRING TIDE NETWORKS, INC. +00501A UISIQN +00501B ABL CANADA, INC. +00501C JATOM SYSTEMS, INC. +00501E Miranda Technologies, Inc. +00501F MRG SYSTEMS, LTD. +005020 MEDIASTAR CO., LTD. +005021 EIS INTERNATIONAL, INC. +005022 ZONET TECHNOLOGY, INC. +005023 PG DESIGN ELECTRONICS, INC. +005024 NAVIC SYSTEMS, INC. +005026 COSYSTEMS, INC. +005027 GENICOM CORPORATION +005028 AVAL COMMUNICATIONS +005029 1394 PRINTER WORKING GROUP +00502A CISCO SYSTEMS, INC. +00502B GENRAD LTD. +00502C SOYO COMPUTER, INC. +00502D ACCEL, INC. +00502E CAMBEX CORPORATION +00502F TollBridge Technologies, Inc. +005030 FUTURE PLUS SYSTEMS +005031 AEROFLEX LABORATORIES, INC. +005032 PICAZO COMMUNICATIONS, INC. +005033 MAYAN NETWORKS +005036 NETCAM, LTD. +005037 KOGA ELECTRONICS CO. +005038 DAIN TELECOM CO., LTD. +005039 MARINER NETWORKS +00503A DATONG ELECTRONICS LTD. +00503B MEDIAFIRE CORPORATION +00503C TSINGHUA NOVEL ELECTRONICS +00503E CISCO SYSTEMS, INC. +00503F ANCHOR GAMES +005040 EMWARE, INC. +005041 CTX OPTO ELECTRONIC CORP. +005042 SCI MANUFACTURING SINGAPORE PTE, LTD. +005043 MARVELL SEMICONDUCTOR, INC. +005044 ASACA CORPORATION +005045 RIOWORKS SOLUTIONS, INC. +005046 MENICX INTERNATIONAL CO., LTD. +005048 INFOLIBRIA +005049 ELLACOYA NETWORKS, INC. +00504A ELTECO A.S. +00504B BARCONET N.V. +00504C GALIL MOTION CONTROL, INC. +00504D TOKYO ELECTRON DEVICE LTD. +00504E SIERRA MONITOR CORP. +00504F OLENCOM ELECTRONICS +005050 CISCO SYSTEMS, INC. +005051 IWATSU ELECTRIC CO., LTD. +005052 TIARA NETWORKS, INC. +005053 CISCO SYSTEMS, INC. +005054 CISCO SYSTEMS, INC. +005055 DOMS A/S +005056 VMWare, Inc. +005057 BROADBAND ACCESS SYSTEMS +005058 VEGASTREAM LIMITED +005059 SUITE TECHNOLOGY SYSTEMS NETWORK +00505A NETWORK ALCHEMY, INC. +00505B KAWASAKI LSI U.S.A., INC. +00505C TUNDO CORPORATION +00505E DIGITEK MICROLOGIC S.A. +00505F BRAND INNOVATORS +005060 TANDBERG TELECOM AS +005062 KOUWELL ELECTRONICS CORP. ** +005063 OY COMSEL SYSTEM AB +005064 CAE ELECTRONICS +005065 DENSEI-LAMBAD Co., Ltd. +005066 AtecoM GmbH advanced telecomunication modules +005067 AEROCOMM, INC. +005068 ELECTRONIC INDUSTRIES ASSOCIATION +005069 PixStream Incorporated +00506A EDEVA, INC. +00506B SPX-ATEG +00506C G & L BEIJER ELECTRONICS AB +00506D VIDEOJET SYSTEMS +00506E CORDER ENGINEERING CORPORATION +00506F G-CONNECT +005070 CHAINTECH COMPUTER CO., LTD. +005071 AIWA CO., LTD. +005072 CORVIS CORPORATION +005073 CISCO SYSTEMS, INC. +005074 ADVANCED HI-TECH CORP. +005075 KESTREL SOLUTIONS +005076 IBM +005077 PROLIFIC TECHNOLOGY, INC. +005078 MEGATON HOUSE, LTD. +00507A XPEED, INC. +00507B MERLOT COMMUNICATIONS +00507C VIDEOCON AG +00507D IFP +00507E NEWER TECHNOLOGY +00507F DrayTek Corp. +005080 CISCO SYSTEMS, INC. +005081 MURATA MACHINERY, LTD. +005082 FORESSON CORPORATION +005083 GILBARCO, INC. +005084 ATL PRODUCTS +005086 TELKOM SA, LTD. +005087 TERASAKI ELECTRIC CO., LTD. +005088 AMANO CORPORATION +005089 SAFETY MANAGEMENT SYSTEMS +00508B COMPAQ COMPUTER CORPORATION +00508C RSI SYSTEMS +00508D ABIT COMPUTER CORPORATION +00508E OPTIMATION, INC. +00508F ASITA TECHNOLOGIES INT'L LTD. +005090 DCTRI +005091 NETACCESS, INC. +005092 RIGAKU INDUSTRIAL CORPORATION +005093 BOEING +005094 PACE MICRO TECHNOLOGY PLC +005095 PERACOM NETWORKS +005096 SALIX TECHNOLOGIES, INC. +005097 MMC-EMBEDDED COMPUTERTECHNIK GmbH +005098 GLOBALOOP, LTD. +005099 3COM EUROPE, LTD. +00509A TAG ELECTRONIC SYSTEMS +00509B SWITCHCORE AB +00509C BETA RESEARCH +00509D THE INDUSTREE B.V. +00509E Les Technologies SoftAcoustik Inc. +00509F HORIZON COMPUTER +0050A0 DELTA COMPUTER SYSTEMS, INC. +0050A1 CARLO GAVAZZI, INC. +0050A2 CISCO SYSTEMS, INC. +0050A3 TransMedia Communications, Inc. +0050A4 IO TECH, INC. +0050A5 CAPITOL BUSINESS SYSTEMS, LTD. +0050A6 OPTRONICS +0050A7 CISCO SYSTEMS, INC. +0050A8 OpenCon Systems, Inc. +0050A9 MOLDAT WIRELESS TECHNOLGIES +0050AA KONICA MINOLTA HOLDINGS, INC. +0050AB NALTEC, INC. +0050AC MAPLE COMPUTER CORPORATION +0050AD CommUnique Wireless Corp. +0050AE IWAKI ELECTRONICS CO., LTD. +0050AF INTERGON, INC. +0050B0 TECHNOLOGY ATLANTA CORPORATION +0050B1 GIDDINGS & LEWIS +0050B2 BRODEL AUTOMATION +0050B3 VOICEBOARD CORPORATION +0050B4 SATCHWELL CONTROL SYSTEMS, LTD +0050B5 FICHET-BAUCHE +0050B6 GOOD WAY IND. CO., LTD. +0050B7 BOSER TECHNOLOGY CO., LTD. +0050B8 INOVA COMPUTERS GMBH & CO. KG +0050B9 XITRON TECHNOLOGIES, INC. +0050BA D-LINK +0050BB CMS TECHNOLOGIES +0050BC HAMMER STORAGE SOLUTIONS +0050BD CISCO SYSTEMS, INC. +0050BE FAST MULTIMEDIA AG +0050BF MOTOTECH INC. +0050C0 GATAN, INC. +0050C1 GEMFLEX NETWORKS, LTD. +0050C2 IEEE REGISTRATION AUTHORITY +0050C4 IMD +0050C5 ADS TECHNOLOGIES, INC. +0050C6 LOOP TELECOMMUNICATION INTERNATIONAL, INC. +0050C8 ADDONICS COMMUNICATIONS, INC. +0050C9 MASPRO DENKOH CORP. +0050CA NET TO NET TECHNOLOGIES +0050CB JETTER +0050CC XYRATEX +0050CD DIGIANSWER A/S +0050CE LG INTERNATIONAL CORP. +0050CF VANLINK COMMUNICATION TECHNOLOGY RESEARCH INSTITUTE +0050D0 MINERVA SYSTEMS +0050D1 CISCO SYSTEMS, INC. +0050D2 BAE Systems Canada, Inc. +0050D3 DIGITAL AUDIO PROCESSING PTY. LTD. +0050D4 JOOHONG INFORMATION & +0050D5 AD SYSTEMS CORP. +0050D6 ATLAS COPCO TOOLS AB +0050D7 TELSTRAT +0050D8 UNICORN COMPUTER CORP. +0050D9 ENGETRON-ENGENHARIA ELETRONICA IND. e COM. LTDA +0050DA 3COM CORPORATION +0050DB CONTEMPORARY CONTROL +0050DC TAS TELEFONBAU A. SCHWABE GMBH & CO. KG +0050DD SERRA SOLDADURA, S.A. +0050DE SIGNUM SYSTEMS CORP. +0050DF AirFiber, Inc. +0050E1 NS TECH ELECTRONICS SDN BHD +0050E2 CISCO SYSTEMS, INC. +0050E3 Terayon Communications Systems +0050E4 APPLE COMPUTER, INC. +0050E6 HAKUSAN CORPORATION +0050E7 PARADISE INNOVATIONS (ASIA) +0050E8 NOMADIX INC. +0050EA XEL COMMUNICATIONS, INC. +0050EB ALPHA-TOP CORPORATION +0050EC OLICOM A/S +0050ED ANDA NETWORKS +0050EE TEK DIGITEL CORPORATION +0050EF SPE Systemhaus GmbH +0050F0 CISCO SYSTEMS, INC. +0050F1 LIBIT SIGNAL PROCESSING, LTD. +0050F2 MICROSOFT CORP. +0050F3 GLOBAL NET INFORMATION CO., Ltd. +0050F4 SIGMATEK GMBH & CO. KG +0050F6 PAN-INTERNATIONAL INDUSTRIAL CORP. +0050F7 VENTURE MANUFACTURING (SINGAPORE) LTD. +0050F8 ENTREGA TECHNOLOGIES, INC. +0050FA OXTEL, LTD. +0050FB VSK ELECTRONICS +0050FC EDIMAX TECHNOLOGY CO., LTD. +0050FD VISIONCOMM CO., LTD. +0050FE PCTVnet ASA +0050FF HAKKO ELECTRONICS CO., LTD. +006000 XYCOM INC. +006001 InnoSys, Inc. +006002 SCREEN SUBTITLING SYSTEMS, LTD +006003 TERAOKA WEIGH SYSTEM PTE, LTD. +006004 COMPUTADORES MODULARES SA +006005 FEEDBACK DATA LTD. +006006 SOTEC CO., LTD +006007 ACRES GAMING, INC. +006008 3COM CORPORATION +006009 CISCO SYSTEMS, INC. +00600A SORD COMPUTER CORPORATION +00600B LOGWARE GmbH +00600C APPLIED DATA SYSTEMS, INC. +00600D Digital Logic GmbH +00600E WAVENET INTERNATIONAL, INC. +00600F WESTELL, INC. +006010 NETWORK MACHINES, INC. +006011 CRYSTAL SEMICONDUCTOR CORP. +006012 POWER COMPUTING CORPORATION +006013 NETSTAL MASCHINEN AG +006014 EDEC CO., LTD. +006015 NET2NET CORPORATION +006016 CLARIION +006017 TOKIMEC INC. +006018 STELLAR ONE CORPORATION +006019 Roche Diagnostics +00601A KEITHLEY INSTRUMENTS +00601B MESA ELECTRONICS +00601C TELXON CORPORATION +00601D LUCENT TECHNOLOGIES +00601E SOFTLAB, INC. +00601F STALLION TECHNOLOGIES +006020 PIVOTAL NETWORKING, INC. +006021 DSC CORPORATION +006022 VICOM SYSTEMS, INC. +006023 PERICOM SEMICONDUCTOR CORP. +006024 GRADIENT TECHNOLOGIES, INC. +006025 ACTIVE IMAGING PLC +006026 VIKING COMPONENTS, INC. +006027 Superior Modular Products +006028 MACROVISION CORPORATION +006029 CARY PERIPHERALS INC. +00602A SYMICRON COMPUTER COMMUNICATIONS, LTD. +00602B PEAK AUDIO +00602C LINX Data Terminals, Inc. +00602D ALERTON TECHNOLOGIES, INC. +00602E CYCLADES CORPORATION +00602F CISCO SYSTEMS, INC. +006030 VILLAGE TRONIC ENTWICKLUNG +006031 HRK SYSTEMS +006032 I-CUBE, INC. +006033 ACUITY IMAGING, INC. +006034 ROBERT BOSCH GmbH +006035 DALLAS SEMICONDUCTOR, INC. +006036 AUSTRIAN RESEARCH CENTER SEIBERSDORF +006037 PHILIPS SEMICONDUCTORS +006038 Nortel Networks +006039 SanCom Technology, Inc. +00603A QUICK CONTROLS LTD. +00603B AMTEC spa +00603C HAGIWARA SYS-COM CO., LTD. +00603D 3CX +00603E CISCO SYSTEMS, INC. +00603F PATAPSCO DESIGNS +006040 NETRO CORP. +006041 Yokogawa Electric Corporation +006042 TKS (USA), INC. +006043 ComSoft Systems, Inc. +006044 LITTON/POLY-SCIENTIFIC +006045 PATHLIGHT TECHNOLOGIES +006046 VMETRO, INC. +006047 CISCO SYSTEMS, INC. +006048 EMC CORPORATION +006049 VINA TECHNOLOGIES +00604A SAIC IDEAS GROUP +00604B BIODATA GmbH +00604C SAT +00604D MMC NETWORKS, INC. +00604E CYCLE COMPUTER CORPORATION, INC. +00604F SUZUKI MFG. CO., LTD. +006050 INTERNIX INC. +006051 QUALITY SEMICONDUCTOR +006052 PERIPHERALS ENTERPRISE CO., Ltd. +006053 TOYODA MACHINE WORKS, LTD. +006054 CONTROLWARE GMBH +006055 CORNELL UNIVERSITY +006056 NETWORK TOOLS, INC. +006057 MURATA MANUFACTURING CO., LTD. +006058 COPPER MOUNTAIN COMMUNICATIONS, INC. +006059 TECHNICAL COMMUNICATIONS CORP. +00605A CELCORE, INC. +00605B IntraServer Technology, Inc. +00605C CISCO SYSTEMS, INC. +00605D SCANIVALVE CORP. +00605E LIBERTY TECHNOLOGY NETWORKING +00605F NIPPON UNISOFT CORPORATION +006060 DAWNING TECHNOLOGIES, INC. +006061 WHISTLE COMMUNICATIONS CORP. +006062 TELESYNC, INC. +006063 PSION DACOM PLC. +006064 NETCOMM LIMITED +006065 BERNECKER & RAINER INDUSTRIE-ELEKTRONIC GmbH +006066 LACROIX TECHNOLGIE +006067 ACER NETXUS INC. +006068 EICON TECHNOLOGY CORPORATION +006069 BROCADE COMMUNICATIONS SYSTEMS, Inc. +00606A MITSUBISHI WIRELESS COMMUNICATIONS. INC. +00606B Synclayer Inc. +00606C ARESCOM +00606D DIGITAL EQUIPMENT CORP. +00606E DAVICOM SEMICONDUCTOR, INC. +00606F CLARION CORPORATION OF AMERICA +006070 CISCO SYSTEMS, INC. +006071 MIDAS LAB, INC. +006072 VXL INSTRUMENTS, LIMITED +006073 REDCREEK COMMUNICATIONS, INC. +006074 QSC AUDIO PRODUCTS +006075 PENTEK, INC. +006076 SCHLUMBERGER TECHNOLOGIES RETAIL PETROLEUM SYSTEMS +006077 PRISA NETWORKS +006078 POWER MEASUREMENT LTD. +006079 Mainstream Data, Inc. +00607A DVS GmbH +00607B FORE SYSTEMS, INC. +00607C WaveAccess, Ltd. +00607D SENTIENT NETWORKS INC. +00607E GIGALABS, INC. +00607F AURORA TECHNOLOGIES, INC. +006080 MICROTRONIX DATACOM LTD. +006081 TV/COM INTERNATIONAL +006082 NOVALINK TECHNOLOGIES, INC. +006083 CISCO SYSTEMS, INC. +006084 DIGITAL VIDEO +006085 Storage Concepts +006086 LOGIC REPLACEMENT TECH. LTD. +006087 KANSAI ELECTRIC CO., LTD. +006088 WHITE MOUNTAIN DSP, INC. +006089 XATA +00608A CITADEL COMPUTER +00608B ConferTech International +00608C 3COM CORPORATION +00608D UNIPULSE CORP. +00608E HE ELECTRONICS, TECHNOLOGIE & SYSTEMTECHNIK GmbH +00608F TEKRAM TECHNOLOGY CO., LTD. +006090 ABLE COMMUNICATIONS, INC. +006091 FIRST PACIFIC NETWORKS, INC. +006092 MICRO/SYS, INC. +006093 VARIAN +006094 IBM CORP. +006095 ACCU-TIME SYSTEMS, INC. +006096 T.S. MICROTECH INC. +006097 3COM CORPORATION +006098 HT COMMUNICATIONS +006099 LAN MEDIA CORPORATION +00609A NJK TECHNO CO. +00609B ASTRO-MED, INC. +00609C Perkin-Elmer Incorporated +00609D PMI FOOD EQUIPMENT GROUP +00609E ASC X3 - INFORMATION TECHNOLOGY STANDARDS SECRETARIATS +00609F PHAST CORPORATION +0060A0 SWITCHED NETWORK TECHNOLOGIES, INC. +0060A1 VPNet, Inc. +0060A2 NIHON UNISYS LIMITED CO. +0060A3 CONTINUUM TECHNOLOGY CORP. +0060A4 GRINAKER SYSTEM TECHNOLOGIES +0060A5 PERFORMANCE TELECOM CORP. +0060A6 PARTICLE MEASURING SYSTEMS +0060A7 MICROSENS GmbH & CO. KG +0060A8 TIDOMAT AB +0060A9 GESYTEC MbH +0060AA INTELLIGENT DEVICES INC. (IDI) +0060AB LARSCOM INCORPORATED +0060AC RESILIENCE CORPORATION +0060AD MegaChips Corporation +0060AE TRIO INFORMATION SYSTEMS AB +0060AF PACIFIC MICRO DATA, INC. +0060B0 HEWLETT-PACKARD CO. +0060B1 INPUT/OUTPUT, INC. +0060B2 PROCESS CONTROL CORP. +0060B3 Z-COM, INC. +0060B4 GLENAYRE R&D INC. +0060B5 KEBA GmbH +0060B6 LAND COMPUTER CO., LTD. +0060B7 CHANNELMATIC, INC. +0060B8 CORELIS INC. +0060B9 NITSUKO CORPORATION +0060BA SAHARA NETWORKS, INC. +0060BB CABLETRON - NETLINK, INC. +0060BC KeunYoung Electronics & Communication Co., Ltd. +0060BD HUBBELL-PULSECOM +0060BE WEBTRONICS +0060BF MACRAIGOR SYSTEMS, INC. +0060C0 NERA AS +0060C1 WaveSpan Corporation +0060C2 MPL AG +0060C3 NETVISION CORPORATION +0060C4 SOLITON SYSTEMS K.K. +0060C5 ANCOT CORP. +0060C6 DCS AG +0060C7 AMATI COMMUNICATIONS CORP. +0060C8 KUKA WELDING SYSTEMS & ROBOTS +0060C9 ControlNet, Inc. +0060CA HARMONIC SYSTEMS INCORPORATED +0060CB HITACHI ZOSEN CORPORATION +0060CC EMTRAK, INCORPORATED +0060CD VideoServer, Inc. +0060CE ACCLAIM COMMUNICATIONS +0060CF ALTEON NETWORKS, INC. +0060D0 SNMP RESEARCH INCORPORATED +0060D1 CASCADE COMMUNICATIONS +0060D2 LUCENT TECHNOLOGIES TAIWAN TELECOMMUNICATIONS CO., LTD. +0060D3 AT&T +0060D4 ELDAT COMMUNICATION LTD. +0060D5 MIYACHI TECHNOS CORP. +0060D6 NovAtel Wireless Technologies Ltd. +0060D7 ECOLE POLYTECHNIQUE FEDERALE DE LAUSANNE (EPFL) +0060D8 ELMIC SYSTEMS, INC. +0060D9 TRANSYS NETWORKS INC. +0060DA JBM ELECTRONICS CO. +0060DB NTP ELEKTRONIK A/S +0060DC TOYO COMMUNICATION EQUIPMENT Co., Ltd. +0060DD MYRICOM, INC. +0060DE KAYSER-THREDE GmbH +0060DF CNT Corporation +0060E0 AXIOM TECHNOLOGY CO., LTD. +0060E1 ORCKIT COMMUNICATIONS LTD. +0060E2 QUEST ENGINEERING & DEVELOPMENT +0060E3 ARBIN INSTRUMENTS +0060E4 COMPUSERVE, INC. +0060E5 FUJI AUTOMATION CO., LTD. +0060E6 SHOMITI SYSTEMS INCORPORATED +0060E7 RANDATA +0060E8 HITACHI COMPUTER PRODUCTS (AMERICA), INC. +0060E9 ATOP TECHNOLOGIES, INC. +0060EA StreamLogic +0060EB FOURTHTRACK SYSTEMS +0060EC HERMARY OPTO ELECTRONICS INC. +0060ED RICARDO TEST AUTOMATION LTD. +0060EE APOLLO +0060EF FLYTECH TECHNOLOGY CO., LTD. +0060F0 JOHNSON & JOHNSON MEDICAL, INC +0060F1 EXP COMPUTER, INC. +0060F2 LASERGRAPHICS, INC. +0060F3 Performance Analysis Broadband, Spirent plc +0060F4 ADVANCED COMPUTER SOLUTIONS, Inc. +0060F5 ICON WEST, INC. +0060F6 NEXTEST COMMUNICATIONS PRODUCTS, INC. +0060F7 DATAFUSION SYSTEMS +0060F8 Loran International Technologies Inc. +0060F9 DIAMOND LANE COMMUNICATIONS +0060FA EDUCATIONAL TECHNOLOGY RESOURCES, INC. +0060FB PACKETEER, INC. +0060FC CONSERVATION THROUGH INNOVATION LTD. +0060FD NetICs, Inc. +0060FE LYNX SYSTEM DEVELOPERS, INC. +0060FF QuVis, Inc. +0070B0 M/A-COM INC. COMPANIES +0070B3 DATA RECALL LTD. +008000 MULTITECH SYSTEMS, INC. +008001 PERIPHONICS CORPORATION +008002 SATELCOM (UK) LTD +008003 HYTEC ELECTRONICS LTD. +008004 ANTLOW COMMUNICATIONS, LTD. +008005 CACTUS COMPUTER INC. +008006 COMPUADD CORPORATION +008007 DLOG NC-SYSTEME +008008 DYNATECH COMPUTER SYSTEMS +008009 JUPITER SYSTEMS, INC. +00800A JAPAN COMPUTER CORP. +00800B CSK CORPORATION +00800C VIDECOM LIMITED +00800D VOSSWINKEL F.U. +00800E ATLANTIX CORPORATION +00800F STANDARD MICROSYSTEMS +008010 COMMODORE INTERNATIONAL +008011 DIGITAL SYSTEMS INT'L. INC. +008012 INTEGRATED MEASUREMENT SYSTEMS +008013 THOMAS-CONRAD CORPORATION +008014 ESPRIT SYSTEMS +008015 SEIKO SYSTEMS, INC. +008016 WANDEL AND GOLTERMANN +008017 PFU LIMITED +008018 KOBE STEEL, LTD. +008019 DAYNA COMMUNICATIONS, INC. +00801A BELL ATLANTIC +00801B KODIAK TECHNOLOGY +00801C NEWPORT SYSTEMS SOLUTIONS +00801D INTEGRATED INFERENCE MACHINES +00801E XINETRON, INC. +00801F KRUPP ATLAS ELECTRONIK GMBH +008020 NETWORK PRODUCTS +008021 Alcatel Canada Inc. +008022 SCAN-OPTICS +008023 INTEGRATED BUSINESS NETWORKS +008024 KALPANA, INC. +008025 STOLLMANN GMBH +008026 NETWORK PRODUCTS CORPORATION +008027 ADAPTIVE SYSTEMS, INC. +008028 TRADPOST (HK) LTD +008029 EAGLE TECHNOLOGY, INC. +00802A TEST SYSTEMS & SIMULATIONS INC +00802B INTEGRATED MARKETING CO +00802C THE SAGE GROUP PLC +00802D XYLOGICS INC +00802E CASTLE ROCK COMPUTING +00802F NATIONAL INSTRUMENTS CORP. +008030 NEXUS ELECTRONICS +008031 BASYS, CORP. +008032 ACCESS CO., LTD. +008033 FORMATION, INC. +008034 SMT GOUPIL +008035 TECHNOLOGY WORKS, INC. +008036 REFLEX MANUFACTURING SYSTEMS +008037 Ericsson Group +008038 DATA RESEARCH & APPLICATIONS +008039 ALCATEL STC AUSTRALIA +00803A VARITYPER, INC. +00803B APT COMMUNICATIONS, INC. +00803C TVS ELECTRONICS LTD +00803D SURIGIKEN CO., LTD. +00803E SYNERNETICS +00803F TATUNG COMPANY +008040 JOHN FLUKE MANUFACTURING CO. +008041 VEB KOMBINAT ROBOTRON +008042 FORCE COMPUTERS +008043 NETWORLD, INC. +008044 SYSTECH COMPUTER CORP. +008045 MATSUSHITA ELECTRIC IND. CO +008046 UNIVERSITY OF TORONTO +008047 IN-NET CORP. +008048 COMPEX INCORPORATED +008049 NISSIN ELECTRIC CO., LTD. +00804A PRO-LOG +00804B EAGLE TECHNOLOGIES PTY.LTD. +00804C CONTEC CO., LTD. +00804D CYCLONE MICROSYSTEMS, INC. +00804E APEX COMPUTER COMPANY +00804F DAIKIN INDUSTRIES, LTD. +008050 ZIATECH CORPORATION +008051 FIBERMUX +008052 TECHNICALLY ELITE CONCEPTS +008053 INTELLICOM, INC. +008054 FRONTIER TECHNOLOGIES CORP. +008055 FERMILAB +008056 SPHINX ELEKTRONIK GMBH +008057 ADSOFT, LTD. +008058 PRINTER SYSTEMS CORPORATION +008059 STANLEY ELECTRIC CO., LTD +00805A TULIP COMPUTERS INTERNAT'L B.V +00805B CONDOR SYSTEMS, INC. +00805C AGILIS CORPORATION +00805D CANSTAR +00805E LSI LOGIC CORPORATION +00805F COMPAQ COMPUTER CORPORATION +008060 NETWORK INTERFACE CORPORATION +008061 LITTON SYSTEMS, INC. +008062 INTERFACE CO. +008063 RICHARD HIRSCHMANN GMBH & CO. +008064 WYSE TECHNOLOGY +008065 CYBERGRAPHIC SYSTEMS PTY LTD. +008066 ARCOM CONTROL SYSTEMS, LTD. +008067 SQUARE D COMPANY +008068 YAMATECH SCIENTIFIC LTD. +008069 COMPUTONE SYSTEMS +00806A ERI (EMPAC RESEARCH INC.) +00806B SCHMID TELECOMMUNICATION +00806C CEGELEC PROJECTS LTD +00806D CENTURY SYSTEMS CORP. +00806E NIPPON STEEL CORPORATION +00806F ONELAN LTD. +008070 COMPUTADORAS MICRON +008071 SAI TECHNOLOGY +008072 MICROPLEX SYSTEMS LTD. +008073 DWB ASSOCIATES +008074 FISHER CONTROLS +008075 PARSYTEC GMBH +008076 MCNC +008077 BROTHER INDUSTRIES, LTD. +008078 PRACTICAL PERIPHERALS, INC. +008079 MICROBUS DESIGNS LTD. +00807A AITECH SYSTEMS LTD. +00807B ARTEL COMMUNICATIONS CORP. +00807C FIBERCOM, INC. +00807D EQUINOX SYSTEMS INC. +00807E SOUTHERN PACIFIC LTD. +00807F DY-4 INCORPORATED +008080 DATAMEDIA CORPORATION +008081 KENDALL SQUARE RESEARCH CORP. +008082 PEP MODULAR COMPUTERS GMBH +008083 AMDAHL +008084 THE CLOUD INC. +008085 H-THREE SYSTEMS CORPORATION +008086 COMPUTER GENERATION INC. +008087 OKI ELECTRIC INDUSTRY CO., LTD +008088 VICTOR COMPANY OF JAPAN, LTD. +008089 TECNETICS (PTY) LTD. +00808A SUMMIT MICROSYSTEMS CORP. +00808B DACOLL LIMITED +00808C NetScout Systems, Inc. +00808D WESTCOAST TECHNOLOGY B.V. +00808E RADSTONE TECHNOLOGY +00808F C. ITOH ELECTRONICS, INC. +008090 MICROTEK INTERNATIONAL, INC. +008091 TOKYO ELECTRIC CO.,LTD +008092 JAPAN COMPUTER INDUSTRY, INC. +008093 XYRON CORPORATION +008094 ALFA LAVAL AUTOMATION AB +008095 BASIC MERTON HANDELSGES.M.B.H. +008096 HUMAN DESIGNED SYSTEMS, INC. +008097 CENTRALP AUTOMATISMES +008098 TDK CORPORATION +008099 KLOCKNER MOELLER IPC +00809A NOVUS NETWORKS LTD +00809B JUSTSYSTEM CORPORATION +00809C LUXCOM, INC. +00809D Commscraft Ltd. +00809E DATUS GMBH +00809F ALCATEL BUSINESS SYSTEMS +0080A0 EDISA HEWLETT PACKARD S/A +0080A1 MICROTEST, INC. +0080A2 CREATIVE ELECTRONIC SYSTEMS +0080A3 LANTRONIX +0080A4 LIBERTY ELECTRONICS +0080A5 SPEED INTERNATIONAL +0080A6 REPUBLIC TECHNOLOGY, INC. +0080A7 MEASUREX CORP. +0080A8 VITACOM CORPORATION +0080A9 CLEARPOINT RESEARCH +0080AA MAXPEED +0080AB DUKANE NETWORK INTEGRATION +0080AC IMLOGIX, DIVISION OF GENESYS +0080AD CNET TECHNOLOGY, INC. +0080AE HUGHES NETWORK SYSTEMS +0080AF ALLUMER CO., LTD. +0080B0 ADVANCED INFORMATION +0080B1 SOFTCOM A/S +0080B2 NETWORK EQUIPMENT TECHNOLOGIES +0080B3 AVAL DATA CORPORATION +0080B4 SOPHIA SYSTEMS +0080B5 UNITED NETWORKS INC. +0080B6 THEMIS COMPUTER +0080B7 STELLAR COMPUTER +0080B8 BUG, INCORPORATED +0080B9 ARCHE TECHNOLIGIES INC. +0080BA SPECIALIX (ASIA) PTE, LTD +0080BB HUGHES LAN SYSTEMS +0080BC HITACHI ENGINEERING CO., LTD +0080BD THE FURUKAWA ELECTRIC CO., LTD +0080BE ARIES RESEARCH +0080BF TAKAOKA ELECTRIC MFG. CO. LTD. +0080C0 PENRIL DATACOMM +0080C1 LANEX CORPORATION +0080C2 IEEE 802.1 COMMITTEE +0080C3 BICC INFORMATION SYSTEMS & SVC +0080C4 DOCUMENT TECHNOLOGIES, INC. +0080C5 NOVELLCO DE MEXICO +0080C6 NATIONAL DATACOMM CORPORATION +0080C7 XIRCOM +0080C8 D-LINK SYSTEMS, INC. +0080C9 ALBERTA MICROELECTRONIC CENTRE +0080CA NETCOM RESEARCH INCORPORATED +0080CB FALCO DATA PRODUCTS +0080CC MICROWAVE BYPASS SYSTEMS +0080CD MICRONICS COMPUTER, INC. +0080CE BROADCAST TELEVISION SYSTEMS +0080CF EMBEDDED PERFORMANCE INC. +0080D0 COMPUTER PERIPHERALS, INC. +0080D1 KIMTRON CORPORATION +0080D2 SHINNIHONDENKO CO., LTD. +0080D3 SHIVA CORP. +0080D4 CHASE RESEARCH LTD. +0080D5 CADRE TECHNOLOGIES +0080D6 NUVOTECH, INC. +0080D7 Fantum Engineering +0080D8 NETWORK PERIPHERALS INC. +0080D9 EMK ELEKTRONIK +0080DA BRUEL & KJAER +0080DB GRAPHON CORPORATION +0080DC PICKER INTERNATIONAL +0080DD GMX INC/GIMIX +0080DE GIPSI S.A. +0080DF ADC CODENOLL TECHNOLOGY CORP. +0080E0 XTP SYSTEMS, INC. +0080E1 STMICROELECTRONICS +0080E2 T.D.I. CO., LTD. +0080E3 CORAL NETWORK CORPORATION +0080E4 NORTHWEST DIGITAL SYSTEMS, INC +0080E5 MYLEX CORPORATION +0080E6 PEER NETWORKS, INC. +0080E7 LYNWOOD SCIENTIFIC DEV. LTD. +0080E8 CUMULUS CORPORATIION +0080E9 Madge Ltd. +0080EA ADVA Optical Networking Ltd. +0080EB COMPCONTROL B.V. +0080EC SUPERCOMPUTING SOLUTIONS, INC. +0080ED IQ TECHNOLOGIES, INC. +0080EE THOMSON CSF +0080EF RATIONAL +0080F0 Panasonic Communications Co., Ltd. +0080F1 OPUS SYSTEMS +0080F2 RAYCOM SYSTEMS INC +0080F3 SUN ELECTRONICS CORP. +0080F4 TELEMECANIQUE ELECTRIQUE +0080F5 QUANTEL LTD +0080F6 SYNERGY MICROSYSTEMS +0080F7 ZENITH ELECTRONICS +0080F8 MIZAR, INC. +0080F9 HEURIKON CORPORATION +0080FA RWT GMBH +0080FB BVM LIMITED +0080FC AVATAR CORPORATION +0080FD EXSCEED CORPRATION +0080FE AZURE TECHNOLOGIES, INC. +0080FF SOC. DE TELEINFORMATIQUE RTC +009000 DIAMOND MULTIMEDIA +009001 NISHIMU ELECTRONICS INDUSTRIES CO., LTD. +009002 ALLGON AB +009003 APLIO +009004 3COM EUROPE LTD. +009005 PROTECH SYSTEMS CO., LTD. +009006 HAMAMATSU PHOTONICS K.K. +009007 DOMEX TECHNOLOGY CORP. +009008 HanA Systems Inc. +009009 i Controls, Inc. +00900A PROTON ELECTRONIC INDUSTRIAL CO., LTD. +00900B LANNER ELECTRONICS, INC. +00900C CISCO SYSTEMS, INC. +00900D OVERLAND DATA INC. +00900E HANDLINK TECHNOLOGIES, INC. +00900F KAWASAKI HEAVY INDUSTRIES, LTD +009010 SIMULATION LABORATORIES, INC. +009011 WAVTrace, Inc. +009012 GLOBESPAN SEMICONDUCTOR, INC. +009013 SAMSAN CORP. +009014 ROTORK INSTRUMENTS, LTD. +009015 CENTIGRAM COMMUNICATIONS CORP. +009016 ZAC +009017 ZYPCOM, INC. +009018 ITO ELECTRIC INDUSTRY CO, LTD. +009019 HERMES ELECTRONICS CO., LTD. +00901A UNISPHERE SOLUTIONS +00901B DIGITAL CONTROLS +00901C mps Software Gmbh +00901D PEC (NZ) LTD. +00901E SELESTA INGEGNE RIA S.P.A. +00901F ADTEC PRODUCTIONS, INC. +009020 PHILIPS ANALYTICAL X-RAY B.V. +009021 CISCO SYSTEMS, INC. +009022 IVEX +009023 ZILOG INC. +009024 PIPELINKS, INC. +009025 VISION SYSTEMS LTD. PTY +009026 ADVANCED SWITCHING COMMUNICATIONS, INC. +009027 INTEL CORPORATION +009028 NIPPON SIGNAL CO., LTD. +009029 CRYPTO AG +00902A COMMUNICATION DEVICES, INC. +00902B CISCO SYSTEMS, INC. +00902C DATA & CONTROL EQUIPMENT LTD. +00902D DATA ELECTRONICS (AUST.) PTY, LTD. +00902E NAMCO LIMITED +00902F NETCORE SYSTEMS, INC. +009030 HONEYWELL-DATING +009031 MYSTICOM, LTD. +009032 PELCOMBE GROUP LTD. +009033 INNOVAPHONE GmbH +009034 IMAGIC, INC. +009035 ALPHA TELECOM, INC. +009036 ens, inc. +009037 ACUCOMM, INC. +009038 FOUNTAIN TECHNOLOGIES, INC. +009039 SHASTA NETWORKS +00903A NIHON MEDIA TOOL INC. +00903B TriEMS Research Lab, Inc. +00903C ATLANTIC NETWORK SYSTEMS +00903D BIOPAC SYSTEMS, INC. +00903E N.V. PHILIPS INDUSTRIAL ACTIVITIES +00903F AZTEC RADIOMEDIA +009040 Siemens Network Convergence LLC +009041 APPLIED DIGITAL ACCESS +009042 ECCS, Inc. +009043 NICHIBEI DENSHI CO., LTD. +009044 ASSURED DIGITAL, INC. +009045 Marconi Communications +009046 DEXDYNE, LTD. +009047 GIGA FAST E. LTD. +009048 ZEAL CORPORATION +009049 ENTRIDIA CORPORATION +00904A CONCUR SYSTEM TECHNOLOGIES +00904B GemTek Technology Co., Ltd. +00904C EPIGRAM, INC. +00904D SPEC S.A. +00904E DELEM BV +00904F ABB POWER T&D COMPANY, INC. +009050 TELESTE OY +009051 ULTIMATE TECHNOLOGY CORP. +009052 SELCOM ELETTRONICA S.R.L. +009053 DAEWOO ELECTRONICS CO., LTD. +009054 INNOVATIVE SEMICONDUCTORS, INC +009055 PARKER HANNIFIN CORPORATION COMPUMOTOR DIVISION +009056 TELESTREAM, INC. +009057 AANetcom, Inc. +009058 Ultra Electronics Ltd., Command and Control Systems +009059 TELECOM DEVICE K.K. +00905A DEARBORN GROUP, INC. +00905B RAYMOND AND LAE ENGINEERING +00905C EDMI +00905D NETCOM SICHERHEITSTECHNIK GmbH +00905E RAULAND-BORG CORPORATION +00905F CISCO SYSTEMS, INC. +009060 SYSTEM CREATE CORP. +009061 PACIFIC RESEARCH & ENGINEERING CORPORATION +009062 ICP VORTEX COMPUTERSYSTEME GmbH +009063 COHERENT COMMUNICATIONS SYSTEMS CORPORATION +009064 THOMSON BROADCAST SYSTEMS +009065 FINISAR CORPORATION +009066 Troika Networks, Inc. +009067 WalkAbout Computers, Inc. +009068 DVT CORP. +009069 JUNIPER NETWORKS, INC. +00906A TURNSTONE SYSTEMS, INC. +00906B APPLIED RESOURCES, INC. +00906C GWT GLOBAL WEIGHING TECHNOLOGIES GmbH +00906D CISCO SYSTEMS, INC. +00906E PRAXON, INC. +00906F CISCO SYSTEMS, INC. +009070 NEO NETWORKS, INC. +009071 Applied Innovation Inc. +009072 SIMRAD AS +009073 GAIO TECHNOLOGY +009074 ARGON NETWORKS, INC. +009075 NEC DO BRASIL S.A. +009076 FMT AIRCRAFT GATE SUPPORT SYSTEMS AB +009077 ADVANCED FIBRE COMMUNICATIONS +009078 MER TELEMANAGEMENT SOLUTIONS, LTD. +009079 ClearOne, Inc. +00907A SPECTRALINK CORP. +00907B E-TECH, INC. +00907C DIGITALCAST, INC. +00907D Lake Communications +00907E VETRONIX CORP. +00907F WatchGuard Technologies, Inc. +009080 NOT LIMITED, INC. +009081 ALOHA NETWORKS, INC. +009082 FORCE INSTITUTE +009083 TURBO COMMUNICATION, INC. +009084 ATECH SYSTEM +009085 GOLDEN ENTERPRISES, INC. +009086 CISCO SYSTEMS, INC. +009087 ITIS +009088 BAXALL SECURITY LTD. +009089 SOFTCOM MICROSYSTEMS, INC. +00908A BAYLY COMMUNICATIONS, INC. +00908B CELL COMPUTING, INC. +00908C ETREND ELECTRONICS, INC. +00908D VICKERS ELECTRONICS SYSTEMS +00908E Nortel Networks Broadband Access +00908F AUDIO CODES LTD. +009090 I-BUS +009091 DigitalScape, Inc. +009092 CISCO SYSTEMS, INC. +009093 NANAO CORPORATION +009094 OSPREY TECHNOLOGIES, INC. +009095 UNIVERSAL AVIONICS +009096 ASKEY COMPUTER CORP. +009097 SYCAMORE NETWORKS +009098 SBC DESIGNS, INC. +009099 ALLIED TELESIS, K.K. +00909A ONE WORLD SYSTEMS, INC. +00909B MARKPOINT AB +00909C Terayon Communications Systems +00909D GSE SYSTEMS, INC. +00909E Critical IO, LLC +00909F DIGI-DATA CORPORATION +0090A0 8X8 INC. +0090A1 FLYING PIG SYSTEMS, LTD. +0090A2 CYBERTAN TECHNOLOGY, INC. +0090A3 Corecess Inc. +0090A4 ALTIGA NETWORKS +0090A5 SPECTRA LOGIC +0090A6 CISCO SYSTEMS, INC. +0090A7 CLIENTEC CORPORATION +0090A8 NineTiles Networks, Ltd. +0090A9 WESTERN DIGITAL +0090AA INDIGO ACTIVE VISION SYSTEMS LIMITED +0090AB CISCO SYSTEMS, INC. +0090AC OPTIVISION, INC. +0090AD ASPECT ELECTRONICS, INC. +0090AE ITALTEL S.p.A. +0090AF J. MORITA MFG. CORP. +0090B0 VADEM +0090B1 CISCO SYSTEMS, INC. +0090B2 AVICI SYSTEMS INC. +0090B3 AGRANAT SYSTEMS +0090B4 WILLOWBROOK TECHNOLOGIES +0090B5 NIKON CORPORATION +0090B6 FIBEX SYSTEMS +0090B7 DIGITAL LIGHTWAVE, INC. +0090B8 ROHDE & SCHWARZ GMBH & CO. KG +0090B9 BERAN INSTRUMENTS LTD. +0090BA VALID NETWORKS, INC. +0090BB TAINET COMMUNICATION SYSTEM Corp. +0090BC TELEMANN CO., LTD. +0090BD OMNIA COMMUNICATIONS, INC. +0090BE IBC/INTEGRATED BUSINESS COMPUTERS +0090BF CISCO SYSTEMS, INC. +0090C0 K.J. LAW ENGINEERS, INC. +0090C1 Peco II, Inc. +0090C2 JK microsystems, Inc. +0090C3 TOPIC SEMICONDUCTOR CORP. +0090C4 JAVELIN SYSTEMS, INC. +0090C5 INTERNET MAGIC, INC. +0090C6 OPTIM SYSTEMS, INC. +0090C7 ICOM INC. +0090C8 WAVERIDER COMMUNICATIONS (CANADA) INC. +0090C9 DPAC Technologies +0090CA ACCORD VIDEO TELECOMMUNICATIONS, LTD. +0090CB Wireless OnLine, Inc. +0090CC PLANET COMMUNICATIONS, INC. +0090CD ENT-EMPRESA NACIONAL DE TELECOMMUNICACOES, S.A. +0090CE TETRA GmbH +0090CF NORTEL +0090D0 Thomson Belgium +0090D1 LEICHU ENTERPRISE CO., LTD. +0090D2 ARTEL VIDEO SYSTEMS +0090D3 GIESECKE & DEVRIENT GmbH +0090D4 BindView Development Corp. +0090D5 EUPHONIX, INC. +0090D6 CRYSTAL GROUP +0090D7 NetBoost Corp. +0090D8 WHITECROSS SYSTEMS +0090D9 CISCO SYSTEMS, INC. +0090DA DYNARC, INC. +0090DB NEXT LEVEL COMMUNICATIONS +0090DC TECO INFORMATION SYSTEMS +0090DD THE MIHARU COMMUNICATIONS CO., LTD. +0090DE CARDKEY SYSTEMS, INC. +0090DF MITSUBISHI CHEMICAL AMERICA, INC. +0090E0 SYSTRAN CORP. +0090E1 TELENA S.P.A. +0090E2 DISTRIBUTED PROCESSING TECHNOLOGY +0090E3 AVEX ELECTRONICS INC. +0090E4 NEC AMERICA, INC. +0090E5 TEKNEMA, INC. +0090E6 ACER LABORATORIES, INC. +0090E7 HORSCH ELEKTRONIK AG +0090E8 MOXA TECHNOLOGIES CORP., LTD. +0090E9 JANZ COMPUTER AG +0090EA ALPHA TECHNOLOGIES, INC. +0090EB SENTRY TELECOM SYSTEMS +0090EC PYRESCOM +0090ED CENTRAL SYSTEM RESEARCH CO., LTD. +0090EE PERSONAL COMMUNICATIONS TECHNOLOGIES +0090EF INTEGRIX, INC. +0090F0 HARMONIC LIGHTWAVES, LTD. +0090F1 DOT HILL SYSTEMS CORPORATION +0090F2 CISCO SYSTEMS, INC. +0090F3 ASPECT COMMUNICATIONS +0090F4 LIGHTNING INSTRUMENTATION +0090F5 CLEVO CO. +0090F6 ESCALATE NETWORKS, INC. +0090F7 NBASE COMMUNICATIONS LTD. +0090F8 MEDIATRIX TELECOM +0090F9 LEITCH +0090FA GigaNet, Inc. +0090FB PORTWELL, INC. +0090FC NETWORK COMPUTING DEVICES +0090FD CopperCom, Inc. +0090FE ELECOM CO., LTD. (LANEED DIV.) +0090FF TELLUS TECHNOLOGY INC. +0091D6 Crystal Group, Inc. +009D8E CARDIAC RECORDERS, INC. +00A000 CENTILLION NETWORKS, INC. +00A001 WATKINS-JOHNSON COMPANY +00A002 LEEDS & NORTHRUP AUSTRALIA PTY LTD +00A003 STAEFA CONTROL SYSTEM +00A004 NETPOWER, INC. +00A005 DANIEL INSTRUMENTS, LTD. +00A006 IMAGE DATA PROCESSING SYSTEM GROUP +00A007 APEXX TECHNOLOGY, INC. +00A008 NETCORP +00A009 WHITETREE NETWORK +00A00A R.D.C. COMMUNICATION +00A00B COMPUTEX CO., LTD. +00A00C KINGMAX TECHNOLOGY, INC. +00A00D THE PANDA PROJECT +00A00E VISUAL NETWORKS, INC. +00A00F Broadband Technologies +00A010 SYSLOGIC DATENTECHNIK AG +00A011 MUTOH INDUSTRIES LTD. +00A012 B.A.T.M. ADVANCED TECHNOLOGIES +00A013 TELTREND LTD. +00A014 CSIR +00A015 WYLE +00A016 MICROPOLIS CORP. +00A017 J B M CORPORATION +00A018 CREATIVE CONTROLLERS, INC. +00A019 NEBULA CONSULTANTS, INC. +00A01A BINAR ELEKTRONIK AB +00A01B PREMISYS COMMUNICATIONS, INC. +00A01C NASCENT NETWORKS CORPORATION +00A01D SIXNET +00A01E EST CORPORATION +00A01F TRICORD SYSTEMS, INC. +00A020 CITICORP/TTI +00A021 GENERAL DYNAMICS- +00A022 CENTRE FOR DEVELOPMENT OF ADVANCED COMPUTING +00A023 APPLIED CREATIVE TECHNOLOGY, INC. +00A024 3COM CORPORATION +00A025 REDCOM LABS INC. +00A026 TELDAT, S.A. +00A027 FIREPOWER SYSTEMS, INC. +00A028 CONNER PERIPHERALS +00A029 COULTER CORPORATION +00A02A TRANCELL SYSTEMS +00A02B TRANSITIONS RESEARCH CORP. +00A02C interWAVE Communications +00A02D 1394 Trade Association +00A02E BRAND COMMUNICATIONS, LTD. +00A02F PIRELLI CAVI +00A030 CAPTOR NV/SA +00A031 HAZELTINE CORPORATION, MS 1-17 +00A032 GES SINGAPORE PTE. LTD. +00A033 imc MeBsysteme GmbH +00A034 AXEL +00A035 CYLINK CORPORATION +00A036 APPLIED NETWORK TECHNOLOGY +00A037 DATASCOPE CORPORATION +00A038 EMAIL ELECTRONICS +00A039 ROSS TECHNOLOGY, INC. +00A03A KUBOTEK CORPORATION +00A03B TOSHIN ELECTRIC CO., LTD. +00A03C EG&G NUCLEAR INSTRUMENTS +00A03D OPTO-22 +00A03E ATM FORUM +00A03F COMPUTER SOCIETY MICROPROCESSOR & MICROPROCESSOR STANDARDS C +00A040 APPLE COMPUTER +00A041 LEYBOLD-INFICON +00A042 SPUR PRODUCTS CORP. +00A043 AMERICAN TECHNOLOGY LABS, INC. +00A044 NTT IT CO., LTD. +00A045 PHOENIX CONTACT GMBH & CO. +00A046 SCITEX CORP. LTD. +00A047 INTEGRATED FITNESS CORP. +00A048 QUESTECH, LTD. +00A049 DIGITECH INDUSTRIES, INC. +00A04A NISSHIN ELECTRIC CO., LTD. +00A04B TFL LAN INC. +00A04C INNOVATIVE SYSTEMS & TECHNOLOGIES, INC. +00A04D EDA INSTRUMENTS, INC. +00A04E VOELKER TECHNOLOGIES, INC. +00A04F AMERITEC CORP. +00A050 CYPRESS SEMICONDUCTOR +00A051 ANGIA COMMUNICATIONS. INC. +00A052 STANILITE ELECTRONICS PTY. LTD +00A053 COMPACT DEVICES, INC. +00A055 Data Device Corporation +00A056 MICROPROSS +00A057 LANCOM Systems GmbH +00A058 GLORY, LTD. +00A059 HAMILTON HALLMARK +00A05A KOFAX IMAGE PRODUCTS +00A05B MARQUIP, INC. +00A05C INVENTORY CONVERSION, INC./ +00A05D CS COMPUTER SYSTEME GmbH +00A05E MYRIAD LOGIC INC. +00A05F BTG ENGINEERING BV +00A060 ACER PERIPHERALS, INC. +00A061 PURITAN BENNETT +00A062 AES PRODATA +00A063 JRL SYSTEMS, INC. +00A064 KVB/ANALECT +00A065 NEXLAND, INC. +00A066 ISA CO., LTD. +00A067 NETWORK SERVICES GROUP +00A068 BHP LIMITED +00A069 Symmetricom, Inc. +00A06A Verilink Corporation +00A06B DMS DORSCH MIKROSYSTEM GMBH +00A06C SHINDENGEN ELECTRIC MFG. CO., LTD. +00A06D MANNESMANN TALLY CORPORATION +00A06E AUSTRON, INC. +00A06F THE APPCON GROUP, INC. +00A070 COASTCOM +00A071 VIDEO LOTTERY TECHNOLOGIES,INC +00A072 OVATION SYSTEMS LTD. +00A073 COM21, INC. +00A074 PERCEPTION TECHNOLOGY +00A075 MICRON TECHNOLOGY, INC. +00A076 CARDWARE LAB, INC. +00A077 FUJITSU NEXION, INC. +00A078 Marconi Communications +00A079 ALPS ELECTRIC (USA), INC. +00A07A ADVANCED PERIPHERALS TECHNOLOGIES, INC. +00A07B DAWN COMPUTER INCORPORATION +00A07C TONYANG NYLON CO., LTD. +00A07D SEEQ TECHNOLOGY, INC. +00A07E AVID TECHNOLOGY, INC. +00A07F GSM-SYNTEL, LTD. +00A080 ANTARES MICROSYSTEMS +00A081 ALCATEL DATA NETWORKS +00A082 NKT ELEKTRONIK A/S +00A083 ASIMMPHONY TURKEY +00A084 DATAPLEX PTY. LTD. +00A086 AMBER WAVE SYSTEMS, INC. +00A087 Zarlink Semiconductor Ltd. +00A088 ESSENTIAL COMMUNICATIONS +00A089 XPOINT TECHNOLOGIES, INC. +00A08A BROOKTROUT TECHNOLOGY, INC. +00A08B ASTON ELECTRONIC DESIGNS LTD. +00A08C MultiMedia LANs, Inc. +00A08D JACOMO CORPORATION +00A08E Nokia Internet Communications +00A08F DESKNET SYSTEMS, INC. +00A090 TimeStep Corporation +00A091 APPLICOM INTERNATIONAL +00A092 H. BOLLMANN MANUFACTURERS, LTD +00A093 B/E AEROSPACE, Inc. +00A094 COMSAT CORPORATION +00A095 ACACIA NETWORKS, INC. +00A096 MITUMI ELECTRIC CO., LTD. +00A097 JC INFORMATION SYSTEMS +00A098 NETWORK APPLIANCE CORP. +00A099 K-NET LTD. +00A09A NIHON KOHDEN AMERICA +00A09B QPSX COMMUNICATIONS, LTD. +00A09C Xyplex, Inc. +00A09D JOHNATHON FREEMAN TECHNOLOGIES +00A09E ICTV +00A09F COMMVISION CORP. +00A0A0 COMPACT DATA, LTD. +00A0A1 EPIC DATA INC. +00A0A2 DIGICOM S.P.A. +00A0A3 RELIABLE POWER METERS +00A0A4 MICROS SYSTEMS, INC. +00A0A5 TEKNOR MICROSYSTEME, INC. +00A0A6 M.I. SYSTEMS, K.K. +00A0A7 VORAX CORPORATION +00A0A8 RENEX CORPORATION +00A0A9 GN NETTEST (CANADA) NAVTEL DIVISION +00A0AA SPACELABS MEDICAL +00A0AB NETCS INFORMATIONSTECHNIK GMBH +00A0AC GILAT SATELLITE NETWORKS, LTD. +00A0AD MARCONI SPA +00A0AE NUCOM SYSTEMS, INC. +00A0AF WMS INDUSTRIES +00A0B0 I-O DATA DEVICE, INC. +00A0B1 FIRST VIRTUAL CORPORATION +00A0B2 SHIMA SEIKI +00A0B3 ZYKRONIX +00A0B4 TEXAS MICROSYSTEMS, INC. +00A0B5 3H TECHNOLOGY +00A0B6 SANRITZ AUTOMATION CO., LTD. +00A0B7 CORDANT, INC. +00A0B8 SYMBIOS LOGIC INC. +00A0B9 EAGLE TECHNOLOGY, INC. +00A0BA PATTON ELECTRONICS CO. +00A0BB HILAN GMBH +00A0BC VIASAT, INCORPORATED +00A0BD I-TECH CORP. +00A0BE INTEGRATED CIRCUIT SYSTEMS, INC. COMMUNICATIONS GROUP +00A0BF WIRELESS DATA GROUP MOTOROLA +00A0C0 DIGITAL LINK CORP. +00A0C1 ORTIVUS MEDICAL AB +00A0C2 R.A. SYSTEMS CO., LTD. +00A0C3 UNICOMPUTER GMBH +00A0C4 CRISTIE ELECTRONICS LTD. +00A0C5 ZYXEL COMMUNICATION +00A0C6 QUALCOMM INCORPORATED +00A0C7 TADIRAN TELECOMMUNICATIONS +00A0C8 ADTRAN INC. +00A0C9 INTEL CORPORATION - HF1-06 +00A0CA FUJITSU DENSO LTD. +00A0CB ARK TELECOMMUNICATIONS, INC. +00A0CC LITE-ON COMMUNICATIONS, INC. +00A0CD DR. JOHANNES HEIDENHAIN GmbH +00A0CE ASTROCOM CORPORATION +00A0CF SOTAS, INC. +00A0D0 TEN X TECHNOLOGY, INC. +00A0D1 INVENTEC CORPORATION +00A0D2 ALLIED TELESIS INTERNATIONAL CORPORATION +00A0D3 INSTEM COMPUTER SYSTEMS, LTD. +00A0D4 RADIOLAN, INC. +00A0D5 SIERRA WIRELESS INC. +00A0D6 SBE, INC. +00A0D7 KASTEN CHASE APPLIED RESEARCH +00A0D8 SPECTRA - TEK +00A0D9 CONVEX COMPUTER CORPORATION +00A0DA INTEGRATED SYSTEMS Technology, Inc. +00A0DB FISHER & PAYKEL PRODUCTION +00A0DC O.N. ELECTRONIC CO., LTD. +00A0DD AZONIX CORPORATION +00A0DE YAMAHA CORPORATION +00A0DF STS TECHNOLOGIES, INC. +00A0E0 TENNYSON TECHNOLOGIES PTY LTD +00A0E1 WESTPORT RESEARCH ASSOCIATES, INC. +00A0E2 KEISOKU GIKEN CORP. +00A0E3 XKL SYSTEMS CORP. +00A0E4 OPTIQUEST +00A0E5 NHC COMMUNICATIONS +00A0E6 DIALOGIC CORPORATION +00A0E7 CENTRAL DATA CORPORATION +00A0E8 REUTERS HOLDINGS PLC +00A0E9 ELECTRONIC RETAILING SYSTEMS INTERNATIONAL +00A0EA ETHERCOM CORP. +00A0EB Encore Networks +00A0EC TRANSMITTON LTD. +00A0ED PRI AUTOMATION +00A0EE NASHOBA NETWORKS +00A0EF LUCIDATA LTD. +00A0F0 TORONTO MICROELECTRONICS INC. +00A0F1 MTI +00A0F2 INFOTEK COMMUNICATIONS, INC. +00A0F3 STAUBLI +00A0F4 GE +00A0F5 RADGUARD LTD. +00A0F6 AutoGas Systems Inc. +00A0F7 V.I COMPUTER CORP. +00A0F8 SYMBOL TECHNOLOGIES, INC. +00A0F9 BINTEC COMMUNICATIONS GMBH +00A0FA Marconi Communication GmbH +00A0FB TORAY ENGINEERING CO., LTD. +00A0FC IMAGE SCIENCES, INC. +00A0FD SCITEX DIGITAL PRINTING, INC. +00A0FE BOSTON TECHNOLOGY, INC. +00A0FF TELLABS OPERATIONS, INC. +00AA00 INTEL CORPORATION +00AA01 INTEL CORPORATION +00AA02 INTEL CORPORATION +00AA3C OLIVETTI TELECOM SPA (OLTECO) +00B009 Grass Valley Group +00B017 InfoGear Technology Corp. +00B019 Casi-Rusco +00B01C Westport Technologies +00B01E Rantic Labs, Inc. +00B02A ORSYS GmbH +00B02D ViaGate Technologies, Inc. +00B03B HiQ Networks +00B048 Marconi Communications Inc. +00B04A Cisco Systems, Inc. +00B052 Intellon Corporation +00B064 Cisco Systems, Inc. +00B069 Honewell Oy +00B06D Jones Futurex Inc. +00B080 Mannesmann Ipulsys B.V. +00B086 LocSoft Limited +00B08E Cisco Systems, Inc. +00B091 Transmeta Corp. +00B094 Alaris, Inc. +00B09A Morrow Technologies Corp. +00B09D Point Grey Research Inc. +00B0AC SIAE-Microelettronica S.p.A. +00B0AE Symmetricom +00B0B3 Xstreamis PLC +00B0C2 Cisco Systems, Inc. +00B0C7 Tellabs Operations, Inc. +00B0CE TECHNOLOGY RESCUE +00B0D0 Dell Computer Corp. +00B0DB Nextcell, Inc. +00B0DF Reliable Data Technology, Inc. +00B0E7 British Federal Ltd. +00B0EC EACEM +00B0EE Ajile Systems, Inc. +00B0F0 CALY NETWORKS +00B0F5 NetWorth Technologies, Inc. +00BB01 OCTOTHORPE CORP. +00BBF0 UNGERMANN-BASS INC. +00C000 LANOPTICS, LTD. +00C001 DIATEK PATIENT MANAGMENT +00C002 SERCOMM CORPORATION +00C003 GLOBALNET COMMUNICATIONS +00C004 JAPAN BUSINESS COMPUTER CO.LTD +00C005 LIVINGSTON ENTERPRISES, INC. +00C006 NIPPON AVIONICS CO., LTD. +00C007 PINNACLE DATA SYSTEMS, INC. +00C008 SECO SRL +00C009 KT TECHNOLOGY (S) PTE LTD +00C00A MICRO CRAFT +00C00B NORCONTROL A.S. +00C00C RELIA TECHNOLGIES +00C00D ADVANCED LOGIC RESEARCH, INC. +00C00E PSITECH, INC. +00C00F QUANTUM SOFTWARE SYSTEMS LTD. +00C010 HIRAKAWA HEWTECH CORP. +00C011 INTERACTIVE COMPUTING DEVICES +00C012 NETSPAN CORPORATION +00C013 NETRIX +00C014 TELEMATICS CALABASAS INT'L,INC +00C015 NEW MEDIA CORPORATION +00C016 ELECTRONIC THEATRE CONTROLS +00C017 FORTE NETWORKS +00C018 LANART CORPORATION +00C019 LEAP TECHNOLOGY, INC. +00C01A COROMETRICS MEDICAL SYSTEMS +00C01B SOCKET COMMUNICATIONS, INC. +00C01C INTERLINK COMMUNICATIONS LTD. +00C01D GRAND JUNCTION NETWORKS, INC. +00C01E LA FRANCAISE DES JEUX +00C01F S.E.R.C.E.L. +00C020 ARCO ELECTRONIC, CONTROL LTD. +00C021 NETEXPRESS +00C022 LASERMASTER TECHNOLOGIES, INC. +00C023 TUTANKHAMON ELECTRONICS +00C024 EDEN SISTEMAS DE COMPUTACAO SA +00C025 DATAPRODUCTS CORPORATION +00C026 LANS TECHNOLOGY CO., LTD. +00C027 CIPHER SYSTEMS, INC. +00C028 JASCO CORPORATION +00C029 Nexans Deutschland AG - ANS +00C02A OHKURA ELECTRIC CO., LTD. +00C02B GERLOFF GESELLSCHAFT FUR +00C02C CENTRUM COMMUNICATIONS, INC. +00C02D FUJI PHOTO FILM CO., LTD. +00C02E NETWIZ +00C02F OKUMA CORPORATION +00C030 INTEGRATED ENGINEERING B. V. +00C031 DESIGN RESEARCH SYSTEMS, INC. +00C032 I-CUBED LIMITED +00C033 TELEBIT COMMUNICATIONS APS +00C034 TRANSACTION NETWORK +00C035 QUINTAR COMPANY +00C036 RAYTECH ELECTRONIC CORP. +00C037 DYNATEM +00C038 RASTER IMAGE PROCESSING SYSTEM +00C039 TDK SEMICONDUCTOR CORPORATION +00C03A MEN-MIKRO ELEKTRONIK GMBH +00C03B MULTIACCESS COMPUTING CORP. +00C03C TOWER TECH S.R.L. +00C03D WIESEMANN & THEIS GMBH +00C03E FA. GEBR. HELLER GMBH +00C03F STORES AUTOMATED SYSTEMS, INC. +00C040 ECCI +00C041 DIGITAL TRANSMISSION SYSTEMS +00C042 DATALUX CORP. +00C043 STRATACOM +00C044 EMCOM CORPORATION +00C045 ISOLATION SYSTEMS, LTD. +00C046 KEMITRON LTD. +00C047 UNIMICRO SYSTEMS, INC. +00C048 BAY TECHNICAL ASSOCIATES +00C049 U.S. ROBOTICS, INC. +00C04A GROUP 2000 AG +00C04B CREATIVE MICROSYSTEMS +00C04C DEPARTMENT OF FOREIGN AFFAIRS +00C04D MITEC, INC. +00C04E COMTROL CORPORATION +00C04F DELL COMPUTER CORPORATION +00C050 TOYO DENKI SEIZO K.K. +00C051 ADVANCED INTEGRATION RESEARCH +00C052 BURR-BROWN +00C053 DAVOX CORPORATION +00C054 NETWORK PERIPHERALS, LTD. +00C055 MODULAR COMPUTING TECHNOLOGIES +00C056 SOMELEC +00C057 MYCO ELECTRONICS +00C058 DATAEXPERT CORP. +00C059 NIPPON DENSO CO., LTD. +00C05A SEMAPHORE COMMUNICATIONS CORP. +00C05B NETWORKS NORTHWEST, INC. +00C05C ELONEX PLC +00C05D L&N TECHNOLOGIES +00C05E VARI-LITE, INC. +00C05F FINE-PAL COMPANY LIMITED +00C060 ID SCANDINAVIA AS +00C061 SOLECTEK CORPORATION +00C062 IMPULSE TECHNOLOGY +00C063 MORNING STAR TECHNOLOGIES, INC +00C064 GENERAL DATACOMM IND. INC. +00C065 SCOPE COMMUNICATIONS, INC. +00C066 DOCUPOINT, INC. +00C067 UNITED BARCODE INDUSTRIES +00C068 PHILIP DRAKE ELECTRONICS LTD. +00C069 Axxcelera Broadband Wireless +00C06A ZAHNER-ELEKTRIK GMBH & CO. KG +00C06B OSI PLUS CORPORATION +00C06C SVEC COMPUTER CORP. +00C06D BOCA RESEARCH, INC. +00C06E HAFT TECHNOLOGY, INC. +00C06F KOMATSU LTD. +00C070 SECTRA SECURE-TRANSMISSION AB +00C071 AREANEX COMMUNICATIONS, INC. +00C072 KNX LTD. +00C073 XEDIA CORPORATION +00C074 TOYODA AUTOMATIC LOOM +00C075 XANTE CORPORATION +00C076 I-DATA INTERNATIONAL A-S +00C077 DAEWOO TELECOM LTD. +00C078 COMPUTER SYSTEMS ENGINEERING +00C079 FONSYS CO.,LTD. +00C07A PRIVA B.V. +00C07B ASCEND COMMUNICATIONS, INC. +00C07C HIGHTECH INFORMATION +00C07D RISC DEVELOPMENTS LTD. +00C07E KUBOTA CORPORATION ELECTRONIC +00C07F NUPON COMPUTING CORP. +00C080 NETSTAR, INC. +00C081 METRODATA LTD. +00C082 MOORE PRODUCTS CO. +00C083 TRACE MOUNTAIN PRODUCTS, INC. +00C084 DATA LINK CORP. LTD. +00C085 ELECTRONICS FOR IMAGING, INC. +00C086 THE LYNK CORPORATION +00C087 UUNET TECHNOLOGIES, INC. +00C088 EKF ELEKTRONIK GMBH +00C089 TELINDUS DISTRIBUTION +00C08A LAUTERBACH DATENTECHNIK GMBH +00C08B RISQ MODULAR SYSTEMS, INC. +00C08C PERFORMANCE TECHNOLOGIES, INC. +00C08D TRONIX PRODUCT DEVELOPMENT +00C08E NETWORK INFORMATION TECHNOLOGY +00C08F MATSUSHITA ELECTRIC WORKS, LTD +00C090 PRAIM S.R.L. +00C091 JABIL CIRCUIT, INC. +00C092 MENNEN MEDICAL INC. +00C093 ALTA RESEARCH CORP. +00C094 VMX INC. +00C095 ZNYX +00C096 TAMURA CORPORATION +00C097 ARCHIPEL SA +00C098 CHUNTEX ELECTRONIC CO., LTD. +00C099 YOSHIKI INDUSTRIAL CO.,LTD. +00C09A PHOTONICS CORPORATION +00C09B RELIANCE COMM/TEC, R-TEC +00C09C TOA ELECTRONIC LTD. +00C09D DISTRIBUTED SYSTEMS INT'L, INC +00C09E CACHE COMPUTERS, INC. +00C09F QUANTA COMPUTER, INC. +00C0A0 ADVANCE MICRO RESEARCH, INC. +00C0A1 TOKYO DENSHI SEKEI CO. +00C0A2 INTERMEDIUM A/S +00C0A3 DUAL ENTERPRISES CORPORATION +00C0A4 UNIGRAF OY +00C0A5 DICKENS DATA SYSTEMS +00C0A6 EXICOM AUSTRALIA PTY. LTD +00C0A7 SEEL LTD. +00C0A8 GVC CORPORATION +00C0A9 BARRON MCCANN LTD. +00C0AA SILICON VALLEY COMPUTER +00C0AB Telco Systems, Inc. +00C0AC GAMBIT COMPUTER COMMUNICATIONS +00C0AD MARBEN COMMUNICATION SYSTEMS +00C0AE TOWERCOM CO. INC. DBA PC HOUSE +00C0AF TEKLOGIX INC. +00C0B0 GCC TECHNOLOGIES,INC. +00C0B1 GENIUS NET CO. +00C0B2 NORAND CORPORATION +00C0B3 COMSTAT DATACOMM CORPORATION +00C0B4 MYSON TECHNOLOGY, INC. +00C0B5 CORPORATE NETWORK SYSTEMS,INC. +00C0B6 Snap Appliance, Inc. +00C0B7 AMERICAN POWER CONVERSION CORP +00C0B8 FRASER'S HILL LTD. +00C0B9 FUNK SOFTWARE, INC. +00C0BA NETVANTAGE +00C0BB FORVAL CREATIVE, INC. +00C0BC TELECOM AUSTRALIA/CSSC +00C0BD INEX TECHNOLOGIES, INC. +00C0BE ALCATEL - SEL +00C0BF TECHNOLOGY CONCEPTS, LTD. +00C0C0 SHORE MICROSYSTEMS, INC. +00C0C1 QUAD/GRAPHICS, INC. +00C0C2 INFINITE NETWORKS LTD. +00C0C3 ACUSON COMPUTED SONOGRAPHY +00C0C4 COMPUTER OPERATIONAL +00C0C5 SID INFORMATICA +00C0C6 PERSONAL MEDIA CORP. +00C0C7 SPARKTRUM MICROSYSTEMS, INC. +00C0C8 MICRO BYTE PTY. LTD. +00C0C9 ELSAG BAILEY PROCESS +00C0CA ALFA, INC. +00C0CB CONTROL TECHNOLOGY CORPORATION +00C0CC TELESCIENCES CO SYSTEMS, INC. +00C0CD COMELTA, S.A. +00C0CE CEI SYSTEMS & ENGINEERING PTE +00C0CF IMATRAN VOIMA OY +00C0D0 RATOC SYSTEM INC. +00C0D1 COMTREE TECHNOLOGY CORPORATION +00C0D2 SYNTELLECT, INC. +00C0D3 OLYMPUS IMAGE SYSTEMS, INC. +00C0D4 AXON NETWORKS, INC. +00C0D5 QUANCOM ELECTRONIC GMBH +00C0D6 J1 SYSTEMS, INC. +00C0D7 TAIWAN TRADING CENTER DBA +00C0D8 UNIVERSAL DATA SYSTEMS +00C0D9 QUINTE NETWORK CONFIDENTIALITY +00C0DA NICE SYSTEMS LTD. +00C0DB IPC CORPORATION (PTE) LTD. +00C0DC EOS TECHNOLOGIES, INC. +00C0DD QLogic Corporation +00C0DE ZCOMM, INC. +00C0DF KYE Systems Corp. +00C0E0 DSC COMMUNICATION CORP. +00C0E1 SONIC SOLUTIONS +00C0E2 CALCOMP, INC. +00C0E3 OSITECH COMMUNICATIONS, INC. +00C0E4 SIEMENS BUILDING +00C0E5 GESPAC, S.A. +00C0E6 Verilink Corporation +00C0E7 FIBERDATA AB +00C0E8 PLEXCOM, INC. +00C0E9 OAK SOLUTIONS, LTD. +00C0EA ARRAY TECHNOLOGY LTD. +00C0EB SEH COMPUTERTECHNIK GMBH +00C0EC DAUPHIN TECHNOLOGY +00C0ED US ARMY ELECTRONIC +00C0EE KYOCERA CORPORATION +00C0EF ABIT CORPORATION +00C0F0 KINGSTON TECHNOLOGY CORP. +00C0F1 SHINKO ELECTRIC CO., LTD. +00C0F2 TRANSITION NETWORKS +00C0F3 NETWORK COMMUNICATIONS CORP. +00C0F4 INTERLINK SYSTEM CO., LTD. +00C0F5 METACOMP, INC. +00C0F6 CELAN TECHNOLOGY INC. +00C0F7 ENGAGE COMMUNICATION, INC. +00C0F8 ABOUT COMPUTING INC. +00C0F9 HARRIS AND JEFFRIES, INC. +00C0FA CANARY COMMUNICATIONS, INC. +00C0FB ADVANCED TECHNOLOGY LABS +00C0FC ELASTIC REALITY, INC. +00C0FD PROSUM +00C0FE APTEC COMPUTER SYSTEMS, INC. +00C0FF DOT HILL SYSTEMS CORPORATION +00CBBD Cambridge Broadband Ltd. +00CF1C COMMUNICATION MACHINERY CORP. +00D000 FERRAN SCIENTIFIC, INC. +00D001 VST TECHNOLOGIES, INC. +00D002 DITECH CORPORATION +00D003 COMDA ENTERPRISES CORP. +00D004 PENTACOM LTD. +00D005 ZHS ZEITMANAGEMENTSYSTEME +00D006 CISCO SYSTEMS, INC. +00D007 MIC ASSOCIATES, INC. +00D008 MACTELL CORPORATION +00D009 HSING TECH. ENTERPRISE CO. LTD +00D00A LANACCESS TELECOM S.A. +00D00B RHK TECHNOLOGY, INC. +00D00C SNIJDER MICRO SYSTEMS +00D00D MICROMERITICS INSTRUMENT +00D00E PLURIS, INC. +00D00F SPEECH DESIGN GMBH +00D010 CONVERGENT NETWORKS, INC. +00D011 PRISM VIDEO, INC. +00D012 GATEWORKS CORP. +00D013 PRIMEX AEROSPACE COMPANY +00D014 ROOT, INC. +00D015 UNIVEX MICROTECHNOLOGY CORP. +00D016 SCM MICROSYSTEMS, INC. +00D017 SYNTECH INFORMATION CO., LTD. +00D018 QWES. COM, INC. +00D019 DAINIPPON SCREEN CORPORATE +00D01A URMET SUD S.P.A. +00D01B MIMAKI ENGINEERING CO., LTD. +00D01C SBS TECHNOLOGIES, +00D01D FURUNO ELECTRIC CO., LTD. +00D01E PINGTEL CORP. +00D01F CTAM PTY. LTD. +00D020 AIM SYSTEM, INC. +00D021 REGENT ELECTRONICS CORP. +00D022 INCREDIBLE TECHNOLOGIES, INC. +00D023 INFORTREND TECHNOLOGY, INC. +00D024 Cognex Corporation +00D025 XROSSTECH, INC. +00D026 HIRSCHMANN AUSTRIA GMBH +00D027 APPLIED AUTOMATION, INC. +00D028 OMNEON VIDEO NETWORKS +00D029 WAKEFERN FOOD CORPORATION +00D02A Voxent Systems Ltd. +00D02B JETCELL, INC. +00D02C CAMPBELL SCIENTIFIC, INC. +00D02D ADEMCO +00D02E COMMUNICATION AUTOMATION CORP. +00D02F VLSI TECHNOLOGY INC. +00D030 SAFETRAN SYSTEMS CORP. +00D031 INDUSTRIAL LOGIC CORPORATION +00D032 YANO ELECTRIC CO., LTD. +00D033 DALIAN DAXIAN NETWORK +00D034 ORMEC SYSTEMS CORP. +00D035 BEHAVIOR TECH. COMPUTER CORP. +00D036 TECHNOLOGY ATLANTA CORP. +00D037 PHILIPS-DVS-LO BDR +00D038 FIVEMERE, LTD. +00D039 UTILICOM, INC. +00D03A ZONEWORX, INC. +00D03B VISION PRODUCTS PTY. LTD. +00D03C Vieo, Inc. +00D03D GALILEO TECHNOLOGY, LTD. +00D03E ROCKETCHIPS, INC. +00D03F AMERICAN COMMUNICATION +00D040 SYSMATE CO., LTD. +00D041 AMIGO TECHNOLOGY CO., LTD. +00D042 MAHLO GMBH & CO. UG +00D043 ZONAL RETAIL DATA SYSTEMS +00D044 ALIDIAN NETWORKS, INC. +00D045 KVASER AB +00D046 DOLBY LABORATORIES, INC. +00D047 XN TECHNOLOGIES +00D048 ECTON, INC. +00D049 IMPRESSTEK CO., LTD. +00D04A PRESENCE TECHNOLOGY GMBH +00D04B LA CIE GROUP S.A. +00D04C EUROTEL TELECOM LTD. +00D04D DIV OF RESEARCH & STATISTICS +00D04E LOGIBAG +00D04F BITRONICS, INC. +00D050 ISKRATEL +00D051 O2 MICRO, INC. +00D052 ASCEND COMMUNICATIONS, INC. +00D053 CONNECTED SYSTEMS +00D054 SAS INSTITUTE INC. +00D055 KATHREIN-WERKE KG +00D056 SOMAT CORPORATION +00D057 ULTRAK, INC. +00D058 CISCO SYSTEMS, INC. +00D059 AMBIT MICROSYSTEMS CORP. +00D05A SYMBIONICS, LTD. +00D05B ACROLOOP MOTION CONTROL +00D05C TECHNOTREND SYSTEMTECHNIK GMBH +00D05D INTELLIWORXX, INC. +00D05E STRATABEAM TECHNOLOGY, INC. +00D05F VALCOM, INC. +00D060 PANASONIC EUROPEAN +00D061 TREMON ENTERPRISES CO., LTD. +00D062 DIGIGRAM +00D063 CISCO SYSTEMS, INC. +00D064 MULTITEL +00D065 TOKO ELECTRIC +00D066 WINTRISS ENGINEERING CORP. +00D067 CAMPIO COMMUNICATIONS +00D068 IWILL CORPORATION +00D069 TECHNOLOGIC SYSTEMS +00D06A LINKUP SYSTEMS CORPORATION +00D06B SR TELECOM INC. +00D06C SHAREWAVE, INC. +00D06D ACRISON, INC. +00D06E TRENDVIEW RECORDERS LTD. +00D06F KMC CONTROLS +00D070 LONG WELL ELECTRONICS CORP. +00D071 ECHELON CORP. +00D072 BROADLOGIC +00D073 ACN ADVANCED COMMUNICATIONS +00D074 TAQUA SYSTEMS, INC. +00D075 ALARIS MEDICAL SYSTEMS, INC. +00D076 MERRILL LYNCH & CO., INC. +00D077 LUCENT TECHNOLOGIES +00D078 ELTEX OF SWEDEN AB +00D079 CISCO SYSTEMS, INC. +00D07A AMAQUEST COMPUTER CORP. +00D07B COMCAM INTERNATIONAL LTD. +00D07C KOYO ELECTRONICS INC. CO.,LTD. +00D07D COSINE COMMUNICATIONS +00D07E KEYCORP LTD. +00D07F STRATEGY & TECHNOLOGY, LIMITED +00D080 EXABYTE CORPORATION +00D081 REAL TIME DEVICES USA, INC. +00D082 IOWAVE INC. +00D083 INVERTEX, INC. +00D084 NEXCOMM SYSTEMS, INC. +00D085 OTIS ELEVATOR COMPANY +00D086 FOVEON, INC. +00D087 MICROFIRST INC. +00D088 Terayon Communications Systems +00D089 DYNACOLOR, INC. +00D08A PHOTRON USA +00D08B ADVA Limited +00D08C GENOA TECHNOLOGY, INC. +00D08D PHOENIX GROUP, INC. +00D08E NVISION INC. +00D08F ARDENT TECHNOLOGIES, INC. +00D090 CISCO SYSTEMS, INC. +00D091 SMARTSAN SYSTEMS, INC. +00D092 GLENAYRE WESTERN MULTIPLEX +00D093 TQ - COMPONENTS GMBH +00D094 TIMELINE VISTA, INC. +00D095 XYLAN CORPORATION +00D096 3COM EUROPE LTD. +00D097 CISCO SYSTEMS, INC. +00D098 Photon Dynamics Canada Inc. +00D099 ELCARD OY +00D09A FILANET CORPORATION +00D09B SPECTEL LTD. +00D09C KAPADIA COMMUNICATIONS +00D09D VERIS INDUSTRIES +00D09E 2WIRE, INC. +00D09F NOVTEK TEST SYSTEMS +00D0A0 MIPS DENMARK +00D0A1 OSKAR VIERLING GMBH + CO. KG +00D0A2 INTEGRATED DEVICE +00D0A3 VOCAL DATA, INC. +00D0A4 ALANTRO COMMUNICATIONS +00D0A5 AMERICAN ARIUM +00D0A6 LANBIRD TECHNOLOGY CO., LTD. +00D0A7 TOKYO SOKKI KENKYUJO CO., LTD. +00D0A8 NETWORK ENGINES, INC. +00D0A9 SHINANO KENSHI CO., LTD. +00D0AA CHASE COMMUNICATIONS +00D0AB DELTAKABEL TELECOM CV +00D0AC GRAYSON WIRELESS +00D0AD TL INDUSTRIES +00D0AE ORESIS COMMUNICATIONS, INC. +00D0AF CUTLER-HAMMER, INC. +00D0B0 BITSWITCH LTD. +00D0B1 OMEGA ELECTRONICS SA +00D0B2 XIOTECH CORPORATION +00D0B3 DRS FLIGHT SAFETY AND +00D0B4 KATSUJIMA CO., LTD. +00D0B5 IPricot formerly DotCom +00D0B6 CRESCENT NETWORKS, INC. +00D0B7 INTEL CORPORATION +00D0B8 IOMEGA CORP. +00D0B9 MICROTEK INTERNATIONAL, INC. +00D0BA CISCO SYSTEMS, INC. +00D0BB CISCO SYSTEMS, INC. +00D0BC CISCO SYSTEMS, INC. +00D0BD SICAN GMBH +00D0BE EMUTEC INC. +00D0BF PIVOTAL TECHNOLOGIES +00D0C0 CISCO SYSTEMS, INC. +00D0C1 HARMONIC DATA SYSTEMS, LTD. +00D0C2 BALTHAZAR TECHNOLOGY AB +00D0C3 VIVID TECHNOLOGY PTE, LTD. +00D0C4 TERATECH CORPORATION +00D0C5 COMPUTATIONAL SYSTEMS, INC. +00D0C6 THOMAS & BETTS CORP. +00D0C7 PATHWAY, INC. +00D0C8 I/O CONSULTING A/S +00D0C9 ADVANTECH CO., LTD. +00D0CA INTRINSYC SOFTWARE INC. +00D0CB DASAN CO., LTD. +00D0CC TECHNOLOGIES LYRE INC. +00D0CD ATAN TECHNOLOGY INC. +00D0CE ASYST ELECTRONIC +00D0CF MORETON BAY +00D0D0 ZHONGXING TELECOM LTD. +00D0D1 SIROCCO SYSTEMS, INC. +00D0D2 EPILOG CORPORATION +00D0D3 CISCO SYSTEMS, INC. +00D0D4 V-BITS, INC. +00D0D5 GRUNDIG AG +00D0D6 AETHRA TELECOMUNICAZIONI +00D0D7 B2C2, INC. +00D0D8 3Com Corporation +00D0D9 DEDICATED MICROCOMPUTERS +00D0DA TAICOM DATA SYSTEMS CO., LTD. +00D0DB MCQUAY INTERNATIONAL +00D0DC MODULAR MINING SYSTEMS, INC. +00D0DD SUNRISE TELECOM, INC. +00D0DE PHILIPS MULTIMEDIA NETWORK +00D0DF KUZUMI ELECTRONICS, INC. +00D0E0 DOOIN ELECTRONICS CO. +00D0E1 AVIONITEK ISRAEL INC. +00D0E2 MRT MICRO, INC. +00D0E3 ELE-CHEM ENGINEERING CO., LTD. +00D0E4 CISCO SYSTEMS, INC. +00D0E5 SOLIDUM SYSTEMS CORP. +00D0E6 IBOND INC. +00D0E7 VCON TELECOMMUNICATION LTD. +00D0E8 MAC SYSTEM CO., LTD. +00D0E9 ADVANTAGE CENTURY +00D0EA NEXTONE COMMUNICATIONS, INC. +00D0EB LIGHTERA NETWORKS, INC. +00D0EC NAKAYO TELECOMMUNICATIONS, INC +00D0ED XIOX +00D0EE DICTAPHONE CORPORATION +00D0EF IGT +00D0F0 CONVISION TECHNOLOGY GMBH +00D0F1 SEGA ENTERPRISES, LTD. +00D0F2 MONTEREY NETWORKS +00D0F3 SOLARI DI UDINE SPA +00D0F4 CARINTHIAN TECH INSTITUTE +00D0F5 ORANGE MICRO, INC. +00D0F6 Alcatel Canada +00D0F7 NEXT NETS CORPORATION +00D0F8 FUJIAN STAR TERMINAL +00D0F9 ACUTE COMMUNICATIONS CORP. +00D0FA RACAL GUARDATA +00D0FB TEK MICROSYSTEMS, INCORPORATED +00D0FC GRANITE MICROSYSTEMS +00D0FD OPTIMA TELE.COM, INC. +00D0FE ASTRAL POINT +00D0FF CISCO SYSTEMS, INC. +00DD00 UNGERMANN-BASS INC. +00DD01 UNGERMANN-BASS INC. +00DD02 UNGERMANN-BASS INC. +00DD03 UNGERMANN-BASS INC. +00DD04 UNGERMANN-BASS INC. +00DD05 UNGERMANN-BASS INC. +00DD06 UNGERMANN-BASS INC. +00DD07 UNGERMANN-BASS INC. +00DD08 UNGERMANN-BASS INC. +00DD09 UNGERMANN-BASS INC. +00DD0A UNGERMANN-BASS INC. +00DD0B UNGERMANN-BASS INC. +00DD0C UNGERMANN-BASS INC. +00DD0D UNGERMANN-BASS INC. +00DD0E UNGERMANN-BASS INC. +00DD0F UNGERMANN-BASS INC. +00E000 FUJITSU, LTD +00E001 STRAND LIGHTING LIMITED +00E002 CROSSROADS SYSTEMS, INC. +00E003 NOKIA WIRELESS BUSINESS COMMUN +00E004 PMC-SIERRA, INC. +00E005 TECHNICAL CORP. +00E006 SILICON INTEGRATED SYS. CORP. +00E007 NETWORK ALCHEMY LTD. +00E008 AMAZING CONTROLS! INC. +00E009 MARATHON TECHNOLOGIES CORP. +00E00A DIBA, INC. +00E00B ROOFTOP COMMUNICATIONS CORP. +00E00C MOTOROLA +00E00D RADIANT SYSTEMS +00E00E AVALON IMAGING SYSTEMS, INC. +00E00F SHANGHAI BAUD DATA +00E010 HESS SB-AUTOMATENBAU GmbH +00E011 UNIDEN SAN DIEGO R&D CENTER, INC. +00E012 PLUTO TECHNOLOGIES INTERNATIONAL INC. +00E013 EASTERN ELECTRONIC CO., LTD. +00E014 CISCO SYSTEMS, INC. +00E015 HEIWA CORPORATION +00E016 RAPID CITY COMMUNICATIONS +00E017 EXXACT GmbH +00E018 ASUSTEK COMPUTER INC. +00E019 ING. GIORDANO ELETTRONICA +00E01A COMTEC SYSTEMS. CO., LTD. +00E01B SPHERE COMMUNICATIONS, INC. +00E01C MOBILITY ELECTRONICSY +00E01D WebTV NETWORKS, INC. +00E01E CISCO SYSTEMS, INC. +00E01F AVIDIA Systems, Inc. +00E020 TECNOMEN OY +00E021 FREEGATE CORP. +00E022 MediaLight, Inc. +00E023 TELRAD +00E024 GADZOOX NETWORKS +00E025 dit CO., LTD. +00E026 EASTMAN KODAK CO. +00E027 DUX, INC. +00E028 APTIX CORPORATION +00E029 STANDARD MICROSYSTEMS CORP. +00E02A TANDBERG TELEVISION AS +00E02B EXTREME NETWORKS +00E02C AST COMPUTER +00E02D InnoMediaLogic, Inc. +00E02E SPC ELECTRONICS CORPORATION +00E02F MCNS HOLDINGS, L.P. +00E030 MELITA INTERNATIONAL CORP. +00E031 HAGIWARA ELECTRIC CO., LTD. +00E032 MISYS FINANCIAL SYSTEMS, LTD. +00E033 E.E.P.D. GmbH +00E034 CISCO SYSTEMS, INC. +00E035 LOUGHBOROUGH SOUND IMAGES, PLC +00E036 PIONEER CORPORATION +00E037 CENTURY CORPORATION +00E038 PROXIMA CORPORATION +00E039 PARADYNE CORP. +00E03A CABLETRON SYSTEMS, INC. +00E03B PROMINET CORPORATION +00E03C AdvanSys +00E03D FOCON ELECTRONIC SYSTEMS A/S +00E03E ALFATECH, INC. +00E03F JATON CORPORATION +00E040 DeskStation Technology, Inc. +00E041 CSPI +00E042 Pacom Systems Ltd. +00E043 VitalCom +00E044 LSICS CORPORATION +00E045 TOUCHWAVE, INC. +00E046 BENTLY NEVADA CORP. +00E047 INFOCUS SYSTEMS +00E048 SDL COMMUNICATIONS, INC. +00E049 MICROWI ELECTRONIC GmbH +00E04A ENHANCED MESSAGING SYSTEMS, INC +00E04B JUMP INDUSTRIELLE COMPUTERTECHNIK GmbH +00E04C REALTEK SEMICONDUCTOR CORP. +00E04D INTERNET INITIATIVE JAPAN, INC +00E04E SANYO DENKI CO., LTD. +00E04F CISCO SYSTEMS, INC. +00E050 EXECUTONE INFORMATION SYSTEMS, INC. +00E051 TALX CORPORATION +00E052 FOUNDRY NETWORKS, INC. +00E053 CELLPORT LABS, INC. +00E054 KODAI HITEC CO., LTD. +00E055 INGENIERIA ELECTRONICA COMERCIAL INELCOM S.A. +00E056 HOLONTECH CORPORATION +00E057 HAN MICROTELECOM. CO., LTD. +00E058 PHASE ONE DENMARK A/S +00E059 CONTROLLED ENVIRONMENTS, LTD. +00E05A GALEA NETWORK SECURITY +00E05B WEST END SYSTEMS CORP. +00E05C MATSUSHITA KOTOBUKI ELECTRONICS INDUSTRIES, LTD. +00E05D UNITEC CO., LTD. +00E05E JAPAN AVIATION ELECTRONICS INDUSTRY, LTD. +00E05F e-Net, Inc. +00E060 SHERWOOD +00E061 EdgePoint Networks, Inc. +00E062 HOST ENGINEERING +00E063 CABLETRON - YAGO SYSTEMS, INC. +00E064 SAMSUNG ELECTRONICS +00E065 OPTICAL ACCESS INTERNATIONAL +00E066 ProMax Systems, Inc. +00E067 eac AUTOMATION-CONSULTING GmbH +00E068 MERRIMAC SYSTEMS INC. +00E069 JAYCOR +00E06A KAPSCH AG +00E06B W&G SPECIAL PRODUCTS +00E06C AEP Systems International Ltd +00E06D COMPUWARE CORPORATION +00E06E FAR SYSTEMS S.p.A. +00E06F Terayon Communications Systems +00E070 DH TECHNOLOGY +00E071 EPIS MICROCOMPUTER +00E072 LYNK +00E073 NATIONAL AMUSEMENT NETWORK, INC. +00E074 TIERNAN COMMUNICATIONS, INC. +00E075 Verilink Corporation +00E076 DEVELOPMENT CONCEPTS, INC. +00E077 WEBGEAR, INC. +00E078 BERKELEY NETWORKS +00E079 A.T.N.R. +00E07A MIKRODIDAKT AB +00E07B BAY NETWORKS +00E07C METTLER-TOLEDO, INC. +00E07D NETRONIX, INC. +00E07E WALT DISNEY IMAGINEERING +00E07F LOGISTISTEM s.r.l. +00E080 CONTROL RESOURCES CORPORATION +00E081 TYAN COMPUTER CORP. +00E082 ANERMA +00E083 JATO TECHNOLOGIES, INC. +00E084 COMPULITE R&D +00E085 GLOBAL MAINTECH, INC. +00E086 CYBEX COMPUTER PRODUCTS +00E087 LeCroy - Networking Productions Division +00E088 LTX CORPORATION +00E089 ION Networks, Inc. +00E08A GEC AVERY, LTD. +00E08B QLogic Corp. +00E08C NEOPARADIGM LABS, INC. +00E08D PRESSURE SYSTEMS, INC. +00E08E UTSTARCOM +00E08F CISCO SYSTEMS, INC. +00E090 BECKMAN LAB. AUTOMATION DIV. +00E091 LG ELECTRONICS, INC. +00E092 ADMTEK INCORPORATED +00E093 ACKFIN NETWORKS +00E094 OSAI SRL +00E095 ADVANCED-VISION TECHNOLGIES CORP. +00E096 SHIMADZU CORPORATION +00E097 CARRIER ACCESS CORPORATION +00E098 AboCom Systems, Inc. +00E099 SAMSON AG +00E09A POSITRON INDUSTRIES, INC. +00E09B ENGAGE NETWORKS, INC. +00E09C MII +00E09D SARNOFF CORPORATION +00E09E QUANTUM CORPORATION +00E09F PIXEL VISION +00E0A0 WILTRON CO. +00E0A1 HIMA PAUL HILDEBRANDT GmbH Co. KG +00E0A2 MICROSLATE INC. +00E0A3 CISCO SYSTEMS, INC. +00E0A4 ESAOTE S.p.A. +00E0A5 ComCore Semiconductor, Inc. +00E0A6 TELOGY NETWORKS, INC. +00E0A7 IPC INFORMATION SYSTEMS, INC. +00E0A8 SAT GmbH & Co. +00E0A9 FUNAI ELECTRIC CO., LTD. +00E0AA ELECTROSONIC LTD. +00E0AB DIMAT S.A. +00E0AC MIDSCO, INC. +00E0AD EES TECHNOLOGY, LTD. +00E0AE XAQTI CORPORATION +00E0AF GENERAL DYNAMICS INFORMATION SYSTEMS +00E0B0 CISCO SYSTEMS, INC. +00E0B1 PACKET ENGINES, INC. +00E0B2 TELMAX COMMUNICATIONS CORP. +00E0B3 EtherWAN Systems, Inc. +00E0B4 TECHNO SCOPE CO., LTD. +00E0B5 ARDENT COMMUNICATIONS CORP. +00E0B6 Entrada Networks +00E0B7 PI GROUP, LTD. +00E0B8 GATEWAY 2000 +00E0B9 BYAS SYSTEMS +00E0BA BERGHOF AUTOMATIONSTECHNIK GmbH +00E0BB NBX CORPORATION +00E0BC SYMON COMMUNICATIONS, INC. +00E0BD INTERFACE SYSTEMS, INC. +00E0BE GENROCO INTERNATIONAL, INC. +00E0BF TORRENT NETWORKING TECHNOLOGIES CORP. +00E0C0 SEIWA ELECTRIC MFG. CO., LTD. +00E0C1 MEMOREX TELEX JAPAN, LTD. +00E0C2 NECSY S.p.A. +00E0C3 SAKAI SYSTEM DEVELOPMENT CORP. +00E0C4 HORNER ELECTRIC, INC. +00E0C5 BCOM ELECTRONICS INC. +00E0C6 LINK2IT, L.L.C. +00E0C7 EUROTECH SRL +00E0C8 VIRTUAL ACCESS, LTD. +00E0C9 AutomatedLogic Corporation +00E0CA BEST DATA PRODUCTS +00E0CB RESON, INC. +00E0CC HERO SYSTEMS, LTD. +00E0CD SENSIS CORPORATION +00E0CE ARN +00E0CF INTEGRATED DEVICE TECHNOLOGY, INC. +00E0D0 NETSPEED, INC. +00E0D1 TELSIS LIMITED +00E0D2 VERSANET COMMUNICATIONS, INC. +00E0D3 DATENTECHNIK GmbH +00E0D4 EXCELLENT COMPUTER +00E0D5 ARCXEL TECHNOLOGIES, INC. +00E0D6 COMPUTER & COMMUNICATION RESEARCH LAB. +00E0D7 SUNSHINE ELECTRONICS, INC. +00E0D8 LANBit Computer, Inc. +00E0D9 TAZMO CO., LTD. +00E0DA ASSURED ACCESS TECHNOLOGY, INC. +00E0DB ViaVideo Communications, Inc. +00E0DC NEXWARE CORP. +00E0DD ZENITH ELECTRONICS CORPORATION +00E0DE DATAX NV +00E0DF KE KOMMUNIKATIONS-ELECTRONIK +00E0E0 SI ELECTRONICS, LTD. +00E0E1 G2 NETWORKS, INC. +00E0E2 INNOVA CORP. +00E0E3 SK-ELEKTRONIK GmbH +00E0E4 FANUC ROBOTICS NORTH AMERICA, Inc. +00E0E5 CINCO NETWORKS, INC. +00E0E6 INCAA DATACOM B.V. +00E0E7 RAYTHEON E-SYSTEMS, INC. +00E0E8 GRETACODER Data Systems AG +00E0E9 DATA LABS, INC. +00E0EA INNOVAT COMMUNICATIONS, INC. +00E0EB DIGICOM SYSTEMS, INCORPORATED +00E0EC CELESTICA INC. +00E0ED SILICOM, LTD. +00E0EE MAREL HF +00E0EF DIONEX +00E0F0 ABLER TECHNOLOGY, INC. +00E0F1 THAT CORPORATION +00E0F2 ARLOTTO COMNET, INC. +00E0F3 WebSprint Communications, Inc. +00E0F4 INSIDE Technology A/S +00E0F5 TELES AG +00E0F6 DECISION EUROPE +00E0F7 CISCO SYSTEMS, INC. +00E0F8 DICNA CONTROL AB +00E0F9 CISCO SYSTEMS, INC. +00E0FA TRL TECHNOLOGY, LTD. +00E0FB LEIGHTRONIX, INC. +00E0FC HUAWEI TECHNOLOGIES CO., LTD. +00E0FD A-TREND TECHNOLOGY CO., LTD. +00E0FE CISCO SYSTEMS, INC. +00E0FF SECURITY DYNAMICS TECHNOLOGIES, Inc. +00E6D3 NIXDORF COMPUTER CORP. +020701 RACAL-DATACOM +021C7C PERQ SYSTEMS CORPORATION +026086 LOGIC REPLACEMENT TECH. LTD. +02608C 3COM CORPORATION +027001 RACAL-DATACOM +0270B0 M/A-COM INC. COMPANIES +0270B3 DATA RECALL LTD +029D8E CARDIAC RECORDERS INC. +02AA3C OLIVETTI TELECOMM SPA (OLTECO) +02BB01 OCTOTHORPE CORP. +02C08C 3COM CORPORATION +02CF1C COMMUNICATION MACHINERY CORP. +02E6D3 NIXDORF COMPUTER CORPORATION +040AE0 XMIT AG COMPUTER NETWORKS +04E0C4 TRIUMPH-ADLER AG +080001 COMPUTERVISION CORPORATION +080002 BRIDGE COMMUNICATIONS INC. +080003 ADVANCED COMPUTER COMM. +080004 CROMEMCO INCORPORATED +080005 SYMBOLICS INC. +080006 SIEMENS AG +080007 APPLE COMPUTER INC. +080008 BOLT BERANEK AND NEWMAN INC. +080009 HEWLETT PACKARD +08000A NESTAR SYSTEMS INCORPORATED +08000B UNISYS CORPORATION +08000C MIKLYN DEVELOPMENT CO. +08000D INTERNATIONAL COMPUTERS LTD. +08000E NCR CORPORATION +08000F MITEL CORPORATION +080011 TEKTRONIX INC. +080012 BELL ATLANTIC INTEGRATED SYST. +080013 EXXON +080014 EXCELAN +080015 STC BUSINESS SYSTEMS +080016 BARRISTER INFO SYS CORP +080017 NATIONAL SEMICONDUCTOR +080018 PIRELLI FOCOM NETWORKS +080019 GENERAL ELECTRIC CORPORATION +08001A TIARA/ 10NET +08001B DATA GENERAL +08001C KDD-KOKUSAI DEBNSIN DENWA CO. +08001D ABLE COMMUNICATIONS INC. +08001E APOLLO COMPUTER INC. +08001F SHARP CORPORATION +080020 SUN MICROSYSTEMS INC. +080021 3M COMPANY +080022 NBI INC. +080023 Panasonic Communications Co., Ltd. +080024 10NET COMMUNICATIONS/DCA +080025 CONTROL DATA +080026 NORSK DATA A.S. +080027 CADMUS COMPUTER SYSTEMS +080028 Texas Instruments +080029 MEGATEK CORPORATION +08002A MOSAIC TECHNOLOGIES INC. +08002B DIGITAL EQUIPMENT CORPORATION +08002C BRITTON LEE INC. +08002D LAN-TEC INC. +08002E METAPHOR COMPUTER SYSTEMS +08002F PRIME COMPUTER INC. +080030 NETWORK RESEARCH CORPORATION +080030 CERN +080030 ROYAL MELBOURNE INST OF TECH +080031 LITTLE MACHINES INC. +080032 TIGAN INCORPORATED +080033 BAUSCH & LOMB +080034 FILENET CORPORATION +080035 MICROFIVE CORPORATION +080036 INTERGRAPH CORPORATION +080037 FUJI-XEROX CO. LTD. +080038 CII HONEYWELL BULL +080039 SPIDER SYSTEMS LIMITED +08003A ORCATECH INC. +08003B TORUS SYSTEMS LIMITED +08003C SCHLUMBERGER WELL SERVICES +08003D CADNETIX CORPORATIONS +08003E CODEX CORPORATION +08003F FRED KOSCHARA ENTERPRISES +080040 FERRANTI COMPUTER SYS. LIMITED +080041 RACAL-MILGO INFORMATION SYS.. +080042 JAPAN MACNICS CORP. +080043 PIXEL COMPUTER INC. +080044 DAVID SYSTEMS INC. +080045 CONCURRENT COMPUTER CORP. +080046 SONY CORPORATION LTD. +080047 SEQUENT COMPUTER SYSTEMS INC. +080048 EUROTHERM GAUGING SYSTEMS +080049 UNIVATION +08004A BANYAN SYSTEMS INC. +08004B PLANNING RESEARCH CORP. +08004C HYDRA COMPUTER SYSTEMS INC. +08004D CORVUS SYSTEMS INC. +08004E 3COM EUROPE LTD. +08004F CYGNET SYSTEMS +080050 DAISY SYSTEMS CORP. +080051 EXPERDATA +080052 INSYSTEC +080053 MIDDLE EAST TECH. UNIVERSITY +080055 STANFORD TELECOMM. INC. +080056 STANFORD LINEAR ACCEL. CENTER +080057 EVANS & SUTHERLAND +080058 SYSTEMS CONCEPTS +080059 A/S MYCRON +08005A IBM CORPORATION +08005B VTA TECHNOLOGIES INC. +08005C FOUR PHASE SYSTEMS +08005D GOULD INC. +08005E COUNTERPOINT COMPUTER INC. +08005F SABER TECHNOLOGY CORP. +080060 INDUSTRIAL NETWORKING INC. +080061 JAROGATE LTD. +080062 GENERAL DYNAMICS +080063 PLESSEY +080064 AUTOPHON AG +080065 GENRAD INC. +080066 AGFA CORPORATION +080067 COMDESIGN +080068 RIDGE COMPUTERS +080069 SILICON GRAPHICS INC. +08006A ATT BELL LABORATORIES +08006B ACCEL TECHNOLOGIES INC. +08006C SUNTEK TECHNOLOGY INT'L +08006D WHITECHAPEL COMPUTER WORKS +08006E MASSCOMP +08006F PHILIPS APELDOORN B.V. +080070 MITSUBISHI ELECTRIC CORP. +080071 MATRA (DSIE) +080072 XEROX CORP UNIV GRANT PROGRAM +080073 TECMAR INC. +080074 CASIO COMPUTER CO. LTD. +080075 DANSK DATA ELECTRONIK +080076 PC LAN TECHNOLOGIES +080077 TSL COMMUNICATIONS LTD. +080078 ACCELL CORPORATION +080079 THE DROID WORKS +08007A INDATA +08007B SANYO ELECTRIC CO. LTD. +08007C VITALINK COMMUNICATIONS CORP. +08007E AMALGAMATED WIRELESS(AUS) LTD +08007F CARNEGIE-MELLON UNIVERSITY +080080 AES DATA INC. +080081 ,ASTECH INC. +080082 VERITAS SOFTWARE +080083 Seiko Instruments Inc. +080084 TOMEN ELECTRONICS CORP. +080085 ELXSI +080086 KONICA MINOLTA HOLDINGS, INC. +080087 XYPLEX +080088 MCDATA CORPORATION +080089 KINETICS +08008A PERFORMANCE TECHNOLOGY +08008B PYRAMID TECHNOLOGY CORP. +08008C NETWORK RESEARCH CORPORATION +08008D XYVISION INC. +08008E TANDEM COMPUTERS +08008F CHIPCOM CORPORATION +080090 SONOMA SYSTEMS +081443 UNIBRAIN S.A. +08BBCC AK-NORD EDV VERTRIEBSGES. mbH +10005A IBM CORPORATION +1000E8 NATIONAL SEMICONDUCTOR +800010 ATT BELL LABORATORIES +A06A00 Verilink Corporation +AA0000 DIGITAL EQUIPMENT CORPORATION +AA0001 DIGITAL EQUIPMENT CORPORATION +AA0002 DIGITAL EQUIPMENT CORPORATION +AA0003 DIGITAL EQUIPMENT CORPORATION +AA0004 DIGITAL EQUIPMENT CORPORATION diff --git a/drivers/ieee1394/oui2c.sh b/drivers/ieee1394/oui2c.sh new file mode 100644 index 0000000..d50dc7a --- /dev/null +++ b/drivers/ieee1394/oui2c.sh @@ -0,0 +1,23 @@ +#!/bin/sh + +cat <<EOF +/* Generated file for OUI database */ + +#include <linux/config.h> + +#ifdef CONFIG_IEEE1394_OUI_DB +struct oui_list_struct { + int oui; + char *name; +} oui_list[] = { +EOF + +while read oui name; do + echo " { 0x$oui, \"$name\" }," +done + +cat <<EOF +}; + +#endif /* CONFIG_IEEE1394_OUI_DB */ +EOF diff --git a/drivers/ieee1394/pcilynx.c b/drivers/ieee1394/pcilynx.c new file mode 100644 index 0000000..a261d2b --- /dev/null +++ b/drivers/ieee1394/pcilynx.c @@ -0,0 +1,1982 @@ +/* + * 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/config.h> +#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 <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 = 0; +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 int bit_reg(struct i2c_client *client) +{ + return 0; +} + +static int bit_unreg(struct i2c_client *client) +{ + return 0; +} + +static struct i2c_algo_bit_data bit_data = { + .setsda = bit_setsda, + .setscl = bit_setscl, + .getsda = bit_getsda, + .getscl = bit_getscl, + .udelay = 5, + .mdelay = 5, + .timeout = 100, +}; + +static struct i2c_adapter bit_ops = { + .id = 0xAA, //FIXME: probably we should get an id in i2c-id.h + .client_register = bit_reg, + .client_unregister = bit_unreg, + .name = "PCILynx I2C", +}; + + + +/* + * 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", + __FUNCTION__, 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", + __FUNCTION__); + 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", __FUNCTION__, addr); + return -1; + } + + if (val > 0xff) { + PRINT(KERN_ERR, lynx->id, + "%s: PHY register value %d out of range", __FUNCTION__, 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", __FUNCTION__, 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", __FUNCTION__, 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; + + d = (what == hpsb_iso ? &lynx->iso_send : &lynx->async); + 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; + case hpsb_iso: + pcl.buffer[0].control |= PCL_CMD_XMT | PCL_ISOMODE; + break; + 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; + case hpsb_iso: + d = &lynx->iso_send; + break; + 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(&lynx->async.queue, &packet_list); + INIT_LIST_HEAD(&lynx->async.queue); + + 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; + struct hpsb_packet *packet; + + 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; + + 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; + + default: + PRINT(KERN_ERR, lynx->id, "unknown devctl command %d", cmd); + retval = -1; + } + + return retval; +} + + +/*************************************** + * IEEE-1394 functionality section END * + ***************************************/ + +#ifdef CONFIG_IEEE1394_PCILYNX_PORTS +/* VFS functions for local bus / aux device access. Access to those + * is implemented as a character device instead of block devices + * because buffers are not wanted for this. Therefore llseek (from + * VFS) can be used for these char devices with obvious effects. + */ +static int mem_open(struct inode*, struct file*); +static int mem_release(struct inode*, struct file*); +static unsigned int aux_poll(struct file*, struct poll_table_struct*); +static loff_t mem_llseek(struct file*, loff_t, int); +static ssize_t mem_read (struct file*, char*, size_t, loff_t*); +static ssize_t mem_write(struct file*, const char*, size_t, loff_t*); + + +static struct file_operations aux_ops = { + .owner = THIS_MODULE, + .read = mem_read, + .write = mem_write, + .poll = aux_poll, + .llseek = mem_llseek, + .open = mem_open, + .release = mem_release, +}; + + +static void aux_setup_pcls(struct ti_lynx *lynx) +{ + struct ti_pcl pcl; + + pcl.next = PCL_NEXT_INVALID; + pcl.user_data = pcl_bus(lynx, lynx->dmem_pcl); + put_pcl(lynx, lynx->dmem_pcl, &pcl); +} + +static int mem_open(struct inode *inode, struct file *file) +{ + int cid = iminor(inode); + enum { t_rom, t_aux, t_ram } type; + struct memdata *md; + + if (cid < PCILYNX_MINOR_AUX_START) { + /* just for completeness */ + return -ENXIO; + } else if (cid < PCILYNX_MINOR_ROM_START) { + cid -= PCILYNX_MINOR_AUX_START; + if (cid >= num_of_cards || !cards[cid].aux_port) + return -ENXIO; + type = t_aux; + } else if (cid < PCILYNX_MINOR_RAM_START) { + cid -= PCILYNX_MINOR_ROM_START; + if (cid >= num_of_cards || !cards[cid].local_rom) + return -ENXIO; + type = t_rom; + } else { + /* WARNING: Know what you are doing when opening RAM. + * It is currently used inside the driver! */ + cid -= PCILYNX_MINOR_RAM_START; + if (cid >= num_of_cards || !cards[cid].local_ram) + return -ENXIO; + type = t_ram; + } + + md = (struct memdata *)kmalloc(sizeof(struct memdata), SLAB_KERNEL); + if (md == NULL) + return -ENOMEM; + + md->lynx = &cards[cid]; + md->cid = cid; + + switch (type) { + case t_rom: + md->type = rom; + break; + case t_ram: + md->type = ram; + break; + case t_aux: + atomic_set(&md->aux_intr_last_seen, + atomic_read(&cards[cid].aux_intr_seen)); + md->type = aux; + break; + } + + file->private_data = md; + + return 0; +} + +static int mem_release(struct inode *inode, struct file *file) +{ + kfree(file->private_data); + return 0; +} + +static unsigned int aux_poll(struct file *file, poll_table *pt) +{ + struct memdata *md = (struct memdata *)file->private_data; + int cid = md->cid; + unsigned int mask; + + /* reading and writing is always allowed */ + mask = POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM; + + if (md->type == aux) { + poll_wait(file, &cards[cid].aux_intr_wait, pt); + + if (atomic_read(&md->aux_intr_last_seen) + != atomic_read(&cards[cid].aux_intr_seen)) { + mask |= POLLPRI; + atomic_inc(&md->aux_intr_last_seen); + } + } + + return mask; +} + +loff_t mem_llseek(struct file *file, loff_t offs, int orig) +{ + loff_t newoffs; + + switch (orig) { + case 0: + newoffs = offs; + break; + case 1: + newoffs = offs + file->f_pos; + break; + case 2: + newoffs = PCILYNX_MAX_MEMORY + 1 + offs; + break; + default: + return -EINVAL; + } + + if (newoffs < 0 || newoffs > PCILYNX_MAX_MEMORY + 1) return -EINVAL; + + file->f_pos = newoffs; + return newoffs; +} + +/* + * do not DMA if count is too small because this will have a serious impact + * on performance - the value 2400 was found by experiment and may not work + * everywhere as good as here - use mem_mindma option for modules to change + */ +static short mem_mindma = 2400; +module_param(mem_mindma, short, 0444); +MODULE_PARM_DESC(mem_mindma, "Minimum amount of data required to use DMA"); + +static ssize_t mem_dmaread(struct memdata *md, u32 physbuf, ssize_t count, + int offset) +{ + pcltmp_t pcltmp; + struct ti_pcl *pcl; + size_t retval; + int i; + DECLARE_WAITQUEUE(wait, current); + + count &= ~3; + count = min(count, 53196); + retval = count; + + if (reg_read(md->lynx, DMA_CHAN_CTRL(CHANNEL_LOCALBUS)) + & DMA_CHAN_CTRL_BUSY) { + PRINT(KERN_WARNING, md->lynx->id, "DMA ALREADY ACTIVE!"); + } + + reg_write(md->lynx, LBUS_ADDR, md->type | offset); + + pcl = edit_pcl(md->lynx, md->lynx->dmem_pcl, &pcltmp); + pcl->buffer[0].control = PCL_CMD_LBUS_TO_PCI | min(count, 4092); + pcl->buffer[0].pointer = physbuf; + count -= 4092; + + i = 0; + while (count > 0) { + i++; + pcl->buffer[i].control = min(count, 4092); + pcl->buffer[i].pointer = physbuf + i * 4092; + count -= 4092; + } + pcl->buffer[i].control |= PCL_LAST_BUFF; + commit_pcl(md->lynx, md->lynx->dmem_pcl, &pcltmp); + + set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue(&md->lynx->mem_dma_intr_wait, &wait); + run_sub_pcl(md->lynx, md->lynx->dmem_pcl, 2, CHANNEL_LOCALBUS); + + schedule(); + while (reg_read(md->lynx, DMA_CHAN_CTRL(CHANNEL_LOCALBUS)) + & DMA_CHAN_CTRL_BUSY) { + if (signal_pending(current)) { + retval = -EINTR; + break; + } + schedule(); + } + + reg_write(md->lynx, DMA_CHAN_CTRL(CHANNEL_LOCALBUS), 0); + remove_wait_queue(&md->lynx->mem_dma_intr_wait, &wait); + + if (reg_read(md->lynx, DMA_CHAN_CTRL(CHANNEL_LOCALBUS)) + & DMA_CHAN_CTRL_BUSY) { + PRINT(KERN_ERR, md->lynx->id, "DMA STILL ACTIVE!"); + } + + return retval; +} + +static ssize_t mem_read(struct file *file, char *buffer, size_t count, + loff_t *offset) +{ + struct memdata *md = (struct memdata *)file->private_data; + ssize_t bcount; + size_t alignfix; + loff_t off = *offset; /* avoid useless 64bit-arithmetic */ + ssize_t retval; + void *membase; + + if ((off + count) > PCILYNX_MAX_MEMORY+1) { + count = PCILYNX_MAX_MEMORY+1 - off; + } + if (count == 0 || off > PCILYNX_MAX_MEMORY) { + return -ENOSPC; + } + + switch (md->type) { + case rom: + membase = md->lynx->local_rom; + break; + case ram: + membase = md->lynx->local_ram; + break; + case aux: + membase = md->lynx->aux_port; + break; + default: + panic("pcilynx%d: unsupported md->type %d in %s", + md->lynx->id, md->type, __FUNCTION__); + } + + down(&md->lynx->mem_dma_mutex); + + if (count < mem_mindma) { + memcpy_fromio(md->lynx->mem_dma_buffer, membase+off, count); + goto out; + } + + bcount = count; + alignfix = 4 - (off % 4); + if (alignfix != 4) { + if (bcount < alignfix) { + alignfix = bcount; + } + memcpy_fromio(md->lynx->mem_dma_buffer, membase+off, + alignfix); + if (bcount == alignfix) { + goto out; + } + bcount -= alignfix; + off += alignfix; + } + + while (bcount >= 4) { + retval = mem_dmaread(md, md->lynx->mem_dma_buffer_dma + + count - bcount, bcount, off); + if (retval < 0) return retval; + + bcount -= retval; + off += retval; + } + + if (bcount) { + memcpy_fromio(md->lynx->mem_dma_buffer + count - bcount, + membase+off, bcount); + } + + out: + retval = copy_to_user(buffer, md->lynx->mem_dma_buffer, count); + up(&md->lynx->mem_dma_mutex); + + if (retval) return -EFAULT; + *offset += count; + return count; +} + + +static ssize_t mem_write(struct file *file, const char *buffer, size_t count, + loff_t *offset) +{ + struct memdata *md = (struct memdata *)file->private_data; + + if (((*offset) + count) > PCILYNX_MAX_MEMORY+1) { + count = PCILYNX_MAX_MEMORY+1 - *offset; + } + if (count == 0 || *offset > PCILYNX_MAX_MEMORY) { + return -ENOSPC; + } + + /* FIXME: dereferencing pointers to PCI mem doesn't work everywhere */ + switch (md->type) { + case aux: + if (copy_from_user(md->lynx->aux_port+(*offset), buffer, count)) + return -EFAULT; + break; + case ram: + if (copy_from_user(md->lynx->local_ram+(*offset), buffer, count)) + return -EFAULT; + break; + case rom: + /* the ROM may be writeable */ + if (copy_from_user(md->lynx->local_rom+(*offset), buffer, count)) + return -EFAULT; + break; + } + + file->f_pos += count; + return count; +} +#endif /* CONFIG_IEEE1394_PCILYNX_PORTS */ + + +/******************************************************** + * Global stuff (interrupt handler, init/shutdown code) * + ********************************************************/ + + +static irqreturn_t lynx_irq_handler(int irq, void *dev_id, + struct pt_regs *regs_are_unused) +{ + 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); + +#ifdef CONFIG_IEEE1394_PCILYNX_PORTS + if (intmask & PCI_INT_AUX_INT) { + atomic_inc(&lynx->aux_intr_seen); + wake_up_interruptible(&lynx->aux_intr_wait); + } + + if (intmask & PCI_INT_DMA_HLT(CHANNEL_LOCALBUS)) { + wake_up_interruptible(&lynx->mem_dma_intr_wait); + } +#endif + + + 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 (!list_empty(&lynx->iso_send.queue)) { + send_next(lynx, hpsb_iso); + } + + 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: +#ifdef CONFIG_IEEE1394_PCILYNX_PORTS + pci_free_consistent(lynx->dev, 65536, lynx->mem_dma_buffer, + lynx->mem_dma_buffer_dma); +#endif + case have_pcl_mem: +#ifndef CONFIG_IEEE1394_PCILYNX_LOCALRAM + pci_free_consistent(lynx->dev, LOCALRAM_SIZE, lynx->pcl_mem, + lynx->pcl_mem_dma); +#endif + 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, 0xffffffff)) + 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); + +#ifndef CONFIG_IEEE1394_PCILYNX_LOCALRAM + 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"); + } +#endif + +#ifdef CONFIG_IEEE1394_PCILYNX_PORTS + lynx->mem_dma_buffer = pci_alloc_consistent(dev, 65536, + &lynx->mem_dma_buffer_dma); + if (lynx->mem_dma_buffer == NULL) { + FAIL("failed to allocate DMA buffer for aux"); + } + lynx->state = have_aux_buf; +#endif + + 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"); + } + +#ifdef CONFIG_IEEE1394_PCILYNX_LOCALRAM + if (lynx->local_ram == NULL) { + FAIL("failed to remap local RAM which is required for " + "operation"); + } +#endif + + 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); + +#ifndef __sparc__ + sprintf (irq_buf, "%d", dev->irq); +#else + sprintf (irq_buf, "%s", __irq_itoa(dev->irq)); +#endif + + if (!request_irq(dev->irq, lynx_irq_handler, SA_SHIRQ, + 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 */ +#ifdef CONFIG_IEEE1394_PCILYNX_PORTS + if (lynx->aux_port != NULL) { + lynx->dmem_pcl = alloc_pcl(lynx); + aux_setup_pcls(lynx); + sema_init(&lynx->mem_dma_mutex, 1); + } +#endif + 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); + +#ifdef CONFIG_IEEE1394_PCILYNX_PORTS + reg_set_bits(lynx, PCI_INT_ENABLE, PCI_INT_AUX_INT); + init_waitqueue_head(&lynx->mem_dma_intr_wait); + init_waitqueue_head(&lynx->aux_intr_wait); +#endif + + 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 = kmalloc(sizeof(struct i2c_adapter), SLAB_KERNEL); + if (!i2c_ad) FAIL("failed to allocate I2C adapter memory"); + + memcpy(i2c_ad, &bit_ops, sizeof(struct i2c_adapter)); + i2c_adapter_data = bit_data; + i2c_ad->algo_data = &i2c_adapter_data; + i2c_adapter_data.data = lynx; + + 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 } + }; + + +#ifdef CONFIG_IEEE1394_VERBOSEDEBUG + union i2c_smbus_data data; + + if (i2c_smbus_xfer(i2c_ad, 80, 0, I2C_SMBUS_WRITE, 0, I2C_SMBUS_BYTE,NULL)) + PRINT(KERN_ERR, lynx->id,"eeprom read start has failed"); + else + { + u16 addr; + for (addr=0x00; addr < 0x100; addr++) { + if (i2c_smbus_xfer(i2c_ad, 80, 0, I2C_SMBUS_READ, 0, I2C_SMBUS_BYTE,& data)) { + PRINT(KERN_ERR, lynx->id, "unable to read i2c %x", addr); + break; + } + else + PRINT(KERN_DEBUG, lynx->id,"got serial eeprom data at %x: %x",addr, data.byte); + } + } +#endif + + /* we use i2c_transfer, because i2c_smbus_read_block_data does not work properly and we + do it more efficiently in one transaction rather then using several reads */ + if (i2c_transfer(i2c_ad, msg, 2) < 0) { + PRINT(KERN_ERR, lynx->id, "unable to read bus info block from i2c"); + } else { + int i; + + 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_bit_del_bus(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; + +#ifdef CONFIG_IEEE1394_PCILYNX_PORTS + if (register_chrdev(PCILYNX_MAJOR, PCILYNX_DRIVER_NAME, &aux_ops)) { + PRINT_G(KERN_ERR, "allocation of char major number %d failed", + PCILYNX_MAJOR); + return -EBUSY; + } +#endif + + ret = pci_register_driver(&lynx_pci_driver); + if (ret < 0) { + PRINT_G(KERN_ERR, "PCI module init failed"); + goto free_char_dev; + } + + return 0; + + free_char_dev: +#ifdef CONFIG_IEEE1394_PCILYNX_PORTS + unregister_chrdev(PCILYNX_MAJOR, PCILYNX_DRIVER_NAME); +#endif + + return ret; +} + +static void __exit pcilynx_cleanup(void) +{ + pci_unregister_driver(&lynx_pci_driver); + +#ifdef CONFIG_IEEE1394_PCILYNX_PORTS + unregister_chrdev(PCILYNX_MAJOR, PCILYNX_DRIVER_NAME); +#endif +} + + +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..644ec55 --- /dev/null +++ b/drivers/ieee1394/pcilynx.h @@ -0,0 +1,516 @@ +#ifndef __PCILYNX_H__ +#define __PCILYNX_H__ + +#include <linux/config.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]; + +#ifdef CONFIG_IEEE1394_PCILYNX_PORTS + atomic_t aux_intr_seen; + wait_queue_head_t aux_intr_wait; + + void *mem_dma_buffer; + dma_addr_t mem_dma_buffer_dma; + struct semaphore mem_dma_mutex; + wait_queue_head_t mem_dma_intr_wait; +#endif + + /* + * 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]; + +#ifndef CONFIG_IEEE1394_PCILYNX_LOCALRAM + /* point to PCLs memory area if needed */ + void *pcl_mem; + dma_addr_t pcl_mem_dma; +#endif + + /* 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)) + + +#ifdef CONFIG_IEEE1394_PCILYNX_LOCALRAM + +static inline void put_pcl(const struct ti_lynx *lynx, pcl_t pclid, + const struct ti_pcl *pcl) +{ + int i; + u32 *in = (u32 *)pcl; + u32 *out = (u32 *)(lynx->local_ram + pclid * sizeof(struct ti_pcl)); + + for (i = 0; i < 32; i++, out++, in++) { + writel(*in, out); + } +} + +static inline void get_pcl(const struct ti_lynx *lynx, pcl_t pclid, + struct ti_pcl *pcl) +{ + int i; + u32 *out = (u32 *)pcl; + u32 *in = (u32 *)(lynx->local_ram + pclid * sizeof(struct ti_pcl)); + + for (i = 0; i < 32; i++, out++, in++) { + *out = readl(in); + } +} + +static inline u32 pcl_bus(const struct ti_lynx *lynx, pcl_t pclid) +{ + return pci_resource_start(lynx->dev, 1) + pclid * sizeof(struct ti_pcl); +} + +#else /* CONFIG_IEEE1394_PCILYNX_LOCALRAM */ + +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); +} + +#endif /* CONFIG_IEEE1394_PCILYNX_LOCALRAM */ + + +#if defined (CONFIG_IEEE1394_PCILYNX_LOCALRAM) || 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..c93587b --- /dev/null +++ b/drivers/ieee1394/raw1394-private.h @@ -0,0 +1,86 @@ +#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; + + enum { opened, initialized, connected } state; + unsigned int protocol_version; + + struct hpsb_host *host; + + struct list_head req_pending; + struct list_head req_complete; + struct semaphore complete_sem; + spinlock_t reqlists_lock; + wait_queue_head_t poll_wait_complete; + + struct list_head addr_list; + + u8 __user *fcp_buffer; + + /* old ISO API */ + u64 listen_channels; + quadlet_t __user *iso_buffer; + size_t iso_buffer_length; + + 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 */ +}; + +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; +}; + +#endif /* IEEE1394_RAW1394_PRIVATE_H */ diff --git a/drivers/ieee1394/raw1394.c b/drivers/ieee1394/raw1394.c new file mode 100644 index 0000000..6a08a89 --- /dev/null +++ b/drivers/ieee1394/raw1394.c @@ -0,0 +1,2958 @@ +/* + * 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/init.h> +#include <linux/smp_lock.h> +#include <linux/interrupt.h> +#include <linux/vmalloc.h> +#include <linux/cdev.h> +#include <asm/uaccess.h> +#include <asm/atomic.h> +#include <linux/devfs_fs_kernel.h> + +#include "csr1212.h" +#include "ieee1394.h" +#include "ieee1394_types.h" +#include "ieee1394_core.h" +#include "nodemgr.h" +#include "hosts.h" +#include "highlevel.h" +#include "iso.h" +#include "ieee1394_transactions.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...) +#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(int flags) +{ + struct pending_request *req; + + req = (struct pending_request *)kmalloc(sizeof(struct pending_request), + flags); + if (req != NULL) { + memset(req, 0, sizeof(struct pending_request)); + INIT_LIST_HEAD(&req->list); + } + + return req; +} + +static inline struct pending_request *alloc_pending_request(void) +{ + return __alloc_pending_request(SLAB_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_del(&req->list); + list_add_tail(&req->list, &fi->req_complete); + + up(&fi->complete_sem); + wake_up_interruptible(&fi->poll_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 = (struct host_info *)kmalloc(sizeof(struct host_info), GFP_KERNEL); + + if (hi != NULL) { + 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(SLAB_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 iso_receive(struct hpsb_host *host, int channel, quadlet_t * 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 iso packet"); + 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->listen_channels & (1ULL << channel))) + continue; + + req = __alloc_pending_request(SLAB_ATOMIC); + if (!req) + break; + + if (!ibs) { + ibs = kmalloc(sizeof(struct iso_block_store) + + length, SLAB_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_ISO_RECEIVE; + req->req.generation = get_hpsb_generation(host); + req->req.misc = 0; + req->req.recvb = ptr2int(fi->iso_buffer); + req->req.length = min(length, fi->iso_buffer_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); +} + +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(SLAB_ATOMIC); + if (!req) + break; + + if (!ibs) { + ibs = kmalloc(sizeof(struct iso_block_store) + + length, SLAB_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); +} + +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 list_head *lh; + struct pending_request *req; + ssize_t ret; + + if (count != sizeof(struct raw1394_request)) { + return -EINVAL; + } + + if (!access_ok(VERIFY_WRITE, buffer, count)) { + return -EFAULT; + } + + if (file->f_flags & O_NONBLOCK) { + if (down_trylock(&fi->complete_sem)) { + return -EAGAIN; + } + } else { + if (down_interruptible(&fi->complete_sem)) { + return -ERESTARTSYS; + } + } + + spin_lock_irq(&fi->reqlists_lock); + lh = fi->req_complete.next; + list_del(lh); + spin_unlock_irq(&fi->reqlists_lock); + + req = list_entry(lh, struct pending_request, list); + + if (req->req.length) { + if (copy_to_user(int2ptr(req->req.recvb), req->data, + req->req.length)) { + req->req.error = RAW1394_ERROR_MEMFAULT; + } + } + 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 sizeof(struct raw1394_request); +} + +static int state_initialized(struct file_info *fi, struct pending_request *req) +{ + 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 sizeof(struct raw1394_request); + } + + switch (req->req.type) { + case RAW1394_REQ_LIST_CARDS: + spin_lock_irq(&host_info_lock); + khl = kmalloc(sizeof(struct raw1394_khost_list) * host_count, + SLAB_ATOMIC); + + if (khl != NULL) { + 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_irq(&host_info_lock); + + if (khl != NULL) { + 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_irq(&host_info_lock); + if (req->req.misc < host_count) { + list_for_each_entry(hi, &host_info_list, list) { + if (!req->req.misc--) + break; + } + get_device(&hi->host->device); // XXX Need to handle failure case + list_add_tail(&fi->list, &hi->file_info_list); + 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; + } + } else { + req->req.error = RAW1394_ERROR_INVALID_ARG; + } + spin_unlock_irq(&host_info_lock); + + req->req.length = 0; + break; + + default: + req->req.error = RAW1394_ERROR_STATE_ORDER; + req->req.length = 0; + break; + } + + queue_complete_req(req); + return sizeof(struct raw1394_request); +} + +static void handle_iso_listen(struct file_info *fi, struct pending_request *req) +{ + int channel = req->req.misc; + + spin_lock_irq(&host_info_lock); + if ((channel > 63) || (channel < -64)) { + req->req.error = RAW1394_ERROR_INVALID_ARG; + } else if (channel >= 0) { + /* allocate channel req.misc */ + if (fi->listen_channels & (1ULL << channel)) { + req->req.error = RAW1394_ERROR_ALREADY; + } else { + if (hpsb_listen_channel + (&raw1394_highlevel, fi->host, channel)) { + req->req.error = RAW1394_ERROR_ALREADY; + } else { + fi->listen_channels |= 1ULL << channel; + fi->iso_buffer = int2ptr(req->req.recvb); + fi->iso_buffer_length = req->req.length; + } + } + } else { + /* deallocate channel (one's complement neg) req.misc */ + channel = ~channel; + + if (fi->listen_channels & (1ULL << channel)) { + hpsb_unlisten_channel(&raw1394_highlevel, fi->host, + channel); + fi->listen_channels &= ~(1ULL << channel); + } else { + req->req.error = RAW1394_ERROR_INVALID_ARG; + } + } + + req->req.length = 0; + queue_complete_req(req); + spin_unlock_irq(&host_info_lock); +} + +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) +{ + 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 sizeof(struct raw1394_request); + } + + hpsb_set_packet_complete_task(packet, + (void (*)(void *))queue_complete_cb, req); + + spin_lock_irq(&fi->reqlists_lock); + list_add_tail(&req->list, &fi->req_pending); + spin_unlock_irq(&fi->reqlists_lock); + + 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 sizeof(struct raw1394_request); +} + +static int handle_iso_send(struct file_info *fi, struct pending_request *req, + int channel) +{ + struct hpsb_packet *packet; + + packet = hpsb_make_isopacket(fi->host, req->req.length, channel & 0x3f, + (req->req.misc >> 16) & 0x3, + req->req.misc & 0xf); + if (!packet) + return -ENOMEM; + + packet->speed_code = req->req.address & 0x3; + + req->packet = packet; + + if (copy_from_user(packet->data, int2ptr(req->req.sendb), + req->req.length)) { + req->req.error = RAW1394_ERROR_MEMFAULT; + req->req.length = 0; + queue_complete_req(req); + return sizeof(struct raw1394_request); + } + + req->req.length = 0; + hpsb_set_packet_complete_task(packet, + (void (*)(void *))queue_complete_req, + req); + + spin_lock_irq(&fi->reqlists_lock); + list_add_tail(&req->list, &fi->req_pending); + spin_unlock_irq(&fi->reqlists_lock); + + /* 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 sizeof(struct raw1394_request); +} + +static int handle_async_send(struct file_info *fi, struct pending_request *req) +{ + struct hpsb_packet *packet; + int header_length = req->req.misc & 0xffff; + int expect_response = req->req.misc >> 16; + + if ((header_length > req->req.length) || (header_length < 12)) { + req->req.error = RAW1394_ERROR_INVALID_ARG; + req->req.length = 0; + queue_complete_req(req); + return sizeof(struct raw1394_request); + } + + packet = hpsb_alloc_packet(req->req.length - header_length); + 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 sizeof(struct raw1394_request); + } + + if (copy_from_user + (packet->data, int2ptr(req->req.sendb) + header_length, + packet->data_size)) { + req->req.error = RAW1394_ERROR_MEMFAULT; + req->req.length = 0; + queue_complete_req(req); + return sizeof(struct raw1394_request); + } + + 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 = req->req.length - header_length; + + req->req.length = 0; + hpsb_set_packet_complete_task(packet, + (void (*)(void *))queue_complete_cb, req); + + spin_lock_irq(&fi->reqlists_lock); + list_add_tail(&req->list, &fi->req_pending); + spin_unlock_irq(&fi->reqlists_lock); + + /* 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 sizeof(struct raw1394_request); +} + +static int arm_read(struct hpsb_host *host, int nodeid, quadlet_t * buffer, + u64 addr, size_t length, u16 flags) +{ + 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(&host_info_lock); + 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(&host_info_lock); + 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(SLAB_ATOMIC); + if (!req) { + DBGMSG("arm_read -> rcode_conflict_error"); + spin_unlock(&host_info_lock); + 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, SLAB_ATOMIC); + if (!(req->data)) { + free_pending_request(req); + DBGMSG("arm_read -> rcode_conflict_error"); + spin_unlock(&host_info_lock); + 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(&host_info_lock); + return (rcode); +} + +static int arm_write(struct hpsb_host *host, int nodeid, int destid, + quadlet_t * data, u64 addr, size_t length, u16 flags) +{ + 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(&host_info_lock); + 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(&host_info_lock); + 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(SLAB_ATOMIC); + if (!req) { + DBGMSG("arm_write -> rcode_conflict_error"); + spin_unlock(&host_info_lock); + 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, SLAB_ATOMIC); + if (!(req->data)) { + free_pending_request(req); + DBGMSG("arm_write -> rcode_conflict_error"); + spin_unlock(&host_info_lock); + 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(&host_info_lock); + 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) +{ + 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(&host_info_lock); + 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(&host_info_lock); + 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(SLAB_ATOMIC); + if (!req) { + DBGMSG("arm_lock -> rcode_conflict_error"); + spin_unlock(&host_info_lock); + 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, SLAB_ATOMIC); + if (!(req->data)) { + free_pending_request(req); + DBGMSG("arm_lock -> rcode_conflict_error"); + spin_unlock(&host_info_lock); + 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(&host_info_lock); + 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) +{ + 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(&host_info_lock); + 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(&host_info_lock); + 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(SLAB_ATOMIC); + if (!req) { + spin_unlock(&host_info_lock); + 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, SLAB_ATOMIC); + if (!(req->data)) { + free_pending_request(req); + spin_unlock(&host_info_lock); + 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(&host_info_lock); + 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 = (struct arm_addr *)kmalloc(sizeof(struct arm_addr), SLAB_KERNEL); + if (!addr) { + req->req.length = 0; + return (-ENOMEM); + } + /* allocation of addr_space_buffer */ + addr->addr_space_buffer = (u8 *) 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 */ + vfree(addr->addr_space_buffer); + kfree(addr); + spin_unlock_irqrestore(&host_info_lock, flags); + 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; + } + } + } + } + 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); + spin_unlock_irqrestore(&host_info_lock, flags); + return (-EFAULT); + } + free_pending_request(req); /* immediate success or fail */ + /* INSERT ENTRY */ + list_add_tail(&addr->addr_list, &fi->addr_list); + spin_unlock_irqrestore(&host_info_lock, flags); + return sizeof(struct raw1394_request); + } + retval = + hpsb_register_addrspace(&raw1394_highlevel, fi->host, &arm_ops, + req->req.address, + req->req.address + req->req.length); + if (retval) { + /* INSERT ENTRY */ + list_add_tail(&addr->addr_list, &fi->addr_list); + } else { + DBGMSG("arm_register failed errno: %d \n", retval); + vfree(addr->addr_space_buffer); + kfree(addr); + spin_unlock_irqrestore(&host_info_lock, flags); + return (-EALREADY); + } + spin_unlock_irqrestore(&host_info_lock, flags); + free_pending_request(req); /* immediate success or fail */ + return sizeof(struct raw1394_request); +} + +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); + vfree(addr->addr_space_buffer); + kfree(addr); + free_pending_request(req); /* immediate success or fail */ + spin_unlock_irqrestore(&host_info_lock, flags); + return sizeof(struct raw1394_request); + } + 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 sizeof(struct raw1394_request); +} + +/* 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; + + 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)) { + spin_unlock_irqrestore(&host_info_lock, + flags); + return (-EFAULT); + } + + spin_unlock_irqrestore(&host_info_lock, flags); + /* We have to free the request, because we + * queue no response, and therefore nobody + * will free it. */ + free_pending_request(req); + return sizeof(struct raw1394_request); + } 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; + + 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)) { + spin_unlock_irqrestore(&host_info_lock, + flags); + return (-EFAULT); + } + + spin_unlock_irqrestore(&host_info_lock, flags); + free_pending_request(req); /* we have to free the request, because we queue no response, and therefore nobody will free it */ + return sizeof(struct raw1394_request); + } 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 sizeof(struct raw1394_request); + } + /* 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; + + 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_irq(&fi->reqlists_lock); + list_add_tail(&req->list, &fi->req_pending); + spin_unlock_irq(&fi->reqlists_lock); + 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 sizeof(struct raw1394_request); +} + +static int get_config_rom(struct file_info *fi, struct pending_request *req) +{ + int ret = sizeof(struct raw1394_request); + quadlet_t *data = kmalloc(req->req.length, SLAB_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 = sizeof(struct raw1394_request); + quadlet_t *data = kmalloc(req->req.length, SLAB_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 sizeof(struct raw1394_request); + } + } + + 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(struct csr1212_cache_region), 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; + CSR1212_FREE(cache); + 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); + kfree(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 sizeof(struct raw1394_request); + } 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 sizeof(struct raw1394_request); + + case RAW1394_REQ_ISO_SEND: + return handle_iso_send(fi, req, node); + + 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_LISTEN: + handle_iso_listen(fi, req); + return sizeof(struct raw1394_request); + + case RAW1394_REQ_FCP_LISTEN: + handle_fcp_listen(fi, req); + return sizeof(struct raw1394_request); + + 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 sizeof(struct raw1394_request); + } + 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 sizeof(struct raw1394_request); + } + /* 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 sizeof(struct raw1394_request); + } + + 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 sizeof(struct raw1394_request); + } + + 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 = 0; + + 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; + } + + 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; + } + + if (retval < 0) { + free_pending_request(req); + } + + 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(SLAB_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) +{ + 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 = atomic_read(&iso->overflows); + 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); + + 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 > hpsb_iso_n_ready(fi->iso_handle)) + return -EINVAL; + + /* 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; +} + +/* 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; + + if (fi->iso_state == RAW1394_ISO_INACTIVE) + return -EINVAL; + + return dma_region_mmap(&fi->iso_handle->data_buf, file, vma); +} + +/* ioctl is only used for rawiso operations */ +static int raw1394_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct file_info *fi = file->private_data; + void __user *argp = (void __user *)arg; + + switch (fi->iso_state) { + case RAW1394_ISO_INACTIVE: + 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: + break; + } + break; + case RAW1394_ISO_RECV: + switch (cmd) { + case RAW1394_IOC_ISO_RECV_START:{ + /* copy args from user-space */ + 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:{ + /* copy the u64 from user-space */ + 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; + } + break; + case RAW1394_ISO_XMIT: + switch (cmd) { + case RAW1394_IOC_ISO_XMIT_START:{ + /* copy two ints from user-space */ + 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; + } + break; + default: + break; + } + + return -EINVAL; +} + +static unsigned int raw1394_poll(struct file *file, poll_table * pt) +{ + struct file_info *fi = file->private_data; + unsigned int mask = POLLOUT | POLLWRNORM; + + poll_wait(file, &fi->poll_wait_complete, pt); + + spin_lock_irq(&fi->reqlists_lock); + if (!list_empty(&fi->req_complete)) { + mask |= POLLIN | POLLRDNORM; + } + spin_unlock_irq(&fi->reqlists_lock); + + return mask; +} + +static int raw1394_open(struct inode *inode, struct file *file) +{ + struct file_info *fi; + + fi = kmalloc(sizeof(struct file_info), SLAB_KERNEL); + if (fi == NULL) + return -ENOMEM; + + memset(fi, 0, sizeof(struct file_info)); + fi->notification = (u8) RAW1394_NOTIFY_ON; /* busreset notification */ + + INIT_LIST_HEAD(&fi->list); + fi->state = opened; + INIT_LIST_HEAD(&fi->req_pending); + INIT_LIST_HEAD(&fi->req_complete); + sema_init(&fi->complete_sem, 0); + spin_lock_init(&fi->reqlists_lock); + init_waitqueue_head(&fi->poll_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 done = 0, i, fail = 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; + int csr_mod = 0; + + if (fi->iso_state != RAW1394_ISO_INACTIVE) + raw1394_iso_shutdown(fi); + + for (i = 0; i < 64; i++) { + if (fi->listen_channels & (1ULL << i)) { + hpsb_unlisten_channel(&raw1394_highlevel, fi->host, i); + } + } + + spin_lock_irq(&host_info_lock); + fi->listen_channels = 0; + spin_unlock_irq(&host_info_lock); + + fail = 0; + /* set address-entries invalid */ + spin_lock_irq(&host_info_lock); + + 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_irq(&host_info_lock); + if (fail > 0) { + printk(KERN_ERR "raw1394: during addr_list-release " + "error(s) occurred \n"); + } + + while (!done) { + spin_lock_irq(&fi->reqlists_lock); + + while (!list_empty(&fi->req_complete)) { + lh = fi->req_complete.next; + list_del(lh); + + req = list_entry(lh, struct pending_request, list); + + free_pending_request(req); + } + + if (list_empty(&fi->req_pending)) + done = 1; + + spin_unlock_irq(&fi->reqlists_lock); + + if (!done) + down_interruptible(&fi->complete_sem); + } + + /* 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_irq(&host_info_lock); + list_del(&fi->list); + spin_unlock_irq(&host_info_lock); + + put_device(&fi->host->device); + } + + kfree(fi); + + return 0; +} + +/*** HOTPLUG STUFF **********************************************************/ +/* + * Export information about protocols/devices supported by this driver. + */ +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); + +static struct hpsb_protocol_driver raw1394_driver = { + .name = "raw1394 Driver", + .id_table = raw1394_id_table, + .driver = { + .name = "raw1394", + .bus = &ieee1394_bus_type, + }, +}; + +/******************************************************************************/ + +static struct hpsb_highlevel raw1394_highlevel = { + .name = RAW1394_DEVICE_NAME, + .add_host = add_host, + .remove_host = remove_host, + .host_reset = host_reset, + .iso_receive = iso_receive, + .fcp_request = fcp_request, +}; + +static struct cdev raw1394_cdev; +static struct file_operations raw1394_fops = { + .owner = THIS_MODULE, + .read = raw1394_read, + .write = raw1394_write, + .mmap = raw1394_mmap, + .ioctl = raw1394_ioctl, + .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(class_simple_device_add(hpsb_protocol_class, MKDEV( + IEEE1394_MAJOR, IEEE1394_MINOR_BLOCK_RAW1394 * 16), + NULL, RAW1394_DEVICE_NAME))) { + ret = -EFAULT; + goto out_unreg; + } + + devfs_mk_cdev(MKDEV( + IEEE1394_MAJOR, IEEE1394_MINOR_BLOCK_RAW1394 * 16), + S_IFCHR | S_IRUSR | S_IWUSR, RAW1394_DEVICE_NAME); + + cdev_init(&raw1394_cdev, &raw1394_fops); + raw1394_cdev.owner = THIS_MODULE; + kobject_set_name(&raw1394_cdev.kobj, RAW1394_DEVICE_NAME); + 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: + devfs_remove(RAW1394_DEVICE_NAME); + class_simple_device_remove(MKDEV( + IEEE1394_MAJOR, IEEE1394_MINOR_BLOCK_RAW1394 * 16)); +out_unreg: + hpsb_unregister_highlevel(&raw1394_highlevel); +out: + return ret; +} + +static void __exit cleanup_raw1394(void) +{ + class_simple_device_remove(MKDEV( + IEEE1394_MAJOR, IEEE1394_MINOR_BLOCK_RAW1394 * 16)); + cdev_del(&raw1394_cdev); + devfs_remove(RAW1394_DEVICE_NAME); + hpsb_unregister_highlevel(&raw1394_highlevel); + hpsb_unregister_protocol(&raw1394_driver); +} + +module_init(init_raw1394); +module_exit(cleanup_raw1394); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_CHARDEV(IEEE1394_MAJOR, IEEE1394_MINOR_BLOCK_RAW1394 * 16); diff --git a/drivers/ieee1394/raw1394.h b/drivers/ieee1394/raw1394.h new file mode 100644 index 0000000..35bfc38 --- /dev/null +++ b/drivers/ieee1394/raw1394.h @@ -0,0 +1,181 @@ +#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 +#define RAW1394_REQ_ASYNC_SEND 105 +#define RAW1394_REQ_ASYNC_STREAM 106 + +#define RAW1394_REQ_ISO_LISTEN 200 +#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; +}; + +#endif /* IEEE1394_RAW1394_H */ diff --git a/drivers/ieee1394/sbp2.c b/drivers/ieee1394/sbp2.c new file mode 100644 index 0000000..00c7b95 --- /dev/null +++ b/drivers/ieee1394/sbp2.c @@ -0,0 +1,2864 @@ +/* + * 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 storage devices as if they were SCSI + * devices (e.g. mount /dev/sda1, fdisk, mkfs, etc.). + * + * Current Issues: + * + * - Error Handling: SCSI aborts and bus reset requests are handled somewhat + * but the code needs additional debugging. + */ + +#include <linux/config.h> +#include <linux/kernel.h> +#include <linux/list.h> +#include <linux/string.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/fs.h> +#include <linux/poll.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/types.h> +#include <linux/delay.h> +#include <linux/sched.h> +#include <linux/blkdev.h> +#include <linux/smp_lock.h> +#include <linux/init.h> +#include <linux/pci.h> + +#include <asm/current.h> +#include <asm/uaccess.h> +#include <asm/io.h> +#include <asm/byteorder.h> +#include <asm/atomic.h> +#include <asm/system.h> +#include <asm/scatterlist.h> + +#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 "ieee1394.h" +#include "ieee1394_types.h" +#include "ieee1394_core.h" +#include "nodemgr.h" +#include "hosts.h" +#include "highlevel.h" +#include "ieee1394_transactions.h" +#include "sbp2.h" + +static char version[] __devinitdata = + "$Rev: 1219 $ Ben Collins <bcollins@debian.org>"; + +/* + * 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 max_speed = IEEE1394_SPEED_MAX; +module_param(max_speed, int, 0644); +MODULE_PARM_DESC(max_speed, "Force max speed (3 = 800mb, 2 = 400mb default, 1 = 200mb, 0 = 100mb)"); + +/* + * Set serialize_io to 1 if you'd like only one scsi command sent + * down to us at a time (debugging). This might be necessary for very + * badly behaved sbp2 devices. + */ +static int serialize_io = 0; +module_param(serialize_io, int, 0444); +MODULE_PARM_DESC(serialize_io, "Serialize all I/O coming down from the scsi drivers (default = 0)"); + +/* + * Bump up max_sectors if you'd like to support very large sized + * transfers. Please note that some older sbp2 bridge chips are broken for + * transfers greater or equal to 128KB. Default is a value of 255 + * sectors, or just under 128KB (at 512 byte sector size). I can note that + * the Oxsemi sbp2 chipsets have no problems supporting very large + * transfer sizes. + */ +static int max_sectors = SBP2_MAX_SECTORS; +module_param(max_sectors, int, 0444); +MODULE_PARM_DESC(max_sectors, "Change max sectors per I/O supported (default = 255)"); + +/* + * 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 (one such filesystem is OpenGFS, + * see opengfs.sourceforge.net for more info), then set exclusive_login + * to zero. Note: The Oxsemi OXFW911 sbp2 chipset supports up to four + * concurrent logins. + */ +static int exclusive_login = 1; +module_param(exclusive_login, int, 0644); +MODULE_PARM_DESC(exclusive_login, "Exclusive login to sbp2 device (default = 1)"); + +/* + * SCSI inquiry hack for really badly behaved sbp2 devices. Turn this on + * if your sbp2 device is not properly handling the SCSI inquiry command. + * This hack makes the inquiry look more like a typical MS Windows + * inquiry. + * + * If force_inquiry_hack=1 is required for your device to work, + * please submit the logged sbp2_firmware_revision value of this device to + * the linux1394-devel mailing list. + */ +static int force_inquiry_hack = 0; +module_param(force_inquiry_hack, int, 0444); +MODULE_PARM_DESC(force_inquiry_hack, "Force SCSI inquiry hack (default = 0)"); + + +/* + * Export information about protocols/devices supported by this driver. + */ +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); + +/* + * Debug levels, configured via kernel config, or enable here. + */ + +/* #define CONFIG_IEEE1394_SBP2_DEBUG_ORBS */ +/* #define CONFIG_IEEE1394_SBP2_DEBUG_DMA */ +/* #define CONFIG_IEEE1394_SBP2_DEBUG 1 */ +/* #define CONFIG_IEEE1394_SBP2_DEBUG 2 */ +/* #define CONFIG_IEEE1394_SBP2_PACKET_DUMP */ + +#ifdef CONFIG_IEEE1394_SBP2_DEBUG_ORBS +#define SBP2_ORB_DEBUG(fmt, args...) HPSB_ERR("sbp2(%s): "fmt, __FUNCTION__, ## args) +static u32 global_outstanding_command_orbs = 0; +#define outstanding_orb_incr global_outstanding_command_orbs++ +#define outstanding_orb_decr global_outstanding_command_orbs-- +#else +#define SBP2_ORB_DEBUG(fmt, args...) +#define outstanding_orb_incr +#define outstanding_orb_decr +#endif + +#ifdef CONFIG_IEEE1394_SBP2_DEBUG_DMA +#define SBP2_DMA_ALLOC(fmt, args...) \ + HPSB_ERR("sbp2(%s)alloc(%d): "fmt, __FUNCTION__, \ + ++global_outstanding_dmas, ## args) +#define SBP2_DMA_FREE(fmt, args...) \ + HPSB_ERR("sbp2(%s)free(%d): "fmt, __FUNCTION__, \ + --global_outstanding_dmas, ## args) +static u32 global_outstanding_dmas = 0; +#else +#define SBP2_DMA_ALLOC(fmt, args...) +#define SBP2_DMA_FREE(fmt, args...) +#endif + +#if CONFIG_IEEE1394_SBP2_DEBUG >= 2 +#define SBP2_DEBUG(fmt, args...) HPSB_ERR("sbp2: "fmt, ## args) +#define SBP2_INFO(fmt, args...) HPSB_ERR("sbp2: "fmt, ## args) +#define SBP2_NOTICE(fmt, args...) HPSB_ERR("sbp2: "fmt, ## args) +#define SBP2_WARN(fmt, args...) HPSB_ERR("sbp2: "fmt, ## args) +#elif CONFIG_IEEE1394_SBP2_DEBUG == 1 +#define SBP2_DEBUG(fmt, args...) HPSB_DEBUG("sbp2: "fmt, ## args) +#define SBP2_INFO(fmt, args...) HPSB_INFO("sbp2: "fmt, ## args) +#define SBP2_NOTICE(fmt, args...) HPSB_NOTICE("sbp2: "fmt, ## args) +#define SBP2_WARN(fmt, args...) HPSB_WARN("sbp2: "fmt, ## args) +#else +#define SBP2_DEBUG(fmt, args...) +#define SBP2_INFO(fmt, args...) HPSB_INFO("sbp2: "fmt, ## args) +#define SBP2_NOTICE(fmt, args...) HPSB_NOTICE("sbp2: "fmt, ## args) +#define SBP2_WARN(fmt, args...) HPSB_WARN("sbp2: "fmt, ## args) +#endif + +#define SBP2_ERR(fmt, args...) HPSB_ERR("sbp2: "fmt, ## args) + + +/* + * Globals + */ + +static void sbp2scsi_complete_all_commands(struct scsi_id_instance_data *scsi_id, + u32 status); + +static void sbp2scsi_complete_command(struct scsi_id_instance_data *scsi_id, + u32 scsi_status, struct scsi_cmnd *SCpnt, + void (*done)(struct scsi_cmnd *)); + +static struct scsi_host_template scsi_driver_template; + +static const u8 sbp2_speedto_max_payload[] = { 0x7, 0x8, 0x9, 0xA, 0xB, 0xC }; + +static void sbp2_host_reset(struct hpsb_host *host); + +static int sbp2_probe(struct device *dev); +static int sbp2_remove(struct device *dev); +static int sbp2_update(struct unit_directory *ud); + +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 struct hpsb_address_ops sbp2_physdma_ops = { + .read = sbp2_handle_physdma_read, + .write = sbp2_handle_physdma_write, +}; +#endif + +static struct hpsb_protocol_driver sbp2_driver = { + .name = "SBP2 Driver", + .id_table = sbp2_id_table, + .update = sbp2_update, + .driver = { + .name = SBP2_DEVICE_NAME, + .bus = &ieee1394_bus_type, + .probe = sbp2_probe, + .remove = sbp2_remove, + }, +}; + + +/* List of device firmware's that require a forced 36 byte inquiry. */ +static u32 sbp2_broken_inquiry_list[] = { + 0x00002800, /* Stefan Richter <richtest@bauwesen.tu-cottbus.de> */ + /* DViCO Momobay CX-1 */ + 0x00000200 /* Andreas Plesch <plesch@fas.harvard.edu> */ + /* QPS Fire DVDBurner */ +}; + +#define NUM_BROKEN_INQUIRY_DEVS \ + (sizeof(sbp2_broken_inquiry_list)/sizeof(*sbp2_broken_inquiry_list)) + +/************************************** + * 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]); + + return; +} + +/* + * 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]); + + return; +} +#else /* BIG_ENDIAN */ +/* Why waste the cpu cycles? */ +#define sbp2util_be32_to_cpu_buffer(x,y) +#define sbp2util_cpu_to_be32_buffer(x,y) +#endif + +#ifdef CONFIG_IEEE1394_SBP2_PACKET_DUMP +/* + * Debug packet dump routine. Length is in bytes. + */ +static void sbp2util_packet_dump(void *buffer, int length, char *dump_name, u32 dump_phys_addr) +{ + int i; + unsigned char *dump = buffer; + + if (!dump || !length || !dump_name) + return; + + if (dump_phys_addr) + printk("[%s, 0x%x]", dump_name, dump_phys_addr); + else + printk("[%s]", dump_name); + for (i = 0; i < length; i++) { + if (i > 0x3f) { + printk("\n ..."); + break; + } + if ((i & 0x3) == 0) + printk(" "); + if ((i & 0xf) == 0) + printk("\n "); + printk("%02x ", (int) dump[i]); + } + printk("\n"); + + return; +} +#else +#define sbp2util_packet_dump(w,x,y,z) +#endif + +/* + * Goofy routine that basically does a down_timeout function. + */ +static int sbp2util_down_timeout(atomic_t *done, int timeout) +{ + int i; + + for (i = timeout; (i > 0 && atomic_read(done) == 0); i-= HZ/10) { + if (msleep_interruptible(100)) /* 100ms */ + return(1); + } + return ((i > 0) ? 0:1); +} + +/* Free's an allocated packet */ +static void sbp2_free_packet(struct hpsb_packet *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 interrupts. + */ +static int sbp2util_node_write_no_wait(struct node_entry *ne, u64 addr, + quadlet_t *buffer, size_t length) +{ + struct hpsb_packet *packet; + + packet = hpsb_make_writepacket(ne->host, ne->nodeid, + addr, buffer, length); + if (!packet) + return -ENOMEM; + + hpsb_set_packet_complete_task(packet, (void (*)(void*))sbp2_free_packet, + packet); + + hpsb_node_fill_packet(ne, packet); + + if (hpsb_send_packet(packet) < 0) { + sbp2_free_packet(packet); + return -EIO; + } + + return 0; +} + +/* + * This function is called to create a pool of command orbs used for + * command processing. It is called when a new sbp2 device is detected. + */ +static int sbp2util_create_command_orb_pool(struct scsi_id_instance_data *scsi_id) +{ + struct sbp2scsi_host_info *hi = scsi_id->hi; + int i; + unsigned long flags, orbs; + struct sbp2_command_info *command; + + orbs = serialize_io ? 2 : SBP2_MAX_CMDS; + + spin_lock_irqsave(&scsi_id->sbp2_command_orb_lock, flags); + for (i = 0; i < orbs; i++) { + command = (struct sbp2_command_info *) + kmalloc(sizeof(struct sbp2_command_info), GFP_ATOMIC); + if (!command) { + spin_unlock_irqrestore(&scsi_id->sbp2_command_orb_lock, flags); + return(-ENOMEM); + } + memset(command, '\0', sizeof(struct sbp2_command_info)); + command->command_orb_dma = + pci_map_single (hi->host->pdev, &command->command_orb, + sizeof(struct sbp2_command_orb), + PCI_DMA_BIDIRECTIONAL); + SBP2_DMA_ALLOC("single command orb DMA"); + command->sge_dma = + pci_map_single (hi->host->pdev, &command->scatter_gather_element, + sizeof(command->scatter_gather_element), + PCI_DMA_BIDIRECTIONAL); + SBP2_DMA_ALLOC("scatter_gather_element"); + INIT_LIST_HEAD(&command->list); + list_add_tail(&command->list, &scsi_id->sbp2_command_orb_completed); + } + spin_unlock_irqrestore(&scsi_id->sbp2_command_orb_lock, flags); + return 0; +} + +/* + * This function is called to delete a pool of command orbs. + */ +static void sbp2util_remove_command_orb_pool(struct scsi_id_instance_data *scsi_id) +{ + struct hpsb_host *host = scsi_id->hi->host; + struct list_head *lh, *next; + struct sbp2_command_info *command; + unsigned long flags; + + spin_lock_irqsave(&scsi_id->sbp2_command_orb_lock, flags); + if (!list_empty(&scsi_id->sbp2_command_orb_completed)) { + list_for_each_safe(lh, next, &scsi_id->sbp2_command_orb_completed) { + command = list_entry(lh, struct sbp2_command_info, list); + + /* Release our generic DMA's */ + pci_unmap_single(host->pdev, command->command_orb_dma, + sizeof(struct sbp2_command_orb), + PCI_DMA_BIDIRECTIONAL); + SBP2_DMA_FREE("single command orb DMA"); + pci_unmap_single(host->pdev, command->sge_dma, + sizeof(command->scatter_gather_element), + PCI_DMA_BIDIRECTIONAL); + SBP2_DMA_FREE("scatter_gather_element"); + + kfree(command); + } + } + spin_unlock_irqrestore(&scsi_id->sbp2_command_orb_lock, flags); + return; +} + +/* + * This function finds the sbp2_command for a given outstanding command + * orb.Only looks at the inuse list. + */ +static struct sbp2_command_info *sbp2util_find_command_for_orb( + struct scsi_id_instance_data *scsi_id, dma_addr_t orb) +{ + struct sbp2_command_info *command; + unsigned long flags; + + spin_lock_irqsave(&scsi_id->sbp2_command_orb_lock, flags); + if (!list_empty(&scsi_id->sbp2_command_orb_inuse)) { + list_for_each_entry(command, &scsi_id->sbp2_command_orb_inuse, list) { + if (command->command_orb_dma == orb) { + spin_unlock_irqrestore(&scsi_id->sbp2_command_orb_lock, flags); + return (command); + } + } + } + spin_unlock_irqrestore(&scsi_id->sbp2_command_orb_lock, flags); + + SBP2_ORB_DEBUG("could not match command orb %x", (unsigned int)orb); + + return(NULL); +} + +/* + * This function finds the sbp2_command for a given outstanding SCpnt. + * Only looks at the inuse list. + */ +static struct sbp2_command_info *sbp2util_find_command_for_SCpnt(struct scsi_id_instance_data *scsi_id, void *SCpnt) +{ + struct sbp2_command_info *command; + unsigned long flags; + + spin_lock_irqsave(&scsi_id->sbp2_command_orb_lock, flags); + if (!list_empty(&scsi_id->sbp2_command_orb_inuse)) { + list_for_each_entry(command, &scsi_id->sbp2_command_orb_inuse, list) { + if (command->Current_SCpnt == SCpnt) { + spin_unlock_irqrestore(&scsi_id->sbp2_command_orb_lock, flags); + return (command); + } + } + } + spin_unlock_irqrestore(&scsi_id->sbp2_command_orb_lock, flags); + return(NULL); +} + +/* + * This function allocates a command orb used to send a scsi command. + */ +static struct sbp2_command_info *sbp2util_allocate_command_orb( + struct scsi_id_instance_data *scsi_id, + struct scsi_cmnd *Current_SCpnt, + void (*Current_done)(struct scsi_cmnd *)) +{ + struct list_head *lh; + struct sbp2_command_info *command = NULL; + unsigned long flags; + + spin_lock_irqsave(&scsi_id->sbp2_command_orb_lock, flags); + if (!list_empty(&scsi_id->sbp2_command_orb_completed)) { + lh = scsi_id->sbp2_command_orb_completed.next; + list_del(lh); + command = list_entry(lh, struct sbp2_command_info, list); + command->Current_done = Current_done; + command->Current_SCpnt = Current_SCpnt; + list_add_tail(&command->list, &scsi_id->sbp2_command_orb_inuse); + } else { + SBP2_ERR("sbp2util_allocate_command_orb - No orbs available!"); + } + spin_unlock_irqrestore(&scsi_id->sbp2_command_orb_lock, flags); + return (command); +} + +/* Free our DMA's */ +static void sbp2util_free_command_dma(struct sbp2_command_info *command) +{ + struct scsi_id_instance_data *scsi_id = + (struct scsi_id_instance_data *)command->Current_SCpnt->device->host->hostdata[0]; + struct hpsb_host *host; + + if (!scsi_id) { + printk(KERN_ERR "%s: scsi_id == NULL\n", __FUNCTION__); + return; + } + + host = scsi_id->ud->ne->host; + + if (command->cmd_dma) { + if (command->dma_type == CMD_DMA_SINGLE) { + pci_unmap_single(host->pdev, command->cmd_dma, + command->dma_size, command->dma_dir); + SBP2_DMA_FREE("single bulk"); + } else if (command->dma_type == CMD_DMA_PAGE) { + pci_unmap_page(host->pdev, command->cmd_dma, + command->dma_size, command->dma_dir); + SBP2_DMA_FREE("single page"); + } /* XXX: Check for CMD_DMA_NONE bug */ + command->dma_type = CMD_DMA_NONE; + command->cmd_dma = 0; + } + + if (command->sge_buffer) { + pci_unmap_sg(host->pdev, command->sge_buffer, + command->dma_size, command->dma_dir); + SBP2_DMA_FREE("scatter list"); + command->sge_buffer = NULL; + } +} + +/* + * This function moves a command to the completed orb list. + */ +static void sbp2util_mark_command_completed(struct scsi_id_instance_data *scsi_id, struct sbp2_command_info *command) +{ + unsigned long flags; + + spin_lock_irqsave(&scsi_id->sbp2_command_orb_lock, flags); + list_del(&command->list); + sbp2util_free_command_dma(command); + list_add_tail(&command->list, &scsi_id->sbp2_command_orb_completed); + spin_unlock_irqrestore(&scsi_id->sbp2_command_orb_lock, flags); +} + + + +/********************************************* + * IEEE-1394 core driver stack related section + *********************************************/ +static struct scsi_id_instance_data *sbp2_alloc_device(struct unit_directory *ud); + +static int sbp2_probe(struct device *dev) +{ + struct unit_directory *ud; + struct scsi_id_instance_data *scsi_id; + + SBP2_DEBUG("sbp2_probe"); + + 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; + + scsi_id = sbp2_alloc_device(ud); + + if (!scsi_id) + return -ENOMEM; + + sbp2_parse_unit_directory(scsi_id, ud); + + return sbp2_start_device(scsi_id); +} + +static int sbp2_remove(struct device *dev) +{ + struct unit_directory *ud; + struct scsi_id_instance_data *scsi_id; + + SBP2_DEBUG("sbp2_remove"); + + ud = container_of(dev, struct unit_directory, device); + scsi_id = ud->device.driver_data; + + sbp2_logout_device(scsi_id); + sbp2_remove_device(scsi_id); + + return 0; +} + +static int sbp2_update(struct unit_directory *ud) +{ + struct scsi_id_instance_data *scsi_id = ud->device.driver_data; + + SBP2_DEBUG("sbp2_update"); + + if (sbp2_reconnect_device(scsi_id)) { + + /* + * Ok, reconnect has failed. Perhaps we didn't + * reconnect fast enough. Try doing a regular login, but + * first do a logout just in case of any weirdness. + */ + sbp2_logout_device(scsi_id); + + if (sbp2_login_device(scsi_id)) { + /* Login failed too, just fail, and the backend + * will call our sbp2_remove for us */ + SBP2_ERR("Failed to reconnect to sbp2 device!"); + return -EBUSY; + } + } + + /* Set max retries to something large on the device. */ + sbp2_set_busy_timeout(scsi_id); + + /* Do a SBP-2 fetch agent reset. */ + sbp2_agent_reset(scsi_id, 1); + + /* Get the max speed and packet size that we can use. */ + sbp2_max_speed_and_size(scsi_id); + + /* Complete any pending commands with busy (so they get + * retried) and remove them from our queue + */ + sbp2scsi_complete_all_commands(scsi_id, DID_BUS_BUSY); + + /* Make sure we unblock requests (since this is likely after a bus + * reset). */ + scsi_unblock_requests(scsi_id->scsi_host); + + return 0; +} + +/* This functions is called by the sbp2_probe, for each new device. We now + * allocate one scsi host for each scsi_id (unit directory). */ +static struct scsi_id_instance_data *sbp2_alloc_device(struct unit_directory *ud) +{ + struct sbp2scsi_host_info *hi; + struct Scsi_Host *scsi_host = NULL; + struct scsi_id_instance_data *scsi_id = NULL; + + SBP2_DEBUG("sbp2_alloc_device"); + + scsi_id = kmalloc(sizeof(*scsi_id), GFP_KERNEL); + if (!scsi_id) { + SBP2_ERR("failed to create scsi_id"); + goto failed_alloc; + } + memset(scsi_id, 0, sizeof(*scsi_id)); + + scsi_id->ne = ud->ne; + scsi_id->ud = ud; + scsi_id->speed_code = IEEE1394_SPEED_100; + scsi_id->max_payload_size = sbp2_speedto_max_payload[IEEE1394_SPEED_100]; + atomic_set(&scsi_id->sbp2_login_complete, 0); + INIT_LIST_HEAD(&scsi_id->sbp2_command_orb_inuse); + INIT_LIST_HEAD(&scsi_id->sbp2_command_orb_completed); + INIT_LIST_HEAD(&scsi_id->scsi_list); + spin_lock_init(&scsi_id->sbp2_command_orb_lock); + scsi_id->sbp2_device_type_and_lun = SBP2_DEVICE_TYPE_LUN_UNINITIALIZED; + + ud->device.driver_data = scsi_id; + + 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; + } + SBP2_DEBUG("sbp2_alloc_device: allocated hostinfo"); + hi->host = ud->ne->host; + INIT_LIST_HEAD(&hi->scsi_ids); + + /* Register our sbp2 status address space... */ + hpsb_register_addrspace(&sbp2_highlevel, ud->ne->host, &sbp2_ops, + SBP2_STATUS_FIFO_ADDRESS, + SBP2_STATUS_FIFO_ADDRESS + + SBP2_STATUS_FIFO_ENTRY_TO_OFFSET(SBP2_MAX_UDS_PER_NODE+1)); +#ifdef CONFIG_IEEE1394_SBP2_PHYS_DMA + /* Handle data movement if physical dma is not + * enabled/supportedon host controller */ + hpsb_register_addrspace(&sbp2_highlevel, ud->ne->host, &sbp2_physdma_ops, + 0x0ULL, 0xfffffffcULL); +#endif + } + + scsi_id->hi = hi; + + list_add_tail(&scsi_id->scsi_list, &hi->scsi_ids); + + /* Register our host with the SCSI stack. */ + scsi_host = scsi_host_alloc(&scsi_driver_template, 0); + if (!scsi_host) { + SBP2_ERR("failed to register scsi host"); + goto failed_alloc; + } + + scsi_host->hostdata[0] = (unsigned long)scsi_id; + + if (!scsi_add_host(scsi_host, &ud->device)) { + scsi_id->scsi_host = scsi_host; + return scsi_id; + } + + SBP2_ERR("failed to add scsi host"); + scsi_host_put(scsi_host); + +failed_alloc: + sbp2_remove_device(scsi_id); + return NULL; +} + + +static void sbp2_host_reset(struct hpsb_host *host) +{ + struct sbp2scsi_host_info *hi; + struct scsi_id_instance_data *scsi_id; + + hi = hpsb_get_hostinfo(&sbp2_highlevel, host); + + if (hi) { + list_for_each_entry(scsi_id, &hi->scsi_ids, scsi_list) + scsi_block_requests(scsi_id->scsi_host); + } +} + + +/* + * This function is where we first pull the node unique ids, and then + * allocate memory and register a SBP-2 device. + */ +static int sbp2_start_device(struct scsi_id_instance_data *scsi_id) +{ + struct sbp2scsi_host_info *hi = scsi_id->hi; + struct scsi_device *sdev; + + SBP2_DEBUG("sbp2_start_device"); + + /* Login FIFO DMA */ + scsi_id->login_response = + pci_alloc_consistent(hi->host->pdev, sizeof(struct sbp2_login_response), + &scsi_id->login_response_dma); + if (!scsi_id->login_response) + goto alloc_fail; + SBP2_DMA_ALLOC("consistent DMA region for login FIFO"); + + /* Query logins ORB DMA */ + scsi_id->query_logins_orb = + pci_alloc_consistent(hi->host->pdev, sizeof(struct sbp2_query_logins_orb), + &scsi_id->query_logins_orb_dma); + if (!scsi_id->query_logins_orb) + goto alloc_fail; + SBP2_DMA_ALLOC("consistent DMA region for query logins ORB"); + + /* Query logins response DMA */ + scsi_id->query_logins_response = + pci_alloc_consistent(hi->host->pdev, sizeof(struct sbp2_query_logins_response), + &scsi_id->query_logins_response_dma); + if (!scsi_id->query_logins_response) + goto alloc_fail; + SBP2_DMA_ALLOC("consistent DMA region for query logins response"); + + /* Reconnect ORB DMA */ + scsi_id->reconnect_orb = + pci_alloc_consistent(hi->host->pdev, sizeof(struct sbp2_reconnect_orb), + &scsi_id->reconnect_orb_dma); + if (!scsi_id->reconnect_orb) + goto alloc_fail; + SBP2_DMA_ALLOC("consistent DMA region for reconnect ORB"); + + /* Logout ORB DMA */ + scsi_id->logout_orb = + pci_alloc_consistent(hi->host->pdev, sizeof(struct sbp2_logout_orb), + &scsi_id->logout_orb_dma); + if (!scsi_id->logout_orb) + goto alloc_fail; + SBP2_DMA_ALLOC("consistent DMA region for logout ORB"); + + /* Login ORB DMA */ + scsi_id->login_orb = + pci_alloc_consistent(hi->host->pdev, sizeof(struct sbp2_login_orb), + &scsi_id->login_orb_dma); + if (!scsi_id->login_orb) { +alloc_fail: + if (scsi_id->query_logins_response) { + pci_free_consistent(hi->host->pdev, + sizeof(struct sbp2_query_logins_response), + scsi_id->query_logins_response, + scsi_id->query_logins_response_dma); + SBP2_DMA_FREE("query logins response DMA"); + } + + if (scsi_id->query_logins_orb) { + pci_free_consistent(hi->host->pdev, + sizeof(struct sbp2_query_logins_orb), + scsi_id->query_logins_orb, + scsi_id->query_logins_orb_dma); + SBP2_DMA_FREE("query logins ORB DMA"); + } + + if (scsi_id->logout_orb) { + pci_free_consistent(hi->host->pdev, + sizeof(struct sbp2_logout_orb), + scsi_id->logout_orb, + scsi_id->logout_orb_dma); + SBP2_DMA_FREE("logout ORB DMA"); + } + + if (scsi_id->reconnect_orb) { + pci_free_consistent(hi->host->pdev, + sizeof(struct sbp2_reconnect_orb), + scsi_id->reconnect_orb, + scsi_id->reconnect_orb_dma); + SBP2_DMA_FREE("reconnect ORB DMA"); + } + + if (scsi_id->login_response) { + pci_free_consistent(hi->host->pdev, + sizeof(struct sbp2_login_response), + scsi_id->login_response, + scsi_id->login_response_dma); + SBP2_DMA_FREE("login FIFO DMA"); + } + + list_del(&scsi_id->scsi_list); + + kfree(scsi_id); + + SBP2_ERR ("Could not allocate memory for scsi_id"); + + return -ENOMEM; + } + SBP2_DMA_ALLOC("consistent DMA region for login ORB"); + + SBP2_DEBUG("New SBP-2 device inserted, SCSI ID = %x", scsi_id->ud->id); + + /* + * Create our command orb pool + */ + if (sbp2util_create_command_orb_pool(scsi_id)) { + SBP2_ERR("sbp2util_create_command_orb_pool failed!"); + sbp2_remove_device(scsi_id); + return -ENOMEM; + } + + /* Schedule a timeout here. The reason is that we may be so close + * to a bus reset, that the device is not available for logins. + * This can happen when the bus reset is caused by the host + * connected to the sbp2 device being removed. That host would + * have a certain amount of time to relogin before the sbp2 device + * allows someone else to login instead. One second makes sense. */ + msleep_interruptible(1000); + if (signal_pending(current)) { + SBP2_WARN("aborting sbp2_start_device due to event"); + sbp2_remove_device(scsi_id); + return -EINTR; + } + + /* + * Login to the sbp-2 device + */ + if (sbp2_login_device(scsi_id)) { + /* Login failed, just remove the device. */ + sbp2_remove_device(scsi_id); + return -EBUSY; + } + + /* + * Set max retries to something large on the device + */ + sbp2_set_busy_timeout(scsi_id); + + /* + * Do a SBP-2 fetch agent reset + */ + sbp2_agent_reset(scsi_id, 1); + + /* + * Get the max speed and packet size that we can use + */ + sbp2_max_speed_and_size(scsi_id); + + /* Add this device to the scsi layer now */ + sdev = scsi_add_device(scsi_id->scsi_host, 0, scsi_id->ud->id, 0); + if (IS_ERR(sdev)) { + SBP2_ERR("scsi_add_device failed"); + return PTR_ERR(sdev); + } + + return 0; +} + +/* + * This function removes an sbp2 device from the sbp2scsi_host_info struct. + */ +static void sbp2_remove_device(struct scsi_id_instance_data *scsi_id) +{ + struct sbp2scsi_host_info *hi; + + SBP2_DEBUG("sbp2_remove_device"); + + if (!scsi_id) + return; + + hi = scsi_id->hi; + + /* This will remove our scsi device aswell */ + if (scsi_id->scsi_host) { + scsi_remove_host(scsi_id->scsi_host); + scsi_host_put(scsi_id->scsi_host); + } + + sbp2util_remove_command_orb_pool(scsi_id); + + list_del(&scsi_id->scsi_list); + + if (scsi_id->login_response) { + pci_free_consistent(hi->host->pdev, + sizeof(struct sbp2_login_response), + scsi_id->login_response, + scsi_id->login_response_dma); + SBP2_DMA_FREE("single login FIFO"); + } + + if (scsi_id->login_orb) { + pci_free_consistent(hi->host->pdev, + sizeof(struct sbp2_login_orb), + scsi_id->login_orb, + scsi_id->login_orb_dma); + SBP2_DMA_FREE("single login ORB"); + } + + if (scsi_id->reconnect_orb) { + pci_free_consistent(hi->host->pdev, + sizeof(struct sbp2_reconnect_orb), + scsi_id->reconnect_orb, + scsi_id->reconnect_orb_dma); + SBP2_DMA_FREE("single reconnect orb"); + } + + if (scsi_id->logout_orb) { + pci_free_consistent(hi->host->pdev, + sizeof(struct sbp2_logout_orb), + scsi_id->logout_orb, + scsi_id->logout_orb_dma); + SBP2_DMA_FREE("single logout orb"); + } + + if (scsi_id->query_logins_orb) { + pci_free_consistent(hi->host->pdev, + sizeof(struct sbp2_query_logins_orb), + scsi_id->query_logins_orb, + scsi_id->query_logins_orb_dma); + SBP2_DMA_FREE("single query logins orb"); + } + + if (scsi_id->query_logins_response) { + pci_free_consistent(hi->host->pdev, + sizeof(struct sbp2_query_logins_response), + scsi_id->query_logins_response, + scsi_id->query_logins_response_dma); + SBP2_DMA_FREE("single query logins data"); + } + + scsi_id->ud->device.driver_data = NULL; + + SBP2_DEBUG("SBP-2 device removed, SCSI ID = %d", scsi_id->ud->id); + + kfree(scsi_id); +} + +#ifdef CONFIG_IEEE1394_SBP2_PHYS_DMA +/* + * This function deals with physical dma write requests (for adapters that do not support + * physical dma in hardware). Mostly just here for debugging... + */ +static int sbp2_handle_physdma_write(struct hpsb_host *host, int nodeid, int destid, quadlet_t *data, + u64 addr, size_t length, u16 flags) +{ + + /* + * Manually put the data in the right place. + */ + memcpy(bus_to_virt((u32)addr), data, length); + sbp2util_packet_dump(data, length, "sbp2 phys dma write by device", (u32)addr); + return(RCODE_COMPLETE); +} + +/* + * This function deals with physical dma read requests (for adapters that do not support + * physical dma in hardware). Mostly just here for debugging... + */ +static int sbp2_handle_physdma_read(struct hpsb_host *host, int nodeid, quadlet_t *data, + u64 addr, size_t length, u16 flags) +{ + + /* + * Grab data from memory and send a read response. + */ + memcpy(data, bus_to_virt((u32)addr), length); + sbp2util_packet_dump(data, length, "sbp2 phys dma read by device", (u32)addr); + return(RCODE_COMPLETE); +} +#endif + + +/************************************** + * SBP-2 protocol related section + **************************************/ + +/* + * This function determines if we should convert scsi commands for a particular sbp2 device type + */ +static __inline__ int sbp2_command_conversion_device_type(u8 device_type) +{ + return (((device_type == TYPE_DISK) || + (device_type == TYPE_SDAD) || + (device_type == TYPE_ROM)) ? 1:0); +} + +/* + * This function queries the device for the maximum concurrent logins it + * supports. + */ +static int sbp2_query_logins(struct scsi_id_instance_data *scsi_id) +{ + struct sbp2scsi_host_info *hi = scsi_id->hi; + quadlet_t data[2]; + int max_logins; + int active_logins; + + SBP2_DEBUG("sbp2_query_logins"); + + scsi_id->query_logins_orb->reserved1 = 0x0; + scsi_id->query_logins_orb->reserved2 = 0x0; + + scsi_id->query_logins_orb->query_response_lo = scsi_id->query_logins_response_dma; + scsi_id->query_logins_orb->query_response_hi = ORB_SET_NODE_ID(hi->host->node_id); + SBP2_DEBUG("sbp2_query_logins: query_response_hi/lo initialized"); + + scsi_id->query_logins_orb->lun_misc = ORB_SET_FUNCTION(SBP2_QUERY_LOGINS_REQUEST); + scsi_id->query_logins_orb->lun_misc |= ORB_SET_NOTIFY(1); + if (scsi_id->sbp2_device_type_and_lun != SBP2_DEVICE_TYPE_LUN_UNINITIALIZED) { + scsi_id->query_logins_orb->lun_misc |= ORB_SET_LUN(scsi_id->sbp2_device_type_and_lun); + SBP2_DEBUG("sbp2_query_logins: set lun to %d", + ORB_SET_LUN(scsi_id->sbp2_device_type_and_lun)); + } + SBP2_DEBUG("sbp2_query_logins: lun_misc initialized"); + + scsi_id->query_logins_orb->reserved_resp_length = + ORB_SET_QUERY_LOGINS_RESP_LENGTH(sizeof(struct sbp2_query_logins_response)); + SBP2_DEBUG("sbp2_query_logins: reserved_resp_length initialized"); + + scsi_id->query_logins_orb->status_FIFO_lo = SBP2_STATUS_FIFO_ADDRESS_LO + + SBP2_STATUS_FIFO_ENTRY_TO_OFFSET(scsi_id->ud->id); + scsi_id->query_logins_orb->status_FIFO_hi = (ORB_SET_NODE_ID(hi->host->node_id) | + SBP2_STATUS_FIFO_ADDRESS_HI); + SBP2_DEBUG("sbp2_query_logins: status FIFO initialized"); + + sbp2util_cpu_to_be32_buffer(scsi_id->query_logins_orb, sizeof(struct sbp2_query_logins_orb)); + + SBP2_DEBUG("sbp2_query_logins: orb byte-swapped"); + + sbp2util_packet_dump(scsi_id->query_logins_orb, sizeof(struct sbp2_query_logins_orb), + "sbp2 query logins orb", scsi_id->query_logins_orb_dma); + + memset(scsi_id->query_logins_response, 0, sizeof(struct sbp2_query_logins_response)); + memset(&scsi_id->status_block, 0, sizeof(struct sbp2_status_block)); + + SBP2_DEBUG("sbp2_query_logins: query_logins_response/status FIFO memset"); + + data[0] = ORB_SET_NODE_ID(hi->host->node_id); + data[1] = scsi_id->query_logins_orb_dma; + sbp2util_cpu_to_be32_buffer(data, 8); + + atomic_set(&scsi_id->sbp2_login_complete, 0); + + SBP2_DEBUG("sbp2_query_logins: prepared to write"); + hpsb_node_write(scsi_id->ne, scsi_id->sbp2_management_agent_addr, data, 8); + SBP2_DEBUG("sbp2_query_logins: written"); + + if (sbp2util_down_timeout(&scsi_id->sbp2_login_complete, 2*HZ)) { + SBP2_INFO("Error querying logins to SBP-2 device - timed out"); + return(-EIO); + } + + if (scsi_id->status_block.ORB_offset_lo != scsi_id->query_logins_orb_dma) { + SBP2_INFO("Error querying logins to SBP-2 device - timed out"); + return(-EIO); + } + + if (STATUS_GET_RESP(scsi_id->status_block.ORB_offset_hi_misc) || + STATUS_GET_DEAD_BIT(scsi_id->status_block.ORB_offset_hi_misc) || + STATUS_GET_SBP_STATUS(scsi_id->status_block.ORB_offset_hi_misc)) { + + SBP2_INFO("Error querying logins to SBP-2 device - timed out"); + return(-EIO); + } + + sbp2util_cpu_to_be32_buffer(scsi_id->query_logins_response, sizeof(struct sbp2_query_logins_response)); + + SBP2_DEBUG("length_max_logins = %x", + (unsigned int)scsi_id->query_logins_response->length_max_logins); + + SBP2_DEBUG("Query logins to SBP-2 device successful"); + + max_logins = RESPONSE_GET_MAX_LOGINS(scsi_id->query_logins_response->length_max_logins); + SBP2_DEBUG("Maximum concurrent logins supported: %d", max_logins); + + active_logins = RESPONSE_GET_ACTIVE_LOGINS(scsi_id->query_logins_response->length_max_logins); + SBP2_DEBUG("Number of active logins: %d", active_logins); + + if (active_logins >= max_logins) { + return(-EIO); + } + + return 0; +} + +/* + * This function is called in order to login to a particular SBP-2 device, + * after a bus reset. + */ +static int sbp2_login_device(struct scsi_id_instance_data *scsi_id) +{ + struct sbp2scsi_host_info *hi = scsi_id->hi; + quadlet_t data[2]; + + SBP2_DEBUG("sbp2_login_device"); + + if (!scsi_id->login_orb) { + SBP2_DEBUG("sbp2_login_device: login_orb not alloc'd!"); + return(-EIO); + } + + if (!exclusive_login) { + if (sbp2_query_logins(scsi_id)) { + SBP2_INFO("Device does not support any more concurrent logins"); + return(-EIO); + } + } + + /* Set-up login ORB, assume no password */ + scsi_id->login_orb->password_hi = 0; + scsi_id->login_orb->password_lo = 0; + SBP2_DEBUG("sbp2_login_device: password_hi/lo initialized"); + + scsi_id->login_orb->login_response_lo = scsi_id->login_response_dma; + scsi_id->login_orb->login_response_hi = ORB_SET_NODE_ID(hi->host->node_id); + SBP2_DEBUG("sbp2_login_device: login_response_hi/lo initialized"); + + scsi_id->login_orb->lun_misc = ORB_SET_FUNCTION(SBP2_LOGIN_REQUEST); + scsi_id->login_orb->lun_misc |= ORB_SET_RECONNECT(0); /* One second reconnect time */ + scsi_id->login_orb->lun_misc |= ORB_SET_EXCLUSIVE(exclusive_login); /* Exclusive access to device */ + scsi_id->login_orb->lun_misc |= ORB_SET_NOTIFY(1); /* Notify us of login complete */ + /* Set the lun if we were able to pull it from the device's unit directory */ + if (scsi_id->sbp2_device_type_and_lun != SBP2_DEVICE_TYPE_LUN_UNINITIALIZED) { + scsi_id->login_orb->lun_misc |= ORB_SET_LUN(scsi_id->sbp2_device_type_and_lun); + SBP2_DEBUG("sbp2_query_logins: set lun to %d", + ORB_SET_LUN(scsi_id->sbp2_device_type_and_lun)); + } + SBP2_DEBUG("sbp2_login_device: lun_misc initialized"); + + scsi_id->login_orb->passwd_resp_lengths = + ORB_SET_LOGIN_RESP_LENGTH(sizeof(struct sbp2_login_response)); + SBP2_DEBUG("sbp2_login_device: passwd_resp_lengths initialized"); + + scsi_id->login_orb->status_FIFO_lo = SBP2_STATUS_FIFO_ADDRESS_LO + + SBP2_STATUS_FIFO_ENTRY_TO_OFFSET(scsi_id->ud->id); + scsi_id->login_orb->status_FIFO_hi = (ORB_SET_NODE_ID(hi->host->node_id) | + SBP2_STATUS_FIFO_ADDRESS_HI); + SBP2_DEBUG("sbp2_login_device: status FIFO initialized"); + + /* + * Byte swap ORB if necessary + */ + sbp2util_cpu_to_be32_buffer(scsi_id->login_orb, sizeof(struct sbp2_login_orb)); + + SBP2_DEBUG("sbp2_login_device: orb byte-swapped"); + + sbp2util_packet_dump(scsi_id->login_orb, sizeof(struct sbp2_login_orb), + "sbp2 login orb", scsi_id->login_orb_dma); + + /* + * Initialize login response and status fifo + */ + memset(scsi_id->login_response, 0, sizeof(struct sbp2_login_response)); + memset(&scsi_id->status_block, 0, sizeof(struct sbp2_status_block)); + + SBP2_DEBUG("sbp2_login_device: login_response/status FIFO memset"); + + /* + * Ok, let's write to the target's management agent register + */ + data[0] = ORB_SET_NODE_ID(hi->host->node_id); + data[1] = scsi_id->login_orb_dma; + sbp2util_cpu_to_be32_buffer(data, 8); + + atomic_set(&scsi_id->sbp2_login_complete, 0); + + SBP2_DEBUG("sbp2_login_device: prepared to write to %08x", + (unsigned int)scsi_id->sbp2_management_agent_addr); + hpsb_node_write(scsi_id->ne, scsi_id->sbp2_management_agent_addr, data, 8); + SBP2_DEBUG("sbp2_login_device: written"); + + /* + * Wait for login status (up to 20 seconds)... + */ + if (sbp2util_down_timeout(&scsi_id->sbp2_login_complete, 20*HZ)) { + SBP2_ERR("Error logging into SBP-2 device - login timed-out"); + return(-EIO); + } + + /* + * Sanity. Make sure status returned matches login orb. + */ + if (scsi_id->status_block.ORB_offset_lo != scsi_id->login_orb_dma) { + SBP2_ERR("Error logging into SBP-2 device - login timed-out"); + return(-EIO); + } + + /* + * Check status + */ + if (STATUS_GET_RESP(scsi_id->status_block.ORB_offset_hi_misc) || + STATUS_GET_DEAD_BIT(scsi_id->status_block.ORB_offset_hi_misc) || + STATUS_GET_SBP_STATUS(scsi_id->status_block.ORB_offset_hi_misc)) { + + SBP2_ERR("Error logging into SBP-2 device - login failed"); + return(-EIO); + } + + /* + * Byte swap the login response, for use when reconnecting or + * logging out. + */ + sbp2util_cpu_to_be32_buffer(scsi_id->login_response, sizeof(struct sbp2_login_response)); + + /* + * Grab our command block agent address from the login response. + */ + SBP2_DEBUG("command_block_agent_hi = %x", + (unsigned int)scsi_id->login_response->command_block_agent_hi); + SBP2_DEBUG("command_block_agent_lo = %x", + (unsigned int)scsi_id->login_response->command_block_agent_lo); + + scsi_id->sbp2_command_block_agent_addr = + ((u64)scsi_id->login_response->command_block_agent_hi) << 32; + scsi_id->sbp2_command_block_agent_addr |= ((u64)scsi_id->login_response->command_block_agent_lo); + scsi_id->sbp2_command_block_agent_addr &= 0x0000ffffffffffffULL; + + SBP2_INFO("Logged into SBP-2 device"); + + return(0); + +} + +/* + * This function is called in order to logout from a particular SBP-2 + * device, usually called during driver unload. + */ +static int sbp2_logout_device(struct scsi_id_instance_data *scsi_id) +{ + struct sbp2scsi_host_info *hi = scsi_id->hi; + quadlet_t data[2]; + int error; + + SBP2_DEBUG("sbp2_logout_device"); + + /* + * Set-up logout ORB + */ + scsi_id->logout_orb->reserved1 = 0x0; + scsi_id->logout_orb->reserved2 = 0x0; + scsi_id->logout_orb->reserved3 = 0x0; + scsi_id->logout_orb->reserved4 = 0x0; + + scsi_id->logout_orb->login_ID_misc = ORB_SET_FUNCTION(SBP2_LOGOUT_REQUEST); + scsi_id->logout_orb->login_ID_misc |= ORB_SET_LOGIN_ID(scsi_id->login_response->length_login_ID); + + /* Notify us when complete */ + scsi_id->logout_orb->login_ID_misc |= ORB_SET_NOTIFY(1); + + scsi_id->logout_orb->reserved5 = 0x0; + scsi_id->logout_orb->status_FIFO_lo = SBP2_STATUS_FIFO_ADDRESS_LO + + SBP2_STATUS_FIFO_ENTRY_TO_OFFSET(scsi_id->ud->id); + scsi_id->logout_orb->status_FIFO_hi = (ORB_SET_NODE_ID(hi->host->node_id) | + SBP2_STATUS_FIFO_ADDRESS_HI); + + /* + * Byte swap ORB if necessary + */ + sbp2util_cpu_to_be32_buffer(scsi_id->logout_orb, sizeof(struct sbp2_logout_orb)); + + sbp2util_packet_dump(scsi_id->logout_orb, sizeof(struct sbp2_logout_orb), + "sbp2 logout orb", scsi_id->logout_orb_dma); + + /* + * Ok, let's write to the target's management agent register + */ + data[0] = ORB_SET_NODE_ID(hi->host->node_id); + data[1] = scsi_id->logout_orb_dma; + sbp2util_cpu_to_be32_buffer(data, 8); + + atomic_set(&scsi_id->sbp2_login_complete, 0); + + error = hpsb_node_write(scsi_id->ne, + scsi_id->sbp2_management_agent_addr, + data, 8); + if (error) + return error; + + /* Wait for device to logout...1 second. */ + if (sbp2util_down_timeout(&scsi_id->sbp2_login_complete, HZ)) + return -EIO; + + SBP2_INFO("Logged out of SBP-2 device"); + + return(0); + +} + +/* + * This function is called in order to reconnect to a particular SBP-2 + * device, after a bus reset. + */ +static int sbp2_reconnect_device(struct scsi_id_instance_data *scsi_id) +{ + struct sbp2scsi_host_info *hi = scsi_id->hi; + quadlet_t data[2]; + int error; + + SBP2_DEBUG("sbp2_reconnect_device"); + + /* + * Set-up reconnect ORB + */ + scsi_id->reconnect_orb->reserved1 = 0x0; + scsi_id->reconnect_orb->reserved2 = 0x0; + scsi_id->reconnect_orb->reserved3 = 0x0; + scsi_id->reconnect_orb->reserved4 = 0x0; + + scsi_id->reconnect_orb->login_ID_misc = ORB_SET_FUNCTION(SBP2_RECONNECT_REQUEST); + scsi_id->reconnect_orb->login_ID_misc |= + ORB_SET_LOGIN_ID(scsi_id->login_response->length_login_ID); + + /* Notify us when complete */ + scsi_id->reconnect_orb->login_ID_misc |= ORB_SET_NOTIFY(1); + + scsi_id->reconnect_orb->reserved5 = 0x0; + scsi_id->reconnect_orb->status_FIFO_lo = SBP2_STATUS_FIFO_ADDRESS_LO + + SBP2_STATUS_FIFO_ENTRY_TO_OFFSET(scsi_id->ud->id); + scsi_id->reconnect_orb->status_FIFO_hi = + (ORB_SET_NODE_ID(hi->host->node_id) | SBP2_STATUS_FIFO_ADDRESS_HI); + + /* + * Byte swap ORB if necessary + */ + sbp2util_cpu_to_be32_buffer(scsi_id->reconnect_orb, sizeof(struct sbp2_reconnect_orb)); + + sbp2util_packet_dump(scsi_id->reconnect_orb, sizeof(struct sbp2_reconnect_orb), + "sbp2 reconnect orb", scsi_id->reconnect_orb_dma); + + /* + * Initialize status fifo + */ + memset(&scsi_id->status_block, 0, sizeof(struct sbp2_status_block)); + + /* + * Ok, let's write to the target's management agent register + */ + data[0] = ORB_SET_NODE_ID(hi->host->node_id); + data[1] = scsi_id->reconnect_orb_dma; + sbp2util_cpu_to_be32_buffer(data, 8); + + atomic_set(&scsi_id->sbp2_login_complete, 0); + + error = hpsb_node_write(scsi_id->ne, + scsi_id->sbp2_management_agent_addr, + data, 8); + if (error) + return error; + + /* + * Wait for reconnect status (up to 1 second)... + */ + if (sbp2util_down_timeout(&scsi_id->sbp2_login_complete, HZ)) { + SBP2_ERR("Error reconnecting to SBP-2 device - reconnect timed-out"); + return(-EIO); + } + + /* + * Sanity. Make sure status returned matches reconnect orb. + */ + if (scsi_id->status_block.ORB_offset_lo != scsi_id->reconnect_orb_dma) { + SBP2_ERR("Error reconnecting to SBP-2 device - reconnect timed-out"); + return(-EIO); + } + + /* + * Check status + */ + if (STATUS_GET_RESP(scsi_id->status_block.ORB_offset_hi_misc) || + STATUS_GET_DEAD_BIT(scsi_id->status_block.ORB_offset_hi_misc) || + STATUS_GET_SBP_STATUS(scsi_id->status_block.ORB_offset_hi_misc)) { + + SBP2_ERR("Error reconnecting to SBP-2 device - reconnect failed"); + return(-EIO); + } + + HPSB_DEBUG("Reconnected to SBP-2 device"); + + return(0); + +} + +/* + * This function is called in order to set the busy timeout (number of + * retries to attempt) on the sbp2 device. + */ +static int sbp2_set_busy_timeout(struct scsi_id_instance_data *scsi_id) +{ + quadlet_t data; + + SBP2_DEBUG("sbp2_set_busy_timeout"); + + /* + * Ok, let's write to the target's busy timeout register + */ + data = cpu_to_be32(SBP2_BUSY_TIMEOUT_VALUE); + + if (hpsb_node_write(scsi_id->ne, SBP2_BUSY_TIMEOUT_ADDRESS, &data, 4)) { + SBP2_ERR("sbp2_set_busy_timeout error"); + } + + return(0); +} + + +/* + * This function is called to parse sbp2 device's config rom unit + * directory. Used to determine things like sbp2 management agent offset, + * and command set used (SCSI or RBC). + */ +static void sbp2_parse_unit_directory(struct scsi_id_instance_data *scsi_id, + struct unit_directory *ud) +{ + struct csr1212_keyval *kv; + struct csr1212_dentry *dentry; + u64 management_agent_addr; + u32 command_set_spec_id, command_set, unit_characteristics, + firmware_revision, workarounds; + int i; + + SBP2_DEBUG("sbp2_parse_unit_directory"); + + management_agent_addr = 0x0; + command_set_spec_id = 0x0; + command_set = 0x0; + unit_characteristics = 0x0; + firmware_revision = 0x0; + + /* Handle different fields in the unit directory, based on keys */ + 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) { + /* Save off the management agent address */ + management_agent_addr = + CSR1212_REGISTER_SPACE_BASE + + (kv->value.csr_offset << 2); + + SBP2_DEBUG("sbp2_management_agent_addr = %x", + (unsigned int) management_agent_addr); + } else if (kv->key.type == CSR1212_KV_TYPE_IMMEDIATE) { + scsi_id->sbp2_device_type_and_lun = kv->value.immediate; + } + break; + + case SBP2_COMMAND_SET_SPEC_ID_KEY: + /* Command spec organization */ + command_set_spec_id = kv->value.immediate; + SBP2_DEBUG("sbp2_command_set_spec_id = %x", + (unsigned int) command_set_spec_id); + break; + + case SBP2_COMMAND_SET_KEY: + /* Command set used by sbp2 device */ + command_set = kv->value.immediate; + SBP2_DEBUG("sbp2_command_set = %x", + (unsigned int) command_set); + break; + + case SBP2_UNIT_CHARACTERISTICS_KEY: + /* + * Unit characterisitcs (orb related stuff + * that I'm not yet paying attention to) + */ + unit_characteristics = kv->value.immediate; + SBP2_DEBUG("sbp2_unit_characteristics = %x", + (unsigned int) unit_characteristics); + break; + + case SBP2_FIRMWARE_REVISION_KEY: + /* Firmware revision */ + firmware_revision = kv->value.immediate; + if (force_inquiry_hack) + SBP2_INFO("sbp2_firmware_revision = %x", + (unsigned int) firmware_revision); + else SBP2_DEBUG("sbp2_firmware_revision = %x", + (unsigned int) firmware_revision); + break; + + default: + break; + } + } + + /* This is the start of our broken device checking. We try to hack + * around oddities and known defects. */ + workarounds = 0x0; + + /* If the vendor id is 0xa0b8 (Symbios vendor id), then we have a + * bridge with 128KB max transfer size limitation. For sanity, we + * only voice this when the current max_sectors setting + * exceeds the 128k limit. By default, that is not the case. + * + * It would be really nice if we could detect this before the scsi + * host gets initialized. That way we can down-force the + * max_sectors to account for it. That is not currently + * possible. */ + if ((firmware_revision & 0xffff00) == + SBP2_128KB_BROKEN_FIRMWARE && + (max_sectors * 512) > (128*1024)) { + SBP2_WARN("Node " NODE_BUS_FMT ": Bridge only supports 128KB max transfer size.", + NODE_BUS_ARGS(ud->ne->host, ud->ne->nodeid)); + SBP2_WARN("WARNING: Current max_sectors setting is larger than 128KB (%d sectors)!", + max_sectors); + workarounds |= SBP2_BREAKAGE_128K_MAX_TRANSFER; + } + + /* Check for a blacklisted set of devices that require us to force + * a 36 byte host inquiry. This can be overriden as a module param + * (to force all hosts). */ + for (i = 0; i < NUM_BROKEN_INQUIRY_DEVS; i++) { + if ((firmware_revision & 0xffff00) == + sbp2_broken_inquiry_list[i]) { + SBP2_WARN("Node " NODE_BUS_FMT ": Using 36byte inquiry workaround", + NODE_BUS_ARGS(ud->ne->host, ud->ne->nodeid)); + workarounds |= SBP2_BREAKAGE_INQUIRY_HACK; + break; /* No need to continue. */ + } + } + + /* 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(scsi_id, parent_ud); + } else { + scsi_id->sbp2_management_agent_addr = management_agent_addr; + scsi_id->sbp2_command_set_spec_id = command_set_spec_id; + scsi_id->sbp2_command_set = command_set; + scsi_id->sbp2_unit_characteristics = unit_characteristics; + scsi_id->sbp2_firmware_revision = firmware_revision; + scsi_id->workarounds = workarounds; + if (ud->flags & UNIT_DIRECTORY_HAS_LUN) + scsi_id->sbp2_device_type_and_lun = ud->lun; + } +} + +/* + * 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 scsi_id_instance_data *scsi_id) +{ + struct sbp2scsi_host_info *hi = scsi_id->hi; + + SBP2_DEBUG("sbp2_max_speed_and_size"); + + /* Initial setting comes from the hosts speed map */ + scsi_id->speed_code = hi->host->speed_map[NODEID_TO_NODE(hi->host->node_id) * 64 + + NODEID_TO_NODE(scsi_id->ne->nodeid)]; + + /* Bump down our speed if the user requested it */ + if (scsi_id->speed_code > max_speed) { + scsi_id->speed_code = max_speed; + SBP2_ERR("Forcing SBP-2 max speed down to %s", + hpsb_speedto_str[scsi_id->speed_code]); + } + + /* Payload size is the lesser of what our speed supports and what + * our host supports. */ + scsi_id->max_payload_size = min(sbp2_speedto_max_payload[scsi_id->speed_code], + (u8)(hi->host->csr.max_rec - 1)); + + HPSB_DEBUG("Node " NODE_BUS_FMT ": Max speed [%s] - Max payload [%u]", + NODE_BUS_ARGS(hi->host, scsi_id->ne->nodeid), + hpsb_speedto_str[scsi_id->speed_code], + 1 << ((u32)scsi_id->max_payload_size + 2)); + + return(0); +} + +/* + * This function is called in order to perform a SBP-2 agent reset. + */ +static int sbp2_agent_reset(struct scsi_id_instance_data *scsi_id, int wait) +{ + quadlet_t data; + u64 addr; + int retval; + + SBP2_DEBUG("sbp2_agent_reset"); + + /* + * Ok, let's write to the target's management agent register + */ + data = ntohl(SBP2_AGENT_RESET_DATA); + addr = scsi_id->sbp2_command_block_agent_addr + SBP2_AGENT_RESET_OFFSET; + + if (wait) + retval = hpsb_node_write(scsi_id->ne, addr, &data, 4); + else + retval = sbp2util_node_write_no_wait(scsi_id->ne, addr, &data, 4); + + if (retval < 0) { + SBP2_ERR("hpsb_node_write failed.\n"); + return -EIO; + } + + /* + * Need to make sure orb pointer is written on next command + */ + scsi_id->last_orb = NULL; + + return(0); +} + +/* + * This function is called to create the actual command orb and s/g list + * out of the scsi command itself. + */ +static int sbp2_create_command_orb(struct scsi_id_instance_data *scsi_id, + struct sbp2_command_info *command, + unchar *scsi_cmd, + unsigned int scsi_use_sg, + unsigned int scsi_request_bufflen, + void *scsi_request_buffer, + enum dma_data_direction dma_dir) + +{ + struct sbp2scsi_host_info *hi = scsi_id->hi; + struct scatterlist *sgpnt = (struct scatterlist *) scsi_request_buffer; + struct sbp2_command_orb *command_orb = &command->command_orb; + struct sbp2_unrestricted_page_table *scatter_gather_element = + &command->scatter_gather_element[0]; + u32 sg_count, sg_len, orb_direction; + dma_addr_t sg_addr; + int i; + + /* + * 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). + */ + command_orb->next_ORB_hi = ORB_SET_NULL_PTR(1); + command_orb->next_ORB_lo = 0x0; + command_orb->misc = ORB_SET_MAX_PAYLOAD(scsi_id->max_payload_size); + command_orb->misc |= ORB_SET_SPEED(scsi_id->speed_code); + command_orb->misc |= ORB_SET_NOTIFY(1); /* Notify us when complete */ + + /* + * Get the direction of the transfer. If the direction is unknown, then use our + * goofy table as a back-up. + */ + switch (dma_dir) { + case DMA_NONE: + orb_direction = ORB_DIRECTION_NO_DATA_TRANSFER; + break; + case DMA_TO_DEVICE: + orb_direction = ORB_DIRECTION_WRITE_TO_MEDIA; + break; + case DMA_FROM_DEVICE: + orb_direction = ORB_DIRECTION_READ_FROM_MEDIA; + break; + case DMA_BIDIRECTIONAL: + default: + SBP2_ERR("SCSI data transfer direction not specified. " + "Update the SBP2 direction table in sbp2.h if " + "necessary for your application"); + __scsi_print_command(scsi_cmd); + orb_direction = sbp2scsi_direction_table[*scsi_cmd]; + break; + } + + /* + * Set-up our pagetable stuff... unfortunately, this has become + * messier than I'd like. Need to clean this up a bit. ;-) + */ + if (orb_direction == ORB_DIRECTION_NO_DATA_TRANSFER) { + + SBP2_DEBUG("No data transfer"); + + /* + * Handle no data transfer + */ + command_orb->data_descriptor_hi = 0x0; + command_orb->data_descriptor_lo = 0x0; + command_orb->misc |= ORB_SET_DIRECTION(1); + + } else if (scsi_use_sg) { + + SBP2_DEBUG("Use scatter/gather"); + + /* + * Special case if only one element (and less than 64KB in size) + */ + if ((scsi_use_sg == 1) && (sgpnt[0].length <= SBP2_MAX_SG_ELEMENT_LENGTH)) { + + SBP2_DEBUG("Only one s/g element"); + command->dma_dir = dma_dir; + command->dma_size = sgpnt[0].length; + command->dma_type = CMD_DMA_PAGE; + command->cmd_dma = pci_map_page(hi->host->pdev, + sgpnt[0].page, + sgpnt[0].offset, + command->dma_size, + command->dma_dir); + SBP2_DMA_ALLOC("single page scatter element"); + + command_orb->data_descriptor_hi = ORB_SET_NODE_ID(hi->host->node_id); + command_orb->data_descriptor_lo = command->cmd_dma; + command_orb->misc |= ORB_SET_DATA_SIZE(command->dma_size); + command_orb->misc |= ORB_SET_DIRECTION(orb_direction); + + } else { + int count = pci_map_sg(hi->host->pdev, sgpnt, scsi_use_sg, dma_dir); + SBP2_DMA_ALLOC("scatter list"); + + command->dma_size = scsi_use_sg; + command->dma_dir = dma_dir; + command->sge_buffer = sgpnt; + + /* use page tables (s/g) */ + command_orb->misc |= ORB_SET_PAGE_TABLE_PRESENT(0x1); + command_orb->misc |= ORB_SET_DIRECTION(orb_direction); + command_orb->data_descriptor_hi = ORB_SET_NODE_ID(hi->host->node_id); + command_orb->data_descriptor_lo = command->sge_dma; + + /* + * Loop through and fill out our sbp-2 page tables + * (and split up anything too large) + */ + for (i = 0, sg_count = 0 ; i < count; i++, sgpnt++) { + sg_len = sg_dma_len(sgpnt); + sg_addr = sg_dma_address(sgpnt); + while (sg_len) { + scatter_gather_element[sg_count].segment_base_lo = sg_addr; + if (sg_len > SBP2_MAX_SG_ELEMENT_LENGTH) { + scatter_gather_element[sg_count].length_segment_base_hi = + PAGE_TABLE_SET_SEGMENT_LENGTH(SBP2_MAX_SG_ELEMENT_LENGTH); + sg_addr += SBP2_MAX_SG_ELEMENT_LENGTH; + sg_len -= SBP2_MAX_SG_ELEMENT_LENGTH; + } else { + scatter_gather_element[sg_count].length_segment_base_hi = + PAGE_TABLE_SET_SEGMENT_LENGTH(sg_len); + sg_len = 0; + } + sg_count++; + } + } + + /* Number of page table (s/g) elements */ + command_orb->misc |= ORB_SET_DATA_SIZE(sg_count); + + sbp2util_packet_dump(scatter_gather_element, + (sizeof(struct sbp2_unrestricted_page_table)) * sg_count, + "sbp2 s/g list", command->sge_dma); + + /* + * Byte swap page tables if necessary + */ + sbp2util_cpu_to_be32_buffer(scatter_gather_element, + (sizeof(struct sbp2_unrestricted_page_table)) * + sg_count); + + } + + } else { + + SBP2_DEBUG("No scatter/gather"); + + command->dma_dir = dma_dir; + command->dma_size = scsi_request_bufflen; + command->dma_type = CMD_DMA_SINGLE; + command->cmd_dma = pci_map_single (hi->host->pdev, scsi_request_buffer, + command->dma_size, + command->dma_dir); + SBP2_DMA_ALLOC("single bulk"); + + /* + * Handle case where we get a command w/o s/g enabled (but + * check for transfers larger than 64K) + */ + if (scsi_request_bufflen <= SBP2_MAX_SG_ELEMENT_LENGTH) { + + command_orb->data_descriptor_hi = ORB_SET_NODE_ID(hi->host->node_id); + command_orb->data_descriptor_lo = command->cmd_dma; + command_orb->misc |= ORB_SET_DATA_SIZE(scsi_request_bufflen); + command_orb->misc |= ORB_SET_DIRECTION(orb_direction); + + /* + * Sanity, in case our direction table is not + * up-to-date + */ + if (!scsi_request_bufflen) { + command_orb->data_descriptor_hi = 0x0; + command_orb->data_descriptor_lo = 0x0; + command_orb->misc |= ORB_SET_DIRECTION(1); + } + + } else { + /* + * Need to turn this into page tables, since the + * buffer is too large. + */ + command_orb->data_descriptor_hi = ORB_SET_NODE_ID(hi->host->node_id); + command_orb->data_descriptor_lo = command->sge_dma; + + /* Use page tables (s/g) */ + command_orb->misc |= ORB_SET_PAGE_TABLE_PRESENT(0x1); + command_orb->misc |= ORB_SET_DIRECTION(orb_direction); + + /* + * fill out our sbp-2 page tables (and split up + * the large buffer) + */ + sg_count = 0; + sg_len = scsi_request_bufflen; + sg_addr = command->cmd_dma; + while (sg_len) { + scatter_gather_element[sg_count].segment_base_lo = sg_addr; + if (sg_len > SBP2_MAX_SG_ELEMENT_LENGTH) { + scatter_gather_element[sg_count].length_segment_base_hi = + PAGE_TABLE_SET_SEGMENT_LENGTH(SBP2_MAX_SG_ELEMENT_LENGTH); + sg_addr += SBP2_MAX_SG_ELEMENT_LENGTH; + sg_len -= SBP2_MAX_SG_ELEMENT_LENGTH; + } else { + scatter_gather_element[sg_count].length_segment_base_hi = + PAGE_TABLE_SET_SEGMENT_LENGTH(sg_len); + sg_len = 0; + } + sg_count++; + } + + /* Number of page table (s/g) elements */ + command_orb->misc |= ORB_SET_DATA_SIZE(sg_count); + + sbp2util_packet_dump(scatter_gather_element, + (sizeof(struct sbp2_unrestricted_page_table)) * sg_count, + "sbp2 s/g list", command->sge_dma); + + /* + * Byte swap page tables if necessary + */ + sbp2util_cpu_to_be32_buffer(scatter_gather_element, + (sizeof(struct sbp2_unrestricted_page_table)) * + sg_count); + + } + + } + + /* + * Byte swap command ORB if necessary + */ + sbp2util_cpu_to_be32_buffer(command_orb, sizeof(struct sbp2_command_orb)); + + /* + * Put our scsi command in the command ORB + */ + memset(command_orb->cdb, 0, 12); + memcpy(command_orb->cdb, scsi_cmd, COMMAND_SIZE(*scsi_cmd)); + + return(0); +} + +/* + * This function is called in order to begin a regular SBP-2 command. + */ +static int sbp2_link_orb_command(struct scsi_id_instance_data *scsi_id, + struct sbp2_command_info *command) +{ + struct sbp2scsi_host_info *hi = scsi_id->hi; + struct sbp2_command_orb *command_orb = &command->command_orb; + struct node_entry *ne = scsi_id->ne; + u64 addr; + + outstanding_orb_incr; + SBP2_ORB_DEBUG("sending command orb %p, total orbs = %x", + command_orb, global_outstanding_command_orbs); + + pci_dma_sync_single_for_device(hi->host->pdev, command->command_orb_dma, + sizeof(struct sbp2_command_orb), + PCI_DMA_BIDIRECTIONAL); + pci_dma_sync_single_for_device(hi->host->pdev, command->sge_dma, + sizeof(command->scatter_gather_element), + PCI_DMA_BIDIRECTIONAL); + /* + * Check to see if there are any previous orbs to use + */ + if (scsi_id->last_orb == NULL) { + quadlet_t data[2]; + + /* + * Ok, let's write to the target's management agent register + */ + addr = scsi_id->sbp2_command_block_agent_addr + SBP2_ORB_POINTER_OFFSET; + data[0] = ORB_SET_NODE_ID(hi->host->node_id); + data[1] = command->command_orb_dma; + sbp2util_cpu_to_be32_buffer(data, 8); + + SBP2_ORB_DEBUG("write command agent, command orb %p", command_orb); + + if (sbp2util_node_write_no_wait(ne, addr, data, 8) < 0) { + SBP2_ERR("sbp2util_node_write_no_wait failed.\n"); + return -EIO; + } + + SBP2_ORB_DEBUG("write command agent complete"); + + scsi_id->last_orb = command_orb; + scsi_id->last_orb_dma = command->command_orb_dma; + + } else { + quadlet_t data; + + /* + * We have an orb already sent (maybe or maybe not + * processed) that we can append this orb to. So do so, + * and ring the doorbell. Have to be very careful + * modifying these next orb pointers, as they are accessed + * both by the sbp2 device and us. + */ + scsi_id->last_orb->next_ORB_lo = + cpu_to_be32(command->command_orb_dma); + /* Tells hardware that this pointer is valid */ + scsi_id->last_orb->next_ORB_hi = 0x0; + pci_dma_sync_single_for_device(hi->host->pdev, scsi_id->last_orb_dma, + sizeof(struct sbp2_command_orb), + PCI_DMA_BIDIRECTIONAL); + + /* + * Ring the doorbell + */ + data = cpu_to_be32(command->command_orb_dma); + addr = scsi_id->sbp2_command_block_agent_addr + SBP2_DOORBELL_OFFSET; + + SBP2_ORB_DEBUG("ring doorbell, command orb %p", command_orb); + + if (sbp2util_node_write_no_wait(ne, addr, &data, 4) < 0) { + SBP2_ERR("sbp2util_node_write_no_wait failed"); + return(-EIO); + } + + scsi_id->last_orb = command_orb; + scsi_id->last_orb_dma = command->command_orb_dma; + + } + return(0); +} + +/* + * This function is called in order to begin a regular SBP-2 command. + */ +static int sbp2_send_command(struct scsi_id_instance_data *scsi_id, + struct scsi_cmnd *SCpnt, + void (*done)(struct scsi_cmnd *)) +{ + unchar *cmd = (unchar *) SCpnt->cmnd; + unsigned int request_bufflen = SCpnt->request_bufflen; + struct sbp2_command_info *command; + + SBP2_DEBUG("sbp2_send_command"); +#if (CONFIG_IEEE1394_SBP2_DEBUG >= 2) || defined(CONFIG_IEEE1394_SBP2_PACKET_DUMP) + printk("[scsi command]\n "); + scsi_print_command(SCpnt); +#endif + SBP2_DEBUG("SCSI transfer size = %x", request_bufflen); + SBP2_DEBUG("SCSI s/g elements = %x", (unsigned int)SCpnt->use_sg); + + /* + * Allocate a command orb and s/g structure + */ + command = sbp2util_allocate_command_orb(scsi_id, SCpnt, done); + if (!command) { + return(-EIO); + } + + /* + * The scsi stack sends down a request_bufflen which does not match the + * length field in the scsi cdb. This causes some sbp2 devices to + * reject this inquiry command. Fix the request_bufflen. + */ + if (*cmd == INQUIRY) { + if (force_inquiry_hack || scsi_id->workarounds & SBP2_BREAKAGE_INQUIRY_HACK) + request_bufflen = cmd[4] = 0x24; + else + request_bufflen = cmd[4]; + } + + /* + * Now actually fill in the comamnd orb and sbp2 s/g list + */ + sbp2_create_command_orb(scsi_id, command, cmd, SCpnt->use_sg, + request_bufflen, SCpnt->request_buffer, + SCpnt->sc_data_direction); + /* + * Update our cdb if necessary (to handle sbp2 RBC command set + * differences). This is where the command set hacks go! =) + */ + sbp2_check_sbp2_command(scsi_id, command->command_orb.cdb); + + sbp2util_packet_dump(&command->command_orb, sizeof(struct sbp2_command_orb), + "sbp2 command orb", command->command_orb_dma); + + /* + * Initialize status fifo + */ + memset(&scsi_id->status_block, 0, sizeof(struct sbp2_status_block)); + + /* + * Link up the orb, and ring the doorbell if needed + */ + sbp2_link_orb_command(scsi_id, command); + + return(0); +} + + +/* + * This function deals with command set differences between Linux scsi + * command set and sbp2 RBC command set. + */ +static void sbp2_check_sbp2_command(struct scsi_id_instance_data *scsi_id, unchar *cmd) +{ + unchar new_cmd[16]; + u8 device_type = SBP2_DEVICE_TYPE (scsi_id->sbp2_device_type_and_lun); + + SBP2_DEBUG("sbp2_check_sbp2_command"); + + switch (*cmd) { + + case READ_6: + + if (sbp2_command_conversion_device_type(device_type)) { + + SBP2_DEBUG("Convert READ_6 to READ_10"); + + /* + * Need to turn read_6 into read_10 + */ + new_cmd[0] = 0x28; + new_cmd[1] = (cmd[1] & 0xe0); + new_cmd[2] = 0x0; + new_cmd[3] = (cmd[1] & 0x1f); + new_cmd[4] = cmd[2]; + new_cmd[5] = cmd[3]; + new_cmd[6] = 0x0; + new_cmd[7] = 0x0; + new_cmd[8] = cmd[4]; + new_cmd[9] = cmd[5]; + + memcpy(cmd, new_cmd, 10); + + } + + break; + + case WRITE_6: + + if (sbp2_command_conversion_device_type(device_type)) { + + SBP2_DEBUG("Convert WRITE_6 to WRITE_10"); + + /* + * Need to turn write_6 into write_10 + */ + new_cmd[0] = 0x2a; + new_cmd[1] = (cmd[1] & 0xe0); + new_cmd[2] = 0x0; + new_cmd[3] = (cmd[1] & 0x1f); + new_cmd[4] = cmd[2]; + new_cmd[5] = cmd[3]; + new_cmd[6] = 0x0; + new_cmd[7] = 0x0; + new_cmd[8] = cmd[4]; + new_cmd[9] = cmd[5]; + + memcpy(cmd, new_cmd, 10); + + } + + break; + + case MODE_SENSE: + + if (sbp2_command_conversion_device_type(device_type)) { + + SBP2_DEBUG("Convert MODE_SENSE_6 to MODE_SENSE_10"); + + /* + * Need to turn mode_sense_6 into mode_sense_10 + */ + new_cmd[0] = 0x5a; + new_cmd[1] = cmd[1]; + new_cmd[2] = cmd[2]; + new_cmd[3] = 0x0; + new_cmd[4] = 0x0; + new_cmd[5] = 0x0; + new_cmd[6] = 0x0; + new_cmd[7] = 0x0; + new_cmd[8] = cmd[4]; + new_cmd[9] = cmd[5]; + + memcpy(cmd, new_cmd, 10); + + } + + break; + + case MODE_SELECT: + + /* + * TODO. Probably need to change mode select to 10 byte version + */ + + default: + break; + } + + return; +} + +/* + * 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) +{ + SBP2_DEBUG("sbp2_status_to_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); /* return scsi status */ +} + +/* + * This function is called after a command is completed, in order to do any necessary SBP-2 + * response data translations for the SCSI stack + */ +static void sbp2_check_sbp2_response(struct scsi_id_instance_data *scsi_id, + struct scsi_cmnd *SCpnt) +{ + u8 *scsi_buf = SCpnt->request_buffer; + u8 device_type = SBP2_DEVICE_TYPE (scsi_id->sbp2_device_type_and_lun); + + SBP2_DEBUG("sbp2_check_sbp2_response"); + + switch (SCpnt->cmnd[0]) { + + case INQUIRY: + + /* + * If scsi_id->sbp2_device_type_and_lun is uninitialized, then fill + * this information in from the inquiry response data. Lun is set to zero. + */ + if (scsi_id->sbp2_device_type_and_lun == SBP2_DEVICE_TYPE_LUN_UNINITIALIZED) { + SBP2_DEBUG("Creating sbp2_device_type_and_lun from scsi inquiry data"); + scsi_id->sbp2_device_type_and_lun = (scsi_buf[0] & 0x1f) << 16; + } + + /* + * Make sure data length is ok. Minimum length is 36 bytes + */ + if (scsi_buf[4] == 0) { + scsi_buf[4] = 36 - 5; + } + + /* + * Check for Simple Direct Access Device and change it to TYPE_DISK + */ + if ((scsi_buf[0] & 0x1f) == TYPE_SDAD) { + SBP2_DEBUG("Changing TYPE_SDAD to TYPE_DISK"); + scsi_buf[0] &= 0xe0; + } + + /* + * Fix ansi revision and response data format + */ + scsi_buf[2] |= 2; + scsi_buf[3] = (scsi_buf[3] & 0xf0) | 2; + + break; + + case MODE_SENSE: + + if (sbp2_command_conversion_device_type(device_type)) { + + SBP2_DEBUG("Modify mode sense response (10 byte version)"); + + scsi_buf[0] = scsi_buf[1]; /* Mode data length */ + scsi_buf[1] = scsi_buf[2]; /* Medium type */ + scsi_buf[2] = scsi_buf[3]; /* Device specific parameter */ + scsi_buf[3] = scsi_buf[7]; /* Block descriptor length */ + memcpy(scsi_buf + 4, scsi_buf + 8, scsi_buf[0]); + } + + break; + + case MODE_SELECT: + + /* + * TODO. Probably need to change mode select to 10 byte version + */ + + default: + break; + } + return; +} + +/* + * This function deals with status writes from the SBP-2 device + */ +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 sbp2scsi_host_info *hi; + struct scsi_id_instance_data *scsi_id = NULL, *scsi_id_tmp; + u32 id; + struct scsi_cmnd *SCpnt = NULL; + u32 scsi_status = SBP2_SCSI_STATUS_GOOD; + struct sbp2_command_info *command; + + SBP2_DEBUG("sbp2_handle_status_write"); + + sbp2util_packet_dump(data, length, "sbp2 status write by device", (u32)addr); + + if (!host) { + SBP2_ERR("host is NULL - this is bad!"); + return(RCODE_ADDRESS_ERROR); + } + + hi = hpsb_get_hostinfo(&sbp2_highlevel, host); + + if (!hi) { + SBP2_ERR("host info is NULL - this is bad!"); + return(RCODE_ADDRESS_ERROR); + } + + /* + * Find our scsi_id structure by looking at the status fifo address written to by + * the sbp2 device. + */ + id = SBP2_STATUS_FIFO_OFFSET_TO_ENTRY((u32)(addr - SBP2_STATUS_FIFO_ADDRESS)); + list_for_each_entry(scsi_id_tmp, &hi->scsi_ids, scsi_list) { + if (scsi_id_tmp->ne->nodeid == nodeid && scsi_id_tmp->ud->id == id) { + scsi_id = scsi_id_tmp; + break; + } + } + + if (!scsi_id) { + SBP2_ERR("scsi_id is NULL - device is gone?"); + return(RCODE_ADDRESS_ERROR); + } + + /* + * Put response into scsi_id status fifo... + */ + memcpy(&scsi_id->status_block, data, length); + + /* + * Byte swap first two quadlets (8 bytes) of status for processing + */ + sbp2util_be32_to_cpu_buffer(&scsi_id->status_block, 8); + + /* + * Handle command ORB status here if necessary. First, need to match status with command. + */ + command = sbp2util_find_command_for_orb(scsi_id, scsi_id->status_block.ORB_offset_lo); + if (command) { + + SBP2_DEBUG("Found status for command ORB"); + pci_dma_sync_single_for_cpu(hi->host->pdev, command->command_orb_dma, + sizeof(struct sbp2_command_orb), + PCI_DMA_BIDIRECTIONAL); + pci_dma_sync_single_for_cpu(hi->host->pdev, command->sge_dma, + sizeof(command->scatter_gather_element), + PCI_DMA_BIDIRECTIONAL); + + SBP2_ORB_DEBUG("matched command orb %p", &command->command_orb); + outstanding_orb_decr; + + /* + * Matched status with command, now grab scsi command pointers and check status + */ + SCpnt = command->Current_SCpnt; + sbp2util_mark_command_completed(scsi_id, command); + + if (SCpnt) { + + /* + * See if the target stored any scsi status information + */ + if (STATUS_GET_LENGTH(scsi_id->status_block.ORB_offset_hi_misc) > 1) { + /* + * Translate SBP-2 status to SCSI sense data + */ + SBP2_DEBUG("CHECK CONDITION"); + scsi_status = sbp2_status_to_sense_data((unchar *)&scsi_id->status_block, SCpnt->sense_buffer); + } + + /* + * Check to see if the dead bit is set. If so, we'll have to initiate + * a fetch agent reset. + */ + if (STATUS_GET_DEAD_BIT(scsi_id->status_block.ORB_offset_hi_misc)) { + + /* + * Initiate a fetch agent reset. + */ + SBP2_DEBUG("Dead bit set - initiating fetch agent reset"); + sbp2_agent_reset(scsi_id, 0); + } + + SBP2_ORB_DEBUG("completing command orb %p", &command->command_orb); + } + + /* + * Check here to see if there are no commands in-use. If there are none, we can + * null out last orb so that next time around we write directly to the orb pointer... + * Quick start saves one 1394 bus transaction. + */ + if (list_empty(&scsi_id->sbp2_command_orb_inuse)) { + scsi_id->last_orb = NULL; + } + + } else { + + /* + * It's probably a login/logout/reconnect status. + */ + if ((scsi_id->login_orb_dma == scsi_id->status_block.ORB_offset_lo) || + (scsi_id->query_logins_orb_dma == scsi_id->status_block.ORB_offset_lo) || + (scsi_id->reconnect_orb_dma == scsi_id->status_block.ORB_offset_lo) || + (scsi_id->logout_orb_dma == scsi_id->status_block.ORB_offset_lo)) { + atomic_set(&scsi_id->sbp2_login_complete, 1); + } + } + + if (SCpnt) { + + /* Complete the SCSI command. */ + SBP2_DEBUG("Completing SCSI command"); + sbp2scsi_complete_command(scsi_id, scsi_status, SCpnt, + command->Current_done); + SBP2_ORB_DEBUG("command orb completed"); + } + + return(RCODE_COMPLETE); +} + + +/************************************** + * SCSI interface related section + **************************************/ + +/* + * This routine is the main request entry routine for doing I/O. It is + * called from the scsi stack directly. + */ +static int sbp2scsi_queuecommand(struct scsi_cmnd *SCpnt, + void (*done)(struct scsi_cmnd *)) +{ + struct scsi_id_instance_data *scsi_id = + (struct scsi_id_instance_data *)SCpnt->device->host->hostdata[0]; + struct sbp2scsi_host_info *hi; + + SBP2_DEBUG("sbp2scsi_queuecommand"); + + /* + * If scsi_id is null, it means there is no device in this slot, + * so we should return selection timeout. + */ + if (!scsi_id) { + SCpnt->result = DID_NO_CONNECT << 16; + done (SCpnt); + return 0; + } + + hi = scsi_id->hi; + + if (!hi) { + SBP2_ERR("sbp2scsi_host_info is NULL - this is bad!"); + SCpnt->result = DID_NO_CONNECT << 16; + done (SCpnt); + return(0); + } + + /* + * Until we handle multiple luns, just return selection time-out + * to any IO directed at non-zero LUNs + */ + if (SCpnt->device->lun) { + SCpnt->result = DID_NO_CONNECT << 16; + done (SCpnt); + return(0); + } + + /* + * Check for request sense command, and handle it here + * (autorequest sense) + */ + if (SCpnt->cmnd[0] == REQUEST_SENSE) { + SBP2_DEBUG("REQUEST_SENSE"); + memcpy(SCpnt->request_buffer, SCpnt->sense_buffer, SCpnt->request_bufflen); + memset(SCpnt->sense_buffer, 0, sizeof(SCpnt->sense_buffer)); + sbp2scsi_complete_command(scsi_id, SBP2_SCSI_STATUS_GOOD, SCpnt, done); + return(0); + } + + /* + * Check to see if we are in the middle of a bus reset. + */ + if (!hpsb_node_entry_valid(scsi_id->ne)) { + SBP2_ERR("Bus reset in progress - rejecting command"); + SCpnt->result = DID_BUS_BUSY << 16; + done (SCpnt); + return(0); + } + + /* + * Try and send our SCSI command + */ + if (sbp2_send_command(scsi_id, SCpnt, done)) { + SBP2_ERR("Error sending SCSI command"); + sbp2scsi_complete_command(scsi_id, SBP2_SCSI_STATUS_SELECTION_TIMEOUT, + SCpnt, done); + } + + return(0); +} + +/* + * This function is called in order to complete all outstanding SBP-2 + * commands (in case of resets, etc.). + */ +static void sbp2scsi_complete_all_commands(struct scsi_id_instance_data *scsi_id, + u32 status) +{ + struct sbp2scsi_host_info *hi = scsi_id->hi; + struct list_head *lh; + struct sbp2_command_info *command; + + SBP2_DEBUG("sbp2scsi_complete_all_commands"); + + while (!list_empty(&scsi_id->sbp2_command_orb_inuse)) { + SBP2_DEBUG("Found pending command to complete"); + lh = scsi_id->sbp2_command_orb_inuse.next; + command = list_entry(lh, struct sbp2_command_info, list); + pci_dma_sync_single_for_cpu(hi->host->pdev, command->command_orb_dma, + sizeof(struct sbp2_command_orb), + PCI_DMA_BIDIRECTIONAL); + pci_dma_sync_single_for_cpu(hi->host->pdev, command->sge_dma, + sizeof(command->scatter_gather_element), + PCI_DMA_BIDIRECTIONAL); + sbp2util_mark_command_completed(scsi_id, command); + if (command->Current_SCpnt) { + command->Current_SCpnt->result = status << 16; + command->Current_done(command->Current_SCpnt); + } + } + + return; +} + +/* + * This function is called in order to complete a regular SBP-2 command. + * + * This can be called in interrupt context. + */ +static void sbp2scsi_complete_command(struct scsi_id_instance_data *scsi_id, + u32 scsi_status, struct scsi_cmnd *SCpnt, + void (*done)(struct scsi_cmnd *)) +{ + unsigned long flags; + + SBP2_DEBUG("sbp2scsi_complete_command"); + + /* + * Sanity + */ + if (!SCpnt) { + SBP2_ERR("SCpnt is NULL"); + return; + } + + /* + * If a bus reset is in progress and there was an error, don't + * complete the command, just let it get retried at the end of the + * bus reset. + */ + if (!hpsb_node_entry_valid(scsi_id->ne) && (scsi_status != SBP2_SCSI_STATUS_GOOD)) { + SBP2_ERR("Bus reset in progress - retry command later"); + return; + } + + /* + * Switch on scsi status + */ + switch (scsi_status) { + case SBP2_SCSI_STATUS_GOOD: + SCpnt->result = DID_OK; + 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: + SBP2_DEBUG("SBP2_SCSI_STATUS_CHECK_CONDITION"); + SCpnt->result = CHECK_CONDITION << 1; + + /* + * Debug stuff + */ +#if CONFIG_IEEE1394_SBP2_DEBUG >= 1 + scsi_print_command(SCpnt); + scsi_print_sense("bh", SCpnt); +#endif + + 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; + } + + /* + * Take care of any sbp2 response data mucking here (RBC stuff, etc.) + */ + if (SCpnt->result == DID_OK) { + sbp2_check_sbp2_response(scsi_id, SCpnt); + } + + /* + * 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(scsi_id->ne) && (scsi_status != SBP2_SCSI_STATUS_GOOD)) { + SBP2_ERR("Completing command with busy (bus reset)"); + SCpnt->result = DID_BUS_BUSY << 16; + } + + /* + * If a unit attention occurs, return busy status so it gets + * retried... it could have happened because of a 1394 bus reset + * or hot-plug... + */ +#if 0 + if ((scsi_status == SBP2_SCSI_STATUS_CHECK_CONDITION) && + (SCpnt->sense_buffer[2] == UNIT_ATTENTION)) { + SBP2_DEBUG("UNIT ATTENTION - return busy"); + SCpnt->result = DID_BUS_BUSY << 16; + } +#endif + + /* + * Tell scsi stack that we're done with this command + */ + spin_lock_irqsave(scsi_id->scsi_host->host_lock,flags); + done (SCpnt); + spin_unlock_irqrestore(scsi_id->scsi_host->host_lock,flags); + + return; +} + + +static int sbp2scsi_slave_configure (struct scsi_device *sdev) +{ + blk_queue_dma_alignment(sdev->request_queue, (512 - 1)); + + return 0; +} + + +/* + * 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 scsi_id_instance_data *scsi_id = + (struct scsi_id_instance_data *)SCpnt->device->host->hostdata[0]; + struct sbp2scsi_host_info *hi = scsi_id->hi; + struct sbp2_command_info *command; + + SBP2_ERR("aborting sbp2 command"); + scsi_print_command(SCpnt); + + if (scsi_id) { + + /* + * Right now, just return any matching command structures + * to the free pool. + */ + command = sbp2util_find_command_for_SCpnt(scsi_id, SCpnt); + if (command) { + SBP2_DEBUG("Found command to abort"); + pci_dma_sync_single_for_cpu(hi->host->pdev, + command->command_orb_dma, + sizeof(struct sbp2_command_orb), + PCI_DMA_BIDIRECTIONAL); + pci_dma_sync_single_for_cpu(hi->host->pdev, + command->sge_dma, + sizeof(command->scatter_gather_element), + PCI_DMA_BIDIRECTIONAL); + sbp2util_mark_command_completed(scsi_id, command); + if (command->Current_SCpnt) { + command->Current_SCpnt->result = DID_ABORT << 16; + command->Current_done(command->Current_SCpnt); + } + } + + /* + * Initiate a fetch agent reset. + */ + sbp2_agent_reset(scsi_id, 0); + sbp2scsi_complete_all_commands(scsi_id, DID_BUS_BUSY); + } + + return(SUCCESS); +} + +/* + * Called by scsi stack when something has really gone wrong. + */ +static int sbp2scsi_reset(struct scsi_cmnd *SCpnt) +{ + struct scsi_id_instance_data *scsi_id = + (struct scsi_id_instance_data *)SCpnt->device->host->hostdata[0]; + + SBP2_ERR("reset requested"); + + if (scsi_id) { + SBP2_ERR("Generating sbp2 fetch agent reset"); + sbp2_agent_reset(scsi_id, 0); + } + + return(SUCCESS); +} + +static const char *sbp2scsi_info (struct Scsi_Host *host) +{ + return "SCSI emulation for IEEE-1394 SBP-2 Devices"; +} + +static ssize_t sbp2_sysfs_ieee1394_id_show(struct device *dev, char *buf) +{ + struct scsi_device *sdev; + struct scsi_id_instance_data *scsi_id; + int lun; + + if (!(sdev = to_scsi_device(dev))) + return 0; + + if (!(scsi_id = (struct scsi_id_instance_data *)sdev->host->hostdata[0])) + return 0; + + if (scsi_id->sbp2_device_type_and_lun == SBP2_DEVICE_TYPE_LUN_UNINITIALIZED) + lun = 0; + else + lun = ORB_SET_LUN(scsi_id->sbp2_device_type_and_lun); + + return sprintf(buf, "%016Lx:%d:%d\n", (unsigned long long)scsi_id->ne->guid, + scsi_id->ud->id, lun); +} +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 +}; + +MODULE_AUTHOR("Ben Collins <bcollins@debian.org>"); +MODULE_DESCRIPTION("IEEE-1394 SBP-2 protocol driver"); +MODULE_SUPPORTED_DEVICE(SBP2_DEVICE_NAME); +MODULE_LICENSE("GPL"); + +/* SCSI host template */ +static struct scsi_host_template scsi_driver_template = { + .module = THIS_MODULE, + .name = "SBP-2 IEEE-1394", + .proc_name = SBP2_DEVICE_NAME, + .info = sbp2scsi_info, + .queuecommand = sbp2scsi_queuecommand, + .eh_abort_handler = sbp2scsi_abort, + .eh_device_reset_handler = sbp2scsi_reset, + .eh_bus_reset_handler = sbp2scsi_reset, + .eh_host_reset_handler = sbp2scsi_reset, + .slave_configure = sbp2scsi_slave_configure, + .this_id = -1, + .sg_tablesize = SG_ALL, + .use_clustering = ENABLE_CLUSTERING, + .cmd_per_lun = SBP2_MAX_CMDS, + .can_queue = SBP2_MAX_CMDS, + .emulated = 1, + .sdev_attrs = sbp2_sysfs_sdev_attrs, +}; + +static int sbp2_module_init(void) +{ + int ret; + + SBP2_DEBUG("sbp2_module_init"); + + printk(KERN_INFO "sbp2: %s\n", version); + + /* Module load debug option to force one command at a time (serializing I/O) */ + if (serialize_io) { + SBP2_ERR("Driver forced to serialize I/O (serialize_io = 1)"); + scsi_driver_template.can_queue = 1; + scsi_driver_template.cmd_per_lun = 1; + } + + /* Set max sectors (module load option). Default is 255 sectors. */ + scsi_driver_template.max_sectors = max_sectors; + + + /* Register our high level driver with 1394 stack */ + 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) +{ + SBP2_DEBUG("sbp2_module_exit"); + + 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..a84b039 --- /dev/null +++ b/drivers/ieee1394/sbp2.h @@ -0,0 +1,484 @@ +/* + * 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" + +/* + * SBP2 specific structures and defines + */ + +#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(value) ((value & 0x1) << 31) +#define ORB_SET_NOTIFY(value) ((value & 0x1) << 31) +#define ORB_SET_RQ_FMT(value) ((value & 0x3) << 29) /* unused ? */ +#define ORB_SET_NODE_ID(value) ((value & 0xffff) << 16) +#define ORB_SET_DATA_SIZE(value) (value & 0xffff) +#define ORB_SET_PAGE_SIZE(value) ((value & 0x7) << 16) +#define ORB_SET_PAGE_TABLE_PRESENT(value) ((value & 0x1) << 19) +#define ORB_SET_MAX_PAYLOAD(value) ((value & 0xf) << 20) +#define ORB_SET_SPEED(value) ((value & 0x7) << 24) +#define ORB_SET_DIRECTION(value) ((value & 0x1) << 27) + +struct sbp2_command_orb { + volatile u32 next_ORB_hi; + volatile u32 next_ORB_lo; + u32 data_descriptor_hi; + u32 data_descriptor_lo; + u32 misc; + u8 cdb[12]; +}; + +#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(value) (value & 0xffff) +#define ORB_SET_FUNCTION(value) ((value & 0xf) << 16) +#define ORB_SET_RECONNECT(value) ((value & 0xf) << 20) +#define ORB_SET_EXCLUSIVE(value) ((value & 0x1) << 28) +#define ORB_SET_LOGIN_RESP_LENGTH(value) (value & 0xffff) +#define ORB_SET_PASSWD_LENGTH(value) ((value & 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; +}; + +#define RESPONSE_GET_LOGIN_ID(value) (value & 0xffff) +#define RESPONSE_GET_LENGTH(value) ((value >> 16) & 0xffff) +#define RESPONSE_GET_RECONNECT_HOLD(value) (value & 0xffff) + +struct sbp2_login_response { + u32 length_login_ID; + u32 command_block_agent_hi; + u32 command_block_agent_lo; + u32 reconnect_hold; +}; + +#define ORB_SET_LOGIN_ID(value) (value & 0xffff) + +#define ORB_SET_QUERY_LOGINS_RESP_LENGTH(value) (value & 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; +}; + +#define RESPONSE_GET_MAX_LOGINS(value) (value & 0xffff) +#define RESPONSE_GET_ACTIVE_LOGINS(value) ((RESPONSE_GET_LENGTH(value) - 4) / 12) + +struct sbp2_query_logins_response { + u32 length_max_logins; + u32 misc_IDs; + u32 initiator_misc_hi; + u32 initiator_misc_lo; +}; + +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; +}; + +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; +}; + +#define PAGE_TABLE_SET_SEGMENT_BASE_HI(value) (value & 0xffff) +#define PAGE_TABLE_SET_SEGMENT_LENGTH(value) ((value & 0xffff) << 16) + +struct sbp2_unrestricted_page_table { + u32 length_segment_base_hi; + u32 segment_base_lo; +}; + +#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 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 + +#define STATUS_GET_ORB_OFFSET_HI(value) (value & 0xffff) +#define STATUS_GET_SBP_STATUS(value) ((value >> 16) & 0xff) +#define STATUS_GET_LENGTH(value) ((value >> 24) & 0x7) +#define STATUS_GET_DEAD_BIT(value) ((value >> 27) & 0x1) +#define STATUS_GET_RESP(value) ((value >> 28) & 0x3) +#define STATUS_GET_SRC(value) ((value >> 30) & 0x3) + +struct sbp2_status_block { + u32 ORB_offset_hi_misc; + u32 ORB_offset_lo; + u8 command_set_dependent[24]; +}; + +/* + * Miscellaneous SBP2 related config rom defines + */ + +/* The status fifo address definition below is used as a base for each + * node, which a chunk seperately assigned to each unit directory in the + * node. For example, 0xfffe00000000ULL is used for the first sbp2 device + * detected on node 0, 0xfffe00000020ULL for the next sbp2 device on node + * 0, and so on. + * + * Note: We could use a single status fifo address for all sbp2 devices, + * and figure out which sbp2 device the status belongs to by looking at + * the source node id of the status write... but, using separate addresses + * for each sbp2 unit directory allows for better code and the ability to + * support multiple luns within a single 1394 node. + * + * Also note that we choose the address range below as it is a region + * specified for write posting, where the ohci controller will + * automatically send an ack_complete when the status is written by the + * sbp2 device... saving a split transaction. =) + */ +#define SBP2_STATUS_FIFO_ADDRESS 0xfffe00000000ULL +#define SBP2_STATUS_FIFO_ADDRESS_HI 0xfffe +#define SBP2_STATUS_FIFO_ADDRESS_LO 0x0 + +#define SBP2_STATUS_FIFO_ENTRY_TO_OFFSET(entry) ((entry) << 5) +#define SBP2_STATUS_FIFO_OFFSET_TO_ENTRY(offset) ((offset) >> 5) + +#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_DEVICE_TYPE(q) (((q) >> 16) & 0x1f) +#define SBP2_DEVICE_LUN(q) ((q) & 0xffff) + +#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 +#define SBP2_BUSY_TIMEOUT_VALUE 0xf + +#define SBP2_AGENT_RESET_DATA 0xf + +/* + * Unit spec id and sw version entry for SBP-2 devices + */ + +#define SBP2_UNIT_SPEC_ID_ENTRY 0x0000609e +#define SBP2_SW_VERSION_ENTRY 0x00010483 + +/* + * Other misc defines + */ +#define SBP2_128KB_BROKEN_FIRMWARE 0xa0b800 + +#define SBP2_DEVICE_TYPE_LUN_UNINITIALIZED 0xffffffff + +/* + * SCSI specific stuff + */ + +#define SBP2_MAX_SG_ELEMENT_LENGTH 0xf000 +#define SBP2_MAX_UDS_PER_NODE 16 /* Maximum scsi devices per node */ +#define SBP2_MAX_SECTORS 255 /* Max sectors supported */ + +#ifndef TYPE_SDAD +#define TYPE_SDAD 0x0e /* simplified direct access device */ +#endif + +/* + * SCSI direction table... + * (now used as a back-up in case the direction passed down from above is "unknown") + * + * DIN = IN data direction + * DOU = OUT data direction + * DNO = No data transfer + * DUN = Unknown data direction + * + * Opcode 0xec (Teac specific "opc execute") possibly should be DNO, + * but we'll change it when somebody reports a problem with this. + */ +#define DIN ORB_DIRECTION_READ_FROM_MEDIA +#define DOU ORB_DIRECTION_WRITE_TO_MEDIA +#define DNO ORB_DIRECTION_NO_DATA_TRANSFER +#define DUN DIN + +static unchar sbp2scsi_direction_table[0x100] = { + DNO,DNO,DIN,DIN,DOU,DIN,DIN,DOU,DIN,DUN,DOU,DOU,DUN,DUN,DUN,DIN, + DNO,DIN,DIN,DOU,DIN,DOU,DNO,DNO,DOU,DNO,DIN,DNO,DIN,DOU,DNO,DUN, + DIN,DUN,DIN,DIN,DOU,DIN,DUN,DUN,DIN,DIN,DOU,DNO,DUN,DIN,DOU,DOU, + DOU,DOU,DOU,DNO,DIN,DNO,DNO,DIN,DOU,DOU,DOU,DOU,DIN,DOU,DIN,DOU, + DOU,DOU,DIN,DIN,DIN,DNO,DIN,DNO,DNO,DNO,DUN,DNO,DOU,DIN,DNO,DUN, + DUN,DIN,DIN,DNO,DNO,DOU,DUN,DUN,DNO,DIN,DIN,DNO,DIN,DOU,DUN,DUN, + DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN, + DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN, + DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN, + DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN, + DUN,DNO,DOU,DOU,DIN,DNO,DNO,DNO,DIN,DNO,DOU,DUN,DNO,DIN,DOU,DOU, + DOU,DOU,DOU,DNO,DUN,DIN,DOU,DIN,DIN,DIN,DNO,DNO,DNO,DIN,DIN,DUN, + DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN, + DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN, + DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DOU,DUN,DUN,DUN,DUN,DUN, + DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN +}; + +/* This should be safe */ +#define SBP2_MAX_CMDS 8 + +/* This is the two dma types we use for cmd_dma below */ +enum cmd_dma_types { + CMD_DMA_NONE, + CMD_DMA_PAGE, + CMD_DMA_SINGLE +}; + +/* + * Encapsulates all the info necessary for an outstanding command. + */ +struct sbp2_command_info { + + struct list_head list; + struct sbp2_command_orb command_orb ____cacheline_aligned; + dma_addr_t command_orb_dma ____cacheline_aligned; + 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] ____cacheline_aligned; + dma_addr_t sge_dma ____cacheline_aligned; + void *sge_buffer; + dma_addr_t cmd_dma; + enum cmd_dma_types dma_type; + unsigned long dma_size; + int dma_dir; + +}; + +/* A list of flags for detected oddities and brokeness. */ +#define SBP2_BREAKAGE_128K_MAX_TRANSFER 0x1 +#define SBP2_BREAKAGE_INQUIRY_HACK 0x2 + + +struct sbp2scsi_host_info; + + +/* + * Information needed on a per scsi id basis (one for each sbp2 device) + */ +struct scsi_id_instance_data { + /* + * Various sbp2 specific structures + */ + 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; + + /* + * Stuff we need to know about the sbp2 device itself + */ + u64 sbp2_management_agent_addr; + u64 sbp2_command_block_agent_addr; + u32 speed_code; + u32 max_payload_size; + + /* + * Values pulled from the device's unit directory + */ + u32 sbp2_command_set_spec_id; + u32 sbp2_command_set; + u32 sbp2_unit_characteristics; + u32 sbp2_device_type_and_lun; + u32 sbp2_firmware_revision; + + /* + * Variable used for logins, reconnects, logouts, query logins + */ + atomic_t sbp2_login_complete; + + /* + * Pool of command orbs, so we can have more than overlapped command per id + */ + spinlock_t sbp2_command_orb_lock; + struct list_head sbp2_command_orb_inuse; + struct list_head sbp2_command_orb_completed; + + struct list_head scsi_list; + + /* Node entry, as retrieved from NodeMgr entries */ + struct node_entry *ne; + struct unit_directory *ud; + + /* A backlink to our host_info */ + struct sbp2scsi_host_info *hi; + + /* SCSI related pointers */ + struct scsi_device *sdev; + struct Scsi_Host *scsi_host; + + /* Device specific workarounds/brokeness */ + u32 workarounds; +}; + + +/* Sbp2 host data structure (one per IEEE1394 host) */ +struct sbp2scsi_host_info { + struct hpsb_host *host; /* IEEE1394 host */ + struct list_head scsi_ids; /* List of scsi ids on this host */ +}; + +/* + * Function prototypes + */ + +/* + * Various utility prototypes + */ +static int sbp2util_create_command_orb_pool(struct scsi_id_instance_data *scsi_id); +static void sbp2util_remove_command_orb_pool(struct scsi_id_instance_data *scsi_id); +static struct sbp2_command_info *sbp2util_find_command_for_orb(struct scsi_id_instance_data *scsi_id, dma_addr_t orb); +static struct sbp2_command_info *sbp2util_find_command_for_SCpnt(struct scsi_id_instance_data *scsi_id, void *SCpnt); +static struct sbp2_command_info *sbp2util_allocate_command_orb(struct scsi_id_instance_data *scsi_id, + struct scsi_cmnd *Current_SCpnt, + void (*Current_done)(struct scsi_cmnd *)); +static void sbp2util_mark_command_completed(struct scsi_id_instance_data *scsi_id, + struct sbp2_command_info *command); + + +static int sbp2_start_device(struct scsi_id_instance_data *scsi_id); +static void sbp2_remove_device(struct scsi_id_instance_data *scsi_id); + +#ifdef CONFIG_IEEE1394_SBP2_PHYS_DMA +static int sbp2_handle_physdma_write(struct hpsb_host *host, int nodeid, int destid, quadlet_t *data, + u64 addr, size_t length, u16 flags); +static int sbp2_handle_physdma_read(struct hpsb_host *host, int nodeid, quadlet_t *data, + u64 addr, size_t length, u16 flags); +#endif + +/* + * SBP-2 protocol related prototypes + */ +static int sbp2_query_logins(struct scsi_id_instance_data *scsi_id); +static int sbp2_login_device(struct scsi_id_instance_data *scsi_id); +static int sbp2_reconnect_device(struct scsi_id_instance_data *scsi_id); +static int sbp2_logout_device(struct scsi_id_instance_data *scsi_id); +static int sbp2_handle_status_write(struct hpsb_host *host, int nodeid, int destid, + quadlet_t *data, u64 addr, size_t length, u16 flags); +static int sbp2_agent_reset(struct scsi_id_instance_data *scsi_id, int wait); +static int sbp2_create_command_orb(struct scsi_id_instance_data *scsi_id, + struct sbp2_command_info *command, + unchar *scsi_cmd, + unsigned int scsi_use_sg, + unsigned int scsi_request_bufflen, + void *scsi_request_buffer, + enum dma_data_direction dma_dir); +static int sbp2_link_orb_command(struct scsi_id_instance_data *scsi_id, + struct sbp2_command_info *command); +static int sbp2_send_command(struct scsi_id_instance_data *scsi_id, + struct scsi_cmnd *SCpnt, + void (*done)(struct scsi_cmnd *)); +static unsigned int sbp2_status_to_sense_data(unchar *sbp2_status, unchar *sense_data); +static void sbp2_check_sbp2_command(struct scsi_id_instance_data *scsi_id, unchar *cmd); +static void sbp2_check_sbp2_response(struct scsi_id_instance_data *scsi_id, + struct scsi_cmnd *SCpnt); +static void sbp2_parse_unit_directory(struct scsi_id_instance_data *scsi_id, + struct unit_directory *ud); +static int sbp2_set_busy_timeout(struct scsi_id_instance_data *scsi_id); +static int sbp2_max_speed_and_size(struct scsi_id_instance_data *scsi_id); + +#endif /* SBP2_H */ diff --git a/drivers/ieee1394/video1394.c b/drivers/ieee1394/video1394.c new file mode 100644 index 0000000..4bedf71 --- /dev/null +++ b/drivers/ieee1394/video1394.c @@ -0,0 +1,1527 @@ +/* + * 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: + * + * jds -- add private data to file to keep track of iso contexts associated + * with each open -- so release won't kill all iso transfers. + * + * Damien Douxchamps: Fix failure when the number of DMA pages per frame is + * one. + * + * 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/config.h> +#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/smp_lock.h> +#include <linux/delay.h> +#include <linux/devfs_fs_kernel.h> +#include <linux/bitops.h> +#include <linux/types.h> +#include <linux/vmalloc.h> +#include <linux/timex.h> +#include <linux/mm.h> +#include <linux/ioctl32.h> +#include <linux/compat.h> +#include <linux/cdev.h> + +#include "ieee1394.h" +#include "ieee1394_types.h" +#include "hosts.h" +#include "ieee1394_core.h" +#include "highlevel.h" +#include "video1394.h" +#include "nodemgr.h" +#include "dma.h" + +#include "ohci1394.h" + +#define ISO_CHANNELS 64 + +#ifndef virt_to_page +#define virt_to_page(x) MAP_NR(x) +#endif + +#ifndef vmalloc_32 +#define vmalloc_32(x) vmalloc(x) +#endif + +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; + 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...) +#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); + } + + if (d->ir_prg) + kfree(d->ir_prg); + + if (d->it_prg) + kfree(d->it_prg); + + if (d->buffer_status) + kfree(d->buffer_status); + if (d->buffer_time) + kfree(d->buffer_time); + if (d->last_used_cmd) + kfree(d->last_used_cmd); + if (d->next_buffer) + 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 = kmalloc(sizeof(struct dma_iso_ctx), GFP_KERNEL); + if (d == NULL) { + PRINT(KERN_ERR, ohci->host->id, "Failed to allocate dma_iso_ctx"); + return NULL; + } + + memset(d, 0, sizeof *d); + + 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 * 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(struct dma_prog_region), + GFP_KERNEL); + if (d->prg_reg == NULL) { + 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 = kmalloc(d->num_desc * sizeof(struct dma_cmd *), + GFP_KERNEL); + + if (d->ir_prg == NULL) { + PRINT(KERN_ERR, ohci->host->id, "Failed to allocate dma ir prg"); + free_dma_iso_ctx(d); + return NULL; + } + memset(d->ir_prg, 0, d->num_desc * sizeof(struct dma_cmd *)); + + 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 = kmalloc(d->num_desc * sizeof(struct it_dma_prg *), + GFP_KERNEL); + + if (d->it_prg == NULL) { + PRINT(KERN_ERR, ohci->host->id, + "Failed to allocate dma it prg"); + free_dma_iso_ctx(d); + return NULL; + } + memset(d->it_prg, 0, d->num_desc*sizeof(struct it_dma_prg *)); + + 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 = kmalloc(d->num_desc * sizeof(unsigned int), + GFP_KERNEL); + d->buffer_time = kmalloc(d->num_desc * sizeof(struct timeval), + GFP_KERNEL); + d->last_used_cmd = kmalloc(d->num_desc * sizeof(unsigned int), + GFP_KERNEL); + d->next_buffer = kmalloc(d->num_desc * sizeof(int), + GFP_KERNEL); + + if (d->buffer_status == NULL) { + PRINT(KERN_ERR, ohci->host->id, "Failed to allocate buffer_status"); + free_dma_iso_ctx(d); + return NULL; + } + if (d->buffer_time == NULL) { + PRINT(KERN_ERR, ohci->host->id, "Failed to allocate buffer_time"); + free_dma_iso_ctx(d); + return NULL; + } + if (d->last_used_cmd == NULL) { + PRINT(KERN_ERR, ohci->host->id, "Failed to allocate last_used_cmd"); + free_dma_iso_ctx(d); + return NULL; + } + if (d->next_buffer == NULL) { + PRINT(KERN_ERR, ohci->host->id, "Failed to allocate next_buffer"); + free_dma_iso_ctx(d); + return NULL; + } + memset(d->buffer_status, 0, d->num_desc * sizeof(unsigned int)); + memset(d->buffer_time, 0, d->num_desc * sizeof(struct timeval)); + memset(d->last_used_cmd, 0, d->num_desc * sizeof(unsigned int)); + memset(d->next_buffer, -1, d->num_desc * sizeof(int)); + + spin_lock_init(&d->lock); + + PRINT(KERN_INFO, 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, 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 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 + n * d->buf_size; + 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[i] = VIDEO1394_BUFFER_READY; + do_gettimeofday(&d->buffer_time[i]); + } + } + + 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[i] = VIDEO1394_BUFFER_READY; + } + } + + spin_unlock(&d->lock); + + if (waitqueue_active(&d->waitq)) + wake_up_interruptible(&d->waitq); +} + +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 + n * d->buf_size; + 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 int __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; + } + PRINT(KERN_INFO, 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, 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); + + PRINT(KERN_INFO, 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, 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); + + PRINT(KERN_INFO, ohci->host->id, + "Iso context %d talk on channel %d", d->ctx, + v.channel); + } + + if (copy_to_user((void *)arg, &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; + PRINT(KERN_INFO, 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; + + if (copy_from_user(&v, argp, sizeof(v))) + return -EFAULT; + + d = find_ctx(&ctx->context_list, OHCI_ISO_RECEIVE, v.channel); + if (d == NULL) return -EFAULT; + + if ((v.buffer<0) || (v.buffer>d->num_desc)) { + PRINT(KERN_ERR, ohci->host->id, + "Buffer %d out of range",v.buffer); + return -EINVAL; + } + + spin_lock_irqsave(&d->lock,flags); + + if (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; + + 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[v.buffer], 0) + & 0xfffffff0) | 0x1); + + d->last_buffer = v.buffer; + + 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[v.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)) { + PRINT(KERN_INFO, 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; + + if (copy_from_user(&v, argp, sizeof(v))) + return -EFAULT; + + d = find_ctx(&ctx->context_list, OHCI_ISO_RECEIVE, v.channel); + if (d == NULL) return -EFAULT; + + if ((v.buffer<0) || (v.buffer>d->num_desc)) { + 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]; +// printk("Buffer %d time %d\n", v.buffer, (d->buffer_time[v.buffer]).tv_usec); + + /* + * Look ahead to see how many more buffers have been received + */ + i=0; + while (d->buffer_status[(v.buffer+1)%d->num_desc]== + VIDEO1394_BUFFER_READY) { + v.buffer=(v.buffer+1)%d->num_desc; + i++; + } + spin_unlock_irqrestore(&d->lock, flags); + + v.buffer=i; + if (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; + + 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<0) || (v.buffer>d->num_desc)) { + 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(unsigned int); + 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); + + 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); + if (psizes) + kfree(psizes); + return -EBUSY; + } + + if (d->flags & VIDEO1394_VARIABLE_PACKET_SIZE) { + initialize_dma_it_prg_var_packet_queue( + d, v.buffer, 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[v.buffer], + 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[v.buffer], + 0) & 0xfffffff0) | 0x3); + d->next_buffer[d->last_buffer] = v.buffer; + } + 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); + + /* Tell the controller where the first program is */ + reg_write(ohci, d->cmdPtr, + dma_prog_region_offset_to_bus(&d->prg_reg[v.buffer], 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)) { + PRINT(KERN_INFO, ohci->host->id, + "Waking up iso transmit dma ctx=%d", + d->ctx); + put_timestamp(ohci, d, d->last_buffer); + reg_write(ohci, d->ctrlSet, 0x1000); + } + } + + if (psizes) + 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<0) || (v.buffer>d->num_desc)) { + 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; + } +} + +static long video1394_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + int err; + lock_kernel(); + err = __video1394_ioctl(file, cmd, arg); + unlock_kernel(); + return err; +} + +/* + * 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; + int res = -EINVAL; + + lock_kernel(); + if (ctx->current_ctx == NULL) { + PRINT(KERN_ERR, ctx->ohci->host->id, "Current iso context not set"); + } else + res = dma_region_mmap(&ctx->current_ctx->dma, file, vma); + unlock_kernel(); + + return res; +} + +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 = kmalloc(sizeof(struct file_ctx), GFP_KERNEL); + if (ctx == NULL) { + PRINT(KERN_ERR, ohci->host->id, "Cannot malloc file_ctx"); + return -ENOMEM; + } + + memset(ctx, 0, sizeof(struct file_ctx)); + 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; + + lock_kernel(); + 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; + PRINT(KERN_INFO, 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; + + unlock_kernel(); + 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 struct file_operations video1394_fops= +{ + .owner = THIS_MODULE, + .unlocked_ioctl = video1394_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = video1394_compat_ioctl, +#endif + .mmap = video1394_mmap, + .open = video1394_open, + .release = video1394_release +}; + +/*** HOTPLUG STUFF **********************************************************/ +/* + * Export information about protocols/devices supported by this driver. + */ +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); + +static struct hpsb_protocol_driver video1394_driver = { + .name = "1394 Digital Camera Driver", + .id_table = video1394_id_table, + .driver = { + .name = VIDEO1394_DRIVER_NAME, + .bus = &ieee1394_bus_type, + }, +}; + + +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; + class_simple_device_add(hpsb_protocol_class, MKDEV( + IEEE1394_MAJOR, minor), + NULL, "%s-%d", VIDEO1394_DRIVER_NAME, ohci->host->id); + devfs_mk_cdev(MKDEV(IEEE1394_MAJOR, minor), + S_IFCHR | S_IRUSR | S_IWUSR, + "%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) { + class_simple_device_remove(MKDEV(IEEE1394_MAJOR, + IEEE1394_MINOR_BLOCK_VIDEO1394 * 16 + ohci->host->id)); + devfs_remove("%s/%d", VIDEO1394_DRIVER_NAME, 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); + + devfs_remove(VIDEO1394_DRIVER_NAME); + cdev_del(&video1394_cdev); + + PRINT_G(KERN_INFO, "Removed " VIDEO1394_DRIVER_NAME " module"); +} + +static int __init video1394_init_module (void) +{ + int ret; + + cdev_init(&video1394_cdev, &video1394_fops); + video1394_cdev.owner = THIS_MODULE; + kobject_set_name(&video1394_cdev.kobj, VIDEO1394_DRIVER_NAME); + ret = cdev_add(&video1394_cdev, IEEE1394_VIDEO1394_DEV, 16); + if (ret) { + PRINT_G(KERN_ERR, "video1394: unable to get minor device block"); + return ret; + } + + devfs_mk_dir(VIDEO1394_DRIVER_NAME); + + 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); + devfs_remove(VIDEO1394_DRIVER_NAME); + 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); +MODULE_ALIAS_CHARDEV(IEEE1394_MAJOR, IEEE1394_MINOR_BLOCK_VIDEO1394 * 16); 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 |