diff options
Diffstat (limited to 'drivers/net/wimax')
-rw-r--r-- | drivers/net/wimax/i2400m/control.c | 100 | ||||
-rw-r--r-- | drivers/net/wimax/i2400m/driver.c | 5 | ||||
-rw-r--r-- | drivers/net/wimax/i2400m/i2400m.h | 5 | ||||
-rw-r--r-- | drivers/net/wimax/i2400m/netdev.c | 4 | ||||
-rw-r--r-- | drivers/net/wimax/i2400m/rx.c | 6 | ||||
-rw-r--r-- | drivers/net/wimax/i2400m/sdio.c | 18 | ||||
-rw-r--r-- | drivers/net/wimax/i2400m/usb.c | 35 |
7 files changed, 109 insertions, 64 deletions
diff --git a/drivers/net/wimax/i2400m/control.c b/drivers/net/wimax/i2400m/control.c index b3cadb626fe0..bd193ae2178b 100644 --- a/drivers/net/wimax/i2400m/control.c +++ b/drivers/net/wimax/i2400m/control.c | |||
@@ -292,8 +292,6 @@ void i2400m_report_tlv_system_state(struct i2400m *i2400m, | |||
292 | 292 | ||
293 | d_fnstart(3, dev, "(i2400m %p ss %p [%u])\n", i2400m, ss, i2400m_state); | 293 | d_fnstart(3, dev, "(i2400m %p ss %p [%u])\n", i2400m, ss, i2400m_state); |
294 | 294 | ||
295 | if (unlikely(i2400m->ready == 0)) /* act if up */ | ||
296 | goto out; | ||
297 | if (i2400m->state != i2400m_state) { | 295 | if (i2400m->state != i2400m_state) { |
298 | i2400m->state = i2400m_state; | 296 | i2400m->state = i2400m_state; |
299 | wake_up_all(&i2400m->state_wq); | 297 | wake_up_all(&i2400m->state_wq); |
@@ -341,7 +339,6 @@ void i2400m_report_tlv_system_state(struct i2400m *i2400m, | |||
341 | i2400m->bus_reset(i2400m, I2400M_RT_WARM); | 339 | i2400m->bus_reset(i2400m, I2400M_RT_WARM); |
342 | break; | 340 | break; |
343 | }; | 341 | }; |
344 | out: | ||
345 | d_fnend(3, dev, "(i2400m %p ss %p [%u]) = void\n", | 342 | d_fnend(3, dev, "(i2400m %p ss %p [%u]) = void\n", |
346 | i2400m, ss, i2400m_state); | 343 | i2400m, ss, i2400m_state); |
347 | } | 344 | } |
@@ -372,8 +369,6 @@ void i2400m_report_tlv_media_status(struct i2400m *i2400m, | |||
372 | 369 | ||
373 | d_fnstart(3, dev, "(i2400m %p ms %p [%u])\n", i2400m, ms, status); | 370 | d_fnstart(3, dev, "(i2400m %p ms %p [%u])\n", i2400m, ms, status); |
374 | 371 | ||
375 | if (unlikely(i2400m->ready == 0)) /* act if up */ | ||
376 | goto out; | ||
377 | switch (status) { | 372 | switch (status) { |
378 | case I2400M_MEDIA_STATUS_LINK_UP: | 373 | case I2400M_MEDIA_STATUS_LINK_UP: |
379 | netif_carrier_on(net_dev); | 374 | netif_carrier_on(net_dev); |
@@ -393,14 +388,59 @@ void i2400m_report_tlv_media_status(struct i2400m *i2400m, | |||
393 | dev_err(dev, "HW BUG? unknown media status %u\n", | 388 | dev_err(dev, "HW BUG? unknown media status %u\n", |
394 | status); | 389 | status); |
395 | }; | 390 | }; |
396 | out: | ||
397 | d_fnend(3, dev, "(i2400m %p ms %p [%u]) = void\n", | 391 | d_fnend(3, dev, "(i2400m %p ms %p [%u]) = void\n", |
398 | i2400m, ms, status); | 392 | i2400m, ms, status); |
399 | } | 393 | } |
400 | 394 | ||
401 | 395 | ||
402 | /* | 396 | /* |
403 | * Parse a 'state report' and extract carrier on/off information | 397 | * Process a TLV from a 'state report' |
398 | * | ||
399 | * @i2400m: device descriptor | ||
400 | * @tlv: pointer to the TLV header; it has been already validated for | ||
401 | * consistent size. | ||
402 | * @tag: for error messages | ||
403 | * | ||
404 | * Act on the TLVs from a 'state report'. | ||
405 | */ | ||
406 | static | ||
407 | void i2400m_report_state_parse_tlv(struct i2400m *i2400m, | ||
408 | const struct i2400m_tlv_hdr *tlv, | ||
409 | const char *tag) | ||
410 | { | ||
411 | struct device *dev = i2400m_dev(i2400m); | ||
412 | const struct i2400m_tlv_media_status *ms; | ||
413 | const struct i2400m_tlv_system_state *ss; | ||
414 | const struct i2400m_tlv_rf_switches_status *rfss; | ||
415 | |||
416 | if (0 == i2400m_tlv_match(tlv, I2400M_TLV_SYSTEM_STATE, sizeof(*ss))) { | ||
417 | ss = container_of(tlv, typeof(*ss), hdr); | ||
418 | d_printf(2, dev, "%s: system state TLV " | ||
419 | "found (0x%04x), state 0x%08x\n", | ||
420 | tag, I2400M_TLV_SYSTEM_STATE, | ||
421 | le32_to_cpu(ss->state)); | ||
422 | i2400m_report_tlv_system_state(i2400m, ss); | ||
423 | } | ||
424 | if (0 == i2400m_tlv_match(tlv, I2400M_TLV_RF_STATUS, sizeof(*rfss))) { | ||
425 | rfss = container_of(tlv, typeof(*rfss), hdr); | ||
426 | d_printf(2, dev, "%s: RF status TLV " | ||
427 | "found (0x%04x), sw 0x%02x hw 0x%02x\n", | ||
428 | tag, I2400M_TLV_RF_STATUS, | ||
429 | le32_to_cpu(rfss->sw_rf_switch), | ||
430 | le32_to_cpu(rfss->hw_rf_switch)); | ||
431 | i2400m_report_tlv_rf_switches_status(i2400m, rfss); | ||
432 | } | ||
433 | if (0 == i2400m_tlv_match(tlv, I2400M_TLV_MEDIA_STATUS, sizeof(*ms))) { | ||
434 | ms = container_of(tlv, typeof(*ms), hdr); | ||
435 | d_printf(2, dev, "%s: Media Status TLV: %u\n", | ||
436 | tag, le32_to_cpu(ms->media_status)); | ||
437 | i2400m_report_tlv_media_status(i2400m, ms); | ||
438 | } | ||
439 | } | ||
440 | |||
441 | |||
442 | /* | ||
443 | * Parse a 'state report' and extract information | ||
404 | * | 444 | * |
405 | * @i2400m: device descriptor | 445 | * @i2400m: device descriptor |
406 | * @l3l4_hdr: pointer to message; it has been already validated for | 446 | * @l3l4_hdr: pointer to message; it has been already validated for |
@@ -409,13 +449,7 @@ out: | |||
409 | * declaration is assumed to be congruent with @size (as in | 449 | * declaration is assumed to be congruent with @size (as in |
410 | * sizeof(*l3l4_hdr) + l3l4_hdr->length == size) | 450 | * sizeof(*l3l4_hdr) + l3l4_hdr->length == size) |
411 | * | 451 | * |
412 | * Extract from the report state the system state TLV and infer from | 452 | * Walk over the TLVs in a report state and act on them. |
413 | * there if we have a carrier or not. Update our local state and tell | ||
414 | * netdev. | ||
415 | * | ||
416 | * When setting the carrier, it's fine to set OFF twice (for example), | ||
417 | * as netif_carrier_off() will not generate two OFF events (just on | ||
418 | * the transitions). | ||
419 | */ | 453 | */ |
420 | static | 454 | static |
421 | void i2400m_report_state_hook(struct i2400m *i2400m, | 455 | void i2400m_report_state_hook(struct i2400m *i2400m, |
@@ -424,9 +458,6 @@ void i2400m_report_state_hook(struct i2400m *i2400m, | |||
424 | { | 458 | { |
425 | struct device *dev = i2400m_dev(i2400m); | 459 | struct device *dev = i2400m_dev(i2400m); |
426 | const struct i2400m_tlv_hdr *tlv; | 460 | const struct i2400m_tlv_hdr *tlv; |
427 | const struct i2400m_tlv_system_state *ss; | ||
428 | const struct i2400m_tlv_rf_switches_status *rfss; | ||
429 | const struct i2400m_tlv_media_status *ms; | ||
430 | size_t tlv_size = le16_to_cpu(l3l4_hdr->length); | 461 | size_t tlv_size = le16_to_cpu(l3l4_hdr->length); |
431 | 462 | ||
432 | d_fnstart(4, dev, "(i2400m %p, l3l4_hdr %p, size %zu, %s)\n", | 463 | d_fnstart(4, dev, "(i2400m %p, l3l4_hdr %p, size %zu, %s)\n", |
@@ -434,34 +465,8 @@ void i2400m_report_state_hook(struct i2400m *i2400m, | |||
434 | tlv = NULL; | 465 | tlv = NULL; |
435 | 466 | ||
436 | while ((tlv = i2400m_tlv_buffer_walk(i2400m, &l3l4_hdr->pl, | 467 | while ((tlv = i2400m_tlv_buffer_walk(i2400m, &l3l4_hdr->pl, |
437 | tlv_size, tlv))) { | 468 | tlv_size, tlv))) |
438 | if (0 == i2400m_tlv_match(tlv, I2400M_TLV_SYSTEM_STATE, | 469 | i2400m_report_state_parse_tlv(i2400m, tlv, tag); |
439 | sizeof(*ss))) { | ||
440 | ss = container_of(tlv, typeof(*ss), hdr); | ||
441 | d_printf(2, dev, "%s: system state TLV " | ||
442 | "found (0x%04x), state 0x%08x\n", | ||
443 | tag, I2400M_TLV_SYSTEM_STATE, | ||
444 | le32_to_cpu(ss->state)); | ||
445 | i2400m_report_tlv_system_state(i2400m, ss); | ||
446 | } | ||
447 | if (0 == i2400m_tlv_match(tlv, I2400M_TLV_RF_STATUS, | ||
448 | sizeof(*rfss))) { | ||
449 | rfss = container_of(tlv, typeof(*rfss), hdr); | ||
450 | d_printf(2, dev, "%s: RF status TLV " | ||
451 | "found (0x%04x), sw 0x%02x hw 0x%02x\n", | ||
452 | tag, I2400M_TLV_RF_STATUS, | ||
453 | le32_to_cpu(rfss->sw_rf_switch), | ||
454 | le32_to_cpu(rfss->hw_rf_switch)); | ||
455 | i2400m_report_tlv_rf_switches_status(i2400m, rfss); | ||
456 | } | ||
457 | if (0 == i2400m_tlv_match(tlv, I2400M_TLV_MEDIA_STATUS, | ||
458 | sizeof(*ms))) { | ||
459 | ms = container_of(tlv, typeof(*ms), hdr); | ||
460 | d_printf(2, dev, "%s: Media Status TLV: %u\n", | ||
461 | tag, le32_to_cpu(ms->media_status)); | ||
462 | i2400m_report_tlv_media_status(i2400m, ms); | ||
463 | } | ||
464 | } | ||
465 | d_fnend(4, dev, "(i2400m %p, l3l4_hdr %p, size %zu, %s) = void\n", | 470 | d_fnend(4, dev, "(i2400m %p, l3l4_hdr %p, size %zu, %s) = void\n", |
466 | i2400m, l3l4_hdr, size, tag); | 471 | i2400m, l3l4_hdr, size, tag); |
467 | } | 472 | } |
@@ -721,6 +726,8 @@ struct sk_buff *i2400m_msg_to_dev(struct i2400m *i2400m, | |||
721 | ack_timeout = HZ; | 726 | ack_timeout = HZ; |
722 | }; | 727 | }; |
723 | 728 | ||
729 | if (unlikely(i2400m->trace_msg_from_user)) | ||
730 | wimax_msg(&i2400m->wimax_dev, "echo", buf, buf_len, GFP_KERNEL); | ||
724 | /* The RX path in rx.c will put any response for this message | 731 | /* The RX path in rx.c will put any response for this message |
725 | * in i2400m->ack_skb and wake us up. If we cancel the wait, | 732 | * in i2400m->ack_skb and wake us up. If we cancel the wait, |
726 | * we need to change the value of i2400m->ack_skb to something | 733 | * we need to change the value of i2400m->ack_skb to something |
@@ -755,6 +762,9 @@ struct sk_buff *i2400m_msg_to_dev(struct i2400m *i2400m, | |||
755 | ack_l3l4_hdr = wimax_msg_data_len(ack_skb, &ack_len); | 762 | ack_l3l4_hdr = wimax_msg_data_len(ack_skb, &ack_len); |
756 | 763 | ||
757 | /* Check the ack and deliver it if it is ok */ | 764 | /* Check the ack and deliver it if it is ok */ |
765 | if (unlikely(i2400m->trace_msg_from_user)) | ||
766 | wimax_msg(&i2400m->wimax_dev, "echo", | ||
767 | ack_l3l4_hdr, ack_len, GFP_KERNEL); | ||
758 | result = i2400m_msg_size_check(i2400m, ack_l3l4_hdr, ack_len); | 768 | result = i2400m_msg_size_check(i2400m, ack_l3l4_hdr, ack_len); |
759 | if (result < 0) { | 769 | if (result < 0) { |
760 | dev_err(dev, "HW BUG? reply to message 0x%04x: %d\n", | 770 | dev_err(dev, "HW BUG? reply to message 0x%04x: %d\n", |
diff --git a/drivers/net/wimax/i2400m/driver.c b/drivers/net/wimax/i2400m/driver.c index 07a54bad237b..ef16c573bb22 100644 --- a/drivers/net/wimax/i2400m/driver.c +++ b/drivers/net/wimax/i2400m/driver.c | |||
@@ -62,6 +62,7 @@ | |||
62 | * unregister_netdev() | 62 | * unregister_netdev() |
63 | */ | 63 | */ |
64 | #include "i2400m.h" | 64 | #include "i2400m.h" |
65 | #include <linux/etherdevice.h> | ||
65 | #include <linux/wimax/i2400m.h> | 66 | #include <linux/wimax/i2400m.h> |
66 | #include <linux/module.h> | 67 | #include <linux/module.h> |
67 | #include <linux/moduleparam.h> | 68 | #include <linux/moduleparam.h> |
@@ -234,9 +235,6 @@ int i2400m_op_msg_from_user(struct wimax_dev *wimax_dev, | |||
234 | result = PTR_ERR(ack_skb); | 235 | result = PTR_ERR(ack_skb); |
235 | if (IS_ERR(ack_skb)) | 236 | if (IS_ERR(ack_skb)) |
236 | goto error_msg_to_dev; | 237 | goto error_msg_to_dev; |
237 | if (unlikely(i2400m->trace_msg_from_user)) | ||
238 | wimax_msg(&i2400m->wimax_dev, "trace", | ||
239 | msg_buf, msg_len, GFP_KERNEL); | ||
240 | result = wimax_msg_send(&i2400m->wimax_dev, ack_skb); | 238 | result = wimax_msg_send(&i2400m->wimax_dev, ack_skb); |
241 | error_msg_to_dev: | 239 | error_msg_to_dev: |
242 | d_fnend(4, dev, "(wimax_dev %p [i2400m %p] msg_buf %p msg_len %zu " | 240 | d_fnend(4, dev, "(wimax_dev %p [i2400m %p] msg_buf %p msg_len %zu " |
@@ -650,6 +648,7 @@ int i2400m_setup(struct i2400m *i2400m, enum i2400m_bri bm_flags) | |||
650 | result = i2400m_read_mac_addr(i2400m); | 648 | result = i2400m_read_mac_addr(i2400m); |
651 | if (result < 0) | 649 | if (result < 0) |
652 | goto error_read_mac_addr; | 650 | goto error_read_mac_addr; |
651 | random_ether_addr(i2400m->src_mac_addr); | ||
653 | 652 | ||
654 | result = register_netdev(net_dev); /* Okey dokey, bring it up */ | 653 | result = register_netdev(net_dev); /* Okey dokey, bring it up */ |
655 | if (result < 0) { | 654 | if (result < 0) { |
diff --git a/drivers/net/wimax/i2400m/i2400m.h b/drivers/net/wimax/i2400m/i2400m.h index 3ae2df38b59a..434ba310c2fe 100644 --- a/drivers/net/wimax/i2400m/i2400m.h +++ b/drivers/net/wimax/i2400m/i2400m.h | |||
@@ -323,6 +323,10 @@ struct i2400m_roq; | |||
323 | * delivered. Then the driver can release them to the host. See | 323 | * delivered. Then the driver can release them to the host. See |
324 | * drivers/net/i2400m/rx.c for details. | 324 | * drivers/net/i2400m/rx.c for details. |
325 | * | 325 | * |
326 | * @src_mac_addr: MAC address used to make ethernet packets be coming | ||
327 | * from. This is generated at i2400m_setup() time and used during | ||
328 | * the life cycle of the instance. See i2400m_fake_eth_header(). | ||
329 | * | ||
326 | * @init_mutex: Mutex used for serializing the device bringup | 330 | * @init_mutex: Mutex used for serializing the device bringup |
327 | * sequence; this way if the device reboots in the middle, we | 331 | * sequence; this way if the device reboots in the middle, we |
328 | * don't try to do a bringup again while we are tearing down the | 332 | * don't try to do a bringup again while we are tearing down the |
@@ -421,6 +425,7 @@ struct i2400m { | |||
421 | unsigned rx_pl_num, rx_pl_max, rx_pl_min, | 425 | unsigned rx_pl_num, rx_pl_max, rx_pl_min, |
422 | rx_num, rx_size_acc, rx_size_min, rx_size_max; | 426 | rx_num, rx_size_acc, rx_size_min, rx_size_max; |
423 | struct i2400m_roq *rx_roq; /* not under rx_lock! */ | 427 | struct i2400m_roq *rx_roq; /* not under rx_lock! */ |
428 | u8 src_mac_addr[ETH_HLEN]; | ||
424 | 429 | ||
425 | struct mutex msg_mutex; /* serialize command execution */ | 430 | struct mutex msg_mutex; /* serialize command execution */ |
426 | struct completion msg_completion; | 431 | struct completion msg_completion; |
diff --git a/drivers/net/wimax/i2400m/netdev.c b/drivers/net/wimax/i2400m/netdev.c index 6b1fe7a81f25..9653f478b382 100644 --- a/drivers/net/wimax/i2400m/netdev.c +++ b/drivers/net/wimax/i2400m/netdev.c | |||
@@ -404,10 +404,12 @@ static | |||
404 | void i2400m_rx_fake_eth_header(struct net_device *net_dev, | 404 | void i2400m_rx_fake_eth_header(struct net_device *net_dev, |
405 | void *_eth_hdr, __be16 protocol) | 405 | void *_eth_hdr, __be16 protocol) |
406 | { | 406 | { |
407 | struct i2400m *i2400m = net_dev_to_i2400m(net_dev); | ||
407 | struct ethhdr *eth_hdr = _eth_hdr; | 408 | struct ethhdr *eth_hdr = _eth_hdr; |
408 | 409 | ||
409 | memcpy(eth_hdr->h_dest, net_dev->dev_addr, sizeof(eth_hdr->h_dest)); | 410 | memcpy(eth_hdr->h_dest, net_dev->dev_addr, sizeof(eth_hdr->h_dest)); |
410 | memset(eth_hdr->h_source, 0, sizeof(eth_hdr->h_dest)); | 411 | memcpy(eth_hdr->h_source, i2400m->src_mac_addr, |
412 | sizeof(eth_hdr->h_source)); | ||
411 | eth_hdr->h_proto = protocol; | 413 | eth_hdr->h_proto = protocol; |
412 | } | 414 | } |
413 | 415 | ||
diff --git a/drivers/net/wimax/i2400m/rx.c b/drivers/net/wimax/i2400m/rx.c index f9fc38902322..7643850a6fb8 100644 --- a/drivers/net/wimax/i2400m/rx.c +++ b/drivers/net/wimax/i2400m/rx.c | |||
@@ -177,7 +177,8 @@ void i2400m_report_hook_work(struct work_struct *ws) | |||
177 | struct i2400m_work *iw = | 177 | struct i2400m_work *iw = |
178 | container_of(ws, struct i2400m_work, ws); | 178 | container_of(ws, struct i2400m_work, ws); |
179 | struct i2400m_report_hook_args *args = (void *) iw->pl; | 179 | struct i2400m_report_hook_args *args = (void *) iw->pl; |
180 | i2400m_report_hook(iw->i2400m, args->l3l4_hdr, args->size); | 180 | if (iw->i2400m->ready) |
181 | i2400m_report_hook(iw->i2400m, args->l3l4_hdr, args->size); | ||
181 | kfree_skb(args->skb_rx); | 182 | kfree_skb(args->skb_rx); |
182 | i2400m_put(iw->i2400m); | 183 | i2400m_put(iw->i2400m); |
183 | kfree(iw); | 184 | kfree(iw); |
@@ -309,6 +310,9 @@ void i2400m_rx_ctl(struct i2400m *i2400m, struct sk_buff *skb_rx, | |||
309 | skb_get(skb_rx); | 310 | skb_get(skb_rx); |
310 | i2400m_queue_work(i2400m, i2400m_report_hook_work, | 311 | i2400m_queue_work(i2400m, i2400m_report_hook_work, |
311 | GFP_KERNEL, &args, sizeof(args)); | 312 | GFP_KERNEL, &args, sizeof(args)); |
313 | if (unlikely(i2400m->trace_msg_from_user)) | ||
314 | wimax_msg(&i2400m->wimax_dev, "echo", | ||
315 | l3l4_hdr, size, GFP_KERNEL); | ||
312 | result = wimax_msg(&i2400m->wimax_dev, NULL, l3l4_hdr, size, | 316 | result = wimax_msg(&i2400m->wimax_dev, NULL, l3l4_hdr, size, |
313 | GFP_KERNEL); | 317 | GFP_KERNEL); |
314 | if (result < 0) | 318 | if (result < 0) |
diff --git a/drivers/net/wimax/i2400m/sdio.c b/drivers/net/wimax/i2400m/sdio.c index 5ac5e76701cd..777c981676fc 100644 --- a/drivers/net/wimax/i2400m/sdio.c +++ b/drivers/net/wimax/i2400m/sdio.c | |||
@@ -409,19 +409,19 @@ int i2400ms_probe(struct sdio_func *func, | |||
409 | i2400m->bus_fw_names = i2400ms_bus_fw_names; | 409 | i2400m->bus_fw_names = i2400ms_bus_fw_names; |
410 | i2400m->bus_bm_mac_addr_impaired = 1; | 410 | i2400m->bus_bm_mac_addr_impaired = 1; |
411 | 411 | ||
412 | result = i2400ms_enable_function(i2400ms->func); | ||
413 | if (result < 0) { | ||
414 | dev_err(dev, "Cannot enable SDIO function: %d\n", result); | ||
415 | goto error_func_enable; | ||
416 | } | ||
417 | |||
418 | sdio_claim_host(func); | 412 | sdio_claim_host(func); |
419 | result = sdio_set_block_size(func, I2400MS_BLK_SIZE); | 413 | result = sdio_set_block_size(func, I2400MS_BLK_SIZE); |
414 | sdio_release_host(func); | ||
420 | if (result < 0) { | 415 | if (result < 0) { |
421 | dev_err(dev, "Failed to set block size: %d\n", result); | 416 | dev_err(dev, "Failed to set block size: %d\n", result); |
422 | goto error_set_blk_size; | 417 | goto error_set_blk_size; |
423 | } | 418 | } |
424 | sdio_release_host(func); | 419 | |
420 | result = i2400ms_enable_function(i2400ms->func); | ||
421 | if (result < 0) { | ||
422 | dev_err(dev, "Cannot enable SDIO function: %d\n", result); | ||
423 | goto error_func_enable; | ||
424 | } | ||
425 | 425 | ||
426 | result = i2400m_setup(i2400m, I2400M_BRI_NO_REBOOT); | 426 | result = i2400m_setup(i2400m, I2400M_BRI_NO_REBOOT); |
427 | if (result < 0) { | 427 | if (result < 0) { |
@@ -440,12 +440,12 @@ int i2400ms_probe(struct sdio_func *func, | |||
440 | error_debugfs_add: | 440 | error_debugfs_add: |
441 | i2400m_release(i2400m); | 441 | i2400m_release(i2400m); |
442 | error_setup: | 442 | error_setup: |
443 | sdio_set_drvdata(func, NULL); | ||
444 | sdio_claim_host(func); | 443 | sdio_claim_host(func); |
445 | error_set_blk_size: | ||
446 | sdio_disable_func(func); | 444 | sdio_disable_func(func); |
447 | sdio_release_host(func); | 445 | sdio_release_host(func); |
448 | error_func_enable: | 446 | error_func_enable: |
447 | error_set_blk_size: | ||
448 | sdio_set_drvdata(func, NULL); | ||
449 | free_netdev(net_dev); | 449 | free_netdev(net_dev); |
450 | error_alloc_netdev: | 450 | error_alloc_netdev: |
451 | return result; | 451 | return result; |
diff --git a/drivers/net/wimax/i2400m/usb.c b/drivers/net/wimax/i2400m/usb.c index ca4151a9e222..17851321b7fd 100644 --- a/drivers/net/wimax/i2400m/usb.c +++ b/drivers/net/wimax/i2400m/usb.c | |||
@@ -505,27 +505,52 @@ int i2400mu_suspend(struct usb_interface *iface, pm_message_t pm_msg) | |||
505 | #ifdef CONFIG_PM | 505 | #ifdef CONFIG_PM |
506 | struct usb_device *usb_dev = i2400mu->usb_dev; | 506 | struct usb_device *usb_dev = i2400mu->usb_dev; |
507 | #endif | 507 | #endif |
508 | unsigned is_autosuspend = 0; | ||
508 | struct i2400m *i2400m = &i2400mu->i2400m; | 509 | struct i2400m *i2400m = &i2400mu->i2400m; |
509 | 510 | ||
511 | #ifdef CONFIG_PM | ||
512 | if (usb_dev->auto_pm > 0) | ||
513 | is_autosuspend = 1; | ||
514 | #endif | ||
515 | |||
510 | d_fnstart(3, dev, "(iface %p pm_msg %u)\n", iface, pm_msg.event); | 516 | d_fnstart(3, dev, "(iface %p pm_msg %u)\n", iface, pm_msg.event); |
511 | if (i2400m->updown == 0) | 517 | if (i2400m->updown == 0) |
512 | goto no_firmware; | 518 | goto no_firmware; |
513 | d_printf(1, dev, "fw up, requesting standby\n"); | 519 | if (i2400m->state == I2400M_SS_DATA_PATH_CONNECTED && is_autosuspend) { |
520 | /* ugh -- the device is connected and this suspend | ||
521 | * request is an autosuspend one (not a system standby | ||
522 | * / hibernate). | ||
523 | * | ||
524 | * The only way the device can go to standby is if the | ||
525 | * link with the base station is in IDLE mode; that | ||
526 | * were the case, we'd be in status | ||
527 | * I2400M_SS_CONNECTED_IDLE. But we are not. | ||
528 | * | ||
529 | * If we *tell* him to go power save now, it'll reset | ||
530 | * as a precautionary measure, so if this is an | ||
531 | * autosuspend thing, say no and it'll come back | ||
532 | * later, when the link is IDLE | ||
533 | */ | ||
534 | result = -EBADF; | ||
535 | d_printf(1, dev, "fw up, link up, not-idle, autosuspend: " | ||
536 | "not entering powersave\n"); | ||
537 | goto error_not_now; | ||
538 | } | ||
539 | d_printf(1, dev, "fw up: entering powersave\n"); | ||
514 | atomic_dec(&i2400mu->do_autopm); | 540 | atomic_dec(&i2400mu->do_autopm); |
515 | result = i2400m_cmd_enter_powersave(i2400m); | 541 | result = i2400m_cmd_enter_powersave(i2400m); |
516 | atomic_inc(&i2400mu->do_autopm); | 542 | atomic_inc(&i2400mu->do_autopm); |
517 | #ifdef CONFIG_PM | 543 | if (result < 0 && !is_autosuspend) { |
518 | if (result < 0 && usb_dev->auto_pm == 0) { | ||
519 | /* System suspend, can't fail */ | 544 | /* System suspend, can't fail */ |
520 | dev_err(dev, "failed to suspend, will reset on resume\n"); | 545 | dev_err(dev, "failed to suspend, will reset on resume\n"); |
521 | result = 0; | 546 | result = 0; |
522 | } | 547 | } |
523 | #endif | ||
524 | if (result < 0) | 548 | if (result < 0) |
525 | goto error_enter_powersave; | 549 | goto error_enter_powersave; |
526 | i2400mu_notification_release(i2400mu); | 550 | i2400mu_notification_release(i2400mu); |
527 | d_printf(1, dev, "fw up, got standby\n"); | 551 | d_printf(1, dev, "powersave requested\n"); |
528 | error_enter_powersave: | 552 | error_enter_powersave: |
553 | error_not_now: | ||
529 | no_firmware: | 554 | no_firmware: |
530 | d_fnend(3, dev, "(iface %p pm_msg %u) = %d\n", | 555 | d_fnend(3, dev, "(iface %p pm_msg %u) = %d\n", |
531 | iface, pm_msg.event, result); | 556 | iface, pm_msg.event, result); |