]>
Commit | Line | Data |
---|---|---|
ad522180 GKH |
1 | From c8701a08d6a4efeae45d84d0aa87172f23b14e3c Mon Sep 17 00:00:00 2001 |
2 | From: Tilman Schmidt <tilman@imap.cc> | |
3 | Date: Thu, 30 Sep 2010 13:34:40 +0000 | |
4 | Subject: isdn/gigaset: fix bas_gigaset AT read error handling | |
5 | ||
6 | From: Tilman Schmidt <tilman@imap.cc> | |
7 | ||
8 | commit c8701a08d6a4efeae45d84d0aa87172f23b14e3c upstream. | |
9 | ||
10 | Rework the handling of USB errors in AT response reads | |
11 | to fix a possible infinite retry loop and a memory leak, | |
12 | and silence a few overly verbose kernel messages. | |
13 | ||
14 | Signed-off-by: Tilman Schmidt <tilman@imap.cc> | |
15 | Signed-off-by: David S. Miller <davem@davemloft.net> | |
16 | Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> | |
17 | ||
18 | --- | |
19 | drivers/isdn/gigaset/bas-gigaset.c | 83 ++++++++++++++----------------------- | |
20 | 1 file changed, 33 insertions(+), 50 deletions(-) | |
21 | ||
22 | --- a/drivers/isdn/gigaset/bas-gigaset.c | |
23 | +++ b/drivers/isdn/gigaset/bas-gigaset.c | |
24 | @@ -438,23 +438,27 @@ static void cmd_in_timeout(unsigned long | |
25 | return; | |
26 | } | |
27 | ||
28 | - if (ucs->retry_cmd_in++ < BAS_RETRY) { | |
29 | - dev_notice(cs->dev, "control read: timeout, retry %d\n", | |
30 | - ucs->retry_cmd_in); | |
31 | - rc = atread_submit(cs, BAS_TIMEOUT); | |
32 | - if (rc >= 0 || rc == -ENODEV) | |
33 | - /* resubmitted or disconnected */ | |
34 | - /* - bypass regular exit block */ | |
35 | - return; | |
36 | - } else { | |
37 | + if (ucs->retry_cmd_in++ >= BAS_RETRY) { | |
38 | dev_err(cs->dev, | |
39 | "control read: timeout, giving up after %d tries\n", | |
40 | ucs->retry_cmd_in); | |
41 | + kfree(ucs->rcvbuf); | |
42 | + ucs->rcvbuf = NULL; | |
43 | + ucs->rcvbuf_size = 0; | |
44 | + error_reset(cs); | |
45 | + return; | |
46 | + } | |
47 | + | |
48 | + gig_dbg(DEBUG_USBREQ, "%s: timeout, retry %d", | |
49 | + __func__, ucs->retry_cmd_in); | |
50 | + rc = atread_submit(cs, BAS_TIMEOUT); | |
51 | + if (rc < 0) { | |
52 | + kfree(ucs->rcvbuf); | |
53 | + ucs->rcvbuf = NULL; | |
54 | + ucs->rcvbuf_size = 0; | |
55 | + if (rc != -ENODEV) | |
56 | + error_reset(cs); | |
57 | } | |
58 | - kfree(ucs->rcvbuf); | |
59 | - ucs->rcvbuf = NULL; | |
60 | - ucs->rcvbuf_size = 0; | |
61 | - error_reset(cs); | |
62 | } | |
63 | ||
64 | /* read_ctrl_callback | |
65 | @@ -470,18 +474,11 @@ static void read_ctrl_callback(struct ur | |
66 | struct cardstate *cs = inbuf->cs; | |
67 | struct bas_cardstate *ucs = cs->hw.bas; | |
68 | int status = urb->status; | |
69 | - int have_data = 0; | |
70 | unsigned numbytes; | |
71 | int rc; | |
72 | ||
73 | update_basstate(ucs, 0, BS_ATRDPEND); | |
74 | wake_up(&ucs->waitqueue); | |
75 | - | |
76 | - if (!ucs->rcvbuf_size) { | |
77 | - dev_warn(cs->dev, "%s: no receive in progress\n", __func__); | |
78 | - return; | |
79 | - } | |
80 | - | |
81 | del_timer(&ucs->timer_cmd_in); | |
82 | ||
83 | switch (status) { | |
84 | @@ -495,19 +492,10 @@ static void read_ctrl_callback(struct ur | |
85 | numbytes = ucs->rcvbuf_size; | |
86 | } | |
87 | ||
88 | - /* copy received bytes to inbuf */ | |
89 | - have_data = gigaset_fill_inbuf(inbuf, ucs->rcvbuf, numbytes); | |
90 | - | |
91 | - if (unlikely(numbytes < ucs->rcvbuf_size)) { | |
92 | - /* incomplete - resubmit for remaining bytes */ | |
93 | - ucs->rcvbuf_size -= numbytes; | |
94 | - ucs->retry_cmd_in = 0; | |
95 | - rc = atread_submit(cs, BAS_TIMEOUT); | |
96 | - if (rc >= 0 || rc == -ENODEV) | |
97 | - /* resubmitted or disconnected */ | |
98 | - /* - bypass regular exit block */ | |
99 | - return; | |
100 | - error_reset(cs); | |
101 | + /* copy received bytes to inbuf, notify event layer */ | |
102 | + if (gigaset_fill_inbuf(inbuf, ucs->rcvbuf, numbytes)) { | |
103 | + gig_dbg(DEBUG_INTR, "%s-->BH", __func__); | |
104 | + gigaset_schedule_event(cs); | |
105 | } | |
106 | break; | |
107 | ||
108 | @@ -516,37 +504,32 @@ static void read_ctrl_callback(struct ur | |
109 | case -EINPROGRESS: /* pending */ | |
110 | case -ENODEV: /* device removed */ | |
111 | case -ESHUTDOWN: /* device shut down */ | |
112 | - /* no action necessary */ | |
113 | + /* no further action necessary */ | |
114 | gig_dbg(DEBUG_USBREQ, "%s: %s", | |
115 | __func__, get_usb_statmsg(status)); | |
116 | break; | |
117 | ||
118 | - default: /* severe trouble */ | |
119 | - dev_warn(cs->dev, "control read: %s\n", | |
120 | - get_usb_statmsg(status)); | |
121 | + default: /* other errors: retry */ | |
122 | if (ucs->retry_cmd_in++ < BAS_RETRY) { | |
123 | - dev_notice(cs->dev, "control read: retry %d\n", | |
124 | - ucs->retry_cmd_in); | |
125 | + gig_dbg(DEBUG_USBREQ, "%s: %s, retry %d", __func__, | |
126 | + get_usb_statmsg(status), ucs->retry_cmd_in); | |
127 | rc = atread_submit(cs, BAS_TIMEOUT); | |
128 | - if (rc >= 0 || rc == -ENODEV) | |
129 | - /* resubmitted or disconnected */ | |
130 | - /* - bypass regular exit block */ | |
131 | + if (rc >= 0) | |
132 | + /* successfully resubmitted, skip freeing */ | |
133 | return; | |
134 | - } else { | |
135 | - dev_err(cs->dev, | |
136 | - "control read: giving up after %d tries\n", | |
137 | - ucs->retry_cmd_in); | |
138 | + if (rc == -ENODEV) | |
139 | + /* disconnect, no further action necessary */ | |
140 | + break; | |
141 | } | |
142 | + dev_err(cs->dev, "control read: %s, giving up after %d tries\n", | |
143 | + get_usb_statmsg(status), ucs->retry_cmd_in); | |
144 | error_reset(cs); | |
145 | } | |
146 | ||
147 | + /* read finished, free buffer */ | |
148 | kfree(ucs->rcvbuf); | |
149 | ucs->rcvbuf = NULL; | |
150 | ucs->rcvbuf_size = 0; | |
151 | - if (have_data) { | |
152 | - gig_dbg(DEBUG_INTR, "%s-->BH", __func__); | |
153 | - gigaset_schedule_event(cs); | |
154 | - } | |
155 | } | |
156 | ||
157 | /* atread_submit |