/* * Copyright (c) 2008 by Alan Maguire * Copyright (c) 2008 by Ben Taylor * Copyright (c) 2007 by Lukas Turek * Copyright (c) 2007 by Jiri Svoboda * Copyright (c) 2007 by Martin Krulis * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * */ /*- * Copyright (c) 2006 by Damien Bergamini * Copyright (c) 2006 by Florian Stoehr * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #pragma ident "%Z%%M% %I% %E% SMI" /* * ZD1211 wLAN driver * Device hardware control * * Control the ZD1211 chip and the RF chip. */ #include #include #include "zyd_usb.h" #include "zyd_reg.h" #include "zyd_impl.h" #include "zyd_hw.h" void zyd_set_channel(struct zyd_softc *, struct ieee80211_channel *); zyd_res zyd_al2230_rf_init(struct zyd_softc *); zyd_res zyd_al2230_rf_init_b(struct zyd_softc *); zyd_res zyd_al2230_switch_radio(struct zyd_softc *, boolean_t); zyd_res zyd_al2230_set_channel(struct zyd_softc *, uint8_t); zyd_res zyd_rfmd_rf_init(struct zyd_softc *); zyd_res zyd_rfmd_switch_radio(struct zyd_softc *, boolean_t); zyd_res zyd_rfmd_set_channel(struct zyd_softc *, uint8_t); zyd_res zyd_al7230b_rf_init(struct zyd_softc *); zyd_res zyd_al7230b_switch_radio(struct zyd_softc *, boolean_t); zyd_res zyd_al7230b_set_channel(struct zyd_softc *, uint8_t); const char *zyd_rf_name(uint8_t); /* io write sequences to initialize RF-independent PHY registers */ static const struct zyd_iowrite16 zyd_def_phy[] = ZYD_DEF_PHY; static const struct zyd_iowrite16 zyd_def_phyB[] = ZYD_DEF_PHYB; /* * Read a 32-bit I/O register. * * sc soft state * reg register number * *val place to store the value */ zyd_res zyd_read32(struct zyd_softc *sc, uint16_t reg, uint32_t *val) { zyd_res result; uint16_t tmp[4]; uint16_t regs[2]; regs[0] = LE_16(ZYD_REG32_HI(reg)); regs[1] = LE_16(ZYD_REG32_LO(reg)); result = zyd_usb_ioread_req(&sc->usb, regs, sizeof (regs), tmp, sizeof (tmp)); if (result != USB_SUCCESS) return (ZYD_FAILURE); if (tmp[0] != regs[0] || tmp[2] != regs[1]) { ZYD_WARN("ioread response doesn't match request"); ZYD_WARN("requested regs %04x, %04x; got %04x, %04x", LE_16(regs[0]), LE_16(regs[1]), LE_16(tmp[0]), LE_16(tmp[2])); return (ZYD_FAILURE); } *val = ((uint32_t)LE_16(tmp[1]) << 16) | (uint32_t)LE_16(tmp[3]); return (ZYD_SUCCESS); } /* * Write a 32-bit I/O register. * * sc soft state * reg register number * val value to write */ zyd_res zyd_write32(struct zyd_softc *sc, uint16_t reg, uint32_t val) { zyd_res result; uint16_t tmp[4]; tmp[0] = LE_16(ZYD_REG32_HI(reg)); tmp[1] = LE_16(val >> 16); tmp[2] = LE_16(ZYD_REG32_LO(reg)); tmp[3] = LE_16(val & 0xffff); result = zyd_usb_cmd_send(&sc->usb, ZYD_CMD_IOWR, tmp, sizeof (tmp)); return (result); } /* * Read a 16-bit I/O register. * * sc soft state * reg register number * *val place to store the value */ zyd_res zyd_read16(struct zyd_softc *sc, uint16_t reg, uint16_t *val) { zyd_res result; uint16_t tmp[2]; uint16_t regbuf; regbuf = LE_16(reg); result = zyd_usb_ioread_req(&sc->usb, ®buf, sizeof (regbuf), tmp, sizeof (tmp)); if (result != USB_SUCCESS) return (ZYD_FAILURE); if (tmp[0] != regbuf) { ZYD_WARN("ioread response doesn't match request"); ZYD_WARN("requested reg %04x; got %04x", LE_16(regbuf), LE_16(tmp[0])); return (ZYD_FAILURE); } if (result != USB_SUCCESS) return (ZYD_FAILURE); *val = LE_16(tmp[1]); return (ZYD_SUCCESS); } /* * Write a 16-bit I/O register. * * sc soft state * reg register number * val value to write */ zyd_res zyd_write16(struct zyd_softc *sc, uint16_t reg, uint16_t val) { zyd_res result; uint16_t tmp[2]; tmp[0] = LE_16(ZYD_REG32_LO(reg)); tmp[1] = LE_16(val & 0xffff); result = zyd_usb_cmd_send(&sc->usb, ZYD_CMD_IOWR, tmp, sizeof (tmp)); return (result); } /* * Write an array of 16-bit registers. * * sc soft state * *reqa array of register-value pairs * n number of registers */ zyd_res zyd_write16a(struct zyd_softc *sc, const struct zyd_iowrite16 *reqa, int n) { zyd_res res; int i; for (i=0; imacaddr[0] = tmp & 0xff; sc->macaddr[1] = tmp >> 8; sc->macaddr[2] = tmp >> 16; sc->macaddr[3] = tmp >> 24; ZYD_DEBUG("read second part of mac"); if (zyd_read32(sc, ZYD_EEPROM_MAC_ADDR_P2, &tmp) != ZYD_SUCCESS) return (ZYD_FAILURE); sc->macaddr[4] = tmp & 0xff; sc->macaddr[5] = tmp >> 8; ZYD_DEBUG("done"); return (ZYD_SUCCESS); } /* * Write bits to RF configuration register. */ zyd_res zyd_rfwrite(struct zyd_softc *sc, uint32_t val, int bits) { uint16_t cr203; struct zyd_rfwrite req; uint16_t tmp; int bit; zyd_res res; int i; if (zyd_read16(sc, ZYD_CR203, &cr203) != ZYD_SUCCESS) return (ZYD_FAILURE); cr203 &= ~(ZYD_RF_IF_LE | ZYD_RF_CLK | ZYD_RF_DATA); req.code = LE_16(ZYD_RFCFG_VALUE); req.width = LE_16((uint16_t)bits); for (i = 0; i < bits; i++) { bit = (val & (1 << (bits - i - 1))) != 0; tmp = LE_16(cr203) | (bit ? LE_16(ZYD_RF_DATA) : 0); req.bit[i] = tmp; } res = zyd_usb_cmd_send(&sc->usb, ZYD_CMD_RFCFG, &req, sizeof(uint16_t)*(2+bits)); if (res != ZYD_SUCCESS) { ZYD_WARN("failed configuring rf register"); return (ZYD_FAILURE); } return (ZYD_SUCCESS); } /* * Control the LEDs. */ static void zyd_set_led(struct zyd_softc *sc, int which, boolean_t on) { uint32_t tmp; (void)zyd_read32(sc, ZYD_MAC_TX_PE_CONTROL, &tmp); tmp &= ~which; if (on == B_TRUE) tmp |= which; (void)zyd_write32(sc, ZYD_MAC_TX_PE_CONTROL, tmp); } /* * Set MAC address. */ static void zyd_set_macaddr(struct zyd_softc *sc, const uint8_t *addr) { uint32_t tmp; tmp = addr[3] << 24 | addr[2] << 16 | addr[1] << 8 | addr[0]; (void)zyd_write32(sc, ZYD_MAC_MACADRL, tmp); tmp = addr[5] << 8 | addr[4]; (void)zyd_write32(sc, ZYD_MAC_MACADRH, tmp); } /* * Read data from EEPROM. */ static void zyd_read_eeprom(struct zyd_softc *sc) { uint32_t tmp; uint16_t val; int i; /* read RF chip type */ (void)zyd_read32(sc, ZYD_EEPROM_POD, &tmp); sc->rf_rev = tmp & 0x0f; sc->pa_rev = (tmp >> 16) & 0x0f; sc->fix_cr47 = (tmp >> 8) & 0x01; sc->fix_cr157 = (tmp >> 13) & 0x01; ZYD_DEBUG("sc->fix_cr47 = 0x%x", sc->fix_cr47); ZYD_DEBUG("sc->fix_cr157 = 0x%x", sc->fix_cr157); ZYD_DEBUG("Found RF %s chip, sc->rf_rev = 0x%x\n", zyd_rf_name(sc->rf_rev), sc->rf_rev); /* read regulatory domain (currently unused) */ (void)zyd_read32(sc, ZYD_EEPROM_SUBID, &tmp); sc->regdomain = tmp >> 16; ZYD_DEBUG("regulatory domain %x\n", sc->regdomain); /* read Tx power calibration tables */ for (i = 0; i < 7; i++) { (void)zyd_read16(sc, ZYD_EEPROM_PWR_CAL + i, &val); sc->pwr_cal[i * 2] = val >> 8; sc->pwr_cal[i * 2 + 1] = val & 0xff; (void)zyd_read16(sc, ZYD_EEPROM_PWR_INT + i, &val); sc->pwr_int[i * 2] = val >> 8; sc->pwr_int[i * 2 + 1] = val & 0xff; (void)zyd_read16(sc, ZYD_EEPROM_36M_CAL + i, &val); sc->ofdm36_cal[i * 2] = val >> 8; sc->ofdm36_cal[i * 2 + 1] = val & 0xff; (void)zyd_read16(sc, ZYD_EEPROM_48M_CAL + i, &val); sc->ofdm48_cal[i * 2] = val >> 8; sc->ofdm48_cal[i * 2 + 1] = val & 0xff; (void)zyd_read16(sc, ZYD_EEPROM_54M_CAL + i, &val); sc->ofdm54_cal[i * 2] = val >> 8; sc->ofdm54_cal[i * 2 + 1] = val & 0xff; } } /* * Finish ZD chip initialization. */ static zyd_res zyd_hw_configure(struct zyd_softc *sc) { zyd_res res; uint32_t tmp; /* specify that the plug and play is finished */ (void)zyd_write32(sc, ZYD_MAC_AFTER_PNP, 1); (void)zyd_read16(sc, ZYD_FIRMWARE_BASE_ADDR, &sc->fwbase); ZYD_DEBUG("firmware base address=0x%04x\n", sc->fwbase); /* retrieve firmware revision number */ (void)zyd_read16(sc, sc->fwbase + ZYD_FW_FIRMWARE_REV, &sc->fw_rev); ZYD_DEBUG("firmware revision number=0x%04x\n", sc->fw_rev); (void)zyd_write32(sc, ZYD_CR_GPI_EN, 0); (void)zyd_write32(sc, ZYD_MAC_CONT_WIN_LIMIT, 0x7f043f); /* disable interrupts */ (void)zyd_write32(sc, ZYD_CR_INTERRUPT, 0); /* Init RF chip-independent PHY registers */ zyd_lock_phy(sc); if (sc->mac_rev == ZYD_ZD1211) { res = zyd_write16a(sc, zyd_def_phy, ZYD_ARRAY_LENGTH(zyd_def_phy)); } else { res = zyd_write16a(sc, zyd_def_phyB, ZYD_ARRAY_LENGTH(zyd_def_phyB)); } if (sc->fix_cr157) { if (zyd_read32(sc, ZYD_EEPROM_PHY_REG, &tmp) == 0) (void)zyd_write32(sc, ZYD_CR157, tmp >> 8); } zyd_unlock_phy(sc); if (res != ZYD_SUCCESS) return (ZYD_FAILURE); /* HMAC initialization magic */ if (sc->mac_rev == ZYD_ZD1211) { zyd_write32(sc, ZYD_MAC_RETRY, 0x00000002); } else { zyd_write32(sc, ZYD_MACB_MAX_RETRY, 0x02020202); zyd_write32(sc, ZYD_MACB_TXPWR_CTL4, 0x007f003f); zyd_write32(sc, ZYD_MACB_TXPWR_CTL3, 0x007f003f); zyd_write32(sc, ZYD_MACB_TXPWR_CTL2, 0x003f001f); zyd_write32(sc, ZYD_MACB_TXPWR_CTL1, 0x001f000f); zyd_write32(sc, ZYD_MACB_AIFS_CTL1, 0x00280028); zyd_write32(sc, ZYD_MACB_AIFS_CTL2, 0x008C003c); zyd_write32(sc, ZYD_MACB_TXOP, 0x01800824); } zyd_write32(sc, ZYD_MAC_ACK_EXT, 0x00000020); zyd_write32(sc, ZYD_CR_ADDA_MBIAS_WT, 0x30000808); zyd_write32(sc, ZYD_MAC_SNIFFER, 0x00000000); zyd_write32(sc, ZYD_MAC_RXFILTER, 0x00000000); zyd_write32(sc, ZYD_MAC_GHTBL, 0x00000000); zyd_write32(sc, ZYD_MAC_GHTBH, 0x80000000); zyd_write32(sc, ZYD_MAC_MISC, 0x000000a4); zyd_write32(sc, ZYD_CR_ADDA_PWR_DWN, 0x0000007f); zyd_write32(sc, ZYD_MAC_BCNCFG, 0x00f00401); zyd_write32(sc, ZYD_MAC_PHY_DELAY2, 0x00000000); zyd_write32(sc, ZYD_MAC_ACK_EXT, 0x00000080); zyd_write32(sc, ZYD_CR_ADDA_PWR_DWN, 0x00000000); zyd_write32(sc, ZYD_MAC_SIFS_ACK_TIME, 0x00000100); zyd_write32(sc, ZYD_MAC_DIFS_EIFS_SIFS, 0x0547c032); zyd_write32(sc, ZYD_CR_RX_PE_DELAY, 0x00000070); zyd_write32(sc, ZYD_CR_PS_CTRL, 0x10000000); zyd_write32(sc, ZYD_MAC_RTSCTSRATE, 0x02030203); zyd_write32(sc, ZYD_MAC_RX_THRESHOLD, 0x000c0640); zyd_write32(sc, ZYD_MAC_BACKOFF_PROTECT, 0x00000114); return (ZYD_SUCCESS); } /* * Set active channel number. */ void zyd_hw_set_channel(struct zyd_softc *sc, uint8_t chan) { uint32_t tmp; zyd_lock_phy(sc); switch(sc->rf_rev) { case ZYD_RF_AL2230: zyd_al2230_set_channel(sc, chan); break; case ZYD_RF_RFMD: zyd_rfmd_set_channel(sc, chan); break; case ZYD_RF_AL7230B: zyd_al7230b_set_channel(sc, chan); break; } /* update Tx power */ (void)zyd_write16(sc, ZYD_CR31, sc->pwr_int[chan - 1]); if (sc->mac_rev == ZYD_ZD1211B) { (void)zyd_write16(sc, ZYD_CR67, sc->ofdm36_cal[chan - 1]); (void)zyd_write16(sc, ZYD_CR66, sc->ofdm48_cal[chan - 1]); (void)zyd_write16(sc, ZYD_CR65, sc->ofdm54_cal[chan - 1]); (void)zyd_write16(sc, ZYD_CR68, sc->pwr_cal[chan - 1]); (void)zyd_write16(sc, ZYD_CR69, 0x28); (void)zyd_write16(sc, ZYD_CR69, 0x2a); } if (sc->fix_cr47) { /* set CCK baseband gain from EEPROM */ if (zyd_read32(sc, ZYD_EEPROM_PHY_REG, &tmp) == 0) (void)zyd_write16(sc, ZYD_CR47, tmp & 0xff); } (void)zyd_write32(sc, ZYD_CR_CONFIG_PHILIPS, 0); zyd_unlock_phy(sc); } /* * Initialize hardware. */ zyd_res zyd_hw_init(struct zyd_softc *sc) { zyd_res res; if (!sc->fwloaded && zyd_usb_init(sc) != ZYD_SUCCESS) return (ZYD_FAILURE); if (zyd_read_mac(sc) != ZYD_SUCCESS) { ZYD_WARN("failed to read MAC address"); return (ZYD_FAILURE); } zyd_read_eeprom(sc); switch (sc->rf_rev) { case ZYD_RF_AL2230: case ZYD_RF_RFMD: case ZYD_RF_AL7230B: break; default: ZYD_WARN("Unsupported RF %s, chip type 0x%x", zyd_rf_name(sc->rf_rev), sc->rf_rev); return (ZYD_FAILURE); break; } if (zyd_hw_configure(sc) != ZYD_SUCCESS) { ZYD_WARN("Failed to configure hardware."); return (ZYD_FAILURE); } /* RF chip init */ zyd_lock_phy(sc); switch(sc->rf_rev) { case ZYD_RF_AL2230: if (sc->mac_rev == ZYD_ZD1211) { res = zyd_al2230_rf_init(sc); } else { res = zyd_al2230_rf_init_b(sc); } break; case ZYD_RF_RFMD: res = zyd_rfmd_rf_init(sc); break; case ZYD_RF_AL7230B: res = zyd_al7230b_rf_init(sc); break; default: ZYD_WARN("Unsupported Radio %s, code = 0x%x", zyd_rf_name(sc->rf_rev), sc->rf_rev); res = ZYD_FAILURE; break; } zyd_unlock_phy(sc); if (res != ZYD_SUCCESS) { ZYD_WARN("Failed to configure RF chip"); return (ZYD_FAILURE); } ZYD_DEBUG("Zyd HW inited. MAC=%02X:%02X:%02X:%02X:%02X:%02X", sc->macaddr[0],sc->macaddr[1],sc->macaddr[2], sc->macaddr[3],sc->macaddr[4],sc->macaddr[5]); return (ZYD_SUCCESS); } #if 0 /* Initialize the radios */ zyd_res zyd_rf_attach(struct zyd_softc *sc, uint8_t type) { struct zyd_rf *rf = &sc->sc_rf; rf->rf_sc = sc; switch (type) { case ZYD_RF_AL2230: if (sc->mac_rev == ZYD_ZD1211) rf->init = zyd_al2230_rf_init; else rf->init = zyd_al2230_rf_init_b; rf->switch_radio = zyd_al2230_switch_radio; rf->set_channel = zyd_al2230_set_channel; rf->width = ZYD_AL2230_RF_BITS; /* 24-bit RF values */ break; default: printf("%s: sorry, radio \"%s\" is not supported yet\n", "Changeme", zyd_rf_name(type)); return (ZYD_FAILURE); } return (ZYD_SUCCESS); } #endif /* * Deinitialize hardware. */ void zyd_hw_deinit(struct zyd_softc *sc) { zyd_usb_deinit(&sc->usb); } /* * Activate the device. */ zyd_res zyd_hw_start(struct zyd_softc *sc) { zyd_res res; struct ieee80211com *ic = &sc->ic; if (zyd_usb_data_in_enable(&sc->usb) != ZYD_SUCCESS) { ZYD_WARN("error starting rx transfer"); return (ZYD_FAILURE); } ZYD_DEBUG("setting MAC address\n"); zyd_set_macaddr(sc, sc->macaddr); /* we'll do software WEP decryption for now */ ZYD_DEBUG("setting encryption type\n"); res = zyd_write32(sc, ZYD_MAC_ENCRYPTION_TYPE, ZYD_ENC_SNIFFER); if (res != ZYD_SUCCESS) return (ZYD_FAILURE); /* promiscuous mode */ (void)zyd_write32(sc, ZYD_MAC_SNIFFER, 0); /* try to catch all packets */ (void)zyd_write32(sc, ZYD_MAC_RXFILTER, ZYD_FILTER_BSS); /* switch radio transmitter ON */ switch(sc->rf_rev) { case ZYD_RF_AL2230: zyd_al2230_switch_radio(sc, B_TRUE); break; case ZYD_RF_RFMD: zyd_rfmd_switch_radio(sc, B_TRUE); break; case ZYD_RF_AL7230B: zyd_al7230b_switch_radio(sc, B_TRUE); break; } /* set basic rates */ ZYD_DEBUG("set basic rates\n"); if (ic->ic_curmode == IEEE80211_MODE_11B) (void)zyd_write32(sc, ZYD_MAC_BAS_RATE, 0x0003); else if (ic->ic_curmode == IEEE80211_MODE_11A) (void)zyd_write32(sc, ZYD_MAC_BAS_RATE, 0x1500); else /* assumes 802.11b/g */ (void)zyd_write32(sc, ZYD_MAC_BAS_RATE, 0x000f); /* set mandatory rates */ ZYD_DEBUG("set mandatory rates\n"); if (ic->ic_curmode == IEEE80211_MODE_11B) (void)zyd_write32(sc, ZYD_MAC_MAN_RATE, 0x000f); else if (ic->ic_curmode == IEEE80211_MODE_11A) (void)zyd_write32(sc, ZYD_MAC_MAN_RATE, 0x1500); else /* assumes 802.11b/g */ (void)zyd_write32(sc, ZYD_MAC_MAN_RATE, 0x150f); /* enable interrupts */ (void)zyd_write32(sc, ZYD_CR_INTERRUPT, ZYD_HWINT_MASK); zyd_set_led(sc, ZYD_LED2, B_TRUE); return (ZYD_SUCCESS); } /* * Deactivate the device. */ void zyd_hw_stop(struct zyd_softc *sc) { ZYD_DEBUG("begin\n"); /* switch radio transmitter OFF */ switch(sc->rf_rev) { case ZYD_RF_AL2230: zyd_al2230_switch_radio(sc, B_FALSE); break; case ZYD_RF_RFMD: zyd_rfmd_switch_radio(sc, B_FALSE); break; case ZYD_RF_AL7230B: zyd_al7230b_switch_radio(sc, B_FALSE); break; } /* disable reception */ (void)zyd_write32(sc, ZYD_MAC_RXFILTER, 0); /* disable interrupts */ (void)zyd_write32(sc, ZYD_CR_INTERRUPT, 0); zyd_usb_data_in_disable(&sc->usb); zyd_set_led(sc, ZYD_LED2, B_FALSE); } uint8_t zyd_plcp_signal(uint16_t rate) { switch (rate) { /* CCK rates (returned values are device-dependent) */ case 2: return 0x0; case 4: return 0x1; case 11: return 0x2; case 22: return 0x3; /* OFDM rates (cf IEEE Std 802.11a-1999, pp. 14 Table 80) */ case 12: return 0xb; case 18: return 0xf; case 24: return 0xa; case 36: return 0xe; case 48: return 0x9; case 72: return 0xd; case 96: return 0x8; case 108: return 0xc; /* unsupported rates (should not get there) */ default: return 0xff; } } /* * Transmit a 802.11 frame. * * Constructs a packet from zyd_tx_header and 802.11 frame data * and sends it to the chip. */ zyd_res zyd_hw_send_frame(ieee80211com_t *ic, struct zyd_softc *sc, mblk_t *mp) { void *buffer; uint8_t *buf_data; struct zyd_tx_header *buf_hdr; struct ieee80211_frame *wh; struct ieee80211_node *in; struct ieee80211_key *k; mblk_t *m, *m0; int len, total_len, off, mblen; uint16_t frame_size, additional_size, rate; uint8_t service; m = allocb(msgdsize(mp) + 32, BPRI_MED); if (m == NULL) { sc->tx_nobuf++; return (ZYD_FAILURE); } for (off = 0, m0 = mp; m0 != NULL; m0 = m0->b_cont) { mblen = MBLKL(m0); (void) memcpy(m->b_rptr + off, m0->b_rptr, mblen); off += mblen; } m->b_wptr += off; wh = (struct ieee80211_frame *)m->b_rptr; in = ieee80211_find_txnode(ic, wh->i_addr1); if (in == NULL) { freemsg(m); sc->tx_err++; return (ZYD_FAILURE); } in->in_inact = 0; (void) ieee80211_encap(ic, m, in); if (wh->i_fc[1] & IEEE80211_FC1_WEP) { k = ieee80211_crypto_encap(ic, m); if (k == NULL) { sc->tx_err++; ieee80211_free_node(in); freemsg(m); return (ZYD_FAILURE); } /* packet header may have moved, reset our local pointer */ wh = (struct ieee80211_frame *)m->b_rptr; } /* * pickup a rate. May need work to make adaptive - at present, * picks best rate for mode. */ if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_MGT) { /* mgmt frames are sent at 1M */ rate = (uint16_t)in->in_rates.ir_rates[0]; } else if (ic->ic_fixed_rate != IEEE80211_FIXED_RATE_NONE) { rate = (uint16_t)ic->ic_sup_rates[ic->ic_curmode]. ir_rates[ic->ic_fixed_rate]; } else { in->in_txrate = in->in_rates.ir_nrates - 1; rate = (uint16_t)ic->ic_sup_rates[ic->ic_curmode]. ir_rates[in->in_txrate]; } rate &= IEEE80211_RATE_VAL; if (rate == 0) /* XXX should not happen */ rate = 2; /* Get total length of frame */ len = msgsize(m); /* Total number of bytes to send to the chip */ total_len = sizeof (struct zyd_tx_header) + len; /* Allocate a buffer to store the packet */ buffer = kmem_alloc(total_len, KM_NOSLEEP); if (buffer == NULL) { ieee80211_free_node(in); freemsg(m); return (ZYD_FAILURE); } /* Get pointers to header buffer and 802.11 frame buffer */ buf_hdr = (struct zyd_tx_header *)buffer; buf_data = ((uint8_t *)buffer) + sizeof (struct zyd_tx_header); /*** Fill header information ***/ /* * Frame size in bytes including CRC32 checksum */ frame_size = (uint16_t)len + IEEE80211_FCS_LEN; buf_hdr->frame_size = LE_16(frame_size); /* * Compute "packet size". What the 10 stands for, * nobody knows. */ additional_size = sizeof (struct zyd_tx_header) + 10; if (sc->mac_rev == ZYD_ZD1211) buf_hdr->packet_size = LE_16(frame_size + additional_size); else buf_hdr->packet_size = LE_16(additional_size); buf_hdr->rate_mod_flags = LE_8(zyd_plcp_signal(rate)); buf_hdr->type_flags = LE_8(ZYD_TX_FLAG_BACKOFF); if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { /* multicast frames are not sent at OFDM rates in 802.11b/g */ if (frame_size > ic->ic_rtsthreshold) { buf_hdr->type_flags |= ZYD_TX_FLAG_RTS; } else if (ZYD_RATE_IS_OFDM(rate) && (ic->ic_flags & IEEE80211_F_USEPROT)) { if (ic->ic_protmode == IEEE80211_PROT_CTSONLY) buf_hdr->type_flags |= ZYD_TX_FLAG_CTS_TO_SELF; else if (ic->ic_protmode == IEEE80211_PROT_RTSCTS) buf_hdr->type_flags |= ZYD_TX_FLAG_RTS; } } else buf_hdr->type_flags |= ZYD_TX_FLAG_MULTICAST; if ((wh->i_fc[0] & (IEEE80211_FC0_TYPE_MASK | IEEE80211_FC0_SUBTYPE_MASK)) == (IEEE80211_FC0_TYPE_CTL | IEEE80211_FC0_SUBTYPE_PS_POLL)) buf_hdr->type_flags |= ZYD_TX_FLAG_TYPE(ZYD_TX_TYPE_PS_POLL); if (ZYD_RATE_IS_OFDM(rate)) { buf_hdr->rate_mod_flags |= ZYD_TX_RMF_OFDM; if (ic->ic_curmode == IEEE80211_MODE_11A) buf_hdr->rate_mod_flags |= ZYD_TX_RMF_5GHZ; } else if (rate != 2 && (ic->ic_flags & IEEE80211_F_SHPREAMBLE)) buf_hdr->rate_mod_flags |= ZYD_TX_RMF_SH_PREAMBLE; /* * Compute frame duration and length-extension service flag. */ service = 0x00; buf_hdr->frame_duration = LE_16((16 * frame_size + rate - 1) / rate); buf_hdr->service = LE_8(service); buf_hdr->next_frame_duration = LE_16(0); if (rate == 22) { const int remainder = (16 * frame_size) % 22; if (remainder != 0 && remainder < 7) buf_hdr->service |= ZYD_TX_SERVICE_LENGTH_EXTENSION; } /* Copy the 802.11 frame */ mcopymsg(m, buf_data); /* Send it to the device */ zyd_usb_send_packet(&sc->usb, buffer, total_len); ic->ic_stats.is_tx_frags++; ic->ic_stats.is_tx_bytes += buf_hdr->packet_size; /* Free memory */ ieee80211_free_node(in); kmem_free(buffer, total_len); buffer = NULL; buf_hdr = NULL; buf_data = NULL; return (ZYD_SUCCESS); } const char * zyd_rf_name(uint8_t type) { static const char * const zyd_rfs[] = { "unknown", "unknown", "UW2451", "UCHIP", "AL2230", "AL7230B", "THETA", "AL2210", "MAXIM_NEW", "GCT", "PV2000", "RALINK", "INTERSIL", "RFMD", "MAXIM_NEW2", "PHILIPS" }; return zyd_rfs[(type > 15) ? 0 : type]; } zyd_mac_rev_t zyd_hw_lookup(uint16_t vendor, uint16_t product) { ZYD_DEBUG("looking up chip type (ZD1211/ZD1211B) for usb%x,%x", vendor, product); return (zyd_usb_mac_rev(vendor, product)); }