From bda3b8a1faf209a98063ccd77d6833a2bb0fc77e Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Thu, 10 Jun 2010 08:23:28 +0200 Subject: firewire: core: retry on local errors in bus manager election When the candidate bus manager fails to do the lock request with which it tries to become bus manager, it assumes that the current IRM is not actually IRM capable and forces itself to become root. However, if that lock request failed because the local node itself was not able to send it, then we cannot blame the current IRM and should not steal its rootness. In this case, RCODE_SEND_ERROR is likely to indicate a temporary error condition such as exhausted tlabels or low memory, so we better try again later. Signed-off-by: Clemens Ladisch --- drivers/firewire/core-card.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers/firewire/core-card.c') diff --git a/drivers/firewire/core-card.c b/drivers/firewire/core-card.c index 9dcb30466ec0..901435cdd5c2 100644 --- a/drivers/firewire/core-card.c +++ b/drivers/firewire/core-card.c @@ -306,6 +306,16 @@ static void fw_card_bm_work(struct work_struct *work) goto out; } + if (rcode == RCODE_SEND_ERROR) { + /* + * We have been unable to send the lock request due to + * some local problem. Let's try again later and hope + * that the problem has gone away by then. + */ + fw_schedule_bm_work(card, DIV_ROUND_UP(HZ, 8)); + goto out; + } + spin_lock_irqsave(&card->lock, flags); if (rcode != RCODE_COMPLETE) { -- cgit v1.2.3 From 8e4b50f94e8c1435a3e0ece42b7f97bc857d0145 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Thu, 10 Jun 2010 08:26:28 +0200 Subject: firewire: core: add CSR SPLIT_TIMEOUT support Implement the SPLIT_TIMEOUT registers. Besides being required by the spec, this is desirable for some IIDC devices and necessary for many audio devices to be able to increase the timeout from userspace. Signed-off-by: Clemens Ladisch --- drivers/firewire/core-card.c | 4 ++ drivers/firewire/core-transaction.c | 76 +++++++++++++++++++++++++++++++------ include/linux/firewire.h | 5 +++ 3 files changed, 74 insertions(+), 11 deletions(-) (limited to 'drivers/firewire/core-card.c') diff --git a/drivers/firewire/core-card.c b/drivers/firewire/core-card.c index 901435cdd5c2..d0f15c2f1e1d 100644 --- a/drivers/firewire/core-card.c +++ b/drivers/firewire/core-card.c @@ -428,6 +428,10 @@ void fw_card_initialize(struct fw_card *card, card->device = device; card->current_tlabel = 0; card->tlabel_mask = 0; + card->split_timeout_hi = 0; + card->split_timeout_lo = 800 << 19; + card->split_timeout_cycles = 800; + card->split_timeout_jiffies = DIV_ROUND_UP(HZ, 10); card->color = 0; card->broadcast_channel = BROADCAST_CHANNEL_INITIAL; diff --git a/drivers/firewire/core-transaction.c b/drivers/firewire/core-transaction.c index 0034229dfd14..9a7d3ec23f2b 100644 --- a/drivers/firewire/core-transaction.c +++ b/drivers/firewire/core-transaction.c @@ -339,7 +339,8 @@ void fw_send_request(struct fw_card *card, struct fw_transaction *t, int tcode, setup_timer(&t->split_timeout_timer, split_transaction_timeout_callback, (unsigned long)t); /* FIXME: start this timer later, relative to t->timestamp */ - mod_timer(&t->split_timeout_timer, jiffies + DIV_ROUND_UP(HZ, 10)); + mod_timer(&t->split_timeout_timer, + jiffies + card->split_timeout_jiffies); t->callback = callback; t->callback_data = callback_data; @@ -673,11 +674,28 @@ void fw_fill_response(struct fw_packet *response, u32 *request_header, } EXPORT_SYMBOL(fw_fill_response); -static struct fw_request *allocate_request(struct fw_packet *p) +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) { struct fw_request *request; u32 *data, length; - int request_tcode, t; + int request_tcode; request_tcode = HEADER_GET_TCODE(p->header[0]); switch (request_tcode) { @@ -712,14 +730,9 @@ static struct fw_request *allocate_request(struct fw_packet *p) if (request == NULL) return NULL; - t = (p->timestamp & 0x1fff) + 4000; - if (t >= 8000) - t = (p->timestamp & ~0x1fff) + 0x2000 + t - 8000; - else - t = (p->timestamp & ~0x1fff) + t; - request->response.speed = p->speed; - request->response.timestamp = t; + request->response.timestamp = + compute_split_timeout_timestamp(card, p->timestamp); request->response.generation = p->generation; request->response.ack = 0; request->response.callback = free_response_callback; @@ -845,7 +858,7 @@ void fw_core_handle_request(struct fw_card *card, struct fw_packet *p) if (p->ack != ACK_PENDING && p->ack != ACK_COMPLETE) return; - request = allocate_request(p); + request = allocate_request(card, p); if (request == NULL) { /* FIXME: send statically allocated busy packet. */ return; @@ -993,6 +1006,19 @@ static u32 read_state_register(struct fw_card *card) return 0; } +static void update_split_timeout(struct fw_card *card) +{ + unsigned int cycles; + + cycles = card->split_timeout_hi * 8000 + (card->split_timeout_lo >> 19); + + cycles = max(cycles, 800u); /* minimum as per the spec */ + cycles = min(cycles, 3u * 8000u); /* maximum OHCI timeout */ + + card->split_timeout_cycles = cycles; + card->split_timeout_jiffies = DIV_ROUND_UP(cycles * HZ, 8000); +} + static void handle_registers(struct fw_card *card, struct fw_request *request, int tcode, int destination, int source, int generation, int speed, unsigned long long offset, @@ -1001,6 +1027,7 @@ static void handle_registers(struct fw_card *card, struct fw_request *request, int reg = offset & ~CSR_REGISTER_BASE; __be32 *data = payload; int rcode = RCODE_COMPLETE; + unsigned long flags; switch (reg) { case CSR_STATE_CLEAR: @@ -1039,6 +1066,33 @@ static void handle_registers(struct fw_card *card, struct fw_request *request, rcode = RCODE_TYPE_ERROR; break; + 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; + case CSR_CYCLE_TIME: if (TCODE_IS_READ_REQUEST(tcode) && length == 4) *data = cpu_to_be32(card->driver-> diff --git a/include/linux/firewire.h b/include/linux/firewire.h index 72e2b8ac2a5a..cdf8213c68ca 100644 --- a/include/linux/firewire.h +++ b/include/linux/firewire.h @@ -89,6 +89,11 @@ struct fw_card { struct list_head transaction_list; unsigned long reset_jiffies; + u32 split_timeout_hi; + u32 split_timeout_lo; + unsigned int split_timeout_cycles; + unsigned int split_timeout_jiffies; + unsigned long long guid; unsigned max_receive; int link_speed; -- cgit v1.2.3 From 7e0e314f198d5048b74c8f0ef9f4c1c02e5ecfc9 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Thu, 10 Jun 2010 08:37:15 +0200 Subject: firewire: core: add CSR abdicate support Implement the abdicate bit, which is required for bus manager capable nodes and tested by the Base 1394 Test Suite. Finally, something to do at a command reset! :-) Signed-off-by: Clemens Ladisch --- drivers/firewire/core-card.c | 3 ++- drivers/firewire/core-topology.c | 2 ++ drivers/firewire/core-transaction.c | 13 +++++++++++-- drivers/firewire/core.h | 1 + include/linux/firewire.h | 2 ++ 5 files changed, 18 insertions(+), 3 deletions(-) (limited to 'drivers/firewire/core-card.c') diff --git a/drivers/firewire/core-card.c b/drivers/firewire/core-card.c index d0f15c2f1e1d..7c4cf6cfa746 100644 --- a/drivers/firewire/core-card.c +++ b/drivers/firewire/core-card.c @@ -260,7 +260,8 @@ static void fw_card_bm_work(struct work_struct *work) grace = time_after(jiffies, card->reset_jiffies + DIV_ROUND_UP(HZ, 8)); - if (is_next_generation(generation, card->bm_generation) || + if ((is_next_generation(generation, card->bm_generation) && + !card->bm_abdicate) || (card->bm_generation != generation && grace)) { /* * This first step is to figure out who is IRM and diff --git a/drivers/firewire/core-topology.c b/drivers/firewire/core-topology.c index 93ec64cdeef7..ca3c65318165 100644 --- a/drivers/firewire/core-topology.c +++ b/drivers/firewire/core-topology.c @@ -552,6 +552,8 @@ void fw_core_handle_bus_reset(struct fw_card *card, int node_id, int generation, smp_wmb(); card->generation = generation; card->reset_jiffies = jiffies; + card->bm_abdicate = card->csr_abdicate; + card->csr_abdicate = false; fw_schedule_bm_work(card, 0); local_node = build_tree(card, self_ids, self_id_count); diff --git a/drivers/firewire/core-transaction.c b/drivers/firewire/core-transaction.c index e0c6cce894cf..85a54da243e2 100644 --- a/drivers/firewire/core-transaction.c +++ b/drivers/firewire/core-transaction.c @@ -1008,6 +1008,10 @@ static u32 read_state_register(struct fw_card *card) /* Bit 8 (cmstr): */ value |= card->driver->read_csr_reg(card, CSR_STATE_CLEAR); + /* Bit 10 (abdicate): */ + if (card->csr_abdicate) + value |= CSR_STATE_BIT_ABDICATE; + return value; } @@ -1041,6 +1045,8 @@ static void handle_registers(struct fw_card *card, struct fw_request *request, } else if (tcode == TCODE_WRITE_QUADLET_REQUEST) { card->driver->write_csr_reg(card, CSR_STATE_CLEAR, be32_to_cpu(*data)); + if (*data & cpu_to_be32(CSR_STATE_BIT_ABDICATE)) + card->csr_abdicate = false; } else { rcode = RCODE_TYPE_ERROR; } @@ -1052,7 +1058,8 @@ static void handle_registers(struct fw_card *card, struct fw_request *request, } else if (tcode == TCODE_WRITE_QUADLET_REQUEST) { card->driver->write_csr_reg(card, CSR_STATE_SET, be32_to_cpu(*data)); - /* FIXME: implement abdicate */ + if (*data & cpu_to_be32(CSR_STATE_BIT_ABDICATE)) + card->csr_abdicate = true; } else { rcode = RCODE_TYPE_ERROR; } @@ -1070,7 +1077,9 @@ static void handle_registers(struct fw_card *card, struct fw_request *request, break; case CSR_RESET_START: - if (tcode != TCODE_WRITE_QUADLET_REQUEST) + if (tcode == TCODE_WRITE_QUADLET_REQUEST) + card->csr_abdicate = false; + else rcode = RCODE_TYPE_ERROR; break; diff --git a/drivers/firewire/core.h b/drivers/firewire/core.h index aaecdd1c1767..a9ace1f8dc3f 100644 --- a/drivers/firewire/core.h +++ b/drivers/firewire/core.h @@ -41,6 +41,7 @@ struct fw_packet; #define FEATURE_PRIORITY_BUDGET 0x01 #define CSR_STATE_BIT_CMSTR (1 << 8) +#define CSR_STATE_BIT_ABDICATE (1 << 10) struct fw_card_driver { /* diff --git a/include/linux/firewire.h b/include/linux/firewire.h index f1160e831dad..4d22643215ef 100644 --- a/include/linux/firewire.h +++ b/include/linux/firewire.h @@ -119,6 +119,8 @@ struct fw_card { int bm_retries; int bm_generation; __be32 bm_transaction_data[2]; + bool bm_abdicate; /* value of csr_abdicate before last bus reset */ + bool csr_abdicate; /* visible in CSR STATE_CLEAR/SET registers */ bool broadcast_channel_allocated; u32 broadcast_channel; -- cgit v1.2.3 From e91b2787d0a2e4719b016e8dec0afd2d5ab6c30f Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Thu, 10 Jun 2010 08:40:49 +0200 Subject: firewire: allocate broadcast channel in hardware On OHCI 1.1 controllers, let the hardware allocate the broadcast channel automatically. This removes a theoretical race condition directly after a bus reset where it could be possible to read the channel allocation register with channel 31 still being unallocated. Signed-off-by: Clemens Ladisch --- drivers/firewire/core-card.c | 16 +++++++++++----- drivers/firewire/core-topology.c | 3 ++- drivers/firewire/core.h | 1 + drivers/firewire/ohci.c | 18 ++++++++++++------ 4 files changed, 26 insertions(+), 12 deletions(-) (limited to 'drivers/firewire/core-card.c') diff --git a/drivers/firewire/core-card.c b/drivers/firewire/core-card.c index 7c4cf6cfa746..faf2eee473b9 100644 --- a/drivers/firewire/core-card.c +++ b/drivers/firewire/core-card.c @@ -208,13 +208,19 @@ static void allocate_broadcast_channel(struct fw_card *card, int generation) { int channel, bandwidth = 0; - fw_iso_resource_manage(card, generation, 1ULL << 31, &channel, - &bandwidth, true, card->bm_transaction_data); - if (channel == 31) { + if (!card->broadcast_channel_allocated) { + fw_iso_resource_manage(card, generation, 1ULL << 31, + &channel, &bandwidth, true, + card->bm_transaction_data); + if (channel != 31) { + fw_notify("failed to allocate broadcast channel\n"); + return; + } card->broadcast_channel_allocated = true; - device_for_each_child(card->device, (void *)(long)generation, - fw_device_set_broadcast_channel); } + + device_for_each_child(card->device, (void *)(long)generation, + fw_device_set_broadcast_channel); } static const char gap_count_table[] = { diff --git a/drivers/firewire/core-topology.c b/drivers/firewire/core-topology.c index ca3c65318165..00a556f3a585 100644 --- a/drivers/firewire/core-topology.c +++ b/drivers/firewire/core-topology.c @@ -543,7 +543,8 @@ void fw_core_handle_bus_reset(struct fw_card *card, int node_id, int generation, spin_lock_irqsave(&card->lock, flags); - card->broadcast_channel_allocated = false; + card->broadcast_channel_allocated = (card->driver->get_features(card) & + FEATURE_CHANNEL_31_ALLOCATED) != 0; card->node_id = node_id; /* * Update node_id before generation to prevent anybody from using diff --git a/drivers/firewire/core.h b/drivers/firewire/core.h index a9ace1f8dc3f..3f9e39b60bca 100644 --- a/drivers/firewire/core.h +++ b/drivers/firewire/core.h @@ -39,6 +39,7 @@ struct fw_packet; #define BROADCAST_CHANNEL_VALID (1 << 30) #define FEATURE_PRIORITY_BUDGET 0x01 +#define FEATURE_CHANNEL_31_ALLOCATED 0x02 #define CSR_STATE_BIT_CMSTR (1 << 8) #define CSR_STATE_BIT_ABDICATE (1 << 10) diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index 1dcc2e427eb1..51a55808d88a 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -171,6 +171,7 @@ struct fw_ohci { int request_generation; /* for timestamping incoming requests */ unsigned quirks; unsigned int pri_req_max; + unsigned int features; u32 bus_time; bool is_root; @@ -1694,7 +1695,7 @@ static int ohci_enable(struct fw_card *card, { struct fw_ohci *ohci = fw_ohci(card); struct pci_dev *dev = to_pci_dev(card->device); - u32 lps, seconds, irqs; + u32 lps, seconds, version, irqs; int i, ret; if (software_reset(ohci)) { @@ -1747,10 +1748,19 @@ static int ohci_enable(struct fw_card *card, reg_write(ohci, OHCI1394_IsochronousCycleTimer, seconds << 25); ohci->bus_time = seconds & ~0x3f; + version = reg_read(ohci, OHCI1394_Version) & 0x00ff00ff; + if (version >= OHCI_VERSION_1_1) { + reg_write(ohci, OHCI1394_InitialChannelsAvailableHi, + 0xfffffffe); + ohci->features |= FEATURE_CHANNEL_31_ALLOCATED; + } + /* Get implemented bits of the priority arbitration request counter. */ reg_write(ohci, OHCI1394_FairnessControl, 0x3f); ohci->pri_req_max = reg_read(ohci, OHCI1394_FairnessControl) & 0x3f; reg_write(ohci, OHCI1394_FairnessControl, 0); + if (ohci->pri_req_max != 0) + ohci->features |= FEATURE_PRIORITY_BUDGET; ar_context_run(&ohci->ar_request_ctx); ar_context_run(&ohci->ar_response_ctx); @@ -2124,12 +2134,8 @@ static void ohci_write_csr_reg(struct fw_card *card, int csr_offset, u32 value) static unsigned int ohci_get_features(struct fw_card *card) { struct fw_ohci *ohci = fw_ohci(card); - unsigned int features = 0; - - if (ohci->pri_req_max != 0) - features |= FEATURE_PRIORITY_BUDGET; - return features; + return ohci->features; } static void copy_iso_headers(struct iso_context *ctx, void *p) -- cgit v1.2.3 From c374ab424249b6ab91b1aee7460419d3f2c321df Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Thu, 10 Jun 2010 08:41:51 +0200 Subject: firewire: core: always enable cycle master packets As part of the bus manager responsibilities, make sure that the cycle master sends cycle start packets. This is needed when the old bus manager disabled the cycle master's cmstr bit and there are iso-capable nodes on the new bus. Signed-off-by: Clemens Ladisch --- drivers/firewire/core-card.c | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) (limited to 'drivers/firewire/core-card.c') diff --git a/drivers/firewire/core-card.c b/drivers/firewire/core-card.c index faf2eee473b9..ef6f2616cac7 100644 --- a/drivers/firewire/core-card.c +++ b/drivers/firewire/core-card.c @@ -369,10 +369,8 @@ static void fw_card_bm_work(struct work_struct *work) goto out; } else if (root_device_is_cmc) { /* - * FIXME: I suppose we should set the cmstr bit in the - * STATE_CLEAR register of this node, as described in - * 1394-1995, 8.4.2.6. Also, send out a force root - * packet for this node. + * We will send out a force root packet for this + * node as part of the gap count optimization. */ new_root_id = root_id; } else { @@ -413,11 +411,25 @@ static void fw_card_bm_work(struct work_struct *work) fw_send_phy_config(card, new_root_id, generation, gap_count); fw_core_initiate_bus_reset(card, 1); /* Will allocate broadcast channel after the reset. */ - } else { - if (local_id == irm_id) - allocate_broadcast_channel(card, generation); + goto out; } + if (root_device_is_cmc) { + /* + * Make sure that the cycle master sends cycle start packets. + */ + card->bm_transaction_data[0] = cpu_to_be32(CSR_STATE_BIT_CMSTR); + rcode = fw_run_transaction(card, TCODE_WRITE_QUADLET_REQUEST, + root_id, generation, SCODE_100, + CSR_REGISTER_BASE + CSR_STATE_SET, + card->bm_transaction_data, sizeof(u32)); + if (rcode == RCODE_GENERATION) + goto out; + } + + if (local_id == irm_id) + allocate_broadcast_channel(card, generation); + out: fw_node_put(root_node); out_put_card: -- cgit v1.2.3 From e847cc832bab50aad939a0c30414cd986637564d Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Sat, 12 Jun 2010 20:29:07 +0200 Subject: firewire: drop sizeof expressions from some request size arguments In case of fw_card_bm_work()'s lock request, the present sizeof expression is going to be wrong if somebody changes the fw_card's DMA scratch buffer's size in the future. In case of quadlet write requests, sizeof(u32) is just silly; it's 4. In case of SBP-2 ORB pointer write requests, 8 is arguably quicker to understand as the correct and only possible value than sizeof(some_datum). Signed-off-by: Stefan Richter --- drivers/firewire/core-card.c | 5 ++--- drivers/firewire/sbp2.c | 10 ++++------ 2 files changed, 6 insertions(+), 9 deletions(-) (limited to 'drivers/firewire/core-card.c') diff --git a/drivers/firewire/core-card.c b/drivers/firewire/core-card.c index ef6f2616cac7..156b098b0ada 100644 --- a/drivers/firewire/core-card.c +++ b/drivers/firewire/core-card.c @@ -296,8 +296,7 @@ static void fw_card_bm_work(struct work_struct *work) rcode = fw_run_transaction(card, TCODE_LOCK_COMPARE_SWAP, irm_id, generation, SCODE_100, CSR_REGISTER_BASE + CSR_BUS_MANAGER_ID, - card->bm_transaction_data, - sizeof(card->bm_transaction_data)); + card->bm_transaction_data, 8); if (rcode == RCODE_GENERATION) /* Another bus reset, BM work has been rescheduled. */ @@ -422,7 +421,7 @@ static void fw_card_bm_work(struct work_struct *work) rcode = fw_run_transaction(card, TCODE_WRITE_QUADLET_REQUEST, root_id, generation, SCODE_100, CSR_REGISTER_BASE + CSR_STATE_SET, - card->bm_transaction_data, sizeof(u32)); + card->bm_transaction_data, 4); if (rcode == RCODE_GENERATION) goto out; } diff --git a/drivers/firewire/sbp2.c b/drivers/firewire/sbp2.c index 7e33b0b1704c..ae715c82da2e 100644 --- a/drivers/firewire/sbp2.c +++ b/drivers/firewire/sbp2.c @@ -508,8 +508,7 @@ static void sbp2_send_orb(struct sbp2_orb *orb, struct sbp2_logical_unit *lu, fw_send_request(device->card, &orb->t, TCODE_WRITE_BLOCK_REQUEST, node_id, generation, device->max_speed, offset, - &orb->pointer, sizeof(orb->pointer), - complete_transaction, orb); + &orb->pointer, 8, complete_transaction, orb); } static int sbp2_cancel_orbs(struct sbp2_logical_unit *lu) @@ -654,7 +653,7 @@ static void sbp2_agent_reset(struct sbp2_logical_unit *lu) 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)); + &d, 4); } static void complete_agent_reset_write_no_wait(struct fw_card *card, @@ -676,7 +675,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, - &d, sizeof(d), complete_agent_reset_write_no_wait, t); + &d, 4, complete_agent_reset_write_no_wait, t); } static inline void sbp2_allow_block(struct sbp2_logical_unit *lu) @@ -866,8 +865,7 @@ static void sbp2_set_busy_timeout(struct sbp2_logical_unit *lu) 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)); + CSR_REGISTER_BASE + CSR_BUSY_TIMEOUT, &d, 4); } static void sbp2_reconnect(struct work_struct *work); -- cgit v1.2.3 From ae948011071c12ff6a328348859c717ea885ed40 Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Mon, 21 Jun 2010 23:23:52 +0200 Subject: firewire: core: no need to track irq flags in bm_work This is a workqueue job and always entered with IRQs enabled. Signed-off-by: Stefan Richter --- drivers/firewire/core-card.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) (limited to 'drivers/firewire/core-card.c') diff --git a/drivers/firewire/core-card.c b/drivers/firewire/core-card.c index 156b098b0ada..11fc81500f82 100644 --- a/drivers/firewire/core-card.c +++ b/drivers/firewire/core-card.c @@ -239,17 +239,16 @@ static void fw_card_bm_work(struct work_struct *work) struct fw_card *card = container_of(work, struct fw_card, work.work); struct fw_device *root_device; struct fw_node *root_node; - unsigned long flags; int root_id, new_root_id, irm_id, local_id; int gap_count, generation, grace, rcode; bool do_reset = false; bool root_device_is_running; bool root_device_is_cmc; - spin_lock_irqsave(&card->lock, flags); + spin_lock_irq(&card->lock); if (card->local_node == NULL) { - spin_unlock_irqrestore(&card->lock, flags); + spin_unlock_irq(&card->lock); goto out_put_card; } @@ -291,7 +290,7 @@ static void fw_card_bm_work(struct work_struct *work) card->bm_transaction_data[0] = cpu_to_be32(0x3f); card->bm_transaction_data[1] = cpu_to_be32(local_id); - spin_unlock_irqrestore(&card->lock, flags); + spin_unlock_irq(&card->lock); rcode = fw_run_transaction(card, TCODE_LOCK_COMPARE_SWAP, irm_id, generation, SCODE_100, @@ -322,7 +321,7 @@ static void fw_card_bm_work(struct work_struct *work) goto out; } - spin_lock_irqsave(&card->lock, flags); + spin_lock_irq(&card->lock); if (rcode != RCODE_COMPLETE) { /* @@ -341,7 +340,7 @@ static void fw_card_bm_work(struct work_struct *work) * We weren't BM in the last generation, and the last * bus reset is less than 125ms ago. Reschedule this job. */ - spin_unlock_irqrestore(&card->lock, flags); + spin_unlock_irq(&card->lock); fw_schedule_bm_work(card, DIV_ROUND_UP(HZ, 8)); goto out; } @@ -364,7 +363,7 @@ static void fw_card_bm_work(struct work_struct *work) * If we haven't probed this device yet, bail out now * and let's try again once that's done. */ - spin_unlock_irqrestore(&card->lock, flags); + spin_unlock_irq(&card->lock); goto out; } else if (root_device_is_cmc) { /* @@ -402,7 +401,7 @@ static void fw_card_bm_work(struct work_struct *work) (card->gap_count != gap_count || new_root_id != root_id)) do_reset = true; - spin_unlock_irqrestore(&card->lock, flags); + spin_unlock_irq(&card->lock); if (do_reset) { fw_notify("phy config: card %d, new root=%x, gap_count=%d\n", -- cgit v1.2.3 From 250b2b6dd421c9f8844a867d2ac06e0661e0ad93 Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Mon, 21 Jun 2010 23:24:35 +0200 Subject: firewire: cdev: fix fw_cdev_event_bus_reset.bm_node_id Fix an obscure ABI feature that is a bit of a hassle to implement. However, somebody put it into the ABI, so let's fill in a sensible value there. Signed-off-by: Stefan Richter --- drivers/firewire/core-card.c | 12 +++++++++--- drivers/firewire/core-cdev.c | 2 +- drivers/firewire/core-topology.c | 1 + include/linux/firewire-cdev.h | 5 +++++ include/linux/firewire.h | 1 + 5 files changed, 17 insertions(+), 4 deletions(-) (limited to 'drivers/firewire/core-card.c') diff --git a/drivers/firewire/core-card.c b/drivers/firewire/core-card.c index 11fc81500f82..6c316cfe70c4 100644 --- a/drivers/firewire/core-card.c +++ b/drivers/firewire/core-card.c @@ -239,7 +239,7 @@ 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; - int root_id, new_root_id, irm_id, local_id; + int root_id, new_root_id, irm_id, bm_id, local_id; int gap_count, generation, grace, rcode; bool do_reset = false; bool root_device_is_running; @@ -301,9 +301,15 @@ static void fw_card_bm_work(struct work_struct *work) /* Another bus reset, BM work has been rescheduled. */ goto out; - if (rcode == RCODE_COMPLETE && - card->bm_transaction_data[0] != cpu_to_be32(0x3f)) { + bm_id = be32_to_cpu(card->bm_transaction_data[0]); + spin_lock_irq(&card->lock); + if (rcode == RCODE_COMPLETE && generation == card->generation) + card->bm_node_id = + bm_id == 0x3f ? local_id : 0xffc0 | bm_id; + spin_unlock_irq(&card->lock); + + if (rcode == RCODE_COMPLETE && bm_id != 0x3f) { /* Somebody else is BM. Only act as IRM. */ if (local_id == irm_id) allocate_broadcast_channel(card, generation); diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index 9b8df2039155..d8ac0ce2d6bf 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -318,7 +318,7 @@ static void fill_bus_reset_event(struct fw_cdev_event_bus_reset *event, event->generation = client->device->generation; event->node_id = client->device->node_id; event->local_node_id = card->local_node->node_id; - event->bm_node_id = 0; /* FIXME: We don't track the BM. */ + event->bm_node_id = card->bm_node_id; event->irm_node_id = card->irm_node->node_id; event->root_node_id = card->root_node->node_id; diff --git a/drivers/firewire/core-topology.c b/drivers/firewire/core-topology.c index 56e908ba43f1..88d5133ae702 100644 --- a/drivers/firewire/core-topology.c +++ b/drivers/firewire/core-topology.c @@ -552,6 +552,7 @@ void fw_core_handle_bus_reset(struct fw_card *card, int node_id, int generation, smp_wmb(); card->generation = generation; card->reset_jiffies = jiffies; + card->bm_node_id = 0xffff; card->bm_abdicate = bm_abdicate; fw_schedule_bm_work(card, 0); diff --git a/include/linux/firewire-cdev.h b/include/linux/firewire-cdev.h index 8b9b27373219..d31022b05bd9 100644 --- a/include/linux/firewire-cdev.h +++ b/include/linux/firewire-cdev.h @@ -66,6 +66,10 @@ struct fw_cdev_event_common { * This event is sent when the bus the device belongs to goes through a bus * reset. It provides information about the new bus configuration, such as * new node ID for this device, new root ID, and others. + * + * If @bm_node_id is 0xffff right after bus reset it can be reread by an + * %FW_CDEV_IOC_GET_INFO ioctl after bus manager selection was finished. + * Kernels with ABI version < 4 do not set @bm_node_id. */ struct fw_cdev_event_bus_reset { __u64 closure; @@ -348,6 +352,7 @@ union fw_cdev_event { * 3 (2.6.34) - made &fw_cdev_get_cycle_timer reliable * - added %FW_CDEV_IOC_GET_CYCLE_TIMER2 * 4 (2.6.36) - added %FW_CDEV_EVENT_REQUEST2 + * - implemented &fw_cdev_event_bus_reset.bm_node_id */ #define FW_CDEV_VERSION 3 /* Meaningless; don't use this macro. */ diff --git a/include/linux/firewire.h b/include/linux/firewire.h index e44b502c8341..db30a752a87a 100644 --- a/include/linux/firewire.h +++ b/include/linux/firewire.h @@ -119,6 +119,7 @@ struct fw_card { int bm_retries; int bm_generation; __be32 bm_transaction_data[2]; + int bm_node_id; bool bm_abdicate; bool priority_budget_implemented; /* controller feature */ -- cgit v1.2.3 From 02d37bed188c500ee7afb0a2dc6b65a80704c58e Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Thu, 8 Jul 2010 16:09:06 +0200 Subject: firewire: core: integrate software-forced bus resets with bus management Bus resets which are triggered - by the kernel drivers after updates of the local nodes' config ROM, - by userspace software via ioctl shall be deferred until after >=2 seconds after the last bus reset. If multiple modifications of the local nodes' config ROM happen in a row, only a single bus reset should happen after them. When the local node's link goes from inactive to active or vice versa, and at the two occasions of bus resets mentioned above --- and if the current gap count differs from 63 --- the bus reset should be preceded by a PHY configuration packet that reaffirms the gap count. Otherwise a bus manager would have to reset the bus again right after that. This is necessary to promote bus stability, e.g. leave grace periods for allocations and reallocations of isochronous channels and bandwidth, SBP-2 reconnections etc.; see IEEE 1394 clause 8.2.1. This change implements all of the above by moving bus reset initiation into a delayed work (except for bus resets which are triggered by the bus manager workqueue job and are performed there immediately). It comes with a necessary addition to the card driver methods that allows to get the current gap count from PHY registers. Signed-off-by: Stefan Richter --- drivers/firewire/core-card.c | 68 ++++++++++++++++++++++++++++--------- drivers/firewire/core-cdev.c | 3 +- drivers/firewire/core-transaction.c | 18 ++++++++-- drivers/firewire/core.h | 6 +++- drivers/firewire/ohci.c | 53 +++++++++++++++++++++-------- include/linux/firewire.h | 6 ++-- 6 files changed, 116 insertions(+), 38 deletions(-) (limited to 'drivers/firewire/core-card.c') diff --git a/drivers/firewire/core-card.c b/drivers/firewire/core-card.c index 6c316cfe70c4..2bb5c036e806 100644 --- a/drivers/firewire/core-card.c +++ b/drivers/firewire/core-card.c @@ -204,6 +204,45 @@ void fw_core_remove_descriptor(struct fw_descriptor *desc) } EXPORT_SYMBOL(fw_core_remove_descriptor); +static int reset_bus(struct fw_card *card, bool short_reset) +{ + int reg = short_reset ? 5 : 1; + int bit = short_reset ? PHY_BUS_SHORT_RESET : PHY_BUS_RESET; + + return card->driver->update_phy_reg(card, reg, 0, bit); +} + +void fw_schedule_bus_reset(struct fw_card *card, bool delayed, bool short_reset) +{ + /* We don't try hard to sort out requests of long vs. short resets. */ + card->br_short = short_reset; + + /* Use an arbitrary short delay to combine multiple reset requests. */ + fw_card_get(card); + if (!schedule_delayed_work(&card->br_work, + delayed ? DIV_ROUND_UP(HZ, 100) : 0)) + fw_card_put(card); +} +EXPORT_SYMBOL(fw_schedule_bus_reset); + +static void br_work(struct work_struct *work) +{ + struct fw_card *card = container_of(work, struct fw_card, br_work.work); + + /* Delay for 2s after last reset per IEEE 1394 clause 8.2.1. */ + if (card->reset_jiffies != 0 && + time_is_after_jiffies(card->reset_jiffies + 2 * HZ)) { + if (!schedule_delayed_work(&card->br_work, 2 * HZ)) + fw_card_put(card); + return; + } + + fw_send_phy_config(card, FW_PHY_CONFIG_NO_NODE_ID, card->generation, + FW_PHY_CONFIG_CURRENT_GAP_COUNT); + reset_bus(card, card->br_short); + fw_card_put(card); +} + static void allocate_broadcast_channel(struct fw_card *card, int generation) { int channel, bandwidth = 0; @@ -230,13 +269,13 @@ static const char gap_count_table[] = { void fw_schedule_bm_work(struct fw_card *card, unsigned long delay) { fw_card_get(card); - if (!schedule_delayed_work(&card->work, delay)) + if (!schedule_delayed_work(&card->bm_work, delay)) fw_card_put(card); } -static void fw_card_bm_work(struct work_struct *work) +static void bm_work(struct work_struct *work) { - struct fw_card *card = container_of(work, struct fw_card, work.work); + struct fw_card *card = container_of(work, struct fw_card, bm_work.work); struct fw_device *root_device; struct fw_node *root_node; int root_id, new_root_id, irm_id, bm_id, local_id; @@ -413,7 +452,7 @@ static void fw_card_bm_work(struct work_struct *work) fw_notify("phy config: card %d, new root=%x, gap_count=%d\n", card->index, new_root_id, gap_count); fw_send_phy_config(card, new_root_id, generation, gap_count); - fw_core_initiate_bus_reset(card, 1); + reset_bus(card, true); /* Will allocate broadcast channel after the reset. */ goto out; } @@ -465,7 +504,8 @@ void fw_card_initialize(struct fw_card *card, card->local_node = NULL; - INIT_DELAYED_WORK(&card->work, fw_card_bm_work); + INIT_DELAYED_WORK(&card->br_work, br_work); + INIT_DELAYED_WORK(&card->bm_work, bm_work); } EXPORT_SYMBOL(fw_card_initialize); @@ -491,7 +531,6 @@ int fw_card_add(struct fw_card *card, } EXPORT_SYMBOL(fw_card_add); - /* * The next few functions implement a dummy driver that is used once a card * driver shuts down an fw_card. This allows the driver to cleanly unload, @@ -507,6 +546,11 @@ static int dummy_enable(struct fw_card *card, return -1; } +static int dummy_read_phy_reg(struct fw_card *card, int address) +{ + return -ENODEV; +} + static int dummy_update_phy_reg(struct fw_card *card, int address, int clear_bits, int set_bits) { @@ -547,6 +591,7 @@ static int dummy_enable_phys_dma(struct fw_card *card, static const struct fw_card_driver dummy_driver_template = { .enable = dummy_enable, + .read_phy_reg = dummy_read_phy_reg, .update_phy_reg = dummy_update_phy_reg, .set_config_rom = dummy_set_config_rom, .send_request = dummy_send_request, @@ -568,7 +613,7 @@ void fw_core_remove_card(struct fw_card *card) card->driver->update_phy_reg(card, 4, PHY_LINK_ACTIVE | PHY_CONTENDER, 0); - fw_core_initiate_bus_reset(card, 1); + fw_schedule_bus_reset(card, false, true); mutex_lock(&card_mutex); list_del_init(&card->link); @@ -588,12 +633,3 @@ void fw_core_remove_card(struct fw_card *card) WARN_ON(!list_empty(&card->transaction_list)); } EXPORT_SYMBOL(fw_core_remove_card); - -int fw_core_initiate_bus_reset(struct fw_card *card, int short_reset) -{ - int reg = short_reset ? 5 : 1; - int bit = short_reset ? PHY_BUS_SHORT_RESET : PHY_BUS_RESET; - - return card->driver->update_phy_reg(card, reg, 0, bit); -} -EXPORT_SYMBOL(fw_core_initiate_bus_reset); diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index 7a690c466ce9..ee2e87353102 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -820,8 +820,9 @@ static int ioctl_send_response(struct client *client, union ioctl_arg *arg) static int ioctl_initiate_bus_reset(struct client *client, union ioctl_arg *arg) { - return fw_core_initiate_bus_reset(client->device->card, + fw_schedule_bus_reset(client->device->card, true, arg->initiate_bus_reset.type == FW_CDEV_SHORT_RESET); + return 0; } static void release_descriptor(struct client *client, diff --git a/drivers/firewire/core-transaction.c b/drivers/firewire/core-transaction.c index 7813da8a1293..5f5a7852f7ac 100644 --- a/drivers/firewire/core-transaction.c +++ b/drivers/firewire/core-transaction.c @@ -426,9 +426,21 @@ void fw_send_phy_config(struct fw_card *card, int node_id, int generation, int gap_count) { long timeout = DIV_ROUND_UP(HZ, 10); - u32 data = PHY_IDENTIFIER(PHY_PACKET_CONFIG) | - PHY_CONFIG_ROOT_ID(node_id) | - PHY_CONFIG_GAP_COUNT(gap_count); + 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); mutex_lock(&phy_config_mutex); diff --git a/drivers/firewire/core.h b/drivers/firewire/core.h index 3000dd74acfd..ff6c90922001 100644 --- a/drivers/firewire/core.h +++ b/drivers/firewire/core.h @@ -51,6 +51,7 @@ struct fw_card_driver { int (*enable)(struct fw_card *card, const __be32 *config_rom, size_t length); + int (*read_phy_reg)(struct fw_card *card, int address); int (*update_phy_reg)(struct fw_card *card, int address, int clear_bits, int set_bits); @@ -102,8 +103,8 @@ void fw_card_initialize(struct fw_card *card, int fw_card_add(struct fw_card *card, u32 max_receive, u32 link_speed, u64 guid); void fw_core_remove_card(struct fw_card *card); -int fw_core_initiate_bus_reset(struct fw_card *card, int short_reset); int fw_compute_block_crc(__be32 *block); +void fw_schedule_bus_reset(struct fw_card *card, bool delayed, bool short_reset); void fw_schedule_bm_work(struct fw_card *card, unsigned long delay); static inline struct fw_card *fw_card_get(struct fw_card *card) @@ -225,6 +226,9 @@ void fw_core_handle_response(struct fw_card *card, struct fw_packet *packet); int fw_get_response_length(struct fw_request *request); void fw_fill_response(struct fw_packet *response, u32 *request_header, int rcode, void *payload, size_t length); + +#define FW_PHY_CONFIG_NO_NODE_ID -1 +#define FW_PHY_CONFIG_CURRENT_GAP_COUNT -1 void fw_send_phy_config(struct fw_card *card, int node_id, int generation, int gap_count); diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index a4bbf3dadf58..bb6a92bc9e6a 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -182,6 +183,8 @@ struct fw_ohci { */ spinlock_t lock; + struct mutex phy_reg_mutex; + struct ar_context ar_request_ctx; struct ar_context ar_response_ctx; struct context at_request_ctx; @@ -517,13 +520,10 @@ static int write_phy_reg(const struct fw_ohci *ohci, int addr, u32 val) return -EBUSY; } -static int ohci_update_phy_reg(struct fw_card *card, int addr, - int clear_bits, int set_bits) +static int update_phy_reg(struct fw_ohci *ohci, int addr, + int clear_bits, int set_bits) { - struct fw_ohci *ohci = fw_ohci(card); - int ret; - - ret = read_phy_reg(ohci, addr); + int ret = read_phy_reg(ohci, addr); if (ret < 0) return ret; @@ -541,13 +541,38 @@ static int read_paged_phy_reg(struct fw_ohci *ohci, int page, int addr) { int ret; - ret = ohci_update_phy_reg(&ohci->card, 7, PHY_PAGE_SELECT, page << 5); + ret = update_phy_reg(ohci, 7, PHY_PAGE_SELECT, page << 5); if (ret < 0) return ret; return read_phy_reg(ohci, addr); } +static int ohci_read_phy_reg(struct fw_card *card, int addr) +{ + struct fw_ohci *ohci = fw_ohci(card); + int ret; + + mutex_lock(&ohci->phy_reg_mutex); + ret = read_phy_reg(ohci, addr); + mutex_unlock(&ohci->phy_reg_mutex); + + return ret; +} + +static int ohci_update_phy_reg(struct fw_card *card, int addr, + int clear_bits, int set_bits) +{ + struct fw_ohci *ohci = fw_ohci(card); + int ret; + + mutex_lock(&ohci->phy_reg_mutex); + ret = update_phy_reg(ohci, addr, clear_bits, set_bits); + mutex_unlock(&ohci->phy_reg_mutex); + + return ret; +} + static int ar_context_add_page(struct ar_context *ctx) { struct device *dev = ctx->ohci->card.device; @@ -1676,7 +1701,7 @@ static int configure_1394a_enhancements(struct fw_ohci *ohci) clear = PHY_ENABLE_ACCEL | PHY_ENABLE_MULTI; set = 0; } - ret = ohci_update_phy_reg(&ohci->card, 5, clear, set); + ret = update_phy_reg(ohci, 5, clear, set); if (ret < 0) return ret; @@ -1856,12 +1881,8 @@ static int ohci_enable(struct fw_card *card, OHCI1394_HCControl_BIBimageValid); flush_writes(ohci); - /* - * We are ready to go, initiate bus reset to finish the - * initialization. - */ - - fw_core_initiate_bus_reset(&ohci->card, 1); + /* We are ready to go, reset bus to finish initialization. */ + fw_schedule_bus_reset(&ohci->card, false, true); return 0; } @@ -1936,7 +1957,7 @@ static int ohci_set_config_rom(struct fw_card *card, * takes effect. */ if (ret == 0) - fw_core_initiate_bus_reset(&ohci->card, 1); + fw_schedule_bus_reset(&ohci->card, true, true); else dma_free_coherent(ohci->card.device, CONFIG_ROM_SIZE, next_config_rom, next_config_rom_bus); @@ -2570,6 +2591,7 @@ static int ohci_queue_iso(struct fw_iso_context *base, static const struct fw_card_driver ohci_driver = { .enable = ohci_enable, + .read_phy_reg = ohci_read_phy_reg, .update_phy_reg = ohci_update_phy_reg, .set_config_rom = ohci_set_config_rom, .send_request = ohci_send_request, @@ -2645,6 +2667,7 @@ static int __devinit pci_probe(struct pci_dev *dev, pci_set_drvdata(dev, ohci); spin_lock_init(&ohci->lock); + mutex_init(&ohci->phy_reg_mutex); tasklet_init(&ohci->bus_reset_tasklet, bus_reset_tasklet, (unsigned long)ohci); diff --git a/include/linux/firewire.h b/include/linux/firewire.h index db30a752a87a..adc5b55e6e5f 100644 --- a/include/linux/firewire.h +++ b/include/linux/firewire.h @@ -114,8 +114,10 @@ struct fw_card { struct list_head link; - /* Work struct for BM duties. */ - struct delayed_work work; + struct delayed_work br_work; /* bus reset job */ + bool br_short; + + struct delayed_work bm_work; /* bus manager job */ int bm_retries; int bm_generation; __be32 bm_transaction_data[2]; -- cgit v1.2.3 From 18d0cdfd1a4cc9028c0ef80f94538b31541f8fe5 Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Sun, 18 Jul 2010 12:44:01 +0200 Subject: firewire: normalize status values in packet callbacks core-transaction.c transmit_complete_callback() and close_transaction() expect packet callback status to be an ACK or RCODE, and ACKs get translated to RCODEs for transaction callbacks. An old comment on the packet callback API (been there from the initial submission of the stack) and the dummy_driver implementation of send_request/send_response deviated from this as they also included -ERRNO in the range of status values. Let's narrow status values down to ACK and RCODE to prevent surprises. RCODE_CANCELLED is chosen as the dummy_driver's RCODE as its meaning of "transaction timed out" comes closest to what happens when a transaction coincides with card removal. Signed-off-by: Stefan Richter --- drivers/firewire/core-card.c | 4 ++-- drivers/firewire/core-transaction.c | 5 ++++- include/linux/firewire.h | 8 ++++---- 3 files changed, 10 insertions(+), 7 deletions(-) (limited to 'drivers/firewire/core-card.c') diff --git a/drivers/firewire/core-card.c b/drivers/firewire/core-card.c index 2bb5c036e806..0c312c4bb4bd 100644 --- a/drivers/firewire/core-card.c +++ b/drivers/firewire/core-card.c @@ -570,12 +570,12 @@ static int dummy_set_config_rom(struct fw_card *card, static void dummy_send_request(struct fw_card *card, struct fw_packet *packet) { - packet->callback(packet, card, -ENODEV); + packet->callback(packet, card, RCODE_CANCELLED); } static void dummy_send_response(struct fw_card *card, struct fw_packet *packet) { - packet->callback(packet, card, -ENODEV); + packet->callback(packet, card, RCODE_CANCELLED); } static int dummy_cancel_packet(struct fw_card *card, struct fw_packet *packet) diff --git a/drivers/firewire/core-transaction.c b/drivers/firewire/core-transaction.c index 5f5a7852f7ac..e2e4dc624fb6 100644 --- a/drivers/firewire/core-transaction.c +++ b/drivers/firewire/core-transaction.c @@ -310,7 +310,10 @@ static int allocate_tlabel(struct fw_card *card) * 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, - * the firewire-core specific %RCODE_SEND_ERROR. + * 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. * * 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, diff --git a/include/linux/firewire.h b/include/linux/firewire.h index adc5b55e6e5f..0c38b8e97722 100644 --- a/include/linux/firewire.h +++ b/include/linux/firewire.h @@ -285,10 +285,10 @@ struct fw_packet { u32 timestamp; /* - * This callback is called when the packet transmission has - * completed; for successful transmission, the status code is - * the ack received from the destination, otherwise it's a - * negative errno: ENOMEM, ESTALE, ETIMEDOUT, ENODEV, EIO. + * This callback is called when the packet transmission has completed. + * For successful transmission, the status code is the ack received + * from the destination. Otherwise it is one of the juju-specific + * rcodes: RCODE_SEND_ERROR, _CANCELLED, _BUSY, _GENERATION, _NO_ACK. * The callback can be called from tasklet context and thus * must never block. */ -- cgit v1.2.3 From bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bc Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Fri, 16 Jul 2010 22:25:51 +0200 Subject: firewire: cdev: add PHY packet reception Add an FW_CDEV_IOC_RECEIVE_PHY_PACKETS ioctl() and FW_CDEV_EVENT_PHY_PACKET_RECEIVED poll()/read() event for /dev/fw*. This can be used to get information from remote PHYs by remote access PHY packets. This is also the 2nd half of the functionality (the receive part) to support a userspace implementation of a VersaPHY transaction layer. Safety considerations: - PHY packets are generally broadcasts, hence some kind of elevated privileges should be required of a process to be able to listen in on PHY packets. This implementation assumes that a process that is allowed to open the /dev/fw* of a local node does have this privilege. There was an inconclusive discussion about introducing POSIX capabilities as a means to check for user privileges for these kinds of operations. Other limitations: - PHY packet reception may be switched on by ioctl() but cannot be switched off again. It would be trivial to provide an off switch, but this is not worth the code. The client should simply close() the fd then, or just ignore further events. - For sake of simplicity of API and kernel-side implementation, no filter per packet content is provided. Signed-off-by: Stefan Richter --- drivers/firewire/core-card.c | 1 + drivers/firewire/core-cdev.c | 73 ++++++++++++++++++++++++++++++++++--- drivers/firewire/core-transaction.c | 5 +++ drivers/firewire/core.h | 2 + drivers/firewire/ohci.c | 3 +- include/linux/firewire-cdev.h | 39 ++++++++++++++++---- include/linux/firewire.h | 3 +- 7 files changed, 111 insertions(+), 15 deletions(-) (limited to 'drivers/firewire/core-card.c') diff --git a/drivers/firewire/core-card.c b/drivers/firewire/core-card.c index 0c312c4bb4bd..6d1cfae6aad4 100644 --- a/drivers/firewire/core-card.c +++ b/drivers/firewire/core-card.c @@ -500,6 +500,7 @@ void fw_card_initialize(struct fw_card *card, kref_init(&card->kref); init_completion(&card->done); INIT_LIST_HEAD(&card->transaction_list); + INIT_LIST_HEAD(&card->phy_receiver_list); spin_lock_init(&card->lock); card->local_node = NULL; diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index f95719926487..0425dd5dfcd3 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -69,6 +69,9 @@ struct client { struct fw_iso_buffer buffer; unsigned long vm_start; + struct list_head phy_receiver_link; + u64 phy_receiver_closure; + struct list_head link; struct kref kref; }; @@ -201,6 +204,11 @@ struct outbound_phy_packet_event { struct fw_cdev_event_phy_packet phy_packet; }; +struct inbound_phy_packet_event { + struct event event; + struct fw_cdev_event_phy_packet phy_packet; +}; + static inline void __user *u64_to_uptr(__u64 value) { return (void __user *)(unsigned long)value; @@ -236,6 +244,7 @@ static int fw_device_op_open(struct inode *inode, struct file *file) idr_init(&client->resource_idr); INIT_LIST_HEAD(&client->event_list); init_waitqueue_head(&client->wait); + INIT_LIST_HEAD(&client->phy_receiver_link); kref_init(&client->kref); file->private_data = client; @@ -357,7 +366,7 @@ static void queue_bus_reset_event(struct client *client) e = kzalloc(sizeof(*e), GFP_KERNEL); if (e == NULL) { - fw_notify("Out of memory when allocating bus reset event\n"); + fw_notify("Out of memory when allocating event\n"); return; } @@ -404,6 +413,7 @@ union ioctl_arg { struct fw_cdev_send_stream_packet send_stream_packet; struct fw_cdev_get_cycle_timer2 get_cycle_timer2; struct fw_cdev_send_phy_packet send_phy_packet; + struct fw_cdev_receive_phy_packets receive_phy_packets; }; static int ioctl_get_info(struct client *client, union ioctl_arg *arg) @@ -671,9 +681,10 @@ static void handle_request(struct fw_card *card, struct fw_request *request, r = kmalloc(sizeof(*r), GFP_ATOMIC); e = kmalloc(sizeof(*e), GFP_ATOMIC); - if (r == NULL || e == NULL) + if (r == NULL || e == NULL) { + fw_notify("Out of memory when allocating event\n"); goto failed; - + } r->card = card; r->request = request; r->data = payload; @@ -902,9 +913,10 @@ static void iso_callback(struct fw_iso_context *context, u32 cycle, struct iso_interrupt_event *e; e = kmalloc(sizeof(*e) + header_length, GFP_ATOMIC); - if (e == NULL) + if (e == NULL) { + fw_notify("Out of memory when allocating event\n"); return; - + } e->interrupt.type = FW_CDEV_EVENT_ISO_INTERRUPT; e->interrupt.closure = client->iso_closure; e->interrupt.cycle = cycle; @@ -1447,6 +1459,52 @@ static int ioctl_send_phy_packet(struct client *client, union ioctl_arg *arg) return 0; } +static int ioctl_receive_phy_packets(struct client *client, union ioctl_arg *arg) +{ + struct fw_cdev_receive_phy_packets *a = &arg->receive_phy_packets; + struct fw_card *card = client->device->card; + + /* Access policy: Allow this ioctl only on local nodes' device files. */ + if (!client->device->is_local) + return -ENOSYS; + + spin_lock_irq(&card->lock); + + list_move_tail(&client->phy_receiver_link, &card->phy_receiver_list); + client->phy_receiver_closure = a->closure; + + spin_unlock_irq(&card->lock); + + return 0; +} + +void fw_cdev_handle_phy_packet(struct fw_card *card, struct fw_packet *p) +{ + struct client *client; + struct inbound_phy_packet_event *e; + unsigned long flags; + + spin_lock_irqsave(&card->lock, flags); + + list_for_each_entry(client, &card->phy_receiver_list, phy_receiver_link) { + e = kmalloc(sizeof(*e) + 8, GFP_ATOMIC); + if (e == NULL) { + fw_notify("Out of memory when allocating event\n"); + break; + } + e->phy_packet.closure = client->phy_receiver_closure; + e->phy_packet.type = FW_CDEV_EVENT_PHY_PACKET_RECEIVED; + e->phy_packet.rcode = RCODE_COMPLETE; + e->phy_packet.length = 8; + e->phy_packet.data[0] = p->header[1]; + e->phy_packet.data[1] = p->header[2]; + queue_event(client, &e->event, + &e->phy_packet, sizeof(e->phy_packet) + 8, NULL, 0); + } + + spin_unlock_irqrestore(&card->lock, flags); +} + static int (* const ioctl_handlers[])(struct client *, union ioctl_arg *) = { [0x00] = ioctl_get_info, [0x01] = ioctl_send_request, @@ -1470,6 +1528,7 @@ static int (* const ioctl_handlers[])(struct client *, union ioctl_arg *) = { [0x13] = ioctl_send_stream_packet, [0x14] = ioctl_get_cycle_timer2, [0x15] = ioctl_send_phy_packet, + [0x16] = ioctl_receive_phy_packets, }; static int dispatch_ioctl(struct client *client, @@ -1577,6 +1636,10 @@ static int fw_device_op_release(struct inode *inode, struct file *file) struct client *client = file->private_data; struct event *event, *next_event; + spin_lock_irq(&client->device->card->lock); + list_del(&client->phy_receiver_link); + spin_unlock_irq(&client->device->card->lock); + mutex_lock(&client->device->client_list_mutex); list_del(&client->link); mutex_unlock(&client->device->client_list_mutex); diff --git a/drivers/firewire/core-transaction.c b/drivers/firewire/core-transaction.c index e2e4dc624fb6..6f225cacbc3d 100644 --- a/drivers/firewire/core-transaction.c +++ b/drivers/firewire/core-transaction.c @@ -883,6 +883,11 @@ void fw_core_handle_request(struct fw_card *card, struct fw_packet *p) if (p->ack != ACK_PENDING && p->ack != ACK_COMPLETE) return; + if (TCODE_IS_LINK_INTERNAL(HEADER_GET_TCODE(p->header[0]))) { + fw_cdev_handle_phy_packet(card, p); + return; + } + request = allocate_request(card, p); if (request == NULL) { /* FIXME: send statically allocated busy packet. */ diff --git a/drivers/firewire/core.h b/drivers/firewire/core.h index ff6c90922001..3102b6b63438 100644 --- a/drivers/firewire/core.h +++ b/drivers/firewire/core.h @@ -128,6 +128,7 @@ extern const struct file_operations fw_device_ops; void fw_device_cdev_update(struct fw_device *device); void fw_device_cdev_remove(struct fw_device *device); +void fw_cdev_handle_phy_packet(struct fw_card *card, struct fw_packet *p); /* -device */ @@ -214,6 +215,7 @@ static inline bool is_next_generation(int new_generation, int old_generation) #define TCODE_IS_READ_REQUEST(tcode) (((tcode) & ~1) == 4) #define TCODE_IS_BLOCK_PACKET(tcode) (((tcode) & 1) != 0) +#define TCODE_IS_LINK_INTERNAL(tcode) ((tcode) == 0xe) #define TCODE_IS_REQUEST(tcode) (((tcode) & 2) == 0) #define TCODE_IS_RESPONSE(tcode) (((tcode) & 2) != 0) #define TCODE_HAS_REQUEST_DATA(tcode) (((tcode) & 12) != 4) diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index bb6a92bc9e6a..08afccc66333 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -1759,10 +1759,9 @@ static int ohci_enable(struct fw_card *card, OHCI1394_HCControl_noByteSwapData); reg_write(ohci, OHCI1394_SelfIDBuffer, ohci->self_id_bus); - reg_write(ohci, OHCI1394_LinkControlClear, - OHCI1394_LinkControl_rcvPhyPkt); reg_write(ohci, OHCI1394_LinkControlSet, OHCI1394_LinkControl_rcvSelfID | + OHCI1394_LinkControl_rcvPhyPkt | OHCI1394_LinkControl_cycleTimerEnable | OHCI1394_LinkControl_cycleMaster); diff --git a/include/linux/firewire-cdev.h b/include/linux/firewire-cdev.h index 5bc051b9a013..b87409160794 100644 --- a/include/linux/firewire-cdev.h +++ b/include/linux/firewire-cdev.h @@ -35,6 +35,7 @@ /* available since kernel version 2.6.36 */ #define FW_CDEV_EVENT_REQUEST2 0x06 #define FW_CDEV_EVENT_PHY_PACKET_SENT 0x07 +#define FW_CDEV_EVENT_PHY_PACKET_RECEIVED 0x08 /** * struct fw_cdev_event_common - Common part of all fw_cdev_event_ types @@ -285,16 +286,24 @@ struct fw_cdev_event_iso_resource { }; /** - * struct fw_cdev_event_phy_packet - A PHY packet was transmitted - * @closure: See &fw_cdev_event_common; - * set by %FW_CDEV_IOC_SEND_PHY_PACKET ioctl - * @type: %FW_CDEV_EVENT_PHY_PACKET_SENT + * struct fw_cdev_event_phy_packet - A PHY packet was transmitted or received + * @closure: See &fw_cdev_event_common; set by %FW_CDEV_IOC_SEND_PHY_PACKET + * or %FW_CDEV_IOC_RECEIVE_PHY_PACKETS ioctl + * @type: %FW_CDEV_EVENT_PHY_PACKET_SENT or %..._RECEIVED * @rcode: %RCODE_..., indicates success or failure of transmission + * @length: Data length in bytes + * @data: Incoming data + * + * If @type is %FW_CDEV_EVENT_PHY_PACKET_SENT, @length is 0 and @data empty. + * If @type is %FW_CDEV_EVENT_PHY_PACKET_RECEIVED, @length is 8 and @data + * consists of the two PHY packet quadlets, in host byte order. */ struct fw_cdev_event_phy_packet { __u64 closure; __u32 type; __u32 rcode; + __u32 length; + __u32 data[0]; }; /** @@ -308,7 +317,9 @@ struct fw_cdev_event_phy_packet { * @iso_resource: Valid if @common.type == * %FW_CDEV_EVENT_ISO_RESOURCE_ALLOCATED or * %FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED - * @phy_packet: Valid if @common.type == %FW_CDEV_EVENT_PHY_PACKET_SENT + * @phy_packet: Valid if @common.type == + * %FW_CDEV_EVENT_PHY_PACKET_SENT or + * %FW_CDEV_EVENT_PHY_PACKET_RECEIVED * * Convenience union for userspace use. Events could be read(2) into an * appropriately aligned char buffer and then cast to this union for further @@ -360,6 +371,7 @@ union fw_cdev_event { /* available since kernel version 2.6.36 */ #define FW_CDEV_IOC_SEND_PHY_PACKET _IOWR('#', 0x15, struct fw_cdev_send_phy_packet) +#define FW_CDEV_IOC_RECEIVE_PHY_PACKETS _IOW('#', 0x16, struct fw_cdev_receive_phy_packets) /* * ABI version history @@ -376,9 +388,9 @@ union fw_cdev_event { * - shared use and auto-response for FCP registers * 3 (2.6.34) - made &fw_cdev_get_cycle_timer reliable * - added %FW_CDEV_IOC_GET_CYCLE_TIMER2 - * 4 (2.6.36) - added %FW_CDEV_EVENT_REQUEST2, %FW_CDEV_EVENT_PHY_PACKET_SENT + * 4 (2.6.36) - added %FW_CDEV_EVENT_REQUEST2, %FW_CDEV_EVENT_PHY_PACKET_* * - implemented &fw_cdev_event_bus_reset.bm_node_id - * - added %FW_CDEV_IOC_SEND_PHY_PACKET + * - added %FW_CDEV_IOC_SEND_PHY_PACKET, _RECEIVE_PHY_PACKETS */ #define FW_CDEV_VERSION 3 /* Meaningless; don't use this macro. */ @@ -850,4 +862,17 @@ struct fw_cdev_send_phy_packet { __u32 generation; }; +/** + * struct fw_cdev_receive_phy_packets - start reception of PHY packets + * @closure: Passed back to userspace in phy packet events + * + * This ioctl activates issuing of %FW_CDEV_EVENT_PHY_PACKET_RECEIVED due to + * incoming PHY packets from any node on the same bus as the device. + * + * The ioctl is only permitted on device files which represent a local node. + */ +struct fw_cdev_receive_phy_packets { + __u64 closure; +}; + #endif /* _LINUX_FIREWIRE_CDEV_H */ diff --git a/include/linux/firewire.h b/include/linux/firewire.h index 0c38b8e97722..d974aa4a24c9 100644 --- a/include/linux/firewire.h +++ b/include/linux/firewire.h @@ -111,9 +111,10 @@ struct fw_card { bool beta_repeaters_present; int index; - struct list_head link; + struct list_head phy_receiver_list; + struct delayed_work br_work; /* bus reset job */ bool br_short; -- cgit v1.2.3 From 20802224298ce9dfd99a7e26b675fc0c8ae26cac Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Sun, 1 Aug 2010 12:23:14 +0200 Subject: firewire: core: add forgotten dummy driver methods, remove unused ones There is an at least theoretic race condition in which .start_iso etc. could still be called between when the dummy driver is bound to the card and when the children devices are being shut down. Add dummy_start_iso and friends. On the other hand, .enable, .set_config_rom, .read_csr, write_csr do not need to be implemented by the dummy driver, as commented. Signed-off-by: Stefan Richter --- drivers/firewire/core-card.c | 64 ++++++++++++++++++++++++++------------------ 1 file changed, 38 insertions(+), 26 deletions(-) (limited to 'drivers/firewire/core-card.c') diff --git a/drivers/firewire/core-card.c b/drivers/firewire/core-card.c index 6d1cfae6aad4..417b8a4b1af1 100644 --- a/drivers/firewire/core-card.c +++ b/drivers/firewire/core-card.c @@ -538,15 +538,13 @@ EXPORT_SYMBOL(fw_card_add); * as all IO to the card will be handled (and failed) by the dummy driver * instead of calling into the module. Only functions for iso context * shutdown still need to be provided by the card driver. + * + * .read/write_csr() should never be called anymore after the dummy driver + * was bound since they are only used within request handler context. + * .set_config_rom() is never called since the card is taken out of card_list + * before switching to the dummy driver. */ -static int dummy_enable(struct fw_card *card, - const __be32 *config_rom, size_t length) -{ - BUG(); - return -1; -} - static int dummy_read_phy_reg(struct fw_card *card, int address) { return -ENODEV; @@ -558,17 +556,6 @@ static int dummy_update_phy_reg(struct fw_card *card, int address, return -ENODEV; } -static int dummy_set_config_rom(struct fw_card *card, - const __be32 *config_rom, size_t length) -{ - /* - * We take the card out of card_list before setting the dummy - * driver, so this should never get called. - */ - BUG(); - return -1; -} - static void dummy_send_request(struct fw_card *card, struct fw_packet *packet) { packet->callback(packet, card, RCODE_CANCELLED); @@ -590,15 +577,40 @@ static int dummy_enable_phys_dma(struct fw_card *card, return -ENODEV; } +static struct fw_iso_context *dummy_allocate_iso_context(struct fw_card *card, + int type, int channel, size_t header_size) +{ + return ERR_PTR(-ENODEV); +} + +static int dummy_start_iso(struct fw_iso_context *ctx, + s32 cycle, u32 sync, u32 tags) +{ + return -ENODEV; +} + +static int dummy_set_iso_channels(struct fw_iso_context *ctx, u64 *channels) +{ + return -ENODEV; +} + +static int dummy_queue_iso(struct fw_iso_context *ctx, struct fw_iso_packet *p, + struct fw_iso_buffer *buffer, unsigned long payload) +{ + return -ENODEV; +} + static const struct fw_card_driver dummy_driver_template = { - .enable = dummy_enable, - .read_phy_reg = dummy_read_phy_reg, - .update_phy_reg = dummy_update_phy_reg, - .set_config_rom = dummy_set_config_rom, - .send_request = dummy_send_request, - .cancel_packet = dummy_cancel_packet, - .send_response = dummy_send_response, - .enable_phys_dma = dummy_enable_phys_dma, + .read_phy_reg = dummy_read_phy_reg, + .update_phy_reg = dummy_update_phy_reg, + .send_request = dummy_send_request, + .send_response = dummy_send_response, + .cancel_packet = dummy_cancel_packet, + .enable_phys_dma = dummy_enable_phys_dma, + .allocate_iso_context = dummy_allocate_iso_context, + .start_iso = dummy_start_iso, + .set_iso_channels = dummy_set_iso_channels, + .queue_iso = dummy_queue_iso, }; void fw_card_release(struct kref *kref) -- cgit v1.2.3