diff options
author | Vasanthakumar Thiagarajan <vasanth@atheros.com> | 2008-09-10 09:19:27 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2008-09-15 16:48:19 -0400 |
commit | 8feceb67929bd23bfca58d5f49df93d7fc315bb1 (patch) | |
tree | fe6449d4a96141d520bcbbf6637ab04013044585 /drivers | |
parent | f8e77caefea8940ee1fb09c9ebb0107ca2eadb72 (diff) |
ath9k: Re-order functions in main.c
Some of the functions in main.c are re-ordered in such
a way that all local functions are defined before mac80211
and pci callbacks.
Signed-off-by: Vasanthakumar Thiagarajan <vasanth@atheros.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/net/wireless/ath9k/main.c | 938 |
1 files changed, 469 insertions, 469 deletions
diff --git a/drivers/net/wireless/ath9k/main.c b/drivers/net/wireless/ath9k/main.c index 57d7cc87cb0f..07e5b5d877b6 100644 --- a/drivers/net/wireless/ath9k/main.c +++ b/drivers/net/wireless/ath9k/main.c | |||
@@ -325,6 +325,475 @@ static u8 parse_mpdudensity(u8 mpdudensity) | |||
325 | } | 325 | } |
326 | } | 326 | } |
327 | 327 | ||
328 | static void ath9k_ht_conf(struct ath_softc *sc, | ||
329 | struct ieee80211_bss_conf *bss_conf) | ||
330 | { | ||
331 | #define IEEE80211_HT_CAP_40MHZ_INTOLERANT BIT(14) | ||
332 | struct ath_ht_info *ht_info = &sc->sc_ht_info; | ||
333 | |||
334 | if (bss_conf->assoc_ht) { | ||
335 | ht_info->ext_chan_offset = | ||
336 | bss_conf->ht_bss_conf->bss_cap & | ||
337 | IEEE80211_HT_IE_CHA_SEC_OFFSET; | ||
338 | |||
339 | if (!(bss_conf->ht_conf->cap & | ||
340 | IEEE80211_HT_CAP_40MHZ_INTOLERANT) && | ||
341 | (bss_conf->ht_bss_conf->bss_cap & | ||
342 | IEEE80211_HT_IE_CHA_WIDTH)) | ||
343 | ht_info->tx_chan_width = ATH9K_HT_MACMODE_2040; | ||
344 | else | ||
345 | ht_info->tx_chan_width = ATH9K_HT_MACMODE_20; | ||
346 | |||
347 | ath9k_hw_set11nmac2040(sc->sc_ah, ht_info->tx_chan_width); | ||
348 | ht_info->maxampdu = 1 << (IEEE80211_HTCAP_MAXRXAMPDU_FACTOR + | ||
349 | bss_conf->ht_conf->ampdu_factor); | ||
350 | ht_info->mpdudensity = | ||
351 | parse_mpdudensity(bss_conf->ht_conf->ampdu_density); | ||
352 | |||
353 | } | ||
354 | |||
355 | #undef IEEE80211_HT_CAP_40MHZ_INTOLERANT | ||
356 | } | ||
357 | |||
358 | static void ath9k_bss_assoc_info(struct ath_softc *sc, | ||
359 | struct ieee80211_bss_conf *bss_conf) | ||
360 | { | ||
361 | struct ieee80211_hw *hw = sc->hw; | ||
362 | struct ieee80211_channel *curchan = hw->conf.channel; | ||
363 | struct ath_vap *avp; | ||
364 | int pos; | ||
365 | DECLARE_MAC_BUF(mac); | ||
366 | |||
367 | if (bss_conf->assoc) { | ||
368 | DPRINTF(sc, ATH_DBG_CONFIG, "%s: Bss Info ASSOC %d\n", | ||
369 | __func__, | ||
370 | bss_conf->aid); | ||
371 | |||
372 | avp = sc->sc_vaps[0]; | ||
373 | if (avp == NULL) { | ||
374 | DPRINTF(sc, ATH_DBG_FATAL, "%s: Invalid interface\n", | ||
375 | __func__); | ||
376 | return; | ||
377 | } | ||
378 | |||
379 | /* New association, store aid */ | ||
380 | if (avp->av_opmode == ATH9K_M_STA) { | ||
381 | sc->sc_curaid = bss_conf->aid; | ||
382 | ath9k_hw_write_associd(sc->sc_ah, sc->sc_curbssid, | ||
383 | sc->sc_curaid); | ||
384 | } | ||
385 | |||
386 | /* Configure the beacon */ | ||
387 | ath_beacon_config(sc, 0); | ||
388 | sc->sc_flags |= SC_OP_BEACONS; | ||
389 | |||
390 | /* Reset rssi stats */ | ||
391 | sc->sc_halstats.ns_avgbrssi = ATH_RSSI_DUMMY_MARKER; | ||
392 | sc->sc_halstats.ns_avgrssi = ATH_RSSI_DUMMY_MARKER; | ||
393 | sc->sc_halstats.ns_avgtxrssi = ATH_RSSI_DUMMY_MARKER; | ||
394 | sc->sc_halstats.ns_avgtxrate = ATH_RATE_DUMMY_MARKER; | ||
395 | |||
396 | /* Update chainmask */ | ||
397 | ath_update_chainmask(sc, bss_conf->assoc_ht); | ||
398 | |||
399 | DPRINTF(sc, ATH_DBG_CONFIG, | ||
400 | "%s: bssid %s aid 0x%x\n", | ||
401 | __func__, | ||
402 | print_mac(mac, sc->sc_curbssid), sc->sc_curaid); | ||
403 | |||
404 | DPRINTF(sc, ATH_DBG_CONFIG, "%s: Set channel: %d MHz\n", | ||
405 | __func__, | ||
406 | curchan->center_freq); | ||
407 | |||
408 | pos = ath_get_channel(sc, curchan); | ||
409 | if (pos == -1) { | ||
410 | DPRINTF(sc, ATH_DBG_FATAL, | ||
411 | "%s: Invalid channel\n", __func__); | ||
412 | return; | ||
413 | } | ||
414 | |||
415 | if (hw->conf.ht_conf.ht_supported) | ||
416 | sc->sc_ah->ah_channels[pos].chanmode = | ||
417 | ath_get_extchanmode(sc, curchan); | ||
418 | else | ||
419 | sc->sc_ah->ah_channels[pos].chanmode = | ||
420 | (curchan->band == IEEE80211_BAND_2GHZ) ? | ||
421 | CHANNEL_G : CHANNEL_A; | ||
422 | |||
423 | /* set h/w channel */ | ||
424 | if (ath_set_channel(sc, &sc->sc_ah->ah_channels[pos]) < 0) | ||
425 | DPRINTF(sc, ATH_DBG_FATAL, | ||
426 | "%s: Unable to set channel\n", | ||
427 | __func__); | ||
428 | |||
429 | ath_rate_newstate(sc, avp); | ||
430 | /* Update ratectrl about the new state */ | ||
431 | ath_rc_node_update(hw, avp->rc_node); | ||
432 | } else { | ||
433 | DPRINTF(sc, ATH_DBG_CONFIG, | ||
434 | "%s: Bss Info DISSOC\n", __func__); | ||
435 | sc->sc_curaid = 0; | ||
436 | } | ||
437 | } | ||
438 | |||
439 | void ath_get_beaconconfig(struct ath_softc *sc, | ||
440 | int if_id, | ||
441 | struct ath_beacon_config *conf) | ||
442 | { | ||
443 | struct ieee80211_hw *hw = sc->hw; | ||
444 | |||
445 | /* fill in beacon config data */ | ||
446 | |||
447 | conf->beacon_interval = hw->conf.beacon_int; | ||
448 | conf->listen_interval = 100; | ||
449 | conf->dtim_count = 1; | ||
450 | conf->bmiss_timeout = ATH_DEFAULT_BMISS_LIMIT * conf->listen_interval; | ||
451 | } | ||
452 | |||
453 | void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb, | ||
454 | struct ath_xmit_status *tx_status, struct ath_node *an) | ||
455 | { | ||
456 | struct ieee80211_hw *hw = sc->hw; | ||
457 | struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); | ||
458 | |||
459 | DPRINTF(sc, ATH_DBG_XMIT, | ||
460 | "%s: TX complete: skb: %p\n", __func__, skb); | ||
461 | |||
462 | if (tx_info->flags & IEEE80211_TX_CTL_NO_ACK || | ||
463 | tx_info->flags & IEEE80211_TX_STAT_TX_FILTERED) { | ||
464 | /* free driver's private data area of tx_info */ | ||
465 | if (tx_info->driver_data[0] != NULL) | ||
466 | kfree(tx_info->driver_data[0]); | ||
467 | tx_info->driver_data[0] = NULL; | ||
468 | } | ||
469 | |||
470 | if (tx_status->flags & ATH_TX_BAR) { | ||
471 | tx_info->flags |= IEEE80211_TX_STAT_AMPDU_NO_BACK; | ||
472 | tx_status->flags &= ~ATH_TX_BAR; | ||
473 | } | ||
474 | |||
475 | if (tx_status->flags & (ATH_TX_ERROR | ATH_TX_XRETRY)) { | ||
476 | if (!(tx_info->flags & IEEE80211_TX_CTL_NO_ACK)) { | ||
477 | /* Frame was not ACKed, but an ACK was expected */ | ||
478 | tx_info->status.excessive_retries = 1; | ||
479 | } | ||
480 | } else { | ||
481 | /* Frame was ACKed */ | ||
482 | tx_info->flags |= IEEE80211_TX_STAT_ACK; | ||
483 | } | ||
484 | |||
485 | tx_info->status.retry_count = tx_status->retries; | ||
486 | |||
487 | ieee80211_tx_status(hw, skb); | ||
488 | if (an) | ||
489 | ath_node_put(sc, an, ATH9K_BH_STATUS_CHANGE); | ||
490 | } | ||
491 | |||
492 | int _ath_rx_indicate(struct ath_softc *sc, | ||
493 | struct sk_buff *skb, | ||
494 | struct ath_recv_status *status, | ||
495 | u16 keyix) | ||
496 | { | ||
497 | struct ieee80211_hw *hw = sc->hw; | ||
498 | struct ath_node *an = NULL; | ||
499 | struct ieee80211_rx_status rx_status; | ||
500 | struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; | ||
501 | int hdrlen = ieee80211_get_hdrlen_from_skb(skb); | ||
502 | int padsize; | ||
503 | enum ATH_RX_TYPE st; | ||
504 | |||
505 | /* see if any padding is done by the hw and remove it */ | ||
506 | if (hdrlen & 3) { | ||
507 | padsize = hdrlen % 4; | ||
508 | memmove(skb->data + padsize, skb->data, hdrlen); | ||
509 | skb_pull(skb, padsize); | ||
510 | } | ||
511 | |||
512 | /* Prepare rx status */ | ||
513 | ath9k_rx_prepare(sc, skb, status, &rx_status); | ||
514 | |||
515 | if (!(keyix == ATH9K_RXKEYIX_INVALID) && | ||
516 | !(status->flags & ATH_RX_DECRYPT_ERROR)) { | ||
517 | rx_status.flag |= RX_FLAG_DECRYPTED; | ||
518 | } else if ((le16_to_cpu(hdr->frame_control) & IEEE80211_FCTL_PROTECTED) | ||
519 | && !(status->flags & ATH_RX_DECRYPT_ERROR) | ||
520 | && skb->len >= hdrlen + 4) { | ||
521 | keyix = skb->data[hdrlen + 3] >> 6; | ||
522 | |||
523 | if (test_bit(keyix, sc->sc_keymap)) | ||
524 | rx_status.flag |= RX_FLAG_DECRYPTED; | ||
525 | } | ||
526 | |||
527 | spin_lock_bh(&sc->node_lock); | ||
528 | an = ath_node_find(sc, hdr->addr2); | ||
529 | spin_unlock_bh(&sc->node_lock); | ||
530 | |||
531 | if (an) { | ||
532 | ath_rx_input(sc, an, | ||
533 | hw->conf.ht_conf.ht_supported, | ||
534 | skb, status, &st); | ||
535 | } | ||
536 | if (!an || (st != ATH_RX_CONSUMED)) | ||
537 | __ieee80211_rx(hw, skb, &rx_status); | ||
538 | |||
539 | return 0; | ||
540 | } | ||
541 | |||
542 | int ath_rx_subframe(struct ath_node *an, | ||
543 | struct sk_buff *skb, | ||
544 | struct ath_recv_status *status) | ||
545 | { | ||
546 | struct ath_softc *sc = an->an_sc; | ||
547 | struct ieee80211_hw *hw = sc->hw; | ||
548 | struct ieee80211_rx_status rx_status; | ||
549 | |||
550 | /* Prepare rx status */ | ||
551 | ath9k_rx_prepare(sc, skb, status, &rx_status); | ||
552 | if (!(status->flags & ATH_RX_DECRYPT_ERROR)) | ||
553 | rx_status.flag |= RX_FLAG_DECRYPTED; | ||
554 | |||
555 | __ieee80211_rx(hw, skb, &rx_status); | ||
556 | |||
557 | return 0; | ||
558 | } | ||
559 | |||
560 | /********************************/ | ||
561 | /* LED functions */ | ||
562 | /********************************/ | ||
563 | |||
564 | static void ath_led_brightness(struct led_classdev *led_cdev, | ||
565 | enum led_brightness brightness) | ||
566 | { | ||
567 | struct ath_led *led = container_of(led_cdev, struct ath_led, led_cdev); | ||
568 | struct ath_softc *sc = led->sc; | ||
569 | |||
570 | switch (brightness) { | ||
571 | case LED_OFF: | ||
572 | if (led->led_type == ATH_LED_ASSOC || | ||
573 | led->led_type == ATH_LED_RADIO) | ||
574 | sc->sc_flags &= ~SC_OP_LED_ASSOCIATED; | ||
575 | ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN, | ||
576 | (led->led_type == ATH_LED_RADIO) ? 1 : | ||
577 | !!(sc->sc_flags & SC_OP_LED_ASSOCIATED)); | ||
578 | break; | ||
579 | case LED_FULL: | ||
580 | if (led->led_type == ATH_LED_ASSOC) | ||
581 | sc->sc_flags |= SC_OP_LED_ASSOCIATED; | ||
582 | ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN, 0); | ||
583 | break; | ||
584 | default: | ||
585 | break; | ||
586 | } | ||
587 | } | ||
588 | |||
589 | static int ath_register_led(struct ath_softc *sc, struct ath_led *led, | ||
590 | char *trigger) | ||
591 | { | ||
592 | int ret; | ||
593 | |||
594 | led->sc = sc; | ||
595 | led->led_cdev.name = led->name; | ||
596 | led->led_cdev.default_trigger = trigger; | ||
597 | led->led_cdev.brightness_set = ath_led_brightness; | ||
598 | |||
599 | ret = led_classdev_register(wiphy_dev(sc->hw->wiphy), &led->led_cdev); | ||
600 | if (ret) | ||
601 | DPRINTF(sc, ATH_DBG_FATAL, | ||
602 | "Failed to register led:%s", led->name); | ||
603 | else | ||
604 | led->registered = 1; | ||
605 | return ret; | ||
606 | } | ||
607 | |||
608 | static void ath_unregister_led(struct ath_led *led) | ||
609 | { | ||
610 | if (led->registered) { | ||
611 | led_classdev_unregister(&led->led_cdev); | ||
612 | led->registered = 0; | ||
613 | } | ||
614 | } | ||
615 | |||
616 | static void ath_deinit_leds(struct ath_softc *sc) | ||
617 | { | ||
618 | ath_unregister_led(&sc->assoc_led); | ||
619 | sc->sc_flags &= ~SC_OP_LED_ASSOCIATED; | ||
620 | ath_unregister_led(&sc->tx_led); | ||
621 | ath_unregister_led(&sc->rx_led); | ||
622 | ath_unregister_led(&sc->radio_led); | ||
623 | ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN, 1); | ||
624 | } | ||
625 | |||
626 | static void ath_init_leds(struct ath_softc *sc) | ||
627 | { | ||
628 | char *trigger; | ||
629 | int ret; | ||
630 | |||
631 | /* Configure gpio 1 for output */ | ||
632 | ath9k_hw_cfg_output(sc->sc_ah, ATH_LED_PIN, | ||
633 | AR_GPIO_OUTPUT_MUX_AS_OUTPUT); | ||
634 | /* LED off, active low */ | ||
635 | ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN, 1); | ||
636 | |||
637 | trigger = ieee80211_get_radio_led_name(sc->hw); | ||
638 | snprintf(sc->radio_led.name, sizeof(sc->radio_led.name), | ||
639 | "ath9k-%s:radio", wiphy_name(sc->hw->wiphy)); | ||
640 | ret = ath_register_led(sc, &sc->radio_led, trigger); | ||
641 | sc->radio_led.led_type = ATH_LED_RADIO; | ||
642 | if (ret) | ||
643 | goto fail; | ||
644 | |||
645 | trigger = ieee80211_get_assoc_led_name(sc->hw); | ||
646 | snprintf(sc->assoc_led.name, sizeof(sc->assoc_led.name), | ||
647 | "ath9k-%s:assoc", wiphy_name(sc->hw->wiphy)); | ||
648 | ret = ath_register_led(sc, &sc->assoc_led, trigger); | ||
649 | sc->assoc_led.led_type = ATH_LED_ASSOC; | ||
650 | if (ret) | ||
651 | goto fail; | ||
652 | |||
653 | trigger = ieee80211_get_tx_led_name(sc->hw); | ||
654 | snprintf(sc->tx_led.name, sizeof(sc->tx_led.name), | ||
655 | "ath9k-%s:tx", wiphy_name(sc->hw->wiphy)); | ||
656 | ret = ath_register_led(sc, &sc->tx_led, trigger); | ||
657 | sc->tx_led.led_type = ATH_LED_TX; | ||
658 | if (ret) | ||
659 | goto fail; | ||
660 | |||
661 | trigger = ieee80211_get_rx_led_name(sc->hw); | ||
662 | snprintf(sc->rx_led.name, sizeof(sc->rx_led.name), | ||
663 | "ath9k-%s:rx", wiphy_name(sc->hw->wiphy)); | ||
664 | ret = ath_register_led(sc, &sc->rx_led, trigger); | ||
665 | sc->rx_led.led_type = ATH_LED_RX; | ||
666 | if (ret) | ||
667 | goto fail; | ||
668 | |||
669 | return; | ||
670 | |||
671 | fail: | ||
672 | ath_deinit_leds(sc); | ||
673 | } | ||
674 | |||
675 | static int ath_detach(struct ath_softc *sc) | ||
676 | { | ||
677 | struct ieee80211_hw *hw = sc->hw; | ||
678 | |||
679 | DPRINTF(sc, ATH_DBG_CONFIG, "%s: Detach ATH hw\n", __func__); | ||
680 | |||
681 | /* Deinit LED control */ | ||
682 | ath_deinit_leds(sc); | ||
683 | |||
684 | /* Unregister hw */ | ||
685 | |||
686 | ieee80211_unregister_hw(hw); | ||
687 | |||
688 | /* unregister Rate control */ | ||
689 | ath_rate_control_unregister(); | ||
690 | |||
691 | /* tx/rx cleanup */ | ||
692 | |||
693 | ath_rx_cleanup(sc); | ||
694 | ath_tx_cleanup(sc); | ||
695 | |||
696 | /* Deinit */ | ||
697 | |||
698 | ath_deinit(sc); | ||
699 | |||
700 | return 0; | ||
701 | } | ||
702 | |||
703 | static int ath_attach(u16 devid, | ||
704 | struct ath_softc *sc) | ||
705 | { | ||
706 | struct ieee80211_hw *hw = sc->hw; | ||
707 | int error = 0; | ||
708 | |||
709 | DPRINTF(sc, ATH_DBG_CONFIG, "%s: Attach ATH hw\n", __func__); | ||
710 | |||
711 | error = ath_init(devid, sc); | ||
712 | if (error != 0) | ||
713 | return error; | ||
714 | |||
715 | /* Init nodes */ | ||
716 | |||
717 | INIT_LIST_HEAD(&sc->node_list); | ||
718 | spin_lock_init(&sc->node_lock); | ||
719 | |||
720 | /* get mac address from hardware and set in mac80211 */ | ||
721 | |||
722 | SET_IEEE80211_PERM_ADDR(hw, sc->sc_myaddr); | ||
723 | |||
724 | /* setup channels and rates */ | ||
725 | |||
726 | sc->sbands[IEEE80211_BAND_2GHZ].channels = | ||
727 | sc->channels[IEEE80211_BAND_2GHZ]; | ||
728 | sc->sbands[IEEE80211_BAND_2GHZ].bitrates = | ||
729 | sc->rates[IEEE80211_BAND_2GHZ]; | ||
730 | sc->sbands[IEEE80211_BAND_2GHZ].band = IEEE80211_BAND_2GHZ; | ||
731 | |||
732 | if (sc->sc_ah->ah_caps.hw_caps & ATH9K_HW_CAP_HT) | ||
733 | /* Setup HT capabilities for 2.4Ghz*/ | ||
734 | setup_ht_cap(&sc->sbands[IEEE80211_BAND_2GHZ].ht_info); | ||
735 | |||
736 | hw->wiphy->bands[IEEE80211_BAND_2GHZ] = | ||
737 | &sc->sbands[IEEE80211_BAND_2GHZ]; | ||
738 | |||
739 | if (test_bit(ATH9K_MODE_11A, sc->sc_ah->ah_caps.wireless_modes)) { | ||
740 | sc->sbands[IEEE80211_BAND_5GHZ].channels = | ||
741 | sc->channels[IEEE80211_BAND_5GHZ]; | ||
742 | sc->sbands[IEEE80211_BAND_5GHZ].bitrates = | ||
743 | sc->rates[IEEE80211_BAND_5GHZ]; | ||
744 | sc->sbands[IEEE80211_BAND_5GHZ].band = | ||
745 | IEEE80211_BAND_5GHZ; | ||
746 | |||
747 | if (sc->sc_ah->ah_caps.hw_caps & ATH9K_HW_CAP_HT) | ||
748 | /* Setup HT capabilities for 5Ghz*/ | ||
749 | setup_ht_cap(&sc->sbands[IEEE80211_BAND_5GHZ].ht_info); | ||
750 | |||
751 | hw->wiphy->bands[IEEE80211_BAND_5GHZ] = | ||
752 | &sc->sbands[IEEE80211_BAND_5GHZ]; | ||
753 | } | ||
754 | |||
755 | /* FIXME: Have to figure out proper hw init values later */ | ||
756 | |||
757 | hw->queues = 4; | ||
758 | hw->ampdu_queues = 1; | ||
759 | |||
760 | /* Register rate control */ | ||
761 | hw->rate_control_algorithm = "ath9k_rate_control"; | ||
762 | error = ath_rate_control_register(); | ||
763 | if (error != 0) { | ||
764 | DPRINTF(sc, ATH_DBG_FATAL, | ||
765 | "%s: Unable to register rate control " | ||
766 | "algorithm:%d\n", __func__, error); | ||
767 | ath_rate_control_unregister(); | ||
768 | goto bad; | ||
769 | } | ||
770 | |||
771 | error = ieee80211_register_hw(hw); | ||
772 | if (error != 0) { | ||
773 | ath_rate_control_unregister(); | ||
774 | goto bad; | ||
775 | } | ||
776 | |||
777 | /* Initialize LED control */ | ||
778 | ath_init_leds(sc); | ||
779 | |||
780 | /* initialize tx/rx engine */ | ||
781 | |||
782 | error = ath_tx_init(sc, ATH_TXBUF); | ||
783 | if (error != 0) | ||
784 | goto detach; | ||
785 | |||
786 | error = ath_rx_init(sc, ATH_RXBUF); | ||
787 | if (error != 0) | ||
788 | goto detach; | ||
789 | |||
790 | return 0; | ||
791 | detach: | ||
792 | ath_detach(sc); | ||
793 | bad: | ||
794 | return error; | ||
795 | } | ||
796 | |||
328 | static int ath9k_start(struct ieee80211_hw *hw) | 797 | static int ath9k_start(struct ieee80211_hw *hw) |
329 | { | 798 | { |
330 | struct ath_softc *sc = hw->priv; | 799 | struct ath_softc *sc = hw->priv; |
@@ -798,117 +1267,6 @@ static int ath9k_set_key(struct ieee80211_hw *hw, | |||
798 | return ret; | 1267 | return ret; |
799 | } | 1268 | } |
800 | 1269 | ||
801 | static void ath9k_ht_conf(struct ath_softc *sc, | ||
802 | struct ieee80211_bss_conf *bss_conf) | ||
803 | { | ||
804 | #define IEEE80211_HT_CAP_40MHZ_INTOLERANT BIT(14) | ||
805 | struct ath_ht_info *ht_info = &sc->sc_ht_info; | ||
806 | |||
807 | if (bss_conf->assoc_ht) { | ||
808 | ht_info->ext_chan_offset = | ||
809 | bss_conf->ht_bss_conf->bss_cap & | ||
810 | IEEE80211_HT_IE_CHA_SEC_OFFSET; | ||
811 | |||
812 | if (!(bss_conf->ht_conf->cap & | ||
813 | IEEE80211_HT_CAP_40MHZ_INTOLERANT) && | ||
814 | (bss_conf->ht_bss_conf->bss_cap & | ||
815 | IEEE80211_HT_IE_CHA_WIDTH)) | ||
816 | ht_info->tx_chan_width = ATH9K_HT_MACMODE_2040; | ||
817 | else | ||
818 | ht_info->tx_chan_width = ATH9K_HT_MACMODE_20; | ||
819 | |||
820 | ath9k_hw_set11nmac2040(sc->sc_ah, ht_info->tx_chan_width); | ||
821 | ht_info->maxampdu = 1 << (IEEE80211_HTCAP_MAXRXAMPDU_FACTOR + | ||
822 | bss_conf->ht_conf->ampdu_factor); | ||
823 | ht_info->mpdudensity = | ||
824 | parse_mpdudensity(bss_conf->ht_conf->ampdu_density); | ||
825 | |||
826 | } | ||
827 | |||
828 | #undef IEEE80211_HT_CAP_40MHZ_INTOLERANT | ||
829 | } | ||
830 | |||
831 | static void ath9k_bss_assoc_info(struct ath_softc *sc, | ||
832 | struct ieee80211_bss_conf *bss_conf) | ||
833 | { | ||
834 | struct ieee80211_hw *hw = sc->hw; | ||
835 | struct ieee80211_channel *curchan = hw->conf.channel; | ||
836 | struct ath_vap *avp; | ||
837 | int pos; | ||
838 | DECLARE_MAC_BUF(mac); | ||
839 | |||
840 | if (bss_conf->assoc) { | ||
841 | DPRINTF(sc, ATH_DBG_CONFIG, "%s: Bss Info ASSOC %d\n", | ||
842 | __func__, | ||
843 | bss_conf->aid); | ||
844 | |||
845 | avp = sc->sc_vaps[0]; | ||
846 | if (avp == NULL) { | ||
847 | DPRINTF(sc, ATH_DBG_FATAL, "%s: Invalid interface\n", | ||
848 | __func__); | ||
849 | return; | ||
850 | } | ||
851 | |||
852 | /* New association, store aid */ | ||
853 | if (avp->av_opmode == ATH9K_M_STA) { | ||
854 | sc->sc_curaid = bss_conf->aid; | ||
855 | ath9k_hw_write_associd(sc->sc_ah, sc->sc_curbssid, | ||
856 | sc->sc_curaid); | ||
857 | } | ||
858 | |||
859 | /* Configure the beacon */ | ||
860 | ath_beacon_config(sc, 0); | ||
861 | sc->sc_flags |= SC_OP_BEACONS; | ||
862 | |||
863 | /* Reset rssi stats */ | ||
864 | sc->sc_halstats.ns_avgbrssi = ATH_RSSI_DUMMY_MARKER; | ||
865 | sc->sc_halstats.ns_avgrssi = ATH_RSSI_DUMMY_MARKER; | ||
866 | sc->sc_halstats.ns_avgtxrssi = ATH_RSSI_DUMMY_MARKER; | ||
867 | sc->sc_halstats.ns_avgtxrate = ATH_RATE_DUMMY_MARKER; | ||
868 | |||
869 | /* Update chainmask */ | ||
870 | ath_update_chainmask(sc, bss_conf->assoc_ht); | ||
871 | |||
872 | DPRINTF(sc, ATH_DBG_CONFIG, | ||
873 | "%s: bssid %s aid 0x%x\n", | ||
874 | __func__, | ||
875 | print_mac(mac, sc->sc_curbssid), sc->sc_curaid); | ||
876 | |||
877 | DPRINTF(sc, ATH_DBG_CONFIG, "%s: Set channel: %d MHz\n", | ||
878 | __func__, | ||
879 | curchan->center_freq); | ||
880 | |||
881 | pos = ath_get_channel(sc, curchan); | ||
882 | if (pos == -1) { | ||
883 | DPRINTF(sc, ATH_DBG_FATAL, | ||
884 | "%s: Invalid channel\n", __func__); | ||
885 | return; | ||
886 | } | ||
887 | |||
888 | if (hw->conf.ht_conf.ht_supported) | ||
889 | sc->sc_ah->ah_channels[pos].chanmode = | ||
890 | ath_get_extchanmode(sc, curchan); | ||
891 | else | ||
892 | sc->sc_ah->ah_channels[pos].chanmode = | ||
893 | (curchan->band == IEEE80211_BAND_2GHZ) ? | ||
894 | CHANNEL_G : CHANNEL_A; | ||
895 | |||
896 | /* set h/w channel */ | ||
897 | if (ath_set_channel(sc, &sc->sc_ah->ah_channels[pos]) < 0) | ||
898 | DPRINTF(sc, ATH_DBG_FATAL, | ||
899 | "%s: Unable to set channel\n", | ||
900 | __func__); | ||
901 | |||
902 | ath_rate_newstate(sc, avp); | ||
903 | /* Update ratectrl about the new state */ | ||
904 | ath_rc_node_update(hw, avp->rc_node); | ||
905 | } else { | ||
906 | DPRINTF(sc, ATH_DBG_CONFIG, | ||
907 | "%s: Bss Info DISSOC\n", __func__); | ||
908 | sc->sc_curaid = 0; | ||
909 | } | ||
910 | } | ||
911 | |||
912 | static void ath9k_bss_info_changed(struct ieee80211_hw *hw, | 1270 | static void ath9k_bss_info_changed(struct ieee80211_hw *hw, |
913 | struct ieee80211_vif *vif, | 1271 | struct ieee80211_vif *vif, |
914 | struct ieee80211_bss_conf *bss_conf, | 1272 | struct ieee80211_bss_conf *bss_conf, |
@@ -1048,364 +1406,6 @@ static struct ieee80211_ops ath9k_ops = { | |||
1048 | .ampdu_action = ath9k_ampdu_action | 1406 | .ampdu_action = ath9k_ampdu_action |
1049 | }; | 1407 | }; |
1050 | 1408 | ||
1051 | void ath_get_beaconconfig(struct ath_softc *sc, | ||
1052 | int if_id, | ||
1053 | struct ath_beacon_config *conf) | ||
1054 | { | ||
1055 | struct ieee80211_hw *hw = sc->hw; | ||
1056 | |||
1057 | /* fill in beacon config data */ | ||
1058 | |||
1059 | conf->beacon_interval = hw->conf.beacon_int; | ||
1060 | conf->listen_interval = 100; | ||
1061 | conf->dtim_count = 1; | ||
1062 | conf->bmiss_timeout = ATH_DEFAULT_BMISS_LIMIT * conf->listen_interval; | ||
1063 | } | ||
1064 | |||
1065 | void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb, | ||
1066 | struct ath_xmit_status *tx_status, struct ath_node *an) | ||
1067 | { | ||
1068 | struct ieee80211_hw *hw = sc->hw; | ||
1069 | struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); | ||
1070 | |||
1071 | DPRINTF(sc, ATH_DBG_XMIT, | ||
1072 | "%s: TX complete: skb: %p\n", __func__, skb); | ||
1073 | |||
1074 | if (tx_info->flags & IEEE80211_TX_CTL_NO_ACK || | ||
1075 | tx_info->flags & IEEE80211_TX_STAT_TX_FILTERED) { | ||
1076 | /* free driver's private data area of tx_info */ | ||
1077 | if (tx_info->driver_data[0] != NULL) | ||
1078 | kfree(tx_info->driver_data[0]); | ||
1079 | tx_info->driver_data[0] = NULL; | ||
1080 | } | ||
1081 | |||
1082 | if (tx_status->flags & ATH_TX_BAR) { | ||
1083 | tx_info->flags |= IEEE80211_TX_STAT_AMPDU_NO_BACK; | ||
1084 | tx_status->flags &= ~ATH_TX_BAR; | ||
1085 | } | ||
1086 | |||
1087 | if (tx_status->flags & (ATH_TX_ERROR | ATH_TX_XRETRY)) { | ||
1088 | if (!(tx_info->flags & IEEE80211_TX_CTL_NO_ACK)) { | ||
1089 | /* Frame was not ACKed, but an ACK was expected */ | ||
1090 | tx_info->status.excessive_retries = 1; | ||
1091 | } | ||
1092 | } else { | ||
1093 | /* Frame was ACKed */ | ||
1094 | tx_info->flags |= IEEE80211_TX_STAT_ACK; | ||
1095 | } | ||
1096 | |||
1097 | tx_info->status.retry_count = tx_status->retries; | ||
1098 | |||
1099 | ieee80211_tx_status(hw, skb); | ||
1100 | if (an) | ||
1101 | ath_node_put(sc, an, ATH9K_BH_STATUS_CHANGE); | ||
1102 | } | ||
1103 | |||
1104 | int _ath_rx_indicate(struct ath_softc *sc, | ||
1105 | struct sk_buff *skb, | ||
1106 | struct ath_recv_status *status, | ||
1107 | u16 keyix) | ||
1108 | { | ||
1109 | struct ieee80211_hw *hw = sc->hw; | ||
1110 | struct ath_node *an = NULL; | ||
1111 | struct ieee80211_rx_status rx_status; | ||
1112 | struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; | ||
1113 | int hdrlen = ieee80211_get_hdrlen_from_skb(skb); | ||
1114 | int padsize; | ||
1115 | enum ATH_RX_TYPE st; | ||
1116 | |||
1117 | /* see if any padding is done by the hw and remove it */ | ||
1118 | if (hdrlen & 3) { | ||
1119 | padsize = hdrlen % 4; | ||
1120 | memmove(skb->data + padsize, skb->data, hdrlen); | ||
1121 | skb_pull(skb, padsize); | ||
1122 | } | ||
1123 | |||
1124 | /* Prepare rx status */ | ||
1125 | ath9k_rx_prepare(sc, skb, status, &rx_status); | ||
1126 | |||
1127 | if (!(keyix == ATH9K_RXKEYIX_INVALID) && | ||
1128 | !(status->flags & ATH_RX_DECRYPT_ERROR)) { | ||
1129 | rx_status.flag |= RX_FLAG_DECRYPTED; | ||
1130 | } else if ((le16_to_cpu(hdr->frame_control) & IEEE80211_FCTL_PROTECTED) | ||
1131 | && !(status->flags & ATH_RX_DECRYPT_ERROR) | ||
1132 | && skb->len >= hdrlen + 4) { | ||
1133 | keyix = skb->data[hdrlen + 3] >> 6; | ||
1134 | |||
1135 | if (test_bit(keyix, sc->sc_keymap)) | ||
1136 | rx_status.flag |= RX_FLAG_DECRYPTED; | ||
1137 | } | ||
1138 | |||
1139 | spin_lock_bh(&sc->node_lock); | ||
1140 | an = ath_node_find(sc, hdr->addr2); | ||
1141 | spin_unlock_bh(&sc->node_lock); | ||
1142 | |||
1143 | if (an) { | ||
1144 | ath_rx_input(sc, an, | ||
1145 | hw->conf.ht_conf.ht_supported, | ||
1146 | skb, status, &st); | ||
1147 | } | ||
1148 | if (!an || (st != ATH_RX_CONSUMED)) | ||
1149 | __ieee80211_rx(hw, skb, &rx_status); | ||
1150 | |||
1151 | return 0; | ||
1152 | } | ||
1153 | |||
1154 | int ath_rx_subframe(struct ath_node *an, | ||
1155 | struct sk_buff *skb, | ||
1156 | struct ath_recv_status *status) | ||
1157 | { | ||
1158 | struct ath_softc *sc = an->an_sc; | ||
1159 | struct ieee80211_hw *hw = sc->hw; | ||
1160 | struct ieee80211_rx_status rx_status; | ||
1161 | |||
1162 | /* Prepare rx status */ | ||
1163 | ath9k_rx_prepare(sc, skb, status, &rx_status); | ||
1164 | if (!(status->flags & ATH_RX_DECRYPT_ERROR)) | ||
1165 | rx_status.flag |= RX_FLAG_DECRYPTED; | ||
1166 | |||
1167 | __ieee80211_rx(hw, skb, &rx_status); | ||
1168 | |||
1169 | return 0; | ||
1170 | } | ||
1171 | |||
1172 | /********************************/ | ||
1173 | /* LED functions */ | ||
1174 | /********************************/ | ||
1175 | |||
1176 | static void ath_led_brightness(struct led_classdev *led_cdev, | ||
1177 | enum led_brightness brightness) | ||
1178 | { | ||
1179 | struct ath_led *led = container_of(led_cdev, struct ath_led, led_cdev); | ||
1180 | struct ath_softc *sc = led->sc; | ||
1181 | |||
1182 | switch (brightness) { | ||
1183 | case LED_OFF: | ||
1184 | if (led->led_type == ATH_LED_ASSOC || | ||
1185 | led->led_type == ATH_LED_RADIO) | ||
1186 | sc->sc_flags &= ~SC_OP_LED_ASSOCIATED; | ||
1187 | ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN, | ||
1188 | (led->led_type == ATH_LED_RADIO) ? 1 : | ||
1189 | !!(sc->sc_flags & SC_OP_LED_ASSOCIATED)); | ||
1190 | break; | ||
1191 | case LED_FULL: | ||
1192 | if (led->led_type == ATH_LED_ASSOC) | ||
1193 | sc->sc_flags |= SC_OP_LED_ASSOCIATED; | ||
1194 | ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN, 0); | ||
1195 | break; | ||
1196 | default: | ||
1197 | break; | ||
1198 | } | ||
1199 | } | ||
1200 | |||
1201 | static int ath_register_led(struct ath_softc *sc, struct ath_led *led, | ||
1202 | char *trigger) | ||
1203 | { | ||
1204 | int ret; | ||
1205 | |||
1206 | led->sc = sc; | ||
1207 | led->led_cdev.name = led->name; | ||
1208 | led->led_cdev.default_trigger = trigger; | ||
1209 | led->led_cdev.brightness_set = ath_led_brightness; | ||
1210 | |||
1211 | ret = led_classdev_register(wiphy_dev(sc->hw->wiphy), &led->led_cdev); | ||
1212 | if (ret) | ||
1213 | DPRINTF(sc, ATH_DBG_FATAL, | ||
1214 | "Failed to register led:%s", led->name); | ||
1215 | else | ||
1216 | led->registered = 1; | ||
1217 | return ret; | ||
1218 | } | ||
1219 | |||
1220 | static void ath_unregister_led(struct ath_led *led) | ||
1221 | { | ||
1222 | if (led->registered) { | ||
1223 | led_classdev_unregister(&led->led_cdev); | ||
1224 | led->registered = 0; | ||
1225 | } | ||
1226 | } | ||
1227 | |||
1228 | static void ath_deinit_leds(struct ath_softc *sc) | ||
1229 | { | ||
1230 | ath_unregister_led(&sc->assoc_led); | ||
1231 | sc->sc_flags &= ~SC_OP_LED_ASSOCIATED; | ||
1232 | ath_unregister_led(&sc->tx_led); | ||
1233 | ath_unregister_led(&sc->rx_led); | ||
1234 | ath_unregister_led(&sc->radio_led); | ||
1235 | ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN, 1); | ||
1236 | } | ||
1237 | |||
1238 | static void ath_init_leds(struct ath_softc *sc) | ||
1239 | { | ||
1240 | char *trigger; | ||
1241 | int ret; | ||
1242 | |||
1243 | /* Configure gpio 1 for output */ | ||
1244 | ath9k_hw_cfg_output(sc->sc_ah, ATH_LED_PIN, | ||
1245 | AR_GPIO_OUTPUT_MUX_AS_OUTPUT); | ||
1246 | /* LED off, active low */ | ||
1247 | ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN, 1); | ||
1248 | |||
1249 | trigger = ieee80211_get_radio_led_name(sc->hw); | ||
1250 | snprintf(sc->radio_led.name, sizeof(sc->radio_led.name), | ||
1251 | "ath9k-%s:radio", wiphy_name(sc->hw->wiphy)); | ||
1252 | ret = ath_register_led(sc, &sc->radio_led, trigger); | ||
1253 | sc->radio_led.led_type = ATH_LED_RADIO; | ||
1254 | if (ret) | ||
1255 | goto fail; | ||
1256 | |||
1257 | trigger = ieee80211_get_assoc_led_name(sc->hw); | ||
1258 | snprintf(sc->assoc_led.name, sizeof(sc->assoc_led.name), | ||
1259 | "ath9k-%s:assoc", wiphy_name(sc->hw->wiphy)); | ||
1260 | ret = ath_register_led(sc, &sc->assoc_led, trigger); | ||
1261 | sc->assoc_led.led_type = ATH_LED_ASSOC; | ||
1262 | if (ret) | ||
1263 | goto fail; | ||
1264 | |||
1265 | trigger = ieee80211_get_tx_led_name(sc->hw); | ||
1266 | snprintf(sc->tx_led.name, sizeof(sc->tx_led.name), | ||
1267 | "ath9k-%s:tx", wiphy_name(sc->hw->wiphy)); | ||
1268 | ret = ath_register_led(sc, &sc->tx_led, trigger); | ||
1269 | sc->tx_led.led_type = ATH_LED_TX; | ||
1270 | if (ret) | ||
1271 | goto fail; | ||
1272 | |||
1273 | trigger = ieee80211_get_rx_led_name(sc->hw); | ||
1274 | snprintf(sc->rx_led.name, sizeof(sc->rx_led.name), | ||
1275 | "ath9k-%s:rx", wiphy_name(sc->hw->wiphy)); | ||
1276 | ret = ath_register_led(sc, &sc->rx_led, trigger); | ||
1277 | sc->rx_led.led_type = ATH_LED_RX; | ||
1278 | if (ret) | ||
1279 | goto fail; | ||
1280 | |||
1281 | return; | ||
1282 | |||
1283 | fail: | ||
1284 | ath_deinit_leds(sc); | ||
1285 | } | ||
1286 | |||
1287 | static int ath_detach(struct ath_softc *sc) | ||
1288 | { | ||
1289 | struct ieee80211_hw *hw = sc->hw; | ||
1290 | |||
1291 | DPRINTF(sc, ATH_DBG_CONFIG, "%s: Detach ATH hw\n", __func__); | ||
1292 | |||
1293 | /* Deinit LED control */ | ||
1294 | ath_deinit_leds(sc); | ||
1295 | |||
1296 | /* Unregister hw */ | ||
1297 | |||
1298 | ieee80211_unregister_hw(hw); | ||
1299 | |||
1300 | /* unregister Rate control */ | ||
1301 | ath_rate_control_unregister(); | ||
1302 | |||
1303 | /* tx/rx cleanup */ | ||
1304 | |||
1305 | ath_rx_cleanup(sc); | ||
1306 | ath_tx_cleanup(sc); | ||
1307 | |||
1308 | /* Deinit */ | ||
1309 | |||
1310 | ath_deinit(sc); | ||
1311 | |||
1312 | return 0; | ||
1313 | } | ||
1314 | |||
1315 | static int ath_attach(u16 devid, | ||
1316 | struct ath_softc *sc) | ||
1317 | { | ||
1318 | struct ieee80211_hw *hw = sc->hw; | ||
1319 | int error = 0; | ||
1320 | |||
1321 | DPRINTF(sc, ATH_DBG_CONFIG, "%s: Attach ATH hw\n", __func__); | ||
1322 | |||
1323 | error = ath_init(devid, sc); | ||
1324 | if (error != 0) | ||
1325 | return error; | ||
1326 | |||
1327 | /* Init nodes */ | ||
1328 | |||
1329 | INIT_LIST_HEAD(&sc->node_list); | ||
1330 | spin_lock_init(&sc->node_lock); | ||
1331 | |||
1332 | /* get mac address from hardware and set in mac80211 */ | ||
1333 | |||
1334 | SET_IEEE80211_PERM_ADDR(hw, sc->sc_myaddr); | ||
1335 | |||
1336 | /* setup channels and rates */ | ||
1337 | |||
1338 | sc->sbands[IEEE80211_BAND_2GHZ].channels = | ||
1339 | sc->channels[IEEE80211_BAND_2GHZ]; | ||
1340 | sc->sbands[IEEE80211_BAND_2GHZ].bitrates = | ||
1341 | sc->rates[IEEE80211_BAND_2GHZ]; | ||
1342 | sc->sbands[IEEE80211_BAND_2GHZ].band = IEEE80211_BAND_2GHZ; | ||
1343 | |||
1344 | if (sc->sc_ah->ah_caps.hw_caps & ATH9K_HW_CAP_HT) | ||
1345 | /* Setup HT capabilities for 2.4Ghz*/ | ||
1346 | setup_ht_cap(&sc->sbands[IEEE80211_BAND_2GHZ].ht_info); | ||
1347 | |||
1348 | hw->wiphy->bands[IEEE80211_BAND_2GHZ] = | ||
1349 | &sc->sbands[IEEE80211_BAND_2GHZ]; | ||
1350 | |||
1351 | if (test_bit(ATH9K_MODE_11A, sc->sc_ah->ah_caps.wireless_modes)) { | ||
1352 | sc->sbands[IEEE80211_BAND_5GHZ].channels = | ||
1353 | sc->channels[IEEE80211_BAND_5GHZ]; | ||
1354 | sc->sbands[IEEE80211_BAND_5GHZ].bitrates = | ||
1355 | sc->rates[IEEE80211_BAND_5GHZ]; | ||
1356 | sc->sbands[IEEE80211_BAND_5GHZ].band = | ||
1357 | IEEE80211_BAND_5GHZ; | ||
1358 | |||
1359 | if (sc->sc_ah->ah_caps.hw_caps & ATH9K_HW_CAP_HT) | ||
1360 | /* Setup HT capabilities for 5Ghz*/ | ||
1361 | setup_ht_cap(&sc->sbands[IEEE80211_BAND_5GHZ].ht_info); | ||
1362 | |||
1363 | hw->wiphy->bands[IEEE80211_BAND_5GHZ] = | ||
1364 | &sc->sbands[IEEE80211_BAND_5GHZ]; | ||
1365 | } | ||
1366 | |||
1367 | /* FIXME: Have to figure out proper hw init values later */ | ||
1368 | |||
1369 | hw->queues = 4; | ||
1370 | hw->ampdu_queues = 1; | ||
1371 | |||
1372 | /* Register rate control */ | ||
1373 | hw->rate_control_algorithm = "ath9k_rate_control"; | ||
1374 | error = ath_rate_control_register(); | ||
1375 | if (error != 0) { | ||
1376 | DPRINTF(sc, ATH_DBG_FATAL, | ||
1377 | "%s: Unable to register rate control " | ||
1378 | "algorithm:%d\n", __func__, error); | ||
1379 | ath_rate_control_unregister(); | ||
1380 | goto bad; | ||
1381 | } | ||
1382 | |||
1383 | error = ieee80211_register_hw(hw); | ||
1384 | if (error != 0) { | ||
1385 | ath_rate_control_unregister(); | ||
1386 | goto bad; | ||
1387 | } | ||
1388 | |||
1389 | /* Initialize LED control */ | ||
1390 | ath_init_leds(sc); | ||
1391 | |||
1392 | /* initialize tx/rx engine */ | ||
1393 | |||
1394 | error = ath_tx_init(sc, ATH_TXBUF); | ||
1395 | if (error != 0) | ||
1396 | goto detach; | ||
1397 | |||
1398 | error = ath_rx_init(sc, ATH_RXBUF); | ||
1399 | if (error != 0) | ||
1400 | goto detach; | ||
1401 | |||
1402 | return 0; | ||
1403 | detach: | ||
1404 | ath_detach(sc); | ||
1405 | bad: | ||
1406 | return error; | ||
1407 | } | ||
1408 | |||
1409 | static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) | 1409 | static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) |
1410 | { | 1410 | { |
1411 | void __iomem *mem; | 1411 | void __iomem *mem; |