diff options
author | Daniel Gollub <dgollub@suse.de> | 2007-01-16 05:03:01 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2007-01-22 14:46:55 -0500 |
commit | deb31f1764e0a11bcfe8d44e0658f83d83860e84 (patch) | |
tree | 618d3ac0b7860b1ff58a43154db7f91fac180900 /drivers/usb | |
parent | d0ffff8fddd5853e4b2b101790ac0c3690655af5 (diff) |
USB: rndis_host: fix crash while probing a Nokia S60 mobile
Bug fix for driver rndis_host which fixes rndis_host probing certain
Nokia S60 (Series 60) mobiles. While the rndis_host get probed by usbnet
and tries to bind the Nokia mobile the bind is going to fail. The
rndis_host module tries to release the device, in a wrong way, which
cause the oops.
Fixes Bugzilla #7201
Signed-off-by: Daniel Gollub <dgollub@suse.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb')
-rw-r--r-- | drivers/usb/net/rndis_host.c | 23 |
1 files changed, 14 insertions, 9 deletions
diff --git a/drivers/usb/net/rndis_host.c b/drivers/usb/net/rndis_host.c index ea5f44de3de2..a322a16d9cf8 100644 --- a/drivers/usb/net/rndis_host.c +++ b/drivers/usb/net/rndis_host.c | |||
@@ -379,6 +379,7 @@ static int rndis_bind(struct usbnet *dev, struct usb_interface *intf) | |||
379 | { | 379 | { |
380 | int retval; | 380 | int retval; |
381 | struct net_device *net = dev->net; | 381 | struct net_device *net = dev->net; |
382 | struct cdc_state *info = (void *) &dev->data; | ||
382 | union { | 383 | union { |
383 | void *buf; | 384 | void *buf; |
384 | struct rndis_msg_hdr *header; | 385 | struct rndis_msg_hdr *header; |
@@ -397,7 +398,7 @@ static int rndis_bind(struct usbnet *dev, struct usb_interface *intf) | |||
397 | return -ENOMEM; | 398 | return -ENOMEM; |
398 | retval = usbnet_generic_cdc_bind(dev, intf); | 399 | retval = usbnet_generic_cdc_bind(dev, intf); |
399 | if (retval < 0) | 400 | if (retval < 0) |
400 | goto done; | 401 | goto fail; |
401 | 402 | ||
402 | net->hard_header_len += sizeof (struct rndis_data_hdr); | 403 | net->hard_header_len += sizeof (struct rndis_data_hdr); |
403 | 404 | ||
@@ -412,10 +413,7 @@ static int rndis_bind(struct usbnet *dev, struct usb_interface *intf) | |||
412 | if (unlikely(retval < 0)) { | 413 | if (unlikely(retval < 0)) { |
413 | /* it might not even be an RNDIS device!! */ | 414 | /* it might not even be an RNDIS device!! */ |
414 | dev_err(&intf->dev, "RNDIS init failed, %d\n", retval); | 415 | dev_err(&intf->dev, "RNDIS init failed, %d\n", retval); |
415 | fail: | 416 | goto fail_and_release; |
416 | usb_driver_release_interface(driver_of(intf), | ||
417 | ((struct cdc_state *)&(dev->data))->data); | ||
418 | goto done; | ||
419 | } | 417 | } |
420 | dev->hard_mtu = le32_to_cpu(u.init_c->max_transfer_size); | 418 | dev->hard_mtu = le32_to_cpu(u.init_c->max_transfer_size); |
421 | /* REVISIT: peripheral "alignment" request is ignored ... */ | 419 | /* REVISIT: peripheral "alignment" request is ignored ... */ |
@@ -431,7 +429,7 @@ fail: | |||
431 | retval = rndis_command(dev, u.header); | 429 | retval = rndis_command(dev, u.header); |
432 | if (unlikely(retval < 0)) { | 430 | if (unlikely(retval < 0)) { |
433 | dev_err(&intf->dev, "rndis get ethaddr, %d\n", retval); | 431 | dev_err(&intf->dev, "rndis get ethaddr, %d\n", retval); |
434 | goto fail; | 432 | goto fail_and_release; |
435 | } | 433 | } |
436 | tmp = le32_to_cpu(u.get_c->offset); | 434 | tmp = le32_to_cpu(u.get_c->offset); |
437 | if (unlikely((tmp + 8) > (1024 - ETH_ALEN) | 435 | if (unlikely((tmp + 8) > (1024 - ETH_ALEN) |
@@ -439,7 +437,7 @@ fail: | |||
439 | dev_err(&intf->dev, "rndis ethaddr off %d len %d ?\n", | 437 | dev_err(&intf->dev, "rndis ethaddr off %d len %d ?\n", |
440 | tmp, le32_to_cpu(u.get_c->len)); | 438 | tmp, le32_to_cpu(u.get_c->len)); |
441 | retval = -EDOM; | 439 | retval = -EDOM; |
442 | goto fail; | 440 | goto fail_and_release; |
443 | } | 441 | } |
444 | memcpy(net->dev_addr, tmp + (char *)&u.get_c->request_id, ETH_ALEN); | 442 | memcpy(net->dev_addr, tmp + (char *)&u.get_c->request_id, ETH_ALEN); |
445 | 443 | ||
@@ -455,11 +453,18 @@ fail: | |||
455 | retval = rndis_command(dev, u.header); | 453 | retval = rndis_command(dev, u.header); |
456 | if (unlikely(retval < 0)) { | 454 | if (unlikely(retval < 0)) { |
457 | dev_err(&intf->dev, "rndis set packet filter, %d\n", retval); | 455 | dev_err(&intf->dev, "rndis set packet filter, %d\n", retval); |
458 | goto fail; | 456 | goto fail_and_release; |
459 | } | 457 | } |
460 | 458 | ||
461 | retval = 0; | 459 | retval = 0; |
462 | done: | 460 | |
461 | kfree(u.buf); | ||
462 | return retval; | ||
463 | |||
464 | fail_and_release: | ||
465 | usb_set_intfdata(info->data, NULL); | ||
466 | usb_driver_release_interface(driver_of(intf), info->data); | ||
467 | fail: | ||
463 | kfree(u.buf); | 468 | kfree(u.buf); |
464 | return retval; | 469 | return retval; |
465 | } | 470 | } |