Hi, Greg & others:
In order to allow usbmon to be statically linked, I had to implement a support
for hotplugged USB buses in it (which can occur if someone runs modprobe, too).
The simplest solution for it was to add a few hooks to the usbcore which tell
usbmon if a new bus comes or old bus goes away.
Everything works fine, and I'm almost ready to submit, except for one thing.
These hooks are hideous (see the appended patch). I suppose I can make them
just a little prettier with some sort of usb_register_monitor() scheme.
But I am thinking now if it may be better to simplify things by declaring
usbmon a part of the core. I see a few advantages to it, in particular there
is no need to rmmod notification hook, which I cannot even implement right.
On the other hand, such integration will make life harder for someone who
wants to create some sort of Super USBmon. What do you think?
But regardless, usb_bus_get and usb_bus_put are not needed anymore. If usbmon
gets to be a part of usbcore, then it's obvious. But if we use hooks, when a bus
is removed, a removal notification hook is called, and usbmon removes its
reference. If you haven't pushed the patch which adds exports, I ask you not
to do it.
One last thing. The following part makes my life diffcult, so I removed it:
if (urb->dev == hcd->self.root_hub) {
/* NOTE: requirement on hub callers (usbfs and the hub
* driver, for now) that URBs' urb->transfer_buffer be
* valid and usb_buffer_{sync,unmap}() not be needed, since
* they could clobber root hub response data.
*/
urb->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP
| URB_NO_SETUP_DMA_MAP);
status = rh_urb_enqueue (hcd, urb);
There's a place in usbmon where it has to decide it it's safe to look
at urb->transfer_buffer. With this artificial setting of flags, usbmon
cannot trace exchanges with root hubs. I think the new code is actually
more direct (or less circumspect).
Best wishes,
-- Pete
diff -urpN -X dontdiff linux-2.6.11-rc1-bk4/drivers/usb/core/hcd.c
linux-2.6.11-rc1-bk4-lem/drivers/usb/core/hcd.c
--- linux-2.6.11-rc1-bk4/drivers/usb/core/hcd.c 2005-01-12 16:35:53.000000000
-0800
+++ linux-2.6.11-rc1-bk4-lem/drivers/usb/core/hcd.c 2005-01-17
21:38:51.000000000 -0800
@@ -105,6 +105,16 @@ static spinlock_t hcd_data_lock = SPIN_L
/* wait queue for synchronous unlinks */
DECLARE_WAIT_QUEUE_HEAD(usb_kill_urb_queue);
+/* Super ugly hooks for usbmon */
+#if defined(CONFIG_USB_MON) || defined(CONFIG_USB_MON_MODULE)
+void (*usb_mon_notify_bus_add)(struct usb_bus *bus);
+void (*usb_mon_notify_bus_remove)(struct usb_bus *bus);
+void (*usb_mon_notify_rmmod)(void);
+EXPORT_SYMBOL_GPL (usb_mon_notify_bus_add);
+EXPORT_SYMBOL_GPL (usb_mon_notify_bus_remove);
+EXPORT_SYMBOL_GPL (usb_mon_notify_rmmod);
+#endif
+
/*-------------------------------------------------------------------------*/
/*
@@ -748,6 +751,7 @@ int usb_register_bus(struct usb_bus *bus
up (&usb_bus_list_lock);
usbfs_add_bus (bus);
+ usbmon_notify_bus_add (bus);
dev_info (bus->controller, "new USB bus registered, assigned bus number
%d\n", bus->busnum);
return 0;
@@ -775,6 +779,7 @@ void usb_deregister_bus (struct usb_bus
list_del (&bus->bus_list);
up (&usb_bus_list_lock);
+ usbmon_notify_bus_remove (bus);
usbfs_remove_bus (bus);
clear_bit (bus->busnum, busmap.busmap);
@@ -1099,14 +1104,12 @@ static int hcd_submit_urb (struct urb *u
urb = usb_get_urb (urb);
atomic_inc (&urb->use_count);
- if (urb->dev == hcd->self.root_hub) {
+ if (usb_pipedevice(urb->pipe) == 1) {
/* NOTE: requirement on hub callers (usbfs and the hub
* driver, for now) that URBs' urb->transfer_buffer be
* valid and usb_buffer_{sync,unmap}() not be needed, since
* they could clobber root hub response data.
*/
- urb->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP
- | URB_NO_SETUP_DMA_MAP);
status = rh_urb_enqueue (hcd, urb);
goto done;
}
@@ -1168,7 +1171,7 @@ unlink1 (struct usb_hcd *hcd, struct urb
{
int value;
- if (urb->dev == hcd->self.root_hub)
+ if (usb_pipedevice(urb->pipe) == 1)
value = usb_rh_urb_dequeue (hcd, urb);
else {
@@ -1258,7 +1261,7 @@ static int hcd_unlink_urb (struct urb *u
* finish unlinking the initial failed usb_set_address()
* or device descriptor fetch.
*/
- if (!hcd->saw_irq && hcd->self.root_hub != urb->dev) {
+ if (!hcd->saw_irq && usb_pipedevice(urb->pipe) != 1) {
dev_warn (hcd->self.controller, "Unlink after no-IRQ? "
"Controller is probably using the wrong IRQ."
"\n");
@@ -1465,12 +1468,8 @@ void usb_hcd_giveback_urb (struct usb_hc
{
urb_unlink (urb);
- // NOTE: a generic device/urb monitoring hook would go here.
- // hcd_monitor_hook(MONITOR_URB_FINISH, urb, dev)
- // It would catch exit/unlink paths for all urbs.
-
/* lower level hcd code should use *_dma exclusively */
- if (hcd->self.controller->dma_mask) {
+ if (hcd->self.controller->dma_mask && usb_pipedevice(urb->pipe) != 1) {
if (usb_pipecontrol (urb->pipe)
&& !(urb->transfer_flags & URB_NO_SETUP_DMA_MAP))
dma_unmap_single (hcd->self.controller, urb->setup_dma,
diff -urpN -X dontdiff linux-2.6.11-rc1-bk4/drivers/usb/core/hcd.h
linux-2.6.11-rc1-bk4-lem/drivers/usb/core/hcd.h
--- linux-2.6.11-rc1-bk4/drivers/usb/core/hcd.h 2005-01-12 16:20:50.000000000
-0800
+++ linux-2.6.11-rc1-bk4-lem/drivers/usb/core/hcd.h 2005-01-17
21:40:56.000000000 -0800
@@ -411,6 +411,38 @@ static inline void usbfs_cleanup(void) {
/*-------------------------------------------------------------------------*/
+#if defined(CONFIG_USB_MON) || defined(CONFIG_USB_MON_MODULE)
+
+#define usbmon_notify_bus_add(bus) \
+ do { \
+ if (usb_mon_notify_bus_add) \
+ (*usb_mon_notify_bus_add)(bus); \
+ } while(0)
+#define usbmon_notify_bus_remove(bus) \
+ do { \
+ if (usb_mon_notify_bus_remove) \
+ (*usb_mon_notify_bus_remove)(bus); \
+ } while(0)
+#define usbmon_notify_rmmod() \
+ do { \
+ if (usb_mon_notify_rmmod) \
+ (*usb_mon_notify_rmmod)(); \
+ } while(0)
+
+extern void (*usb_mon_notify_bus_add)(struct usb_bus *bus);
+extern void (*usb_mon_notify_bus_remove)(struct usb_bus *bus);
+extern void (*usb_mon_notify_rmmod)(void);
+
+#else
+
+#define usbmon_notify_bus_add(bus) /* */
+#define usbmon_notify_bus_remove(bus) /* */
+#define usbmon_notify_rmmod() /* */
+
+#endif /* CONFIG_USB_MON */
+
+/*-------------------------------------------------------------------------*/
+
/* hub.h ... DeviceRemovable in 2.4.2-ac11, gone in 2.4.10 */
// bleech -- resurfaced in 2.4.11 or 2.4.12
#define bitmap DeviceRemovable
diff -urpN -X dontdiff linux-2.6.11-rc1-bk4/drivers/usb/core/urb.c
linux-2.6.11-rc1-bk4-lem/drivers/usb/core/urb.c
--- linux-2.6.11-rc1-bk4/drivers/usb/core/urb.c 2005-01-12 16:20:50.000000000
-0800
+++ linux-2.6.11-rc1-bk4-lem/drivers/usb/core/urb.c 2005-01-16
13:58:12.000000000 -0800
@@ -111,6 +111,19 @@ struct urb * usb_get_urb(struct urb *urb
/*-------------------------------------------------------------------*/
+#if defined(CONFIG_USB_MON) || defined(CONFIG_USB_MON_MODULE)
+static inline int usb_mon_submit(struct usb_bus *bus, struct urb *urb, int
memf)
+{
+ int (*submit)(struct urb *urb, int mem_flags);
+
+ if ((submit = bus->mon_submit) != NULL)
+ return (*submit)(urb, memf);
+ return bus->op->submit_urb (urb, memf);
+}
+#else
+#define usb_mon_submit(bus, urb, memf) (*(bus)->op->submit_urb)(urb, memf)
+#endif
+
/**
* usb_submit_urb - issue an asynchronous transfer request for an endpoint
* @urb: pointer to the urb describing the request
@@ -383,7 +396,7 @@ int usb_submit_urb(struct urb *urb, int
urb->interval = temp;
}
- return op->submit_urb (urb, mem_flags);
+ return usb_mon_submit(dev->bus, urb, mem_flags);
}
/*-------------------------------------------------------------------*/
diff -urpN -X dontdiff linux-2.6.11-rc1-bk4/drivers/usb/core/usb.c
linux-2.6.11-rc1-bk4-lem/drivers/usb/core/usb.c
--- linux-2.6.11-rc1-bk4/drivers/usb/core/usb.c 2005-01-12 16:20:50.000000000
-0800
+++ linux-2.6.11-rc1-bk4-lem/drivers/usb/core/usb.c 2005-01-17
21:34:52.000000000 -0800
@@ -1476,6 +1476,7 @@ static void __exit usb_exit(void)
if (nousb)
return;
+ usbmon_notify_rmmod();
driver_unregister(&usb_generic_driver);
usb_major_cleanup();
usbfs_cleanup();
diff -urpN -X dontdiff linux-2.6.11-rc1-bk4/drivers/usb/Kconfig
linux-2.6.11-rc1-bk4-lem/drivers/usb/Kconfig
--- linux-2.6.11-rc1-bk4/drivers/usb/Kconfig 2005-01-05 00:37:56.000000000
-0800
+++ linux-2.6.11-rc1-bk4-lem/drivers/usb/Kconfig 2005-01-05
14:52:52.000000000 -0800
@@ -56,6 +56,8 @@ source "drivers/usb/media/Kconfig"
source "drivers/usb/net/Kconfig"
+source "drivers/usb/mon/Kconfig"
+
comment "USB port drivers"
depends on USB
diff -urpN -X dontdiff linux-2.6.11-rc1-bk4/drivers/usb/Makefile
linux-2.6.11-rc1-bk4-lem/drivers/usb/Makefile
--- linux-2.6.11-rc1-bk4/drivers/usb/Makefile 2005-01-12 16:20:50.000000000
-0800
+++ linux-2.6.11-rc1-bk4-lem/drivers/usb/Makefile 2005-01-16
13:58:12.000000000 -0800
@@ -6,6 +6,8 @@
obj-$(CONFIG_USB) += core/
+obj-$(CONFIG_USB_MON) += mon/
+
obj-$(CONFIG_USB_EHCI_HCD) += host/
obj-$(CONFIG_USB_OHCI_HCD) += host/
obj-$(CONFIG_USB_UHCI_HCD) += host/
diff -urpN -X dontdiff linux-2.6.11-rc1-bk4/drivers/usb/mon/Kconfig
linux-2.6.11-rc1-bk4-lem/drivers/usb/mon/Kconfig
--- linux-2.6.11-rc1-bk4/drivers/usb/mon/Kconfig 1969-12-31
16:00:00.000000000 -0800
+++ linux-2.6.11-rc1-bk4-lem/drivers/usb/mon/Kconfig 2005-01-18
00:17:43.000000000 -0800
@@ -0,0 +1,22 @@
+#
+# USB Monitor configuration
+#
+
+# In normal life, it makes little sense to have usbmon as a module, and in fact
+# it is harmful, because there is no way to autoload the module.
+# The 'm' option is allowed for hackers who debug the usbmon itself,
+# and for those who have usbcore as a module.
+config USB_MON
+ tristate "USB Monitor"
+ depends on USB
+ default y
+ help
+ If you say Y here, a component which captures the USB traffic
+ between peripheral-specific drivers and HC drivers will be built.
+ The USB_MON is similar in spirit and may be compatible with Dave
+ Harding's USBMon.
+
+ This is somewhat experimental at this time, but it should be safe,
+ as long as you aren't building this as a module and then removing it.
+
+ If unsure, say Y. Do not say M.
diff -urpN -X dontdiff linux-2.6.11-rc1-bk4/drivers/usb/mon/Makefile
linux-2.6.11-rc1-bk4-lem/drivers/usb/mon/Makefile
--- linux-2.6.11-rc1-bk4/drivers/usb/mon/Makefile 1969-12-31
16:00:00.000000000 -0800
+++ linux-2.6.11-rc1-bk4-lem/drivers/usb/mon/Makefile 2005-01-16
13:58:12.000000000 -0800
@@ -0,0 +1,7 @@
+#
+# Makefile for USB Core files and filesystem
+#
+
+usbmon-objs := mon_main.o mon_stat.o mon_text.o
+
+obj-$(CONFIG_USB_MON) += usbmon.o
diff -urpN -X dontdiff linux-2.6.11-rc1-bk4/drivers/usb/mon/mon_main.c
linux-2.6.11-rc1-bk4-lem/drivers/usb/mon/mon_main.c
--- linux-2.6.11-rc1-bk4/drivers/usb/mon/mon_main.c 1969-12-31
16:00:00.000000000 -0800
+++ linux-2.6.11-rc1-bk4-lem/drivers/usb/mon/mon_main.c 2005-01-18
00:39:23.000000000 -0800
@@ -0,0 +1,415 @@
+/*
+ * The USB Monitor, inspired by Dave Harding's USBMon.
+ *
+ * mon_main.c: Main file, module initiation and exit, registrations, etc.
+ *
+ * - If an URB was submitted before a reader opened a pipe, it's not tracked.
+ * The only solution is to reject intercept-on-open and do intercept-always.
+ * - Hotplugging HCs will cause usbmon to crash on unload
+ * - Type files as fifos, sockets, or character specials? /proc/kmsg is
regular.
+ * - Awaken rmmod when outstanding URBs and opens are gone.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/debugfs.h>
+#include <linux/smp_lock.h>
+
+#include "usb_mon.h"
+#include "../core/hcd.h"
+
+static int mon_submit(struct urb *urb, int mem_flags);
+static void mon_complete(struct urb *urb, struct pt_regs *regs);
+static void mon_stop(struct mon_bus *mbus);
+static void mon_dissolve(struct mon_bus *mbus, struct usb_bus *ubus);
+static void mon_bus_init(struct dentry *mondir, struct usb_bus *ubus);
+
+DECLARE_MUTEX(mon_lock);
+
+static struct dentry *mon_dir; /* /dbg/usbmon */
+static LIST_HEAD(mon_buses); /* All buses we know: struct mon_bus */
+
+/*
+ * Link a reader into the bus.
+ */
+void mon_reader_add(struct mon_bus *mbus, struct mon_reader *r)
+{
+ unsigned long flags;
+ struct usb_bus *ubus;
+
+ spin_lock_irqsave(&mbus->lock, flags);
+ if (mbus->nreaders == 0 && mbus->nurbs == 0) {
+ ubus = mbus->u_bus;
+ if (ubus->mon_submit != NULL) {
+ /*
+ * Something is really broken, refuse to go on and
+ * possibly corrupt ops pointers or worse.
+ */
+ printk(KERN_ERR TAG ": bus %d is already monitored\n",
+ ubus->busnum);
+ spin_unlock_irqrestore(&mbus->lock, flags);
+ return;
+ }
+ ubus->mon_submit = mon_submit;
+ }
+ mbus->nreaders++;
+ mbus->ref++;
+
+ list_add_tail(&r->r_link, &mbus->r_list);
+ spin_unlock_irqrestore(&mbus->lock, flags);
+}
+
+/*
+ * Unlink reader from the bus.
+ *
+ * This is called with mon_lock taken, so we can decrement mbus->ref.
+ */
+void mon_reader_del(struct mon_bus *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 && mbus->nurbs == 0)
+ mon_stop(mbus);
+ spin_unlock_irqrestore(&mbus->lock, flags);
+
+ if (--mbus->ref == 0)
+ kfree(mbus);
+}
+
+/*
+ */
+static int mon_submit(struct urb *urb, int mem_flags)
+{
+ struct usb_bus *ubus;
+ struct mon_bus *mbus;
+ unsigned long flags;
+ struct list_head *pos;
+ struct mon_reader *r;
+ int rc;
+
+ ubus = urb->dev->bus;
+ mbus = ubus->mon_bus;
+ if (mbus == NULL)
+ goto out_unlocked;
+
+ spin_lock_irqsave(&mbus->lock, flags);
+ if (mbus->nreaders == 0)
+ goto out_locked;
+
+ urb->mon_bus = mbus;
+ urb->mon_saved_complete = urb->complete;
+ urb->complete = mon_complete;
+ mbus->nurbs++;
+
+ list_for_each (pos, &mbus->r_list) {
+ r = list_entry(pos, struct mon_reader, r_link);
+ r->rnf_submit(r->r_data, urb);
+ }
+
+ spin_unlock_irqrestore(&mbus->lock, flags);
+
+ if ((rc = ubus->op->submit_urb(urb, mem_flags)) != 0) {
+ spin_lock_irqsave(&mbus->lock, flags);
+ urb->complete = urb->mon_saved_complete;
+ urb->mon_bus = NULL;
+ --mbus->nurbs;
+ if (mbus->nurbs == 0 && mbus->nreaders == 0)
+ mon_stop(mbus);
+ spin_unlock_irqrestore(&mbus->lock, flags);
+ }
+ return rc;
+
+out_locked:
+ spin_unlock_irqrestore(&mbus->lock, flags);
+out_unlocked:
+ return ubus->op->submit_urb(urb, mem_flags);
+}
+
+/*
+ * This is called by an HC driver directly.
+ */
+static void mon_complete(struct urb *urb, struct pt_regs *regs)
+{
+ struct mon_bus *mbus;
+ unsigned long flags;
+ struct list_head *pos;
+ struct mon_reader *r;
+
+ mbus = urb->mon_bus;
+ if (mbus == NULL) {
+ /*
+ * This should not happen.
+ * At this point we do not even know the bus number...
+ */
+ printk(KERN_ERR TAG ": Null mon bus in URB, pipe 0x%x\n",
+ urb->pipe);
+ return;
+ }
+
+ spin_lock_irqsave(&mbus->lock, flags);
+ list_for_each (pos, &mbus->r_list) {
+ r = list_entry(pos, struct mon_reader, r_link);
+ r->rnf_complete(r->r_data, urb);
+ }
+
+ /*
+ * Drop the URB from tracking.
+ * XXX ISOs and Interrupt get lost after the first complete this way.
+ */
+ urb->complete = urb->mon_saved_complete;
+ urb->mon_bus = NULL;
+ --mbus->nurbs;
+ if (mbus->nurbs == 0 && mbus->nreaders == 0)
+ mon_stop(mbus);
+ spin_unlock_irqrestore(&mbus->lock, flags);
+
+ (*urb->complete)(urb, regs);
+}
+
+/* int (*unlink_urb) (struct urb *urb, int status); */
+
+/*
+ * Stop monitoring.
+ * Obviously this must be well locked, so no need to play with mb's.
+ */
+static void mon_stop(struct mon_bus *mbus)
+{
+ struct usb_bus *ubus = mbus->u_bus;
+
+ /*
+ * A stop can be called for a dissolved mon_bus in case of
+ * a reader staying across an rmmod foo_hcd.
+ */
+ if (ubus != NULL)
+ ubus->mon_submit = NULL;
+}
+
+/*
+ * Add a USB bus (usually by a modprobe foo-hcd)
+ *
+ * This does not return an error code because the core cannot care less
+ * if monitoring is not established.
+ */
+static void mon_bus_add(struct usb_bus *ubus)
+{
+ mon_bus_init(mon_dir, ubus);
+}
+
+/*
+ * Remove a USB bus (either from rmmod foo-hcd or from a hot-remove event).
+ */
+static void mon_bus_remove(struct usb_bus *ubus)
+{
+ struct mon_bus *mbus = ubus->mon_bus;
+
+ /*
+ * First, make sure that this bus cannot be opened anymore.
+ */
+ down(&mon_lock);
+ list_del(&mbus->bus_link);
+ debugfs_remove(mbus->dent_t);
+ debugfs_remove(mbus->dent_s);
+
+ /*
+ * This cannot happen, because the USB core cancels all outstanding
+ * URBs before coming to usb_deregister_bus and here. Still...
+ */
+ if (mbus->nurbs) {
+ printk(KERN_ERR TAG
+ ": Stuck URBs (%d) on usb%d, leaking...\n",
+ mbus->nurbs, ubus->busnum);
+ mbus->ref = 3; /* Force leak */
+ }
+
+ mon_dissolve(mbus, ubus);
+ if (--mbus->ref == 0)
+ kfree(mbus);
+
+ up(&mon_lock);
+}
+
+/*
+ */
+static void mon_core_rmmod(void)
+{
+ printk(KERN_ERR TAG
+ ": We are confused by rmmod usbcore and hanging...\n");
+ for (;;) {
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule();
+ }
+}
+
+/*
+ * Tear usb_bus and mon_bus apart.
+ */
+static void mon_dissolve(struct mon_bus *mbus, struct usb_bus *ubus)
+{
+
+ /*
+ * Never happens, but...
+ */
+ if (ubus->mon_submit) {
+ printk(KERN_ERR TAG ": bus %d is dissolved while monitored\n",
+ ubus->busnum);
+ ubus->mon_submit = NULL;
+ }
+
+ ubus->mon_bus = NULL;
+ mbus->u_bus = NULL;
+ mb();
+ // usb_bus_put(ubus);
+}
+
+/*
+ * Initialize a bus for us:
+ * - allocate mon_bus
+ * - refcount USB bus struct
+ * - link
+ */
+static void mon_bus_init(struct dentry *mondir, struct usb_bus *ubus)
+{
+ struct dentry *d;
+ struct mon_bus *mbus;
+ enum { NAMESZ = 10 };
+ char name[NAMESZ];
+ int rc;
+
+ if ((mbus = kmalloc(sizeof(struct mon_bus), GFP_KERNEL)) == NULL)
+ goto err_alloc;
+ memset(mbus, 0, sizeof(struct mon_bus));
+ spin_lock_init(&mbus->lock);
+ INIT_LIST_HEAD(&mbus->r_list);
+ mbus->ref = 1;
+
+ /*
+ * This usb_bus_get here is superfluous, because we receive
+ * a notification if usb_bus is about to be removed.
+ */
+ // usb_bus_get(ubus);
+ mbus->u_bus = ubus;
+ ubus->mon_bus = mbus;
+
+ rc = snprintf(name, NAMESZ, "%dt", ubus->busnum);
+ if (rc <= 0 || rc >= NAMESZ)
+ goto err_print_t;
+ d = debugfs_create_file(name, 0600, mondir, mbus, &mon_fops_text);
+ if (d == NULL)
+ goto err_create_t;
+ mbus->dent_t = d;
+
+ rc = snprintf(name, NAMESZ, "%ds", ubus->busnum);
+ if (rc <= 0 || rc >= NAMESZ)
+ goto err_print_s;
+ d = debugfs_create_file(name, 0600, mondir, mbus, &mon_fops_stat);
+ if (d == NULL)
+ goto err_create_s;
+ mbus->dent_s = d;
+
+ down(&mon_lock);
+ list_add_tail(&mbus->bus_link, &mon_buses);
+ up(&mon_lock);
+ return;
+
+err_create_s:
+err_print_s:
+ debugfs_remove(mbus->dent_t);
+err_create_t:
+err_print_t:
+ kfree(mbus);
+err_alloc:
+ return;
+}
+
+static int __init mon_init(void)
+{
+ struct usb_bus *ubus;
+ struct dentry *mondir;
+
+ mondir = debugfs_create_dir("usbmon", NULL);
+ if (IS_ERR(mondir)) {
+ printk(KERN_NOTICE TAG ": debugs is not available\n");
+ return -ENODEV;
+ }
+ if (mondir == NULL) {
+ printk(KERN_NOTICE TAG ": unable to create usbmon directory\n");
+ return -ENODEV;
+ }
+ mon_dir = mondir;
+
+ /*
+ * Ewww... Too tightly integrated with the core. XXX
+ */
+ usb_mon_notify_bus_add = mon_bus_add;
+ usb_mon_notify_bus_remove = mon_bus_remove;
+ usb_mon_notify_rmmod = mon_core_rmmod;
+ // MOD_INC_USE_COUNT(which_module?);
+
+ down(&usb_bus_list_lock);
+ list_for_each_entry (ubus, &usb_bus_list, bus_list) {
+ mon_bus_init(mondir, ubus);
+ }
+ up(&usb_bus_list_lock);
+ return 0;
+}
+
+static void __exit mon_exit(void)
+{
+ struct mon_bus *mbus;
+ struct list_head *p;
+
+ usb_mon_notify_bus_remove = NULL;
+ usb_mon_notify_bus_add = NULL;
+ usb_mon_notify_rmmod = NULL;
+
+ down(&mon_lock);
+ while (!list_empty(&mon_buses)) {
+ p = mon_buses.next;
+ mbus = list_entry(p, struct mon_bus, bus_link);
+ list_del(p);
+
+ debugfs_remove(mbus->dent_t);
+ debugfs_remove(mbus->dent_s);
+
+ /*
+ * Stuck URBs happen when user starts monitoring long-living
+ * URBs, for instance, interrupt URBs in HID, then exits.
+ * In such situation there's no other way but to refrain
+ * from rmmod-ing usbmon.
+ */
+ if (mbus->nurbs) {
+ printk(KERN_ERR TAG
+ ": Stuck URBs (%d) on usb%d, leaking...\n",
+ mbus->nurbs, mbus->u_bus->busnum);
+ mbus->ref = 2; /* Force leak */
+ }
+
+ /*
+ * 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 usb%d, leaking...\n",
+ mbus->nreaders, mbus->u_bus->busnum);
+ mbus->ref = 2; /* Force leak */
+ }
+
+ mon_dissolve(mbus, mbus->u_bus);
+ if (--mbus->ref == 0)
+ kfree(mbus);
+ }
+ up(&mon_lock);
+
+ debugfs_remove(mon_dir);
+}
+
+module_init(mon_init);
+module_exit(mon_exit);
+
+MODULE_LICENSE("GPL");
diff -urpN -X dontdiff linux-2.6.11-rc1-bk4/drivers/usb/mon/mon_stat.c
linux-2.6.11-rc1-bk4-lem/drivers/usb/mon/mon_stat.c
--- linux-2.6.11-rc1-bk4/drivers/usb/mon/mon_stat.c 1969-12-31
16:00:00.000000000 -0800
+++ linux-2.6.11-rc1-bk4-lem/drivers/usb/mon/mon_stat.c 2005-01-17
19:39:19.000000000 -0800
@@ -0,0 +1,75 @@
+/*
+ * The USB Monitor, inspired by Dave Harding's USBMon.
+ *
+ * This is the 's' or 'stat' reader which debugs usbmon itself.
+ * Note that this code blows through locks, so make sure that
+ * /dbg/usbmon/0s is well protected from non-root users.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <asm/uaccess.h>
+
+#include "usb_mon.h"
+
+#define STAT_BUF_SIZE 80
+
+struct snap {
+ int slen;
+ char str[STAT_BUF_SIZE];
+};
+
+static int mon_stat_open(struct inode *inode, struct file *file)
+{
+ struct mon_bus *mbus;
+ struct snap *sp;
+
+ if ((sp = kmalloc(sizeof(struct snap), GFP_KERNEL)) == NULL)
+ return -ENOMEM;
+
+ mbus = inode->u.generic_ip;
+
+ sp->slen = snprintf(sp->str, STAT_BUF_SIZE,
+ "nreaders %d nurbs %d text_lost %u\n",
+ mbus->nreaders, mbus->nurbs, mbus->cnt_text_lost);
+
+ file->private_data = sp;
+ return 0;
+}
+
+static ssize_t mon_stat_read(struct file *file, char __user *buf,
+ size_t nbytes, loff_t *ppos)
+{
+ struct snap *sp = file->private_data;
+ loff_t pos = *ppos;
+ int cnt;
+
+ if (pos < 0 || pos >= sp->slen)
+ return 0;
+ if (nbytes == 0)
+ return 0;
+ if ((cnt = sp->slen - pos) > nbytes)
+ cnt = nbytes;
+ if (copy_to_user(buf, sp->str + pos, cnt))
+ return -EFAULT;
+ *ppos = pos + cnt;
+ return cnt;
+}
+
+static int mon_stat_release(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+struct file_operations mon_fops_stat = {
+ .owner = THIS_MODULE,
+ .open = mon_stat_open,
+ .llseek = no_llseek,
+ .read = mon_stat_read,
+ /* .write = mon_stat_write, */
+ /* .poll = mon_stat_poll, */
+ /* .ioctl = mon_stat_ioctl, */
+ .release = mon_stat_release,
+};
diff -urpN -X dontdiff linux-2.6.11-rc1-bk4/drivers/usb/mon/mon_text.c
linux-2.6.11-rc1-bk4-lem/drivers/usb/mon/mon_text.c
--- linux-2.6.11-rc1-bk4/drivers/usb/mon/mon_text.c 1969-12-31
16:00:00.000000000 -0800
+++ linux-2.6.11-rc1-bk4-lem/drivers/usb/mon/mon_text.c 2005-01-18
00:23:51.000000000 -0800
@@ -0,0 +1,396 @@
+/*
+ * The USB Monitor, inspired by Dave Harding's USBMon.
+ *
+ * This is a text format reader.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/list.h>
+#include <linux/usb.h>
+#include <linux/time.h>
+#include <asm/uaccess.h>
+
+#include "usb_mon.h"
+
+/*
+ * No, we do not want arbitrarily long data strings.
+ * Use the binary interface if you want to capture bulk data!
+ */
+#define DATA_MAX 32
+
+/*
+ * This limit exists to prevent OOMs when the user process stops reading.
+ */
+#define EVENT_MAX 25
+
+#define PRINTF_DFL 120
+
+struct mon_event_text {
+ struct list_head e_link;
+ int type; /* submit, complete, etc. */
+ unsigned int pipe; /* Pipe */
+ unsigned long id; /* From pointer, most of the time */
+ unsigned int tstamp;
+ int length; /* Depends on type: xfer length or act length */
+ int status;
+ char data_flag;
+ unsigned char data[DATA_MAX];
+};
+
+#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 */
+
+ wait_queue_head_t wait;
+ int printf_size;
+ char *printf_buf;
+ struct semaphore printf_lock;
+
+ char slab_name[SLAB_NAME_SZ];
+};
+
+static void mon_text_ctor(void *, kmem_cache_t *, unsigned long);
+static void mon_text_dtor(void *, kmem_cache_t *, unsigned long);
+
+/*
+ * mon_text_submit
+ * mon_text_complete
+ *
+ * May be called from an interrupt.
+ *
+ * This is called with the whole mon_bus locked, so no additional lock.
+ */
+
+static inline char mon_text_get_data(struct mon_event_text *ep, struct urb
*urb,
+ int len, char ev_type)
+{
+ int pipe = urb->pipe;
+ unsigned char *data;
+
+ /*
+ * The check to see if it's safe to poke at data has an enormous
+ * number of corner cases, but it seems that the following is
+ * more or less safe.
+ *
+ * We do not even try to look transfer_buffer, because it can
+ * contain non-NULL garbage in case the upper level promised to
+ * set DMA for the HCD.
+ */
+ if (urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)
+ return 'D';
+
+ if (len <= 0)
+ return 'L';
+
+ if ((data = urb->transfer_buffer) == NULL)
+ return 'Z'; /* '0' would be not as pretty. */
+
+ /*
+ * Bulk is easy to shortcut reliably.
+ * XXX Control needs setup packet taken.
+ * XXX Other pipe types need consideration. Currently, we overdo it
+ * and collect garbage for them: better more than less.
+ */
+ if (usb_pipebulk(pipe) || usb_pipecontrol(pipe)) {
+ if (usb_pipein(pipe)) {
+ if (ev_type == 'S')
+ return '<';
+ } else {
+ if (ev_type == 'C')
+ return '>';
+ }
+ }
+
+ if (len >= DATA_MAX)
+ len = DATA_MAX;
+ memcpy(ep->data, urb->transfer_buffer, len);
+ return 0;
+}
+
+static inline unsigned int mon_get_timestamp(void)
+{
+ struct timeval tval;
+ unsigned int stamp;
+
+ do_gettimeofday(&tval);
+ stamp = tval.tv_sec & 0xFFFF; /* 2^32 = 4294967296. Limit to 4096s. */
+ stamp = stamp * 1000000 + tval.tv_usec;
+ return stamp;
+}
+
+static void mon_text_event(struct mon_reader_text *rp, struct urb *urb,
+ char ev_type)
+{
+ struct mon_event_text *ep;
+ unsigned int stamp;
+
+ stamp = mon_get_timestamp();
+
+ if (rp->nevents >= EVENT_MAX ||
+ (ep = kmem_cache_alloc(rp->e_slab, SLAB_ATOMIC)) == NULL) {
+ rp->r.m_bus->cnt_text_lost++;
+ return;
+ }
+
+ ep->type = ev_type;
+ ep->pipe = urb->pipe;
+ ep->id = (unsigned long) urb;
+ ep->tstamp = stamp;
+ ep->length = (ev_type == 'S') ?
+ urb->transfer_buffer_length : urb->actual_length;
+ /* Collecting status makes debugging sense for submits, too */
+ ep->status = urb->status;
+
+ ep->data_flag = mon_text_get_data(ep, urb, ep->length, ev_type);
+
+ rp->nevents++;
+ list_add_tail(&ep->e_link, &rp->e_list);
+ wake_up(&rp->wait);
+}
+
+static void mon_text_submit(void *data, struct urb *urb)
+{
+ struct mon_reader_text *rp = data;
+ mon_text_event(rp, urb, 'S');
+}
+
+static void mon_text_complete(void *data, struct urb *urb)
+{
+ struct mon_reader_text *rp = data;
+ mon_text_event(rp, urb, 'C');
+}
+
+/*
+ * Fetch next event from the circular buffer.
+ */
+static struct mon_event_text *mon_text_fetch(struct mon_reader_text *rp,
+ struct mon_bus *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 mon_bus *mbus;
+ struct usb_bus *ubus;
+ struct mon_reader_text *rp;
+ int rc;
+
+ down(&mon_lock);
+ mbus = inode->u.generic_ip;
+ ubus = mbus->u_bus;
+
+ rp = kmalloc(sizeof(struct mon_reader_text), GFP_KERNEL);
+ if (rp == NULL) {
+ rc = -ENOMEM;
+ goto err_alloc;
+ }
+ memset(rp, 0, sizeof(struct mon_reader_text));
+ INIT_LIST_HEAD(&rp->e_list);
+ init_waitqueue_head(&rp->wait);
+ init_MUTEX(&rp->printf_lock);
+
+ 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->r.m_bus = mbus;
+ rp->r.r_data = rp;
+ rp->r.rnf_submit = mon_text_submit;
+ rp->r.rnf_complete = mon_text_complete;
+
+ snprintf(rp->slab_name, SLAB_NAME_SZ, "mon%dt_%lx", ubus->busnum,
+ (long)rp);
+ rp->e_slab = kmem_cache_create(rp->slab_name,
+ sizeof(struct mon_event_text), sizeof(long), 0,
+ mon_text_ctor, mon_text_dtor);
+ if (rp->e_slab == NULL) {
+ rc = -ENOMEM;
+ goto err_slab;
+ }
+
+ mon_reader_add(mbus, &rp->r);
+
+ file->private_data = rp;
+ up(&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:
+ up(&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=/dbg/usbmon/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 mon_bus *mbus = rp->r.m_bus;
+ DECLARE_WAITQUEUE(waita, current);
+ struct mon_event_text *ep;
+ int cnt, limit;
+ char *pbuf;
+ int data_len, i;
+
+ 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);
+
+ down(&rp->printf_lock);
+ cnt = 0;
+ pbuf = rp->printf_buf;
+ limit = rp->printf_size;
+
+ cnt += snprintf(pbuf + cnt, limit - cnt,
+ "%lx %u %c %08x %d %d",
+ ep->id, ep->tstamp, ep->type, ep->pipe, ep->status, ep->length);
+
+ if ((data_len = ep->length) > 0) {
+ if (ep->data_flag == 0) {
+ cnt += snprintf(pbuf + cnt, limit - cnt, " =");
+ if (data_len >= DATA_MAX)
+ data_len = DATA_MAX;
+ for (i = 0; i < data_len; i++) {
+ if (i % 4 == 0) {
+ cnt += snprintf(pbuf + cnt, limit - cnt,
+ " ");
+ }
+ cnt += snprintf(pbuf + cnt, limit - cnt,
+ "%02x", ep->data[i]);
+ }
+ cnt += snprintf(pbuf + cnt, limit - cnt, "\n");
+ } else {
+ cnt += snprintf(pbuf + cnt, limit - cnt,
+ " %c\n", ep->data_flag);
+ }
+ } else {
+ cnt += snprintf(pbuf + cnt, limit - cnt, "\n");
+ }
+
+ if (copy_to_user(buf, rp->printf_buf, cnt))
+ cnt = -EFAULT;
+ up(&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 mon_bus *mbus;
+ /* unsigned long flags; */
+ struct list_head *p;
+ struct mon_event_text *ep;
+
+ down(&mon_lock);
+ mbus = inode->u.generic_ip;
+
+ if (mbus->nreaders <= 0) {
+ printk(KERN_ERR TAG ": consistency error on close\n");
+ up(&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);
+
+ up(&mon_lock);
+ return 0;
+}
+
+struct file_operations mon_fops_text = {
+ .owner = THIS_MODULE,
+ .open = mon_text_open,
+ .llseek = no_llseek,
+ .read = mon_text_read,
+ /* .write = mon_text_write, */
+ /* .poll = mon_text_poll, */
+ /* .ioctl = mon_text_ioctl, */
+ .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 void mon_text_dtor(void *mem, kmem_cache_t *slab, unsigned long sflags)
+{
+ ;
+}
diff -urpN -X dontdiff linux-2.6.11-rc1-bk4/drivers/usb/mon/usb_mon.h
linux-2.6.11-rc1-bk4-lem/drivers/usb/mon/usb_mon.h
--- linux-2.6.11-rc1-bk4/drivers/usb/mon/usb_mon.h 1969-12-31
16:00:00.000000000 -0800
+++ linux-2.6.11-rc1-bk4-lem/drivers/usb/mon/usb_mon.h 2005-01-18
00:01:04.000000000 -0800
@@ -0,0 +1,51 @@
+/*
+ * The USB Monitor, inspired by Dave Harding's USBMon.
+ */
+
+#ifndef __USB_MON_H
+#define __USB_MON_H
+
+#include <linux/list.h>
+#include <linux/slab.h>
+/* #include <linux/usb.h> */ /* We use struct pointers only in this header */
+
+#define TAG "usbmon"
+
+struct mon_bus {
+ struct list_head bus_link;
+ spinlock_t lock;
+ int nurbs; /* Number of tracked URBs */
+ struct dentry *dent_s; /* Debugging file */
+ struct dentry *dent_t; /* Text interface file */
+ struct usb_bus *u_bus;
+
+ /* Ref */
+ int nreaders; /* Under mon_lock AND mbus->lock */
+ int ref; /* Under mon_lock */
+ struct list_head r_list; /* Chain of readers (usually one) */
+
+ /* Stats */
+ 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 mon_bus *m_bus;
+ void *r_data; /* Use container_of instead? */
+
+ void (*rnf_submit)(void *data, struct urb *urb);
+ void (*rnf_complete)(void *data, struct urb *urb);
+};
+
+void mon_reader_add(struct mon_bus *mbus, struct mon_reader *r);
+void mon_reader_del(struct mon_bus *mbus, struct mon_reader *r);
+
+extern struct semaphore mon_lock;
+
+extern struct file_operations mon_fops_text;
+extern struct file_operations mon_fops_stat;
+
+#endif /* __USB_MON_H */
diff -urpN -X dontdiff linux-2.6.11-rc1-bk4/drivers/usb/serial/pl2303.c
linux-2.6.11-rc1-bk4-lem/drivers/usb/serial/pl2303.c
--- linux-2.6.11-rc1-bk4/drivers/usb/serial/pl2303.c 2005-01-05
00:37:59.000000000 -0800
+++ linux-2.6.11-rc1-bk4-lem/drivers/usb/serial/pl2303.c 2005-01-16
13:58:22.000000000 -0800
@@ -510,6 +510,12 @@ static void pl2303_set_termios (struct u
dbg ("0xa1:0x21:0:0 %d - %x %x %x %x %x %x %x", i,
buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]);
+ if (cflag & CLOCAL) {
+ i = usb_control_msg (serial->dev, usb_sndctrlpipe (serial->dev,
0),
+ VENDOR_WRITE_REQUEST,
VENDOR_WRITE_REQUEST_TYPE,
+ 0x0, 0x0, NULL, 0, 100);
+ dbg ("0x40:0x1:0x0:0x0 %d", i);
+ }
if (cflag & CRTSCTS) {
__u16 index;
if (priv->type == HX)
diff -urpN -X dontdiff linux-2.6.11-rc1-bk4/include/linux/usb.h
linux-2.6.11-rc1-bk4-lem/include/linux/usb.h
--- linux-2.6.11-rc1-bk4/include/linux/usb.h 2005-01-12 16:21:01.000000000
-0800
+++ linux-2.6.11-rc1-bk4-lem/include/linux/usb.h 2005-01-16
13:58:22.000000000 -0800
@@ -22,6 +22,7 @@
struct usb_device;
struct usb_driver;
+struct urb;
/*-------------------------------------------------------------------------*/
@@ -285,6 +286,10 @@ struct usb_bus {
struct class_device class_dev; /* class device for this bus */
void (*release)(struct usb_bus *bus); /* function to destroy this
bus's memory */
+#if defined(CONFIG_USB_MON) || defined(CONFIG_USB_MON_MODULE)
+ struct mon_bus *mon_bus;
+ int (*mon_submit)(struct urb *, int mem_flags);
+#endif
};
#define to_usb_bus(d) container_of(d, struct usb_bus, class_dev)
@@ -616,7 +621,6 @@ struct usb_iso_packet_descriptor {
unsigned int status;
};
-struct urb;
struct pt_regs;
typedef void (*usb_complete_t)(struct urb *, struct pt_regs *);
@@ -814,6 +818,10 @@ struct urb
int error_count; /* (return) number of ISO errors */
void *context; /* (in) context for completion */
usb_complete_t complete; /* (in) completion routine */
+#if defined(CONFIG_USB_MON) || defined(CONFIG_USB_MON_MODULE)
+ usb_complete_t mon_saved_complete; /* (core private) */
+ struct mon_bus *mon_bus; /* (core private) */
+#endif
struct usb_iso_packet_descriptor iso_frame_desc[0]; /* (in) ISO
ONLY */
};
diff -urpN -X dontdiff linux-2.6.11-rc1-bk4/init/main.c
linux-2.6.11-rc1-bk4-lem/init/main.c
--- linux-2.6.11-rc1-bk4/init/main.c 2005-01-12 16:21:03.000000000 -0800
+++ linux-2.6.11-rc1-bk4-lem/init/main.c 2005-01-16 13:58:22.000000000
-0800
@@ -629,6 +629,8 @@ static inline void fixup_cpu_present_map
static int init(void * unused)
{
+ int rc;
+
lock_kernel();
/*
* Tell the world that we're going to be the grim
@@ -676,8 +678,8 @@ static int init(void * unused)
system_state = SYSTEM_RUNNING;
numa_default_policy();
- if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0)
- printk("Warning: unable to open an initial console.\n");
+ if ((rc = sys_open((const char __user *) "/dev/console", O_RDWR, 0)) <
0)
+ printk("Warning: unable to open an initial console (%d).\n",
rc);
(void) sys_dup(0);
(void) sys_dup(0);
-------------------------------------------------------
The SF.Net email is sponsored by: Beat the post-holiday blues
Get a FREE limited edition SourceForge.net t-shirt from ThinkGeek.
It's fun and FREE -- well, almost....http://www.thinkgeek.com/sfshirt
_______________________________________________
[email protected]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel