The jujumon which is more or less useful but needs manual probe insertion. Also, a couple of small fixes around DMA API. diff -urpN -X dontdiff linux-2.6.18-32.el5/drivers/firewire/fw-card.c linux-2.6.18-32.el5-test/drivers/firewire/fw-card.c --- linux-2.6.18-32.el5/drivers/firewire/fw-card.c 2007-06-26 13:57:27.000000000 -0700 +++ linux-2.6.18-32.el5-test/drivers/firewire/fw-card.c 2007-06-29 21:32:00.000000000 -0700 @@ -464,6 +464,7 @@ fw_card_add(struct fw_card *card, { u32 *config_rom; size_t length; + int rc; card->max_receive = max_receive; card->link_speed = link_speed; @@ -480,7 +481,14 @@ fw_card_add(struct fw_card *card, list_add_tail(&card->link, &card_list); mutex_unlock(&card_mutex); - return card->driver->enable(card, config_rom, length); + fw_mon_card_add(card); + + rc = card->driver->enable(card, config_rom, length); + + if (rc != 0) + fw_mon_card_remove(card); + + return rc; } EXPORT_SYMBOL(fw_card_add); @@ -562,6 +570,8 @@ fw_core_remove_card(struct fw_card *card PHY_LINK_ACTIVE | PHY_CONTENDER, 0); fw_core_initiate_bus_reset(card, 1); + fw_mon_card_remove(card); + mutex_lock(&card_mutex); list_del(&card->link); mutex_unlock(&card_mutex); diff -urpN -X dontdiff linux-2.6.18-32.el5/drivers/firewire/fw-cdev.c linux-2.6.18-32.el5-test/drivers/firewire/fw-cdev.c --- linux-2.6.18-32.el5/drivers/firewire/fw-cdev.c 2007-06-26 13:57:27.000000000 -0700 +++ linux-2.6.18-32.el5-test/drivers/firewire/fw-cdev.c 2007-06-26 14:45:28.000000000 -0700 @@ -363,7 +363,7 @@ complete_transaction(struct fw_card *car response->response.data, response->response.length); } -static ssize_t ioctl_send_request(struct client *client, void *buffer) +static int ioctl_send_request(struct client *client, void *buffer) { struct fw_device *device = client->device; struct fw_cdev_send_request *request = buffer; diff -urpN -X dontdiff linux-2.6.18-32.el5/drivers/firewire/fw-mon.c linux-2.6.18-32.el5-test/drivers/firewire/fw-mon.c --- linux-2.6.18-32.el5/drivers/firewire/fw-mon.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.18-32.el5-test/drivers/firewire/fw-mon.c 2007-07-13 11:43:59.000000000 -0700 @@ -0,0 +1,855 @@ +/* + * Copyright (c) 2007 Red Hat, Inc. + * This program is a part of Linux. See common file COPYING for terms. + */ + +#include +#include +#include +#include +#include + +#include "fw-transaction.h" +/* #include "fw-device.h" */ + +#define TAG "jujumon" + +/* XXX Temporarily inside */ +struct fw_mon { + struct list_head bus_link; + spinlock_t lock; + struct fw_card *fw_bus; /* XXX Has several connectrs... bus vs. card */ + + int text_inited; + // int bin_inited; + // struct dentry *dent_s; /* Debugging file */ + // struct dentry *dent_t; /* Text interface file */ + struct device *classdev; /* Device in jujumon class */ + + /* Ref */ + int nreaders; /* Under mon_lock AND mbus->lock */ + struct list_head r_list; /* Chain of readers (usually one) */ + struct kref ref; /* Under mon_lock */ + + /* Stats XXX Add an interface to extract them -- ioctl? */ + unsigned int cnt_events; + unsigned int cnt_text_lost; +}; + +/* + * An instance of a process which opened a file (but can fork later) + */ +struct mon_reader { + struct list_head r_link; + struct fw_mon *m_bus; + void *r_data; /* Use container_of instead? */ + + void (*rnf_event)(void *data, struct fw_mon_event_arg *arg); +}; + +#define MON_TEXT_MAX_MINOR 128 + +/* + * This limit exists to prevent OOMs when the user process stops reading. + * If jujumon were available to unprivileged processes, it might be open + * to a local DoS. But we have to keep to root in order to prevent + * password sniffing from HID devices. + */ +#define EVENT_MAX (2*PAGE_SIZE / sizeof(struct mon_event_text)) + +#define PRINTF_DFL 240 + +struct mon_event_text { + struct list_head e_link; + struct { + u64 sec; /* Works through 2038? */ + u32 usec; + } tstamp; + struct fw_mon_event_arg rec; +}; + +#define SLAB_NAME_SZ 30 +struct mon_reader_text { + kmem_cache_t *e_slab; + int nevents; + struct list_head e_list; + struct mon_reader r; /* In C, parent class can be placed anywhere */ + u64 start_seconds; + + wait_queue_head_t wait; + int printf_size; + char *printf_buf; + struct mutex printf_lock; + + char slab_name[SLAB_NAME_SZ]; +}; + +DEFINE_MUTEX(mon_lock); + +struct fw_mon mon_bus0; /* Pseudo bus meaning "all buses" */ +static LIST_HEAD(mon_buses); /* All buses we know: struct fw_mon */ + +static struct class *mon_text_class; +static dev_t mon_text_dev0; +static struct cdev mon_text_cdev; + +struct fw_mon_operations *fw_mon_ops; /* Core plug, maybe remove later? XXX */ +EXPORT_SYMBOL_GPL(fw_mon_ops); + +static void mon_text_ctor(void *, kmem_cache_t *, unsigned long); +static void mon_text_del(struct fw_mon *mbus); +static void mon_dissolve(struct fw_mon *mbus, struct fw_card *card); +static void mon_bus_drop(struct kref *r); +static void mon_bus_init(struct fw_card *card); +static void mon_reader_add(struct fw_mon *mbus, struct mon_reader *r); +static void mon_reader_del(struct fw_mon *mbus, struct mon_reader *r); +static struct fw_mon *mon_bus_lookup(unsigned int num); + +/* + */ +static void mon_text_event(void *data, struct fw_mon_event_arg *arg) +{ + struct mon_reader_text *rp = data; + struct mon_event_text *ep; + struct timeval tval; + + do_gettimeofday(&tval); + + if (rp->nevents >= EVENT_MAX || + (ep = kmem_cache_alloc(rp->e_slab, SLAB_ATOMIC)) == NULL) { + rp->r.m_bus->cnt_text_lost++; + return; + } + + memcpy(&ep->rec, arg, sizeof(struct fw_mon_event_arg)); + ep->tstamp.sec = tval.tv_sec; + ep->tstamp.usec = tval.tv_usec; + + rp->nevents++; + list_add_tail(&ep->e_link, &rp->e_list); + wake_up(&rp->wait); +} + +/* + * Stop monitoring. + */ +static void mon_stop(struct fw_mon *mbus) +{ + struct fw_card *card; + struct list_head *p; + + if (mbus == &mon_bus0) { + list_for_each (p, &mon_buses) { + mbus = list_entry(p, struct fw_mon, bus_link); + /* + * We do not change nreaders here, so rely on mon_lock. + */ + if (mbus->nreaders == 0 && (card = mbus->fw_bus) != NULL) + card->monitored = 0; + } + } else { + /* + * A stop can be called for a dissolved fw_bus in case of + * a reader staying across an rmmod foo_hcd, so test ->fw_bus. + */ + if (mon_bus0.nreaders == 0 && (card = mbus->fw_bus) != NULL) { + card->monitored = 0; + mb(); + } + } +} + +/* + * Add a Firewire card (usually by a modprobe fw_ohci) + * + * This does not return an error code because the core cannot care less + * if monitoring is not established. + */ +void fw_mon_card_add(struct fw_card *card) +{ + mon_bus_init(card); +} + +/* + * Remove a FW bus (either from rmmod foo-hcd or from a hot-remove event). + */ +void fw_mon_card_remove(struct fw_card *card) +{ + struct fw_mon *mbus = card->mon_bus; + + mutex_lock(&mon_lock); + list_del(&mbus->bus_link); + if (mbus->text_inited) + mon_text_del(mbus); + // if (mbus->bin_inited) + // mon_bin_del(mbus); + + mon_dissolve(mbus, card); + kref_put(&mbus->ref, mon_bus_drop); + mutex_unlock(&mon_lock); +} + +#if 0 /* Maybe overkill of Firewire. We link statically into core anyway... */ +static int mon_notify(struct notifier_block *self, unsigned long action, + void *dev) +{ + switch (action) { + case USB_BUS_ADD: + mon_bus_add(dev); + break; + case USB_BUS_REMOVE: + mon_bus_remove(dev); + } + return NOTIFY_OK; +} + +static struct notifier_block mon_nb = { + .notifier_call = mon_notify, +}; +#endif + +/* + * Fetch next event from the circular buffer. + */ +static struct mon_event_text *mon_text_fetch(struct mon_reader_text *rp, + struct fw_mon *mbus) +{ + struct list_head *p; + unsigned long flags; + + spin_lock_irqsave(&mbus->lock, flags); + if (list_empty(&rp->e_list)) { + spin_unlock_irqrestore(&mbus->lock, flags); + return NULL; + } + p = rp->e_list.next; + list_del(p); + --rp->nevents; + spin_unlock_irqrestore(&mbus->lock, flags); + return list_entry(p, struct mon_event_text, e_link); +} + +/* + */ +static int mon_text_open(struct inode *inode, struct file *file) +{ + struct fw_mon *mbus; + struct fw_card *card; + struct mon_reader_text *rp; + struct timeval tval; + int rc; + + mutex_lock(&mon_lock); + if ((mbus = mon_bus_lookup(iminor(inode))) == NULL) { + mutex_unlock(&mon_lock); + return -ENODEV; + } + if (mbus != &mon_bus0 && mbus->fw_bus == NULL) { + printk(KERN_ERR TAG ": consistency error on open\n"); + mutex_unlock(&mon_lock); + return -ENODEV; + } + card = mbus->fw_bus; + + rp = kzalloc(sizeof(struct mon_reader_text), GFP_KERNEL); + if (rp == NULL) { + rc = -ENOMEM; + goto err_alloc; + } + INIT_LIST_HEAD(&rp->e_list); + init_waitqueue_head(&rp->wait); + mutex_init(&rp->printf_lock); + + do_gettimeofday(&tval); + + rp->printf_size = PRINTF_DFL; + rp->printf_buf = kmalloc(rp->printf_size, GFP_KERNEL); + if (rp->printf_buf == NULL) { + rc = -ENOMEM; + goto err_alloc_pr; + } + rp->start_seconds = tval.tv_sec; + + rp->r.m_bus = mbus; + rp->r.r_data = rp; + rp->r.rnf_event = mon_text_event; + + snprintf(rp->slab_name, SLAB_NAME_SZ, "mon%dt_%lx", + card? card->index+1: 0, (long)rp); + rp->e_slab = kmem_cache_create(rp->slab_name, + sizeof(struct mon_event_text), sizeof(long), 0, + mon_text_ctor, NULL); + if (rp->e_slab == NULL) { + rc = -ENOMEM; + goto err_slab; + } + + mon_reader_add(mbus, &rp->r); + + file->private_data = rp; + mutex_unlock(&mon_lock); + return 0; + +// err_busy: +// kmem_cache_destroy(rp->e_slab); +err_slab: + kfree(rp->printf_buf); +err_alloc_pr: + kfree(rp); +err_alloc: + mutex_unlock(&mon_lock); + return rc; +} + +/* + * For simplicity, we read one record in one system call and throw out + * what does not fit. This means that the following does not work: + * dd if=/dev/jujumon/0t bs=10 + * Also, we do not allow seeks and do not bother advancing the offset. + */ +static ssize_t mon_text_read(struct file *file, char __user *buf, + size_t nbytes, loff_t *ppos) +{ + struct mon_reader_text *rp = file->private_data; + struct fw_mon *mbus = rp->r.m_bus; + DECLARE_WAITQUEUE(waita, current); + struct mon_event_text *ep; + int cnt, limit; + char *pbuf; + __le32 *ar_buf; + + add_wait_queue(&rp->wait, &waita); + set_current_state(TASK_INTERRUPTIBLE); + while ((ep = mon_text_fetch(rp, mbus)) == NULL) { + if (file->f_flags & O_NONBLOCK) { + set_current_state(TASK_RUNNING); + remove_wait_queue(&rp->wait, &waita); + return -EWOULDBLOCK; /* Same as EAGAIN in Linux */ + } + /* + * We do not count nwaiters, because ->release is supposed + * to be called when all openers are gone only. + */ + schedule(); + if (signal_pending(current)) { + remove_wait_queue(&rp->wait, &waita); + return -EINTR; + } + set_current_state(TASK_INTERRUPTIBLE); + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&rp->wait, &waita); + + mutex_lock(&rp->printf_lock); + cnt = 0; + pbuf = rp->printf_buf; + limit = rp->printf_size; + + cnt += snprintf(pbuf + cnt, limit - cnt, "%lu.%06u", + (unsigned long)(ep->tstamp.sec - rp->start_seconds), + ep->tstamp.usec); + + switch (ep->rec.type) { + case MON_ET_SBP2_CMD: + { + int i; + u32 n; + + cnt += snprintf(pbuf + cnt, limit - cnt, + " Sbp2cmd @%lx(%016llx)", + ep->rec.u.sbp2_cmd.req_virt, + ep->rec.u.sbp2_cmd.req_bus); + for (i = 0; i < 8; i++) { + n = ((u32 *)ep->rec.u.sbp2_cmd.req_virt)[i]; + cnt += snprintf(pbuf + cnt, limit - cnt, + "%c%08x", (i==0)? ' ': ':', be32_to_cpu(n)); + } + cnt += snprintf(pbuf + cnt, limit - cnt, + " %d", ep->rec.u.sbp2_cmd.sg); + cnt += snprintf(pbuf + cnt, limit - cnt, + " @%lx(%016llx)", + ep->rec.u.sbp2_cmd.ptab_virt, + ep->rec.u.sbp2_cmd.ptab_bus); + for (i = 0; i < 4; i++) { + n = ((u32 *)ep->rec.u.sbp2_cmd.ptab_virt)[i]; + cnt += snprintf(pbuf + cnt, limit - cnt, + "%c%08x", (i==0)? ' ': ':', be32_to_cpu(n)); + } + } + break; + case MON_ET_OHCI_AR: + ar_buf = &ep->rec.u.ohci_ar.buf[0]; + cnt += snprintf(pbuf + cnt, limit - cnt, + " OhciAR %p %08x %08x %08x %08x", + ep->rec.u.ohci_ar.bufp, + le32_to_cpu(ar_buf[0]), + le32_to_cpu(ar_buf[1]), + le32_to_cpu(ar_buf[2]), + le32_to_cpu(ar_buf[3])); + break; + case MON_ET_SEND_REQ: + cnt += snprintf(pbuf + cnt, limit - cnt, + " SendReq t=%d %p [%ld %ld] off=%llx h=%08x:%08x:%08x:%08x" + " @%p %08x:%08x", + ep->rec.u.send_req.tcode, + ep->rec.u.send_req.packet, + (long) ep->rec.u.send_req.hlen, + (long) ep->rec.u.send_req.plen, + ep->rec.u.send_req.offset, + ep->rec.u.send_req.hdr[0], + ep->rec.u.send_req.hdr[1], + ep->rec.u.send_req.hdr[2], + ep->rec.u.send_req.hdr[3], + ep->rec.u.send_req.payload, + ep->rec.u.send_req.pay[0], + ep->rec.u.send_req.pay[1]); + break; + case MON_ET_CORE_REQ: + cnt += snprintf(pbuf + cnt, limit - cnt, + " CoreReq a=%d %p [%ld %ld]", + ep->rec.u.core_req.ack, + ep->rec.u.core_req.packet, + (long) ep->rec.u.core_req.hlen, + (long) ep->rec.u.core_req.plen); + break; + case MON_ET_MISC: + if (ep->rec.u.misc.packet != NULL) { + cnt += snprintf(pbuf + cnt, limit - cnt, " Misc %d %p", + ep->rec.u.misc.val, + ep->rec.u.misc.packet); + } else { + cnt += snprintf(pbuf + cnt, limit - cnt, " Misc %d -", + ep->rec.u.misc.val); + } + break; + default: + cnt += snprintf(pbuf + cnt, limit - cnt, " %d", ep->rec.type); + } + + cnt += snprintf(pbuf + cnt, limit - cnt, "\n"); + + if (copy_to_user(buf, rp->printf_buf, cnt)) + cnt = -EFAULT; + mutex_unlock(&rp->printf_lock); + kmem_cache_free(rp->e_slab, ep); + return cnt; +} + +static int mon_text_release(struct inode *inode, struct file *file) +{ + struct mon_reader_text *rp = file->private_data; + struct fw_mon *mbus = rp->r.m_bus; + /* unsigned long flags; */ + struct list_head *p; + struct mon_event_text *ep; + + mutex_lock(&mon_lock); + + if (mbus->nreaders <= 0) { + printk(KERN_ERR TAG ": consistency error on close\n"); + mutex_unlock(&mon_lock); + return 0; + } + mon_reader_del(mbus, &rp->r); + + /* + * In theory, e_list is protected by mbus->lock. However, + * after mon_reader_del has finished, the following is the case: + * - we are not on reader list anymore, so new events won't be added; + * - whole mbus may be dropped if it was orphaned. + * So, we better not touch mbus. + */ + /* spin_lock_irqsave(&mbus->lock, flags); */ + while (!list_empty(&rp->e_list)) { + p = rp->e_list.next; + ep = list_entry(p, struct mon_event_text, e_link); + list_del(p); + --rp->nevents; + kmem_cache_free(rp->e_slab, ep); + } + /* spin_unlock_irqrestore(&mbus->lock, flags); */ + + kmem_cache_destroy(rp->e_slab); + kfree(rp->printf_buf); + kfree(rp); + + mutex_unlock(&mon_lock); + return 0; +} + +struct file_operations mon_fops_text = { + .owner = THIS_MODULE, + .open = mon_text_open, + .llseek = no_llseek, + .read = mon_text_read, + .release = mon_text_release, +}; + +/* + * Slab interface: constructor. + */ +static void mon_text_ctor(void *mem, kmem_cache_t *slab, unsigned long sflags) +{ + /* + * Nothing to initialize. No, really! + * So, we fill it with garbage to emulate a reused object. + */ + memset(mem, 0xe5, sizeof(struct mon_event_text)); +} + +static int mon_text_add(struct fw_mon *mbus, const struct fw_card *card) +{ + struct device *dev; + unsigned minor = card? card->index+1: 0; + + if (minor >= MON_TEXT_MAX_MINOR) + return 0; + + dev = device_create(mon_text_class, card? card->device: NULL, + MKDEV(MAJOR(mon_text_dev0), minor), "jujumon%d", minor); + if (IS_ERR(dev)) + return 0; + + mbus->classdev = dev; + return 1; +} + +static void mon_text_del(struct fw_mon *mbus) +{ + device_destroy(mon_text_class, mbus->classdev->devt); +} + +int __init mon_text_init(void) +{ + int rc; + + mon_text_class = class_create(THIS_MODULE, "jujumon"); + if (IS_ERR(mon_text_class)) { + rc = PTR_ERR(mon_text_class); + goto err_class; + } + + rc = alloc_chrdev_region(&mon_text_dev0, 0, MON_TEXT_MAX_MINOR, "jujumon"); + if (rc < 0) + goto err_dev; + + cdev_init(&mon_text_cdev, &mon_fops_text); + mon_text_cdev.owner = THIS_MODULE; + + rc = cdev_add(&mon_text_cdev, mon_text_dev0, MON_TEXT_MAX_MINOR); + if (rc < 0) + goto err_add; + + return 0; + +err_add: + unregister_chrdev_region(mon_text_dev0, MON_TEXT_MAX_MINOR); +err_dev: + class_destroy(mon_text_class); +err_class: + return rc; +} + +void mon_text_exit(void) +{ + cdev_del(&mon_text_cdev); + unregister_chrdev_region(mon_text_dev0, MON_TEXT_MAX_MINOR); + class_destroy(mon_text_class); +} + +/* + * XXX common parts: split or merge + */ + +/* + * Link a reader into the bus. + * + * This must be called with mon_lock taken because of mbus->ref. + */ +static void mon_reader_add(struct fw_mon *mbus, struct mon_reader *r) +{ + unsigned long flags; + struct list_head *p; + + spin_lock_irqsave(&mbus->lock, flags); + if (mbus->nreaders == 0) { + if (mbus == &mon_bus0) { + list_for_each (p, &mon_buses) { + struct fw_mon *m1; + m1 = list_entry(p, struct fw_mon, bus_link); + m1->fw_bus->monitored = 1; + } + } else { + mbus->fw_bus->monitored = 1; + } + } + mbus->nreaders++; + list_add_tail(&r->r_link, &mbus->r_list); + spin_unlock_irqrestore(&mbus->lock, flags); + + kref_get(&mbus->ref); +} + +/* + * Unlink reader from the bus. + * + * This is called with mon_lock taken, so we can decrement mbus->ref. + */ +static void mon_reader_del(struct fw_mon *mbus, struct mon_reader *r) +{ + unsigned long flags; + + spin_lock_irqsave(&mbus->lock, flags); + list_del(&r->r_link); + --mbus->nreaders; + if (mbus->nreaders == 0) + mon_stop(mbus); + spin_unlock_irqrestore(&mbus->lock, flags); + + kref_put(&mbus->ref, mon_bus_drop); +} + +static void mon_bus_event(struct fw_mon *mbus, struct fw_mon_event_arg *arg) +{ + unsigned long flags; + struct list_head *pos; + struct mon_reader *r; + + spin_lock_irqsave(&mbus->lock, flags); + mbus->cnt_events++; + list_for_each (pos, &mbus->r_list) { + r = list_entry(pos, struct mon_reader, r_link); + r->rnf_event(r->r_data, arg); + } + spin_unlock_irqrestore(&mbus->lock, flags); + return; +} + +static void mon_event(struct fw_card *card, struct fw_mon_event_arg *arg) +{ + struct fw_mon *mbus; + + if ((mbus = card->mon_bus) != NULL) + mon_bus_event(mbus, arg); + mon_bus_event(&mon_bus0, arg); /* XXX Pass currect card as well */ +} + +/* + * Ops + */ +static struct fw_mon_operations mon_ops_0 = { + .fw_event = mon_event, +}; + +/* + * Tear fw_card and fw_mon apart. + */ +static void mon_dissolve(struct fw_mon *mbus, struct fw_card *card) +{ + + if (card->monitored) { + card->monitored = 0; + mb(); + } + + card->mon_bus = NULL; + mbus->fw_bus = NULL; + mb(); + + /* We want synchronize_irq() here, but that needs an argument. */ +} + +/* + */ +static void mon_bus_drop(struct kref *r) +{ + struct fw_mon *mbus = container_of(r, struct fw_mon, ref); + kfree(mbus); +} + +/* + * Initialize a bus for us: + * - allocate fw_mon + * - link + */ +static void mon_bus_init(struct fw_card *card) +{ + struct fw_mon *mbus; + + if ((mbus = kzalloc(sizeof(struct fw_mon), GFP_KERNEL)) == NULL) + goto err_alloc; + kref_init(&mbus->ref); + spin_lock_init(&mbus->lock); + INIT_LIST_HEAD(&mbus->r_list); + + mbus->fw_bus = card; + card->mon_bus = mbus; + + mbus->text_inited = mon_text_add(mbus, card); + // mbus->bin_inited = mon_bin_add(mbus, ubus); + + mutex_lock(&mon_lock); + list_add_tail(&mbus->bus_link, &mon_buses); + mutex_unlock(&mon_lock); + return; + + // kfree(mbus); +err_alloc: + return; +} + +static void __init mon_bus0_init(void) +{ + struct fw_mon *mbus = &mon_bus0; + + kref_init(&mbus->ref); + spin_lock_init(&mbus->lock); + INIT_LIST_HEAD(&mbus->r_list); + + mbus->text_inited = mon_text_add(mbus, NULL); + // mbus->bin_inited = mon_bin_add(mbus, NULL); +} + +/* + * Search a bus by number. Notice that bus numbers start from one, + * which we may later use to identify "all" with zero. + * + * This function must be called with mon_lock held. + * + * This is obviously inefficient and may be revised in the future. + */ +static struct fw_mon *mon_bus_lookup(unsigned int num) +{ + struct list_head *p; + struct fw_mon *mbus; + + if (num == 0) { + return &mon_bus0; + } + list_for_each (p, &mon_buses) { + mbus = list_entry(p, struct fw_mon, bus_link); + if (mbus->fw_bus->index + 1 == num) { + return mbus; + } + } + return NULL; +} + +int __init fw_mon_init(void) +{ + int rc; + + if ((rc = mon_text_init()) != 0) + goto err_text; + // if ((rc = mon_bin_init()) != 0) + // goto err_bin; + + mon_bus0_init(); + + if (fw_mon_register(&mon_ops_0) != 0) { + printk(KERN_NOTICE TAG ": unable to register with the core\n"); + rc = -ENODEV; + goto err_reg; + } + +#if 0 + usb_register_notify(&mon_nb); +#endif + +#if 0 /* This is never needed because we are built into the core. */ + struct fw_card *card; + mutex_lock(&card_mutex); + list_for_each_entry (card, &card_list, link) { + mon_bus_init(card); + } + mutex_unlock(&card_mutex); +#endif + + return 0; + +err_reg: +// mon_bin_exit(); +// err_bin: + mon_text_exit(); +err_text: + return rc; +} + +void fw_mon_exit(void) +{ + struct fw_mon *mbus; + struct list_head *p; + + // usb_unregister_notify(&mon_nb); + fw_mon_deregister(); + + mutex_lock(&mon_lock); + + while (!list_empty(&mon_buses)) { + p = mon_buses.next; + mbus = list_entry(p, struct fw_mon, bus_link); + list_del(p); + + if (mbus->text_inited) + mon_text_del(mbus); + // if (mbus->bin_inited) + // mon_bin_del(mbus); + + /* + * This never happens, because the open/close paths in + * file level maintain module use counters and so rmmod fails + * before reaching here. However, better be safe... + */ + if (mbus->nreaders) { + printk(KERN_ERR TAG + ": Outstanding opens (%d) on fw ??, leaking...\n", + mbus->nreaders); + atomic_set(&mbus->ref.refcount, 2); /* Force leak */ + } + + mon_dissolve(mbus, mbus->fw_bus); + kref_put(&mbus->ref, mon_bus_drop); + } + + mbus = &mon_bus0; + if (mbus->text_inited) + mon_text_del(mbus); + // if (mbus->bin_inited) + // mon_bin_del(mbus); + + mutex_unlock(&mon_lock); + + mon_text_exit(); + // mon_bin_exit(); +} + +/* + * The registration is unlocked. + * We do it this way because we do not want to lock in hot paths. + */ + +int fw_mon_register(struct fw_mon_operations *ops) +{ + + if (fw_mon_ops) + return -EBUSY; + + fw_mon_ops = ops; + mb(); + return 0; +} +EXPORT_SYMBOL_GPL(fw_mon_register); + +void fw_mon_deregister(void) +{ + + if (fw_mon_ops == NULL) { + printk(KERN_ERR "firewire: monitor was not registered\n"); + return; + } + fw_mon_ops = NULL; + mb(); +} +EXPORT_SYMBOL_GPL(fw_mon_deregister); /* For ohci, sbp2, etc. */ + +MODULE_LICENSE("GPL"); diff -urpN -X dontdiff linux-2.6.18-32.el5/drivers/firewire/fw-ohci.c linux-2.6.18-32.el5-test/drivers/firewire/fw-ohci.c --- linux-2.6.18-32.el5/drivers/firewire/fw-ohci.c 2007-06-26 13:57:27.000000000 -0700 +++ linux-2.6.18-32.el5-test/drivers/firewire/fw-ohci.c 2007-07-13 17:56:56.000000000 -0700 @@ -282,6 +282,7 @@ static __le32 *handle_ar_packet(struct a struct fw_packet p; u32 status, length, tcode; + fw_mon_ohci_ar(&ohci->card, buffer); p.header[0] = le32_to_cpu(buffer[0]); p.header[1] = le32_to_cpu(buffer[1]); p.header[2] = le32_to_cpu(buffer[2]); @@ -371,8 +372,8 @@ static void ar_context_tasklet(unsigned offset = offsetof(struct ar_buffer, data); dma_unmap_single(ohci->card.device, - ab->descriptor.data_address - offset, - PAGE_SIZE, DMA_BIDIRECTIONAL); + le32_to_cpu(ab->descriptor.data_address) - offset, + PAGE_SIZE, DMA_BIDIRECTIONAL); buffer = ab; ab = ab->next; @@ -425,7 +426,7 @@ static void ar_context_run(struct ar_con size_t offset; offset = offsetof(struct ar_buffer, data); - ab_bus = ab->descriptor.data_address - offset; + ab_bus = le32_to_cpu(ab->descriptor.data_address) - offset; reg_write(ctx->ohci, COMMAND_PTR(ctx->regs), ab_bus | 1); reg_write(ctx->ohci, CONTROL_SET(ctx->regs), CONTEXT_RUN); diff -urpN -X dontdiff linux-2.6.18-32.el5/drivers/firewire/fw-sbp2.c linux-2.6.18-32.el5-test/drivers/firewire/fw-sbp2.c --- linux-2.6.18-32.el5/drivers/firewire/fw-sbp2.c 2007-06-26 13:57:27.000000000 -0700 +++ linux-2.6.18-32.el5-test/drivers/firewire/fw-sbp2.c 2007-07-13 18:18:12.000000000 -0700 @@ -225,6 +225,7 @@ struct sbp2_command_orb { struct sbp2_pointer page_table[SG_ALL]; dma_addr_t page_table_bus; + size_t page_table_size; }; /* @@ -294,6 +295,7 @@ sbp2_status_write(struct fw_card *card, size_t header_size; unsigned long flags; +/* P3 */ fw_mon_event(card, 3000000 + tcode, NULL); if (tcode != TCODE_WRITE_BLOCK_REQUEST || length == 0 || length > sizeof(status)) { fw_send_response(card, request, RCODE_TYPE_ERROR); @@ -337,6 +339,7 @@ complete_transaction(struct fw_card *car struct sbp2_orb *orb = data; unsigned long flags; +/* P3 */ fw_mon_event(card, 4000000 + (rcode & 0xFFFF), &orb->t.packet); orb->rcode = rcode; if (rcode != RCODE_COMPLETE) { spin_lock_irqsave(&card->lock, flags); @@ -401,6 +404,7 @@ complete_management_orb(struct sbp2_orb struct sbp2_management_orb *orb = (struct sbp2_management_orb *)base_orb; +/* P3 */ /* fw_mon_event(device->card, 5000000, &base_orb->t.packet); */ if (status) memcpy(&orb->status, status, sizeof(*status)); complete(&orb->done); @@ -848,6 +852,7 @@ complete_command_orb(struct sbp2_orb *ba struct scatterlist *sg; int result; +/* P3 */ fw_mon_event(device->card, 1000000 + ((status->status >> 16) & 0xffff), &orb->base.t.packet); if (status != NULL) { if (STATUS_GET_DEAD(*status)) sbp2_agent_reset(unit); @@ -887,21 +892,20 @@ complete_command_orb(struct sbp2_orb *ba orb->cmd->sc_data_direction); } - if (orb->page_table_bus != 0) + if (orb->page_table_size != 0) dma_unmap_single(device->card->device, orb->page_table_bus, - sizeof(orb->page_table_bus), DMA_TO_DEVICE); + orb->page_table_size, DMA_TO_DEVICE); +/* P3 */ fw_mon_event(device->card, 2000000 + result, &orb->base.t.packet); orb->cmd->result = result; orb->done(orb->cmd); kfree(orb); } -static int sbp2_command_orb_map_scatterlist(struct sbp2_command_orb *orb) +static int sbp2_command_orb_map_scatterlist(struct sbp2_command_orb *orb, + struct sbp2_device *sd) { - struct sbp2_device *sd = - (struct sbp2_device *)orb->cmd->device->host->hostdata; - struct fw_unit *unit = sd->unit; - struct fw_device *device = fw_device(unit->device.parent); + struct fw_device *device = fw_device(sd->unit->device.parent); struct scatterlist *sg; int sg_len, l, i, j, count; size_t size; @@ -955,6 +959,7 @@ static int sbp2_command_orb_map_scatterl * on other nodes so we need to put our ID in descriptor.high. */ + orb->page_table_size = size; orb->page_table_bus = dma_map_single(device->card->device, orb->page_table, size, DMA_TO_DEVICE); @@ -1034,7 +1039,7 @@ static int sbp2_scsi_queuecommand(struct orb->request.misc |= COMMAND_ORB_DIRECTION(SBP2_DIRECTION_TO_MEDIA); - if (cmd->use_sg && sbp2_command_orb_map_scatterlist(orb) < 0) + if (cmd->use_sg && sbp2_command_orb_map_scatterlist(orb, sd) < 0) goto fail_map_payload; fw_memcpy_to_be32(&orb->request, &orb->request, sizeof(orb->request)); @@ -1044,6 +1049,9 @@ static int sbp2_scsi_queuecommand(struct orb->base.callback = complete_command_orb; + fw_mon_sbp2_cmd(device->card, &orb->request, orb->base.request_bus, + cmd->use_sg, cmd->request_buffer, + orb->page_table, orb->page_table_bus); sbp2_send_orb(&orb->base, unit, sd->node_id, sd->generation, sd->command_block_agent_address + SBP2_ORB_POINTER); @@ -1168,7 +1176,7 @@ static struct scsi_host_template scsi_dr .slave_configure = sbp2_scsi_slave_configure, .eh_abort_handler = sbp2_scsi_abort, .this_id = -1, - .sg_tablesize = SG_ALL, + .sg_tablesize = 1, /* SG_ALL, */ .use_clustering = ENABLE_CLUSTERING, .cmd_per_lun = 1, .can_queue = 1, diff -urpN -X dontdiff linux-2.6.18-32.el5/drivers/firewire/fw-transaction.c linux-2.6.18-32.el5-test/drivers/firewire/fw-transaction.c --- linux-2.6.18-32.el5/drivers/firewire/fw-transaction.c 2007-06-26 13:57:27.000000000 -0700 +++ linux-2.6.18-32.el5-test/drivers/firewire/fw-transaction.c 2007-07-12 18:49:39.000000000 -0700 @@ -291,6 +291,7 @@ fw_send_request(struct fw_card *card, st speed, offset, payload, length); t->packet.callback = transmit_complete_callback; + fw_mon_send_req(card, t, tcode, offset, payload); card->driver->send_request(card, &t->packet); } EXPORT_SYMBOL(fw_send_request); @@ -628,6 +629,7 @@ fw_core_handle_request(struct fw_card *c unsigned long flags; int tcode, destination, source; + fw_mon_core_req(card, p); if (p->payload_length > 2048) { /* FIXME: send error response. */ fw_error("fw_core_handle_request: too big\n"); @@ -874,13 +876,19 @@ static int __init fw_core_init(void) { int retval; + if ((retval = fw_mon_init()) != 0) + return retval; + retval = bus_register(&fw_bus_type); - if (retval < 0) + if (retval < 0) { + fw_mon_exit(); return retval; + } fw_cdev_major = register_chrdev(0, "firewire", &fw_device_ops); if (fw_cdev_major < 0) { bus_unregister(&fw_bus_type); + fw_mon_exit(); return fw_cdev_major; } @@ -903,6 +911,7 @@ static int __init fw_core_init(void) static void __exit fw_core_cleanup(void) { + fw_mon_exit(); unregister_chrdev(fw_cdev_major, "firewire"); bus_unregister(&fw_bus_type); } diff -urpN -X dontdiff linux-2.6.18-32.el5/drivers/firewire/fw-transaction.h linux-2.6.18-32.el5-test/drivers/firewire/fw-transaction.h --- linux-2.6.18-32.el5/drivers/firewire/fw-transaction.h 2007-06-26 13:57:27.000000000 -0700 +++ linux-2.6.18-32.el5-test/drivers/firewire/fw-transaction.h 2007-07-13 11:29:12.000000000 -0700 @@ -216,6 +216,7 @@ extern struct bus_type fw_bus_type; struct fw_card { const struct fw_card_driver *driver; struct device *device; + struct fw_mon *mon_bus; struct kref kref; int node_id; @@ -231,6 +232,7 @@ struct fw_card { int max_receive; int link_speed; int config_rom_generation; + int monitored; /* * We need to store up to 4 self ID for a maximum of 63 @@ -468,4 +470,159 @@ fw_core_handle_request(struct fw_card *c void fw_core_handle_response(struct fw_card *card, struct fw_packet *packet); +/* + * The jujumon + * We import a few of unnecessary parts from USB, such as monitor registration, + * in the interests of bootstrapping something that is certain to work. + * It can be jettisoned later. + */ + +void fw_mon_card_add(struct fw_card *card); +void fw_mon_card_remove(struct fw_card *card); +int fw_mon_init(void); +void fw_mon_exit(void); + +enum fw_mon_event_type { + MON_ET_NONE, + MON_ET_MISC, + MON_ET_CORE_REQ, + MON_ET_SEND_REQ, + MON_ET_OHCI_AR, + MON_ET_SBP2_CMD +}; + +/* + * We could have used vararg easily, but it's the same stack use, same cycles, + * plus murky syntax on top. + */ +struct fw_mon_event_arg { + enum fw_mon_event_type type; + union { + struct { + int val; + void *packet; // Do not refer + } misc; + struct { + int ack; + void *packet; // Do not refer + size_t hlen, plen; + } core_req; + struct { + int tcode; + void *packet; // Do not refer + void *payload; // Do not refer + unsigned long long offset; + u32 hdr[4]; + u32 pay[2]; + size_t hlen, plen; + } send_req; + struct { + void *bufp; + __le32 buf[4]; + } ohci_ar; + struct { + unsigned long req_virt; + unsigned long long req_bus; + unsigned char req[32]; + int sg; + unsigned long ptab_virt; + unsigned long long ptab_bus; + unsigned char ptab[16]; + } sbp2_cmd; + } u; +}; + +struct fw_mon_operations { + void (*fw_event)(struct fw_card *bus, struct fw_mon_event_arg *arg); +}; + +extern struct fw_mon_operations *fw_mon_ops; + +static inline void fw_mon_event(struct fw_card *bus, int val, struct fw_packet *p) +{ + if (bus->monitored) { + struct fw_mon_event_arg arg; + arg.type = MON_ET_MISC; + arg.u.misc.val = val; + arg.u.misc.packet = p; + (*fw_mon_ops->fw_event)(bus, &arg); + } +} + +static inline void fw_mon_core_req(struct fw_card *bus, struct fw_packet *p) +{ + if (bus->monitored) { + struct fw_mon_event_arg arg; + arg.type = MON_ET_CORE_REQ; + arg.u.core_req.packet = p; + arg.u.core_req.ack = p->ack; + arg.u.core_req.hlen = p->header_length; + arg.u.core_req.plen = p->payload_length; + (*fw_mon_ops->fw_event)(bus, &arg); + } +} + +static inline void fw_mon_send_req(struct fw_card *bus, + struct fw_transaction *t, int tcode, unsigned long long offset, + void *payload) +{ + if (bus->monitored) { + struct fw_mon_event_arg arg; + arg.type = MON_ET_SEND_REQ; + arg.u.send_req.tcode = tcode; + arg.u.send_req.packet = &t->packet; + arg.u.send_req.payload = payload; + arg.u.send_req.offset = offset; + arg.u.send_req.hdr[0] = t->packet.header[0]; + arg.u.send_req.hdr[1] = t->packet.header[1]; + arg.u.send_req.hdr[2] = t->packet.header[2]; + arg.u.send_req.hdr[3] = t->packet.header[3]; + arg.u.send_req.hlen = t->packet.header_length; + arg.u.send_req.plen = t->packet.payload_length; + if (payload != NULL && t->packet.payload_length != 0) { + arg.u.send_req.pay[0] = ((u32 *)payload)[0]; + arg.u.send_req.pay[1] = ((u32 *)payload)[1]; + } else { + arg.u.send_req.pay[0] = 0; + arg.u.send_req.pay[1] = 0; + } + (*fw_mon_ops->fw_event)(bus, &arg); + } +} + +static inline void fw_mon_ohci_ar(struct fw_card *bus, __le32 *bufp) +{ + if (bus->monitored) { + struct fw_mon_event_arg arg; + arg.type = MON_ET_OHCI_AR; + arg.u.ohci_ar.bufp = bufp; + arg.u.ohci_ar.buf[0] = bufp[0]; + arg.u.ohci_ar.buf[1] = bufp[1]; + arg.u.ohci_ar.buf[2] = bufp[2]; + arg.u.ohci_ar.buf[3] = bufp[3]; + (*fw_mon_ops->fw_event)(bus, &arg); + } +} + +static inline void fw_mon_sbp2_cmd(struct fw_card *bus, + void *req_virt, unsigned long req_bus, int sg, void *req_buf, + void *ptab_virt, unsigned long ptab_bus) +{ + if (bus->monitored) { + struct fw_mon_event_arg arg; + arg.type = MON_ET_SBP2_CMD; + arg.u.sbp2_cmd.req_virt = (unsigned long) req_virt; + arg.u.sbp2_cmd.req_bus = req_bus; + memcpy(arg.u.sbp2_cmd.req, req_virt, 32); + arg.u.sbp2_cmd.sg = sg; + arg.u.sbp2_cmd.ptab_virt = (unsigned long) ptab_virt; + arg.u.sbp2_cmd.ptab_bus = ptab_bus; + memcpy(arg.u.sbp2_cmd.ptab, ptab_virt, 16); + (*fw_mon_ops->fw_event)(bus, &arg); + } +} + +int fw_mon_register(struct fw_mon_operations *ops); +void fw_mon_deregister(void); + #endif /* __fw_transaction_h */ diff -urpN -X dontdiff linux-2.6.18-32.el5/drivers/firewire/Makefile linux-2.6.18-32.el5-test/drivers/firewire/Makefile --- linux-2.6.18-32.el5/drivers/firewire/Makefile 2007-06-26 13:57:27.000000000 -0700 +++ linux-2.6.18-32.el5-test/drivers/firewire/Makefile 2007-06-27 20:21:45.000000000 -0700 @@ -3,7 +3,7 @@ # firewire-core-y := fw-card.o fw-topology.o fw-transaction.o fw-iso.o \ - fw-device.o fw-cdev.o + fw-device.o fw-cdev.o fw-mon.o firewire-ohci-y += fw-ohci.o firewire-sbp2-y += fw-sbp2.o