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 --- include/linux/firewire.h | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'include') 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 a1a1132bd83d0aea51d4f19be4b4a58a064a0131 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Thu, 10 Jun 2010 08:35:06 +0200 Subject: firewire: add CSR PRIORITY_BUDGET support If supported by the OHCI controller, implement the PRIORITY_BUDGET register, which is required for nodes that can use asynchronous priority arbitration. To allow the core to determine what features the lowlevel device supports, add a new card driver callback. Signed-off-by: Clemens Ladisch --- drivers/firewire/core-transaction.c | 14 ++++++++++++++ drivers/firewire/core.h | 4 ++++ drivers/firewire/ohci.c | 27 +++++++++++++++++++++++++++ include/linux/firewire.h | 1 + 4 files changed, 46 insertions(+) (limited to 'include') diff --git a/drivers/firewire/core-transaction.c b/drivers/firewire/core-transaction.c index 8146133818dc..a61eb3fb9573 100644 --- a/drivers/firewire/core-transaction.c +++ b/drivers/firewire/core-transaction.c @@ -1126,6 +1126,20 @@ static void handle_registers(struct fw_card *card, struct fw_request *request, rcode = RCODE_TYPE_ERROR; break; + case CSR_PRIORITY_BUDGET: + if (!(card->driver->get_features(card) & + FEATURE_PRIORITY_BUDGET)) + rcode = RCODE_ADDRESS_ERROR; + else if (tcode == TCODE_READ_QUADLET_REQUEST) + *data = cpu_to_be32(card->driver-> + read_csr_reg(card, CSR_PRIORITY_BUDGET)); + else if (tcode == TCODE_WRITE_QUADLET_REQUEST) + card->driver->write_csr_reg(card, CSR_PRIORITY_BUDGET, + be32_to_cpu(*data)); + else + rcode = RCODE_TYPE_ERROR; + break; + case CSR_BROADCAST_CHANNEL: if (tcode == TCODE_READ_QUADLET_REQUEST) *data = cpu_to_be32(card->broadcast_channel); diff --git a/drivers/firewire/core.h b/drivers/firewire/core.h index efcdeb2e31e6..3b8c0f042f49 100644 --- a/drivers/firewire/core.h +++ b/drivers/firewire/core.h @@ -38,6 +38,8 @@ struct fw_packet; #define BROADCAST_CHANNEL_INITIAL (1 << 31 | 31) #define BROADCAST_CHANNEL_VALID (1 << 30) +#define FEATURE_PRIORITY_BUDGET 0x01 + struct fw_card_driver { /* * Enable the given card with the given initial config rom. @@ -78,6 +80,8 @@ struct fw_card_driver { u32 (*read_csr_reg)(struct fw_card *card, int csr_offset); void (*write_csr_reg)(struct fw_card *card, int csr_offset, u32 value); + unsigned int (*get_features)(struct fw_card *card); + struct fw_iso_context * (*allocate_iso_context)(struct fw_card *card, int type, int channel, size_t header_size); diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index 9c588fd01250..0e5413531785 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -170,6 +170,7 @@ struct fw_ohci { int generation; int request_generation; /* for timestamping incoming requests */ unsigned quirks; + unsigned int pri_req_max; u32 bus_time; /* @@ -1738,6 +1739,11 @@ static int ohci_enable(struct fw_card *card, reg_write(ohci, OHCI1394_IsochronousCycleTimer, seconds << 25); ohci->bus_time = seconds & ~0x3f; + /* 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); + ar_context_run(&ohci->ar_request_ctx); ar_context_run(&ohci->ar_response_ctx); @@ -2028,6 +2034,10 @@ static u32 ohci_read_csr_reg(struct fw_card *card, int csr_offset) value = reg_read(ohci, OHCI1394_ATRetries); return (value >> 4) & 0x0ffff00f; + case CSR_PRIORITY_BUDGET: + return (reg_read(ohci, OHCI1394_FairnessControl) & 0x3f) | + (ohci->pri_req_max << 8); + default: WARN_ON(1); return 0; @@ -2065,12 +2075,28 @@ static void ohci_write_csr_reg(struct fw_card *card, int csr_offset, u32 value) flush_writes(ohci); break; + case CSR_PRIORITY_BUDGET: + reg_write(ohci, OHCI1394_FairnessControl, value & 0x3f); + flush_writes(ohci); + break; + default: WARN_ON(1); break; } } +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; +} + static void copy_iso_headers(struct iso_context *ctx, void *p) { int i = ctx->header_length; @@ -2510,6 +2536,7 @@ static const struct fw_card_driver ohci_driver = { .enable_phys_dma = ohci_enable_phys_dma, .read_csr_reg = ohci_read_csr_reg, .write_csr_reg = ohci_write_csr_reg, + .get_features = ohci_get_features, .allocate_iso_context = ohci_allocate_iso_context, .free_iso_context = ohci_free_iso_context, diff --git a/include/linux/firewire.h b/include/linux/firewire.h index cdf8213c68ca..a50377d91254 100644 --- a/include/linux/firewire.h +++ b/include/linux/firewire.h @@ -32,6 +32,7 @@ #define CSR_CYCLE_TIME 0x200 #define CSR_BUS_TIME 0x204 #define CSR_BUSY_TIMEOUT 0x210 +#define CSR_PRIORITY_BUDGET 0x218 #define CSR_BUS_MANAGER_ID 0x21c #define CSR_BANDWIDTH_AVAILABLE 0x220 #define CSR_CHANNELS_AVAILABLE 0x224 -- cgit v1.2.3 From 3d1f46eb60b155c705e389ecdf313f11b4b91976 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Thu, 10 Jun 2010 08:35:37 +0200 Subject: firewire: core: add CSR MAINT_UTILITY support Implement the MAIN_UTILITY register, which is utterly optional but useful as a safe target for diagnostic read/write/broadcast transactions. Signed-off-by: Clemens Ladisch --- drivers/firewire/core-transaction.c | 9 +++++++++ include/linux/firewire.h | 3 +++ 2 files changed, 12 insertions(+) (limited to 'include') diff --git a/drivers/firewire/core-transaction.c b/drivers/firewire/core-transaction.c index a61eb3fb9573..dd8ef650a7cb 100644 --- a/drivers/firewire/core-transaction.c +++ b/drivers/firewire/core-transaction.c @@ -1140,6 +1140,15 @@ static void handle_registers(struct fw_card *card, struct fw_request *request, rcode = RCODE_TYPE_ERROR; break; + 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; + case CSR_BROADCAST_CHANNEL: if (tcode == TCODE_READ_QUADLET_REQUEST) *data = cpu_to_be32(card->broadcast_channel); diff --git a/include/linux/firewire.h b/include/linux/firewire.h index a50377d91254..f1160e831dad 100644 --- a/include/linux/firewire.h +++ b/include/linux/firewire.h @@ -38,6 +38,7 @@ #define CSR_CHANNELS_AVAILABLE 0x224 #define CSR_CHANNELS_AVAILABLE_HI 0x224 #define CSR_CHANNELS_AVAILABLE_LO 0x228 +#define CSR_MAINT_UTILITY 0x230 #define CSR_BROADCAST_CHANNEL 0x234 #define CSR_CONFIG_ROM 0x400 #define CSR_CONFIG_ROM_END 0x800 @@ -122,6 +123,8 @@ struct fw_card { bool broadcast_channel_allocated; u32 broadcast_channel; __be32 topology_map[(CSR_TOPOLOGY_MAP_END - CSR_TOPOLOGY_MAP) / 4]; + + __be32 maint_utility_register; }; struct fw_attribute_group { -- 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 'include') 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 db3c9cc105ee844f6cd7a1beb9926fb8e9a093ae Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Sat, 12 Jun 2010 20:30:21 +0200 Subject: firewire: replace get_features card driver hook by feature variables in the fw_card struct. The hook appeared to be an unnecessary abstraction in the card driver interface. Cleaner would be to pass those feature flags as arguments to fw_card_initialize() or fw_card_add(), but the FairnessControl register is in the SCLK domain and may therefore not be accessible while Link Power Status is off, i.e. before the card->driver->enable call from fw_card_add(). Signed-off-by: Stefan Richter --- drivers/firewire/core-topology.c | 3 +-- drivers/firewire/core-transaction.c | 3 +-- drivers/firewire/core.h | 5 ----- drivers/firewire/ohci.c | 14 ++------------ include/linux/firewire.h | 3 +++ 5 files changed, 7 insertions(+), 21 deletions(-) (limited to 'include') diff --git a/drivers/firewire/core-topology.c b/drivers/firewire/core-topology.c index 00a556f3a585..3b9667c37b67 100644 --- a/drivers/firewire/core-topology.c +++ b/drivers/firewire/core-topology.c @@ -543,8 +543,7 @@ 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 = (card->driver->get_features(card) & - FEATURE_CHANNEL_31_ALLOCATED) != 0; + card->broadcast_channel_allocated = card->broadcast_channel_auto_allocated; card->node_id = node_id; /* * Update node_id before generation to prevent anybody from using diff --git a/drivers/firewire/core-transaction.c b/drivers/firewire/core-transaction.c index 5069cfc75b50..62bf30560a3e 100644 --- a/drivers/firewire/core-transaction.c +++ b/drivers/firewire/core-transaction.c @@ -1129,8 +1129,7 @@ static void handle_registers(struct fw_card *card, struct fw_request *request, break; case CSR_PRIORITY_BUDGET: - if (!(card->driver->get_features(card) & - FEATURE_PRIORITY_BUDGET)) + if (!card->priority_budget_implemented) rcode = RCODE_ADDRESS_ERROR; else if (tcode == TCODE_READ_QUADLET_REQUEST) *data = cpu_to_be32(card->driver-> diff --git a/drivers/firewire/core.h b/drivers/firewire/core.h index 3f9e39b60bca..8dc76d8711a5 100644 --- a/drivers/firewire/core.h +++ b/drivers/firewire/core.h @@ -38,9 +38,6 @@ struct fw_packet; #define BROADCAST_CHANNEL_INITIAL (1 << 31 | 31) #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) @@ -84,8 +81,6 @@ struct fw_card_driver { u32 (*read_csr_reg)(struct fw_card *card, int csr_offset); void (*write_csr_reg)(struct fw_card *card, int csr_offset, u32 value); - unsigned int (*get_features)(struct fw_card *card); - struct fw_iso_context * (*allocate_iso_context)(struct fw_card *card, int type, int channel, size_t header_size); diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index 2abdb3268a10..09bba9315de9 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -172,7 +172,6 @@ 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; @@ -1753,15 +1752,14 @@ static int ohci_enable(struct fw_card *card, if (version >= OHCI_VERSION_1_1) { reg_write(ohci, OHCI1394_InitialChannelsAvailableHi, 0xfffffffe); - ohci->features |= FEATURE_CHANNEL_31_ALLOCATED; + card->broadcast_channel_auto_allocated = true; } /* 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; + card->priority_budget_implemented = ohci->pri_req_max != 0; ar_context_run(&ohci->ar_request_ctx); ar_context_run(&ohci->ar_response_ctx); @@ -2132,13 +2130,6 @@ 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); - - return ohci->features; -} - static void copy_iso_headers(struct iso_context *ctx, void *p) { int i = ctx->header_length; @@ -2578,7 +2569,6 @@ static const struct fw_card_driver ohci_driver = { .enable_phys_dma = ohci_enable_phys_dma, .read_csr_reg = ohci_read_csr_reg, .write_csr_reg = ohci_write_csr_reg, - .get_features = ohci_get_features, .allocate_iso_context = ohci_allocate_iso_context, .free_iso_context = ohci_free_iso_context, diff --git a/include/linux/firewire.h b/include/linux/firewire.h index 4d22643215ef..5acb5fc19180 100644 --- a/include/linux/firewire.h +++ b/include/linux/firewire.h @@ -122,6 +122,9 @@ struct fw_card { bool bm_abdicate; /* value of csr_abdicate before last bus reset */ bool csr_abdicate; /* visible in CSR STATE_CLEAR/SET registers */ + bool priority_budget_implemented; /* controller feature */ + bool broadcast_channel_auto_allocated; /* controller feature */ + bool broadcast_channel_allocated; u32 broadcast_channel; __be32 topology_map[(CSR_TOPOLOGY_MAP_END - CSR_TOPOLOGY_MAP) / 4]; -- cgit v1.2.3 From c8a94ded57e9cc2498d401b2f5c856213a3e19fb Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Sat, 12 Jun 2010 20:34:50 +0200 Subject: firewire: normalize STATE_CLEAR/SET CSR access interface Push the maintenance of STATE_CLEAR/SET.abdicate down into the card driver. This way, the read/write_csr_reg driver method works uniformly across all CSR offsets. Signed-off-by: Stefan Richter --- drivers/firewire/core-topology.c | 5 ++--- drivers/firewire/core-transaction.c | 41 +++++++++++-------------------------- drivers/firewire/core.h | 2 +- drivers/firewire/ohci.c | 19 ++++++++++++----- include/linux/firewire.h | 3 +-- 5 files changed, 30 insertions(+), 40 deletions(-) (limited to 'include') diff --git a/drivers/firewire/core-topology.c b/drivers/firewire/core-topology.c index 3b9667c37b67..56e908ba43f1 100644 --- a/drivers/firewire/core-topology.c +++ b/drivers/firewire/core-topology.c @@ -524,7 +524,7 @@ static void update_topology_map(struct fw_card *card, } void fw_core_handle_bus_reset(struct fw_card *card, int node_id, int generation, - int self_id_count, u32 *self_ids) + int self_id_count, u32 *self_ids, bool bm_abdicate) { struct fw_node *local_node; unsigned long flags; @@ -552,8 +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_abdicate = card->csr_abdicate; - card->csr_abdicate = false; + card->bm_abdicate = bm_abdicate; 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 62bf30560a3e..87d69cddb231 100644 --- a/drivers/firewire/core-transaction.c +++ b/drivers/firewire/core-transaction.c @@ -982,20 +982,6 @@ static const struct fw_address_region registers_region = { .start = CSR_REGISTER_BASE, .end = CSR_REGISTER_BASE | CSR_CONFIG_ROM, }; -static u32 read_state_register(struct fw_card *card) -{ - u32 value; - - /* 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; -} - static void update_split_timeout(struct fw_card *card) { unsigned int cycles; @@ -1021,29 +1007,25 @@ static void handle_registers(struct fw_card *card, struct fw_request *request, switch (reg) { case CSR_STATE_CLEAR: - if (tcode == TCODE_READ_QUADLET_REQUEST) { - *data = cpu_to_be32(read_state_register(card)); - } else if (tcode == TCODE_WRITE_QUADLET_REQUEST) { + if (tcode == TCODE_READ_QUADLET_REQUEST) + *data = cpu_to_be32(card->driver-> + read_csr_reg(card, CSR_STATE_CLEAR)); + 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 { + else rcode = RCODE_TYPE_ERROR; - } break; case CSR_STATE_SET: - if (tcode == TCODE_READ_QUADLET_REQUEST) { - *data = cpu_to_be32(read_state_register(card)); - } else if (tcode == TCODE_WRITE_QUADLET_REQUEST) { + if (tcode == TCODE_READ_QUADLET_REQUEST) + *data = cpu_to_be32(card->driver-> + read_csr_reg(card, CSR_STATE_SET)); + else if (tcode == TCODE_WRITE_QUADLET_REQUEST) card->driver->write_csr_reg(card, CSR_STATE_SET, be32_to_cpu(*data)); - if (*data & cpu_to_be32(CSR_STATE_BIT_ABDICATE)) - card->csr_abdicate = true; - } else { + else rcode = RCODE_TYPE_ERROR; - } break; case CSR_NODE_IDS: @@ -1063,7 +1045,8 @@ static void handle_registers(struct fw_card *card, struct fw_request *request, case CSR_RESET_START: if (tcode == TCODE_WRITE_QUADLET_REQUEST) - card->csr_abdicate = false; + card->driver->write_csr_reg(card, CSR_STATE_CLEAR, + CSR_STATE_BIT_ABDICATE); else rcode = RCODE_TYPE_ERROR; break; diff --git a/drivers/firewire/core.h b/drivers/firewire/core.h index 8dc76d8711a5..8280c625170b 100644 --- a/drivers/firewire/core.h +++ b/drivers/firewire/core.h @@ -196,7 +196,7 @@ static inline void fw_node_put(struct fw_node *node) } void fw_core_handle_bus_reset(struct fw_card *card, int node_id, - int generation, int self_id_count, u32 *self_ids); + int generation, int self_id_count, u32 *self_ids, bool bm_abdicate); void fw_destroy_nodes(struct fw_card *card); /* diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index 09bba9315de9..a55cf0911b72 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -174,6 +174,7 @@ struct fw_ohci { unsigned int pri_req_max; u32 bus_time; bool is_root; + bool csr_state_setclear_abdicate; /* * Spinlock for accessing fw_ohci data. Never call out of @@ -1529,7 +1530,9 @@ static void bus_reset_tasklet(unsigned long data) self_id_count, ohci->self_id_buffer); fw_core_handle_bus_reset(&ohci->card, ohci->node_id, generation, - self_id_count, ohci->self_id_buffer); + self_id_count, ohci->self_id_buffer, + ohci->csr_state_setclear_abdicate); + ohci->csr_state_setclear_abdicate = false; } static irqreturn_t irq_handler(int irq, void *data) @@ -2032,13 +2035,16 @@ static u32 ohci_read_csr_reg(struct fw_card *card, int csr_offset) switch (csr_offset) { case CSR_STATE_CLEAR: case CSR_STATE_SET: - /* the controller driver handles only the cmstr bit */ if (ohci->is_root && (reg_read(ohci, OHCI1394_LinkControlSet) & OHCI1394_LinkControl_cycleMaster)) - return CSR_STATE_BIT_CMSTR; + value = CSR_STATE_BIT_CMSTR; else - return 0; + value = 0; + if (ohci->csr_state_setclear_abdicate) + value |= CSR_STATE_BIT_ABDICATE; + + return value; case CSR_NODE_IDS: return reg_read(ohci, OHCI1394_NodeID) << 16; @@ -2078,12 +2084,13 @@ static void ohci_write_csr_reg(struct fw_card *card, int csr_offset, u32 value) switch (csr_offset) { case CSR_STATE_CLEAR: - /* the controller driver handles only the cmstr bit */ if ((value & CSR_STATE_BIT_CMSTR) && ohci->is_root) { reg_write(ohci, OHCI1394_LinkControlClear, OHCI1394_LinkControl_cycleMaster); flush_writes(ohci); } + if (value & CSR_STATE_BIT_ABDICATE) + ohci->csr_state_setclear_abdicate = false; break; case CSR_STATE_SET: @@ -2092,6 +2099,8 @@ static void ohci_write_csr_reg(struct fw_card *card, int csr_offset, u32 value) OHCI1394_LinkControl_cycleMaster); flush_writes(ohci); } + if (value & CSR_STATE_BIT_ABDICATE) + ohci->csr_state_setclear_abdicate = true; break; case CSR_NODE_IDS: diff --git a/include/linux/firewire.h b/include/linux/firewire.h index 5acb5fc19180..5553018d45d6 100644 --- a/include/linux/firewire.h +++ b/include/linux/firewire.h @@ -119,8 +119,7 @@ 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 bm_abdicate; bool priority_budget_implemented; /* controller feature */ bool broadcast_channel_auto_allocated; /* controller feature */ -- cgit v1.2.3 From 33e553fe2b4a983ef34a57ab1440d8d33397bb12 Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Sun, 20 Jun 2010 22:50:35 +0200 Subject: firewire: remove an unused function argument void (*fw_address_callback_t)(..., int speed, ...) is the speed that a remote node chose to transmit a request to us. In case of split transactions, firewire-core will transmit the response at that speed. Upper layer drivers on the other hand (firewire-net, -sbp2, firedtv, and userspace drivers) cannot do anything useful with that speed datum, except log it for debug purposes. But data that is merely potentially (not even actually) used for debug purposes does not belong into the API. Signed-off-by: Stefan Richter --- drivers/firewire/core-cdev.c | 3 +-- drivers/firewire/core-transaction.c | 14 +++++++------- drivers/firewire/net.c | 4 ++-- drivers/firewire/sbp2.c | 3 +-- drivers/media/dvb/firewire/firedtv-fw.c | 4 ++-- include/linux/firewire.h | 2 +- 6 files changed, 14 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index ca72cdaa68c9..4e0478d70d4d 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -632,8 +632,7 @@ static void release_request(struct client *client, static void handle_request(struct fw_card *card, struct fw_request *request, int tcode, int destination, int source, - int generation, int speed, - unsigned long long offset, + int generation, unsigned long long offset, void *payload, size_t length, void *callback_data) { struct address_handler_resource *handler = callback_data; diff --git a/drivers/firewire/core-transaction.c b/drivers/firewire/core-transaction.c index cb6390fe3686..2f67c8d5ce91 100644 --- a/drivers/firewire/core-transaction.c +++ b/drivers/firewire/core-transaction.c @@ -802,7 +802,7 @@ static void handle_exclusive_region_request(struct fw_card *card, else handler->address_callback(card, request, tcode, destination, source, - p->generation, p->speed, offset, + p->generation, offset, request->data, request->length, handler->callback_data); } @@ -840,8 +840,8 @@ static void handle_fcp_region_request(struct fw_card *card, if (is_enclosing_handler(handler, offset, request->length)) handler->address_callback(card, NULL, tcode, destination, source, - p->generation, p->speed, - offset, request->data, + p->generation, offset, + request->data, request->length, handler->callback_data); } @@ -951,8 +951,8 @@ static const struct fw_address_region topology_map_region = static void handle_topology_map(struct fw_card *card, struct fw_request *request, int tcode, int destination, int source, int generation, - int speed, unsigned long long offset, - void *payload, size_t length, void *callback_data) + unsigned long long offset, void *payload, size_t length, + void *callback_data) { int start; @@ -996,8 +996,8 @@ static void update_split_timeout(struct fw_card *card) static void handle_registers(struct fw_card *card, struct fw_request *request, int tcode, int destination, int source, int generation, - int speed, unsigned long long offset, - void *payload, size_t length, void *callback_data) + unsigned long long offset, void *payload, size_t length, + void *callback_data) { int reg = offset & ~CSR_REGISTER_BASE; __be32 *data = payload; diff --git a/drivers/firewire/net.c b/drivers/firewire/net.c index 2d3dc7ded0a9..4bb3fb882f63 100644 --- a/drivers/firewire/net.c +++ b/drivers/firewire/net.c @@ -805,8 +805,8 @@ static int fwnet_incoming_packet(struct fwnet_device *dev, __be32 *buf, int len, static void fwnet_receive_packet(struct fw_card *card, struct fw_request *r, int tcode, int destination, int source, int generation, - int speed, unsigned long long offset, void *payload, - size_t length, void *callback_data) + unsigned long long offset, void *payload, size_t length, + void *callback_data) { struct fwnet_device *dev = callback_data; int rcode; diff --git a/drivers/firewire/sbp2.c b/drivers/firewire/sbp2.c index ae715c82da2e..1931964c4fbf 100644 --- a/drivers/firewire/sbp2.c +++ b/drivers/firewire/sbp2.c @@ -410,8 +410,7 @@ static void free_orb(struct kref *kref) static void sbp2_status_write(struct fw_card *card, struct fw_request *request, int tcode, int destination, int source, - int generation, int speed, - unsigned long long offset, + int generation, unsigned long long offset, void *payload, size_t length, void *callback_data) { struct sbp2_logical_unit *lu = callback_data; diff --git a/drivers/media/dvb/firewire/firedtv-fw.c b/drivers/media/dvb/firewire/firedtv-fw.c index 4253b7ab0097..4dcae63f8cff 100644 --- a/drivers/media/dvb/firewire/firedtv-fw.c +++ b/drivers/media/dvb/firewire/firedtv-fw.c @@ -194,8 +194,8 @@ static const struct firedtv_backend backend = { static void handle_fcp(struct fw_card *card, struct fw_request *request, int tcode, int destination, int source, int generation, - int speed, unsigned long long offset, - void *payload, size_t length, void *callback_data) + unsigned long long offset, void *payload, size_t length, + void *callback_data) { struct firedtv *f, *fdtv = NULL; struct fw_device *device; diff --git a/include/linux/firewire.h b/include/linux/firewire.h index 5553018d45d6..e44b502c8341 100644 --- a/include/linux/firewire.h +++ b/include/linux/firewire.h @@ -265,7 +265,7 @@ typedef void (*fw_transaction_callback_t)(struct fw_card *card, int rcode, typedef void (*fw_address_callback_t)(struct fw_card *card, struct fw_request *request, int tcode, int destination, int source, - int generation, int speed, + int generation, unsigned long long offset, void *data, size_t length, void *callback_data); -- cgit v1.2.3 From 604f45167824e18ad5766e51ecf1d4d65f15118d Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Sun, 20 Jun 2010 22:52:55 +0200 Subject: firewire: cdev: freeze FW_CDEV_VERSION due to libraw1394 bug libraw1394 v2.0.0...v2.0.5 takes FW_CDEV_VERSION from an externally installed header file and uses it to declare its own implementation level in FW_CDEV_IOC_GET_INFO. This is wrong; it should set the real version for which it was actually written. If we add features to the kernel ABI that require the kernel to check a client's implementation level, we can not trust the client version if it was set from FW_CDEV_VERSION. Hence freeze FW_CDEV_VERSION at the current value (no damage has been done yet), clearly document FW_CDEV_VERSION as a dummy version and what clients are expected to do with fw_cdev_get_info.version, and use a new defined constant (which is not placed into the exported header file) as kernel implementation level. Note, in order to check in client program source code which features are present in an externally installed linux/firewire-cdev.h, use preprocessor directives like #ifdef FW_CDEV_IOC_ALLOCATE_ISO_RESOURCE or #ifdef FW_CDEV_EVENT_ISO_RESOURCE_ALLOCATED instead of a check of FW_CDEV_VERSION. Signed-off-by: Stefan Richter --- drivers/firewire/core-cdev.c | 7 ++++++- include/linux/firewire-cdev.h | 22 ++++++++++++++++------ 2 files changed, 22 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index 8f8c8eeaf046..0cf86bcbeea2 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -46,6 +46,11 @@ #include "core.h" +/* + * ABI version history is documented in linux/firewire-cdev.h. + */ +#define FW_CDEV_KERNEL_VERSION 3 + struct client { u32 version; struct fw_device *device; @@ -395,7 +400,7 @@ static int ioctl_get_info(struct client *client, union ioctl_arg *arg) unsigned long ret = 0; client->version = a->version; - a->version = FW_CDEV_VERSION; + a->version = FW_CDEV_KERNEL_VERSION; a->card = client->device->card->index; down_read(&fw_device_rwsem); diff --git a/include/linux/firewire-cdev.h b/include/linux/firewire-cdev.h index 6ffb24a1f2f2..0d0cc07358af 100644 --- a/include/linux/firewire-cdev.h +++ b/include/linux/firewire-cdev.h @@ -219,7 +219,7 @@ union fw_cdev_event { struct fw_cdev_event_response response; struct fw_cdev_event_request request; struct fw_cdev_event_iso_interrupt iso_interrupt; - struct fw_cdev_event_iso_resource iso_resource; + struct fw_cdev_event_iso_resource iso_resource; /* added in 2.6.30 */ }; /* available since kernel version 2.6.22 */ @@ -252,22 +252,32 @@ union fw_cdev_event { #define FW_CDEV_IOC_GET_CYCLE_TIMER2 _IOWR('#', 0x14, struct fw_cdev_get_cycle_timer2) /* - * FW_CDEV_VERSION History + * ABI version history * 1 (2.6.22) - initial version + * (2.6.24) - added %FW_CDEV_IOC_GET_CYCLE_TIMER * 2 (2.6.30) - changed &fw_cdev_event_iso_interrupt.header if * &fw_cdev_create_iso_context.header_size is 8 or more + * - added %FW_CDEV_IOC_*_ISO_RESOURCE*, + * %FW_CDEV_IOC_GET_SPEED, %FW_CDEV_IOC_SEND_BROADCAST_REQUEST, + * %FW_CDEV_IOC_SEND_STREAM_PACKET * (2.6.32) - added time stamp to xmit &fw_cdev_event_iso_interrupt * (2.6.33) - IR has always packet-per-buffer semantics now, not one of * dual-buffer or packet-per-buffer depending on hardware * 3 (2.6.34) - made &fw_cdev_get_cycle_timer reliable + * - added %FW_CDEV_IOC_GET_CYCLE_TIMER2 */ -#define FW_CDEV_VERSION 3 +#define FW_CDEV_VERSION 3 /* Meaningless; don't use this macro. */ /** * struct fw_cdev_get_info - General purpose information ioctl - * @version: The version field is just a running serial number. - * We never break backwards compatibility, but may add more - * structs and ioctls in later revisions. + * @version: The version field is just a running serial number. Both an + * input parameter (ABI version implemented by the client) and + * output parameter (ABI version implemented by the kernel). + * A client must not fill in an %FW_CDEV_VERSION defined from an + * included kernel header file but the actual version for which + * the client was implemented. This is necessary for forward + * compatibility. We never break backwards compatibility, but + * may add more structs, events, and ioctls in later revisions. * @rom_length: If @rom is non-zero, at most rom_length bytes of configuration * ROM will be copied into that user space address. In either * case, @rom_length is updated with the actual length of the -- cgit v1.2.3 From e205597d188a9ea69ce43f740a14f07b3f5b996a Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Sun, 20 Jun 2010 22:53:55 +0200 Subject: firewire: cdev: fix ABI for FCP and address range mapping, add fw_cdev_event_request2 The problem: A target-like userspace driver, e.g. AV/C target or SBP-2/3 target, needs to be able to act as responder and requester. In the latter role, it needs to send requests to nods from which it received requests. This is currently impossible because fw_cdev_event_request lacks information about sender node ID. Reported-by: Jay Fenlason Libffado + libraw1394 + firewire-core is currently unable to drive two or more audio devices on the same bus. Reported-by: Arnold Krille This is because libffado requires destination node ID of FCP requests and sender node ID of FCP responses to match. It even prohibits libffado from working with a bus on which libraw1394 opens a /dev/fw* as default ioctl device that does not correspond with the audio device. This is because libraw1394 does not receive the sender node ID from the kernel. Moreover, fw_cdev_event_request makes it impossible to tell unicast and broadcast write requests apart. The fix: Add a replacement of struct fw_cdev_event_request request, boringly called struct fw_cdev_event_request2. The new event will be sent to a userspace client instead of the old one if the client claims compatibility with ABI version 4 or later. libraw1394 needs to be extended to make use of the new event, in order to properly support libffado and other FCP or address range mapping users who require correct sender node IDs. Further notes: While we are at it, change back the range of possible values of fw_cdev_event_request.tcode to 0x0...0xb like in ABI version <= 3. The preceding change "firewire: expose extended tcode of incoming lock requests to (userspace) drivers" expanded it to 0x0...0x17 which could catch sloppily coded clients by surprise. The extended range of codes is only used in the new fw_cdev_event_request2.tcode. Jay and I also suggested an alternative approach to fix the ABI for incoming requests: Add an FW_CDEV_IOC_GET_REQUEST_INFO ioctl which can be called after reception of an fw_cdev_event_request, before issuing of the closing FW_CDEV_IOC_SEND_RESPONSE ioctl. The new ioctl would reveal the vital information about a request that fw_cdev_event_request lacks. Jay showed an implementation of this approach. The former event approach adds 27 LOC of rather trivial code to core-cdev.c, the ioctl approach 34 LOC, some of which is nontrivial. The ioctl approach would certainly also add more LOC to userspace programs which require the expanded information on inbound requests. This approach is probably only on the lighter-weight side in case of clients that want to be compatible with kernels that lack the new capability, like libraw1394. However, the code to be added to such libraw1394-like clients in case of the event approach is a straight- forward additional switch () case in its event handler. Signed-off-by: Stefan Richter --- drivers/firewire/core-cdev.c | 45 ++++++++++++++++++++----- include/linux/firewire-cdev.h | 76 +++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 110 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index 0cf86bcbeea2..9b8df2039155 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -49,7 +49,8 @@ /* * ABI version history is documented in linux/firewire-cdev.h. */ -#define FW_CDEV_KERNEL_VERSION 3 +#define FW_CDEV_KERNEL_VERSION 4 +#define FW_CDEV_VERSION_EVENT_REQUEST2 4 struct client { u32 version; @@ -176,7 +177,10 @@ struct outbound_transaction_event { struct inbound_transaction_event { struct event event; - struct fw_cdev_event_request request; + union { + struct fw_cdev_event_request request; + struct fw_cdev_event_request2 request2; + } req; }; struct iso_interrupt_event { @@ -645,6 +649,7 @@ static void handle_request(struct fw_card *card, struct fw_request *request, struct address_handler_resource *handler = callback_data; struct inbound_transaction_resource *r; struct inbound_transaction_event *e; + size_t event_size0; void *fcp_frame = NULL; int ret; @@ -678,15 +683,37 @@ static void handle_request(struct fw_card *card, struct fw_request *request, if (ret < 0) goto failed; - e->request.type = FW_CDEV_EVENT_REQUEST; - e->request.tcode = tcode; - e->request.offset = offset; - e->request.length = length; - e->request.handle = r->resource.handle; - e->request.closure = handler->closure; + if (handler->client->version < FW_CDEV_VERSION_EVENT_REQUEST2) { + struct fw_cdev_event_request *req = &e->req.request; + + if (tcode & 0x10) + tcode = TCODE_LOCK_REQUEST; + + req->type = FW_CDEV_EVENT_REQUEST; + req->tcode = tcode; + req->offset = offset; + req->length = length; + req->handle = r->resource.handle; + req->closure = handler->closure; + event_size0 = sizeof(*req); + } else { + struct fw_cdev_event_request2 *req = &e->req.request2; + + req->type = FW_CDEV_EVENT_REQUEST2; + req->tcode = tcode; + req->offset = offset; + req->source_node_id = source; + req->destination_node_id = destination; + req->card = card->index; + req->generation = generation; + req->length = length; + req->handle = r->resource.handle; + req->closure = handler->closure; + event_size0 = sizeof(*req); + } queue_event(handler->client, &e->event, - &e->request, sizeof(e->request), r->data, length); + &e->req, event_size0, r->data, length); return; failed: diff --git a/include/linux/firewire-cdev.h b/include/linux/firewire-cdev.h index 0d0cc07358af..52c7ffe934ad 100644 --- a/include/linux/firewire-cdev.h +++ b/include/linux/firewire-cdev.h @@ -32,6 +32,9 @@ #define FW_CDEV_EVENT_ISO_RESOURCE_ALLOCATED 0x04 #define FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED 0x05 +/* available since kernel version 2.6.36 */ +#define FW_CDEV_EVENT_REQUEST2 0x06 + /** * struct fw_cdev_event_common - Common part of all fw_cdev_event_ types * @closure: For arbitrary use by userspace @@ -98,11 +101,46 @@ struct fw_cdev_event_response { }; /** - * struct fw_cdev_event_request - Sent on incoming request to an address region + * struct fw_cdev_event_request - Old version of &fw_cdev_event_request2 * @closure: See &fw_cdev_event_common; set by %FW_CDEV_IOC_ALLOCATE ioctl * @type: See &fw_cdev_event_common; always %FW_CDEV_EVENT_REQUEST + * @tcode: See &fw_cdev_event_request2 + * @offset: See &fw_cdev_event_request2 + * @handle: See &fw_cdev_event_request2 + * @length: See &fw_cdev_event_request2 + * @data: See &fw_cdev_event_request2 + * + * This event is sent instead of &fw_cdev_event_request2 if the kernel or + * the client implements ABI version <= 3. + * + * Unlike &fw_cdev_event_request2, the sender identity cannot be established, + * broadcast write requests cannot be distinguished from unicast writes, and + * @tcode of lock requests is %TCODE_LOCK_REQUEST. + * + * Requests to the FCP_REQUEST or FCP_RESPONSE register are responded to as + * with &fw_cdev_event_request2, except in kernel 2.6.32 and older which send + * the response packet of the client's %FW_CDEV_IOC_SEND_RESPONSE ioctl. + */ +struct fw_cdev_event_request { + __u64 closure; + __u32 type; + __u32 tcode; + __u64 offset; + __u32 handle; + __u32 length; + __u32 data[0]; +}; + +/** + * struct fw_cdev_event_request2 - Sent on incoming request to an address region + * @closure: See &fw_cdev_event_common; set by %FW_CDEV_IOC_ALLOCATE ioctl + * @type: See &fw_cdev_event_common; always %FW_CDEV_EVENT_REQUEST2 * @tcode: Transaction code of the incoming request * @offset: The offset into the 48-bit per-node address space + * @source_node_id: Sender node ID + * @destination_node_id: Destination node ID + * @card: The index of the card from which the request came + * @generation: Bus generation in which the request is valid * @handle: Reference to the kernel-side pending request * @length: Data length, i.e. the request's payload size in bytes * @data: Incoming data, if any @@ -115,12 +153,42 @@ struct fw_cdev_event_response { * * The payload data for requests carrying data (write and lock requests) * follows immediately and can be accessed through the @data field. + * + * Unlike &fw_cdev_event_request, @tcode of lock requests is one of the + * firewire-core specific %TCODE_LOCK_MASK_SWAP...%TCODE_LOCK_VENDOR_DEPENDENT, + * i.e. encodes the extended transaction code. + * + * @card may differ from &fw_cdev_get_info.card because requests are received + * from all cards of the Linux host. @source_node_id, @destination_node_id, and + * @generation pertain to that card. Destination node ID and bus generation may + * therefore differ from the corresponding fields of the last + * &fw_cdev_event_bus_reset. + * + * @destination_node_id may also differ from the current node ID because of a + * non-local bus ID part or in case of a broadcast write request. Note, a + * client must call an %FW_CDEV_IOC_SEND_RESPONSE ioctl even in case of a + * broadcast write request; the kernel will then release the kernel-side pending + * request but will not actually send a response packet. + * + * In case of a write request to FCP_REQUEST or FCP_RESPONSE, the kernel already + * sent a write response immediately after the request was received; in this + * case the client must still call an %FW_CDEV_IOC_SEND_RESPONSE ioctl to + * release the kernel-side pending request, though another response won't be + * sent. + * + * If the client subsequently needs to initiate requests to the sender node of + * an &fw_cdev_event_request2, it needs to use a device file with matching + * card index, node ID, and generation for outbound requests. */ -struct fw_cdev_event_request { +struct fw_cdev_event_request2 { __u64 closure; __u32 type; __u32 tcode; __u64 offset; + __u32 source_node_id; + __u32 destination_node_id; + __u32 card; + __u32 generation; __u32 handle; __u32 length; __u32 data[0]; @@ -200,6 +268,7 @@ struct fw_cdev_event_iso_resource { * @bus_reset: Valid if @common.type == %FW_CDEV_EVENT_BUS_RESET * @response: Valid if @common.type == %FW_CDEV_EVENT_RESPONSE * @request: Valid if @common.type == %FW_CDEV_EVENT_REQUEST + * @request2: Valid if @common.type == %FW_CDEV_EVENT_REQUEST2 * @iso_interrupt: Valid if @common.type == %FW_CDEV_EVENT_ISO_INTERRUPT * @iso_resource: Valid if @common.type == * %FW_CDEV_EVENT_ISO_RESOURCE_ALLOCATED or @@ -218,6 +287,7 @@ union fw_cdev_event { struct fw_cdev_event_bus_reset bus_reset; struct fw_cdev_event_response response; struct fw_cdev_event_request request; + struct fw_cdev_event_request2 request2; /* added in 2.6.36 */ struct fw_cdev_event_iso_interrupt iso_interrupt; struct fw_cdev_event_iso_resource iso_resource; /* added in 2.6.30 */ }; @@ -263,8 +333,10 @@ union fw_cdev_event { * (2.6.32) - added time stamp to xmit &fw_cdev_event_iso_interrupt * (2.6.33) - IR has always packet-per-buffer semantics now, not one of * dual-buffer or packet-per-buffer depending on hardware + * - 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 */ #define FW_CDEV_VERSION 3 /* Meaningless; don't use this macro. */ -- cgit v1.2.3 From 3b2b65d68fc87b02ac393a031a4ebb3de84a8218 Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Sun, 20 Jun 2010 22:54:22 +0200 Subject: firewire: cdev: extend fw_cdev_event_iso_interrupt documentation Add information regarding the 2.6.32 update to the xmit variant of fw_cdev_event_iso_interrupt. Signed-off-by: Stefan Richter --- include/linux/firewire-cdev.h | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/linux/firewire-cdev.h b/include/linux/firewire-cdev.h index 52c7ffe934ad..8b9b27373219 100644 --- a/include/linux/firewire-cdev.h +++ b/include/linux/firewire-cdev.h @@ -204,10 +204,21 @@ struct fw_cdev_event_request2 { * @header: Stripped headers, if any * * This event is sent when the controller has completed an &fw_cdev_iso_packet - * with the %FW_CDEV_ISO_INTERRUPT bit set. In the receive case, the headers - * stripped of all packets up until and including the interrupt packet are - * returned in the @header field. The amount of header data per packet is as - * specified at iso context creation by &fw_cdev_create_iso_context.header_size. + * with the %FW_CDEV_ISO_INTERRUPT bit set. + * + * Isochronous transmit events: + * + * In version 1 of the ABI, &header_length is 0. In version 3 and some + * implementations of version 2 of the ABI, &header_length is a multiple of 4 + * and &header contains timestamps of all packets up until the interrupt packet. + * The format of the timestamps is as described below for isochronous reception. + * + * Isochronous receive events: + * + * The headers stripped of all packets up until and including the interrupt + * packet are returned in the @header field. The amount of header data per + * packet is as specified at iso context creation by + * &fw_cdev_create_iso_context.header_size. * * In version 1 of this ABI, header data consisted of the 1394 isochronous * packet header, followed by quadlets from the packet payload if -- 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 'include') 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 'include') 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 'include') 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 d505e6e87127d4dbdaa5d91561eed810c180ca23 Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Sat, 17 Jul 2010 21:36:02 +0200 Subject: firewire: cdev: some clarifications to the API documentation Response events: - are generated on more occasions than their documentation claimed. CSR allocation: - An already occupied CSR can be determined from errno==EBUSY. Bus resets: - Note that FW_CDEV_IOC_INITIATE_BUS_RESET is nonblocking and that the client is not required to observe a grace period since kernels 2.6.36+ will enforce it now (commit 02d37bed). - The possible values of fw_cdev_initiate_bus_reset.type are listed in the kerneldoc comment already. - Clarify that an application that uses FW_CDEV_IOC_ADD_DESCRIPTOR and FW_CDEV_IOC_REMOVE_DESCRIPTOR does not have to issue a bus reset. Isochronous I/O contexts: - At most one can be created per open file descriptor. Signed-off-by: Stefan Richter --- include/linux/firewire-cdev.h | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/linux/firewire-cdev.h b/include/linux/firewire-cdev.h index d31022b05bd9..fde9568151d5 100644 --- a/include/linux/firewire-cdev.h +++ b/include/linux/firewire-cdev.h @@ -84,8 +84,9 @@ struct fw_cdev_event_bus_reset { /** * struct fw_cdev_event_response - Sent when a response packet was received - * @closure: See &fw_cdev_event_common; - * set by %FW_CDEV_IOC_SEND_REQUEST ioctl + * @closure: See &fw_cdev_event_common; set by %FW_CDEV_IOC_SEND_REQUEST + * or %FW_CDEV_IOC_SEND_BROADCAST_REQUEST + * or %FW_CDEV_IOC_SEND_STREAM_PACKET ioctl * @type: See &fw_cdev_event_common; always %FW_CDEV_EVENT_RESPONSE * @rcode: Response code returned by the remote node * @length: Data length, i.e. the response's payload size in bytes @@ -95,6 +96,11 @@ struct fw_cdev_event_bus_reset { * sent by %FW_CDEV_IOC_SEND_REQUEST ioctl. The payload data for responses * carrying data (read and lock responses) follows immediately and can be * accessed through the @data field. + * + * The event is also generated after conclusions of transactions that do not + * involve response packets. This includes unified write transactions, + * broadcast write transactions, and transmission of asynchronous stream + * packets. @rcode indicates success or failure of such transmissions. */ struct fw_cdev_event_response { __u64 closure; @@ -447,7 +453,9 @@ struct fw_cdev_send_response { * range to be used for later deallocation of the range. * * The address range is allocated on all local nodes. The address allocation - * is exclusive except for the FCP command and response registers. + * is exclusive except for the FCP command and response registers. If an + * exclusive address region is already in use, the ioctl fails with errno set + * to %EBUSY. */ struct fw_cdev_allocate { __u64 offset; @@ -475,9 +483,14 @@ struct fw_cdev_deallocate { * Initiate a bus reset for the bus this device is on. The bus reset can be * either the original (long) bus reset or the arbitrated (short) bus reset * introduced in 1394a-2000. + * + * The ioctl returns immediately. A subsequent &fw_cdev_event_bus_reset + * indicates when the reset actually happened. Since ABI v4, this may be + * considerably later than the ioctl because the kernel ensures a grace period + * between subsequent bus resets as per IEEE 1394 bus management specification. */ struct fw_cdev_initiate_bus_reset { - __u32 type; /* FW_CDEV_SHORT_RESET or FW_CDEV_LONG_RESET */ + __u32 type; }; /** @@ -501,9 +514,10 @@ struct fw_cdev_initiate_bus_reset { * * @immediate, @key, and @data array elements are CPU-endian quadlets. * - * If successful, the kernel adds the descriptor and writes back a handle to the - * kernel-side object to be used for later removal of the descriptor block and - * immediate key. + * If successful, the kernel adds the descriptor and writes back a @handle to + * the kernel-side object to be used for later removal of the descriptor block + * and immediate key. The kernel will also generate a bus reset to signal the + * change of the configuration ROM to other nodes. * * This ioctl affects the configuration ROMs of all local nodes. * The ioctl only succeeds on device files which represent a local node. @@ -522,7 +536,8 @@ struct fw_cdev_add_descriptor { * descriptor was added * * Remove a descriptor block and accompanying immediate key from the local - * nodes' configuration ROMs. + * nodes' configuration ROMs. The kernel will also generate a bus reset to + * signal the change of the configuration ROM to other nodes. */ struct fw_cdev_remove_descriptor { __u32 handle; @@ -554,6 +569,8 @@ struct fw_cdev_remove_descriptor { * * Note that the effect of a @header_size > 4 depends on * &fw_cdev_get_info.version, as documented at &fw_cdev_event_iso_interrupt. + * + * No more than one iso context can be created per fd. */ struct fw_cdev_create_iso_context { __u32 type; -- cgit v1.2.3 From 850bb6f23b93c04ce1e4509a87fa607dc17d97c1 Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Fri, 16 Jul 2010 22:25:14 +0200 Subject: firewire: cdev: add PHY packet transmission Add an FW_CDEV_IOC_SEND_PHY_PACKET ioctl() for /dev/fw* which can be used to implement bus management related functionality in userspace. This is also half of the functionality (the transmit part) that is needed to support a userspace implementation of a VersaPHY transaction layer. Safety considerations: - PHY packets are generally broadcasts and may have interesting effects on PHYs and the bus, e.g. make asynchronous arbitration impossible due to too low gap count. Hence some kind of elevated privileges should be required of a process to be able to send 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. - The kernel does not check integrity of the supplied packet data. That would be far too much code, considering the many kinds of PHY packets. A process which got the privilege to send these packets is trusted to do it correctly. Just like with the other "send packet" ioctls, a non-blocking API is chosen; i.e. the ioctl may return even before AT DMA started. After transmission, an event for poll()/read() is enqueued. Most users are going to need a blocking API, but a blocking userspace wrapper is easy to implement, and the second of the two existing libraw1394 calls raw1394_phy_packet_write() and raw1394_start_phy_packet_write() can be better supported that way. Signed-off-by: Stefan Richter --- drivers/firewire/core-cdev.c | 64 +++++++++++++++++++++++++++++++++++++++++++ include/linux/firewire-cdev.h | 44 ++++++++++++++++++++++++++++- 2 files changed, 107 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index acf4fa1f3f8c..f95719926487 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -194,6 +194,13 @@ struct iso_resource_event { struct fw_cdev_event_iso_resource iso_resource; }; +struct outbound_phy_packet_event { + struct event event; + struct client *client; + struct fw_packet p; + struct fw_cdev_event_phy_packet phy_packet; +}; + static inline void __user *u64_to_uptr(__u64 value) { return (void __user *)(unsigned long)value; @@ -396,6 +403,7 @@ union ioctl_arg { struct fw_cdev_allocate_iso_resource allocate_iso_resource; 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; }; static int ioctl_get_info(struct client *client, union ioctl_arg *arg) @@ -1384,6 +1392,61 @@ static int ioctl_send_stream_packet(struct client *client, union ioctl_arg *arg) return init_request(client, &request, dest, a->speed); } +static void outbound_phy_packet_callback(struct fw_packet *packet, + struct fw_card *card, int status) +{ + struct outbound_phy_packet_event *e = + container_of(packet, struct outbound_phy_packet_event, p); + + switch (status) { + /* expected: */ + case ACK_COMPLETE: e->phy_packet.rcode = RCODE_COMPLETE; break; + /* should never happen with PHY packets: */ + case ACK_PENDING: e->phy_packet.rcode = RCODE_COMPLETE; break; + case ACK_BUSY_X: + case ACK_BUSY_A: + case ACK_BUSY_B: e->phy_packet.rcode = RCODE_BUSY; break; + case ACK_DATA_ERROR: e->phy_packet.rcode = RCODE_DATA_ERROR; break; + case ACK_TYPE_ERROR: e->phy_packet.rcode = RCODE_TYPE_ERROR; break; + /* stale generation; cancelled; on certain controllers: no ack */ + default: e->phy_packet.rcode = status; break; + } + + queue_event(e->client, &e->event, + &e->phy_packet, sizeof(e->phy_packet), NULL, 0); + client_put(e->client); +} + +static int ioctl_send_phy_packet(struct client *client, union ioctl_arg *arg) +{ + struct fw_cdev_send_phy_packet *a = &arg->send_phy_packet; + struct fw_card *card = client->device->card; + struct outbound_phy_packet_event *e; + + /* Access policy: Allow this ioctl only on local nodes' device files. */ + if (!client->device->is_local) + return -ENOSYS; + + e = kzalloc(sizeof(*e), GFP_KERNEL); + if (e == NULL) + return -ENOMEM; + + client_get(client); + e->client = client; + e->p.speed = SCODE_100; + e->p.generation = a->generation; + e->p.header[0] = a->data[0]; + e->p.header[1] = a->data[1]; + e->p.header_length = 8; + e->p.callback = outbound_phy_packet_callback; + e->phy_packet.closure = a->closure; + e->phy_packet.type = FW_CDEV_EVENT_PHY_PACKET_SENT; + + card->driver->send_request(card, &e->p); + + return 0; +} + static int (* const ioctl_handlers[])(struct client *, union ioctl_arg *) = { [0x00] = ioctl_get_info, [0x01] = ioctl_send_request, @@ -1406,6 +1469,7 @@ static int (* const ioctl_handlers[])(struct client *, union ioctl_arg *) = { [0x12] = ioctl_send_broadcast_request, [0x13] = ioctl_send_stream_packet, [0x14] = ioctl_get_cycle_timer2, + [0x15] = ioctl_send_phy_packet, }; static int dispatch_ioctl(struct client *client, diff --git a/include/linux/firewire-cdev.h b/include/linux/firewire-cdev.h index fde9568151d5..5bc051b9a013 100644 --- a/include/linux/firewire-cdev.h +++ b/include/linux/firewire-cdev.h @@ -34,6 +34,7 @@ /* available since kernel version 2.6.36 */ #define FW_CDEV_EVENT_REQUEST2 0x06 +#define FW_CDEV_EVENT_PHY_PACKET_SENT 0x07 /** * struct fw_cdev_event_common - Common part of all fw_cdev_event_ types @@ -283,6 +284,19 @@ struct fw_cdev_event_iso_resource { __s32 bandwidth; }; +/** + * 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 + * @rcode: %RCODE_..., indicates success or failure of transmission + */ +struct fw_cdev_event_phy_packet { + __u64 closure; + __u32 type; + __u32 rcode; +}; + /** * union fw_cdev_event - Convenience union of fw_cdev_event_ types * @common: Valid for all types @@ -294,6 +308,7 @@ struct fw_cdev_event_iso_resource { * @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 * * Convenience union for userspace use. Events could be read(2) into an * appropriately aligned char buffer and then cast to this union for further @@ -311,6 +326,7 @@ union fw_cdev_event { struct fw_cdev_event_request2 request2; /* added in 2.6.36 */ struct fw_cdev_event_iso_interrupt iso_interrupt; struct fw_cdev_event_iso_resource iso_resource; /* added in 2.6.30 */ + struct fw_cdev_event_phy_packet phy_packet; /* added in 2.6.36 */ }; /* available since kernel version 2.6.22 */ @@ -342,6 +358,9 @@ union fw_cdev_event { /* available since kernel version 2.6.34 */ #define FW_CDEV_IOC_GET_CYCLE_TIMER2 _IOWR('#', 0x14, struct fw_cdev_get_cycle_timer2) +/* available since kernel version 2.6.36 */ +#define FW_CDEV_IOC_SEND_PHY_PACKET _IOWR('#', 0x15, struct fw_cdev_send_phy_packet) + /* * ABI version history * 1 (2.6.22) - initial version @@ -357,8 +376,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 + * 4 (2.6.36) - added %FW_CDEV_EVENT_REQUEST2, %FW_CDEV_EVENT_PHY_PACKET_SENT * - implemented &fw_cdev_event_bus_reset.bm_node_id + * - added %FW_CDEV_IOC_SEND_PHY_PACKET */ #define FW_CDEV_VERSION 3 /* Meaningless; don't use this macro. */ @@ -808,4 +828,26 @@ struct fw_cdev_send_stream_packet { __u32 speed; }; +/** + * struct fw_cdev_send_phy_packet - send a PHY packet + * @closure: Passed back to userspace in the PHY-packet-sent event + * @data: First and second quadlet of the PHY packet + * @generation: The bus generation where packet is valid + * + * The %FW_CDEV_IOC_SEND_PHY_PACKET ioctl sends a PHY packet to all nodes + * on the same card as this device. After transmission, an + * %FW_CDEV_EVENT_PHY_PACKET_SENT event is generated. + * + * The payload @data[] shall be specified in host byte order. Usually, + * @data[1] needs to be the bitwise inverse of @data[0]. VersaPHY packets + * are an exception to this rule. + * + * The ioctl is only permitted on device files which represent a local node. + */ +struct fw_cdev_send_phy_packet { + __u64 closure; + __u32 data[2]; + __u32 generation; +}; + #endif /* _LINUX_FIREWIRE_CDEV_H */ -- 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 'include') 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 cc550216ae9a2993ef3973464714dc1a39ab1f86 Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Sun, 18 Jul 2010 13:00:50 +0200 Subject: firewire: cdev: add PHY pinging This extends the FW_CDEV_IOC_SEND_PHY_PACKET ioctl() for /dev/fw* to be useful for ping time measurements. One application for it would be gap count optimization in userspace that is based on ping times rather than hop count. (The latter is implemented in firewire-core itself but is not applicable to beta PHYs that act as repeater.) Signed-off-by: Stefan Richter --- drivers/firewire/core-cdev.c | 9 ++++++--- drivers/firewire/core.h | 5 +++++ drivers/firewire/ohci.c | 3 +++ include/linux/firewire-cdev.h | 5 ++++- 4 files changed, 18 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index 0425dd5dfcd3..31863cf8b6c4 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -1423,9 +1423,10 @@ static void outbound_phy_packet_callback(struct fw_packet *packet, /* stale generation; cancelled; on certain controllers: no ack */ default: e->phy_packet.rcode = status; break; } + e->phy_packet.data[0] = packet->timestamp; - queue_event(e->client, &e->event, - &e->phy_packet, sizeof(e->phy_packet), NULL, 0); + queue_event(e->client, &e->event, &e->phy_packet, + sizeof(e->phy_packet) + e->phy_packet.length, NULL, 0); client_put(e->client); } @@ -1439,7 +1440,7 @@ static int ioctl_send_phy_packet(struct client *client, union ioctl_arg *arg) if (!client->device->is_local) return -ENOSYS; - e = kzalloc(sizeof(*e), GFP_KERNEL); + e = kzalloc(sizeof(*e) + 4, GFP_KERNEL); if (e == NULL) return -ENOMEM; @@ -1453,6 +1454,8 @@ static int ioctl_send_phy_packet(struct client *client, union ioctl_arg *arg) e->p.callback = outbound_phy_packet_callback; e->phy_packet.closure = a->closure; e->phy_packet.type = FW_CDEV_EVENT_PHY_PACKET_SENT; + if (is_ping_packet(a->data)) + e->phy_packet.length = 4; card->driver->send_request(card, &e->p); diff --git a/drivers/firewire/core.h b/drivers/firewire/core.h index 3102b6b63438..28621e44b111 100644 --- a/drivers/firewire/core.h +++ b/drivers/firewire/core.h @@ -234,4 +234,9 @@ void fw_fill_response(struct fw_packet *response, u32 *request_header, void fw_send_phy_config(struct fw_card *card, int node_id, int generation, int gap_count); +static inline bool is_ping_packet(u32 *data) +{ + return (data[0] & 0xc0ffffff) == 0 && ~data[0] == data[1]; +} + #endif /* _FIREWIRE_CORE_H */ diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index 08afccc66333..5f6bb2c53808 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -1068,6 +1068,9 @@ static int at_context_queue_packet(struct context *ctx, header[1] = cpu_to_le32(packet->header[0]); header[2] = cpu_to_le32(packet->header[1]); d[0].req_count = cpu_to_le16(12); + + if (is_ping_packet(packet->header)) + d[0].control |= cpu_to_le16(DESCRIPTOR_PING); break; case 4: diff --git a/include/linux/firewire-cdev.h b/include/linux/firewire-cdev.h index b87409160794..da0fec7e8dc0 100644 --- a/include/linux/firewire-cdev.h +++ b/include/linux/firewire-cdev.h @@ -294,7 +294,10 @@ struct fw_cdev_event_iso_resource { * @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_SENT, @length is 0 and @data empty, + * except in case of a ping packet: Then, @length is 4, and @data[0] is the + * ping time in 49.152MHz clocks if @rcode is %RCODE_COMPLETE. + * * 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. */ -- cgit v1.2.3 From 8e2b2b46ea4ca5ef790dddf78b360ed736a62d7c Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Fri, 23 Jul 2010 13:05:39 +0200 Subject: firewire: cdev: improve FW_CDEV_IOC_ALLOCATE In both the ieee1394 stack and the firewire stack, the core treats kernelspace drivers better than userspace drivers when it comes to CSR address range allocation: The former may request a register to be placed automatically at a free spot anywhere inside a specified address range. The latter may only request a register at a fixed offset. Hence, userspace drivers which do not require a fixed offset potentially need to implement a retry loop with incremented offset in each retry until the kernel does not fail allocation with EBUSY. This awkward procedure is not fundamentally necessary as the core already provides a superior allocation API to kernelspace drivers. Therefore change the ioctl() ABI by addition of a region_end member in the existing struct fw_cdev_allocate. Userspace and kernelspace APIs work the same way now. There is a small cost to pay by clients though: If client source code is required to compile with older kernel headers too, then any use of the new member fw_cdev_allocate.region_end needs to be enclosed by #ifdef/#endif directives. However, any client program that seriously wants to use address range allocations will require a kernel of cdev ABI version >= 4 at runtime and a linux/firewire-cdev.h header of >= 4 anyway. This is because v4 brings FW_CDEV_EVENT_REQUEST2. The only client program in which build-time compatibility with struct fw_cdev_allocate as found in older kernel headers makes sense is libraw1394. (libraw1394 uses the older broken FW_CDEV_EVENT_REQUEST to implement a makeshift, incorrect transaction responder that does at least work somewhat in many simple scenarios, relying on guesswork by libraw1394 and by libraw1394 based applications. Plus, address range allocation and transaction responder is only one of many features that libraw1394 needs to provide, and these other features need to work with kernel and kernel-headers as old as possible. Any new linux/firewire-cdev.h based client that implements a transaction responder should never attempt to do it like libraw1394; instead it should make a header and kernel of v4 or later a hard requirement.) While we are at it, update the struct fw_cdev_allocate documentation to better reflect the recent fw_cdev_event_request2 ABI addition. Signed-off-by: Stefan Richter --- drivers/firewire/core-cdev.c | 12 +++++++++--- include/linux/firewire-cdev.h | 29 ++++++++++++++++++++++++----- 2 files changed, 33 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index 31863cf8b6c4..f40098dec14b 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -50,8 +50,9 @@ /* * ABI version history is documented in linux/firewire-cdev.h. */ -#define FW_CDEV_KERNEL_VERSION 4 -#define FW_CDEV_VERSION_EVENT_REQUEST2 4 +#define FW_CDEV_KERNEL_VERSION 4 +#define FW_CDEV_VERSION_EVENT_REQUEST2 4 +#define FW_CDEV_VERSION_ALLOCATE_REGION_END 4 struct client { u32 version; @@ -773,7 +774,11 @@ static int ioctl_allocate(struct client *client, union ioctl_arg *arg) return -ENOMEM; region.start = a->offset; - region.end = a->offset + a->length; + if (client->version < FW_CDEV_VERSION_ALLOCATE_REGION_END) + region.end = a->offset + a->length; + else + region.end = a->region_end; + r->handler.length = a->length; r->handler.address_callback = handle_request; r->handler.callback_data = r; @@ -785,6 +790,7 @@ static int ioctl_allocate(struct client *client, union ioctl_arg *arg) kfree(r); return ret; } + a->offset = r->handler.offset; r->resource.release = release_address_handler; ret = add_client_resource(client, &r->resource, GFP_KERNEL); diff --git a/include/linux/firewire-cdev.h b/include/linux/firewire-cdev.h index da0fec7e8dc0..14831119ff71 100644 --- a/include/linux/firewire-cdev.h +++ b/include/linux/firewire-cdev.h @@ -394,6 +394,7 @@ union fw_cdev_event { * 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, _RECEIVE_PHY_PACKETS + * - added &fw_cdev_allocate.region_end */ #define FW_CDEV_VERSION 3 /* Meaningless; don't use this macro. */ @@ -473,17 +474,21 @@ struct fw_cdev_send_response { }; /** - * struct fw_cdev_allocate - Allocate a CSR address range + * struct fw_cdev_allocate - Allocate a CSR in an address range * @offset: Start offset of the address range * @closure: To be passed back to userspace in request events - * @length: Length of the address range, in bytes + * @length: Length of the CSR, in bytes * @handle: Handle to the allocation, written by the kernel + * @region_end: First address above the address range (added in ABI v4, 2.6.36) * * Allocate an address range in the 48-bit address space on the local node * (the controller). This allows userspace to listen for requests with an - * offset within that address range. When the kernel receives a request - * within the range, an &fw_cdev_event_request event will be written back. - * The @closure field is passed back to userspace in the response event. + * offset within that address range. Every time when the kernel receives a + * request within the range, an &fw_cdev_event_request2 event will be emitted. + * (If the kernel or the client implements ABI version <= 3, an + * &fw_cdev_event_request will be generated instead.) + * + * The @closure field is passed back to userspace in these request events. * The @handle field is an out parameter, returning a handle to the allocated * range to be used for later deallocation of the range. * @@ -491,12 +496,26 @@ struct fw_cdev_send_response { * is exclusive except for the FCP command and response registers. If an * exclusive address region is already in use, the ioctl fails with errno set * to %EBUSY. + * + * If kernel and client implement ABI version >= 4, the kernel looks up a free + * spot of size @length inside [@offset..@region_end) and, if found, writes + * the start address of the new CSR back in @offset. I.e. @offset is an + * in and out parameter. If this automatic placement of a CSR in a bigger + * address range is not desired, the client simply needs to set @region_end + * = @offset + @length. + * + * If the kernel or the client implements ABI version <= 3, @region_end is + * ignored and effectively assumed to be @offset + @length. + * + * @region_end is only present in a kernel header >= 2.6.36. If necessary, + * this can for example be tested by #ifdef FW_CDEV_EVENT_REQUEST2. */ struct fw_cdev_allocate { __u64 offset; __u64 closure; __u32 length; __u32 handle; + __u64 region_end; /* available since kernel version 2.6.36 */ }; /** -- cgit v1.2.3 From 872e330e38806d835bd6c311c93ab998e2fb9058 Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Thu, 29 Jul 2010 18:19:22 +0200 Subject: firewire: add isochronous multichannel reception This adds the DMA context programming and userspace ABI for multichannel reception, i.e. for listening on multiple channel numbers by means of a single DMA context. The use case is reception of more streams than there are IR DMA units offered by the link layer. This is already implemented by the older ohci1394 + ieee1394 + raw1394 stack. And as discussed recently on linux1394-devel, this feature is occasionally used in practice. The big drawbacks of this mode are that buffer layout and interrupt generation necessarily differ from single-channel reception: Headers and trailers are not stripped from packets, packets are not aligned with buffer chunks, interrupts are per buffer chunk, not per packet. These drawbacks also cause a rather hefty code footprint to support this rarely used OHCI-1394 feature. (367 lines added, among them 94 lines of added userspace ABI documentation.) This implementation enforces that a multichannel reception context may only listen to channels to which no single-channel context on the same link layer is presently listening to. OHCI-1394 would allow to overlay single-channel contexts by the multi-channel context, but this would be a departure from the present first-come-first-served policy of IR context creation. The implementation is heavily based on an earlier one by Jay Fenlason. Thanks Jay. Signed-off-by: Stefan Richter --- drivers/firewire/core-cdev.c | 93 ++++++++++--- drivers/firewire/core-iso.c | 32 ++++- drivers/firewire/core.h | 2 + drivers/firewire/ohci.c | 316 +++++++++++++++++++++++++++++++++--------- include/linux/firewire-cdev.h | 281 +++++++++++++++++++++++++------------ include/linux/firewire.h | 29 ++-- 6 files changed, 560 insertions(+), 193 deletions(-) (limited to 'include') diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index cf989e1635e1..ba23646bb108 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -193,6 +193,11 @@ struct iso_interrupt_event { struct fw_cdev_event_iso_interrupt interrupt; }; +struct iso_interrupt_mc_event { + struct event event; + struct fw_cdev_event_iso_interrupt_mc interrupt; +}; + struct iso_resource_event { struct event event; struct fw_cdev_event_iso_resource iso_resource; @@ -415,6 +420,7 @@ union ioctl_arg { 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; + struct fw_cdev_set_iso_channels set_iso_channels; }; static int ioctl_get_info(struct client *client, union ioctl_arg *arg) @@ -932,26 +938,54 @@ static void iso_callback(struct fw_iso_context *context, u32 cycle, sizeof(e->interrupt) + header_length, NULL, 0); } +static void iso_mc_callback(struct fw_iso_context *context, + dma_addr_t completed, void *data) +{ + struct client *client = data; + struct iso_interrupt_mc_event *e; + + e = kmalloc(sizeof(*e), GFP_ATOMIC); + if (e == NULL) { + fw_notify("Out of memory when allocating event\n"); + return; + } + e->interrupt.type = FW_CDEV_EVENT_ISO_INTERRUPT_MULTICHANNEL; + e->interrupt.closure = client->iso_closure; + e->interrupt.completed = fw_iso_buffer_lookup(&client->buffer, + completed); + queue_event(client, &e->event, &e->interrupt, + sizeof(e->interrupt), NULL, 0); +} + static int ioctl_create_iso_context(struct client *client, union ioctl_arg *arg) { struct fw_cdev_create_iso_context *a = &arg->create_iso_context; struct fw_iso_context *context; + fw_iso_callback_t cb; BUILD_BUG_ON(FW_CDEV_ISO_CONTEXT_TRANSMIT != FW_ISO_CONTEXT_TRANSMIT || - FW_CDEV_ISO_CONTEXT_RECEIVE != FW_ISO_CONTEXT_RECEIVE); - - if (a->channel > 63) - return -EINVAL; + FW_CDEV_ISO_CONTEXT_RECEIVE != FW_ISO_CONTEXT_RECEIVE || + FW_CDEV_ISO_CONTEXT_RECEIVE_MULTICHANNEL != + FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL); switch (a->type) { - case FW_ISO_CONTEXT_RECEIVE: - if (a->header_size < 4 || (a->header_size & 3)) + case FW_ISO_CONTEXT_TRANSMIT: + if (a->speed > SCODE_3200 || a->channel > 63) return -EINVAL; + + cb = iso_callback; break; - case FW_ISO_CONTEXT_TRANSMIT: - if (a->speed > SCODE_3200) + case FW_ISO_CONTEXT_RECEIVE: + if (a->header_size < 4 || (a->header_size & 3) || + a->channel > 63) return -EINVAL; + + cb = iso_callback; + break; + + case FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL: + cb = (fw_iso_callback_t)iso_mc_callback; break; default: @@ -959,8 +993,7 @@ static int ioctl_create_iso_context(struct client *client, union ioctl_arg *arg) } context = fw_iso_context_create(client->device->card, a->type, - a->channel, a->speed, a->header_size, - iso_callback, client); + a->channel, a->speed, a->header_size, cb, client); if (IS_ERR(context)) return PTR_ERR(context); @@ -980,6 +1013,17 @@ static int ioctl_create_iso_context(struct client *client, union ioctl_arg *arg) return 0; } +static int ioctl_set_iso_channels(struct client *client, union ioctl_arg *arg) +{ + struct fw_cdev_set_iso_channels *a = &arg->set_iso_channels; + struct fw_iso_context *ctx = client->iso_context; + + if (ctx == NULL || a->handle != 0) + return -EINVAL; + + return fw_iso_context_set_channels(ctx, &a->channels); +} + /* Macros for decoding the iso packet control header. */ #define GET_PAYLOAD_LENGTH(v) ((v) & 0xffff) #define GET_INTERRUPT(v) (((v) >> 16) & 0x01) @@ -993,7 +1037,7 @@ static int ioctl_queue_iso(struct client *client, union ioctl_arg *arg) struct fw_cdev_queue_iso *a = &arg->queue_iso; struct fw_cdev_iso_packet __user *p, *end, *next; struct fw_iso_context *ctx = client->iso_context; - unsigned long payload, buffer_end, transmit_header_bytes; + unsigned long payload, buffer_end, transmit_header_bytes = 0; u32 control; int count; struct { @@ -1013,7 +1057,6 @@ static int ioctl_queue_iso(struct client *client, union ioctl_arg *arg) * use the indirect payload, the iso buffer need not be mapped * and the a->data pointer is ignored. */ - payload = (unsigned long)a->data - client->vm_start; buffer_end = client->buffer.page_count << PAGE_SHIFT; if (a->data == 0 || client->buffer.pages == NULL || @@ -1022,8 +1065,10 @@ static int ioctl_queue_iso(struct client *client, union ioctl_arg *arg) buffer_end = 0; } - p = (struct fw_cdev_iso_packet __user *)u64_to_uptr(a->packets); + if (ctx->type == FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL && payload & 3) + return -EINVAL; + p = (struct fw_cdev_iso_packet __user *)u64_to_uptr(a->packets); if (!access_ok(VERIFY_READ, p, a->size)) return -EFAULT; @@ -1039,19 +1084,24 @@ static int ioctl_queue_iso(struct client *client, union ioctl_arg *arg) u.packet.sy = GET_SY(control); u.packet.header_length = GET_HEADER_LENGTH(control); - if (ctx->type == FW_ISO_CONTEXT_TRANSMIT) { - if (u.packet.header_length % 4 != 0) + switch (ctx->type) { + case FW_ISO_CONTEXT_TRANSMIT: + if (u.packet.header_length & 3) return -EINVAL; transmit_header_bytes = u.packet.header_length; - } else { - /* - * We require that header_length is a multiple of - * the fixed header size, ctx->header_size. - */ + break; + + case FW_ISO_CONTEXT_RECEIVE: if (u.packet.header_length == 0 || u.packet.header_length % ctx->header_size != 0) return -EINVAL; - transmit_header_bytes = 0; + break; + + case FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL: + if (u.packet.payload_length == 0 || + u.packet.payload_length & 3) + return -EINVAL; + break; } next = (struct fw_cdev_iso_packet __user *) @@ -1534,6 +1584,7 @@ static int (* const ioctl_handlers[])(struct client *, union ioctl_arg *) = { [0x14] = ioctl_get_cycle_timer2, [0x15] = ioctl_send_phy_packet, [0x16] = ioctl_receive_phy_packets, + [0x17] = ioctl_set_iso_channels, }; static int dispatch_ioctl(struct client *client, diff --git a/drivers/firewire/core-iso.c b/drivers/firewire/core-iso.c index 4fe932e60fb8..0c8e662a5daf 100644 --- a/drivers/firewire/core-iso.c +++ b/drivers/firewire/core-iso.c @@ -117,6 +117,23 @@ void fw_iso_buffer_destroy(struct fw_iso_buffer *buffer, } EXPORT_SYMBOL(fw_iso_buffer_destroy); +/* Convert DMA address to offset into virtually contiguous buffer. */ +size_t fw_iso_buffer_lookup(struct fw_iso_buffer *buffer, dma_addr_t completed) +{ + int i; + dma_addr_t address; + ssize_t offset; + + for (i = 0; i < buffer->page_count; i++) { + address = page_private(buffer->pages[i]); + offset = (ssize_t)completed - (ssize_t)address; + if (offset > 0 && offset <= PAGE_SIZE) + return (i << PAGE_SHIFT) + offset; + } + + return 0; +} + struct fw_iso_context *fw_iso_context_create(struct fw_card *card, int type, int channel, int speed, size_t header_size, fw_iso_callback_t callback, void *callback_data) @@ -133,7 +150,7 @@ struct fw_iso_context *fw_iso_context_create(struct fw_card *card, ctx->channel = channel; ctx->speed = speed; ctx->header_size = header_size; - ctx->callback = callback; + ctx->callback.sc = callback; ctx->callback_data = callback_data; return ctx; @@ -142,9 +159,7 @@ EXPORT_SYMBOL(fw_iso_context_create); void fw_iso_context_destroy(struct fw_iso_context *ctx) { - struct fw_card *card = ctx->card; - - card->driver->free_iso_context(ctx); + ctx->card->driver->free_iso_context(ctx); } EXPORT_SYMBOL(fw_iso_context_destroy); @@ -155,14 +170,17 @@ int fw_iso_context_start(struct fw_iso_context *ctx, } EXPORT_SYMBOL(fw_iso_context_start); +int fw_iso_context_set_channels(struct fw_iso_context *ctx, u64 *channels) +{ + return ctx->card->driver->set_iso_channels(ctx, channels); +} + int fw_iso_context_queue(struct fw_iso_context *ctx, struct fw_iso_packet *packet, struct fw_iso_buffer *buffer, unsigned long payload) { - struct fw_card *card = ctx->card; - - return card->driver->queue_iso(ctx, packet, buffer, payload); + return ctx->card->driver->queue_iso(ctx, packet, buffer, payload); } EXPORT_SYMBOL(fw_iso_context_queue); diff --git a/drivers/firewire/core.h b/drivers/firewire/core.h index 28621e44b111..e6239f971be6 100644 --- a/drivers/firewire/core.h +++ b/drivers/firewire/core.h @@ -90,6 +90,8 @@ struct fw_card_driver { int (*start_iso)(struct fw_iso_context *ctx, s32 cycle, u32 sync, u32 tags); + int (*set_iso_channels)(struct fw_iso_context *ctx, u64 *channels); + int (*queue_iso)(struct fw_iso_context *ctx, struct fw_iso_packet *packet, struct fw_iso_buffer *buffer, diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index 2e4b425847a7..4bda1c1b74ba 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -190,11 +190,13 @@ struct fw_ohci { struct context at_request_ctx; struct context at_response_ctx; - u32 it_context_mask; + u32 it_context_mask; /* unoccupied IT contexts */ struct iso_context *it_context_list; - u64 ir_context_channels; - u32 ir_context_mask; + u64 ir_context_channels; /* unoccupied channels */ + u32 ir_context_mask; /* unoccupied IR contexts */ struct iso_context *ir_context_list; + u64 mc_channels; /* channels in use by the multichannel IR context */ + bool mc_allocated; __be32 *config_rom; dma_addr_t config_rom_bus; @@ -2197,10 +2199,9 @@ static int handle_ir_packet_per_buffer(struct context *context, __le32 *ir_header; void *p; - for (pd = d; pd <= last; pd++) { + for (pd = d; pd <= last; pd++) if (pd->transfer_status) break; - } if (pd > last) /* Descriptor(s) not done yet, stop iteration */ return 0; @@ -2210,16 +2211,38 @@ static int handle_ir_packet_per_buffer(struct context *context, if (le16_to_cpu(last->control) & DESCRIPTOR_IRQ_ALWAYS) { ir_header = (__le32 *) p; - ctx->base.callback(&ctx->base, - le32_to_cpu(ir_header[0]) & 0xffff, - ctx->header_length, ctx->header, - ctx->base.callback_data); + ctx->base.callback.sc(&ctx->base, + le32_to_cpu(ir_header[0]) & 0xffff, + ctx->header_length, ctx->header, + ctx->base.callback_data); ctx->header_length = 0; } return 1; } +/* d == last because each descriptor block is only a single descriptor. */ +static int handle_ir_buffer_fill(struct context *context, + struct descriptor *d, + struct descriptor *last) +{ + struct iso_context *ctx = + container_of(context, struct iso_context, context); + + if (!last->transfer_status) + /* Descriptor(s) not done yet, stop iteration */ + return 0; + + if (le16_to_cpu(last->control) & DESCRIPTOR_IRQ_ALWAYS) + ctx->base.callback.mc(&ctx->base, + le32_to_cpu(last->data_address) + + le16_to_cpu(last->req_count) - + le16_to_cpu(last->res_count), + ctx->base.callback_data); + + return 1; +} + static int handle_it_packet(struct context *context, struct descriptor *d, struct descriptor *last) @@ -2245,72 +2268,118 @@ static int handle_it_packet(struct context *context, ctx->header_length += 4; } if (le16_to_cpu(last->control) & DESCRIPTOR_IRQ_ALWAYS) { - ctx->base.callback(&ctx->base, le16_to_cpu(last->res_count), - ctx->header_length, ctx->header, - ctx->base.callback_data); + ctx->base.callback.sc(&ctx->base, le16_to_cpu(last->res_count), + ctx->header_length, ctx->header, + ctx->base.callback_data); ctx->header_length = 0; } return 1; } +static void set_multichannel_mask(struct fw_ohci *ohci, u64 channels) +{ + u32 hi = channels >> 32, lo = channels; + + reg_write(ohci, OHCI1394_IRMultiChanMaskHiClear, ~hi); + reg_write(ohci, OHCI1394_IRMultiChanMaskLoClear, ~lo); + reg_write(ohci, OHCI1394_IRMultiChanMaskHiSet, hi); + reg_write(ohci, OHCI1394_IRMultiChanMaskLoSet, lo); + mmiowb(); + ohci->mc_channels = channels; +} + static struct fw_iso_context *ohci_allocate_iso_context(struct fw_card *card, int type, int channel, size_t header_size) { struct fw_ohci *ohci = fw_ohci(card); - struct iso_context *ctx, *list; - descriptor_callback_t callback; - u64 *channels, dont_care = ~0ULL; - u32 *mask, regs; + struct iso_context *uninitialized_var(ctx); + descriptor_callback_t uninitialized_var(callback); + u64 *uninitialized_var(channels); + u32 *uninitialized_var(mask), uninitialized_var(regs); unsigned long flags; - int index, ret = -ENOMEM; + int index, ret = -EBUSY; - if (type == FW_ISO_CONTEXT_TRANSMIT) { - channels = &dont_care; - mask = &ohci->it_context_mask; - list = ohci->it_context_list; + spin_lock_irqsave(&ohci->lock, flags); + + switch (type) { + case FW_ISO_CONTEXT_TRANSMIT: + mask = &ohci->it_context_mask; callback = handle_it_packet; - } else { + index = ffs(*mask) - 1; + if (index >= 0) { + *mask &= ~(1 << index); + regs = OHCI1394_IsoXmitContextBase(index); + ctx = &ohci->it_context_list[index]; + } + break; + + case FW_ISO_CONTEXT_RECEIVE: channels = &ohci->ir_context_channels; - mask = &ohci->ir_context_mask; - list = ohci->ir_context_list; + mask = &ohci->ir_context_mask; callback = handle_ir_packet_per_buffer; - } + index = *channels & 1ULL << channel ? ffs(*mask) - 1 : -1; + if (index >= 0) { + *channels &= ~(1ULL << channel); + *mask &= ~(1 << index); + regs = OHCI1394_IsoRcvContextBase(index); + ctx = &ohci->ir_context_list[index]; + } + break; - spin_lock_irqsave(&ohci->lock, flags); - index = *channels & 1ULL << channel ? ffs(*mask) - 1 : -1; - if (index >= 0) { - *channels &= ~(1ULL << channel); - *mask &= ~(1 << index); + case FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL: + mask = &ohci->ir_context_mask; + callback = handle_ir_buffer_fill; + index = !ohci->mc_allocated ? ffs(*mask) - 1 : -1; + if (index >= 0) { + ohci->mc_allocated = true; + *mask &= ~(1 << index); + regs = OHCI1394_IsoRcvContextBase(index); + ctx = &ohci->ir_context_list[index]; + } + break; + + default: + index = -1; + ret = -ENOSYS; } + spin_unlock_irqrestore(&ohci->lock, flags); if (index < 0) - return ERR_PTR(-EBUSY); - - if (type == FW_ISO_CONTEXT_TRANSMIT) - regs = OHCI1394_IsoXmitContextBase(index); - else - regs = OHCI1394_IsoRcvContextBase(index); + return ERR_PTR(ret); - ctx = &list[index]; memset(ctx, 0, sizeof(*ctx)); ctx->header_length = 0; ctx->header = (void *) __get_free_page(GFP_KERNEL); - if (ctx->header == NULL) + if (ctx->header == NULL) { + ret = -ENOMEM; goto out; - + } ret = context_init(&ctx->context, ohci, regs, callback); if (ret < 0) goto out_with_header; + if (type == FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL) + set_multichannel_mask(ohci, 0); + return &ctx->base; out_with_header: free_page((unsigned long)ctx->header); out: spin_lock_irqsave(&ohci->lock, flags); - *channels |= 1ULL << channel; + + switch (type) { + case FW_ISO_CONTEXT_RECEIVE: + *channels |= 1ULL << channel; + break; + + case FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL: + ohci->mc_allocated = false; + break; + } *mask |= 1 << index; + spin_unlock_irqrestore(&ohci->lock, flags); return ERR_PTR(ret); @@ -2321,10 +2390,11 @@ static int ohci_start_iso(struct fw_iso_context *base, { struct iso_context *ctx = container_of(base, struct iso_context, base); struct fw_ohci *ohci = ctx->context.ohci; - u32 control, match; + u32 control = IR_CONTEXT_ISOCH_HEADER, match; int index; - if (ctx->base.type == FW_ISO_CONTEXT_TRANSMIT) { + switch (ctx->base.type) { + case FW_ISO_CONTEXT_TRANSMIT: index = ctx - ohci->it_context_list; match = 0; if (cycle >= 0) @@ -2334,9 +2404,13 @@ static int ohci_start_iso(struct fw_iso_context *base, reg_write(ohci, OHCI1394_IsoXmitIntEventClear, 1 << index); reg_write(ohci, OHCI1394_IsoXmitIntMaskSet, 1 << index); context_run(&ctx->context, match); - } else { + break; + + case FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL: + control |= IR_CONTEXT_BUFFER_FILL|IR_CONTEXT_MULTI_CHANNEL_MODE; + /* fall through */ + case FW_ISO_CONTEXT_RECEIVE: index = ctx - ohci->ir_context_list; - control = IR_CONTEXT_ISOCH_HEADER; match = (tags << 28) | (sync << 8) | ctx->base.channel; if (cycle >= 0) { match |= (cycle & 0x07fff) << 12; @@ -2347,6 +2421,7 @@ static int ohci_start_iso(struct fw_iso_context *base, reg_write(ohci, OHCI1394_IsoRecvIntMaskSet, 1 << index); reg_write(ohci, CONTEXT_MATCH(ctx->context.regs), match); context_run(&ctx->context, control); + break; } return 0; @@ -2358,12 +2433,17 @@ static int ohci_stop_iso(struct fw_iso_context *base) struct iso_context *ctx = container_of(base, struct iso_context, base); int index; - if (ctx->base.type == FW_ISO_CONTEXT_TRANSMIT) { + switch (ctx->base.type) { + case FW_ISO_CONTEXT_TRANSMIT: index = ctx - ohci->it_context_list; reg_write(ohci, OHCI1394_IsoXmitIntMaskClear, 1 << index); - } else { + break; + + case FW_ISO_CONTEXT_RECEIVE: + case FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL: index = ctx - ohci->ir_context_list; reg_write(ohci, OHCI1394_IsoRecvIntMaskClear, 1 << index); + break; } flush_writes(ohci); context_stop(&ctx->context); @@ -2384,24 +2464,65 @@ static void ohci_free_iso_context(struct fw_iso_context *base) spin_lock_irqsave(&ohci->lock, flags); - if (ctx->base.type == FW_ISO_CONTEXT_TRANSMIT) { + switch (base->type) { + case FW_ISO_CONTEXT_TRANSMIT: index = ctx - ohci->it_context_list; ohci->it_context_mask |= 1 << index; - } else { + break; + + case FW_ISO_CONTEXT_RECEIVE: index = ctx - ohci->ir_context_list; ohci->ir_context_mask |= 1 << index; ohci->ir_context_channels |= 1ULL << base->channel; + break; + + case FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL: + index = ctx - ohci->ir_context_list; + ohci->ir_context_mask |= 1 << index; + ohci->ir_context_channels |= ohci->mc_channels; + ohci->mc_channels = 0; + ohci->mc_allocated = false; + break; } spin_unlock_irqrestore(&ohci->lock, flags); } -static int ohci_queue_iso_transmit(struct fw_iso_context *base, - struct fw_iso_packet *packet, - struct fw_iso_buffer *buffer, - unsigned long payload) +static int ohci_set_iso_channels(struct fw_iso_context *base, u64 *channels) +{ + struct fw_ohci *ohci = fw_ohci(base->card); + unsigned long flags; + int ret; + + switch (base->type) { + case FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL: + + spin_lock_irqsave(&ohci->lock, flags); + + /* Don't allow multichannel to grab other contexts' channels. */ + if (~ohci->ir_context_channels & ~ohci->mc_channels & *channels) { + *channels = ohci->ir_context_channels; + ret = -EBUSY; + } else { + set_multichannel_mask(ohci, *channels); + ret = 0; + } + + spin_unlock_irqrestore(&ohci->lock, flags); + + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static int queue_iso_transmit(struct iso_context *ctx, + struct fw_iso_packet *packet, + struct fw_iso_buffer *buffer, + unsigned long payload) { - struct iso_context *ctx = container_of(base, struct iso_context, base); struct descriptor *d, *last, *pd; struct fw_iso_packet *p; __le32 *header; @@ -2497,14 +2618,12 @@ static int ohci_queue_iso_transmit(struct fw_iso_context *base, return 0; } -static int ohci_queue_iso_receive_packet_per_buffer(struct fw_iso_context *base, - struct fw_iso_packet *packet, - struct fw_iso_buffer *buffer, - unsigned long payload) +static int queue_iso_packet_per_buffer(struct iso_context *ctx, + struct fw_iso_packet *packet, + struct fw_iso_buffer *buffer, + unsigned long payload) { - struct iso_context *ctx = container_of(base, struct iso_context, base); struct descriptor *d, *pd; - struct fw_iso_packet *p = packet; dma_addr_t d_bus, page_bus; u32 z, header_z, rest; int i, j, length; @@ -2514,14 +2633,14 @@ static int ohci_queue_iso_receive_packet_per_buffer(struct fw_iso_context *base, * The OHCI controller puts the isochronous header and trailer in the * buffer, so we need at least 8 bytes. */ - packet_count = p->header_length / ctx->base.header_size; + packet_count = packet->header_length / ctx->base.header_size; header_size = max(ctx->base.header_size, (size_t)8); /* Get header size in number of descriptors. */ header_z = DIV_ROUND_UP(header_size, sizeof(*d)); page = payload >> PAGE_SHIFT; offset = payload & ~PAGE_MASK; - payload_per_buffer = p->payload_length / packet_count; + payload_per_buffer = packet->payload_length / packet_count; for (i = 0; i < packet_count; i++) { /* d points to the header descriptor */ @@ -2533,7 +2652,7 @@ static int ohci_queue_iso_receive_packet_per_buffer(struct fw_iso_context *base, d->control = cpu_to_le16(DESCRIPTOR_STATUS | DESCRIPTOR_INPUT_MORE); - if (p->skip && i == 0) + if (packet->skip && i == 0) d->control |= cpu_to_le16(DESCRIPTOR_WAIT); d->req_count = cpu_to_le16(header_size); d->res_count = d->req_count; @@ -2566,7 +2685,7 @@ static int ohci_queue_iso_receive_packet_per_buffer(struct fw_iso_context *base, pd->control = cpu_to_le16(DESCRIPTOR_STATUS | DESCRIPTOR_INPUT_LAST | DESCRIPTOR_BRANCH_ALWAYS); - if (p->interrupt && i == packet_count - 1) + if (packet->interrupt && i == packet_count - 1) pd->control |= cpu_to_le16(DESCRIPTOR_IRQ_ALWAYS); context_append(&ctx->context, d, z, header_z); @@ -2575,6 +2694,58 @@ static int ohci_queue_iso_receive_packet_per_buffer(struct fw_iso_context *base, return 0; } +static int queue_iso_buffer_fill(struct iso_context *ctx, + struct fw_iso_packet *packet, + struct fw_iso_buffer *buffer, + unsigned long payload) +{ + struct descriptor *d; + dma_addr_t d_bus, page_bus; + int page, offset, rest, z, i, length; + + page = payload >> PAGE_SHIFT; + offset = payload & ~PAGE_MASK; + rest = packet->payload_length; + + /* We need one descriptor for each page in the buffer. */ + z = DIV_ROUND_UP(offset + rest, PAGE_SIZE); + + if (WARN_ON(offset & 3 || rest & 3 || page + z > buffer->page_count)) + return -EFAULT; + + for (i = 0; i < z; i++) { + d = context_get_descriptors(&ctx->context, 1, &d_bus); + if (d == NULL) + return -ENOMEM; + + d->control = cpu_to_le16(DESCRIPTOR_INPUT_MORE | + DESCRIPTOR_BRANCH_ALWAYS); + if (packet->skip && i == 0) + d->control |= cpu_to_le16(DESCRIPTOR_WAIT); + if (packet->interrupt && i == z - 1) + d->control |= cpu_to_le16(DESCRIPTOR_IRQ_ALWAYS); + + if (offset + rest < PAGE_SIZE) + length = rest; + else + length = PAGE_SIZE - offset; + d->req_count = cpu_to_le16(length); + d->res_count = d->req_count; + d->transfer_status = 0; + + page_bus = page_private(buffer->pages[page]); + d->data_address = cpu_to_le32(page_bus + offset); + + rest -= length; + offset = 0; + page++; + + context_append(&ctx->context, d, 1, 0); + } + + return 0; +} + static int ohci_queue_iso(struct fw_iso_context *base, struct fw_iso_packet *packet, struct fw_iso_buffer *buffer, @@ -2582,14 +2753,20 @@ static int ohci_queue_iso(struct fw_iso_context *base, { struct iso_context *ctx = container_of(base, struct iso_context, base); unsigned long flags; - int ret; + int ret = -ENOSYS; spin_lock_irqsave(&ctx->context.ohci->lock, flags); - if (base->type == FW_ISO_CONTEXT_TRANSMIT) - ret = ohci_queue_iso_transmit(base, packet, buffer, payload); - else - ret = ohci_queue_iso_receive_packet_per_buffer(base, packet, - buffer, payload); + switch (base->type) { + case FW_ISO_CONTEXT_TRANSMIT: + ret = queue_iso_transmit(ctx, packet, buffer, payload); + break; + case FW_ISO_CONTEXT_RECEIVE: + ret = queue_iso_packet_per_buffer(ctx, packet, buffer, payload); + break; + case FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL: + ret = queue_iso_buffer_fill(ctx, packet, buffer, payload); + break; + } spin_unlock_irqrestore(&ctx->context.ohci->lock, flags); return ret; @@ -2609,6 +2786,7 @@ static const struct fw_card_driver ohci_driver = { .allocate_iso_context = ohci_allocate_iso_context, .free_iso_context = ohci_free_iso_context, + .set_iso_channels = ohci_set_iso_channels, .queue_iso = ohci_queue_iso, .start_iso = ohci_start_iso, .stop_iso = ohci_stop_iso, diff --git a/include/linux/firewire-cdev.h b/include/linux/firewire-cdev.h index 14831119ff71..bc5c26fc1c64 100644 --- a/include/linux/firewire-cdev.h +++ b/include/linux/firewire-cdev.h @@ -25,17 +25,18 @@ #include #include -#define FW_CDEV_EVENT_BUS_RESET 0x00 -#define FW_CDEV_EVENT_RESPONSE 0x01 -#define FW_CDEV_EVENT_REQUEST 0x02 -#define FW_CDEV_EVENT_ISO_INTERRUPT 0x03 -#define FW_CDEV_EVENT_ISO_RESOURCE_ALLOCATED 0x04 -#define FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED 0x05 +#define FW_CDEV_EVENT_BUS_RESET 0x00 +#define FW_CDEV_EVENT_RESPONSE 0x01 +#define FW_CDEV_EVENT_REQUEST 0x02 +#define FW_CDEV_EVENT_ISO_INTERRUPT 0x03 +#define FW_CDEV_EVENT_ISO_RESOURCE_ALLOCATED 0x04 +#define FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED 0x05 /* 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 +#define FW_CDEV_EVENT_REQUEST2 0x06 +#define FW_CDEV_EVENT_PHY_PACKET_SENT 0x07 +#define FW_CDEV_EVENT_PHY_PACKET_RECEIVED 0x08 +#define FW_CDEV_EVENT_ISO_INTERRUPT_MULTICHANNEL 0x09 /** * struct fw_cdev_event_common - Common part of all fw_cdev_event_ types @@ -218,35 +219,41 @@ struct fw_cdev_event_request2 { * This event is sent when the controller has completed an &fw_cdev_iso_packet * with the %FW_CDEV_ISO_INTERRUPT bit set. * - * Isochronous transmit events: + * Isochronous transmit events (context type %FW_CDEV_ISO_CONTEXT_TRANSMIT): * - * In version 1 of the ABI, &header_length is 0. In version 3 and some - * implementations of version 2 of the ABI, &header_length is a multiple of 4 - * and &header contains timestamps of all packets up until the interrupt packet. - * The format of the timestamps is as described below for isochronous reception. + * In version 3 and some implementations of version 2 of the ABI, &header_length + * is a multiple of 4 and &header contains timestamps of all packets up until + * the interrupt packet. The format of the timestamps is as described below for + * isochronous reception. In version 1 of the ABI, &header_length was 0. * - * Isochronous receive events: + * Isochronous receive events (context type %FW_CDEV_ISO_CONTEXT_RECEIVE): * * The headers stripped of all packets up until and including the interrupt * packet are returned in the @header field. The amount of header data per * packet is as specified at iso context creation by * &fw_cdev_create_iso_context.header_size. * - * In version 1 of this ABI, header data consisted of the 1394 isochronous - * packet header, followed by quadlets from the packet payload if - * &fw_cdev_create_iso_context.header_size > 4. + * Hence, _interrupt.header_length / _context.header_size is the number of + * packets received in this interrupt event. The client can now iterate + * through the mmap()'ed DMA buffer according to this number of packets and + * to the buffer sizes as the client specified in &fw_cdev_queue_iso. * - * In version 2 of this ABI, header data consist of the 1394 isochronous - * packet header, followed by a timestamp quadlet if - * &fw_cdev_create_iso_context.header_size > 4, followed by quadlets from the - * packet payload if &fw_cdev_create_iso_context.header_size > 8. + * Since version 2 of this ABI, the portion for each packet in _interrupt.header + * consists of the 1394 isochronous packet header, followed by a timestamp + * quadlet if &fw_cdev_create_iso_context.header_size > 4, followed by quadlets + * from the packet payload if &fw_cdev_create_iso_context.header_size > 8. * - * Behaviour of ver. 1 of this ABI is no longer available since ABI ver. 2. + * Format of 1394 iso packet header: 16 bits data_length, 2 bits tag, 6 bits + * channel, 4 bits tcode, 4 bits sy, in big endian byte order. + * data_length is the actual received size of the packet without the four + * 1394 iso packet header bytes. + * + * Format of timestamp: 16 bits invalid, 3 bits cycleSeconds, 13 bits + * cycleCount, in big endian byte order. * - * Format of 1394 iso packet header: 16 bits len, 2 bits tag, 6 bits channel, - * 4 bits tcode, 4 bits sy, in big endian byte order. Format of timestamp: - * 16 bits invalid, 3 bits cycleSeconds, 13 bits cycleCount, in big endian byte - * order. + * In version 1 of the ABI, no timestamp quadlet was inserted; instead, payload + * data followed directly after the 1394 is header if header_size > 4. + * Behaviour of ver. 1 of this ABI is no longer available since ABI ver. 2. */ struct fw_cdev_event_iso_interrupt { __u64 closure; @@ -256,6 +263,43 @@ struct fw_cdev_event_iso_interrupt { __u32 header[0]; }; +/** + * struct fw_cdev_event_iso_interrupt_mc - An iso buffer chunk was completed + * @closure: See &fw_cdev_event_common; + * set by %FW_CDEV_CREATE_ISO_CONTEXT ioctl + * @type: %FW_CDEV_EVENT_ISO_INTERRUPT_MULTICHANNEL + * @completed: Offset into the receive buffer; data before this offest is valid + * + * This event is sent in multichannel contexts (context type + * %FW_CDEV_ISO_CONTEXT_RECEIVE_MULTICHANNEL) for &fw_cdev_iso_packet buffer + * chunks that have the %FW_CDEV_ISO_INTERRUPT bit set. Whether this happens + * when a packet is completed and/or when a buffer chunk is completed depends + * on the hardware implementation. + * + * The buffer is continuously filled with the following data, per packet: + * - the 1394 iso packet header as described at &fw_cdev_event_iso_interrupt, + * but in little endian byte order, + * - packet payload (as many bytes as specified in the data_length field of + * the 1394 iso packet header) in big endian byte order, + * - 0...3 padding bytes as needed to align the following trailer quadlet, + * - trailer quadlet, containing the reception timestamp as described at + * &fw_cdev_event_iso_interrupt, but in little endian byte order. + * + * Hence the per-packet size is data_length (rounded up to a multiple of 4) + 8. + * When processing the data, stop before a packet that would cross the + * @completed offset. + * + * A packet near the end of a buffer chunk will typically spill over into the + * next queued buffer chunk. It is the responsibility of the client to check + * for this condition, assemble a broken-up packet from its parts, and not to + * re-queue any buffer chunks in which as yet unread packet parts reside. + */ +struct fw_cdev_event_iso_interrupt_mc { + __u64 closure; + __u32 type; + __u32 completed; +}; + /** * struct fw_cdev_event_iso_resource - Iso resources were allocated or freed * @closure: See &fw_cdev_event_common; @@ -311,16 +355,18 @@ struct fw_cdev_event_phy_packet { /** * union fw_cdev_event - Convenience union of fw_cdev_event_ types - * @common: Valid for all types - * @bus_reset: Valid if @common.type == %FW_CDEV_EVENT_BUS_RESET - * @response: Valid if @common.type == %FW_CDEV_EVENT_RESPONSE - * @request: Valid if @common.type == %FW_CDEV_EVENT_REQUEST - * @request2: Valid if @common.type == %FW_CDEV_EVENT_REQUEST2 - * @iso_interrupt: Valid if @common.type == %FW_CDEV_EVENT_ISO_INTERRUPT - * @iso_resource: Valid if @common.type == + * @common: Valid for all types + * @bus_reset: Valid if @common.type == %FW_CDEV_EVENT_BUS_RESET + * @response: Valid if @common.type == %FW_CDEV_EVENT_RESPONSE + * @request: Valid if @common.type == %FW_CDEV_EVENT_REQUEST + * @request2: Valid if @common.type == %FW_CDEV_EVENT_REQUEST2 + * @iso_interrupt: Valid if @common.type == %FW_CDEV_EVENT_ISO_INTERRUPT + * @iso_interrupt_mc: Valid if @common.type == + * %FW_CDEV_EVENT_ISO_INTERRUPT_MULTICHANNEL + * @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 == + * @phy_packet: Valid if @common.type == * %FW_CDEV_EVENT_PHY_PACKET_SENT or * %FW_CDEV_EVENT_PHY_PACKET_RECEIVED * @@ -337,10 +383,11 @@ union fw_cdev_event { struct fw_cdev_event_bus_reset bus_reset; struct fw_cdev_event_response response; struct fw_cdev_event_request request; - struct fw_cdev_event_request2 request2; /* added in 2.6.36 */ + struct fw_cdev_event_request2 request2; /* added in 2.6.36 */ struct fw_cdev_event_iso_interrupt iso_interrupt; - struct fw_cdev_event_iso_resource iso_resource; /* added in 2.6.30 */ - struct fw_cdev_event_phy_packet phy_packet; /* added in 2.6.36 */ + struct fw_cdev_event_iso_interrupt_mc iso_interrupt_mc; /* added in 2.6.36 */ + struct fw_cdev_event_iso_resource iso_resource; /* added in 2.6.30 */ + struct fw_cdev_event_phy_packet phy_packet; /* added in 2.6.36 */ }; /* available since kernel version 2.6.22 */ @@ -375,6 +422,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) +#define FW_CDEV_IOC_SET_ISO_CHANNELS _IOW('#', 0x17, struct fw_cdev_set_iso_channels) /* * ABI version history @@ -391,10 +439,13 @@ 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_* + * 4 (2.6.36) - added %FW_CDEV_EVENT_REQUEST2, %FW_CDEV_EVENT_PHY_PACKET_*, + * and &fw_cdev_allocate.region_end * - implemented &fw_cdev_event_bus_reset.bm_node_id * - added %FW_CDEV_IOC_SEND_PHY_PACKET, _RECEIVE_PHY_PACKETS - * - added &fw_cdev_allocate.region_end + * - added %FW_CDEV_EVENT_ISO_INTERRUPT_MULTICHANNEL, + * %FW_CDEV_ISO_CONTEXT_RECEIVE_MULTICHANNEL, and + * %FW_CDEV_IOC_SET_ISO_CHANNELS */ #define FW_CDEV_VERSION 3 /* Meaningless; don't use this macro. */ @@ -597,34 +648,43 @@ struct fw_cdev_remove_descriptor { __u32 handle; }; -#define FW_CDEV_ISO_CONTEXT_TRANSMIT 0 -#define FW_CDEV_ISO_CONTEXT_RECEIVE 1 +#define FW_CDEV_ISO_CONTEXT_TRANSMIT 0 +#define FW_CDEV_ISO_CONTEXT_RECEIVE 1 +#define FW_CDEV_ISO_CONTEXT_RECEIVE_MULTICHANNEL 2 /* added in 2.6.36 */ /** - * struct fw_cdev_create_iso_context - Create a context for isochronous IO - * @type: %FW_CDEV_ISO_CONTEXT_TRANSMIT or %FW_CDEV_ISO_CONTEXT_RECEIVE - * @header_size: Header size to strip for receive contexts - * @channel: Channel to bind to - * @speed: Speed for transmit contexts - * @closure: To be returned in &fw_cdev_event_iso_interrupt + * struct fw_cdev_create_iso_context - Create a context for isochronous I/O + * @type: %FW_CDEV_ISO_CONTEXT_TRANSMIT or %FW_CDEV_ISO_CONTEXT_RECEIVE or + * %FW_CDEV_ISO_CONTEXT_RECEIVE_MULTICHANNEL + * @header_size: Header size to strip in single-channel reception + * @channel: Channel to bind to in single-channel reception or transmission + * @speed: Transmission speed + * @closure: To be returned in &fw_cdev_event_iso_interrupt or + * &fw_cdev_event_iso_interrupt_multichannel * @handle: Handle to context, written back by kernel * * Prior to sending or receiving isochronous I/O, a context must be created. * The context records information about the transmit or receive configuration * and typically maps to an underlying hardware resource. A context is set up * for either sending or receiving. It is bound to a specific isochronous - * channel. + * @channel. * - * If a context was successfully created, the kernel writes back a handle to the - * context, which must be passed in for subsequent operations on that context. + * In case of multichannel reception, @header_size and @channel are ignored + * and the channels are selected by %FW_CDEV_IOC_SET_ISO_CHANNELS. + * + * For %FW_CDEV_ISO_CONTEXT_RECEIVE contexts, @header_size must be at least 4 + * and must be a multiple of 4. It is ignored in other context types. * - * For receive contexts, @header_size must be at least 4 and must be a multiple - * of 4. + * @speed is ignored in receive context types. * - * Note that the effect of a @header_size > 4 depends on - * &fw_cdev_get_info.version, as documented at &fw_cdev_event_iso_interrupt. + * If a context was successfully created, the kernel writes back a handle to the + * context, which must be passed in for subsequent operations on that context. * + * Limitations: * No more than one iso context can be created per fd. + * The total number of contexts that all userspace and kernelspace drivers can + * create on a card at a time is a hardware limit, typically 4 or 8 contexts per + * direction, and of them at most one multichannel receive context. */ struct fw_cdev_create_iso_context { __u32 type; @@ -635,6 +695,22 @@ struct fw_cdev_create_iso_context { __u32 handle; }; +/** + * struct fw_cdev_set_iso_channels - Select channels in multichannel reception + * @channels: Bitmask of channels to listen to + * @handle: Handle of the mutichannel receive context + * + * @channels is the bitwise or of 1ULL << n for each channel n to listen to. + * + * The ioctl fails with errno %EBUSY if there is already another receive context + * on a channel in @channels. In that case, the bitmask of all unoccupied + * channels is returned in @channels. + */ +struct fw_cdev_set_iso_channels { + __u64 channels; + __u32 handle; +}; + #define FW_CDEV_ISO_PAYLOAD_LENGTH(v) (v) #define FW_CDEV_ISO_INTERRUPT (1 << 16) #define FW_CDEV_ISO_SKIP (1 << 17) @@ -645,42 +721,72 @@ struct fw_cdev_create_iso_context { /** * struct fw_cdev_iso_packet - Isochronous packet - * @control: Contains the header length (8 uppermost bits), the sy field - * (4 bits), the tag field (2 bits), a sync flag (1 bit), - * a skip flag (1 bit), an interrupt flag (1 bit), and the + * @control: Contains the header length (8 uppermost bits), + * the sy field (4 bits), the tag field (2 bits), a sync flag + * or a skip flag (1 bit), an interrupt flag (1 bit), and the * payload length (16 lowermost bits) - * @header: Header and payload + * @header: Header and payload in case of a transmit context. * * &struct fw_cdev_iso_packet is used to describe isochronous packet queues. - * * Use the FW_CDEV_ISO_ macros to fill in @control. + * The @header array is empty in case of receive contexts. + * + * Context type %FW_CDEV_ISO_CONTEXT_TRANSMIT: + * + * @control.HEADER_LENGTH must be a multiple of 4. It specifies the numbers of + * bytes in @header that will be prepended to the packet's payload. These bytes + * are copied into the kernel and will not be accessed after the ioctl has + * returned. + * + * The @control.SY and TAG fields are copied to the iso packet header. These + * fields are specified by IEEE 1394a and IEC 61883-1. + * + * The @control.SKIP flag specifies that no packet is to be sent in a frame. + * When using this, all other fields except @control.INTERRUPT must be zero. + * + * When a packet with the @control.INTERRUPT flag set has been completed, an + * &fw_cdev_event_iso_interrupt event will be sent. + * + * Context type %FW_CDEV_ISO_CONTEXT_RECEIVE: * - * For transmit packets, the header length must be a multiple of 4 and specifies - * the numbers of bytes in @header that will be prepended to the packet's - * payload; these bytes are copied into the kernel and will not be accessed - * after the ioctl has returned. The sy and tag fields are copied to the iso - * packet header (these fields are specified by IEEE 1394a and IEC 61883-1). - * The skip flag specifies that no packet is to be sent in a frame; when using - * this, all other fields except the interrupt flag must be zero. - * - * For receive packets, the header length must be a multiple of the context's - * header size; if the header length is larger than the context's header size, - * multiple packets are queued for this entry. The sy and tag fields are - * ignored. If the sync flag is set, the context drops all packets until - * a packet with a matching sy field is received (the sync value to wait for is - * specified in the &fw_cdev_start_iso structure). The payload length defines - * how many payload bytes can be received for one packet (in addition to payload - * quadlets that have been defined as headers and are stripped and returned in - * the &fw_cdev_event_iso_interrupt structure). If more bytes are received, the - * additional bytes are dropped. If less bytes are received, the remaining - * bytes in this part of the payload buffer will not be written to, not even by - * the next packet, i.e., packets received in consecutive frames will not - * necessarily be consecutive in memory. If an entry has queued multiple - * packets, the payload length is divided equally among them. - * - * When a packet with the interrupt flag set has been completed, the + * @control.HEADER_LENGTH must be a multiple of the context's header_size. + * If the HEADER_LENGTH is larger than the context's header_size, multiple + * packets are queued for this entry. + * + * The @control.SY and TAG fields are ignored. + * + * If the @control.SYNC flag is set, the context drops all packets until a + * packet with a sy field is received which matches &fw_cdev_start_iso.sync. + * + * @control.PAYLOAD_LENGTH defines how many payload bytes can be received for + * one packet (in addition to payload quadlets that have been defined as headers + * and are stripped and returned in the &fw_cdev_event_iso_interrupt structure). + * If more bytes are received, the additional bytes are dropped. If less bytes + * are received, the remaining bytes in this part of the payload buffer will not + * be written to, not even by the next packet. I.e., packets received in + * consecutive frames will not necessarily be consecutive in memory. If an + * entry has queued multiple packets, the PAYLOAD_LENGTH is divided equally + * among them. + * + * When a packet with the @control.INTERRUPT flag set has been completed, an * &fw_cdev_event_iso_interrupt event will be sent. An entry that has queued * multiple receive packets is completed when its last packet is completed. + * + * Context type %FW_CDEV_ISO_CONTEXT_RECEIVE_MULTICHANNEL: + * + * Here, &fw_cdev_iso_packet would be more aptly named _iso_buffer_chunk since + * it specifies a chunk of the mmap()'ed buffer, while the number and alignment + * of packets to be placed into the buffer chunk is not known beforehand. + * + * @control.PAYLOAD_LENGTH is the size of the buffer chunk and specifies room + * for header, payload, padding, and trailer bytes of one or more packets. + * It must be a multiple of 4. + * + * @control.HEADER_LENGTH, TAG and SY are ignored. SYNC is treated as described + * for single-channel reception. + * + * When a buffer chunk with the @control.INTERRUPT flag set has been filled + * entirely, an &fw_cdev_event_iso_interrupt_mc event will be sent. */ struct fw_cdev_iso_packet { __u32 control; @@ -689,9 +795,9 @@ struct fw_cdev_iso_packet { /** * struct fw_cdev_queue_iso - Queue isochronous packets for I/O - * @packets: Userspace pointer to packet data + * @packets: Userspace pointer to an array of &fw_cdev_iso_packet * @data: Pointer into mmap()'ed payload buffer - * @size: Size of packet data in bytes + * @size: Size of the @packets array, in bytes * @handle: Isochronous context handle * * Queue a number of isochronous packets for reception or transmission. @@ -704,6 +810,9 @@ struct fw_cdev_iso_packet { * The kernel may or may not queue all packets, but will write back updated * values of the @packets, @data and @size fields, so the ioctl can be * resubmitted easily. + * + * In case of a multichannel receive context, @data must be quadlet-aligned + * relative to the buffer start. */ struct fw_cdev_queue_iso { __u64 packets; diff --git a/include/linux/firewire.h b/include/linux/firewire.h index d974aa4a24c9..1cd637ef62d2 100644 --- a/include/linux/firewire.h +++ b/include/linux/firewire.h @@ -372,17 +372,19 @@ void fw_core_remove_descriptor(struct fw_descriptor *desc); * scatter-gather streaming (e.g. assembling video frame automatically). */ struct fw_iso_packet { - u16 payload_length; /* Length of indirect payload. */ - u32 interrupt:1; /* Generate interrupt on this packet */ - u32 skip:1; /* Set to not send packet at all. */ - u32 tag:2; - u32 sy:4; - u32 header_length:8; /* Length of immediate header. */ - u32 header[0]; + u16 payload_length; /* Length of indirect payload */ + u32 interrupt:1; /* Generate interrupt on this packet */ + u32 skip:1; /* tx: Set to not send packet at all */ + /* rx: Sync bit, wait for matching sy */ + u32 tag:2; /* tx: Tag in packet header */ + u32 sy:4; /* tx: Sy in packet header */ + u32 header_length:8; /* Length of immediate header */ + u32 header[0]; /* tx: Top of 1394 isoch. data_block */ }; -#define FW_ISO_CONTEXT_TRANSMIT 0 -#define FW_ISO_CONTEXT_RECEIVE 1 +#define FW_ISO_CONTEXT_TRANSMIT 0 +#define FW_ISO_CONTEXT_RECEIVE 1 +#define FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL 2 #define FW_ISO_CONTEXT_MATCH_TAG0 1 #define FW_ISO_CONTEXT_MATCH_TAG1 2 @@ -406,24 +408,31 @@ struct fw_iso_buffer { int fw_iso_buffer_init(struct fw_iso_buffer *buffer, struct fw_card *card, int page_count, enum dma_data_direction direction); void fw_iso_buffer_destroy(struct fw_iso_buffer *buffer, struct fw_card *card); +size_t fw_iso_buffer_lookup(struct fw_iso_buffer *buffer, dma_addr_t completed); struct fw_iso_context; typedef void (*fw_iso_callback_t)(struct fw_iso_context *context, u32 cycle, size_t header_length, void *header, void *data); +typedef void (*fw_iso_mc_callback_t)(struct fw_iso_context *context, + dma_addr_t completed, void *data); struct fw_iso_context { struct fw_card *card; int type; int channel; int speed; size_t header_size; - fw_iso_callback_t callback; + union { + fw_iso_callback_t sc; + fw_iso_mc_callback_t mc; + } callback; void *callback_data; }; struct fw_iso_context *fw_iso_context_create(struct fw_card *card, int type, int channel, int speed, size_t header_size, fw_iso_callback_t callback, void *callback_data); +int fw_iso_context_set_channels(struct fw_iso_context *ctx, u64 *channels); int fw_iso_context_queue(struct fw_iso_context *ctx, struct fw_iso_packet *packet, struct fw_iso_buffer *buffer, -- cgit v1.2.3