From e71d31da062095d8b0b02a26fb5e8879e8d3d0de Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Fri, 5 Jun 2009 16:26:18 +0200 Subject: firewire: rename source files The source files of firewire-core, firewire-ohci, firewire-sbp2, i.e. "drivers/firewire/fw-*.c" are renamed to "drivers/firewire/core-*.c", "drivers/firewire/ohci.c", "drivers/firewire/sbp2.c". The old fw- prefix was redundant to the directory name. The new core- prefix distinguishes the files according to which driver they belong to. This change comes a little late, but still before further firewire drivers are added as anticipated RSN. Signed-off-by: Stefan Richter --- drivers/firewire/core-card.c | 567 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 567 insertions(+) create mode 100644 drivers/firewire/core-card.c (limited to 'drivers/firewire/core-card.c') diff --git a/drivers/firewire/core-card.c b/drivers/firewire/core-card.c new file mode 100644 index 00000000000..ba6cd70b851 --- /dev/null +++ b/drivers/firewire/core-card.c @@ -0,0 +1,567 @@ +/* + * Copyright (C) 2005-2007 Kristian Hoegsberg + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "core.h" + +int fw_compute_block_crc(u32 *block) +{ + __be32 be32_block[256]; + int i, length; + + length = (*block >> 16) & 0xff; + for (i = 0; i < length; i++) + be32_block[i] = cpu_to_be32(block[i + 1]); + *block |= crc_itu_t(0, (u8 *) be32_block, length * 4); + + return length; +} + +static DEFINE_MUTEX(card_mutex); +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 matches + * 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 = fw_compute_block_crc(config_rom + i); + + *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; + + mutex_lock(&card_mutex); + + list_add_tail(&desc->link, &descriptor_list); + descriptor_count++; + if (desc->immediate > 0) + descriptor_count++; + update_config_roms(); + + mutex_unlock(&card_mutex); + + return 0; +} + +void fw_core_remove_descriptor(struct fw_descriptor *desc) +{ + mutex_lock(&card_mutex); + + list_del(&desc->link); + descriptor_count--; + if (desc->immediate > 0) + descriptor_count--; + update_config_roms(); + + mutex_unlock(&card_mutex); +} + +static int set_broadcast_channel(struct device *dev, void *data) +{ + fw_device_set_broadcast_channel(fw_device(dev), (long)data); + return 0; +} + +static void allocate_broadcast_channel(struct fw_card *card, int generation) +{ + int channel, bandwidth = 0; + + fw_iso_resource_manage(card, generation, 1ULL << 31, + &channel, &bandwidth, true); + if (channel == 31) { + card->broadcast_channel_allocated = true; + device_for_each_child(card->device, (void *)(long)generation, + set_broadcast_channel); + } +} + +static const char gap_count_table[] = { + 63, 5, 7, 8, 10, 13, 16, 18, 21, 24, 26, 29, 32, 35, 37, 40 +}; + +void fw_schedule_bm_work(struct fw_card *card, unsigned long delay) +{ + int scheduled; + + fw_card_get(card); + scheduled = schedule_delayed_work(&card->work, delay); + if (!scheduled) + fw_card_put(card); +} + +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_device; + struct fw_node *root_node; + unsigned long flags; + int root_id, new_root_id, irm_id, local_id; + int gap_count, generation, grace, rcode; + bool do_reset = false; + bool root_device_is_running; + bool root_device_is_cmc; + __be32 lock_data[2]; + + spin_lock_irqsave(&card->lock, flags); + + if (card->local_node == NULL) { + spin_unlock_irqrestore(&card->lock, flags); + goto out_put_card; + } + + generation = card->generation; + root_node = card->root_node; + fw_node_get(root_node); + root_device = root_node->data; + root_device_is_running = root_device && + atomic_read(&root_device->state) == FW_DEVICE_RUNNING; + root_device_is_cmc = root_device && root_device->cmc; + root_id = root_node->node_id; + irm_id = card->irm_node->node_id; + local_id = card->local_node->node_id; + + grace = time_after(jiffies, card->reset_jiffies + DIV_ROUND_UP(HZ, 8)); + + if (is_next_generation(generation, card->bm_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. + */ + + if (!card->irm_node->link_on) { + new_root_id = local_id; + fw_notify("IRM has link off, making local node (%02x) root.\n", + new_root_id); + goto pick_me; + } + + lock_data[0] = cpu_to_be32(0x3f); + lock_data[1] = cpu_to_be32(local_id); + + spin_unlock_irqrestore(&card->lock, flags); + + rcode = fw_run_transaction(card, TCODE_LOCK_COMPARE_SWAP, + irm_id, generation, SCODE_100, + CSR_REGISTER_BASE + CSR_BUS_MANAGER_ID, + lock_data, sizeof(lock_data)); + + if (rcode == RCODE_GENERATION) + /* Another bus reset, BM work has been rescheduled. */ + goto out; + + if (rcode == RCODE_COMPLETE && + lock_data[0] != cpu_to_be32(0x3f)) { + + /* Somebody else is BM. Only act as IRM. */ + if (local_id == irm_id) + allocate_broadcast_channel(card, generation); + + goto out; + } + + spin_lock_irqsave(&card->lock, flags); + + if (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 = local_id; + fw_notify("BM lock failed, making local node (%02x) root.\n", + new_root_id); + goto pick_me; + } + } else if (card->bm_generation != generation) { + /* + * We weren't BM in the last generation, and the last + * bus reset is less than 125ms ago. Reschedule this job. + */ + spin_unlock_irqrestore(&card->lock, flags); + fw_schedule_bm_work(card, DIV_ROUND_UP(HZ, 8)); + goto out; + } + + /* + * 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_device == NULL) { + /* + * Either link_on is false, or we failed to read the + * config rom. In either case, pick another root. + */ + new_root_id = local_id; + } else if (!root_device_is_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); + goto out; + } else if (root_device_is_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 = local_id; + } + + pick_me: + /* + * Pick a gap count from 1394a table E-1. The table doesn't cover + * the typically much larger 1394b beta repeater delays though. + */ + if (!card->beta_repeaters_present && + root_node->max_hops < ARRAY_SIZE(gap_count_table)) + gap_count = gap_count_table[root_node->max_hops]; + else + gap_count = 63; + + /* + * Finally, figure out if we should do a reset or not. If we have + * done less than 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 = true; + + 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); + /* Will allocate broadcast channel after the reset. */ + } else { + if (local_id == irm_id) + allocate_broadcast_channel(card, generation); + } + + out: + fw_node_put(root_node); + out_put_card: + fw_card_put(card); +} + +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); + + card->index = atomic_inc_return(&index); + card->driver = driver; + card->device = device; + card->current_tlabel = 0; + card->tlabel_mask = 0; + card->color = 0; + card->broadcast_channel = BROADCAST_CHANNEL_INITIAL; + + kref_init(&card->kref); + init_completion(&card->done); + 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; + int ret; + + card->max_receive = max_receive; + card->link_speed = link_speed; + card->guid = guid; + + mutex_lock(&card_mutex); + config_rom = generate_config_rom(card, &length); + list_add_tail(&card->link, &card_list); + mutex_unlock(&card_mutex); + + ret = card->driver->enable(card, config_rom, length); + if (ret < 0) { + mutex_lock(&card_mutex); + list_del(&card->link); + mutex_unlock(&card_mutex); + } + + return ret; +} +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 = { + .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_card_release(struct kref *kref) +{ + struct fw_card *card = container_of(kref, struct fw_card, kref); + + complete(&card->done); +} + +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); + + mutex_lock(&card_mutex); + list_del_init(&card->link); + mutex_unlock(&card_mutex); + + /* Set up the dummy driver. */ + card->driver = &dummy_driver; + + fw_destroy_nodes(card); + + /* Wait for all users, especially device workqueue jobs, to finish. */ + fw_card_put(card); + wait_for_completion(&card->done); + + WARN_ON(!list_empty(&card->transaction_list)); + del_timer_sync(&card->flush_timer); +} +EXPORT_SYMBOL(fw_core_remove_card); + +int fw_core_initiate_bus_reset(struct fw_card *card, int short_reset) +{ + int reg = short_reset ? 5 : 1; + 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); -- cgit v1.2.3-70-g09d2 From 099d54143e49d49c33cd25779ca725191df59b73 Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Sat, 6 Jun 2009 18:37:25 +0200 Subject: firewire: core: prepare for non-core children of card devices The IP-over-1394 driver will add child devices beneath card devices which are not of type fw_device. Hence firewire-core's callbacks in device_for_each_child() and device_find_child() need to check for the device type now. Initial version written by Jay Fenlason. Signed-off-by: Stefan Richter --- drivers/firewire/core-card.c | 8 +------- drivers/firewire/core-device.c | 24 ++++++++++++++++++++---- drivers/firewire/core.h | 2 +- 3 files changed, 22 insertions(+), 12 deletions(-) (limited to 'drivers/firewire/core-card.c') diff --git a/drivers/firewire/core-card.c b/drivers/firewire/core-card.c index ba6cd70b851..4c1be64fddd 100644 --- a/drivers/firewire/core-card.c +++ b/drivers/firewire/core-card.c @@ -190,12 +190,6 @@ void fw_core_remove_descriptor(struct fw_descriptor *desc) mutex_unlock(&card_mutex); } -static int set_broadcast_channel(struct device *dev, void *data) -{ - fw_device_set_broadcast_channel(fw_device(dev), (long)data); - return 0; -} - static void allocate_broadcast_channel(struct fw_card *card, int generation) { int channel, bandwidth = 0; @@ -205,7 +199,7 @@ static void allocate_broadcast_channel(struct fw_card *card, int generation) if (channel == 31) { card->broadcast_channel_allocated = true; device_for_each_child(card->device, (void *)(long)generation, - set_broadcast_channel); + fw_device_set_broadcast_channel); } } diff --git a/drivers/firewire/core-device.c b/drivers/firewire/core-device.c index 3f4e646367b..d6e54a5173f 100644 --- a/drivers/firewire/core-device.c +++ b/drivers/firewire/core-device.c @@ -59,7 +59,7 @@ int fw_csr_iterator_next(struct fw_csr_iterator *ci, int *key, int *value) } EXPORT_SYMBOL(fw_csr_iterator_next); -static int is_fw_unit(struct device *dev); +static bool is_fw_unit(struct device *dev); static int match_unit_directory(u32 *directory, u32 match_flags, const struct ieee1394_device_id *id) @@ -599,7 +599,7 @@ static struct device_type fw_unit_type = { .release = fw_unit_release, }; -static int is_fw_unit(struct device *dev) +static bool is_fw_unit(struct device *dev) { return dev->type == &fw_unit_type; } @@ -749,6 +749,11 @@ static struct device_type fw_device_type = { .release = fw_device_release, }; +static bool is_fw_device(struct device *dev) +{ + return dev->type == &fw_device_type; +} + static int update_unit(struct device *dev, void *data) { struct fw_unit *unit = fw_unit(dev); @@ -785,6 +790,9 @@ static int lookup_existing_device(struct device *dev, void *data) struct fw_card *card = new->card; int match = 0; + if (!is_fw_device(dev)) + return 0; + down_read(&fw_device_rwsem); /* serialize config_rom access */ spin_lock_irq(&card->lock); /* serialize node access */ @@ -824,7 +832,7 @@ static int lookup_existing_device(struct device *dev, void *data) enum { BC_UNKNOWN = 0, BC_UNIMPLEMENTED, BC_IMPLEMENTED, }; -void fw_device_set_broadcast_channel(struct fw_device *device, int generation) +static void set_broadcast_channel(struct fw_device *device, int generation) { struct fw_card *card = device->card; __be32 data; @@ -860,6 +868,14 @@ void fw_device_set_broadcast_channel(struct fw_device *device, int generation) } } +int fw_device_set_broadcast_channel(struct device *dev, void *gen) +{ + if (is_fw_device(dev)) + set_broadcast_channel(fw_device(dev), (long)gen); + + return 0; +} + static void fw_device_init(struct work_struct *work) { struct fw_device *device = @@ -958,7 +974,7 @@ static void fw_device_init(struct work_struct *work) 1 << device->max_speed); device->config_rom_retries = 0; - fw_device_set_broadcast_channel(device, device->generation); + set_broadcast_channel(device, device->generation); } /* diff --git a/drivers/firewire/core.h b/drivers/firewire/core.h index 273f0ab8292..0a25a7b38a8 100644 --- a/drivers/firewire/core.h +++ b/drivers/firewire/core.h @@ -124,7 +124,7 @@ extern struct idr fw_device_idr; extern int fw_cdev_major; struct fw_device *fw_device_get_by_devt(dev_t devt); -void fw_device_set_broadcast_channel(struct fw_device *device, int generation); +int fw_device_set_broadcast_channel(struct device *dev, void *gen); void fw_node_event(struct fw_card *card, struct fw_node *node, int event); -- cgit v1.2.3-70-g09d2 From c76acec6d55107b652a37c90b36c00bc8b04dabb Mon Sep 17 00:00:00 2001 From: Jay Fenlason Date: Mon, 18 May 2009 13:08:06 -0400 Subject: firewire: add IPv4 support Implement IPv4 over IEEE 1394 as per RFC 2734 for the newer firewire stack. This feature has only been present in the older ieee1394 stack via the eth1394 driver. Still to do: - fix ipv4_priv and ipv4_node lifetime logic - fix determination of speeds and max payloads - fix bus reset handling - fix unaligned memory accesses - fix coding style - further testing/ improvement of fragment reassembly - perhaps multicast support Signed-off-by: Jay Fenlason Signed-off-by: Stefan Richter (rebased, copyright note, changelog) --- drivers/firewire/Makefile | 2 + drivers/firewire/core-card.c | 4 + drivers/firewire/core-iso.c | 7 + drivers/firewire/core.h | 87 -- drivers/firewire/fw-ipv4.c | 1819 ++++++++++++++++++++++++++++++++++++++++++ include/linux/firewire.h | 94 +++ 6 files changed, 1926 insertions(+), 87 deletions(-) create mode 100644 drivers/firewire/fw-ipv4.c (limited to 'drivers/firewire/core-card.c') diff --git a/drivers/firewire/Makefile b/drivers/firewire/Makefile index bc3b9bf822b..31edf30c558 100644 --- a/drivers/firewire/Makefile +++ b/drivers/firewire/Makefile @@ -6,7 +6,9 @@ firewire-core-y += core-card.o core-cdev.o core-device.o \ core-iso.o core-topology.o core-transaction.o firewire-ohci-y += ohci.o firewire-sbp2-y += sbp2.o +firewire-ipv4-y += fw-ipv4.o obj-$(CONFIG_FIREWIRE) += firewire-core.o obj-$(CONFIG_FIREWIRE_OHCI) += firewire-ohci.o obj-$(CONFIG_FIREWIRE_SBP2) += firewire-sbp2.o +obj-$(CONFIG_FIREWIRE_IPV4) += firewire-ipv4.o diff --git a/drivers/firewire/core-card.c b/drivers/firewire/core-card.c index 4c1be64fddd..cdab32b2067 100644 --- a/drivers/firewire/core-card.c +++ b/drivers/firewire/core-card.c @@ -176,6 +176,7 @@ int fw_core_add_descriptor(struct fw_descriptor *desc) return 0; } +EXPORT_SYMBOL(fw_core_add_descriptor); void fw_core_remove_descriptor(struct fw_descriptor *desc) { @@ -189,6 +190,7 @@ void fw_core_remove_descriptor(struct fw_descriptor *desc) mutex_unlock(&card_mutex); } +EXPORT_SYMBOL(fw_core_remove_descriptor); static void allocate_broadcast_channel(struct fw_card *card, int generation) { @@ -427,6 +429,8 @@ void fw_card_initialize(struct fw_card *card, card->local_node = NULL; INIT_DELAYED_WORK(&card->work, fw_card_bm_work); + card->netdev = NULL; + INIT_LIST_HEAD(&card->ipv4_nodes); } EXPORT_SYMBOL(fw_card_initialize); diff --git a/drivers/firewire/core-iso.c b/drivers/firewire/core-iso.c index 28076c892d7..448ddd7d887 100644 --- a/drivers/firewire/core-iso.c +++ b/drivers/firewire/core-iso.c @@ -80,6 +80,7 @@ int fw_iso_buffer_init(struct fw_iso_buffer *buffer, struct fw_card *card, return -ENOMEM; } +EXPORT_SYMBOL(fw_iso_buffer_init); int fw_iso_buffer_map(struct fw_iso_buffer *buffer, struct vm_area_struct *vma) { @@ -114,6 +115,7 @@ void fw_iso_buffer_destroy(struct fw_iso_buffer *buffer, kfree(buffer->pages); buffer->pages = NULL; } +EXPORT_SYMBOL(fw_iso_buffer_destroy); struct fw_iso_context *fw_iso_context_create(struct fw_card *card, int type, int channel, int speed, size_t header_size, @@ -136,6 +138,7 @@ struct fw_iso_context *fw_iso_context_create(struct fw_card *card, return ctx; } +EXPORT_SYMBOL(fw_iso_context_create); void fw_iso_context_destroy(struct fw_iso_context *ctx) { @@ -143,12 +146,14 @@ void fw_iso_context_destroy(struct fw_iso_context *ctx) 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, @@ -159,11 +164,13 @@ int fw_iso_context_queue(struct fw_iso_context *ctx, 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); /* * Isochronous bus resource management (channels, bandwidth), client side diff --git a/drivers/firewire/core.h b/drivers/firewire/core.h index 0a25a7b38a8..c3cfc647e5e 100644 --- a/drivers/firewire/core.h +++ b/drivers/firewire/core.h @@ -1,7 +1,6 @@ #ifndef _FIREWIRE_CORE_H #define _FIREWIRE_CORE_H -#include #include #include #include @@ -97,17 +96,6 @@ int fw_core_initiate_bus_reset(struct fw_card *card, int short_reset); int fw_compute_block_crc(u32 *block); void fw_schedule_bm_work(struct fw_card *card, unsigned long delay); -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); - /* -cdev */ @@ -130,77 +118,7 @@ void fw_node_event(struct fw_card *card, struct fw_node *node, int event); /* -iso */ -/* - * 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 - -/* - * 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; -}; - -typedef void (*fw_iso_callback_t)(struct fw_iso_context *context, - u32 cycle, size_t header_length, - void *header, void *data); - -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); -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); -void fw_iso_context_destroy(struct fw_iso_context *ctx); - void fw_iso_resource_manage(struct fw_card *card, int generation, u64 channels_mask, int *channel, int *bandwidth, bool allocate); @@ -285,9 +203,4 @@ 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); -static inline int fw_stream_packet_destination_id(int tag, int channel, int sy) -{ - return tag << 14 | channel << 8 | sy; -} - #endif /* _FIREWIRE_CORE_H */ diff --git a/drivers/firewire/fw-ipv4.c b/drivers/firewire/fw-ipv4.c new file mode 100644 index 00000000000..4de6dbb95f0 --- /dev/null +++ b/drivers/firewire/fw-ipv4.c @@ -0,0 +1,1819 @@ +/* + * IPv4 over IEEE 1394, per RFC 2734 + * + * Copyright (C) 2009 Jay Fenlason + * + * based on eth1394 by Ben Collins et al + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* Things to potentially make runtime cofigurable */ +/* must be at least as large as our maximum receive size */ +#define FIFO_SIZE 4096 +/* Network timeout in glibbles */ +#define IPV4_TIMEOUT 100000 + +/* Runitme configurable paramaters */ +static int ipv4_mpd = 25; +static int ipv4_max_xmt = 0; +/* 16k for receiving arp and broadcast packets. Enough? */ +static int ipv4_iso_page_count = 4; + +MODULE_AUTHOR("Jay Fenlason (fenlason@redhat.com)"); +MODULE_DESCRIPTION("Firewire IPv4 Driver (IPv4-over-IEEE1394 as per RFC 2734)"); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(ieee1394, ipv4_id_table); +module_param_named(max_partial_datagrams, ipv4_mpd, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(max_partial_datagrams, "Maximum number of received" + " incomplete fragmented datagrams (default = 25)."); + +/* Max xmt is useful for forcing fragmentation, which makes testing easier. */ +module_param_named(max_transmit, ipv4_max_xmt, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(max_transmit, "Maximum datagram size to transmit" + " (larger datagrams will be fragmented) (default = 0 (use hardware defaults)."); + +/* iso page count controls how many pages will be used for receiving broadcast packets. */ +module_param_named(iso_pages, ipv4_iso_page_count, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(iso_pages, "Number of pages to use for receiving broadcast packets" + " (default = 4)."); + +/* uncomment this line to do debugging */ +#define fw_debug(s, args...) printk(KERN_DEBUG KBUILD_MODNAME ": " s, ## args) + +/* comment out these lines to do debugging. */ +/* #undef fw_debug */ +/* #define fw_debug(s...) */ +/* #define print_hex_dump(l...) */ + +/* Define a fake hardware header format for the networking core. Note that + * header size cannot exceed 16 bytes as that is the size of the header cache. + * Also, we do not need the source address in the header so we omit it and + * keep the header to under 16 bytes */ +#define IPV4_ALEN (8) +/* This must equal sizeof(struct ipv4_ether_hdr) */ +#define IPV4_HLEN (10) + +/* FIXME: what's a good size for this? */ +#define INVALID_FIFO_ADDR (u64)~0ULL + +/* Things specified by standards */ +#define BROADCAST_CHANNEL 31 + +#define S100_BUFFER_SIZE 512 +#define MAX_BUFFER_SIZE 4096 + +#define IPV4_GASP_SPECIFIER_ID 0x00005EU +#define IPV4_GASP_VERSION 0x00000001U + +#define IPV4_GASP_OVERHEAD (2 * sizeof(u32)) /* for GASP header */ + +#define IPV4_UNFRAG_HDR_SIZE sizeof(u32) +#define IPV4_FRAG_HDR_SIZE (2 * sizeof(u32)) +#define IPV4_FRAG_OVERHEAD sizeof(u32) + +#define ALL_NODES (0xffc0 | 0x003f) + +#define IPV4_HDR_UNFRAG 0 /* unfragmented */ +#define IPV4_HDR_FIRSTFRAG 1 /* first fragment */ +#define IPV4_HDR_LASTFRAG 2 /* last fragment */ +#define IPV4_HDR_INTFRAG 3 /* interior fragment */ + +/* Our arp packet (ARPHRD_IEEE1394) */ +/* FIXME: note that this is probably bogus on weird-endian machines */ +struct ipv4_arp { + u16 hw_type; /* 0x0018 */ + u16 proto_type; /* 0x0806 */ + u8 hw_addr_len; /* 16 */ + u8 ip_addr_len; /* 4 */ + u16 opcode; /* ARP Opcode */ + /* Above is exactly the same format as struct arphdr */ + + u64 s_uniq_id; /* Sender's 64bit EUI */ + u8 max_rec; /* Sender's max packet size */ + u8 sspd; /* Sender's max speed */ + u16 fifo_hi; /* hi 16bits of sender's FIFO addr */ + u32 fifo_lo; /* lo 32bits of sender's FIFO addr */ + u32 sip; /* Sender's IP Address */ + u32 tip; /* IP Address of requested hw addr */ +} __attribute__((packed)); + +struct ipv4_ether_hdr { + unsigned char h_dest[IPV4_ALEN]; /* destination address */ + unsigned short h_proto; /* packet type ID field */ +} __attribute__((packed)); + +static inline struct ipv4_ether_hdr *ipv4_ether_hdr(const struct sk_buff *skb) +{ + return (struct ipv4_ether_hdr *)skb_mac_header(skb); +} + +enum ipv4_tx_type { + IPV4_UNKNOWN = 0, + IPV4_GASP = 1, + IPV4_WRREQ = 2, +}; + +enum ipv4_broadcast_state { + IPV4_BROADCAST_ERROR, + IPV4_BROADCAST_RUNNING, + IPV4_BROADCAST_STOPPED, +}; + +#define ipv4_get_hdr_lf(h) (((h)->w0&0xC0000000)>>30) +#define ipv4_get_hdr_ether_type(h) (((h)->w0&0x0000FFFF) ) +#define ipv4_get_hdr_dg_size(h) (((h)->w0&0x0FFF0000)>>16) +#define ipv4_get_hdr_fg_off(h) (((h)->w0&0x00000FFF) ) +#define ipv4_get_hdr_dgl(h) (((h)->w1&0xFFFF0000)>>16) + +#define ipv4_set_hdr_lf(lf) (( lf)<<30) +#define ipv4_set_hdr_ether_type(et) (( et) ) +#define ipv4_set_hdr_dg_size(dgs) ((dgs)<<16) +#define ipv4_set_hdr_fg_off(fgo) ((fgo) ) + +#define ipv4_set_hdr_dgl(dgl) ((dgl)<<16) + +struct ipv4_hdr { + u32 w0; + u32 w1; +}; + +static inline void ipv4_make_uf_hdr( struct ipv4_hdr *hdr, unsigned ether_type) { + hdr->w0 = ipv4_set_hdr_lf(IPV4_HDR_UNFRAG) + |ipv4_set_hdr_ether_type(ether_type); + fw_debug ( "Setting unfragmented header %p to %x\n", hdr, hdr->w0 ); +} + +static inline void ipv4_make_ff_hdr ( struct ipv4_hdr *hdr, unsigned ether_type, unsigned dg_size, unsigned dgl ) { + hdr->w0 = ipv4_set_hdr_lf(IPV4_HDR_FIRSTFRAG) + |ipv4_set_hdr_dg_size(dg_size) + |ipv4_set_hdr_ether_type(ether_type); + hdr->w1 = ipv4_set_hdr_dgl(dgl); + fw_debug ( "Setting fragmented header %p to first_frag %x,%x (et %x, dgs %x, dgl %x)\n", hdr, hdr->w0, hdr->w1, + ether_type, dg_size, dgl ); +} + +static inline void ipv4_make_sf_hdr ( struct ipv4_hdr *hdr, unsigned lf, unsigned dg_size, unsigned fg_off, unsigned dgl) { + hdr->w0 = ipv4_set_hdr_lf(lf) + |ipv4_set_hdr_dg_size(dg_size) + |ipv4_set_hdr_fg_off(fg_off); + hdr->w1 = ipv4_set_hdr_dgl(dgl); + fw_debug ( "Setting fragmented header %p to %x,%x (lf %x, dgs %x, fo %x dgl %x)\n", + hdr, hdr->w0, hdr->w1, + lf, dg_size, fg_off, dgl ); +} + +/* End of IP1394 headers */ + +/* Fragment types */ +#define ETH1394_HDR_LF_UF 0 /* unfragmented */ +#define ETH1394_HDR_LF_FF 1 /* first fragment */ +#define ETH1394_HDR_LF_LF 2 /* last fragment */ +#define ETH1394_HDR_LF_IF 3 /* interior fragment */ + +#define IP1394_HW_ADDR_LEN 16 /* As per RFC */ + +/* This list keeps track of what parts of the datagram have been filled in */ +struct ipv4_fragment_info { + struct list_head fragment_info; + u16 offset; + u16 len; +}; + +struct ipv4_partial_datagram { + struct list_head pdg_list; + struct list_head fragment_info; + struct sk_buff *skb; + /* FIXME Why not use skb->data? */ + char *pbuf; + u16 datagram_label; + u16 ether_type; + u16 datagram_size; +}; + +/* + * We keep one of these for each IPv4 capable device attached to a fw_card. + * The list of them is stored in the fw_card structure rather than in the + * ipv4_priv because the remote IPv4 nodes may be probed before the card is, + * so we need a place to store them before the ipv4_priv structure is + * allocated. + */ +struct ipv4_node { + struct list_head ipv4_nodes; + /* guid of the remote node */ + u64 guid; + /* FIFO address to transmit datagrams to, or INVALID_FIFO_ADDR */ + u64 fifo; + + spinlock_t pdg_lock; /* partial datagram lock */ + /* List of partial datagrams received from this node */ + struct list_head pdg_list; + /* Number of entries in pdg_list at the moment */ + unsigned pdg_size; + + /* max payload to transmit to this remote node */ + /* This already includes the IPV4_FRAG_HDR_SIZE overhead */ + u16 max_payload; + /* outgoing datagram label */ + u16 datagram_label; + /* Current node_id of the remote node */ + u16 nodeid; + /* current generation of the remote node */ + u8 generation; + /* max speed that this node can receive at */ + u8 xmt_speed; +}; + +struct ipv4_priv { + spinlock_t lock; + + enum ipv4_broadcast_state broadcast_state; + struct fw_iso_context *broadcast_rcv_context; + struct fw_iso_buffer broadcast_rcv_buffer; + void **broadcast_rcv_buffer_ptrs; + unsigned broadcast_rcv_next_ptr; + unsigned num_broadcast_rcv_ptrs; + unsigned rcv_buffer_size; + /* + * This value is the maximum unfragmented datagram size that can be + * sent by the hardware. It already has the GASP overhead and the + * unfragmented datagram header overhead calculated into it. + */ + unsigned broadcast_xmt_max_payload; + u16 broadcast_xmt_datagramlabel; + + /* + * The csr address that remote nodes must send datagrams to for us to + * receive them. + */ + struct fw_address_handler handler; + u64 local_fifo; + + /* Wake up to xmt */ + /* struct work_struct wake;*/ + /* List of packets to be sent */ + struct list_head packet_list; + /* + * List of packets that were broadcasted. When we get an ISO interrupt + * one of them has been sent + */ + struct list_head broadcasted_list; + /* List of packets that have been sent but not yet acked */ + struct list_head sent_list; + + struct fw_card *card; +}; + +/* This is our task struct. It's used for the packet complete callback. */ +struct ipv4_packet_task { + /* + * ptask can actually be on priv->packet_list, priv->broadcasted_list, + * or priv->sent_list depending on its current state. + */ + struct list_head packet_list; + struct fw_transaction transaction; + struct ipv4_hdr hdr; + struct sk_buff *skb; + struct ipv4_priv *priv; + enum ipv4_tx_type tx_type; + int outstanding_pkts; + unsigned max_payload; + u64 fifo_addr; + u16 dest_node; + u8 generation; + u8 speed; +}; + +static struct kmem_cache *ipv4_packet_task_cache; + +static const char ipv4_driver_name[] = "firewire-ipv4"; + +static const struct ieee1394_device_id ipv4_id_table[] = { + { + .match_flags = IEEE1394_MATCH_SPECIFIER_ID | + IEEE1394_MATCH_VERSION, + .specifier_id = IPV4_GASP_SPECIFIER_ID, + .version = IPV4_GASP_VERSION, + }, + { } +}; + +static u32 ipv4_unit_directory_data[] = { + 0x00040000, /* unit directory */ + 0x12000000 | IPV4_GASP_SPECIFIER_ID, /* specifier ID */ + 0x81000003, /* text descriptor */ + 0x13000000 | IPV4_GASP_VERSION, /* version */ + 0x81000005, /* text descriptor */ + + 0x00030000, /* Three quadlets */ + 0x00000000, /* Text */ + 0x00000000, /* Language 0 */ + 0x49414e41, /* I A N A */ + 0x00030000, /* Three quadlets */ + 0x00000000, /* Text */ + 0x00000000, /* Language 0 */ + 0x49507634, /* I P v 4 */ +}; + +static struct fw_descriptor ipv4_unit_directory = { + .length = ARRAY_SIZE(ipv4_unit_directory_data), + .key = 0xd1000000, + .data = ipv4_unit_directory_data +}; + +static int ipv4_send_packet(struct ipv4_packet_task *ptask ); + +/* ------------------------------------------------------------------ */ +/****************************************** + * HW Header net device functions + ******************************************/ + /* These functions have been adapted from net/ethernet/eth.c */ + +/* Create a fake MAC header for an arbitrary protocol layer. + * saddr=NULL means use device source address + * daddr=NULL means leave destination address (eg unresolved arp). */ + +static int ipv4_header ( struct sk_buff *skb, struct net_device *dev, + unsigned short type, const void *daddr, + const void *saddr, unsigned len) { + struct ipv4_ether_hdr *eth; + + eth = (struct ipv4_ether_hdr *)skb_push(skb, sizeof(*eth)); + eth->h_proto = htons(type); + + if (dev->flags & (IFF_LOOPBACK | IFF_NOARP)) { + memset(eth->h_dest, 0, dev->addr_len); + return dev->hard_header_len; + } + + if (daddr) { + memcpy(eth->h_dest, daddr, dev->addr_len); + return dev->hard_header_len; + } + + return -dev->hard_header_len; +} + +/* Rebuild the faked MAC header. This is called after an ARP + * (or in future other address resolution) has completed on this + * sk_buff. We now let ARP fill in the other fields. + * + * This routine CANNOT use cached dst->neigh! + * Really, it is used only when dst->neigh is wrong. + */ + +static int ipv4_rebuild_header(struct sk_buff *skb) +{ + struct ipv4_ether_hdr *eth; + + eth = (struct ipv4_ether_hdr *)skb->data; + if (eth->h_proto == htons(ETH_P_IP)) + return arp_find((unsigned char *)ð->h_dest, skb); + + fw_notify ( "%s: unable to resolve type %04x addresses\n", + skb->dev->name,ntohs(eth->h_proto) ); + return 0; +} + +static int ipv4_header_cache(const struct neighbour *neigh, struct hh_cache *hh) { + unsigned short type = hh->hh_type; + struct net_device *dev; + struct ipv4_ether_hdr *eth; + + if (type == htons(ETH_P_802_3)) + return -1; + dev = neigh->dev; + eth = (struct ipv4_ether_hdr *)((u8 *)hh->hh_data + 16 - sizeof(*eth)); + eth->h_proto = type; + memcpy(eth->h_dest, neigh->ha, dev->addr_len); + + hh->hh_len = IPV4_HLEN; + return 0; +} + +/* Called by Address Resolution module to notify changes in address. */ +static void ipv4_header_cache_update(struct hh_cache *hh, const struct net_device *dev, const unsigned char * haddr ) { + memcpy((u8 *)hh->hh_data + 16 - IPV4_HLEN, haddr, dev->addr_len); +} + +static int ipv4_header_parse(const struct sk_buff *skb, unsigned char *haddr) { + memcpy(haddr, skb->dev->dev_addr, IPV4_ALEN); + return IPV4_ALEN; +} + +static const struct header_ops ipv4_header_ops = { + .create = ipv4_header, + .rebuild = ipv4_rebuild_header, + .cache = ipv4_header_cache, + .cache_update = ipv4_header_cache_update, + .parse = ipv4_header_parse, +}; + +/* ------------------------------------------------------------------ */ + +/* FIXME: is this correct for all cases? */ +static bool ipv4_frag_overlap(struct ipv4_partial_datagram *pd, unsigned offset, unsigned len) +{ + struct ipv4_fragment_info *fi; + unsigned end = offset + len; + + list_for_each_entry(fi, &pd->fragment_info, fragment_info) { + if (offset < fi->offset + fi->len && end > fi->offset) { + fw_debug ( "frag_overlap pd %p fi %p (%x@%x) with %x@%x\n", pd, fi, fi->len, fi->offset, len, offset ); + return true; + } + } + fw_debug ( "frag_overlap %p does not overlap with %x@%x\n", pd, len, offset ); + return false; +} + +/* Assumes that new fragment does not overlap any existing fragments */ +static struct ipv4_fragment_info *ipv4_frag_new ( struct ipv4_partial_datagram *pd, unsigned offset, unsigned len ) { + struct ipv4_fragment_info *fi, *fi2, *new; + struct list_head *list; + + fw_debug ( "frag_new pd %p %x@%x\n", pd, len, offset ); + list = &pd->fragment_info; + list_for_each_entry(fi, &pd->fragment_info, fragment_info) { + if (fi->offset + fi->len == offset) { + /* The new fragment can be tacked on to the end */ + /* Did the new fragment plug a hole? */ + fi2 = list_entry(fi->fragment_info.next, struct ipv4_fragment_info, fragment_info); + if (fi->offset + fi->len == fi2->offset) { + fw_debug ( "pd %p: hole filling %p (%x@%x) and %p(%x@%x): now %x@%x\n", pd, fi, fi->len, fi->offset, + fi2, fi2->len, fi2->offset, fi->len + len + fi2->len, fi->offset ); + /* glue fragments together */ + fi->len += len + fi2->len; + list_del(&fi2->fragment_info); + kfree(fi2); + } else { + fw_debug ( "pd %p: extending %p from %x@%x to %x@%x\n", pd, fi, fi->len, fi->offset, fi->len+len, fi->offset ); + fi->len += len; + } + return fi; + } + if (offset + len == fi->offset) { + /* The new fragment can be tacked on to the beginning */ + /* Did the new fragment plug a hole? */ + fi2 = list_entry(fi->fragment_info.prev, struct ipv4_fragment_info, fragment_info); + if (fi2->offset + fi2->len == fi->offset) { + /* glue fragments together */ + fw_debug ( "pd %p: extending %p and merging with %p from %x@%x to %x@%x\n", + pd, fi2, fi, fi2->len, fi2->offset, fi2->len + fi->len + len, fi2->offset ); + fi2->len += fi->len + len; + list_del(&fi->fragment_info); + kfree(fi); + return fi2; + } + fw_debug ( "pd %p: extending %p from %x@%x to %x@%x\n", pd, fi, fi->len, fi->offset, offset, fi->len + len ); + fi->offset = offset; + fi->len += len; + return fi; + } + if (offset > fi->offset + fi->len) { + list = &fi->fragment_info; + break; + } + if (offset + len < fi->offset) { + list = fi->fragment_info.prev; + break; + } + } + + new = kmalloc(sizeof(*new), GFP_ATOMIC); + if (!new) { + fw_error ( "out of memory in fragment handling!\n" ); + return NULL; + } + + new->offset = offset; + new->len = len; + list_add(&new->fragment_info, list); + fw_debug ( "pd %p: new frag %p %x@%x\n", pd, new, new->len, new->offset ); + list_for_each_entry( fi, &pd->fragment_info, fragment_info ) + fw_debug ( "fi %p %x@%x\n", fi, fi->len, fi->offset ); + return new; +} + +/* ------------------------------------------------------------------ */ + +static struct ipv4_partial_datagram *ipv4_pd_new(struct net_device *netdev, + struct ipv4_node *node, u16 datagram_label, unsigned dg_size, u32 *frag_buf, + unsigned frag_off, unsigned frag_len) { + struct ipv4_partial_datagram *new; + struct ipv4_fragment_info *fi; + + new = kmalloc(sizeof(*new), GFP_ATOMIC); + if (!new) + goto fail; + INIT_LIST_HEAD(&new->fragment_info); + fi = ipv4_frag_new ( new, frag_off, frag_len); + if ( fi == NULL ) + goto fail_w_new; + new->datagram_label = datagram_label; + new->datagram_size = dg_size; + new->skb = dev_alloc_skb(dg_size + netdev->hard_header_len + 15); + if ( new->skb == NULL ) + goto fail_w_fi; + skb_reserve(new->skb, (netdev->hard_header_len + 15) & ~15); + new->pbuf = skb_put(new->skb, dg_size); + memcpy(new->pbuf + frag_off, frag_buf, frag_len); + list_add_tail(&new->pdg_list, &node->pdg_list); + fw_debug ( "pd_new: new pd %p { dgl %u, dg_size %u, skb %p, pbuf %p } on node %p\n", + new, new->datagram_label, new->datagram_size, new->skb, new->pbuf, node ); + return new; + +fail_w_fi: + kfree(fi); +fail_w_new: + kfree(new); +fail: + fw_error("ipv4_pd_new: no memory\n"); + return NULL; +} + +static struct ipv4_partial_datagram *ipv4_pd_find(struct ipv4_node *node, u16 datagram_label) { + struct ipv4_partial_datagram *pd; + + list_for_each_entry(pd, &node->pdg_list, pdg_list) { + if ( pd->datagram_label == datagram_label ) { + fw_debug ( "pd_find(node %p, label %u): pd %p\n", node, datagram_label, pd ); + return pd; + } + } + fw_debug ( "pd_find(node %p, label %u) no entry\n", node, datagram_label ); + return NULL; +} + + +static void ipv4_pd_delete ( struct ipv4_partial_datagram *old ) { + struct ipv4_fragment_info *fi, *n; + + fw_debug ( "pd_delete %p\n", old ); + list_for_each_entry_safe(fi, n, &old->fragment_info, fragment_info) { + fw_debug ( "Freeing fi %p\n", fi ); + kfree(fi); + } + list_del(&old->pdg_list); + dev_kfree_skb_any(old->skb); + kfree(old); +} + +static bool ipv4_pd_update ( struct ipv4_node *node, struct ipv4_partial_datagram *pd, + u32 *frag_buf, unsigned frag_off, unsigned frag_len) { + fw_debug ( "pd_update node %p, pd %p, frag_buf %p, %x@%x\n", node, pd, frag_buf, frag_len, frag_off ); + if ( ipv4_frag_new ( pd, frag_off, frag_len ) == NULL) + return false; + memcpy(pd->pbuf + frag_off, frag_buf, frag_len); + + /* + * Move list entry to beginnig of list so that oldest partial + * datagrams percolate to the end of the list + */ + list_move_tail(&pd->pdg_list, &node->pdg_list); + fw_debug ( "New pd list:\n" ); + list_for_each_entry ( pd, &node->pdg_list, pdg_list ) { + fw_debug ( "pd %p\n", pd ); + } + return true; +} + +static bool ipv4_pd_is_complete ( struct ipv4_partial_datagram *pd ) { + struct ipv4_fragment_info *fi; + bool ret; + + fi = list_entry(pd->fragment_info.next, struct ipv4_fragment_info, fragment_info); + + ret = (fi->len == pd->datagram_size); + fw_debug ( "pd_is_complete (pd %p, dgs %x): fi %p (%x@%x) %s\n", pd, pd->datagram_size, fi, fi->len, fi->offset, ret ? "yes" : "no" ); + return ret; +} + +/* ------------------------------------------------------------------ */ + +static int ipv4_node_new ( struct fw_card *card, struct fw_device *device ) { + struct ipv4_node *node; + + node = kmalloc ( sizeof(*node), GFP_KERNEL ); + if ( ! node ) { + fw_error ( "allocate new node failed\n" ); + return -ENOMEM; + } + node->guid = (u64)device->config_rom[3] << 32 | device->config_rom[4]; + node->fifo = INVALID_FIFO_ADDR; + INIT_LIST_HEAD(&node->pdg_list); + spin_lock_init(&node->pdg_lock); + node->pdg_size = 0; + node->generation = device->generation; + rmb(); + node->nodeid = device->node_id; + /* FIXME what should it really be? */ + node->max_payload = S100_BUFFER_SIZE - IPV4_UNFRAG_HDR_SIZE; + node->datagram_label = 0U; + node->xmt_speed = device->max_speed; + list_add_tail ( &node->ipv4_nodes, &card->ipv4_nodes ); + fw_debug ( "node_new: %p { guid %016llx, generation %u, nodeid %x, max_payload %x, xmt_speed %x } added\n", + node, (unsigned long long)node->guid, node->generation, node->nodeid, node->max_payload, node->xmt_speed ); + return 0; +} + +static struct ipv4_node *ipv4_node_find_by_guid(struct ipv4_priv *priv, u64 guid) { + struct ipv4_node *node; + unsigned long flags; + + spin_lock_irqsave(&priv->lock, flags); + list_for_each_entry(node, &priv->card->ipv4_nodes, ipv4_nodes) + if (node->guid == guid) { + /* FIXME: lock the node first? */ + spin_unlock_irqrestore ( &priv->lock, flags ); + fw_debug ( "node_find_by_guid (%016llx) found %p\n", (unsigned long long)guid, node ); + return node; + } + + spin_unlock_irqrestore ( &priv->lock, flags ); + fw_debug ( "node_find_by_guid (%016llx) not found\n", (unsigned long long)guid ); + return NULL; +} + +static struct ipv4_node *ipv4_node_find_by_nodeid(struct ipv4_priv *priv, u16 nodeid) { + struct ipv4_node *node; + unsigned long flags; + + spin_lock_irqsave(&priv->lock, flags); + list_for_each_entry(node, &priv->card->ipv4_nodes, ipv4_nodes) + if (node->nodeid == nodeid) { + /* FIXME: lock the node first? */ + spin_unlock_irqrestore ( &priv->lock, flags ); + fw_debug ( "node_find_by_nodeid (%x) found %p\n", nodeid, node ); + return node; + } + fw_debug ( "node_find_by_nodeid (%x) not found\n", nodeid ); + spin_unlock_irqrestore ( &priv->lock, flags ); + return NULL; +} + +/* This is only complicated because we can't assume priv exists */ +static void ipv4_node_delete ( struct fw_card *card, struct fw_device *device ) { + struct net_device *netdev; + struct ipv4_priv *priv; + struct ipv4_node *node; + u64 guid; + unsigned long flags; + struct ipv4_partial_datagram *pd, *pd_next; + + guid = (u64)device->config_rom[3] << 32 | device->config_rom[4]; + netdev = card->netdev; + if ( netdev ) + priv = netdev_priv ( netdev ); + else + priv = NULL; + if ( priv ) + spin_lock_irqsave ( &priv->lock, flags ); + list_for_each_entry( node, &card->ipv4_nodes, ipv4_nodes ) { + if ( node->guid == guid ) { + list_del ( &node->ipv4_nodes ); + list_for_each_entry_safe( pd, pd_next, &node->pdg_list, pdg_list ) + ipv4_pd_delete ( pd ); + break; + } + } + if ( priv ) + spin_unlock_irqrestore ( &priv->lock, flags ); +} + +/* ------------------------------------------------------------------ */ + + +static int ipv4_finish_incoming_packet ( struct net_device *netdev, + struct sk_buff *skb, u16 source_node_id, bool is_broadcast, u16 ether_type ) { + struct ipv4_priv *priv; + static u64 broadcast_hw = ~0ULL; + int status; + u64 guid; + + fw_debug ( "ipv4_finish_incoming_packet(%p, %p, %x, %s, %x\n", + netdev, skb, source_node_id, is_broadcast ? "true" : "false", ether_type ); + priv = netdev_priv(netdev); + /* Write metadata, and then pass to the receive level */ + skb->dev = netdev; + skb->ip_summed = CHECKSUM_UNNECESSARY; /* don't check it */ + + /* + * Parse the encapsulation header. This actually does the job of + * converting to an ethernet frame header, as well as arp + * conversion if needed. ARP conversion is easier in this + * direction, since we are using ethernet as our backend. + */ + /* + * If this is an ARP packet, convert it. First, we want to make + * use of some of the fields, since they tell us a little bit + * about the sending machine. + */ + if (ether_type == ETH_P_ARP) { + struct ipv4_arp *arp1394; + struct arphdr *arp; + unsigned char *arp_ptr; + u64 fifo_addr; + u8 max_rec; + u8 sspd; + u16 max_payload; + struct ipv4_node *node; + static const u16 ipv4_speed_to_max_payload[] = { + /* S100, S200, S400, S800, S1600, S3200 */ + 512, 1024, 2048, 4096, 4096, 4096 + }; + + /* fw_debug ( "ARP packet\n" ); */ + arp1394 = (struct ipv4_arp *)skb->data; + arp = (struct arphdr *)skb->data; + arp_ptr = (unsigned char *)(arp + 1); + fifo_addr = (u64)ntohs(arp1394->fifo_hi) << 32 | + ntohl(arp1394->fifo_lo); + max_rec = priv->card->max_receive; + if ( arp1394->max_rec < max_rec ) + max_rec = arp1394->max_rec; + sspd = arp1394->sspd; + /* + * Sanity check. MacOSX seems to be sending us 131 in this + * field (atleast on my Panther G5). Not sure why. + */ + if (sspd > 5 ) { + fw_notify ( "sspd %x out of range\n", sspd ); + sspd = 0; + } + + max_payload = min(ipv4_speed_to_max_payload[sspd], + (u16)(1 << (max_rec + 1))) - IPV4_UNFRAG_HDR_SIZE; + + guid = be64_to_cpu(get_unaligned(&arp1394->s_uniq_id)); + node = ipv4_node_find_by_guid(priv, guid); + if (!node) { + fw_notify ( "No node for ARP packet from %llx\n", guid ); + goto failed_proto; + } + if ( node->nodeid != source_node_id || node->generation != priv->card->generation ) { + fw_notify ( "Internal error: node->nodeid (%x) != soucre_node_id (%x) or node->generation (%x) != priv->card->generation(%x)\n", + node->nodeid, source_node_id, node->generation, priv->card->generation ); + node->nodeid = source_node_id; + node->generation = priv->card->generation; + } + + /* FIXME: for debugging */ + if ( sspd > SCODE_400 ) + sspd = SCODE_400; + /* Update our speed/payload/fifo_offset table */ + /* + * FIXME: this does not handle cases where two high-speed endpoints must use a slower speed because of + * a lower speed hub between them. We need to look at the actual topology map here. + */ + fw_debug ( "Setting node %p fifo %llx (was %llx), max_payload %x (was %x), speed %x (was %x)\n", + node, fifo_addr, node->fifo, max_payload, node->max_payload, sspd, node->xmt_speed ); + node->fifo = fifo_addr; + node->max_payload = max_payload; + /* + * Only allow speeds to go down from their initial value. + * Otherwise a local node that can only do S400 or slower may + * be told to transmit at S800 to a faster remote node. + */ + if ( node->xmt_speed > sspd ) + node->xmt_speed = sspd; + + /* + * Now that we're done with the 1394 specific stuff, we'll + * need to alter some of the data. Believe it or not, all + * that needs to be done is sender_IP_address needs to be + * moved, the destination hardware address get stuffed + * in and the hardware address length set to 8. + * + * IMPORTANT: The code below overwrites 1394 specific data + * needed above so keep the munging of the data for the + * higher level IP stack last. + */ + + arp->ar_hln = 8; + arp_ptr += arp->ar_hln; /* skip over sender unique id */ + *(u32 *)arp_ptr = arp1394->sip; /* move sender IP addr */ + arp_ptr += arp->ar_pln; /* skip over sender IP addr */ + + if (arp->ar_op == htons(ARPOP_REQUEST)) + memset(arp_ptr, 0, sizeof(u64)); + else + memcpy(arp_ptr, netdev->dev_addr, sizeof(u64)); + } + + /* Now add the ethernet header. */ + guid = cpu_to_be64(priv->card->guid); + if (dev_hard_header(skb, netdev, ether_type, is_broadcast ? &broadcast_hw : &guid, NULL, + skb->len) >= 0) { + struct ipv4_ether_hdr *eth; + u16 *rawp; + __be16 protocol; + + skb_reset_mac_header(skb); + skb_pull(skb, sizeof(*eth)); + eth = ipv4_ether_hdr(skb); + if (*eth->h_dest & 1) { + if (memcmp(eth->h_dest, netdev->broadcast, netdev->addr_len) == 0) { + fw_debug ( "Broadcast\n" ); + skb->pkt_type = PACKET_BROADCAST; + } +#if 0 + else + skb->pkt_type = PACKET_MULTICAST; +#endif + } else { + if (memcmp(eth->h_dest, netdev->dev_addr, netdev->addr_len)) { + u64 a1, a2; + + memcpy ( &a1, eth->h_dest, sizeof(u64)); + memcpy ( &a2, netdev->dev_addr, sizeof(u64)); + fw_debug ( "Otherhost %llx %llx %x\n", a1, a2, netdev->addr_len ); + skb->pkt_type = PACKET_OTHERHOST; + } + } + if (ntohs(eth->h_proto) >= 1536) { + fw_debug ( " proto %x %x\n", eth->h_proto, ntohs(eth->h_proto) ); + protocol = eth->h_proto; + } else { + rawp = (u16 *)skb->data; + if (*rawp == 0xFFFF) { + fw_debug ( "proto 802_3\n" ); + protocol = htons(ETH_P_802_3); + } else { + fw_debug ( "proto 802_2\n" ); + protocol = htons(ETH_P_802_2); + } + } + skb->protocol = protocol; + } + status = netif_rx(skb); + if ( status == NET_RX_DROP) { + netdev->stats.rx_errors++; + netdev->stats.rx_dropped++; + } else { + netdev->stats.rx_packets++; + netdev->stats.rx_bytes += skb->len; + } + if (netif_queue_stopped(netdev)) + netif_wake_queue(netdev); + return 0; + + failed_proto: + netdev->stats.rx_errors++; + netdev->stats.rx_dropped++; + dev_kfree_skb_any(skb); + if (netif_queue_stopped(netdev)) + netif_wake_queue(netdev); + netdev->last_rx = jiffies; + return 0; +} + +/* ------------------------------------------------------------------ */ + +static int ipv4_incoming_packet ( struct ipv4_priv *priv, u32 *buf, int len, u16 source_node_id, bool is_broadcast ) { + struct sk_buff *skb; + struct net_device *netdev; + struct ipv4_hdr hdr; + unsigned lf; + unsigned long flags; + struct ipv4_node *node; + struct ipv4_partial_datagram *pd; + int fg_off; + int dg_size; + u16 datagram_label; + int retval; + u16 ether_type; + + fw_debug ( "ipv4_incoming_packet(%p, %p, %d, %x, %s)\n", priv, buf, len, source_node_id, is_broadcast ? "true" : "false" ); + netdev = priv->card->netdev; + + hdr.w0 = ntohl(buf[0]); + lf = ipv4_get_hdr_lf(&hdr); + if ( lf == IPV4_HDR_UNFRAG ) { + /* + * An unfragmented datagram has been received by the ieee1394 + * bus. Build an skbuff around it so we can pass it to the + * high level network layer. + */ + ether_type = ipv4_get_hdr_ether_type(&hdr); + fw_debug ( "header w0 = %x, lf = %x, ether_type = %x\n", hdr.w0, lf, ether_type ); + buf++; + len -= IPV4_UNFRAG_HDR_SIZE; + + skb = dev_alloc_skb(len + netdev->hard_header_len + 15); + if (unlikely(!skb)) { + fw_error ( "Out of memory for incoming packet\n"); + netdev->stats.rx_dropped++; + return -1; + } + skb_reserve(skb, (netdev->hard_header_len + 15) & ~15); + memcpy(skb_put(skb, len), buf, len ); + return ipv4_finish_incoming_packet(netdev, skb, source_node_id, is_broadcast, ether_type ); + } + /* A datagram fragment has been received, now the fun begins. */ + hdr.w1 = ntohl(buf[1]); + buf +=2; + len -= IPV4_FRAG_HDR_SIZE; + if ( lf ==IPV4_HDR_FIRSTFRAG ) { + ether_type = ipv4_get_hdr_ether_type(&hdr); + fg_off = 0; + } else { + fg_off = ipv4_get_hdr_fg_off(&hdr); + ether_type = 0; /* Shut up compiler! */ + } + datagram_label = ipv4_get_hdr_dgl(&hdr); + dg_size = ipv4_get_hdr_dg_size(&hdr); /* ??? + 1 */ + fw_debug ( "fragmented: %x.%x = lf %x, ether_type %x, fg_off %x, dgl %x, dg_size %x\n", hdr.w0, hdr.w1, lf, ether_type, fg_off, datagram_label, dg_size ); + node = ipv4_node_find_by_nodeid ( priv, source_node_id); + spin_lock_irqsave(&node->pdg_lock, flags); + pd = ipv4_pd_find( node, datagram_label ); + if (pd == NULL) { + while ( node->pdg_size >= ipv4_mpd ) { + /* remove the oldest */ + ipv4_pd_delete ( list_first_entry(&node->pdg_list, struct ipv4_partial_datagram, pdg_list) ); + node->pdg_size--; + } + pd = ipv4_pd_new ( netdev, node, datagram_label, dg_size, + buf, fg_off, len); + if ( pd == NULL) { + retval = -ENOMEM; + goto bad_proto; + } + node->pdg_size++; + } else { + if (ipv4_frag_overlap(pd, fg_off, len) || pd->datagram_size != dg_size) { + /* + * Differing datagram sizes or overlapping fragments, + * Either way the remote machine is playing silly buggers + * with us: obliterate the old datagram and start a new one. + */ + ipv4_pd_delete ( pd ); + pd = ipv4_pd_new ( netdev, node, datagram_label, + dg_size, buf, fg_off, len); + if ( pd == NULL ) { + retval = -ENOMEM; + node->pdg_size--; + goto bad_proto; + } + } else { + bool worked; + + worked = ipv4_pd_update ( node, pd, + buf, fg_off, len ); + if ( ! worked ) { + /* + * Couldn't save off fragment anyway + * so might as well obliterate the + * datagram now. + */ + ipv4_pd_delete ( pd ); + node->pdg_size--; + goto bad_proto; + } + } + } /* new datagram or add to existing one */ + + if ( lf == IPV4_HDR_FIRSTFRAG ) + pd->ether_type = ether_type; + if ( ipv4_pd_is_complete ( pd ) ) { + ether_type = pd->ether_type; + node->pdg_size--; + skb = skb_get(pd->skb); + ipv4_pd_delete ( pd ); + spin_unlock_irqrestore(&node->pdg_lock, flags); + return ipv4_finish_incoming_packet ( netdev, skb, source_node_id, false, ether_type ); + } + /* + * Datagram is not complete, we're done for the + * moment. + */ + spin_unlock_irqrestore(&node->pdg_lock, flags); + return 0; + + bad_proto: + spin_unlock_irqrestore(&node->pdg_lock, flags); + if (netif_queue_stopped(netdev)) + netif_wake_queue(netdev); + return 0; +} + +static void ipv4_receive_packet ( 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 ipv4_priv *priv; + int status; + + fw_debug ( "ipv4_receive_packet(%p,%p,%x,%x,%x,%x,%x,%llx,%p,%lx,%p)\n", + card, r, tcode, destination, source, generation, speed, offset, payload, + (unsigned long)length, callback_data); + print_hex_dump ( KERN_DEBUG, "header: ", DUMP_PREFIX_OFFSET, 32, 1, payload, length, false ); + priv = callback_data; + if ( tcode != TCODE_WRITE_BLOCK_REQUEST + || destination != card->node_id + || generation != card->generation + || offset != priv->handler.offset ) { + fw_send_response(card, r, RCODE_CONFLICT_ERROR); + fw_debug("Conflict error card node_id=%x, card generation=%x, local offset %llx\n", + card->node_id, card->generation, (unsigned long long)priv->handler.offset ); + return; + } + status = ipv4_incoming_packet ( priv, payload, length, source, false ); + if ( status != 0 ) { + fw_error ( "Incoming packet failure\n" ); + fw_send_response ( card, r, RCODE_CONFLICT_ERROR ); + return; + } + fw_send_response ( card, r, RCODE_COMPLETE ); +} + +static void ipv4_receive_broadcast(struct fw_iso_context *context, u32 cycle, + size_t header_length, void *header, void *data) { + struct ipv4_priv *priv; + struct fw_iso_packet packet; + struct fw_card *card; + u16 *hdr_ptr; + u32 *buf_ptr; + int retval; + u32 length; + u16 source_node_id; + u32 specifier_id; + u32 ver; + unsigned long offset; + unsigned long flags; + + fw_debug ( "ipv4_receive_broadcast ( context=%p, cycle=%x, header_length=%lx, header=%p, data=%p )\n", context, cycle, (unsigned long)header_length, header, data ); + print_hex_dump ( KERN_DEBUG, "header: ", DUMP_PREFIX_OFFSET, 32, 1, header, header_length, false ); + priv = data; + card = priv->card; + hdr_ptr = header; + length = ntohs(hdr_ptr[0]); + spin_lock_irqsave(&priv->lock,flags); + offset = priv->rcv_buffer_size * priv->broadcast_rcv_next_ptr; + buf_ptr = priv->broadcast_rcv_buffer_ptrs[priv->broadcast_rcv_next_ptr++]; + if ( priv->broadcast_rcv_next_ptr == priv->num_broadcast_rcv_ptrs ) + priv->broadcast_rcv_next_ptr = 0; + spin_unlock_irqrestore(&priv->lock,flags); + fw_debug ( "length %u at %p\n", length, buf_ptr ); + print_hex_dump ( KERN_DEBUG, "buffer: ", DUMP_PREFIX_OFFSET, 32, 1, buf_ptr, length, false ); + + specifier_id = (be32_to_cpu(buf_ptr[0]) & 0xffff) << 8 + | (be32_to_cpu(buf_ptr[1]) & 0xff000000) >> 24; + ver = be32_to_cpu(buf_ptr[1]) & 0xFFFFFF; + source_node_id = be32_to_cpu(buf_ptr[0]) >> 16; + /* fw_debug ( "source %x SpecID %x ver %x\n", source_node_id, specifier_id, ver ); */ + if ( specifier_id == IPV4_GASP_SPECIFIER_ID && ver == IPV4_GASP_VERSION ) { + buf_ptr += 2; + length -= IPV4_GASP_OVERHEAD; + ipv4_incoming_packet(priv, buf_ptr, length, source_node_id, true); + } else + fw_debug ( "Ignoring packet: not GASP\n" ); + packet.payload_length = priv->rcv_buffer_size; + packet.interrupt = 1; + packet.skip = 0; + packet.tag = 3; + packet.sy = 0; + packet.header_length = IPV4_GASP_OVERHEAD; + spin_lock_irqsave(&priv->lock,flags); + retval = fw_iso_context_queue ( priv->broadcast_rcv_context, &packet, + &priv->broadcast_rcv_buffer, offset ); + spin_unlock_irqrestore(&priv->lock,flags); + if ( retval < 0 ) + fw_error ( "requeue failed\n" ); +} + +static void debug_ptask ( struct ipv4_packet_task *ptask ) { + static const char *tx_types[] = { "Unknown", "GASP", "Write" }; + + fw_debug ( "packet %p { hdr { w0 %x w1 %x }, skb %p, priv %p," + " tx_type %s, outstanding_pkts %d, max_payload %x, fifo %llx," + " speed %x, dest_node %x, generation %x }\n", + ptask, ptask->hdr.w0, ptask->hdr.w1, ptask->skb, ptask->priv, + ptask->tx_type > IPV4_WRREQ ? "Invalid" : tx_types[ptask->tx_type], + ptask->outstanding_pkts, ptask->max_payload, + ptask->fifo_addr, ptask->speed, ptask->dest_node, ptask->generation ); + print_hex_dump ( KERN_DEBUG, "packet :", DUMP_PREFIX_OFFSET, 32, 1, + ptask->skb->data, ptask->skb->len, false ); +} + +static void ipv4_transmit_packet_done ( struct ipv4_packet_task *ptask ) { + struct ipv4_priv *priv; + unsigned long flags; + + priv = ptask->priv; + spin_lock_irqsave ( &priv->lock, flags ); + list_del ( &ptask->packet_list ); + spin_unlock_irqrestore ( &priv->lock, flags ); + ptask->outstanding_pkts--; + if ( ptask->outstanding_pkts > 0 ) { + u16 dg_size; + u16 fg_off; + u16 datagram_label; + u16 lf; + struct sk_buff *skb; + + /* Update the ptask to point to the next fragment and send it */ + lf = ipv4_get_hdr_lf(&ptask->hdr); + switch (lf) { + case IPV4_HDR_LASTFRAG: + case IPV4_HDR_UNFRAG: + default: + fw_error ( "Outstanding packet %x lf %x, header %x,%x\n", ptask->outstanding_pkts, lf, ptask->hdr.w0, ptask->hdr.w1 ); + BUG(); + + case IPV4_HDR_FIRSTFRAG: + /* Set frag type here for future interior fragments */ + dg_size = ipv4_get_hdr_dg_size(&ptask->hdr); + fg_off = ptask->max_payload - IPV4_FRAG_HDR_SIZE; + datagram_label = ipv4_get_hdr_dgl(&ptask->hdr); + break; + + case IPV4_HDR_INTFRAG: + dg_size = ipv4_get_hdr_dg_size(&ptask->hdr); + fg_off = ipv4_get_hdr_fg_off(&ptask->hdr) + ptask->max_payload - IPV4_FRAG_HDR_SIZE; + datagram_label = ipv4_get_hdr_dgl(&ptask->hdr); + break; + } + skb = ptask->skb; + skb_pull ( skb, ptask->max_payload ); + if ( ptask->outstanding_pkts > 1 ) { + ipv4_make_sf_hdr ( &ptask->hdr, + IPV4_HDR_INTFRAG, dg_size, fg_off, datagram_label ); + } else { + ipv4_make_sf_hdr ( &ptask->hdr, + IPV4_HDR_LASTFRAG, dg_size, fg_off, datagram_label ); + ptask->max_payload = skb->len + IPV4_FRAG_HDR_SIZE; + + } + ipv4_send_packet ( ptask ); + } else { + dev_kfree_skb_any ( ptask->skb ); + kmem_cache_free( ipv4_packet_task_cache, ptask ); + } +} + +static void ipv4_write_complete ( struct fw_card *card, int rcode, + void *payload, size_t length, void *data ) { + struct ipv4_packet_task *ptask; + + ptask = data; + fw_debug ( "ipv4_write_complete ( %p, %x, %p, %lx, %p )\n", + card, rcode, payload, (unsigned long)length, data ); + debug_ptask ( ptask ); + + if ( rcode == RCODE_COMPLETE ) { + ipv4_transmit_packet_done ( ptask ); + } else { + fw_error ( "ipv4_write_complete: failed: %x\n", rcode ); + /* ??? error recovery */ + } +} + +static int ipv4_send_packet ( struct ipv4_packet_task *ptask ) { + struct ipv4_priv *priv; + unsigned tx_len; + struct ipv4_hdr *bufhdr; + unsigned long flags; + struct net_device *netdev; +#if 0 /* stefanr */ + int retval; +#endif + + fw_debug ( "ipv4_send_packet\n" ); + debug_ptask ( ptask ); + priv = ptask->priv; + tx_len = ptask->max_payload; + switch (ipv4_get_hdr_lf(&ptask->hdr)) { + case IPV4_HDR_UNFRAG: + bufhdr = (struct ipv4_hdr *)skb_push(ptask->skb, IPV4_UNFRAG_HDR_SIZE); + bufhdr->w0 = htonl(ptask->hdr.w0); + break; + + case IPV4_HDR_FIRSTFRAG: + case IPV4_HDR_INTFRAG: + case IPV4_HDR_LASTFRAG: + bufhdr = (struct ipv4_hdr *)skb_push(ptask->skb, IPV4_FRAG_HDR_SIZE); + bufhdr->w0 = htonl(ptask->hdr.w0); + bufhdr->w1 = htonl(ptask->hdr.w1); + break; + + default: + BUG(); + } + if ( ptask->tx_type == IPV4_GASP ) { + u32 *packets; + int generation; + int nodeid; + + /* ptask->generation may not have been set yet */ + generation = priv->card->generation; + smp_rmb(); + nodeid = priv->card->node_id; + packets = (u32 *)skb_push(ptask->skb, sizeof(u32)*2); + packets[0] = htonl(nodeid << 16 | (IPV4_GASP_SPECIFIER_ID>>8)); + packets[1] = htonl((IPV4_GASP_SPECIFIER_ID & 0xFF) << 24 | IPV4_GASP_VERSION); + fw_send_request ( priv->card, &ptask->transaction, TCODE_STREAM_DATA, + fw_stream_packet_destination_id(3, BROADCAST_CHANNEL, 0), + generation, SCODE_100, 0ULL, ptask->skb->data, tx_len + 8, ipv4_write_complete, ptask ); + spin_lock_irqsave(&priv->lock,flags); + list_add_tail ( &ptask->packet_list, &priv->broadcasted_list ); + spin_unlock_irqrestore(&priv->lock,flags); +#if 0 /* stefanr */ + return retval; +#else + return 0; +#endif + } + fw_debug("send_request (%p, %p, WRITE_BLOCK, %x, %x, %x, %llx, %p, %d, %p, %p\n", + priv->card, &ptask->transaction, ptask->dest_node, ptask->generation, + ptask->speed, (unsigned long long)ptask->fifo_addr, ptask->skb->data, tx_len, + ipv4_write_complete, ptask ); + fw_send_request ( priv->card, &ptask->transaction, + TCODE_WRITE_BLOCK_REQUEST, ptask->dest_node, ptask->generation, ptask->speed, + ptask->fifo_addr, ptask->skb->data, tx_len, ipv4_write_complete, ptask ); + spin_lock_irqsave(&priv->lock,flags); + list_add_tail ( &ptask->packet_list, &priv->sent_list ); + spin_unlock_irqrestore(&priv->lock,flags); + netdev = priv->card->netdev; + netdev->trans_start = jiffies; + return 0; +} + +static int ipv4_broadcast_start ( struct ipv4_priv *priv ) { + struct fw_iso_context *context; + int retval; + unsigned num_packets; + unsigned max_receive; + struct fw_iso_packet packet; + unsigned long offset; + unsigned u; + /* unsigned transmit_speed; */ + +#if 0 /* stefanr */ + if ( priv->card->broadcast_channel != (BROADCAST_CHANNEL_VALID|BROADCAST_CHANNEL_INITIAL)) { + fw_notify ( "Invalid broadcast channel %x\n", priv->card->broadcast_channel ); + /* FIXME: try again later? */ + /* return -EINVAL; */ + } +#endif + if ( priv->local_fifo == INVALID_FIFO_ADDR ) { + struct fw_address_region region; + + priv->handler.length = FIFO_SIZE; + priv->handler.address_callback = ipv4_receive_packet; + priv->handler.callback_data = priv; + /* FIXME: this is OHCI, but what about others? */ + region.start = 0xffff00000000ULL; + region.end = 0xfffffffffffcULL; + + retval = fw_core_add_address_handler ( &priv->handler, ®ion ); + if ( retval < 0 ) + goto failed_initial; + priv->local_fifo = priv->handler.offset; + } + + /* + * FIXME: rawiso limits us to PAGE_SIZE. This only matters if we ever have + * a machine with PAGE_SIZE < 4096 + */ + max_receive = 1U << (priv->card->max_receive + 1); + num_packets = ( ipv4_iso_page_count * PAGE_SIZE ) / max_receive; + if ( ! priv->broadcast_rcv_context ) { + void **ptrptr; + + context = fw_iso_context_create ( priv->card, + FW_ISO_CONTEXT_RECEIVE, BROADCAST_CHANNEL, + priv->card->link_speed, 8, ipv4_receive_broadcast, priv ); + if (IS_ERR(context)) { + retval = PTR_ERR(context); + goto failed_context_create; + } + retval = fw_iso_buffer_init ( &priv->broadcast_rcv_buffer, + priv->card, ipv4_iso_page_count, DMA_FROM_DEVICE ); + if ( retval < 0 ) + goto failed_buffer_init; + ptrptr = kmalloc ( sizeof(void*)*num_packets, GFP_KERNEL ); + if ( ! ptrptr ) { + retval = -ENOMEM; + goto failed_ptrs_alloc; + } + priv->broadcast_rcv_buffer_ptrs = ptrptr; + for ( u = 0; u < ipv4_iso_page_count; u++ ) { + void *ptr; + unsigned v; + + ptr = kmap ( priv->broadcast_rcv_buffer.pages[u] ); + for ( v = 0; v < num_packets / ipv4_iso_page_count; v++ ) + *ptrptr++ = (void *)((char *)ptr + v * max_receive); + } + priv->broadcast_rcv_context = context; + } else + context = priv->broadcast_rcv_context; + + packet.payload_length = max_receive; + packet.interrupt = 1; + packet.skip = 0; + packet.tag = 3; + packet.sy = 0; + packet.header_length = IPV4_GASP_OVERHEAD; + offset = 0; + for ( u = 0; u < num_packets; u++ ) { + retval = fw_iso_context_queue ( context, &packet, + &priv->broadcast_rcv_buffer, offset ); + if ( retval < 0 ) + goto failed_rcv_queue; + offset += max_receive; + } + priv->num_broadcast_rcv_ptrs = num_packets; + priv->rcv_buffer_size = max_receive; + priv->broadcast_rcv_next_ptr = 0U; + retval = fw_iso_context_start ( context, -1, 0, FW_ISO_CONTEXT_MATCH_ALL_TAGS ); /* ??? sync */ + if ( retval < 0 ) + goto failed_rcv_queue; + /* FIXME: adjust this when we know the max receive speeds of all other IP nodes on the bus. */ + /* since we only xmt at S100 ??? */ + priv->broadcast_xmt_max_payload = S100_BUFFER_SIZE - IPV4_GASP_OVERHEAD - IPV4_UNFRAG_HDR_SIZE; + priv->broadcast_state = IPV4_BROADCAST_RUNNING; + return 0; + + failed_rcv_queue: + kfree ( priv->broadcast_rcv_buffer_ptrs ); + priv->broadcast_rcv_buffer_ptrs = NULL; + failed_ptrs_alloc: + fw_iso_buffer_destroy ( &priv->broadcast_rcv_buffer, priv->card ); + failed_buffer_init: + fw_iso_context_destroy ( context ); + priv->broadcast_rcv_context = NULL; + failed_context_create: + fw_core_remove_address_handler ( &priv->handler ); + failed_initial: + priv->local_fifo = INVALID_FIFO_ADDR; + return retval; +} + +/* This is called after an "ifup" */ +static int ipv4_open(struct net_device *dev) { + struct ipv4_priv *priv; + int ret; + + priv = netdev_priv(dev); + if (priv->broadcast_state == IPV4_BROADCAST_ERROR) { + ret = ipv4_broadcast_start ( priv ); + if (ret) + return ret; + } + netif_start_queue(dev); + return 0; +} + +/* This is called after an "ifdown" */ +static int ipv4_stop(struct net_device *netdev) +{ + /* flush priv->wake */ + /* flush_scheduled_work(); */ + + netif_stop_queue(netdev); + return 0; +} + +/* Transmit a packet (called by kernel) */ +static int ipv4_tx(struct sk_buff *skb, struct net_device *netdev) +{ + struct ipv4_ether_hdr hdr_buf; + struct ipv4_priv *priv = netdev_priv(netdev); + __be16 proto; + u16 dest_node; + enum ipv4_tx_type tx_type; + unsigned max_payload; + u16 dg_size; + u16 *datagram_label_ptr; + struct ipv4_packet_task *ptask; + struct ipv4_node *node = NULL; + + ptask = kmem_cache_alloc(ipv4_packet_task_cache, GFP_ATOMIC); + if (ptask == NULL) + goto fail; + + skb = skb_share_check(skb, GFP_ATOMIC); + if (!skb) + goto fail; + + /* + * Get rid of the fake ipv4 header, but first make a copy. + * We might need to rebuild the header on tx failure. + */ + memcpy(&hdr_buf, skb->data, sizeof(hdr_buf)); + skb_pull(skb, sizeof(hdr_buf)); + + proto = hdr_buf.h_proto; + dg_size = skb->len; + + /* + * Set the transmission type for the packet. ARP packets and IP + * broadcast packets are sent via GASP. + */ + if ( memcmp(hdr_buf.h_dest, netdev->broadcast, IPV4_ALEN) == 0 + || proto == htons(ETH_P_ARP) + || ( proto == htons(ETH_P_IP) + && IN_MULTICAST(ntohl(ip_hdr(skb)->daddr)) ) ) { + /* fw_debug ( "transmitting arp or multicast packet\n" );*/ + tx_type = IPV4_GASP; + dest_node = ALL_NODES; + max_payload = priv->broadcast_xmt_max_payload; + /* BUG_ON(max_payload < S100_BUFFER_SIZE - IPV4_GASP_OVERHEAD); */ + datagram_label_ptr = &priv->broadcast_xmt_datagramlabel; + ptask->fifo_addr = INVALID_FIFO_ADDR; + ptask->generation = 0U; + ptask->dest_node = 0U; + ptask->speed = 0; + } else { + __be64 guid = get_unaligned((u64 *)hdr_buf.h_dest); + u8 generation; + + node = ipv4_node_find_by_guid(priv, be64_to_cpu(guid)); + if (!node) { + fw_debug ( "Normal packet but no node\n" ); + goto fail; + } + + if (node->fifo == INVALID_FIFO_ADDR) { + fw_debug ( "Normal packet but no fifo addr\n" ); + goto fail; + } + + /* fw_debug ( "Transmitting normal packet to %x at %llxx\n", node->nodeid, node->fifo ); */ + generation = node->generation; + dest_node = node->nodeid; + max_payload = node->max_payload; + /* BUG_ON(max_payload < S100_BUFFER_SIZE - IPV4_FRAG_HDR_SIZE); */ + + datagram_label_ptr = &node->datagram_label; + tx_type = IPV4_WRREQ; + ptask->fifo_addr = node->fifo; + ptask->generation = generation; + ptask->dest_node = dest_node; + ptask->speed = node->xmt_speed; + } + + /* If this is an ARP packet, convert it */ + if (proto == htons(ETH_P_ARP)) { + /* Convert a standard ARP packet to 1394 ARP. The first 8 bytes (the entire + * arphdr) is the same format as the ip1394 header, so they overlap. The rest + * needs to be munged a bit. The remainder of the arphdr is formatted based + * on hwaddr len and ipaddr len. We know what they'll be, so it's easy to + * judge. + * + * Now that the EUI is used for the hardware address all we need to do to make + * this work for 1394 is to insert 2 quadlets that contain max_rec size, + * speed, and unicast FIFO address information between the sender_unique_id + * and the IP addresses. + */ + struct arphdr *arp = (struct arphdr *)skb->data; + unsigned char *arp_ptr = (unsigned char *)(arp + 1); + struct ipv4_arp *arp1394 = (struct ipv4_arp *)skb->data; + u32 ipaddr; + + ipaddr = *(u32*)(arp_ptr + IPV4_ALEN); + arp1394->hw_addr_len = 16; + arp1394->max_rec = priv->card->max_receive; + arp1394->sspd = priv->card->link_speed; + arp1394->fifo_hi = htons(priv->local_fifo >> 32); + arp1394->fifo_lo = htonl(priv->local_fifo & 0xFFFFFFFF); + arp1394->sip = ipaddr; + } + if ( ipv4_max_xmt && max_payload > ipv4_max_xmt ) + max_payload = ipv4_max_xmt; + + ptask->hdr.w0 = 0; + ptask->hdr.w1 = 0; + ptask->skb = skb; + ptask->priv = priv; + ptask->tx_type = tx_type; + /* Does it all fit in one packet? */ + if ( dg_size <= max_payload ) { + ipv4_make_uf_hdr(&ptask->hdr, be16_to_cpu(proto)); + ptask->outstanding_pkts = 1; + max_payload = dg_size + IPV4_UNFRAG_HDR_SIZE; + } else { + u16 datagram_label; + + max_payload -= IPV4_FRAG_OVERHEAD; + datagram_label = (*datagram_label_ptr)++; + ipv4_make_ff_hdr(&ptask->hdr, be16_to_cpu(proto), dg_size, datagram_label ); + ptask->outstanding_pkts = DIV_ROUND_UP(dg_size, max_payload); + max_payload += IPV4_FRAG_HDR_SIZE; + } + ptask->max_payload = max_payload; + ipv4_send_packet ( ptask ); + return NETDEV_TX_OK; + + fail: + if (ptask) + kmem_cache_free(ipv4_packet_task_cache, ptask); + + if (skb != NULL) + dev_kfree_skb(skb); + + netdev->stats.tx_dropped++; + netdev->stats.tx_errors++; + + /* + * FIXME: According to a patch from 2003-02-26, "returning non-zero + * causes serious problems" here, allegedly. Before that patch, + * -ERRNO was returned which is not appropriate under Linux 2.6. + * Perhaps more needs to be done? Stop the queue in serious + * conditions and restart it elsewhere? + */ + return NETDEV_TX_OK; +} + +/* + * FIXME: What to do if we timeout? I think a host reset is probably in order, + * so that's what we do. Should we increment the stat counters too? + */ +static void ipv4_tx_timeout(struct net_device *dev) { + struct ipv4_priv *priv; + + priv = netdev_priv(dev); + fw_error ( "%s: Timeout, resetting host\n", dev->name ); +#if 0 /* stefanr */ + fw_core_initiate_bus_reset ( priv->card, 1 ); +#endif +} + +static int ipv4_change_mtu ( struct net_device *dev, int new_mtu ) { +#if 0 + int max_mtu; + struct ipv4_priv *priv; +#endif + + if (new_mtu < 68) + return -EINVAL; + +#if 0 + priv = netdev_priv(dev); + /* This is not actually true because we can fragment packets at the firewire layer */ + max_mtu = (1 << (priv->card->max_receive + 1)) + - sizeof(struct ipv4_hdr) - IPV4_GASP_OVERHEAD; + if (new_mtu > max_mtu) { + fw_notify ( "%s: Local node constrains MTU to %d\n", dev->name, max_mtu); + return -ERANGE; + } +#endif + dev->mtu = new_mtu; + return 0; +} + +static void ipv4_get_drvinfo(struct net_device *dev, +struct ethtool_drvinfo *info) { + strcpy(info->driver, ipv4_driver_name); + strcpy(info->bus_info, "ieee1394"); /* FIXME provide more detail? */ +} + +static struct ethtool_ops ipv4_ethtool_ops = { + .get_drvinfo = ipv4_get_drvinfo, +}; + +static const struct net_device_ops ipv4_netdev_ops = { + .ndo_open = ipv4_open, + .ndo_stop = ipv4_stop, + .ndo_start_xmit = ipv4_tx, + .ndo_tx_timeout = ipv4_tx_timeout, + .ndo_change_mtu = ipv4_change_mtu, +}; + +static void ipv4_init_dev ( struct net_device *dev ) { + dev->header_ops = &ipv4_header_ops; + dev->netdev_ops = &ipv4_netdev_ops; + SET_ETHTOOL_OPS(dev, &ipv4_ethtool_ops); + + dev->watchdog_timeo = IPV4_TIMEOUT; + dev->flags = IFF_BROADCAST | IFF_MULTICAST; + dev->features = NETIF_F_HIGHDMA; + dev->addr_len = IPV4_ALEN; + dev->hard_header_len = IPV4_HLEN; + dev->type = ARPHRD_IEEE1394; + + /* FIXME: This value was copied from ether_setup(). Is it too much? */ + dev->tx_queue_len = 1000; +} + +static int ipv4_probe ( struct device *dev ) { + struct fw_unit * unit; + struct fw_device *device; + struct fw_card *card; + struct net_device *netdev; + struct ipv4_priv *priv; + unsigned max_mtu; + __be64 guid; + + fw_debug("ipv4 Probing\n" ); + unit = fw_unit ( dev ); + device = fw_device ( unit->device.parent ); + card = device->card; + + if ( ! device->is_local ) { + int added; + + fw_debug ( "Non-local, adding remote node entry\n" ); + added = ipv4_node_new ( card, device ); + return added; + } + fw_debug("ipv4 Local: adding netdev\n" ); + netdev = alloc_netdev ( sizeof(*priv), "fw-ipv4-%d", ipv4_init_dev ); + if ( netdev == NULL) { + fw_error( "Out of memory\n"); + goto out; + } + + SET_NETDEV_DEV(netdev, card->device); + priv = netdev_priv(netdev); + + spin_lock_init(&priv->lock); + priv->broadcast_state = IPV4_BROADCAST_ERROR; + priv->broadcast_rcv_context = NULL; + priv->broadcast_xmt_max_payload = 0; + priv->broadcast_xmt_datagramlabel = 0; + + priv->local_fifo = INVALID_FIFO_ADDR; + + /* INIT_WORK(&priv->wake, ipv4_handle_queue);*/ + INIT_LIST_HEAD(&priv->packet_list); + INIT_LIST_HEAD(&priv->broadcasted_list); + INIT_LIST_HEAD(&priv->sent_list ); + + priv->card = card; + + /* + * Use the RFC 2734 default 1500 octets or the maximum payload + * as initial MTU + */ + max_mtu = (1 << (card->max_receive + 1)) + - sizeof(struct ipv4_hdr) - IPV4_GASP_OVERHEAD; + netdev->mtu = min(1500U, max_mtu); + + /* Set our hardware address while we're at it */ + guid = cpu_to_be64(card->guid); + memcpy(netdev->dev_addr, &guid, sizeof(u64)); + memset(netdev->broadcast, 0xff, sizeof(u64)); + if ( register_netdev ( netdev ) ) { + fw_error ( "Cannot register the driver\n"); + goto out; + } + + fw_notify ( "%s: IPv4 over Firewire on device %016llx\n", + netdev->name, card->guid ); + card->netdev = netdev; + + return 0 /* ipv4_new_node ( ud ) */; + out: + if ( netdev ) + free_netdev ( netdev ); + return -ENOENT; +} + + +static int ipv4_remove ( struct device *dev ) { + struct fw_unit * unit; + struct fw_device *device; + struct fw_card *card; + struct net_device *netdev; + struct ipv4_priv *priv; + struct ipv4_node *node; + struct ipv4_partial_datagram *pd, *pd_next; + struct ipv4_packet_task *ptask, *pt_next; + + fw_debug("ipv4 Removing\n" ); + unit = fw_unit ( dev ); + device = fw_device ( unit->device.parent ); + card = device->card; + + if ( ! device->is_local ) { + fw_debug ( "Node %x is non-local, removing remote node entry\n", device->node_id ); + ipv4_node_delete ( card, device ); + return 0; + } + netdev = card->netdev; + if ( netdev ) { + fw_debug ( "Node %x is local: deleting netdev\n", device->node_id ); + priv = netdev_priv ( netdev ); + unregister_netdev ( netdev ); + fw_debug ( "unregistered\n" ); + if ( priv->local_fifo != INVALID_FIFO_ADDR ) + fw_core_remove_address_handler ( &priv->handler ); + fw_debug ( "address handler gone\n" ); + if ( priv->broadcast_rcv_context ) { + fw_iso_context_stop ( priv->broadcast_rcv_context ); + fw_iso_buffer_destroy ( &priv->broadcast_rcv_buffer, priv->card ); + fw_iso_context_destroy ( priv->broadcast_rcv_context ); + fw_debug ( "rcv stopped\n" ); + } + list_for_each_entry_safe( ptask, pt_next, &priv->packet_list, packet_list ) { + dev_kfree_skb_any ( ptask->skb ); + kmem_cache_free( ipv4_packet_task_cache, ptask ); + } + list_for_each_entry_safe( ptask, pt_next, &priv->broadcasted_list, packet_list ) { + dev_kfree_skb_any ( ptask->skb ); + kmem_cache_free( ipv4_packet_task_cache, ptask ); + } + list_for_each_entry_safe( ptask, pt_next, &priv->sent_list, packet_list ) { + dev_kfree_skb_any ( ptask->skb ); + kmem_cache_free( ipv4_packet_task_cache, ptask ); + } + fw_debug ( "lists emptied\n" ); + list_for_each_entry( node, &card->ipv4_nodes, ipv4_nodes ) { + if ( node->pdg_size ) { + list_for_each_entry_safe( pd, pd_next, &node->pdg_list, pdg_list ) + ipv4_pd_delete ( pd ); + node->pdg_size = 0; + } + node->fifo = INVALID_FIFO_ADDR; + } + fw_debug ( "nodes cleaned up\n" ); + free_netdev ( netdev ); + card->netdev = NULL; + fw_debug ( "done\n" ); + } + return 0; +} + +static void ipv4_update ( struct fw_unit *unit ) { + struct fw_device *device; + struct fw_card *card; + + fw_debug ( "ipv4_update unit %p\n", unit ); + device = fw_device ( unit->device.parent ); + card = device->card; + if ( ! device->is_local ) { + struct ipv4_node *node; + u64 guid; + struct net_device *netdev; + struct ipv4_priv *priv; + + netdev = card->netdev; + if ( netdev ) { + priv = netdev_priv ( netdev ); + guid = (u64)device->config_rom[3] << 32 | device->config_rom[4]; + node = ipv4_node_find_by_guid ( priv, guid ); + if ( ! node ) { + fw_error ( "ipv4_update: no node for device %llx\n", guid ); + return; + } + fw_debug ( "Non-local, updating remote node entry for guid %llx old generation %x, old nodeid %x\n", guid, node->generation, node->nodeid ); + node->generation = device->generation; + rmb(); + node->nodeid = device->node_id; + fw_debug ( "New generation %x, new nodeid %x\n", node->generation, node->nodeid ); + } else + fw_error ( "nonlocal, but no netdev? How can that be?\n" ); + } else { + /* FIXME: What do we need to do on bus reset? */ + fw_debug ( "Local, doing nothing\n" ); + } +} + +static struct fw_driver ipv4_driver = { + .driver = { + .owner = THIS_MODULE, + .name = ipv4_driver_name, + .bus = &fw_bus_type, + .probe = ipv4_probe, + .remove = ipv4_remove, + }, + .update = ipv4_update, + .id_table = ipv4_id_table, +}; + +static int __init ipv4_init ( void ) { + int added; + + added = fw_core_add_descriptor ( &ipv4_unit_directory ); + if ( added < 0 ) + fw_error ( "Failed to add descriptor" ); + ipv4_packet_task_cache = kmem_cache_create("packet_task", + sizeof(struct ipv4_packet_task), 0, 0, NULL); + fw_debug("Adding ipv4 module\n" ); + return driver_register ( &ipv4_driver.driver ); +} + +static void __exit ipv4_cleanup ( void ) { + fw_core_remove_descriptor ( &ipv4_unit_directory ); + fw_debug("Removing ipv4 module\n" ); + driver_unregister ( &ipv4_driver.driver ); +} + +module_init(ipv4_init); +module_exit(ipv4_cleanup); diff --git a/include/linux/firewire.h b/include/linux/firewire.h index e584b7215e8..d44f47d3b2d 100644 --- a/include/linux/firewire.h +++ b/include/linux/firewire.h @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -130,6 +131,13 @@ struct fw_card { bool broadcast_channel_allocated; u32 broadcast_channel; u32 topology_map[(CSR_TOPOLOGY_MAP_END - CSR_TOPOLOGY_MAP) / 4]; + /* Only non-NULL if firewire-ipv4 is active on this card. */ + void *netdev; + /* + * The nodes get probed before the card, so we need a place to store + * them independent of card->netdev + */ + struct list_head ipv4_nodes; }; static inline struct fw_card *fw_card_get(struct fw_card *card) @@ -355,4 +363,90 @@ int fw_run_transaction(struct fw_card *card, int tcode, int destination_id, int generation, int speed, unsigned long long offset, void *payload, size_t length); +static inline int fw_stream_packet_destination_id(int tag, int channel, int sy) +{ + return tag << 14 | channel << 8 | sy; +} + +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); + +/* + * 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 + +/* + * 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; +}; + +int fw_iso_buffer_init(struct fw_iso_buffer *buffer, struct fw_card *card, + int page_count, enum dma_data_direction direction); +void fw_iso_buffer_destroy(struct fw_iso_buffer *buffer, struct fw_card *card); + +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); +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; +}; + +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); +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); +void fw_iso_context_destroy(struct fw_iso_context *ctx); + #endif /* _LINUX_FIREWIRE_H */ -- cgit v1.2.3-70-g09d2 From f91e3bd842ec6f5cea245993926ee8ff26250467 Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Sun, 7 Jun 2009 22:57:53 +0200 Subject: firewire: net: style changes Change names of types, variables, functions. Omit debug code. Use get_unaligned*, put_unaligned*. Annotate big endian data. Handle errors in __init. Change whitespace. Signed-off-by: Stefan Richter --- drivers/firewire/core-card.c | 2 +- drivers/firewire/net.c | 2041 ++++++++++++++++++++---------------------- include/linux/firewire.h | 9 +- 3 files changed, 969 insertions(+), 1083 deletions(-) (limited to 'drivers/firewire/core-card.c') diff --git a/drivers/firewire/core-card.c b/drivers/firewire/core-card.c index cdab32b2067..8c45e43da7c 100644 --- a/drivers/firewire/core-card.c +++ b/drivers/firewire/core-card.c @@ -430,7 +430,7 @@ void fw_card_initialize(struct fw_card *card, INIT_DELAYED_WORK(&card->work, fw_card_bm_work); card->netdev = NULL; - INIT_LIST_HEAD(&card->ipv4_nodes); + INIT_LIST_HEAD(&card->peer_list); } EXPORT_SYMBOL(fw_card_initialize); diff --git a/drivers/firewire/net.c b/drivers/firewire/net.c index 15353886bd8..ba6f924b1b1 100644 --- a/drivers/firewire/net.c +++ b/drivers/firewire/net.c @@ -6,6 +6,7 @@ * based on eth1394 by Ben Collins et al */ +#include #include #include #include @@ -13,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -22,181 +24,109 @@ #include #include -/* Things to potentially make runtime cofigurable */ -/* must be at least as large as our maximum receive size */ -#define FIFO_SIZE 4096 -/* Network timeout in glibbles */ -#define IPV4_TIMEOUT 100000 +#define FWNET_MAX_FRAGMENTS 25 /* arbitrary limit */ +#define FWNET_ISO_PAGE_COUNT (PAGE_SIZE < 16 * 1024 ? 4 : 2) -/* Runitme configurable paramaters */ -static int ipv4_mpd = 25; -static int ipv4_max_xmt = 0; -/* 16k for receiving arp and broadcast packets. Enough? */ -static int ipv4_iso_page_count = 4; +#define IEEE1394_BROADCAST_CHANNEL 31 +#define IEEE1394_ALL_NODES (0xffc0 | 0x003f) +#define IEEE1394_MAX_PAYLOAD_S100 512 +#define FWNET_NO_FIFO_ADDR (~0ULL) -MODULE_AUTHOR("Jay Fenlason (fenlason@redhat.com)"); -MODULE_DESCRIPTION("Firewire IPv4 Driver (IPv4-over-IEEE1394 as per RFC 2734)"); -MODULE_LICENSE("GPL"); -MODULE_DEVICE_TABLE(ieee1394, ipv4_id_table); -module_param_named(max_partial_datagrams, ipv4_mpd, int, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(max_partial_datagrams, "Maximum number of received" - " incomplete fragmented datagrams (default = 25)."); - -/* Max xmt is useful for forcing fragmentation, which makes testing easier. */ -module_param_named(max_transmit, ipv4_max_xmt, int, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(max_transmit, "Maximum datagram size to transmit" - " (larger datagrams will be fragmented) (default = 0 (use hardware defaults)."); - -/* iso page count controls how many pages will be used for receiving broadcast packets. */ -module_param_named(iso_pages, ipv4_iso_page_count, int, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(iso_pages, "Number of pages to use for receiving broadcast packets" - " (default = 4)."); - -/* uncomment this line to do debugging */ -#define fw_debug(s, args...) printk(KERN_DEBUG KBUILD_MODNAME ": " s, ## args) - -/* comment out these lines to do debugging. */ -/* #undef fw_debug */ -/* #define fw_debug(s...) */ -/* #define print_hex_dump(l...) */ - -/* Define a fake hardware header format for the networking core. Note that - * header size cannot exceed 16 bytes as that is the size of the header cache. - * Also, we do not need the source address in the header so we omit it and - * keep the header to under 16 bytes */ -#define IPV4_ALEN (8) -/* This must equal sizeof(struct ipv4_ether_hdr) */ -#define IPV4_HLEN (10) - -/* FIXME: what's a good size for this? */ -#define INVALID_FIFO_ADDR (u64)~0ULL - -/* Things specified by standards */ -#define BROADCAST_CHANNEL 31 - -#define S100_BUFFER_SIZE 512 -#define MAX_BUFFER_SIZE 4096 - -#define IPV4_GASP_SPECIFIER_ID 0x00005EU -#define IPV4_GASP_VERSION 0x00000001U - -#define IPV4_GASP_OVERHEAD (2 * sizeof(u32)) /* for GASP header */ - -#define IPV4_UNFRAG_HDR_SIZE sizeof(u32) -#define IPV4_FRAG_HDR_SIZE (2 * sizeof(u32)) -#define IPV4_FRAG_OVERHEAD sizeof(u32) - -#define ALL_NODES (0xffc0 | 0x003f) - -#define IPV4_HDR_UNFRAG 0 /* unfragmented */ -#define IPV4_HDR_FIRSTFRAG 1 /* first fragment */ -#define IPV4_HDR_LASTFRAG 2 /* last fragment */ -#define IPV4_HDR_INTFRAG 3 /* interior fragment */ - -/* Our arp packet (ARPHRD_IEEE1394) */ -/* FIXME: note that this is probably bogus on weird-endian machines */ -struct ipv4_arp { - u16 hw_type; /* 0x0018 */ - u16 proto_type; /* 0x0806 */ - u8 hw_addr_len; /* 16 */ - u8 ip_addr_len; /* 4 */ - u16 opcode; /* ARP Opcode */ - /* Above is exactly the same format as struct arphdr */ - - u64 s_uniq_id; /* Sender's 64bit EUI */ - u8 max_rec; /* Sender's max packet size */ - u8 sspd; /* Sender's max speed */ - u16 fifo_hi; /* hi 16bits of sender's FIFO addr */ - u32 fifo_lo; /* lo 32bits of sender's FIFO addr */ - u32 sip; /* Sender's IP Address */ - u32 tip; /* IP Address of requested hw addr */ -} __attribute__((packed)); +#define IANA_SPECIFIER_ID 0x00005eU +#define RFC2734_SW_VERSION 0x000001U -struct ipv4_ether_hdr { - unsigned char h_dest[IPV4_ALEN]; /* destination address */ - unsigned short h_proto; /* packet type ID field */ -} __attribute__((packed)); +#define IEEE1394_GASP_HDR_SIZE 8 -static inline struct ipv4_ether_hdr *ipv4_ether_hdr(const struct sk_buff *skb) -{ - return (struct ipv4_ether_hdr *)skb_mac_header(skb); -} +#define RFC2374_UNFRAG_HDR_SIZE 4 +#define RFC2374_FRAG_HDR_SIZE 8 +#define RFC2374_FRAG_OVERHEAD 4 -enum ipv4_tx_type { - IPV4_UNKNOWN = 0, - IPV4_GASP = 1, - IPV4_WRREQ = 2, -}; +#define RFC2374_HDR_UNFRAG 0 /* unfragmented */ +#define RFC2374_HDR_FIRSTFRAG 1 /* first fragment */ +#define RFC2374_HDR_LASTFRAG 2 /* last fragment */ +#define RFC2374_HDR_INTFRAG 3 /* interior fragment */ -enum ipv4_broadcast_state { - IPV4_BROADCAST_ERROR, - IPV4_BROADCAST_RUNNING, - IPV4_BROADCAST_STOPPED, -}; +#define RFC2734_HW_ADDR_LEN 16 -#define ipv4_get_hdr_lf(h) (((h)->w0&0xC0000000)>>30) -#define ipv4_get_hdr_ether_type(h) (((h)->w0&0x0000FFFF) ) -#define ipv4_get_hdr_dg_size(h) (((h)->w0&0x0FFF0000)>>16) -#define ipv4_get_hdr_fg_off(h) (((h)->w0&0x00000FFF) ) -#define ipv4_get_hdr_dgl(h) (((h)->w1&0xFFFF0000)>>16) +struct rfc2734_arp { + __be16 hw_type; /* 0x0018 */ + __be16 proto_type; /* 0x0806 */ + u8 hw_addr_len; /* 16 */ + u8 ip_addr_len; /* 4 */ + __be16 opcode; /* ARP Opcode */ + /* Above is exactly the same format as struct arphdr */ -#define ipv4_set_hdr_lf(lf) (( lf)<<30) -#define ipv4_set_hdr_ether_type(et) (( et) ) -#define ipv4_set_hdr_dg_size(dgs) ((dgs)<<16) -#define ipv4_set_hdr_fg_off(fgo) ((fgo) ) + __be64 s_uniq_id; /* Sender's 64bit EUI */ + u8 max_rec; /* Sender's max packet size */ + u8 sspd; /* Sender's max speed */ + __be16 fifo_hi; /* hi 16bits of sender's FIFO addr */ + __be32 fifo_lo; /* lo 32bits of sender's FIFO addr */ + __be32 sip; /* Sender's IP Address */ + __be32 tip; /* IP Address of requested hw addr */ +} __attribute__((packed)); -#define ipv4_set_hdr_dgl(dgl) ((dgl)<<16) +/* This header format is specific to this driver implementation. */ +#define FWNET_ALEN 8 +#define FWNET_HLEN 10 +struct fwnet_header { + u8 h_dest[FWNET_ALEN]; /* destination address */ + __be16 h_proto; /* packet type ID field */ +} __attribute__((packed)); -struct ipv4_hdr { +/* IPv4 and IPv6 encapsulation header */ +struct rfc2734_header { u32 w0; u32 w1; }; -static inline void ipv4_make_uf_hdr( struct ipv4_hdr *hdr, unsigned ether_type) { - hdr->w0 = ipv4_set_hdr_lf(IPV4_HDR_UNFRAG) - |ipv4_set_hdr_ether_type(ether_type); - fw_debug ( "Setting unfragmented header %p to %x\n", hdr, hdr->w0 ); -} +#define fwnet_get_hdr_lf(h) (((h)->w0 & 0xc0000000) >> 30) +#define fwnet_get_hdr_ether_type(h) (((h)->w0 & 0x0000ffff)) +#define fwnet_get_hdr_dg_size(h) (((h)->w0 & 0x0fff0000) >> 16) +#define fwnet_get_hdr_fg_off(h) (((h)->w0 & 0x00000fff)) +#define fwnet_get_hdr_dgl(h) (((h)->w1 & 0xffff0000) >> 16) -static inline void ipv4_make_ff_hdr ( struct ipv4_hdr *hdr, unsigned ether_type, unsigned dg_size, unsigned dgl ) { - hdr->w0 = ipv4_set_hdr_lf(IPV4_HDR_FIRSTFRAG) - |ipv4_set_hdr_dg_size(dg_size) - |ipv4_set_hdr_ether_type(ether_type); - hdr->w1 = ipv4_set_hdr_dgl(dgl); - fw_debug ( "Setting fragmented header %p to first_frag %x,%x (et %x, dgs %x, dgl %x)\n", hdr, hdr->w0, hdr->w1, - ether_type, dg_size, dgl ); -} +#define fwnet_set_hdr_lf(lf) ((lf) << 30) +#define fwnet_set_hdr_ether_type(et) (et) +#define fwnet_set_hdr_dg_size(dgs) ((dgs) << 16) +#define fwnet_set_hdr_fg_off(fgo) (fgo) -static inline void ipv4_make_sf_hdr ( struct ipv4_hdr *hdr, unsigned lf, unsigned dg_size, unsigned fg_off, unsigned dgl) { - hdr->w0 = ipv4_set_hdr_lf(lf) - |ipv4_set_hdr_dg_size(dg_size) - |ipv4_set_hdr_fg_off(fg_off); - hdr->w1 = ipv4_set_hdr_dgl(dgl); - fw_debug ( "Setting fragmented header %p to %x,%x (lf %x, dgs %x, fo %x dgl %x)\n", - hdr, hdr->w0, hdr->w1, - lf, dg_size, fg_off, dgl ); -} +#define fwnet_set_hdr_dgl(dgl) ((dgl) << 16) -/* End of IP1394 headers */ +static inline void fwnet_make_uf_hdr(struct rfc2734_header *hdr, + unsigned ether_type) +{ + hdr->w0 = fwnet_set_hdr_lf(RFC2374_HDR_UNFRAG) + | fwnet_set_hdr_ether_type(ether_type); +} -/* Fragment types */ -#define ETH1394_HDR_LF_UF 0 /* unfragmented */ -#define ETH1394_HDR_LF_FF 1 /* first fragment */ -#define ETH1394_HDR_LF_LF 2 /* last fragment */ -#define ETH1394_HDR_LF_IF 3 /* interior fragment */ +static inline void fwnet_make_ff_hdr(struct rfc2734_header *hdr, + unsigned ether_type, unsigned dg_size, unsigned dgl) +{ + hdr->w0 = fwnet_set_hdr_lf(RFC2374_HDR_FIRSTFRAG) + | fwnet_set_hdr_dg_size(dg_size) + | fwnet_set_hdr_ether_type(ether_type); + hdr->w1 = fwnet_set_hdr_dgl(dgl); +} -#define IP1394_HW_ADDR_LEN 16 /* As per RFC */ +static inline void fwnet_make_sf_hdr(struct rfc2734_header *hdr, + unsigned lf, unsigned dg_size, unsigned fg_off, unsigned dgl) +{ + hdr->w0 = fwnet_set_hdr_lf(lf) + | fwnet_set_hdr_dg_size(dg_size) + | fwnet_set_hdr_fg_off(fg_off); + hdr->w1 = fwnet_set_hdr_dgl(dgl); +} /* This list keeps track of what parts of the datagram have been filled in */ -struct ipv4_fragment_info { - struct list_head fragment_info; +struct fwnet_fragment_info { + struct list_head fi_link; u16 offset; u16 len; }; -struct ipv4_partial_datagram { - struct list_head pdg_list; - struct list_head fragment_info; +struct fwnet_partial_datagram { + struct list_head pd_link; + struct list_head fi_list; struct sk_buff *skb; /* FIXME Why not use skb->data? */ char *pbuf; @@ -208,40 +138,43 @@ struct ipv4_partial_datagram { /* * We keep one of these for each IPv4 capable device attached to a fw_card. * The list of them is stored in the fw_card structure rather than in the - * ipv4_priv because the remote IPv4 nodes may be probed before the card is, - * so we need a place to store them before the ipv4_priv structure is + * fwnet_device because the remote IPv4 nodes may be probed before the card is, + * so we need a place to store them before the fwnet_device structure is * allocated. */ -struct ipv4_node { - struct list_head ipv4_nodes; - /* guid of the remote node */ +struct fwnet_peer { + struct list_head peer_link; + /* guid of the remote peer */ u64 guid; - /* FIFO address to transmit datagrams to, or INVALID_FIFO_ADDR */ + /* FIFO address to transmit datagrams to, or FWNET_NO_FIFO_ADDR */ u64 fifo; spinlock_t pdg_lock; /* partial datagram lock */ - /* List of partial datagrams received from this node */ - struct list_head pdg_list; - /* Number of entries in pdg_list at the moment */ + /* List of partial datagrams received from this peer */ + struct list_head pd_list; + /* Number of entries in pd_list at the moment */ unsigned pdg_size; - /* max payload to transmit to this remote node */ - /* This already includes the IPV4_FRAG_HDR_SIZE overhead */ + /* max payload to transmit to this remote peer */ + /* This already includes the RFC2374_FRAG_HDR_SIZE overhead */ u16 max_payload; /* outgoing datagram label */ u16 datagram_label; - /* Current node_id of the remote node */ - u16 nodeid; - /* current generation of the remote node */ + /* Current node_id of the remote peer */ + u16 node_id; + /* current generation of the remote peer */ u8 generation; - /* max speed that this node can receive at */ + /* max speed that this peer can receive at */ u8 xmt_speed; }; -struct ipv4_priv { +struct fwnet_device { spinlock_t lock; - - enum ipv4_broadcast_state broadcast_state; + enum { + FWNET_BROADCAST_ERROR, + FWNET_BROADCAST_RUNNING, + FWNET_BROADCAST_STOPPED, + } broadcast_state; struct fw_iso_context *broadcast_rcv_context; struct fw_iso_buffer broadcast_rcv_buffer; void **broadcast_rcv_buffer_ptrs; @@ -257,14 +190,12 @@ struct ipv4_priv { u16 broadcast_xmt_datagramlabel; /* - * The csr address that remote nodes must send datagrams to for us to + * The CSR address that remote nodes must send datagrams to for us to * receive them. */ struct fw_address_handler handler; u64 local_fifo; - /* Wake up to xmt */ - /* struct work_struct wake;*/ /* List of packets to be sent */ struct list_head packet_list; /* @@ -279,17 +210,17 @@ struct ipv4_priv { }; /* This is our task struct. It's used for the packet complete callback. */ -struct ipv4_packet_task { +struct fwnet_packet_task { /* - * ptask can actually be on priv->packet_list, priv->broadcasted_list, - * or priv->sent_list depending on its current state. + * ptask can actually be on dev->packet_list, dev->broadcasted_list, + * or dev->sent_list depending on its current state. */ - struct list_head packet_list; + struct list_head pt_link; struct fw_transaction transaction; - struct ipv4_hdr hdr; + struct rfc2734_header hdr; struct sk_buff *skb; - struct ipv4_priv *priv; - enum ipv4_tx_type tx_type; + struct fwnet_device *dev; + int outstanding_pkts; unsigned max_payload; u64 fifo_addr; @@ -298,243 +229,192 @@ struct ipv4_packet_task { u8 speed; }; -static struct kmem_cache *ipv4_packet_task_cache; - -static const char ipv4_driver_name[] = "firewire-ipv4"; - -static const struct ieee1394_device_id ipv4_id_table[] = { - { - .match_flags = IEEE1394_MATCH_SPECIFIER_ID | - IEEE1394_MATCH_VERSION, - .specifier_id = IPV4_GASP_SPECIFIER_ID, - .version = IPV4_GASP_VERSION, - }, - { } -}; - -static u32 ipv4_unit_directory_data[] = { - 0x00040000, /* unit directory */ - 0x12000000 | IPV4_GASP_SPECIFIER_ID, /* specifier ID */ - 0x81000003, /* text descriptor */ - 0x13000000 | IPV4_GASP_VERSION, /* version */ - 0x81000005, /* text descriptor */ - - 0x00030000, /* Three quadlets */ - 0x00000000, /* Text */ - 0x00000000, /* Language 0 */ - 0x49414e41, /* I A N A */ - 0x00030000, /* Three quadlets */ - 0x00000000, /* Text */ - 0x00000000, /* Language 0 */ - 0x49507634, /* I P v 4 */ -}; - -static struct fw_descriptor ipv4_unit_directory = { - .length = ARRAY_SIZE(ipv4_unit_directory_data), - .key = 0xd1000000, - .data = ipv4_unit_directory_data -}; - -static int ipv4_send_packet(struct ipv4_packet_task *ptask ); - -/* ------------------------------------------------------------------ */ -/****************************************** - * HW Header net device functions - ******************************************/ - /* These functions have been adapted from net/ethernet/eth.c */ - -/* Create a fake MAC header for an arbitrary protocol layer. - * saddr=NULL means use device source address - * daddr=NULL means leave destination address (eg unresolved arp). */ +/* + * saddr == NULL means use device source address. + * daddr == NULL means leave destination address (eg unresolved arp). + */ +static int fwnet_header_create(struct sk_buff *skb, struct net_device *net, + unsigned short type, const void *daddr, + const void *saddr, unsigned len) +{ + struct fwnet_header *h; -static int ipv4_header ( struct sk_buff *skb, struct net_device *dev, - unsigned short type, const void *daddr, - const void *saddr, unsigned len) { - struct ipv4_ether_hdr *eth; + h = (struct fwnet_header *)skb_push(skb, sizeof(*h)); + put_unaligned_be16(type, &h->h_proto); - eth = (struct ipv4_ether_hdr *)skb_push(skb, sizeof(*eth)); - eth->h_proto = htons(type); + if (net->flags & (IFF_LOOPBACK | IFF_NOARP)) { + memset(h->h_dest, 0, net->addr_len); - if (dev->flags & (IFF_LOOPBACK | IFF_NOARP)) { - memset(eth->h_dest, 0, dev->addr_len); - return dev->hard_header_len; + return net->hard_header_len; } if (daddr) { - memcpy(eth->h_dest, daddr, dev->addr_len); - return dev->hard_header_len; + memcpy(h->h_dest, daddr, net->addr_len); + + return net->hard_header_len; } - return -dev->hard_header_len; + return -net->hard_header_len; } -/* Rebuild the faked MAC header. This is called after an ARP - * (or in future other address resolution) has completed on this - * sk_buff. We now let ARP fill in the other fields. - * - * This routine CANNOT use cached dst->neigh! - * Really, it is used only when dst->neigh is wrong. - */ - -static int ipv4_rebuild_header(struct sk_buff *skb) +static int fwnet_header_rebuild(struct sk_buff *skb) { - struct ipv4_ether_hdr *eth; + struct fwnet_header *h = (struct fwnet_header *)skb->data; - eth = (struct ipv4_ether_hdr *)skb->data; - if (eth->h_proto == htons(ETH_P_IP)) - return arp_find((unsigned char *)ð->h_dest, skb); + if (get_unaligned_be16(&h->h_proto) == ETH_P_IP) + return arp_find((unsigned char *)&h->h_dest, skb); - fw_notify ( "%s: unable to resolve type %04x addresses\n", - skb->dev->name,ntohs(eth->h_proto) ); + fw_notify("%s: unable to resolve type %04x addresses\n", + skb->dev->name, be16_to_cpu(h->h_proto)); return 0; } -static int ipv4_header_cache(const struct neighbour *neigh, struct hh_cache *hh) { - unsigned short type = hh->hh_type; - struct net_device *dev; - struct ipv4_ether_hdr *eth; +static int fwnet_header_cache(const struct neighbour *neigh, + struct hh_cache *hh) +{ + struct net_device *net; + struct fwnet_header *h; - if (type == htons(ETH_P_802_3)) + if (hh->hh_type == cpu_to_be16(ETH_P_802_3)) return -1; - dev = neigh->dev; - eth = (struct ipv4_ether_hdr *)((u8 *)hh->hh_data + 16 - sizeof(*eth)); - eth->h_proto = type; - memcpy(eth->h_dest, neigh->ha, dev->addr_len); + net = neigh->dev; + h = (struct fwnet_header *)((u8 *)hh->hh_data + 16 - sizeof(*h)); + h->h_proto = hh->hh_type; + memcpy(h->h_dest, neigh->ha, net->addr_len); + hh->hh_len = FWNET_HLEN; - hh->hh_len = IPV4_HLEN; return 0; } /* Called by Address Resolution module to notify changes in address. */ -static void ipv4_header_cache_update(struct hh_cache *hh, const struct net_device *dev, const unsigned char * haddr ) { - memcpy((u8 *)hh->hh_data + 16 - IPV4_HLEN, haddr, dev->addr_len); +static void fwnet_header_cache_update(struct hh_cache *hh, + const struct net_device *net, const unsigned char *haddr) +{ + memcpy((u8 *)hh->hh_data + 16 - FWNET_HLEN, haddr, net->addr_len); } -static int ipv4_header_parse(const struct sk_buff *skb, unsigned char *haddr) { - memcpy(haddr, skb->dev->dev_addr, IPV4_ALEN); - return IPV4_ALEN; +static int fwnet_header_parse(const struct sk_buff *skb, unsigned char *haddr) +{ + memcpy(haddr, skb->dev->dev_addr, FWNET_ALEN); + + return FWNET_ALEN; } -static const struct header_ops ipv4_header_ops = { - .create = ipv4_header, - .rebuild = ipv4_rebuild_header, - .cache = ipv4_header_cache, - .cache_update = ipv4_header_cache_update, - .parse = ipv4_header_parse, +static const struct header_ops fwnet_header_ops = { + .create = fwnet_header_create, + .rebuild = fwnet_header_rebuild, + .cache = fwnet_header_cache, + .cache_update = fwnet_header_cache_update, + .parse = fwnet_header_parse, }; -/* ------------------------------------------------------------------ */ - /* FIXME: is this correct for all cases? */ -static bool ipv4_frag_overlap(struct ipv4_partial_datagram *pd, unsigned offset, unsigned len) +static bool fwnet_frag_overlap(struct fwnet_partial_datagram *pd, + unsigned offset, unsigned len) { - struct ipv4_fragment_info *fi; + struct fwnet_fragment_info *fi; unsigned end = offset + len; - list_for_each_entry(fi, &pd->fragment_info, fragment_info) { - if (offset < fi->offset + fi->len && end > fi->offset) { - fw_debug ( "frag_overlap pd %p fi %p (%x@%x) with %x@%x\n", pd, fi, fi->len, fi->offset, len, offset ); + list_for_each_entry(fi, &pd->fi_list, fi_link) + if (offset < fi->offset + fi->len && end > fi->offset) return true; - } - } - fw_debug ( "frag_overlap %p does not overlap with %x@%x\n", pd, len, offset ); + return false; } /* Assumes that new fragment does not overlap any existing fragments */ -static struct ipv4_fragment_info *ipv4_frag_new ( struct ipv4_partial_datagram *pd, unsigned offset, unsigned len ) { - struct ipv4_fragment_info *fi, *fi2, *new; +static struct fwnet_fragment_info *fwnet_frag_new( + struct fwnet_partial_datagram *pd, unsigned offset, unsigned len) +{ + struct fwnet_fragment_info *fi, *fi2, *new; struct list_head *list; - fw_debug ( "frag_new pd %p %x@%x\n", pd, len, offset ); - list = &pd->fragment_info; - list_for_each_entry(fi, &pd->fragment_info, fragment_info) { + list = &pd->fi_list; + list_for_each_entry(fi, &pd->fi_list, fi_link) { if (fi->offset + fi->len == offset) { /* The new fragment can be tacked on to the end */ /* Did the new fragment plug a hole? */ - fi2 = list_entry(fi->fragment_info.next, struct ipv4_fragment_info, fragment_info); + fi2 = list_entry(fi->fi_link.next, + struct fwnet_fragment_info, fi_link); if (fi->offset + fi->len == fi2->offset) { - fw_debug ( "pd %p: hole filling %p (%x@%x) and %p(%x@%x): now %x@%x\n", pd, fi, fi->len, fi->offset, - fi2, fi2->len, fi2->offset, fi->len + len + fi2->len, fi->offset ); /* glue fragments together */ fi->len += len + fi2->len; - list_del(&fi2->fragment_info); + list_del(&fi2->fi_link); kfree(fi2); } else { - fw_debug ( "pd %p: extending %p from %x@%x to %x@%x\n", pd, fi, fi->len, fi->offset, fi->len+len, fi->offset ); fi->len += len; } + return fi; } if (offset + len == fi->offset) { /* The new fragment can be tacked on to the beginning */ /* Did the new fragment plug a hole? */ - fi2 = list_entry(fi->fragment_info.prev, struct ipv4_fragment_info, fragment_info); + fi2 = list_entry(fi->fi_link.prev, + struct fwnet_fragment_info, fi_link); if (fi2->offset + fi2->len == fi->offset) { /* glue fragments together */ - fw_debug ( "pd %p: extending %p and merging with %p from %x@%x to %x@%x\n", - pd, fi2, fi, fi2->len, fi2->offset, fi2->len + fi->len + len, fi2->offset ); fi2->len += fi->len + len; - list_del(&fi->fragment_info); + list_del(&fi->fi_link); kfree(fi); + return fi2; } - fw_debug ( "pd %p: extending %p from %x@%x to %x@%x\n", pd, fi, fi->len, fi->offset, offset, fi->len + len ); fi->offset = offset; fi->len += len; + return fi; } if (offset > fi->offset + fi->len) { - list = &fi->fragment_info; + list = &fi->fi_link; break; } if (offset + len < fi->offset) { - list = fi->fragment_info.prev; + list = fi->fi_link.prev; break; } } new = kmalloc(sizeof(*new), GFP_ATOMIC); if (!new) { - fw_error ( "out of memory in fragment handling!\n" ); + fw_error("out of memory\n"); return NULL; } new->offset = offset; new->len = len; - list_add(&new->fragment_info, list); - fw_debug ( "pd %p: new frag %p %x@%x\n", pd, new, new->len, new->offset ); - list_for_each_entry( fi, &pd->fragment_info, fragment_info ) - fw_debug ( "fi %p %x@%x\n", fi, fi->len, fi->offset ); + list_add(&new->fi_link, list); + return new; } -/* ------------------------------------------------------------------ */ - -static struct ipv4_partial_datagram *ipv4_pd_new(struct net_device *netdev, - struct ipv4_node *node, u16 datagram_label, unsigned dg_size, u32 *frag_buf, - unsigned frag_off, unsigned frag_len) { - struct ipv4_partial_datagram *new; - struct ipv4_fragment_info *fi; +static struct fwnet_partial_datagram *fwnet_pd_new(struct net_device *net, + struct fwnet_peer *peer, u16 datagram_label, unsigned dg_size, + void *frag_buf, unsigned frag_off, unsigned frag_len) +{ + struct fwnet_partial_datagram *new; + struct fwnet_fragment_info *fi; new = kmalloc(sizeof(*new), GFP_ATOMIC); if (!new) goto fail; - INIT_LIST_HEAD(&new->fragment_info); - fi = ipv4_frag_new ( new, frag_off, frag_len); - if ( fi == NULL ) + + INIT_LIST_HEAD(&new->fi_list); + fi = fwnet_frag_new(new, frag_off, frag_len); + if (fi == NULL) goto fail_w_new; + new->datagram_label = datagram_label; new->datagram_size = dg_size; - new->skb = dev_alloc_skb(dg_size + netdev->hard_header_len + 15); - if ( new->skb == NULL ) + new->skb = dev_alloc_skb(dg_size + net->hard_header_len + 15); + if (new->skb == NULL) goto fail_w_fi; - skb_reserve(new->skb, (netdev->hard_header_len + 15) & ~15); + + skb_reserve(new->skb, (net->hard_header_len + 15) & ~15); new->pbuf = skb_put(new->skb, dg_size); memcpy(new->pbuf + frag_off, frag_buf, frag_len); - list_add_tail(&new->pdg_list, &node->pdg_list); - fw_debug ( "pd_new: new pd %p { dgl %u, dg_size %u, skb %p, pbuf %p } on node %p\n", - new, new->datagram_label, new->datagram_size, new->skb, new->pbuf, node ); + list_add_tail(&new->pd_link, &peer->pd_list); + return new; fail_w_fi: @@ -542,174 +422,171 @@ fail_w_fi: fail_w_new: kfree(new); fail: - fw_error("ipv4_pd_new: no memory\n"); + fw_error("out of memory\n"); + return NULL; } -static struct ipv4_partial_datagram *ipv4_pd_find(struct ipv4_node *node, u16 datagram_label) { - struct ipv4_partial_datagram *pd; +static struct fwnet_partial_datagram *fwnet_pd_find(struct fwnet_peer *peer, + u16 datagram_label) +{ + struct fwnet_partial_datagram *pd; - list_for_each_entry(pd, &node->pdg_list, pdg_list) { - if ( pd->datagram_label == datagram_label ) { - fw_debug ( "pd_find(node %p, label %u): pd %p\n", node, datagram_label, pd ); + list_for_each_entry(pd, &peer->pd_list, pd_link) + if (pd->datagram_label == datagram_label) return pd; - } - } - fw_debug ( "pd_find(node %p, label %u) no entry\n", node, datagram_label ); + return NULL; } -static void ipv4_pd_delete ( struct ipv4_partial_datagram *old ) { - struct ipv4_fragment_info *fi, *n; +static void fwnet_pd_delete(struct fwnet_partial_datagram *old) +{ + struct fwnet_fragment_info *fi, *n; - fw_debug ( "pd_delete %p\n", old ); - list_for_each_entry_safe(fi, n, &old->fragment_info, fragment_info) { - fw_debug ( "Freeing fi %p\n", fi ); + list_for_each_entry_safe(fi, n, &old->fi_list, fi_link) kfree(fi); - } - list_del(&old->pdg_list); + + list_del(&old->pd_link); dev_kfree_skb_any(old->skb); kfree(old); } -static bool ipv4_pd_update ( struct ipv4_node *node, struct ipv4_partial_datagram *pd, - u32 *frag_buf, unsigned frag_off, unsigned frag_len) { - fw_debug ( "pd_update node %p, pd %p, frag_buf %p, %x@%x\n", node, pd, frag_buf, frag_len, frag_off ); - if ( ipv4_frag_new ( pd, frag_off, frag_len ) == NULL) +static bool fwnet_pd_update(struct fwnet_peer *peer, + struct fwnet_partial_datagram *pd, void *frag_buf, + unsigned frag_off, unsigned frag_len) +{ + if (fwnet_frag_new(pd, frag_off, frag_len) == NULL) return false; + memcpy(pd->pbuf + frag_off, frag_buf, frag_len); /* * Move list entry to beginnig of list so that oldest partial * datagrams percolate to the end of the list */ - list_move_tail(&pd->pdg_list, &node->pdg_list); - fw_debug ( "New pd list:\n" ); - list_for_each_entry ( pd, &node->pdg_list, pdg_list ) { - fw_debug ( "pd %p\n", pd ); - } + list_move_tail(&pd->pd_link, &peer->pd_list); + return true; } -static bool ipv4_pd_is_complete ( struct ipv4_partial_datagram *pd ) { - struct ipv4_fragment_info *fi; - bool ret; +static bool fwnet_pd_is_complete(struct fwnet_partial_datagram *pd) +{ + struct fwnet_fragment_info *fi; - fi = list_entry(pd->fragment_info.next, struct ipv4_fragment_info, fragment_info); + fi = list_entry(pd->fi_list.next, struct fwnet_fragment_info, fi_link); - ret = (fi->len == pd->datagram_size); - fw_debug ( "pd_is_complete (pd %p, dgs %x): fi %p (%x@%x) %s\n", pd, pd->datagram_size, fi, fi->len, fi->offset, ret ? "yes" : "no" ); - return ret; + return fi->len == pd->datagram_size; } -/* ------------------------------------------------------------------ */ +static int fwnet_peer_new(struct fw_card *card, struct fw_device *device) +{ + struct fwnet_peer *peer; -static int ipv4_node_new ( struct fw_card *card, struct fw_device *device ) { - struct ipv4_node *node; + peer = kmalloc(sizeof(*peer), GFP_KERNEL); + if (!peer) { + fw_error("out of memory\n"); - node = kmalloc ( sizeof(*node), GFP_KERNEL ); - if ( ! node ) { - fw_error ( "allocate new node failed\n" ); return -ENOMEM; } - node->guid = (u64)device->config_rom[3] << 32 | device->config_rom[4]; - node->fifo = INVALID_FIFO_ADDR; - INIT_LIST_HEAD(&node->pdg_list); - spin_lock_init(&node->pdg_lock); - node->pdg_size = 0; - node->generation = device->generation; + peer->guid = (u64)device->config_rom[3] << 32 | device->config_rom[4]; + peer->fifo = FWNET_NO_FIFO_ADDR; + INIT_LIST_HEAD(&peer->pd_list); + spin_lock_init(&peer->pdg_lock); + peer->pdg_size = 0; + peer->generation = device->generation; rmb(); - node->nodeid = device->node_id; + peer->node_id = device->node_id; /* FIXME what should it really be? */ - node->max_payload = S100_BUFFER_SIZE - IPV4_UNFRAG_HDR_SIZE; - node->datagram_label = 0U; - node->xmt_speed = device->max_speed; - list_add_tail ( &node->ipv4_nodes, &card->ipv4_nodes ); - fw_debug ( "node_new: %p { guid %016llx, generation %u, nodeid %x, max_payload %x, xmt_speed %x } added\n", - node, (unsigned long long)node->guid, node->generation, node->nodeid, node->max_payload, node->xmt_speed ); + peer->max_payload = IEEE1394_MAX_PAYLOAD_S100 - RFC2374_UNFRAG_HDR_SIZE; + peer->datagram_label = 0U; + peer->xmt_speed = device->max_speed; + list_add_tail(&peer->peer_link, &card->peer_list); + return 0; } -static struct ipv4_node *ipv4_node_find_by_guid(struct ipv4_priv *priv, u64 guid) { - struct ipv4_node *node; +/* FIXME caller must take the lock, or peer needs to be reference-counted */ +static struct fwnet_peer *fwnet_peer_find_by_guid(struct fwnet_device *dev, + u64 guid) +{ + struct fwnet_peer *p, *peer = NULL; unsigned long flags; - spin_lock_irqsave(&priv->lock, flags); - list_for_each_entry(node, &priv->card->ipv4_nodes, ipv4_nodes) - if (node->guid == guid) { - /* FIXME: lock the node first? */ - spin_unlock_irqrestore ( &priv->lock, flags ); - fw_debug ( "node_find_by_guid (%016llx) found %p\n", (unsigned long long)guid, node ); - return node; + spin_lock_irqsave(&dev->lock, flags); + list_for_each_entry(p, &dev->card->peer_list, peer_link) + if (p->guid == guid) { + peer = p; + break; } + spin_unlock_irqrestore(&dev->lock, flags); - spin_unlock_irqrestore ( &priv->lock, flags ); - fw_debug ( "node_find_by_guid (%016llx) not found\n", (unsigned long long)guid ); - return NULL; + return peer; } -static struct ipv4_node *ipv4_node_find_by_nodeid(struct ipv4_priv *priv, u16 nodeid) { - struct ipv4_node *node; +/* FIXME caller must take the lock, or peer needs to be reference-counted */ +/* FIXME node_id doesn't mean anything without generation */ +static struct fwnet_peer *fwnet_peer_find_by_node_id(struct fwnet_device *dev, + u16 node_id) +{ + struct fwnet_peer *p, *peer = NULL; unsigned long flags; - spin_lock_irqsave(&priv->lock, flags); - list_for_each_entry(node, &priv->card->ipv4_nodes, ipv4_nodes) - if (node->nodeid == nodeid) { - /* FIXME: lock the node first? */ - spin_unlock_irqrestore ( &priv->lock, flags ); - fw_debug ( "node_find_by_nodeid (%x) found %p\n", nodeid, node ); - return node; + spin_lock_irqsave(&dev->lock, flags); + list_for_each_entry(p, &dev->card->peer_list, peer_link) + if (p->node_id == node_id) { + peer = p; + break; } - fw_debug ( "node_find_by_nodeid (%x) not found\n", nodeid ); - spin_unlock_irqrestore ( &priv->lock, flags ); - return NULL; + spin_unlock_irqrestore(&dev->lock, flags); + + return peer; } -/* This is only complicated because we can't assume priv exists */ -static void ipv4_node_delete ( struct fw_card *card, struct fw_device *device ) { - struct net_device *netdev; - struct ipv4_priv *priv; - struct ipv4_node *node; +/* FIXME */ +static void fwnet_peer_delete(struct fw_card *card, struct fw_device *device) +{ + struct net_device *net; + struct fwnet_device *dev; + struct fwnet_peer *peer; u64 guid; unsigned long flags; - struct ipv4_partial_datagram *pd, *pd_next; + struct fwnet_partial_datagram *pd, *pd_next; guid = (u64)device->config_rom[3] << 32 | device->config_rom[4]; - netdev = card->netdev; - if ( netdev ) - priv = netdev_priv ( netdev ); + net = card->netdev; + if (net) + dev = netdev_priv(net); else - priv = NULL; - if ( priv ) - spin_lock_irqsave ( &priv->lock, flags ); - list_for_each_entry( node, &card->ipv4_nodes, ipv4_nodes ) { - if ( node->guid == guid ) { - list_del ( &node->ipv4_nodes ); - list_for_each_entry_safe( pd, pd_next, &node->pdg_list, pdg_list ) - ipv4_pd_delete ( pd ); + dev = NULL; + if (dev) + spin_lock_irqsave(&dev->lock, flags); + + list_for_each_entry(peer, &card->peer_list, peer_link) { + if (peer->guid == guid) { + list_del(&peer->peer_link); + list_for_each_entry_safe(pd, pd_next, &peer->pd_list, + pd_link) + fwnet_pd_delete(pd); break; } } - if ( priv ) - spin_unlock_irqrestore ( &priv->lock, flags ); + if (dev) + spin_unlock_irqrestore(&dev->lock, flags); } -/* ------------------------------------------------------------------ */ - - -static int ipv4_finish_incoming_packet ( struct net_device *netdev, - struct sk_buff *skb, u16 source_node_id, bool is_broadcast, u16 ether_type ) { - struct ipv4_priv *priv; - static u64 broadcast_hw = ~0ULL; +static int fwnet_finish_incoming_packet(struct net_device *net, + struct sk_buff *skb, u16 source_node_id, + bool is_broadcast, u16 ether_type) +{ + struct fwnet_device *dev; + static const __be64 broadcast_hw = cpu_to_be64(~0ULL); int status; - u64 guid; + __be64 guid; - fw_debug ( "ipv4_finish_incoming_packet(%p, %p, %x, %s, %x\n", - netdev, skb, source_node_id, is_broadcast ? "true" : "false", ether_type ); - priv = netdev_priv(netdev); + dev = netdev_priv(net); /* Write metadata, and then pass to the receive level */ - skb->dev = netdev; + skb->dev = net; skb->ip_summed = CHECKSUM_UNNECESSARY; /* don't check it */ /* @@ -724,73 +601,75 @@ static int ipv4_finish_incoming_packet ( struct net_device *netdev, * about the sending machine. */ if (ether_type == ETH_P_ARP) { - struct ipv4_arp *arp1394; + struct rfc2734_arp *arp1394; struct arphdr *arp; unsigned char *arp_ptr; u64 fifo_addr; + u64 peer_guid; u8 max_rec; u8 sspd; u16 max_payload; - struct ipv4_node *node; - static const u16 ipv4_speed_to_max_payload[] = { + struct fwnet_peer *peer; + static const u16 fwnet_speed_to_max_payload[] = { /* S100, S200, S400, S800, S1600, S3200 */ 512, 1024, 2048, 4096, 4096, 4096 }; - /* fw_debug ( "ARP packet\n" ); */ - arp1394 = (struct ipv4_arp *)skb->data; + arp1394 = (struct rfc2734_arp *)skb->data; arp = (struct arphdr *)skb->data; arp_ptr = (unsigned char *)(arp + 1); - fifo_addr = (u64)ntohs(arp1394->fifo_hi) << 32 | - ntohl(arp1394->fifo_lo); - max_rec = priv->card->max_receive; - if ( arp1394->max_rec < max_rec ) + fifo_addr = (u64)ntohs(arp1394->fifo_hi) << 32 + | ntohl(arp1394->fifo_lo); + max_rec = dev->card->max_receive; + if (arp1394->max_rec < max_rec) max_rec = arp1394->max_rec; sspd = arp1394->sspd; - /* - * Sanity check. MacOSX seems to be sending us 131 in this - * field (atleast on my Panther G5). Not sure why. - */ - if (sspd > 5 ) { - fw_notify ( "sspd %x out of range\n", sspd ); + /* Sanity check. OS X 10.3 PPC reportedly sends 131. */ + if (sspd > SCODE_3200) { + fw_notify("sspd %x out of range\n", sspd); sspd = 0; } - max_payload = min(ipv4_speed_to_max_payload[sspd], - (u16)(1 << (max_rec + 1))) - IPV4_UNFRAG_HDR_SIZE; + max_payload = min(fwnet_speed_to_max_payload[sspd], + (u16)(1 << (max_rec + 1))) - RFC2374_UNFRAG_HDR_SIZE; - guid = be64_to_cpu(get_unaligned(&arp1394->s_uniq_id)); - node = ipv4_node_find_by_guid(priv, guid); - if (!node) { - fw_notify ( "No node for ARP packet from %llx\n", guid ); + peer_guid = get_unaligned_be64(&arp1394->s_uniq_id); + peer = fwnet_peer_find_by_guid(dev, peer_guid); + if (!peer) { + fw_notify("No peer for ARP packet from %016llx\n", + (unsigned long long)peer_guid); goto failed_proto; } - if ( node->nodeid != source_node_id || node->generation != priv->card->generation ) { - fw_notify ( "Internal error: node->nodeid (%x) != soucre_node_id (%x) or node->generation (%x) != priv->card->generation(%x)\n", - node->nodeid, source_node_id, node->generation, priv->card->generation ); - node->nodeid = source_node_id; - node->generation = priv->card->generation; + + /* FIXME don't use card->generation */ + if (peer->node_id != source_node_id || + peer->generation != dev->card->generation) { + fw_notify("Internal error: peer->node_id (%x) != " + "source_node_id (%x) or peer->generation (%x)" + " != dev->card->generation(%x)\n", + peer->node_id, source_node_id, + peer->generation, dev->card->generation); + peer->node_id = source_node_id; + peer->generation = dev->card->generation; } /* FIXME: for debugging */ - if ( sspd > SCODE_400 ) + if (sspd > SCODE_400) sspd = SCODE_400; /* Update our speed/payload/fifo_offset table */ /* * FIXME: this does not handle cases where two high-speed endpoints must use a slower speed because of * a lower speed hub between them. We need to look at the actual topology map here. */ - fw_debug ( "Setting node %p fifo %llx (was %llx), max_payload %x (was %x), speed %x (was %x)\n", - node, fifo_addr, node->fifo, max_payload, node->max_payload, sspd, node->xmt_speed ); - node->fifo = fifo_addr; - node->max_payload = max_payload; + peer->fifo = fifo_addr; + peer->max_payload = max_payload; /* * Only allow speeds to go down from their initial value. - * Otherwise a local node that can only do S400 or slower may - * be told to transmit at S800 to a faster remote node. + * Otherwise a local peer that can only do S400 or slower may + * be told to transmit at S800 to a faster remote peer. */ - if ( node->xmt_speed > sspd ) - node->xmt_speed = sspd; + if (peer->xmt_speed > sspd) + peer->xmt_speed = sspd; /* * Now that we're done with the 1394 specific stuff, we'll @@ -805,248 +684,257 @@ static int ipv4_finish_incoming_packet ( struct net_device *netdev, */ arp->ar_hln = 8; - arp_ptr += arp->ar_hln; /* skip over sender unique id */ - *(u32 *)arp_ptr = arp1394->sip; /* move sender IP addr */ - arp_ptr += arp->ar_pln; /* skip over sender IP addr */ + /* skip over sender unique id */ + arp_ptr += arp->ar_hln; + /* move sender IP addr */ + put_unaligned(arp1394->sip, (u32 *)arp_ptr); + /* skip over sender IP addr */ + arp_ptr += arp->ar_pln; if (arp->ar_op == htons(ARPOP_REQUEST)) memset(arp_ptr, 0, sizeof(u64)); else - memcpy(arp_ptr, netdev->dev_addr, sizeof(u64)); + memcpy(arp_ptr, net->dev_addr, sizeof(u64)); } /* Now add the ethernet header. */ - guid = cpu_to_be64(priv->card->guid); - if (dev_hard_header(skb, netdev, ether_type, is_broadcast ? &broadcast_hw : &guid, NULL, - skb->len) >= 0) { - struct ipv4_ether_hdr *eth; + guid = cpu_to_be64(dev->card->guid); + if (dev_hard_header(skb, net, ether_type, + is_broadcast ? &broadcast_hw : &guid, + NULL, skb->len) >= 0) { + struct fwnet_header *eth; u16 *rawp; __be16 protocol; skb_reset_mac_header(skb); skb_pull(skb, sizeof(*eth)); - eth = ipv4_ether_hdr(skb); + eth = (struct fwnet_header *)skb_mac_header(skb); if (*eth->h_dest & 1) { - if (memcmp(eth->h_dest, netdev->broadcast, netdev->addr_len) == 0) { - fw_debug ( "Broadcast\n" ); + if (memcmp(eth->h_dest, net->broadcast, + net->addr_len) == 0) skb->pkt_type = PACKET_BROADCAST; - } #if 0 else skb->pkt_type = PACKET_MULTICAST; #endif } else { - if (memcmp(eth->h_dest, netdev->dev_addr, netdev->addr_len)) { + if (memcmp(eth->h_dest, net->dev_addr, net->addr_len)) { u64 a1, a2; - memcpy ( &a1, eth->h_dest, sizeof(u64)); - memcpy ( &a2, netdev->dev_addr, sizeof(u64)); - fw_debug ( "Otherhost %llx %llx %x\n", a1, a2, netdev->addr_len ); + memcpy(&a1, eth->h_dest, sizeof(u64)); + memcpy(&a2, net->dev_addr, sizeof(u64)); skb->pkt_type = PACKET_OTHERHOST; } } if (ntohs(eth->h_proto) >= 1536) { - fw_debug ( " proto %x %x\n", eth->h_proto, ntohs(eth->h_proto) ); protocol = eth->h_proto; } else { rawp = (u16 *)skb->data; - if (*rawp == 0xFFFF) { - fw_debug ( "proto 802_3\n" ); + if (*rawp == 0xffff) protocol = htons(ETH_P_802_3); - } else { - fw_debug ( "proto 802_2\n" ); + else protocol = htons(ETH_P_802_2); - } } skb->protocol = protocol; } status = netif_rx(skb); - if ( status == NET_RX_DROP) { - netdev->stats.rx_errors++; - netdev->stats.rx_dropped++; + if (status == NET_RX_DROP) { + net->stats.rx_errors++; + net->stats.rx_dropped++; } else { - netdev->stats.rx_packets++; - netdev->stats.rx_bytes += skb->len; + net->stats.rx_packets++; + net->stats.rx_bytes += skb->len; } - if (netif_queue_stopped(netdev)) - netif_wake_queue(netdev); + if (netif_queue_stopped(net)) + netif_wake_queue(net); + return 0; failed_proto: - netdev->stats.rx_errors++; - netdev->stats.rx_dropped++; + net->stats.rx_errors++; + net->stats.rx_dropped++; + dev_kfree_skb_any(skb); - if (netif_queue_stopped(netdev)) - netif_wake_queue(netdev); - netdev->last_rx = jiffies; + if (netif_queue_stopped(net)) + netif_wake_queue(net); + + net->last_rx = jiffies; + return 0; } -/* ------------------------------------------------------------------ */ - -static int ipv4_incoming_packet ( struct ipv4_priv *priv, u32 *buf, int len, u16 source_node_id, bool is_broadcast ) { +static int fwnet_incoming_packet(struct fwnet_device *dev, __be32 *buf, int len, + u16 source_node_id, bool is_broadcast) +{ struct sk_buff *skb; - struct net_device *netdev; - struct ipv4_hdr hdr; + struct net_device *net; + struct rfc2734_header hdr; unsigned lf; unsigned long flags; - struct ipv4_node *node; - struct ipv4_partial_datagram *pd; + struct fwnet_peer *peer; + struct fwnet_partial_datagram *pd; int fg_off; int dg_size; u16 datagram_label; int retval; u16 ether_type; - fw_debug ( "ipv4_incoming_packet(%p, %p, %d, %x, %s)\n", priv, buf, len, source_node_id, is_broadcast ? "true" : "false" ); - netdev = priv->card->netdev; + net = dev->card->netdev; - hdr.w0 = ntohl(buf[0]); - lf = ipv4_get_hdr_lf(&hdr); - if ( lf == IPV4_HDR_UNFRAG ) { + hdr.w0 = be32_to_cpu(buf[0]); + lf = fwnet_get_hdr_lf(&hdr); + if (lf == RFC2374_HDR_UNFRAG) { /* * An unfragmented datagram has been received by the ieee1394 * bus. Build an skbuff around it so we can pass it to the * high level network layer. */ - ether_type = ipv4_get_hdr_ether_type(&hdr); - fw_debug ( "header w0 = %x, lf = %x, ether_type = %x\n", hdr.w0, lf, ether_type ); + ether_type = fwnet_get_hdr_ether_type(&hdr); buf++; - len -= IPV4_UNFRAG_HDR_SIZE; + len -= RFC2374_UNFRAG_HDR_SIZE; - skb = dev_alloc_skb(len + netdev->hard_header_len + 15); + skb = dev_alloc_skb(len + net->hard_header_len + 15); if (unlikely(!skb)) { - fw_error ( "Out of memory for incoming packet\n"); - netdev->stats.rx_dropped++; + fw_error("out of memory\n"); + net->stats.rx_dropped++; + return -1; } - skb_reserve(skb, (netdev->hard_header_len + 15) & ~15); - memcpy(skb_put(skb, len), buf, len ); - return ipv4_finish_incoming_packet(netdev, skb, source_node_id, is_broadcast, ether_type ); + skb_reserve(skb, (net->hard_header_len + 15) & ~15); + memcpy(skb_put(skb, len), buf, len); + + return fwnet_finish_incoming_packet(net, skb, source_node_id, + is_broadcast, ether_type); } /* A datagram fragment has been received, now the fun begins. */ hdr.w1 = ntohl(buf[1]); - buf +=2; - len -= IPV4_FRAG_HDR_SIZE; - if ( lf ==IPV4_HDR_FIRSTFRAG ) { - ether_type = ipv4_get_hdr_ether_type(&hdr); + buf += 2; + len -= RFC2374_FRAG_HDR_SIZE; + if (lf == RFC2374_HDR_FIRSTFRAG) { + ether_type = fwnet_get_hdr_ether_type(&hdr); fg_off = 0; } else { - fg_off = ipv4_get_hdr_fg_off(&hdr); - ether_type = 0; /* Shut up compiler! */ + ether_type = 0; + fg_off = fwnet_get_hdr_fg_off(&hdr); } - datagram_label = ipv4_get_hdr_dgl(&hdr); - dg_size = ipv4_get_hdr_dg_size(&hdr); /* ??? + 1 */ - fw_debug ( "fragmented: %x.%x = lf %x, ether_type %x, fg_off %x, dgl %x, dg_size %x\n", hdr.w0, hdr.w1, lf, ether_type, fg_off, datagram_label, dg_size ); - node = ipv4_node_find_by_nodeid ( priv, source_node_id); - spin_lock_irqsave(&node->pdg_lock, flags); - pd = ipv4_pd_find( node, datagram_label ); + datagram_label = fwnet_get_hdr_dgl(&hdr); + dg_size = fwnet_get_hdr_dg_size(&hdr); /* ??? + 1 */ + peer = fwnet_peer_find_by_node_id(dev, source_node_id); + + spin_lock_irqsave(&peer->pdg_lock, flags); + + pd = fwnet_pd_find(peer, datagram_label); if (pd == NULL) { - while ( node->pdg_size >= ipv4_mpd ) { + while (peer->pdg_size >= FWNET_MAX_FRAGMENTS) { /* remove the oldest */ - ipv4_pd_delete ( list_first_entry(&node->pdg_list, struct ipv4_partial_datagram, pdg_list) ); - node->pdg_size--; + fwnet_pd_delete(list_first_entry(&peer->pd_list, + struct fwnet_partial_datagram, pd_link)); + peer->pdg_size--; } - pd = ipv4_pd_new ( netdev, node, datagram_label, dg_size, - buf, fg_off, len); - if ( pd == NULL) { + pd = fwnet_pd_new(net, peer, datagram_label, + dg_size, buf, fg_off, len); + if (pd == NULL) { retval = -ENOMEM; goto bad_proto; } - node->pdg_size++; + peer->pdg_size++; } else { - if (ipv4_frag_overlap(pd, fg_off, len) || pd->datagram_size != dg_size) { + if (fwnet_frag_overlap(pd, fg_off, len) || + pd->datagram_size != dg_size) { /* * Differing datagram sizes or overlapping fragments, - * Either way the remote machine is playing silly buggers - * with us: obliterate the old datagram and start a new one. + * discard old datagram and start a new one. */ - ipv4_pd_delete ( pd ); - pd = ipv4_pd_new ( netdev, node, datagram_label, - dg_size, buf, fg_off, len); - if ( pd == NULL ) { + fwnet_pd_delete(pd); + pd = fwnet_pd_new(net, peer, datagram_label, + dg_size, buf, fg_off, len); + if (pd == NULL) { retval = -ENOMEM; - node->pdg_size--; + peer->pdg_size--; goto bad_proto; } } else { - bool worked; - - worked = ipv4_pd_update ( node, pd, - buf, fg_off, len ); - if ( ! worked ) { + if (!fwnet_pd_update(peer, pd, buf, fg_off, len)) { /* * Couldn't save off fragment anyway * so might as well obliterate the * datagram now. */ - ipv4_pd_delete ( pd ); - node->pdg_size--; + fwnet_pd_delete(pd); + peer->pdg_size--; goto bad_proto; } } } /* new datagram or add to existing one */ - if ( lf == IPV4_HDR_FIRSTFRAG ) + if (lf == RFC2374_HDR_FIRSTFRAG) pd->ether_type = ether_type; - if ( ipv4_pd_is_complete ( pd ) ) { + + if (fwnet_pd_is_complete(pd)) { ether_type = pd->ether_type; - node->pdg_size--; + peer->pdg_size--; skb = skb_get(pd->skb); - ipv4_pd_delete ( pd ); - spin_unlock_irqrestore(&node->pdg_lock, flags); - return ipv4_finish_incoming_packet ( netdev, skb, source_node_id, false, ether_type ); + fwnet_pd_delete(pd); + + spin_unlock_irqrestore(&peer->pdg_lock, flags); + + return fwnet_finish_incoming_packet(net, skb, source_node_id, + false, ether_type); } /* * Datagram is not complete, we're done for the * moment. */ - spin_unlock_irqrestore(&node->pdg_lock, flags); + spin_unlock_irqrestore(&peer->pdg_lock, flags); + return 0; bad_proto: - spin_unlock_irqrestore(&node->pdg_lock, flags); - if (netif_queue_stopped(netdev)) - netif_wake_queue(netdev); + spin_unlock_irqrestore(&peer->pdg_lock, flags); + + if (netif_queue_stopped(net)) + netif_wake_queue(net); + return 0; } -static void ipv4_receive_packet ( 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 ipv4_priv *priv; +static void fwnet_receive_packet(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 fwnet_device *dev; int status; - fw_debug ( "ipv4_receive_packet(%p,%p,%x,%x,%x,%x,%x,%llx,%p,%lx,%p)\n", - card, r, tcode, destination, source, generation, speed, offset, payload, - (unsigned long)length, callback_data); - print_hex_dump ( KERN_DEBUG, "header: ", DUMP_PREFIX_OFFSET, 32, 1, payload, length, false ); - priv = callback_data; - if ( tcode != TCODE_WRITE_BLOCK_REQUEST - || destination != card->node_id - || generation != card->generation - || offset != priv->handler.offset ) { + dev = callback_data; + if (tcode != TCODE_WRITE_BLOCK_REQUEST + || destination != card->node_id /* <- FIXME */ + || generation != card->generation /* <- FIXME */ + || offset != dev->handler.offset) { fw_send_response(card, r, RCODE_CONFLICT_ERROR); - fw_debug("Conflict error card node_id=%x, card generation=%x, local offset %llx\n", - card->node_id, card->generation, (unsigned long long)priv->handler.offset ); + return; } - status = ipv4_incoming_packet ( priv, payload, length, source, false ); - if ( status != 0 ) { - fw_error ( "Incoming packet failure\n" ); - fw_send_response ( card, r, RCODE_CONFLICT_ERROR ); + + status = fwnet_incoming_packet(dev, payload, length, source, false); + if (status != 0) { + fw_error("Incoming packet failure\n"); + fw_send_response(card, r, RCODE_CONFLICT_ERROR); + return; } - fw_send_response ( card, r, RCODE_COMPLETE ); + + fw_send_response(card, r, RCODE_COMPLETE); } -static void ipv4_receive_broadcast(struct fw_iso_context *context, u32 cycle, - size_t header_length, void *header, void *data) { - struct ipv4_priv *priv; +static void fwnet_receive_broadcast(struct fw_iso_context *context, + u32 cycle, size_t header_length, void *header, void *data) +{ + struct fwnet_device *dev; struct fw_iso_packet packet; struct fw_card *card; - u16 *hdr_ptr; - u32 *buf_ptr; + __be16 *hdr_ptr; + __be32 *buf_ptr; int retval; u32 length; u16 source_node_id; @@ -1055,70 +943,68 @@ static void ipv4_receive_broadcast(struct fw_iso_context *context, u32 cycle, unsigned long offset; unsigned long flags; - fw_debug ( "ipv4_receive_broadcast ( context=%p, cycle=%x, header_length=%lx, header=%p, data=%p )\n", context, cycle, (unsigned long)header_length, header, data ); - print_hex_dump ( KERN_DEBUG, "header: ", DUMP_PREFIX_OFFSET, 32, 1, header, header_length, false ); - priv = data; - card = priv->card; + dev = data; + card = dev->card; hdr_ptr = header; - length = ntohs(hdr_ptr[0]); - spin_lock_irqsave(&priv->lock,flags); - offset = priv->rcv_buffer_size * priv->broadcast_rcv_next_ptr; - buf_ptr = priv->broadcast_rcv_buffer_ptrs[priv->broadcast_rcv_next_ptr++]; - if ( priv->broadcast_rcv_next_ptr == priv->num_broadcast_rcv_ptrs ) - priv->broadcast_rcv_next_ptr = 0; - spin_unlock_irqrestore(&priv->lock,flags); - fw_debug ( "length %u at %p\n", length, buf_ptr ); - print_hex_dump ( KERN_DEBUG, "buffer: ", DUMP_PREFIX_OFFSET, 32, 1, buf_ptr, length, false ); + length = be16_to_cpup(hdr_ptr); + + spin_lock_irqsave(&dev->lock, flags); + + offset = dev->rcv_buffer_size * dev->broadcast_rcv_next_ptr; + buf_ptr = dev->broadcast_rcv_buffer_ptrs[dev->broadcast_rcv_next_ptr++]; + if (dev->broadcast_rcv_next_ptr == dev->num_broadcast_rcv_ptrs) + dev->broadcast_rcv_next_ptr = 0; + + spin_unlock_irqrestore(&dev->lock, flags); specifier_id = (be32_to_cpu(buf_ptr[0]) & 0xffff) << 8 | (be32_to_cpu(buf_ptr[1]) & 0xff000000) >> 24; - ver = be32_to_cpu(buf_ptr[1]) & 0xFFFFFF; + ver = be32_to_cpu(buf_ptr[1]) & 0xffffff; source_node_id = be32_to_cpu(buf_ptr[0]) >> 16; - /* fw_debug ( "source %x SpecID %x ver %x\n", source_node_id, specifier_id, ver ); */ - if ( specifier_id == IPV4_GASP_SPECIFIER_ID && ver == IPV4_GASP_VERSION ) { + + if (specifier_id == IANA_SPECIFIER_ID && ver == RFC2734_SW_VERSION) { buf_ptr += 2; - length -= IPV4_GASP_OVERHEAD; - ipv4_incoming_packet(priv, buf_ptr, length, source_node_id, true); - } else - fw_debug ( "Ignoring packet: not GASP\n" ); - packet.payload_length = priv->rcv_buffer_size; + length -= IEEE1394_GASP_HDR_SIZE; + fwnet_incoming_packet(dev, buf_ptr, length, + source_node_id, true); + } + + packet.payload_length = dev->rcv_buffer_size; packet.interrupt = 1; packet.skip = 0; packet.tag = 3; packet.sy = 0; - packet.header_length = IPV4_GASP_OVERHEAD; - spin_lock_irqsave(&priv->lock,flags); - retval = fw_iso_context_queue ( priv->broadcast_rcv_context, &packet, - &priv->broadcast_rcv_buffer, offset ); - spin_unlock_irqrestore(&priv->lock,flags); - if ( retval < 0 ) - fw_error ( "requeue failed\n" ); -} + packet.header_length = IEEE1394_GASP_HDR_SIZE; + + spin_lock_irqsave(&dev->lock, flags); -static void debug_ptask ( struct ipv4_packet_task *ptask ) { - static const char *tx_types[] = { "Unknown", "GASP", "Write" }; - - fw_debug ( "packet %p { hdr { w0 %x w1 %x }, skb %p, priv %p," - " tx_type %s, outstanding_pkts %d, max_payload %x, fifo %llx," - " speed %x, dest_node %x, generation %x }\n", - ptask, ptask->hdr.w0, ptask->hdr.w1, ptask->skb, ptask->priv, - ptask->tx_type > IPV4_WRREQ ? "Invalid" : tx_types[ptask->tx_type], - ptask->outstanding_pkts, ptask->max_payload, - ptask->fifo_addr, ptask->speed, ptask->dest_node, ptask->generation ); - print_hex_dump ( KERN_DEBUG, "packet :", DUMP_PREFIX_OFFSET, 32, 1, - ptask->skb->data, ptask->skb->len, false ); + retval = fw_iso_context_queue(dev->broadcast_rcv_context, &packet, + &dev->broadcast_rcv_buffer, offset); + + spin_unlock_irqrestore(&dev->lock, flags); + + if (retval < 0) + fw_error("requeue failed\n"); } -static void ipv4_transmit_packet_done ( struct ipv4_packet_task *ptask ) { - struct ipv4_priv *priv; +static struct kmem_cache *fwnet_packet_task_cache; + +static int fwnet_send_packet(struct fwnet_packet_task *ptask); + +static void fwnet_transmit_packet_done(struct fwnet_packet_task *ptask) +{ + struct fwnet_device *dev; unsigned long flags; - priv = ptask->priv; - spin_lock_irqsave ( &priv->lock, flags ); - list_del ( &ptask->packet_list ); - spin_unlock_irqrestore ( &priv->lock, flags ); - ptask->outstanding_pkts--; - if ( ptask->outstanding_pkts > 0 ) { + dev = ptask->dev; + + spin_lock_irqsave(&dev->lock, flags); + list_del(&ptask->pt_link); + spin_unlock_irqrestore(&dev->lock, flags); + + ptask->outstanding_pkts--; /* FIXME access inside lock */ + + if (ptask->outstanding_pkts > 0) { u16 dg_size; u16 fg_off; u16 datagram_label; @@ -1126,133 +1012,139 @@ static void ipv4_transmit_packet_done ( struct ipv4_packet_task *ptask ) { struct sk_buff *skb; /* Update the ptask to point to the next fragment and send it */ - lf = ipv4_get_hdr_lf(&ptask->hdr); + lf = fwnet_get_hdr_lf(&ptask->hdr); switch (lf) { - case IPV4_HDR_LASTFRAG: - case IPV4_HDR_UNFRAG: + case RFC2374_HDR_LASTFRAG: + case RFC2374_HDR_UNFRAG: default: - fw_error ( "Outstanding packet %x lf %x, header %x,%x\n", ptask->outstanding_pkts, lf, ptask->hdr.w0, ptask->hdr.w1 ); + fw_error("Outstanding packet %x lf %x, header %x,%x\n", + ptask->outstanding_pkts, lf, ptask->hdr.w0, + ptask->hdr.w1); BUG(); - case IPV4_HDR_FIRSTFRAG: + case RFC2374_HDR_FIRSTFRAG: /* Set frag type here for future interior fragments */ - dg_size = ipv4_get_hdr_dg_size(&ptask->hdr); - fg_off = ptask->max_payload - IPV4_FRAG_HDR_SIZE; - datagram_label = ipv4_get_hdr_dgl(&ptask->hdr); + dg_size = fwnet_get_hdr_dg_size(&ptask->hdr); + fg_off = ptask->max_payload - RFC2374_FRAG_HDR_SIZE; + datagram_label = fwnet_get_hdr_dgl(&ptask->hdr); break; - case IPV4_HDR_INTFRAG: - dg_size = ipv4_get_hdr_dg_size(&ptask->hdr); - fg_off = ipv4_get_hdr_fg_off(&ptask->hdr) + ptask->max_payload - IPV4_FRAG_HDR_SIZE; - datagram_label = ipv4_get_hdr_dgl(&ptask->hdr); + case RFC2374_HDR_INTFRAG: + dg_size = fwnet_get_hdr_dg_size(&ptask->hdr); + fg_off = fwnet_get_hdr_fg_off(&ptask->hdr) + + ptask->max_payload - RFC2374_FRAG_HDR_SIZE; + datagram_label = fwnet_get_hdr_dgl(&ptask->hdr); break; } skb = ptask->skb; - skb_pull ( skb, ptask->max_payload ); - if ( ptask->outstanding_pkts > 1 ) { - ipv4_make_sf_hdr ( &ptask->hdr, - IPV4_HDR_INTFRAG, dg_size, fg_off, datagram_label ); + skb_pull(skb, ptask->max_payload); + if (ptask->outstanding_pkts > 1) { + fwnet_make_sf_hdr(&ptask->hdr, RFC2374_HDR_INTFRAG, + dg_size, fg_off, datagram_label); } else { - ipv4_make_sf_hdr ( &ptask->hdr, - IPV4_HDR_LASTFRAG, dg_size, fg_off, datagram_label ); - ptask->max_payload = skb->len + IPV4_FRAG_HDR_SIZE; - + fwnet_make_sf_hdr(&ptask->hdr, RFC2374_HDR_LASTFRAG, + dg_size, fg_off, datagram_label); + ptask->max_payload = skb->len + RFC2374_FRAG_HDR_SIZE; } - ipv4_send_packet ( ptask ); + fwnet_send_packet(ptask); } else { - dev_kfree_skb_any ( ptask->skb ); - kmem_cache_free( ipv4_packet_task_cache, ptask ); + dev_kfree_skb_any(ptask->skb); + kmem_cache_free(fwnet_packet_task_cache, ptask); } } -static void ipv4_write_complete ( struct fw_card *card, int rcode, - void *payload, size_t length, void *data ) { - struct ipv4_packet_task *ptask; +static void fwnet_write_complete(struct fw_card *card, int rcode, + void *payload, size_t length, void *data) +{ + struct fwnet_packet_task *ptask; ptask = data; - fw_debug ( "ipv4_write_complete ( %p, %x, %p, %lx, %p )\n", - card, rcode, payload, (unsigned long)length, data ); - debug_ptask ( ptask ); - if ( rcode == RCODE_COMPLETE ) { - ipv4_transmit_packet_done ( ptask ); - } else { - fw_error ( "ipv4_write_complete: failed: %x\n", rcode ); + if (rcode == RCODE_COMPLETE) + fwnet_transmit_packet_done(ptask); + else + fw_error("fwnet_write_complete: failed: %x\n", rcode); /* ??? error recovery */ - } } -static int ipv4_send_packet ( struct ipv4_packet_task *ptask ) { - struct ipv4_priv *priv; +static int fwnet_send_packet(struct fwnet_packet_task *ptask) +{ + struct fwnet_device *dev; unsigned tx_len; - struct ipv4_hdr *bufhdr; + struct rfc2734_header *bufhdr; unsigned long flags; - struct net_device *netdev; -#if 0 /* stefanr */ - int retval; -#endif + struct net_device *net; - fw_debug ( "ipv4_send_packet\n" ); - debug_ptask ( ptask ); - priv = ptask->priv; + dev = ptask->dev; tx_len = ptask->max_payload; - switch (ipv4_get_hdr_lf(&ptask->hdr)) { - case IPV4_HDR_UNFRAG: - bufhdr = (struct ipv4_hdr *)skb_push(ptask->skb, IPV4_UNFRAG_HDR_SIZE); - bufhdr->w0 = htonl(ptask->hdr.w0); + switch (fwnet_get_hdr_lf(&ptask->hdr)) { + case RFC2374_HDR_UNFRAG: + bufhdr = (struct rfc2734_header *) + skb_push(ptask->skb, RFC2374_UNFRAG_HDR_SIZE); + put_unaligned_be32(ptask->hdr.w0, &bufhdr->w0); break; - case IPV4_HDR_FIRSTFRAG: - case IPV4_HDR_INTFRAG: - case IPV4_HDR_LASTFRAG: - bufhdr = (struct ipv4_hdr *)skb_push(ptask->skb, IPV4_FRAG_HDR_SIZE); - bufhdr->w0 = htonl(ptask->hdr.w0); - bufhdr->w1 = htonl(ptask->hdr.w1); + case RFC2374_HDR_FIRSTFRAG: + case RFC2374_HDR_INTFRAG: + case RFC2374_HDR_LASTFRAG: + bufhdr = (struct rfc2734_header *) + skb_push(ptask->skb, RFC2374_FRAG_HDR_SIZE); + put_unaligned_be32(ptask->hdr.w0, &bufhdr->w0); + put_unaligned_be32(ptask->hdr.w1, &bufhdr->w1); break; default: BUG(); } - if ( ptask->tx_type == IPV4_GASP ) { - u32 *packets; + if (ptask->dest_node == IEEE1394_ALL_NODES) { + u8 *p; int generation; - int nodeid; + int node_id; /* ptask->generation may not have been set yet */ - generation = priv->card->generation; + generation = dev->card->generation; smp_rmb(); - nodeid = priv->card->node_id; - packets = (u32 *)skb_push(ptask->skb, sizeof(u32)*2); - packets[0] = htonl(nodeid << 16 | (IPV4_GASP_SPECIFIER_ID>>8)); - packets[1] = htonl((IPV4_GASP_SPECIFIER_ID & 0xFF) << 24 | IPV4_GASP_VERSION); - fw_send_request ( priv->card, &ptask->transaction, TCODE_STREAM_DATA, - fw_stream_packet_destination_id(3, BROADCAST_CHANNEL, 0), - generation, SCODE_100, 0ULL, ptask->skb->data, tx_len + 8, ipv4_write_complete, ptask ); - spin_lock_irqsave(&priv->lock,flags); - list_add_tail ( &ptask->packet_list, &priv->broadcasted_list ); - spin_unlock_irqrestore(&priv->lock,flags); -#if 0 /* stefanr */ - return retval; -#else + node_id = dev->card->node_id; + + p = skb_push(ptask->skb, 8); + put_unaligned_be32(node_id << 16 | IANA_SPECIFIER_ID >> 8, p); + put_unaligned_be32((IANA_SPECIFIER_ID & 0xff) << 24 + | RFC2734_SW_VERSION, &p[4]); + + /* We should not transmit if broadcast_channel.valid == 0. */ + fw_send_request(dev->card, &ptask->transaction, + TCODE_STREAM_DATA, + fw_stream_packet_destination_id(3, + IEEE1394_BROADCAST_CHANNEL, 0), + generation, SCODE_100, 0ULL, ptask->skb->data, + tx_len + 8, fwnet_write_complete, ptask); + + /* FIXME race? */ + spin_lock_irqsave(&dev->lock, flags); + list_add_tail(&ptask->pt_link, &dev->broadcasted_list); + spin_unlock_irqrestore(&dev->lock, flags); + return 0; -#endif } - fw_debug("send_request (%p, %p, WRITE_BLOCK, %x, %x, %x, %llx, %p, %d, %p, %p\n", - priv->card, &ptask->transaction, ptask->dest_node, ptask->generation, - ptask->speed, (unsigned long long)ptask->fifo_addr, ptask->skb->data, tx_len, - ipv4_write_complete, ptask ); - fw_send_request ( priv->card, &ptask->transaction, - TCODE_WRITE_BLOCK_REQUEST, ptask->dest_node, ptask->generation, ptask->speed, - ptask->fifo_addr, ptask->skb->data, tx_len, ipv4_write_complete, ptask ); - spin_lock_irqsave(&priv->lock,flags); - list_add_tail ( &ptask->packet_list, &priv->sent_list ); - spin_unlock_irqrestore(&priv->lock,flags); - netdev = priv->card->netdev; - netdev->trans_start = jiffies; + + fw_send_request(dev->card, &ptask->transaction, + TCODE_WRITE_BLOCK_REQUEST, ptask->dest_node, + ptask->generation, ptask->speed, ptask->fifo_addr, + ptask->skb->data, tx_len, fwnet_write_complete, ptask); + + /* FIXME race? */ + spin_lock_irqsave(&dev->lock, flags); + list_add_tail(&ptask->pt_link, &dev->sent_list); + spin_unlock_irqrestore(&dev->lock, flags); + + net = dev->card->netdev; + net->trans_start = jiffies; + return 0; } -static int ipv4_broadcast_start ( struct ipv4_priv *priv ) { +static int fwnet_broadcast_start(struct fwnet_device *dev) +{ struct fw_iso_context *context; int retval; unsigned num_packets; @@ -1260,150 +1152,151 @@ static int ipv4_broadcast_start ( struct ipv4_priv *priv ) { struct fw_iso_packet packet; unsigned long offset; unsigned u; - /* unsigned transmit_speed; */ -#if 0 /* stefanr */ - if ( priv->card->broadcast_channel != (BROADCAST_CHANNEL_VALID|BROADCAST_CHANNEL_INITIAL)) { - fw_notify ( "Invalid broadcast channel %x\n", priv->card->broadcast_channel ); - /* FIXME: try again later? */ - /* return -EINVAL; */ - } -#endif - if ( priv->local_fifo == INVALID_FIFO_ADDR ) { - struct fw_address_region region; - - priv->handler.length = FIFO_SIZE; - priv->handler.address_callback = ipv4_receive_packet; - priv->handler.callback_data = priv; - /* FIXME: this is OHCI, but what about others? */ - region.start = 0xffff00000000ULL; - region.end = 0xfffffffffffcULL; - - retval = fw_core_add_address_handler ( &priv->handler, ®ion ); - if ( retval < 0 ) + if (dev->local_fifo == FWNET_NO_FIFO_ADDR) { + /* outside OHCI posted write area? */ + static const struct fw_address_region region = { + .start = 0xffff00000000ULL, + .end = CSR_REGISTER_BASE, + }; + + dev->handler.length = 4096; + dev->handler.address_callback = fwnet_receive_packet; + dev->handler.callback_data = dev; + + retval = fw_core_add_address_handler(&dev->handler, ®ion); + if (retval < 0) goto failed_initial; - priv->local_fifo = priv->handler.offset; + + dev->local_fifo = dev->handler.offset; } - /* - * FIXME: rawiso limits us to PAGE_SIZE. This only matters if we ever have - * a machine with PAGE_SIZE < 4096 - */ - max_receive = 1U << (priv->card->max_receive + 1); - num_packets = ( ipv4_iso_page_count * PAGE_SIZE ) / max_receive; - if ( ! priv->broadcast_rcv_context ) { + max_receive = 1U << (dev->card->max_receive + 1); + num_packets = (FWNET_ISO_PAGE_COUNT * PAGE_SIZE) / max_receive; + + if (!dev->broadcast_rcv_context) { void **ptrptr; - context = fw_iso_context_create ( priv->card, - FW_ISO_CONTEXT_RECEIVE, BROADCAST_CHANNEL, - priv->card->link_speed, 8, ipv4_receive_broadcast, priv ); + context = fw_iso_context_create(dev->card, + FW_ISO_CONTEXT_RECEIVE, IEEE1394_BROADCAST_CHANNEL, + dev->card->link_speed, 8, fwnet_receive_broadcast, dev); if (IS_ERR(context)) { retval = PTR_ERR(context); goto failed_context_create; } - retval = fw_iso_buffer_init ( &priv->broadcast_rcv_buffer, - priv->card, ipv4_iso_page_count, DMA_FROM_DEVICE ); - if ( retval < 0 ) + + retval = fw_iso_buffer_init(&dev->broadcast_rcv_buffer, + dev->card, FWNET_ISO_PAGE_COUNT, DMA_FROM_DEVICE); + if (retval < 0) goto failed_buffer_init; - ptrptr = kmalloc ( sizeof(void*)*num_packets, GFP_KERNEL ); - if ( ! ptrptr ) { + + ptrptr = kmalloc(sizeof(void *) * num_packets, GFP_KERNEL); + if (!ptrptr) { retval = -ENOMEM; goto failed_ptrs_alloc; } - priv->broadcast_rcv_buffer_ptrs = ptrptr; - for ( u = 0; u < ipv4_iso_page_count; u++ ) { + + dev->broadcast_rcv_buffer_ptrs = ptrptr; + for (u = 0; u < FWNET_ISO_PAGE_COUNT; u++) { void *ptr; unsigned v; - ptr = kmap ( priv->broadcast_rcv_buffer.pages[u] ); - for ( v = 0; v < num_packets / ipv4_iso_page_count; v++ ) - *ptrptr++ = (void *)((char *)ptr + v * max_receive); + ptr = kmap(dev->broadcast_rcv_buffer.pages[u]); + for (v = 0; v < num_packets / FWNET_ISO_PAGE_COUNT; v++) + *ptrptr++ = (void *) + ((char *)ptr + v * max_receive); } - priv->broadcast_rcv_context = context; - } else - context = priv->broadcast_rcv_context; + dev->broadcast_rcv_context = context; + } else { + context = dev->broadcast_rcv_context; + } packet.payload_length = max_receive; packet.interrupt = 1; packet.skip = 0; packet.tag = 3; packet.sy = 0; - packet.header_length = IPV4_GASP_OVERHEAD; + packet.header_length = IEEE1394_GASP_HDR_SIZE; offset = 0; - for ( u = 0; u < num_packets; u++ ) { - retval = fw_iso_context_queue ( context, &packet, - &priv->broadcast_rcv_buffer, offset ); - if ( retval < 0 ) + + for (u = 0; u < num_packets; u++) { + retval = fw_iso_context_queue(context, &packet, + &dev->broadcast_rcv_buffer, offset); + if (retval < 0) goto failed_rcv_queue; + offset += max_receive; } - priv->num_broadcast_rcv_ptrs = num_packets; - priv->rcv_buffer_size = max_receive; - priv->broadcast_rcv_next_ptr = 0U; - retval = fw_iso_context_start ( context, -1, 0, FW_ISO_CONTEXT_MATCH_ALL_TAGS ); /* ??? sync */ - if ( retval < 0 ) + dev->num_broadcast_rcv_ptrs = num_packets; + dev->rcv_buffer_size = max_receive; + dev->broadcast_rcv_next_ptr = 0U; + retval = fw_iso_context_start(context, -1, 0, + FW_ISO_CONTEXT_MATCH_ALL_TAGS); /* ??? sync */ + if (retval < 0) goto failed_rcv_queue; - /* FIXME: adjust this when we know the max receive speeds of all other IP nodes on the bus. */ - /* since we only xmt at S100 ??? */ - priv->broadcast_xmt_max_payload = S100_BUFFER_SIZE - IPV4_GASP_OVERHEAD - IPV4_UNFRAG_HDR_SIZE; - priv->broadcast_state = IPV4_BROADCAST_RUNNING; + + /* FIXME: adjust it according to the min. speed of all known peers? */ + dev->broadcast_xmt_max_payload = IEEE1394_MAX_PAYLOAD_S100 + - IEEE1394_GASP_HDR_SIZE - RFC2374_UNFRAG_HDR_SIZE; + dev->broadcast_state = FWNET_BROADCAST_RUNNING; + return 0; failed_rcv_queue: - kfree ( priv->broadcast_rcv_buffer_ptrs ); - priv->broadcast_rcv_buffer_ptrs = NULL; + kfree(dev->broadcast_rcv_buffer_ptrs); + dev->broadcast_rcv_buffer_ptrs = NULL; failed_ptrs_alloc: - fw_iso_buffer_destroy ( &priv->broadcast_rcv_buffer, priv->card ); + fw_iso_buffer_destroy(&dev->broadcast_rcv_buffer, dev->card); failed_buffer_init: - fw_iso_context_destroy ( context ); - priv->broadcast_rcv_context = NULL; + fw_iso_context_destroy(context); + dev->broadcast_rcv_context = NULL; failed_context_create: - fw_core_remove_address_handler ( &priv->handler ); + fw_core_remove_address_handler(&dev->handler); failed_initial: - priv->local_fifo = INVALID_FIFO_ADDR; + dev->local_fifo = FWNET_NO_FIFO_ADDR; + return retval; } -/* This is called after an "ifup" */ -static int ipv4_open(struct net_device *dev) { - struct ipv4_priv *priv; +/* ifup */ +static int fwnet_open(struct net_device *net) +{ + struct fwnet_device *dev = netdev_priv(net); int ret; - priv = netdev_priv(dev); - if (priv->broadcast_state == IPV4_BROADCAST_ERROR) { - ret = ipv4_broadcast_start ( priv ); + if (dev->broadcast_state == FWNET_BROADCAST_ERROR) { + ret = fwnet_broadcast_start(dev); if (ret) return ret; } - netif_start_queue(dev); + netif_start_queue(net); + return 0; } -/* This is called after an "ifdown" */ -static int ipv4_stop(struct net_device *netdev) +/* ifdown */ +static int fwnet_stop(struct net_device *net) { - /* flush priv->wake */ - /* flush_scheduled_work(); */ + netif_stop_queue(net); + + /* Deallocate iso context for use by other applications? */ - netif_stop_queue(netdev); return 0; } -/* Transmit a packet (called by kernel) */ -static int ipv4_tx(struct sk_buff *skb, struct net_device *netdev) +static int fwnet_tx(struct sk_buff *skb, struct net_device *net) { - struct ipv4_ether_hdr hdr_buf; - struct ipv4_priv *priv = netdev_priv(netdev); + struct fwnet_header hdr_buf; + struct fwnet_device *dev = netdev_priv(net); __be16 proto; u16 dest_node; - enum ipv4_tx_type tx_type; unsigned max_payload; u16 dg_size; u16 *datagram_label_ptr; - struct ipv4_packet_task *ptask; - struct ipv4_node *node = NULL; + struct fwnet_packet_task *ptask; + struct fwnet_peer *peer = NULL; - ptask = kmem_cache_alloc(ipv4_packet_task_cache, GFP_ATOMIC); + ptask = kmem_cache_alloc(fwnet_packet_task_cache, GFP_ATOMIC); if (ptask == NULL) goto fail; @@ -1412,7 +1305,7 @@ static int ipv4_tx(struct sk_buff *skb, struct net_device *netdev) goto fail; /* - * Get rid of the fake ipv4 header, but first make a copy. + * Make a copy of the driver-specific header. * We might need to rebuild the header on tx failure. */ memcpy(&hdr_buf, skb->data, sizeof(hdr_buf)); @@ -1425,110 +1318,95 @@ static int ipv4_tx(struct sk_buff *skb, struct net_device *netdev) * Set the transmission type for the packet. ARP packets and IP * broadcast packets are sent via GASP. */ - if ( memcmp(hdr_buf.h_dest, netdev->broadcast, IPV4_ALEN) == 0 + if (memcmp(hdr_buf.h_dest, net->broadcast, FWNET_ALEN) == 0 || proto == htons(ETH_P_ARP) - || ( proto == htons(ETH_P_IP) - && IN_MULTICAST(ntohl(ip_hdr(skb)->daddr)) ) ) { - /* fw_debug ( "transmitting arp or multicast packet\n" );*/ - tx_type = IPV4_GASP; - dest_node = ALL_NODES; - max_payload = priv->broadcast_xmt_max_payload; - /* BUG_ON(max_payload < S100_BUFFER_SIZE - IPV4_GASP_OVERHEAD); */ - datagram_label_ptr = &priv->broadcast_xmt_datagramlabel; - ptask->fifo_addr = INVALID_FIFO_ADDR; - ptask->generation = 0U; - ptask->dest_node = 0U; - ptask->speed = 0; + || (proto == htons(ETH_P_IP) + && IN_MULTICAST(ntohl(ip_hdr(skb)->daddr)))) { + max_payload = dev->broadcast_xmt_max_payload; + datagram_label_ptr = &dev->broadcast_xmt_datagramlabel; + + ptask->fifo_addr = FWNET_NO_FIFO_ADDR; + ptask->generation = 0; + ptask->dest_node = IEEE1394_ALL_NODES; + ptask->speed = SCODE_100; } else { - __be64 guid = get_unaligned((u64 *)hdr_buf.h_dest); + __be64 guid = get_unaligned((__be64 *)hdr_buf.h_dest); u8 generation; - node = ipv4_node_find_by_guid(priv, be64_to_cpu(guid)); - if (!node) { - fw_debug ( "Normal packet but no node\n" ); + peer = fwnet_peer_find_by_guid(dev, be64_to_cpu(guid)); + if (!peer) goto fail; - } - if (node->fifo == INVALID_FIFO_ADDR) { - fw_debug ( "Normal packet but no fifo addr\n" ); + if (peer->fifo == FWNET_NO_FIFO_ADDR) goto fail; - } - /* fw_debug ( "Transmitting normal packet to %x at %llxx\n", node->nodeid, node->fifo ); */ - generation = node->generation; - dest_node = node->nodeid; - max_payload = node->max_payload; - /* BUG_ON(max_payload < S100_BUFFER_SIZE - IPV4_FRAG_HDR_SIZE); */ + generation = peer->generation; + smp_rmb(); + dest_node = peer->node_id; + + max_payload = peer->max_payload; + datagram_label_ptr = &peer->datagram_label; - datagram_label_ptr = &node->datagram_label; - tx_type = IPV4_WRREQ; - ptask->fifo_addr = node->fifo; + ptask->fifo_addr = peer->fifo; ptask->generation = generation; ptask->dest_node = dest_node; - ptask->speed = node->xmt_speed; + ptask->speed = peer->xmt_speed; } /* If this is an ARP packet, convert it */ if (proto == htons(ETH_P_ARP)) { - /* Convert a standard ARP packet to 1394 ARP. The first 8 bytes (the entire - * arphdr) is the same format as the ip1394 header, so they overlap. The rest - * needs to be munged a bit. The remainder of the arphdr is formatted based - * on hwaddr len and ipaddr len. We know what they'll be, so it's easy to - * judge. - * - * Now that the EUI is used for the hardware address all we need to do to make - * this work for 1394 is to insert 2 quadlets that contain max_rec size, - * speed, and unicast FIFO address information between the sender_unique_id - * and the IP addresses. - */ struct arphdr *arp = (struct arphdr *)skb->data; unsigned char *arp_ptr = (unsigned char *)(arp + 1); - struct ipv4_arp *arp1394 = (struct ipv4_arp *)skb->data; - u32 ipaddr; - - ipaddr = *(u32*)(arp_ptr + IPV4_ALEN); - arp1394->hw_addr_len = 16; - arp1394->max_rec = priv->card->max_receive; - arp1394->sspd = priv->card->link_speed; - arp1394->fifo_hi = htons(priv->local_fifo >> 32); - arp1394->fifo_lo = htonl(priv->local_fifo & 0xFFFFFFFF); - arp1394->sip = ipaddr; + struct rfc2734_arp *arp1394 = (struct rfc2734_arp *)skb->data; + __be32 ipaddr; + + ipaddr = get_unaligned((__be32 *)(arp_ptr + FWNET_ALEN)); + + arp1394->hw_addr_len = RFC2734_HW_ADDR_LEN; + arp1394->max_rec = dev->card->max_receive; + arp1394->sspd = dev->card->link_speed; + + put_unaligned_be16(dev->local_fifo >> 32, + &arp1394->fifo_hi); + put_unaligned_be32(dev->local_fifo & 0xffffffff, + &arp1394->fifo_lo); + put_unaligned(ipaddr, &arp1394->sip); } - if ( ipv4_max_xmt && max_payload > ipv4_max_xmt ) - max_payload = ipv4_max_xmt; ptask->hdr.w0 = 0; ptask->hdr.w1 = 0; ptask->skb = skb; - ptask->priv = priv; - ptask->tx_type = tx_type; + ptask->dev = dev; + /* Does it all fit in one packet? */ - if ( dg_size <= max_payload ) { - ipv4_make_uf_hdr(&ptask->hdr, be16_to_cpu(proto)); + if (dg_size <= max_payload) { + fwnet_make_uf_hdr(&ptask->hdr, ntohs(proto)); ptask->outstanding_pkts = 1; - max_payload = dg_size + IPV4_UNFRAG_HDR_SIZE; + max_payload = dg_size + RFC2374_UNFRAG_HDR_SIZE; } else { u16 datagram_label; - max_payload -= IPV4_FRAG_OVERHEAD; + max_payload -= RFC2374_FRAG_OVERHEAD; datagram_label = (*datagram_label_ptr)++; - ipv4_make_ff_hdr(&ptask->hdr, be16_to_cpu(proto), dg_size, datagram_label ); + fwnet_make_ff_hdr(&ptask->hdr, ntohs(proto), dg_size, + datagram_label); ptask->outstanding_pkts = DIV_ROUND_UP(dg_size, max_payload); - max_payload += IPV4_FRAG_HDR_SIZE; + max_payload += RFC2374_FRAG_HDR_SIZE; } ptask->max_payload = max_payload; - ipv4_send_packet ( ptask ); + fwnet_send_packet(ptask); + return NETDEV_TX_OK; fail: if (ptask) - kmem_cache_free(ipv4_packet_task_cache, ptask); + kmem_cache_free(fwnet_packet_task_cache, ptask); if (skb != NULL) dev_kfree_skb(skb); - netdev->stats.tx_dropped++; - netdev->stats.tx_errors++; + net->stats.tx_dropped++; + net->stats.tx_errors++; /* * FIXME: According to a patch from 2003-02-26, "returning non-zero @@ -1540,280 +1418,291 @@ static int ipv4_tx(struct sk_buff *skb, struct net_device *netdev) return NETDEV_TX_OK; } -/* - * FIXME: What to do if we timeout? I think a host reset is probably in order, - * so that's what we do. Should we increment the stat counters too? - */ -static void ipv4_tx_timeout(struct net_device *dev) { - struct ipv4_priv *priv; +static void fwnet_tx_timeout(struct net_device *net) +{ + fw_error("%s: timeout\n", net->name); - priv = netdev_priv(dev); - fw_error ( "%s: Timeout, resetting host\n", dev->name ); -#if 0 /* stefanr */ - fw_core_initiate_bus_reset ( priv->card, 1 ); -#endif + /* FIXME: What to do if we timeout? */ } -static int ipv4_change_mtu ( struct net_device *dev, int new_mtu ) { -#if 0 - int max_mtu; - struct ipv4_priv *priv; -#endif - +static int fwnet_change_mtu(struct net_device *net, int new_mtu) +{ if (new_mtu < 68) return -EINVAL; -#if 0 - priv = netdev_priv(dev); - /* This is not actually true because we can fragment packets at the firewire layer */ - max_mtu = (1 << (priv->card->max_receive + 1)) - - sizeof(struct ipv4_hdr) - IPV4_GASP_OVERHEAD; - if (new_mtu > max_mtu) { - fw_notify ( "%s: Local node constrains MTU to %d\n", dev->name, max_mtu); - return -ERANGE; - } -#endif - dev->mtu = new_mtu; + net->mtu = new_mtu; return 0; } -static void ipv4_get_drvinfo(struct net_device *dev, -struct ethtool_drvinfo *info) { - strcpy(info->driver, ipv4_driver_name); - strcpy(info->bus_info, "ieee1394"); /* FIXME provide more detail? */ +static void fwnet_get_drvinfo(struct net_device *net, + struct ethtool_drvinfo *info) +{ + strcpy(info->driver, KBUILD_MODNAME); + strcpy(info->bus_info, "ieee1394"); } -static struct ethtool_ops ipv4_ethtool_ops = { - .get_drvinfo = ipv4_get_drvinfo, +static struct ethtool_ops fwnet_ethtool_ops = { + .get_drvinfo = fwnet_get_drvinfo, }; -static const struct net_device_ops ipv4_netdev_ops = { - .ndo_open = ipv4_open, - .ndo_stop = ipv4_stop, - .ndo_start_xmit = ipv4_tx, - .ndo_tx_timeout = ipv4_tx_timeout, - .ndo_change_mtu = ipv4_change_mtu, +static const struct net_device_ops fwnet_netdev_ops = { + .ndo_open = fwnet_open, + .ndo_stop = fwnet_stop, + .ndo_start_xmit = fwnet_tx, + .ndo_tx_timeout = fwnet_tx_timeout, + .ndo_change_mtu = fwnet_change_mtu, }; -static void ipv4_init_dev ( struct net_device *dev ) { - dev->header_ops = &ipv4_header_ops; - dev->netdev_ops = &ipv4_netdev_ops; - SET_ETHTOOL_OPS(dev, &ipv4_ethtool_ops); - - dev->watchdog_timeo = IPV4_TIMEOUT; - dev->flags = IFF_BROADCAST | IFF_MULTICAST; - dev->features = NETIF_F_HIGHDMA; - dev->addr_len = IPV4_ALEN; - dev->hard_header_len = IPV4_HLEN; - dev->type = ARPHRD_IEEE1394; - - /* FIXME: This value was copied from ether_setup(). Is it too much? */ - dev->tx_queue_len = 1000; +static void fwnet_init_dev(struct net_device *net) +{ + net->header_ops = &fwnet_header_ops; + net->netdev_ops = &fwnet_netdev_ops; + net->watchdog_timeo = 100000; /* ? FIXME */ + net->flags = IFF_BROADCAST | IFF_MULTICAST; + net->features = NETIF_F_HIGHDMA; + net->addr_len = FWNET_ALEN; + net->hard_header_len = FWNET_HLEN; + net->type = ARPHRD_IEEE1394; + net->tx_queue_len = 1000; /* ? FIXME */ + SET_ETHTOOL_OPS(net, &fwnet_ethtool_ops); } -static int ipv4_probe ( struct device *dev ) { - struct fw_unit * unit; - struct fw_device *device; - struct fw_card *card; - struct net_device *netdev; - struct ipv4_priv *priv; +/* FIXME create netdev upon first fw_unit of a card, not upon local fw_unit */ +static int fwnet_probe(struct device *_dev) +{ + struct fw_unit *unit = fw_unit(_dev); + struct fw_device *device = fw_parent_device(unit); + struct fw_card *card = device->card; + struct net_device *net; + struct fwnet_device *dev; unsigned max_mtu; - __be64 guid; - - fw_debug("ipv4 Probing\n" ); - unit = fw_unit ( dev ); - device = fw_device ( unit->device.parent ); - card = device->card; - if ( ! device->is_local ) { + if (!device->is_local) { int added; - fw_debug ( "Non-local, adding remote node entry\n" ); - added = ipv4_node_new ( card, device ); + added = fwnet_peer_new(card, device); return added; } - fw_debug("ipv4 Local: adding netdev\n" ); - netdev = alloc_netdev ( sizeof(*priv), "firewire%d", ipv4_init_dev ); - if ( netdev == NULL) { - fw_error( "Out of memory\n"); + net = alloc_netdev(sizeof(*dev), "firewire%d", fwnet_init_dev); + if (net == NULL) { + fw_error("out of memory\n"); goto out; } - SET_NETDEV_DEV(netdev, card->device); - priv = netdev_priv(netdev); + SET_NETDEV_DEV(net, card->device); + dev = netdev_priv(net); - spin_lock_init(&priv->lock); - priv->broadcast_state = IPV4_BROADCAST_ERROR; - priv->broadcast_rcv_context = NULL; - priv->broadcast_xmt_max_payload = 0; - priv->broadcast_xmt_datagramlabel = 0; + spin_lock_init(&dev->lock); + dev->broadcast_state = FWNET_BROADCAST_ERROR; + dev->broadcast_rcv_context = NULL; + dev->broadcast_xmt_max_payload = 0; + dev->broadcast_xmt_datagramlabel = 0; - priv->local_fifo = INVALID_FIFO_ADDR; + dev->local_fifo = FWNET_NO_FIFO_ADDR; - /* INIT_WORK(&priv->wake, ipv4_handle_queue);*/ - INIT_LIST_HEAD(&priv->packet_list); - INIT_LIST_HEAD(&priv->broadcasted_list); - INIT_LIST_HEAD(&priv->sent_list ); + /* INIT_WORK(&dev->wake, fwnet_handle_queue);*/ + INIT_LIST_HEAD(&dev->packet_list); + INIT_LIST_HEAD(&dev->broadcasted_list); + INIT_LIST_HEAD(&dev->sent_list); - priv->card = card; + dev->card = card; /* * Use the RFC 2734 default 1500 octets or the maximum payload * as initial MTU */ max_mtu = (1 << (card->max_receive + 1)) - - sizeof(struct ipv4_hdr) - IPV4_GASP_OVERHEAD; - netdev->mtu = min(1500U, max_mtu); + - sizeof(struct rfc2734_header) - IEEE1394_GASP_HDR_SIZE; + net->mtu = min(1500U, max_mtu); /* Set our hardware address while we're at it */ - guid = cpu_to_be64(card->guid); - memcpy(netdev->dev_addr, &guid, sizeof(u64)); - memset(netdev->broadcast, 0xff, sizeof(u64)); - if ( register_netdev ( netdev ) ) { - fw_error ( "Cannot register the driver\n"); + put_unaligned_be64(card->guid, net->dev_addr); + put_unaligned_be64(~0ULL, net->broadcast); + if (register_netdev(net)) { + fw_error("Cannot register the driver\n"); goto out; } - fw_notify ( "%s: IPv4 over Firewire on device %016llx\n", - netdev->name, card->guid ); - card->netdev = netdev; + fw_notify("%s: IPv4 over FireWire on device %016llx\n", + net->name, (unsigned long long)card->guid); + card->netdev = net; - return 0 /* ipv4_new_node ( ud ) */; + return 0; out: - if ( netdev ) - free_netdev ( netdev ); + if (net) + free_netdev(net); + return -ENOENT; } +static int fwnet_remove(struct device *_dev) +{ + struct fw_unit *unit = fw_unit(_dev); + struct fw_device *device = fw_parent_device(unit); + struct fw_card *card = device->card; + struct net_device *net; + struct fwnet_device *dev; + struct fwnet_peer *peer; + struct fwnet_partial_datagram *pd, *pd_next; + struct fwnet_packet_task *ptask, *pt_next; + + if (!device->is_local) { + fwnet_peer_delete(card, device); -static int ipv4_remove ( struct device *dev ) { - struct fw_unit * unit; - struct fw_device *device; - struct fw_card *card; - struct net_device *netdev; - struct ipv4_priv *priv; - struct ipv4_node *node; - struct ipv4_partial_datagram *pd, *pd_next; - struct ipv4_packet_task *ptask, *pt_next; - - fw_debug("ipv4 Removing\n" ); - unit = fw_unit ( dev ); - device = fw_device ( unit->device.parent ); - card = device->card; - - if ( ! device->is_local ) { - fw_debug ( "Node %x is non-local, removing remote node entry\n", device->node_id ); - ipv4_node_delete ( card, device ); return 0; } - netdev = card->netdev; - if ( netdev ) { - fw_debug ( "Node %x is local: deleting netdev\n", device->node_id ); - priv = netdev_priv ( netdev ); - unregister_netdev ( netdev ); - fw_debug ( "unregistered\n" ); - if ( priv->local_fifo != INVALID_FIFO_ADDR ) - fw_core_remove_address_handler ( &priv->handler ); - fw_debug ( "address handler gone\n" ); - if ( priv->broadcast_rcv_context ) { - fw_iso_context_stop ( priv->broadcast_rcv_context ); - fw_iso_buffer_destroy ( &priv->broadcast_rcv_buffer, priv->card ); - fw_iso_context_destroy ( priv->broadcast_rcv_context ); - fw_debug ( "rcv stopped\n" ); + + net = card->netdev; + if (net) { + dev = netdev_priv(net); + unregister_netdev(net); + + if (dev->local_fifo != FWNET_NO_FIFO_ADDR) + fw_core_remove_address_handler(&dev->handler); + if (dev->broadcast_rcv_context) { + fw_iso_context_stop(dev->broadcast_rcv_context); + fw_iso_buffer_destroy(&dev->broadcast_rcv_buffer, + dev->card); + fw_iso_context_destroy(dev->broadcast_rcv_context); } - list_for_each_entry_safe( ptask, pt_next, &priv->packet_list, packet_list ) { - dev_kfree_skb_any ( ptask->skb ); - kmem_cache_free( ipv4_packet_task_cache, ptask ); + list_for_each_entry_safe(ptask, pt_next, + &dev->packet_list, pt_link) { + dev_kfree_skb_any(ptask->skb); + kmem_cache_free(fwnet_packet_task_cache, ptask); } - list_for_each_entry_safe( ptask, pt_next, &priv->broadcasted_list, packet_list ) { - dev_kfree_skb_any ( ptask->skb ); - kmem_cache_free( ipv4_packet_task_cache, ptask ); + list_for_each_entry_safe(ptask, pt_next, + &dev->broadcasted_list, pt_link) { + dev_kfree_skb_any(ptask->skb); + kmem_cache_free(fwnet_packet_task_cache, ptask); } - list_for_each_entry_safe( ptask, pt_next, &priv->sent_list, packet_list ) { - dev_kfree_skb_any ( ptask->skb ); - kmem_cache_free( ipv4_packet_task_cache, ptask ); + list_for_each_entry_safe(ptask, pt_next, + &dev->sent_list, pt_link) { + dev_kfree_skb_any(ptask->skb); + kmem_cache_free(fwnet_packet_task_cache, ptask); } - fw_debug ( "lists emptied\n" ); - list_for_each_entry( node, &card->ipv4_nodes, ipv4_nodes ) { - if ( node->pdg_size ) { - list_for_each_entry_safe( pd, pd_next, &node->pdg_list, pdg_list ) - ipv4_pd_delete ( pd ); - node->pdg_size = 0; + list_for_each_entry(peer, &card->peer_list, peer_link) { + if (peer->pdg_size) { + list_for_each_entry_safe(pd, pd_next, + &peer->pd_list, pd_link) + fwnet_pd_delete(pd); + peer->pdg_size = 0; } - node->fifo = INVALID_FIFO_ADDR; + peer->fifo = FWNET_NO_FIFO_ADDR; } - fw_debug ( "nodes cleaned up\n" ); - free_netdev ( netdev ); + free_netdev(net); card->netdev = NULL; - fw_debug ( "done\n" ); } + return 0; } -static void ipv4_update ( struct fw_unit *unit ) { - struct fw_device *device; - struct fw_card *card; +/* + * FIXME abort partially sent fragmented datagrams, + * discard partially received fragmented datagrams + */ +static void fwnet_update(struct fw_unit *unit) +{ + struct fw_device *device = fw_parent_device(unit); + struct net_device *net = device->card->netdev; + struct fwnet_device *dev; + struct fwnet_peer *peer; + u64 guid; - fw_debug ( "ipv4_update unit %p\n", unit ); - device = fw_device ( unit->device.parent ); - card = device->card; - if ( ! device->is_local ) { - struct ipv4_node *node; - u64 guid; - struct net_device *netdev; - struct ipv4_priv *priv; - - netdev = card->netdev; - if ( netdev ) { - priv = netdev_priv ( netdev ); - guid = (u64)device->config_rom[3] << 32 | device->config_rom[4]; - node = ipv4_node_find_by_guid ( priv, guid ); - if ( ! node ) { - fw_error ( "ipv4_update: no node for device %llx\n", guid ); - return; - } - fw_debug ( "Non-local, updating remote node entry for guid %llx old generation %x, old nodeid %x\n", guid, node->generation, node->nodeid ); - node->generation = device->generation; - rmb(); - node->nodeid = device->node_id; - fw_debug ( "New generation %x, new nodeid %x\n", node->generation, node->nodeid ); - } else - fw_error ( "nonlocal, but no netdev? How can that be?\n" ); - } else { - /* FIXME: What do we need to do on bus reset? */ - fw_debug ( "Local, doing nothing\n" ); + if (net && !device->is_local) { + dev = netdev_priv(net); + guid = (u64)device->config_rom[3] << 32 | device->config_rom[4]; + peer = fwnet_peer_find_by_guid(dev, guid); + if (!peer) { + fw_error("fwnet_update: no peer for device %016llx\n", + (unsigned long long)guid); + return; + } + peer->generation = device->generation; + rmb(); + peer->node_id = device->node_id; } } -static struct fw_driver ipv4_driver = { +static const struct ieee1394_device_id fwnet_id_table[] = { + { + .match_flags = IEEE1394_MATCH_SPECIFIER_ID | + IEEE1394_MATCH_VERSION, + .specifier_id = IANA_SPECIFIER_ID, + .version = RFC2734_SW_VERSION, + }, + { } +}; + +static struct fw_driver fwnet_driver = { .driver = { - .owner = THIS_MODULE, - .name = ipv4_driver_name, - .bus = &fw_bus_type, - .probe = ipv4_probe, - .remove = ipv4_remove, + .owner = THIS_MODULE, + .name = "net", + .bus = &fw_bus_type, + .probe = fwnet_probe, + .remove = fwnet_remove, }, - .update = ipv4_update, - .id_table = ipv4_id_table, + .update = fwnet_update, + .id_table = fwnet_id_table, +}; + +static const u32 rfc2374_unit_directory_data[] = { + 0x00040000, /* directory_length */ + 0x1200005e, /* unit_specifier_id: IANA */ + 0x81000003, /* textual descriptor offset */ + 0x13000001, /* unit_sw_version: RFC 2734 */ + 0x81000005, /* textual descriptor offset */ + 0x00030000, /* descriptor_length */ + 0x00000000, /* text */ + 0x00000000, /* minimal ASCII, en */ + 0x49414e41, /* I A N A */ + 0x00030000, /* descriptor_length */ + 0x00000000, /* text */ + 0x00000000, /* minimal ASCII, en */ + 0x49507634, /* I P v 4 */ +}; + +static struct fw_descriptor rfc2374_unit_directory = { + .length = ARRAY_SIZE(rfc2374_unit_directory_data), + .key = (CSR_DIRECTORY | CSR_UNIT) << 24, + .data = rfc2374_unit_directory_data }; -static int __init ipv4_init ( void ) { - int added; +static int __init fwnet_init(void) +{ + int err; + + err = fw_core_add_descriptor(&rfc2374_unit_directory); + if (err) + return err; - added = fw_core_add_descriptor ( &ipv4_unit_directory ); - if ( added < 0 ) - fw_error ( "Failed to add descriptor" ); - ipv4_packet_task_cache = kmem_cache_create("packet_task", - sizeof(struct ipv4_packet_task), 0, 0, NULL); - fw_debug("Adding ipv4 module\n" ); - return driver_register ( &ipv4_driver.driver ); + fwnet_packet_task_cache = kmem_cache_create("packet_task", + sizeof(struct fwnet_packet_task), 0, 0, NULL); + if (!fwnet_packet_task_cache) { + err = -ENOMEM; + goto out; + } + + err = driver_register(&fwnet_driver.driver); + if (!err) + return 0; + + kmem_cache_destroy(fwnet_packet_task_cache); +out: + fw_core_remove_descriptor(&rfc2374_unit_directory); + + return err; } +module_init(fwnet_init); -static void __exit ipv4_cleanup ( void ) { - fw_core_remove_descriptor ( &ipv4_unit_directory ); - fw_debug("Removing ipv4 module\n" ); - driver_unregister ( &ipv4_driver.driver ); +static void __exit fwnet_cleanup(void) +{ + driver_unregister(&fwnet_driver.driver); + kmem_cache_destroy(fwnet_packet_task_cache); + fw_core_remove_descriptor(&rfc2374_unit_directory); } +module_exit(fwnet_cleanup); -module_init(ipv4_init); -module_exit(ipv4_cleanup); +MODULE_AUTHOR("Jay Fenlason "); +MODULE_DESCRIPTION("IPv4 over IEEE1394 as per RFC 2734"); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(ieee1394, fwnet_id_table); diff --git a/include/linux/firewire.h b/include/linux/firewire.h index d44f47d3b2d..5cb0c1549ff 100644 --- a/include/linux/firewire.h +++ b/include/linux/firewire.h @@ -131,13 +131,10 @@ struct fw_card { bool broadcast_channel_allocated; u32 broadcast_channel; u32 topology_map[(CSR_TOPOLOGY_MAP_END - CSR_TOPOLOGY_MAP) / 4]; - /* Only non-NULL if firewire-ipv4 is active on this card. */ + + /* firewire-net driver data */ void *netdev; - /* - * The nodes get probed before the card, so we need a place to store - * them independent of card->netdev - */ - struct list_head ipv4_nodes; + struct list_head peer_list; }; static inline struct fw_card *fw_card_get(struct fw_card *card) -- cgit v1.2.3-70-g09d2 From 5a124d382ea5c97be43c779e4f481455e0287654 Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Sun, 14 Jun 2009 11:45:27 +0200 Subject: firewire: net: allow for unordered unit discovery Decouple the creation and destruction of the net_device from the order of discovery and removal of nodes with RFC 2734 unit directories since there is no reliable order. The net_device is now created when the first RFC 2734 unit on a card is discovered, and destroyed when the last RFC 2734 unit on a card went away. This includes all remote units as well as the local unit, which is therefore tracked as a peer now too. Also, locking around the list of peers is slightly extended to guard against peer removal. As a side effect, fwnet_peer.pdg_lock has become superfluous and is deleted. Peer data (max_rec, speed, node ID, generation) are updated more carefully. Signed-off-by: Stefan Richter --- drivers/firewire/core-card.c | 2 - drivers/firewire/net.c | 454 ++++++++++++++++++++----------------------- include/linux/firewire.h | 4 - 3 files changed, 207 insertions(+), 253 deletions(-) (limited to 'drivers/firewire/core-card.c') diff --git a/drivers/firewire/core-card.c b/drivers/firewire/core-card.c index 8c45e43da7c..667603ac14b 100644 --- a/drivers/firewire/core-card.c +++ b/drivers/firewire/core-card.c @@ -429,8 +429,6 @@ void fw_card_initialize(struct fw_card *card, card->local_node = NULL; INIT_DELAYED_WORK(&card->work, fw_card_bm_work); - card->netdev = NULL; - INIT_LIST_HEAD(&card->peer_list); } EXPORT_SYMBOL(fw_card_initialize); diff --git a/drivers/firewire/net.c b/drivers/firewire/net.c index ba6f924b1b1..d83c54587a6 100644 --- a/drivers/firewire/net.c +++ b/drivers/firewire/net.c @@ -18,8 +18,10 @@ #include #include #include +#include #include #include +#include #include #include @@ -135,40 +137,11 @@ struct fwnet_partial_datagram { u16 datagram_size; }; -/* - * We keep one of these for each IPv4 capable device attached to a fw_card. - * The list of them is stored in the fw_card structure rather than in the - * fwnet_device because the remote IPv4 nodes may be probed before the card is, - * so we need a place to store them before the fwnet_device structure is - * allocated. - */ -struct fwnet_peer { - struct list_head peer_link; - /* guid of the remote peer */ - u64 guid; - /* FIFO address to transmit datagrams to, or FWNET_NO_FIFO_ADDR */ - u64 fifo; - - spinlock_t pdg_lock; /* partial datagram lock */ - /* List of partial datagrams received from this peer */ - struct list_head pd_list; - /* Number of entries in pd_list at the moment */ - unsigned pdg_size; - - /* max payload to transmit to this remote peer */ - /* This already includes the RFC2374_FRAG_HDR_SIZE overhead */ - u16 max_payload; - /* outgoing datagram label */ - u16 datagram_label; - /* Current node_id of the remote peer */ - u16 node_id; - /* current generation of the remote peer */ - u8 generation; - /* max speed that this peer can receive at */ - u8 xmt_speed; -}; +static DEFINE_MUTEX(fwnet_device_mutex); +static LIST_HEAD(fwnet_device_list); struct fwnet_device { + struct list_head dev_link; spinlock_t lock; enum { FWNET_BROADCAST_ERROR, @@ -206,7 +179,26 @@ struct fwnet_device { /* List of packets that have been sent but not yet acked */ struct list_head sent_list; + struct list_head peer_list; struct fw_card *card; + struct net_device *netdev; +}; + +struct fwnet_peer { + struct list_head peer_link; + struct fwnet_device *dev; + u64 guid; + u64 fifo; + + /* guarded by dev->lock */ + struct list_head pd_list; /* received partial datagrams */ + unsigned pdg_size; /* pd_list size */ + + u16 datagram_label; /* outgoing datagram label */ + unsigned max_payload; /* includes RFC2374_FRAG_HDR_SIZE overhead */ + int node_id; + int generation; + unsigned speed; }; /* This is our task struct. It's used for the packet complete callback. */ @@ -479,102 +471,47 @@ static bool fwnet_pd_is_complete(struct fwnet_partial_datagram *pd) return fi->len == pd->datagram_size; } -static int fwnet_peer_new(struct fw_card *card, struct fw_device *device) -{ - struct fwnet_peer *peer; - - peer = kmalloc(sizeof(*peer), GFP_KERNEL); - if (!peer) { - fw_error("out of memory\n"); - - return -ENOMEM; - } - peer->guid = (u64)device->config_rom[3] << 32 | device->config_rom[4]; - peer->fifo = FWNET_NO_FIFO_ADDR; - INIT_LIST_HEAD(&peer->pd_list); - spin_lock_init(&peer->pdg_lock); - peer->pdg_size = 0; - peer->generation = device->generation; - rmb(); - peer->node_id = device->node_id; - /* FIXME what should it really be? */ - peer->max_payload = IEEE1394_MAX_PAYLOAD_S100 - RFC2374_UNFRAG_HDR_SIZE; - peer->datagram_label = 0U; - peer->xmt_speed = device->max_speed; - list_add_tail(&peer->peer_link, &card->peer_list); - - return 0; -} - -/* FIXME caller must take the lock, or peer needs to be reference-counted */ +/* caller must hold dev->lock */ static struct fwnet_peer *fwnet_peer_find_by_guid(struct fwnet_device *dev, u64 guid) { - struct fwnet_peer *p, *peer = NULL; - unsigned long flags; + struct fwnet_peer *peer; - spin_lock_irqsave(&dev->lock, flags); - list_for_each_entry(p, &dev->card->peer_list, peer_link) - if (p->guid == guid) { - peer = p; - break; - } - spin_unlock_irqrestore(&dev->lock, flags); + list_for_each_entry(peer, &dev->peer_list, peer_link) + if (peer->guid == guid) + return peer; - return peer; + return NULL; } -/* FIXME caller must take the lock, or peer needs to be reference-counted */ -/* FIXME node_id doesn't mean anything without generation */ +/* caller must hold dev->lock */ static struct fwnet_peer *fwnet_peer_find_by_node_id(struct fwnet_device *dev, - u16 node_id) + int node_id, int generation) { - struct fwnet_peer *p, *peer = NULL; - unsigned long flags; + struct fwnet_peer *peer; - spin_lock_irqsave(&dev->lock, flags); - list_for_each_entry(p, &dev->card->peer_list, peer_link) - if (p->node_id == node_id) { - peer = p; - break; - } - spin_unlock_irqrestore(&dev->lock, flags); + list_for_each_entry(peer, &dev->peer_list, peer_link) + if (peer->node_id == node_id && + peer->generation == generation) + return peer; - return peer; + return NULL; } -/* FIXME */ -static void fwnet_peer_delete(struct fw_card *card, struct fw_device *device) +/* See IEEE 1394-2008 table 6-4, table 8-8, table 16-18. */ +static unsigned fwnet_max_payload(unsigned max_rec, unsigned speed) { - struct net_device *net; - struct fwnet_device *dev; - struct fwnet_peer *peer; - u64 guid; - unsigned long flags; - struct fwnet_partial_datagram *pd, *pd_next; - - guid = (u64)device->config_rom[3] << 32 | device->config_rom[4]; - net = card->netdev; - if (net) - dev = netdev_priv(net); - else - dev = NULL; - if (dev) - spin_lock_irqsave(&dev->lock, flags); - - list_for_each_entry(peer, &card->peer_list, peer_link) { - if (peer->guid == guid) { - list_del(&peer->peer_link); - list_for_each_entry_safe(pd, pd_next, &peer->pd_list, - pd_link) - fwnet_pd_delete(pd); - break; - } + max_rec = min(max_rec, speed + 8); + max_rec = min(max_rec, 0xbU); /* <= 4096 */ + if (max_rec < 8) { + fw_notify("max_rec %x out of range\n", max_rec); + max_rec = 8; } - if (dev) - spin_unlock_irqrestore(&dev->lock, flags); + + return (1 << (max_rec + 1)) - RFC2374_FRAG_HDR_SIZE; } + static int fwnet_finish_incoming_packet(struct net_device *net, struct sk_buff *skb, u16 source_node_id, bool is_broadcast, u16 ether_type) @@ -606,71 +543,44 @@ static int fwnet_finish_incoming_packet(struct net_device *net, unsigned char *arp_ptr; u64 fifo_addr; u64 peer_guid; - u8 max_rec; - u8 sspd; + unsigned sspd; u16 max_payload; struct fwnet_peer *peer; - static const u16 fwnet_speed_to_max_payload[] = { - /* S100, S200, S400, S800, S1600, S3200 */ - 512, 1024, 2048, 4096, 4096, 4096 - }; + unsigned long flags; + + arp1394 = (struct rfc2734_arp *)skb->data; + arp = (struct arphdr *)skb->data; + arp_ptr = (unsigned char *)(arp + 1); + peer_guid = get_unaligned_be64(&arp1394->s_uniq_id); + fifo_addr = (u64)get_unaligned_be16(&arp1394->fifo_hi) << 32 + | get_unaligned_be32(&arp1394->fifo_lo); - arp1394 = (struct rfc2734_arp *)skb->data; - arp = (struct arphdr *)skb->data; - arp_ptr = (unsigned char *)(arp + 1); - fifo_addr = (u64)ntohs(arp1394->fifo_hi) << 32 - | ntohl(arp1394->fifo_lo); - max_rec = dev->card->max_receive; - if (arp1394->max_rec < max_rec) - max_rec = arp1394->max_rec; sspd = arp1394->sspd; /* Sanity check. OS X 10.3 PPC reportedly sends 131. */ if (sspd > SCODE_3200) { fw_notify("sspd %x out of range\n", sspd); - sspd = 0; + sspd = SCODE_3200; } + max_payload = fwnet_max_payload(arp1394->max_rec, sspd); - max_payload = min(fwnet_speed_to_max_payload[sspd], - (u16)(1 << (max_rec + 1))) - RFC2374_UNFRAG_HDR_SIZE; - - peer_guid = get_unaligned_be64(&arp1394->s_uniq_id); + spin_lock_irqsave(&dev->lock, flags); peer = fwnet_peer_find_by_guid(dev, peer_guid); + if (peer) { + peer->fifo = fifo_addr; + + if (peer->speed > sspd) + peer->speed = sspd; + if (peer->max_payload > max_payload) + peer->max_payload = max_payload; + } + spin_unlock_irqrestore(&dev->lock, flags); + if (!peer) { fw_notify("No peer for ARP packet from %016llx\n", (unsigned long long)peer_guid); goto failed_proto; } - /* FIXME don't use card->generation */ - if (peer->node_id != source_node_id || - peer->generation != dev->card->generation) { - fw_notify("Internal error: peer->node_id (%x) != " - "source_node_id (%x) or peer->generation (%x)" - " != dev->card->generation(%x)\n", - peer->node_id, source_node_id, - peer->generation, dev->card->generation); - peer->node_id = source_node_id; - peer->generation = dev->card->generation; - } - - /* FIXME: for debugging */ - if (sspd > SCODE_400) - sspd = SCODE_400; - /* Update our speed/payload/fifo_offset table */ - /* - * FIXME: this does not handle cases where two high-speed endpoints must use a slower speed because of - * a lower speed hub between them. We need to look at the actual topology map here. - */ - peer->fifo = fifo_addr; - peer->max_payload = max_payload; - /* - * Only allow speeds to go down from their initial value. - * Otherwise a local peer that can only do S400 or slower may - * be told to transmit at S800 to a faster remote peer. - */ - if (peer->xmt_speed > sspd) - peer->xmt_speed = sspd; - /* * Now that we're done with the 1394 specific stuff, we'll * need to alter some of the data. Believe it or not, all @@ -764,10 +674,11 @@ static int fwnet_finish_incoming_packet(struct net_device *net, } static int fwnet_incoming_packet(struct fwnet_device *dev, __be32 *buf, int len, - u16 source_node_id, bool is_broadcast) + int source_node_id, int generation, + bool is_broadcast) { struct sk_buff *skb; - struct net_device *net; + struct net_device *net = dev->netdev; struct rfc2734_header hdr; unsigned lf; unsigned long flags; @@ -779,8 +690,6 @@ static int fwnet_incoming_packet(struct fwnet_device *dev, __be32 *buf, int len, int retval; u16 ether_type; - net = dev->card->netdev; - hdr.w0 = be32_to_cpu(buf[0]); lf = fwnet_get_hdr_lf(&hdr); if (lf == RFC2374_HDR_UNFRAG) { @@ -819,9 +728,12 @@ static int fwnet_incoming_packet(struct fwnet_device *dev, __be32 *buf, int len, } datagram_label = fwnet_get_hdr_dgl(&hdr); dg_size = fwnet_get_hdr_dg_size(&hdr); /* ??? + 1 */ - peer = fwnet_peer_find_by_node_id(dev, source_node_id); - spin_lock_irqsave(&peer->pdg_lock, flags); + spin_lock_irqsave(&dev->lock, flags); + + peer = fwnet_peer_find_by_node_id(dev, source_node_id, generation); + if (!peer) + goto bad_proto; pd = fwnet_pd_find(peer, datagram_label); if (pd == NULL) { @@ -876,7 +788,7 @@ static int fwnet_incoming_packet(struct fwnet_device *dev, __be32 *buf, int len, skb = skb_get(pd->skb); fwnet_pd_delete(pd); - spin_unlock_irqrestore(&peer->pdg_lock, flags); + spin_unlock_irqrestore(&dev->lock, flags); return fwnet_finish_incoming_packet(net, skb, source_node_id, false, ether_type); @@ -885,12 +797,12 @@ static int fwnet_incoming_packet(struct fwnet_device *dev, __be32 *buf, int len, * Datagram is not complete, we're done for the * moment. */ - spin_unlock_irqrestore(&peer->pdg_lock, flags); + spin_unlock_irqrestore(&dev->lock, flags); return 0; bad_proto: - spin_unlock_irqrestore(&peer->pdg_lock, flags); + spin_unlock_irqrestore(&dev->lock, flags); if (netif_queue_stopped(net)) netif_wake_queue(net); @@ -916,7 +828,8 @@ static void fwnet_receive_packet(struct fw_card *card, struct fw_request *r, return; } - status = fwnet_incoming_packet(dev, payload, length, source, false); + status = fwnet_incoming_packet(dev, payload, length, + source, generation, false); if (status != 0) { fw_error("Incoming packet failure\n"); fw_send_response(card, r, RCODE_CONFLICT_ERROR); @@ -966,7 +879,7 @@ static void fwnet_receive_broadcast(struct fw_iso_context *context, buf_ptr += 2; length -= IEEE1394_GASP_HDR_SIZE; fwnet_incoming_packet(dev, buf_ptr, length, - source_node_id, true); + source_node_id, -1, true); } packet.payload_length = dev->rcv_buffer_size; @@ -1073,7 +986,6 @@ static int fwnet_send_packet(struct fwnet_packet_task *ptask) unsigned tx_len; struct rfc2734_header *bufhdr; unsigned long flags; - struct net_device *net; dev = ptask->dev; tx_len = ptask->max_payload; @@ -1137,8 +1049,7 @@ static int fwnet_send_packet(struct fwnet_packet_task *ptask) list_add_tail(&ptask->pt_link, &dev->sent_list); spin_unlock_irqrestore(&dev->lock, flags); - net = dev->card->netdev; - net->trans_start = jiffies; + dev->netdev->trans_start = jiffies; return 0; } @@ -1294,7 +1205,8 @@ static int fwnet_tx(struct sk_buff *skb, struct net_device *net) u16 dg_size; u16 *datagram_label_ptr; struct fwnet_packet_task *ptask; - struct fwnet_peer *peer = NULL; + struct fwnet_peer *peer; + unsigned long flags; ptask = kmem_cache_alloc(fwnet_packet_task_cache, GFP_ATOMIC); if (ptask == NULL) @@ -1314,6 +1226,9 @@ static int fwnet_tx(struct sk_buff *skb, struct net_device *net) proto = hdr_buf.h_proto; dg_size = skb->len; + /* serialize access to peer, including peer->datagram_label */ + spin_lock_irqsave(&dev->lock, flags); + /* * Set the transmission type for the packet. ARP packets and IP * broadcast packets are sent via GASP. @@ -1322,35 +1237,30 @@ static int fwnet_tx(struct sk_buff *skb, struct net_device *net) || proto == htons(ETH_P_ARP) || (proto == htons(ETH_P_IP) && IN_MULTICAST(ntohl(ip_hdr(skb)->daddr)))) { - max_payload = dev->broadcast_xmt_max_payload; + max_payload = dev->broadcast_xmt_max_payload; datagram_label_ptr = &dev->broadcast_xmt_datagramlabel; - ptask->fifo_addr = FWNET_NO_FIFO_ADDR; - ptask->generation = 0; - ptask->dest_node = IEEE1394_ALL_NODES; - ptask->speed = SCODE_100; + ptask->fifo_addr = FWNET_NO_FIFO_ADDR; + ptask->generation = 0; + ptask->dest_node = IEEE1394_ALL_NODES; + ptask->speed = SCODE_100; } else { __be64 guid = get_unaligned((__be64 *)hdr_buf.h_dest); u8 generation; peer = fwnet_peer_find_by_guid(dev, be64_to_cpu(guid)); - if (!peer) - goto fail; - - if (peer->fifo == FWNET_NO_FIFO_ADDR) - goto fail; + if (!peer || peer->fifo == FWNET_NO_FIFO_ADDR) + goto fail_unlock; - generation = peer->generation; - smp_rmb(); - dest_node = peer->node_id; - - max_payload = peer->max_payload; + generation = peer->generation; + dest_node = peer->node_id; + max_payload = peer->max_payload; datagram_label_ptr = &peer->datagram_label; - ptask->fifo_addr = peer->fifo; - ptask->generation = generation; - ptask->dest_node = dest_node; - ptask->speed = peer->xmt_speed; + ptask->fifo_addr = peer->fifo; + ptask->generation = generation; + ptask->dest_node = dest_node; + ptask->speed = peer->speed; } /* If this is an ARP packet, convert it */ @@ -1393,11 +1303,16 @@ static int fwnet_tx(struct sk_buff *skb, struct net_device *net) ptask->outstanding_pkts = DIV_ROUND_UP(dg_size, max_payload); max_payload += RFC2374_FRAG_HDR_SIZE; } + + spin_unlock_irqrestore(&dev->lock, flags); + ptask->max_payload = max_payload; fwnet_send_packet(ptask); return NETDEV_TX_OK; + fail_unlock: + spin_unlock_irqrestore(&dev->lock, flags); fail: if (ptask) kmem_cache_free(fwnet_packet_task_cache, ptask); @@ -1467,7 +1382,48 @@ static void fwnet_init_dev(struct net_device *net) SET_ETHTOOL_OPS(net, &fwnet_ethtool_ops); } -/* FIXME create netdev upon first fw_unit of a card, not upon local fw_unit */ +/* caller must hold fwnet_device_mutex */ +static struct fwnet_device *fwnet_dev_find(struct fw_card *card) +{ + struct fwnet_device *dev; + + list_for_each_entry(dev, &fwnet_device_list, dev_link) + if (dev->card == card) + return dev; + + return NULL; +} + +static int fwnet_add_peer(struct fwnet_device *dev, + struct fw_unit *unit, struct fw_device *device) +{ + struct fwnet_peer *peer; + + peer = kmalloc(sizeof(*peer), GFP_KERNEL); + if (!peer) + return -ENOMEM; + + unit->device.driver_data = peer; + peer->dev = dev; + peer->guid = (u64)device->config_rom[3] << 32 | device->config_rom[4]; + peer->fifo = FWNET_NO_FIFO_ADDR; + INIT_LIST_HEAD(&peer->pd_list); + peer->pdg_size = 0; + peer->datagram_label = 0; + peer->speed = device->max_speed; + peer->max_payload = fwnet_max_payload(device->max_rec, peer->speed); + + peer->generation = device->generation; + smp_rmb(); + peer->node_id = device->node_id; + + spin_lock_irq(&dev->lock); + list_add_tail(&peer->peer_link, &dev->peer_list); + spin_unlock_irq(&dev->lock); + + return 0; +} + static int fwnet_probe(struct device *_dev) { struct fw_unit *unit = fw_unit(_dev); @@ -1476,16 +1432,22 @@ static int fwnet_probe(struct device *_dev) struct net_device *net; struct fwnet_device *dev; unsigned max_mtu; + bool new_netdev; + int ret; - if (!device->is_local) { - int added; + mutex_lock(&fwnet_device_mutex); - added = fwnet_peer_new(card, device); - return added; + dev = fwnet_dev_find(card); + if (dev) { + new_netdev = false; + net = dev->netdev; + goto have_dev; } + + new_netdev = true; net = alloc_netdev(sizeof(*dev), "firewire%d", fwnet_init_dev); if (net == NULL) { - fw_error("out of memory\n"); + ret = -ENOMEM; goto out; } @@ -1500,12 +1462,13 @@ static int fwnet_probe(struct device *_dev) dev->local_fifo = FWNET_NO_FIFO_ADDR; - /* INIT_WORK(&dev->wake, fwnet_handle_queue);*/ INIT_LIST_HEAD(&dev->packet_list); INIT_LIST_HEAD(&dev->broadcasted_list); INIT_LIST_HEAD(&dev->sent_list); + INIT_LIST_HEAD(&dev->peer_list); dev->card = card; + dev->netdev = net; /* * Use the RFC 2734 default 1500 octets or the maximum payload @@ -1518,43 +1481,57 @@ static int fwnet_probe(struct device *_dev) /* Set our hardware address while we're at it */ put_unaligned_be64(card->guid, net->dev_addr); put_unaligned_be64(~0ULL, net->broadcast); - if (register_netdev(net)) { + ret = register_netdev(net); + if (ret) { fw_error("Cannot register the driver\n"); goto out; } + list_add_tail(&dev->dev_link, &fwnet_device_list); fw_notify("%s: IPv4 over FireWire on device %016llx\n", net->name, (unsigned long long)card->guid); - card->netdev = net; - - return 0; + have_dev: + ret = fwnet_add_peer(dev, unit, device); + if (ret && new_netdev) { + unregister_netdev(net); + list_del(&dev->dev_link); + } out: - if (net) + if (ret && new_netdev) free_netdev(net); - return -ENOENT; + mutex_unlock(&fwnet_device_mutex); + + return ret; +} + +static void fwnet_remove_peer(struct fwnet_peer *peer) +{ + struct fwnet_partial_datagram *pd, *pd_next; + + spin_lock_irq(&peer->dev->lock); + list_del(&peer->peer_link); + spin_unlock_irq(&peer->dev->lock); + + list_for_each_entry_safe(pd, pd_next, &peer->pd_list, pd_link) + fwnet_pd_delete(pd); + + kfree(peer); } static int fwnet_remove(struct device *_dev) { - struct fw_unit *unit = fw_unit(_dev); - struct fw_device *device = fw_parent_device(unit); - struct fw_card *card = device->card; + struct fwnet_peer *peer = _dev->driver_data; + struct fwnet_device *dev = peer->dev; struct net_device *net; - struct fwnet_device *dev; - struct fwnet_peer *peer; - struct fwnet_partial_datagram *pd, *pd_next; struct fwnet_packet_task *ptask, *pt_next; - if (!device->is_local) { - fwnet_peer_delete(card, device); + mutex_lock(&fwnet_device_mutex); - return 0; - } + fwnet_remove_peer(peer); - net = card->netdev; - if (net) { - dev = netdev_priv(net); + if (list_empty(&dev->peer_list)) { + net = dev->netdev; unregister_netdev(net); if (dev->local_fifo != FWNET_NO_FIFO_ADDR) @@ -1580,19 +1557,11 @@ static int fwnet_remove(struct device *_dev) dev_kfree_skb_any(ptask->skb); kmem_cache_free(fwnet_packet_task_cache, ptask); } - list_for_each_entry(peer, &card->peer_list, peer_link) { - if (peer->pdg_size) { - list_for_each_entry_safe(pd, pd_next, - &peer->pd_list, pd_link) - fwnet_pd_delete(pd); - peer->pdg_size = 0; - } - peer->fifo = FWNET_NO_FIFO_ADDR; - } free_netdev(net); - card->netdev = NULL; } + mutex_unlock(&fwnet_device_mutex); + return 0; } @@ -1603,24 +1572,15 @@ static int fwnet_remove(struct device *_dev) static void fwnet_update(struct fw_unit *unit) { struct fw_device *device = fw_parent_device(unit); - struct net_device *net = device->card->netdev; - struct fwnet_device *dev; - struct fwnet_peer *peer; - u64 guid; + struct fwnet_peer *peer = unit->device.driver_data; + int generation; - if (net && !device->is_local) { - dev = netdev_priv(net); - guid = (u64)device->config_rom[3] << 32 | device->config_rom[4]; - peer = fwnet_peer_find_by_guid(dev, guid); - if (!peer) { - fw_error("fwnet_update: no peer for device %016llx\n", - (unsigned long long)guid); - return; - } - peer->generation = device->generation; - rmb(); - peer->node_id = device->node_id; - } + generation = device->generation; + + spin_lock_irq(&peer->dev->lock); + peer->node_id = device->node_id; + peer->generation = generation; + spin_unlock_irq(&peer->dev->lock); } static const struct ieee1394_device_id fwnet_id_table[] = { diff --git a/include/linux/firewire.h b/include/linux/firewire.h index 5cb0c1549ff..9823946adbc 100644 --- a/include/linux/firewire.h +++ b/include/linux/firewire.h @@ -131,10 +131,6 @@ struct fw_card { bool broadcast_channel_allocated; u32 broadcast_channel; u32 topology_map[(CSR_TOPOLOGY_MAP_END - CSR_TOPOLOGY_MAP) / 4]; - - /* firewire-net driver data */ - void *netdev; - struct list_head peer_list; }; static inline struct fw_card *fw_card_get(struct fw_card *card) -- cgit v1.2.3-70-g09d2 From d645f4dad056a98089df904294f66b96d04e91b6 Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Tue, 16 Jun 2009 19:15:25 +0200 Subject: firewire: core: fix iso context shutdown on card removal If isochronous contexts existed when firewire-ohci was unloaded, the core iso shutdown functions crashed with NULL dereferences, and buffers etc. weren't released. How the fix works: We first copy the card driver's iso shutdown hooks into the dummy driver, then fw_destroy_nodes notifies upper layers of devices going away, these should shut down (including their iso contexts), wait_for_completion(&card->done) will be triggered after upper layers gave up all fw_device references, after which the card driver's shutdown proceeds. Signed-off-by: Stefan Richter --- drivers/firewire/core-card.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) (limited to 'drivers/firewire/core-card.c') diff --git a/drivers/firewire/core-card.c b/drivers/firewire/core-card.c index 667603ac14b..543fccac81b 100644 --- a/drivers/firewire/core-card.c +++ b/drivers/firewire/core-card.c @@ -461,11 +461,11 @@ 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. + * The next few functions implement a dummy driver that is used 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 (and failed) by the dummy driver + * instead of calling into the module. Only functions for iso context + * shutdown still need to be provided by the card driver. */ static int dummy_enable(struct fw_card *card, u32 *config_rom, size_t length) @@ -512,7 +512,7 @@ static int dummy_enable_phys_dma(struct fw_card *card, return -ENODEV; } -static struct fw_card_driver dummy_driver = { +static const struct fw_card_driver dummy_driver_template = { .enable = dummy_enable, .update_phy_reg = dummy_update_phy_reg, .set_config_rom = dummy_set_config_rom, @@ -531,6 +531,8 @@ void fw_card_release(struct kref *kref) void fw_core_remove_card(struct fw_card *card) { + struct fw_card_driver dummy_driver = dummy_driver_template; + card->driver->update_phy_reg(card, 4, PHY_LINK_ACTIVE | PHY_CONTENDER, 0); fw_core_initiate_bus_reset(card, 1); @@ -539,7 +541,9 @@ void fw_core_remove_card(struct fw_card *card) list_del_init(&card->link); mutex_unlock(&card_mutex); - /* Set up the dummy driver. */ + /* Switch off most of the card driver interface. */ + dummy_driver.free_iso_context = card->driver->free_iso_context; + dummy_driver.stop_iso = card->driver->stop_iso; card->driver = &dummy_driver; fw_destroy_nodes(card); -- cgit v1.2.3-70-g09d2