Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 214838
b: refs/heads/master
c: c9c0c30
h: refs/heads/master
v: v3
  • Loading branch information
Tilman Schmidt authored and David S. Miller committed Oct 1, 2010
1 parent a76b10e commit 044f22a
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 14 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 4cb5e42f6132bf2d2851f8dd67bd8499979c7ebc
refs/heads/master: c9c0c3043452d30ab87135ba3753ce7855a3a8e7
106 changes: 93 additions & 13 deletions trunk/drivers/isdn/gigaset/bas-gigaset.c
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,9 @@ struct bas_cardstate {

struct urb *urb_int_in; /* URB for interrupt pipe */
unsigned char *int_in_buf;
struct work_struct int_in_wq; /* for usb_clear_halt() */
struct timer_list timer_int_in; /* int read retry delay */
int retry_int_in;

spinlock_t lock; /* locks all following */
int basstate; /* bitmap (BS_*) */
Expand Down Expand Up @@ -595,6 +598,62 @@ static int atread_submit(struct cardstate *cs, int timeout)
return 0;
}

/* int_in_work
* workqueue routine to clear halt on interrupt in endpoint
*/

static void int_in_work(struct work_struct *work)
{
struct bas_cardstate *ucs =
container_of(work, struct bas_cardstate, int_in_wq);
struct urb *urb = ucs->urb_int_in;
struct cardstate *cs = urb->context;
int rc;

/* clear halt condition */
rc = usb_clear_halt(ucs->udev, urb->pipe);
gig_dbg(DEBUG_USBREQ, "clear_halt: %s", get_usb_rcmsg(rc));
if (rc == 0)
/* success, resubmit interrupt read URB */
rc = usb_submit_urb(urb, GFP_ATOMIC);
if (rc != 0 && rc != -ENODEV) {
dev_err(cs->dev, "clear halt failed: %s\n", get_usb_rcmsg(rc));
rc = usb_lock_device_for_reset(ucs->udev, ucs->interface);
if (rc == 0) {
rc = usb_reset_device(ucs->udev);
usb_unlock_device(ucs->udev);
}
}
ucs->retry_int_in = 0;
}

/* int_in_resubmit
* timer routine for interrupt read delayed resubmit
* argument:
* controller state structure
*/
static void int_in_resubmit(unsigned long data)
{
struct cardstate *cs = (struct cardstate *) data;
struct bas_cardstate *ucs = cs->hw.bas;
int rc;

if (ucs->retry_int_in++ >= BAS_RETRY) {
dev_err(cs->dev, "interrupt read: giving up after %d tries\n",
ucs->retry_int_in);
usb_queue_reset_device(ucs->interface);
return;
}

gig_dbg(DEBUG_USBREQ, "%s: retry %d", __func__, ucs->retry_int_in);
rc = usb_submit_urb(ucs->urb_int_in, GFP_ATOMIC);
if (rc != 0 && rc != -ENODEV) {
dev_err(cs->dev, "could not resubmit interrupt URB: %s\n",
get_usb_rcmsg(rc));
usb_queue_reset_device(ucs->interface);
}
}

/* read_int_callback
* USB completion handler for interrupt pipe input
* called by the USB subsystem in interrupt context
Expand All @@ -615,19 +674,29 @@ static void read_int_callback(struct urb *urb)

switch (status) {
case 0: /* success */
ucs->retry_int_in = 0;
break;
case -EPIPE: /* endpoint stalled */
schedule_work(&ucs->int_in_wq);
/* fall through */
case -ENOENT: /* cancelled */
case -ECONNRESET: /* cancelled (async) */
case -EINPROGRESS: /* pending */
/* ignore silently */
case -ENODEV: /* device removed */
case -ESHUTDOWN: /* device shut down */
/* no further action necessary */
gig_dbg(DEBUG_USBREQ, "%s: %s",
__func__, get_usb_statmsg(status));
return;
case -ENODEV: /* device removed */
case -ESHUTDOWN: /* device shut down */
gig_dbg(DEBUG_USBREQ, "%s: device disconnected", __func__);
case -EPROTO: /* protocol error or unplug */
case -EILSEQ:
case -ETIME:
/* resubmit after delay */
gig_dbg(DEBUG_USBREQ, "%s: %s",
__func__, get_usb_statmsg(status));
mod_timer(&ucs->timer_int_in, jiffies + HZ / 10);
return;
default: /* severe trouble */
default: /* other errors: just resubmit */
dev_warn(cs->dev, "interrupt read: %s\n",
get_usb_statmsg(status));
goto resubmit;
Expand Down Expand Up @@ -705,6 +774,13 @@ static void read_int_callback(struct urb *urb)
break;
}
spin_lock_irqsave(&cs->lock, flags);
if (ucs->basstate & BS_ATRDPEND) {
spin_unlock_irqrestore(&cs->lock, flags);
dev_warn(cs->dev,
"HD_RECEIVEATDATA_ACK(%d) during HD_READ_ATMESSAGE(%d) ignored\n",
l, ucs->rcvbuf_size);
break;
}
if (ucs->rcvbuf_size) {
/* throw away previous buffer - we have no queue */
dev_err(cs->dev,
Expand All @@ -717,7 +793,6 @@ static void read_int_callback(struct urb *urb)
if (ucs->rcvbuf == NULL) {
spin_unlock_irqrestore(&cs->lock, flags);
dev_err(cs->dev, "out of memory receiving AT data\n");
error_reset(cs);
break;
}
ucs->rcvbuf_size = l;
Expand All @@ -727,13 +802,10 @@ static void read_int_callback(struct urb *urb)
kfree(ucs->rcvbuf);
ucs->rcvbuf = NULL;
ucs->rcvbuf_size = 0;
if (rc != -ENODEV) {
spin_unlock_irqrestore(&cs->lock, flags);
error_reset(cs);
break;
}
}
spin_unlock_irqrestore(&cs->lock, flags);
if (rc < 0 && rc != -ENODEV)
error_reset(cs);
break;

case HD_RESET_INTERRUPT_PIPE_ACK:
Expand Down Expand Up @@ -2138,7 +2210,9 @@ static int gigaset_initcshw(struct cardstate *cs)
setup_timer(&ucs->timer_ctrl, req_timeout, (unsigned long) cs);
setup_timer(&ucs->timer_atrdy, atrdy_timeout, (unsigned long) cs);
setup_timer(&ucs->timer_cmd_in, cmd_in_timeout, (unsigned long) cs);
setup_timer(&ucs->timer_int_in, int_in_resubmit, (unsigned long) cs);
init_waitqueue_head(&ucs->waitqueue);
INIT_WORK(&ucs->int_in_wq, int_in_work);

return 1;
}
Expand Down Expand Up @@ -2286,6 +2360,7 @@ static int gigaset_probe(struct usb_interface *interface,
get_usb_rcmsg(rc));
goto error;
}
ucs->retry_int_in = 0;

/* tell the device that the driver is ready */
rc = req_submit(cs->bcs, HD_DEVICE_INIT_ACK, 0, 0);
Expand Down Expand Up @@ -2338,10 +2413,12 @@ static void gigaset_disconnect(struct usb_interface *interface)
/* stop driver (common part) */
gigaset_stop(cs);

/* stop timers and URBs, free ressources */
/* stop delayed work and URBs, free ressources */
del_timer_sync(&ucs->timer_ctrl);
del_timer_sync(&ucs->timer_atrdy);
del_timer_sync(&ucs->timer_cmd_in);
del_timer_sync(&ucs->timer_int_in);
cancel_work_sync(&ucs->int_in_wq);
freeurbs(cs);
usb_set_intfdata(interface, NULL);
kfree(ucs->rcvbuf);
Expand Down Expand Up @@ -2404,12 +2481,14 @@ static int gigaset_suspend(struct usb_interface *intf, pm_message_t message)
/* in case of timeout, proceed anyway */
}

/* kill all URBs and timers that might still be pending */
/* kill all URBs and delayed work that might still be pending */
usb_kill_urb(ucs->urb_ctrl);
usb_kill_urb(ucs->urb_int_in);
del_timer_sync(&ucs->timer_ctrl);
del_timer_sync(&ucs->timer_atrdy);
del_timer_sync(&ucs->timer_cmd_in);
del_timer_sync(&ucs->timer_int_in);
cancel_work_sync(&ucs->int_in_wq);

gig_dbg(DEBUG_SUSPEND, "suspend complete");
return 0;
Expand All @@ -2431,6 +2510,7 @@ static int gigaset_resume(struct usb_interface *intf)
get_usb_rcmsg(rc));
return rc;
}
ucs->retry_int_in = 0;

/* clear suspend flag to reallow activity */
update_basstate(ucs, 0, BS_SUSPEND);
Expand Down

0 comments on commit 044f22a

Please sign in to comment.