summaryrefslogtreecommitdiffstats
path: root/drivers/firewire
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2008-10-17 00:02:24 +0200
committerLinus Torvalds <torvalds@linux-foundation.org>2008-10-17 00:02:24 +0200
commit1eee21abaf54338b379b33d85b28b495292c2211 (patch)
treef51f3e270c7e075f2844a2c5e400cfc1b9481ce3 /drivers/firewire
parentlib: remove defining macros for strict_strto?? (diff)
parentfirewire: Add more documentation to firewire-cdev.h (diff)
downloadlinux-1eee21abaf54338b379b33d85b28b495292c2211.tar.xz
linux-1eee21abaf54338b379b33d85b28b495292c2211.zip
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ieee1394/linux1394-2.6
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ieee1394/linux1394-2.6: firewire: Add more documentation to firewire-cdev.h firewire: fix ioctl() return code firewire: fix setting tag and sy in iso transmission firewire: fw-sbp2: fix another small generation access bug firewire: fw-sbp2: enforce s/g segment size limit firewire: fw_send_request_sync() ieee1394: survive a few seconds connection loss ieee1394: nodemgr clean up class iterators ieee1394: dv1394, video1394: remove unnecessary expressions ieee1394: raw1394: make write() thread-safe ieee1394: raw1394: narrow down the state_mutex protected region ieee1394: raw1394: replace BKL by local mutex, make ioctl() and mmap() thread-safe ieee1394: sbp2: enforce s/g segment size limit ieee1394: sbp2: check for DMA mapping failures ieee1394: sbp2: stricter dma_sync ieee1394: Use DIV_ROUND_UP
Diffstat (limited to 'drivers/firewire')
-rw-r--r--drivers/firewire/fw-card.c56
-rw-r--r--drivers/firewire/fw-cdev.c6
-rw-r--r--drivers/firewire/fw-device.c37
-rw-r--r--drivers/firewire/fw-sbp2.c116
-rw-r--r--drivers/firewire/fw-transaction.c48
-rw-r--r--drivers/firewire/fw-transaction.h9
6 files changed, 117 insertions, 155 deletions
diff --git a/drivers/firewire/fw-card.c b/drivers/firewire/fw-card.c
index bbd73a406e53..418c18f07e9d 100644
--- a/drivers/firewire/fw-card.c
+++ b/drivers/firewire/fw-card.c
@@ -189,39 +189,16 @@ static const char gap_count_table[] = {
63, 5, 7, 8, 10, 13, 16, 18, 21, 24, 26, 29, 32, 35, 37, 40
};
-struct bm_data {
- struct fw_transaction t;
- struct {
- __be32 arg;
- __be32 data;
- } lock;
- u32 old;
- int rcode;
- struct completion done;
-};
-
-static void
-complete_bm_lock(struct fw_card *card, int rcode,
- void *payload, size_t length, void *data)
-{
- struct bm_data *bmd = data;
-
- if (rcode == RCODE_COMPLETE)
- bmd->old = be32_to_cpu(*(__be32 *) payload);
- bmd->rcode = rcode;
- complete(&bmd->done);
-}
-
static void
fw_card_bm_work(struct work_struct *work)
{
struct fw_card *card = container_of(work, struct fw_card, work.work);
struct fw_device *root_device;
struct fw_node *root_node, *local_node;
- struct bm_data bmd;
unsigned long flags;
- int root_id, new_root_id, irm_id, gap_count, generation, grace;
+ int root_id, new_root_id, irm_id, gap_count, generation, grace, rcode;
bool do_reset = false;
+ __be32 lock_data[2];
spin_lock_irqsave(&card->lock, flags);
local_node = card->local_node;
@@ -263,33 +240,28 @@ fw_card_bm_work(struct work_struct *work)
goto pick_me;
}
- bmd.lock.arg = cpu_to_be32(0x3f);
- bmd.lock.data = cpu_to_be32(local_node->node_id);
+ lock_data[0] = cpu_to_be32(0x3f);
+ lock_data[1] = cpu_to_be32(local_node->node_id);
spin_unlock_irqrestore(&card->lock, flags);
- init_completion(&bmd.done);
- fw_send_request(card, &bmd.t, TCODE_LOCK_COMPARE_SWAP,
- irm_id, generation,
- SCODE_100, CSR_REGISTER_BASE + CSR_BUS_MANAGER_ID,
- &bmd.lock, sizeof(bmd.lock),
- complete_bm_lock, &bmd);
- wait_for_completion(&bmd.done);
+ 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 (bmd.rcode == RCODE_GENERATION) {
- /*
- * Another bus reset happened. Just return,
- * the BM work has been rescheduled.
- */
+ if (rcode == RCODE_GENERATION)
+ /* Another bus reset, BM work has been rescheduled. */
goto out;
- }
- if (bmd.rcode == RCODE_COMPLETE && bmd.old != 0x3f)
+ if (rcode == RCODE_COMPLETE &&
+ lock_data[0] != cpu_to_be32(0x3f))
/* Somebody else is BM, let them do the work. */
goto out;
spin_lock_irqsave(&card->lock, flags);
- if (bmd.rcode != RCODE_COMPLETE) {
+
+ if (rcode != RCODE_COMPLETE) {
/*
* The lock request failed, maybe the IRM
* isn't really IRM capable after all. Let's
diff --git a/drivers/firewire/fw-cdev.c b/drivers/firewire/fw-cdev.c
index 2e6d5848d217..ed03234cbea8 100644
--- a/drivers/firewire/fw-cdev.c
+++ b/drivers/firewire/fw-cdev.c
@@ -720,8 +720,8 @@ static int ioctl_create_iso_context(struct client *client, void *buffer)
#define GET_PAYLOAD_LENGTH(v) ((v) & 0xffff)
#define GET_INTERRUPT(v) (((v) >> 16) & 0x01)
#define GET_SKIP(v) (((v) >> 17) & 0x01)
-#define GET_TAG(v) (((v) >> 18) & 0x02)
-#define GET_SY(v) (((v) >> 20) & 0x04)
+#define GET_TAG(v) (((v) >> 18) & 0x03)
+#define GET_SY(v) (((v) >> 20) & 0x0f)
#define GET_HEADER_LENGTH(v) (((v) >> 24) & 0xff)
static int ioctl_queue_iso(struct client *client, void *buffer)
@@ -913,7 +913,7 @@ dispatch_ioctl(struct client *client, unsigned int cmd, void __user *arg)
return -EFAULT;
}
- return 0;
+ return retval;
}
static long
diff --git a/drivers/firewire/fw-device.c b/drivers/firewire/fw-device.c
index 0855fb5568e8..3fccdd484100 100644
--- a/drivers/firewire/fw-device.c
+++ b/drivers/firewire/fw-device.c
@@ -381,46 +381,21 @@ static struct device_attribute fw_device_attributes[] = {
__ATTR_NULL,
};
-struct read_quadlet_callback_data {
- struct completion done;
- int rcode;
- u32 data;
-};
-
-static void
-complete_transaction(struct fw_card *card, int rcode,
- void *payload, size_t length, void *data)
-{
- struct read_quadlet_callback_data *callback_data = data;
-
- if (rcode == RCODE_COMPLETE)
- callback_data->data = be32_to_cpu(*(__be32 *)payload);
- callback_data->rcode = rcode;
- complete(&callback_data->done);
-}
-
static int
read_rom(struct fw_device *device, int generation, int index, u32 *data)
{
- struct read_quadlet_callback_data callback_data;
- struct fw_transaction t;
- u64 offset;
+ int rcode;
/* device->node_id, accessed below, must not be older than generation */
smp_rmb();
- init_completion(&callback_data.done);
-
- offset = (CSR_REGISTER_BASE | CSR_CONFIG_ROM) + index * 4;
- fw_send_request(device->card, &t, TCODE_READ_QUADLET_REQUEST,
+ rcode = fw_run_transaction(device->card, TCODE_READ_QUADLET_REQUEST,
device->node_id, generation, device->max_speed,
- offset, NULL, 4, complete_transaction, &callback_data);
-
- wait_for_completion(&callback_data.done);
-
- *data = callback_data.data;
+ (CSR_REGISTER_BASE | CSR_CONFIG_ROM) + index * 4,
+ data, 4);
+ be32_to_cpus(data);
- return callback_data.rcode;
+ return rcode;
}
#define READ_BIB_ROM_SIZE 256
diff --git a/drivers/firewire/fw-sbp2.c b/drivers/firewire/fw-sbp2.c
index aaff50ebba1d..ef0b9b419c27 100644
--- a/drivers/firewire/fw-sbp2.c
+++ b/drivers/firewire/fw-sbp2.c
@@ -29,6 +29,7 @@
*/
#include <linux/blkdev.h>
+#include <linux/bug.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/dma-mapping.h>
@@ -181,10 +182,16 @@ struct sbp2_target {
#define SBP2_MAX_LOGIN_ORB_TIMEOUT 40000U /* Timeout in ms */
#define SBP2_ORB_TIMEOUT 2000U /* Timeout in ms */
#define SBP2_ORB_NULL 0x80000000
-#define SBP2_MAX_SG_ELEMENT_LENGTH 0xf000
#define SBP2_RETRY_LIMIT 0xf /* 15 retries */
#define SBP2_CYCLE_LIMIT (0xc8 << 12) /* 200 125us cycles */
+/*
+ * The default maximum s/g segment size of a FireWire controller is
+ * usually 0x10000, but SBP-2 only allows 0xffff. Since buffers have to
+ * be quadlet-aligned, we set the length limit to 0xffff & ~3.
+ */
+#define SBP2_MAX_SEG_SIZE 0xfffc
+
/* Unit directory keys */
#define SBP2_CSR_UNIT_CHARACTERISTICS 0x3a
#define SBP2_CSR_FIRMWARE_REVISION 0x3c
@@ -621,25 +628,15 @@ sbp2_send_management_orb(struct sbp2_logical_unit *lu, int node_id,
return retval;
}
-static void
-complete_agent_reset_write(struct fw_card *card, int rcode,
- void *payload, size_t length, void *done)
-{
- complete(done);
-}
-
static void sbp2_agent_reset(struct sbp2_logical_unit *lu)
{
struct fw_device *device = fw_device(lu->tgt->unit->device.parent);
- DECLARE_COMPLETION_ONSTACK(done);
- struct fw_transaction t;
- static u32 z;
+ __be32 d = 0;
- fw_send_request(device->card, &t, TCODE_WRITE_QUADLET_REQUEST,
- lu->tgt->node_id, lu->generation, device->max_speed,
- lu->command_block_agent_address + SBP2_AGENT_RESET,
- &z, sizeof(z), complete_agent_reset_write, &done);
- wait_for_completion(&done);
+ fw_run_transaction(device->card, TCODE_WRITE_QUADLET_REQUEST,
+ lu->tgt->node_id, lu->generation, device->max_speed,
+ lu->command_block_agent_address + SBP2_AGENT_RESET,
+ &d, sizeof(d));
}
static void
@@ -653,7 +650,7 @@ static void sbp2_agent_reset_no_wait(struct sbp2_logical_unit *lu)
{
struct fw_device *device = fw_device(lu->tgt->unit->device.parent);
struct fw_transaction *t;
- static u32 z;
+ static __be32 d;
t = kmalloc(sizeof(*t), GFP_ATOMIC);
if (t == NULL)
@@ -662,7 +659,7 @@ static void sbp2_agent_reset_no_wait(struct sbp2_logical_unit *lu)
fw_send_request(device->card, t, TCODE_WRITE_QUADLET_REQUEST,
lu->tgt->node_id, lu->generation, device->max_speed,
lu->command_block_agent_address + SBP2_AGENT_RESET,
- &z, sizeof(z), complete_agent_reset_write_no_wait, t);
+ &d, sizeof(d), complete_agent_reset_write_no_wait, t);
}
static void sbp2_set_generation(struct sbp2_logical_unit *lu, int generation)
@@ -823,13 +820,6 @@ static void sbp2_target_put(struct sbp2_target *tgt)
kref_put(&tgt->kref, sbp2_release_target);
}
-static void
-complete_set_busy_timeout(struct fw_card *card, int rcode,
- void *payload, size_t length, void *done)
-{
- complete(done);
-}
-
/*
* Write retransmit retry values into the BUSY_TIMEOUT register.
* - The single-phase retry protocol is supported by all SBP-2 devices, but the
@@ -849,17 +839,12 @@ complete_set_busy_timeout(struct fw_card *card, int rcode,
static void sbp2_set_busy_timeout(struct sbp2_logical_unit *lu)
{
struct fw_device *device = fw_device(lu->tgt->unit->device.parent);
- DECLARE_COMPLETION_ONSTACK(done);
- struct fw_transaction t;
- static __be32 busy_timeout;
-
- busy_timeout = cpu_to_be32(SBP2_CYCLE_LIMIT | SBP2_RETRY_LIMIT);
+ __be32 d = cpu_to_be32(SBP2_CYCLE_LIMIT | SBP2_RETRY_LIMIT);
- fw_send_request(device->card, &t, TCODE_WRITE_QUADLET_REQUEST,
- lu->tgt->node_id, lu->generation, device->max_speed,
- CSR_REGISTER_BASE + CSR_BUSY_TIMEOUT, &busy_timeout,
- sizeof(busy_timeout), complete_set_busy_timeout, &done);
- wait_for_completion(&done);
+ fw_run_transaction(device->card, TCODE_WRITE_QUADLET_REQUEST,
+ lu->tgt->node_id, lu->generation, device->max_speed,
+ CSR_REGISTER_BASE + CSR_BUSY_TIMEOUT,
+ &d, sizeof(d));
}
static void sbp2_reconnect(struct work_struct *work);
@@ -1121,6 +1106,10 @@ static int sbp2_probe(struct device *dev)
struct Scsi_Host *shost;
u32 model, firmware_revision;
+ if (dma_get_max_seg_size(device->card->device) > SBP2_MAX_SEG_SIZE)
+ BUG_ON(dma_set_max_seg_size(device->card->device,
+ SBP2_MAX_SEG_SIZE));
+
shost = scsi_host_alloc(&scsi_driver_template, sizeof(*tgt));
if (shost == NULL)
return -ENOMEM;
@@ -1369,14 +1358,12 @@ static int
sbp2_map_scatterlist(struct sbp2_command_orb *orb, struct fw_device *device,
struct sbp2_logical_unit *lu)
{
- struct scatterlist *sg;
- int sg_len, l, i, j, count;
- dma_addr_t sg_addr;
-
- sg = scsi_sglist(orb->cmd);
- count = dma_map_sg(device->card->device, sg, scsi_sg_count(orb->cmd),
- orb->cmd->sc_data_direction);
- if (count == 0)
+ struct scatterlist *sg = scsi_sglist(orb->cmd);
+ int i, n;
+
+ n = dma_map_sg(device->card->device, sg, scsi_sg_count(orb->cmd),
+ orb->cmd->sc_data_direction);
+ if (n == 0)
goto fail;
/*
@@ -1386,7 +1373,7 @@ sbp2_map_scatterlist(struct sbp2_command_orb *orb, struct fw_device *device,
* as the second generation iPod which doesn't support page
* tables.
*/
- if (count == 1 && sg_dma_len(sg) < SBP2_MAX_SG_ELEMENT_LENGTH) {
+ if (n == 1) {
orb->request.data_descriptor.high =
cpu_to_be32(lu->tgt->address_high);
orb->request.data_descriptor.low =
@@ -1396,29 +1383,9 @@ sbp2_map_scatterlist(struct sbp2_command_orb *orb, struct fw_device *device,
return 0;
}
- /*
- * Convert the scatterlist to an sbp2 page table. If any
- * scatterlist entries are too big for sbp2, we split them as we
- * go. Even if we ask the block I/O layer to not give us sg
- * elements larger than 65535 bytes, some IOMMUs may merge sg elements
- * during DMA mapping, and Linux currently doesn't prevent this.
- */
- for (i = 0, j = 0; i < count; i++, sg = sg_next(sg)) {
- sg_len = sg_dma_len(sg);
- sg_addr = sg_dma_address(sg);
- while (sg_len) {
- /* FIXME: This won't get us out of the pinch. */
- if (unlikely(j >= ARRAY_SIZE(orb->page_table))) {
- fw_error("page table overflow\n");
- goto fail_page_table;
- }
- l = min(sg_len, SBP2_MAX_SG_ELEMENT_LENGTH);
- orb->page_table[j].low = cpu_to_be32(sg_addr);
- orb->page_table[j].high = cpu_to_be32(l << 16);
- sg_addr += l;
- sg_len -= l;
- j++;
- }
+ for_each_sg(sg, sg, n, i) {
+ orb->page_table[i].high = cpu_to_be32(sg_dma_len(sg) << 16);
+ orb->page_table[i].low = cpu_to_be32(sg_dma_address(sg));
}
orb->page_table_bus =
@@ -1437,13 +1404,13 @@ sbp2_map_scatterlist(struct sbp2_command_orb *orb, struct fw_device *device,
orb->request.data_descriptor.high = cpu_to_be32(lu->tgt->address_high);
orb->request.data_descriptor.low = cpu_to_be32(orb->page_table_bus);
orb->request.misc |= cpu_to_be32(COMMAND_ORB_PAGE_TABLE_PRESENT |
- COMMAND_ORB_DATA_SIZE(j));
+ COMMAND_ORB_DATA_SIZE(n));
return 0;
fail_page_table:
- dma_unmap_sg(device->card->device, sg, scsi_sg_count(orb->cmd),
- orb->cmd->sc_data_direction);
+ dma_unmap_sg(device->card->device, scsi_sglist(orb->cmd),
+ scsi_sg_count(orb->cmd), orb->cmd->sc_data_direction);
fail:
return -ENOMEM;
}
@@ -1456,7 +1423,7 @@ static int sbp2_scsi_queuecommand(struct scsi_cmnd *cmd, scsi_done_fn_t done)
struct fw_device *device = fw_device(lu->tgt->unit->device.parent);
struct sbp2_command_orb *orb;
unsigned int max_payload;
- int retval = SCSI_MLQUEUE_HOST_BUSY;
+ int generation, retval = SCSI_MLQUEUE_HOST_BUSY;
/*
* Bidirectional commands are not yet implemented, and unknown
@@ -1500,6 +1467,9 @@ static int sbp2_scsi_queuecommand(struct scsi_cmnd *cmd, scsi_done_fn_t done)
if (cmd->sc_data_direction == DMA_FROM_DEVICE)
orb->request.misc |= cpu_to_be32(COMMAND_ORB_DIRECTION);
+ generation = device->generation;
+ smp_rmb(); /* sbp2_map_scatterlist looks at tgt->address_high */
+
if (scsi_sg_count(cmd) && sbp2_map_scatterlist(orb, device, lu) < 0)
goto out;
@@ -1512,7 +1482,7 @@ static int sbp2_scsi_queuecommand(struct scsi_cmnd *cmd, scsi_done_fn_t done)
if (dma_mapping_error(device->card->device, orb->base.request_bus))
goto out;
- sbp2_send_orb(&orb->base, lu, lu->tgt->node_id, lu->generation,
+ sbp2_send_orb(&orb->base, lu, lu->tgt->node_id, generation,
lu->command_block_agent_address + SBP2_ORB_POINTER);
retval = 0;
out:
@@ -1564,6 +1534,8 @@ static int sbp2_scsi_slave_configure(struct scsi_device *sdev)
if (lu->tgt->workarounds & SBP2_WORKAROUND_128K_MAX_TRANS)
blk_queue_max_sectors(sdev->request_queue, 128 * 1024 / 512);
+ blk_queue_max_segment_size(sdev->request_queue, SBP2_MAX_SEG_SIZE);
+
return 0;
}
diff --git a/drivers/firewire/fw-transaction.c b/drivers/firewire/fw-transaction.c
index e5d1a0b64fcf..022ac4fabb67 100644
--- a/drivers/firewire/fw-transaction.c
+++ b/drivers/firewire/fw-transaction.c
@@ -247,7 +247,7 @@ fw_fill_request(struct fw_packet *packet, int tcode, int tlabel,
*/
void
fw_send_request(struct fw_card *card, struct fw_transaction *t,
- int tcode, int node_id, int generation, int speed,
+ 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)
@@ -279,13 +279,14 @@ fw_send_request(struct fw_card *card, struct fw_transaction *t,
card->current_tlabel = (card->current_tlabel + 1) & 0x1f;
card->tlabel_mask |= (1 << tlabel);
- t->node_id = node_id;
+ t->node_id = destination_id;
t->tlabel = tlabel;
t->callback = callback;
t->callback_data = callback_data;
- fw_fill_request(&t->packet, tcode, t->tlabel, node_id, card->node_id,
- generation, speed, offset, payload, length);
+ fw_fill_request(&t->packet, tcode, t->tlabel,
+ destination_id, card->node_id, generation,
+ speed, offset, payload, length);
t->packet.callback = transmit_complete_callback;
list_add_tail(&t->link, &card->transaction_list);
@@ -296,6 +297,45 @@ fw_send_request(struct fw_card *card, struct fw_transaction *t,
}
EXPORT_SYMBOL(fw_send_request);
+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);
+}
+
+/**
+ * fw_run_transaction - send request and sleep until transaction is completed
+ *
+ * Returns the RCODE.
+ */
+int fw_run_transaction(struct fw_card *card, int tcode, int destination_id,
+ int generation, int speed, unsigned long long offset,
+ void *data, size_t length)
+{
+ struct transaction_callback_data d;
+ struct fw_transaction t;
+
+ init_completion(&d.done);
+ d.payload = data;
+ fw_send_request(card, &t, tcode, destination_id, generation, speed,
+ offset, data, length, transaction_callback, &d);
+ wait_for_completion(&d.done);
+
+ return d.rcode;
+}
+EXPORT_SYMBOL(fw_run_transaction);
+
static DEFINE_MUTEX(phy_config_mutex);
static DECLARE_COMPLETION(phy_config_done);
diff --git a/drivers/firewire/fw-transaction.h b/drivers/firewire/fw-transaction.h
index 2ae1b0d6cb7b..027f58ce81ad 100644
--- a/drivers/firewire/fw-transaction.h
+++ b/drivers/firewire/fw-transaction.h
@@ -426,11 +426,14 @@ fw_core_initiate_bus_reset(struct fw_card *card, int short_reset);
void
fw_send_request(struct fw_card *card, struct fw_transaction *t,
- int tcode, int node_id, int generation, int speed,
- unsigned long long offset,
- void *data, size_t length,
+ int tcode, int destination_id, int generation, int speed,
+ unsigned long long offset, void *data, size_t length,
fw_transaction_callback_t callback, void *callback_data);
+int fw_run_transaction(struct fw_card *card, int tcode, int destination_id,
+ int generation, int speed, unsigned long long offset,
+ void *data, size_t length);
+
int fw_cancel_transaction(struct fw_card *card,
struct fw_transaction *transaction);