1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun * Intel Wireless WiMAX Connection 2400m
3*4882a593Smuzhiyun * Firmware uploader's USB specifics
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun *
6*4882a593Smuzhiyun * Copyright (C) 2007-2008 Intel Corporation. All rights reserved.
7*4882a593Smuzhiyun *
8*4882a593Smuzhiyun * Redistribution and use in source and binary forms, with or without
9*4882a593Smuzhiyun * modification, are permitted provided that the following conditions
10*4882a593Smuzhiyun * are met:
11*4882a593Smuzhiyun *
12*4882a593Smuzhiyun * * Redistributions of source code must retain the above copyright
13*4882a593Smuzhiyun * notice, this list of conditions and the following disclaimer.
14*4882a593Smuzhiyun * * Redistributions in binary form must reproduce the above copyright
15*4882a593Smuzhiyun * notice, this list of conditions and the following disclaimer in
16*4882a593Smuzhiyun * the documentation and/or other materials provided with the
17*4882a593Smuzhiyun * distribution.
18*4882a593Smuzhiyun * * Neither the name of Intel Corporation nor the names of its
19*4882a593Smuzhiyun * contributors may be used to endorse or promote products derived
20*4882a593Smuzhiyun * from this software without specific prior written permission.
21*4882a593Smuzhiyun *
22*4882a593Smuzhiyun * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23*4882a593Smuzhiyun * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24*4882a593Smuzhiyun * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
25*4882a593Smuzhiyun * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
26*4882a593Smuzhiyun * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27*4882a593Smuzhiyun * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
28*4882a593Smuzhiyun * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29*4882a593Smuzhiyun * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30*4882a593Smuzhiyun * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31*4882a593Smuzhiyun * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
32*4882a593Smuzhiyun * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33*4882a593Smuzhiyun *
34*4882a593Smuzhiyun *
35*4882a593Smuzhiyun * Intel Corporation <linux-wimax@intel.com>
36*4882a593Smuzhiyun * Yanir Lubetkin <yanirx.lubetkin@intel.com>
37*4882a593Smuzhiyun * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
38*4882a593Smuzhiyun * - Initial implementation
39*4882a593Smuzhiyun *
40*4882a593Smuzhiyun * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
41*4882a593Smuzhiyun * - bus generic/specific split
42*4882a593Smuzhiyun *
43*4882a593Smuzhiyun * THE PROCEDURE
44*4882a593Smuzhiyun *
45*4882a593Smuzhiyun * See fw.c for the generic description of this procedure.
46*4882a593Smuzhiyun *
47*4882a593Smuzhiyun * This file implements only the USB specifics. It boils down to how
48*4882a593Smuzhiyun * to send a command and waiting for an acknowledgement from the
49*4882a593Smuzhiyun * device.
50*4882a593Smuzhiyun *
51*4882a593Smuzhiyun * This code (and process) is single threaded. It assumes it is the
52*4882a593Smuzhiyun * only thread poking around (guaranteed by fw.c).
53*4882a593Smuzhiyun *
54*4882a593Smuzhiyun * COMMAND EXECUTION
55*4882a593Smuzhiyun *
56*4882a593Smuzhiyun * A write URB is posted with the buffer to the bulk output endpoint.
57*4882a593Smuzhiyun *
58*4882a593Smuzhiyun * ACK RECEPTION
59*4882a593Smuzhiyun *
60*4882a593Smuzhiyun * We just post a URB to the notification endpoint and wait for
61*4882a593Smuzhiyun * data. We repeat until we get all the data we expect (as indicated
62*4882a593Smuzhiyun * by the call from the bus generic code).
63*4882a593Smuzhiyun *
64*4882a593Smuzhiyun * The data is not read from the bulk in endpoint for boot mode.
65*4882a593Smuzhiyun *
66*4882a593Smuzhiyun * ROADMAP
67*4882a593Smuzhiyun *
68*4882a593Smuzhiyun * i2400mu_bus_bm_cmd_send
69*4882a593Smuzhiyun * i2400m_bm_cmd_prepare...
70*4882a593Smuzhiyun * i2400mu_tx_bulk_out
71*4882a593Smuzhiyun *
72*4882a593Smuzhiyun * i2400mu_bus_bm_wait_for_ack
73*4882a593Smuzhiyun * i2400m_notif_submit
74*4882a593Smuzhiyun */
75*4882a593Smuzhiyun #include <linux/usb.h>
76*4882a593Smuzhiyun #include <linux/gfp.h>
77*4882a593Smuzhiyun #include "i2400m-usb.h"
78*4882a593Smuzhiyun
79*4882a593Smuzhiyun
80*4882a593Smuzhiyun #define D_SUBMODULE fw
81*4882a593Smuzhiyun #include "usb-debug-levels.h"
82*4882a593Smuzhiyun
83*4882a593Smuzhiyun
84*4882a593Smuzhiyun /*
85*4882a593Smuzhiyun * Synchronous write to the device
86*4882a593Smuzhiyun *
87*4882a593Smuzhiyun * Takes care of updating EDC counts and thus, handle device errors.
88*4882a593Smuzhiyun */
89*4882a593Smuzhiyun static
i2400mu_tx_bulk_out(struct i2400mu * i2400mu,void * buf,size_t buf_size)90*4882a593Smuzhiyun ssize_t i2400mu_tx_bulk_out(struct i2400mu *i2400mu, void *buf, size_t buf_size)
91*4882a593Smuzhiyun {
92*4882a593Smuzhiyun int result;
93*4882a593Smuzhiyun struct device *dev = &i2400mu->usb_iface->dev;
94*4882a593Smuzhiyun int len;
95*4882a593Smuzhiyun struct usb_endpoint_descriptor *epd;
96*4882a593Smuzhiyun int pipe, do_autopm = 1;
97*4882a593Smuzhiyun
98*4882a593Smuzhiyun result = usb_autopm_get_interface(i2400mu->usb_iface);
99*4882a593Smuzhiyun if (result < 0) {
100*4882a593Smuzhiyun dev_err(dev, "BM-CMD: can't get autopm: %d\n", result);
101*4882a593Smuzhiyun do_autopm = 0;
102*4882a593Smuzhiyun }
103*4882a593Smuzhiyun epd = usb_get_epd(i2400mu->usb_iface, i2400mu->endpoint_cfg.bulk_out);
104*4882a593Smuzhiyun pipe = usb_sndbulkpipe(i2400mu->usb_dev, epd->bEndpointAddress);
105*4882a593Smuzhiyun retry:
106*4882a593Smuzhiyun result = usb_bulk_msg(i2400mu->usb_dev, pipe, buf, buf_size, &len, 200);
107*4882a593Smuzhiyun switch (result) {
108*4882a593Smuzhiyun case 0:
109*4882a593Smuzhiyun if (len != buf_size) {
110*4882a593Smuzhiyun dev_err(dev, "BM-CMD: short write (%u B vs %zu "
111*4882a593Smuzhiyun "expected)\n", len, buf_size);
112*4882a593Smuzhiyun result = -EIO;
113*4882a593Smuzhiyun break;
114*4882a593Smuzhiyun }
115*4882a593Smuzhiyun result = len;
116*4882a593Smuzhiyun break;
117*4882a593Smuzhiyun case -EPIPE:
118*4882a593Smuzhiyun /*
119*4882a593Smuzhiyun * Stall -- maybe the device is choking with our
120*4882a593Smuzhiyun * requests. Clear it and give it some time. If they
121*4882a593Smuzhiyun * happen to often, it might be another symptom, so we
122*4882a593Smuzhiyun * reset.
123*4882a593Smuzhiyun *
124*4882a593Smuzhiyun * No error handling for usb_clear_halt(0; if it
125*4882a593Smuzhiyun * works, the retry works; if it fails, this switch
126*4882a593Smuzhiyun * does the error handling for us.
127*4882a593Smuzhiyun */
128*4882a593Smuzhiyun if (edc_inc(&i2400mu->urb_edc,
129*4882a593Smuzhiyun 10 * EDC_MAX_ERRORS, EDC_ERROR_TIMEFRAME)) {
130*4882a593Smuzhiyun dev_err(dev, "BM-CMD: too many stalls in "
131*4882a593Smuzhiyun "URB; resetting device\n");
132*4882a593Smuzhiyun usb_queue_reset_device(i2400mu->usb_iface);
133*4882a593Smuzhiyun } else {
134*4882a593Smuzhiyun usb_clear_halt(i2400mu->usb_dev, pipe);
135*4882a593Smuzhiyun msleep(10); /* give the device some time */
136*4882a593Smuzhiyun goto retry;
137*4882a593Smuzhiyun }
138*4882a593Smuzhiyun fallthrough;
139*4882a593Smuzhiyun case -EINVAL: /* while removing driver */
140*4882a593Smuzhiyun case -ENODEV: /* dev disconnect ... */
141*4882a593Smuzhiyun case -ENOENT: /* just ignore it */
142*4882a593Smuzhiyun case -ESHUTDOWN: /* and exit */
143*4882a593Smuzhiyun case -ECONNRESET:
144*4882a593Smuzhiyun result = -ESHUTDOWN;
145*4882a593Smuzhiyun break;
146*4882a593Smuzhiyun case -ETIMEDOUT: /* bah... */
147*4882a593Smuzhiyun break;
148*4882a593Smuzhiyun default: /* any other? */
149*4882a593Smuzhiyun if (edc_inc(&i2400mu->urb_edc,
150*4882a593Smuzhiyun EDC_MAX_ERRORS, EDC_ERROR_TIMEFRAME)) {
151*4882a593Smuzhiyun dev_err(dev, "BM-CMD: maximum errors in "
152*4882a593Smuzhiyun "URB exceeded; resetting device\n");
153*4882a593Smuzhiyun usb_queue_reset_device(i2400mu->usb_iface);
154*4882a593Smuzhiyun result = -ENODEV;
155*4882a593Smuzhiyun break;
156*4882a593Smuzhiyun }
157*4882a593Smuzhiyun dev_err(dev, "BM-CMD: URB error %d, retrying\n",
158*4882a593Smuzhiyun result);
159*4882a593Smuzhiyun goto retry;
160*4882a593Smuzhiyun }
161*4882a593Smuzhiyun if (do_autopm)
162*4882a593Smuzhiyun usb_autopm_put_interface(i2400mu->usb_iface);
163*4882a593Smuzhiyun return result;
164*4882a593Smuzhiyun }
165*4882a593Smuzhiyun
166*4882a593Smuzhiyun
167*4882a593Smuzhiyun /*
168*4882a593Smuzhiyun * Send a boot-mode command over the bulk-out pipe
169*4882a593Smuzhiyun *
170*4882a593Smuzhiyun * Command can be a raw command, which requires no preparation (and
171*4882a593Smuzhiyun * which might not even be following the command format). Checks that
172*4882a593Smuzhiyun * the right amount of data was transferred.
173*4882a593Smuzhiyun *
174*4882a593Smuzhiyun * To satisfy USB requirements (no onstack, vmalloc or in data segment
175*4882a593Smuzhiyun * buffers), we copy the command to i2400m->bm_cmd_buf and send it from
176*4882a593Smuzhiyun * there.
177*4882a593Smuzhiyun *
178*4882a593Smuzhiyun * @flags: pass thru from i2400m_bm_cmd()
179*4882a593Smuzhiyun * @return: cmd_size if ok, < 0 errno code on error.
180*4882a593Smuzhiyun */
i2400mu_bus_bm_cmd_send(struct i2400m * i2400m,const struct i2400m_bootrom_header * _cmd,size_t cmd_size,int flags)181*4882a593Smuzhiyun ssize_t i2400mu_bus_bm_cmd_send(struct i2400m *i2400m,
182*4882a593Smuzhiyun const struct i2400m_bootrom_header *_cmd,
183*4882a593Smuzhiyun size_t cmd_size, int flags)
184*4882a593Smuzhiyun {
185*4882a593Smuzhiyun ssize_t result;
186*4882a593Smuzhiyun struct device *dev = i2400m_dev(i2400m);
187*4882a593Smuzhiyun struct i2400mu *i2400mu = container_of(i2400m, struct i2400mu, i2400m);
188*4882a593Smuzhiyun int opcode = _cmd == NULL ? -1 : i2400m_brh_get_opcode(_cmd);
189*4882a593Smuzhiyun struct i2400m_bootrom_header *cmd;
190*4882a593Smuzhiyun size_t cmd_size_a = ALIGN(cmd_size, 16); /* USB restriction */
191*4882a593Smuzhiyun
192*4882a593Smuzhiyun d_fnstart(8, dev, "(i2400m %p cmd %p size %zu)\n",
193*4882a593Smuzhiyun i2400m, _cmd, cmd_size);
194*4882a593Smuzhiyun result = -E2BIG;
195*4882a593Smuzhiyun if (cmd_size > I2400M_BM_CMD_BUF_SIZE)
196*4882a593Smuzhiyun goto error_too_big;
197*4882a593Smuzhiyun if (_cmd != i2400m->bm_cmd_buf)
198*4882a593Smuzhiyun memmove(i2400m->bm_cmd_buf, _cmd, cmd_size);
199*4882a593Smuzhiyun cmd = i2400m->bm_cmd_buf;
200*4882a593Smuzhiyun if (cmd_size_a > cmd_size) /* Zero pad space */
201*4882a593Smuzhiyun memset(i2400m->bm_cmd_buf + cmd_size, 0, cmd_size_a - cmd_size);
202*4882a593Smuzhiyun if ((flags & I2400M_BM_CMD_RAW) == 0) {
203*4882a593Smuzhiyun if (WARN_ON(i2400m_brh_get_response_required(cmd) == 0))
204*4882a593Smuzhiyun dev_warn(dev, "SW BUG: response_required == 0\n");
205*4882a593Smuzhiyun i2400m_bm_cmd_prepare(cmd);
206*4882a593Smuzhiyun }
207*4882a593Smuzhiyun result = i2400mu_tx_bulk_out(i2400mu, i2400m->bm_cmd_buf, cmd_size);
208*4882a593Smuzhiyun if (result < 0) {
209*4882a593Smuzhiyun dev_err(dev, "boot-mode cmd %d: cannot send: %zd\n",
210*4882a593Smuzhiyun opcode, result);
211*4882a593Smuzhiyun goto error_cmd_send;
212*4882a593Smuzhiyun }
213*4882a593Smuzhiyun if (result != cmd_size) { /* all was transferred? */
214*4882a593Smuzhiyun dev_err(dev, "boot-mode cmd %d: incomplete transfer "
215*4882a593Smuzhiyun "(%zd vs %zu submitted)\n", opcode, result, cmd_size);
216*4882a593Smuzhiyun result = -EIO;
217*4882a593Smuzhiyun goto error_cmd_size;
218*4882a593Smuzhiyun }
219*4882a593Smuzhiyun error_cmd_size:
220*4882a593Smuzhiyun error_cmd_send:
221*4882a593Smuzhiyun error_too_big:
222*4882a593Smuzhiyun d_fnend(8, dev, "(i2400m %p cmd %p size %zu) = %zd\n",
223*4882a593Smuzhiyun i2400m, _cmd, cmd_size, result);
224*4882a593Smuzhiyun return result;
225*4882a593Smuzhiyun }
226*4882a593Smuzhiyun
227*4882a593Smuzhiyun
228*4882a593Smuzhiyun static
__i2400mu_bm_notif_cb(struct urb * urb)229*4882a593Smuzhiyun void __i2400mu_bm_notif_cb(struct urb *urb)
230*4882a593Smuzhiyun {
231*4882a593Smuzhiyun complete(urb->context);
232*4882a593Smuzhiyun }
233*4882a593Smuzhiyun
234*4882a593Smuzhiyun
235*4882a593Smuzhiyun /*
236*4882a593Smuzhiyun * submit a read to the notification endpoint
237*4882a593Smuzhiyun *
238*4882a593Smuzhiyun * @i2400m: device descriptor
239*4882a593Smuzhiyun * @urb: urb to use
240*4882a593Smuzhiyun * @completion: completion variable to complete when done
241*4882a593Smuzhiyun *
242*4882a593Smuzhiyun * Data is always read to i2400m->bm_ack_buf
243*4882a593Smuzhiyun */
244*4882a593Smuzhiyun static
i2400mu_notif_submit(struct i2400mu * i2400mu,struct urb * urb,struct completion * completion)245*4882a593Smuzhiyun int i2400mu_notif_submit(struct i2400mu *i2400mu, struct urb *urb,
246*4882a593Smuzhiyun struct completion *completion)
247*4882a593Smuzhiyun {
248*4882a593Smuzhiyun struct i2400m *i2400m = &i2400mu->i2400m;
249*4882a593Smuzhiyun struct usb_endpoint_descriptor *epd;
250*4882a593Smuzhiyun int pipe;
251*4882a593Smuzhiyun
252*4882a593Smuzhiyun epd = usb_get_epd(i2400mu->usb_iface,
253*4882a593Smuzhiyun i2400mu->endpoint_cfg.notification);
254*4882a593Smuzhiyun pipe = usb_rcvintpipe(i2400mu->usb_dev, epd->bEndpointAddress);
255*4882a593Smuzhiyun usb_fill_int_urb(urb, i2400mu->usb_dev, pipe,
256*4882a593Smuzhiyun i2400m->bm_ack_buf, I2400M_BM_ACK_BUF_SIZE,
257*4882a593Smuzhiyun __i2400mu_bm_notif_cb, completion,
258*4882a593Smuzhiyun epd->bInterval);
259*4882a593Smuzhiyun return usb_submit_urb(urb, GFP_KERNEL);
260*4882a593Smuzhiyun }
261*4882a593Smuzhiyun
262*4882a593Smuzhiyun
263*4882a593Smuzhiyun /*
264*4882a593Smuzhiyun * Read an ack from the notification endpoint
265*4882a593Smuzhiyun *
266*4882a593Smuzhiyun * @i2400m:
267*4882a593Smuzhiyun * @_ack: pointer to where to store the read data
268*4882a593Smuzhiyun * @ack_size: how many bytes we should read
269*4882a593Smuzhiyun *
270*4882a593Smuzhiyun * Returns: < 0 errno code on error; otherwise, amount of received bytes.
271*4882a593Smuzhiyun *
272*4882a593Smuzhiyun * Submits a notification read, appends the read data to the given ack
273*4882a593Smuzhiyun * buffer and then repeats (until @ack_size bytes have been
274*4882a593Smuzhiyun * received).
275*4882a593Smuzhiyun */
i2400mu_bus_bm_wait_for_ack(struct i2400m * i2400m,struct i2400m_bootrom_header * _ack,size_t ack_size)276*4882a593Smuzhiyun ssize_t i2400mu_bus_bm_wait_for_ack(struct i2400m *i2400m,
277*4882a593Smuzhiyun struct i2400m_bootrom_header *_ack,
278*4882a593Smuzhiyun size_t ack_size)
279*4882a593Smuzhiyun {
280*4882a593Smuzhiyun ssize_t result = -ENOMEM;
281*4882a593Smuzhiyun struct device *dev = i2400m_dev(i2400m);
282*4882a593Smuzhiyun struct i2400mu *i2400mu = container_of(i2400m, struct i2400mu, i2400m);
283*4882a593Smuzhiyun struct urb notif_urb;
284*4882a593Smuzhiyun void *ack = _ack;
285*4882a593Smuzhiyun size_t offset, len;
286*4882a593Smuzhiyun long val;
287*4882a593Smuzhiyun int do_autopm = 1;
288*4882a593Smuzhiyun DECLARE_COMPLETION_ONSTACK(notif_completion);
289*4882a593Smuzhiyun
290*4882a593Smuzhiyun d_fnstart(8, dev, "(i2400m %p ack %p size %zu)\n",
291*4882a593Smuzhiyun i2400m, ack, ack_size);
292*4882a593Smuzhiyun BUG_ON(_ack == i2400m->bm_ack_buf);
293*4882a593Smuzhiyun result = usb_autopm_get_interface(i2400mu->usb_iface);
294*4882a593Smuzhiyun if (result < 0) {
295*4882a593Smuzhiyun dev_err(dev, "BM-ACK: can't get autopm: %d\n", (int) result);
296*4882a593Smuzhiyun do_autopm = 0;
297*4882a593Smuzhiyun }
298*4882a593Smuzhiyun usb_init_urb(¬if_urb); /* ready notifications */
299*4882a593Smuzhiyun usb_get_urb(¬if_urb);
300*4882a593Smuzhiyun offset = 0;
301*4882a593Smuzhiyun while (offset < ack_size) {
302*4882a593Smuzhiyun init_completion(¬if_completion);
303*4882a593Smuzhiyun result = i2400mu_notif_submit(i2400mu, ¬if_urb,
304*4882a593Smuzhiyun ¬if_completion);
305*4882a593Smuzhiyun if (result < 0)
306*4882a593Smuzhiyun goto error_notif_urb_submit;
307*4882a593Smuzhiyun val = wait_for_completion_interruptible_timeout(
308*4882a593Smuzhiyun ¬if_completion, HZ);
309*4882a593Smuzhiyun if (val == 0) {
310*4882a593Smuzhiyun result = -ETIMEDOUT;
311*4882a593Smuzhiyun usb_kill_urb(¬if_urb); /* Timedout */
312*4882a593Smuzhiyun goto error_notif_wait;
313*4882a593Smuzhiyun }
314*4882a593Smuzhiyun if (val == -ERESTARTSYS) {
315*4882a593Smuzhiyun result = -EINTR; /* Interrupted */
316*4882a593Smuzhiyun usb_kill_urb(¬if_urb);
317*4882a593Smuzhiyun goto error_notif_wait;
318*4882a593Smuzhiyun }
319*4882a593Smuzhiyun result = notif_urb.status; /* How was the ack? */
320*4882a593Smuzhiyun switch (result) {
321*4882a593Smuzhiyun case 0:
322*4882a593Smuzhiyun break;
323*4882a593Smuzhiyun case -EINVAL: /* while removing driver */
324*4882a593Smuzhiyun case -ENODEV: /* dev disconnect ... */
325*4882a593Smuzhiyun case -ENOENT: /* just ignore it */
326*4882a593Smuzhiyun case -ESHUTDOWN: /* and exit */
327*4882a593Smuzhiyun case -ECONNRESET:
328*4882a593Smuzhiyun result = -ESHUTDOWN;
329*4882a593Smuzhiyun goto error_dev_gone;
330*4882a593Smuzhiyun default: /* any other? */
331*4882a593Smuzhiyun usb_kill_urb(¬if_urb); /* Timedout */
332*4882a593Smuzhiyun if (edc_inc(&i2400mu->urb_edc,
333*4882a593Smuzhiyun EDC_MAX_ERRORS, EDC_ERROR_TIMEFRAME))
334*4882a593Smuzhiyun goto error_exceeded;
335*4882a593Smuzhiyun dev_err(dev, "BM-ACK: URB error %d, "
336*4882a593Smuzhiyun "retrying\n", notif_urb.status);
337*4882a593Smuzhiyun continue; /* retry */
338*4882a593Smuzhiyun }
339*4882a593Smuzhiyun if (notif_urb.actual_length == 0) {
340*4882a593Smuzhiyun d_printf(6, dev, "ZLP received, retrying\n");
341*4882a593Smuzhiyun continue;
342*4882a593Smuzhiyun }
343*4882a593Smuzhiyun /* Got data, append it to the buffer */
344*4882a593Smuzhiyun len = min(ack_size - offset, (size_t) notif_urb.actual_length);
345*4882a593Smuzhiyun memcpy(ack + offset, i2400m->bm_ack_buf, len);
346*4882a593Smuzhiyun offset += len;
347*4882a593Smuzhiyun }
348*4882a593Smuzhiyun result = offset;
349*4882a593Smuzhiyun error_notif_urb_submit:
350*4882a593Smuzhiyun error_notif_wait:
351*4882a593Smuzhiyun error_dev_gone:
352*4882a593Smuzhiyun out:
353*4882a593Smuzhiyun if (do_autopm)
354*4882a593Smuzhiyun usb_autopm_put_interface(i2400mu->usb_iface);
355*4882a593Smuzhiyun d_fnend(8, dev, "(i2400m %p ack %p size %zu) = %ld\n",
356*4882a593Smuzhiyun i2400m, ack, ack_size, (long) result);
357*4882a593Smuzhiyun usb_put_urb(¬if_urb);
358*4882a593Smuzhiyun return result;
359*4882a593Smuzhiyun
360*4882a593Smuzhiyun error_exceeded:
361*4882a593Smuzhiyun dev_err(dev, "bm: maximum errors in notification URB exceeded; "
362*4882a593Smuzhiyun "resetting device\n");
363*4882a593Smuzhiyun usb_queue_reset_device(i2400mu->usb_iface);
364*4882a593Smuzhiyun goto out;
365*4882a593Smuzhiyun }
366