Hi Linus,
As you may know, we've been working on a new FireWire stack over on
linux1394-devel. The main driver behind this work is to get a small,
maintainable and supportable FireWire stack, with an acceptable
backwards compatibility story.
I've been talking to Stefan Richter about it and we feel that the new
stack is ready for inclusion into mainline. What I'd like to propose
is that we carry both the new and the old stack in mainline for a few
releases. Once we've reached a satisfactory level of stability and
worked through what regressions there may be, we can consider
deprecating the old stack. Carrying two FireWire stacks in the kernel
at the same time is not ideal, but it allows for wider testing of the
new stack, while keeping the old stack as a fallback for cases where
regressions make the new stack not usable.
There's a lot of good reasons to switch to the new stack and a lot of
reasons to switch away from the old one. Highlights:
- Has been in Fedora rawhide (development branch) and -mm for 3
months, will be shipping in Fedora 7.
- Backwards compatible at the library level; existing user space
libraries have been ported to use the new user space interface.
- Less than 8k lines of code compared to 30k lines of code in the old
stack, and a similar size reduction in the sizes of the .ko's.
- No kernel threads, compared to one subsystem thread and one thread
per FireWire controller in the old stack.
- One user space interface to support zero-copy scatter-gather
streaming, as opposed to the old stacks 4 (was 5) different
streaming interfaces.
- Per-device device files, letting userspace set up more finegrained
access control, such as preventing direct access to FireWire
storage devices.
Regressions:
- eth1394 not ported over. There is nothing preventing this from
being done, though, but there's a couple of infrastructure bits
that aren't done yet.
- No support for the PCILynx chipset. Nobody has this chipset
anymore, and the pcilynx driver in the old stack is bit-rotting anyway.
- Some SBP-2 (storage) devices fail after significant amounts of IO.
Not clear what the problem is, but I can reproduce it here and am
working on fixing it.
Please pull from the juju branch in Stefans repo:
git://git.kernel.org/pub/scm/linux/kernel/git/ieee1394/linux1394-2.6.git juju
thanks,
Kristian
drivers/Makefile | 1 +
drivers/firewire/Kconfig | 60 ++
drivers/firewire/Makefile | 10 +
drivers/firewire/fw-card.c | 544 +++++++++++
drivers/firewire/fw-cdev.c | 954 +++++++++++++++++++
drivers/firewire/fw-device.c | 781 +++++++++++++++
drivers/firewire/fw-device.h | 149 +++
drivers/firewire/fw-iso.c | 163 ++++
drivers/firewire/fw-ohci.c | 1896 +++++++++++++++++++++++++++++++++++++
drivers/firewire/fw-ohci.h | 153 +++
drivers/firewire/fw-sbp2.c | 1165 +++++++++++++++++++++++
drivers/firewire/fw-topology.c | 519 ++++++++++
drivers/firewire/fw-topology.h | 94 ++
drivers/firewire/fw-transaction.c | 889 +++++++++++++++++
drivers/firewire/fw-transaction.h | 505 ++++++++++
drivers/ieee1394/Kconfig | 2 +
include/linux/firewire-cdev.h | 268 ++++++
17 files changed, 8153 insertions(+), 0 deletions(-)
Kristian H?gsberg wrote:
...
> Carrying two FireWire stacks in the kernel
> at the same time is not ideal, but it allows for wider testing of the
> new stack, while keeping the old stack as a fallback for cases where
> regressions make the new stack not usable.
IMO, giving the new stack full exposure will get us the required input
from developers and users so that a migration schedule can be prepared.
> There's a lot of good reasons to switch to the new stack and a lot of
> reasons to switch away from the old one. Highlights:
>
> - Has been in Fedora rawhide (development branch) and -mm for 3
> months, will be shipping in Fedora 7.
There were also a few enthusiasts who gave the new stack a spin via
patchkits which I used to publish.
> - Backwards compatible at the library level; existing user space
> libraries have been ported to use the new user space interface.
>
> - Less than 8k lines of code compared to 30k lines of code in the old
> stack, and a similar size reduction in the sizes of the .ko's.
>
> - No kernel threads, compared to one subsystem thread and one thread
> per FireWire controller in the old stack.
>
> - One user space interface to support zero-copy scatter-gather
> streaming, as opposed to the old stacks 4 (was 5) different
> streaming interfaces.
>
> - Per-device device files, letting userspace set up more finegrained
> access control, such as preventing direct access to FireWire
> storage devices.
Or in short, Kristian has been addressing a number of big TODOs of the
old stack with his reimplementation in a relatively short timeframe,
TODOs which haven't been worked on in the existing stack for years,
literally. There are also some smaller but effective features in the
new stack like gap count optimization for better asynchronous
throughput, something which I never got around to implement for the
mainline stack because I have been busy with bugfixing and janitorial work.
> Regressions:
>
> - eth1394 not ported over. There is nothing preventing this from
> being done, though, but there's a couple of infrastructure bits
> that aren't done yet.
Actually that's one of the reasons why I started to work on eth1394
recently.
> - No support for the PCILynx chipset. Nobody has this chipset
> anymore, and the pcilynx driver in the old stack is bit-rotting anyway.
>
> - Some SBP-2 (storage) devices fail after significant amounts of IO.
> Not clear what the problem is, but I can reproduce it here and am
> working on fixing it.
>
> Please pull from the juju branch in Stefans repo:
>
> git://git.kernel.org/pub/scm/linux/kernel/git/ieee1394/linux1394-2.6.git juju
>
> thanks,
> Kristian
Linus,
the juju branch is branched off v2.6.21-rc3, and it contains a linear
series of 121 commits:
Adrian Bunk (1)
Andrew Morton (4)
Kristian H?gsberg (91)
Marc Butler (1)
Randy Dunlap (1)
Stefan Richter (22)
Thomas Gleixner (1)
If you would rather have it rebased on e.g. 2.6.21 or otherwise
reorganized, or want to have the resulting diff posted for everyone to
look at, I'd gladly do that.
The stat as I get to see it locally and at master.kernel.org:
drivers/Makefile | 1 +
drivers/firewire/Kconfig | 60 +
drivers/firewire/Makefile | 10 +
drivers/firewire/fw-card.c | 544 ++++++++
drivers/firewire/fw-cdev.c | 954 ++++++++++++++
drivers/firewire/fw-device.c | 782 +++++++++++
drivers/firewire/fw-device.h | 149 +++
drivers/firewire/fw-iso.c | 163 +++
drivers/firewire/fw-ohci.c | 1896 +++++++++++++++++++++++++++
drivers/firewire/fw-ohci.h | 153 +++
drivers/firewire/fw-sbp2.c | 1165 ++++++++++++++++
drivers/firewire/fw-topology.c | 519 ++++++++
drivers/firewire/fw-topology.h | 94 ++
drivers/firewire/fw-transaction.c | 889 +++++++++++++
drivers/firewire/fw-transaction.h | 505 +++++++
drivers/ieee1394/Kconfig | 2 +
include/linux/firewire-cdev.h | 268 ++++
17 files changed, 8154 insertions(+), 0 deletions(-)
create mode 100644 drivers/firewire/Kconfig
create mode 100644 drivers/firewire/Makefile
create mode 100644 drivers/firewire/fw-card.c
create mode 100644 drivers/firewire/fw-cdev.c
create mode 100644 drivers/firewire/fw-device.c
create mode 100644 drivers/firewire/fw-device.h
create mode 100644 drivers/firewire/fw-iso.c
create mode 100644 drivers/firewire/fw-ohci.c
create mode 100644 drivers/firewire/fw-ohci.h
create mode 100644 drivers/firewire/fw-sbp2.c
create mode 100644 drivers/firewire/fw-topology.c
create mode 100644 drivers/firewire/fw-topology.h
create mode 100644 drivers/firewire/fw-transaction.c
create mode 100644 drivers/firewire/fw-transaction.h
create mode 100644 include/linux/firewire-cdev.h
--
Stefan Richter
-=====-=-=== -=-= ----=
http://arcgraph.de/sr/
On Tue, May 01, 2007 at 04:27:11PM -0400, Kristian H??gsberg wrote:
> Hi Linus,
>
> As you may know, we've been working on a new FireWire stack over on
> linux1394-devel. The main driver behind this work is to get a small,
> maintainable and supportable FireWire stack, with an acceptable
> backwards compatibility story.
Please send out patches for review first.
Christoph Hellwig wrote:
> On Tue, May 01, 2007 at 04:27:11PM -0400, Kristian H??gsberg wrote:
>> Hi Linus,
>>
>> As you may know, we've been working on a new FireWire stack over on
>> linux1394-devel. The main driver behind this work is to get a small,
>> maintainable and supportable FireWire stack, with an acceptable
>> backwards compatibility story.
>
> Please send out patches for review first.
>
Yes, it's been a while since the last submission for review [1], and
most of the changes went over linux1394-devel only. And to put it
mildly, there aren't a lot of capable reviewers watching that list.
Changes since last submission, AFAIR:
- completion of the DMA engine
- completion of the userspace ABI
- extensions to exported sysfs attributes
- some other feature additions like bus manager capability
- lots of bug fixes
- some style fixes
I'll folllow up with "git diff v2.6.21-rc3..juju" ripped apart,
reordered, and refreshed against 2.6.21:
[PATCH 1/6] firewire: handling of cards, buses, nodes
[PATCH 2/6] firewire: isochronous and asynchronous I/O
[PATCH 3/6] firewire: char device interface
[PATCH 4/6] firewire: OHCI-1394 lowlevel driver
[PATCH 5/6] firewire: SBP-2 highlevel driver
[PATCH 6/6] firewire: add it all to kbuild
(If this division seems odd, don't blame Kristian, blame me. :-)
I'm looking forward to comments.
[1] http://lkml.org/lkml/2006/12/19/306
http://lkml.org/lkml/2006/12/19/307
http://lkml.org/lkml/2006/12/19/309
http://lkml.org/lkml/2006/12/19/310
http://lkml.org/lkml/2006/12/19/308
--
Stefan Richter
-=====-=-=== -=-= ---=-
http://arcgraph.de/sr/
Signed-off-by: Stefan Richter <[email protected]>
---
drivers/firewire/fw-iso.c | 163 +++++
drivers/firewire/fw-transaction.c | 889 ++++++++++++++++++++++++++++++
drivers/firewire/fw-transaction.h | 505 +++++++++++++++++
3 files changed, 1557 insertions(+)
Index: linux_juju/drivers/firewire/fw-iso.c
===================================================================
--- /dev/null
+++ linux_juju/drivers/firewire/fw-iso.c
@@ -0,0 +1,163 @@
+/* -*- c-basic-offset: 8 -*-
+ *
+ * fw-iso.c - Isochronous IO
+ * Copyright (C) 2006 Kristian Hoegsberg <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/dma-mapping.h>
+#include <linux/vmalloc.h>
+#include <linux/mm.h>
+
+#include "fw-transaction.h"
+#include "fw-topology.h"
+#include "fw-device.h"
+
+int
+fw_iso_buffer_init(struct fw_iso_buffer *buffer, struct fw_card *card,
+ int page_count, enum dma_data_direction direction)
+{
+ int i, j, retval = -ENOMEM;
+ dma_addr_t address;
+
+ buffer->page_count = page_count;
+ buffer->direction = direction;
+
+ buffer->pages = kmalloc(page_count * sizeof(buffer->pages[0]),
+ GFP_KERNEL);
+ if (buffer->pages == NULL)
+ goto out;
+
+ for (i = 0; i < buffer->page_count; i++) {
+ buffer->pages[i] = alloc_page(GFP_KERNEL | GFP_DMA32 | __GFP_ZERO);
+ if (buffer->pages[i] == NULL)
+ goto out_pages;
+
+ address = dma_map_page(card->device, buffer->pages[i],
+ 0, PAGE_SIZE, direction);
+ if (dma_mapping_error(address)) {
+ __free_page(buffer->pages[i]);
+ goto out_pages;
+ }
+ set_page_private(buffer->pages[i], address);
+ }
+
+ return 0;
+
+ out_pages:
+ for (j = 0; j < i; j++) {
+ address = page_private(buffer->pages[j]);
+ dma_unmap_page(card->device, address,
+ PAGE_SIZE, DMA_TO_DEVICE);
+ __free_page(buffer->pages[j]);
+ }
+ kfree(buffer->pages);
+ out:
+ buffer->pages = NULL;
+ return retval;
+}
+
+int fw_iso_buffer_map(struct fw_iso_buffer *buffer, struct vm_area_struct *vma)
+{
+ unsigned long uaddr;
+ int i, retval;
+
+ uaddr = vma->vm_start;
+ for (i = 0; i < buffer->page_count; i++) {
+ retval = vm_insert_page(vma, uaddr, buffer->pages[i]);
+ if (retval)
+ return retval;
+ uaddr += PAGE_SIZE;
+ }
+
+ return 0;
+}
+
+void fw_iso_buffer_destroy(struct fw_iso_buffer *buffer,
+ struct fw_card *card)
+{
+ int i;
+ dma_addr_t address;
+
+ for (i = 0; i < buffer->page_count; i++) {
+ address = page_private(buffer->pages[i]);
+ dma_unmap_page(card->device, address,
+ PAGE_SIZE, DMA_TO_DEVICE);
+ __free_page(buffer->pages[i]);
+ }
+
+ kfree(buffer->pages);
+ buffer->pages = NULL;
+}
+
+struct fw_iso_context *
+fw_iso_context_create(struct fw_card *card, int type,
+ int channel, int speed, size_t header_size,
+ fw_iso_callback_t callback, void *callback_data)
+{
+ struct fw_iso_context *ctx;
+
+ ctx = card->driver->allocate_iso_context(card, type, header_size);
+ if (IS_ERR(ctx))
+ return ctx;
+
+ ctx->card = card;
+ ctx->type = type;
+ ctx->channel = channel;
+ ctx->speed = speed;
+ ctx->header_size = header_size;
+ ctx->callback = callback;
+ ctx->callback_data = callback_data;
+
+ return ctx;
+}
+EXPORT_SYMBOL(fw_iso_context_create);
+
+void fw_iso_context_destroy(struct fw_iso_context *ctx)
+{
+ struct fw_card *card = ctx->card;
+
+ card->driver->free_iso_context(ctx);
+}
+EXPORT_SYMBOL(fw_iso_context_destroy);
+
+int
+fw_iso_context_start(struct fw_iso_context *ctx, int cycle, int sync, int tags)
+{
+ return ctx->card->driver->start_iso(ctx, cycle, sync, tags);
+}
+EXPORT_SYMBOL(fw_iso_context_start);
+
+int
+fw_iso_context_queue(struct fw_iso_context *ctx,
+ struct fw_iso_packet *packet,
+ struct fw_iso_buffer *buffer,
+ unsigned long payload)
+{
+ struct fw_card *card = ctx->card;
+
+ return card->driver->queue_iso(ctx, packet, buffer, payload);
+}
+EXPORT_SYMBOL(fw_iso_context_queue);
+
+int
+fw_iso_context_stop(struct fw_iso_context *ctx)
+{
+ return ctx->card->driver->stop_iso(ctx);
+}
+EXPORT_SYMBOL(fw_iso_context_stop);
Index: linux_juju/drivers/firewire/fw-transaction.h
===================================================================
--- /dev/null
+++ linux_juju/drivers/firewire/fw-transaction.h
@@ -0,0 +1,505 @@
+/* -*- c-basic-offset: 8 -*-
+ *
+ * fw-transaction.h - Header for IEEE1394 transaction logic
+ *
+ * Copyright (C) 2003-2006 Kristian Hoegsberg <[email protected]>
+ *
+ * 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 __fw_transaction_h
+#define __fw_transaction_h
+
+#include <linux/device.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/list.h>
+#include <linux/fs.h>
+#include <linux/dma-mapping.h>
+
+#define TCODE_WRITE_QUADLET_REQUEST 0
+#define TCODE_WRITE_BLOCK_REQUEST 1
+#define TCODE_WRITE_RESPONSE 2
+#define TCODE_READ_QUADLET_REQUEST 4
+#define TCODE_READ_BLOCK_REQUEST 5
+#define TCODE_READ_QUADLET_RESPONSE 6
+#define TCODE_READ_BLOCK_RESPONSE 7
+#define TCODE_CYCLE_START 8
+#define TCODE_LOCK_REQUEST 9
+#define TCODE_STREAM_DATA 10
+#define TCODE_LOCK_RESPONSE 11
+
+#define TCODE_IS_READ_REQUEST(tcode) (((tcode) & ~1) == 4)
+#define TCODE_IS_BLOCK_PACKET(tcode) (((tcode) & 1) != 0)
+#define TCODE_IS_REQUEST(tcode) (((tcode) & 2) == 0)
+#define TCODE_IS_RESPONSE(tcode) (((tcode) & 2) != 0)
+#define TCODE_HAS_REQUEST_DATA(tcode) (((tcode) & 12) != 4)
+#define TCODE_HAS_RESPONSE_DATA(tcode) (((tcode) & 12) != 0)
+
+/* Juju specific tcodes */
+#define TCODE_DEALLOCATE 0x10
+#define TCODE_LOCK_MASK_SWAP 0x11
+#define TCODE_LOCK_COMPARE_SWAP 0x12
+#define TCODE_LOCK_FETCH_ADD 0x13
+#define TCODE_LOCK_LITTLE_ADD 0x14
+#define TCODE_LOCK_BOUNDED_ADD 0x15
+#define TCODE_LOCK_WRAP_ADD 0x16
+#define TCODE_LOCK_VENDOR_SPECIFIC 0x17
+
+#define SCODE_100 0x0
+#define SCODE_200 0x1
+#define SCODE_400 0x2
+#define SCODE_BETA 0x3
+
+#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_DATA_ERROR 0xd
+#define ACK_TYPE_ERROR 0xe
+
+#define RCODE_COMPLETE 0x0
+#define RCODE_CONFLICT_ERROR 0x4
+#define RCODE_DATA_ERROR 0x5
+#define RCODE_TYPE_ERROR 0x6
+#define RCODE_ADDRESS_ERROR 0x7
+
+/* Juju specific rcodes */
+#define RCODE_SEND_ERROR 0x10
+#define RCODE_CANCELLED 0x11
+#define RCODE_BUSY 0x12
+#define RCODE_GENERATION 0x13
+#define RCODE_NO_ACK 0x14
+
+#define RETRY_1 0x00
+#define RETRY_X 0x01
+#define RETRY_A 0x02
+#define RETRY_B 0x03
+
+#define LOCAL_BUS 0xffc0
+
+#define SELFID_PORT_CHILD 0x3
+#define SELFID_PORT_PARENT 0x2
+#define SELFID_PORT_NCONN 0x1
+#define SELFID_PORT_NONE 0x0
+
+#define PHY_PACKET_CONFIG 0x0
+#define PHY_PACKET_LINK_ON 0x1
+#define PHY_PACKET_SELF_ID 0x2
+
+/* Bit fields _within_ the PHY registers. */
+#define PHY_LINK_ACTIVE 0x80
+#define PHY_CONTENDER 0x40
+#define PHY_BUS_RESET 0x40
+#define PHY_BUS_SHORT_RESET 0x40
+
+#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
+
+#define fw_notify(s, args...) printk(KERN_NOTICE KBUILD_MODNAME ": " s, ## args)
+#define fw_error(s, args...) printk(KERN_ERR KBUILD_MODNAME ": " s, ## args)
+#define fw_debug(s, args...) printk(KERN_DEBUG KBUILD_MODNAME ": " s, ## args)
+
+static inline void
+fw_memcpy_from_be32(void *_dst, void *_src, size_t size)
+{
+ u32 *dst = _dst;
+ u32 *src = _src;
+ int i;
+
+ for (i = 0; i < size / 4; i++)
+ dst[i] = cpu_to_be32(src[i]);
+}
+
+static inline void
+fw_memcpy_to_be32(void *_dst, void *_src, size_t size)
+{
+ fw_memcpy_from_be32(_dst, _src, size);
+}
+
+struct fw_card;
+struct fw_packet;
+struct fw_node;
+struct fw_request;
+
+struct fw_descriptor {
+ struct list_head link;
+ size_t length;
+ u32 immediate;
+ u32 key;
+ const u32 *data;
+};
+
+int fw_core_add_descriptor (struct fw_descriptor *desc);
+void fw_core_remove_descriptor (struct fw_descriptor *desc);
+
+typedef void (*fw_packet_callback_t) (struct fw_packet *packet,
+ struct fw_card *card, int status);
+
+typedef void (*fw_transaction_callback_t)(struct fw_card *card, int rcode,
+ void *data,
+ size_t length,
+ void *callback_data);
+
+typedef void (*fw_address_callback_t)(struct fw_card *card,
+ struct fw_request *request,
+ int tcode, int destination, int source,
+ int generation, int speed,
+ unsigned long long offset,
+ void *data, size_t length,
+ void *callback_data);
+
+typedef void (*fw_bus_reset_callback_t)(struct fw_card *handle,
+ int node_id, int generation,
+ u32 *self_ids,
+ int self_id_count,
+ void *callback_data);
+
+struct fw_packet {
+ int speed;
+ int generation;
+ u32 header[4];
+ size_t header_length;
+ void *payload;
+ size_t payload_length;
+ u32 timestamp;
+
+ /* This callback is called when the packet transmission has
+ * completed; for successful transmission, the status code is
+ * the ack received from the destination, otherwise it's a
+ * negative errno: ENOMEM, ESTALE, ETIMEDOUT, ENODEV, EIO.
+ * The callback can be called from tasklet context and thus
+ * must never block.
+ */
+ fw_packet_callback_t callback;
+ int ack;
+ struct list_head link;
+ void *driver_data;
+};
+
+struct fw_transaction {
+ int node_id; /* The generation is implied; it is always the current. */
+ int tlabel;
+ int timestamp;
+ struct list_head link;
+
+ struct fw_packet packet;
+
+ /* The data passed to the callback is valid only during the
+ * callback. */
+ fw_transaction_callback_t callback;
+ void *callback_data;
+};
+
+static inline struct fw_packet *
+fw_packet(struct list_head *l)
+{
+ return list_entry (l, struct fw_packet, link);
+}
+
+struct fw_address_handler {
+ u64 offset;
+ size_t length;
+ fw_address_callback_t address_callback;
+ void *callback_data;
+ struct list_head link;
+};
+
+
+struct fw_address_region {
+ u64 start;
+ u64 end;
+};
+
+extern const struct fw_address_region fw_low_memory_region;
+extern const struct fw_address_region fw_high_memory_region;
+extern const struct fw_address_region fw_private_region;
+extern const struct fw_address_region fw_csr_region;
+extern const struct fw_address_region fw_unit_space_region;
+
+int fw_core_add_address_handler(struct fw_address_handler *handler,
+ const struct fw_address_region *region);
+void fw_core_remove_address_handler(struct fw_address_handler *handler);
+void fw_fill_response(struct fw_packet *response, u32 *request_header,
+ int rcode, void *payload, size_t length);
+void fw_send_response(struct fw_card *card,
+ struct fw_request *request, int rcode);
+
+extern struct bus_type fw_bus_type;
+
+struct fw_card {
+ const struct fw_card_driver *driver;
+ struct device *device;
+ struct kref kref;
+
+ int node_id;
+ int generation;
+ /* This is the generation used for timestamping incoming requests. */
+ int request_generation;
+ int current_tlabel, tlabel_mask;
+ struct list_head transaction_list;
+ struct timer_list flush_timer;
+ unsigned long reset_jiffies;
+
+ unsigned long long guid;
+ int max_receive;
+ int link_speed;
+ int config_rom_generation;
+
+ /* We need to store up to 4 self ID for a maximum of 63
+ * devices plus 3 words for the topology map header. */
+ int self_id_count;
+ u32 topology_map[252 + 3];
+
+ spinlock_t lock; /* Take this lock when handling the lists in
+ * this struct. */
+ struct fw_node *local_node;
+ struct fw_node *root_node;
+ struct fw_node *irm_node;
+ int color;
+ int gap_count;
+ int topology_type;
+
+ int index;
+
+ struct list_head link;
+
+ /* Work struct for BM duties. */
+ struct delayed_work work;
+ int bm_retries;
+ int bm_generation;
+};
+
+struct fw_card *fw_card_get(struct fw_card *card);
+void fw_card_put(struct fw_card *card);
+
+/* The iso packet format allows for an immediate header/payload part
+ * stored in 'header' immediately after the packet info plus an
+ * indirect payload part that is pointer to by the 'payload' field.
+ * Applications can use one or the other or both to implement simple
+ * low-bandwidth streaming (e.g. audio) or more advanced
+ * scatter-gather streaming (e.g. assembling video frame automatically). */
+
+struct fw_iso_packet {
+ u16 payload_length; /* Length of indirect payload. */
+ u32 interrupt : 1; /* Generate interrupt on this packet */
+ u32 skip : 1; /* Set to not send packet at all. */
+ u32 tag : 2;
+ u32 sy : 4;
+ u32 header_length : 8; /* Length of immediate header. */
+ u32 header[0];
+};
+
+#define FW_ISO_CONTEXT_TRANSMIT 0
+#define FW_ISO_CONTEXT_RECEIVE 1
+
+#define FW_ISO_CONTEXT_MATCH_TAG0 1
+#define FW_ISO_CONTEXT_MATCH_TAG1 2
+#define FW_ISO_CONTEXT_MATCH_TAG2 4
+#define FW_ISO_CONTEXT_MATCH_TAG3 8
+#define FW_ISO_CONTEXT_MATCH_ALL_TAGS 15
+
+struct fw_iso_context;
+
+typedef void (*fw_iso_callback_t) (struct fw_iso_context *context,
+ u32 cycle,
+ size_t header_length,
+ void *header,
+ void *data);
+
+/* An iso buffer is just a set of pages mapped for DMA in the
+ * specified direction. Since the pages are to be used for DMA, they
+ * are not mapped into the kernel virtual address space. We store the
+ * DMA address in the page private. The helper function
+ * fw_iso_buffer_map() will map the pages into a given vma. */
+
+struct fw_iso_buffer {
+ enum dma_data_direction direction;
+ struct page **pages;
+ int page_count;
+};
+
+struct fw_iso_context {
+ struct fw_card *card;
+ int type;
+ int channel;
+ int speed;
+ size_t header_size;
+ fw_iso_callback_t callback;
+ void *callback_data;
+};
+
+int
+fw_iso_buffer_init(struct fw_iso_buffer *buffer,
+ struct fw_card *card,
+ int page_count,
+ enum dma_data_direction direction);
+int
+fw_iso_buffer_map(struct fw_iso_buffer *buffer, struct vm_area_struct *vma);
+void
+fw_iso_buffer_destroy(struct fw_iso_buffer *buffer, struct fw_card *card);
+
+struct fw_iso_context *
+fw_iso_context_create(struct fw_card *card, int type,
+ int channel, int speed, size_t header_size,
+ fw_iso_callback_t callback, void *callback_data);
+
+void
+fw_iso_context_destroy(struct fw_iso_context *ctx);
+
+int
+fw_iso_context_queue(struct fw_iso_context *ctx,
+ struct fw_iso_packet *packet,
+ struct fw_iso_buffer *buffer,
+ unsigned long payload);
+
+int
+fw_iso_context_start(struct fw_iso_context *ctx,
+ int cycle, int sync, int tags);
+
+int
+fw_iso_context_stop(struct fw_iso_context *ctx);
+
+struct fw_card_driver {
+ const char *name;
+
+ /* Enable the given card with the given initial config rom.
+ * This function is expected to activate the card, and either
+ * enable the PHY or set the link_on bit and initiate a bus
+ * reset. */
+ int (*enable) (struct fw_card *card, u32 *config_rom, size_t length);
+
+ int (*update_phy_reg) (struct fw_card *card, int address,
+ int clear_bits, int set_bits);
+
+ /* Update the config rom for an enabled card. This function
+ * should change the config rom that is presented on the bus
+ * an initiate a bus reset. */
+ int (*set_config_rom) (struct fw_card *card,
+ u32 *config_rom, size_t length);
+
+ void (*send_request) (struct fw_card *card, struct fw_packet *packet);
+ void (*send_response) (struct fw_card *card, struct fw_packet *packet);
+ /* Calling cancel is valid once a packet has been submitted. */
+ int (*cancel_packet) (struct fw_card *card, struct fw_packet *packet);
+
+ /* Allow the specified node ID to do direct DMA out and in of
+ * host memory. The card will disable this for all node when
+ * a bus reset happens, so driver need to reenable this after
+ * bus reset. Returns 0 on success, -ENODEV if the card
+ * doesn't support this, -ESTALE if the generation doesn't
+ * match. */
+ int (*enable_phys_dma) (struct fw_card *card,
+ int node_id, int generation);
+
+ u64 (*get_bus_time) (struct fw_card *card);
+
+ struct fw_iso_context *
+ (*allocate_iso_context)(struct fw_card *card,
+ int type, size_t header_size);
+ void (*free_iso_context)(struct fw_iso_context *ctx);
+
+ int (*start_iso)(struct fw_iso_context *ctx,
+ s32 cycle, u32 sync, u32 tags);
+
+ int (*queue_iso)(struct fw_iso_context *ctx,
+ struct fw_iso_packet *packet,
+ struct fw_iso_buffer *buffer,
+ unsigned long payload);
+
+ int (*stop_iso)(struct fw_iso_context *ctx);
+};
+
+int
+fw_core_initiate_bus_reset(struct fw_card *card, int short_reset);
+
+void
+fw_send_request(struct fw_card *card, struct fw_transaction *t,
+ int tcode, int node_id, int generation, int speed,
+ unsigned long long offset,
+ void *data, size_t length,
+ fw_transaction_callback_t callback, void *callback_data);
+
+int fw_cancel_transaction(struct fw_card *card,
+ struct fw_transaction *transaction);
+
+void fw_flush_transactions(struct fw_card *card);
+
+void fw_send_phy_config(struct fw_card *card,
+ int node_id, int generation, int gap_count);
+
+/* Called by the topology code to inform the device code of node
+ * activity; found, lost, or updated nodes */
+void
+fw_node_event(struct fw_card *card, struct fw_node *node, int event);
+
+/* API used by card level drivers */
+
+/* Do we need phy speed here also? If we add more args, maybe we
+ should go back to struct fw_card_info. */
+void
+fw_card_initialize(struct fw_card *card, const struct fw_card_driver *driver,
+ struct device *device);
+int
+fw_card_add(struct fw_card *card,
+ u32 max_receive, u32 link_speed, u64 guid);
+
+void
+fw_core_remove_card(struct fw_card *card);
+
+void
+fw_core_handle_bus_reset(struct fw_card *card,
+ int node_id, int generation,
+ int self_id_count, u32 *self_ids);
+void
+fw_core_handle_request(struct fw_card *card, struct fw_packet *request);
+
+void
+fw_core_handle_response(struct fw_card *card, struct fw_packet *packet);
+
+#endif /* __fw_transaction_h */
Index: linux_juju/drivers/firewire/fw-transaction.c
===================================================================
--- /dev/null
+++ linux_juju/drivers/firewire/fw-transaction.c
@@ -0,0 +1,889 @@
+/* -*- c-basic-offset: 8 -*-
+ *
+ * fw-transaction.c - core IEEE1394 transaction logic
+ *
+ * Copyright (C) 2004-2006 Kristian Hoegsberg <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/poll.h>
+#include <linux/list.h>
+#include <linux/kthread.h>
+#include <asm/uaccess.h>
+#include <asm/semaphore.h>
+
+#include "fw-transaction.h"
+#include "fw-topology.h"
+#include "fw-device.h"
+
+#define header_pri(pri) ((pri) << 0)
+#define header_tcode(tcode) ((tcode) << 4)
+#define header_retry(retry) ((retry) << 8)
+#define header_tlabel(tlabel) ((tlabel) << 10)
+#define header_destination(destination) ((destination) << 16)
+#define header_source(source) ((source) << 16)
+#define header_rcode(rcode) ((rcode) << 12)
+#define header_offset_high(offset_high) ((offset_high) << 0)
+#define header_data_length(length) ((length) << 16)
+#define header_extended_tcode(tcode) ((tcode) << 0)
+
+#define header_get_tcode(q) (((q) >> 4) & 0x0f)
+#define header_get_tlabel(q) (((q) >> 10) & 0x3f)
+#define header_get_rcode(q) (((q) >> 12) & 0x0f)
+#define header_get_destination(q) (((q) >> 16) & 0xffff)
+#define header_get_source(q) (((q) >> 16) & 0xffff)
+#define header_get_offset_high(q) (((q) >> 0) & 0xffff)
+#define header_get_data_length(q) (((q) >> 16) & 0xffff)
+#define header_get_extended_tcode(q) (((q) >> 0) & 0xffff)
+
+#define phy_config_gap_count(gap_count) (((gap_count) << 16) | (1 << 22))
+#define phy_config_root_id(node_id) ((((node_id) & 0x3f) << 24) | (1 << 23))
+#define phy_identifier(id) ((id) << 30)
+
+static int
+close_transaction(struct fw_transaction *transaction,
+ struct fw_card *card, int rcode,
+ u32 *payload, size_t length)
+{
+ struct fw_transaction *t;
+ unsigned long flags;
+
+ spin_lock_irqsave(&card->lock, flags);
+ list_for_each_entry(t, &card->transaction_list, link) {
+ if (t == transaction) {
+ list_del(&t->link);
+ card->tlabel_mask &= ~(1 << t->tlabel);
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&card->lock, flags);
+
+ if (&t->link != &card->transaction_list) {
+ t->callback(card, rcode, payload, length, t->callback_data);
+ return 0;
+ }
+
+ return -ENOENT;
+}
+
+/* Only valid for transactions that are potentially pending (ie have
+ * been sent). */
+int
+fw_cancel_transaction(struct fw_card *card,
+ struct fw_transaction *transaction)
+{
+ /* Cancel the packet transmission if it's still queued. That
+ * will call the packet transmission callback which cancels
+ * the transaction. */
+
+ if (card->driver->cancel_packet(card, &transaction->packet) == 0)
+ return 0;
+
+ /* If the request packet has already been sent, we need to see
+ * if the transaction is still pending and remove it in that case. */
+
+ return close_transaction(transaction, card, RCODE_CANCELLED, NULL, 0);
+}
+EXPORT_SYMBOL(fw_cancel_transaction);
+
+static void
+transmit_complete_callback(struct fw_packet *packet,
+ struct fw_card *card, int status)
+{
+ struct fw_transaction *t =
+ container_of(packet, struct fw_transaction, packet);
+
+ switch (status) {
+ case ACK_COMPLETE:
+ close_transaction(t, card, RCODE_COMPLETE, NULL, 0);
+ break;
+ case ACK_PENDING:
+ t->timestamp = packet->timestamp;
+ break;
+ case ACK_BUSY_X:
+ case ACK_BUSY_A:
+ case ACK_BUSY_B:
+ close_transaction(t, card, RCODE_BUSY, NULL, 0);
+ break;
+ case ACK_DATA_ERROR:
+ close_transaction(t, card, RCODE_DATA_ERROR, NULL, 0);
+ break;
+ case ACK_TYPE_ERROR:
+ close_transaction(t, card, RCODE_TYPE_ERROR, NULL, 0);
+ break;
+ default:
+ /* In this case the ack is really a juju specific
+ * rcode, so just forward that to the callback. */
+ close_transaction(t, card, status, NULL, 0);
+ break;
+ }
+}
+
+static void
+fw_fill_request(struct fw_packet *packet, int tcode, int tlabel,
+ int node_id, int source_id, int generation, int speed,
+ unsigned long long offset, void *payload, size_t length)
+{
+ int ext_tcode;
+
+ if (tcode > 0x10) {
+ ext_tcode = tcode - 0x10;
+ tcode = TCODE_LOCK_REQUEST;
+ } else
+ ext_tcode = 0;
+
+ packet->header[0] =
+ header_retry(RETRY_X) |
+ header_tlabel(tlabel) |
+ header_tcode(tcode) |
+ header_destination(node_id);
+ packet->header[1] =
+ header_offset_high(offset >> 32) | header_source(source_id);
+ packet->header[2] =
+ offset;
+
+ switch (tcode) {
+ case TCODE_WRITE_QUADLET_REQUEST:
+ packet->header[3] = *(u32 *)payload;
+ packet->header_length = 16;
+ packet->payload_length = 0;
+ break;
+
+ case TCODE_LOCK_REQUEST:
+ case TCODE_WRITE_BLOCK_REQUEST:
+ packet->header[3] =
+ header_data_length(length) |
+ header_extended_tcode(ext_tcode);
+ packet->header_length = 16;
+ packet->payload = payload;
+ packet->payload_length = length;
+ break;
+
+ case TCODE_READ_QUADLET_REQUEST:
+ packet->header_length = 12;
+ packet->payload_length = 0;
+ break;
+
+ case TCODE_READ_BLOCK_REQUEST:
+ packet->header[3] =
+ header_data_length(length) |
+ header_extended_tcode(ext_tcode);
+ packet->header_length = 16;
+ packet->payload_length = 0;
+ break;
+ }
+
+ packet->speed = speed;
+ packet->generation = generation;
+ packet->ack = 0;
+}
+
+/**
+ * This function provides low-level access to the IEEE1394 transaction
+ * logic. Most C programs would use either fw_read(), fw_write() or
+ * fw_lock() instead - those function are convenience wrappers for
+ * this function. The fw_send_request() function is primarily
+ * provided as a flexible, one-stop entry point for languages bindings
+ * and protocol bindings.
+ *
+ * FIXME: Document this function further, in particular the possible
+ * values for rcode in the callback. In short, we map ACK_COMPLETE to
+ * RCODE_COMPLETE, internal errors set errno and set rcode to
+ * RCODE_SEND_ERROR (which is out of range for standard ieee1394
+ * rcodes). All other rcodes are forwarded unchanged. For all
+ * errors, payload is NULL, length is 0.
+ *
+ * Can not expect the callback to be called before the function
+ * returns, though this does happen in some cases (ACK_COMPLETE and
+ * errors).
+ *
+ * The payload is only used for write requests and must not be freed
+ * until the callback has been called.
+ *
+ * @param card the card from which to send the request
+ * @param tcode the tcode for this transaction. Do not use
+ * TCODE_LOCK_REQUEST directly, insted use TCODE_LOCK_MASK_SWAP
+ * etc. to specify tcode and ext_tcode.
+ * @param node_id the destination node ID (bus ID and PHY ID concatenated)
+ * @param generation the generation for which node_id is valid
+ * @param speed the speed to use for sending the request
+ * @param offset the 48 bit offset on the destination node
+ * @param payload the data payload for the request subaction
+ * @param length the length in bytes of the data to read
+ * @param callback function to be called when the transaction is completed
+ * @param callback_data pointer to arbitrary data, which will be
+ * passed to the callback
+ */
+void
+fw_send_request(struct fw_card *card, struct fw_transaction *t,
+ int tcode, int node_id, int generation, int speed,
+ unsigned long long offset,
+ void *payload, size_t length,
+ fw_transaction_callback_t callback, void *callback_data)
+{
+ unsigned long flags;
+ int tlabel, source;
+
+ /* Bump the flush timer up 100ms first of all so we
+ * don't race with a flush timer callback. */
+
+ mod_timer(&card->flush_timer, jiffies + DIV_ROUND_UP(HZ, 10));
+
+ /* Allocate tlabel from the bitmap and put the transaction on
+ * the list while holding the card spinlock. */
+
+ spin_lock_irqsave(&card->lock, flags);
+
+ source = card->node_id;
+ tlabel = card->current_tlabel;
+ if (card->tlabel_mask & (1 << tlabel)) {
+ spin_unlock_irqrestore(&card->lock, flags);
+ callback(card, RCODE_SEND_ERROR, NULL, 0, callback_data);
+ return;
+ }
+
+ card->current_tlabel = (card->current_tlabel + 1) & 0x1f;
+ card->tlabel_mask |= (1 << tlabel);
+
+ list_add_tail(&t->link, &card->transaction_list);
+
+ spin_unlock_irqrestore(&card->lock, flags);
+
+ /* Initialize rest of transaction, fill out packet and send it. */
+ t->node_id = node_id;
+ t->tlabel = tlabel;
+ t->callback = callback;
+ t->callback_data = callback_data;
+
+ fw_fill_request(&t->packet, tcode, t->tlabel,
+ node_id, source, generation,
+ speed, offset, payload, length);
+ t->packet.callback = transmit_complete_callback;
+
+ card->driver->send_request(card, &t->packet);
+}
+EXPORT_SYMBOL(fw_send_request);
+
+static void
+transmit_phy_packet_callback(struct fw_packet *packet,
+ struct fw_card *card, int status)
+{
+ kfree(packet);
+}
+
+static void send_phy_packet(struct fw_card *card, u32 data, int generation)
+{
+ struct fw_packet *packet;
+
+ packet = kzalloc(sizeof *packet, GFP_ATOMIC);
+ if (packet == NULL)
+ return;
+
+ packet->header[0] = data;
+ packet->header[1] = ~data;
+ packet->header_length = 8;
+ packet->payload_length = 0;
+ packet->speed = SCODE_100;
+ packet->generation = generation;
+ packet->callback = transmit_phy_packet_callback;
+
+ card->driver->send_request(card, packet);
+}
+
+void fw_send_phy_config(struct fw_card *card,
+ int node_id, int generation, int gap_count)
+{
+ u32 q;
+
+ q = phy_identifier(PHY_PACKET_CONFIG) |
+ phy_config_root_id(node_id) |
+ phy_config_gap_count(gap_count);
+
+ send_phy_packet(card, q, generation);
+}
+
+void fw_flush_transactions(struct fw_card *card)
+{
+ struct fw_transaction *t, *next;
+ struct list_head list;
+ unsigned long flags;
+
+ INIT_LIST_HEAD(&list);
+ spin_lock_irqsave(&card->lock, flags);
+ list_splice_init(&card->transaction_list, &list);
+ card->tlabel_mask = 0;
+ spin_unlock_irqrestore(&card->lock, flags);
+
+ list_for_each_entry_safe(t, next, &list, link) {
+ card->driver->cancel_packet(card, &t->packet);
+
+ /* At this point cancel_packet will never call the
+ * transaction callback, since we just took all the
+ * transactions out of the list. So do it here.*/
+ t->callback(card, RCODE_CANCELLED, NULL, 0, t->callback_data);
+ }
+}
+
+static struct fw_address_handler *
+lookup_overlapping_address_handler(struct list_head *list,
+ unsigned long long offset, size_t length)
+{
+ struct fw_address_handler *handler;
+
+ list_for_each_entry(handler, list, link) {
+ if (handler->offset < offset + length &&
+ offset < handler->offset + handler->length)
+ return handler;
+ }
+
+ return NULL;
+}
+
+static struct fw_address_handler *
+lookup_enclosing_address_handler(struct list_head *list,
+ unsigned long long offset, size_t length)
+{
+ struct fw_address_handler *handler;
+
+ list_for_each_entry(handler, list, link) {
+ if (handler->offset <= offset &&
+ offset + length <= handler->offset + handler->length)
+ return handler;
+ }
+
+ return NULL;
+}
+
+static DEFINE_SPINLOCK(address_handler_lock);
+static LIST_HEAD(address_handler_list);
+
+const struct fw_address_region fw_low_memory_region =
+ { .start = 0x000000000000ULL, .end = 0x000100000000ULL, };
+const struct fw_address_region fw_high_memory_region =
+ { .start = 0x000100000000ULL, .end = 0xffffe0000000ULL, };
+const struct fw_address_region fw_private_region =
+ { .start = 0xffffe0000000ULL, .end = 0xfffff0000000ULL, };
+const struct fw_address_region fw_csr_region =
+ { .start = 0xfffff0000000ULL, .end = 0xfffff0000800ULL, };
+const struct fw_address_region fw_unit_space_region =
+ { .start = 0xfffff0000900ULL, .end = 0x1000000000000ULL, };
+EXPORT_SYMBOL(fw_low_memory_region);
+EXPORT_SYMBOL(fw_high_memory_region);
+EXPORT_SYMBOL(fw_private_region);
+EXPORT_SYMBOL(fw_csr_region);
+EXPORT_SYMBOL(fw_unit_space_region);
+
+/**
+ * Allocate a range of addresses in the node space of the OHCI
+ * controller. When a request is received that falls within the
+ * specified address range, the specified callback is invoked. The
+ * parameters passed to the callback give the details of the
+ * particular request
+ */
+int
+fw_core_add_address_handler(struct fw_address_handler *handler,
+ const struct fw_address_region *region)
+{
+ struct fw_address_handler *other;
+ unsigned long flags;
+ int ret = -EBUSY;
+
+ spin_lock_irqsave(&address_handler_lock, flags);
+
+ handler->offset = region->start;
+ while (handler->offset + handler->length <= region->end) {
+ other =
+ lookup_overlapping_address_handler(&address_handler_list,
+ handler->offset,
+ handler->length);
+ if (other != NULL) {
+ handler->offset += other->length;
+ } else {
+ list_add_tail(&handler->link, &address_handler_list);
+ ret = 0;
+ break;
+ }
+ }
+
+ spin_unlock_irqrestore(&address_handler_lock, flags);
+
+ return ret;
+}
+EXPORT_SYMBOL(fw_core_add_address_handler);
+
+/**
+ * Deallocate a range of addresses allocated with fw_allocate. This
+ * will call the associated callback one last time with a the special
+ * tcode TCODE_DEALLOCATE, to let the client destroy the registered
+ * callback data. For convenience, the callback parameters offset and
+ * length are set to the start and the length respectively for the
+ * deallocated region, payload is set to NULL.
+ */
+void fw_core_remove_address_handler(struct fw_address_handler *handler)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&address_handler_lock, flags);
+ list_del(&handler->link);
+ spin_unlock_irqrestore(&address_handler_lock, flags);
+}
+EXPORT_SYMBOL(fw_core_remove_address_handler);
+
+struct fw_request {
+ struct fw_packet response;
+ u32 request_header[4];
+ int ack;
+ u32 length;
+ u32 data[0];
+};
+
+static void
+free_response_callback(struct fw_packet *packet,
+ struct fw_card *card, int status)
+{
+ struct fw_request *request;
+
+ request = container_of(packet, struct fw_request, response);
+ kfree(request);
+}
+
+void
+fw_fill_response(struct fw_packet *response, u32 *request_header,
+ int rcode, void *payload, size_t length)
+{
+ int tcode, tlabel, extended_tcode, source, destination;
+
+ tcode = header_get_tcode(request_header[0]);
+ tlabel = header_get_tlabel(request_header[0]);
+ source = header_get_destination(request_header[0]);
+ destination = header_get_source(request_header[1]);
+ extended_tcode = header_get_extended_tcode(request_header[3]);
+
+ response->header[0] =
+ header_retry(RETRY_1) |
+ header_tlabel(tlabel) |
+ header_destination(destination);
+ response->header[1] =
+ header_source(source) |
+ header_rcode(rcode);
+ response->header[2] = 0;
+
+ switch (tcode) {
+ case TCODE_WRITE_QUADLET_REQUEST:
+ case TCODE_WRITE_BLOCK_REQUEST:
+ response->header[0] |= header_tcode(TCODE_WRITE_RESPONSE);
+ response->header_length = 12;
+ response->payload_length = 0;
+ break;
+
+ case TCODE_READ_QUADLET_REQUEST:
+ response->header[0] |=
+ header_tcode(TCODE_READ_QUADLET_RESPONSE);
+ if (payload != NULL)
+ response->header[3] = *(u32 *)payload;
+ else
+ response->header[3] = 0;
+ response->header_length = 16;
+ response->payload_length = 0;
+ break;
+
+ case TCODE_READ_BLOCK_REQUEST:
+ case TCODE_LOCK_REQUEST:
+ response->header[0] |= header_tcode(tcode + 2);
+ response->header[3] =
+ header_data_length(length) |
+ header_extended_tcode(extended_tcode);
+ response->header_length = 16;
+ response->payload = payload;
+ response->payload_length = length;
+ break;
+
+ default:
+ BUG();
+ return;
+ }
+}
+EXPORT_SYMBOL(fw_fill_response);
+
+static struct fw_request *
+allocate_request(struct fw_packet *p)
+{
+ struct fw_request *request;
+ u32 *data, length;
+ int request_tcode, t;
+
+ request_tcode = header_get_tcode(p->header[0]);
+ switch (request_tcode) {
+ case TCODE_WRITE_QUADLET_REQUEST:
+ data = &p->header[3];
+ length = 4;
+ break;
+
+ case TCODE_WRITE_BLOCK_REQUEST:
+ case TCODE_LOCK_REQUEST:
+ data = p->payload;
+ length = header_get_data_length(p->header[3]);
+ break;
+
+ case TCODE_READ_QUADLET_REQUEST:
+ data = NULL;
+ length = 4;
+ break;
+
+ case TCODE_READ_BLOCK_REQUEST:
+ data = NULL;
+ length = header_get_data_length(p->header[3]);
+ break;
+
+ default:
+ BUG();
+ return NULL;
+ }
+
+ request = kmalloc(sizeof *request + length, GFP_ATOMIC);
+ if (request == NULL)
+ return NULL;
+
+ t = (p->timestamp & 0x1fff) + 4000;
+ if (t >= 8000)
+ t = (p->timestamp & ~0x1fff) + 0x2000 + t - 8000;
+ else
+ t = (p->timestamp & ~0x1fff) + t;
+
+ request->response.speed = p->speed;
+ request->response.timestamp = t;
+ request->response.generation = p->generation;
+ request->response.ack = 0;
+ request->response.callback = free_response_callback;
+ request->ack = p->ack;
+ request->length = length;
+ if (data)
+ memcpy(request->data, data, length);
+
+ memcpy(request->request_header, p->header, sizeof p->header);
+
+ return request;
+}
+
+void
+fw_send_response(struct fw_card *card, struct fw_request *request, int rcode)
+{
+ /* Broadcast packets are reported as ACK_COMPLETE, so this
+ * check is sufficient to ensure we don't send response to
+ * broadcast packets or posted writes. */
+ if (request->ack != ACK_PENDING)
+ return;
+
+ if (rcode == RCODE_COMPLETE)
+ fw_fill_response(&request->response, request->request_header,
+ rcode, request->data, request->length);
+ else
+ fw_fill_response(&request->response, request->request_header,
+ rcode, NULL, 0);
+
+ card->driver->send_response(card, &request->response);
+}
+EXPORT_SYMBOL(fw_send_response);
+
+void
+fw_core_handle_request(struct fw_card *card, struct fw_packet *p)
+{
+ struct fw_address_handler *handler;
+ struct fw_request *request;
+ unsigned long long offset;
+ unsigned long flags;
+ int tcode, destination, source;
+
+ if (p->payload_length > 2048) {
+ /* FIXME: send error response. */
+ return;
+ }
+
+ if (p->ack != ACK_PENDING && p->ack != ACK_COMPLETE)
+ return;
+
+ request = allocate_request(p);
+ if (request == NULL) {
+ /* FIXME: send statically allocated busy packet. */
+ return;
+ }
+
+ offset =
+ ((unsigned long long)
+ header_get_offset_high(p->header[1]) << 32) | p->header[2];
+ tcode = header_get_tcode(p->header[0]);
+ destination = header_get_destination(p->header[0]);
+ source = header_get_source(p->header[0]);
+
+ spin_lock_irqsave(&address_handler_lock, flags);
+ handler = lookup_enclosing_address_handler(&address_handler_list,
+ offset, request->length);
+ spin_unlock_irqrestore(&address_handler_lock, flags);
+
+ /* FIXME: lookup the fw_node corresponding to the sender of
+ * this request and pass that to the address handler instead
+ * of the node ID. We may also want to move the address
+ * allocations to fw_node so we only do this callback if the
+ * upper layers registered it for this node. */
+
+ if (handler == NULL)
+ fw_send_response(card, request, RCODE_ADDRESS_ERROR);
+ else
+ handler->address_callback(card, request,
+ tcode, destination, source,
+ p->generation, p->speed, offset,
+ request->data, request->length,
+ handler->callback_data);
+}
+EXPORT_SYMBOL(fw_core_handle_request);
+
+void
+fw_core_handle_response(struct fw_card *card, struct fw_packet *p)
+{
+ struct fw_transaction *t;
+ unsigned long flags;
+ u32 *data;
+ size_t data_length;
+ int tcode, tlabel, destination, source, rcode;
+
+ tcode = header_get_tcode(p->header[0]);
+ tlabel = header_get_tlabel(p->header[0]);
+ destination = header_get_destination(p->header[0]);
+ source = header_get_source(p->header[1]);
+ rcode = header_get_rcode(p->header[1]);
+
+ spin_lock_irqsave(&card->lock, flags);
+ list_for_each_entry(t, &card->transaction_list, link) {
+ if (t->node_id == source && t->tlabel == tlabel) {
+ list_del(&t->link);
+ card->tlabel_mask &= ~(1 << t->tlabel);
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&card->lock, flags);
+
+ if (&t->link == &card->transaction_list) {
+ fw_notify("Unsolicited response (source %x, tlabel %x)\n",
+ source, tlabel);
+ return;
+ }
+
+ /* FIXME: sanity check packet, is length correct, does tcodes
+ * and addresses match. */
+
+ switch (tcode) {
+ case TCODE_READ_QUADLET_RESPONSE:
+ data = (u32 *) &p->header[3];
+ data_length = 4;
+ break;
+
+ case TCODE_WRITE_RESPONSE:
+ data = NULL;
+ data_length = 0;
+ break;
+
+ case TCODE_READ_BLOCK_RESPONSE:
+ case TCODE_LOCK_RESPONSE:
+ data = p->payload;
+ data_length = header_get_data_length(p->header[3]);
+ break;
+
+ default:
+ /* Should never happen, this is just to shut up gcc. */
+ data = NULL;
+ data_length = 0;
+ break;
+ }
+
+ t->callback(card, rcode, data, data_length, t->callback_data);
+}
+EXPORT_SYMBOL(fw_core_handle_response);
+
+const struct fw_address_region topology_map_region =
+ { .start = 0xfffff0001000ull, .end = 0xfffff0001400ull, };
+
+static void
+handle_topology_map(struct fw_card *card, struct fw_request *request,
+ int tcode, int destination, int source,
+ int generation, int speed,
+ unsigned long long offset,
+ void *payload, size_t length, void *callback_data)
+{
+ int i, start, end;
+ u32 *map;
+
+ if (!TCODE_IS_READ_REQUEST(tcode)) {
+ fw_send_response(card, request, RCODE_TYPE_ERROR);
+ return;
+ }
+
+ if ((offset & 3) > 0 || (length & 3) > 0) {
+ fw_send_response(card, request, RCODE_ADDRESS_ERROR);
+ return;
+ }
+
+ start = (offset - topology_map_region.start) / 4;
+ end = start + length / 4;
+ map = payload;
+
+ for (i = 0; i < length / 4; i++)
+ map[i] = cpu_to_be32(card->topology_map[start + i]);
+
+ fw_send_response(card, request, RCODE_COMPLETE);
+}
+
+static struct fw_address_handler topology_map = {
+ .length = 0x200,
+ .address_callback = handle_topology_map,
+};
+
+const struct fw_address_region registers_region =
+ { .start = 0xfffff0000000ull, .end = 0xfffff0000400ull, };
+
+static void
+handle_registers(struct fw_card *card, struct fw_request *request,
+ int tcode, int destination, int source,
+ int generation, int speed,
+ unsigned long long offset,
+ void *payload, size_t length, void *callback_data)
+{
+ int reg = offset - CSR_REGISTER_BASE;
+ unsigned long long bus_time;
+ __be32 *data = payload;
+
+ switch (reg) {
+ case CSR_CYCLE_TIME:
+ case CSR_BUS_TIME:
+ if (!TCODE_IS_READ_REQUEST(tcode) || length != 4) {
+ fw_send_response(card, request, RCODE_TYPE_ERROR);
+ break;
+ }
+
+ bus_time = card->driver->get_bus_time(card);
+ if (reg == CSR_CYCLE_TIME)
+ *data = cpu_to_be32(bus_time);
+ else
+ *data = cpu_to_be32(bus_time >> 25);
+ fw_send_response(card, request, RCODE_COMPLETE);
+ break;
+
+ case CSR_BUS_MANAGER_ID:
+ case CSR_BANDWIDTH_AVAILABLE:
+ case CSR_CHANNELS_AVAILABLE_HI:
+ case CSR_CHANNELS_AVAILABLE_LO:
+ /* FIXME: these are handled by the OHCI hardware and
+ * the stack never sees these request. If we add
+ * support for a new type of controller that doesn't
+ * handle this in hardware we need to deal with these
+ * transactions. */
+ BUG();
+ break;
+
+ case CSR_BUSY_TIMEOUT:
+ /* FIXME: Implement this. */
+ default:
+ fw_send_response(card, request, RCODE_ADDRESS_ERROR);
+ break;
+ }
+}
+
+static struct fw_address_handler registers = {
+ .length = 0x400,
+ .address_callback = handle_registers,
+};
+
+MODULE_AUTHOR("Kristian Hoegsberg <[email protected]>");
+MODULE_DESCRIPTION("Core IEEE1394 transaction logic");
+MODULE_LICENSE("GPL");
+
+static const u32 vendor_textual_descriptor[] = {
+ /* textual descriptor leaf () */
+ 0x00060000,
+ 0x00000000,
+ 0x00000000,
+ 0x4c696e75, /* L i n u */
+ 0x78204669, /* x F i */
+ 0x72657769, /* r e w i */
+ 0x72650000, /* r e */
+};
+
+static const u32 model_textual_descriptor[] = {
+ /* model descriptor leaf () */
+ 0x00030000,
+ 0x00000000,
+ 0x00000000,
+ 0x4a756a75, /* J u j u */
+};
+
+static struct fw_descriptor vendor_id_descriptor = {
+ .length = ARRAY_SIZE(vendor_textual_descriptor),
+ .immediate = 0x03d00d1e,
+ .key = 0x81000000,
+ .data = vendor_textual_descriptor,
+};
+
+static struct fw_descriptor model_id_descriptor = {
+ .length = ARRAY_SIZE(model_textual_descriptor),
+ .immediate = 0x17000001,
+ .key = 0x81000000,
+ .data = model_textual_descriptor,
+};
+
+static int __init fw_core_init(void)
+{
+ int retval;
+
+ retval = bus_register(&fw_bus_type);
+ if (retval < 0)
+ return retval;
+
+ fw_cdev_major = register_chrdev(0, "firewire", &fw_device_ops);
+ if (fw_cdev_major < 0) {
+ bus_unregister(&fw_bus_type);
+ return fw_cdev_major;
+ }
+
+ retval = fw_core_add_address_handler(&topology_map,
+ &topology_map_region);
+ BUG_ON(retval < 0);
+
+ retval = fw_core_add_address_handler(®isters,
+ ®isters_region);
+ BUG_ON(retval < 0);
+
+ /* Add the vendor textual descriptor. */
+ retval = fw_core_add_descriptor(&vendor_id_descriptor);
+ BUG_ON(retval < 0);
+ retval = fw_core_add_descriptor(&model_id_descriptor);
+ BUG_ON(retval < 0);
+
+ return 0;
+}
+
+static void __exit fw_core_cleanup(void)
+{
+ unregister_chrdev(fw_cdev_major, "firewire");
+ bus_unregister(&fw_bus_type);
+}
+
+module_init(fw_core_init);
+module_exit(fw_core_cleanup);
--
Stefan Richter
-=====-=-=== -=-= ---=-
http://arcgraph.de/sr/
Signed-off-by: Stefan Richter <[email protected]>
---
drivers/firewire/fw-card.c | 544 ++++++++++++++++++++++
drivers/firewire/fw-device.c | 782 +++++++++++++++++++++++++++++++++
drivers/firewire/fw-device.h | 149 ++++++
drivers/firewire/fw-topology.c | 519 +++++++++++++++++++++
drivers/firewire/fw-topology.h | 94 +++
5 files changed, 2088 insertions(+)
Index: linux_juju/drivers/firewire/fw-card.c
===================================================================
--- /dev/null
+++ linux_juju/drivers/firewire/fw-card.c
@@ -0,0 +1,544 @@
+/* -*- c-basic-offset: 8 -*-
+ *
+ * fw-card.c - card level functions
+ *
+ * Copyright (C) 2005-2006 Kristian Hoegsberg <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/device.h>
+#include <linux/rwsem.h>
+#include "fw-transaction.h"
+#include "fw-topology.h"
+#include "fw-device.h"
+
+/* The lib/crc16.c implementation uses the standard (0x8005)
+ * polynomial, but we need the ITU-T (or CCITT) polynomial (0x1021).
+ * The implementation below works on an array of host-endian u32
+ * words, assuming they'll be transmited msb first. */
+u16
+crc16_itu_t(const u32 *buffer, size_t length)
+{
+ int shift, i;
+ u32 data;
+ u16 sum, crc = 0;
+
+ for (i = 0; i < length; i++) {
+ data = *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 crc;
+}
+
+static DECLARE_RWSEM(card_rwsem);
+static LIST_HEAD(card_list);
+
+static LIST_HEAD(descriptor_list);
+static int descriptor_count;
+
+#define bib_crc(v) ((v) << 0)
+#define bib_crc_length(v) ((v) << 16)
+#define bib_info_length(v) ((v) << 24)
+
+#define bib_link_speed(v) ((v) << 0)
+#define bib_generation(v) ((v) << 4)
+#define bib_max_rom(v) ((v) << 8)
+#define bib_max_receive(v) ((v) << 12)
+#define bib_cyc_clk_acc(v) ((v) << 16)
+#define bib_pmc ((1) << 27)
+#define bib_bmc ((1) << 28)
+#define bib_isc ((1) << 29)
+#define bib_cmc ((1) << 30)
+#define bib_imc ((1) << 31)
+
+static u32 *
+generate_config_rom (struct fw_card *card, size_t *config_rom_length)
+{
+ struct fw_descriptor *desc;
+ static u32 config_rom[256];
+ int i, j, length;
+
+ /* Initialize contents of config rom buffer. On the OHCI
+ * controller, block reads to the config rom accesses the host
+ * memory, but quadlet read access the hardware bus info block
+ * registers. That's just crack, but it means we should make
+ * sure the contents of bus info block in host memory mathces
+ * the version stored in the OHCI registers. */
+
+ memset(config_rom, 0, sizeof config_rom);
+ config_rom[0] = bib_crc_length(4) | bib_info_length(4) | bib_crc(0);
+ config_rom[1] = 0x31333934;
+
+ config_rom[2] =
+ bib_link_speed(card->link_speed) |
+ bib_generation(card->config_rom_generation++ % 14 + 2) |
+ bib_max_rom(2) |
+ bib_max_receive(card->max_receive) |
+ bib_bmc | bib_isc | bib_cmc | bib_imc;
+ config_rom[3] = card->guid >> 32;
+ config_rom[4] = card->guid;
+
+ /* Generate root directory. */
+ i = 5;
+ config_rom[i++] = 0;
+ config_rom[i++] = 0x0c0083c0; /* node capabilities */
+ j = i + descriptor_count;
+
+ /* Generate root directory entries for descriptors. */
+ list_for_each_entry (desc, &descriptor_list, link) {
+ if (desc->immediate > 0)
+ config_rom[i++] = desc->immediate;
+ config_rom[i] = desc->key | (j - i);
+ i++;
+ j += desc->length;
+ }
+
+ /* Update root directory length. */
+ config_rom[5] = (i - 5 - 1) << 16;
+
+ /* End of root directory, now copy in descriptors. */
+ list_for_each_entry (desc, &descriptor_list, link) {
+ memcpy(&config_rom[i], desc->data, desc->length * 4);
+ i += desc->length;
+ }
+
+ /* Calculate CRCs for all blocks in the config rom. This
+ * assumes that CRC length and info length are identical for
+ * the bus info block, which is always the case for this
+ * implementation. */
+ for (i = 0; i < j; i += length + 1) {
+ length = (config_rom[i] >> 16) & 0xff;
+ config_rom[i] |= crc16_itu_t(&config_rom[i + 1], length);
+ }
+
+ *config_rom_length = j;
+
+ return config_rom;
+}
+
+static void
+update_config_roms (void)
+{
+ struct fw_card *card;
+ u32 *config_rom;
+ size_t length;
+
+ list_for_each_entry (card, &card_list, link) {
+ config_rom = generate_config_rom(card, &length);
+ card->driver->set_config_rom(card, config_rom, length);
+ }
+}
+
+int
+fw_core_add_descriptor (struct fw_descriptor *desc)
+{
+ size_t i;
+
+ /* Check descriptor is valid; the length of all blocks in the
+ * descriptor has to add up to exactly the length of the
+ * block. */
+ i = 0;
+ while (i < desc->length)
+ i += (desc->data[i] >> 16) + 1;
+
+ if (i != desc->length)
+ return -EINVAL;
+
+ down_write(&card_rwsem);
+
+ list_add_tail (&desc->link, &descriptor_list);
+ descriptor_count++;
+ if (desc->immediate > 0)
+ descriptor_count++;
+ update_config_roms();
+
+ up_write(&card_rwsem);
+
+ return 0;
+}
+EXPORT_SYMBOL(fw_core_add_descriptor);
+
+void
+fw_core_remove_descriptor (struct fw_descriptor *desc)
+{
+ down_write(&card_rwsem);
+
+ list_del(&desc->link);
+ descriptor_count--;
+ if (desc->immediate > 0)
+ descriptor_count--;
+ update_config_roms();
+
+ up_write(&card_rwsem);
+}
+EXPORT_SYMBOL(fw_core_remove_descriptor);
+
+static const char gap_count_table[] = {
+ 63, 5, 7, 8, 10, 13, 16, 18, 21, 24, 26, 29, 32, 35, 37, 40
+};
+
+struct bm_data {
+ struct fw_transaction t;
+ struct {
+ __be32 arg;
+ __be32 data;
+ } lock;
+ u32 old;
+ int rcode;
+ struct completion done;
+};
+
+static void
+complete_bm_lock(struct fw_card *card, int rcode,
+ void *payload, size_t length, void *data)
+{
+ struct bm_data *bmd = data;
+
+ if (rcode == RCODE_COMPLETE)
+ bmd->old = be32_to_cpu(*(__be32 *) payload);
+ bmd->rcode = rcode;
+ complete(&bmd->done);
+}
+
+static void
+fw_card_bm_work(struct work_struct *work)
+{
+ struct fw_card *card = container_of(work, struct fw_card, work.work);
+ struct fw_device *root;
+ struct bm_data bmd;
+ unsigned long flags;
+ int root_id, new_root_id, irm_id, gap_count, generation, grace;
+ int do_reset = 0;
+
+ spin_lock_irqsave(&card->lock, flags);
+
+ generation = card->generation;
+ root = card->root_node->data;
+ root_id = card->root_node->node_id;
+ grace = time_after(jiffies, card->reset_jiffies + DIV_ROUND_UP(HZ, 10));
+
+ if (card->bm_generation + 1 == generation ||
+ (card->bm_generation != generation && grace)) {
+ /* This first step is to figure out who is IRM and
+ * then try to become bus manager. If the IRM is not
+ * well defined (e.g. does not have an active link
+ * layer or does not responds to our lock request, we
+ * will have to do a little vigilante bus management.
+ * In that case, we do a goto into the gap count logic
+ * so that when we do the reset, we still optimize the
+ * gap count. That could well save a reset in the
+ * next generation. */
+
+ irm_id = card->irm_node->node_id;
+ if (!card->irm_node->link_on) {
+ new_root_id = card->local_node->node_id;
+ fw_notify("IRM has link off, making local node (%02x) root.\n",
+ new_root_id);
+ goto pick_me;
+ }
+
+ bmd.lock.arg = cpu_to_be32(0x3f);
+ bmd.lock.data = cpu_to_be32(card->local_node->node_id);
+
+ spin_unlock_irqrestore(&card->lock, flags);
+
+ init_completion(&bmd.done);
+ fw_send_request(card, &bmd.t, TCODE_LOCK_COMPARE_SWAP,
+ irm_id, generation,
+ SCODE_100, CSR_REGISTER_BASE + CSR_BUS_MANAGER_ID,
+ &bmd.lock, sizeof bmd.lock,
+ complete_bm_lock, &bmd);
+ wait_for_completion(&bmd.done);
+
+ if (bmd.rcode == RCODE_GENERATION) {
+ /* Another bus reset happened. Just return,
+ * the BM work has been rescheduled. */
+ return;
+ }
+
+ if (bmd.rcode == RCODE_COMPLETE && bmd.old != 0x3f)
+ /* Somebody else is BM, let them do the work. */
+ return;
+
+ spin_lock_irqsave(&card->lock, flags);
+ if (bmd.rcode != RCODE_COMPLETE) {
+ /* The lock request failed, maybe the IRM
+ * isn't really IRM capable after all. Let's
+ * do a bus reset and pick the local node as
+ * root, and thus, IRM. */
+ new_root_id = card->local_node->node_id;
+ fw_notify("BM lock failed, making local node (%02x) root.\n",
+ new_root_id);
+ goto pick_me;
+ }
+ } else if (card->bm_generation != generation) {
+ /* OK, we weren't BM in the last generation, and it's
+ * less than 100ms since last bus reset. Reschedule
+ * this task 100ms from now. */
+ spin_unlock_irqrestore(&card->lock, flags);
+ schedule_delayed_work(&card->work, DIV_ROUND_UP(HZ, 10));
+ return;
+ }
+
+ /* We're bus manager for this generation, so next step is to
+ * make sure we have an active cycle master and do gap count
+ * optimization. */
+ card->bm_generation = generation;
+
+ if (root == NULL) {
+ /* Either link_on is false, or we failed to read the
+ * config rom. In either case, pick another root. */
+ new_root_id = card->local_node->node_id;
+ } else if (atomic_read(&root->state) != FW_DEVICE_RUNNING) {
+ /* If we haven't probed this device yet, bail out now
+ * and let's try again once that's done. */
+ spin_unlock_irqrestore(&card->lock, flags);
+ return;
+ } else if (root->config_rom[2] & bib_cmc) {
+ /* FIXME: I suppose we should set the cmstr bit in the
+ * STATE_CLEAR register of this node, as described in
+ * 1394-1995, 8.4.2.6. Also, send out a force root
+ * packet for this node. */
+ new_root_id = root_id;
+ } else {
+ /* Current root has an active link layer and we
+ * successfully read the config rom, but it's not
+ * cycle master capable. */
+ new_root_id = card->local_node->node_id;
+ }
+
+ pick_me:
+ /* Now figure out what gap count to set. */
+ if (card->topology_type == FW_TOPOLOGY_A &&
+ card->root_node->max_hops < ARRAY_SIZE(gap_count_table))
+ gap_count = gap_count_table[card->root_node->max_hops];
+ else
+ gap_count = 63;
+
+ /* Finally, figure out if we should do a reset or not. If we've
+ * done less that 5 resets with the same physical topology and we
+ * have either a new root or a new gap count setting, let's do it. */
+
+ if (card->bm_retries++ < 5 &&
+ (card->gap_count != gap_count || new_root_id != root_id))
+ do_reset = 1;
+
+ spin_unlock_irqrestore(&card->lock, flags);
+
+ if (do_reset) {
+ fw_notify("phy config: card %d, new root=%x, gap_count=%d\n",
+ card->index, new_root_id, gap_count);
+ fw_send_phy_config(card, new_root_id, generation, gap_count);
+ fw_core_initiate_bus_reset(card, 1);
+ }
+}
+
+static void
+flush_timer_callback(unsigned long data)
+{
+ struct fw_card *card = (struct fw_card *)data;
+
+ fw_flush_transactions(card);
+}
+
+void
+fw_card_initialize(struct fw_card *card, const struct fw_card_driver *driver,
+ struct device *device)
+{
+ static atomic_t index = ATOMIC_INIT(-1);
+
+ kref_init(&card->kref);
+ card->index = atomic_inc_return(&index);
+ card->driver = driver;
+ card->device = device;
+ card->current_tlabel = 0;
+ card->tlabel_mask = 0;
+ card->color = 0;
+
+ INIT_LIST_HEAD(&card->transaction_list);
+ spin_lock_init(&card->lock);
+ setup_timer(&card->flush_timer,
+ flush_timer_callback, (unsigned long)card);
+
+ card->local_node = NULL;
+
+ INIT_DELAYED_WORK(&card->work, fw_card_bm_work);
+}
+EXPORT_SYMBOL(fw_card_initialize);
+
+int
+fw_card_add(struct fw_card *card,
+ u32 max_receive, u32 link_speed, u64 guid)
+{
+ u32 *config_rom;
+ size_t length;
+
+ card->max_receive = max_receive;
+ card->link_speed = link_speed;
+ card->guid = guid;
+
+ /* Activate link_on bit and contender bit in our self ID packets.*/
+ if (card->driver->update_phy_reg(card, 4, 0,
+ PHY_LINK_ACTIVE | PHY_CONTENDER) < 0)
+ return -EIO;
+
+ /* The subsystem grabs a reference when the card is added and
+ * drops it when the driver calls fw_core_remove_card. */
+ fw_card_get(card);
+
+ down_write(&card_rwsem);
+ config_rom = generate_config_rom (card, &length);
+ list_add_tail(&card->link, &card_list);
+ up_write(&card_rwsem);
+
+ return card->driver->enable(card, config_rom, length);
+}
+EXPORT_SYMBOL(fw_card_add);
+
+
+/* The next few functions implements a dummy driver that use once a
+ * card driver shuts down an fw_card. This allows the driver to
+ * cleanly unload, as all IO to the card will be handled by the dummy
+ * driver instead of calling into the (possibly) unloaded module. The
+ * dummy driver just fails all IO. */
+
+static int
+dummy_enable(struct fw_card *card, u32 *config_rom, size_t length)
+{
+ BUG();
+ return -1;
+}
+
+static int
+dummy_update_phy_reg(struct fw_card *card, int address,
+ int clear_bits, int set_bits)
+{
+ return -ENODEV;
+}
+
+static int
+dummy_set_config_rom(struct fw_card *card,
+ u32 *config_rom, size_t length)
+{
+ /* We take the card out of card_list before setting the dummy
+ * driver, so this should never get called. */
+ BUG();
+ return -1;
+}
+
+static void
+dummy_send_request(struct fw_card *card, struct fw_packet *packet)
+{
+ packet->callback(packet, card, -ENODEV);
+}
+
+static void
+dummy_send_response(struct fw_card *card, struct fw_packet *packet)
+{
+ packet->callback(packet, card, -ENODEV);
+}
+
+static int
+dummy_cancel_packet(struct fw_card *card, struct fw_packet *packet)
+{
+ return -ENOENT;
+}
+
+static int
+dummy_enable_phys_dma(struct fw_card *card,
+ int node_id, int generation)
+{
+ return -ENODEV;
+}
+
+static struct fw_card_driver dummy_driver = {
+ .name = "dummy",
+ .enable = dummy_enable,
+ .update_phy_reg = dummy_update_phy_reg,
+ .set_config_rom = dummy_set_config_rom,
+ .send_request = dummy_send_request,
+ .cancel_packet = dummy_cancel_packet,
+ .send_response = dummy_send_response,
+ .enable_phys_dma = dummy_enable_phys_dma,
+};
+
+void
+fw_core_remove_card(struct fw_card *card)
+{
+ card->driver->update_phy_reg(card, 4,
+ PHY_LINK_ACTIVE | PHY_CONTENDER, 0);
+ fw_core_initiate_bus_reset(card, 1);
+
+ down_write(&card_rwsem);
+ list_del(&card->link);
+ up_write(&card_rwsem);
+
+ /* Set up the dummy driver. */
+ card->driver = &dummy_driver;
+
+ fw_flush_transactions(card);
+
+ fw_destroy_nodes(card);
+
+ fw_card_put(card);
+}
+EXPORT_SYMBOL(fw_core_remove_card);
+
+struct fw_card *
+fw_card_get(struct fw_card *card)
+{
+ kref_get(&card->kref);
+
+ return card;
+}
+EXPORT_SYMBOL(fw_card_get);
+
+static void
+release_card(struct kref *kref)
+{
+ struct fw_card *card = container_of(kref, struct fw_card, kref);
+
+ kfree(card);
+}
+
+/* An assumption for fw_card_put() is that the card driver allocates
+ * the fw_card struct with kalloc and that it has been shut down
+ * before the last ref is dropped. */
+void
+fw_card_put(struct fw_card *card)
+{
+ kref_put(&card->kref, release_card);
+}
+EXPORT_SYMBOL(fw_card_put);
+
+int
+fw_core_initiate_bus_reset(struct fw_card *card, int short_reset)
+{
+ int reg = short_reset ? 5 : 1;
+ /* The following values happen to be the same bit. However be
+ * explicit for clarity. */
+ int bit = short_reset ? PHY_BUS_SHORT_RESET : PHY_BUS_RESET;
+
+ return card->driver->update_phy_reg(card, reg, 0, bit);
+}
+EXPORT_SYMBOL(fw_core_initiate_bus_reset);
Index: linux_juju/drivers/firewire/fw-device.h
===================================================================
--- /dev/null
+++ linux_juju/drivers/firewire/fw-device.h
@@ -0,0 +1,149 @@
+/* -*- c-basic-offset: 8 -*-
+ *
+ * fw-device.h - Device probing and sysfs code.
+ *
+ * Copyright (C) 2005-2006 Kristian Hoegsberg <[email protected]>
+ *
+ * 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 __fw_device_h
+#define __fw_device_h
+
+#include <linux/fs.h>
+#include <linux/cdev.h>
+#include <asm/atomic.h>
+
+enum fw_device_state {
+ FW_DEVICE_INITIALIZING,
+ FW_DEVICE_RUNNING,
+ FW_DEVICE_SHUTDOWN,
+};
+
+struct fw_attribute_group {
+ struct attribute_group *groups[2];
+ struct attribute_group group;
+ struct attribute *attrs[11];
+};
+
+struct fw_device {
+ atomic_t state;
+ struct fw_node *node;
+ int node_id;
+ int generation;
+ struct fw_card *card;
+ struct device device;
+ struct list_head link;
+ struct list_head client_list;
+ u32 *config_rom;
+ size_t config_rom_length;
+ int config_rom_retries;
+ struct delayed_work work;
+ struct fw_attribute_group attribute_group;
+};
+
+static inline struct fw_device *
+fw_device(struct device *dev)
+{
+ return container_of(dev, struct fw_device, device);
+}
+
+static inline int
+fw_device_is_shutdown(struct fw_device *device)
+{
+ return atomic_read(&device->state) == FW_DEVICE_SHUTDOWN;
+}
+
+struct fw_device *fw_device_get(struct fw_device *device);
+void fw_device_put(struct fw_device *device);
+int fw_device_enable_phys_dma(struct fw_device *device);
+
+void fw_device_cdev_update(struct fw_device *device);
+void fw_device_cdev_remove(struct fw_device *device);
+
+struct fw_device *fw_device_from_devt(dev_t devt);
+extern int fw_cdev_major;
+
+struct fw_unit {
+ struct device device;
+ u32 *directory;
+ struct fw_attribute_group attribute_group;
+};
+
+static inline struct fw_unit *
+fw_unit(struct device *dev)
+{
+ return container_of(dev, struct fw_unit, device);
+}
+
+#define CSR_OFFSET 0x40
+#define CSR_LEAF 0x80
+#define CSR_DIRECTORY 0xc0
+
+#define CSR_DESCRIPTOR 0x01
+#define CSR_VENDOR 0x03
+#define CSR_HARDWARE_VERSION 0x04
+#define CSR_NODE_CAPABILITIES 0x0c
+#define CSR_UNIT 0x11
+#define CSR_SPECIFIER_ID 0x12
+#define CSR_VERSION 0x13
+#define CSR_DEPENDENT_INFO 0x14
+#define CSR_MODEL 0x17
+#define CSR_INSTANCE 0x18
+
+#define SBP2_COMMAND_SET_SPECIFIER 0x38
+#define SBP2_COMMAND_SET 0x39
+#define SBP2_COMMAND_SET_REVISION 0x3b
+#define SBP2_FIRMWARE_REVISION 0x3c
+
+struct fw_csr_iterator {
+ u32 *p;
+ u32 *end;
+};
+
+void fw_csr_iterator_init(struct fw_csr_iterator *ci, u32 *p);
+int fw_csr_iterator_next(struct fw_csr_iterator *ci,
+ int *key, int *value);
+
+#define FW_MATCH_VENDOR 0x0001
+#define FW_MATCH_MODEL 0x0002
+#define FW_MATCH_SPECIFIER_ID 0x0004
+#define FW_MATCH_VERSION 0x0008
+
+struct fw_device_id {
+ u32 match_flags;
+ u32 vendor;
+ u32 model;
+ u32 specifier_id;
+ u32 version;
+ void *driver_data;
+};
+
+struct fw_driver {
+ struct device_driver driver;
+ /* Called when the parent device sits through a bus reset. */
+ void (*update) (struct fw_unit *unit);
+ const struct fw_device_id *id_table;
+};
+
+static inline struct fw_driver *
+fw_driver(struct device_driver *drv)
+{
+ return container_of(drv, struct fw_driver, driver);
+}
+
+extern const struct file_operations fw_device_ops;
+
+#endif /* __fw_device_h */
Index: linux_juju/drivers/firewire/fw-device.c
===================================================================
--- /dev/null
+++ linux_juju/drivers/firewire/fw-device.c
@@ -0,0 +1,782 @@
+/* -*- c-basic-offset: 8 -*-
+ *
+ * fw-device.c - Device probing and sysfs code.
+ *
+ * Copyright (C) 2005-2006 Kristian Hoegsberg <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/wait.h>
+#include <linux/errno.h>
+#include <linux/kthread.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/idr.h>
+#include <linux/rwsem.h>
+#include <asm/semaphore.h>
+#include <linux/ctype.h>
+#include "fw-transaction.h"
+#include "fw-topology.h"
+#include "fw-device.h"
+
+void fw_csr_iterator_init(struct fw_csr_iterator *ci, u32 * p)
+{
+ ci->p = p + 1;
+ ci->end = ci->p + (p[0] >> 16);
+}
+EXPORT_SYMBOL(fw_csr_iterator_init);
+
+int fw_csr_iterator_next(struct fw_csr_iterator *ci, int *key, int *value)
+{
+ *key = *ci->p >> 24;
+ *value = *ci->p & 0xffffff;
+
+ return ci->p++ < ci->end;
+}
+EXPORT_SYMBOL(fw_csr_iterator_next);
+
+static int is_fw_unit(struct device *dev);
+
+static int match_unit_directory(u32 * directory, const struct fw_device_id *id)
+{
+ struct fw_csr_iterator ci;
+ int key, value, match;
+
+ match = 0;
+ fw_csr_iterator_init(&ci, directory);
+ while (fw_csr_iterator_next(&ci, &key, &value)) {
+ if (key == CSR_VENDOR && value == id->vendor)
+ match |= FW_MATCH_VENDOR;
+ if (key == CSR_MODEL && value == id->model)
+ match |= FW_MATCH_MODEL;
+ if (key == CSR_SPECIFIER_ID && value == id->specifier_id)
+ match |= FW_MATCH_SPECIFIER_ID;
+ if (key == CSR_VERSION && value == id->version)
+ match |= FW_MATCH_VERSION;
+ }
+
+ return (match & id->match_flags) == id->match_flags;
+}
+
+static int fw_unit_match(struct device *dev, struct device_driver *drv)
+{
+ struct fw_unit *unit = fw_unit(dev);
+ struct fw_driver *driver = fw_driver(drv);
+ int i;
+
+ /* We only allow binding to fw_units. */
+ if (!is_fw_unit(dev))
+ return 0;
+
+ for (i = 0; driver->id_table[i].match_flags != 0; i++) {
+ if (match_unit_directory(unit->directory, &driver->id_table[i]))
+ return 1;
+ }
+
+ return 0;
+}
+
+static int get_modalias(struct fw_unit *unit, char *buffer, size_t buffer_size)
+{
+ struct fw_device *device = fw_device(unit->device.parent);
+ struct fw_csr_iterator ci;
+
+ int key, value;
+ int vendor = 0;
+ int model = 0;
+ int specifier_id = 0;
+ int version = 0;
+
+ fw_csr_iterator_init(&ci, &device->config_rom[5]);
+ while (fw_csr_iterator_next(&ci, &key, &value)) {
+ switch (key) {
+ case CSR_VENDOR:
+ vendor = value;
+ break;
+ case CSR_MODEL:
+ model = value;
+ break;
+ }
+ }
+
+ fw_csr_iterator_init(&ci, unit->directory);
+ while (fw_csr_iterator_next(&ci, &key, &value)) {
+ switch (key) {
+ case CSR_SPECIFIER_ID:
+ specifier_id = value;
+ break;
+ case CSR_VERSION:
+ version = value;
+ break;
+ }
+ }
+
+ return snprintf(buffer, buffer_size,
+ "ieee1394:ven%08Xmo%08Xsp%08Xver%08X",
+ vendor, model, specifier_id, version);
+}
+
+static int
+fw_unit_uevent(struct device *dev, char **envp, int num_envp,
+ char *buffer, int buffer_size)
+{
+ struct fw_unit *unit = fw_unit(dev);
+ char modalias[64];
+ int length = 0;
+ int i = 0;
+
+ get_modalias(unit, modalias, sizeof modalias);
+
+ if (add_uevent_var(envp, num_envp, &i,
+ buffer, buffer_size, &length,
+ "MODALIAS=%s", modalias))
+ return -ENOMEM;
+
+ envp[i] = NULL;
+
+ return 0;
+}
+
+struct bus_type fw_bus_type = {
+ .name = "firewire",
+ .match = fw_unit_match,
+};
+EXPORT_SYMBOL(fw_bus_type);
+
+struct fw_device *fw_device_get(struct fw_device *device)
+{
+ get_device(&device->device);
+
+ return device;
+}
+
+void fw_device_put(struct fw_device *device)
+{
+ put_device(&device->device);
+}
+
+static void fw_device_release(struct device *dev)
+{
+ struct fw_device *device = fw_device(dev);
+ unsigned long flags;
+
+ /* Take the card lock so we don't set this to NULL while a
+ * FW_NODE_UPDATED callback is being handled. */
+ spin_lock_irqsave(&device->card->lock, flags);
+ device->node->data = NULL;
+ spin_unlock_irqrestore(&device->card->lock, flags);
+
+ fw_node_put(device->node);
+ fw_card_put(device->card);
+ kfree(device->config_rom);
+ kfree(device);
+}
+
+int fw_device_enable_phys_dma(struct fw_device *device)
+{
+ return device->card->driver->enable_phys_dma(device->card,
+ device->node_id,
+ device->generation);
+}
+EXPORT_SYMBOL(fw_device_enable_phys_dma);
+
+struct config_rom_attribute {
+ struct device_attribute attr;
+ u32 key;
+};
+
+static ssize_t
+show_immediate(struct device *dev, struct device_attribute *dattr, char *buf)
+{
+ struct config_rom_attribute *attr =
+ container_of(dattr, struct config_rom_attribute, attr);
+ struct fw_csr_iterator ci;
+ u32 *dir;
+ int key, value;
+
+ if (is_fw_unit(dev))
+ dir = fw_unit(dev)->directory;
+ else
+ dir = fw_device(dev)->config_rom + 5;
+
+ fw_csr_iterator_init(&ci, dir);
+ while (fw_csr_iterator_next(&ci, &key, &value))
+ if (attr->key == key)
+ return snprintf(buf, buf ? PAGE_SIZE : 0,
+ "0x%06x\n", value);
+
+ return -ENOENT;
+}
+
+#define IMMEDIATE_ATTR(name, key) \
+ { __ATTR(name, S_IRUGO, show_immediate, NULL), key }
+
+static ssize_t
+show_text_leaf(struct device *dev, struct device_attribute *dattr, char *buf)
+{
+ struct config_rom_attribute *attr =
+ container_of(dattr, struct config_rom_attribute, attr);
+ struct fw_csr_iterator ci;
+ u32 *dir, *block = NULL, *p, *end;
+ int length, key, value, last_key = 0;
+ char *b;
+
+ if (is_fw_unit(dev))
+ dir = fw_unit(dev)->directory;
+ else
+ dir = fw_device(dev)->config_rom + 5;
+
+ fw_csr_iterator_init(&ci, dir);
+ while (fw_csr_iterator_next(&ci, &key, &value)) {
+ if (attr->key == last_key &&
+ key == (CSR_DESCRIPTOR | CSR_LEAF))
+ block = ci.p - 1 + value;
+ last_key = key;
+ }
+
+ if (block == NULL)
+ return -ENOENT;
+
+ length = min(block[0] >> 16, 256U);
+ if (length < 3)
+ return -ENOENT;
+
+ if (block[1] != 0 || block[2] != 0)
+ /* Unknown encoding. */
+ return -ENOENT;
+
+ if (buf == NULL)
+ return length * 4;
+
+ b = buf;
+ end = &block[length + 1];
+ for (p = &block[3]; p < end; p++, b += 4)
+ * (u32 *) b = (__force u32) __cpu_to_be32(*p);
+
+ /* Strip trailing whitespace and add newline. */
+ while (b--, (isspace(*b) || *b == '\0') && b > buf);
+ strcpy(b + 1, "\n");
+
+ return b + 2 - buf;
+}
+
+#define TEXT_LEAF_ATTR(name, key) \
+ { __ATTR(name, S_IRUGO, show_text_leaf, NULL), key }
+
+static struct config_rom_attribute config_rom_attributes[] = {
+ IMMEDIATE_ATTR(vendor, CSR_VENDOR),
+ IMMEDIATE_ATTR(hardware_version, CSR_HARDWARE_VERSION),
+ IMMEDIATE_ATTR(specifier_id, CSR_SPECIFIER_ID),
+ IMMEDIATE_ATTR(version, CSR_VERSION),
+ IMMEDIATE_ATTR(model, CSR_MODEL),
+ TEXT_LEAF_ATTR(vendor_name, CSR_VENDOR),
+ TEXT_LEAF_ATTR(model_name, CSR_MODEL),
+ TEXT_LEAF_ATTR(hardware_version_name, CSR_HARDWARE_VERSION),
+};
+
+static void
+init_fw_attribute_group(struct device *dev,
+ struct device_attribute *attrs,
+ struct fw_attribute_group *group)
+{
+ struct device_attribute *attr;
+ int i, j;
+
+ for (j = 0; attrs[j].attr.name != NULL; j++)
+ group->attrs[j] = &attrs[j].attr;
+
+ for (i = 0; i < ARRAY_SIZE(config_rom_attributes); i++) {
+ attr = &config_rom_attributes[i].attr;
+ if (attr->show(dev, attr, NULL) < 0)
+ continue;
+ group->attrs[j++] = &attr->attr;
+ }
+
+ BUG_ON(j >= ARRAY_SIZE(group->attrs));
+ group->attrs[j++] = NULL;
+ group->groups[0] = &group->group;
+ group->groups[1] = NULL;
+ group->group.attrs = group->attrs;
+ dev->groups = group->groups;
+}
+
+static ssize_t
+modalias_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct fw_unit *unit = fw_unit(dev);
+ int length;
+
+ length = get_modalias(unit, buf, PAGE_SIZE);
+ strcpy(buf + length, "\n");
+
+ return length + 1;
+}
+
+static ssize_t
+rom_index_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct fw_device *device = fw_device(dev->parent);
+ struct fw_unit *unit = fw_unit(dev);
+
+ return snprintf(buf, PAGE_SIZE, "%d\n",
+ (int)(unit->directory - device->config_rom));
+}
+
+static struct device_attribute fw_unit_attributes[] = {
+ __ATTR_RO(modalias),
+ __ATTR_RO(rom_index),
+ __ATTR_NULL,
+};
+
+static ssize_t
+config_rom_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct fw_device *device = fw_device(dev);
+
+ memcpy(buf, device->config_rom, device->config_rom_length * 4);
+
+ return device->config_rom_length * 4;
+}
+
+static ssize_t
+guid_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct fw_device *device = fw_device(dev);
+ u64 guid;
+
+ guid = ((u64)device->config_rom[3] << 32) | device->config_rom[4];
+
+ return snprintf(buf, PAGE_SIZE, "0x%016llx\n",
+ (unsigned long long)guid);
+}
+
+static struct device_attribute fw_device_attributes[] = {
+ __ATTR_RO(config_rom),
+ __ATTR_RO(guid),
+ __ATTR_NULL,
+};
+
+struct read_quadlet_callback_data {
+ struct completion done;
+ int rcode;
+ u32 data;
+};
+
+static void
+complete_transaction(struct fw_card *card, int rcode,
+ void *payload, size_t length, void *data)
+{
+ struct read_quadlet_callback_data *callback_data = data;
+
+ if (rcode == RCODE_COMPLETE)
+ callback_data->data = be32_to_cpu(*(__be32 *)payload);
+ callback_data->rcode = rcode;
+ complete(&callback_data->done);
+}
+
+static int read_rom(struct fw_device *device, int index, u32 * data)
+{
+ struct read_quadlet_callback_data callback_data;
+ struct fw_transaction t;
+ u64 offset;
+
+ init_completion(&callback_data.done);
+
+ offset = 0xfffff0000400ULL + index * 4;
+ fw_send_request(device->card, &t, TCODE_READ_QUADLET_REQUEST,
+ device->node_id,
+ device->generation, SCODE_100,
+ offset, NULL, 4, complete_transaction, &callback_data);
+
+ wait_for_completion(&callback_data.done);
+
+ *data = callback_data.data;
+
+ return callback_data.rcode;
+}
+
+static int read_bus_info_block(struct fw_device *device)
+{
+ static u32 rom[256];
+ u32 stack[16], sp, key;
+ int i, end, length;
+
+ /* First read the bus info block. */
+ for (i = 0; i < 5; i++) {
+ if (read_rom(device, i, &rom[i]) != RCODE_COMPLETE)
+ return -1;
+ /* As per IEEE1212 7.2, during power-up, devices can
+ * reply with a 0 for the first quadlet of the config
+ * rom to indicate that they are booting (for example,
+ * if the firmware is on the disk of a external
+ * harddisk). In that case we just fail, and the
+ * retry mechanism will try again later. */
+ if (i == 0 && rom[i] == 0)
+ return -1;
+ }
+
+ /* Now parse the config rom. The config rom is a recursive
+ * directory structure so we parse it using a stack of
+ * references to the blocks that make up the structure. We
+ * push a reference to the root directory on the stack to
+ * start things off. */
+ length = i;
+ sp = 0;
+ stack[sp++] = 0xc0000005;
+ while (sp > 0) {
+ /* Pop the next block reference of the stack. The
+ * lower 24 bits is the offset into the config rom,
+ * the upper 8 bits are the type of the reference the
+ * block. */
+ key = stack[--sp];
+ i = key & 0xffffff;
+ if (i >= ARRAY_SIZE(rom))
+ /* The reference points outside the standard
+ * config rom area, something's fishy. */
+ return -1;
+
+ /* Read header quadlet for the block to get the length. */
+ if (read_rom(device, i, &rom[i]) != RCODE_COMPLETE)
+ return -1;
+ end = i + (rom[i] >> 16) + 1;
+ i++;
+ if (end > ARRAY_SIZE(rom))
+ /* This block extends outside standard config
+ * area (and the array we're reading it
+ * into). That's broken, so ignore this
+ * device. */
+ return -1;
+
+ /* Now read in the block. If this is a directory
+ * block, check the entries as we read them to see if
+ * it references another block, and push it in that case. */
+ while (i < end) {
+ if (read_rom(device, i, &rom[i]) != RCODE_COMPLETE)
+ return -1;
+ if ((key >> 30) == 3 && (rom[i] >> 30) > 1 &&
+ sp < ARRAY_SIZE(stack))
+ stack[sp++] = i + rom[i];
+ i++;
+ }
+ if (length < i)
+ length = i;
+ }
+
+ device->config_rom = kmalloc(length * 4, GFP_KERNEL);
+ if (device->config_rom == NULL)
+ return -1;
+ memcpy(device->config_rom, rom, length * 4);
+ device->config_rom_length = length;
+
+ return 0;
+}
+
+static void fw_unit_release(struct device *dev)
+{
+ struct fw_unit *unit = fw_unit(dev);
+
+ kfree(unit);
+}
+
+static struct device_type fw_unit_type = {
+ .uevent = fw_unit_uevent,
+ .release = fw_unit_release,
+};
+
+static int is_fw_unit(struct device *dev)
+{
+ return dev->type == &fw_unit_type;
+}
+
+static void create_units(struct fw_device *device)
+{
+ struct fw_csr_iterator ci;
+ struct fw_unit *unit;
+ int key, value, i;
+
+ i = 0;
+ fw_csr_iterator_init(&ci, &device->config_rom[5]);
+ while (fw_csr_iterator_next(&ci, &key, &value)) {
+ if (key != (CSR_UNIT | CSR_DIRECTORY))
+ continue;
+
+ /* Get the address of the unit directory and try to
+ * match the drivers id_tables against it. */
+ unit = kzalloc(sizeof *unit, GFP_KERNEL);
+ if (unit == NULL) {
+ fw_error("failed to allocate memory for unit\n");
+ continue;
+ }
+
+ unit->directory = ci.p + value - 1;
+ unit->device.bus = &fw_bus_type;
+ unit->device.type = &fw_unit_type;
+ unit->device.parent = &device->device;
+ snprintf(unit->device.bus_id, sizeof unit->device.bus_id,
+ "%s.%d", device->device.bus_id, i++);
+
+ init_fw_attribute_group(&unit->device,
+ fw_unit_attributes,
+ &unit->attribute_group);
+ if (device_register(&unit->device) < 0)
+ goto skip_unit;
+
+ continue;
+
+ skip_unit:
+ kfree(unit);
+ }
+}
+
+static int shutdown_unit(struct device *device, void *data)
+{
+ device_unregister(device);
+
+ return 0;
+}
+
+static DECLARE_RWSEM(idr_rwsem);
+static DEFINE_IDR(fw_device_idr);
+int fw_cdev_major;
+
+struct fw_device *fw_device_from_devt(dev_t devt)
+{
+ struct fw_device *device;
+
+ down_read(&idr_rwsem);
+ device = idr_find(&fw_device_idr, MINOR(devt));
+ up_read(&idr_rwsem);
+
+ return device;
+}
+
+static void fw_device_shutdown(struct work_struct *work)
+{
+ struct fw_device *device =
+ container_of(work, struct fw_device, work.work);
+ int minor = MINOR(device->device.devt);
+
+ down_write(&idr_rwsem);
+ idr_remove(&fw_device_idr, minor);
+ up_write(&idr_rwsem);
+
+ fw_device_cdev_remove(device);
+ device_for_each_child(&device->device, NULL, shutdown_unit);
+ device_unregister(&device->device);
+}
+
+static struct device_type fw_device_type = {
+ .release = fw_device_release,
+};
+
+/* These defines control the retry behavior for reading the config
+ * rom. It shouldn't be necessary to tweak these; if the device
+ * doesn't respond to a config rom read within 10 seconds, it's not
+ * going to respond at all. As for the initial delay, a lot of
+ * devices will be able to respond within half a second after bus
+ * reset. On the other hand, it's not really worth being more
+ * aggressive than that, since it scales pretty well; if 10 devices
+ * are plugged in, they're all getting read within one second. */
+
+#define MAX_RETRIES 10
+#define RETRY_DELAY (3 * HZ)
+#define INITIAL_DELAY (HZ / 2)
+
+static void fw_device_init(struct work_struct *work)
+{
+ struct fw_device *device =
+ container_of(work, struct fw_device, work.work);
+ int minor, err;
+
+ /* All failure paths here set node->data to NULL, so that we
+ * don't try to do device_for_each_child() on a kfree()'d
+ * device. */
+
+ if (read_bus_info_block(device) < 0) {
+ if (device->config_rom_retries < MAX_RETRIES) {
+ device->config_rom_retries++;
+ schedule_delayed_work(&device->work, RETRY_DELAY);
+ } else {
+ fw_notify("giving up on config rom for node id %x\n",
+ device->node_id);
+ if (device->node == device->card->root_node)
+ schedule_delayed_work(&device->card->work, 0);
+ fw_device_release(&device->device);
+ }
+ return;
+ }
+
+ err = -ENOMEM;
+ down_write(&idr_rwsem);
+ if (idr_pre_get(&fw_device_idr, GFP_KERNEL))
+ err = idr_get_new(&fw_device_idr, device, &minor);
+ up_write(&idr_rwsem);
+ if (err < 0)
+ goto error;
+
+ device->device.bus = &fw_bus_type;
+ device->device.type = &fw_device_type;
+ device->device.parent = device->card->device;
+ device->device.devt = MKDEV(fw_cdev_major, minor);
+ snprintf(device->device.bus_id, sizeof device->device.bus_id,
+ "fw%d", minor);
+
+ init_fw_attribute_group(&device->device,
+ fw_device_attributes,
+ &device->attribute_group);
+ if (device_add(&device->device)) {
+ fw_error("Failed to add device.\n");
+ goto error_with_cdev;
+ }
+
+ create_units(device);
+
+ /* Transition the device to running state. If it got pulled
+ * out from under us while we did the intialization work, we
+ * have to shut down the device again here. Normally, though,
+ * fw_node_event will be responsible for shutting it down when
+ * necessary. We have to use the atomic cmpxchg here to avoid
+ * racing with the FW_NODE_DESTROYED case in
+ * fw_node_event(). */
+ if (atomic_cmpxchg(&device->state,
+ FW_DEVICE_INITIALIZING,
+ FW_DEVICE_RUNNING) == FW_DEVICE_SHUTDOWN)
+ fw_device_shutdown(&device->work.work);
+ else
+ fw_notify("created new fw device %s (%d config rom retries)\n",
+ device->device.bus_id, device->config_rom_retries);
+
+ /* Reschedule the IRM work if we just finished reading the
+ * root node config rom. If this races with a bus reset we
+ * just end up running the IRM work a couple of extra times -
+ * pretty harmless. */
+ if (device->node == device->card->root_node)
+ schedule_delayed_work(&device->card->work, 0);
+
+ return;
+
+ error_with_cdev:
+ down_write(&idr_rwsem);
+ idr_remove(&fw_device_idr, minor);
+ up_write(&idr_rwsem);
+ error:
+ put_device(&device->device);
+}
+
+static int update_unit(struct device *dev, void *data)
+{
+ struct fw_unit *unit = fw_unit(dev);
+ struct fw_driver *driver = (struct fw_driver *)dev->driver;
+
+ if (is_fw_unit(dev) && driver != NULL && driver->update != NULL) {
+ down(&dev->sem);
+ driver->update(unit);
+ up(&dev->sem);
+ }
+
+ return 0;
+}
+
+static void fw_device_update(struct work_struct *work)
+{
+ struct fw_device *device =
+ container_of(work, struct fw_device, work.work);
+
+ fw_device_cdev_update(device);
+ device_for_each_child(&device->device, NULL, update_unit);
+}
+
+void fw_node_event(struct fw_card *card, struct fw_node *node, int event)
+{
+ struct fw_device *device;
+
+ switch (event) {
+ case FW_NODE_CREATED:
+ case FW_NODE_LINK_ON:
+ if (!node->link_on)
+ break;
+
+ device = kzalloc(sizeof(*device), GFP_ATOMIC);
+ if (device == NULL)
+ break;
+
+ /* Do minimal intialization of the device here, the
+ * rest will happen in fw_device_init(). We need the
+ * card and node so we can read the config rom and we
+ * need to do device_initialize() now so
+ * device_for_each_child() in FW_NODE_UPDATED is
+ * doesn't freak out. */
+ device_initialize(&device->device);
+ atomic_set(&device->state, FW_DEVICE_INITIALIZING);
+ device->card = fw_card_get(card);
+ device->node = fw_node_get(node);
+ device->node_id = node->node_id;
+ device->generation = card->generation;
+ INIT_LIST_HEAD(&device->client_list);
+
+ /* Set the node data to point back to this device so
+ * FW_NODE_UPDATED callbacks can update the node_id
+ * and generation for the device. */
+ node->data = device;
+
+ /* Many devices are slow to respond after bus resets,
+ * especially if they are bus powered and go through
+ * power-up after getting plugged in. We schedule the
+ * first config rom scan half a second after bus reset. */
+ INIT_DELAYED_WORK(&device->work, fw_device_init);
+ schedule_delayed_work(&device->work, INITIAL_DELAY);
+ break;
+
+ case FW_NODE_UPDATED:
+ if (!node->link_on || node->data == NULL)
+ break;
+
+ device = node->data;
+ device->node_id = node->node_id;
+ device->generation = card->generation;
+ if (atomic_read(&device->state) == FW_DEVICE_RUNNING) {
+ PREPARE_DELAYED_WORK(&device->work, fw_device_update);
+ schedule_delayed_work(&device->work, 0);
+ }
+ break;
+
+ case FW_NODE_DESTROYED:
+ case FW_NODE_LINK_OFF:
+ if (!node->data)
+ break;
+
+ /* Destroy the device associated with the node. There
+ * are two cases here: either the device is fully
+ * initialized (FW_DEVICE_RUNNING) or we're in the
+ * process of reading its config rom
+ * (FW_DEVICE_INITIALIZING). If it is fully
+ * initialized we can reuse device->work to schedule a
+ * full fw_device_shutdown(). If not, there's work
+ * scheduled to read it's config rom, and we just put
+ * the device in shutdown state to have that code fail
+ * to create the device. */
+ device = node->data;
+ if (atomic_xchg(&device->state,
+ FW_DEVICE_SHUTDOWN) == FW_DEVICE_RUNNING) {
+ PREPARE_DELAYED_WORK(&device->work, fw_device_shutdown);
+ schedule_delayed_work(&device->work, 0);
+ }
+ break;
+ }
+}
Index: linux_juju/drivers/firewire/fw-topology.h
===================================================================
--- /dev/null
+++ linux_juju/drivers/firewire/fw-topology.h
@@ -0,0 +1,94 @@
+/* -*- c-basic-offset: 8 -*-
+ *
+ * fw-topology.h -- Incremental bus scan, based on bus topology
+ *
+ * Copyright (C) 2003-2006 Kristian Hoegsberg <[email protected]>
+ *
+ * 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 __fw_topology_h
+#define __fw_topology_h
+
+enum {
+ FW_TOPOLOGY_A = 0x01,
+ FW_TOPOLOGY_B = 0x02,
+ FW_TOPOLOGY_MIXED = 0x03,
+};
+
+enum {
+ FW_NODE_CREATED = 0x00,
+ FW_NODE_UPDATED = 0x01,
+ FW_NODE_DESTROYED = 0x02,
+ FW_NODE_LINK_ON = 0x03,
+ FW_NODE_LINK_OFF = 0x04,
+};
+
+struct fw_port {
+ struct fw_node *node;
+ unsigned speed : 3; /* S100, S200, ... S3200 */
+};
+
+struct fw_node {
+ u16 node_id;
+ u8 color;
+ u8 port_count;
+ unsigned link_on : 1;
+ unsigned initiated_reset : 1;
+ unsigned b_path : 1;
+ u8 phy_speed : 3; /* As in the self ID packet. */
+ u8 max_speed : 5; /* Minimum of all phy-speeds and port speeds on
+ * the path from the local node to this node. */
+ u8 max_depth : 4; /* Maximum depth to any leaf node */
+ u8 max_hops : 4; /* Max hops in this sub tree */
+ atomic_t ref_count;
+
+ /* For serializing node topology into a list. */
+ struct list_head link;
+
+ /* Upper layer specific data. */
+ void *data;
+
+ struct fw_port ports[0];
+};
+
+static inline struct fw_node *
+fw_node(struct list_head *l)
+{
+ return list_entry (l, struct fw_node, link);
+}
+
+static inline struct fw_node *
+fw_node_get(struct fw_node *node)
+{
+ atomic_inc(&node->ref_count);
+
+ return node;
+}
+
+static inline void
+fw_node_put(struct fw_node *node)
+{
+ if (atomic_dec_and_test(&node->ref_count))
+ kfree(node);
+}
+
+void
+fw_destroy_nodes(struct fw_card *card);
+
+u16
+crc16_itu_t(const u32 *buffer, size_t length);
+
+#endif /* __fw_topology_h */
Index: linux_juju/drivers/firewire/fw-topology.c
===================================================================
--- /dev/null
+++ linux_juju/drivers/firewire/fw-topology.c
@@ -0,0 +1,519 @@
+/* -*- c-basic-offset: 8 -*-
+ *
+ * fw-topology.c - Incremental bus scan, based on bus topology
+ *
+ * Copyright (C) 2004-2006 Kristian Hoegsberg <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/wait.h>
+#include <linux/errno.h>
+#include "fw-transaction.h"
+#include "fw-topology.h"
+
+#define self_id_phy_id(q) (((q) >> 24) & 0x3f)
+#define self_id_extended(q) (((q) >> 23) & 0x01)
+#define self_id_link_on(q) (((q) >> 22) & 0x01)
+#define self_id_gap_count(q) (((q) >> 16) & 0x3f)
+#define self_id_phy_speed(q) (((q) >> 14) & 0x03)
+#define self_id_contender(q) (((q) >> 11) & 0x01)
+#define self_id_phy_initiator(q) (((q) >> 1) & 0x01)
+#define self_id_more_packets(q) (((q) >> 0) & 0x01)
+
+#define self_id_ext_sequence(q) (((q) >> 20) & 0x07)
+
+static u32 *count_ports(u32 *sid, int *total_port_count, int *child_port_count)
+{
+ u32 q;
+ int port_type, shift, seq;
+
+ *total_port_count = 0;
+ *child_port_count = 0;
+
+ shift = 6;
+ q = *sid;
+ seq = 0;
+
+ while (1) {
+ port_type = (q >> shift) & 0x03;
+ switch (port_type) {
+ case SELFID_PORT_CHILD:
+ (*child_port_count)++;
+ case SELFID_PORT_PARENT:
+ case SELFID_PORT_NCONN:
+ (*total_port_count)++;
+ case SELFID_PORT_NONE:
+ break;
+ }
+
+ shift -= 2;
+ if (shift == 0) {
+ if (!self_id_more_packets(q))
+ return sid + 1;
+
+ shift = 16;
+ sid++;
+ q = *sid;
+
+ /* Check that the extra packets actually are
+ * extended self ID packets and that the
+ * sequence numbers in the extended self ID
+ * packets increase as expected. */
+
+ if (!self_id_extended(q) ||
+ seq != self_id_ext_sequence(q))
+ return NULL;
+
+ seq++;
+ }
+ }
+}
+
+static int get_port_type(u32 *sid, int port_index)
+{
+ int index, shift;
+
+ index = (port_index + 5) / 8;
+ shift = 16 - ((port_index + 5) & 7) * 2;
+ return (sid[index] >> shift) & 0x03;
+}
+
+static struct fw_node *fw_node_create(u32 sid, int port_count, int color)
+{
+ struct fw_node *node;
+
+ node = kzalloc(sizeof *node + port_count * sizeof node->ports[0],
+ GFP_ATOMIC);
+ if (node == NULL)
+ return NULL;
+
+ node->color = color;
+ node->node_id = LOCAL_BUS | self_id_phy_id(sid);
+ node->link_on = self_id_link_on(sid);
+ node->phy_speed = self_id_phy_speed(sid);
+ node->port_count = port_count;
+
+ atomic_set(&node->ref_count, 1);
+ INIT_LIST_HEAD(&node->link);
+
+ return node;
+}
+
+/* Compute the maximum hop count for this node and it's children. The
+ * maximum hop count is the maximum number of connections between any
+ * two nodes in the subtree rooted at this node. We need this for
+ * setting the gap count. As we build the tree bottom up in
+ * build_tree() below, this is fairly easy to do: for each node we
+ * maintain the max hop count and the max depth, ie the number of hops
+ * to the furthest leaf. Computing the max hop count breaks down into
+ * two cases: either the path goes through this node, in which case
+ * the hop count is the sum of the two biggest child depths plus 2.
+ * Or it could be the case that the max hop path is entirely
+ * containted in a child tree, in which case the max hop count is just
+ * the max hop count of this child.
+ */
+static void update_hop_count(struct fw_node *node)
+{
+ int depths[2] = { -1, -1 };
+ int max_child_hops = 0;
+ int i;
+
+ for (i = 0; i < node->port_count; i++) {
+ if (node->ports[i].node == NULL)
+ continue;
+
+ if (node->ports[i].node->max_hops > max_child_hops)
+ max_child_hops = node->ports[i].node->max_hops;
+
+ if (node->ports[i].node->max_depth > depths[0]) {
+ depths[1] = depths[0];
+ depths[0] = node->ports[i].node->max_depth;
+ } else if (node->ports[i].node->max_depth > depths[1])
+ depths[1] = node->ports[i].node->max_depth;
+ }
+
+ node->max_depth = depths[0] + 1;
+ node->max_hops = max(max_child_hops, depths[0] + depths[1] + 2);
+}
+
+
+/**
+ * build_tree - Build the tree representation of the topology
+ * @self_ids: array of self IDs to create the tree from
+ * @self_id_count: the length of the self_ids array
+ * @local_id: the node ID of the local node
+ *
+ * This function builds the tree representation of the topology given
+ * by the self IDs from the latest bus reset. During the construction
+ * of the tree, the function checks that the self IDs are valid and
+ * internally consistent. On succcess this funtions returns the
+ * fw_node corresponding to the local card otherwise NULL.
+ */
+static struct fw_node *build_tree(struct fw_card *card,
+ u32 *sid, int self_id_count)
+{
+ struct fw_node *node, *child, *local_node, *irm_node;
+ struct list_head stack, *h;
+ u32 *next_sid, *end, q;
+ int i, port_count, child_port_count, phy_id, parent_count, stack_depth;
+ int gap_count, topology_type;
+
+ local_node = NULL;
+ node = NULL;
+ INIT_LIST_HEAD(&stack);
+ stack_depth = 0;
+ end = sid + self_id_count;
+ phy_id = 0;
+ irm_node = NULL;
+ gap_count = self_id_gap_count(*sid);
+ topology_type = 0;
+
+ while (sid < end) {
+ next_sid = count_ports(sid, &port_count, &child_port_count);
+
+ if (next_sid == NULL) {
+ fw_error("Inconsistent extended self IDs.\n");
+ return NULL;
+ }
+
+ q = *sid;
+ if (phy_id != self_id_phy_id(q)) {
+ fw_error("PHY ID mismatch in self ID: %d != %d.\n",
+ phy_id, self_id_phy_id(q));
+ return NULL;
+ }
+
+ if (child_port_count > stack_depth) {
+ fw_error("Topology stack underflow\n");
+ return NULL;
+ }
+
+ /* Seek back from the top of our stack to find the
+ * start of the child nodes for this node. */
+ for (i = 0, h = &stack; i < child_port_count; i++)
+ h = h->prev;
+ child = fw_node(h);
+
+ node = fw_node_create(q, port_count, card->color);
+ if (node == NULL) {
+ fw_error("Out of memory while building topology.");
+ return NULL;
+ }
+
+ if (phy_id == (card->node_id & 0x3f))
+ local_node = node;
+
+ if (self_id_contender(q))
+ irm_node = node;
+
+ if (node->phy_speed == SCODE_BETA)
+ topology_type |= FW_TOPOLOGY_B;
+ else
+ topology_type |= FW_TOPOLOGY_A;
+
+ parent_count = 0;
+
+ for (i = 0; i < port_count; i++) {
+ switch (get_port_type(sid, i)) {
+ case SELFID_PORT_PARENT:
+ /* Who's your daddy? We dont know the
+ * parent node at this time, so we
+ * temporarily abuse node->color for
+ * remembering the entry in the
+ * node->ports array where the parent
+ * node should be. Later, when we
+ * handle the parent node, we fix up
+ * the reference.
+ */
+ parent_count++;
+ node->color = i;
+ break;
+
+ case SELFID_PORT_CHILD:
+ node->ports[i].node = child;
+ /* Fix up parent reference for this
+ * child node. */
+ child->ports[child->color].node = node;
+ child->color = card->color;
+ child = fw_node(child->link.next);
+ break;
+ }
+ }
+
+ /* Check that the node reports exactly one parent
+ * port, except for the root, which of course should
+ * have no parents. */
+ if ((next_sid == end && parent_count != 0) ||
+ (next_sid < end && parent_count != 1)) {
+ fw_error("Parent port inconsistency for node %d: "
+ "parent_count=%d\n", phy_id, parent_count);
+ return NULL;
+ }
+
+ /* Pop the child nodes off the stack and push the new node. */
+ __list_del(h->prev, &stack);
+ list_add_tail(&node->link, &stack);
+ stack_depth += 1 - child_port_count;
+
+ /* If all PHYs does not report the same gap count
+ * setting, we fall back to 63 which will force a gap
+ * count reconfiguration and a reset. */
+ if (self_id_gap_count(q) != gap_count)
+ gap_count = 63;
+
+ update_hop_count(node);
+
+ sid = next_sid;
+ phy_id++;
+ }
+
+ card->root_node = node;
+ card->irm_node = irm_node;
+ card->gap_count = gap_count;
+ card->topology_type = topology_type;
+
+ return local_node;
+}
+
+typedef void (*fw_node_callback_t) (struct fw_card * card,
+ struct fw_node * node,
+ struct fw_node * parent);
+
+static void
+for_each_fw_node(struct fw_card *card, struct fw_node *root,
+ fw_node_callback_t callback)
+{
+ struct list_head list;
+ struct fw_node *node, *next, *child, *parent;
+ int i;
+
+ INIT_LIST_HEAD(&list);
+
+ fw_node_get(root);
+ list_add_tail(&root->link, &list);
+ parent = NULL;
+ list_for_each_entry(node, &list, link) {
+ node->color = card->color;
+
+ for (i = 0; i < node->port_count; i++) {
+ child = node->ports[i].node;
+ if (!child)
+ continue;
+ if (child->color == card->color)
+ parent = child;
+ else {
+ fw_node_get(child);
+ list_add_tail(&child->link, &list);
+ }
+ }
+
+ callback(card, node, parent);
+ }
+
+ list_for_each_entry_safe(node, next, &list, link)
+ fw_node_put(node);
+}
+
+static void
+report_lost_node(struct fw_card *card,
+ struct fw_node *node, struct fw_node *parent)
+{
+ fw_node_event(card, node, FW_NODE_DESTROYED);
+ fw_node_put(node);
+}
+
+static void
+report_found_node(struct fw_card *card,
+ struct fw_node *node, struct fw_node *parent)
+{
+ int b_path = (node->phy_speed == SCODE_BETA);
+
+ if (parent != NULL) {
+ /* min() macro doesn't work here with gcc 3.4 */
+ node->max_speed = parent->max_speed < node->phy_speed ?
+ parent->max_speed : node->phy_speed;
+ node->b_path = parent->b_path && b_path;
+ } else {
+ node->max_speed = node->phy_speed;
+ node->b_path = b_path;
+ }
+
+ fw_node_event(card, node, FW_NODE_CREATED);
+}
+
+void fw_destroy_nodes(struct fw_card *card)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&card->lock, flags);
+ card->color++;
+ if (card->local_node != NULL)
+ for_each_fw_node(card, card->local_node, report_lost_node);
+ spin_unlock_irqrestore(&card->lock, flags);
+}
+
+static void move_tree(struct fw_node *node0, struct fw_node *node1, int port)
+{
+ struct fw_node *tree;
+ int i;
+
+ tree = node1->ports[port].node;
+ node0->ports[port].node = tree;
+ for (i = 0; i < tree->port_count; i++) {
+ if (tree->ports[i].node == node1) {
+ tree->ports[i].node = node0;
+ break;
+ }
+ }
+}
+
+/**
+ * update_tree - compare the old topology tree for card with the new
+ * one specified by root. Queue the nodes and mark them as either
+ * found, lost or updated. Update the nodes in the card topology tree
+ * as we go.
+ */
+static void
+update_tree(struct fw_card *card, struct fw_node *root)
+{
+ struct list_head list0, list1;
+ struct fw_node *node0, *node1;
+ int i, event;
+
+ INIT_LIST_HEAD(&list0);
+ list_add_tail(&card->local_node->link, &list0);
+ INIT_LIST_HEAD(&list1);
+ list_add_tail(&root->link, &list1);
+
+ node0 = fw_node(list0.next);
+ node1 = fw_node(list1.next);
+
+ while (&node0->link != &list0) {
+
+ /* assert(node0->port_count == node1->port_count); */
+ if (node0->link_on && !node1->link_on)
+ event = FW_NODE_LINK_OFF;
+ else if (!node0->link_on && node1->link_on)
+ event = FW_NODE_LINK_ON;
+ else
+ event = FW_NODE_UPDATED;
+
+ node0->node_id = node1->node_id;
+ node0->color = card->color;
+ node0->link_on = node1->link_on;
+ node0->initiated_reset = node1->initiated_reset;
+ node0->max_hops = node1->max_hops;
+ node1->color = card->color;
+ fw_node_event(card, node0, event);
+
+ if (card->root_node == node1)
+ card->root_node = node0;
+ if (card->irm_node == node1)
+ card->irm_node = node0;
+
+ for (i = 0; i < node0->port_count; i++) {
+ if (node0->ports[i].node && node1->ports[i].node) {
+ /* This port didn't change, queue the
+ * connected node for further
+ * investigation. */
+ if (node0->ports[i].node->color == card->color)
+ continue;
+ list_add_tail(&node0->ports[i].node->link,
+ &list0);
+ list_add_tail(&node1->ports[i].node->link,
+ &list1);
+ } else if (node0->ports[i].node) {
+ /* The nodes connected here were
+ * unplugged; unref the lost nodes and
+ * queue FW_NODE_LOST callbacks for
+ * them. */
+
+ for_each_fw_node(card, node0->ports[i].node,
+ report_lost_node);
+ node0->ports[i].node = NULL;
+ } else if (node1->ports[i].node) {
+ /* One or more node were connected to
+ * this port. Move the new nodes into
+ * the tree and queue FW_NODE_CREATED
+ * callbacks for them. */
+ move_tree(node0, node1, i);
+ for_each_fw_node(card, node0->ports[i].node,
+ report_found_node);
+ }
+ }
+
+ node0 = fw_node(node0->link.next);
+ node1 = fw_node(node1->link.next);
+ }
+}
+
+static void
+update_topology_map(struct fw_card *card, u32 *self_ids, int self_id_count)
+{
+ int node_count;
+ u32 crc;
+
+ card->topology_map[1]++;
+ node_count = (card->root_node->node_id & 0x3f) + 1;
+ card->topology_map[2] = (node_count << 16) | self_id_count;
+ crc = crc16_itu_t(card->topology_map + 1, self_id_count + 2);
+ card->topology_map[0] = ((self_id_count + 2) << 16) | crc;
+ memcpy(&card->topology_map[3], self_ids, self_id_count * 4);
+}
+
+void
+fw_core_handle_bus_reset(struct fw_card *card,
+ int node_id, int generation,
+ int self_id_count, u32 * self_ids)
+{
+ struct fw_node *local_node;
+ unsigned long flags;
+
+ fw_flush_transactions(card);
+
+ spin_lock_irqsave(&card->lock, flags);
+
+ /* If the new topology has a different self_id_count the topology
+ * changed, either nodes were added or removed. In that case we
+ * reset the IRM reset counter. */
+ if (card->self_id_count != self_id_count)
+ card->bm_retries = 0;
+
+ card->node_id = node_id;
+ card->generation = generation;
+ card->reset_jiffies = jiffies;
+ schedule_delayed_work(&card->work, 0);
+
+ local_node = build_tree(card, self_ids, self_id_count);
+
+ update_topology_map(card, self_ids, self_id_count);
+
+ card->color++;
+
+ if (local_node == NULL) {
+ fw_error("topology build failed\n");
+ /* FIXME: We need to issue a bus reset in this case. */
+ } else if (card->local_node == NULL) {
+ card->local_node = local_node;
+ for_each_fw_node(card, local_node, report_found_node);
+ } else {
+ update_tree(card, local_node);
+ }
+
+ spin_unlock_irqrestore(&card->lock, flags);
+}
+EXPORT_SYMBOL(fw_core_handle_bus_reset);
--
Stefan Richter
-=====-=-=== -=-= ---=-
http://arcgraph.de/sr/
Signed-off-by: Stefan Richter <[email protected]>
---
drivers/firewire/fw-cdev.c | 954 ++++++++++++++++++++++++++++++++++
include/linux/firewire-cdev.h | 268 +++++++++
2 files changed, 1222 insertions(+)
Index: linux_juju/include/linux/firewire-cdev.h
===================================================================
--- /dev/null
+++ linux_juju/include/linux/firewire-cdev.h
@@ -0,0 +1,268 @@
+/* -*- c-basic-offset: 8 -*-
+ *
+ * fw-device-cdev.h -- Char device interface.
+ *
+ * Copyright (C) 2005-2006 Kristian Hoegsberg <[email protected]>
+ *
+ * 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 __fw_cdev_h
+#define __fw_cdev_h
+
+#include <asm/ioctl.h>
+#include <asm/types.h>
+
+#define TCODE_WRITE_QUADLET_REQUEST 0
+#define TCODE_WRITE_BLOCK_REQUEST 1
+#define TCODE_WRITE_RESPONSE 2
+#define TCODE_READ_QUADLET_REQUEST 4
+#define TCODE_READ_BLOCK_REQUEST 5
+#define TCODE_READ_QUADLET_RESPONSE 6
+#define TCODE_READ_BLOCK_RESPONSE 7
+#define TCODE_CYCLE_START 8
+#define TCODE_LOCK_REQUEST 9
+#define TCODE_STREAM_DATA 10
+#define TCODE_LOCK_RESPONSE 11
+
+#define TCODE_LOCK_MASK_SWAP 0x11
+#define TCODE_LOCK_COMPARE_SWAP 0x12
+#define TCODE_LOCK_FETCH_ADD 0x13
+#define TCODE_LOCK_LITTLE_ADD 0x14
+#define TCODE_LOCK_BOUNDED_ADD 0x15
+#define TCODE_LOCK_WRAP_ADD 0x16
+#define TCODE_LOCK_VENDOR_DEPENDENT 0x17
+
+#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 RCODE_SEND_ERROR 0x10
+#define RCODE_CANCELLED 0x11
+#define RCODE_BUSY 0x12
+#define RCODE_GENERATION 0x13
+#define RCODE_NO_ACK 0x14
+
+#define SCODE_100 0x0
+#define SCODE_200 0x1
+#define SCODE_400 0x2
+#define SCODE_800 0x3
+#define SCODE_1600 0x4
+#define SCODE_3200 0x5
+
+#define FW_CDEV_EVENT_BUS_RESET 0x00
+#define FW_CDEV_EVENT_RESPONSE 0x01
+#define FW_CDEV_EVENT_REQUEST 0x02
+#define FW_CDEV_EVENT_ISO_INTERRUPT 0x03
+
+/* The 'closure' fields are for user space to use. Data passed in the
+ * 'closure' field for a request will be returned in the corresponding
+ * event. It's a 64-bit type so that it's a fixed size type big
+ * enough to hold a pointer on all platforms. */
+
+struct fw_cdev_event_common {
+ __u64 closure;
+ __u32 type;
+};
+
+struct fw_cdev_event_bus_reset {
+ __u64 closure;
+ __u32 type;
+ __u32 node_id;
+ __u32 local_node_id;
+ __u32 bm_node_id;
+ __u32 irm_node_id;
+ __u32 root_node_id;
+ __u32 generation;
+};
+
+struct fw_cdev_event_response {
+ __u64 closure;
+ __u32 type;
+ __u32 rcode;
+ __u32 length;
+ __u32 data[0];
+};
+
+struct fw_cdev_event_request {
+ __u64 closure;
+ __u32 type;
+ __u32 tcode;
+ __u64 offset;
+ __u32 handle;
+ __u32 length;
+ __u32 data[0];
+};
+
+struct fw_cdev_event_iso_interrupt {
+ __u64 closure;
+ __u32 type;
+ __u32 cycle;
+ __u32 header_length; /* Length in bytes of following headers. */
+ __u32 header[0];
+};
+
+union fw_cdev_event {
+ struct fw_cdev_event_common common;
+ struct fw_cdev_event_bus_reset bus_reset;
+ struct fw_cdev_event_response response;
+ struct fw_cdev_event_request request;
+ struct fw_cdev_event_iso_interrupt iso_interrupt;
+};
+
+#define FW_CDEV_IOC_GET_INFO _IOWR('#', 0x00, struct fw_cdev_get_info)
+#define FW_CDEV_IOC_SEND_REQUEST _IOW('#', 0x01, struct fw_cdev_send_request)
+#define FW_CDEV_IOC_ALLOCATE _IOWR('#', 0x02, struct fw_cdev_allocate)
+#define FW_CDEV_IOC_DEALLOCATE _IOW('#', 0x03, struct fw_cdev_deallocate)
+#define FW_CDEV_IOC_SEND_RESPONSE _IOW('#', 0x04, struct fw_cdev_send_response)
+#define FW_CDEV_IOC_INITIATE_BUS_RESET _IOW('#', 0x05, struct fw_cdev_initiate_bus_reset)
+#define FW_CDEV_IOC_ADD_DESCRIPTOR _IOWR('#', 0x06, struct fw_cdev_add_descriptor)
+#define FW_CDEV_IOC_REMOVE_DESCRIPTOR _IOW('#', 0x07, struct fw_cdev_remove_descriptor)
+
+#define FW_CDEV_IOC_CREATE_ISO_CONTEXT _IOWR('#', 0x08, struct fw_cdev_create_iso_context)
+#define FW_CDEV_IOC_QUEUE_ISO _IOWR('#', 0x09, struct fw_cdev_queue_iso)
+#define FW_CDEV_IOC_START_ISO _IOW('#', 0x0a, struct fw_cdev_start_iso)
+#define FW_CDEV_IOC_STOP_ISO _IOW('#', 0x0b, struct fw_cdev_stop_iso)
+
+/* FW_CDEV_VERSION History
+ *
+ * 1 Feb 18, 2007: Initial version.
+ */
+#define FW_CDEV_VERSION 1
+
+struct fw_cdev_get_info {
+ /* The version field is just a running serial number. We
+ * never break backwards compatibility. Userspace passes in
+ * the version it expects and the kernel passes back the
+ * highest version it can provide. Even if the structs in
+ * this interface are extended in a later version, the kernel
+ * will not copy back more data than what was present in the
+ * interface version userspace expects. */
+ __u32 version;
+
+ /* If non-zero, at most rom_length bytes of config rom will be
+ * copied into that user space address. In either case,
+ * rom_length is updated with the actual length of the config
+ * rom. */
+ __u32 rom_length;
+ __u64 rom;
+
+ /* If non-zero, a fw_cdev_event_bus_reset struct will be
+ * copied here with the current state of the bus. This does
+ * not cause a bus reset to happen. The value of closure in
+ * this and sub-sequent bus reset events is set to
+ * bus_reset_closure. */
+ __u64 bus_reset;
+ __u64 bus_reset_closure;
+
+ /* The index of the card this devices belongs to. */
+ __u32 card;
+};
+
+struct fw_cdev_send_request {
+ __u32 tcode;
+ __u32 length;
+ __u64 offset;
+ __u64 closure;
+ __u64 data;
+ __u32 generation;
+};
+
+struct fw_cdev_send_response {
+ __u32 rcode;
+ __u32 length;
+ __u64 data;
+ __u32 handle;
+};
+
+struct fw_cdev_allocate {
+ __u64 offset;
+ __u64 closure;
+ __u32 length;
+ __u32 handle;
+};
+
+struct fw_cdev_deallocate {
+ __u32 handle;
+};
+
+#define FW_CDEV_LONG_RESET 0
+#define FW_CDEV_SHORT_RESET 1
+
+struct fw_cdev_initiate_bus_reset {
+ __u32 type;
+};
+
+struct fw_cdev_add_descriptor {
+ __u32 immediate;
+ __u32 key;
+ __u64 data;
+ __u32 length;
+ __u32 handle;
+};
+
+struct fw_cdev_remove_descriptor {
+ __u32 handle;
+};
+
+#define FW_CDEV_ISO_CONTEXT_TRANSMIT 0
+#define FW_CDEV_ISO_CONTEXT_RECEIVE 1
+
+#define FW_CDEV_ISO_CONTEXT_MATCH_TAG0 1
+#define FW_CDEV_ISO_CONTEXT_MATCH_TAG1 2
+#define FW_CDEV_ISO_CONTEXT_MATCH_TAG2 4
+#define FW_CDEV_ISO_CONTEXT_MATCH_TAG3 8
+#define FW_CDEV_ISO_CONTEXT_MATCH_ALL_TAGS 15
+
+struct fw_cdev_create_iso_context {
+ __u32 type;
+ __u32 header_size;
+ __u32 channel;
+ __u32 speed;
+ __u64 closure;
+ __u32 handle;
+};
+
+struct fw_cdev_iso_packet {
+ __u16 payload_length; /* Length of indirect payload. */
+ __u32 interrupt : 1; /* Generate interrupt on this packet */
+ __u32 skip : 1; /* Set to not send packet at all. */
+ __u32 tag : 2;
+ __u32 sy : 4;
+ __u32 header_length : 8; /* Length of immediate header. */
+ __u32 header[0];
+};
+
+struct fw_cdev_queue_iso {
+ __u64 packets;
+ __u64 data;
+ __u32 size;
+ __u32 handle;
+};
+
+struct fw_cdev_start_iso {
+ __s32 cycle;
+ __u32 sync;
+ __u32 tags;
+ __u32 handle;
+};
+
+struct fw_cdev_stop_iso {
+ __u32 handle;
+};
+
+#endif /* __fw_cdev_h */
Index: linux_juju/drivers/firewire/fw-cdev.c
===================================================================
--- /dev/null
+++ linux_juju/drivers/firewire/fw-cdev.c
@@ -0,0 +1,954 @@
+/* -*- c-basic-offset: 8 -*-
+ *
+ * fw-device-cdev.c - Char device for device raw access
+ *
+ * Copyright (C) 2005-2006 Kristian Hoegsberg <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/wait.h>
+#include <linux/errno.h>
+#include <linux/device.h>
+#include <linux/vmalloc.h>
+#include <linux/poll.h>
+#include <linux/delay.h>
+#include <linux/mm.h>
+#include <linux/idr.h>
+#include <linux/compat.h>
+#include <linux/firewire-cdev.h>
+#include <asm/uaccess.h>
+#include "fw-transaction.h"
+#include "fw-topology.h"
+#include "fw-device.h"
+
+/* dequeue_event() just kfree()'s the event, so the event has to be
+ * the first field in the struct. */
+
+struct client;
+struct client_resource {
+ struct list_head link;
+ void (*release)(struct client *client, struct client_resource *r);
+ u32 handle;
+};
+
+struct event {
+ struct { void *data; size_t size; } v[2];
+ struct list_head link;
+};
+
+struct bus_reset {
+ struct event event;
+ struct fw_cdev_event_bus_reset reset;
+};
+
+struct response {
+ struct event event;
+ struct fw_transaction transaction;
+ struct client *client;
+ struct client_resource resource;
+ struct fw_cdev_event_response response;
+};
+
+struct iso_interrupt {
+ struct event event;
+ struct fw_cdev_event_iso_interrupt interrupt;
+};
+
+struct client {
+ u32 version;
+ struct fw_device *device;
+ spinlock_t lock;
+ u32 resource_handle;
+ struct list_head resource_list;
+ struct list_head event_list;
+ wait_queue_head_t wait;
+ u64 bus_reset_closure;
+
+ struct fw_iso_context *iso_context;
+ u64 iso_closure;
+ struct fw_iso_buffer buffer;
+ unsigned long vm_start;
+
+ struct list_head link;
+};
+
+static inline void __user *
+u64_to_uptr(__u64 value)
+{
+ return (void __user *)(unsigned long)value;
+}
+
+static inline __u64
+uptr_to_u64(void __user *ptr)
+{
+ return (__u64)(unsigned long)ptr;
+}
+
+static int fw_device_op_open(struct inode *inode, struct file *file)
+{
+ struct fw_device *device;
+ struct client *client;
+ unsigned long flags;
+
+ device = fw_device_from_devt(inode->i_rdev);
+ if (device == NULL)
+ return -ENODEV;
+
+ client = kzalloc(sizeof *client, GFP_KERNEL);
+ if (client == NULL)
+ return -ENOMEM;
+
+ client->device = fw_device_get(device);
+ INIT_LIST_HEAD(&client->event_list);
+ INIT_LIST_HEAD(&client->resource_list);
+ spin_lock_init(&client->lock);
+ init_waitqueue_head(&client->wait);
+
+ file->private_data = client;
+
+ spin_lock_irqsave(&device->card->lock, flags);
+ list_add_tail(&client->link, &device->client_list);
+ spin_unlock_irqrestore(&device->card->lock, flags);
+
+ return 0;
+}
+
+static void queue_event(struct client *client, struct event *event,
+ void *data0, size_t size0, void *data1, size_t size1)
+{
+ unsigned long flags;
+
+ event->v[0].data = data0;
+ event->v[0].size = size0;
+ event->v[1].data = data1;
+ event->v[1].size = size1;
+
+ spin_lock_irqsave(&client->lock, flags);
+
+ list_add_tail(&event->link, &client->event_list);
+ wake_up_interruptible(&client->wait);
+
+ spin_unlock_irqrestore(&client->lock, flags);
+}
+
+static int
+dequeue_event(struct client *client, char __user *buffer, size_t count)
+{
+ unsigned long flags;
+ struct event *event;
+ size_t size, total;
+ int i, retval;
+
+ retval = wait_event_interruptible(client->wait,
+ !list_empty(&client->event_list) ||
+ fw_device_is_shutdown(client->device));
+ if (retval < 0)
+ return retval;
+
+ if (list_empty(&client->event_list) &&
+ fw_device_is_shutdown(client->device))
+ return -ENODEV;
+
+ spin_lock_irqsave(&client->lock, flags);
+ event = container_of(client->event_list.next, struct event, link);
+ list_del(&event->link);
+ spin_unlock_irqrestore(&client->lock, flags);
+
+ total = 0;
+ for (i = 0; i < ARRAY_SIZE(event->v) && total < count; i++) {
+ size = min(event->v[i].size, count - total);
+ if (copy_to_user(buffer + total, event->v[i].data, size)) {
+ retval = -EFAULT;
+ goto out;
+ }
+ total += size;
+ }
+ retval = total;
+
+ out:
+ kfree(event);
+
+ return retval;
+}
+
+static ssize_t
+fw_device_op_read(struct file *file,
+ char __user *buffer, size_t count, loff_t *offset)
+{
+ struct client *client = file->private_data;
+
+ return dequeue_event(client, buffer, count);
+}
+
+static void
+fill_bus_reset_event(struct fw_cdev_event_bus_reset *event,
+ struct client *client)
+{
+ struct fw_card *card = client->device->card;
+
+ event->closure = client->bus_reset_closure;
+ event->type = FW_CDEV_EVENT_BUS_RESET;
+ event->node_id = client->device->node_id;
+ event->local_node_id = card->local_node->node_id;
+ event->bm_node_id = 0; /* FIXME: We don't track the BM. */
+ event->irm_node_id = card->irm_node->node_id;
+ event->root_node_id = card->root_node->node_id;
+ event->generation = card->generation;
+}
+
+static void
+for_each_client(struct fw_device *device,
+ void (*callback)(struct client *client))
+{
+ struct fw_card *card = device->card;
+ struct client *c;
+ unsigned long flags;
+
+ spin_lock_irqsave(&card->lock, flags);
+
+ list_for_each_entry(c, &device->client_list, link)
+ callback(c);
+
+ spin_unlock_irqrestore(&card->lock, flags);
+}
+
+static void
+queue_bus_reset_event(struct client *client)
+{
+ struct bus_reset *bus_reset;
+
+ bus_reset = kzalloc(sizeof *bus_reset, GFP_ATOMIC);
+ if (bus_reset == NULL) {
+ fw_notify("Out of memory when allocating bus reset event\n");
+ return;
+ }
+
+ fill_bus_reset_event(&bus_reset->reset, client);
+
+ queue_event(client, &bus_reset->event,
+ &bus_reset->reset, sizeof bus_reset->reset, NULL, 0);
+}
+
+void fw_device_cdev_update(struct fw_device *device)
+{
+ for_each_client(device, queue_bus_reset_event);
+}
+
+static void wake_up_client(struct client *client)
+{
+ wake_up_interruptible(&client->wait);
+}
+
+void fw_device_cdev_remove(struct fw_device *device)
+{
+ for_each_client(device, wake_up_client);
+}
+
+static int ioctl_get_info(struct client *client, void *buffer)
+{
+ struct fw_cdev_get_info *get_info = buffer;
+ struct fw_cdev_event_bus_reset bus_reset;
+
+ client->version = get_info->version;
+ get_info->version = FW_CDEV_VERSION;
+
+ if (get_info->rom != 0) {
+ void __user *uptr = u64_to_uptr(get_info->rom);
+ size_t want = get_info->rom_length;
+ size_t have = client->device->config_rom_length * 4;
+
+ if (copy_to_user(uptr, client->device->config_rom,
+ min(want, have)))
+ return -EFAULT;
+ }
+ get_info->rom_length = client->device->config_rom_length * 4;
+
+ client->bus_reset_closure = get_info->bus_reset_closure;
+ if (get_info->bus_reset != 0) {
+ void __user *uptr = u64_to_uptr(get_info->bus_reset);
+
+ fill_bus_reset_event(&bus_reset, client);
+ if (copy_to_user(uptr, &bus_reset, sizeof bus_reset))
+ return -EFAULT;
+ }
+
+ get_info->card = client->device->card->index;
+
+ return 0;
+}
+
+static void
+add_client_resource(struct client *client, struct client_resource *resource)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&client->lock, flags);
+ list_add_tail(&resource->link, &client->resource_list);
+ resource->handle = client->resource_handle++;
+ spin_unlock_irqrestore(&client->lock, flags);
+}
+
+static int
+release_client_resource(struct client *client, u32 handle,
+ struct client_resource **resource)
+{
+ struct client_resource *r;
+ unsigned long flags;
+
+ spin_lock_irqsave(&client->lock, flags);
+ list_for_each_entry(r, &client->resource_list, link) {
+ if (r->handle == handle) {
+ list_del(&r->link);
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&client->lock, flags);
+
+ if (&r->link == &client->resource_list)
+ return -EINVAL;
+
+ if (resource)
+ *resource = r;
+ else
+ r->release(client, r);
+
+ return 0;
+}
+
+static void
+release_transaction(struct client *client, struct client_resource *resource)
+{
+ struct response *response =
+ container_of(resource, struct response, resource);
+
+ fw_cancel_transaction(client->device->card, &response->transaction);
+}
+
+static void
+complete_transaction(struct fw_card *card, int rcode,
+ void *payload, size_t length, void *data)
+{
+ struct response *response = data;
+ struct client *client = response->client;
+ unsigned long flags;
+
+ if (length < response->response.length)
+ response->response.length = length;
+ if (rcode == RCODE_COMPLETE)
+ memcpy(response->response.data, payload,
+ response->response.length);
+
+ spin_lock_irqsave(&client->lock, flags);
+ list_del(&response->resource.link);
+ spin_unlock_irqrestore(&client->lock, flags);
+
+ response->response.type = FW_CDEV_EVENT_RESPONSE;
+ response->response.rcode = rcode;
+ queue_event(client, &response->event,
+ &response->response, sizeof response->response,
+ response->response.data, response->response.length);
+}
+
+static ssize_t ioctl_send_request(struct client *client, void *buffer)
+{
+ struct fw_device *device = client->device;
+ struct fw_cdev_send_request *request = buffer;
+ struct response *response;
+
+ /* What is the biggest size we'll accept, really? */
+ if (request->length > 4096)
+ return -EINVAL;
+
+ response = kmalloc(sizeof *response + request->length, GFP_KERNEL);
+ if (response == NULL)
+ return -ENOMEM;
+
+ response->client = client;
+ response->response.length = request->length;
+ response->response.closure = request->closure;
+
+ if (request->data &&
+ copy_from_user(response->response.data,
+ u64_to_uptr(request->data), request->length)) {
+ kfree(response);
+ return -EFAULT;
+ }
+
+ response->resource.release = release_transaction;
+ add_client_resource(client, &response->resource);
+
+ fw_send_request(device->card, &response->transaction,
+ request->tcode & 0x1f,
+ device->node->node_id,
+ request->generation,
+ device->node->max_speed,
+ request->offset,
+ response->response.data, request->length,
+ complete_transaction, response);
+
+ if (request->data)
+ return sizeof request + request->length;
+ else
+ return sizeof request;
+}
+
+struct address_handler {
+ struct fw_address_handler handler;
+ __u64 closure;
+ struct client *client;
+ struct client_resource resource;
+};
+
+struct request {
+ struct fw_request *request;
+ void *data;
+ size_t length;
+ struct client_resource resource;
+};
+
+struct request_event {
+ struct event event;
+ struct fw_cdev_event_request request;
+};
+
+static void
+release_request(struct client *client, struct client_resource *resource)
+{
+ struct request *request =
+ container_of(resource, struct request, resource);
+
+ fw_send_response(client->device->card, request->request,
+ RCODE_CONFLICT_ERROR);
+ kfree(request);
+}
+
+static void
+handle_request(struct fw_card *card, struct fw_request *r,
+ int tcode, int destination, int source,
+ int generation, int speed,
+ unsigned long long offset,
+ void *payload, size_t length, void *callback_data)
+{
+ struct address_handler *handler = callback_data;
+ struct request *request;
+ struct request_event *e;
+ struct client *client = handler->client;
+
+ request = kmalloc(sizeof *request, GFP_ATOMIC);
+ e = kmalloc(sizeof *e, GFP_ATOMIC);
+ if (request == NULL || e == NULL) {
+ kfree(request);
+ kfree(e);
+ fw_send_response(card, r, RCODE_CONFLICT_ERROR);
+ return;
+ }
+
+ request->request = r;
+ request->data = payload;
+ request->length = length;
+
+ request->resource.release = release_request;
+ add_client_resource(client, &request->resource);
+
+ e->request.type = FW_CDEV_EVENT_REQUEST;
+ e->request.tcode = tcode;
+ e->request.offset = offset;
+ e->request.length = length;
+ e->request.handle = request->resource.handle;
+ e->request.closure = handler->closure;
+
+ queue_event(client, &e->event,
+ &e->request, sizeof e->request, payload, length);
+}
+
+static void
+release_address_handler(struct client *client,
+ struct client_resource *resource)
+{
+ struct address_handler *handler =
+ container_of(resource, struct address_handler, resource);
+
+ fw_core_remove_address_handler(&handler->handler);
+ kfree(handler);
+}
+
+static int ioctl_allocate(struct client *client, void *buffer)
+{
+ struct fw_cdev_allocate *request = buffer;
+ struct address_handler *handler;
+ struct fw_address_region region;
+
+ handler = kmalloc(sizeof *handler, GFP_KERNEL);
+ if (handler == NULL)
+ return -ENOMEM;
+
+ region.start = request->offset;
+ region.end = request->offset + request->length;
+ handler->handler.length = request->length;
+ handler->handler.address_callback = handle_request;
+ handler->handler.callback_data = handler;
+ handler->closure = request->closure;
+ handler->client = client;
+
+ if (fw_core_add_address_handler(&handler->handler, ®ion) < 0) {
+ kfree(handler);
+ return -EBUSY;
+ }
+
+ handler->resource.release = release_address_handler;
+ add_client_resource(client, &handler->resource);
+ request->handle = handler->resource.handle;
+
+ return 0;
+}
+
+static int ioctl_deallocate(struct client *client, void *buffer)
+{
+ struct fw_cdev_deallocate *request = buffer;
+
+ return release_client_resource(client, request->handle, NULL);
+}
+
+static int ioctl_send_response(struct client *client, void *buffer)
+{
+ struct fw_cdev_send_response *request = buffer;
+ struct client_resource *resource;
+ struct request *r;
+
+ if (release_client_resource(client, request->handle, &resource) < 0)
+ return -EINVAL;
+ r = container_of(resource, struct request, resource);
+ if (request->length < r->length)
+ r->length = request->length;
+ if (copy_from_user(r->data, u64_to_uptr(request->data), r->length))
+ return -EFAULT;
+
+ fw_send_response(client->device->card, r->request, request->rcode);
+ kfree(r);
+
+ return 0;
+}
+
+static int ioctl_initiate_bus_reset(struct client *client, void *buffer)
+{
+ struct fw_cdev_initiate_bus_reset *request = buffer;
+ int short_reset;
+
+ short_reset = (request->type == FW_CDEV_SHORT_RESET);
+
+ return fw_core_initiate_bus_reset(client->device->card, short_reset);
+}
+
+struct descriptor {
+ struct fw_descriptor d;
+ struct client_resource resource;
+ u32 data[0];
+};
+
+static void release_descriptor(struct client *client,
+ struct client_resource *resource)
+{
+ struct descriptor *descriptor =
+ container_of(resource, struct descriptor, resource);
+
+ fw_core_remove_descriptor(&descriptor->d);
+ kfree(descriptor);
+}
+
+static int ioctl_add_descriptor(struct client *client, void *buffer)
+{
+ struct fw_cdev_add_descriptor *request = buffer;
+ struct descriptor *descriptor;
+ int retval;
+
+ if (request->length > 256)
+ return -EINVAL;
+
+ descriptor =
+ kmalloc(sizeof *descriptor + request->length * 4, GFP_KERNEL);
+ if (descriptor == NULL)
+ return -ENOMEM;
+
+ if (copy_from_user(descriptor->data,
+ u64_to_uptr(request->data), request->length * 4)) {
+ kfree(descriptor);
+ return -EFAULT;
+ }
+
+ descriptor->d.length = request->length;
+ descriptor->d.immediate = request->immediate;
+ descriptor->d.key = request->key;
+ descriptor->d.data = descriptor->data;
+
+ retval = fw_core_add_descriptor(&descriptor->d);
+ if (retval < 0) {
+ kfree(descriptor);
+ return retval;
+ }
+
+ descriptor->resource.release = release_descriptor;
+ add_client_resource(client, &descriptor->resource);
+ request->handle = descriptor->resource.handle;
+
+ return 0;
+}
+
+static int ioctl_remove_descriptor(struct client *client, void *buffer)
+{
+ struct fw_cdev_remove_descriptor *request = buffer;
+
+ return release_client_resource(client, request->handle, NULL);
+}
+
+static void
+iso_callback(struct fw_iso_context *context, u32 cycle,
+ size_t header_length, void *header, void *data)
+{
+ struct client *client = data;
+ struct iso_interrupt *interrupt;
+
+ interrupt = kzalloc(sizeof *interrupt + header_length, GFP_ATOMIC);
+ if (interrupt == NULL)
+ return;
+
+ interrupt->interrupt.type = FW_CDEV_EVENT_ISO_INTERRUPT;
+ interrupt->interrupt.closure = client->iso_closure;
+ interrupt->interrupt.cycle = cycle;
+ interrupt->interrupt.header_length = header_length;
+ memcpy(interrupt->interrupt.header, header, header_length);
+ queue_event(client, &interrupt->event,
+ &interrupt->interrupt,
+ sizeof interrupt->interrupt + header_length, NULL, 0);
+}
+
+static int ioctl_create_iso_context(struct client *client, void *buffer)
+{
+ struct fw_cdev_create_iso_context *request = buffer;
+
+ if (request->channel > 63)
+ return -EINVAL;
+
+ switch (request->type) {
+ case FW_ISO_CONTEXT_RECEIVE:
+ if (request->header_size < 4 || (request->header_size & 3))
+ return -EINVAL;
+
+ break;
+
+ case FW_ISO_CONTEXT_TRANSMIT:
+ if (request->speed > SCODE_3200)
+ return -EINVAL;
+
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ client->iso_closure = request->closure;
+ client->iso_context = fw_iso_context_create(client->device->card,
+ request->type,
+ request->channel,
+ request->speed,
+ request->header_size,
+ iso_callback, client);
+ if (IS_ERR(client->iso_context))
+ return PTR_ERR(client->iso_context);
+
+ /* We only support one context at this time. */
+ request->handle = 0;
+
+ return 0;
+}
+
+static int ioctl_queue_iso(struct client *client, void *buffer)
+{
+ struct fw_cdev_queue_iso *request = buffer;
+ struct fw_cdev_iso_packet __user *p, *end, *next;
+ struct fw_iso_context *ctx = client->iso_context;
+ unsigned long payload, buffer_end, header_length;
+ int count;
+ struct {
+ struct fw_iso_packet packet;
+ u8 header[256];
+ } u;
+
+ if (ctx == NULL || request->handle != 0)
+ return -EINVAL;
+
+ /* If the user passes a non-NULL data pointer, has mmap()'ed
+ * the iso buffer, and the pointer points inside the buffer,
+ * we setup the payload pointers accordingly. Otherwise we
+ * set them both to 0, which will still let packets with
+ * payload_length == 0 through. In other words, if no packets
+ * use the indirect payload, the iso buffer need not be mapped
+ * and the request->data pointer is ignored.*/
+
+ payload = (unsigned long)request->data - client->vm_start;
+ buffer_end = client->buffer.page_count << PAGE_SHIFT;
+ if (request->data == 0 || client->buffer.pages == NULL ||
+ payload >= buffer_end) {
+ payload = 0;
+ buffer_end = 0;
+ }
+
+ if (!access_ok(VERIFY_READ, request->packets, request->size))
+ return -EFAULT;
+
+ p = (struct fw_cdev_iso_packet __user *)u64_to_uptr(request->packets);
+ end = (void __user *)p + request->size;
+ count = 0;
+ while (p < end) {
+ if (__copy_from_user(&u.packet, p, sizeof *p))
+ return -EFAULT;
+
+ if (ctx->type == FW_ISO_CONTEXT_TRANSMIT) {
+ header_length = u.packet.header_length;
+ } else {
+ /* We require that header_length is a multiple of
+ * the fixed header size, ctx->header_size */
+ if (ctx->header_size == 0) {
+ if (u.packet.header_length > 0)
+ return -EINVAL;
+ } else if (u.packet.header_length % ctx->header_size != 0) {
+ return -EINVAL;
+ }
+ header_length = 0;
+ }
+
+ next = (struct fw_cdev_iso_packet __user *)
+ &p->header[header_length / 4];
+ if (next > end)
+ return -EINVAL;
+ if (__copy_from_user
+ (u.packet.header, p->header, header_length))
+ return -EFAULT;
+ if (u.packet.skip && ctx->type == FW_ISO_CONTEXT_TRANSMIT &&
+ u.packet.header_length + u.packet.payload_length > 0)
+ return -EINVAL;
+ if (payload + u.packet.payload_length > buffer_end)
+ return -EINVAL;
+
+ if (fw_iso_context_queue(ctx, &u.packet,
+ &client->buffer, payload))
+ break;
+
+ p = next;
+ payload += u.packet.payload_length;
+ count++;
+ }
+
+ request->size -= uptr_to_u64(p) - request->packets;
+ request->packets = uptr_to_u64(p);
+ request->data = client->vm_start + payload;
+
+ return count;
+}
+
+static int ioctl_start_iso(struct client *client, void *buffer)
+{
+ struct fw_cdev_start_iso *request = buffer;
+
+ if (request->handle != 0)
+ return -EINVAL;
+ if (client->iso_context->type == FW_ISO_CONTEXT_RECEIVE) {
+ if (request->tags == 0 || request->tags > 15)
+ return -EINVAL;
+
+ if (request->sync > 15)
+ return -EINVAL;
+ }
+
+ return fw_iso_context_start(client->iso_context, request->cycle,
+ request->sync, request->tags);
+}
+
+static int ioctl_stop_iso(struct client *client, void *buffer)
+{
+ struct fw_cdev_stop_iso *request = buffer;
+
+ if (request->handle != 0)
+ return -EINVAL;
+
+ return fw_iso_context_stop(client->iso_context);
+}
+
+static int (* const ioctl_handlers[])(struct client *client, void *buffer) = {
+ ioctl_get_info,
+ ioctl_send_request,
+ ioctl_allocate,
+ ioctl_deallocate,
+ ioctl_send_response,
+ ioctl_initiate_bus_reset,
+ ioctl_add_descriptor,
+ ioctl_remove_descriptor,
+ ioctl_create_iso_context,
+ ioctl_queue_iso,
+ ioctl_start_iso,
+ ioctl_stop_iso,
+};
+
+static int
+dispatch_ioctl(struct client *client, unsigned int cmd, void __user *arg)
+{
+ char buffer[256];
+ int retval;
+
+ if (_IOC_TYPE(cmd) != '#' ||
+ _IOC_NR(cmd) >= ARRAY_SIZE(ioctl_handlers))
+ return -EINVAL;
+
+ if (_IOC_DIR(cmd) & _IOC_WRITE) {
+ if (_IOC_SIZE(cmd) > sizeof buffer ||
+ copy_from_user(buffer, arg, _IOC_SIZE(cmd)))
+ return -EFAULT;
+ }
+
+ retval = ioctl_handlers[_IOC_NR(cmd)](client, buffer);
+ if (retval < 0)
+ return retval;
+
+ if (_IOC_DIR(cmd) & _IOC_READ) {
+ if (_IOC_SIZE(cmd) > sizeof buffer ||
+ copy_to_user(arg, buffer, _IOC_SIZE(cmd)))
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+static long
+fw_device_op_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct client *client = file->private_data;
+
+ return dispatch_ioctl(client, cmd, (void __user *) arg);
+}
+
+#ifdef CONFIG_COMPAT
+static long
+fw_device_op_compat_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct client *client = file->private_data;
+
+ return dispatch_ioctl(client, cmd, compat_ptr(arg));
+}
+#endif
+
+static int fw_device_op_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct client *client = file->private_data;
+ enum dma_data_direction direction;
+ unsigned long size;
+ int page_count, retval;
+
+ /* FIXME: We could support multiple buffers, but we don't. */
+ if (client->buffer.pages != NULL)
+ return -EBUSY;
+
+ if (!(vma->vm_flags & VM_SHARED))
+ return -EINVAL;
+
+ if (vma->vm_start & ~PAGE_MASK)
+ return -EINVAL;
+
+ client->vm_start = vma->vm_start;
+ size = vma->vm_end - vma->vm_start;
+ page_count = size >> PAGE_SHIFT;
+ if (size & ~PAGE_MASK)
+ return -EINVAL;
+
+ if (vma->vm_flags & VM_WRITE)
+ direction = DMA_TO_DEVICE;
+ else
+ direction = DMA_FROM_DEVICE;
+
+ retval = fw_iso_buffer_init(&client->buffer, client->device->card,
+ page_count, direction);
+ if (retval < 0)
+ return retval;
+
+ retval = fw_iso_buffer_map(&client->buffer, vma);
+ if (retval < 0)
+ fw_iso_buffer_destroy(&client->buffer, client->device->card);
+
+ return retval;
+}
+
+static int fw_device_op_release(struct inode *inode, struct file *file)
+{
+ struct client *client = file->private_data;
+ struct event *e, *next_e;
+ struct client_resource *r, *next_r;
+ unsigned long flags;
+
+ if (client->buffer.pages)
+ fw_iso_buffer_destroy(&client->buffer, client->device->card);
+
+ if (client->iso_context)
+ fw_iso_context_destroy(client->iso_context);
+
+ list_for_each_entry_safe(r, next_r, &client->resource_list, link)
+ r->release(client, r);
+
+ /* FIXME: We should wait for the async tasklets to stop
+ * running before freeing the memory. */
+
+ list_for_each_entry_safe(e, next_e, &client->event_list, link)
+ kfree(e);
+
+ spin_lock_irqsave(&client->device->card->lock, flags);
+ list_del(&client->link);
+ spin_unlock_irqrestore(&client->device->card->lock, flags);
+
+ fw_device_put(client->device);
+ kfree(client);
+
+ return 0;
+}
+
+static unsigned int fw_device_op_poll(struct file *file, poll_table * pt)
+{
+ struct client *client = file->private_data;
+ unsigned int mask = 0;
+
+ poll_wait(file, &client->wait, pt);
+
+ if (fw_device_is_shutdown(client->device))
+ mask |= POLLHUP | POLLERR;
+ if (!list_empty(&client->event_list))
+ mask |= POLLIN | POLLRDNORM;
+
+ return mask;
+}
+
+const struct file_operations fw_device_ops = {
+ .owner = THIS_MODULE,
+ .open = fw_device_op_open,
+ .read = fw_device_op_read,
+ .unlocked_ioctl = fw_device_op_ioctl,
+ .poll = fw_device_op_poll,
+ .release = fw_device_op_release,
+ .mmap = fw_device_op_mmap,
+
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = fw_device_op_compat_ioctl,
+#endif
+};
--
Stefan Richter
-=====-=-=== -=-= ---=-
http://arcgraph.de/sr/
Signed-off-by: Stefan Richter <[email protected]>
---
drivers/firewire/fw-ohci.c | 1896 +++++++++++++++++++++++++++++++++++++
drivers/firewire/fw-ohci.h | 153 ++
2 files changed, 2049 insertions(+)
Index: linux_juju/drivers/firewire/fw-ohci.h
===================================================================
--- /dev/null
+++ linux_juju/drivers/firewire/fw-ohci.h
@@ -0,0 +1,153 @@
+#ifndef __fw_ohci_h
+#define __fw_ohci_h
+
+/* OHCI 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_BIBimageValid 0x80000000
+#define OHCI1394_HCControl_noByteSwapData 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 (1 << 9)
+#define OHCI1394_LinkControl_rcvPhyPkt (1 << 10)
+#define OHCI1394_LinkControl_cycleTimerEnable (1 << 20)
+#define OHCI1394_LinkControl_cycleMaster (1 << 21)
+#define OHCI1394_LinkControl_cycleSource (1 << 22)
+#define OHCI1394_NodeID 0x0E8
+#define OHCI1394_NodeID_idValid 0x80000000
+#define OHCI1394_PhyControl 0x0EC
+#define OHCI1394_PhyControl_Read(addr) (((addr) << 8) | 0x00008000)
+#define OHCI1394_PhyControl_ReadDone 0x80000000
+#define OHCI1394_PhyControl_ReadData(r) (((r) & 0x00ff0000) >> 16)
+#define OHCI1394_PhyControl_Write(addr, data) (((addr) << 8) | (data) | 0x00004000)
+#define OHCI1394_PhyControl_WriteDone 0x00004000
+#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 */
+#define OHCI1394_IsoXmitContextBase(n) (0x200 + 16 * (n))
+#define OHCI1394_IsoXmitContextControlSet(n) (0x200 + 16 * (n))
+#define OHCI1394_IsoXmitContextControlClear(n) (0x204 + 16 * (n))
+#define OHCI1394_IsoXmitCommandPtr(n) (0x20C + 16 * (n))
+
+/* Isochronous receive registers */
+#define OHCI1394_IsoRcvContextBase(n) (0x400 + 32 * (n))
+#define OHCI1394_IsoRcvContextControlSet(n) (0x400 + 32 * (n))
+#define OHCI1394_IsoRcvContextControlClear(n) (0x404 + 32 * (n))
+#define OHCI1394_IsoRcvCommandPtr(n) (0x40C + 32 * (n))
+#define OHCI1394_IsoRcvContextMatch(n) (0x410 + 32 * (n))
+
+/* 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
+
+#define OHCI1394_evt_no_status 0x0
+#define OHCI1394_evt_long_packet 0x2
+#define OHCI1394_evt_missing_ack 0x3
+#define OHCI1394_evt_underrun 0x4
+#define OHCI1394_evt_overrun 0x5
+#define OHCI1394_evt_descriptor_read 0x6
+#define OHCI1394_evt_data_read 0x7
+#define OHCI1394_evt_data_write 0x8
+#define OHCI1394_evt_bus_reset 0x9
+#define OHCI1394_evt_timeout 0xa
+#define OHCI1394_evt_tcode_err 0xb
+#define OHCI1394_evt_reserved_b 0xc
+#define OHCI1394_evt_reserved_c 0xd
+#define OHCI1394_evt_unknown 0xe
+#define OHCI1394_evt_flushed 0xf
+
+#define OHCI1394_phy_tcode 0xe
+
+#endif /* __fw_ohci_h */
Index: linux_juju/drivers/firewire/fw-ohci.c
===================================================================
--- /dev/null
+++ linux_juju/drivers/firewire/fw-ohci.c
@@ -0,0 +1,1896 @@
+/* -*- c-basic-offset: 8 -*-
+ *
+ * fw-ohci.c - Driver for OHCI 1394 boards
+ * Copyright (C) 2003-2006 Kristian Hoegsberg <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/poll.h>
+#include <linux/dma-mapping.h>
+
+#include <asm/uaccess.h>
+#include <asm/semaphore.h>
+
+#include "fw-transaction.h"
+#include "fw-ohci.h"
+
+#define descriptor_output_more 0
+#define descriptor_output_last (1 << 12)
+#define descriptor_input_more (2 << 12)
+#define descriptor_input_last (3 << 12)
+#define descriptor_status (1 << 11)
+#define descriptor_key_immediate (2 << 8)
+#define descriptor_ping (1 << 7)
+#define descriptor_yy (1 << 6)
+#define descriptor_no_irq (0 << 4)
+#define descriptor_irq_error (1 << 4)
+#define descriptor_irq_always (3 << 4)
+#define descriptor_branch_always (3 << 2)
+#define descriptor_wait (3 << 0)
+
+struct descriptor {
+ __le16 req_count;
+ __le16 control;
+ __le32 data_address;
+ __le32 branch_address;
+ __le16 res_count;
+ __le16 transfer_status;
+} __attribute__((aligned(16)));
+
+struct db_descriptor {
+ __le16 first_size;
+ __le16 control;
+ __le16 second_req_count;
+ __le16 first_req_count;
+ __le32 branch_address;
+ __le16 second_res_count;
+ __le16 first_res_count;
+ __le32 reserved0;
+ __le32 first_buffer;
+ __le32 second_buffer;
+ __le32 reserved1;
+} __attribute__((aligned(16)));
+
+#define control_set(regs) (regs)
+#define control_clear(regs) ((regs) + 4)
+#define command_ptr(regs) ((regs) + 12)
+#define context_match(regs) ((regs) + 16)
+
+struct ar_buffer {
+ struct descriptor descriptor;
+ struct ar_buffer *next;
+ __le32 data[0];
+};
+
+struct ar_context {
+ struct fw_ohci *ohci;
+ struct ar_buffer *current_buffer;
+ struct ar_buffer *last_buffer;
+ void *pointer;
+ u32 regs;
+ struct tasklet_struct tasklet;
+};
+
+struct context;
+
+typedef int (*descriptor_callback_t)(struct context *ctx,
+ struct descriptor *d,
+ struct descriptor *last);
+struct context {
+ struct fw_ohci *ohci;
+ u32 regs;
+
+ struct descriptor *buffer;
+ dma_addr_t buffer_bus;
+ size_t buffer_size;
+ struct descriptor *head_descriptor;
+ struct descriptor *tail_descriptor;
+ struct descriptor *tail_descriptor_last;
+ struct descriptor *prev_descriptor;
+
+ descriptor_callback_t callback;
+
+ struct tasklet_struct tasklet;
+};
+
+#define it_header_sy(v) ((v) << 0)
+#define it_header_tcode(v) ((v) << 4)
+#define it_header_channel(v) ((v) << 8)
+#define it_header_tag(v) ((v) << 14)
+#define it_header_speed(v) ((v) << 16)
+#define it_header_data_length(v) ((v) << 16)
+
+struct iso_context {
+ struct fw_iso_context base;
+ struct context context;
+ void *header;
+ size_t header_length;
+};
+
+#define CONFIG_ROM_SIZE 1024
+
+struct fw_ohci {
+ struct fw_card card;
+
+ u32 version;
+ __iomem char *registers;
+ dma_addr_t self_id_bus;
+ __le32 *self_id_cpu;
+ struct tasklet_struct bus_reset_tasklet;
+ int node_id;
+ int generation;
+ int request_generation;
+ u32 bus_seconds;
+
+ /* Spinlock for accessing fw_ohci data. Never call out of
+ * this driver with this lock held. */
+ spinlock_t lock;
+ u32 self_id_buffer[512];
+
+ /* Config rom buffers */
+ __be32 *config_rom;
+ dma_addr_t config_rom_bus;
+ __be32 *next_config_rom;
+ dma_addr_t next_config_rom_bus;
+ u32 next_header;
+
+ struct ar_context ar_request_ctx;
+ struct ar_context ar_response_ctx;
+ struct context at_request_ctx;
+ struct context at_response_ctx;
+
+ u32 it_context_mask;
+ struct iso_context *it_context_list;
+ u32 ir_context_mask;
+ struct iso_context *ir_context_list;
+};
+
+static inline struct fw_ohci *fw_ohci(struct fw_card *card)
+{
+ return container_of(card, struct fw_ohci, card);
+}
+
+#define IT_CONTEXT_CYCLE_MATCH_ENABLE 0x80000000
+#define IR_CONTEXT_BUFFER_FILL 0x80000000
+#define IR_CONTEXT_ISOCH_HEADER 0x40000000
+#define IR_CONTEXT_CYCLE_MATCH_ENABLE 0x20000000
+#define IR_CONTEXT_MULTI_CHANNEL_MODE 0x10000000
+#define IR_CONTEXT_DUAL_BUFFER_MODE 0x08000000
+
+#define CONTEXT_RUN 0x8000
+#define CONTEXT_WAKE 0x1000
+#define CONTEXT_DEAD 0x0800
+#define CONTEXT_ACTIVE 0x0400
+
+#define OHCI1394_MAX_AT_REQ_RETRIES 0x2
+#define OHCI1394_MAX_AT_RESP_RETRIES 0x2
+#define OHCI1394_MAX_PHYS_RESP_RETRIES 0x8
+
+#define FW_OHCI_MAJOR 240
+#define OHCI1394_REGISTER_SIZE 0x800
+#define OHCI_LOOP_COUNT 500
+#define OHCI1394_PCI_HCI_Control 0x40
+#define SELF_ID_BUF_SIZE 0x800
+#define OHCI_TCODE_PHY_PACKET 0x0e
+#define OHCI_VERSION_1_1 0x010010
+#define ISO_BUFFER_SIZE (64 * 1024)
+#define AT_BUFFER_SIZE 4096
+
+static char ohci_driver_name[] = KBUILD_MODNAME;
+
+static inline void reg_write(const struct fw_ohci *ohci, int offset, u32 data)
+{
+ writel(data, ohci->registers + offset);
+}
+
+static inline u32 reg_read(const struct fw_ohci *ohci, int offset)
+{
+ return readl(ohci->registers + offset);
+}
+
+static inline void flush_writes(const struct fw_ohci *ohci)
+{
+ /* Do a dummy read to flush writes. */
+ reg_read(ohci, OHCI1394_Version);
+}
+
+static int
+ohci_update_phy_reg(struct fw_card *card, int addr,
+ int clear_bits, int set_bits)
+{
+ struct fw_ohci *ohci = fw_ohci(card);
+ u32 val, old;
+
+ reg_write(ohci, OHCI1394_PhyControl, OHCI1394_PhyControl_Read(addr));
+ msleep(2);
+ val = reg_read(ohci, OHCI1394_PhyControl);
+ if ((val & OHCI1394_PhyControl_ReadDone) == 0) {
+ fw_error("failed to set phy reg bits.\n");
+ return -EBUSY;
+ }
+
+ old = OHCI1394_PhyControl_ReadData(val);
+ old = (old & ~clear_bits) | set_bits;
+ reg_write(ohci, OHCI1394_PhyControl,
+ OHCI1394_PhyControl_Write(addr, old));
+
+ return 0;
+}
+
+static int ar_context_add_page(struct ar_context *ctx)
+{
+ struct device *dev = ctx->ohci->card.device;
+ struct ar_buffer *ab;
+ dma_addr_t ab_bus;
+ size_t offset;
+
+ ab = (struct ar_buffer *) __get_free_page(GFP_ATOMIC);
+ if (ab == NULL)
+ return -ENOMEM;
+
+ ab_bus = dma_map_single(dev, ab, PAGE_SIZE, DMA_BIDIRECTIONAL);
+ if (dma_mapping_error(ab_bus)) {
+ free_page((unsigned long) ab);
+ return -ENOMEM;
+ }
+
+ memset(&ab->descriptor, 0, sizeof ab->descriptor);
+ ab->descriptor.control = cpu_to_le16(descriptor_input_more |
+ descriptor_status |
+ descriptor_branch_always);
+ offset = offsetof(struct ar_buffer, data);
+ ab->descriptor.req_count = cpu_to_le16(PAGE_SIZE - offset);
+ ab->descriptor.data_address = cpu_to_le32(ab_bus + offset);
+ ab->descriptor.res_count = cpu_to_le16(PAGE_SIZE - offset);
+ ab->descriptor.branch_address = 0;
+
+ dma_sync_single_for_device(dev, ab_bus, PAGE_SIZE, DMA_BIDIRECTIONAL);
+
+ ctx->last_buffer->descriptor.branch_address = ab_bus | 1;
+ ctx->last_buffer->next = ab;
+ ctx->last_buffer = ab;
+
+ reg_write(ctx->ohci, control_set(ctx->regs), CONTEXT_WAKE);
+ flush_writes(ctx->ohci);
+
+ return 0;
+}
+
+static __le32 *handle_ar_packet(struct ar_context *ctx, __le32 *buffer)
+{
+ struct fw_ohci *ohci = ctx->ohci;
+ struct fw_packet p;
+ u32 status, length, tcode;
+
+ p.header[0] = le32_to_cpu(buffer[0]);
+ p.header[1] = le32_to_cpu(buffer[1]);
+ p.header[2] = le32_to_cpu(buffer[2]);
+
+ tcode = (p.header[0] >> 4) & 0x0f;
+ switch (tcode) {
+ case TCODE_WRITE_QUADLET_REQUEST:
+ case TCODE_READ_QUADLET_RESPONSE:
+ p.header[3] = (__force __u32) buffer[3];
+ p.header_length = 16;
+ p.payload_length = 0;
+ break;
+
+ case TCODE_READ_BLOCK_REQUEST :
+ p.header[3] = le32_to_cpu(buffer[3]);
+ p.header_length = 16;
+ p.payload_length = 0;
+ break;
+
+ case TCODE_WRITE_BLOCK_REQUEST:
+ case TCODE_READ_BLOCK_RESPONSE:
+ case TCODE_LOCK_REQUEST:
+ case TCODE_LOCK_RESPONSE:
+ p.header[3] = le32_to_cpu(buffer[3]);
+ p.header_length = 16;
+ p.payload_length = p.header[3] >> 16;
+ break;
+
+ case TCODE_WRITE_RESPONSE:
+ case TCODE_READ_QUADLET_REQUEST:
+ case OHCI_TCODE_PHY_PACKET:
+ p.header_length = 12;
+ p.payload_length = 0;
+ break;
+ }
+
+ p.payload = (void *) buffer + p.header_length;
+
+ /* FIXME: What to do about evt_* errors? */
+ length = (p.header_length + p.payload_length + 3) / 4;
+ status = le32_to_cpu(buffer[length]);
+
+ p.ack = ((status >> 16) & 0x1f) - 16;
+ p.speed = (status >> 21) & 0x7;
+ p.timestamp = status & 0xffff;
+ p.generation = ohci->request_generation;
+
+ /* The OHCI bus reset handler synthesizes a phy packet with
+ * the new generation number when a bus reset happens (see
+ * section 8.4.2.3). This helps us determine when a request
+ * was received and make sure we send the response in the same
+ * generation. We only need this for requests; for responses
+ * we use the unique tlabel for finding the matching
+ * request. */
+
+ if (p.ack + 16 == 0x09)
+ ohci->request_generation = (buffer[2] >> 16) & 0xff;
+ else if (ctx == &ohci->ar_request_ctx)
+ fw_core_handle_request(&ohci->card, &p);
+ else
+ fw_core_handle_response(&ohci->card, &p);
+
+ return buffer + length + 1;
+}
+
+static void ar_context_tasklet(unsigned long data)
+{
+ struct ar_context *ctx = (struct ar_context *)data;
+ struct fw_ohci *ohci = ctx->ohci;
+ struct ar_buffer *ab;
+ struct descriptor *d;
+ void *buffer, *end;
+
+ ab = ctx->current_buffer;
+ d = &ab->descriptor;
+
+ if (d->res_count == 0) {
+ size_t size, rest, offset;
+
+ /* This descriptor is finished and we may have a
+ * packet split across this and the next buffer. We
+ * reuse the page for reassembling the split packet. */
+
+ offset = offsetof(struct ar_buffer, data);
+ dma_unmap_single(ohci->card.device,
+ ab->descriptor.data_address - offset,
+ PAGE_SIZE, DMA_BIDIRECTIONAL);
+
+ buffer = ab;
+ ab = ab->next;
+ d = &ab->descriptor;
+ size = buffer + PAGE_SIZE - ctx->pointer;
+ rest = le16_to_cpu(d->req_count) - le16_to_cpu(d->res_count);
+ memmove(buffer, ctx->pointer, size);
+ memcpy(buffer + size, ab->data, rest);
+ ctx->current_buffer = ab;
+ ctx->pointer = (void *) ab->data + rest;
+ end = buffer + size + rest;
+
+ while (buffer < end)
+ buffer = handle_ar_packet(ctx, buffer);
+
+ free_page((unsigned long)buffer);
+ ar_context_add_page(ctx);
+ } else {
+ buffer = ctx->pointer;
+ ctx->pointer = end =
+ (void *) ab + PAGE_SIZE - le16_to_cpu(d->res_count);
+
+ while (buffer < end)
+ buffer = handle_ar_packet(ctx, buffer);
+ }
+}
+
+static int
+ar_context_init(struct ar_context *ctx, struct fw_ohci *ohci, u32 regs)
+{
+ struct ar_buffer ab;
+
+ ctx->regs = regs;
+ ctx->ohci = ohci;
+ ctx->last_buffer = &ab;
+ tasklet_init(&ctx->tasklet, ar_context_tasklet, (unsigned long)ctx);
+
+ ar_context_add_page(ctx);
+ ar_context_add_page(ctx);
+ ctx->current_buffer = ab.next;
+ ctx->pointer = ctx->current_buffer->data;
+
+ reg_write(ctx->ohci, command_ptr(ctx->regs), ab.descriptor.branch_address);
+ reg_write(ctx->ohci, control_set(ctx->regs), CONTEXT_RUN);
+ flush_writes(ctx->ohci);
+
+ return 0;
+}
+
+static void context_tasklet(unsigned long data)
+{
+ struct context *ctx = (struct context *) data;
+ struct fw_ohci *ohci = ctx->ohci;
+ struct descriptor *d, *last;
+ u32 address;
+ int z;
+
+ dma_sync_single_for_cpu(ohci->card.device, ctx->buffer_bus,
+ ctx->buffer_size, DMA_TO_DEVICE);
+
+ d = ctx->tail_descriptor;
+ last = ctx->tail_descriptor_last;
+
+ while (last->branch_address != 0) {
+ address = le32_to_cpu(last->branch_address);
+ z = address & 0xf;
+ d = ctx->buffer + (address - ctx->buffer_bus) / sizeof *d;
+ last = (z == 2) ? d : d + z - 1;
+
+ if (!ctx->callback(ctx, d, last))
+ break;
+
+ ctx->tail_descriptor = d;
+ ctx->tail_descriptor_last = last;
+ }
+}
+
+static int
+context_init(struct context *ctx, struct fw_ohci *ohci,
+ size_t buffer_size, u32 regs,
+ descriptor_callback_t callback)
+{
+ ctx->ohci = ohci;
+ ctx->regs = regs;
+ ctx->buffer_size = buffer_size;
+ ctx->buffer = kmalloc(buffer_size, GFP_KERNEL);
+ if (ctx->buffer == NULL)
+ return -ENOMEM;
+
+ tasklet_init(&ctx->tasklet, context_tasklet, (unsigned long)ctx);
+ ctx->callback = callback;
+
+ ctx->buffer_bus =
+ dma_map_single(ohci->card.device, ctx->buffer,
+ buffer_size, DMA_TO_DEVICE);
+ if (dma_mapping_error(ctx->buffer_bus)) {
+ kfree(ctx->buffer);
+ return -ENOMEM;
+ }
+
+ ctx->head_descriptor = ctx->buffer;
+ ctx->prev_descriptor = ctx->buffer;
+ ctx->tail_descriptor = ctx->buffer;
+ ctx->tail_descriptor_last = ctx->buffer;
+
+ /* We put a dummy descriptor in the buffer that has a NULL
+ * branch address and looks like it's been sent. That way we
+ * have a descriptor to append DMA programs to. Also, the
+ * ring buffer invariant is that it always has at least one
+ * element so that head == tail means buffer full. */
+
+ memset(ctx->head_descriptor, 0, sizeof *ctx->head_descriptor);
+ ctx->head_descriptor->control = cpu_to_le16(descriptor_output_last);
+ ctx->head_descriptor->transfer_status = cpu_to_le16(0x8011);
+ ctx->head_descriptor++;
+
+ return 0;
+}
+
+static void
+context_release(struct context *ctx)
+{
+ struct fw_card *card = &ctx->ohci->card;
+
+ dma_unmap_single(card->device, ctx->buffer_bus,
+ ctx->buffer_size, DMA_TO_DEVICE);
+ kfree(ctx->buffer);
+}
+
+static struct descriptor *
+context_get_descriptors(struct context *ctx, int z, dma_addr_t *d_bus)
+{
+ struct descriptor *d, *tail, *end;
+
+ d = ctx->head_descriptor;
+ tail = ctx->tail_descriptor;
+ end = ctx->buffer + ctx->buffer_size / sizeof(struct descriptor);
+
+ if (d + z <= tail) {
+ goto has_space;
+ } else if (d > tail && d + z <= end) {
+ goto has_space;
+ } else if (d > tail && ctx->buffer + z <= tail) {
+ d = ctx->buffer;
+ goto has_space;
+ }
+
+ return NULL;
+
+ has_space:
+ memset(d, 0, z * sizeof *d);
+ *d_bus = ctx->buffer_bus + (d - ctx->buffer) * sizeof *d;
+
+ return d;
+}
+
+static void context_run(struct context *ctx, u32 extra)
+{
+ struct fw_ohci *ohci = ctx->ohci;
+
+ reg_write(ohci, command_ptr(ctx->regs),
+ le32_to_cpu(ctx->tail_descriptor_last->branch_address));
+ reg_write(ohci, control_clear(ctx->regs), ~0);
+ reg_write(ohci, control_set(ctx->regs), CONTEXT_RUN | extra);
+ flush_writes(ohci);
+}
+
+static void context_append(struct context *ctx,
+ struct descriptor *d, int z, int extra)
+{
+ dma_addr_t d_bus;
+
+ d_bus = ctx->buffer_bus + (d - ctx->buffer) * sizeof *d;
+
+ ctx->head_descriptor = d + z + extra;
+ ctx->prev_descriptor->branch_address = cpu_to_le32(d_bus | z);
+ ctx->prev_descriptor = z == 2 ? d : d + z - 1;
+
+ dma_sync_single_for_device(ctx->ohci->card.device, ctx->buffer_bus,
+ ctx->buffer_size, DMA_TO_DEVICE);
+
+ reg_write(ctx->ohci, control_set(ctx->regs), CONTEXT_WAKE);
+ flush_writes(ctx->ohci);
+}
+
+static void context_stop(struct context *ctx)
+{
+ u32 reg;
+ int i;
+
+ reg_write(ctx->ohci, control_clear(ctx->regs), CONTEXT_RUN);
+ flush_writes(ctx->ohci);
+
+ for (i = 0; i < 10; i++) {
+ reg = reg_read(ctx->ohci, control_set(ctx->regs));
+ if ((reg & CONTEXT_ACTIVE) == 0)
+ break;
+
+ fw_notify("context_stop: still active (0x%08x)\n", reg);
+ msleep(1);
+ }
+}
+
+struct driver_data {
+ struct fw_packet *packet;
+};
+
+/* This function apppends a packet to the DMA queue for transmission.
+ * Must always be called with the ochi->lock held to ensure proper
+ * generation handling and locking around packet queue manipulation. */
+static int
+at_context_queue_packet(struct context *ctx, struct fw_packet *packet)
+{
+ struct fw_ohci *ohci = ctx->ohci;
+ dma_addr_t d_bus, payload_bus;
+ struct driver_data *driver_data;
+ struct descriptor *d, *last;
+ __le32 *header;
+ int z, tcode;
+ u32 reg;
+
+ d = context_get_descriptors(ctx, 4, &d_bus);
+ if (d == NULL) {
+ packet->ack = RCODE_SEND_ERROR;
+ return -1;
+ }
+
+ d[0].control = cpu_to_le16(descriptor_key_immediate);
+ d[0].res_count = cpu_to_le16(packet->timestamp);
+
+ /* The DMA format for asyncronous link packets is different
+ * from the IEEE1394 layout, so shift the fields around
+ * accordingly. If header_length is 8, it's a PHY packet, to
+ * which we need to prepend an extra quadlet. */
+
+ header = (__le32 *) &d[1];
+ if (packet->header_length > 8) {
+ header[0] = cpu_to_le32((packet->header[0] & 0xffff) |
+ (packet->speed << 16));
+ header[1] = cpu_to_le32((packet->header[1] & 0xffff) |
+ (packet->header[0] & 0xffff0000));
+ header[2] = cpu_to_le32(packet->header[2]);
+
+ tcode = (packet->header[0] >> 4) & 0x0f;
+ if (TCODE_IS_BLOCK_PACKET(tcode))
+ header[3] = cpu_to_le32(packet->header[3]);
+ else
+ header[3] = (__force __le32) packet->header[3];
+
+ d[0].req_count = cpu_to_le16(packet->header_length);
+ } else {
+ header[0] = cpu_to_le32((OHCI1394_phy_tcode << 4) |
+ (packet->speed << 16));
+ header[1] = cpu_to_le32(packet->header[0]);
+ header[2] = cpu_to_le32(packet->header[1]);
+ d[0].req_count = cpu_to_le16(12);
+ }
+
+ driver_data = (struct driver_data *) &d[3];
+ driver_data->packet = packet;
+ packet->driver_data = driver_data;
+
+ if (packet->payload_length > 0) {
+ payload_bus =
+ dma_map_single(ohci->card.device, packet->payload,
+ packet->payload_length, DMA_TO_DEVICE);
+ if (dma_mapping_error(payload_bus)) {
+ packet->ack = RCODE_SEND_ERROR;
+ return -1;
+ }
+
+ d[2].req_count = cpu_to_le16(packet->payload_length);
+ d[2].data_address = cpu_to_le32(payload_bus);
+ last = &d[2];
+ z = 3;
+ } else {
+ last = &d[0];
+ z = 2;
+ }
+
+ last->control |= cpu_to_le16(descriptor_output_last |
+ descriptor_irq_always |
+ descriptor_branch_always);
+
+ /* FIXME: Document how the locking works. */
+ if (ohci->generation != packet->generation) {
+ packet->ack = RCODE_GENERATION;
+ return -1;
+ }
+
+ context_append(ctx, d, z, 4 - z);
+
+ /* If the context isn't already running, start it up. */
+ reg = reg_read(ctx->ohci, control_set(ctx->regs));
+ if ((reg & CONTEXT_RUN) == 0)
+ context_run(ctx, 0);
+
+ return 0;
+}
+
+static int handle_at_packet(struct context *context,
+ struct descriptor *d,
+ struct descriptor *last)
+{
+ struct driver_data *driver_data;
+ struct fw_packet *packet;
+ struct fw_ohci *ohci = context->ohci;
+ dma_addr_t payload_bus;
+ int evt;
+
+ if (last->transfer_status == 0)
+ /* This descriptor isn't done yet, stop iteration. */
+ return 0;
+
+ driver_data = (struct driver_data *) &d[3];
+ packet = driver_data->packet;
+ if (packet == NULL)
+ /* This packet was cancelled, just continue. */
+ return 1;
+
+ payload_bus = le32_to_cpu(last->data_address);
+ if (payload_bus != 0)
+ dma_unmap_single(ohci->card.device, payload_bus,
+ packet->payload_length, DMA_TO_DEVICE);
+
+ evt = le16_to_cpu(last->transfer_status) & 0x1f;
+ packet->timestamp = le16_to_cpu(last->res_count);
+
+ switch (evt) {
+ case OHCI1394_evt_timeout:
+ /* Async response transmit timed out. */
+ packet->ack = RCODE_CANCELLED;
+ break;
+
+ case OHCI1394_evt_flushed:
+ /* The packet was flushed should give same error as
+ * when we try to use a stale generation count. */
+ packet->ack = RCODE_GENERATION;
+ break;
+
+ case OHCI1394_evt_missing_ack:
+ /* Using a valid (current) generation count, but the
+ * node is not on the bus or not sending acks. */
+ packet->ack = RCODE_NO_ACK;
+ break;
+
+ case ACK_COMPLETE + 0x10:
+ case ACK_PENDING + 0x10:
+ case ACK_BUSY_X + 0x10:
+ case ACK_BUSY_A + 0x10:
+ case ACK_BUSY_B + 0x10:
+ case ACK_DATA_ERROR + 0x10:
+ case ACK_TYPE_ERROR + 0x10:
+ packet->ack = evt - 0x10;
+ break;
+
+ default:
+ packet->ack = RCODE_SEND_ERROR;
+ break;
+ }
+
+ packet->callback(packet, &ohci->card, packet->ack);
+
+ return 1;
+}
+
+#define header_get_destination(q) (((q) >> 16) & 0xffff)
+#define header_get_tcode(q) (((q) >> 4) & 0x0f)
+#define header_get_offset_high(q) (((q) >> 0) & 0xffff)
+#define header_get_data_length(q) (((q) >> 16) & 0xffff)
+#define header_get_extended_tcode(q) (((q) >> 0) & 0xffff)
+
+static void
+handle_local_rom(struct fw_ohci *ohci, struct fw_packet *packet, u32 csr)
+{
+ struct fw_packet response;
+ int tcode, length, i;
+
+ tcode = header_get_tcode(packet->header[0]);
+ if (TCODE_IS_BLOCK_PACKET(tcode))
+ length = header_get_data_length(packet->header[3]);
+ else
+ length = 4;
+
+ i = csr - CSR_CONFIG_ROM;
+ if (i + length > CONFIG_ROM_SIZE) {
+ fw_fill_response(&response, packet->header,
+ RCODE_ADDRESS_ERROR, NULL, 0);
+ } else if (!TCODE_IS_READ_REQUEST(tcode)) {
+ fw_fill_response(&response, packet->header,
+ RCODE_TYPE_ERROR, NULL, 0);
+ } else {
+ fw_fill_response(&response, packet->header, RCODE_COMPLETE,
+ (void *) ohci->config_rom + i, length);
+ }
+
+ fw_core_handle_response(&ohci->card, &response);
+}
+
+static void
+handle_local_lock(struct fw_ohci *ohci, struct fw_packet *packet, u32 csr)
+{
+ struct fw_packet response;
+ int tcode, length, ext_tcode, sel;
+ __be32 *payload, lock_old;
+ u32 lock_arg, lock_data;
+
+ tcode = header_get_tcode(packet->header[0]);
+ length = header_get_data_length(packet->header[3]);
+ payload = packet->payload;
+ ext_tcode = header_get_extended_tcode(packet->header[3]);
+
+ if (tcode == TCODE_LOCK_REQUEST &&
+ ext_tcode == EXTCODE_COMPARE_SWAP && length == 8) {
+ lock_arg = be32_to_cpu(payload[0]);
+ lock_data = be32_to_cpu(payload[1]);
+ } else if (tcode == TCODE_READ_QUADLET_REQUEST) {
+ lock_arg = 0;
+ lock_data = 0;
+ } else {
+ fw_fill_response(&response, packet->header,
+ RCODE_TYPE_ERROR, NULL, 0);
+ goto out;
+ }
+
+ sel = (csr - CSR_BUS_MANAGER_ID) / 4;
+ reg_write(ohci, OHCI1394_CSRData, lock_data);
+ reg_write(ohci, OHCI1394_CSRCompareData, lock_arg);
+ reg_write(ohci, OHCI1394_CSRControl, sel);
+
+ if (reg_read(ohci, OHCI1394_CSRControl) & 0x80000000)
+ lock_old = cpu_to_be32(reg_read(ohci, OHCI1394_CSRData));
+ else
+ fw_notify("swap not done yet\n");
+
+ fw_fill_response(&response, packet->header,
+ RCODE_COMPLETE, &lock_old, sizeof lock_old);
+ out:
+ fw_core_handle_response(&ohci->card, &response);
+}
+
+static void
+handle_local_request(struct context *ctx, struct fw_packet *packet)
+{
+ u64 offset;
+ u32 csr;
+
+ if (ctx == &ctx->ohci->at_request_ctx) {
+ packet->ack = ACK_PENDING;
+ packet->callback(packet, &ctx->ohci->card, packet->ack);
+ }
+
+ offset =
+ ((unsigned long long)
+ header_get_offset_high(packet->header[1]) << 32) |
+ packet->header[2];
+ csr = offset - CSR_REGISTER_BASE;
+
+ /* Handle config rom reads. */
+ if (csr >= CSR_CONFIG_ROM && csr < CSR_CONFIG_ROM_END)
+ handle_local_rom(ctx->ohci, packet, csr);
+ else switch (csr) {
+ case CSR_BUS_MANAGER_ID:
+ case CSR_BANDWIDTH_AVAILABLE:
+ case CSR_CHANNELS_AVAILABLE_HI:
+ case CSR_CHANNELS_AVAILABLE_LO:
+ handle_local_lock(ctx->ohci, packet, csr);
+ break;
+ default:
+ if (ctx == &ctx->ohci->at_request_ctx)
+ fw_core_handle_request(&ctx->ohci->card, packet);
+ else
+ fw_core_handle_response(&ctx->ohci->card, packet);
+ break;
+ }
+
+ if (ctx == &ctx->ohci->at_response_ctx) {
+ packet->ack = ACK_COMPLETE;
+ packet->callback(packet, &ctx->ohci->card, packet->ack);
+ }
+}
+
+static void
+at_context_transmit(struct context *ctx, struct fw_packet *packet)
+{
+ unsigned long flags;
+ int retval;
+
+ spin_lock_irqsave(&ctx->ohci->lock, flags);
+
+ if (header_get_destination(packet->header[0]) == ctx->ohci->node_id &&
+ ctx->ohci->generation == packet->generation) {
+ spin_unlock_irqrestore(&ctx->ohci->lock, flags);
+ handle_local_request(ctx, packet);
+ return;
+ }
+
+ retval = at_context_queue_packet(ctx, packet);
+ spin_unlock_irqrestore(&ctx->ohci->lock, flags);
+
+ if (retval < 0)
+ packet->callback(packet, &ctx->ohci->card, packet->ack);
+
+}
+
+static void bus_reset_tasklet(unsigned long data)
+{
+ struct fw_ohci *ohci = (struct fw_ohci *)data;
+ int self_id_count, i, j, reg;
+ int generation, new_generation;
+ unsigned long flags;
+
+ reg = reg_read(ohci, OHCI1394_NodeID);
+ if (!(reg & OHCI1394_NodeID_idValid)) {
+ fw_error("node ID not valid, new bus reset in progress\n");
+ return;
+ }
+ ohci->node_id = reg & 0xffff;
+
+ /* The count in the SelfIDCount register is the number of
+ * bytes in the self ID receive buffer. Since we also receive
+ * the inverted quadlets and a header quadlet, we shift one
+ * bit extra to get the actual number of self IDs. */
+
+ self_id_count = (reg_read(ohci, OHCI1394_SelfIDCount) >> 3) & 0x3ff;
+ generation = (le32_to_cpu(ohci->self_id_cpu[0]) >> 16) & 0xff;
+
+ for (i = 1, j = 0; j < self_id_count; i += 2, j++) {
+ if (ohci->self_id_cpu[i] != ~ohci->self_id_cpu[i + 1])
+ fw_error("inconsistent self IDs\n");
+ ohci->self_id_buffer[j] = le32_to_cpu(ohci->self_id_cpu[i]);
+ }
+
+ /* Check the consistency of the self IDs we just read. The
+ * problem we face is that a new bus reset can start while we
+ * read out the self IDs from the DMA buffer. If this happens,
+ * the DMA buffer will be overwritten with new self IDs and we
+ * will read out inconsistent data. The OHCI specification
+ * (section 11.2) recommends a technique similar to
+ * linux/seqlock.h, where we remember the generation of the
+ * self IDs in the buffer before reading them out and compare
+ * it to the current generation after reading them out. If
+ * the two generations match we know we have a consistent set
+ * of self IDs. */
+
+ new_generation = (reg_read(ohci, OHCI1394_SelfIDCount) >> 16) & 0xff;
+ if (new_generation != generation) {
+ fw_notify("recursive bus reset detected, "
+ "discarding self ids\n");
+ return;
+ }
+
+ /* FIXME: Document how the locking works. */
+ spin_lock_irqsave(&ohci->lock, flags);
+
+ ohci->generation = generation;
+ context_stop(&ohci->at_request_ctx);
+ context_stop(&ohci->at_response_ctx);
+ reg_write(ohci, OHCI1394_IntEventClear, OHCI1394_busReset);
+
+ /* This next bit is unrelated to the AT context stuff but we
+ * have to do it under the spinlock also. If a new config rom
+ * was set up before this reset, the old one is now no longer
+ * in use and we can free it. Update the config rom pointers
+ * to point to the current config rom and clear the
+ * next_config_rom pointer so a new udpate can take place. */
+
+ if (ohci->next_config_rom != NULL) {
+ dma_free_coherent(ohci->card.device, CONFIG_ROM_SIZE,
+ ohci->config_rom, ohci->config_rom_bus);
+ ohci->config_rom = ohci->next_config_rom;
+ ohci->config_rom_bus = ohci->next_config_rom_bus;
+ ohci->next_config_rom = NULL;
+
+ /* Restore config_rom image and manually update
+ * config_rom registers. Writing the header quadlet
+ * will indicate that the config rom is ready, so we
+ * do that last. */
+ reg_write(ohci, OHCI1394_BusOptions,
+ be32_to_cpu(ohci->config_rom[2]));
+ ohci->config_rom[0] = cpu_to_be32(ohci->next_header);
+ reg_write(ohci, OHCI1394_ConfigROMhdr, ohci->next_header);
+ }
+
+ spin_unlock_irqrestore(&ohci->lock, flags);
+
+ fw_core_handle_bus_reset(&ohci->card, ohci->node_id, generation,
+ self_id_count, ohci->self_id_buffer);
+}
+
+static irqreturn_t irq_handler(int irq, void *data)
+{
+ struct fw_ohci *ohci = data;
+ u32 event, iso_event, cycle_time;
+ int i;
+
+ event = reg_read(ohci, OHCI1394_IntEventClear);
+
+ if (!event)
+ return IRQ_NONE;
+
+ reg_write(ohci, OHCI1394_IntEventClear, event);
+
+ if (event & OHCI1394_selfIDComplete)
+ tasklet_schedule(&ohci->bus_reset_tasklet);
+
+ if (event & OHCI1394_RQPkt)
+ tasklet_schedule(&ohci->ar_request_ctx.tasklet);
+
+ if (event & OHCI1394_RSPkt)
+ tasklet_schedule(&ohci->ar_response_ctx.tasklet);
+
+ if (event & OHCI1394_reqTxComplete)
+ tasklet_schedule(&ohci->at_request_ctx.tasklet);
+
+ if (event & OHCI1394_respTxComplete)
+ tasklet_schedule(&ohci->at_response_ctx.tasklet);
+
+ iso_event = reg_read(ohci, OHCI1394_IsoRecvIntEventClear);
+ reg_write(ohci, OHCI1394_IsoRecvIntEventClear, iso_event);
+
+ while (iso_event) {
+ i = ffs(iso_event) - 1;
+ tasklet_schedule(&ohci->ir_context_list[i].context.tasklet);
+ iso_event &= ~(1 << i);
+ }
+
+ iso_event = reg_read(ohci, OHCI1394_IsoXmitIntEventClear);
+ reg_write(ohci, OHCI1394_IsoXmitIntEventClear, iso_event);
+
+ while (iso_event) {
+ i = ffs(iso_event) - 1;
+ tasklet_schedule(&ohci->it_context_list[i].context.tasklet);
+ iso_event &= ~(1 << i);
+ }
+
+ if (event & OHCI1394_cycle64Seconds) {
+ cycle_time = reg_read(ohci, OHCI1394_IsochronousCycleTimer);
+ if ((cycle_time & 0x80000000) == 0)
+ ohci->bus_seconds++;
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int ohci_enable(struct fw_card *card, u32 *config_rom, size_t length)
+{
+ struct fw_ohci *ohci = fw_ohci(card);
+ struct pci_dev *dev = to_pci_dev(card->device);
+
+ /* When the link is not yet enabled, the atomic config rom
+ * update mechanism described below in ohci_set_config_rom()
+ * is not active. We have to update ConfigRomHeader and
+ * BusOptions manually, and the write to ConfigROMmap takes
+ * effect immediately. We tie this to the enabling of the
+ * link, so we have a valid config rom before enabling - the
+ * OHCI requires that ConfigROMhdr and BusOptions have valid
+ * values before enabling.
+ *
+ * However, when the ConfigROMmap is written, some controllers
+ * always read back quadlets 0 and 2 from the config rom to
+ * the ConfigRomHeader and BusOptions registers on bus reset.
+ * They shouldn't do that in this initial case where the link
+ * isn't enabled. This means we have to use the same
+ * workaround here, setting the bus header to 0 and then write
+ * the right values in the bus reset tasklet.
+ */
+
+ ohci->next_config_rom =
+ dma_alloc_coherent(ohci->card.device, CONFIG_ROM_SIZE,
+ &ohci->next_config_rom_bus, GFP_KERNEL);
+ if (ohci->next_config_rom == NULL)
+ return -ENOMEM;
+
+ memset(ohci->next_config_rom, 0, CONFIG_ROM_SIZE);
+ fw_memcpy_to_be32(ohci->next_config_rom, config_rom, length * 4);
+
+ ohci->next_header = config_rom[0];
+ ohci->next_config_rom[0] = 0;
+ reg_write(ohci, OHCI1394_ConfigROMhdr, 0);
+ reg_write(ohci, OHCI1394_BusOptions, config_rom[2]);
+ reg_write(ohci, OHCI1394_ConfigROMmap, ohci->next_config_rom_bus);
+
+ reg_write(ohci, OHCI1394_AsReqFilterHiSet, 0x80000000);
+
+ if (request_irq(dev->irq, irq_handler,
+ IRQF_SHARED, ohci_driver_name, ohci)) {
+ fw_error("Failed to allocate shared interrupt %d.\n",
+ dev->irq);
+ dma_free_coherent(ohci->card.device, CONFIG_ROM_SIZE,
+ ohci->config_rom, ohci->config_rom_bus);
+ return -EIO;
+ }
+
+ reg_write(ohci, OHCI1394_HCControlSet,
+ OHCI1394_HCControl_linkEnable |
+ OHCI1394_HCControl_BIBimageValid);
+ flush_writes(ohci);
+
+ /* We are ready to go, initiate bus reset to finish the
+ * initialization. */
+
+ fw_core_initiate_bus_reset(&ohci->card, 1);
+
+ return 0;
+}
+
+static int
+ohci_set_config_rom(struct fw_card *card, u32 *config_rom, size_t length)
+{
+ struct fw_ohci *ohci;
+ unsigned long flags;
+ int retval = 0;
+ __be32 *next_config_rom;
+ dma_addr_t next_config_rom_bus;
+
+ ohci = fw_ohci(card);
+
+ /* When the OHCI controller is enabled, the config rom update
+ * mechanism is a bit tricky, but easy enough to use. See
+ * section 5.5.6 in the OHCI specification.
+ *
+ * The OHCI controller caches the new config rom address in a
+ * shadow register (ConfigROMmapNext) and needs a bus reset
+ * for the changes to take place. When the bus reset is
+ * detected, the controller loads the new values for the
+ * ConfigRomHeader and BusOptions registers from the specified
+ * config rom and loads ConfigROMmap from the ConfigROMmapNext
+ * shadow register. All automatically and atomically.
+ *
+ * Now, there's a twist to this story. The automatic load of
+ * ConfigRomHeader and BusOptions doesn't honor the
+ * noByteSwapData bit, so with a be32 config rom, the
+ * controller will load be32 values in to these registers
+ * during the atomic update, even on litte endian
+ * architectures. The workaround we use is to put a 0 in the
+ * header quadlet; 0 is endian agnostic and means that the
+ * config rom isn't ready yet. In the bus reset tasklet we
+ * then set up the real values for the two registers.
+ *
+ * We use ohci->lock to avoid racing with the code that sets
+ * ohci->next_config_rom to NULL (see bus_reset_tasklet).
+ */
+
+ next_config_rom =
+ dma_alloc_coherent(ohci->card.device, CONFIG_ROM_SIZE,
+ &next_config_rom_bus, GFP_KERNEL);
+ if (next_config_rom == NULL)
+ return -ENOMEM;
+
+ spin_lock_irqsave(&ohci->lock, flags);
+
+ if (ohci->next_config_rom == NULL) {
+ ohci->next_config_rom = next_config_rom;
+ ohci->next_config_rom_bus = next_config_rom_bus;
+
+ memset(ohci->next_config_rom, 0, CONFIG_ROM_SIZE);
+ fw_memcpy_to_be32(ohci->next_config_rom, config_rom,
+ length * 4);
+
+ ohci->next_header = config_rom[0];
+ ohci->next_config_rom[0] = 0;
+
+ reg_write(ohci, OHCI1394_ConfigROMmap,
+ ohci->next_config_rom_bus);
+ } else {
+ dma_free_coherent(ohci->card.device, CONFIG_ROM_SIZE,
+ next_config_rom, next_config_rom_bus);
+ retval = -EBUSY;
+ }
+
+ spin_unlock_irqrestore(&ohci->lock, flags);
+
+ /* Now initiate a bus reset to have the changes take
+ * effect. We clean up the old config rom memory and DMA
+ * mappings in the bus reset tasklet, since the OHCI
+ * controller could need to access it before the bus reset
+ * takes effect. */
+ if (retval == 0)
+ fw_core_initiate_bus_reset(&ohci->card, 1);
+
+ return retval;
+}
+
+static void ohci_send_request(struct fw_card *card, struct fw_packet *packet)
+{
+ struct fw_ohci *ohci = fw_ohci(card);
+
+ at_context_transmit(&ohci->at_request_ctx, packet);
+}
+
+static void ohci_send_response(struct fw_card *card, struct fw_packet *packet)
+{
+ struct fw_ohci *ohci = fw_ohci(card);
+
+ at_context_transmit(&ohci->at_response_ctx, packet);
+}
+
+static int ohci_cancel_packet(struct fw_card *card, struct fw_packet *packet)
+{
+ struct fw_ohci *ohci = fw_ohci(card);
+ struct context *ctx = &ohci->at_request_ctx;
+ struct driver_data *driver_data = packet->driver_data;
+ int retval = -ENOENT;
+
+ tasklet_disable(&ctx->tasklet);
+
+ if (packet->ack != 0)
+ goto out;
+
+ driver_data->packet = NULL;
+ packet->ack = RCODE_CANCELLED;
+ packet->callback(packet, &ohci->card, packet->ack);
+ retval = 0;
+
+ out:
+ tasklet_enable(&ctx->tasklet);
+
+ return retval;
+}
+
+static int
+ohci_enable_phys_dma(struct fw_card *card, int node_id, int generation)
+{
+ struct fw_ohci *ohci = fw_ohci(card);
+ unsigned long flags;
+ int n, retval = 0;
+
+ /* FIXME: Make sure this bitmask is cleared when we clear the busReset
+ * interrupt bit. Clear physReqResourceAllBuses on bus reset. */
+
+ spin_lock_irqsave(&ohci->lock, flags);
+
+ if (ohci->generation != generation) {
+ retval = -ESTALE;
+ goto out;
+ }
+
+ /* NOTE, if the node ID contains a non-local bus ID, physical DMA is
+ * enabled for _all_ nodes on remote buses. */
+
+ n = (node_id & 0xffc0) == LOCAL_BUS ? node_id & 0x3f : 63;
+ if (n < 32)
+ reg_write(ohci, OHCI1394_PhyReqFilterLoSet, 1 << n);
+ else
+ reg_write(ohci, OHCI1394_PhyReqFilterHiSet, 1 << (n - 32));
+
+ flush_writes(ohci);
+ out:
+ spin_unlock_irqrestore(&ohci->lock, flags);
+ return retval;
+}
+
+static u64
+ohci_get_bus_time(struct fw_card *card)
+{
+ struct fw_ohci *ohci = fw_ohci(card);
+ u32 cycle_time;
+ u64 bus_time;
+
+ cycle_time = reg_read(ohci, OHCI1394_IsochronousCycleTimer);
+ bus_time = ((u64) ohci->bus_seconds << 32) | cycle_time;
+
+ return bus_time;
+}
+
+static int handle_ir_dualbuffer_packet(struct context *context,
+ struct descriptor *d,
+ struct descriptor *last)
+{
+ struct iso_context *ctx =
+ container_of(context, struct iso_context, context);
+ struct db_descriptor *db = (struct db_descriptor *) d;
+ __le32 *ir_header;
+ size_t header_length;
+ void *p, *end;
+ int i;
+
+ if (db->first_res_count > 0 && db->second_res_count > 0)
+ /* This descriptor isn't done yet, stop iteration. */
+ return 0;
+
+ header_length = le16_to_cpu(db->first_req_count) -
+ le16_to_cpu(db->first_res_count);
+
+ i = ctx->header_length;
+ p = db + 1;
+ end = p + header_length;
+ while (p < end && i + ctx->base.header_size <= PAGE_SIZE) {
+ /* The iso header is byteswapped to little endian by
+ * the controller, but the remaining header quadlets
+ * are big endian. We want to present all the headers
+ * as big endian, so we have to swap the first
+ * quadlet. */
+ *(u32 *) (ctx->header + i) = __swab32(*(u32 *) (p + 4));
+ memcpy(ctx->header + i + 4, p + 8, ctx->base.header_size - 4);
+ i += ctx->base.header_size;
+ p += ctx->base.header_size + 4;
+ }
+
+ ctx->header_length = i;
+
+ if (le16_to_cpu(db->control) & descriptor_irq_always) {
+ ir_header = (__le32 *) (db + 1);
+ ctx->base.callback(&ctx->base,
+ le32_to_cpu(ir_header[0]) & 0xffff,
+ ctx->header_length, ctx->header,
+ ctx->base.callback_data);
+ ctx->header_length = 0;
+ }
+
+ return 1;
+}
+
+static int handle_it_packet(struct context *context,
+ struct descriptor *d,
+ struct descriptor *last)
+{
+ struct iso_context *ctx =
+ container_of(context, struct iso_context, context);
+
+ if (last->transfer_status == 0)
+ /* This descriptor isn't done yet, stop iteration. */
+ return 0;
+
+ if (le16_to_cpu(last->control) & descriptor_irq_always)
+ ctx->base.callback(&ctx->base, le16_to_cpu(last->res_count),
+ 0, NULL, ctx->base.callback_data);
+
+ return 1;
+}
+
+static struct fw_iso_context *
+ohci_allocate_iso_context(struct fw_card *card, int type, size_t header_size)
+{
+ struct fw_ohci *ohci = fw_ohci(card);
+ struct iso_context *ctx, *list;
+ descriptor_callback_t callback;
+ u32 *mask, regs;
+ unsigned long flags;
+ int index, retval = -ENOMEM;
+
+ if (type == FW_ISO_CONTEXT_TRANSMIT) {
+ mask = &ohci->it_context_mask;
+ list = ohci->it_context_list;
+ callback = handle_it_packet;
+ } else {
+ mask = &ohci->ir_context_mask;
+ list = ohci->ir_context_list;
+ callback = handle_ir_dualbuffer_packet;
+ }
+
+ /* FIXME: We need a fallback for pre 1.1 OHCI. */
+ if (callback == handle_ir_dualbuffer_packet &&
+ ohci->version < OHCI_VERSION_1_1)
+ return ERR_PTR(-EINVAL);
+
+ spin_lock_irqsave(&ohci->lock, flags);
+ index = ffs(*mask) - 1;
+ if (index >= 0)
+ *mask &= ~(1 << index);
+ spin_unlock_irqrestore(&ohci->lock, flags);
+
+ if (index < 0)
+ return ERR_PTR(-EBUSY);
+
+ if (type == FW_ISO_CONTEXT_TRANSMIT)
+ regs = OHCI1394_IsoXmitContextBase(index);
+ else
+ regs = OHCI1394_IsoRcvContextBase(index);
+
+ ctx = &list[index];
+ memset(ctx, 0, sizeof *ctx);
+ ctx->header_length = 0;
+ ctx->header = (void *) __get_free_page(GFP_KERNEL);
+ if (ctx->header == NULL)
+ goto out;
+
+ retval = context_init(&ctx->context, ohci, ISO_BUFFER_SIZE,
+ regs, callback);
+ if (retval < 0)
+ goto out_with_header;
+
+ return &ctx->base;
+
+ out_with_header:
+ free_page((unsigned long)ctx->header);
+ out:
+ spin_lock_irqsave(&ohci->lock, flags);
+ *mask |= 1 << index;
+ spin_unlock_irqrestore(&ohci->lock, flags);
+
+ return ERR_PTR(retval);
+}
+
+static int ohci_start_iso(struct fw_iso_context *base,
+ s32 cycle, u32 sync, u32 tags)
+{
+ struct iso_context *ctx = container_of(base, struct iso_context, base);
+ struct fw_ohci *ohci = ctx->context.ohci;
+ u32 control, match;
+ int index;
+
+ if (ctx->base.type == FW_ISO_CONTEXT_TRANSMIT) {
+ index = ctx - ohci->it_context_list;
+ match = 0;
+ if (cycle >= 0)
+ match = IT_CONTEXT_CYCLE_MATCH_ENABLE |
+ (cycle & 0x7fff) << 16;
+
+ reg_write(ohci, OHCI1394_IsoXmitIntEventClear, 1 << index);
+ reg_write(ohci, OHCI1394_IsoXmitIntMaskSet, 1 << index);
+ context_run(&ctx->context, match);
+ } else {
+ index = ctx - ohci->ir_context_list;
+ control = IR_CONTEXT_DUAL_BUFFER_MODE | IR_CONTEXT_ISOCH_HEADER;
+ match = (tags << 28) | (sync << 8) | ctx->base.channel;
+ if (cycle >= 0) {
+ match |= (cycle & 0x07fff) << 12;
+ control |= IR_CONTEXT_CYCLE_MATCH_ENABLE;
+ }
+
+ reg_write(ohci, OHCI1394_IsoRecvIntEventClear, 1 << index);
+ reg_write(ohci, OHCI1394_IsoRecvIntMaskSet, 1 << index);
+ reg_write(ohci, context_match(ctx->context.regs), match);
+ context_run(&ctx->context, control);
+ }
+
+ return 0;
+}
+
+static int ohci_stop_iso(struct fw_iso_context *base)
+{
+ struct fw_ohci *ohci = fw_ohci(base->card);
+ struct iso_context *ctx = container_of(base, struct iso_context, base);
+ int index;
+
+ if (ctx->base.type == FW_ISO_CONTEXT_TRANSMIT) {
+ index = ctx - ohci->it_context_list;
+ reg_write(ohci, OHCI1394_IsoXmitIntMaskClear, 1 << index);
+ } else {
+ index = ctx - ohci->ir_context_list;
+ reg_write(ohci, OHCI1394_IsoRecvIntMaskClear, 1 << index);
+ }
+ flush_writes(ohci);
+ context_stop(&ctx->context);
+
+ return 0;
+}
+
+static void ohci_free_iso_context(struct fw_iso_context *base)
+{
+ struct fw_ohci *ohci = fw_ohci(base->card);
+ struct iso_context *ctx = container_of(base, struct iso_context, base);
+ unsigned long flags;
+ int index;
+
+ ohci_stop_iso(base);
+ context_release(&ctx->context);
+ free_page((unsigned long)ctx->header);
+
+ spin_lock_irqsave(&ohci->lock, flags);
+
+ if (ctx->base.type == FW_ISO_CONTEXT_TRANSMIT) {
+ index = ctx - ohci->it_context_list;
+ ohci->it_context_mask |= 1 << index;
+ } else {
+ index = ctx - ohci->ir_context_list;
+ ohci->ir_context_mask |= 1 << index;
+ }
+
+ spin_unlock_irqrestore(&ohci->lock, flags);
+}
+
+static int
+ohci_queue_iso_transmit(struct fw_iso_context *base,
+ struct fw_iso_packet *packet,
+ struct fw_iso_buffer *buffer,
+ unsigned long payload)
+{
+ struct iso_context *ctx = container_of(base, struct iso_context, base);
+ struct descriptor *d, *last, *pd;
+ struct fw_iso_packet *p;
+ __le32 *header;
+ dma_addr_t d_bus, page_bus;
+ u32 z, header_z, payload_z, irq;
+ u32 payload_index, payload_end_index, next_page_index;
+ int page, end_page, i, length, offset;
+
+ /* FIXME: Cycle lost behavior should be configurable: lose
+ * packet, retransmit or terminate.. */
+
+ p = packet;
+ payload_index = payload;
+
+ if (p->skip)
+ z = 1;
+ else
+ z = 2;
+ if (p->header_length > 0)
+ z++;
+
+ /* Determine the first page the payload isn't contained in. */
+ end_page = PAGE_ALIGN(payload_index + p->payload_length) >> PAGE_SHIFT;
+ if (p->payload_length > 0)
+ payload_z = end_page - (payload_index >> PAGE_SHIFT);
+ else
+ payload_z = 0;
+
+ z += payload_z;
+
+ /* Get header size in number of descriptors. */
+ header_z = DIV_ROUND_UP(p->header_length, sizeof *d);
+
+ d = context_get_descriptors(&ctx->context, z + header_z, &d_bus);
+ if (d == NULL)
+ return -ENOMEM;
+
+ if (!p->skip) {
+ d[0].control = cpu_to_le16(descriptor_key_immediate);
+ d[0].req_count = cpu_to_le16(8);
+
+ header = (__le32 *) &d[1];
+ header[0] = cpu_to_le32(it_header_sy(p->sy) |
+ it_header_tag(p->tag) |
+ it_header_tcode(TCODE_STREAM_DATA) |
+ it_header_channel(ctx->base.channel) |
+ it_header_speed(ctx->base.speed));
+ header[1] =
+ cpu_to_le32(it_header_data_length(p->header_length +
+ p->payload_length));
+ }
+
+ if (p->header_length > 0) {
+ d[2].req_count = cpu_to_le16(p->header_length);
+ d[2].data_address = cpu_to_le32(d_bus + z * sizeof *d);
+ memcpy(&d[z], p->header, p->header_length);
+ }
+
+ pd = d + z - payload_z;
+ payload_end_index = payload_index + p->payload_length;
+ for (i = 0; i < payload_z; i++) {
+ page = payload_index >> PAGE_SHIFT;
+ offset = payload_index & ~PAGE_MASK;
+ next_page_index = (page + 1) << PAGE_SHIFT;
+ length =
+ min(next_page_index, payload_end_index) - payload_index;
+ pd[i].req_count = cpu_to_le16(length);
+
+ page_bus = page_private(buffer->pages[page]);
+ pd[i].data_address = cpu_to_le32(page_bus + offset);
+
+ payload_index += length;
+ }
+
+ if (p->interrupt)
+ irq = descriptor_irq_always;
+ else
+ irq = descriptor_no_irq;
+
+ last = z == 2 ? d : d + z - 1;
+ last->control |= cpu_to_le16(descriptor_output_last |
+ descriptor_status |
+ descriptor_branch_always |
+ irq);
+
+ context_append(&ctx->context, d, z, header_z);
+
+ return 0;
+}
+
+static int
+ohci_queue_iso_receive_dualbuffer(struct fw_iso_context *base,
+ struct fw_iso_packet *packet,
+ struct fw_iso_buffer *buffer,
+ unsigned long payload)
+{
+ struct iso_context *ctx = container_of(base, struct iso_context, base);
+ struct db_descriptor *db = NULL;
+ struct descriptor *d;
+ struct fw_iso_packet *p;
+ dma_addr_t d_bus, page_bus;
+ u32 z, header_z, length, rest;
+ int page, offset, packet_count, header_size;
+
+ /* FIXME: Cycle lost behavior should be configurable: lose
+ * packet, retransmit or terminate.. */
+
+ if (packet->skip) {
+ d = context_get_descriptors(&ctx->context, 2, &d_bus);
+ if (d == NULL)
+ return -ENOMEM;
+
+ db = (struct db_descriptor *) d;
+ db->control = cpu_to_le16(descriptor_status |
+ descriptor_branch_always |
+ descriptor_wait);
+ db->first_size = cpu_to_le16(ctx->base.header_size + 4);
+ context_append(&ctx->context, d, 2, 0);
+ }
+
+ p = packet;
+ z = 2;
+
+ /* The OHCI controller puts the status word in the header
+ * buffer too, so we need 4 extra bytes per packet. */
+ packet_count = p->header_length / ctx->base.header_size;
+ header_size = packet_count * (ctx->base.header_size + 4);
+
+ /* Get header size in number of descriptors. */
+ header_z = DIV_ROUND_UP(header_size, sizeof *d);
+ page = payload >> PAGE_SHIFT;
+ offset = payload & ~PAGE_MASK;
+ rest = p->payload_length;
+
+ /* FIXME: OHCI 1.0 doesn't support dual buffer receive */
+ /* FIXME: make packet-per-buffer/dual-buffer a context option */
+ while (rest > 0) {
+ d = context_get_descriptors(&ctx->context,
+ z + header_z, &d_bus);
+ if (d == NULL)
+ return -ENOMEM;
+
+ db = (struct db_descriptor *) d;
+ db->control = cpu_to_le16(descriptor_status |
+ descriptor_branch_always);
+ db->first_size = cpu_to_le16(ctx->base.header_size + 4);
+ db->first_req_count = cpu_to_le16(header_size);
+ db->first_res_count = db->first_req_count;
+ db->first_buffer = cpu_to_le32(d_bus + sizeof *db);
+
+ if (offset + rest < PAGE_SIZE)
+ length = rest;
+ else
+ length = PAGE_SIZE - offset;
+
+ db->second_req_count = cpu_to_le16(length);
+ db->second_res_count = db->second_req_count;
+ page_bus = page_private(buffer->pages[page]);
+ db->second_buffer = cpu_to_le32(page_bus + offset);
+
+ if (p->interrupt && length == rest)
+ db->control |= cpu_to_le16(descriptor_irq_always);
+
+ context_append(&ctx->context, d, z, header_z);
+ offset = (offset + length) & ~PAGE_MASK;
+ rest -= length;
+ page++;
+ }
+
+ return 0;
+}
+
+static int
+ohci_queue_iso(struct fw_iso_context *base,
+ struct fw_iso_packet *packet,
+ struct fw_iso_buffer *buffer,
+ unsigned long payload)
+{
+ struct iso_context *ctx = container_of(base, struct iso_context, base);
+
+ if (base->type == FW_ISO_CONTEXT_TRANSMIT)
+ return ohci_queue_iso_transmit(base, packet, buffer, payload);
+ else if (ctx->context.ohci->version >= OHCI_VERSION_1_1)
+ return ohci_queue_iso_receive_dualbuffer(base, packet,
+ buffer, payload);
+ else
+ /* FIXME: Implement fallback for OHCI 1.0 controllers. */
+ return -EINVAL;
+}
+
+static const struct fw_card_driver ohci_driver = {
+ .name = ohci_driver_name,
+ .enable = ohci_enable,
+ .update_phy_reg = ohci_update_phy_reg,
+ .set_config_rom = ohci_set_config_rom,
+ .send_request = ohci_send_request,
+ .send_response = ohci_send_response,
+ .cancel_packet = ohci_cancel_packet,
+ .enable_phys_dma = ohci_enable_phys_dma,
+ .get_bus_time = ohci_get_bus_time,
+
+ .allocate_iso_context = ohci_allocate_iso_context,
+ .free_iso_context = ohci_free_iso_context,
+ .queue_iso = ohci_queue_iso,
+ .start_iso = ohci_start_iso,
+ .stop_iso = ohci_stop_iso,
+};
+
+static int software_reset(struct fw_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) == 0)
+ return 0;
+ msleep(1);
+ }
+
+ return -EBUSY;
+}
+
+/* ---------- pci subsystem interface ---------- */
+
+enum {
+ CLEANUP_SELF_ID,
+ CLEANUP_REGISTERS,
+ CLEANUP_IOMEM,
+ CLEANUP_DISABLE,
+ CLEANUP_PUT_CARD,
+};
+
+static int cleanup(struct fw_ohci *ohci, int stage, int code)
+{
+ struct pci_dev *dev = to_pci_dev(ohci->card.device);
+
+ switch (stage) {
+ case CLEANUP_SELF_ID:
+ dma_free_coherent(ohci->card.device, SELF_ID_BUF_SIZE,
+ ohci->self_id_cpu, ohci->self_id_bus);
+ case CLEANUP_REGISTERS:
+ kfree(ohci->it_context_list);
+ kfree(ohci->ir_context_list);
+ pci_iounmap(dev, ohci->registers);
+ case CLEANUP_IOMEM:
+ pci_release_region(dev, 0);
+ case CLEANUP_DISABLE:
+ pci_disable_device(dev);
+ case CLEANUP_PUT_CARD:
+ fw_card_put(&ohci->card);
+ }
+
+ return code;
+}
+
+static int __devinit
+pci_probe(struct pci_dev *dev, const struct pci_device_id *ent)
+{
+ struct fw_ohci *ohci;
+ u32 bus_options, max_receive, link_speed;
+ u64 guid;
+ int error_code;
+ size_t size;
+
+ ohci = kzalloc(sizeof *ohci, GFP_KERNEL);
+ if (ohci == NULL) {
+ fw_error("Could not malloc fw_ohci data.\n");
+ return -ENOMEM;
+ }
+
+ fw_card_initialize(&ohci->card, &ohci_driver, &dev->dev);
+
+ if (pci_enable_device(dev)) {
+ fw_error("Failed to enable OHCI hardware.\n");
+ return cleanup(ohci, CLEANUP_PUT_CARD, -ENODEV);
+ }
+
+ pci_set_master(dev);
+ pci_write_config_dword(dev, OHCI1394_PCI_HCI_Control, 0);
+ pci_set_drvdata(dev, ohci);
+
+ spin_lock_init(&ohci->lock);
+
+ tasklet_init(&ohci->bus_reset_tasklet,
+ bus_reset_tasklet, (unsigned long)ohci);
+
+ if (pci_request_region(dev, 0, ohci_driver_name)) {
+ fw_error("MMIO resource unavailable\n");
+ return cleanup(ohci, CLEANUP_DISABLE, -EBUSY);
+ }
+
+ ohci->registers = pci_iomap(dev, 0, OHCI1394_REGISTER_SIZE);
+ if (ohci->registers == NULL) {
+ fw_error("Failed to remap registers\n");
+ return cleanup(ohci, CLEANUP_IOMEM, -ENXIO);
+ }
+
+ if (software_reset(ohci)) {
+ fw_error("Failed to reset ohci card.\n");
+ return cleanup(ohci, CLEANUP_REGISTERS, -EBUSY);
+ }
+
+ /* 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 |
+ OHCI1394_HCControl_postedWriteEnable);
+ flush_writes(ohci);
+ msleep(50);
+
+ reg_write(ohci, OHCI1394_HCControlClear,
+ OHCI1394_HCControl_noByteSwapData);
+
+ reg_write(ohci, OHCI1394_LinkControlSet,
+ OHCI1394_LinkControl_rcvSelfID |
+ OHCI1394_LinkControl_cycleTimerEnable |
+ OHCI1394_LinkControl_cycleMaster);
+
+ ar_context_init(&ohci->ar_request_ctx, ohci,
+ OHCI1394_AsReqRcvContextControlSet);
+
+ ar_context_init(&ohci->ar_response_ctx, ohci,
+ OHCI1394_AsRspRcvContextControlSet);
+
+ context_init(&ohci->at_request_ctx, ohci, AT_BUFFER_SIZE,
+ OHCI1394_AsReqTrContextControlSet, handle_at_packet);
+
+ context_init(&ohci->at_response_ctx, ohci, AT_BUFFER_SIZE,
+ OHCI1394_AsRspTrContextControlSet, handle_at_packet);
+
+ reg_write(ohci, OHCI1394_ATRetries,
+ OHCI1394_MAX_AT_REQ_RETRIES |
+ (OHCI1394_MAX_AT_RESP_RETRIES << 4) |
+ (OHCI1394_MAX_PHYS_RESP_RETRIES << 8));
+
+ reg_write(ohci, OHCI1394_IsoRecvIntMaskSet, ~0);
+ ohci->it_context_mask = reg_read(ohci, OHCI1394_IsoRecvIntMaskSet);
+ reg_write(ohci, OHCI1394_IsoRecvIntMaskClear, ~0);
+ size = sizeof(struct iso_context) * hweight32(ohci->it_context_mask);
+ ohci->it_context_list = kzalloc(size, GFP_KERNEL);
+
+ reg_write(ohci, OHCI1394_IsoXmitIntMaskSet, ~0);
+ ohci->ir_context_mask = reg_read(ohci, OHCI1394_IsoXmitIntMaskSet);
+ reg_write(ohci, OHCI1394_IsoXmitIntMaskClear, ~0);
+ size = sizeof(struct iso_context) * hweight32(ohci->ir_context_mask);
+ ohci->ir_context_list = kzalloc(size, GFP_KERNEL);
+
+ if (ohci->it_context_list == NULL || ohci->ir_context_list == NULL) {
+ fw_error("Out of memory for it/ir contexts.\n");
+ return cleanup(ohci, CLEANUP_REGISTERS, -ENOMEM);
+ }
+
+ /* self-id dma buffer allocation */
+ ohci->self_id_cpu = dma_alloc_coherent(ohci->card.device,
+ SELF_ID_BUF_SIZE,
+ &ohci->self_id_bus,
+ GFP_KERNEL);
+ if (ohci->self_id_cpu == NULL) {
+ fw_error("Out of memory for self ID buffer.\n");
+ return cleanup(ohci, CLEANUP_REGISTERS, -ENOMEM);
+ }
+
+ reg_write(ohci, OHCI1394_SelfIDBuffer, ohci->self_id_bus);
+ reg_write(ohci, OHCI1394_PhyUpperBound, 0x00010000);
+ reg_write(ohci, OHCI1394_IntEventClear, ~0);
+ reg_write(ohci, OHCI1394_IntMaskClear, ~0);
+ reg_write(ohci, OHCI1394_IntMaskSet,
+ OHCI1394_selfIDComplete |
+ OHCI1394_RQPkt | OHCI1394_RSPkt |
+ OHCI1394_reqTxComplete | OHCI1394_respTxComplete |
+ OHCI1394_isochRx | OHCI1394_isochTx |
+ OHCI1394_masterIntEnable |
+ OHCI1394_cycle64Seconds);
+
+ bus_options = reg_read(ohci, OHCI1394_BusOptions);
+ max_receive = (bus_options >> 12) & 0xf;
+ link_speed = bus_options & 0x7;
+ guid = ((u64) reg_read(ohci, OHCI1394_GUIDHi) << 32) |
+ reg_read(ohci, OHCI1394_GUIDLo);
+
+ error_code = fw_card_add(&ohci->card, max_receive, link_speed, guid);
+ if (error_code < 0)
+ return cleanup(ohci, CLEANUP_SELF_ID, error_code);
+
+ ohci->version = reg_read(ohci, OHCI1394_Version) & 0x00ff00ff;
+ fw_notify("Added fw-ohci device %s, OHCI version %x.%x\n",
+ dev->dev.bus_id, ohci->version >> 16, ohci->version & 0xff);
+
+ return 0;
+}
+
+static void pci_remove(struct pci_dev *dev)
+{
+ struct fw_ohci *ohci;
+
+ ohci = pci_get_drvdata(dev);
+ reg_write(ohci, OHCI1394_IntMaskClear, ~0);
+ flush_writes(ohci);
+ fw_core_remove_card(&ohci->card);
+
+ /* FIXME: Fail all pending packets here, now that the upper
+ * layers can't queue any more. */
+
+ software_reset(ohci);
+ free_irq(dev->irq, ohci);
+ cleanup(ohci, CLEANUP_SELF_ID, 0);
+
+ fw_notify("Removed fw-ohci device.\n");
+}
+
+static struct pci_device_id pci_table[] = {
+ { PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_FIREWIRE_OHCI, ~0) },
+ { }
+};
+
+MODULE_DEVICE_TABLE(pci, pci_table);
+
+static struct pci_driver fw_ohci_pci_driver = {
+ .name = ohci_driver_name,
+ .id_table = pci_table,
+ .probe = pci_probe,
+ .remove = pci_remove,
+};
+
+MODULE_AUTHOR("Kristian Hoegsberg <[email protected]>");
+MODULE_DESCRIPTION("Driver for PCI OHCI IEEE1394 controllers");
+MODULE_LICENSE("GPL");
+
+static int __init fw_ohci_init(void)
+{
+ return pci_register_driver(&fw_ohci_pci_driver);
+}
+
+static void __exit fw_ohci_cleanup(void)
+{
+ pci_unregister_driver(&fw_ohci_pci_driver);
+}
+
+module_init(fw_ohci_init);
+module_exit(fw_ohci_cleanup);
--
Stefan Richter
-=====-=-=== -=-= ---=-
http://arcgraph.de/sr/
Signed-off-by: Stefan Richter <[email protected]>
---
drivers/firewire/fw-sbp2.c | 1165 +++++++++++++++++++++++++++++++++++++
1 file changed, 1165 insertions(+)
Index: linux_juju/drivers/firewire/fw-sbp2.c
===================================================================
--- /dev/null
+++ linux_juju/drivers/firewire/fw-sbp2.c
@@ -0,0 +1,1165 @@
+/* -*- c-basic-offset: 8 -*-
+ * fw-spb2.c -- SBP2 driver (SCSI over IEEE1394)
+ *
+ * Copyright (C) 2005-2007 Kristian Hoegsberg <[email protected]>
+ *
+ * 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.
+ */
+
+/* The basic structure of this driver is based the old storage driver,
+ * drivers/ieee1394/sbp2.c, originally written by
+ * James Goodwin <[email protected]>
+ * with later contributions and ongoing maintenance from
+ * Ben Collins <[email protected]>,
+ * Stefan Richter <[email protected]>
+ * and many others.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/device.h>
+#include <linux/scatterlist.h>
+#include <linux/dma-mapping.h>
+#include <linux/timer.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 "fw-transaction.h"
+#include "fw-topology.h"
+#include "fw-device.h"
+
+/* I don't know why the SCSI stack doesn't define something like this... */
+typedef void (*scsi_done_fn_t) (struct scsi_cmnd *);
+
+static const char sbp2_driver_name[] = "sbp2";
+
+struct sbp2_device {
+ struct kref kref;
+ struct fw_unit *unit;
+ struct fw_address_handler address_handler;
+ struct list_head orb_list;
+ u64 management_agent_address;
+ u64 command_block_agent_address;
+ u32 workarounds;
+ int login_id;
+
+ /* We cache these addresses and only update them once we've
+ * logged in or reconnected to the sbp2 device. That way, any
+ * IO to the device will automatically fail and get retried if
+ * it happens in a window where the device is not ready to
+ * handle it (e.g. after a bus reset but before we reconnect). */
+ int node_id;
+ int address_high;
+ int generation;
+
+ int retries;
+ struct delayed_work work;
+ struct Scsi_Host *scsi_host;
+};
+
+#define SBP2_MAX_SG_ELEMENT_LENGTH 0xf000
+#define SBP2_MAX_SECTORS 255 /* Max sectors supported */
+#define SBP2_ORB_TIMEOUT 2000 /* Timeout in ms */
+
+#define SBP2_ORB_NULL 0x80000000
+
+#define SBP2_DIRECTION_TO_MEDIA 0x0
+#define SBP2_DIRECTION_FROM_MEDIA 0x1
+
+/* Unit directory keys */
+#define SBP2_COMMAND_SET_SPECIFIER 0x38
+#define SBP2_COMMAND_SET 0x39
+#define SBP2_COMMAND_SET_REVISION 0x3b
+#define SBP2_FIRMWARE_REVISION 0x3c
+
+/* Flags for detected oddities and brokeness */
+#define SBP2_WORKAROUND_128K_MAX_TRANS 0x1
+#define SBP2_WORKAROUND_INQUIRY_36 0x2
+#define SBP2_WORKAROUND_MODE_SENSE_8 0x4
+#define SBP2_WORKAROUND_FIX_CAPACITY 0x8
+#define SBP2_WORKAROUND_OVERRIDE 0x100
+
+/* Management orb opcodes */
+#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
+
+/* Offsets for command block agent registers */
+#define SBP2_AGENT_STATE 0x00
+#define SBP2_AGENT_RESET 0x04
+#define SBP2_ORB_POINTER 0x08
+#define SBP2_DOORBELL 0x10
+#define SBP2_UNSOLICITED_STATUS_ENABLE 0x14
+
+/* Status write response codes */
+#define SBP2_STATUS_REQUEST_COMPLETE 0x0
+#define SBP2_STATUS_TRANSPORT_FAILURE 0x1
+#define SBP2_STATUS_ILLEGAL_REQUEST 0x2
+#define SBP2_STATUS_VENDOR_DEPENDENT 0x3
+
+#define status_get_orb_high(v) ((v).status & 0xffff)
+#define status_get_sbp_status(v) (((v).status >> 16) & 0xff)
+#define status_get_len(v) (((v).status >> 24) & 0x07)
+#define status_get_dead(v) (((v).status >> 27) & 0x01)
+#define status_get_response(v) (((v).status >> 28) & 0x03)
+#define status_get_source(v) (((v).status >> 30) & 0x03)
+#define status_get_orb_low(v) ((v).orb_low)
+#define status_get_data(v) ((v).data)
+
+struct sbp2_status {
+ u32 status;
+ u32 orb_low;
+ u8 data[24];
+};
+
+struct sbp2_pointer {
+ u32 high;
+ u32 low;
+};
+
+struct sbp2_orb {
+ struct fw_transaction t;
+ dma_addr_t request_bus;
+ int rcode;
+ struct sbp2_pointer pointer;
+ void (*callback) (struct sbp2_orb * orb, struct sbp2_status * status);
+ struct list_head link;
+};
+
+#define management_orb_lun(v) ((v))
+#define management_orb_function(v) ((v) << 16)
+#define management_orb_reconnect(v) ((v) << 20)
+#define management_orb_exclusive ((1) << 28)
+#define management_orb_request_format(v) ((v) << 29)
+#define management_orb_notify ((1) << 31)
+
+#define management_orb_response_length(v) ((v))
+#define management_orb_password_length(v) ((v) << 16)
+
+struct sbp2_management_orb {
+ struct sbp2_orb base;
+ struct {
+ struct sbp2_pointer password;
+ struct sbp2_pointer response;
+ u32 misc;
+ u32 length;
+ struct sbp2_pointer status_fifo;
+ } request;
+ __be32 response[4];
+ dma_addr_t response_bus;
+ struct completion done;
+ struct sbp2_status status;
+};
+
+#define login_response_get_login_id(v) ((v).misc & 0xffff)
+#define login_response_get_length(v) (((v).misc >> 16) & 0xffff)
+
+struct sbp2_login_response {
+ u32 misc;
+ struct sbp2_pointer command_block_agent;
+ u32 reconnect_hold;
+};
+
+#define command_orb_data_size(v) ((v))
+#define command_orb_page_size(v) ((v) << 16)
+#define command_orb_page_table_present ((1) << 19)
+#define command_orb_max_payload(v) ((v) << 20)
+#define command_orb_speed(v) ((v) << 24)
+#define command_orb_direction(v) ((v) << 27)
+#define command_orb_request_format(v) ((v) << 29)
+#define command_orb_notify ((1) << 31)
+
+struct sbp2_command_orb {
+ struct sbp2_orb base;
+ struct {
+ struct sbp2_pointer next;
+ struct sbp2_pointer data_descriptor;
+ u32 misc;
+ u8 command_block[12];
+ } request;
+ struct scsi_cmnd *cmd;
+ scsi_done_fn_t done;
+ struct fw_unit *unit;
+
+ struct sbp2_pointer page_table[SG_ALL];
+ dma_addr_t page_table_bus;
+ dma_addr_t request_buffer_bus;
+};
+
+/*
+ * List of devices with known bugs.
+ *
+ * The firmware_revision field, masked with 0xffff00, is the best
+ * indicator for the type of bridge chip of a device. It yields a few
+ * false positives but this did not break correctly behaving devices
+ * so far. We use ~0 as a wildcard, since the 24 bit values we get
+ * from the config rom can never match that.
+ */
+static const struct {
+ u32 firmware_revision;
+ u32 model;
+ unsigned workarounds;
+} sbp2_workarounds_table[] = {
+ /* DViCO Momobay CX-1 with TSB42AA9 bridge */ {
+ .firmware_revision = 0x002800,
+ .model = 0x001010,
+ .workarounds = SBP2_WORKAROUND_INQUIRY_36 |
+ SBP2_WORKAROUND_MODE_SENSE_8,
+ },
+ /* Initio bridges, actually only needed for some older ones */ {
+ .firmware_revision = 0x000200,
+ .model = ~0,
+ .workarounds = SBP2_WORKAROUND_INQUIRY_36,
+ },
+ /* Symbios bridge */ {
+ .firmware_revision = 0xa0b800,
+ .model = ~0,
+ .workarounds = SBP2_WORKAROUND_128K_MAX_TRANS,
+ },
+ /* There are iPods (2nd gen, 3rd gen) with model_id == 0, but
+ * these iPods do not feature the read_capacity bug according
+ * to one report. Read_capacity behaviour as well as model_id
+ * could change due to Apple-supplied firmware updates though. */
+ /* iPod 4th generation. */ {
+ .firmware_revision = 0x0a2700,
+ .model = 0x000021,
+ .workarounds = SBP2_WORKAROUND_FIX_CAPACITY,
+ },
+ /* iPod mini */ {
+ .firmware_revision = 0x0a2700,
+ .model = 0x000023,
+ .workarounds = SBP2_WORKAROUND_FIX_CAPACITY,
+ },
+ /* iPod Photo */ {
+ .firmware_revision = 0x0a2700,
+ .model = 0x00007e,
+ .workarounds = SBP2_WORKAROUND_FIX_CAPACITY,
+ }
+};
+
+static void
+sbp2_status_write(struct fw_card *card, struct fw_request *request,
+ int tcode, int destination, int source,
+ int generation, int speed,
+ unsigned long long offset,
+ void *payload, size_t length, void *callback_data)
+{
+ struct sbp2_device *sd = callback_data;
+ struct sbp2_orb *orb;
+ struct sbp2_status status;
+ size_t header_size;
+ unsigned long flags;
+
+ if (tcode != TCODE_WRITE_BLOCK_REQUEST ||
+ length == 0 || length > sizeof status) {
+ fw_send_response(card, request, RCODE_TYPE_ERROR);
+ return;
+ }
+
+ header_size = min(length, 2 * sizeof(u32));
+ fw_memcpy_from_be32(&status, payload, header_size);
+ if (length > header_size)
+ memcpy(status.data, payload + 8, length - header_size);
+ if (status_get_source(status) == 2 || status_get_source(status) == 3) {
+ fw_notify("non-orb related status write, not handled\n");
+ fw_send_response(card, request, RCODE_COMPLETE);
+ return;
+ }
+
+ /* Lookup the orb corresponding to this status write. */
+ spin_lock_irqsave(&card->lock, flags);
+ list_for_each_entry(orb, &sd->orb_list, link) {
+ if (status_get_orb_high(status) == 0 &&
+ status_get_orb_low(status) == orb->request_bus &&
+ orb->rcode == RCODE_COMPLETE) {
+ list_del(&orb->link);
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&card->lock, flags);
+
+ if (&orb->link != &sd->orb_list)
+ orb->callback(orb, &status);
+ else
+ fw_error("status write for unknown orb\n");
+
+ fw_send_response(card, request, RCODE_COMPLETE);
+}
+
+static void
+complete_transaction(struct fw_card *card, int rcode,
+ void *payload, size_t length, void *data)
+{
+ struct sbp2_orb *orb = data;
+ unsigned long flags;
+
+ orb->rcode = rcode;
+ if (rcode != RCODE_COMPLETE) {
+ spin_lock_irqsave(&card->lock, flags);
+ list_del(&orb->link);
+ spin_unlock_irqrestore(&card->lock, flags);
+ orb->callback(orb, NULL);
+ }
+}
+
+static void
+sbp2_send_orb(struct sbp2_orb *orb, struct fw_unit *unit,
+ int node_id, int generation, u64 offset)
+{
+ struct fw_device *device = fw_device(unit->device.parent);
+ struct sbp2_device *sd = unit->device.driver_data;
+ unsigned long flags;
+
+ orb->pointer.high = 0;
+ orb->pointer.low = orb->request_bus;
+ fw_memcpy_to_be32(&orb->pointer, &orb->pointer, sizeof orb->pointer);
+
+ spin_lock_irqsave(&device->card->lock, flags);
+ list_add_tail(&orb->link, &sd->orb_list);
+ spin_unlock_irqrestore(&device->card->lock, flags);
+
+ fw_send_request(device->card, &orb->t, TCODE_WRITE_BLOCK_REQUEST,
+ node_id, generation,
+ device->node->max_speed, offset,
+ &orb->pointer, sizeof orb->pointer,
+ complete_transaction, orb);
+}
+
+static int sbp2_cancel_orbs(struct fw_unit *unit)
+{
+ struct fw_device *device = fw_device(unit->device.parent);
+ struct sbp2_device *sd = unit->device.driver_data;
+ struct sbp2_orb *orb, *next;
+ struct list_head list;
+ unsigned long flags;
+ int retval = -ENOENT;
+
+ INIT_LIST_HEAD(&list);
+ spin_lock_irqsave(&device->card->lock, flags);
+ list_splice_init(&sd->orb_list, &list);
+ spin_unlock_irqrestore(&device->card->lock, flags);
+
+ list_for_each_entry_safe(orb, next, &list, link) {
+ retval = 0;
+ if (fw_cancel_transaction(device->card, &orb->t) == 0)
+ continue;
+
+ orb->rcode = RCODE_CANCELLED;
+ orb->callback(orb, NULL);
+ }
+
+ return retval;
+}
+
+static void
+complete_management_orb(struct sbp2_orb *base_orb, struct sbp2_status *status)
+{
+ struct sbp2_management_orb *orb =
+ (struct sbp2_management_orb *)base_orb;
+
+ if (status)
+ memcpy(&orb->status, status, sizeof *status);
+ complete(&orb->done);
+}
+
+static int
+sbp2_send_management_orb(struct fw_unit *unit, int node_id, int generation,
+ int function, int lun, void *response)
+{
+ struct fw_device *device = fw_device(unit->device.parent);
+ struct sbp2_device *sd = unit->device.driver_data;
+ struct sbp2_management_orb *orb;
+ int retval = -ENOMEM;
+
+ orb = kzalloc(sizeof *orb, GFP_ATOMIC);
+ if (orb == NULL)
+ return -ENOMEM;
+
+ /* The sbp2 device is going to send a block read request to
+ * read out the request from host memory, so map it for
+ * dma. */
+ orb->base.request_bus =
+ dma_map_single(device->card->device, &orb->request,
+ sizeof orb->request, DMA_TO_DEVICE);
+ if (dma_mapping_error(orb->base.request_bus))
+ goto out;
+
+ orb->response_bus =
+ dma_map_single(device->card->device, &orb->response,
+ sizeof orb->response, DMA_FROM_DEVICE);
+ if (dma_mapping_error(orb->response_bus))
+ goto out;
+
+ orb->request.response.high = 0;
+ orb->request.response.low = orb->response_bus;
+
+ orb->request.misc =
+ management_orb_notify |
+ management_orb_function(function) |
+ management_orb_lun(lun);
+ orb->request.length =
+ management_orb_response_length(sizeof orb->response);
+
+ orb->request.status_fifo.high = sd->address_handler.offset >> 32;
+ orb->request.status_fifo.low = sd->address_handler.offset;
+
+ /* FIXME: Yeah, ok this isn't elegant, we hardwire exclusive
+ * login and 1 second reconnect time. The reconnect setting
+ * is probably fine, but the exclusive login should be an
+ * option. */
+ if (function == SBP2_LOGIN_REQUEST) {
+ orb->request.misc |=
+ management_orb_exclusive |
+ management_orb_reconnect(0);
+ }
+
+ fw_memcpy_to_be32(&orb->request, &orb->request, sizeof orb->request);
+
+ init_completion(&orb->done);
+ orb->base.callback = complete_management_orb;
+
+ sbp2_send_orb(&orb->base, unit,
+ node_id, generation, sd->management_agent_address);
+
+ wait_for_completion_timeout(&orb->done,
+ msecs_to_jiffies(SBP2_ORB_TIMEOUT));
+
+ retval = -EIO;
+ if (sbp2_cancel_orbs(unit) == 0) {
+ fw_error("orb reply timed out, rcode=0x%02x\n",
+ orb->base.rcode);
+ goto out;
+ }
+
+ if (orb->base.rcode != RCODE_COMPLETE) {
+ fw_error("management write failed, rcode 0x%02x\n",
+ orb->base.rcode);
+ goto out;
+ }
+
+ if (status_get_response(orb->status) != 0 ||
+ status_get_sbp_status(orb->status) != 0) {
+ fw_error("error status: %d:%d\n",
+ status_get_response(orb->status),
+ status_get_sbp_status(orb->status));
+ goto out;
+ }
+
+ retval = 0;
+ out:
+ dma_unmap_single(device->card->device, orb->base.request_bus,
+ sizeof orb->request, DMA_TO_DEVICE);
+ dma_unmap_single(device->card->device, orb->response_bus,
+ sizeof orb->response, DMA_FROM_DEVICE);
+
+ if (response)
+ fw_memcpy_from_be32(response,
+ orb->response, sizeof orb->response);
+ kfree(orb);
+
+ return retval;
+}
+
+static void
+complete_agent_reset_write(struct fw_card *card, int rcode,
+ void *payload, size_t length, void *data)
+{
+ struct fw_transaction *t = data;
+
+ kfree(t);
+}
+
+static int sbp2_agent_reset(struct fw_unit *unit)
+{
+ struct fw_device *device = fw_device(unit->device.parent);
+ struct sbp2_device *sd = unit->device.driver_data;
+ struct fw_transaction *t;
+ static u32 zero;
+
+ t = kzalloc(sizeof *t, GFP_ATOMIC);
+ if (t == NULL)
+ return -ENOMEM;
+
+ fw_send_request(device->card, t, TCODE_WRITE_QUADLET_REQUEST,
+ sd->node_id, sd->generation, SCODE_400,
+ sd->command_block_agent_address + SBP2_AGENT_RESET,
+ &zero, sizeof zero, complete_agent_reset_write, t);
+
+ return 0;
+}
+
+static int add_scsi_devices(struct fw_unit *unit);
+static void remove_scsi_devices(struct fw_unit *unit);
+static void sbp2_reconnect(struct work_struct *work);
+
+static void
+release_sbp2_device(struct kref *kref)
+{
+ struct sbp2_device *sd = container_of(kref, struct sbp2_device, kref);
+
+ sbp2_send_management_orb(sd->unit, sd->node_id, sd->generation,
+ SBP2_LOGOUT_REQUEST, sd->login_id, NULL);
+
+ remove_scsi_devices(sd->unit);
+
+ fw_core_remove_address_handler(&sd->address_handler);
+ fw_notify("removed sbp2 unit %s\n", sd->unit->device.bus_id);
+ put_device(&sd->unit->device);
+ kfree(sd);
+}
+
+static void sbp2_login(struct work_struct *work)
+{
+ struct sbp2_device *sd =
+ container_of(work, struct sbp2_device, work.work);
+ struct fw_unit *unit = sd->unit;
+ struct fw_device *device = fw_device(unit->device.parent);
+ struct sbp2_login_response response;
+ int generation, node_id, local_node_id, lun, retval;
+
+ /* FIXME: Make this work for multi-lun devices. */
+ lun = 0;
+
+ generation = device->card->generation;
+ node_id = device->node->node_id;
+ local_node_id = device->card->local_node->node_id;
+
+ if (sbp2_send_management_orb(unit, node_id, generation,
+ SBP2_LOGIN_REQUEST, lun, &response) < 0) {
+ if (sd->retries++ < 5) {
+ schedule_delayed_work(&sd->work, DIV_ROUND_UP(HZ, 5));
+ } else {
+ fw_error("failed to login to %s\n",
+ unit->device.bus_id);
+ remove_scsi_devices(unit);
+ kref_put(&sd->kref, release_sbp2_device);
+ }
+ return;
+ }
+
+ sd->generation = generation;
+ sd->node_id = node_id;
+ sd->address_high = local_node_id << 16;
+
+ /* Get command block agent offset and login id. */
+ sd->command_block_agent_address =
+ ((u64) (response.command_block_agent.high & 0xffff) << 32) |
+ response.command_block_agent.low;
+ sd->login_id = login_response_get_login_id(response);
+
+ fw_notify("logged in to sbp2 unit %s (%d retries)\n",
+ unit->device.bus_id, sd->retries);
+ fw_notify(" - management_agent_address: 0x%012llx\n",
+ (unsigned long long) sd->management_agent_address);
+ fw_notify(" - command_block_agent_address: 0x%012llx\n",
+ (unsigned long long) sd->command_block_agent_address);
+ fw_notify(" - status write address: 0x%012llx\n",
+ (unsigned long long) sd->address_handler.offset);
+
+#if 0
+ /* FIXME: The linux1394 sbp2 does this last step. */
+ sbp2_set_busy_timeout(scsi_id);
+#endif
+
+ PREPARE_DELAYED_WORK(&sd->work, sbp2_reconnect);
+ sbp2_agent_reset(unit);
+
+ retval = add_scsi_devices(unit);
+ if (retval < 0) {
+ sbp2_send_management_orb(unit, sd->node_id, sd->generation,
+ SBP2_LOGOUT_REQUEST, sd->login_id,
+ NULL);
+ /* Set this back to sbp2_login so we fall back and
+ * retry login on bus reset. */
+ PREPARE_DELAYED_WORK(&sd->work, sbp2_login);
+ }
+ kref_put(&sd->kref, release_sbp2_device);
+}
+
+static int sbp2_probe(struct device *dev)
+{
+ struct fw_unit *unit = fw_unit(dev);
+ struct fw_device *device = fw_device(unit->device.parent);
+ struct sbp2_device *sd;
+ struct fw_csr_iterator ci;
+ int i, key, value;
+ u32 model, firmware_revision;
+
+ sd = kzalloc(sizeof *sd, GFP_KERNEL);
+ if (sd == NULL)
+ return -ENOMEM;
+
+ unit->device.driver_data = sd;
+ sd->unit = unit;
+ INIT_LIST_HEAD(&sd->orb_list);
+ kref_init(&sd->kref);
+
+ sd->address_handler.length = 0x100;
+ sd->address_handler.address_callback = sbp2_status_write;
+ sd->address_handler.callback_data = sd;
+
+ if (fw_core_add_address_handler(&sd->address_handler,
+ &fw_high_memory_region) < 0) {
+ kfree(sd);
+ return -EBUSY;
+ }
+
+ if (fw_device_enable_phys_dma(device) < 0) {
+ fw_core_remove_address_handler(&sd->address_handler);
+ kfree(sd);
+ return -EBUSY;
+ }
+
+ /* Scan unit directory to get management agent address,
+ * firmware revison and model. Initialize firmware_revision
+ * and model to values that wont match anything in our table. */
+ firmware_revision = 0xff000000;
+ model = 0xff000000;
+ fw_csr_iterator_init(&ci, unit->directory);
+ while (fw_csr_iterator_next(&ci, &key, &value)) {
+ switch (key) {
+ case CSR_DEPENDENT_INFO | CSR_OFFSET:
+ sd->management_agent_address =
+ 0xfffff0000000ULL + 4 * value;
+ break;
+ case SBP2_FIRMWARE_REVISION:
+ firmware_revision = value;
+ break;
+ case CSR_MODEL:
+ model = value;
+ break;
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(sbp2_workarounds_table); i++) {
+ if (sbp2_workarounds_table[i].firmware_revision !=
+ (firmware_revision & 0xffffff00))
+ continue;
+ if (sbp2_workarounds_table[i].model != model &&
+ sbp2_workarounds_table[i].model != ~0)
+ continue;
+ sd->workarounds |= sbp2_workarounds_table[i].workarounds;
+ break;
+ }
+
+ if (sd->workarounds)
+ fw_notify("Workarounds for node %s: 0x%x "
+ "(firmware_revision 0x%06x, model_id 0x%06x)\n",
+ unit->device.bus_id,
+ sd->workarounds, firmware_revision, model);
+
+ get_device(&unit->device);
+
+ /* We schedule work to do the login so we can easily
+ * reschedule retries. Always get the ref before scheduling
+ * work.*/
+ INIT_DELAYED_WORK(&sd->work, sbp2_login);
+ if (schedule_delayed_work(&sd->work, 0))
+ kref_get(&sd->kref);
+
+ return 0;
+}
+
+static int sbp2_remove(struct device *dev)
+{
+ struct fw_unit *unit = fw_unit(dev);
+ struct sbp2_device *sd = unit->device.driver_data;
+
+ kref_put(&sd->kref, release_sbp2_device);
+
+ return 0;
+}
+
+static void sbp2_reconnect(struct work_struct *work)
+{
+ struct sbp2_device *sd =
+ container_of(work, struct sbp2_device, work.work);
+ struct fw_unit *unit = sd->unit;
+ struct fw_device *device = fw_device(unit->device.parent);
+ int generation, node_id, local_node_id;
+
+ generation = device->card->generation;
+ node_id = device->node->node_id;
+ local_node_id = device->card->local_node->node_id;
+
+ if (sbp2_send_management_orb(unit, node_id, generation,
+ SBP2_RECONNECT_REQUEST,
+ sd->login_id, NULL) < 0) {
+ if (sd->retries++ >= 5) {
+ fw_error("failed to reconnect to %s\n",
+ unit->device.bus_id);
+ /* Fall back and try to log in again. */
+ sd->retries = 0;
+ PREPARE_DELAYED_WORK(&sd->work, sbp2_login);
+ }
+ schedule_delayed_work(&sd->work, DIV_ROUND_UP(HZ, 5));
+ return;
+ }
+
+ sd->generation = generation;
+ sd->node_id = node_id;
+ sd->address_high = local_node_id << 16;
+
+ fw_notify("reconnected to unit %s (%d retries)\n",
+ unit->device.bus_id, sd->retries);
+ sbp2_agent_reset(unit);
+ sbp2_cancel_orbs(unit);
+ kref_put(&sd->kref, release_sbp2_device);
+}
+
+static void sbp2_update(struct fw_unit *unit)
+{
+ struct fw_device *device = fw_device(unit->device.parent);
+ struct sbp2_device *sd = unit->device.driver_data;
+
+ sd->retries = 0;
+ fw_device_enable_phys_dma(device);
+ if (schedule_delayed_work(&sd->work, 0))
+ kref_get(&sd->kref);
+}
+
+#define SBP2_UNIT_SPEC_ID_ENTRY 0x0000609e
+#define SBP2_SW_VERSION_ENTRY 0x00010483
+
+static const struct fw_device_id sbp2_id_table[] = {
+ {
+ .match_flags = FW_MATCH_SPECIFIER_ID | FW_MATCH_VERSION,
+ .specifier_id = SBP2_UNIT_SPEC_ID_ENTRY,
+ .version = SBP2_SW_VERSION_ENTRY,
+ },
+ { }
+};
+
+static struct fw_driver sbp2_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = sbp2_driver_name,
+ .bus = &fw_bus_type,
+ .probe = sbp2_probe,
+ .remove = sbp2_remove,
+ },
+ .update = sbp2_update,
+ .id_table = sbp2_id_table,
+};
+
+static unsigned int
+sbp2_status_to_sense_data(u8 *sbp2_status, u8 *sense_data)
+{
+ int sam_status;
+
+ sense_data[0] = 0x70;
+ sense_data[1] = 0x0;
+ sense_data[2] = sbp2_status[1];
+ sense_data[3] = sbp2_status[4];
+ sense_data[4] = sbp2_status[5];
+ sense_data[5] = sbp2_status[6];
+ sense_data[6] = sbp2_status[7];
+ sense_data[7] = 10;
+ sense_data[8] = sbp2_status[8];
+ sense_data[9] = sbp2_status[9];
+ sense_data[10] = sbp2_status[10];
+ sense_data[11] = sbp2_status[11];
+ sense_data[12] = sbp2_status[2];
+ sense_data[13] = sbp2_status[3];
+ sense_data[14] = sbp2_status[12];
+ sense_data[15] = sbp2_status[13];
+
+ sam_status = sbp2_status[0] & 0x3f;
+
+ switch (sam_status) {
+ case SAM_STAT_GOOD:
+ case SAM_STAT_CHECK_CONDITION:
+ case SAM_STAT_CONDITION_MET:
+ case SAM_STAT_BUSY:
+ case SAM_STAT_RESERVATION_CONFLICT:
+ case SAM_STAT_COMMAND_TERMINATED:
+ return DID_OK << 16 | sam_status;
+
+ default:
+ return DID_ERROR << 16;
+ }
+}
+
+static void
+complete_command_orb(struct sbp2_orb *base_orb, struct sbp2_status *status)
+{
+ struct sbp2_command_orb *orb = (struct sbp2_command_orb *)base_orb;
+ struct fw_unit *unit = orb->unit;
+ struct fw_device *device = fw_device(unit->device.parent);
+ struct scatterlist *sg;
+ int result;
+
+ if (status != NULL) {
+ if (status_get_dead(*status))
+ sbp2_agent_reset(unit);
+
+ switch (status_get_response(*status)) {
+ case SBP2_STATUS_REQUEST_COMPLETE:
+ result = DID_OK << 16;
+ break;
+ case SBP2_STATUS_TRANSPORT_FAILURE:
+ result = DID_BUS_BUSY << 16;
+ break;
+ case SBP2_STATUS_ILLEGAL_REQUEST:
+ case SBP2_STATUS_VENDOR_DEPENDENT:
+ default:
+ result = DID_ERROR << 16;
+ break;
+ }
+
+ if (result == DID_OK << 16 && status_get_len(*status) > 1)
+ result = sbp2_status_to_sense_data(status_get_data(*status),
+ orb->cmd->sense_buffer);
+ } else {
+ /* If the orb completes with status == NULL, something
+ * went wrong, typically a bus reset happened mid-orb
+ * or when sending the write (less likely). */
+ result = DID_BUS_BUSY << 16;
+ }
+
+ dma_unmap_single(device->card->device, orb->base.request_bus,
+ sizeof orb->request, DMA_TO_DEVICE);
+
+ if (orb->cmd->use_sg > 0) {
+ sg = (struct scatterlist *)orb->cmd->request_buffer;
+ dma_unmap_sg(device->card->device, sg, orb->cmd->use_sg,
+ orb->cmd->sc_data_direction);
+ }
+
+ if (orb->page_table_bus != 0)
+ dma_unmap_single(device->card->device, orb->page_table_bus,
+ sizeof orb->page_table_bus, DMA_TO_DEVICE);
+
+ if (orb->request_buffer_bus != 0)
+ dma_unmap_single(device->card->device, orb->request_buffer_bus,
+ sizeof orb->request_buffer_bus,
+ DMA_FROM_DEVICE);
+
+ orb->cmd->result = result;
+ orb->done(orb->cmd);
+ kfree(orb);
+}
+
+static void sbp2_command_orb_map_scatterlist(struct sbp2_command_orb *orb)
+{
+ struct fw_unit *unit =
+ (struct fw_unit *)orb->cmd->device->host->hostdata[0];
+ struct fw_device *device = fw_device(unit->device.parent);
+ struct sbp2_device *sd = unit->device.driver_data;
+ struct scatterlist *sg;
+ int sg_len, l, i, j, count;
+ size_t size;
+ dma_addr_t sg_addr;
+
+ sg = (struct scatterlist *)orb->cmd->request_buffer;
+ count = dma_map_sg(device->card->device, sg, orb->cmd->use_sg,
+ orb->cmd->sc_data_direction);
+
+ /* Handle the special case where there is only one element in
+ * the scatter list by converting it to an immediate block
+ * request. This is also a workaround for broken devices such
+ * as the second generation iPod which doesn't support page
+ * tables. */
+ if (count == 1 && sg_dma_len(sg) < SBP2_MAX_SG_ELEMENT_LENGTH) {
+ orb->request.data_descriptor.high = sd->address_high;
+ orb->request.data_descriptor.low = sg_dma_address(sg);
+ orb->request.misc |=
+ command_orb_data_size(sg_dma_len(sg));
+ return;
+ }
+
+ /* Convert the scatterlist to an sbp2 page table. If any
+ * scatterlist entries are too big for sbp2 we split the as we go. */
+ for (i = 0, j = 0; i < count; i++) {
+ sg_len = sg_dma_len(sg + i);
+ sg_addr = sg_dma_address(sg + i);
+ while (sg_len) {
+ l = min(sg_len, SBP2_MAX_SG_ELEMENT_LENGTH);
+ orb->page_table[j].low = sg_addr;
+ orb->page_table[j].high = (l << 16);
+ sg_addr += l;
+ sg_len -= l;
+ j++;
+ }
+ }
+
+ size = sizeof orb->page_table[0] * j;
+
+ /* The data_descriptor pointer is the one case where we need
+ * to fill in the node ID part of the address. All other
+ * pointers assume that the data referenced reside on the
+ * initiator (i.e. us), but data_descriptor can refer to data
+ * on other nodes so we need to put our ID in descriptor.high. */
+
+ orb->page_table_bus =
+ dma_map_single(device->card->device, orb->page_table,
+ size, DMA_TO_DEVICE);
+ orb->request.data_descriptor.high = sd->address_high;
+ orb->request.data_descriptor.low = orb->page_table_bus;
+ orb->request.misc |=
+ command_orb_page_table_present |
+ command_orb_data_size(j);
+
+ fw_memcpy_to_be32(orb->page_table, orb->page_table, size);
+}
+
+static void sbp2_command_orb_map_buffer(struct sbp2_command_orb *orb)
+{
+ struct fw_unit *unit =
+ (struct fw_unit *)orb->cmd->device->host->hostdata[0];
+ struct fw_device *device = fw_device(unit->device.parent);
+ struct sbp2_device *sd = unit->device.driver_data;
+
+ /* As for map_scatterlist, we need to fill in the high bits of
+ * the data_descriptor pointer. */
+
+ orb->request_buffer_bus =
+ dma_map_single(device->card->device,
+ orb->cmd->request_buffer,
+ orb->cmd->request_bufflen,
+ orb->cmd->sc_data_direction);
+ orb->request.data_descriptor.high = sd->address_high;
+ orb->request.data_descriptor.low = orb->request_buffer_bus;
+ orb->request.misc |=
+ command_orb_data_size(orb->cmd->request_bufflen);
+}
+
+/* SCSI stack integration */
+
+static int sbp2_scsi_queuecommand(struct scsi_cmnd *cmd, scsi_done_fn_t done)
+{
+ struct fw_unit *unit = (struct fw_unit *)cmd->device->host->hostdata[0];
+ struct fw_device *device = fw_device(unit->device.parent);
+ struct sbp2_device *sd = unit->device.driver_data;
+ struct sbp2_command_orb *orb;
+
+ /* Bidirectional commands are not yet implemented, and unknown
+ * transfer direction not handled. */
+ if (cmd->sc_data_direction == DMA_BIDIRECTIONAL) {
+ fw_error("Cannot handle DMA_BIDIRECTIONAL - rejecting command");
+ goto fail_alloc;
+ }
+
+ orb = kzalloc(sizeof *orb, GFP_ATOMIC);
+ if (orb == NULL) {
+ fw_notify("failed to alloc orb\n");
+ goto fail_alloc;
+ }
+
+ /* Initialize rcode to something not RCODE_COMPLETE. */
+ orb->base.rcode = -1;
+ orb->base.request_bus =
+ dma_map_single(device->card->device, &orb->request,
+ sizeof orb->request, DMA_TO_DEVICE);
+ if (dma_mapping_error(orb->base.request_bus))
+ goto fail_mapping;
+
+ orb->unit = unit;
+ orb->done = done;
+ orb->cmd = cmd;
+
+ orb->request.next.high = SBP2_ORB_NULL;
+ orb->request.next.low = 0x0;
+ /* At speed 100 we can do 512 bytes per packet, at speed 200,
+ * 1024 bytes per packet etc. The SBP-2 max_payload field
+ * specifies the max payload size as 2 ^ (max_payload + 2), so
+ * if we set this to max_speed + 7, we get the right value. */
+ orb->request.misc =
+ command_orb_max_payload(device->node->max_speed + 7) |
+ command_orb_speed(device->node->max_speed) |
+ command_orb_notify;
+
+ if (cmd->sc_data_direction == DMA_FROM_DEVICE)
+ orb->request.misc |=
+ command_orb_direction(SBP2_DIRECTION_FROM_MEDIA);
+ else if (cmd->sc_data_direction == DMA_TO_DEVICE)
+ orb->request.misc |=
+ command_orb_direction(SBP2_DIRECTION_TO_MEDIA);
+
+ if (cmd->use_sg) {
+ sbp2_command_orb_map_scatterlist(orb);
+ } else if (cmd->request_bufflen > SBP2_MAX_SG_ELEMENT_LENGTH) {
+ /* FIXME: Need to split this into a sg list... but
+ * could we get the scsi or blk layer to do that by
+ * reporting our max supported block size? */
+ fw_error("command > 64k\n");
+ goto fail_bufflen;
+ } else if (cmd->request_bufflen > 0) {
+ sbp2_command_orb_map_buffer(orb);
+ }
+
+ fw_memcpy_to_be32(&orb->request, &orb->request, sizeof orb->request);
+
+ memset(orb->request.command_block,
+ 0, sizeof orb->request.command_block);
+ memcpy(orb->request.command_block, cmd->cmnd, COMMAND_SIZE(*cmd->cmnd));
+
+ orb->base.callback = complete_command_orb;
+
+ sbp2_send_orb(&orb->base, unit, sd->node_id, sd->generation,
+ sd->command_block_agent_address + SBP2_ORB_POINTER);
+
+ return 0;
+
+ fail_bufflen:
+ dma_unmap_single(device->card->device, orb->base.request_bus,
+ sizeof orb->request, DMA_TO_DEVICE);
+ fail_mapping:
+ kfree(orb);
+ fail_alloc:
+ cmd->result = DID_ERROR << 16;
+ done(cmd);
+ return 0;
+}
+
+static int sbp2_scsi_slave_alloc(struct scsi_device *sdev)
+{
+ struct fw_unit *unit = (struct fw_unit *)sdev->host->hostdata[0];
+ struct sbp2_device *sd = unit->device.driver_data;
+
+ sdev->allow_restart = 1;
+
+ if (sd->workarounds & SBP2_WORKAROUND_INQUIRY_36)
+ sdev->inquiry_len = 36;
+ return 0;
+}
+
+static int sbp2_scsi_slave_configure(struct scsi_device *sdev)
+{
+ struct fw_unit *unit = (struct fw_unit *)sdev->host->hostdata[0];
+ struct sbp2_device *sd = unit->device.driver_data;
+
+ sdev->use_10_for_rw = 1;
+
+ if (sdev->type == TYPE_ROM)
+ sdev->use_10_for_ms = 1;
+ if (sdev->type == TYPE_DISK &&
+ sd->workarounds & SBP2_WORKAROUND_MODE_SENSE_8)
+ sdev->skip_ms_page_8 = 1;
+ if (sd->workarounds & SBP2_WORKAROUND_FIX_CAPACITY) {
+ fw_notify("setting fix_capacity for %s\n", unit->device.bus_id);
+ sdev->fix_capacity = 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 sbp2_scsi_abort(struct scsi_cmnd *cmd)
+{
+ struct fw_unit *unit = (struct fw_unit *)cmd->device->host->hostdata[0];
+
+ fw_notify("sbp2_scsi_abort\n");
+ sbp2_agent_reset(unit);
+ sbp2_cancel_orbs(unit);
+
+ return SUCCESS;
+}
+
+static struct scsi_host_template scsi_driver_template = {
+ .module = THIS_MODULE,
+ .name = "SBP-2 IEEE-1394",
+ .proc_name = (char *)sbp2_driver_name,
+ .queuecommand = sbp2_scsi_queuecommand,
+ .slave_alloc = sbp2_scsi_slave_alloc,
+ .slave_configure = sbp2_scsi_slave_configure,
+ .eh_abort_handler = sbp2_scsi_abort,
+ .this_id = -1,
+ .sg_tablesize = SG_ALL,
+ .use_clustering = ENABLE_CLUSTERING,
+ .cmd_per_lun = 1,
+ .can_queue = 1,
+};
+
+static int add_scsi_devices(struct fw_unit *unit)
+{
+ struct sbp2_device *sd = unit->device.driver_data;
+ int retval, lun;
+
+ if (sd->scsi_host != NULL)
+ return 0;
+
+ sd->scsi_host = scsi_host_alloc(&scsi_driver_template,
+ sizeof(unsigned long));
+ if (sd->scsi_host == NULL) {
+ fw_error("failed to register scsi host\n");
+ return -1;
+ }
+
+ sd->scsi_host->hostdata[0] = (unsigned long)unit;
+ retval = scsi_add_host(sd->scsi_host, &unit->device);
+ if (retval < 0) {
+ fw_error("failed to add scsi host\n");
+ scsi_host_put(sd->scsi_host);
+ sd->scsi_host = NULL;
+ return retval;
+ }
+
+ /* FIXME: Loop over luns here. */
+ lun = 0;
+ retval = scsi_add_device(sd->scsi_host, 0, 0, lun);
+ if (retval < 0) {
+ fw_error("failed to add scsi device\n");
+ scsi_remove_host(sd->scsi_host);
+ scsi_host_put(sd->scsi_host);
+ sd->scsi_host = NULL;
+ return retval;
+ }
+
+ return 0;
+}
+
+static void remove_scsi_devices(struct fw_unit *unit)
+{
+ struct sbp2_device *sd = unit->device.driver_data;
+
+ if (sd->scsi_host != NULL) {
+ scsi_remove_host(sd->scsi_host);
+ scsi_host_put(sd->scsi_host);
+ }
+ sd->scsi_host = NULL;
+}
+
+MODULE_AUTHOR("Kristian Hoegsberg <[email protected]>");
+MODULE_DESCRIPTION("SCSI over IEEE1394");
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(ieee1394, sbp2_id_table);
+
+static int __init sbp2_init(void)
+{
+ return driver_register(&sbp2_driver.driver);
+}
+
+static void __exit sbp2_cleanup(void)
+{
+ driver_unregister(&sbp2_driver.driver);
+}
+
+module_init(sbp2_init);
+module_exit(sbp2_cleanup);
--
Stefan Richter
-=====-=-=== -=-= ---=-
http://arcgraph.de/sr/
Signed-off-by: Stefan Richter <[email protected]>
---
drivers/Makefile | 1
drivers/firewire/Kconfig | 60 ++++++++++++++++++++++++++++++++++++++
drivers/firewire/Makefile | 10 ++++++
drivers/ieee1394/Kconfig | 2 +
4 files changed, 73 insertions(+)
Index: linux_juju/drivers/Makefile
===================================================================
--- linux_juju.orig/drivers/Makefile
+++ linux_juju/drivers/Makefile
@@ -36,6 +36,7 @@ obj-$(CONFIG_FC4) += fc4/
obj-$(CONFIG_SCSI) += scsi/
obj-$(CONFIG_ATA) += ata/
obj-$(CONFIG_FUSION) += message/
+obj-$(CONFIG_FIREWIRE) += firewire/
obj-$(CONFIG_IEEE1394) += ieee1394/
obj-y += cdrom/
obj-y += auxdisplay/
Index: linux_juju/drivers/firewire/Makefile
===================================================================
--- /dev/null
+++ linux_juju/drivers/firewire/Makefile
@@ -0,0 +1,10 @@
+#
+# Makefile for the Linux IEEE 1394 implementation
+#
+
+fw-core-objs := fw-card.o fw-topology.o fw-transaction.o fw-iso.o \
+ fw-device.o fw-cdev.o
+
+obj-$(CONFIG_FIREWIRE) += fw-core.o
+obj-$(CONFIG_FIREWIRE_OHCI) += fw-ohci.o
+obj-$(CONFIG_FIREWIRE_SBP2) += fw-sbp2.o
Index: linux_juju/drivers/ieee1394/Kconfig
===================================================================
--- linux_juju.orig/drivers/ieee1394/Kconfig
+++ linux_juju/drivers/ieee1394/Kconfig
@@ -2,6 +2,8 @@
menu "IEEE 1394 (FireWire) support"
+source "drivers/firewire/Kconfig"
+
config IEEE1394
tristate "IEEE 1394 (FireWire) support"
depends on PCI || BROKEN
Index: linux_juju/drivers/firewire/Kconfig
===================================================================
--- /dev/null
+++ linux_juju/drivers/firewire/Kconfig
@@ -0,0 +1,60 @@
+# -*- shell-script -*-
+
+comment "An alternative FireWire stack is available with EXPERIMENTAL=y"
+ depends on EXPERIMENTAL=n
+
+config FIREWIRE
+ tristate "IEEE 1394 (FireWire) support (JUJU alternative stack, experimental)"
+ depends on EXPERIMENTAL
+ 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 fw-core.
+
+ This is the "JUJU" FireWire stack, an alternative implementation
+ designed for robustness and simplicity. You can build either this
+ stack, or the classic stack (the ieee1394 driver, ohci1394 etc.)
+ or both.
+
+config FIREWIRE_OHCI
+ tristate "Support for OHCI FireWire host controllers"
+ depends on PCI && FIREWIRE
+ help
+ Enable this driver if you have a FireWire controller based
+ on the OHCI specification. For all practical purposes, this
+ is the only chipset in use, so say Y here.
+
+ To compile this driver as a module, say M here: The module will be
+ called fw-ohci.
+
+ If you also build ohci1394 of the classic IEEE 1394 driver stack,
+ blacklist either ohci1394 or fw-ohci to let hotplug load the desired
+ driver.
+
+config FIREWIRE_SBP2
+ tristate "Support for storage devices (SBP-2 protocol driver)"
+ depends on FIREWIRE && SCSI
+ help
+ This option enables you to use SBP-2 devices connected to a
+ FireWire bus. SBP-2 devices include storage devices like
+ harddisks and DVD drives, also some other FireWire devices
+ like scanners.
+
+ To compile this driver as a module, say M here: The module will be
+ called fw-sbp2.
+
+ You should also enable support for disks, CD-ROMs, etc. in the SCSI
+ configuration section.
+
+ If you also build sbp2 of the classic IEEE 1394 driver stack,
+ blacklist either sbp2 or fw-sbp2 to let hotplug load the desired
+ driver.
+
--
Stefan Richter
-=====-=-=== -=-= ---=-
http://arcgraph.de/sr/
On Tue, May 01, Kristian Høgsberg wrote:
> drivers/firewire/Kconfig | 60 ++
NACK.
Upgrade the current drivers/ieee1394/ with the new code, and keep all
existing module names.
Olaf Hering wrote:
> On Tue, May 01, Kristian H?gsberg wrote:
>
>> drivers/firewire/Kconfig | 60 ++
>
> NACK.
> Upgrade the current drivers/ieee1394/ with the new code,
Last time I believe I was the only one who asked whether to put it into
drivers/ieee1394 instead of another directory. Of course I acknowledge
that everytime a new review round is started, people do reconsider.
Especially since we had a gap of a few months since the last LKML review.
> and keep all existing module names.
I'm impartial to that. Using same names might ease the transition from
the userspace side, as far as there is userland which relies on module
names.
A certain drawback of same names would be that geeks cannot install both
stacks at once during the transition period. Therefore, checking
whether eventual problems are in fact regressions involves a module
unload/ configure/ build/ install/ reload cycle, instead of just module
unload/ reload. This especially means we can only get help from testers
who are able to build kernels.
Other opinions?
--
Stefan Richter
-=====-=-=== -=-= ---=-
http://arcgraph.de/sr/
On Wednesday 02 May 2007, Stefan Richter wrote:
>Olaf Hering wrote:
>> On Tue, May 01, Kristian H?gsberg wrote:
>>> drivers/firewire/Kconfig | 60 ++
>>
>> NACK.
>> Upgrade the current drivers/ieee1394/ with the new code,
>
>Last time I believe I was the only one who asked whether to put it into
>drivers/ieee1394 instead of another directory. Of course I acknowledge
>that everytime a new review round is started, people do reconsider.
>Especially since we had a gap of a few months since the last LKML review.
>
>> and keep all existing module names.
>
>I'm impartial to that. Using same names might ease the transition from
>the userspace side, as far as there is userland which relies on module
>names.
>
>A certain drawback of same names would be that geeks cannot install both
>stacks at once during the transition period. Therefore, checking
>whether eventual problems are in fact regressions involves a module
>unload/ configure/ build/ install/ reload cycle, instead of just module
>unload/ reload. This especially means we can only get help from testers
>who are able to build kernels.
>
>Other opinions?
I can and do build my own kernels so that's not a problem for me. I also have
a firewire movie camera that went through absolute hell the last time a
firewire upgrade came by, and it was over 5 months before I had a working
kino install again. So I'd vote unconditionally to have 2 trees to select
from at module load time until the shakeout has produced usable code in the
2nd tree.
>From me, its a definite ACK.
--
Cheers, Gene
"There are four boxes to be used in defense of liberty:
soap, ballot, jury, and ammo. Please use in that order."
-Ed Howdershelt (Author)
I owe, I owe,
It's off to work I go...
On Wed, May 02, 2007 at 02:48:11PM +0200, Stefan Richter wrote:
> Olaf Hering wrote:
> > On Tue, May 01, Kristian Høgsberg wrote:
> >
> >> drivers/firewire/Kconfig | 60 ++
> >
> > NACK.
> > Upgrade the current drivers/ieee1394/ with the new code,
>
> Last time I believe I was the only one who asked whether to put it into
> drivers/ieee1394 instead of another directory. Of course I acknowledge
> that everytime a new review round is started, people do reconsider.
> Especially since we had a gap of a few months since the last LKML review.
>
> > and keep all existing module names.
>
> I'm impartial to that. Using same names might ease the transition from
> the userspace side, as far as there is userland which relies on module
> names.
>
> A certain drawback of same names would be that geeks cannot install both
> stacks at once during the transition period. Therefore, checking
> whether eventual problems are in fact regressions involves a module
> unload/ configure/ build/ install/ reload cycle, instead of just module
> unload/ reload. This especially means we can only get help from testers
> who are able to build kernels.
>
> Other opinions?
An advantage of changing the names is that they are now prefixed.
But looking at them, there will again be the point whether everyone will
think that "fw" is firmware, and perhaps switching to the (although
longer) prefix "firewire" might make sense?
> Stefan Richter
cu
Adrian
--
"Is there not promise of rain?" Ling Tan asked suddenly out
of the darkness. There had been need of rain for many days.
"Only a promise," Lao Er said.
Pearl S. Buck - Dragon Seed
>>>>> "Stefan" == Stefan Richter <[email protected]> writes:
Stefan> Signed-off-by: Stefan Richter <[email protected]>
Stefan> ---
Stefan> drivers/firewire/fw-cdev.c | 954 ++++++++++++++++++++++++++++++++++
Stefan> include/linux/firewire-cdev.h | 268 +++++++++
Stefan> 2 files changed, 1222 insertions(+)
Stefan> Index: linux_juju/include/linux/firewire-cdev.h
Stefan> ===================================================================
Stefan> --- /dev/null
Stefan> +++ linux_juju/include/linux/firewire-cdev.h
Stefan> @@ -0,0 +1,268 @@
Stefan> +/* -*- c-basic-offset: 8 -*-
Stefan> + *
Stefan> + * fw-device-cdev.h -- Char device interface.
Stefan> + *
Stefan> + * Copyright (C) 2005-2006 Kristian Hoegsberg <[email protected]>
Stefan> + *
Stefan> + * This program is free software; you can redistribute it and/or modify
Stefan> + * it under the terms of the GNU General Public License as published by
Stefan> + * the Free Software Foundation; either version 2 of the License, or
Stefan> + * (at your option) any later version.
Stefan> + *
Stefan> + * This program is distributed in the hope that it will be useful,
Stefan> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
Stefan> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
Stefan> + * GNU General Public License for more details.
Stefan> + *
Stefan> + * You should have received a copy of the GNU General Public License
Stefan> + * along with this program; if not, write to the Free Software Foundation,
Stefan> + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
Stefan> + */
Stefan> +
Stefan> +#ifndef __fw_cdev_h
Stefan> +#define __fw_cdev_h
Stefan> +
Stefan> +#include <asm/ioctl.h>
Stefan> +#include <asm/types.h>
Stefan> +
Stefan> +#define TCODE_WRITE_QUADLET_REQUEST 0
Stefan> +#define TCODE_WRITE_BLOCK_REQUEST 1
Stefan> +#define TCODE_WRITE_RESPONSE 2
Stefan> +#define TCODE_READ_QUADLET_REQUEST 4
Stefan> +#define TCODE_READ_BLOCK_REQUEST 5
Stefan> +#define TCODE_READ_QUADLET_RESPONSE 6
Stefan> +#define TCODE_READ_BLOCK_RESPONSE 7
Stefan> +#define TCODE_CYCLE_START 8
Stefan> +#define TCODE_LOCK_REQUEST 9
Stefan> +#define TCODE_STREAM_DATA 10
Stefan> +#define TCODE_LOCK_RESPONSE 11
Stefan> +
Stefan> +#define TCODE_LOCK_MASK_SWAP 0x11
Stefan> +#define TCODE_LOCK_COMPARE_SWAP 0x12
Stefan> +#define TCODE_LOCK_FETCH_ADD 0x13
Stefan> +#define TCODE_LOCK_LITTLE_ADD 0x14
Stefan> +#define TCODE_LOCK_BOUNDED_ADD 0x15
Stefan> +#define TCODE_LOCK_WRAP_ADD 0x16
Stefan> +#define TCODE_LOCK_VENDOR_DEPENDENT 0x17
Stefan> +
Stefan> +#define RCODE_COMPLETE 0x0
Stefan> +#define RCODE_CONFLICT_ERROR 0x4
Stefan> +#define RCODE_DATA_ERROR 0x5
Stefan> +#define RCODE_TYPE_ERROR 0x6
Stefan> +#define RCODE_ADDRESS_ERROR 0x7
Stefan> +
Stefan> +#define RCODE_SEND_ERROR 0x10
Stefan> +#define RCODE_CANCELLED 0x11
Stefan> +#define RCODE_BUSY 0x12
Stefan> +#define RCODE_GENERATION 0x13
Stefan> +#define RCODE_NO_ACK 0x14
Stefan> +
Stefan> +#define SCODE_100 0x0
Stefan> +#define SCODE_200 0x1
Stefan> +#define SCODE_400 0x2
Stefan> +#define SCODE_800 0x3
Stefan> +#define SCODE_1600 0x4
Stefan> +#define SCODE_3200 0x5
These are also defined in fw-transaction.h, though that file doesn't
have all the values. Can these just be combined into a single
fw-constants.h file instead?
I honestly haven't checked all your defines....
Stefan> +#define FW_CDEV_EVENT_BUS_RESET 0x00
Stefan> +#define FW_CDEV_EVENT_RESPONSE 0x01
Stefan> +#define FW_CDEV_EVENT_REQUEST 0x02
Stefan> +#define FW_CDEV_EVENT_ISO_INTERRUPT 0x03
Stefan> +
Stefan> +/* The 'closure' fields are for user space to use. Data passed in the
Stefan> + * 'closure' field for a request will be returned in the corresponding
Stefan> + * event. It's a 64-bit type so that it's a fixed size type big
Stefan> + * enough to hold a pointer on all platforms. */
Stefan> +
Stefan> +struct fw_cdev_event_common {
Stefan> + __u64 closure;
Stefan> + __u32 type;
Stefan> +};
Stefan> +
Stefan> +struct fw_cdev_event_bus_reset {
Stefan> + __u64 closure;
Stefan> + __u32 type;
Stefan> + __u32 node_id;
Stefan> + __u32 local_node_id;
Stefan> + __u32 bm_node_id;
Stefan> + __u32 irm_node_id;
Stefan> + __u32 root_node_id;
Stefan> + __u32 generation;
Stefan> +};
Stefan> +
Stefan> +struct fw_cdev_event_response {
Stefan> + __u64 closure;
Stefan> + __u32 type;
Stefan> + __u32 rcode;
Stefan> + __u32 length;
Stefan> + __u32 data[0];
Stefan> +};
Stefan> +
Stefan> +struct fw_cdev_event_request {
Stefan> + __u64 closure;
Stefan> + __u32 type;
Stefan> + __u32 tcode;
Stefan> + __u64 offset;
Stefan> + __u32 handle;
Stefan> + __u32 length;
Stefan> + __u32 data[0];
Stefan> +};
Stefan> +
Stefan> +struct fw_cdev_event_iso_interrupt {
Stefan> + __u64 closure;
Stefan> + __u32 type;
Stefan> + __u32 cycle;
Stefan> + __u32 header_length; /* Length in bytes of following headers. */
Stefan> + __u32 header[0];
Stefan> +};
Stefan> +
Stefan> +union fw_cdev_event {
Stefan> + struct fw_cdev_event_common common;
Stefan> + struct fw_cdev_event_bus_reset bus_reset;
Stefan> + struct fw_cdev_event_response response;
Stefan> + struct fw_cdev_event_request request;
Stefan> + struct fw_cdev_event_iso_interrupt iso_interrupt;
Stefan> +};
Stefan> +
Stefan> +#define FW_CDEV_IOC_GET_INFO _IOWR('#', 0x00, struct fw_cdev_get_info)
Stefan> +#define FW_CDEV_IOC_SEND_REQUEST _IOW('#', 0x01, struct fw_cdev_send_request)
Stefan> +#define FW_CDEV_IOC_ALLOCATE _IOWR('#', 0x02, struct fw_cdev_allocate)
Stefan> +#define FW_CDEV_IOC_DEALLOCATE _IOW('#', 0x03, struct fw_cdev_deallocate)
Stefan> +#define FW_CDEV_IOC_SEND_RESPONSE _IOW('#', 0x04, struct fw_cdev_send_response)
Stefan> +#define FW_CDEV_IOC_INITIATE_BUS_RESET _IOW('#', 0x05, struct fw_cdev_initiate_bus_reset)
Stefan> +#define FW_CDEV_IOC_ADD_DESCRIPTOR _IOWR('#', 0x06, struct fw_cdev_add_descriptor)
Stefan> +#define FW_CDEV_IOC_REMOVE_DESCRIPTOR _IOW('#', 0x07, struct fw_cdev_remove_descriptor)
Stefan> +
Stefan> +#define FW_CDEV_IOC_CREATE_ISO_CONTEXT _IOWR('#', 0x08, struct fw_cdev_create_iso_context)
Stefan> +#define FW_CDEV_IOC_QUEUE_ISO _IOWR('#', 0x09, struct fw_cdev_queue_iso)
Stefan> +#define FW_CDEV_IOC_START_ISO _IOW('#', 0x0a, struct fw_cdev_start_iso)
Stefan> +#define FW_CDEV_IOC_STOP_ISO _IOW('#', 0x0b, struct fw_cdev_stop_iso)
Stefan> +
Stefan> +/* FW_CDEV_VERSION History
Stefan> + *
Stefan> + * 1 Feb 18, 2007: Initial version.
Stefan> + */
Stefan> +#define FW_CDEV_VERSION 1
Stefan> +
Stefan> +struct fw_cdev_get_info {
Stefan> + /* The version field is just a running serial number. We
Stefan> + * never break backwards compatibility. Userspace passes in
Stefan> + * the version it expects and the kernel passes back the
Stefan> + * highest version it can provide. Even if the structs in
Stefan> + * this interface are extended in a later version, the kernel
Stefan> + * will not copy back more data than what was present in the
Stefan> + * interface version userspace expects. */
Stefan> + __u32 version;
Stefan> +
Stefan> + /* If non-zero, at most rom_length bytes of config rom will be
Stefan> + * copied into that user space address. In either case,
Stefan> + * rom_length is updated with the actual length of the config
Stefan> + * rom. */
Stefan> + __u32 rom_length;
Stefan> + __u64 rom;
Stefan> +
Stefan> + /* If non-zero, a fw_cdev_event_bus_reset struct will be
Stefan> + * copied here with the current state of the bus. This does
Stefan> + * not cause a bus reset to happen. The value of closure in
Stefan> + * this and sub-sequent bus reset events is set to
Stefan> + * bus_reset_closure. */
Stefan> + __u64 bus_reset;
Stefan> + __u64 bus_reset_closure;
Stefan> +
Stefan> + /* The index of the card this devices belongs to. */
Stefan> + __u32 card;
Stefan> +};
Stefan> +
Stefan> +struct fw_cdev_send_request {
Stefan> + __u32 tcode;
Stefan> + __u32 length;
Stefan> + __u64 offset;
Stefan> + __u64 closure;
Stefan> + __u64 data;
Stefan> + __u32 generation;
Stefan> +};
Stefan> +
Stefan> +struct fw_cdev_send_response {
Stefan> + __u32 rcode;
Stefan> + __u32 length;
Stefan> + __u64 data;
Stefan> + __u32 handle;
Stefan> +};
Stefan> +
Stefan> +struct fw_cdev_allocate {
Stefan> + __u64 offset;
Stefan> + __u64 closure;
Stefan> + __u32 length;
Stefan> + __u32 handle;
Stefan> +};
Stefan> +
Stefan> +struct fw_cdev_deallocate {
Stefan> + __u32 handle;
Stefan> +};
Stefan> +
Stefan> +#define FW_CDEV_LONG_RESET 0
Stefan> +#define FW_CDEV_SHORT_RESET 1
Stefan> +
Stefan> +struct fw_cdev_initiate_bus_reset {
Stefan> + __u32 type;
Stefan> +};
Stefan> +
Stefan> +struct fw_cdev_add_descriptor {
Stefan> + __u32 immediate;
Stefan> + __u32 key;
Stefan> + __u64 data;
Stefan> + __u32 length;
Stefan> + __u32 handle;
Stefan> +};
Stefan> +
Stefan> +struct fw_cdev_remove_descriptor {
Stefan> + __u32 handle;
Stefan> +};
Stefan> +
Stefan> +#define FW_CDEV_ISO_CONTEXT_TRANSMIT 0
Stefan> +#define FW_CDEV_ISO_CONTEXT_RECEIVE 1
Stefan> +
Stefan> +#define FW_CDEV_ISO_CONTEXT_MATCH_TAG0 1
Stefan> +#define FW_CDEV_ISO_CONTEXT_MATCH_TAG1 2
Stefan> +#define FW_CDEV_ISO_CONTEXT_MATCH_TAG2 4
Stefan> +#define FW_CDEV_ISO_CONTEXT_MATCH_TAG3 8
Stefan> +#define FW_CDEV_ISO_CONTEXT_MATCH_ALL_TAGS 15
Stefan> +
Stefan> +struct fw_cdev_create_iso_context {
Stefan> + __u32 type;
Stefan> + __u32 header_size;
Stefan> + __u32 channel;
Stefan> + __u32 speed;
Stefan> + __u64 closure;
Stefan> + __u32 handle;
Stefan> +};
Stefan> +
Stefan> +struct fw_cdev_iso_packet {
Stefan> + __u16 payload_length; /* Length of indirect payload. */
Stefan> + __u32 interrupt : 1; /* Generate interrupt on this packet */
Stefan> + __u32 skip : 1; /* Set to not send packet at all. */
Stefan> + __u32 tag : 2;
Stefan> + __u32 sy : 4;
Stefan> + __u32 header_length : 8; /* Length of immediate header. */
Stefan> + __u32 header[0];
Stefan> +};
Stefan> +
Stefan> +struct fw_cdev_queue_iso {
Stefan> + __u64 packets;
Stefan> + __u64 data;
Stefan> + __u32 size;
Stefan> + __u32 handle;
Stefan> +};
Stefan> +
Stefan> +struct fw_cdev_start_iso {
Stefan> + __s32 cycle;
Stefan> + __u32 sync;
Stefan> + __u32 tags;
Stefan> + __u32 handle;
Stefan> +};
Stefan> +
Stefan> +struct fw_cdev_stop_iso {
Stefan> + __u32 handle;
Stefan> +};
Stefan> +
Stefan> +#endif /* __fw_cdev_h */
Stefan> Index: linux_juju/drivers/firewire/fw-cdev.c
Stefan> ===================================================================
Stefan> --- /dev/null
Stefan> +++ linux_juju/drivers/firewire/fw-cdev.c
Stefan> @@ -0,0 +1,954 @@
Stefan> +/* -*- c-basic-offset: 8 -*-
Stefan> + *
Stefan> + * fw-device-cdev.c - Char device for device raw access
Stefan> + *
Stefan> + * Copyright (C) 2005-2006 Kristian Hoegsberg <[email protected]>
Stefan> + *
Stefan> + * This program is free software; you can redistribute it and/or modify
Stefan> + * it under the terms of the GNU General Public License as published by
Stefan> + * the Free Software Foundation; either version 2 of the License, or
Stefan> + * (at your option) any later version.
Stefan> + *
Stefan> + * This program is distributed in the hope that it will be useful,
Stefan> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
Stefan> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
Stefan> + * GNU General Public License for more details.
Stefan> + *
Stefan> + * You should have received a copy of the GNU General Public License
Stefan> + * along with this program; if not, write to the Free Software Foundation,
Stefan> + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
Stefan> + */
Stefan> +
Stefan> +#include <linux/module.h>
Stefan> +#include <linux/kernel.h>
Stefan> +#include <linux/wait.h>
Stefan> +#include <linux/errno.h>
Stefan> +#include <linux/device.h>
Stefan> +#include <linux/vmalloc.h>
Stefan> +#include <linux/poll.h>
Stefan> +#include <linux/delay.h>
Stefan> +#include <linux/mm.h>
Stefan> +#include <linux/idr.h>
Stefan> +#include <linux/compat.h>
Stefan> +#include <linux/firewire-cdev.h>
Stefan> +#include <asm/uaccess.h>
Stefan> +#include "fw-transaction.h"
Stefan> +#include "fw-topology.h"
Stefan> +#include "fw-device.h"
Stefan> +
Stefan> +/* dequeue_event() just kfree()'s the event, so the event has to be
Stefan> + * the first field in the struct. */
Stefan> +
Stefan> +struct client;
Stefan> +struct client_resource {
Stefan> + struct list_head link;
Stefan> + void (*release)(struct client *client, struct client_resource *r);
Stefan> + u32 handle;
Stefan> +};
Stefan> +
Stefan> +struct event {
Stefan> + struct { void *data; size_t size; } v[2];
Stefan> + struct list_head link;
Stefan> +};
Stefan> +
Stefan> +struct bus_reset {
Stefan> + struct event event;
Stefan> + struct fw_cdev_event_bus_reset reset;
Stefan> +};
Stefan> +
Stefan> +struct response {
Stefan> + struct event event;
Stefan> + struct fw_transaction transaction;
Stefan> + struct client *client;
Stefan> + struct client_resource resource;
Stefan> + struct fw_cdev_event_response response;
Stefan> +};
Stefan> +
Stefan> +struct iso_interrupt {
Stefan> + struct event event;
Stefan> + struct fw_cdev_event_iso_interrupt interrupt;
Stefan> +};
Stefan> +
Stefan> +struct client {
Stefan> + u32 version;
Stefan> + struct fw_device *device;
Stefan> + spinlock_t lock;
Stefan> + u32 resource_handle;
Stefan> + struct list_head resource_list;
Stefan> + struct list_head event_list;
Stefan> + wait_queue_head_t wait;
Stefan> + u64 bus_reset_closure;
Stefan> +
Stefan> + struct fw_iso_context *iso_context;
Stefan> + u64 iso_closure;
Stefan> + struct fw_iso_buffer buffer;
Stefan> + unsigned long vm_start;
Stefan> +
Stefan> + struct list_head link;
Stefan> +};
Stefan> +
Stefan> +static inline void __user *
Stefan> +u64_to_uptr(__u64 value)
Stefan> +{
Stefan> + return (void __user *)(unsigned long)value;
Stefan> +}
Stefan> +
Stefan> +static inline __u64
Stefan> +uptr_to_u64(void __user *ptr)
Stefan> +{
Stefan> + return (__u64)(unsigned long)ptr;
Stefan> +}
Stefan> +
Stefan> +static int fw_device_op_open(struct inode *inode, struct file *file)
Stefan> +{
Stefan> + struct fw_device *device;
Stefan> + struct client *client;
Stefan> + unsigned long flags;
Stefan> +
Stefan> + device = fw_device_from_devt(inode->i_rdev);
Stefan> + if (device == NULL)
Stefan> + return -ENODEV;
Stefan> +
Stefan> + client = kzalloc(sizeof *client, GFP_KERNEL);
Stefan> + if (client == NULL)
Stefan> + return -ENOMEM;
Stefan> +
Stefan> + client->device = fw_device_get(device);
Stefan> + INIT_LIST_HEAD(&client->event_list);
Stefan> + INIT_LIST_HEAD(&client->resource_list);
Stefan> + spin_lock_init(&client->lock);
Stefan> + init_waitqueue_head(&client->wait);
Stefan> +
Stefan> + file->private_data = client;
Stefan> +
Stefan> + spin_lock_irqsave(&device->card->lock, flags);
Stefan> + list_add_tail(&client->link, &device->client_list);
Stefan> + spin_unlock_irqrestore(&device->card->lock, flags);
Stefan> +
Stefan> + return 0;
Stefan> +}
Stefan> +
Stefan> +static void queue_event(struct client *client, struct event *event,
Stefan> + void *data0, size_t size0, void *data1, size_t size1)
Stefan> +{
Stefan> + unsigned long flags;
Stefan> +
Stefan> + event->v[0].data = data0;
Stefan> + event->v[0].size = size0;
Stefan> + event->v[1].data = data1;
Stefan> + event->v[1].size = size1;
Stefan> +
Stefan> + spin_lock_irqsave(&client->lock, flags);
Stefan> +
Stefan> + list_add_tail(&event->link, &client->event_list);
Stefan> + wake_up_interruptible(&client->wait);
Stefan> +
Stefan> + spin_unlock_irqrestore(&client->lock, flags);
Stefan> +}
Stefan> +
Stefan> +static int
Stefan> +dequeue_event(struct client *client, char __user *buffer, size_t count)
Stefan> +{
Stefan> + unsigned long flags;
Stefan> + struct event *event;
Stefan> + size_t size, total;
Stefan> + int i, retval;
Stefan> +
Stefan> + retval = wait_event_interruptible(client->wait,
Stefan> + !list_empty(&client->event_list) ||
Stefan> + fw_device_is_shutdown(client->device));
Stefan> + if (retval < 0)
Stefan> + return retval;
Stefan> +
Stefan> + if (list_empty(&client->event_list) &&
Stefan> + fw_device_is_shutdown(client->device))
Stefan> + return -ENODEV;
Stefan> +
Stefan> + spin_lock_irqsave(&client->lock, flags);
Stefan> + event = container_of(client->event_list.next, struct event, link);
Stefan> + list_del(&event->link);
Stefan> + spin_unlock_irqrestore(&client->lock, flags);
Stefan> +
Stefan> + total = 0;
Stefan> + for (i = 0; i < ARRAY_SIZE(event->v) && total < count; i++) {
Stefan> + size = min(event->v[i].size, count - total);
Stefan> + if (copy_to_user(buffer + total, event->v[i].data, size)) {
Stefan> + retval = -EFAULT;
Stefan> + goto out;
Stefan> + }
Stefan> + total += size;
Stefan> + }
Stefan> + retval = total;
Stefan> +
Stefan> + out:
Stefan> + kfree(event);
Stefan> +
Stefan> + return retval;
Stefan> +}
Stefan> +
Stefan> +static ssize_t
Stefan> +fw_device_op_read(struct file *file,
Stefan> + char __user *buffer, size_t count, loff_t *offset)
Stefan> +{
Stefan> + struct client *client = file->private_data;
Stefan> +
Stefan> + return dequeue_event(client, buffer, count);
Stefan> +}
Stefan> +
Stefan> +static void
Stefan> +fill_bus_reset_event(struct fw_cdev_event_bus_reset *event,
Stefan> + struct client *client)
Stefan> +{
Stefan> + struct fw_card *card = client->device->card;
Stefan> +
Stefan> + event->closure = client->bus_reset_closure;
Stefan> + event->type = FW_CDEV_EVENT_BUS_RESET;
Stefan> + event->node_id = client->device->node_id;
Stefan> + event->local_node_id = card->local_node->node_id;
Stefan> + event->bm_node_id = 0; /* FIXME: We don't track the BM. */
Stefan> + event->irm_node_id = card->irm_node->node_id;
Stefan> + event->root_node_id = card->root_node->node_id;
Stefan> + event->generation = card->generation;
Stefan> +}
Stefan> +
Stefan> +static void
Stefan> +for_each_client(struct fw_device *device,
Stefan> + void (*callback)(struct client *client))
Stefan> +{
Stefan> + struct fw_card *card = device->card;
Stefan> + struct client *c;
Stefan> + unsigned long flags;
Stefan> +
Stefan> + spin_lock_irqsave(&card->lock, flags);
Stefan> +
Stefan> + list_for_each_entry(c, &device->client_list, link)
Stefan> + callback(c);
Stefan> +
Stefan> + spin_unlock_irqrestore(&card->lock, flags);
Stefan> +}
Stefan> +
Stefan> +static void
Stefan> +queue_bus_reset_event(struct client *client)
Stefan> +{
Stefan> + struct bus_reset *bus_reset;
Stefan> +
Stefan> + bus_reset = kzalloc(sizeof *bus_reset, GFP_ATOMIC);
Stefan> + if (bus_reset == NULL) {
Stefan> + fw_notify("Out of memory when allocating bus reset event\n");
Stefan> + return;
Stefan> + }
Stefan> +
Stefan> + fill_bus_reset_event(&bus_reset->reset, client);
Stefan> +
Stefan> + queue_event(client, &bus_reset->event,
Stefan> + &bus_reset->reset, sizeof bus_reset->reset, NULL, 0);
Stefan> +}
Stefan> +
Stefan> +void fw_device_cdev_update(struct fw_device *device)
Stefan> +{
Stefan> + for_each_client(device, queue_bus_reset_event);
Stefan> +}
Stefan> +
Stefan> +static void wake_up_client(struct client *client)
Stefan> +{
Stefan> + wake_up_interruptible(&client->wait);
Stefan> +}
Stefan> +
Stefan> +void fw_device_cdev_remove(struct fw_device *device)
Stefan> +{
Stefan> + for_each_client(device, wake_up_client);
Stefan> +}
Stefan> +
Stefan> +static int ioctl_get_info(struct client *client, void *buffer)
Stefan> +{
Stefan> + struct fw_cdev_get_info *get_info = buffer;
Stefan> + struct fw_cdev_event_bus_reset bus_reset;
Stefan> +
Stefan> + client->version = get_info->version;
Stefan> + get_info->version = FW_CDEV_VERSION;
Stefan> +
Stefan> + if (get_info->rom != 0) {
Stefan> + void __user *uptr = u64_to_uptr(get_info->rom);
Stefan> + size_t want = get_info->rom_length;
Stefan> + size_t have = client->device->config_rom_length * 4;
Stefan> +
Stefan> + if (copy_to_user(uptr, client->device->config_rom,
Stefan> + min(want, have)))
Stefan> + return -EFAULT;
Stefan> + }
Stefan> + get_info->rom_length = client->device->config_rom_length * 4;
Stefan> +
Stefan> + client->bus_reset_closure = get_info->bus_reset_closure;
Stefan> + if (get_info->bus_reset != 0) {
Stefan> + void __user *uptr = u64_to_uptr(get_info->bus_reset);
Stefan> +
Stefan> + fill_bus_reset_event(&bus_reset, client);
Stefan> + if (copy_to_user(uptr, &bus_reset, sizeof bus_reset))
Stefan> + return -EFAULT;
Stefan> + }
Stefan> +
Stefan> + get_info->card = client->device->card->index;
Stefan> +
Stefan> + return 0;
Stefan> +}
Stefan> +
Stefan> +static void
Stefan> +add_client_resource(struct client *client, struct client_resource *resource)
Stefan> +{
Stefan> + unsigned long flags;
Stefan> +
Stefan> + spin_lock_irqsave(&client->lock, flags);
Stefan> + list_add_tail(&resource->link, &client->resource_list);
Stefan> + resource->handle = client->resource_handle++;
Stefan> + spin_unlock_irqrestore(&client->lock, flags);
Stefan> +}
Stefan> +
Stefan> +static int
Stefan> +release_client_resource(struct client *client, u32 handle,
Stefan> + struct client_resource **resource)
Stefan> +{
Stefan> + struct client_resource *r;
Stefan> + unsigned long flags;
Stefan> +
Stefan> + spin_lock_irqsave(&client->lock, flags);
Stefan> + list_for_each_entry(r, &client->resource_list, link) {
Stefan> + if (r->handle == handle) {
Stefan> + list_del(&r->link);
Stefan> + break;
Stefan> + }
Stefan> + }
Stefan> + spin_unlock_irqrestore(&client->lock, flags);
Stefan> +
Stefan> + if (&r->link == &client->resource_list)
Stefan> + return -EINVAL;
Stefan> +
Stefan> + if (resource)
Stefan> + *resource = r;
Stefan> + else
Stefan> + r->release(client, r);
Stefan> +
Stefan> + return 0;
Stefan> +}
Stefan> +
Stefan> +static void
Stefan> +release_transaction(struct client *client, struct client_resource *resource)
Stefan> +{
Stefan> + struct response *response =
Stefan> + container_of(resource, struct response, resource);
Stefan> +
Stefan> + fw_cancel_transaction(client->device->card, &response->transaction);
Stefan> +}
Stefan> +
Stefan> +static void
Stefan> +complete_transaction(struct fw_card *card, int rcode,
Stefan> + void *payload, size_t length, void *data)
Stefan> +{
Stefan> + struct response *response = data;
Stefan> + struct client *client = response->client;
Stefan> + unsigned long flags;
Stefan> +
Stefan> + if (length < response->response.length)
Stefan> + response->response.length = length;
Stefan> + if (rcode == RCODE_COMPLETE)
Stefan> + memcpy(response->response.data, payload,
Stefan> + response->response.length);
Stefan> +
Stefan> + spin_lock_irqsave(&client->lock, flags);
Stefan> + list_del(&response->resource.link);
Stefan> + spin_unlock_irqrestore(&client->lock, flags);
Stefan> +
Stefan> + response->response.type = FW_CDEV_EVENT_RESPONSE;
Stefan> + response->response.rcode = rcode;
Stefan> + queue_event(client, &response->event,
Stefan> + &response->response, sizeof response->response,
Stefan> + response->response.data, response->response.length);
Stefan> +}
Stefan> +
Stefan> +static ssize_t ioctl_send_request(struct client *client, void *buffer)
Stefan> +{
Stefan> + struct fw_device *device = client->device;
Stefan> + struct fw_cdev_send_request *request = buffer;
Stefan> + struct response *response;
Stefan> +
Stefan> + /* What is the biggest size we'll accept, really? */
Stefan> + if (request->length > 4096)
Stefan> + return -EINVAL;
Stefan> +
Stefan> + response = kmalloc(sizeof *response + request->length, GFP_KERNEL);
Stefan> + if (response == NULL)
Stefan> + return -ENOMEM;
Stefan> +
Stefan> + response->client = client;
Stefan> + response->response.length = request->length;
Stefan> + response->response.closure = request->closure;
Stefan> +
Stefan> + if (request->data &&
Stefan> + copy_from_user(response->response.data,
Stefan> + u64_to_uptr(request->data), request->length)) {
Stefan> + kfree(response);
Stefan> + return -EFAULT;
Stefan> + }
Stefan> +
Stefan> + response->resource.release = release_transaction;
Stefan> + add_client_resource(client, &response->resource);
Stefan> +
Stefan> + fw_send_request(device->card, &response->transaction,
Stefan> + request->tcode & 0x1f,
Stefan> + device->node->node_id,
Stefan> + request->generation,
Stefan> + device->node->max_speed,
Stefan> + request->offset,
Stefan> + response->response.data, request->length,
Stefan> + complete_transaction, response);
Stefan> +
Stefan> + if (request->data)
Stefan> + return sizeof request + request->length;
Stefan> + else
Stefan> + return sizeof request;
Stefan> +}
Stefan> +
Stefan> +struct address_handler {
Stefan> + struct fw_address_handler handler;
Stefan> + __u64 closure;
Stefan> + struct client *client;
Stefan> + struct client_resource resource;
Stefan> +};
Stefan> +
Stefan> +struct request {
Stefan> + struct fw_request *request;
Stefan> + void *data;
Stefan> + size_t length;
Stefan> + struct client_resource resource;
Stefan> +};
Stefan> +
Stefan> +struct request_event {
Stefan> + struct event event;
Stefan> + struct fw_cdev_event_request request;
Stefan> +};
Stefan> +
Stefan> +static void
Stefan> +release_request(struct client *client, struct client_resource *resource)
Stefan> +{
Stefan> + struct request *request =
Stefan> + container_of(resource, struct request, resource);
Stefan> +
Stefan> + fw_send_response(client->device->card, request->request,
Stefan> + RCODE_CONFLICT_ERROR);
Stefan> + kfree(request);
Stefan> +}
Stefan> +
Stefan> +static void
Stefan> +handle_request(struct fw_card *card, struct fw_request *r,
Stefan> + int tcode, int destination, int source,
Stefan> + int generation, int speed,
Stefan> + unsigned long long offset,
Stefan> + void *payload, size_t length, void *callback_data)
Stefan> +{
Stefan> + struct address_handler *handler = callback_data;
Stefan> + struct request *request;
Stefan> + struct request_event *e;
Stefan> + struct client *client = handler->client;
Stefan> +
Stefan> + request = kmalloc(sizeof *request, GFP_ATOMIC);
Stefan> + e = kmalloc(sizeof *e, GFP_ATOMIC);
Stefan> + if (request == NULL || e == NULL) {
Stefan> + kfree(request);
Stefan> + kfree(e);
Stefan> + fw_send_response(card, r, RCODE_CONFLICT_ERROR);
Stefan> + return;
Stefan> + }
Stefan> +
Stefan> + request->request = r;
Stefan> + request->data = payload;
Stefan> + request->length = length;
Stefan> +
Stefan> + request->resource.release = release_request;
Stefan> + add_client_resource(client, &request->resource);
Stefan> +
Stefan> + e->request.type = FW_CDEV_EVENT_REQUEST;
Stefan> + e->request.tcode = tcode;
Stefan> + e->request.offset = offset;
Stefan> + e->request.length = length;
Stefan> + e->request.handle = request->resource.handle;
Stefan> + e->request.closure = handler->closure;
Stefan> +
Stefan> + queue_event(client, &e->event,
Stefan> + &e->request, sizeof e->request, payload, length);
Stefan> +}
Stefan> +
Stefan> +static void
Stefan> +release_address_handler(struct client *client,
Stefan> + struct client_resource *resource)
Stefan> +{
Stefan> + struct address_handler *handler =
Stefan> + container_of(resource, struct address_handler, resource);
Stefan> +
Stefan> + fw_core_remove_address_handler(&handler->handler);
Stefan> + kfree(handler);
Stefan> +}
Stefan> +
Stefan> +static int ioctl_allocate(struct client *client, void *buffer)
Stefan> +{
Stefan> + struct fw_cdev_allocate *request = buffer;
Stefan> + struct address_handler *handler;
Stefan> + struct fw_address_region region;
Stefan> +
Stefan> + handler = kmalloc(sizeof *handler, GFP_KERNEL);
Stefan> + if (handler == NULL)
Stefan> + return -ENOMEM;
Stefan> +
Stefan> + region.start = request->offset;
Stefan> + region.end = request->offset + request->length;
Stefan> + handler->handler.length = request->length;
Stefan> + handler->handler.address_callback = handle_request;
Stefan> + handler->handler.callback_data = handler;
Stefan> + handler->closure = request->closure;
Stefan> + handler->client = client;
Stefan> +
Stefan> + if (fw_core_add_address_handler(&handler->handler, ®ion) < 0) {
Stefan> + kfree(handler);
Stefan> + return -EBUSY;
Stefan> + }
Stefan> +
Stefan> + handler->resource.release = release_address_handler;
Stefan> + add_client_resource(client, &handler->resource);
Stefan> + request->handle = handler->resource.handle;
Stefan> +
Stefan> + return 0;
Stefan> +}
Stefan> +
Stefan> +static int ioctl_deallocate(struct client *client, void *buffer)
Stefan> +{
Stefan> + struct fw_cdev_deallocate *request = buffer;
Stefan> +
Stefan> + return release_client_resource(client, request->handle, NULL);
Stefan> +}
Stefan> +
Stefan> +static int ioctl_send_response(struct client *client, void *buffer)
Stefan> +{
Stefan> + struct fw_cdev_send_response *request = buffer;
Stefan> + struct client_resource *resource;
Stefan> + struct request *r;
Stefan> +
Stefan> + if (release_client_resource(client, request->handle, &resource) < 0)
Stefan> + return -EINVAL;
Stefan> + r = container_of(resource, struct request, resource);
Stefan> + if (request->length < r->length)
Stefan> + r->length = request->length;
Stefan> + if (copy_from_user(r->data, u64_to_uptr(request->data), r->length))
Stefan> + return -EFAULT;
Stefan> +
Stefan> + fw_send_response(client->device->card, r->request, request->rcode);
Stefan> + kfree(r);
Stefan> +
Stefan> + return 0;
Stefan> +}
Stefan> +
Stefan> +static int ioctl_initiate_bus_reset(struct client *client, void *buffer)
Stefan> +{
Stefan> + struct fw_cdev_initiate_bus_reset *request = buffer;
Stefan> + int short_reset;
Stefan> +
Stefan> + short_reset = (request->type == FW_CDEV_SHORT_RESET);
Stefan> +
Stefan> + return fw_core_initiate_bus_reset(client->device->card, short_reset);
Stefan> +}
Stefan> +
Stefan> +struct descriptor {
Stefan> + struct fw_descriptor d;
Stefan> + struct client_resource resource;
Stefan> + u32 data[0];
Stefan> +};
Stefan> +
Stefan> +static void release_descriptor(struct client *client,
Stefan> + struct client_resource *resource)
Stefan> +{
Stefan> + struct descriptor *descriptor =
Stefan> + container_of(resource, struct descriptor, resource);
Stefan> +
Stefan> + fw_core_remove_descriptor(&descriptor->d);
Stefan> + kfree(descriptor);
Stefan> +}
Stefan> +
Stefan> +static int ioctl_add_descriptor(struct client *client, void *buffer)
Stefan> +{
Stefan> + struct fw_cdev_add_descriptor *request = buffer;
Stefan> + struct descriptor *descriptor;
Stefan> + int retval;
Stefan> +
Stefan> + if (request->length > 256)
Stefan> + return -EINVAL;
Stefan> +
Stefan> + descriptor =
Stefan> + kmalloc(sizeof *descriptor + request->length * 4, GFP_KERNEL);
Stefan> + if (descriptor == NULL)
Stefan> + return -ENOMEM;
Stefan> +
Stefan> + if (copy_from_user(descriptor->data,
Stefan> + u64_to_uptr(request->data), request->length * 4)) {
Stefan> + kfree(descriptor);
Stefan> + return -EFAULT;
Stefan> + }
Stefan> +
Stefan> + descriptor->d.length = request->length;
Stefan> + descriptor->d.immediate = request->immediate;
Stefan> + descriptor->d.key = request->key;
Stefan> + descriptor->d.data = descriptor->data;
Stefan> +
Stefan> + retval = fw_core_add_descriptor(&descriptor->d);
Stefan> + if (retval < 0) {
Stefan> + kfree(descriptor);
Stefan> + return retval;
Stefan> + }
Stefan> +
Stefan> + descriptor->resource.release = release_descriptor;
Stefan> + add_client_resource(client, &descriptor->resource);
Stefan> + request->handle = descriptor->resource.handle;
Stefan> +
Stefan> + return 0;
Stefan> +}
Stefan> +
Stefan> +static int ioctl_remove_descriptor(struct client *client, void *buffer)
Stefan> +{
Stefan> + struct fw_cdev_remove_descriptor *request = buffer;
Stefan> +
Stefan> + return release_client_resource(client, request->handle, NULL);
Stefan> +}
Stefan> +
Stefan> +static void
Stefan> +iso_callback(struct fw_iso_context *context, u32 cycle,
Stefan> + size_t header_length, void *header, void *data)
Stefan> +{
Stefan> + struct client *client = data;
Stefan> + struct iso_interrupt *interrupt;
Stefan> +
Stefan> + interrupt = kzalloc(sizeof *interrupt + header_length, GFP_ATOMIC);
Stefan> + if (interrupt == NULL)
Stefan> + return;
Stefan> +
Stefan> + interrupt->interrupt.type = FW_CDEV_EVENT_ISO_INTERRUPT;
Stefan> + interrupt->interrupt.closure = client->iso_closure;
Stefan> + interrupt->interrupt.cycle = cycle;
Stefan> + interrupt->interrupt.header_length = header_length;
Stefan> + memcpy(interrupt->interrupt.header, header, header_length);
Stefan> + queue_event(client, &interrupt->event,
Stefan> + &interrupt->interrupt,
Stefan> + sizeof interrupt->interrupt + header_length, NULL, 0);
Stefan> +}
Stefan> +
Stefan> +static int ioctl_create_iso_context(struct client *client, void *buffer)
Stefan> +{
Stefan> + struct fw_cdev_create_iso_context *request = buffer;
Stefan> +
Stefan> + if (request->channel > 63)
Stefan> + return -EINVAL;
Stefan> +
Stefan> + switch (request->type) {
Stefan> + case FW_ISO_CONTEXT_RECEIVE:
Stefan> + if (request->header_size < 4 || (request->header_size & 3))
Stefan> + return -EINVAL;
Stefan> +
Stefan> + break;
Stefan> +
Stefan> + case FW_ISO_CONTEXT_TRANSMIT:
Stefan> + if (request->speed > SCODE_3200)
Stefan> + return -EINVAL;
Stefan> +
Stefan> + break;
Stefan> +
Stefan> + default:
Stefan> + return -EINVAL;
Stefan> + }
Stefan> +
Stefan> + client->iso_closure = request->closure;
Stefan> + client->iso_context = fw_iso_context_create(client->device->card,
Stefan> + request->type,
Stefan> + request->channel,
Stefan> + request->speed,
Stefan> + request->header_size,
Stefan> + iso_callback, client);
Stefan> + if (IS_ERR(client->iso_context))
Stefan> + return PTR_ERR(client->iso_context);
Stefan> +
Stefan> + /* We only support one context at this time. */
Stefan> + request->handle = 0;
Stefan> +
Stefan> + return 0;
Stefan> +}
Stefan> +
Stefan> +static int ioctl_queue_iso(struct client *client, void *buffer)
Stefan> +{
Stefan> + struct fw_cdev_queue_iso *request = buffer;
Stefan> + struct fw_cdev_iso_packet __user *p, *end, *next;
Stefan> + struct fw_iso_context *ctx = client->iso_context;
Stefan> + unsigned long payload, buffer_end, header_length;
Stefan> + int count;
Stefan> + struct {
Stefan> + struct fw_iso_packet packet;
Stefan> + u8 header[256];
Stefan> + } u;
Stefan> +
Stefan> + if (ctx == NULL || request->handle != 0)
Stefan> + return -EINVAL;
Stefan> +
Stefan> + /* If the user passes a non-NULL data pointer, has mmap()'ed
Stefan> + * the iso buffer, and the pointer points inside the buffer,
Stefan> + * we setup the payload pointers accordingly. Otherwise we
Stefan> + * set them both to 0, which will still let packets with
Stefan> + * payload_length == 0 through. In other words, if no packets
Stefan> + * use the indirect payload, the iso buffer need not be mapped
Stefan> + * and the request->data pointer is ignored.*/
Stefan> +
Stefan> + payload = (unsigned long)request->data - client->vm_start;
Stefan> + buffer_end = client->buffer.page_count << PAGE_SHIFT;
Stefan> + if (request->data == 0 || client->buffer.pages == NULL ||
Stefan> + payload >= buffer_end) {
Stefan> + payload = 0;
Stefan> + buffer_end = 0;
Stefan> + }
Stefan> +
Stefan> + if (!access_ok(VERIFY_READ, request->packets, request->size))
Stefan> + return -EFAULT;
Stefan> +
Stefan> + p = (struct fw_cdev_iso_packet __user *)u64_to_uptr(request->packets);
Stefan> + end = (void __user *)p + request->size;
Stefan> + count = 0;
Stefan> + while (p < end) {
Stefan> + if (__copy_from_user(&u.packet, p, sizeof *p))
Stefan> + return -EFAULT;
Stefan> +
Stefan> + if (ctx->type == FW_ISO_CONTEXT_TRANSMIT) {
Stefan> + header_length = u.packet.header_length;
Stefan> + } else {
Stefan> + /* We require that header_length is a multiple of
Stefan> + * the fixed header size, ctx->header_size */
Stefan> + if (ctx->header_size == 0) {
Stefan> + if (u.packet.header_length > 0)
Stefan> + return -EINVAL;
Stefan> + } else if (u.packet.header_length % ctx->header_size != 0) {
Stefan> + return -EINVAL;
Stefan> + }
Stefan> + header_length = 0;
Stefan> + }
Stefan> +
Stefan> + next = (struct fw_cdev_iso_packet __user *)
Stefan> + &p->header[header_length / 4];
Stefan> + if (next > end)
Stefan> + return -EINVAL;
Stefan> + if (__copy_from_user
Stefan> + (u.packet.header, p->header, header_length))
Stefan> + return -EFAULT;
Stefan> + if (u.packet.skip && ctx->type == FW_ISO_CONTEXT_TRANSMIT &&
Stefan> + u.packet.header_length + u.packet.payload_length > 0)
Stefan> + return -EINVAL;
Stefan> + if (payload + u.packet.payload_length > buffer_end)
Stefan> + return -EINVAL;
Stefan> +
Stefan> + if (fw_iso_context_queue(ctx, &u.packet,
Stefan> + &client->buffer, payload))
Stefan> + break;
Stefan> +
Stefan> + p = next;
Stefan> + payload += u.packet.payload_length;
Stefan> + count++;
Stefan> + }
Stefan> +
Stefan> + request->size -= uptr_to_u64(p) - request->packets;
Stefan> + request->packets = uptr_to_u64(p);
Stefan> + request->data = client->vm_start + payload;
Stefan> +
Stefan> + return count;
Stefan> +}
Stefan> +
Stefan> +static int ioctl_start_iso(struct client *client, void *buffer)
Stefan> +{
Stefan> + struct fw_cdev_start_iso *request = buffer;
Stefan> +
Stefan> + if (request->handle != 0)
Stefan> + return -EINVAL;
Stefan> + if (client->iso_context->type == FW_ISO_CONTEXT_RECEIVE) {
Stefan> + if (request->tags == 0 || request->tags > 15)
Stefan> + return -EINVAL;
Stefan> +
Stefan> + if (request->sync > 15)
Stefan> + return -EINVAL;
Stefan> + }
Stefan> +
Stefan> + return fw_iso_context_start(client->iso_context, request->cycle,
Stefan> + request->sync, request->tags);
Stefan> +}
Stefan> +
Stefan> +static int ioctl_stop_iso(struct client *client, void *buffer)
Stefan> +{
Stefan> + struct fw_cdev_stop_iso *request = buffer;
Stefan> +
Stefan> + if (request->handle != 0)
Stefan> + return -EINVAL;
Stefan> +
Stefan> + return fw_iso_context_stop(client->iso_context);
Stefan> +}
Stefan> +
Stefan> +static int (* const ioctl_handlers[])(struct client *client, void *buffer) = {
Stefan> + ioctl_get_info,
Stefan> + ioctl_send_request,
Stefan> + ioctl_allocate,
Stefan> + ioctl_deallocate,
Stefan> + ioctl_send_response,
Stefan> + ioctl_initiate_bus_reset,
Stefan> + ioctl_add_descriptor,
Stefan> + ioctl_remove_descriptor,
Stefan> + ioctl_create_iso_context,
Stefan> + ioctl_queue_iso,
Stefan> + ioctl_start_iso,
Stefan> + ioctl_stop_iso,
Stefan> +};
Stefan> +
Stefan> +static int
Stefan> +dispatch_ioctl(struct client *client, unsigned int cmd, void __user *arg)
Stefan> +{
Stefan> + char buffer[256];
Stefan> + int retval;
Stefan> +
Stefan> + if (_IOC_TYPE(cmd) != '#' ||
Stefan> + _IOC_NR(cmd) >= ARRAY_SIZE(ioctl_handlers))
Stefan> + return -EINVAL;
Stefan> +
Stefan> + if (_IOC_DIR(cmd) & _IOC_WRITE) {
Stefan> + if (_IOC_SIZE(cmd) > sizeof buffer ||
Stefan> + copy_from_user(buffer, arg, _IOC_SIZE(cmd)))
Stefan> + return -EFAULT;
Stefan> + }
Stefan> +
Stefan> + retval = ioctl_handlers[_IOC_NR(cmd)](client, buffer);
Stefan> + if (retval < 0)
Stefan> + return retval;
Stefan> +
Stefan> + if (_IOC_DIR(cmd) & _IOC_READ) {
Stefan> + if (_IOC_SIZE(cmd) > sizeof buffer ||
Stefan> + copy_to_user(arg, buffer, _IOC_SIZE(cmd)))
Stefan> + return -EFAULT;
Stefan> + }
Stefan> +
Stefan> + return 0;
Stefan> +}
Stefan> +
Stefan> +static long
Stefan> +fw_device_op_ioctl(struct file *file,
Stefan> + unsigned int cmd, unsigned long arg)
Stefan> +{
Stefan> + struct client *client = file->private_data;
Stefan> +
Stefan> + return dispatch_ioctl(client, cmd, (void __user *) arg);
Stefan> +}
Stefan> +
Stefan> +#ifdef CONFIG_COMPAT
Stefan> +static long
Stefan> +fw_device_op_compat_ioctl(struct file *file,
Stefan> + unsigned int cmd, unsigned long arg)
Stefan> +{
Stefan> + struct client *client = file->private_data;
Stefan> +
Stefan> + return dispatch_ioctl(client, cmd, compat_ptr(arg));
Stefan> +}
Stefan> +#endif
Stefan> +
Stefan> +static int fw_device_op_mmap(struct file *file, struct vm_area_struct *vma)
Stefan> +{
Stefan> + struct client *client = file->private_data;
Stefan> + enum dma_data_direction direction;
Stefan> + unsigned long size;
Stefan> + int page_count, retval;
Stefan> +
Stefan> + /* FIXME: We could support multiple buffers, but we don't. */
Stefan> + if (client->buffer.pages != NULL)
Stefan> + return -EBUSY;
Stefan> +
Stefan> + if (!(vma->vm_flags & VM_SHARED))
Stefan> + return -EINVAL;
Stefan> +
Stefan> + if (vma->vm_start & ~PAGE_MASK)
Stefan> + return -EINVAL;
Stefan> +
Stefan> + client->vm_start = vma->vm_start;
Stefan> + size = vma->vm_end - vma->vm_start;
Stefan> + page_count = size >> PAGE_SHIFT;
Stefan> + if (size & ~PAGE_MASK)
Stefan> + return -EINVAL;
Stefan> +
Stefan> + if (vma->vm_flags & VM_WRITE)
Stefan> + direction = DMA_TO_DEVICE;
Stefan> + else
Stefan> + direction = DMA_FROM_DEVICE;
Stefan> +
Stefan> + retval = fw_iso_buffer_init(&client->buffer, client->device->card,
Stefan> + page_count, direction);
Stefan> + if (retval < 0)
Stefan> + return retval;
Stefan> +
Stefan> + retval = fw_iso_buffer_map(&client->buffer, vma);
Stefan> + if (retval < 0)
Stefan> + fw_iso_buffer_destroy(&client->buffer, client->device->card);
Stefan> +
Stefan> + return retval;
Stefan> +}
Stefan> +
Stefan> +static int fw_device_op_release(struct inode *inode, struct file *file)
Stefan> +{
Stefan> + struct client *client = file->private_data;
Stefan> + struct event *e, *next_e;
Stefan> + struct client_resource *r, *next_r;
Stefan> + unsigned long flags;
Stefan> +
Stefan> + if (client->buffer.pages)
Stefan> + fw_iso_buffer_destroy(&client->buffer, client->device->card);
Stefan> +
Stefan> + if (client->iso_context)
Stefan> + fw_iso_context_destroy(client->iso_context);
Stefan> +
Stefan> + list_for_each_entry_safe(r, next_r, &client->resource_list, link)
Stefan> + r->release(client, r);
Stefan> +
Stefan> + /* FIXME: We should wait for the async tasklets to stop
Stefan> + * running before freeing the memory. */
Stefan> +
Stefan> + list_for_each_entry_safe(e, next_e, &client->event_list, link)
Stefan> + kfree(e);
Stefan> +
Stefan> + spin_lock_irqsave(&client->device->card->lock, flags);
Stefan> + list_del(&client->link);
Stefan> + spin_unlock_irqrestore(&client->device->card->lock, flags);
Stefan> +
Stefan> + fw_device_put(client->device);
Stefan> + kfree(client);
Stefan> +
Stefan> + return 0;
Stefan> +}
Stefan> +
Stefan> +static unsigned int fw_device_op_poll(struct file *file, poll_table * pt)
Stefan> +{
Stefan> + struct client *client = file->private_data;
Stefan> + unsigned int mask = 0;
Stefan> +
Stefan> + poll_wait(file, &client->wait, pt);
Stefan> +
Stefan> + if (fw_device_is_shutdown(client->device))
Stefan> + mask |= POLLHUP | POLLERR;
Stefan> + if (!list_empty(&client->event_list))
Stefan> + mask |= POLLIN | POLLRDNORM;
Stefan> +
Stefan> + return mask;
Stefan> +}
Stefan> +
Stefan> +const struct file_operations fw_device_ops = {
Stefan> + .owner = THIS_MODULE,
Stefan> + .open = fw_device_op_open,
Stefan> + .read = fw_device_op_read,
Stefan> + .unlocked_ioctl = fw_device_op_ioctl,
Stefan> + .poll = fw_device_op_poll,
Stefan> + .release = fw_device_op_release,
Stefan> + .mmap = fw_device_op_mmap,
Stefan> +
Stefan> +#ifdef CONFIG_COMPAT
Stefan> + .compat_ioctl = fw_device_op_compat_ioctl,
Stefan> +#endif
Stefan> +};
Stefan> --
Stefan> Stefan Richter
Stefan> -=====-=-=== -=-= ---=-
Stefan> http://arcgraph.de/sr/
Stefan> -
Stefan> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
Stefan> the body of a message to [email protected]
Stefan> More majordomo info at http://vger.kernel.org/majordomo-info.html
Stefan> Please read the FAQ at http://www.tux.org/lkml/
Stefan> !DSPAM:463882d8304681268012190!
On 5/2/07, Stefan Richter <[email protected]> wrote:
> +/* The lib/crc16.c implementation uses the standard (0x8005)
> + * polynomial, but we need the ITU-T (or CCITT) polynomial (0x1021).
> + * The implementation below works on an array of host-endian u32
> + * words, assuming they'll be transmited msb first. */
> +u16
> +crc16_itu_t(const u32 *buffer, size_t length)
> +{
[snip]
So put it in lib/crc-itu-t.c? Btw, there's apparently another ITU-T
implementation floating around by Ivo van Doorn.
John Stoffel wrote:
>>>>>> "Stefan" == Stefan Richter <[email protected]> writes:
[...]
Hmm, I'm just spotting some artifacts after a recent file-renaming patch:
> Stefan> --- /dev/null
> Stefan> +++ linux_juju/include/linux/firewire-cdev.h
^^^^^^^^^^^^^^^
> Stefan> @@ -0,0 +1,268 @@
> Stefan> +/* -*- c-basic-offset: 8 -*-
> Stefan> + *
> Stefan> + * fw-device-cdev.h -- Char device interface.
^^^^^^^^^^^^^^^^
[...]
> Stefan> +#ifndef __fw_cdev_h
> Stefan> +#define __fw_cdev_h
^^^^^^^^^^^
[...]
> Stefan> +#define TCODE_WRITE_QUADLET_REQUEST 0
> Stefan> +#define TCODE_WRITE_BLOCK_REQUEST 1
[...]
> Stefan> +#define SCODE_100 0x0
> Stefan> +#define SCODE_200 0x1
> Stefan> +#define SCODE_400 0x2
> Stefan> +#define SCODE_800 0x3
> Stefan> +#define SCODE_1600 0x4
> Stefan> +#define SCODE_3200 0x5
>
> These are also defined in fw-transaction.h, though that file doesn't
> have all the values. Can these just be combined into a single
> fw-constants.h file instead?
>
> I honestly haven't checked all your defines....
[...]
Right, there are some overlaps...
Note, firewire-cdev.h declares a userspace ABI, while fw-transaction.h
declares an in-kernel API. I'm not quite sure right now how it should
rather be organized. Maybe rename fw-transaction.h's macros. Only
fw-cdev.c (which implements the userspace ABI) includes both header files.
(PS: I'm not the author; only subsystem maintainer.)
--
Stefan Richter
-=====-=-=== -=-= ---=-
http://arcgraph.de/sr/
I wrote:
> Signed-off-by: Stefan Richter <[email protected]>
PS, just to avoid misunderstandings: The "From:" and "Signed-off-by:" in
the messages 1/6...6/6 don't signify authorship. Authorship information
can be found in linux1394-2.6.git.
--
Stefan Richter
-=====-=-=== -=-= ---=-
http://arcgraph.de/sr/
Gene Heskett wrote:
> On Wednesday 02 May 2007, Stefan Richter wrote:
>> Olaf Hering wrote:
>>> NACK.
>>> Upgrade the current drivers/ieee1394/ with the new code,
>>> and keep all existing module names.
>> I'm impartial to that. Using same names might ease the transition from
>> the userspace side, as far as there is userland which relies on module
>> names.
>>
>> A certain drawback of same names would be that geeks cannot install both
>> stacks at once during the transition period.
[...]
> So I'd vote unconditionally to have 2 trees to select
> from at module load time until the shakeout has produced usable code in the
> 2nd tree.
[...]
Adrian Bunk wrote:
> An advantage of changing the names is that they are now prefixed.
> But looking at them, there will again be the point whether everyone will
> think that "fw" is firmware, and perhaps switching to the (although
> longer) prefix "firewire" might make sense?
Whatever names the FireWire modules will get, we shouldn't change the
names anymore _after_ the new code appears in mainline.
Anyway, if the same names really should be used, as Olaf demands, then
it affects at most ohci1394, sbp2, eth1394, ieee1394. However, ieee1394
is probably never loaded explicitly by scripts (only auto-loaded per
dependency of the others), and the other three all have proper module
aliases.
If the new modules are meant to look like the old ones, then there is
also the problem with module parameters. Many of the old stack's module
parameters are merely there as hacks or workarounds though; therefore
they shouldn't appear in shipped scripts and shipped configurations anyway.
--
Stefan Richter
-=====-=-=== -=-= ---=-
http://arcgraph.de/sr/
Pekka Enberg wrote:
> On 5/2/07, Stefan Richter <[email protected]> wrote:
>> +/* The lib/crc16.c implementation uses the standard (0x8005)
>> + * polynomial, but we need the ITU-T (or CCITT) polynomial (0x1021).
>> + * The implementation below works on an array of host-endian u32
>> + * words, assuming they'll be transmited msb first. */
>> +u16
>> +crc16_itu_t(const u32 *buffer, size_t length)
>> +{
>
> [snip]
>
> So put it in lib/crc-itu-t.c? Btw, there's apparently another ITU-T
> implementation floating around by Ivo van Doorn.
I looked around a bit with grep -R and a few search terms but didn't
find something definite. Is there any other user of a crc16_itu_t or
crc_ccitt or whatever which operates on a (CPU byte ordered) u32[]
instead of on a (network byte ordered) u8[]?
The only value in having a shared implementation would be a potentially
smaller kernel. Sharing it to ensure correctness is not an issue;
fw-topology.c::crc16_itu_t is simply the one in IEEE 1212 table 5.
Performance is also not an issue (if better algorithms exist) because
the FireWire stack uses it only infrequently on a moderate amount of data.
--
Stefan Richter
-=====-=-=== -=-= ---=-
http://arcgraph.de/sr/
On Wed, May 02, 2007 at 02:15:42PM +0200, Stefan Richter wrote:
> +/* -*- c-basic-offset: 8 -*-
Please don't pollute the code with annotation for some editors.
> + * fw-card.c - card level functions
Please don't put the filename into a comment inside the filename.
It's not actually useful and it gets out of date very easily.
> +static DECLARE_RWSEM(card_rwsem);
this one is only ever used with down_read/up_read so a normal
mutex would probably better suited.
> +static LIST_HEAD(card_list);
> +#define bib_pmc ((1) << 27)
> +#define bib_bmc ((1) << 28)
> +#define bib_isc ((1) << 29)
> +#define bib_cmc ((1) << 30)
> +#define bib_imc ((1) << 31)
These are rather odd names for constants. They should at least
start with an uppercase letter if not be all uppercase. Also
an enum might be right here.
> +static u32 *
> +generate_config_rom (struct fw_card *card, size_t *config_rom_length)
Please don't put white spaces after the function identifier.
> + /* Initialize contents of config rom buffer. On the OHCI
> + * controller, block reads to the config rom accesses the host
> + * memory, but quadlet read access the hardware bus info block
> + * registers. That's just crack, but it means we should make
> + * sure the contents of bus info block in host memory mathces
> + * the version stored in the OHCI registers. */
Normal style for block comments is:
/*
* Initialize contents of config rom buffer. On the OHCI
* controller, block reads to the config rom accesses the host
* memory, but quadlet read access the hardware bus info block
* registers. That's just crack, but it means we should make
* sure the contents of bus info block in host memory mathces
* the version stored in the OHCI registers.
*/
> +struct fw_node {
> + u16 node_id;
> + u8 color;
> + u8 port_count;
> + unsigned link_on : 1;
> + unsigned initiated_reset : 1;
> + unsigned b_path : 1;
> + u8 phy_speed : 3; /* As in the self ID packet. */
> + u8 max_speed : 5; /* Minimum of all phy-speeds and port speeds on
> + * the path from the local node to this node. */
> + u8 max_depth : 4; /* Maximum depth to any leaf node */
> + u8 max_hops : 4; /* Max hops in this sub tree */
I don't think we need to save the few bits here and can just use
u8 instead of bitfields.
> + /* Pop the child nodes off the stack and push the new node. */
> + __list_del(h->prev, &stack);
Please don't ever use __list_del directly, it's an interna implementation
detail.
I also notices that close to none of the export functions have kerneldoc
comment blocks. I think we really should have them on something that
is a driver API.
> + for (i = 0; i < buffer->page_count; i++) {
> + buffer->pages[i] = alloc_page(GFP_KERNEL | GFP_DMA32 | __GFP_ZERO);
> + if (buffer->pages[i] == NULL)
> + goto out_pages;
> +
> + address = dma_map_page(card->device, buffer->pages[i],
> + 0, PAGE_SIZE, direction);
> + if (dma_mapping_error(address)) {
> + __free_page(buffer->pages[i]);
> + goto out_pages;
> + }
Are you sure using streaming dma mapping is safe here? I don't see
actual user in this patch, but doing the proper ownership protocol
for them is quite difficult if you reuse them, and allocating them
in kernelspace usually means you want to keep reusing them.
> +#include <linux/kthread.h>
You don't actually seem to use this one ..
> +#include <asm/uaccess.h>
.. or this one ..
> +#include <asm/semaphore.h>
.. or this one.
> + retval = fw_core_add_address_handler(&topology_map,
> + &topology_map_region);
> + BUG_ON(retval < 0);
> +
> + retval = fw_core_add_address_handler(®isters,
> + ®isters_region);
> + BUG_ON(retval < 0);
> +
> + /* Add the vendor textual descriptor. */
> + retval = fw_core_add_descriptor(&vendor_id_descriptor);
> + BUG_ON(retval < 0);
> + retval = fw_core_add_descriptor(&model_id_descriptor);
> + BUG_ON(retval < 0);
These kinds of bug checks look wrong. Either the operations
can't fail in which case they should not return an error value
or you should handle them properly.
Both the previous and this patch contain quite a lot of GFP_ATOMIC
allocation which are a sign of not having a very good layering.
On Wed, May 02, 2007 at 02:17:26PM +0200, Stefan Richter wrote:
> +#include <asm/ioctl.h>
> +#include <asm/types.h>
Always use the <linux/ versions.
> +struct fw_cdev_get_info {
> + /* The version field is just a running serial number. We
> + * never break backwards compatibility. Userspace passes in
> + * the version it expects and the kernel passes back the
> + * highest version it can provide. Even if the structs in
> + * this interface are extended in a later version, the kernel
> + * will not copy back more data than what was present in the
> + * interface version userspace expects. */
> + __u32 version;
Please don't even try to build interfaces this complicated. If your
current interface needs changes at some point just introduce new ioctls.
> + sg = (struct scatterlist *)orb->cmd->request_buffer;
> + count = dma_map_sg(device->card->device, sg, orb->cmd->use_sg,
> + orb->cmd->sc_data_direction);
you need to handle the error case (count == 0)
> + /* Convert the scatterlist to an sbp2 page table. If any
> + * scatterlist entries are too big for sbp2 we split the as we go. */
Please set the max_sectors value in your host template so that the
block layer doesn't build sg entries too big for you.
> + orb->page_table_bus =
> + dma_map_single(device->card->device, orb->page_table,
> + size, DMA_TO_DEVICE);
This needs handling of mapping errors (dma_mapping_error())
> + orb = kzalloc(sizeof *orb, GFP_ATOMIC);
Normal kernel style is sizeof(*orb)
> + if (cmd->use_sg) {
> + sbp2_command_orb_map_scatterlist(orb);
> + } else if (cmd->request_bufflen > SBP2_MAX_SG_ELEMENT_LENGTH) {
> + /* FIXME: Need to split this into a sg list... but
> + * could we get the scsi or blk layer to do that by
> + * reporting our max supported block size? */
> + fw_error("command > 64k\n");
> + goto fail_bufflen;
> + } else if (cmd->request_bufflen > 0) {
> + sbp2_command_orb_map_buffer(orb);
> + }
The use_sg == 0, request_bufflen != 0 case can't happen anymore.
> + fail_mapping:
> + kfree(orb);
> + fail_alloc:
> + cmd->result = DID_ERROR << 16;
> + done(cmd);
Failure due to ressource shortage should not complete the command
but return SCSI_MLQUEUE_HOST_BUSY/SCSI_MLQUEUE_DEVICE_BUSY.
> + return 0;
> +}
> +static struct scsi_host_template scsi_driver_template = {
> + .module = THIS_MODULE,
> + .name = "SBP-2 IEEE-1394",
> + .proc_name = (char *)sbp2_driver_name,
Please don't use casrs here. Either fix up the definition so it
accepts const strings or pass a non-const one.
> +static int add_scsi_devices(struct fw_unit *unit)
> +{
> + struct sbp2_device *sd = unit->device.driver_data;
> + int retval, lun;
> +
> + if (sd->scsi_host != NULL)
> + return 0;
> +
> + sd->scsi_host = scsi_host_alloc(&scsi_driver_template,
> + sizeof(unsigned long));
> + if (sd->scsi_host == NULL) {
> + fw_error("failed to register scsi host\n");
> + return -1;
> + }
> +
> + sd->scsi_host->hostdata[0] = (unsigned long)unit;
Please take a look ar ther other scsi drivers how this is supposed
to be used.
> + retval = scsi_add_host(sd->scsi_host, &unit->device);
> + if (retval < 0) {
> + fw_error("failed to add scsi host\n");
> + scsi_host_put(sd->scsi_host);
> + sd->scsi_host = NULL;
> + return retval;
> + }
> +
> + /* FIXME: Loop over luns here. */
> + lun = 0;
> + retval = scsi_add_device(sd->scsi_host, 0, 0, lun);
> + if (retval < 0) {
> + fw_error("failed to add scsi device\n");
> + scsi_remove_host(sd->scsi_host);
> + scsi_host_put(sd->scsi_host);
> + sd->scsi_host = NULL;
> + return retval;
> + }
> +
> + return 0;
> +}
Do we really need another scanning algorithm? Can't you use
scsi_scan_target instead and let the core scsi code handle the
scanning?
> +
> +static void remove_scsi_devices(struct fw_unit *unit)
> +{
> + struct sbp2_device *sd = unit->device.driver_data;
> +
> + if (sd->scsi_host != NULL) {
> + scsi_remove_host(sd->scsi_host);
> + scsi_host_put(sd->scsi_host);
> + }
> + sd->scsi_host = NULL;
> +}
This function seems rather oddly named. And the checking and
setting of scsi_host looks like you have some lifetime rule
problems.
> +fw-core-objs := fw-card.o fw-topology.o fw-transaction.o fw-iso.o \
> + fw-device.o fw-cdev.o
fw-core-y += ..
Olaf Hering wrote:
> On Tue, May 01, Kristian Høgsberg wrote:
>
>> drivers/firewire/Kconfig | 60 ++
>
> NACK.
> Upgrade the current drivers/ieee1394/ with the new code, and keep all
> existing module names.
What's your reasoning here? Having different module names allows people to
compile both stacks and switch between them as they wish.
Another point in favour of different module names is that the new stack
doesn't actually provide the same user space interfaces as the old stack.
Basically, no applications use the raw kernel interfaces and the new stack is
only compatible at the library level. In the light of this, I think it's fair
to change the module names.
As for putting the new stack in drivers/ieee1394 - I don't know, I think it
makes sense to keep the new stack in it's own directory. If it's a deal
breaker for inclusion, let's talk about moving it, but it would be nice if you
could elaborate just a little bit beyond "NACK".
thanks,
Kristian
On Wed, May 02, Kristian Høgsberg wrote:
> Olaf Hering wrote:
> >On Tue, May 01, Kristian Høgsberg wrote:
> >
> >> drivers/firewire/Kconfig | 60 ++
> >
> >NACK.
> >Upgrade the current drivers/ieee1394/ with the new code, and keep all
> >existing module names.
>
> What's your reasoning here?
Whats the upgrade path for root on sbp2?
Right now mkinitrd puts 'ohci1394 spb2' into the initrd because both
names are stored in a config file. How does one configure that config
file to have a setup that works with kernels which provide only
drivers/ieee1394 and a newer ones which provide only drivers/firewire?
Adrian Bunk wrote:
> On Wed, May 02, 2007 at 02:48:11PM +0200, Stefan Richter wrote:
>> Olaf Hering wrote:
>>> On Tue, May 01, Kristian Høgsberg wrote:
>>>
>>>> drivers/firewire/Kconfig | 60 ++
>>> NACK.
>>> Upgrade the current drivers/ieee1394/ with the new code,
>> Last time I believe I was the only one who asked whether to put it into
>> drivers/ieee1394 instead of another directory. Of course I acknowledge
>> that everytime a new review round is started, people do reconsider.
>> Especially since we had a gap of a few months since the last LKML review.
>>
>>> and keep all existing module names.
>> I'm impartial to that. Using same names might ease the transition from
>> the userspace side, as far as there is userland which relies on module
>> names.
>>
>> A certain drawback of same names would be that geeks cannot install both
>> stacks at once during the transition period. Therefore, checking
>> whether eventual problems are in fact regressions involves a module
>> unload/ configure/ build/ install/ reload cycle, instead of just module
>> unload/ reload. This especially means we can only get help from testers
>> who are able to build kernels.
>>
>> Other opinions?
>
> An advantage of changing the names is that they are now prefixed.
> But looking at them, there will again be the point whether everyone will
> think that "fw" is firmware, and perhaps switching to the (although
> longer) prefix "firewire" might make sense?
I like "firewire" better, I'm already using that for the userspace header
file. Renaming the modules to firewire-core, firewire-ohci and firewire-sbp2
sounds good to me.
Kristian
Stefan Richter wrote:
> Christoph Hellwig wrote:
..
>> Please send out patches for review first.
>
> Yes, it's been a while since the last submission for review [1], and
> most of the changes went over linux1394-devel only. And to put it
> mildly, there aren't a lot of capable reviewers watching that list.
...
> (If this division seems odd, don't blame Kristian, blame me. :-)
> I'm looking forward to comments.
Looks good to me, thanks Stefan. The first three patches don't compile on
their own as they are, but it's a good split of the core stack.
Kristian
On 5/2/07, Stefan Richter <[email protected]> wrote:
> I looked around a bit with grep -R and a few search terms but didn't
> find something definite. Is there any other user of a crc16_itu_t or
> crc_ccitt or whatever which operates on a (CPU byte ordered) u32[]
> instead of on a (network byte ordered) u8[]?
I was referring to this: http://lkml.org/lkml/2006/6/12/137
On 5/2/07, Stefan Richter <[email protected]> wrote:
> The only value in having a shared implementation would be a potentially
> smaller kernel. Sharing it to ensure correctness is not an issue;
> fw-topology.c::crc16_itu_t is simply the one in IEEE 1212 table 5.
> Performance is also not an issue (if better algorithms exist) because
> the FireWire stack uses it only infrequently on a moderate amount of data.
Yeah, it's not a biggie, but we do have a tradition of putting
generally useful things into lib/ so that everyone doesn't invent
their own.
John Stoffel wrote:
>>>>>> "Stefan" == Stefan Richter <[email protected]> writes:
>
> Stefan> Signed-off-by: Stefan Richter <[email protected]>
> Stefan> ---
> Stefan> drivers/firewire/fw-cdev.c | 954 ++++++++++++++++++++++++++++++++++
> Stefan> include/linux/firewire-cdev.h | 268 +++++++++
> Stefan> 2 files changed, 1222 insertions(+)
>
> Stefan> Index: linux_juju/include/linux/firewire-cdev.h
...
> Stefan> +#define RCODE_SEND_ERROR 0x10
> Stefan> +#define RCODE_CANCELLED 0x11
> Stefan> +#define RCODE_BUSY 0x12
> Stefan> +#define RCODE_GENERATION 0x13
> Stefan> +#define RCODE_NO_ACK 0x14
> Stefan> +
> Stefan> +#define SCODE_100 0x0
> Stefan> +#define SCODE_200 0x1
> Stefan> +#define SCODE_400 0x2
> Stefan> +#define SCODE_800 0x3
> Stefan> +#define SCODE_1600 0x4
> Stefan> +#define SCODE_3200 0x5
>
> These are also defined in fw-transaction.h, though that file doesn't
> have all the values. Can these just be combined into a single
> fw-constants.h file instead?
>
> I honestly haven't checked all your defines....
The firewire-cdev.h file is meant to be a self-contained userspace header file
and shouldn't include other kernel header files. All duplicated values are
standardized ieee1394 values and won't ever change. I should put a #ifndef
__FW_COMMON_DEFINES protection around the duplicate values, I guess, but I'm
just wondering why I never saw a "symbol redefined" warning...
Kristian
Pekka Enberg wrote:
> On 5/2/07, Stefan Richter <[email protected]> wrote:
>> +/* The lib/crc16.c implementation uses the standard (0x8005)
>> + * polynomial, but we need the ITU-T (or CCITT) polynomial (0x1021).
>> + * The implementation below works on an array of host-endian u32
>> + * words, assuming they'll be transmited msb first. */
>> +u16
>> +crc16_itu_t(const u32 *buffer, size_t length)
>> +{
>
> [snip]
>
> So put it in lib/crc-itu-t.c? Btw, there's apparently another ITU-T
> implementation floating around by Ivo van Doorn.
I looked into this a while back, and the kernel does have a CCITT based crc16
function. The problem is that it works on the opposite bit order from what I
need, so to use it I'll have to bitswap every byte as it goes in and then
bitswap the resulting crc16. That's more code and probably less efficient
than just implementing the crc itself. I wouldn't mind moving it to lib/
though, but nobody else in the kernel is using this bit order.
Kristian
Christoph Hellwig wrote:
>> + /* Convert the scatterlist to an sbp2 page table. If any
>> + * scatterlist entries are too big for sbp2 we split the as we go. */
>
> Please set the max_sectors value in your host template so that the
> block layer doesn't build sg entries too big for you.
Hmm, what about this:
James Bottomley wrote on 2007-01-15:
| Actually, there's one unfortunate case where Linux won't respect this:
| an IOMMU that can do virtual merging. This parameter is a block queue
| parameter, so block will happily make sure the request segments obey it.
| However, when you get to dma_map_rq() it doesn't see the segment limits,
| so, if the iommu merges, you can end up with SG elements the other side
| that violate this. I've been meaning to do something about this for
| ages (IDE is the other subsystem that has an absolute requirement for a
| fixed maximum segment size) but never found an excuse to fix it.
http://marc.info/?l=linux-scsi&m=116889641203397
>> +static int add_scsi_devices(struct fw_unit *unit)
>> +{
>> + struct sbp2_device *sd = unit->device.driver_data;
>> + int retval, lun;
>> +
>> + if (sd->scsi_host != NULL)
>> + return 0;
>> +
>> + sd->scsi_host = scsi_host_alloc(&scsi_driver_template,
>> + sizeof(unsigned long));
>> + if (sd->scsi_host == NULL) {
>> + fw_error("failed to register scsi host\n");
>> + return -1;
>> + }
>> +
>> + sd->scsi_host->hostdata[0] = (unsigned long)unit;
>
> Please take a look ar ther other scsi drivers how this is supposed
> to be used.
Do you mean the one Scsi_Host per LU? If it is that, then it was just
taken over from drivers/ieee1394/sbp2.c. Sbp2 is doing this still today
mostly for historical reasons; I just didn't find the time yet to try to
get to a leaner scheme.
Or do you mean something else?
>> + retval = scsi_add_host(sd->scsi_host, &unit->device);
>> + if (retval < 0) {
>> + fw_error("failed to add scsi host\n");
>> + scsi_host_put(sd->scsi_host);
>> + sd->scsi_host = NULL;
>> + return retval;
>> + }
>> +
>> + /* FIXME: Loop over luns here. */
>> + lun = 0;
>> + retval = scsi_add_device(sd->scsi_host, 0, 0, lun);
>> + if (retval < 0) {
>> + fw_error("failed to add scsi device\n");
>> + scsi_remove_host(sd->scsi_host);
>> + scsi_host_put(sd->scsi_host);
>> + sd->scsi_host = NULL;
>> + return retval;
>> + }
>> +
>> + return 0;
>> +}
>
> Do we really need another scanning algorithm?
Yes.
> Can't you use scsi_scan_target instead and let the core scsi code
> handle the scanning?
No. The discovery of LUs of SBP-2 targets happens on the IEEE 1212
level of things. The initiator has to parse the configuration ROM of
the target FireWire node; the ROM has entries for each LU. (After that,
SBP-2 login protocol commences for each LU, and only after that can SCSI
requests be issued. There is nothing SCSIish going on before that.)
What's missing as a /* FIXME */ here is actually implemented in the
mainline sbp2.c and needs to be brought over here; converted to the new
FireWire core APIs.
>> +
>> +static void remove_scsi_devices(struct fw_unit *unit)
>> +{
>> + struct sbp2_device *sd = unit->device.driver_data;
>> +
>> + if (sd->scsi_host != NULL) {
>> + scsi_remove_host(sd->scsi_host);
>> + scsi_host_put(sd->scsi_host);
>> + }
>> + sd->scsi_host = NULL;
>> +}
>
> This function seems rather oddly named. And the checking and
> setting of scsi_host looks like you have some lifetime rule
> problems.
>
The NULL probably has to do with the ability to call remove_scsi_devices
in different paths. (These paths are not concurrent.)
--
Stefan Richter
-=====-=-=== -=-= ---=-
http://arcgraph.de/sr/
I wrote:
> Christoph Hellwig wrote:
>>> + /* Convert the scatterlist to an sbp2 page table. If any
>>> + * scatterlist entries are too big for sbp2 we split the as we go. */
>> Please set the max_sectors value in your host template so that the
>> block layer doesn't build sg entries too big for you.
>
> Hmm, what about this:
>
> James Bottomley wrote on 2007-01-15:
> | Actually, there's one unfortunate case where Linux won't respect this:
> | an IOMMU that can do virtual merging. This parameter is a block queue
> | parameter, so block will happily make sure the request segments obey it.
> | However, when you get to dma_map_rq() it doesn't see the segment limits,
> | so, if the iommu merges, you can end up with SG elements the other side
> | that violate this. I've been meaning to do something about this for
> | ages (IDE is the other subsystem that has an absolute requirement for a
> | fixed maximum segment size) but never found an excuse to fix it.
>
> http://marc.info/?l=linux-scsi&m=116889641203397
PS:
Nonetheless, yes, fw-sbp2.c is curiously missing to set the template's
max_sectors.
--
Stefan Richter
-=====-=-=== -=-= ---==
http://arcgraph.de/sr/
Christoph Hellwig wrote:
>> +fw-core-objs := fw-card.o fw-topology.o fw-transaction.o fw-iso.o \
>> + fw-device.o fw-cdev.o
>
> fw-core-y += ..
>
Like such?
--- linux.orig/drivers/usb/core/Makefile
+++ linux/drivers/usb/core/Makefile
@@ -2,17 +2,12 @@
# Makefile for USB Core files and filesystem
#
-usbcore-objs := usb.o hub.o hcd.o urb.o message.o driver.o \
+usbcore-y += usb.o hub.o hcd.o urb.o message.o driver.o \
config.o file.o buffer.o sysfs.o endpoint.o \
devio.o notify.o generic.o quirks.o
-ifeq ($(CONFIG_PCI),y)
- usbcore-objs += hcd-pci.o
-endif
-
-ifeq ($(CONFIG_USB_DEVICEFS),y)
- usbcore-objs += inode.o devices.o
-endif
+usbcore-$(CONFIG_PCI) += hcd-pci.o
+usbcore-$(CONFIG_USB_DEVICEFS) += inode.o devices.o
obj-$(CONFIG_USB) += usbcore.o
--
Stefan Richter
-=====-=-=== -=-= ---==
http://arcgraph.de/sr/
Christoph Hellwig wrote:
>> + for (i = 0; i < buffer->page_count; i++) {
>> + buffer->pages[i] = alloc_page(GFP_KERNEL | GFP_DMA32 | __GFP_ZERO);
>> + if (buffer->pages[i] == NULL)
>> + goto out_pages;
>> +
>> + address = dma_map_page(card->device, buffer->pages[i],
>> + 0, PAGE_SIZE, direction);
>> + if (dma_mapping_error(address)) {
>> + __free_page(buffer->pages[i]);
>> + goto out_pages;
>> + }
>
> Are you sure using streaming dma mapping is safe here? I don't see
> actual user in this patch, but doing the proper ownership protocol
> for them is quite difficult if you reuse them, and allocating them
> in kernelspace usually means you want to keep reusing them.
What other options are there? The only user in the stack is the userspace
interface, which lets you mmap the pages in the iso_buffer from an
application. The pages need to stay around and stay mapped as long as that
buffer is mmapped by userspace. The buffer can be several megabytes and I
don't want to set up a kernel side virtual mapping for it. The pages are only
used for either outgoing or incoming data, never both, so device/driver
ownership isn't too difficult to handle.
>> +#include <linux/kthread.h>
>
> You don't actually seem to use this one ..
>
>> +#include <asm/uaccess.h>
>
> .. or this one ..
>
>> +#include <asm/semaphore.h>
>
> .. or this one.
Ah, right, I'll get rid of those.
>> + retval = fw_core_add_address_handler(&topology_map,
>> + &topology_map_region);
>> + BUG_ON(retval < 0);
>> +
>> + retval = fw_core_add_address_handler(®isters,
>> + ®isters_region);
>> + BUG_ON(retval < 0);
>> +
>> + /* Add the vendor textual descriptor. */
>> + retval = fw_core_add_descriptor(&vendor_id_descriptor);
>> + BUG_ON(retval < 0);
>> + retval = fw_core_add_descriptor(&model_id_descriptor);
>> + BUG_ON(retval < 0);
>
> These kinds of bug checks look wrong. Either the operations
> can't fail in which case they should not return an error value
> or you should handle them properly.
The fw_core_add_descriptor() checks that the descriptor block it's passed is
internally consistent and is used for blocks passed in from userspace too. In
these two cases, the blocks are static const arrays in the driver and if
fw_core_add_descriptor returns < 0 it's a bug in the driver.
> Both the previous and this patch contain quite a lot of GFP_ATOMIC
> allocation which are a sign of not having a very good layering.
Looking through the GFP_ATOMIC allocations, I see a couple that could be
rolled back to GFP_KERNEL. But I don't know that it means bad layering, I'm
just typically using a GFP_ATOMIC kmalloc than, preallocating some fixed
number of, say, packets or nodes or whatever. For example, the old SBP-2
(storage) driver uses a free-list of packets and grabs one from that list when
the SCSI stacks asks it to send a request. If that list is empty, it fails
and lets the SCSI stack retry the command. I'm using a GFP_ATOMIC kmalloc
instead in that case, and I believe it's a better approach than implementing
ad-hoc allocation data structures.
Thanks for the reviews, I'll look through your other emails.
Kristian
Kritian wrote:
> Olaf Hering wrote:
> > On Tue, May 01, Kristian H?gsberg wrote:
> >
> >> drivers/firewire/Kconfig | 60 ++
> >
> > NACK.
> > Upgrade the current drivers/ieee1394/ with the new code, and keep all
> > existing module names.
>
> What's your reasoning here? Having different module names allows people to
> compile both stacks and switch between them as they wish.
While I'm not fussed about the implementation details I agree with those
who have advocated a migration period where both stacks are present. A
major change such as this is almost certain to turn up bugs when it becomes
more widely tested, and many firewire users are unlikely to test the
new stack without an easy fallback to a known working system. Yes, I know
development and production systems should be separate, but I (and many
others) can't afford enough hardware for that.
> Another point in favour of different module names is that the new stack
> doesn't actually provide the same user space interfaces as the old stack.
> Basically, no applications use the raw kernel interfaces and the new stack
> is only compatible at the library level. In the light of this, I think
> it's fair to change the module names.
Sounds reasonable to me.
However, as a compromise how about renaming the existing stack's modules and
then reusing the existing names for the new stack? Messy I know, but this
way both stacks would still be available without recompilation for those who
needed them and the sbp2-as-root dilemma raised by Olaf would also be
covered.
> As for putting the new stack in drivers/ieee1394 - I don't know, I think it
> makes sense to keep the new stack in it's own directory.
My immediate thought it that it would be neater and clearer to have both
stacks in different directories, but I could live with either.
Oh yes, it would be nice to have working PCILynx support again (although I
acknowledge it's unlikely to happen). Some of us do have these cards
installed for sniffing purposes (using nosy) but it would be nice to be able
to use them with libraw1394 as well. It would for example save me having to
swap cards depending on what I needed to do (I have insufficient PCI slots
to have both the PCILynx and OHCI cards installed simultaneously).
Regards
jonathan
On Thu, May 03, 2007 at 01:01:08AM +0200, Stefan Richter wrote:
> Christoph Hellwig wrote:
> >> +fw-core-objs := fw-card.o fw-topology.o fw-transaction.o fw-iso.o \
> >> + fw-device.o fw-cdev.o
> >
> > fw-core-y += ..
> >
>
> Like such?
Yes - the latter is much more readable.
Sam
>
> --- linux.orig/drivers/usb/core/Makefile
> +++ linux/drivers/usb/core/Makefile
> @@ -2,17 +2,12 @@
> # Makefile for USB Core files and filesystem
> #
>
> -usbcore-objs := usb.o hub.o hcd.o urb.o message.o driver.o \
> +usbcore-y += usb.o hub.o hcd.o urb.o message.o driver.o \
> config.o file.o buffer.o sysfs.o endpoint.o \
> devio.o notify.o generic.o quirks.o
>
> -ifeq ($(CONFIG_PCI),y)
> - usbcore-objs += hcd-pci.o
> -endif
> -
> -ifeq ($(CONFIG_USB_DEVICEFS),y)
> - usbcore-objs += inode.o devices.o
> -endif
> +usbcore-$(CONFIG_PCI) += hcd-pci.o
> +usbcore-$(CONFIG_USB_DEVICEFS) += inode.o devices.o
>
> obj-$(CONFIG_USB) += usbcore.o
>
>
> --
> Stefan Richter
> -=====-=-=== -=-= ---==
> http://arcgraph.de/sr/
> -
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at http://www.tux.org/lkml/
On Thu, May 03, 2007 at 01:01:08AM +0200, Stefan Richter wrote:
> Christoph Hellwig wrote:
> >> +fw-core-objs := fw-card.o fw-topology.o fw-transaction.o fw-iso.o \
> >> + fw-device.o fw-cdev.o
> >
> > fw-core-y += ..
> >
>
> Like such?
Exactly.
> --- linux.orig/drivers/usb/core/Makefile
> +++ linux/drivers/usb/core/Makefile
> @@ -2,17 +2,12 @@
> # Makefile for USB Core files and filesystem
> #
>
> -usbcore-objs := usb.o hub.o hcd.o urb.o message.o driver.o \
> +usbcore-y += usb.o hub.o hcd.o urb.o message.o driver.o \
> config.o file.o buffer.o sysfs.o endpoint.o \
> devio.o notify.o generic.o quirks.o
>
> -ifeq ($(CONFIG_PCI),y)
> - usbcore-objs += hcd-pci.o
> -endif
> -
> -ifeq ($(CONFIG_USB_DEVICEFS),y)
> - usbcore-objs += inode.o devices.o
> -endif
> +usbcore-$(CONFIG_PCI) += hcd-pci.o
> +usbcore-$(CONFIG_USB_DEVICEFS) += inode.o devices.o
>
> obj-$(CONFIG_USB) += usbcore.o
Jonathan Woithe wrote:
>> Olaf Hering wrote:
>>> NACK.
>>> Upgrade the current drivers/ieee1394/ with the new code, and keep all
>>> existing module names.
[...]
> However, as a compromise how about renaming the existing stack's modules and
> then reusing the existing names for the new stack? Messy I know, but this
> way both stacks would still be available without recompilation for those who
> needed them and the sbp2-as-root dilemma raised by Olaf would also be
> covered.
I.e. new modules:
ieee1394 (was fw-core)
ohci1394 (was fw-ohci)
sbp2 (was fw-sbp2)
eth1394 (to be done) --- but that's a bad name anyway, it
implements IP over 1394, not Ethernet
old modules, for example:
ieee1394-old
ohci1394-old
sbp2-old
eth1394-old
pcilynx (unless somebody plans to port it, then pcilynx-old)
raw1394 (or rename it too?, it requires ieee1394-old)
video1394 (ditto)
dv1394 (ditto)
Looks... weird.
On the other hand, a 1394 module compilation cycle in order to do the
fallback is not such a huge issue, except that it requires the person to
be able to compile modules. That's probably the main issue.
>> As for putting the new stack in drivers/ieee1394 - I don't know, I think it
>> makes sense to keep the new stack in it's own directory.
>
> My immediate thought it that it would be neater and clearer to have both
> stacks in different directories, but I could live with either.
We could rename some of the existing source files to make the
distinction clearer.
> Oh yes, it would be nice to have working PCILynx support again (although I
> acknowledge it's unlikely to happen). Some of us do have these cards
> installed for sniffing purposes (using nosy) but it would be nice to be able
> to use them with libraw1394 as well. It would for example save me having to
> swap cards depending on what I needed to do (I have insufficient PCI slots
> to have both the PCILynx and OHCI cards installed simultaneously).
But then, what is the actual utility of pcilynx? (I mean the current
driver, not the card or a future driver.) Last time I checked, sbp2 was
broken without OHCI's physical DMA, and AFAIK raw1394's newer iso API
and video1394 and dv1394 don't work with pcilynx either.
Porting pcilynx to the new low-level API would be quite resource
demanding --- seen in relation to which resources we have, what the
existing pcilynx driver's state of affairs is, and how rare the hardware
is. (For those who have the hardware, the stand-alone Nosy is
undoubtedly the killer application, not pcilynx.)
--
Stefan Richter
-=====-=-=== -=-= ---==
http://arcgraph.de/sr/
Kristian Høgsberg wrote:
> Christoph Hellwig wrote:
[...]
>>> + retval = fw_core_add_descriptor(&model_id_descriptor);
>>> + BUG_ON(retval < 0);
>>
>> These kinds of bug checks look wrong. Either the operations
>> can't fail in which case they should not return an error value
>> or you should handle them properly.
>
> The fw_core_add_descriptor() checks that the descriptor block it's passed is
> internally consistent and is used for blocks passed in from userspace too. In
> these two cases, the blocks are static const arrays in the driver and if
> fw_core_add_descriptor returns < 0 it's a bug in the driver.
When you submitted it, I too wondered whether these BUG_ONs are correct
(they are) and whether they are worth having (not so sure).
A plus of a smaller stack is that we need less assertions to guard its
integrity. :-)
--
Stefan Richter
-=====-=-=== -=-= ---==
http://arcgraph.de/sr/
On Thu, May 03, Stefan Richter wrote:
> ieee1394-old
Noone will seriously ship two firewire stacks, so that cant be the
issue (for distributors).
Once there is a way to easily switch between kernel releases, I'm ok
with whatever module names you pick.
Olaf Hering wrote:
> On Thu, May 03, Stefan Richter wrote:
>
>> ieee1394-old
>
> Noone will seriously ship two firewire stacks, so that cant be the
> issue (for distributors).
I don't actively watch distributions, but I believe there are frequently
alternative drivers shipped. How much sense that makes for FireWire is
another question.
> Once there is a way to easily switch between kernel releases, I'm ok
> with whatever module names you pick.
It may actually be easier to let problem reporters (who cannot or don't
want to compile kernels) compare between different kernel releases,
rather than to ask them to compare between different stacks on top of
the same kernel. Still, it potentially reduces the testers base.
Adrian wrote:
| An advantage of changing the names is that they are now prefixed.
Is the opportunity to clean up module names compelling enough, vs. (the
wish for) minimized trouble with scripts which refer to module names?
Kristian wrote:
| Another point in favour of different module names is that the new
| stack doesn't actually provide the same user space interfaces as the
| old stack.
This applies mostly to <fw-core> vs. <ieee1394 + raw1394 + video1394 +
dv1394>. The rest should look identical to the user, except that some
inapplicable module parameters (and bugs) were removed.
--
Stefan Richter
-=====-=-=== -=-= ---==
http://arcgraph.de/sr/
On Thu, May 03, 2007 at 03:30:36PM +0200, Stefan Richter wrote:
> Olaf Hering wrote:
> > On Thu, May 03, Stefan Richter wrote:
> >
> >> ieee1394-old
> >
> > Noone will seriously ship two firewire stacks, so that cant be the
> > issue (for distributors).
>
> I don't actively watch distributions, but I believe there are frequently
> alternative drivers shipped. How much sense that makes for FireWire is
> another question.
>
> > Once there is a way to easily switch between kernel releases, I'm ok
> > with whatever module names you pick.
>
> It may actually be easier to let problem reporters (who cannot or don't
> want to compile kernels) compare between different kernel releases,
> rather than to ask them to compare between different stacks on top of
> the same kernel. Still, it potentially reduces the testers base.
>
> Adrian wrote:
> | An advantage of changing the names is that they are now prefixed.
>
> Is the opportunity to clean up module names compelling enough, vs. (the
> wish for) minimized trouble with scripts which refer to module names?
>...
How big is the trouble actually?
We have never and cannot guarantee stable module names (think of e.g.
PATA, e100 or sky2/skge), so changed names are always possible when
upgrading kernels.
Module aliases can solve many issues.
And sometimes it might even be an advantage that different drivers for
the same hardware get different names (consider especially PATA).
Not changing the module names also has the problem that the old code
must be removed from the kernel tree before the new one can be added
since two different modules with the same names could easily cause
trouble - consider a user first compiling and installing the one
modular, and then compiling and installing the other one modular (e.g.
due to problems with the one he tried first). Which module will now be
loaded by modprobe?
> Stefan Richter
cu
Adrian
--
"Is there not promise of rain?" Ling Tan asked suddenly out
of the darkness. There had been need of rain for many days.
"Only a promise," Lao Er said.
Pearl S. Buck - Dragon Seed
Adrian Bunk wrote:
>> | An advantage of changing the names is that they are now prefixed.
>>
>> Is the opportunity to clean up module names compelling enough, vs. (the
>> wish for) minimized trouble with scripts which refer to module names?
>> ...
>
> How big is the trouble actually?
Exactly. In Fedora we've just added a fw-sbp2 case to mkinitrd, it's only a
couple of lines of extra shell code:
elif [ "$modName" = "fw-sbp2" ]; then
findmodule fw-core
findmodule fw-ohci
modName="fw-sbp2"
and that's the extent of the changes. The sbp2 case for the old drivers is
still in there and in the end mkinitrd works with either stack.
Kristian
> Jonathan Woithe wrote:
> >> Olaf Hering wrote:
> >>> NACK.
> >>> Upgrade the current drivers/ieee1394/ with the new code, and keep all
> >>> existing module names.
> [...]
> > However, as a compromise how about renaming the existing stack's modules and
> > then reusing the existing names for the new stack? Messy I know, but this
> > way both stacks would still be available without recompilation for those who
> > needed them and the sbp2-as-root dilemma raised by Olaf would also be
> > covered.
>
> I.e. new modules:
> ieee1394 (was fw-core)
> ohci1394 (was fw-ohci)
> :
> old modules, for example:
> ieee1394-old
> ohci1394-old
> :
>
> Looks... weird.
>
> On the other hand, a 1394 module compilation cycle in order to do the
> fallback is not such a huge issue, except that it requires the person to
> be able to compile modules. That's probably the main issue.
True on all counts. I guess it's a question of whether the lack of an easy
fallback path will significantly reduce the number of testers. I don't have
enough of a feel to answer that.
> eth1394 (to be done) --- but that's a bad name anyway, it
> implements IP over 1394, not Ethernet
So, when eth1394 is ported the name should be something like fw-ip, at least
if we are to remain consistent with the other 3 module names.
> > Oh yes, it would be nice to have working PCILynx support again (although I
> > acknowledge it's unlikely to happen). Some of us do have these cards
> > installed for sniffing purposes (using nosy) but it would be nice to be able
> > to use them with libraw1394 as well. It would for example save me having to
> > swap cards depending on what I needed to do (I have insufficient PCI slots
> > to have both the PCILynx and OHCI cards installed simultaneously).
>
> But then, what is the actual utility of pcilynx? (I mean the current
> driver, not the card or a future driver.) Last time I checked, sbp2 was
> broken without OHCI's physical DMA, and AFAIK raw1394's newer iso API
> and video1394 and dv1394 don't work with pcilynx either.
It certainly doesn't support the raw1394 API so its current usefulness is
extremely limited.
> Porting pcilynx to the new low-level API would be quite resource
> demanding --- seen in relation to which resources we have, what the
> existing pcilynx driver's state of affairs is, and how rare the hardware
> is. (For those who have the hardware, the stand-alone Nosy is
> undoubtedly the killer application, not pcilynx.)
Precisely. As I said, I've probably got a corner case and it's certainly
not worth the effort just for that. It would be nice though. You're right
about nosy; so long as nosy (which is independent of the firewire stack)
keeps working I'll be happy. :)
Regards
jonathan
On Thu, 03 May 2007, Kristian H?gsberg wrote:
> Adrian Bunk wrote:
> >> | An advantage of changing the names is that they are now prefixed.
> >>
> >> Is the opportunity to clean up module names compelling enough, vs. (the
> >> wish for) minimized trouble with scripts which refer to module names?
> >> ...
> >
> > How big is the trouble actually?
>
> Exactly. In Fedora we've just added a fw-sbp2 case to mkinitrd, it's only a
> couple of lines of extra shell code:
>
> elif [ "$modName" = "fw-sbp2" ]; then
> findmodule fw-core
> findmodule fw-ohci
> modName="fw-sbp2"
>
> and that's the extent of the changes. The sbp2 case for the old drivers is
> still in there and in the end mkinitrd works with either stack.
>
> Kristian
I also think both stacks should be provided in the mainline kernel,
preferably in their own separate directories. I still need the old
stack for dv1394, which isn't available in the new stack. But if
the new stack is also there, I might be motivated for example to try
out the new sbp2 module, to see how well it works and how it compares
in performance to the old sbp2 module. If it's not there, I'm probably
not going to go out of my way to download it from the net, since my
existing setup is working just fine for me.
-Bill
On Wed, May 02, 2007 at 05:11:45PM -0400, Kristian H??gsberg wrote:
> The firewire-cdev.h file is meant to be a self-contained userspace header
> file and shouldn't include other kernel header files. All duplicated
> values are standardized ieee1394 values and won't ever change. I should
> put a #ifndef __FW_COMMON_DEFINES protection around the duplicate values, I
> guess, but I'm just wondering why I never saw a "symbol redefined"
> warning...
No, defining things in two places is not okay. Just add a new header
that defines these protocol constants, which needs to be included by
userspace that wants to use them.
On Wed, May 02, 2007 at 11:53:30PM +0200, Stefan Richter wrote:
> > Please set the max_sectors value in your host template so that the
> > block layer doesn't build sg entries too big for you.
>
> Hmm, what about this:
>
> James Bottomley wrote on 2007-01-15:
> | Actually, there's one unfortunate case where Linux won't respect this:
> | an IOMMU that can do virtual merging. This parameter is a block queue
> | parameter, so block will happily make sure the request segments obey it.
> | However, when you get to dma_map_rq() it doesn't see the segment limits,
> | so, if the iommu merges, you can end up with SG elements the other side
> | that violate this. I've been meaning to do something about this for
> | ages (IDE is the other subsystem that has an absolute requirement for a
> | fixed maximum segment size) but never found an excuse to fix it.
>
> http://marc.info/?l=linux-scsi&m=116889641203397
Hmm, okay. Probably wants a comment in there explaining it, and we
should poke jejb to fix it for real :)
> >> +static int add_scsi_devices(struct fw_unit *unit)
> >> +{
> >> + struct sbp2_device *sd = unit->device.driver_data;
> >> + int retval, lun;
> >> +
> >> + if (sd->scsi_host != NULL)
> >> + return 0;
> >> +
> >> + sd->scsi_host = scsi_host_alloc(&scsi_driver_template,
> >> + sizeof(unsigned long));
> >> + if (sd->scsi_host == NULL) {
> >> + fw_error("failed to register scsi host\n");
> >> + return -1;
> >> + }
> >> +
> >> + sd->scsi_host->hostdata[0] = (unsigned long)unit;
> >
> > Please take a look ar ther other scsi drivers how this is supposed
> > to be used.
>
> Do you mean the one Scsi_Host per LU? If it is that, then it was just
> taken over from drivers/ieee1394/sbp2.c. Sbp2 is doing this still today
> mostly for historical reasons; I just didn't find the time yet to try to
> get to a leaner scheme.
No, sorry. I should have written a better explanation. scsi_host_alloc
is designed to allocate space for your private data aswell. So you
should call it early on an allocate the sbp2_device as part of the Scsi_Host
instead of just stuffing in a pointer.
> > Do we really need another scanning algorithm?
>
> Yes.
>
> > Can't you use scsi_scan_target instead and let the core scsi code
> > handle the scanning?
>
> No. The discovery of LUs of SBP-2 targets happens on the IEEE 1212
> level of things. The initiator has to parse the configuration ROM of
> the target FireWire node; the ROM has entries for each LU. (After that,
> SBP-2 login protocol commences for each LU, and only after that can SCSI
> requests be issued. There is nothing SCSIish going on before that.)
>
> What's missing as a /* FIXME */ here is actually implemented in the
> mainline sbp2.c and needs to be brought over here; converted to the new
> FireWire core APIs.
Okay, so sbp2 decided to be non-standard here, what a pity. It's probably
better to use scsi_scan_target with a specific lun, though as scsi_add_device
is a rather awkward API.
> > This function seems rather oddly named. And the checking and
> > setting of scsi_host looks like you have some lifetime rule
> > problems.
> >
>
> The NULL probably has to do with the ability to call remove_scsi_devices
> in different paths. (These paths are not concurrent.)
Needs documentation at least. And at least my preference would be
to have a deleted flag instead of the null setting because the latter
can easily paper over bugs.
> +/* ---------- pci subsystem interface ---------- */
> +
> +enum {
> + CLEANUP_SELF_ID,
> + CLEANUP_REGISTERS,
> + CLEANUP_IOMEM,
> + CLEANUP_DISABLE,
> + CLEANUP_PUT_CARD,
> +};
> +
> +static int cleanup(struct fw_ohci *ohci, int stage, int code)
> +{
> + struct pci_dev *dev = to_pci_dev(ohci->card.device);
> +
> + switch (stage) {
> + case CLEANUP_SELF_ID:
> + dma_free_coherent(ohci->card.device, SELF_ID_BUF_SIZE,
> + ohci->self_id_cpu, ohci->self_id_bus);
> + case CLEANUP_REGISTERS:
> + kfree(ohci->it_context_list);
> + kfree(ohci->ir_context_list);
> + pci_iounmap(dev, ohci->registers);
> + case CLEANUP_IOMEM:
> + pci_release_region(dev, 0);
> + case CLEANUP_DISABLE:
> + pci_disable_device(dev);
> + case CLEANUP_PUT_CARD:
> + fw_card_put(&ohci->card);
> + }
> +
> + return code;
> +}
> +
> +static int __devinit
> +pci_probe(struct pci_dev *dev, const struct pci_device_id *ent)
> +{
> + struct fw_ohci *ohci;
> + u32 bus_options, max_receive, link_speed;
> + u64 guid;
> + int error_code;
> + size_t size;
> +
> + ohci = kzalloc(sizeof *ohci, GFP_KERNEL);
> + if (ohci == NULL) {
> + fw_error("Could not malloc fw_ohci data.\n");
> + return -ENOMEM;
> + }
> +
> + fw_card_initialize(&ohci->card, &ohci_driver, &dev->dev);
> +
> + if (pci_enable_device(dev)) {
> + fw_error("Failed to enable OHCI hardware.\n");
> + return cleanup(ohci, CLEANUP_PUT_CARD, -ENODEV);
Please use normal goto unwinding so the driver follows the same model
as almost all other pci drivers and allows people to follow your driver
more easily. Also it's not a alot of cleanup code, so removing this
might actually be a net decrease in lines of code.
> + if (software_reset(ohci)) {
Please give all your function a nice prefix so that oops messages are
more readable.
Christoph Hellwig wrote:
>> >> + sd->scsi_host->hostdata[0] = (unsigned long)unit;
...
> scsi_host_alloc
> is designed to allocate space for your private data aswell. So you
> should call it early on an allocate the sbp2_device as part of the Scsi_Host
> instead of just stuffing in a pointer.
Aha, OK.
As long as we have 1 SBP-2 target LU == 1 instance of struct Scsi_Host,
the usage of ->hostdata as backpointer to an fw_unit is appropriate
though. The lifetime of the fw_unit starts before and ends after that
of the Scsi_Host. Of course if we reorganize this to use Scsi_Host more
as a representation of an SBP-2 initiator port (or for all our initiator
ports at once), some oddities like this ->hostdata usage will go away.
>> > The discovery of LUs of SBP-2 targets happens on the IEEE 1212
>> > level of things.
[...]
> Okay, so sbp2 decided to be non-standard here, what a pity.
Well, SBP-2 (the spec) is from the earlier days when the SCSI
Architecture Model was young and there didn't exist that many other SCSI
transports/interconnects besides SCSI Parallel Interconnect. And for
better or worse, SBP-3 inherited this part of SBP-2's specialties.
> It's probably better to use scsi_scan_target with a specific lun,
> though as scsi_add_device is a rather awkward API.
Looking into these things were on my long-term agenda for mainline's
sbp2 driver anyway. My plans just got dragged out when I expanded my
activities from ieee1394/sbp2 to ieee1394/. As far as fw-sbp2 is
concerned, I don't know if these issues (which it merely took over from
sbp2) need to be addressed before integration into mainline. fw-sbp2 is
something in the middle between a new submission and a gradual update.
Do you see the TODOs related to integration with the SCSI stack (which
apply to sbp2 as well) as blocking for fw-sbp2?
Thanks for looking into it and for all the advice,
--
Stefan Richter
-=====-=-=== -=-= --=--
http://arcgraph.de/sr/
On Thu, May 03, Olaf Hering wrote:
> On Thu, May 03, Stefan Richter wrote:
>
> > ieee1394-old
>
> Noone will seriously ship two firewire stacks, so that cant be the
> issue (for distributors).
>
> Once there is a way to easily switch between kernel releases, I'm ok
> with whatever module names you pick.
This patch loads fw-sbp2 if sbp2 is still in the config file. So one can
go back and forth between releases without worry about the root
filesystem drivers.
Index: linux-2.6.21/drivers/firewire/fw-ohci.c
===================================================================
--- linux-2.6.21.orig/drivers/firewire/fw-ohci.c
+++ linux-2.6.21/drivers/firewire/fw-ohci.c
@@ -1881,6 +1881,9 @@ static struct pci_driver fw_ohci_pci_dri
MODULE_AUTHOR("Kristian Hoegsberg <[email protected]>");
MODULE_DESCRIPTION("Driver for PCI OHCI IEEE1394 controllers");
MODULE_LICENSE("GPL");
+#ifndef CONFIG_IEEE1394_OHCI1394_MODULE
+MODULE_ALIAS("ohci1394");
+#endif
static int __init fw_ohci_init(void)
{
Index: linux-2.6.21/drivers/firewire/fw-sbp2.c
===================================================================
--- linux-2.6.21.orig/drivers/firewire/fw-sbp2.c
+++ linux-2.6.21/drivers/firewire/fw-sbp2.c
@@ -1150,6 +1150,9 @@ MODULE_AUTHOR("Kristian Hoegsberg <krh@b
MODULE_DESCRIPTION("SCSI over IEEE1394");
MODULE_LICENSE("GPL");
MODULE_DEVICE_TABLE(ieee1394, sbp2_id_table);
+#ifndef CONFIG_IEEE1394_SBP2_MODULE
+MODULE_ALIAS("sbp2");
+#endif
static int __init sbp2_init(void)
{
Pekka Enberg wrote:
> On 5/2/07, Stefan Richter <[email protected]> wrote:
>> I looked around a bit with grep -R and a few search terms but didn't
>> find something definite. Is there any other user of a crc16_itu_t or
>> crc_ccitt or whatever which operates on a (CPU byte ordered) u32[]
>> instead of on a (network byte ordered) u8[]?
>
> I was referring to this: http://lkml.org/lkml/2006/6/12/137
That works. If you look closely, you can see that the ITU-T table is just
crc_itu_t_table[i] = bitrev16(crc_ccitt_table[bitrev8(i)])
but that's a expensive enough operation that I think it deserves its own
table. Especially if there's another driver out there that needs this.
> On 5/2/07, Stefan Richter <[email protected]> wrote:
>> The only value in having a shared implementation would be a potentially
>> smaller kernel. Sharing it to ensure correctness is not an issue;
>> fw-topology.c::crc16_itu_t is simply the one in IEEE 1212 table 5.
>> Performance is also not an issue (if better algorithms exist) because
>> the FireWire stack uses it only infrequently on a moderate amount of
>> data.
>
> Yeah, it's not a biggie, but we do have a tradition of putting
> generally useful things into lib/ so that everyone doesn't invent
> their own.
I'll pull in Ivo's patch and add it to the firewire branch.
thanks,
Kristian
Christoph Hellwig wrote:
> On Wed, May 02, 2007 at 02:15:42PM +0200, Stefan Richter wrote:
>> +/* -*- c-basic-offset: 8 -*-
>
> Please don't pollute the code with annotation for some editors.
OK.
>> + * fw-card.c - card level functions
>
> Please don't put the filename into a comment inside the filename.
> It's not actually useful and it gets out of date very easily.
Yeah, true.
>> +static DECLARE_RWSEM(card_rwsem);
>
> this one is only ever used with down_read/up_read so a normal
> mutex would probably better suited.
down_write/up_write, but yes, I'll change that to a mutex. The card_rwsem was
previously subsystem.rwsem, but that went away.
>> +static LIST_HEAD(card_list);
>
>
>
>> +#define bib_pmc ((1) << 27)
>> +#define bib_bmc ((1) << 28)
>> +#define bib_isc ((1) << 29)
>> +#define bib_cmc ((1) << 30)
>> +#define bib_imc ((1) << 31)
>
> These are rather odd names for constants. They should at least
> start with an uppercase letter if not be all uppercase. Also
> an enum might be right here.
Sure, I'll do that... all those uppercase letters hurt my eyes, though.
>> +static u32 *
>> +generate_config_rom (struct fw_card *card, size_t *config_rom_length)
>
> Please don't put white spaces after the function identifier.
Ugh, yeah, I don't know why I keep doing that...
>> + /* Initialize contents of config rom buffer. On the OHCI
>> + * controller, block reads to the config rom accesses the host
>> + * memory, but quadlet read access the hardware bus info block
>> + * registers. That's just crack, but it means we should make
>> + * sure the contents of bus info block in host memory mathces
>> + * the version stored in the OHCI registers. */
>
> Normal style for block comments is:
>
> /*
> * Initialize contents of config rom buffer. On the OHCI
> * controller, block reads to the config rom accesses the host
> * memory, but quadlet read access the hardware bus info block
> * registers. That's just crack, but it means we should make
> * sure the contents of bus info block in host memory mathces
> * the version stored in the OHCI registers.
> */
Oh, ok, I can clean that up.
>> +struct fw_node {
>> + u16 node_id;
>> + u8 color;
>> + u8 port_count;
>> + unsigned link_on : 1;
>> + unsigned initiated_reset : 1;
>> + unsigned b_path : 1;
>> + u8 phy_speed : 3; /* As in the self ID packet. */
>> + u8 max_speed : 5; /* Minimum of all phy-speeds and port speeds on
>> + * the path from the local node to this node. */
>> + u8 max_depth : 4; /* Maximum depth to any leaf node */
>> + u8 max_hops : 4; /* Max hops in this sub tree */
>
> I don't think we need to save the few bits here and can just use
> u8 instead of bitfields.
These bitfields aren't used on the wire or anywhere outside the driver... what
the problem? Perfomance?
>> + /* Pop the child nodes off the stack and push the new node. */
>> + __list_del(h->prev, &stack);
>
> Please don't ever use __list_del directly, it's an interna implementation
> detail.
Do you have an alternative suggestion here? I need to take a sub-list of the
list, which is exactly what __list_del does. I could loop through the
sublist, but it's pretty pointless since I have both ends of the sublist.
> I also notices that close to none of the export functions have kerneldoc
> comment blocks. I think we really should have them on something that
> is a driver API.
Yeah... I'll sit down and try to document it better.
thanks
Kristian
Christoph Hellwig wrote:
> On Wed, May 02, 2007 at 02:17:26PM +0200, Stefan Richter wrote:
>> +#include <asm/ioctl.h>
>> +#include <asm/types.h>
>
> Always use the <linux/ versions.
Fixed.
>> +struct fw_cdev_get_info {
>> + /* The version field is just a running serial number. We
>> + * never break backwards compatibility. Userspace passes in
>> + * the version it expects and the kernel passes back the
>> + * highest version it can provide. Even if the structs in
>> + * this interface are extended in a later version, the kernel
>> + * will not copy back more data than what was present in the
>> + * interface version userspace expects. */
>> + __u32 version;
>
> Please don't even try to build interfaces this complicated. If your
> current interface needs changes at some point just introduce new ioctls.
Sure, I don't expect a lot of changes anyway, and the few features I know that
will show up down the road are strictly additions anyway. I think it makes
sense to keep the version number there, though, as a way to advertise which
ioctls are present.
Kristian
Christoph Hellwig wrote:
> On Thu, May 03, 2007 at 01:01:08AM +0200, Stefan Richter wrote:
>> Christoph Hellwig wrote:
>>>> +fw-core-objs := fw-card.o fw-topology.o fw-transaction.o fw-iso.o \
>>>> + fw-device.o fw-cdev.o
>>> fw-core-y += ..
>>>
>> Like such?
>
> Exactly.
OK, changed that here, will push out new patches soon.
Kristian
Christoph Hellwig wrote:
> On Wed, May 02, 2007 at 05:11:45PM -0400, Kristian H??gsberg wrote:
>> The firewire-cdev.h file is meant to be a self-contained userspace header
>> file and shouldn't include other kernel header files. All duplicated
>> values are standardized ieee1394 values and won't ever change. I should
>> put a #ifndef __FW_COMMON_DEFINES protection around the duplicate values, I
>> guess, but I'm just wondering why I never saw a "symbol redefined"
>> warning...
>
> No, defining things in two places is not okay. Just add a new header
> that defines these protocol constants, which needs to be included by
> userspace that wants to use them.
Ok, I split out the shared constants into linux/firewire-constants.h which
gets included by linux/firewire-cdev.h.
Kristian
Olaf Hering wrote:
> On Thu, May 03, Olaf Hering wrote:
>
>> On Thu, May 03, Stefan Richter wrote:
>>
>>> ieee1394-old
>> Noone will seriously ship two firewire stacks, so that cant be the
>> issue (for distributors).
>>
>> Once there is a way to easily switch between kernel releases, I'm ok
>> with whatever module names you pick.
>
> This patch loads fw-sbp2 if sbp2 is still in the config file. So one can
> go back and forth between releases without worry about the root
> filesystem drivers.
That's a good solution, that should work. I've committed it locally, will
push out changes soon.
thanks,
Kristian
Christoph Hellwig wrote:
>> + sg = (struct scatterlist *)orb->cmd->request_buffer;
>> + count = dma_map_sg(device->card->device, sg, orb->cmd->use_sg,
>> + orb->cmd->sc_data_direction);
>
> you need to handle the error case (count == 0)
Yup, done.
>> + /* Convert the scatterlist to an sbp2 page table. If any
>> + * scatterlist entries are too big for sbp2 we split the as we go. */
>
> Please set the max_sectors value in your host template so that the
> block layer doesn't build sg entries too big for you.
As Stefan, said, dma_map_sg() breaks the limit guarantee, so we have to split
things manually if sg entries got merged. I've added a comment explaining this.
Isn't max_sectors the overall size limit of the request, though? The SBP-2
protocol imposes a maximum size of 65535 bytes per sg entry, but the total
size of a request can be larger. I guess, setting dma_boundary to 2^15 could
work.
>> + orb->page_table_bus =
>> + dma_map_single(device->card->device, orb->page_table,
>> + size, DMA_TO_DEVICE);
>
> This needs handling of mapping errors (dma_mapping_error())
Done.
>> + orb = kzalloc(sizeof *orb, GFP_ATOMIC);
>
> Normal kernel style is sizeof(*orb)
Oh, hmm... I though the kernel style typically was to avoid excess parens :)
But, sure, I see the comment about preferring parens with sizeof in CodingStyle.
>> + if (cmd->use_sg) {
>> + sbp2_command_orb_map_scatterlist(orb);
>> + } else if (cmd->request_bufflen > SBP2_MAX_SG_ELEMENT_LENGTH) {
>> + /* FIXME: Need to split this into a sg list... but
>> + * could we get the scsi or blk layer to do that by
>> + * reporting our max supported block size? */
>> + fw_error("command > 64k\n");
>> + goto fail_bufflen;
>> + } else if (cmd->request_bufflen > 0) {
>> + sbp2_command_orb_map_buffer(orb);
>> + }
>
> The use_sg == 0, request_bufflen != 0 case can't happen anymore.
That should simplify the code a bit. How long has that been the case?
>> + fail_mapping:
>> + kfree(orb);
>> + fail_alloc:
>> + cmd->result = DID_ERROR << 16;
>> + done(cmd);
>
> Failure due to ressource shortage should not complete the command
> but return SCSI_MLQUEUE_HOST_BUSY/SCSI_MLQUEUE_DEVICE_BUSY.
Ok, I've changed that.
>> + return 0;
>> +}
>
>> +static struct scsi_host_template scsi_driver_template = {
>> + .module = THIS_MODULE,
>> + .name = "SBP-2 IEEE-1394",
>> + .proc_name = (char *)sbp2_driver_name,
>
> Please don't use casrs here. Either fix up the definition so it
> accepts const strings or pass a non-const one.
Ok, I'll patch the scsi host template definition.
>> +static int add_scsi_devices(struct fw_unit *unit)
>> +{
>> + struct sbp2_device *sd = unit->device.driver_data;
>> + int retval, lun;
>> +
>> + if (sd->scsi_host != NULL)
>> + return 0;
>> +
>> + sd->scsi_host = scsi_host_alloc(&scsi_driver_template,
>> + sizeof(unsigned long));
>> + if (sd->scsi_host == NULL) {
>> + fw_error("failed to register scsi host\n");
>> + return -1;
>> + }
>> +
>> + sd->scsi_host->hostdata[0] = (unsigned long)unit;
>
> Please take a look ar ther other scsi drivers how this is supposed
> to be used.
I was trying to be clever and only allocate the host once the device had been
discovered and initialized. I have now changed the code to just allocate the
host up front and use the hostdata mechanism for the sbp2_device struct, which
also addresses the host life cycle comments below.
>> + retval = scsi_add_host(sd->scsi_host, &unit->device);
>> + if (retval < 0) {
>> + fw_error("failed to add scsi host\n");
>> + scsi_host_put(sd->scsi_host);
>> + sd->scsi_host = NULL;
>> + return retval;
>> + }
>> +
>> + /* FIXME: Loop over luns here. */
>> + lun = 0;
>> + retval = scsi_add_device(sd->scsi_host, 0, 0, lun);
>> + if (retval < 0) {
>> + fw_error("failed to add scsi device\n");
>> + scsi_remove_host(sd->scsi_host);
>> + scsi_host_put(sd->scsi_host);
>> + sd->scsi_host = NULL;
>> + return retval;
>> + }
>> +
>> + return 0;
>> +}
>
> Do we really need another scanning algorithm? Can't you use
> scsi_scan_target instead and let the core scsi code handle the
> scanning?
Stefan addressed this one.
>> +
>> +static void remove_scsi_devices(struct fw_unit *unit)
>> +{
>> + struct sbp2_device *sd = unit->device.driver_data;
>> +
>> + if (sd->scsi_host != NULL) {
>> + scsi_remove_host(sd->scsi_host);
>> + scsi_host_put(sd->scsi_host);
>> + }
>> + sd->scsi_host = NULL;
>> +}
>
> This function seems rather oddly named. And the checking and
> setting of scsi_host looks like you have some lifetime rule
> problems.
Now fixed as described above.
Thanks for the review, will send out new patches.
Kristian
Kristian H?gsberg wrote:
> Isn't max_sectors the overall size limit of the request, though?
BTW, mainline sbp2 sets max_sectors=512 per default because one 1st
generation bridge needed it. fw-sbp2 lets the SCSI stack chose the
default max_sectors, which is SCSI_DEFAULT_MAX_SECTORS i.e. 1024.
I think that should be fine with practically all SBP-2 bridges in
use today, but max_sectors > 512 was never widely tested.
--
Stefan Richter
-=====-=-=== -=-= -=--=
http://arcgraph.de/sr/
Kristian H?gsberg wrote:
> Christoph Hellwig wrote:
>>> +static int add_scsi_devices(struct fw_unit *unit)
>>> +{
>>> + struct sbp2_device *sd = unit->device.driver_data;
>>> + int retval, lun;
>>> +
>>> + if (sd->scsi_host != NULL)
>>> + return 0;
>>> +
>>> + sd->scsi_host = scsi_host_alloc(&scsi_driver_template,
>>> + sizeof(unsigned long));
>>> + if (sd->scsi_host == NULL) {
>>> + fw_error("failed to register scsi host\n");
>>> + return -1;
>>> + }
>>> +
>>> + sd->scsi_host->hostdata[0] = (unsigned long)unit;
>>
>> Please take a look ar ther other scsi drivers how this is supposed
>> to be used.
>
> I was trying to be clever and only allocate the host once the device had
> been discovered and initialized. I have now changed the code to just
> allocate the host up front and use the hostdata mechanism for the
> sbp2_device struct, which also addresses the host life cycle comments
> below.
I have doubts. IMO the previous code is 100% correct as long as 1 SBP-2
target LU maps to 1 Scsi_Host.
The lifetime of the Scsi_Host would only be longer than that of the LU
if all LUs (or all LUs at the same initiator port) would be added beneath
the same instance of Scsi_Host. Then the lifetime of the Scsi_Host would
be that of the fw-sbp2 driver, or that of fw-sbp2's representation of a
FireWire bus.
However, I'll see how it turns out in your update patch.
--
Stefan Richter
-=====-=-=== -=-= -=--=
http://arcgraph.de/sr/
Stefan Richter wrote:
> Kristian H?gsberg wrote:
>> I was trying to be clever and only allocate the host once the device had
>> been discovered and initialized. I have now changed the code to just
>> allocate the host up front and use the hostdata mechanism for the
>> sbp2_device struct, which also addresses the host life cycle comments
>> below.
>
> I have doubts. IMO the previous code is 100% correct as long as 1 SBP-2
> target LU maps to 1 Scsi_Host.
>
> The lifetime of the Scsi_Host would only be longer than that of the LU
> if all LUs (or all LUs at the same initiator port) would be added beneath
> the same instance of Scsi_Host. Then the lifetime of the Scsi_Host would
> be that of the fw-sbp2 driver, or that of fw-sbp2's representation of a
> FireWire bus.
In the patch, the sbp2_device is now allocated with the scsi_host and is the
hostdata part of the host struct. This mean we have to add all the LUs from
the unit directory corresponding to the sbp2_device struct to that host. Is
that a problem? I think we had this discussion before, but I still don't
understand why this approach isn't feasible.
Kristian
Kristian H?gsberg wrote:
> This mean we have to add all the LUs from the unit directory
> corresponding to the sbp2_device struct to that host. Is that
> a problem?
Most certainly not for now.
> I think we had this discussion before,
I believe that was on a different aspect of multi-LU targets.
> but I still don't understand why this approach isn't feasible.
I didn't say it was infeasible. On the contrary, I too wanted (am
wanting) to rework sbp2 so that it instantiates only a single
Scsi_Host globally or per FireWire initiator port. It's just one
of the things that I always pushed back, and where the devil is in
the detail. (Notably if multiple targets, not just multiple LUs
of one target, live beneath one Scsi_Host.)
--
Stefan Richter
-=====-=-=== -=-= -=-=-
http://arcgraph.de/sr/
Christoph Hellwig wrote:
>> + if (pci_enable_device(dev)) {
>> + fw_error("Failed to enable OHCI hardware.\n");
>> + return cleanup(ohci, CLEANUP_PUT_CARD, -ENODEV);
>
> Please use normal goto unwinding so the driver follows the same model
> as almost all other pci drivers and allows people to follow your driver
> more easily. Also it's not a alot of cleanup code, so removing this
> might actually be a net decrease in lines of code.
Sure. The patch I have here says 42 insertions, 44 deletions :)
>> + if (software_reset(ohci)) {
>
> Please give all your function a nice prefix so that oops messages are
> more readable.
I don't see a strong precedence for this for static functions. For example,
if you grep for 'static' in drivers/usb/core, there's a mix of usb_* prefixed
functions and functions without a consistent prefix. Plus, when the drivers
are loaded as modules, the module name will be in the stack trace. I can
track down the most generic sounding functions and give them a fw_ prefix, but
doing a whole-sale prefixing of static functions will make the source more
noisy and reduce readabilty - I'm not sure it's worth it.
cheers,
Kristian
Linus, please pull from the juju branch at
git://git.kernel.org/pub/scm/linux/kernel/git/ieee1394/linux1394-2.6.git juju
to receive the new, alternative IEEE 1394 subsystem ( --- unless you
consider it too close to the end of the merge window now or have other
reservations...).
Many thanks to all who reviewed the submission from May 1st. Your work
is of great help and is very much appreciated.
What changed since last submission:
Ivo van Doorn (1):
CRC ITU-T V.41
Kristian H?gsberg (14):
firewire: Use lib/ implementation of CRC ITU-T.
firewire: Clean up comment style.
firewire: Convert card_rwsem to a regular mutex.
firewire: Coding style cleanup: no spaces after function names.
firewire: Uppercase most macro names.
firewire: Use linux/*.h instead of asm/*.h header files.
firewire: Break out shared IEEE1394 constant to separate header file.
firewire: Add to fw-core-y instead of assigning fw-core-objs in Makefile.
firewire: Allocate scsi_host up front and allocate the sbp2_device as hostdata.
firewire: Handle the last few DMA mapping error cases.
firewire: Return SCSI_MLQUEUE_HOST_BUSY for out of memory cases in queuecommand.
firewire: Drop single buffer request support.
firewire: Always use parens with sizeof.
firewire: Convert OHCI driver to use standard goto unwinding for error handling.
Kristian H?gsberg, Stefan Richter (1):
firewire: Add a comment to describe why we split the sg list.
Olaf Hering (1):
firewire: Provide module aliase for backwards compatibility.
What did _not_ change:
- The drivers are still named fw-core, fw-ohci, fw-sbp2 (but they
feature conditional aliases sbp2 and ohci1394 now). Adrian
suggested the prefix firewire- instead of fw-.
- The drivers still live in drivers/firewire/, i.e. have not been put
into mainline's drivers/ieee1394/.
As mentioned in the previous submission, the main item that is still to
do regarding the new codebase is to bring over IP over 1394, currently
known as eth1394. But the biggest task is certainly to come up with a
transition schedule for the removal of the existing stack. IMO,
exposure of the new stack at this time will enable the community of IEEE
1394 developers and users to provide us with the necessary feedback to
tackle that schedule.
To rehash the reasons to undertake this transition:
+ Considerably leaner codebase,
+ cleaned-up and improved in-stack APIs
(with the side effect of getting rid of a bunch of old bugs),
+ consolidation of the currently four userspace ABIs into one improved
ABI (which is already supported by the two FireWire low-level
libraries libraw1394 and libdc1394),
+ improved host security and device security.
As noted, linux1394-2.6.git's juju branch is based on 2.6.21-rc3. I can
rebase it onto 2.6.21 or current git if you wish. The juju branch
contains a linear series of now 138 commits:
Adrian Bunk (1)
Andrew Morton (4)
Ivo van Doorn (1)
Kristian H?gsberg (106)
Marc Butler (1)
Olaf Hering (1)
Randy Dunlap (1)
Stefan Richter (22)
Thomas Gleixner (1)
drivers/Makefile | 1 +
drivers/firewire/Kconfig | 61 ++
drivers/firewire/Makefile | 10 +
drivers/firewire/fw-card.c | 560 +++++++++++
drivers/firewire/fw-cdev.c | 961 ++++++++++++++++++
drivers/firewire/fw-device.c | 813 +++++++++++++++
drivers/firewire/fw-device.h | 146 +++
drivers/firewire/fw-iso.c | 163 +++
drivers/firewire/fw-ohci.c | 1943 ++++++++++++++++++++++++++++++++++++
drivers/firewire/fw-ohci.h | 153 +++
drivers/firewire/fw-sbp2.c | 1147 +++++++++++++++++++++
drivers/firewire/fw-topology.c | 537 ++++++++++
drivers/firewire/fw-topology.h | 92 ++
drivers/firewire/fw-transaction.c | 910 +++++++++++++++++
drivers/firewire/fw-transaction.h | 458 +++++++++
drivers/ieee1394/Kconfig | 2 +
include/linux/crc-itu-t.h | 28 +
include/linux/firewire-cdev.h | 229 +++++
include/linux/firewire-constants.h | 67 ++
lib/Kconfig | 8 +
lib/Makefile | 1 +
lib/crc-itu-t.c | 69 ++
22 files changed, 8359 insertions(+), 0 deletions(-)
create mode 100644 drivers/firewire/Kconfig
create mode 100644 drivers/firewire/Makefile
create mode 100644 drivers/firewire/fw-card.c
create mode 100644 drivers/firewire/fw-cdev.c
create mode 100644 drivers/firewire/fw-device.c
create mode 100644 drivers/firewire/fw-device.h
create mode 100644 drivers/firewire/fw-iso.c
create mode 100644 drivers/firewire/fw-ohci.c
create mode 100644 drivers/firewire/fw-ohci.h
create mode 100644 drivers/firewire/fw-sbp2.c
create mode 100644 drivers/firewire/fw-topology.c
create mode 100644 drivers/firewire/fw-topology.h
create mode 100644 drivers/firewire/fw-transaction.c
create mode 100644 drivers/firewire/fw-transaction.h
create mode 100644 include/linux/crc-itu-t.h
create mode 100644 include/linux/firewire-cdev.h
create mode 100644 include/linux/firewire-constants.h
create mode 100644 lib/crc-itu-t.c
Thanks,
--
Stefan Richter
-=====-=-=== -=-= -=-=-
http://arcgraph.de/sr/
On Thu, May 10, 2007 at 07:26:56PM +0200, Stefan Richter wrote:
> Linus, please pull from the juju branch at
>
> git://git.kernel.org/pub/scm/linux/kernel/git/ieee1394/linux1394-2.6.git juju
>
ACK from me. I still have some comments but none of them is a merge
blocker.
> What did _not_ change:
>
> - The drivers are still named fw-core, fw-ohci, fw-sbp2 (but they
> feature conditional aliases sbp2 and ohci1394 now). Adrian
> suggested the prefix firewire- instead of fw-.
>
> - The drivers still live in drivers/firewire/, i.e. have not been put
> into mainline's drivers/ieee1394/.
I don't quite like that. Then again git handles renames pretty nicely
and I hope we'll just shift the new drivers in place when the old code
goes away completely to give people a seamless migration.
On Thu, May 10, 2007 at 06:38:50PM +0100, Christoph Hellwig wrote:
> On Thu, May 10, 2007 at 07:26:56PM +0200, Stefan Richter wrote:
> > Linus, please pull from the juju branch at
> >
> > git://git.kernel.org/pub/scm/linux/kernel/git/ieee1394/linux1394-2.6.git juju
> >
>
> ACK from me. I still have some comments but none of them is a merge
> blocker.
>
> > What did _not_ change:
> >
> > - The drivers are still named fw-core, fw-ohci, fw-sbp2 (but they
> > feature conditional aliases sbp2 and ohci1394 now). Adrian
> > suggested the prefix firewire- instead of fw-.
> >
> > - The drivers still live in drivers/firewire/, i.e. have not been put
> > into mainline's drivers/ieee1394/.
>
> I don't quite like that. Then again git handles renames pretty nicely
> and I hope we'll just shift the new drivers in place when the old code
> goes away completely to give people a seamless migration.
For users it shouldn't matter where the code is located in the kernel
sources (and "firewire" is IMHO for many people more meaningful than
"ieee1394").
cu
Adrian
--
"Is there not promise of rain?" Ling Tan asked suddenly out
of the darkness. There had been need of rain for many days.
"Only a promise," Lao Er said.
Pearl S. Buck - Dragon Seed
Christoph Hellwig wrote:
> On Thu, May 10, 2007 at 07:26:56PM +0200, Stefan Richter wrote:
>> - The drivers still live in drivers/firewire/, i.e. have not been put
>> into mainline's drivers/ieee1394/.
>
> I don't quite like that. Then again git handles renames pretty nicely
> and I hope we'll just shift the new drivers in place when the old code
> goes away completely to give people a seamless migration.
There were some comments in this and past reviews pro and contra the
directory split. For me, it was easier to deal with patch handling that
way since the stack went into -mm, but once the stack is in Linus' tree,
my work will probably be easier anyway.
--
Stefan Richter
-=====-=-=== -=-= -=-=-
http://arcgraph.de/sr/
Christoph Hellwig wrote:
> ACK from me. I still have some comments but none of them is a merge
> blocker.
I put one small fix that Kristian sent on hold (const char * vs char *
for shostt->proc_name) to let a related patch go to scsi-misc first.
So this open item is my fault, not his. :-)
--
Stefan Richter
-=====-=-=== -=-= -=-=-
http://arcgraph.de/sr/