2007-05-08 02:33:32 +02:00
|
|
|
/*
|
|
|
|
* Core IEEE1394 transaction logic
|
2006-12-20 01:58:27 +01:00
|
|
|
*
|
|
|
|
* Copyright (C) 2004-2006 Kristian Hoegsberg <krh@bitplanet.net>
|
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
2009-06-04 21:09:38 +02:00
|
|
|
#include <linux/bug.h>
|
2008-03-20 23:48:23 +01:00
|
|
|
#include <linux/completion.h>
|
2009-06-04 21:09:38 +02:00
|
|
|
#include <linux/device.h>
|
|
|
|
#include <linux/errno.h>
|
firewire: reorganize header files
The three header files of firewire-core, i.e.
"drivers/firewire/fw-device.h",
"drivers/firewire/fw-topology.h",
"drivers/firewire/fw-transaction.h",
are replaced by
"drivers/firewire/core.h",
"include/linux/firewire.h".
The latter includes everything which a firewire high-level driver (like
firewire-sbp2) needs besides linux/firewire-constants.h, while core.h
contains the rest which is needed by firewire-core itself and by low-
level drivers (card drivers) like firewire-ohci.
High-level drivers can now also reside outside of drivers/firewire
without having to add drivers/firewire to the header file search path in
makefiles. At least the firedtv driver will be such a driver.
I also considered to spread the contents of core.h over several files,
one for each .c file where the respective implementation resides. But
it turned out that most core .c files will end up including most of the
core .h files. Also, the combined core.h isn't unreasonably big, and it
will lose more of its contents to linux/firewire.h anyway soon when more
firewire drivers are added. (IP-over-1394, firedtv, and there are plans
for one or two more.)
Furthermore, fw-ohci.h is renamed to ohci.h. The name of core.h and
ohci.h is chosen with regard to name changes of the .c files in a
follow-up change.
Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
2009-06-05 16:26:18 +02:00
|
|
|
#include <linux/firewire.h>
|
2009-06-04 21:09:38 +02:00
|
|
|
#include <linux/firewire-constants.h>
|
|
|
|
#include <linux/fs.h>
|
|
|
|
#include <linux/init.h>
|
2008-11-24 20:40:00 +01:00
|
|
|
#include <linux/idr.h>
|
2009-06-04 21:09:38 +02:00
|
|
|
#include <linux/jiffies.h>
|
2006-12-20 01:58:27 +01:00
|
|
|
#include <linux/kernel.h>
|
|
|
|
#include <linux/list.h>
|
2009-06-04 21:09:38 +02:00
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/slab.h>
|
|
|
|
#include <linux/spinlock.h>
|
|
|
|
#include <linux/string.h>
|
|
|
|
#include <linux/timer.h>
|
|
|
|
#include <linux/types.h>
|
firewire: core: use non-reentrant workqueue with rescuer
firewire-core manages the following types of work items:
fw_card.br_work:
- resets the bus on a card and possibly sends a PHY packet before that
- does not sleep for long or not at all
- is scheduled via fw_schedule_bus_reset() by
- firewire-ohci's pci_probe method
- firewire-ohci's set_config_rom method, called by kernelspace
protocol drivers and userspace drivers which add/remove
Configuration ROM descriptors
- userspace drivers which use the bus reset ioctl
- itself if the last reset happened less than 2 seconds ago
fw_card.bm_work:
- performs bus management duties
- usually does not (but may in corner cases) sleep for long
- is scheduled via fw_schedule_bm_work() by
- firewire-ohci's self-ID-complete IRQ handler tasklet
- firewire-core's fw_device.work instances whenever the root node
device was (successfully or unsuccessfully) discovered,
refreshed, or rediscovered
- itself in case of resource allocation failures or in order to
obey the 125ms bus manager arbitration interval
fw_device.work:
- performs node probe, update, shutdown, revival, removal; including
kernel driver probe, update, shutdown and bus reset notification to
userspace drivers
- usually sleeps moderately long, in corner cases very long
- is scheduled by
- firewire-ohci's self-ID-complete IRQ handler tasklet via the
core's fw_node_event
- firewire-ohci's pci_remove method via core's fw_destroy_nodes/
fw_node_event
- itself during retries, e.g. while a node is powering up
iso_resource.work:
- accesses registers at the Isochronous Resource Manager node
- usually does not (but may in corner cases) sleep for long
- is scheduled via schedule_iso_resource() by
- the owning userspace driver at addition and removal of the
resource
- firewire-core's fw_device.work instances after bus reset
- itself in case of resource allocation if necessary to obey the
1000ms reallocation period after bus reset
fw_card.br_work instances should not, and instances of the others must
not, be executed in parallel by multiple CPUs -- but were not protected
against that. Hence allocate a non-reentrant workqueue for them.
fw_device.work may be used in the memory reclaim path in case of SBP-2
device updates. Hence we need a workqueue with rescuer and cannot use
system_nrt_wq.
Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
Reviewed-by: Tejun Heo <tj@kernel.org>
2010-10-13 13:39:46 +02:00
|
|
|
#include <linux/workqueue.h>
|
2009-06-04 21:09:38 +02:00
|
|
|
|
|
|
|
#include <asm/byteorder.h>
|
2006-12-20 01:58:27 +01:00
|
|
|
|
firewire: reorganize header files
The three header files of firewire-core, i.e.
"drivers/firewire/fw-device.h",
"drivers/firewire/fw-topology.h",
"drivers/firewire/fw-transaction.h",
are replaced by
"drivers/firewire/core.h",
"include/linux/firewire.h".
The latter includes everything which a firewire high-level driver (like
firewire-sbp2) needs besides linux/firewire-constants.h, while core.h
contains the rest which is needed by firewire-core itself and by low-
level drivers (card drivers) like firewire-ohci.
High-level drivers can now also reside outside of drivers/firewire
without having to add drivers/firewire to the header file search path in
makefiles. At least the firedtv driver will be such a driver.
I also considered to spread the contents of core.h over several files,
one for each .c file where the respective implementation resides. But
it turned out that most core .c files will end up including most of the
core .h files. Also, the combined core.h isn't unreasonably big, and it
will lose more of its contents to linux/firewire.h anyway soon when more
firewire drivers are added. (IP-over-1394, firedtv, and there are plans
for one or two more.)
Furthermore, fw-ohci.h is renamed to ohci.h. The name of core.h and
ohci.h is chosen with regard to name changes of the .c files in a
follow-up change.
Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
2009-06-05 16:26:18 +02:00
|
|
|
#include "core.h"
|
2006-12-20 01:58:27 +01:00
|
|
|
|
2007-05-08 02:33:35 +02:00
|
|
|
#define HEADER_PRI(pri) ((pri) << 0)
|
|
|
|
#define HEADER_TCODE(tcode) ((tcode) << 4)
|
|
|
|
#define HEADER_RETRY(retry) ((retry) << 8)
|
|
|
|
#define HEADER_TLABEL(tlabel) ((tlabel) << 10)
|
|
|
|
#define HEADER_DESTINATION(destination) ((destination) << 16)
|
|
|
|
#define HEADER_SOURCE(source) ((source) << 16)
|
|
|
|
#define HEADER_RCODE(rcode) ((rcode) << 12)
|
|
|
|
#define HEADER_OFFSET_HIGH(offset_high) ((offset_high) << 0)
|
|
|
|
#define HEADER_DATA_LENGTH(length) ((length) << 16)
|
|
|
|
#define HEADER_EXTENDED_TCODE(tcode) ((tcode) << 0)
|
|
|
|
|
|
|
|
#define HEADER_GET_TCODE(q) (((q) >> 4) & 0x0f)
|
|
|
|
#define HEADER_GET_TLABEL(q) (((q) >> 10) & 0x3f)
|
|
|
|
#define HEADER_GET_RCODE(q) (((q) >> 12) & 0x0f)
|
|
|
|
#define HEADER_GET_DESTINATION(q) (((q) >> 16) & 0xffff)
|
|
|
|
#define HEADER_GET_SOURCE(q) (((q) >> 16) & 0xffff)
|
|
|
|
#define HEADER_GET_OFFSET_HIGH(q) (((q) >> 0) & 0xffff)
|
|
|
|
#define HEADER_GET_DATA_LENGTH(q) (((q) >> 16) & 0xffff)
|
|
|
|
#define HEADER_GET_EXTENDED_TCODE(q) (((q) >> 0) & 0xffff)
|
|
|
|
|
firewire: don't respond to broadcast write requests
Contrary to a comment in the source, request->ack of a broadcast write
request can be ACK_PENDING. Hence the existing check is insufficient.
Debug dmesg before:
AR spd 0 tl 00, ffc0 -> ffff, ack_pending , QW req, fffff0000234 = ffffffff
AT spd 0 tl 00, ffff -> ffc0, ack_complete, W resp
And the requesting node (linux1394) reports an unsolicited response.
Debug dmesg after:
AR spd 0 tl 00, ffc0 -> ffff, ack_pending , QW req, fffff0000234 = ffffffff
Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
2008-05-25 11:06:55 +02:00
|
|
|
#define HEADER_DESTINATION_IS_BROADCAST(q) \
|
|
|
|
(((q) & HEADER_DESTINATION(0x3f)) == HEADER_DESTINATION(0x3f))
|
|
|
|
|
firewire: reorganize header files
The three header files of firewire-core, i.e.
"drivers/firewire/fw-device.h",
"drivers/firewire/fw-topology.h",
"drivers/firewire/fw-transaction.h",
are replaced by
"drivers/firewire/core.h",
"include/linux/firewire.h".
The latter includes everything which a firewire high-level driver (like
firewire-sbp2) needs besides linux/firewire-constants.h, while core.h
contains the rest which is needed by firewire-core itself and by low-
level drivers (card drivers) like firewire-ohci.
High-level drivers can now also reside outside of drivers/firewire
without having to add drivers/firewire to the header file search path in
makefiles. At least the firedtv driver will be such a driver.
I also considered to spread the contents of core.h over several files,
one for each .c file where the respective implementation resides. But
it turned out that most core .c files will end up including most of the
core .h files. Also, the combined core.h isn't unreasonably big, and it
will lose more of its contents to linux/firewire.h anyway soon when more
firewire drivers are added. (IP-over-1394, firedtv, and there are plans
for one or two more.)
Furthermore, fw-ohci.h is renamed to ohci.h. The name of core.h and
ohci.h is chosen with regard to name changes of the .c files in a
follow-up change.
Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
2009-06-05 16:26:18 +02:00
|
|
|
#define PHY_PACKET_CONFIG 0x0
|
|
|
|
#define PHY_PACKET_LINK_ON 0x1
|
|
|
|
#define PHY_PACKET_SELF_ID 0x2
|
|
|
|
|
2007-05-08 02:33:35 +02:00
|
|
|
#define PHY_CONFIG_GAP_COUNT(gap_count) (((gap_count) << 16) | (1 << 22))
|
|
|
|
#define PHY_CONFIG_ROOT_ID(node_id) ((((node_id) & 0x3f) << 24) | (1 << 23))
|
|
|
|
#define PHY_IDENTIFIER(id) ((id) << 30)
|
2006-12-20 01:58:27 +01:00
|
|
|
|
2010-12-13 14:56:02 +01:00
|
|
|
/* returns 0 if the split timeout handler is already running */
|
|
|
|
static int try_cancel_split_timeout(struct fw_transaction *t)
|
|
|
|
{
|
|
|
|
if (t->is_split_transaction)
|
|
|
|
return del_timer(&t->split_timeout_timer);
|
|
|
|
else
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2008-12-14 21:47:04 +01:00
|
|
|
static int close_transaction(struct fw_transaction *transaction,
|
2009-03-10 21:07:06 +01:00
|
|
|
struct fw_card *card, int rcode)
|
2006-12-20 01:58:27 +01:00
|
|
|
{
|
2007-02-06 20:49:32 +01:00
|
|
|
struct fw_transaction *t;
|
2006-12-20 01:58:27 +01:00
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&card->lock, flags);
|
2007-02-06 20:49:32 +01:00
|
|
|
list_for_each_entry(t, &card->transaction_list, link) {
|
|
|
|
if (t == transaction) {
|
2010-12-13 14:56:02 +01:00
|
|
|
if (!try_cancel_split_timeout(t)) {
|
2010-08-18 15:05:02 +02:00
|
|
|
spin_unlock_irqrestore(&card->lock, flags);
|
|
|
|
goto timed_out;
|
|
|
|
}
|
2010-04-27 09:07:00 +02:00
|
|
|
list_del_init(&t->link);
|
2009-06-14 13:23:58 +02:00
|
|
|
card->tlabel_mask &= ~(1ULL << t->tlabel);
|
2007-02-06 20:49:32 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2006-12-20 01:58:27 +01:00
|
|
|
spin_unlock_irqrestore(&card->lock, flags);
|
|
|
|
|
2007-02-06 20:49:32 +01:00
|
|
|
if (&t->link != &card->transaction_list) {
|
2009-03-10 21:07:06 +01:00
|
|
|
t->callback(card, rcode, NULL, 0, t->callback_data);
|
2007-02-06 20:49:32 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-08-18 15:05:02 +02:00
|
|
|
timed_out:
|
2007-02-06 20:49:32 +01:00
|
|
|
return -ENOENT;
|
2006-12-20 01:58:27 +01:00
|
|
|
}
|
|
|
|
|
2007-05-08 02:33:32 +02:00
|
|
|
/*
|
|
|
|
* Only valid for transactions that are potentially pending (ie have
|
|
|
|
* been sent).
|
|
|
|
*/
|
2008-12-14 21:47:04 +01:00
|
|
|
int fw_cancel_transaction(struct fw_card *card,
|
|
|
|
struct fw_transaction *transaction)
|
2007-02-06 20:49:32 +01:00
|
|
|
{
|
2007-05-08 02:33:32 +02:00
|
|
|
/*
|
|
|
|
* Cancel the packet transmission if it's still queued. That
|
2007-02-06 20:49:32 +01:00
|
|
|
* will call the packet transmission callback which cancels
|
2007-05-08 02:33:32 +02:00
|
|
|
* the transaction.
|
|
|
|
*/
|
2007-02-06 20:49:32 +01:00
|
|
|
|
|
|
|
if (card->driver->cancel_packet(card, &transaction->packet) == 0)
|
|
|
|
return 0;
|
|
|
|
|
2007-05-08 02:33:32 +02:00
|
|
|
/*
|
|
|
|
* If the request packet has already been sent, we need to see
|
|
|
|
* if the transaction is still pending and remove it in that case.
|
|
|
|
*/
|
2007-02-06 20:49:32 +01:00
|
|
|
|
2009-03-10 21:07:06 +01:00
|
|
|
return close_transaction(transaction, card, RCODE_CANCELLED);
|
2007-02-06 20:49:32 +01:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(fw_cancel_transaction);
|
|
|
|
|
2010-04-27 09:07:00 +02:00
|
|
|
static void split_transaction_timeout_callback(unsigned long data)
|
|
|
|
{
|
|
|
|
struct fw_transaction *t = (struct fw_transaction *)data;
|
|
|
|
struct fw_card *card = t->card;
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&card->lock, flags);
|
|
|
|
if (list_empty(&t->link)) {
|
|
|
|
spin_unlock_irqrestore(&card->lock, flags);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
list_del(&t->link);
|
|
|
|
card->tlabel_mask &= ~(1ULL << t->tlabel);
|
|
|
|
spin_unlock_irqrestore(&card->lock, flags);
|
|
|
|
|
|
|
|
t->callback(card, RCODE_CANCELLED, NULL, 0, t->callback_data);
|
|
|
|
}
|
|
|
|
|
2010-12-13 14:56:02 +01:00
|
|
|
static void start_split_transaction_timeout(struct fw_transaction *t,
|
|
|
|
struct fw_card *card)
|
|
|
|
{
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&card->lock, flags);
|
|
|
|
|
|
|
|
if (list_empty(&t->link) || WARN_ON(t->is_split_transaction)) {
|
|
|
|
spin_unlock_irqrestore(&card->lock, flags);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
t->is_split_transaction = true;
|
|
|
|
mod_timer(&t->split_timeout_timer,
|
|
|
|
jiffies + card->split_timeout_jiffies);
|
|
|
|
|
|
|
|
spin_unlock_irqrestore(&card->lock, flags);
|
|
|
|
}
|
|
|
|
|
2008-12-14 21:47:04 +01:00
|
|
|
static void transmit_complete_callback(struct fw_packet *packet,
|
|
|
|
struct fw_card *card, int status)
|
2006-12-20 01:58:27 +01:00
|
|
|
{
|
|
|
|
struct fw_transaction *t =
|
|
|
|
container_of(packet, struct fw_transaction, packet);
|
|
|
|
|
|
|
|
switch (status) {
|
|
|
|
case ACK_COMPLETE:
|
2009-03-10 21:07:06 +01:00
|
|
|
close_transaction(t, card, RCODE_COMPLETE);
|
2006-12-20 01:58:27 +01:00
|
|
|
break;
|
|
|
|
case ACK_PENDING:
|
2010-12-13 14:56:02 +01:00
|
|
|
start_split_transaction_timeout(t, card);
|
2006-12-20 01:58:27 +01:00
|
|
|
break;
|
|
|
|
case ACK_BUSY_X:
|
|
|
|
case ACK_BUSY_A:
|
|
|
|
case ACK_BUSY_B:
|
2009-03-10 21:07:06 +01:00
|
|
|
close_transaction(t, card, RCODE_BUSY);
|
2006-12-20 01:58:27 +01:00
|
|
|
break;
|
|
|
|
case ACK_DATA_ERROR:
|
2009-03-10 21:07:06 +01:00
|
|
|
close_transaction(t, card, RCODE_DATA_ERROR);
|
2007-01-26 06:38:34 +01:00
|
|
|
break;
|
2006-12-20 01:58:27 +01:00
|
|
|
case ACK_TYPE_ERROR:
|
2009-03-10 21:07:06 +01:00
|
|
|
close_transaction(t, card, RCODE_TYPE_ERROR);
|
2006-12-20 01:58:27 +01:00
|
|
|
break;
|
|
|
|
default:
|
2007-05-08 02:33:32 +02:00
|
|
|
/*
|
|
|
|
* In this case the ack is really a juju specific
|
|
|
|
* rcode, so just forward that to the callback.
|
|
|
|
*/
|
2009-03-10 21:07:06 +01:00
|
|
|
close_transaction(t, card, status);
|
2006-12-20 01:58:27 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-12-14 21:47:04 +01:00
|
|
|
static void fw_fill_request(struct fw_packet *packet, int tcode, int tlabel,
|
2008-07-12 14:50:42 +02:00
|
|
|
int destination_id, int source_id, int generation, int speed,
|
2007-01-26 06:38:13 +01:00
|
|
|
unsigned long long offset, void *payload, size_t length)
|
2006-12-20 01:58:27 +01:00
|
|
|
{
|
|
|
|
int ext_tcode;
|
|
|
|
|
2009-03-10 21:02:21 +01:00
|
|
|
if (tcode == TCODE_STREAM_DATA) {
|
|
|
|
packet->header[0] =
|
|
|
|
HEADER_DATA_LENGTH(length) |
|
|
|
|
destination_id |
|
|
|
|
HEADER_TCODE(TCODE_STREAM_DATA);
|
|
|
|
packet->header_length = 4;
|
|
|
|
packet->payload = payload;
|
|
|
|
packet->payload_length = length;
|
|
|
|
|
|
|
|
goto common;
|
|
|
|
}
|
|
|
|
|
2006-12-20 01:58:27 +01:00
|
|
|
if (tcode > 0x10) {
|
2008-01-23 22:05:45 +01:00
|
|
|
ext_tcode = tcode & ~0x10;
|
2006-12-20 01:58:27 +01:00
|
|
|
tcode = TCODE_LOCK_REQUEST;
|
|
|
|
} else
|
|
|
|
ext_tcode = 0;
|
|
|
|
|
|
|
|
packet->header[0] =
|
2007-05-08 02:33:35 +02:00
|
|
|
HEADER_RETRY(RETRY_X) |
|
|
|
|
HEADER_TLABEL(tlabel) |
|
|
|
|
HEADER_TCODE(tcode) |
|
2008-07-12 14:50:42 +02:00
|
|
|
HEADER_DESTINATION(destination_id);
|
2006-12-20 01:58:27 +01:00
|
|
|
packet->header[1] =
|
2007-05-08 02:33:35 +02:00
|
|
|
HEADER_OFFSET_HIGH(offset >> 32) | HEADER_SOURCE(source_id);
|
2006-12-20 01:58:27 +01:00
|
|
|
packet->header[2] =
|
|
|
|
offset;
|
|
|
|
|
|
|
|
switch (tcode) {
|
|
|
|
case TCODE_WRITE_QUADLET_REQUEST:
|
|
|
|
packet->header[3] = *(u32 *)payload;
|
|
|
|
packet->header_length = 16;
|
|
|
|
packet->payload_length = 0;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TCODE_LOCK_REQUEST:
|
|
|
|
case TCODE_WRITE_BLOCK_REQUEST:
|
|
|
|
packet->header[3] =
|
2007-05-08 02:33:35 +02:00
|
|
|
HEADER_DATA_LENGTH(length) |
|
|
|
|
HEADER_EXTENDED_TCODE(ext_tcode);
|
2006-12-20 01:58:27 +01:00
|
|
|
packet->header_length = 16;
|
|
|
|
packet->payload = payload;
|
|
|
|
packet->payload_length = length;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TCODE_READ_QUADLET_REQUEST:
|
|
|
|
packet->header_length = 12;
|
|
|
|
packet->payload_length = 0;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TCODE_READ_BLOCK_REQUEST:
|
|
|
|
packet->header[3] =
|
2007-05-08 02:33:35 +02:00
|
|
|
HEADER_DATA_LENGTH(length) |
|
|
|
|
HEADER_EXTENDED_TCODE(ext_tcode);
|
2006-12-20 01:58:27 +01:00
|
|
|
packet->header_length = 16;
|
|
|
|
packet->payload_length = 0;
|
|
|
|
break;
|
2009-10-14 20:37:36 +02:00
|
|
|
|
|
|
|
default:
|
2010-10-30 23:08:27 +02:00
|
|
|
WARN(1, "wrong tcode %d\n", tcode);
|
2006-12-20 01:58:27 +01:00
|
|
|
}
|
2009-03-10 21:02:21 +01:00
|
|
|
common:
|
2006-12-20 01:58:27 +01:00
|
|
|
packet->speed = speed;
|
|
|
|
packet->generation = generation;
|
2007-02-06 20:49:32 +01:00
|
|
|
packet->ack = 0;
|
2009-10-14 20:40:10 +02:00
|
|
|
packet->payload_mapped = false;
|
2006-12-20 01:58:27 +01:00
|
|
|
}
|
|
|
|
|
2010-04-19 17:29:14 +02:00
|
|
|
static int allocate_tlabel(struct fw_card *card)
|
|
|
|
{
|
|
|
|
int tlabel;
|
|
|
|
|
|
|
|
tlabel = card->current_tlabel;
|
|
|
|
while (card->tlabel_mask & (1ULL << tlabel)) {
|
|
|
|
tlabel = (tlabel + 1) & 0x3f;
|
|
|
|
if (tlabel == card->current_tlabel)
|
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
|
|
|
|
card->current_tlabel = (tlabel + 1) & 0x3f;
|
|
|
|
card->tlabel_mask |= 1ULL << tlabel;
|
|
|
|
|
|
|
|
return tlabel;
|
|
|
|
}
|
|
|
|
|
2006-12-20 01:58:27 +01:00
|
|
|
/**
|
2010-07-07 13:26:18 +02:00
|
|
|
* fw_send_request() - submit a request packet for transmission
|
|
|
|
* @card: interface to send the request at
|
|
|
|
* @t: transaction instance to which the request belongs
|
|
|
|
* @tcode: transaction code
|
|
|
|
* @destination_id: destination node ID, consisting of bus_ID and phy_ID
|
|
|
|
* @generation: bus generation in which request and response are valid
|
|
|
|
* @speed: transmission speed
|
|
|
|
* @offset: 48bit wide offset into destination's address space
|
|
|
|
* @payload: data payload for the request subaction
|
|
|
|
* @length: length of the payload, in bytes
|
|
|
|
* @callback: function to be called when the transaction is completed
|
|
|
|
* @callback_data: data to be passed to the transaction completion callback
|
2006-12-20 01:58:27 +01:00
|
|
|
*
|
2010-07-07 13:26:18 +02:00
|
|
|
* Submit a request packet into the asynchronous request transmission queue.
|
|
|
|
* Can be called from atomic context. If you prefer a blocking API, use
|
|
|
|
* fw_run_transaction() in a context that can sleep.
|
2006-12-20 01:58:27 +01:00
|
|
|
*
|
2010-07-07 13:26:18 +02:00
|
|
|
* In case of lock requests, specify one of the firewire-core specific %TCODE_
|
|
|
|
* constants instead of %TCODE_LOCK_REQUEST in @tcode.
|
2006-12-20 01:58:27 +01:00
|
|
|
*
|
2010-07-07 13:26:18 +02:00
|
|
|
* Make sure that the value in @destination_id is not older than the one in
|
|
|
|
* @generation. Otherwise the request is in danger to be sent to a wrong node.
|
2006-12-20 01:58:27 +01:00
|
|
|
*
|
2010-07-07 13:26:18 +02:00
|
|
|
* In case of asynchronous stream packets i.e. %TCODE_STREAM_DATA, the caller
|
2009-03-10 21:02:21 +01:00
|
|
|
* needs to synthesize @destination_id with fw_stream_packet_destination_id().
|
2010-07-07 13:26:18 +02:00
|
|
|
* It will contain tag, channel, and sy data instead of a node ID then.
|
|
|
|
*
|
|
|
|
* The payload buffer at @data is going to be DMA-mapped except in case of
|
2011-04-22 15:13:54 +02:00
|
|
|
* @length <= 8 or of local (loopback) requests. Hence make sure that the
|
|
|
|
* buffer complies with the restrictions of the streaming DMA mapping API.
|
2010-07-07 13:26:18 +02:00
|
|
|
* @payload must not be freed before the @callback is called.
|
|
|
|
*
|
|
|
|
* In case of request types without payload, @data is NULL and @length is 0.
|
|
|
|
*
|
|
|
|
* After the transaction is completed successfully or unsuccessfully, the
|
|
|
|
* @callback will be called. Among its parameters is the response code which
|
|
|
|
* is either one of the rcodes per IEEE 1394 or, in case of internal errors,
|
2010-07-18 12:44:01 +02:00
|
|
|
* the firewire-core specific %RCODE_SEND_ERROR. The other firewire-core
|
|
|
|
* specific rcodes (%RCODE_CANCELLED, %RCODE_BUSY, %RCODE_GENERATION,
|
|
|
|
* %RCODE_NO_ACK) denote transaction timeout, busy responder, stale request
|
|
|
|
* generation, or missing ACK respectively.
|
2010-07-07 13:26:18 +02:00
|
|
|
*
|
|
|
|
* Note some timing corner cases: fw_send_request() may complete much earlier
|
|
|
|
* than when the request packet actually hits the wire. On the other hand,
|
|
|
|
* transaction completion and hence execution of @callback may happen even
|
|
|
|
* before fw_send_request() returns.
|
2006-12-20 01:58:27 +01:00
|
|
|
*/
|
2008-12-14 21:47:04 +01:00
|
|
|
void fw_send_request(struct fw_card *card, struct fw_transaction *t, int tcode,
|
|
|
|
int destination_id, int generation, int speed,
|
|
|
|
unsigned long long offset, void *payload, size_t length,
|
|
|
|
fw_transaction_callback_t callback, void *callback_data)
|
2006-12-20 01:58:27 +01:00
|
|
|
{
|
|
|
|
unsigned long flags;
|
2008-07-12 14:50:42 +02:00
|
|
|
int tlabel;
|
2006-12-20 01:58:27 +01:00
|
|
|
|
2007-05-08 02:33:32 +02:00
|
|
|
/*
|
|
|
|
* Allocate tlabel from the bitmap and put the transaction on
|
|
|
|
* the list while holding the card spinlock.
|
|
|
|
*/
|
2006-12-20 01:58:27 +01:00
|
|
|
|
|
|
|
spin_lock_irqsave(&card->lock, flags);
|
|
|
|
|
2010-04-19 17:29:14 +02:00
|
|
|
tlabel = allocate_tlabel(card);
|
|
|
|
if (tlabel < 0) {
|
2006-12-20 01:58:27 +01:00
|
|
|
spin_unlock_irqrestore(&card->lock, flags);
|
|
|
|
callback(card, RCODE_SEND_ERROR, NULL, 0, callback_data);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2008-07-20 14:20:53 +02:00
|
|
|
t->node_id = destination_id;
|
2006-12-20 01:58:27 +01:00
|
|
|
t->tlabel = tlabel;
|
2010-04-27 09:07:00 +02:00
|
|
|
t->card = card;
|
2010-12-13 14:56:02 +01:00
|
|
|
t->is_split_transaction = false;
|
2010-04-27 09:07:00 +02:00
|
|
|
setup_timer(&t->split_timeout_timer,
|
|
|
|
split_transaction_timeout_callback, (unsigned long)t);
|
2006-12-20 01:58:27 +01:00
|
|
|
t->callback = callback;
|
|
|
|
t->callback_data = callback_data;
|
|
|
|
|
2008-07-20 14:20:53 +02:00
|
|
|
fw_fill_request(&t->packet, tcode, t->tlabel,
|
|
|
|
destination_id, card->node_id, generation,
|
|
|
|
speed, offset, payload, length);
|
2006-12-20 01:58:27 +01:00
|
|
|
t->packet.callback = transmit_complete_callback;
|
|
|
|
|
2008-07-12 14:50:06 +02:00
|
|
|
list_add_tail(&t->link, &card->transaction_list);
|
|
|
|
|
|
|
|
spin_unlock_irqrestore(&card->lock, flags);
|
|
|
|
|
2006-12-20 01:58:27 +01:00
|
|
|
card->driver->send_request(card, &t->packet);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(fw_send_request);
|
|
|
|
|
2008-07-20 14:20:53 +02:00
|
|
|
struct transaction_callback_data {
|
|
|
|
struct completion done;
|
|
|
|
void *payload;
|
|
|
|
int rcode;
|
|
|
|
};
|
|
|
|
|
|
|
|
static void transaction_callback(struct fw_card *card, int rcode,
|
|
|
|
void *payload, size_t length, void *data)
|
|
|
|
{
|
|
|
|
struct transaction_callback_data *d = data;
|
|
|
|
|
|
|
|
if (rcode == RCODE_COMPLETE)
|
|
|
|
memcpy(d->payload, payload, length);
|
|
|
|
d->rcode = rcode;
|
|
|
|
complete(&d->done);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2010-07-07 13:26:18 +02:00
|
|
|
* fw_run_transaction() - send request and sleep until transaction is completed
|
2008-07-20 14:20:53 +02:00
|
|
|
*
|
2010-07-07 13:26:18 +02:00
|
|
|
* Returns the RCODE. See fw_send_request() for parameter documentation.
|
|
|
|
* Unlike fw_send_request(), @data points to the payload of the request or/and
|
2011-04-22 15:13:54 +02:00
|
|
|
* to the payload of the response. DMA mapping restrictions apply to outbound
|
|
|
|
* request payloads of >= 8 bytes but not to inbound response payloads.
|
2008-07-20 14:20:53 +02:00
|
|
|
*/
|
|
|
|
int fw_run_transaction(struct fw_card *card, int tcode, int destination_id,
|
2008-12-14 21:47:04 +01:00
|
|
|
int generation, int speed, unsigned long long offset,
|
2009-03-05 19:07:00 +01:00
|
|
|
void *payload, size_t length)
|
2008-07-20 14:20:53 +02:00
|
|
|
{
|
|
|
|
struct transaction_callback_data d;
|
|
|
|
struct fw_transaction t;
|
|
|
|
|
2010-04-27 09:07:00 +02:00
|
|
|
init_timer_on_stack(&t.split_timeout_timer);
|
2008-07-20 14:20:53 +02:00
|
|
|
init_completion(&d.done);
|
2009-03-05 19:07:00 +01:00
|
|
|
d.payload = payload;
|
2008-07-20 14:20:53 +02:00
|
|
|
fw_send_request(card, &t, tcode, destination_id, generation, speed,
|
2009-03-05 19:07:00 +01:00
|
|
|
offset, payload, length, transaction_callback, &d);
|
2008-07-20 14:20:53 +02:00
|
|
|
wait_for_completion(&d.done);
|
2010-04-27 09:07:00 +02:00
|
|
|
destroy_timer_on_stack(&t.split_timeout_timer);
|
2008-07-20 14:20:53 +02:00
|
|
|
|
|
|
|
return d.rcode;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(fw_run_transaction);
|
|
|
|
|
2008-07-22 21:35:47 +02:00
|
|
|
static DEFINE_MUTEX(phy_config_mutex);
|
|
|
|
static DECLARE_COMPLETION(phy_config_done);
|
2008-06-18 18:20:45 +02:00
|
|
|
|
|
|
|
static void transmit_phy_packet_callback(struct fw_packet *packet,
|
|
|
|
struct fw_card *card, int status)
|
2006-12-20 01:58:27 +01:00
|
|
|
{
|
2008-07-22 21:35:47 +02:00
|
|
|
complete(&phy_config_done);
|
2006-12-20 01:58:27 +01:00
|
|
|
}
|
|
|
|
|
2008-07-22 21:35:47 +02:00
|
|
|
static struct fw_packet phy_config_packet = {
|
2010-11-30 08:24:47 +01:00
|
|
|
.header_length = 12,
|
|
|
|
.header[0] = TCODE_LINK_INTERNAL << 4,
|
2008-07-22 21:35:47 +02:00
|
|
|
.payload_length = 0,
|
|
|
|
.speed = SCODE_100,
|
|
|
|
.callback = transmit_phy_packet_callback,
|
|
|
|
};
|
|
|
|
|
2007-01-26 06:37:50 +01:00
|
|
|
void fw_send_phy_config(struct fw_card *card,
|
|
|
|
int node_id, int generation, int gap_count)
|
2006-12-20 01:58:27 +01:00
|
|
|
{
|
2008-06-18 18:20:45 +02:00
|
|
|
long timeout = DIV_ROUND_UP(HZ, 10);
|
2010-07-08 16:09:06 +02:00
|
|
|
u32 data = PHY_IDENTIFIER(PHY_PACKET_CONFIG);
|
|
|
|
|
|
|
|
if (node_id != FW_PHY_CONFIG_NO_NODE_ID)
|
|
|
|
data |= PHY_CONFIG_ROOT_ID(node_id);
|
|
|
|
|
|
|
|
if (gap_count == FW_PHY_CONFIG_CURRENT_GAP_COUNT) {
|
|
|
|
gap_count = card->driver->read_phy_reg(card, 1);
|
|
|
|
if (gap_count < 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
gap_count &= 63;
|
|
|
|
if (gap_count == 63)
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
data |= PHY_CONFIG_GAP_COUNT(gap_count);
|
2008-03-20 23:48:23 +01:00
|
|
|
|
2008-07-22 21:35:47 +02:00
|
|
|
mutex_lock(&phy_config_mutex);
|
|
|
|
|
2010-11-30 08:24:47 +01:00
|
|
|
phy_config_packet.header[1] = data;
|
|
|
|
phy_config_packet.header[2] = ~data;
|
2008-07-22 21:35:47 +02:00
|
|
|
phy_config_packet.generation = generation;
|
|
|
|
INIT_COMPLETION(phy_config_done);
|
|
|
|
|
|
|
|
card->driver->send_request(card, &phy_config_packet);
|
|
|
|
wait_for_completion_timeout(&phy_config_done, timeout);
|
2008-06-18 18:20:45 +02:00
|
|
|
|
2008-07-22 21:35:47 +02:00
|
|
|
mutex_unlock(&phy_config_mutex);
|
2006-12-20 01:58:27 +01:00
|
|
|
}
|
|
|
|
|
2008-12-14 21:47:04 +01:00
|
|
|
static struct fw_address_handler *lookup_overlapping_address_handler(
|
|
|
|
struct list_head *list, unsigned long long offset, size_t length)
|
2006-12-20 01:58:27 +01:00
|
|
|
{
|
|
|
|
struct fw_address_handler *handler;
|
|
|
|
|
|
|
|
list_for_each_entry(handler, list, link) {
|
|
|
|
if (handler->offset < offset + length &&
|
|
|
|
offset < handler->offset + handler->length)
|
|
|
|
return handler;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
firewire: fix use of multiple AV/C devices, allow multiple FCP listeners
Control of more than one AV/C device at once --- e.g. camcorders, tape
decks, audio devices, TV tuners --- failed or worked only unreliably,
depending on driver implementation. This affected kernelspace and
userspace drivers alike and was caused by firewire-core's inability to
accept multiple registrations of FCP listeners.
The fix allows multiple address handlers to be registered for the FCP
command and response registers. When a request for these registers is
received, all handlers are invoked, and the Firewire response is
generated by the core and not by any handler.
The cdev API does not change, i.e., userspace is still expected to send
a response for FCP requests; this response is silently ignored.
Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de> (changelog, rebased, whitespace)
2009-12-24 12:05:58 +01:00
|
|
|
static bool is_enclosing_handler(struct fw_address_handler *handler,
|
|
|
|
unsigned long long offset, size_t length)
|
|
|
|
{
|
|
|
|
return handler->offset <= offset &&
|
|
|
|
offset + length <= handler->offset + handler->length;
|
|
|
|
}
|
|
|
|
|
2008-12-14 21:47:04 +01:00
|
|
|
static struct fw_address_handler *lookup_enclosing_address_handler(
|
|
|
|
struct list_head *list, unsigned long long offset, size_t length)
|
2006-12-20 01:58:27 +01:00
|
|
|
{
|
|
|
|
struct fw_address_handler *handler;
|
|
|
|
|
|
|
|
list_for_each_entry(handler, list, link) {
|
firewire: fix use of multiple AV/C devices, allow multiple FCP listeners
Control of more than one AV/C device at once --- e.g. camcorders, tape
decks, audio devices, TV tuners --- failed or worked only unreliably,
depending on driver implementation. This affected kernelspace and
userspace drivers alike and was caused by firewire-core's inability to
accept multiple registrations of FCP listeners.
The fix allows multiple address handlers to be registered for the FCP
command and response registers. When a request for these registers is
received, all handlers are invoked, and the Firewire response is
generated by the core and not by any handler.
The cdev API does not change, i.e., userspace is still expected to send
a response for FCP requests; this response is silently ignored.
Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de> (changelog, rebased, whitespace)
2009-12-24 12:05:58 +01:00
|
|
|
if (is_enclosing_handler(handler, offset, length))
|
2006-12-20 01:58:27 +01:00
|
|
|
return handler;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static DEFINE_SPINLOCK(address_handler_lock);
|
|
|
|
static LIST_HEAD(address_handler_list);
|
|
|
|
|
2007-01-14 15:29:07 +01:00
|
|
|
const struct fw_address_region fw_high_memory_region =
|
2007-01-21 20:45:32 +01:00
|
|
|
{ .start = 0x000100000000ULL, .end = 0xffffe0000000ULL, };
|
2008-03-31 01:22:11 +02:00
|
|
|
EXPORT_SYMBOL(fw_high_memory_region);
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
const struct fw_address_region fw_low_memory_region =
|
|
|
|
{ .start = 0x000000000000ULL, .end = 0x000100000000ULL, };
|
2007-01-14 15:29:07 +01:00
|
|
|
const struct fw_address_region fw_private_region =
|
2007-01-21 20:45:32 +01:00
|
|
|
{ .start = 0xffffe0000000ULL, .end = 0xfffff0000000ULL, };
|
2007-01-14 15:29:07 +01:00
|
|
|
const struct fw_address_region fw_csr_region =
|
2008-03-08 18:52:03 +01:00
|
|
|
{ .start = CSR_REGISTER_BASE,
|
|
|
|
.end = CSR_REGISTER_BASE | CSR_CONFIG_ROM_END, };
|
2007-01-14 15:29:07 +01:00
|
|
|
const struct fw_address_region fw_unit_space_region =
|
2007-01-21 20:45:32 +01:00
|
|
|
{ .start = 0xfffff0000900ULL, .end = 0x1000000000000ULL, };
|
2008-03-31 01:22:11 +02:00
|
|
|
#endif /* 0 */
|
2006-12-20 01:58:27 +01:00
|
|
|
|
firewire: fix use of multiple AV/C devices, allow multiple FCP listeners
Control of more than one AV/C device at once --- e.g. camcorders, tape
decks, audio devices, TV tuners --- failed or worked only unreliably,
depending on driver implementation. This affected kernelspace and
userspace drivers alike and was caused by firewire-core's inability to
accept multiple registrations of FCP listeners.
The fix allows multiple address handlers to be registered for the FCP
command and response registers. When a request for these registers is
received, all handlers are invoked, and the Firewire response is
generated by the core and not by any handler.
The cdev API does not change, i.e., userspace is still expected to send
a response for FCP requests; this response is silently ignored.
Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de> (changelog, rebased, whitespace)
2009-12-24 12:05:58 +01:00
|
|
|
static bool is_in_fcp_region(u64 offset, size_t length)
|
|
|
|
{
|
|
|
|
return offset >= (CSR_REGISTER_BASE | CSR_FCP_COMMAND) &&
|
|
|
|
offset + length <= (CSR_REGISTER_BASE | CSR_FCP_END);
|
|
|
|
}
|
|
|
|
|
2006-12-20 01:58:27 +01:00
|
|
|
/**
|
2010-07-07 13:26:18 +02:00
|
|
|
* fw_core_add_address_handler() - register for incoming requests
|
|
|
|
* @handler: callback
|
|
|
|
* @region: region in the IEEE 1212 node space address range
|
2008-12-14 19:21:01 +01:00
|
|
|
*
|
|
|
|
* region->start, ->end, and handler->length have to be quadlet-aligned.
|
|
|
|
*
|
|
|
|
* When a request is received that falls within the specified address range,
|
|
|
|
* the specified callback is invoked. The parameters passed to the callback
|
|
|
|
* give the details of the particular request.
|
2007-07-17 02:10:16 +02:00
|
|
|
*
|
|
|
|
* Return value: 0 on success, non-zero otherwise.
|
firewire: fix use of multiple AV/C devices, allow multiple FCP listeners
Control of more than one AV/C device at once --- e.g. camcorders, tape
decks, audio devices, TV tuners --- failed or worked only unreliably,
depending on driver implementation. This affected kernelspace and
userspace drivers alike and was caused by firewire-core's inability to
accept multiple registrations of FCP listeners.
The fix allows multiple address handlers to be registered for the FCP
command and response registers. When a request for these registers is
received, all handlers are invoked, and the Firewire response is
generated by the core and not by any handler.
The cdev API does not change, i.e., userspace is still expected to send
a response for FCP requests; this response is silently ignored.
Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de> (changelog, rebased, whitespace)
2009-12-24 12:05:58 +01:00
|
|
|
*
|
2007-07-17 02:10:16 +02:00
|
|
|
* The start offset of the handler's address region is determined by
|
|
|
|
* fw_core_add_address_handler() and is returned in handler->offset.
|
firewire: fix use of multiple AV/C devices, allow multiple FCP listeners
Control of more than one AV/C device at once --- e.g. camcorders, tape
decks, audio devices, TV tuners --- failed or worked only unreliably,
depending on driver implementation. This affected kernelspace and
userspace drivers alike and was caused by firewire-core's inability to
accept multiple registrations of FCP listeners.
The fix allows multiple address handlers to be registered for the FCP
command and response registers. When a request for these registers is
received, all handlers are invoked, and the Firewire response is
generated by the core and not by any handler.
The cdev API does not change, i.e., userspace is still expected to send
a response for FCP requests; this response is silently ignored.
Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de> (changelog, rebased, whitespace)
2009-12-24 12:05:58 +01:00
|
|
|
*
|
|
|
|
* Address allocations are exclusive, except for the FCP registers.
|
2006-12-20 01:58:27 +01:00
|
|
|
*/
|
2008-12-14 21:47:04 +01:00
|
|
|
int fw_core_add_address_handler(struct fw_address_handler *handler,
|
|
|
|
const struct fw_address_region *region)
|
2006-12-20 01:58:27 +01:00
|
|
|
{
|
|
|
|
struct fw_address_handler *other;
|
|
|
|
int ret = -EBUSY;
|
|
|
|
|
2008-12-14 19:21:01 +01:00
|
|
|
if (region->start & 0xffff000000000003ULL ||
|
|
|
|
region->start >= region->end ||
|
2010-07-23 13:02:54 +02:00
|
|
|
region->end > 0x0001000000000000ULL ||
|
2008-12-14 19:21:01 +01:00
|
|
|
handler->length & 3 ||
|
|
|
|
handler->length == 0)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2012-02-18 20:42:00 +01:00
|
|
|
spin_lock_bh(&address_handler_lock);
|
2006-12-20 01:58:27 +01:00
|
|
|
|
2008-12-14 19:21:01 +01:00
|
|
|
handler->offset = region->start;
|
2006-12-20 01:58:27 +01:00
|
|
|
while (handler->offset + handler->length <= region->end) {
|
firewire: fix use of multiple AV/C devices, allow multiple FCP listeners
Control of more than one AV/C device at once --- e.g. camcorders, tape
decks, audio devices, TV tuners --- failed or worked only unreliably,
depending on driver implementation. This affected kernelspace and
userspace drivers alike and was caused by firewire-core's inability to
accept multiple registrations of FCP listeners.
The fix allows multiple address handlers to be registered for the FCP
command and response registers. When a request for these registers is
received, all handlers are invoked, and the Firewire response is
generated by the core and not by any handler.
The cdev API does not change, i.e., userspace is still expected to send
a response for FCP requests; this response is silently ignored.
Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de> (changelog, rebased, whitespace)
2009-12-24 12:05:58 +01:00
|
|
|
if (is_in_fcp_region(handler->offset, handler->length))
|
|
|
|
other = NULL;
|
|
|
|
else
|
|
|
|
other = lookup_overlapping_address_handler
|
|
|
|
(&address_handler_list,
|
|
|
|
handler->offset, handler->length);
|
2006-12-20 01:58:27 +01:00
|
|
|
if (other != NULL) {
|
2008-12-14 19:21:01 +01:00
|
|
|
handler->offset += other->length;
|
2006-12-20 01:58:27 +01:00
|
|
|
} else {
|
|
|
|
list_add_tail(&handler->link, &address_handler_list);
|
|
|
|
ret = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-02-18 20:42:00 +01:00
|
|
|
spin_unlock_bh(&address_handler_lock);
|
2006-12-20 01:58:27 +01:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(fw_core_add_address_handler);
|
|
|
|
|
|
|
|
/**
|
2010-07-07 13:26:18 +02:00
|
|
|
* fw_core_remove_address_handler() - unregister an address handler
|
2012-02-18 19:54:45 +01:00
|
|
|
*
|
|
|
|
* When fw_core_remove_address_handler() returns, @handler->callback() is
|
|
|
|
* guaranteed to not run on any CPU anymore.
|
2006-12-20 01:58:27 +01:00
|
|
|
*/
|
|
|
|
void fw_core_remove_address_handler(struct fw_address_handler *handler)
|
|
|
|
{
|
2012-02-18 20:42:00 +01:00
|
|
|
spin_lock_bh(&address_handler_lock);
|
2006-12-20 01:58:27 +01:00
|
|
|
list_del(&handler->link);
|
2012-02-18 20:42:00 +01:00
|
|
|
spin_unlock_bh(&address_handler_lock);
|
2006-12-20 01:58:27 +01:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(fw_core_remove_address_handler);
|
|
|
|
|
|
|
|
struct fw_request {
|
|
|
|
struct fw_packet response;
|
2007-01-26 06:38:13 +01:00
|
|
|
u32 request_header[4];
|
2006-12-20 01:58:27 +01:00
|
|
|
int ack;
|
|
|
|
u32 length;
|
|
|
|
u32 data[0];
|
|
|
|
};
|
|
|
|
|
2008-12-14 21:47:04 +01:00
|
|
|
static void free_response_callback(struct fw_packet *packet,
|
|
|
|
struct fw_card *card, int status)
|
2006-12-20 01:58:27 +01:00
|
|
|
{
|
|
|
|
struct fw_request *request;
|
|
|
|
|
|
|
|
request = container_of(packet, struct fw_request, response);
|
|
|
|
kfree(request);
|
|
|
|
}
|
|
|
|
|
2010-05-19 08:28:32 +02:00
|
|
|
int fw_get_response_length(struct fw_request *r)
|
|
|
|
{
|
|
|
|
int tcode, ext_tcode, data_length;
|
|
|
|
|
|
|
|
tcode = HEADER_GET_TCODE(r->request_header[0]);
|
|
|
|
|
|
|
|
switch (tcode) {
|
|
|
|
case TCODE_WRITE_QUADLET_REQUEST:
|
|
|
|
case TCODE_WRITE_BLOCK_REQUEST:
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
case TCODE_READ_QUADLET_REQUEST:
|
|
|
|
return 4;
|
|
|
|
|
|
|
|
case TCODE_READ_BLOCK_REQUEST:
|
|
|
|
data_length = HEADER_GET_DATA_LENGTH(r->request_header[3]);
|
|
|
|
return data_length;
|
|
|
|
|
|
|
|
case TCODE_LOCK_REQUEST:
|
|
|
|
ext_tcode = HEADER_GET_EXTENDED_TCODE(r->request_header[3]);
|
|
|
|
data_length = HEADER_GET_DATA_LENGTH(r->request_header[3]);
|
|
|
|
switch (ext_tcode) {
|
|
|
|
case EXTCODE_FETCH_ADD:
|
|
|
|
case EXTCODE_LITTLE_ADD:
|
|
|
|
return data_length;
|
|
|
|
default:
|
|
|
|
return data_length / 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
2010-10-30 23:08:27 +02:00
|
|
|
WARN(1, "wrong tcode %d\n", tcode);
|
2010-05-19 08:28:32 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-12-14 21:47:04 +01:00
|
|
|
void fw_fill_response(struct fw_packet *response, u32 *request_header,
|
|
|
|
int rcode, void *payload, size_t length)
|
2006-12-20 01:58:27 +01:00
|
|
|
{
|
|
|
|
int tcode, tlabel, extended_tcode, source, destination;
|
|
|
|
|
2007-05-08 02:33:35 +02:00
|
|
|
tcode = HEADER_GET_TCODE(request_header[0]);
|
|
|
|
tlabel = HEADER_GET_TLABEL(request_header[0]);
|
|
|
|
source = HEADER_GET_DESTINATION(request_header[0]);
|
|
|
|
destination = HEADER_GET_SOURCE(request_header[1]);
|
|
|
|
extended_tcode = HEADER_GET_EXTENDED_TCODE(request_header[3]);
|
2006-12-20 01:58:27 +01:00
|
|
|
|
|
|
|
response->header[0] =
|
2007-05-08 02:33:35 +02:00
|
|
|
HEADER_RETRY(RETRY_1) |
|
|
|
|
HEADER_TLABEL(tlabel) |
|
|
|
|
HEADER_DESTINATION(destination);
|
2007-01-26 06:38:13 +01:00
|
|
|
response->header[1] =
|
2007-05-08 02:33:35 +02:00
|
|
|
HEADER_SOURCE(source) |
|
|
|
|
HEADER_RCODE(rcode);
|
2006-12-20 01:58:27 +01:00
|
|
|
response->header[2] = 0;
|
|
|
|
|
|
|
|
switch (tcode) {
|
|
|
|
case TCODE_WRITE_QUADLET_REQUEST:
|
|
|
|
case TCODE_WRITE_BLOCK_REQUEST:
|
2007-05-08 02:33:35 +02:00
|
|
|
response->header[0] |= HEADER_TCODE(TCODE_WRITE_RESPONSE);
|
2006-12-20 01:58:27 +01:00
|
|
|
response->header_length = 12;
|
|
|
|
response->payload_length = 0;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TCODE_READ_QUADLET_REQUEST:
|
|
|
|
response->header[0] |=
|
2007-05-08 02:33:35 +02:00
|
|
|
HEADER_TCODE(TCODE_READ_QUADLET_RESPONSE);
|
2007-01-26 06:38:26 +01:00
|
|
|
if (payload != NULL)
|
|
|
|
response->header[3] = *(u32 *)payload;
|
|
|
|
else
|
|
|
|
response->header[3] = 0;
|
2006-12-20 01:58:27 +01:00
|
|
|
response->header_length = 16;
|
|
|
|
response->payload_length = 0;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TCODE_READ_BLOCK_REQUEST:
|
|
|
|
case TCODE_LOCK_REQUEST:
|
2007-05-08 02:33:35 +02:00
|
|
|
response->header[0] |= HEADER_TCODE(tcode + 2);
|
2006-12-20 01:58:27 +01:00
|
|
|
response->header[3] =
|
2007-05-08 02:33:35 +02:00
|
|
|
HEADER_DATA_LENGTH(length) |
|
|
|
|
HEADER_EXTENDED_TCODE(extended_tcode);
|
2006-12-20 01:58:27 +01:00
|
|
|
response->header_length = 16;
|
2007-01-26 06:38:13 +01:00
|
|
|
response->payload = payload;
|
|
|
|
response->payload_length = length;
|
2006-12-20 01:58:27 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2010-10-30 23:08:27 +02:00
|
|
|
WARN(1, "wrong tcode %d\n", tcode);
|
2006-12-20 01:58:27 +01:00
|
|
|
}
|
2008-12-10 00:20:38 +01:00
|
|
|
|
2009-10-14 20:40:10 +02:00
|
|
|
response->payload_mapped = false;
|
2006-12-20 01:58:27 +01:00
|
|
|
}
|
2007-01-26 06:38:26 +01:00
|
|
|
EXPORT_SYMBOL(fw_fill_response);
|
2006-12-20 01:58:27 +01:00
|
|
|
|
2010-06-10 08:26:28 +02:00
|
|
|
static u32 compute_split_timeout_timestamp(struct fw_card *card,
|
|
|
|
u32 request_timestamp)
|
|
|
|
{
|
|
|
|
unsigned int cycles;
|
|
|
|
u32 timestamp;
|
|
|
|
|
|
|
|
cycles = card->split_timeout_cycles;
|
|
|
|
cycles += request_timestamp & 0x1fff;
|
|
|
|
|
|
|
|
timestamp = request_timestamp & ~0x1fff;
|
|
|
|
timestamp += (cycles / 8000) << 13;
|
|
|
|
timestamp |= cycles % 8000;
|
|
|
|
|
|
|
|
return timestamp;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct fw_request *allocate_request(struct fw_card *card,
|
|
|
|
struct fw_packet *p)
|
2006-12-20 01:58:27 +01:00
|
|
|
{
|
|
|
|
struct fw_request *request;
|
|
|
|
u32 *data, length;
|
2010-06-10 08:26:28 +02:00
|
|
|
int request_tcode;
|
2006-12-20 01:58:27 +01:00
|
|
|
|
2007-05-08 02:33:35 +02:00
|
|
|
request_tcode = HEADER_GET_TCODE(p->header[0]);
|
2006-12-20 01:58:27 +01:00
|
|
|
switch (request_tcode) {
|
|
|
|
case TCODE_WRITE_QUADLET_REQUEST:
|
2007-01-26 06:37:57 +01:00
|
|
|
data = &p->header[3];
|
2006-12-20 01:58:27 +01:00
|
|
|
length = 4;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TCODE_WRITE_BLOCK_REQUEST:
|
|
|
|
case TCODE_LOCK_REQUEST:
|
2007-01-26 06:37:57 +01:00
|
|
|
data = p->payload;
|
2007-05-08 02:33:35 +02:00
|
|
|
length = HEADER_GET_DATA_LENGTH(p->header[3]);
|
2006-12-20 01:58:27 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
case TCODE_READ_QUADLET_REQUEST:
|
|
|
|
data = NULL;
|
|
|
|
length = 4;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TCODE_READ_BLOCK_REQUEST:
|
|
|
|
data = NULL;
|
2007-05-08 02:33:35 +02:00
|
|
|
length = HEADER_GET_DATA_LENGTH(p->header[3]);
|
2006-12-20 01:58:27 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
firewire: core: prefix log messages with card name
Associate all log messages from firewire-core with the respective card
because some people have more than one card. E.g.
firewire_ohci 0000:04:00.0: added OHCI v1.10 device as card 0, 8 IR + 8 IT contexts, quirks 0x0
firewire_ohci 0000:05:00.0: added OHCI v1.10 device as card 1, 8 IR + 8 IT contexts, quirks 0x0
firewire_core: created device fw0: GUID 0814438400000389, S800
firewire_core: phy config: new root=ffc1, gap_count=5
firewire_core: created device fw1: GUID 0814438400000388, S800
firewire_core: created device fw2: GUID 0001d202e06800d1, S800
turns into
firewire_ohci 0000:04:00.0: added OHCI v1.10 device as card 0, 8 IR + 8 IT contexts, quirks 0x0
firewire_ohci 0000:05:00.0: added OHCI v1.10 device as card 1, 8 IR + 8 IT contexts, quirks 0x0
firewire_core 0000:04:00.0: created device fw0: GUID 0814438400000389, S800
firewire_core 0000:04:00.0: phy config: new root=ffc1, gap_count=5
firewire_core 0000:05:00.0: created device fw1: GUID 0814438400000388, S800
firewire_core 0000:04:00.0: created device fw2: GUID 0001d202e06800d1, S800
This increases the module size slightly; to keep this in check, turn the
former printk wrapper macros into functions. Their implementation is
largely copied from driver core's dev_printk counterparts.
Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
2012-02-18 22:03:14 +01:00
|
|
|
fw_notice(card, "ERROR - corrupt request received - %08x %08x %08x\n",
|
2008-05-31 19:01:26 +02:00
|
|
|
p->header[0], p->header[1], p->header[2]);
|
2006-12-20 01:58:27 +01:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2007-05-10 01:23:14 +02:00
|
|
|
request = kmalloc(sizeof(*request) + length, GFP_ATOMIC);
|
2006-12-20 01:58:27 +01:00
|
|
|
if (request == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
2007-01-26 06:37:57 +01:00
|
|
|
request->response.speed = p->speed;
|
2010-06-10 08:26:28 +02:00
|
|
|
request->response.timestamp =
|
|
|
|
compute_split_timeout_timestamp(card, p->timestamp);
|
2007-01-26 06:37:57 +01:00
|
|
|
request->response.generation = p->generation;
|
2007-02-06 20:49:32 +01:00
|
|
|
request->response.ack = 0;
|
2006-12-20 01:58:27 +01:00
|
|
|
request->response.callback = free_response_callback;
|
2007-01-26 06:37:57 +01:00
|
|
|
request->ack = p->ack;
|
2007-01-26 06:38:26 +01:00
|
|
|
request->length = length;
|
2006-12-20 01:58:27 +01:00
|
|
|
if (data)
|
2007-02-16 23:34:37 +01:00
|
|
|
memcpy(request->data, data, length);
|
2006-12-20 01:58:27 +01:00
|
|
|
|
2007-05-10 01:23:14 +02:00
|
|
|
memcpy(request->request_header, p->header, sizeof(p->header));
|
2006-12-20 01:58:27 +01:00
|
|
|
|
|
|
|
return request;
|
|
|
|
}
|
|
|
|
|
2008-12-14 21:47:04 +01:00
|
|
|
void fw_send_response(struct fw_card *card,
|
|
|
|
struct fw_request *request, int rcode)
|
2006-12-20 01:58:27 +01:00
|
|
|
{
|
firewire: fix use of multiple AV/C devices, allow multiple FCP listeners
Control of more than one AV/C device at once --- e.g. camcorders, tape
decks, audio devices, TV tuners --- failed or worked only unreliably,
depending on driver implementation. This affected kernelspace and
userspace drivers alike and was caused by firewire-core's inability to
accept multiple registrations of FCP listeners.
The fix allows multiple address handlers to be registered for the FCP
command and response registers. When a request for these registers is
received, all handlers are invoked, and the Firewire response is
generated by the core and not by any handler.
The cdev API does not change, i.e., userspace is still expected to send
a response for FCP requests; this response is silently ignored.
Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de> (changelog, rebased, whitespace)
2009-12-24 12:05:58 +01:00
|
|
|
if (WARN_ONCE(!request, "invalid for FCP address handlers"))
|
|
|
|
return;
|
|
|
|
|
firewire: don't respond to broadcast write requests
Contrary to a comment in the source, request->ack of a broadcast write
request can be ACK_PENDING. Hence the existing check is insufficient.
Debug dmesg before:
AR spd 0 tl 00, ffc0 -> ffff, ack_pending , QW req, fffff0000234 = ffffffff
AT spd 0 tl 00, ffff -> ffc0, ack_complete, W resp
And the requesting node (linux1394) reports an unsolicited response.
Debug dmesg after:
AR spd 0 tl 00, ffc0 -> ffff, ack_pending , QW req, fffff0000234 = ffffffff
Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
2008-05-25 11:06:55 +02:00
|
|
|
/* unified transaction or broadcast transaction: don't respond */
|
|
|
|
if (request->ack != ACK_PENDING ||
|
|
|
|
HEADER_DESTINATION_IS_BROADCAST(request->request_header[0])) {
|
2007-07-17 02:15:36 +02:00
|
|
|
kfree(request);
|
2006-12-20 01:58:27 +01:00
|
|
|
return;
|
2007-07-17 02:15:36 +02:00
|
|
|
}
|
2006-12-20 01:58:27 +01:00
|
|
|
|
2007-01-26 06:38:13 +01:00
|
|
|
if (rcode == RCODE_COMPLETE)
|
|
|
|
fw_fill_response(&request->response, request->request_header,
|
2010-05-19 08:28:32 +02:00
|
|
|
rcode, request->data,
|
|
|
|
fw_get_response_length(request));
|
2007-01-26 06:38:13 +01:00
|
|
|
else
|
|
|
|
fw_fill_response(&request->response, request->request_header,
|
|
|
|
rcode, NULL, 0);
|
2006-12-20 01:58:27 +01:00
|
|
|
|
|
|
|
card->driver->send_response(card, &request->response);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(fw_send_response);
|
|
|
|
|
firewire: fix use of multiple AV/C devices, allow multiple FCP listeners
Control of more than one AV/C device at once --- e.g. camcorders, tape
decks, audio devices, TV tuners --- failed or worked only unreliably,
depending on driver implementation. This affected kernelspace and
userspace drivers alike and was caused by firewire-core's inability to
accept multiple registrations of FCP listeners.
The fix allows multiple address handlers to be registered for the FCP
command and response registers. When a request for these registers is
received, all handlers are invoked, and the Firewire response is
generated by the core and not by any handler.
The cdev API does not change, i.e., userspace is still expected to send
a response for FCP requests; this response is silently ignored.
Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de> (changelog, rebased, whitespace)
2009-12-24 12:05:58 +01:00
|
|
|
static void handle_exclusive_region_request(struct fw_card *card,
|
|
|
|
struct fw_packet *p,
|
|
|
|
struct fw_request *request,
|
|
|
|
unsigned long long offset)
|
2006-12-20 01:58:27 +01:00
|
|
|
{
|
|
|
|
struct fw_address_handler *handler;
|
2007-01-26 06:37:57 +01:00
|
|
|
int tcode, destination, source;
|
2006-12-20 01:58:27 +01:00
|
|
|
|
2007-05-08 02:33:35 +02:00
|
|
|
destination = HEADER_GET_DESTINATION(p->header[0]);
|
2007-12-21 18:32:15 +01:00
|
|
|
source = HEADER_GET_SOURCE(p->header[1]);
|
2010-05-18 16:57:33 +02:00
|
|
|
tcode = HEADER_GET_TCODE(p->header[0]);
|
|
|
|
if (tcode == TCODE_LOCK_REQUEST)
|
|
|
|
tcode = 0x10 + HEADER_GET_EXTENDED_TCODE(p->header[3]);
|
2006-12-20 01:58:27 +01:00
|
|
|
|
2012-02-18 20:42:00 +01:00
|
|
|
spin_lock_bh(&address_handler_lock);
|
2006-12-20 01:58:27 +01:00
|
|
|
handler = lookup_enclosing_address_handler(&address_handler_list,
|
|
|
|
offset, request->length);
|
2012-02-18 19:54:45 +01:00
|
|
|
if (handler)
|
2006-12-20 01:58:27 +01:00
|
|
|
handler->address_callback(card, request,
|
|
|
|
tcode, destination, source,
|
2010-06-20 22:50:35 +02:00
|
|
|
p->generation, offset,
|
2006-12-20 01:58:27 +01:00
|
|
|
request->data, request->length,
|
|
|
|
handler->callback_data);
|
2012-02-18 20:42:00 +01:00
|
|
|
spin_unlock_bh(&address_handler_lock);
|
2012-02-18 19:54:45 +01:00
|
|
|
|
|
|
|
if (!handler)
|
|
|
|
fw_send_response(card, request, RCODE_ADDRESS_ERROR);
|
2006-12-20 01:58:27 +01:00
|
|
|
}
|
firewire: fix use of multiple AV/C devices, allow multiple FCP listeners
Control of more than one AV/C device at once --- e.g. camcorders, tape
decks, audio devices, TV tuners --- failed or worked only unreliably,
depending on driver implementation. This affected kernelspace and
userspace drivers alike and was caused by firewire-core's inability to
accept multiple registrations of FCP listeners.
The fix allows multiple address handlers to be registered for the FCP
command and response registers. When a request for these registers is
received, all handlers are invoked, and the Firewire response is
generated by the core and not by any handler.
The cdev API does not change, i.e., userspace is still expected to send
a response for FCP requests; this response is silently ignored.
Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de> (changelog, rebased, whitespace)
2009-12-24 12:05:58 +01:00
|
|
|
|
|
|
|
static void handle_fcp_region_request(struct fw_card *card,
|
|
|
|
struct fw_packet *p,
|
|
|
|
struct fw_request *request,
|
|
|
|
unsigned long long offset)
|
|
|
|
{
|
|
|
|
struct fw_address_handler *handler;
|
|
|
|
int tcode, destination, source;
|
|
|
|
|
|
|
|
if ((offset != (CSR_REGISTER_BASE | CSR_FCP_COMMAND) &&
|
|
|
|
offset != (CSR_REGISTER_BASE | CSR_FCP_RESPONSE)) ||
|
|
|
|
request->length > 0x200) {
|
|
|
|
fw_send_response(card, request, RCODE_ADDRESS_ERROR);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
tcode = HEADER_GET_TCODE(p->header[0]);
|
|
|
|
destination = HEADER_GET_DESTINATION(p->header[0]);
|
|
|
|
source = HEADER_GET_SOURCE(p->header[1]);
|
|
|
|
|
|
|
|
if (tcode != TCODE_WRITE_QUADLET_REQUEST &&
|
|
|
|
tcode != TCODE_WRITE_BLOCK_REQUEST) {
|
|
|
|
fw_send_response(card, request, RCODE_TYPE_ERROR);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-02-18 20:42:00 +01:00
|
|
|
spin_lock_bh(&address_handler_lock);
|
firewire: fix use of multiple AV/C devices, allow multiple FCP listeners
Control of more than one AV/C device at once --- e.g. camcorders, tape
decks, audio devices, TV tuners --- failed or worked only unreliably,
depending on driver implementation. This affected kernelspace and
userspace drivers alike and was caused by firewire-core's inability to
accept multiple registrations of FCP listeners.
The fix allows multiple address handlers to be registered for the FCP
command and response registers. When a request for these registers is
received, all handlers are invoked, and the Firewire response is
generated by the core and not by any handler.
The cdev API does not change, i.e., userspace is still expected to send
a response for FCP requests; this response is silently ignored.
Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de> (changelog, rebased, whitespace)
2009-12-24 12:05:58 +01:00
|
|
|
list_for_each_entry(handler, &address_handler_list, link) {
|
|
|
|
if (is_enclosing_handler(handler, offset, request->length))
|
|
|
|
handler->address_callback(card, NULL, tcode,
|
|
|
|
destination, source,
|
2010-06-20 22:50:35 +02:00
|
|
|
p->generation, offset,
|
|
|
|
request->data,
|
firewire: fix use of multiple AV/C devices, allow multiple FCP listeners
Control of more than one AV/C device at once --- e.g. camcorders, tape
decks, audio devices, TV tuners --- failed or worked only unreliably,
depending on driver implementation. This affected kernelspace and
userspace drivers alike and was caused by firewire-core's inability to
accept multiple registrations of FCP listeners.
The fix allows multiple address handlers to be registered for the FCP
command and response registers. When a request for these registers is
received, all handlers are invoked, and the Firewire response is
generated by the core and not by any handler.
The cdev API does not change, i.e., userspace is still expected to send
a response for FCP requests; this response is silently ignored.
Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de> (changelog, rebased, whitespace)
2009-12-24 12:05:58 +01:00
|
|
|
request->length,
|
|
|
|
handler->callback_data);
|
|
|
|
}
|
2012-02-18 20:42:00 +01:00
|
|
|
spin_unlock_bh(&address_handler_lock);
|
firewire: fix use of multiple AV/C devices, allow multiple FCP listeners
Control of more than one AV/C device at once --- e.g. camcorders, tape
decks, audio devices, TV tuners --- failed or worked only unreliably,
depending on driver implementation. This affected kernelspace and
userspace drivers alike and was caused by firewire-core's inability to
accept multiple registrations of FCP listeners.
The fix allows multiple address handlers to be registered for the FCP
command and response registers. When a request for these registers is
received, all handlers are invoked, and the Firewire response is
generated by the core and not by any handler.
The cdev API does not change, i.e., userspace is still expected to send
a response for FCP requests; this response is silently ignored.
Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de> (changelog, rebased, whitespace)
2009-12-24 12:05:58 +01:00
|
|
|
|
|
|
|
fw_send_response(card, request, RCODE_COMPLETE);
|
|
|
|
}
|
|
|
|
|
|
|
|
void fw_core_handle_request(struct fw_card *card, struct fw_packet *p)
|
|
|
|
{
|
|
|
|
struct fw_request *request;
|
|
|
|
unsigned long long offset;
|
|
|
|
|
|
|
|
if (p->ack != ACK_PENDING && p->ack != ACK_COMPLETE)
|
|
|
|
return;
|
|
|
|
|
2010-07-16 22:25:51 +02:00
|
|
|
if (TCODE_IS_LINK_INTERNAL(HEADER_GET_TCODE(p->header[0]))) {
|
|
|
|
fw_cdev_handle_phy_packet(card, p);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2010-06-10 08:26:28 +02:00
|
|
|
request = allocate_request(card, p);
|
firewire: fix use of multiple AV/C devices, allow multiple FCP listeners
Control of more than one AV/C device at once --- e.g. camcorders, tape
decks, audio devices, TV tuners --- failed or worked only unreliably,
depending on driver implementation. This affected kernelspace and
userspace drivers alike and was caused by firewire-core's inability to
accept multiple registrations of FCP listeners.
The fix allows multiple address handlers to be registered for the FCP
command and response registers. When a request for these registers is
received, all handlers are invoked, and the Firewire response is
generated by the core and not by any handler.
The cdev API does not change, i.e., userspace is still expected to send
a response for FCP requests; this response is silently ignored.
Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de> (changelog, rebased, whitespace)
2009-12-24 12:05:58 +01:00
|
|
|
if (request == NULL) {
|
|
|
|
/* FIXME: send statically allocated busy packet. */
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
offset = ((u64)HEADER_GET_OFFSET_HIGH(p->header[1]) << 32) |
|
|
|
|
p->header[2];
|
|
|
|
|
|
|
|
if (!is_in_fcp_region(offset, request->length))
|
|
|
|
handle_exclusive_region_request(card, p, request, offset);
|
|
|
|
else
|
|
|
|
handle_fcp_region_request(card, p, request, offset);
|
|
|
|
|
|
|
|
}
|
2006-12-20 01:58:27 +01:00
|
|
|
EXPORT_SYMBOL(fw_core_handle_request);
|
|
|
|
|
2008-12-14 21:47:04 +01:00
|
|
|
void fw_core_handle_response(struct fw_card *card, struct fw_packet *p)
|
2006-12-20 01:58:27 +01:00
|
|
|
{
|
|
|
|
struct fw_transaction *t;
|
|
|
|
unsigned long flags;
|
|
|
|
u32 *data;
|
|
|
|
size_t data_length;
|
2010-06-15 01:22:45 +02:00
|
|
|
int tcode, tlabel, source, rcode;
|
2006-12-20 01:58:27 +01:00
|
|
|
|
2010-06-15 01:22:45 +02:00
|
|
|
tcode = HEADER_GET_TCODE(p->header[0]);
|
|
|
|
tlabel = HEADER_GET_TLABEL(p->header[0]);
|
|
|
|
source = HEADER_GET_SOURCE(p->header[1]);
|
|
|
|
rcode = HEADER_GET_RCODE(p->header[1]);
|
2006-12-20 01:58:27 +01:00
|
|
|
|
|
|
|
spin_lock_irqsave(&card->lock, flags);
|
|
|
|
list_for_each_entry(t, &card->transaction_list, link) {
|
|
|
|
if (t->node_id == source && t->tlabel == tlabel) {
|
2010-12-13 14:56:02 +01:00
|
|
|
if (!try_cancel_split_timeout(t)) {
|
2010-08-18 15:05:02 +02:00
|
|
|
spin_unlock_irqrestore(&card->lock, flags);
|
|
|
|
goto timed_out;
|
|
|
|
}
|
2010-04-27 09:07:00 +02:00
|
|
|
list_del_init(&t->link);
|
2010-04-24 01:27:25 +02:00
|
|
|
card->tlabel_mask &= ~(1ULL << t->tlabel);
|
2006-12-20 01:58:27 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
spin_unlock_irqrestore(&card->lock, flags);
|
|
|
|
|
|
|
|
if (&t->link == &card->transaction_list) {
|
2010-08-18 15:05:02 +02:00
|
|
|
timed_out:
|
firewire: core: prefix log messages with card name
Associate all log messages from firewire-core with the respective card
because some people have more than one card. E.g.
firewire_ohci 0000:04:00.0: added OHCI v1.10 device as card 0, 8 IR + 8 IT contexts, quirks 0x0
firewire_ohci 0000:05:00.0: added OHCI v1.10 device as card 1, 8 IR + 8 IT contexts, quirks 0x0
firewire_core: created device fw0: GUID 0814438400000389, S800
firewire_core: phy config: new root=ffc1, gap_count=5
firewire_core: created device fw1: GUID 0814438400000388, S800
firewire_core: created device fw2: GUID 0001d202e06800d1, S800
turns into
firewire_ohci 0000:04:00.0: added OHCI v1.10 device as card 0, 8 IR + 8 IT contexts, quirks 0x0
firewire_ohci 0000:05:00.0: added OHCI v1.10 device as card 1, 8 IR + 8 IT contexts, quirks 0x0
firewire_core 0000:04:00.0: created device fw0: GUID 0814438400000389, S800
firewire_core 0000:04:00.0: phy config: new root=ffc1, gap_count=5
firewire_core 0000:05:00.0: created device fw1: GUID 0814438400000388, S800
firewire_core 0000:04:00.0: created device fw2: GUID 0001d202e06800d1, S800
This increases the module size slightly; to keep this in check, turn the
former printk wrapper macros into functions. Their implementation is
largely copied from driver core's dev_printk counterparts.
Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
2012-02-18 22:03:14 +01:00
|
|
|
fw_notice(card, "unsolicited response (source %x, tlabel %x)\n",
|
2007-02-06 20:49:30 +01:00
|
|
|
source, tlabel);
|
2006-12-20 01:58:27 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2007-05-08 02:33:32 +02:00
|
|
|
/*
|
|
|
|
* FIXME: sanity check packet, is length correct, does tcodes
|
|
|
|
* and addresses match.
|
|
|
|
*/
|
2006-12-20 01:58:27 +01:00
|
|
|
|
|
|
|
switch (tcode) {
|
|
|
|
case TCODE_READ_QUADLET_RESPONSE:
|
2007-01-26 06:37:57 +01:00
|
|
|
data = (u32 *) &p->header[3];
|
2006-12-20 01:58:27 +01:00
|
|
|
data_length = 4;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TCODE_WRITE_RESPONSE:
|
|
|
|
data = NULL;
|
|
|
|
data_length = 0;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TCODE_READ_BLOCK_RESPONSE:
|
|
|
|
case TCODE_LOCK_RESPONSE:
|
2007-01-26 06:38:26 +01:00
|
|
|
data = p->payload;
|
2007-05-08 02:33:35 +02:00
|
|
|
data_length = HEADER_GET_DATA_LENGTH(p->header[3]);
|
2006-12-20 01:58:27 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
/* Should never happen, this is just to shut up gcc. */
|
|
|
|
data = NULL;
|
|
|
|
data_length = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2008-03-16 00:56:41 +01:00
|
|
|
/*
|
|
|
|
* The response handler may be executed while the request handler
|
|
|
|
* is still pending. Cancel the request handler.
|
|
|
|
*/
|
|
|
|
card->driver->cancel_packet(card, &t->packet);
|
|
|
|
|
2006-12-20 01:58:27 +01:00
|
|
|
t->callback(card, rcode, data, data_length, t->callback_data);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(fw_core_handle_response);
|
|
|
|
|
2007-08-02 20:34:17 +02:00
|
|
|
static const struct fw_address_region topology_map_region =
|
2008-03-08 18:52:03 +01:00
|
|
|
{ .start = CSR_REGISTER_BASE | CSR_TOPOLOGY_MAP,
|
|
|
|
.end = CSR_REGISTER_BASE | CSR_TOPOLOGY_MAP_END, };
|
2007-03-07 18:12:55 +01:00
|
|
|
|
2008-12-14 21:47:04 +01:00
|
|
|
static void handle_topology_map(struct fw_card *card, struct fw_request *request,
|
|
|
|
int tcode, int destination, int source, int generation,
|
2010-06-20 22:50:35 +02:00
|
|
|
unsigned long long offset, void *payload, size_t length,
|
|
|
|
void *callback_data)
|
2007-03-07 18:12:55 +01:00
|
|
|
{
|
2009-10-08 00:42:53 +02:00
|
|
|
int start;
|
2007-03-07 18:12:55 +01:00
|
|
|
|
|
|
|
if (!TCODE_IS_READ_REQUEST(tcode)) {
|
|
|
|
fw_send_response(card, request, RCODE_TYPE_ERROR);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((offset & 3) > 0 || (length & 3) > 0) {
|
|
|
|
fw_send_response(card, request, RCODE_ADDRESS_ERROR);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
start = (offset - topology_map_region.start) / 4;
|
2009-10-08 00:42:53 +02:00
|
|
|
memcpy(payload, &card->topology_map[start], length);
|
2007-03-07 18:12:55 +01:00
|
|
|
|
|
|
|
fw_send_response(card, request, RCODE_COMPLETE);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct fw_address_handler topology_map = {
|
2009-09-08 01:13:53 +02:00
|
|
|
.length = 0x400,
|
2007-03-07 18:12:55 +01:00
|
|
|
.address_callback = handle_topology_map,
|
|
|
|
};
|
|
|
|
|
2007-08-02 20:34:17 +02:00
|
|
|
static const struct fw_address_region registers_region =
|
2008-03-08 18:52:03 +01:00
|
|
|
{ .start = CSR_REGISTER_BASE,
|
|
|
|
.end = CSR_REGISTER_BASE | CSR_CONFIG_ROM, };
|
2007-03-07 18:12:56 +01:00
|
|
|
|
2010-06-10 08:26:28 +02:00
|
|
|
static void update_split_timeout(struct fw_card *card)
|
|
|
|
{
|
|
|
|
unsigned int cycles;
|
|
|
|
|
|
|
|
cycles = card->split_timeout_hi * 8000 + (card->split_timeout_lo >> 19);
|
|
|
|
|
2011-09-19 00:20:48 +02:00
|
|
|
/* minimum per IEEE 1394, maximum which doesn't overflow OHCI */
|
|
|
|
cycles = clamp(cycles, 800u, 3u * 8000u);
|
2010-06-10 08:26:28 +02:00
|
|
|
|
|
|
|
card->split_timeout_cycles = cycles;
|
|
|
|
card->split_timeout_jiffies = DIV_ROUND_UP(cycles * HZ, 8000);
|
|
|
|
}
|
|
|
|
|
2008-12-14 21:47:04 +01:00
|
|
|
static void handle_registers(struct fw_card *card, struct fw_request *request,
|
|
|
|
int tcode, int destination, int source, int generation,
|
2010-06-20 22:50:35 +02:00
|
|
|
unsigned long long offset, void *payload, size_t length,
|
|
|
|
void *callback_data)
|
2007-03-07 18:12:56 +01:00
|
|
|
{
|
2008-03-08 19:18:58 +01:00
|
|
|
int reg = offset & ~CSR_REGISTER_BASE;
|
2007-03-07 18:12:56 +01:00
|
|
|
__be32 *data = payload;
|
2008-05-24 16:41:09 +02:00
|
|
|
int rcode = RCODE_COMPLETE;
|
2010-06-10 08:26:28 +02:00
|
|
|
unsigned long flags;
|
2007-03-07 18:12:56 +01:00
|
|
|
|
|
|
|
switch (reg) {
|
2010-06-12 20:35:21 +02:00
|
|
|
case CSR_PRIORITY_BUDGET:
|
|
|
|
if (!card->priority_budget_implemented) {
|
|
|
|
rcode = RCODE_ADDRESS_ERROR;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/* else fall through */
|
2010-06-10 08:24:03 +02:00
|
|
|
|
2010-06-10 08:25:19 +02:00
|
|
|
case CSR_NODE_IDS:
|
2010-06-12 20:26:51 +02:00
|
|
|
/*
|
|
|
|
* per IEEE 1394-2008 8.3.22.3, not IEEE 1394.1-2004 3.2.8
|
|
|
|
* and 9.6, but interoperable with IEEE 1394.1-2004 bridges
|
|
|
|
*/
|
2010-06-12 20:35:21 +02:00
|
|
|
/* fall through */
|
|
|
|
|
|
|
|
case CSR_STATE_CLEAR:
|
|
|
|
case CSR_STATE_SET:
|
|
|
|
case CSR_CYCLE_TIME:
|
|
|
|
case CSR_BUS_TIME:
|
|
|
|
case CSR_BUSY_TIMEOUT:
|
2010-06-10 08:25:19 +02:00
|
|
|
if (tcode == TCODE_READ_QUADLET_REQUEST)
|
2010-06-12 20:35:52 +02:00
|
|
|
*data = cpu_to_be32(card->driver->read_csr(card, reg));
|
2010-06-10 08:25:19 +02:00
|
|
|
else if (tcode == TCODE_WRITE_QUADLET_REQUEST)
|
2010-06-12 20:35:52 +02:00
|
|
|
card->driver->write_csr(card, reg, be32_to_cpu(*data));
|
2010-06-10 08:25:19 +02:00
|
|
|
else
|
|
|
|
rcode = RCODE_TYPE_ERROR;
|
|
|
|
break;
|
|
|
|
|
2010-06-10 08:25:46 +02:00
|
|
|
case CSR_RESET_START:
|
2010-06-10 08:37:15 +02:00
|
|
|
if (tcode == TCODE_WRITE_QUADLET_REQUEST)
|
2010-06-12 20:35:52 +02:00
|
|
|
card->driver->write_csr(card, CSR_STATE_CLEAR,
|
|
|
|
CSR_STATE_BIT_ABDICATE);
|
2010-06-10 08:37:15 +02:00
|
|
|
else
|
2010-06-10 08:25:46 +02:00
|
|
|
rcode = RCODE_TYPE_ERROR;
|
|
|
|
break;
|
|
|
|
|
2010-06-10 08:26:28 +02:00
|
|
|
case CSR_SPLIT_TIMEOUT_HI:
|
|
|
|
if (tcode == TCODE_READ_QUADLET_REQUEST) {
|
|
|
|
*data = cpu_to_be32(card->split_timeout_hi);
|
|
|
|
} else if (tcode == TCODE_WRITE_QUADLET_REQUEST) {
|
|
|
|
spin_lock_irqsave(&card->lock, flags);
|
|
|
|
card->split_timeout_hi = be32_to_cpu(*data) & 7;
|
|
|
|
update_split_timeout(card);
|
|
|
|
spin_unlock_irqrestore(&card->lock, flags);
|
|
|
|
} else {
|
|
|
|
rcode = RCODE_TYPE_ERROR;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CSR_SPLIT_TIMEOUT_LO:
|
|
|
|
if (tcode == TCODE_READ_QUADLET_REQUEST) {
|
|
|
|
*data = cpu_to_be32(card->split_timeout_lo);
|
|
|
|
} else if (tcode == TCODE_WRITE_QUADLET_REQUEST) {
|
|
|
|
spin_lock_irqsave(&card->lock, flags);
|
|
|
|
card->split_timeout_lo =
|
|
|
|
be32_to_cpu(*data) & 0xfff80000;
|
|
|
|
update_split_timeout(card);
|
|
|
|
spin_unlock_irqrestore(&card->lock, flags);
|
|
|
|
} else {
|
|
|
|
rcode = RCODE_TYPE_ERROR;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2010-06-10 08:35:37 +02:00
|
|
|
case CSR_MAINT_UTILITY:
|
|
|
|
if (tcode == TCODE_READ_QUADLET_REQUEST)
|
|
|
|
*data = card->maint_utility_register;
|
|
|
|
else if (tcode == TCODE_WRITE_QUADLET_REQUEST)
|
|
|
|
card->maint_utility_register = *data;
|
|
|
|
else
|
|
|
|
rcode = RCODE_TYPE_ERROR;
|
|
|
|
break;
|
|
|
|
|
2008-05-24 16:41:09 +02:00
|
|
|
case CSR_BROADCAST_CHANNEL:
|
|
|
|
if (tcode == TCODE_READ_QUADLET_REQUEST)
|
|
|
|
*data = cpu_to_be32(card->broadcast_channel);
|
|
|
|
else if (tcode == TCODE_WRITE_QUADLET_REQUEST)
|
|
|
|
card->broadcast_channel =
|
|
|
|
(be32_to_cpu(*data) & BROADCAST_CHANNEL_VALID) |
|
|
|
|
BROADCAST_CHANNEL_INITIAL;
|
|
|
|
else
|
|
|
|
rcode = RCODE_TYPE_ERROR;
|
2007-03-07 18:12:56 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
case CSR_BUS_MANAGER_ID:
|
|
|
|
case CSR_BANDWIDTH_AVAILABLE:
|
|
|
|
case CSR_CHANNELS_AVAILABLE_HI:
|
|
|
|
case CSR_CHANNELS_AVAILABLE_LO:
|
2007-05-08 02:33:32 +02:00
|
|
|
/*
|
|
|
|
* FIXME: these are handled by the OHCI hardware and
|
2007-03-07 18:12:56 +01:00
|
|
|
* the stack never sees these request. If we add
|
|
|
|
* support for a new type of controller that doesn't
|
|
|
|
* handle this in hardware we need to deal with these
|
2007-05-08 02:33:32 +02:00
|
|
|
* transactions.
|
|
|
|
*/
|
2007-03-07 18:12:56 +01:00
|
|
|
BUG();
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2008-05-24 16:41:09 +02:00
|
|
|
rcode = RCODE_ADDRESS_ERROR;
|
2007-03-07 18:12:56 +01:00
|
|
|
break;
|
|
|
|
}
|
2008-05-24 16:41:09 +02:00
|
|
|
|
|
|
|
fw_send_response(card, request, rcode);
|
2007-03-07 18:12:56 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static struct fw_address_handler registers = {
|
|
|
|
.length = 0x400,
|
|
|
|
.address_callback = handle_registers,
|
|
|
|
};
|
|
|
|
|
2006-12-20 01:58:27 +01:00
|
|
|
MODULE_AUTHOR("Kristian Hoegsberg <krh@bitplanet.net>");
|
|
|
|
MODULE_DESCRIPTION("Core IEEE1394 transaction logic");
|
|
|
|
MODULE_LICENSE("GPL");
|
|
|
|
|
2007-03-07 18:12:36 +01:00
|
|
|
static const u32 vendor_textual_descriptor[] = {
|
2006-12-20 01:58:27 +01:00
|
|
|
/* textual descriptor leaf () */
|
2007-03-07 18:12:36 +01:00
|
|
|
0x00060000,
|
2006-12-20 01:58:27 +01:00
|
|
|
0x00000000,
|
|
|
|
0x00000000,
|
|
|
|
0x4c696e75, /* L i n u */
|
|
|
|
0x78204669, /* x F i */
|
|
|
|
0x72657769, /* r e w i */
|
2007-03-07 18:12:36 +01:00
|
|
|
0x72650000, /* r e */
|
2006-12-20 01:58:27 +01:00
|
|
|
};
|
|
|
|
|
2007-03-07 18:12:36 +01:00
|
|
|
static const u32 model_textual_descriptor[] = {
|
|
|
|
/* model descriptor leaf () */
|
|
|
|
0x00030000,
|
|
|
|
0x00000000,
|
|
|
|
0x00000000,
|
|
|
|
0x4a756a75, /* J u j u */
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct fw_descriptor vendor_id_descriptor = {
|
|
|
|
.length = ARRAY_SIZE(vendor_textual_descriptor),
|
|
|
|
.immediate = 0x03d00d1e,
|
2006-12-20 01:58:27 +01:00
|
|
|
.key = 0x81000000,
|
2007-03-07 18:12:36 +01:00
|
|
|
.data = vendor_textual_descriptor,
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct fw_descriptor model_id_descriptor = {
|
|
|
|
.length = ARRAY_SIZE(model_textual_descriptor),
|
|
|
|
.immediate = 0x17000001,
|
|
|
|
.key = 0x81000000,
|
|
|
|
.data = model_textual_descriptor,
|
2006-12-20 01:58:27 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
static int __init fw_core_init(void)
|
|
|
|
{
|
2008-12-14 21:45:45 +01:00
|
|
|
int ret;
|
2006-12-20 01:58:27 +01:00
|
|
|
|
firewire: sbp2: parallelize login, reconnect, logout
The struct sbp2_logical_unit.work items can all be executed in parallel
but are not reentrant. Furthermore, reconnect or re-login work must be
executed in a WQ_MEM_RECLAIM workqueue.
Hence replace the old single-threaded firewire-sbp2 workqueue by a
concurrency-managed but non-reentrant workqueue with rescuer.
firewire-core already maintains one, hence use this one.
In earlier versions of this change, I observed occasional failures of
parallel INQUIRY to an Initio INIC-2430 FireWire 800 to dual IDE bridge.
More testing indicates that parallel INQUIRY is not actually a problem,
but too quick successions of logout and login + INQUIRY, e.g. a quick
sequence of cable plugout and plugin, can result in failed INQUIRY.
This does not seem to be something that should or could be addressed by
serialization.
Another dual-LU device to which I currently have access to, an
OXUF924DSB FireWire 800 to dual SATA bridge with firmware from MacPower,
has been successfully tested with this too.
This change is beneficial to environments with two or more FireWire
storage devices, especially if they are located on the same bus.
Management tasks that should be performed as soon and as quickly as
possible, especially reconnect, are no longer held up by tasks on other
devices that may take a long time, especially login with INQUIRY and sd
or sr driver probe.
Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
2011-05-01 20:50:31 +02:00
|
|
|
fw_workqueue = alloc_workqueue("firewire",
|
|
|
|
WQ_NON_REENTRANT | WQ_MEM_RECLAIM, 0);
|
|
|
|
if (!fw_workqueue)
|
firewire: core: use non-reentrant workqueue with rescuer
firewire-core manages the following types of work items:
fw_card.br_work:
- resets the bus on a card and possibly sends a PHY packet before that
- does not sleep for long or not at all
- is scheduled via fw_schedule_bus_reset() by
- firewire-ohci's pci_probe method
- firewire-ohci's set_config_rom method, called by kernelspace
protocol drivers and userspace drivers which add/remove
Configuration ROM descriptors
- userspace drivers which use the bus reset ioctl
- itself if the last reset happened less than 2 seconds ago
fw_card.bm_work:
- performs bus management duties
- usually does not (but may in corner cases) sleep for long
- is scheduled via fw_schedule_bm_work() by
- firewire-ohci's self-ID-complete IRQ handler tasklet
- firewire-core's fw_device.work instances whenever the root node
device was (successfully or unsuccessfully) discovered,
refreshed, or rediscovered
- itself in case of resource allocation failures or in order to
obey the 125ms bus manager arbitration interval
fw_device.work:
- performs node probe, update, shutdown, revival, removal; including
kernel driver probe, update, shutdown and bus reset notification to
userspace drivers
- usually sleeps moderately long, in corner cases very long
- is scheduled by
- firewire-ohci's self-ID-complete IRQ handler tasklet via the
core's fw_node_event
- firewire-ohci's pci_remove method via core's fw_destroy_nodes/
fw_node_event
- itself during retries, e.g. while a node is powering up
iso_resource.work:
- accesses registers at the Isochronous Resource Manager node
- usually does not (but may in corner cases) sleep for long
- is scheduled via schedule_iso_resource() by
- the owning userspace driver at addition and removal of the
resource
- firewire-core's fw_device.work instances after bus reset
- itself in case of resource allocation if necessary to obey the
1000ms reallocation period after bus reset
fw_card.br_work instances should not, and instances of the others must
not, be executed in parallel by multiple CPUs -- but were not protected
against that. Hence allocate a non-reentrant workqueue for them.
fw_device.work may be used in the memory reclaim path in case of SBP-2
device updates. Hence we need a workqueue with rescuer and cannot use
system_nrt_wq.
Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
Reviewed-by: Tejun Heo <tj@kernel.org>
2010-10-13 13:39:46 +02:00
|
|
|
return -ENOMEM;
|
|
|
|
|
2008-12-14 21:45:45 +01:00
|
|
|
ret = bus_register(&fw_bus_type);
|
firewire: core: use non-reentrant workqueue with rescuer
firewire-core manages the following types of work items:
fw_card.br_work:
- resets the bus on a card and possibly sends a PHY packet before that
- does not sleep for long or not at all
- is scheduled via fw_schedule_bus_reset() by
- firewire-ohci's pci_probe method
- firewire-ohci's set_config_rom method, called by kernelspace
protocol drivers and userspace drivers which add/remove
Configuration ROM descriptors
- userspace drivers which use the bus reset ioctl
- itself if the last reset happened less than 2 seconds ago
fw_card.bm_work:
- performs bus management duties
- usually does not (but may in corner cases) sleep for long
- is scheduled via fw_schedule_bm_work() by
- firewire-ohci's self-ID-complete IRQ handler tasklet
- firewire-core's fw_device.work instances whenever the root node
device was (successfully or unsuccessfully) discovered,
refreshed, or rediscovered
- itself in case of resource allocation failures or in order to
obey the 125ms bus manager arbitration interval
fw_device.work:
- performs node probe, update, shutdown, revival, removal; including
kernel driver probe, update, shutdown and bus reset notification to
userspace drivers
- usually sleeps moderately long, in corner cases very long
- is scheduled by
- firewire-ohci's self-ID-complete IRQ handler tasklet via the
core's fw_node_event
- firewire-ohci's pci_remove method via core's fw_destroy_nodes/
fw_node_event
- itself during retries, e.g. while a node is powering up
iso_resource.work:
- accesses registers at the Isochronous Resource Manager node
- usually does not (but may in corner cases) sleep for long
- is scheduled via schedule_iso_resource() by
- the owning userspace driver at addition and removal of the
resource
- firewire-core's fw_device.work instances after bus reset
- itself in case of resource allocation if necessary to obey the
1000ms reallocation period after bus reset
fw_card.br_work instances should not, and instances of the others must
not, be executed in parallel by multiple CPUs -- but were not protected
against that. Hence allocate a non-reentrant workqueue for them.
fw_device.work may be used in the memory reclaim path in case of SBP-2
device updates. Hence we need a workqueue with rescuer and cannot use
system_nrt_wq.
Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
Reviewed-by: Tejun Heo <tj@kernel.org>
2010-10-13 13:39:46 +02:00
|
|
|
if (ret < 0) {
|
firewire: sbp2: parallelize login, reconnect, logout
The struct sbp2_logical_unit.work items can all be executed in parallel
but are not reentrant. Furthermore, reconnect or re-login work must be
executed in a WQ_MEM_RECLAIM workqueue.
Hence replace the old single-threaded firewire-sbp2 workqueue by a
concurrency-managed but non-reentrant workqueue with rescuer.
firewire-core already maintains one, hence use this one.
In earlier versions of this change, I observed occasional failures of
parallel INQUIRY to an Initio INIC-2430 FireWire 800 to dual IDE bridge.
More testing indicates that parallel INQUIRY is not actually a problem,
but too quick successions of logout and login + INQUIRY, e.g. a quick
sequence of cable plugout and plugin, can result in failed INQUIRY.
This does not seem to be something that should or could be addressed by
serialization.
Another dual-LU device to which I currently have access to, an
OXUF924DSB FireWire 800 to dual SATA bridge with firmware from MacPower,
has been successfully tested with this too.
This change is beneficial to environments with two or more FireWire
storage devices, especially if they are located on the same bus.
Management tasks that should be performed as soon and as quickly as
possible, especially reconnect, are no longer held up by tasks on other
devices that may take a long time, especially login with INQUIRY and sd
or sr driver probe.
Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
2011-05-01 20:50:31 +02:00
|
|
|
destroy_workqueue(fw_workqueue);
|
2008-12-14 21:45:45 +01:00
|
|
|
return ret;
|
firewire: core: use non-reentrant workqueue with rescuer
firewire-core manages the following types of work items:
fw_card.br_work:
- resets the bus on a card and possibly sends a PHY packet before that
- does not sleep for long or not at all
- is scheduled via fw_schedule_bus_reset() by
- firewire-ohci's pci_probe method
- firewire-ohci's set_config_rom method, called by kernelspace
protocol drivers and userspace drivers which add/remove
Configuration ROM descriptors
- userspace drivers which use the bus reset ioctl
- itself if the last reset happened less than 2 seconds ago
fw_card.bm_work:
- performs bus management duties
- usually does not (but may in corner cases) sleep for long
- is scheduled via fw_schedule_bm_work() by
- firewire-ohci's self-ID-complete IRQ handler tasklet
- firewire-core's fw_device.work instances whenever the root node
device was (successfully or unsuccessfully) discovered,
refreshed, or rediscovered
- itself in case of resource allocation failures or in order to
obey the 125ms bus manager arbitration interval
fw_device.work:
- performs node probe, update, shutdown, revival, removal; including
kernel driver probe, update, shutdown and bus reset notification to
userspace drivers
- usually sleeps moderately long, in corner cases very long
- is scheduled by
- firewire-ohci's self-ID-complete IRQ handler tasklet via the
core's fw_node_event
- firewire-ohci's pci_remove method via core's fw_destroy_nodes/
fw_node_event
- itself during retries, e.g. while a node is powering up
iso_resource.work:
- accesses registers at the Isochronous Resource Manager node
- usually does not (but may in corner cases) sleep for long
- is scheduled via schedule_iso_resource() by
- the owning userspace driver at addition and removal of the
resource
- firewire-core's fw_device.work instances after bus reset
- itself in case of resource allocation if necessary to obey the
1000ms reallocation period after bus reset
fw_card.br_work instances should not, and instances of the others must
not, be executed in parallel by multiple CPUs -- but were not protected
against that. Hence allocate a non-reentrant workqueue for them.
fw_device.work may be used in the memory reclaim path in case of SBP-2
device updates. Hence we need a workqueue with rescuer and cannot use
system_nrt_wq.
Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
Reviewed-by: Tejun Heo <tj@kernel.org>
2010-10-13 13:39:46 +02:00
|
|
|
}
|
2006-12-20 01:58:27 +01:00
|
|
|
|
2007-03-07 18:12:44 +01:00
|
|
|
fw_cdev_major = register_chrdev(0, "firewire", &fw_device_ops);
|
|
|
|
if (fw_cdev_major < 0) {
|
|
|
|
bus_unregister(&fw_bus_type);
|
firewire: sbp2: parallelize login, reconnect, logout
The struct sbp2_logical_unit.work items can all be executed in parallel
but are not reentrant. Furthermore, reconnect or re-login work must be
executed in a WQ_MEM_RECLAIM workqueue.
Hence replace the old single-threaded firewire-sbp2 workqueue by a
concurrency-managed but non-reentrant workqueue with rescuer.
firewire-core already maintains one, hence use this one.
In earlier versions of this change, I observed occasional failures of
parallel INQUIRY to an Initio INIC-2430 FireWire 800 to dual IDE bridge.
More testing indicates that parallel INQUIRY is not actually a problem,
but too quick successions of logout and login + INQUIRY, e.g. a quick
sequence of cable plugout and plugin, can result in failed INQUIRY.
This does not seem to be something that should or could be addressed by
serialization.
Another dual-LU device to which I currently have access to, an
OXUF924DSB FireWire 800 to dual SATA bridge with firmware from MacPower,
has been successfully tested with this too.
This change is beneficial to environments with two or more FireWire
storage devices, especially if they are located on the same bus.
Management tasks that should be performed as soon and as quickly as
possible, especially reconnect, are no longer held up by tasks on other
devices that may take a long time, especially login with INQUIRY and sd
or sr driver probe.
Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
2011-05-01 20:50:31 +02:00
|
|
|
destroy_workqueue(fw_workqueue);
|
2007-03-07 18:12:44 +01:00
|
|
|
return fw_cdev_major;
|
|
|
|
}
|
|
|
|
|
2008-12-14 21:45:14 +01:00
|
|
|
fw_core_add_address_handler(&topology_map, &topology_map_region);
|
|
|
|
fw_core_add_address_handler(®isters, ®isters_region);
|
|
|
|
fw_core_add_descriptor(&vendor_id_descriptor);
|
|
|
|
fw_core_add_descriptor(&model_id_descriptor);
|
2006-12-20 01:58:27 +01:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void __exit fw_core_cleanup(void)
|
|
|
|
{
|
2007-03-07 18:12:44 +01:00
|
|
|
unregister_chrdev(fw_cdev_major, "firewire");
|
2006-12-20 01:58:27 +01:00
|
|
|
bus_unregister(&fw_bus_type);
|
firewire: sbp2: parallelize login, reconnect, logout
The struct sbp2_logical_unit.work items can all be executed in parallel
but are not reentrant. Furthermore, reconnect or re-login work must be
executed in a WQ_MEM_RECLAIM workqueue.
Hence replace the old single-threaded firewire-sbp2 workqueue by a
concurrency-managed but non-reentrant workqueue with rescuer.
firewire-core already maintains one, hence use this one.
In earlier versions of this change, I observed occasional failures of
parallel INQUIRY to an Initio INIC-2430 FireWire 800 to dual IDE bridge.
More testing indicates that parallel INQUIRY is not actually a problem,
but too quick successions of logout and login + INQUIRY, e.g. a quick
sequence of cable plugout and plugin, can result in failed INQUIRY.
This does not seem to be something that should or could be addressed by
serialization.
Another dual-LU device to which I currently have access to, an
OXUF924DSB FireWire 800 to dual SATA bridge with firmware from MacPower,
has been successfully tested with this too.
This change is beneficial to environments with two or more FireWire
storage devices, especially if they are located on the same bus.
Management tasks that should be performed as soon and as quickly as
possible, especially reconnect, are no longer held up by tasks on other
devices that may take a long time, especially login with INQUIRY and sd
or sr driver probe.
Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
2011-05-01 20:50:31 +02:00
|
|
|
destroy_workqueue(fw_workqueue);
|
2008-11-24 20:40:00 +01:00
|
|
|
idr_destroy(&fw_device_idr);
|
2006-12-20 01:58:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
module_init(fw_core_init);
|
|
|
|
module_exit(fw_core_cleanup);
|