usb_wwan.c 16.7 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
/*
  USB Driver layer for GSM modems

  Copyright (C) 2005  Matthias Urlichs <smurf@smurf.noris.de>

  This driver is free software; you can redistribute it and/or modify
  it under the terms of Version 2 of the GNU General Public License as
  published by the Free Software Foundation.

  Portions copied from the Keyspan driver by Hugh Blemings <hugh@blemings.org>

  History: see the git log.

  Work sponsored by: Sigos GmbH, Germany <info@sigos.de>

  This driver exists because the "normal" serial driver doesn't work too well
  with GSM modems. Issues:
  - data loss -- one single Receive URB is not nearly enough
  - controlling the baud rate doesn't make sense
*/

#define DRIVER_AUTHOR "Matthias Urlichs <smurf@smurf.noris.de>"
#define DRIVER_DESC "USB Driver for GSM modems"

#include <linux/kernel.h>
#include <linux/jiffies.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/module.h>
#include <linux/bitops.h>
33
#include <linux/uaccess.h>
34 35
#include <linux/usb.h>
#include <linux/usb/serial.h>
36
#include <linux/serial.h>
37 38 39 40 41 42 43
#include "usb-wwan.h"

void usb_wwan_dtr_rts(struct usb_serial_port *port, int on)
{
	struct usb_wwan_port_private *portdata;
	struct usb_wwan_intf_private *intfdata;

44
	intfdata = usb_get_serial_data(port->serial);
45 46 47 48 49

	if (!intfdata->send_setup)
		return;

	portdata = usb_get_serial_port_data(port);
50
	/* FIXME: locking */
51 52
	portdata->rts_state = on;
	portdata->dtr_state = on;
53 54

	intfdata->send_setup(port);
55 56 57
}
EXPORT_SYMBOL(usb_wwan_dtr_rts);

58
int usb_wwan_tiocmget(struct tty_struct *tty)
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
{
	struct usb_serial_port *port = tty->driver_data;
	unsigned int value;
	struct usb_wwan_port_private *portdata;

	portdata = usb_get_serial_port_data(port);

	value = ((portdata->rts_state) ? TIOCM_RTS : 0) |
	    ((portdata->dtr_state) ? TIOCM_DTR : 0) |
	    ((portdata->cts_state) ? TIOCM_CTS : 0) |
	    ((portdata->dsr_state) ? TIOCM_DSR : 0) |
	    ((portdata->dcd_state) ? TIOCM_CAR : 0) |
	    ((portdata->ri_state) ? TIOCM_RNG : 0);

	return value;
}
EXPORT_SYMBOL(usb_wwan_tiocmget);

77
int usb_wwan_tiocmset(struct tty_struct *tty,
78 79 80 81 82 83 84
		      unsigned int set, unsigned int clear)
{
	struct usb_serial_port *port = tty->driver_data;
	struct usb_wwan_port_private *portdata;
	struct usb_wwan_intf_private *intfdata;

	portdata = usb_get_serial_port_data(port);
85
	intfdata = usb_get_serial_data(port->serial);
86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103

	if (!intfdata->send_setup)
		return -EINVAL;

	/* FIXME: what locks portdata fields ? */
	if (set & TIOCM_RTS)
		portdata->rts_state = 1;
	if (set & TIOCM_DTR)
		portdata->dtr_state = 1;

	if (clear & TIOCM_RTS)
		portdata->rts_state = 0;
	if (clear & TIOCM_DTR)
		portdata->dtr_state = 0;
	return intfdata->send_setup(port);
}
EXPORT_SYMBOL(usb_wwan_tiocmset);

104 105 106 107 108 109 110 111 112
static int get_serial_info(struct usb_serial_port *port,
			   struct serial_struct __user *retinfo)
{
	struct serial_struct tmp;

	if (!retinfo)
		return -EFAULT;

	memset(&tmp, 0, sizeof(tmp));
113
	tmp.line            = port->minor;
114
	tmp.port            = port->port_number;
115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
	tmp.baud_base       = tty_get_baud_rate(port->port.tty);
	tmp.close_delay	    = port->port.close_delay / 10;
	tmp.closing_wait    = port->port.closing_wait == ASYNC_CLOSING_WAIT_NONE ?
				 ASYNC_CLOSING_WAIT_NONE :
				 port->port.closing_wait / 10;

	if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
		return -EFAULT;
	return 0;
}

static int set_serial_info(struct usb_serial_port *port,
			   struct serial_struct __user *newinfo)
{
	struct serial_struct new_serial;
	unsigned int closing_wait, close_delay;
	int retval = 0;

	if (copy_from_user(&new_serial, newinfo, sizeof(new_serial)))
		return -EFAULT;

	close_delay = new_serial.close_delay * 10;
	closing_wait = new_serial.closing_wait == ASYNC_CLOSING_WAIT_NONE ?
			ASYNC_CLOSING_WAIT_NONE : new_serial.closing_wait * 10;

	mutex_lock(&port->port.mutex);

	if (!capable(CAP_SYS_ADMIN)) {
		if ((close_delay != port->port.close_delay) ||
		    (closing_wait != port->port.closing_wait))
			retval = -EPERM;
		else
			retval = -EOPNOTSUPP;
	} else {
		port->port.close_delay  = close_delay;
		port->port.closing_wait = closing_wait;
	}

	mutex_unlock(&port->port.mutex);
	return retval;
}

157
int usb_wwan_ioctl(struct tty_struct *tty,
158 159 160 161
		   unsigned int cmd, unsigned long arg)
{
	struct usb_serial_port *port = tty->driver_data;

162
	dev_dbg(&port->dev, "%s cmd 0x%04x\n", __func__, cmd);
163 164 165 166 167 168 169 170 171 172 173 174

	switch (cmd) {
	case TIOCGSERIAL:
		return get_serial_info(port,
				       (struct serial_struct __user *) arg);
	case TIOCSSERIAL:
		return set_serial_info(port,
				       (struct serial_struct __user *) arg);
	default:
		break;
	}

175
	dev_dbg(&port->dev, "%s arg not supported\n", __func__);
176 177 178 179 180

	return -ENOIOCTLCMD;
}
EXPORT_SYMBOL(usb_wwan_ioctl);

181 182 183 184 185 186 187 188 189 190 191 192
int usb_wwan_write(struct tty_struct *tty, struct usb_serial_port *port,
		   const unsigned char *buf, int count)
{
	struct usb_wwan_port_private *portdata;
	struct usb_wwan_intf_private *intfdata;
	int i;
	int left, todo;
	struct urb *this_urb = NULL;	/* spurious */
	int err;
	unsigned long flags;

	portdata = usb_get_serial_port_data(port);
193
	intfdata = usb_get_serial_data(port->serial);
194

195
	dev_dbg(&port->dev, "%s: write (%d chars)\n", __func__, count);
196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211

	i = 0;
	left = count;
	for (i = 0; left > 0 && i < N_OUT_URB; i++) {
		todo = left;
		if (todo > OUT_BUFLEN)
			todo = OUT_BUFLEN;

		this_urb = portdata->out_urbs[i];
		if (test_and_set_bit(i, &portdata->out_busy)) {
			if (time_before(jiffies,
					portdata->tx_start_time[i] + 10 * HZ))
				continue;
			usb_unlink_urb(this_urb);
			continue;
		}
212 213
		dev_dbg(&port->dev, "%s: endpoint %d buf %d\n", __func__,
			usb_pipeendpoint(this_urb->pipe), i);
214 215

		err = usb_autopm_get_interface_async(port->serial->interface);
216 217
		if (err < 0) {
			clear_bit(i, &portdata->out_busy);
218
			break;
219
		}
220 221 222 223 224 225 226 227 228 229 230 231 232 233

		/* send the data */
		memcpy(this_urb->transfer_buffer, buf, todo);
		this_urb->transfer_buffer_length = todo;

		spin_lock_irqsave(&intfdata->susp_lock, flags);
		if (intfdata->suspended) {
			usb_anchor_urb(this_urb, &portdata->delayed);
			spin_unlock_irqrestore(&intfdata->susp_lock, flags);
		} else {
			intfdata->in_flight++;
			spin_unlock_irqrestore(&intfdata->susp_lock, flags);
			err = usb_submit_urb(this_urb, GFP_ATOMIC);
			if (err) {
234 235 236
				dev_dbg(&port->dev,
					"usb_submit_urb %p (write bulk) failed (%d)\n",
					this_urb, err);
237 238 239 240 241
				clear_bit(i, &portdata->out_busy);
				spin_lock_irqsave(&intfdata->susp_lock, flags);
				intfdata->in_flight--;
				spin_unlock_irqrestore(&intfdata->susp_lock,
						       flags);
242
				usb_autopm_put_interface_async(port->serial->interface);
243
				break;
244 245 246 247 248 249 250 251 252
			}
		}

		portdata->tx_start_time[i] = jiffies;
		buf += todo;
		left -= todo;
	}

	count -= left;
253
	dev_dbg(&port->dev, "%s: wrote (did %d)\n", __func__, count);
254 255 256 257 258 259 260 261 262
	return count;
}
EXPORT_SYMBOL(usb_wwan_write);

static void usb_wwan_indat_callback(struct urb *urb)
{
	int err;
	int endpoint;
	struct usb_serial_port *port;
263
	struct device *dev;
264 265 266 267 268
	unsigned char *data = urb->transfer_buffer;
	int status = urb->status;

	endpoint = usb_pipeendpoint(urb->pipe);
	port = urb->context;
269
	dev = &port->dev;
270 271

	if (status) {
272 273
		dev_dbg(dev, "%s: nonzero status: %d on endpoint %02x.\n",
			__func__, status, endpoint);
274
	} else {
J
Jiri Slaby 已提交
275 276 277 278 279 280
		if (urb->actual_length) {
			tty_insert_flip_string(&port->port, data,
					urb->actual_length);
			tty_flip_buffer_push(&port->port);
		} else
			dev_dbg(dev, "%s: empty read urb received\n", __func__);
281 282 283 284 285 286 287 288
	}
	/* Resubmit urb so we continue receiving */
	err = usb_submit_urb(urb, GFP_ATOMIC);
	if (err) {
		if (err != -EPERM) {
			dev_err(dev, "%s: resubmit read urb failed. (%d)\n",
				__func__, err);
			/* busy also in error unless we are killed */
289
			usb_mark_last_busy(port->serial->dev);
290
		}
291 292
	} else {
		usb_mark_last_busy(port->serial->dev);
293 294 295 296 297 298 299 300 301 302 303
	}
}

static void usb_wwan_outdat_callback(struct urb *urb)
{
	struct usb_serial_port *port;
	struct usb_wwan_port_private *portdata;
	struct usb_wwan_intf_private *intfdata;
	int i;

	port = urb->context;
304
	intfdata = usb_get_serial_data(port->serial);
305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337

	usb_serial_port_softint(port);
	usb_autopm_put_interface_async(port->serial->interface);
	portdata = usb_get_serial_port_data(port);
	spin_lock(&intfdata->susp_lock);
	intfdata->in_flight--;
	spin_unlock(&intfdata->susp_lock);

	for (i = 0; i < N_OUT_URB; ++i) {
		if (portdata->out_urbs[i] == urb) {
			smp_mb__before_clear_bit();
			clear_bit(i, &portdata->out_busy);
			break;
		}
	}
}

int usb_wwan_write_room(struct tty_struct *tty)
{
	struct usb_serial_port *port = tty->driver_data;
	struct usb_wwan_port_private *portdata;
	int i;
	int data_len = 0;
	struct urb *this_urb;

	portdata = usb_get_serial_port_data(port);

	for (i = 0; i < N_OUT_URB; i++) {
		this_urb = portdata->out_urbs[i];
		if (this_urb && !test_bit(i, &portdata->out_busy))
			data_len += OUT_BUFLEN;
	}

338
	dev_dbg(&port->dev, "%s: %d\n", __func__, data_len);
339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359
	return data_len;
}
EXPORT_SYMBOL(usb_wwan_write_room);

int usb_wwan_chars_in_buffer(struct tty_struct *tty)
{
	struct usb_serial_port *port = tty->driver_data;
	struct usb_wwan_port_private *portdata;
	int i;
	int data_len = 0;
	struct urb *this_urb;

	portdata = usb_get_serial_port_data(port);

	for (i = 0; i < N_OUT_URB; i++) {
		this_urb = portdata->out_urbs[i];
		/* FIXME: This locking is insufficient as this_urb may
		   go unused during the test */
		if (this_urb && test_bit(i, &portdata->out_busy))
			data_len += this_urb->transfer_buffer_length;
	}
360
	dev_dbg(&port->dev, "%s: %d\n", __func__, data_len);
361 362 363 364 365 366 367 368 369 370 371 372 373
	return data_len;
}
EXPORT_SYMBOL(usb_wwan_chars_in_buffer);

int usb_wwan_open(struct tty_struct *tty, struct usb_serial_port *port)
{
	struct usb_wwan_port_private *portdata;
	struct usb_wwan_intf_private *intfdata;
	struct usb_serial *serial = port->serial;
	int i, err;
	struct urb *urb;

	portdata = usb_get_serial_port_data(port);
374
	intfdata = usb_get_serial_data(serial);
375

376 377 378 379 380 381 382 383
	if (port->interrupt_in_urb) {
		err = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
		if (err) {
			dev_dbg(&port->dev, "%s: submit int urb failed: %d\n",
				__func__, err);
		}
	}

384 385 386 387 388 389 390
	/* Start reading from the IN endpoint */
	for (i = 0; i < N_IN_URB; i++) {
		urb = portdata->in_urbs[i];
		if (!urb)
			continue;
		err = usb_submit_urb(urb, GFP_KERNEL);
		if (err) {
391 392
			dev_dbg(&port->dev, "%s: submit urb %d failed (%d) %d\n",
				__func__, i, err, urb->transfer_buffer_length);
393 394 395 396 397
		}
	}

	spin_lock_irq(&intfdata->susp_lock);
	portdata->opened = 1;
J
Johan Hovold 已提交
398 399
	if (++intfdata->open_ports == 1)
		serial->interface->needs_remote_wakeup = 1;
400
	spin_unlock_irq(&intfdata->susp_lock);
401
	/* this balances a get in the generic USB serial code */
402 403 404 405 406 407
	usb_autopm_put_interface(serial->interface);

	return 0;
}
EXPORT_SYMBOL(usb_wwan_open);

408 409 410 411 412 413 414 415 416 417 418 419 420
static void unbusy_queued_urb(struct urb *urb,
					struct usb_wwan_port_private *portdata)
{
	int i;

	for (i = 0; i < N_OUT_URB; i++) {
		if (urb == portdata->out_urbs[i]) {
			clear_bit(i, &portdata->out_busy);
			break;
		}
	}
}

421 422 423 424 425
void usb_wwan_close(struct usb_serial_port *port)
{
	int i;
	struct usb_serial *serial = port->serial;
	struct usb_wwan_port_private *portdata;
426
	struct usb_wwan_intf_private *intfdata = usb_get_serial_data(serial);
427
	struct urb *urb;
428 429 430

	portdata = usb_get_serial_port_data(port);

431 432
	spin_lock_irq(&intfdata->susp_lock);
	portdata->opened = 0;
J
Johan Hovold 已提交
433 434
	if (--intfdata->open_ports == 0)
		serial->interface->needs_remote_wakeup = 0;
435 436
	spin_unlock_irq(&intfdata->susp_lock);

437 438 439 440 441 442 443 444
	for (;;) {
		urb = usb_get_from_anchor(&portdata->delayed);
		if (!urb)
			break;
		unbusy_queued_urb(urb, portdata);
		usb_autopm_put_interface_async(serial->interface);
	}

445 446 447 448
	for (i = 0; i < N_IN_URB; i++)
		usb_kill_urb(portdata->in_urbs[i]);
	for (i = 0; i < N_OUT_URB; i++)
		usb_kill_urb(portdata->out_urbs[i]);
449
	usb_kill_urb(port->interrupt_in_urb);
450 451

	usb_autopm_get_interface_no_resume(serial->interface);
452 453 454
}
EXPORT_SYMBOL(usb_wwan_close);

455 456
static struct urb *usb_wwan_setup_urb(struct usb_serial_port *port,
				      int endpoint,
457 458 459
				      int dir, void *ctx, char *buf, int len,
				      void (*callback) (struct urb *))
{
460
	struct usb_serial *serial = port->serial;
461 462 463
	struct urb *urb;

	urb = usb_alloc_urb(0, GFP_KERNEL);	/* No ISO */
464
	if (!urb)
465 466 467 468 469 470 471 472 473
		return NULL;

	usb_fill_bulk_urb(urb, serial->dev,
			  usb_sndbulkpipe(serial->dev, endpoint) | dir,
			  buf, len, callback, ctx);

	return urb;
}

474
int usb_wwan_port_probe(struct usb_serial_port *port)
475 476
{
	struct usb_wwan_port_private *portdata;
477 478 479
	struct urb *urb;
	u8 *buffer;
	int i;
480

481 482 483
	if (!port->bulk_in_size || !port->bulk_out_size)
		return -ENODEV;

484 485 486
	portdata = kzalloc(sizeof(*portdata), GFP_KERNEL);
	if (!portdata)
		return -ENOMEM;
487

488
	init_usb_anchor(&portdata->delayed);
489

490 491 492 493 494 495 496 497 498 499 500 501
	for (i = 0; i < N_IN_URB; i++) {
		buffer = (u8 *)__get_free_page(GFP_KERNEL);
		if (!buffer)
			goto bail_out_error;
		portdata->in_buffer[i] = buffer;

		urb = usb_wwan_setup_urb(port, port->bulk_in_endpointAddress,
						USB_DIR_IN, port,
						buffer, IN_BUFLEN,
						usb_wwan_indat_callback);
		portdata->in_urbs[i] = urb;
	}
502

503 504 505 506 507
	for (i = 0; i < N_OUT_URB; i++) {
		buffer = kmalloc(OUT_BUFLEN, GFP_KERNEL);
		if (!buffer)
			goto bail_out_error2;
		portdata->out_buffer[i] = buffer;
508

509 510 511 512 513 514
		urb = usb_wwan_setup_urb(port, port->bulk_out_endpointAddress,
						USB_DIR_OUT, port,
						buffer, OUT_BUFLEN,
						usb_wwan_outdat_callback);
		portdata->out_urbs[i] = urb;
	}
515

516
	usb_set_serial_port_data(port, portdata);
517 518 519 520

	return 0;

bail_out_error2:
521 522 523 524
	for (i = 0; i < N_OUT_URB; i++) {
		usb_free_urb(portdata->out_urbs[i]);
		kfree(portdata->out_buffer[i]);
	}
525
bail_out_error:
526 527 528 529
	for (i = 0; i < N_IN_URB; i++) {
		usb_free_urb(portdata->in_urbs[i]);
		free_page((unsigned long)portdata->in_buffer[i]);
	}
530
	kfree(portdata);
531 532

	return -ENOMEM;
533
}
534
EXPORT_SYMBOL_GPL(usb_wwan_port_probe);
535

536
int usb_wwan_port_remove(struct usb_serial_port *port)
537
{
538
	int i;
539 540
	struct usb_wwan_port_private *portdata;

541 542 543 544 545 546 547 548 549 550
	portdata = usb_get_serial_port_data(port);
	usb_set_serial_port_data(port, NULL);

	for (i = 0; i < N_IN_URB; i++) {
		usb_free_urb(portdata->in_urbs[i]);
		free_page((unsigned long)portdata->in_buffer[i]);
	}
	for (i = 0; i < N_OUT_URB; i++) {
		usb_free_urb(portdata->out_urbs[i]);
		kfree(portdata->out_buffer[i]);
551 552
	}

553
	kfree(portdata);
554

555
	return 0;
556
}
557
EXPORT_SYMBOL(usb_wwan_port_remove);
558

559
#ifdef CONFIG_PM
560
static void stop_urbs(struct usb_serial *serial)
561 562 563 564 565 566 567 568
{
	int i, j;
	struct usb_serial_port *port;
	struct usb_wwan_port_private *portdata;

	for (i = 0; i < serial->num_ports; ++i) {
		port = serial->port[i];
		portdata = usb_get_serial_port_data(port);
569 570
		if (!portdata)
			continue;
571 572 573 574
		for (j = 0; j < N_IN_URB; j++)
			usb_kill_urb(portdata->in_urbs[j]);
		for (j = 0; j < N_OUT_URB; j++)
			usb_kill_urb(portdata->out_urbs[j]);
575
		usb_kill_urb(port->interrupt_in_urb);
576 577 578 579 580
	}
}

int usb_wwan_suspend(struct usb_serial *serial, pm_message_t message)
{
581
	struct usb_wwan_intf_private *intfdata = usb_get_serial_data(serial);
582

583
	spin_lock_irq(&intfdata->susp_lock);
584
	if (PMSG_IS_AUTO(message)) {
585 586
		if (intfdata->in_flight) {
			spin_unlock_irq(&intfdata->susp_lock);
587
			return -EBUSY;
588
		}
589 590 591
	}
	intfdata->suspended = 1;
	spin_unlock_irq(&intfdata->susp_lock);
592

593
	stop_urbs(serial);
594 595 596 597 598

	return 0;
}
EXPORT_SYMBOL(usb_wwan_suspend);

599 600
/* Caller must hold susp_lock. */
static int usb_wwan_submit_delayed_urbs(struct usb_serial_port *port)
601
{
602
	struct usb_serial *serial = port->serial;
603
	struct usb_wwan_intf_private *data = usb_get_serial_data(serial);
604 605
	struct usb_wwan_port_private *portdata;
	struct urb *urb;
606 607
	int err_count = 0;
	int err;
608 609

	portdata = usb_get_serial_port_data(port);
610

611 612 613 614 615
	for (;;) {
		urb = usb_get_from_anchor(&portdata->delayed);
		if (!urb)
			break;

616
		err = usb_submit_urb(urb, GFP_ATOMIC);
617
		if (err) {
618
			dev_err(&port->dev, "%s: submit urb failed: %d\n",
619 620 621 622 623
					__func__, err);
			err_count++;
			unbusy_queued_urb(urb, portdata);
			usb_autopm_put_interface_async(serial->interface);
			continue;
O
Oliver Neukum 已提交
624
		}
625
		data->in_flight++;
626
	}
627

628 629 630 631
	if (err_count)
		return -EIO;

	return 0;
632 633 634 635 636 637
}

int usb_wwan_resume(struct usb_serial *serial)
{
	int i, j;
	struct usb_serial_port *port;
638
	struct usb_wwan_intf_private *intfdata = usb_get_serial_data(serial);
639 640
	struct usb_wwan_port_private *portdata;
	struct urb *urb;
641 642
	int err;
	int err_count = 0;
643

644
	spin_lock_irq(&intfdata->susp_lock);
645 646 647 648
	for (i = 0; i < serial->num_ports; i++) {
		port = serial->port[i];
		portdata = usb_get_serial_port_data(port);

649
		if (!portdata || !portdata->opened)
650 651
			continue;

652 653 654 655 656 657 658
		if (port->interrupt_in_urb) {
			err = usb_submit_urb(port->interrupt_in_urb,
					GFP_ATOMIC);
			if (err) {
				dev_err(&port->dev,
					"%s: submit int urb failed: %d\n",
					__func__, err);
659
				err_count++;
660 661 662
			}
		}

663
		err = usb_wwan_submit_delayed_urbs(port);
664 665 666
		if (err)
			err_count++;

667 668 669 670
		for (j = 0; j < N_IN_URB; j++) {
			urb = portdata->in_urbs[j];
			err = usb_submit_urb(urb, GFP_ATOMIC);
			if (err < 0) {
671 672 673
				dev_err(&port->dev,
					"%s: submit read urb %d failed: %d\n",
					__func__, i, err);
674
				err_count++;
675 676 677 678 679
			}
		}
	}
	intfdata->suspended = 0;
	spin_unlock_irq(&intfdata->susp_lock);
680 681 682 683 684

	if (err_count)
		return -EIO;

	return 0;
685 686 687 688 689 690 691
}
EXPORT_SYMBOL(usb_wwan_resume);
#endif

MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");