From 92d4251090865cf23cc0fae9fdf1d57244d34fa9 Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Fri, 3 Sep 2004 17:24:05 +0200 Subject: [PATCH] USB: update of help text for hpusbscsi Signed-Off-By: Oliver Neukum Signed-off-by: Greg Kroah-Hartman --- drivers/usb/image/Kconfig | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/image/Kconfig b/drivers/usb/image/Kconfig index 0b0f80e4f9a7..b541b67d2eb6 100644 --- a/drivers/usb/image/Kconfig +++ b/drivers/usb/image/Kconfig @@ -30,11 +30,12 @@ config USB_MICROTEK This driver can be compiled as a module, called microtek. config USB_HPUSBSCSI - tristate "HP53xx USB scanner support (EXPERIMENTAL)" - depends on USB && SCSI && EXPERIMENTAL + tristate "HP53xx USB scanner support" + depends on USB && SCSI help Say Y here if you want support for the HP 53xx series of scanners - and the Minolta Scan Dual. This driver is experimental. + and the Minolta Scan Dual. The scanner will be accessible as a SCSI device. + Please note that recent versions of SANE use usbfs, not this driver. This can be compiled as a module, called hpusbscsi. -- cgit v1.2.3 From ba6cf9f4aefe91625b75b3970be06194b723568b Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Fri, 3 Sep 2004 17:24:41 +0200 Subject: [PATCH] USB: switching microtek to usb_kill_urb this reworks the microtek driver's disconnect method, now that usb_kill_urb is available. Signed-Off-By: Oliver Neukum Signed-off-by: Greg Kroah-Hartman --- drivers/usb/image/microtek.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/image/microtek.c b/drivers/usb/image/microtek.c index 9d3aa3075086..c54aa114a51e 100644 --- a/drivers/usb/image/microtek.c +++ b/drivers/usb/image/microtek.c @@ -324,7 +324,7 @@ static inline void mts_urb_abort(struct mts_desc* desc) { MTS_DEBUG_GOT_HERE(); mts_debug_dump(desc); - usb_unlink_urb( desc->urb ); + usb_kill_urb( desc->urb ); } static int mts_scsi_abort (Scsi_Cmnd *srb) @@ -822,9 +822,8 @@ static void mts_usb_disconnect (struct usb_interface *intf) usb_set_intfdata(intf, NULL); + usb_kill_urb(desc->urb); scsi_remove_host(desc->host); - usb_unlink_urb(desc->urb); - scsi_host_put(desc->host); usb_free_urb(desc->urb); kfree(desc); -- cgit v1.2.3 From 98b069b9e50c88105ef9e7fa134554ae0cdcd044 Mon Sep 17 00:00:00 2001 From: Pete Zaitcev Date: Fri, 3 Sep 2004 17:25:14 +0200 Subject: [PATCH] USB: Fixes for ub in 2.4.9-rc1 from Oliver and Pat - Set the allocation size in REQUEST SENSE (Pat LaVarre) - Move add_timer invocations to safer places (Oliver Neukum) Signed-off-by: Greg Kroah-Hartman --- drivers/block/ub.c | 59 +++++++++++++++++++++++++++++------------------------- 1 file changed, 32 insertions(+), 27 deletions(-) (limited to 'drivers') diff --git a/drivers/block/ub.c b/drivers/block/ub.c index f605535d3f56..6379b9043631 100644 --- a/drivers/block/ub.c +++ b/drivers/block/ub.c @@ -786,17 +786,16 @@ static int ub_scsi_cmd_start(struct ub_dev *sc, struct ub_scsi_cmd *cmd) sc->work_urb.error_count = 0; sc->work_urb.status = 0; - sc->work_timer.expires = jiffies + UB_URB_TIMEOUT; - add_timer(&sc->work_timer); - if ((rc = usb_submit_urb(&sc->work_urb, GFP_ATOMIC)) != 0) { /* XXX Clear stalls */ printk("ub: cmd #%d start failed (%d)\n", cmd->tag, rc); /* P3 */ - del_timer(&sc->work_timer); ub_complete(&sc->work_done); return rc; } + sc->work_timer.expires = jiffies + UB_URB_TIMEOUT; + add_timer(&sc->work_timer); + cmd->state = UB_CMDST_CMD; ub_cmdtr_state(sc, cmd); return 0; @@ -968,18 +967,17 @@ static void ub_scsi_urb_compl(struct ub_dev *sc, struct ub_scsi_cmd *cmd) sc->work_urb.error_count = 0; sc->work_urb.status = 0; - sc->work_timer.expires = jiffies + UB_URB_TIMEOUT; - add_timer(&sc->work_timer); - if ((rc = usb_submit_urb(&sc->work_urb, GFP_ATOMIC)) != 0) { /* XXX Clear stalls */ printk("ub: data #%d submit failed (%d)\n", cmd->tag, rc); /* P3 */ - del_timer(&sc->work_timer); ub_complete(&sc->work_done); ub_state_done(sc, cmd, rc); return; } + sc->work_timer.expires = jiffies + UB_URB_TIMEOUT; + add_timer(&sc->work_timer); + cmd->state = UB_CMDST_DATA; ub_cmdtr_state(sc, cmd); @@ -1063,19 +1061,18 @@ static void ub_scsi_urb_compl(struct ub_dev *sc, struct ub_scsi_cmd *cmd) sc->work_urb.error_count = 0; sc->work_urb.status = 0; - sc->work_timer.expires = jiffies + UB_URB_TIMEOUT; - add_timer(&sc->work_timer); - rc = usb_submit_urb(&sc->work_urb, GFP_ATOMIC); if (rc != 0) { /* XXX Clear stalls */ printk("%s: CSW #%d submit failed (%d)\n", sc->name, cmd->tag, rc); /* P3 */ - del_timer(&sc->work_timer); ub_complete(&sc->work_done); ub_state_done(sc, cmd, rc); return; } + + sc->work_timer.expires = jiffies + UB_URB_TIMEOUT; + add_timer(&sc->work_timer); return; } @@ -1186,18 +1183,17 @@ static void ub_state_stat(struct ub_dev *sc, struct ub_scsi_cmd *cmd) sc->work_urb.error_count = 0; sc->work_urb.status = 0; - sc->work_timer.expires = jiffies + UB_URB_TIMEOUT; - add_timer(&sc->work_timer); - if ((rc = usb_submit_urb(&sc->work_urb, GFP_ATOMIC)) != 0) { /* XXX Clear stalls */ printk("ub: CSW #%d submit failed (%d)\n", cmd->tag, rc); /* P3 */ - del_timer(&sc->work_timer); ub_complete(&sc->work_done); ub_state_done(sc, cmd, rc); return; } + sc->work_timer.expires = jiffies + UB_URB_TIMEOUT; + add_timer(&sc->work_timer); + cmd->stat_count = 0; cmd->state = UB_CMDST_STAT; ub_cmdtr_state(sc, cmd); @@ -1217,9 +1213,17 @@ static void ub_state_sense(struct ub_dev *sc, struct ub_scsi_cmd *cmd) goto error; } + /* + * ``If the allocation length is eighteen or greater, and a device + * server returns less than eithteen bytes of data, the application + * client should assume that the bytes not transferred would have been + * zeroes had the device server returned those bytes.'' + */ memset(&sc->top_sense, 0, UB_SENSE_SIZE); + scmd = &sc->top_rqs_cmd; scmd->cdb[0] = REQUEST_SENSE; + scmd->cdb[4] = UB_SENSE_SIZE; scmd->cdb_len = 6; scmd->dir = UB_DIR_READ; scmd->state = UB_CMDST_INIT; @@ -1271,14 +1275,13 @@ static int ub_submit_clear_stall(struct ub_dev *sc, struct ub_scsi_cmd *cmd, sc->work_urb.error_count = 0; sc->work_urb.status = 0; - sc->work_timer.expires = jiffies + UB_CTRL_TIMEOUT; - add_timer(&sc->work_timer); - if ((rc = usb_submit_urb(&sc->work_urb, GFP_ATOMIC)) != 0) { - del_timer(&sc->work_timer); ub_complete(&sc->work_done); return rc; } + + sc->work_timer.expires = jiffies + UB_CTRL_TIMEOUT; + add_timer(&sc->work_timer); return 0; } @@ -1289,6 +1292,9 @@ static void ub_top_sense_done(struct ub_dev *sc, struct ub_scsi_cmd *scmd) unsigned char *sense = scmd->data; struct ub_scsi_cmd *cmd; + /* + * Ignoring scmd->act_len, because the buffer was pre-zeroed. + */ ub_cmdtr_sense(sc, scmd, sense); if ((cmd = ub_cmdq_peek(sc)) == NULL) { @@ -1725,19 +1731,18 @@ static int ub_probe_clear_stall(struct ub_dev *sc, int stalled_pipe) sc->work_urb.error_count = 0; sc->work_urb.status = 0; - init_timer(&timer); - timer.function = ub_probe_timeout; - timer.data = (unsigned long) &compl; - timer.expires = jiffies + UB_CTRL_TIMEOUT; - add_timer(&timer); - if ((rc = usb_submit_urb(&sc->work_urb, GFP_KERNEL)) != 0) { printk(KERN_WARNING "%s: Unable to submit a probe clear (%d)\n", sc->name, rc); - del_timer_sync(&timer); return rc; } + init_timer(&timer); + timer.function = ub_probe_timeout; + timer.data = (unsigned long) &compl; + timer.expires = jiffies + UB_CTRL_TIMEOUT; + add_timer(&timer); + wait_for_completion(&compl); del_timer_sync(&timer); -- cgit v1.2.3 From 9e0aea63d90fc678e3d2ed5c038d657e4fabaa2c Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Fri, 3 Sep 2004 17:25:50 +0200 Subject: [PATCH] USB: Make usbcore use usb_kill_urb() This patch changes the only places in usbcore where usb_unlink_urb() is still used for synchronous unlinking; now they will use usb_kill_urb(). As it turns out, there were only a couple of changes needed. This still leaves all the drivers to audit! Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/devio.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 776c1bf0df9b..17ca7690dbde 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -286,9 +286,10 @@ static void destroy_async (struct dev_state *ps, struct list_head *list) while (!list_empty(list)) { as = list_entry(list->next, struct async, asynclist); list_del_init(&as->asynclist); + + /* drop the spinlock so the completion handler can run */ spin_unlock_irqrestore(&ps->lock, flags); - /* usb_unlink_urb calls the completion handler with status == -ENOENT */ - usb_unlink_urb(as->urb); + usb_kill_urb(as->urb); spin_lock_irqsave(&ps->lock, flags); } spin_unlock_irqrestore(&ps->lock, flags); @@ -976,7 +977,7 @@ static int proc_unlinkurb(struct dev_state *ps, void __user *arg) as = async_getpending(ps, arg); if (!as) return -EINVAL; - usb_unlink_urb(as->urb); + usb_kill_urb(as->urb); return 0; } -- cgit v1.2.3 From 465f27945df776ee9b640bc02380db7b595f7209 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Fri, 3 Sep 2004 17:26:21 +0200 Subject: [PATCH] USB: Suspend/resume/wakeup support for UHCI root hub ports This patch adds support for suspending, resuming, and remote wakeup detection on root hub ports to the UHCI driver. It doesn't add support for suspending or resuming the root hub itself (beyond what's already there) -- that will require considerably more work. But at least devices plugged directly into the computer will interact nicely with power management. Of lesser importance, the patch also simplifies some constant expressions. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/uhci-hcd.c | 2 + drivers/usb/host/uhci-hcd.h | 6 +++ drivers/usb/host/uhci-hub.c | 91 ++++++++++++++++++++++++++++++++------------- 3 files changed, 73 insertions(+), 26 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index 00e6eb15a6ce..fe924e65ba35 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c @@ -1661,6 +1661,8 @@ static void stall_callback(unsigned long ptr) /* Poll for and perform state transitions */ hc_state_transitions(uhci); + if (unlikely(uhci->suspended_ports && uhci->state != UHCI_SUSPENDED)) + uhci_check_resume(uhci); init_stall_timer(hcd); } diff --git a/drivers/usb/host/uhci-hcd.h b/drivers/usb/host/uhci-hcd.h index b4bf3b6cac38..3a6735740364 100644 --- a/drivers/usb/host/uhci-hcd.h +++ b/drivers/usb/host/uhci-hcd.h @@ -352,6 +352,12 @@ struct uhci_hcd { int resume_detect; /* Need a Global Resume */ unsigned int saved_framenumber; /* Save during PM suspend */ + /* Support for port suspend/resume */ + unsigned long port_c_suspend; /* Bit-arrays of ports */ + unsigned long suspended_ports; + unsigned long resuming_ports; + unsigned long resume_timeout; /* Time to stop signalling */ + /* Main list of URB's currently controlled by this HC */ struct list_head urb_list; /* P: uhci->schedule_lock */ diff --git a/drivers/usb/host/uhci-hub.c b/drivers/usb/host/uhci-hub.c index 2dde381632e9..e869ad4061f1 100644 --- a/drivers/usb/host/uhci-hub.c +++ b/drivers/usb/host/uhci-hub.c @@ -36,13 +36,13 @@ static __u8 root_hub_hub_des[] = static int uhci_hub_status_data(struct usb_hcd *hcd, char *buf) { struct uhci_hcd *uhci = hcd_to_uhci(hcd); - unsigned long io_addr = uhci->io_addr; - int i; + int port; *buf = 0; - for (i = 0; i < uhci->rh_numports; i++) { - if (inw(io_addr + USBPORTSC1 + i * 2) & RWC_BITS) - *buf |= (1 << (i + 1)); + for (port = 0; port < uhci->rh_numports; ++port) { + if ((inw(uhci->io_addr + USBPORTSC1 + port * 2) & RWC_BITS) || + test_bit(port, &uhci->port_c_suspend)) + *buf |= (1 << (port + 1)); } return !!*buf; } @@ -62,6 +62,35 @@ static int uhci_hub_status_data(struct usb_hcd *hcd, char *buf) status &= ~(RWC_BITS|WZ_BITS); \ outw(status, port_addr) +/* UHCI controllers don't automatically stop resume signalling after 20 msec, + * so we have to poll and check timeouts in order to take care of it. + * FIXME: Synchronize access to these fields by a spinlock. + */ +static void uhci_check_resume(struct uhci_hcd *uhci) +{ + unsigned int port; + unsigned int port_addr; + int status; + + for (port = 0; port < uhci->rh_numports; ++port) { + port_addr = uhci->io_addr + USBPORTSC1 + 2 * port; + if (unlikely(inw(port_addr) & USBPORTSC_RD)) { + if (!test_bit(port, &uhci->resuming_ports)) { + + /* Port received a wakeup request */ + set_bit(port, &uhci->resuming_ports); + uhci->resume_timeout = jiffies + + msecs_to_jiffies(20); + } else if (time_after_eq(jiffies, + uhci->resume_timeout)) { + CLR_RH_PORTSTAT(USBPORTSC_SUSP | USBPORTSC_RD); + clear_bit(port, &uhci->resuming_ports); + clear_bit(port, &uhci->suspended_ports); + set_bit(port, &uhci->port_c_suspend); + } + } + } +} /* size of returned buffer is part of USB spec */ static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, @@ -69,8 +98,9 @@ static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, { struct uhci_hcd *uhci = hcd_to_uhci(hcd); int status, retval = 0, len = 0; - unsigned long port_addr = uhci->io_addr + USBPORTSC1 + 2 * (wIndex-1); - __u16 wPortChange, wPortStatus; + unsigned int port = wIndex - 1; + unsigned long port_addr = uhci->io_addr + USBPORTSC1 + 2 * port; + u16 wPortChange, wPortStatus; switch (typeReq) { /* Request Destination: @@ -82,11 +112,15 @@ static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, */ case GetHubStatus: - *(__u32 *)buf = cpu_to_le32(0); + *(u32 *) buf = cpu_to_le32(0); OK(4); /* hub power */ case GetPortStatus: - if (!wIndex || wIndex > uhci->rh_numports) + if (port >= uhci->rh_numports) goto err; + + if (uhci->resuming_ports) + uhci_check_resume(uhci); + status = inw(port_addr); /* Intel controllers report the OverCurrent bit active on. @@ -97,37 +131,39 @@ static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, PCI_VENDOR_ID_VIA) status ^= USBPORTSC_OC; - /* UHCI doesn't support C_SUSPEND and C_RESET (always false) */ + /* UHCI doesn't support C_RESET (always false) */ wPortChange = 0; if (status & USBPORTSC_CSC) - wPortChange |= 1 << (USB_PORT_FEAT_C_CONNECTION - 16); + wPortChange |= USB_PORT_STAT_C_CONNECTION; if (status & USBPORTSC_PEC) - wPortChange |= 1 << (USB_PORT_FEAT_C_ENABLE - 16); + wPortChange |= USB_PORT_STAT_C_ENABLE; if (status & USBPORTSC_OCC) - wPortChange |= 1 << (USB_PORT_FEAT_C_OVER_CURRENT - 16); + wPortChange |= USB_PORT_STAT_C_OVERCURRENT; + if (test_bit(port, &uhci->port_c_suspend)) + wPortChange |= USB_PORT_STAT_C_SUSPEND; /* UHCI has no power switching (always on) */ - wPortStatus = 1 << USB_PORT_FEAT_POWER; + wPortStatus = USB_PORT_STAT_POWER; if (status & USBPORTSC_CCS) - wPortStatus |= 1 << USB_PORT_FEAT_CONNECTION; + wPortStatus |= USB_PORT_STAT_CONNECTION; if (status & USBPORTSC_PE) { - wPortStatus |= 1 << USB_PORT_FEAT_ENABLE; + wPortStatus |= USB_PORT_STAT_ENABLE; if (status & (USBPORTSC_SUSP | USBPORTSC_RD)) - wPortStatus |= 1 << USB_PORT_FEAT_SUSPEND; + wPortStatus |= USB_PORT_STAT_SUSPEND; } if (status & USBPORTSC_OC) - wPortStatus |= 1 << USB_PORT_FEAT_OVER_CURRENT; + wPortStatus |= USB_PORT_STAT_OVERCURRENT; if (status & USBPORTSC_PR) - wPortStatus |= 1 << USB_PORT_FEAT_RESET; + wPortStatus |= USB_PORT_STAT_RESET; if (status & USBPORTSC_LSDA) - wPortStatus |= 1 << USB_PORT_FEAT_LOWSPEED; + wPortStatus |= USB_PORT_STAT_LOW_SPEED; if (wPortChange) dev_dbg(uhci_dev(uhci), "port %d portsc %04x\n", wIndex, status); - *(__u16 *)buf = cpu_to_le16(wPortStatus); - *(__u16 *)(buf + 2) = cpu_to_le16(wPortChange); + *(u16 *) buf = cpu_to_le16(wPortStatus); + *(u16 *) (buf + 2) = cpu_to_le16(wPortChange); OK(4); case SetHubFeature: /* We don't implement these */ case ClearHubFeature: @@ -140,11 +176,12 @@ static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, } break; case SetPortFeature: - if (!wIndex || wIndex > uhci->rh_numports) + if (port >= uhci->rh_numports) goto err; switch (wValue) { case USB_PORT_FEAT_SUSPEND: + set_bit(port, &uhci->suspended_ports); SET_RH_PORTSTAT(USBPORTSC_SUSP); OK(0); case USB_PORT_FEAT_RESET: @@ -164,7 +201,7 @@ static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, } break; case ClearPortFeature: - if (!wIndex || wIndex > uhci->rh_numports) + if (port >= uhci->rh_numports) goto err; switch (wValue) { @@ -175,10 +212,12 @@ static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, CLR_RH_PORTSTAT(USBPORTSC_PEC); OK(0); case USB_PORT_FEAT_SUSPEND: - CLR_RH_PORTSTAT(USBPORTSC_SUSP); + set_bit(port, &uhci->resuming_ports); + uhci->resume_timeout = jiffies + msecs_to_jiffies(20); + SET_RH_PORTSTAT(USBPORTSC_RD); OK(0); case USB_PORT_FEAT_C_SUSPEND: - /* this driver won't report these */ + clear_bit(port, &uhci->port_c_suspend); OK(0); case USB_PORT_FEAT_POWER: /* UHCI has no power switching */ -- cgit v1.2.3 From cab5916ae0e171fb74728e1f1bdcfa06c6957da9 Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Fri, 3 Sep 2004 17:26:56 +0200 Subject: [PATCH] USB: correct interrupt interval for kaweth - overriding the media check interrupt interval Signed-Off-By: Oliver Neukum Signed-off-by: Greg Kroah-Hartman --- drivers/usb/net/kaweth.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/net/kaweth.c b/drivers/usb/net/kaweth.c index c366fe957060..a84569a43c39 100644 --- a/drivers/usb/net/kaweth.c +++ b/drivers/usb/net/kaweth.c @@ -668,7 +668,7 @@ static int kaweth_open(struct net_device *net) INTBUFFERSIZE, int_callback, kaweth, - 8); + 250); /* overriding the descriptor */ kaweth->irq_urb->transfer_dma = kaweth->intbufferhandle; kaweth->irq_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; -- cgit v1.2.3 From 5e2522ee43db9ae51e89b24c8c5d3a0192f2943a Mon Sep 17 00:00:00 2001 From: Wouter Van Hemel Date: Fri, 3 Sep 2004 17:27:27 +0200 Subject: [PATCH] USB: usb audio is for oss only Doh. Fixed. --- drivers/usb/class/Kconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/class/Kconfig b/drivers/usb/class/Kconfig index 3d49e9d8cb67..0561d0234f23 100644 --- a/drivers/usb/class/Kconfig +++ b/drivers/usb/class/Kconfig @@ -9,7 +9,8 @@ config USB_AUDIO depends on USB && SOUND help Say Y here if you want to connect USB audio equipment such as - speakers to your computer's USB port. + speakers to your computer's USB port. You only need this if you use + the OSS sound driver; ALSA has its own option for usb audio support. To compile this driver as a module, choose M here: the module will be called audio. -- cgit v1.2.3 From f388f977d2ef1a9f0c2e2a5e268ff8d390819c68 Mon Sep 17 00:00:00 2001 From: Petko Manolov Date: Fri, 3 Sep 2004 17:28:02 +0200 Subject: [PATCH] USB: small rtl8150 patch this one make rtl8150 auto-load its register values at reset. Not doing so is known to cause improper setup when the device is being reseted frequently. Signed-off-by: Greg Kroah-Hartman --- drivers/usb/net/rtl8150.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/net/rtl8150.c b/drivers/usb/net/rtl8150.c index 6724399513f3..c7daf82e3a34 100644 --- a/drivers/usb/net/rtl8150.c +++ b/drivers/usb/net/rtl8150.c @@ -20,7 +20,7 @@ #include /* Version Information */ -#define DRIVER_VERSION "v0.6.1 (2004/03/13)" +#define DRIVER_VERSION "v0.6.2 (2004/08/27)" #define DRIVER_AUTHOR "Petko Manolov " #define DRIVER_DESC "rtl8150 based usb-ethernet driver" @@ -344,7 +344,7 @@ static int rtl8150_set_mac_address(struct net_device *netdev, void *p) static int rtl8150_reset(rtl8150_t * dev) { - u8 data = 0x10; + u8 data = 0x11; int i = HZ; set_registers(dev, CR, 1, &data); -- cgit v1.2.3 From d4fcfc90ed7621d072e88e6e18f9ed84692a26cf Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Fri, 3 Sep 2004 17:28:34 +0200 Subject: [PATCH] USB: switching microtek to usb_kill_urb Am Dienstag, 31. August 2004 17:41 schrieb Alan Stern: > On Tue, 31 Aug 2004, Oliver Neukum wrote: > > Alan Stern > > > > > > @@ -822,9 +822,8 @@ > > > > usb_set_intfdata(intf, NULL); > > > > + usb_kill_urb(desc->urb); > > scsi_remove_host(desc->host); > > - usb_unlink_urb(desc->urb); > > - scsi_host_put(desc->host); > > > > usb_free_urb(desc->urb); > > kfree(desc); > > I think you still need the scsi_host_put(), to account for the fact that > scsi_host_alloc() sets the refcount to 1 initially. Right you are. Greg, please also apply this one. Signed-Off-By: Oliver Neukum Signed-off-by: Greg Kroah-Hartman --- drivers/usb/image/microtek.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/usb/image/microtek.c b/drivers/usb/image/microtek.c index c54aa114a51e..14b218239a8c 100644 --- a/drivers/usb/image/microtek.c +++ b/drivers/usb/image/microtek.c @@ -825,6 +825,7 @@ static void mts_usb_disconnect (struct usb_interface *intf) usb_kill_urb(desc->urb); scsi_remove_host(desc->host); + scsi_host_put(desc->host); usb_free_urb(desc->urb); kfree(desc); } -- cgit v1.2.3 From f6799f05b11b2709a21300cfe34de5612c7ba3a1 Mon Sep 17 00:00:00 2001 From: Al Borchers Date: Fri, 3 Sep 2004 17:29:11 +0200 Subject: [PATCH] USB: corrected digi_acceleport 2.6.9-rc1 fix for hang on disconnect - Fixed hang on disconnect in digi_acceleport USB serial driver. See http://bugme.osdl.org/show_bug.cgi?id=2459. Close after disconnect no longer tries to communicate with the device. Signed-off-by: Al Borchers Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/digi_acceleport.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c index f187b3165d57..6d01337eefc0 100644 --- a/drivers/usb/serial/digi_acceleport.c +++ b/drivers/usb/serial/digi_acceleport.c @@ -1553,13 +1553,17 @@ static void digi_close( struct usb_serial_port *port, struct file *filp ) dbg( "digi_close: TOP: port=%d, open_count=%d", priv->dp_port_num, port->open_count ); + /* if disconnected, just clear flags */ + if (!usb_get_intfdata(port->serial->interface)) + goto exit; + /* do cleanup only after final close on this port */ spin_lock_irqsave( &priv->dp_port_lock, flags ); priv->dp_in_close = 1; spin_unlock_irqrestore( &priv->dp_port_lock, flags ); /* tell line discipline to process only XON/XOFF */ - tty->closing = 1; + tty->closing = 1; /* wait for output to drain */ if( (filp->f_flags&(O_NDELAY|O_NONBLOCK)) == 0 ) { @@ -1624,6 +1628,7 @@ dbg( "digi_close: TOP: port=%d, open_count=%d", priv->dp_port_num, port->open_co tty->closing = 0; +exit: spin_lock_irqsave( &priv->dp_port_lock, flags ); priv->dp_write_urb_in_use = 0; priv->dp_in_close = 0; -- cgit v1.2.3 From 13319e150b1e6b880cbc72e0efcfccd6d00dbde5 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Fri, 3 Sep 2004 17:29:55 +0200 Subject: [PATCH] USB: OHCI init cleanups This ought to fix the NS9750 init issue, and make the AMD756 case at least somewhat better. It makes the init go "by the book" in more ways, and formalizes one quirk. Various OHCI init/reset cleanups for different silicion environments: - Reset a bit more "by the book". * Define a new quirk flag for the SiS and OPTi problem seen earlier. Since 2.4 we've always worked around that quirk, even though we've not seen that on other chips; but it's "wrong" and doesn't work on some chips (notably NetSilicon NS9750). The quirk still seems to be needed for SiS, but either this test machine is too fast for the OPTi problem to show up, or the frame timing setup problem there came from a now-fixed bug. * Look at the HC state before resetting it; depending on whether it was previously owned by BIOS, SMM, an OS, or nobody, different USB signaling (and timings) might be needed. * Re-init the frame timings right after soft reset, rather than later (potentially too much later). * Restore a reset in the PCI startup code, so this logic more closely resembles the non-PCI paths (future code sharing). It also makes it easier to guarantee a 1-millisecond ceiling between reset and "go". An earlier reset is being done to help workaround BIOS-related problems on some boards, but we may need an even earlier one (as a PCI quirk, before IRQs get reconfigured). - Add an explicit #define to disable the BIOS/SMM handoff; it's not just HPPA, many embedded chips don't expect BIOS either. - There are reports of AMD 756 machines disliking the OHCI suspend patch of a few months back. Erratum #10 partly explains that, so now root hubs won't autosuspend on those Slot-A era chips. - Other minor fixes * We've got lots of non-PCI OHCI now too, so comments shouldn't be assuming all-is-pci! * Hey, it's unsafe to call hc_reset() in IRQ (after unrecoverable error); so just force a soft reset, don't do the whole thing. Tested on half a dozen different OHCI versions, but maybe some other versions of OHCI will be sensitive to one of these changes. Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ohci-hcd.c | 74 +++++++++++++++++++++++++++++++-------------- drivers/usb/host/ohci-pci.c | 18 +++++++++++ drivers/usb/host/ohci.h | 1 + 3 files changed, 71 insertions(+), 22 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index a087e72b94ae..12cc2844906d 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -2,7 +2,7 @@ * OHCI HCD (Host Controller Driver) for USB. * * (C) Copyright 1999 Roman Weissgaerber - * (C) Copyright 2000-2002 David Brownell + * (C) Copyright 2000-2004 David Brownell * * [ Initialisation is based on Linus' ] * [ uhci code and gregs ohci fragments ] @@ -122,6 +122,16 @@ #define OHCI_INTR_INIT \ (OHCI_INTR_MIE | OHCI_INTR_UE | OHCI_INTR_RD | OHCI_INTR_WDH) +#ifdef __hppa__ +/* On PA-RISC, PDC can leave IR set incorrectly; ignore it there. */ +#define IR_DISABLE +#endif + +#ifdef CONFIG_ARCH_OMAP +/* OMAP doesn't support IR (no SMM; not needed) */ +#define IR_DISABLE +#endif + /*-------------------------------------------------------------------------*/ static const char hcd_name [] = "ohci_hcd"; @@ -407,10 +417,8 @@ static int hc_reset (struct ohci_hcd *ohci) /* also: power/overcurrent flags in roothub.a */ } - /* SMM owns the HC? not for long! - * On PA-RISC, PDC can leave IR set incorrectly; ignore it there. - */ -#ifndef __hppa__ +#ifndef IR_DISABLE + /* SMM owns the HC? not for long! */ if (ohci_readl (&ohci->regs->control) & OHCI_CTRL_IR) { ohci_dbg (ohci, "USB HC TakeOver from BIOS/SMM\n"); @@ -435,18 +443,40 @@ static int hc_reset (struct ohci_hcd *ohci) /* Disable HC interrupts */ writel (OHCI_INTR_MIE, &ohci->regs->intrdisable); - ohci_dbg (ohci, "reset, control = 0x%x\n", - ohci_readl (&ohci->regs->control)); - - /* Reset USB (needed by some controllers); RemoteWakeupConnected + /* Reset USB nearly "by the book". RemoteWakeupConnected * saved if boot firmware (BIOS/SMM/...) told us it's connected * (for OHCI integrated on mainboard, it normally is) */ ohci->hc_control = ohci_readl (&ohci->regs->control); - ohci->hc_control &= OHCI_CTRL_RWC; /* hcfs 0 = RESET */ - if (ohci->hc_control) + ohci_dbg (ohci, "resetting from state '%s', control = 0x%x\n", + hcfs2string (ohci->hc_control & OHCI_CTRL_HCFS), + ohci->hc_control); + + if (ohci->hc_control & OHCI_CTRL_RWC + && !(ohci->flags & OHCI_QUIRK_AMD756)) ohci->hcd.can_wakeup = 1; + + switch (ohci->hc_control & OHCI_CTRL_HCFS) { + case OHCI_USB_OPER: + temp = 0; + break; + case OHCI_USB_SUSPEND: + case OHCI_USB_RESUME: + ohci->hc_control &= OHCI_CTRL_RWC; + ohci->hc_control |= OHCI_USB_RESUME; + temp = 10 /* msec wait */; + break; + // case OHCI_USB_RESET: + default: + ohci->hc_control &= OHCI_CTRL_RWC; + ohci->hc_control |= OHCI_USB_RESET; + temp = 50 /* msec wait */; + break; + } writel (ohci->hc_control, &ohci->regs->control); + // flush the writes + (void) ohci_readl (&ohci->regs->control); + msleep(temp); if (power_switching) { unsigned ports = roothub_a (ohci) & RH_A_NDP; @@ -455,9 +485,8 @@ static int hc_reset (struct ohci_hcd *ohci) writel (RH_PS_LSDA, &ohci->regs->roothub.portstatus [temp]); } - // flush those pci writes + // flush those writes (void) ohci_readl (&ohci->regs->control); - msleep (50); /* HC Reset requires max 10 us delay */ writel (OHCI_HCR, &ohci->regs->cmdstatus); @@ -469,6 +498,7 @@ static int hc_reset (struct ohci_hcd *ohci) } udelay (1); } + periodic_reinit (ohci); /* now we're in the SUSPEND state ... must go OPERATIONAL * within 2msec else HC enters RESUME @@ -477,10 +507,11 @@ static int hc_reset (struct ohci_hcd *ohci) * (SiS, OPTi ...), so reset again instead. SiS doesn't need * this if we write fmInterval after we're OPERATIONAL. */ - writel (ohci->hc_control, &ohci->regs->control); - // flush those pci writes - (void) ohci_readl (&ohci->regs->control); - + if (ohci->flags & OHCI_QUIRK_INITRESET) { + writel (ohci->hc_control, &ohci->regs->control); + // flush those writes + (void) ohci_readl (&ohci->regs->control); + } return 0; } @@ -506,8 +537,6 @@ static int hc_start (struct ohci_hcd *ohci) /* a reset clears this */ writel ((u32) ohci->hcca_dma, &ohci->regs->hcca); - periodic_reinit (ohci); - /* some OHCI implementations are finicky about how they init. * bogus values here mean not even enumeration could work. */ @@ -553,7 +582,7 @@ static int hc_start (struct ohci_hcd *ohci) writel (tmp, &ohci->regs->roothub.a); writel (RH_HS_LPSC, &ohci->regs->roothub.status); writel (power_switching ? RH_B_PPCM : 0, &ohci->regs->roothub.b); - // flush those pci writes + // flush those writes (void) ohci_readl (&ohci->regs->control); // POTPGT delay is bits 24-31, in 2 ms units. @@ -620,7 +649,8 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd, struct pt_regs *ptregs) // e.g. due to PCI Master/Target Abort ohci_dump (ohci, 1); - hc_reset (ohci); + ohci->hc_control &= OHCI_CTRL_RWC; /* hcfs 0 = RESET */ + writel (ohci->hc_control, &ohci->regs->control); } if (ints & OHCI_INTR_RD) { @@ -655,7 +685,7 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd, struct pt_regs *ptregs) if (HCD_IS_RUNNING(ohci->hcd.state)) { writel (ints, ®s->intrstatus); writel (OHCI_INTR_MIE, ®s->intrenable); - // flush those pci writes + // flush those writes (void) ohci_readl (&ohci->regs->control); } diff --git a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c index 73d9aee657d5..bf0c5d8ec231 100644 --- a/drivers/usb/host/ohci-pci.c +++ b/drivers/usb/host/ohci-pci.c @@ -61,6 +61,7 @@ ohci_pci_start (struct usb_hcd *hcd) && pdev->device == 0x740c) { ohci->flags = OHCI_QUIRK_AMD756; ohci_info (ohci, "AMD756 erratum 4 workaround\n"); + // also somewhat erratum 10 (suspend/resume issues) } /* FIXME for some of the early AMD 760 southbridges, OHCI @@ -75,6 +76,8 @@ ohci_pci_start (struct usb_hcd *hcd) && pdev->device == 0xc861) { ohci_info (ohci, "WARNING: OPTi workarounds unavailable\n"); + /* OPTi sometimes acts wierd during init */ + ohci->flags = OHCI_QUIRK_INITRESET; } /* Check for NSC87560. We have to look at the bridge (fn1) to @@ -92,6 +95,12 @@ ohci_pci_start (struct usb_hcd *hcd) ohci_info (ohci, "Using NSC SuperIO setup\n"); } } + + /* SiS sometimes acts wierd during init */ + else if (pdev->vendor == PCI_VENDOR_ID_SI) { + ohci->flags = OHCI_QUIRK_INITRESET; + ohci_info(ohci, "SiS init quirk\n"); + } } @@ -101,6 +110,15 @@ ohci_pci_start (struct usb_hcd *hcd) return ret; } + /* NOTE: this is a second reset. the first one helps + * keep bios/smm irqs from making trouble, but it was + * probably more than 1msec ago... + */ + if (hc_reset (ohci) < 0) { + ohci_stop (hcd); + return -ENODEV; + } + if (hc_start (ohci) < 0) { ohci_err (ohci, "can't start\n"); ohci_stop (hcd); diff --git a/drivers/usb/host/ohci.h b/drivers/usb/host/ohci.h index dcc511662c2c..c60f445d0011 100644 --- a/drivers/usb/host/ohci.h +++ b/drivers/usb/host/ohci.h @@ -387,6 +387,7 @@ struct ohci_hcd { unsigned long flags; /* for HC bugs */ #define OHCI_QUIRK_AMD756 0x01 /* erratum #4 */ #define OHCI_QUIRK_SUPERIO 0x02 /* natsemi */ +#define OHCI_QUIRK_INITRESET 0x04 /* SiS, OPTi, ... */ // there are also chip quirks/bugs in init logic /* -- cgit v1.2.3 From b0c177eccd1e99941bf1eb70b6b5c636c6a9fdb5 Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Fri, 3 Sep 2004 17:30:33 +0200 Subject: [PATCH] USB: Add B&B Electronics VID/PIDs to ftdi_sio This patch adds VID/PIDs for a few FTDI-based USB serial devices from B&B Electronics to the ftdi_sio driver. Signed-off-by: Ian Abbott Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/ftdi_sio.c | 9 +++++++++ drivers/usb/serial/ftdi_sio.h | 8 ++++++++ 2 files changed, 17 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 9073ea84d8d3..b72f7ad4d05e 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -368,6 +368,9 @@ static struct usb_device_id id_table_8U232AM [] = { { USB_DEVICE_VER(INTREPID_VID, INTREPID_NEOVI_PID, 0, 0x3ff) }, { USB_DEVICE_VER(FALCOM_VID, FALCOM_TWIST_PID, 0, 0x3ff) }, { USB_DEVICE_VER(FTDI_VID, FTDI_SUUNTO_SPORTS_PID, 0, 0x3ff) }, + { USB_DEVICE_VER(BANDB_VID, BANDB_USOTL4_PID, 0, 0x3ff) }, + { USB_DEVICE_VER(BANDB_VID, BANDB_USTL4_PID, 0, 0x3ff) }, + { USB_DEVICE_VER(BANDB_VID, BANDB_USO9ML2_PID, 0, 0x3ff) }, { } /* Terminating entry */ }; @@ -478,6 +481,9 @@ static struct usb_device_id id_table_FT232BM [] = { { USB_DEVICE_VER(INTREPID_VID, INTREPID_NEOVI_PID, 0x400, 0xffff) }, { USB_DEVICE_VER(FALCOM_VID, FALCOM_TWIST_PID, 0x400, 0xffff) }, { USB_DEVICE_VER(FTDI_VID, FTDI_SUUNTO_SPORTS_PID, 0x400, 0xffff) }, + { USB_DEVICE_VER(BANDB_VID, BANDB_USOTL4_PID, 0x400, 0xffff) }, + { USB_DEVICE_VER(BANDB_VID, BANDB_USTL4_PID, 0x400, 0xffff) }, + { USB_DEVICE_VER(BANDB_VID, BANDB_USO9ML2_PID, 0x400, 0xffff) }, { } /* Terminating entry */ }; @@ -595,6 +601,9 @@ static struct usb_device_id id_table_combined [] = { { USB_DEVICE(INTREPID_VID, INTREPID_NEOVI_PID) }, { USB_DEVICE(FALCOM_VID, FALCOM_TWIST_PID) }, { USB_DEVICE(FTDI_VID, FTDI_SUUNTO_SPORTS_PID) }, + { USB_DEVICE(BANDB_VID, BANDB_USOTL4_PID) }, + { USB_DEVICE(BANDB_VID, BANDB_USTL4_PID) }, + { USB_DEVICE(BANDB_VID, BANDB_USO9ML2_PID) }, { } /* Terminating entry */ }; diff --git a/drivers/usb/serial/ftdi_sio.h b/drivers/usb/serial/ftdi_sio.h index 232213b02860..bc2b6d5aacaa 100644 --- a/drivers/usb/serial/ftdi_sio.h +++ b/drivers/usb/serial/ftdi_sio.h @@ -225,6 +225,14 @@ */ #define FTDI_SUUNTO_SPORTS_PID 0xF680 /* Suunto Sports instrument */ +/* + * Definitions for B&B Electronics products. + */ +#define BANDB_VID 0x0856 /* B&B Electronics Vendor ID */ +#define BANDB_USOTL4_PID 0xAC01 /* USOTL4 Isolated RS-485 Converter */ +#define BANDB_USTL4_PID 0xAC02 /* USTL4 RS-485 Converter */ +#define BANDB_USO9ML2_PID 0xAC03 /* USO9ML2 Isolated RS-232 Converter */ + /* Commands */ #define FTDI_SIO_RESET 0 /* Reset the port */ #define FTDI_SIO_MODEM_CTRL 1 /* Set the modem control register */ -- cgit v1.2.3 From 2894dd886913fbe990411ac061626b360bbc0262 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Fri, 3 Sep 2004 17:31:05 +0200 Subject: [PATCH] USB: "Lost sync on frames" error in konicawc module From: http://bugme.osdl.org/show_bug.cgi?id=3286 The kernel keeps printing "Lost sync on frames" error messages as soon as a program tries to access the webcam. No video data can be retrieved from the webcam. The following patch seems enough to solve the problem. (just inverting the order at which the old and new data blocks are sent to the user). Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- drivers/usb/media/konicawc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/media/konicawc.c b/drivers/usb/media/konicawc.c index 3c741a8f5b73..3376654ca051 100644 --- a/drivers/usb/media/konicawc.c +++ b/drivers/usb/media/konicawc.c @@ -362,8 +362,8 @@ static void konicawc_isoc_irq(struct urb *urb, struct pt_regs *regs) else if (!urb->status && !cam->last_data_urb->status) len = konicawc_compress_iso(uvd, cam->last_data_urb, urb); - resubmit_urb(uvd, urb); resubmit_urb(uvd, cam->last_data_urb); + resubmit_urb(uvd, urb); cam->last_data_urb = NULL; uvd->stats.urb_length = len; uvd->stats.data_count += len; -- cgit v1.2.3 From b374dfa1487fc64b03884da73322e4944df0b668 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Fri, 3 Sep 2004 17:31:41 +0200 Subject: [PATCH] USB: Remove inappropriate unusual_devs.h entry A couple of months ago you applied a patch from Torsten Scherer to create a new unusual_devs.h entry. In further discussions with him I learned that the entry wasn't needed to access the device; the only reason for it was as a workaround for some old, buggy hotplug program on his system. Now Evan Fletcher reports that the entry actively prevents him from using his device. For me that's the last straw, so here's a patch to remove the entry. Torsten should be okay if he simply upgrades his hotplug package or removes the buggy program. On Mon, 30 Aug 2004, Evan Fletcher wrote: > Hi list, > > I have a Bytecc 5.25" External Enclosure, model ME-320U2F, that has > both USB 2.0 and Firewire connections > (http://www.byteccusa.com/product/enclosure/ME-320.htm). It used to > work fine on earlier Linux 2.6 kernels, but stopped working a few > revisions ago. > > I tracked the problem down to an entry in unusual_devs.h: > > /* : I don't know the name of the bridge > * manufacturer, but I've got an external USB drive by the Revoltec company > * that needs this. otherwise the drive is recognized as /dev/sda, but any > * access to it blocks indefinitely. > */ > UNUSUAL_DEV( 0x0402, 0x5621, 0x0103, 0x0103, > "Revoltec", > "USB/IDE Bridge (ATA/ATAPI)", > US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_FIX_INQUIRY), <...> > So, is there some way that this entry can be modified so that my DVD+R > works properly, and Mr. Scherer can still use his Revoltec external > disk? > > Thank you, > Evan Fletcher Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/unusual_devs.h | 10 ---------- 1 file changed, 10 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index 4fe25f6b0432..69554115a098 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -68,16 +68,6 @@ UNUSUAL_DEV( 0x03f0, 0x0307, 0x0001, 0x0001, US_SC_8070, US_PR_SCM_ATAPI, init_8200e, 0), #endif -/* : I don't know the name of the bridge - * manufacturer, but I've got an external USB drive by the Revoltec company - * that needs this. otherwise the drive is recognized as /dev/sda, but any - * access to it blocks indefinitely. - */ -UNUSUAL_DEV( 0x0402, 0x5621, 0x0103, 0x0103, - "Revoltec", - "USB/IDE Bridge (ATA/ATAPI)", - US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_FIX_INQUIRY), - /* Deduced by Jonathan Woithe * Entry needed for flags: US_FL_FIX_INQUIRY because initial inquiry message * always fails and confuses drive. -- cgit v1.2.3 From e3c4c5cd31f4ac69525de5e70bfaea9d86fded96 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 3 Sep 2004 17:32:12 +0200 Subject: [PATCH] HCD PCI probe: print actual, not ioremapped, address I think the USB HCD should print the actual PCI memory address, not the ioremapped address. AFAIK, there's no reason the ioremapped address has to have any fixed relationship to the actual address. Also, this makes it match what's in /proc/iomem. I also added a leading "0x". Example from ia64: - ehci_hcd 0000:00:01.2: irq 52, pci mem c000000080021000 + ehci_hcd 0000:00:01.2: irq 52, pci mem 0x80021000 USB HCD: print actual PCI mem address, not the ioremapped value. Signed-off-by: Bjorn Helgaas Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd-pci.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c index e7a7ba51371f..f6f9c3b2325c 100644 --- a/drivers/usb/core/hcd-pci.c +++ b/drivers/usb/core/hcd-pci.c @@ -188,9 +188,9 @@ clean_3: } hcd->irq = dev->irq; - dev_info (hcd->self.controller, "irq %s, %s %p\n", bufp, + dev_info (hcd->self.controller, "irq %s, %s 0x%lx\n", bufp, (driver->flags & HCD_MEMORY) ? "pci mem" : "io base", - base); + resource); usb_bus_init (&hcd->self); hcd->self.op = &usb_hcd_operations; -- cgit v1.2.3 From cbed817c10334e2baa05f087a4f65e17d0bbfd22 Mon Sep 17 00:00:00 2001 From: Maximilian Attems Date: Fri, 3 Sep 2004 17:32:49 +0200 Subject: [PATCH] usb/tiglusb: insert set_current_state() before schedule_timeout() Insert set_current_state() so schedule_timeout() functions as expected. Signed-off-by: Nishanth Aravamudan Signed-off-by: Maximilian Attems Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/tiglusb.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/usb/misc/tiglusb.c b/drivers/usb/misc/tiglusb.c index 0f9c5753772d..f902884a7bbb 100644 --- a/drivers/usb/misc/tiglusb.c +++ b/drivers/usb/misc/tiglusb.c @@ -115,6 +115,7 @@ tiglusb_open (struct inode *inode, struct file *filp) return -EBUSY; } + set_current_state(TASK_INTERRUPTIBLE); schedule_timeout (HZ / 2); if (signal_pending (current)) { -- cgit v1.2.3 From c7c58cf31825c7f89a5f8d5043057c54ba6d4001 Mon Sep 17 00:00:00 2001 From: Maximilian Attems Date: Fri, 3 Sep 2004 17:33:20 +0200 Subject: [PATCH] usb/dabusb: insert set_current_state() before schedule_timeout() After discussing this patch with Mark Hollomon, I think it is much safer / better to leave the conditional check within the while loop. This way the mutex state is as expected and maintainability is not compromised. The previous patch should not be applied. Description: Inserts appropriate set_current_state() call so that schedule_timeout() functions as expected. Signed-off-by: Nishanth Aravamudan Signed-off-by: Maximilian Attems Signed-off-by: Greg Kroah-Hartman --- drivers/usb/media/dabusb.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/usb/media/dabusb.c b/drivers/usb/media/dabusb.c index 0e5425f328d0..4cc6e57dfa30 100644 --- a/drivers/usb/media/dabusb.c +++ b/drivers/usb/media/dabusb.c @@ -598,6 +598,7 @@ static int dabusb_open (struct inode *inode, struct file *file) if (file->f_flags & O_NONBLOCK) { return -EBUSY; } + set_current_state(TASK_INTERRUPTIBLE); schedule_timeout (HZ / 2); if (signal_pending (current)) { -- cgit v1.2.3 From 6e14531432bcc3d41a640f6ce77a65a91f03a98f Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Fri, 3 Sep 2004 17:33:55 +0200 Subject: [PATCH] USB: Nag message for usb_kill_urb This patch is only for nuisance value. It puts a nag message in the system log every time usb_unlink_urb() is called for synchronous unlinking. My hope is this will speed the process of converting drivers to use usb_kill_urb(). Don't apply this if it generates too much noise, but otherwise go ahead. A little prodding never hurt anyone. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/urb.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c index 3c14361bbeb3..376e0e939d00 100644 --- a/drivers/usb/core/urb.c +++ b/drivers/usb/core/urb.c @@ -451,6 +451,9 @@ int usb_unlink_urb(struct urb *urb) if (!urb) return -EINVAL; if (!(urb->transfer_flags & URB_ASYNC_UNLINK)) { + printk(KERN_NOTICE "usb_unlink_urb() is deprecated for " + "synchronous unlinks. Use usb_kill_urb()\n"); + WARN_ON(1); usb_kill_urb(urb); return 0; } -- cgit v1.2.3 From cf59b8531ba7787a3e9871c96c699399aa06450e Mon Sep 17 00:00:00 2001 From: Al Borchers Date: Fri, 3 Sep 2004 18:29:06 +0200 Subject: [PATCH] USB: circular buffer for pl2303 Here is the patch adding a circular buffer to pl2303 updated to 2.6.9-rc1. Phil and I both tested this. (Phil tested a slightly different earlier version.) This fixes the carriage return newline problem Olaf Hering reported and helps Phil with hotsyncing his phone. This patch also fixes a problem that would sometimes leave a pl2303 port unable to send data, reporting "already writing", after closing the port while writing was in progress. This happened about 1/3 of the time in my tests with the stock 2.6.9-rc1 pl2303 driver. - Added a circular write buffer, protected by the existing spin lock. - Write_room and chars_in_buffer now report room and chars in the circular buffer. - Added a "bounce buffer" when transfering data from user space to the circular buffer--needed for locking. - Replaced (urb->status != -EINPROGRESS) with a private write_urb_in_use flag protected by the existing spin lock. Clear this flag when the urb is unlinked. - Free memory on failed startup. - These changes make ONLCR mapping work and fix a bug that would sometimes leave the port unable to write, reporting "already writing", after closing the port while writing was in progress. Signed-off-by: Al Borchers Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/pl2303.c | 349 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 324 insertions(+), 25 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index c61e56829a96..a9dc4ff4089b 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -55,11 +55,24 @@ /* * Version Information */ -#define DRIVER_VERSION "v0.11" +#define DRIVER_VERSION "v0.12" #define DRIVER_DESC "Prolific PL2303 USB to serial adaptor driver" static int debug; +#define PL2303_BUF_SIZE 1024 +#define PL2303_TMP_BUF_SIZE 1024 + +static char pl2303_tmp_buf[PL2303_TMP_BUF_SIZE]; +static DECLARE_MUTEX(pl2303_tmp_buf_sem); + +struct pl2303_buf { + unsigned int buf_size; + char *buf_buf; + char *buf_get; + char *buf_put; +}; + static struct usb_device_id id_table [] = { { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID) }, { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_RSAQ2) }, @@ -134,12 +147,23 @@ static void pl2303_read_bulk_callback (struct urb *urb, struct pt_regs *regs); static void pl2303_write_bulk_callback (struct urb *urb, struct pt_regs *regs); static int pl2303_write (struct usb_serial_port *port, int from_user, const unsigned char *buf, int count); +static void pl2303_send (struct usb_serial_port *port); +static int pl2303_write_room(struct usb_serial_port *port); +static int pl2303_chars_in_buffer(struct usb_serial_port *port); static void pl2303_break_ctl(struct usb_serial_port *port,int break_state); static int pl2303_tiocmget (struct usb_serial_port *port, struct file *file); static int pl2303_tiocmset (struct usb_serial_port *port, struct file *file, unsigned int set, unsigned int clear); static int pl2303_startup (struct usb_serial *serial); static void pl2303_shutdown (struct usb_serial *serial); +static struct pl2303_buf *pl2303_buf_alloc(unsigned int size); +static void pl2303_buf_free(struct pl2303_buf *pb); +static unsigned int pl2303_buf_data_avail(struct pl2303_buf *pb); +static unsigned int pl2303_buf_space_avail(struct pl2303_buf *pb); +static unsigned int pl2303_buf_put(struct pl2303_buf *pb, const char *buf, + unsigned int count); +static unsigned int pl2303_buf_get(struct pl2303_buf *pb, char *buf, + unsigned int count); /* All of the device info needed for the PL2303 SIO serial converter */ @@ -162,6 +186,8 @@ static struct usb_serial_device_type pl2303_device = { .read_bulk_callback = pl2303_read_bulk_callback, .read_int_callback = pl2303_read_int_callback, .write_bulk_callback = pl2303_write_bulk_callback, + .write_room = pl2303_write_room, + .chars_in_buffer = pl2303_chars_in_buffer, .attach = pl2303_startup, .shutdown = pl2303_shutdown, }; @@ -174,6 +200,8 @@ enum pl2303_type { struct pl2303_private { spinlock_t lock; + struct pl2303_buf *buf; + int write_urb_in_use; wait_queue_head_t delta_msr_wait; u8 line_control; u8 line_status; @@ -201,14 +229,28 @@ static int pl2303_startup (struct usb_serial *serial) for (i = 0; i < serial->num_ports; ++i) { priv = kmalloc (sizeof (struct pl2303_private), GFP_KERNEL); if (!priv) - return -ENOMEM; + goto cleanup; memset (priv, 0x00, sizeof (struct pl2303_private)); spin_lock_init(&priv->lock); + priv->buf = pl2303_buf_alloc(PL2303_BUF_SIZE); + if (priv->buf == NULL) { + kfree(priv); + goto cleanup; + } init_waitqueue_head(&priv->delta_msr_wait); priv->type = type; usb_set_serial_port_data(serial->port[i], priv); } return 0; + +cleanup: + for (--i; i>=0; --i) { + priv = usb_get_serial_port_data(serial->port[i]); + pl2303_buf_free(priv->buf); + kfree(priv); + usb_set_serial_port_data(serial->port[i], NULL); + } + return -ENOMEM; } static int set_control_lines (struct usb_device *dev, u8 value) @@ -224,40 +266,109 @@ static int set_control_lines (struct usb_device *dev, u8 value) static int pl2303_write (struct usb_serial_port *port, int from_user, const unsigned char *buf, int count) { - int result; + struct pl2303_private *priv = usb_get_serial_port_data(port); + unsigned long flags; dbg("%s - port %d, %d bytes", __FUNCTION__, port->number, count); if (!count) return count; - if (port->write_urb->status == -EINPROGRESS) { - dbg("%s - already writing", __FUNCTION__); - return 0; - } - - count = (count > port->bulk_out_size) ? port->bulk_out_size : count; if (from_user) { - if (copy_from_user (port->write_urb->transfer_buffer, buf, count)) + if (count > PL2303_TMP_BUF_SIZE) + count = PL2303_TMP_BUF_SIZE; + down(&pl2303_tmp_buf_sem); + if (copy_from_user(pl2303_tmp_buf, buf, count)) { + up(&pl2303_tmp_buf_sem); return -EFAULT; - } else { - memcpy (port->write_urb->transfer_buffer, buf, count); + } + buf = pl2303_tmp_buf; } - + + spin_lock_irqsave(&priv->lock, flags); + count = pl2303_buf_put(priv->buf, buf, count); + spin_unlock_irqrestore(&priv->lock, flags); + + if (from_user) + up(&pl2303_tmp_buf_sem); + + pl2303_send(port); + + return count; +} + +static void pl2303_send(struct usb_serial_port *port) +{ + int count, result; + struct pl2303_private *priv = usb_get_serial_port_data(port); + unsigned long flags; + + dbg("%s - port %d", __FUNCTION__, port->number); + + spin_lock_irqsave(&priv->lock, flags); + + if (priv->write_urb_in_use) { + spin_unlock_irqrestore(&priv->lock, flags); + return; + } + + count = pl2303_buf_get(priv->buf, port->write_urb->transfer_buffer, + port->bulk_out_size); + + if (count == 0) { + spin_unlock_irqrestore(&priv->lock, flags); + return; + } + + priv->write_urb_in_use = 1; + + spin_unlock_irqrestore(&priv->lock, flags); + usb_serial_debug_data(debug, &port->dev, __FUNCTION__, count, port->write_urb->transfer_buffer); port->write_urb->transfer_buffer_length = count; port->write_urb->dev = port->serial->dev; result = usb_submit_urb (port->write_urb, GFP_ATOMIC); - if (result) + if (result) { dev_err(&port->dev, "%s - failed submitting write urb, error %d\n", __FUNCTION__, result); - else - result = count; + priv->write_urb_in_use = 0; + // TODO: reschedule pl2303_send + } - return result; + schedule_work(&port->work); } +static int pl2303_write_room(struct usb_serial_port *port) +{ + struct pl2303_private *priv = usb_get_serial_port_data(port); + int room = 0; + unsigned long flags; + + dbg("%s - port %d", __FUNCTION__, port->number); + spin_lock_irqsave(&priv->lock, flags); + room = pl2303_buf_space_avail(priv->buf); + spin_unlock_irqrestore(&priv->lock, flags); + + dbg("%s - returns %d", __FUNCTION__, room); + return room; +} + +static int pl2303_chars_in_buffer(struct usb_serial_port *port) +{ + struct pl2303_private *priv = usb_get_serial_port_data(port); + int chars = 0; + unsigned long flags; + + dbg("%s - port %d", __FUNCTION__, port->number); + + spin_lock_irqsave(&priv->lock, flags); + chars = pl2303_buf_data_avail(priv->buf); + spin_unlock_irqrestore(&priv->lock, flags); + + dbg("%s - returns %d", __FUNCTION__, chars); + return chars; +} static void pl2303_set_termios (struct usb_serial_port *port, struct termios *old_termios) { @@ -422,7 +533,7 @@ static void pl2303_set_termios (struct usb_serial_port *port, struct termios *ol } kfree (buf); -} +} static int pl2303_open (struct usb_serial_port *port, struct file *filp) { @@ -461,7 +572,7 @@ static int pl2303_open (struct usb_serial_port *port, struct file *filp) FISH (VENDOR_READ_REQUEST_TYPE, VENDOR_READ_REQUEST, 0x8383, 0); SOUP (VENDOR_WRITE_REQUEST_TYPE, VENDOR_WRITE_REQUEST, 0, 1); SOUP (VENDOR_WRITE_REQUEST_TYPE, VENDOR_WRITE_REQUEST, 1, 0); - + if (priv->type == HX) { /* HX chip */ SOUP (VENDOR_WRITE_REQUEST_TYPE, VENDOR_WRITE_REQUEST, 2, 0x44); @@ -672,12 +783,17 @@ static void pl2303_break_ctl (struct usb_serial_port *port, int break_state) static void pl2303_shutdown (struct usb_serial *serial) { int i; + struct pl2303_private *priv; dbg("%s", __FUNCTION__); for (i = 0; i < serial->num_ports; ++i) { - kfree (usb_get_serial_port_data(serial->port[i])); - usb_set_serial_port_data(serial->port[i], NULL); + priv = usb_get_serial_port_data(serial->port[i]); + if (priv) { + pl2303_buf_free(priv->buf); + kfree(priv); + usb_set_serial_port_data(serial->port[i], NULL); + } } } @@ -815,11 +931,23 @@ static void pl2303_read_bulk_callback (struct urb *urb, struct pt_regs *regs) static void pl2303_write_bulk_callback (struct urb *urb, struct pt_regs *regs) { struct usb_serial_port *port = (struct usb_serial_port *) urb->context; + struct pl2303_private *priv = usb_get_serial_port_data(port); int result; dbg("%s - port %d", __FUNCTION__, port->number); - - if (urb->status) { + + switch (urb->status) { + case 0: + /* success */ + break; + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + /* this urb is terminated, clean up */ + dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status); + priv->write_urb_in_use = 0; + return; + default: /* error in the urb, so we have to resubmit it */ dbg("%s - Overflow in write", __FUNCTION__); dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, urb->status); @@ -828,14 +956,185 @@ static void pl2303_write_bulk_callback (struct urb *urb, struct pt_regs *regs) result = usb_submit_urb (port->write_urb, GFP_ATOMIC); if (result) dev_err(&urb->dev->dev, "%s - failed resubmitting write urb, error %d\n", __FUNCTION__, result); + else + return; + } - return; + priv->write_urb_in_use = 0; + + /* send any buffered data */ + pl2303_send(port); +} + + +/* + * pl2303_buf_alloc + * + * Allocate a circular buffer and all associated memory. + */ + +static struct pl2303_buf *pl2303_buf_alloc(unsigned int size) +{ + + struct pl2303_buf *pb; + + + if (size == 0) + return NULL; + + pb = (struct pl2303_buf *)kmalloc(sizeof(struct pl2303_buf), GFP_KERNEL); + if (pb == NULL) + return NULL; + + pb->buf_buf = kmalloc(size, GFP_KERNEL); + if (pb->buf_buf == NULL) { + kfree(pb); + return NULL; } - schedule_work(&port->work); + pb->buf_size = size; + pb->buf_get = pb->buf_put = pb->buf_buf; + + return pb; + +} + + +/* + * pl2303_buf_free + * + * Free the buffer and all associated memory. + */ + +void pl2303_buf_free(struct pl2303_buf *pb) +{ + if (pb != NULL) { + if (pb->buf_buf != NULL) + kfree(pb->buf_buf); + kfree(pb); + } } +/* + * pl2303_buf_data_avail + * + * Return the number of bytes of data available in the circular + * buffer. + */ + +static unsigned int pl2303_buf_data_avail(struct pl2303_buf *pb) +{ + if (pb != NULL) + return ((pb->buf_size + pb->buf_put - pb->buf_get) % pb->buf_size); + else + return 0; +} + + +/* + * pl2303_buf_space_avail + * + * Return the number of bytes of space available in the circular + * buffer. + */ + +static unsigned int pl2303_buf_space_avail(struct pl2303_buf *pb) +{ + if (pb != NULL) + return ((pb->buf_size + pb->buf_get - pb->buf_put - 1) % pb->buf_size); + else + return 0; +} + + +/* + * pl2303_buf_put + * + * Copy data data from a user buffer and put it into the circular buffer. + * Restrict to the amount of space available. + * + * Return the number of bytes copied. + */ + +static unsigned int pl2303_buf_put(struct pl2303_buf *pb, const char *buf, + unsigned int count) +{ + + unsigned int len; + + + if (pb == NULL) + return 0; + + len = pl2303_buf_space_avail(pb); + if (count > len) + count = len; + + if (count == 0) + return 0; + + len = pb->buf_buf + pb->buf_size - pb->buf_put; + if (count > len) { + memcpy(pb->buf_put, buf, len); + memcpy(pb->buf_buf, buf+len, count - len); + pb->buf_put = pb->buf_buf + count - len; + } else { + memcpy(pb->buf_put, buf, count); + if (count < len) + pb->buf_put += count; + else /* count == len */ + pb->buf_put = pb->buf_buf; + } + + return count; + +} + + +/* + * pl2303_buf_get + * + * Get data from the circular buffer and copy to the given buffer. + * Restrict to the amount of data available. + * + * Return the number of bytes copied. + */ + +static unsigned int pl2303_buf_get(struct pl2303_buf *pb, char *buf, + unsigned int count) +{ + + unsigned int len; + + + if (pb == NULL) + return 0; + + len = pl2303_buf_data_avail(pb); + if (count > len) + count = len; + + if (count == 0) + return 0; + + len = pb->buf_buf + pb->buf_size - pb->buf_get; + if (count > len) { + memcpy(buf, pb->buf_get, len); + memcpy(buf+len, pb->buf_buf, count - len); + pb->buf_get = pb->buf_buf + count - len; + } else { + memcpy(buf, pb->buf_get, count); + if (count < len) + pb->buf_get += count; + else /* count == len */ + pb->buf_get = pb->buf_buf; + } + + return count; + +} + static int __init pl2303_init (void) { int retval; -- cgit v1.2.3 From 66d2dc06414f5e975f4a27213806d4e71091c827 Mon Sep 17 00:00:00 2001 From: Al Borchers Date: Fri, 3 Sep 2004 18:29:45 +0200 Subject: [PATCH] USB: close waits for drain in pl2303 Here is an additional patch for pl2303 in 2.6.9-rc1, to be applied after my previous circular buffer patch. This patch waits for the buffer to drain on close and then clears the buffer. In addition to the obvious features, this also fixes a bug where closing a port with a full buffer would accidentally disable writes when the port was re-opened. The original pl2303 could lose data that was still going out the port on close, and even this patch only solves the problem completely for data rates of 1200 bps and above. Waiting for data to drain from the circular buffer is easy, but the problem is waiting for data to drain from the 256 byte hardware buffer on the device. I don't know how much data is in the hardware buffer, so I just wait long enough for a potentially full hardware buffer to drain, but I don't want to wait that long for low data rates if the buffer isn't full. There is probably some way to query the pl2303 to find out how much data is in its hardware buffer, maybe snooping would reveal that. - Added a wait for data to drain from the driver write buffer when closing. - Added a wait for the data to drain from the device hardware buffer when closing. - Cleared the driver write buffer when closing. Signed-off-by: Al Borchers Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/pl2303.c | 62 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 58 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index a9dc4ff4089b..416ea72951f6 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -60,6 +60,8 @@ static int debug; +#define PL2303_CLOSING_WAIT (30*HZ) + #define PL2303_BUF_SIZE 1024 #define PL2303_TMP_BUF_SIZE 1024 @@ -158,6 +160,7 @@ static int pl2303_startup (struct usb_serial *serial); static void pl2303_shutdown (struct usb_serial *serial); static struct pl2303_buf *pl2303_buf_alloc(unsigned int size); static void pl2303_buf_free(struct pl2303_buf *pb); +static void pl2303_buf_clear(struct pl2303_buf *pb); static unsigned int pl2303_buf_data_avail(struct pl2303_buf *pb); static unsigned int pl2303_buf_space_avail(struct pl2303_buf *pb); static unsigned int pl2303_buf_put(struct pl2303_buf *pb, const char *buf, @@ -615,13 +618,52 @@ static int pl2303_open (struct usb_serial_port *port, struct file *filp) static void pl2303_close (struct usb_serial_port *port, struct file *filp) { - struct pl2303_private *priv; + struct pl2303_private *priv = usb_get_serial_port_data(port); unsigned long flags; unsigned int c_cflag; int result; + int bps; + long timeout; + wait_queue_t wait; \ dbg("%s - port %d", __FUNCTION__, port->number); + /* wait for data to drain from the buffer */ + spin_lock_irqsave(&priv->lock, flags); + timeout = PL2303_CLOSING_WAIT; + init_waitqueue_entry(&wait, current); + add_wait_queue(&port->tty->write_wait, &wait); + for (;;) { + set_current_state(TASK_INTERRUPTIBLE); + if (pl2303_buf_data_avail(priv->buf) == 0 + || timeout == 0 || signal_pending(current) + || !usb_get_intfdata(port->serial->interface)) /* disconnect */ + break; + spin_unlock_irqrestore(&priv->lock, flags); + timeout = schedule_timeout(timeout); + spin_lock_irqsave(&priv->lock, flags); + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&port->tty->write_wait, &wait); + /* clear out any remaining data in the buffer */ + pl2303_buf_clear(priv->buf); + spin_unlock_irqrestore(&priv->lock, flags); + + /* wait for characters to drain from the device */ + /* (this is long enough for the entire 256 byte */ + /* pl2303 hardware buffer to drain with no flow */ + /* control for data rates of 1200 bps or more, */ + /* for lower rates we should really know how much */ + /* data is in the buffer to compute a delay */ + /* that is not unnecessarily long) */ + bps = tty_get_baud_rate(port->tty); + if (bps > 1200) + timeout = max((HZ*2560)/bps,HZ/10); + else + timeout = 2*HZ; + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(timeout); + /* shutdown our urbs */ dbg("%s - shutting down urbs", __FUNCTION__); result = usb_unlink_urb (port->write_urb); @@ -646,14 +688,12 @@ static void pl2303_close (struct usb_serial_port *port, struct file *filp) c_cflag = port->tty->termios->c_cflag; if (c_cflag & HUPCL) { /* drop DTR and RTS */ - priv = usb_get_serial_port_data(port); spin_lock_irqsave(&priv->lock, flags); priv->line_control = 0; spin_unlock_irqrestore (&priv->lock, flags); set_control_lines (port->serial->dev, 0); } } - } static int pl2303_tiocmset (struct usb_serial_port *port, struct file *file, @@ -1006,7 +1046,7 @@ static struct pl2303_buf *pl2303_buf_alloc(unsigned int size) * Free the buffer and all associated memory. */ -void pl2303_buf_free(struct pl2303_buf *pb) +static void pl2303_buf_free(struct pl2303_buf *pb) { if (pb != NULL) { if (pb->buf_buf != NULL) @@ -1016,6 +1056,20 @@ void pl2303_buf_free(struct pl2303_buf *pb) } +/* + * pl2303_buf_clear + * + * Clear out all data in the circular buffer. + */ + +static void pl2303_buf_clear(struct pl2303_buf *pb) +{ + if (pb != NULL) + pb->buf_get = pb->buf_put; + /* equivalent to a get of all data available */ +} + + /* * pl2303_buf_data_avail * -- cgit v1.2.3 From ee124eaab952e381d81ab0f4c63f31a4d7bc8bd3 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sun, 5 Sep 2004 08:53:18 +0200 Subject: USB: remove usbdevfs filesystem name, usbfs is the proper one to use. This has been publicised for years now, and the usvfs name will work just fine with a 2.4 kernel, so we are not breaking backwards compatibility. Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/devices.c | 4 +- drivers/usb/core/devio.c | 10 ++-- drivers/usb/core/inode.c | 121 +++++-------------------------------------- drivers/usb/core/usb.h | 6 +++ include/linux/usb.h | 2 - include/linux/usbdevice_fs.h | 10 ---- 6 files changed, 26 insertions(+), 127 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c index 8e535789fce7..15d773fc539d 100644 --- a/drivers/usb/core/devices.c +++ b/drivers/usb/core/devices.c @@ -149,7 +149,7 @@ static const struct class_info clas_info[] = /*****************************************************************/ -void usbdevfs_conn_disc_event(void) +void usbfs_conn_disc_event(void) { conndiscevcnt++; wake_up(&deviceconndiscwq); @@ -682,7 +682,7 @@ static loff_t usb_device_lseek(struct file * file, loff_t offset, int orig) return ret; } -struct file_operations usbdevfs_devices_fops = { +struct file_operations usbfs_devices_fops = { .llseek = usb_device_lseek, .read = usb_device_read, .poll = usb_device_poll, diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 17ca7690dbde..0b3bf6634ee4 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -21,7 +21,7 @@ * * $Id: devio.c,v 1.7 2000/02/01 17:28:48 fliegl Exp $ * - * This file implements the usbdevfs/x/y files, where + * This file implements the usbfs/x/y files, where * x is the bus number and y the device number. * * It allows user space programs/"drivers" to communicate directly @@ -354,7 +354,7 @@ static void driver_disconnect(struct usb_interface *intf) destroy_async_on_interface(ps, ifnum); } -struct usb_driver usbdevfs_driver = { +struct usb_driver usbfs_driver = { .owner = THIS_MODULE, .name = "usbfs", .probe = driver_probe, @@ -379,7 +379,7 @@ static int claimintf(struct dev_state *ps, unsigned int ifnum) if (!intf) err = -ENOENT; else - err = usb_driver_claim_interface(&usbdevfs_driver, intf, ps); + err = usb_driver_claim_interface(&usbfs_driver, intf, ps); up_write(&usb_bus_type.subsys.rwsem); if (err == 0) set_bit(ifnum, &ps->ifclaimed); @@ -402,7 +402,7 @@ static int releaseintf(struct dev_state *ps, unsigned int ifnum) if (!intf) err = -ENOENT; else if (test_and_clear_bit(ifnum, &ps->ifclaimed)) { - usb_driver_release_interface(&usbdevfs_driver, intf); + usb_driver_release_interface(&usbfs_driver, intf); err = 0; } up_write(&usb_bus_type.subsys.rwsem); @@ -1315,7 +1315,7 @@ static unsigned int usbdev_poll(struct file *file, struct poll_table_struct *wai return mask; } -struct file_operations usbdevfs_device_file_operations = { +struct file_operations usbfs_device_file_operations = { .llseek = usbdev_lseek, .read = usbdev_read, .poll = usbdev_poll, diff --git a/drivers/usb/core/inode.c b/drivers/usb/core/inode.c index c9f57e334a4f..24452d66d576 100644 --- a/drivers/usb/core/inode.c +++ b/drivers/usb/core/inode.c @@ -4,7 +4,7 @@ * inode.c -- Inode/Dentry functions for the USB device file system. * * Copyright (C) 2000 Thomas Sailer (sailer@ife.ee.ethz.ch) - * Copyright (C) 2001,2002 Greg Kroah-Hartman (greg@kroah.com) + * Copyright (C) 2001,2002,2004 Greg Kroah-Hartman (greg@kroah.com) * * 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 @@ -40,17 +40,15 @@ #include #include #include +#include "usb.h" static struct super_operations usbfs_ops; static struct file_operations default_file_operations; static struct inode_operations usbfs_dir_inode_operations; -static struct vfsmount *usbdevfs_mount; static struct vfsmount *usbfs_mount; -static int usbdevfs_mount_count; /* = 0 */ static int usbfs_mount_count; /* = 0 */ static int ignore_mount = 0; -static struct dentry *devices_usbdevfs_dentry; static struct dentry *devices_usbfs_dentry; static int num_buses; /* = 0 */ @@ -240,9 +238,6 @@ static int remount(struct super_block *sb, int *flags, char *data) if (usbfs_mount && usbfs_mount->mnt_sb) update_sb(usbfs_mount->mnt_sb); - if (usbdevfs_mount && usbdevfs_mount->mnt_sb) - update_sb(usbdevfs_mount->mnt_sb); - return 0; } @@ -561,28 +556,12 @@ static void fs_remove_file (struct dentry *dentry) /* --------------------------------------------------------------------- */ - - -/* - * The usbdevfs name is now deprecated (as of 2.5.1). - * It will be removed when the 2.7.x development cycle is started. - * You have been warned :) - */ -static struct file_system_type usbdevice_fs_type; - static struct super_block *usb_get_sb(struct file_system_type *fs_type, int flags, const char *dev_name, void *data) { return get_sb_single(fs_type, flags, data, usbfs_fill_super); } -static struct file_system_type usbdevice_fs_type = { - .owner = THIS_MODULE, - .name = "usbdevfs", - .get_sb = usb_get_sb, - .kill_sb = kill_litter_super, -}; - static struct file_system_type usb_fs_type = { .owner = THIS_MODULE, .name = "usbfs", @@ -603,16 +582,10 @@ static int create_special_files (void) ignore_mount = 1; /* create the devices special file */ - retval = simple_pin_fs("usbdevfs", &usbdevfs_mount, &usbdevfs_mount_count); - if (retval) { - err ("Unable to get usbdevfs mount"); - goto exit; - } - retval = simple_pin_fs("usbfs", &usbfs_mount, &usbfs_mount_count); if (retval) { err ("Unable to get usbfs mount"); - goto error_clean_usbdevfs_mount; + goto exit; } ignore_mount = 0; @@ -620,7 +593,7 @@ static int create_special_files (void) parent = usbfs_mount->mnt_sb->s_root; devices_usbfs_dentry = fs_create_file ("devices", listmode | S_IFREG, parent, - NULL, &usbdevfs_devices_fops, + NULL, &usbfs_devices_fops, listuid, listgid); if (devices_usbfs_dentry == NULL) { err ("Unable to create devices usbfs file"); @@ -628,42 +601,19 @@ static int create_special_files (void) goto error_clean_mounts; } - parent = usbdevfs_mount->mnt_sb->s_root; - devices_usbdevfs_dentry = fs_create_file ("devices", - listmode | S_IFREG, parent, - NULL, &usbdevfs_devices_fops, - listuid, listgid); - if (devices_usbdevfs_dentry == NULL) { - err ("Unable to create devices usbfs file"); - retval = -ENODEV; - goto error_remove_file; - } - goto exit; -error_remove_file: - fs_remove_file (devices_usbfs_dentry); - devices_usbfs_dentry = NULL; - error_clean_mounts: simple_release_fs(&usbfs_mount, &usbfs_mount_count); - -error_clean_usbdevfs_mount: - simple_release_fs(&usbdevfs_mount, &usbdevfs_mount_count); - exit: return retval; } static void remove_special_files (void) { - if (devices_usbdevfs_dentry) - fs_remove_file (devices_usbdevfs_dentry); if (devices_usbfs_dentry) fs_remove_file (devices_usbfs_dentry); - devices_usbdevfs_dentry = NULL; devices_usbfs_dentry = NULL; - simple_release_fs(&usbdevfs_mount, &usbdevfs_mount_count); simple_release_fs(&usbfs_mount, &usbfs_mount_count); } @@ -671,11 +621,6 @@ void usbfs_update_special (void) { struct inode *inode; - if (devices_usbdevfs_dentry) { - inode = devices_usbdevfs_dentry->d_inode; - if (inode) - inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; - } if (devices_usbfs_dentry) { inode = devices_usbfs_dentry->d_inode; if (inode) @@ -707,29 +652,16 @@ void usbfs_add_bus(struct usb_bus *bus) return; } - parent = usbdevfs_mount->mnt_sb->s_root; - bus->usbdevfs_dentry = fs_create_file (name, busmode | S_IFDIR, parent, - bus, NULL, busuid, busgid); - if (bus->usbdevfs_dentry == NULL) { - err ("error creating usbdevfs bus entry"); - return; - } - usbfs_update_special(); - usbdevfs_conn_disc_event(); + usbfs_conn_disc_event(); } - void usbfs_remove_bus(struct usb_bus *bus) { if (bus->usbfs_dentry) { fs_remove_file (bus->usbfs_dentry); bus->usbfs_dentry = NULL; } - if (bus->usbdevfs_dentry) { - fs_remove_file (bus->usbdevfs_dentry); - bus->usbdevfs_dentry = NULL; - } --num_buses; if (num_buses <= 0) { @@ -738,7 +670,7 @@ void usbfs_remove_bus(struct usb_bus *bus) } usbfs_update_special(); - usbdevfs_conn_disc_event(); + usbfs_conn_disc_event(); } void usbfs_add_device(struct usb_device *dev) @@ -750,20 +682,12 @@ void usbfs_add_device(struct usb_device *dev) sprintf (name, "%03d", dev->devnum); dev->usbfs_dentry = fs_create_file (name, devmode | S_IFREG, dev->bus->usbfs_dentry, dev, - &usbdevfs_device_file_operations, + &usbfs_device_file_operations, devuid, devgid); if (dev->usbfs_dentry == NULL) { err ("error creating usbfs device entry"); return; } - dev->usbdevfs_dentry = fs_create_file (name, devmode | S_IFREG, - dev->bus->usbdevfs_dentry, dev, - &usbdevfs_device_file_operations, - devuid, devgid); - if (dev->usbdevfs_dentry == NULL) { - err ("error creating usbdevfs device entry"); - return; - } /* Set the size of the device's file to be * equal to the size of the device descriptors. */ @@ -775,11 +699,9 @@ void usbfs_add_device(struct usb_device *dev) } if (dev->usbfs_dentry->d_inode) dev->usbfs_dentry->d_inode->i_size = i_size; - if (dev->usbdevfs_dentry->d_inode) - dev->usbdevfs_dentry->d_inode->i_size = i_size; usbfs_update_special(); - usbdevfs_conn_disc_event(); + usbfs_conn_disc_event(); } void usbfs_remove_device(struct usb_device *dev) @@ -791,10 +713,6 @@ void usbfs_remove_device(struct usb_device *dev) fs_remove_file (dev->usbfs_dentry); dev->usbfs_dentry = NULL; } - if (dev->usbdevfs_dentry) { - fs_remove_file (dev->usbdevfs_dentry); - dev->usbdevfs_dentry = NULL; - } while (!list_empty(&dev->filelist)) { ds = list_entry(dev->filelist.next, struct dev_state, list); list_del_init(&ds->list); @@ -807,51 +725,38 @@ void usbfs_remove_device(struct usb_device *dev) } } usbfs_update_special(); - usbdevfs_conn_disc_event(); + usbfs_conn_disc_event(); } /* --------------------------------------------------------------------- */ -#ifdef CONFIG_PROC_FS static struct proc_dir_entry *usbdir = NULL; -#endif int __init usbfs_init(void) { int retval; - retval = usb_register(&usbdevfs_driver); + retval = usb_register(&usbfs_driver); if (retval) return retval; retval = register_filesystem(&usb_fs_type); if (retval) { - usb_deregister(&usbdevfs_driver); - return retval; - } - retval = register_filesystem(&usbdevice_fs_type); - if (retval) { - unregister_filesystem(&usb_fs_type); - usb_deregister(&usbdevfs_driver); + usb_deregister(&usbfs_driver); return retval; } -#ifdef CONFIG_PROC_FS - /* create mount point for usbdevfs */ + /* create mount point for usbfs */ usbdir = proc_mkdir("usb", proc_bus); -#endif return 0; } void usbfs_cleanup(void) { - usb_deregister(&usbdevfs_driver); + usb_deregister(&usbfs_driver); unregister_filesystem(&usb_fs_type); - unregister_filesystem(&usbdevice_fs_type); -#ifdef CONFIG_PROC_FS if (usbdir) remove_proc_entry("usb", proc_bus); -#endif } diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index f1dff4f4d5d6..b44bb8d7cdfa 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -27,3 +27,9 @@ extern void usb_set_device_state(struct usb_device *udev, /* for labeling diagnostics */ extern const char *usbcore_name; + +/* usbfs stuff */ +extern struct usb_driver usbfs_driver; +extern struct file_operations usbfs_devices_fops; +extern struct file_operations usbfs_device_file_operations; +extern void usbfs_conn_disc_event(void); diff --git a/include/linux/usb.h b/include/linux/usb.h index a43c95a016d7..d07d5aba9e7f 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -264,7 +264,6 @@ struct usb_bus { int bandwidth_isoc_reqs; /* number of Isoc. requests */ struct dentry *usbfs_dentry; /* usbfs dentry entry for the bus */ - struct dentry *usbdevfs_dentry; /* usbdevfs dentry entry for the bus */ struct class_device class_dev; /* class device for this bus */ void (*release)(struct usb_bus *bus); /* function to destroy this bus's memory */ @@ -315,7 +314,6 @@ struct usb_device { struct list_head filelist; struct dentry *usbfs_dentry; /* usbfs dentry entry for the device */ - struct dentry *usbdevfs_dentry; /* usbdevfs dentry entry for the device */ /* * Child devices - these can be either new devices diff --git a/include/linux/usbdevice_fs.h b/include/linux/usbdevice_fs.h index 78f47434b757..af49afaf7bb4 100644 --- a/include/linux/usbdevice_fs.h +++ b/include/linux/usbdevice_fs.h @@ -166,16 +166,6 @@ struct dev_state { unsigned long ifclaimed; }; -/* internal methods & data */ -extern struct usb_driver usbdevfs_driver; -extern struct file_operations usbdevfs_drivers_fops; -extern struct file_operations usbdevfs_devices_fops; -extern struct file_operations usbdevfs_device_file_operations; -extern struct inode_operations usbdevfs_device_inode_operations; -extern struct inode_operations usbdevfs_bus_inode_operations; -extern struct file_operations usbdevfs_bus_file_operations; -extern void usbdevfs_conn_disc_event(void); - #endif /* __KERNEL__ */ /* --------------------------------------------------------------------- */ -- cgit v1.2.3 From 533c4dbbef975b507905b961fc5687b38f312000 Mon Sep 17 00:00:00 2001 From: Maximilian Attems Date: Sun, 5 Sep 2004 09:04:18 +0200 Subject: [PATCH] list_for_each_entry: drivers-usb-core-devices.c Make code more readable with list_for_each_entry. Compile tested. Patch incremental on the list_for_each() change. Signed-off-by: Domen Puncer Signed-off-by: Maximilian Attems Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/devices.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c index 15d773fc539d..195e365675af 100644 --- a/drivers/usb/core/devices.c +++ b/drivers/usb/core/devices.c @@ -569,7 +569,6 @@ static ssize_t usb_device_dump(char __user **buffer, size_t *nbytes, loff_t *ski static ssize_t usb_device_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos) { - struct list_head *buslist; struct usb_bus *bus; ssize_t ret, total_written = 0; loff_t skip_bytes = *ppos; @@ -581,12 +580,9 @@ static ssize_t usb_device_read(struct file *file, char __user *buf, size_t nbyte if (!access_ok(VERIFY_WRITE, buf, nbytes)) return -EFAULT; - /* enumerate busses */ down (&usb_bus_list_lock); - list_for_each(buslist, &usb_bus_list) { - /* print devices for this bus */ - bus = list_entry(buslist, struct usb_bus, bus_list); - + /* print devices for all busses */ + list_for_each_entry(bus, &usb_bus_list, bus_list) { /* recurse through all children of the root hub */ if (!bus->root_hub) continue; -- cgit v1.2.3 From 094be4f389c0b5f9f619ba51933a1b6e7dc41424 Mon Sep 17 00:00:00 2001 From: Maximilian Attems Date: Sun, 5 Sep 2004 09:21:11 +0200 Subject: [PATCH] list_for_each_entry: drivers-usb-serial-ipaq.c Use list_for_each_entry_safe to make code more readable. Compile tested. Signed-off-by: Domen Puncer Signed-off-by: Maximilian Attems Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/ipaq.c | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/serial/ipaq.c b/drivers/usb/serial/ipaq.c index 8fbce2732c51..00cb88dd67b6 100644 --- a/drivers/usb/serial/ipaq.c +++ b/drivers/usb/serial/ipaq.c @@ -419,9 +419,8 @@ static void ipaq_write_gather(struct usb_serial_port *port) struct ipaq_private *priv = usb_get_serial_port_data(port); struct usb_serial *serial = port->serial; int count, room; - struct ipaq_packet *pkt; + struct ipaq_packet *pkt, *tmp; struct urb *urb = port->write_urb; - struct list_head *tmp; if (urb->status == -EINPROGRESS) { /* Should never happen */ @@ -429,9 +428,7 @@ static void ipaq_write_gather(struct usb_serial_port *port) return; } room = URBDATA_SIZE; - for (tmp = priv->queue.next; tmp != &priv->queue;) { - pkt = list_entry(tmp, struct ipaq_packet, list); - tmp = tmp->next; + list_for_each_entry_safe(pkt, tmp, &priv->queue, list) { count = min(room, (int)(pkt->len - pkt->written)); memcpy(urb->transfer_buffer + (URBDATA_SIZE - room), pkt->data + pkt->written, count); @@ -503,22 +500,16 @@ static int ipaq_chars_in_buffer(struct usb_serial_port *port) static void ipaq_destroy_lists(struct usb_serial_port *port) { struct ipaq_private *priv = usb_get_serial_port_data(port); - struct list_head *tmp; - struct ipaq_packet *pkt; + struct ipaq_packet *pkt, *tmp; - for (tmp = priv->queue.next; tmp != &priv->queue;) { - pkt = list_entry(tmp, struct ipaq_packet, list); - tmp = tmp->next; + list_for_each_entry_safe(pkt, tmp, &priv->queue, list) { kfree(pkt->data); kfree(pkt); } - for (tmp = priv->freelist.next; tmp != &priv->freelist;) { - pkt = list_entry(tmp, struct ipaq_packet, list); - tmp = tmp->next; + list_for_each_entry_safe(pkt, tmp, &priv->freelist, list) { kfree(pkt->data); kfree(pkt); } - return; } -- cgit v1.2.3 From c0759750faffec450f8768c399632d62bb4b9c54 Mon Sep 17 00:00:00 2001 From: Maximilian Attems Date: Sun, 5 Sep 2004 09:21:45 +0200 Subject: [PATCH] list_for_each_entry: drivers-usb-host-hc_sl811.c Make code more readable with list_for_each_entry_safe. (Is this a non i386? I can't compile it.) Signed-off-by: Domen Puncer Signed-off-by: Maximilian Attems Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/hc_sl811.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/hc_sl811.c b/drivers/usb/host/hc_sl811.c index b57f1fe8258d..baf0e8086352 100644 --- a/drivers/usb/host/hc_sl811.c +++ b/drivers/usb/host/hc_sl811.c @@ -1343,15 +1343,11 @@ static int __init hci_hcd_init (void) *****************************************************************/ static void __exit hci_hcd_cleanup (void) { - struct list_head *hci_l; - hci_t *hci; + hci_t *hci, *tmp; DBGFUNC ("Enter hci_hcd_cleanup\n"); - for (hci_l = hci_hcd_list.next; hci_l != &hci_hcd_list;) { - hci = list_entry (hci_l, hci_t, hci_hcd_list); - hci_l = hci_l->next; + list_for_each_entry_safe(hci, tmp, &hci_hcd_list, hci_hcd_list) hc_release_hci (hci); - } } module_init (hci_hcd_init); -- cgit v1.2.3 From a8422ce56102c2e0a96a2ffe3e9485a389e548ce Mon Sep 17 00:00:00 2001 From: Maximilian Attems Date: Sun, 5 Sep 2004 09:22:18 +0200 Subject: [PATCH] list_for_each_entry: drivers-usb-media-dabusb.c Use list_for_each_entry to make code more readable. Compile tested. Signed-off-by: Domen Puncer Signed-off-by: Maximilian Attems Signed-off-by: Greg Kroah-Hartman --- drivers/usb/media/dabusb.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/media/dabusb.c b/drivers/usb/media/dabusb.c index 4cc6e57dfa30..0aae6ecc7ad6 100644 --- a/drivers/usb/media/dabusb.c +++ b/drivers/usb/media/dabusb.c @@ -109,16 +109,13 @@ static void dump_urb (struct urb *urb) static int dabusb_cancel_queue (pdabusb_t s, struct list_head *q) { unsigned long flags; - struct list_head *p; pbuff_t b; dbg("dabusb_cancel_queue"); spin_lock_irqsave (&s->lock, flags); - for (p = q->next; p != q; p = p->next) { - b = list_entry (p, buff_t, buff_list); - + list_for_each_entry(b, q, buff_list) { #ifdef DEBUG dump_urb(b->purb); #endif -- cgit v1.2.3 From ba3b76470a73294cded6b93b0bff89884ed90ea5 Mon Sep 17 00:00:00 2001 From: Maximilian Attems Date: Sun, 5 Sep 2004 09:22:50 +0200 Subject: [PATCH] list_for_each_entry: drivers-usb-class-usb-midi.c Make code more readable with list_for_each_entry. Compile tested. Signed-off-by: Domen Puncer Signed-off-by: Maximilian Attems Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/usb-midi.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/class/usb-midi.c b/drivers/usb/class/usb-midi.c index a9dc047a04a7..c4192ea52269 100644 --- a/drivers/usb/class/usb-midi.c +++ b/drivers/usb/class/usb-midi.c @@ -805,7 +805,6 @@ static int usb_midi_open(struct inode *inode, struct file *file) { int minor = iminor(inode); DECLARE_WAITQUEUE(wait, current); - struct list_head *devs, *mdevs; struct usb_midi_state *s; struct usb_mididev *m; unsigned long flags; @@ -817,10 +816,8 @@ static int usb_midi_open(struct inode *inode, struct file *file) for(;;) { down(&open_sem); - list_for_each(devs, &mididevs) { - s = list_entry(devs, struct usb_midi_state, mididev); - list_for_each(mdevs, &s->midiDevList) { - m = list_entry(mdevs, struct usb_mididev, list); + list_for_each_entry(s, &mididevs, mididev) { + list_for_each_entry(m, &s->midiDevList, list) { if ( !((m->dev_midi ^ minor) & ~0xf) ) goto device_found; } @@ -1994,7 +1991,6 @@ static int usb_midi_probe(struct usb_interface *intf, static void usb_midi_disconnect(struct usb_interface *intf) { struct usb_midi_state *s = usb_get_intfdata (intf); - struct list_head *list; struct usb_mididev *m; if ( !s ) @@ -2012,8 +2008,7 @@ static void usb_midi_disconnect(struct usb_interface *intf) s->usbdev = NULL; usb_set_intfdata (intf, NULL); - list_for_each(list, &s->midiDevList) { - m = list_entry(list, struct usb_mididev, list); + list_for_each_entry(m, &s->midiDevList, list) { wake_up(&(m->min.ep->wait)); wake_up(&(m->mout.ep->wait)); if ( m->dev_midi >= 0 ) { -- cgit v1.2.3 From 1c94b8328631a97a1a93039ae95e2bc634c20327 Mon Sep 17 00:00:00 2001 From: Maximilian Attems Date: Sun, 5 Sep 2004 09:23:24 +0200 Subject: [PATCH] list_for_each_entry: drivers-usb-class-audio.c Make code more readable with list_for_each_entry. Compile tested. Signed-off-by: Domen Puncer Signed-off-by: Maximilian Attems Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/audio.c | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/class/audio.c b/drivers/usb/class/audio.c index 17a6dd74713f..36b3f3663e62 100644 --- a/drivers/usb/class/audio.c +++ b/drivers/usb/class/audio.c @@ -1949,15 +1949,12 @@ static inline int prog_dmabuf_out(struct usb_audiodev *as) static int usb_audio_open_mixdev(struct inode *inode, struct file *file) { unsigned int minor = iminor(inode); - struct list_head *devs, *mdevs; struct usb_mixerdev *ms; struct usb_audio_state *s; down(&open_sem); - list_for_each(devs, &audiodevs) { - s = list_entry(devs, struct usb_audio_state, audiodev); - list_for_each(mdevs, &s->mixerlist) { - ms = list_entry(mdevs, struct usb_mixerdev, list); + list_for_each_entry(s, &audiodevs, audiodev) { + list_for_each_entry(ms, &s->mixerlist, list) { if (ms->dev_mixer == minor) goto mixer_found; } @@ -2634,16 +2631,13 @@ static int usb_audio_open(struct inode *inode, struct file *file) { unsigned int minor = iminor(inode); DECLARE_WAITQUEUE(wait, current); - struct list_head *devs, *adevs; struct usb_audiodev *as; struct usb_audio_state *s; for (;;) { down(&open_sem); - list_for_each(devs, &audiodevs) { - s = list_entry(devs, struct usb_audio_state, audiodev); - list_for_each(adevs, &s->audiolist) { - as = list_entry(adevs, struct usb_audiodev, list); + list_for_each_entry(s, &audiodevs, audiodev) { + list_for_each_entry(as, &s->audiolist, list) { if (!((as->dev_audio ^ minor) & ~0xf)) goto device_found; } @@ -3809,7 +3803,6 @@ static int usb_audio_probe(struct usb_interface *intf, static void usb_audio_disconnect(struct usb_interface *intf) { struct usb_audio_state *s = usb_get_intfdata (intf); - struct list_head *list; struct usb_audiodev *as; struct usb_mixerdev *ms; @@ -3831,8 +3824,7 @@ static void usb_audio_disconnect(struct usb_interface *intf) usb_set_intfdata (intf, NULL); /* deregister all audio and mixer devices, so no new processes can open this device */ - list_for_each(list, &s->audiolist) { - as = list_entry(list, struct usb_audiodev, list); + list_for_each_entry(as, &s->audiolist, list) { usbin_disc(as); usbout_disc(as); wake_up(&as->usbin.dma.wait); @@ -3843,8 +3835,7 @@ static void usb_audio_disconnect(struct usb_interface *intf) } as->dev_audio = -1; } - list_for_each(list, &s->mixerlist) { - ms = list_entry(list, struct usb_mixerdev, list); + list_for_each_entry(ms, &s->mixerlist, list) { if (ms->dev_mixer >= 0) { unregister_sound_mixer(ms->dev_mixer); printk(KERN_INFO "usbaudio: unregister mixer 14,%d\n", ms->dev_mixer); -- cgit v1.2.3 From 7a5baeee17b33ebd029aae01f8374f4798e37bb0 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Sun, 5 Sep 2004 09:53:15 +0200 Subject: [PATCH] USB: Centralize logical disconnects in the hub driver This patch takes some code that was only used in one place in the hub driver, and packages it up into a subroutine which is now called from several places. The routine does a logical disconnect -- for example, after issuing a port reset, if the device descriptors have changed (because of a firmware update perhaps) we logically disconnect the old device structure and create a new one. Or if a resume fails for any reason we can do the same thing. This touches some of David Brownell's suspend/resume code, obviously. (It also fixes a couple of small errors in there.) He has said it looks okay. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hub.c | 59 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 36 insertions(+), 23 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 7f72ee96c7fe..0ea93676b6e8 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -1384,7 +1384,6 @@ static int hub_port_disable(struct usb_device *hdev, int port) int ret; if (hdev->children[port]) { - /* FIXME need disconnect() for NOTATTACHED device */ usb_set_device_state(hdev->children[port], USB_STATE_NOTATTACHED); } @@ -1396,6 +1395,30 @@ static int hub_port_disable(struct usb_device *hdev, int port) return ret; } +/* + * Disable a port and mark a logical connnect-change event, so that some + * time later khubd will disconnect() any existing usb_device on the port + * and will re-enumerate if there actually is a device attached. + */ +static void hub_port_logical_disconnect(struct usb_device *hdev, int port) +{ + struct usb_hub *hub; + + dev_dbg(hubdev(hdev), "logical disconnect on port %d\n", port + 1); + hub_port_disable(hdev, port); + + hub = usb_get_intfdata(hdev->actconfig->interface[0]); + set_bit(port, hub->change_bits); + + spin_lock_irq(&hub_event_lock); + if (list_empty(&hub->event_list)) { + list_add_tail(&hub->event_list, &hub_event_list); + wake_up(&khubd_wait); + } + spin_unlock_irq(&hub_event_lock); +} + + #ifdef CONFIG_USB_SUSPEND /* @@ -1729,7 +1752,7 @@ hub_port_resume(struct usb_device *hdev, int port) } } if (status < 0) - status = hub_port_disable(hdev, port); + hub_port_logical_disconnect(hdev, port - 1); return status; } @@ -1869,11 +1892,11 @@ static int hub_resume(struct usb_interface *intf) status = hub_port_resume(hdev, port + 1); else { status = finish_port_resume(udev); - if (status < 0) - status = hub_port_disable(hdev, port); - if (status < 0) + if (status < 0) { dev_dbg(&intf->dev, "resume port %d --> %d\n", - port, status); + port + 1, status); + hub_port_logical_disconnect(hdev, port); + } } up(&udev->serialize); } @@ -2502,15 +2525,17 @@ static void hub_events(void) if (portchange & USB_PORT_STAT_C_SUSPEND) { clear_port_feature(hdev, i + 1, USB_PORT_FEAT_C_SUSPEND); - if (hdev->children[i]) + if (hdev->children[i]) { ret = remote_wakeup(hdev->children[i]); - else + if (ret < 0) + connect_change = 1; + } else { ret = -ENODEV; + hub_port_disable(hdev, i); + } dev_dbg (hub_dev, "resume on port %d, status %d\n", i + 1, ret); - if (ret < 0) - ret = hub_port_disable(hdev, i); } if (portchange & USB_PORT_STAT_C_OVERCURRENT) { @@ -2713,7 +2738,6 @@ int __usb_reset_device(struct usb_device *udev) struct usb_device *parent = udev->parent; struct usb_device_descriptor descriptor = udev->descriptor; int i, ret, port = -1; - struct usb_hub *hub; if (udev->state == USB_STATE_NOTATTACHED || udev->state == USB_STATE_SUSPENDED) { @@ -2794,18 +2818,7 @@ int __usb_reset_device(struct usb_device *udev) return 0; re_enumerate: - hub_port_disable(parent, port); - - hub = usb_get_intfdata(parent->actconfig->interface[0]); - set_bit(port, hub->change_bits); - - spin_lock_irq(&hub_event_lock); - if (list_empty(&hub->event_list)) { - list_add_tail(&hub->event_list, &hub_event_list); - wake_up(&khubd_wait); - } - spin_unlock_irq(&hub_event_lock); - + hub_port_logical_disconnect(parent, port); return -ENODEV; } EXPORT_SYMBOL(__usb_reset_device); -- cgit v1.2.3 From 8164fc747f79016db37c0f7bbe8fa98639f2c4f4 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 7 Sep 2004 19:44:25 -0700 Subject: [PATCH] USB: Internal port numbers start at 0 This patch changes a couple of new routines in the suspend/resume code. Internally they use port numbers starting from 1, unlike every other routine in the hub driver. This changes the port numbers to origin-0, for consistency's sake. Of course, messages sent to the system log will continue to start counting from 1. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hub.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 0ea93676b6e8..8c9f120ea9aa 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -1436,8 +1436,8 @@ static int hub_port_suspend(struct usb_device *hdev, int port) int status; struct usb_device *udev; - udev = hdev->children[port - 1]; - // dev_dbg(hubdev(hdev), "suspend port %d\n", port); + udev = hdev->children[port]; + // dev_dbg(hubdev(hdev), "suspend port %d\n", port + 1); /* enable remote wakeup when appropriate; this lets the device * wake up the upstream hub (including maybe the root hub). @@ -1462,11 +1462,11 @@ static int hub_port_suspend(struct usb_device *hdev, int port) } /* see 7.1.7.6 */ - status = set_port_feature(hdev, port, USB_PORT_FEAT_SUSPEND); + status = set_port_feature(hdev, port + 1, USB_PORT_FEAT_SUSPEND); if (status) { dev_dbg(hubdev(hdev), "can't suspend port %d, status %d\n", - port, status); + port + 1, status); /* paranoia: "should not happen" */ (void) usb_control_msg(udev, usb_sndctrlpipe(udev, 0), USB_REQ_CLEAR_FEATURE, USB_RECIP_DEVICE, @@ -1585,7 +1585,7 @@ static int __usb_suspend_device (struct usb_device *udev, int port, u32 state) else status = -EOPNOTSUPP; } else - status = hub_port_suspend(udev->parent, port + 1); + status = hub_port_suspend(udev->parent, port); if (status == 0) udev->dev.power.power_state = state; @@ -1710,15 +1710,15 @@ hub_port_resume(struct usb_device *hdev, int port) int status; struct usb_device *udev; - udev = hdev->children[port - 1]; - // dev_dbg(hubdev(hdev), "resume port %d\n", port); + udev = hdev->children[port]; + // dev_dbg(hubdev(hdev), "resume port %d\n", port + 1); /* see 7.1.7.7; affects power usage, but not budgeting */ - status = clear_port_feature(hdev, port, USB_PORT_FEAT_SUSPEND); + status = clear_port_feature(hdev, port + 1, USB_PORT_FEAT_SUSPEND); if (status) { dev_dbg(&hdev->actconfig->interface[0]->dev, "can't resume port %d, status %d\n", - port, status); + port + 1, status); } else { u16 devstatus; u16 portchange; @@ -1736,7 +1736,7 @@ hub_port_resume(struct usb_device *hdev, int port) * sequence. */ devstatus = portchange = 0; - status = hub_port_status(hdev, port - 1, + status = hub_port_status(hdev, port, &devstatus, &portchange); if (status < 0 || (devstatus & LIVE_FLAGS) != LIVE_FLAGS @@ -1744,7 +1744,7 @@ hub_port_resume(struct usb_device *hdev, int port) ) { dev_dbg(&hdev->actconfig->interface[0]->dev, "port %d status %04x.%04x after resume, %d\n", - port, portchange, devstatus, status); + port + 1, portchange, devstatus, status); } else { /* TRSMRCY = 10 msec */ msleep(10); @@ -1752,7 +1752,7 @@ hub_port_resume(struct usb_device *hdev, int port) } } if (status < 0) - hub_port_logical_disconnect(hdev, port - 1); + hub_port_logical_disconnect(hdev, port); return status; } @@ -1796,7 +1796,7 @@ int usb_resume_device(struct usb_device *udev) ->actconfig->interface[0]); } } else if (udev->state == USB_STATE_SUSPENDED) { - status = hub_port_resume(udev->parent, port + 1); + status = hub_port_resume(udev->parent, port); } else { status = 0; udev->dev.power.power_state = PM_SUSPEND_ON; @@ -1889,7 +1889,7 @@ static int hub_resume(struct usb_interface *intf) continue; down (&udev->serialize); if (portstat & USB_PORT_STAT_SUSPEND) - status = hub_port_resume(hdev, port + 1); + status = hub_port_resume(hdev, port); else { status = finish_port_resume(udev); if (status < 0) { -- cgit v1.2.3 From d78de54e39931e71c94f452f2b729f90e7243da7 Mon Sep 17 00:00:00 2001 From: Luca Risolia Date: Tue, 7 Sep 2004 21:05:50 -0700 Subject: [PATCH] USB: SN9C10x driver update Changes: - Add support for SN9C103 based devices. The audio capability is already supported but not released in this version. I will release it once someone donates two SN9C103 based devices. - Implement VIDIOC_G_CTRL for TAS5110C1B and TAS51130D1B - Replace "SN9C10[12]" strings with "SN9C10x" - Add red, green, blue gain controls to the SN9C103 - Memory offsets are now page-aligned - Fix typos in the documentation - Documentation updates - Setting bounds are checked by the core module - Add exposure control for TAS51130D1B Signed-off-by: Luca Risolia Signed-off-by: Greg Kroah-Hartman --- CREDITS | 2 +- Documentation/usb/sn9c102.txt | 100 +++++++++++++++++--------- MAINTAINERS | 2 +- drivers/usb/media/Kconfig | 6 +- drivers/usb/media/sn9c102.h | 15 ++-- drivers/usb/media/sn9c102_core.c | 124 ++++++++++++++++++++++++++------- drivers/usb/media/sn9c102_pas106b.c | 14 ++-- drivers/usb/media/sn9c102_pas202bcb.c | 12 ++-- drivers/usb/media/sn9c102_sensor.h | 58 ++++++++++----- drivers/usb/media/sn9c102_tas5110c1b.c | 45 +++++++++--- drivers/usb/media/sn9c102_tas5130d1b.c | 60 +++++++++++++--- 11 files changed, 317 insertions(+), 121 deletions(-) (limited to 'drivers') diff --git a/CREDITS b/CREDITS index 08f699ba8b2b..e6c482a46393 100644 --- a/CREDITS +++ b/CREDITS @@ -2718,7 +2718,7 @@ N: Luca Risolia E: luca.risolia@studio.unibo.it P: 1024D/FCE635A4 88E8 F32F 7244 68BA 3958 5D40 99DA 5D2A FCE6 35A4 D: V4L driver for W996[87]CF JPEG USB Dual Mode Camera Chips -D: V4L2 driver for SN9C10[12] PC Camera Controllers +D: V4L2 driver for SN9C10x PC Camera Controllers S: Via Liberta' 41/A S: Osio Sotto, 24046, Bergamo S: Italy diff --git a/Documentation/usb/sn9c102.txt b/Documentation/usb/sn9c102.txt index 3999069b94bc..ea4f4274b371 100644 --- a/Documentation/usb/sn9c102.txt +++ b/Documentation/usb/sn9c102.txt @@ -1,7 +1,7 @@ - SN9C10[12] PC Camera Controllers + SN9C10x PC Camera Controllers Driver for Linux - ================================ + ============================= - Documentation - @@ -49,22 +49,23 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 3. Overview =========== -This driver attempts to support the video streaming capabilities of the devices -mounting the SONiX SN9C101 or SONiX SN9C102 PC Camera Controllers. +This driver attempts to support the video and audio streaming capabilities of +the devices mounting the SONiX SN9C101, SN9C102 and SN9C103 (or SUI-102) PC +Camera Controllers. - It's worth to note that SONiX has never collaborated with me during the -development of this project, despite of several requests for enough detailed +development of this project, despite several requests for enough detailed specifications of the register tables, compression engine and video data format of the above chips - Up to 64 cameras can be handled at the same time. They can be connected and disconnected from the host many times without turning off the computer, if -your system supports the hotplug facility. +your system supports hotplugging. The driver relies on the Video4Linux2 and USB core modules. It has been designed to run properly on SMP systems as well. -The latest version of the SN9C10[12] driver can be found at the following URL: +The latest version of the SN9C10x driver can be found at the following URL: http://go.lamarinapunto.com/ @@ -150,17 +151,20 @@ Default: 2 7. Optional device control through "sysfs" ========================================== -It is possible to read and write both the SN9C10[12] and the image sensor +It is possible to read and write both the SN9C10x and the image sensor registers by using the "sysfs" filesystem interface. -Every time a supported device is recognized, a read-only file named "green" is +Every time a supported device is recognized, a write-only file named "green" is created in the /sys/class/video4linux/videoX directory. You can set the green channel's gain by writing the desired value to it. The value may range from 0 -to 15. +to 15 for SN9C101 or SN9C102 bridges, from 0 to 127 for SN9C103 bridges. +Similarly, only for SN9C103 controllers, blue and red gain control files are +available in the same directory, for which accepted values may range from 0 to +127. There are other four entries in the directory above for each registered camera: "reg", "val", "i2c_reg" and "i2c_val". The first two files control the -SN9C10[12] bridge, while the other two control the sensor chip. "reg" and +SN9C10x bridge, while the other two control the sensor chip. "reg" and "i2c_reg" hold the values of the current register index where the following reading/writing operations are addressed at through "val" and "i2c_val". Their use is not intended for end-users, unless you know what you are doing. Note @@ -169,19 +173,19 @@ support the standard I2C protocol. Also, remember that you must be logged in as root before writing to them. As an example, suppose we were to want to read the value contained in the -register number 1 of the sensor register table - which usually is the product +register number 1 of the sensor register table - which is usually the product identifier - of the camera registered as "/dev/video0": [root@localhost #] cd /sys/class/video4linux/video0 [root@localhost #] echo 1 > i2c_reg [root@localhost #] cat i2c_val -Now let's set the green gain's register of the SN9C10[12] chip to 2: +Now let's set the green gain's register of the SN9C10x chip to 2: [root@localhost #] echo 0x11 > reg [root@localhost #] echo 2 > val -Note that the SN9C10[12] always returns 0 when some of its registers are read. +Note that the SN9C10x always returns 0 when some of its registers are read. To avoid race conditions, all the I/O accesses to the files are serialized. @@ -192,25 +196,52 @@ here. They have never collaborated with me, so no advertising - From the point of view of a driver, what unambiguously identify a device are its vendor and product USB identifiers. Below is a list of known identifiers of -devices mounting the SN9C10[12] PC camera controllers: +devices mounting the SN9C10x PC camera controllers: Vendor ID Product ID --------- ---------- -0xc45 0x6001 -0xc45 0x6005 -0xc45 0x6009 -0xc45 0x600d -0xc45 0x6024 -0xc45 0x6025 -0xc45 0x6028 -0xc45 0x6029 -0xc45 0x602a -0xc45 0x602c -0xc45 0x6030 +0x0c45 0x6001 +0x0c45 0x6005 +0x0c45 0x6009 +0x0c45 0x600d +0x0c45 0x6024 +0x0c45 0x6025 +0x0c45 0x6028 +0x0c45 0x6029 +0x0c45 0x602a +0x0c45 0x602b +0x0c45 0x602c +0x0c45 0x6030 +0x0c45 0x6080 +0x0c45 0x6082 +0x0c45 0x6083 +0x0c45 0x6088 +0x0c45 0x608a +0x0c45 0x608b +0x0c45 0x608c +0x0c45 0x608e +0x0c45 0x608f +0x0c45 0x60a0 +0x0c45 0x60a2 +0x0c45 0x60a3 +0x0c45 0x60a8 +0x0c45 0x60aa +0x0c45 0x60ab +0x0c45 0x60ac +0x0c45 0x60ae +0x0c45 0x60af +0x0c45 0x60b0 +0x0c45 0x60b2 +0x0c45 0x60b3 +0x0c45 0x60b8 +0x0c45 0x60ba +0x0c45 0x60bb +0x0c45 0x60bc +0x0c45 0x60be The list above does NOT imply that all those devices work with this driver: up -until now only the ones that mount the following image sensors are supported. -Kernel messages will always tell you whether this is the case: +until now only the ones that mount the following image sensors are supported; +kernel messages will always tell you whether this is the case: Model Manufacturer ----- ------------ @@ -224,7 +255,7 @@ listed in the above table, you may try to add the specific USB VendorID and ProductID identifiers to the sn9c102_id_table[] in the file "sn9c102_sensor.h"; then compile, load the module again and look at the kernel output. If this works, please send an email to me reporting the kernel messages, so -that I will add a new entry in the list of supported devices. +that I can add a new entry in the list of supported devices. Donations of new models for further testing and support would be much appreciated. I won't add official support for hardware that I don't actually @@ -238,8 +269,8 @@ have created for this purpose, which is present in "sn9c102_sensor.h" (documentation is included there). As an example, have a look at the code in "sn9c102_pas106b.c", which uses the mentioned interface. -At the moment, not yet supported image sensors are: HV7131[D|E1] (VGA), -MI03 (VGA), OV7620 (VGA). +At the moment, possible unsupported image sensors are: HV7131x series (VGA), +MI03x series (VGA), OV7620 (VGA), OV7630 (VGA), CIS-VF10 (VGA). 10. Notes for V4L2 application developers @@ -254,12 +285,13 @@ device to switch to the other I/O method; - previously mapped buffer memory must always be unmapped before calling any of the "VIDIOC_S_CROP", "VIDIOC_TRY_FMT" and "VIDIOC_S_FMT" ioctl's. The same number of buffers as before will be allocated again to match the size of the -new video frames, so you have to map them again before any I/O attempts. +new video frames, so you have to map the buffers again before any I/O attempts +on them. Consistently with the hardware limits, this driver also supports image downscaling with arbitrary scaling factors from 1, 2 and 4 in both directions. -However the V4L2 API specifications don't correctly define how the scaling -factor can be choosen arbitrarily by the "negotiation" of the "source" and +However, the V4L2 API specifications don't correctly define how the scaling +factor can be chosen arbitrarily by the "negotiation" of the "source" and "target" rectangles. To work around this flaw, we have added the convention that, during the negotiation, whenever the "VIDIOC_S_CROP" ioctl is issued, the scaling factor is restored to 1. diff --git a/MAINTAINERS b/MAINTAINERS index 629e0a82aafd..09b549d488e1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2346,7 +2346,7 @@ L: linux-usb-devel@lists.sourceforge.net W: http://www.connecttech.com S: Supported -USB SN9C10[12] DRIVER +USB SN9C10x DRIVER P: Luca Risolia M: luca.risolia@studio.unibo.it L: linux-usb-devel@lists.sourceforge.net diff --git a/drivers/usb/media/Kconfig b/drivers/usb/media/Kconfig index 4ac7ac4f3b1d..678e5fcbe29a 100644 --- a/drivers/usb/media/Kconfig +++ b/drivers/usb/media/Kconfig @@ -123,11 +123,11 @@ config USB_SE401 module will be called se401. config USB_SN9C102 - tristate "USB SN9C10[12] PC Camera Controller support" + tristate "USB SN9C10x PC Camera Controller support" depends on USB && VIDEO_DEV ---help--- - Say Y here if you want support for cameras based on SONiX SN9C101 - or SN9C102 PC Camera Controllers. + Say Y here if you want support for cameras based on SONiX SN9C101, + SN9C102 or SN9C103 PC Camera Controllers. See for more informations. diff --git a/drivers/usb/media/sn9c102.h b/drivers/usb/media/sn9c102.h index 62ff2144392c..050a25d6f6aa 100644 --- a/drivers/usb/media/sn9c102.h +++ b/drivers/usb/media/sn9c102.h @@ -1,5 +1,5 @@ /*************************************************************************** - * V4L2 driver for SN9C10[12] PC Camera Controllers * + * V4L2 driver for SN9C10x PC Camera Controllers * * * * Copyright (C) 2004 by Luca Risolia * * * @@ -49,12 +49,18 @@ /*****************************************************************************/ -#define SN9C102_MODULE_NAME "V4L2 driver for SN9C10[12] PC Camera Controllers" +#define SN9C102_MODULE_NAME "V4L2 driver for SN9C10x PC Camera Controllers" #define SN9C102_MODULE_AUTHOR "(C) 2004 Luca Risolia" #define SN9C102_AUTHOR_EMAIL "" #define SN9C102_MODULE_LICENSE "GPL" -#define SN9C102_MODULE_VERSION "1:1.08" -#define SN9C102_MODULE_VERSION_CODE KERNEL_VERSION(1, 0, 8) +#define SN9C102_MODULE_VERSION "1:1.10" +#define SN9C102_MODULE_VERSION_CODE KERNEL_VERSION(1, 0, 10) + +enum sn9c102_bridge { + BRIDGE_SN9C101 = 0x01, + BRIDGE_SN9C102 = 0x02, + BRIDGE_SN9C103 = 0x04, +}; SN9C102_ID_TABLE; SN9C102_SENSOR_TABLE; @@ -105,6 +111,7 @@ struct sn9c102_device { struct video_device* v4ldev; + enum sn9c102_bridge bridge; struct sn9c102_sensor* sensor; struct usb_device* usbdev; diff --git a/drivers/usb/media/sn9c102_core.c b/drivers/usb/media/sn9c102_core.c index 9f775f74002c..3881ae5ea08f 100644 --- a/drivers/usb/media/sn9c102_core.c +++ b/drivers/usb/media/sn9c102_core.c @@ -1,5 +1,5 @@ /*************************************************************************** - * V4L2 driver for SN9C10[12] PC Camera Controllers * + * V4L2 driver for SN9C10x PC Camera Controllers * * * * Copyright (C) 2004 by Luca Risolia * * * @@ -169,15 +169,15 @@ static u32 sn9c102_request_buffers(struct sn9c102_device* cam, u32 count) cam->nbuffers = count; while (cam->nbuffers > 0) { - if ((buff = rvmalloc(cam->nbuffers * imagesize))) + if ((buff = rvmalloc(cam->nbuffers * PAGE_ALIGN(imagesize)))) break; cam->nbuffers--; } for (i = 0; i < cam->nbuffers; i++) { - cam->frame[i].bufmem = buff + i*imagesize; + cam->frame[i].bufmem = buff + i*PAGE_ALIGN(imagesize); cam->frame[i].buf.index = i; - cam->frame[i].buf.m.offset = i*imagesize; + cam->frame[i].buf.m.offset = i*PAGE_ALIGN(imagesize); cam->frame[i].buf.length = imagesize; cam->frame[i].buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; cam->frame[i].buf.sequence = 0; @@ -388,7 +388,7 @@ sn9c102_i2c_try_raw_write(struct sn9c102_device* cam, data[4] = data3; data[5] = data4; data[6] = data5; - data[7] = 0x10; + data[7] = 0x14; res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, 0x41, 0x08, 0, data, 8, SN9C102_CTRL_TIMEOUT); if (res < 0) @@ -1022,15 +1022,79 @@ sn9c102_store_i2c_val(struct class_device* cd, const char* buf, size_t len) static ssize_t sn9c102_store_green(struct class_device* cd, const char* buf, size_t len) { + struct sn9c102_device* cam; + enum sn9c102_bridge bridge; ssize_t res = 0; u8 value; ssize_t count; + if (down_interruptible(&sn9c102_sysfs_lock)) + return -ERESTARTSYS; + + cam = video_get_drvdata(to_video_device(cd)); + if (!cam) { + up(&sn9c102_sysfs_lock); + return -ENODEV; + } + + bridge = cam->bridge; + + up(&sn9c102_sysfs_lock); + value = sn9c102_strtou8(buf, len, &count); - if (!count || value > 0x0f) + if (!count) return -EINVAL; - if ((res = sn9c102_store_reg(cd, "0x11", 4)) >= 0) + switch (bridge) { + case BRIDGE_SN9C101: + case BRIDGE_SN9C102: + if (value > 0x0f) + return -EINVAL; + if ((res = sn9c102_store_reg(cd, "0x11", 4)) >= 0) + res = sn9c102_store_val(cd, buf, len); + break; + case BRIDGE_SN9C103: + if (value > 0x7f) + return -EINVAL; + if ((res = sn9c102_store_reg(cd, "0x04", 4)) >= 0) + res = sn9c102_store_val(cd, buf, len); + break; + } + + return res; +} + + +static ssize_t +sn9c102_store_blue(struct class_device* cd, const char* buf, size_t len) +{ + ssize_t res = 0; + u8 value; + ssize_t count; + + value = sn9c102_strtou8(buf, len, &count); + if (!count || value > 0x7f) + return -EINVAL; + + if ((res = sn9c102_store_reg(cd, "0x06", 4)) >= 0) + res = sn9c102_store_val(cd, buf, len); + + return res; +} + + +static ssize_t +sn9c102_store_red(struct class_device* cd, const char* buf, size_t len) +{ + ssize_t res = 0; + u8 value; + ssize_t count; + + value = sn9c102_strtou8(buf, len, &count); + if (!count || value > 0x7f) + return -EINVAL; + + if ((res = sn9c102_store_reg(cd, "0x05", 4)) >= 0) res = sn9c102_store_val(cd, buf, len); return res; @@ -1046,6 +1110,8 @@ static CLASS_DEVICE_ATTR(i2c_reg, S_IRUGO | S_IWUSR, static CLASS_DEVICE_ATTR(i2c_val, S_IRUGO | S_IWUSR, sn9c102_show_i2c_val, sn9c102_store_i2c_val); static CLASS_DEVICE_ATTR(green, S_IWUGO, NULL, sn9c102_store_green); +static CLASS_DEVICE_ATTR(blue, S_IWUGO, NULL, sn9c102_store_blue); +static CLASS_DEVICE_ATTR(red, S_IWUGO, NULL, sn9c102_store_red); static void sn9c102_create_sysfs(struct sn9c102_device* cam) @@ -1054,7 +1120,12 @@ static void sn9c102_create_sysfs(struct sn9c102_device* cam) video_device_create_file(v4ldev, &class_device_attr_reg); video_device_create_file(v4ldev, &class_device_attr_val); - video_device_create_file(v4ldev, &class_device_attr_green); + if (cam->bridge == BRIDGE_SN9C101 || cam->bridge == BRIDGE_SN9C102) + video_device_create_file(v4ldev, &class_device_attr_green); + else if (cam->bridge == BRIDGE_SN9C103) { + video_device_create_file(v4ldev, &class_device_attr_blue); + video_device_create_file(v4ldev, &class_device_attr_red); + } if (cam->sensor->slave_write_id && cam->sensor->slave_read_id) { video_device_create_file(v4ldev, &class_device_attr_i2c_reg); video_device_create_file(v4ldev, &class_device_attr_i2c_val); @@ -1092,21 +1163,13 @@ static int sn9c102_set_crop(struct sn9c102_device* cam, struct v4l2_rect* rect) u8 h_start = (u8)(rect->left - s->cropcap.bounds.left), v_start = (u8)(rect->top - s->cropcap.bounds.top), h_size = (u8)(rect->width / 16), - v_size = (u8)(rect->height / 16), - ae_strx = 0x00, - ae_stry = 0x00, - ae_endx = h_size / 2, - ae_endy = v_size / 2; + v_size = (u8)(rect->height / 16); int err = 0; err += sn9c102_write_reg(cam, h_start, 0x12); err += sn9c102_write_reg(cam, v_start, 0x13); err += sn9c102_write_reg(cam, h_size, 0x15); err += sn9c102_write_reg(cam, v_size, 0x16); - err += sn9c102_write_reg(cam, ae_strx, 0x1c); - err += sn9c102_write_reg(cam, ae_stry, 0x1d); - err += sn9c102_write_reg(cam, ae_endx, 0x1e); - err += sn9c102_write_reg(cam, ae_endy, 0x1f); if (err) return -EIO; @@ -1636,16 +1699,21 @@ static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp, if (copy_from_user(&ctrl, arg, sizeof(ctrl))) return -EFAULT; - if ((err = s->set_ctrl(cam, &ctrl))) - return err; - n = sizeof(s->qctrl) / sizeof(s->qctrl[0]); for (i = 0; i < n; i++) if (ctrl.id == s->qctrl[i].id) { - s->_qctrl[i].default_value = ctrl.value; + if (ctrl.value < s->qctrl[i].minimum || + ctrl.value > s->qctrl[i].maximum) + return -ERANGE; + ctrl.value -= ctrl.value % s->qctrl[i].step; break; } + if ((err = s->set_ctrl(cam, &ctrl))) + return err; + + s->_qctrl[i].default_value = ctrl.value; + return 0; } @@ -1776,7 +1844,7 @@ static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp, DBG(1, "VIDIOC_S_CROP failed because of hardware " "problems. To use the camera, close and open " "/dev/video%d again.", cam->v4ldev->minor) - return err; + return -EIO; } s->pix_format.width = rect->width/scale; @@ -1951,7 +2019,7 @@ static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp, DBG(1, "VIDIOC_S_FMT failed because of hardware " "problems. To use the camera, close and open " "/dev/video%d again.", cam->v4ldev->minor) - return err; + return -EIO; } memcpy(pfmt, pix, sizeof(*pix)); @@ -2286,15 +2354,17 @@ sn9c102_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) r = sn9c102_read_reg(cam, 0x00); if (r < 0 || r != 0x10) { - DBG(1, "Sorry, this is not a SN9C10[12] based camera " + DBG(1, "Sorry, this is not a SN9C10x based camera " "(vid/pid 0x%04X/0x%04X)", sn9c102_id_table[i].idVendor,sn9c102_id_table[i].idProduct) err = -ENODEV; goto fail; } - DBG(2, "SN9C10[12] PC Camera Controller detected " - "(vid/pid 0x%04X/0x%04X)", + cam->bridge = (sn9c102_id_table[i].idProduct & 0xffc0) == 0x6080 ? + BRIDGE_SN9C102 : BRIDGE_SN9C103; + + DBG(2, "SN9C10x PC Camera Controller detected (vid/pid 0x%04X/0x%04X)", sn9c102_id_table[i].idVendor, sn9c102_id_table[i].idProduct) for (i = 0; sn9c102_sensor_table[i]; i++) { @@ -2318,7 +2388,7 @@ sn9c102_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) cam->state |= DEV_MISCONFIGURED; } - strcpy(cam->v4ldev->name, "SN9C10[12] PC Camera"); + strcpy(cam->v4ldev->name, "SN9C10x PC Camera"); cam->v4ldev->owner = THIS_MODULE; cam->v4ldev->type = VID_TYPE_CAPTURE | VID_TYPE_SCALES; cam->v4ldev->hardware = VID_HARDWARE_SN9C102; diff --git a/drivers/usb/media/sn9c102_pas106b.c b/drivers/usb/media/sn9c102_pas106b.c index a302a4845893..8453409ea125 100644 --- a/drivers/usb/media/sn9c102_pas106b.c +++ b/drivers/usb/media/sn9c102_pas106b.c @@ -1,5 +1,5 @@ /*************************************************************************** - * Driver for PAS106B image sensor connected to the SN9C10[12] PC Camera * + * Driver for PAS106B image sensor connected to the SN9C10x PC Camera * * Controllers * * * * Copyright (C) 2004 by Luca Risolia * @@ -100,26 +100,26 @@ static int pas106b_set_ctrl(struct sn9c102_device* cam, switch (ctrl->id) { case V4L2_CID_RED_BALANCE: - err += sn9c102_i2c_write(cam, 0x0c, ctrl->value & 0x1f); + err += sn9c102_i2c_write(cam, 0x0c, ctrl->value); break; case V4L2_CID_BLUE_BALANCE: - err += sn9c102_i2c_write(cam, 0x09, ctrl->value & 0x1f); + err += sn9c102_i2c_write(cam, 0x09, ctrl->value); break; case V4L2_CID_GAIN: - err += sn9c102_i2c_write(cam, 0x0e, ctrl->value & 0x1f); + err += sn9c102_i2c_write(cam, 0x0e, ctrl->value); break; case V4L2_CID_BRIGHTNESS: - err += sn9c102_i2c_write(cam, 0x0d, 0x1f-(ctrl->value & 0x1f)); + err += sn9c102_i2c_write(cam, 0x0d, 0x1f - ctrl->value); break; case V4L2_CID_CONTRAST: - err += sn9c102_i2c_write(cam, 0x0f, ctrl->value & 0x03); + err += sn9c102_i2c_write(cam, 0x0f, ctrl->value); break; default: return -EINVAL; } err += sn9c102_i2c_write(cam, 0x13, 0x01); - return err; + return err ? -EIO : 0; } diff --git a/drivers/usb/media/sn9c102_pas202bcb.c b/drivers/usb/media/sn9c102_pas202bcb.c index 26944eaf85d0..ffd12ed14e34 100644 --- a/drivers/usb/media/sn9c102_pas202bcb.c +++ b/drivers/usb/media/sn9c102_pas202bcb.c @@ -1,5 +1,5 @@ /*************************************************************************** - * Driver for PAS202BCB image sensor connected to the SN9C10[12] PC Camera * + * Driver for PAS202BCB image sensor connected to the SN9C10x PC Camera * * Controllers * * * * Copyright (C) 2004 by Carlos Eduardo Medaglia Dyonisio * @@ -95,23 +95,23 @@ static int pas202bcb_set_ctrl(struct sn9c102_device* cam, switch (ctrl->id) { case V4L2_CID_RED_BALANCE: - err += sn9c102_i2c_write(cam, 0x09, ctrl->value & 0x0f); + err += sn9c102_i2c_write(cam, 0x09, ctrl->value); break; case V4L2_CID_BLUE_BALANCE: - err += sn9c102_i2c_write(cam, 0x07, ctrl->value & 0x0f); + err += sn9c102_i2c_write(cam, 0x07, ctrl->value); break; case V4L2_CID_GAIN: - err += sn9c102_i2c_write(cam, 0x10, ctrl->value & 0x1f); + err += sn9c102_i2c_write(cam, 0x10, ctrl->value); break; case V4L2_CID_BRIGHTNESS: - err += sn9c102_i2c_write(cam, 0x06, 0x0f-(ctrl->value & 0x0f)); + err += sn9c102_i2c_write(cam, 0x06, 0x0f - ctrl->value); break; default: return -EINVAL; } err += sn9c102_i2c_write(cam, 0x11, 0x01); - return err; + return err ? -EIO : 0; } diff --git a/drivers/usb/media/sn9c102_sensor.h b/drivers/usb/media/sn9c102_sensor.h index 3e7e4a2578bc..9632a5a63ccc 100644 --- a/drivers/usb/media/sn9c102_sensor.h +++ b/drivers/usb/media/sn9c102_sensor.h @@ -1,5 +1,5 @@ /*************************************************************************** - * API for image sensors connected to the SN9C10[12] PC Camera Controllers * + * API for image sensors connected to the SN9C10x PC Camera Controllers * * * * Copyright (C) 2004 by Luca Risolia * * * @@ -89,17 +89,44 @@ sn9c102_attach_sensor(struct sn9c102_device* cam, /* Each SN9C10X camera has proper PID/VID identifiers. Add them here in case.*/ #define SN9C102_ID_TABLE \ static const struct usb_device_id sn9c102_id_table[] = { \ - { USB_DEVICE(0xc45, 0x6001), }, /* TAS5110C1B */ \ - { USB_DEVICE(0xc45, 0x6005), }, /* TAS5110C1B */ \ - { USB_DEVICE(0xc45, 0x6009), }, /* PAS106B */ \ - { USB_DEVICE(0xc45, 0x600d), }, /* PAS106B */ \ - { USB_DEVICE(0xc45, 0x6024), }, \ - { USB_DEVICE(0xc45, 0x6025), }, /* TAS5130D1B and TAS5110C1B */ \ - { USB_DEVICE(0xc45, 0x6028), }, /* PAS202BCB */ \ - { USB_DEVICE(0xc45, 0x6029), }, /* PAS106B */ \ - { USB_DEVICE(0xc45, 0x602a), }, /* HV7131[D|E1] */ \ - { USB_DEVICE(0xc45, 0x602c), }, /* OV7620 */ \ - { USB_DEVICE(0xc45, 0x6030), }, /* MI03 */ \ + { USB_DEVICE(0x0c45, 0x6001), }, /* TAS5110C1B */ \ + { USB_DEVICE(0x0c45, 0x6005), }, /* TAS5110C1B */ \ + { USB_DEVICE(0x0c45, 0x6009), }, /* PAS106B */ \ + { USB_DEVICE(0x0c45, 0x600d), }, /* PAS106B */ \ + { USB_DEVICE(0x0c45, 0x6024), }, \ + { USB_DEVICE(0x0c45, 0x6025), }, /* TAS5130D1B and TAS5110C1B */ \ + { USB_DEVICE(0x0c45, 0x6028), }, /* PAS202BCB */ \ + { USB_DEVICE(0x0c45, 0x6029), }, /* PAS106B */ \ + { USB_DEVICE(0x0c45, 0x602a), }, /* HV7131[D|E1] */ \ + { USB_DEVICE(0x0c45, 0x602b), }, \ + { USB_DEVICE(0x0c45, 0x602c), }, /* OV7620 */ \ + { USB_DEVICE(0x0c45, 0x6030), }, /* MI03x */ \ + { USB_DEVICE(0x0c45, 0x6080), }, \ + { USB_DEVICE(0x0c45, 0x6082), }, /* MI0343 and MI0360 */ \ + { USB_DEVICE(0x0c45, 0x6083), }, /* HV7131[D|E1] */ \ + { USB_DEVICE(0x0c45, 0x6088), }, \ + { USB_DEVICE(0x0c45, 0x608a), }, \ + { USB_DEVICE(0x0c45, 0x608b), }, \ + { USB_DEVICE(0x0c45, 0x608c), }, /* HV7131x */ \ + { USB_DEVICE(0x0c45, 0x608e), }, /* CIS-VF10 */ \ + { USB_DEVICE(0x0c45, 0x608f), }, /* OV7630 */ \ + { USB_DEVICE(0x0c45, 0x60a0), }, \ + { USB_DEVICE(0x0c45, 0x60a2), }, \ + { USB_DEVICE(0x0c45, 0x60a3), }, \ + { USB_DEVICE(0x0c45, 0x60a8), }, /* PAS106B */ \ + { USB_DEVICE(0x0c45, 0x60aa), }, /* TAS5130D1B */ \ + { USB_DEVICE(0x0c45, 0x60ab), }, /* TAS5110C1B */ \ + { USB_DEVICE(0x0c45, 0x60ac), }, \ + { USB_DEVICE(0x0c45, 0x60ae), }, \ + { USB_DEVICE(0x0c45, 0x60af), }, /* PAS202BCB */ \ + { USB_DEVICE(0x0c45, 0x60b0), }, \ + { USB_DEVICE(0x0c45, 0x60b2), }, \ + { USB_DEVICE(0x0c45, 0x60b3), }, \ + { USB_DEVICE(0x0c45, 0x60b8), }, \ + { USB_DEVICE(0x0c45, 0x60ba), }, \ + { USB_DEVICE(0x0c45, 0x60bb), }, \ + { USB_DEVICE(0x0c45, 0x60bc), }, \ + { USB_DEVICE(0x0c45, 0x60be), }, \ { } \ }; @@ -173,9 +200,7 @@ struct sn9c102_sensor { /* These identifiers must be provided if the image sensor implements - the standard I2C protocol. TASC sensors don't, although they have a - serial interface: so this is a case where the "raw" I2C version - could be helpful. + the standard I2C protocol. */ u8 slave_read_id, slave_write_id; /* reg. 0x09 */ @@ -214,7 +239,8 @@ struct sn9c102_sensor { the list above. The returned value must follow the V4L2 specifications for the VIDIOC_G|C_CTRL ioctls. V4L2_CID_H|VCENTER are not supported by this driver, so do not implement them. Also, - passed values are NOT checked to see if they are out of bounds. + you don't have to check whether the passed values are out of bounds, + given that this is done by the core module. */ struct v4l2_cropcap cropcap; diff --git a/drivers/usb/media/sn9c102_tas5110c1b.c b/drivers/usb/media/sn9c102_tas5110c1b.c index 68e1b2e0ce18..006a6b569f4a 100644 --- a/drivers/usb/media/sn9c102_tas5110c1b.c +++ b/drivers/usb/media/sn9c102_tas5110c1b.c @@ -1,6 +1,6 @@ /*************************************************************************** - * Driver for TAS5110C1B image sensor connected to the SN9C10[12] PC * - * Camera Controllers * + * Driver for TAS5110C1B image sensor connected to the SN9C10x PC Camera * + * Controllers * * * * Copyright (C) 2004 by Luca Risolia * * * @@ -24,6 +24,8 @@ static struct sn9c102_sensor tas5110c1b; +static struct v4l2_control tas5110c1b_gain; + static int tas5110c1b_init(struct sn9c102_device* cam) { @@ -38,25 +40,42 @@ static int tas5110c1b_init(struct sn9c102_device* cam) err += sn9c102_write_reg(cam, 0x06, 0x18); err += sn9c102_write_reg(cam, 0xfb, 0x19); - err += sn9c102_i2c_try_raw_write(cam, &tas5110c1b, 4, 0x11, 0x00, 0xc0, - 0x80, 0, 0); + err += sn9c102_i2c_write(cam, 0xc0, 0x80); return err; } +static int tas5110c1b_get_ctrl(struct sn9c102_device* cam, + struct v4l2_control* ctrl) +{ + switch (ctrl->id) { + case V4L2_CID_GAIN: + ctrl->value = tas5110c1b_gain.value; + break; + default: + return -EINVAL; + } + + return 0; +} + + static int tas5110c1b_set_ctrl(struct sn9c102_device* cam, const struct v4l2_control* ctrl) { + int err = 0; + switch (ctrl->id) { case V4L2_CID_GAIN: - return sn9c102_i2c_try_raw_write(cam, &tas5110c1b, 4, 0x11, - 0x02, 0x20, - 0xff - (ctrl->value & 0xff), - 0, 0); + if (!(err += sn9c102_i2c_write(cam, 0x20, 0xf6 - ctrl->value))) + tas5110c1b_gain.value = ctrl->value; + break; default: return -EINVAL; } + + return err ? -EIO : 0; } @@ -85,6 +104,8 @@ static struct sn9c102_sensor tas5110c1b = { .maintainer = "Luca Risolia ", .frequency = SN9C102_I2C_100KHZ, .interface = SN9C102_I2C_3WIRES, + .slave_read_id = 0xff, /* fictitious */ + .slave_write_id = 0xff, /* fictitious */ .init = &tas5110c1b_init, .qctrl = { { @@ -92,9 +113,9 @@ static struct sn9c102_sensor tas5110c1b = { .type = V4L2_CTRL_TYPE_INTEGER, .name = "global gain", .minimum = 0x00, - .maximum = 0xff, + .maximum = 0xf6, .step = 0x01, - .default_value = 0x48, + .default_value = 0x40, .flags = 0, }, }, @@ -113,6 +134,7 @@ static struct sn9c102_sensor tas5110c1b = { .height = 288, }, }, + .get_ctrl = &tas5110c1b_get_ctrl, .set_crop = &tas5110c1b_set_crop, .pix_format = { .width = 352, @@ -130,7 +152,8 @@ int sn9c102_probe_tas5110c1b(struct sn9c102_device* cam) /* At the moment, sensor detection is based on USB pid/vid */ if (tas5110c1b.usbdev->descriptor.idProduct != 0x6001 && - tas5110c1b.usbdev->descriptor.idProduct != 0x6005) + tas5110c1b.usbdev->descriptor.idProduct != 0x6005 && + tas5110c1b.usbdev->descriptor.idProduct != 0x60ab) return -ENODEV; return 0; diff --git a/drivers/usb/media/sn9c102_tas5130d1b.c b/drivers/usb/media/sn9c102_tas5130d1b.c index 0bab19435399..7af73acd2c37 100644 --- a/drivers/usb/media/sn9c102_tas5130d1b.c +++ b/drivers/usb/media/sn9c102_tas5130d1b.c @@ -1,6 +1,6 @@ /*************************************************************************** - * Driver for TAS5130D1B image sensor connected to the SN9C10[12] PC * - * Camera Controllers * + * Driver for TAS5130D1B image sensor connected to the SN9C10x PC Camera * + * Controllers * * * * Copyright (C) 2004 by Luca Risolia * * * @@ -24,6 +24,8 @@ static struct sn9c102_sensor tas5130d1b; +static struct v4l2_control tas5130d1b_gain, tas5130d1b_exposure; + static int tas5130d1b_init(struct sn9c102_device* cam) { @@ -38,25 +40,47 @@ static int tas5130d1b_init(struct sn9c102_device* cam) err += sn9c102_write_reg(cam, 0x60, 0x17); err += sn9c102_write_reg(cam, 0x07, 0x18); - err += sn9c102_i2c_try_raw_write(cam, &tas5130d1b, 4, 0x11, 0x00, 0x40, - 0x47, 0, 0); - return err; } +static int tas5130d1b_get_ctrl(struct sn9c102_device* cam, + struct v4l2_control* ctrl) +{ + switch (ctrl->id) { + case V4L2_CID_GAIN: + ctrl->value = tas5130d1b_gain.value; + break; + case V4L2_CID_EXPOSURE: + ctrl->value = tas5130d1b_exposure.value; + break; + default: + return -EINVAL; + } + + return 0; +} + + static int tas5130d1b_set_ctrl(struct sn9c102_device* cam, const struct v4l2_control* ctrl) { + int err = 0; + switch (ctrl->id) { case V4L2_CID_GAIN: - return sn9c102_i2c_try_raw_write(cam, &tas5130d1b, 4, 0x11, - 0x02, 0x20, - 0xff - (ctrl->value & 0xff), - 0, 0); + if (!(err += sn9c102_i2c_write(cam, 0x20, 0xf6 - ctrl->value))) + tas5130d1b_gain.value = ctrl->value; + break; + case V4L2_CID_EXPOSURE: + if (!(err += sn9c102_i2c_write(cam, 0x40, 0x47 - ctrl->value))) + tas5130d1b_exposure.value = ctrl->value; + break; default: return -EINVAL; } + + return err ? -EIO : 0; } @@ -85,6 +109,8 @@ static struct sn9c102_sensor tas5130d1b = { .maintainer = "Luca Risolia ", .frequency = SN9C102_I2C_100KHZ, .interface = SN9C102_I2C_3WIRES, + .slave_read_id = 0xff, /* fictitious */ + .slave_write_id = 0xff, /* fictitious */ .init = &tas5130d1b_init, .qctrl = { { @@ -92,12 +118,23 @@ static struct sn9c102_sensor tas5130d1b = { .type = V4L2_CTRL_TYPE_INTEGER, .name = "global gain", .minimum = 0x00, - .maximum = 0xff, + .maximum = 0xf6, + .step = 0x02, + .default_value = 0x00, + .flags = 0, + }, + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "exposure", + .minimum = 0x00, + .maximum = 0x47, .step = 0x01, .default_value = 0x00, .flags = 0, }, }, + .get_ctrl = &tas5130d1b_get_ctrl, .set_ctrl = &tas5130d1b_set_ctrl, .cropcap = { .bounds = { @@ -129,7 +166,8 @@ int sn9c102_probe_tas5130d1b(struct sn9c102_device* cam) sn9c102_attach_sensor(cam, &tas5130d1b); /* At the moment, sensor detection is based on USB pid/vid */ - if (tas5130d1b.usbdev->descriptor.idProduct != 0x6025) + if (tas5130d1b.usbdev->descriptor.idProduct != 0x6025 && + tas5130d1b.usbdev->descriptor.idProduct != 0x60aa) return -ENODEV; return 0; -- cgit v1.2.3 From 1321d5f1a3d6d7618df0c933024b6ccc1f7c5be6 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Tue, 7 Sep 2004 21:08:27 -0700 Subject: [PATCH] USB: EHCI SMP fix This addresses an SMP-only issue with the EHCI driver, where only one CPU should scan the schedule at a time (scanning is not re-entrant) but either the IRQ handler or a watchdog timer could end up starting it. Many thanks to Olaf Hering for isolating the failure mode, and testing this fix! Once once CPU starts scanning, any other might as well finish right away. This fix just adds a flag to detect that case. Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-hcd.c | 9 +++++++++ drivers/usb/host/ehci.h | 1 + 2 files changed, 10 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 554af349d5e2..2f204b864a90 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -695,9 +695,18 @@ static void ehci_work (struct ehci_hcd *ehci, struct pt_regs *regs) timer_action_done (ehci, TIMER_IO_WATCHDOG); if (ehci->reclaim_ready) end_unlink_async (ehci, regs); + + /* another CPU may drop ehci->lock during a schedule scan while + * it reports urb completions. this flag guards against bogus + * attempts at re-entrant schedule scanning. + */ + if (ehci->scanning) + return; + ehci->scanning = 1; scan_async (ehci, regs); if (ehci->next_uframe != -1) scan_periodic (ehci, regs); + ehci->scanning = 0; /* the IO watchdog guards against hardware or driver bugs that * misplace IRQs, and should let us run completely without IRQs. diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 14e001d98f57..de666f6b3314 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -53,6 +53,7 @@ struct ehci_hcd { /* one per controller */ struct ehci_qh *async; struct ehci_qh *reclaim; unsigned reclaim_ready : 1; + unsigned scanning : 1; /* periodic schedule support */ #define DEFAULT_I_TDPS 1024 /* some HCs can do less */ -- cgit v1.2.3 From 3a18409b10d6759e80715b690c9da8d8e7c0c7fc Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Tue, 7 Sep 2004 21:18:37 -0700 Subject: [PATCH] USB: Codemercs IO-Warrior support From: Steffen Zieger Here is a patch to get the kernel module from Codemerces to work. The module is available in source for the 2.4 and 2.6 series except the needed changes in hid-core.c. Codemercs distribute the needed changes as a complete file (version 2.6.4). This isn't working with 2.6.8.1. http://www.codemercs.com Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- drivers/usb/input/hid-core.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/input/hid-core.c b/drivers/usb/input/hid-core.c index 1b94a27f2eb4..34b0e91aeb1b 100644 --- a/drivers/usb/input/hid-core.c +++ b/drivers/usb/input/hid-core.c @@ -1439,6 +1439,12 @@ void hid_init_reports(struct hid_device *hid) #define USB_DEVICE_ID_1_PHIDGETSERVO_20 0x8101 #define USB_DEVICE_ID_4_PHIDGETSERVO_20 0x8104 +#define USB_VENDOR_ID_CODEMERCS 0x07c0 +#define USB_DEVICE_ID_CODEMERCS_IOW40 0x1500 +#define USB_DEVICE_ID_CODEMERCS_IOW24 0x1501 +#define USB_DEVICE_ID_CODEMERCS_IOW48 0x1502 +#define USB_DEVICE_ID_CODEMERCS_IOW28 0x1503 + static struct hid_blacklist { __u16 idVendor; __u16 idProduct; @@ -1521,6 +1527,11 @@ static struct hid_blacklist { { USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RUMBLEPAD, HID_QUIRK_BADPAD }, { USB_VENDOR_ID_TOPMAX, USB_DEVICE_ID_TOPMAX_COBRAPAD, HID_QUIRK_BADPAD }, + { USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW40, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW24, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW48, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW28, HID_QUIRK_IGNORE }, + { 0, 0 } }; -- cgit v1.2.3 From e9bd0a314ab6182cef28768edc6abffaafe3dc57 Mon Sep 17 00:00:00 2001 From: Catalin Boie Date: Tue, 7 Sep 2004 21:22:56 -0700 Subject: [PATCH] USB Serial: Correct a use of out of range variable Bug found by Coverity: http://linuxbugs.coverity.com/external/editbugparent.php?viewbugid=2137&checkers%5B%5D=all&status%5B%5D=BUG&status%5B%5D=UNINSPECTED&status%5B%5D=UNKNOWN&status%5B%5D=DON%27T%20CARE&status%5B%5D=PENDING&product%5B%5D=all&component%5B%5D=all&file=&fn=&sortby=reverse_rank&before=&after=&curpage=2&bugid=-1&comment=&reason= Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/usb-serial.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 0e59c134134c..fd6bb67c69c1 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -388,7 +388,7 @@ static struct usb_serial *get_free_serial (struct usb_serial *serial, int num_po good_spot = 1; for (j = 1; j <= num_ports-1; ++j) - if ((serial_table[i+j]) || (i+j >= SERIAL_TTY_MINORS)) { + if ((i+j >= SERIAL_TTY_MINORS) || (serial_table[i+j])) { good_spot = 0; i += j; break; -- cgit v1.2.3 From 3aaad15ad14b56feec341f809d82c19fef59b302 Mon Sep 17 00:00:00 2001 From: Matthew Dharm Date: Tue, 7 Sep 2004 21:23:33 -0700 Subject: [PATCH] USB Storage: change how INQUIRY is fixed up The usb-storage driver 'fixes up' the INQUIRY data returned by a device so that it reads SCSI rev 2 by intercepting the data in-flight. This was done to make various SCSI drivers (sd, sr, etc.) work with the device. However, this technique also has the unfortunate side-effect that nobody can see the real rev. -- not even sg users. This patch changes that. Now, the SCSI revision is changed in the slave_configure() function. Thus, the 'real' data is available to anyone who wants to issue an INQUIRY directly via any means. This also simplifies the code somewhat. Signed-off-by: Matthew Dharm Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/isd200.c | 6 ------ drivers/usb/storage/protocol.c | 49 ------------------------------------------ drivers/usb/storage/scsiglue.c | 17 +++++++++++++++ 3 files changed, 17 insertions(+), 55 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/storage/isd200.c b/drivers/usb/storage/isd200.c index a6aa4a918d1e..b6db57596e96 100644 --- a/drivers/usb/storage/isd200.c +++ b/drivers/usb/storage/isd200.c @@ -1052,12 +1052,6 @@ static int isd200_get_inquiry_data( struct us_data *us ) /* Standard IDE interface only supports disks */ info->InquiryData.DeviceType = DIRECT_ACCESS_DEVICE; - /* Fix-up the return data from an INQUIRY command to show - * ANSI SCSI rev 2 so we don't confuse the SCSI layers above us - * in Linux. - */ - info->InquiryData.Versions = 0x2; - /* The length must be at least 36 (5 + 31) */ info->InquiryData.AdditionalLength = 0x1F; diff --git a/drivers/usb/storage/protocol.c b/drivers/usb/storage/protocol.c index 5141c508e3a1..2836c1735d9e 100644 --- a/drivers/usb/storage/protocol.c +++ b/drivers/usb/storage/protocol.c @@ -57,38 +57,6 @@ * Helper routines ***********************************************************************/ -/* - * Fix-up the return data from an INQUIRY command to show - * ANSI SCSI rev 2 so we don't confuse the SCSI layers above us - */ -static void fix_inquiry_data(struct scsi_cmnd *srb) -{ - unsigned char databuf[3]; - unsigned int index, offset; - - /* verify that it's an INQUIRY command */ - if (srb->cmnd[0] != INQUIRY) - return; - - index = offset = 0; - if (usb_stor_access_xfer_buf(databuf, sizeof(databuf), srb, - &index, &offset, FROM_XFER_BUF) != sizeof(databuf)) - return; - - if ((databuf[2] & 7) == 2) - return; - - US_DEBUGP("Fixing INQUIRY data to show SCSI rev 2 - was %d\n", - databuf[2] & 7); - - /* Change the SCSI revision number */ - databuf[2] = (databuf[2] & ~7) | 2; - - index = offset = 0; - usb_stor_access_xfer_buf(databuf, sizeof(databuf), srb, - &index, &offset, TO_XFER_BUF); -} - /* * Fix-up the return data from a READ CAPACITY command. My Feiya reader * returns a value that is 1 too large. @@ -137,10 +105,6 @@ void usb_stor_qic157_command(struct scsi_cmnd *srb, struct us_data *us) /* send the command to the transport layer */ usb_stor_invoke_transport(srb, us); - if (srb->result == SAM_STAT_GOOD) { - /* fix the INQUIRY data if necessary */ - fix_inquiry_data(srb); - } } void usb_stor_ATAPI_command(struct scsi_cmnd *srb, struct us_data *us) @@ -160,11 +124,6 @@ void usb_stor_ATAPI_command(struct scsi_cmnd *srb, struct us_data *us) /* send the command to the transport layer */ usb_stor_invoke_transport(srb, us); - - if (srb->result == SAM_STAT_GOOD) { - /* fix the INQUIRY data if necessary */ - fix_inquiry_data(srb); - } } @@ -208,11 +167,6 @@ void usb_stor_ufi_command(struct scsi_cmnd *srb, struct us_data *us) /* send the command to the transport layer */ usb_stor_invoke_transport(srb, us); - - if (srb->result == SAM_STAT_GOOD) { - /* Fix the data for an INQUIRY, if necessary */ - fix_inquiry_data(srb); - } } void usb_stor_transparent_scsi_command(struct scsi_cmnd *srb, @@ -222,9 +176,6 @@ void usb_stor_transparent_scsi_command(struct scsi_cmnd *srb, usb_stor_invoke_transport(srb, us); if (srb->result == SAM_STAT_GOOD) { - /* Fix the INQUIRY data if necessary */ - fix_inquiry_data(srb); - /* Fix the READ CAPACITY result if necessary */ if (us->flags & US_FL_FIX_CAPACITY) fix_read_capacity(srb); diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c index c18cd06ea31c..29f4e59759e6 100644 --- a/drivers/usb/storage/scsiglue.c +++ b/drivers/usb/storage/scsiglue.c @@ -98,6 +98,23 @@ static int slave_configure(struct scsi_device *sdev) * the end, scatter-gather buffers follow page boundaries. */ blk_queue_dma_alignment(sdev->request_queue, (512 - 1)); + /* Set the SCSI level to at least 2. We'll leave it at 3 if that's + * what is originally reported. We need this to avoid confusing + * the SCSI layer with devices that report 0 or 1, but need 10-byte + * commands (ala ATAPI devices behind certain bridges, or devices + * which simply have broken INQUIRY data). + * + * NOTE: This means /dev/sg programs (ala cdrecord) will get the + * actual information. This seems to be the preference for + * programs like that. + * + * NOTE: This also means that /proc/scsi/scsi and sysfs may report + * the actual value or the modified one, depending on where the + * data comes from. + */ + if (sdev->scsi_level < SCSI_2) + sdev->scsi_level = SCSI_2; + /* According to the technical support people at Genesys Logic, * devices using their chips have problems transferring more than * 32 KB at a time. In practice people have found that 64 KB -- cgit v1.2.3 From 0aa13ba4244b20daa66c2c9a32c195a166e9619c Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 8 Sep 2004 19:49:54 -0700 Subject: USB: make usb_unlink_urb() message only show up if CONFIG_DEBUG_KERNEL is enabled. Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/urb.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c index 376e0e939d00..f57de4dca2bf 100644 --- a/drivers/usb/core/urb.c +++ b/drivers/usb/core/urb.c @@ -451,9 +451,11 @@ int usb_unlink_urb(struct urb *urb) if (!urb) return -EINVAL; if (!(urb->transfer_flags & URB_ASYNC_UNLINK)) { +#ifdef CONFIG_DEBUG_KERNEL printk(KERN_NOTICE "usb_unlink_urb() is deprecated for " - "synchronous unlinks. Use usb_kill_urb()\n"); + "synchronous unlinks. Use usb_kill_urb() instead.\n"); WARN_ON(1); +#endif usb_kill_urb(urb); return 0; } -- cgit v1.2.3 From ecabf237ffd4f70f656096120ce22b1e1b62b323 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 8 Sep 2004 20:03:05 -0700 Subject: [PATCH] USB: Add OTG support to g_file_storage This patch adds minimal USB On-The-Go support (mainly just an extra descriptor) to the File-Storage Gadget. The changes were based on the additions made to the Gadget Zero driver. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/file_storage.c | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c index 6e8008fa43cb..1280a7cfacf4 100644 --- a/drivers/usb/gadget/file_storage.c +++ b/drivers/usb/gadget/file_storage.c @@ -248,7 +248,7 @@ #define DRIVER_DESC "File-backed Storage Gadget" #define DRIVER_NAME "g_file_storage" -#define DRIVER_VERSION "28 July 2004" +#define DRIVER_VERSION "31 August 2004" static const char longname[] = DRIVER_DESC; static const char shortname[] = DRIVER_NAME; @@ -866,6 +866,14 @@ config_desc = { .bMaxPower = 1, // self-powered }; +static struct usb_otg_descriptor +otg_desc = { + .bLength = sizeof(otg_desc), + .bDescriptorType = USB_DT_OTG, + + .bmAttributes = USB_OTG_SRP, +}; + /* There is only one interface. */ static struct usb_interface_descriptor @@ -914,6 +922,7 @@ fs_intr_in_desc = { }; static const struct usb_descriptor_header *fs_function[] = { + (struct usb_descriptor_header *) &otg_desc, (struct usb_descriptor_header *) &intf_desc, (struct usb_descriptor_header *) &fs_bulk_in_desc, (struct usb_descriptor_header *) &fs_bulk_out_desc, @@ -976,6 +985,7 @@ hs_intr_in_desc = { }; static const struct usb_descriptor_header *hs_function[] = { + (struct usb_descriptor_header *) &otg_desc, (struct usb_descriptor_header *) &intf_desc, (struct usb_descriptor_header *) &hs_bulk_in_desc, (struct usb_descriptor_header *) &hs_bulk_out_desc, @@ -1018,9 +1028,10 @@ static struct usb_gadget_strings stringtab = { * and with code managing interfaces and their altsettings. They must * also handle different speeds and other-speed requests. */ -static int populate_config_buf(enum usb_device_speed speed, +static int populate_config_buf(struct usb_gadget *gadget, u8 *buf, u8 type, unsigned index) { + enum usb_device_speed speed = gadget->speed; int len; const struct usb_descriptor_header **function; @@ -1036,6 +1047,10 @@ static int populate_config_buf(enum usb_device_speed speed, #endif function = fs_function; + /* for now, don't advertise srp-only devices */ + if (!gadget->is_otg) + function++; + len = usb_gadget_config_buf(&config_desc, buf, EP0_BUFSIZE, function); ((struct usb_config_descriptor *) buf)->bDescriptorType = type; return len; @@ -1366,7 +1381,7 @@ static int standard_setup_req(struct fsg_dev *fsg, #ifdef CONFIG_USB_GADGET_DUALSPEED get_config: #endif - value = populate_config_buf(fsg->gadget->speed, + value = populate_config_buf(fsg->gadget, req->buf, ctrl->wValue >> 8, ctrl->wValue & 0xff); @@ -3896,6 +3911,11 @@ static int __init fsg_bind(struct usb_gadget *gadget) hs_intr_in_desc.bEndpointAddress = fs_intr_in_desc.bEndpointAddress; #endif + if (gadget->is_otg) { + otg_desc.bmAttributes |= USB_OTG_HNP, + config_desc.bmAttributes |= USB_CONFIG_ATT_WAKEUP; + } + rc = -ENOMEM; /* Allocate the request and buffer for endpoint 0 */ -- cgit v1.2.3 From af084fbb3d2144ef38d7b260de188bac25181610 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 8 Sep 2004 20:03:34 -0700 Subject: [PATCH] USB: New submission procedure for unusual_devs.h As you requested, this patch updates the documented procedure for submitting new unusual_devs.h entries. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/unusual_devs.h | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index 69554115a098..6a25b58af696 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -36,13 +36,16 @@ /* If you edit this file, please try to keep it sorted first by VendorID, * then by ProductID. * - * If you want to add an entry for this file, please send the following - * to greg@kroah.com: - * - patch that adds the entry for your device which includes your - * email address right above the entry. + * If you want to add an entry for this file, be sure to include the + * following information: + * - a patch that adds the entry for your device, including your + * email address right above the entry (plus maybe a brief + * explanation of the reason for the entry), * - a copy of /proc/bus/usb/devices with your device plugged in * running with this patch. - * + * Send your submission to either Phil Dibowitz or + * Alan Stern , and don't forget to CC: the + * USB development list . */ UNUSUAL_DEV( 0x03ee, 0x6901, 0x0000, 0x0100, -- cgit v1.2.3 From 691d62e8f67f0a5ffd582001a5e58cddf0f35aa2 Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Wed, 8 Sep 2004 20:04:05 -0700 Subject: [PATCH] USB: fix up usblp usb_unlink_urb() warning On Thursday 09 September 2004 08:38, Greg KH wrote: > On Thu, Sep 09, 2004 at 01:07:05AM -0400, Gene Heskett wrote: > > Greetings; > > > > I just had to reboot back to -mm2 after playing with some printer configs > > in cups, although the test pages worked, so I'm not sure what this all > > about, from var log/messages: > > > > Sep 8 23:13:42 coyote cups: cupsd -HUP succeeded > > Sep 8 23:13:43 coyote kernel: usb_unlink_urb() is deprecated for > > synchronous unlinks. Use usb_kill_urb() Sep 8 23:13:43 coyote kernel: > > Badness in usb_unlink_urb at drivers/usb/core/urb.c:456 Sep 8 23:13:44 > > coyote kernel: [] dump_stack+0x1e/0x20 Sep 8 23:13:44 coyote > > kernel: [] usb_unlink_urb+0x85/0xa0 Sep 8 23:13:44 coyote > > kernel: [] usblp_unlink_urbs+0x17/0x40 Sep 8 23:13:44 coyote > > kernel: [] usblp_release+0x38/0x60 Sep 8 23:13:44 coyote > > kernel: [] __fput+0x12a/0x140 > > Sep 8 23:13:44 coyote kernel: [] filp_close+0x57/0x80 > > Sep 8 23:13:44 coyote kernel: [] sys_close+0x61/0x90 > > Sep 8 23:13:44 coyote kernel: [] sysenter_past_esp+0x52/0x71 Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/usblp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/class/usblp.c b/drivers/usb/class/usblp.c index dd6950e674f4..3174bf78fefc 100644 --- a/drivers/usb/class/usblp.c +++ b/drivers/usb/class/usblp.c @@ -410,9 +410,9 @@ static void usblp_cleanup (struct usblp *usblp) static void usblp_unlink_urbs(struct usblp *usblp) { - usb_unlink_urb(usblp->writeurb); + usb_kill_urb(usblp->writeurb); if (usblp->bidir) - usb_unlink_urb(usblp->readurb); + usb_kill_urb(usblp->readurb); } static int usblp_release(struct inode *inode, struct file *file) -- cgit v1.2.3 From 03ef9d05be029dee489ba4cd911de5ca4bf8ec95 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 8 Sep 2004 20:04:25 -0700 Subject: [PATCH] USB: Unusual_devs entry for Panasonic cameras Thanks to Tom Hughes for jogging my memory about this patch, which has been sitting here waiting for Greg's return. It adds an unusual_devs.h entry for the Panasonic DMC-LCx line of cameras, which incorrectly report the total number of blocks in response to READ CAPACITY rather than the highest available block number. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/unusual_devs.h | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index 6a25b58af696..9d0d846e19e3 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -173,6 +173,16 @@ UNUSUAL_DEV( 0x04da, 0x0d05, 0x0000, 0x0000, "CD-R/RW Drive", US_SC_8070, US_PR_CB, NULL, 0), +/* Reported by Adriaan Penning + * Note that these cameras report "Medium not present" after + * ALLOW_MEDIUM_REMOVAL, so they also need to be marked + * NOT_LOCKABLE in the SCSI blacklist (and the vendor is MATSHITA). */ +UNUSUAL_DEV( 0x04da, 0x2372, 0x0000, 0x9999, + "Panasonic", + "DMC-LCx Camera", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_FIX_CAPACITY ), + /* Most of the following entries were developed with the help of * Shuttle/SCM directly. */ -- cgit v1.2.3 From d2e1a8d0dd754d1666f680863792aa2cc8d4611e Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 8 Sep 2004 21:29:39 -0700 Subject: USB: fix usb_unlink_urb() usage in pl2303 driver Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/pl2303.c | 21 +++------------------ 1 file changed, 3 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index 416ea72951f6..5be9c370313b 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -621,7 +621,6 @@ static void pl2303_close (struct usb_serial_port *port, struct file *filp) struct pl2303_private *priv = usb_get_serial_port_data(port); unsigned long flags; unsigned int c_cflag; - int result; int bps; long timeout; wait_queue_t wait; \ @@ -666,23 +665,9 @@ static void pl2303_close (struct usb_serial_port *port, struct file *filp) /* shutdown our urbs */ dbg("%s - shutting down urbs", __FUNCTION__); - result = usb_unlink_urb (port->write_urb); - if (result) - dbg("%s - usb_unlink_urb (write_urb)" - " failed with reason: %d", __FUNCTION__, - result); - - result = usb_unlink_urb (port->read_urb); - if (result) - dbg("%s - usb_unlink_urb (read_urb) " - "failed with reason: %d", __FUNCTION__, - result); - - result = usb_unlink_urb (port->interrupt_in_urb); - if (result) - dbg("%s - usb_unlink_urb (interrupt_in_urb)" - " failed with reason: %d", __FUNCTION__, - result); + usb_kill_urb(port->write_urb); + usb_kill_urb(port->read_urb); + usb_kill_urb(port->interrupt_in_urb); if (port->tty) { c_cflag = port->tty->termios->c_cflag; -- cgit v1.2.3 From 48f9435bca1023b7db4b67ce89169e5559b92ad6 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 8 Sep 2004 21:32:18 -0700 Subject: USB: fix usb_unlink_urb() usage in usb-serial core Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/usb-serial.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index fd6bb67c69c1..6b2aa1f6aa86 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -455,15 +455,15 @@ static void destroy_serial(struct kref *kref) if (!port) continue; if (port->read_urb) { - usb_unlink_urb(port->read_urb); + usb_kill_urb(port->read_urb); usb_free_urb(port->read_urb); } if (port->write_urb) { - usb_unlink_urb(port->write_urb); + usb_kill_urb(port->write_urb); usb_free_urb(port->write_urb); } if (port->interrupt_in_urb) { - usb_unlink_urb(port->interrupt_in_urb); + usb_kill_urb(port->interrupt_in_urb); usb_free_urb(port->interrupt_in_urb); } kfree(port->bulk_in_buffer); @@ -819,15 +819,15 @@ static void port_release(struct device *dev) dbg ("%s - %s", __FUNCTION__, dev->bus_id); if (port->read_urb) { - usb_unlink_urb(port->read_urb); + usb_kill_urb(port->read_urb); usb_free_urb(port->read_urb); } if (port->write_urb) { - usb_unlink_urb(port->write_urb); + usb_kill_urb(port->write_urb); usb_free_urb(port->write_urb); } if (port->interrupt_in_urb) { - usb_unlink_urb(port->interrupt_in_urb); + usb_kill_urb(port->interrupt_in_urb); usb_free_urb(port->interrupt_in_urb); } kfree(port->bulk_in_buffer); -- cgit v1.2.3 From 2e7970b7fc623cb948063de694b2d25a16445064 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 8 Sep 2004 21:34:02 -0700 Subject: USB: fix usb_unlink_urb() usage in belkin_sa driver Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/belkin_sa.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/serial/belkin_sa.c b/drivers/usb/serial/belkin_sa.c index bcf56011ec22..a44cb9d96113 100644 --- a/drivers/usb/serial/belkin_sa.c +++ b/drivers/usb/serial/belkin_sa.c @@ -228,7 +228,7 @@ static int belkin_sa_open (struct usb_serial_port *port, struct file *filp) port->interrupt_in_urb->dev = port->serial->dev; retval = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL); if (retval) { - usb_unlink_urb(port->read_urb); + usb_kill_urb(port->read_urb); err(" usb_submit_urb(read int) failed"); } @@ -242,9 +242,9 @@ static void belkin_sa_close (struct usb_serial_port *port, struct file *filp) dbg("%s port %d", __FUNCTION__, port->number); /* shutdown our bulk reads and writes */ - usb_unlink_urb (port->write_urb); - usb_unlink_urb (port->read_urb); - usb_unlink_urb (port->interrupt_in_urb); + usb_kill_urb(port->write_urb); + usb_kill_urb(port->read_urb); + usb_kill_urb(port->interrupt_in_urb); } /* belkin_sa_close */ -- cgit v1.2.3 From c4c3ce8e38324dccb78b8380217690f8dee0796f Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 8 Sep 2004 21:34:35 -0700 Subject: USB: fix usb_unlink_urb() usage in cyberjack driver Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/cyberjack.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/serial/cyberjack.c b/drivers/usb/serial/cyberjack.c index 33613b008557..6b5ec2af32ad 100644 --- a/drivers/usb/serial/cyberjack.c +++ b/drivers/usb/serial/cyberjack.c @@ -149,7 +149,7 @@ static void cyberjack_shutdown (struct usb_serial *serial) dbg("%s", __FUNCTION__); for (i=0; i < serial->num_ports; ++i) { - usb_unlink_urb (serial->port[i]->interrupt_in_urb); + usb_kill_urb(serial->port[i]->interrupt_in_urb); /* My special items, the standard routines free my urbs */ kfree(usb_get_serial_port_data(serial->port[i])); usb_set_serial_port_data(serial->port[i], NULL); @@ -189,8 +189,8 @@ static void cyberjack_close (struct usb_serial_port *port, struct file *filp) if (port->serial->dev) { /* shutdown any bulk reads that might be going on */ - usb_unlink_urb (port->write_urb); - usb_unlink_urb (port->read_urb); + usb_kill_urb(port->write_urb); + usb_kill_urb(port->read_urb); } } -- cgit v1.2.3 From f0afcd40164946b36dc7da0016ed38c142ab2229 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 8 Sep 2004 21:35:24 -0700 Subject: USB: fix usb_unlink_urb() usage in whiteheat driver Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/whiteheat.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c index b27741bf5497..fc20c8324fe7 100644 --- a/drivers/usb/serial/whiteheat.c +++ b/drivers/usb/serial/whiteheat.c @@ -680,7 +680,7 @@ static void whiteheat_close(struct usb_serial_port *port, struct file * filp) list_for_each_safe(tmp, tmp2, &info->rx_urbs_submitted) { wrap = list_entry(tmp, struct whiteheat_urb_wrap, list); urb = wrap->urb; - usb_unlink_urb(urb); + usb_kill_urb(urb); list_del(tmp); list_add(tmp, &info->rx_urbs_free); } @@ -691,7 +691,7 @@ static void whiteheat_close(struct usb_serial_port *port, struct file * filp) list_for_each_safe(tmp, tmp2, &info->tx_urbs_submitted) { wrap = list_entry(tmp, struct whiteheat_urb_wrap, list); urb = wrap->urb; - usb_unlink_urb(urb); + usb_kill_urb(urb); list_del(tmp); list_add(tmp, &info->tx_urbs_free); } @@ -1344,7 +1344,7 @@ static void stop_command_port(struct usb_serial *serial) spin_lock_irqsave(&command_info->lock, flags); command_info->port_running--; if (!command_info->port_running) - usb_unlink_urb(command_port->read_urb); + usb_kill_urb(command_port->read_urb); spin_unlock_irqrestore(&command_info->lock, flags); } @@ -1372,7 +1372,7 @@ static int start_port_read(struct usb_serial_port *port) list_for_each_safe(tmp, tmp2, &info->rx_urbs_submitted) { wrap = list_entry(tmp, struct whiteheat_urb_wrap, list); urb = wrap->urb; - usb_unlink_urb(urb); + usb_kill_urb(urb); list_del(tmp); list_add(tmp, &info->rx_urbs_free); } -- cgit v1.2.3 From 0f694983eb9af685caf003f94e920db85326cf47 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 8 Sep 2004 21:36:08 -0700 Subject: USB: fix usb_unlink_urb() usage in io_edgeport driver Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/io_edgeport.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c index fc3a536f3696..d8d492ef99e8 100644 --- a/drivers/usb/serial/io_edgeport.c +++ b/drivers/usb/serial/io_edgeport.c @@ -1243,7 +1243,7 @@ static void edge_close (struct usb_serial_port *port, struct file * filp) edge_port->openPending = FALSE; if (edge_port->write_urb) { - usb_unlink_urb (edge_port->write_urb); + usb_kill_urb(edge_port->write_urb); } if (edge_port->write_urb) { @@ -2448,8 +2448,8 @@ static int write_cmd_usb (struct edgeport_port *edge_port, unsigned char *buffer if (status) { /* something went wrong */ dbg("%s - usb_submit_urb(write bulk) failed", __FUNCTION__); - usb_unlink_urb (urb); - usb_free_urb (urb); + usb_kill_urb(urb); + usb_free_urb(urb); return status; } -- cgit v1.2.3 From 221b7993fc1bbafe3bd404c2135728a496e6a6a8 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 8 Sep 2004 21:41:32 -0700 Subject: USB: fix usb_unlink_urb() usage in ir-usb driver Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/ir-usb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/serial/ir-usb.c b/drivers/usb/serial/ir-usb.c index cb42cc7c3224..f3a75de21d66 100644 --- a/drivers/usb/serial/ir-usb.c +++ b/drivers/usb/serial/ir-usb.c @@ -322,7 +322,7 @@ static void ir_close (struct usb_serial_port *port, struct file * filp) dbg("%s - port %d", __FUNCTION__, port->number); /* shutdown our bulk read */ - usb_unlink_urb (port->read_urb); + usb_kill_urb(port->read_urb); } static int ir_write (struct usb_serial_port *port, int from_user, const unsigned char *buf, int count) -- cgit v1.2.3 From 7abd81fca985aa5bd7c90c44775005ab47e8cbe9 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 8 Sep 2004 21:42:45 -0700 Subject: USB: fix usb_unlink_urb() usage in ipaq driver Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/ipaq.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/serial/ipaq.c b/drivers/usb/serial/ipaq.c index 00cb88dd67b6..1a748b515112 100644 --- a/drivers/usb/serial/ipaq.c +++ b/drivers/usb/serial/ipaq.c @@ -288,8 +288,8 @@ static void ipaq_close(struct usb_serial_port *port, struct file *filp) /* * shut down bulk read and write */ - usb_unlink_urb(port->write_urb); - usb_unlink_urb(port->read_urb); + usb_kill_urb(port->write_urb); + usb_kill_urb(port->read_urb); ipaq_destroy_lists(port); kfree(priv); usb_set_serial_port_data(port, NULL); -- cgit v1.2.3 From 96f155845c12d7f8aee9b6e34dd1e94f11c0ea4f Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 8 Sep 2004 21:44:04 -0700 Subject: USB: fix usb_unlink_urb() usage in digi_acceleport driver Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/digi_acceleport.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c index 6d01337eefc0..5c4669db9b86 100644 --- a/drivers/usb/serial/digi_acceleport.c +++ b/drivers/usb/serial/digi_acceleport.c @@ -1623,7 +1623,7 @@ dbg( "digi_close: TOP: port=%d, open_count=%d", priv->dp_port_num, port->open_co DIGI_CLOSE_TIMEOUT ); /* shutdown any outstanding bulk writes */ - usb_unlink_urb (port->write_urb); + usb_kill_urb(port->write_urb); } tty->closing = 0; @@ -1762,8 +1762,8 @@ dbg( "digi_shutdown: TOP, in_interrupt()=%ld", in_interrupt() ); /* stop reads and writes on all ports */ for( i=0; itype->num_ports+1; i++ ) { - usb_unlink_urb( serial->port[i]->read_urb ); - usb_unlink_urb( serial->port[i]->write_urb ); + usb_kill_urb(serial->port[i]->read_urb); + usb_kill_urb(serial->port[i]->write_urb); } /* free the private data structures for all ports */ -- cgit v1.2.3 From 9cc3cf0a6eb86359f330ea63e8c45219beb7cacc Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 8 Sep 2004 21:45:53 -0700 Subject: USB: fix usb_unlink_urb() usage in empeg driver Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/empeg.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/serial/empeg.c b/drivers/usb/serial/empeg.c index 972f3b91d41c..edb3559d546a 100644 --- a/drivers/usb/serial/empeg.c +++ b/drivers/usb/serial/empeg.c @@ -185,7 +185,7 @@ static void empeg_close (struct usb_serial_port *port, struct file * filp) dbg("%s - port %d", __FUNCTION__, port->number); /* shutdown our bulk read */ - usb_unlink_urb (port->read_urb); + usb_kill_urb(port->read_urb); /* Uncomment the following line if you want to see some statistics in your syslog */ /* dev_info (&port->dev, "Bytes In = %d Bytes Out = %d\n", bytes_in, bytes_out); */ } @@ -406,7 +406,7 @@ static void empeg_read_bulk_callback (struct urb *urb, struct pt_regs *regs) static void empeg_throttle (struct usb_serial_port *port) { dbg("%s - port %d", __FUNCTION__, port->number); - usb_unlink_urb (port->read_urb); + usb_kill_urb(port->read_urb); } @@ -583,10 +583,10 @@ static void __exit empeg_exit (void) for (i = 0; i < NUM_URBS; ++i) { if (write_urb_pool[i]) { - /* FIXME - uncomment the following usb_unlink_urb call when + /* FIXME - uncomment the following usb_kill_urb call when * the host controllers get fixed to set urb->dev = NULL after * the urb is finished. Otherwise this call oopses. */ - /* usb_unlink_urb(write_urb_pool[i]); */ + /* usb_kill_urb(write_urb_pool[i]); */ if (write_urb_pool[i]->transfer_buffer) kfree(write_urb_pool[i]->transfer_buffer); usb_free_urb (write_urb_pool[i]); -- cgit v1.2.3 From 6af886e661ff4460e4e612ca1bff5e8d670cee34 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 8 Sep 2004 21:47:46 -0700 Subject: USB: fix usb_unlink_urb() usage in mct_u232 driver Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/mct_u232.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c index b4961f0d2509..3ae89d03b883 100644 --- a/drivers/usb/serial/mct_u232.c +++ b/drivers/usb/serial/mct_u232.c @@ -480,9 +480,9 @@ static void mct_u232_close (struct usb_serial_port *port, struct file *filp) if (port->serial->dev) { /* shutdown our urbs */ - usb_unlink_urb (port->write_urb); - usb_unlink_urb (port->read_urb); - usb_unlink_urb (port->interrupt_in_urb); + usb_kill_urb(port->write_urb); + usb_kill_urb(port->read_urb); + usb_kill_urb(port->interrupt_in_urb); } } /* mct_u232_close */ -- cgit v1.2.3 From d3d55268a92ebe297934969db00762a4c3ab1e70 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 8 Sep 2004 21:48:32 -0700 Subject: USB: fix usb_unlink_urb() usage in omninet driver Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/omninet.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/serial/omninet.c b/drivers/usb/serial/omninet.c index 371aa2e8e3f1..7032d5f7c6e1 100644 --- a/drivers/usb/serial/omninet.c +++ b/drivers/usb/serial/omninet.c @@ -183,8 +183,8 @@ static void omninet_close (struct usb_serial_port *port, struct file * filp) dbg("%s - port %d", __FUNCTION__, port->number); wport = serial->port[1]; - usb_unlink_urb(wport->write_urb); - usb_unlink_urb(port->read_urb); + usb_kill_urb(wport->write_urb); + usb_kill_urb(port->read_urb); od = usb_get_serial_port_data(port); if (od) -- cgit v1.2.3 From 2af16876c4e7dac7787163a19b6b44dbd7ce4941 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 8 Sep 2004 21:49:19 -0700 Subject: USB: fix usb_unlink_urb() usage in visor driver Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/visor.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/serial/visor.c b/drivers/usb/serial/visor.c index 8a0a75533e70..0e5d90552789 100644 --- a/drivers/usb/serial/visor.c +++ b/drivers/usb/serial/visor.c @@ -446,9 +446,9 @@ static void visor_close (struct usb_serial_port *port, struct file * filp) dbg("%s - port %d", __FUNCTION__, port->number); /* shutdown our urbs */ - usb_unlink_urb (port->read_urb); + usb_kill_urb(port->read_urb); if (port->interrupt_in_urb) - usb_unlink_urb (port->interrupt_in_urb); + usb_kill_urb(port->interrupt_in_urb); /* Try to send shutdown message, if the device is gone, this will just fail. */ transfer_buffer = kmalloc (0x12, GFP_KERNEL); @@ -655,7 +655,7 @@ exit: static void visor_throttle (struct usb_serial_port *port) { dbg("%s - port %d", __FUNCTION__, port->number); - usb_unlink_urb (port->read_urb); + usb_kill_urb(port->read_urb); } -- cgit v1.2.3 From 6b8f5df3fd50592f962b5ffeda8b197fdc6e47fa Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 8 Sep 2004 21:49:56 -0700 Subject: USB: fix usb_unlink_urb() usage in kl5kusb105 driver Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/kl5kusb105.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/serial/kl5kusb105.c b/drivers/usb/serial/kl5kusb105.c index c5207d19401a..9e60b3476775 100644 --- a/drivers/usb/serial/kl5kusb105.c +++ b/drivers/usb/serial/kl5kusb105.c @@ -336,12 +336,12 @@ static void klsi_105_shutdown (struct usb_serial *serial) for (j = 0; j < NUM_URBS; j++) { if (write_urbs[j]) { /* FIXME - uncomment the following - * usb_unlink_urb call when the host + * usb_kill_urb call when the host * controllers get fixed to set * urb->dev = NULL after the urb is * finished. Otherwise this call * oopses. */ - /* usb_unlink_urb(write_urbs[j]); */ + /* usb_kill_urb(write_urbs[j]); */ if (write_urbs[j]->transfer_buffer) kfree(write_urbs[j]->transfer_buffer); usb_free_urb (write_urbs[j]); @@ -467,12 +467,12 @@ static void klsi_105_close (struct usb_serial_port *port, struct file *filp) err("Disabling read failed (error = %d)", rc); /* shutdown our bulk reads and writes */ - usb_unlink_urb (port->write_urb); - usb_unlink_urb (port->read_urb); + usb_kill_urb(port->write_urb); + usb_kill_urb(port->read_urb); /* unlink our write pool */ /* FIXME */ /* wgg - do I need this? I think so. */ - usb_unlink_urb (port->interrupt_in_urb); + usb_kill_urb(port->interrupt_in_urb); info("kl5kusb105 port stats: %ld bytes in, %ld bytes out", priv->bytes_in, priv->bytes_out); } /* klsi_105_close */ @@ -994,7 +994,7 @@ static int klsi_105_ioctl (struct usb_serial_port *port, struct file * file, static void klsi_105_throttle (struct usb_serial_port *port) { dbg("%s - port %d", __FUNCTION__, port->number); - usb_unlink_urb (port->read_urb); + usb_kill_urb(port->read_urb); } static void klsi_105_unthrottle (struct usb_serial_port *port) -- cgit v1.2.3 From f3a87694aea417c9d61b99c41025cbcc21c16727 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 8 Sep 2004 21:50:55 -0700 Subject: USB: fix usb_unlink_urb() usage in kobil_sct driver Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/kobil_sct.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/serial/kobil_sct.c b/drivers/usb/serial/kobil_sct.c index b479916ce269..33539874bd0c 100644 --- a/drivers/usb/serial/kobil_sct.c +++ b/drivers/usb/serial/kobil_sct.c @@ -350,14 +350,13 @@ static void kobil_close (struct usb_serial_port *port, struct file *filp) { dbg("%s - port %d", __FUNCTION__, port->number); - if (port->write_urb){ - usb_unlink_urb( port->write_urb ); + if (port->write_urb) { + usb_kill_urb(port->write_urb); usb_free_urb( port->write_urb ); port->write_urb = NULL; } - if (port->interrupt_in_urb){ - usb_unlink_urb (port->interrupt_in_urb); - } + if (port->interrupt_in_urb) + usb_kill_urb(port->interrupt_in_urb); } @@ -458,9 +457,8 @@ static int kobil_write (struct usb_serial_port *port, int from_user, ((priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID) && (priv->filled > 3) && (priv->filled >= (priv->buf[2] + 4))) ) { // stop reading (except TWIN and KAAN SIM) - if ( (priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID) || (priv->device_type == KOBIL_ADAPTER_K_PRODUCT_ID) ) { - usb_unlink_urb( port->interrupt_in_urb ); - } + if ( (priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID) || (priv->device_type == KOBIL_ADAPTER_K_PRODUCT_ID) ) + usb_kill_urb(port->interrupt_in_urb); todo = priv->filled - priv->cur_pos; -- cgit v1.2.3 From 1f859b6efd97a06fc8bc5476f895438da16e4b1c Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 8 Sep 2004 21:51:42 -0700 Subject: USB: fix usb_unlink_urb() usage in io_ti driver Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/io_ti.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c index 55be299fdf84..41afb4c77f69 100644 --- a/drivers/usb/serial/io_ti.c +++ b/drivers/usb/serial/io_ti.c @@ -1977,7 +1977,7 @@ static void edge_close (struct usb_serial_port *port, struct file * filp) /* chase the port close */ TIChasePort (edge_port); - usb_unlink_urb (port->read_urb); + usb_kill_urb(port->read_urb); /* assuming we can still talk to the device, * send a close port command to it */ @@ -1992,7 +1992,7 @@ static void edge_close (struct usb_serial_port *port, struct file * filp) --edge_port->edge_serial->num_ports_open; if (edge_port->edge_serial->num_ports_open <= 0) { /* last port is now closed, let's shut down our interrupt urb */ - usb_unlink_urb (port->serial->port[0]->interrupt_in_urb); + usb_kill_urb(port->serial->port[0]->interrupt_in_urb); edge_port->edge_serial->num_ports_open = 0; } edge_port->close_pending = 0; @@ -2126,7 +2126,7 @@ static void edge_throttle (struct usb_serial_port *port) status = TIClearRts (edge_port); } - usb_unlink_urb (port->read_urb); + usb_kill_urb(port->read_urb); } static void edge_unthrottle (struct usb_serial_port *port) -- cgit v1.2.3 From 8f5d1a12813f8a7c4a42c753e5ada18fc1e04b63 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 8 Sep 2004 21:52:22 -0700 Subject: USB: fix usb_unlink_urb() usage in ftdi_sio driver Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/ftdi_sio.c | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index b72f7ad4d05e..9d3a8e831326 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -1488,16 +1488,8 @@ static void ftdi_close (struct usb_serial_port *port, struct file *filp) } /* Note change no line if hupcl is off */ /* shutdown our bulk read */ - if (port->read_urb) { - if (usb_unlink_urb (port->read_urb) < 0) { - /* Generally, this isn't an error. If the previous - read bulk callback occurred (or is about to occur) - while the port was being closed or was throtted - (and is still throttled), the read urb will not - have been submitted. */ - dbg("%s - failed to unlink read urb (generally not an error)", __FUNCTION__); - } - } + if (port->read_urb) + usb_kill_urb(port->read_urb); } /* ftdi_close */ -- cgit v1.2.3 From 5426b1c448a21a259f545827f049f5edb23cb9d5 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 8 Sep 2004 21:54:43 -0700 Subject: USB: fix usb_unlink_urb() usage in keyspan_pda driver Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/keyspan_pda.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c index fd6c1b8388aa..47c60ea09e9f 100644 --- a/drivers/usb/serial/keyspan_pda.c +++ b/drivers/usb/serial/keyspan_pda.c @@ -291,7 +291,7 @@ static void keyspan_pda_rx_throttle (struct usb_serial_port *port) upon the device too. */ dbg("keyspan_pda_rx_throttle port %d", port->number); - usb_unlink_urb(port->interrupt_in_urb); + usb_kill_urb(port->interrupt_in_urb); } @@ -712,8 +712,8 @@ static void keyspan_pda_close(struct usb_serial_port *port, struct file *filp) keyspan_pda_set_modem_info(serial, 0); /* shutdown our bulk reads and writes */ - usb_unlink_urb (port->write_urb); - usb_unlink_urb (port->interrupt_in_urb); + usb_kill_urb(port->write_urb); + usb_kill_urb(port->interrupt_in_urb); } } -- cgit v1.2.3 From 6cd8c6c6351b9cdf210bea6816925018eac7d700 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 8 Sep 2004 21:56:19 -0700 Subject: USB: fix usb_unlink_urb() usage in generic usb-serial driver Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/generic.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c index 196fea084f21..c88885f8ca05 100644 --- a/drivers/usb/serial/generic.c +++ b/drivers/usb/serial/generic.c @@ -147,9 +147,9 @@ static void generic_cleanup (struct usb_serial_port *port) if (serial->dev) { /* shutdown any bulk reads that might be going on */ if (serial->num_bulk_out) - usb_unlink_urb (port->write_urb); + usb_kill_urb(port->write_urb); if (serial->num_bulk_in) - usb_unlink_urb (port->read_urb); + usb_kill_urb(port->read_urb); } } -- cgit v1.2.3 From c1a4dd61d48e857e1583ceb4b6fa707fbedeb96f Mon Sep 17 00:00:00 2001 From: David Brownell Date: Thu, 9 Sep 2004 02:05:09 -0700 Subject: [PATCH] export usb_set_device_state(), use in ohci This patch is mostly cleanup, but it all helps make PM_SUSPEND_DISK start to behave better. This exports the new usb_set_device_state() routine for the virtual root hubs, and uses it in OHCI during resume after power-off to replace some HC-private code doing almost the same thing. Note that all HCDs will likely need the same kind of suspend-to-disk support (though it's different when BIOS kicks in). Some systems even power-off during suspend-to-ram (to save extra power), which is why OHCI already has this logic! Related updates: - Use usb_set_device_state() immediately when an HC dies, making khubd handle disconnect processing instead of a workqueue. So now drivers won't self-deadlock in this should-be-rare path, when disconnect() calls flush_scheduled_work(). - Don't warn about "Unlink after no-IRQ" for the the root hub's status URB ... like when suspending an HCD that never enumerated a device. - Minor IRQ handler cleanup, including more accurate tracking of whether this driver ever returned IRQ_HANDLED (shared IRQs don't count). Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd.c | 29 ++++++----------------------- drivers/usb/core/hcd.h | 4 +++- drivers/usb/core/hub.c | 3 ++- drivers/usb/core/usb.h | 2 -- drivers/usb/host/ohci-hcd.c | 14 +------------- 5 files changed, 12 insertions(+), 40 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 08a3378bc875..c1de465a10d1 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -1263,7 +1263,7 @@ static int hcd_unlink_urb (struct urb *urb, int status) * never get completion IRQs ... maybe even the ones we need to * finish unlinking the initial failed usb_set_address(). */ - if (!hcd->saw_irq) { + if (!hcd->saw_irq && hcd->rh_timer.data != (unsigned long) urb) { dev_warn (hcd->self.controller, "Unlink after no-IRQ? " "Different ACPI or APIC settings may help." "\n"); @@ -1572,13 +1572,12 @@ irqreturn_t usb_hcd_irq (int irq, void *__hcd, struct pt_regs * r) struct usb_hcd *hcd = __hcd; int start = hcd->state; - if (unlikely (hcd->state == USB_STATE_HALT)) /* irq sharing? */ + if (start == USB_STATE_HALT) return IRQ_NONE; - - hcd->saw_irq = 1; if (hcd->driver->irq (hcd, r) == IRQ_NONE) return IRQ_NONE; + hcd->saw_irq = 1; if (hcd->state != start && hcd->state == USB_STATE_HALT) usb_hc_died (hcd); return IRQ_HANDLED; @@ -1587,22 +1586,6 @@ EXPORT_SYMBOL (usb_hcd_irq); /*-------------------------------------------------------------------------*/ -static void hcd_panic (void *_hcd) -{ - struct usb_hcd *hcd = _hcd; - struct usb_device *hub = hcd->self.root_hub; - unsigned i; - - /* hc's root hub is removed later removed in hcd->stop() */ - down (&hub->serialize); - usb_set_device_state(hub, USB_STATE_NOTATTACHED); - for (i = 0; i < hub->maxchild; i++) { - if (hub->children [i]) - usb_disconnect (&hub->children [i]); - } - up (&hub->serialize); -} - /** * usb_hc_died - report abnormal shutdown of a host controller (bus glue) * @hcd: pointer to the HCD representing the controller @@ -1615,9 +1598,9 @@ void usb_hc_died (struct usb_hcd *hcd) { dev_err (hcd->self.controller, "HC died; cleaning up\n"); - /* clean up old urbs and devices; needs a task context */ - INIT_WORK (&hcd->work, hcd_panic, hcd); - (void) schedule_work (&hcd->work); + /* make khubd clean up old urbs and devices */ + usb_set_device_state(hcd->self.root_hub, USB_STATE_NOTATTACHED); + mod_timer(&hcd->rh_timer, jiffies); } EXPORT_SYMBOL (usb_hc_died); diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h index 90e7d081b484..5e0337dfbcc9 100644 --- a/drivers/usb/core/hcd.h +++ b/drivers/usb/core/hcd.h @@ -67,7 +67,6 @@ struct usb_hcd { /* usb_bus.hcpriv points to this */ struct timer_list rh_timer; /* drives root hub */ struct list_head dev_list; /* devices on this bus */ - struct work_struct work; /* * hardware info/state @@ -363,6 +362,9 @@ static inline int hcd_register_root (struct usb_device *usb_dev, return usb_register_root_hub (usb_dev, hcd->self.controller); } +extern void usb_set_device_state(struct usb_device *udev, + enum usb_device_state new_state); + /*-------------------------------------------------------------------------*/ /* exported only within usbcore */ diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 8c9f120ea9aa..499ce8a1a388 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -910,7 +910,7 @@ static int locktree(struct usb_device *udev) } /** - * usb_set_device_state - change a device's current state (usbcore-internal) + * usb_set_device_state - change a device's current state (usbcore, hcds) * @udev: pointer to device whose state should be changed * @new_state: new state value to be stored * @@ -941,6 +941,7 @@ void usb_set_device_state(struct usb_device *udev, recursively_mark_NOTATTACHED(udev); spin_unlock_irqrestore(&device_state_lock, flags); } +EXPORT_SYMBOL(usb_set_device_state); static void choose_address(struct usb_device *udev) diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index b44bb8d7cdfa..8e9c923bff0b 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -22,8 +22,6 @@ extern int usb_get_device_descriptor(struct usb_device *dev, unsigned int size); extern int usb_set_configuration(struct usb_device *dev, int configuration); -extern void usb_set_device_state(struct usb_device *udev, - enum usb_device_state new_state); /* for labeling diagnostics */ extern const char *usbcore_name; diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index 12cc2844906d..10486ee608d2 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -726,18 +726,6 @@ static void ohci_stop (struct usb_hcd *hcd) #if defined(CONFIG_USB_SUSPEND) || defined(CONFIG_PM) -static void mark_children_gone (struct usb_device *dev) -{ - unsigned i; - - for (i = 0; i < dev->maxchild; i++) { - if (dev->children [i] == 0) - continue; - dev->children [i]->state = USB_STATE_NOTATTACHED; - mark_children_gone (dev->children [i]); - } -} - static int hc_restart (struct ohci_hcd *ohci) { int temp; @@ -751,7 +739,7 @@ static int hc_restart (struct ohci_hcd *ohci) */ spin_lock_irq(&ohci->lock); disable (ohci); - mark_children_gone (ohci->hcd.self.root_hub); + usb_set_device_state (ohci->hcd.self.root_hub, USB_STATE_NOTATTACHED); if (!list_empty (&ohci->pending)) ohci_dbg(ohci, "abort schedule...\n"); list_for_each_entry (priv, &ohci->pending, pending) { -- cgit v1.2.3 From a16d62137975ce06f514406c00eff823536ffd1a Mon Sep 17 00:00:00 2001 From: David Brownell Date: Thu, 9 Sep 2004 02:05:48 -0700 Subject: [PATCH] USB: gadget_is_n9604 This adds recognition of one more UDC driver. (Driver is available separately.) Tell gadget drivers about the National 9603/9604 UDC. Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/ether.c | 6 ++++++ drivers/usb/gadget/file_storage.c | 4 +++- drivers/usb/gadget/gadget_chips.h | 6 ++++++ drivers/usb/gadget/zero.c | 2 ++ 4 files changed, 17 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c index 763d0552146a..0c5f37c2ba0a 100644 --- a/drivers/usb/gadget/ether.c +++ b/drivers/usb/gadget/ether.c @@ -231,6 +231,10 @@ MODULE_PARM_DESC(host_addr, "Host Ethernet Address"); #define DEV_CONFIG_CDC #endif +#ifdef CONFIG_USB_GADGET_N9604 +#define DEV_CONFIG_CDC +#endif + /* For CDC-incapable hardware, choose the simple cdc subset. * Anything that talks bulk (without notable bugs) can do this. @@ -2334,6 +2338,8 @@ eth_bind (struct usb_gadget *gadget) device_desc.bcdDevice = __constant_cpu_to_le16 (0x0208); } else if (gadget_is_lh7a40x(gadget)) { device_desc.bcdDevice = __constant_cpu_to_le16 (0x0209); + } else if (gadget_is_n9604(gadget)) { + device_desc.bcdDevice = __constant_cpu_to_le16 (0x020a); } else { /* can't assume CDC works. don't want to default to * anything less functional on CDC-capable hardware, diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c index 1280a7cfacf4..2d2b2c032f19 100644 --- a/drivers/usb/gadget/file_storage.c +++ b/drivers/usb/gadget/file_storage.c @@ -3728,8 +3728,10 @@ static int __init check_parameters(struct fsg_dev *fsg) mod_data.release = __constant_cpu_to_le16(0x0307); else if (gadget_is_omap(fsg->gadget)) mod_data.release = __constant_cpu_to_le16(0x0308); - else if (gadget_is_lh7a40x(gadget)) + else if (gadget_is_lh7a40x(fsg->gadget)) mod_data.release = __constant_cpu_to_le16 (0x0309); + else if (gadget_is_n9604(fsg->gadget)) + mod_data.release = __constant_cpu_to_le16 (0x030a); else { WARN(fsg, "controller '%s' not recognized\n", fsg->gadget->name); diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h index e24e2f9ad28e..f6273701fec6 100644 --- a/drivers/usb/gadget/gadget_chips.h +++ b/drivers/usb/gadget/gadget_chips.h @@ -62,6 +62,12 @@ #define gadget_is_omap(g) 0 #endif +#ifdef CONFIG_USB_GADGET_N9604 +#define gadget_is_n9604(g) !strcmp("n9604_udc", (g)->name) +#else +#define gadget_is_n9604(g) 0 +#endif + // CONFIG_USB_GADGET_AT91RM9200 // CONFIG_USB_GADGET_SX2 // CONFIG_USB_GADGET_AU1X00 diff --git a/drivers/usb/gadget/zero.c b/drivers/usb/gadget/zero.c index a415a33ed117..69962c30a3a9 100644 --- a/drivers/usb/gadget/zero.c +++ b/drivers/usb/gadget/zero.c @@ -1188,6 +1188,8 @@ autoconf_fail: device_desc.bcdDevice = __constant_cpu_to_le16 (0x0208); } else if (gadget_is_lh7a40x(gadget)) { device_desc.bcdDevice = __constant_cpu_to_le16 (0x0209); + } else if (gadget_is_n9604(gadget)) { + device_desc.bcdDevice = __constant_cpu_to_le16 (0x020a); } else { /* gadget zero is so simple (for now, no altsettings) that * it SHOULD NOT have problems with bulk-capable hardware. -- cgit v1.2.3 From 65a7d3e495b3a02d58e28cb61eecf5a212509031 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Thu, 9 Sep 2004 02:06:55 -0700 Subject: [PATCH] USB: ohci updates Small bugfixes, at least one of which gets rid of some rather random behavior from certain board init behaviors. OHCI updates: - Bugfix the code taking frame clock adjustments from the boot loader. A recent change had a bug which caused inconsistent failures on some OHCI configs, including amd756. Thanks to for tracking down the specifics. - From Lothar Wassmann two fixes: (a) don't let tick clock sign-extend, that can make unlinks take excessively long (could happen easily enough); (b) when re-activating schedules after suspend, use the right bitmask (rare/exotic) - When suspending the root hub, mark it as USB_STATE_SUSPENDED Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ohci-dbg.c | 4 ++-- drivers/usb/host/ohci-hcd.c | 16 ++++++++++++---- drivers/usb/host/ohci-hub.c | 9 +++++---- drivers/usb/host/ohci.h | 5 +++-- 4 files changed, 22 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/ohci-dbg.c b/drivers/usb/host/ohci-dbg.c index 54daaacdc8c9..532164079cfa 100644 --- a/drivers/usb/host/ohci-dbg.c +++ b/drivers/usb/host/ohci-dbg.c @@ -640,14 +640,14 @@ show_registers (struct class_device *class_dev, char *buf) rdata = ohci_readl (®s->fminterval); temp = scnprintf (next, size, "fmintvl 0x%08x %sFSMPS=0x%04x FI=0x%04x\n", - rdata, (rdata >> 31) ? " FIT" : "", + rdata, (rdata >> 31) ? "FIT " : "", (rdata >> 16) & 0xefff, rdata & 0xffff); size -= temp; next += temp; rdata = ohci_readl (®s->fmremaining); temp = scnprintf (next, size, "fmremaining 0x%08x %sFR=0x%04x\n", - rdata, (rdata >> 31) ? " FRT" : "", + rdata, (rdata >> 31) ? "FRT " : "", rdata & 0x3fff); size -= temp; next += temp; diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index 10486ee608d2..d96a4edbd022 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -409,11 +409,19 @@ static int hc_reset (struct ohci_hcd *ohci) /* boot firmware should have set this up (5.1.1.3.1) */ if (!ohci->fminterval) { + u32 t2; + temp = ohci_readl (&ohci->regs->fminterval); - if (temp & 0x3fff0000) - ohci->fminterval = temp; - else - ohci->fminterval = DEFAULT_FMINTERVAL; + ohci->fminterval = temp & 0x3fff; + if (ohci->fminterval != FI) + ohci_dbg (ohci, "fminterval delta %d\n", + ohci->fminterval - FI); + + t2 = FSMP (ohci->fminterval); + temp >>= 16; + if ((t2/2) < temp || temp > t2) + temp = t2; + ohci->fminterval |= temp << 16; /* also: power/overcurrent flags in roothub.a */ } diff --git a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c index 344b1cab16e1..11093ed39cb1 100644 --- a/drivers/usb/host/ohci-hub.c +++ b/drivers/usb/host/ohci-hub.c @@ -146,10 +146,11 @@ static int ohci_hub_suspend (struct usb_hcd *hcd) ohci->next_statechange = jiffies + msecs_to_jiffies (5); succeed: - /* it's not USB_STATE_SUSPENDED unless access to this + /* it's not HCD_STATE_SUSPENDED unless access to this * hub from the non-usb side (PCI, SOC, etc) stopped */ root->dev.power.power_state = 3; + root->state = USB_STATE_SUSPENDED; done: spin_unlock_irq (&ohci->lock); return status; @@ -289,7 +290,7 @@ static int ohci_hub_resume (struct usb_hcd *hcd) ohci->hc_control |= enables; writel (ohci->hc_control, &ohci->regs->control); if (temp) - writel (status, &ohci->regs->cmdstatus); + writel (temp, &ohci->regs->cmdstatus); (void) ohci_readl (&ohci->regs->control); } @@ -481,8 +482,8 @@ static void start_hnp(struct ohci_hcd *ohci); /* this timer value might be vendor-specific ... */ #define PORT_RESET_HW_MSEC 10 -/* wrap-aware logic stolen from */ -#define tick_before(t1,t2) ((((s16)(t1))-((s16)(t2))) < 0) +/* wrap-aware logic morphed from */ +#define tick_before(t1,t2) ((s16)(((s16)(t1))-((s16)(t2))) < 0) /* called from some task, normally khubd */ static inline void root_port_reset (struct ohci_hcd *ohci, unsigned port) diff --git a/drivers/usb/host/ohci.h b/drivers/usb/host/ohci.h index c60f445d0011..079bbc1b00c9 100644 --- a/drivers/usb/host/ohci.h +++ b/drivers/usb/host/ohci.h @@ -406,13 +406,14 @@ static inline void disable (struct ohci_hcd *ohci) } #define FI 0x2edf /* 12000 bits per frame (-1) */ -#define DEFAULT_FMINTERVAL ((((6 * (FI - 210)) / 7) << 16) | FI) +#define FSMP(fi) ((6 * ((fi) - 210)) / 7) #define LSTHRESH 0x628 /* lowspeed bit threshold */ static inline void periodic_reinit (struct ohci_hcd *ohci) { + u32 fi = ohci->fminterval & 0x0ffff; writel (ohci->fminterval, &ohci->regs->fminterval); - writel (((9 * FI) / 10) & 0x3fff, &ohci->regs->periodicstart); + writel (((9 * fi) / 10) & 0x3fff, &ohci->regs->periodicstart); writel (LSTHRESH, &ohci->regs->lsthresh); } -- cgit v1.2.3 From 697910158a242b972329b1f99037ae814766f8ba Mon Sep 17 00:00:00 2001 From: David Brownell Date: Thu, 9 Sep 2004 02:07:40 -0700 Subject: [PATCH] USB: khubd looks at ports after probe Fix the hub probe logic so that when khubd wakes up, it will actually look at all ports on the new hub. This resolved a root hub enumeration issue, and I expect the "boot from USB disk" folk will like this too. The fix includes cleanup: centralizing the logic to make khubd look at a given hub, instead of cloning it three times (or in the case of hub probing, frankensteining it). This also adds a FIXME to the new routine centralizing disconnect processing: we also want "power down port but don't wake khubd" (for PM_SUSPEND_DISK as well as SRP). Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hub.c | 47 +++++++++++++++++++++++++++++++---------------- 1 file changed, 31 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 499ce8a1a388..13879305bf01 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -39,12 +39,13 @@ /* Protect struct usb_device state and children members */ static spinlock_t device_state_lock = SPIN_LOCK_UNLOCKED; -/* Wakes up khubd */ +/* khubd's worklist and its lock */ static spinlock_t hub_event_lock = SPIN_LOCK_UNLOCKED; - static LIST_HEAD(hub_event_list); /* List of hubs needing servicing */ +/* Wakes up khubd */ static DECLARE_WAIT_QUEUE_HEAD(khubd_wait); + static pid_t khubd_pid = 0; /* PID of khubd */ static DECLARE_COMPLETION(khubd_exited); @@ -226,6 +227,19 @@ static int get_port_status(struct usb_device *hdev, int port, data, sizeof(*data), HZ * USB_CTRL_GET_TIMEOUT); } +static void kick_khubd(struct usb_hub *hub) +{ + unsigned long flags; + + spin_lock_irqsave(&hub_event_lock, flags); + if (list_empty(&hub->event_list)) { + list_add_tail(&hub->event_list, &hub_event_list); + wake_up(&khubd_wait); + } + spin_unlock_irqrestore(&hub_event_lock, flags); +} + + /* completion function, fires on port status changes and various faults */ static void hub_irq(struct urb *urb, struct pt_regs *regs) { @@ -261,12 +275,7 @@ static void hub_irq(struct urb *urb, struct pt_regs *regs) hub->nerrors = 0; /* Something happened, let khubd figure it out */ - spin_lock(&hub_event_lock); - if (list_empty(&hub->event_list)) { - list_add_tail(&hub->event_list, &hub_event_list); - wake_up(&khubd_wait); - } - spin_unlock(&hub_event_lock); + kick_khubd(hub); resubmit: if (hub->quiescing) @@ -579,6 +588,9 @@ static int hub_configure(struct usb_hub *hub, dev_dbg(hub_dev, "%sover-current condition exists\n", (hubstatus & HUB_STATUS_OVERCURRENT) ? "" : "no "); + /* scan all ports ASAP on new hubs */ + hub->change_bits[0] = ~0; + /* Start the interrupt endpoint */ pipe = usb_rcvintpipe(hdev, endpoint->bEndpointAddress); maxp = usb_maxpacket(hdev, pipe, usb_pipeout(pipe)); @@ -604,7 +616,7 @@ static int hub_configure(struct usb_hub *hub, } /* Wake up khubd */ - wake_up(&khubd_wait); + kick_khubd(hub); /* maybe start cycling the hub leds */ if (hub->has_indicators && blinkenlights) { @@ -1408,15 +1420,18 @@ static void hub_port_logical_disconnect(struct usb_device *hdev, int port) dev_dbg(hubdev(hdev), "logical disconnect on port %d\n", port + 1); hub_port_disable(hdev, port); + /* FIXME let caller ask to power down the port: + * - some devices won't enumerate without a VBUS power cycle + * - SRP saves power that way + * - usb_suspend_device(dev,PM_SUSPEND_DISK) + * That's easy if this hub can switch power per-port, and + * khubd reactivates the port later (timer, SRP, etc). + * Powerdown must be optional, because of reset/DFU. + */ + hub = usb_get_intfdata(hdev->actconfig->interface[0]); set_bit(port, hub->change_bits); - - spin_lock_irq(&hub_event_lock); - if (list_empty(&hub->event_list)) { - list_add_tail(&hub->event_list, &hub_event_list); - wake_up(&khubd_wait); - } - spin_unlock_irq(&hub_event_lock); + kick_khubd(hub); } -- cgit v1.2.3 From 10f249cf7f98600d2984b14524e7885bca786516 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Thu, 9 Sep 2004 02:46:47 -0700 Subject: [PATCH] USB: omap_udc supports 5910/1510 chips This removes the "don't run on OMAP 1510" constraint from the driver and teaches it how not to use registers only available on newer chips. It's effectively just a compile fix, though ... hasn't got any board support yet. Basic build fixes for the OMAP 1510/5910. This first generation chip doesn't include an OTG controller. The patch makes the driver stop using OTG registers, and adds some #defines that will be needed to support boards using those chips. Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/omap_udc.c | 144 +++++++++++++++++++++++------------------- drivers/usb/gadget/omap_udc.h | 11 +++- 2 files changed, 89 insertions(+), 66 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/omap_udc.c index e40089d79365..84de1faeab64 100644 --- a/drivers/usb/gadget/omap_udc.c +++ b/drivers/usb/gadget/omap_udc.c @@ -1200,7 +1200,8 @@ static void pullup_enable(struct omap_udc *udc) { UDC_SYSCON1_REG |= UDC_PULLUP_EN; #ifndef CONFIG_USB_OTG - OTG_CTRL_REG |= OTG_BSESSVLD; + if (!cpu_is_omap15xx()) + OTG_CTRL_REG |= OTG_BSESSVLD; #endif UDC_IRQ_EN_REG = UDC_DS_CHG_IE; } @@ -1208,7 +1209,8 @@ static void pullup_enable(struct omap_udc *udc) static void pullup_disable(struct omap_udc *udc) { #ifndef CONFIG_USB_OTG - OTG_CTRL_REG &= ~OTG_BSESSVLD; + if (!cpu_is_omap15xx()) + OTG_CTRL_REG &= ~OTG_BSESSVLD; #endif UDC_IRQ_EN_REG = UDC_DS_CHG_IE; UDC_SYSCON1_REG &= ~UDC_PULLUP_EN; @@ -1688,7 +1690,7 @@ static void devstate_irq(struct omap_udc *udc, u16 irq_src) } change &= ~UDC_SUS; } - if (change & OTG_FLAGS) { + if (!cpu_is_omap15xx() && (change & OTG_FLAGS)) { update_otg(udc); change &= ~OTG_FLAGS; } @@ -2036,34 +2038,14 @@ static char *trx_mode(unsigned m) } } -static int proc_udc_show(struct seq_file *s, void *_) +static int proc_otg_show(struct seq_file *s) { u32 tmp; - struct omap_ep *ep; - unsigned long flags; - - spin_lock_irqsave(&udc->lock, flags); - seq_printf(s, "%s, version: " DRIVER_VERSION -#ifdef USE_ISO - " (iso)" -#endif - "%s\n", - driver_desc, - use_dma ? " (dma)" : ""); - - tmp = UDC_REV_REG & 0xff; - seq_printf(s, - "UDC rev %d.%d, OTG rev %d.%d, fifo mode %d, gadget %s\n" - "hmc %d, transceiver %08x %s\n", + tmp = OTG_REV_REG; + seq_printf(s, "OTG rev %d.%d, transceiver_ctrl %08x\n", tmp >> 4, tmp & 0xf, - OTG_REV_REG >> 4, OTG_REV_REG & 0xf, - fifo_mode, - udc->driver ? udc->driver->driver.name : "(none)", - HMC, USB_TRANSCEIVER_CTRL_REG, - udc->transceiver ? udc->transceiver->label : ""); - - /* OTG controller registers */ + USB_TRANSCEIVER_CTRL_REG); tmp = OTG_SYSCON_1_REG; seq_printf(s, "otg_syscon1 %08x usb2 %s, usb1 %s, usb0 %s," FOURBITS "\n", tmp, @@ -2117,6 +2099,37 @@ static int proc_udc_show(struct seq_file *s, void *_) seq_printf(s, "otg_outctrl %04x" "\n", tmp); tmp = OTG_TEST_REG; seq_printf(s, "otg_test %04x" "\n", tmp); +} + +static int proc_udc_show(struct seq_file *s, void *_) +{ + u32 tmp; + struct omap_ep *ep; + unsigned long flags; + + spin_lock_irqsave(&udc->lock, flags); + + seq_printf(s, "%s, version: " DRIVER_VERSION +#ifdef USE_ISO + " (iso)" +#endif + "%s\n", + driver_desc, + use_dma ? " (dma)" : ""); + + tmp = UDC_REV_REG & 0xff; + seq_printf(s, + "UDC rev %d.%d, fifo mode %d, gadget %s\n" + "hmc %d, transceiver %s\n", + tmp >> 4, tmp & 0xf, + fifo_mode, + udc->driver ? udc->driver->driver.name : "(none)", + HMC, + udc->transceiver ? udc->transceiver->label : ""); + + /* OTG controller registers */ + if (!cpu_is_omap15xx()) + proc_otg_show(s); tmp = UDC_SYSCON1_REG; seq_printf(s, "\nsyscon1 %04x" EIGHTBITS "\n", tmp, @@ -2496,41 +2509,51 @@ static int __init omap_udc_probe(struct device *dev) return -EBUSY; } - INFO("OMAP UDC rev %d.%d, OTG rev %d.%d, %s receptacle\n", + INFO("OMAP UDC rev %d.%d, %s receptacle\n", UDC_REV_REG >> 4, UDC_REV_REG & 0xf, - OTG_REV_REG >> 4, OTG_REV_REG & 0xf, config->otg ? "Mini-AB" : "B/Mini-B"); /* use the mode given to us by board init code */ - hmc = HMC; - switch (hmc) { - case 3: - case 11: - case 19: - case 25: - xceiv = otg_get_transceiver(); - if (!xceiv) { - DBG("external transceiver not registered!\n"); - goto cleanup0; - } - type = xceiv->label; - break; - case 0: /* POWERUP DEFAULT == 0 */ - case 4: - case 12: - case 20: - type = "INTEGRATED"; - break; - case 21: /* internal loopback */ - type = "(loopback)"; - break; - case 14: /* transceiverless */ - type = "(none)"; - break; + if (cpu_is_omap15xx()) { + hmc = HMC_1510; + type = "(unknown)"; - default: - ERR("unrecognized UDC HMC mode %d\n", hmc); - return -ENODEV; + /* FIXME may need a GPIO-0 handler to call + * usb_gadget_vbus_{dis,}connect() on us... + */ + } else { + hmc = HMC_1610; + switch (hmc) { + case 3: + case 11: + case 19: + case 25: + xceiv = otg_get_transceiver(); + if (!xceiv) { + DBG("external transceiver not registered!\n"); + if (config->otg) + goto cleanup0; + type = "(unknown external)"; + } else + type = xceiv->label; + break; + case 0: /* POWERUP DEFAULT == 0 */ + case 4: + case 12: + case 20: + type = "INTEGRATED"; + break; + case 21: /* internal loopback */ + type = "(loopback)"; + break; + case 14: /* transceiverless */ + type = "(none)"; + break; + + default: + ERR("unrecognized UDC HMC mode %d\n", hmc); + return -ENODEV; + } } INFO("hmc mode %d, transceiver %s\n", hmc, type); @@ -2671,13 +2694,6 @@ static struct device_driver udc_driver = { static int __init udc_init(void) { - /* should work on many OMAP systems with at most minor changes, - * but the 1510 doesn't have an OTG controller. - */ - if (cpu_is_omap1510()) { - DBG("no OMAP1510 support yet\n"); - return -ENODEV; - } INFO("%s, version: " DRIVER_VERSION "%s\n", driver_desc, use_dma ? " (dma)" : ""); return driver_register(&udc_driver); diff --git a/drivers/usb/gadget/omap_udc.h b/drivers/usb/gadget/omap_udc.h index bd5420cd0b05..ca8572314f95 100644 --- a/drivers/usb/gadget/omap_udc.h +++ b/drivers/usb/gadget/omap_udc.h @@ -193,7 +193,14 @@ struct omap_udc { /*-------------------------------------------------------------------------*/ -// #define HMC_1510 ((MOD_CONF_CTRL_0_REG >> 1) & 0x3f) +#define MOD_CONF_CTRL_0_REG __REG32(MOD_CONF_CTRL_0) +#define VBUS_W2FC_1510 (1 << 17) /* 0 gpio0, 1 dvdd2 pin */ + +#define FUNC_MUX_CTRL_0_REG __REG32(FUNC_MUX_CTRL_0) +#define VBUS_CTRL_1510 (1 << 19) /* 1 connected (software) */ +#define VBUS_MODE_1510 (1 << 18) /* 0 hardware, 1 software */ + +#define HMC_1510 ((MOD_CONF_CTRL_0_REG >> 1) & 0x3f) #define HMC_1610 (OTG_SYSCON_2_REG & 0x3f) -#define HMC HMC_1610 +#define HMC (cpu_is_omap15xx() ? HMC_1510 : HMC_1610) -- cgit v1.2.3 From a83b8e193a0302f30a6df3f24bfef62c0ebc2e88 Mon Sep 17 00:00:00 2001 From: John Stultz Date: Fri, 10 Sep 2004 01:02:06 -0700 Subject: [PATCH] USB: early usb handoff for 2.6 Apparently there is an issue w/ the IBM x440/x445's BIOS's USB Legacy support. Due to the delay in issuing SMI's across the IOAPICs, its possible for I/O to ports 60/64 to cause register corruption. The solution is to disable the BIOS's USB Legacy support early in boot(via PCI quirks) for x440/x445 systems. Originally written by Vojtech against SuSE's tree, this patch was then updated to disable EHCI by Aleksey Gorelov, cleaned up by Pete Zaitcev for 2.4 and finally tweaked and updated against 2.6 by me. I've lightly tested this version of the patch, but it differs little from what Aleksey, Pete and I have been testing for 2.4. Signed-off-by: John Stultz Signed-off-by: Greg Kroah-Hartman --- Documentation/kernel-parameters.txt | 2 + drivers/pci/quirks.c | 230 ++++++++++++++++++++++++++++ include/asm-i386/mach-summit/mach_mpparse.h | 3 + 3 files changed, 235 insertions(+) (limited to 'drivers') diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index ae0eb1a3d4e4..393986feb77e 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -1270,6 +1270,8 @@ running once the system is up. uart6850= [HW,OSS] Format: , + + usb-handoff [HW] Enable early USB BIOS -> OS handoff video= [FB] Frame buffer configuration See Documentation/fb/modedb.txt. diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 164f738df6a7..34f072c9823e 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -826,6 +826,236 @@ static void __init quirk_sis_96x_smbus(struct pci_dev *dev) pci_read_config_byte(dev, 0x77, &val); } + +#define UHCI_USBLEGSUP 0xc0 /* legacy support */ +#define UHCI_USBCMD 0 /* command register */ +#define UHCI_USBSTS 2 /* status register */ +#define UHCI_USBINTR 4 /* interrupt register */ +#define UHCI_USBLEGSUP_DEFAULT 0x2000 /* only PIRQ enable set */ +#define UHCI_USBCMD_RUN (1 << 0) /* RUN/STOP bit */ +#define UHCI_USBCMD_GRESET (1 << 2) /* Global reset */ +#define UHCI_USBCMD_CONFIGURE (1 << 6) /* config semaphore */ +#define UHCI_USBSTS_HALTED (1 << 5) /* HCHalted bit */ + +#define OHCI_CONTROL 0x04 +#define OHCI_CMDSTATUS 0x08 +#define OHCI_INTRSTATUS 0x0c +#define OHCI_INTRENABLE 0x10 +#define OHCI_INTRDISABLE 0x14 +#define OHCI_OCR (1 << 3) /* ownership change request */ +#define OHCI_CTRL_IR (1 << 8) /* interrupt routing */ +#define OHCI_INTR_OC (1 << 30) /* ownership change */ + +#define EHCI_HCC_PARAMS 0x08 /* extended capabilities */ +#define EHCI_USBCMD 0 /* command register */ +#define EHCI_USBCMD_RUN (1 << 0) /* RUN/STOP bit */ +#define EHCI_USBSTS 4 /* status register */ +#define EHCI_USBSTS_HALTED (1 << 12) /* HCHalted bit */ +#define EHCI_USBINTR 8 /* interrupt register */ +#define EHCI_USBLEGSUP 0 /* legacy support register */ +#define EHCI_USBLEGSUP_BIOS (1 << 16) /* BIOS semaphore */ +#define EHCI_USBLEGSUP_OS (1 << 24) /* OS semaphore */ +#define EHCI_USBLEGCTLSTS 4 /* legacy control/status */ +#define EHCI_USBLEGCTLSTS_SOOE (1 << 13) /* SMI on ownership change */ + +int usb_early_handoff __initdata = 0; +static int __init usb_handoff_early(char *str) +{ + usb_early_handoff = 1; + return 0; +} +__setup("usb-handoff", usb_handoff_early); + +static void __init quirk_usb_handoff_uhci(struct pci_dev *pdev) +{ + unsigned long base = 0; + int wait_time, delta; + u16 val, sts; + int i; + + for (i = 0; i < PCI_ROM_RESOURCE; i++) + if ((pci_resource_flags(pdev, i) & IORESOURCE_IO)) { + base = pci_resource_start(pdev, i); + break; + } + + if (!base) + return; + + /* + * stop controller + */ + sts = inw(base + UHCI_USBSTS); + val = inw(base + UHCI_USBCMD); + val &= ~(u16)(UHCI_USBCMD_RUN | UHCI_USBCMD_CONFIGURE); + outw(val, base + UHCI_USBCMD); + + /* + * wait while it stops if it was running + */ + if ((sts & UHCI_USBSTS_HALTED) == 0) + { + wait_time = 1000; + delta = 100; + + do { + outw(0x1f, base + UHCI_USBSTS); + udelay(delta); + wait_time -= delta; + val = inw(base + UHCI_USBSTS); + if (val & UHCI_USBSTS_HALTED) + break; + } while (wait_time > 0); + } + + /* + * disable interrupts & legacy support + */ + outw(0, base + UHCI_USBINTR); + outw(0x1f, base + UHCI_USBSTS); + pci_read_config_word(pdev, UHCI_USBLEGSUP, &val); + if (val & 0xbf) + pci_write_config_word(pdev, UHCI_USBLEGSUP, UHCI_USBLEGSUP_DEFAULT); + +} + +static void __init quirk_usb_handoff_ohci(struct pci_dev *pdev) +{ + char *base; + int wait_time; + + base = ioremap_nocache(pci_resource_start(pdev, 0), + pci_resource_len(pdev, 0)); + if (base == NULL) return; + + if (readl(base + OHCI_CONTROL) & OHCI_CTRL_IR) { + wait_time = 500; /* 0.5 seconds */ + writel(OHCI_INTR_OC, base + OHCI_INTRENABLE); + writel(OHCI_OCR, base + OHCI_CMDSTATUS); + while (wait_time > 0 && + readl(base + OHCI_CONTROL) & OHCI_CTRL_IR) { + wait_time -= 10; + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((HZ*10 + 999) / 1000); + } + } + + /* + * disable interrupts + */ + writel(~(u32)0, base + OHCI_INTRDISABLE); + writel(~(u32)0, base + OHCI_INTRSTATUS); + + iounmap(base); +} + +static void __init quirk_usb_disable_ehci(struct pci_dev *pdev) +{ + int wait_time, delta; + char *base, *op_reg_base; + u32 hcc_params, val, temp; + u8 cap_length; + + base = ioremap_nocache(pci_resource_start(pdev, 0), + pci_resource_len(pdev, 0)); + if (base == NULL) return; + + cap_length = readb(base); + op_reg_base = base + cap_length; + hcc_params = readl(base + EHCI_HCC_PARAMS); + hcc_params = (hcc_params >> 8) & 0xff; + if (hcc_params) { + pci_read_config_dword(pdev, + hcc_params + EHCI_USBLEGSUP, + &val); + if (((val & 0xff) == 1) && (val & EHCI_USBLEGSUP_BIOS)) { + /* + * Ok, BIOS is in smm mode, try to hand off... + */ + pci_read_config_dword(pdev, + hcc_params + EHCI_USBLEGCTLSTS, + &temp); + pci_write_config_dword(pdev, + hcc_params + EHCI_USBLEGCTLSTS, + temp | EHCI_USBLEGCTLSTS_SOOE); + val |= EHCI_USBLEGSUP_OS; + pci_write_config_dword(pdev, + hcc_params + EHCI_USBLEGSUP, + val); + + wait_time = 500; + do { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((HZ*10+999)/1000); + wait_time -= 10; + pci_read_config_dword(pdev, + hcc_params + EHCI_USBLEGSUP, + &val); + } while (wait_time && (val & EHCI_USBLEGSUP_BIOS)); + if (!wait_time) { + /* + * well, possibly buggy BIOS... + */ + printk(KERN_WARNING "EHCI early BIOS handoff " + "failed (BIOS bug ?)\n"); + pci_write_config_dword(pdev, + hcc_params + EHCI_USBLEGSUP, + EHCI_USBLEGSUP_OS); + pci_write_config_dword(pdev, + hcc_params + EHCI_USBLEGCTLSTS, + 0); + } + } + } + + /* + * halt EHCI & disable its interrupts in any case + */ + val = readl(op_reg_base + EHCI_USBSTS); + if ((val & EHCI_USBSTS_HALTED) == 0) { + val = readl(op_reg_base + EHCI_USBCMD); + val &= ~EHCI_USBCMD_RUN; + writel(val, op_reg_base + EHCI_USBCMD); + + wait_time = 2000; + delta = 100; + do { + writel(0x3f, op_reg_base + EHCI_USBSTS); + udelay(delta); + wait_time -= delta; + val = readl(op_reg_base + EHCI_USBSTS); + if ((val == ~(u32)0) || (val & EHCI_USBSTS_HALTED)) { + break; + } + } while (wait_time > 0); + } + writel(0, op_reg_base + EHCI_USBINTR); + writel(0x3f, op_reg_base + EHCI_USBSTS); + + iounmap(base); + + return; +} + + + +static void __init quirk_usb_early_handoff(struct pci_dev *pdev) +{ + if (!usb_early_handoff) + return; + + if (pdev->class == ((PCI_CLASS_SERIAL_USB << 8) | 0x00)) { /* UHCI */ + quirk_usb_handoff_uhci(pdev); + } else if (pdev->class == ((PCI_CLASS_SERIAL_USB << 8) | 0x10)) { /* OHCI */ + quirk_usb_handoff_ohci(pdev); + } else if (pdev->class == ((PCI_CLASS_SERIAL_USB << 8) | 0x20)) { /* EHCI */ + quirk_usb_disable_ehci(pdev); + } + + return; +} +DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, quirk_usb_early_handoff); + /* * ... This is further complicated by the fact that some SiS96x south * bridges pretend to be 85C503/5513 instead. In that case see if we diff --git a/include/asm-i386/mach-summit/mach_mpparse.h b/include/asm-i386/mach-summit/mach_mpparse.h index 1cce2b924a80..2b9e6d55bef1 100644 --- a/include/asm-i386/mach-summit/mach_mpparse.h +++ b/include/asm-i386/mach-summit/mach_mpparse.h @@ -22,6 +22,7 @@ static inline void mpc_oem_pci_bus(struct mpc_config_bus *m, { } +extern int usb_early_handoff; static inline int mps_oem_check(struct mp_config_table *mpc, char *oem, char *productid) { @@ -31,6 +32,7 @@ static inline int mps_oem_check(struct mp_config_table *mpc, char *oem, || !strncmp(productid, "RUTHLESS SMP", 12))){ use_cyclone = 1; /*enable cyclone-timer*/ setup_summit(); + usb_early_handoff = 1; return 1; } return 0; @@ -44,6 +46,7 @@ static inline int acpi_madt_oem_check(char *oem_id, char *oem_table_id) || !strncmp(oem_table_id, "EXA", 3))){ use_cyclone = 1; /*enable cyclone-timer*/ setup_summit(); + usb_early_handoff = 1; return 1; } return 0; -- cgit v1.2.3 From 6a51d280116e0ce69b8105242c61047c690ff49d Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Fri, 10 Sep 2004 01:02:34 -0700 Subject: [PATCH] USB: Improve UHCI suspend/resume This patch fixes some problems with the suspend/resume handling in the UHCI driver: Don't try to resume ports that aren't already suspended. The controller won't permit Resume signals to be sent to a port if the port isn't enabled, so don't assume it will work. Port reset and port disable will terminate Resume signalling. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/uhci-hub.c | 67 +++++++++++++++++++++++++++++++-------------- 1 file changed, 47 insertions(+), 20 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/uhci-hub.c b/drivers/usb/host/uhci-hub.c index e869ad4061f1..538be7c5a409 100644 --- a/drivers/usb/host/uhci-hub.c +++ b/drivers/usb/host/uhci-hub.c @@ -66,11 +66,23 @@ static int uhci_hub_status_data(struct usb_hcd *hcd, char *buf) * so we have to poll and check timeouts in order to take care of it. * FIXME: Synchronize access to these fields by a spinlock. */ +static void uhci_finish_suspend(struct uhci_hcd *uhci, int port, + unsigned int port_addr) +{ + int status; + + if (test_bit(port, &uhci->suspended_ports)) { + CLR_RH_PORTSTAT(USBPORTSC_SUSP | USBPORTSC_RD); + clear_bit(port, &uhci->suspended_ports); + clear_bit(port, &uhci->resuming_ports); + set_bit(port, &uhci->port_c_suspend); + } +} + static void uhci_check_resume(struct uhci_hcd *uhci) { unsigned int port; unsigned int port_addr; - int status; for (port = 0; port < uhci->rh_numports; ++port) { port_addr = uhci->io_addr + USBPORTSC1 + 2 * port; @@ -83,10 +95,7 @@ static void uhci_check_resume(struct uhci_hcd *uhci) msecs_to_jiffies(20); } else if (time_after_eq(jiffies, uhci->resume_timeout)) { - CLR_RH_PORTSTAT(USBPORTSC_SUSP | USBPORTSC_RD); - clear_bit(port, &uhci->resuming_ports); - clear_bit(port, &uhci->suspended_ports); - set_bit(port, &uhci->port_c_suspend); + uhci_finish_suspend(uhci, port, port_addr); } } } @@ -97,19 +106,12 @@ static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex, char *buf, u16 wLength) { struct uhci_hcd *uhci = hcd_to_uhci(hcd); - int status, retval = 0, len = 0; + int status, lstatus, retval = 0, len = 0; unsigned int port = wIndex - 1; unsigned long port_addr = uhci->io_addr + USBPORTSC1 + 2 * port; u16 wPortChange, wPortStatus; switch (typeReq) { - /* Request Destination: - without flags: Device, - RH_INTERFACE: interface, - RH_ENDPOINT: endpoint, - RH_CLASS means HUB here, - RH_OTHER | RH_CLASS almost ever means HUB_PORT here - */ case GetHubStatus: *(u32 *) buf = cpu_to_le32(0); @@ -132,15 +134,22 @@ static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, status ^= USBPORTSC_OC; /* UHCI doesn't support C_RESET (always false) */ - wPortChange = 0; + wPortChange = lstatus = 0; if (status & USBPORTSC_CSC) wPortChange |= USB_PORT_STAT_C_CONNECTION; if (status & USBPORTSC_PEC) wPortChange |= USB_PORT_STAT_C_ENABLE; if (status & USBPORTSC_OCC) wPortChange |= USB_PORT_STAT_C_OVERCURRENT; - if (test_bit(port, &uhci->port_c_suspend)) + + if (test_bit(port, &uhci->port_c_suspend)) { wPortChange |= USB_PORT_STAT_C_SUSPEND; + lstatus |= 1; + } + if (test_bit(port, &uhci->suspended_ports)) + lstatus |= 2; + if (test_bit(port, &uhci->resuming_ports)) + lstatus |= 4; /* UHCI has no power switching (always on) */ wPortStatus = USB_PORT_STAT_POWER; @@ -159,8 +168,8 @@ static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, wPortStatus |= USB_PORT_STAT_LOW_SPEED; if (wPortChange) - dev_dbg(uhci_dev(uhci), "port %d portsc %04x\n", - wIndex, status); + dev_dbg(uhci_dev(uhci), "port %d portsc %04x,%02x\n", + wIndex, status, lstatus); *(u16 *) buf = cpu_to_le16(wPortStatus); *(u16 *) (buf + 2) = cpu_to_le16(wPortChange); @@ -189,6 +198,9 @@ static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, mdelay(50); /* USB v1.1 7.1.7.3 */ CLR_RH_PORTSTAT(USBPORTSC_PR); udelay(10); + + /* Reset terminates Resume signalling */ + uhci_finish_suspend(uhci, port, port_addr); SET_RH_PORTSTAT(USBPORTSC_PE); mdelay(10); CLR_RH_PORTSTAT(USBPORTSC_PEC|USBPORTSC_CSC); @@ -207,14 +219,29 @@ static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, switch (wValue) { case USB_PORT_FEAT_ENABLE: CLR_RH_PORTSTAT(USBPORTSC_PE); + + /* Disable terminates Resume signalling */ + uhci_finish_suspend(uhci, port, port_addr); OK(0); case USB_PORT_FEAT_C_ENABLE: CLR_RH_PORTSTAT(USBPORTSC_PEC); OK(0); case USB_PORT_FEAT_SUSPEND: - set_bit(port, &uhci->resuming_ports); - uhci->resume_timeout = jiffies + msecs_to_jiffies(20); - SET_RH_PORTSTAT(USBPORTSC_RD); + if (test_bit(port, &uhci->suspended_ports) && + !test_and_set_bit(port, + &uhci->resuming_ports)) { + uhci->resume_timeout = jiffies + + msecs_to_jiffies(20); + SET_RH_PORTSTAT(USBPORTSC_RD); + + /* The controller won't allow RD to be set + * if the port is disabled. When this happens + * just skip the Resume signalling. + */ + if (!(inw(port_addr) & USBPORTSC_RD)) + uhci_finish_suspend(uhci, port, + port_addr); + } OK(0); case USB_PORT_FEAT_C_SUSPEND: clear_bit(port, &uhci->port_c_suspend); -- cgit v1.2.3 From 26d61c640c05c6cf04987437fad260908faaddf7 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Fri, 10 Sep 2004 01:04:14 -0700 Subject: [PATCH] USB: Fix off-by-one error in the hub driver This one-liner fixes a simple mistake in a newly-added part of the hub driver. When a connect change takes place on a suspended port the code clears the suspend, but the port number it passes is origin-0 and it should be origin-1. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hub.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 13879305bf01..29bba5e78da3 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -2050,7 +2050,7 @@ hub_port_init (struct usb_device *hdev, struct usb_device *udev, int port) hdev->bus->b_hnp_enable = 0; } - retval = clear_port_feature(hdev, port, USB_PORT_FEAT_SUSPEND); + retval = clear_port_feature(hdev, port + 1, USB_PORT_FEAT_SUSPEND); if (retval < 0 && retval != -EPIPE) dev_dbg(&udev->dev, "can't clear suspend; %d\n", retval); -- cgit v1.2.3 From fa4b40f4ef571816068e6d08b174d0a5c713ad36 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Mon, 13 Sep 2004 01:55:23 -0700 Subject: [PATCH] USB: ohci init refactor Please merge, some recent changes made problems by making init take too long. This also adds a bit of support for detecting the funky resume states that happen with suspend-to-disk (like swsusp, pmdisk). Refactor controller initialization ... this is most of the patch by volume. - A time-critical section now runs with IRQs blocked, rather than being split over two separate routines. (I've recently seen init failures because of preemption in the middle of that 2msec timeout, presumably by khubd.) - Bus glue for PCI, LH7A404, OMAP, and SA-1100 now shares more init logic; that'll help shrink support for upcoming non-PCI patches too. - Move the root hub register macros to the header (for debug build issue) - More tweaks to the frame clock initialization, including slightly more helpful diagnostics on "init err". Better SWSUSP support. - Detects and handles some funky "resume after suspend-to-disk" cases. These need to go through full driver re-init. - Restore root hub to CONFIGURED state on resume. Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ohci-hcd.c | 173 ++++++++++++++++++++++++++-------------- drivers/usb/host/ohci-hub.c | 46 +++-------- drivers/usb/host/ohci-lh7a404.c | 30 +------ drivers/usb/host/ohci-omap.c | 30 +------ drivers/usb/host/ohci-pci.c | 34 ++------ drivers/usb/host/ohci-sa1111.c | 25 +----- drivers/usb/host/ohci.h | 30 ++++++- 7 files changed, 167 insertions(+), 201 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index d96a4edbd022..c5b806265504 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -138,6 +138,11 @@ static const char hcd_name [] = "ohci_hcd"; #include "ohci.h" +static void ohci_dump (struct ohci_hcd *ohci, int verbose); +static int ohci_init (struct ohci_hcd *ohci); +static int ohci_restart (struct ohci_hcd *ohci); +static void ohci_stop (struct usb_hcd *hcd); + #include "ohci-hub.c" #include "ohci-dbg.c" #include "ohci-mem.c" @@ -397,33 +402,27 @@ static int ohci_get_frame (struct usb_hcd *hcd) return OHCI_FRAME_NO(ohci->hcca); } +static void ohci_usb_reset (struct ohci_hcd *ohci) +{ + ohci->hc_control = ohci_readl (&ohci->regs->control); + ohci->hc_control &= OHCI_CTRL_RWC; + writel (ohci->hc_control, &ohci->regs->control); +} + /*-------------------------------------------------------------------------* * HC functions *-------------------------------------------------------------------------*/ -/* reset the HC and BUS */ +/* init memory, and kick BIOS/SMM off */ -static int hc_reset (struct ohci_hcd *ohci) +static int ohci_init (struct ohci_hcd *ohci) { u32 temp; + int ret; - /* boot firmware should have set this up (5.1.1.3.1) */ - if (!ohci->fminterval) { - u32 t2; - - temp = ohci_readl (&ohci->regs->fminterval); - ohci->fminterval = temp & 0x3fff; - if (ohci->fminterval != FI) - ohci_dbg (ohci, "fminterval delta %d\n", - ohci->fminterval - FI); - - t2 = FSMP (ohci->fminterval); - temp >>= 16; - if ((t2/2) < temp || temp > t2) - temp = t2; - ohci->fminterval |= temp << 16; - /* also: power/overcurrent flags in roothub.a */ - } + disable (ohci); + ohci->regs = ohci->hcd.regs; + ohci->next_statechange = jiffies; #ifndef IR_DISABLE /* SMM owns the HC? not for long! */ @@ -442,14 +441,60 @@ static int hc_reset (struct ohci_hcd *ohci) msleep (10); if (--temp == 0) { ohci_err (ohci, "USB HC TakeOver failed!\n"); - return -1; + return -EBUSY; } } + ohci_usb_reset (ohci); } #endif /* Disable HC interrupts */ writel (OHCI_INTR_MIE, &ohci->regs->intrdisable); + // flush the writes + (void) ohci_readl (&ohci->regs->control); + + if (ohci->hcca) + return 0; + + ohci->hcca = dma_alloc_coherent (ohci->hcd.self.controller, + sizeof *ohci->hcca, &ohci->hcca_dma, 0); + if (!ohci->hcca) + return -ENOMEM; + + if ((ret = ohci_mem_init (ohci)) < 0) + ohci_stop (&ohci->hcd); + + return ret; + +} + +/*-------------------------------------------------------------------------*/ + +/* Start an OHCI controller, set the BUS operational + * resets USB and controller + * enable interrupts + * connect the virtual root hub + */ +static int ohci_run (struct ohci_hcd *ohci) +{ + u32 mask, temp; + struct usb_device *udev; + struct usb_bus *bus; + int first = ohci->fminterval == 0; + + disable (ohci); + + /* boot firmware should have set this up (5.1.1.3.1) */ + if (first) { + + temp = ohci_readl (&ohci->regs->fminterval); + ohci->fminterval = temp & 0x3fff; + if (ohci->fminterval != FI) + ohci_dbg (ohci, "fminterval delta %d\n", + ohci->fminterval - FI); + ohci->fminterval |= FSMP (ohci->fminterval) << 16; + /* also: power/overcurrent flags in roothub.a */ + } /* Reset USB nearly "by the book". RemoteWakeupConnected * saved if boot firmware (BIOS/SMM/...) told us it's connected @@ -495,18 +540,22 @@ static int hc_reset (struct ohci_hcd *ohci) } // flush those writes (void) ohci_readl (&ohci->regs->control); + memset (ohci->hcca, 0, sizeof (struct ohci_hcca)); + + /* 2msec timelimit here means no irqs/preempt */ + spin_lock_irq (&ohci->lock); /* HC Reset requires max 10 us delay */ writel (OHCI_HCR, &ohci->regs->cmdstatus); temp = 30; /* ... allow extra time */ while ((ohci_readl (&ohci->regs->cmdstatus) & OHCI_HCR) != 0) { if (--temp == 0) { + spin_unlock_irq (&ohci->lock); ohci_err (ohci, "USB HC reset timed out!\n"); return -1; } udelay (1); } - periodic_reinit (ohci); /* now we're in the SUSPEND state ... must go OPERATIONAL * within 2msec else HC enters RESUME @@ -520,22 +569,7 @@ static int hc_reset (struct ohci_hcd *ohci) // flush those writes (void) ohci_readl (&ohci->regs->control); } - return 0; -} - -/*-------------------------------------------------------------------------*/ - -/* Start an OHCI controller, set the BUS operational - * enable interrupts - * connect the virtual root hub - */ -static int hc_start (struct ohci_hcd *ohci) -{ - u32 mask, tmp; - struct usb_device *udev; - struct usb_bus *bus; - - disable (ohci); + writel (ohci->fminterval, &ohci->regs->fminterval); /* Tell the controller where the control and bulk lists are * The lists are empty now. */ @@ -545,12 +579,17 @@ static int hc_start (struct ohci_hcd *ohci) /* a reset clears this */ writel ((u32) ohci->hcca_dma, &ohci->regs->hcca); + periodic_reinit (ohci); + /* some OHCI implementations are finicky about how they init. * bogus values here mean not even enumeration could work. */ if ((ohci_readl (&ohci->regs->fminterval) & 0x3fff0000) == 0 || !ohci_readl (&ohci->regs->periodicstart)) { - ohci_err (ohci, "init err\n"); + spin_unlock_irq (&ohci->lock); + ohci_err (ohci, "init err (%08x %04x)\n", + ohci_readl (&ohci->regs->fminterval), + ohci_readl (&ohci->regs->periodicstart)); return -EOVERFLOW; } @@ -569,42 +608,48 @@ static int hc_start (struct ohci_hcd *ohci) writel (mask, &ohci->regs->intrenable); /* handle root hub init quirks ... */ - tmp = roothub_a (ohci); - tmp &= ~(RH_A_PSM | RH_A_OCPM); + temp = roothub_a (ohci); + temp &= ~(RH_A_PSM | RH_A_OCPM); if (ohci->flags & OHCI_QUIRK_SUPERIO) { /* NSC 87560 and maybe others */ - tmp |= RH_A_NOCP; - tmp &= ~(RH_A_POTPGT | RH_A_NPS); + temp |= RH_A_NOCP; + temp &= ~(RH_A_POTPGT | RH_A_NPS); } else if (power_switching) { /* act like most external hubs: use per-port power * switching and overcurrent reporting. */ - tmp &= ~(RH_A_NPS | RH_A_NOCP); - tmp |= RH_A_PSM | RH_A_OCPM; + temp &= ~(RH_A_NPS | RH_A_NOCP); + temp |= RH_A_PSM | RH_A_OCPM; } else { /* hub power always on; required for AMD-756 and some * Mac platforms. ganged overcurrent reporting, if any. */ - tmp |= RH_A_NPS; + temp |= RH_A_NPS; } - writel (tmp, &ohci->regs->roothub.a); + writel (temp, &ohci->regs->roothub.a); writel (RH_HS_LPSC, &ohci->regs->roothub.status); writel (power_switching ? RH_B_PPCM : 0, &ohci->regs->roothub.b); // flush those writes (void) ohci_readl (&ohci->regs->control); + spin_unlock_irq (&ohci->lock); + // POTPGT delay is bits 24-31, in 2 ms units. mdelay ((roothub_a (ohci) >> 23) & 0x1fe); bus = hcd_to_bus (&ohci->hcd); + ohci->hcd.state = USB_STATE_RUNNING; - if (bus->root_hub) { - ohci->hcd.state = USB_STATE_RUNNING; + ohci_dump (ohci, 1); + + udev = hcd_to_bus (&ohci->hcd)->root_hub; + if (udev) { + udev->dev.power.power_state = 0; + usb_set_device_state (udev, USB_STATE_CONFIGURED); return 0; } /* connect the virtual root hub */ udev = usb_alloc_dev (NULL, bus, 0); - ohci->hcd.state = USB_STATE_RUNNING; if (!udev) { disable (ohci); ohci->hc_control &= ~OHCI_CTRL_HCFS; @@ -620,7 +665,10 @@ static int hc_start (struct ohci_hcd *ohci) writel (ohci->hc_control, &ohci->regs->control); return -ENODEV; } + if (ohci->power_budget) + hub_set_power_budget(udev, ohci->power_budget); + create_debug_files (ohci); return 0; } @@ -657,8 +705,7 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd, struct pt_regs *ptregs) // e.g. due to PCI Master/Target Abort ohci_dump (ohci, 1); - ohci->hc_control &= OHCI_CTRL_RWC; /* hcfs 0 = RESET */ - writel (ohci->hc_control, &ohci->regs->control); + ohci_usb_reset (ohci); } if (ints & OHCI_INTR_RD) { @@ -712,10 +759,9 @@ static void ohci_stop (struct usb_hcd *hcd) ohci_dump (ohci, 1); flush_scheduled_work(); - if (HCD_IS_RUNNING(ohci->hcd.state)) - hc_reset (ohci); - else - writel (OHCI_INTR_MIE, &ohci->regs->intrdisable); + + ohci_usb_reset (ohci); + writel (OHCI_INTR_MIE, &ohci->regs->intrdisable); remove_debug_files (ohci); ohci_mem_cleanup (ohci); @@ -734,7 +780,7 @@ static void ohci_stop (struct usb_hcd *hcd) #if defined(CONFIG_USB_SUSPEND) || defined(CONFIG_PM) -static int hc_restart (struct ohci_hcd *ohci) +static int ohci_restart (struct ohci_hcd *ohci) { int temp; int i; @@ -791,7 +837,7 @@ static int hc_restart (struct ohci_hcd *ohci) ohci->ed_controltail = NULL; ohci->ed_bulktail = NULL; - if ((temp = hc_reset (ohci)) < 0 || (temp = hc_start (ohci)) < 0) { + if ((temp = ohci_run (ohci)) < 0) { ohci_err (ohci, "can't restart, %d\n", temp); return temp; } else { @@ -803,10 +849,7 @@ static int hc_restart (struct ohci_hcd *ohci) while (i--) writel (RH_PS_PSS, &ohci->regs->roothub.portstatus [temp]); - ohci->hcd.self.root_hub->dev.power.power_state = 0; - ohci->hcd.state = USB_STATE_RUNNING; ohci_dbg (ohci, "restart complete\n"); - ohci_dump (ohci, 1); } return 0; } @@ -843,3 +886,13 @@ MODULE_LICENSE ("GPL"); ) #error "missing bus glue for ohci-hcd" #endif + +#if !defined(HAVE_HNP) && defined(CONFIG_USB_OTG) + +#warning non-OTG configuration, too many HCDs + +static void start_hnp(struct ohci_hcd *ohci) +{ + /* "can't happen" */ +} +#endif diff --git a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c index 11093ed39cb1..1f21cf815ad4 100644 --- a/drivers/usb/host/ohci-hub.c +++ b/drivers/usb/host/ohci-hub.c @@ -2,7 +2,7 @@ * OHCI HCD (Host Controller Driver) for USB. * * (C) Copyright 1999 Roman Weissgaerber - * (C) Copyright 2000-2002 David Brownell + * (C) Copyright 2000-2004 David Brownell * * This file is licenced under GPL */ @@ -11,34 +11,8 @@ /* * OHCI Root Hub ... the nonsharable stuff - * - * Registers don't need cpu_to_le32, that happens transparently */ -/* AMD-756 (D2 rev) reports corrupt register contents in some cases. - * The erratum (#4) description is incorrect. AMD's workaround waits - * till some bits (mostly reserved) are clear; ok for all revs. - */ -#define read_roothub(hc, register, mask) ({ \ - u32 temp = ohci_readl (&hc->regs->roothub.register); \ - if (temp == -1) \ - disable (hc); \ - else if (hc->flags & OHCI_QUIRK_AMD756) \ - while (temp & mask) \ - temp = ohci_readl (&hc->regs->roothub.register); \ - temp; }) - -static u32 roothub_a (struct ohci_hcd *hc) - { return read_roothub (hc, a, 0xfc0fe000); } -static inline u32 roothub_b (struct ohci_hcd *hc) - { return ohci_readl (&hc->regs->roothub.b); } -static inline u32 roothub_status (struct ohci_hcd *hc) - { return ohci_readl (&hc->regs->roothub.status); } -static u32 roothub_portstatus (struct ohci_hcd *hc, int i) - { return read_roothub (hc, portstatus [i], 0xffe0fce0); } - -/*-------------------------------------------------------------------------*/ - #define dbg_port(hc,label,num,value) \ ohci_dbg (hc, \ "%s roothub.portstatus [%d] " \ @@ -150,7 +124,7 @@ succeed: * hub from the non-usb side (PCI, SOC, etc) stopped */ root->dev.power.power_state = 3; - root->state = USB_STATE_SUSPENDED; + usb_set_device_state (root, USB_STATE_SUSPENDED); done: spin_unlock_irq (&ohci->lock); return status; @@ -164,8 +138,6 @@ static inline struct ed *find_head (struct ed *ed) return ed; } -static int hc_restart (struct ohci_hcd *ohci); - /* caller owns root->serialize */ static int ohci_hub_resume (struct usb_hcd *hcd) { @@ -181,7 +153,12 @@ static int ohci_hub_resume (struct usb_hcd *hcd) spin_lock_irq (&ohci->lock); ohci->hc_control = ohci_readl (&ohci->regs->control); - switch (ohci->hc_control & OHCI_CTRL_HCFS) { + if (ohci->hc_control & (OHCI_CTRL_IR | OHCI_SCHED_ENABLES)) { + /* this can happen after suspend-to-disk */ + ohci_dbg (ohci, "BIOS/SMM active, control %03x\n", + ohci->hc_control); + status = -EBUSY; + } else switch (ohci->hc_control & OHCI_CTRL_HCFS) { case OHCI_USB_SUSPEND: ohci->hc_control &= ~(OHCI_CTRL_HCFS|OHCI_SCHED_ENABLES); ohci->hc_control |= OHCI_USB_RESUME; @@ -203,8 +180,10 @@ static int ohci_hub_resume (struct usb_hcd *hcd) status = -EBUSY; } spin_unlock_irq (&ohci->lock); - if (status == -EBUSY) - return hc_restart (ohci); + if (status == -EBUSY) { + (void) ohci_init (ohci); + return ohci_restart (ohci); + } if (status != -EINPROGRESS) return status; @@ -261,6 +240,7 @@ static int ohci_hub_resume (struct usb_hcd *hcd) /* TRSMRCY */ msleep (10); root->dev.power.power_state = 0; + usb_set_device_state (root, USB_STATE_CONFIGURED); /* keep it alive for ~5x suspend + resume costs */ ohci->next_statechange = jiffies + msecs_to_jiffies (250); diff --git a/drivers/usb/host/ohci-lh7a404.c b/drivers/usb/host/ohci-lh7a404.c index 4e11a8eae481..1594d1e635da 100644 --- a/drivers/usb/host/ohci-lh7a404.c +++ b/drivers/usb/host/ohci-lh7a404.c @@ -229,38 +229,14 @@ ohci_lh7a404_start (struct usb_hcd *hcd) int ret; ohci_dbg (ohci, "ohci_lh7a404_start, ohci:%p", ohci); - - ohci->hcca = dma_alloc_coherent (hcd->self.controller, - sizeof *ohci->hcca, &ohci->hcca_dma, 0); - if (!ohci->hcca) - return -ENOMEM; - - ohci_dbg (ohci, "ohci_lh7a404_start, ohci->hcca:%p", - ohci->hcca); - - memset (ohci->hcca, 0, sizeof (struct ohci_hcca)); - - if ((ret = ohci_mem_init (ohci)) < 0) { - ohci_stop (hcd); + if ((ret = ohci_init(ohci)) < 0) return ret; - } - ohci->regs = hcd->regs; - - if (hc_reset (ohci) < 0) { - ohci_stop (hcd); - return -ENODEV; - } - if (hc_start (ohci) < 0) { + if ((ret = ohci_run (ohci)) < 0) { err ("can't start %s", ohci->hcd.self.bus_name); ohci_stop (hcd); - return -EBUSY; + return ret; } - create_debug_files (ohci); - -#ifdef DEBUG - ohci_dump (ohci, 1); -#endif /*DEBUG*/ return 0; } diff --git a/drivers/usb/host/ohci-omap.c b/drivers/usb/host/ohci-omap.c index d133ff22a4a7..a8e641f595bb 100644 --- a/drivers/usb/host/ohci-omap.c +++ b/drivers/usb/host/ohci-omap.c @@ -428,40 +428,18 @@ ohci_omap_start (struct usb_hcd *hcd) struct ohci_hcd *ohci = hcd_to_ohci (hcd); int ret; - config = hcd->self.controller->platform_data; - ohci->hcca = dma_alloc_coherent (hcd->self.controller, - sizeof *ohci->hcca, &ohci->hcca_dma, 0); - if (!ohci->hcca) - return -ENOMEM; - - memset (ohci->hcca, 0, sizeof (struct ohci_hcca)); - if ((ret = ohci_mem_init (ohci)) < 0) { - ohci_stop (hcd); + if ((ret = ohci_init(ohci)) < 0) return ret; - } - ohci->regs = hcd->regs; + config = hcd->self.controller->platform_data; if (config->otg || config->rwc) writel(OHCI_CTRL_RWC, &ohci->regs->control); - if (hc_reset (ohci) < 0) { - ohci_stop (hcd); - return -ENODEV; - } - - if (hc_start (ohci) < 0) { + if ((ret = ohci_run (ohci)) < 0) { err ("can't start %s", ohci->hcd.self.bus_name); ohci_stop (hcd); - return -EBUSY; + return ret; } - if (ohci->power_budget) - hub_set_power_budget(ohci->hcd.self.root_hub, - ohci->power_budget); - create_debug_files (ohci); - -#ifdef DEBUG - ohci_dump (ohci, 1); -#endif return 0; } diff --git a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c index bf0c5d8ec231..b1dbea9e4b23 100644 --- a/drivers/usb/host/ohci-pci.c +++ b/drivers/usb/host/ohci-pci.c @@ -35,9 +35,7 @@ ohci_pci_reset (struct usb_hcd *hcd) { struct ohci_hcd *ohci = hcd_to_ohci (hcd); - ohci->regs = hcd->regs; - ohci->next_statechange = jiffies; - return hc_reset (ohci); + return ohci_init (ohci); } static int __devinit @@ -46,11 +44,6 @@ ohci_pci_start (struct usb_hcd *hcd) struct ohci_hcd *ohci = hcd_to_ohci (hcd); int ret; - ohci->hcca = dma_alloc_coherent (hcd->self.controller, - sizeof *ohci->hcca, &ohci->hcca_dma, 0); - if (!ohci->hcca) - return -ENOMEM; - if(hcd->self.controller && hcd->self.controller->bus == &pci_bus_type) { struct pci_dev *pdev = to_pci_dev(hcd->self.controller); @@ -104,31 +97,14 @@ ohci_pci_start (struct usb_hcd *hcd) } - memset (ohci->hcca, 0, sizeof (struct ohci_hcca)); - if ((ret = ohci_mem_init (ohci)) < 0) { - ohci_stop (hcd); - return ret; - } - - /* NOTE: this is a second reset. the first one helps - * keep bios/smm irqs from making trouble, but it was - * probably more than 1msec ago... + /* NOTE: there may have already been a first reset, to + * keep bios/smm irqs from making trouble */ - if (hc_reset (ohci) < 0) { - ohci_stop (hcd); - return -ENODEV; - } - - if (hc_start (ohci) < 0) { + if ((ret = ohci_run (ohci)) < 0) { ohci_err (ohci, "can't start\n"); ohci_stop (hcd); - return -EBUSY; + return ret; } - create_debug_files (ohci); - -#ifdef DEBUG - ohci_dump (ohci, 1); -#endif return 0; } diff --git a/drivers/usb/host/ohci-sa1111.c b/drivers/usb/host/ohci-sa1111.c index c3cd76aba163..35d71aceff63 100644 --- a/drivers/usb/host/ohci-sa1111.c +++ b/drivers/usb/host/ohci-sa1111.c @@ -272,33 +272,14 @@ ohci_sa1111_start (struct usb_hcd *hcd) struct ohci_hcd *ohci = hcd_to_ohci (hcd); int ret; - ohci->hcca = dma_alloc_coherent (hcd->self.controller, - sizeof *ohci->hcca, &ohci->hcca_dma, 0); - if (!ohci->hcca) - return -ENOMEM; - - memset (ohci->hcca, 0, sizeof (struct ohci_hcca)); - if ((ret = ohci_mem_init (ohci)) < 0) { - ohci_stop (hcd); + if ((ret = ohci_init(ohci)) < 0) return ret; - } - ohci->regs = hcd->regs; - - if (hc_reset (ohci) < 0) { - ohci_stop (hcd); - return -ENODEV; - } - if (hc_start (ohci) < 0) { + if ((ret = ohci_run (ohci)) < 0) { err ("can't start %s", ohci->hcd.self.bus_name); ohci_stop (hcd); - return -EBUSY; + return ret; } - create_debug_files (ohci); - -#ifdef DEBUG - ohci_dump (ohci, 1); -#endif return 0; } diff --git a/drivers/usb/host/ohci.h b/drivers/usb/host/ohci.h index 079bbc1b00c9..6a3fa725ef4a 100644 --- a/drivers/usb/host/ohci.h +++ b/drivers/usb/host/ohci.h @@ -42,7 +42,6 @@ struct ed { /* create --> IDLE --> OPER --> ... --> IDLE --> destroy * usually: OPER --> UNLINK --> (IDLE | OPER) --> ... - * some special cases : OPER --> IDLE ... */ u8 state; /* ED_{IDLE,UNLINK,OPER} */ #define ED_IDLE 0x00 /* NOT linked to HC */ @@ -406,15 +405,14 @@ static inline void disable (struct ohci_hcd *ohci) } #define FI 0x2edf /* 12000 bits per frame (-1) */ -#define FSMP(fi) ((6 * ((fi) - 210)) / 7) +#define FSMP(fi) (0x7fff & ((6 * ((fi) - 210)) / 7)) #define LSTHRESH 0x628 /* lowspeed bit threshold */ static inline void periodic_reinit (struct ohci_hcd *ohci) { u32 fi = ohci->fminterval & 0x0ffff; - writel (ohci->fminterval, &ohci->regs->fminterval); + writel (((9 * fi) / 10) & 0x3fff, &ohci->regs->periodicstart); - writel (LSTHRESH, &ohci->regs->lsthresh); } /*-------------------------------------------------------------------------*/ @@ -438,6 +436,8 @@ static inline void periodic_reinit (struct ohci_hcd *ohci) # define ohci_vdbg(ohci, fmt, args...) do { } while (0) #endif +/*-------------------------------------------------------------------------*/ + #ifdef CONFIG_ARCH_LH7A404 /* Marc Singer: at the time this code was written, the LH7A404 * had a problem reading the USB host registers. This @@ -457,3 +457,25 @@ static inline unsigned int ohci_readl (void* regs) return readl (regs); } #endif + +/* AMD-756 (D2 rev) reports corrupt register contents in some cases. + * The erratum (#4) description is incorrect. AMD's workaround waits + * till some bits (mostly reserved) are clear; ok for all revs. + */ +#define read_roothub(hc, register, mask) ({ \ + u32 temp = ohci_readl (&hc->regs->roothub.register); \ + if (temp == -1) \ + disable (hc); \ + else if (hc->flags & OHCI_QUIRK_AMD756) \ + while (temp & mask) \ + temp = ohci_readl (&hc->regs->roothub.register); \ + temp; }) + +static u32 roothub_a (struct ohci_hcd *hc) + { return read_roothub (hc, a, 0xfc0fe000); } +static inline u32 roothub_b (struct ohci_hcd *hc) + { return ohci_readl (&hc->regs->roothub.b); } +static inline u32 roothub_status (struct ohci_hcd *hc) + { return ohci_readl (&hc->regs->roothub.status); } +static u32 roothub_portstatus (struct ohci_hcd *hc, int i) + { return read_roothub (hc, portstatus [i], 0xffe0fce0); } -- cgit v1.2.3 From 11c763cc105faf3f3148fa209b87ded8bb1fa07e Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Mon, 13 Sep 2004 20:33:44 -0700 Subject: [PATCH] USB: Updated USB device locking This patch reintroduces the USB device locking code we tried out earlier. As before, it solves the problem of effectively locking all the devices while drivers are registered and unregistered by introducing an rwsem. Unlike the earlier attempt, this version does not ever try to acquire a lock re-entrantly. I trust that will eliminate the races and hang-ups you observed with the earlier version. There are also copious comments explaining exactly how things should work. The patch interacts slightly with the locktree() code introduced by David for suspend/resume support. It doesn't change the functionality at all; it just updates the routine to follow the new locking rules. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/devices.c | 6 +- drivers/usb/core/devio.c | 38 ++++++------ drivers/usb/core/hcd.c | 4 +- drivers/usb/core/hub.c | 93 ++++++++++++++++------------- drivers/usb/core/message.c | 16 ++--- drivers/usb/core/sysfs.c | 4 +- drivers/usb/core/usb.c | 138 +++++++++++++++++++++++++++++++++++++++++--- drivers/usb/core/usb.h | 2 + drivers/usb/host/ehci-hub.c | 2 +- drivers/usb/host/ohci-hub.c | 10 ++-- drivers/usb/host/ohci-pci.c | 8 +-- include/linux/usb.h | 12 ++++ 12 files changed, 242 insertions(+), 91 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c index 195e365675af..50009ed51e8d 100644 --- a/drivers/usb/core/devices.c +++ b/drivers/usb/core/devices.c @@ -451,7 +451,7 @@ static char *usb_dump_string(char *start, char *end, const struct usb_device *de * nbytes - the maximum number of bytes to write * skip_bytes - the number of bytes to skip before writing anything * file_offset - the offset into the devices file on completion - * The caller must own the usbdev->serialize semaphore. + * The caller must own the device lock. */ static ssize_t usb_device_dump(char __user **buffer, size_t *nbytes, loff_t *skip_bytes, loff_t *file_offset, struct usb_device *usbdev, struct usb_bus *bus, int level, int index, int count) @@ -586,9 +586,9 @@ static ssize_t usb_device_read(struct file *file, char __user *buf, size_t nbyte /* recurse through all children of the root hub */ if (!bus->root_hub) continue; - down(&bus->root_hub->serialize); + usb_lock_device(bus->root_hub); ret = usb_device_dump(&buf, &nbytes, &skip_bytes, ppos, bus->root_hub, bus, 0, 0, 0); - up(&bus->root_hub->serialize); + usb_unlock_device(bus->root_hub); if (ret < 0) { up(&usb_bus_list_lock); return ret; diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 0b3bf6634ee4..c3e76e465c2b 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -113,7 +113,7 @@ static ssize_t usbdev_read(struct file *file, char __user *buf, size_t nbytes, l int i; pos = *ppos; - down(&dev->serialize); + usb_lock_device(dev); if (!connected(dev)) { ret = -ENODEV; goto err; @@ -175,7 +175,7 @@ static ssize_t usbdev_read(struct file *file, char __user *buf, size_t nbytes, l } err: - up(&dev->serialize); + usb_unlock_device(dev); return ret; } @@ -517,7 +517,7 @@ static int usbdev_release(struct inode *inode, struct file *file) struct usb_device *dev = ps->dev; unsigned int ifnum; - down(&dev->serialize); + usb_lock_device(dev); list_del_init(&ps->list); if (connected(dev)) { @@ -526,7 +526,7 @@ static int usbdev_release(struct inode *inode, struct file *file) releaseintf(ps, ifnum); destroy_all_async(ps); } - up(&dev->serialize); + usb_unlock_device(dev); usb_put_dev(dev); ps->dev = NULL; kfree(ps); @@ -558,10 +558,10 @@ static int proc_control(struct dev_state *ps, void __user *arg) snoop(&dev->dev, "control read: bRequest=%02x bRrequestType=%02x wValue=%04x wIndex=%04x\n", ctrl.bRequest, ctrl.bRequestType, ctrl.wValue, ctrl.wIndex); - up(&dev->serialize); + usb_unlock_device(dev); i = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), ctrl.bRequest, ctrl.bRequestType, ctrl.wValue, ctrl.wIndex, tbuf, ctrl.wLength, tmo); - down(&dev->serialize); + usb_lock_device(dev); if ((i > 0) && ctrl.wLength) { if (usbfs_snoop) { dev_info(&dev->dev, "control read: data "); @@ -589,10 +589,10 @@ static int proc_control(struct dev_state *ps, void __user *arg) printk ("%02x ", (unsigned char)(tbuf)[j]); printk("\n"); } - up(&dev->serialize); + usb_unlock_device(dev); i = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), ctrl.bRequest, ctrl.bRequestType, ctrl.wValue, ctrl.wIndex, tbuf, ctrl.wLength, tmo); - down(&dev->serialize); + usb_lock_device(dev); } free_page((unsigned long)tbuf); if (i<0) { @@ -636,9 +636,9 @@ static int proc_bulk(struct dev_state *ps, void __user *arg) kfree(tbuf); return -EINVAL; } - up(&dev->serialize); + usb_unlock_device(dev); i = usb_bulk_msg(dev, pipe, tbuf, len1, &len2, tmo); - down(&dev->serialize); + usb_lock_device(dev); if (!i && len2) { if (copy_to_user(bulk.data, tbuf, len2)) { kfree(tbuf); @@ -652,9 +652,9 @@ static int proc_bulk(struct dev_state *ps, void __user *arg) return -EFAULT; } } - up(&dev->serialize); + usb_unlock_device(dev); i = usb_bulk_msg(dev, pipe, tbuf, len1, &len2, tmo); - down(&dev->serialize); + usb_lock_device(dev); } kfree(tbuf); if (i < 0) { @@ -1025,9 +1025,9 @@ static int proc_reapurb(struct dev_state *ps, void __user *arg) break; if (signal_pending(current)) break; - up(&dev->serialize); + usb_unlock_device(dev); schedule(); - down(&dev->serialize); + usb_lock_device(dev); } remove_wait_queue(&ps->wait, &wait); set_current_state(TASK_RUNNING); @@ -1150,7 +1150,11 @@ static int proc_ioctl (struct dev_state *ps, void __user *arg) /* let kernel drivers try to (re)bind to the interface */ case USBDEVFS_CONNECT: + usb_unlock_device(ps->dev); + usb_lock_all_devices(); bus_rescan_devices(intf->dev.bus); + usb_unlock_all_devices(); + usb_lock_device(ps->dev); break; /* talk directly to the interface's driver */ @@ -1193,9 +1197,9 @@ static int usbdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd if (!(file->f_mode & FMODE_WRITE)) return -EPERM; - down(&dev->serialize); + usb_lock_device(dev); if (!connected(dev)) { - up(&dev->serialize); + usb_unlock_device(dev); return -ENODEV; } @@ -1295,7 +1299,7 @@ static int usbdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd ret = proc_ioctl(ps, p); break; } - up(&dev->serialize); + usb_unlock_device(dev); if (ret >= 0) inode->i_atime = CURRENT_TIME; return ret; diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index c1de465a10d1..ed3e723cc9c5 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -797,9 +797,9 @@ int usb_register_root_hub (struct usb_device *usb_dev, struct device *parent_dev return (retval < 0) ? retval : -EMSGSIZE; } - down (&usb_dev->serialize); + usb_lock_device (usb_dev); retval = usb_new_device (usb_dev); - up (&usb_dev->serialize); + usb_unlock_device (usb_dev); if (retval) { usb_dev->bus->root_hub = NULL; dev_err (parent_dev, "can't register root hub for %s, %d\n", diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 29bba5e78da3..36ef3db882a2 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -36,7 +36,9 @@ #include "hcd.h" #include "hub.h" -/* Protect struct usb_device state and children members */ +/* Protect struct usb_device->state and ->children members + * Note: Both are also protected by ->serialize, except that ->state can + * change to USB_STATE_NOTATTACHED even when the semaphore isn't held. */ static spinlock_t device_state_lock = SPIN_LOCK_UNLOCKED; /* khubd's worklist and its lock */ @@ -857,17 +859,6 @@ static void hub_start_disconnect(struct usb_device *hdev) } -static void recursively_mark_NOTATTACHED(struct usb_device *udev) -{ - int i; - - for (i = 0; i < udev->maxchild; ++i) { - if (udev->children[i]) - recursively_mark_NOTATTACHED(udev->children[i]); - } - udev->state = USB_STATE_NOTATTACHED; -} - /* grab device/port lock, returning index of that port (zero based). * protects the upstream link used by this device from concurrent * tree operations like suspend, resume, reset, and disconnect, which @@ -884,21 +875,16 @@ static int locktree(struct usb_device *udev) /* root hub is always the first lock in the series */ hdev = udev->parent; if (!hdev) { - down(&udev->serialize); + usb_lock_device(udev); return 0; } /* on the path from root to us, lock everything from * top down, dropping parent locks when not needed - * - * NOTE: if disconnect were to ignore the locking, we'd need - * to get extra refcounts to everything since hdev->children - * and udev->parent could be invalidated while we work... */ t = locktree(hdev); if (t < 0) return t; - spin_lock_irq(&device_state_lock); for (t = 0; t < hdev->maxchild; t++) { if (hdev->children[t] == udev) { /* everything is fail-fast once disconnect @@ -910,33 +896,45 @@ static int locktree(struct usb_device *udev) /* when everyone grabs locks top->bottom, * non-overlapping work may be concurrent */ - spin_unlock_irq(&device_state_lock); down(&udev->serialize); up(&hdev->serialize); return t; } } - spin_unlock_irq(&device_state_lock); - up(&hdev->serialize); + usb_unlock_device(hdev); return -ENODEV; } +static void recursively_mark_NOTATTACHED(struct usb_device *udev) +{ + int i; + + for (i = 0; i < udev->maxchild; ++i) { + if (udev->children[i]) + recursively_mark_NOTATTACHED(udev->children[i]); + } + udev->state = USB_STATE_NOTATTACHED; +} + /** * usb_set_device_state - change a device's current state (usbcore, hcds) * @udev: pointer to device whose state should be changed * @new_state: new state value to be stored * - * udev->state is _not_ protected by the device lock. This + * udev->state is _not_ fully protected by the device lock. Although + * most transitions are made only while holding the lock, the state can + * can change to USB_STATE_NOTATTACHED at almost any time. This * is so that devices can be marked as disconnected as soon as possible, - * without having to wait for the semaphore to be released. Instead, - * changes to the state must be protected by the device_state_lock spinlock. + * without having to wait for any semaphores to be released. As a result, + * all changes to any device's state must be protected by the + * device_state_lock spinlock. * * Once a device has been added to the device tree, all changes to its state * should be made using this routine. The state should _not_ be set directly. * * If udev->state is already USB_STATE_NOTATTACHED then no change is made. * Otherwise udev->state is set to new_state, and if new_state is - * USB_STATE_NOTATTACHED then all of udev's descendant's states are also set + * USB_STATE_NOTATTACHED then all of udev's descendants' states are also set * to USB_STATE_NOTATTACHED. */ void usb_set_device_state(struct usb_device *udev, @@ -987,11 +985,12 @@ static void release_address(struct usb_device *udev) /** * usb_disconnect - disconnect a device (usbcore-internal) - * @pdev: pointer to device being disconnected, into a locked hub + * @pdev: pointer to device being disconnected * Context: !in_interrupt () * - * Something got disconnected. Get rid of it, and all of its children. - * If *pdev is a normal device then the parent hub should be locked. + * Something got disconnected. Get rid of it and all of its children. + * + * If *pdev is a normal device then the parent hub must already be locked. * If *pdev is a root hub then this routine will acquire the * usb_bus_list_lock on behalf of the caller. * @@ -1017,9 +1016,11 @@ void usb_disconnect(struct usb_device **pdev) usb_set_device_state(udev, USB_STATE_NOTATTACHED); /* lock the bus list on behalf of HCDs unregistering their root hubs */ - if (!udev->parent) + if (!udev->parent) { down(&usb_bus_list_lock); - down(&udev->serialize); + usb_lock_device(udev); + } else + down(&udev->serialize); dev_info (&udev->dev, "USB disconnect, address %d\n", udev->devnum); @@ -1044,14 +1045,16 @@ void usb_disconnect(struct usb_device **pdev) usbfs_remove_device(udev); usb_remove_sysfs_dev_files(udev); - /* Avoid races with recursively_mark_NOTATTACHED() and locktree() */ + /* Avoid races with recursively_mark_NOTATTACHED() */ spin_lock_irq(&device_state_lock); *pdev = NULL; spin_unlock_irq(&device_state_lock); - up(&udev->serialize); - if (!udev->parent) + if (!udev->parent) { + usb_unlock_device(udev); up(&usb_bus_list_lock); + } else + up(&udev->serialize); device_unregister(&udev->dev); } @@ -1516,16 +1519,14 @@ static int __usb_suspend_device (struct usb_device *udev, int port, u32 state) { int status; + /* caller owns the udev device lock */ if (port < 0) return port; - /* NOTE: udev->serialize released on all real returns! */ - if (state <= udev->dev.power.power_state || state < PM_SUSPEND_MEM || udev->state == USB_STATE_SUSPENDED || udev->state == USB_STATE_NOTATTACHED) { - up(&udev->serialize); return 0; } @@ -1605,7 +1606,6 @@ static int __usb_suspend_device (struct usb_device *udev, int port, u32 state) if (status == 0) udev->dev.power.power_state = state; - up(&udev->serialize); return status; } @@ -1629,7 +1629,15 @@ static int __usb_suspend_device (struct usb_device *udev, int port, u32 state) */ int usb_suspend_device(struct usb_device *udev, u32 state) { - return __usb_suspend_device(udev, locktree(udev), state); + int port, status; + + port = locktree(udev); + if (port < 0) + return port; + + status = __usb_suspend_device(udev, port, state); + usb_unlock_device(udev); + return status; } /* @@ -1642,7 +1650,7 @@ static int finish_port_resume(struct usb_device *udev) int status; u16 devstatus; - /* caller owns udev->serialize */ + /* caller owns the udev device lock */ dev_dbg(&udev->dev, "usb resume\n"); udev->dev.power.power_state = PM_SUSPEND_ON; @@ -1822,10 +1830,12 @@ int usb_resume_device(struct usb_device *udev) status); } - up(&udev->serialize); + usb_unlock_device(udev); /* rebind drivers that had no suspend() */ + usb_lock_all_devices(); bus_rescan_devices(&usb_bus_type); + usb_unlock_all_devices(); return status; } @@ -1867,6 +1877,7 @@ static int hub_suspend(struct usb_interface *intf, u32 state) continue; down(&udev->serialize); status = __usb_suspend_device(udev, port, state); + up(&udev->serialize); if (status < 0) dev_dbg(&intf->dev, "suspend port %d --> %d\n", port, status); @@ -2595,7 +2606,7 @@ static void hub_events(void) } loop: - up(&hdev->serialize); + usb_unlock_device(hdev); usb_put_dev(hdev); } /* end while (1) */ diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index 98695c68d687..e20dde5d793a 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -1132,6 +1132,8 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate) * use usb_set_interface() on the interfaces it claims. Resetting the whole * configuration would affect other drivers' interfaces. * + * The caller must own the device lock. + * * Returns zero on success, else a negative error code. */ int usb_reset_configuration(struct usb_device *dev) @@ -1142,9 +1144,9 @@ int usb_reset_configuration(struct usb_device *dev) if (dev->state == USB_STATE_SUSPENDED) return -EHOSTUNREACH; - /* caller must own dev->serialize (config won't change) - * and the usb bus readlock (so driver bindings are stable); - * so calls during probe() are fine + /* caller must have locked the device and must own + * the usb bus readlock (so driver bindings are stable); + * calls during probe() are fine */ for (i = 1; i < 16; ++i) { @@ -1199,7 +1201,7 @@ static void release_interface(struct device *dev) * usb_set_configuration - Makes a particular device setting be current * @dev: the device whose configuration is being updated * @configuration: the configuration being chosen. - * Context: !in_interrupt(), caller holds dev->serialize + * Context: !in_interrupt(), caller owns the device lock * * This is used to enable non-default device modes. Not all devices * use this kind of configurability; many devices only have one @@ -1220,8 +1222,8 @@ static void release_interface(struct device *dev) * usb_set_interface(). * * This call is synchronous. The calling context must be able to sleep, - * and must not hold the driver model lock for USB; usb device driver - * probe() methods may not use this routine. + * must own the device lock, and must not hold the driver model's USB + * bus rwsem; usb device driver probe() methods cannot use this routine. * * Returns zero on success, or else the status code returned by the * underlying call that failed. On succesful completion, each interface @@ -1236,8 +1238,6 @@ int usb_set_configuration(struct usb_device *dev, int configuration) struct usb_interface **new_interfaces = NULL; int n, nintf; - /* dev->serialize guards all config changes */ - for (i = 0; i < dev->descriptor.bNumConfigurations; i++) { if (dev->config[i].desc.bConfigurationValue == configuration) { cp = &dev->config[i]; diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index 78c5ca2f1051..007baee22013 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c @@ -55,9 +55,9 @@ set_bConfigurationValue (struct device *dev, const char *buf, size_t count) if (sscanf (buf, "%u", &config) != 1 || config > 255) return -EINVAL; - down(&udev->serialize); + usb_lock_device(udev); value = usb_set_configuration (udev, config); - up(&udev->serialize); + usb_unlock_device(udev); return (value < 0) ? value : count; } diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index de2edfb08582..93e2000043e4 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -39,6 +39,7 @@ #include #include #include +#include #include #include @@ -62,6 +63,8 @@ const char *usbcore_name = "usbcore"; int nousb; /* Disable USB when built into kernel image */ /* Not honored on modular build */ +static DECLARE_RWSEM(usb_all_devices_rwsem); + static int generic_probe (struct device *dev) { @@ -151,7 +154,9 @@ int usb_register(struct usb_driver *new_driver) new_driver->driver.probe = usb_probe_interface; new_driver->driver.remove = usb_unbind_interface; + usb_lock_all_devices(); retval = driver_register(&new_driver->driver); + usb_unlock_all_devices(); if (!retval) { pr_info("%s: registered new driver %s\n", @@ -180,7 +185,9 @@ void usb_deregister(struct usb_driver *driver) { pr_info("%s: deregistering driver %s\n", usbcore_name, driver->name); + usb_lock_all_devices(); driver_unregister (&driver->driver); + usb_unlock_all_devices(); usbfs_update_special(); } @@ -202,7 +209,7 @@ void usb_deregister(struct usb_driver *driver) * alternate settings available for this interfaces. * * Don't call this function unless you are bound to one of the interfaces - * on this device or you own the dev->serialize semaphore! + * on this device or you have locked the device! */ struct usb_interface *usb_ifnum_to_if(struct usb_device *dev, unsigned ifnum) { @@ -235,7 +242,7 @@ struct usb_interface *usb_ifnum_to_if(struct usb_device *dev, unsigned ifnum) * drivers avoid such mistakes. * * Don't call this function unless you are bound to the intf interface - * or you own the device's ->serialize semaphore! + * or you have locked the device! */ struct usb_host_interface *usb_altnum_to_altsetting(struct usb_interface *intf, unsigned int altnum) @@ -303,11 +310,12 @@ usb_epnum_to_ep_desc(struct usb_device *dev, unsigned epnum) * way to bind to an interface is to return the private data from * the driver's probe() method. * - * Callers must own the driver model's usb bus writelock. So driver - * probe() entries don't need extra locking, but other call contexts - * may need to explicitly claim that lock. + * Callers must own the device lock and the driver model's usb_bus_type.subsys + * writelock. So driver probe() entries don't need extra locking, + * but other call contexts may need to explicitly claim those locks. */ -int usb_driver_claim_interface(struct usb_driver *driver, struct usb_interface *iface, void* priv) +int usb_driver_claim_interface(struct usb_driver *driver, + struct usb_interface *iface, void* priv) { struct device *dev = &iface->dev; @@ -336,8 +344,8 @@ int usb_driver_claim_interface(struct usb_driver *driver, struct usb_interface * * also causes the driver disconnect() method to be called. * * This call is synchronous, and may not be used in an interrupt context. - * Callers must own the usb_device serialize semaphore and the driver model's - * usb bus writelock. So driver disconnect() entries don't need extra locking, + * Callers must own the device lock and the driver model's usb_bus_type.subsys + * writelock. So driver disconnect() entries don't need extra locking, * but other call contexts may need to explicitly claim those locks. */ void usb_driver_release_interface(struct usb_driver *driver, @@ -830,6 +838,112 @@ void usb_put_intf(struct usb_interface *intf) put_device(&intf->dev); } + +/* USB device locking + * + * Although locking USB devices should be straightforward, it is + * complicated by the way the driver-model core works. When a new USB + * driver is registered or unregistered, the core will automatically + * probe or disconnect all matching interfaces on all USB devices while + * holding the USB subsystem writelock. There's no good way for us to + * tell which devices will be used or to lock them beforehand; our only + * option is to effectively lock all the USB devices. + * + * We do that by using a private rw-semaphore, usb_all_devices_rwsem. + * When locking an individual device you must first acquire the rwsem's + * readlock. When a driver is registered or unregistered the writelock + * must be held. These actions are encapsulated in the subroutines + * below, so all a driver needs to do is call usb_lock_device() and + * usb_unlock_device(). + * + * Complications arise when several devices are to be locked at the same + * time. Only hub-aware drivers that are part of usbcore ever have to + * do this; nobody else needs to worry about it. The problem is that + * usb_lock_device() must not be called to lock a second device since it + * would acquire the rwsem's readlock reentrantly, leading to deadlock if + * another thread was waiting for the writelock. The solution is simple: + * + * When locking more than one device, call usb_lock_device() + * to lock the first one. Lock the others by calling + * down(&udev->serialize) directly. + * + * When unlocking multiple devices, use up(&udev->serialize) + * to unlock all but the last one. Unlock the last one by + * calling usb_unlock_device(). + * + * When locking both a device and its parent, always lock the + * the parent first. + */ + +/** + * usb_lock_device - acquire the lock for a usb device structure + * @udev: device that's being locked + * + * Use this routine when you don't hold any other device locks; + * to acquire nested inner locks call down(&udev->serialize) directly. + * This is necessary for proper interaction with usb_lock_all_devices(). + */ +void usb_lock_device(struct usb_device *udev) +{ + down_read(&usb_all_devices_rwsem); + down(&udev->serialize); +} + +/** + * usb_trylock_device - attempt to acquire the lock for a usb device structure + * @udev: device that's being locked + * + * Don't use this routine if you already hold a device lock; + * use down_trylock(&udev->serialize) instead. + * This is necessary for proper interaction with usb_lock_all_devices(). + * + * Returns 1 if successful, 0 if contention. + */ +int usb_trylock_device(struct usb_device *udev) +{ + if (!down_read_trylock(&usb_all_devices_rwsem)) + return 0; + if (down_trylock(&udev->serialize)) { + up_read(&usb_all_devices_rwsem); + return 0; + } + return 1; +} + +/** + * usb_unlock_device - release the lock for a usb device structure + * @udev: device that's being unlocked + * + * Use this routine when releasing the only device lock you hold; + * to release inner nested locks call up(&udev->serialize) directly. + * This is necessary for proper interaction with usb_lock_all_devices(). + */ +void usb_unlock_device(struct usb_device *udev) +{ + up(&udev->serialize); + up_read(&usb_all_devices_rwsem); +} + +/** + * usb_lock_all_devices - acquire the lock for all usb device structures + * + * This is necessary when registering a new driver or probing a bus, + * since the driver-model core may try to use any usb_device. + */ +void usb_lock_all_devices(void) +{ + down_write(&usb_all_devices_rwsem); +} + +/** + * usb_unlock_all_devices - release the lock for all usb device structures + */ +void usb_unlock_all_devices(void) +{ + up_write(&usb_all_devices_rwsem); +} + + static struct usb_device *match_device(struct usb_device *dev, u16 vendor_id, u16 product_id) { @@ -851,8 +965,10 @@ static struct usb_device *match_device(struct usb_device *dev, /* look through all of the children of this device */ for (child = 0; child < dev->maxchild; ++child) { if (dev->children[child]) { + down(&dev->children[child]->serialize); ret_dev = match_device(dev->children[child], vendor_id, product_id); + up(&dev->children[child]->serialize); if (ret_dev) goto exit; } @@ -887,7 +1003,9 @@ struct usb_device *usb_find_device(u16 vendor_id, u16 product_id) bus = container_of(buslist, struct usb_bus, bus_list); if (!bus->root_hub) continue; + usb_lock_device(bus->root_hub); dev = match_device(bus->root_hub, vendor_id, product_id); + usb_unlock_device(bus->root_hub); if (dev) goto exit; } @@ -1373,6 +1491,10 @@ EXPORT_SYMBOL(usb_put_dev); EXPORT_SYMBOL(usb_get_dev); EXPORT_SYMBOL(usb_hub_tt_clear_buffer); +EXPORT_SYMBOL(usb_lock_device); +EXPORT_SYMBOL(usb_trylock_device); +EXPORT_SYMBOL(usb_unlock_device); + EXPORT_SYMBOL(usb_driver_claim_interface); EXPORT_SYMBOL(usb_driver_release_interface); EXPORT_SYMBOL(usb_match_id); diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index 8e9c923bff0b..30d2cf7dd762 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -22,6 +22,8 @@ extern int usb_get_device_descriptor(struct usb_device *dev, unsigned int size); extern int usb_set_configuration(struct usb_device *dev, int configuration); +extern void usb_lock_all_devices(void); +extern void usb_unlock_all_devices(void); /* for labeling diagnostics */ extern const char *usbcore_name; diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 438701e17946..4a23fbf1f7af 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -81,7 +81,7 @@ static int ehci_hub_suspend (struct usb_hcd *hcd) } -/* caller owns root->serialize, and should reset/reinit on error */ +/* caller has locked the root hub, and should reset/reinit on error */ static int ehci_hub_resume (struct usb_hcd *hcd) { struct ehci_hcd *ehci = hcd_to_ehci (hcd); diff --git a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c index 1f21cf815ad4..d7a28d8eefb9 100644 --- a/drivers/usb/host/ohci-hub.c +++ b/drivers/usb/host/ohci-hub.c @@ -138,7 +138,7 @@ static inline struct ed *find_head (struct ed *ed) return ed; } -/* caller owns root->serialize */ +/* caller has locked the root hub */ static int ohci_hub_resume (struct usb_hcd *hcd) { struct ohci_hcd *ohci = hcd_to_ohci (hcd); @@ -282,9 +282,9 @@ static void ohci_rh_resume (void *_hcd) { struct usb_hcd *hcd = _hcd; - down (&hcd->self.root_hub->serialize); + usb_lock_device (hcd->self.root_hub); (void) ohci_hub_resume (hcd); - up (&hcd->self.root_hub->serialize); + usb_unlock_device (hcd->self.root_hub); } #else @@ -362,12 +362,12 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf) && ((OHCI_CTRL_HCFS | OHCI_SCHED_ENABLES) & ohci->hc_control) == OHCI_USB_OPER - && down_trylock (&hcd->self.root_hub->serialize) == 0 + && usb_trylock_device (hcd->self.root_hub) ) { ohci_vdbg (ohci, "autosuspend\n"); (void) ohci_hub_suspend (&ohci->hcd); ohci->hcd.state = USB_STATE_RUNNING; - up (&hcd->self.root_hub->serialize); + usb_unlock_device (hcd->self.root_hub); } #endif diff --git a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c index b1dbea9e4b23..23f6b5f3fee5 100644 --- a/drivers/usb/host/ohci-pci.c +++ b/drivers/usb/host/ohci-pci.c @@ -121,9 +121,9 @@ static int ohci_pci_suspend (struct usb_hcd *hcd, u32 state) #ifdef CONFIG_USB_SUSPEND (void) usb_suspend_device (hcd->self.root_hub, state); #else - down (&hcd->self.root_hub->serialize); + usb_lock_device (hcd->self.root_hub); (void) ohci_hub_suspend (hcd); - up (&hcd->self.root_hub->serialize); + usb_unlock_device (hcd->self.root_hub); #endif /* let things settle down a bit */ @@ -169,9 +169,9 @@ static int ohci_pci_resume (struct usb_hcd *hcd) /* get extra cleanup even if remote wakeup isn't in use */ retval = usb_resume_device (hcd->self.root_hub); #else - down (&hcd->self.root_hub->serialize); + usb_lock_device (hcd->self.root_hub); retval = ohci_hub_resume (hcd); - up (&hcd->self.root_hub->serialize); + usb_unlock_device (hcd->self.root_hub); #endif if (retval == 0) { diff --git a/include/linux/usb.h b/include/linux/usb.h index d07d5aba9e7f..67a8d7bce43b 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -281,6 +281,14 @@ struct usb_bus { struct usb_tt; +/* + * struct usb_device - kernel's representation of a USB device + * + * FIXME: Write the kerneldoc! + * + * Usbcore drivers should not set usbdev->state directly. Instead use + * usb_set_device_state(). + */ struct usb_device { int devnum; /* Address on USB bus */ char devpath [16]; /* Use in messages: /port/port/... */ @@ -331,6 +339,10 @@ struct usb_device { extern struct usb_device *usb_get_dev(struct usb_device *dev); extern void usb_put_dev(struct usb_device *dev); +extern void usb_lock_device(struct usb_device *udev); +extern int usb_trylock_device(struct usb_device *udev); +extern void usb_unlock_device(struct usb_device *udev); + /* mostly for devices emulating SCSI over USB */ extern int usb_reset_device(struct usb_device *dev); extern int __usb_reset_device(struct usb_device *dev); -- cgit v1.2.3 From 7d8d0405f07a1777cad6898346127b478b68dd0e Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Mon, 13 Sep 2004 20:34:46 -0700 Subject: [PATCH] USB: Add locking support for USB device resets This patch reintroduces the usb_lock_device_for_reset() routine, which is specially tailored to meet the needs of drivers that have to reset a device either during probe() or during normal operations. It updates a few drivers that do device resets, to make them use the new routine. It also adds a new field to struct usb_interface, to keep track of whether the interface is in the process of being bound to or unbound from a driver. This is necessary, because during binding we know the device is already locked so we don't want to try to acquire the lock again! With this patch in place, USB device resets should finally become reliable. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/net/irda/stir4200.c | 10 ++++++++ drivers/usb/core/devio.c | 2 +- drivers/usb/core/hub.c | 23 +++++++---------- drivers/usb/core/usb.c | 57 ++++++++++++++++++++++++++++++++++++++++++ drivers/usb/image/microtek.c | 11 ++++++-- drivers/usb/image/microtek.h | 1 + drivers/usb/storage/scsiglue.c | 14 ++++++++--- include/linux/usb.h | 15 +++++++++-- 8 files changed, 111 insertions(+), 22 deletions(-) (limited to 'drivers') diff --git a/drivers/net/irda/stir4200.c b/drivers/net/irda/stir4200.c index 81282d94f91d..5f26d9ff30e9 100644 --- a/drivers/net/irda/stir4200.c +++ b/drivers/net/irda/stir4200.c @@ -168,6 +168,7 @@ enum StirTestMask { struct stir_cb { struct usb_device *usbdev; /* init: probe_irda */ + struct usb_interface *usbintf; struct net_device *netdev; /* network layer */ struct irlap_cb *irlap; /* The link layer we are binded to */ struct net_device_stats stats; /* network statistics */ @@ -508,6 +509,7 @@ static int change_speed(struct stir_cb *stir, unsigned speed) { int i, err; __u8 mode; + int rc; for (i = 0; i < ARRAY_SIZE(stir_modes); ++i) { if (speed == stir_modes[i].speed) @@ -521,7 +523,14 @@ static int change_speed(struct stir_cb *stir, unsigned speed) pr_debug("speed change from %d to %d\n", stir->speed, speed); /* sometimes needed to get chip out of stuck state */ + rc = usb_lock_device_for_reset(stir->usbdev, stir->usbintf); + if (rc < 0) { + err = rc; + goto out; + } err = usb_reset_device(stir->usbdev); + if (rc) + usb_unlock_device(stir->usbdev); if (err) goto out; @@ -1066,6 +1075,7 @@ static int stir_probe(struct usb_interface *intf, stir = net->priv; stir->netdev = net; stir->usbdev = dev; + stir->usbintf = intf; ret = usb_reset_configuration(dev); if (ret != 0) { diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index c3e76e465c2b..0e0ea0806606 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -735,7 +735,7 @@ static int proc_connectinfo(struct dev_state *ps, void __user *arg) static int proc_resetdevice(struct dev_state *ps) { - return __usb_reset_device(ps->dev); + return usb_reset_device(ps->dev); } diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 36ef3db882a2..848ab62e71a6 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -826,7 +826,7 @@ static int hub_reset(struct usb_hub *hub) else return -1; - if (__usb_reset_device(hdev)) + if (usb_reset_device(hdev)) return -1; hub->urb->dev = hdev; @@ -2759,8 +2759,10 @@ static int config_descriptors_changed(struct usb_device *udev) * * The caller must own the device lock. For example, it's safe to use * this from a driver probe() routine after downloading new firmware. + * For calls that might not occur during probe(), drivers should lock + * the device using usb_lock_device_for_reset(). */ -int __usb_reset_device(struct usb_device *udev) +int usb_reset_device(struct usb_device *udev) { struct usb_device *parent = udev->parent; struct usb_device_descriptor descriptor = udev->descriptor; @@ -2795,6 +2797,11 @@ int __usb_reset_device(struct usb_device *udev) return -ENOENT; } + /* ep0 maxpacket size may change; let the HCD know about it. + * Other endpoints will be handled by re-enumeration. */ + usb_disable_endpoint(udev, 0); + usb_disable_endpoint(udev, 0 + USB_DIR_IN); + ret = hub_port_init(parent, udev, port); if (ret < 0) goto re_enumerate; @@ -2848,15 +2855,3 @@ re_enumerate: hub_port_logical_disconnect(parent, port); return -ENODEV; } -EXPORT_SYMBOL(__usb_reset_device); - -int usb_reset_device(struct usb_device *udev) -{ - int r; - - down(&udev->serialize); - r = __usb_reset_device(udev); - up(&udev->serialize); - - return r; -} diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 93e2000043e4..c4c457975ae3 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -102,7 +102,10 @@ int usb_probe_interface(struct device *dev) id = usb_match_id (intf, driver->id_table); if (id) { dev_dbg (dev, "%s - got id\n", __FUNCTION__); + intf->condition = USB_INTERFACE_BINDING; error = driver->probe (intf, id); + intf->condition = error ? USB_INTERFACE_UNBOUND : + USB_INTERFACE_BOUND; } return error; @@ -114,6 +117,8 @@ int usb_unbind_interface(struct device *dev) struct usb_interface *intf = to_usb_interface(dev); struct usb_driver *driver = to_usb_driver(intf->dev.driver); + intf->condition = USB_INTERFACE_UNBINDING; + /* release all urbs for this interface */ usb_disable_interface(interface_to_usbdev(intf), intf); @@ -125,6 +130,7 @@ int usb_unbind_interface(struct device *dev) intf->altsetting[0].desc.bInterfaceNumber, 0); usb_set_intfdata(intf, NULL); + intf->condition = USB_INTERFACE_UNBOUND; return 0; } @@ -324,6 +330,7 @@ int usb_driver_claim_interface(struct usb_driver *driver, dev->driver = &driver->driver; usb_set_intfdata(iface, priv); + iface->condition = USB_INTERFACE_BOUND; /* if interface was already added, bind now; else let * the future device_add() bind it, bypassing probe() @@ -363,6 +370,7 @@ void usb_driver_release_interface(struct usb_driver *driver, dev->driver = NULL; usb_set_intfdata(iface, NULL); + iface->condition = USB_INTERFACE_UNBOUND; } /** @@ -910,6 +918,54 @@ int usb_trylock_device(struct usb_device *udev) return 1; } +/** + * usb_lock_device_for_reset - cautiously acquire the lock for a + * usb device structure + * @udev: device that's being locked + * @iface: interface bound to the driver making the request (optional) + * + * Attempts to acquire the device lock, but fails if the device is + * NOTATTACHED or SUSPENDED, or if iface is specified and the interface + * is neither BINDING nor BOUND. Rather than sleeping to wait for the + * lock, the routine polls repeatedly. This is to prevent deadlock with + * disconnect; in some drivers (such as usb-storage) the disconnect() + * callback will block waiting for a device reset to complete. + * + * Returns a negative error code for failure, otherwise 1 or 0 to indicate + * that the device will or will not have to be unlocked. (0 can be + * returned when an interface is given and is BINDING, because in that + * case the driver already owns the device lock.) + */ +int usb_lock_device_for_reset(struct usb_device *udev, + struct usb_interface *iface) +{ + if (udev->state == USB_STATE_NOTATTACHED) + return -ENODEV; + if (udev->state == USB_STATE_SUSPENDED) + return -EHOSTUNREACH; + if (iface) { + switch (iface->condition) { + case USB_INTERFACE_BINDING: + return 0; + case USB_INTERFACE_BOUND: + break; + default: + return -EINTR; + } + } + + while (!usb_trylock_device(udev)) { + msleep(15); + if (udev->state == USB_STATE_NOTATTACHED) + return -ENODEV; + if (udev->state == USB_STATE_SUSPENDED) + return -EHOSTUNREACH; + if (iface && iface->condition != USB_INTERFACE_BOUND) + return -EINTR; + } + return 1; +} + /** * usb_unlock_device - release the lock for a usb device structure * @udev: device that's being unlocked @@ -1493,6 +1549,7 @@ EXPORT_SYMBOL(usb_hub_tt_clear_buffer); EXPORT_SYMBOL(usb_lock_device); EXPORT_SYMBOL(usb_trylock_device); +EXPORT_SYMBOL(usb_lock_device_for_reset); EXPORT_SYMBOL(usb_unlock_device); EXPORT_SYMBOL(usb_driver_claim_interface); diff --git a/drivers/usb/image/microtek.c b/drivers/usb/image/microtek.c index 14b218239a8c..2a18c35629eb 100644 --- a/drivers/usb/image/microtek.c +++ b/drivers/usb/image/microtek.c @@ -341,12 +341,18 @@ static int mts_scsi_abort (Scsi_Cmnd *srb) static int mts_scsi_host_reset (Scsi_Cmnd *srb) { struct mts_desc* desc = (struct mts_desc*)(srb->device->host->hostdata[0]); + int result, rc; MTS_DEBUG_GOT_HERE(); mts_debug_dump(desc); - usb_reset_device(desc->usb_dev); /*FIXME: untested on new reset code */ - return 0; /* RANT why here 0 and not SUCCESS */ + rc = usb_lock_device_for_reset(desc->usb_dev, desc->usb_intf); + if (rc < 0) + return FAILED; + result = usb_reset_device(desc->usb_dev);; + if (rc) + usb_unlock_device(desc->usb_dev); + return result ? FAILED : SUCCESS; } static @@ -777,6 +783,7 @@ static int mts_usb_probe(struct usb_interface *intf, goto out_kfree; new_desc->usb_dev = dev; + new_desc->usb_intf = intf; init_MUTEX(&new_desc->lock); /* endpoints */ diff --git a/drivers/usb/image/microtek.h b/drivers/usb/image/microtek.h index 206994ddcedf..3271deb8c001 100644 --- a/drivers/usb/image/microtek.h +++ b/drivers/usb/image/microtek.h @@ -31,6 +31,7 @@ struct mts_desc { struct mts_desc *prev; struct usb_device *usb_dev; + struct usb_interface *usb_intf; /* Endpoint addresses */ u8 ep_out; diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c index 29f4e59759e6..83d7003861ff 100644 --- a/drivers/usb/storage/scsiglue.c +++ b/drivers/usb/storage/scsiglue.c @@ -283,7 +283,7 @@ static int device_reset(struct scsi_cmnd *srb) static int bus_reset(struct scsi_cmnd *srb) { struct us_data *us = (struct us_data *)srb->device->host->hostdata[0]; - int result; + int result, rc; US_DEBUGP("%s called\n", __FUNCTION__); if (us->sm_state != US_STATE_IDLE) { @@ -308,8 +308,16 @@ static int bus_reset(struct scsi_cmnd *srb) result = -EBUSY; US_DEBUGP("Refusing to reset a multi-interface device\n"); } else { - result = usb_reset_device(us->pusb_dev); - US_DEBUGP("usb_reset_device returns %d\n", result); + rc = usb_lock_device_for_reset(us->pusb_dev, us->pusb_intf); + if (rc < 0) { + US_DEBUGP("unable to lock device for reset: %d\n", rc); + result = rc; + } else { + result = usb_reset_device(us->pusb_dev); + if (rc) + usb_unlock_device(us->pusb_dev); + US_DEBUGP("usb_reset_device returns %d\n", result); + } } up(&(us->dev_semaphore)); diff --git a/include/linux/usb.h b/include/linux/usb.h index 67a8d7bce43b..18ee0751a32b 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -61,6 +61,13 @@ struct usb_host_interface { int extralen; }; +enum usb_interface_condition { + USB_INTERFACE_UNBOUND = 0, + USB_INTERFACE_BINDING, + USB_INTERFACE_BOUND, + USB_INTERFACE_UNBINDING, +}; + /** * struct usb_interface - what usb device drivers talk to * @altsetting: array of interface structures, one for each alternate @@ -75,6 +82,8 @@ struct usb_host_interface { * be unused. The driver should set this value in the probe() * function of the driver, after it has been assigned a minor * number from the USB core by calling usb_register_dev(). + * @condition: binding state of the interface: not bound, binding + * (in probe()), bound to a driver, or unbinding (in disconnect()) * @dev: driver model's view of this device * @class_dev: driver model's class view of this device. * @@ -113,6 +122,7 @@ struct usb_interface { unsigned num_altsetting; /* number of alternate settings */ int minor; /* minor number this interface is bound to */ + enum usb_interface_condition condition; /* state of binding */ struct device dev; /* interface specific device info */ struct class_device *class_dev; }; @@ -341,11 +351,12 @@ extern void usb_put_dev(struct usb_device *dev); extern void usb_lock_device(struct usb_device *udev); extern int usb_trylock_device(struct usb_device *udev); +extern int usb_lock_device_for_reset(struct usb_device *udev, + struct usb_interface *iface); extern void usb_unlock_device(struct usb_device *udev); -/* mostly for devices emulating SCSI over USB */ +/* USB port reset for device reinitialization */ extern int usb_reset_device(struct usb_device *dev); -extern int __usb_reset_device(struct usb_device *dev); extern struct usb_device *usb_find_device(u16 vendor_id, u16 product_id); -- cgit v1.2.3 From 5a2a6de557b8b4c98d40697da256a6533d2565cd Mon Sep 17 00:00:00 2001 From: Luca Risolia Date: Mon, 13 Sep 2004 20:51:00 -0700 Subject: [PATCH] USB: SN9C10x driver updates SN9C10x driver updates. Changes: (+ new, - removed, * cleanup, @ bugfix, = sync with kernels) @ Create correct red,green,blue entries under /sys according to the detected bridge * Add and use defined symbols for I2C slave ids of TAS5110C1B and TAS51130D1B * Color fixes for PAS202BCB - from its maintainer - Signed-off-by: Luca Risolia Signed-off-by: Greg Kroah-Hartman --- Documentation/usb/sn9c102.txt | 11 ++++++++--- drivers/usb/media/sn9c102.h | 8 ++++---- drivers/usb/media/sn9c102_core.c | 30 +++++++++++++++++++++++------- drivers/usb/media/sn9c102_pas202bcb.c | 9 +++++---- drivers/usb/media/sn9c102_sensor.h | 3 +++ drivers/usb/media/sn9c102_tas5110c1b.c | 4 ++-- drivers/usb/media/sn9c102_tas5130d1b.c | 4 ++-- 7 files changed, 47 insertions(+), 22 deletions(-) (limited to 'drivers') diff --git a/Documentation/usb/sn9c102.txt b/Documentation/usb/sn9c102.txt index ea4f4274b371..40c2f73a707f 100644 --- a/Documentation/usb/sn9c102.txt +++ b/Documentation/usb/sn9c102.txt @@ -123,12 +123,12 @@ analyze kernel messages and verify that the loading process has gone well: Module parameters are listed below: ------------------------------------------------------------------------------- Name: video_nr -Type: int array (min = 0, max = 32) +Type: int array (min = 0, max = 64) Syntax: <-1|n[,...]> Description: Specify V4L2 minor mode number: -1 = use next available n = use minor number n - You can specify up to 32 cameras this way. + You can specify up to 64 cameras this way. For example: video_nr=-1,2,-1 would assign minor number 2 to the second recognized camera and use auto for the first one and for every @@ -180,7 +180,9 @@ identifier - of the camera registered as "/dev/video0": [root@localhost #] echo 1 > i2c_reg [root@localhost #] cat i2c_val -Now let's set the green gain's register of the SN9C10x chip to 2: +Note that "cat" will fail if sensor registers cannot be read. + +Now let's set the green gain's register of the SN9C101 or SN9C102 chips to 2: [root@localhost #] echo 0x11 > reg [root@localhost #] echo 2 > val @@ -250,6 +252,9 @@ PAS202BCB PixArt Imaging Inc. TAS5110C1B Taiwan Advanced Sensor Corporation TAS5130D1B Taiwan Advanced Sensor Corporation +All the available control settings of each image sensor are supported through +the V4L2 interface. + If you think your camera is based on the above hardware and is not actually listed in the above table, you may try to add the specific USB VendorID and ProductID identifiers to the sn9c102_id_table[] in the file "sn9c102_sensor.h"; diff --git a/drivers/usb/media/sn9c102.h b/drivers/usb/media/sn9c102.h index 050a25d6f6aa..a682dfc2f747 100644 --- a/drivers/usb/media/sn9c102.h +++ b/drivers/usb/media/sn9c102.h @@ -53,8 +53,8 @@ #define SN9C102_MODULE_AUTHOR "(C) 2004 Luca Risolia" #define SN9C102_AUTHOR_EMAIL "" #define SN9C102_MODULE_LICENSE "GPL" -#define SN9C102_MODULE_VERSION "1:1.10" -#define SN9C102_MODULE_VERSION_CODE KERNEL_VERSION(1, 0, 10) +#define SN9C102_MODULE_VERSION "1:1.12" +#define SN9C102_MODULE_VERSION_CODE KERNEL_VERSION(1, 0, 12) enum sn9c102_bridge { BRIDGE_SN9C101 = 0x01, @@ -62,8 +62,8 @@ enum sn9c102_bridge { BRIDGE_SN9C103 = 0x04, }; -SN9C102_ID_TABLE; -SN9C102_SENSOR_TABLE; +SN9C102_ID_TABLE +SN9C102_SENSOR_TABLE enum sn9c102_frame_state { F_UNUSED, diff --git a/drivers/usb/media/sn9c102_core.c b/drivers/usb/media/sn9c102_core.c index 3881ae5ea08f..6d428a5b8d03 100644 --- a/drivers/usb/media/sn9c102_core.c +++ b/drivers/usb/media/sn9c102_core.c @@ -400,7 +400,7 @@ sn9c102_i2c_try_raw_write(struct sn9c102_device* cam, if (err) DBG(3, "I2C write failed for %s image sensor", sensor->name) - PDBGG("I2C write: %u bytes, data0 = 0x%02X, data1 = 0x%02X, " + PDBGG("I2C raw write: %u bytes, data0 = 0x%02X, data1 = 0x%02X, " "data2 = 0x%02X, data3 = 0x%02X, data4 = 0x%02X, data5 = 0x%02X", n, data0, data1, data2, data3, data4, data5) @@ -634,7 +634,7 @@ static int sn9c102_start_transfer(struct sn9c102_device* cam) struct usb_device *udev = cam->usbdev; struct urb* urb; const unsigned int wMaxPacketSize[] = {0, 128, 256, 384, 512, - 680, 800, 900, 1023}; + 680, 800, 900, 1023}; const unsigned int psz = wMaxPacketSize[SN9C102_ALTERNATE_SETTING]; s8 i, j; int err = 0; @@ -965,6 +965,11 @@ static ssize_t sn9c102_show_i2c_val(struct class_device* cd, char* buf) return -ENODEV; } + if (cam->sensor->slave_read_id == SN9C102_I2C_SLAVEID_UNAVAILABLE) { + up(&sn9c102_sysfs_lock); + return -ENOSYS; + } + if ((val = sn9c102_i2c_read(cam, cam->sysfs.i2c_reg)) < 0) { up(&sn9c102_sysfs_lock); return -EIO; @@ -1126,7 +1131,8 @@ static void sn9c102_create_sysfs(struct sn9c102_device* cam) video_device_create_file(v4ldev, &class_device_attr_blue); video_device_create_file(v4ldev, &class_device_attr_red); } - if (cam->sensor->slave_write_id && cam->sensor->slave_read_id) { + if (cam->sensor->slave_write_id != SN9C102_I2C_SLAVEID_UNAVAILABLE || + cam->sensor->slave_read_id != SN9C102_I2C_SLAVEID_UNAVAILABLE) { video_device_create_file(v4ldev, &class_device_attr_i2c_reg); video_device_create_file(v4ldev, &class_device_attr_i2c_val); } @@ -2362,10 +2368,20 @@ sn9c102_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) } cam->bridge = (sn9c102_id_table[i].idProduct & 0xffc0) == 0x6080 ? - BRIDGE_SN9C102 : BRIDGE_SN9C103; - - DBG(2, "SN9C10x PC Camera Controller detected (vid/pid 0x%04X/0x%04X)", - sn9c102_id_table[i].idVendor, sn9c102_id_table[i].idProduct) + BRIDGE_SN9C103 : BRIDGE_SN9C102; + switch (cam->bridge) { + case BRIDGE_SN9C101: + case BRIDGE_SN9C102: + DBG(2, "SN9C10[12] PC Camera Controller detected " + "(vid/pid 0x%04X/0x%04X)", sn9c102_id_table[i].idVendor, + sn9c102_id_table[i].idProduct) + break; + case BRIDGE_SN9C103: + DBG(2, "SN9C103 PC Camera Controller detected " + "(vid/pid 0x%04X/0x%04X)", sn9c102_id_table[i].idVendor, + sn9c102_id_table[i].idProduct) + break; + } for (i = 0; sn9c102_sensor_table[i]; i++) { err = sn9c102_sensor_table[i](cam); diff --git a/drivers/usb/media/sn9c102_pas202bcb.c b/drivers/usb/media/sn9c102_pas202bcb.c index ffd12ed14e34..72063e885871 100644 --- a/drivers/usb/media/sn9c102_pas202bcb.c +++ b/drivers/usb/media/sn9c102_pas202bcb.c @@ -36,18 +36,19 @@ static int pas202bcb_init(struct sn9c102_device* cam) err += sn9c102_write_reg(cam, 0x00, 0x11); err += sn9c102_write_reg(cam, 0x00, 0x14); err += sn9c102_write_reg(cam, 0x20, 0x17); - err += sn9c102_write_reg(cam, 0x20, 0x19); + err += sn9c102_write_reg(cam, 0x30, 0x19); err += sn9c102_write_reg(cam, 0x09, 0x18); - err += sn9c102_i2c_write(cam, 0x02, 0x0c); + err += sn9c102_i2c_write(cam, 0x02, 0x14); err += sn9c102_i2c_write(cam, 0x03, 0x40); err += sn9c102_i2c_write(cam, 0x04, 0x07); err += sn9c102_i2c_write(cam, 0x05, 0x25); err += sn9c102_i2c_write(cam, 0x0d, 0x2c); err += sn9c102_i2c_write(cam, 0x0e, 0x01); err += sn9c102_i2c_write(cam, 0x0f, 0xa9); - err += sn9c102_i2c_write(cam, 0x08, 0x01); + err += sn9c102_i2c_write(cam, 0x10, 0x08); err += sn9c102_i2c_write(cam, 0x0b, 0x01); + err += sn9c102_i2c_write(cam, 0x0c, 0x04); err += sn9c102_i2c_write(cam, 0x13, 0x63); err += sn9c102_i2c_write(cam, 0x15, 0x70); err += sn9c102_i2c_write(cam, 0x11, 0x01); @@ -217,7 +218,7 @@ int sn9c102_probe_pas202bcb(struct sn9c102_device* cam) * NOTE: do NOT change the values! */ err += sn9c102_write_reg(cam, 0x01, 0x01); /* sensor power down */ - err += sn9c102_write_reg(cam, 0x00, 0x01); /* sensor power on */ + err += sn9c102_write_reg(cam, 0x40, 0x01); /* sensor power on */ err += sn9c102_write_reg(cam, 0x28, 0x17); /* sensor clock at 24 MHz */ if (err) return -EIO; diff --git a/drivers/usb/media/sn9c102_sensor.h b/drivers/usb/media/sn9c102_sensor.h index 9632a5a63ccc..01bcad14ca6d 100644 --- a/drivers/usb/media/sn9c102_sensor.h +++ b/drivers/usb/media/sn9c102_sensor.h @@ -186,6 +186,9 @@ enum sn9c102_i2c_interface { SN9C102_I2C_3WIRES, }; +#define SN9C102_I2C_SLAVEID_FICTITIOUS 0xff +#define SN9C102_I2C_SLAVEID_UNAVAILABLE 0x00 + struct sn9c102_sensor { char name[32], /* sensor name */ maintainer[64]; /* name of the mantainer */ diff --git a/drivers/usb/media/sn9c102_tas5110c1b.c b/drivers/usb/media/sn9c102_tas5110c1b.c index 006a6b569f4a..ce8b47b59a75 100644 --- a/drivers/usb/media/sn9c102_tas5110c1b.c +++ b/drivers/usb/media/sn9c102_tas5110c1b.c @@ -104,8 +104,8 @@ static struct sn9c102_sensor tas5110c1b = { .maintainer = "Luca Risolia ", .frequency = SN9C102_I2C_100KHZ, .interface = SN9C102_I2C_3WIRES, - .slave_read_id = 0xff, /* fictitious */ - .slave_write_id = 0xff, /* fictitious */ + .slave_read_id = SN9C102_I2C_SLAVEID_UNAVAILABLE, + .slave_write_id = SN9C102_I2C_SLAVEID_FICTITIOUS, .init = &tas5110c1b_init, .qctrl = { { diff --git a/drivers/usb/media/sn9c102_tas5130d1b.c b/drivers/usb/media/sn9c102_tas5130d1b.c index 7af73acd2c37..0048e9c20d80 100644 --- a/drivers/usb/media/sn9c102_tas5130d1b.c +++ b/drivers/usb/media/sn9c102_tas5130d1b.c @@ -109,8 +109,8 @@ static struct sn9c102_sensor tas5130d1b = { .maintainer = "Luca Risolia ", .frequency = SN9C102_I2C_100KHZ, .interface = SN9C102_I2C_3WIRES, - .slave_read_id = 0xff, /* fictitious */ - .slave_write_id = 0xff, /* fictitious */ + .slave_read_id = SN9C102_I2C_SLAVEID_UNAVAILABLE, + .slave_write_id = SN9C102_I2C_SLAVEID_FICTITIOUS, .init = &tas5130d1b_init, .qctrl = { { -- cgit v1.2.3 From 1db17dc9cc37941ffd1fff8f63db004eff182c94 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Mon, 13 Sep 2004 21:23:52 -0700 Subject: [PATCH] USB: add missing pci_disable_device for PCI-based USB HCD From: Kenji Kaneshige This patch adds pci_disable_device() into usb_hcd_pci_remove(). If the driver decides to stop using the device, it should call pci_disable_device() to deallocate any IRQ resources, disable PCI bus-mastering, etc. Signed-off-by: Kenji Kaneshige Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd-pci.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c index f6f9c3b2325c..163345016aa2 100644 --- a/drivers/usb/core/hcd-pci.c +++ b/drivers/usb/core/hcd-pci.c @@ -260,6 +260,8 @@ void usb_hcd_pci_remove (struct pci_dev *dev) } usb_deregister_bus (&hcd->self); + + pci_disable_device(dev); } EXPORT_SYMBOL (usb_hcd_pci_remove); -- cgit v1.2.3 From 36b0e6a79d56563683a81b24c493b84937056ca1 Mon Sep 17 00:00:00 2001 From: Eric Valette Date: Mon, 13 Sep 2004 21:54:59 -0700 Subject: [PATCH] USB: rtl8150.c ethernet driver : usb_unlink_urb ->usb_kill_urb While we are looking at this driver, here is a way to avoid one full page of annoying messages at shutdown/module unload. Signed-off-by: Eric Valette Signed-off-by: Greg Kroah-Hartman --- drivers/usb/net/rtl8150.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/net/rtl8150.c b/drivers/usb/net/rtl8150.c index c7daf82e3a34..4caad655aeb0 100644 --- a/drivers/usb/net/rtl8150.c +++ b/drivers/usb/net/rtl8150.c @@ -344,7 +344,7 @@ static int rtl8150_set_mac_address(struct net_device *netdev, void *p) static int rtl8150_reset(rtl8150_t * dev) { - u8 data = 0x11; + u8 data = 0x10; int i = HZ; set_registers(dev, CR, 1, &data); @@ -392,10 +392,10 @@ static void free_all_urbs(rtl8150_t * dev) static void unlink_all_urbs(rtl8150_t * dev) { - usb_unlink_urb(dev->rx_urb); - usb_unlink_urb(dev->tx_urb); - usb_unlink_urb(dev->intr_urb); - usb_unlink_urb(dev->ctrl_urb); + usb_kill_urb(dev->rx_urb); + usb_kill_urb(dev->tx_urb); + usb_kill_urb(dev->intr_urb); + usb_kill_urb(dev->ctrl_urb); } static inline struct sk_buff *pull_skb(rtl8150_t *dev) @@ -663,7 +663,7 @@ static void rtl8150_tx_timeout(struct net_device *netdev) return; warn("%s: Tx timeout.", netdev->name); dev->tx_urb->transfer_flags |= URB_ASYNC_UNLINK; - usb_unlink_urb(dev->tx_urb); + usb_kill_urb(dev->tx_urb); dev->stats.tx_errors++; } -- cgit v1.2.3 From b51f163dd33b10311d61f2e2b55107d0a316cac5 Mon Sep 17 00:00:00 2001 From: Pete Zaitcev Date: Mon, 13 Sep 2004 21:56:06 -0700 Subject: [PATCH] USB: Patch for 3 ub bugs in 2.6.9-rc1-mm4 Actual users of ub quickly found problems, so here's a patch to address some of them. #1: An attempt to mount a CF card, pull the plug, then unmount causes a message "getblk: bad sector size 512" and an oops. This is caused by trying to do put_disk from disconnect instead of using a reference count. The sd.c does it this way (it uses kref). #2: The hald fills /var/log/messages with block device errors. It seems that it happens because ub allowed opens of known offline devices, and then partition checking produced those errors. I hope taking code from sd.c should fix it. Also I replaced usb_unlink_urb with usb_kill_urb. Signed-off-by: Greg Kroah-Hartman --- drivers/block/ub.c | 43 +++++++++++++++++++++++++------------------ 1 file changed, 25 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/block/ub.c b/drivers/block/ub.c index 6379b9043631..184b8a22e86f 100644 --- a/drivers/block/ub.c +++ b/drivers/block/ub.c @@ -490,6 +490,18 @@ static void ub_id_put(int id) */ static void ub_cleanup(struct ub_dev *sc) { + + /* + * If we zero disk->private_data BEFORE put_disk, we have to check + * for NULL all over the place in open, release, check_media and + * revalidate, because the block level semaphore is well inside the + * put_disk. But we cannot zero after the call, because *disk is gone. + * The sd.c is blatantly racy in this area. + */ + /* disk->private_data = NULL; */ + put_disk(sc->disk); + sc->disk = NULL; + ub_id_put(sc->id); kfree(sc); } @@ -1413,7 +1425,15 @@ static int ub_bd_open(struct inode *inode, struct file *filp) if (sc->removable || sc->readonly) check_disk_change(inode->i_bdev); - /* XXX sd.c and floppy.c bail on open if media is not present. */ + /* + * The sd.c considers ->media_present and ->changed not equivalent, + * under some pretty murky conditions (a failure of READ CAPACITY). + * We may need it one day. + */ + if (sc->removable && sc->changed && !(filp->f_flags & O_NDELAY)) { + rc = -ENOMEDIUM; + goto err_open; + } if (sc->readonly && (filp->f_mode & FMODE_WRITE)) { rc = -EROFS; @@ -1498,8 +1518,11 @@ static int ub_bd_revalidate(struct gendisk *disk) printk(KERN_INFO "%s: device %u capacity nsec %ld bsize %u\n", sc->name, sc->dev->devnum, sc->capacity.nsec, sc->capacity.bsize); + /* XXX Support sector size switching like in sr.c */ + // blk_queue_hardsect_size(q, sc->capacity.bsize); set_capacity(disk, sc->capacity.nsec); // set_disk_ro(sdkp->disk, sc->readonly); + return 0; } @@ -1746,12 +1769,7 @@ static int ub_probe_clear_stall(struct ub_dev *sc, int stalled_pipe) wait_for_completion(&compl); del_timer_sync(&timer); - /* - * Most of the time, URB was done and dev set to NULL, and so - * the unlink bounces out with ENODEV. We do not call usb_kill_urb - * because we still think about a backport to 2.4. - */ - usb_unlink_urb(&sc->work_urb); + usb_kill_urb(&sc->work_urb); /* reset the endpoint toggle */ usb_settoggle(sc->dev, endp, usb_pipeout(sc->last_pipe), 0); @@ -2010,17 +2028,6 @@ static void ub_disconnect(struct usb_interface *intf) if (q) blk_cleanup_queue(q); - /* - * If we zero disk->private_data BEFORE put_disk, we have to check - * for NULL all over the place in open, release, check_media and - * revalidate, because the block level semaphore is well inside the - * put_disk. But we cannot zero after the call, because *disk is gone. - * The sd.c is blatantly racy in this area. - */ - /* disk->private_data = NULL; */ - put_disk(disk); - sc->disk = NULL; - /* * We really expect blk_cleanup_queue() to wait, so no amount * of paranoya is too much. -- cgit v1.2.3 From 73c858138787db9287673e2daa366ba53ab49b32 Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Mon, 13 Sep 2004 21:58:23 -0700 Subject: [PATCH] USB: acm work around for misplaced this implements a work around for some devices which have correct extra descriptors, but misplace them. Signed-Off-By: Oliver Neukum Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-acm.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 37af64263c1b..b174d35b8d88 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -547,6 +547,17 @@ static int acm_probe (struct usb_interface *intf, return -EINVAL; } + if (!buflen) { + if (intf->cur_altsetting->endpoint->extralen && intf->cur_altsetting->endpoint->extra) { + dev_dbg(&intf->dev,"Seeking extra descriptors on endpoint"); + buflen = intf->cur_altsetting->endpoint->extralen; + buffer = intf->cur_altsetting->endpoint->extra; + } else { + err("Zero length descriptor references"); + return -EINVAL; + } + } + while (buflen > 0) { if (buffer [1] != USB_DT_CS_INTERFACE) { err("skipping garbage"); -- cgit v1.2.3 From 190b57996ef1d8d0520af964f0bbd92976aa341a Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Mon, 13 Sep 2004 22:00:01 -0700 Subject: [PATCH] USB: Descriptor listing bugfix for g_file_storage This patch repairs a mistake I made when adding OTG support to the file-storage gadget. All the descriptor entries were bumped up by one, which caused a problem during initialization. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/file_storage.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c index 2d2b2c032f19..6987b7c3e7c3 100644 --- a/drivers/usb/gadget/file_storage.c +++ b/drivers/usb/gadget/file_storage.c @@ -929,6 +929,7 @@ static const struct usb_descriptor_header *fs_function[] = { (struct usb_descriptor_header *) &fs_intr_in_desc, NULL, }; +#define FS_FUNCTION_PRE_EP_ENTRIES 2 #ifdef CONFIG_USB_GADGET_DUALSPEED @@ -992,6 +993,7 @@ static const struct usb_descriptor_header *hs_function[] = { (struct usb_descriptor_header *) &hs_intr_in_desc, NULL, }; +#define HS_FUNCTION_PRE_EP_ENTRIES 2 /* Maxpacket and other transfer characteristics vary by speed. */ #define ep_desc(g,fs,hs) (((g)->speed==USB_SPEED_HIGH) ? (hs) : (fs)) @@ -3899,10 +3901,10 @@ static int __init fsg_bind(struct usb_gadget *gadget) intf_desc.bNumEndpoints = i; intf_desc.bInterfaceSubClass = mod_data.protocol_type; intf_desc.bInterfaceProtocol = mod_data.transport_type; - fs_function[i+1] = NULL; + fs_function[i + FS_FUNCTION_PRE_EP_ENTRIES] = NULL; #ifdef CONFIG_USB_GADGET_DUALSPEED - hs_function[i+1] = NULL; + hs_function[i + HS_FUNCTION_PRE_EP_ENTRIES] = NULL; /* Assume ep0 uses the same maxpacket value for both speeds */ dev_qualifier.bMaxPacketSize0 = fsg->ep0->maxpacket; -- cgit v1.2.3 From 3bac8d2b031552e0cf00985590cd7714b8b290b1 Mon Sep 17 00:00:00 2001 From: Luiz Capitulino Date: Mon, 13 Sep 2004 22:00:27 -0700 Subject: [PATCH] USB: remove ugly code from usb/serial/usb-serial.c. This patch removes ugly code from some function in usb/serial/usb-serial.c which is using a goto statement intead of a simple `return'. To be true, I'm not certain if there is a special reason to do that, if so ignore me. ;) Signed-off-by: Luiz Capitulino Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/usb-serial.c | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 6b2aa1f6aa86..aa5c226197f7 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -621,15 +621,12 @@ static void serial_throttle (struct tty_struct * tty) if (!port->open_count) { dbg ("%s - port not open", __FUNCTION__); - goto exit; + return; } /* pass on to the driver specific version of this function */ if (port->serial->type->throttle) port->serial->type->throttle(port); - -exit: - ; } static void serial_unthrottle (struct tty_struct * tty) @@ -640,15 +637,12 @@ static void serial_unthrottle (struct tty_struct * tty) if (!port->open_count) { dbg("%s - port not open", __FUNCTION__); - goto exit; + return; } /* pass on to the driver specific version of this function */ if (port->serial->type->unthrottle) port->serial->type->unthrottle(port); - -exit: - ; } static int serial_ioctl (struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg) @@ -681,15 +675,12 @@ static void serial_set_termios (struct tty_struct *tty, struct termios * old) if (!port->open_count) { dbg("%s - port not open", __FUNCTION__); - goto exit; + return; } /* pass on to the driver specific version of this function if it is available */ if (port->serial->type->set_termios) port->serial->type->set_termios(port, old); - -exit: - ; } static void serial_break (struct tty_struct *tty, int break_state) @@ -700,15 +691,12 @@ static void serial_break (struct tty_struct *tty, int break_state) if (!port->open_count) { dbg("%s - port not open", __FUNCTION__); - goto exit; + return; } /* pass on to the driver specific version of this function if it is available */ if (port->serial->type->break_ctl) port->serial->type->break_ctl(port, break_state); - -exit: - ; } static int serial_read_proc (char *page, char **start, off_t off, int count, int *eof, void *data) -- cgit v1.2.3 From 2290c3778a6a3cf84f73580fd8e4cafcf63fa21d Mon Sep 17 00:00:00 2001 From: Luiz Capitulino Date: Mon, 13 Sep 2004 22:00:53 -0700 Subject: [PATCH] USB: missing check in usb/serial/usb-serial.c. This patch add a missing check in the call to bus_register() and not initialise `result' (which is not necessary). Signed-off-by: Luiz Capitulino Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/usb-serial.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index aa5c226197f7..73efa4270b79 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -1217,7 +1217,7 @@ struct tty_driver *usb_serial_tty_driver; static int __init usb_serial_init(void) { int i; - int result = 0; + int result; usb_serial_tty_driver = alloc_tty_driver(SERIAL_TTY_MINORS); if (!usb_serial_tty_driver) @@ -1228,13 +1228,17 @@ static int __init usb_serial_init(void) serial_table[i] = NULL; } - bus_register(&usb_serial_bus_type); + result = bus_register(&usb_serial_bus_type); + if (result) { + err("%s - registering bus driver failed", __FUNCTION__); + goto exit_bus; + } /* register the generic driver, if we should */ result = usb_serial_generic_register(debug); if (result < 0) { err("%s - registering generic driver failed", __FUNCTION__); - goto exit; + goto exit_generic; } usb_serial_tty_driver->owner = THIS_MODULE; @@ -1252,7 +1256,7 @@ static int __init usb_serial_init(void) result = tty_register_driver(usb_serial_tty_driver); if (result) { err("%s - tty_register_driver failed", __FUNCTION__); - goto exit_generic; + goto exit_reg_driver; } /* register the USB driver */ @@ -1269,10 +1273,13 @@ static int __init usb_serial_init(void) exit_tty: tty_unregister_driver(usb_serial_tty_driver); -exit_generic: +exit_reg_driver: usb_serial_generic_deregister(); -exit: +exit_generic: + bus_unregister(&usb_serial_bus_type); + +exit_bus: err ("%s - returning with error %d", __FUNCTION__, result); put_tty_driver(usb_serial_tty_driver); return result; -- cgit v1.2.3 From 2fd4d42b1a104a15beb9c8434f256a0a8ab83f38 Mon Sep 17 00:00:00 2001 From: Catalin Boie Date: Mon, 13 Sep 2004 22:06:27 -0700 Subject: [PATCH] USB: cdc-acm-usb-use-uninit-mem-bug.patch Attached is a patch for cdc-acm out of bounds access. Signed-off-by: Catalin(ux aka Dino) BOIE Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-acm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index b174d35b8d88..5614dde2c751 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -653,7 +653,7 @@ next_desc: dbg("interfaces are valid"); for (minor = 0; minor < ACM_TTY_MINORS && acm_table[minor]; minor++); - if (acm_table[minor]) { + if (minor == ACM_TTY_MINORS) { err("no more free acm devices"); return -ENODEV; } -- cgit v1.2.3 From 808f57d45fafe80fd64d1065a24b354f4aa452b0 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Mon, 13 Sep 2004 22:16:46 -0700 Subject: [PATCH] USB: Allow device resets for hubs This patch adds support for calling usb_reset_device() on hubs (other than root hubs). It uses some of the new routines added recently by David Brownell to shorten and simplify the code. The only place where this is called is if khubd encounters a serious error with a hub, so I don't expect it to turn up very often in ordinary use. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hub.c | 82 ++++++++++++++++++-------------------------------- 1 file changed, 29 insertions(+), 53 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 848ab62e71a6..c1ba248b6aac 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -651,8 +651,6 @@ static void hub_quiesce(struct usb_hub *hub) flush_scheduled_work(); } -#ifdef CONFIG_USB_SUSPEND - static void hub_reactivate(struct usb_hub *hub) { int status; @@ -665,8 +663,6 @@ static void hub_reactivate(struct usb_hub *hub) schedule_delayed_work(&hub->leds, LED_CYCLE_PERIOD); } -#endif - static void hub_disconnect(struct usb_interface *intf) { struct usb_hub *hub = usb_get_intfdata (intf); @@ -808,54 +804,26 @@ hub_ioctl(struct usb_interface *intf, unsigned int code, void *user_data) } } -/* caller has locked the hub and must own the device lock */ -static int hub_reset(struct usb_hub *hub) +/* caller has locked the hub device */ +static void hub_pre_reset(struct usb_device *hdev) { - struct usb_device *hdev = hub->hdev; + struct usb_hub *hub = usb_get_intfdata(hdev->actconfig->interface[0]); int i; - /* Disconnect any attached devices */ - for (i = 0; i < hub->descriptor->bNbrPorts; i++) { + for (i = 0; i < hdev->maxchild; ++i) { if (hdev->children[i]) usb_disconnect(&hdev->children[i]); } - - /* Attempt to reset the hub */ - if (hub->urb) - usb_kill_urb(hub->urb); - else - return -1; - - if (usb_reset_device(hdev)) - return -1; - - hub->urb->dev = hdev; - if (usb_submit_urb(hub->urb, GFP_KERNEL)) - return -1; - - hub_power_on(hub); - - return 0; + hub_quiesce(hub); } -/* caller has locked the hub */ -/* FIXME! This routine should be subsumed into hub_reset */ -static void hub_start_disconnect(struct usb_device *hdev) +/* caller has locked the hub device */ +static void hub_post_reset(struct usb_device *hdev) { - struct usb_device *parent = hdev->parent; - int i; - - /* Find the device pointer to disconnect */ - if (parent) { - for (i = 0; i < parent->maxchild; i++) { - if (parent->children[i] == hdev) { - usb_disconnect(&parent->children[i]); - return; - } - } - } + struct usb_hub *hub = usb_get_intfdata(hdev->actconfig->interface[0]); - dev_err(&hdev->dev, "cannot disconnect hub!\n"); + hub_reactivate(hub); + hub_power_on(hub); } @@ -2493,10 +2461,10 @@ static void hub_events(void) dev_dbg (hub_dev, "resetting for error %d\n", hub->error); - if (hub_reset(hub)) { + ret = usb_reset_device(hdev); + if (ret) { dev_dbg (hub_dev, - "can't reset; disconnecting\n"); - hub_start_disconnect(hdev); + "error resetting hub: %d\n", ret); goto loop; } @@ -2767,6 +2735,7 @@ int usb_reset_device(struct usb_device *udev) struct usb_device *parent = udev->parent; struct usb_device_descriptor descriptor = udev->descriptor; int i, ret, port = -1; + int udev_is_a_hub = 0; if (udev->state == USB_STATE_NOTATTACHED || udev->state == USB_STATE_SUSPENDED) { @@ -2775,13 +2744,9 @@ int usb_reset_device(struct usb_device *udev) return -EINVAL; } - /* FIXME: This should be legal for regular hubs. Root hubs may - * have special requirements. */ - if (udev->maxchild) { - /* this requires hub- or hcd-specific logic; - * see hub_reset() and OHCI hc_restart() - */ - dev_dbg(&udev->dev, "%s for hub!\n", __FUNCTION__); + if (!parent) { + /* this requires hcd-specific logic; see OHCI hc_restart() */ + dev_dbg(&udev->dev, "%s for root hub!\n", __FUNCTION__); return -EISDIR; } @@ -2797,6 +2762,14 @@ int usb_reset_device(struct usb_device *udev) return -ENOENT; } + /* If we're resetting an active hub, take some special actions */ + if (udev->actconfig && + udev->actconfig->interface[0]->dev.driver == + &hub_driver.driver) { + udev_is_a_hub = 1; + hub_pre_reset(udev); + } + /* ep0 maxpacket size may change; let the HCD know about it. * Other endpoints will be handled by re-enumeration. */ usb_disable_endpoint(udev, 0); @@ -2815,7 +2788,7 @@ int usb_reset_device(struct usb_device *udev) } if (!udev->actconfig) - return 0; + goto done; ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), USB_REQ_SET_CONFIGURATION, 0, @@ -2849,6 +2822,9 @@ int usb_reset_device(struct usb_device *udev) } } +done: + if (udev_is_a_hub) + hub_post_reset(udev); return 0; re_enumerate: -- cgit v1.2.3 From cd1f76678b398c0e84815177902414a2a2122729 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 14 Sep 2004 00:49:59 -0700 Subject: PCI: fix __iomem warnings in quirk code Signed-off-by: Greg Kroah-Hartman --- drivers/pci/quirks.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 34f072c9823e..8ded4fe07f51 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -921,7 +921,7 @@ static void __init quirk_usb_handoff_uhci(struct pci_dev *pdev) static void __init quirk_usb_handoff_ohci(struct pci_dev *pdev) { - char *base; + void __iomem *base; int wait_time; base = ioremap_nocache(pci_resource_start(pdev, 0), @@ -952,7 +952,7 @@ static void __init quirk_usb_handoff_ohci(struct pci_dev *pdev) static void __init quirk_usb_disable_ehci(struct pci_dev *pdev) { int wait_time, delta; - char *base, *op_reg_base; + void __iomem *base, *op_reg_base; u32 hcc_params, val, temp; u8 cap_length; -- cgit v1.2.3 From f6ab701ab0b8e4b1acf4bcdae64592d1598755dd Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 14 Sep 2004 21:52:18 -0700 Subject: USB: fix hcd-pci's __iomem warnings Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd-pci.c | 4 ++-- drivers/usb/core/hcd.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c index 163345016aa2..837bb267f194 100644 --- a/drivers/usb/core/hcd-pci.c +++ b/drivers/usb/core/hcd-pci.c @@ -65,7 +65,7 @@ int usb_hcd_pci_probe (struct pci_dev *dev, const struct pci_device_id *id) { struct hc_driver *driver; unsigned long resource, len; - void *base; + void __iomem *base; struct usb_hcd *hcd; int retval, region; char buf [8], *bufp = buf; @@ -121,7 +121,7 @@ clean_1: dev_dbg (&dev->dev, "no i/o regions available\n"); return -EBUSY; } - base = (void *) resource; + base = (void __iomem *) resource; } // driver->reset(), later on, will transfer device from diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h index 5e0337dfbcc9..eb8ae109096e 100644 --- a/drivers/usb/core/hcd.h +++ b/drivers/usb/core/hcd.h @@ -76,7 +76,7 @@ struct usb_hcd { /* usb_bus.hcpriv points to this */ unsigned can_wakeup:1; /* hw supports wakeup? */ unsigned remote_wakeup:1;/* sw should use wakeup? */ int irq; /* irq allocated */ - void *regs; /* device memory/io */ + void __iomem *regs; /* device memory/io */ #ifdef CONFIG_PCI int region; /* pci region for regs */ -- cgit v1.2.3 From 3e4e0e447be544b3bab52524f95bbfa2d4c11749 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 14 Sep 2004 23:07:28 -0700 Subject: USB: fix up __iomem warnings in the ehci driver. Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-hcd.c | 7 +++---- drivers/usb/host/ehci.h | 4 ++-- 2 files changed, 5 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 2f204b864a90..03d427a672a4 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -155,7 +155,7 @@ MODULE_PARM_DESC (log2_irq_thresh, "log2 IRQ latency, 1-64 microframes"); * before driver shutdown. But it also seems to be caused by bugs in cardbus * bridge shutdown: shutting down the bridge before the devices using it. */ -static int handshake (u32 *ptr, u32 mask, u32 done, int usec) +static int handshake (void __iomem *ptr, u32 mask, u32 done, int usec) { u32 result; @@ -340,9 +340,8 @@ static int ehci_hc_reset (struct usb_hcd *hcd) spin_lock_init (&ehci->lock); - ehci->caps = (struct ehci_caps *) hcd->regs; - ehci->regs = (struct ehci_regs *) (hcd->regs + - HC_LENGTH (readl (&ehci->caps->hc_capbase))); + ehci->caps = hcd->regs; + ehci->regs = hcd->regs + HC_LENGTH (readl (&ehci->caps->hc_capbase)); dbg_hcs_params (ehci, "reset"); dbg_hcc_params (ehci, "reset"); diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index de666f6b3314..c66b8d132d66 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -71,8 +71,8 @@ struct ehci_hcd { /* one per controller */ /* glue to PCI and HCD framework */ struct usb_hcd hcd; - struct ehci_caps *caps; - struct ehci_regs *regs; + struct ehci_caps __iomem *caps; + struct ehci_regs __iomem *regs; u32 hcs_params; /* cached register copy */ /* per-HC memory pools (could be per-bus, but ...) */ -- cgit v1.2.3 From 2d31f96408789bdbe7bd030308a3cd9dca45fe2e Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 14 Sep 2004 23:17:50 -0700 Subject: USB: fix up __iomem warnings in the ohci driver. Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ohci-dbg.c | 4 ++-- drivers/usb/host/ohci-hcd.c | 2 +- drivers/usb/host/ohci-hub.c | 2 +- drivers/usb/host/ohci.h | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/ohci-dbg.c b/drivers/usb/host/ohci-dbg.c index 532164079cfa..271908e82f0e 100644 --- a/drivers/usb/host/ohci-dbg.c +++ b/drivers/usb/host/ohci-dbg.c @@ -131,7 +131,7 @@ static char *hcfs2string (int state) static void ohci_dump_status (struct ohci_hcd *controller, char **next, unsigned *size) { - struct ohci_regs *regs = controller->regs; + struct ohci_regs __iomem *regs = controller->regs; u32 temp; temp = ohci_readl (®s->revision) & 0xff; @@ -599,7 +599,7 @@ show_registers (struct class_device *class_dev, char *buf) struct usb_bus *bus; struct usb_hcd *hcd; struct ohci_hcd *ohci; - struct ohci_regs *regs; + struct ohci_regs __iomem *regs; unsigned long flags; unsigned temp, size; char *next; diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index c5b806265504..84867d1d0bb7 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -679,7 +679,7 @@ static int ohci_run (struct ohci_hcd *ohci) static irqreturn_t ohci_irq (struct usb_hcd *hcd, struct pt_regs *ptregs) { struct ohci_hcd *ohci = hcd_to_ohci (hcd); - struct ohci_regs *regs = ohci->regs; + struct ohci_regs __iomem *regs = ohci->regs; int ints; /* we can eliminate a (slow) ohci_readl() diff --git a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c index d7a28d8eefb9..cf03c1fc0a2a 100644 --- a/drivers/usb/host/ohci-hub.c +++ b/drivers/usb/host/ohci-hub.c @@ -468,7 +468,7 @@ static void start_hnp(struct ohci_hcd *ohci); /* called from some task, normally khubd */ static inline void root_port_reset (struct ohci_hcd *ohci, unsigned port) { - u32 *portstat = &ohci->regs->roothub.portstatus [port]; + u32 __iomem *portstat = &ohci->regs->roothub.portstatus [port]; u32 temp; u16 now = readl(&ohci->regs->fmnumber); u16 reset_done = now + PORT_RESET_MSEC; diff --git a/drivers/usb/host/ohci.h b/drivers/usb/host/ohci.h index 6a3fa725ef4a..5b4e0e0d753b 100644 --- a/drivers/usb/host/ohci.h +++ b/drivers/usb/host/ohci.h @@ -342,7 +342,7 @@ struct ohci_hcd { /* * I/O memory used to communicate with the HC (dma-consistent) */ - struct ohci_regs *regs; + struct ohci_regs __iomem *regs; /* * main memory used to communicate with the HC (dma-consistent). @@ -452,7 +452,7 @@ static inline unsigned int ohci_readl (void* regs) #else /* Standard version of ohci_readl uses standard, platform * specific implementation. */ -static inline unsigned int ohci_readl (void* regs) +static inline unsigned int ohci_readl (void __iomem * regs) { return readl (regs); } -- cgit v1.2.3 From d4ab1ca0f02e6c2c4d82135c1d2ca8e043435443 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 14 Sep 2004 23:25:04 -0700 Subject: USB: fix up some minor sparse warnings in the uhci driver. Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/uhci-hcd.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/uhci-hcd.h b/drivers/usb/host/uhci-hcd.h index 3a6735740364..77efaf96a016 100644 --- a/drivers/usb/host/uhci-hcd.h +++ b/drivers/usb/host/uhci-hcd.h @@ -391,12 +391,12 @@ struct urb_priv { struct uhci_qh *qh; /* QH for this URB */ struct list_head td_list; /* P: urb->lock */ - int fsbr : 1; /* URB turned on FSBR */ - int fsbr_timeout : 1; /* URB timed out on FSBR */ - int queued : 1; /* QH was queued (not linked in) */ - int short_control_packet : 1; /* If we get a short packet during */ - /* a control transfer, retrigger */ - /* the status phase */ + unsigned fsbr : 1; /* URB turned on FSBR */ + unsigned fsbr_timeout : 1; /* URB timed out on FSBR */ + unsigned queued : 1; /* QH was queued (not linked in) */ + unsigned short_control_packet : 1; /* If we get a short packet during */ + /* a control transfer, retrigger */ + /* the status phase */ unsigned long inserttime; /* In jiffies */ unsigned long fsbrtime; /* In jiffies */ -- cgit v1.2.3 From c5cefba03a1392703e59df3f63b27245e7a7265b Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Wed, 15 Sep 2004 01:36:20 -0700 Subject: [PATCH] USB: remove calls to usb_unlink_urb in class/audio.c Signed-off-by: Borislav Petkov Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/audio.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/class/audio.c b/drivers/usb/class/audio.c index 36b3f3663e62..88628d85dff2 100644 --- a/drivers/usb/class/audio.c +++ b/drivers/usb/class/audio.c @@ -635,13 +635,13 @@ static void usbin_stop(struct usb_audiodev *as) spin_unlock_irqrestore(&as->lock, flags); if (notkilled && signal_pending(current)) { if (i & FLG_URB0RUNNING) - usb_unlink_urb(u->durb[0].urb); + usb_kill_urb(u->durb[0].urb); if (i & FLG_URB1RUNNING) - usb_unlink_urb(u->durb[1].urb); + usb_kill_urb(u->durb[1].urb); if (i & FLG_SYNC0RUNNING) - usb_unlink_urb(u->surb[0].urb); + usb_kill_urb(u->surb[0].urb); if (i & FLG_SYNC1RUNNING) - usb_unlink_urb(u->surb[1].urb); + usb_kill_urb(u->surb[1].urb); notkilled = 0; } } @@ -1114,13 +1114,13 @@ static void usbout_stop(struct usb_audiodev *as) spin_unlock_irqrestore(&as->lock, flags); if (notkilled && signal_pending(current)) { if (i & FLG_URB0RUNNING) - usb_unlink_urb(u->durb[0].urb); + usb_kill_urb(u->durb[0].urb); if (i & FLG_URB1RUNNING) - usb_unlink_urb(u->durb[1].urb); + usb_kill_urb(u->durb[1].urb); if (i & FLG_SYNC0RUNNING) - usb_unlink_urb(u->surb[0].urb); + usb_kill_urb(u->surb[0].urb); if (i & FLG_SYNC1RUNNING) - usb_unlink_urb(u->surb[1].urb); + usb_kill_urb(u->surb[1].urb); notkilled = 0; } } -- cgit v1.2.3 From cf371fa220183d54c4486c5b53978990b2acab31 Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Wed, 15 Sep 2004 01:36:51 -0700 Subject: [PATCH] USB: remove calls to usb_unlink_urb in class/bluetty.c Signed-off-by: Borislav Petkov Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/bluetty.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/class/bluetty.c b/drivers/usb/class/bluetty.c index 2e8f5200ca40..82cf2907b79a 100644 --- a/drivers/usb/class/bluetty.c +++ b/drivers/usb/class/bluetty.c @@ -426,8 +426,8 @@ static void bluetooth_close (struct tty_struct *tty, struct file * filp) bluetooth->open_count = 0; /* shutdown any in-flight urbs that we know about */ - usb_unlink_urb (bluetooth->read_urb); - usb_unlink_urb (bluetooth->interrupt_in_urb); + usb_kill_urb (bluetooth->read_urb); + usb_kill_urb (bluetooth->interrupt_in_urb); } up(&bluetooth->lock); } @@ -705,7 +705,7 @@ void btusb_disable_bulk_read(struct tty_struct *tty){ } if ((bluetooth->read_urb) && (bluetooth->read_urb->actual_length)) - usb_unlink_urb(bluetooth->read_urb); + usb_kill_urb(bluetooth->read_urb); } #endif @@ -1187,14 +1187,14 @@ static void usb_bluetooth_disconnect(struct usb_interface *intf) bluetooth->open_count = 0; if (bluetooth->read_urb) { - usb_unlink_urb (bluetooth->read_urb); + usb_kill_urb (bluetooth->read_urb); usb_free_urb (bluetooth->read_urb); } if (bluetooth->bulk_in_buffer) kfree (bluetooth->bulk_in_buffer); if (bluetooth->interrupt_in_urb) { - usb_unlink_urb (bluetooth->interrupt_in_urb); + usb_kill_urb (bluetooth->interrupt_in_urb); usb_free_urb (bluetooth->interrupt_in_urb); } if (bluetooth->interrupt_in_buffer) @@ -1204,7 +1204,7 @@ static void usb_bluetooth_disconnect(struct usb_interface *intf) for (i = 0; i < NUM_CONTROL_URBS; ++i) { if (bluetooth->control_urb_pool[i]) { - usb_unlink_urb (bluetooth->control_urb_pool[i]); + usb_kill_urb (bluetooth->control_urb_pool[i]); if (bluetooth->control_urb_pool[i]->transfer_buffer) kfree (bluetooth->control_urb_pool[i]->transfer_buffer); usb_free_urb (bluetooth->control_urb_pool[i]); -- cgit v1.2.3 From 6aa9d4c375d6860ea639fbaa3606ae1606ecaef3 Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Wed, 15 Sep 2004 01:37:16 -0700 Subject: [PATCH] USB: remove calls to usb_unlink_urb in class/cdc-acm.c Signed-off-by: Borislav Petkov Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-acm.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 5614dde2c751..d8fcf2f5264f 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -306,9 +306,9 @@ done: return 0; full_bailout: - usb_unlink_urb(acm->readurb); + usb_kill_urb(acm->readurb); bail_out_and_unlink: - usb_unlink_urb(acm->ctrlurb); + usb_kill_urb(acm->ctrlurb); bail_out: up(&open_sem); return -EIO; @@ -325,9 +325,9 @@ static void acm_tty_close(struct tty_struct *tty, struct file *filp) if (!--acm->used) { if (acm->dev) { acm_set_control(acm, acm->ctrlout = 0); - usb_unlink_urb(acm->ctrlurb); - usb_unlink_urb(acm->writeurb); - usb_unlink_urb(acm->readurb); + usb_kill_urb(acm->ctrlurb); + usb_kill_urb(acm->writeurb); + usb_kill_urb(acm->readurb); } else { tty_unregister_device(acm_tty_driver, acm->minor); acm_table[acm->minor] = NULL; @@ -778,9 +778,9 @@ static void acm_disconnect(struct usb_interface *intf) acm->dev = NULL; usb_set_intfdata (intf, NULL); - usb_unlink_urb(acm->ctrlurb); - usb_unlink_urb(acm->readurb); - usb_unlink_urb(acm->writeurb); + usb_kill_urb(acm->ctrlurb); + usb_kill_urb(acm->readurb); + usb_kill_urb(acm->writeurb); flush_scheduled_work(); /* wait for acm_softint */ -- cgit v1.2.3 From bf4d34766d8b9a3ef48699808ce933e7a2b963ee Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Wed, 15 Sep 2004 01:37:45 -0700 Subject: [PATCH] USB: remove calls to usb_unlink_urb in class/usb-midi.c Signed-off-by: Borislav Petkov Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/usb-midi.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/class/usb-midi.c b/drivers/usb/class/usb-midi.c index c4192ea52269..1dc952e9bc81 100644 --- a/drivers/usb/class/usb-midi.c +++ b/drivers/usb/class/usb-midi.c @@ -936,7 +936,7 @@ static int usb_midi_release(struct inode *inode, struct file *file) if ( m->open_mode & FMODE_WRITE ) { m->open_mode &= ~FMODE_WRITE; - usb_unlink_urb( m->mout.ep->urb ); + usb_kill_urb( m->mout.ep->urb ); } if ( m->open_mode & FMODE_READ ) { @@ -948,7 +948,7 @@ static int usb_midi_release(struct inode *inode, struct file *file) if ( m->min.ep->readers == 0 && m->min.ep->urbSubmitted ) { m->min.ep->urbSubmitted = 0; - usb_unlink_urb(m->min.ep->urb); + usb_kill_urb(m->min.ep->urb); } spin_unlock_irqrestore( &m->min.ep->lock, flagsep ); } @@ -1039,7 +1039,7 @@ static struct midi_in_endpoint *alloc_midi_in_endpoint( struct usb_device *d, in static int remove_midi_in_endpoint( struct midi_in_endpoint *min ) { - usb_unlink_urb( min->urb ); + usb_kill_urb( min->urb ); usb_free_urb( min->urb ); kfree( min->recvBuf ); kfree( min ); @@ -1099,7 +1099,7 @@ static struct midi_out_endpoint *alloc_midi_out_endpoint( struct usb_device *d, static int remove_midi_out_endpoint( struct midi_out_endpoint *mout ) { - usb_unlink_urb( mout->urb ); + usb_kill_urb( mout->urb ); usb_free_urb( mout->urb ); kfree( mout->buf ); kfree( mout ); -- cgit v1.2.3 From cbe7180db4bfce7bc2c34873799e3d49e74bed30 Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Wed, 15 Sep 2004 01:38:17 -0700 Subject: [PATCH] USB: remove calls to usb_unlink_urb() in image/hpusbscsi.c here's my next cleanup. I've changed the name of the goto label in order to remain consistent with naming. Signed-off-by: Borislav Petkov Signed-off-by: Greg Kroah-Hartman --- drivers/usb/image/hpusbscsi.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/image/hpusbscsi.c b/drivers/usb/image/hpusbscsi.c index dabc3666aee7..47a864b29f3d 100644 --- a/drivers/usb/image/hpusbscsi.c +++ b/drivers/usb/image/hpusbscsi.c @@ -106,7 +106,7 @@ hpusbscsi_usb_probe(struct usb_interface *intf, /* In host->hostdata we store a pointer to desc */ new->host = scsi_host_alloc(&hpusbscsi_scsi_host_template, sizeof(new)); if (!new->host) - goto out_unlink_controlurb; + goto out_kill_controlurb; new->host->hostdata[0] = (unsigned long)new; scsi_add_host(new->host, &intf->dev); /* XXX handle failure */ @@ -118,8 +118,8 @@ hpusbscsi_usb_probe(struct usb_interface *intf, usb_set_intfdata(intf, new); return 0; - out_unlink_controlurb: - usb_unlink_urb(new->controlurb); + out_kill_controlurb: + usb_kill_urb(new->controlurb); out_free_controlurb: usb_free_urb(new->controlurb); out_free_dataurb: @@ -137,7 +137,7 @@ hpusbscsi_usb_disconnect(struct usb_interface *intf) usb_set_intfdata(intf, NULL); scsi_remove_host(desc->host); - usb_unlink_urb(desc->controlurb); + usb_kill_urb(desc->controlurb); scsi_host_put(desc->host); usb_free_urb(desc->controlurb); @@ -280,8 +280,8 @@ static int hpusbscsi_scsi_abort (Scsi_Cmnd *srb) struct hpusbscsi* hpusbscsi = (struct hpusbscsi*)(srb->device->host->hostdata[0]); printk(KERN_DEBUG"Requested is canceled.\n"); - usb_unlink_urb(hpusbscsi->dataurb); - usb_unlink_urb(hpusbscsi->controlurb); + usb_kill_urb(hpusbscsi->dataurb); + usb_kill_urb(hpusbscsi->controlurb); hpusbscsi->state = HP_STATE_FREE; return SCSI_ABORT_PENDING; -- cgit v1.2.3 From 8bffe82eee0703a86f9119e65fa301190c849532 Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Mon, 20 Sep 2004 20:33:57 -0700 Subject: [PATCH] USB: remove call to usb_unlink_urb() in media/usbvideo.c Signed-off-by: Borislav Petkov Signed-off-by: Greg Kroah-Hartman --- drivers/usb/media/usbvideo.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/media/usbvideo.c b/drivers/usb/media/usbvideo.c index 1107e398d6f3..122b7accf1e1 100644 --- a/drivers/usb/media/usbvideo.c +++ b/drivers/usb/media/usbvideo.c @@ -1910,9 +1910,7 @@ static void usbvideo_StopDataPump(struct uvd *uvd) /* Unschedule all of the iso td's */ for (i=0; i < USBVIDEO_NUMSBUF; i++) { - j = usb_unlink_urb(uvd->sbuf[i].urb); - if (j < 0) - err("%s: usb_unlink_urb() error %d.", __FUNCTION__, j); + usb_kill_urb(uvd->sbuf[i].urb); } if (uvd->debug > 1) info("%s: streaming=0", __FUNCTION__); -- cgit v1.2.3 From d11aa2b83b8b23a408fb5e730e6389159d8408ac Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Mon, 20 Sep 2004 20:34:25 -0700 Subject: [PATCH] USB: remove calls to usb_unlink_urb in media/stv680.c Signed-off-by: Borislav Petkov Signed-off-by: Greg Kroah-Hartman --- drivers/usb/media/stv680.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/media/stv680.c b/drivers/usb/media/stv680.c index cd73a45d0dd0..a91870f0b412 100644 --- a/drivers/usb/media/stv680.c +++ b/drivers/usb/media/stv680.c @@ -725,7 +725,7 @@ static int stv680_stop_stream (struct usb_stv *stv680) for (i = 0; i < STV680_NUMSBUF; i++) if (stv680->urb[i]) { - usb_unlink_urb (stv680->urb[i]); + usb_kill_urb (stv680->urb[i]); usb_free_urb (stv680->urb[i]); stv680->urb[i] = NULL; kfree (stv680->sbuf[i].data); @@ -1457,7 +1457,7 @@ static inline void usb_stv680_remove_disconnected (struct usb_stv *stv680) for (i = 0; i < STV680_NUMSBUF; i++) if (stv680->urb[i]) { - usb_unlink_urb (stv680->urb[i]); + usb_kill_urb (stv680->urb[i]); usb_free_urb (stv680->urb[i]); stv680->urb[i] = NULL; kfree (stv680->sbuf[i].data); -- cgit v1.2.3 From 79e48216732db4d516ce520d18771584b5df8618 Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Mon, 20 Sep 2004 20:35:01 -0700 Subject: [PATCH] USB: remove calls to usb_unlink_urb in media/se401.c Signed-off-by: Borislav Petkov Signed-off-by: Greg Kroah-Hartman --- drivers/usb/media/se401.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/media/se401.c b/drivers/usb/media/se401.c index c694caaac447..37d28640c790 100644 --- a/drivers/usb/media/se401.c +++ b/drivers/usb/media/se401.c @@ -514,7 +514,7 @@ static int se401_stop_stream(struct usb_se401 *se401) se401_sndctrl(1, se401, SE401_REQ_CAMERA_POWER, 0, NULL, 0); for (i=0; iurb[i]) { - usb_unlink_urb(se401->urb[i]); + usb_kill_urb(se401->urb[i]); usb_free_urb(se401->urb[i]); se401->urb[i]=NULL; kfree(se401->sbuf[i].data); @@ -883,7 +883,7 @@ static void usb_se401_remove_disconnected (struct usb_se401 *se401) se401->dev = NULL; for (i=0; iurb[i]) { - usb_unlink_urb(se401->urb[i]); + usb_kill_urb(se401->urb[i]); usb_free_urb(se401->urb[i]); se401->urb[i] = NULL; kfree(se401->sbuf[i].data); @@ -892,7 +892,7 @@ static void usb_se401_remove_disconnected (struct usb_se401 *se401) kfree(se401->scratch[i].data); } if (se401->inturb) { - usb_unlink_urb(se401->inturb); + usb_kill_urb(se401->inturb); usb_free_urb(se401->inturb); } info("%s disconnected", se401->camera_name); -- cgit v1.2.3 From ac04d3853809aceeaef9841eed9d90b119e1d089 Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Mon, 20 Sep 2004 20:35:57 -0700 Subject: [PATCH] USB: remove call to usb_unlink_urb in media/ov511.c Signed-off-by: Borislav Petkov Signed-off-by: Greg Kroah-Hartman --- drivers/usb/media/ov511.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/media/ov511.c b/drivers/usb/media/ov511.c index 12f46de2bfe9..fe679ef2364b 100644 --- a/drivers/usb/media/ov511.c +++ b/drivers/usb/media/ov511.c @@ -3830,7 +3830,7 @@ ov51x_unlink_isoc(struct usb_ov511 *ov) /* Unschedule all of the iso td's */ for (n = OV511_NUMSBUF - 1; n >= 0; n--) { if (ov->sbuf[n].urb) { - usb_unlink_urb(ov->sbuf[n].urb); + usb_kill_urb(ov->sbuf[n].urb); usb_free_urb(ov->sbuf[n].urb); ov->sbuf[n].urb = NULL; } -- cgit v1.2.3 From 83990a4d054a5d3e16a92b4e5e300c6f0618b19f Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Mon, 20 Sep 2004 20:36:25 -0700 Subject: [PATCH] USB: remove calls to usb_unlink_urb in media/konicawc.c since usb_kill_urb is a void function, the checking of the return status had to go too. Signed-off-by: Borislav Petkov Signed-off-by: Greg Kroah-Hartman --- drivers/usb/media/konicawc.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/media/konicawc.c b/drivers/usb/media/konicawc.c index 3376654ca051..aca3093c2b87 100644 --- a/drivers/usb/media/konicawc.c +++ b/drivers/usb/media/konicawc.c @@ -474,13 +474,8 @@ static void konicawc_stop_data(struct uvd *uvd) /* Unschedule all of the iso td's */ for (i=0; i < USBVIDEO_NUMSBUF; i++) { - j = usb_unlink_urb(uvd->sbuf[i].urb); - if (j < 0) - err("usb_unlink_urb() error %d.", j); - - j = usb_unlink_urb(cam->sts_urb[i]); - if (j < 0) - err("usb_unlink_urb() error %d.", j); + usb_kill_urb(uvd->sbuf[i].urb); + usb_kill_urb(cam->sts_urb[i]); } if (!uvd->remove_pending) { -- cgit v1.2.3 From 1dc47eb73b95024d20724dcc3925b02e8925fbb8 Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Mon, 20 Sep 2004 20:36:51 -0700 Subject: [PATCH] USB: remove calls to usb_unlink_urb in input/xpad.c Signed-off-by: Borislav Petkov Signed-off-by: Greg Kroah-Hartman --- drivers/usb/input/xpad.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/input/xpad.c b/drivers/usb/input/xpad.c index 1956eb06b75c..c5fa87edb667 100644 --- a/drivers/usb/input/xpad.c +++ b/drivers/usb/input/xpad.c @@ -214,7 +214,7 @@ static void xpad_close (struct input_dev *dev) struct usb_xpad *xpad = dev->private; if (!--xpad->open_count) - usb_unlink_urb(xpad->irq_in); + usb_kill_urb(xpad->irq_in); } static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id) @@ -325,7 +325,7 @@ static void xpad_disconnect(struct usb_interface *intf) usb_set_intfdata(intf, NULL); if (xpad) { - usb_unlink_urb(xpad->irq_in); + usb_kill_urb(xpad->irq_in); input_unregister_device(&xpad->dev); usb_free_urb(xpad->irq_in); usb_buffer_free(interface_to_usbdev(intf), XPAD_PKT_LEN, xpad->idata, xpad->idata_dma); -- cgit v1.2.3 From d73c9a169190d882877ed0fdd00d511648d5b375 Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Mon, 20 Sep 2004 20:37:20 -0700 Subject: [PATCH] USB: usb_unlink_urb removal from input/ati_remote.c Signed-off-by: Borislav Petkov Signed-off-by: Greg Kroah-Hartman --- drivers/usb/input/ati_remote.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/input/ati_remote.c b/drivers/usb/input/ati_remote.c index 61a42bdae1d8..3adbc7bab3c3 100644 --- a/drivers/usb/input/ati_remote.c +++ b/drivers/usb/input/ati_remote.c @@ -424,7 +424,7 @@ static int ati_remote_sendpacket(struct ati_remote *ati_remote, u16 cmd, unsigne set_current_state(TASK_RUNNING); remove_wait_queue(&ati_remote->wait, &wait); - usb_unlink_urb(ati_remote->out_urb); + usb_kill_urb(ati_remote->out_urb); return retval; } @@ -624,10 +624,10 @@ static void ati_remote_delete(struct ati_remote *ati_remote) if (!ati_remote) return; if (ati_remote->irq_urb) - usb_unlink_urb(ati_remote->irq_urb); + usb_kill_urb(ati_remote->irq_urb); if (ati_remote->out_urb) - usb_unlink_urb(ati_remote->out_urb); + usb_kill_urb(ati_remote->out_urb); input_unregister_device(&ati_remote->idev); -- cgit v1.2.3 From 0a52ea9f0611a911bdd31e48b35a3d0f4d876bcc Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Mon, 20 Sep 2004 20:37:49 -0700 Subject: [PATCH] USB: remove calls to usb_unlink_urb in input/wacom.c Signed-off-by: Borislav Petkov Signed-off-by: Greg Kroah-Hartman --- drivers/usb/input/wacom.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/input/wacom.c b/drivers/usb/input/wacom.c index c66001351165..b977c47a1af1 100644 --- a/drivers/usb/input/wacom.c +++ b/drivers/usb/input/wacom.c @@ -608,7 +608,7 @@ static void wacom_close(struct input_dev *dev) struct wacom *wacom = dev->private; if (!--wacom->open) - usb_unlink_urb(wacom->irq); + usb_kill_urb(wacom->irq); } static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *id) @@ -729,7 +729,7 @@ static void wacom_disconnect(struct usb_interface *intf) usb_set_intfdata(intf, NULL); if (wacom) { - usb_unlink_urb(wacom->irq); + usb_kill_urb(wacom->irq); input_unregister_device(&wacom->dev); usb_free_urb(wacom->irq); usb_buffer_free(interface_to_usbdev(intf), 10, wacom->data, wacom->data_dma); -- cgit v1.2.3 From 1ec4eb3a1d0e5f0613596de3482cc18edb6e2506 Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Mon, 20 Sep 2004 20:38:17 -0700 Subject: [PATCH] USB: remove calls to usb_unlink_urb in input/usbmouse.c Signed-off-by: Borislav Petkov Signed-off-by: Greg Kroah-Hartman --- drivers/usb/input/usbmouse.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/input/usbmouse.c b/drivers/usb/input/usbmouse.c index 5bf27306ea3c..8c7381b74499 100644 --- a/drivers/usb/input/usbmouse.c +++ b/drivers/usb/input/usbmouse.c @@ -118,7 +118,7 @@ static void usb_mouse_close(struct input_dev *dev) struct usb_mouse *mouse = dev->private; if (!--mouse->open) - usb_unlink_urb(mouse->irq); + usb_kill_urb(mouse->irq); } static int usb_mouse_probe(struct usb_interface * intf, const struct usb_device_id * id) @@ -223,7 +223,7 @@ static void usb_mouse_disconnect(struct usb_interface *intf) usb_set_intfdata(intf, NULL); if (mouse) { - usb_unlink_urb(mouse->irq); + usb_kill_urb(mouse->irq); input_unregister_device(&mouse->dev); usb_free_urb(mouse->irq); usb_buffer_free(interface_to_usbdev(intf), 8, mouse->data, mouse->data_dma); -- cgit v1.2.3 From 7689ae9c6eb774e6e23bdac5b960c070748676cd Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Mon, 20 Sep 2004 20:38:50 -0700 Subject: [PATCH] USB: remove calls to usb_unlink_urb in core/message.c Signed-off-by: Borislav Petkov Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/message.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index e20dde5d793a..3a10b1baf744 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -36,7 +36,7 @@ static void timeout_kill(unsigned long data) usb_pipecontrol(urb->pipe) ? "control" : "bulk", usb_pipeendpoint(urb->pipe), usb_pipein(urb->pipe) ? "in" : "out"); - usb_unlink_urb(urb); + usb_kill_urb(urb); } // Starts urb and waits for completion or timeout @@ -251,7 +251,7 @@ static void sg_complete (struct urb *urb, struct pt_regs *regs) if (!io->urbs [i] || !io->urbs [i]->dev) continue; if (found) { - status = usb_unlink_urb (io->urbs [i]); + status = usb_kill_urb (io->urbs [i]); if (status != -EINPROGRESS && status != -EBUSY) dev_err (&io->dev->dev, "%s, unlink --> %d\n", @@ -525,7 +525,7 @@ void usb_sg_cancel (struct usb_sg_request *io) if (!io->urbs [i]->dev) continue; - retval = usb_unlink_urb (io->urbs [i]); + retval = usb_kill_urb (io->urbs [i]); if (retval != -EINPROGRESS && retval != -EBUSY) dev_warn (&io->dev->dev, "%s, unlink --> %d\n", __FUNCTION__, retval); -- cgit v1.2.3 From 214ec5cb8bb7321420c258dbf5475f14ef026df6 Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Mon, 20 Sep 2004 20:39:16 -0700 Subject: [PATCH] USB: remove usb_unlink_urb() calls in input/kbtab.c Signed-off-by: Borislav Petkov Signed-off-by: Greg Kroah-Hartman --- drivers/usb/input/kbtab.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/input/kbtab.c b/drivers/usb/input/kbtab.c index 7a29610e5218..bef4028741be 100644 --- a/drivers/usb/input/kbtab.c +++ b/drivers/usb/input/kbtab.c @@ -122,7 +122,7 @@ static void kbtab_close(struct input_dev *dev) struct kbtab *kbtab = dev->private; if (!--kbtab->open) - usb_unlink_urb(kbtab->irq); + usb_kill_urb(kbtab->irq); } static int kbtab_probe(struct usb_interface *intf, const struct usb_device_id *id) @@ -205,7 +205,7 @@ static void kbtab_disconnect(struct usb_interface *intf) usb_set_intfdata(intf, NULL); if (kbtab) { - usb_unlink_urb(kbtab->irq); + usb_kill_urb(kbtab->irq); input_unregister_device(&kbtab->dev); usb_free_urb(kbtab->irq); usb_buffer_free(interface_to_usbdev(intf), 10, kbtab->data, kbtab->data_dma); -- cgit v1.2.3 From 6bc76e9daf89515611b8be63223ebcd78186096e Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Mon, 20 Sep 2004 20:39:39 -0700 Subject: [PATCH] USB: usb_unlink_urb removal from input/hid-core.c Signed-off-by: Borislav Petkov Signed-off-by: Greg Kroah-Hartman --- drivers/usb/input/hid-core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/input/hid-core.c b/drivers/usb/input/hid-core.c index 34b0e91aeb1b..e57638d6af7a 100644 --- a/drivers/usb/input/hid-core.c +++ b/drivers/usb/input/hid-core.c @@ -1334,9 +1334,9 @@ void hid_init_reports(struct hid_device *hid) while (ret) { err |= ret; if (test_bit(HID_CTRL_RUNNING, &hid->iofl)) - usb_unlink_urb(hid->urbctrl); + usb_kill_urb(hid->urbctrl); if (test_bit(HID_OUT_RUNNING, &hid->iofl)) - usb_unlink_urb(hid->urbout); + usb_kill_urb(hid->urbout); ret = hid_wait_io(hid); } -- cgit v1.2.3 From c8de0f9bf77eb46d2a33174a945153e088e76b10 Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Mon, 20 Sep 2004 20:40:11 -0700 Subject: [PATCH] USB: remove calls to usb_unlink_urb in input/mtouchusb.c Signed-off-by: Borislav Petkov Signed-off-by: Greg Kroah-Hartman --- drivers/usb/input/mtouchusb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/input/mtouchusb.c b/drivers/usb/input/mtouchusb.c index ae5713876d4c..9dcfd7e2ffbf 100644 --- a/drivers/usb/input/mtouchusb.c +++ b/drivers/usb/input/mtouchusb.c @@ -155,7 +155,7 @@ static void mtouchusb_close (struct input_dev *input) struct mtouch_usb *mtouch = input->private; if (!--mtouch->open) - usb_unlink_urb (mtouch->irq); + usb_kill_urb (mtouch->irq); } static int mtouchusb_alloc_buffers(struct usb_device *udev, struct mtouch_usb *mtouch) @@ -320,7 +320,7 @@ static void mtouchusb_disconnect(struct usb_interface *intf) usb_set_intfdata(intf, NULL); if (mtouch) { dbg("%s - mtouch is initialized, cleaning up", __FUNCTION__); - usb_unlink_urb(mtouch->irq); + usb_kill_urb(mtouch->irq); input_unregister_device(&mtouch->input); usb_free_urb(mtouch->irq); mtouchusb_free_buffers(interface_to_usbdev(intf), mtouch); -- cgit v1.2.3 From 1b00690cf6a638a4a2a00988d2a92158acd8e9a7 Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Mon, 20 Sep 2004 20:40:39 -0700 Subject: [PATCH] USB: usb_unlink_urb removal from input/aiptek.c Signed-off-by: Borislav Petkov Signed-off-by: Greg Kroah-Hartman --- drivers/usb/input/aiptek.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/input/aiptek.c b/drivers/usb/input/aiptek.c index a97e2c5e2482..74c1754d9c21 100644 --- a/drivers/usb/input/aiptek.c +++ b/drivers/usb/input/aiptek.c @@ -837,7 +837,7 @@ static void aiptek_close(struct input_dev *inputdev) struct aiptek *aiptek = inputdev->private; if (--aiptek->openCount == 0) { - usb_unlink_urb(aiptek->urb); + usb_kill_urb(aiptek->urb); } } @@ -2258,7 +2258,7 @@ static void aiptek_disconnect(struct usb_interface *intf) if (aiptek != NULL) { /* Free & unhook everything from the system. */ - usb_unlink_urb(aiptek->urb); + usb_kill_urb(aiptek->urb); input_unregister_device(&aiptek->inputdev); aiptek_delete_files(&intf->dev); usb_free_urb(aiptek->urb); -- cgit v1.2.3 From b5df272b6abf2a070d4b2de3010977a432d2d5e7 Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Mon, 20 Sep 2004 20:41:02 -0700 Subject: [PATCH] USB: remove calls to usb_unlink_urb in input/usbkbd.c Signed-off-by: Borislav Petkov Signed-off-by: Greg Kroah-Hartman --- drivers/usb/input/usbkbd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/input/usbkbd.c b/drivers/usb/input/usbkbd.c index 0c51b1eb84cf..1700f405b00b 100644 --- a/drivers/usb/input/usbkbd.c +++ b/drivers/usb/input/usbkbd.c @@ -196,7 +196,7 @@ static void usb_kbd_close(struct input_dev *dev) struct usb_kbd *kbd = dev->private; if (!--kbd->open) - usb_unlink_urb(kbd->irq); + usb_kill_urb(kbd->irq); } static int usb_kbd_alloc_mem(struct usb_device *dev, struct usb_kbd *kbd) @@ -343,7 +343,7 @@ static void usb_kbd_disconnect(struct usb_interface *intf) usb_set_intfdata(intf, NULL); if (kbd) { - usb_unlink_urb(kbd->irq); + usb_kill_urb(kbd->irq); input_unregister_device(&kbd->dev); usb_kbd_free_mem(interface_to_usbdev(intf), kbd); kfree(kbd); -- cgit v1.2.3 From efbaa5b3ed7b5752f5c06c9394ec4a7dea43c41f Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Mon, 20 Sep 2004 20:41:24 -0700 Subject: [PATCH] USB: remove calls to usb_unlink_urb() in input/pid.c Signed-off-by: Borislav Petkov Signed-off-by: Greg Kroah-Hartman --- drivers/usb/input/pid.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/input/pid.c b/drivers/usb/input/pid.c index a38b0db8dcea..d1ea1f056599 100644 --- a/drivers/usb/input/pid.c +++ b/drivers/usb/input/pid.c @@ -56,7 +56,7 @@ static void hid_pid_exit(struct hid_device* hid) struct hid_ff_pid *private = hid->ff_private; if (private->urbffout) { - usb_unlink_urb(private->urbffout); + usb_kill_urb(private->urbffout); usb_free_urb(private->urbffout); } } -- cgit v1.2.3 From 4fcaaa7e8b92b684965fc6a207d03e65c94e59f4 Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Mon, 20 Sep 2004 20:42:01 -0700 Subject: [PATCH] USB: remove calls to usb_unlink_urb in image/mdc800.c (v2) Signed-off-by: Borislav Petkov Signed-off-by: Greg Kroah-Hartman --- drivers/usb/image/mdc800.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/image/mdc800.c b/drivers/usb/image/mdc800.c index f401557f0356..17e900c198cf 100644 --- a/drivers/usb/image/mdc800.c +++ b/drivers/usb/image/mdc800.c @@ -543,9 +543,9 @@ static void mdc800_usb_disconnect (struct usb_interface *intf) mdc800->state=NOT_CONNECTED; - usb_unlink_urb (mdc800->irq_urb); - usb_unlink_urb (mdc800->write_urb); - usb_unlink_urb (mdc800->download_urb); + usb_kill_urb(mdc800->irq_urb); + usb_kill_urb(mdc800->write_urb); + usb_kill_urb(mdc800->download_urb); mdc800->dev = NULL; usb_set_intfdata(intf, NULL); @@ -649,9 +649,9 @@ static int mdc800_device_release (struct inode* inode, struct file *file) down (&mdc800->io_lock); if (mdc800->open && (mdc800->state != NOT_CONNECTED)) { - usb_unlink_urb (mdc800->irq_urb); - usb_unlink_urb (mdc800->write_urb); - usb_unlink_urb (mdc800->download_urb); + usb_kill_urb(mdc800->irq_urb); + usb_kill_urb(mdc800->write_urb); + usb_kill_urb(mdc800->download_urb); mdc800->open=0; } else @@ -856,7 +856,7 @@ static ssize_t mdc800_device_write (struct file *file, const char __user *buf, s mdc800->written = 0; if (mdc800->state == WORKING) { - usb_unlink_urb (mdc800->write_urb); + usb_kill_urb(mdc800->write_urb); up (&mdc800->io_lock); return -EIO; } -- cgit v1.2.3 From 7158ba3acaecf1b920e352f15c5635119fb52891 Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Mon, 20 Sep 2004 20:42:28 -0700 Subject: [PATCH] USB: remove calls to usb_unlink_urb in input/powermate.c Signed-off-by: Borislav Petkov Signed-off-by: Greg Kroah-Hartman --- drivers/usb/input/powermate.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/input/powermate.c b/drivers/usb/input/powermate.c index 0ba1bcbfb431..852ebf8574a8 100644 --- a/drivers/usb/input/powermate.c +++ b/drivers/usb/input/powermate.c @@ -417,7 +417,7 @@ static void powermate_disconnect(struct usb_interface *intf) usb_set_intfdata(intf, NULL); if (pm) { pm->requires_update = 0; - usb_unlink_urb(pm->irq); + usb_kill_urb(pm->irq); input_unregister_device(&pm->input); usb_free_urb(pm->irq); usb_free_urb(pm->config); -- cgit v1.2.3 From 9e26f5920faf28aa8af8392fd5f58a51efbf2643 Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Mon, 20 Sep 2004 20:42:57 -0700 Subject: [PATCH] USB: remove calls to usb_unlink_urb() in input/touchkitusb.c Signed-off-by: Borislav Petkov Signed-off-by: Greg Kroah-Hartman --- drivers/usb/input/touchkitusb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/input/touchkitusb.c b/drivers/usb/input/touchkitusb.c index 4917b042ef9a..14443f926ab0 100644 --- a/drivers/usb/input/touchkitusb.c +++ b/drivers/usb/input/touchkitusb.c @@ -141,7 +141,7 @@ static void touchkit_close(struct input_dev *input) struct touchkit_usb *touchkit = input->private; if (!--touchkit->open) - usb_unlink_urb(touchkit->irq); + usb_kill_urb(touchkit->irq); } static int touchkit_alloc_buffers(struct usb_device *udev, @@ -276,7 +276,7 @@ static void touchkit_disconnect(struct usb_interface *intf) dbg("%s - touchkit is initialized, cleaning up", __FUNCTION__); usb_set_intfdata(intf, NULL); input_unregister_device(&touchkit->input); - usb_unlink_urb(touchkit->irq); + usb_kill_urb(touchkit->irq); usb_free_urb(touchkit->irq); touchkit_free_buffers(interface_to_usbdev(intf), touchkit); kfree(touchkit); -- cgit v1.2.3 From 90578145020a53b84d0cc6850b042cff939aa8e7 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 20 Sep 2004 21:16:22 -0700 Subject: [PATCH] USB: oops, revert drivers/usb/core/message.c change. That patch was just not right... Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/message.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index 3a10b1baf744..e20dde5d793a 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -36,7 +36,7 @@ static void timeout_kill(unsigned long data) usb_pipecontrol(urb->pipe) ? "control" : "bulk", usb_pipeendpoint(urb->pipe), usb_pipein(urb->pipe) ? "in" : "out"); - usb_kill_urb(urb); + usb_unlink_urb(urb); } // Starts urb and waits for completion or timeout @@ -251,7 +251,7 @@ static void sg_complete (struct urb *urb, struct pt_regs *regs) if (!io->urbs [i] || !io->urbs [i]->dev) continue; if (found) { - status = usb_kill_urb (io->urbs [i]); + status = usb_unlink_urb (io->urbs [i]); if (status != -EINPROGRESS && status != -EBUSY) dev_err (&io->dev->dev, "%s, unlink --> %d\n", @@ -525,7 +525,7 @@ void usb_sg_cancel (struct usb_sg_request *io) if (!io->urbs [i]->dev) continue; - retval = usb_kill_urb (io->urbs [i]); + retval = usb_unlink_urb (io->urbs [i]); if (retval != -EINPROGRESS && retval != -EBUSY) dev_warn (&io->dev->dev, "%s, unlink --> %d\n", __FUNCTION__, retval); -- cgit v1.2.3 From a0e64cc0ae584bbf8c3e8637cec61289f17bf1ff Mon Sep 17 00:00:00 2001 From: Duncan Sands Date: Mon, 20 Sep 2004 21:17:31 -0700 Subject: [PATCH] usb speedtch: no side-effects in BUG_ON It seems that people want BUG_ON to be like an assertion: harmless if removed. Signed-off-by: Duncan Sands Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/speedtch.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/misc/speedtch.c b/drivers/usb/misc/speedtch.c index 667e2d1b227c..9e191e0a2837 100644 --- a/drivers/usb/misc/speedtch.c +++ b/drivers/usb/misc/speedtch.c @@ -93,7 +93,7 @@ #ifdef DEBUG #define DEBUG_ON(x) BUG_ON(x) #else -#define DEBUG_ON(x) do { if (x); } while (0) +#define DEBUG_ON(x) #endif #ifdef VERBOSE_DEBUG @@ -518,7 +518,8 @@ static unsigned int udsl_write_cells (unsigned int howmany, struct sk_buff *skb, memset (target, 0, ATM_CELL_PAYLOAD - ATM_AAL5_TRAILER); target += ATM_CELL_PAYLOAD - ATM_AAL5_TRAILER; - DEBUG_ON (--ctrl->num_cells); + --ctrl->num_cells; + DEBUG_ON (ctrl->num_cells); } memcpy (target, ctrl->aal5_trailer, ATM_AAL5_TRAILER); @@ -1243,8 +1244,10 @@ static void udsl_usb_disconnect (struct usb_interface *intf) do { count = 0; spin_lock_irq (&instance->receive_lock); - list_for_each (pos, &instance->spare_receivers) - DEBUG_ON (++count > num_rcv_urbs); + list_for_each (pos, &instance->spare_receivers) { + ++count; + DEBUG_ON (count > num_rcv_urbs); + } spin_unlock_irq (&instance->receive_lock); dbg ("udsl_usb_disconnect: found %u spare receivers", count); @@ -1279,8 +1282,10 @@ static void udsl_usb_disconnect (struct usb_interface *intf) do { count = 0; spin_lock_irq (&instance->send_lock); - list_for_each (pos, &instance->spare_senders) - DEBUG_ON (++count > num_snd_urbs); + list_for_each (pos, &instance->spare_senders) { + ++count; + DEBUG_ON (count > num_snd_urbs); + } spin_unlock_irq (&instance->send_lock); dbg ("udsl_usb_disconnect: found %u spare senders", count); -- cgit v1.2.3 From 43e6b5b421cd9b09282f0c4aa9b49e1231826e22 Mon Sep 17 00:00:00 2001 From: Duncan Sands Date: Mon, 20 Sep 2004 21:18:43 -0700 Subject: [PATCH] usb speedtch: convert to using usb_kill_urb Unlike usb_unlink_urb, usb_kill_urb guarantees that completion handlers have finished running before it returns. The bunch of disconnect code that waited for completion handlers can now go away. Signed-off-by: Duncan Sands Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/speedtch.c | 48 +++------------------------------------------ 1 file changed, 3 insertions(+), 45 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/misc/speedtch.c b/drivers/usb/misc/speedtch.c index 9e191e0a2837..c8b29eef1c7d 100644 --- a/drivers/usb/misc/speedtch.c +++ b/drivers/usb/misc/speedtch.c @@ -1220,9 +1220,7 @@ fail: static void udsl_usb_disconnect (struct usb_interface *intf) { struct udsl_instance_data *instance = usb_get_intfdata (intf); - struct list_head *pos; - unsigned int count; - int result, i; + int i; dbg ("udsl_usb_disconnect entered"); @@ -1237,27 +1235,7 @@ static void udsl_usb_disconnect (struct usb_interface *intf) tasklet_disable (&instance->receive_tasklet); for (i = 0; i < num_rcv_urbs; i++) - if ((result = usb_unlink_urb (instance->receivers [i].urb)) < 0) - dbg ("udsl_usb_disconnect: usb_unlink_urb on receive urb %d returned %d!", i, result); - - /* wait for completion handlers to finish */ - do { - count = 0; - spin_lock_irq (&instance->receive_lock); - list_for_each (pos, &instance->spare_receivers) { - ++count; - DEBUG_ON (count > num_rcv_urbs); - } - spin_unlock_irq (&instance->receive_lock); - - dbg ("udsl_usb_disconnect: found %u spare receivers", count); - - if (count == num_rcv_urbs) - break; - - set_current_state (TASK_RUNNING); - schedule (); - } while (1); + usb_kill_urb (instance->receivers [i].urb); /* no need to take the spinlock */ INIT_LIST_HEAD (&instance->filled_receive_buffers); @@ -1275,27 +1253,7 @@ static void udsl_usb_disconnect (struct usb_interface *intf) tasklet_disable (&instance->send_tasklet); for (i = 0; i < num_snd_urbs; i++) - if ((result = usb_unlink_urb (instance->senders [i].urb)) < 0) - dbg ("udsl_usb_disconnect: usb_unlink_urb on send urb %d returned %d!", i, result); - - /* wait for completion handlers to finish */ - do { - count = 0; - spin_lock_irq (&instance->send_lock); - list_for_each (pos, &instance->spare_senders) { - ++count; - DEBUG_ON (count > num_snd_urbs); - } - spin_unlock_irq (&instance->send_lock); - - dbg ("udsl_usb_disconnect: found %u spare senders", count); - - if (count == num_snd_urbs) - break; - - set_current_state (TASK_RUNNING); - schedule (); - } while (1); + usb_kill_urb (instance->senders [i].urb); /* no need to take the spinlock */ INIT_LIST_HEAD (&instance->spare_senders); -- cgit v1.2.3 From 77022ae334b1083915eae525acf72ea4931777c9 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 21 Sep 2004 00:41:16 -0700 Subject: USB: fix incorrect usage of usb_kill_urb in rtl8150 driver. Signed-off-by: Greg Kroah-Hartman --- drivers/usb/net/rtl8150.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/net/rtl8150.c b/drivers/usb/net/rtl8150.c index 4caad655aeb0..c8190731fd06 100644 --- a/drivers/usb/net/rtl8150.c +++ b/drivers/usb/net/rtl8150.c @@ -663,7 +663,7 @@ static void rtl8150_tx_timeout(struct net_device *netdev) return; warn("%s: Tx timeout.", netdev->name); dev->tx_urb->transfer_flags |= URB_ASYNC_UNLINK; - usb_kill_urb(dev->tx_urb); + usb_unlink_urb(dev->tx_urb); dev->stats.tx_errors++; } -- cgit v1.2.3 From 811c19c25076a4f68fc10570082f6bdc15d93290 Mon Sep 17 00:00:00 2001 From: Vojtech Pavlik Date: Tue, 21 Sep 2004 01:56:32 -0700 Subject: [PATCH] USB: Fix oops in usblp driver This bug was reported back in July, and I sent out a patch but apparently it never got to you. The usblp driver was calling usb_buffer_free() from usblp_cleanup(), which runs after disconnect() if a user process holds the device open. But once the usb_device is gone usb_buffer_free() will oops. The patch frees the buffers in usb_disconnect() instead. Recently Joost Witteveen reported the same oops and found that the patch solved it for him. So there shouldn't be problems with accepting it. Signed-off-by: Alan Stern Signed-off-by: Vojtech Pavlik Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/usblp.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/class/usblp.c b/drivers/usb/class/usblp.c index 3174bf78fefc..2b9a42bbd32b 100644 --- a/drivers/usb/class/usblp.c +++ b/drivers/usb/class/usblp.c @@ -397,10 +397,6 @@ static void usblp_cleanup (struct usblp *usblp) { info("usblp%d: removed", usblp->minor); - usb_buffer_free (usblp->dev, USBLP_BUF_SIZE, - usblp->writebuf, usblp->writeurb->transfer_dma); - usb_buffer_free (usblp->dev, USBLP_BUF_SIZE, - usblp->readbuf, usblp->readurb->transfer_dma); kfree (usblp->device_id_string); kfree (usblp->statusbuf); usb_free_urb(usblp->writeurb); @@ -1159,6 +1155,10 @@ static void usblp_disconnect(struct usb_interface *intf) usb_set_intfdata (intf, NULL); usblp_unlink_urbs(usblp); + usb_buffer_free (usblp->dev, USBLP_BUF_SIZE, + usblp->writebuf, usblp->writeurb->transfer_dma); + usb_buffer_free (usblp->dev, USBLP_BUF_SIZE, + usblp->readbuf, usblp->readurb->transfer_dma); if (!usblp->used) usblp_cleanup (usblp); -- cgit v1.2.3 From 8f895a819d542b33909b433229db3a5d192fa34c Mon Sep 17 00:00:00 2001 From: David Brownell Date: Tue, 21 Sep 2004 01:57:04 -0700 Subject: [PATCH] USB: net2280 updates Net2280 patches to: - Make reset logic ensure endpoint toggle and halt bits only get cleared on endpoints other than ep0 to fix an extremely unlikely (but possible) state when a setup packet come in after we've checked the reset status but before calling ep_reset(). - Avoid a disconnect hang by exchanging SUSPEND_IMMEDIATELY (intended for use when the 8051 is the PCI host) with SUSPEND_REQUEST_INTERRUPT to suspend the NET2280. - Make rmmod of gadget drivers trigger disconnect; earlier changes to reset logic broke this. Signed-off-by: Alex Sanks Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/net2280.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/net2280.c b/drivers/usb/gadget/net2280.c index 5b61f9e49959..9148f7ed4701 100644 --- a/drivers/usb/gadget/net2280.c +++ b/drivers/usb/gadget/net2280.c @@ -303,13 +303,16 @@ static void ep_reset (struct net2280_regs *regs, struct net2280_ep *ep) /* init to our chosen defaults, notably so that we NAK OUT * packets until the driver queues a read (+note erratum 0112) */ - writel ( (1 << SET_NAK_OUT_PACKETS_MODE) + tmp = (1 << SET_NAK_OUT_PACKETS_MODE) | (1 << SET_NAK_OUT_PACKETS) | (1 << CLEAR_EP_HIDE_STATUS_PHASE) - | (1 << CLEAR_INTERRUPT_MODE) - | (1 << CLEAR_ENDPOINT_TOGGLE) - | (1 << CLEAR_ENDPOINT_HALT) - , &ep->regs->ep_rsp); + | (1 << CLEAR_INTERRUPT_MODE); + + if (ep->num != 0) { + tmp |= (1 << CLEAR_ENDPOINT_TOGGLE) + | (1 << CLEAR_ENDPOINT_HALT); + } + writel (tmp, &ep->regs->ep_rsp); /* scrub most status bits, and flush any fifo state */ writel ( (1 << TIMEOUT) @@ -1919,8 +1922,6 @@ static void ep0_start (struct net2280 *dev) , &dev->usb->stdrsp); writel ( (1 << USB_ROOT_PORT_WAKEUP_ENABLE) | (1 << SELF_POWERED_USB_DEVICE) - /* erratum 0102 workaround */ - | ((dev->chiprev == 0100) ? 0 : 1) << SUSPEND_IMMEDIATELY | (1 << REMOTE_WAKEUP_SUPPORT) | (dev->softconnect << USB_DETECT_ENABLE) | (1 << SELF_POWERED_STATUS) @@ -2046,6 +2047,8 @@ int usb_gadget_unregister_driver (struct usb_gadget_driver *driver) stop_activity (dev, driver); spin_unlock_irqrestore (&dev->lock, flags); + net2280_pullup (&dev->gadget, 0); + driver->unbind (&dev->gadget); dev->gadget.dev.driver = NULL; dev->driver = NULL; @@ -2551,8 +2554,6 @@ static void handle_stat1_irqs (struct net2280 *dev, u32 stat) if (stat & (1 << SUSPEND_REQUEST_INTERRUPT)) { if (dev->driver->suspend) dev->driver->suspend (&dev->gadget); - /* we use SUSPEND_IMMEDIATELY */ - stat &= ~(1 << SUSPEND_REQUEST_INTERRUPT); } else { if (dev->driver->resume) dev->driver->resume (&dev->gadget); -- cgit v1.2.3 From cf8b58a59527b2b35f0a6db5c5e3c84185356259 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Tue, 21 Sep 2004 01:57:33 -0700 Subject: [PATCH] USB: host side fixes for pxa2xx/ethernet/rndis gadgets, like gumstix This fixes an interop glitch with PXA gadgets; please merge. This resolves an issue that's more or less specific to hosts trying to talk pxa255 based Linux devices using the Ethernet/RNDIS gadget driver. - Teaches "usbnet" about the product ID used by pxa255 based devices when they enable RNDIS, since it won't be using CDC Ethernet. - Forces usbcore config selection code to use non-RNDIS configurations, even when their class is vendor-specific (as in the pxa255 case). This makes gumstix devices, for example, talk with Linux 2.6.9 hosts AND with Windows ... previously only one would work. From: Craig Hughes Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hub.c | 12 ++++++++++-- drivers/usb/net/usbnet.c | 13 ++++++++++++- 2 files changed, 22 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index c1ba248b6aac..7090e4497b37 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -1045,11 +1045,19 @@ static int choose_configuration(struct usb_device *udev) ->altsetting->desc; if (desc->bInterfaceClass == USB_CLASS_VENDOR_SPEC) continue; - /* COMM/2/all is CDC ACM, except 0xff is MSFT RNDIS */ + /* COMM/2/all is CDC ACM, except 0xff is MSFT RNDIS. + * MSFT needs this to be the first config; never use + * it as the default unless Linux has host-side RNDIS. + * A second config would ideally be CDC-Ethernet, but + * may instead be the "vendor specific" CDC subset + * long used by ARM Linux for sa1100 or pxa255. + */ if (desc->bInterfaceClass == USB_CLASS_COMM && desc->bInterfaceSubClass == 2 - && desc->bInterfaceProtocol == 0xff) + && desc->bInterfaceProtocol == 0xff) { + c = udev->config[1].desc.bConfigurationValue; continue; + } c = udev->config[i].desc.bConfigurationValue; break; } diff --git a/drivers/usb/net/usbnet.c b/drivers/usb/net/usbnet.c index 67b2b2c12647..0ca25c357e5b 100644 --- a/drivers/usb/net/usbnet.c +++ b/drivers/usb/net/usbnet.c @@ -3308,11 +3308,18 @@ static const struct usb_device_id products [] = { * * PXA25x or PXA210 ... these use a "usb-eth" driver much like * the sa1100 one, but hardware uses different endpoint numbers. + * + * Or the Linux "Ethernet" gadget on hardware that can't talk + * CDC Ethernet (e.g., no altsettings), in either of two modes: + * - acting just like the old "usb-eth" firmware, though + * the implementation is different + * - supporting RNDIS as the first/default configuration for + * MS-Windows interop; Linux needs to use the other config */ { // 1183 = 0x049F, both used as hex values? // Compaq "Itsy" vendor/product id - USB_DEVICE (0x049F, 0x505A), + USB_DEVICE (0x049F, 0x505A), // usb-eth, or compatible .driver_info = (unsigned long) &linuxdev_info, }, { USB_DEVICE (0x0E7E, 0x1001), // G.Mate "Yopy" @@ -3320,6 +3327,10 @@ static const struct usb_device_id products [] = { }, { USB_DEVICE (0x8086, 0x07d3), // "blob" bootloader .driver_info = (unsigned long) &blob_info, +}, { + // Linux Ethernet/RNDIS gadget on pxa210/25x/26x + USB_DEVICE_VER (0x0525, 0xa4a2, 0x0203, 0x0203), + .driver_info = (unsigned long) &linuxdev_info, }, #endif -- cgit v1.2.3 From 2a7b57bd9490ab88ff1997d99d517d3254652f7d Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 21 Sep 2004 01:58:11 -0700 Subject: [PATCH] USB: Support system suspend in File-Storage Gadget This patch adds support for system-wide suspend to the File-Storage Gadget. Please apply. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/file_storage.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c index 6987b7c3e7c3..c0f131f5e235 100644 --- a/drivers/usb/gadget/file_storage.c +++ b/drivers/usb/gadget/file_storage.c @@ -234,6 +234,7 @@ #include #include #include +#include #include #include #include @@ -1540,6 +1541,8 @@ static int sleep_thread(struct fsg_dev *fsg) rc = wait_event_interruptible(fsg->thread_wqh, fsg->thread_wakeup_needed); fsg->thread_wakeup_needed = 0; + if (current->flags & PF_FREEZE) + refrigerator(PF_FREEZE); return (rc ? -EINTR : 0); } -- cgit v1.2.3 From d021dd823f451f18dd752b1b4fbbcae7e88e76cc Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 21 Sep 2004 01:58:41 -0700 Subject: [PATCH] USB: Suspend update for dummy_hcd This patch fixes a few minor errors in the port suspend/resume handling code of the dummy_hcd driver. There are a couple of other small changes too, such as removing a BUG_ON(). Please apply. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/dummy_hcd.c | 39 +++++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c index 91e878642b9f..ed532f7176f9 100644 --- a/drivers/usb/gadget/dummy_hcd.c +++ b/drivers/usb/gadget/dummy_hcd.c @@ -770,7 +770,8 @@ usb_gadget_unregister_driver (struct usb_gadget_driver *driver) spin_lock_irqsave (&dum->lock, flags); stop_activity (dum, driver); - dum->port_status &= ~USB_PORT_STAT_CONNECTION; + dum->port_status &= ~(USB_PORT_STAT_CONNECTION | USB_PORT_STAT_ENABLE | + USB_PORT_STAT_LOW_SPEED | USB_PORT_STAT_HIGH_SPEED); dum->port_status |= (1 << USB_PORT_FEAT_C_CONNECTION); spin_unlock_irqrestore (&dum->lock, flags); @@ -815,8 +816,8 @@ static int dummy_urb_enqueue ( struct dummy *dum; unsigned long flags; - /* patch to usb_sg_init() is in 2.5.60 */ - BUG_ON (!urb->transfer_buffer && urb->transfer_buffer_length); + if (!urb->transfer_buffer && urb->transfer_buffer_length) + return -EINVAL; dum = container_of (hcd, struct dummy, hcd); spin_lock_irqsave (&dum->lock, flags); @@ -1102,10 +1103,10 @@ restart: ep = find_endpoint(dum, address); if (!ep) { /* set_configuration() disagreement */ - dev_err (hardware, + dev_dbg (hardware, "no ep configured for urb %p\n", urb); - maybe_set_status (urb, -ETIMEDOUT); + maybe_set_status (urb, -EPROTO); goto return_urb; } @@ -1409,9 +1410,12 @@ static int dummy_hub_control ( case ClearPortFeature: switch (wValue) { case USB_PORT_FEAT_SUSPEND: - /* 20msec resume signaling */ - dum->resuming = 1; - dum->re_timeout = jiffies + ((HZ * 20)/1000); + if (dum->port_status & (1 << USB_PORT_FEAT_SUSPEND)) { + /* 20msec resume signaling */ + dum->resuming = 1; + dum->re_timeout = jiffies + + msecs_to_jiffies(20); + } break; case USB_PORT_FEAT_POWER: dum->port_status = 0; @@ -1440,7 +1444,7 @@ static int dummy_hub_control ( dum->port_status &= ~(1 << USB_PORT_FEAT_SUSPEND); dum->resuming = 0; dum->re_timeout = 0; - if (dum->driver->resume) { + if (dum->driver && dum->driver->resume) { spin_unlock (&dum->lock); dum->driver->resume (&dum->gadget); spin_lock (&dum->lock); @@ -1481,11 +1485,15 @@ static int dummy_hub_control ( case SetPortFeature: switch (wValue) { case USB_PORT_FEAT_SUSPEND: - dum->port_status |= (1 << USB_PORT_FEAT_SUSPEND); - if (dum->driver->suspend) { - spin_unlock (&dum->lock); - dum->driver->suspend (&dum->gadget); - spin_lock (&dum->lock); + if ((dum->port_status & (1 << USB_PORT_FEAT_SUSPEND)) + == 0) { + dum->port_status |= + (1 << USB_PORT_FEAT_SUSPEND); + if (dum->driver && dum->driver->suspend) { + spin_unlock (&dum->lock); + dum->driver->suspend (&dum->gadget); + spin_lock (&dum->lock); + } } break; case USB_PORT_FEAT_RESET: @@ -1502,7 +1510,7 @@ static int dummy_hub_control ( /* FIXME test that code path! */ } /* 50msec reset signaling */ - dum->re_timeout = jiffies + ((HZ * 50)/1000); + dum->re_timeout = jiffies + msecs_to_jiffies(50); /* FALLTHROUGH */ default: dum->port_status |= (1 << wValue); @@ -1790,4 +1798,3 @@ static void __exit cleanup (void) the_controller = 0; } module_exit (cleanup); - -- cgit v1.2.3 From baf4a4c3a6b857b34f67708078507716e144eae1 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 21 Sep 2004 01:59:23 -0700 Subject: [PATCH] USB: Activate new hubs and resumed hubs the same way This patch makes the newly-probed hub pathway, the hub reset pathway, and the resumed hub pathway all use the same routine for activating the hub and scanning for port change events as soon as possible. It's a modified version of something originally written by David Brownell, updated to match the current source. Please apply. Signed-off-by: David Brownell Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hub.c | 76 ++++++++++++++++++++++---------------------------- 1 file changed, 33 insertions(+), 43 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 7090e4497b37..10baa8f52566 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -395,6 +395,33 @@ static void hub_power_on(struct usb_hub *hub) msleep(hub->descriptor->bPwrOn2PwrGood * 2); } +static void hub_quiesce(struct usb_hub *hub) +{ + /* stop khubd and related activity */ + hub->quiescing = 1; + usb_kill_urb(hub->urb); + if (hub->has_indicators) + cancel_delayed_work(&hub->leds); + if (hub->has_indicators || hub->tt.hub) + flush_scheduled_work(); +} + +static void hub_activate(struct usb_hub *hub) +{ + int status; + + hub->quiescing = 0; + status = usb_submit_urb(hub->urb, GFP_NOIO); + if (status < 0) + dev_err(&hub->intf->dev, "activate --> %d\n", status); + if (hub->has_indicators && blinkenlights) + schedule_delayed_work(&hub->leds, LED_CYCLE_PERIOD); + + /* scan all ports ASAP */ + hub->event_bits[0] = ~0; + kick_khubd(hub); +} + static int hub_hub_status(struct usb_hub *hub, u16 *status, u16 *change) { @@ -590,10 +617,7 @@ static int hub_configure(struct usb_hub *hub, dev_dbg(hub_dev, "%sover-current condition exists\n", (hubstatus & HUB_STATUS_OVERCURRENT) ? "" : "no "); - /* scan all ports ASAP on new hubs */ - hub->change_bits[0] = ~0; - - /* Start the interrupt endpoint */ + /* set up the interrupt endpoint */ pipe = usb_rcvintpipe(hdev, endpoint->bEndpointAddress); maxp = usb_maxpacket(hdev, pipe, usb_pipeout(pipe)); @@ -611,24 +635,13 @@ static int hub_configure(struct usb_hub *hub, hub, endpoint->bInterval); hub->urb->transfer_dma = hub->buffer_dma; hub->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; - ret = usb_submit_urb(hub->urb, GFP_KERNEL); - if (ret) { - message = "couldn't submit status urb"; - goto fail; - } - /* Wake up khubd */ - kick_khubd(hub); - - /* maybe start cycling the hub leds */ - if (hub->has_indicators && blinkenlights) { - set_port_led(hdev, 1, HUB_LED_GREEN); + /* maybe cycle the hub leds */ + if (hub->has_indicators && blinkenlights) hub->indicator [0] = INDICATOR_CYCLE; - schedule_delayed_work(&hub->leds, LED_CYCLE_PERIOD); - } hub_power_on(hub); - + hub_activate(hub); return 0; fail: @@ -640,29 +653,6 @@ fail: static unsigned highspeed_hubs; -static void hub_quiesce(struct usb_hub *hub) -{ - /* stop khubd and related activity */ - hub->quiescing = 1; - usb_kill_urb(hub->urb); - if (hub->has_indicators) - cancel_delayed_work(&hub->leds); - if (hub->has_indicators || hub->tt.hub) - flush_scheduled_work(); -} - -static void hub_reactivate(struct usb_hub *hub) -{ - int status; - - hub->quiescing = 0; - status = usb_submit_urb(hub->urb, GFP_NOIO); - if (status < 0) - dev_err(&hub->intf->dev, "reactivate --> %d\n", status); - if (hub->has_indicators && blinkenlights) - schedule_delayed_work(&hub->leds, LED_CYCLE_PERIOD); -} - static void hub_disconnect(struct usb_interface *intf) { struct usb_hub *hub = usb_get_intfdata (intf); @@ -822,7 +812,7 @@ static void hub_post_reset(struct usb_device *hdev) { struct usb_hub *hub = usb_get_intfdata(hdev->actconfig->interface[0]); - hub_reactivate(hub); + hub_activate(hub); hub_power_on(hub); } @@ -1905,7 +1895,7 @@ static int hub_resume(struct usb_interface *intf) } intf->dev.power.power_state = PM_SUSPEND_ON; - hub_reactivate(hub); + hub_activate(hub); return 0; } -- cgit v1.2.3 From a2ea17cb132468fabad09b94f78662000fb937c6 Mon Sep 17 00:00:00 2001 From: "David T. Hollis" Date: Tue, 21 Sep 2004 01:59:57 -0700 Subject: [PATCH] USB: Add Surecom USB Ethernet device ids to usbnet Add Surecom EP-1427X-2 device ids. Signed-off-by: David Hollis Signed-off-by: Greg Kroah-Hartman --- drivers/usb/net/usbnet.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/net/usbnet.c b/drivers/usb/net/usbnet.c index 0ca25c357e5b..380cc5db14d4 100644 --- a/drivers/usb/net/usbnet.c +++ b/drivers/usb/net/usbnet.c @@ -3252,6 +3252,10 @@ static const struct usb_device_id products [] = { // Sitecom LN-029 "USB 2.0 10/100 Ethernet adapter" USB_DEVICE (0x6189, 0x182d), .driver_info = (unsigned long) &ax8817x_info, +}, { + // Surecom EP-1427X-2 + USB_DEVICE (0x1189, 0x0893), + .driver_info = (unsigned long) &ax8817x_info, }, #endif -- cgit v1.2.3 From 0a5b752b9d2dd13041ae008abe2d8fa6cd75fbb7 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 21 Sep 2004 02:00:26 -0700 Subject: [PATCH] USB: Use list_for_each_entry etc. in UHCI driver This patch was developed largely by Domen Puncer. It makes the code in the UHCI driver more readable by utilizing list_for_each_entry and related macros. I merely made a couple of very small stylistic changes and converted one additional hunk of code. Signed-off-by: Domen Puncer Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/uhci-hcd.c | 237 ++++++++++---------------------------------- 1 file changed, 52 insertions(+), 185 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index fe924e65ba35..07076731937f 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c @@ -230,42 +230,22 @@ static void uhci_remove_td(struct uhci_hcd *uhci, struct uhci_td *td) } /* - * Inserts a td into qh list at the top. + * Inserts a td list into qh. */ static void uhci_insert_tds_in_qh(struct uhci_qh *qh, struct urb *urb, u32 breadth) { - struct list_head *tmp, *head; struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; - struct uhci_td *td, *ptd; - - if (list_empty(&urbp->td_list)) - return; - - head = &urbp->td_list; - tmp = head->next; + struct uhci_td *td; + u32 *plink; /* Ordering isn't important here yet since the QH hasn't been */ - /* inserted into the schedule yet */ - td = list_entry(tmp, struct uhci_td, list); - - /* Add the first TD to the QH element pointer */ - qh->element = cpu_to_le32(td->dma_handle) | breadth; - - ptd = td; - - /* Then link the rest of the TD's */ - tmp = tmp->next; - while (tmp != head) { - td = list_entry(tmp, struct uhci_td, list); - - tmp = tmp->next; - - ptd->link = cpu_to_le32(td->dma_handle) | breadth; - - ptd = td; + /* inserted into the schedule yet */ + plink = &qh->element; + list_for_each_entry(td, &urbp->td_list, list) { + *plink = cpu_to_le32(td->dma_handle) | breadth; + plink = &td->link; } - - ptd->link = UHCI_PTR_TERM; + *plink = UHCI_PTR_TERM; } static void uhci_free_td(struct uhci_hcd *uhci, struct uhci_td *td) @@ -330,7 +310,7 @@ static void uhci_free_qh(struct uhci_hcd *uhci, struct uhci_qh *qh) static void uhci_insert_qh(struct uhci_hcd *uhci, struct uhci_qh *skelqh, struct urb *urb) { struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; - struct list_head *tmp; + struct urb_priv *turbp; struct uhci_qh *lqh; /* Grab the last QH */ @@ -358,12 +338,8 @@ static void uhci_insert_qh(struct uhci_hcd *uhci, struct uhci_qh *skelqh, struct */ lqh->link = cpu_to_le32(urbp->qh->dma_handle) | UHCI_PTR_QH; if (lqh->urbp) { - list_for_each (tmp, &lqh->urbp->queue_list) { - struct urb_priv *turbp = - list_entry(tmp, struct urb_priv, queue_list); - + list_for_each_entry(turbp, &lqh->urbp->queue_list, queue_list) turbp->qh->link = lqh->link; - } } list_add_tail(&urbp->qh->list, &skelqh->list); @@ -405,18 +381,11 @@ static void uhci_remove_qh(struct uhci_hcd *uhci, struct uhci_qh *qh) pqh = list_entry(qh->list.prev, struct uhci_qh, list); pqh->link = newlink; if (pqh->urbp) { - struct list_head *head, *tmp; - - head = &pqh->urbp->queue_list; - tmp = head->next; - while (head != tmp) { - struct urb_priv *turbp = - list_entry(tmp, struct urb_priv, queue_list); - - tmp = tmp->next; + struct urb_priv *turbp; + list_for_each_entry(turbp, &pqh->urbp->queue_list, + queue_list) turbp->qh->link = newlink; - } } wmb(); @@ -447,21 +416,14 @@ static void uhci_remove_qh(struct uhci_hcd *uhci, struct uhci_qh *qh) static int uhci_fixup_toggle(struct urb *urb, unsigned int toggle) { struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; - struct list_head *head, *tmp; - - head = &urbp->td_list; - tmp = head->next; - while (head != tmp) { - struct uhci_td *td = list_entry(tmp, struct uhci_td, list); - - tmp = tmp->next; + struct uhci_td *td; + list_for_each_entry(td, &urbp->td_list, list) { if (toggle) td->token |= cpu_to_le32(TD_TOKEN_TOGGLE); else td->token &= ~cpu_to_le32(TD_TOKEN_TOGGLE); - toggle ^= 1; } @@ -473,30 +435,19 @@ static int uhci_fixup_toggle(struct urb *urb, unsigned int toggle) static void uhci_append_queued_urb(struct uhci_hcd *uhci, struct urb *eurb, struct urb *urb) { struct urb_priv *eurbp, *urbp, *furbp, *lurbp; - struct list_head *tmp; struct uhci_td *lltd; eurbp = eurb->hcpriv; urbp = urb->hcpriv; /* Find the first URB in the queue */ + furbp = eurbp; if (eurbp->queued) { - struct list_head *head = &eurbp->queue_list; - - tmp = head->next; - while (tmp != head) { - struct urb_priv *turbp = - list_entry(tmp, struct urb_priv, queue_list); - - if (!turbp->queued) + list_for_each_entry(furbp, &eurbp->queue_list, queue_list) + if (!furbp->queued) break; + } - tmp = tmp->next; - } - } else - tmp = &eurbp->queue_list; - - furbp = list_entry(tmp, struct urb_priv, queue_list); lurbp = list_entry(furbp->queue_list.prev, struct urb_priv, queue_list); lltd = list_entry(lurbp->td_list.prev, struct uhci_td, list); @@ -522,9 +473,7 @@ static void uhci_append_queued_urb(struct uhci_hcd *uhci, struct urb *eurb, stru static void uhci_delete_queued_urb(struct uhci_hcd *uhci, struct urb *urb) { - struct urb_priv *urbp, *nurbp; - struct list_head *head, *tmp; - struct urb_priv *purbp; + struct urb_priv *urbp, *nurbp, *purbp, *turbp; struct uhci_td *pltd; unsigned int toggle; @@ -556,14 +505,7 @@ static void uhci_delete_queued_urb(struct uhci_hcd *uhci, struct urb *urb) toggle = uhci_toggle(td_token(pltd)) ^ 1; } - head = &urbp->queue_list; - tmp = head->next; - while (head != tmp) { - struct urb_priv *turbp; - - turbp = list_entry(tmp, struct urb_priv, queue_list); - tmp = tmp->next; - + list_for_each_entry(turbp, &urbp->queue_list, queue_list) { if (!turbp->queued) break; toggle = uhci_fixup_toggle(turbp->urb, toggle); @@ -637,7 +579,7 @@ static void uhci_remove_td_from_urb(struct uhci_td *td) static void uhci_destroy_urb_priv(struct uhci_hcd *uhci, struct urb *urb) { - struct list_head *head, *tmp; + struct uhci_td *td, *tmp; struct urb_priv *urbp; unsigned int age; @@ -660,13 +602,7 @@ static void uhci_destroy_urb_priv(struct uhci_hcd *uhci, struct urb *urb) if (list_empty(&uhci->td_remove_list)) uhci_set_next_interrupt(uhci); - head = &urbp->td_list; - tmp = head->next; - while (tmp != head) { - struct uhci_td *td = list_entry(tmp, struct uhci_td, list); - - tmp = tmp->next; - + list_for_each_entry_safe(td, tmp, &urbp->td_list, list) { uhci_remove_td_from_urb(td); uhci_remove_td(uhci, td); list_add(&td->remove_list, &uhci->td_remove_list); @@ -1083,7 +1019,6 @@ static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb, struct urb */ static int uhci_result_common(struct uhci_hcd *uhci, struct urb *urb) { - struct list_head *tmp, *head; struct urb_priv *urbp = urb->hcpriv; struct uhci_td *td; unsigned int status = 0; @@ -1091,13 +1026,7 @@ static int uhci_result_common(struct uhci_hcd *uhci, struct urb *urb) urb->actual_length = 0; - head = &urbp->td_list; - tmp = head->next; - while (tmp != head) { - td = list_entry(tmp, struct uhci_td, list); - - tmp = tmp->next; - + list_for_each_entry(td, &urbp->td_list, list) { status = uhci_status_bits(td_status(td)); if (status & TD_CTRL_ACTIVE) return -EINPROGRESS; @@ -1176,17 +1105,12 @@ static inline int uhci_submit_interrupt(struct uhci_hcd *uhci, struct urb *urb, static int isochronous_find_limits(struct uhci_hcd *uhci, struct urb *urb, unsigned int *start, unsigned int *end) { struct urb *last_urb = NULL; - struct list_head *tmp, *head; + struct urb_priv *up; int ret = 0; - head = &uhci->urb_list; - tmp = head->next; - while (tmp != head) { - struct urb_priv *up = list_entry(tmp, struct urb_priv, urb_list); + list_for_each_entry(up, &uhci->urb_list, urb_list) { struct urb *u = up->urb; - tmp = tmp->next; - /* look for pending URB's with identical pipe handle */ if ((urb->pipe == u->pipe) && (urb->dev == u->dev) && (u->status == -EINPROGRESS) && (u != urb)) { @@ -1272,7 +1196,7 @@ static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb) static int uhci_result_isochronous(struct uhci_hcd *uhci, struct urb *urb) { - struct list_head *tmp, *head; + struct uhci_td *td; struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; int status; int i, ret = 0; @@ -1280,14 +1204,9 @@ static int uhci_result_isochronous(struct uhci_hcd *uhci, struct urb *urb) urb->actual_length = 0; i = 0; - head = &urbp->td_list; - tmp = head->next; - while (tmp != head) { - struct uhci_td *td = list_entry(tmp, struct uhci_td, list); + list_for_each_entry(td, &urbp->td_list, list) { int actlength; - tmp = tmp->next; - if (td_status(td) & TD_CTRL_ACTIVE) return -EINPROGRESS; @@ -1311,20 +1230,15 @@ static int uhci_result_isochronous(struct uhci_hcd *uhci, struct urb *urb) static struct urb *uhci_find_urb_ep(struct uhci_hcd *uhci, struct urb *urb) { - struct list_head *tmp, *head; + struct urb_priv *up; /* We don't match Isoc transfers since they are special */ if (usb_pipeisoc(urb->pipe)) return NULL; - head = &uhci->urb_list; - tmp = head->next; - while (tmp != head) { - struct urb_priv *up = list_entry(tmp, struct urb_priv, urb_list); + list_for_each_entry(up, &uhci->urb_list, urb_list) { struct urb *u = up->urb; - tmp = tmp->next; - if (u->dev == urb->dev && u->status == -EINPROGRESS) { /* For control, ignore the direction */ if (usb_pipecontrol(urb->pipe) && @@ -1475,7 +1389,8 @@ out: static void uhci_unlink_generic(struct uhci_hcd *uhci, struct urb *urb) { - struct list_head *head, *tmp; + struct list_head *head; + struct uhci_td *td; struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; int prevactive = 1; @@ -1495,15 +1410,10 @@ static void uhci_unlink_generic(struct uhci_hcd *uhci, struct urb *urb) * for all types */ head = &urbp->td_list; - tmp = head->next; - while (tmp != head) { - struct uhci_td *td = list_entry(tmp, struct uhci_td, list); - - tmp = tmp->next; - + list_for_each_entry(td, head, list) { if (!(td_status(td) & TD_CTRL_ACTIVE) && (uhci_actual_length(td_status(td)) < uhci_expected_length(td_token(td)) || - tmp == head)) + td->list.next == head)) usb_settoggle(urb->dev, uhci_endpoint(td_token(td)), uhci_packetout(td_token(td)), uhci_toggle(td_token(td)) ^ 1); @@ -1556,7 +1466,8 @@ done: static int uhci_fsbr_timeout(struct uhci_hcd *uhci, struct urb *urb) { struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; - struct list_head *head, *tmp; + struct list_head *head; + struct uhci_td *td; int count = 0; uhci_dec_fsbr(uhci, urb); @@ -1570,18 +1481,14 @@ static int uhci_fsbr_timeout(struct uhci_hcd *uhci, struct urb *urb) */ head = &urbp->td_list; - tmp = head->next; - while (tmp != head) { - struct uhci_td *td = list_entry(tmp, struct uhci_td, list); - - tmp = tmp->next; - + list_for_each_entry(td, head, list) { /* * Make sure we don't do the last one (since it'll have the * TERM bit set) as well as we skip every so many TD's to * make sure it doesn't hog the bandwidth */ - if (tmp != head && (count % DEPTH_INTERVAL) == (DEPTH_INTERVAL - 1)) + if (td->list.next != head && (count % DEPTH_INTERVAL) == + (DEPTH_INTERVAL - 1)) td->link |= UHCI_PTR_DEPTH; count++; @@ -1606,12 +1513,10 @@ static void stall_callback(unsigned long ptr) { struct usb_hcd *hcd = (struct usb_hcd *)ptr; struct uhci_hcd *uhci = hcd_to_uhci(hcd); - struct list_head list, *tmp, *head; + struct urb_priv *up; unsigned long flags; int called_uhci_finish_completion = 0; - INIT_LIST_HEAD(&list); - spin_lock_irqsave(&uhci->schedule_lock, flags); if (!list_empty(&uhci->urb_remove_list) && uhci_get_current_frame_number(uhci) != uhci->urb_remove_age) { @@ -1620,14 +1525,9 @@ static void stall_callback(unsigned long ptr) called_uhci_finish_completion = 1; } - head = &uhci->urb_list; - tmp = head->next; - while (tmp != head) { - struct urb_priv *up = list_entry(tmp, struct urb_priv, urb_list); + list_for_each_entry(up, &uhci->urb_list, urb_list) { struct urb *u = up->urb; - tmp = tmp->next; - spin_lock(&u->lock); /* Check if the FSBR timed out */ @@ -1642,17 +1542,6 @@ static void stall_callback(unsigned long ptr) if (called_uhci_finish_completion) wake_up_all(&uhci->waitqh); - head = &list; - tmp = head->next; - while (tmp != head) { - struct urb_priv *up = list_entry(tmp, struct urb_priv, urb_list); - struct urb *u = up->urb; - - tmp = tmp->next; - - uhci_urb_dequeue(hcd, u); - } - /* Really disable FSBR */ if (!uhci->fsbr && uhci->fsbrtimeout && time_after_eq(jiffies, uhci->fsbrtimeout)) { uhci->fsbrtimeout = 0; @@ -1682,15 +1571,9 @@ static int init_stall_timer(struct usb_hcd *hcd) static void uhci_free_pending_qhs(struct uhci_hcd *uhci) { - struct list_head *tmp, *head; - - head = &uhci->qh_remove_list; - tmp = head->next; - while (tmp != head) { - struct uhci_qh *qh = list_entry(tmp, struct uhci_qh, remove_list); - - tmp = tmp->next; + struct uhci_qh *qh, *tmp; + list_for_each_entry_safe(qh, tmp, &uhci->qh_remove_list, remove_list) { list_del_init(&qh->remove_list); uhci_free_qh(uhci, qh); @@ -1699,15 +1582,9 @@ static void uhci_free_pending_qhs(struct uhci_hcd *uhci) static void uhci_free_pending_tds(struct uhci_hcd *uhci) { - struct list_head *tmp, *head; - - head = &uhci->td_remove_list; - tmp = head->next; - while (tmp != head) { - struct uhci_td *td = list_entry(tmp, struct uhci_td, remove_list); - - tmp = tmp->next; + struct uhci_td *td, *tmp; + list_for_each_entry_safe(td, tmp, &uhci->td_remove_list, remove_list) { list_del_init(&td->remove_list); uhci_free_td(uhci, td); @@ -1728,19 +1605,13 @@ static void uhci_finish_urb(struct usb_hcd *hcd, struct urb *urb, struct pt_regs static void uhci_finish_completion(struct usb_hcd *hcd, struct pt_regs *regs) { struct uhci_hcd *uhci = hcd_to_uhci(hcd); - struct list_head *tmp, *head; + struct urb_priv *urbp, *tmp; - head = &uhci->complete_list; - tmp = head->next; - while (tmp != head) { - struct urb_priv *urbp = list_entry(tmp, struct urb_priv, urb_list); + list_for_each_entry_safe(urbp, tmp, &uhci->complete_list, urb_list) { struct urb *urb = urbp->urb; list_del_init(&urbp->urb_list); uhci_finish_urb(hcd, urb, regs); - - head = &uhci->complete_list; - tmp = head->next; } } @@ -1756,7 +1627,7 @@ static irqreturn_t uhci_irq(struct usb_hcd *hcd, struct pt_regs *regs) struct uhci_hcd *uhci = hcd_to_uhci(hcd); unsigned long io_addr = uhci->io_addr; unsigned short status; - struct list_head *tmp, *head; + struct urb_priv *urbp, *tmp; unsigned int age; /* @@ -1803,15 +1674,11 @@ static irqreturn_t uhci_irq(struct usb_hcd *hcd, struct pt_regs *regs) else uhci_set_next_interrupt(uhci); - /* Walk the list of pending URB's to see which ones completed */ - head = &uhci->urb_list; - tmp = head->next; - while (tmp != head) { - struct urb_priv *urbp = list_entry(tmp, struct urb_priv, urb_list); + /* Walk the list of pending URBs to see which ones completed + * (must be _safe because uhci_transfer_result() dequeues URBs) */ + list_for_each_entry_safe(urbp, tmp, &uhci->urb_list, urb_list) { struct urb *urb = urbp->urb; - tmp = tmp->next; - /* Checks the status and does all of the magic necessary */ uhci_transfer_result(uhci, urb); } -- cgit v1.2.3 From 349740f3b9bec2ae96455baea83b8e17cc64ed75 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 21 Sep 2004 02:00:55 -0700 Subject: [PATCH] USB: Fix data toggle handling in the UHCI driver This patch fixes an error in the way the UHCI driver computes data-toggle updates when unlinking a bulk or interrupt URB that hasn't transferred any data. Thanks to Thierry Chantry for finding this bug and testing the patch. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/uhci-hcd.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index 07076731937f..ed6c7f86c6ba 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c @@ -1392,7 +1392,7 @@ static void uhci_unlink_generic(struct uhci_hcd *uhci, struct urb *urb) struct list_head *head; struct uhci_td *td; struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; - int prevactive = 1; + int prevactive = 0; uhci_dec_fsbr(uhci, urb); /* Safe since it checks */ @@ -1400,20 +1400,28 @@ static void uhci_unlink_generic(struct uhci_hcd *uhci, struct urb *urb) * Now we need to find out what the last successful toggle was * so we can update the local data toggle for the next transfer * - * There's 3 way's the last successful completed TD is found: + * There are 2 ways the last successful completed TD is found: * * 1) The TD is NOT active and the actual length < expected length * 2) The TD is NOT active and it's the last TD in the chain + * + * and a third way the first uncompleted TD is found: + * * 3) The TD is active and the previous TD is NOT active * * Control and Isochronous ignore the toggle, so this is safe * for all types + * + * FIXME: The toggle fixups won't be 100% reliable until we + * change over to using a single queue for each endpoint and + * stop the queue before unlinking. */ head = &urbp->td_list; list_for_each_entry(td, head, list) { if (!(td_status(td) & TD_CTRL_ACTIVE) && - (uhci_actual_length(td_status(td)) < uhci_expected_length(td_token(td)) || - td->list.next == head)) + (uhci_actual_length(td_status(td)) < + uhci_expected_length(td_token(td)) || + td->list.next == head)) usb_settoggle(urb->dev, uhci_endpoint(td_token(td)), uhci_packetout(td_token(td)), uhci_toggle(td_token(td)) ^ 1); -- cgit v1.2.3 From 1116edbfbb6c1165851257da882f443dca63ae14 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Tue, 28 Sep 2004 01:56:39 -0700 Subject: [PATCH] PCI: fix up usb quirk __init marks Lukas Hejtmanek wrote: > > Unable to handle kernel paging request at virtual address 0000ffff > printing eip: > c0402097 > *pde = 00000000 > Oops: 0002 [#1] > PREEMPT > Modules linked in: yenta_socket pcmcia_core i830 ehci_hcd uhci_hcd rtc > CPU: 0 > EIP: 0060:[] Tainted: P VLI > EFLAGS: 00010246 (2.6.9-rc2-mm2) > EIP is at quirk_usb_early_handoff+0x0/0x3e > eax: 0000ffff ebx: c035d954 ecx: c6866000 edx: 00020000 > esi: c6866000 edi: c035da4c ebp: ceede380 esp: c7f4bed8 > ds: 007b es: 007b ss: 0068 > Process pccardd (pid: 3386, threadinfo=c7f4a000 task=c5ea2d70) > Stack: c01d2076 c6866000 c6866000 ceede380 00000000 c01d20ba c6866000 c035d81c > c035da4c c01d01ce 00000000 c6866000 00000000 00000000 c01d0214 ceede380 > 00000000 c686642c ceede380 ceede394 c7f4a000 cfbdd0de ceede380 00000000 > Call Trace: > [] pci_do_fixups+0x49/0x4b Well quirk_usb_early_handoff() should be __devinit, not __init. There are a few other things in there which look hotpluggy, and are marked __init. The whole thing needs a review. Signed-off-by: Greg Kroah-Hartman --- drivers/pci/quirks.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 8ded4fe07f51..6579764b95d2 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -866,7 +866,7 @@ static int __init usb_handoff_early(char *str) } __setup("usb-handoff", usb_handoff_early); -static void __init quirk_usb_handoff_uhci(struct pci_dev *pdev) +static void __devinit quirk_usb_handoff_uhci(struct pci_dev *pdev) { unsigned long base = 0; int wait_time, delta; @@ -919,7 +919,7 @@ static void __init quirk_usb_handoff_uhci(struct pci_dev *pdev) } -static void __init quirk_usb_handoff_ohci(struct pci_dev *pdev) +static void __devinit quirk_usb_handoff_ohci(struct pci_dev *pdev) { void __iomem *base; int wait_time; @@ -949,7 +949,7 @@ static void __init quirk_usb_handoff_ohci(struct pci_dev *pdev) iounmap(base); } -static void __init quirk_usb_disable_ehci(struct pci_dev *pdev) +static void __devinit quirk_usb_disable_ehci(struct pci_dev *pdev) { int wait_time, delta; void __iomem *base, *op_reg_base; @@ -1039,7 +1039,7 @@ static void __init quirk_usb_disable_ehci(struct pci_dev *pdev) -static void __init quirk_usb_early_handoff(struct pci_dev *pdev) +static void __devinit quirk_usb_early_handoff(struct pci_dev *pdev) { if (!usb_early_handoff) return; -- cgit v1.2.3 From 3bc5c62a11835a7317fc18512256952cb8cb9ca9 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Tue, 28 Sep 2004 21:24:23 -0700 Subject: [PATCH] USB Gadget: Ethernet/RNDIS gadget, minor updates Helps battery recharge, and gives this a new version string since it's changed a lot since St. Patrick's Day. Just minor updates: - use usb_gadget_vbus_draw() when changing configs, so that USB can be used to recharge batteries (or whatever); and report power use. bMaxPower is now 100 mA except with OTG (where it's 8mA). - rename /proc/rndis/000 as /proc/driver/rndis-000, and only have it when a new CONFIG variable is set (added by a later patch) - minor fixes to make some debug-only rndis messages compile - version is now "Equinox 2004". Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/ether.c | 40 +++++++++++++++++++++++------------- drivers/usb/gadget/rndis.c | 51 ++++++++++++++++++++++------------------------ 2 files changed, 50 insertions(+), 41 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c index 0c5f37c2ba0a..e3fec4a5a7f8 100644 --- a/drivers/usb/gadget/ether.c +++ b/drivers/usb/gadget/ether.c @@ -84,7 +84,7 @@ */ #define DRIVER_DESC "Ethernet Gadget" -#define DRIVER_VERSION "St Patrick's Day 2004" +#define DRIVER_VERSION "Equinox 2004" static const char shortname [] = "ether"; static const char driver_desc [] = DRIVER_DESC; @@ -391,7 +391,7 @@ eth_config = { .bConfigurationValue = DEV_CONFIG_VALUE, .iConfiguration = STRING_CDC, .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, - .bMaxPower = 1, + .bMaxPower = 50, }; #ifdef CONFIG_USB_ETH_RNDIS @@ -405,7 +405,7 @@ rndis_config = { .bConfigurationValue = DEV_RNDIS_CONFIG_VALUE, .iConfiguration = STRING_RNDIS, .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, - .bMaxPower = 1, + .bMaxPower = 50, }; #endif @@ -1202,13 +1202,20 @@ eth_set_config (struct eth_dev *dev, unsigned number, int gfp_flags) result = -EINVAL; /* FALL THROUGH */ case 0: - return result; + break; } - if (result) - eth_reset_config (dev); - else { + if (result) { + if (number) + eth_reset_config (dev); + usb_gadget_vbus_draw(dev->gadget, + dev->gadget->is_otg ? 8 : 100); + } else { char *speed; + unsigned power; + + power = 2 * eth_config.bMaxPower; + usb_gadget_vbus_draw(dev->gadget, power); switch (gadget->speed) { case USB_SPEED_FULL: speed = "full"; break; @@ -1219,8 +1226,8 @@ eth_set_config (struct eth_dev *dev, unsigned number, int gfp_flags) } dev->config = number; - INFO (dev, "%s speed config #%d: %s, using %s\n", - speed, number, driver_desc, + INFO (dev, "%s speed config #%d: %d mA, %s, using %s\n", + speed, number, power, driver_desc, dev->rndis ? "RNDIS" : (dev->cdc @@ -1379,8 +1386,9 @@ static void eth_setup_complete (struct usb_ep *ep, struct usb_request *req) static void rndis_response_complete (struct usb_ep *ep, struct usb_request *req) { if (req->status || req->actual != req->length) - DEBUG (dev, "rndis response complete --> %d, %d/%d\n", - req->status, req->actual, req->length); + DEBUG ((struct eth_dev *) ep->driver_data, + "rndis response complete --> %d, %d/%d\n", + req->status, req->actual, req->length); /* done sending after CDC_GET_ENCAPSULATED_RESPONSE */ } @@ -2102,11 +2110,13 @@ static void rndis_send_media_state (struct eth_dev *dev, int connect) } } -static void rndis_control_ack_complete (struct usb_ep *ep, struct usb_request *req) +static void +rndis_control_ack_complete (struct usb_ep *ep, struct usb_request *req) { if (req->status || req->actual != req->length) - DEBUG (dev, "rndis control ack complete --> %d, %d/%d\n", - req->status, req->actual, req->length); + DEBUG ((struct eth_dev *) ep->driver_data, + "rndis control ack complete --> %d, %d/%d\n", + req->status, req->actual, req->length); usb_ep_free_buffer(ep, req->buf, req->dma, 8); usb_ep_free_request(ep, req); @@ -2472,8 +2482,10 @@ autoconf_fail: if (gadget->is_otg) { otg_descriptor.bmAttributes |= USB_OTG_HNP, eth_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP; + eth_config.bMaxPower = 4; #ifdef CONFIG_USB_ETH_RNDIS rndis_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP; + rndis_config.bMaxPower = 4; #endif } diff --git a/drivers/usb/gadget/rndis.c b/drivers/usb/gadget/rndis.c index 1cd445a01d13..561ca545e4e5 100644 --- a/drivers/usb/gadget/rndis.c +++ b/drivers/usb/gadget/rndis.c @@ -70,8 +70,6 @@ MODULE_PARM_DESC (rndis_debug, "enable debugging"); #define RNDIS_MAX_CONFIGS 1 -static struct proc_dir_entry *rndis_connect_dir; -static struct proc_dir_entry *rndis_connect_state [RNDIS_MAX_CONFIGS]; static rndis_params rndis_per_dev_params [RNDIS_MAX_CONFIGS]; @@ -1275,6 +1273,8 @@ int rndis_rm_hdr (u8 *buf, u32 *length) return 0; } +#ifdef CONFIG_USB_GADGET_DEBUG_FILES + int rndis_proc_read (char *page, char **start, off_t off, int count, int *eof, void *data) { @@ -1365,43 +1365,40 @@ int rndis_proc_write (struct file *file, const char __user *buffer, return count; } +#define NAME_TEMPLATE "driver/rndis-%03d" + +static struct proc_dir_entry *rndis_connect_state [RNDIS_MAX_CONFIGS]; + +#endif /* CONFIG_USB_GADGET_DEBUG_FILES */ + + int __init rndis_init (void) { u8 i; - char name [4]; - /* FIXME this should probably be /proc/driver/rndis, - * and only if debugging is enabled - */ - - if (!(rndis_connect_dir = proc_mkdir ("rndis", NULL))) { - printk (KERN_ERR "%s: couldn't create /proc/rndis entry", - __FUNCTION__); - return -EIO; - } - for (i = 0; i < RNDIS_MAX_CONFIGS; i++) { - sprintf (name, "%03d", i); +#ifdef CONFIG_USB_GADGET_DEBUG_FILES + char name [20]; + + sprintf (name, NAME_TEMPLATE, i); if (!(rndis_connect_state [i] - = create_proc_entry (name, 0660, - rndis_connect_dir))) + = create_proc_entry (name, 0660, NULL))) { DEBUG ("%s :remove entries", __FUNCTION__); - for (i--; i > 0; i--) { - sprintf (name, "%03d", i); - remove_proc_entry (name, rndis_connect_dir); + while (i) { + sprintf (name, NAME_TEMPLATE, --i); + remove_proc_entry (name, NULL); } DEBUG ("\n"); - - remove_proc_entry ("000", rndis_connect_dir); - remove_proc_entry ("rndis", NULL); return -EIO; } + rndis_connect_state [i]->nlink = 1; rndis_connect_state [i]->write_proc = rndis_proc_write; rndis_connect_state [i]->read_proc = rndis_proc_read; rndis_connect_state [i]->data = (void *) (rndis_per_dev_params + i); +#endif rndis_per_dev_params [i].confignr = i; rndis_per_dev_params [i].used = 0; rndis_per_dev_params [i].state = RNDIS_UNINITIALIZED; @@ -1415,14 +1412,14 @@ int __init rndis_init (void) void rndis_exit (void) { +#ifdef CONFIG_USB_GADGET_DEBUG_FILES u8 i; - char name [4]; + char name [20]; for (i = 0; i < RNDIS_MAX_CONFIGS; i++) { - sprintf (name, "%03d", i); - remove_proc_entry (name, rndis_connect_dir); + sprintf (name, NAME_TEMPLATE, i); + remove_proc_entry (name, NULL); } - remove_proc_entry ("rndis", NULL); - return; +#endif } -- cgit v1.2.3 From e1bf2cc8ca11a42544f9402f79c50f6c1dd1c75f Mon Sep 17 00:00:00 2001 From: David Brownell Date: Tue, 28 Sep 2004 21:24:57 -0700 Subject: [PATCH] USB: OHCI support for PXA27x here's a patch from Nico to add PXA27x OHCI support. Please merge; it applies on top of your current BK with some offsets, but I can't build that tree for ARM because of some #include lossage. OHCI driver for the Intel PXA27x processor Signed-off-by: Nicolas Pitre Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- arch/arm/mach-pxa/pxa27x.c | 44 ++++ drivers/usb/Kconfig | 2 +- drivers/usb/host/ohci-hcd.c | 5 + drivers/usb/host/ohci-pxa27x.c | 460 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 510 insertions(+), 1 deletion(-) create mode 100644 drivers/usb/host/ohci-pxa27x.c (limited to 'drivers') diff --git a/arch/arm/mach-pxa/pxa27x.c b/arch/arm/mach-pxa/pxa27x.c index b1573c837b6a..3144d48cae2c 100644 --- a/arch/arm/mach-pxa/pxa27x.c +++ b/arch/arm/mach-pxa/pxa27x.c @@ -16,8 +16,10 @@ #include #include #include +#include #include +#include #include "generic.h" @@ -116,3 +118,45 @@ unsigned int get_lcdclk_frequency_10khz(void) EXPORT_SYMBOL(get_clk_frequency_khz); EXPORT_SYMBOL(get_memclk_frequency_10khz); EXPORT_SYMBOL(get_lcdclk_frequency_10khz); + + +/* + * device registration specific to PXA27x. + */ + +static u64 pxa27x_dmamask = 0xffffffffUL; + +static struct resource pxa27x_ohci_resources[] = { + [0] = { + .start = 0x4C000000, + .end = 0x4C00ff6f, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_USBH1, + .end = IRQ_USBH1, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device ohci_device = { + .name = "pxa27x-ohci", + .id = -1, + .dev = { + .dma_mask = &pxa27x_dmamask, + .coherent_dma_mask = 0xffffffff, + }, + .num_resources = ARRAY_SIZE(pxa27x_ohci_resources), + .resource = pxa27x_ohci_resources, +}; + +static struct platform_device *devices[] __initdata = { + &ohci_device, +}; + +static int __init pxa27x_init(void) +{ + return platform_add_devices(devices, ARRAY_SIZE(devices)); +} + +subsys_initcall(pxa27x_init); diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig index 0e805e4f3aa1..4354a3eb93d4 100644 --- a/drivers/usb/Kconfig +++ b/drivers/usb/Kconfig @@ -7,7 +7,7 @@ menu "USB support" # ARM SA1111 chips have a non-PCI based "OHCI-compatible" USB host interface. config USB tristate "Support for Host-side USB" - depends on PCI || SA1111 || ARCH_OMAP1510 || ARCH_OMAP1610 || ARCH_LH7A404 + depends on PCI || SA1111 || ARCH_OMAP1510 || ARCH_OMAP1610 || ARCH_LH7A404 || PXA27x ---help--- Universal Serial Bus (USB) is a specification for a serial bus subsystem which offers higher speeds and more features than the diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index 84867d1d0bb7..28f3fdd0751f 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -879,10 +879,15 @@ MODULE_LICENSE ("GPL"); #include "ohci-lh7a404.c" #endif +#ifdef CONFIG_PXA27x +#include "ohci-pxa27x.c" +#endif + #if !(defined(CONFIG_PCI) \ || defined(CONFIG_SA1111) \ || defined(CONFIG_ARCH_OMAP) \ || defined (CONFIG_ARCH_LH7A404) \ + || defined (CONFIG_PXA27x) \ ) #error "missing bus glue for ohci-hcd" #endif diff --git a/drivers/usb/host/ohci-pxa27x.c b/drivers/usb/host/ohci-pxa27x.c new file mode 100644 index 000000000000..4bfe74123373 --- /dev/null +++ b/drivers/usb/host/ohci-pxa27x.c @@ -0,0 +1,460 @@ +/* + * OHCI HCD (Host Controller Driver) for USB. + * + * (C) Copyright 1999 Roman Weissgaerber + * (C) Copyright 2000-2002 David Brownell + * (C) Copyright 2002 Hewlett-Packard Company + * + * Bus Glue for pxa27x + * + * Written by Christopher Hoover + * Based on fragments of previous driver by Russell King et al. + * + * Modified for LH7A404 from ohci-sa1111.c + * by Durgesh Pattamatta + * + * Modified for pxa27x from ohci-lh7a404.c + * by Nick Bane 26-8-2004 + * + * This file is licenced under the GPL. + */ + +#include +#include +#include + + +#define PMM_NPS_MODE 1 +#define PMM_GLOBAL_MODE 2 +#define PMM_PERPORT_MODE 3 + +#define PXA_UHC_MAX_PORTNUM 3 + +#define UHCRHPS(x) __REG2( 0x4C000050, (x)<<2 ) + +static int pxa27x_ohci_pmm_state; + +/* + PMM_NPS_MODE -- PMM Non-power switching mode + Ports are powered continuously. + + PMM_GLOBAL_MODE -- PMM global switching mode + All ports are powered at the same time. + + PMM_PERPORT_MODE -- PMM per port switching mode + Ports are powered individually. + */ +static int pxa27x_ohci_select_pmm( int mode ) +{ + pxa27x_ohci_pmm_state = mode; + + switch ( mode ) { + case PMM_NPS_MODE: + UHCRHDA |= RH_A_NPS; + break; + case PMM_GLOBAL_MODE: + UHCRHDA &= ~(RH_A_NPS & RH_A_PSM); + break; + case PMM_PERPORT_MODE: + UHCRHDA &= ~(RH_A_NPS); + UHCRHDA |= RH_A_PSM; + + /* Set port power control mask bits, only 3 ports. */ + UHCRHDB |= (0x7<<17); + break; + default: + printk( KERN_ERR + "Invalid mode %d, set to non-power switch mode.\n", + mode ); + + pxa27x_ohci_pmm_state = PMM_NPS_MODE; + UHCRHDA |= RH_A_NPS; + } + + return 0; +} + +/* + If you select PMM_PERPORT_MODE, you should set the port power + */ +static int pxa27x_ohci_set_port_power( int port ) +{ + if ( (pxa27x_ohci_pmm_state==PMM_PERPORT_MODE) + && (port>0) && (port0) && (portresource[0].start, + dev->resource[0].end + - dev->resource[0].start + 1, hcd_name)) { + pr_debug("request_mem_region failed"); + return -EBUSY; + } + + pxa27x_start_hc(dev); + + /* Select Power Management Mode */ + pxa27x_ohci_select_pmm( PMM_PERPORT_MODE ); + + /* If choosing PMM_PERPORT_MODE, we should set the port power before we use it. */ + if (pxa27x_ohci_set_port_power(1) < 0) + printk(KERN_ERR "Setting port 1 power failed.\n"); + + if (pxa27x_ohci_clear_port_power(2) < 0) + printk(KERN_ERR "Setting port 2 power failed.\n"); + + if (pxa27x_ohci_clear_port_power(3) < 0) + printk(KERN_ERR "Setting port 3 power failed.\n"); + + addr = ioremap(dev->resource[0].start, + dev->resource[0].end - dev->resource[0].start + 1); + if (!addr) { + pr_debug("ioremap failed"); + retval = -ENOMEM; + goto err1; + } + + hcd = driver->hcd_alloc (); + if (hcd == NULL){ + pr_debug ("hcd_alloc failed"); + retval = -ENOMEM; + goto err1; + } + + if(dev->resource[1].flags != IORESOURCE_IRQ){ + pr_debug ("resource[1] is not IORESOURCE_IRQ"); + retval = -ENOMEM; + goto err1; + } + + hcd->driver = (struct hc_driver *) driver; + hcd->description = driver->description; + hcd->irq = dev->resource[1].start; + hcd->regs = addr; + hcd->self.controller = &dev->dev; + + retval = hcd_buffer_create (hcd); + if (retval != 0) { + pr_debug ("pool alloc fail"); + goto err1; + } + + retval = request_irq (hcd->irq, usb_hcd_irq, SA_INTERRUPT, + hcd->description, hcd); + if (retval != 0) { + pr_debug("request_irq(%d) failed with retval %d\n",hcd->irq,retval); + retval = -EBUSY; + goto err2; + } + + pr_debug ("%s (pxa27x) at 0x%p, irq %d", + hcd->description, hcd->regs, hcd->irq); + + usb_bus_init (&hcd->self); + hcd->self.op = &usb_hcd_operations; + hcd->self.hcpriv = (void *) hcd; + hcd->self.bus_name = "pxa27x"; + hcd->product_desc = "PXA27x OHCI"; + + INIT_LIST_HEAD (&hcd->dev_list); + + usb_register_bus (&hcd->self); + + if ((retval = driver->start (hcd)) < 0) { + usb_hcd_pxa27x_remove(hcd, dev); + return retval; + } + + *hcd_out = hcd; + return 0; + + err2: + hcd_buffer_destroy (hcd); + if (hcd) + driver->hcd_free(hcd); + err1: + pxa27x_stop_hc(dev); + release_mem_region(dev->resource[0].start, + dev->resource[0].end + - dev->resource[0].start + 1); + return retval; +} + + +/* may be called without controller electrically present */ +/* may be called with controller, bus, and devices active */ + +/** + * usb_hcd_pxa27x_remove - shutdown processing for pxa27x-based HCDs + * @dev: USB Host Controller being removed + * Context: !in_interrupt() + * + * Reverses the effect of usb_hcd_pxa27x_probe(), first invoking + * the HCD's stop() method. It is always called from a thread + * context, normally "rmmod", "apmd", or something similar. + * + */ +void usb_hcd_pxa27x_remove (struct usb_hcd *hcd, struct platform_device *dev) +{ + void *base; + + pr_debug ("remove: %s, state %x", hcd->self.bus_name, hcd->state); + + if (in_interrupt ()) + BUG (); + + hcd->state = USB_STATE_QUIESCING; + + pr_debug ("%s: roothub graceful disconnect", hcd->self.bus_name); + usb_disconnect (&hcd->self.root_hub); + + hcd->driver->stop (hcd); + hcd->state = USB_STATE_HALT; + + free_irq (hcd->irq, hcd); + hcd_buffer_destroy (hcd); + + usb_deregister_bus (&hcd->self); + + base = hcd->regs; + hcd->driver->hcd_free (hcd); + + pxa27x_stop_hc(dev); + release_mem_region(dev->resource[0].start, + dev->resource[0].end - dev->resource[0].start + 1); +} + +/*-------------------------------------------------------------------------*/ + +static int __devinit +ohci_pxa27x_start (struct usb_hcd *hcd) +{ + struct ohci_hcd *ohci = hcd_to_ohci (hcd); + int ret; + + ohci_dbg (ohci, "ohci_pxa27x_start, ohci:%p", ohci); + + if ((ret = ohci_init(ohci)) < 0) + return ret; + + if ((ret = ohci_run (ohci)) < 0) { + err ("can't start %s", ohci->hcd.self.bus_name); + ohci_stop (hcd); + return ret; + } + + return 0; +} + +/*-------------------------------------------------------------------------*/ + +static const struct hc_driver ohci_pxa27x_hc_driver = { + .description = hcd_name, + + /* + * generic hardware linkage + */ + .irq = ohci_irq, + .flags = HCD_USB11, + + /* + * basic lifecycle operations + */ + .start = ohci_pxa27x_start, + .stop = ohci_stop, + + /* + * memory lifecycle (except per-request) + */ + .hcd_alloc = ohci_hcd_alloc, + .hcd_free = ohci_hcd_free, + + /* + * managing i/o requests and associated device resources + */ + .urb_enqueue = ohci_urb_enqueue, + .urb_dequeue = ohci_urb_dequeue, + .endpoint_disable = ohci_endpoint_disable, + + /* + * scheduling support + */ + .get_frame_number = ohci_get_frame, + + /* + * root hub support + */ + .hub_status_data = ohci_hub_status_data, + .hub_control = ohci_hub_control, +#ifdef CONFIG_USB_SUSPEND + .hub_suspend = ohci_hub_suspend, + .hub_resume = ohci_hub_resume, +#endif +}; + +/*-------------------------------------------------------------------------*/ + +static int ohci_hcd_pxa27x_drv_probe(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct usb_hcd *hcd = NULL; + int ret; + + pr_debug ("In ohci_hcd_pxa27x_drv_probe"); + + if (usb_disabled()) + return -ENODEV; + + ret = usb_hcd_pxa27x_probe(&ohci_pxa27x_hc_driver, &hcd, pdev); + + if (ret == 0) + dev_set_drvdata(dev, hcd); + + return ret; +} + +static int ohci_hcd_pxa27x_drv_remove(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct usb_hcd *hcd = dev_get_drvdata(dev); + + usb_hcd_pxa27x_remove(hcd, pdev); + dev_set_drvdata(dev, NULL); + return 0; +} + +static int ohci_hcd_pxa27x_drv_suspend(struct device *dev, u32 state, u32 level) +{ +// struct platform_device *pdev = to_platform_device(dev); +// struct usb_hcd *hcd = dev_get_drvdata(dev); + printk("%s: not implemented yet\n", __FUNCTION__); + + return 0; +} + +static int ohci_hcd_pxa27x_drv_resume(struct device *dev, u32 state) +{ +// struct platform_device *pdev = to_platform_device(dev); +// struct usb_hcd *hcd = dev_get_drvdata(dev); + printk("%s: not implemented yet\n", __FUNCTION__); + + return 0; +} + + +static struct device_driver ohci_hcd_pxa27x_driver = { + .name = "pxa27x-ohci", + .bus = &platform_bus_type, + .probe = ohci_hcd_pxa27x_drv_probe, + .remove = ohci_hcd_pxa27x_drv_remove, + .suspend = ohci_hcd_pxa27x_drv_suspend, + .resume = ohci_hcd_pxa27x_drv_resume, +}; + +static int __init ohci_hcd_pxa27x_init (void) +{ + pr_debug (DRIVER_INFO " (pxa27x)"); + pr_debug ("block sizes: ed %d td %d\n", + sizeof (struct ed), sizeof (struct td)); + + return driver_register(&ohci_hcd_pxa27x_driver); +} + +static void __exit ohci_hcd_pxa27x_cleanup (void) +{ + driver_unregister(&ohci_hcd_pxa27x_driver); +} + +module_init (ohci_hcd_pxa27x_init); +module_exit (ohci_hcd_pxa27x_cleanup); -- cgit v1.2.3 From 6584ec1af8160583c607fb84e85ab3645c167931 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Tue, 28 Sep 2004 21:25:54 -0700 Subject: [PATCH] USB Gadget: debug files now Kconfigured This adds a new Kconfig option, so now all the usb peripheral controllers have the same way to enable debug files (or more typically, disable them). Several of the USB peripheral controller drivers have a /proc/driver/udc file that's handy when debugging, but probably not wanted otherwise. This patch adds CONFIG_USB_GADGET_DEBUG_FILES, and uses it to replace some driver-internal defines. Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/Kconfig | 15 +++++++++++---- drivers/usb/gadget/goku_udc.c | 14 ++++---------- drivers/usb/gadget/lh7a40x_udc.c | 7 +++---- drivers/usb/gadget/net2280.c | 10 +++++++--- drivers/usb/gadget/omap_udc.c | 2 +- drivers/usb/gadget/pxa2xx_udc.c | 17 ++++------------- 6 files changed, 30 insertions(+), 35 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index decbca335389..c6e0693ed1d6 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -39,6 +39,17 @@ config USB_GADGET If in doubt, say "N" and don't enable these drivers; most people don't have this kind of hardware (except maybe inside Linux PDAs). +config USB_GADGET_DEBUG_FILES + boolean "Debugging information files" + depends on USB_GADGET && PROC_FS + help + Some of the drivers in the "gadget" framework can expose + debugging information in files such as /proc/driver/udc + (for a peripheral controller). The information in these + files may help when you're troubleshooting or bringing up a + driver on a new board. Enable these files by choosing "Y" + here. If in doubt, or to conserve kernel memory, say "N". + # # USB Peripheral Controller Support # @@ -206,10 +217,6 @@ config USB_OTG Select this only if your OMAP board has a Mini-AB connector. -config USB_OMAP_PROC - boolean "/proc/driver/udc file" - depends on USB_GADGET_OMAP - endchoice config USB_GADGET_DUALSPEED diff --git a/drivers/usb/gadget/goku_udc.c b/drivers/usb/gadget/goku_udc.c index 61ce5b2eea41..f9cdb23da15b 100644 --- a/drivers/usb/gadget/goku_udc.c +++ b/drivers/usb/gadget/goku_udc.c @@ -1092,13 +1092,7 @@ static inline char *dmastr(void) return "(dma IN)"; } -/* if we're trying to save space, don't bother with this proc file */ - -#if defined(CONFIG_PROC_FS) && !defined(CONFIG_EMBEDDED) -# define UDC_PROC_FILE -#endif - -#ifdef UDC_PROC_FILE +#ifdef CONFIG_USB_GADGET_DEBUG_FILES static const char proc_node_name [] = "driver/udc"; @@ -1312,7 +1306,7 @@ done: return count - size; } -#endif /* UDC_PROC_FILE */ +#endif /* CONFIG_USB_GADGET_DEBUG_FILES */ /*-------------------------------------------------------------------------*/ @@ -1815,7 +1809,7 @@ static void goku_remove(struct pci_dev *pdev) usb_gadget_unregister_driver(dev->driver); } -#ifdef UDC_PROC_FILE +#ifdef CONFIG_USB_GADGET_DEBUG_FILES remove_proc_entry(proc_node_name, NULL); #endif if (dev->regs) @@ -1933,7 +1927,7 @@ static int goku_probe(struct pci_dev *pdev, const struct pci_device_id *id) pci_set_master(pdev); -#ifdef UDC_PROC_FILE +#ifdef CONFIG_USB_GADGET_DEBUG_FILES create_proc_read_entry(proc_node_name, 0, NULL, udc_proc_read, dev); #endif diff --git a/drivers/usb/gadget/lh7a40x_udc.c b/drivers/usb/gadget/lh7a40x_udc.c index 772627e97a00..0def9f70e889 100644 --- a/drivers/usb/gadget/lh7a40x_udc.c +++ b/drivers/usb/gadget/lh7a40x_udc.c @@ -54,7 +54,6 @@ static const char ep0name[] = "ep0-control"; /* Local definintions. */ -#define UDC_PROC_FILE #ifndef NO_STATES static char *state_names[] = { @@ -192,7 +191,7 @@ static __inline__ void usb_clear(u32 val, u32 port) */ #define is_usb_connected() get_portc_pdr(2) -#ifdef UDC_PROC_FILE +#ifdef CONFIG_USB_GADGET_DEBUG_FILES static const char proc_node_name[] = "driver/udc"; @@ -248,12 +247,12 @@ udc_proc_read(char *page, char **start, off_t off, int count, #define create_proc_files() create_proc_read_entry(proc_node_name, 0, NULL, udc_proc_read, dev) #define remove_proc_files() remove_proc_entry(proc_node_name, NULL) -#else /* !UDC_PROC_FILE */ +#else /* !CONFIG_USB_GADGET_DEBUG_FILES */ #define create_proc_files() do {} while (0) #define remove_proc_files() do {} while (0) -#endif /* UDC_PROC_FILE */ +#endif /* CONFIG_USB_GADGET_DEBUG_FILES */ /* * udc_disable - disable USB device controller diff --git a/drivers/usb/gadget/net2280.c b/drivers/usb/gadget/net2280.c index 9148f7ed4701..f797b67b49f8 100644 --- a/drivers/usb/gadget/net2280.c +++ b/drivers/usb/gadget/net2280.c @@ -76,7 +76,6 @@ #define EP_DONTUSE 13 /* nonzero */ #define USE_RDK_LEDS /* GPIO pins control three LEDs */ -#define USE_SYSFS_DEBUG_FILES static const char driver_name [] = "net2280"; @@ -117,7 +116,7 @@ module_param (fifo_mode, ushort, 0644); #define DIR_STRING(bAddress) (((bAddress) & USB_DIR_IN) ? "in" : "out") -#if defined(USE_SYSFS_DEBUG_FILES) || defined (DEBUG) +#if defined(CONFIG_USB_GADGET_DEBUG_FILES) || defined (DEBUG) static char *type_string (u8 bmAttributes) { switch ((bmAttributes) & USB_ENDPOINT_XFERTYPE_MASK) { @@ -1449,7 +1448,12 @@ static const struct usb_gadget_ops net2280_ops = { /*-------------------------------------------------------------------------*/ -#ifdef USE_SYSFS_DEBUG_FILES +#ifdef CONFIG_USB_GADGET_DEBUG_FILES + +/* FIXME move these into procfs, and use seq_file. + * Sysfs _still_ doesn't behave for arbitrarily sized files, + * and also doesn't help products using this with 2.4 kernels. + */ /* "function" sysfs attribute */ static ssize_t diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/omap_udc.c index 84de1faeab64..c321c542f0df 100644 --- a/drivers/usb/gadget/omap_udc.c +++ b/drivers/usb/gadget/omap_udc.c @@ -1976,7 +1976,7 @@ EXPORT_SYMBOL(usb_gadget_unregister_driver); /*-------------------------------------------------------------------------*/ -#ifdef CONFIG_USB_OMAP_PROC +#ifdef CONFIG_USB_GADGET_DEBUG_FILES #include diff --git a/drivers/usb/gadget/pxa2xx_udc.c b/drivers/usb/gadget/pxa2xx_udc.c index 54e32c9d2cf2..68020861b8fa 100644 --- a/drivers/usb/gadget/pxa2xx_udc.c +++ b/drivers/usb/gadget/pxa2xx_udc.c @@ -91,10 +91,6 @@ static const char ep0name [] = "ep0"; // #define USE_OUT_DMA // #define DISABLE_TEST_MODE -#ifdef CONFIG_PROC_FS -#define UDC_PROC_FILE -#endif - #ifdef CONFIG_ARCH_IXP4XX #undef USE_DMA @@ -108,12 +104,6 @@ static const char ep0name [] = "ep0"; #include "pxa2xx_udc.h" -#ifdef CONFIG_EMBEDDED -/* few strings, and little code to use them */ -#undef DEBUG -#undef UDC_PROC_FILE -#endif - #ifdef USE_DMA static int use_dma = 1; module_param(use_dma, bool, 0); @@ -1211,7 +1201,7 @@ static const struct usb_gadget_ops pxa2xx_udc_ops = { /*-------------------------------------------------------------------------*/ -#ifdef UDC_PROC_FILE +#ifdef CONFIG_USB_GADGET_DEBUG_FILES static const char proc_node_name [] = "driver/udc"; @@ -1367,11 +1357,12 @@ done: #define remove_proc_files() \ remove_proc_entry(proc_node_name, NULL) -#else /* !UDC_PROC_FILE */ +#else /* !CONFIG_USB_GADGET_DEBUG_FILES */ + #define create_proc_files() do {} while (0) #define remove_proc_files() do {} while (0) -#endif /* UDC_PROC_FILE */ +#endif /* CONFIG_USB_GADGET_DEBUG_FILES */ /* "function" sysfs attribute */ static ssize_t -- cgit v1.2.3 From 2c5b9798d842088d3842f17774d428c0e49ce605 Mon Sep 17 00:00:00 2001 From: Matthew Dharm Date: Tue, 28 Sep 2004 21:42:28 -0700 Subject: [PATCH] USB storage: delayed device scanning This patch started life as as366, got some modifications, and lives now as as366b. It implements a delay in SCSI-layer device scanning for usb-storage devices at insertion time. Many devices work better with this delay. We believe we can remove several US_FL_FIX_INQUIRY unusual_devs.h entries with this patch. (That's a hint, Phil!) The delay is adjustable via a sysfs parameter which is global to the usb-storage module. The default is 5 seconds. Signed-off-by: Alan Stern Signed-off-by: Matthew Dharm Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/usb.c | 70 +++++++++++++++++++++++++++++++++++++++-------- drivers/usb/storage/usb.h | 2 ++ 2 files changed, 61 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c index 9579bdf4969a..32e2f1482c57 100644 --- a/drivers/usb/storage/usb.c +++ b/drivers/usb/storage/usb.c @@ -97,6 +97,11 @@ MODULE_AUTHOR("Matthew Dharm "); MODULE_DESCRIPTION("USB Mass Storage driver for Linux"); MODULE_LICENSE("GPL"); +static unsigned int delay_use = 5; +module_param(delay_use, uint, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(delay_use, "seconds to delay before using a new device"); + + static int storage_probe(struct usb_interface *iface, const struct usb_device_id *id); @@ -882,6 +887,42 @@ static void dissociate_dev(struct us_data *us) kfree(us); } +/* Thread to carry out delayed SCSI-device scanning */ +static int usb_stor_scan_thread(void * __us) +{ + struct us_data *us = (struct us_data *)__us; + + /* + * This thread doesn't need any user-level access, + * so get rid of all our resources. + */ + lock_kernel(); + daemonize("usb-stor"); + current->flags |= PF_NOFREEZE; + unlock_kernel(); + + printk(KERN_DEBUG + "usb-storage: device found at %d\n", us->pusb_dev->devnum); + + /* Wait for the timeout to expire or for a disconnect */ + if (delay_use > 0) { + printk(KERN_DEBUG "usb-storage: waiting for device " + "to settle before scanning\n"); + wait_event_interruptible_timeout(us->scsi_scan_wait, + test_bit(US_FLIDX_DISCONNECTING, &us->flags), + delay_use * HZ); + } + + /* If the device is still connected, perform the scanning */ + if (!test_bit(US_FLIDX_DISCONNECTING, &us->flags)) { + scsi_scan_host(us->host); + printk(KERN_DEBUG "usb-storage: device scan complete\n"); + } + + complete_and_exit(&us->scsi_scan_done, 0); +} + + /* Probe to see if we can drive a newly-connected USB device */ static int storage_probe(struct usb_interface *intf, const struct usb_device_id *id) @@ -903,6 +944,8 @@ static int storage_probe(struct usb_interface *intf, init_MUTEX_LOCKED(&(us->sema)); init_completion(&(us->notify)); init_waitqueue_head(&us->dev_reset_wait); + init_waitqueue_head(&us->scsi_scan_wait); + init_completion(&us->scsi_scan_done); /* Associate the us_data structure with the USB device */ result = associate_dev(us, intf); @@ -951,12 +994,10 @@ static int storage_probe(struct usb_interface *intf, if (result) goto BadDevice; - /* Acquire all the other resources */ + /* Acquire all the other resources and add the host */ result = usb_stor_acquire_resources(us); if (result) goto BadDevice; - - /* Finally, add the host (this does SCSI device scanning) */ result = scsi_add_host(us->host, &intf->dev); if (result) { printk(KERN_WARNING USB_STORAGE @@ -964,10 +1005,15 @@ static int storage_probe(struct usb_interface *intf, goto BadDevice; } - scsi_scan_host(us->host); + /* Start up the thread for delayed SCSI-device scanning */ + result = kernel_thread(usb_stor_scan_thread, us, CLONE_VM); + if (result < 0) { + printk(KERN_WARNING USB_STORAGE + "Unable to start the device-scanning thread\n"); + scsi_remove_host(us->host); + goto BadDevice; + } - printk(KERN_DEBUG - "USB Mass Storage device found at %d\n", us->pusb_dev->devnum); return 0; /* We come here if there are any problems */ @@ -991,6 +1037,11 @@ static void storage_disconnect(struct usb_interface *intf) usb_stor_stop_transport(us); wake_up(&us->dev_reset_wait); + /* Interrupt the SCSI-device-scanning thread's time delay, and + * wait for the thread to finish */ + wake_up(&us->scsi_scan_wait); + wait_for_completion(&us->scsi_scan_done); + /* Wait for the current command to finish, then remove the host */ down(&us->dev_semaphore); up(&us->dev_semaphore); @@ -1012,12 +1063,9 @@ static int __init usb_stor_init(void) /* register the driver, return usb_register return code if error */ retval = usb_register(&usb_storage_driver); - if (retval) - goto out; + if (retval == 0) + printk(KERN_INFO "USB Mass Storage support registered.\n"); - /* we're all set */ - printk(KERN_INFO "USB Mass Storage support registered.\n"); -out: return retval; } diff --git a/drivers/usb/storage/usb.h b/drivers/usb/storage/usb.h index 785f30be68c5..17b25a44172d 100644 --- a/drivers/usb/storage/usb.h +++ b/drivers/usb/storage/usb.h @@ -161,6 +161,8 @@ struct us_data { struct semaphore sema; /* to sleep thread on */ struct completion notify; /* thread begin/end */ wait_queue_head_t dev_reset_wait; /* wait during reset */ + wait_queue_head_t scsi_scan_wait; /* wait before scanning */ + struct completion scsi_scan_done; /* scan thread end */ /* subdriver information */ void *extra; /* Any extra data */ -- cgit v1.2.3 From f6b3ed42bf041d1799964e041cb9de44167cc12d Mon Sep 17 00:00:00 2001 From: Matthew Dharm Date: Tue, 28 Sep 2004 21:42:55 -0700 Subject: [PATCH] USB Storage: ignore bogus residue values This patch allows usb-storage to ignore the reported residue values when required by some devices. A few devices are included... I know more are waiting to be merged into unusual_devs.h In case anyone is curious... yes, these are broken devices. Signed-off-by: Alan Stern Signed-off-by: Matthew Dharm Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/transport.c | 9 +++++++-- drivers/usb/storage/unusual_devs.h | 23 ++++++++++++++++++++++- drivers/usb/storage/usb.h | 1 + 3 files changed, 30 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c index 42f784321607..e0459825fd45 100644 --- a/drivers/usb/storage/transport.c +++ b/drivers/usb/storage/transport.c @@ -1055,8 +1055,13 @@ int usb_stor_Bulk_transport(struct scsi_cmnd *srb, struct us_data *us) /* try to compute the actual residue, based on how much data * was really transferred and what the device tells us */ - residue = min(residue, transfer_length); - srb->resid = max(srb->resid, (int) residue); + if (residue) { + if (!(us->flags & US_FL_IGNORE_RESIDUE) || + srb->sc_data_direction == DMA_TO_DEVICE) { + residue = min(residue, transfer_length); + srb->resid = max(srb->resid, (int) residue); + } + } /* based on the status code, we report good or bad */ switch (bcs->Status) { diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index 9d0d846e19e3..1d5181430529 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -268,6 +268,13 @@ UNUSUAL_DEV( 0x0525, 0xa140, 0x0100, 0x0100, US_SC_8070, US_PR_BULK, NULL, US_FL_FIX_INQUIRY ), +/* Reported by Iacopo Spalletti */ +UNUSUAL_DEV( 0x052b, 0x1807, 0x0100, 0x0100, + "Tekom Technologies, Inc", + "300_CAMERA", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_IGNORE_RESIDUE ), + /* This entry is needed because the device reports Sub=ff */ UNUSUAL_DEV( 0x054c, 0x0010, 0x0106, 0x0450, "Sony", @@ -811,7 +818,14 @@ UNUSUAL_DEV( 0x0ed1, 0x6660, 0x0100, 0x0300, "Solid state disk", US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_FIX_INQUIRY ), - + +/* Reported by Rastislav Stanik */ +UNUSUAL_DEV( 0x0ea0, 0x6828, 0x0110, 0x0110, + "USB", + "Flash Disk", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_IGNORE_RESIDUE ), + /* Reported by Kevin Cernekee * Tested on hardware version 1.10. * Entry is needed only for the initializer function override. @@ -833,6 +847,13 @@ UNUSUAL_DEV( 0x1065, 0x2136, 0x0000, 0x9999, US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_MODE_XLATE ), +/* Reported by Kotrla Vitezslav */ +UNUSUAL_DEV( 0x1370, 0x6828, 0x0110, 0x0110, + "SWISSBIT", + "Black Silver", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_IGNORE_RESIDUE ), + #ifdef CONFIG_USB_STORAGE_SDDR55 UNUSUAL_DEV( 0x55aa, 0xa103, 0x0000, 0x9999, "Sandisk", diff --git a/drivers/usb/storage/usb.h b/drivers/usb/storage/usb.h index 17b25a44172d..199594967870 100644 --- a/drivers/usb/storage/usb.h +++ b/drivers/usb/storage/usb.h @@ -73,6 +73,7 @@ struct us_unusual_dev { #define US_FL_SCM_MULT_TARG 0x00000020 /* supports multiple targets */ #define US_FL_FIX_INQUIRY 0x00000040 /* INQUIRY response needs faking */ #define US_FL_FIX_CAPACITY 0x00000080 /* READ CAPACITY response too big */ +#define US_FL_IGNORE_RESIDUE 0x00000100 /* reported residue is wrong */ /* Dynamic flag definitions: used in set_bit() etc. */ #define US_FLIDX_URB_ACTIVE 18 /* 0x00040000 current_urb is in use */ -- cgit v1.2.3 From e4b60557a2d30de110b1b6659b432aaca50ebd58 Mon Sep 17 00:00:00 2001 From: Matthew Dharm Date: Tue, 28 Sep 2004 21:43:21 -0700 Subject: [PATCH] USB Storage: revert GetMaxLUN strictness This is patch as384. It reverts some of our sanity checks on the GetMaxLUN part of the Bulk-only protocol. Apparently, this is one area where vendors can't even get close to correct. So, in the face of any sort of error, we assume a single LUN. We also include some comments so we don't make this mistake again. Signed-off-by: Alan Stern Signed-off-by: Matthew Dharm Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/transport.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c index e0459825fd45..07b919d800a1 100644 --- a/drivers/usb/storage/transport.c +++ b/drivers/usb/storage/transport.c @@ -911,7 +911,6 @@ int usb_stor_Bulk_max_lun(struct us_data *us) int result; /* issue the command */ - us->iobuf[0] = 0; result = usb_stor_control_msg(us, us->recv_ctrl_pipe, US_BULK_GET_MAX_LUN, USB_DIR_IN | USB_TYPE_CLASS | @@ -922,7 +921,7 @@ int usb_stor_Bulk_max_lun(struct us_data *us) result, us->iobuf[0]); /* if we have a successful request, return the result */ - if (result >= 0) + if (result > 0) return us->iobuf[0]; /* @@ -934,13 +933,16 @@ int usb_stor_Bulk_max_lun(struct us_data *us) if (result == -EPIPE) { usb_stor_clear_halt(us, us->recv_bulk_pipe); usb_stor_clear_halt(us, us->send_bulk_pipe); - /* return the default -- no LUNs */ - return 0; } - /* An answer or a STALL are the only valid responses. If we get - * something else, return an indication of error */ - return -1; + /* + * Some devices don't like GetMaxLUN. They may STALL the control + * pipe, they may return a zero-length result, they may do nothing at + * all and timeout, or they may fail in even more bizarrely creative + * ways. In these cases the best approach is to use the default + * value: only one LUN. + */ + return 0; } int usb_stor_Bulk_transport(struct scsi_cmnd *srb, struct us_data *us) -- cgit v1.2.3 From b0178e2c364f579f220a80926c7fe55877d37271 Mon Sep 17 00:00:00 2001 From: Luiz Capitulino Date: Tue, 28 Sep 2004 22:48:33 -0700 Subject: [PATCH] usb-serial: Moves the search in device list out of usb_serial_probe(). Move the search in `usb_serial_driver_list' out of usb_serial_probe(). Note that: 1) The `found' variable is not necessary; 2) If the device does have a probe function, I'm calling usb_match_id() again. I'm uncertain if this is the better thing to do. Signed-off-by: Luiz Capitulino Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/usb-serial.c | 46 ++++++++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 19 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 73efa4270b79..5e550ab6ec72 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -846,6 +846,25 @@ static struct usb_serial * create_serial (struct usb_device *dev, return serial; } +static struct usb_serial_device_type *search_serial_device(struct usb_interface *iface) +{ + struct list_head *p; + const struct usb_device_id *id; + struct usb_serial_device_type *t; + + /* List trough know devices and see if the usb id matches */ + list_for_each(p, &usb_serial_driver_list) { + t = list_entry(p, struct usb_serial_device_type, driver_list); + id = usb_match_id(iface, t->id_table); + if (id != NULL) { + dbg("descriptor matches"); + return t; + } + } + + return NULL; +} + int usb_serial_probe(struct usb_interface *interface, const struct usb_device_id *id) { @@ -858,9 +877,7 @@ int usb_serial_probe(struct usb_interface *interface, struct usb_endpoint_descriptor *bulk_in_endpoint[MAX_NUM_PORTS]; struct usb_endpoint_descriptor *bulk_out_endpoint[MAX_NUM_PORTS]; struct usb_serial_device_type *type = NULL; - struct list_head *tmp; int retval; - int found; int minor; int buffer_size; int i; @@ -869,22 +886,9 @@ int usb_serial_probe(struct usb_interface *interface, int num_bulk_out = 0; int num_ports = 0; int max_endpoints; - const struct usb_device_id *id_pattern = NULL; - - /* loop through our list of known serial converters, and see if this - device matches. */ - found = 0; - list_for_each (tmp, &usb_serial_driver_list) { - type = list_entry(tmp, struct usb_serial_device_type, driver_list); - id_pattern = usb_match_id(interface, type->id_table); - if (id_pattern != NULL) { - dbg("descriptor matches"); - found = 1; - break; - } - } - if (!found) { - /* no match */ + + type = search_serial_device(interface); + if (!type) { dbg("none matched"); return -ENODEV; } @@ -897,12 +901,16 @@ int usb_serial_probe(struct usb_interface *interface, /* if this device type has a probe function, call it */ if (type->probe) { + const struct usb_device_id *id; + if (!try_module_get(type->owner)) { dev_err(&interface->dev, "module get failed, exiting\n"); kfree (serial); return -EIO; } - retval = type->probe (serial, id_pattern); + + id = usb_match_id(interface, type->id_table); + retval = type->probe(serial, id); module_put(type->owner); if (retval) { -- cgit v1.2.3 From 3b8b2da5bbeaf701f97c039ba238fcaa2cd175cc Mon Sep 17 00:00:00 2001 From: Luiz Capitulino Date: Tue, 28 Sep 2004 22:48:57 -0700 Subject: [PATCH] usb-serial: create_serial() return value trivial fix. create_serial() only returns NULL if there is no memory enough to a new `usb_serial' structure, thus, the right error code to return is -ENOMEM. Signed-off-by: Luiz Capitulino Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/usb-serial.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 5e550ab6ec72..a25d089202e8 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -896,7 +896,7 @@ int usb_serial_probe(struct usb_interface *interface, serial = create_serial (dev, interface, type); if (!serial) { dev_err(&interface->dev, "%s - out of memory\n", __FUNCTION__); - return -ENODEV; + return -ENOMEM; } /* if this device type has a probe function, call it */ -- cgit v1.2.3 From 10503542d27a021a1bacb28de0f1730c800f31fa Mon Sep 17 00:00:00 2001 From: Luiz Capitulino Date: Tue, 28 Sep 2004 22:49:23 -0700 Subject: [PATCH] usb-serial: return_serial() trivial cleanup. return_serial() trivial cleanup: 1) CodingStyle fix; 2) The `return' is not necessary, we are at the end of a function which don't return nothing (void). Signed-off-by: Luiz Capitulino Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/usb-serial.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index a25d089202e8..41f78bc5d590 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -405,7 +405,7 @@ static struct usb_serial *get_free_serial (struct usb_serial *serial, int num_po return NULL; } -static void return_serial (struct usb_serial *serial) +static void return_serial(struct usb_serial *serial) { int i; @@ -417,8 +417,6 @@ static void return_serial (struct usb_serial *serial) for (i = 0; i < serial->num_ports; ++i) { serial_table[serial->minor + i] = NULL; } - - return; } static void destroy_serial(struct kref *kref) -- cgit v1.2.3 From 2ab98bc1040248f25ed1f2b9638509856778227b Mon Sep 17 00:00:00 2001 From: Luiz Capitulino Date: Tue, 28 Sep 2004 22:49:52 -0700 Subject: [PATCH] usb-serial: usb_serial_register() cleanup. usb_serial_register() cleanup 1) CodingStyle in the call of usb_serial_bus_register() 2) The goto and the duplicate `return retval' are not necessary Signed-off-by: Luiz Capitulino Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/usb-serial.c | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 41f78bc5d590..2359fb886875 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -1338,17 +1338,13 @@ int usb_serial_register(struct usb_serial_device_type *new_device) /* Add this device to our list of devices */ list_add(&new_device->driver_list, &usb_serial_driver_list); - retval = usb_serial_bus_register (new_device); - - if (retval) - goto error; - - info("USB Serial support registered for %s", new_device->name); - - return retval; -error: - err("problem %d when registering driver %s", retval, new_device->name); - list_del(&new_device->driver_list); + retval = usb_serial_bus_register(new_device); + if (retval) { + err("problem %d when registering driver %s", retval, new_device->name); + list_del(&new_device->driver_list); + } + else + info("USB Serial support registered for %s", new_device->name); return retval; } -- cgit v1.2.3 From e3797e9df58e6cd3270137b1da1e6319a17a7fc7 Mon Sep 17 00:00:00 2001 From: Luiz Capitulino Date: Tue, 28 Sep 2004 22:50:15 -0700 Subject: [PATCH] usb-serial: Add module version information. Add module version information for drivers/usb/serial/usb-serial.c. Signed-off-by: Luiz Capitulino Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/usb-serial.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 2359fb886875..fe4af3e1ec4b 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -1371,6 +1371,7 @@ EXPORT_SYMBOL(usb_serial_port_softint); /* Module information */ MODULE_AUTHOR( DRIVER_AUTHOR ); MODULE_DESCRIPTION( DRIVER_DESC ); +MODULE_VERSION( DRIVER_VERSION ); MODULE_LICENSE("GPL"); module_param(debug, bool, S_IRUGO | S_IWUSR); -- cgit v1.2.3 From 6af0e11bc00c5eb333dab6f41fb9ba3956d4f664 Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Tue, 28 Sep 2004 22:50:37 -0700 Subject: [PATCH] USB: remove calls to usb_unlink_urb in net/catc.c Signed-off-by: Borislav Petkov Signed-off-by: Greg Kroah-Hartman --- drivers/usb/net/catc.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/net/catc.c b/drivers/usb/net/catc.c index de9bf863ef49..7a3efd1338fd 100644 --- a/drivers/usb/net/catc.c +++ b/drivers/usb/net/catc.c @@ -765,10 +765,10 @@ static int catc_stop(struct net_device *netdev) if (!catc->is_f5u011) del_timer_sync(&catc->timer); - usb_unlink_urb(catc->rx_urb); - usb_unlink_urb(catc->tx_urb); - usb_unlink_urb(catc->irq_urb); - usb_unlink_urb(catc->ctrl_urb); + usb_kill_urb(catc->rx_urb); + usb_kill_urb(catc->tx_urb); + usb_kill_urb(catc->irq_urb); + usb_kill_urb(catc->ctrl_urb); return 0; } -- cgit v1.2.3 From 4afa423c4b1ec1d8d73bae1dcd1b689425199bd6 Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Tue, 28 Sep 2004 22:50:58 -0700 Subject: [PATCH] USB: remove calls to usb_unlink_urb in misc/legousbtower.c another trivial one. Signed-off-by: Borislav Petkov Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/legousbtower.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/misc/legousbtower.c b/drivers/usb/misc/legousbtower.c index c9a822f2cf96..2fe2a1cb8b49 100644 --- a/drivers/usb/misc/legousbtower.c +++ b/drivers/usb/misc/legousbtower.c @@ -505,12 +505,12 @@ static void tower_abort_transfers (struct lego_usb_tower *dev) dev->interrupt_in_running = 0; mb(); if (dev->interrupt_in_urb != NULL && dev->udev) { - usb_unlink_urb (dev->interrupt_in_urb); + usb_kill_urb (dev->interrupt_in_urb); } } if (dev->interrupt_out_busy) { if (dev->interrupt_out_urb != NULL && dev->udev) { - usb_unlink_urb (dev->interrupt_out_urb); + usb_kill_urb (dev->interrupt_out_urb); } } -- cgit v1.2.3 From 81ff542ab3cd7126776e03011f1304f55f1ecdec Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Tue, 28 Sep 2004 22:51:18 -0700 Subject: [PATCH] USB: remove _some_ calls to usb_unlink_urb in misc/auerswald.c here's a more controversial one. Firstly, auerchain_unlink_urb:1180 is a wrapper for usb_unlink_urb so i don't think we should change that. Secondly, auerswald_int_release returns an int which is the error code returned by usb_unlink_urb. usb_kill_urb is a void, however, so I've removed the error checking. For the sake of debugging, the dbg-line announcing the name of the function is probably enough. Signed-off-by: Borislav Petkov Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/auerswald.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/misc/auerswald.c b/drivers/usb/misc/auerswald.c index d248186f3a75..28e1736e18d4 100644 --- a/drivers/usb/misc/auerswald.c +++ b/drivers/usb/misc/auerswald.c @@ -516,7 +516,7 @@ static void auerchain_unlink_all (pauerchain_t acp) urbp = acep->urbp; urbp->transfer_flags &= ~URB_ASYNC_UNLINK; dbg ("unlink active urb"); - usb_unlink_urb (urbp); + usb_kill_urb (urbp); } } @@ -1171,22 +1171,16 @@ intoend: endpoint. This function returns 0 if successful or an error code. NOTE: no mutex please! */ -static int auerswald_int_release (pauerswald_t cp) +static void auerswald_int_release (pauerswald_t cp) { - int ret = 0; dbg ("auerswald_int_release"); /* stop the int endpoint */ - if (cp->inturbp) { - ret = usb_unlink_urb (cp->inturbp); - if (ret) - dbg ("nonzero int unlink result received: %d", ret); - } + if (cp->inturbp) + usb_kill_urb (cp->inturbp); /* deallocate memory */ auerswald_int_free (cp); - - return ret; } /* --------------------------------------------------------------------- */ -- cgit v1.2.3 From 957f22f3569378cec8fc5cbae38ac676ed691264 Mon Sep 17 00:00:00 2001 From: Pete Zaitcev Date: Wed, 29 Sep 2004 00:51:35 -0700 Subject: [PATCH] USB: Fixes for ub in 2.4.9-rc2-mm2 - Do retries for a memory key which was handed out on Kernel Summit 04. - Add missing del_timer. - Add shifts for a 2KB block size device, from Pat LaVarre. Signed-off-by: Pete Zaitcev Signed-off-by: Greg Kroah-Hartman --- drivers/block/ub.c | 53 +++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 37 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/block/ub.c b/drivers/block/ub.c index 184b8a22e86f..efaf8264c345 100644 --- a/drivers/block/ub.c +++ b/drivers/block/ub.c @@ -25,6 +25,7 @@ * -- prune comments, they are too volumnous * -- Exterminate P3 printks * -- Resove XXX's + * -- Redo "benh's retries", perhaps have spin-up code to handle them. V:D=? */ #include #include @@ -157,7 +158,8 @@ struct ub_scsi_cmd { struct ub_scsi_cmd *next; int error; /* Return code - valid upon done */ - int act_len; /* Return size */ + unsigned int act_len; /* Return size */ + unsigned char key, asc, ascq; /* May be valid if error==-EIO */ int stat_count; /* Retries getting status. */ @@ -673,9 +675,12 @@ static inline int ub_bd_rq_fn_1(request_queue_t *q) /* * build the command + * + * The call to blk_queue_hardsect_size() guarantees that request + * is aligned, but it is given in terms of 512 byte units, always. */ - block = rq->sector; - nblks = rq->nr_sectors; + block = rq->sector >> sc->capacity.bshift; + nblks = rq->nr_sectors >> sc->capacity.bshift; memset(cmd, 0, sizeof(struct ub_scsi_cmd)); cmd->cdb[0] = (ub_dir == UB_DIR_READ)? READ_10: WRITE_10; @@ -690,7 +695,7 @@ static inline int ub_bd_rq_fn_1(request_queue_t *q) cmd->dir = ub_dir; cmd->state = UB_CMDST_INIT; cmd->data = rq->buffer; - cmd->len = nblks * 512; + cmd->len = rq->nr_sectors * 512; cmd->done = ub_rw_cmd_done; cmd->back = rq; @@ -837,6 +842,7 @@ static void ub_urb_complete(struct urb *urb, struct pt_regs *pt) { struct ub_dev *sc = urb->context; + del_timer(&sc->work_timer); ub_complete(&sc->work_done); tasklet_schedule(&sc->tasklet); } @@ -1141,16 +1147,8 @@ static void ub_scsi_urb_compl(struct ub_dev *sc, struct ub_scsi_cmd *cmd) (*cmd->done)(sc, cmd); } else if (cmd->state == UB_CMDST_SENSE) { - /* - * We do not look at sense, because even if there was no sense, - * we get into UB_CMDST_SENSE from a STALL or CSW FAIL only. - * We request sense because we want to clear CHECK CONDITION - * on devices with delusions of SCSI, and not because we - * are curious in any way about the sense itself. - */ - /* if ((cmd->top_sense[2] & 0x0F) == NO_SENSE) { foo } */ - ub_state_done(sc, cmd, -EIO); + } else { printk(KERN_WARNING "%s: " "wrong command state %d on device %u\n", @@ -1309,6 +1307,10 @@ static void ub_top_sense_done(struct ub_dev *sc, struct ub_scsi_cmd *scmd) */ ub_cmdtr_sense(sc, scmd, sense); + /* + * Find the command which triggered the unit attention or a check, + * save the sense into it, and advance its state machine. + */ if ((cmd = ub_cmdq_peek(sc)) == NULL) { printk(KERN_WARNING "%s: sense done while idle\n", sc->name); return; @@ -1326,6 +1328,10 @@ static void ub_top_sense_done(struct ub_dev *sc, struct ub_scsi_cmd *scmd) return; } + cmd->key = sense[2] & 0x0F; + cmd->asc = sense[12]; + cmd->ascq = sense[13]; + ub_scsi_urb_compl(sc, cmd); } @@ -1519,7 +1525,7 @@ static int ub_bd_revalidate(struct gendisk *disk) sc->name, sc->dev->devnum, sc->capacity.nsec, sc->capacity.bsize); /* XXX Support sector size switching like in sr.c */ - // blk_queue_hardsect_size(q, sc->capacity.bsize); + blk_queue_hardsect_size(disk->queue, sc->capacity.bsize); set_capacity(disk, sc->capacity.nsec); // set_disk_ro(sdkp->disk, sc->readonly); @@ -1621,6 +1627,9 @@ static int ub_sync_tur(struct ub_dev *sc) rc = cmd->error; + if (rc == -EIO && cmd->key != 0) /* Retries for benh's key */ + rc = cmd->key; + err_submit: kfree(cmd); err_alloc: @@ -1836,6 +1845,7 @@ static int ub_probe(struct usb_interface *intf, request_queue_t *q; struct gendisk *disk; int rc; + int i; rc = -ENOMEM; if ((sc = kmalloc(sizeof(struct ub_dev), GFP_KERNEL)) == NULL) @@ -1902,7 +1912,11 @@ static int ub_probe(struct usb_interface *intf, * has to succeed, so we clear checks with an additional one here. * In any case it's not our business how revaliadation is implemented. */ - ub_sync_tur(sc); + for (i = 0; i < 3; i++) { /* Retries for benh's key */ + if ((rc = ub_sync_tur(sc)) <= 0) break; + if (rc != 0x6) break; + msleep(10); + } sc->removable = 1; /* XXX Query this from the device */ @@ -1938,7 +1952,7 @@ static int ub_probe(struct usb_interface *intf, blk_queue_max_phys_segments(q, UB_MAX_REQ_SG); // blk_queue_segment_boundary(q, CARM_SG_BOUNDARY); blk_queue_max_sectors(q, UB_MAX_SECTORS); - // blk_queue_hardsect_size(q, xxxxx); + blk_queue_hardsect_size(q, sc->capacity.bsize); /* * This is a serious infraction, caused by a deficiency in the @@ -2046,6 +2060,13 @@ static void ub_disconnect(struct usb_interface *intf) } spin_unlock_irqrestore(&sc->lock, flags); + /* + * There is virtually no chance that other CPU runs times so long + * after ub_urb_complete should have called del_timer, but only if HCD + * didn't forget to deliver a callback on unlink. + */ + del_timer_sync(&sc->work_timer); + /* * At this point there must be no commands coming from anyone * and no URBs left in transit. -- cgit v1.2.3 From c1e15eeb5643f6ca4287f3f0d62f005d1711bd49 Mon Sep 17 00:00:00 2001 From: Duncan Sands Date: Wed, 29 Sep 2004 00:52:03 -0700 Subject: [PATCH] usb: extract sensible strings from buggy string descriptors The Freebox is a USB modem popular in France. It returns bogus string descriptors: while the string part is there, the length and type bytes are both zero. This patch detects that case and tries to recover a sensible string by scanning for printable Latin characters. This not only causes the modem to spring to life (because usbnet gets a valid MAC address) - it also means you get the correct Product and Manufacturer strings in sysfs and elsewhere. This patch is in the "mostly harmless" category. Signed-off-by: Duncan Sands Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/message.c | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index e20dde5d793a..f2cd4770eb4f 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -17,6 +17,8 @@ #include #include #include +#include +#include #include #include "hcd.h" /* for usbcore internals */ @@ -623,6 +625,20 @@ int usb_get_string(struct usb_device *dev, unsigned short langid, return result; } +static void usb_try_string_workarounds(unsigned char *buf, int *length) +{ + int newlength, oldlength = *length; + + for (newlength = 2; newlength + 1 < oldlength; newlength += 2) + if (!isprint(buf[newlength]) || buf[newlength + 1]) + break; + + if (newlength > 2) { + buf[0] = newlength; + *length = newlength; + } +} + static int usb_string_sub(struct usb_device *dev, unsigned int langid, unsigned int index, unsigned char *buf) { @@ -634,19 +650,26 @@ static int usb_string_sub(struct usb_device *dev, unsigned int langid, /* If that failed try to read the descriptor length, then * ask for just that many bytes */ - if (rc < 0) { + if (rc < 2) { rc = usb_get_string(dev, langid, index, buf, 2); if (rc == 2) rc = usb_get_string(dev, langid, index, buf, buf[0]); } - if (rc >= 0) { + if (rc >= 2) { + if (!buf[0] && !buf[1]) + usb_try_string_workarounds(buf, &rc); + /* There might be extra junk at the end of the descriptor */ if (buf[0] < rc) rc = buf[0]; - if (rc < 2) - rc = -EINVAL; + + rc = rc - (rc & 1); /* force a multiple of two */ } + + if (rc < 2) + rc = (rc < 0 ? rc : -EINVAL); + return rc; } @@ -724,6 +747,9 @@ int usb_string(struct usb_device *dev, int index, char *buf, size_t size) buf[idx] = 0; err = idx; + if (tbuf[1] != USB_DT_STRING) + dev_dbg(&dev->dev, "wrong descriptor type %02x for string %d (\"%s\")\n", tbuf[1], index, buf); + errout: kfree(tbuf); return err; -- cgit v1.2.3 From 8e8f500e90f77638bebe46d86220bb9cf99ca69e Mon Sep 17 00:00:00 2001 From: Alex Kanavin Date: Wed, 29 Sep 2004 00:52:26 -0700 Subject: [PATCH] USB: export inteface and configuration strings to sysfs this patch adds exporting of configuration and interface strings to sysfs. both configuration and interface strings are present on my Nokia 7610 phone, which can be connected via USB, and I thought it would be a good idea to make them easily accessible via sysfs: [root@cs181035096 usb1]# cat 1-1/manufacturer 1-1/product Nokia 7610 [root@cs181035096 usb1]# grep "" `find . -name configuration` ./1-1/configuration:First and Last and Always [root@cs181035096 usb1]# grep "" `find . -name interface` ./1-1/1-1:1.6/interface:CDC Data Interface ./1-1/1-1:1.5/interface:CDC Comms Interface ./1-1/1-1:1.3/interface:PC Suite Services ./1-1/1-1:1.1/interface:SYNCML-SYNC The first two interfaces are for accessing the GPRS modem, they are recognized and supported perfectly by the cdc_acm driver. The last two are CDC OBEX interfaces, for which there is no driver currently, but I plan to write one. This would allow userspace to do really nifty things, for example accessing the phone filesystem, and exchanging contacts and calendar entries via SyncML. Pretty much the same thing that Nokia PC Suite does. But the software needs to distinguish between the two OBEX interfaces (which is syncml and which is file transfer? the only way to know is to read the strings), and that"s why this patch was written. Signed-off-by: Alex Kanavin Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/sysfs.c | 60 ++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 56 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index 007baee22013..bae974d587ae 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c @@ -27,11 +27,13 @@ static ssize_t show_##field (struct device *dev, char *buf) \ { \ struct usb_device *udev; \ + struct usb_host_config *actconfig; \ \ udev = to_usb_device (dev); \ - if (udev->actconfig) \ + actconfig = udev->actconfig; \ + if (actconfig) \ return sprintf (buf, format_string, \ - udev->actconfig->desc.field * multiplier); \ + actconfig->desc.field * multiplier); \ else \ return 0; \ } \ @@ -44,6 +46,28 @@ usb_actconfig_attr (bNumInterfaces, 1, "%2d\n") usb_actconfig_attr (bmAttributes, 1, "%2x\n") usb_actconfig_attr (bMaxPower, 2, "%3dmA\n") +#define usb_actconfig_str(name, field) \ +static ssize_t show_##name(struct device *dev, char *buf) \ +{ \ + struct usb_device *udev; \ + struct usb_host_config *actconfig; \ + int len; \ + \ + udev = to_usb_device (dev); \ + actconfig = udev->actconfig; \ + if (!actconfig) \ + return 0; \ + len = usb_string(udev, actconfig->desc.field, buf, PAGE_SIZE); \ + if (len < 0) \ + return 0; \ + buf[len] = '\n'; \ + buf[len+1] = 0; \ + return len+1; \ +} \ +static DEVICE_ATTR(name, S_IRUGO, show_##name, NULL); + +usb_actconfig_str (configuration, iConfiguration) + /* configuration value is always present, and r/w */ usb_actconfig_show(bConfigurationValue, 1, "%u\n"); @@ -198,6 +222,7 @@ void usb_create_sysfs_dev_files (struct usb_device *udev) device_create_file (dev, &dev_attr_product); if (udev->descriptor.iSerialNumber) device_create_file (dev, &dev_attr_serial); + device_create_file (dev, &dev_attr_configuration); } void usb_remove_sysfs_dev_files (struct usb_device *udev) @@ -212,6 +237,7 @@ void usb_remove_sysfs_dev_files (struct usb_device *udev) device_remove_file(dev, &dev_attr_product); if (udev->descriptor.iSerialNumber) device_remove_file(dev, &dev_attr_serial); + device_remove_file (dev, &dev_attr_configuration); } /* Interface fields */ @@ -231,7 +257,26 @@ usb_intf_attr (bNumEndpoints, "%02x\n") usb_intf_attr (bInterfaceClass, "%02x\n") usb_intf_attr (bInterfaceSubClass, "%02x\n") usb_intf_attr (bInterfaceProtocol, "%02x\n") -usb_intf_attr (iInterface, "%02x\n") + +#define usb_intf_str(name, field) \ +static ssize_t show_##name(struct device *dev, char *buf) \ +{ \ + struct usb_interface *intf; \ + struct usb_device *udev; \ + int len; \ + \ + intf = to_usb_interface (dev); \ + udev = interface_to_usbdev (intf); \ + len = usb_string(udev, intf->cur_altsetting->desc.field, buf, PAGE_SIZE);\ + if (len < 0) \ + return 0; \ + buf[len] = '\n'; \ + buf[len+1] = 0; \ + return len+1; \ +} \ +static DEVICE_ATTR(name, S_IRUGO, show_##name, NULL); + +usb_intf_str (interface, iInterface); static struct attribute *intf_attrs[] = { &dev_attr_bInterfaceNumber.attr, @@ -240,7 +285,6 @@ static struct attribute *intf_attrs[] = { &dev_attr_bInterfaceClass.attr, &dev_attr_bInterfaceSubClass.attr, &dev_attr_bInterfaceProtocol.attr, - &dev_attr_iInterface.attr, NULL, }; static struct attribute_group intf_attr_grp = { @@ -250,9 +294,17 @@ static struct attribute_group intf_attr_grp = { void usb_create_sysfs_intf_files (struct usb_interface *intf) { sysfs_create_group(&intf->dev.kobj, &intf_attr_grp); + + if (intf->cur_altsetting->desc.iInterface) + device_create_file(&intf->dev, &dev_attr_interface); + } void usb_remove_sysfs_intf_files (struct usb_interface *intf) { sysfs_remove_group(&intf->dev.kobj, &intf_attr_grp); + + if (intf->cur_altsetting->desc.iInterface) + device_remove_file(&intf->dev, &dev_attr_interface); + } -- cgit v1.2.3 From 51876731fb277bbc22eb41dd1c8bbf4ab35af200 Mon Sep 17 00:00:00 2001 From: Nishanth Aravamudan Date: Wed, 29 Sep 2004 02:03:33 -0700 Subject: [PATCH] usb/file_storage: replace schedule_timeout() with msleep_interruptible() Use msleep_interruptible() instead of schedule_timeout() so that the task delays as expected. Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/file_storage.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c index c0f131f5e235..d8c8ab7750ec 100644 --- a/drivers/usb/gadget/file_storage.c +++ b/drivers/usb/gadget/file_storage.c @@ -217,6 +217,7 @@ #include #include #include +#include #include #include #include @@ -2300,8 +2301,7 @@ static int halt_bulk_in_endpoint(struct fsg_dev *fsg) } /* Wait for a short time and then try again */ - set_current_state(TASK_INTERRUPTIBLE); - if (schedule_timeout(HZ / 10) != 0) + if (msleep_interruptible(100) != 0) return -EINTR; rc = usb_ep_set_halt(fsg->bulk_in); } -- cgit v1.2.3 From ddd63249a3bd56411f374600d0332cd25fb04084 Mon Sep 17 00:00:00 2001 From: Nishanth Aravamudan Date: Wed, 29 Sep 2004 02:04:04 -0700 Subject: [PATCH] usb/ati_remote: add set_current_state() Add set_current_state() before schedule_timeout() so that if the while-loop iterates multiple times, schedule_timeout() delays as expected. Without the addition, schedule_timeout() will return immediately. Signed-off-by: Greg Kroah-Hartman --- drivers/usb/input/ati_remote.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/usb/input/ati_remote.c b/drivers/usb/input/ati_remote.c index 3adbc7bab3c3..91f7f434dd48 100644 --- a/drivers/usb/input/ati_remote.c +++ b/drivers/usb/input/ati_remote.c @@ -418,6 +418,7 @@ static int ati_remote_sendpacket(struct ati_remote *ati_remote, u16 cmd, unsigne while (timeout && (ati_remote->out_urb->status == -EINPROGRESS) && !(ati_remote->send_flags & SEND_FLAG_COMPLETE)) { + set_current_state(TASK_INTERRUPTIBLE); timeout = schedule_timeout(timeout); rmb(); } -- cgit v1.2.3 From 0ae8fe6f9404fbeb6613aa99b2c52c793b45398c Mon Sep 17 00:00:00 2001 From: Nishanth Aravamudan Date: Wed, 29 Sep 2004 02:04:49 -0700 Subject: [PATCH] usb/kaweth: reorder set_current_state() and schedule_timeout() Reorder set_current_state() and schedule_timeout() for a minor cleanup. The reorder allows removing two of the set_current_state() calls. Signed-off-by: Greg Kroah-Hartman --- drivers/usb/net/kaweth.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/net/kaweth.c b/drivers/usb/net/kaweth.c index a84569a43c39..e5e5c649702c 100644 --- a/drivers/usb/net/kaweth.c +++ b/drivers/usb/net/kaweth.c @@ -1250,13 +1250,11 @@ static int usb_start_wait_urb(struct urb *urb, int timeout, int* actual_length) return status; } - set_current_state(TASK_UNINTERRUPTIBLE); while (timeout && !awd.done) { - timeout = schedule_timeout(timeout); set_current_state(TASK_UNINTERRUPTIBLE); + timeout = schedule_timeout(timeout); } - set_current_state(TASK_RUNNING); remove_wait_queue(&awd.wqh, &wait); if (!timeout) { -- cgit v1.2.3 From bd8a148a27cae7667b419540360f2af1c5c6f96b Mon Sep 17 00:00:00 2001 From: Nishanth Aravamudan Date: Wed, 29 Sep 2004 02:05:26 -0700 Subject: [PATCH] usb/hid-core: add set_current_state() before schedule_timeout() Add set_current_state() before schedule_timeout() so that the task delays as expected. Without the addition, schedule_timeout() will return immediately on subsequent iterations of the while-loop. Signed-off-by: Greg Kroah-Hartman --- drivers/usb/input/hid-core.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/input/hid-core.c b/drivers/usb/input/hid-core.c index 4039efd5e7dd..cb0421b04c1e 100644 --- a/drivers/usb/input/hid-core.c +++ b/drivers/usb/input/hid-core.c @@ -1258,8 +1258,10 @@ int hid_wait_io(struct hid_device *hid) add_wait_queue(&hid->wait, &wait); while (timeout && (test_bit(HID_CTRL_RUNNING, &hid->iofl) || - test_bit(HID_OUT_RUNNING, &hid->iofl))) + test_bit(HID_OUT_RUNNING, &hid->iofl))) { + set_current_state(TASK_UNINTERRUPTIBLE); timeout = schedule_timeout(timeout); + } set_current_state(TASK_RUNNING); remove_wait_queue(&hid->wait, &wait); -- cgit v1.2.3 From 6b2d5150fd0e20f449e8a1e55f758386ea72179e Mon Sep 17 00:00:00 2001 From: Nishanth Aravamudan Date: Wed, 29 Sep 2004 02:06:36 -0700 Subject: [PATCH] usb/mdc800: cleanup set_current_state() around wait queues This patch cleans up the wait queue usage in this driver. The state is no longer set until just before the task sleeps, which removes a few set_current_state()s. Correspondingly, the state doesn't need to be set back to TASK_RUNNING outside of the while-loops, as schedule_timeout() takes care of it. Signed-off-by: Greg Kroah-Hartman --- drivers/usb/image/mdc800.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/image/mdc800.c b/drivers/usb/image/mdc800.c index 17e900c198cf..66bb13307df5 100644 --- a/drivers/usb/image/mdc800.c +++ b/drivers/usb/image/mdc800.c @@ -317,7 +317,6 @@ static int mdc800_usb_waitForIRQ (int mode, int msec) mdc800->camera_request_ready=1+mode; add_wait_queue(&mdc800->irq_wait, &wait); - set_current_state(TASK_INTERRUPTIBLE); timeout = msec*HZ/1000; while (!mdc800->irq_woken && timeout) { @@ -325,7 +324,6 @@ static int mdc800_usb_waitForIRQ (int mode, int msec) timeout = schedule_timeout (timeout); } remove_wait_queue(&mdc800->irq_wait, &wait); - set_current_state(TASK_RUNNING); mdc800->irq_woken = 0; if (mdc800->camera_request_ready>0) @@ -725,7 +723,6 @@ static ssize_t mdc800_device_read (struct file *file, char __user *buf, size_t l set_current_state(TASK_UNINTERRUPTIBLE); timeout = schedule_timeout (timeout); } - set_current_state(TASK_RUNNING); remove_wait_queue(&mdc800->download_wait, &wait); mdc800->downloaded = 0; if (mdc800->download_urb->status != 0) @@ -851,7 +848,6 @@ static ssize_t mdc800_device_write (struct file *file, const char __user *buf, s set_current_state(TASK_UNINTERRUPTIBLE); timeout = schedule_timeout (timeout); } - set_current_state(TASK_RUNNING); remove_wait_queue(&mdc800->write_wait, &wait); mdc800->written = 0; if (mdc800->state == WORKING) -- cgit v1.2.3 From 888df350fee33f44385bcd1419b1fa948b07a0b3 Mon Sep 17 00:00:00 2001 From: Nishanth Aravamudan Date: Wed, 29 Sep 2004 02:07:09 -0700 Subject: [PATCH] usb/uss720: replace schedule_timeout() with msleep_interruptible() Use msleep_interruptible() instead of schedule_timeout() to guarantee the task delays as expected. Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/uss720.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/misc/uss720.c b/drivers/usb/misc/uss720.c index 0bc5ccc244ba..8b22320d5ea6 100644 --- a/drivers/usb/misc/uss720.c +++ b/drivers/usb/misc/uss720.c @@ -44,6 +44,7 @@ #include #include #include +#include /* * Version Information @@ -159,8 +160,7 @@ static int change_mode(struct parport *pp, int m) if (time_after_eq (jiffies, expire)) /* The FIFO is stuck. */ return -EBUSY; - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout((HZ + 99) / 100); + msleep_interruptible(10); if (signal_pending (current)) break; } -- cgit v1.2.3 From ec211cf2c6192f578fe7542db3054ac5af41ea0b Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Wed, 29 Sep 2004 02:20:23 -0700 Subject: [PATCH] USB: handle usb host allocation failures gracefully It looks like a host (like ohci or whatever) could try to allocate a new usb_device structure with usb_alloc_dev and get back a valid pointer even if the allocation of its private data failed. I first saw this in the 2.4 sources, but it looks like 2.6 has the same problem. This patch attempts to fix it by freeing dev if the ->allocate() routine fails, and then returns NULL instead of a potentially dangerous dev pointer. Signed-off-by: Jesse Barnes Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/usb.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index c4c457975ae3..2932c62b371f 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -775,7 +775,10 @@ usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus, unsigned port) init_MUTEX(&dev->serialize); if (dev->bus->op->allocate) - dev->bus->op->allocate(dev); + if (dev->bus->op->allocate(dev)) { + kfree(dev); + return NULL; + } return dev; } -- cgit v1.2.3 From cf44e793f222c3ff75f8608e5337dca96b910f1a Mon Sep 17 00:00:00 2001 From: David Brownell Date: Wed, 29 Sep 2004 02:20:47 -0700 Subject: [PATCH] USB: OHCI autodetects "need" for init reset quirk Ther recent QUIRK_INITRESET update turns out to need support for some ALi and ServerWorks chips, as well as the original SiS and OPTi cases. Rather than trying to maintain a quirk table (for what I still think must be a subtle init sequence bug), this patch kicks it in automatically when the frame clock init problem is detected. Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ohci-hcd.c | 8 ++++++++ drivers/usb/host/ohci-pci.c | 9 --------- 2 files changed, 8 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index 28f3fdd0751f..09f37cce2c75 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -545,6 +545,7 @@ static int ohci_run (struct ohci_hcd *ohci) /* 2msec timelimit here means no irqs/preempt */ spin_lock_irq (&ohci->lock); +retry: /* HC Reset requires max 10 us delay */ writel (OHCI_HCR, &ohci->regs->cmdstatus); temp = 30; /* ... allow extra time */ @@ -563,6 +564,8 @@ static int ohci_run (struct ohci_hcd *ohci) * ... but some hardware won't init fmInterval "by the book" * (SiS, OPTi ...), so reset again instead. SiS doesn't need * this if we write fmInterval after we're OPERATIONAL. + * Unclear about ALi, ServerWorks, and others ... this could + * easily be a longstanding bug in chip init on Linux. */ if (ohci->flags & OHCI_QUIRK_INITRESET) { writel (ohci->hc_control, &ohci->regs->control); @@ -586,6 +589,11 @@ static int ohci_run (struct ohci_hcd *ohci) */ if ((ohci_readl (&ohci->regs->fminterval) & 0x3fff0000) == 0 || !ohci_readl (&ohci->regs->periodicstart)) { + if (!(ohci->flags & OHCI_QUIRK_INITRESET)) { + ohci->flags |= OHCI_QUIRK_INITRESET; + ohci_dbg (ohci, "enabling initreset quirk\n"); + goto retry; + } spin_unlock_irq (&ohci->lock); ohci_err (ohci, "init err (%08x %04x)\n", ohci_readl (&ohci->regs->fminterval), diff --git a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c index 23f6b5f3fee5..2211a69e0b9d 100644 --- a/drivers/usb/host/ohci-pci.c +++ b/drivers/usb/host/ohci-pci.c @@ -69,8 +69,6 @@ ohci_pci_start (struct usb_hcd *hcd) && pdev->device == 0xc861) { ohci_info (ohci, "WARNING: OPTi workarounds unavailable\n"); - /* OPTi sometimes acts wierd during init */ - ohci->flags = OHCI_QUIRK_INITRESET; } /* Check for NSC87560. We have to look at the bridge (fn1) to @@ -88,13 +86,6 @@ ohci_pci_start (struct usb_hcd *hcd) ohci_info (ohci, "Using NSC SuperIO setup\n"); } } - - /* SiS sometimes acts wierd during init */ - else if (pdev->vendor == PCI_VENDOR_ID_SI) { - ohci->flags = OHCI_QUIRK_INITRESET; - ohci_info(ohci, "SiS init quirk\n"); - } - } /* NOTE: there may have already been a first reset, to -- cgit v1.2.3 From 680a90e8bd0ebbd8e82b01abca713a652c665768 Mon Sep 17 00:00:00 2001 From: Pete Zaitcev Date: Mon, 4 Oct 2004 00:34:36 -0700 Subject: [PATCH] USB: fix oops with latest ub driver in -mm tree On Fri, 1 Oct 2004 11:39:17 -0700 Greg KH wrote: > Pete, any ideas? Oh, it also happens on my UP laptop. >[...] > kernel BUG at kernel/timer.c:413! I have a suspicion. Actually, it was pointed to me by a kind soul before, but I forgot who he was, unfortunately. I'm not sure if this is the problem, but please try it if you can. It should apply on top of "latest". I really hate that word, but in this case I haven't got a version number. Signed-off-by: Greg Kroah-Hartman --- drivers/block/ub.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/block/ub.c b/drivers/block/ub.c index efaf8264c345..0c3567159375 100644 --- a/drivers/block/ub.c +++ b/drivers/block/ub.c @@ -842,7 +842,6 @@ static void ub_urb_complete(struct urb *urb, struct pt_regs *pt) { struct ub_dev *sc = urb->context; - del_timer(&sc->work_timer); ub_complete(&sc->work_done); tasklet_schedule(&sc->tasklet); } @@ -853,6 +852,7 @@ static void ub_scsi_action(unsigned long _dev) unsigned long flags; spin_lock_irqsave(&sc->lock, flags); + del_timer(&sc->work_timer); ub_scsi_dispatch(sc); spin_unlock_irqrestore(&sc->lock, flags); } -- cgit v1.2.3 From 68353b96ab49df0b62b2b5d735ac4d20ea2b35d5 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Mon, 4 Oct 2004 22:32:34 +0100 Subject: USB: Generic USB ATM/DSL core and completed SpeedTouch driver This can now load the firmware and initialise the modem for itself, with no need for any userspace help (except for putting the firmware in /lib/firmware in the first place). The core packet I/O code is split out into a separate file where it can be used by drivers for some of the other similar modems. Signed-off-by: David Woodhouse Signed-off-by: Greg Kroah-Hartman --- drivers/usb/Kconfig | 2 + drivers/usb/Makefile | 4 +- drivers/usb/atm/Kconfig | 30 + drivers/usb/atm/Makefile | 7 + drivers/usb/atm/speedtch.c | 879 +++++++++++++++++++++++++++ drivers/usb/atm/usb_atm.c | 1192 +++++++++++++++++++++++++++++++++++++ drivers/usb/atm/usb_atm.h | 159 +++++ drivers/usb/misc/Kconfig | 12 - drivers/usb/misc/Makefile | 1 - drivers/usb/misc/speedtch.c | 1373 ------------------------------------------- 10 files changed, 2272 insertions(+), 1387 deletions(-) create mode 100644 drivers/usb/atm/Kconfig create mode 100644 drivers/usb/atm/Makefile create mode 100644 drivers/usb/atm/speedtch.c create mode 100644 drivers/usb/atm/usb_atm.c create mode 100644 drivers/usb/atm/usb_atm.h delete mode 100644 drivers/usb/misc/speedtch.c (limited to 'drivers') diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig index 0e805e4f3aa1..42ab55b9b538 100644 --- a/drivers/usb/Kconfig +++ b/drivers/usb/Kconfig @@ -91,6 +91,8 @@ source "drivers/usb/serial/Kconfig" source "drivers/usb/misc/Kconfig" +source "drivers/usb/atm/Kconfig" + source "drivers/usb/gadget/Kconfig" endmenu diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index e3787f35cd21..63a7f2d80f83 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -62,8 +62,10 @@ obj-$(CONFIG_USB_LCD) += misc/ obj-$(CONFIG_USB_LED) += misc/ obj-$(CONFIG_USB_LEGOTOWER) += misc/ obj-$(CONFIG_USB_RIO500) += misc/ -obj-$(CONFIG_USB_SPEEDTOUCH) += misc/ obj-$(CONFIG_USB_TEST) += misc/ obj-$(CONFIG_USB_TIGL) += misc/ obj-$(CONFIG_USB_USS720) += misc/ obj-$(CONFIG_USB_PHIDGETSERVO) += misc/ + +obj-$(CONFIG_USB_ATM) += atm/ +obj-$(CONFIG_USB_SPEEDTOUCH) += atm/ diff --git a/drivers/usb/atm/Kconfig b/drivers/usb/atm/Kconfig new file mode 100644 index 000000000000..0d9f5379b8cf --- /dev/null +++ b/drivers/usb/atm/Kconfig @@ -0,0 +1,30 @@ +# +# USB ATM driver configuration +# +comment "USB ATM/DSL drivers" + depends on USB + +config USB_ATM + tristate "Generic USB ATM/DSL core I/O support" + depends on USB && ATM + select CRC32 + default n + help + This provides a library which is used for packet I/O by USB DSL + modems, such as the SpeedTouch driver below. + + To compile this driver as a module, choose M here: the + module will be called usb_atm. + +config USB_SPEEDTOUCH + tristate "Alcatel Speedtouch USB support" + depends on USB && ATM + select USB_ATM + help + Say Y here if you have an Alcatel SpeedTouch USB or SpeedTouch 330 + modem. In order to use your modem you will need to install the + two parts of the firmware, extracted by the user space tools; see + for details. + + To compile this driver as a module, choose M here: the + module will be called speedtch. diff --git a/drivers/usb/atm/Makefile b/drivers/usb/atm/Makefile new file mode 100644 index 000000000000..9213b8b97587 --- /dev/null +++ b/drivers/usb/atm/Makefile @@ -0,0 +1,7 @@ +# +# Makefile for the rest of the USB drivers +# (the ones that don't fit into any other categories) +# + +obj-$(CONFIG_USB_ATM) += usb_atm.o +obj-$(CONFIG_USB_SPEEDTOUCH) += speedtch.o diff --git a/drivers/usb/atm/speedtch.c b/drivers/usb/atm/speedtch.c new file mode 100644 index 000000000000..29be861235e0 --- /dev/null +++ b/drivers/usb/atm/speedtch.c @@ -0,0 +1,879 @@ +/****************************************************************************** + * speedtch.c - Alcatel SpeedTouch USB xDSL modem driver + * + * Copyright (C) 2001, Alcatel + * Copyright (C) 2003, Duncan Sands + * Copyright (C) 2004, David Woodhouse + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "usb_atm.h" + +/* +#define DEBUG +#define VERBOSE_DEBUG +*/ + +#if !defined (DEBUG) && defined (CONFIG_USB_DEBUG) +# define DEBUG +#endif + +#include + +#if defined(CONFIG_FW_LOADER) || defined(CONFIG_FW_LOADER_MODULE) +# define USE_FW_LOADER +#endif + +#ifdef DEBUG +#define DEBUG_ON(x) BUG_ON(x) +#else +#define DEBUG_ON(x) do { if (x); } while (0) +#endif + +#ifdef VERBOSE_DEBUG +static int udsl_print_packet (const unsigned char *data, int len); +#define PACKETDEBUG(arg...) udsl_print_packet (arg) +#define vdbg(arg...) dbg (arg) +#else +#define PACKETDEBUG(arg...) +#define vdbg(arg...) +#endif + +#define DRIVER_AUTHOR "Johan Verrept, Duncan Sands " +#define DRIVER_VERSION "1.8" +#define DRIVER_DESC "Alcatel SpeedTouch USB driver version " DRIVER_VERSION + +static const char speedtch_driver_name [] = "speedtch"; + +#define SPEEDTOUCH_VENDORID 0x06b9 +#define SPEEDTOUCH_PRODUCTID 0x4061 + +/* Timeout in jiffies */ +#define CTRL_TIMEOUT (2*HZ) +#define DATA_TIMEOUT (2*HZ) + +#define OFFSET_7 0 /* size 1 */ +#define OFFSET_b 1 /* size 8 */ +#define OFFSET_d 9 /* size 4 */ +#define OFFSET_e 13 /* size 1 */ +#define OFFSET_f 14 /* size 1 */ +#define TOTAL 15 + +#define SIZE_7 1 +#define SIZE_b 8 +#define SIZE_d 4 +#define SIZE_e 1 +#define SIZE_f 1 + +static int dl_512_first = 0; +static int sw_buffering = 0; + +module_param (dl_512_first, bool, 0444); +MODULE_PARM_DESC (dl_512_first, "Read 512 bytes before sending firmware"); + +module_param (sw_buffering, uint, 0444); +MODULE_PARM_DESC (sw_buffering, "Enable software buffering"); + +#define UDSL_IOCTL_LINE_UP 1 +#define UDSL_IOCTL_LINE_DOWN 2 + +#define SPEEDTCH_ENDPOINT_INT 0x81 +#define SPEEDTCH_ENDPOINT_DATA 0x07 +#define SPEEDTCH_ENDPOINT_FIRMWARE 0x05 + +#define hex2int(c) ( (c >= '0') && (c <= '9') ? (c - '0') : ((c & 0xf) + 9) ) + +static struct usb_device_id speedtch_usb_ids [] = { + { USB_DEVICE (SPEEDTOUCH_VENDORID, SPEEDTOUCH_PRODUCTID) }, + { } +}; + +MODULE_DEVICE_TABLE (usb, speedtch_usb_ids); + + +struct speedtch_instance_data { + struct udsl_instance_data u; + + u16 revision; + + /* Status */ + struct urb *int_urb; + unsigned char int_data[16]; + struct work_struct poll_work; + struct timer_list poll_timer; + char fwname[25]; +}; +/* USB */ + +static int speedtch_usb_probe (struct usb_interface *intf, const struct usb_device_id *id); +static void speedtch_usb_disconnect (struct usb_interface *intf); +static int speedtch_usb_ioctl (struct usb_interface *intf, unsigned int code, void *user_data); +static void speedtch_handle_int (struct urb *urb, struct pt_regs *regs); +static void speedtch_poll_status (struct speedtch_instance_data *instance); + +static struct usb_driver speedtch_usb_driver = { + .owner = THIS_MODULE, + .name = speedtch_driver_name, + .probe = speedtch_usb_probe, + .disconnect = speedtch_usb_disconnect, + .ioctl = speedtch_usb_ioctl, + .id_table = speedtch_usb_ids, +}; + + +/*************** +** firmware ** +***************/ + +static void speedtch_got_firmware (struct speedtch_instance_data *instance, int got_it) +{ + int err; + struct usb_interface *intf; + + down (&instance->u.serialize); /* vs self, speedtch_firmware_start */ + if (instance->u.status == UDSL_LOADED_FIRMWARE) + goto out; + if (!got_it) { + instance->u.status = UDSL_NO_FIRMWARE; + goto out; + } + if ((err = usb_set_interface (instance->u.usb_dev, 1, 1)) < 0) { + dbg ("speedtch_got_firmware: usb_set_interface returned %d!", err); + instance->u.status = UDSL_NO_FIRMWARE; + goto out; + } + + /* Set up interrupt endpoint */ + intf = usb_ifnum_to_if(instance->u.usb_dev, 0); + if (intf && !usb_driver_claim_interface (&speedtch_usb_driver, intf, NULL)) { + + instance->int_urb = usb_alloc_urb(0, GFP_KERNEL); + if (instance->int_urb) { + + usb_fill_int_urb(instance->int_urb, instance->u.usb_dev, + usb_rcvintpipe(instance->u.usb_dev, SPEEDTCH_ENDPOINT_INT), + instance->int_data, sizeof(instance->int_data), + speedtch_handle_int, instance, 50); + err = usb_submit_urb(instance->int_urb, GFP_KERNEL); + if (err) { + /* Doesn't matter; we'll poll anyway */ + dbg ("speedtch_got_firmware: Submission of interrupt URB failed %d", err); + usb_free_urb(instance->int_urb); + instance->int_urb = NULL; + usb_driver_release_interface (&speedtch_usb_driver, intf); + } + } + } + /* Start status polling */ + mod_timer(&instance->poll_timer, jiffies + (1*HZ)); + + instance->u.status = UDSL_LOADED_FIRMWARE; + tasklet_schedule (&instance->u.receive_tasklet); +out: + up (&instance->u.serialize); + wake_up_interruptible (&instance->u.firmware_waiters); +} + +static int speedtch_set_swbuff (struct speedtch_instance_data *instance, int state) +{ + struct usb_device *dev = instance->u.usb_dev; + int ret; + + ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + 0x32, 0x40, state?0x01:0x00, + 0x00, NULL, 0, 100); + if (ret < 0) { + printk("Warning: %sabling SW buffering: usb_control_msg returned %d\n", + state?"En":"Dis", ret); + return ret; + } + + dbg("speedtch_set_swbuff: %sbled SW buffering", state?"En":"Dis"); + return 0; +} + +static void speedtch_test_sequence(struct speedtch_instance_data *instance) +{ + struct usb_device *dev = instance->u.usb_dev; + unsigned char buf[10]; + int ret; + + /* URB 147 */ + buf[0] = 0x1c; buf[1] = 0x50; + ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + 0x01, 0x40, 0x0b, 0x00, buf, 2, 100); + if (ret < 0) + printk(KERN_WARNING "%s failed on URB147: %d\n", __func__, ret); + + /* URB 148 */ + buf[0] = 0x32; buf[1] = 0x00; + ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + 0x01, 0x40, 0x02, 0x00, buf, 2, 100); + if (ret < 0) + printk(KERN_WARNING "%s failed on URB148: %d\n", __func__, ret); + + /* URB 149 */ + buf[0] = 0x01; buf[1] = 0x00; buf[2] = 0x01; + ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + 0x01, 0x40, 0x03, 0x00, buf, 3, 100); + if (ret < 0) + printk(KERN_WARNING "%s failed on URB149: %d\n", __func__, ret); + + /* URB 150 */ + buf[0] = 0x01; buf[1] = 0x00; buf[2] = 0x01; + ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + 0x01, 0x40, 0x04, 0x00, buf, 3, 100); + if (ret < 0) + printk(KERN_WARNING "%s failed on URB150: %d\n", __func__, ret); +} + +static int speedtch_start_synchro (struct speedtch_instance_data *instance) +{ + struct usb_device *dev = instance->u.usb_dev; + unsigned char buf[2]; + int ret; + + ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + 0x12, 0xc0, 0x04, 0x00, + buf, sizeof(buf), CTRL_TIMEOUT); + if (ret < 0) { + printk(KERN_WARNING "SpeedTouch: Failed to start ADSL synchronisation: %d\n", ret); + return ret; + } + + dbg("speedtch_start_synchro: modem prodded. %d Bytes returned: %02x %02x", ret, buf[0], buf[1]); + return 0; +} + +static void speedtch_handle_int (struct urb *urb, struct pt_regs *regs) +{ + struct speedtch_instance_data *instance = urb->context; + unsigned int count = urb->actual_length; + int ret; + + /* The magic interrupt for "up state" */ + const static unsigned char up_int[6] = { 0xa1, 0x00, 0x01, 0x00, 0x00, 0x00}; + /* The magic interrupt for "down state" */ + const static unsigned char down_int[6] = { 0xa1, 0x00, 0x00, 0x00, 0x00, 0x00}; + + + switch (urb->status) { + case 0: + /* success */ + break; + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + /* this urb is terminated, clean up */ + dbg("%s - urb shutting down with status: %d", __func__, urb->status); + return; + default: + dbg("%s - nonzero urb status received: %d", __func__, urb->status); + goto exit; + } + + if (count < 6) { + dbg("%s - int packet too short", __func__); + goto exit; + } + + if (!memcmp(up_int, instance->int_data, 6)) { + del_timer(&instance->poll_timer); + printk(KERN_NOTICE "DSL line goes up\n"); + } else if (!memcmp(down_int, instance->int_data, 6)) { + del_timer(&instance->poll_timer); + printk(KERN_NOTICE "DSL line goes down\n"); + } else { + int i; + + printk(KERN_DEBUG "Unknown interrupt packet of %d bytes:", count); + for (i=0; i < count; i++) + printk(" %02x", instance->int_data[i]); + printk("\n"); + } + schedule_work(&instance->poll_work); + + exit: + rmb(); + if (!instance->int_urb) + return; + + ret = usb_submit_urb (urb, GFP_ATOMIC); + if (ret) + err ("%s - usb_submit_urb failed with result %d", + __func__, ret); +} + +static int speedtch_get_status(struct speedtch_instance_data *instance, unsigned char *buf) +{ + struct usb_device *dev = instance->u.usb_dev; + int ret; + + memset(buf,0,TOTAL); + + ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + 0x12, 0xc0, 0x07, 0x00, buf+OFFSET_7, SIZE_7, CTRL_TIMEOUT); + if (ret<0) { + dbg("MSG 7 failed"); + return(ret); + } + + ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + 0x12, 0xc0, 0x0b, 0x00, buf+OFFSET_b, SIZE_b, CTRL_TIMEOUT); + if (ret<0) { + dbg("MSG B failed"); + return(ret); + } + + ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + 0x12, 0xc0, 0x0d, 0x00, buf+OFFSET_d, SIZE_d, CTRL_TIMEOUT); + if (ret<0) { + dbg("MSG D failed"); + return(ret); + } + + ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + 0x01, 0xc0, 0x0e, 0x00, buf+OFFSET_e, SIZE_e, CTRL_TIMEOUT); + if (ret<0) { + dbg("MSG E failed"); + return(ret); + } + + ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + 0x01, 0xc0, 0x0f, 0x00, buf+OFFSET_f, SIZE_f, CTRL_TIMEOUT); + if (ret<0) { + dbg("MSG F failed"); + return(ret); + } + + return 0; +} + +static void speedtch_poll_status(struct speedtch_instance_data *instance) +{ + unsigned char buf[TOTAL]; + int ret; + + ret = speedtch_get_status(instance, buf); + if (ret) { + printk(KERN_WARNING "SpeedTouch: Error %d fetching device status\n", ret); + return; + } + + dbg("Line state %02x", buf[OFFSET_7]); + + switch (buf[OFFSET_7]) { + case 0: + if (instance->u.atm_dev->signal != ATM_PHY_SIG_LOST) { + instance->u.atm_dev->signal = ATM_PHY_SIG_LOST; + printk(KERN_NOTICE "ADSL line is down\n"); + } + break; + + case 0x08: + if (instance->u.atm_dev->signal != ATM_PHY_SIG_UNKNOWN) { + instance->u.atm_dev->signal = ATM_PHY_SIG_UNKNOWN; + printk(KERN_NOTICE "ADSL line is blocked?\n"); + } + break; + + case 0x10: + if (instance->u.atm_dev->signal != ATM_PHY_SIG_LOST) { + instance->u.atm_dev->signal = ATM_PHY_SIG_LOST; + printk(KERN_NOTICE "ADSL line is synchronising\n"); + } + break; + + case 0x20: + if (instance->u.atm_dev->signal != ATM_PHY_SIG_FOUND) { + int down_speed = buf[OFFSET_b] | (buf[OFFSET_b+1]<<8) + | (buf[OFFSET_b+2]<<16) | (buf[OFFSET_b+3]<<24); + int up_speed = buf[OFFSET_b+4] | (buf[OFFSET_b+5]<<8) + | (buf[OFFSET_b+6]<<16) | (buf[OFFSET_b+7]<<24); + + if(!(down_speed & 0x0000ffff) && + !(up_speed & 0x0000ffff)) { + down_speed>>=16; + up_speed>>=16; + } + instance->u.atm_dev->link_rate = down_speed * 1000 / 424; + instance->u.atm_dev->signal = ATM_PHY_SIG_FOUND; + + printk(KERN_NOTICE "ADSL line is up (%d Kib/s down | %d Kib/s up)\n", + down_speed, up_speed); + } + break; + + default: + if (instance->u.atm_dev->signal != ATM_PHY_SIG_UNKNOWN) { + instance->u.atm_dev->signal = ATM_PHY_SIG_UNKNOWN; + printk(KERN_NOTICE "Unknown line state %02x\n", buf[OFFSET_7]); + } + break; + } +} + +static void speedtch_timer_poll (unsigned long data) +{ + struct speedtch_instance_data *instance = (struct speedtch_instance_data *) data; + + schedule_work(&instance->poll_work); + mod_timer(&instance->poll_timer, jiffies + (5*HZ)); +} + +static void speedtch_firmware_load(const struct firmware *fw1, void *context) +{ + const struct firmware *fw2; + unsigned char *buffer; + struct speedtch_instance_data *instance = context; + struct usb_interface *intf; + struct usb_device *dev = instance->u.usb_dev; + int actual_length, ret; + int pg; + + dbg ("speedtch_firmware_load"); + + ret = -1; + + BUG_ON(!instance); + + if (!(intf = usb_ifnum_to_if (dev, 2))) { + dbg ("speedtch_firmware_load: interface not found!"); + goto fail; + } + + if (!(buffer = kmalloc (0x1000, GFP_KERNEL))) { + dbg ("speedtch_firmware_load: no memory for buffer!"); + goto fail; + } + + if (!fw1) { + dbg ("speedtch_firmware_load: no firmware 'speedtch_boot_rev%d.%02x", + instance->revision >> 8, instance->revision & 0xff); + + snprintf(instance->fwname, sizeof(instance->fwname), "speedtch_boot_rev%d", + instance->revision >> 8); + + ret = request_firmware(&fw1, instance->fwname, &dev->dev); + if (ret < 0) { + dbg ("speedtch_firmware_load: no firmware '%s'", instance->fwname); + + strlcpy(instance->fwname, "speedtch_boot", sizeof(instance->fwname)); + ret = request_firmware(&fw1, instance->fwname, &dev->dev); + } + if (ret < 0) { + dbg ("speedtch_firmware_load: no firmware '%s'", instance->fwname); + printk(KERN_INFO "speedtch: No boot firmware found. Assuming userspace firmware loader\n"); + goto fail_buf; + } + } + snprintf(instance->fwname, sizeof(instance->fwname), "speedtch_main_rev%d.%02x", + instance->revision >> 8, instance->revision & 0xff); + ret = request_firmware(&fw2, instance->fwname, &dev->dev); + if (ret < 0) { + dbg ("speedtch_firmware_load: no firmware '%s'", instance->fwname); + snprintf(instance->fwname, sizeof(instance->fwname), "speedtch_main_rev%d", + instance->revision >> 8); + + ret = request_firmware(&fw2, instance->fwname, &dev->dev); + } + if (ret < 0) { + dbg ("speedtch_firmware_load: no firmware '%s'", instance->fwname); + + strlcpy(instance->fwname, "speedtch_main", sizeof(instance->fwname)); + ret = request_firmware(&fw2, instance->fwname, &dev->dev); + } + if (ret < 0) { + dbg ("speedtch_firmware_load: no firmware '%s'", instance->fwname); + printk(KERN_INFO "speedtch: No main firmware found. Assuming userspace firmware loader\n"); + goto fail_buf; + } + + /* OK, we have the firmware. So try to claim interface #2 and actually + try uploading it. There's a slight possibility that the userspace + modem_run could be running too, and may have beaten us to it */ + if ((ret = usb_driver_claim_interface (&speedtch_usb_driver, intf, NULL)) < 0) { + dbg ("speedtch_firmware_start: interface in use (%d)!", ret); + goto fail_fw2; + } + + /* URB 7 */ + if (dl_512_first) { /* some modems need a read before writing the firmware */ + ret = usb_bulk_msg (instance->u.usb_dev, + usb_rcvbulkpipe (instance->u.usb_dev, SPEEDTCH_ENDPOINT_FIRMWARE), + buffer, + 0x200, + &actual_length, + 2 * HZ); + + if (ret < 0 && ret != -ETIMEDOUT) + dbg ("speedtch_firmware_load: read BLOCK0 from modem failed (%d)!", ret); + else + dbg("speedtch_firmware_load: BLOCK0 downloaded (%d bytes)", ret); + } + + /* URB 8 : both leds are static green */ + for (pg = 0; pg * 0x1000 < fw1->size; pg++) { + int thislen = min_t(int, 0x1000, fw1->size - (pg*0x1000)); + memcpy(buffer, fw1->data + (pg*0x1000), thislen); + + ret = usb_bulk_msg (instance->u.usb_dev, + usb_sndbulkpipe (instance->u.usb_dev, SPEEDTCH_ENDPOINT_FIRMWARE), + buffer, + thislen, + &actual_length, + DATA_TIMEOUT); + + if (ret < 0) { + dbg ("speedtch_firmware_load: write BLOCK1 to modem failed (%d)!", ret); + goto fail_release; + } + dbg("speedtch_firmware_load: BLOCK1 uploaded (%d bytes)", fw1->size); + } + + /* USB led blinking green, ADSL led off */ + + /* URB 11 */ + ret = usb_bulk_msg (instance->u.usb_dev, + usb_rcvbulkpipe (instance->u.usb_dev, SPEEDTCH_ENDPOINT_FIRMWARE), + buffer, + 0x200, + &actual_length, + DATA_TIMEOUT); + + if (ret < 0) { + dbg ("speedtch_firmware_load: read BLOCK2 from modem failed (%d)!", ret); + goto fail_release; + } + dbg("speedtch_firmware_load: BLOCK2 downloaded (%d bytes)", actual_length); + + /* URBs 12 to 139 - USB led blinking green, ADSL led off */ + for (pg = 0; pg * 0x1000 < fw2->size; pg++) { + int thislen = min_t(int, 0x1000, fw2->size - (pg*0x1000)); + memcpy(buffer, fw2->data + (pg*0x1000), thislen); + + ret = usb_bulk_msg (instance->u.usb_dev, + usb_sndbulkpipe (instance->u.usb_dev, SPEEDTCH_ENDPOINT_FIRMWARE), + buffer, + thislen, + &actual_length, + DATA_TIMEOUT); + + if (ret < 0) { + dbg ("speedtch_firmware_load: write BLOCK3 to modem failed (%d)!", ret); + goto fail_release; + } + } + dbg("speedtch_firmware_load: BLOCK3 uploaded (%d bytes)", fw2->size); + + /* USB led static green, ADSL led static red */ + + /* URB 142 */ + ret = usb_bulk_msg (instance->u.usb_dev, + usb_rcvbulkpipe (instance->u.usb_dev, SPEEDTCH_ENDPOINT_FIRMWARE), + buffer, + 0x200, + &actual_length, + DATA_TIMEOUT); + + if (ret < 0) { + dbg("speedtch_firmware_load: read BLOCK4 from modem failed (%d)!", ret); + goto fail_release; + } + + /* success */ + dbg("speedtch_firmware_load: BLOCK4 downloaded (%d bytes)", actual_length); + + /* Delay to allow firmware to start up. We can do this here + because we're in our own kernel thread anyway. */ + msleep(1000); + + /* Enable software buffering, if requested */ + if (sw_buffering) + speedtch_set_swbuff(instance, 1); + + /* Magic spell; don't ask us what this does */ + speedtch_test_sequence(instance); + + /* Start modem synchronisation */ + if (speedtch_start_synchro(instance)) + dbg("speedtch_start_synchro: failed\n"); + + speedtch_got_firmware(instance, 1); + + goto fail_fw2; /* The got_firmware(0) is a NOP anyway */ + + fail_release: + /* We only release interface #2 if loading the firmware failed; we don't + do it if we succeeded. This prevents the userspace modem_run tool from + trying to load the firmware itself */ + usb_driver_release_interface (&speedtch_usb_driver, intf); + fail_fw2: + release_firmware(fw2); + fail_buf: + kfree (buffer); + fail: + speedtch_got_firmware (instance, 0); + udsl_put_instance(&instance->u); +} + +static void speedtch_firmware_start (struct speedtch_instance_data *instance) +{ +#ifdef USE_FW_LOADER + int ret; +#endif + + dbg ("speedtch_firmware_start"); + + down (&instance->u.serialize); /* vs self, speedtch_got_firmware */ + + if (instance->u.status >= UDSL_LOADING_FIRMWARE) { + up (&instance->u.serialize); + return; + } + + instance->u.status = UDSL_LOADING_FIRMWARE; + up (&instance->u.serialize); + + udsl_get_instance(&instance->u); + +#ifdef USE_FW_LOADER + snprintf(instance->fwname, sizeof(instance->fwname), "speedtch_boot_rev%d.%02x", + instance->revision >> 8, instance->revision & 0xff); + printk("Look for %s\n", instance->fwname); + ret = request_firmware_nowait (THIS_MODULE, + instance->fwname, + &instance->u.usb_dev->dev, + instance, + speedtch_firmware_load); + + if (ret >= 0) + return; /* OK */ + + dbg ("speedtch_firmware_start: request_firmware_nowait failed (%d)!", ret); + + + /* Just pretend it never happened... hope modem_run happens */ +#endif /* USE_FW_LOADER */ + speedtch_got_firmware (instance, 0); + udsl_put_instance(&instance->u); +} + +static int speedtch_firmware_wait (struct udsl_instance_data *instance) +{ + speedtch_firmware_start ((void *) instance); + + if (wait_event_interruptible (instance->firmware_waiters, instance->status != UDSL_LOADING_FIRMWARE) < 0) + return -ERESTARTSYS; + + return (instance->status == UDSL_LOADED_FIRMWARE) ? 0 : -EAGAIN; +} + +/********** +** USB ** +**********/ + +static int speedtch_usb_ioctl (struct usb_interface *intf, unsigned int code, void *user_data) +{ + struct speedtch_instance_data *instance = usb_get_intfdata (intf); + + dbg ("speedtch_usb_ioctl entered"); + + if (!instance) { + dbg ("speedtch_usb_ioctl: NULL instance!"); + return -ENODEV; + } + + switch (code) { + case UDSL_IOCTL_LINE_UP: + instance->u.atm_dev->signal = ATM_PHY_SIG_FOUND; + speedtch_got_firmware (instance, 1); + return (instance->u.status == UDSL_LOADED_FIRMWARE) ? 0 : -EIO; + case UDSL_IOCTL_LINE_DOWN: + instance->u.atm_dev->signal = ATM_PHY_SIG_LOST; + return 0; + default: + return -ENOTTY; + } +} + +static int speedtch_usb_probe (struct usb_interface *intf, const struct usb_device_id *id) +{ + struct usb_device *dev = interface_to_usbdev(intf); + int ifnum = intf->altsetting->desc.bInterfaceNumber; + struct speedtch_instance_data *instance; + unsigned char mac_str [13]; + int ret, i; + char buf7[SIZE_7]; + + dbg ("speedtch_usb_probe: trying device with vendor=0x%x, product=0x%x, ifnum %d", + dev->descriptor.idVendor, dev->descriptor.idProduct, ifnum); + + if ((dev->descriptor.bDeviceClass != USB_CLASS_VENDOR_SPEC) || + (dev->descriptor.idVendor != SPEEDTOUCH_VENDORID) || + (dev->descriptor.idProduct != SPEEDTOUCH_PRODUCTID) || (ifnum != 1)) + return -ENODEV; + + dbg ("speedtch_usb_probe: device accepted"); + + /* instance init */ + if (!(instance = kmalloc (sizeof (struct speedtch_instance_data), GFP_KERNEL))) { + dbg ("speedtch_usb_probe: no memory for instance data!"); + return -ENOMEM; + } + + memset (instance, 0, sizeof (struct speedtch_instance_data)); + + if ((ret = usb_set_interface (dev, 0, 0)) < 0) + goto fail; + + if ((ret = usb_set_interface (dev, 2, 0)) < 0) + goto fail; + + instance->u.data_endpoint = SPEEDTCH_ENDPOINT_DATA; + instance->u.firmware_wait = speedtch_firmware_wait; + instance->u.driver_name = speedtch_driver_name; + + ret = udsl_instance_setup(dev, &instance->u); + if (ret) + goto fail; + + init_timer(&instance->poll_timer); + instance->poll_timer.function = speedtch_timer_poll; + instance->poll_timer.data = (unsigned long)instance; + + INIT_WORK(&instance->poll_work, (void *)speedtch_poll_status, instance); + + switch (dev->descriptor.bcdDevice) { + case 0x0000: + case 0x0200: + case 0x0400: + case 0x0401: + instance->revision = dev->descriptor.bcdDevice; + break; + default: + instance->revision = 0x200; + printk(KERN_INFO "Unexpected SpeedTouch revision %04x, treating as Rev 2.00.\n", + dev->descriptor.bcdDevice); + break; + } + + /* set MAC address, it is stored in the serial number */ + memset (instance->u.atm_dev->esi, 0, sizeof (instance->u.atm_dev->esi)); + if (usb_string (dev, dev->descriptor.iSerialNumber, mac_str, sizeof (mac_str)) == 12) + for (i = 0; i < 6; i++) + instance->u.atm_dev->esi [i] = (hex2int (mac_str [i * 2]) * 16) + (hex2int (mac_str [i * 2 + 1])); + + + /* First check whether the modem already seems to be alive */ + ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + 0x12, 0xc0, 0x07, 0x00, buf7, SIZE_7, HZ/2); + + if (ret == SIZE_7) { + dbg("firmware appears to be already loaded"); + speedtch_got_firmware(instance, 1); + speedtch_poll_status(instance); + } else { + speedtch_firmware_start (instance); + } + + usb_set_intfdata (intf, instance); + + return 0; + +fail: + kfree (instance); + + return -ENOMEM; +} + +static void speedtch_usb_disconnect (struct usb_interface *intf) +{ + struct speedtch_instance_data *instance = usb_get_intfdata (intf); + + dbg ("speedtch_usb_disconnect entered"); + + if (!instance) { + dbg ("speedtch_usb_disconnect: NULL instance!"); + return; + } + + if (instance->int_urb) { + struct urb *int_urb = instance->int_urb; + instance->int_urb = NULL; + wmb(); + usb_unlink_urb(int_urb); + usb_free_urb(int_urb); + } + + instance->int_data[0] = 1; + del_timer_sync(&instance->poll_timer); + wmb(); + flush_scheduled_work(); + + udsl_instance_disconnect(&instance->u); + + /* clean up */ + usb_set_intfdata (intf, NULL); + udsl_put_instance(&instance->u); +} + + +/*********** +** init ** +***********/ + +static int __init speedtch_usb_init (void) +{ + dbg ("speedtch_usb_init: driver version " DRIVER_VERSION); + + return usb_register (&speedtch_usb_driver); +} + +static void __exit speedtch_usb_cleanup (void) +{ + dbg ("speedtch_usb_cleanup entered"); + + usb_deregister (&speedtch_usb_driver); +} + +module_init (speedtch_usb_init); +module_exit (speedtch_usb_cleanup); + +MODULE_AUTHOR (DRIVER_AUTHOR); +MODULE_DESCRIPTION (DRIVER_DESC); +MODULE_LICENSE ("GPL"); +MODULE_VERSION (DRIVER_VERSION); diff --git a/drivers/usb/atm/usb_atm.c b/drivers/usb/atm/usb_atm.c new file mode 100644 index 000000000000..9a792ab71e49 --- /dev/null +++ b/drivers/usb/atm/usb_atm.c @@ -0,0 +1,1192 @@ +/****************************************************************************** + * usb_atm.c - Generic USB xDSL driver core + * + * Copyright (C) 2001, Alcatel + * Copyright (C) 2003, Duncan Sands, SolNegro, Josep Comas + * Copyright (C) 2004, David Woodhouse + * + * 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. + * + ******************************************************************************/ + +/* + * Written by Johan Verrept, maintained by Duncan Sands (duncan.sands@free.fr) + * + * 1.7+: - See the check-in logs + * + * 1.6: - No longer opens a connection if the firmware is not loaded + * - Added support for the speedtouch 330 + * - Removed the limit on the number of devices + * - Module now autoloads on device plugin + * - Merged relevant parts of sarlib + * - Replaced the kernel thread with a tasklet + * - New packet transmission code + * - Changed proc file contents + * - Fixed all known SMP races + * - Many fixes and cleanups + * - Various fixes by Oliver Neukum (oliver@neukum.name) + * + * 1.5A: - Version for inclusion in 2.5 series kernel + * - Modifications by Richard Purdie (rpurdie@rpsys.net) + * - made compatible with kernel 2.5.6 onwards by changing + * udsl_usb_send_data_context->urb to a pointer and adding code + * to alloc and free it + * - remove_wait_queue() added to udsl_atm_processqueue_thread() + * + * 1.5: - fixed memory leak when atmsar_decode_aal5 returned NULL. + * (reported by stephen.robinson@zen.co.uk) + * + * 1.4: - changed the spin_lock() under interrupt to spin_lock_irqsave() + * - unlink all active send urbs of a vcc that is being closed. + * + * 1.3.1: - added the version number + * + * 1.3: - Added multiple send urb support + * - fixed memory leak and vcc->tx_inuse starvation bug + * when not enough memory left in vcc. + * + * 1.2: - Fixed race condition in udsl_usb_send_data() + * 1.1: - Turned off packet debugging + * + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "usb_atm.h" + +/* +#define DEBUG +#define VERBOSE_DEBUG +*/ + +#if !defined (DEBUG) && defined (CONFIG_USB_DEBUG) +# define DEBUG +#endif + +#include + +#ifdef DEBUG +#define DEBUG_ON(x) BUG_ON(x) +#else +#define DEBUG_ON(x) do { if (x); } while (0) +#endif + +#ifdef VERBOSE_DEBUG +static int udsl_print_packet (const unsigned char *data, int len); +#define PACKETDEBUG(arg...) udsl_print_packet (arg) +#define vdbg(arg...) dbg (arg) +#else +#define PACKETDEBUG(arg...) +#define vdbg(arg...) +#endif + +#define DRIVER_AUTHOR "Johan Verrept, Duncan Sands " +#define DRIVER_VERSION "1.8" +#define DRIVER_DESC "Alcatel SpeedTouch USB driver version " DRIVER_VERSION + +static unsigned int num_rcv_urbs = UDSL_DEFAULT_RCV_URBS; +static unsigned int num_snd_urbs = UDSL_DEFAULT_SND_URBS; +static unsigned int num_rcv_bufs = UDSL_DEFAULT_RCV_BUFS; +static unsigned int num_snd_bufs = UDSL_DEFAULT_SND_BUFS; +static unsigned int rcv_buf_size = UDSL_DEFAULT_RCV_BUF_SIZE; +static unsigned int snd_buf_size = UDSL_DEFAULT_SND_BUF_SIZE; + +module_param (num_rcv_urbs, uint, 0444); +MODULE_PARM_DESC (num_rcv_urbs, "Number of urbs used for reception (range: 0-" __MODULE_STRING (UDSL_MAX_RCV_URBS) ", default: " __MODULE_STRING (UDSL_DEFAULT_RCV_URBS) ")"); + +module_param (num_snd_urbs, uint, 0444); +MODULE_PARM_DESC (num_snd_urbs, "Number of urbs used for transmission (range: 0-" __MODULE_STRING (UDSL_MAX_SND_URBS) ", default: " __MODULE_STRING (UDSL_DEFAULT_SND_URBS) ")"); + +module_param (num_rcv_bufs, uint, 0444); +MODULE_PARM_DESC (num_rcv_bufs, "Number of buffers used for reception (range: 0-" __MODULE_STRING (UDSL_MAX_RCV_BUFS) ", default: " __MODULE_STRING (UDSL_DEFAULT_RCV_BUFS) ")"); + +module_param (num_snd_bufs, uint, 0444); +MODULE_PARM_DESC (num_snd_bufs, "Number of buffers used for transmission (range: 0-" __MODULE_STRING (UDSL_MAX_SND_BUFS) ", default: " __MODULE_STRING (UDSL_DEFAULT_SND_BUFS) ")"); + +module_param (rcv_buf_size, uint, 0444); +MODULE_PARM_DESC (rcv_buf_size, "Size of the buffers used for reception (range: 0-" __MODULE_STRING (UDSL_MAX_RCV_BUF_SIZE) ", default: " __MODULE_STRING (UDSL_DEFAULT_RCV_BUF_SIZE) ")"); + +module_param (snd_buf_size, uint, 0444); +MODULE_PARM_DESC (snd_buf_size, "Size of the buffers used for transmission (range: 0-" __MODULE_STRING (UDSL_MAX_SND_BUF_SIZE) ", default: " __MODULE_STRING (UDSL_DEFAULT_SND_BUF_SIZE) ")"); + +/* ATM */ + +static void udsl_atm_dev_close (struct atm_dev *dev); +static int udsl_atm_open (struct atm_vcc *vcc); +static void udsl_atm_close (struct atm_vcc *vcc); +static int udsl_atm_ioctl (struct atm_dev *dev, unsigned int cmd, void __user *arg); +static int udsl_atm_send (struct atm_vcc *vcc, struct sk_buff *skb); +static int udsl_atm_proc_read (struct atm_dev *atm_dev, loff_t *pos, char *page); + +static struct atmdev_ops udsl_atm_devops = { + .dev_close = udsl_atm_dev_close, + .open = udsl_atm_open, + .close = udsl_atm_close, + .ioctl = udsl_atm_ioctl, + .send = udsl_atm_send, + .proc_read = udsl_atm_proc_read, + .owner = THIS_MODULE, +}; + + +/*********** +** misc ** +***********/ + +static inline void udsl_pop (struct atm_vcc *vcc, struct sk_buff *skb) +{ + if (vcc->pop) + vcc->pop (vcc, skb); + else + dev_kfree_skb (skb); +} + + +/************* +** decode ** +*************/ + +static inline struct udsl_vcc_data *udsl_find_vcc (struct udsl_instance_data *instance, short vpi, int vci) +{ + struct udsl_vcc_data *vcc; + + list_for_each_entry (vcc, &instance->vcc_list, list) + if ((vcc->vci == vci) && (vcc->vpi == vpi)) + return vcc; + return NULL; +} + +static void udsl_extract_cells (struct udsl_instance_data *instance, unsigned char *source, unsigned int howmany) +{ + struct udsl_vcc_data *cached_vcc = NULL; + struct atm_vcc *vcc; + struct sk_buff *sarb; + struct udsl_vcc_data *vcc_data; + int cached_vci = 0; + unsigned int i; + int pti; + int vci; + short cached_vpi = 0; + short vpi; + + for (i = 0; i < howmany; i++, source += ATM_CELL_SIZE + instance->rcv_padding) { + vpi = ((source [0] & 0x0f) << 4) | (source [1] >> 4); + vci = ((source [1] & 0x0f) << 12) | (source [2] << 4) | (source [3] >> 4); + pti = (source [3] & 0x2) != 0; + + vdbg ("udsl_extract_cells: vpi %hd, vci %d, pti %d", vpi, vci, pti); + + if (cached_vcc && (vci == cached_vci) && (vpi == cached_vpi)) + vcc_data = cached_vcc; + else if ((vcc_data = udsl_find_vcc (instance, vpi, vci))) { + cached_vcc = vcc_data; + cached_vpi = vpi; + cached_vci = vci; + } else { + dbg ("udsl_extract_cells: unknown vpi/vci (%hd/%d)!", vpi, vci); + continue; + } + + vcc = vcc_data->vcc; + sarb = vcc_data->sarb; + + if (sarb->tail + ATM_CELL_PAYLOAD > sarb->end) { + dbg ("udsl_extract_cells: buffer overrun (sarb->len %u, vcc: 0x%p)!", sarb->len, vcc); + /* discard cells already received */ + skb_trim (sarb, 0); + } + + memcpy (sarb->tail, source + ATM_CELL_HEADER, ATM_CELL_PAYLOAD); + __skb_put (sarb, ATM_CELL_PAYLOAD); + + if (pti) { + struct sk_buff *skb; + unsigned int length; + unsigned int pdu_length; + + length = (source [ATM_CELL_SIZE - 6] << 8) + source [ATM_CELL_SIZE - 5]; + + /* guard against overflow */ + if (length > ATM_MAX_AAL5_PDU) { + dbg ("udsl_extract_cells: bogus length %u (vcc: 0x%p)!", length, vcc); + atomic_inc (&vcc->stats->rx_err); + goto out; + } + + pdu_length = UDSL_NUM_CELLS (length) * ATM_CELL_PAYLOAD; + + if (sarb->len < pdu_length) { + dbg ("udsl_extract_cells: bogus pdu_length %u (sarb->len: %u, vcc: 0x%p)!", pdu_length, sarb->len, vcc); + atomic_inc (&vcc->stats->rx_err); + goto out; + } + + if (crc32_be (~0, sarb->tail - pdu_length, pdu_length) != 0xc704dd7b) { + dbg ("udsl_extract_cells: packet failed crc check (vcc: 0x%p)!", vcc); + atomic_inc (&vcc->stats->rx_err); + goto out; + } + + vdbg ("udsl_extract_cells: got packet (length: %u, pdu_length: %u, vcc: 0x%p)", length, pdu_length, vcc); + + if (!(skb = dev_alloc_skb (length))) { + dbg ("udsl_extract_cells: no memory for skb (length: %u)!", length); + atomic_inc (&vcc->stats->rx_drop); + goto out; + } + + vdbg ("udsl_extract_cells: allocated new sk_buff (skb: 0x%p, skb->truesize: %u)", skb, skb->truesize); + + if (!atm_charge (vcc, skb->truesize)) { + dbg ("udsl_extract_cells: failed atm_charge (skb->truesize: %u)!", skb->truesize); + dev_kfree_skb (skb); + goto out; /* atm_charge increments rx_drop */ + } + + memcpy (skb->data, sarb->tail - pdu_length, length); + __skb_put (skb, length); + + vdbg ("udsl_extract_cells: sending skb 0x%p, skb->len %u, skb->truesize %u", skb, skb->len, skb->truesize); + + PACKETDEBUG (skb->data, skb->len); + + vcc->push (vcc, skb); + + atomic_inc (&vcc->stats->rx); +out: + skb_trim (sarb, 0); + } + } +} + + +/************* +** encode ** +*************/ + +static const unsigned char zeros [ATM_CELL_PAYLOAD]; + +static void udsl_groom_skb (struct atm_vcc *vcc, struct sk_buff *skb) +{ + struct udsl_control *ctrl = UDSL_SKB (skb); + unsigned int zero_padding; + u32 crc; + + ctrl->atm_data.vcc = vcc; + ctrl->cell_header [0] = vcc->vpi >> 4; + ctrl->cell_header [1] = (vcc->vpi << 4) | (vcc->vci >> 12); + ctrl->cell_header [2] = vcc->vci >> 4; + ctrl->cell_header [3] = vcc->vci << 4; + ctrl->cell_header [4] = 0xec; + + ctrl->num_cells = UDSL_NUM_CELLS (skb->len); + ctrl->num_entire = skb->len / ATM_CELL_PAYLOAD; + + zero_padding = ctrl->num_cells * ATM_CELL_PAYLOAD - skb->len - ATM_AAL5_TRAILER; + + if (ctrl->num_entire + 1 < ctrl->num_cells) + ctrl->pdu_padding = zero_padding - (ATM_CELL_PAYLOAD - ATM_AAL5_TRAILER); + else + ctrl->pdu_padding = zero_padding; + + ctrl->aal5_trailer [0] = 0; /* UU = 0 */ + ctrl->aal5_trailer [1] = 0; /* CPI = 0 */ + ctrl->aal5_trailer [2] = skb->len >> 8; + ctrl->aal5_trailer [3] = skb->len; + + crc = crc32_be (~0, skb->data, skb->len); + crc = crc32_be (crc, zeros, zero_padding); + crc = crc32_be (crc, ctrl->aal5_trailer, 4); + crc = ~crc; + + ctrl->aal5_trailer [4] = crc >> 24; + ctrl->aal5_trailer [5] = crc >> 16; + ctrl->aal5_trailer [6] = crc >> 8; + ctrl->aal5_trailer [7] = crc; +} + +static unsigned int udsl_write_cells(struct udsl_instance_data *instance, unsigned int howmany, + struct sk_buff *skb, unsigned char **target_p) +{ + struct udsl_control *ctrl = UDSL_SKB (skb); + unsigned char *target = *target_p; + unsigned int nc, ne, i; + + vdbg ("udsl_write_cells: howmany=%u, skb->len=%d, num_cells=%u, num_entire=%u, pdu_padding=%u", howmany, skb->len, ctrl->num_cells, ctrl->num_entire, ctrl->pdu_padding); + + nc = ctrl->num_cells; + ne = min (howmany, ctrl->num_entire); + + for (i = 0; i < ne; i++) { + memcpy (target, ctrl->cell_header, ATM_CELL_HEADER); + target += ATM_CELL_HEADER; + memcpy (target, skb->data, ATM_CELL_PAYLOAD); + target += ATM_CELL_PAYLOAD; + if (instance->snd_padding) { + memset (target, 0, instance->snd_padding); + target += instance->snd_padding; + } + __skb_pull (skb, ATM_CELL_PAYLOAD); + } + + ctrl->num_entire -= ne; + + if (!(ctrl->num_cells -= ne) || !(howmany -= ne)) + goto out; + + if (instance->snd_padding) { + memset (target, 0, instance->snd_padding); + target += instance->snd_padding; + } + memcpy (target, ctrl->cell_header, ATM_CELL_HEADER); + target += ATM_CELL_HEADER; + memcpy (target, skb->data, skb->len); + target += skb->len; + __skb_pull (skb, skb->len); + memset (target, 0, ctrl->pdu_padding); + target += ctrl->pdu_padding; + + if (--ctrl->num_cells) { + if (!--howmany) { + ctrl->pdu_padding = ATM_CELL_PAYLOAD - ATM_AAL5_TRAILER; + goto out; + } + + memcpy (target, ctrl->cell_header, ATM_CELL_HEADER); + target += ATM_CELL_HEADER; + memset (target, 0, ATM_CELL_PAYLOAD - ATM_AAL5_TRAILER); + target += ATM_CELL_PAYLOAD - ATM_AAL5_TRAILER; + + DEBUG_ON (--ctrl->num_cells); + } + + memcpy (target, ctrl->aal5_trailer, ATM_AAL5_TRAILER); + target += ATM_AAL5_TRAILER; + /* set pti bit in last cell */ + *(target + 3 - ATM_CELL_SIZE) |= 0x2; + if (instance->snd_padding) { + memset (target, 0, instance->snd_padding); + target += instance->snd_padding; + } +out: + *target_p = target; + return nc - ctrl->num_cells; +} + + +/************** +** receive ** +**************/ + +static void udsl_complete_receive (struct urb *urb, struct pt_regs *regs) +{ + struct udsl_receive_buffer *buf; + struct udsl_instance_data *instance; + struct udsl_receiver *rcv; + unsigned long flags; + + if (!urb || !(rcv = urb->context)) { + dbg ("udsl_complete_receive: bad urb!"); + return; + } + + instance = rcv->instance; + buf = rcv->buffer; + + buf->filled_cells = urb->actual_length / (ATM_CELL_SIZE + instance->rcv_padding); + + vdbg ("udsl_complete_receive: urb 0x%p, status %d, actual_length %d, filled_cells %u, rcv 0x%p, buf 0x%p", urb, urb->status, urb->actual_length, buf->filled_cells, rcv, buf); + + DEBUG_ON (buf->filled_cells > rcv_buf_size); + + /* may not be in_interrupt() */ + spin_lock_irqsave (&instance->receive_lock, flags); + list_add (&rcv->list, &instance->spare_receivers); + list_add_tail (&buf->list, &instance->filled_receive_buffers); + if (likely (!urb->status)) + tasklet_schedule (&instance->receive_tasklet); + spin_unlock_irqrestore (&instance->receive_lock, flags); +} + +static void udsl_process_receive (unsigned long data) +{ + struct udsl_receive_buffer *buf; + struct udsl_instance_data *instance = (struct udsl_instance_data *) data; + struct udsl_receiver *rcv; + int err; + +made_progress: + while (!list_empty (&instance->spare_receive_buffers)) { + spin_lock_irq (&instance->receive_lock); + if (list_empty (&instance->spare_receivers)) { + spin_unlock_irq (&instance->receive_lock); + break; + } + rcv = list_entry (instance->spare_receivers.next, struct udsl_receiver, list); + list_del (&rcv->list); + spin_unlock_irq (&instance->receive_lock); + + buf = list_entry (instance->spare_receive_buffers.next, struct udsl_receive_buffer, list); + list_del (&buf->list); + + rcv->buffer = buf; + + usb_fill_bulk_urb (rcv->urb, + instance->usb_dev, + usb_rcvbulkpipe (instance->usb_dev, instance->data_endpoint), + buf->base, + rcv_buf_size * (ATM_CELL_SIZE + instance->rcv_padding), + udsl_complete_receive, + rcv); + + vdbg ("udsl_process_receive: sending urb 0x%p, rcv 0x%p, buf 0x%p", rcv->urb, rcv, buf); + + if ((err = usb_submit_urb(rcv->urb, GFP_ATOMIC)) < 0) { + dbg ("udsl_process_receive: urb submission failed (%d)!", err); + list_add (&buf->list, &instance->spare_receive_buffers); + spin_lock_irq (&instance->receive_lock); + list_add (&rcv->list, &instance->spare_receivers); + spin_unlock_irq (&instance->receive_lock); + break; + } + } + + spin_lock_irq (&instance->receive_lock); + if (list_empty (&instance->filled_receive_buffers)) { + spin_unlock_irq (&instance->receive_lock); + return; /* done - no more buffers */ + } + buf = list_entry (instance->filled_receive_buffers.next, struct udsl_receive_buffer, list); + list_del (&buf->list); + spin_unlock_irq (&instance->receive_lock); + vdbg ("udsl_process_receive: processing buf 0x%p", buf); + udsl_extract_cells (instance, buf->base, buf->filled_cells); + list_add (&buf->list, &instance->spare_receive_buffers); + goto made_progress; +} + + +/*********** +** send ** +***********/ + +static void udsl_complete_send (struct urb *urb, struct pt_regs *regs) +{ + struct udsl_instance_data *instance; + struct udsl_sender *snd; + unsigned long flags; + + if (!urb || !(snd = urb->context) || !(instance = snd->instance)) { + dbg ("udsl_complete_send: bad urb!"); + return; + } + + vdbg ("udsl_complete_send: urb 0x%p, status %d, snd 0x%p, buf 0x%p", urb, urb->status, snd, snd->buffer); + + /* may not be in_interrupt() */ + spin_lock_irqsave (&instance->send_lock, flags); + list_add (&snd->list, &instance->spare_senders); + list_add (&snd->buffer->list, &instance->spare_send_buffers); + tasklet_schedule (&instance->send_tasklet); + spin_unlock_irqrestore (&instance->send_lock, flags); +} + +static void udsl_process_send (unsigned long data) +{ + struct udsl_send_buffer *buf; + struct udsl_instance_data *instance = (struct udsl_instance_data *) data; + struct sk_buff *skb; + struct udsl_sender *snd; + int err; + unsigned int num_written; + +made_progress: + spin_lock_irq (&instance->send_lock); + while (!list_empty (&instance->spare_senders)) { + if (!list_empty (&instance->filled_send_buffers)) { + buf = list_entry (instance->filled_send_buffers.next, struct udsl_send_buffer, list); + list_del (&buf->list); + } else if ((buf = instance->current_buffer)) { + instance->current_buffer = NULL; + } else /* all buffers empty */ + break; + + snd = list_entry (instance->spare_senders.next, struct udsl_sender, list); + list_del (&snd->list); + spin_unlock_irq (&instance->send_lock); + + snd->buffer = buf; + usb_fill_bulk_urb (snd->urb, + instance->usb_dev, + usb_sndbulkpipe (instance->usb_dev, instance->data_endpoint), + buf->base, + (snd_buf_size - buf->free_cells) * (ATM_CELL_SIZE + instance->snd_padding), + udsl_complete_send, + snd); + + vdbg ("udsl_process_send: submitting urb 0x%p (%d cells), snd 0x%p, buf 0x%p", snd->urb, snd_buf_size - buf->free_cells, snd, buf); + + if ((err = usb_submit_urb(snd->urb, GFP_ATOMIC)) < 0) { + dbg ("udsl_process_send: urb submission failed (%d)!", err); + spin_lock_irq (&instance->send_lock); + list_add (&snd->list, &instance->spare_senders); + spin_unlock_irq (&instance->send_lock); + list_add (&buf->list, &instance->filled_send_buffers); + return; /* bail out */ + } + + spin_lock_irq (&instance->send_lock); + } /* while */ + spin_unlock_irq (&instance->send_lock); + + if (!instance->current_skb && !(instance->current_skb = skb_dequeue (&instance->sndqueue))) + return; /* done - no more skbs */ + + skb = instance->current_skb; + + if (!(buf = instance->current_buffer)) { + spin_lock_irq (&instance->send_lock); + if (list_empty (&instance->spare_send_buffers)) { + instance->current_buffer = NULL; + spin_unlock_irq (&instance->send_lock); + return; /* done - no more buffers */ + } + buf = list_entry (instance->spare_send_buffers.next, struct udsl_send_buffer, list); + list_del (&buf->list); + spin_unlock_irq (&instance->send_lock); + + buf->free_start = buf->base; + buf->free_cells = snd_buf_size; + + instance->current_buffer = buf; + } + + num_written = udsl_write_cells(instance, buf->free_cells, skb, &buf->free_start); + + vdbg ("udsl_process_send: wrote %u cells from skb 0x%p to buffer 0x%p", num_written, skb, buf); + + if (!(buf->free_cells -= num_written)) { + list_add_tail (&buf->list, &instance->filled_send_buffers); + instance->current_buffer = NULL; + } + + vdbg ("udsl_process_send: buffer contains %d cells, %d left", snd_buf_size - buf->free_cells, buf->free_cells); + + if (!UDSL_SKB (skb)->num_cells) { + struct atm_vcc *vcc = UDSL_SKB (skb)->atm_data.vcc; + + udsl_pop (vcc, skb); + instance->current_skb = NULL; + + atomic_inc (&vcc->stats->tx); + } + + goto made_progress; +} + +static void udsl_cancel_send (struct udsl_instance_data *instance, struct atm_vcc *vcc) +{ + struct sk_buff *skb, *n; + + dbg ("udsl_cancel_send entered"); + spin_lock_irq (&instance->sndqueue.lock); + for (skb = instance->sndqueue.next, n = skb->next; skb != (struct sk_buff *)&instance->sndqueue; skb = n, n = skb->next) + if (UDSL_SKB (skb)->atm_data.vcc == vcc) { + dbg ("udsl_cancel_send: popping skb 0x%p", skb); + __skb_unlink (skb, &instance->sndqueue); + udsl_pop (vcc, skb); + } + spin_unlock_irq (&instance->sndqueue.lock); + + tasklet_disable (&instance->send_tasklet); + if ((skb = instance->current_skb) && (UDSL_SKB (skb)->atm_data.vcc == vcc)) { + dbg ("udsl_cancel_send: popping current skb (0x%p)", skb); + instance->current_skb = NULL; + udsl_pop (vcc, skb); + } + tasklet_enable (&instance->send_tasklet); + dbg ("udsl_cancel_send done"); +} + +static int udsl_atm_send (struct atm_vcc *vcc, struct sk_buff *skb) +{ + struct udsl_instance_data *instance = vcc->dev->dev_data; + int err; + + vdbg ("udsl_atm_send called (skb 0x%p, len %u)", skb, skb->len); + + if (!instance) { + dbg ("udsl_atm_send: NULL data!"); + err = -ENODEV; + goto fail; + } + + if (vcc->qos.aal != ATM_AAL5) { + dbg ("udsl_atm_send: unsupported ATM type %d!", vcc->qos.aal); + err = -EINVAL; + goto fail; + } + + if (skb->len > ATM_MAX_AAL5_PDU) { + dbg ("udsl_atm_send: packet too long (%d vs %d)!", skb->len, ATM_MAX_AAL5_PDU); + err = -EINVAL; + goto fail; + } + + PACKETDEBUG (skb->data, skb->len); + + udsl_groom_skb (vcc, skb); + skb_queue_tail (&instance->sndqueue, skb); + tasklet_schedule (&instance->send_tasklet); + + return 0; + +fail: + udsl_pop (vcc, skb); + return err; +} + + +/******************** +** bean counting ** +********************/ + +static void udsl_destroy_instance(struct kref *kref) +{ + struct udsl_instance_data *instance = container_of (kref, struct udsl_instance_data, refcount); + + tasklet_kill (&instance->receive_tasklet); + tasklet_kill (&instance->send_tasklet); + usb_put_dev (instance->usb_dev); + kfree (instance); +} + + + +void udsl_get_instance(struct udsl_instance_data *instance) +{ + kref_get (&instance->refcount); +} + +void udsl_put_instance(struct udsl_instance_data *instance) +{ + kref_put (&instance->refcount, udsl_destroy_instance); +} + + +/********** +** ATM ** +**********/ + +static void udsl_atm_dev_close (struct atm_dev *dev) +{ + struct udsl_instance_data *instance = dev->dev_data; + + dev->dev_data = NULL; + udsl_put_instance (instance); +} + +static int udsl_atm_proc_read (struct atm_dev *atm_dev, loff_t *pos, char *page) +{ + struct udsl_instance_data *instance = atm_dev->dev_data; + int left = *pos; + + if (!instance) { + dbg ("udsl_atm_proc_read: NULL instance!"); + return -ENODEV; + } + + if (!left--) + return sprintf (page, "%s\n", instance->description); + + if (!left--) + return sprintf (page, "MAC: %02x:%02x:%02x:%02x:%02x:%02x\n", + atm_dev->esi [0], atm_dev->esi [1], atm_dev->esi [2], + atm_dev->esi [3], atm_dev->esi [4], atm_dev->esi [5]); + + if (!left--) + return sprintf (page, "AAL5: tx %d ( %d err ), rx %d ( %d err, %d drop )\n", + atomic_read (&atm_dev->stats.aal5.tx), + atomic_read (&atm_dev->stats.aal5.tx_err), + atomic_read (&atm_dev->stats.aal5.rx), + atomic_read (&atm_dev->stats.aal5.rx_err), + atomic_read (&atm_dev->stats.aal5.rx_drop)); + + if (!left--) { + switch (atm_dev->signal) { + case ATM_PHY_SIG_FOUND: + sprintf (page, "Line up"); + break; + case ATM_PHY_SIG_LOST: + sprintf (page, "Line down"); + break; + default: + sprintf (page, "Line state unknown"); + break; + } + + if (instance->usb_dev->state == USB_STATE_NOTATTACHED) + strcat (page, ", disconnected\n"); + else { + if (instance->status == UDSL_LOADED_FIRMWARE) + strcat (page, ", firmware loaded\n"); + else if (instance->status == UDSL_LOADING_FIRMWARE) + strcat (page, ", firmware loading\n"); + else + strcat (page, ", no firmware\n"); + } + + return strlen (page); + } + + return 0; +} + +static int udsl_atm_open (struct atm_vcc *vcc) +{ + struct udsl_instance_data *instance = vcc->dev->dev_data; + struct udsl_vcc_data *new; + unsigned int max_pdu; + int vci = vcc->vci; + short vpi = vcc->vpi; + int err; + + dbg ("udsl_atm_open: vpi %hd, vci %d", vpi, vci); + + if (!instance) { + dbg ("udsl_atm_open: NULL data!"); + return -ENODEV; + } + + /* only support AAL5 */ + if ((vcc->qos.aal != ATM_AAL5) || (vcc->qos.rxtp.max_sdu < 0) || (vcc->qos.rxtp.max_sdu > ATM_MAX_AAL5_PDU)) { + dbg ("udsl_atm_open: unsupported ATM type %d!", vcc->qos.aal); + return -EINVAL; + } + + if (instance->firmware_wait && + (err = instance->firmware_wait (instance)) < 0) { + dbg ("udsl_atm_open: firmware not loaded (%d)!", err); + return err; + } + + down (&instance->serialize); /* vs self, udsl_atm_close */ + + if (udsl_find_vcc (instance, vpi, vci)) { + dbg ("udsl_atm_open: %hd/%d already in use!", vpi, vci); + up (&instance->serialize); + return -EADDRINUSE; + } + + if (!(new = kmalloc (sizeof (struct udsl_vcc_data), GFP_KERNEL))) { + dbg ("udsl_atm_open: no memory for vcc_data!"); + up (&instance->serialize); + return -ENOMEM; + } + + memset (new, 0, sizeof (struct udsl_vcc_data)); + new->vcc = vcc; + new->vpi = vpi; + new->vci = vci; + + /* udsl_extract_cells requires at least one cell */ + max_pdu = max (1, UDSL_NUM_CELLS (vcc->qos.rxtp.max_sdu)) * ATM_CELL_PAYLOAD; + if (!(new->sarb = alloc_skb (max_pdu, GFP_KERNEL))) { + dbg ("udsl_atm_open: no memory for SAR buffer!"); + kfree (new); + up (&instance->serialize); + return -ENOMEM; + } + + vcc->dev_data = new; + + tasklet_disable (&instance->receive_tasklet); + list_add (&new->list, &instance->vcc_list); + tasklet_enable (&instance->receive_tasklet); + + set_bit (ATM_VF_ADDR, &vcc->flags); + set_bit (ATM_VF_PARTIAL, &vcc->flags); + set_bit (ATM_VF_READY, &vcc->flags); + + up (&instance->serialize); + + tasklet_schedule (&instance->receive_tasklet); + + dbg ("udsl_atm_open: allocated vcc data 0x%p (max_pdu: %u)", new, max_pdu); + + return 0; +} + +static void udsl_atm_close (struct atm_vcc *vcc) +{ + struct udsl_instance_data *instance = vcc->dev->dev_data; + struct udsl_vcc_data *vcc_data = vcc->dev_data; + + dbg ("udsl_atm_close called"); + + if (!instance || !vcc_data) { + dbg ("udsl_atm_close: NULL data!"); + return; + } + + dbg ("udsl_atm_close: deallocating vcc 0x%p with vpi %d vci %d", vcc_data, vcc_data->vpi, vcc_data->vci); + + udsl_cancel_send (instance, vcc); + + down (&instance->serialize); /* vs self, udsl_atm_open */ + + tasklet_disable (&instance->receive_tasklet); + list_del (&vcc_data->list); + tasklet_enable (&instance->receive_tasklet); + + kfree_skb (vcc_data->sarb); + vcc_data->sarb = NULL; + + kfree (vcc_data); + vcc->dev_data = NULL; + + vcc->vpi = ATM_VPI_UNSPEC; + vcc->vci = ATM_VCI_UNSPEC; + clear_bit (ATM_VF_READY, &vcc->flags); + clear_bit (ATM_VF_PARTIAL, &vcc->flags); + clear_bit (ATM_VF_ADDR, &vcc->flags); + + up (&instance->serialize); + + dbg ("udsl_atm_close successful"); +} + +static int udsl_atm_ioctl (struct atm_dev *dev, unsigned int cmd, void __user *arg) +{ + switch (cmd) { + case ATM_QUERYLOOP: + return put_user (ATM_LM_NONE, (int __user *)arg) ? -EFAULT : 0; + default: + return -ENOIOCTLCMD; + } +} + + +/********** +** USB ** +**********/ + +int udsl_instance_setup(struct usb_device *dev, struct udsl_instance_data *instance) +{ + char *buf; + int i, length; + + kref_init (&instance->refcount); /* one for USB */ + udsl_get_instance (instance); /* one for ATM */ + + + init_MUTEX (&instance->serialize); + + instance->usb_dev = dev; + + INIT_LIST_HEAD (&instance->vcc_list); + + instance->status = UDSL_NO_FIRMWARE; + init_waitqueue_head (&instance->firmware_waiters); + + spin_lock_init (&instance->receive_lock); + INIT_LIST_HEAD (&instance->spare_receivers); + INIT_LIST_HEAD (&instance->filled_receive_buffers); + + tasklet_init (&instance->receive_tasklet, udsl_process_receive, (unsigned long) instance); + INIT_LIST_HEAD (&instance->spare_receive_buffers); + + skb_queue_head_init (&instance->sndqueue); + + spin_lock_init (&instance->send_lock); + INIT_LIST_HEAD (&instance->spare_senders); + INIT_LIST_HEAD (&instance->spare_send_buffers); + + tasklet_init (&instance->send_tasklet, udsl_process_send, (unsigned long) instance); + INIT_LIST_HEAD (&instance->filled_send_buffers); + + /* receive init */ + for (i = 0; i < num_rcv_urbs; i++) { + struct udsl_receiver *rcv = &(instance->receivers [i]); + + if (!(rcv->urb = usb_alloc_urb (0, GFP_KERNEL))) { + dbg ("udsl_usb_probe: no memory for receive urb %d!", i); + goto fail; + } + + rcv->instance = instance; + + list_add (&rcv->list, &instance->spare_receivers); + } + + for (i = 0; i < num_rcv_bufs; i++) { + struct udsl_receive_buffer *buf = &(instance->receive_buffers [i]); + + if (!(buf->base = kmalloc (rcv_buf_size * (ATM_CELL_SIZE + instance->rcv_padding), GFP_KERNEL))) { + dbg ("udsl_usb_probe: no memory for receive buffer %d!", i); + goto fail; + } + + list_add (&buf->list, &instance->spare_receive_buffers); + } + + /* send init */ + for (i = 0; i < num_snd_urbs; i++) { + struct udsl_sender *snd = &(instance->senders [i]); + + if (!(snd->urb = usb_alloc_urb (0, GFP_KERNEL))) { + dbg ("udsl_usb_probe: no memory for send urb %d!", i); + goto fail; + } + + snd->instance = instance; + + list_add (&snd->list, &instance->spare_senders); + } + + for (i = 0; i < num_snd_bufs; i++) { + struct udsl_send_buffer *buf = &(instance->send_buffers [i]); + + if (!(buf->base = kmalloc (snd_buf_size * (ATM_CELL_SIZE + instance->snd_padding), GFP_KERNEL))) { + dbg ("udsl_usb_probe: no memory for send buffer %d!", i); + goto fail; + } + + list_add (&buf->list, &instance->spare_send_buffers); + } + + /* ATM init */ + if (!(instance->atm_dev = atm_dev_register (instance->driver_name, &udsl_atm_devops, -1, NULL))) { + dbg ("udsl_usb_probe: failed to register ATM device!"); + goto fail; + } + + instance->atm_dev->ci_range.vpi_bits = ATM_CI_MAX; + instance->atm_dev->ci_range.vci_bits = ATM_CI_MAX; + instance->atm_dev->signal = ATM_PHY_SIG_UNKNOWN; + + /* temp init ATM device, set to 128kbit */ + instance->atm_dev->link_rate = 128 * 1000 / 424; + + /* device description */ + buf = instance->description; + length = sizeof (instance->description); + + if ((i = usb_string (dev, dev->descriptor.iProduct, buf, length)) < 0) + goto finish; + + buf += i; + length -= i; + + i = scnprintf (buf, length, " ("); + buf += i; + length -= i; + + if (length <= 0 || (i = usb_make_path (dev, buf, length)) < 0) + goto finish; + + buf += i; + length -= i; + + snprintf (buf, length, ")"); + +finish: + /* ready for ATM callbacks */ + wmb (); + instance->atm_dev->dev_data = instance; + + usb_get_dev (dev); + + return 0; + +fail: + for (i = 0; i < num_snd_bufs; i++) + kfree (instance->send_buffers [i].base); + + for (i = 0; i < num_snd_urbs; i++) + usb_free_urb (instance->senders [i].urb); + + for (i = 0; i < num_rcv_bufs; i++) + kfree (instance->receive_buffers [i].base); + + for (i = 0; i < num_rcv_urbs; i++) + usb_free_urb (instance->receivers [i].urb); + + return -ENOMEM; +} + +void udsl_instance_disconnect (struct udsl_instance_data *instance) +{ + struct list_head *pos; + unsigned int count; + int result, i; + + dbg ("udsl_instance_disconnect entered"); + + if (!instance) { + dbg ("udsl_instance_disconnect: NULL instance!"); + return; + } + + /* receive finalize */ + tasklet_disable (&instance->receive_tasklet); + + for (i = 0; i < num_rcv_urbs; i++) + if ((result = usb_unlink_urb (instance->receivers [i].urb)) < 0) + dbg ("udsl_instance_disconnect: usb_unlink_urb on receive urb %d returned %d!", i, result); + + /* wait for completion handlers to finish */ + do { + count = 0; + spin_lock_irq (&instance->receive_lock); + list_for_each (pos, &instance->spare_receivers) + DEBUG_ON (++count > num_rcv_urbs); + spin_unlock_irq (&instance->receive_lock); + + dbg ("udsl_instance_disconnect: found %u spare receivers", count); + + if (count == num_rcv_urbs) + break; + + set_current_state (TASK_RUNNING); + schedule (); + } while (1); + + /* no need to take the spinlock */ + INIT_LIST_HEAD (&instance->filled_receive_buffers); + INIT_LIST_HEAD (&instance->spare_receive_buffers); + + tasklet_enable (&instance->receive_tasklet); + + for (i = 0; i < num_rcv_urbs; i++) + usb_free_urb (instance->receivers [i].urb); + + for (i = 0; i < num_rcv_bufs; i++) + kfree (instance->receive_buffers [i].base); + + /* send finalize */ + tasklet_disable (&instance->send_tasklet); + + for (i = 0; i < num_snd_urbs; i++) + if ((result = usb_unlink_urb (instance->senders [i].urb)) < 0) + dbg ("udsl_instance_disconnect: usb_unlink_urb on send urb %d returned %d!", i, result); + + /* wait for completion handlers to finish */ + do { + count = 0; + spin_lock_irq (&instance->send_lock); + list_for_each (pos, &instance->spare_senders) + DEBUG_ON (++count > num_snd_urbs); + spin_unlock_irq (&instance->send_lock); + + dbg ("udsl_instance_disconnect: found %u spare senders", count); + + if (count == num_snd_urbs) + break; + + set_current_state (TASK_RUNNING); + schedule (); + } while (1); + + /* no need to take the spinlock */ + INIT_LIST_HEAD (&instance->spare_senders); + INIT_LIST_HEAD (&instance->spare_send_buffers); + instance->current_buffer = NULL; + + tasklet_enable (&instance->send_tasklet); + + for (i = 0; i < num_snd_urbs; i++) + usb_free_urb (instance->senders [i].urb); + + for (i = 0; i < num_snd_bufs; i++) + kfree (instance->send_buffers [i].base); + + /* ATM finalize */ + shutdown_atm_dev (instance->atm_dev); +} + +EXPORT_SYMBOL_GPL(udsl_get_instance); +EXPORT_SYMBOL_GPL(udsl_put_instance); +EXPORT_SYMBOL_GPL(udsl_instance_setup); +EXPORT_SYMBOL_GPL(udsl_instance_disconnect); + +/*********** +** init ** +***********/ + +static int __init udsl_usb_init (void) +{ + dbg ("udsl_usb_init: driver version " DRIVER_VERSION); + + if (sizeof (struct udsl_control) > sizeof (((struct sk_buff *)0)->cb)) { + printk (KERN_ERR __FILE__ ": unusable with this kernel!\n"); + return -EIO; + } + + if ((num_rcv_urbs > UDSL_MAX_RCV_URBS) || (num_snd_urbs > UDSL_MAX_SND_URBS) || + (num_rcv_bufs > UDSL_MAX_RCV_BUFS) || (num_snd_bufs > UDSL_MAX_SND_BUFS) || + (rcv_buf_size > UDSL_MAX_RCV_BUF_SIZE) || (snd_buf_size > UDSL_MAX_SND_BUF_SIZE)) + return -EINVAL; + + return 0; +} + +module_init (udsl_usb_init); + +MODULE_AUTHOR (DRIVER_AUTHOR); +MODULE_DESCRIPTION (DRIVER_DESC); +MODULE_LICENSE ("GPL"); +MODULE_VERSION (DRIVER_VERSION); + + +/************ +** debug ** +************/ + +#ifdef VERBOSE_DEBUG +static int udsl_print_packet (const unsigned char *data, int len) +{ + unsigned char buffer [256]; + int i = 0, j = 0; + + for (i = 0; i < len;) { + buffer [0] = '\0'; + sprintf (buffer, "%.3d :", i); + for (j = 0; (j < 16) && (i < len); j++, i++) { + sprintf (buffer, "%s %2.2x", buffer, data [i]); + } + dbg ("%s", buffer); + } + return i; +} +#endif diff --git a/drivers/usb/atm/usb_atm.h b/drivers/usb/atm/usb_atm.h new file mode 100644 index 000000000000..1eef3571d312 --- /dev/null +++ b/drivers/usb/atm/usb_atm.h @@ -0,0 +1,159 @@ +/****************************************************************************** + * usb_atm.h - Generic USB xDSL driver core + * + * Copyright (C) 2001, Alcatel + * Copyright (C) 2003, Duncan Sands, SolNegro, Josep Comas + * Copyright (C) 2004, David Woodhouse + * + * 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 +#include +#include + +#define UDSL_MAX_RCV_URBS 4 +#define UDSL_MAX_SND_URBS 4 +#define UDSL_MAX_RCV_BUFS 8 +#define UDSL_MAX_SND_BUFS 8 +#define UDSL_MAX_RCV_BUF_SIZE 1024 /* ATM cells */ +#define UDSL_MAX_SND_BUF_SIZE 1024 /* ATM cells */ +#define UDSL_DEFAULT_RCV_URBS 2 +#define UDSL_DEFAULT_SND_URBS 2 +#define UDSL_DEFAULT_RCV_BUFS 4 +#define UDSL_DEFAULT_SND_BUFS 4 +#define UDSL_DEFAULT_RCV_BUF_SIZE 64 /* ATM cells */ +#define UDSL_DEFAULT_SND_BUF_SIZE 64 /* ATM cells */ + +#define ATM_CELL_HEADER (ATM_CELL_SIZE - ATM_CELL_PAYLOAD) +#define UDSL_NUM_CELLS(x) (((x) + ATM_AAL5_TRAILER + ATM_CELL_PAYLOAD - 1) / ATM_CELL_PAYLOAD) + +/* receive */ + +struct udsl_receive_buffer { + struct list_head list; + unsigned char *base; + unsigned int filled_cells; +}; + +struct udsl_receiver { + struct list_head list; + struct udsl_receive_buffer *buffer; + struct urb *urb; + struct udsl_instance_data *instance; +}; + +struct udsl_vcc_data { + /* vpi/vci lookup */ + struct list_head list; + short vpi; + int vci; + struct atm_vcc *vcc; + + /* raw cell reassembly */ + struct sk_buff *sarb; +}; + +/* send */ + +struct udsl_send_buffer { + struct list_head list; + unsigned char *base; + unsigned char *free_start; + unsigned int free_cells; +}; + +struct udsl_sender { + struct list_head list; + struct udsl_send_buffer *buffer; + struct urb *urb; + struct udsl_instance_data *instance; +}; + +struct udsl_control { + struct atm_skb_data atm_data; + unsigned int num_cells; + unsigned int num_entire; + unsigned int pdu_padding; + unsigned char cell_header [ATM_CELL_HEADER]; + unsigned char aal5_trailer [ATM_AAL5_TRAILER]; +}; + +#define UDSL_SKB(x) ((struct udsl_control *)(x)->cb) + +/* main driver data */ + +enum udsl_status { + UDSL_NO_FIRMWARE, + UDSL_LOADING_FIRMWARE, + UDSL_LOADED_FIRMWARE +}; + +struct udsl_instance_data { + struct kref refcount; + struct semaphore serialize; + + /* USB device part */ + struct usb_device *usb_dev; + char description [64]; + int data_endpoint; + int snd_padding; + int rcv_padding; + const char *driver_name; + + /* ATM device part */ + struct atm_dev *atm_dev; + struct list_head vcc_list; + + /* firmware */ + int (*firmware_wait) (struct udsl_instance_data *); + enum udsl_status status; + wait_queue_head_t firmware_waiters; + + /* receive */ + struct udsl_receiver receivers [UDSL_MAX_RCV_URBS]; + struct udsl_receive_buffer receive_buffers [UDSL_MAX_RCV_BUFS]; + + spinlock_t receive_lock; + struct list_head spare_receivers; + struct list_head filled_receive_buffers; + + struct tasklet_struct receive_tasklet; + struct list_head spare_receive_buffers; + + /* send */ + struct udsl_sender senders [UDSL_MAX_SND_URBS]; + struct udsl_send_buffer send_buffers [UDSL_MAX_SND_BUFS]; + + struct sk_buff_head sndqueue; + + spinlock_t send_lock; + struct list_head spare_senders; + struct list_head spare_send_buffers; + + struct tasklet_struct send_tasklet; + struct sk_buff *current_skb; /* being emptied */ + struct udsl_send_buffer *current_buffer; /* being filled */ + struct list_head filled_send_buffers; +}; + +extern int udsl_instance_setup(struct usb_device *dev, struct udsl_instance_data *instance); +extern void udsl_instance_disconnect (struct udsl_instance_data *instance); +extern void udsl_get_instance(struct udsl_instance_data *instance); +extern void udsl_put_instance(struct udsl_instance_data *instance); diff --git a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig index 8c958af4a1d3..d6401c0a80d5 100644 --- a/drivers/usb/misc/Kconfig +++ b/drivers/usb/misc/Kconfig @@ -121,18 +121,6 @@ config USB_CYTHERM To compile this driver as a module, choose M here: the module will be called cytherm. -config USB_SPEEDTOUCH - tristate "Alcatel Speedtouch USB support" - depends on USB && ATM - select CRC32 - help - Say Y here if you have an Alcatel SpeedTouch USB or SpeedTouch 330 - modem. In order to use your modem you will need to install some user - space tools, see for details. - - To compile this driver as a module, choose M here: the - module will be called speedtch. - config USB_PHIDGETSERVO tristate "USB PhidgetServo support" depends on USB diff --git a/drivers/usb/misc/Makefile b/drivers/usb/misc/Makefile index d641a470e8d7..074a1d66250b 100644 --- a/drivers/usb/misc/Makefile +++ b/drivers/usb/misc/Makefile @@ -11,7 +11,6 @@ obj-$(CONFIG_USB_LCD) += usblcd.o obj-$(CONFIG_USB_LED) += usbled.o obj-$(CONFIG_USB_LEGOTOWER) += legousbtower.o obj-$(CONFIG_USB_RIO500) += rio500.o -obj-$(CONFIG_USB_SPEEDTOUCH) += speedtch.o obj-$(CONFIG_USB_TEST) += usbtest.o obj-$(CONFIG_USB_TIGL) += tiglusb.o obj-$(CONFIG_USB_USS720) += uss720.o diff --git a/drivers/usb/misc/speedtch.c b/drivers/usb/misc/speedtch.c deleted file mode 100644 index 667e2d1b227c..000000000000 --- a/drivers/usb/misc/speedtch.c +++ /dev/null @@ -1,1373 +0,0 @@ -/****************************************************************************** - * speedtouch.c - Alcatel SpeedTouch USB xDSL modem driver - * - * Copyright (C) 2001, Alcatel - * Copyright (C) 2003, Duncan Sands - * - * 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. - * - ******************************************************************************/ - -/* - * Written by Johan Verrept, maintained by Duncan Sands (duncan.sands@free.fr) - * - * 1.7+: - See the check-in logs - * - * 1.6: - No longer opens a connection if the firmware is not loaded - * - Added support for the speedtouch 330 - * - Removed the limit on the number of devices - * - Module now autoloads on device plugin - * - Merged relevant parts of sarlib - * - Replaced the kernel thread with a tasklet - * - New packet transmission code - * - Changed proc file contents - * - Fixed all known SMP races - * - Many fixes and cleanups - * - Various fixes by Oliver Neukum (oliver@neukum.name) - * - * 1.5A: - Version for inclusion in 2.5 series kernel - * - Modifications by Richard Purdie (rpurdie@rpsys.net) - * - made compatible with kernel 2.5.6 onwards by changing - * udsl_usb_send_data_context->urb to a pointer and adding code - * to alloc and free it - * - remove_wait_queue() added to udsl_atm_processqueue_thread() - * - * 1.5: - fixed memory leak when atmsar_decode_aal5 returned NULL. - * (reported by stephen.robinson@zen.co.uk) - * - * 1.4: - changed the spin_lock() under interrupt to spin_lock_irqsave() - * - unlink all active send urbs of a vcc that is being closed. - * - * 1.3.1: - added the version number - * - * 1.3: - Added multiple send urb support - * - fixed memory leak and vcc->tx_inuse starvation bug - * when not enough memory left in vcc. - * - * 1.2: - Fixed race condition in udsl_usb_send_data() - * 1.1: - Turned off packet debugging - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* -#define DEBUG -#define VERBOSE_DEBUG -*/ - -#if !defined (DEBUG) && defined (CONFIG_USB_DEBUG) -# define DEBUG -#endif - -#include - -#ifdef DEBUG -#define DEBUG_ON(x) BUG_ON(x) -#else -#define DEBUG_ON(x) do { if (x); } while (0) -#endif - -#ifdef VERBOSE_DEBUG -static int udsl_print_packet (const unsigned char *data, int len); -#define PACKETDEBUG(arg...) udsl_print_packet (arg) -#define vdbg(arg...) dbg (arg) -#else -#define PACKETDEBUG(arg...) -#define vdbg(arg...) -#endif - -#define DRIVER_AUTHOR "Johan Verrept, Duncan Sands " -#define DRIVER_VERSION "1.8" -#define DRIVER_DESC "Alcatel SpeedTouch USB driver version " DRIVER_VERSION - -static const char udsl_driver_name [] = "speedtch"; - -#define SPEEDTOUCH_VENDORID 0x06b9 -#define SPEEDTOUCH_PRODUCTID 0x4061 - -#define UDSL_MAX_RCV_URBS 4 -#define UDSL_MAX_SND_URBS 4 -#define UDSL_MAX_RCV_BUFS 8 -#define UDSL_MAX_SND_BUFS 8 -#define UDSL_MAX_RCV_BUF_SIZE 1024 /* ATM cells */ -#define UDSL_MAX_SND_BUF_SIZE 1024 /* ATM cells */ -#define UDSL_DEFAULT_RCV_URBS 2 -#define UDSL_DEFAULT_SND_URBS 2 -#define UDSL_DEFAULT_RCV_BUFS 4 -#define UDSL_DEFAULT_SND_BUFS 4 -#define UDSL_DEFAULT_RCV_BUF_SIZE 64 /* ATM cells */ -#define UDSL_DEFAULT_SND_BUF_SIZE 64 /* ATM cells */ - -static unsigned int num_rcv_urbs = UDSL_DEFAULT_RCV_URBS; -static unsigned int num_snd_urbs = UDSL_DEFAULT_SND_URBS; -static unsigned int num_rcv_bufs = UDSL_DEFAULT_RCV_BUFS; -static unsigned int num_snd_bufs = UDSL_DEFAULT_SND_BUFS; -static unsigned int rcv_buf_size = UDSL_DEFAULT_RCV_BUF_SIZE; -static unsigned int snd_buf_size = UDSL_DEFAULT_SND_BUF_SIZE; - -module_param (num_rcv_urbs, uint, 0444); -MODULE_PARM_DESC (num_rcv_urbs, "Number of urbs used for reception (range: 0-" __MODULE_STRING (UDSL_MAX_RCV_URBS) ", default: " __MODULE_STRING (UDSL_DEFAULT_RCV_URBS) ")"); - -module_param (num_snd_urbs, uint, 0444); -MODULE_PARM_DESC (num_snd_urbs, "Number of urbs used for transmission (range: 0-" __MODULE_STRING (UDSL_MAX_SND_URBS) ", default: " __MODULE_STRING (UDSL_DEFAULT_SND_URBS) ")"); - -module_param (num_rcv_bufs, uint, 0444); -MODULE_PARM_DESC (num_rcv_bufs, "Number of buffers used for reception (range: 0-" __MODULE_STRING (UDSL_MAX_RCV_BUFS) ", default: " __MODULE_STRING (UDSL_DEFAULT_RCV_BUFS) ")"); - -module_param (num_snd_bufs, uint, 0444); -MODULE_PARM_DESC (num_snd_bufs, "Number of buffers used for transmission (range: 0-" __MODULE_STRING (UDSL_MAX_SND_BUFS) ", default: " __MODULE_STRING (UDSL_DEFAULT_SND_BUFS) ")"); - -module_param (rcv_buf_size, uint, 0444); -MODULE_PARM_DESC (rcv_buf_size, "Size of the buffers used for reception (range: 0-" __MODULE_STRING (UDSL_MAX_RCV_BUF_SIZE) ", default: " __MODULE_STRING (UDSL_DEFAULT_RCV_BUF_SIZE) ")"); - -module_param (snd_buf_size, uint, 0444); -MODULE_PARM_DESC (snd_buf_size, "Size of the buffers used for transmission (range: 0-" __MODULE_STRING (UDSL_MAX_SND_BUF_SIZE) ", default: " __MODULE_STRING (UDSL_DEFAULT_SND_BUF_SIZE) ")"); - -#define UDSL_IOCTL_LINE_UP 1 -#define UDSL_IOCTL_LINE_DOWN 2 - -#define UDSL_ENDPOINT_DATA_OUT 0x07 -#define UDSL_ENDPOINT_DATA_IN 0x87 - -#define ATM_CELL_HEADER (ATM_CELL_SIZE - ATM_CELL_PAYLOAD) -#define UDSL_NUM_CELLS(x) (((x) + ATM_AAL5_TRAILER + ATM_CELL_PAYLOAD - 1) / ATM_CELL_PAYLOAD) - -#define hex2int(c) ( (c >= '0') && (c <= '9') ? (c - '0') : ((c & 0xf) + 9) ) - -static struct usb_device_id udsl_usb_ids [] = { - { USB_DEVICE (SPEEDTOUCH_VENDORID, SPEEDTOUCH_PRODUCTID) }, - { } -}; - -MODULE_DEVICE_TABLE (usb, udsl_usb_ids); - -/* receive */ - -struct udsl_receive_buffer { - struct list_head list; - unsigned char *base; - unsigned int filled_cells; -}; - -struct udsl_receiver { - struct list_head list; - struct udsl_receive_buffer *buffer; - struct urb *urb; - struct udsl_instance_data *instance; -}; - -struct udsl_vcc_data { - /* vpi/vci lookup */ - struct list_head list; - short vpi; - int vci; - struct atm_vcc *vcc; - - /* raw cell reassembly */ - struct sk_buff *sarb; -}; - -/* send */ - -struct udsl_send_buffer { - struct list_head list; - unsigned char *base; - unsigned char *free_start; - unsigned int free_cells; -}; - -struct udsl_sender { - struct list_head list; - struct udsl_send_buffer *buffer; - struct urb *urb; - struct udsl_instance_data *instance; -}; - -struct udsl_control { - struct atm_skb_data atm_data; - unsigned int num_cells; - unsigned int num_entire; - unsigned int pdu_padding; - unsigned char cell_header [ATM_CELL_HEADER]; - unsigned char aal5_trailer [ATM_AAL5_TRAILER]; -}; - -#define UDSL_SKB(x) ((struct udsl_control *)(x)->cb) - -/* main driver data */ - -struct udsl_instance_data { - struct semaphore serialize; - - /* USB device part */ - struct usb_device *usb_dev; - char description [64]; - int firmware_loaded; - - /* ATM device part */ - struct atm_dev *atm_dev; - struct list_head vcc_list; - - /* receive */ - struct udsl_receiver receivers [UDSL_MAX_RCV_URBS]; - struct udsl_receive_buffer receive_buffers [UDSL_MAX_RCV_BUFS]; - - spinlock_t receive_lock; - struct list_head spare_receivers; - struct list_head filled_receive_buffers; - - struct tasklet_struct receive_tasklet; - struct list_head spare_receive_buffers; - - /* send */ - struct udsl_sender senders [UDSL_MAX_SND_URBS]; - struct udsl_send_buffer send_buffers [UDSL_MAX_SND_BUFS]; - - struct sk_buff_head sndqueue; - - spinlock_t send_lock; - struct list_head spare_senders; - struct list_head spare_send_buffers; - - struct tasklet_struct send_tasklet; - struct sk_buff *current_skb; /* being emptied */ - struct udsl_send_buffer *current_buffer; /* being filled */ - struct list_head filled_send_buffers; -}; - -/* ATM */ - -static void udsl_atm_dev_close (struct atm_dev *dev); -static int udsl_atm_open (struct atm_vcc *vcc); -static void udsl_atm_close (struct atm_vcc *vcc); -static int udsl_atm_ioctl (struct atm_dev *dev, unsigned int cmd, void __user *arg); -static int udsl_atm_send (struct atm_vcc *vcc, struct sk_buff *skb); -static int udsl_atm_proc_read (struct atm_dev *atm_dev, loff_t *pos, char *page); - -static struct atmdev_ops udsl_atm_devops = { - .dev_close = udsl_atm_dev_close, - .open = udsl_atm_open, - .close = udsl_atm_close, - .ioctl = udsl_atm_ioctl, - .send = udsl_atm_send, - .proc_read = udsl_atm_proc_read, - .owner = THIS_MODULE, -}; - -/* USB */ - -static int udsl_usb_probe (struct usb_interface *intf, const struct usb_device_id *id); -static void udsl_usb_disconnect (struct usb_interface *intf); -static int udsl_usb_ioctl (struct usb_interface *intf, unsigned int code, void *user_data); - -static struct usb_driver udsl_usb_driver = { - .owner = THIS_MODULE, - .name = udsl_driver_name, - .probe = udsl_usb_probe, - .disconnect = udsl_usb_disconnect, - .ioctl = udsl_usb_ioctl, - .id_table = udsl_usb_ids, -}; - - -/*********** -** misc ** -***********/ - -static inline void udsl_pop (struct atm_vcc *vcc, struct sk_buff *skb) -{ - if (vcc->pop) - vcc->pop (vcc, skb); - else - dev_kfree_skb (skb); -} - - -/************* -** decode ** -*************/ - -static inline struct udsl_vcc_data *udsl_find_vcc (struct udsl_instance_data *instance, short vpi, int vci) -{ - struct udsl_vcc_data *vcc; - - list_for_each_entry (vcc, &instance->vcc_list, list) - if ((vcc->vci == vci) && (vcc->vpi == vpi)) - return vcc; - return NULL; -} - -static void udsl_extract_cells (struct udsl_instance_data *instance, unsigned char *source, unsigned int howmany) -{ - struct udsl_vcc_data *cached_vcc = NULL; - struct atm_vcc *vcc; - struct sk_buff *sarb; - struct udsl_vcc_data *vcc_data; - int cached_vci = 0; - unsigned int i; - int pti; - int vci; - short cached_vpi = 0; - short vpi; - - for (i = 0; i < howmany; i++, source += ATM_CELL_SIZE) { - vpi = ((source [0] & 0x0f) << 4) | (source [1] >> 4); - vci = ((source [1] & 0x0f) << 12) | (source [2] << 4) | (source [3] >> 4); - pti = (source [3] & 0x2) != 0; - - vdbg ("udsl_extract_cells: vpi %hd, vci %d, pti %d", vpi, vci, pti); - - if (cached_vcc && (vci == cached_vci) && (vpi == cached_vpi)) - vcc_data = cached_vcc; - else if ((vcc_data = udsl_find_vcc (instance, vpi, vci))) { - cached_vcc = vcc_data; - cached_vpi = vpi; - cached_vci = vci; - } else { - dbg ("udsl_extract_cells: unknown vpi/vci (%hd/%d)!", vpi, vci); - continue; - } - - vcc = vcc_data->vcc; - sarb = vcc_data->sarb; - - if (sarb->tail + ATM_CELL_PAYLOAD > sarb->end) { - dbg ("udsl_extract_cells: buffer overrun (sarb->len %u, vcc: 0x%p)!", sarb->len, vcc); - /* discard cells already received */ - skb_trim (sarb, 0); - } - - memcpy (sarb->tail, source + ATM_CELL_HEADER, ATM_CELL_PAYLOAD); - __skb_put (sarb, ATM_CELL_PAYLOAD); - - if (pti) { - struct sk_buff *skb; - unsigned int length; - unsigned int pdu_length; - - length = (source [ATM_CELL_SIZE - 6] << 8) + source [ATM_CELL_SIZE - 5]; - - /* guard against overflow */ - if (length > ATM_MAX_AAL5_PDU) { - dbg ("udsl_extract_cells: bogus length %u (vcc: 0x%p)!", length, vcc); - atomic_inc (&vcc->stats->rx_err); - goto out; - } - - pdu_length = UDSL_NUM_CELLS (length) * ATM_CELL_PAYLOAD; - - if (sarb->len < pdu_length) { - dbg ("udsl_extract_cells: bogus pdu_length %u (sarb->len: %u, vcc: 0x%p)!", pdu_length, sarb->len, vcc); - atomic_inc (&vcc->stats->rx_err); - goto out; - } - - if (crc32_be (~0, sarb->tail - pdu_length, pdu_length) != 0xc704dd7b) { - dbg ("udsl_extract_cells: packet failed crc check (vcc: 0x%p)!", vcc); - atomic_inc (&vcc->stats->rx_err); - goto out; - } - - vdbg ("udsl_extract_cells: got packet (length: %u, pdu_length: %u, vcc: 0x%p)", length, pdu_length, vcc); - - if (!(skb = dev_alloc_skb (length))) { - dbg ("udsl_extract_cells: no memory for skb (length: %u)!", length); - atomic_inc (&vcc->stats->rx_drop); - goto out; - } - - vdbg ("udsl_extract_cells: allocated new sk_buff (skb: 0x%p, skb->truesize: %u)", skb, skb->truesize); - - if (!atm_charge (vcc, skb->truesize)) { - dbg ("udsl_extract_cells: failed atm_charge (skb->truesize: %u)!", skb->truesize); - dev_kfree_skb (skb); - goto out; /* atm_charge increments rx_drop */ - } - - memcpy (skb->data, sarb->tail - pdu_length, length); - __skb_put (skb, length); - - vdbg ("udsl_extract_cells: sending skb 0x%p, skb->len %u, skb->truesize %u", skb, skb->len, skb->truesize); - - PACKETDEBUG (skb->data, skb->len); - - vcc->push (vcc, skb); - - atomic_inc (&vcc->stats->rx); -out: - skb_trim (sarb, 0); - } - } -} - - -/************* -** encode ** -*************/ - -static const unsigned char zeros [ATM_CELL_PAYLOAD]; - -static void udsl_groom_skb (struct atm_vcc *vcc, struct sk_buff *skb) -{ - struct udsl_control *ctrl = UDSL_SKB (skb); - unsigned int zero_padding; - u32 crc; - - ctrl->atm_data.vcc = vcc; - ctrl->cell_header [0] = vcc->vpi >> 4; - ctrl->cell_header [1] = (vcc->vpi << 4) | (vcc->vci >> 12); - ctrl->cell_header [2] = vcc->vci >> 4; - ctrl->cell_header [3] = vcc->vci << 4; - ctrl->cell_header [4] = 0xec; - - ctrl->num_cells = UDSL_NUM_CELLS (skb->len); - ctrl->num_entire = skb->len / ATM_CELL_PAYLOAD; - - zero_padding = ctrl->num_cells * ATM_CELL_PAYLOAD - skb->len - ATM_AAL5_TRAILER; - - if (ctrl->num_entire + 1 < ctrl->num_cells) - ctrl->pdu_padding = zero_padding - (ATM_CELL_PAYLOAD - ATM_AAL5_TRAILER); - else - ctrl->pdu_padding = zero_padding; - - ctrl->aal5_trailer [0] = 0; /* UU = 0 */ - ctrl->aal5_trailer [1] = 0; /* CPI = 0 */ - ctrl->aal5_trailer [2] = skb->len >> 8; - ctrl->aal5_trailer [3] = skb->len; - - crc = crc32_be (~0, skb->data, skb->len); - crc = crc32_be (crc, zeros, zero_padding); - crc = crc32_be (crc, ctrl->aal5_trailer, 4); - crc = ~crc; - - ctrl->aal5_trailer [4] = crc >> 24; - ctrl->aal5_trailer [5] = crc >> 16; - ctrl->aal5_trailer [6] = crc >> 8; - ctrl->aal5_trailer [7] = crc; -} - -static unsigned int udsl_write_cells (unsigned int howmany, struct sk_buff *skb, unsigned char **target_p) -{ - struct udsl_control *ctrl = UDSL_SKB (skb); - unsigned char *target = *target_p; - unsigned int nc, ne, i; - - vdbg ("udsl_write_cells: howmany=%u, skb->len=%d, num_cells=%u, num_entire=%u, pdu_padding=%u", howmany, skb->len, ctrl->num_cells, ctrl->num_entire, ctrl->pdu_padding); - - nc = ctrl->num_cells; - ne = min (howmany, ctrl->num_entire); - - for (i = 0; i < ne; i++) { - memcpy (target, ctrl->cell_header, ATM_CELL_HEADER); - target += ATM_CELL_HEADER; - memcpy (target, skb->data, ATM_CELL_PAYLOAD); - target += ATM_CELL_PAYLOAD; - __skb_pull (skb, ATM_CELL_PAYLOAD); - } - - ctrl->num_entire -= ne; - - if (!(ctrl->num_cells -= ne) || !(howmany -= ne)) - goto out; - - memcpy (target, ctrl->cell_header, ATM_CELL_HEADER); - target += ATM_CELL_HEADER; - memcpy (target, skb->data, skb->len); - target += skb->len; - __skb_pull (skb, skb->len); - memset (target, 0, ctrl->pdu_padding); - target += ctrl->pdu_padding; - - if (--ctrl->num_cells) { - if (!--howmany) { - ctrl->pdu_padding = ATM_CELL_PAYLOAD - ATM_AAL5_TRAILER; - goto out; - } - - memcpy (target, ctrl->cell_header, ATM_CELL_HEADER); - target += ATM_CELL_HEADER; - memset (target, 0, ATM_CELL_PAYLOAD - ATM_AAL5_TRAILER); - target += ATM_CELL_PAYLOAD - ATM_AAL5_TRAILER; - - DEBUG_ON (--ctrl->num_cells); - } - - memcpy (target, ctrl->aal5_trailer, ATM_AAL5_TRAILER); - target += ATM_AAL5_TRAILER; - /* set pti bit in last cell */ - *(target + 3 - ATM_CELL_SIZE) |= 0x2; - -out: - *target_p = target; - return nc - ctrl->num_cells; -} - - -/************** -** receive ** -**************/ - -static void udsl_complete_receive (struct urb *urb, struct pt_regs *regs) -{ - struct udsl_receive_buffer *buf; - struct udsl_instance_data *instance; - struct udsl_receiver *rcv; - unsigned long flags; - - if (!urb || !(rcv = urb->context)) { - dbg ("udsl_complete_receive: bad urb!"); - return; - } - - instance = rcv->instance; - buf = rcv->buffer; - - buf->filled_cells = urb->actual_length / ATM_CELL_SIZE; - - vdbg ("udsl_complete_receive: urb 0x%p, status %d, actual_length %d, filled_cells %u, rcv 0x%p, buf 0x%p", urb, urb->status, urb->actual_length, buf->filled_cells, rcv, buf); - - DEBUG_ON (buf->filled_cells > rcv_buf_size); - - /* may not be in_interrupt() */ - spin_lock_irqsave (&instance->receive_lock, flags); - list_add (&rcv->list, &instance->spare_receivers); - list_add_tail (&buf->list, &instance->filled_receive_buffers); - if (likely (!urb->status)) - tasklet_schedule (&instance->receive_tasklet); - spin_unlock_irqrestore (&instance->receive_lock, flags); -} - -static void udsl_process_receive (unsigned long data) -{ - struct udsl_receive_buffer *buf; - struct udsl_instance_data *instance = (struct udsl_instance_data *) data; - struct udsl_receiver *rcv; - int err; - -made_progress: - while (!list_empty (&instance->spare_receive_buffers)) { - spin_lock_irq (&instance->receive_lock); - if (list_empty (&instance->spare_receivers)) { - spin_unlock_irq (&instance->receive_lock); - break; - } - rcv = list_entry (instance->spare_receivers.next, struct udsl_receiver, list); - list_del (&rcv->list); - spin_unlock_irq (&instance->receive_lock); - - buf = list_entry (instance->spare_receive_buffers.next, struct udsl_receive_buffer, list); - list_del (&buf->list); - - rcv->buffer = buf; - - usb_fill_bulk_urb (rcv->urb, - instance->usb_dev, - usb_rcvbulkpipe (instance->usb_dev, UDSL_ENDPOINT_DATA_IN), - buf->base, - rcv_buf_size * ATM_CELL_SIZE, - udsl_complete_receive, - rcv); - - vdbg ("udsl_process_receive: sending urb 0x%p, rcv 0x%p, buf 0x%p", rcv->urb, rcv, buf); - - if ((err = usb_submit_urb(rcv->urb, GFP_ATOMIC)) < 0) { - dbg ("udsl_process_receive: urb submission failed (%d)!", err); - list_add (&buf->list, &instance->spare_receive_buffers); - spin_lock_irq (&instance->receive_lock); - list_add (&rcv->list, &instance->spare_receivers); - spin_unlock_irq (&instance->receive_lock); - break; - } - } - - spin_lock_irq (&instance->receive_lock); - if (list_empty (&instance->filled_receive_buffers)) { - spin_unlock_irq (&instance->receive_lock); - return; /* done - no more buffers */ - } - buf = list_entry (instance->filled_receive_buffers.next, struct udsl_receive_buffer, list); - list_del (&buf->list); - spin_unlock_irq (&instance->receive_lock); - vdbg ("udsl_process_receive: processing buf 0x%p", buf); - udsl_extract_cells (instance, buf->base, buf->filled_cells); - list_add (&buf->list, &instance->spare_receive_buffers); - goto made_progress; -} - - -/*********** -** send ** -***********/ - -static void udsl_complete_send (struct urb *urb, struct pt_regs *regs) -{ - struct udsl_instance_data *instance; - struct udsl_sender *snd; - unsigned long flags; - - if (!urb || !(snd = urb->context) || !(instance = snd->instance)) { - dbg ("udsl_complete_send: bad urb!"); - return; - } - - vdbg ("udsl_complete_send: urb 0x%p, status %d, snd 0x%p, buf 0x%p", urb, urb->status, snd, snd->buffer); - - /* may not be in_interrupt() */ - spin_lock_irqsave (&instance->send_lock, flags); - list_add (&snd->list, &instance->spare_senders); - list_add (&snd->buffer->list, &instance->spare_send_buffers); - tasklet_schedule (&instance->send_tasklet); - spin_unlock_irqrestore (&instance->send_lock, flags); -} - -static void udsl_process_send (unsigned long data) -{ - struct udsl_send_buffer *buf; - struct udsl_instance_data *instance = (struct udsl_instance_data *) data; - struct sk_buff *skb; - struct udsl_sender *snd; - int err; - unsigned int num_written; - -made_progress: - spin_lock_irq (&instance->send_lock); - while (!list_empty (&instance->spare_senders)) { - if (!list_empty (&instance->filled_send_buffers)) { - buf = list_entry (instance->filled_send_buffers.next, struct udsl_send_buffer, list); - list_del (&buf->list); - } else if ((buf = instance->current_buffer)) { - instance->current_buffer = NULL; - } else /* all buffers empty */ - break; - - snd = list_entry (instance->spare_senders.next, struct udsl_sender, list); - list_del (&snd->list); - spin_unlock_irq (&instance->send_lock); - - snd->buffer = buf; - usb_fill_bulk_urb (snd->urb, - instance->usb_dev, - usb_sndbulkpipe (instance->usb_dev, UDSL_ENDPOINT_DATA_OUT), - buf->base, - (snd_buf_size - buf->free_cells) * ATM_CELL_SIZE, - udsl_complete_send, - snd); - - vdbg ("udsl_process_send: submitting urb 0x%p (%d cells), snd 0x%p, buf 0x%p", snd->urb, snd_buf_size - buf->free_cells, snd, buf); - - if ((err = usb_submit_urb(snd->urb, GFP_ATOMIC)) < 0) { - dbg ("udsl_process_send: urb submission failed (%d)!", err); - spin_lock_irq (&instance->send_lock); - list_add (&snd->list, &instance->spare_senders); - spin_unlock_irq (&instance->send_lock); - list_add (&buf->list, &instance->filled_send_buffers); - return; /* bail out */ - } - - spin_lock_irq (&instance->send_lock); - } /* while */ - spin_unlock_irq (&instance->send_lock); - - if (!instance->current_skb && !(instance->current_skb = skb_dequeue (&instance->sndqueue))) - return; /* done - no more skbs */ - - skb = instance->current_skb; - - if (!(buf = instance->current_buffer)) { - spin_lock_irq (&instance->send_lock); - if (list_empty (&instance->spare_send_buffers)) { - instance->current_buffer = NULL; - spin_unlock_irq (&instance->send_lock); - return; /* done - no more buffers */ - } - buf = list_entry (instance->spare_send_buffers.next, struct udsl_send_buffer, list); - list_del (&buf->list); - spin_unlock_irq (&instance->send_lock); - - buf->free_start = buf->base; - buf->free_cells = snd_buf_size; - - instance->current_buffer = buf; - } - - num_written = udsl_write_cells (buf->free_cells, skb, &buf->free_start); - - vdbg ("udsl_process_send: wrote %u cells from skb 0x%p to buffer 0x%p", num_written, skb, buf); - - if (!(buf->free_cells -= num_written)) { - list_add_tail (&buf->list, &instance->filled_send_buffers); - instance->current_buffer = NULL; - } - - vdbg ("udsl_process_send: buffer contains %d cells, %d left", snd_buf_size - buf->free_cells, buf->free_cells); - - if (!UDSL_SKB (skb)->num_cells) { - struct atm_vcc *vcc = UDSL_SKB (skb)->atm_data.vcc; - - udsl_pop (vcc, skb); - instance->current_skb = NULL; - - atomic_inc (&vcc->stats->tx); - } - - goto made_progress; -} - -static void udsl_cancel_send (struct udsl_instance_data *instance, struct atm_vcc *vcc) -{ - struct sk_buff *skb, *n; - - dbg ("udsl_cancel_send entered"); - spin_lock_irq (&instance->sndqueue.lock); - for (skb = instance->sndqueue.next, n = skb->next; skb != (struct sk_buff *)&instance->sndqueue; skb = n, n = skb->next) - if (UDSL_SKB (skb)->atm_data.vcc == vcc) { - dbg ("udsl_cancel_send: popping skb 0x%p", skb); - __skb_unlink (skb, &instance->sndqueue); - udsl_pop (vcc, skb); - } - spin_unlock_irq (&instance->sndqueue.lock); - - tasklet_disable (&instance->send_tasklet); - if ((skb = instance->current_skb) && (UDSL_SKB (skb)->atm_data.vcc == vcc)) { - dbg ("udsl_cancel_send: popping current skb (0x%p)", skb); - instance->current_skb = NULL; - udsl_pop (vcc, skb); - } - tasklet_enable (&instance->send_tasklet); - dbg ("udsl_cancel_send done"); -} - -static int udsl_atm_send (struct atm_vcc *vcc, struct sk_buff *skb) -{ - struct udsl_instance_data *instance = vcc->dev->dev_data; - int err; - - vdbg ("udsl_atm_send called (skb 0x%p, len %u)", skb, skb->len); - - if (!instance || !instance->usb_dev) { - dbg ("udsl_atm_send: NULL data!"); - err = -ENODEV; - goto fail; - } - - if (vcc->qos.aal != ATM_AAL5) { - dbg ("udsl_atm_send: unsupported ATM type %d!", vcc->qos.aal); - err = -EINVAL; - goto fail; - } - - if (skb->len > ATM_MAX_AAL5_PDU) { - dbg ("udsl_atm_send: packet too long (%d vs %d)!", skb->len, ATM_MAX_AAL5_PDU); - err = -EINVAL; - goto fail; - } - - PACKETDEBUG (skb->data, skb->len); - - udsl_groom_skb (vcc, skb); - skb_queue_tail (&instance->sndqueue, skb); - tasklet_schedule (&instance->send_tasklet); - - return 0; - -fail: - udsl_pop (vcc, skb); - return err; -} - - -/********** -** ATM ** -**********/ - -static void udsl_atm_dev_close (struct atm_dev *dev) -{ - struct udsl_instance_data *instance = dev->dev_data; - - if (!instance) { - dbg ("udsl_atm_dev_close: NULL instance!"); - return; - } - - dbg ("udsl_atm_dev_close: queue has %u elements", instance->sndqueue.qlen); - - tasklet_kill (&instance->receive_tasklet); - tasklet_kill (&instance->send_tasklet); - kfree (instance); - dev->dev_data = NULL; -} - -static int udsl_atm_proc_read (struct atm_dev *atm_dev, loff_t *pos, char *page) -{ - struct udsl_instance_data *instance = atm_dev->dev_data; - int left = *pos; - - if (!instance) { - dbg ("udsl_atm_proc_read: NULL instance!"); - return -ENODEV; - } - - if (!left--) - return sprintf (page, "%s\n", instance->description); - - if (!left--) - return sprintf (page, "MAC: %02x:%02x:%02x:%02x:%02x:%02x\n", - atm_dev->esi [0], atm_dev->esi [1], atm_dev->esi [2], - atm_dev->esi [3], atm_dev->esi [4], atm_dev->esi [5]); - - if (!left--) - return sprintf (page, "AAL5: tx %d ( %d err ), rx %d ( %d err, %d drop )\n", - atomic_read (&atm_dev->stats.aal5.tx), - atomic_read (&atm_dev->stats.aal5.tx_err), - atomic_read (&atm_dev->stats.aal5.rx), - atomic_read (&atm_dev->stats.aal5.rx_err), - atomic_read (&atm_dev->stats.aal5.rx_drop)); - - if (!left--) { - switch (atm_dev->signal) { - case ATM_PHY_SIG_FOUND: - sprintf (page, "Line up"); - break; - case ATM_PHY_SIG_LOST: - sprintf (page, "Line down"); - break; - default: - sprintf (page, "Line state unknown"); - break; - } - - if (instance->usb_dev) { - if (!instance->firmware_loaded) - strcat (page, ", no firmware\n"); - else - strcat (page, ", firmware loaded\n"); - } else - strcat (page, ", disconnected\n"); - - return strlen (page); - } - - return 0; -} - -static int udsl_atm_open (struct atm_vcc *vcc) -{ - struct udsl_instance_data *instance = vcc->dev->dev_data; - struct udsl_vcc_data *new; - unsigned int max_pdu; - int vci = vcc->vci; - short vpi = vcc->vpi; - - dbg ("udsl_atm_open: vpi %hd, vci %d", vpi, vci); - - if (!instance || !instance->usb_dev) { - dbg ("udsl_atm_open: NULL data!"); - return -ENODEV; - } - - /* only support AAL5 */ - if ((vcc->qos.aal != ATM_AAL5) || (vcc->qos.rxtp.max_sdu < 0) || (vcc->qos.rxtp.max_sdu > ATM_MAX_AAL5_PDU)) { - dbg ("udsl_atm_open: unsupported ATM type %d!", vcc->qos.aal); - return -EINVAL; - } - - if (!instance->firmware_loaded) { - dbg ("udsl_atm_open: firmware not loaded!"); - return -EAGAIN; - } - - down (&instance->serialize); /* vs self, udsl_atm_close */ - - if (udsl_find_vcc (instance, vpi, vci)) { - dbg ("udsl_atm_open: %hd/%d already in use!", vpi, vci); - up (&instance->serialize); - return -EADDRINUSE; - } - - if (!(new = kmalloc (sizeof (struct udsl_vcc_data), GFP_KERNEL))) { - dbg ("udsl_atm_open: no memory for vcc_data!"); - up (&instance->serialize); - return -ENOMEM; - } - - memset (new, 0, sizeof (struct udsl_vcc_data)); - new->vcc = vcc; - new->vpi = vpi; - new->vci = vci; - - /* udsl_extract_cells requires at least one cell */ - max_pdu = max (1, UDSL_NUM_CELLS (vcc->qos.rxtp.max_sdu)) * ATM_CELL_PAYLOAD; - if (!(new->sarb = alloc_skb (max_pdu, GFP_KERNEL))) { - dbg ("udsl_atm_open: no memory for SAR buffer!"); - kfree (new); - up (&instance->serialize); - return -ENOMEM; - } - - vcc->dev_data = new; - - tasklet_disable (&instance->receive_tasklet); - list_add (&new->list, &instance->vcc_list); - tasklet_enable (&instance->receive_tasklet); - - set_bit (ATM_VF_ADDR, &vcc->flags); - set_bit (ATM_VF_PARTIAL, &vcc->flags); - set_bit (ATM_VF_READY, &vcc->flags); - - up (&instance->serialize); - - tasklet_schedule (&instance->receive_tasklet); - - dbg ("udsl_atm_open: allocated vcc data 0x%p (max_pdu: %u)", new, max_pdu); - - return 0; -} - -static void udsl_atm_close (struct atm_vcc *vcc) -{ - struct udsl_instance_data *instance = vcc->dev->dev_data; - struct udsl_vcc_data *vcc_data = vcc->dev_data; - - dbg ("udsl_atm_close called"); - - if (!instance || !vcc_data) { - dbg ("udsl_atm_close: NULL data!"); - return; - } - - dbg ("udsl_atm_close: deallocating vcc 0x%p with vpi %d vci %d", vcc_data, vcc_data->vpi, vcc_data->vci); - - udsl_cancel_send (instance, vcc); - - down (&instance->serialize); /* vs self, udsl_atm_open */ - - tasklet_disable (&instance->receive_tasklet); - list_del (&vcc_data->list); - tasklet_enable (&instance->receive_tasklet); - - kfree_skb (vcc_data->sarb); - vcc_data->sarb = NULL; - - kfree (vcc_data); - vcc->dev_data = NULL; - - vcc->vpi = ATM_VPI_UNSPEC; - vcc->vci = ATM_VCI_UNSPEC; - clear_bit (ATM_VF_READY, &vcc->flags); - clear_bit (ATM_VF_PARTIAL, &vcc->flags); - clear_bit (ATM_VF_ADDR, &vcc->flags); - - up (&instance->serialize); - - dbg ("udsl_atm_close successful"); -} - -static int udsl_atm_ioctl (struct atm_dev *dev, unsigned int cmd, void __user *arg) -{ - switch (cmd) { - case ATM_QUERYLOOP: - return put_user (ATM_LM_NONE, (int __user *)arg) ? -EFAULT : 0; - default: - return -ENOIOCTLCMD; - } -} - - -/********** -** USB ** -**********/ - -static int udsl_set_alternate (struct udsl_instance_data *instance) -{ - down (&instance->serialize); /* vs self */ - if (!instance->firmware_loaded) { - int ret; - - if ((ret = usb_set_interface (instance->usb_dev, 1, 1)) < 0) { - dbg ("udsl_set_alternate: usb_set_interface returned %d!", ret); - up (&instance->serialize); - return ret; - } - instance->firmware_loaded = 1; - } - up (&instance->serialize); - - tasklet_schedule (&instance->receive_tasklet); - - return 0; -} - -static int udsl_usb_ioctl (struct usb_interface *intf, unsigned int code, void *user_data) -{ - struct udsl_instance_data *instance = usb_get_intfdata (intf); - - dbg ("udsl_usb_ioctl entered"); - - if (!instance) { - dbg ("udsl_usb_ioctl: NULL instance!"); - return -ENODEV; - } - - switch (code) { - case UDSL_IOCTL_LINE_UP: - instance->atm_dev->signal = ATM_PHY_SIG_FOUND; - return udsl_set_alternate (instance); - case UDSL_IOCTL_LINE_DOWN: - instance->atm_dev->signal = ATM_PHY_SIG_LOST; - return 0; - default: - return -ENOTTY; - } -} - -static int udsl_usb_probe (struct usb_interface *intf, const struct usb_device_id *id) -{ - struct usb_device *dev = interface_to_usbdev(intf); - int ifnum = intf->altsetting->desc.bInterfaceNumber; - struct udsl_instance_data *instance; - unsigned char mac_str [13]; - int i, length; - char *buf; - - dbg ("udsl_usb_probe: trying device with vendor=0x%x, product=0x%x, ifnum %d", - dev->descriptor.idVendor, dev->descriptor.idProduct, ifnum); - - if ((dev->descriptor.bDeviceClass != USB_CLASS_VENDOR_SPEC) || - (dev->descriptor.idVendor != SPEEDTOUCH_VENDORID) || - (dev->descriptor.idProduct != SPEEDTOUCH_PRODUCTID) || (ifnum != 1)) - return -ENODEV; - - dbg ("udsl_usb_probe: device accepted"); - - /* instance init */ - if (!(instance = kmalloc (sizeof (struct udsl_instance_data), GFP_KERNEL))) { - dbg ("udsl_usb_probe: no memory for instance data!"); - return -ENOMEM; - } - - memset (instance, 0, sizeof (struct udsl_instance_data)); - - init_MUTEX (&instance->serialize); - - instance->usb_dev = dev; - - INIT_LIST_HEAD (&instance->vcc_list); - - spin_lock_init (&instance->receive_lock); - INIT_LIST_HEAD (&instance->spare_receivers); - INIT_LIST_HEAD (&instance->filled_receive_buffers); - - tasklet_init (&instance->receive_tasklet, udsl_process_receive, (unsigned long) instance); - INIT_LIST_HEAD (&instance->spare_receive_buffers); - - skb_queue_head_init (&instance->sndqueue); - - spin_lock_init (&instance->send_lock); - INIT_LIST_HEAD (&instance->spare_senders); - INIT_LIST_HEAD (&instance->spare_send_buffers); - - tasklet_init (&instance->send_tasklet, udsl_process_send, (unsigned long) instance); - INIT_LIST_HEAD (&instance->filled_send_buffers); - - /* receive init */ - for (i = 0; i < num_rcv_urbs; i++) { - struct udsl_receiver *rcv = &(instance->receivers [i]); - - if (!(rcv->urb = usb_alloc_urb (0, GFP_KERNEL))) { - dbg ("udsl_usb_probe: no memory for receive urb %d!", i); - goto fail; - } - - rcv->instance = instance; - - list_add (&rcv->list, &instance->spare_receivers); - } - - for (i = 0; i < num_rcv_bufs; i++) { - struct udsl_receive_buffer *buf = &(instance->receive_buffers [i]); - - if (!(buf->base = kmalloc (rcv_buf_size * ATM_CELL_SIZE, GFP_KERNEL))) { - dbg ("udsl_usb_probe: no memory for receive buffer %d!", i); - goto fail; - } - - list_add (&buf->list, &instance->spare_receive_buffers); - } - - /* send init */ - for (i = 0; i < num_snd_urbs; i++) { - struct udsl_sender *snd = &(instance->senders [i]); - - if (!(snd->urb = usb_alloc_urb (0, GFP_KERNEL))) { - dbg ("udsl_usb_probe: no memory for send urb %d!", i); - goto fail; - } - - snd->instance = instance; - - list_add (&snd->list, &instance->spare_senders); - } - - for (i = 0; i < num_snd_bufs; i++) { - struct udsl_send_buffer *buf = &(instance->send_buffers [i]); - - if (!(buf->base = kmalloc (snd_buf_size * ATM_CELL_SIZE, GFP_KERNEL))) { - dbg ("udsl_usb_probe: no memory for send buffer %d!", i); - goto fail; - } - - list_add (&buf->list, &instance->spare_send_buffers); - } - - /* ATM init */ - if (!(instance->atm_dev = atm_dev_register (udsl_driver_name, &udsl_atm_devops, -1, NULL))) { - dbg ("udsl_usb_probe: failed to register ATM device!"); - goto fail; - } - - instance->atm_dev->ci_range.vpi_bits = ATM_CI_MAX; - instance->atm_dev->ci_range.vci_bits = ATM_CI_MAX; - instance->atm_dev->signal = ATM_PHY_SIG_UNKNOWN; - - /* temp init ATM device, set to 128kbit */ - instance->atm_dev->link_rate = 128 * 1000 / 424; - - /* set MAC address, it is stored in the serial number */ - memset (instance->atm_dev->esi, 0, sizeof (instance->atm_dev->esi)); - if (usb_string (dev, dev->descriptor.iSerialNumber, mac_str, sizeof (mac_str)) == 12) - for (i = 0; i < 6; i++) - instance->atm_dev->esi [i] = (hex2int (mac_str [i * 2]) * 16) + (hex2int (mac_str [i * 2 + 1])); - - /* device description */ - buf = instance->description; - length = sizeof (instance->description); - - if ((i = usb_string (dev, dev->descriptor.iProduct, buf, length)) < 0) - goto finish; - - buf += i; - length -= i; - - i = scnprintf (buf, length, " ("); - buf += i; - length -= i; - - if (length <= 0 || (i = usb_make_path (dev, buf, length)) < 0) - goto finish; - - buf += i; - length -= i; - - snprintf (buf, length, ")"); - -finish: - /* ready for ATM callbacks */ - wmb (); - instance->atm_dev->dev_data = instance; - - usb_set_intfdata (intf, instance); - - return 0; - -fail: - for (i = 0; i < num_snd_bufs; i++) - kfree (instance->send_buffers [i].base); - - for (i = 0; i < num_snd_urbs; i++) - usb_free_urb (instance->senders [i].urb); - - for (i = 0; i < num_rcv_bufs; i++) - kfree (instance->receive_buffers [i].base); - - for (i = 0; i < num_rcv_urbs; i++) - usb_free_urb (instance->receivers [i].urb); - - kfree (instance); - - return -ENOMEM; -} - -static void udsl_usb_disconnect (struct usb_interface *intf) -{ - struct udsl_instance_data *instance = usb_get_intfdata (intf); - struct list_head *pos; - unsigned int count; - int result, i; - - dbg ("udsl_usb_disconnect entered"); - - usb_set_intfdata (intf, NULL); - - if (!instance) { - dbg ("udsl_usb_disconnect: NULL instance!"); - return; - } - - /* receive finalize */ - tasklet_disable (&instance->receive_tasklet); - - for (i = 0; i < num_rcv_urbs; i++) - if ((result = usb_unlink_urb (instance->receivers [i].urb)) < 0) - dbg ("udsl_usb_disconnect: usb_unlink_urb on receive urb %d returned %d!", i, result); - - /* wait for completion handlers to finish */ - do { - count = 0; - spin_lock_irq (&instance->receive_lock); - list_for_each (pos, &instance->spare_receivers) - DEBUG_ON (++count > num_rcv_urbs); - spin_unlock_irq (&instance->receive_lock); - - dbg ("udsl_usb_disconnect: found %u spare receivers", count); - - if (count == num_rcv_urbs) - break; - - set_current_state (TASK_RUNNING); - schedule (); - } while (1); - - /* no need to take the spinlock */ - INIT_LIST_HEAD (&instance->filled_receive_buffers); - INIT_LIST_HEAD (&instance->spare_receive_buffers); - - tasklet_enable (&instance->receive_tasklet); - - for (i = 0; i < num_rcv_urbs; i++) - usb_free_urb (instance->receivers [i].urb); - - for (i = 0; i < num_rcv_bufs; i++) - kfree (instance->receive_buffers [i].base); - - /* send finalize */ - tasklet_disable (&instance->send_tasklet); - - for (i = 0; i < num_snd_urbs; i++) - if ((result = usb_unlink_urb (instance->senders [i].urb)) < 0) - dbg ("udsl_usb_disconnect: usb_unlink_urb on send urb %d returned %d!", i, result); - - /* wait for completion handlers to finish */ - do { - count = 0; - spin_lock_irq (&instance->send_lock); - list_for_each (pos, &instance->spare_senders) - DEBUG_ON (++count > num_snd_urbs); - spin_unlock_irq (&instance->send_lock); - - dbg ("udsl_usb_disconnect: found %u spare senders", count); - - if (count == num_snd_urbs) - break; - - set_current_state (TASK_RUNNING); - schedule (); - } while (1); - - /* no need to take the spinlock */ - INIT_LIST_HEAD (&instance->spare_senders); - INIT_LIST_HEAD (&instance->spare_send_buffers); - instance->current_buffer = NULL; - - tasklet_enable (&instance->send_tasklet); - - for (i = 0; i < num_snd_urbs; i++) - usb_free_urb (instance->senders [i].urb); - - for (i = 0; i < num_snd_bufs; i++) - kfree (instance->send_buffers [i].base); - - wmb (); - instance->usb_dev = NULL; - - /* ATM finalize */ - shutdown_atm_dev (instance->atm_dev); /* frees instance, kills tasklets */ -} - - -/*********** -** init ** -***********/ - -static int __init udsl_usb_init (void) -{ - dbg ("udsl_usb_init: driver version " DRIVER_VERSION); - - if (sizeof (struct udsl_control) > sizeof (((struct sk_buff *)0)->cb)) { - printk (KERN_ERR __FILE__ ": unusable with this kernel!\n"); - return -EIO; - } - - if ((num_rcv_urbs > UDSL_MAX_RCV_URBS) || (num_snd_urbs > UDSL_MAX_SND_URBS) || - (num_rcv_bufs > UDSL_MAX_RCV_BUFS) || (num_snd_bufs > UDSL_MAX_SND_BUFS) || - (rcv_buf_size > UDSL_MAX_RCV_BUF_SIZE) || (snd_buf_size > UDSL_MAX_SND_BUF_SIZE)) - return -EINVAL; - - return usb_register (&udsl_usb_driver); -} - -static void __exit udsl_usb_cleanup (void) -{ - dbg ("udsl_usb_cleanup entered"); - - usb_deregister (&udsl_usb_driver); -} - -module_init (udsl_usb_init); -module_exit (udsl_usb_cleanup); - -MODULE_AUTHOR (DRIVER_AUTHOR); -MODULE_DESCRIPTION (DRIVER_DESC); -MODULE_LICENSE ("GPL"); -MODULE_VERSION (DRIVER_VERSION); - - -/************ -** debug ** -************/ - -#ifdef VERBOSE_DEBUG -static int udsl_print_packet (const unsigned char *data, int len) -{ - unsigned char buffer [256]; - int i = 0, j = 0; - - for (i = 0; i < len;) { - buffer [0] = '\0'; - sprintf (buffer, "%.3d :", i); - for (j = 0; (j < 16) && (i < len); j++, i++) { - sprintf (buffer, "%s %2.2x", buffer, data [i]); - } - dbg ("%s", buffer); - } - return i; -} -#endif -- cgit v1.2.3 From 6897bdb5816c602a4f081c1b281b643f69229c83 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Tue, 5 Oct 2004 19:09:35 +0100 Subject: USB: Reformat usb-atm code and rework SpeedTouch firmware loading. Now it's all from a single kernel thread started directly by the probe routine, and it checks it can find _all_ the firmware it wants before starting to poke at the modem. Signed-off-by: David Woodhouse Signed-off-by: Greg Kroah-Hartman --- drivers/usb/atm/speedtch.c | 602 +++++++++++++++--------------- drivers/usb/atm/usb_atm.c | 908 +++++++++++++++++++++++---------------------- drivers/usb/atm/usb_atm.h | 27 +- 3 files changed, 760 insertions(+), 777 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/atm/speedtch.c b/drivers/usb/atm/speedtch.c index 29be861235e0..696541b57c88 100644 --- a/drivers/usb/atm/speedtch.c +++ b/drivers/usb/atm/speedtch.c @@ -23,6 +23,7 @@ #include #include +#include #include #include #include @@ -31,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -57,14 +59,8 @@ # define USE_FW_LOADER #endif -#ifdef DEBUG -#define DEBUG_ON(x) BUG_ON(x) -#else -#define DEBUG_ON(x) do { if (x); } while (0) -#endif - #ifdef VERBOSE_DEBUG -static int udsl_print_packet (const unsigned char *data, int len); +static int udsl_print_packet(const unsigned char *data, int len); #define PACKETDEBUG(arg...) udsl_print_packet (arg) #define vdbg(arg...) dbg (arg) #else @@ -76,7 +72,7 @@ static int udsl_print_packet (const unsigned char *data, int len); #define DRIVER_VERSION "1.8" #define DRIVER_DESC "Alcatel SpeedTouch USB driver version " DRIVER_VERSION -static const char speedtch_driver_name [] = "speedtch"; +static const char speedtch_driver_name[] = "speedtch"; #define SPEEDTOUCH_VENDORID 0x06b9 #define SPEEDTOUCH_PRODUCTID 0x4061 @@ -85,11 +81,11 @@ static const char speedtch_driver_name [] = "speedtch"; #define CTRL_TIMEOUT (2*HZ) #define DATA_TIMEOUT (2*HZ) -#define OFFSET_7 0 /* size 1 */ -#define OFFSET_b 1 /* size 8 */ -#define OFFSET_d 9 /* size 4 */ -#define OFFSET_e 13 /* size 1 */ -#define OFFSET_f 14 /* size 1 */ +#define OFFSET_7 0 /* size 1 */ +#define OFFSET_b 1 /* size 8 */ +#define OFFSET_d 9 /* size 4 */ +#define OFFSET_e 13 /* size 1 */ +#define OFFSET_f 14 /* size 1 */ #define TOTAL 15 #define SIZE_7 1 @@ -101,11 +97,11 @@ static const char speedtch_driver_name [] = "speedtch"; static int dl_512_first = 0; static int sw_buffering = 0; -module_param (dl_512_first, bool, 0444); -MODULE_PARM_DESC (dl_512_first, "Read 512 bytes before sending firmware"); +module_param(dl_512_first, bool, 0444); +MODULE_PARM_DESC(dl_512_first, "Read 512 bytes before sending firmware"); -module_param (sw_buffering, uint, 0444); -MODULE_PARM_DESC (sw_buffering, "Enable software buffering"); +module_param(sw_buffering, uint, 0444); +MODULE_PARM_DESC(sw_buffering, "Enable software buffering"); #define UDSL_IOCTL_LINE_UP 1 #define UDSL_IOCTL_LINE_DOWN 2 @@ -116,112 +112,113 @@ MODULE_PARM_DESC (sw_buffering, "Enable software buffering"); #define hex2int(c) ( (c >= '0') && (c <= '9') ? (c - '0') : ((c & 0xf) + 9) ) -static struct usb_device_id speedtch_usb_ids [] = { - { USB_DEVICE (SPEEDTOUCH_VENDORID, SPEEDTOUCH_PRODUCTID) }, - { } +static struct usb_device_id speedtch_usb_ids[] = { + {USB_DEVICE(SPEEDTOUCH_VENDORID, SPEEDTOUCH_PRODUCTID)}, + {} }; -MODULE_DEVICE_TABLE (usb, speedtch_usb_ids); - +MODULE_DEVICE_TABLE(usb, speedtch_usb_ids); struct speedtch_instance_data { struct udsl_instance_data u; - u16 revision; - /* Status */ struct urb *int_urb; unsigned char int_data[16]; struct work_struct poll_work; struct timer_list poll_timer; - char fwname[25]; }; /* USB */ -static int speedtch_usb_probe (struct usb_interface *intf, const struct usb_device_id *id); -static void speedtch_usb_disconnect (struct usb_interface *intf); -static int speedtch_usb_ioctl (struct usb_interface *intf, unsigned int code, void *user_data); -static void speedtch_handle_int (struct urb *urb, struct pt_regs *regs); -static void speedtch_poll_status (struct speedtch_instance_data *instance); +static int speedtch_usb_probe(struct usb_interface *intf, + const struct usb_device_id *id); +static void speedtch_usb_disconnect(struct usb_interface *intf); +static int speedtch_usb_ioctl(struct usb_interface *intf, unsigned int code, + void *user_data); +static void speedtch_handle_int(struct urb *urb, struct pt_regs *regs); +static void speedtch_poll_status(struct speedtch_instance_data *instance); static struct usb_driver speedtch_usb_driver = { - .owner = THIS_MODULE, - .name = speedtch_driver_name, - .probe = speedtch_usb_probe, - .disconnect = speedtch_usb_disconnect, - .ioctl = speedtch_usb_ioctl, - .id_table = speedtch_usb_ids, + .owner = THIS_MODULE, + .name = speedtch_driver_name, + .probe = speedtch_usb_probe, + .disconnect = speedtch_usb_disconnect, + .ioctl = speedtch_usb_ioctl, + .id_table = speedtch_usb_ids, }; - /*************** ** firmware ** ***************/ -static void speedtch_got_firmware (struct speedtch_instance_data *instance, int got_it) +static void speedtch_got_firmware(struct speedtch_instance_data *instance, + int got_it) { int err; struct usb_interface *intf; - down (&instance->u.serialize); /* vs self, speedtch_firmware_start */ + down(&instance->u.serialize); /* vs self, speedtch_firmware_start */ if (instance->u.status == UDSL_LOADED_FIRMWARE) goto out; if (!got_it) { instance->u.status = UDSL_NO_FIRMWARE; goto out; } - if ((err = usb_set_interface (instance->u.usb_dev, 1, 1)) < 0) { - dbg ("speedtch_got_firmware: usb_set_interface returned %d!", err); + if ((err = usb_set_interface(instance->u.usb_dev, 1, 1)) < 0) { + dbg("speedtch_got_firmware: usb_set_interface returned %d!", + err); instance->u.status = UDSL_NO_FIRMWARE; goto out; } /* Set up interrupt endpoint */ intf = usb_ifnum_to_if(instance->u.usb_dev, 0); - if (intf && !usb_driver_claim_interface (&speedtch_usb_driver, intf, NULL)) { + if (intf && !usb_driver_claim_interface(&speedtch_usb_driver, intf, NULL)) { instance->int_urb = usb_alloc_urb(0, GFP_KERNEL); if (instance->int_urb) { usb_fill_int_urb(instance->int_urb, instance->u.usb_dev, usb_rcvintpipe(instance->u.usb_dev, SPEEDTCH_ENDPOINT_INT), - instance->int_data, sizeof(instance->int_data), + instance->int_data, + sizeof(instance->int_data), speedtch_handle_int, instance, 50); err = usb_submit_urb(instance->int_urb, GFP_KERNEL); if (err) { /* Doesn't matter; we'll poll anyway */ - dbg ("speedtch_got_firmware: Submission of interrupt URB failed %d", err); + dbg("speedtch_got_firmware: Submission of interrupt URB failed %d", err); usb_free_urb(instance->int_urb); instance->int_urb = NULL; - usb_driver_release_interface (&speedtch_usb_driver, intf); + usb_driver_release_interface(&speedtch_usb_driver, intf); } } } /* Start status polling */ - mod_timer(&instance->poll_timer, jiffies + (1*HZ)); + mod_timer(&instance->poll_timer, jiffies + (1 * HZ)); instance->u.status = UDSL_LOADED_FIRMWARE; - tasklet_schedule (&instance->u.receive_tasklet); -out: - up (&instance->u.serialize); - wake_up_interruptible (&instance->u.firmware_waiters); + tasklet_schedule(&instance->u.receive_tasklet); + out: + up(&instance->u.serialize); + wake_up_interruptible(&instance->u.firmware_waiters); } -static int speedtch_set_swbuff (struct speedtch_instance_data *instance, int state) +static int speedtch_set_swbuff(struct speedtch_instance_data *instance, + int state) { struct usb_device *dev = instance->u.usb_dev; int ret; ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), - 0x32, 0x40, state?0x01:0x00, + 0x32, 0x40, state ? 0x01 : 0x00, 0x00, NULL, 0, 100); if (ret < 0) { printk("Warning: %sabling SW buffering: usb_control_msg returned %d\n", - state?"En":"Dis", ret); + state ? "En" : "Dis", ret); return ret; } - dbg("speedtch_set_swbuff: %sbled SW buffering", state?"En":"Dis"); + dbg("speedtch_set_swbuff: %sbled SW buffering", state ? "En" : "Dis"); return 0; } @@ -232,35 +229,41 @@ static void speedtch_test_sequence(struct speedtch_instance_data *instance) int ret; /* URB 147 */ - buf[0] = 0x1c; buf[1] = 0x50; + buf[0] = 0x1c; + buf[1] = 0x50; ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), 0x01, 0x40, 0x0b, 0x00, buf, 2, 100); if (ret < 0) printk(KERN_WARNING "%s failed on URB147: %d\n", __func__, ret); /* URB 148 */ - buf[0] = 0x32; buf[1] = 0x00; + buf[0] = 0x32; + buf[1] = 0x00; ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), 0x01, 0x40, 0x02, 0x00, buf, 2, 100); if (ret < 0) printk(KERN_WARNING "%s failed on URB148: %d\n", __func__, ret); /* URB 149 */ - buf[0] = 0x01; buf[1] = 0x00; buf[2] = 0x01; + buf[0] = 0x01; + buf[1] = 0x00; + buf[2] = 0x01; ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), 0x01, 0x40, 0x03, 0x00, buf, 3, 100); if (ret < 0) printk(KERN_WARNING "%s failed on URB149: %d\n", __func__, ret); /* URB 150 */ - buf[0] = 0x01; buf[1] = 0x00; buf[2] = 0x01; + buf[0] = 0x01; + buf[1] = 0x00; + buf[2] = 0x01; ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), 0x01, 0x40, 0x04, 0x00, buf, 3, 100); if (ret < 0) printk(KERN_WARNING "%s failed on URB150: %d\n", __func__, ret); } -static int speedtch_start_synchro (struct speedtch_instance_data *instance) +static int speedtch_start_synchro(struct speedtch_instance_data *instance) { struct usb_device *dev = instance->u.usb_dev; unsigned char buf[2]; @@ -278,37 +281,36 @@ static int speedtch_start_synchro (struct speedtch_instance_data *instance) return 0; } -static void speedtch_handle_int (struct urb *urb, struct pt_regs *regs) +static void speedtch_handle_int(struct urb *urb, struct pt_regs *regs) { struct speedtch_instance_data *instance = urb->context; unsigned int count = urb->actual_length; int ret; /* The magic interrupt for "up state" */ - const static unsigned char up_int[6] = { 0xa1, 0x00, 0x01, 0x00, 0x00, 0x00}; + const static unsigned char up_int[6] = { 0xa1, 0x00, 0x01, 0x00, 0x00, 0x00 }; /* The magic interrupt for "down state" */ - const static unsigned char down_int[6] = { 0xa1, 0x00, 0x00, 0x00, 0x00, 0x00}; - + const static unsigned char down_int[6] = { 0xa1, 0x00, 0x00, 0x00, 0x00, 0x00 }; switch (urb->status) { - case 0: - /* success */ - break; - case -ECONNRESET: - case -ENOENT: - case -ESHUTDOWN: - /* this urb is terminated, clean up */ - dbg("%s - urb shutting down with status: %d", __func__, urb->status); - return; - default: - dbg("%s - nonzero urb status received: %d", __func__, urb->status); - goto exit; - } - - if (count < 6) { - dbg("%s - int packet too short", __func__); - goto exit; - } + case 0: + /* success */ + break; + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + /* this urb is terminated; clean up */ + dbg("%s - urb shutting down with status: %d", __func__, urb->status); + return; + default: + dbg("%s - nonzero urb status received: %d", __func__, urb->status); + goto exit; + } + + if (count < 6) { + dbg("%s - int packet too short", __func__); + goto exit; + } if (!memcmp(up_int, instance->int_data, 6)) { del_timer(&instance->poll_timer); @@ -320,7 +322,7 @@ static void speedtch_handle_int (struct urb *urb, struct pt_regs *regs) int i; printk(KERN_DEBUG "Unknown interrupt packet of %d bytes:", count); - for (i=0; i < count; i++) + for (i = 0; i < count; i++) printk(" %02x", instance->int_data[i]); printk("\n"); } @@ -331,52 +333,57 @@ static void speedtch_handle_int (struct urb *urb, struct pt_regs *regs) if (!instance->int_urb) return; - ret = usb_submit_urb (urb, GFP_ATOMIC); + ret = usb_submit_urb(urb, GFP_ATOMIC); if (ret) - err ("%s - usb_submit_urb failed with result %d", - __func__, ret); + err("%s - usb_submit_urb failed with result %d", __func__, ret); } -static int speedtch_get_status(struct speedtch_instance_data *instance, unsigned char *buf) +static int speedtch_get_status(struct speedtch_instance_data *instance, + unsigned char *buf) { struct usb_device *dev = instance->u.usb_dev; int ret; - memset(buf,0,TOTAL); + memset(buf, 0, TOTAL); ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), - 0x12, 0xc0, 0x07, 0x00, buf+OFFSET_7, SIZE_7, CTRL_TIMEOUT); - if (ret<0) { + 0x12, 0xc0, 0x07, 0x00, buf + OFFSET_7, SIZE_7, + CTRL_TIMEOUT); + if (ret < 0) { dbg("MSG 7 failed"); - return(ret); + return ret; } ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), - 0x12, 0xc0, 0x0b, 0x00, buf+OFFSET_b, SIZE_b, CTRL_TIMEOUT); - if (ret<0) { + 0x12, 0xc0, 0x0b, 0x00, buf + OFFSET_b, SIZE_b, + CTRL_TIMEOUT); + if (ret < 0) { dbg("MSG B failed"); - return(ret); + return ret; } ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), - 0x12, 0xc0, 0x0d, 0x00, buf+OFFSET_d, SIZE_d, CTRL_TIMEOUT); - if (ret<0) { + 0x12, 0xc0, 0x0d, 0x00, buf + OFFSET_d, SIZE_d, + CTRL_TIMEOUT); + if (ret < 0) { dbg("MSG D failed"); - return(ret); + return ret; } ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), - 0x01, 0xc0, 0x0e, 0x00, buf+OFFSET_e, SIZE_e, CTRL_TIMEOUT); - if (ret<0) { + 0x01, 0xc0, 0x0e, 0x00, buf + OFFSET_e, SIZE_e, + CTRL_TIMEOUT); + if (ret < 0) { dbg("MSG E failed"); - return(ret); + return ret; } ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), - 0x01, 0xc0, 0x0f, 0x00, buf+OFFSET_f, SIZE_f, CTRL_TIMEOUT); - if (ret<0) { + 0x01, 0xc0, 0x0f, 0x00, buf + OFFSET_f, SIZE_f, + CTRL_TIMEOUT); + if (ret < 0) { dbg("MSG F failed"); - return(ret); + return ret; } return 0; @@ -389,7 +396,8 @@ static void speedtch_poll_status(struct speedtch_instance_data *instance) ret = speedtch_get_status(instance, buf); if (ret) { - printk(KERN_WARNING "SpeedTouch: Error %d fetching device status\n", ret); + printk(KERN_WARNING + "SpeedTouch: Error %d fetching device status\n", ret); return; } @@ -419,20 +427,21 @@ static void speedtch_poll_status(struct speedtch_instance_data *instance) case 0x20: if (instance->u.atm_dev->signal != ATM_PHY_SIG_FOUND) { - int down_speed = buf[OFFSET_b] | (buf[OFFSET_b+1]<<8) - | (buf[OFFSET_b+2]<<16) | (buf[OFFSET_b+3]<<24); - int up_speed = buf[OFFSET_b+4] | (buf[OFFSET_b+5]<<8) - | (buf[OFFSET_b+6]<<16) | (buf[OFFSET_b+7]<<24); - - if(!(down_speed & 0x0000ffff) && - !(up_speed & 0x0000ffff)) { - down_speed>>=16; - up_speed>>=16; + int down_speed = buf[OFFSET_b] | (buf[OFFSET_b + 1] << 8) + | (buf[OFFSET_b + 2] << 16) | (buf[OFFSET_b + 3] << 24); + int up_speed = buf[OFFSET_b + 4] | (buf[OFFSET_b + 5] << 8) + | (buf[OFFSET_b + 6] << 16) | (buf[OFFSET_b + 7] << 24); + + if (!(down_speed & 0x0000ffff) && + !(up_speed & 0x0000ffff)) { + down_speed >>= 16; + up_speed >>= 16; } instance->u.atm_dev->link_rate = down_speed * 1000 / 424; instance->u.atm_dev->signal = ATM_PHY_SIG_FOUND; - printk(KERN_NOTICE "ADSL line is up (%d Kib/s down | %d Kib/s up)\n", + printk(KERN_NOTICE + "ADSL line is up (%d Kib/s down | %d Kib/s up)\n", down_speed, up_speed); } break; @@ -446,176 +455,111 @@ static void speedtch_poll_status(struct speedtch_instance_data *instance) } } -static void speedtch_timer_poll (unsigned long data) +static void speedtch_timer_poll(unsigned long data) { - struct speedtch_instance_data *instance = (struct speedtch_instance_data *) data; + struct speedtch_instance_data *instance = (void *)data; schedule_work(&instance->poll_work); - mod_timer(&instance->poll_timer, jiffies + (5*HZ)); + mod_timer(&instance->poll_timer, jiffies + (5 * HZ)); } -static void speedtch_firmware_load(const struct firmware *fw1, void *context) +#ifdef USE_FW_LOADER + +static void speedtch_upload_firmware(struct speedtch_instance_data *instance, + const struct firmware *fw1, + const struct firmware *fw2) { - const struct firmware *fw2; unsigned char *buffer; - struct speedtch_instance_data *instance = context; + struct usb_device *usb_dev = instance->u.usb_dev; struct usb_interface *intf; - struct usb_device *dev = instance->u.usb_dev; int actual_length, ret; - int pg; + int offset; - dbg ("speedtch_firmware_load"); + dbg("speedtch_upload_firmware"); - ret = -1; - - BUG_ON(!instance); - - if (!(intf = usb_ifnum_to_if (dev, 2))) { - dbg ("speedtch_firmware_load: interface not found!"); + if (!(intf = usb_ifnum_to_if(usb_dev, 2))) { + dbg("speedtch_upload_firmware: interface not found!"); goto fail; } - if (!(buffer = kmalloc (0x1000, GFP_KERNEL))) { - dbg ("speedtch_firmware_load: no memory for buffer!"); + if (!(buffer = (unsigned char *)__get_free_page(GFP_KERNEL))) { + dbg("speedtch_upload_firmware: no memory for buffer!"); goto fail; } - if (!fw1) { - dbg ("speedtch_firmware_load: no firmware 'speedtch_boot_rev%d.%02x", - instance->revision >> 8, instance->revision & 0xff); - - snprintf(instance->fwname, sizeof(instance->fwname), "speedtch_boot_rev%d", - instance->revision >> 8); - - ret = request_firmware(&fw1, instance->fwname, &dev->dev); - if (ret < 0) { - dbg ("speedtch_firmware_load: no firmware '%s'", instance->fwname); - - strlcpy(instance->fwname, "speedtch_boot", sizeof(instance->fwname)); - ret = request_firmware(&fw1, instance->fwname, &dev->dev); - } - if (ret < 0) { - dbg ("speedtch_firmware_load: no firmware '%s'", instance->fwname); - printk(KERN_INFO "speedtch: No boot firmware found. Assuming userspace firmware loader\n"); - goto fail_buf; - } - } - snprintf(instance->fwname, sizeof(instance->fwname), "speedtch_main_rev%d.%02x", - instance->revision >> 8, instance->revision & 0xff); - ret = request_firmware(&fw2, instance->fwname, &dev->dev); - if (ret < 0) { - dbg ("speedtch_firmware_load: no firmware '%s'", instance->fwname); - snprintf(instance->fwname, sizeof(instance->fwname), "speedtch_main_rev%d", - instance->revision >> 8); - - ret = request_firmware(&fw2, instance->fwname, &dev->dev); - } - if (ret < 0) { - dbg ("speedtch_firmware_load: no firmware '%s'", instance->fwname); - - strlcpy(instance->fwname, "speedtch_main", sizeof(instance->fwname)); - ret = request_firmware(&fw2, instance->fwname, &dev->dev); - } - if (ret < 0) { - dbg ("speedtch_firmware_load: no firmware '%s'", instance->fwname); - printk(KERN_INFO "speedtch: No main firmware found. Assuming userspace firmware loader\n"); - goto fail_buf; - } - - /* OK, we have the firmware. So try to claim interface #2 and actually - try uploading it. There's a slight possibility that the userspace - modem_run could be running too, and may have beaten us to it */ - if ((ret = usb_driver_claim_interface (&speedtch_usb_driver, intf, NULL)) < 0) { - dbg ("speedtch_firmware_start: interface in use (%d)!", ret); - goto fail_fw2; + /* A user-space firmware loader may already have claimed interface #2 */ + if ((ret = + usb_driver_claim_interface(&speedtch_usb_driver, intf, NULL)) < 0) { + dbg("speedtch_upload_firmware: interface in use (%d)!", ret); + goto fail_free; } /* URB 7 */ - if (dl_512_first) { /* some modems need a read before writing the firmware */ - ret = usb_bulk_msg (instance->u.usb_dev, - usb_rcvbulkpipe (instance->u.usb_dev, SPEEDTCH_ENDPOINT_FIRMWARE), - buffer, - 0x200, - &actual_length, - 2 * HZ); + if (dl_512_first) { /* some modems need a read before writing the firmware */ + ret = usb_bulk_msg(usb_dev, usb_rcvbulkpipe(usb_dev, SPEEDTCH_ENDPOINT_FIRMWARE), + buffer, 0x200, &actual_length, 2 * HZ); if (ret < 0 && ret != -ETIMEDOUT) - dbg ("speedtch_firmware_load: read BLOCK0 from modem failed (%d)!", ret); + dbg("speedtch_upload_firmware: read BLOCK0 from modem failed (%d)!", ret); else - dbg("speedtch_firmware_load: BLOCK0 downloaded (%d bytes)", ret); + dbg("speedtch_upload_firmware: BLOCK0 downloaded (%d bytes)", ret); } /* URB 8 : both leds are static green */ - for (pg = 0; pg * 0x1000 < fw1->size; pg++) { - int thislen = min_t(int, 0x1000, fw1->size - (pg*0x1000)); - memcpy(buffer, fw1->data + (pg*0x1000), thislen); + for (offset = 0; offset < fw1->size; offset += PAGE_SIZE) { + int thislen = min_t(int, PAGE_SIZE, fw1->size - offset); + memcpy(buffer, fw1->data + offset, thislen); - ret = usb_bulk_msg (instance->u.usb_dev, - usb_sndbulkpipe (instance->u.usb_dev, SPEEDTCH_ENDPOINT_FIRMWARE), - buffer, - thislen, - &actual_length, - DATA_TIMEOUT); + ret = usb_bulk_msg(usb_dev, usb_sndbulkpipe(usb_dev, SPEEDTCH_ENDPOINT_FIRMWARE), + buffer, thislen, &actual_length, DATA_TIMEOUT); if (ret < 0) { - dbg ("speedtch_firmware_load: write BLOCK1 to modem failed (%d)!", ret); + dbg("speedtch_upload_firmware: write BLOCK1 to modem failed (%d)!", ret); goto fail_release; } - dbg("speedtch_firmware_load: BLOCK1 uploaded (%d bytes)", fw1->size); + dbg("speedtch_upload_firmware: BLOCK1 uploaded (%d bytes)", fw1->size); } /* USB led blinking green, ADSL led off */ /* URB 11 */ - ret = usb_bulk_msg (instance->u.usb_dev, - usb_rcvbulkpipe (instance->u.usb_dev, SPEEDTCH_ENDPOINT_FIRMWARE), - buffer, - 0x200, - &actual_length, - DATA_TIMEOUT); + ret = usb_bulk_msg(usb_dev, usb_rcvbulkpipe(usb_dev, SPEEDTCH_ENDPOINT_FIRMWARE), + buffer, 0x200, &actual_length, DATA_TIMEOUT); if (ret < 0) { - dbg ("speedtch_firmware_load: read BLOCK2 from modem failed (%d)!", ret); + dbg("speedtch_upload_firmware: read BLOCK2 from modem failed (%d)!", ret); goto fail_release; } - dbg("speedtch_firmware_load: BLOCK2 downloaded (%d bytes)", actual_length); + dbg("speedtch_upload_firmware: BLOCK2 downloaded (%d bytes)", actual_length); /* URBs 12 to 139 - USB led blinking green, ADSL led off */ - for (pg = 0; pg * 0x1000 < fw2->size; pg++) { - int thislen = min_t(int, 0x1000, fw2->size - (pg*0x1000)); - memcpy(buffer, fw2->data + (pg*0x1000), thislen); + for (offset = 0; offset < fw2->size; offset += PAGE_SIZE) { + int thislen = min_t(int, PAGE_SIZE, fw2->size - offset); + memcpy(buffer, fw2->data + offset, thislen); - ret = usb_bulk_msg (instance->u.usb_dev, - usb_sndbulkpipe (instance->u.usb_dev, SPEEDTCH_ENDPOINT_FIRMWARE), - buffer, - thislen, - &actual_length, - DATA_TIMEOUT); + ret = usb_bulk_msg(usb_dev, usb_sndbulkpipe(usb_dev, SPEEDTCH_ENDPOINT_FIRMWARE), + buffer, thislen, &actual_length, DATA_TIMEOUT); if (ret < 0) { - dbg ("speedtch_firmware_load: write BLOCK3 to modem failed (%d)!", ret); + dbg("speedtch_upload_firmware: write BLOCK3 to modem failed (%d)!", ret); goto fail_release; } } - dbg("speedtch_firmware_load: BLOCK3 uploaded (%d bytes)", fw2->size); + dbg("speedtch_upload_firmware: BLOCK3 uploaded (%d bytes)", fw2->size); /* USB led static green, ADSL led static red */ /* URB 142 */ - ret = usb_bulk_msg (instance->u.usb_dev, - usb_rcvbulkpipe (instance->u.usb_dev, SPEEDTCH_ENDPOINT_FIRMWARE), - buffer, - 0x200, - &actual_length, - DATA_TIMEOUT); + ret = usb_bulk_msg(usb_dev, usb_rcvbulkpipe(usb_dev, SPEEDTCH_ENDPOINT_FIRMWARE), + buffer, 0x200, &actual_length, DATA_TIMEOUT); if (ret < 0) { - dbg("speedtch_firmware_load: read BLOCK4 from modem failed (%d)!", ret); + dbg("speedtch_upload_firmware: read BLOCK4 from modem failed (%d)!", ret); goto fail_release; } /* success */ - dbg("speedtch_firmware_load: BLOCK4 downloaded (%d bytes)", actual_length); + dbg("speedtch_upload_firmware: BLOCK4 downloaded (%d bytes)", actual_length); /* Delay to allow firmware to start up. We can do this here because we're in our own kernel thread anyway. */ @@ -634,69 +578,112 @@ static void speedtch_firmware_load(const struct firmware *fw1, void *context) speedtch_got_firmware(instance, 1); - goto fail_fw2; /* The got_firmware(0) is a NOP anyway */ + free_page((unsigned long)buffer); + return; fail_release: - /* We only release interface #2 if loading the firmware failed; we don't - do it if we succeeded. This prevents the userspace modem_run tool from - trying to load the firmware itself */ - usb_driver_release_interface (&speedtch_usb_driver, intf); - fail_fw2: - release_firmware(fw2); - fail_buf: - kfree (buffer); + /* Only release interface #2 if uploading failed; we don't release it + we succeeded. This prevents the userspace tools from trying to load + the firmware themselves */ + usb_driver_release_interface(&speedtch_usb_driver, intf); + fail_free: + kfree(buffer); fail: - speedtch_got_firmware (instance, 0); + speedtch_got_firmware(instance, 0); +} + +static int speedtch_find_firmware(struct speedtch_instance_data + *instance, int phase, + const struct firmware **fw_p) +{ + char buf[24]; + const u16 bcdDevice = instance->u.usb_dev->descriptor.bcdDevice; + + sprintf(buf, "speedtch-%d.bin.%x.%02x", phase, bcdDevice >> 8, + bcdDevice & 0xff); + dbg("speedtch_find_firmware: looking for %s\n", buf); + + if (!request_firmware(fw_p, buf, &instance->u.usb_dev->dev)) + return 0; + + sprintf(buf, "speedtch-%d.bin.%x", phase, bcdDevice >> 8); + dbg("speedtch_find_firmware: looking for %s\n", buf); + + if (!request_firmware(fw_p, buf, &instance->u.usb_dev->dev)) + return 0; + + sprintf(buf, "speedtch-%d.bin", phase); + dbg("speedtch_find_firmware: looking for %s\n", buf); + + if (!request_firmware(fw_p, buf, &instance->u.usb_dev->dev)) + return 0; + + dev_warn(&instance->u.usb_dev->dev, "no stage %d firmware found!", phase); + return -ENOENT; +} + +static int speedtch_load_firmware(void *arg) +{ + const struct firmware *fw1, *fw2; + struct speedtch_instance_data *instance = arg; + + BUG_ON(!instance); + + daemonize("firmware/speedtch"); + + if (!speedtch_find_firmware(instance, 1, &fw1)) { + if (!speedtch_find_firmware(instance, 2, &fw2)) { + speedtch_upload_firmware(instance, fw1, fw2); + release_firmware(fw2); + } + release_firmware(fw1); + } + udsl_put_instance(&instance->u); + return 0; } +#endif /* USE_FW_LOADER */ -static void speedtch_firmware_start (struct speedtch_instance_data *instance) +static void speedtch_firmware_start(struct speedtch_instance_data *instance) { #ifdef USE_FW_LOADER int ret; #endif - dbg ("speedtch_firmware_start"); + dbg("speedtch_firmware_start"); - down (&instance->u.serialize); /* vs self, speedtch_got_firmware */ + down(&instance->u.serialize); /* vs self, speedtch_got_firmware */ if (instance->u.status >= UDSL_LOADING_FIRMWARE) { - up (&instance->u.serialize); + up(&instance->u.serialize); return; } instance->u.status = UDSL_LOADING_FIRMWARE; - up (&instance->u.serialize); + up(&instance->u.serialize); udsl_get_instance(&instance->u); #ifdef USE_FW_LOADER - snprintf(instance->fwname, sizeof(instance->fwname), "speedtch_boot_rev%d.%02x", - instance->revision >> 8, instance->revision & 0xff); - printk("Look for %s\n", instance->fwname); - ret = request_firmware_nowait (THIS_MODULE, - instance->fwname, - &instance->u.usb_dev->dev, - instance, - speedtch_firmware_load); + ret = kernel_thread(speedtch_load_firmware, instance, + CLONE_FS | CLONE_FILES); if (ret >= 0) - return; /* OK */ - - dbg ("speedtch_firmware_start: request_firmware_nowait failed (%d)!", ret); + return; /* OK */ + dbg("speedtch_firmware_start: kernel_thread failed (%d)!", ret); /* Just pretend it never happened... hope modem_run happens */ -#endif /* USE_FW_LOADER */ - speedtch_got_firmware (instance, 0); +#endif /* USE_FW_LOADER */ + speedtch_got_firmware(instance, 0); udsl_put_instance(&instance->u); } -static int speedtch_firmware_wait (struct udsl_instance_data *instance) +static int speedtch_firmware_wait(struct udsl_instance_data *instance) { - speedtch_firmware_start ((void *) instance); + speedtch_firmware_start((void *)instance); - if (wait_event_interruptible (instance->firmware_waiters, instance->status != UDSL_LOADING_FIRMWARE) < 0) + if (wait_event_interruptible(instance->firmware_waiters, instance->status != UDSL_LOADING_FIRMWARE) < 0) return -ERESTARTSYS; return (instance->status == UDSL_LOADED_FIRMWARE) ? 0 : -EAGAIN; @@ -706,21 +693,22 @@ static int speedtch_firmware_wait (struct udsl_instance_data *instance) ** USB ** **********/ -static int speedtch_usb_ioctl (struct usb_interface *intf, unsigned int code, void *user_data) +static int speedtch_usb_ioctl(struct usb_interface *intf, unsigned int code, + void *user_data) { - struct speedtch_instance_data *instance = usb_get_intfdata (intf); + struct speedtch_instance_data *instance = usb_get_intfdata(intf); - dbg ("speedtch_usb_ioctl entered"); + dbg("speedtch_usb_ioctl entered"); if (!instance) { - dbg ("speedtch_usb_ioctl: NULL instance!"); + dbg("speedtch_usb_ioctl: NULL instance!"); return -ENODEV; } switch (code) { case UDSL_IOCTL_LINE_UP: instance->u.atm_dev->signal = ATM_PHY_SIG_FOUND; - speedtch_got_firmware (instance, 1); + speedtch_got_firmware(instance, 1); return (instance->u.status == UDSL_LOADED_FIRMWARE) ? 0 : -EIO; case UDSL_IOCTL_LINE_DOWN: instance->u.atm_dev->signal = ATM_PHY_SIG_LOST; @@ -730,37 +718,38 @@ static int speedtch_usb_ioctl (struct usb_interface *intf, unsigned int code, vo } } -static int speedtch_usb_probe (struct usb_interface *intf, const struct usb_device_id *id) +static int speedtch_usb_probe(struct usb_interface *intf, + const struct usb_device_id *id) { struct usb_device *dev = interface_to_usbdev(intf); int ifnum = intf->altsetting->desc.bInterfaceNumber; struct speedtch_instance_data *instance; - unsigned char mac_str [13]; + unsigned char mac_str[13]; int ret, i; char buf7[SIZE_7]; - dbg ("speedtch_usb_probe: trying device with vendor=0x%x, product=0x%x, ifnum %d", - dev->descriptor.idVendor, dev->descriptor.idProduct, ifnum); + dbg("speedtch_usb_probe: trying device with vendor=0x%x, product=0x%x, ifnum %d", dev->descriptor.idVendor, dev->descriptor.idProduct, ifnum); if ((dev->descriptor.bDeviceClass != USB_CLASS_VENDOR_SPEC) || (dev->descriptor.idVendor != SPEEDTOUCH_VENDORID) || (dev->descriptor.idProduct != SPEEDTOUCH_PRODUCTID) || (ifnum != 1)) return -ENODEV; - dbg ("speedtch_usb_probe: device accepted"); + dbg("speedtch_usb_probe: device accepted"); /* instance init */ - if (!(instance = kmalloc (sizeof (struct speedtch_instance_data), GFP_KERNEL))) { - dbg ("speedtch_usb_probe: no memory for instance data!"); + instance = kmalloc(sizeof(*instance), GFP_KERNEL); + if (!instance) { + dbg("speedtch_usb_probe: no memory for instance data!"); return -ENOMEM; } - memset (instance, 0, sizeof (struct speedtch_instance_data)); + memset(instance, 0, sizeof(struct speedtch_instance_data)); - if ((ret = usb_set_interface (dev, 0, 0)) < 0) + if ((ret = usb_set_interface(dev, 0, 0)) < 0) goto fail; - if ((ret = usb_set_interface (dev, 2, 0)) < 0) + if ((ret = usb_set_interface(dev, 2, 0)) < 0) goto fail; instance->u.data_endpoint = SPEEDTCH_ENDPOINT_DATA; @@ -777,60 +766,50 @@ static int speedtch_usb_probe (struct usb_interface *intf, const struct usb_devi INIT_WORK(&instance->poll_work, (void *)speedtch_poll_status, instance); - switch (dev->descriptor.bcdDevice) { - case 0x0000: - case 0x0200: - case 0x0400: - case 0x0401: - instance->revision = dev->descriptor.bcdDevice; - break; - default: - instance->revision = 0x200; - printk(KERN_INFO "Unexpected SpeedTouch revision %04x, treating as Rev 2.00.\n", - dev->descriptor.bcdDevice); - break; - } - /* set MAC address, it is stored in the serial number */ - memset (instance->u.atm_dev->esi, 0, sizeof (instance->u.atm_dev->esi)); - if (usb_string (dev, dev->descriptor.iSerialNumber, mac_str, sizeof (mac_str)) == 12) + memset(instance->u.atm_dev->esi, 0, sizeof(instance->u.atm_dev->esi)); + if (usb_string(dev, dev->descriptor.iSerialNumber, mac_str, sizeof(mac_str)) == 12) { for (i = 0; i < 6; i++) - instance->u.atm_dev->esi [i] = (hex2int (mac_str [i * 2]) * 16) + (hex2int (mac_str [i * 2 + 1])); - + instance->u.atm_dev->esi[i] = + (hex2int(mac_str[i * 2]) * 16) + (hex2int(mac_str[i * 2 + 1])); + } /* First check whether the modem already seems to be alive */ ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), - 0x12, 0xc0, 0x07, 0x00, buf7, SIZE_7, HZ/2); + 0x12, 0xc0, 0x07, 0x00, buf7, SIZE_7, HZ / 2); if (ret == SIZE_7) { dbg("firmware appears to be already loaded"); speedtch_got_firmware(instance, 1); speedtch_poll_status(instance); } else { - speedtch_firmware_start (instance); + speedtch_firmware_start(instance); } - usb_set_intfdata (intf, instance); + usb_set_intfdata(intf, instance); return 0; -fail: - kfree (instance); + fail: + kfree(instance); return -ENOMEM; } -static void speedtch_usb_disconnect (struct usb_interface *intf) +static void speedtch_usb_disconnect(struct usb_interface *intf) { - struct speedtch_instance_data *instance = usb_get_intfdata (intf); + struct speedtch_instance_data *instance = usb_get_intfdata(intf); - dbg ("speedtch_usb_disconnect entered"); + dbg("speedtch_usb_disconnect entered"); if (!instance) { - dbg ("speedtch_usb_disconnect: NULL instance!"); + dbg("speedtch_usb_disconnect: NULL instance!"); return; } +/*QQ need to handle disconnects on interface #2 while uploading firmware */ +/*QQ and what about interface #1? */ + if (instance->int_urb) { struct urb *int_urb = instance->int_urb; instance->int_urb = NULL; @@ -847,33 +826,32 @@ static void speedtch_usb_disconnect (struct usb_interface *intf) udsl_instance_disconnect(&instance->u); /* clean up */ - usb_set_intfdata (intf, NULL); + usb_set_intfdata(intf, NULL); udsl_put_instance(&instance->u); } - /*********** ** init ** ***********/ -static int __init speedtch_usb_init (void) +static int __init speedtch_usb_init(void) { - dbg ("speedtch_usb_init: driver version " DRIVER_VERSION); + dbg("speedtch_usb_init: driver version " DRIVER_VERSION); - return usb_register (&speedtch_usb_driver); + return usb_register(&speedtch_usb_driver); } -static void __exit speedtch_usb_cleanup (void) +static void __exit speedtch_usb_cleanup(void) { - dbg ("speedtch_usb_cleanup entered"); + dbg("speedtch_usb_cleanup entered"); - usb_deregister (&speedtch_usb_driver); + usb_deregister(&speedtch_usb_driver); } -module_init (speedtch_usb_init); -module_exit (speedtch_usb_cleanup); +module_init(speedtch_usb_init); +module_exit(speedtch_usb_cleanup); -MODULE_AUTHOR (DRIVER_AUTHOR); -MODULE_DESCRIPTION (DRIVER_DESC); -MODULE_LICENSE ("GPL"); -MODULE_VERSION (DRIVER_VERSION); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRIVER_VERSION); diff --git a/drivers/usb/atm/usb_atm.c b/drivers/usb/atm/usb_atm.c index 9a792ab71e49..486458d86fe1 100644 --- a/drivers/usb/atm/usb_atm.c +++ b/drivers/usb/atm/usb_atm.c @@ -62,7 +62,6 @@ * */ - #include #include #include @@ -96,13 +95,13 @@ #include #ifdef DEBUG -#define DEBUG_ON(x) BUG_ON(x) +#define UDSL_ASSERT(x) BUG_ON(x) #else -#define DEBUG_ON(x) do { if (x); } while (0) +#define UDSL_ASSERT(x) #endif #ifdef VERBOSE_DEBUG -static int udsl_print_packet (const unsigned char *data, int len); +static int udsl_print_packet(const unsigned char *data, int len); #define PACKETDEBUG(arg...) udsl_print_packet (arg) #define vdbg(arg...) dbg (arg) #else @@ -121,72 +120,90 @@ static unsigned int num_snd_bufs = UDSL_DEFAULT_SND_BUFS; static unsigned int rcv_buf_size = UDSL_DEFAULT_RCV_BUF_SIZE; static unsigned int snd_buf_size = UDSL_DEFAULT_SND_BUF_SIZE; -module_param (num_rcv_urbs, uint, 0444); -MODULE_PARM_DESC (num_rcv_urbs, "Number of urbs used for reception (range: 0-" __MODULE_STRING (UDSL_MAX_RCV_URBS) ", default: " __MODULE_STRING (UDSL_DEFAULT_RCV_URBS) ")"); - -module_param (num_snd_urbs, uint, 0444); -MODULE_PARM_DESC (num_snd_urbs, "Number of urbs used for transmission (range: 0-" __MODULE_STRING (UDSL_MAX_SND_URBS) ", default: " __MODULE_STRING (UDSL_DEFAULT_SND_URBS) ")"); - -module_param (num_rcv_bufs, uint, 0444); -MODULE_PARM_DESC (num_rcv_bufs, "Number of buffers used for reception (range: 0-" __MODULE_STRING (UDSL_MAX_RCV_BUFS) ", default: " __MODULE_STRING (UDSL_DEFAULT_RCV_BUFS) ")"); - -module_param (num_snd_bufs, uint, 0444); -MODULE_PARM_DESC (num_snd_bufs, "Number of buffers used for transmission (range: 0-" __MODULE_STRING (UDSL_MAX_SND_BUFS) ", default: " __MODULE_STRING (UDSL_DEFAULT_SND_BUFS) ")"); - -module_param (rcv_buf_size, uint, 0444); -MODULE_PARM_DESC (rcv_buf_size, "Size of the buffers used for reception (range: 0-" __MODULE_STRING (UDSL_MAX_RCV_BUF_SIZE) ", default: " __MODULE_STRING (UDSL_DEFAULT_RCV_BUF_SIZE) ")"); - -module_param (snd_buf_size, uint, 0444); -MODULE_PARM_DESC (snd_buf_size, "Size of the buffers used for transmission (range: 0-" __MODULE_STRING (UDSL_MAX_SND_BUF_SIZE) ", default: " __MODULE_STRING (UDSL_DEFAULT_SND_BUF_SIZE) ")"); +module_param(num_rcv_urbs, uint, 0444); +MODULE_PARM_DESC(num_rcv_urbs, + "Number of urbs used for reception (range: 0-" + __MODULE_STRING(UDSL_MAX_RCV_URBS) ", default: " + __MODULE_STRING(UDSL_DEFAULT_RCV_URBS) ")"); + +module_param(num_snd_urbs, uint, 0444); +MODULE_PARM_DESC(num_snd_urbs, + "Number of urbs used for transmission (range: 0-" + __MODULE_STRING(UDSL_MAX_SND_URBS) ", default: " + __MODULE_STRING(UDSL_DEFAULT_SND_URBS) ")"); + +module_param(num_rcv_bufs, uint, 0444); +MODULE_PARM_DESC(num_rcv_bufs, + "Number of buffers used for reception (range: 0-" + __MODULE_STRING(UDSL_MAX_RCV_BUFS) ", default: " + __MODULE_STRING(UDSL_DEFAULT_RCV_BUFS) ")"); + +module_param(num_snd_bufs, uint, 0444); +MODULE_PARM_DESC(num_snd_bufs, + "Number of buffers used for transmission (range: 0-" + __MODULE_STRING(UDSL_MAX_SND_BUFS) ", default: " + __MODULE_STRING(UDSL_DEFAULT_SND_BUFS) ")"); + +module_param(rcv_buf_size, uint, 0444); +MODULE_PARM_DESC(rcv_buf_size, + "Size of the buffers used for reception (range: 0-" + __MODULE_STRING(UDSL_MAX_RCV_BUF_SIZE) ", default: " + __MODULE_STRING(UDSL_DEFAULT_RCV_BUF_SIZE) ")"); + +module_param(snd_buf_size, uint, 0444); +MODULE_PARM_DESC(snd_buf_size, + "Size of the buffers used for transmission (range: 0-" + __MODULE_STRING(UDSL_MAX_SND_BUF_SIZE) ", default: " + __MODULE_STRING(UDSL_DEFAULT_SND_BUF_SIZE) ")"); /* ATM */ -static void udsl_atm_dev_close (struct atm_dev *dev); -static int udsl_atm_open (struct atm_vcc *vcc); -static void udsl_atm_close (struct atm_vcc *vcc); -static int udsl_atm_ioctl (struct atm_dev *dev, unsigned int cmd, void __user *arg); -static int udsl_atm_send (struct atm_vcc *vcc, struct sk_buff *skb); -static int udsl_atm_proc_read (struct atm_dev *atm_dev, loff_t *pos, char *page); +static void udsl_atm_dev_close(struct atm_dev *dev); +static int udsl_atm_open(struct atm_vcc *vcc); +static void udsl_atm_close(struct atm_vcc *vcc); +static int udsl_atm_ioctl(struct atm_dev *dev, unsigned int cmd, void __user * arg); +static int udsl_atm_send(struct atm_vcc *vcc, struct sk_buff *skb); +static int udsl_atm_proc_read(struct atm_dev *atm_dev, loff_t * pos, char *page); static struct atmdev_ops udsl_atm_devops = { - .dev_close = udsl_atm_dev_close, - .open = udsl_atm_open, - .close = udsl_atm_close, - .ioctl = udsl_atm_ioctl, - .send = udsl_atm_send, - .proc_read = udsl_atm_proc_read, - .owner = THIS_MODULE, + .dev_close = udsl_atm_dev_close, + .open = udsl_atm_open, + .close = udsl_atm_close, + .ioctl = udsl_atm_ioctl, + .send = udsl_atm_send, + .proc_read = udsl_atm_proc_read, + .owner = THIS_MODULE, }; - /*********** ** misc ** ***********/ -static inline void udsl_pop (struct atm_vcc *vcc, struct sk_buff *skb) +static inline void udsl_pop(struct atm_vcc *vcc, struct sk_buff *skb) { if (vcc->pop) - vcc->pop (vcc, skb); + vcc->pop(vcc, skb); else - dev_kfree_skb (skb); + dev_kfree_skb(skb); } - /************* ** decode ** *************/ -static inline struct udsl_vcc_data *udsl_find_vcc (struct udsl_instance_data *instance, short vpi, int vci) +static inline struct udsl_vcc_data *udsl_find_vcc(struct udsl_instance_data *instance, + short vpi, int vci) { struct udsl_vcc_data *vcc; - list_for_each_entry (vcc, &instance->vcc_list, list) + list_for_each_entry(vcc, &instance->vcc_list, list) if ((vcc->vci == vci) && (vcc->vpi == vpi)) return vcc; return NULL; } -static void udsl_extract_cells (struct udsl_instance_data *instance, unsigned char *source, unsigned int howmany) +static void udsl_extract_cells(struct udsl_instance_data *instance, + unsigned char *source, unsigned int howmany) { struct udsl_vcc_data *cached_vcc = NULL; struct atm_vcc *vcc; @@ -199,21 +216,22 @@ static void udsl_extract_cells (struct udsl_instance_data *instance, unsigned ch short cached_vpi = 0; short vpi; - for (i = 0; i < howmany; i++, source += ATM_CELL_SIZE + instance->rcv_padding) { - vpi = ((source [0] & 0x0f) << 4) | (source [1] >> 4); - vci = ((source [1] & 0x0f) << 12) | (source [2] << 4) | (source [3] >> 4); - pti = (source [3] & 0x2) != 0; + for (i = 0; i < howmany; + i++, source += ATM_CELL_SIZE + instance->rcv_padding) { + vpi = ((source[0] & 0x0f) << 4) | (source[1] >> 4); + vci = ((source[1] & 0x0f) << 12) | (source[2] << 4) | (source[3] >> 4); + pti = (source[3] & 0x2) != 0; - vdbg ("udsl_extract_cells: vpi %hd, vci %d, pti %d", vpi, vci, pti); + vdbg("udsl_extract_cells: vpi %hd, vci %d, pti %d", vpi, vci, pti); if (cached_vcc && (vci == cached_vci) && (vpi == cached_vpi)) vcc_data = cached_vcc; - else if ((vcc_data = udsl_find_vcc (instance, vpi, vci))) { + else if ((vcc_data = udsl_find_vcc(instance, vpi, vci))) { cached_vcc = vcc_data; cached_vpi = vpi; cached_vci = vci; } else { - dbg ("udsl_extract_cells: unknown vpi/vci (%hd/%d)!", vpi, vci); + dbg("udsl_extract_cells: unknown vpi/vci (%hd/%d)!", vpi, vci); continue; } @@ -221,95 +239,94 @@ static void udsl_extract_cells (struct udsl_instance_data *instance, unsigned ch sarb = vcc_data->sarb; if (sarb->tail + ATM_CELL_PAYLOAD > sarb->end) { - dbg ("udsl_extract_cells: buffer overrun (sarb->len %u, vcc: 0x%p)!", sarb->len, vcc); + dbg("udsl_extract_cells: buffer overrun (sarb->len %u, vcc: 0x%p)!", sarb->len, vcc); /* discard cells already received */ - skb_trim (sarb, 0); + skb_trim(sarb, 0); } - memcpy (sarb->tail, source + ATM_CELL_HEADER, ATM_CELL_PAYLOAD); - __skb_put (sarb, ATM_CELL_PAYLOAD); + memcpy(sarb->tail, source + ATM_CELL_HEADER, ATM_CELL_PAYLOAD); + __skb_put(sarb, ATM_CELL_PAYLOAD); if (pti) { struct sk_buff *skb; unsigned int length; unsigned int pdu_length; - length = (source [ATM_CELL_SIZE - 6] << 8) + source [ATM_CELL_SIZE - 5]; + length = (source[ATM_CELL_SIZE - 6] << 8) + source[ATM_CELL_SIZE - 5]; /* guard against overflow */ if (length > ATM_MAX_AAL5_PDU) { - dbg ("udsl_extract_cells: bogus length %u (vcc: 0x%p)!", length, vcc); - atomic_inc (&vcc->stats->rx_err); + dbg("udsl_extract_cells: bogus length %u (vcc: 0x%p)!", length, vcc); + atomic_inc(&vcc->stats->rx_err); goto out; } - pdu_length = UDSL_NUM_CELLS (length) * ATM_CELL_PAYLOAD; + pdu_length = UDSL_NUM_CELLS(length) * ATM_CELL_PAYLOAD; if (sarb->len < pdu_length) { - dbg ("udsl_extract_cells: bogus pdu_length %u (sarb->len: %u, vcc: 0x%p)!", pdu_length, sarb->len, vcc); - atomic_inc (&vcc->stats->rx_err); + dbg("udsl_extract_cells: bogus pdu_length %u (sarb->len: %u, vcc: 0x%p)!", pdu_length, sarb->len, vcc); + atomic_inc(&vcc->stats->rx_err); goto out; } - if (crc32_be (~0, sarb->tail - pdu_length, pdu_length) != 0xc704dd7b) { - dbg ("udsl_extract_cells: packet failed crc check (vcc: 0x%p)!", vcc); - atomic_inc (&vcc->stats->rx_err); + if (crc32_be(~0, sarb->tail - pdu_length, pdu_length) != 0xc704dd7b) { + dbg("udsl_extract_cells: packet failed crc check (vcc: 0x%p)!", vcc); + atomic_inc(&vcc->stats->rx_err); goto out; } - vdbg ("udsl_extract_cells: got packet (length: %u, pdu_length: %u, vcc: 0x%p)", length, pdu_length, vcc); + vdbg("udsl_extract_cells: got packet (length: %u, pdu_length: %u, vcc: 0x%p)", length, pdu_length, vcc); - if (!(skb = dev_alloc_skb (length))) { - dbg ("udsl_extract_cells: no memory for skb (length: %u)!", length); - atomic_inc (&vcc->stats->rx_drop); + if (!(skb = dev_alloc_skb(length))) { + dbg("udsl_extract_cells: no memory for skb (length: %u)!", length); + atomic_inc(&vcc->stats->rx_drop); goto out; } - vdbg ("udsl_extract_cells: allocated new sk_buff (skb: 0x%p, skb->truesize: %u)", skb, skb->truesize); + vdbg("udsl_extract_cells: allocated new sk_buff (skb: 0x%p, skb->truesize: %u)", skb, skb->truesize); - if (!atm_charge (vcc, skb->truesize)) { - dbg ("udsl_extract_cells: failed atm_charge (skb->truesize: %u)!", skb->truesize); - dev_kfree_skb (skb); - goto out; /* atm_charge increments rx_drop */ + if (!atm_charge(vcc, skb->truesize)) { + dbg("udsl_extract_cells: failed atm_charge (skb->truesize: %u)!", skb->truesize); + dev_kfree_skb(skb); + goto out; /* atm_charge increments rx_drop */ } - memcpy (skb->data, sarb->tail - pdu_length, length); - __skb_put (skb, length); + memcpy(skb->data, sarb->tail - pdu_length, length); + __skb_put(skb, length); - vdbg ("udsl_extract_cells: sending skb 0x%p, skb->len %u, skb->truesize %u", skb, skb->len, skb->truesize); + vdbg("udsl_extract_cells: sending skb 0x%p, skb->len %u, skb->truesize %u", skb, skb->len, skb->truesize); - PACKETDEBUG (skb->data, skb->len); + PACKETDEBUG(skb->data, skb->len); - vcc->push (vcc, skb); + vcc->push(vcc, skb); - atomic_inc (&vcc->stats->rx); -out: - skb_trim (sarb, 0); + atomic_inc(&vcc->stats->rx); + out: + skb_trim(sarb, 0); } } } - /************* ** encode ** *************/ -static const unsigned char zeros [ATM_CELL_PAYLOAD]; +static const unsigned char zeros[ATM_CELL_PAYLOAD]; -static void udsl_groom_skb (struct atm_vcc *vcc, struct sk_buff *skb) +static void udsl_groom_skb(struct atm_vcc *vcc, struct sk_buff *skb) { - struct udsl_control *ctrl = UDSL_SKB (skb); + struct udsl_control *ctrl = UDSL_SKB(skb); unsigned int zero_padding; u32 crc; ctrl->atm_data.vcc = vcc; - ctrl->cell_header [0] = vcc->vpi >> 4; - ctrl->cell_header [1] = (vcc->vpi << 4) | (vcc->vci >> 12); - ctrl->cell_header [2] = vcc->vci >> 4; - ctrl->cell_header [3] = vcc->vci << 4; - ctrl->cell_header [4] = 0xec; + ctrl->cell_header[0] = vcc->vpi >> 4; + ctrl->cell_header[1] = (vcc->vpi << 4) | (vcc->vci >> 12); + ctrl->cell_header[2] = vcc->vci >> 4; + ctrl->cell_header[3] = vcc->vci << 4; + ctrl->cell_header[4] = 0xec; - ctrl->num_cells = UDSL_NUM_CELLS (skb->len); + ctrl->num_cells = UDSL_NUM_CELLS(skb->len); ctrl->num_entire = skb->len / ATM_CELL_PAYLOAD; zero_padding = ctrl->num_cells * ATM_CELL_PAYLOAD - skb->len - ATM_AAL5_TRAILER; @@ -319,44 +336,45 @@ static void udsl_groom_skb (struct atm_vcc *vcc, struct sk_buff *skb) else ctrl->pdu_padding = zero_padding; - ctrl->aal5_trailer [0] = 0; /* UU = 0 */ - ctrl->aal5_trailer [1] = 0; /* CPI = 0 */ - ctrl->aal5_trailer [2] = skb->len >> 8; - ctrl->aal5_trailer [3] = skb->len; + ctrl->aal5_trailer[0] = 0; /* UU = 0 */ + ctrl->aal5_trailer[1] = 0; /* CPI = 0 */ + ctrl->aal5_trailer[2] = skb->len >> 8; + ctrl->aal5_trailer[3] = skb->len; - crc = crc32_be (~0, skb->data, skb->len); - crc = crc32_be (crc, zeros, zero_padding); - crc = crc32_be (crc, ctrl->aal5_trailer, 4); + crc = crc32_be(~0, skb->data, skb->len); + crc = crc32_be(crc, zeros, zero_padding); + crc = crc32_be(crc, ctrl->aal5_trailer, 4); crc = ~crc; - ctrl->aal5_trailer [4] = crc >> 24; - ctrl->aal5_trailer [5] = crc >> 16; - ctrl->aal5_trailer [6] = crc >> 8; - ctrl->aal5_trailer [7] = crc; + ctrl->aal5_trailer[4] = crc >> 24; + ctrl->aal5_trailer[5] = crc >> 16; + ctrl->aal5_trailer[6] = crc >> 8; + ctrl->aal5_trailer[7] = crc; } -static unsigned int udsl_write_cells(struct udsl_instance_data *instance, unsigned int howmany, - struct sk_buff *skb, unsigned char **target_p) +static unsigned int udsl_write_cells(struct udsl_instance_data *instance, + unsigned int howmany, struct sk_buff *skb, + unsigned char **target_p) { - struct udsl_control *ctrl = UDSL_SKB (skb); + struct udsl_control *ctrl = UDSL_SKB(skb); unsigned char *target = *target_p; unsigned int nc, ne, i; - vdbg ("udsl_write_cells: howmany=%u, skb->len=%d, num_cells=%u, num_entire=%u, pdu_padding=%u", howmany, skb->len, ctrl->num_cells, ctrl->num_entire, ctrl->pdu_padding); + vdbg("udsl_write_cells: howmany=%u, skb->len=%d, num_cells=%u, num_entire=%u, pdu_padding=%u", howmany, skb->len, ctrl->num_cells, ctrl->num_entire, ctrl->pdu_padding); nc = ctrl->num_cells; - ne = min (howmany, ctrl->num_entire); + ne = min(howmany, ctrl->num_entire); for (i = 0; i < ne; i++) { - memcpy (target, ctrl->cell_header, ATM_CELL_HEADER); + memcpy(target, ctrl->cell_header, ATM_CELL_HEADER); target += ATM_CELL_HEADER; - memcpy (target, skb->data, ATM_CELL_PAYLOAD); + memcpy(target, skb->data, ATM_CELL_PAYLOAD); target += ATM_CELL_PAYLOAD; if (instance->snd_padding) { - memset (target, 0, instance->snd_padding); + memset(target, 0, instance->snd_padding); target += instance->snd_padding; } - __skb_pull (skb, ATM_CELL_PAYLOAD); + __skb_pull(skb, ATM_CELL_PAYLOAD); } ctrl->num_entire -= ne; @@ -365,15 +383,15 @@ static unsigned int udsl_write_cells(struct udsl_instance_data *instance, unsign goto out; if (instance->snd_padding) { - memset (target, 0, instance->snd_padding); + memset(target, 0, instance->snd_padding); target += instance->snd_padding; } - memcpy (target, ctrl->cell_header, ATM_CELL_HEADER); + memcpy(target, ctrl->cell_header, ATM_CELL_HEADER); target += ATM_CELL_HEADER; - memcpy (target, skb->data, skb->len); + memcpy(target, skb->data, skb->len); target += skb->len; - __skb_pull (skb, skb->len); - memset (target, 0, ctrl->pdu_padding); + __skb_pull(skb, skb->len); + memset(target, 0, ctrl->pdu_padding); target += ctrl->pdu_padding; if (--ctrl->num_cells) { @@ -382,33 +400,33 @@ static unsigned int udsl_write_cells(struct udsl_instance_data *instance, unsign goto out; } - memcpy (target, ctrl->cell_header, ATM_CELL_HEADER); + memcpy(target, ctrl->cell_header, ATM_CELL_HEADER); target += ATM_CELL_HEADER; - memset (target, 0, ATM_CELL_PAYLOAD - ATM_AAL5_TRAILER); + memset(target, 0, ATM_CELL_PAYLOAD - ATM_AAL5_TRAILER); target += ATM_CELL_PAYLOAD - ATM_AAL5_TRAILER; - DEBUG_ON (--ctrl->num_cells); + --ctrl->num_cells; + UDSL_ASSERT(ctrl->num_cells); } - memcpy (target, ctrl->aal5_trailer, ATM_AAL5_TRAILER); + memcpy(target, ctrl->aal5_trailer, ATM_AAL5_TRAILER); target += ATM_AAL5_TRAILER; /* set pti bit in last cell */ *(target + 3 - ATM_CELL_SIZE) |= 0x2; if (instance->snd_padding) { - memset (target, 0, instance->snd_padding); + memset(target, 0, instance->snd_padding); target += instance->snd_padding; } -out: + out: *target_p = target; return nc - ctrl->num_cells; } - /************** ** receive ** **************/ -static void udsl_complete_receive (struct urb *urb, struct pt_regs *regs) +static void udsl_complete_receive(struct urb *urb, struct pt_regs *regs) { struct udsl_receive_buffer *buf; struct udsl_instance_data *instance; @@ -416,7 +434,7 @@ static void udsl_complete_receive (struct urb *urb, struct pt_regs *regs) unsigned long flags; if (!urb || !(rcv = urb->context)) { - dbg ("udsl_complete_receive: bad urb!"); + dbg("udsl_complete_receive: bad urb!"); return; } @@ -425,165 +443,172 @@ static void udsl_complete_receive (struct urb *urb, struct pt_regs *regs) buf->filled_cells = urb->actual_length / (ATM_CELL_SIZE + instance->rcv_padding); - vdbg ("udsl_complete_receive: urb 0x%p, status %d, actual_length %d, filled_cells %u, rcv 0x%p, buf 0x%p", urb, urb->status, urb->actual_length, buf->filled_cells, rcv, buf); + vdbg("udsl_complete_receive: urb 0x%p, status %d, actual_length %d, filled_cells %u, rcv 0x%p, buf 0x%p", urb, urb->status, urb->actual_length, buf->filled_cells, rcv, buf); - DEBUG_ON (buf->filled_cells > rcv_buf_size); + UDSL_ASSERT(buf->filled_cells > rcv_buf_size); /* may not be in_interrupt() */ - spin_lock_irqsave (&instance->receive_lock, flags); - list_add (&rcv->list, &instance->spare_receivers); - list_add_tail (&buf->list, &instance->filled_receive_buffers); - if (likely (!urb->status)) - tasklet_schedule (&instance->receive_tasklet); - spin_unlock_irqrestore (&instance->receive_lock, flags); + spin_lock_irqsave(&instance->receive_lock, flags); + list_add(&rcv->list, &instance->spare_receivers); + list_add_tail(&buf->list, &instance->filled_receive_buffers); + if (likely(!urb->status)) + tasklet_schedule(&instance->receive_tasklet); + spin_unlock_irqrestore(&instance->receive_lock, flags); } -static void udsl_process_receive (unsigned long data) +static void udsl_process_receive(unsigned long data) { struct udsl_receive_buffer *buf; - struct udsl_instance_data *instance = (struct udsl_instance_data *) data; + struct udsl_instance_data *instance = (struct udsl_instance_data *)data; struct udsl_receiver *rcv; int err; -made_progress: - while (!list_empty (&instance->spare_receive_buffers)) { - spin_lock_irq (&instance->receive_lock); - if (list_empty (&instance->spare_receivers)) { - spin_unlock_irq (&instance->receive_lock); + made_progress: + while (!list_empty(&instance->spare_receive_buffers)) { + spin_lock_irq(&instance->receive_lock); + if (list_empty(&instance->spare_receivers)) { + spin_unlock_irq(&instance->receive_lock); break; } - rcv = list_entry (instance->spare_receivers.next, struct udsl_receiver, list); - list_del (&rcv->list); - spin_unlock_irq (&instance->receive_lock); + rcv = list_entry(instance->spare_receivers.next, + struct udsl_receiver, list); + list_del(&rcv->list); + spin_unlock_irq(&instance->receive_lock); - buf = list_entry (instance->spare_receive_buffers.next, struct udsl_receive_buffer, list); - list_del (&buf->list); + buf = list_entry(instance->spare_receive_buffers.next, + struct udsl_receive_buffer, list); + list_del(&buf->list); rcv->buffer = buf; - usb_fill_bulk_urb (rcv->urb, - instance->usb_dev, - usb_rcvbulkpipe (instance->usb_dev, instance->data_endpoint), - buf->base, - rcv_buf_size * (ATM_CELL_SIZE + instance->rcv_padding), - udsl_complete_receive, - rcv); + usb_fill_bulk_urb(rcv->urb, instance->usb_dev, + usb_rcvbulkpipe(instance->usb_dev, instance->data_endpoint), + buf->base, + rcv_buf_size * (ATM_CELL_SIZE + instance->rcv_padding), + udsl_complete_receive, rcv); - vdbg ("udsl_process_receive: sending urb 0x%p, rcv 0x%p, buf 0x%p", rcv->urb, rcv, buf); + vdbg("udsl_process_receive: sending urb 0x%p, rcv 0x%p, buf 0x%p", + rcv->urb, rcv, buf); if ((err = usb_submit_urb(rcv->urb, GFP_ATOMIC)) < 0) { - dbg ("udsl_process_receive: urb submission failed (%d)!", err); - list_add (&buf->list, &instance->spare_receive_buffers); - spin_lock_irq (&instance->receive_lock); - list_add (&rcv->list, &instance->spare_receivers); - spin_unlock_irq (&instance->receive_lock); + dbg("udsl_process_receive: urb submission failed (%d)!", err); + list_add(&buf->list, &instance->spare_receive_buffers); + spin_lock_irq(&instance->receive_lock); + list_add(&rcv->list, &instance->spare_receivers); + spin_unlock_irq(&instance->receive_lock); break; } } - spin_lock_irq (&instance->receive_lock); - if (list_empty (&instance->filled_receive_buffers)) { - spin_unlock_irq (&instance->receive_lock); - return; /* done - no more buffers */ + spin_lock_irq(&instance->receive_lock); + if (list_empty(&instance->filled_receive_buffers)) { + spin_unlock_irq(&instance->receive_lock); + return; /* done - no more buffers */ } - buf = list_entry (instance->filled_receive_buffers.next, struct udsl_receive_buffer, list); - list_del (&buf->list); - spin_unlock_irq (&instance->receive_lock); - vdbg ("udsl_process_receive: processing buf 0x%p", buf); - udsl_extract_cells (instance, buf->base, buf->filled_cells); - list_add (&buf->list, &instance->spare_receive_buffers); + buf = list_entry(instance->filled_receive_buffers.next, + struct udsl_receive_buffer, list); + list_del(&buf->list); + spin_unlock_irq(&instance->receive_lock); + + vdbg("udsl_process_receive: processing buf 0x%p", buf); + udsl_extract_cells(instance, buf->base, buf->filled_cells); + list_add(&buf->list, &instance->spare_receive_buffers); goto made_progress; } - /*********** ** send ** ***********/ -static void udsl_complete_send (struct urb *urb, struct pt_regs *regs) +static void udsl_complete_send(struct urb *urb, struct pt_regs *regs) { struct udsl_instance_data *instance; struct udsl_sender *snd; unsigned long flags; if (!urb || !(snd = urb->context) || !(instance = snd->instance)) { - dbg ("udsl_complete_send: bad urb!"); + dbg("udsl_complete_send: bad urb!"); return; } - vdbg ("udsl_complete_send: urb 0x%p, status %d, snd 0x%p, buf 0x%p", urb, urb->status, snd, snd->buffer); + vdbg("udsl_complete_send: urb 0x%p, status %d, snd 0x%p, buf 0x%p", urb, + urb->status, snd, snd->buffer); /* may not be in_interrupt() */ - spin_lock_irqsave (&instance->send_lock, flags); - list_add (&snd->list, &instance->spare_senders); - list_add (&snd->buffer->list, &instance->spare_send_buffers); - tasklet_schedule (&instance->send_tasklet); - spin_unlock_irqrestore (&instance->send_lock, flags); + spin_lock_irqsave(&instance->send_lock, flags); + list_add(&snd->list, &instance->spare_senders); + list_add(&snd->buffer->list, &instance->spare_send_buffers); + tasklet_schedule(&instance->send_tasklet); + spin_unlock_irqrestore(&instance->send_lock, flags); } -static void udsl_process_send (unsigned long data) +static void udsl_process_send(unsigned long data) { struct udsl_send_buffer *buf; - struct udsl_instance_data *instance = (struct udsl_instance_data *) data; + struct udsl_instance_data *instance = (struct udsl_instance_data *)data; struct sk_buff *skb; struct udsl_sender *snd; int err; unsigned int num_written; -made_progress: - spin_lock_irq (&instance->send_lock); - while (!list_empty (&instance->spare_senders)) { - if (!list_empty (&instance->filled_send_buffers)) { - buf = list_entry (instance->filled_send_buffers.next, struct udsl_send_buffer, list); - list_del (&buf->list); + made_progress: + spin_lock_irq(&instance->send_lock); + while (!list_empty(&instance->spare_senders)) { + if (!list_empty(&instance->filled_send_buffers)) { + buf = list_entry(instance->filled_send_buffers.next, + struct udsl_send_buffer, list); + list_del(&buf->list); } else if ((buf = instance->current_buffer)) { instance->current_buffer = NULL; - } else /* all buffers empty */ + } else /* all buffers empty */ break; - snd = list_entry (instance->spare_senders.next, struct udsl_sender, list); - list_del (&snd->list); - spin_unlock_irq (&instance->send_lock); + snd = list_entry(instance->spare_senders.next, + struct udsl_sender, list); + list_del(&snd->list); + spin_unlock_irq(&instance->send_lock); snd->buffer = buf; - usb_fill_bulk_urb (snd->urb, - instance->usb_dev, - usb_sndbulkpipe (instance->usb_dev, instance->data_endpoint), - buf->base, - (snd_buf_size - buf->free_cells) * (ATM_CELL_SIZE + instance->snd_padding), - udsl_complete_send, - snd); + usb_fill_bulk_urb(snd->urb, instance->usb_dev, + usb_sndbulkpipe(instance->usb_dev, instance->data_endpoint), + buf->base, + (snd_buf_size - buf->free_cells) * (ATM_CELL_SIZE + instance->snd_padding), + udsl_complete_send, snd); - vdbg ("udsl_process_send: submitting urb 0x%p (%d cells), snd 0x%p, buf 0x%p", snd->urb, snd_buf_size - buf->free_cells, snd, buf); + vdbg("udsl_process_send: submitting urb 0x%p (%d cells), snd 0x%p, buf 0x%p", + snd->urb, snd_buf_size - buf->free_cells, snd, buf); if ((err = usb_submit_urb(snd->urb, GFP_ATOMIC)) < 0) { - dbg ("udsl_process_send: urb submission failed (%d)!", err); - spin_lock_irq (&instance->send_lock); - list_add (&snd->list, &instance->spare_senders); - spin_unlock_irq (&instance->send_lock); - list_add (&buf->list, &instance->filled_send_buffers); - return; /* bail out */ + dbg("udsl_process_send: urb submission failed (%d)!", err); + spin_lock_irq(&instance->send_lock); + list_add(&snd->list, &instance->spare_senders); + spin_unlock_irq(&instance->send_lock); + list_add(&buf->list, &instance->filled_send_buffers); + return; /* bail out */ } - spin_lock_irq (&instance->send_lock); - } /* while */ - spin_unlock_irq (&instance->send_lock); + spin_lock_irq(&instance->send_lock); + } /* while */ + spin_unlock_irq(&instance->send_lock); - if (!instance->current_skb && !(instance->current_skb = skb_dequeue (&instance->sndqueue))) - return; /* done - no more skbs */ + if (!instance->current_skb) + instance->current_skb = skb_dequeue(&instance->sndqueue); + if (!instance->current_skb) + return; /* done - no more skbs */ skb = instance->current_skb; if (!(buf = instance->current_buffer)) { - spin_lock_irq (&instance->send_lock); - if (list_empty (&instance->spare_send_buffers)) { + spin_lock_irq(&instance->send_lock); + if (list_empty(&instance->spare_send_buffers)) { instance->current_buffer = NULL; - spin_unlock_irq (&instance->send_lock); - return; /* done - no more buffers */ + spin_unlock_irq(&instance->send_lock); + return; /* done - no more buffers */ } - buf = list_entry (instance->spare_send_buffers.next, struct udsl_send_buffer, list); - list_del (&buf->list); - spin_unlock_irq (&instance->send_lock); + buf = list_entry(instance->spare_send_buffers.next, + struct udsl_send_buffer, list); + list_del(&buf->list); + spin_unlock_irq(&instance->send_lock); buf->free_start = buf->base; buf->free_cells = snd_buf_size; @@ -593,186 +618,191 @@ made_progress: num_written = udsl_write_cells(instance, buf->free_cells, skb, &buf->free_start); - vdbg ("udsl_process_send: wrote %u cells from skb 0x%p to buffer 0x%p", num_written, skb, buf); + vdbg("udsl_process_send: wrote %u cells from skb 0x%p to buffer 0x%p", + num_written, skb, buf); if (!(buf->free_cells -= num_written)) { - list_add_tail (&buf->list, &instance->filled_send_buffers); + list_add_tail(&buf->list, &instance->filled_send_buffers); instance->current_buffer = NULL; } - vdbg ("udsl_process_send: buffer contains %d cells, %d left", snd_buf_size - buf->free_cells, buf->free_cells); + vdbg("udsl_process_send: buffer contains %d cells, %d left", + snd_buf_size - buf->free_cells, buf->free_cells); - if (!UDSL_SKB (skb)->num_cells) { - struct atm_vcc *vcc = UDSL_SKB (skb)->atm_data.vcc; + if (!UDSL_SKB(skb)->num_cells) { + struct atm_vcc *vcc = UDSL_SKB(skb)->atm_data.vcc; - udsl_pop (vcc, skb); + udsl_pop(vcc, skb); instance->current_skb = NULL; - atomic_inc (&vcc->stats->tx); + atomic_inc(&vcc->stats->tx); } goto made_progress; } -static void udsl_cancel_send (struct udsl_instance_data *instance, struct atm_vcc *vcc) +static void udsl_cancel_send(struct udsl_instance_data *instance, + struct atm_vcc *vcc) { struct sk_buff *skb, *n; - dbg ("udsl_cancel_send entered"); - spin_lock_irq (&instance->sndqueue.lock); - for (skb = instance->sndqueue.next, n = skb->next; skb != (struct sk_buff *)&instance->sndqueue; skb = n, n = skb->next) - if (UDSL_SKB (skb)->atm_data.vcc == vcc) { - dbg ("udsl_cancel_send: popping skb 0x%p", skb); - __skb_unlink (skb, &instance->sndqueue); - udsl_pop (vcc, skb); + dbg("udsl_cancel_send entered"); + spin_lock_irq(&instance->sndqueue.lock); + for (skb = instance->sndqueue.next, n = skb->next; + skb != (struct sk_buff *)&instance->sndqueue; + skb = n, n = skb->next) + if (UDSL_SKB(skb)->atm_data.vcc == vcc) { + dbg("udsl_cancel_send: popping skb 0x%p", skb); + __skb_unlink(skb, &instance->sndqueue); + udsl_pop(vcc, skb); } - spin_unlock_irq (&instance->sndqueue.lock); + spin_unlock_irq(&instance->sndqueue.lock); - tasklet_disable (&instance->send_tasklet); - if ((skb = instance->current_skb) && (UDSL_SKB (skb)->atm_data.vcc == vcc)) { - dbg ("udsl_cancel_send: popping current skb (0x%p)", skb); + tasklet_disable(&instance->send_tasklet); + if ((skb = instance->current_skb) && (UDSL_SKB(skb)->atm_data.vcc == vcc)) { + dbg("udsl_cancel_send: popping current skb (0x%p)", skb); instance->current_skb = NULL; - udsl_pop (vcc, skb); + udsl_pop(vcc, skb); } - tasklet_enable (&instance->send_tasklet); - dbg ("udsl_cancel_send done"); + tasklet_enable(&instance->send_tasklet); + dbg("udsl_cancel_send done"); } -static int udsl_atm_send (struct atm_vcc *vcc, struct sk_buff *skb) +static int udsl_atm_send(struct atm_vcc *vcc, struct sk_buff *skb) { struct udsl_instance_data *instance = vcc->dev->dev_data; int err; - vdbg ("udsl_atm_send called (skb 0x%p, len %u)", skb, skb->len); + vdbg("udsl_atm_send called (skb 0x%p, len %u)", skb, skb->len); if (!instance) { - dbg ("udsl_atm_send: NULL data!"); + dbg("udsl_atm_send: NULL data!"); err = -ENODEV; goto fail; } if (vcc->qos.aal != ATM_AAL5) { - dbg ("udsl_atm_send: unsupported ATM type %d!", vcc->qos.aal); + dbg("udsl_atm_send: unsupported ATM type %d!", vcc->qos.aal); err = -EINVAL; goto fail; } if (skb->len > ATM_MAX_AAL5_PDU) { - dbg ("udsl_atm_send: packet too long (%d vs %d)!", skb->len, ATM_MAX_AAL5_PDU); + dbg("udsl_atm_send: packet too long (%d vs %d)!", skb->len, + ATM_MAX_AAL5_PDU); err = -EINVAL; goto fail; } - PACKETDEBUG (skb->data, skb->len); + PACKETDEBUG(skb->data, skb->len); - udsl_groom_skb (vcc, skb); - skb_queue_tail (&instance->sndqueue, skb); - tasklet_schedule (&instance->send_tasklet); + udsl_groom_skb(vcc, skb); + skb_queue_tail(&instance->sndqueue, skb); + tasklet_schedule(&instance->send_tasklet); return 0; -fail: - udsl_pop (vcc, skb); + fail: + udsl_pop(vcc, skb); return err; } - /******************** ** bean counting ** ********************/ static void udsl_destroy_instance(struct kref *kref) { - struct udsl_instance_data *instance = container_of (kref, struct udsl_instance_data, refcount); + struct udsl_instance_data *instance = + container_of(kref, struct udsl_instance_data, refcount); - tasklet_kill (&instance->receive_tasklet); - tasklet_kill (&instance->send_tasklet); - usb_put_dev (instance->usb_dev); - kfree (instance); + tasklet_kill(&instance->receive_tasklet); + tasklet_kill(&instance->send_tasklet); + usb_put_dev(instance->usb_dev); + kfree(instance); } - - void udsl_get_instance(struct udsl_instance_data *instance) { - kref_get (&instance->refcount); + kref_get(&instance->refcount); } void udsl_put_instance(struct udsl_instance_data *instance) { - kref_put (&instance->refcount, udsl_destroy_instance); + kref_put(&instance->refcount, udsl_destroy_instance); } - /********** ** ATM ** **********/ -static void udsl_atm_dev_close (struct atm_dev *dev) +static void udsl_atm_dev_close(struct atm_dev *dev) { struct udsl_instance_data *instance = dev->dev_data; dev->dev_data = NULL; - udsl_put_instance (instance); + udsl_put_instance(instance); } -static int udsl_atm_proc_read (struct atm_dev *atm_dev, loff_t *pos, char *page) +static int udsl_atm_proc_read(struct atm_dev *atm_dev, loff_t * pos, char *page) { struct udsl_instance_data *instance = atm_dev->dev_data; int left = *pos; if (!instance) { - dbg ("udsl_atm_proc_read: NULL instance!"); + dbg("udsl_atm_proc_read: NULL instance!"); return -ENODEV; } if (!left--) - return sprintf (page, "%s\n", instance->description); + return sprintf(page, "%s\n", instance->description); if (!left--) - return sprintf (page, "MAC: %02x:%02x:%02x:%02x:%02x:%02x\n", - atm_dev->esi [0], atm_dev->esi [1], atm_dev->esi [2], - atm_dev->esi [3], atm_dev->esi [4], atm_dev->esi [5]); + return sprintf(page, "MAC: %02x:%02x:%02x:%02x:%02x:%02x\n", + atm_dev->esi[0], atm_dev->esi[1], + atm_dev->esi[2], atm_dev->esi[3], + atm_dev->esi[4], atm_dev->esi[5]); if (!left--) - return sprintf (page, "AAL5: tx %d ( %d err ), rx %d ( %d err, %d drop )\n", - atomic_read (&atm_dev->stats.aal5.tx), - atomic_read (&atm_dev->stats.aal5.tx_err), - atomic_read (&atm_dev->stats.aal5.rx), - atomic_read (&atm_dev->stats.aal5.rx_err), - atomic_read (&atm_dev->stats.aal5.rx_drop)); + return sprintf(page, + "AAL5: tx %d ( %d err ), rx %d ( %d err, %d drop )\n", + atomic_read(&atm_dev->stats.aal5.tx), + atomic_read(&atm_dev->stats.aal5.tx_err), + atomic_read(&atm_dev->stats.aal5.rx), + atomic_read(&atm_dev->stats.aal5.rx_err), + atomic_read(&atm_dev->stats.aal5.rx_drop)); if (!left--) { switch (atm_dev->signal) { case ATM_PHY_SIG_FOUND: - sprintf (page, "Line up"); + sprintf(page, "Line up"); break; case ATM_PHY_SIG_LOST: - sprintf (page, "Line down"); + sprintf(page, "Line down"); break; default: - sprintf (page, "Line state unknown"); + sprintf(page, "Line state unknown"); break; } if (instance->usb_dev->state == USB_STATE_NOTATTACHED) - strcat (page, ", disconnected\n"); + strcat(page, ", disconnected\n"); else { if (instance->status == UDSL_LOADED_FIRMWARE) - strcat (page, ", firmware loaded\n"); + strcat(page, ", firmware loaded\n"); else if (instance->status == UDSL_LOADING_FIRMWARE) - strcat (page, ", firmware loading\n"); + strcat(page, ", firmware loading\n"); else - strcat (page, ", no firmware\n"); + strcat(page, ", no firmware\n"); } - return strlen (page); + return strlen(page); } return 0; } -static int udsl_atm_open (struct atm_vcc *vcc) +static int udsl_atm_open(struct atm_vcc *vcc) { struct udsl_instance_data *instance = vcc->dev->dev_data; struct udsl_vcc_data *new; @@ -781,213 +811,223 @@ static int udsl_atm_open (struct atm_vcc *vcc) short vpi = vcc->vpi; int err; - dbg ("udsl_atm_open: vpi %hd, vci %d", vpi, vci); + dbg("udsl_atm_open: vpi %hd, vci %d", vpi, vci); if (!instance) { - dbg ("udsl_atm_open: NULL data!"); + dbg("udsl_atm_open: NULL data!"); return -ENODEV; } /* only support AAL5 */ - if ((vcc->qos.aal != ATM_AAL5) || (vcc->qos.rxtp.max_sdu < 0) || (vcc->qos.rxtp.max_sdu > ATM_MAX_AAL5_PDU)) { - dbg ("udsl_atm_open: unsupported ATM type %d!", vcc->qos.aal); + if ((vcc->qos.aal != ATM_AAL5) || (vcc->qos.rxtp.max_sdu < 0) + || (vcc->qos.rxtp.max_sdu > ATM_MAX_AAL5_PDU)) { + dbg("udsl_atm_open: unsupported ATM type %d!", vcc->qos.aal); return -EINVAL; } if (instance->firmware_wait && - (err = instance->firmware_wait (instance)) < 0) { - dbg ("udsl_atm_open: firmware not loaded (%d)!", err); + (err = instance->firmware_wait(instance)) < 0) { + dbg("udsl_atm_open: firmware not loaded (%d)!", err); return err; } - down (&instance->serialize); /* vs self, udsl_atm_close */ + down(&instance->serialize); /* vs self, udsl_atm_close */ - if (udsl_find_vcc (instance, vpi, vci)) { - dbg ("udsl_atm_open: %hd/%d already in use!", vpi, vci); - up (&instance->serialize); + if (udsl_find_vcc(instance, vpi, vci)) { + dbg("udsl_atm_open: %hd/%d already in use!", vpi, vci); + up(&instance->serialize); return -EADDRINUSE; } - if (!(new = kmalloc (sizeof (struct udsl_vcc_data), GFP_KERNEL))) { - dbg ("udsl_atm_open: no memory for vcc_data!"); - up (&instance->serialize); + if (!(new = kmalloc(sizeof(struct udsl_vcc_data), GFP_KERNEL))) { + dbg("udsl_atm_open: no memory for vcc_data!"); + up(&instance->serialize); return -ENOMEM; } - memset (new, 0, sizeof (struct udsl_vcc_data)); + memset(new, 0, sizeof(struct udsl_vcc_data)); new->vcc = vcc; new->vpi = vpi; new->vci = vci; /* udsl_extract_cells requires at least one cell */ - max_pdu = max (1, UDSL_NUM_CELLS (vcc->qos.rxtp.max_sdu)) * ATM_CELL_PAYLOAD; - if (!(new->sarb = alloc_skb (max_pdu, GFP_KERNEL))) { - dbg ("udsl_atm_open: no memory for SAR buffer!"); - kfree (new); - up (&instance->serialize); + max_pdu = max(1, UDSL_NUM_CELLS(vcc->qos.rxtp.max_sdu)) * ATM_CELL_PAYLOAD; + if (!(new->sarb = alloc_skb(max_pdu, GFP_KERNEL))) { + dbg("udsl_atm_open: no memory for SAR buffer!"); + kfree(new); + up(&instance->serialize); return -ENOMEM; } vcc->dev_data = new; - tasklet_disable (&instance->receive_tasklet); - list_add (&new->list, &instance->vcc_list); - tasklet_enable (&instance->receive_tasklet); + tasklet_disable(&instance->receive_tasklet); + list_add(&new->list, &instance->vcc_list); + tasklet_enable(&instance->receive_tasklet); - set_bit (ATM_VF_ADDR, &vcc->flags); - set_bit (ATM_VF_PARTIAL, &vcc->flags); - set_bit (ATM_VF_READY, &vcc->flags); + set_bit(ATM_VF_ADDR, &vcc->flags); + set_bit(ATM_VF_PARTIAL, &vcc->flags); + set_bit(ATM_VF_READY, &vcc->flags); - up (&instance->serialize); + up(&instance->serialize); - tasklet_schedule (&instance->receive_tasklet); + tasklet_schedule(&instance->receive_tasklet); - dbg ("udsl_atm_open: allocated vcc data 0x%p (max_pdu: %u)", new, max_pdu); + dbg("udsl_atm_open: allocated vcc data 0x%p (max_pdu: %u)", new, max_pdu); return 0; } -static void udsl_atm_close (struct atm_vcc *vcc) +static void udsl_atm_close(struct atm_vcc *vcc) { struct udsl_instance_data *instance = vcc->dev->dev_data; struct udsl_vcc_data *vcc_data = vcc->dev_data; - dbg ("udsl_atm_close called"); + dbg("udsl_atm_close called"); if (!instance || !vcc_data) { - dbg ("udsl_atm_close: NULL data!"); + dbg("udsl_atm_close: NULL data!"); return; } - dbg ("udsl_atm_close: deallocating vcc 0x%p with vpi %d vci %d", vcc_data, vcc_data->vpi, vcc_data->vci); + dbg("udsl_atm_close: deallocating vcc 0x%p with vpi %d vci %d", + vcc_data, vcc_data->vpi, vcc_data->vci); - udsl_cancel_send (instance, vcc); + udsl_cancel_send(instance, vcc); - down (&instance->serialize); /* vs self, udsl_atm_open */ + down(&instance->serialize); /* vs self, udsl_atm_open */ - tasklet_disable (&instance->receive_tasklet); - list_del (&vcc_data->list); - tasklet_enable (&instance->receive_tasklet); + tasklet_disable(&instance->receive_tasklet); + list_del(&vcc_data->list); + tasklet_enable(&instance->receive_tasklet); - kfree_skb (vcc_data->sarb); + kfree_skb(vcc_data->sarb); vcc_data->sarb = NULL; - kfree (vcc_data); + kfree(vcc_data); vcc->dev_data = NULL; vcc->vpi = ATM_VPI_UNSPEC; vcc->vci = ATM_VCI_UNSPEC; - clear_bit (ATM_VF_READY, &vcc->flags); - clear_bit (ATM_VF_PARTIAL, &vcc->flags); - clear_bit (ATM_VF_ADDR, &vcc->flags); + clear_bit(ATM_VF_READY, &vcc->flags); + clear_bit(ATM_VF_PARTIAL, &vcc->flags); + clear_bit(ATM_VF_ADDR, &vcc->flags); - up (&instance->serialize); + up(&instance->serialize); - dbg ("udsl_atm_close successful"); + dbg("udsl_atm_close successful"); } -static int udsl_atm_ioctl (struct atm_dev *dev, unsigned int cmd, void __user *arg) +static int udsl_atm_ioctl(struct atm_dev *dev, unsigned int cmd, + void __user * arg) { switch (cmd) { case ATM_QUERYLOOP: - return put_user (ATM_LM_NONE, (int __user *)arg) ? -EFAULT : 0; + return put_user(ATM_LM_NONE, (int __user *)arg) ? -EFAULT : 0; default: return -ENOIOCTLCMD; } } - /********** ** USB ** **********/ -int udsl_instance_setup(struct usb_device *dev, struct udsl_instance_data *instance) +int udsl_instance_setup(struct usb_device *dev, + struct udsl_instance_data *instance) { char *buf; int i, length; - kref_init (&instance->refcount); /* one for USB */ - udsl_get_instance (instance); /* one for ATM */ + kref_init(&instance->refcount); /* one for USB */ + udsl_get_instance(instance); /* one for ATM */ - - init_MUTEX (&instance->serialize); + init_MUTEX(&instance->serialize); instance->usb_dev = dev; - INIT_LIST_HEAD (&instance->vcc_list); + INIT_LIST_HEAD(&instance->vcc_list); instance->status = UDSL_NO_FIRMWARE; - init_waitqueue_head (&instance->firmware_waiters); + init_waitqueue_head(&instance->firmware_waiters); - spin_lock_init (&instance->receive_lock); - INIT_LIST_HEAD (&instance->spare_receivers); - INIT_LIST_HEAD (&instance->filled_receive_buffers); + spin_lock_init(&instance->receive_lock); + INIT_LIST_HEAD(&instance->spare_receivers); + INIT_LIST_HEAD(&instance->filled_receive_buffers); - tasklet_init (&instance->receive_tasklet, udsl_process_receive, (unsigned long) instance); - INIT_LIST_HEAD (&instance->spare_receive_buffers); + tasklet_init(&instance->receive_tasklet, udsl_process_receive, (unsigned long)instance); + INIT_LIST_HEAD(&instance->spare_receive_buffers); - skb_queue_head_init (&instance->sndqueue); + skb_queue_head_init(&instance->sndqueue); - spin_lock_init (&instance->send_lock); - INIT_LIST_HEAD (&instance->spare_senders); - INIT_LIST_HEAD (&instance->spare_send_buffers); + spin_lock_init(&instance->send_lock); + INIT_LIST_HEAD(&instance->spare_senders); + INIT_LIST_HEAD(&instance->spare_send_buffers); - tasklet_init (&instance->send_tasklet, udsl_process_send, (unsigned long) instance); - INIT_LIST_HEAD (&instance->filled_send_buffers); + tasklet_init(&instance->send_tasklet, udsl_process_send, + (unsigned long)instance); + INIT_LIST_HEAD(&instance->filled_send_buffers); /* receive init */ for (i = 0; i < num_rcv_urbs; i++) { - struct udsl_receiver *rcv = &(instance->receivers [i]); + struct udsl_receiver *rcv = &(instance->receivers[i]); - if (!(rcv->urb = usb_alloc_urb (0, GFP_KERNEL))) { - dbg ("udsl_usb_probe: no memory for receive urb %d!", i); + if (!(rcv->urb = usb_alloc_urb(0, GFP_KERNEL))) { + dbg("udsl_usb_probe: no memory for receive urb %d!", i); goto fail; } rcv->instance = instance; - list_add (&rcv->list, &instance->spare_receivers); + list_add(&rcv->list, &instance->spare_receivers); } for (i = 0; i < num_rcv_bufs; i++) { - struct udsl_receive_buffer *buf = &(instance->receive_buffers [i]); + struct udsl_receive_buffer *buf = + &(instance->receive_buffers[i]); - if (!(buf->base = kmalloc (rcv_buf_size * (ATM_CELL_SIZE + instance->rcv_padding), GFP_KERNEL))) { - dbg ("udsl_usb_probe: no memory for receive buffer %d!", i); + buf->base = kmalloc(rcv_buf_size * (ATM_CELL_SIZE + instance->rcv_padding), + GFP_KERNEL); + if (!buf->base) { + dbg("udsl_usb_probe: no memory for receive buffer %d!", i); goto fail; } - list_add (&buf->list, &instance->spare_receive_buffers); + list_add(&buf->list, &instance->spare_receive_buffers); } /* send init */ for (i = 0; i < num_snd_urbs; i++) { - struct udsl_sender *snd = &(instance->senders [i]); + struct udsl_sender *snd = &(instance->senders[i]); - if (!(snd->urb = usb_alloc_urb (0, GFP_KERNEL))) { - dbg ("udsl_usb_probe: no memory for send urb %d!", i); + if (!(snd->urb = usb_alloc_urb(0, GFP_KERNEL))) { + dbg("udsl_usb_probe: no memory for send urb %d!", i); goto fail; } snd->instance = instance; - list_add (&snd->list, &instance->spare_senders); + list_add(&snd->list, &instance->spare_senders); } for (i = 0; i < num_snd_bufs; i++) { - struct udsl_send_buffer *buf = &(instance->send_buffers [i]); + struct udsl_send_buffer *buf = &(instance->send_buffers[i]); - if (!(buf->base = kmalloc (snd_buf_size * (ATM_CELL_SIZE + instance->snd_padding), GFP_KERNEL))) { - dbg ("udsl_usb_probe: no memory for send buffer %d!", i); + buf->base = kmalloc(snd_buf_size * (ATM_CELL_SIZE + instance->snd_padding), + GFP_KERNEL); + if (!buf->base) { + dbg("udsl_usb_probe: no memory for send buffer %d!", i); goto fail; } - list_add (&buf->list, &instance->spare_send_buffers); + list_add(&buf->list, &instance->spare_send_buffers); } /* ATM init */ - if (!(instance->atm_dev = atm_dev_register (instance->driver_name, &udsl_atm_devops, -1, NULL))) { - dbg ("udsl_usb_probe: failed to register ATM device!"); + instance->atm_dev = atm_dev_register(instance->driver_name, + &udsl_atm_devops, -1, NULL); + if (!instance->atm_dev) { + dbg("udsl_usb_probe: failed to register ATM device!"); goto fail; } @@ -1000,139 +1040,101 @@ int udsl_instance_setup(struct usb_device *dev, struct udsl_instance_data *insta /* device description */ buf = instance->description; - length = sizeof (instance->description); + length = sizeof(instance->description); - if ((i = usb_string (dev, dev->descriptor.iProduct, buf, length)) < 0) + if ((i = usb_string(dev, dev->descriptor.iProduct, buf, length)) < 0) goto finish; buf += i; length -= i; - i = scnprintf (buf, length, " ("); + i = scnprintf(buf, length, " ("); buf += i; length -= i; - if (length <= 0 || (i = usb_make_path (dev, buf, length)) < 0) + if (length <= 0 || (i = usb_make_path(dev, buf, length)) < 0) goto finish; buf += i; length -= i; - snprintf (buf, length, ")"); + snprintf(buf, length, ")"); -finish: + finish: /* ready for ATM callbacks */ - wmb (); + wmb(); instance->atm_dev->dev_data = instance; - usb_get_dev (dev); + usb_get_dev(dev); return 0; -fail: + fail: for (i = 0; i < num_snd_bufs; i++) - kfree (instance->send_buffers [i].base); + kfree(instance->send_buffers[i].base); for (i = 0; i < num_snd_urbs; i++) - usb_free_urb (instance->senders [i].urb); + usb_free_urb(instance->senders[i].urb); for (i = 0; i < num_rcv_bufs; i++) - kfree (instance->receive_buffers [i].base); + kfree(instance->receive_buffers[i].base); for (i = 0; i < num_rcv_urbs; i++) - usb_free_urb (instance->receivers [i].urb); + usb_free_urb(instance->receivers[i].urb); return -ENOMEM; } -void udsl_instance_disconnect (struct udsl_instance_data *instance) +void udsl_instance_disconnect(struct udsl_instance_data *instance) { - struct list_head *pos; - unsigned int count; - int result, i; + int i; - dbg ("udsl_instance_disconnect entered"); + dbg("udsl_instance_disconnect entered"); if (!instance) { - dbg ("udsl_instance_disconnect: NULL instance!"); + dbg("udsl_instance_disconnect: NULL instance!"); return; } /* receive finalize */ - tasklet_disable (&instance->receive_tasklet); + tasklet_disable(&instance->receive_tasklet); for (i = 0; i < num_rcv_urbs; i++) - if ((result = usb_unlink_urb (instance->receivers [i].urb)) < 0) - dbg ("udsl_instance_disconnect: usb_unlink_urb on receive urb %d returned %d!", i, result); - - /* wait for completion handlers to finish */ - do { - count = 0; - spin_lock_irq (&instance->receive_lock); - list_for_each (pos, &instance->spare_receivers) - DEBUG_ON (++count > num_rcv_urbs); - spin_unlock_irq (&instance->receive_lock); - - dbg ("udsl_instance_disconnect: found %u spare receivers", count); - - if (count == num_rcv_urbs) - break; - - set_current_state (TASK_RUNNING); - schedule (); - } while (1); + usb_kill_urb(instance->receivers[i].urb); /* no need to take the spinlock */ - INIT_LIST_HEAD (&instance->filled_receive_buffers); - INIT_LIST_HEAD (&instance->spare_receive_buffers); + INIT_LIST_HEAD(&instance->filled_receive_buffers); + INIT_LIST_HEAD(&instance->spare_receive_buffers); - tasklet_enable (&instance->receive_tasklet); + tasklet_enable(&instance->receive_tasklet); for (i = 0; i < num_rcv_urbs; i++) - usb_free_urb (instance->receivers [i].urb); + usb_free_urb(instance->receivers[i].urb); for (i = 0; i < num_rcv_bufs; i++) - kfree (instance->receive_buffers [i].base); + kfree(instance->receive_buffers[i].base); /* send finalize */ - tasklet_disable (&instance->send_tasklet); + tasklet_disable(&instance->send_tasklet); for (i = 0; i < num_snd_urbs; i++) - if ((result = usb_unlink_urb (instance->senders [i].urb)) < 0) - dbg ("udsl_instance_disconnect: usb_unlink_urb on send urb %d returned %d!", i, result); - - /* wait for completion handlers to finish */ - do { - count = 0; - spin_lock_irq (&instance->send_lock); - list_for_each (pos, &instance->spare_senders) - DEBUG_ON (++count > num_snd_urbs); - spin_unlock_irq (&instance->send_lock); - - dbg ("udsl_instance_disconnect: found %u spare senders", count); - - if (count == num_snd_urbs) - break; - - set_current_state (TASK_RUNNING); - schedule (); - } while (1); + usb_kill_urb(instance->senders[i].urb); /* no need to take the spinlock */ - INIT_LIST_HEAD (&instance->spare_senders); - INIT_LIST_HEAD (&instance->spare_send_buffers); + INIT_LIST_HEAD(&instance->spare_senders); + INIT_LIST_HEAD(&instance->spare_send_buffers); instance->current_buffer = NULL; - tasklet_enable (&instance->send_tasklet); + tasklet_enable(&instance->send_tasklet); for (i = 0; i < num_snd_urbs; i++) - usb_free_urb (instance->senders [i].urb); + usb_free_urb(instance->senders[i].urb); for (i = 0; i < num_snd_bufs; i++) - kfree (instance->send_buffers [i].base); + kfree(instance->send_buffers[i].base); /* ATM finalize */ - shutdown_atm_dev (instance->atm_dev); + shutdown_atm_dev(instance->atm_dev); } EXPORT_SYMBOL_GPL(udsl_get_instance); @@ -1144,48 +1146,50 @@ EXPORT_SYMBOL_GPL(udsl_instance_disconnect); ** init ** ***********/ -static int __init udsl_usb_init (void) +static int __init udsl_usb_init(void) { - dbg ("udsl_usb_init: driver version " DRIVER_VERSION); + dbg("udsl_usb_init: driver version " DRIVER_VERSION); - if (sizeof (struct udsl_control) > sizeof (((struct sk_buff *)0)->cb)) { - printk (KERN_ERR __FILE__ ": unusable with this kernel!\n"); + if (sizeof(struct udsl_control) > sizeof(((struct sk_buff *) 0)->cb)) { + printk(KERN_ERR __FILE__ ": unusable with this kernel!\n"); return -EIO; } - if ((num_rcv_urbs > UDSL_MAX_RCV_URBS) || (num_snd_urbs > UDSL_MAX_SND_URBS) || - (num_rcv_bufs > UDSL_MAX_RCV_BUFS) || (num_snd_bufs > UDSL_MAX_SND_BUFS) || - (rcv_buf_size > UDSL_MAX_RCV_BUF_SIZE) || (snd_buf_size > UDSL_MAX_SND_BUF_SIZE)) + if ((num_rcv_urbs > UDSL_MAX_RCV_URBS) + || (num_snd_urbs > UDSL_MAX_SND_URBS) + || (num_rcv_bufs > UDSL_MAX_RCV_BUFS) + || (num_snd_bufs > UDSL_MAX_SND_BUFS) + || (rcv_buf_size > UDSL_MAX_RCV_BUF_SIZE) + || (snd_buf_size > UDSL_MAX_SND_BUF_SIZE)) return -EINVAL; return 0; } -module_init (udsl_usb_init); - -MODULE_AUTHOR (DRIVER_AUTHOR); -MODULE_DESCRIPTION (DRIVER_DESC); -MODULE_LICENSE ("GPL"); -MODULE_VERSION (DRIVER_VERSION); +module_init(udsl_usb_init); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRIVER_VERSION); /************ ** debug ** ************/ #ifdef VERBOSE_DEBUG -static int udsl_print_packet (const unsigned char *data, int len) +static int udsl_print_packet(const unsigned char *data, int len) { - unsigned char buffer [256]; + unsigned char buffer[256]; int i = 0, j = 0; for (i = 0; i < len;) { - buffer [0] = '\0'; - sprintf (buffer, "%.3d :", i); + buffer[0] = '\0'; + sprintf(buffer, "%.3d :", i); for (j = 0; (j < 16) && (i < len); j++, i++) { - sprintf (buffer, "%s %2.2x", buffer, data [i]); + sprintf(buffer, "%s %2.2x", buffer, data[i]); } - dbg ("%s", buffer); + dbg("%s", buffer); } return i; } diff --git a/drivers/usb/atm/usb_atm.h b/drivers/usb/atm/usb_atm.h index 1eef3571d312..188e9171073e 100644 --- a/drivers/usb/atm/usb_atm.h +++ b/drivers/usb/atm/usb_atm.h @@ -32,14 +32,14 @@ #define UDSL_MAX_SND_URBS 4 #define UDSL_MAX_RCV_BUFS 8 #define UDSL_MAX_SND_BUFS 8 -#define UDSL_MAX_RCV_BUF_SIZE 1024 /* ATM cells */ -#define UDSL_MAX_SND_BUF_SIZE 1024 /* ATM cells */ +#define UDSL_MAX_RCV_BUF_SIZE 1024 /* ATM cells */ +#define UDSL_MAX_SND_BUF_SIZE 1024 /* ATM cells */ #define UDSL_DEFAULT_RCV_URBS 2 #define UDSL_DEFAULT_SND_URBS 2 #define UDSL_DEFAULT_RCV_BUFS 4 #define UDSL_DEFAULT_SND_BUFS 4 -#define UDSL_DEFAULT_RCV_BUF_SIZE 64 /* ATM cells */ -#define UDSL_DEFAULT_SND_BUF_SIZE 64 /* ATM cells */ +#define UDSL_DEFAULT_RCV_BUF_SIZE 64 /* ATM cells */ +#define UDSL_DEFAULT_SND_BUF_SIZE 64 /* ATM cells */ #define ATM_CELL_HEADER (ATM_CELL_SIZE - ATM_CELL_PAYLOAD) #define UDSL_NUM_CELLS(x) (((x) + ATM_AAL5_TRAILER + ATM_CELL_PAYLOAD - 1) / ATM_CELL_PAYLOAD) @@ -91,8 +91,8 @@ struct udsl_control { unsigned int num_cells; unsigned int num_entire; unsigned int pdu_padding; - unsigned char cell_header [ATM_CELL_HEADER]; - unsigned char aal5_trailer [ATM_AAL5_TRAILER]; + unsigned char cell_header[ATM_CELL_HEADER]; + unsigned char aal5_trailer[ATM_AAL5_TRAILER]; }; #define UDSL_SKB(x) ((struct udsl_control *)(x)->cb) @@ -111,7 +111,7 @@ struct udsl_instance_data { /* USB device part */ struct usb_device *usb_dev; - char description [64]; + char description[64]; int data_endpoint; int snd_padding; int rcv_padding; @@ -127,8 +127,8 @@ struct udsl_instance_data { wait_queue_head_t firmware_waiters; /* receive */ - struct udsl_receiver receivers [UDSL_MAX_RCV_URBS]; - struct udsl_receive_buffer receive_buffers [UDSL_MAX_RCV_BUFS]; + struct udsl_receiver receivers[UDSL_MAX_RCV_URBS]; + struct udsl_receive_buffer receive_buffers[UDSL_MAX_RCV_BUFS]; spinlock_t receive_lock; struct list_head spare_receivers; @@ -138,8 +138,8 @@ struct udsl_instance_data { struct list_head spare_receive_buffers; /* send */ - struct udsl_sender senders [UDSL_MAX_SND_URBS]; - struct udsl_send_buffer send_buffers [UDSL_MAX_SND_BUFS]; + struct udsl_sender senders[UDSL_MAX_SND_URBS]; + struct udsl_send_buffer send_buffers[UDSL_MAX_SND_BUFS]; struct sk_buff_head sndqueue; @@ -153,7 +153,8 @@ struct udsl_instance_data { struct list_head filled_send_buffers; }; -extern int udsl_instance_setup(struct usb_device *dev, struct udsl_instance_data *instance); -extern void udsl_instance_disconnect (struct udsl_instance_data *instance); +extern int udsl_instance_setup(struct usb_device *dev, + struct udsl_instance_data *instance); +extern void udsl_instance_disconnect(struct udsl_instance_data *instance); extern void udsl_get_instance(struct udsl_instance_data *instance); extern void udsl_put_instance(struct udsl_instance_data *instance); -- cgit v1.2.3 From 64a050305d12a5b694ded683e6973abf2aefcdb7 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Wed, 6 Oct 2004 03:22:40 -0700 Subject: [PATCH] USB: Fix hiddev devfs oops From: Herbert Xu There is a long-standing devfs_unregister oops in hid/hiddev. It's caused by hid calling hiddev_exit before unregistering itself which in turn calls hiddev_disconnect. hiddev_exit removes the directory which contains the hiddev devices. Therefore it needs to be called after the hiddev devices have been disconnected. Signed-off-by: Herbert Xu Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- drivers/usb/input/hid-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/input/hid-core.c b/drivers/usb/input/hid-core.c index fbeb6b728e2e..5c9f01790aee 100644 --- a/drivers/usb/input/hid-core.c +++ b/drivers/usb/input/hid-core.c @@ -1867,8 +1867,8 @@ hiddev_init_fail: static void __exit hid_exit(void) { - hiddev_exit(); usb_deregister(&hid_driver); + hiddev_exit(); } module_init(hid_init); -- cgit v1.2.3 From eef30f58b4da4f0e0891af085e5f40f5f2155b38 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 6 Oct 2004 15:25:20 +0100 Subject: USB: Fix assertion logic in USB ATM core. We assert something we believe to be _true_, not false. Signed-off-by: David Woodhouse Signed-off-by: Greg Kroah-Hartman --- drivers/usb/atm/usb_atm.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/atm/usb_atm.c b/drivers/usb/atm/usb_atm.c index 486458d86fe1..ac9c80600a6e 100644 --- a/drivers/usb/atm/usb_atm.c +++ b/drivers/usb/atm/usb_atm.c @@ -95,9 +95,9 @@ #include #ifdef DEBUG -#define UDSL_ASSERT(x) BUG_ON(x) +#define UDSL_ASSERT(x) BUG_ON(!(x)) #else -#define UDSL_ASSERT(x) +#define UDSL_ASSERT(x) warn("failed assertion '" #x "' at line %d", __LINE__) #endif #ifdef VERBOSE_DEBUG @@ -406,7 +406,7 @@ static unsigned int udsl_write_cells(struct udsl_instance_data *instance, target += ATM_CELL_PAYLOAD - ATM_AAL5_TRAILER; --ctrl->num_cells; - UDSL_ASSERT(ctrl->num_cells); + UDSL_ASSERT(!ctrl->num_cells); } memcpy(target, ctrl->aal5_trailer, ATM_AAL5_TRAILER); @@ -445,7 +445,7 @@ static void udsl_complete_receive(struct urb *urb, struct pt_regs *regs) vdbg("udsl_complete_receive: urb 0x%p, status %d, actual_length %d, filled_cells %u, rcv 0x%p, buf 0x%p", urb, urb->status, urb->actual_length, buf->filled_cells, rcv, buf); - UDSL_ASSERT(buf->filled_cells > rcv_buf_size); + UDSL_ASSERT(buf->filled_cells <= rcv_buf_size); /* may not be in_interrupt() */ spin_lock_irqsave(&instance->receive_lock, flags); -- cgit v1.2.3 From b537028034c092322eb4c5d3e767d91478b3b053 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Thu, 7 Oct 2004 16:56:25 +0100 Subject: USB: SpeedTouch / ATM update: - Continue polling even if we receive a 'line down' interrupt. The Rev 4 modems give that one but not 'line up'. - Make the warn() on debugging assertions actually conditional :) - Keep a reference count on the module while its thread is running. - Provide empty exit function for usb_atm.ko so it's unloadable. - Handle firmware which is in /usr so only turns up later. Signed-off-by: David Woodhouse Signed-off-by: Greg Kroah-Hartman --- drivers/usb/atm/speedtch.c | 16 ++++++++++++---- drivers/usb/atm/usb_atm.c | 7 ++++++- 2 files changed, 18 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/atm/speedtch.c b/drivers/usb/atm/speedtch.c index 696541b57c88..13d0a50cadfc 100644 --- a/drivers/usb/atm/speedtch.c +++ b/drivers/usb/atm/speedtch.c @@ -316,7 +316,6 @@ static void speedtch_handle_int(struct urb *urb, struct pt_regs *regs) del_timer(&instance->poll_timer); printk(KERN_NOTICE "DSL line goes up\n"); } else if (!memcmp(down_int, instance->int_data, 6)) { - del_timer(&instance->poll_timer); printk(KERN_NOTICE "DSL line goes down\n"); } else { int i; @@ -464,7 +463,6 @@ static void speedtch_timer_poll(unsigned long data) } #ifdef USE_FW_LOADER - static void speedtch_upload_firmware(struct speedtch_instance_data *instance, const struct firmware *fw1, const struct firmware *fw2) @@ -639,6 +637,13 @@ static int speedtch_load_firmware(void *arg) release_firmware(fw1); } + /* In case we failed, set state back to NO_FIRMWARE so that + another later attempt may work. Otherwise, we never actually + manage to recover if, for example, the firmware is on /usr and + we look for it too early. */ + speedtch_got_firmware(instance, 0); + + module_put(THIS_MODULE); udsl_put_instance(&instance->u); return 0; } @@ -662,9 +667,10 @@ static void speedtch_firmware_start(struct speedtch_instance_data *instance) instance->u.status = UDSL_LOADING_FIRMWARE; up(&instance->u.serialize); +#ifdef USE_FW_LOADER udsl_get_instance(&instance->u); + try_module_get(THIS_MODULE); -#ifdef USE_FW_LOADER ret = kernel_thread(speedtch_load_firmware, instance, CLONE_FS | CLONE_FILES); @@ -673,10 +679,12 @@ static void speedtch_firmware_start(struct speedtch_instance_data *instance) dbg("speedtch_firmware_start: kernel_thread failed (%d)!", ret); + module_put(THIS_MODULE); + udsl_put_instance(&instance->u); /* Just pretend it never happened... hope modem_run happens */ #endif /* USE_FW_LOADER */ + speedtch_got_firmware(instance, 0); - udsl_put_instance(&instance->u); } static int speedtch_firmware_wait(struct udsl_instance_data *instance) diff --git a/drivers/usb/atm/usb_atm.c b/drivers/usb/atm/usb_atm.c index ac9c80600a6e..9180dda04fc5 100644 --- a/drivers/usb/atm/usb_atm.c +++ b/drivers/usb/atm/usb_atm.c @@ -97,7 +97,7 @@ #ifdef DEBUG #define UDSL_ASSERT(x) BUG_ON(!(x)) #else -#define UDSL_ASSERT(x) warn("failed assertion '" #x "' at line %d", __LINE__) +#define UDSL_ASSERT(x) do { if (!(x)) warn("failed assertion '" #x "' at line %d", __LINE__); } while(0) #endif #ifdef VERBOSE_DEBUG @@ -1166,7 +1166,12 @@ static int __init udsl_usb_init(void) return 0; } +static void __exit udsl_usb_exit(void) +{ +} + module_init(udsl_usb_init); +module_exit(udsl_usb_exit); MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); -- cgit v1.2.3 From 0cbd0fa4ac47991d38f945cb2bc51923204a0917 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 15 Oct 2004 02:12:52 -0700 Subject: USB: add endian markups to the ub driver. Signed-off-by: Greg Kroah-Hartman --- drivers/block/ub.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/block/ub.c b/drivers/block/ub.c index 0c3567159375..dd3be0a06219 100644 --- a/drivers/block/ub.c +++ b/drivers/block/ub.c @@ -63,9 +63,9 @@ /* command block wrapper */ struct bulk_cb_wrap { - u32 Signature; /* contains 'USBC' */ + __le32 Signature; /* contains 'USBC' */ u32 Tag; /* unique per command id */ - u32 DataTransferLength; /* size of data */ + __le32 DataTransferLength; /* size of data */ u8 Flags; /* direction in bit 0 */ u8 Lun; /* LUN normally 0 */ u8 Length; /* of of the CDB */ @@ -79,9 +79,9 @@ struct bulk_cb_wrap { /* command status wrapper */ struct bulk_cs_wrap { - u32 Signature; /* should = 'USBS' */ + __le32 Signature; /* should = 'USBS' */ u32 Tag; /* same as original command */ - u32 Residue; /* amount not transferred */ + __le32 Residue; /* amount not transferred */ u8 Status; /* see below */ }; @@ -1692,8 +1692,8 @@ static int ub_sync_read_cap(struct ub_dev *sc, struct ub_capacity *ret) } /* sd.c special-cases sector size of 0 to mean 512. Needed? Safe? */ - nsec = be32_to_cpu(*(u32 *)p) + 1; - bsize = be32_to_cpu(*(u32 *)(p + 4)); + nsec = be32_to_cpu(*(__be32 *)p) + 1; + bsize = be32_to_cpu(*(__be32 *)(p + 4)); switch (bsize) { case 512: shift = 0; break; case 1024: shift = 1; break; -- cgit v1.2.3 From b5b2b4a87dfbb15a31e78b0da139559afe78a05a Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Mon, 18 Oct 2004 11:25:49 +0100 Subject: USB SpeedTouch / ATM: Make it work on 64-bit hosts. Reduce size of struct udsl_control to make it fit in skb->cb, by dropping the cell_header and generating it later instead of storing it. Signed-off-by: David Woodhouse Signed-off-by: Duncan Sands Signed-off-by: Greg Kroah-Hartman --- drivers/usb/atm/usb_atm.c | 20 ++++++++++++-------- drivers/usb/atm/usb_atm.h | 1 - 2 files changed, 12 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/atm/usb_atm.c b/drivers/usb/atm/usb_atm.c index 9180dda04fc5..8d1d365846a1 100644 --- a/drivers/usb/atm/usb_atm.c +++ b/drivers/usb/atm/usb_atm.c @@ -311,6 +311,15 @@ static void udsl_extract_cells(struct udsl_instance_data *instance, ** encode ** *************/ +static inline void udsl_fill_cell_header(unsigned char *target, struct atm_vcc *vcc) +{ + target[0] = vcc->vpi >> 4; + target[1] = (vcc->vpi << 4) | (vcc->vci >> 12); + target[2] = vcc->vci >> 4; + target[3] = vcc->vci << 4; + target[4] = 0xec; +} + static const unsigned char zeros[ATM_CELL_PAYLOAD]; static void udsl_groom_skb(struct atm_vcc *vcc, struct sk_buff *skb) @@ -320,11 +329,6 @@ static void udsl_groom_skb(struct atm_vcc *vcc, struct sk_buff *skb) u32 crc; ctrl->atm_data.vcc = vcc; - ctrl->cell_header[0] = vcc->vpi >> 4; - ctrl->cell_header[1] = (vcc->vpi << 4) | (vcc->vci >> 12); - ctrl->cell_header[2] = vcc->vci >> 4; - ctrl->cell_header[3] = vcc->vci << 4; - ctrl->cell_header[4] = 0xec; ctrl->num_cells = UDSL_NUM_CELLS(skb->len); ctrl->num_entire = skb->len / ATM_CELL_PAYLOAD; @@ -366,7 +370,7 @@ static unsigned int udsl_write_cells(struct udsl_instance_data *instance, ne = min(howmany, ctrl->num_entire); for (i = 0; i < ne; i++) { - memcpy(target, ctrl->cell_header, ATM_CELL_HEADER); + udsl_fill_cell_header(target, ctrl->atm_data.vcc); target += ATM_CELL_HEADER; memcpy(target, skb->data, ATM_CELL_PAYLOAD); target += ATM_CELL_PAYLOAD; @@ -386,7 +390,7 @@ static unsigned int udsl_write_cells(struct udsl_instance_data *instance, memset(target, 0, instance->snd_padding); target += instance->snd_padding; } - memcpy(target, ctrl->cell_header, ATM_CELL_HEADER); + udsl_fill_cell_header(target, ctrl->atm_data.vcc); target += ATM_CELL_HEADER; memcpy(target, skb->data, skb->len); target += skb->len; @@ -400,7 +404,7 @@ static unsigned int udsl_write_cells(struct udsl_instance_data *instance, goto out; } - memcpy(target, ctrl->cell_header, ATM_CELL_HEADER); + udsl_fill_cell_header(target, ctrl->atm_data.vcc); target += ATM_CELL_HEADER; memset(target, 0, ATM_CELL_PAYLOAD - ATM_AAL5_TRAILER); target += ATM_CELL_PAYLOAD - ATM_AAL5_TRAILER; diff --git a/drivers/usb/atm/usb_atm.h b/drivers/usb/atm/usb_atm.h index 188e9171073e..219763cc3242 100644 --- a/drivers/usb/atm/usb_atm.h +++ b/drivers/usb/atm/usb_atm.h @@ -91,7 +91,6 @@ struct udsl_control { unsigned int num_cells; unsigned int num_entire; unsigned int pdu_padding; - unsigned char cell_header[ATM_CELL_HEADER]; unsigned char aal5_trailer[ATM_AAL5_TRAILER]; }; -- cgit v1.2.3 From 1881642411926cea530c2c9a23b5c93589cc2274 Mon Sep 17 00:00:00 2001 From: Duncan Sands Date: Mon, 18 Oct 2004 11:29:11 +0100 Subject: USB SpeedTouch cleanup. - Don't free pages with kfree() - Clean up debugging messages - Print name of firmware file loaded. Signed-off-by: Duncan Sands Signed-off-by: David Woodhouse Signed-off-by: Greg Kroah-Hartman --- drivers/usb/atm/speedtch.c | 41 +++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 20 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/atm/speedtch.c b/drivers/usb/atm/speedtch.c index 13d0a50cadfc..f17e576d12ec 100644 --- a/drivers/usb/atm/speedtch.c +++ b/drivers/usb/atm/speedtch.c @@ -165,8 +165,7 @@ static void speedtch_got_firmware(struct speedtch_instance_data *instance, goto out; } if ((err = usb_set_interface(instance->u.usb_dev, 1, 1)) < 0) { - dbg("speedtch_got_firmware: usb_set_interface returned %d!", - err); + dbg("speedtch_got_firmware: usb_set_interface returned %d!", err); instance->u.status = UDSL_NO_FIRMWARE; goto out; } @@ -572,7 +571,7 @@ static void speedtch_upload_firmware(struct speedtch_instance_data *instance, /* Start modem synchronisation */ if (speedtch_start_synchro(instance)) - dbg("speedtch_start_synchro: failed\n"); + dbg("speedtch_start_synchro: failed"); speedtch_got_firmware(instance, 1); @@ -585,7 +584,7 @@ static void speedtch_upload_firmware(struct speedtch_instance_data *instance, the firmware themselves */ usb_driver_release_interface(&speedtch_usb_driver, intf); fail_free: - kfree(buffer); + free_page((unsigned long)buffer); fail: speedtch_got_firmware(instance, 0); } @@ -596,28 +595,30 @@ static int speedtch_find_firmware(struct speedtch_instance_data { char buf[24]; const u16 bcdDevice = instance->u.usb_dev->descriptor.bcdDevice; + const u8 major_revision = bcdDevice >> 8; + const u8 minor_revision = bcdDevice & 0xff; - sprintf(buf, "speedtch-%d.bin.%x.%02x", phase, bcdDevice >> 8, - bcdDevice & 0xff); - dbg("speedtch_find_firmware: looking for %s\n", buf); + sprintf(buf, "speedtch-%d.bin.%x.%02x", phase, major_revision, minor_revision); + dbg("speedtch_find_firmware: looking for %s", buf); - if (!request_firmware(fw_p, buf, &instance->u.usb_dev->dev)) - return 0; + if (request_firmware(fw_p, buf, &instance->u.usb_dev->dev)) { + sprintf(buf, "speedtch-%d.bin.%x", phase, major_revision); + dbg("speedtch_find_firmware: looking for %s", buf); - sprintf(buf, "speedtch-%d.bin.%x", phase, bcdDevice >> 8); - dbg("speedtch_find_firmware: looking for %s\n", buf); + if (request_firmware(fw_p, buf, &instance->u.usb_dev->dev)) { + sprintf(buf, "speedtch-%d.bin", phase); + dbg("speedtch_find_firmware: looking for %s", buf); - if (!request_firmware(fw_p, buf, &instance->u.usb_dev->dev)) - return 0; - - sprintf(buf, "speedtch-%d.bin", phase); - dbg("speedtch_find_firmware: looking for %s\n", buf); + if (request_firmware(fw_p, buf, &instance->u.usb_dev->dev)) { + dev_warn(&instance->u.usb_dev->dev, "no stage %d firmware found!", phase); + return -ENOENT; + } + } + } - if (!request_firmware(fw_p, buf, &instance->u.usb_dev->dev)) - return 0; + dev_info(&instance->u.usb_dev->dev, "found stage %d firmware %s\n", phase, buf); - dev_warn(&instance->u.usb_dev->dev, "no stage %d firmware found!", phase); - return -ENOENT; + return 0; } static int speedtch_load_firmware(void *arg) -- cgit v1.2.3 From c638b507f22df9d6c61bfe596574dadbc20de314 Mon Sep 17 00:00:00 2001 From: Phil Dibowitz Date: Sun, 17 Oct 2004 23:55:12 -0700 Subject: [PATCH] USB Storage: Unusual_dev patch for Finepix 1300 and 1400's. There are several cameras (1300s and 1400s) with the same bcdDevice number (1000). Most of them are 8070 devices, but some of them are UFI devices (similar to 8070 devices but don't clear the sense data after an INQUIRY or REQUEST SENSE). Furthermore if the devices that truely do act like 8070 devices are overridden to be UFI, they find more than one LUN. Specifiying UFI and SINGLE_LUN seems to satisfy all kinds of devices that claim device number 1000. From: Pavel Machek Signed-off-by: Phil Dibowitz Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/unusual_devs.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index 1d5181430529..1712773edaa1 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -142,10 +142,13 @@ UNUSUAL_DEV( 0x04b8, 0x0602, 0x0110, 0x0110, "785EPX Storage", US_SC_SCSI, US_PR_BULK, NULL, US_FL_SINGLE_LUN), +/* Not sure who reported this originally but + * Pavel Machek reported that the extra US_FL_SINGLE_LUN + * flag be added */ UNUSUAL_DEV( 0x04cb, 0x0100, 0x0000, 0x2210, "Fujifilm", "FinePix 1400Zoom", - US_SC_UFI, US_PR_DEVICE, NULL, US_FL_FIX_INQUIRY), + US_SC_UFI, US_PR_DEVICE, NULL, US_FL_FIX_INQUIRY | US_FL_SINGLE_LUN), /* Reported by Peter Wächtler * The device needs the flags only. -- cgit v1.2.3 From 62aaf0a199c253469dd7a9b8ea340ec5881dd184 Mon Sep 17 00:00:00 2001 From: Phil Dibowitz Date: Sun, 17 Oct 2004 23:55:35 -0700 Subject: [PATCH] USB Storage: unusual_devs patch for new tekom entry Stephan Fuhrmann sent in the entry for a device needing the new RESIDUE flag. Here is an appropriate tested patch. From: Stephan Fuhrmann Signed-off-by: Phil Dibowitz Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/unusual_devs.h | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index 1712773edaa1..045078d2de5e 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -278,6 +278,14 @@ UNUSUAL_DEV( 0x052b, 0x1807, 0x0100, 0x0100, US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_IGNORE_RESIDUE ), +/* Yakumo Mega Image 37 + * Submitted by Stephan Fuhrmann */ +UNUSUAL_DEV( 0x052b, 0x1801, 0x0100, 0x0100, + "Tekom Technologies, Inc", + "300_CAMERA", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_IGNORE_RESIDUE ), + /* This entry is needed because the device reports Sub=ff */ UNUSUAL_DEV( 0x054c, 0x0010, 0x0106, 0x0450, "Sony", -- cgit v1.2.3 From 7dec0c7a16304b238444cb2c3c8661b6198351e9 Mon Sep 17 00:00:00 2001 From: Phil Dibowitz Date: Sun, 17 Oct 2004 23:55:59 -0700 Subject: [PATCH] USB Storage: unusual_devs patch for winward music disk The following is needed for Winward Music Disk. I narrowed the range of the original patch which was sent by Stephan Walter. From: Stephan Walter Signed-off-by: Phil Dibowitz Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/unusual_devs.h | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index 045078d2de5e..a80ef1a7856c 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -823,6 +823,14 @@ UNUSUAL_DEV( 0x0dd8, 0x1060, 0x0000, 0xffff, US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_FIX_INQUIRY ), +/* Patch by Stephan Walter + * I don't know why, but it works... */ +UNUSUAL_DEV( 0x0dda, 0x0001, 0x0012, 0x0012, + "WINWARD", + "Music Disk", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_IGNORE_RESIDUE ), + /* Submitted by Antoine Mairesse */ UNUSUAL_DEV( 0x0ed1, 0x6660, 0x0100, 0x0300, "USB", -- cgit v1.2.3 From 892716153657e2c535a44ce253129ed44e887a07 Mon Sep 17 00:00:00 2001 From: Phil Dibowitz Date: Sun, 17 Oct 2004 23:56:31 -0700 Subject: [PATCH] USB Storage: Remove unusual_dev entry for IBM Storage Key This removes the 0a16/8888/0100 unusual_devs entry for an IBM USB Storag key. It does not appear to be needed and caused issues for Buddha Henry , who has tested with this patch and his device works properly. Signed-off-by: Phil Dibowitz Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/unusual_devs.h | 6 ------ 1 file changed, 6 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index a80ef1a7856c..32dd023581ce 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -745,12 +745,6 @@ UNUSUAL_DEV( 0x097a, 0x0001, 0x0000, 0x0001, US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_MODE_XLATE ), -UNUSUAL_DEV( 0x0a16, 0x8888, 0x0100, 0x0100, - "IBM", - "IBM USB Memory Key", - US_SC_DEVICE, US_PR_DEVICE, NULL, - US_FL_FIX_INQUIRY ), - /* This Pentax still camera is not conformant * to the USB storage specification: - * - It does not like the INQUIRY command. So we must handle this command -- cgit v1.2.3 From d6e561b1fae51dfc339e899df7594ccd4375b8a8 Mon Sep 17 00:00:00 2001 From: Phil Dibowitz Date: Sun, 17 Oct 2004 23:56:54 -0700 Subject: [PATCH] USB Storage: Remove unusual_devs entries for Genesys Drives Here's a patch to remove the three Genesys disk entries in unusual_devs. They don't appear to be needed anymore because: 1. The inquiries now request the right amount of data 2. MODE_XLATE, according to Alan, isn't used in 2.6 Signed-off-by: Phil Dibowitz Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/unusual_devs.h | 30 ------------------------------ 1 file changed, 30 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index 32dd023581ce..131ed94af904 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -460,36 +460,6 @@ UNUSUAL_DEV( 0x05dc, 0xb002, 0x0000, 0x0113, US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_FIX_INQUIRY ), -/* Reported by Carlos Villegas - * This device needs an INQUIRY of exactly 36-bytes to function. - * That is the only reason this entry is needed. - */ -UNUSUAL_DEV( 0x05e3, 0x0700, 0x0000, 0xffff, - "Genesys Logic", - "USB to IDE Card Reader", - US_SC_DEVICE, US_PR_DEVICE, NULL, - US_FL_FIX_INQUIRY ), - -/* Submitted Alexander Oltu */ -UNUSUAL_DEV( 0x05e3, 0x0701, 0x0000, 0xffff, - "Genesys Logic", - "USB to IDE Optical", - US_SC_DEVICE, US_PR_DEVICE, NULL, - US_FL_MODE_XLATE ), - -/* Reported by Peter Marks - * Like the SIIG unit above, this unit needs an INQUIRY to ask for exactly - * 36 bytes of data. No more, no less. That is the only reason this entry - * is needed. - * - * ST818 slim drives (rev 0.02) don't need special care. -*/ -UNUSUAL_DEV( 0x05e3, 0x0702, 0x0000, 0xffff, - "Genesys Logic", - "USB to IDE Disk", - US_SC_DEVICE, US_PR_DEVICE, NULL, - US_FL_FIX_INQUIRY ), - /* Reported by Hanno Boeck * Taken from the Lycoris Kernel */ UNUSUAL_DEV( 0x0636, 0x0003, 0x0000, 0x9999, -- cgit v1.2.3 From 03a8c871ace48695b7ed940c658a439dc9e32317 Mon Sep 17 00:00:00 2001 From: Phil Dibowitz Date: Sun, 17 Oct 2004 23:57:23 -0700 Subject: [PATCH] USB Storage: Fix Kyocera order This removes a duplicate entry and fixes order. Trivial. Signed-off-by: Phil Dibowitz Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/unusual_devs.h | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index 131ed94af904..9c12922cd2d9 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -88,12 +88,6 @@ UNUSUAL_DEV( 0x0436, 0x0005, 0x0100, 0x0100, US_SC_SCSI, US_PR_DPCM_USB, NULL, 0 ), #endif -/* Patch submitted by Alessandro Fracchetti */ -UNUSUAL_DEV( 0x0482, 0x0105, 0x0100, 0x0100, - "Kyocera", - "Finecam L3", - US_SC_SCSI, US_PR_BULK, NULL, US_FL_FIX_INQUIRY), - /* Patch submitted by Philipp Friedrich */ UNUSUAL_DEV( 0x0482, 0x0100, 0x0100, 0x0100, "Kyocera", @@ -114,6 +108,7 @@ UNUSUAL_DEV( 0x0482, 0x0103, 0x0100, 0x0100, /* Patch for Kyocera Finecam L3 * Submitted by Michael Krauth + * and Alessandro Fracchetti */ UNUSUAL_DEV( 0x0482, 0x0105, 0x0100, 0x0100, "Kyocera", -- cgit v1.2.3 From 35ddffa9df10e78d2f34932b3ce68a6009c650ba Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Sun, 17 Oct 2004 23:57:50 -0700 Subject: [PATCH] USB Storage: new unusual_devs entry Here's another USB mass storage device that incorrectly reports the total number of disk blocks. Please apply. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/unusual_devs.h | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index 9c12922cd2d9..e4cfa620b39d 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -540,6 +540,13 @@ UNUSUAL_DEV( 0x07ab, 0xfc01, 0x0000, 0x9999, US_SC_QIC, US_PR_FREECOM, freecom_init, 0), #endif +/* Reported by Eero Volotinen */ +UNUSUAL_DEV( 0x07ab, 0xfccd, 0x0406, 0x0406, + "Freecom Technologies", + "FHD-Classic", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_FIX_CAPACITY), + UNUSUAL_DEV( 0x07af, 0x0004, 0x0100, 0x0133, "Microtech", "USB-SCSI-DB25", -- cgit v1.2.3 From 0bc22f26591c4265fed2d7754a5f328f8d54e11f Mon Sep 17 00:00:00 2001 From: Phil Dibowitz Date: Sun, 17 Oct 2004 23:58:20 -0700 Subject: [PATCH] USB Storage: unusual_dev modification The following patch changes the 0x059f/0xa601/0x0200 per the report from Torsten Eriksson. It adds comments to the uncommented entry and changes the subclass. Signed-off-by: Phil Dibowitz Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/unusual_devs.h | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index e4cfa620b39d..1e0eb58f0c09 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -399,10 +399,17 @@ UNUSUAL_DEV( 0x0595, 0x4343, 0x0000, 0x2210, "Digital Camera EX-20 DSC", US_SC_8070, US_PR_DEVICE, NULL, 0 ), +/* The entry was here before I took over, and had US_SC_RBC. It turns + * out that isn't needed. Additionally, Torsten Eriksson + * is able to use his device fine + * without this entry at all - but I don't suspect that will be true + * for all users (the protocol is likely needed), so is staying at + * this time. - Phil Dibowitz + */ UNUSUAL_DEV( 0x059f, 0xa601, 0x0200, 0x0200, "LaCie", "USB Hard Disk", - US_SC_RBC, US_PR_CB, NULL, 0 ), + US_SC_DEVICE, US_PR_CB, NULL, 0 ), /* Submitted by Joel Bourquard * Some versions of this device need the SubClass and Protocol overrides -- cgit v1.2.3 From f5d29b1b8e91c8f7dfb97fd682eb817a55f2ccd1 Mon Sep 17 00:00:00 2001 From: Alex Kanavin Date: Mon, 18 Oct 2004 00:41:41 -0700 Subject: [PATCH] USB: USB CDC OBEX driver Also, as the full patch isn't going in, can you please apply this tiny part of it? Somehow the header descriptor was omitted from the CDC ACM driver, but it's present on my phone (thus giving me the "ignoring extra header" error when it's plugged in) and in the CDC spec (section 5.2.3.1). Signed-off-by: Alex Kanavin Signed-Off-By: Oliver Neukum Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-acm.c | 4 +++- drivers/usb/class/cdc-acm.h | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index e302a6cdaa11..0d0d49923bee 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -569,6 +569,8 @@ static int acm_probe (struct usb_interface *intf, break; case CDC_COUNTRY_TYPE: /* maybe somehow export */ break; /* for now we ignore it */ + case CDC_HEADER_TYPE: /* maybe check version */ + break; /* for now we ignore it */ case CDC_AC_MANAGEMENT_TYPE: ac_management_function = buffer[3]; break; @@ -580,7 +582,7 @@ static int acm_probe (struct usb_interface *intf, break; default: - err("Ignoring extra header"); + err("Ignoring extra header, type %d, length %d", buffer[2], buffer[0]); break; } next_desc: diff --git a/drivers/usb/class/cdc-acm.h b/drivers/usb/class/cdc-acm.h index cc26d9517398..da945b367a85 100644 --- a/drivers/usb/class/cdc-acm.h +++ b/drivers/usb/class/cdc-acm.h @@ -117,6 +117,7 @@ struct union_desc { } __attribute__ ((packed)); /* class specific descriptor types */ +#define CDC_HEADER_TYPE 0x00 #define CDC_CALL_MANAGEMENT_TYPE 0x01 #define CDC_AC_MANAGEMENT_TYPE 0x02 #define CDC_UNION_TYPE 0x06 -- cgit v1.2.3 From 7df90674c6656e18d30a90681f3d0bffa9ce084b Mon Sep 17 00:00:00 2001 From: Heinz-Juergen Oertel Date: Mon, 18 Oct 2004 00:42:08 -0700 Subject: [PATCH] USB: usb/serial RM vendor/product id for ftdi_sio Hello, this is a small patch of the USB ftdi_sio driver against linux-2.6.8.1. I only added a new vendor and product id for the RM-CANview, a CAN fieldbus interface: http://www.rmcan.com/site/en/products/gateways/usb/index.htm Thanks Heinz Signed-off-by: Heinz-Juergen Oertel Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/ftdi_sio.c | 3 +++ drivers/usb/serial/ftdi_sio.h | 7 +++++++ 2 files changed, 10 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 9d3a8e831326..b1a985470e42 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -368,6 +368,7 @@ static struct usb_device_id id_table_8U232AM [] = { { USB_DEVICE_VER(INTREPID_VID, INTREPID_NEOVI_PID, 0, 0x3ff) }, { USB_DEVICE_VER(FALCOM_VID, FALCOM_TWIST_PID, 0, 0x3ff) }, { USB_DEVICE_VER(FTDI_VID, FTDI_SUUNTO_SPORTS_PID, 0, 0x3ff) }, + { USB_DEVICE_VER(FTDI_RM_VID, FTDI_RMCANVIEW_PID, 0, 0x3ff) }, { USB_DEVICE_VER(BANDB_VID, BANDB_USOTL4_PID, 0, 0x3ff) }, { USB_DEVICE_VER(BANDB_VID, BANDB_USTL4_PID, 0, 0x3ff) }, { USB_DEVICE_VER(BANDB_VID, BANDB_USO9ML2_PID, 0, 0x3ff) }, @@ -481,6 +482,7 @@ static struct usb_device_id id_table_FT232BM [] = { { USB_DEVICE_VER(INTREPID_VID, INTREPID_NEOVI_PID, 0x400, 0xffff) }, { USB_DEVICE_VER(FALCOM_VID, FALCOM_TWIST_PID, 0x400, 0xffff) }, { USB_DEVICE_VER(FTDI_VID, FTDI_SUUNTO_SPORTS_PID, 0x400, 0xffff) }, + { USB_DEVICE_VER(FTDI_RM_VID, FTDI_RMCANVIEW_PID, 0x400, 0xffff) }, { USB_DEVICE_VER(BANDB_VID, BANDB_USOTL4_PID, 0x400, 0xffff) }, { USB_DEVICE_VER(BANDB_VID, BANDB_USTL4_PID, 0x400, 0xffff) }, { USB_DEVICE_VER(BANDB_VID, BANDB_USO9ML2_PID, 0x400, 0xffff) }, @@ -601,6 +603,7 @@ static struct usb_device_id id_table_combined [] = { { USB_DEVICE(INTREPID_VID, INTREPID_NEOVI_PID) }, { USB_DEVICE(FALCOM_VID, FALCOM_TWIST_PID) }, { USB_DEVICE(FTDI_VID, FTDI_SUUNTO_SPORTS_PID) }, + { USB_DEVICE(FTDI_RM_VID, FTDI_RMCANVIEW_PID) }, { USB_DEVICE(BANDB_VID, BANDB_USOTL4_PID) }, { USB_DEVICE(BANDB_VID, BANDB_USTL4_PID) }, { USB_DEVICE(BANDB_VID, BANDB_USO9ML2_PID) }, diff --git a/drivers/usb/serial/ftdi_sio.h b/drivers/usb/serial/ftdi_sio.h index bc2b6d5aacaa..e7a7e0aba4f2 100644 --- a/drivers/usb/serial/ftdi_sio.h +++ b/drivers/usb/serial/ftdi_sio.h @@ -233,6 +233,13 @@ #define BANDB_USTL4_PID 0xAC02 /* USTL4 RS-485 Converter */ #define BANDB_USO9ML2_PID 0xAC03 /* USO9ML2 Isolated RS-232 Converter */ +/* + * RM Michaelides CANview USB (http://www.rmcan.com) + * CAN filedbus interface adapter, addad by port GmbH www.port.de) + */ +#define FTDI_RM_VID 0x0403 /* Vendor Id */ +#define FTDI_RMCANVIEW_PID 0xfd60 /* Product Id */ + /* Commands */ #define FTDI_SIO_RESET 0 /* Reset the port */ #define FTDI_SIO_MODEM_CTRL 1 /* Set the modem control register */ -- cgit v1.2.3 From 5a78b22e03bd403db51b4ac905b08c4c1faba37b Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Mon, 18 Oct 2004 00:51:13 -0700 Subject: [PATCH] USB: remove calls to usb_unlink_urb in net/usbnet.c Signed-off-by: Borislav Petkov Signed-off-by: Greg Kroah-Hartman --- drivers/usb/net/usbnet.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/net/usbnet.c b/drivers/usb/net/usbnet.c index d88503a4e698..ad65e741fa44 100644 --- a/drivers/usb/net/usbnet.c +++ b/drivers/usb/net/usbnet.c @@ -825,7 +825,7 @@ static void ax8817x_unbind(struct usbnet *dev, struct usb_interface *intf) { struct ax8817x_data *data = (struct ax8817x_data *)dev->data; - usb_unlink_urb(data->int_urb); + usb_kill_urb(data->int_urb); usb_free_urb(data->int_urb); kfree(data->int_buf); } @@ -1437,7 +1437,7 @@ static int genelink_free (struct usbnet *dev) // handling needs to be generic) // cancel irq urb first - usb_unlink_urb (priv->irq_urb); + usb_kill_urb (priv->irq_urb); // free irq urb usb_free_urb (priv->irq_urb); -- cgit v1.2.3 From 5b667dd09ac9080ccefd5f12efc6e8cda30d47c0 Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Mon, 18 Oct 2004 00:51:42 -0700 Subject: [PATCH] USB: remove calls to usb_unlink_urb() in net/pegasus.c Signed-off-by: Borislav Petkov Signed-off-by: Greg Kroah-Hartman --- drivers/usb/net/pegasus.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/net/pegasus.c b/drivers/usb/net/pegasus.c index c515916e37a7..98cb5ae3f6f1 100644 --- a/drivers/usb/net/pegasus.c +++ b/drivers/usb/net/pegasus.c @@ -854,10 +854,10 @@ static void free_all_urbs(pegasus_t * pegasus) static void unlink_all_urbs(pegasus_t * pegasus) { - usb_unlink_urb(pegasus->intr_urb); - usb_unlink_urb(pegasus->tx_urb); - usb_unlink_urb(pegasus->rx_urb); - usb_unlink_urb(pegasus->ctrl_urb); + usb_kill_urb(pegasus->intr_urb); + usb_kill_urb(pegasus->tx_urb); + usb_kill_urb(pegasus->rx_urb); + usb_kill_urb(pegasus->ctrl_urb); } static int alloc_urbs(pegasus_t * pegasus) @@ -920,8 +920,8 @@ static int pegasus_open(struct net_device *net) if ((res = enable_net_traffic(net, pegasus->usb))) { err("can't enable_net_traffic() - %d", res); res = -EIO; - usb_unlink_urb(pegasus->rx_urb); - usb_unlink_urb(pegasus->intr_urb); + usb_kill_urb(pegasus->rx_urb); + usb_kill_urb(pegasus->intr_urb); free_skb_pool(pegasus); goto exit; } -- cgit v1.2.3 From b94e7ae2aefaec5d5b26016164993340df4f3a71 Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Mon, 18 Oct 2004 00:52:07 -0700 Subject: [PATCH] USB: remove calls to usb_unlink_urb() in net/kaweth.c Hi there Greg, here's another one. Signed-off-by: Borislav Petkov Signed-off-by: Greg Kroah-Hartman --- drivers/usb/net/kaweth.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/net/kaweth.c b/drivers/usb/net/kaweth.c index 044d19ece304..2092cb9cb33f 100644 --- a/drivers/usb/net/kaweth.c +++ b/drivers/usb/net/kaweth.c @@ -674,7 +674,7 @@ static int kaweth_open(struct net_device *net) res = usb_submit_urb(kaweth->irq_urb, GFP_KERNEL); if (res) { - usb_unlink_urb(kaweth->rx_urb); + usb_kill_urb(kaweth->rx_urb); return -EIO; } @@ -695,15 +695,15 @@ static int kaweth_close(struct net_device *net) kaweth->status |= KAWETH_STATUS_CLOSING; - usb_unlink_urb(kaweth->irq_urb); - usb_unlink_urb(kaweth->rx_urb); + usb_kill_urb(kaweth->irq_urb); + usb_kill_urb(kaweth->rx_urb); flush_scheduled_work(); /* a scheduled work may have resubmitted, we hit them again */ - usb_unlink_urb(kaweth->irq_urb); - usb_unlink_urb(kaweth->rx_urb); + usb_kill_urb(kaweth->irq_urb); + usb_kill_urb(kaweth->rx_urb); kaweth->status &= ~KAWETH_STATUS_CLOSING; @@ -1173,8 +1173,8 @@ static void kaweth_disconnect(struct usb_interface *intf) } kaweth->removed = 1; - usb_unlink_urb(kaweth->irq_urb); - usb_unlink_urb(kaweth->rx_urb); + usb_kill_urb(kaweth->irq_urb); + usb_kill_urb(kaweth->rx_urb); /* we need to wait for the urb to be cancelled, if it is active */ spin_lock(&kaweth->device_lock); @@ -1260,7 +1260,7 @@ static int usb_start_wait_urb(struct urb *urb, int timeout, int* actual_length) if (!timeout) { // timeout kaweth_warn("usb_control/bulk_msg: timeout"); - usb_unlink_urb(urb); // remove urb safely + usb_kill_urb(urb); // remove urb safely status = -ETIMEDOUT; } else { -- cgit v1.2.3 From 0c802d2940e8e4ca9757718d754ac4178e999262 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 18 Oct 2004 03:07:29 -0700 Subject: USB: add bulk_in_size for usb-serial devices. Don't know why I didn't do it before... Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/usb-serial.c | 1 + drivers/usb/serial/usb-serial.h | 1 + 2 files changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index ef701224edb6..e3b7c1dd2f4c 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -1047,6 +1047,7 @@ int usb_serial_probe(struct usb_interface *interface, goto probe_error; } buffer_size = endpoint->wMaxPacketSize; + port->bulk_in_size = buffer_size; port->bulk_in_endpointAddress = endpoint->bEndpointAddress; port->bulk_in_buffer = kmalloc (buffer_size, GFP_KERNEL); if (!port->bulk_in_buffer) { diff --git a/drivers/usb/serial/usb-serial.h b/drivers/usb/serial/usb-serial.h index e2e59560d7fb..d3702957b609 100644 --- a/drivers/usb/serial/usb-serial.h +++ b/drivers/usb/serial/usb-serial.h @@ -100,6 +100,7 @@ struct usb_serial_port { __u8 interrupt_in_endpointAddress; unsigned char * bulk_in_buffer; + int bulk_in_size; struct urb * read_urb; __u8 bulk_in_endpointAddress; -- cgit v1.2.3 From c6fcf5a9eadb0aed33c07b4e829ca9c6614f3807 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 18 Oct 2004 17:14:46 -0700 Subject: USB: add serial ipw driver Based on a 2.4 tty usb driver from Roelf Diedericks Cleaned up and ported to 2.6 and the usb-serial layer by me. Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/Kconfig | 10 + drivers/usb/serial/Makefile | 1 + drivers/usb/serial/ipw.c | 496 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 507 insertions(+) create mode 100644 drivers/usb/serial/ipw.c (limited to 'drivers') diff --git a/drivers/usb/serial/Kconfig b/drivers/usb/serial/Kconfig index 88fede7b447e..1499bf2fcd13 100644 --- a/drivers/usb/serial/Kconfig +++ b/drivers/usb/serial/Kconfig @@ -187,6 +187,16 @@ config USB_SERIAL_EDGEPORT_TI To compile this driver as a module, choose M here: the module will be called io_ti. +config USB_SERIAL_IPW + tristate "USB IPWireless (3G UMTS TDD) Driver (EXPERIMENTAL)" + depends on USB_SERIAL && EXPERIMENTAL + help + Say Y here if you want to use a IPWireless USB modem such as + the ones supplied by Axity3G/Sentech South Africa. + + To compile this driver as a module, choose M here: the + module will be called ipw. + config USB_SERIAL_KEYSPAN_PDA tristate "USB Keyspan PDA Single Port Serial Driver" depends on USB_SERIAL diff --git a/drivers/usb/serial/Makefile b/drivers/usb/serial/Makefile index 6cfbe3108ccb..dfd43ec80ff3 100644 --- a/drivers/usb/serial/Makefile +++ b/drivers/usb/serial/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_USB_SERIAL_EDGEPORT_TI) += io_ti.o obj-$(CONFIG_USB_SERIAL_EMPEG) += empeg.o obj-$(CONFIG_USB_SERIAL_FTDI_SIO) += ftdi_sio.o obj-$(CONFIG_USB_SERIAL_IPAQ) += ipaq.o +obj-$(CONFIG_USB_SERIAL_IPW) += ipw.o obj-$(CONFIG_USB_SERIAL_IR) += ir-usb.o obj-$(CONFIG_USB_SERIAL_KEYSPAN) += keyspan.o obj-$(CONFIG_USB_SERIAL_KEYSPAN_PDA) += keyspan_pda.o diff --git a/drivers/usb/serial/ipw.c b/drivers/usb/serial/ipw.c new file mode 100644 index 000000000000..2fc04f905a5c --- /dev/null +++ b/drivers/usb/serial/ipw.c @@ -0,0 +1,496 @@ +/* + * IPWireless 3G UMTS TDD Modem driver (USB connected) + * + * Copyright (C) 2004 Roelf Diedericks + * Copyright (C) 2004 Greg Kroah-Hartman + * + * 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. + * + * All information about the device was acquired using SnoopyPro + * on MSFT's O/S, and examing the MSFT drivers' debug output + * (insanely left _on_ in the enduser version) + * + * It was written out of frustration with the IPWireless USB modem + * supplied by Axity3G/Sentech South Africa not supporting + * Linux whatsoever. + * + * Nobody provided any proprietary information that was not already + * available for this device. + * + * The modem adheres to the "3GPP TS 27.007 AT command set for 3G + * User Equipment (UE)" standard, available from + * http://www.3gpp.org/ftp/Specs/html-info/27007.htm + * + * The code was only tested the IPWireless handheld modem distributed + * in South Africa by Sentech. + * + * It may work for Woosh Inc in .nz too, as it appears they use the + * same kit. + * + * There is still some work to be done in terms of handling + * DCD, DTR, RTS, CTS which are currently faked. + * It's good enough for PPP at this point. It's based off all kinds of + * code found in usb/serial and usb/class + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "usb-serial.h" + +/* + * Version Information + */ +#define DRIVER_VERSION "v0.3" +#define DRIVER_AUTHOR "Roelf Diedericks" +#define DRIVER_DESC "IPWireless tty driver" + +#define IPW_TTY_MAJOR 240 /* real device node major id, experimental range */ +#define IPW_TTY_MINORS 256 /* we support 256 devices, dunno why, it'd be insane :) */ + +#define USB_IPW_MAGIC 0x6d02 /* magic number for ipw struct */ + + +/* Message sizes */ +#define EVENT_BUFFER_SIZE 0xFF +#define CHAR2INT16(c1,c0) (((u32)((c1) & 0xff) << 8) + (u32)((c0) & 0xff)) +#define NUM_BULK_URBS 24 +#define NUM_CONTROL_URBS 16 + +/* vendor/product pairs that are known work with this driver*/ +#define IPW_VID 0x0bc3 +#define IPW_PID 0x0001 + + +/* Vendor commands: */ + +/* baud rates */ +enum { + ipw_sio_b256000 = 0x000e, + ipw_sio_b128000 = 0x001d, + ipw_sio_b115200 = 0x0020, + ipw_sio_b57600 = 0x0040, + ipw_sio_b56000 = 0x0042, + ipw_sio_b38400 = 0x0060, + ipw_sio_b19200 = 0x00c0, + ipw_sio_b14400 = 0x0100, + ipw_sio_b9600 = 0x0180, + ipw_sio_b4800 = 0x0300, + ipw_sio_b2400 = 0x0600, + ipw_sio_b1200 = 0x0c00, + ipw_sio_b600 = 0x1800 +}; + +/* data bits */ +#define ipw_dtb_7 0x700 +#define ipw_dtb_8 0x810 // ok so the define is misleading, I know, but forces 8,n,1 + // I mean, is there a point to any other setting these days? :) + +/* usb control request types : */ +#define IPW_SIO_RXCTL 0x00 // control bulk rx channel transmissions, value=1/0 (on/off) +#define IPW_SIO_SET_BAUD 0x01 // set baud, value=requested ipw_sio_bxxxx +#define IPW_SIO_SET_LINE 0x03 // set databits, parity. value=ipw_dtb_x +#define IPW_SIO_SET_PIN 0x03 // set/clear dtr/rts value=ipw_pin_xxx +#define IPW_SIO_POLL 0x08 // get serial port status byte, call with value=0 +#define IPW_SIO_INIT 0x11 // initializes ? value=0 (appears as first thing todo on open) +#define IPW_SIO_PURGE 0x12 // purge all transmissions?, call with value=numchar_to_purge +#define IPW_SIO_HANDFLOW 0x13 // set xon/xoff limits value=0, and a buffer of 0x10 bytes +#define IPW_SIO_SETCHARS 0x13 // set the flowcontrol special chars, value=0, buf=6 bytes, + // last 2 bytes contain flowcontrol chars e.g. 00 00 00 00 11 13 + +/* values used for request IPW_SIO_SET_PIN */ +#define IPW_PIN_SETDTR 0x101 +#define IPW_PIN_SETRTS 0x202 +#define IPW_PIN_CLRDTR 0x100 +#define IPW_PIN_CLRRTS 0x200 // unconfirmed + +/* values used for request IPW_SIO_RXCTL */ +#define IPW_RXBULK_ON 1 +#define IPW_RXBULK_OFF 0 + +/* various 16 byte hardcoded transferbuffers used by flow control */ +#define IPW_BYTES_FLOWINIT { 0x01, 0, 0, 0, 0x40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } + +/* Interpretation of modem status lines */ +/* These need sorting out by individually connecting pins and checking + * results. FIXME! + * When data is being sent we see 0x30 in the lower byte; this must + * contain DSR and CTS ... + */ +#define IPW_DSR ((1<<4) | (1<<5)) +#define IPW_CTS ((1<<5) | (1<<4)) + +#define IPW_WANTS_TO_SEND 0x30 +//#define IPW_DTR /* Data Terminal Ready */ +//#define IPW_CTS /* Clear To Send */ +//#define IPW_CD /* Carrier Detect */ +//#define IPW_DSR /* Data Set Ready */ +//#define IPW_RxD /* Receive pin */ + +//#define IPW_LE +//#define IPW_RTS +//#define IPW_ST +//#define IPW_SR +//#define IPW_RI /* Ring Indicator */ + +static struct usb_device_id usb_ipw_ids[] = { + { USB_DEVICE(IPW_VID, IPW_PID) }, + { }, +}; + +MODULE_DEVICE_TABLE(usb, usb_ipw_ids); + +static struct usb_driver usb_ipw_driver = { + .owner = THIS_MODULE, + .name = "ipwtty", + .probe = usb_serial_probe, + .disconnect = usb_serial_disconnect, + .id_table = usb_ipw_ids, +}; + +static int debug; + +static void ipw_read_bulk_callback(struct urb *urb, struct pt_regs *regs) +{ + struct usb_serial_port *port = urb->context; + unsigned char *data = urb->transfer_buffer; + struct tty_struct *tty; + int i; + int result; + + dbg("%s - port %d", __FUNCTION__, port->number); + + if (urb->status) { + dbg("%s - nonzero read bulk status received: %d", __FUNCTION__, urb->status); + return; + } + + usb_serial_debug_data(debug, &port->dev, __FUNCTION__, urb->actual_length, data); + + tty = port->tty; + if (tty && urb->actual_length) { + for (i = 0; i < urb->actual_length ; ++i) { + /* if we insert more than TTY_FLIPBUF_SIZE characters, we drop them. */ + if(tty->flip.count >= TTY_FLIPBUF_SIZE) { + tty_flip_buffer_push(tty); + } + /* this doesn't actually push the data through unless tty->low_latency is set */ + tty_insert_flip_char(tty, data[i], 0); + } + tty_flip_buffer_push(tty); + } + + /* Continue trying to always read */ + usb_fill_bulk_urb (port->read_urb, port->serial->dev, + usb_rcvbulkpipe(port->serial->dev, + port->bulk_in_endpointAddress), + port->read_urb->transfer_buffer, + port->read_urb->transfer_buffer_length, + ipw_read_bulk_callback, port); + result = usb_submit_urb(port->read_urb, GFP_ATOMIC); + if (result) + dev_err(&port->dev, "%s - failed resubmitting read urb, error %d\n", __FUNCTION__, result); + return; +} + +static int ipw_open(struct usb_serial_port *port, struct file *filp) +{ + struct usb_device *dev = port->serial->dev; + u8 buf_flow_static[16] = IPW_BYTES_FLOWINIT; + u8 *buf_flow_init; + int result; + + dbg("%s", __FUNCTION__); + + buf_flow_init = kmalloc(16, GFP_KERNEL); + if (!buf_flow_init) + return -ENOMEM; + memcpy(buf_flow_init, buf_flow_static, 16); + + if (port->tty) + port->tty->low_latency = 1; + + /* --1: Tell the modem to initialize (we think) From sniffs this is always the + * first thing that gets sent to the modem during opening of the device */ + dbg("%s: Sending SIO_INIT (we guess)",__FUNCTION__); + result = usb_control_msg(dev, usb_sndctrlpipe(dev,0), + IPW_SIO_INIT, + USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT, + 0, + 0, /* index */ + NULL, + 0, + 100*HZ); + if (result < 0) + dev_err(&port->dev, "Init of modem failed (error = %d)", result); + + /* reset the bulk pipes */ + usb_clear_halt(dev, usb_rcvbulkpipe(dev, port->bulk_in_endpointAddress)); + usb_clear_halt(dev, usb_sndbulkpipe(dev, port->bulk_out_endpointAddress)); + + /*--2: Start reading from the device */ + dbg("%s: setting up bulk read callback",__FUNCTION__); + usb_fill_bulk_urb(port->read_urb, dev, + usb_rcvbulkpipe(dev, port->bulk_in_endpointAddress), + port->bulk_in_buffer, + port->bulk_in_size, + ipw_read_bulk_callback, port); + result = usb_submit_urb(port->read_urb, GFP_KERNEL); + if (result < 0) + dbg("%s - usb_submit_urb(read bulk) failed with status %d", __FUNCTION__, result); + + /*--3: Tell the modem to open the floodgates on the rx bulk channel */ + dbg("%s:asking modem for RxRead (RXBULK_ON)",__FUNCTION__); + result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + IPW_SIO_RXCTL, + USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT, + IPW_RXBULK_ON, + 0, /* index */ + NULL, + 0, + 100*HZ); + if (result < 0) + dev_err(&port->dev, "Enabling bulk RxRead failed (error = %d)", result); + + /*--4: setup the initial flowcontrol */ + dbg("%s:setting init flowcontrol (%s)",__FUNCTION__,buf_flow_init); + result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + IPW_SIO_HANDFLOW, + USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT, + 0, + 0, + buf_flow_init, + 0x10, + 200*HZ); + if (result < 0) + dev_err(&port->dev, "initial flowcontrol failed (error = %d)", result); + + + /*--5: raise the dtr */ + dbg("%s:raising dtr",__FUNCTION__); + result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + IPW_SIO_SET_PIN, + USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT, + IPW_PIN_SETDTR, + 0, + NULL, + 0, + 200*HZ); + if (result < 0) + dev_err(&port->dev, "setting dtr failed (error = %d)", result); + + /*--6: raise the rts */ + dbg("%s:raising rts",__FUNCTION__); + result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + IPW_SIO_SET_PIN, + USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT, + IPW_PIN_SETRTS, + 0, + NULL, + 0, + 200*HZ); + if (result < 0) + dev_err(&port->dev, "setting dtr failed (error = %d)", result); + + kfree(buf_flow_init); + return 0; +} + +static void ipw_close(struct usb_serial_port *port, struct file * filp) +{ + struct usb_device *dev = port->serial->dev; + int result; + + if (tty_hung_up_p(filp)) { + dbg("%s: tty_hung_up_p ...", __FUNCTION__); + return; + } + + /*--1: drop the dtr */ + dbg("%s:dropping dtr",__FUNCTION__); + result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + IPW_SIO_SET_PIN, + USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT, + IPW_PIN_CLRDTR, + 0, + NULL, + 0, + 200*HZ); + if (result < 0) + dev_err(&port->dev, "dropping dtr failed (error = %d)", result); + + /*--2: drop the rts */ + dbg("%s:dropping rts",__FUNCTION__); + result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + IPW_SIO_SET_PIN, USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT, + IPW_PIN_CLRRTS, + 0, + NULL, + 0, + 200*HZ); + if (result < 0) + dev_err(&port->dev, "dropping rts failed (error = %d)", result); + + + /*--3: purge */ + dbg("%s:sending purge",__FUNCTION__); + result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + IPW_SIO_PURGE, USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT, + 0x03, + 0, + NULL, + 0, + 200*HZ); + if (result < 0) + dev_err(&port->dev, "purge failed (error = %d)", result); + + + /* send RXBULK_off (tell modem to stop transmitting bulk data on rx chan) */ + result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + IPW_SIO_RXCTL, + USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT, + IPW_RXBULK_OFF, + 0, /* index */ + NULL, + 0, + 100*HZ); + + if (result < 0) + dev_err(&port->dev, "Disabling bulk RxRead failed (error = %d)", result); + + /* shutdown any in-flight urbs that we know about */ + usb_kill_urb(port->read_urb); + usb_kill_urb(port->write_urb); +} + +static void ipw_write_bulk_callback(struct urb *urb, struct pt_regs *regs) +{ + struct usb_serial_port *port = urb->context; + + dbg("%s", __FUNCTION__); + + if (urb->status) + dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, urb->status); + + schedule_work(&port->work); +} + +static int ipw_write(struct usb_serial_port *port, int from_user, const unsigned char *buf, int count) +{ + struct usb_device *dev = port->serial->dev; + int ret; + + dbg("%s: TOP: count=%d, from_user=%d, in_interrupt=%ld", __FUNCTION__, + count, from_user, in_interrupt() ); + + if (count == 0) { + dbg("%s - write request of 0 bytes", __FUNCTION__); + return 0; + } + + /* Racy and broken, FIXME properly! */ + if (port->write_urb->status == -EINPROGRESS) + return 0; + + count = min(count, port->bulk_out_size); + if (from_user) { + if (copy_from_user(port->bulk_out_buffer, buf, count)) + return -EFAULT; + } else { + memcpy(port->bulk_out_buffer, buf, count); + } + + dbg("%s count now:%d", __FUNCTION__, count); + + usb_fill_bulk_urb(port->write_urb, dev, + usb_sndbulkpipe(dev, port->bulk_out_endpointAddress), + port->write_urb->transfer_buffer, + count, + ipw_write_bulk_callback, + port); + + ret = usb_submit_urb(port->write_urb, GFP_ATOMIC); + if (ret != 0) { + dbg("%s - usb_submit_urb(write bulk) failed with error = %d", __FUNCTION__, ret); + return ret; + } + + dbg("%s returning %d", __FUNCTION__, count); + return count; +} + +static int ipw_probe(struct usb_serial_port *port) +{ + return 0; +} + +static int ipw_disconnect(struct usb_serial_port *port) +{ + usb_set_serial_port_data(port, NULL); + return 0; +} + +static struct usb_serial_device_type ipw_device = { + .owner = THIS_MODULE, + .name = "IPWireless converter", + .short_name = "ipw", + .id_table = usb_ipw_ids, + .num_interrupt_in = NUM_DONT_CARE, + .num_bulk_in = 1, + .num_bulk_out = 1, + .num_ports = 1, + .open = ipw_open, + .close = ipw_close, + .port_probe = ipw_probe, + .port_remove = ipw_disconnect, + .write = ipw_write, + .write_bulk_callback = ipw_write_bulk_callback, + .read_bulk_callback = ipw_read_bulk_callback, +}; + + + +int usb_ipw_init(void) +{ + int retval; + + retval = usb_serial_register(&ipw_device); + if (retval) + return retval; + retval = usb_register(&usb_ipw_driver); + if (retval) { + usb_serial_deregister(&ipw_device); + return retval; + } + info(DRIVER_DESC " " DRIVER_VERSION); + return 0; +} + +void usb_ipw_exit(void) +{ + usb_deregister(&usb_ipw_driver); + usb_serial_deregister(&ipw_device); +} + +module_init(usb_ipw_init); +module_exit(usb_ipw_exit); + +/* Module information */ +MODULE_AUTHOR( DRIVER_AUTHOR ); +MODULE_DESCRIPTION( DRIVER_DESC ); +MODULE_LICENSE("GPL"); + +module_param(debug, bool, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug, "Debug enabled or not"); -- cgit v1.2.3 From ed594d2d7d644a80402e4da598035538c454a3cf Mon Sep 17 00:00:00 2001 From: Peter Osterlund Date: Mon, 18 Oct 2004 17:57:09 -0700 Subject: [PATCH] DVD+RW support This patch adds support for using DVD+RW drives as writable block devices. The patch is based on work from: Andy Polyakov - Wrote the 2.4 patch Nigel Kukard - Initial porting to 2.6.x It works for me using an Iomega Super DVD 8x USB drive. Nov 5 2001, Aug 8 2002. Modified by Andy Polyakov to support MMC-3 complaint DVD+RW units. Modified by Nigel Kukard - support DVD+RW 2.4.x patch by Andy Polyakov This patch implements CDRW packet writing as a kernel block device. Usage instructions are in the packet-writing.txt file. A hint: If you don't want to wait for a complete disc format, you can format just a part of the disc. For example: cdrwtool -d /dev/hdc -m 10240 This will format 10240 blocks, ie 20MB. Signed-off-by: Peter Osterlund Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/cdrom/cdrom.c | 80 +++++++++++++++++++++++++++++++++++++++++++++++++++ drivers/ide/ide-cd.c | 2 ++ drivers/scsi/sr.c | 1 + include/linux/cdrom.h | 2 ++ 4 files changed, 85 insertions(+) (limited to 'drivers') diff --git a/drivers/cdrom/cdrom.c b/drivers/cdrom/cdrom.c index e57d19031f8e..d815b8a5b1e8 100644 --- a/drivers/cdrom/cdrom.c +++ b/drivers/cdrom/cdrom.c @@ -848,6 +848,41 @@ static int cdrom_ram_open_write(struct cdrom_device_info *cdi) return ret; } +static void cdrom_mmc3_profile(struct cdrom_device_info *cdi) +{ + struct packet_command cgc; + char buffer[32]; + int ret, mmc3_profile; + + init_cdrom_command(&cgc, buffer, sizeof(buffer), CGC_DATA_READ); + + cgc.cmd[0] = GPCMD_GET_CONFIGURATION; + cgc.cmd[1] = 0; + cgc.cmd[2] = cgc.cmd[3] = 0; /* Starting Feature Number */ + cgc.cmd[7] = 0; cgc.cmd [8] = 8; /* Allocation Length */ + cgc.quiet = 1; + + if ((ret = cdi->ops->generic_packet(cdi, &cgc))) { + mmc3_profile = 0xffff; + } else { + mmc3_profile = (buffer[6] << 8) | buffer[7]; + printk(KERN_INFO "cdrom: %s: mmc-3 profile capable, current profile: %Xh\n", + cdi->name, mmc3_profile); + } + cdi->mmc3_profile = mmc3_profile; +} + +static int cdrom_is_dvd_rw(struct cdrom_device_info *cdi) +{ + switch (cdi->mmc3_profile) { + case 0x12: /* DVD-RAM */ + case 0x1A: /* DVD+RW */ + return 0; + default: + return 1; + } +} + /* * returns 0 for ok to open write, non-0 to disallow */ @@ -889,10 +924,50 @@ static int cdrom_open_write(struct cdrom_device_info *cdi) ret = cdrom_ram_open_write(cdi); else if (CDROM_CAN(CDC_MO_DRIVE)) ret = mo_open_write(cdi); + else if (!cdrom_is_dvd_rw(cdi)) + ret = 0; return ret; } +static void cdrom_dvd_rw_close_write(struct cdrom_device_info *cdi) +{ + struct packet_command cgc; + + if (cdi->mmc3_profile != 0x1a) { + cdinfo(CD_CLOSE, "%s: No DVD+RW\n", cdi->name); + return; + } + + if (!cdi->media_written) { + cdinfo(CD_CLOSE, "%s: DVD+RW media clean\n", cdi->name); + return; + } + + printk(KERN_INFO "cdrom: %s: dirty DVD+RW media, \"finalizing\"\n", + cdi->name); + + init_cdrom_command(&cgc, NULL, 0, CGC_DATA_NONE); + cgc.cmd[0] = GPCMD_FLUSH_CACHE; + cgc.timeout = 30*HZ; + cdi->ops->generic_packet(cdi, &cgc); + + init_cdrom_command(&cgc, NULL, 0, CGC_DATA_NONE); + cgc.cmd[0] = GPCMD_CLOSE_TRACK; + cgc.timeout = 3000*HZ; + cgc.quiet = 1; + cdi->ops->generic_packet(cdi, &cgc); + + init_cdrom_command(&cgc, NULL, 0, CGC_DATA_NONE); + cgc.cmd[0] = GPCMD_CLOSE_TRACK; + cgc.cmd[2] = 2; /* Close session */ + cgc.quiet = 1; + cgc.timeout = 3000*HZ; + cdi->ops->generic_packet(cdi, &cgc); + + cdi->media_written = 0; +} + static int cdrom_close_write(struct cdrom_device_info *cdi) { #if 0 @@ -925,6 +1000,7 @@ int cdrom_open(struct cdrom_device_info *cdi, struct inode *ip, struct file *fp) ret = open_for_data(cdi); if (ret) goto err; + cdrom_mmc3_profile(cdi); if (fp->f_mode & FMODE_WRITE) { ret = -EROFS; if (cdrom_open_write(cdi)) @@ -932,6 +1008,7 @@ int cdrom_open(struct cdrom_device_info *cdi, struct inode *ip, struct file *fp) if (!CDROM_CAN(CDC_RAM)) goto err; ret = 0; + cdi->media_written = 0; } } @@ -1123,6 +1200,8 @@ int cdrom_release(struct cdrom_device_info *cdi, struct file *fp) cdi->use_count--; if (cdi->use_count == 0) cdinfo(CD_CLOSE, "Use count for \"/dev/%s\" now zero\n", cdi->name); + if (cdi->use_count == 0) + cdrom_dvd_rw_close_write(cdi); if (cdi->use_count == 0 && (cdo->capability & CDC_LOCK) && !keeplocked) { cdinfo(CD_CLOSE, "Unlocking door!\n"); @@ -1329,6 +1408,7 @@ int media_changed(struct cdrom_device_info *cdi, int queue) if (cdi->ops->media_changed(cdi, CDSL_CURRENT)) { cdi->mc_flags = 0x3; /* set bit on both queues */ ret |= 1; + cdi->media_written = 0; } cdi->mc_flags &= ~mask; /* clear bit */ return ret; diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c index cc6b66e34e37..468eec62246f 100644 --- a/drivers/ide/ide-cd.c +++ b/drivers/ide/ide-cd.c @@ -1932,6 +1932,8 @@ static ide_startstop_t cdrom_start_write(ide_drive_t *drive, struct request *rq) info->dma = drive->using_dma ? 1 : 0; info->cmd = WRITE; + info->devinfo.media_written = 1; + /* Start sending the write request to the drive. */ return cdrom_start_packet_command(drive, 32768, cdrom_start_write_cont); } diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c index b6c76b5680a2..20a10a3ed5e4 100644 --- a/drivers/scsi/sr.c +++ b/drivers/scsi/sr.c @@ -377,6 +377,7 @@ static int sr_init_command(struct scsi_cmnd * SCpnt) return 0; SCpnt->cmnd[0] = WRITE_10; SCpnt->sc_data_direction = DMA_TO_DEVICE; + cd->cdi.media_written = 1; } else if (rq_data_dir(SCpnt->request) == READ) { SCpnt->cmnd[0] = READ_10; SCpnt->sc_data_direction = DMA_FROM_DEVICE; diff --git a/include/linux/cdrom.h b/include/linux/cdrom.h index bcc9410761d9..73a1ade6a8e9 100644 --- a/include/linux/cdrom.h +++ b/include/linux/cdrom.h @@ -947,6 +947,8 @@ struct cdrom_device_info { __u8 reserved : 6; /* not used yet */ int cdda_method; /* see flags */ __u8 last_sense; + __u8 media_written; /* dirty flag, DVD+RW bookkeeping */ + unsigned short mmc3_profile; /* current MMC3 profile */ int for_data; int (*exit)(struct cdrom_device_info *); int mrw_mode_page; -- cgit v1.2.3 From a7cbd7da4fb59371ed56373caca648daeb2f1d55 Mon Sep 17 00:00:00 2001 From: Peter Osterlund Date: Mon, 18 Oct 2004 17:57:21 -0700 Subject: [PATCH] packet-writing: add credits Nigel pointed out that the earlier patches contained attributions that are not present in this patch. The 2.4 patch contains: Nov 5 2001, Aug 8 2002. Modified by Andy Polyakov to support MMC-3 complaint DVD+RW units. and Nigel changed it to this in his 2.6 patch: Modified by Nigel Kukard - support DVD+RW 2.4.x patch by Andy Polyakov The patch I sent you deleted most of the earlier work and moved the rest to cdrom.c, but the comments were not moved over, since the earlier authors didn't modify cdrom.c. Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/cdrom/cdrom.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers') diff --git a/drivers/cdrom/cdrom.c b/drivers/cdrom/cdrom.c index d815b8a5b1e8..3da410b2c940 100644 --- a/drivers/cdrom/cdrom.c +++ b/drivers/cdrom/cdrom.c @@ -234,6 +234,12 @@ -- Mt Rainier support -- DVD-RAM write open fixes + Nov 5 2001, Aug 8 2002. Modified by Andy Polyakov + to support MMC-3 compliant DVD+RW units. + + Modified by Nigel Kukard - support DVD+RW + 2.4.x patch by Andy Polyakov + -------------------------------------------------------------------------*/ #define REVISION "Revision: 3.20" -- cgit v1.2.3 From 2f8e2dc86c9876edca632e8ef2ab1f68d1b753f0 Mon Sep 17 00:00:00 2001 From: Peter Osterlund Date: Mon, 18 Oct 2004 17:57:34 -0700 Subject: [PATCH] CDRW packet writing support This patch implements CDRW packet writing as a kernel block device. Usage instructions are in the packet-writing.txt file. A hint: If you don't want to wait for a complete disc format, you can format just a part of the disc. For example: cdrwtool -d /dev/hdc -m 10240 This will format 10240 blocks, ie 20MB. Signed-off-by: Peter Osterlund Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/cdrom/00-INDEX | 2 + Documentation/cdrom/packet-writing.txt | 86 + drivers/block/Kconfig | 33 + drivers/block/Makefile | 1 + drivers/block/pktcdvd.c | 2679 ++++++++++++++++++++++++++++++++ drivers/cdrom/Makefile | 1 + drivers/ide/ide-cd.c | 6 +- drivers/scsi/sr.c | 6 +- fs/compat_ioctl.c | 1 + include/linux/cdrom.h | 1 + include/linux/compat_ioctl.h | 2 + include/linux/pktcdvd.h | 275 ++++ 12 files changed, 3088 insertions(+), 5 deletions(-) create mode 100644 Documentation/cdrom/packet-writing.txt create mode 100644 drivers/block/pktcdvd.c create mode 100644 include/linux/pktcdvd.h (limited to 'drivers') diff --git a/Documentation/cdrom/00-INDEX b/Documentation/cdrom/00-INDEX index eae6896676f2..916dafe29d3f 100644 --- a/Documentation/cdrom/00-INDEX +++ b/Documentation/cdrom/00-INDEX @@ -22,6 +22,8 @@ mcdx - info on improved Mitsumi CD-ROM driver. optcd - info on the Optics Storage 8000 AT CD-ROM driver +packet-writing.txt + - Info on the CDRW packet writing module sbpcd - info on the SoundBlaster/Panasonic CD-ROM interface driver. sjcd diff --git a/Documentation/cdrom/packet-writing.txt b/Documentation/cdrom/packet-writing.txt new file mode 100644 index 000000000000..d34fcbca9f27 --- /dev/null +++ b/Documentation/cdrom/packet-writing.txt @@ -0,0 +1,86 @@ +Getting started quick +--------------------- + +- Select packet support in the block device section and UDF support in + the file system section. + +- Compile and install kernel and modules, reboot. + +- You need the udftools package (pktsetup, mkudffs, cdrwtool). + Download from http://sourceforge.net/projects/linux-udf/ + +- Grab a new CD-RW disc and format it (assuming CD-RW is hdc, substitute + as appropriate): + # cdrwtool -d /dev/hdc -q + +- Setup your writer + # pktsetup dev_name /dev/hdc + +- Now you can mount /dev/pktcdvd/dev_name and copy files to it. Enjoy! + # mount /dev/pktcdvd/dev_name /cdrom -t udf -o rw,noatime + + +Packet writing for DVD-RW media +------------------------------- + +DVD-RW discs can be written to much like CD-RW discs if they are in +the so called "restricted overwrite" mode. To put a disc in restricted +overwrite mode, run: + + # dvd+rw-format /dev/hdc + +You can then use the disc the same way you would use a CD-RW disc: + + # pktsetup dev_name /dev/hdc + # mount /dev/pktcdvd/dev_name /cdrom -t udf -o rw,noatime + + +Packet writing for DVD+RW media +------------------------------- + +According to the DVD+RW specification, a drive supporting DVD+RW discs +shall implement "true random writes with 2KB granularity", which means +that it should be possible to put any filesystem with a block size >= +2KB on such a disc. For example, it should be possible to do: + + # mkudffs /dev/hdc + # mount /dev/hdc /cdrom -t udf -o rw,noatime + +However, some drives don't follow the specification and expect the +host to perform aligned writes at 32KB boundaries. Other drives do +follow the specification, but suffer bad performance problems if the +writes are not 32KB aligned. + +Both problems can be solved by using the pktcdvd driver, which always +generates aligned writes. + + # pktsetup dev_name /dev/hdc + # mkudffs /dev/pktcdvd/dev_name + # mount /dev/pktcdvd/dev_name /cdrom -t udf -o rw,noatime + + +Notes +----- + +- CD-RW media can usually not be overwritten more than about 1000 + times, so to avoid unnecessary wear on the media, you should always + use the noatime mount option. + +- Defect management (ie automatic remapping of bad sectors) has not + been implemented yet, so you are likely to get at least some + filesystem corruption if the disc wears out. + +- Since the pktcdvd driver makes the disc appear as a regular block + device with a 2KB block size, you can put any filesystem you like on + the disc. For example, run: + + # /sbin/mke2fs /dev/pktcdvd/dev_name + + to create an ext2 filesystem on the disc. + + +Links +----- + +See http://fy.chalmers.se/~appro/linux/DVD+RW/ for more information +about DVD writing. diff --git a/drivers/block/Kconfig b/drivers/block/Kconfig index a1d50242b8cd..6a43c807497d 100644 --- a/drivers/block/Kconfig +++ b/drivers/block/Kconfig @@ -356,6 +356,39 @@ config LBD your machine, or if you want to have a raid or loopback device bigger than 2TB. Otherwise say N. +config CDROM_PKTCDVD + tristate "Packet writing on CD/DVD media" + help + If you have a CDROM drive that supports packet writing, say Y to + include preliminary support. It should work with any MMC/Mt Fuji + compliant ATAPI or SCSI drive, which is just about any newer CD + writer. + + Currently only writing to CD-RW, DVD-RW and DVD+RW discs is possible. + DVD-RW disks must be in restricted overwrite mode. + + To compile this driver as a module, choose M here: the + module will be called pktcdvd. + +config CDROM_PKTCDVD_BUFFERS + int "Free buffers for data gathering" + depends on CDROM_PKTCDVD + default "8" + help + This controls the maximum number of active concurrent packets. More + concurrent packets can increase write performance, but also require + more memory. Each concurrent packet will require approximately 64Kb + of non-swappable kernel memory, memory which will be allocated at + pktsetup time. + +config CDROM_PKTCDVD_WCACHE + bool "Enable write caching" + depends on CDROM_PKTCDVD + help + If enabled, write caching will be set for the CD-R/W device. For now + this option is dangerous unless the CD-RW media is known good, as we + don't do deferred write error handling yet. + source "drivers/s390/block/Kconfig" endmenu diff --git a/drivers/block/Makefile b/drivers/block/Makefile index c8fbbf14ce94..1cf09a1c065b 100644 --- a/drivers/block/Makefile +++ b/drivers/block/Makefile @@ -35,6 +35,7 @@ obj-$(CONFIG_BLK_DEV_XD) += xd.o obj-$(CONFIG_BLK_CPQ_DA) += cpqarray.o obj-$(CONFIG_BLK_CPQ_CISS_DA) += cciss.o obj-$(CONFIG_BLK_DEV_DAC960) += DAC960.o +obj-$(CONFIG_CDROM_PKTCDVD) += pktcdvd.o obj-$(CONFIG_BLK_DEV_UMEM) += umem.o obj-$(CONFIG_BLK_DEV_NBD) += nbd.o diff --git a/drivers/block/pktcdvd.c b/drivers/block/pktcdvd.c new file mode 100644 index 000000000000..fb80b6a91f84 --- /dev/null +++ b/drivers/block/pktcdvd.c @@ -0,0 +1,2679 @@ +/* + * Copyright (C) 2000 Jens Axboe + * Copyright (C) 2001-2004 Peter Osterlund + * + * May be copied or modified under the terms of the GNU General Public + * License. See linux/COPYING for more information. + * + * Packet writing layer for ATAPI and SCSI CD-R, CD-RW, DVD-R, and + * DVD-RW devices (aka an exercise in block layer masturbation) + * + * + * TODO: (circa order of when I will fix it) + * - Only able to write on CD-RW media right now. + * - check host application code on media and set it in write page + * - interface for UDF <-> packet to negotiate a new location when a write + * fails. + * - handle OPC, especially for -RW media + * + * Theory of operation: + * + * We use a custom make_request_fn function that forwards reads directly to + * the underlying CD device. Write requests are either attached directly to + * a live packet_data object, or simply stored sequentially in a list for + * later processing by the kcdrwd kernel thread. This driver doesn't use + * any elevator functionally as defined by the elevator_s struct, but the + * underlying CD device uses a standard elevator. + * + * This strategy makes it possible to do very late merging of IO requests. + * A new bio sent to pkt_make_request can be merged with a live packet_data + * object even if the object is in the data gathering state. + * + *************************************************************************/ + +#define VERSION_CODE "v0.2.0a 2004-07-14 Jens Axboe (axboe@suse.de) and petero2@telia.com" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#if PACKET_DEBUG +#define DPRINTK(fmt, args...) printk(KERN_NOTICE fmt, ##args) +#else +#define DPRINTK(fmt, args...) +#endif + +#if PACKET_DEBUG > 1 +#define VPRINTK(fmt, args...) printk(KERN_NOTICE fmt, ##args) +#else +#define VPRINTK(fmt, args...) +#endif + +#define MAX_SPEED 0xffff + +#define ZONE(sector, pd) (((sector) + (pd)->offset) & ~((pd)->settings.size - 1)) + +static struct pktcdvd_device *pkt_devs[MAX_WRITERS]; +static struct proc_dir_entry *pkt_proc; +static int pkt_major; +static struct semaphore ctl_mutex; /* Serialize open/close/setup/teardown */ +static mempool_t *psd_pool; + + +static void pkt_bio_finished(struct pktcdvd_device *pd) +{ + BUG_ON(atomic_read(&pd->cdrw.pending_bios) <= 0); + if (atomic_dec_and_test(&pd->cdrw.pending_bios)) { + VPRINTK("pktcdvd: queue empty\n"); + atomic_set(&pd->iosched.attention, 1); + wake_up(&pd->wqueue); + } +} + +static void pkt_bio_destructor(struct bio *bio) +{ + kfree(bio->bi_io_vec); + kfree(bio); +} + +static struct bio *pkt_bio_alloc(int nr_iovecs) +{ + struct bio_vec *bvl = NULL; + struct bio *bio; + + bio = kmalloc(sizeof(struct bio), GFP_KERNEL); + if (!bio) + goto no_bio; + bio_init(bio); + + bvl = kmalloc(nr_iovecs * sizeof(struct bio_vec), GFP_KERNEL); + if (!bvl) + goto no_bvl; + memset(bvl, 0, nr_iovecs * sizeof(struct bio_vec)); + + bio->bi_max_vecs = nr_iovecs; + bio->bi_io_vec = bvl; + bio->bi_destructor = pkt_bio_destructor; + + return bio; + + no_bvl: + kfree(bio); + no_bio: + return NULL; +} + +/* + * Allocate a packet_data struct + */ +static struct packet_data *pkt_alloc_packet_data(void) +{ + int i; + struct packet_data *pkt; + + pkt = kmalloc(sizeof(struct packet_data), GFP_KERNEL); + if (!pkt) + goto no_pkt; + memset(pkt, 0, sizeof(struct packet_data)); + + pkt->w_bio = pkt_bio_alloc(PACKET_MAX_SIZE); + if (!pkt->w_bio) + goto no_bio; + + for (i = 0; i < PAGES_PER_PACKET; i++) { + pkt->pages[i] = alloc_page(GFP_KERNEL); + if (!pkt->pages[i]) + goto no_page; + } + for (i = 0; i < PAGES_PER_PACKET; i++) + clear_page(page_address(pkt->pages[i])); + + spin_lock_init(&pkt->lock); + + for (i = 0; i < PACKET_MAX_SIZE; i++) { + struct bio *bio = pkt_bio_alloc(1); + if (!bio) + goto no_rd_bio; + pkt->r_bios[i] = bio; + } + + return pkt; + +no_rd_bio: + for (i = 0; i < PACKET_MAX_SIZE; i++) { + struct bio *bio = pkt->r_bios[i]; + if (bio) + bio_put(bio); + } + +no_page: + for (i = 0; i < PAGES_PER_PACKET; i++) + if (pkt->pages[i]) + __free_page(pkt->pages[i]); + bio_put(pkt->w_bio); +no_bio: + kfree(pkt); +no_pkt: + return NULL; +} + +/* + * Free a packet_data struct + */ +static void pkt_free_packet_data(struct packet_data *pkt) +{ + int i; + + for (i = 0; i < PACKET_MAX_SIZE; i++) { + struct bio *bio = pkt->r_bios[i]; + if (bio) + bio_put(bio); + } + for (i = 0; i < PAGES_PER_PACKET; i++) + __free_page(pkt->pages[i]); + bio_put(pkt->w_bio); + kfree(pkt); +} + +static void pkt_shrink_pktlist(struct pktcdvd_device *pd) +{ + struct packet_data *pkt, *next; + + BUG_ON(!list_empty(&pd->cdrw.pkt_active_list)); + + list_for_each_entry_safe(pkt, next, &pd->cdrw.pkt_free_list, list) { + pkt_free_packet_data(pkt); + } +} + +static int pkt_grow_pktlist(struct pktcdvd_device *pd, int nr_packets) +{ + struct packet_data *pkt; + + INIT_LIST_HEAD(&pd->cdrw.pkt_free_list); + INIT_LIST_HEAD(&pd->cdrw.pkt_active_list); + spin_lock_init(&pd->cdrw.active_list_lock); + while (nr_packets > 0) { + pkt = pkt_alloc_packet_data(); + if (!pkt) { + pkt_shrink_pktlist(pd); + return 0; + } + pkt->id = nr_packets; + pkt->pd = pd; + list_add(&pkt->list, &pd->cdrw.pkt_free_list); + nr_packets--; + } + return 1; +} + +static void *pkt_rb_alloc(int gfp_mask, void *data) +{ + return kmalloc(sizeof(struct pkt_rb_node), gfp_mask); +} + +static void pkt_rb_free(void *ptr, void *data) +{ + kfree(ptr); +} + +static inline struct pkt_rb_node *pkt_rbtree_next(struct pkt_rb_node *node) +{ + struct rb_node *n = rb_next(&node->rb_node); + if (!n) + return NULL; + return rb_entry(n, struct pkt_rb_node, rb_node); +} + +static inline void pkt_rbtree_erase(struct pktcdvd_device *pd, struct pkt_rb_node *node) +{ + rb_erase(&node->rb_node, &pd->bio_queue); + mempool_free(node, pd->rb_pool); + pd->bio_queue_size--; + BUG_ON(pd->bio_queue_size < 0); +} + +/* + * Find the first node in the pd->bio_queue rb tree with a starting sector >= s. + */ +static struct pkt_rb_node *pkt_rbtree_find(struct pktcdvd_device *pd, sector_t s) +{ + struct rb_node *n = pd->bio_queue.rb_node; + struct rb_node *next; + struct pkt_rb_node *tmp; + + if (!n) { + BUG_ON(pd->bio_queue_size > 0); + return NULL; + } + + for (;;) { + tmp = rb_entry(n, struct pkt_rb_node, rb_node); + if (s <= tmp->bio->bi_sector) + next = n->rb_left; + else + next = n->rb_right; + if (!next) + break; + n = next; + } + + if (s > tmp->bio->bi_sector) { + tmp = pkt_rbtree_next(tmp); + if (!tmp) + return NULL; + } + BUG_ON(s > tmp->bio->bi_sector); + return tmp; +} + +/* + * Insert a node into the pd->bio_queue rb tree. + */ +static void pkt_rbtree_insert(struct pktcdvd_device *pd, struct pkt_rb_node *node) +{ + struct rb_node **p = &pd->bio_queue.rb_node; + struct rb_node *parent = NULL; + sector_t s = node->bio->bi_sector; + struct pkt_rb_node *tmp; + + while (*p) { + parent = *p; + tmp = rb_entry(parent, struct pkt_rb_node, rb_node); + if (s < tmp->bio->bi_sector) + p = &(*p)->rb_left; + else + p = &(*p)->rb_right; + } + rb_link_node(&node->rb_node, parent, p); + rb_insert_color(&node->rb_node, &pd->bio_queue); + pd->bio_queue_size++; +} + +/* + * Add a bio to a single linked list defined by its head and tail pointers. + */ +static inline void pkt_add_list_last(struct bio *bio, struct bio **list_head, struct bio **list_tail) +{ + bio->bi_next = NULL; + if (*list_tail) { + BUG_ON((*list_head) == NULL); + (*list_tail)->bi_next = bio; + (*list_tail) = bio; + } else { + BUG_ON((*list_head) != NULL); + (*list_head) = bio; + (*list_tail) = bio; + } +} + +/* + * Remove and return the first bio from a single linked list defined by its + * head and tail pointers. + */ +static inline struct bio *pkt_get_list_first(struct bio **list_head, struct bio **list_tail) +{ + struct bio *bio; + + if (*list_head == NULL) + return NULL; + + bio = *list_head; + *list_head = bio->bi_next; + if (*list_head == NULL) + *list_tail = NULL; + + bio->bi_next = NULL; + return bio; +} + +/* + * Send a packet_command to the underlying block device and + * wait for completion. + */ +static int pkt_generic_packet(struct pktcdvd_device *pd, struct packet_command *cgc) +{ + char sense[SCSI_SENSE_BUFFERSIZE]; + request_queue_t *q; + struct request *rq; + DECLARE_COMPLETION(wait); + int err = 0; + + q = bdev_get_queue(pd->bdev); + + rq = blk_get_request(q, (cgc->data_direction == CGC_DATA_WRITE) ? WRITE : READ, + __GFP_WAIT); + rq->errors = 0; + rq->rq_disk = pd->bdev->bd_disk; + rq->bio = NULL; + rq->buffer = NULL; + rq->timeout = 60*HZ; + rq->data = cgc->buffer; + rq->data_len = cgc->buflen; + rq->sense = sense; + memset(sense, 0, sizeof(sense)); + rq->sense_len = 0; + rq->flags |= REQ_BLOCK_PC | REQ_HARDBARRIER; + if (cgc->quiet) + rq->flags |= REQ_QUIET; + memcpy(rq->cmd, cgc->cmd, CDROM_PACKET_SIZE); + if (sizeof(rq->cmd) > CDROM_PACKET_SIZE) + memset(rq->cmd + CDROM_PACKET_SIZE, 0, sizeof(rq->cmd) - CDROM_PACKET_SIZE); + + rq->ref_count++; + rq->flags |= REQ_NOMERGE; + rq->waiting = &wait; + elv_add_request(q, rq, ELEVATOR_INSERT_BACK, 1); + generic_unplug_device(q); + wait_for_completion(&wait); + + if (rq->errors) + err = -EIO; + + blk_put_request(rq); + return err; +} + +/* + * A generic sense dump / resolve mechanism should be implemented across + * all ATAPI + SCSI devices. + */ +static void pkt_dump_sense(struct packet_command *cgc) +{ + static char *info[9] = { "No sense", "Recovered error", "Not ready", + "Medium error", "Hardware error", "Illegal request", + "Unit attention", "Data protect", "Blank check" }; + int i; + struct request_sense *sense = cgc->sense; + + printk("pktcdvd:"); + for (i = 0; i < CDROM_PACKET_SIZE; i++) + printk(" %02x", cgc->cmd[i]); + printk(" - "); + + if (sense == NULL) { + printk("no sense\n"); + return; + } + + printk("sense %02x.%02x.%02x", sense->sense_key, sense->asc, sense->ascq); + + if (sense->sense_key > 8) { + printk(" (INVALID)\n"); + return; + } + + printk(" (%s)\n", info[sense->sense_key]); +} + +/* + * flush the drive cache to media + */ +static int pkt_flush_cache(struct pktcdvd_device *pd) +{ + struct packet_command cgc; + + init_cdrom_command(&cgc, NULL, 0, CGC_DATA_NONE); + cgc.cmd[0] = GPCMD_FLUSH_CACHE; + cgc.quiet = 1; + + /* + * the IMMED bit -- we default to not setting it, although that + * would allow a much faster close, this is safer + */ +#if 0 + cgc.cmd[1] = 1 << 1; +#endif + return pkt_generic_packet(pd, &cgc); +} + +/* + * speed is given as the normal factor, e.g. 4 for 4x + */ +static int pkt_set_speed(struct pktcdvd_device *pd, unsigned write_speed, unsigned read_speed) +{ + struct packet_command cgc; + struct request_sense sense; + int ret; + + init_cdrom_command(&cgc, NULL, 0, CGC_DATA_NONE); + cgc.sense = &sense; + cgc.cmd[0] = GPCMD_SET_SPEED; + cgc.cmd[2] = (read_speed >> 8) & 0xff; + cgc.cmd[3] = read_speed & 0xff; + cgc.cmd[4] = (write_speed >> 8) & 0xff; + cgc.cmd[5] = write_speed & 0xff; + + if ((ret = pkt_generic_packet(pd, &cgc))) + pkt_dump_sense(&cgc); + + return ret; +} + +/* + * Queue a bio for processing by the low-level CD device. Must be called + * from process context. + */ +static void pkt_queue_bio(struct pktcdvd_device *pd, struct bio *bio, int high_prio_read) +{ + spin_lock(&pd->iosched.lock); + if (bio_data_dir(bio) == READ) { + pkt_add_list_last(bio, &pd->iosched.read_queue, + &pd->iosched.read_queue_tail); + if (high_prio_read) + pd->iosched.high_prio_read = 1; + } else { + pkt_add_list_last(bio, &pd->iosched.write_queue, + &pd->iosched.write_queue_tail); + } + spin_unlock(&pd->iosched.lock); + + atomic_set(&pd->iosched.attention, 1); + wake_up(&pd->wqueue); +} + +/* + * Process the queued read/write requests. This function handles special + * requirements for CDRW drives: + * - A cache flush command must be inserted before a read request if the + * previous request was a write. + * - Switching between reading and writing is slow, so don't it more often + * than necessary. + * - Set the read speed according to current usage pattern. When only reading + * from the device, it's best to use the highest possible read speed, but + * when switching often between reading and writing, it's better to have the + * same read and write speeds. + * - Reads originating from user space should have higher priority than reads + * originating from pkt_gather_data, because some process is usually waiting + * on reads of the first kind. + */ +static void pkt_iosched_process_queue(struct pktcdvd_device *pd) +{ + request_queue_t *q; + + if (atomic_read(&pd->iosched.attention) == 0) + return; + atomic_set(&pd->iosched.attention, 0); + + q = bdev_get_queue(pd->bdev); + + for (;;) { + struct bio *bio; + int reads_queued, writes_queued, high_prio_read; + + spin_lock(&pd->iosched.lock); + reads_queued = (pd->iosched.read_queue != NULL); + writes_queued = (pd->iosched.write_queue != NULL); + if (!reads_queued) + pd->iosched.high_prio_read = 0; + high_prio_read = pd->iosched.high_prio_read; + spin_unlock(&pd->iosched.lock); + + if (!reads_queued && !writes_queued) + break; + + if (pd->iosched.writing) { + if (high_prio_read || (!writes_queued && reads_queued)) { + if (atomic_read(&pd->cdrw.pending_bios) > 0) { + VPRINTK("pktcdvd: write, waiting\n"); + break; + } + pkt_flush_cache(pd); + pd->iosched.writing = 0; + } + } else { + if (!reads_queued && writes_queued) { + if (atomic_read(&pd->cdrw.pending_bios) > 0) { + VPRINTK("pktcdvd: read, waiting\n"); + break; + } + pd->iosched.writing = 1; + } + } + + spin_lock(&pd->iosched.lock); + if (pd->iosched.writing) { + bio = pkt_get_list_first(&pd->iosched.write_queue, + &pd->iosched.write_queue_tail); + } else { + bio = pkt_get_list_first(&pd->iosched.read_queue, + &pd->iosched.read_queue_tail); + } + spin_unlock(&pd->iosched.lock); + + if (!bio) + continue; + + if (bio_data_dir(bio) == READ) + pd->iosched.successive_reads += bio->bi_size >> 10; + else + pd->iosched.successive_reads = 0; + if (pd->iosched.successive_reads >= HI_SPEED_SWITCH) { + if (pd->read_speed == pd->write_speed) { + pd->read_speed = MAX_SPEED; + pkt_set_speed(pd, pd->write_speed, pd->read_speed); + } + } else { + if (pd->read_speed != pd->write_speed) { + pd->read_speed = pd->write_speed; + pkt_set_speed(pd, pd->write_speed, pd->read_speed); + } + } + + atomic_inc(&pd->cdrw.pending_bios); + generic_make_request(bio); + } +} + +/* + * Special care is needed if the underlying block device has a small + * max_phys_segments value. + */ +static int pkt_set_segment_merging(struct pktcdvd_device *pd, request_queue_t *q) +{ + if ((pd->settings.size << 9) / CD_FRAMESIZE <= q->max_phys_segments) { + /* + * The cdrom device can handle one segment/frame + */ + clear_bit(PACKET_MERGE_SEGS, &pd->flags); + return 0; + } else if ((pd->settings.size << 9) / PAGE_SIZE <= q->max_phys_segments) { + /* + * We can handle this case at the expense of some extra memory + * copies during write operations + */ + set_bit(PACKET_MERGE_SEGS, &pd->flags); + return 0; + } else { + printk("pktcdvd: cdrom max_phys_segments too small\n"); + return -EIO; + } +} + +/* + * Copy CD_FRAMESIZE bytes from src_bio into a destination page + */ +static void pkt_copy_bio_data(struct bio *src_bio, int seg, int offs, + struct page *dst_page, int dst_offs) +{ + unsigned int copy_size = CD_FRAMESIZE; + + while (copy_size > 0) { + struct bio_vec *src_bvl = bio_iovec_idx(src_bio, seg); + void *vfrom = kmap_atomic(src_bvl->bv_page, KM_USER0) + + src_bvl->bv_offset + offs; + void *vto = page_address(dst_page) + dst_offs; + int len = min_t(int, copy_size, src_bvl->bv_len - offs); + + BUG_ON(len < 0); + memcpy(vto, vfrom, len); + kunmap_atomic(src_bvl->bv_page, KM_USER0); + + seg++; + offs = 0; + dst_offs += len; + copy_size -= len; + } +} + +/* + * Copy all data for this packet to pkt->pages[], so that + * a) The number of required segments for the write bio is minimized, which + * is necessary for some scsi controllers. + * b) The data can be used as cache to avoid read requests if we receive a + * new write request for the same zone. + */ +static void pkt_make_local_copy(struct packet_data *pkt, struct page **pages, int *offsets) +{ + int f, p, offs; + + /* Copy all data to pkt->pages[] */ + p = 0; + offs = 0; + for (f = 0; f < pkt->frames; f++) { + if (pages[f] != pkt->pages[p]) { + void *vfrom = kmap_atomic(pages[f], KM_USER0) + offsets[f]; + void *vto = page_address(pkt->pages[p]) + offs; + memcpy(vto, vfrom, CD_FRAMESIZE); + kunmap_atomic(pages[f], KM_USER0); + pages[f] = pkt->pages[p]; + offsets[f] = offs; + } else { + BUG_ON(offsets[f] != offs); + } + offs += CD_FRAMESIZE; + if (offs >= PAGE_SIZE) { + BUG_ON(offs > PAGE_SIZE); + offs = 0; + p++; + } + } +} + +static int pkt_end_io_read(struct bio *bio, unsigned int bytes_done, int err) +{ + struct packet_data *pkt = bio->bi_private; + struct pktcdvd_device *pd = pkt->pd; + BUG_ON(!pd); + + if (bio->bi_size) + return 1; + + VPRINTK("pkt_end_io_read: bio=%p sec0=%llx sec=%llx err=%d\n", bio, + (unsigned long long)pkt->sector, (unsigned long long)bio->bi_sector, err); + + if (err) + atomic_inc(&pkt->io_errors); + if (atomic_dec_and_test(&pkt->io_wait)) { + atomic_inc(&pkt->run_sm); + wake_up(&pd->wqueue); + } + pkt_bio_finished(pd); + + return 0; +} + +static int pkt_end_io_packet_write(struct bio *bio, unsigned int bytes_done, int err) +{ + struct packet_data *pkt = bio->bi_private; + struct pktcdvd_device *pd = pkt->pd; + BUG_ON(!pd); + + if (bio->bi_size) + return 1; + + VPRINTK("pkt_end_io_packet_write: id=%d, err=%d\n", pkt->id, err); + + pd->stats.pkt_ended++; + + pkt_bio_finished(pd); + atomic_dec(&pkt->io_wait); + atomic_inc(&pkt->run_sm); + wake_up(&pd->wqueue); + return 0; +} + +/* + * Schedule reads for the holes in a packet + */ +static void pkt_gather_data(struct pktcdvd_device *pd, struct packet_data *pkt) +{ + int frames_read = 0; + struct bio *bio; + int f; + char written[PACKET_MAX_SIZE]; + + BUG_ON(!pkt->orig_bios); + + atomic_set(&pkt->io_wait, 0); + atomic_set(&pkt->io_errors, 0); + + if (pkt->cache_valid) { + VPRINTK("pkt_gather_data: zone %llx cached\n", + (unsigned long long)pkt->sector); + goto out_account; + } + + /* + * Figure out which frames we need to read before we can write. + */ + memset(written, 0, sizeof(written)); + spin_lock(&pkt->lock); + for (bio = pkt->orig_bios; bio; bio = bio->bi_next) { + int first_frame = (bio->bi_sector - pkt->sector) / (CD_FRAMESIZE >> 9); + int num_frames = bio->bi_size / CD_FRAMESIZE; + BUG_ON(first_frame < 0); + BUG_ON(first_frame + num_frames > pkt->frames); + for (f = first_frame; f < first_frame + num_frames; f++) + written[f] = 1; + } + spin_unlock(&pkt->lock); + + /* + * Schedule reads for missing parts of the packet. + */ + for (f = 0; f < pkt->frames; f++) { + int p, offset; + if (written[f]) + continue; + bio = pkt->r_bios[f]; + bio_init(bio); + bio->bi_max_vecs = 1; + bio->bi_sector = pkt->sector + f * (CD_FRAMESIZE >> 9); + bio->bi_bdev = pd->bdev; + bio->bi_end_io = pkt_end_io_read; + bio->bi_private = pkt; + + p = (f * CD_FRAMESIZE) / PAGE_SIZE; + offset = (f * CD_FRAMESIZE) % PAGE_SIZE; + VPRINTK("pkt_gather_data: Adding frame %d, page:%p offs:%d\n", + f, pkt->pages[p], offset); + if (!bio_add_page(bio, pkt->pages[p], CD_FRAMESIZE, offset)) + BUG(); + + atomic_inc(&pkt->io_wait); + bio->bi_rw = READ; + pkt_queue_bio(pd, bio, 0); + frames_read++; + } + +out_account: + VPRINTK("pkt_gather_data: need %d frames for zone %llx\n", + frames_read, (unsigned long long)pkt->sector); + pd->stats.pkt_started++; + pd->stats.secs_rg += frames_read * (CD_FRAMESIZE >> 9); + pd->stats.secs_w += pd->settings.size; +} + +/* + * Find a packet matching zone, or the least recently used packet if + * there is no match. + */ +static struct packet_data *pkt_get_packet_data(struct pktcdvd_device *pd, int zone) +{ + struct packet_data *pkt; + + list_for_each_entry(pkt, &pd->cdrw.pkt_free_list, list) { + if (pkt->sector == zone || pkt->list.next == &pd->cdrw.pkt_free_list) { + list_del_init(&pkt->list); + if (pkt->sector != zone) + pkt->cache_valid = 0; + break; + } + } + return pkt; +} + +static void pkt_put_packet_data(struct pktcdvd_device *pd, struct packet_data *pkt) +{ + if (pkt->cache_valid) { + list_add(&pkt->list, &pd->cdrw.pkt_free_list); + } else { + list_add_tail(&pkt->list, &pd->cdrw.pkt_free_list); + } +} + +/* + * recover a failed write, query for relocation if possible + * + * returns 1 if recovery is possible, or 0 if not + * + */ +static int pkt_start_recovery(struct packet_data *pkt) +{ + /* + * FIXME. We need help from the file system to implement + * recovery handling. + */ + return 0; +#if 0 + struct request *rq = pkt->rq; + struct pktcdvd_device *pd = rq->rq_disk->private_data; + struct block_device *pkt_bdev; + struct super_block *sb = NULL; + unsigned long old_block, new_block; + sector_t new_sector; + + pkt_bdev = bdget(kdev_t_to_nr(pd->pkt_dev)); + if (pkt_bdev) { + sb = get_super(pkt_bdev); + bdput(pkt_bdev); + } + + if (!sb) + return 0; + + if (!sb->s_op || !sb->s_op->relocate_blocks) + goto out; + + old_block = pkt->sector / (CD_FRAMESIZE >> 9); + if (sb->s_op->relocate_blocks(sb, old_block, &new_block)) + goto out; + + new_sector = new_block * (CD_FRAMESIZE >> 9); + pkt->sector = new_sector; + + pkt->bio->bi_sector = new_sector; + pkt->bio->bi_next = NULL; + pkt->bio->bi_flags = 1 << BIO_UPTODATE; + pkt->bio->bi_idx = 0; + + BUG_ON(pkt->bio->bi_rw != (1 << BIO_RW)); + BUG_ON(pkt->bio->bi_vcnt != pkt->frames); + BUG_ON(pkt->bio->bi_size != pkt->frames * CD_FRAMESIZE); + BUG_ON(pkt->bio->bi_end_io != pkt_end_io_packet_write); + BUG_ON(pkt->bio->bi_private != pkt); + + drop_super(sb); + return 1; + +out: + drop_super(sb); + return 0; +#endif +} + +static inline void pkt_set_state(struct packet_data *pkt, enum packet_data_state state) +{ +#if PACKET_DEBUG > 1 + static const char *state_name[] = { + "IDLE", "WAITING", "READ_WAIT", "WRITE_WAIT", "RECOVERY", "FINISHED" + }; + enum packet_data_state old_state = pkt->state; + VPRINTK("pkt %2d : s=%6llx %s -> %s\n", pkt->id, (unsigned long long)pkt->sector, + state_name[old_state], state_name[state]); +#endif + pkt->state = state; +} + +/* + * Scan the work queue to see if we can start a new packet. + * returns non-zero if any work was done. + */ +static int pkt_handle_queue(struct pktcdvd_device *pd) +{ + struct packet_data *pkt, *p; + struct bio *bio = NULL; + sector_t zone = 0; /* Suppress gcc warning */ + struct pkt_rb_node *node, *first_node; + struct rb_node *n; + + VPRINTK("handle_queue\n"); + + atomic_set(&pd->scan_queue, 0); + + if (list_empty(&pd->cdrw.pkt_free_list)) { + VPRINTK("handle_queue: no pkt\n"); + return 0; + } + + /* + * Try to find a zone we are not already working on. + */ + spin_lock(&pd->lock); + first_node = pkt_rbtree_find(pd, pd->current_sector); + if (!first_node) { + n = rb_first(&pd->bio_queue); + if (n) + first_node = rb_entry(n, struct pkt_rb_node, rb_node); + } + node = first_node; + while (node) { + bio = node->bio; + zone = ZONE(bio->bi_sector, pd); + list_for_each_entry(p, &pd->cdrw.pkt_active_list, list) { + if (p->sector == zone) + goto try_next_bio; + } + break; +try_next_bio: + node = pkt_rbtree_next(node); + if (!node) { + n = rb_first(&pd->bio_queue); + if (n) + node = rb_entry(n, struct pkt_rb_node, rb_node); + } + if (node == first_node) + node = NULL; + } + spin_unlock(&pd->lock); + if (!bio) { + VPRINTK("handle_queue: no bio\n"); + return 0; + } + + pkt = pkt_get_packet_data(pd, zone); + BUG_ON(!pkt); + + pd->current_sector = zone + pd->settings.size; + pkt->sector = zone; + pkt->frames = pd->settings.size >> 2; + BUG_ON(pkt->frames > PACKET_MAX_SIZE); + pkt->write_size = 0; + + /* + * Scan work queue for bios in the same zone and link them + * to this packet. + */ + spin_lock(&pd->lock); + VPRINTK("pkt_handle_queue: looking for zone %llx\n", (unsigned long long)zone); + while ((node = pkt_rbtree_find(pd, zone)) != NULL) { + bio = node->bio; + VPRINTK("pkt_handle_queue: found zone=%llx\n", + (unsigned long long)ZONE(bio->bi_sector, pd)); + if (ZONE(bio->bi_sector, pd) != zone) + break; + pkt_rbtree_erase(pd, node); + spin_lock(&pkt->lock); + pkt_add_list_last(bio, &pkt->orig_bios, &pkt->orig_bios_tail); + pkt->write_size += bio->bi_size / CD_FRAMESIZE; + spin_unlock(&pkt->lock); + } + spin_unlock(&pd->lock); + + pkt->sleep_time = max(PACKET_WAIT_TIME, 1); + pkt_set_state(pkt, PACKET_WAITING_STATE); + atomic_set(&pkt->run_sm, 1); + + spin_lock(&pd->cdrw.active_list_lock); + list_add(&pkt->list, &pd->cdrw.pkt_active_list); + spin_unlock(&pd->cdrw.active_list_lock); + + return 1; +} + +/* + * Assemble a bio to write one packet and queue the bio for processing + * by the underlying block device. + */ +static void pkt_start_write(struct pktcdvd_device *pd, struct packet_data *pkt) +{ + struct bio *bio; + struct page *pages[PACKET_MAX_SIZE]; + int offsets[PACKET_MAX_SIZE]; + int f; + int frames_write; + + for (f = 0; f < pkt->frames; f++) { + pages[f] = pkt->pages[(f * CD_FRAMESIZE) / PAGE_SIZE]; + offsets[f] = (f * CD_FRAMESIZE) % PAGE_SIZE; + } + + /* + * Fill-in pages[] and offsets[] with data from orig_bios. + */ + frames_write = 0; + spin_lock(&pkt->lock); + for (bio = pkt->orig_bios; bio; bio = bio->bi_next) { + int segment = bio->bi_idx; + int src_offs = 0; + int first_frame = (bio->bi_sector - pkt->sector) / (CD_FRAMESIZE >> 9); + int num_frames = bio->bi_size / CD_FRAMESIZE; + BUG_ON(first_frame < 0); + BUG_ON(first_frame + num_frames > pkt->frames); + for (f = first_frame; f < first_frame + num_frames; f++) { + struct bio_vec *src_bvl = bio_iovec_idx(bio, segment); + + while (src_offs >= src_bvl->bv_len) { + src_offs -= src_bvl->bv_len; + segment++; + BUG_ON(segment >= bio->bi_vcnt); + src_bvl = bio_iovec_idx(bio, segment); + } + + if (src_bvl->bv_len - src_offs >= CD_FRAMESIZE) { + pages[f] = src_bvl->bv_page; + offsets[f] = src_bvl->bv_offset + src_offs; + } else { + pkt_copy_bio_data(bio, segment, src_offs, + pages[f], offsets[f]); + } + src_offs += CD_FRAMESIZE; + frames_write++; + } + } + pkt_set_state(pkt, PACKET_WRITE_WAIT_STATE); + spin_unlock(&pkt->lock); + + VPRINTK("pkt_start_write: Writing %d frames for zone %llx\n", + frames_write, (unsigned long long)pkt->sector); + BUG_ON(frames_write != pkt->write_size); + + if (test_bit(PACKET_MERGE_SEGS, &pd->flags) || (pkt->write_size < pkt->frames)) { + pkt_make_local_copy(pkt, pages, offsets); + pkt->cache_valid = 1; + } else { + pkt->cache_valid = 0; + } + + /* Start the write request */ + bio_init(pkt->w_bio); + pkt->w_bio->bi_max_vecs = PACKET_MAX_SIZE; + pkt->w_bio->bi_sector = pkt->sector; + pkt->w_bio->bi_bdev = pd->bdev; + pkt->w_bio->bi_end_io = pkt_end_io_packet_write; + pkt->w_bio->bi_private = pkt; + for (f = 0; f < pkt->frames; f++) { + if ((f + 1 < pkt->frames) && (pages[f + 1] == pages[f]) && + (offsets[f + 1] = offsets[f] + CD_FRAMESIZE)) { + if (!bio_add_page(pkt->w_bio, pages[f], CD_FRAMESIZE * 2, offsets[f])) + BUG(); + f++; + } else { + if (!bio_add_page(pkt->w_bio, pages[f], CD_FRAMESIZE, offsets[f])) + BUG(); + } + } + VPRINTK("pktcdvd: vcnt=%d\n", pkt->w_bio->bi_vcnt); + + atomic_set(&pkt->io_wait, 1); + pkt->w_bio->bi_rw = WRITE; + pkt_queue_bio(pd, pkt->w_bio, 0); +} + +static void pkt_finish_packet(struct packet_data *pkt, int uptodate) +{ + struct bio *bio, *next; + + if (!uptodate) + pkt->cache_valid = 0; + + /* Finish all bios corresponding to this packet */ + bio = pkt->orig_bios; + while (bio) { + next = bio->bi_next; + bio->bi_next = NULL; + bio_endio(bio, bio->bi_size, uptodate ? 0 : -EIO); + bio = next; + } + pkt->orig_bios = pkt->orig_bios_tail = NULL; +} + +static void pkt_run_state_machine(struct pktcdvd_device *pd, struct packet_data *pkt) +{ + int uptodate; + + VPRINTK("run_state_machine: pkt %d\n", pkt->id); + + for (;;) { + switch (pkt->state) { + case PACKET_WAITING_STATE: + if ((pkt->write_size < pkt->frames) && (pkt->sleep_time > 0)) + return; + + pkt->sleep_time = 0; + pkt_gather_data(pd, pkt); + pkt_set_state(pkt, PACKET_READ_WAIT_STATE); + break; + + case PACKET_READ_WAIT_STATE: + if (atomic_read(&pkt->io_wait) > 0) + return; + + if (atomic_read(&pkt->io_errors) > 0) { + pkt_set_state(pkt, PACKET_RECOVERY_STATE); + } else { + pkt_start_write(pd, pkt); + } + break; + + case PACKET_WRITE_WAIT_STATE: + if (atomic_read(&pkt->io_wait) > 0) + return; + + if (test_bit(BIO_UPTODATE, &pkt->w_bio->bi_flags)) { + pkt_set_state(pkt, PACKET_FINISHED_STATE); + } else { + pkt_set_state(pkt, PACKET_RECOVERY_STATE); + } + break; + + case PACKET_RECOVERY_STATE: + if (pkt_start_recovery(pkt)) { + pkt_start_write(pd, pkt); + } else { + VPRINTK("No recovery possible\n"); + pkt_set_state(pkt, PACKET_FINISHED_STATE); + } + break; + + case PACKET_FINISHED_STATE: + uptodate = test_bit(BIO_UPTODATE, &pkt->w_bio->bi_flags); + pkt_finish_packet(pkt, uptodate); + return; + + default: + BUG(); + break; + } + } +} + +static void pkt_handle_packets(struct pktcdvd_device *pd) +{ + struct packet_data *pkt, *next; + + VPRINTK("pkt_handle_packets\n"); + + /* + * Run state machine for active packets + */ + list_for_each_entry(pkt, &pd->cdrw.pkt_active_list, list) { + if (atomic_read(&pkt->run_sm) > 0) { + atomic_set(&pkt->run_sm, 0); + pkt_run_state_machine(pd, pkt); + } + } + + /* + * Move no longer active packets to the free list + */ + spin_lock(&pd->cdrw.active_list_lock); + list_for_each_entry_safe(pkt, next, &pd->cdrw.pkt_active_list, list) { + if (pkt->state == PACKET_FINISHED_STATE) { + list_del(&pkt->list); + pkt_put_packet_data(pd, pkt); + pkt_set_state(pkt, PACKET_IDLE_STATE); + atomic_set(&pd->scan_queue, 1); + } + } + spin_unlock(&pd->cdrw.active_list_lock); +} + +static void pkt_count_states(struct pktcdvd_device *pd, int *states) +{ + struct packet_data *pkt; + int i; + + for (i = 0; i <= PACKET_NUM_STATES; i++) + states[i] = 0; + + spin_lock(&pd->cdrw.active_list_lock); + list_for_each_entry(pkt, &pd->cdrw.pkt_active_list, list) { + states[pkt->state]++; + } + spin_unlock(&pd->cdrw.active_list_lock); +} + +/* + * kcdrwd is woken up when writes have been queued for one of our + * registered devices + */ +static int kcdrwd(void *foobar) +{ + struct pktcdvd_device *pd = foobar; + struct packet_data *pkt; + long min_sleep_time, residue; + + set_user_nice(current, -20); + + for (;;) { + DECLARE_WAITQUEUE(wait, current); + + /* + * Wait until there is something to do + */ + add_wait_queue(&pd->wqueue, &wait); + for (;;) { + set_current_state(TASK_INTERRUPTIBLE); + + /* Check if we need to run pkt_handle_queue */ + if (atomic_read(&pd->scan_queue) > 0) + goto work_to_do; + + /* Check if we need to run the state machine for some packet */ + list_for_each_entry(pkt, &pd->cdrw.pkt_active_list, list) { + if (atomic_read(&pkt->run_sm) > 0) + goto work_to_do; + } + + /* Check if we need to process the iosched queues */ + if (atomic_read(&pd->iosched.attention) != 0) + goto work_to_do; + + /* Otherwise, go to sleep */ + if (PACKET_DEBUG > 1) { + int states[PACKET_NUM_STATES]; + pkt_count_states(pd, states); + VPRINTK("kcdrwd: i:%d ow:%d rw:%d ww:%d rec:%d fin:%d\n", + states[0], states[1], states[2], states[3], + states[4], states[5]); + } + + min_sleep_time = MAX_SCHEDULE_TIMEOUT; + list_for_each_entry(pkt, &pd->cdrw.pkt_active_list, list) { + if (pkt->sleep_time && pkt->sleep_time < min_sleep_time) + min_sleep_time = pkt->sleep_time; + } + + generic_unplug_device(bdev_get_queue(pd->bdev)); + + VPRINTK("kcdrwd: sleeping\n"); + residue = schedule_timeout(min_sleep_time); + VPRINTK("kcdrwd: wake up\n"); + + /* make swsusp happy with our thread */ + if (current->flags & PF_FREEZE) + refrigerator(PF_FREEZE); + + list_for_each_entry(pkt, &pd->cdrw.pkt_active_list, list) { + if (!pkt->sleep_time) + continue; + pkt->sleep_time -= min_sleep_time - residue; + if (pkt->sleep_time <= 0) { + pkt->sleep_time = 0; + atomic_inc(&pkt->run_sm); + } + } + + if (signal_pending(current)) { + flush_signals(current); + } + if (kthread_should_stop()) + break; + } +work_to_do: + set_current_state(TASK_RUNNING); + remove_wait_queue(&pd->wqueue, &wait); + + if (kthread_should_stop()) + break; + + /* + * if pkt_handle_queue returns true, we can queue + * another request. + */ + while (pkt_handle_queue(pd)) + ; + + /* + * Handle packet state machine + */ + pkt_handle_packets(pd); + + /* + * Handle iosched queues + */ + pkt_iosched_process_queue(pd); + } + + return 0; +} + +static void pkt_print_settings(struct pktcdvd_device *pd) +{ + printk("pktcdvd: %s packets, ", pd->settings.fp ? "Fixed" : "Variable"); + printk("%u blocks, ", pd->settings.size >> 2); + printk("Mode-%c disc\n", pd->settings.block_mode == 8 ? '1' : '2'); +} + +static int pkt_mode_sense(struct pktcdvd_device *pd, struct packet_command *cgc, + int page_code, int page_control) +{ + memset(cgc->cmd, 0, sizeof(cgc->cmd)); + + cgc->cmd[0] = GPCMD_MODE_SENSE_10; + cgc->cmd[2] = page_code | (page_control << 6); + cgc->cmd[7] = cgc->buflen >> 8; + cgc->cmd[8] = cgc->buflen & 0xff; + cgc->data_direction = CGC_DATA_READ; + return pkt_generic_packet(pd, cgc); +} + +static int pkt_mode_select(struct pktcdvd_device *pd, struct packet_command *cgc) +{ + memset(cgc->cmd, 0, sizeof(cgc->cmd)); + memset(cgc->buffer, 0, 2); + cgc->cmd[0] = GPCMD_MODE_SELECT_10; + cgc->cmd[1] = 0x10; /* PF */ + cgc->cmd[7] = cgc->buflen >> 8; + cgc->cmd[8] = cgc->buflen & 0xff; + cgc->data_direction = CGC_DATA_WRITE; + return pkt_generic_packet(pd, cgc); +} + +static int pkt_get_disc_info(struct pktcdvd_device *pd, disc_information *di) +{ + struct packet_command cgc; + int ret; + + /* set up command and get the disc info */ + init_cdrom_command(&cgc, di, sizeof(*di), CGC_DATA_READ); + cgc.cmd[0] = GPCMD_READ_DISC_INFO; + cgc.cmd[8] = cgc.buflen = 2; + cgc.quiet = 1; + + if ((ret = pkt_generic_packet(pd, &cgc))) + return ret; + + /* not all drives have the same disc_info length, so requeue + * packet with the length the drive tells us it can supply + */ + cgc.buflen = be16_to_cpu(di->disc_information_length) + + sizeof(di->disc_information_length); + + if (cgc.buflen > sizeof(disc_information)) + cgc.buflen = sizeof(disc_information); + + cgc.cmd[8] = cgc.buflen; + return pkt_generic_packet(pd, &cgc); +} + +static int pkt_get_track_info(struct pktcdvd_device *pd, __u16 track, __u8 type, track_information *ti) +{ + struct packet_command cgc; + int ret; + + init_cdrom_command(&cgc, ti, 8, CGC_DATA_READ); + cgc.cmd[0] = GPCMD_READ_TRACK_RZONE_INFO; + cgc.cmd[1] = type & 3; + cgc.cmd[4] = (track & 0xff00) >> 8; + cgc.cmd[5] = track & 0xff; + cgc.cmd[8] = 8; + cgc.quiet = 1; + + if ((ret = pkt_generic_packet(pd, &cgc))) + return ret; + + cgc.buflen = be16_to_cpu(ti->track_information_length) + + sizeof(ti->track_information_length); + + if (cgc.buflen > sizeof(track_information)) + cgc.buflen = sizeof(track_information); + + cgc.cmd[8] = cgc.buflen; + return pkt_generic_packet(pd, &cgc); +} + +static int pkt_get_last_written(struct pktcdvd_device *pd, long *last_written) +{ + disc_information di; + track_information ti; + __u32 last_track; + int ret = -1; + + if ((ret = pkt_get_disc_info(pd, &di))) + return ret; + + last_track = (di.last_track_msb << 8) | di.last_track_lsb; + if ((ret = pkt_get_track_info(pd, last_track, 1, &ti))) + return ret; + + /* if this track is blank, try the previous. */ + if (ti.blank) { + last_track--; + if ((ret = pkt_get_track_info(pd, last_track, 1, &ti))) + return ret; + } + + /* if last recorded field is valid, return it. */ + if (ti.lra_v) { + *last_written = be32_to_cpu(ti.last_rec_address); + } else { + /* make it up instead */ + *last_written = be32_to_cpu(ti.track_start) + + be32_to_cpu(ti.track_size); + if (ti.free_blocks) + *last_written -= (be32_to_cpu(ti.free_blocks) + 7); + } + return 0; +} + +/* + * write mode select package based on pd->settings + */ +static int pkt_set_write_settings(struct pktcdvd_device *pd) +{ + struct packet_command cgc; + struct request_sense sense; + write_param_page *wp; + char buffer[128]; + int ret, size; + + /* doesn't apply to DVD+RW */ + if (pd->mmc3_profile == 0x1a) + return 0; + + memset(buffer, 0, sizeof(buffer)); + init_cdrom_command(&cgc, buffer, sizeof(*wp), CGC_DATA_READ); + cgc.sense = &sense; + if ((ret = pkt_mode_sense(pd, &cgc, GPMODE_WRITE_PARMS_PAGE, 0))) { + pkt_dump_sense(&cgc); + return ret; + } + + size = 2 + ((buffer[0] << 8) | (buffer[1] & 0xff)); + pd->mode_offset = (buffer[6] << 8) | (buffer[7] & 0xff); + if (size > sizeof(buffer)) + size = sizeof(buffer); + + /* + * now get it all + */ + init_cdrom_command(&cgc, buffer, size, CGC_DATA_READ); + cgc.sense = &sense; + if ((ret = pkt_mode_sense(pd, &cgc, GPMODE_WRITE_PARMS_PAGE, 0))) { + pkt_dump_sense(&cgc); + return ret; + } + + /* + * write page is offset header + block descriptor length + */ + wp = (write_param_page *) &buffer[sizeof(struct mode_page_header) + pd->mode_offset]; + + wp->fp = pd->settings.fp; + wp->track_mode = pd->settings.track_mode; + wp->write_type = pd->settings.write_type; + wp->data_block_type = pd->settings.block_mode; + + wp->multi_session = 0; + +#ifdef PACKET_USE_LS + wp->link_size = 7; + wp->ls_v = 1; +#endif + + if (wp->data_block_type == PACKET_BLOCK_MODE1) { + wp->session_format = 0; + wp->subhdr2 = 0x20; + } else if (wp->data_block_type == PACKET_BLOCK_MODE2) { + wp->session_format = 0x20; + wp->subhdr2 = 8; +#if 0 + wp->mcn[0] = 0x80; + memcpy(&wp->mcn[1], PACKET_MCN, sizeof(wp->mcn) - 1); +#endif + } else { + /* + * paranoia + */ + printk("pktcdvd: write mode wrong %d\n", wp->data_block_type); + return 1; + } + wp->packet_size = cpu_to_be32(pd->settings.size >> 2); + + cgc.buflen = cgc.cmd[8] = size; + if ((ret = pkt_mode_select(pd, &cgc))) { + pkt_dump_sense(&cgc); + return ret; + } + + pkt_print_settings(pd); + return 0; +} + +/* + * 0 -- we can write to this track, 1 -- we can't + */ +static int pkt_good_track(track_information *ti) +{ + /* + * only good for CD-RW at the moment, not DVD-RW + */ + + /* + * FIXME: only for FP + */ + if (ti->fp == 0) + return 0; + + /* + * "good" settings as per Mt Fuji. + */ + if (ti->rt == 0 && ti->blank == 0 && ti->packet == 1) + return 0; + + if (ti->rt == 0 && ti->blank == 1 && ti->packet == 1) + return 0; + + if (ti->rt == 1 && ti->blank == 0 && ti->packet == 1) + return 0; + + printk("pktcdvd: bad state %d-%d-%d\n", ti->rt, ti->blank, ti->packet); + return 1; +} + +/* + * 0 -- we can write to this disc, 1 -- we can't + */ +static int pkt_good_disc(struct pktcdvd_device *pd, disc_information *di) +{ + switch (pd->mmc3_profile) { + case 0x0a: /* CD-RW */ + case 0xffff: /* MMC3 not supported */ + break; + case 0x1a: /* DVD+RW */ + case 0x13: /* DVD-RW */ + return 0; + default: + printk("pktcdvd: Wrong disc profile (%x)\n", pd->mmc3_profile); + return 1; + } + + /* + * for disc type 0xff we should probably reserve a new track. + * but i'm not sure, should we leave this to user apps? probably. + */ + if (di->disc_type == 0xff) { + printk("pktcdvd: Unknown disc. No track?\n"); + return 1; + } + + if (di->disc_type != 0x20 && di->disc_type != 0) { + printk("pktcdvd: Wrong disc type (%x)\n", di->disc_type); + return 1; + } + + if (di->erasable == 0) { + printk("pktcdvd: Disc not erasable\n"); + return 1; + } + + if (di->border_status == PACKET_SESSION_RESERVED) { + printk("pktcdvd: Can't write to last track (reserved)\n"); + return 1; + } + + return 0; +} + +static int pkt_probe_settings(struct pktcdvd_device *pd) +{ + struct packet_command cgc; + unsigned char buf[12]; + disc_information di; + track_information ti; + int ret, track; + + init_cdrom_command(&cgc, buf, sizeof(buf), CGC_DATA_READ); + cgc.cmd[0] = GPCMD_GET_CONFIGURATION; + cgc.cmd[8] = 8; + ret = pkt_generic_packet(pd, &cgc); + pd->mmc3_profile = ret ? 0xffff : buf[6] << 8 | buf[7]; + + memset(&di, 0, sizeof(disc_information)); + memset(&ti, 0, sizeof(track_information)); + + if ((ret = pkt_get_disc_info(pd, &di))) { + printk("failed get_disc\n"); + return ret; + } + + if (pkt_good_disc(pd, &di)) + return -ENXIO; + + switch (pd->mmc3_profile) { + case 0x1a: /* DVD+RW */ + printk("pktcdvd: inserted media is DVD+RW\n"); + break; + case 0x13: /* DVD-RW */ + printk("pktcdvd: inserted media is DVD-RW\n"); + break; + default: + printk("pktcdvd: inserted media is CD-R%s\n", di.erasable ? "W" : ""); + break; + } + pd->type = di.erasable ? PACKET_CDRW : PACKET_CDR; + + track = 1; /* (di.last_track_msb << 8) | di.last_track_lsb; */ + if ((ret = pkt_get_track_info(pd, track, 1, &ti))) { + printk("pktcdvd: failed get_track\n"); + return ret; + } + + if (pkt_good_track(&ti)) { + printk("pktcdvd: can't write to this track\n"); + return -ENXIO; + } + + /* + * we keep packet size in 512 byte units, makes it easier to + * deal with request calculations. + */ + pd->settings.size = be32_to_cpu(ti.fixed_packet_size) << 2; + if (pd->settings.size == 0) { + printk("pktcdvd: detected zero packet size!\n"); + pd->settings.size = 128; + } + pd->settings.fp = ti.fp; + pd->offset = (be32_to_cpu(ti.track_start) << 2) & (pd->settings.size - 1); + + if (ti.nwa_v) { + pd->nwa = be32_to_cpu(ti.next_writable); + set_bit(PACKET_NWA_VALID, &pd->flags); + } + + /* + * in theory we could use lra on -RW media as well and just zero + * blocks that haven't been written yet, but in practice that + * is just a no-go. we'll use that for -R, naturally. + */ + if (ti.lra_v) { + pd->lra = be32_to_cpu(ti.last_rec_address); + set_bit(PACKET_LRA_VALID, &pd->flags); + } else { + pd->lra = 0xffffffff; + set_bit(PACKET_LRA_VALID, &pd->flags); + } + + /* + * fine for now + */ + pd->settings.link_loss = 7; + pd->settings.write_type = 0; /* packet */ + pd->settings.track_mode = ti.track_mode; + + /* + * mode1 or mode2 disc + */ + switch (ti.data_mode) { + case PACKET_MODE1: + pd->settings.block_mode = PACKET_BLOCK_MODE1; + break; + case PACKET_MODE2: + pd->settings.block_mode = PACKET_BLOCK_MODE2; + break; + default: + printk("pktcdvd: unknown data mode\n"); + return 1; + } + return 0; +} + +/* + * enable/disable write caching on drive + */ +static int pkt_write_caching(struct pktcdvd_device *pd, int set) +{ + struct packet_command cgc; + struct request_sense sense; + unsigned char buf[64]; + int ret; + + memset(buf, 0, sizeof(buf)); + init_cdrom_command(&cgc, buf, sizeof(buf), CGC_DATA_READ); + cgc.sense = &sense; + cgc.buflen = pd->mode_offset + 12; + + /* + * caching mode page might not be there, so quiet this command + */ + cgc.quiet = 1; + + if ((ret = pkt_mode_sense(pd, &cgc, GPMODE_WCACHING_PAGE, 0))) + return ret; + + buf[pd->mode_offset + 10] |= (!!set << 2); + + cgc.buflen = cgc.cmd[8] = 2 + ((buf[0] << 8) | (buf[1] & 0xff)); + ret = pkt_mode_select(pd, &cgc); + if (ret) { + printk("pktcdvd: write caching control failed\n"); + pkt_dump_sense(&cgc); + } else if (!ret && set) + printk("pktcdvd: enabled write caching on %s\n", pd->name); + return ret; +} + +static int pkt_lock_door(struct pktcdvd_device *pd, int lockflag) +{ + struct packet_command cgc; + + init_cdrom_command(&cgc, NULL, 0, CGC_DATA_NONE); + cgc.cmd[0] = GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL; + cgc.cmd[4] = lockflag ? 1 : 0; + return pkt_generic_packet(pd, &cgc); +} + +/* + * Returns drive maximum write speed + */ +static int pkt_get_max_speed(struct pktcdvd_device *pd, unsigned *write_speed) +{ + struct packet_command cgc; + struct request_sense sense; + unsigned char buf[256+18]; + unsigned char *cap_buf; + int ret, offset; + + memset(buf, 0, sizeof(buf)); + cap_buf = &buf[sizeof(struct mode_page_header) + pd->mode_offset]; + init_cdrom_command(&cgc, buf, sizeof(buf), CGC_DATA_UNKNOWN); + cgc.sense = &sense; + + ret = pkt_mode_sense(pd, &cgc, GPMODE_CAPABILITIES_PAGE, 0); + if (ret) { + cgc.buflen = pd->mode_offset + cap_buf[1] + 2 + + sizeof(struct mode_page_header); + ret = pkt_mode_sense(pd, &cgc, GPMODE_CAPABILITIES_PAGE, 0); + if (ret) { + pkt_dump_sense(&cgc); + return ret; + } + } + + offset = 20; /* Obsoleted field, used by older drives */ + if (cap_buf[1] >= 28) + offset = 28; /* Current write speed selected */ + if (cap_buf[1] >= 30) { + /* If the drive reports at least one "Logical Unit Write + * Speed Performance Descriptor Block", use the information + * in the first block. (contains the highest speed) + */ + int num_spdb = (cap_buf[30] << 8) + cap_buf[31]; + if (num_spdb > 0) + offset = 34; + } + + *write_speed = (cap_buf[offset] << 8) | cap_buf[offset + 1]; + return 0; +} + +/* These tables from cdrecord - I don't have orange book */ +/* standard speed CD-RW (1-4x) */ +static char clv_to_speed[16] = { + /* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 */ + 0, 2, 4, 6, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; +/* high speed CD-RW (-10x) */ +static char hs_clv_to_speed[16] = { + /* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 */ + 0, 2, 4, 6, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; +/* ultra high speed CD-RW */ +static char us_clv_to_speed[16] = { + /* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 */ + 0, 2, 4, 8, 0, 0,16, 0,24,32,40,48, 0, 0, 0, 0 +}; + +/* + * reads the maximum media speed from ATIP + */ +static int pkt_media_speed(struct pktcdvd_device *pd, unsigned *speed) +{ + struct packet_command cgc; + struct request_sense sense; + unsigned char buf[64]; + unsigned int size, st, sp; + int ret; + + init_cdrom_command(&cgc, buf, 2, CGC_DATA_READ); + cgc.sense = &sense; + cgc.cmd[0] = GPCMD_READ_TOC_PMA_ATIP; + cgc.cmd[1] = 2; + cgc.cmd[2] = 4; /* READ ATIP */ + cgc.cmd[8] = 2; + ret = pkt_generic_packet(pd, &cgc); + if (ret) { + pkt_dump_sense(&cgc); + return ret; + } + size = ((unsigned int) buf[0]<<8) + buf[1] + 2; + if (size > sizeof(buf)) + size = sizeof(buf); + + init_cdrom_command(&cgc, buf, size, CGC_DATA_READ); + cgc.sense = &sense; + cgc.cmd[0] = GPCMD_READ_TOC_PMA_ATIP; + cgc.cmd[1] = 2; + cgc.cmd[2] = 4; + cgc.cmd[8] = size; + ret = pkt_generic_packet(pd, &cgc); + if (ret) { + pkt_dump_sense(&cgc); + return ret; + } + + if (!buf[6] & 0x40) { + printk("pktcdvd: Disc type is not CD-RW\n"); + return 1; + } + if (!buf[6] & 0x4) { + printk("pktcdvd: A1 values on media are not valid, maybe not CDRW?\n"); + return 1; + } + + st = (buf[6] >> 3) & 0x7; /* disc sub-type */ + + sp = buf[16] & 0xf; /* max speed from ATIP A1 field */ + + /* Info from cdrecord */ + switch (st) { + case 0: /* standard speed */ + *speed = clv_to_speed[sp]; + break; + case 1: /* high speed */ + *speed = hs_clv_to_speed[sp]; + break; + case 2: /* ultra high speed */ + *speed = us_clv_to_speed[sp]; + break; + default: + printk("pktcdvd: Unknown disc sub-type %d\n",st); + return 1; + } + if (*speed) { + printk("pktcdvd: Max. media speed: %d\n",*speed); + return 0; + } else { + printk("pktcdvd: Unknown speed %d for sub-type %d\n",sp,st); + return 1; + } +} + +static int pkt_perform_opc(struct pktcdvd_device *pd) +{ + struct packet_command cgc; + struct request_sense sense; + int ret; + + VPRINTK("pktcdvd: Performing OPC\n"); + + init_cdrom_command(&cgc, NULL, 0, CGC_DATA_NONE); + cgc.sense = &sense; + cgc.timeout = 60*HZ; + cgc.cmd[0] = GPCMD_SEND_OPC; + cgc.cmd[1] = 1; + if ((ret = pkt_generic_packet(pd, &cgc))) + pkt_dump_sense(&cgc); + return ret; +} + +static int pkt_open_write(struct pktcdvd_device *pd) +{ + int ret; + unsigned int write_speed, media_write_speed, read_speed; + + if ((ret = pkt_probe_settings(pd))) { + DPRINTK("pktcdvd: %s failed probe\n", pd->name); + return -EIO; + } + + if ((ret = pkt_set_write_settings(pd))) { + DPRINTK("pktcdvd: %s failed saving write settings\n", pd->name); + return -EIO; + } + + pkt_write_caching(pd, USE_WCACHING); + + if ((ret = pkt_get_max_speed(pd, &write_speed))) + write_speed = 16 * 177; + switch (pd->mmc3_profile) { + case 0x13: /* DVD-RW */ + case 0x1a: /* DVD+RW */ + DPRINTK("pktcdvd: write speed %ukB/s\n", write_speed); + break; + default: + if ((ret = pkt_media_speed(pd, &media_write_speed))) + media_write_speed = 16; + write_speed = min(write_speed, media_write_speed * 177); + DPRINTK("pktcdvd: write speed %ux\n", write_speed / 176); + break; + } + read_speed = write_speed; + + if ((ret = pkt_set_speed(pd, write_speed, read_speed))) { + DPRINTK("pktcdvd: %s couldn't set write speed\n", pd->name); + return -EIO; + } + pd->write_speed = write_speed; + pd->read_speed = read_speed; + + if ((ret = pkt_perform_opc(pd))) { + DPRINTK("pktcdvd: %s Optimum Power Calibration failed\n", pd->name); + } + + return 0; +} + +/* + * called at open time. + */ +static int pkt_open_dev(struct pktcdvd_device *pd, int write) +{ + int ret; + long lba; + request_queue_t *q; + + /* + * We need to re-open the cdrom device without O_NONBLOCK to be able + * to read/write from/to it. It is already opened in O_NONBLOCK mode + * so bdget() can't fail. + */ + bdget(pd->bdev->bd_dev); + if ((ret = blkdev_get(pd->bdev, FMODE_READ, O_RDONLY))) + goto out; + + if ((ret = pkt_get_last_written(pd, &lba))) { + printk("pktcdvd: pkt_get_last_written failed\n"); + goto out_putdev; + } + + set_capacity(pd->disk, lba << 2); + set_capacity(pd->bdev->bd_disk, lba << 2); + bd_set_size(pd->bdev, (loff_t)lba << 11); + + q = bdev_get_queue(pd->bdev); + if (write) { + if ((ret = pkt_open_write(pd))) + goto out_putdev; + /* + * Some CDRW drives can not handle writes larger than one packet, + * even if the size is a multiple of the packet size. + */ + spin_lock_irq(q->queue_lock); + blk_queue_max_sectors(q, pd->settings.size); + spin_unlock_irq(q->queue_lock); + set_bit(PACKET_WRITABLE, &pd->flags); + } else { + pkt_set_speed(pd, MAX_SPEED, MAX_SPEED); + clear_bit(PACKET_WRITABLE, &pd->flags); + } + + if ((ret = pkt_set_segment_merging(pd, q))) + goto out_putdev; + + if (write) + printk("pktcdvd: %lukB available on disc\n", lba << 1); + + return 0; + +out_putdev: + blkdev_put(pd->bdev); +out: + return ret; +} + +/* + * called when the device is closed. makes sure that the device flushes + * the internal cache before we close. + */ +static void pkt_release_dev(struct pktcdvd_device *pd, int flush) +{ + if (flush && pkt_flush_cache(pd)) + DPRINTK("pktcdvd: %s not flushing cache\n", pd->name); + + pkt_lock_door(pd, 0); + + pkt_set_speed(pd, MAX_SPEED, MAX_SPEED); + blkdev_put(pd->bdev); +} + +static struct pktcdvd_device *pkt_find_dev_from_minor(int dev_minor) +{ + if (dev_minor >= MAX_WRITERS) + return NULL; + return pkt_devs[dev_minor]; +} + +static int pkt_open(struct inode *inode, struct file *file) +{ + struct pktcdvd_device *pd = NULL; + int ret; + + VPRINTK("pktcdvd: entering open\n"); + + down(&ctl_mutex); + pd = pkt_find_dev_from_minor(iminor(inode)); + if (!pd) { + ret = -ENODEV; + goto out; + } + BUG_ON(pd->refcnt < 0); + + pd->refcnt++; + if (pd->refcnt == 1) { + if (pkt_open_dev(pd, file->f_mode & FMODE_WRITE)) { + ret = -EIO; + goto out_dec; + } + /* + * needed here as well, since ext2 (among others) may change + * the blocksize at mount time + */ + set_blocksize(inode->i_bdev, CD_FRAMESIZE); + } + + up(&ctl_mutex); + return 0; + +out_dec: + pd->refcnt--; +out: + VPRINTK("pktcdvd: failed open (%d)\n", ret); + up(&ctl_mutex); + return ret; +} + +static int pkt_close(struct inode *inode, struct file *file) +{ + struct pktcdvd_device *pd = inode->i_bdev->bd_disk->private_data; + int ret = 0; + + down(&ctl_mutex); + pd->refcnt--; + BUG_ON(pd->refcnt < 0); + if (pd->refcnt == 0) { + int flush = test_bit(PACKET_WRITABLE, &pd->flags); + pkt_release_dev(pd, flush); + } + up(&ctl_mutex); + return ret; +} + + +static void *psd_pool_alloc(int gfp_mask, void *data) +{ + return kmalloc(sizeof(struct packet_stacked_data), gfp_mask); +} + +static void psd_pool_free(void *ptr, void *data) +{ + kfree(ptr); +} + +static int pkt_end_io_read_cloned(struct bio *bio, unsigned int bytes_done, int err) +{ + struct packet_stacked_data *psd = bio->bi_private; + struct pktcdvd_device *pd = psd->pd; + + if (bio->bi_size) + return 1; + + bio_put(bio); + bio_endio(psd->bio, psd->bio->bi_size, err); + mempool_free(psd, psd_pool); + pkt_bio_finished(pd); + return 0; +} + +static int pkt_make_request(request_queue_t *q, struct bio *bio) +{ + struct pktcdvd_device *pd; + char b[BDEVNAME_SIZE]; + sector_t zone; + struct packet_data *pkt; + int was_empty, blocked_bio; + struct pkt_rb_node *node; + + pd = q->queuedata; + if (!pd) { + printk("pktcdvd: %s incorrect request queue\n", bdevname(bio->bi_bdev, b)); + goto end_io; + } + + /* + * Clone READ bios so we can have our own bi_end_io callback. + */ + if (bio_data_dir(bio) == READ) { + struct bio *cloned_bio = bio_clone(bio, GFP_NOIO); + struct packet_stacked_data *psd = mempool_alloc(psd_pool, GFP_NOIO); + + psd->pd = pd; + psd->bio = bio; + cloned_bio->bi_bdev = pd->bdev; + cloned_bio->bi_private = psd; + cloned_bio->bi_end_io = pkt_end_io_read_cloned; + pd->stats.secs_r += bio->bi_size >> 9; + pkt_queue_bio(pd, cloned_bio, 1); + return 0; + } + + if (!test_bit(PACKET_WRITABLE, &pd->flags)) { + printk("pktcdvd: WRITE for ro device %s (%llu)\n", + pd->name, (unsigned long long)bio->bi_sector); + goto end_io; + } + + if (!bio->bi_size || (bio->bi_size % CD_FRAMESIZE)) { + printk("pktcdvd: wrong bio size\n"); + goto end_io; + } + + blk_queue_bounce(q, &bio); + + zone = ZONE(bio->bi_sector, pd); + VPRINTK("pkt_make_request: start = %6llx stop = %6llx\n", + (unsigned long long)bio->bi_sector, + (unsigned long long)(bio->bi_sector + bio_sectors(bio))); + + /* Check if we have to split the bio */ + { + struct bio_pair *bp; + sector_t last_zone; + int first_sectors; + + last_zone = ZONE(bio->bi_sector + bio_sectors(bio) - 1, pd); + if (last_zone != zone) { + BUG_ON(last_zone != zone + pd->settings.size); + first_sectors = last_zone - bio->bi_sector; + bp = bio_split(bio, bio_split_pool, first_sectors); + BUG_ON(!bp); + pkt_make_request(q, &bp->bio1); + pkt_make_request(q, &bp->bio2); + bio_pair_release(bp); + return 0; + } + } + + /* + * If we find a matching packet in state WAITING or READ_WAIT, we can + * just append this bio to that packet. + */ + spin_lock(&pd->cdrw.active_list_lock); + blocked_bio = 0; + list_for_each_entry(pkt, &pd->cdrw.pkt_active_list, list) { + if (pkt->sector == zone) { + spin_lock(&pkt->lock); + if ((pkt->state == PACKET_WAITING_STATE) || + (pkt->state == PACKET_READ_WAIT_STATE)) { + pkt_add_list_last(bio, &pkt->orig_bios, + &pkt->orig_bios_tail); + pkt->write_size += bio->bi_size / CD_FRAMESIZE; + if ((pkt->write_size >= pkt->frames) && + (pkt->state == PACKET_WAITING_STATE)) { + atomic_inc(&pkt->run_sm); + wake_up(&pd->wqueue); + } + spin_unlock(&pkt->lock); + spin_unlock(&pd->cdrw.active_list_lock); + return 0; + } else { + blocked_bio = 1; + } + spin_unlock(&pkt->lock); + } + } + spin_unlock(&pd->cdrw.active_list_lock); + + /* + * No matching packet found. Store the bio in the work queue. + */ + node = mempool_alloc(pd->rb_pool, GFP_NOIO); + BUG_ON(!node); + node->bio = bio; + spin_lock(&pd->lock); + BUG_ON(pd->bio_queue_size < 0); + was_empty = (pd->bio_queue_size == 0); + pkt_rbtree_insert(pd, node); + spin_unlock(&pd->lock); + + /* + * Wake up the worker thread. + */ + atomic_set(&pd->scan_queue, 1); + if (was_empty) { + /* This wake_up is required for correct operation */ + wake_up(&pd->wqueue); + } else if (!list_empty(&pd->cdrw.pkt_free_list) && !blocked_bio) { + /* + * This wake up is not required for correct operation, + * but improves performance in some cases. + */ + wake_up(&pd->wqueue); + } + return 0; +end_io: + bio_io_error(bio, bio->bi_size); + return 0; +} + + + +static int pkt_merge_bvec(request_queue_t *q, struct bio *bio, struct bio_vec *bvec) +{ + struct pktcdvd_device *pd = q->queuedata; + sector_t zone = ZONE(bio->bi_sector, pd); + int used = ((bio->bi_sector - zone) << 9) + bio->bi_size; + int remaining = (pd->settings.size << 9) - used; + int remaining2; + + /* + * A bio <= PAGE_SIZE must be allowed. If it crosses a packet + * boundary, pkt_make_request() will split the bio. + */ + remaining2 = PAGE_SIZE - bio->bi_size; + remaining = max(remaining, remaining2); + + BUG_ON(remaining < 0); + return remaining; +} + +static void pkt_init_queue(struct pktcdvd_device *pd) +{ + request_queue_t *q = pd->disk->queue; + + blk_queue_make_request(q, pkt_make_request); + blk_queue_hardsect_size(q, CD_FRAMESIZE); + blk_queue_max_sectors(q, PACKET_MAX_SECTORS); + blk_queue_merge_bvec(q, pkt_merge_bvec); + q->queuedata = pd; +} + +static int pkt_seq_show(struct seq_file *m, void *p) +{ + struct pktcdvd_device *pd = m->private; + char *msg; + char bdev_buf[BDEVNAME_SIZE]; + int states[PACKET_NUM_STATES]; + + seq_printf(m, "Writer %s mapped to %s:\n", pd->name, + bdevname(pd->bdev, bdev_buf)); + + seq_printf(m, "\nSettings:\n"); + seq_printf(m, "\tpacket size:\t\t%dkB\n", pd->settings.size / 2); + + if (pd->settings.write_type == 0) + msg = "Packet"; + else + msg = "Unknown"; + seq_printf(m, "\twrite type:\t\t%s\n", msg); + + seq_printf(m, "\tpacket type:\t\t%s\n", pd->settings.fp ? "Fixed" : "Variable"); + seq_printf(m, "\tlink loss:\t\t%d\n", pd->settings.link_loss); + + seq_printf(m, "\ttrack mode:\t\t%d\n", pd->settings.track_mode); + + if (pd->settings.block_mode == PACKET_BLOCK_MODE1) + msg = "Mode 1"; + else if (pd->settings.block_mode == PACKET_BLOCK_MODE2) + msg = "Mode 2"; + else + msg = "Unknown"; + seq_printf(m, "\tblock mode:\t\t%s\n", msg); + + seq_printf(m, "\nStatistics:\n"); + seq_printf(m, "\tpackets started:\t%lu\n", pd->stats.pkt_started); + seq_printf(m, "\tpackets ended:\t\t%lu\n", pd->stats.pkt_ended); + seq_printf(m, "\twritten:\t\t%lukB\n", pd->stats.secs_w >> 1); + seq_printf(m, "\tread gather:\t\t%lukB\n", pd->stats.secs_rg >> 1); + seq_printf(m, "\tread:\t\t\t%lukB\n", pd->stats.secs_r >> 1); + + seq_printf(m, "\nMisc:\n"); + seq_printf(m, "\treference count:\t%d\n", pd->refcnt); + seq_printf(m, "\tflags:\t\t\t0x%lx\n", pd->flags); + seq_printf(m, "\tread speed:\t\t%ukB/s\n", pd->read_speed); + seq_printf(m, "\twrite speed:\t\t%ukB/s\n", pd->write_speed); + seq_printf(m, "\tstart offset:\t\t%lu\n", pd->offset); + seq_printf(m, "\tmode page offset:\t%u\n", pd->mode_offset); + + seq_printf(m, "\nQueue state:\n"); + seq_printf(m, "\tbios queued:\t\t%d\n", pd->bio_queue_size); + seq_printf(m, "\tbios pending:\t\t%d\n", atomic_read(&pd->cdrw.pending_bios)); + seq_printf(m, "\tcurrent sector:\t\t0x%llx\n", (unsigned long long)pd->current_sector); + + pkt_count_states(pd, states); + seq_printf(m, "\tstate:\t\t\ti:%d ow:%d rw:%d ww:%d rec:%d fin:%d\n", + states[0], states[1], states[2], states[3], states[4], states[5]); + + return 0; +} + +static int pkt_seq_open(struct inode *inode, struct file *file) +{ + return single_open(file, pkt_seq_show, PDE(inode)->data); +} + +static struct file_operations pkt_proc_fops = { + .open = pkt_seq_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release +}; + +static int pkt_new_dev(struct pktcdvd_device *pd, dev_t dev) +{ + int i; + int ret = 0; + char b[BDEVNAME_SIZE]; + struct proc_dir_entry *proc; + struct block_device *bdev; + + if (pd->pkt_dev == dev) { + printk("pktcdvd: Recursive setup not allowed\n"); + return -EBUSY; + } + for (i = 0; i < MAX_WRITERS; i++) { + struct pktcdvd_device *pd2 = pkt_devs[i]; + if (!pd2) + continue; + if (pd2->bdev->bd_dev == dev) { + printk("pktcdvd: %s already setup\n", bdevname(pd2->bdev, b)); + return -EBUSY; + } + if (pd2->pkt_dev == dev) { + printk("pktcdvd: Can't chain pktcdvd devices\n"); + return -EBUSY; + } + } + + bdev = bdget(dev); + if (!bdev) + return -ENOMEM; + ret = blkdev_get(bdev, FMODE_READ, O_RDONLY | O_NONBLOCK); + if (ret) + return ret; + + /* This is safe, since we have a reference from open(). */ + __module_get(THIS_MODULE); + + if (!pkt_grow_pktlist(pd, CONFIG_CDROM_PKTCDVD_BUFFERS)) { + printk("pktcdvd: not enough memory for buffers\n"); + ret = -ENOMEM; + goto out_mem; + } + + pd->bdev = bdev; + set_blocksize(bdev, CD_FRAMESIZE); + + pkt_init_queue(pd); + + atomic_set(&pd->cdrw.pending_bios, 0); + pd->cdrw.thread = kthread_run(kcdrwd, pd, "%s", pd->name); + if (IS_ERR(pd->cdrw.thread)) { + printk("pktcdvd: can't start kernel thread\n"); + ret = -ENOMEM; + goto out_thread; + } + + proc = create_proc_entry(pd->name, 0, pkt_proc); + if (proc) { + proc->data = pd; + proc->proc_fops = &pkt_proc_fops; + } + DPRINTK("pktcdvd: writer %s mapped to %s\n", pd->name, bdevname(bdev, b)); + return 0; + +out_thread: + pkt_shrink_pktlist(pd); +out_mem: + blkdev_put(bdev); + /* This is safe: open() is still holding a reference. */ + module_put(THIS_MODULE); + return ret; +} + +static int pkt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct pktcdvd_device *pd = inode->i_bdev->bd_disk->private_data; + + VPRINTK("pkt_ioctl: cmd %x, dev %d:%d\n", cmd, imajor(inode), iminor(inode)); + BUG_ON(!pd); + + switch (cmd) { + /* + * forward selected CDROM ioctls to CD-ROM, for UDF + */ + case CDROMMULTISESSION: + case CDROMREADTOCENTRY: + case CDROM_LAST_WRITTEN: + case CDROM_SEND_PACKET: + case SCSI_IOCTL_SEND_COMMAND: + return ioctl_by_bdev(pd->bdev, cmd, arg); + + case CDROMEJECT: + /* + * The door gets locked when the device is opened, so we + * have to unlock it or else the eject command fails. + */ + pkt_lock_door(pd, 0); + return ioctl_by_bdev(pd->bdev, cmd, arg); + + default: + printk("pktcdvd: Unknown ioctl for %s (%x)\n", pd->name, cmd); + return -ENOTTY; + } + + return 0; +} + +static int pkt_media_changed(struct gendisk *disk) +{ + struct pktcdvd_device *pd = disk->private_data; + struct gendisk *attached_disk; + + if (!pd) + return 0; + if (!pd->bdev) + return 0; + attached_disk = pd->bdev->bd_disk; + if (!attached_disk) + return 0; + return attached_disk->fops->media_changed(attached_disk); +} + +static struct block_device_operations pktcdvd_ops = { + .owner = THIS_MODULE, + .open = pkt_open, + .release = pkt_close, + .ioctl = pkt_ioctl, + .media_changed = pkt_media_changed, +}; + +/* + * Set up mapping from pktcdvd device to CD-ROM device. + */ +static int pkt_setup_dev(struct pkt_ctrl_command *ctrl_cmd) +{ + int idx; + int ret = -ENOMEM; + struct pktcdvd_device *pd; + struct gendisk *disk; + dev_t dev = new_decode_dev(ctrl_cmd->dev); + + for (idx = 0; idx < MAX_WRITERS; idx++) + if (!pkt_devs[idx]) + break; + if (idx == MAX_WRITERS) { + printk("pktcdvd: max %d writers supported\n", MAX_WRITERS); + return -EBUSY; + } + + pd = kmalloc(sizeof(struct pktcdvd_device), GFP_KERNEL); + if (!pd) + return ret; + memset(pd, 0, sizeof(struct pktcdvd_device)); + + pd->rb_pool = mempool_create(PKT_RB_POOL_SIZE, pkt_rb_alloc, pkt_rb_free, NULL); + if (!pd->rb_pool) + goto out_mem; + + disk = alloc_disk(1); + if (!disk) + goto out_mem; + pd->disk = disk; + + spin_lock_init(&pd->lock); + spin_lock_init(&pd->iosched.lock); + sprintf(pd->name, "pktcdvd%d", idx); + init_waitqueue_head(&pd->wqueue); + pd->bio_queue = RB_ROOT; + + disk->major = pkt_major; + disk->first_minor = idx; + disk->fops = &pktcdvd_ops; + disk->flags = GENHD_FL_REMOVABLE; + sprintf(disk->disk_name, "pktcdvd%d", idx); + disk->private_data = pd; + disk->queue = blk_alloc_queue(GFP_KERNEL); + if (!disk->queue) + goto out_mem2; + + pd->pkt_dev = MKDEV(disk->major, disk->first_minor); + ret = pkt_new_dev(pd, dev); + if (ret) + goto out_new_dev; + + add_disk(disk); + pkt_devs[idx] = pd; + ctrl_cmd->pkt_dev = new_encode_dev(pd->pkt_dev); + return 0; + +out_new_dev: + blk_put_queue(disk->queue); +out_mem2: + put_disk(disk); +out_mem: + if (pd->rb_pool) + mempool_destroy(pd->rb_pool); + kfree(pd); + return ret; +} + +/* + * Tear down mapping from pktcdvd device to CD-ROM device. + */ +static int pkt_remove_dev(struct pkt_ctrl_command *ctrl_cmd) +{ + struct pktcdvd_device *pd; + int idx; + dev_t pkt_dev = new_decode_dev(ctrl_cmd->pkt_dev); + + for (idx = 0; idx < MAX_WRITERS; idx++) { + pd = pkt_devs[idx]; + if (pd && (pd->pkt_dev == pkt_dev)) + break; + } + if (idx == MAX_WRITERS) { + DPRINTK("pktcdvd: dev not setup\n"); + return -ENXIO; + } + + if (pd->refcnt > 0) + return -EBUSY; + + if (!IS_ERR(pd->cdrw.thread)) + kthread_stop(pd->cdrw.thread); + + blkdev_put(pd->bdev); + + pkt_shrink_pktlist(pd); + + remove_proc_entry(pd->name, pkt_proc); + DPRINTK("pktcdvd: writer %s unmapped\n", pd->name); + + del_gendisk(pd->disk); + blk_put_queue(pd->disk->queue); + put_disk(pd->disk); + + pkt_devs[idx] = NULL; + mempool_destroy(pd->rb_pool); + kfree(pd); + + /* This is safe: open() is still holding a reference. */ + module_put(THIS_MODULE); + return 0; +} + +static void pkt_get_status(struct pkt_ctrl_command *ctrl_cmd) +{ + struct pktcdvd_device *pd = pkt_find_dev_from_minor(ctrl_cmd->dev_index); + if (pd) { + ctrl_cmd->dev = new_encode_dev(pd->bdev->bd_dev); + ctrl_cmd->pkt_dev = new_encode_dev(pd->pkt_dev); + } else { + ctrl_cmd->dev = 0; + ctrl_cmd->pkt_dev = 0; + } + ctrl_cmd->num_devices = MAX_WRITERS; +} + +static int pkt_ctl_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + void __user *argp = (void __user *)arg; + struct pkt_ctrl_command ctrl_cmd; + int ret = 0; + + if (cmd != PACKET_CTRL_CMD) + return -ENOTTY; + + if (copy_from_user(&ctrl_cmd, argp, sizeof(struct pkt_ctrl_command))) + return -EFAULT; + + switch (ctrl_cmd.command) { + case PKT_CTRL_CMD_SETUP: + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + down(&ctl_mutex); + ret = pkt_setup_dev(&ctrl_cmd); + up(&ctl_mutex); + break; + case PKT_CTRL_CMD_TEARDOWN: + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + down(&ctl_mutex); + ret = pkt_remove_dev(&ctrl_cmd); + up(&ctl_mutex); + break; + case PKT_CTRL_CMD_STATUS: + down(&ctl_mutex); + pkt_get_status(&ctrl_cmd); + up(&ctl_mutex); + break; + default: + return -ENOTTY; + } + + if (copy_to_user(argp, &ctrl_cmd, sizeof(struct pkt_ctrl_command))) + return -EFAULT; + return ret; +} + + +static struct file_operations pkt_ctl_fops = { + .ioctl = pkt_ctl_ioctl, + .owner = THIS_MODULE, +}; + +static struct miscdevice pkt_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "pktcdvd", + .devfs_name = "pktcdvd/control", + .fops = &pkt_ctl_fops +}; + +int pkt_init(void) +{ + int ret; + + psd_pool = mempool_create(PSD_POOL_SIZE, psd_pool_alloc, psd_pool_free, NULL); + if (!psd_pool) + return -ENOMEM; + + ret = register_blkdev(pkt_major, "pktcdvd"); + if (ret < 0) { + printk("pktcdvd: Unable to register block device\n"); + goto out2; + } + if (!pkt_major) + pkt_major = ret; + + ret = misc_register(&pkt_misc); + if (ret) { + printk("pktcdvd: Unable to register misc device\n"); + goto out; + } + + init_MUTEX(&ctl_mutex); + + pkt_proc = proc_mkdir("pktcdvd", proc_root_driver); + + DPRINTK("pktcdvd: %s\n", VERSION_CODE); + return 0; + +out: + unregister_blkdev(pkt_major, "pktcdvd"); +out2: + mempool_destroy(psd_pool); + return ret; +} + +void pkt_exit(void) +{ + remove_proc_entry("pktcdvd", proc_root_driver); + misc_deregister(&pkt_misc); + unregister_blkdev(pkt_major, "pktcdvd"); + mempool_destroy(psd_pool); +} + +MODULE_DESCRIPTION("Packet writing layer for CD/DVD drives"); +MODULE_AUTHOR("Jens Axboe "); +MODULE_LICENSE("GPL"); + +module_init(pkt_init); +module_exit(pkt_exit); diff --git a/drivers/cdrom/Makefile b/drivers/cdrom/Makefile index 5c484f3b3e58..4a8351753e07 100644 --- a/drivers/cdrom/Makefile +++ b/drivers/cdrom/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_BLK_DEV_IDECD) += cdrom.o obj-$(CONFIG_BLK_DEV_SR) += cdrom.o obj-$(CONFIG_PARIDE_PCD) += cdrom.o +obj-$(CONFIG_CDROM_PKTCDVD) += cdrom.o obj-$(CONFIG_AZTCD) += aztcd.o obj-$(CONFIG_CDU31A) += cdu31a.o cdrom.o diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c index 468eec62246f..895f245fcced 100644 --- a/drivers/ide/ide-cd.c +++ b/drivers/ide/ide-cd.c @@ -2001,7 +2001,7 @@ ide_do_rw_cdrom (ide_drive_t *drive, struct request *rq, sector_t block) } CDROM_CONFIG_FLAGS(drive)->seeking = 0; } - if (IDE_LARGE_SEEK(info->last_block, block, IDECD_SEEK_THRESHOLD) && drive->dsc_overlap) { + if ((rq_data_dir(rq) == READ) && IDE_LARGE_SEEK(info->last_block, block, IDECD_SEEK_THRESHOLD) && drive->dsc_overlap) { action = cdrom_start_seek(drive, block); } else { if (rq_data_dir(rq) == READ) @@ -2962,8 +2962,10 @@ int ide_cdrom_probe_capabilities (ide_drive_t *drive) CDROM_CONFIG_FLAGS(drive)->no_eject = 0; if (cap.cd_r_write) CDROM_CONFIG_FLAGS(drive)->cd_r = 1; - if (cap.cd_rw_write) + if (cap.cd_rw_write) { CDROM_CONFIG_FLAGS(drive)->cd_rw = 1; + CDROM_CONFIG_FLAGS(drive)->ram = 1; + } if (cap.test_write) CDROM_CONFIG_FLAGS(drive)->test_write = 1; if (cap.dvd_ram_read || cap.dvd_r_read || cap.dvd_rom) diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c index 20a10a3ed5e4..da06adf48834 100644 --- a/drivers/scsi/sr.c +++ b/drivers/scsi/sr.c @@ -876,10 +876,10 @@ static void get_capabilities(struct scsi_cd *cd) cd->cdi.mask |= CDC_CLOSE_TRAY; */ /* - * if DVD-RAM of MRW-W, we are randomly writeable + * if DVD-RAM, MRW-W or CD-RW, we are randomly writable */ - if ((cd->cdi.mask & (CDC_DVD_RAM | CDC_MRW_W | CDC_RAM)) != - (CDC_DVD_RAM | CDC_MRW_W | CDC_RAM)) { + if ((cd->cdi.mask & (CDC_DVD_RAM | CDC_MRW_W | CDC_RAM | CDC_CD_RW)) != + (CDC_DVD_RAM | CDC_MRW_W | CDC_RAM | CDC_CD_RW)) { cd->device->writeable = 1; } diff --git a/fs/compat_ioctl.c b/fs/compat_ioctl.c index 2edf63444ccb..5642ccc235e8 100644 --- a/fs/compat_ioctl.c +++ b/fs/compat_ioctl.c @@ -115,6 +115,7 @@ #include #include #include +#include #include diff --git a/include/linux/cdrom.h b/include/linux/cdrom.h index 73a1ade6a8e9..1c1f5efffd64 100644 --- a/include/linux/cdrom.h +++ b/include/linux/cdrom.h @@ -499,6 +499,7 @@ struct cdrom_generic_command #define GPMODE_VENDOR_PAGE 0x00 #define GPMODE_R_W_ERROR_PAGE 0x01 #define GPMODE_WRITE_PARMS_PAGE 0x05 +#define GPMODE_WCACHING_PAGE 0x08 #define GPMODE_AUDIO_CTL_PAGE 0x0e #define GPMODE_POWER_PAGE 0x1a #define GPMODE_FAULT_FAIL_PAGE 0x1c diff --git a/include/linux/compat_ioctl.h b/include/linux/compat_ioctl.h index 77f59742c407..71da7d1260cd 100644 --- a/include/linux/compat_ioctl.h +++ b/include/linux/compat_ioctl.h @@ -382,6 +382,8 @@ COMPATIBLE_IOCTL(CDROMREADALL) COMPATIBLE_IOCTL(DVD_READ_STRUCT) COMPATIBLE_IOCTL(DVD_WRITE_STRUCT) COMPATIBLE_IOCTL(DVD_AUTH) +/* pktcdvd */ +COMPATIBLE_IOCTL(PACKET_CTRL_CMD) /* Big L */ ULONG_IOCTL(LOOP_SET_FD) ULONG_IOCTL(LOOP_CHANGE_FD) diff --git a/include/linux/pktcdvd.h b/include/linux/pktcdvd.h new file mode 100644 index 000000000000..4e2d2a942ecb --- /dev/null +++ b/include/linux/pktcdvd.h @@ -0,0 +1,275 @@ +/* + * Copyright (C) 2000 Jens Axboe + * Copyright (C) 2001-2004 Peter Osterlund + * + * May be copied or modified under the terms of the GNU General Public + * License. See linux/COPYING for more information. + * + * Packet writing layer for ATAPI and SCSI CD-R, CD-RW, DVD-R, and + * DVD-RW devices. + * + */ +#ifndef __PKTCDVD_H +#define __PKTCDVD_H + +#include + +/* + * 1 for normal debug messages, 2 is very verbose. 0 to turn it off. + */ +#define PACKET_DEBUG 1 + +#define MAX_WRITERS 8 + +#define PKT_RB_POOL_SIZE 512 + +/* + * How long we should hold a non-full packet before starting data gathering. + */ +#define PACKET_WAIT_TIME (HZ * 5 / 1000) + +/* + * use drive write caching -- we need deferred error handling to be + * able to sucessfully recover with this option (drive will return good + * status as soon as the cdb is validated). + */ +#if defined(CONFIG_CDROM_PKTCDVD_WCACHE) +#define USE_WCACHING 1 +#else +#define USE_WCACHING 0 +#endif + +/* + * No user-servicable parts beyond this point -> + */ + +/* + * device types + */ +#define PACKET_CDR 1 +#define PACKET_CDRW 2 +#define PACKET_DVDR 3 +#define PACKET_DVDRW 4 + +/* + * flags + */ +#define PACKET_WRITABLE 1 /* pd is writable */ +#define PACKET_NWA_VALID 2 /* next writable address valid */ +#define PACKET_LRA_VALID 3 /* last recorded address valid */ +#define PACKET_MERGE_SEGS 4 /* perform segment merging to keep */ + /* underlying cdrom device happy */ + +/* + * Disc status -- from READ_DISC_INFO + */ +#define PACKET_DISC_EMPTY 0 +#define PACKET_DISC_INCOMPLETE 1 +#define PACKET_DISC_COMPLETE 2 +#define PACKET_DISC_OTHER 3 + +/* + * write type, and corresponding data block type + */ +#define PACKET_MODE1 1 +#define PACKET_MODE2 2 +#define PACKET_BLOCK_MODE1 8 +#define PACKET_BLOCK_MODE2 10 + +/* + * Last session/border status + */ +#define PACKET_SESSION_EMPTY 0 +#define PACKET_SESSION_INCOMPLETE 1 +#define PACKET_SESSION_RESERVED 2 +#define PACKET_SESSION_COMPLETE 3 + +#define PACKET_MCN "4a656e734178626f65323030300000" + +#undef PACKET_USE_LS + +#define PKT_CTRL_CMD_SETUP 0 +#define PKT_CTRL_CMD_TEARDOWN 1 +#define PKT_CTRL_CMD_STATUS 2 + +struct pkt_ctrl_command { + __u32 command; /* in: Setup, teardown, status */ + __u32 dev_index; /* in/out: Device index */ + __u32 dev; /* in/out: Device nr for cdrw device */ + __u32 pkt_dev; /* in/out: Device nr for packet device */ + __u32 num_devices; /* out: Largest device index + 1 */ + __u32 padding; /* Not used */ +}; + +/* + * packet ioctls + */ +#define PACKET_IOCTL_MAGIC ('X') +#define PACKET_CTRL_CMD _IOWR(PACKET_IOCTL_MAGIC, 1, struct pkt_ctrl_command) + +#ifdef __KERNEL__ +#include +#include +#include + +struct packet_settings +{ + __u8 size; /* packet size in (512 byte) sectors */ + __u8 fp; /* fixed packets */ + __u8 link_loss; /* the rest is specified + * as per Mt Fuji */ + __u8 write_type; + __u8 track_mode; + __u8 block_mode; +}; + +/* + * Very crude stats for now + */ +struct packet_stats +{ + unsigned long pkt_started; + unsigned long pkt_ended; + unsigned long secs_w; + unsigned long secs_rg; + unsigned long secs_r; +}; + +struct packet_cdrw +{ + struct list_head pkt_free_list; + struct list_head pkt_active_list; + spinlock_t active_list_lock; /* Serialize access to pkt_active_list */ + struct task_struct *thread; + atomic_t pending_bios; +}; + +/* + * Switch to high speed reading after reading this many kilobytes + * with no interspersed writes. + */ +#define HI_SPEED_SWITCH 512 + +struct packet_iosched +{ + atomic_t attention; /* Set to non-zero when queue processing is needed */ + int writing; /* Non-zero when writing, zero when reading */ + spinlock_t lock; /* Protecting read/write queue manipulations */ + struct bio *read_queue; + struct bio *read_queue_tail; + struct bio *write_queue; + struct bio *write_queue_tail; + int high_prio_read; /* An important read request has been queued */ + int successive_reads; +}; + +/* + * 32 buffers of 2048 bytes + */ +#define PACKET_MAX_SIZE 32 +#define PAGES_PER_PACKET (PACKET_MAX_SIZE * CD_FRAMESIZE / PAGE_SIZE) +#define PACKET_MAX_SECTORS (PACKET_MAX_SIZE * CD_FRAMESIZE >> 9) + +enum packet_data_state { + PACKET_IDLE_STATE, /* Not used at the moment */ + PACKET_WAITING_STATE, /* Waiting for more bios to arrive, so */ + /* we don't have to do as much */ + /* data gathering */ + PACKET_READ_WAIT_STATE, /* Waiting for reads to fill in holes */ + PACKET_WRITE_WAIT_STATE, /* Waiting for the write to complete */ + PACKET_RECOVERY_STATE, /* Recover after read/write errors */ + PACKET_FINISHED_STATE, /* After write has finished */ + + PACKET_NUM_STATES /* Number of possible states */ +}; + +/* + * Information needed for writing a single packet + */ +struct pktcdvd_device; + +struct packet_data +{ + struct list_head list; + + spinlock_t lock; /* Lock protecting state transitions and */ + /* orig_bios list */ + + struct bio *orig_bios; /* Original bios passed to pkt_make_request */ + struct bio *orig_bios_tail;/* that will be handled by this packet */ + int write_size; /* Total size of all bios in the orig_bios */ + /* list, measured in number of frames */ + + struct bio *w_bio; /* The bio we will send to the real CD */ + /* device once we have all data for the */ + /* packet we are going to write */ + sector_t sector; /* First sector in this packet */ + int frames; /* Number of frames in this packet */ + + enum packet_data_state state; /* Current state */ + atomic_t run_sm; /* Incremented whenever the state */ + /* machine needs to be run */ + long sleep_time; /* Set this to non-zero to make the state */ + /* machine run after this many jiffies. */ + + atomic_t io_wait; /* Number of pending IO operations */ + atomic_t io_errors; /* Number of read/write errors during IO */ + + struct bio *r_bios[PACKET_MAX_SIZE]; /* bios to use during data gathering */ + struct page *pages[PAGES_PER_PACKET]; + + int cache_valid; /* If non-zero, the data for the zone defined */ + /* by the sector variable is completely cached */ + /* in the pages[] vector. */ + + int id; /* ID number for debugging */ + struct pktcdvd_device *pd; +}; + +struct pkt_rb_node { + struct rb_node rb_node; + struct bio *bio; +}; + +struct packet_stacked_data +{ + struct bio *bio; /* Original read request bio */ + struct pktcdvd_device *pd; +}; +#define PSD_POOL_SIZE 64 + +struct pktcdvd_device +{ + struct block_device *bdev; /* dev attached */ + dev_t pkt_dev; /* our dev */ + char name[20]; + struct packet_settings settings; + struct packet_stats stats; + int refcnt; /* Open count */ + int write_speed; /* current write speed, kB/s */ + int read_speed; /* current read speed, kB/s */ + unsigned long offset; /* start offset */ + __u8 mode_offset; /* 0 / 8 */ + __u8 type; + unsigned long flags; + __u16 mmc3_profile; + __u32 nwa; /* next writable address */ + __u32 lra; /* last recorded address */ + struct packet_cdrw cdrw; + wait_queue_head_t wqueue; + + spinlock_t lock; /* Serialize access to bio_queue */ + struct rb_root bio_queue; /* Work queue of bios we need to handle */ + int bio_queue_size; /* Number of nodes in bio_queue */ + sector_t current_sector; /* Keep track of where the elevator is */ + atomic_t scan_queue; /* Set to non-zero when pkt_handle_queue */ + /* needs to be run. */ + mempool_t *rb_pool; /* mempool for pkt_rb_node allocations */ + + struct packet_iosched iosched; + struct gendisk *disk; +}; + +#endif /* __KERNEL__ */ + +#endif /* __PKTCDVD_H */ -- cgit v1.2.3 From 66d5cab9f5225a0fe501305016129110d580d688 Mon Sep 17 00:00:00 2001 From: Peter Osterlund Date: Mon, 18 Oct 2004 17:57:46 -0700 Subject: [PATCH] cdrom: buffer sizing fix The problem is that some drives fail the "GET CONFIGURATION" command when asked to only return 8 bytes. This happens for example on my drive, which is identified as: hdc: HL-DT-ST DVD+RW GCA-4040N, ATAPI CD/DVD-ROM drive Since the cdrom_mmc3_profile() function already allocates 32 bytes for the reply buffer, this patch is enough to make the command succeed on my drive. Signed-off-by: Peter Osterlund Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/cdrom/cdrom.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/cdrom/cdrom.c b/drivers/cdrom/cdrom.c index 3da410b2c940..4153153aeaf5 100644 --- a/drivers/cdrom/cdrom.c +++ b/drivers/cdrom/cdrom.c @@ -865,7 +865,7 @@ static void cdrom_mmc3_profile(struct cdrom_device_info *cdi) cgc.cmd[0] = GPCMD_GET_CONFIGURATION; cgc.cmd[1] = 0; cgc.cmd[2] = cgc.cmd[3] = 0; /* Starting Feature Number */ - cgc.cmd[7] = 0; cgc.cmd [8] = 8; /* Allocation Length */ + cgc.cmd[8] = sizeof(buffer); /* Allocation Length */ cgc.quiet = 1; if ((ret = cdi->ops->generic_packet(cdi, &cgc))) { -- cgit v1.2.3 From d05dd6d0c370eecba901c66553a06cab6040e715 Mon Sep 17 00:00:00 2001 From: Olaf Dabrunz Date: Mon, 18 Oct 2004 17:59:16 -0700 Subject: [PATCH] TIOCCONS security The ioctl TIOCCONS allows any user to redirect console output to another tty. This allows anyone to suppress messages to the console at will. AFAIK nowadays not many programs write to /dev/console, except for start scripts and the kernel (printk() above console log level). Still, I believe that administrators and operators would not like any user to be able to hijack messages that were written to the console. The only user of TIOCCONS that I am aware of is bootlogd/blogd, which runs as root. Please comment if there are other users. Is there any reason why normal users should be able to use TIOCCONS? Otherwise I would suggest to restrict access to root (CAP_SYS_ADMIN), e.g. with this patch. Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/char/tty_io.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index 89457cc2ed72..9d5e4ba9dc25 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -1981,10 +1981,10 @@ static int tiocswinsz(struct tty_struct *tty, struct tty_struct *real_tty, static int tioccons(struct file *file) { + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; if (file->f_op->write == redirected_tty_write) { struct file *f; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; spin_lock(&redirect_lock); f = redirect; redirect = NULL; -- cgit v1.2.3 From df02202cfb0d7df1c28225c7da0c3deb3698a730 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Mon, 18 Oct 2004 18:01:28 -0700 Subject: [PATCH] switchable and modular io schedulers This patch modularizes the io schedulers completely, allowing them to be modular. Additionally it enables online switching of io schedulers. See also http://lwn.net/Articles/102593/ . There's a scheduler file in the sysfs directory for the block device queue: axboe@router:/sys/block/hda/queue> ls iosched max_sectors_kb read_ahead_kb max_hw_sectors_kb nr_requests scheduler If you list the contents of the file, it will show available schedulers and the active one: axboe@router:/sys/block/hda/queue> cat scheduler [cfq] Lets load a few more. router:/sys/block/hda/queue # modprobe deadline-iosched router:/sys/block/hda/queue # modprobe as-iosched router:/sys/block/hda/queue # cat scheduler [cfq] deadline anticipatory Changing is done with router:/sys/block/hda/queue # echo deadline > scheduler router:/sys/block/hda/queue # cat scheduler cfq [deadline] anticipatory deadline is now the new active io scheduler for hda. Signed-off-by: Jens Axboe Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/block/Kconfig.iosched | 8 +- drivers/block/as-iosched.c | 111 ++++++++------ drivers/block/cfq-iosched.c | 113 +++++++++----- drivers/block/deadline-iosched.c | 90 ++++++----- drivers/block/elevator.c | 316 ++++++++++++++++++++++++++++++++------- drivers/block/ll_rw_blk.c | 140 ++++++++++------- drivers/block/noop-iosched.c | 33 +++- drivers/s390/block/dasd.c | 4 +- drivers/s390/char/tape_block.c | 4 +- include/linux/blkdev.h | 10 +- include/linux/elevator.h | 55 ++++--- 11 files changed, 622 insertions(+), 262 deletions(-) (limited to 'drivers') diff --git a/drivers/block/Kconfig.iosched b/drivers/block/Kconfig.iosched index d938c5fd130b..e0ba6c93717e 100644 --- a/drivers/block/Kconfig.iosched +++ b/drivers/block/Kconfig.iosched @@ -1,5 +1,5 @@ config IOSCHED_NOOP - bool "No-op I/O scheduler" if EMBEDDED + bool default y ---help--- The no-op I/O scheduler is a minimal scheduler that does basic merging @@ -9,7 +9,7 @@ config IOSCHED_NOOP the kernel. config IOSCHED_AS - bool "Anticipatory I/O scheduler" if EMBEDDED + tristate "Anticipatory I/O scheduler" default y ---help--- The anticipatory I/O scheduler is the default disk scheduler. It is @@ -18,7 +18,7 @@ config IOSCHED_AS slower in some cases especially some database loads. config IOSCHED_DEADLINE - bool "Deadline I/O scheduler" if EMBEDDED + tristate "Deadline I/O scheduler" default y ---help--- The deadline I/O scheduler is simple and compact, and is often as @@ -28,7 +28,7 @@ config IOSCHED_DEADLINE anticipatory I/O scheduler and so is a good choice. config IOSCHED_CFQ - bool "CFQ I/O scheduler" if EMBEDDED + tristate "CFQ I/O scheduler" default y ---help--- The CFQ I/O scheduler tries to distribute bandwidth equally diff --git a/drivers/block/as-iosched.c b/drivers/block/as-iosched.c index 0ef6a665d93e..bb3e9b5bab3c 100644 --- a/drivers/block/as-iosched.c +++ b/drivers/block/as-iosched.c @@ -614,7 +614,7 @@ static void as_antic_stop(struct as_data *ad) static void as_antic_timeout(unsigned long data) { struct request_queue *q = (struct request_queue *)data; - struct as_data *ad = q->elevator.elevator_data; + struct as_data *ad = q->elevator->elevator_data; unsigned long flags; spin_lock_irqsave(q->queue_lock, flags); @@ -945,7 +945,7 @@ static void update_write_batch(struct as_data *ad) */ static void as_completed_request(request_queue_t *q, struct request *rq) { - struct as_data *ad = q->elevator.elevator_data; + struct as_data *ad = q->elevator->elevator_data; struct as_rq *arq = RQ_DATA(rq); WARN_ON(!list_empty(&rq->queuelist)); @@ -1030,7 +1030,7 @@ static void as_remove_queued_request(request_queue_t *q, struct request *rq) { struct as_rq *arq = RQ_DATA(rq); const int data_dir = arq->is_sync; - struct as_data *ad = q->elevator.elevator_data; + struct as_data *ad = q->elevator->elevator_data; WARN_ON(arq->state != AS_RQ_QUEUED); @@ -1361,7 +1361,7 @@ fifo_expired: static struct request *as_next_request(request_queue_t *q) { - struct as_data *ad = q->elevator.elevator_data; + struct as_data *ad = q->elevator->elevator_data; struct request *rq = NULL; /* @@ -1469,7 +1469,7 @@ static void as_add_request(struct as_data *ad, struct as_rq *arq) */ static void as_requeue_request(request_queue_t *q, struct request *rq) { - struct as_data *ad = q->elevator.elevator_data; + struct as_data *ad = q->elevator->elevator_data; struct as_rq *arq = RQ_DATA(rq); if (arq) { @@ -1509,7 +1509,7 @@ static void as_account_queued_request(struct as_data *ad, struct request *rq) static void as_insert_request(request_queue_t *q, struct request *rq, int where) { - struct as_data *ad = q->elevator.elevator_data; + struct as_data *ad = q->elevator->elevator_data; struct as_rq *arq = RQ_DATA(rq); if (arq) { @@ -1562,7 +1562,7 @@ as_insert_request(request_queue_t *q, struct request *rq, int where) */ static int as_queue_empty(request_queue_t *q) { - struct as_data *ad = q->elevator.elevator_data; + struct as_data *ad = q->elevator->elevator_data; if (!list_empty(&ad->fifo_list[REQ_ASYNC]) || !list_empty(&ad->fifo_list[REQ_SYNC]) @@ -1601,7 +1601,7 @@ as_latter_request(request_queue_t *q, struct request *rq) static int as_merge(request_queue_t *q, struct request **req, struct bio *bio) { - struct as_data *ad = q->elevator.elevator_data; + struct as_data *ad = q->elevator->elevator_data; sector_t rb_key = bio->bi_sector + bio_sectors(bio); struct request *__rq; int ret; @@ -1656,7 +1656,7 @@ out_insert: static void as_merged_request(request_queue_t *q, struct request *req) { - struct as_data *ad = q->elevator.elevator_data; + struct as_data *ad = q->elevator->elevator_data; struct as_rq *arq = RQ_DATA(req); /* @@ -1701,7 +1701,7 @@ static void as_merged_requests(request_queue_t *q, struct request *req, struct request *next) { - struct as_data *ad = q->elevator.elevator_data; + struct as_data *ad = q->elevator->elevator_data; struct as_rq *arq = RQ_DATA(req); struct as_rq *anext = RQ_DATA(next); @@ -1788,7 +1788,7 @@ static void as_work_handler(void *data) static void as_put_request(request_queue_t *q, struct request *rq) { - struct as_data *ad = q->elevator.elevator_data; + struct as_data *ad = q->elevator->elevator_data; struct as_rq *arq = RQ_DATA(rq); if (!arq) { @@ -1807,7 +1807,7 @@ static void as_put_request(request_queue_t *q, struct request *rq) static int as_set_request(request_queue_t *q, struct request *rq, int gfp_mask) { - struct as_data *ad = q->elevator.elevator_data; + struct as_data *ad = q->elevator->elevator_data; struct as_rq *arq = mempool_alloc(ad->arq_pool, gfp_mask); if (arq) { @@ -1829,7 +1829,7 @@ static int as_set_request(request_queue_t *q, struct request *rq, int gfp_mask) static int as_may_queue(request_queue_t *q, int rw) { int ret = 0; - struct as_data *ad = q->elevator.elevator_data; + struct as_data *ad = q->elevator->elevator_data; struct io_context *ioc; if (ad->antic_status == ANTIC_WAIT_REQ || ad->antic_status == ANTIC_WAIT_NEXT) { @@ -1842,7 +1842,7 @@ static int as_may_queue(request_queue_t *q, int rw) return ret; } -static void as_exit(request_queue_t *q, elevator_t *e) +static void as_exit_queue(elevator_t *e) { struct as_data *ad = e->elevator_data; @@ -1862,7 +1862,7 @@ static void as_exit(request_queue_t *q, elevator_t *e) * initialize elevator private data (as_data), and alloc a arq for * each request on the free lists */ -static int as_init(request_queue_t *q, elevator_t *e) +static int as_init_queue(request_queue_t *q, elevator_t *e) { struct as_data *ad; int i; @@ -2070,39 +2070,64 @@ static struct kobj_type as_ktype = { .default_attrs = default_attrs, }; -static int __init as_slab_setup(void) +static struct elevator_type iosched_as = { + .ops = { + .elevator_merge_fn = as_merge, + .elevator_merged_fn = as_merged_request, + .elevator_merge_req_fn = as_merged_requests, + .elevator_next_req_fn = as_next_request, + .elevator_add_req_fn = as_insert_request, + .elevator_remove_req_fn = as_remove_request, + .elevator_requeue_req_fn = as_requeue_request, + .elevator_queue_empty_fn = as_queue_empty, + .elevator_completed_req_fn = as_completed_request, + .elevator_former_req_fn = as_former_request, + .elevator_latter_req_fn = as_latter_request, + .elevator_set_req_fn = as_set_request, + .elevator_put_req_fn = as_put_request, + .elevator_may_queue_fn = as_may_queue, + .elevator_init_fn = as_init_queue, + .elevator_exit_fn = as_exit_queue, + }, + + .elevator_ktype = &as_ktype, + .elevator_name = "anticipatory", + .elevator_owner = THIS_MODULE, +}; + +int as_init(void) { + int ret; + arq_pool = kmem_cache_create("as_arq", sizeof(struct as_rq), 0, 0, NULL, NULL); - if (!arq_pool) - panic("as: can't init slab pool\n"); + return -ENOMEM; - return 0; + ret = elv_register(&iosched_as); + if (!ret) { + /* + * don't allow AS to get unregistered, since we would have + * to browse all tasks in the system and release their + * as_io_context first + */ + __module_get(THIS_MODULE); + return 0; + } + + kmem_cache_destroy(arq_pool); + return ret; } -subsys_initcall(as_slab_setup); - -elevator_t iosched_as = { - .elevator_merge_fn = as_merge, - .elevator_merged_fn = as_merged_request, - .elevator_merge_req_fn = as_merged_requests, - .elevator_next_req_fn = as_next_request, - .elevator_add_req_fn = as_insert_request, - .elevator_remove_req_fn = as_remove_request, - .elevator_requeue_req_fn = as_requeue_request, - .elevator_queue_empty_fn = as_queue_empty, - .elevator_completed_req_fn = as_completed_request, - .elevator_former_req_fn = as_former_request, - .elevator_latter_req_fn = as_latter_request, - .elevator_set_req_fn = as_set_request, - .elevator_put_req_fn = as_put_request, - .elevator_may_queue_fn = as_may_queue, - .elevator_init_fn = as_init, - .elevator_exit_fn = as_exit, - - .elevator_ktype = &as_ktype, - .elevator_name = "anticipatory", -}; +void as_exit(void) +{ + kmem_cache_destroy(arq_pool); + elv_unregister(&iosched_as); +} + +module_init(as_init); +module_exit(as_exit); -EXPORT_SYMBOL(iosched_as); +MODULE_AUTHOR("Nick Piggin"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("anticipatory IO scheduler"); diff --git a/drivers/block/cfq-iosched.c b/drivers/block/cfq-iosched.c index 068f4eae0b5c..6a424dc65823 100644 --- a/drivers/block/cfq-iosched.c +++ b/drivers/block/cfq-iosched.c @@ -246,7 +246,7 @@ out: static void cfq_remove_request(request_queue_t *q, struct request *rq) { - struct cfq_data *cfqd = q->elevator.elevator_data; + struct cfq_data *cfqd = q->elevator->elevator_data; struct cfq_rq *crq = RQ_DATA(rq); if (crq) { @@ -267,7 +267,7 @@ static void cfq_remove_request(request_queue_t *q, struct request *rq) static int cfq_merge(request_queue_t *q, struct request **req, struct bio *bio) { - struct cfq_data *cfqd = q->elevator.elevator_data; + struct cfq_data *cfqd = q->elevator->elevator_data; struct request *__rq; int ret; @@ -305,7 +305,7 @@ out_insert: static void cfq_merged_request(request_queue_t *q, struct request *req) { - struct cfq_data *cfqd = q->elevator.elevator_data; + struct cfq_data *cfqd = q->elevator->elevator_data; struct cfq_rq *crq = RQ_DATA(req); cfq_del_crq_hash(crq); @@ -404,7 +404,7 @@ restart: static struct request *cfq_next_request(request_queue_t *q) { - struct cfq_data *cfqd = q->elevator.elevator_data; + struct cfq_data *cfqd = q->elevator->elevator_data; struct request *rq; if (!list_empty(cfqd->dispatch)) { @@ -531,7 +531,7 @@ static void cfq_enqueue(struct cfq_data *cfqd, struct cfq_rq *crq) static void cfq_insert_request(request_queue_t *q, struct request *rq, int where) { - struct cfq_data *cfqd = q->elevator.elevator_data; + struct cfq_data *cfqd = q->elevator->elevator_data; struct cfq_rq *crq = RQ_DATA(rq); switch (where) { @@ -562,7 +562,7 @@ cfq_insert_request(request_queue_t *q, struct request *rq, int where) static int cfq_queue_empty(request_queue_t *q) { - struct cfq_data *cfqd = q->elevator.elevator_data; + struct cfq_data *cfqd = q->elevator->elevator_data; if (list_empty(cfqd->dispatch) && list_empty(&cfqd->rr_list)) return 1; @@ -596,7 +596,7 @@ cfq_latter_request(request_queue_t *q, struct request *rq) static int cfq_may_queue(request_queue_t *q, int rw) { - struct cfq_data *cfqd = q->elevator.elevator_data; + struct cfq_data *cfqd = q->elevator->elevator_data; struct cfq_queue *cfqq; int ret = 1; @@ -621,7 +621,7 @@ out: static void cfq_put_request(request_queue_t *q, struct request *rq) { - struct cfq_data *cfqd = q->elevator.elevator_data; + struct cfq_data *cfqd = q->elevator->elevator_data; struct cfq_rq *crq = RQ_DATA(rq); struct request_list *rl; int other_rw; @@ -654,7 +654,7 @@ static void cfq_put_request(request_queue_t *q, struct request *rq) static int cfq_set_request(request_queue_t *q, struct request *rq, int gfp_mask) { - struct cfq_data *cfqd = q->elevator.elevator_data; + struct cfq_data *cfqd = q->elevator->elevator_data; struct cfq_queue *cfqq; struct cfq_rq *crq; @@ -679,7 +679,7 @@ static int cfq_set_request(request_queue_t *q, struct request *rq, int gfp_mask) return 1; } -static void cfq_exit(request_queue_t *q, elevator_t *e) +static void cfq_exit_queue(elevator_t *e) { struct cfq_data *cfqd = e->elevator_data; @@ -690,7 +690,7 @@ static void cfq_exit(request_queue_t *q, elevator_t *e) kfree(cfqd); } -static int cfq_init(request_queue_t *q, elevator_t *e) +static int cfq_init_queue(request_queue_t *q, elevator_t *e) { struct cfq_data *cfqd; int i; @@ -732,7 +732,6 @@ static int cfq_init(request_queue_t *q, elevator_t *e) cfqd->cfq_queued = cfq_queued; cfqd->cfq_quantum = cfq_quantum; - return 0; out_crqpool: kfree(cfqd->cfq_hash); @@ -743,30 +742,38 @@ out_crqhash: return -ENOMEM; } -static int __init cfq_slab_setup(void) +static void cfq_slab_kill(void) +{ + if (crq_pool) + kmem_cache_destroy(crq_pool); + if (cfq_mpool) + mempool_destroy(cfq_mpool); + if (cfq_pool) + kmem_cache_destroy(cfq_pool); +} + +static int cfq_slab_setup(void) { crq_pool = kmem_cache_create("crq_pool", sizeof(struct cfq_rq), 0, 0, NULL, NULL); - if (!crq_pool) - panic("cfq_iosched: can't init crq pool\n"); + goto fail; cfq_pool = kmem_cache_create("cfq_pool", sizeof(struct cfq_queue), 0, 0, NULL, NULL); - if (!cfq_pool) - panic("cfq_iosched: can't init cfq pool\n"); + goto fail; cfq_mpool = mempool_create(64, mempool_alloc_slab, mempool_free_slab, cfq_pool); - if (!cfq_mpool) - panic("cfq_iosched: can't init cfq mpool\n"); + goto fail; return 0; +fail: + cfq_slab_kill(); + return -ENOMEM; } -subsys_initcall(cfq_slab_setup); - /* * sysfs parts below --> */ @@ -868,23 +875,51 @@ struct kobj_type cfq_ktype = { .default_attrs = default_attrs, }; -elevator_t iosched_cfq = { - .elevator_name = "cfq", - .elevator_ktype = &cfq_ktype, - .elevator_merge_fn = cfq_merge, - .elevator_merged_fn = cfq_merged_request, - .elevator_merge_req_fn = cfq_merged_requests, - .elevator_next_req_fn = cfq_next_request, - .elevator_add_req_fn = cfq_insert_request, - .elevator_remove_req_fn = cfq_remove_request, - .elevator_queue_empty_fn = cfq_queue_empty, - .elevator_former_req_fn = cfq_former_request, - .elevator_latter_req_fn = cfq_latter_request, - .elevator_set_req_fn = cfq_set_request, - .elevator_put_req_fn = cfq_put_request, - .elevator_may_queue_fn = cfq_may_queue, - .elevator_init_fn = cfq_init, - .elevator_exit_fn = cfq_exit, +static struct elevator_type iosched_cfq = { + .ops = { + .elevator_merge_fn = cfq_merge, + .elevator_merged_fn = cfq_merged_request, + .elevator_merge_req_fn = cfq_merged_requests, + .elevator_next_req_fn = cfq_next_request, + .elevator_add_req_fn = cfq_insert_request, + .elevator_remove_req_fn = cfq_remove_request, + .elevator_queue_empty_fn = cfq_queue_empty, + .elevator_former_req_fn = cfq_former_request, + .elevator_latter_req_fn = cfq_latter_request, + .elevator_set_req_fn = cfq_set_request, + .elevator_put_req_fn = cfq_put_request, + .elevator_may_queue_fn = cfq_may_queue, + .elevator_init_fn = cfq_init_queue, + .elevator_exit_fn = cfq_exit_queue, + }, + .elevator_ktype = &cfq_ktype, + .elevator_name = "cfq", + .elevator_owner = THIS_MODULE, }; -EXPORT_SYMBOL(iosched_cfq); +int cfq_init(void) +{ + int ret; + + if (cfq_slab_setup()) + return -ENOMEM; + + ret = elv_register(&iosched_cfq); + if (ret) + cfq_slab_kill(); + + return ret; +} + +void cfq_exit(void) +{ + cfq_slab_kill(); + elv_unregister(&iosched_cfq); +} + +module_init(cfq_init); +module_exit(cfq_exit); + +MODULE_AUTHOR("Jens Axboe"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Completely Fair Queueing IO scheduler"); diff --git a/drivers/block/deadline-iosched.c b/drivers/block/deadline-iosched.c index fb7ab733c709..0d3e2411f1d3 100644 --- a/drivers/block/deadline-iosched.c +++ b/drivers/block/deadline-iosched.c @@ -289,7 +289,7 @@ deadline_find_first_drq(struct deadline_data *dd, int data_dir) static inline void deadline_add_request(struct request_queue *q, struct request *rq) { - struct deadline_data *dd = q->elevator.elevator_data; + struct deadline_data *dd = q->elevator->elevator_data; struct deadline_rq *drq = RQ_DATA(rq); const int data_dir = rq_data_dir(drq->request); @@ -317,7 +317,7 @@ static void deadline_remove_request(request_queue_t *q, struct request *rq) struct deadline_rq *drq = RQ_DATA(rq); if (drq) { - struct deadline_data *dd = q->elevator.elevator_data; + struct deadline_data *dd = q->elevator->elevator_data; list_del_init(&drq->fifo); deadline_remove_merge_hints(q, drq); @@ -328,7 +328,7 @@ static void deadline_remove_request(request_queue_t *q, struct request *rq) static int deadline_merge(request_queue_t *q, struct request **req, struct bio *bio) { - struct deadline_data *dd = q->elevator.elevator_data; + struct deadline_data *dd = q->elevator->elevator_data; struct request *__rq; int ret; @@ -383,7 +383,7 @@ out_insert: static void deadline_merged_request(request_queue_t *q, struct request *req) { - struct deadline_data *dd = q->elevator.elevator_data; + struct deadline_data *dd = q->elevator->elevator_data; struct deadline_rq *drq = RQ_DATA(req); /* @@ -407,7 +407,7 @@ static void deadline_merged_requests(request_queue_t *q, struct request *req, struct request *next) { - struct deadline_data *dd = q->elevator.elevator_data; + struct deadline_data *dd = q->elevator->elevator_data; struct deadline_rq *drq = RQ_DATA(req); struct deadline_rq *dnext = RQ_DATA(next); @@ -604,7 +604,7 @@ dispatch_request: static struct request *deadline_next_request(request_queue_t *q) { - struct deadline_data *dd = q->elevator.elevator_data; + struct deadline_data *dd = q->elevator->elevator_data; struct request *rq; /* @@ -625,7 +625,7 @@ dispatch: static void deadline_insert_request(request_queue_t *q, struct request *rq, int where) { - struct deadline_data *dd = q->elevator.elevator_data; + struct deadline_data *dd = q->elevator->elevator_data; /* barriers must flush the reorder queue */ if (unlikely(rq->flags & (REQ_SOFTBARRIER | REQ_HARDBARRIER) @@ -653,7 +653,7 @@ deadline_insert_request(request_queue_t *q, struct request *rq, int where) static int deadline_queue_empty(request_queue_t *q) { - struct deadline_data *dd = q->elevator.elevator_data; + struct deadline_data *dd = q->elevator->elevator_data; if (!list_empty(&dd->fifo_list[WRITE]) || !list_empty(&dd->fifo_list[READ]) @@ -687,7 +687,7 @@ deadline_latter_request(request_queue_t *q, struct request *rq) return NULL; } -static void deadline_exit(request_queue_t *q, elevator_t *e) +static void deadline_exit_queue(elevator_t *e) { struct deadline_data *dd = e->elevator_data; @@ -703,7 +703,7 @@ static void deadline_exit(request_queue_t *q, elevator_t *e) * initialize elevator private data (deadline_data), and alloc a drq for * each request on the free lists */ -static int deadline_init(request_queue_t *q, elevator_t *e) +static int deadline_init_queue(request_queue_t *q, elevator_t *e) { struct deadline_data *dd; int i; @@ -748,7 +748,7 @@ static int deadline_init(request_queue_t *q, elevator_t *e) static void deadline_put_request(request_queue_t *q, struct request *rq) { - struct deadline_data *dd = q->elevator.elevator_data; + struct deadline_data *dd = q->elevator->elevator_data; struct deadline_rq *drq = RQ_DATA(rq); if (drq) { @@ -760,7 +760,7 @@ static void deadline_put_request(request_queue_t *q, struct request *rq) static int deadline_set_request(request_queue_t *q, struct request *rq, int gfp_mask) { - struct deadline_data *dd = q->elevator.elevator_data; + struct deadline_data *dd = q->elevator->elevator_data; struct deadline_rq *drq; drq = mempool_alloc(dd->drq_pool, gfp_mask); @@ -906,36 +906,54 @@ struct kobj_type deadline_ktype = { .default_attrs = default_attrs, }; -static int __init deadline_slab_setup(void) +static struct elevator_type iosched_deadline = { + .ops = { + .elevator_merge_fn = deadline_merge, + .elevator_merged_fn = deadline_merged_request, + .elevator_merge_req_fn = deadline_merged_requests, + .elevator_next_req_fn = deadline_next_request, + .elevator_add_req_fn = deadline_insert_request, + .elevator_remove_req_fn = deadline_remove_request, + .elevator_queue_empty_fn = deadline_queue_empty, + .elevator_former_req_fn = deadline_former_request, + .elevator_latter_req_fn = deadline_latter_request, + .elevator_set_req_fn = deadline_set_request, + .elevator_put_req_fn = deadline_put_request, + .elevator_init_fn = deadline_init_queue, + .elevator_exit_fn = deadline_exit_queue, + }, + + .elevator_ktype = &deadline_ktype, + .elevator_name = "deadline", + .elevator_owner = THIS_MODULE, +}; + +int deadline_init(void) { + int ret; + drq_pool = kmem_cache_create("deadline_drq", sizeof(struct deadline_rq), 0, 0, NULL, NULL); if (!drq_pool) - panic("deadline: can't init slab pool\n"); + return -ENOMEM; - return 0; + ret = elv_register(&iosched_deadline); + if (ret) + kmem_cache_destroy(drq_pool); + + return ret; } -subsys_initcall(deadline_slab_setup); - -elevator_t iosched_deadline = { - .elevator_merge_fn = deadline_merge, - .elevator_merged_fn = deadline_merged_request, - .elevator_merge_req_fn = deadline_merged_requests, - .elevator_next_req_fn = deadline_next_request, - .elevator_add_req_fn = deadline_insert_request, - .elevator_remove_req_fn = deadline_remove_request, - .elevator_queue_empty_fn = deadline_queue_empty, - .elevator_former_req_fn = deadline_former_request, - .elevator_latter_req_fn = deadline_latter_request, - .elevator_set_req_fn = deadline_set_request, - .elevator_put_req_fn = deadline_put_request, - .elevator_init_fn = deadline_init, - .elevator_exit_fn = deadline_exit, - - .elevator_ktype = &deadline_ktype, - .elevator_name = "deadline", -}; +void deadline_exit(void) +{ + kmem_cache_destroy(drq_pool); + elv_unregister(&iosched_deadline); +} + +module_init(deadline_init); +module_exit(deadline_exit); -EXPORT_SYMBOL(iosched_deadline); +MODULE_AUTHOR("Jens Axboe"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("deadline IO scheduler"); diff --git a/drivers/block/elevator.c b/drivers/block/elevator.c index 35c9385ac133..92cc7a9a5c63 100644 --- a/drivers/block/elevator.c +++ b/drivers/block/elevator.c @@ -37,6 +37,9 @@ #include +static spinlock_t elv_list_lock = SPIN_LOCK_UNLOCKED; +static LIST_HEAD(elv_list); + /* * can we safely merge with this request? */ @@ -60,6 +63,7 @@ inline int elv_rq_merge_ok(struct request *rq, struct bio *bio) return 0; } +EXPORT_SYMBOL(elv_rq_merge_ok); inline int elv_try_merge(struct request *__rq, struct bio *bio) { @@ -77,6 +81,7 @@ inline int elv_try_merge(struct request *__rq, struct bio *bio) return ret; } +EXPORT_SYMBOL(elv_try_merge); inline int elv_try_last_merge(request_queue_t *q, struct bio *bio) { @@ -85,31 +90,117 @@ inline int elv_try_last_merge(request_queue_t *q, struct bio *bio) return ELEVATOR_NO_MERGE; } +EXPORT_SYMBOL(elv_try_last_merge); -/* - * general block -> elevator interface starts here - */ -int elevator_init(request_queue_t *q, elevator_t *type) +struct elevator_type *elevator_find(const char *name) +{ + struct elevator_type *e = NULL; + struct list_head *entry; + + spin_lock_irq(&elv_list_lock); + list_for_each(entry, &elv_list) { + struct elevator_type *__e; + + __e = list_entry(entry, struct elevator_type, list); + + if (!strcmp(__e->elevator_name, name)) { + e = __e; + break; + } + } + spin_unlock_irq(&elv_list_lock); + + return e; +} + +static int elevator_attach(request_queue_t *q, struct elevator_type *e, + struct elevator_queue *eq) { - elevator_t *e = &q->elevator; + int ret = 0; - memcpy(e, type, sizeof(*e)); + if (!try_module_get(e->elevator_owner)) + return -EINVAL; + + memset(eq, 0, sizeof(*eq)); + eq->ops = &e->ops; + eq->elevator_type = e; INIT_LIST_HEAD(&q->queue_head); q->last_merge = NULL; + q->elevator = eq; + + if (eq->ops->elevator_init_fn) + ret = eq->ops->elevator_init_fn(q, eq); - if (e->elevator_init_fn) - return e->elevator_init_fn(q, e); + return ret; +} + +static char chosen_elevator[16]; + +static void elevator_setup_default(void) +{ + /* + * check if default is set and exists + */ + if (chosen_elevator[0] && elevator_find(chosen_elevator)) + return; + +#if defined(CONFIG_IOSCHED_AS) + strcpy(chosen_elevator, "anticipatory"); +#elif defined(CONFIG_IOSCHED_DEADLINE) + strcpy(chosen_elevator, "deadline"); +#elif defined(CONFIG_IOSCHED_CFQ) + strcpy(chosen_elevator, "cfq"); +#elif defined(CONFIG_IOSCHED_NOOP) + strcpy(chosen_elevator, "noop"); +#else +#error "You must build at least 1 IO scheduler into the kernel" +#endif + printk("elevator: using %s as default io scheduler\n", chosen_elevator); +} +static int __init elevator_setup(char *str) +{ + strncpy(chosen_elevator, str, sizeof(chosen_elevator) - 1); return 0; } -void elevator_exit(request_queue_t *q) +__setup("elevator=", elevator_setup); + +int elevator_init(request_queue_t *q, char *name) +{ + struct elevator_type *e = NULL; + struct elevator_queue *eq; + int ret = 0; + + elevator_setup_default(); + + if (!name) + name = chosen_elevator; + + e = elevator_find(name); + if (!e) + return -EINVAL; + + eq = kmalloc(sizeof(struct elevator_queue), GFP_KERNEL); + if (!eq) + return -ENOMEM; + + ret = elevator_attach(q, e, eq); + if (ret) + kfree(eq); + + return ret; +} + +void elevator_exit(elevator_t *e) { - elevator_t *e = &q->elevator; + if (e->ops->elevator_exit_fn) + e->ops->elevator_exit_fn(e); - if (e->elevator_exit_fn) - e->elevator_exit_fn(q, e); + module_put(e->elevator_type->elevator_owner); + e->elevator_type = NULL; + kfree(e); } int elevator_global_init(void) @@ -119,32 +210,32 @@ int elevator_global_init(void) int elv_merge(request_queue_t *q, struct request **req, struct bio *bio) { - elevator_t *e = &q->elevator; + elevator_t *e = q->elevator; - if (e->elevator_merge_fn) - return e->elevator_merge_fn(q, req, bio); + if (e->ops->elevator_merge_fn) + return e->ops->elevator_merge_fn(q, req, bio); return ELEVATOR_NO_MERGE; } void elv_merged_request(request_queue_t *q, struct request *rq) { - elevator_t *e = &q->elevator; + elevator_t *e = q->elevator; - if (e->elevator_merged_fn) - e->elevator_merged_fn(q, rq); + if (e->ops->elevator_merged_fn) + e->ops->elevator_merged_fn(q, rq); } void elv_merge_requests(request_queue_t *q, struct request *rq, struct request *next) { - elevator_t *e = &q->elevator; + elevator_t *e = q->elevator; if (q->last_merge == next) q->last_merge = NULL; - if (e->elevator_merge_req_fn) - e->elevator_merge_req_fn(q, rq, next); + if (e->ops->elevator_merge_req_fn) + e->ops->elevator_merge_req_fn(q, rq, next); } void elv_requeue_request(request_queue_t *q, struct request *rq) @@ -160,8 +251,8 @@ void elv_requeue_request(request_queue_t *q, struct request *rq) * if iosched has an explicit requeue hook, then use that. otherwise * just put the request at the front of the queue */ - if (q->elevator.elevator_requeue_req_fn) - q->elevator.elevator_requeue_req_fn(q, rq); + if (q->elevator->ops->elevator_requeue_req_fn) + q->elevator->ops->elevator_requeue_req_fn(q, rq); else __elv_add_request(q, rq, ELEVATOR_INSERT_FRONT, 0); } @@ -180,7 +271,7 @@ void __elv_add_request(request_queue_t *q, struct request *rq, int where, blk_plug_device(q); rq->q = q; - q->elevator.elevator_add_req_fn(q, rq, where); + q->elevator->ops->elevator_add_req_fn(q, rq, where); if (blk_queue_plugged(q)) { int nrq = q->rq.count[READ] + q->rq.count[WRITE] - q->in_flight; @@ -203,7 +294,7 @@ void elv_add_request(request_queue_t *q, struct request *rq, int where, static inline struct request *__elv_next_request(request_queue_t *q) { - return q->elevator.elevator_next_req_fn(q); + return q->elevator->ops->elevator_next_req_fn(q); } struct request *elv_next_request(request_queue_t *q) @@ -252,7 +343,7 @@ struct request *elv_next_request(request_queue_t *q) void elv_remove_request(request_queue_t *q, struct request *rq) { - elevator_t *e = &q->elevator; + elevator_t *e = q->elevator; /* * the time frame between a request being removed from the lists @@ -274,16 +365,16 @@ void elv_remove_request(request_queue_t *q, struct request *rq) if (rq == q->last_merge) q->last_merge = NULL; - if (e->elevator_remove_req_fn) - e->elevator_remove_req_fn(q, rq); + if (e->ops->elevator_remove_req_fn) + e->ops->elevator_remove_req_fn(q, rq); } int elv_queue_empty(request_queue_t *q) { - elevator_t *e = &q->elevator; + elevator_t *e = q->elevator; - if (e->elevator_queue_empty_fn) - return e->elevator_queue_empty_fn(q); + if (e->ops->elevator_queue_empty_fn) + return e->ops->elevator_queue_empty_fn(q); return list_empty(&q->queue_head); } @@ -292,10 +383,10 @@ struct request *elv_latter_request(request_queue_t *q, struct request *rq) { struct list_head *next; - elevator_t *e = &q->elevator; + elevator_t *e = q->elevator; - if (e->elevator_latter_req_fn) - return e->elevator_latter_req_fn(q, rq); + if (e->ops->elevator_latter_req_fn) + return e->ops->elevator_latter_req_fn(q, rq); next = rq->queuelist.next; if (next != &q->queue_head && next != &rq->queuelist) @@ -308,10 +399,10 @@ struct request *elv_former_request(request_queue_t *q, struct request *rq) { struct list_head *prev; - elevator_t *e = &q->elevator; + elevator_t *e = q->elevator; - if (e->elevator_former_req_fn) - return e->elevator_former_req_fn(q, rq); + if (e->ops->elevator_former_req_fn) + return e->ops->elevator_former_req_fn(q, rq); prev = rq->queuelist.prev; if (prev != &q->queue_head && prev != &rq->queuelist) @@ -322,10 +413,10 @@ struct request *elv_former_request(request_queue_t *q, struct request *rq) int elv_set_request(request_queue_t *q, struct request *rq, int gfp_mask) { - elevator_t *e = &q->elevator; + elevator_t *e = q->elevator; - if (e->elevator_set_req_fn) - return e->elevator_set_req_fn(q, rq, gfp_mask); + if (e->ops->elevator_set_req_fn) + return e->ops->elevator_set_req_fn(q, rq, gfp_mask); rq->elevator_private = NULL; return 0; @@ -333,25 +424,25 @@ int elv_set_request(request_queue_t *q, struct request *rq, int gfp_mask) void elv_put_request(request_queue_t *q, struct request *rq) { - elevator_t *e = &q->elevator; + elevator_t *e = q->elevator; - if (e->elevator_put_req_fn) - e->elevator_put_req_fn(q, rq); + if (e->ops->elevator_put_req_fn) + e->ops->elevator_put_req_fn(q, rq); } int elv_may_queue(request_queue_t *q, int rw) { - elevator_t *e = &q->elevator; + elevator_t *e = q->elevator; - if (e->elevator_may_queue_fn) - return e->elevator_may_queue_fn(q, rw); + if (e->ops->elevator_may_queue_fn) + return e->ops->elevator_may_queue_fn(q, rw); return 0; } void elv_completed_request(request_queue_t *q, struct request *rq) { - elevator_t *e = &q->elevator; + elevator_t *e = q->elevator; /* * request is released from the driver, io must be done @@ -359,22 +450,20 @@ void elv_completed_request(request_queue_t *q, struct request *rq) if (blk_account_rq(rq)) q->in_flight--; - if (e->elevator_completed_req_fn) - e->elevator_completed_req_fn(q, rq); + if (e->ops->elevator_completed_req_fn) + e->ops->elevator_completed_req_fn(q, rq); } int elv_register_queue(struct request_queue *q) { - elevator_t *e; - - e = &q->elevator; + elevator_t *e = q->elevator; e->kobj.parent = kobject_get(&q->kobj); if (!e->kobj.parent) return -EBUSY; snprintf(e->kobj.name, KOBJ_NAME_LEN, "%s", "iosched"); - e->kobj.ktype = e->elevator_ktype; + e->kobj.ktype = e->elevator_type->elevator_ktype; return kobject_register(&e->kobj); } @@ -382,12 +471,131 @@ int elv_register_queue(struct request_queue *q) void elv_unregister_queue(struct request_queue *q) { if (q) { - elevator_t * e = &q->elevator; + elevator_t *e = q->elevator; kobject_unregister(&e->kobj); kobject_put(&q->kobj); } } +int elv_register(struct elevator_type *e) +{ + if (elevator_find(e->elevator_name)) + BUG(); + + spin_lock_irq(&elv_list_lock); + list_add_tail(&e->list, &elv_list); + spin_unlock_irq(&elv_list_lock); + + printk("io scheduler %s registered\n", e->elevator_name); + return 0; +} +EXPORT_SYMBOL_GPL(elv_register); + +void elv_unregister(struct elevator_type *e) +{ + spin_lock_irq(&elv_list_lock); + list_del_init(&e->list); + spin_unlock_irq(&elv_list_lock); +} +EXPORT_SYMBOL_GPL(elv_unregister); + +/* + * switch to new_e io scheduler. be careful not to introduce deadlocks - + * we don't free the old io scheduler, before we have allocated what we + * need for the new one. this way we have a chance of going back to the old + * one, if the new one fails init for some reason + */ +static void elevator_switch(request_queue_t *q, struct elevator_type *new_e) +{ + elevator_t *e = kmalloc(sizeof(elevator_t), GFP_KERNEL); + elevator_t *old_elevator; + + if (!e) { + printk("elevator: out of memory\n"); + return; + } + + blk_wait_queue_drained(q); + + /* + * unregister old elevator data + */ + elv_unregister_queue(q); + old_elevator = q->elevator; + + /* + * attach and start new elevator + */ + if (elevator_attach(q, new_e, e)) + goto fail; + + if (elv_register_queue(q)) + goto fail_register; + + /* + * finally exit old elevator and start queue again + */ + elevator_exit(old_elevator); + blk_finish_queue_drain(q); + return; + +fail_register: + /* + * switch failed, exit the new io scheduler and reattach the old + * one again (along with re-adding the sysfs dir) + */ + elevator_exit(e); +fail: + q->elevator = old_elevator; + elv_register_queue(q); + blk_finish_queue_drain(q); + printk("elevator: switch to %s failed\n", new_e->elevator_name); +} + +ssize_t elv_iosched_store(request_queue_t *q, const char *name, size_t count) +{ + char elevator_name[ELV_NAME_MAX]; + struct elevator_type *e; + + memset(elevator_name, 0, sizeof(elevator_name)); + strncpy(elevator_name, name, sizeof(elevator_name)); + + if (elevator_name[strlen(elevator_name) - 1] == '\n') + elevator_name[strlen(elevator_name) - 1] = '\0'; + + e = elevator_find(elevator_name); + if (!e) { + printk("elevator: type %s not found\n", elevator_name); + return -EINVAL; + } + + elevator_switch(q, e); + return count; +} + +ssize_t elv_iosched_show(request_queue_t *q, char *name) +{ + elevator_t *e = q->elevator; + struct elevator_type *elv = e->elevator_type; + struct list_head *entry; + int len = 0; + + spin_lock_irq(q->queue_lock); + list_for_each(entry, &elv_list) { + struct elevator_type *__e; + + __e = list_entry(entry, struct elevator_type, list); + if (!strcmp(elv->elevator_name, __e->elevator_name)) + len += sprintf(name+len, "[%s] ", elv->elevator_name); + else + len += sprintf(name+len, "%s ", __e->elevator_name); + } + spin_unlock_irq(q->queue_lock); + + len += sprintf(len+name, "\n"); + return len; +} + module_init(elevator_global_init); EXPORT_SYMBOL(elv_add_request); diff --git a/drivers/block/ll_rw_blk.c b/drivers/block/ll_rw_blk.c index 26fdf6be6bd0..b3780ca0fdc0 100644 --- a/drivers/block/ll_rw_blk.c +++ b/drivers/block/ll_rw_blk.c @@ -1395,7 +1395,8 @@ void blk_cleanup_queue(request_queue_t * q) if (!atomic_dec_and_test(&q->refcnt)) return; - elevator_exit(q); + if (q->elevator) + elevator_exit(q->elevator); del_timer_sync(&q->unplug_timer); kblockd_flush(); @@ -1418,6 +1419,7 @@ static int blk_init_free_list(request_queue_t *q) rl->count[READ] = rl->count[WRITE] = 0; init_waitqueue_head(&rl->wait[READ]); init_waitqueue_head(&rl->wait[WRITE]); + init_waitqueue_head(&rl->drain); rl->rq_pool = mempool_create(BLKDEV_MIN_RQ, mempool_alloc_slab, mempool_free_slab, request_cachep); @@ -1429,45 +1431,6 @@ static int blk_init_free_list(request_queue_t *q) static int __make_request(request_queue_t *, struct bio *); -static elevator_t *chosen_elevator = -#if defined(CONFIG_IOSCHED_AS) - &iosched_as; -#elif defined(CONFIG_IOSCHED_DEADLINE) - &iosched_deadline; -#elif defined(CONFIG_IOSCHED_CFQ) - &iosched_cfq; -#elif defined(CONFIG_IOSCHED_NOOP) - &elevator_noop; -#else - NULL; -#error "You must have at least 1 I/O scheduler selected" -#endif - -#if defined(CONFIG_IOSCHED_AS) || defined(CONFIG_IOSCHED_DEADLINE) || defined (CONFIG_IOSCHED_NOOP) -static int __init elevator_setup(char *str) -{ -#ifdef CONFIG_IOSCHED_DEADLINE - if (!strcmp(str, "deadline")) - chosen_elevator = &iosched_deadline; -#endif -#ifdef CONFIG_IOSCHED_AS - if (!strcmp(str, "as")) - chosen_elevator = &iosched_as; -#endif -#ifdef CONFIG_IOSCHED_CFQ - if (!strcmp(str, "cfq")) - chosen_elevator = &iosched_cfq; -#endif -#ifdef CONFIG_IOSCHED_NOOP - if (!strcmp(str, "noop")) - chosen_elevator = &elevator_noop; -#endif - return 1; -} - -__setup("elevator=", elevator_setup); -#endif /* CONFIG_IOSCHED_AS || CONFIG_IOSCHED_DEADLINE || CONFIG_IOSCHED_NOOP */ - request_queue_t *blk_alloc_queue(int gfp_mask) { request_queue_t *q = kmem_cache_alloc(requestq_cachep, gfp_mask); @@ -1520,21 +1483,14 @@ EXPORT_SYMBOL(blk_alloc_queue); **/ request_queue_t *blk_init_queue(request_fn_proc *rfn, spinlock_t *lock) { - request_queue_t *q; - static int printed; + request_queue_t *q = blk_alloc_queue(GFP_KERNEL); - q = blk_alloc_queue(GFP_KERNEL); if (!q) return NULL; if (blk_init_free_list(q)) goto out_init; - if (!printed) { - printed = 1; - printk("Using %s io scheduler\n", chosen_elevator->elevator_name); - } - q->request_fn = rfn; q->back_merge_fn = ll_back_merge_fn; q->front_merge_fn = ll_front_merge_fn; @@ -1555,7 +1511,7 @@ request_queue_t *blk_init_queue(request_fn_proc *rfn, spinlock_t *lock) /* * all done */ - if (!elevator_init(q, chosen_elevator)) + if (!elevator_init(q, NULL)) return q; blk_cleanup_queue(q); @@ -1649,6 +1605,9 @@ static void freed_request(request_queue_t *q, int rw) if (!waitqueue_active(&rl->wait[rw])) blk_clear_queue_full(q, rw); } + if (unlikely(waitqueue_active(&rl->drain)) && + !rl->count[READ] && !rl->count[WRITE]) + wake_up(&rl->drain); } #define blkdev_free_rq(list) list_entry((list)->next, struct request, queuelist) @@ -1661,6 +1620,9 @@ static struct request *get_request(request_queue_t *q, int rw, int gfp_mask) struct request_list *rl = &q->rq; struct io_context *ioc = get_io_context(gfp_mask); + if (unlikely(test_bit(QUEUE_FLAG_DRAIN, &q->queue_flags))) + return NULL; + spin_lock_irq(q->queue_lock); if (rl->count[rw]+1 >= q->nr_requests) { /* @@ -2506,6 +2468,70 @@ static inline void blk_partition_remap(struct bio *bio) } } +void blk_finish_queue_drain(request_queue_t *q) +{ + struct request_list *rl = &q->rq; + + clear_bit(QUEUE_FLAG_DRAIN, &q->queue_flags); + wake_up(&rl->wait[0]); + wake_up(&rl->wait[1]); + wake_up(&rl->drain); +} + +/* + * We rely on the fact that only requests allocated through blk_alloc_request() + * have io scheduler private data structures associated with them. Any other + * type of request (allocated on stack or through kmalloc()) should not go + * to the io scheduler core, but be attached to the queue head instead. + */ +void blk_wait_queue_drained(request_queue_t *q) +{ + struct request_list *rl = &q->rq; + DEFINE_WAIT(wait); + + spin_lock_irq(q->queue_lock); + set_bit(QUEUE_FLAG_DRAIN, &q->queue_flags); + + while (rl->count[READ] || rl->count[WRITE]) { + prepare_to_wait(&rl->drain, &wait, TASK_UNINTERRUPTIBLE); + + if (rl->count[READ] || rl->count[WRITE]) { + __generic_unplug_device(q); + spin_unlock_irq(q->queue_lock); + io_schedule(); + spin_lock_irq(q->queue_lock); + } + + finish_wait(&rl->drain, &wait); + } + + spin_unlock_irq(q->queue_lock); +} + +/* + * block waiting for the io scheduler being started again. + */ +static inline void block_wait_queue_running(request_queue_t *q) +{ + DEFINE_WAIT(wait); + + while (test_bit(QUEUE_FLAG_DRAIN, &q->queue_flags)) { + struct request_list *rl = &q->rq; + + prepare_to_wait_exclusive(&rl->drain, &wait, + TASK_UNINTERRUPTIBLE); + + /* + * re-check the condition. avoids using prepare_to_wait() + * in the fast path (queue is running) + */ + if (test_bit(QUEUE_FLAG_DRAIN, &q->queue_flags)) + io_schedule(); + + finish_wait(&rl->drain, &wait); + } +} + /** * generic_make_request: hand a buffer to its device driver for I/O * @bio: The bio describing the location in memory and on the device. @@ -2595,6 +2621,8 @@ end_io: if (test_bit(QUEUE_FLAG_DEAD, &q->queue_flags)) goto end_io; + block_wait_queue_running(q); + /* * If this device has partitions, remap block n * of partition p to block n+start(p) of the disk. @@ -3018,6 +3046,7 @@ void kblockd_flush(void) { flush_workqueue(kblockd_workqueue); } +EXPORT_SYMBOL(kblockd_flush); int __init blk_dev_init(void) { @@ -3036,6 +3065,7 @@ int __init blk_dev_init(void) blk_max_low_pfn = max_low_pfn; blk_max_pfn = max_pfn; + return 0; } @@ -3055,6 +3085,7 @@ void put_io_context(struct io_context *ioc) kmem_cache_free(iocontext_cachep, ioc); } } +EXPORT_SYMBOL(put_io_context); /* Called by the exitting task */ void exit_io_context(void) @@ -3106,6 +3137,7 @@ struct io_context *get_io_context(int gfp_flags) local_irq_restore(flags); return ret; } +EXPORT_SYMBOL(get_io_context); void copy_io_context(struct io_context **pdst, struct io_context **psrc) { @@ -3119,6 +3151,7 @@ void copy_io_context(struct io_context **pdst, struct io_context **psrc) *pdst = src; } } +EXPORT_SYMBOL(copy_io_context); void swap_io_context(struct io_context **ioc1, struct io_context **ioc2) { @@ -3127,7 +3160,7 @@ void swap_io_context(struct io_context **ioc1, struct io_context **ioc2) *ioc1 = *ioc2; *ioc2 = temp; } - +EXPORT_SYMBOL(swap_io_context); /* * sysfs parts below @@ -3285,11 +3318,18 @@ static struct queue_sysfs_entry queue_max_hw_sectors_entry = { .show = queue_max_hw_sectors_show, }; +static struct queue_sysfs_entry queue_iosched_entry = { + .attr = {.name = "scheduler", .mode = S_IRUGO | S_IWUSR }, + .show = elv_iosched_show, + .store = elv_iosched_store, +}; + static struct attribute *default_attrs[] = { &queue_requests_entry.attr, &queue_ra_entry.attr, &queue_max_hw_sectors_entry.attr, &queue_max_sectors_entry.attr, + &queue_iosched_entry.attr, NULL, }; diff --git a/drivers/block/noop-iosched.c b/drivers/block/noop-iosched.c index ffef40be1f92..707dddd7d881 100644 --- a/drivers/block/noop-iosched.c +++ b/drivers/block/noop-iosched.c @@ -83,12 +83,31 @@ struct request *elevator_noop_next_request(request_queue_t *q) return NULL; } -elevator_t elevator_noop = { - .elevator_merge_fn = elevator_noop_merge, - .elevator_merge_req_fn = elevator_noop_merge_requests, - .elevator_next_req_fn = elevator_noop_next_request, - .elevator_add_req_fn = elevator_noop_add_request, - .elevator_name = "noop", +static struct elevator_type elevator_noop = { + .ops = { + .elevator_merge_fn = elevator_noop_merge, + .elevator_merge_req_fn = elevator_noop_merge_requests, + .elevator_next_req_fn = elevator_noop_next_request, + .elevator_add_req_fn = elevator_noop_add_request, + }, + .elevator_name = "noop", + .elevator_owner = THIS_MODULE, }; -EXPORT_SYMBOL(elevator_noop); +int noop_init(void) +{ + return elv_register(&elevator_noop); +} + +void noop_exit(void) +{ + elv_unregister(&elevator_noop); +} + +module_init(noop_init); +module_exit(noop_exit); + + +MODULE_AUTHOR("Jens Axboe"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("No-op IO scheduler"); diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index 2249b78487bd..b3714fbd0083 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -1595,8 +1595,8 @@ dasd_alloc_queue(struct dasd_device * device) device->request_queue->queuedata = device; #if 0 - elevator_exit(device->request_queue); - rc = elevator_init(device->request_queue, &elevator_noop); + elevator_exit(device->request_queue->elevator); + rc = elevator_init(device->request_queue, "noop"); if (rc) { blk_cleanup_queue(device->request_queue); return rc; diff --git a/drivers/s390/char/tape_block.c b/drivers/s390/char/tape_block.c index b7f4e7b8be74..1efc9f21229e 100644 --- a/drivers/s390/char/tape_block.c +++ b/drivers/s390/char/tape_block.c @@ -225,8 +225,8 @@ tapeblock_setup_device(struct tape_device * device) if (!blkdat->request_queue) return -ENOMEM; - elevator_exit(blkdat->request_queue); - rc = elevator_init(blkdat->request_queue, &elevator_noop); + elevator_exit(blkdat->request_queue->elevator); + rc = elevator_init(blkdat->request_queue, "noop"); if (rc) goto cleanup_queue; diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 4efe45d1af7e..5e4a6ab84ecb 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -19,8 +19,8 @@ struct request_queue; typedef struct request_queue request_queue_t; -struct elevator_s; -typedef struct elevator_s elevator_t; +struct elevator_queue; +typedef struct elevator_queue elevator_t; struct request_pm_state; #define BLKDEV_MIN_RQ 4 @@ -80,6 +80,7 @@ struct request_list { int count[2]; mempool_t *rq_pool; wait_queue_head_t wait[2]; + wait_queue_head_t drain; }; #define BLK_MAX_CDB 16 @@ -279,7 +280,7 @@ struct request_queue */ struct list_head queue_head; struct request *last_merge; - elevator_t elevator; + elevator_t *elevator; /* * the queue request freelist, one for reads and one for writes @@ -381,6 +382,7 @@ struct request_queue #define QUEUE_FLAG_REENTER 6 /* Re-entrancy avoidance */ #define QUEUE_FLAG_PLUGGED 7 /* queue is plugged */ #define QUEUE_FLAG_ORDERED 8 /* supports ordered writes */ +#define QUEUE_FLAG_DRAIN 9 /* draining queue for sched switch */ #define blk_queue_plugged(q) test_bit(QUEUE_FLAG_PLUGGED, &(q)->queue_flags) #define blk_queue_tagged(q) test_bit(QUEUE_FLAG_QUEUED, &(q)->queue_flags) @@ -617,6 +619,8 @@ extern void blk_dump_rq_flags(struct request *, char *); extern void generic_unplug_device(request_queue_t *); extern void __generic_unplug_device(request_queue_t *); extern long nr_blockdev_pages(void); +extern void blk_wait_queue_drained(request_queue_t *); +extern void blk_finish_queue_drain(request_queue_t *); int blk_get_queue(request_queue_t *); request_queue_t *blk_alloc_queue(int); diff --git a/include/linux/elevator.h b/include/linux/elevator.h index 27e8183f4776..95cdfb5bb790 100644 --- a/include/linux/elevator.h +++ b/include/linux/elevator.h @@ -22,9 +22,9 @@ typedef int (elevator_set_req_fn) (request_queue_t *, struct request *, int); typedef void (elevator_put_req_fn) (request_queue_t *, struct request *); typedef int (elevator_init_fn) (request_queue_t *, elevator_t *); -typedef void (elevator_exit_fn) (request_queue_t *, elevator_t *); +typedef void (elevator_exit_fn) (elevator_t *); -struct elevator_s +struct elevator_ops { elevator_merge_fn *elevator_merge_fn; elevator_merged_fn *elevator_merged_fn; @@ -48,12 +48,32 @@ struct elevator_s elevator_init_fn *elevator_init_fn; elevator_exit_fn *elevator_exit_fn; +}; - void *elevator_data; +#define ELV_NAME_MAX (16) - struct kobject kobj; +/* + * identifies an elevator type, such as AS or deadline + */ +struct elevator_type +{ + struct list_head list; + struct elevator_ops ops; + struct elevator_type *elevator_type; struct kobj_type *elevator_ktype; - const char *elevator_name; + char elevator_name[ELV_NAME_MAX]; + struct module *elevator_owner; +}; + +/* + * each queue has an elevator_queue assoicated with it + */ +struct elevator_queue +{ + struct elevator_ops *ops; + void *elevator_data; + struct kobject kobj; + struct elevator_type *elevator_type; }; /* @@ -79,28 +99,19 @@ extern int elv_set_request(request_queue_t *, struct request *, int); extern void elv_put_request(request_queue_t *, struct request *); /* - * noop I/O scheduler. always merges, always inserts new request at tail - */ -extern elevator_t elevator_noop; - -/* - * deadline i/o scheduler. uses request time outs to prevent indefinite - * starvation - */ -extern elevator_t iosched_deadline; - -/* - * anticipatory I/O scheduler + * io scheduler registration */ -extern elevator_t iosched_as; +extern int elv_register(struct elevator_type *); +extern void elv_unregister(struct elevator_type *); /* - * completely fair queueing I/O scheduler + * io scheduler sysfs switching */ -extern elevator_t iosched_cfq; +extern ssize_t elv_iosched_show(request_queue_t *, char *); +extern ssize_t elv_iosched_store(request_queue_t *, const char *, size_t); -extern int elevator_init(request_queue_t *, elevator_t *); -extern void elevator_exit(request_queue_t *); +extern int elevator_init(request_queue_t *, char *); +extern void elevator_exit(elevator_t *); extern int elv_rq_merge_ok(struct request *, struct bio *); extern int elv_try_merge(struct request *, struct bio *); extern int elv_try_last_merge(request_queue_t *, struct bio *); -- cgit v1.2.3 From f9887e4a0cc489b33776d43be7362c1284e68819 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Mon, 18 Oct 2004 18:01:41 -0700 Subject: [PATCH] cfq-v2 I/O scheduler update Here is the next incarnation of the CFQ io scheduler, so far known as CFQ v2 locally. It attempts to address some of the limitations of the original CFQ io scheduler (hence forth known as CFQ v1). Some of the problems with CFQ v1 are: - It does accounting for the lifetime of the cfq_queue, which is setup and torn down for the time when a process has io in flight. For a fork heavy work load (such as a kernel compile, for instance), new processes can effectively starve io of running processes. This is in part due to the fact that CFQ v1 gives preference to a new processes to get better latency numbers. Removing that heuristic is not an option exactly because of that. - It makes no attempts to address inter-cfq_queue fairness. - It makes no attempt to limit upper latency bound of a single request. - It only provides per-tgid grouping. You need to change the source to group on a different criteria. - It uses a mempool for the cfq_queues. Theoretically this could deadlock if io bound processes never exit. - The may_queue() logic can be unfair since it fluctuates quickly, thus leaving processes sleeping while new processes are allowed to allocate a request. CFQ v2 attempts to fix these issues. It uses the process io_context logic to maintain a cfq_queue lifetime of the duration of the process (and its io). This means we can now be a lot more clever in deciding which process is allowed to queue or dispatch io to the device. The cfq_io_context is per-process per-queue, this is an extension to what AS currently does in that we truly do have a unique per-process identifier for io grouping. Busy queues are sorted by service time used, sub sorted by in_flight requests. Queues that have no io in flight are also preferred at dispatch time. Accounting is done on completion time of a request, or with a fixed cost for tagged command queueing. Requests are fifo'ed like with deadline, to make sure that a single request doesn't stay in the io scheduler for ages. Process grouping is selectable at runtime. I provide 4 grouping criterias: process group, thread group id, user id, and group id. As usual, settings are sysfs tweakable in /sys/block//queue/iosched axboe@apu:[.]s/block/hda/queue/iosched $ ls back_seek_max fifo_batch_expire find_best_crq queued back_seek_penalty fifo_expire_async key_type show_status clear_elapsed fifo_expire_sync quantum tagged In order, each of these settings control: back_seek_max back_seek_penalty: Useful logic stolen from AS that allow small backwards seeks in the io stream if we deem them useful. CFQ uses a strict ascending elevator otherwise. _max controls the maximum allowed backwards seek, defaulting to 16MiB. _penalty denotes how expensive we account a backwards seek compared to a forward seek. Default is 2, meaning it's twice as expensive. clear_elapsed: Really a debug switch, will go away in the future. It clears the maximum values for completion and dispatch time, shown in show_status. fifo_batch_expire fifo_batch_async fifo_batch_sync: The settings for the expiry fifo. batch_expire is how often we allow the fifo expire to control which request to select. Default is 125ms. _async is the deadline for async requests (typically writes), _sync is the deadline for sync requests (reads and sync writes). Defaults are, respectively, 5 seconds and 0.5 seconds. key_type: The grouping key. Can be set to pgid, tgid, uid, or gid. The current value is shown bracketed: axboe@apu:[.]s/block/hda/queue/iosched $ cat key_type [pgid] tgid uid gid Default is tgid. To set, simply echo any of the 4 words into the file. quantum: The amount of requests we select for dispatch when the driver asks for work to do and the current pending list is empty. Default is 4. queued: The minimum amount of requests a group is allowed to queue. Default is 8. show_status: Debug output showing the current state of the queues. tagged: Set this to 1 if the device is using tagged command queueing. This cannot be reliably detected by CFQ yet, since most drivers don't use the block layer (well it could, by looking at number of requests being between dispatch and completion. but not completely reliably). Default is 0. The patch is a little big, but works reliably here on my laptop. There are a number of other changes and fixes in there (like converting to hlist for hashes). The code is commented a lot better, CFQ v1 has basically no comments (reflecting that it was writting in one go, no touched or tuned much since then). This is of course only done to increase the AAF, akpm acceptance factor. Since I'm on the road, I cannot provide any really good numbers of CFQ v1 compared to v2, maybe someone will help me out there. Signed-off-by: Jens Axboe Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/block/as-iosched.c | 4 +- drivers/block/cfq-iosched.c | 1459 ++++++++++++++++++++++++++++++++++++------- drivers/block/elevator.c | 2 +- drivers/block/ll_rw_blk.c | 115 ++-- include/linux/blkdev.h | 18 + include/linux/elevator.h | 9 + 6 files changed, 1337 insertions(+), 270 deletions(-) (limited to 'drivers') diff --git a/drivers/block/as-iosched.c b/drivers/block/as-iosched.c index bb3e9b5bab3c..b049848c19c3 100644 --- a/drivers/block/as-iosched.c +++ b/drivers/block/as-iosched.c @@ -1828,14 +1828,14 @@ static int as_set_request(request_queue_t *q, struct request *rq, int gfp_mask) static int as_may_queue(request_queue_t *q, int rw) { - int ret = 0; + int ret = ELV_MQUEUE_MAY; struct as_data *ad = q->elevator->elevator_data; struct io_context *ioc; if (ad->antic_status == ANTIC_WAIT_REQ || ad->antic_status == ANTIC_WAIT_NEXT) { ioc = as_get_io_context(); if (ad->io_context == ioc) - ret = 1; + ret = ELV_MQUEUE_MUST; put_io_context(ioc); } diff --git a/drivers/block/cfq-iosched.c b/drivers/block/cfq-iosched.c index 6a424dc65823..738ff90bb2c1 100644 --- a/drivers/block/cfq-iosched.c +++ b/drivers/block/cfq-iosched.c @@ -22,96 +22,216 @@ #include #include +static unsigned long max_elapsed_crq; +static unsigned long max_elapsed_dispatch; + /* * tunables */ -static int cfq_quantum = 4; -static int cfq_queued = 8; +static int cfq_quantum = 4; /* max queue in one round of service */ +static int cfq_queued = 8; /* minimum rq allocate limit per-queue*/ +static int cfq_service = HZ; /* period over which service is avg */ +static int cfq_fifo_expire_r = HZ / 2; /* fifo timeout for sync requests */ +static int cfq_fifo_expire_w = 5 * HZ; /* fifo timeout for async requests */ +static int cfq_fifo_rate = HZ / 8; /* fifo expiry rate */ +static int cfq_back_max = 16 * 1024; /* maximum backwards seek, in KiB */ +static int cfq_back_penalty = 2; /* penalty of a backwards seek */ +/* + * for the hash of cfqq inside the cfqd + */ #define CFQ_QHASH_SHIFT 6 #define CFQ_QHASH_ENTRIES (1 << CFQ_QHASH_SHIFT) -#define list_entry_qhash(entry) list_entry((entry), struct cfq_queue, cfq_hash) +#define list_entry_qhash(entry) hlist_entry((entry), struct cfq_queue, cfq_hash) -#define CFQ_MHASH_SHIFT 8 +/* + * for the hash of crq inside the cfqq + */ +#define CFQ_MHASH_SHIFT 6 #define CFQ_MHASH_BLOCK(sec) ((sec) >> 3) #define CFQ_MHASH_ENTRIES (1 << CFQ_MHASH_SHIFT) -#define CFQ_MHASH_FN(sec) (hash_long(CFQ_MHASH_BLOCK((sec)),CFQ_MHASH_SHIFT)) -#define ON_MHASH(crq) !list_empty(&(crq)->hash) +#define CFQ_MHASH_FN(sec) hash_long(CFQ_MHASH_BLOCK(sec), CFQ_MHASH_SHIFT) #define rq_hash_key(rq) ((rq)->sector + (rq)->nr_sectors) -#define list_entry_hash(ptr) list_entry((ptr), struct cfq_rq, hash) +#define list_entry_hash(ptr) hlist_entry((ptr), struct cfq_rq, hash) #define list_entry_cfqq(ptr) list_entry((ptr), struct cfq_queue, cfq_list) -#define RQ_DATA(rq) ((struct cfq_rq *) (rq)->elevator_private) +#define RQ_DATA(rq) (rq)->elevator_private + +/* + * rb-tree defines + */ +#define RB_NONE (2) +#define RB_EMPTY(node) ((node)->rb_node == NULL) +#define RB_CLEAR_COLOR(node) (node)->rb_color = RB_NONE +#define RB_CLEAR(node) do { \ + (node)->rb_parent = NULL; \ + RB_CLEAR_COLOR((node)); \ + (node)->rb_right = NULL; \ + (node)->rb_left = NULL; \ +} while (0) +#define RB_CLEAR_ROOT(root) ((root)->rb_node = NULL) +#define ON_RB(node) ((node)->rb_color != RB_NONE) +#define rb_entry_crq(node) rb_entry((node), struct cfq_rq, rb_node) +#define rq_rb_key(rq) (rq)->sector + +/* + * threshold for switching off non-tag accounting + */ +#define CFQ_MAX_TAG (4) + +/* + * sort key types and names + */ +enum { + CFQ_KEY_PGID, + CFQ_KEY_TGID, + CFQ_KEY_UID, + CFQ_KEY_GID, + CFQ_KEY_LAST, +}; + +static char *cfq_key_types[] = { "pgid", "tgid", "uid", "gid", NULL }; + +/* + * spare queue + */ +#define CFQ_KEY_SPARE (~0UL) static kmem_cache_t *crq_pool; static kmem_cache_t *cfq_pool; -static mempool_t *cfq_mpool; +static kmem_cache_t *cfq_ioc_pool; struct cfq_data { struct list_head rr_list; - struct list_head *dispatch; - struct list_head *cfq_hash; + struct list_head empty_list; - struct list_head *crq_hash; + struct hlist_head *cfq_hash; + struct hlist_head *crq_hash; + /* queues on rr_list (ie they have pending requests */ unsigned int busy_queues; + unsigned int max_queued; + atomic_t ref; + + int key_type; + mempool_t *crq_pool; request_queue_t *queue; + sector_t last_sector; + + int rq_in_driver; + /* - * tunables + * tunables, see top of file */ unsigned int cfq_quantum; unsigned int cfq_queued; + unsigned int cfq_fifo_expire_r; + unsigned int cfq_fifo_expire_w; + unsigned int cfq_fifo_batch_expire; + unsigned int cfq_back_penalty; + unsigned int cfq_back_max; + unsigned int find_best_crq; + + unsigned int cfq_tagged; }; struct cfq_queue { - struct list_head cfq_hash; + /* reference count */ + atomic_t ref; + /* parent cfq_data */ + struct cfq_data *cfqd; + /* hash of mergeable requests */ + struct hlist_node cfq_hash; + /* hash key */ + unsigned long key; + /* whether queue is on rr (or empty) list */ + int on_rr; + /* on either rr or empty list of cfqd */ struct list_head cfq_list; + /* sorted list of pending requests */ struct rb_root sort_list; - int pid; + /* if fifo isn't expired, next request to serve */ + struct cfq_rq *next_crq; + /* requests queued in sort_list */ int queued[2]; -#if 0 - /* - * with a simple addition like this, we can do io priorities. almost. - * does need a split request free list, too. - */ - int io_prio -#endif + /* currently allocated requests */ + int allocated[2]; + /* fifo list of requests in sort_list */ + struct list_head fifo[2]; + /* last time fifo expired */ + unsigned long last_fifo_expire; + + int key_type; + + unsigned long service_start; + unsigned long service_used; + + unsigned int max_rate; + + /* number of requests that have been handed to the driver */ + int in_flight; + /* number of currently allocated requests */ + int alloc_limit[2]; }; struct cfq_rq { struct rb_node rb_node; sector_t rb_key; - struct request *request; + struct hlist_node hash; struct cfq_queue *cfq_queue; + struct cfq_io_context *io_context; + + unsigned long service_start; + unsigned long queue_start; - struct list_head hash; + unsigned int in_flight : 1; + unsigned int accounted : 1; + unsigned int is_sync : 1; + unsigned int is_write : 1; }; -static void cfq_put_queue(struct cfq_data *cfqd, struct cfq_queue *cfqq); -static struct cfq_queue *cfq_find_cfq_hash(struct cfq_data *cfqd, int pid); -static void cfq_dispatch_sort(struct cfq_data *cfqd, struct cfq_queue *cfqq, - struct cfq_rq *crq); +static struct cfq_queue *cfq_find_cfq_hash(struct cfq_data *, unsigned long); +static void cfq_dispatch_sort(request_queue_t *, struct cfq_rq *); +static void cfq_update_next_crq(struct cfq_rq *); +static void cfq_put_cfqd(struct cfq_data *cfqd); /* - * lots of deadline iosched dupes, can be abstracted later... + * what the fairness is based on (ie how processes are grouped and + * differentiated) */ -static inline void __cfq_del_crq_hash(struct cfq_rq *crq) +static inline unsigned long +cfq_hash_key(struct cfq_data *cfqd, struct task_struct *tsk) { - list_del_init(&crq->hash); + /* + * optimize this so that ->key_type is the offset into the struct + */ + switch (cfqd->key_type) { + case CFQ_KEY_PGID: + return process_group(tsk); + default: + case CFQ_KEY_TGID: + return tsk->tgid; + case CFQ_KEY_UID: + return tsk->uid; + case CFQ_KEY_GID: + return tsk->gid; + } } +/* + * lots of deadline iosched dupes, can be abstracted later... + */ static inline void cfq_del_crq_hash(struct cfq_rq *crq) { - if (ON_MHASH(crq)) - __cfq_del_crq_hash(crq); + hlist_del_init(&crq->hash); } static void cfq_remove_merge_hints(request_queue_t *q, struct cfq_rq *crq) @@ -120,32 +240,32 @@ static void cfq_remove_merge_hints(request_queue_t *q, struct cfq_rq *crq) if (q->last_merge == crq->request) q->last_merge = NULL; + + cfq_update_next_crq(crq); } static inline void cfq_add_crq_hash(struct cfq_data *cfqd, struct cfq_rq *crq) { - struct request *rq = crq->request; + const int hash_idx = CFQ_MHASH_FN(rq_hash_key(crq->request)); - BUG_ON(ON_MHASH(crq)); + BUG_ON(!hlist_unhashed(&crq->hash)); - list_add(&crq->hash, &cfqd->crq_hash[CFQ_MHASH_FN(rq_hash_key(rq))]); + hlist_add_head(&crq->hash, &cfqd->crq_hash[hash_idx]); } static struct request *cfq_find_rq_hash(struct cfq_data *cfqd, sector_t offset) { - struct list_head *hash_list = &cfqd->crq_hash[CFQ_MHASH_FN(offset)]; - struct list_head *entry, *next = hash_list->next; + struct hlist_head *hash_list = &cfqd->crq_hash[CFQ_MHASH_FN(offset)]; + struct hlist_node *entry, *next; - while ((entry = next) != hash_list) { + hlist_for_each_safe(entry, next, hash_list) { struct cfq_rq *crq = list_entry_hash(entry); struct request *__rq = crq->request; - next = entry->next; - - BUG_ON(!ON_MHASH(crq)); + BUG_ON(hlist_unhashed(&crq->hash)); if (!rq_mergeable(__rq)) { - __cfq_del_crq_hash(crq); + cfq_del_crq_hash(crq); continue; } @@ -157,29 +277,257 @@ static struct request *cfq_find_rq_hash(struct cfq_data *cfqd, sector_t offset) } /* - * rb tree support functions + * Lifted from AS - choose which of crq1 and crq2 that is best served now. + * We choose the request that is closest to the head right now. Distance + * behind the head are penalized and only allowed to a certain extent. */ -#define RB_NONE (2) -#define RB_EMPTY(node) ((node)->rb_node == NULL) -#define RB_CLEAR(node) ((node)->rb_color = RB_NONE) -#define RB_CLEAR_ROOT(root) ((root)->rb_node = NULL) -#define ON_RB(node) ((node)->rb_color != RB_NONE) -#define rb_entry_crq(node) rb_entry((node), struct cfq_rq, rb_node) -#define rq_rb_key(rq) (rq)->sector +static struct cfq_rq * +cfq_choose_req(struct cfq_data *cfqd, struct cfq_rq *crq1, struct cfq_rq *crq2) +{ + sector_t last, s1, s2, d1 = 0, d2 = 0; + int r1_wrap = 0, r2_wrap = 0; /* requests are behind the disk head */ + unsigned long back_max; + + if (crq1 == NULL || crq1 == crq2) + return crq2; + if (crq2 == NULL) + return crq1; + + s1 = crq1->request->sector; + s2 = crq2->request->sector; -static inline void cfq_del_crq_rb(struct cfq_queue *cfqq, struct cfq_rq *crq) + last = cfqd->last_sector; + +#if 0 + if (!list_empty(&cfqd->queue->queue_head)) { + struct list_head *entry = &cfqd->queue->queue_head; + unsigned long distance = ~0UL; + struct request *rq; + + while ((entry = entry->prev) != &cfqd->queue->queue_head) { + rq = list_entry_rq(entry); + + if (blk_barrier_rq(rq)) + break; + + if (distance < abs(s1 - rq->sector + rq->nr_sectors)) { + distance = abs(s1 - rq->sector +rq->nr_sectors); + last = rq->sector + rq->nr_sectors; + } + if (distance < abs(s2 - rq->sector + rq->nr_sectors)) { + distance = abs(s2 - rq->sector +rq->nr_sectors); + last = rq->sector + rq->nr_sectors; + } + } + } +#endif + + /* + * by definition, 1KiB is 2 sectors + */ + back_max = cfqd->cfq_back_max * 2; + + /* + * Strict one way elevator _except_ in the case where we allow + * short backward seeks which are biased as twice the cost of a + * similar forward seek. + */ + if (s1 >= last) + d1 = s1 - last; + else if (s1 + back_max >= last) + d1 = (last - s1) * cfqd->cfq_back_penalty; + else + r1_wrap = 1; + + if (s2 >= last) + d2 = s2 - last; + else if (s2 + back_max >= last) + d2 = (last - s2) * cfqd->cfq_back_penalty; + else + r2_wrap = 1; + + /* Found required data */ + if (!r1_wrap && r2_wrap) + return crq1; + else if (!r2_wrap && r1_wrap) + return crq2; + else if (r1_wrap && r2_wrap) { + /* both behind the head */ + if (s1 <= s2) + return crq1; + else + return crq2; + } + + /* Both requests in front of the head */ + if (d1 < d2) + return crq1; + else if (d2 < d1) + return crq2; + else { + if (s1 >= s2) + return crq1; + else + return crq2; + } +} + +/* + * would be nice to take fifo expire time into account as well + */ +static struct cfq_rq * +cfq_find_next_crq(struct cfq_data *cfqd, struct cfq_queue *cfqq, + struct cfq_rq *last) { + struct cfq_rq *crq_next = NULL, *crq_prev = NULL; + struct rb_node *rbnext, *rbprev; + + if (!ON_RB(&last->rb_node)) + return NULL; + + if ((rbnext = rb_next(&last->rb_node)) == NULL) + rbnext = rb_first(&cfqq->sort_list); + + rbprev = rb_prev(&last->rb_node); + + if (rbprev) + crq_prev = rb_entry_crq(rbprev); + if (rbnext) + crq_next = rb_entry_crq(rbnext); + + return cfq_choose_req(cfqd, crq_next, crq_prev); +} + +static void cfq_update_next_crq(struct cfq_rq *crq) +{ + struct cfq_queue *cfqq = crq->cfq_queue; + + if (cfqq->next_crq == crq) + cfqq->next_crq = cfq_find_next_crq(cfqq->cfqd, cfqq, crq); +} + +static int cfq_check_sort_rr_list(struct cfq_queue *cfqq) +{ + struct list_head *head = &cfqq->cfqd->rr_list; + struct list_head *next, *prev; + + /* + * list might still be ordered + */ + next = cfqq->cfq_list.next; + if (next != head) { + struct cfq_queue *cnext = list_entry_cfqq(next); + + if (cfqq->service_used > cnext->service_used) + return 1; + } + + prev = cfqq->cfq_list.prev; + if (prev != head) { + struct cfq_queue *cprev = list_entry_cfqq(prev); + + if (cfqq->service_used < cprev->service_used) + return 1; + } + + return 0; +} + +static void cfq_sort_rr_list(struct cfq_queue *cfqq, int new_queue) +{ + struct list_head *entry = &cfqq->cfqd->rr_list; + + if (!cfqq->on_rr) + return; + if (!new_queue && !cfq_check_sort_rr_list(cfqq)) + return; + + list_del(&cfqq->cfq_list); + + /* + * sort by our mean service_used, sub-sort by in-flight requests + */ + while ((entry = entry->prev) != &cfqq->cfqd->rr_list) { + struct cfq_queue *__cfqq = list_entry_cfqq(entry); + + if (cfqq->service_used > __cfqq->service_used) + break; + else if (cfqq->service_used == __cfqq->service_used) { + struct list_head *prv; + + while ((prv = entry->prev) != &cfqq->cfqd->rr_list) { + __cfqq = list_entry_cfqq(prv); + + WARN_ON(__cfqq->service_used > cfqq->service_used); + if (cfqq->service_used != __cfqq->service_used) + break; + if (cfqq->in_flight > __cfqq->in_flight) + break; + + entry = prv; + } + } + } + + list_add(&cfqq->cfq_list, entry); +} + +/* + * add to busy list of queues for service, trying to be fair in ordering + * the pending list according to requests serviced + */ +static inline void +cfq_add_cfqq_rr(struct cfq_data *cfqd, struct cfq_queue *cfqq) +{ + /* + * it's currently on the empty list + */ + cfqq->on_rr = 1; + cfqd->busy_queues++; + + if (time_after(jiffies, cfqq->service_start + cfq_service)) + cfqq->service_used >>= 3; + + cfq_sort_rr_list(cfqq, 1); +} + +static inline void +cfq_del_cfqq_rr(struct cfq_data *cfqd, struct cfq_queue *cfqq) +{ + list_move(&cfqq->cfq_list, &cfqd->empty_list); + cfqq->on_rr = 0; + + BUG_ON(!cfqd->busy_queues); + cfqd->busy_queues--; +} + +/* + * rb tree support functions + */ +static inline void cfq_del_crq_rb(struct cfq_rq *crq) +{ + struct cfq_queue *cfqq = crq->cfq_queue; + if (ON_RB(&crq->rb_node)) { - cfqq->queued[rq_data_dir(crq->request)]--; + struct cfq_data *cfqd = cfqq->cfqd; + + BUG_ON(!cfqq->queued[crq->is_sync]); + + cfq_update_next_crq(crq); + + cfqq->queued[crq->is_sync]--; rb_erase(&crq->rb_node, &cfqq->sort_list); - crq->cfq_queue = NULL; + RB_CLEAR_COLOR(&crq->rb_node); + + if (RB_EMPTY(&cfqq->sort_list) && cfqq->on_rr) + cfq_del_cfqq_rr(cfqd, cfqq); } } static struct cfq_rq * -__cfq_add_crq_rb(struct cfq_queue *cfqq, struct cfq_rq *crq) +__cfq_add_crq_rb(struct cfq_rq *crq) { - struct rb_node **p = &cfqq->sort_list.rb_node; + struct rb_node **p = &crq->cfq_queue->sort_list.rb_node; struct rb_node *parent = NULL; struct cfq_rq *__crq; @@ -199,30 +547,50 @@ __cfq_add_crq_rb(struct cfq_queue *cfqq, struct cfq_rq *crq) return NULL; } -static void -cfq_add_crq_rb(struct cfq_data *cfqd, struct cfq_queue *cfqq,struct cfq_rq *crq) +static void cfq_add_crq_rb(struct cfq_rq *crq) { + struct cfq_queue *cfqq = crq->cfq_queue; + struct cfq_data *cfqd = cfqq->cfqd; struct request *rq = crq->request; struct cfq_rq *__alias; crq->rb_key = rq_rb_key(rq); - cfqq->queued[rq_data_dir(rq)]++; -retry: - __alias = __cfq_add_crq_rb(cfqq, crq); - if (!__alias) { - rb_insert_color(&crq->rb_node, &cfqq->sort_list); - crq->cfq_queue = cfqq; - return; + cfqq->queued[crq->is_sync]++; + + /* + * looks a little odd, but the first insert might return an alias. + * if that happens, put the alias on the dispatch list + */ + while ((__alias = __cfq_add_crq_rb(crq)) != NULL) + cfq_dispatch_sort(cfqd->queue, __alias); + + rb_insert_color(&crq->rb_node, &cfqq->sort_list); + + if (!cfqq->on_rr) + cfq_add_cfqq_rr(cfqd, cfqq); + + /* + * check if this request is a better next-serve candidate + */ + cfqq->next_crq = cfq_choose_req(cfqd, cfqq->next_crq, crq); +} + +static inline void +cfq_reposition_crq_rb(struct cfq_queue *cfqq, struct cfq_rq *crq) +{ + if (ON_RB(&crq->rb_node)) { + rb_erase(&crq->rb_node, &cfqq->sort_list); + cfqq->queued[crq->is_sync]--; } - cfq_dispatch_sort(cfqd, cfqq, __alias); - goto retry; + cfq_add_crq_rb(crq); } static struct request * cfq_find_rq_rb(struct cfq_data *cfqd, sector_t sector) { - struct cfq_queue *cfqq = cfq_find_cfq_hash(cfqd, current->tgid); + const unsigned long key = cfq_hash_key(cfqd, current); + struct cfq_queue *cfqq = cfq_find_cfq_hash(cfqd, key); struct rb_node *n; if (!cfqq) @@ -244,23 +612,37 @@ out: return NULL; } -static void cfq_remove_request(request_queue_t *q, struct request *rq) +/* + * make sure the service time gets corrected on reissue of this request + */ +static void cfq_requeue_request(request_queue_t *q, struct request *rq) { - struct cfq_data *cfqd = q->elevator->elevator_data; struct cfq_rq *crq = RQ_DATA(rq); if (crq) { struct cfq_queue *cfqq = crq->cfq_queue; + if (cfqq->cfqd->cfq_tagged) { + cfqq->service_used--; + cfq_sort_rr_list(cfqq, 0); + } + + crq->accounted = 0; + cfqq->cfqd->rq_in_driver--; + } + list_add(&rq->queuelist, &q->queue_head); +} + +static void cfq_remove_request(request_queue_t *q, struct request *rq) +{ + struct cfq_rq *crq = RQ_DATA(rq); + + if (crq) { cfq_remove_merge_hints(q, crq); list_del_init(&rq->queuelist); - if (cfqq) { - cfq_del_crq_rb(cfqq, crq); - - if (RB_EMPTY(&cfqq->sort_list)) - cfq_put_queue(cfqd, cfqq); - } + if (crq->cfq_queue) + cfq_del_crq_rb(crq); } } @@ -314,92 +696,240 @@ static void cfq_merged_request(request_queue_t *q, struct request *req) if (ON_RB(&crq->rb_node) && (rq_rb_key(req) != crq->rb_key)) { struct cfq_queue *cfqq = crq->cfq_queue; - cfq_del_crq_rb(cfqq, crq); - cfq_add_crq_rb(cfqd, cfqq, crq); + cfq_update_next_crq(crq); + cfq_reposition_crq_rb(cfqq, crq); } q->last_merge = req; } static void -cfq_merged_requests(request_queue_t *q, struct request *req, +cfq_merged_requests(request_queue_t *q, struct request *rq, struct request *next) { - cfq_merged_request(q, req); + struct cfq_rq *crq = RQ_DATA(rq); + struct cfq_rq *cnext = RQ_DATA(next); + + cfq_merged_request(q, rq); + + if (!list_empty(&rq->queuelist) && !list_empty(&next->queuelist)) { + if (time_before(cnext->queue_start, crq->queue_start)) { + list_move(&rq->queuelist, &next->queuelist); + crq->queue_start = cnext->queue_start; + } + } + + cfq_update_next_crq(cnext); cfq_remove_request(q, next); } -static void -cfq_dispatch_sort(struct cfq_data *cfqd, struct cfq_queue *cfqq, - struct cfq_rq *crq) +/* + * we dispatch cfqd->cfq_quantum requests in total from the rr_list queues, + * this function sector sorts the selected request to minimize seeks. we start + * at cfqd->last_sector, not 0. + */ +static void cfq_dispatch_sort(request_queue_t *q, struct cfq_rq *crq) { - struct list_head *head = cfqd->dispatch, *entry = head; + struct cfq_data *cfqd = q->elevator->elevator_data; + struct cfq_queue *cfqq = crq->cfq_queue; + struct list_head *head = &q->queue_head, *entry = head; struct request *__rq; + sector_t last; - cfq_del_crq_rb(cfqq, crq); - cfq_remove_merge_hints(cfqd->queue, crq); + cfq_del_crq_rb(crq); + cfq_remove_merge_hints(q, crq); + list_del(&crq->request->queuelist); - if (!list_empty(head)) { - __rq = list_entry_rq(head->next); + last = cfqd->last_sector; + while ((entry = entry->prev) != head) { + __rq = list_entry_rq(entry); - if (crq->request->sector < __rq->sector) { - entry = head->prev; - goto link; + if (blk_barrier_rq(crq->request)) + break; + if (!blk_fs_request(crq->request)) + break; + + if (crq->request->sector > __rq->sector) + break; + if (__rq->sector > last && crq->request->sector < last) { + last = crq->request->sector; + break; } } - while ((entry = entry->prev) != head) { - __rq = list_entry_rq(entry); + cfqd->last_sector = last; + crq->in_flight = 1; + cfqq->in_flight++; + list_add(&crq->request->queuelist, entry); +} - if (crq->request->sector <= __rq->sector) - break; +/* + * return expired entry, or NULL to just start from scratch in rbtree + */ +static inline struct cfq_rq *cfq_check_fifo(struct cfq_queue *cfqq) +{ + struct cfq_data *cfqd = cfqq->cfqd; + const int reads = !list_empty(&cfqq->fifo[0]); + const int writes = !list_empty(&cfqq->fifo[1]); + unsigned long now = jiffies; + struct cfq_rq *crq; + + if (time_before(now, cfqq->last_fifo_expire + cfqd->cfq_fifo_batch_expire)) + return NULL; + + crq = RQ_DATA(list_entry(cfqq->fifo[0].next, struct request, queuelist)); + if (reads && time_after(now, crq->queue_start + cfqd->cfq_fifo_expire_r)) { + cfqq->last_fifo_expire = now; + return crq; } -link: - list_add_tail(&crq->request->queuelist, entry); + crq = RQ_DATA(list_entry(cfqq->fifo[1].next, struct request, queuelist)); + if (writes && time_after(now, crq->queue_start + cfqd->cfq_fifo_expire_w)) { + cfqq->last_fifo_expire = now; + return crq; + } + + return NULL; } +/* + * dispatch a single request from given queue + */ static inline void -__cfq_dispatch_requests(request_queue_t *q, struct cfq_data *cfqd, - struct cfq_queue *cfqq) +cfq_dispatch_request(request_queue_t *q, struct cfq_data *cfqd, + struct cfq_queue *cfqq) { - struct cfq_rq *crq = rb_entry_crq(rb_first(&cfqq->sort_list)); + struct cfq_rq *crq; + + /* + * follow expired path, else get first next available + */ + if ((crq = cfq_check_fifo(cfqq)) == NULL) { + if (cfqd->find_best_crq) + crq = cfqq->next_crq; + else + crq = rb_entry_crq(rb_first(&cfqq->sort_list)); + } - cfq_dispatch_sort(cfqd, cfqq, crq); + cfqd->last_sector = crq->request->sector + crq->request->nr_sectors; + + /* + * finally, insert request into driver list + */ + cfq_dispatch_sort(q, crq); } -static int cfq_dispatch_requests(request_queue_t *q, struct cfq_data *cfqd) +static int cfq_dispatch_requests(request_queue_t *q, int max_dispatch) { + struct cfq_data *cfqd = q->elevator->elevator_data; struct cfq_queue *cfqq; struct list_head *entry, *tmp; - int ret, queued, good_queues; + int queued, busy_queues, first_round; if (list_empty(&cfqd->rr_list)) return 0; - queued = ret = 0; + queued = 0; + first_round = 1; restart: - good_queues = 0; + busy_queues = 0; list_for_each_safe(entry, tmp, &cfqd->rr_list) { - cfqq = list_entry_cfqq(cfqd->rr_list.next); + cfqq = list_entry_cfqq(entry); BUG_ON(RB_EMPTY(&cfqq->sort_list)); - __cfq_dispatch_requests(q, cfqd, cfqq); + /* + * first round of queueing, only select from queues that + * don't already have io in-flight + */ + if (first_round && cfqq->in_flight) + continue; + + cfq_dispatch_request(q, cfqd, cfqq); - if (RB_EMPTY(&cfqq->sort_list)) - cfq_put_queue(cfqd, cfqq); - else - good_queues++; + if (!RB_EMPTY(&cfqq->sort_list)) + busy_queues++; queued++; - ret = 1; } - if ((queued < cfqd->cfq_quantum) && good_queues) + if ((queued < max_dispatch) && (busy_queues || first_round)) { + first_round = 0; goto restart; + } - return ret; + return queued; +} + +static inline void cfq_account_dispatch(struct cfq_rq *crq) +{ + struct cfq_queue *cfqq = crq->cfq_queue; + struct cfq_data *cfqd = cfqq->cfqd; + unsigned long now, elapsed; + + /* + * accounted bit is necessary since some drivers will call + * elv_next_request() many times for the same request (eg ide) + */ + if (crq->accounted) + return; + + now = jiffies; + if (cfqq->service_start == ~0UL) + cfqq->service_start = now; + + /* + * on drives with tagged command queueing, command turn-around time + * doesn't necessarily reflect the time spent processing this very + * command inside the drive. so do the accounting differently there, + * by just sorting on the number of requests + */ + if (cfqd->cfq_tagged) { + if (time_after(now, cfqq->service_start + cfq_service)) { + cfqq->service_start = now; + cfqq->service_used /= 10; + } + + cfqq->service_used++; + cfq_sort_rr_list(cfqq, 0); + } + + elapsed = now - crq->queue_start; + if (elapsed > max_elapsed_dispatch) + max_elapsed_dispatch = elapsed; + + crq->accounted = 1; + crq->service_start = now; + + if (++cfqd->rq_in_driver >= CFQ_MAX_TAG && !cfqd->cfq_tagged) { + cfqq->cfqd->cfq_tagged = 1; + printk("cfq: depth %d reached, tagging now on\n", CFQ_MAX_TAG); + } +} + +static inline void +cfq_account_completion(struct cfq_queue *cfqq, struct cfq_rq *crq) +{ + struct cfq_data *cfqd = cfqq->cfqd; + + WARN_ON(!cfqd->rq_in_driver); + cfqd->rq_in_driver--; + + if (!cfqd->cfq_tagged) { + unsigned long now = jiffies; + unsigned long duration = now - crq->service_start; + + if (time_after(now, cfqq->service_start + cfq_service)) { + cfqq->service_start = now; + cfqq->service_used >>= 3; + } + + cfqq->service_used += duration; + cfq_sort_rr_list(cfqq, 0); + + if (duration > max_elapsed_crq) + max_elapsed_crq = duration; + } } static struct request *cfq_next_request(request_queue_t *q) @@ -407,100 +937,305 @@ static struct request *cfq_next_request(request_queue_t *q) struct cfq_data *cfqd = q->elevator->elevator_data; struct request *rq; - if (!list_empty(cfqd->dispatch)) { + if (!list_empty(&q->queue_head)) { struct cfq_rq *crq; dispatch: - rq = list_entry_rq(cfqd->dispatch->next); + rq = list_entry_rq(q->queue_head.next); - crq = RQ_DATA(rq); - if (crq) + if ((crq = RQ_DATA(rq)) != NULL) { cfq_remove_merge_hints(q, crq); + cfq_account_dispatch(crq); + } return rq; } - if (cfq_dispatch_requests(q, cfqd)) + if (cfq_dispatch_requests(q, cfqd->cfq_quantum)) goto dispatch; return NULL; } +/* + * task holds one reference to the queue, dropped when task exits. each crq + * in-flight on this queue also holds a reference, dropped when crq is freed. + * + * queue lock must be held here. + */ +static void cfq_put_queue(struct cfq_queue *cfqq) +{ + BUG_ON(!atomic_read(&cfqq->ref)); + + if (!atomic_dec_and_test(&cfqq->ref)) + return; + + BUG_ON(rb_first(&cfqq->sort_list)); + BUG_ON(cfqq->on_rr); + + cfq_put_cfqd(cfqq->cfqd); + + /* + * it's on the empty list and still hashed + */ + list_del(&cfqq->cfq_list); + hlist_del(&cfqq->cfq_hash); + kmem_cache_free(cfq_pool, cfqq); +} + static inline struct cfq_queue * -__cfq_find_cfq_hash(struct cfq_data *cfqd, int pid, const int hashval) +__cfq_find_cfq_hash(struct cfq_data *cfqd, unsigned long key, const int hashval) { - struct list_head *hash_list = &cfqd->cfq_hash[hashval]; - struct list_head *entry; + struct hlist_head *hash_list = &cfqd->cfq_hash[hashval]; + struct hlist_node *entry, *next; - list_for_each(entry, hash_list) { + hlist_for_each_safe(entry, next, hash_list) { struct cfq_queue *__cfqq = list_entry_qhash(entry); - if (__cfqq->pid == pid) + if (__cfqq->key == key) return __cfqq; } return NULL; } -static struct cfq_queue *cfq_find_cfq_hash(struct cfq_data *cfqd, int pid) +static struct cfq_queue * +cfq_find_cfq_hash(struct cfq_data *cfqd, unsigned long key) +{ + return __cfq_find_cfq_hash(cfqd, key, hash_long(key, CFQ_QHASH_SHIFT)); +} + +static inline void +cfq_rehash_cfqq(struct cfq_data *cfqd, struct cfq_queue **cfqq, + struct cfq_io_context *cic) +{ + unsigned long hashkey = cfq_hash_key(cfqd, current); + unsigned long hashval = hash_long(hashkey, CFQ_QHASH_SHIFT); + struct cfq_queue *__cfqq; + unsigned long flags; + + spin_lock_irqsave(cfqd->queue->queue_lock, flags); + + hlist_del(&(*cfqq)->cfq_hash); + + __cfqq = __cfq_find_cfq_hash(cfqd, hashkey, hashval); + if (!__cfqq || __cfqq == *cfqq) { + __cfqq = *cfqq; + hlist_add_head(&__cfqq->cfq_hash, &cfqd->cfq_hash[hashval]); + __cfqq->key_type = cfqd->key_type; + } else { + atomic_inc(&__cfqq->ref); + cic->cfqq = __cfqq; + cfq_put_queue(*cfqq); + *cfqq = __cfqq; + } + + cic->cfqq = __cfqq; + spin_unlock_irqrestore(cfqd->queue->queue_lock, flags); +} + +static void cfq_free_io_context(struct cfq_io_context *cic) { - const int hashval = hash_long(current->tgid, CFQ_QHASH_SHIFT); + kmem_cache_free(cfq_ioc_pool, cic); +} - return __cfq_find_cfq_hash(cfqd, pid, hashval); +/* + * locking hierarchy is: io_context lock -> queue locks + */ +static void cfq_exit_io_context(struct cfq_io_context *cic) +{ + struct cfq_queue *cfqq = cic->cfqq; + struct list_head *entry = &cic->list; + request_queue_t *q; + unsigned long flags; + + /* + * put the reference this task is holding to the various queues + */ + spin_lock_irqsave(&cic->ioc->lock, flags); + while ((entry = cic->list.next) != &cic->list) { + struct cfq_io_context *__cic; + + __cic = list_entry(entry, struct cfq_io_context, list); + list_del(entry); + + q = __cic->cfqq->cfqd->queue; + spin_lock(q->queue_lock); + cfq_put_queue(__cic->cfqq); + spin_unlock(q->queue_lock); + } + + q = cfqq->cfqd->queue; + spin_lock(q->queue_lock); + cfq_put_queue(cfqq); + spin_unlock(q->queue_lock); + + cic->cfqq = NULL; + spin_unlock_irqrestore(&cic->ioc->lock, flags); } -static void cfq_put_queue(struct cfq_data *cfqd, struct cfq_queue *cfqq) +static struct cfq_io_context *cfq_alloc_io_context(int gfp_flags) { - cfqd->busy_queues--; - list_del(&cfqq->cfq_list); - list_del(&cfqq->cfq_hash); - mempool_free(cfqq, cfq_mpool); + struct cfq_io_context *cic = kmem_cache_alloc(cfq_ioc_pool, gfp_flags); + + if (cic) { + cic->dtor = cfq_free_io_context; + cic->exit = cfq_exit_io_context; + INIT_LIST_HEAD(&cic->list); + cic->cfqq = NULL; + } + + return cic; } -static struct cfq_queue *__cfq_get_queue(struct cfq_data *cfqd, int pid, - int gfp_mask) +/* + * Setup general io context and cfq io context. There can be several cfq + * io contexts per general io context, if this process is doing io to more + * than one device managed by cfq. Note that caller is holding a reference to + * cfqq, so we don't need to worry about it disappearing + */ +static struct cfq_io_context * +cfq_get_io_context(struct cfq_queue **cfqq, int gfp_flags) { - const int hashval = hash_long(current->tgid, CFQ_QHASH_SHIFT); + struct cfq_data *cfqd = (*cfqq)->cfqd; + struct cfq_queue *__cfqq = *cfqq; + struct cfq_io_context *cic; + struct io_context *ioc; + + might_sleep_if(gfp_flags & __GFP_WAIT); + + ioc = get_io_context(gfp_flags); + if (!ioc) + return NULL; + + if ((cic = ioc->cic) == NULL) { + cic = cfq_alloc_io_context(gfp_flags); + + if (cic == NULL) + goto err; + + ioc->cic = cic; + cic->ioc = ioc; + cic->cfqq = __cfqq; + atomic_inc(&__cfqq->ref); + } else { + struct cfq_io_context *__cic; + unsigned long flags; + + /* + * since the first cic on the list is actually the head + * itself, need to check this here or we'll duplicate an + * cic per ioc for no reason + */ + if (cic->cfqq == __cfqq) + goto out; + + /* + * cic exists, check if we already are there. linear search + * should be ok here, the list will usually not be more than + * 1 or a few entries long + */ + spin_lock_irqsave(&ioc->lock, flags); + list_for_each_entry(__cic, &cic->list, list) { + /* + * this process is already holding a reference to + * this queue, so no need to get one more + */ + if (__cic->cfqq == __cfqq) { + cic = __cic; + spin_unlock_irqrestore(&ioc->lock, flags); + goto out; + } + } + spin_unlock_irqrestore(&ioc->lock, flags); + + /* + * nope, process doesn't have a cic assoicated with this + * cfqq yet. get a new one and add to list + */ + __cic = cfq_alloc_io_context(gfp_flags); + if (__cic == NULL) + goto err; + + __cic->ioc = ioc; + __cic->cfqq = __cfqq; + atomic_inc(&__cfqq->ref); + spin_lock_irqsave(&ioc->lock, flags); + list_add(&__cic->list, &cic->list); + spin_unlock_irqrestore(&ioc->lock, flags); + + cic = __cic; + *cfqq = __cfqq; + } + +out: + /* + * if key_type has been changed on the fly, we lazily rehash + * each queue at lookup time + */ + if ((*cfqq)->key_type != cfqd->key_type) + cfq_rehash_cfqq(cfqd, cfqq, cic); + + return cic; +err: + put_io_context(ioc); + return NULL; +} + +static struct cfq_queue * +__cfq_get_queue(struct cfq_data *cfqd, unsigned long key, int gfp_mask) +{ + const int hashval = hash_long(key, CFQ_QHASH_SHIFT); struct cfq_queue *cfqq, *new_cfqq = NULL; - request_queue_t *q = cfqd->queue; retry: - cfqq = __cfq_find_cfq_hash(cfqd, pid, hashval); + cfqq = __cfq_find_cfq_hash(cfqd, key, hashval); if (!cfqq) { if (new_cfqq) { cfqq = new_cfqq; new_cfqq = NULL; } else if (gfp_mask & __GFP_WAIT) { - spin_unlock_irq(q->queue_lock); - new_cfqq = mempool_alloc(cfq_mpool, gfp_mask); - spin_lock_irq(q->queue_lock); + spin_unlock_irq(cfqd->queue->queue_lock); + new_cfqq = kmem_cache_alloc(cfq_pool, gfp_mask); + spin_lock_irq(cfqd->queue->queue_lock); goto retry; } else - return NULL; + goto out; + + memset(cfqq, 0, sizeof(*cfqq)); - INIT_LIST_HEAD(&cfqq->cfq_hash); + INIT_HLIST_NODE(&cfqq->cfq_hash); INIT_LIST_HEAD(&cfqq->cfq_list); RB_CLEAR_ROOT(&cfqq->sort_list); - - cfqq->pid = pid; - cfqq->queued[0] = cfqq->queued[1] = 0; - list_add(&cfqq->cfq_hash, &cfqd->cfq_hash[hashval]); + INIT_LIST_HEAD(&cfqq->fifo[0]); + INIT_LIST_HEAD(&cfqq->fifo[1]); + + cfqq->key = key; + hlist_add_head(&cfqq->cfq_hash, &cfqd->cfq_hash[hashval]); + atomic_set(&cfqq->ref, 0); + cfqq->cfqd = cfqd; + atomic_inc(&cfqd->ref); + cfqq->key_type = cfqd->key_type; + cfqq->service_start = ~0UL; } if (new_cfqq) - mempool_free(new_cfqq, cfq_mpool); + kmem_cache_free(cfq_pool, new_cfqq); + atomic_inc(&cfqq->ref); +out: + WARN_ON((gfp_mask & __GFP_WAIT) && !cfqq); return cfqq; } -static struct cfq_queue *cfq_get_queue(struct cfq_data *cfqd, int pid, - int gfp_mask) +static struct cfq_queue * +cfq_get_queue(struct cfq_data *cfqd, unsigned long key, int gfp_mask) { request_queue_t *q = cfqd->queue; struct cfq_queue *cfqq; spin_lock_irq(q->queue_lock); - cfqq = __cfq_get_queue(cfqd, pid, gfp_mask); + cfqq = __cfq_get_queue(cfqd, key, gfp_mask); spin_unlock_irq(q->queue_lock); return cfqq; @@ -508,24 +1243,14 @@ static struct cfq_queue *cfq_get_queue(struct cfq_data *cfqd, int pid, static void cfq_enqueue(struct cfq_data *cfqd, struct cfq_rq *crq) { - struct cfq_queue *cfqq; + crq->is_sync = 0; + if (rq_data_dir(crq->request) == READ || current->flags & PF_SYNCWRITE) + crq->is_sync = 1; - cfqq = __cfq_get_queue(cfqd, current->tgid, GFP_ATOMIC); - if (cfqq) { - cfq_add_crq_rb(cfqd, cfqq, crq); + cfq_add_crq_rb(crq); + crq->queue_start = jiffies; - if (list_empty(&cfqq->cfq_list)) { - list_add(&cfqq->cfq_list, &cfqd->rr_list); - cfqd->busy_queues++; - } - } else { - /* - * should can only happen if the request wasn't allocated - * through blk_alloc_request(), eg stack requests from ide-cd - * (those should be removed) _and_ we are in OOM. - */ - list_add_tail(&crq->request->queuelist, cfqd->dispatch); - } + list_add_tail(&crq->request->queuelist, &crq->cfq_queue->fifo[crq->is_sync]); } static void @@ -536,12 +1261,12 @@ cfq_insert_request(request_queue_t *q, struct request *rq, int where) switch (where) { case ELEVATOR_INSERT_BACK: - while (cfq_dispatch_requests(q, cfqd)) + while (cfq_dispatch_requests(q, cfqd->cfq_quantum)) ; - list_add_tail(&rq->queuelist, cfqd->dispatch); + list_add_tail(&rq->queuelist, &q->queue_head); break; case ELEVATOR_INSERT_FRONT: - list_add(&rq->queuelist, cfqd->dispatch); + list_add(&rq->queuelist, &q->queue_head); break; case ELEVATOR_INSERT_SORT: BUG_ON(!blk_fs_request(rq)); @@ -564,10 +1289,25 @@ static int cfq_queue_empty(request_queue_t *q) { struct cfq_data *cfqd = q->elevator->elevator_data; - if (list_empty(cfqd->dispatch) && list_empty(&cfqd->rr_list)) - return 1; + return list_empty(&q->queue_head) && list_empty(&cfqd->rr_list); +} + +static void cfq_completed_request(request_queue_t *q, struct request *rq) +{ + struct cfq_rq *crq = RQ_DATA(rq); + + if (unlikely(!blk_fs_request(rq))) + return; + + if (crq->in_flight) { + struct cfq_queue *cfqq = crq->cfq_queue; + + WARN_ON(!cfqq->in_flight); + cfqq->in_flight--; + + cfq_account_completion(cfqq, crq); + } - return 0; } static struct request * @@ -598,90 +1338,167 @@ static int cfq_may_queue(request_queue_t *q, int rw) { struct cfq_data *cfqd = q->elevator->elevator_data; struct cfq_queue *cfqq; - int ret = 1; + int ret = ELV_MQUEUE_MAY; - if (!cfqd->busy_queues) - goto out; + if (current->flags & PF_MEMALLOC) + return ELV_MQUEUE_MAY; - cfqq = cfq_find_cfq_hash(cfqd, current->tgid); + cfqq = cfq_find_cfq_hash(cfqd, cfq_hash_key(cfqd, current)); if (cfqq) { - int limit = (q->nr_requests - cfqd->cfq_queued) / cfqd->busy_queues; + int limit = cfqd->max_queued; - if (limit < 3) - limit = 3; + if (cfqq->allocated[rw] < cfqd->cfq_queued) + return ELV_MQUEUE_MUST; + + if (cfqd->busy_queues) + limit = q->nr_requests / cfqd->busy_queues; + + if (limit < cfqd->cfq_queued) + limit = cfqd->cfq_queued; else if (limit > cfqd->max_queued) limit = cfqd->max_queued; - if (cfqq->queued[rw] > limit) - ret = 0; + if (cfqq->allocated[rw] >= limit) { + if (limit > cfqq->alloc_limit[rw]) + cfqq->alloc_limit[rw] = limit; + + ret = ELV_MQUEUE_NO; + } } -out: + return ret; } +static void cfq_check_waiters(request_queue_t *q, struct cfq_queue *cfqq) +{ + struct request_list *rl = &q->rq; + const int write = waitqueue_active(&rl->wait[WRITE]); + const int read = waitqueue_active(&rl->wait[READ]); + + if (read && cfqq->allocated[READ] < cfqq->alloc_limit[READ]) + wake_up(&rl->wait[READ]); + if (write && cfqq->allocated[WRITE] < cfqq->alloc_limit[WRITE]) + wake_up(&rl->wait[WRITE]); +} + +/* + * queue lock held here + */ static void cfq_put_request(request_queue_t *q, struct request *rq) { struct cfq_data *cfqd = q->elevator->elevator_data; struct cfq_rq *crq = RQ_DATA(rq); - struct request_list *rl; - int other_rw; if (crq) { + struct cfq_queue *cfqq = crq->cfq_queue; + BUG_ON(q->last_merge == rq); - BUG_ON(ON_MHASH(crq)); + BUG_ON(!hlist_unhashed(&crq->hash)); + + if (crq->io_context) + put_io_context(crq->io_context->ioc); + + if (!cfqq->allocated[crq->is_write]) { + WARN_ON(1); + cfqq->allocated[crq->is_write] = 1; + } + cfqq->allocated[crq->is_write]--; mempool_free(crq, cfqd->crq_pool); rq->elevator_private = NULL; - } - /* - * work-around for may_queue "bug": if a read gets issued and refused - * to queue because writes ate all the allowed slots and no other - * reads are pending for this queue, it could get stuck infinitely - * since freed_request() only checks the waitqueue for writes when - * freeing them. or vice versa for a single write vs many reads. - * so check here whether "the other" data direction might be able - * to queue and wake them - */ - rl = &q->rq; - other_rw = rq_data_dir(rq) ^ 1; - if (rl->count[other_rw] <= q->nr_requests) { smp_mb(); - if (waitqueue_active(&rl->wait[other_rw])) - wake_up(&rl->wait[other_rw]); + cfq_check_waiters(q, cfqq); + cfq_put_queue(cfqq); } } +/* + * Allocate cfq data structures associated with this request. A queue and + */ static int cfq_set_request(request_queue_t *q, struct request *rq, int gfp_mask) { struct cfq_data *cfqd = q->elevator->elevator_data; + struct cfq_io_context *cic; + const int rw = rq_data_dir(rq); struct cfq_queue *cfqq; struct cfq_rq *crq; + unsigned long flags; + + might_sleep_if(gfp_mask & __GFP_WAIT); + + spin_lock_irqsave(q->queue_lock, flags); + + cfqq = __cfq_get_queue(cfqd, cfq_hash_key(cfqd, current), gfp_mask); + if (!cfqq) { +#if 0 + cfqq = cfq_get_queue(cfqd, CFQ_KEY_SPARE, gfp_mask); + printk("%s: got spare queue\n", current->comm); +#else + goto out_lock; +#endif + } + + if (cfqq->allocated[rw] >= cfqd->max_queued) + goto out_lock; + + spin_unlock_irqrestore(q->queue_lock, flags); /* - * prepare a queue up front, so cfq_enqueue() doesn't have to + * if hashing type has changed, the cfq_queue might change here. we + * don't bother rechecking ->allocated since it should be a rare + * event */ - cfqq = cfq_get_queue(cfqd, current->tgid, gfp_mask); - if (!cfqq) - return 1; + cic = cfq_get_io_context(&cfqq, gfp_mask); + if (!cic) + goto err; crq = mempool_alloc(cfqd->crq_pool, gfp_mask); if (crq) { - memset(crq, 0, sizeof(*crq)); RB_CLEAR(&crq->rb_node); + crq->rb_key = 0; crq->request = rq; - crq->cfq_queue = NULL; - INIT_LIST_HEAD(&crq->hash); + INIT_HLIST_NODE(&crq->hash); + crq->cfq_queue = cfqq; + crq->io_context = cic; + crq->service_start = crq->queue_start = 0; + crq->in_flight = crq->accounted = crq->is_sync = 0; + crq->is_write = rw; rq->elevator_private = crq; + cfqq->allocated[rw]++; + cfqq->alloc_limit[rw] = 0; return 0; } + put_io_context(cic->ioc); +err: + spin_lock_irqsave(q->queue_lock, flags); + cfq_put_queue(cfqq); +out_lock: + spin_unlock_irqrestore(q->queue_lock, flags); return 1; } -static void cfq_exit_queue(elevator_t *e) +static void cfq_put_cfqd(struct cfq_data *cfqd) { - struct cfq_data *cfqd = e->elevator_data; + request_queue_t *q = cfqd->queue; + elevator_t *e = q->elevator; + struct cfq_queue *cfqq; + + if (!atomic_dec_and_test(&cfqd->ref)) + return; + + /* + * kill spare queue, getting it means we have two refences to it. + * drop both + */ + spin_lock_irq(q->queue_lock); + cfqq = __cfq_get_queue(cfqd, CFQ_KEY_SPARE, GFP_ATOMIC); + cfq_put_queue(cfqq); + cfq_put_queue(cfqq); + spin_unlock_irq(q->queue_lock); + + blk_put_queue(q); e->elevator_data = NULL; mempool_destroy(cfqd->crq_pool); @@ -690,9 +1507,15 @@ static void cfq_exit_queue(elevator_t *e) kfree(cfqd); } +static void cfq_exit_queue(elevator_t *e) +{ + cfq_put_cfqd(e->elevator_data); +} + static int cfq_init_queue(request_queue_t *q, elevator_t *e) { struct cfq_data *cfqd; + struct cfq_queue *cfqq; int i; cfqd = kmalloc(sizeof(*cfqd), GFP_KERNEL); @@ -701,12 +1524,13 @@ static int cfq_init_queue(request_queue_t *q, elevator_t *e) memset(cfqd, 0, sizeof(*cfqd)); INIT_LIST_HEAD(&cfqd->rr_list); + INIT_LIST_HEAD(&cfqd->empty_list); - cfqd->crq_hash = kmalloc(sizeof(struct list_head) * CFQ_MHASH_ENTRIES, GFP_KERNEL); + cfqd->crq_hash = kmalloc(sizeof(struct hlist_head) * CFQ_MHASH_ENTRIES, GFP_KERNEL); if (!cfqd->crq_hash) goto out_crqhash; - cfqd->cfq_hash = kmalloc(sizeof(struct list_head) * CFQ_QHASH_ENTRIES, GFP_KERNEL); + cfqd->cfq_hash = kmalloc(sizeof(struct hlist_head) * CFQ_QHASH_ENTRIES, GFP_KERNEL); if (!cfqd->cfq_hash) goto out_cfqhash; @@ -715,24 +1539,44 @@ static int cfq_init_queue(request_queue_t *q, elevator_t *e) goto out_crqpool; for (i = 0; i < CFQ_MHASH_ENTRIES; i++) - INIT_LIST_HEAD(&cfqd->crq_hash[i]); + INIT_HLIST_HEAD(&cfqd->crq_hash[i]); for (i = 0; i < CFQ_QHASH_ENTRIES; i++) - INIT_LIST_HEAD(&cfqd->cfq_hash[i]); + INIT_HLIST_HEAD(&cfqd->cfq_hash[i]); - cfqd->dispatch = &q->queue_head; e->elevator_data = cfqd; + cfqd->queue = q; + atomic_inc(&q->refcnt); + + /* + * setup spare failure queue + */ + cfqq = cfq_get_queue(cfqd, CFQ_KEY_SPARE, GFP_KERNEL); + if (!cfqq) + goto out_spare; /* * just set it to some high value, we want anyone to be able to queue * some requests. fairness is handled differently */ - cfqd->max_queued = q->nr_requests; - q->nr_requests = 8192; + q->nr_requests = 1024; + cfqd->max_queued = q->nr_requests / 16; + q->nr_batching = cfq_queued; + cfqd->key_type = CFQ_KEY_TGID; + cfqd->find_best_crq = 1; + atomic_set(&cfqd->ref, 1); cfqd->cfq_queued = cfq_queued; cfqd->cfq_quantum = cfq_quantum; + cfqd->cfq_fifo_expire_r = cfq_fifo_expire_r; + cfqd->cfq_fifo_expire_w = cfq_fifo_expire_w; + cfqd->cfq_fifo_batch_expire = cfq_fifo_rate; + cfqd->cfq_back_max = cfq_back_max; + cfqd->cfq_back_penalty = cfq_back_penalty; + return 0; +out_spare: + mempool_destroy(cfqd->crq_pool); out_crqpool: kfree(cfqd->cfq_hash); out_cfqhash: @@ -746,13 +1590,13 @@ static void cfq_slab_kill(void) { if (crq_pool) kmem_cache_destroy(crq_pool); - if (cfq_mpool) - mempool_destroy(cfq_mpool); if (cfq_pool) kmem_cache_destroy(cfq_pool); + if (cfq_ioc_pool) + kmem_cache_destroy(cfq_ioc_pool); } -static int cfq_slab_setup(void) +static int __init cfq_slab_setup(void) { crq_pool = kmem_cache_create("crq_pool", sizeof(struct cfq_rq), 0, 0, NULL, NULL); @@ -764,8 +1608,9 @@ static int cfq_slab_setup(void) if (!cfq_pool) goto fail; - cfq_mpool = mempool_create(64, mempool_alloc_slab, mempool_free_slab, cfq_pool); - if (!cfq_mpool) + cfq_ioc_pool = kmem_cache_create("cfq_ioc_pool", + sizeof(struct cfq_io_context), 0, 0, NULL, NULL); + if (!cfq_ioc_pool) goto fail; return 0; @@ -774,6 +1619,7 @@ fail: return -ENOMEM; } + /* * sysfs parts below --> */ @@ -798,6 +1644,94 @@ cfq_var_store(unsigned int *var, const char *page, size_t count) return count; } +static ssize_t +cfq_clear_elapsed(struct cfq_data *cfqd, const char *page, size_t count) +{ + max_elapsed_dispatch = max_elapsed_crq = 0; + return count; +} + +static ssize_t +cfq_set_key_type(struct cfq_data *cfqd, const char *page, size_t count) +{ + spin_lock_irq(cfqd->queue->queue_lock); + if (!strncmp(page, "pgid", 4)) + cfqd->key_type = CFQ_KEY_PGID; + else if (!strncmp(page, "tgid", 4)) + cfqd->key_type = CFQ_KEY_TGID; + else if (!strncmp(page, "uid", 3)) + cfqd->key_type = CFQ_KEY_UID; + else if (!strncmp(page, "gid", 3)) + cfqd->key_type = CFQ_KEY_GID; + spin_unlock_irq(cfqd->queue->queue_lock); + return count; +} + +static ssize_t +cfq_read_key_type(struct cfq_data *cfqd, char *page) +{ + ssize_t len = 0; + int i; + + for (i = CFQ_KEY_PGID; i < CFQ_KEY_LAST; i++) { + if (cfqd->key_type == i) + len += sprintf(page+len, "[%s] ", cfq_key_types[i]); + else + len += sprintf(page+len, "%s ", cfq_key_types[i]); + } + len += sprintf(page+len, "\n"); + return len; +} + +static ssize_t +cfq_status_show(struct cfq_data *cfqd, char *page) +{ + struct list_head *entry; + struct cfq_queue *cfqq; + ssize_t len; + int i = 0, queues; + + len = sprintf(page, "Busy queues: %u\n", cfqd->busy_queues); + len += sprintf(page+len, "key type: %s\n", + cfq_key_types[cfqd->key_type]); + len += sprintf(page+len, "last sector: %Lu\n", + (unsigned long long)cfqd->last_sector); + len += sprintf(page+len, "max time in iosched: %lu\n", + max_elapsed_dispatch); + len += sprintf(page+len, "max completion time: %lu\n", max_elapsed_crq); + + len += sprintf(page+len, "Busy queue list:\n"); + spin_lock_irq(cfqd->queue->queue_lock); + list_for_each(entry, &cfqd->rr_list) { + i++; + cfqq = list_entry_cfqq(entry); + len += sprintf(page+len, " cfqq: key=%lu alloc=%d/%d, " + "queued=%d/%d, last_fifo=%lu, service_used=%lu\n", + cfqq->key, cfqq->allocated[0], cfqq->allocated[1], + cfqq->queued[0], cfqq->queued[1], + cfqq->last_fifo_expire, cfqq->service_used); + } + len += sprintf(page+len, " busy queues total: %d\n", i); + queues = i; + + len += sprintf(page+len, "Empty queue list:\n"); + i = 0; + list_for_each(entry, &cfqd->empty_list) { + i++; + cfqq = list_entry_cfqq(entry); + len += sprintf(page+len, " cfqq: key=%lu alloc=%d/%d, " + "queued=%d/%d, last_fifo=%lu, service_used=%lu\n", + cfqq->key, cfqq->allocated[0], cfqq->allocated[1], + cfqq->queued[0], cfqq->queued[1], + cfqq->last_fifo_expire, cfqq->service_used); + } + len += sprintf(page+len, " empty queues total: %d\n", i); + queues += i; + len += sprintf(page+len, "Total queues: %d\n", queues); + spin_unlock_irq(cfqd->queue->queue_lock); + return len; +} + #define SHOW_FUNCTION(__FUNC, __VAR) \ static ssize_t __FUNC(struct cfq_data *cfqd, char *page) \ { \ @@ -805,6 +1739,12 @@ static ssize_t __FUNC(struct cfq_data *cfqd, char *page) \ } SHOW_FUNCTION(cfq_quantum_show, cfqd->cfq_quantum); SHOW_FUNCTION(cfq_queued_show, cfqd->cfq_queued); +SHOW_FUNCTION(cfq_fifo_expire_r_show, cfqd->cfq_fifo_expire_r); +SHOW_FUNCTION(cfq_fifo_expire_w_show, cfqd->cfq_fifo_expire_w); +SHOW_FUNCTION(cfq_fifo_batch_expire_show, cfqd->cfq_fifo_batch_expire); +SHOW_FUNCTION(cfq_find_best_show, cfqd->find_best_crq); +SHOW_FUNCTION(cfq_back_max_show, cfqd->cfq_back_max); +SHOW_FUNCTION(cfq_back_penalty_show, cfqd->cfq_back_penalty); #undef SHOW_FUNCTION #define STORE_FUNCTION(__FUNC, __PTR, MIN, MAX) \ @@ -817,8 +1757,14 @@ static ssize_t __FUNC(struct cfq_data *cfqd, const char *page, size_t count) \ *(__PTR) = (MAX); \ return ret; \ } -STORE_FUNCTION(cfq_quantum_store, &cfqd->cfq_quantum, 1, INT_MAX); -STORE_FUNCTION(cfq_queued_store, &cfqd->cfq_queued, 1, INT_MAX); +STORE_FUNCTION(cfq_quantum_store, &cfqd->cfq_quantum, 1, UINT_MAX); +STORE_FUNCTION(cfq_queued_store, &cfqd->cfq_queued, 1, UINT_MAX); +STORE_FUNCTION(cfq_fifo_expire_r_store, &cfqd->cfq_fifo_expire_r, 1, UINT_MAX); +STORE_FUNCTION(cfq_fifo_expire_w_store, &cfqd->cfq_fifo_expire_w, 1, UINT_MAX); +STORE_FUNCTION(cfq_fifo_batch_expire_store, &cfqd->cfq_fifo_batch_expire, 0, UINT_MAX); +STORE_FUNCTION(cfq_find_best_store, &cfqd->find_best_crq, 0, 1); +STORE_FUNCTION(cfq_back_max_store, &cfqd->cfq_back_max, 0, UINT_MAX); +STORE_FUNCTION(cfq_back_penalty_store, &cfqd->cfq_back_penalty, 1, UINT_MAX); #undef STORE_FUNCTION static struct cfq_fs_entry cfq_quantum_entry = { @@ -831,10 +1777,62 @@ static struct cfq_fs_entry cfq_queued_entry = { .show = cfq_queued_show, .store = cfq_queued_store, }; +static struct cfq_fs_entry cfq_fifo_expire_r_entry = { + .attr = {.name = "fifo_expire_sync", .mode = S_IRUGO | S_IWUSR }, + .show = cfq_fifo_expire_r_show, + .store = cfq_fifo_expire_r_store, +}; +static struct cfq_fs_entry cfq_fifo_expire_w_entry = { + .attr = {.name = "fifo_expire_async", .mode = S_IRUGO | S_IWUSR }, + .show = cfq_fifo_expire_w_show, + .store = cfq_fifo_expire_w_store, +}; +static struct cfq_fs_entry cfq_fifo_batch_expire_entry = { + .attr = {.name = "fifo_batch_expire", .mode = S_IRUGO | S_IWUSR }, + .show = cfq_fifo_batch_expire_show, + .store = cfq_fifo_batch_expire_store, +}; +static struct cfq_fs_entry cfq_find_best_entry = { + .attr = {.name = "find_best_crq", .mode = S_IRUGO | S_IWUSR }, + .show = cfq_find_best_show, + .store = cfq_find_best_store, +}; +static struct cfq_fs_entry cfq_back_max_entry = { + .attr = {.name = "back_seek_max", .mode = S_IRUGO | S_IWUSR }, + .show = cfq_back_max_show, + .store = cfq_back_max_store, +}; +static struct cfq_fs_entry cfq_back_penalty_entry = { + .attr = {.name = "back_seek_penalty", .mode = S_IRUGO | S_IWUSR }, + .show = cfq_back_penalty_show, + .store = cfq_back_penalty_store, +}; +static struct cfq_fs_entry cfq_clear_elapsed_entry = { + .attr = {.name = "clear_elapsed", .mode = S_IWUSR }, + .store = cfq_clear_elapsed, +}; +static struct cfq_fs_entry cfq_misc_entry = { + .attr = {.name = "show_status", .mode = S_IRUGO }, + .show = cfq_status_show, +}; +static struct cfq_fs_entry cfq_key_type_entry = { + .attr = {.name = "key_type", .mode = S_IRUGO | S_IWUSR }, + .show = cfq_read_key_type, + .store = cfq_set_key_type, +}; static struct attribute *default_attrs[] = { &cfq_quantum_entry.attr, &cfq_queued_entry.attr, + &cfq_fifo_expire_r_entry.attr, + &cfq_fifo_expire_w_entry.attr, + &cfq_fifo_batch_expire_entry.attr, + &cfq_key_type_entry.attr, + &cfq_find_best_entry.attr, + &cfq_back_max_entry.attr, + &cfq_back_penalty_entry.attr, + &cfq_clear_elapsed_entry.attr, + &cfq_misc_entry.attr, NULL, }; @@ -883,7 +1881,9 @@ static struct elevator_type iosched_cfq = { .elevator_next_req_fn = cfq_next_request, .elevator_add_req_fn = cfq_insert_request, .elevator_remove_req_fn = cfq_remove_request, + .elevator_requeue_req_fn = cfq_requeue_request, .elevator_queue_empty_fn = cfq_queue_empty, + .elevator_completed_req_fn = cfq_completed_request, .elevator_former_req_fn = cfq_former_request, .elevator_latter_req_fn = cfq_latter_request, .elevator_set_req_fn = cfq_set_request, @@ -892,9 +1892,9 @@ static struct elevator_type iosched_cfq = { .elevator_init_fn = cfq_init_queue, .elevator_exit_fn = cfq_exit_queue, }, - .elevator_ktype = &cfq_ktype, - .elevator_name = "cfq", - .elevator_owner = THIS_MODULE, + .elevator_ktype = &cfq_ktype, + .elevator_name = "cfq", + .elevator_owner = THIS_MODULE, }; int cfq_init(void) @@ -905,9 +1905,12 @@ int cfq_init(void) return -ENOMEM; ret = elv_register(&iosched_cfq); - if (ret) - cfq_slab_kill(); + if (!ret) { + __module_get(THIS_MODULE); + return 0; + } + cfq_slab_kill(); return ret; } diff --git a/drivers/block/elevator.c b/drivers/block/elevator.c index 92cc7a9a5c63..1b4f6a70c0ca 100644 --- a/drivers/block/elevator.c +++ b/drivers/block/elevator.c @@ -437,7 +437,7 @@ int elv_may_queue(request_queue_t *q, int rw) if (e->ops->elevator_may_queue_fn) return e->ops->elevator_may_queue_fn(q, rw); - return 0; + return ELV_MQUEUE_MAY; } void elv_completed_request(request_queue_t *q, struct request *rq) diff --git a/drivers/block/ll_rw_blk.c b/drivers/block/ll_rw_blk.c index b3780ca0fdc0..3ba6430899df 100644 --- a/drivers/block/ll_rw_blk.c +++ b/drivers/block/ll_rw_blk.c @@ -243,6 +243,7 @@ void blk_queue_make_request(request_queue_t * q, make_request_fn * mfn) blk_queue_hardsect_size(q, 512); blk_queue_dma_alignment(q, 511); blk_queue_congestion_threshold(q); + q->nr_batching = BLK_BATCH_REQ; q->unplug_thresh = 4; /* hmm */ q->unplug_delay = (3 * HZ) / 1000; /* 3 milliseconds */ @@ -1511,8 +1512,10 @@ request_queue_t *blk_init_queue(request_fn_proc *rfn, spinlock_t *lock) /* * all done */ - if (!elevator_init(q, NULL)) + if (!elevator_init(q, NULL)) { + blk_queue_congestion_threshold(q); return q; + } blk_cleanup_queue(q); out_init: @@ -1540,13 +1543,20 @@ static inline void blk_free_request(request_queue_t *q, struct request *rq) mempool_free(rq, q->rq.rq_pool); } -static inline struct request *blk_alloc_request(request_queue_t *q,int gfp_mask) +static inline struct request *blk_alloc_request(request_queue_t *q, int rw, + int gfp_mask) { struct request *rq = mempool_alloc(q->rq.rq_pool, gfp_mask); if (!rq) return NULL; + /* + * first three bits are identical in rq->flags and bio->bi_rw, + * see bio.h and blkdev.h + */ + rq->flags = rw; + if (!elv_set_request(q, rq, gfp_mask)) return rq; @@ -1558,7 +1568,7 @@ static inline struct request *blk_alloc_request(request_queue_t *q,int gfp_mask) * ioc_batching returns true if the ioc is a valid batching request and * should be given priority access to a request. */ -static inline int ioc_batching(struct io_context *ioc) +static inline int ioc_batching(request_queue_t *q, struct io_context *ioc) { if (!ioc) return 0; @@ -1568,7 +1578,7 @@ static inline int ioc_batching(struct io_context *ioc) * even if the batch times out, otherwise we could theoretically * lose wakeups. */ - return ioc->nr_batch_requests == BLK_BATCH_REQ || + return ioc->nr_batch_requests == q->nr_batching || (ioc->nr_batch_requests > 0 && time_before(jiffies, ioc->last_waited + BLK_BATCH_TIME)); } @@ -1579,12 +1589,12 @@ static inline int ioc_batching(struct io_context *ioc) * is the behaviour we want though - once it gets a wakeup it should be given * a nice run. */ -void ioc_set_batching(struct io_context *ioc) +void ioc_set_batching(request_queue_t *q, struct io_context *ioc) { - if (!ioc || ioc_batching(ioc)) + if (!ioc || ioc_batching(q, ioc)) return; - ioc->nr_batch_requests = BLK_BATCH_REQ; + ioc->nr_batch_requests = q->nr_batching; ioc->last_waited = jiffies; } @@ -1600,10 +1610,10 @@ static void freed_request(request_queue_t *q, int rw) if (rl->count[rw] < queue_congestion_off_threshold(q)) clear_queue_congested(q, rw); if (rl->count[rw]+1 <= q->nr_requests) { + smp_mb(); if (waitqueue_active(&rl->wait[rw])) wake_up(&rl->wait[rw]); - if (!waitqueue_active(&rl->wait[rw])) - blk_clear_queue_full(q, rw); + blk_clear_queue_full(q, rw); } if (unlikely(waitqueue_active(&rl->drain)) && !rl->count[READ] && !rl->count[WRITE]) @@ -1632,13 +1642,22 @@ static struct request *get_request(request_queue_t *q, int rw, int gfp_mask) * will be blocked. */ if (!blk_queue_full(q, rw)) { - ioc_set_batching(ioc); + ioc_set_batching(q, ioc); blk_set_queue_full(q, rw); } } - if (blk_queue_full(q, rw) - && !ioc_batching(ioc) && !elv_may_queue(q, rw)) { + switch (elv_may_queue(q, rw)) { + case ELV_MQUEUE_NO: + spin_unlock_irq(q->queue_lock); + goto out; + case ELV_MQUEUE_MAY: + break; + case ELV_MQUEUE_MUST: + goto get_rq; + } + + if (blk_queue_full(q, rw) && !ioc_batching(q, ioc)) { /* * The queue is full and the allocating process is not a * "batcher", and not exempted by the IO scheduler @@ -1647,12 +1666,13 @@ static struct request *get_request(request_queue_t *q, int rw, int gfp_mask) goto out; } +get_rq: rl->count[rw]++; if (rl->count[rw] >= queue_congestion_on_threshold(q)) set_queue_congested(q, rw); spin_unlock_irq(q->queue_lock); - rq = blk_alloc_request(q, gfp_mask); + rq = blk_alloc_request(q, rw, gfp_mask); if (!rq) { /* * Allocation failed presumably due to memory. Undo anything @@ -1667,17 +1687,11 @@ static struct request *get_request(request_queue_t *q, int rw, int gfp_mask) goto out; } - if (ioc_batching(ioc)) + if (ioc_batching(q, ioc)) ioc->nr_batch_requests--; INIT_LIST_HEAD(&rq->queuelist); - /* - * first three bits are identical in rq->flags and bio->bi_rw, - * see bio.h and blkdev.h - */ - rq->flags = rw; - rq->errors = 0; rq->rq_status = RQ_ACTIVE; rq->bio = rq->biotail = NULL; @@ -1726,7 +1740,7 @@ static struct request *get_request_wait(request_queue_t *q, int rw) * See ioc_batching, ioc_set_batching */ ioc = get_io_context(GFP_NOIO); - ioc_set_batching(ioc); + ioc_set_batching(q, ioc); put_io_context(ioc); } finish_wait(&rl->wait[rw], &wait); @@ -3082,6 +3096,9 @@ void put_io_context(struct io_context *ioc) if (atomic_dec_and_test(&ioc->refcount)) { if (ioc->aic && ioc->aic->dtor) ioc->aic->dtor(ioc->aic); + if (ioc->cic && ioc->cic->dtor) + ioc->cic->dtor(ioc->cic); + kmem_cache_free(iocontext_cachep, ioc); } } @@ -3095,14 +3112,15 @@ void exit_io_context(void) local_irq_save(flags); ioc = current->io_context; - if (ioc) { - if (ioc->aic && ioc->aic->exit) - ioc->aic->exit(ioc->aic); - put_io_context(ioc); - current->io_context = NULL; - } else - WARN_ON(1); + current->io_context = NULL; local_irq_restore(flags); + + if (ioc->aic && ioc->aic->exit) + ioc->aic->exit(ioc->aic); + if (ioc->cic && ioc->cic->exit) + ioc->cic->exit(ioc->cic); + + put_io_context(ioc); } /* @@ -3121,20 +3139,39 @@ struct io_context *get_io_context(int gfp_flags) local_irq_save(flags); ret = tsk->io_context; - if (ret == NULL) { - ret = kmem_cache_alloc(iocontext_cachep, GFP_ATOMIC); - if (ret) { - atomic_set(&ret->refcount, 1); - ret->pid = tsk->pid; - ret->last_waited = jiffies; /* doesn't matter... */ - ret->nr_batch_requests = 0; /* because this is 0 */ - ret->aic = NULL; + if (ret) + goto out; + + local_irq_restore(flags); + + ret = kmem_cache_alloc(iocontext_cachep, gfp_flags); + if (ret) { + atomic_set(&ret->refcount, 1); + ret->pid = tsk->pid; + ret->last_waited = jiffies; /* doesn't matter... */ + ret->nr_batch_requests = 0; /* because this is 0 */ + ret->aic = NULL; + ret->cic = NULL; + spin_lock_init(&ret->lock); + + local_irq_save(flags); + + /* + * very unlikely, someone raced with us in setting up the task + * io context. free new context and just grab a reference. + */ + if (!tsk->io_context) tsk->io_context = ret; + else { + kmem_cache_free(iocontext_cachep, ret); + ret = tsk->io_context; } - } - if (ret) + +out: atomic_inc(&ret->refcount); - local_irq_restore(flags); + local_irq_restore(flags); + } + return ret; } EXPORT_SYMBOL(get_io_context); diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 5e4a6ab84ecb..b2059869cb92 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -52,6 +52,20 @@ struct as_io_context { sector_t seek_mean; }; +struct cfq_queue; +struct cfq_io_context { + void (*dtor)(struct cfq_io_context *); + void (*exit)(struct cfq_io_context *); + + struct io_context *ioc; + + /* + * circular list of cfq_io_contexts belonging to a process io context + */ + struct list_head list; + struct cfq_queue *cfqq; +}; + /* * This is the per-process I/O subsystem state. It is refcounted and * kmalloc'ed. Currently all fields are modified in process io context @@ -67,7 +81,10 @@ struct io_context { unsigned long last_waited; /* Time last woken after wait for request */ int nr_batch_requests; /* Number of requests left in the batch */ + spinlock_t lock; + struct as_io_context *aic; + struct cfq_io_context *cic; }; void put_io_context(struct io_context *ioc); @@ -343,6 +360,7 @@ struct request_queue unsigned long nr_requests; /* Max # of requests */ unsigned int nr_congestion_on; unsigned int nr_congestion_off; + unsigned int nr_batching; unsigned short max_sectors; unsigned short max_hw_sectors; diff --git a/include/linux/elevator.h b/include/linux/elevator.h index 95cdfb5bb790..8cf0e3f290bf 100644 --- a/include/linux/elevator.h +++ b/include/linux/elevator.h @@ -130,4 +130,13 @@ extern int elv_try_last_merge(request_queue_t *, struct bio *); #define ELEVATOR_INSERT_BACK 2 #define ELEVATOR_INSERT_SORT 3 +/* + * return values from elevator_may_queue_fn + */ +enum { + ELV_MQUEUE_MAY, + ELV_MQUEUE_NO, + ELV_MQUEUE_MUST, +}; + #endif -- cgit v1.2.3 From d55249d351bc96f49e30bc3e5dfa1dad5034cc28 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Mon, 18 Oct 2004 18:01:53 -0700 Subject: [PATCH] convert jiffies <-> msecs for io schedulers The various io schedulers don't convert to and from jiffies and ms in their sysfs exported values. This patch adds that. Signed-off-by: Jens Axboe Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/block/as-iosched.c | 7 ++--- drivers/block/cfq-iosched.c | 58 +++++++++++++++++++++++----------------- drivers/block/deadline-iosched.c | 46 ++++++++++++++++++------------- 3 files changed, 64 insertions(+), 47 deletions(-) (limited to 'drivers') diff --git a/drivers/block/as-iosched.c b/drivers/block/as-iosched.c index b049848c19c3..0aa3ee8c309b 100644 --- a/drivers/block/as-iosched.c +++ b/drivers/block/as-iosched.c @@ -1962,10 +1962,10 @@ static ssize_t as_est_show(struct as_data *ad, char *page) return pos; } -#define SHOW_FUNCTION(__FUNC, __VAR) \ +#define SHOW_FUNCTION(__FUNC, __VAR) \ static ssize_t __FUNC(struct as_data *ad, char *page) \ -{ \ - return as_var_show(__VAR, (page)); \ +{ \ + return as_var_show(jiffies_to_msecs((__VAR)), (page)); \ } SHOW_FUNCTION(as_readexpire_show, ad->fifo_expire[REQ_SYNC]); SHOW_FUNCTION(as_writeexpire_show, ad->fifo_expire[REQ_ASYNC]); @@ -1982,6 +1982,7 @@ static ssize_t __FUNC(struct as_data *ad, const char *page, size_t count) \ *(__PTR) = (MIN); \ else if (*(__PTR) > (MAX)) \ *(__PTR) = (MAX); \ + *(__PTR) = msecs_to_jiffies(*(__PTR)); \ return ret; \ } STORE_FUNCTION(as_readexpire_store, &ad->fifo_expire[REQ_SYNC], 0, INT_MAX); diff --git a/drivers/block/cfq-iosched.c b/drivers/block/cfq-iosched.c index 738ff90bb2c1..cf7fc7609e67 100644 --- a/drivers/block/cfq-iosched.c +++ b/drivers/block/cfq-iosched.c @@ -1732,39 +1732,47 @@ cfq_status_show(struct cfq_data *cfqd, char *page) return len; } -#define SHOW_FUNCTION(__FUNC, __VAR) \ +#define SHOW_FUNCTION(__FUNC, __VAR, __CONV) \ static ssize_t __FUNC(struct cfq_data *cfqd, char *page) \ { \ - return cfq_var_show(__VAR, (page)); \ -} -SHOW_FUNCTION(cfq_quantum_show, cfqd->cfq_quantum); -SHOW_FUNCTION(cfq_queued_show, cfqd->cfq_queued); -SHOW_FUNCTION(cfq_fifo_expire_r_show, cfqd->cfq_fifo_expire_r); -SHOW_FUNCTION(cfq_fifo_expire_w_show, cfqd->cfq_fifo_expire_w); -SHOW_FUNCTION(cfq_fifo_batch_expire_show, cfqd->cfq_fifo_batch_expire); -SHOW_FUNCTION(cfq_find_best_show, cfqd->find_best_crq); -SHOW_FUNCTION(cfq_back_max_show, cfqd->cfq_back_max); -SHOW_FUNCTION(cfq_back_penalty_show, cfqd->cfq_back_penalty); + unsigned int __data = __VAR; \ + if (__CONV) \ + __data = jiffies_to_msecs(__data); \ + return cfq_var_show(__data, (page)); \ +} +SHOW_FUNCTION(cfq_quantum_show, cfqd->cfq_quantum, 0); +SHOW_FUNCTION(cfq_queued_show, cfqd->cfq_queued, 0); +SHOW_FUNCTION(cfq_fifo_expire_r_show, cfqd->cfq_fifo_expire_r, 1); +SHOW_FUNCTION(cfq_fifo_expire_w_show, cfqd->cfq_fifo_expire_w, 1); +SHOW_FUNCTION(cfq_fifo_batch_expire_show, cfqd->cfq_fifo_batch_expire, 1); +SHOW_FUNCTION(cfq_find_best_show, cfqd->find_best_crq, 0); +SHOW_FUNCTION(cfq_back_max_show, cfqd->cfq_back_max, 0); +SHOW_FUNCTION(cfq_back_penalty_show, cfqd->cfq_back_penalty, 0); #undef SHOW_FUNCTION -#define STORE_FUNCTION(__FUNC, __PTR, MIN, MAX) \ +#define STORE_FUNCTION(__FUNC, __PTR, MIN, MAX, __CONV) \ static ssize_t __FUNC(struct cfq_data *cfqd, const char *page, size_t count) \ { \ - int ret = cfq_var_store(__PTR, (page), count); \ - if (*(__PTR) < (MIN)) \ - *(__PTR) = (MIN); \ - else if (*(__PTR) > (MAX)) \ - *(__PTR) = (MAX); \ + unsigned int __data; \ + int ret = cfq_var_store(&__data, (page), count); \ + if (__data < (MIN)) \ + __data = (MIN); \ + else if (__data > (MAX)) \ + __data = (MAX); \ + if (__CONV) \ + *(__PTR) = msecs_to_jiffies(__data); \ + else \ + *(__PTR) = __data; \ return ret; \ } -STORE_FUNCTION(cfq_quantum_store, &cfqd->cfq_quantum, 1, UINT_MAX); -STORE_FUNCTION(cfq_queued_store, &cfqd->cfq_queued, 1, UINT_MAX); -STORE_FUNCTION(cfq_fifo_expire_r_store, &cfqd->cfq_fifo_expire_r, 1, UINT_MAX); -STORE_FUNCTION(cfq_fifo_expire_w_store, &cfqd->cfq_fifo_expire_w, 1, UINT_MAX); -STORE_FUNCTION(cfq_fifo_batch_expire_store, &cfqd->cfq_fifo_batch_expire, 0, UINT_MAX); -STORE_FUNCTION(cfq_find_best_store, &cfqd->find_best_crq, 0, 1); -STORE_FUNCTION(cfq_back_max_store, &cfqd->cfq_back_max, 0, UINT_MAX); -STORE_FUNCTION(cfq_back_penalty_store, &cfqd->cfq_back_penalty, 1, UINT_MAX); +STORE_FUNCTION(cfq_quantum_store, &cfqd->cfq_quantum, 1, UINT_MAX, 0); +STORE_FUNCTION(cfq_queued_store, &cfqd->cfq_queued, 1, UINT_MAX, 0); +STORE_FUNCTION(cfq_fifo_expire_r_store, &cfqd->cfq_fifo_expire_r, 1, UINT_MAX, 1); +STORE_FUNCTION(cfq_fifo_expire_w_store, &cfqd->cfq_fifo_expire_w, 1, UINT_MAX, 1); +STORE_FUNCTION(cfq_fifo_batch_expire_store, &cfqd->cfq_fifo_batch_expire, 0, UINT_MAX, 1); +STORE_FUNCTION(cfq_find_best_store, &cfqd->find_best_crq, 0, 1, 0); +STORE_FUNCTION(cfq_back_max_store, &cfqd->cfq_back_max, 0, UINT_MAX, 0); +STORE_FUNCTION(cfq_back_penalty_store, &cfqd->cfq_back_penalty, 1, UINT_MAX, 0); #undef STORE_FUNCTION static struct cfq_fs_entry cfq_quantum_entry = { diff --git a/drivers/block/deadline-iosched.c b/drivers/block/deadline-iosched.c index 0d3e2411f1d3..f482e8bdb4d6 100644 --- a/drivers/block/deadline-iosched.c +++ b/drivers/block/deadline-iosched.c @@ -805,33 +805,41 @@ deadline_var_store(unsigned int *var, const char *page, size_t count) return count; } -#define SHOW_FUNCTION(__FUNC, __VAR) \ +#define SHOW_FUNCTION(__FUNC, __VAR, __CONV) \ static ssize_t __FUNC(struct deadline_data *dd, char *page) \ { \ - return deadline_var_show(__VAR, (page)); \ -} -SHOW_FUNCTION(deadline_readexpire_show, dd->fifo_expire[READ]); -SHOW_FUNCTION(deadline_writeexpire_show, dd->fifo_expire[WRITE]); -SHOW_FUNCTION(deadline_writesstarved_show, dd->writes_starved); -SHOW_FUNCTION(deadline_frontmerges_show, dd->front_merges); -SHOW_FUNCTION(deadline_fifobatch_show, dd->fifo_batch); + unsigned int __data = __VAR; \ + if (__CONV) \ + __data = jiffies_to_msecs(__data); \ + return deadline_var_show(__data, (page)); \ +} +SHOW_FUNCTION(deadline_readexpire_show, dd->fifo_expire[READ], 1); +SHOW_FUNCTION(deadline_writeexpire_show, dd->fifo_expire[WRITE], 1); +SHOW_FUNCTION(deadline_writesstarved_show, dd->writes_starved, 0); +SHOW_FUNCTION(deadline_frontmerges_show, dd->front_merges, 0); +SHOW_FUNCTION(deadline_fifobatch_show, dd->fifo_batch, 0); #undef SHOW_FUNCTION -#define STORE_FUNCTION(__FUNC, __PTR, MIN, MAX) \ +#define STORE_FUNCTION(__FUNC, __PTR, MIN, MAX, __CONV) \ static ssize_t __FUNC(struct deadline_data *dd, const char *page, size_t count) \ { \ - int ret = deadline_var_store(__PTR, (page), count); \ - if (*(__PTR) < (MIN)) \ - *(__PTR) = (MIN); \ - else if (*(__PTR) > (MAX)) \ - *(__PTR) = (MAX); \ + unsigned int __data; \ + int ret = deadline_var_store(&__data, (page), count); \ + if (__data < (MIN)) \ + __data = (MIN); \ + else if (__data > (MAX)) \ + __data = (MAX); \ + if (__CONV) \ + *(__PTR) = msecs_to_jiffies(__data); \ + else \ + *(__PTR) = __data; \ return ret; \ } -STORE_FUNCTION(deadline_readexpire_store, &dd->fifo_expire[READ], 0, INT_MAX); -STORE_FUNCTION(deadline_writeexpire_store, &dd->fifo_expire[WRITE], 0, INT_MAX); -STORE_FUNCTION(deadline_writesstarved_store, &dd->writes_starved, INT_MIN, INT_MAX); -STORE_FUNCTION(deadline_frontmerges_store, &dd->front_merges, 0, 1); -STORE_FUNCTION(deadline_fifobatch_store, &dd->fifo_batch, 0, INT_MAX); +STORE_FUNCTION(deadline_readexpire_store, &dd->fifo_expire[READ], 0, INT_MAX, 1); +STORE_FUNCTION(deadline_writeexpire_store, &dd->fifo_expire[WRITE], 0, INT_MAX, 1); +STORE_FUNCTION(deadline_writesstarved_store, &dd->writes_starved, INT_MIN, INT_MAX, 0); +STORE_FUNCTION(deadline_frontmerges_store, &dd->front_merges, 0, 1, 0); +STORE_FUNCTION(deadline_fifobatch_store, &dd->fifo_batch, 0, INT_MAX, 0); #undef STORE_FUNCTION static struct deadline_fs_entry deadline_readexpire_entry = { -- cgit v1.2.3 From 03808b3fd055447be48735c802e3322127df3c4d Mon Sep 17 00:00:00 2001 From: Antonino Daplas Date: Mon, 18 Oct 2004 18:05:38 -0700 Subject: [PATCH] fbdev: remove unnecessary banshee_wait_idle from tdfxfb - This patch removes the unnecessary call to banshee_wait_idle() from tdfxfb_copyarea, imageblit and fillrect. Removal of the sync will garner an additional ~20% in scrolling speed. - Removes "inverse" which generates a compile warning if modular. Signed-off-by: Antonino Daplas Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/tdfxfb.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/video/tdfxfb.c b/drivers/video/tdfxfb.c index 503ecc3569e8..263a40288118 100644 --- a/drivers/video/tdfxfb.c +++ b/drivers/video/tdfxfb.c @@ -202,7 +202,6 @@ static unsigned long do_lfb_size(struct tdfx_par *par, unsigned short); */ static int nopan = 0; static int nowrap = 1; // not implemented (yet) -static int inverse = 0; static char *mode_option __initdata = NULL; /* ------------------------------------------------------------------------- @@ -921,7 +920,6 @@ static void tdfxfb_fillrect(struct fb_info *info, const struct fb_fillrect *rect tdfx_outl(par, COMMAND_2D, COMMAND_2D_FILLRECT | (tdfx_rop << 24)); tdfx_outl(par, DSTSIZE, rect->width | (rect->height << 16)); tdfx_outl(par, LAUNCH_2D, rect->dx | (rect->dy << 16)); - banshee_wait_idle(info); } /* @@ -957,7 +955,6 @@ static void tdfxfb_copyarea(struct fb_info *info, const struct fb_copyarea *area tdfx_outl(par, DSTSIZE, area->width | (area->height << 16)); tdfx_outl(par, DSTXY, dx | (dy << 16)); tdfx_outl(par, LAUNCH_2D, sx | (sy << 16)); - banshee_wait_idle(info); } static void tdfxfb_imageblit(struct fb_info *info, const struct fb_image *image) @@ -1025,7 +1022,6 @@ static void tdfxfb_imageblit(struct fb_info *info, const struct fb_image *image) case 2: tdfx_outl(par, LAUNCH_2D,*(u16*)chardata); break; case 3: tdfx_outl(par, LAUNCH_2D,*(u16*)chardata | ((chardata[3]) << 24)); break; } - banshee_wait_idle(info); } #endif /* CONFIG_FB_3DFX_ACCEL */ @@ -1397,10 +1393,7 @@ void tdfxfb_setup(char *options) while ((this_opt = strsep(&options, ",")) != NULL) { if (!*this_opt) continue; - if (!strcmp(this_opt, "inverse")) { - inverse = 1; - fb_invert_cmaps(); - } else if(!strcmp(this_opt, "nopan")) { + if(!strcmp(this_opt, "nopan")) { nopan = 1; } else if(!strcmp(this_opt, "nowrap")) { nowrap = 1; -- cgit v1.2.3 From 54f5dd97f7b152f4996c5890a8a6813a37d9d25b Mon Sep 17 00:00:00 2001 From: Antonino Daplas Date: Mon, 18 Oct 2004 18:05:50 -0700 Subject: [PATCH] fbdev: fix logo drawing failure for vga16fb This fixes the logo failing to draw in vga16fb due to faulty boolean logic. Signed-off-by: Antonino Daplas Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/vga16fb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/video/vga16fb.c b/drivers/video/vga16fb.c index 2e132fb61562..f27524cb61c2 100644 --- a/drivers/video/vga16fb.c +++ b/drivers/video/vga16fb.c @@ -1306,7 +1306,7 @@ void vga16fb_imageblit(struct fb_info *info, const struct fb_image *image) { if (image->depth == 1) vga_imageblit_expand(info, image); - else if (image->depth <= info->var.bits_per_pixel) + else vga_imageblit_color(info, image); } -- cgit v1.2.3 From 899a94d126cb9ede8b55a168f72e1b7ab5e00c92 Mon Sep 17 00:00:00 2001 From: Antonino Daplas Date: Mon, 18 Oct 2004 18:06:02 -0700 Subject: [PATCH] fbcon: Fix setup boot options of fbcon This patch fixes the 'fbcon=map: