From 148c7866c31d93f8c79366189075f5a26ad4556c Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Sat, 5 Jun 2010 11:46:49 +0200 Subject: firewire: ohci: do not enable interrupts without the handler On 26 Apr 2010, Clemens Ladisch wrote: > In theory, none of the interrupts should occur before the link is > enabled. In practice, I'd rather make sure to not set the master > interrupt enable bit until we have installed the interrupt handler. and proposed to move OHCI1394_masterIntEnable out of the present reg_write() into a new one before the HCControl.linkEnable reg_write(). Why not defer setting /all/ of the bits until right before linkEnable? Reviewed-by: Clemens Ladisch Signed-off-by: Stefan Richter --- drivers/firewire/ohci.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) (limited to 'drivers/firewire') diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index 07deac77bc13..9743a405e69c 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -1594,7 +1594,7 @@ static int ohci_enable(struct fw_card *card, { struct fw_ohci *ohci = fw_ohci(card); struct pci_dev *dev = to_pci_dev(card->device); - u32 lps; + u32 lps, irqs; int i, ret; if (software_reset(ohci)) { @@ -1648,16 +1648,6 @@ static int ohci_enable(struct fw_card *card, reg_write(ohci, OHCI1394_PhyUpperBound, 0x00010000); reg_write(ohci, OHCI1394_IntEventClear, ~0); reg_write(ohci, OHCI1394_IntMaskClear, ~0); - reg_write(ohci, OHCI1394_IntMaskSet, - OHCI1394_selfIDComplete | - OHCI1394_RQPkt | OHCI1394_RSPkt | - OHCI1394_reqTxComplete | OHCI1394_respTxComplete | - OHCI1394_isochRx | OHCI1394_isochTx | - OHCI1394_postedWriteErr | OHCI1394_cycleTooLong | - OHCI1394_cycleInconsistent | OHCI1394_regAccessFail | - OHCI1394_masterIntEnable); - if (param_debug & OHCI_PARAM_DEBUG_BUSRESETS) - reg_write(ohci, OHCI1394_IntMaskSet, OHCI1394_busReset); ret = configure_1394a_enhancements(ohci); if (ret < 0) @@ -1723,6 +1713,18 @@ static int ohci_enable(struct fw_card *card, return -EIO; } + irqs = OHCI1394_reqTxComplete | OHCI1394_respTxComplete | + OHCI1394_RQPkt | OHCI1394_RSPkt | + OHCI1394_isochTx | OHCI1394_isochRx | + OHCI1394_postedWriteErr | + OHCI1394_selfIDComplete | + OHCI1394_regAccessFail | + OHCI1394_cycleInconsistent | OHCI1394_cycleTooLong | + OHCI1394_masterIntEnable; + if (param_debug & OHCI_PARAM_DEBUG_BUSRESETS) + irqs |= OHCI1394_busReset; + reg_write(ohci, OHCI1394_IntMaskSet, irqs); + reg_write(ohci, OHCI1394_HCControlSet, OHCI1394_HCControl_linkEnable | OHCI1394_HCControl_BIBimageValid); -- cgit v1.2.3 From 262444eecce40950af19ea4d75a3dc03b3c07283 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Sat, 5 Jun 2010 12:31:25 +0200 Subject: firewire: ohci: add MSI support This patch adds support for message-signaled interrupts. Any native PCI-Express OHCI controller should support MSI, but most are just PCI cores behind a PCI-E/PCI bridge. The only chips that are known to claim to support MSI are the Lucent/Agere/LSI FW643 and the VIA VT6315, none of which I have been able to test. Due to the high level of trust I have in the competence of these and any future chip makers, I thought it a good idea to add a disable-MSI quirk. Signed-off-by: Clemens Ladisch Tested Agere FW643 rev 07 [11c1:5901] and JMicron JMB381 [197b:2380]. Added a quirks list entry for JMB38X since it kept its count of MSI events consistently at zero. Signed-off-by: Stefan Richter --- drivers/firewire/ohci.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) (limited to 'drivers/firewire') diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index 9743a405e69c..de5ff376231c 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -231,12 +231,14 @@ static inline struct fw_ohci *fw_ohci(struct fw_card *card) static char ohci_driver_name[] = KBUILD_MODNAME; +#define PCI_DEVICE_ID_JMICRON_JMB38X_FW 0x2380 #define PCI_DEVICE_ID_TI_TSB12LV22 0x8009 #define QUIRK_CYCLE_TIMER 1 #define QUIRK_RESET_PACKET 2 #define QUIRK_BE_HEADERS 4 #define QUIRK_NO_1394A 8 +#define QUIRK_NO_MSI 16 /* In case of multiple matches in ohci_quirks[], only the first one is used. */ static const struct { @@ -247,6 +249,7 @@ static const struct { QUIRK_NO_1394A}, {PCI_VENDOR_ID_TI, PCI_ANY_ID, QUIRK_RESET_PACKET}, {PCI_VENDOR_ID_AL, PCI_ANY_ID, QUIRK_CYCLE_TIMER}, + {PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB38X_FW, QUIRK_NO_MSI}, {PCI_VENDOR_ID_NEC, PCI_ANY_ID, QUIRK_CYCLE_TIMER}, {PCI_VENDOR_ID_VIA, PCI_ANY_ID, QUIRK_CYCLE_TIMER}, {PCI_VENDOR_ID_APPLE, PCI_DEVICE_ID_APPLE_UNI_N_FW, QUIRK_BE_HEADERS}, @@ -260,6 +263,7 @@ MODULE_PARM_DESC(quirks, "Chip quirks (default = 0" ", reset packet generation = " __stringify(QUIRK_RESET_PACKET) ", AR/selfID endianess = " __stringify(QUIRK_BE_HEADERS) ", no 1394a enhancements = " __stringify(QUIRK_NO_1394A) + ", disable MSI = " __stringify(QUIRK_NO_MSI) ")"); #define OHCI_PARAM_DEBUG_AT_AR 1 @@ -1704,10 +1708,13 @@ static int ohci_enable(struct fw_card *card, reg_write(ohci, OHCI1394_AsReqFilterHiSet, 0x80000000); + if (!(ohci->quirks & QUIRK_NO_MSI)) + pci_enable_msi(dev); if (request_irq(dev->irq, irq_handler, - IRQF_SHARED, ohci_driver_name, ohci)) { - fw_error("Failed to allocate shared interrupt %d.\n", - dev->irq); + pci_dev_msi_enabled(dev) ? 0 : IRQF_SHARED, + ohci_driver_name, ohci)) { + fw_error("Failed to allocate interrupt %d.\n", dev->irq); + pci_disable_msi(dev); dma_free_coherent(ohci->card.device, CONFIG_ROM_SIZE, ohci->config_rom, ohci->config_rom_bus); return -EIO; @@ -2622,6 +2629,7 @@ static void pci_remove(struct pci_dev *dev) context_release(&ohci->at_response_ctx); kfree(ohci->it_context_list); kfree(ohci->ir_context_list); + pci_disable_msi(dev); pci_iounmap(dev, ohci->registers); pci_release_region(dev, 0); pci_disable_device(dev); @@ -2639,6 +2647,7 @@ static int pci_suspend(struct pci_dev *dev, pm_message_t state) software_reset(ohci); free_irq(dev->irq, ohci); + pci_disable_msi(dev); err = pci_save_state(dev); if (err) { fw_error("pci_save_state failed\n"); -- cgit v1.2.3 From a10c0ce76098857b899505d05de9f2e13ddf7a7a Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Wed, 19 May 2010 08:28:32 +0200 Subject: firewire: check cdev response length Add a check that the data length in the SEND_RESPONSE ioctl is correct. Incidentally, this also fixes the previously wrong response length of software-handled lock requests. Signed-off-by: Clemens Ladisch Signed-off-by: Stefan Richter --- drivers/firewire/core-cdev.c | 9 ++++++--- drivers/firewire/core-transaction.c | 38 ++++++++++++++++++++++++++++++++++++- drivers/firewire/core.h | 1 + 3 files changed, 44 insertions(+), 4 deletions(-) (limited to 'drivers/firewire') diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index 9d1a1a1a83c9..50332b84f49a 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -756,9 +756,12 @@ static int ioctl_send_response(struct client *client, union ioctl_arg *arg) if (is_fcp_request(r->request)) goto out; - if (a->length < r->length) - r->length = a->length; - if (copy_from_user(r->data, u64_to_uptr(a->data), r->length)) { + if (a->length != fw_get_response_length(r->request)) { + ret = -EINVAL; + kfree(r->request); + goto out; + } + if (copy_from_user(r->data, u64_to_uptr(a->data), a->length)) { ret = -EFAULT; kfree(r->request); goto out; diff --git a/drivers/firewire/core-transaction.c b/drivers/firewire/core-transaction.c index fdc33ff06dc1..4fd5c3b2128e 100644 --- a/drivers/firewire/core-transaction.c +++ b/drivers/firewire/core-transaction.c @@ -580,6 +580,41 @@ static void free_response_callback(struct fw_packet *packet, kfree(request); } +int fw_get_response_length(struct fw_request *r) +{ + int tcode, ext_tcode, data_length; + + tcode = HEADER_GET_TCODE(r->request_header[0]); + + switch (tcode) { + case TCODE_WRITE_QUADLET_REQUEST: + case TCODE_WRITE_BLOCK_REQUEST: + return 0; + + case TCODE_READ_QUADLET_REQUEST: + return 4; + + case TCODE_READ_BLOCK_REQUEST: + data_length = HEADER_GET_DATA_LENGTH(r->request_header[3]); + return data_length; + + case TCODE_LOCK_REQUEST: + ext_tcode = HEADER_GET_EXTENDED_TCODE(r->request_header[3]); + data_length = HEADER_GET_DATA_LENGTH(r->request_header[3]); + switch (ext_tcode) { + case EXTCODE_FETCH_ADD: + case EXTCODE_LITTLE_ADD: + return data_length; + default: + return data_length / 2; + } + + default: + WARN(1, KERN_ERR "wrong tcode %d", tcode); + return 0; + } +} + void fw_fill_response(struct fw_packet *response, u32 *request_header, int rcode, void *payload, size_t length) { @@ -713,7 +748,8 @@ void fw_send_response(struct fw_card *card, if (rcode == RCODE_COMPLETE) fw_fill_response(&request->response, request->request_header, - rcode, request->data, request->length); + rcode, request->data, + fw_get_response_length(request)); else fw_fill_response(&request->response, request->request_header, rcode, NULL, 0); diff --git a/drivers/firewire/core.h b/drivers/firewire/core.h index 0ecfcd95f4c5..25a72e57a0cd 100644 --- a/drivers/firewire/core.h +++ b/drivers/firewire/core.h @@ -218,6 +218,7 @@ static inline bool is_next_generation(int new_generation, int old_generation) void fw_core_handle_request(struct fw_card *card, struct fw_packet *request); 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); void fw_send_phy_config(struct fw_card *card, -- cgit v1.2.3 From f9c70f9129f2d88645c3a26711302a7f6ba9afd0 Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Sat, 5 Jun 2010 20:32:50 +0200 Subject: firewire: core: trivial fix for warning strings WARN's format string argument should not carry a printk level prefix. Signed-off-by: Stefan Richter --- drivers/firewire/core-transaction.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/firewire') diff --git a/drivers/firewire/core-transaction.c b/drivers/firewire/core-transaction.c index 4fd5c3b2128e..f33a629c8379 100644 --- a/drivers/firewire/core-transaction.c +++ b/drivers/firewire/core-transaction.c @@ -246,7 +246,7 @@ static void fw_fill_request(struct fw_packet *packet, int tcode, int tlabel, break; default: - WARN(1, KERN_ERR "wrong tcode %d", tcode); + WARN(1, "wrong tcode %d", tcode); } common: packet->speed = speed; @@ -610,7 +610,7 @@ int fw_get_response_length(struct fw_request *r) } default: - WARN(1, KERN_ERR "wrong tcode %d", tcode); + WARN(1, "wrong tcode %d", tcode); return 0; } } @@ -666,7 +666,7 @@ void fw_fill_response(struct fw_packet *response, u32 *request_header, break; default: - WARN(1, KERN_ERR "wrong tcode %d", tcode); + WARN(1, "wrong tcode %d", tcode); } response->payload_mapped = false; -- cgit v1.2.3 From 153e3979201b76dbd5788f032fb683e95121e159 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Thu, 10 Jun 2010 08:22:07 +0200 Subject: firewire: ohci: speed up PHY register accesses Most PHY chips, when idle, can complete a register access in the time needed for two or three PCI read transactions; bigger delays occur only when data is currently being moved over the link/PHY interface. So if we busy-wait a few times when waiting for the register access to finish, it is likely that we can finish without having to sleep. Signed-off-by: Clemens Ladisch --- drivers/firewire/ohci.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) (limited to 'drivers/firewire') diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index de5ff376231c..65b9bdb8541a 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -474,12 +474,17 @@ static int read_phy_reg(struct fw_ohci *ohci, int addr) int i; reg_write(ohci, OHCI1394_PhyControl, OHCI1394_PhyControl_Read(addr)); - for (i = 0; i < 10; i++) { + for (i = 0; i < 3 + 100; i++) { val = reg_read(ohci, OHCI1394_PhyControl); if (val & OHCI1394_PhyControl_ReadDone) return OHCI1394_PhyControl_ReadData(val); - msleep(1); + /* + * Try a few times without waiting. Sleeping is necessary + * only when the link/PHY interface is busy. + */ + if (i >= 3) + msleep(1); } fw_error("failed to read phy reg\n"); @@ -492,12 +497,13 @@ static int write_phy_reg(const struct fw_ohci *ohci, int addr, u32 val) reg_write(ohci, OHCI1394_PhyControl, OHCI1394_PhyControl_Write(addr, val)); - for (i = 0; i < 100; i++) { + for (i = 0; i < 3 + 100; i++) { val = reg_read(ohci, OHCI1394_PhyControl); if (!(val & OHCI1394_PhyControl_WritePending)) return 0; - msleep(1); + if (i >= 3) + msleep(1); } fw_error("failed to write phy reg\n"); -- cgit v1.2.3 From bda3b8a1faf209a98063ccd77d6833a2bb0fc77e Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Thu, 10 Jun 2010 08:23:28 +0200 Subject: firewire: core: retry on local errors in bus manager election When the candidate bus manager fails to do the lock request with which it tries to become bus manager, it assumes that the current IRM is not actually IRM capable and forces itself to become root. However, if that lock request failed because the local node itself was not able to send it, then we cannot blame the current IRM and should not steal its rootness. In this case, RCODE_SEND_ERROR is likely to indicate a temporary error condition such as exhausted tlabels or low memory, so we better try again later. Signed-off-by: Clemens Ladisch --- drivers/firewire/core-card.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers/firewire') diff --git a/drivers/firewire/core-card.c b/drivers/firewire/core-card.c index 9dcb30466ec0..901435cdd5c2 100644 --- a/drivers/firewire/core-card.c +++ b/drivers/firewire/core-card.c @@ -306,6 +306,16 @@ static void fw_card_bm_work(struct work_struct *work) goto out; } + if (rcode == RCODE_SEND_ERROR) { + /* + * We have been unable to send the lock request due to + * some local problem. Let's try again later and hope + * that the problem has gone away by then. + */ + fw_schedule_bm_work(card, DIV_ROUND_UP(HZ, 8)); + goto out; + } + spin_lock_irqsave(&card->lock, flags); if (rcode != RCODE_COMPLETE) { -- cgit v1.2.3 From 3e07ec0eee1662f89e57f84aff625065beb2b209 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Thu, 10 Jun 2010 08:24:03 +0200 Subject: firewire: core: add CSR STATE_CLEAR/STATE_SET support The state registers are zero and read-only in this implementation, so they are not of much use. However, the specification requires that they are present for transaction capable nodes, and the Base 1394 Test Suite tests for them, so we better implement them. Signed-off-by: Clemens Ladisch --- drivers/firewire/core-transaction.c | 44 +++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) (limited to 'drivers/firewire') diff --git a/drivers/firewire/core-transaction.c b/drivers/firewire/core-transaction.c index f33a629c8379..6971400b6354 100644 --- a/drivers/firewire/core-transaction.c +++ b/drivers/firewire/core-transaction.c @@ -969,6 +969,30 @@ 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) +{ + /* + * Fixed bits (IEEE 1394-2008 8.3.2.2.1): + * Bits 0-1 (state) always read 00=running. + * Bits 2,3 (off, atn) are not implemented as per the spec. + * Bit 4 (elog) is not implemented because there is no error log. + * Bit 6 (dreq) cannot be set. It is intended to "disable requests + * from unreliable nodes"; however, IEEE 1212 states that devices + * may "clear their own dreq bit when it has been improperly set". + * Our implementation might be seen as an improperly extensive + * interpretation of "improperly", but the 1212-2001 revision + * dropped this bit altogether, so we're in the clear. :o) + * Bit 7 (lost) always reads 0 because a power reset has never occurred + * during normal operation. + * Bit 9 (linkoff) is not implemented because the PC is not powered + * from the FireWire cable. + * Bit 15 (gone) always reads 0. It must be set at a power/command/bus + * reset, but then cleared when the units are ready again, which + * happens immediately for us. + */ + return 0; +} + 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, @@ -979,6 +1003,26 @@ static void handle_registers(struct fw_card *card, struct fw_request *request, int rcode = RCODE_COMPLETE; 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) { + } 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) { + /* FIXME: implement cmstr */ + /* FIXME: implement abdicate */ + } else { + rcode = RCODE_TYPE_ERROR; + } + break; + case CSR_CYCLE_TIME: if (TCODE_IS_READ_REQUEST(tcode) && length == 4) *data = cpu_to_be32(card->driver->get_cycle_time(card)); -- cgit v1.2.3 From 60d32970c5a32e8c4f340a9e41993759ad658ef2 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Thu, 10 Jun 2010 08:24:35 +0200 Subject: firewire: add read_csr_reg driver callback To prepare for the following additions of more OHCI-implemented CSR registers, replace the get_cycle_time driver callback with a generic CSR register callback. Signed-off-by: Clemens Ladisch --- drivers/firewire/core-cdev.c | 2 +- drivers/firewire/core-transaction.c | 3 ++- drivers/firewire/core.h | 2 +- drivers/firewire/ohci.c | 19 ++++++++++++++++--- 4 files changed, 20 insertions(+), 6 deletions(-) (limited to 'drivers/firewire') diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index 50332b84f49a..2e62516a4b15 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -1044,7 +1044,7 @@ static int ioctl_get_cycle_timer2(struct client *client, union ioctl_arg *arg) local_irq_disable(); - cycle_time = card->driver->get_cycle_time(card); + cycle_time = card->driver->read_csr_reg(card, CSR_CYCLE_TIME); switch (a->clk_id) { case CLOCK_REALTIME: getnstimeofday(&ts); break; diff --git a/drivers/firewire/core-transaction.c b/drivers/firewire/core-transaction.c index 6971400b6354..a4d8109edec2 100644 --- a/drivers/firewire/core-transaction.c +++ b/drivers/firewire/core-transaction.c @@ -1025,7 +1025,8 @@ static void handle_registers(struct fw_card *card, struct fw_request *request, case CSR_CYCLE_TIME: if (TCODE_IS_READ_REQUEST(tcode) && length == 4) - *data = cpu_to_be32(card->driver->get_cycle_time(card)); + *data = cpu_to_be32(card->driver-> + read_csr_reg(card, CSR_CYCLE_TIME)); else rcode = RCODE_TYPE_ERROR; break; diff --git a/drivers/firewire/core.h b/drivers/firewire/core.h index 25a72e57a0cd..c19e9873e433 100644 --- a/drivers/firewire/core.h +++ b/drivers/firewire/core.h @@ -75,7 +75,7 @@ struct fw_card_driver { int (*enable_phys_dma)(struct fw_card *card, int node_id, int generation); - u32 (*get_cycle_time)(struct fw_card *card); + u32 (*read_csr_reg)(struct fw_card *card, int csr_offset); struct fw_iso_context * (*allocate_iso_context)(struct fw_card *card, diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index 65b9bdb8541a..a8093a9a3fc8 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -1939,9 +1939,8 @@ static u32 cycle_timer_ticks(u32 cycle_timer) * error. (A PCI read should take at least 20 ticks of the 24.576 MHz timer to * execute, so we have enough precision to compute the ratio of the differences.) */ -static u32 ohci_get_cycle_time(struct fw_card *card) +static u32 get_cycle_time(struct fw_ohci *ohci) { - struct fw_ohci *ohci = fw_ohci(card); u32 c0, c1, c2; u32 t0, t1, t2; s32 diff01, diff12; @@ -1970,6 +1969,20 @@ static u32 ohci_get_cycle_time(struct fw_card *card) return c2; } +static u32 ohci_read_csr_reg(struct fw_card *card, int csr_offset) +{ + struct fw_ohci *ohci = fw_ohci(card); + + switch (csr_offset) { + case CSR_CYCLE_TIME: + return get_cycle_time(ohci); + + default: + WARN_ON(1); + return 0; + } +} + static void copy_iso_headers(struct iso_context *ctx, void *p) { int i = ctx->header_length; @@ -2407,7 +2420,7 @@ static const struct fw_card_driver ohci_driver = { .send_response = ohci_send_response, .cancel_packet = ohci_cancel_packet, .enable_phys_dma = ohci_enable_phys_dma, - .get_cycle_time = ohci_get_cycle_time, + .read_csr_reg = ohci_read_csr_reg, .allocate_iso_context = ohci_allocate_iso_context, .free_iso_context = ohci_free_iso_context, -- cgit v1.2.3 From 506f1a31932747f56a5029d5b3c14b1b68f41ccc Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Thu, 10 Jun 2010 08:25:19 +0200 Subject: firewire: add CSR NODE_IDS support The NODE_IDS register, and especially its bus_id field, is quite useless because 1394.1 requires that the bus_id field always stays 0x3ff. However, the 1394 specification requires this register on all transaction capable nodes, and the Base 1394 Test Suite tests for it, so we better implement it. Signed-off-by: Clemens Ladisch --- drivers/firewire/core-transaction.c | 11 +++++++++++ drivers/firewire/core.h | 1 + drivers/firewire/ohci.c | 20 ++++++++++++++++++++ 3 files changed, 32 insertions(+) (limited to 'drivers/firewire') diff --git a/drivers/firewire/core-transaction.c b/drivers/firewire/core-transaction.c index a4d8109edec2..16ffa27d23b7 100644 --- a/drivers/firewire/core-transaction.c +++ b/drivers/firewire/core-transaction.c @@ -1023,6 +1023,17 @@ static void handle_registers(struct fw_card *card, struct fw_request *request, } break; + case CSR_NODE_IDS: + if (tcode == TCODE_READ_QUADLET_REQUEST) + *data = cpu_to_be32(card->driver-> + read_csr_reg(card, CSR_NODE_IDS)); + else if (tcode == TCODE_WRITE_QUADLET_REQUEST) + card->driver->write_csr_reg(card, CSR_NODE_IDS, + be32_to_cpu(*data)); + else + rcode = RCODE_TYPE_ERROR; + break; + case CSR_CYCLE_TIME: if (TCODE_IS_READ_REQUEST(tcode) && length == 4) *data = cpu_to_be32(card->driver-> diff --git a/drivers/firewire/core.h b/drivers/firewire/core.h index c19e9873e433..efcdeb2e31e6 100644 --- a/drivers/firewire/core.h +++ b/drivers/firewire/core.h @@ -76,6 +76,7 @@ struct fw_card_driver { int node_id, int generation); u32 (*read_csr_reg)(struct fw_card *card, int csr_offset); + void (*write_csr_reg)(struct fw_card *card, int csr_offset, u32 value); struct fw_iso_context * (*allocate_iso_context)(struct fw_card *card, diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index a8093a9a3fc8..a55fbbce9e79 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -1974,6 +1974,9 @@ static u32 ohci_read_csr_reg(struct fw_card *card, int csr_offset) struct fw_ohci *ohci = fw_ohci(card); switch (csr_offset) { + case CSR_NODE_IDS: + return reg_read(ohci, OHCI1394_NodeID) << 16; + case CSR_CYCLE_TIME: return get_cycle_time(ohci); @@ -1983,6 +1986,22 @@ static u32 ohci_read_csr_reg(struct fw_card *card, int csr_offset) } } +static void ohci_write_csr_reg(struct fw_card *card, int csr_offset, u32 value) +{ + struct fw_ohci *ohci = fw_ohci(card); + + switch (csr_offset) { + case CSR_NODE_IDS: + reg_write(ohci, OHCI1394_NodeID, value >> 16); + flush_writes(ohci); + break; + + default: + WARN_ON(1); + break; + } +} + static void copy_iso_headers(struct iso_context *ctx, void *p) { int i = ctx->header_length; @@ -2421,6 +2440,7 @@ static const struct fw_card_driver ohci_driver = { .cancel_packet = ohci_cancel_packet, .enable_phys_dma = ohci_enable_phys_dma, .read_csr_reg = ohci_read_csr_reg, + .write_csr_reg = ohci_write_csr_reg, .allocate_iso_context = ohci_allocate_iso_context, .free_iso_context = ohci_free_iso_context, -- cgit v1.2.3 From 446eba0d6896787b2f02f7a665838d32aa7b9d3f Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Thu, 10 Jun 2010 08:25:46 +0200 Subject: firewire: core: add CSR RESET_START support This implements the RESET_START register (as a dummy) to make the Base 1394 Test Suite happy. Signed-off-by: Clemens Ladisch --- drivers/firewire/core-transaction.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/firewire') diff --git a/drivers/firewire/core-transaction.c b/drivers/firewire/core-transaction.c index 16ffa27d23b7..0034229dfd14 100644 --- a/drivers/firewire/core-transaction.c +++ b/drivers/firewire/core-transaction.c @@ -1034,6 +1034,11 @@ static void handle_registers(struct fw_card *card, struct fw_request *request, rcode = RCODE_TYPE_ERROR; break; + case CSR_RESET_START: + if (tcode != TCODE_WRITE_QUADLET_REQUEST) + rcode = RCODE_TYPE_ERROR; + break; + case CSR_CYCLE_TIME: if (TCODE_IS_READ_REQUEST(tcode) && length == 4) *data = cpu_to_be32(card->driver-> -- cgit v1.2.3 From 8e4b50f94e8c1435a3e0ece42b7f97bc857d0145 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Thu, 10 Jun 2010 08:26:28 +0200 Subject: firewire: core: add CSR SPLIT_TIMEOUT support Implement the SPLIT_TIMEOUT registers. Besides being required by the spec, this is desirable for some IIDC devices and necessary for many audio devices to be able to increase the timeout from userspace. Signed-off-by: Clemens Ladisch --- drivers/firewire/core-card.c | 4 ++ drivers/firewire/core-transaction.c | 76 +++++++++++++++++++++++++++++++------ 2 files changed, 69 insertions(+), 11 deletions(-) (limited to 'drivers/firewire') diff --git a/drivers/firewire/core-card.c b/drivers/firewire/core-card.c index 901435cdd5c2..d0f15c2f1e1d 100644 --- a/drivers/firewire/core-card.c +++ b/drivers/firewire/core-card.c @@ -428,6 +428,10 @@ void fw_card_initialize(struct fw_card *card, card->device = device; card->current_tlabel = 0; card->tlabel_mask = 0; + card->split_timeout_hi = 0; + card->split_timeout_lo = 800 << 19; + card->split_timeout_cycles = 800; + card->split_timeout_jiffies = DIV_ROUND_UP(HZ, 10); card->color = 0; card->broadcast_channel = BROADCAST_CHANNEL_INITIAL; diff --git a/drivers/firewire/core-transaction.c b/drivers/firewire/core-transaction.c index 0034229dfd14..9a7d3ec23f2b 100644 --- a/drivers/firewire/core-transaction.c +++ b/drivers/firewire/core-transaction.c @@ -339,7 +339,8 @@ void fw_send_request(struct fw_card *card, struct fw_transaction *t, int tcode, setup_timer(&t->split_timeout_timer, split_transaction_timeout_callback, (unsigned long)t); /* FIXME: start this timer later, relative to t->timestamp */ - mod_timer(&t->split_timeout_timer, jiffies + DIV_ROUND_UP(HZ, 10)); + mod_timer(&t->split_timeout_timer, + jiffies + card->split_timeout_jiffies); t->callback = callback; t->callback_data = callback_data; @@ -673,11 +674,28 @@ void fw_fill_response(struct fw_packet *response, u32 *request_header, } EXPORT_SYMBOL(fw_fill_response); -static struct fw_request *allocate_request(struct fw_packet *p) +static u32 compute_split_timeout_timestamp(struct fw_card *card, + u32 request_timestamp) +{ + unsigned int cycles; + u32 timestamp; + + cycles = card->split_timeout_cycles; + cycles += request_timestamp & 0x1fff; + + timestamp = request_timestamp & ~0x1fff; + timestamp += (cycles / 8000) << 13; + timestamp |= cycles % 8000; + + return timestamp; +} + +static struct fw_request *allocate_request(struct fw_card *card, + struct fw_packet *p) { struct fw_request *request; u32 *data, length; - int request_tcode, t; + int request_tcode; request_tcode = HEADER_GET_TCODE(p->header[0]); switch (request_tcode) { @@ -712,14 +730,9 @@ static struct fw_request *allocate_request(struct fw_packet *p) if (request == NULL) return NULL; - t = (p->timestamp & 0x1fff) + 4000; - if (t >= 8000) - t = (p->timestamp & ~0x1fff) + 0x2000 + t - 8000; - else - t = (p->timestamp & ~0x1fff) + t; - request->response.speed = p->speed; - request->response.timestamp = t; + request->response.timestamp = + compute_split_timeout_timestamp(card, p->timestamp); request->response.generation = p->generation; request->response.ack = 0; request->response.callback = free_response_callback; @@ -845,7 +858,7 @@ void fw_core_handle_request(struct fw_card *card, struct fw_packet *p) if (p->ack != ACK_PENDING && p->ack != ACK_COMPLETE) return; - request = allocate_request(p); + request = allocate_request(card, p); if (request == NULL) { /* FIXME: send statically allocated busy packet. */ return; @@ -993,6 +1006,19 @@ static u32 read_state_register(struct fw_card *card) return 0; } +static void update_split_timeout(struct fw_card *card) +{ + unsigned int cycles; + + cycles = card->split_timeout_hi * 8000 + (card->split_timeout_lo >> 19); + + cycles = max(cycles, 800u); /* minimum as per the spec */ + cycles = min(cycles, 3u * 8000u); /* maximum OHCI timeout */ + + card->split_timeout_cycles = cycles; + card->split_timeout_jiffies = DIV_ROUND_UP(cycles * HZ, 8000); +} + static void handle_registers(struct fw_card *card, struct fw_request *request, int tcode, int destination, int source, int generation, int speed, unsigned long long offset, @@ -1001,6 +1027,7 @@ static void handle_registers(struct fw_card *card, struct fw_request *request, int reg = offset & ~CSR_REGISTER_BASE; __be32 *data = payload; int rcode = RCODE_COMPLETE; + unsigned long flags; switch (reg) { case CSR_STATE_CLEAR: @@ -1039,6 +1066,33 @@ static void handle_registers(struct fw_card *card, struct fw_request *request, rcode = RCODE_TYPE_ERROR; break; + case CSR_SPLIT_TIMEOUT_HI: + if (tcode == TCODE_READ_QUADLET_REQUEST) { + *data = cpu_to_be32(card->split_timeout_hi); + } else if (tcode == TCODE_WRITE_QUADLET_REQUEST) { + spin_lock_irqsave(&card->lock, flags); + card->split_timeout_hi = be32_to_cpu(*data) & 7; + update_split_timeout(card); + spin_unlock_irqrestore(&card->lock, flags); + } else { + rcode = RCODE_TYPE_ERROR; + } + break; + + case CSR_SPLIT_TIMEOUT_LO: + if (tcode == TCODE_READ_QUADLET_REQUEST) { + *data = cpu_to_be32(card->split_timeout_lo); + } else if (tcode == TCODE_WRITE_QUADLET_REQUEST) { + spin_lock_irqsave(&card->lock, flags); + card->split_timeout_lo = + be32_to_cpu(*data) & 0xfff80000; + update_split_timeout(card); + spin_unlock_irqrestore(&card->lock, flags); + } else { + rcode = RCODE_TYPE_ERROR; + } + break; + case CSR_CYCLE_TIME: if (TCODE_IS_READ_REQUEST(tcode) && length == 4) *data = cpu_to_be32(card->driver-> -- cgit v1.2.3 From 9ab5071cd4a16001e4ba790172a7da5e4172462b Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Thu, 10 Jun 2010 08:26:48 +0200 Subject: firewire: add CSR CYCLE_TIME write support The specification requires that CYCLE_TIME is writable so that it can be initialized, so we better implement it. Signed-off-by: Clemens Ladisch --- drivers/firewire/core-transaction.c | 3 +++ drivers/firewire/ohci.c | 7 +++++++ 2 files changed, 10 insertions(+) (limited to 'drivers/firewire') diff --git a/drivers/firewire/core-transaction.c b/drivers/firewire/core-transaction.c index 9a7d3ec23f2b..e3925f67ec12 100644 --- a/drivers/firewire/core-transaction.c +++ b/drivers/firewire/core-transaction.c @@ -1097,6 +1097,9 @@ static void handle_registers(struct fw_card *card, struct fw_request *request, if (TCODE_IS_READ_REQUEST(tcode) && length == 4) *data = cpu_to_be32(card->driver-> read_csr_reg(card, CSR_CYCLE_TIME)); + else if (tcode == TCODE_WRITE_QUADLET_REQUEST) + card->driver->write_csr_reg(card, CSR_CYCLE_TIME, + be32_to_cpu(*data)); else rcode = RCODE_TYPE_ERROR; break; diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index a55fbbce9e79..777811a736b2 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -1996,6 +1996,13 @@ static void ohci_write_csr_reg(struct fw_card *card, int csr_offset, u32 value) flush_writes(ohci); break; + case CSR_CYCLE_TIME: + reg_write(ohci, OHCI1394_IsochronousCycleTimer, value); + reg_write(ohci, OHCI1394_IntEventSet, + OHCI1394_cycleInconsistent); + flush_writes(ohci); + break; + default: WARN_ON(1); break; -- cgit v1.2.3 From a48777e03ad53777ed119a5f86dd22a6c5a378ad Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Thu, 10 Jun 2010 08:33:07 +0200 Subject: firewire: add CSR BUS_TIME support Implement the BUS_TIME register, which is required for cycle master capable nodes and tested for by the Base 1393 Test Suite. Even when there is not yet bus master initialization support, this register allows us to work together with other bus masters. Signed-off-by: Clemens Ladisch --- drivers/firewire/core-transaction.c | 14 ++- drivers/firewire/ohci.c | 168 +++++++++++++++++++++++------------- 2 files changed, 120 insertions(+), 62 deletions(-) (limited to 'drivers/firewire') diff --git a/drivers/firewire/core-transaction.c b/drivers/firewire/core-transaction.c index e3925f67ec12..2a390726fa76 100644 --- a/drivers/firewire/core-transaction.c +++ b/drivers/firewire/core-transaction.c @@ -1104,6 +1104,17 @@ static void handle_registers(struct fw_card *card, struct fw_request *request, rcode = RCODE_TYPE_ERROR; break; + case CSR_BUS_TIME: + if (tcode == TCODE_READ_QUADLET_REQUEST) + *data = cpu_to_be32(card->driver-> + read_csr_reg(card, CSR_BUS_TIME)); + else if (tcode == TCODE_WRITE_QUADLET_REQUEST) + card->driver->write_csr_reg(card, CSR_BUS_TIME, + 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); @@ -1132,9 +1143,6 @@ static void handle_registers(struct fw_card *card, struct fw_request *request, case CSR_BUSY_TIMEOUT: /* FIXME: Implement this. */ - case CSR_BUS_TIME: - /* Useless without initialization by the bus manager. */ - default: rcode = RCODE_ADDRESS_ERROR; break; diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index 777811a736b2..3d4badb7c79b 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; + u32 bus_time; /* * Spinlock for accessing fw_ohci data. Never call out of @@ -292,7 +293,7 @@ static void log_irqs(u32 evt) !(evt & OHCI1394_busReset)) return; - fw_notify("IRQ %08x%s%s%s%s%s%s%s%s%s%s%s%s%s\n", evt, + fw_notify("IRQ %08x%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n", evt, evt & OHCI1394_selfIDComplete ? " selfID" : "", evt & OHCI1394_RQPkt ? " AR_req" : "", evt & OHCI1394_RSPkt ? " AR_resp" : "", @@ -302,6 +303,7 @@ static void log_irqs(u32 evt) evt & OHCI1394_isochTx ? " IT" : "", evt & OHCI1394_postedWriteErr ? " postedWriteErr" : "", evt & OHCI1394_cycleTooLong ? " cycleTooLong" : "", + evt & OHCI1394_cycle64Seconds ? " cycle64Seconds" : "", evt & OHCI1394_cycleInconsistent ? " cycleInconsistent" : "", evt & OHCI1394_regAccessFail ? " regAccessFail" : "", evt & OHCI1394_busReset ? " busReset" : "", @@ -309,7 +311,8 @@ static void log_irqs(u32 evt) OHCI1394_RSPkt | OHCI1394_reqTxComplete | OHCI1394_respTxComplete | OHCI1394_isochRx | OHCI1394_isochTx | OHCI1394_postedWriteErr | - OHCI1394_cycleTooLong | OHCI1394_cycleInconsistent | + OHCI1394_cycleTooLong | OHCI1394_cycle64Seconds | + OHCI1394_cycleInconsistent | OHCI1394_regAccessFail | OHCI1394_busReset) ? " ?" : ""); } @@ -1316,6 +1319,78 @@ static void at_context_transmit(struct context *ctx, struct fw_packet *packet) } +static u32 cycle_timer_ticks(u32 cycle_timer) +{ + u32 ticks; + + ticks = cycle_timer & 0xfff; + ticks += 3072 * ((cycle_timer >> 12) & 0x1fff); + ticks += (3072 * 8000) * (cycle_timer >> 25); + + return ticks; +} + +/* + * Some controllers exhibit one or more of the following bugs when updating the + * iso cycle timer register: + * - When the lowest six bits are wrapping around to zero, a read that happens + * at the same time will return garbage in the lowest ten bits. + * - When the cycleOffset field wraps around to zero, the cycleCount field is + * not incremented for about 60 ns. + * - Occasionally, the entire register reads zero. + * + * To catch these, we read the register three times and ensure that the + * difference between each two consecutive reads is approximately the same, i.e. + * less than twice the other. Furthermore, any negative difference indicates an + * error. (A PCI read should take at least 20 ticks of the 24.576 MHz timer to + * execute, so we have enough precision to compute the ratio of the differences.) + */ +static u32 get_cycle_time(struct fw_ohci *ohci) +{ + u32 c0, c1, c2; + u32 t0, t1, t2; + s32 diff01, diff12; + int i; + + c2 = reg_read(ohci, OHCI1394_IsochronousCycleTimer); + + if (ohci->quirks & QUIRK_CYCLE_TIMER) { + i = 0; + c1 = c2; + c2 = reg_read(ohci, OHCI1394_IsochronousCycleTimer); + do { + c0 = c1; + c1 = c2; + c2 = reg_read(ohci, OHCI1394_IsochronousCycleTimer); + t0 = cycle_timer_ticks(c0); + t1 = cycle_timer_ticks(c1); + t2 = cycle_timer_ticks(c2); + diff01 = t1 - t0; + diff12 = t2 - t1; + } while ((diff01 <= 0 || diff12 <= 0 || + diff01 / diff12 >= 2 || diff12 / diff01 >= 2) + && i++ < 20); + } + + return c2; +} + +/* + * This function has to be called at least every 64 seconds. The bus_time + * field stores not only the upper 25 bits of the BUS_TIME register but also + * the most significant bit of the cycle timer in bit 6 so that we can detect + * changes in this bit. + */ +static u32 update_bus_time(struct fw_ohci *ohci) +{ + u32 cycle_time_seconds = get_cycle_time(ohci) >> 25; + + if ((ohci->bus_time & 0x40) != (cycle_time_seconds & 0x40)) + ohci->bus_time += 0x40; + + return ohci->bus_time | cycle_time_seconds; +} + static void bus_reset_tasklet(unsigned long data) { struct fw_ohci *ohci = (struct fw_ohci *)data; @@ -1520,6 +1595,12 @@ static irqreturn_t irq_handler(int irq, void *data) fw_notify("isochronous cycle inconsistent\n"); } + if (event & OHCI1394_cycle64Seconds) { + spin_lock(&ohci->lock); + update_bus_time(ohci); + spin_unlock(&ohci->lock); + } + return IRQ_HANDLED; } @@ -1604,7 +1685,7 @@ static int ohci_enable(struct fw_card *card, { struct fw_ohci *ohci = fw_ohci(card); struct pci_dev *dev = to_pci_dev(card->device); - u32 lps, irqs; + u32 lps, seconds, irqs; int i, ret; if (software_reset(ohci)) { @@ -1652,6 +1733,10 @@ static int ohci_enable(struct fw_card *card, (OHCI1394_MAX_AT_RESP_RETRIES << 4) | (OHCI1394_MAX_PHYS_RESP_RETRIES << 8)); + seconds = lower_32_bits(get_seconds()); + reg_write(ohci, OHCI1394_IsochronousCycleTimer, seconds << 25); + ohci->bus_time = seconds & ~0x3f; + ar_context_run(&ohci->ar_request_ctx); ar_context_run(&ohci->ar_response_ctx); @@ -1732,6 +1817,7 @@ static int ohci_enable(struct fw_card *card, OHCI1394_postedWriteErr | OHCI1394_selfIDComplete | OHCI1394_regAccessFail | + OHCI1394_cycle64Seconds | OHCI1394_cycleInconsistent | OHCI1394_cycleTooLong | OHCI1394_masterIntEnable; if (param_debug & OHCI_PARAM_DEBUG_BUSRESETS) @@ -1913,65 +1999,11 @@ static int ohci_enable_phys_dma(struct fw_card *card, #endif /* CONFIG_FIREWIRE_OHCI_REMOTE_DMA */ } -static u32 cycle_timer_ticks(u32 cycle_timer) -{ - u32 ticks; - - ticks = cycle_timer & 0xfff; - ticks += 3072 * ((cycle_timer >> 12) & 0x1fff); - ticks += (3072 * 8000) * (cycle_timer >> 25); - - return ticks; -} - -/* - * Some controllers exhibit one or more of the following bugs when updating the - * iso cycle timer register: - * - When the lowest six bits are wrapping around to zero, a read that happens - * at the same time will return garbage in the lowest ten bits. - * - When the cycleOffset field wraps around to zero, the cycleCount field is - * not incremented for about 60 ns. - * - Occasionally, the entire register reads zero. - * - * To catch these, we read the register three times and ensure that the - * difference between each two consecutive reads is approximately the same, i.e. - * less than twice the other. Furthermore, any negative difference indicates an - * error. (A PCI read should take at least 20 ticks of the 24.576 MHz timer to - * execute, so we have enough precision to compute the ratio of the differences.) - */ -static u32 get_cycle_time(struct fw_ohci *ohci) -{ - u32 c0, c1, c2; - u32 t0, t1, t2; - s32 diff01, diff12; - int i; - - c2 = reg_read(ohci, OHCI1394_IsochronousCycleTimer); - - if (ohci->quirks & QUIRK_CYCLE_TIMER) { - i = 0; - c1 = c2; - c2 = reg_read(ohci, OHCI1394_IsochronousCycleTimer); - do { - c0 = c1; - c1 = c2; - c2 = reg_read(ohci, OHCI1394_IsochronousCycleTimer); - t0 = cycle_timer_ticks(c0); - t1 = cycle_timer_ticks(c1); - t2 = cycle_timer_ticks(c2); - diff01 = t1 - t0; - diff12 = t2 - t1; - } while ((diff01 <= 0 || diff12 <= 0 || - diff01 / diff12 >= 2 || diff12 / diff01 >= 2) - && i++ < 20); - } - - return c2; -} - static u32 ohci_read_csr_reg(struct fw_card *card, int csr_offset) { struct fw_ohci *ohci = fw_ohci(card); + unsigned long flags; + u32 value; switch (csr_offset) { case CSR_NODE_IDS: @@ -1980,6 +2012,17 @@ static u32 ohci_read_csr_reg(struct fw_card *card, int csr_offset) case CSR_CYCLE_TIME: return get_cycle_time(ohci); + case CSR_BUS_TIME: + /* + * We might be called just after the cycle timer has wrapped + * around but just before the cycle64Seconds handler, so we + * better check here, too, if the bus time needs to be updated. + */ + spin_lock_irqsave(&ohci->lock, flags); + value = update_bus_time(ohci); + spin_unlock_irqrestore(&ohci->lock, flags); + return value; + default: WARN_ON(1); return 0; @@ -1989,6 +2032,7 @@ static u32 ohci_read_csr_reg(struct fw_card *card, int csr_offset) static void ohci_write_csr_reg(struct fw_card *card, int csr_offset, u32 value) { struct fw_ohci *ohci = fw_ohci(card); + unsigned long flags; switch (csr_offset) { case CSR_NODE_IDS: @@ -2003,6 +2047,12 @@ static void ohci_write_csr_reg(struct fw_card *card, int csr_offset, u32 value) flush_writes(ohci); break; + case CSR_BUS_TIME: + spin_lock_irqsave(&ohci->lock, flags); + ohci->bus_time = (ohci->bus_time & 0x7f) | (value & ~0x7f); + spin_unlock_irqrestore(&ohci->lock, flags); + break; + default: WARN_ON(1); break; -- cgit v1.2.3 From 27a2329f8235d6ce637463f5d83e98d760ef006e Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Thu, 10 Jun 2010 08:34:13 +0200 Subject: firewire: add CSR BUSY_TIMEOUT support Implement the BUSY_TIMEOUT register, which is required for nodes that support retries. Signed-off-by: Clemens Ladisch --- drivers/firewire/core-transaction.c | 14 +++++++++++--- drivers/firewire/ohci.c | 14 +++++++++++++- 2 files changed, 24 insertions(+), 4 deletions(-) (limited to 'drivers/firewire') diff --git a/drivers/firewire/core-transaction.c b/drivers/firewire/core-transaction.c index 2a390726fa76..8146133818dc 100644 --- a/drivers/firewire/core-transaction.c +++ b/drivers/firewire/core-transaction.c @@ -1115,6 +1115,17 @@ static void handle_registers(struct fw_card *card, struct fw_request *request, rcode = RCODE_TYPE_ERROR; break; + case CSR_BUSY_TIMEOUT: + if (tcode == TCODE_READ_QUADLET_REQUEST) + *data = cpu_to_be32(card->driver-> + read_csr_reg(card, CSR_BUSY_TIMEOUT)); + else if (tcode == TCODE_WRITE_QUADLET_REQUEST) + card->driver->write_csr_reg(card, CSR_BUSY_TIMEOUT, + 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); @@ -1140,9 +1151,6 @@ static void handle_registers(struct fw_card *card, struct fw_request *request, BUG(); break; - case CSR_BUSY_TIMEOUT: - /* FIXME: Implement this. */ - default: rcode = RCODE_ADDRESS_ERROR; break; diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index 3d4badb7c79b..9c588fd01250 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -1731,7 +1731,8 @@ static int ohci_enable(struct fw_card *card, reg_write(ohci, OHCI1394_ATRetries, OHCI1394_MAX_AT_REQ_RETRIES | (OHCI1394_MAX_AT_RESP_RETRIES << 4) | - (OHCI1394_MAX_PHYS_RESP_RETRIES << 8)); + (OHCI1394_MAX_PHYS_RESP_RETRIES << 8) | + (200 << 16)); seconds = lower_32_bits(get_seconds()); reg_write(ohci, OHCI1394_IsochronousCycleTimer, seconds << 25); @@ -2023,6 +2024,10 @@ static u32 ohci_read_csr_reg(struct fw_card *card, int csr_offset) spin_unlock_irqrestore(&ohci->lock, flags); return value; + case CSR_BUSY_TIMEOUT: + value = reg_read(ohci, OHCI1394_ATRetries); + return (value >> 4) & 0x0ffff00f; + default: WARN_ON(1); return 0; @@ -2053,6 +2058,13 @@ static void ohci_write_csr_reg(struct fw_card *card, int csr_offset, u32 value) spin_unlock_irqrestore(&ohci->lock, flags); break; + case CSR_BUSY_TIMEOUT: + value = (value & 0xf) | ((value & 0xf) << 4) | + ((value & 0xf) << 8) | ((value & 0x0ffff000) << 4); + reg_write(ohci, OHCI1394_ATRetries, value); + flush_writes(ohci); + break; + default: WARN_ON(1); break; -- 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 +++++++++++++++++++++++++++ 3 files changed, 45 insertions(+) (limited to 'drivers/firewire') 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, -- 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 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers/firewire') 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); -- cgit v1.2.3 From 4ffb7a6a066e4be4577976d1c08e237c7479770a Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Thu, 10 Jun 2010 08:36:37 +0200 Subject: firewire: add CSR cmstr support Implement the cmstr bit, which is required for cycle master capable nodes and tested for by the Base 1394 Test Suite. This bit allows the bus master to disable cycle start packets; there are bus master implementations that actually do this. Signed-off-by: Clemens Ladisch --- drivers/firewire/core-transaction.c | 12 ++++++++++-- drivers/firewire/core.h | 2 ++ drivers/firewire/ohci.c | 35 +++++++++++++++++++++++++++++++++++ drivers/firewire/ohci.h | 1 + 4 files changed, 48 insertions(+), 2 deletions(-) (limited to 'drivers/firewire') diff --git a/drivers/firewire/core-transaction.c b/drivers/firewire/core-transaction.c index dd8ef650a7cb..e0c6cce894cf 100644 --- a/drivers/firewire/core-transaction.c +++ b/drivers/firewire/core-transaction.c @@ -1003,7 +1003,12 @@ static u32 read_state_register(struct fw_card *card) * reset, but then cleared when the units are ready again, which * happens immediately for us. */ - return 0; + u32 value = 0x0000; + + /* Bit 8 (cmstr): */ + value |= card->driver->read_csr_reg(card, CSR_STATE_CLEAR); + + return value; } static void update_split_timeout(struct fw_card *card) @@ -1034,6 +1039,8 @@ static void handle_registers(struct fw_card *card, struct fw_request *request, if (tcode == TCODE_READ_QUADLET_REQUEST) { *data = cpu_to_be32(read_state_register(card)); } else if (tcode == TCODE_WRITE_QUADLET_REQUEST) { + card->driver->write_csr_reg(card, CSR_STATE_CLEAR, + be32_to_cpu(*data)); } else { rcode = RCODE_TYPE_ERROR; } @@ -1043,7 +1050,8 @@ static void handle_registers(struct fw_card *card, struct fw_request *request, if (tcode == TCODE_READ_QUADLET_REQUEST) { *data = cpu_to_be32(read_state_register(card)); } else if (tcode == TCODE_WRITE_QUADLET_REQUEST) { - /* FIXME: implement cmstr */ + card->driver->write_csr_reg(card, CSR_STATE_SET, + be32_to_cpu(*data)); /* FIXME: implement abdicate */ } else { rcode = RCODE_TYPE_ERROR; diff --git a/drivers/firewire/core.h b/drivers/firewire/core.h index 3b8c0f042f49..aaecdd1c1767 100644 --- a/drivers/firewire/core.h +++ b/drivers/firewire/core.h @@ -40,6 +40,8 @@ struct fw_packet; #define FEATURE_PRIORITY_BUDGET 0x01 +#define CSR_STATE_BIT_CMSTR (1 << 8) + struct fw_card_driver { /* * Enable the given card with the given initial config rom. diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index 0e5413531785..1dcc2e427eb1 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -172,6 +172,7 @@ struct fw_ohci { unsigned quirks; unsigned int pri_req_max; u32 bus_time; + bool is_root; /* * Spinlock for accessing fw_ohci data. Never call out of @@ -1400,6 +1401,7 @@ static void bus_reset_tasklet(unsigned long data) unsigned long flags; void *free_rom = NULL; dma_addr_t free_rom_bus = 0; + bool is_new_root; reg = reg_read(ohci, OHCI1394_NodeID); if (!(reg & OHCI1394_NodeID_idValid)) { @@ -1413,6 +1415,12 @@ static void bus_reset_tasklet(unsigned long data) ohci->node_id = reg & (OHCI1394_NodeID_busNumber | OHCI1394_NodeID_nodeNumber); + is_new_root = (reg & OHCI1394_NodeID_root) != 0; + if (!(ohci->is_root && is_new_root)) + reg_write(ohci, OHCI1394_LinkControlSet, + OHCI1394_LinkControl_cycleMaster); + ohci->is_root = is_new_root; + reg = reg_read(ohci, OHCI1394_SelfIDCount); if (reg & OHCI1394_SelfIDCount_selfIDError) { fw_notify("inconsistent self IDs\n"); @@ -2013,6 +2021,16 @@ static u32 ohci_read_csr_reg(struct fw_card *card, int csr_offset) u32 value; 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; + else + return 0; + case CSR_NODE_IDS: return reg_read(ohci, OHCI1394_NodeID) << 16; @@ -2050,6 +2068,23 @@ static void ohci_write_csr_reg(struct fw_card *card, int csr_offset, u32 value) unsigned long flags; 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); + } + break; + + case CSR_STATE_SET: + if ((value & CSR_STATE_BIT_CMSTR) && ohci->is_root) { + reg_write(ohci, OHCI1394_LinkControlSet, + OHCI1394_LinkControl_cycleMaster); + flush_writes(ohci); + } + break; + case CSR_NODE_IDS: reg_write(ohci, OHCI1394_NodeID, value >> 16); flush_writes(ohci); diff --git a/drivers/firewire/ohci.h b/drivers/firewire/ohci.h index 3bc9a5d744eb..0e6c5a466908 100644 --- a/drivers/firewire/ohci.h +++ b/drivers/firewire/ohci.h @@ -60,6 +60,7 @@ #define OHCI1394_LinkControl_cycleSource (1 << 22) #define OHCI1394_NodeID 0x0E8 #define OHCI1394_NodeID_idValid 0x80000000 +#define OHCI1394_NodeID_root 0x40000000 #define OHCI1394_NodeID_nodeNumber 0x0000003f #define OHCI1394_NodeID_busNumber 0x0000ffc0 #define OHCI1394_PhyControl 0x0EC -- 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 + 4 files changed, 16 insertions(+), 3 deletions(-) (limited to 'drivers/firewire') 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 { /* -- cgit v1.2.3 From e91b2787d0a2e4719b016e8dec0afd2d5ab6c30f Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Thu, 10 Jun 2010 08:40:49 +0200 Subject: firewire: allocate broadcast channel in hardware On OHCI 1.1 controllers, let the hardware allocate the broadcast channel automatically. This removes a theoretical race condition directly after a bus reset where it could be possible to read the channel allocation register with channel 31 still being unallocated. Signed-off-by: Clemens Ladisch --- drivers/firewire/core-card.c | 16 +++++++++++----- drivers/firewire/core-topology.c | 3 ++- drivers/firewire/core.h | 1 + drivers/firewire/ohci.c | 18 ++++++++++++------ 4 files changed, 26 insertions(+), 12 deletions(-) (limited to 'drivers/firewire') diff --git a/drivers/firewire/core-card.c b/drivers/firewire/core-card.c index 7c4cf6cfa746..faf2eee473b9 100644 --- a/drivers/firewire/core-card.c +++ b/drivers/firewire/core-card.c @@ -208,13 +208,19 @@ static void allocate_broadcast_channel(struct fw_card *card, int generation) { int channel, bandwidth = 0; - fw_iso_resource_manage(card, generation, 1ULL << 31, &channel, - &bandwidth, true, card->bm_transaction_data); - if (channel == 31) { + if (!card->broadcast_channel_allocated) { + fw_iso_resource_manage(card, generation, 1ULL << 31, + &channel, &bandwidth, true, + card->bm_transaction_data); + if (channel != 31) { + fw_notify("failed to allocate broadcast channel\n"); + return; + } card->broadcast_channel_allocated = true; - device_for_each_child(card->device, (void *)(long)generation, - fw_device_set_broadcast_channel); } + + device_for_each_child(card->device, (void *)(long)generation, + fw_device_set_broadcast_channel); } static const char gap_count_table[] = { diff --git a/drivers/firewire/core-topology.c b/drivers/firewire/core-topology.c index ca3c65318165..00a556f3a585 100644 --- a/drivers/firewire/core-topology.c +++ b/drivers/firewire/core-topology.c @@ -543,7 +543,8 @@ void fw_core_handle_bus_reset(struct fw_card *card, int node_id, int generation, spin_lock_irqsave(&card->lock, flags); - card->broadcast_channel_allocated = false; + card->broadcast_channel_allocated = (card->driver->get_features(card) & + FEATURE_CHANNEL_31_ALLOCATED) != 0; card->node_id = node_id; /* * Update node_id before generation to prevent anybody from using diff --git a/drivers/firewire/core.h b/drivers/firewire/core.h index a9ace1f8dc3f..3f9e39b60bca 100644 --- a/drivers/firewire/core.h +++ b/drivers/firewire/core.h @@ -39,6 +39,7 @@ struct fw_packet; #define BROADCAST_CHANNEL_VALID (1 << 30) #define FEATURE_PRIORITY_BUDGET 0x01 +#define FEATURE_CHANNEL_31_ALLOCATED 0x02 #define CSR_STATE_BIT_CMSTR (1 << 8) #define CSR_STATE_BIT_ABDICATE (1 << 10) diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index 1dcc2e427eb1..51a55808d88a 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -171,6 +171,7 @@ struct fw_ohci { int request_generation; /* for timestamping incoming requests */ unsigned quirks; unsigned int pri_req_max; + unsigned int features; u32 bus_time; bool is_root; @@ -1694,7 +1695,7 @@ static int ohci_enable(struct fw_card *card, { struct fw_ohci *ohci = fw_ohci(card); struct pci_dev *dev = to_pci_dev(card->device); - u32 lps, seconds, irqs; + u32 lps, seconds, version, irqs; int i, ret; if (software_reset(ohci)) { @@ -1747,10 +1748,19 @@ static int ohci_enable(struct fw_card *card, reg_write(ohci, OHCI1394_IsochronousCycleTimer, seconds << 25); ohci->bus_time = seconds & ~0x3f; + version = reg_read(ohci, OHCI1394_Version) & 0x00ff00ff; + if (version >= OHCI_VERSION_1_1) { + reg_write(ohci, OHCI1394_InitialChannelsAvailableHi, + 0xfffffffe); + ohci->features |= FEATURE_CHANNEL_31_ALLOCATED; + } + /* Get implemented bits of the priority arbitration request counter. */ reg_write(ohci, OHCI1394_FairnessControl, 0x3f); ohci->pri_req_max = reg_read(ohci, OHCI1394_FairnessControl) & 0x3f; reg_write(ohci, OHCI1394_FairnessControl, 0); + if (ohci->pri_req_max != 0) + ohci->features |= FEATURE_PRIORITY_BUDGET; ar_context_run(&ohci->ar_request_ctx); ar_context_run(&ohci->ar_response_ctx); @@ -2124,12 +2134,8 @@ static void ohci_write_csr_reg(struct fw_card *card, int csr_offset, u32 value) static unsigned int ohci_get_features(struct fw_card *card) { struct fw_ohci *ohci = fw_ohci(card); - unsigned int features = 0; - - if (ohci->pri_req_max != 0) - features |= FEATURE_PRIORITY_BUDGET; - return features; + return ohci->features; } static void copy_iso_headers(struct iso_context *ctx, void *p) -- cgit v1.2.3 From c374ab424249b6ab91b1aee7460419d3f2c321df Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Thu, 10 Jun 2010 08:41:51 +0200 Subject: firewire: core: always enable cycle master packets As part of the bus manager responsibilities, make sure that the cycle master sends cycle start packets. This is needed when the old bus manager disabled the cycle master's cmstr bit and there are iso-capable nodes on the new bus. Signed-off-by: Clemens Ladisch --- drivers/firewire/core-card.c | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) (limited to 'drivers/firewire') diff --git a/drivers/firewire/core-card.c b/drivers/firewire/core-card.c index faf2eee473b9..ef6f2616cac7 100644 --- a/drivers/firewire/core-card.c +++ b/drivers/firewire/core-card.c @@ -369,10 +369,8 @@ static void fw_card_bm_work(struct work_struct *work) goto out; } else if (root_device_is_cmc) { /* - * FIXME: I suppose we should set the cmstr bit in the - * STATE_CLEAR register of this node, as described in - * 1394-1995, 8.4.2.6. Also, send out a force root - * packet for this node. + * We will send out a force root packet for this + * node as part of the gap count optimization. */ new_root_id = root_id; } else { @@ -413,11 +411,25 @@ static void fw_card_bm_work(struct work_struct *work) fw_send_phy_config(card, new_root_id, generation, gap_count); fw_core_initiate_bus_reset(card, 1); /* Will allocate broadcast channel after the reset. */ - } else { - if (local_id == irm_id) - allocate_broadcast_channel(card, generation); + goto out; } + if (root_device_is_cmc) { + /* + * Make sure that the cycle master sends cycle start packets. + */ + card->bm_transaction_data[0] = cpu_to_be32(CSR_STATE_BIT_CMSTR); + rcode = fw_run_transaction(card, TCODE_WRITE_QUADLET_REQUEST, + root_id, generation, SCODE_100, + CSR_REGISTER_BASE + CSR_STATE_SET, + card->bm_transaction_data, sizeof(u32)); + if (rcode == RCODE_GENERATION) + goto out; + } + + if (local_id == irm_id) + allocate_broadcast_channel(card, generation); + out: fw_node_put(root_node); out_put_card: -- cgit v1.2.3 From 65b2742ac002f554f6ca0c2eab84cc62400eafdf Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Sat, 12 Jun 2010 20:26:51 +0200 Subject: firewire: 'add CSR_... support' addendum Add a comment on which of the conflicting NODE_IDS specifications we implement. Reduce a comment on rather irrelevant register bits that can all be looked up in the spec (or from now on in the code history). Directly include the required indirectly included bug.h. Signed-off-by: Stefan Richter --- drivers/firewire/core-transaction.c | 27 ++++++--------------------- drivers/firewire/ohci.c | 1 + 2 files changed, 7 insertions(+), 21 deletions(-) (limited to 'drivers/firewire') diff --git a/drivers/firewire/core-transaction.c b/drivers/firewire/core-transaction.c index 85a54da243e2..5069cfc75b50 100644 --- a/drivers/firewire/core-transaction.c +++ b/drivers/firewire/core-transaction.c @@ -984,29 +984,10 @@ static const struct fw_address_region registers_region = static u32 read_state_register(struct fw_card *card) { - /* - * Fixed bits (IEEE 1394-2008 8.3.2.2.1): - * Bits 0-1 (state) always read 00=running. - * Bits 2,3 (off, atn) are not implemented as per the spec. - * Bit 4 (elog) is not implemented because there is no error log. - * Bit 6 (dreq) cannot be set. It is intended to "disable requests - * from unreliable nodes"; however, IEEE 1212 states that devices - * may "clear their own dreq bit when it has been improperly set". - * Our implementation might be seen as an improperly extensive - * interpretation of "improperly", but the 1212-2001 revision - * dropped this bit altogether, so we're in the clear. :o) - * Bit 7 (lost) always reads 0 because a power reset has never occurred - * during normal operation. - * Bit 9 (linkoff) is not implemented because the PC is not powered - * from the FireWire cable. - * Bit 15 (gone) always reads 0. It must be set at a power/command/bus - * reset, but then cleared when the units are ready again, which - * happens immediately for us. - */ - u32 value = 0x0000; + u32 value; /* Bit 8 (cmstr): */ - value |= card->driver->read_csr_reg(card, CSR_STATE_CLEAR); + value = card->driver->read_csr_reg(card, CSR_STATE_CLEAR); /* Bit 10 (abdicate): */ if (card->csr_abdicate) @@ -1066,6 +1047,10 @@ static void handle_registers(struct fw_card *card, struct fw_request *request, break; case CSR_NODE_IDS: + /* + * per IEEE 1394-2008 8.3.22.3, not IEEE 1394.1-2004 3.2.8 + * and 9.6, but interoperable with IEEE 1394.1-2004 bridges + */ if (tcode == TCODE_READ_QUADLET_REQUEST) *data = cpu_to_be32(card->driver-> read_csr_reg(card, CSR_NODE_IDS)); diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index 51a55808d88a..2abdb3268a10 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -18,6 +18,7 @@ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +#include #include #include #include -- cgit v1.2.3 From e847cc832bab50aad939a0c30414cd986637564d Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Sat, 12 Jun 2010 20:29:07 +0200 Subject: firewire: drop sizeof expressions from some request size arguments In case of fw_card_bm_work()'s lock request, the present sizeof expression is going to be wrong if somebody changes the fw_card's DMA scratch buffer's size in the future. In case of quadlet write requests, sizeof(u32) is just silly; it's 4. In case of SBP-2 ORB pointer write requests, 8 is arguably quicker to understand as the correct and only possible value than sizeof(some_datum). Signed-off-by: Stefan Richter --- drivers/firewire/core-card.c | 5 ++--- drivers/firewire/sbp2.c | 10 ++++------ 2 files changed, 6 insertions(+), 9 deletions(-) (limited to 'drivers/firewire') diff --git a/drivers/firewire/core-card.c b/drivers/firewire/core-card.c index ef6f2616cac7..156b098b0ada 100644 --- a/drivers/firewire/core-card.c +++ b/drivers/firewire/core-card.c @@ -296,8 +296,7 @@ static void fw_card_bm_work(struct work_struct *work) rcode = fw_run_transaction(card, TCODE_LOCK_COMPARE_SWAP, irm_id, generation, SCODE_100, CSR_REGISTER_BASE + CSR_BUS_MANAGER_ID, - card->bm_transaction_data, - sizeof(card->bm_transaction_data)); + card->bm_transaction_data, 8); if (rcode == RCODE_GENERATION) /* Another bus reset, BM work has been rescheduled. */ @@ -422,7 +421,7 @@ static void fw_card_bm_work(struct work_struct *work) rcode = fw_run_transaction(card, TCODE_WRITE_QUADLET_REQUEST, root_id, generation, SCODE_100, CSR_REGISTER_BASE + CSR_STATE_SET, - card->bm_transaction_data, sizeof(u32)); + card->bm_transaction_data, 4); if (rcode == RCODE_GENERATION) goto out; } diff --git a/drivers/firewire/sbp2.c b/drivers/firewire/sbp2.c index 7e33b0b1704c..ae715c82da2e 100644 --- a/drivers/firewire/sbp2.c +++ b/drivers/firewire/sbp2.c @@ -508,8 +508,7 @@ static void sbp2_send_orb(struct sbp2_orb *orb, struct sbp2_logical_unit *lu, fw_send_request(device->card, &orb->t, TCODE_WRITE_BLOCK_REQUEST, node_id, generation, device->max_speed, offset, - &orb->pointer, sizeof(orb->pointer), - complete_transaction, orb); + &orb->pointer, 8, complete_transaction, orb); } static int sbp2_cancel_orbs(struct sbp2_logical_unit *lu) @@ -654,7 +653,7 @@ static void sbp2_agent_reset(struct sbp2_logical_unit *lu) fw_run_transaction(device->card, TCODE_WRITE_QUADLET_REQUEST, lu->tgt->node_id, lu->generation, device->max_speed, lu->command_block_agent_address + SBP2_AGENT_RESET, - &d, sizeof(d)); + &d, 4); } static void complete_agent_reset_write_no_wait(struct fw_card *card, @@ -676,7 +675,7 @@ static void sbp2_agent_reset_no_wait(struct sbp2_logical_unit *lu) fw_send_request(device->card, t, TCODE_WRITE_QUADLET_REQUEST, lu->tgt->node_id, lu->generation, device->max_speed, lu->command_block_agent_address + SBP2_AGENT_RESET, - &d, sizeof(d), complete_agent_reset_write_no_wait, t); + &d, 4, complete_agent_reset_write_no_wait, t); } static inline void sbp2_allow_block(struct sbp2_logical_unit *lu) @@ -866,8 +865,7 @@ static void sbp2_set_busy_timeout(struct sbp2_logical_unit *lu) fw_run_transaction(device->card, TCODE_WRITE_QUADLET_REQUEST, lu->tgt->node_id, lu->generation, device->max_speed, - CSR_REGISTER_BASE + CSR_BUSY_TIMEOUT, - &d, sizeof(d)); + CSR_REGISTER_BASE + CSR_BUSY_TIMEOUT, &d, 4); } static void sbp2_reconnect(struct work_struct *work); -- cgit v1.2.3 From 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 ++------------ 4 files changed, 4 insertions(+), 21 deletions(-) (limited to 'drivers/firewire') 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, -- 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 ++++++++++++----- 4 files changed, 29 insertions(+), 38 deletions(-) (limited to 'drivers/firewire') 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: -- cgit v1.2.3 From b384cf18873da1ed100662aa7373edf5883a1c24 Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Sat, 12 Jun 2010 20:35:21 +0200 Subject: firewire: core: combine some repeated code All of these CSRs have the same read/ write/ aynthing-else handling, except for CSR_PRIORITY_BUDGET which might not be implemented. The CSR_CYCLE_TIME read handler implementation accepted 4-byte-sized block write requests before this change but this is just silly; the register is only required to support quadlet read and write requests like the other r/w CSR core and Serial-Bus-dependent registers. Signed-off-by: Stefan Richter --- drivers/firewire/core-transaction.c | 84 +++++++------------------------------ 1 file changed, 15 insertions(+), 69 deletions(-) (limited to 'drivers/firewire') diff --git a/drivers/firewire/core-transaction.c b/drivers/firewire/core-transaction.c index 87d69cddb231..b8f6db6a0494 100644 --- a/drivers/firewire/core-transaction.c +++ b/drivers/firewire/core-transaction.c @@ -1006,38 +1006,30 @@ static void handle_registers(struct fw_card *card, struct fw_request *request, unsigned long flags; switch (reg) { - case CSR_STATE_CLEAR: - 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)); - else - rcode = RCODE_TYPE_ERROR; - break; - - case CSR_STATE_SET: - 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)); - else - rcode = RCODE_TYPE_ERROR; - break; + case CSR_PRIORITY_BUDGET: + if (!card->priority_budget_implemented) { + rcode = RCODE_ADDRESS_ERROR; + break; + } + /* else fall through */ case CSR_NODE_IDS: /* * per IEEE 1394-2008 8.3.22.3, not IEEE 1394.1-2004 3.2.8 * and 9.6, but interoperable with IEEE 1394.1-2004 bridges */ + /* fall through */ + + case CSR_STATE_CLEAR: + case CSR_STATE_SET: + case CSR_CYCLE_TIME: + case CSR_BUS_TIME: + case CSR_BUSY_TIMEOUT: if (tcode == TCODE_READ_QUADLET_REQUEST) *data = cpu_to_be32(card->driver-> - read_csr_reg(card, CSR_NODE_IDS)); + read_csr_reg(card, reg)); else if (tcode == TCODE_WRITE_QUADLET_REQUEST) - card->driver->write_csr_reg(card, CSR_NODE_IDS, + card->driver->write_csr_reg(card, reg, be32_to_cpu(*data)); else rcode = RCODE_TYPE_ERROR; @@ -1078,52 +1070,6 @@ static void handle_registers(struct fw_card *card, struct fw_request *request, } break; - case CSR_CYCLE_TIME: - if (TCODE_IS_READ_REQUEST(tcode) && length == 4) - *data = cpu_to_be32(card->driver-> - read_csr_reg(card, CSR_CYCLE_TIME)); - else if (tcode == TCODE_WRITE_QUADLET_REQUEST) - card->driver->write_csr_reg(card, CSR_CYCLE_TIME, - be32_to_cpu(*data)); - else - rcode = RCODE_TYPE_ERROR; - break; - - case CSR_BUS_TIME: - if (tcode == TCODE_READ_QUADLET_REQUEST) - *data = cpu_to_be32(card->driver-> - read_csr_reg(card, CSR_BUS_TIME)); - else if (tcode == TCODE_WRITE_QUADLET_REQUEST) - card->driver->write_csr_reg(card, CSR_BUS_TIME, - be32_to_cpu(*data)); - else - rcode = RCODE_TYPE_ERROR; - break; - - case CSR_BUSY_TIMEOUT: - if (tcode == TCODE_READ_QUADLET_REQUEST) - *data = cpu_to_be32(card->driver-> - read_csr_reg(card, CSR_BUSY_TIMEOUT)); - else if (tcode == TCODE_WRITE_QUADLET_REQUEST) - card->driver->write_csr_reg(card, CSR_BUSY_TIMEOUT, - be32_to_cpu(*data)); - else - rcode = RCODE_TYPE_ERROR; - break; - - case CSR_PRIORITY_BUDGET: - if (!card->priority_budget_implemented) - 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_MAINT_UTILITY: if (tcode == TCODE_READ_QUADLET_REQUEST) *data = card->maint_utility_register; -- cgit v1.2.3 From 0fcff4e39323f466a47684d7c8ffa77e1be86c8a Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Sat, 12 Jun 2010 20:35:52 +0200 Subject: firewire: rename CSR access driver methods Rather than "read a Control and Status Registers (CSR) Architecture register" I prefer to say "read a Control and Status Register". Signed-off-by: Stefan Richter --- drivers/firewire/core-cdev.c | 2 +- drivers/firewire/core-transaction.c | 10 ++++------ drivers/firewire/core.h | 4 ++-- drivers/firewire/ohci.c | 8 ++++---- 4 files changed, 11 insertions(+), 13 deletions(-) (limited to 'drivers/firewire') diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index 2e62516a4b15..32a33da64991 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -1044,7 +1044,7 @@ static int ioctl_get_cycle_timer2(struct client *client, union ioctl_arg *arg) local_irq_disable(); - cycle_time = card->driver->read_csr_reg(card, CSR_CYCLE_TIME); + cycle_time = card->driver->read_csr(card, CSR_CYCLE_TIME); switch (a->clk_id) { case CLOCK_REALTIME: getnstimeofday(&ts); break; diff --git a/drivers/firewire/core-transaction.c b/drivers/firewire/core-transaction.c index b8f6db6a0494..182da69eae44 100644 --- a/drivers/firewire/core-transaction.c +++ b/drivers/firewire/core-transaction.c @@ -1026,19 +1026,17 @@ static void handle_registers(struct fw_card *card, struct fw_request *request, case CSR_BUS_TIME: case CSR_BUSY_TIMEOUT: if (tcode == TCODE_READ_QUADLET_REQUEST) - *data = cpu_to_be32(card->driver-> - read_csr_reg(card, reg)); + *data = cpu_to_be32(card->driver->read_csr(card, reg)); else if (tcode == TCODE_WRITE_QUADLET_REQUEST) - card->driver->write_csr_reg(card, reg, - be32_to_cpu(*data)); + card->driver->write_csr(card, reg, be32_to_cpu(*data)); else rcode = RCODE_TYPE_ERROR; break; case CSR_RESET_START: if (tcode == TCODE_WRITE_QUADLET_REQUEST) - card->driver->write_csr_reg(card, CSR_STATE_CLEAR, - CSR_STATE_BIT_ABDICATE); + card->driver->write_csr(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 8280c625170b..3000dd74acfd 100644 --- a/drivers/firewire/core.h +++ b/drivers/firewire/core.h @@ -78,8 +78,8 @@ struct fw_card_driver { int (*enable_phys_dma)(struct fw_card *card, int node_id, int generation); - u32 (*read_csr_reg)(struct fw_card *card, int csr_offset); - void (*write_csr_reg)(struct fw_card *card, int csr_offset, u32 value); + u32 (*read_csr)(struct fw_card *card, int csr_offset); + void (*write_csr)(struct fw_card *card, int csr_offset, u32 value); struct fw_iso_context * (*allocate_iso_context)(struct fw_card *card, diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index a55cf0911b72..a4bbf3dadf58 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -2026,7 +2026,7 @@ static int ohci_enable_phys_dma(struct fw_card *card, #endif /* CONFIG_FIREWIRE_OHCI_REMOTE_DMA */ } -static u32 ohci_read_csr_reg(struct fw_card *card, int csr_offset) +static u32 ohci_read_csr(struct fw_card *card, int csr_offset) { struct fw_ohci *ohci = fw_ohci(card); unsigned long flags; @@ -2077,7 +2077,7 @@ static u32 ohci_read_csr_reg(struct fw_card *card, int csr_offset) } } -static void ohci_write_csr_reg(struct fw_card *card, int csr_offset, u32 value) +static void ohci_write_csr(struct fw_card *card, int csr_offset, u32 value) { struct fw_ohci *ohci = fw_ohci(card); unsigned long flags; @@ -2576,8 +2576,8 @@ static const struct fw_card_driver ohci_driver = { .send_response = ohci_send_response, .cancel_packet = ohci_cancel_packet, .enable_phys_dma = ohci_enable_phys_dma, - .read_csr_reg = ohci_read_csr_reg, - .write_csr_reg = ohci_write_csr_reg, + .read_csr = ohci_read_csr, + .write_csr = ohci_write_csr, .allocate_iso_context = ohci_allocate_iso_context, .free_iso_context = ohci_free_iso_context, -- cgit v1.2.3 From ae86e81e434072be28ff4e9c1be3cc2562be8749 Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Tue, 15 Jun 2010 01:22:45 +0200 Subject: firewire: core: remove unused variable which caused gcc 4.6 to warn about variable 'destination' set but not used. Since the hardware ensures that we receive only response packets with proper destination node ID (in a given bus generation), we have no use for destination here in the core as well as in upper layers. (This is different with request packets. There we pass destination node ID to upper layers because they may for example need to check whether this was an unicast or broadcast request.) Reported-and-Tested-By: Justin P. Mattock Signed-off-by: Stefan Richter --- drivers/firewire/core-transaction.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'drivers/firewire') diff --git a/drivers/firewire/core-transaction.c b/drivers/firewire/core-transaction.c index 182da69eae44..cb6390fe3686 100644 --- a/drivers/firewire/core-transaction.c +++ b/drivers/firewire/core-transaction.c @@ -881,13 +881,12 @@ void fw_core_handle_response(struct fw_card *card, struct fw_packet *p) unsigned long flags; u32 *data; size_t data_length; - int tcode, tlabel, destination, source, rcode; + int tcode, tlabel, source, rcode; - tcode = HEADER_GET_TCODE(p->header[0]); - tlabel = HEADER_GET_TLABEL(p->header[0]); - destination = HEADER_GET_DESTINATION(p->header[0]); - source = HEADER_GET_SOURCE(p->header[1]); - rcode = HEADER_GET_RCODE(p->header[1]); + tcode = HEADER_GET_TCODE(p->header[0]); + tlabel = HEADER_GET_TLABEL(p->header[0]); + source = HEADER_GET_SOURCE(p->header[1]); + rcode = HEADER_GET_RCODE(p->header[1]); spin_lock_irqsave(&card->lock, flags); list_for_each_entry(t, &card->transaction_list, link) { -- cgit v1.2.3 From 56d04cb189f955e5167c27944d61aa57ad69b598 Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Tue, 8 Jun 2010 00:20:10 +0200 Subject: firewire: core: remove an unnecessary zero initialization All of the fields of the iso_interrupt_event instance are overwritten right after it was allocated. Signed-off-by: Stefan Richter --- drivers/firewire/core-cdev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/firewire') diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index 32a33da64991..ca72cdaa68c9 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -847,7 +847,7 @@ static void iso_callback(struct fw_iso_context *context, u32 cycle, struct client *client = data; struct iso_interrupt_event *e; - e = kzalloc(sizeof(*e) + header_length, GFP_ATOMIC); + e = kmalloc(sizeof(*e) + header_length, GFP_ATOMIC); if (e == NULL) return; -- 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 +-- 4 files changed, 11 insertions(+), 13 deletions(-) (limited to 'drivers/firewire') 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; -- cgit v1.2.3 From bdfe273ee54b29498851fc8058516037d284270c Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Mon, 14 Jun 2010 11:46:25 +0200 Subject: firewire: cdev: fix race in iso context creation Protect the client's iso context pointer against a race that can happen when more than one creation call is executed at the same time. Signed-off-by: Clemens Ladisch Signed-off-by: Stefan Richter --- drivers/firewire/core-cdev.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'drivers/firewire') diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index 4e0478d70d4d..ce8cb6fcbbcd 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -864,10 +864,6 @@ 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; - /* We only support one context at this time. */ - if (client->iso_context != NULL) - return -EBUSY; - if (a->channel > 63) return -EINVAL; @@ -892,10 +888,17 @@ static int ioctl_create_iso_context(struct client *client, union ioctl_arg *arg) if (IS_ERR(context)) return PTR_ERR(context); + /* We only support one context at this time. */ + spin_lock_irq(&client->lock); + if (client->iso_context != NULL) { + spin_unlock_irq(&client->lock); + fw_iso_context_destroy(context); + return -EBUSY; + } client->iso_closure = a->closure; client->iso_context = context; + spin_unlock_irq(&client->lock); - /* We only support one context at this time. */ a->handle = 0; return 0; -- cgit v1.2.3 From 08bd34c98d631fe85744d4c920c80f48a1d95f54 Mon Sep 17 00:00:00 2001 From: Jay Fenlason Date: Tue, 18 May 2010 14:02:45 -0400 Subject: firewire: cdev: fix responses to nodes at different card My box has two firewire cards in it: card0 and card1. My application opens /dev/fw0 (card 0) and allocates an address space. The core makes the address space available on both cards. Along comes the remote device, which sends a READ_QUADLET_REQUEST to card1. The request gets passed up to my application, which calls ioctl_send_response(). ioctl_send_response() then calls fw_send_response() with card0, because that's the card it's bound to. Card0's driver drops the response, because it isn't part of a transaction that it has outstanding. So in core-cdev: handle_request(), we need to stash the card of the inbound request in the struct inbound_transaction_resource and use that card to send the response to. The hard part will be refcounting the card correctly so it can't get deallocated while we hold a pointer to it. Here's a trivial patch, which does not do the card refcounting, but at least demonstrates what the problem is. Note that we can't depend on the fact that the core-cdev:client structure holds a card open, because in this case the card it holds open is not the card the request came in on. ..and there's no way for the core to tell cdev "this card is gone, kill any inbound transactions on it", while cdev holds the transaction open until userspace issues a SEND_RESPONSE ioctl, which may be a very, very long time. But when it does, it calls fw_send_response(), which will dereference the card... So how unhappy are we about userspace potentially holding a fw_card open forever? Signed-off-by: Jay Fenlason Reference counting to be addressed in a separate change. Signed-off-by: Stefan Richter (whitespace) --- drivers/firewire/core-cdev.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers/firewire') diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index ce8cb6fcbbcd..8cbc2b8a8272 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -106,6 +106,7 @@ struct outbound_transaction_resource { struct inbound_transaction_resource { struct client_resource resource; + struct fw_card *card; struct fw_request *request; void *data; size_t length; @@ -625,8 +626,7 @@ static void release_request(struct client *client, if (is_fcp_request(r->request)) kfree(r->data); else - fw_send_response(client->device->card, r->request, - RCODE_CONFLICT_ERROR); + fw_send_response(r->card, r->request, RCODE_CONFLICT_ERROR); kfree(r); } @@ -646,6 +646,7 @@ static void handle_request(struct fw_card *card, struct fw_request *request, if (r == NULL || e == NULL) goto failed; + r->card = card; r->request = request; r->data = payload; r->length = length; @@ -765,7 +766,7 @@ static int ioctl_send_response(struct client *client, union ioctl_arg *arg) kfree(r->request); goto out; } - fw_send_response(client->device->card, r->request, a->rcode); + fw_send_response(r->card, r->request, a->rcode); out: kfree(r); -- cgit v1.2.3 From 0244f57302f7e8bebd2f1ab58767eac2e9f678a6 Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Sun, 20 Jun 2010 22:52:27 +0200 Subject: firewire: cdev: count references of cards during inbound transactions If a request comes in to an address range managed by a userspace driver i.e. client, the card instance of request and response may differ from the card instance of the client device. Therefore we need to take a reference of the card until the response was sent. I thought about putting the reference counting into core-transaction.c, but the various high-level drivers besides cdev clients (firewire-net, firewire-sbp2, firedtv) use the card pointer in their fw_address_handler address_callback method only to look up devices of which they already hold the necessary references. So this seems to be a specific firewire-cdev issue which is better addressed locally. We do not need the reference - in case of FCP_REQUEST or FCP_RESPONSE requests because then the firewire-core will send the split transaction response for us already in the context of the request handler, - if it is the same card as the client device's because we hold a card reference indirectly via teh client->device reference. To keep things simple, we take the reference nevertheless. Jay Fenlason wrote: > there's no way for the core to tell cdev "this card is gone, > kill any inbound transactions on it", while cdev holds the transaction > open until userspace issues a SEND_RESPONSE ioctl, which may be a very, > very long time. But when it does, it calls fw_send_response(), which > will dereference the card... > > So how unhappy are we about userspace potentially holding a fw_card > open forever? While termination of inbound transcations at card removal could be implemented, it is IMO not worth the effort. Currently, the effect of holding a reference of a card that has been removed is to block the process that called the pci_remove of the card. This is - either a user process ran by root. Root can find and kill processes that have /dev/fw* open, if desired. - a kernel thread (which one?) in case of hot removal of a PCCard or ExpressCard. The latter case could be a problem indeed. firewire-core's card shutdown and card release should probably be improved not to block in shutdown, just to defer freeing of memory until release. This is not a new problem though; the same already always happens with the client->device->card without the need of inbound transactions or other special conditions involved, other than the client not closing the file. Signed-off-by: Stefan Richter --- drivers/firewire/core-cdev.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers/firewire') diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index 8cbc2b8a8272..8f8c8eeaf046 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -627,6 +627,8 @@ static void release_request(struct client *client, kfree(r->data); else fw_send_response(r->card, r->request, RCODE_CONFLICT_ERROR); + + fw_card_put(r->card); kfree(r); } @@ -641,6 +643,9 @@ static void handle_request(struct fw_card *card, struct fw_request *request, void *fcp_frame = NULL; int ret; + /* card may be different from handler->client->device->card */ + fw_card_get(card); + r = kmalloc(sizeof(*r), GFP_ATOMIC); e = kmalloc(sizeof(*e), GFP_ATOMIC); if (r == NULL || e == NULL) @@ -686,6 +691,8 @@ static void handle_request(struct fw_card *card, struct fw_request *request, if (!is_fcp_request(request)) fw_send_response(card, request, RCODE_CONFLICT_ERROR); + + fw_card_put(card); } static void release_address_handler(struct client *client, @@ -768,6 +775,7 @@ static int ioctl_send_response(struct client *client, union ioctl_arg *arg) } fw_send_response(r->card, r->request, a->rcode); out: + fw_card_put(r->card); kfree(r); return ret; -- 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 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers/firewire') 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); -- cgit v1.2.3 From c82f91f2663e79b150afd896ec72e798ba4e243d Mon Sep 17 00:00:00 2001 From: Jay Fenlason Date: Tue, 18 May 2010 10:57:33 -0400 Subject: firewire: expose extended tcode of incoming lock requests to (userspace) drivers When a remote device does a LOCK_REQUEST, the core does not pass the extended tcode to userspace. This patch makes it use the juju-specific tcodes listed in firewire-constants.h for incoming requests. Signed-off-by: Jay Fenlason This matches how tcode in the API for outbound requests is treated. Affects kernelspace and userspace drivers alike, but at the moment there are no kernespace drivers that receive lock requests. Split out from a combo patch, slightly reordered, changelog reworded. Signed-off-by: Stefan Richter --- drivers/firewire/core-transaction.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/firewire') diff --git a/drivers/firewire/core-transaction.c b/drivers/firewire/core-transaction.c index 2f67c8d5ce91..fcb455c08200 100644 --- a/drivers/firewire/core-transaction.c +++ b/drivers/firewire/core-transaction.c @@ -780,9 +780,11 @@ static void handle_exclusive_region_request(struct fw_card *card, unsigned long flags; int tcode, destination, source; - tcode = HEADER_GET_TCODE(p->header[0]); destination = HEADER_GET_DESTINATION(p->header[0]); source = HEADER_GET_SOURCE(p->header[1]); + tcode = HEADER_GET_TCODE(p->header[0]); + if (tcode == TCODE_LOCK_REQUEST) + tcode = 0x10 + HEADER_GET_EXTENDED_TCODE(p->header[3]); spin_lock_irqsave(&address_handler_lock, flags); handler = lookup_enclosing_address_handler(&address_handler_list, -- 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 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 36 insertions(+), 9 deletions(-) (limited to 'drivers/firewire') 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: -- cgit v1.2.3 From ae948011071c12ff6a328348859c717ea885ed40 Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Mon, 21 Jun 2010 23:23:52 +0200 Subject: firewire: core: no need to track irq flags in bm_work This is a workqueue job and always entered with IRQs enabled. Signed-off-by: Stefan Richter --- drivers/firewire/core-card.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) (limited to 'drivers/firewire') diff --git a/drivers/firewire/core-card.c b/drivers/firewire/core-card.c index 156b098b0ada..11fc81500f82 100644 --- a/drivers/firewire/core-card.c +++ b/drivers/firewire/core-card.c @@ -239,17 +239,16 @@ static void fw_card_bm_work(struct work_struct *work) struct fw_card *card = container_of(work, struct fw_card, work.work); struct fw_device *root_device; struct fw_node *root_node; - unsigned long flags; int root_id, new_root_id, irm_id, local_id; int gap_count, generation, grace, rcode; bool do_reset = false; bool root_device_is_running; bool root_device_is_cmc; - spin_lock_irqsave(&card->lock, flags); + spin_lock_irq(&card->lock); if (card->local_node == NULL) { - spin_unlock_irqrestore(&card->lock, flags); + spin_unlock_irq(&card->lock); goto out_put_card; } @@ -291,7 +290,7 @@ static void fw_card_bm_work(struct work_struct *work) card->bm_transaction_data[0] = cpu_to_be32(0x3f); card->bm_transaction_data[1] = cpu_to_be32(local_id); - spin_unlock_irqrestore(&card->lock, flags); + spin_unlock_irq(&card->lock); rcode = fw_run_transaction(card, TCODE_LOCK_COMPARE_SWAP, irm_id, generation, SCODE_100, @@ -322,7 +321,7 @@ static void fw_card_bm_work(struct work_struct *work) goto out; } - spin_lock_irqsave(&card->lock, flags); + spin_lock_irq(&card->lock); if (rcode != RCODE_COMPLETE) { /* @@ -341,7 +340,7 @@ static void fw_card_bm_work(struct work_struct *work) * We weren't BM in the last generation, and the last * bus reset is less than 125ms ago. Reschedule this job. */ - spin_unlock_irqrestore(&card->lock, flags); + spin_unlock_irq(&card->lock); fw_schedule_bm_work(card, DIV_ROUND_UP(HZ, 8)); goto out; } @@ -364,7 +363,7 @@ static void fw_card_bm_work(struct work_struct *work) * If we haven't probed this device yet, bail out now * and let's try again once that's done. */ - spin_unlock_irqrestore(&card->lock, flags); + spin_unlock_irq(&card->lock); goto out; } else if (root_device_is_cmc) { /* @@ -402,7 +401,7 @@ static void fw_card_bm_work(struct work_struct *work) (card->gap_count != gap_count || new_root_id != root_id)) do_reset = true; - spin_unlock_irqrestore(&card->lock, flags); + spin_unlock_irq(&card->lock); if (do_reset) { fw_notify("phy config: card %d, new root=%x, gap_count=%d\n", -- cgit v1.2.3 From 250b2b6dd421c9f8844a867d2ac06e0661e0ad93 Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Mon, 21 Jun 2010 23:24:35 +0200 Subject: firewire: cdev: fix fw_cdev_event_bus_reset.bm_node_id Fix an obscure ABI feature that is a bit of a hassle to implement. However, somebody put it into the ABI, so let's fill in a sensible value there. Signed-off-by: Stefan Richter --- drivers/firewire/core-card.c | 12 +++++++++--- drivers/firewire/core-cdev.c | 2 +- drivers/firewire/core-topology.c | 1 + 3 files changed, 11 insertions(+), 4 deletions(-) (limited to 'drivers/firewire') 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); -- cgit v1.2.3 From a8e93f3dccc066cd6dd1e9db1e35942914fc57d1 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Wed, 7 Jul 2010 14:37:30 +0200 Subject: firewire: cdev: check write quadlet request length to avoid buffer overflow Check that the data length of a write quadlet request actually is large enough for a quadlet. Otherwise, fw_fill_request could access the four bytes after the end of the outbound_transaction_event structure. Signed-off-by: Clemens Ladisch Modification of Clemens' change: Consolidate the check into init_request() which is used by the affected ioctl_send_request() and ioctl_send_broadcast_request() and the unaffected ioctl_send_stream_packet(), to save a few lines of code. Note, since struct outbound_transaction_event *e is slab-allocated, such an out-of-bounds access won't hit unallocated memory but may result in a (virtually impossible to exploit) information disclosure. Signed-off-by: Stefan Richter --- drivers/firewire/core-cdev.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/firewire') diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index d8ac0ce2d6bf..f7559bfeaba3 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -563,6 +563,10 @@ static int init_request(struct client *client, (request->length > 4096 || request->length > 512 << speed)) return -EIO; + if (request->tcode == TCODE_WRITE_QUADLET_REQUEST && + request->length < 4) + return -EINVAL; + e = kmalloc(sizeof(*e) + request->length, GFP_KERNEL); if (e == NULL) return -ENOMEM; -- cgit v1.2.3 From 656b7afd40a9f2b0d6cf8ef1972681961b428558 Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Wed, 7 Jul 2010 13:26:18 +0200 Subject: firewire: core: fix fw_send_request kerneldoc comment The present inline documentation of the fw_send_request() in-kernel API refers to userland code that is not applicable to kernel drivers at all. Reported-by: Ben Gamari While we are at fixing the whole documentation of fw_send_request(), also improve the rest of firewire-core's kerneldoc comments: - Add a bit of text concerning fw_run_transaction()'s call parameters. - Append () to function names and tab-align parameter descriptions as suggested by the example in Documentation/kernel-doc-nano-HOWTO.txt. - Remove kerneldoc markers from comments on static functions. - Remove outdated parameter descriptions at build_tree(). Signed-off-by: Stefan Richter --- drivers/firewire/core-device.c | 10 ++--- drivers/firewire/core-iso.c | 2 +- drivers/firewire/core-topology.c | 16 +++---- drivers/firewire/core-transaction.c | 84 ++++++++++++++++++++----------------- 4 files changed, 57 insertions(+), 55 deletions(-) (limited to 'drivers/firewire') diff --git a/drivers/firewire/core-device.c b/drivers/firewire/core-device.c index 92b633d643f2..781fd7374ae9 100644 --- a/drivers/firewire/core-device.c +++ b/drivers/firewire/core-device.c @@ -107,11 +107,11 @@ static int textual_leaf_to_string(const u32 *block, char *buf, size_t size) } /** - * fw_csr_string - reads a string from the configuration ROM - * @directory: e.g. root directory or unit directory - * @key: the key of the preceding directory entry - * @buf: where to put the string - * @size: size of @buf, in bytes + * fw_csr_string() - reads a string from the configuration ROM + * @directory: e.g. root directory or unit directory + * @key: the key of the preceding directory entry + * @buf: where to put the string + * @size: size of @buf, in bytes * * The string is taken from a minimal ASCII text descriptor leaf after * the immediate entry with @key. The string is zero-terminated. diff --git a/drivers/firewire/core-iso.c b/drivers/firewire/core-iso.c index 99c20f1b613a..4fe932e60fb8 100644 --- a/drivers/firewire/core-iso.c +++ b/drivers/firewire/core-iso.c @@ -272,7 +272,7 @@ static void deallocate_channel(struct fw_card *card, int irm_id, } /** - * fw_iso_resource_manage - Allocate or deallocate a channel and/or bandwidth + * fw_iso_resource_manage() - Allocate or deallocate a channel and/or bandwidth * * In parameters: card, generation, channels_mask, bandwidth, allocate * Out parameters: channel, bandwidth diff --git a/drivers/firewire/core-topology.c b/drivers/firewire/core-topology.c index 88d5133ae702..09be1a635505 100644 --- a/drivers/firewire/core-topology.c +++ b/drivers/firewire/core-topology.c @@ -174,12 +174,7 @@ static inline struct fw_node *fw_node(struct list_head *l) return list_entry(l, struct fw_node, link); } -/** - * build_tree - Build the tree representation of the topology - * @self_ids: array of self IDs to create the tree from - * @self_id_count: the length of the self_ids array - * @local_id: the node ID of the local node - * +/* * This function builds the tree representation of the topology given * by the self IDs from the latest bus reset. During the construction * of the tree, the function checks that the self IDs are valid and @@ -420,11 +415,10 @@ static void move_tree(struct fw_node *node0, struct fw_node *node1, int port) } } -/** - * update_tree - compare the old topology tree for card with the new - * one specified by root. Queue the nodes and mark them as either - * found, lost or updated. Update the nodes in the card topology tree - * as we go. +/* + * Compare the old topology tree for card with the new one specified by root. + * Queue the nodes and mark them as either found, lost or updated. + * Update the nodes in the card topology tree as we go. */ static void update_tree(struct fw_card *card, struct fw_node *root) { diff --git a/drivers/firewire/core-transaction.c b/drivers/firewire/core-transaction.c index fcb455c08200..7813da8a1293 100644 --- a/drivers/firewire/core-transaction.c +++ b/drivers/firewire/core-transaction.c @@ -273,43 +273,49 @@ static int allocate_tlabel(struct fw_card *card) } /** - * This function provides low-level access to the IEEE1394 transaction - * logic. Most C programs would use either fw_read(), fw_write() or - * fw_lock() instead - those function are convenience wrappers for - * this function. The fw_send_request() function is primarily - * provided as a flexible, one-stop entry point for languages bindings - * and protocol bindings. + * fw_send_request() - submit a request packet for transmission + * @card: interface to send the request at + * @t: transaction instance to which the request belongs + * @tcode: transaction code + * @destination_id: destination node ID, consisting of bus_ID and phy_ID + * @generation: bus generation in which request and response are valid + * @speed: transmission speed + * @offset: 48bit wide offset into destination's address space + * @payload: data payload for the request subaction + * @length: length of the payload, in bytes + * @callback: function to be called when the transaction is completed + * @callback_data: data to be passed to the transaction completion callback * - * FIXME: Document this function further, in particular the possible - * values for rcode in the callback. In short, we map ACK_COMPLETE to - * RCODE_COMPLETE, internal errors set errno and set rcode to - * RCODE_SEND_ERROR (which is out of range for standard ieee1394 - * rcodes). All other rcodes are forwarded unchanged. For all - * errors, payload is NULL, length is 0. + * Submit a request packet into the asynchronous request transmission queue. + * Can be called from atomic context. If you prefer a blocking API, use + * fw_run_transaction() in a context that can sleep. * - * Can not expect the callback to be called before the function - * returns, though this does happen in some cases (ACK_COMPLETE and - * errors). + * In case of lock requests, specify one of the firewire-core specific %TCODE_ + * constants instead of %TCODE_LOCK_REQUEST in @tcode. * - * The payload is only used for write requests and must not be freed - * until the callback has been called. + * Make sure that the value in @destination_id is not older than the one in + * @generation. Otherwise the request is in danger to be sent to a wrong node. * - * @param card the card from which to send the request - * @param tcode the tcode for this transaction. Do not use - * TCODE_LOCK_REQUEST directly, instead use TCODE_LOCK_MASK_SWAP - * etc. to specify tcode and ext_tcode. - * @param node_id the destination node ID (bus ID and PHY ID concatenated) - * @param generation the generation for which node_id is valid - * @param speed the speed to use for sending the request - * @param offset the 48 bit offset on the destination node - * @param payload the data payload for the request subaction - * @param length the length in bytes of the data to read - * @param callback function to be called when the transaction is completed - * @param callback_data pointer to arbitrary data, which will be - * passed to the callback - * - * In case of asynchronous stream packets i.e. TCODE_STREAM_DATA, the caller + * In case of asynchronous stream packets i.e. %TCODE_STREAM_DATA, the caller * needs to synthesize @destination_id with fw_stream_packet_destination_id(). + * It will contain tag, channel, and sy data instead of a node ID then. + * + * The payload buffer at @data is going to be DMA-mapped except in case of + * quadlet-sized payload or of local (loopback) requests. Hence make sure that + * the buffer complies with the restrictions for DMA-mapped memory. The + * @payload must not be freed before the @callback is called. + * + * In case of request types without payload, @data is NULL and @length is 0. + * + * After the transaction is completed successfully or unsuccessfully, the + * @callback will be called. Among its parameters is the response code which + * is either one of the rcodes per IEEE 1394 or, in case of internal errors, + * the firewire-core specific %RCODE_SEND_ERROR. + * + * Note some timing corner cases: fw_send_request() may complete much earlier + * than when the request packet actually hits the wire. On the other hand, + * transaction completion and hence execution of @callback may happen even + * before fw_send_request() returns. */ void fw_send_request(struct fw_card *card, struct fw_transaction *t, int tcode, int destination_id, int generation, int speed, @@ -375,9 +381,11 @@ static void transaction_callback(struct fw_card *card, int rcode, } /** - * fw_run_transaction - send request and sleep until transaction is completed + * fw_run_transaction() - send request and sleep until transaction is completed * - * Returns the RCODE. + * Returns the RCODE. See fw_send_request() for parameter documentation. + * Unlike fw_send_request(), @data points to the payload of the request or/and + * to the payload of the response. */ int fw_run_transaction(struct fw_card *card, int tcode, int destination_id, int generation, int speed, unsigned long long offset, @@ -495,9 +503,9 @@ static bool is_in_fcp_region(u64 offset, size_t length) } /** - * fw_core_add_address_handler - register for incoming requests - * @handler: callback - * @region: region in the IEEE 1212 node space address range + * fw_core_add_address_handler() - register for incoming requests + * @handler: callback + * @region: region in the IEEE 1212 node space address range * * region->start, ->end, and handler->length have to be quadlet-aligned. * @@ -552,7 +560,7 @@ int fw_core_add_address_handler(struct fw_address_handler *handler, EXPORT_SYMBOL(fw_core_add_address_handler); /** - * fw_core_remove_address_handler - unregister an address handler + * fw_core_remove_address_handler() - unregister an address handler */ void fw_core_remove_address_handler(struct fw_address_handler *handler) { -- cgit v1.2.3 From eb5b35a560510efc6bb62f05c3c82e9596cdfafe Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Wed, 7 Jul 2010 14:13:14 +0200 Subject: firewire: core: ensure some userspace API constants match corresponding kernel API constants The FW_ISO_ constants of the in-kernel API of firewire-core and FW_CDEV_ISO_ constants of the userspace API of firewire-core have nothing to do with each other --- except that the core-cdev.c implementation relies on them having the same values. Hence put some compile-time assertions into core-cdev.c. It's lame but I prefer it over including the userspace API header into the kernelspace API header and defining kernelspace API constants from userspace API constants. Nor do I want to expose the kernelspace constants in one of the two firewire headers that are exported to userland since this only concerns the core-cdev.c implementation. Signed-off-by: Stefan Richter --- drivers/firewire/core-cdev.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'drivers/firewire') diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index f7559bfeaba3..7a690c466ce9 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -18,6 +18,7 @@ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +#include #include #include #include @@ -909,6 +910,9 @@ 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; + 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; @@ -1060,6 +1064,13 @@ static int ioctl_start_iso(struct client *client, union ioctl_arg *arg) { struct fw_cdev_start_iso *a = &arg->start_iso; + BUILD_BUG_ON( + FW_CDEV_ISO_CONTEXT_MATCH_TAG0 != FW_ISO_CONTEXT_MATCH_TAG0 || + FW_CDEV_ISO_CONTEXT_MATCH_TAG1 != FW_ISO_CONTEXT_MATCH_TAG1 || + FW_CDEV_ISO_CONTEXT_MATCH_TAG2 != FW_ISO_CONTEXT_MATCH_TAG2 || + FW_CDEV_ISO_CONTEXT_MATCH_TAG3 != FW_ISO_CONTEXT_MATCH_TAG3 || + FW_CDEV_ISO_CONTEXT_MATCH_ALL_TAGS != FW_ISO_CONTEXT_MATCH_ALL_TAGS); + if (client->iso_context == NULL || a->handle != 0) return -EINVAL; -- cgit v1.2.3 From 8b4f70ba4967cae90d128857af1382026a24230a Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Wed, 7 Jul 2010 15:36:07 +0200 Subject: firewire: cdev: fix fw_cdev_event_bus_reset emission after local config ROM changes When a descriptor was added or removed to the local node's config ROM, userspace clients which had a local node's /dev/fw* open did not receive any fw_cdev_event_bus_reset for poll()/read() consumption. The cause was that the core-device.c facility which re-reads the config ROM of the bus reset initiator node missed to call the fw_device update function. The fw_units are destroyed and newly added, but their parent stays and needs to be updated. Reported-by: Jay Fenlason Signed-off-by: Stefan Richter --- drivers/firewire/core-device.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/firewire') diff --git a/drivers/firewire/core-device.c b/drivers/firewire/core-device.c index 781fd7374ae9..e9c18f582c82 100644 --- a/drivers/firewire/core-device.c +++ b/drivers/firewire/core-device.c @@ -1136,6 +1136,7 @@ static void fw_device_refresh(struct work_struct *work) goto give_up; } + fw_device_cdev_update(device); create_units(device); /* Userspace may want to re-read attributes. */ -- 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 +++++++++++++++++++++-------- 5 files changed, 112 insertions(+), 36 deletions(-) (limited to 'drivers/firewire') 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); -- 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 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) (limited to 'drivers/firewire') 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, -- cgit v1.2.3 From b9dc61cf404165fb77e80c853e9fec9af258f9ce Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Fri, 16 Jul 2010 22:24:29 +0200 Subject: firewire: core: use C99 initializer in array of ioctl handlers to make the correspondence of ioctl numbers and handlers more obvious. Signed-off-by: Stefan Richter --- drivers/firewire/core-cdev.c | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) (limited to 'drivers/firewire') diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index ee2e87353102..acf4fa1f3f8c 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -1385,27 +1385,27 @@ static int ioctl_send_stream_packet(struct client *client, union ioctl_arg *arg) } static int (* const ioctl_handlers[])(struct client *, union ioctl_arg *) = { - ioctl_get_info, - ioctl_send_request, - ioctl_allocate, - ioctl_deallocate, - ioctl_send_response, - ioctl_initiate_bus_reset, - ioctl_add_descriptor, - ioctl_remove_descriptor, - ioctl_create_iso_context, - ioctl_queue_iso, - ioctl_start_iso, - ioctl_stop_iso, - ioctl_get_cycle_timer, - ioctl_allocate_iso_resource, - ioctl_deallocate_iso_resource, - ioctl_allocate_iso_resource_once, - ioctl_deallocate_iso_resource_once, - ioctl_get_speed, - ioctl_send_broadcast_request, - ioctl_send_stream_packet, - ioctl_get_cycle_timer2, + [0x00] = ioctl_get_info, + [0x01] = ioctl_send_request, + [0x02] = ioctl_allocate, + [0x03] = ioctl_deallocate, + [0x04] = ioctl_send_response, + [0x05] = ioctl_initiate_bus_reset, + [0x06] = ioctl_add_descriptor, + [0x07] = ioctl_remove_descriptor, + [0x08] = ioctl_create_iso_context, + [0x09] = ioctl_queue_iso, + [0x0a] = ioctl_start_iso, + [0x0b] = ioctl_stop_iso, + [0x0c] = ioctl_get_cycle_timer, + [0x0d] = ioctl_allocate_iso_resource, + [0x0e] = ioctl_deallocate_iso_resource, + [0x0f] = ioctl_allocate_iso_resource_once, + [0x10] = ioctl_deallocate_iso_resource_once, + [0x11] = ioctl_get_speed, + [0x12] = ioctl_send_broadcast_request, + [0x13] = ioctl_send_stream_packet, + [0x14] = ioctl_get_cycle_timer2, }; static int dispatch_ioctl(struct client *client, -- 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 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) (limited to 'drivers/firewire') 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, -- 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 +- 5 files changed, 77 insertions(+), 7 deletions(-) (limited to 'drivers/firewire') 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); -- 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 +++ 3 files changed, 14 insertions(+), 3 deletions(-) (limited to 'drivers/firewire') 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: -- cgit v1.2.3 From 0c9ae701ae1caf657326db22d61074b40a747c9d Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Fri, 23 Jul 2010 13:02:54 +0200 Subject: firewire: core: fix upper bound of possible CSR allocations region->end is defined as an upper bound of the requested address range, exclusive --- i.e. as an address outside of the range in which the requested CSR is to be placed. Hence 0x0001,0000,0000,0000 is the biggest valid region->end, not 0x0000,ffff,ffff,fffc like the current check asserted. For simplicity, the fix drops the region->end & 3 test because there is no actual problem with these bits set in region->end. The allocated address range will be quadlet aligned and of a size of multiple quadlets due to the checks for region->start & 3 and handler->length & 3 alone. Signed-off-by: Stefan Richter --- drivers/firewire/core-transaction.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/firewire') diff --git a/drivers/firewire/core-transaction.c b/drivers/firewire/core-transaction.c index 6f225cacbc3d..ca7ca56661e0 100644 --- a/drivers/firewire/core-transaction.c +++ b/drivers/firewire/core-transaction.c @@ -543,8 +543,8 @@ int fw_core_add_address_handler(struct fw_address_handler *handler, int ret = -EBUSY; if (region->start & 0xffff000000000003ULL || - region->end & 0xffff000000000003ULL || region->start >= region->end || + region->end > 0x0001000000000000ULL || handler->length & 3 || handler->length == 0) return -EINVAL; -- 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 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'drivers/firewire') 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); -- cgit v1.2.3 From 286468210d83ce0ca1e37e346ed9f4457a161650 Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Tue, 27 Jul 2010 10:26:33 +0200 Subject: firewire: new driver: nosy - IEEE 1394 traffic sniffer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds the traffic sniffer driver for Texas Instruments PCILynx/ PCILynx2 based cards. The use cases for nosy are analysis of nonstandard protocols and as an aid in development of drivers, applications, or firmwares. Author of the driver is Kristian Høgsberg. Known contributers are Jody McIntyre and Jonathan Woithe. Nosy programs PCILynx chips to operate in promiscuous mode, which is a feature that is not found in OHCI-1394 controllers. Hence, only special hardware as mentioned in the Kconfig help text is suitable for nosy. This is only the kernelspace part of nosy. There is a userspace interface to it, called nosy-dump, proposed to be added into the tools/ subdirectory of the kernel sources in a subsequent change. Kernelspace and userspave component of nosy communicate via a 'misc' character device file called /dev/nosy with a simple ioctl() and read() based protocol, as described by nosy-user.h. The files added here are taken from git://anongit.freedesktop.org/~krh/nosy commit ee29be97 (2009-11-10) with the following changes by Stefan Richter: - Kconfig and Makefile hunks are written from scratch. - Commented out version printk in nosy.c. - Included missing , reported by Stephen Rothwell. "git shortlog nosy{-user.h,.c,.h}" from nosy's git repository: Jonathan Woithe (2): Nosy updates for recent kernels Fix uninitialised memory (needed for 2.6.31 kernel) Kristian Høgsberg (5): Pull over nosy from mercurial repo. Use a misc device instead. Add simple AV/C decoder. Don't break down on big payloads. Set parent device for misc device. As a low-level IEEE 1394 driver, its files are placed into drivers/firewire/ although nosy is not part of the firewire driver stack. I am aware of the following literature from Texas Instruments about PCILynx programming: SCPA020A - PCILynx 1394 to PCI Bus Interface TSB12LV21BPGF Functional Specification SLLA023 - Initialization and Asynchronous Programming of the TSB12LV21A 1394 Device Signed-off-by: Stefan Richter Acked-by: Kristian Høgsberg --- drivers/firewire/Kconfig | 23 ++ drivers/firewire/Makefile | 1 + drivers/firewire/nosy-user.h | 25 ++ drivers/firewire/nosy.c | 695 +++++++++++++++++++++++++++++++++++++++++++ drivers/firewire/nosy.h | 238 +++++++++++++++ 5 files changed, 982 insertions(+) create mode 100644 drivers/firewire/nosy-user.h create mode 100644 drivers/firewire/nosy.c create mode 100644 drivers/firewire/nosy.h (limited to 'drivers/firewire') diff --git a/drivers/firewire/Kconfig b/drivers/firewire/Kconfig index a9371b36a9b9..c4edc34d128b 100644 --- a/drivers/firewire/Kconfig +++ b/drivers/firewire/Kconfig @@ -66,4 +66,27 @@ config FIREWIRE_NET source "drivers/ieee1394/Kconfig" +config FIREWIRE_NOSY + tristate "Nosy - a FireWire traffic sniffer for PCILynx cards" + depends on PCI + help + Nosy is an IEEE 1394 packet sniffer that is used for protocol + analysis and in development of IEEE 1394 drivers, applications, + or firmwares. + + This driver lets you use a Texas Instruments PCILynx 1394 to PCI + link layer controller TSB12LV21/A/B as a low-budget bus analyzer. + PCILynx is a nowadays very rare IEEE 1394 controller which is + not OHCI 1394 compliant. + + The following cards are known to be based on PCILynx or PCILynx-2: + IOI IOI-1394TT (PCI card), Unibrain Fireboard 400 PCI Lynx-2 + (PCI card), Newer Technology FireWire 2 Go (CardBus card), + Apple Power Mac G3 blue & white (onboard controller). + + To compile this driver as a module, say M here: The module will be + called nosy. + + If unsure, say N. + endmenu diff --git a/drivers/firewire/Makefile b/drivers/firewire/Makefile index a8f9bb6d9fdf..3c6a7fb20aa7 100644 --- a/drivers/firewire/Makefile +++ b/drivers/firewire/Makefile @@ -12,3 +12,4 @@ obj-$(CONFIG_FIREWIRE) += firewire-core.o obj-$(CONFIG_FIREWIRE_OHCI) += firewire-ohci.o obj-$(CONFIG_FIREWIRE_SBP2) += firewire-sbp2.o obj-$(CONFIG_FIREWIRE_NET) += firewire-net.o +obj-$(CONFIG_FIREWIRE_NOSY) += nosy.o diff --git a/drivers/firewire/nosy-user.h b/drivers/firewire/nosy-user.h new file mode 100644 index 000000000000..c9a1682ab45e --- /dev/null +++ b/drivers/firewire/nosy-user.h @@ -0,0 +1,25 @@ +#ifndef __nosy_user_h +#define __nosy_user_h + +#include +#include + +#define NOSY_IOC_GET_STATS _IOR('&', 0, struct nosy_stats) +#define NOSY_IOC_START _IO('&', 1) +#define NOSY_IOC_STOP _IO('&', 2) +#define NOSY_IOC_FILTER _IOW('&', 2, __u32) + +struct nosy_stats { + __u32 total_packet_count; + __u32 lost_packet_count; +}; + +/* + * Format of packets returned from the kernel driver: + * + * quadlet with timestamp (microseconds) + * quadlet padded packet data... + * quadlet with ack + */ + +#endif /* __nosy_user_h */ diff --git a/drivers/firewire/nosy.c b/drivers/firewire/nosy.c new file mode 100644 index 000000000000..079710bf1197 --- /dev/null +++ b/drivers/firewire/nosy.c @@ -0,0 +1,695 @@ +/* -*- c-file-style: "linux" -*- + * + * nosy.c - Snoop mode driver for TI pcilynx 1394 controllers + * Copyright (C) 2002 Kristian Høgsberg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include /* required for linux/wait.h */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "nosy.h" +#include "nosy-user.h" + +#define TCODE_PHY_PACKET 0x10 +#define PCI_DEVICE_ID_TI_PCILYNX 0x8000 + +#define notify(s, args...) printk(KERN_NOTICE s, ## args) +#define error(s, args...) printk(KERN_ERR s, ## args) +#define debug(s, args...) printk(KERN_DEBUG s, ## args) + +static const char driver_name[] = "nosy"; + +struct pcl_status { + unsigned int transfer_count : 13; + unsigned int reserved0 : 1; + unsigned int ack_type : 1; + unsigned int ack : 4; + unsigned int rcv_speed : 2; + unsigned int rcv_dma_channel : 6; + unsigned int packet_complete : 1; + unsigned int packet_error : 1; + unsigned int master_error : 1; + unsigned int iso_mode : 1; + unsigned int self_id : 1; +}; + +/* this is the physical layout of a PCL, its size is 128 bytes */ +struct pcl { + u32 next; + u32 async_error_next; + u32 user_data; + struct pcl_status pcl_status; + u32 remaining_transfer_count; + u32 next_data_buffer; + struct { + u32 control; + u32 pointer; + } buffer[13] __attribute__ ((packed)); +} __attribute__ ((packed)); + +struct packet { + unsigned int length : 16; + unsigned int code : 16; + char data[0]; +}; + +struct packet_buffer { + char *data; + size_t capacity; + long total_packet_count, lost_packet_count; + atomic_t size; + struct packet *head, *tail; + wait_queue_head_t wait; +}; + +struct pcilynx { + struct pci_dev *pci_device; + unsigned char *registers; + + struct pcl *rcv_start_pcl, *rcv_pcl; + u32 *rcv_buffer; + + dma_addr_t rcv_start_pcl_bus, rcv_pcl_bus, rcv_buffer_bus; + + spinlock_t client_list_lock; + struct list_head client_list; + + struct miscdevice misc; +}; + + +struct client { + struct pcilynx *lynx; + unsigned long tcode_mask; + struct packet_buffer buffer; + struct list_head link; +}; + +#define MAX_MINORS 64 +struct pcilynx *minors[MAX_MINORS]; + +static int +packet_buffer_init(struct packet_buffer *buffer, size_t capacity) +{ + buffer->data = kmalloc(capacity, GFP_KERNEL); + if (buffer->data == NULL) + return -ENOMEM; + buffer->head = (struct packet *) buffer->data; + buffer->tail = (struct packet *) buffer->data; + buffer->capacity = capacity; + buffer->lost_packet_count = 0; + atomic_set(&buffer->size, 0); + init_waitqueue_head(&buffer->wait); + + return 0; +} + +static void +packet_buffer_destroy(struct packet_buffer *buffer) +{ + kfree(buffer->data); +} + +static int +packet_buffer_get(struct packet_buffer *buffer, void *data, size_t user_length) +{ + size_t length; + char *end; + + if (wait_event_interruptible(buffer->wait, + atomic_read(&buffer->size) > 0)) + return -ERESTARTSYS; + + /* FIXME: Check length <= user_length. */ + + end = buffer->data + buffer->capacity; + length = buffer->head->length; + + if (&buffer->head->data[length] < end) { + if (copy_to_user(data, buffer->head->data, length)) + return -EFAULT; + buffer->head = (struct packet *) &buffer->head->data[length]; + } + else { + size_t split = end - buffer->head->data; + + if (copy_to_user(data, buffer->head->data, split)) + return -EFAULT; + if (copy_to_user(data + split, buffer->data, length - split)) + return -EFAULT; + buffer->head = (struct packet *) &buffer->data[length - split]; + } + + /* Decrease buffer->size as the last thing, since this is what + * keeps the interrupt from overwriting the packet we are + * retrieving from the buffer. */ + + atomic_sub(sizeof (struct packet) + length, &buffer->size); + + return length; +} + +static void +packet_buffer_put(struct packet_buffer *buffer, void *data, size_t length) +{ + char *end; + + buffer->total_packet_count++; + + if (buffer->capacity < + atomic_read(&buffer->size) + sizeof (struct packet) + length) { + buffer->lost_packet_count++; + return; + } + + end = buffer->data + buffer->capacity; + buffer->tail->length = length; + + if (&buffer->tail->data[length] < end) { + memcpy(buffer->tail->data, data, length); + buffer->tail = (struct packet *) &buffer->tail->data[length]; + } + else { + size_t split = end - buffer->tail->data; + + memcpy(buffer->tail->data, data, split); + memcpy(buffer->data, data + split, length - split); + buffer->tail = (struct packet *) &buffer->data[length - split]; + } + + /* Finally, adjust buffer size and wake up userspace reader. */ + + atomic_add(sizeof (struct packet) + length, &buffer->size); + wake_up_interruptible(&buffer->wait); +} + +static inline void +reg_write(struct pcilynx *lynx, int offset, u32 data) +{ + writel(data, lynx->registers + offset); +} + +static inline u32 +reg_read(struct pcilynx *lynx, int offset) +{ + return readl(lynx->registers + offset); +} + +static inline void +reg_set_bits(struct pcilynx *lynx, int offset, u32 mask) +{ + reg_write(lynx, offset, (reg_read(lynx, offset) | mask)); +} + +/* Maybe the pcl programs could be setup to just append data instead + * of using a whole packet. */ + +static inline void +run_pcl(struct pcilynx *lynx, dma_addr_t pcl_bus, int dmachan) +{ + reg_write(lynx, DMA0_CURRENT_PCL + dmachan * 0x20, pcl_bus); + reg_write(lynx, DMA0_CHAN_CTRL + dmachan * 0x20, + DMA_CHAN_CTRL_ENABLE | DMA_CHAN_CTRL_LINK); +} + +static int +set_phy_reg(struct pcilynx *lynx, int addr, int val) +{ + if (addr > 15) { + debug("%s: PHY register address %d out of range", + __FUNCTION__, addr); + return -1; + } + + if (val > 0xff) { + debug("%s: PHY register value %d out of range", + __FUNCTION__, val); + return -1; + } + + reg_write(lynx, LINK_PHY, LINK_PHY_WRITE | + LINK_PHY_ADDR(addr) | LINK_PHY_WDATA(val)); + + return 0; +} + +static void +nosy_start_snoop(struct client *client) +{ + unsigned long flags; + + spin_lock_irqsave(&client->lynx->client_list_lock, flags); + list_add_tail(&client->link, &client->lynx->client_list); + spin_unlock_irqrestore(&client->lynx->client_list_lock, flags); +} + +static void +nosy_stop_snoop(struct client *client) +{ + unsigned long flags; + + spin_lock_irqsave(&client->lynx->client_list_lock, flags); + list_del(&client->link); + spin_unlock_irqrestore(&client->lynx->client_list_lock, flags); +} + +static struct client * +nosy_add_client(struct pcilynx *lynx) +{ + struct client *client; + + client = kmalloc(sizeof *client, GFP_KERNEL); + client->tcode_mask = ~0; + client->lynx = lynx; + INIT_LIST_HEAD(&client->link); + + if (packet_buffer_init(&client->buffer, 128 * 1024) < 0) { + kfree(client); + debug("Failed to allocate packet buffer\n"); + return NULL; + } + + return client; +} + +static void +nosy_remove_client(struct client *client) +{ + nosy_stop_snoop(client); + packet_buffer_destroy(&client->buffer); + kfree(client); +} + +static int +nosy_open(struct inode *inode, struct file *file) +{ + int minor = iminor(inode); + + if (minor > MAX_MINORS || minors[minor] == NULL) + return -ENODEV; + + file->private_data = nosy_add_client(minors[minor]); + if (file->private_data == NULL) + return -ENOMEM; + else + return 0; +} + +static int +nosy_release(struct inode *inode, struct file *file) +{ + nosy_remove_client(file->private_data); + + return 0; +} + +static unsigned int +nosy_poll(struct file *file, poll_table *pt) +{ + struct client *client = file->private_data; + + poll_wait(file, &client->buffer.wait, pt); + + if (atomic_read(&client->buffer.size) > 0) + return POLLIN | POLLRDNORM; + else + return 0; +} + +static ssize_t +nosy_read(struct file *file, char *buffer, size_t count, loff_t *offset) +{ + struct client *client = file->private_data; + + return packet_buffer_get(&client->buffer, buffer, count); +} + +static int +nosy_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct client *client = file->private_data; + + switch (cmd) { + case NOSY_IOC_GET_STATS: { + struct nosy_stats stats; + + stats.total_packet_count = client->buffer.total_packet_count; + stats.lost_packet_count = client->buffer.lost_packet_count; + if (copy_to_user((void *) arg, &stats, sizeof stats)) + return -EFAULT; + else + return 0; + } + + case NOSY_IOC_START: + nosy_start_snoop(client); + return 0; + + case NOSY_IOC_STOP: + nosy_stop_snoop(client); + return 0; + + case NOSY_IOC_FILTER: + client->tcode_mask = arg; + return 0; + + default: + return -EINVAL; + /* Flush buffer, configure filter. */ + } +} + +static struct file_operations nosy_ops = { + .owner = THIS_MODULE, + .read = nosy_read, + .ioctl = nosy_ioctl, + .poll = nosy_poll, + .open = nosy_open, + .release = nosy_release, +}; + +#define PHY_PACKET_SIZE 12 /* 1 payload, 1 inverse, 1 ack = 3 quadlets */ + +struct link_packet { + unsigned int priority : 4; + unsigned int tcode : 4; + unsigned int rt : 2; + unsigned int tlabel : 6; + unsigned int destination : 16; +}; + +static void +packet_handler(struct pcilynx *lynx) +{ + unsigned long flags; + struct list_head *pos; + struct client *client; + unsigned long tcode_mask; + size_t length; + struct link_packet *packet; + struct timeval tv; + + /* FIXME: Also report rcv_speed. */ + + length = lynx->rcv_pcl->pcl_status.transfer_count; + packet = (struct link_packet *) &lynx->rcv_buffer[1]; + + do_gettimeofday(&tv); + lynx->rcv_buffer[0] = tv.tv_usec; + + if (length == PHY_PACKET_SIZE) + tcode_mask = 1 << TCODE_PHY_PACKET; + else + tcode_mask = 1 << packet->tcode; + + spin_lock_irqsave(&lynx->client_list_lock, flags); + + list_for_each(pos, &lynx->client_list) { + client = list_entry(pos, struct client, link); + if (client->tcode_mask & tcode_mask) + packet_buffer_put(&client->buffer, + lynx->rcv_buffer, length + 4); + } + + spin_unlock_irqrestore(&lynx->client_list_lock, flags); +} + +static void +bus_reset_handler(struct pcilynx *lynx) +{ + unsigned long flags; + struct list_head *pos; + struct client *client; + struct timeval tv; + + do_gettimeofday(&tv); + + spin_lock_irqsave(&lynx->client_list_lock, flags); + + list_for_each(pos, &lynx->client_list) { + client = list_entry(pos, struct client, link); + packet_buffer_put(&client->buffer, &tv.tv_usec, 4); + } + + spin_unlock_irqrestore(&lynx->client_list_lock, flags); +} + + + +static irqreturn_t +irq_handler(int irq, void *device) +{ + struct pcilynx *lynx = (struct pcilynx *) device; + u32 pci_int_status; + + pci_int_status = reg_read(lynx, PCI_INT_STATUS); + + if ((pci_int_status & PCI_INT_INT_PEND) == 0) + /* Not our interrupt, bail out quickly. */ + return IRQ_NONE; + + if ((pci_int_status & PCI_INT_P1394_INT) != 0) { + u32 link_int_status; + + link_int_status = reg_read(lynx, LINK_INT_STATUS); + reg_write(lynx, LINK_INT_STATUS, link_int_status); + + if ((link_int_status & LINK_INT_PHY_BUSRESET) > 0) + bus_reset_handler(lynx); + } + + /* Clear the PCI_INT_STATUS register only after clearing the + * LINK_INT_STATUS register; otherwise the PCI_INT_P1394 will + * be set again immediately. */ + + reg_write(lynx, PCI_INT_STATUS, pci_int_status); + + if ((pci_int_status & PCI_INT_DMA0_HLT) > 0) { + packet_handler(lynx); + run_pcl(lynx, lynx->rcv_start_pcl_bus, 0); + } + + return IRQ_HANDLED; +} + +static void +remove_card(struct pci_dev *dev) +{ + struct pcilynx *lynx; + + lynx = pci_get_drvdata(dev); + if (!lynx) + return; + pci_set_drvdata(dev, NULL); + + reg_write(lynx, PCI_INT_ENABLE, 0); + free_irq(lynx->pci_device->irq, lynx); + + pci_free_consistent(lynx->pci_device, sizeof (struct pcl), + lynx->rcv_start_pcl, lynx->rcv_start_pcl_bus); + pci_free_consistent(lynx->pci_device, sizeof (struct pcl), + lynx->rcv_pcl, lynx->rcv_pcl_bus); + pci_free_consistent(lynx->pci_device, PAGE_SIZE, + lynx->rcv_buffer, lynx->rcv_buffer_bus); + + iounmap(lynx->registers); + + minors[lynx->misc.minor] = NULL; + misc_deregister(&lynx->misc); + + kfree(lynx); +} + +#define RCV_BUFFER_SIZE (16 * 1024) + +#define FAIL(s, args...) \ + do { \ + error(s, ## args); \ + return err; \ + } while (0) + +static int __devinit +add_card(struct pci_dev *dev, const struct pci_device_id *unused) +{ + struct pcilynx *lynx; + u32 p, end; + int err, i; + + err = -ENXIO; + + if (pci_set_dma_mask(dev, 0xffffffff)) + FAIL("DMA address limits not supported " + "for PCILynx hardware.\n"); + if (pci_enable_device(dev)) + FAIL("Failed to enable PCILynx hardware.\n"); + pci_set_master(dev); + + err = -ENOMEM; + + lynx = kzalloc(sizeof *lynx, GFP_KERNEL); + if (lynx == NULL) + FAIL("Failed to allocate control structure memory.\n"); + + lynx->pci_device = dev; + pci_set_drvdata(dev, lynx); + + spin_lock_init(&lynx->client_list_lock); + INIT_LIST_HEAD(&lynx->client_list); + + lynx->registers = ioremap_nocache(pci_resource_start(dev, 0), + PCILYNX_MAX_REGISTER); + + lynx->rcv_start_pcl = pci_alloc_consistent(lynx->pci_device, + sizeof(struct pcl), + &lynx->rcv_start_pcl_bus); + lynx->rcv_pcl = pci_alloc_consistent(lynx->pci_device, + sizeof(struct pcl), + &lynx->rcv_pcl_bus); + lynx->rcv_buffer = pci_alloc_consistent(lynx->pci_device, RCV_BUFFER_SIZE, + &lynx->rcv_buffer_bus); + if (lynx->rcv_start_pcl == NULL || + lynx->rcv_pcl == NULL || + lynx->rcv_buffer == NULL) + /* FIXME: do proper error handling. */ + FAIL("Failed to allocate receive buffer.\n"); + + lynx->rcv_start_pcl->next = lynx->rcv_pcl_bus; + lynx->rcv_pcl->next = PCL_NEXT_INVALID; + lynx->rcv_pcl->async_error_next = PCL_NEXT_INVALID; + + lynx->rcv_pcl->buffer[0].control = + PCL_CMD_RCV | PCL_BIGENDIAN | 2044; + lynx->rcv_pcl->buffer[0].pointer = lynx->rcv_buffer_bus + 4; + p = lynx->rcv_buffer_bus + 2048; + end = lynx->rcv_buffer_bus + RCV_BUFFER_SIZE; + for (i = 1; p < end; i++, p += 2048) { + lynx->rcv_pcl->buffer[i].control = + PCL_CMD_RCV | PCL_BIGENDIAN | 2048; + lynx->rcv_pcl->buffer[i].pointer = p; + } + lynx->rcv_pcl->buffer[i - 1].control |= PCL_LAST_BUFF; + + reg_set_bits(lynx, MISC_CONTROL, MISC_CONTROL_SWRESET); + /* Fix buggy cards with autoboot pin not tied low: */ + reg_write(lynx, DMA0_CHAN_CTRL, 0); + reg_write(lynx, DMA_GLOBAL_REGISTER, 0x00 << 24); + +#if 0 + /* now, looking for PHY register set */ + if ((get_phy_reg(lynx, 2) & 0xe0) == 0xe0) { + lynx->phyic.reg_1394a = 1; + PRINT(KERN_INFO, lynx->id, + "found 1394a conform PHY (using extended register set)"); + lynx->phyic.vendor = get_phy_vendorid(lynx); + lynx->phyic.product = get_phy_productid(lynx); + } else { + lynx->phyic.reg_1394a = 0; + PRINT(KERN_INFO, lynx->id, "found old 1394 PHY"); + } +#endif + + /* Setup the general receive FIFO max size. */ + reg_write(lynx, FIFO_SIZES, 255); + + reg_set_bits(lynx, PCI_INT_ENABLE, PCI_INT_DMA_ALL); + + reg_write(lynx, LINK_INT_ENABLE, + LINK_INT_PHY_TIME_OUT | LINK_INT_PHY_REG_RCVD | + LINK_INT_PHY_BUSRESET | LINK_INT_IT_STUCK | + LINK_INT_AT_STUCK | LINK_INT_SNTRJ | + LINK_INT_TC_ERR | LINK_INT_GRF_OVER_FLOW | + LINK_INT_ITF_UNDER_FLOW | LINK_INT_ATF_UNDER_FLOW); + + /* Disable the L flag in self ID packets. */ + set_phy_reg(lynx, 4, 0); + + /* Put this baby into snoop mode */ + reg_set_bits(lynx, LINK_CONTROL, LINK_CONTROL_SNOOP_ENABLE); + + run_pcl(lynx, lynx->rcv_start_pcl_bus, 0); + + if (request_irq(dev->irq, irq_handler, IRQF_SHARED, driver_name, lynx)) + FAIL("Failed to allocate shared interrupt %d.", dev->irq); + + lynx->misc.parent = &dev->dev; + lynx->misc.minor = MISC_DYNAMIC_MINOR; + lynx->misc.name = "nosy"; + lynx->misc.fops = &nosy_ops; + if (misc_register(&lynx->misc)) + FAIL("Failed to register misc char device."); + minors[lynx->misc.minor] = lynx; + + notify("Initialized PCILynx IEEE1394 card, irq=%d\n", dev->irq); + + return 0; +} + +static struct pci_device_id pci_table[] __devinitdata = { + { + .vendor = PCI_VENDOR_ID_TI, + .device = PCI_DEVICE_ID_TI_PCILYNX, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + }, + { } /* Terminating entry */ +}; + +static struct pci_driver lynx_pci_driver = { + .name = (char *) driver_name, + .id_table = pci_table, + .probe = add_card, + .remove = __devexit_p(remove_card), +}; + +MODULE_AUTHOR("Kristian Høgsberg"); +MODULE_DESCRIPTION("Snoop mode driver for TI pcilynx 1394 controllers"); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(pci, pci_table); + +static int __init nosy_init(void) +{ + /* notify("Loaded %s version %s.\n", driver_name, VERSION); */ + + return pci_register_driver(&lynx_pci_driver); +} + +static void __exit nosy_cleanup(void) +{ + pci_unregister_driver(&lynx_pci_driver); + + notify("Unloaded %s.\n", driver_name); +} + + +module_init(nosy_init); +module_exit(nosy_cleanup); diff --git a/drivers/firewire/nosy.h b/drivers/firewire/nosy.h new file mode 100644 index 000000000000..3440071ac0d2 --- /dev/null +++ b/drivers/firewire/nosy.h @@ -0,0 +1,238 @@ +/* Chip register definitions for PCILynx chipset. Based on pcilynx.h + * from the Linux 1394 drivers, but modified a bit so the names here + * match the specification exactly (even though they have weird names, + * like xxx_OVER_FLOW, or arbitrary abbreviations like SNTRJ for "sent + * reject" etc.) + */ + +#define PCILYNX_MAX_REGISTER 0xfff +#define PCILYNX_MAX_MEMORY 0xffff + +#define PCI_LATENCY_CACHELINE 0x0c + +#define MISC_CONTROL 0x40 +#define MISC_CONTROL_SWRESET (1<<0) + +#define SERIAL_EEPROM_CONTROL 0x44 + +#define PCI_INT_STATUS 0x48 +#define PCI_INT_ENABLE 0x4c +/* status and enable have identical bit numbers */ +#define PCI_INT_INT_PEND (1<<31) +#define PCI_INT_FRC_INT (1<<30) +#define PCI_INT_SLV_ADR_PERR (1<<28) +#define PCI_INT_SLV_DAT_PERR (1<<27) +#define PCI_INT_MST_DAT_PERR (1<<26) +#define PCI_INT_MST_DEV_TO (1<<25) +#define PCI_INT_INT_SLV_TO (1<<23) +#define PCI_INT_AUX_TO (1<<18) +#define PCI_INT_AUX_INT (1<<17) +#define PCI_INT_P1394_INT (1<<16) +#define PCI_INT_DMA4_PCL (1<<9) +#define PCI_INT_DMA4_HLT (1<<8) +#define PCI_INT_DMA3_PCL (1<<7) +#define PCI_INT_DMA3_HLT (1<<6) +#define PCI_INT_DMA2_PCL (1<<5) +#define PCI_INT_DMA2_HLT (1<<4) +#define PCI_INT_DMA1_PCL (1<<3) +#define PCI_INT_DMA1_HLT (1<<2) +#define PCI_INT_DMA0_PCL (1<<1) +#define PCI_INT_DMA0_HLT (1<<0) +/* all DMA interrupts combined: */ +#define PCI_INT_DMA_ALL 0x3ff + +#define PCI_INT_DMA_HLT(chan) (1 << (chan * 2)) +#define PCI_INT_DMA_PCL(chan) (1 << (chan * 2 + 1)) + +#define LBUS_ADDR 0xb4 +#define LBUS_ADDR_SEL_RAM (0x0<<16) +#define LBUS_ADDR_SEL_ROM (0x1<<16) +#define LBUS_ADDR_SEL_AUX (0x2<<16) +#define LBUS_ADDR_SEL_ZV (0x3<<16) + +#define GPIO_CTRL_A 0xb8 +#define GPIO_CTRL_B 0xbc +#define GPIO_DATA_BASE 0xc0 + +#define DMA_BREG(base, chan) (base + chan * 0x20) +#define DMA_SREG(base, chan) (base + chan * 0x10) + +#define PCL_NEXT_INVALID (1<<0) + +/* transfer commands */ +#define PCL_CMD_RCV (0x1<<24) +#define PCL_CMD_RCV_AND_UPDATE (0xa<<24) +#define PCL_CMD_XMT (0x2<<24) +#define PCL_CMD_UNFXMT (0xc<<24) +#define PCL_CMD_PCI_TO_LBUS (0x8<<24) +#define PCL_CMD_LBUS_TO_PCI (0x9<<24) + +/* aux commands */ +#define PCL_CMD_NOP (0x0<<24) +#define PCL_CMD_LOAD (0x3<<24) +#define PCL_CMD_STOREQ (0x4<<24) +#define PCL_CMD_STORED (0xb<<24) +#define PCL_CMD_STORE0 (0x5<<24) +#define PCL_CMD_STORE1 (0x6<<24) +#define PCL_CMD_COMPARE (0xe<<24) +#define PCL_CMD_SWAP_COMPARE (0xf<<24) +#define PCL_CMD_ADD (0xd<<24) +#define PCL_CMD_BRANCH (0x7<<24) + +/* BRANCH condition codes */ +#define PCL_COND_DMARDY_SET (0x1<<20) +#define PCL_COND_DMARDY_CLEAR (0x2<<20) + +#define PCL_GEN_INTR (1<<19) +#define PCL_LAST_BUFF (1<<18) +#define PCL_LAST_CMD (PCL_LAST_BUFF) +#define PCL_WAITSTAT (1<<17) +#define PCL_BIGENDIAN (1<<16) +#define PCL_ISOMODE (1<<12) + +#define DMA0_PREV_PCL 0x100 +#define DMA1_PREV_PCL 0x120 +#define DMA2_PREV_PCL 0x140 +#define DMA3_PREV_PCL 0x160 +#define DMA4_PREV_PCL 0x180 +#define DMA_PREV_PCL(chan) (DMA_BREG(DMA0_PREV_PCL, chan)) + +#define DMA0_CURRENT_PCL 0x104 +#define DMA1_CURRENT_PCL 0x124 +#define DMA2_CURRENT_PCL 0x144 +#define DMA3_CURRENT_PCL 0x164 +#define DMA4_CURRENT_PCL 0x184 +#define DMA_CURRENT_PCL(chan) (DMA_BREG(DMA0_CURRENT_PCL, chan)) + +#define DMA0_CHAN_STAT 0x10c +#define DMA1_CHAN_STAT 0x12c +#define DMA2_CHAN_STAT 0x14c +#define DMA3_CHAN_STAT 0x16c +#define DMA4_CHAN_STAT 0x18c +#define DMA_CHAN_STAT(chan) (DMA_BREG(DMA0_CHAN_STAT, chan)) +/* CHAN_STATUS registers share bits */ +#define DMA_CHAN_STAT_SELFID (1<<31) +#define DMA_CHAN_STAT_ISOPKT (1<<30) +#define DMA_CHAN_STAT_PCIERR (1<<29) +#define DMA_CHAN_STAT_PKTERR (1<<28) +#define DMA_CHAN_STAT_PKTCMPL (1<<27) +#define DMA_CHAN_STAT_SPECIALACK (1<<14) + + +#define DMA0_CHAN_CTRL 0x110 +#define DMA1_CHAN_CTRL 0x130 +#define DMA2_CHAN_CTRL 0x150 +#define DMA3_CHAN_CTRL 0x170 +#define DMA4_CHAN_CTRL 0x190 +#define DMA_CHAN_CTRL(chan) (DMA_BREG(DMA0_CHAN_CTRL, chan)) +/* CHAN_CTRL registers share bits */ +#define DMA_CHAN_CTRL_ENABLE (1<<31) +#define DMA_CHAN_CTRL_BUSY (1<<30) +#define DMA_CHAN_CTRL_LINK (1<<29) + +#define DMA0_READY 0x114 +#define DMA1_READY 0x134 +#define DMA2_READY 0x154 +#define DMA3_READY 0x174 +#define DMA4_READY 0x194 +#define DMA_READY(chan) (DMA_BREG(DMA0_READY, chan)) + +#define DMA_GLOBAL_REGISTER 0x908 + +#define FIFO_SIZES 0xa00 + +#define FIFO_CONTROL 0xa10 +#define FIFO_CONTROL_GRF_FLUSH (1<<4) +#define FIFO_CONTROL_ITF_FLUSH (1<<3) +#define FIFO_CONTROL_ATF_FLUSH (1<<2) + +#define FIFO_XMIT_THRESHOLD 0xa14 + +#define DMA0_WORD0_CMP_VALUE 0xb00 +#define DMA1_WORD0_CMP_VALUE 0xb10 +#define DMA2_WORD0_CMP_VALUE 0xb20 +#define DMA3_WORD0_CMP_VALUE 0xb30 +#define DMA4_WORD0_CMP_VALUE 0xb40 +#define DMA_WORD0_CMP_VALUE(chan) (DMA_SREG(DMA0_WORD0_CMP_VALUE, chan)) + +#define DMA0_WORD0_CMP_ENABLE 0xb04 +#define DMA1_WORD0_CMP_ENABLE 0xb14 +#define DMA2_WORD0_CMP_ENABLE 0xb24 +#define DMA3_WORD0_CMP_ENABLE 0xb34 +#define DMA4_WORD0_CMP_ENABLE 0xb44 +#define DMA_WORD0_CMP_ENABLE(chan) (DMA_SREG(DMA0_WORD0_CMP_ENABLE,chan)) + +#define DMA0_WORD1_CMP_VALUE 0xb08 +#define DMA1_WORD1_CMP_VALUE 0xb18 +#define DMA2_WORD1_CMP_VALUE 0xb28 +#define DMA3_WORD1_CMP_VALUE 0xb38 +#define DMA4_WORD1_CMP_VALUE 0xb48 +#define DMA_WORD1_CMP_VALUE(chan) (DMA_SREG(DMA0_WORD1_CMP_VALUE, chan)) + +#define DMA0_WORD1_CMP_ENABLE 0xb0c +#define DMA1_WORD1_CMP_ENABLE 0xb1c +#define DMA2_WORD1_CMP_ENABLE 0xb2c +#define DMA3_WORD1_CMP_ENABLE 0xb3c +#define DMA4_WORD1_CMP_ENABLE 0xb4c +#define DMA_WORD1_CMP_ENABLE(chan) (DMA_SREG(DMA0_WORD1_CMP_ENABLE,chan)) +/* word 1 compare enable flags */ +#define DMA_WORD1_CMP_MATCH_OTHERBUS (1<<15) +#define DMA_WORD1_CMP_MATCH_BROADCAST (1<<14) +#define DMA_WORD1_CMP_MATCH_BUS_BCAST (1<<13) +#define DMA_WORD1_CMP_MATCH_LOCAL_NODE (1<<12) +#define DMA_WORD1_CMP_MATCH_EXACT (1<<11) +#define DMA_WORD1_CMP_ENABLE_SELF_ID (1<<10) +#define DMA_WORD1_CMP_ENABLE_MASTER (1<<8) + +#define LINK_ID 0xf00 +#define LINK_ID_BUS(id) (id<<22) +#define LINK_ID_NODE(id) (id<<16) + +#define LINK_CONTROL 0xf04 +#define LINK_CONTROL_BUSY (1<<29) +#define LINK_CONTROL_TX_ISO_EN (1<<26) +#define LINK_CONTROL_RX_ISO_EN (1<<25) +#define LINK_CONTROL_TX_ASYNC_EN (1<<24) +#define LINK_CONTROL_RX_ASYNC_EN (1<<23) +#define LINK_CONTROL_RESET_TX (1<<21) +#define LINK_CONTROL_RESET_RX (1<<20) +#define LINK_CONTROL_CYCMASTER (1<<11) +#define LINK_CONTROL_CYCSOURCE (1<<10) +#define LINK_CONTROL_CYCTIMEREN (1<<9) +#define LINK_CONTROL_RCV_CMP_VALID (1<<7) +#define LINK_CONTROL_SNOOP_ENABLE (1<<6) + +#define CYCLE_TIMER 0xf08 + +#define LINK_PHY 0xf0c +#define LINK_PHY_READ (1<<31) +#define LINK_PHY_WRITE (1<<30) +#define LINK_PHY_ADDR(addr) (addr<<24) +#define LINK_PHY_WDATA(data) (data<<16) +#define LINK_PHY_RADDR(addr) (addr<<8) + + +#define LINK_INT_STATUS 0xf14 +#define LINK_INT_ENABLE 0xf18 +/* status and enable have identical bit numbers */ +#define LINK_INT_LINK_INT (1<<31) +#define LINK_INT_PHY_TIME_OUT (1<<30) +#define LINK_INT_PHY_REG_RCVD (1<<29) +#define LINK_INT_PHY_BUSRESET (1<<28) +#define LINK_INT_TX_RDY (1<<26) +#define LINK_INT_RX_DATA_RDY (1<<25) +#define LINK_INT_IT_STUCK (1<<20) +#define LINK_INT_AT_STUCK (1<<19) +#define LINK_INT_SNTRJ (1<<17) +#define LINK_INT_HDR_ERR (1<<16) +#define LINK_INT_TC_ERR (1<<15) +#define LINK_INT_CYC_SEC (1<<11) +#define LINK_INT_CYC_STRT (1<<10) +#define LINK_INT_CYC_DONE (1<<9) +#define LINK_INT_CYC_PEND (1<<8) +#define LINK_INT_CYC_LOST (1<<7) +#define LINK_INT_CYC_ARB_FAILED (1<<6) +#define LINK_INT_GRF_OVER_FLOW (1<<5) +#define LINK_INT_ITF_UNDER_FLOW (1<<4) +#define LINK_INT_ATF_UNDER_FLOW (1<<3) +#define LINK_INT_IARB_FAILED (1<<0) -- cgit v1.2.3 From b5e47729043c9224b21ab3dc7c63e8a38dbb4923 Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Tue, 27 Jul 2010 10:28:30 +0200 Subject: firewire: nosy: misc cleanups Extend copyright note to 2007, c.f. Kristian's git log. Includes: - replace some by - add required indirectly included - order alphabetically Coding style related changes: - change to utf8 - normalize whitespace - normalize comment style - remove usages of __FUNCTION__ - remove an unnecessary cast from void * Const and static declarations: - driver_name is not const in pci_driver.name, drop const qualifier - driver_name can be taken from KBUILD_MODNAME - the global variable minors[] can and should be static - constify struct file_operations instance Data types: - Remove unused struct member struct packet.code. struct packet is only used for driver-internal bookkeeping; it does not appear on the wire or in DMA programs or the userspace ABI. Hence the unused member .code can be removed without worries. Preprocessor macros: - unroll a preprocessor macro that containd a return - use list_for_each_entry Printk: - add missing terminating \n in some format strings Signed-off-by: Stefan Richter --- drivers/firewire/nosy-user.h | 10 +- drivers/firewire/nosy.c | 322 ++++++++++++++++++++----------------------- drivers/firewire/nosy.h | 27 ++-- 3 files changed, 171 insertions(+), 188 deletions(-) (limited to 'drivers/firewire') diff --git a/drivers/firewire/nosy-user.h b/drivers/firewire/nosy-user.h index c9a1682ab45e..ebef97f4ecf9 100644 --- a/drivers/firewire/nosy-user.h +++ b/drivers/firewire/nosy-user.h @@ -1,8 +1,8 @@ #ifndef __nosy_user_h #define __nosy_user_h -#include -#include +#include +#include #define NOSY_IOC_GET_STATS _IOR('&', 0, struct nosy_stats) #define NOSY_IOC_START _IO('&', 1) @@ -10,11 +10,11 @@ #define NOSY_IOC_FILTER _IOW('&', 2, __u32) struct nosy_stats { - __u32 total_packet_count; - __u32 lost_packet_count; + __u32 total_packet_count; + __u32 lost_packet_count; }; -/* +/* * Format of packets returned from the kernel driver: * * quadlet with timestamp (microseconds) diff --git a/drivers/firewire/nosy.c b/drivers/firewire/nosy.c index 079710bf1197..ea392d0985a5 100644 --- a/drivers/firewire/nosy.c +++ b/drivers/firewire/nosy.c @@ -1,7 +1,6 @@ -/* -*- c-file-style: "linux" -*- - * - * nosy.c - Snoop mode driver for TI pcilynx 1394 controllers - * Copyright (C) 2002 Kristian Høgsberg +/* + * nosy - Snoop mode driver for TI PCILynx 1394 controllers + * Copyright (C) 2002-2007 Kristian Høgsberg * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,23 +17,25 @@ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -#include -#include -#include -#include /* required for linux/wait.h */ -#include #include -#include +#include #include +#include +#include +#include +#include +#include #include -#include #include -#include -#include +#include /* required for linux/wait.h */ +#include +#include +#include +#include +#include + #include -#include -#include -#include +#include #include "nosy.h" #include "nosy-user.h" @@ -46,7 +47,7 @@ #define error(s, args...) printk(KERN_ERR s, ## args) #define debug(s, args...) printk(KERN_DEBUG s, ## args) -static const char driver_name[] = "nosy"; +static char driver_name[] = KBUILD_MODNAME; struct pcl_status { unsigned int transfer_count : 13; @@ -77,8 +78,7 @@ struct pcl { } __attribute__ ((packed)); struct packet { - unsigned int length : 16; - unsigned int code : 16; + unsigned int length; char data[0]; }; @@ -87,8 +87,8 @@ struct packet_buffer { size_t capacity; long total_packet_count, lost_packet_count; atomic_t size; - struct packet *head, *tail; - wait_queue_head_t wait; + struct packet *head, *tail; + wait_queue_head_t wait; }; struct pcilynx { @@ -106,7 +106,6 @@ struct pcilynx { struct miscdevice misc; }; - struct client { struct pcilynx *lynx; unsigned long tcode_mask; @@ -115,7 +114,7 @@ struct client { }; #define MAX_MINORS 64 -struct pcilynx *minors[MAX_MINORS]; +static struct pcilynx *minors[MAX_MINORS]; static int packet_buffer_init(struct packet_buffer *buffer, size_t capacity) @@ -128,7 +127,7 @@ packet_buffer_init(struct packet_buffer *buffer, size_t capacity) buffer->capacity = capacity; buffer->lost_packet_count = 0; atomic_set(&buffer->size, 0); - init_waitqueue_head(&buffer->wait); + init_waitqueue_head(&buffer->wait); return 0; } @@ -158,8 +157,7 @@ packet_buffer_get(struct packet_buffer *buffer, void *data, size_t user_length) if (copy_to_user(data, buffer->head->data, length)) return -EFAULT; buffer->head = (struct packet *) &buffer->head->data[length]; - } - else { + } else { size_t split = end - buffer->head->data; if (copy_to_user(data, buffer->head->data, split)) @@ -169,11 +167,12 @@ packet_buffer_get(struct packet_buffer *buffer, void *data, size_t user_length) buffer->head = (struct packet *) &buffer->data[length - split]; } - /* Decrease buffer->size as the last thing, since this is what + /* + * Decrease buffer->size as the last thing, since this is what * keeps the interrupt from overwriting the packet we are - * retrieving from the buffer. */ - - atomic_sub(sizeof (struct packet) + length, &buffer->size); + * retrieving from the buffer. + */ + atomic_sub(sizeof(struct packet) + length, &buffer->size); return length; } @@ -185,8 +184,8 @@ packet_buffer_put(struct packet_buffer *buffer, void *data, size_t length) buffer->total_packet_count++; - if (buffer->capacity < - atomic_read(&buffer->size) + sizeof (struct packet) + length) { + if (buffer->capacity < + atomic_read(&buffer->size) + sizeof(struct packet) + length) { buffer->lost_packet_count++; return; } @@ -197,69 +196,68 @@ packet_buffer_put(struct packet_buffer *buffer, void *data, size_t length) if (&buffer->tail->data[length] < end) { memcpy(buffer->tail->data, data, length); buffer->tail = (struct packet *) &buffer->tail->data[length]; - } - else { + } else { size_t split = end - buffer->tail->data; memcpy(buffer->tail->data, data, split); memcpy(buffer->data, data + split, length - split); buffer->tail = (struct packet *) &buffer->data[length - split]; } - + /* Finally, adjust buffer size and wake up userspace reader. */ - atomic_add(sizeof (struct packet) + length, &buffer->size); + atomic_add(sizeof(struct packet) + length, &buffer->size); wake_up_interruptible(&buffer->wait); } static inline void reg_write(struct pcilynx *lynx, int offset, u32 data) { - writel(data, lynx->registers + offset); + writel(data, lynx->registers + offset); } static inline u32 reg_read(struct pcilynx *lynx, int offset) { - return readl(lynx->registers + offset); + return readl(lynx->registers + offset); } static inline void reg_set_bits(struct pcilynx *lynx, int offset, u32 mask) { - reg_write(lynx, offset, (reg_read(lynx, offset) | mask)); + reg_write(lynx, offset, (reg_read(lynx, offset) | mask)); } -/* Maybe the pcl programs could be setup to just append data instead - * of using a whole packet. */ - -static inline void -run_pcl(struct pcilynx *lynx, dma_addr_t pcl_bus, int dmachan) +/* + * Maybe the pcl programs could be set up to just append data instead + * of using a whole packet. + */ +static inline void +run_pcl(struct pcilynx *lynx, dma_addr_t pcl_bus, + int dmachan) { - reg_write(lynx, DMA0_CURRENT_PCL + dmachan * 0x20, pcl_bus); - reg_write(lynx, DMA0_CHAN_CTRL + dmachan * 0x20, - DMA_CHAN_CTRL_ENABLE | DMA_CHAN_CTRL_LINK); + reg_write(lynx, DMA0_CURRENT_PCL + dmachan * 0x20, pcl_bus); + reg_write(lynx, DMA0_CHAN_CTRL + dmachan * 0x20, + DMA_CHAN_CTRL_ENABLE | DMA_CHAN_CTRL_LINK); } static int set_phy_reg(struct pcilynx *lynx, int addr, int val) { - if (addr > 15) { - debug("%s: PHY register address %d out of range", - __FUNCTION__, addr); - return -1; - } - - if (val > 0xff) { - debug("%s: PHY register value %d out of range", - __FUNCTION__, val); - return -1; - } - - reg_write(lynx, LINK_PHY, LINK_PHY_WRITE | + if (addr > 15) { + debug("PHY register address %d out of range\n", addr); + return -1; + } + + if (val > 0xff) { + debug("PHY register value %d out of range\n", val); + return -1; + } + + reg_write(lynx, LINK_PHY, LINK_PHY_WRITE | LINK_PHY_ADDR(addr) | LINK_PHY_WDATA(val)); - return 0; + return 0; } static void @@ -317,7 +315,7 @@ nosy_open(struct inode *inode, struct file *file) if (minor > MAX_MINORS || minors[minor] == NULL) return -ENODEV; - file->private_data = nosy_add_client(minors[minor]); + file->private_data = nosy_add_client(minors[minor]); if (file->private_data == NULL) return -ENOMEM; else @@ -329,7 +327,7 @@ nosy_release(struct inode *inode, struct file *file) { nosy_remove_client(file->private_data); - return 0; + return 0; } static unsigned int @@ -358,19 +356,17 @@ nosy_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { struct client *client = file->private_data; + struct nosy_stats stats; switch (cmd) { - case NOSY_IOC_GET_STATS: { - struct nosy_stats stats; - + case NOSY_IOC_GET_STATS: stats.total_packet_count = client->buffer.total_packet_count; stats.lost_packet_count = client->buffer.lost_packet_count; if (copy_to_user((void *) arg, &stats, sizeof stats)) return -EFAULT; else return 0; - } - + case NOSY_IOC_START: nosy_start_snoop(client); return 0; @@ -389,13 +385,13 @@ nosy_ioctl(struct inode *inode, struct file *file, } } -static struct file_operations nosy_ops = { +static const struct file_operations nosy_ops = { .owner = THIS_MODULE, - .read = nosy_read, + .read = nosy_read, .ioctl = nosy_ioctl, - .poll = nosy_poll, - .open = nosy_open, - .release = nosy_release, + .poll = nosy_poll, + .open = nosy_open, + .release = nosy_release, }; #define PHY_PACKET_SIZE 12 /* 1 payload, 1 inverse, 1 ack = 3 quadlets */ @@ -412,7 +408,6 @@ static void packet_handler(struct pcilynx *lynx) { unsigned long flags; - struct list_head *pos; struct client *client; unsigned long tcode_mask; size_t length; @@ -434,12 +429,10 @@ packet_handler(struct pcilynx *lynx) spin_lock_irqsave(&lynx->client_list_lock, flags); - list_for_each(pos, &lynx->client_list) { - client = list_entry(pos, struct client, link); + list_for_each_entry(client, &lynx->client_list, link) if (client->tcode_mask & tcode_mask) - packet_buffer_put(&client->buffer, + packet_buffer_put(&client->buffer, lynx->rcv_buffer, length + 4); - } spin_unlock_irqrestore(&lynx->client_list_lock, flags); } @@ -448,7 +441,6 @@ static void bus_reset_handler(struct pcilynx *lynx) { unsigned long flags; - struct list_head *pos; struct client *client; struct timeval tv; @@ -456,23 +448,19 @@ bus_reset_handler(struct pcilynx *lynx) spin_lock_irqsave(&lynx->client_list_lock, flags); - list_for_each(pos, &lynx->client_list) { - client = list_entry(pos, struct client, link); + list_for_each_entry(client, &lynx->client_list, link) packet_buffer_put(&client->buffer, &tv.tv_usec, 4); - } spin_unlock_irqrestore(&lynx->client_list_lock, flags); } - - static irqreturn_t irq_handler(int irq, void *device) { - struct pcilynx *lynx = (struct pcilynx *) device; + struct pcilynx *lynx = device; u32 pci_int_status; - - pci_int_status = reg_read(lynx, PCI_INT_STATUS); + + pci_int_status = reg_read(lynx, PCI_INT_STATUS); if ((pci_int_status & PCI_INT_INT_PEND) == 0) /* Not our interrupt, bail out quickly. */ @@ -505,19 +493,19 @@ irq_handler(int irq, void *device) static void remove_card(struct pci_dev *dev) { - struct pcilynx *lynx; + struct pcilynx *lynx; - lynx = pci_get_drvdata(dev); - if (!lynx) + lynx = pci_get_drvdata(dev); + if (!lynx) return; - pci_set_drvdata(dev, NULL); + pci_set_drvdata(dev, NULL); reg_write(lynx, PCI_INT_ENABLE, 0); free_irq(lynx->pci_device->irq, lynx); - pci_free_consistent(lynx->pci_device, sizeof (struct pcl), + pci_free_consistent(lynx->pci_device, sizeof(struct pcl), lynx->rcv_start_pcl, lynx->rcv_start_pcl_bus); - pci_free_consistent(lynx->pci_device, sizeof (struct pcl), + pci_free_consistent(lynx->pci_device, sizeof(struct pcl), lynx->rcv_pcl, lynx->rcv_pcl_bus); pci_free_consistent(lynx->pci_device, PAGE_SIZE, lynx->rcv_buffer, lynx->rcv_buffer_bus); @@ -532,64 +520,58 @@ remove_card(struct pci_dev *dev) #define RCV_BUFFER_SIZE (16 * 1024) -#define FAIL(s, args...) \ - do { \ - error(s, ## args); \ - return err; \ - } while (0) - static int __devinit add_card(struct pci_dev *dev, const struct pci_device_id *unused) { - struct pcilynx *lynx; + struct pcilynx *lynx; u32 p, end; - int err, i; - - err = -ENXIO; + int i; - if (pci_set_dma_mask(dev, 0xffffffff)) - FAIL("DMA address limits not supported " - "for PCILynx hardware.\n"); - if (pci_enable_device(dev)) - FAIL("Failed to enable PCILynx hardware.\n"); - pci_set_master(dev); - - err = -ENOMEM; + if (pci_set_dma_mask(dev, 0xffffffff)) { + error("DMA address limits not supported " + "for PCILynx hardware\n"); + return -ENXIO; + } + if (pci_enable_device(dev)) { + error("Failed to enable PCILynx hardware\n"); + return -ENXIO; + } + pci_set_master(dev); lynx = kzalloc(sizeof *lynx, GFP_KERNEL); - if (lynx == NULL) - FAIL("Failed to allocate control structure memory.\n"); - - lynx->pci_device = dev; - pci_set_drvdata(dev, lynx); + if (lynx == NULL) { + error("Failed to allocate control structure memory\n"); + return -ENOMEM; + } + lynx->pci_device = dev; + pci_set_drvdata(dev, lynx); spin_lock_init(&lynx->client_list_lock); INIT_LIST_HEAD(&lynx->client_list); - lynx->registers = ioremap_nocache(pci_resource_start(dev, 0), - PCILYNX_MAX_REGISTER); - - lynx->rcv_start_pcl = pci_alloc_consistent(lynx->pci_device, - sizeof(struct pcl), - &lynx->rcv_start_pcl_bus); - lynx->rcv_pcl = pci_alloc_consistent(lynx->pci_device, - sizeof(struct pcl), - &lynx->rcv_pcl_bus); - lynx->rcv_buffer = pci_alloc_consistent(lynx->pci_device, RCV_BUFFER_SIZE, - &lynx->rcv_buffer_bus); - if (lynx->rcv_start_pcl == NULL || + lynx->registers = ioremap_nocache(pci_resource_start(dev, 0), + PCILYNX_MAX_REGISTER); + + lynx->rcv_start_pcl = pci_alloc_consistent(lynx->pci_device, + sizeof(struct pcl), &lynx->rcv_start_pcl_bus); + lynx->rcv_pcl = pci_alloc_consistent(lynx->pci_device, + sizeof(struct pcl), &lynx->rcv_pcl_bus); + lynx->rcv_buffer = pci_alloc_consistent(lynx->pci_device, + RCV_BUFFER_SIZE, &lynx->rcv_buffer_bus); + if (lynx->rcv_start_pcl == NULL || lynx->rcv_pcl == NULL || - lynx->rcv_buffer == NULL) + lynx->rcv_buffer == NULL) { /* FIXME: do proper error handling. */ - FAIL("Failed to allocate receive buffer.\n"); - + error("Failed to allocate receive buffer\n"); + return -ENOMEM; + } lynx->rcv_start_pcl->next = lynx->rcv_pcl_bus; - lynx->rcv_pcl->next = PCL_NEXT_INVALID; - lynx->rcv_pcl->async_error_next = PCL_NEXT_INVALID; + lynx->rcv_pcl->next = PCL_NEXT_INVALID; + lynx->rcv_pcl->async_error_next = PCL_NEXT_INVALID; - lynx->rcv_pcl->buffer[0].control = + lynx->rcv_pcl->buffer[0].control = PCL_CMD_RCV | PCL_BIGENDIAN | 2044; - lynx->rcv_pcl->buffer[0].pointer = lynx->rcv_buffer_bus + 4; + lynx->rcv_pcl->buffer[0].pointer = lynx->rcv_buffer_bus + 4; p = lynx->rcv_buffer_bus + 2048; end = lynx->rcv_buffer_bus + RCV_BUFFER_SIZE; for (i = 1; p < end; i++, p += 2048) { @@ -599,31 +581,31 @@ add_card(struct pci_dev *dev, const struct pci_device_id *unused) } lynx->rcv_pcl->buffer[i - 1].control |= PCL_LAST_BUFF; - reg_set_bits(lynx, MISC_CONTROL, MISC_CONTROL_SWRESET); - /* Fix buggy cards with autoboot pin not tied low: */ - reg_write(lynx, DMA0_CHAN_CTRL, 0); - reg_write(lynx, DMA_GLOBAL_REGISTER, 0x00 << 24); + reg_set_bits(lynx, MISC_CONTROL, MISC_CONTROL_SWRESET); + /* Fix buggy cards with autoboot pin not tied low: */ + reg_write(lynx, DMA0_CHAN_CTRL, 0); + reg_write(lynx, DMA_GLOBAL_REGISTER, 0x00 << 24); #if 0 - /* now, looking for PHY register set */ - if ((get_phy_reg(lynx, 2) & 0xe0) == 0xe0) { - lynx->phyic.reg_1394a = 1; - PRINT(KERN_INFO, lynx->id, - "found 1394a conform PHY (using extended register set)"); - lynx->phyic.vendor = get_phy_vendorid(lynx); - lynx->phyic.product = get_phy_productid(lynx); - } else { - lynx->phyic.reg_1394a = 0; - PRINT(KERN_INFO, lynx->id, "found old 1394 PHY"); - } + /* now, looking for PHY register set */ + if ((get_phy_reg(lynx, 2) & 0xe0) == 0xe0) { + lynx->phyic.reg_1394a = 1; + PRINT(KERN_INFO, lynx->id, + "found 1394a conform PHY (using extended register set)"); + lynx->phyic.vendor = get_phy_vendorid(lynx); + lynx->phyic.product = get_phy_productid(lynx); + } else { + lynx->phyic.reg_1394a = 0; + PRINT(KERN_INFO, lynx->id, "found old 1394 PHY"); + } #endif /* Setup the general receive FIFO max size. */ reg_write(lynx, FIFO_SIZES, 255); - reg_set_bits(lynx, PCI_INT_ENABLE, PCI_INT_DMA_ALL); + reg_set_bits(lynx, PCI_INT_ENABLE, PCI_INT_DMA_ALL); - reg_write(lynx, LINK_INT_ENABLE, + reg_write(lynx, LINK_INT_ENABLE, LINK_INT_PHY_TIME_OUT | LINK_INT_PHY_REG_RCVD | LINK_INT_PHY_BUSRESET | LINK_INT_IT_STUCK | LINK_INT_AT_STUCK | LINK_INT_SNTRJ | @@ -638,58 +620,60 @@ add_card(struct pci_dev *dev, const struct pci_device_id *unused) run_pcl(lynx, lynx->rcv_start_pcl_bus, 0); - if (request_irq(dev->irq, irq_handler, IRQF_SHARED, driver_name, lynx)) - FAIL("Failed to allocate shared interrupt %d.", dev->irq); + if (request_irq(dev->irq, irq_handler, IRQF_SHARED, + driver_name, lynx)) { + error("Failed to allocate shared interrupt %d\n", dev->irq); + return -EIO; + } lynx->misc.parent = &dev->dev; lynx->misc.minor = MISC_DYNAMIC_MINOR; lynx->misc.name = "nosy"; lynx->misc.fops = &nosy_ops; - if (misc_register(&lynx->misc)) - FAIL("Failed to register misc char device."); + if (misc_register(&lynx->misc)) { + error("Failed to register misc char device\n"); + return -ENOMEM; + } minors[lynx->misc.minor] = lynx; notify("Initialized PCILynx IEEE1394 card, irq=%d\n", dev->irq); - return 0; + return 0; } static struct pci_device_id pci_table[] __devinitdata = { { - .vendor = PCI_VENDOR_ID_TI, - .device = PCI_DEVICE_ID_TI_PCILYNX, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, + .vendor = PCI_VENDOR_ID_TI, + .device = PCI_DEVICE_ID_TI_PCILYNX, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, }, { } /* Terminating entry */ }; static struct pci_driver lynx_pci_driver = { - .name = (char *) driver_name, + .name = driver_name, .id_table = pci_table, .probe = add_card, - .remove = __devexit_p(remove_card), + .remove = remove_card, }; -MODULE_AUTHOR("Kristian Høgsberg"); +MODULE_AUTHOR("Kristian Hoegsberg"); MODULE_DESCRIPTION("Snoop mode driver for TI pcilynx 1394 controllers"); MODULE_LICENSE("GPL"); MODULE_DEVICE_TABLE(pci, pci_table); static int __init nosy_init(void) { - /* notify("Loaded %s version %s.\n", driver_name, VERSION); */ - - return pci_register_driver(&lynx_pci_driver); + return pci_register_driver(&lynx_pci_driver); } static void __exit nosy_cleanup(void) { - pci_unregister_driver(&lynx_pci_driver); + pci_unregister_driver(&lynx_pci_driver); notify("Unloaded %s.\n", driver_name); } - module_init(nosy_init); module_exit(nosy_cleanup); diff --git a/drivers/firewire/nosy.h b/drivers/firewire/nosy.h index 3440071ac0d2..078ff27f4756 100644 --- a/drivers/firewire/nosy.h +++ b/drivers/firewire/nosy.h @@ -1,4 +1,5 @@ -/* Chip register definitions for PCILynx chipset. Based on pcilynx.h +/* + * Chip register definitions for PCILynx chipset. Based on pcilynx.h * from the Linux 1394 drivers, but modified a bit so the names here * match the specification exactly (even though they have weird names, * like xxx_OVER_FLOW, or arbitrary abbreviations like SNTRJ for "sent @@ -16,7 +17,7 @@ #define SERIAL_EEPROM_CONTROL 0x44 #define PCI_INT_STATUS 0x48 -#define PCI_INT_ENABLE 0x4c +#define PCI_INT_ENABLE 0x4c /* status and enable have identical bit numbers */ #define PCI_INT_INT_PEND (1<<31) #define PCI_INT_FRC_INT (1<<30) @@ -48,7 +49,7 @@ #define LBUS_ADDR_SEL_RAM (0x0<<16) #define LBUS_ADDR_SEL_ROM (0x1<<16) #define LBUS_ADDR_SEL_AUX (0x2<<16) -#define LBUS_ADDR_SEL_ZV (0x3<<16) +#define LBUS_ADDR_SEL_ZV (0x3<<16) #define GPIO_CTRL_A 0xb8 #define GPIO_CTRL_B 0xbc @@ -90,14 +91,14 @@ #define PCL_BIGENDIAN (1<<16) #define PCL_ISOMODE (1<<12) -#define DMA0_PREV_PCL 0x100 +#define DMA0_PREV_PCL 0x100 #define DMA1_PREV_PCL 0x120 #define DMA2_PREV_PCL 0x140 #define DMA3_PREV_PCL 0x160 #define DMA4_PREV_PCL 0x180 #define DMA_PREV_PCL(chan) (DMA_BREG(DMA0_PREV_PCL, chan)) -#define DMA0_CURRENT_PCL 0x104 +#define DMA0_CURRENT_PCL 0x104 #define DMA1_CURRENT_PCL 0x124 #define DMA2_CURRENT_PCL 0x144 #define DMA3_CURRENT_PCL 0x164 @@ -118,15 +119,14 @@ #define DMA_CHAN_STAT_PKTCMPL (1<<27) #define DMA_CHAN_STAT_SPECIALACK (1<<14) - -#define DMA0_CHAN_CTRL 0x110 +#define DMA0_CHAN_CTRL 0x110 #define DMA1_CHAN_CTRL 0x130 #define DMA2_CHAN_CTRL 0x150 #define DMA3_CHAN_CTRL 0x170 #define DMA4_CHAN_CTRL 0x190 #define DMA_CHAN_CTRL(chan) (DMA_BREG(DMA0_CHAN_CTRL, chan)) /* CHAN_CTRL registers share bits */ -#define DMA_CHAN_CTRL_ENABLE (1<<31) +#define DMA_CHAN_CTRL_ENABLE (1<<31) #define DMA_CHAN_CTRL_BUSY (1<<30) #define DMA_CHAN_CTRL_LINK (1<<29) @@ -153,28 +153,28 @@ #define DMA2_WORD0_CMP_VALUE 0xb20 #define DMA3_WORD0_CMP_VALUE 0xb30 #define DMA4_WORD0_CMP_VALUE 0xb40 -#define DMA_WORD0_CMP_VALUE(chan) (DMA_SREG(DMA0_WORD0_CMP_VALUE, chan)) +#define DMA_WORD0_CMP_VALUE(chan) (DMA_SREG(DMA0_WORD0_CMP_VALUE, chan)) #define DMA0_WORD0_CMP_ENABLE 0xb04 #define DMA1_WORD0_CMP_ENABLE 0xb14 #define DMA2_WORD0_CMP_ENABLE 0xb24 #define DMA3_WORD0_CMP_ENABLE 0xb34 #define DMA4_WORD0_CMP_ENABLE 0xb44 -#define DMA_WORD0_CMP_ENABLE(chan) (DMA_SREG(DMA0_WORD0_CMP_ENABLE,chan)) +#define DMA_WORD0_CMP_ENABLE(chan) (DMA_SREG(DMA0_WORD0_CMP_ENABLE, chan)) #define DMA0_WORD1_CMP_VALUE 0xb08 #define DMA1_WORD1_CMP_VALUE 0xb18 #define DMA2_WORD1_CMP_VALUE 0xb28 #define DMA3_WORD1_CMP_VALUE 0xb38 #define DMA4_WORD1_CMP_VALUE 0xb48 -#define DMA_WORD1_CMP_VALUE(chan) (DMA_SREG(DMA0_WORD1_CMP_VALUE, chan)) +#define DMA_WORD1_CMP_VALUE(chan) (DMA_SREG(DMA0_WORD1_CMP_VALUE, chan)) #define DMA0_WORD1_CMP_ENABLE 0xb0c #define DMA1_WORD1_CMP_ENABLE 0xb1c #define DMA2_WORD1_CMP_ENABLE 0xb2c #define DMA3_WORD1_CMP_ENABLE 0xb3c #define DMA4_WORD1_CMP_ENABLE 0xb4c -#define DMA_WORD1_CMP_ENABLE(chan) (DMA_SREG(DMA0_WORD1_CMP_ENABLE,chan)) +#define DMA_WORD1_CMP_ENABLE(chan) (DMA_SREG(DMA0_WORD1_CMP_ENABLE, chan)) /* word 1 compare enable flags */ #define DMA_WORD1_CMP_MATCH_OTHERBUS (1<<15) #define DMA_WORD1_CMP_MATCH_BROADCAST (1<<14) @@ -211,7 +211,6 @@ #define LINK_PHY_WDATA(data) (data<<16) #define LINK_PHY_RADDR(addr) (addr<<8) - #define LINK_INT_STATUS 0xf14 #define LINK_INT_ENABLE 0xf18 /* status and enable have identical bit numbers */ @@ -235,4 +234,4 @@ #define LINK_INT_GRF_OVER_FLOW (1<<5) #define LINK_INT_ITF_UNDER_FLOW (1<<4) #define LINK_INT_ATF_UNDER_FLOW (1<<3) -#define LINK_INT_IARB_FAILED (1<<0) +#define LINK_INT_IARB_FAILED (1<<0) -- cgit v1.2.3 From c7b2a99c66e7b40d8843a70f2981e375eeedf062 Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Thu, 22 Jul 2010 11:56:38 +0200 Subject: firewire: nosy: convert to unlocked ioctl The required serialization of NOSY_IOC_START and NOSY_IOC_STOP is already provided by the client_list_lock. NOSY_IOC_FILTER does not really require serialization since accesses to tcode_mask are atomic on any sane CPU architecture. Nevertheless, make it explicit that we want this to be atomic by means of client_list_lock (which also surrounds the other tcode_mask access in the IRQ handler). While we are at it, change the type of tcode_mask to u32 for consistency with the user API. NOSY_IOC_GET_STATS does not require serialization against itself. But there is a bug here regarding concurrent updates of the two counters by the IRQ handler. Fix it by taking the client_list_lock in this ioctl too. Signed-off-by: Stefan Richter --- drivers/firewire/nosy.c | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) (limited to 'drivers/firewire') diff --git a/drivers/firewire/nosy.c b/drivers/firewire/nosy.c index ea392d0985a5..6470514190d5 100644 --- a/drivers/firewire/nosy.c +++ b/drivers/firewire/nosy.c @@ -108,7 +108,7 @@ struct pcilynx { struct client { struct pcilynx *lynx; - unsigned long tcode_mask; + u32 tcode_mask; struct packet_buffer buffer; struct list_head link; }; @@ -351,17 +351,20 @@ nosy_read(struct file *file, char *buffer, size_t count, loff_t *offset) return packet_buffer_get(&client->buffer, buffer, count); } -static int -nosy_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) +static long +nosy_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { struct client *client = file->private_data; + spinlock_t *client_list_lock = &client->lynx->client_list_lock; struct nosy_stats stats; switch (cmd) { case NOSY_IOC_GET_STATS: + spin_lock_irq(client_list_lock); stats.total_packet_count = client->buffer.total_packet_count; - stats.lost_packet_count = client->buffer.lost_packet_count; + stats.lost_packet_count = client->buffer.lost_packet_count; + spin_unlock_irq(client_list_lock); + if (copy_to_user((void *) arg, &stats, sizeof stats)) return -EFAULT; else @@ -376,7 +379,9 @@ nosy_ioctl(struct inode *inode, struct file *file, return 0; case NOSY_IOC_FILTER: + spin_lock_irq(client_list_lock); client->tcode_mask = arg; + spin_unlock_irq(client_list_lock); return 0; default: @@ -386,12 +391,12 @@ nosy_ioctl(struct inode *inode, struct file *file, } static const struct file_operations nosy_ops = { - .owner = THIS_MODULE, - .read = nosy_read, - .ioctl = nosy_ioctl, - .poll = nosy_poll, - .open = nosy_open, - .release = nosy_release, + .owner = THIS_MODULE, + .read = nosy_read, + .unlocked_ioctl = nosy_ioctl, + .poll = nosy_poll, + .open = nosy_open, + .release = nosy_release, }; #define PHY_PACKET_SIZE 12 /* 1 payload, 1 inverse, 1 ack = 3 quadlets */ @@ -409,7 +414,7 @@ packet_handler(struct pcilynx *lynx) { unsigned long flags; struct client *client; - unsigned long tcode_mask; + u32 tcode_mask; size_t length; struct link_packet *packet; struct timeval tv; -- cgit v1.2.3 From a2d39db9dec0e7e403f54c9cf98b7dbc82b4c44a Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Thu, 22 Jul 2010 11:56:38 +0200 Subject: firewire: nosy: fix list corruption by NOSY_IOC_STOP nosy_stop_snoop() would blow up the second time it was called without nosy_start_snoop() in between. Signed-off-by: Stefan Richter --- drivers/firewire/nosy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/firewire') diff --git a/drivers/firewire/nosy.c b/drivers/firewire/nosy.c index 6470514190d5..637e51485a70 100644 --- a/drivers/firewire/nosy.c +++ b/drivers/firewire/nosy.c @@ -276,7 +276,7 @@ nosy_stop_snoop(struct client *client) unsigned long flags; spin_lock_irqsave(&client->lynx->client_list_lock, flags); - list_del(&client->link); + list_del_init(&client->link); spin_unlock_irqrestore(&client->lynx->client_list_lock, flags); } -- cgit v1.2.3 From 685c3f80b6d88478a6428676f9daab59faf3cd4b Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Thu, 22 Jul 2010 11:56:38 +0200 Subject: firewire: nosy: use flagless variants of spinlock accessors nosy_start/stop_snoop() are always only called by the ioctl method, i.e. with IRQs enabled. packet_handler() and bus_reset_handler() are always only called by the IRQ handler. Hence neither one needs to track IRQ flags. To underline the call context of packet_handler() and bus_reset_handler(), rename these functions to *_irq_handler(). Signed-off-by: Stefan Richter --- drivers/firewire/nosy.c | 30 ++++++++++++------------------ 1 file changed, 12 insertions(+), 18 deletions(-) (limited to 'drivers/firewire') diff --git a/drivers/firewire/nosy.c b/drivers/firewire/nosy.c index 637e51485a70..2357e170e930 100644 --- a/drivers/firewire/nosy.c +++ b/drivers/firewire/nosy.c @@ -263,21 +263,17 @@ set_phy_reg(struct pcilynx *lynx, int addr, int val) static void nosy_start_snoop(struct client *client) { - unsigned long flags; - - spin_lock_irqsave(&client->lynx->client_list_lock, flags); + spin_lock_irq(&client->lynx->client_list_lock); list_add_tail(&client->link, &client->lynx->client_list); - spin_unlock_irqrestore(&client->lynx->client_list_lock, flags); + spin_unlock_irq(&client->lynx->client_list_lock); } static void nosy_stop_snoop(struct client *client) { - unsigned long flags; - - spin_lock_irqsave(&client->lynx->client_list_lock, flags); + spin_lock_irq(&client->lynx->client_list_lock); list_del_init(&client->link); - spin_unlock_irqrestore(&client->lynx->client_list_lock, flags); + spin_unlock_irq(&client->lynx->client_list_lock); } static struct client * @@ -410,9 +406,8 @@ struct link_packet { }; static void -packet_handler(struct pcilynx *lynx) +packet_irq_handler(struct pcilynx *lynx) { - unsigned long flags; struct client *client; u32 tcode_mask; size_t length; @@ -432,31 +427,30 @@ packet_handler(struct pcilynx *lynx) else tcode_mask = 1 << packet->tcode; - spin_lock_irqsave(&lynx->client_list_lock, flags); + spin_lock(&lynx->client_list_lock); list_for_each_entry(client, &lynx->client_list, link) if (client->tcode_mask & tcode_mask) packet_buffer_put(&client->buffer, lynx->rcv_buffer, length + 4); - spin_unlock_irqrestore(&lynx->client_list_lock, flags); + spin_unlock(&lynx->client_list_lock); } static void -bus_reset_handler(struct pcilynx *lynx) +bus_reset_irq_handler(struct pcilynx *lynx) { - unsigned long flags; struct client *client; struct timeval tv; do_gettimeofday(&tv); - spin_lock_irqsave(&lynx->client_list_lock, flags); + spin_lock(&lynx->client_list_lock); list_for_each_entry(client, &lynx->client_list, link) packet_buffer_put(&client->buffer, &tv.tv_usec, 4); - spin_unlock_irqrestore(&lynx->client_list_lock, flags); + spin_unlock(&lynx->client_list_lock); } static irqreturn_t @@ -478,7 +472,7 @@ irq_handler(int irq, void *device) reg_write(lynx, LINK_INT_STATUS, link_int_status); if ((link_int_status & LINK_INT_PHY_BUSRESET) > 0) - bus_reset_handler(lynx); + bus_reset_irq_handler(lynx); } /* Clear the PCI_INT_STATUS register only after clearing the @@ -488,7 +482,7 @@ irq_handler(int irq, void *device) reg_write(lynx, PCI_INT_STATUS, pci_int_status); if ((pci_int_status & PCI_INT_DMA0_HLT) > 0) { - packet_handler(lynx); + packet_irq_handler(lynx); run_pcl(lynx, lynx->rcv_start_pcl_bus, 0); } -- cgit v1.2.3 From 55e77c06c6017a70630cf599770369b8ba07c841 Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Thu, 22 Jul 2010 11:56:38 +0200 Subject: firewire: nosy: unroll some simple functions nosy_start/stop_snoop() and nosy_add/remove_client() are simple enough to be inlined into their callers. Signed-off-by: Stefan Richter --- drivers/firewire/nosy.c | 75 +++++++++++++++++++------------------------------ 1 file changed, 29 insertions(+), 46 deletions(-) (limited to 'drivers/firewire') diff --git a/drivers/firewire/nosy.c b/drivers/firewire/nosy.c index 2357e170e930..57a1100f8f4e 100644 --- a/drivers/firewire/nosy.c +++ b/drivers/firewire/nosy.c @@ -260,68 +260,44 @@ set_phy_reg(struct pcilynx *lynx, int addr, int val) return 0; } -static void -nosy_start_snoop(struct client *client) -{ - spin_lock_irq(&client->lynx->client_list_lock); - list_add_tail(&client->link, &client->lynx->client_list); - spin_unlock_irq(&client->lynx->client_list_lock); -} - -static void -nosy_stop_snoop(struct client *client) -{ - spin_lock_irq(&client->lynx->client_list_lock); - list_del_init(&client->link); - spin_unlock_irq(&client->lynx->client_list_lock); -} - -static struct client * -nosy_add_client(struct pcilynx *lynx) +static int +nosy_open(struct inode *inode, struct file *file) { + int minor = iminor(inode); struct client *client; + if (minor > MAX_MINORS || minors[minor] == NULL) + return -ENODEV; + client = kmalloc(sizeof *client, GFP_KERNEL); + if (client == NULL) + return -ENOMEM; + client->tcode_mask = ~0; - client->lynx = lynx; + client->lynx = minors[minor]; INIT_LIST_HEAD(&client->link); if (packet_buffer_init(&client->buffer, 128 * 1024) < 0) { kfree(client); - debug("Failed to allocate packet buffer\n"); - return NULL; + return -ENOMEM; } - return client; -} + file->private_data = client; -static void -nosy_remove_client(struct client *client) -{ - nosy_stop_snoop(client); - packet_buffer_destroy(&client->buffer); - kfree(client); + return 0; } static int -nosy_open(struct inode *inode, struct file *file) +nosy_release(struct inode *inode, struct file *file) { - int minor = iminor(inode); - - if (minor > MAX_MINORS || minors[minor] == NULL) - return -ENODEV; + struct client *client = file->private_data; - file->private_data = nosy_add_client(minors[minor]); - if (file->private_data == NULL) - return -ENOMEM; - else - return 0; -} + spin_lock_irq(&client->lynx->client_list_lock); + list_del_init(&client->link); + spin_unlock_irq(&client->lynx->client_list_lock); -static int -nosy_release(struct inode *inode, struct file *file) -{ - nosy_remove_client(file->private_data); + packet_buffer_destroy(&client->buffer); + kfree(client); return 0; } @@ -367,17 +343,24 @@ nosy_ioctl(struct file *file, unsigned int cmd, unsigned long arg) return 0; case NOSY_IOC_START: - nosy_start_snoop(client); + spin_lock_irq(client_list_lock); + list_add_tail(&client->link, &client->lynx->client_list); + spin_unlock_irq(client_list_lock); + return 0; case NOSY_IOC_STOP: - nosy_stop_snoop(client); + spin_lock_irq(client_list_lock); + list_del_init(&client->link); + spin_unlock_irq(client_list_lock); + return 0; case NOSY_IOC_FILTER: spin_lock_irq(client_list_lock); client->tcode_mask = arg; spin_unlock_irq(client_list_lock); + return 0; default: -- cgit v1.2.3 From 165476671f731b4c3d6cf401d0e1886f4a4f4a8e Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Thu, 22 Jul 2010 11:56:38 +0200 Subject: firewire: nosy: fix IRQ handler for card ejection Untested, I don't have a PCILynx CardBus card. Signed-off-by: Stefan Richter --- drivers/firewire/nosy.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/firewire') diff --git a/drivers/firewire/nosy.c b/drivers/firewire/nosy.c index 57a1100f8f4e..a241b62630b2 100644 --- a/drivers/firewire/nosy.c +++ b/drivers/firewire/nosy.c @@ -444,6 +444,10 @@ irq_handler(int irq, void *device) pci_int_status = reg_read(lynx, PCI_INT_STATUS); + if (pci_int_status == ~0) + /* Card was ejected. */ + return IRQ_NONE; + if ((pci_int_status & PCI_INT_INT_PEND) == 0) /* Not our interrupt, bail out quickly. */ return IRQ_NONE; -- cgit v1.2.3 From b6d9c125e6610591c04ca9045f641e35ce1a9226 Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Thu, 22 Jul 2010 11:56:38 +0200 Subject: firewire: nosy: handle errors in device probe and add a missing pci_disable_device() to device shutdown. Signed-off-by: Stefan Richter --- drivers/firewire/nosy.c | 40 +++++++++++++++++++++++++++++++++------- 1 file changed, 33 insertions(+), 7 deletions(-) (limited to 'drivers/firewire') diff --git a/drivers/firewire/nosy.c b/drivers/firewire/nosy.c index a241b62630b2..ccf9c461bd86 100644 --- a/drivers/firewire/nosy.c +++ b/drivers/firewire/nosy.c @@ -497,6 +497,7 @@ remove_card(struct pci_dev *dev) lynx->rcv_buffer, lynx->rcv_buffer_bus); iounmap(lynx->registers); + pci_disable_device(dev); minors[lynx->misc.minor] = NULL; misc_deregister(&lynx->misc); @@ -511,7 +512,7 @@ add_card(struct pci_dev *dev, const struct pci_device_id *unused) { struct pcilynx *lynx; u32 p, end; - int i; + int ret, i; if (pci_set_dma_mask(dev, 0xffffffff)) { error("DMA address limits not supported " @@ -527,7 +528,8 @@ add_card(struct pci_dev *dev, const struct pci_device_id *unused) lynx = kzalloc(sizeof *lynx, GFP_KERNEL); if (lynx == NULL) { error("Failed to allocate control structure memory\n"); - return -ENOMEM; + ret = -ENOMEM; + goto fail_disable; } lynx->pci_device = dev; pci_set_drvdata(dev, lynx); @@ -547,9 +549,9 @@ add_card(struct pci_dev *dev, const struct pci_device_id *unused) if (lynx->rcv_start_pcl == NULL || lynx->rcv_pcl == NULL || lynx->rcv_buffer == NULL) { - /* FIXME: do proper error handling. */ error("Failed to allocate receive buffer\n"); - return -ENOMEM; + ret = -ENOMEM; + goto fail_deallocate; } lynx->rcv_start_pcl->next = lynx->rcv_pcl_bus; lynx->rcv_pcl->next = PCL_NEXT_INVALID; @@ -609,22 +611,46 @@ add_card(struct pci_dev *dev, const struct pci_device_id *unused) if (request_irq(dev->irq, irq_handler, IRQF_SHARED, driver_name, lynx)) { error("Failed to allocate shared interrupt %d\n", dev->irq); - return -EIO; + ret = -EIO; + goto fail_deallocate; } lynx->misc.parent = &dev->dev; lynx->misc.minor = MISC_DYNAMIC_MINOR; lynx->misc.name = "nosy"; lynx->misc.fops = &nosy_ops; - if (misc_register(&lynx->misc)) { + ret = misc_register(&lynx->misc); + if (ret) { error("Failed to register misc char device\n"); - return -ENOMEM; + goto fail_free_irq; } minors[lynx->misc.minor] = lynx; notify("Initialized PCILynx IEEE1394 card, irq=%d\n", dev->irq); return 0; + +fail_free_irq: + reg_write(lynx, PCI_INT_ENABLE, 0); + free_irq(lynx->pci_device->irq, lynx); + +fail_deallocate: + if (lynx->rcv_start_pcl) + pci_free_consistent(lynx->pci_device, sizeof(struct pcl), + lynx->rcv_start_pcl, lynx->rcv_start_pcl_bus); + if (lynx->rcv_pcl) + pci_free_consistent(lynx->pci_device, sizeof(struct pcl), + lynx->rcv_pcl, lynx->rcv_pcl_bus); + if (lynx->rcv_buffer) + pci_free_consistent(lynx->pci_device, PAGE_SIZE, + lynx->rcv_buffer, lynx->rcv_buffer_bus); + iounmap(lynx->registers); + kfree(lynx); + +fail_disable: + pci_disable_device(dev); + + return ret; } static struct pci_device_id pci_table[] __devinitdata = { -- cgit v1.2.3 From 424d66cedae8bebb00fdb917fc8430f7b8a655cf Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Thu, 22 Jul 2010 11:56:38 +0200 Subject: firewire: nosy: fix device shutdown with active client Fix race between nosy_open() and remove_card() by replacing the unprotected array of card pointers by a mutex-protected list of cards. Make card instances reference-counted and let each client hold a reference. Notify clients about card removal via POLLHUP in poll()'s events bitmap; also let read() fail with errno=ENODEV if the card was removed and everything in the buffer was read. Signed-off-by: Stefan Richter --- drivers/firewire/nosy.c | 111 +++++++++++++++++++++++++++++++++++------------- 1 file changed, 82 insertions(+), 29 deletions(-) (limited to 'drivers/firewire') diff --git a/drivers/firewire/nosy.c b/drivers/firewire/nosy.c index ccf9c461bd86..edd729aafeca 100644 --- a/drivers/firewire/nosy.c +++ b/drivers/firewire/nosy.c @@ -23,8 +23,10 @@ #include #include #include +#include #include #include +#include #include #include #include /* required for linux/wait.h */ @@ -104,8 +106,30 @@ struct pcilynx { struct list_head client_list; struct miscdevice misc; + struct list_head link; + struct kref kref; }; +static inline struct pcilynx * +lynx_get(struct pcilynx *lynx) +{ + kref_get(&lynx->kref); + + return lynx; +} + +static void +lynx_release(struct kref *kref) +{ + kfree(container_of(kref, struct pcilynx, kref)); +} + +static inline void +lynx_put(struct pcilynx *lynx) +{ + kref_put(&lynx->kref, lynx_release); +} + struct client { struct pcilynx *lynx; u32 tcode_mask; @@ -113,8 +137,8 @@ struct client { struct list_head link; }; -#define MAX_MINORS 64 -static struct pcilynx *minors[MAX_MINORS]; +static DEFINE_MUTEX(card_mutex); +static LIST_HEAD(card_list); static int packet_buffer_init(struct packet_buffer *buffer, size_t capacity) @@ -139,15 +163,20 @@ packet_buffer_destroy(struct packet_buffer *buffer) } static int -packet_buffer_get(struct packet_buffer *buffer, void *data, size_t user_length) +packet_buffer_get(struct client *client, void *data, size_t user_length) { + struct packet_buffer *buffer = &client->buffer; size_t length; char *end; if (wait_event_interruptible(buffer->wait, - atomic_read(&buffer->size) > 0)) + atomic_read(&buffer->size) > 0) || + list_empty(&client->lynx->link)) return -ERESTARTSYS; + if (atomic_read(&buffer->size) == 0) + return -ENODEV; + /* FIXME: Check length <= user_length. */ end = buffer->data + buffer->capacity; @@ -265,39 +294,52 @@ nosy_open(struct inode *inode, struct file *file) { int minor = iminor(inode); struct client *client; - - if (minor > MAX_MINORS || minors[minor] == NULL) + struct pcilynx *tmp, *lynx = NULL; + + mutex_lock(&card_mutex); + list_for_each_entry(tmp, &card_list, link) + if (tmp->misc.minor == minor) { + lynx = lynx_get(tmp); + break; + } + mutex_unlock(&card_mutex); + if (lynx == NULL) return -ENODEV; client = kmalloc(sizeof *client, GFP_KERNEL); if (client == NULL) - return -ENOMEM; + goto fail; client->tcode_mask = ~0; - client->lynx = minors[minor]; + client->lynx = lynx; INIT_LIST_HEAD(&client->link); - if (packet_buffer_init(&client->buffer, 128 * 1024) < 0) { - kfree(client); - return -ENOMEM; - } + if (packet_buffer_init(&client->buffer, 128 * 1024) < 0) + goto fail; file->private_data = client; return 0; +fail: + kfree(client); + lynx_put(lynx); + + return -ENOMEM; } static int nosy_release(struct inode *inode, struct file *file) { struct client *client = file->private_data; + struct pcilynx *lynx = client->lynx; - spin_lock_irq(&client->lynx->client_list_lock); + spin_lock_irq(&lynx->client_list_lock); list_del_init(&client->link); - spin_unlock_irq(&client->lynx->client_list_lock); + spin_unlock_irq(&lynx->client_list_lock); packet_buffer_destroy(&client->buffer); kfree(client); + lynx_put(lynx); return 0; } @@ -306,13 +348,17 @@ static unsigned int nosy_poll(struct file *file, poll_table *pt) { struct client *client = file->private_data; + unsigned int ret = 0; poll_wait(file, &client->buffer.wait, pt); if (atomic_read(&client->buffer.size) > 0) - return POLLIN | POLLRDNORM; - else - return 0; + ret = POLLIN | POLLRDNORM; + + if (list_empty(&client->lynx->link)) + ret |= POLLHUP; + + return ret; } static ssize_t @@ -320,7 +366,7 @@ nosy_read(struct file *file, char *buffer, size_t count, loff_t *offset) { struct client *client = file->private_data; - return packet_buffer_get(&client->buffer, buffer, count); + return packet_buffer_get(client, buffer, count); } static long @@ -479,16 +525,22 @@ irq_handler(int irq, void *device) static void remove_card(struct pci_dev *dev) { - struct pcilynx *lynx; + struct pcilynx *lynx = pci_get_drvdata(dev); + struct client *client; - lynx = pci_get_drvdata(dev); - if (!lynx) - return; - pci_set_drvdata(dev, NULL); + mutex_lock(&card_mutex); + list_del_init(&lynx->link); + misc_deregister(&lynx->misc); + mutex_unlock(&card_mutex); reg_write(lynx, PCI_INT_ENABLE, 0); free_irq(lynx->pci_device->irq, lynx); + spin_lock_irq(&lynx->client_list_lock); + list_for_each_entry(client, &lynx->client_list, link) + wake_up_interruptible(&client->buffer.wait); + spin_unlock_irq(&lynx->client_list_lock); + pci_free_consistent(lynx->pci_device, sizeof(struct pcl), lynx->rcv_start_pcl, lynx->rcv_start_pcl_bus); pci_free_consistent(lynx->pci_device, sizeof(struct pcl), @@ -498,11 +550,7 @@ remove_card(struct pci_dev *dev) iounmap(lynx->registers); pci_disable_device(dev); - - minors[lynx->misc.minor] = NULL; - misc_deregister(&lynx->misc); - - kfree(lynx); + lynx_put(lynx); } #define RCV_BUFFER_SIZE (16 * 1024) @@ -536,6 +584,7 @@ add_card(struct pci_dev *dev, const struct pci_device_id *unused) spin_lock_init(&lynx->client_list_lock); INIT_LIST_HEAD(&lynx->client_list); + kref_init(&lynx->kref); lynx->registers = ioremap_nocache(pci_resource_start(dev, 0), PCILYNX_MAX_REGISTER); @@ -619,12 +668,16 @@ add_card(struct pci_dev *dev, const struct pci_device_id *unused) lynx->misc.minor = MISC_DYNAMIC_MINOR; lynx->misc.name = "nosy"; lynx->misc.fops = &nosy_ops; + + mutex_lock(&card_mutex); ret = misc_register(&lynx->misc); if (ret) { error("Failed to register misc char device\n"); + mutex_unlock(&card_mutex); goto fail_free_irq; } - minors[lynx->misc.minor] = lynx; + list_add_tail(&lynx->link, &card_list); + mutex_unlock(&card_mutex); notify("Initialized PCILynx IEEE1394 card, irq=%d\n", dev->irq); -- cgit v1.2.3 From c89db7b8bc88d8288dcfbe7a885b950d2560d564 Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Thu, 22 Jul 2010 11:56:38 +0200 Subject: firewire: nosy: annotate __user pointers and __iomem pointers Signed-off-by: Stefan Richter --- drivers/firewire/nosy.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/firewire') diff --git a/drivers/firewire/nosy.c b/drivers/firewire/nosy.c index edd729aafeca..b8dcaa28e1ad 100644 --- a/drivers/firewire/nosy.c +++ b/drivers/firewire/nosy.c @@ -95,7 +95,7 @@ struct packet_buffer { struct pcilynx { struct pci_dev *pci_device; - unsigned char *registers; + __iomem char *registers; struct pcl *rcv_start_pcl, *rcv_pcl; u32 *rcv_buffer; @@ -163,7 +163,7 @@ packet_buffer_destroy(struct packet_buffer *buffer) } static int -packet_buffer_get(struct client *client, void *data, size_t user_length) +packet_buffer_get(struct client *client, char __user *data, size_t user_length) { struct packet_buffer *buffer = &client->buffer; size_t length; @@ -362,7 +362,7 @@ nosy_poll(struct file *file, poll_table *pt) } static ssize_t -nosy_read(struct file *file, char *buffer, size_t count, loff_t *offset) +nosy_read(struct file *file, char __user *buffer, size_t count, loff_t *offset) { struct client *client = file->private_data; @@ -383,7 +383,7 @@ nosy_ioctl(struct file *file, unsigned int cmd, unsigned long arg) stats.lost_packet_count = client->buffer.lost_packet_count; spin_unlock_irq(client_list_lock); - if (copy_to_user((void *) arg, &stats, sizeof stats)) + if (copy_to_user((void __user *) arg, &stats, sizeof stats)) return -EFAULT; else return 0; -- cgit v1.2.3 From fd8c8d46ca9402c15383d2cf0bc3ee7740de3b62 Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Thu, 22 Jul 2010 11:56:38 +0200 Subject: firewire: nosy: endianess fixes and annotations 1.) The DMA programs (struct pcl) are PCI-endian = little endian data (except for the 3rd quadlet in a PCL which the controller does not touch). Annotate them as such. Fix all accesses of the PCL to work with big endian CPUs also. Not actually tested, I only have a little endian PC to test with. This includes replacement of a bitfield struct pcl_status by open-coded shift and mask operations. 2.) The two __attribute__ ((packed)) at struct pcl are not really required since it consists of u32/__le32 only, i.e. there will be no padding with or without the attribute. 3.) The received IEEE 1394 data are byteswapped by the controller from IEEE 1394 endian = big endian to PCI endian = little endian because the PCL_BIGENDIAN control bit is set. Therefore annotate the DMA buffer as a __le32 array. Fix the one access of the DMA buffer (the check of the transaction code of link packets) to work with big endian CPUs. Also fix the two accesses of the client bounce buffer (the reading of packet length). 4.) Add a comment to the userspace ABI header that all of the data gets out as little endian data, except for the timestamp which is CPU endian. (We could make it little endian too, but why? Vice versa, an ioctl could be added to dump packet data in big endian byte order...) Signed-off-by: Stefan Richter --- drivers/firewire/nosy-user.h | 6 ++-- drivers/firewire/nosy.c | 74 ++++++++++++++++---------------------------- 2 files changed, 29 insertions(+), 51 deletions(-) (limited to 'drivers/firewire') diff --git a/drivers/firewire/nosy-user.h b/drivers/firewire/nosy-user.h index ebef97f4ecf9..e48aa6200c72 100644 --- a/drivers/firewire/nosy-user.h +++ b/drivers/firewire/nosy-user.h @@ -17,9 +17,9 @@ struct nosy_stats { /* * Format of packets returned from the kernel driver: * - * quadlet with timestamp (microseconds) - * quadlet padded packet data... - * quadlet with ack + * quadlet with timestamp (microseconds, CPU endian) + * quadlet-padded packet data... (little endian) + * quadlet with ack (little endian) */ #endif /* __nosy_user_h */ diff --git a/drivers/firewire/nosy.c b/drivers/firewire/nosy.c index b8dcaa28e1ad..225e64956823 100644 --- a/drivers/firewire/nosy.c +++ b/drivers/firewire/nosy.c @@ -51,33 +51,19 @@ static char driver_name[] = KBUILD_MODNAME; -struct pcl_status { - unsigned int transfer_count : 13; - unsigned int reserved0 : 1; - unsigned int ack_type : 1; - unsigned int ack : 4; - unsigned int rcv_speed : 2; - unsigned int rcv_dma_channel : 6; - unsigned int packet_complete : 1; - unsigned int packet_error : 1; - unsigned int master_error : 1; - unsigned int iso_mode : 1; - unsigned int self_id : 1; -}; - /* this is the physical layout of a PCL, its size is 128 bytes */ struct pcl { - u32 next; - u32 async_error_next; - u32 user_data; - struct pcl_status pcl_status; - u32 remaining_transfer_count; - u32 next_data_buffer; - struct { - u32 control; - u32 pointer; - } buffer[13] __attribute__ ((packed)); -} __attribute__ ((packed)); + __le32 next; + __le32 async_error_next; + u32 user_data; + __le32 pcl_status; + __le32 remaining_transfer_count; + __le32 next_data_buffer; + struct { + __le32 control; + __le32 pointer; + } buffer[13]; +}; struct packet { unsigned int length; @@ -98,7 +84,7 @@ struct pcilynx { __iomem char *registers; struct pcl *rcv_start_pcl, *rcv_pcl; - u32 *rcv_buffer; + __le32 *rcv_buffer; dma_addr_t rcv_start_pcl_bus, rcv_pcl_bus, rcv_buffer_bus; @@ -426,35 +412,26 @@ static const struct file_operations nosy_ops = { #define PHY_PACKET_SIZE 12 /* 1 payload, 1 inverse, 1 ack = 3 quadlets */ -struct link_packet { - unsigned int priority : 4; - unsigned int tcode : 4; - unsigned int rt : 2; - unsigned int tlabel : 6; - unsigned int destination : 16; -}; - static void packet_irq_handler(struct pcilynx *lynx) { struct client *client; - u32 tcode_mask; + u32 tcode_mask, tcode; size_t length; - struct link_packet *packet; struct timeval tv; /* FIXME: Also report rcv_speed. */ - length = lynx->rcv_pcl->pcl_status.transfer_count; - packet = (struct link_packet *) &lynx->rcv_buffer[1]; + length = __le32_to_cpu(lynx->rcv_pcl->pcl_status) & 0x00001fff; + tcode = __le32_to_cpu(lynx->rcv_buffer[1]) >> 4 & 0xf; do_gettimeofday(&tv); - lynx->rcv_buffer[0] = tv.tv_usec; + lynx->rcv_buffer[0] = (__force __le32)tv.tv_usec; if (length == PHY_PACKET_SIZE) tcode_mask = 1 << TCODE_PHY_PACKET; else - tcode_mask = 1 << packet->tcode; + tcode_mask = 1 << tcode; spin_lock(&lynx->client_list_lock); @@ -602,21 +579,22 @@ add_card(struct pci_dev *dev, const struct pci_device_id *unused) ret = -ENOMEM; goto fail_deallocate; } - lynx->rcv_start_pcl->next = lynx->rcv_pcl_bus; - lynx->rcv_pcl->next = PCL_NEXT_INVALID; - lynx->rcv_pcl->async_error_next = PCL_NEXT_INVALID; + lynx->rcv_start_pcl->next = cpu_to_le32(lynx->rcv_pcl_bus); + lynx->rcv_pcl->next = cpu_to_le32(PCL_NEXT_INVALID); + lynx->rcv_pcl->async_error_next = cpu_to_le32(PCL_NEXT_INVALID); lynx->rcv_pcl->buffer[0].control = - PCL_CMD_RCV | PCL_BIGENDIAN | 2044; - lynx->rcv_pcl->buffer[0].pointer = lynx->rcv_buffer_bus + 4; + cpu_to_le32(PCL_CMD_RCV | PCL_BIGENDIAN | 2044); + lynx->rcv_pcl->buffer[0].pointer = + cpu_to_le32(lynx->rcv_buffer_bus + 4); p = lynx->rcv_buffer_bus + 2048; end = lynx->rcv_buffer_bus + RCV_BUFFER_SIZE; for (i = 1; p < end; i++, p += 2048) { lynx->rcv_pcl->buffer[i].control = - PCL_CMD_RCV | PCL_BIGENDIAN | 2048; - lynx->rcv_pcl->buffer[i].pointer = p; + cpu_to_le32(PCL_CMD_RCV | PCL_BIGENDIAN | 2048); + lynx->rcv_pcl->buffer[i].pointer = cpu_to_le32(p); } - lynx->rcv_pcl->buffer[i - 1].control |= PCL_LAST_BUFF; + lynx->rcv_pcl->buffer[i - 1].control |= cpu_to_le32(PCL_LAST_BUFF); reg_set_bits(lynx, MISC_CONTROL, MISC_CONTROL_SWRESET); /* Fix buggy cards with autoboot pin not tied low: */ -- cgit v1.2.3 From 7429b17d30a19fd52a0c07de9d3959746d321e15 Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Thu, 22 Jul 2010 11:56:38 +0200 Subject: firewire: nosy: use generic printk macros Replace home-grown printk wrapper macros by ones from kernel.h and device.h. Also raise the log level in set_phy_reg() from debug to error because these are really error conditions. Could even be WARN_ON. Lower the log level in the device probe and driver shutdown from notice to info. Signed-off-by: Stefan Richter --- drivers/firewire/nosy.c | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) (limited to 'drivers/firewire') diff --git a/drivers/firewire/nosy.c b/drivers/firewire/nosy.c index 225e64956823..8528b10763ed 100644 --- a/drivers/firewire/nosy.c +++ b/drivers/firewire/nosy.c @@ -17,6 +17,7 @@ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +#include #include #include #include @@ -45,10 +46,6 @@ #define TCODE_PHY_PACKET 0x10 #define PCI_DEVICE_ID_TI_PCILYNX 0x8000 -#define notify(s, args...) printk(KERN_NOTICE s, ## args) -#define error(s, args...) printk(KERN_ERR s, ## args) -#define debug(s, args...) printk(KERN_DEBUG s, ## args) - static char driver_name[] = KBUILD_MODNAME; /* this is the physical layout of a PCL, its size is 128 bytes */ @@ -260,15 +257,15 @@ static int set_phy_reg(struct pcilynx *lynx, int addr, int val) { if (addr > 15) { - debug("PHY register address %d out of range\n", addr); + dev_err(&lynx->pci_device->dev, + "PHY register address %d out of range\n", addr); return -1; } - if (val > 0xff) { - debug("PHY register value %d out of range\n", val); + dev_err(&lynx->pci_device->dev, + "PHY register value %d out of range\n", val); return -1; } - reg_write(lynx, LINK_PHY, LINK_PHY_WRITE | LINK_PHY_ADDR(addr) | LINK_PHY_WDATA(val)); @@ -540,19 +537,19 @@ add_card(struct pci_dev *dev, const struct pci_device_id *unused) int ret, i; if (pci_set_dma_mask(dev, 0xffffffff)) { - error("DMA address limits not supported " - "for PCILynx hardware\n"); + dev_err(&dev->dev, + "DMA address limits not supported for PCILynx hardware\n"); return -ENXIO; } if (pci_enable_device(dev)) { - error("Failed to enable PCILynx hardware\n"); + dev_err(&dev->dev, "Failed to enable PCILynx hardware\n"); return -ENXIO; } pci_set_master(dev); lynx = kzalloc(sizeof *lynx, GFP_KERNEL); if (lynx == NULL) { - error("Failed to allocate control structure memory\n"); + dev_err(&dev->dev, "Failed to allocate control structure\n"); ret = -ENOMEM; goto fail_disable; } @@ -575,7 +572,7 @@ add_card(struct pci_dev *dev, const struct pci_device_id *unused) if (lynx->rcv_start_pcl == NULL || lynx->rcv_pcl == NULL || lynx->rcv_buffer == NULL) { - error("Failed to allocate receive buffer\n"); + dev_err(&dev->dev, "Failed to allocate receive buffer\n"); ret = -ENOMEM; goto fail_deallocate; } @@ -637,7 +634,8 @@ add_card(struct pci_dev *dev, const struct pci_device_id *unused) if (request_irq(dev->irq, irq_handler, IRQF_SHARED, driver_name, lynx)) { - error("Failed to allocate shared interrupt %d\n", dev->irq); + dev_err(&dev->dev, + "Failed to allocate shared interrupt %d\n", dev->irq); ret = -EIO; goto fail_deallocate; } @@ -650,14 +648,15 @@ add_card(struct pci_dev *dev, const struct pci_device_id *unused) mutex_lock(&card_mutex); ret = misc_register(&lynx->misc); if (ret) { - error("Failed to register misc char device\n"); + dev_err(&dev->dev, "Failed to register misc char device\n"); mutex_unlock(&card_mutex); goto fail_free_irq; } list_add_tail(&lynx->link, &card_list); mutex_unlock(&card_mutex); - notify("Initialized PCILynx IEEE1394 card, irq=%d\n", dev->irq); + dev_info(&dev->dev, + "Initialized PCILynx IEEE1394 card, irq=%d\n", dev->irq); return 0; @@ -715,7 +714,7 @@ static void __exit nosy_cleanup(void) { pci_unregister_driver(&lynx_pci_driver); - notify("Unloaded %s.\n", driver_name); + pr_info("Unloaded %s\n", driver_name); } module_init(nosy_init); -- cgit v1.2.3 From 9f6d3c4b76314c40c866a935d78c80fd284768bd Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Thu, 22 Jul 2010 11:58:05 +0200 Subject: tools/firewire: add userspace front-end of nosy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds nosy-dump, the userspace part of nosy, the IEEE 1394 traffic sniffer for Texas Instruments PCILynx/ PCILynx2 based cards. Author is Kristian Høgsberg. The files added here are taken from git://anongit.freedesktop.org/~krh/nosy commit ee29be97 (2009-11-10) with the following changes by Stefan Richter: - Parts pertaining to the kernel module removed from Makefile. - dist target removed from the Makefile. - Mentioned nosy-dump in the Kconfig help to nosy's kernel component. - Add copyright notice to nosy-dump.c. This is a duplicate of the respective notice in the kernel component nosy.c except for a time span of 2002 - 2006, according to Kristian's git log. "git shortlog decode-fcp.c list.h nosy-dump.[ch]" from nosy's git repository: Jonathan Woithe (1): Save logs on Ctrl-C Kristian Høgsberg (11): Pull over nosy from mercurial repo. Remove some fields from default view, add logging feature. Use infinite time out for poll(), mark more detail fields. Fix byte ordering macro. Add decoding of iso data and lock packets. Add flag to indicate data length field. Add cycle start packet decoding, add --iso and --cycle-start flags. Distinguish between phy-packets and 0-length iso data. Fix transaction and stats view. Add simple AV/C decoder. Don't break down on big payloads. Signed-off-by: Stefan Richter Acked-by: Kristian Høgsberg --- drivers/firewire/Kconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/firewire') diff --git a/drivers/firewire/Kconfig b/drivers/firewire/Kconfig index c4edc34d128b..fcf3ea28340b 100644 --- a/drivers/firewire/Kconfig +++ b/drivers/firewire/Kconfig @@ -85,7 +85,8 @@ config FIREWIRE_NOSY Apple Power Mac G3 blue & white (onboard controller). To compile this driver as a module, say M here: The module will be - called nosy. + called nosy. Source code of a userspace interface to nosy, called + nosy-dump, can be found in tools/firewire/ of the kernel sources. If unsure, say N. -- cgit v1.2.3 From 071595ebdc66d70219e2d1ce746016f64b2b19e7 Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Tue, 27 Jul 2010 13:20:33 +0200 Subject: firewire: ohci: use memory barriers to order descriptor updates When we append to a DMA program, we need to ensure that the order in which initialization of the new descriptors and update of the branch_address of the old tail descriptor, as seen by the PCI device, happen as intended. Signed-off-by: Stefan Richter --- drivers/firewire/ohci.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/firewire') diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index 5f6bb2c53808..3f59d1df6504 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -595,6 +595,7 @@ static int ar_context_add_page(struct ar_context *ctx) ab->descriptor.res_count = cpu_to_le16(PAGE_SIZE - offset); ab->descriptor.branch_address = 0; + wmb(); /* finish init of new descriptors before branch_address update */ ctx->last_buffer->descriptor.branch_address = cpu_to_le32(ab_bus | 1); ctx->last_buffer->next = ab; ctx->last_buffer = ab; @@ -982,6 +983,8 @@ static void context_append(struct context *ctx, d_bus = desc->buffer_bus + (d - desc->buffer) * sizeof(*d); desc->used += (z + extra) * sizeof(*d); + + wmb(); /* finish init of new descriptors before branch_address update */ ctx->prev->branch_address = cpu_to_le32(d_bus | z); ctx->prev = find_branch_descriptor(d, z); -- cgit v1.2.3 From e5b06c077c592e7e1623641520787a3da7b7c6bf Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Wed, 28 Jul 2010 15:50:00 +0200 Subject: firewire: ohci: release channel in error path firewire-ohci keeps book of which isochronous channels are occupied by IR DMA contexts, so that there cannot be more than one context listening to a certain channel. If IR context creation failed due to an out-of-memory condition, this bookkeeping leaked a channel. Signed-off-by: Stefan Richter --- drivers/firewire/ohci.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/firewire') diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index 3f59d1df6504..2e4b425847a7 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -2309,6 +2309,7 @@ static struct fw_iso_context *ohci_allocate_iso_context(struct fw_card *card, free_page((unsigned long)ctx->header); out: spin_lock_irqsave(&ohci->lock, flags); + *channels |= 1ULL << channel; *mask |= 1 << index; spin_unlock_irqrestore(&ohci->lock, flags); -- cgit v1.2.3 From 69e61d0c07fa28a05f699723a88d49e0014019b6 Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Wed, 28 Jul 2010 23:49:45 +0200 Subject: firewire: core: remove unused code ioctl_create_iso_context enforces ctx->header_size >= 4. Signed-off-by: Stefan Richter --- drivers/firewire/core-cdev.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'drivers/firewire') diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index f40098dec14b..1a14dbf097f0 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -1048,13 +1048,9 @@ static int ioctl_queue_iso(struct client *client, union ioctl_arg *arg) * We require that header_length is a multiple of * the fixed header size, ctx->header_size. */ - if (ctx->header_size == 0) { - if (u.packet.header_length > 0) - return -EINVAL; - } else if (u.packet.header_length == 0 || - u.packet.header_length % ctx->header_size != 0) { + if (u.packet.header_length == 0 || + u.packet.header_length % ctx->header_size != 0) return -EINVAL; - } header_length = 0; } -- cgit v1.2.3 From ae2a97661482c1d0f1aa41b837da95054d0e9a1b Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Thu, 29 Jul 2010 09:31:56 +0200 Subject: firewire: core: small clarifications in core-cdev Make a note on the seemingly unused linux/sched.h. Rename an irritatingly named variable. Signed-off-by: Stefan Richter --- drivers/firewire/core-cdev.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers/firewire') diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index 1a14dbf097f0..cf989e1635e1 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -34,7 +34,7 @@ #include #include #include -#include +#include /* required for linux/wait.h */ #include #include #include @@ -993,7 +993,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, header_length; + unsigned long payload, buffer_end, transmit_header_bytes; u32 control; int count; struct { @@ -1042,7 +1042,7 @@ static int ioctl_queue_iso(struct client *client, union ioctl_arg *arg) if (ctx->type == FW_ISO_CONTEXT_TRANSMIT) { if (u.packet.header_length % 4 != 0) return -EINVAL; - header_length = u.packet.header_length; + transmit_header_bytes = u.packet.header_length; } else { /* * We require that header_length is a multiple of @@ -1051,15 +1051,15 @@ static int ioctl_queue_iso(struct client *client, union ioctl_arg *arg) if (u.packet.header_length == 0 || u.packet.header_length % ctx->header_size != 0) return -EINVAL; - header_length = 0; + transmit_header_bytes = 0; } next = (struct fw_cdev_iso_packet __user *) - &p->header[header_length / 4]; + &p->header[transmit_header_bytes / 4]; if (next > end) return -EINVAL; if (__copy_from_user - (u.packet.header, p->header, header_length)) + (u.packet.header, p->header, transmit_header_bytes)) return -EFAULT; if (u.packet.skip && ctx->type == FW_ISO_CONTEXT_TRANSMIT && u.packet.header_length + u.packet.payload_length > 0) -- 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 +++++++++++++++++++++++++++++++++---------- 4 files changed, 346 insertions(+), 97 deletions(-) (limited to 'drivers/firewire') 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, -- cgit v1.2.3 From 20802224298ce9dfd99a7e26b675fc0c8ae26cac Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Sun, 1 Aug 2010 12:23:14 +0200 Subject: firewire: core: add forgotten dummy driver methods, remove unused ones There is an at least theoretic race condition in which .start_iso etc. could still be called between when the dummy driver is bound to the card and when the children devices are being shut down. Add dummy_start_iso and friends. On the other hand, .enable, .set_config_rom, .read_csr, write_csr do not need to be implemented by the dummy driver, as commented. Signed-off-by: Stefan Richter --- drivers/firewire/core-card.c | 64 ++++++++++++++++++++++++++------------------ 1 file changed, 38 insertions(+), 26 deletions(-) (limited to 'drivers/firewire') diff --git a/drivers/firewire/core-card.c b/drivers/firewire/core-card.c index 6d1cfae6aad4..417b8a4b1af1 100644 --- a/drivers/firewire/core-card.c +++ b/drivers/firewire/core-card.c @@ -538,15 +538,13 @@ EXPORT_SYMBOL(fw_card_add); * as all IO to the card will be handled (and failed) by the dummy driver * instead of calling into the module. Only functions for iso context * shutdown still need to be provided by the card driver. + * + * .read/write_csr() should never be called anymore after the dummy driver + * was bound since they are only used within request handler context. + * .set_config_rom() is never called since the card is taken out of card_list + * before switching to the dummy driver. */ -static int dummy_enable(struct fw_card *card, - const __be32 *config_rom, size_t length) -{ - BUG(); - return -1; -} - static int dummy_read_phy_reg(struct fw_card *card, int address) { return -ENODEV; @@ -558,17 +556,6 @@ static int dummy_update_phy_reg(struct fw_card *card, int address, return -ENODEV; } -static int dummy_set_config_rom(struct fw_card *card, - const __be32 *config_rom, size_t length) -{ - /* - * We take the card out of card_list before setting the dummy - * driver, so this should never get called. - */ - BUG(); - return -1; -} - static void dummy_send_request(struct fw_card *card, struct fw_packet *packet) { packet->callback(packet, card, RCODE_CANCELLED); @@ -590,15 +577,40 @@ static int dummy_enable_phys_dma(struct fw_card *card, return -ENODEV; } +static struct fw_iso_context *dummy_allocate_iso_context(struct fw_card *card, + int type, int channel, size_t header_size) +{ + return ERR_PTR(-ENODEV); +} + +static int dummy_start_iso(struct fw_iso_context *ctx, + s32 cycle, u32 sync, u32 tags) +{ + return -ENODEV; +} + +static int dummy_set_iso_channels(struct fw_iso_context *ctx, u64 *channels) +{ + return -ENODEV; +} + +static int dummy_queue_iso(struct fw_iso_context *ctx, struct fw_iso_packet *p, + struct fw_iso_buffer *buffer, unsigned long payload) +{ + return -ENODEV; +} + static const struct fw_card_driver dummy_driver_template = { - .enable = dummy_enable, - .read_phy_reg = dummy_read_phy_reg, - .update_phy_reg = dummy_update_phy_reg, - .set_config_rom = dummy_set_config_rom, - .send_request = dummy_send_request, - .cancel_packet = dummy_cancel_packet, - .send_response = dummy_send_response, - .enable_phys_dma = dummy_enable_phys_dma, + .read_phy_reg = dummy_read_phy_reg, + .update_phy_reg = dummy_update_phy_reg, + .send_request = dummy_send_request, + .send_response = dummy_send_response, + .cancel_packet = dummy_cancel_packet, + .enable_phys_dma = dummy_enable_phys_dma, + .allocate_iso_context = dummy_allocate_iso_context, + .start_iso = dummy_start_iso, + .set_iso_channels = dummy_set_iso_channels, + .queue_iso = dummy_queue_iso, }; void fw_card_release(struct kref *kref) -- cgit v1.2.3