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/nosy.c | 695 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 695 insertions(+) create mode 100644 drivers/firewire/nosy.c (limited to 'drivers/firewire/nosy.c') 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); -- 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.c | 322 +++++++++++++++++++++++------------------------- 1 file changed, 153 insertions(+), 169 deletions(-) (limited to 'drivers/firewire/nosy.c') 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); -- 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/nosy.c') 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/nosy.c') 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/nosy.c') 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/nosy.c') 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/nosy.c') 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/nosy.c') 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/nosy.c') 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/nosy.c') 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.c | 74 +++++++++++++++++-------------------------------- 1 file changed, 26 insertions(+), 48 deletions(-) (limited to 'drivers/firewire/nosy.c') 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/nosy.c') 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