Author: bschmidt
Date: Sat Jun 18 12:00:49 2011
New Revision: 223248
URL: http://svn.freebsd.org/changeset/base/223248

Log:
  MFC r220721,220723-220726:
  - Rename some stuff in favour of the OpenBSD names:
    - prefer EDCA over WME
    - qid for a TXQ ID
    - reg for register values
  - Shuffle code around a bit. Mostly to group functional connected things,
    others to get the same order as the OpenBSD code.
  - Sync debug and error messages with OpenBSD. The device capability
    announcements are now hidden behind bootverbose.
  - Sync comments with OpenBSD.
  - Whitespace sync, some more style(9) conform then others.

Modified:
  stable/8/sys/dev/iwn/if_iwn.c
  stable/8/sys/dev/iwn/if_iwnreg.h
  stable/8/sys/dev/iwn/if_iwnvar.h
Directory Properties:
  stable/8/sys/   (props changed)
  stable/8/sys/amd64/include/xen/   (props changed)
  stable/8/sys/cddl/contrib/opensolaris/   (props changed)
  stable/8/sys/contrib/dev/acpica/   (props changed)
  stable/8/sys/contrib/pf/   (props changed)

Modified: stable/8/sys/dev/iwn/if_iwn.c
==============================================================================
--- stable/8/sys/dev/iwn/if_iwn.c       Sat Jun 18 11:56:40 2011        
(r223247)
+++ stable/8/sys/dev/iwn/if_iwn.c       Sat Jun 18 12:00:49 2011        
(r223248)
@@ -72,16 +72,61 @@ __FBSDID("$FreeBSD$");
 #include <dev/iwn/if_iwnreg.h>
 #include <dev/iwn/if_iwnvar.h>
 
+struct iwn_ident {
+       uint16_t        vendor;
+       uint16_t        device;
+       const char      *name;
+};
+
+static const struct iwn_ident iwn_ident_table [] = {
+       { 0x8086, 0x4229, "Intel(R) PRO/Wireless 4965BGN" },
+       { 0x8086, 0x422D, "Intel(R) PRO/Wireless 4965BGN" },
+       { 0x8086, 0x4230, "Intel(R) PRO/Wireless 4965BGN" },
+       { 0x8086, 0x4233, "Intel(R) PRO/Wireless 4965BGN" },
+       { 0x8086, 0x4232, "Intel(R) PRO/Wireless 5100" },
+       { 0x8086, 0x4237, "Intel(R) PRO/Wireless 5100" },
+       { 0x8086, 0x423C, "Intel(R) PRO/Wireless 5150" },
+       { 0x8086, 0x423D, "Intel(R) PRO/Wireless 5150" },
+       { 0x8086, 0x4235, "Intel(R) PRO/Wireless 5300" },
+       { 0x8086, 0x4236, "Intel(R) PRO/Wireless 5300" },
+       { 0x8086, 0x423A, "Intel(R) PRO/Wireless 5350" },
+       { 0x8086, 0x423B, "Intel(R) PRO/Wireless 5350" },
+       { 0x8086, 0x0083, "Intel(R) PRO/Wireless 1000" },
+       { 0x8086, 0x0084, "Intel(R) PRO/Wireless 1000" },
+       { 0x8086, 0x008D, "Intel(R) PRO/Wireless 6000" },
+       { 0x8086, 0x008E, "Intel(R) PRO/Wireless 6000" },
+       { 0x8086, 0x4238, "Intel(R) PRO/Wireless 6000" },
+       { 0x8086, 0x4239, "Intel(R) PRO/Wireless 6000" },
+       { 0x8086, 0x422B, "Intel(R) PRO/Wireless 6000" },
+       { 0x8086, 0x422C, "Intel(R) PRO/Wireless 6000" },
+       { 0x8086, 0x0087, "Intel(R) PRO/Wireless 6250" },
+       { 0x8086, 0x0089, "Intel(R) PRO/Wireless 6250" },
+       { 0x8086, 0x0082, "Intel(R) PRO/Wireless 6205a" },
+       { 0x8086, 0x0085, "Intel(R) PRO/Wireless 6205a" },
+#ifdef notyet
+       { 0x8086, 0x008a, "Intel(R) PRO/Wireless 6205b" },
+       { 0x8086, 0x008b, "Intel(R) PRO/Wireless 6205b" },
+       { 0x8086, 0x008f, "Intel(R) PRO/Wireless 6205b" },
+       { 0x8086, 0x0090, "Intel(R) PRO/Wireless 6205b" },
+       { 0x8086, 0x0091, "Intel(R) PRO/Wireless 6205b" },
+#endif
+       { 0, 0, NULL }
+};
+
 static int     iwn_probe(device_t);
 static int     iwn_attach(device_t);
 static const struct iwn_hal *iwn_hal_attach(struct iwn_softc *);
 static void    iwn_radiotap_attach(struct iwn_softc *);
+static void    iwn_sysctlattach(struct iwn_softc *);
 static struct ieee80211vap *iwn_vap_create(struct ieee80211com *,
                    const char name[IFNAMSIZ], int unit, int opmode,
                    int flags, const uint8_t bssid[IEEE80211_ADDR_LEN],
                    const uint8_t mac[IEEE80211_ADDR_LEN]);
 static void    iwn_vap_delete(struct ieee80211vap *);
 static int     iwn_detach(device_t);
+static int     iwn_shutdown(device_t);
+static int     iwn_suspend(device_t);
+static int     iwn_resume(device_t);
 static int     iwn_nic_lock(struct iwn_softc *);
 static int     iwn_eeprom_lock(struct iwn_softc *);
 static int     iwn_init_otprom(struct iwn_softc *);
@@ -116,8 +161,12 @@ static void        iwn_read_eeprom_band(struct 
 #if 0  /* HT */
 static void    iwn_read_eeprom_ht40(struct iwn_softc *, int);
 #endif
-static void    iwn_read_eeprom_channels(struct iwn_softc *, int,
-                   uint32_t);
+static void    iwn_read_eeprom_channels(struct iwn_softc *, int, uint32_t);
+static struct iwn_eeprom_chan *iwn_find_eeprom_channel(struct iwn_softc *,
+                   struct ieee80211_channel *);
+static int     iwn_setregdomain(struct ieee80211com *,
+                   struct ieee80211_regdomain *, int,
+                   struct ieee80211_channel[]);
 static void    iwn_read_eeprom_enhinfo(struct iwn_softc *);
 static struct ieee80211_node *iwn_node_alloc(struct ieee80211vap *,
                    const uint8_t mac[IEEE80211_ADDR_LEN]);
@@ -176,7 +225,7 @@ static int  iwn5000_add_node(struct iwn_s
 static int     iwn_set_link_quality(struct iwn_softc *,
                    struct ieee80211_node *);
 static int     iwn_add_broadcast_node(struct iwn_softc *, int);
-static int     iwn_wme_update(struct ieee80211com *);
+static int     iwn_updateedca(struct ieee80211com *);
 static void    iwn_update_mcast(struct ifnet *);
 static void    iwn_set_led(struct iwn_softc *, uint8_t, uint8_t, uint8_t);
 static int     iwn_set_critical_temp(struct iwn_softc *);
@@ -219,10 +268,12 @@ static void       iwn_ampdu_tx_stop(struct iee
                    struct ieee80211_node *, uint8_t);
 static void    iwn4965_ampdu_tx_start(struct iwn_softc *,
                    struct ieee80211_node *, uint8_t, uint16_t);
-static void    iwn4965_ampdu_tx_stop(struct iwn_softc *, uint8_t, uint16_t);
+static void    iwn4965_ampdu_tx_stop(struct iwn_softc *,
+                   uint8_t, uint16_t);
 static void    iwn5000_ampdu_tx_start(struct iwn_softc *,
                    struct ieee80211_node *, uint8_t, uint16_t);
-static void    iwn5000_ampdu_tx_stop(struct iwn_softc *, uint8_t, uint16_t);
+static void    iwn5000_ampdu_tx_stop(struct iwn_softc *,
+                   uint8_t, uint16_t);
 #endif
 static int     iwn5000_query_calibration(struct iwn_softc *);
 static int     iwn5000_send_calibration(struct iwn_softc *);
@@ -251,27 +302,18 @@ static int        iwn5000_nic_config(struct iwn
 static int     iwn_hw_prepare(struct iwn_softc *);
 static int     iwn_hw_init(struct iwn_softc *);
 static void    iwn_hw_stop(struct iwn_softc *);
+static void    iwn_radio_on(void *, int);
+static void    iwn_radio_off(void *, int);
 static void    iwn_init_locked(struct iwn_softc *);
 static void    iwn_init(void *);
 static void    iwn_stop_locked(struct iwn_softc *);
 static void    iwn_stop(struct iwn_softc *);
-static void    iwn_scan_start(struct ieee80211com *);
-static void    iwn_scan_end(struct ieee80211com *);
-static void    iwn_set_channel(struct ieee80211com *);
-static void    iwn_scan_curchan(struct ieee80211_scan_state *, unsigned long);
-static void    iwn_scan_mindwell(struct ieee80211_scan_state *);
-static struct iwn_eeprom_chan *iwn_find_eeprom_channel(struct iwn_softc *,
-                   struct ieee80211_channel *);
-static int     iwn_setregdomain(struct ieee80211com *,
-                   struct ieee80211_regdomain *, int,
-                   struct ieee80211_channel []);
+static void    iwn_scan_start(struct ieee80211com *);
+static void    iwn_scan_end(struct ieee80211com *);
+static void    iwn_set_channel(struct ieee80211com *);
+static void    iwn_scan_curchan(struct ieee80211_scan_state *, unsigned long);
+static void    iwn_scan_mindwell(struct ieee80211_scan_state *);
 static void    iwn_hw_reset(void *, int);
-static void    iwn_radio_on(void *, int);
-static void    iwn_radio_off(void *, int);
-static void    iwn_sysctlattach(struct iwn_softc *);
-static int     iwn_shutdown(device_t);
-static int     iwn_suspend(device_t);
-static int     iwn_resume(device_t);
 
 #define IWN_DEBUG
 #ifdef IWN_DEBUG
@@ -298,51 +340,75 @@ enum {
                printf(fmt, __VA_ARGS__);               \
 } while (0)
 
-static const char *iwn_intr_str(uint8_t);
+static const char *
+iwn_intr_str(uint8_t cmd)
+{
+       switch (cmd) {
+       /* Notifications */
+       case IWN_UC_READY:              return "UC_READY";
+       case IWN_ADD_NODE_DONE:         return "ADD_NODE_DONE";
+       case IWN_TX_DONE:               return "TX_DONE";
+       case IWN_START_SCAN:            return "START_SCAN";
+       case IWN_STOP_SCAN:             return "STOP_SCAN";
+       case IWN_RX_STATISTICS:         return "RX_STATS";
+       case IWN_BEACON_STATISTICS:     return "BEACON_STATS";
+       case IWN_STATE_CHANGED:         return "STATE_CHANGED";
+       case IWN_BEACON_MISSED:         return "BEACON_MISSED";
+       case IWN_RX_PHY:                return "RX_PHY";
+       case IWN_MPDU_RX_DONE:          return "MPDU_RX_DONE";
+       case IWN_RX_DONE:               return "RX_DONE";
+
+       /* Command Notifications */
+       case IWN_CMD_RXON:              return "IWN_CMD_RXON";
+       case IWN_CMD_RXON_ASSOC:        return "IWN_CMD_RXON_ASSOC";
+       case IWN_CMD_EDCA_PARAMS:       return "IWN_CMD_EDCA_PARAMS";
+       case IWN_CMD_TIMING:            return "IWN_CMD_TIMING";
+       case IWN_CMD_LINK_QUALITY:      return "IWN_CMD_LINK_QUALITY";
+       case IWN_CMD_SET_LED:           return "IWN_CMD_SET_LED";
+       case IWN5000_CMD_WIMAX_COEX:    return "IWN5000_CMD_WIMAX_COEX";
+       case IWN5000_CMD_CALIB_CONFIG:  return "IWN5000_CMD_CALIB_CONFIG";
+       case IWN5000_CMD_CALIB_RESULT:  return "IWN5000_CMD_CALIB_RESULT";
+       case IWN5000_CMD_CALIB_COMPLETE: return "IWN5000_CMD_CALIB_COMPLETE";
+       case IWN_CMD_SET_POWER_MODE:    return "IWN_CMD_SET_POWER_MODE";
+       case IWN_CMD_SCAN:              return "IWN_CMD_SCAN";
+       case IWN_CMD_SCAN_RESULTS:      return "IWN_CMD_SCAN_RESULTS";
+       case IWN_CMD_TXPOWER:           return "IWN_CMD_TXPOWER";
+       case IWN_CMD_TXPOWER_DBM:       return "IWN_CMD_TXPOWER_DBM";
+       case IWN5000_CMD_TX_ANT_CONFIG: return "IWN5000_CMD_TX_ANT_CONFIG";
+       case IWN_CMD_BT_COEX:           return "IWN_CMD_BT_COEX";
+       case IWN_CMD_SET_CRITICAL_TEMP: return "IWN_CMD_SET_CRITICAL_TEMP";
+       case IWN_CMD_SET_SENSITIVITY:   return "IWN_CMD_SET_SENSITIVITY";
+       case IWN_CMD_PHY_CALIB:         return "IWN_CMD_PHY_CALIB";
+       }
+       return "UNKNOWN INTR NOTIF/CMD";
+}
 #else
 #define DPRINTF(sc, m, fmt, ...) do { (void) sc; } while (0)
 #endif
 
-struct iwn_ident {
-       uint16_t        vendor;
-       uint16_t        device;
-       const char      *name;
+static device_method_t iwn_methods[] = {
+       /* Device interface */
+       DEVMETHOD(device_probe,         iwn_probe),
+       DEVMETHOD(device_attach,        iwn_attach),
+       DEVMETHOD(device_detach,        iwn_detach),
+       DEVMETHOD(device_shutdown,      iwn_shutdown),
+       DEVMETHOD(device_suspend,       iwn_suspend),
+       DEVMETHOD(device_resume,        iwn_resume),
+       { 0, 0 }
 };
 
-static const struct iwn_ident iwn_ident_table [] = {
-       { 0x8086, 0x4229, "Intel(R) PRO/Wireless 4965BGN" },
-       { 0x8086, 0x422D, "Intel(R) PRO/Wireless 4965BGN" },
-       { 0x8086, 0x4230, "Intel(R) PRO/Wireless 4965BGN" },
-       { 0x8086, 0x4233, "Intel(R) PRO/Wireless 4965BGN" },
-       { 0x8086, 0x4232, "Intel(R) PRO/Wireless 5100" },
-       { 0x8086, 0x4237, "Intel(R) PRO/Wireless 5100" },
-       { 0x8086, 0x423C, "Intel(R) PRO/Wireless 5150" },
-       { 0x8086, 0x423D, "Intel(R) PRO/Wireless 5150" },
-       { 0x8086, 0x4235, "Intel(R) PRO/Wireless 5300" },
-       { 0x8086, 0x4236, "Intel(R) PRO/Wireless 5300" },
-       { 0x8086, 0x423A, "Intel(R) PRO/Wireless 5350" },
-       { 0x8086, 0x423B, "Intel(R) PRO/Wireless 5350" },
-       { 0x8086, 0x0083, "Intel(R) PRO/Wireless 1000" },
-       { 0x8086, 0x0084, "Intel(R) PRO/Wireless 1000" },
-       { 0x8086, 0x008D, "Intel(R) PRO/Wireless 6000" },
-       { 0x8086, 0x008E, "Intel(R) PRO/Wireless 6000" },
-       { 0x8086, 0x4238, "Intel(R) PRO/Wireless 6000" },
-       { 0x8086, 0x4239, "Intel(R) PRO/Wireless 6000" },
-       { 0x8086, 0x422B, "Intel(R) PRO/Wireless 6000" },
-       { 0x8086, 0x422C, "Intel(R) PRO/Wireless 6000" },
-       { 0x8086, 0x0087, "Intel(R) PRO/Wireless 6250" },
-       { 0x8086, 0x0089, "Intel(R) PRO/Wireless 6250" },
-       { 0x8086, 0x0082, "Intel(R) PRO/Wireless 6205a" },
-       { 0x8086, 0x0085, "Intel(R) PRO/Wireless 6205a" },
-#ifdef notyet
-       { 0x8086, 0x008a, "Intel(R) PRO/Wireless 6205b" },
-       { 0x8086, 0x008b, "Intel(R) PRO/Wireless 6205b" },
-       { 0x8086, 0x008f, "Intel(R) PRO/Wireless 6205b" },
-       { 0x8086, 0x0090, "Intel(R) PRO/Wireless 6205b" },
-       { 0x8086, 0x0091, "Intel(R) PRO/Wireless 6205b" },
-#endif
-       { 0, 0, NULL }
+static driver_t iwn_driver = {
+       "iwn",
+       iwn_methods,
+       sizeof(struct iwn_softc)
 };
+static devclass_t iwn_devclass;
+
+DRIVER_MODULE(iwn, pci, iwn_driver, iwn_devclass, 0, 0);
+
+MODULE_DEPEND(iwn, firmware, 1, 1, 1);
+MODULE_DEPEND(iwn, pci, 1, 1, 1);
+MODULE_DEPEND(iwn, wlan, 1, 1, 1);
 
 static const struct iwn_hal iwn4965_hal = {
        iwn4965_load_firmware,
@@ -422,7 +488,7 @@ iwn_attach(device_t dev)
        struct ieee80211com *ic;
        struct ifnet *ifp;
        const struct iwn_hal *hal;
-       uint32_t tmp;
+       uint32_t reg;
        int i, error, result;
        uint8_t macaddr[IEEE80211_ADDR_LEN];
 
@@ -442,12 +508,12 @@ iwn_attach(device_t dev)
        pci_write_config(dev, 0x41, 0, 1);
 
        /* Hardware bug workaround. */
-       tmp = pci_read_config(dev, PCIR_COMMAND, 1);
-       if (tmp & PCIM_CMD_INTxDIS) {
+       reg = pci_read_config(dev, PCIR_COMMAND, 1);
+       if (reg & PCIM_CMD_INTxDIS) {
                DPRINTF(sc, IWN_DEBUG_RESET, "%s: PCIe INTx Disable set\n",
                    __func__);
-               tmp &= ~PCIM_CMD_INTxDIS;
-               pci_write_config(dev, PCIR_COMMAND, tmp, 1);
+               reg &= ~PCIM_CMD_INTxDIS;
+               pci_write_config(dev, PCIR_COMMAND, reg, 1);
        }
 
        /* Enable bus-mastering. */
@@ -456,30 +522,28 @@ iwn_attach(device_t dev)
        sc->mem_rid = PCIR_BAR(0);
        sc->mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->mem_rid,
            RF_ACTIVE);
-       if (sc->mem == NULL ) {
-               device_printf(dev, "could not allocate memory resources\n");
+       if (sc->mem == NULL) {
+               device_printf(dev, "can't map mem space\n");
                error = ENOMEM;
                return error;
        }
-
        sc->sc_st = rman_get_bustag(sc->mem);
        sc->sc_sh = rman_get_bushandle(sc->mem);
+
        sc->irq_rid = 0;
        if ((result = pci_msi_count(dev)) == 1 &&
            pci_alloc_msi(dev, &result) == 0)
                sc->irq_rid = 1;
+       /* Install interrupt handler. */
        sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irq_rid,
            RF_ACTIVE | RF_SHAREABLE);
        if (sc->irq == NULL) {
-               device_printf(dev, "could not allocate interrupt resource\n");
+               device_printf(dev, "can't map interrupt\n");
                error = ENOMEM;
                goto fail;
        }
 
        IWN_LOCK_INIT(sc);
-       TASK_INIT(&sc->sc_reinit_task, 0, iwn_hw_reset, sc );
-       TASK_INIT(&sc->sc_radioon_task, 0, iwn_radio_on, sc );
-       TASK_INIT(&sc->sc_radiooff_task, 0, iwn_radio_off, sc );
 
        /* Attach Hardware Abstraction Layer. */
        hal = iwn_hal_attach(sc);
@@ -488,15 +552,13 @@ iwn_attach(device_t dev)
                goto fail;
        }
 
-       error = iwn_hw_prepare(sc);
-       if (error != 0) {
+       if ((error = iwn_hw_prepare(sc)) != 0) {
                device_printf(dev, "hardware not ready, error %d\n", error);
                goto fail;
        }
 
        /* Allocate DMA memory for firmware transfers. */
-       error = iwn_alloc_fwmem(sc);
-       if (error != 0) {
+       if ((error = iwn_alloc_fwmem(sc)) != 0) {
                device_printf(dev,
                    "could not allocate memory for firmware, error %d\n",
                    error);
@@ -504,47 +566,41 @@ iwn_attach(device_t dev)
        }
 
        /* Allocate "Keep Warm" page. */
-       error = iwn_alloc_kw(sc);
-       if (error != 0) {
+       if ((error = iwn_alloc_kw(sc)) != 0) {
                device_printf(dev,
-                   "could not allocate \"Keep Warm\" page, error %d\n", error);
+                   "could not allocate keep warm page, error %d\n", error);
                goto fail;
        }
 
        /* Allocate ICT table for 5000 Series. */
        if (sc->hw_type != IWN_HW_REV_TYPE_4965 &&
            (error = iwn_alloc_ict(sc)) != 0) {
-               device_printf(dev,
-                   "%s: could not allocate ICT table, error %d\n",
-                   __func__, error);
+               device_printf(dev, "could not allocate ICT table, error %d\n",
+                   error);
                goto fail;
        }
 
        /* Allocate TX scheduler "rings". */
-       error = iwn_alloc_sched(sc);
-       if (error != 0) {
+       if ((error = iwn_alloc_sched(sc)) != 0) {
                device_printf(dev,
-                   "could not allocate TX scheduler rings, error %d\n",
-                   error);
+                   "could not allocate TX scheduler rings, error %d\n", error);
                goto fail;
        }
 
-       /* Allocate TX rings (16 on 4965AGN, 20 on 5000). */
+       /* Allocate TX rings (16 on 4965AGN, 20 on >=5000). */
        for (i = 0; i < hal->ntxqs; i++) {
-               error = iwn_alloc_tx_ring(sc, &sc->txq[i], i);
-               if (error != 0) {
+               if ((error = iwn_alloc_tx_ring(sc, &sc->txq[i], i)) != 0) {
                        device_printf(dev,
-                           "could not allocate Tx ring %d, error %d\n",
-                           i, error);
+                           "could not allocate TX ring %d, error %d\n", i,
+                           error);
                        goto fail;
                }
        }
 
        /* Allocate RX ring. */
-       error = iwn_alloc_rx_ring(sc, &sc->rxq);
-       if (error != 0 ){
-               device_printf(dev,
-                   "could not allocate Rx ring, error %d\n", error);
+       if ((error = iwn_alloc_rx_ring(sc, &sc->rxq)) != 0) {
+               device_printf(dev, "could not allocate RX ring, error %d\n",
+                   error);
                goto fail;
        }
 
@@ -560,14 +616,19 @@ iwn_attach(device_t dev)
            ((sc->rxchainmask >> 2) & 1) +
            ((sc->rxchainmask >> 1) & 1) +
            ((sc->rxchainmask >> 0) & 1);
+       if (bootverbose) {
+               device_printf(dev, "MIMO %dT%dR, %.4s, address %6D\n",
+                   sc->ntxchains, sc->nrxchains, sc->eeprom_domain,
+                   macaddr, ":");
+       }
 
        ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211);
        if (ifp == NULL) {
                device_printf(dev, "can not allocate ifnet structure\n");
                goto fail;
        }
-       ic = ifp->if_l2com;
 
+       ic = ifp->if_l2com;
        ic->ic_ifp = ifp;
        ic->ic_phytype = IEEE80211_T_OFDM;      /* not only, but not used */
        ic->ic_opmode = IEEE80211_M_STA;        /* default to BSS mode */
@@ -618,17 +679,12 @@ iwn_attach(device_t dev)
 #endif
 
        /* Read MAC address, channels, etc from EEPROM. */
-       error = iwn_read_eeprom(sc, macaddr);
-       if (error != 0) {
+       if ((error = iwn_read_eeprom(sc, macaddr)) != 0) {
                device_printf(dev, "could not read EEPROM, error %d\n",
                    error);
                goto fail;
        }
 
-       device_printf(sc->sc_dev, "MIMO %dT%dR, %.4s, address %6D\n",
-           sc->ntxchains, sc->nrxchains, sc->eeprom_domain,
-           macaddr, ":");
-
 #if 0  /* HT */
        /* Set supported HT rates. */
        ic->ic_sup_mcs[0] = 0xff;
@@ -653,8 +709,14 @@ iwn_attach(device_t dev)
        ic->ic_vap_delete = iwn_vap_delete;
        ic->ic_raw_xmit = iwn_raw_xmit;
        ic->ic_node_alloc = iwn_node_alloc;
+#if 0  /* HT */
+       ic->ic_ampdu_rx_start = iwn_ampdu_rx_start;
+       ic->ic_ampdu_rx_stop = iwn_ampdu_rx_stop;
+       ic->ic_ampdu_tx_start = iwn_ampdu_tx_start;
+       ic->ic_ampdu_tx_stop = iwn_ampdu_tx_stop;
+#endif
        ic->ic_newassoc = iwn_newassoc;
-       ic->ic_wme.wme_update = iwn_wme_update;
+       ic->ic_wme.wme_update = iwn_updateedca;
        ic->ic_update_mcast = iwn_update_mcast;
        ic->ic_scan_start = iwn_scan_start;
        ic->ic_scan_end = iwn_scan_end;
@@ -662,17 +724,14 @@ iwn_attach(device_t dev)
        ic->ic_scan_curchan = iwn_scan_curchan;
        ic->ic_scan_mindwell = iwn_scan_mindwell;
        ic->ic_setregdomain = iwn_setregdomain;
-#if 0  /* HT */
-       ic->ic_ampdu_rx_start = iwn_ampdu_rx_start;
-       ic->ic_ampdu_rx_stop = iwn_ampdu_rx_stop;
-       ic->ic_ampdu_tx_start = iwn_ampdu_tx_start;
-       ic->ic_ampdu_tx_stop = iwn_ampdu_tx_stop;
-#endif
 
        iwn_radiotap_attach(sc);
 
        callout_init_mtx(&sc->calib_to, &sc->sc_mtx, 0);
        callout_init_mtx(&sc->watchdog_to, &sc->sc_mtx, 0);
+       TASK_INIT(&sc->sc_reinit_task, 0, iwn_hw_reset, sc);
+       TASK_INIT(&sc->sc_radioon_task, 0, iwn_radio_on, sc);
+       TASK_INIT(&sc->sc_radiooff_task, 0, iwn_radio_off, sc);
 
        iwn_sysctlattach(sc);
 
@@ -682,12 +741,13 @@ iwn_attach(device_t dev)
        error = bus_setup_intr(dev, sc->irq, INTR_TYPE_NET | INTR_MPSAFE,
            NULL, iwn_intr, sc, &sc->sc_ih);
        if (error != 0) {
-               device_printf(dev, "could not set up interrupt, error %d\n",
+               device_printf(dev, "can't establish interrupt, error %d\n",
                    error);
                goto fail;
        }
 
-       ieee80211_announce(ic);
+       if (bootverbose)
+               ieee80211_announce(ic);
        return 0;
 fail:
        iwn_detach(dev);
@@ -791,11 +851,24 @@ iwn_radiotap_attach(struct iwn_softc *sc
                IWN_RX_RADIOTAP_PRESENT);
 }
 
+static void
+iwn_sysctlattach(struct iwn_softc *sc)
+{
+       struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev);
+       struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev);
+
+#ifdef IWN_DEBUG
+       sc->sc_debug = 0;
+       SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+           "debug", CTLFLAG_RW, &sc->sc_debug, 0, "control debugging printfs");
+#endif
+}
+
 static struct ieee80211vap *
 iwn_vap_create(struct ieee80211com *ic,
-       const char name[IFNAMSIZ], int unit, int opmode, int flags,
-       const uint8_t bssid[IEEE80211_ADDR_LEN],
-       const uint8_t mac[IEEE80211_ADDR_LEN])
+    const char name[IFNAMSIZ], int unit, int opmode, int flags,
+    const uint8_t bssid[IEEE80211_ADDR_LEN],
+    const uint8_t mac[IEEE80211_ADDR_LEN])
 {
        struct iwn_vap *ivp;
        struct ieee80211vap *vap;
@@ -836,7 +909,7 @@ iwn_detach(device_t dev)
        struct iwn_softc *sc = device_get_softc(dev);
        struct ifnet *ifp = sc->sc_ifp;
        struct ieee80211com *ic;
-       int i;
+       int qid;
 
        if (ifp != NULL) {
                ic = ifp->if_l2com;
@@ -851,24 +924,25 @@ iwn_detach(device_t dev)
                ieee80211_ifdetach(ic);
        }
 
+       /* Uninstall interrupt handler. */
+       if (sc->irq != NULL) {
+               bus_teardown_intr(dev, sc->irq, sc->sc_ih);
+               bus_release_resource(dev, SYS_RES_IRQ, sc->irq_rid, sc->irq);
+               if (sc->irq_rid == 1)
+                       pci_release_msi(dev);
+       }
+
        /* Free DMA resources. */
        iwn_free_rx_ring(sc, &sc->rxq);
        if (sc->sc_hal != NULL)
-               for (i = 0; i < sc->sc_hal->ntxqs; i++)
-                       iwn_free_tx_ring(sc, &sc->txq[i]);
+               for (qid = 0; qid < sc->sc_hal->ntxqs; qid++)
+                       iwn_free_tx_ring(sc, &sc->txq[qid]);
        iwn_free_sched(sc);
        iwn_free_kw(sc);
        if (sc->ict != NULL)
                iwn_free_ict(sc);
        iwn_free_fwmem(sc);
 
-       if (sc->irq != NULL) {
-               bus_teardown_intr(dev, sc->irq, sc->sc_ih);
-               bus_release_resource(dev, SYS_RES_IRQ, sc->irq_rid, sc->irq);
-               if (sc->irq_rid == 1)
-                       pci_release_msi(dev);
-       }
-
        if (sc->mem != NULL)
                bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem);
 
@@ -880,34 +954,78 @@ iwn_detach(device_t dev)
 }
 
 static int
-iwn_nic_lock(struct iwn_softc *sc)
+iwn_shutdown(device_t dev)
 {
-       int ntries;
-
-       /* Request exclusive access to NIC. */
-       IWN_SETBITS(sc, IWN_GP_CNTRL, IWN_GP_CNTRL_MAC_ACCESS_REQ);
+       struct iwn_softc *sc = device_get_softc(dev);
 
-       /* Spin until we actually get the lock. */
-       for (ntries = 0; ntries < 1000; ntries++) {
-               if ((IWN_READ(sc, IWN_GP_CNTRL) &
-                   (IWN_GP_CNTRL_MAC_ACCESS_ENA | IWN_GP_CNTRL_SLEEP)) ==
-                   IWN_GP_CNTRL_MAC_ACCESS_ENA)
-                       return 0;
-               DELAY(10);
-       }
-       return ETIMEDOUT;
+       iwn_stop(sc);
+       return 0;
 }
 
-static __inline void
-iwn_nic_unlock(struct iwn_softc *sc)
+static int
+iwn_suspend(device_t dev)
 {
-       IWN_CLRBITS(sc, IWN_GP_CNTRL, IWN_GP_CNTRL_MAC_ACCESS_REQ);
+       struct iwn_softc *sc = device_get_softc(dev);
+       struct ifnet *ifp = sc->sc_ifp;
+       struct ieee80211com *ic = ifp->if_l2com;
+       struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
+
+       iwn_stop(sc);
+       if (vap != NULL)
+               ieee80211_stop(vap);
+       return 0;
 }
 
-static __inline uint32_t
-iwn_prph_read(struct iwn_softc *sc, uint32_t addr)
+static int
+iwn_resume(device_t dev)
 {
-       IWN_WRITE(sc, IWN_PRPH_RADDR, IWN_PRPH_DWORD | addr);
+       struct iwn_softc *sc = device_get_softc(dev);
+       struct ifnet *ifp = sc->sc_ifp;
+       struct ieee80211com *ic = ifp->if_l2com;
+       struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
+
+       /* Clear device-specific "PCI retry timeout" register (41h). */
+       pci_write_config(dev, 0x41, 0, 1);
+
+       if (ifp->if_flags & IFF_UP) {
+               iwn_init(sc);
+               if (vap != NULL)
+                       ieee80211_init(vap);
+               if (ifp->if_drv_flags & IFF_DRV_RUNNING)
+                       iwn_start(ifp);
+       }
+       return 0;
+}
+
+static int
+iwn_nic_lock(struct iwn_softc *sc)
+{
+       int ntries;
+
+       /* Request exclusive access to NIC. */
+       IWN_SETBITS(sc, IWN_GP_CNTRL, IWN_GP_CNTRL_MAC_ACCESS_REQ);
+
+       /* Spin until we actually get the lock. */
+       for (ntries = 0; ntries < 1000; ntries++) {
+               if ((IWN_READ(sc, IWN_GP_CNTRL) &
+                    (IWN_GP_CNTRL_MAC_ACCESS_ENA | IWN_GP_CNTRL_SLEEP)) ==
+                   IWN_GP_CNTRL_MAC_ACCESS_ENA)
+                       return 0;
+               DELAY(10);
+       }
+       return ETIMEDOUT;
+}
+
+static __inline void
+iwn_nic_unlock(struct iwn_softc *sc)
+{
+       IWN_CLRBITS(sc, IWN_GP_CNTRL, IWN_GP_CNTRL_MAC_ACCESS_REQ);
+}
+
+static __inline uint32_t
+iwn_prph_read(struct iwn_softc *sc, uint32_t addr)
+{
+       IWN_WRITE(sc, IWN_PRPH_RADDR, IWN_PRPH_DWORD | addr);
        IWN_BARRIER_READ_WRITE(sc);
        return IWN_READ(sc, IWN_PRPH_RDATA);
 }
@@ -1023,12 +1141,10 @@ iwn_init_otprom(struct iwn_softc *sc)
        int count, error;
 
        /* Wait for clock stabilization before accessing prph. */
-       error = iwn_clock_wait(sc);
-       if (error != 0)
+       if ((error = iwn_clock_wait(sc)) != 0)
                return error;
 
-       error = iwn_nic_lock(sc);
-       if (error != 0)
+       if ((error = iwn_nic_lock(sc)) != 0)
                return error;
        iwn_prph_setbits(sc, IWN_APMG_PS, IWN_APMG_PS_RESET_REQ);
        DELAY(5);
@@ -1073,9 +1189,9 @@ iwn_init_otprom(struct iwn_softc *sc)
 static int
 iwn_read_prom_data(struct iwn_softc *sc, uint32_t addr, void *data, int count)
 {
+       uint8_t *out = data;
        uint32_t val, tmp;
        int ntries;
-       uint8_t *out = data;
 
        addr += sc->prom_base;
        for (; count > 0; count -= 2, addr++) {
@@ -1127,8 +1243,8 @@ iwn_dma_contig_alloc(struct iwn_softc *s
 {
        int error;
 
-       dma->size = size;
        dma->tag = NULL;
+       dma->size = size;
 
        error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), alignment,
            0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, size,
@@ -1150,9 +1266,10 @@ iwn_dma_contig_alloc(struct iwn_softc *s
 
        if (kvap != NULL)
                *kvap = dma->vaddr;
+
        return 0;
-fail:
-       iwn_dma_contig_free(dma);
+
+fail:  iwn_dma_contig_free(dma);
        return error;
 }
 
@@ -1309,11 +1426,13 @@ iwn_alloc_rx_ring(struct iwn_softc *sc, 
                /* Set physical address of RX buffer (256-byte aligned). */
                ring->desc[i] = htole32(paddr >> 8);
        }
+
        bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map,
            BUS_DMASYNC_PREWRITE);
+
        return 0;
-fail:
-       iwn_free_rx_ring(sc, ring);
+
+fail:  iwn_free_rx_ring(sc, ring);
        return error;
 }
 
@@ -1331,11 +1450,6 @@ iwn_reset_rx_ring(struct iwn_softc *sc, 
                        DELAY(10);
                }
                iwn_nic_unlock(sc);
-#ifdef IWN_DEBUG
-               if (ntries == 1000)
-                       DPRINTF(sc, IWN_DEBUG_ANY, "%s\n",
-                           "timeout resetting Rx ring");
-#endif
        }
        ring->cur = 0;
        sc->last_rx_valid = 0;
@@ -1371,16 +1485,16 @@ iwn_free_rx_ring(struct iwn_softc *sc, s
 static int
 iwn_alloc_tx_ring(struct iwn_softc *sc, struct iwn_tx_ring *ring, int qid)
 {
-       bus_size_t size;
        bus_addr_t paddr;
+       bus_size_t size;
        int i, error;
 
        ring->qid = qid;
        ring->queued = 0;
        ring->cur = 0;
 
-       /* Allocate TX descriptors (256-byte aligned.) */
-       size = IWN_TX_RING_COUNT * sizeof(struct iwn_tx_desc);
+       /* Allocate TX descriptors (256-byte aligned). */
+       size = IWN_TX_RING_COUNT * sizeof (struct iwn_tx_desc);
        error = iwn_dma_contig_alloc(sc, &ring->desc_dma, (void **)&ring->desc,
            size, 256);
        if (error != 0) {
@@ -1389,15 +1503,15 @@ iwn_alloc_tx_ring(struct iwn_softc *sc, 
                    __func__, error);
                goto fail;
        }
-
        /*
         * We only use rings 0 through 4 (4 EDCA + cmd) so there is no need
         * to allocate commands space for other rings.
+        * XXX Do we really need to allocate descriptors for other rings?
         */
        if (qid > 4)
                return 0;
 
-       size = IWN_TX_RING_COUNT * sizeof(struct iwn_tx_cmd);
+       size = IWN_TX_RING_COUNT * sizeof (struct iwn_tx_cmd);
        error = iwn_dma_contig_alloc(sc, &ring->cmd_dma, (void **)&ring->cmd,
            size, 4);
        if (error != 0) {
@@ -1408,9 +1522,9 @@ iwn_alloc_tx_ring(struct iwn_softc *sc, 
        }
 
        error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0,
-           BUS_SPACE_MAXADDR_32BIT,
-           BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES, IWN_MAX_SCATTER - 1,
-           MCLBYTES, BUS_DMA_NOWAIT, NULL, NULL, &ring->data_dmat);
+           BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES,
+           IWN_MAX_SCATTER - 1, MCLBYTES, BUS_DMA_NOWAIT, NULL, NULL,
+           &ring->data_dmat);
        if (error != 0) {
                device_printf(sc->sc_dev,
                    "%s: could not create TX buf DMA tag, error %d\n",
@@ -1435,8 +1549,8 @@ iwn_alloc_tx_ring(struct iwn_softc *sc, 
                }
        }
        return 0;
-fail:
-       iwn_free_tx_ring(sc, ring);
+
+fail:  iwn_free_tx_ring(sc, ring);
        return error;
 }
 
@@ -1501,7 +1615,7 @@ iwn5000_ict_reset(struct iwn_softc *sc)
        memset(sc->ict, 0, IWN_ICT_SIZE);
        sc->ict_cur = 0;
 
-       /* Set physical address of ICT table (4KB aligned.) */
+       /* Set physical address of ICT table (4KB aligned). */
        DPRINTF(sc, IWN_DEBUG_RESET, "%s: enabling ICT\n", __func__);
        IWN_WRITE(sc, IWN_DRAM_INT_TBL, IWN_DRAM_INT_TBL_ENABLE |
            IWN_DRAM_INT_TBL_WRAP_CHECK | sc->ict_dma.paddr >> 12);
@@ -1520,8 +1634,8 @@ static int
 iwn_read_eeprom(struct iwn_softc *sc, uint8_t macaddr[IEEE80211_ADDR_LEN])
 {
        const struct iwn_hal *hal = sc->sc_hal;
-       int error;
        uint16_t val;
+       int error;
 
        /* Check whether adapter has an EEPROM or an OTPROM. */
        if (sc->hw_type >= IWN_HW_REV_TYPE_1000 &&
@@ -1531,11 +1645,10 @@ iwn_read_eeprom(struct iwn_softc *sc, ui
            (sc->sc_flags & IWN_FLAG_HAS_OTPROM) ? "OTPROM" : "EEPROM");
 
        /* Adapter has to be powered on for EEPROM access to work. */
-       error = iwn_apm_init(sc);
-       if (error != 0) {
+       if ((error = iwn_apm_init(sc)) != 0) {
                device_printf(sc->sc_dev,
-                   "%s: could not power ON adapter, error %d\n",
-                   __func__, error);
+                   "%s: could not power ON adapter, error %d\n", __func__,
+                   error);
                return error;
        }
 
@@ -1543,17 +1656,13 @@ iwn_read_eeprom(struct iwn_softc *sc, ui
                device_printf(sc->sc_dev, "%s: bad ROM signature\n", __func__);
                return EIO;
        }
-       error = iwn_eeprom_lock(sc);
-       if (error != 0) {
-               device_printf(sc->sc_dev,
-                   "%s: could not lock ROM, error %d\n",
+       if ((error = iwn_eeprom_lock(sc)) != 0) {
+               device_printf(sc->sc_dev, "%s: could not lock ROM, error %d\n",
                    __func__, error);
                return error;
        }
-
        if (sc->sc_flags & IWN_FLAG_HAS_OTPROM) {
-               error = iwn_init_otprom(sc);
-               if (error != 0) {
+               if ((error = iwn_init_otprom(sc)) != 0) {
                        device_printf(sc->sc_dev,
                            "%s: could not initialize OTPROM, error %d\n",
                            __func__, error);
@@ -1581,13 +1690,13 @@ static void
 iwn4965_read_eeprom(struct iwn_softc *sc)
 {
        uint32_t addr;
-       int i;
        uint16_t val;
+       int i;
 
-       /* Read regulatory domain (4 ASCII characters.) */
+       /* Read regulatory domain (4 ASCII characters). */
        iwn_read_prom_data(sc, IWN4965_EEPROM_DOMAIN, sc->eeprom_domain, 4);
 
-       /* Read the list of authorized channels (20MHz ones only.) */
+       /* Read the list of authorized channels (20MHz ones only). */
        for (i = 0; i < 5; i++) {
                addr = iwn4965_regulatory_bands[i];
                iwn_read_eeprom_channels(sc, i, addr);
@@ -1664,17 +1773,17 @@ iwn5000_read_eeprom(struct iwn_softc *sc
 {
        struct iwn5000_eeprom_calib_hdr hdr;
        int32_t volt;
-       uint32_t addr, base;
-       int i;
+       uint32_t base, addr;
        uint16_t val;
+       int i;
 
-       /* Read regulatory domain (4 ASCII characters.) */
+       /* Read regulatory domain (4 ASCII characters). */
        iwn_read_prom_data(sc, IWN5000_EEPROM_REG, &val, 2);
        base = le16toh(val);
        iwn_read_prom_data(sc, base + IWN5000_EEPROM_DOMAIN,
            sc->eeprom_domain, 4);
 
-       /* Read the list of authorized channels (20MHz ones only.) */
+       /* Read the list of authorized channels (20MHz ones only). */
        for (i = 0; i < 5; i++) {
                addr = base + iwn5000_regulatory_bands[i];
                iwn_read_eeprom_channels(sc, i, addr);
@@ -1688,8 +1797,8 @@ iwn5000_read_eeprom(struct iwn_softc *sc
        base = le16toh(val);
        iwn_read_prom_data(sc, base, &hdr, sizeof hdr);
        DPRINTF(sc, IWN_DEBUG_CALIBRATE,
-           "%s: calib version=%u pa type=%u voltage=%u\n",
-           __func__, hdr.version, hdr.pa_type, le16toh(hdr.volt));
+           "%s: calib version=%u pa type=%u voltage=%u\n", __func__,
+           hdr.version, hdr.pa_type, le16toh(hdr.volt));
        sc->calib_ver = hdr.version;
 
        if (sc->hw_type == IWN_HW_REV_TYPE_5150) {
@@ -1754,33 +1863,30 @@ iwn_read_eeprom_band(struct iwn_softc *s
                chan = band->chan[i];
                nflags = iwn_eeprom_channel_flags(&channels[i]);
 
-               DPRINTF(sc, IWN_DEBUG_RESET,
-                   "add chan %d flags 0x%x maxpwr %d\n",
-                   chan, channels[i].flags, channels[i].maxpwr);
-
                c = &ic->ic_channels[ic->ic_nchans++];
                c->ic_ieee = chan;
                c->ic_maxregpower = channels[i].maxpwr;
                c->ic_maxpower = 2*c->ic_maxregpower;
 
-               /* Save maximum allowed TX power for this channel. */
-               sc->maxpwr[chan] = channels[i].maxpwr;
-
                if (n == 0) {   /* 2GHz band */
-                       c->ic_freq = ieee80211_ieee2mhz(chan,
-                           IEEE80211_CHAN_G);
-
+                       c->ic_freq = ieee80211_ieee2mhz(chan, IEEE80211_CHAN_G);
                        /* G =>'s B is supported */
                        c->ic_flags = IEEE80211_CHAN_B | nflags;
-
                        c = &ic->ic_channels[ic->ic_nchans++];
                        c[0] = c[-1];
                        c->ic_flags = IEEE80211_CHAN_G | nflags;
                } else {        /* 5GHz band */
-                       c->ic_freq = ieee80211_ieee2mhz(chan,
-                           IEEE80211_CHAN_A);
+                       c->ic_freq = ieee80211_ieee2mhz(chan, IEEE80211_CHAN_A);
                        c->ic_flags = IEEE80211_CHAN_A | nflags;
                }
+
+               /* Save maximum allowed TX power for this channel. */
+               sc->maxpwr[chan] = channels[i].maxpwr;
+
+               DPRINTF(sc, IWN_DEBUG_RESET,
+                   "add chan %d flags 0x%x maxpwr %d\n", chan,
+                   channels[i].flags, channels[i].maxpwr);
+
 #if 0  /* HT */
                /* XXX no constraints on using HT20 */
                /* add HT20, HT40 added separately */
@@ -1869,6 +1975,48 @@ iwn_read_eeprom_channels(struct iwn_soft
        ieee80211_sort_channels(ic->ic_channels, ic->ic_nchans);
 }
 
+static struct iwn_eeprom_chan *
+iwn_find_eeprom_channel(struct iwn_softc *sc, struct ieee80211_channel *c)
+{
+       int i, j;
+
+       for (j = 0; j < 7; j++) {
+               for (i = 0; i < iwn_bands[j].nchan; i++) {
+                       if (iwn_bands[j].chan[i] == c->ic_ieee)
+                               return &sc->eeprom_channels[j][i];
+               }
+       }
+
+       return NULL;
+}
+
+/*
+ * Enforce flags read from EEPROM.
+ */
+static int
+iwn_setregdomain(struct ieee80211com *ic, struct ieee80211_regdomain *rd,
+    int nchan, struct ieee80211_channel chans[])
+{
+       struct iwn_softc *sc = ic->ic_ifp->if_softc;
+       int i;
+
+       for (i = 0; i < nchan; i++) {
+               struct ieee80211_channel *c = &chans[i];
+               struct iwn_eeprom_chan *channel;
+
+               channel = iwn_find_eeprom_channel(sc, c);
+               if (channel == NULL) {
+                       if_printf(ic->ic_ifp,
+                           "%s: invalid channel %u freq %u/0x%x\n",
+                           __func__, c->ic_ieee, c->ic_freq, c->ic_flags);
+                       return EINVAL;
+               }
+               c->ic_flags |= iwn_eeprom_channel_flags(channel);
+       }
+
+       return 0;
+}
+
 #define nitems(_a)     (sizeof((_a)) / sizeof((_a)[0]))
 
 static void
@@ -1929,7 +2077,9 @@ iwn_newassoc(struct ieee80211_node *ni, 
 static int
 iwn_media_change(struct ifnet *ifp)
 {
-       int error = ieee80211_media_change(ifp);
+       int error;

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to