OpenBSDのif_rsu.cを読んでみました。
if_rsu.cはRealtekのWi-Fiチップ用のデバイスドライバのソースコードです。
実装されている機能がとても少ないので読みやすいです。
対象
if_rsu.cの対応機能
- 802.11n 802.11g 802.11b
- STA
- 暗号化なし、WEP、WPA2 (暗号化はドライバ側で行う)
- 802.11e のQoS
- USB
対応チップ
- RTL8171
- RTL8172
- RTL8173
- RTL8174
- RTL8712
- RTL8713
- RTL8188SU
- RTL8191SU
- RTL8192SU
USBのエンドポイント
-
ディスクリプタのid->bNumEndpointsの数字によって異なり、変換テーブルを使ってqidからエンドポイントの数字を導出する。
-
4
-
rsu_qid2idx_4ep[] = { 3, 3, 3, 1, 1, 2, 2, 0, 3, 0 };
-
6
-
rsu_qid2idx_6ep[] = { 3, 3, 3, 1, 4, 2, 5, 0, 3, 0 };
-
11
-
rsu_qid2idx_11ep[] = { 7, 9, 8, 1, 4, 2, 5, 0, 3, 6 };
-
qidの定数
/* Queue ids (used by soft only). */
# define RSU_QID_BCN 0
# define RSU_QID_MGT 1
# define RSU_QID_BMC 2
# define RSU_QID_VO 3
# define RSU_QID_VI 4
# define RSU_QID_BE 5
# define RSU_QID_BK 6
# define RSU_QID_RXOFF 7
# define RSU_QID_H2C 8
# define RSU_QID_C2H 9
- 使用するエンドポイント
- 1 : データ送信 ( RSU_QID_BE )
- 3 : コマンド送信 ( RSU_QID_H2C )
コマンド
コマンド送信
- Tx descriptorを用意
- command heeadrを用意
- command payloadを用意
- RSU_QID_H2Cに対応するendpoint 3に usbd_setup_xfer() usbd_transfer()で送信。
int
rsu_fw_cmd(struct rsu_softc *sc, uint8_t code, void *buf, int len)
{
struct rsu_tx_data *data;
struct r92s_tx_desc *txd;
struct r92s_fw_cmd_hdr *cmd;
struct usbd_pipe *pipe;
int cmdsz, xferlen;
data = sc->fwcmd_data;
/* Round-up command length to a multiple of 8 bytes. */
cmdsz = (len + 7) & ~7;
xferlen = sizeof(*txd) + sizeof(*cmd) + cmdsz;
KASSERT(xferlen <= RSU_TXBUFSZ);
memset(data->buf, 0, xferlen);
/* Setup Tx descriptor. */
txd = (struct r92s_tx_desc *)data->buf;
txd->txdw0 = htole32(
SM(R92S_TXDW0_OFFSET, sizeof(*txd)) |
SM(R92S_TXDW0_PKTLEN, sizeof(*cmd) + cmdsz) |
R92S_TXDW0_OWN | R92S_TXDW0_FSG | R92S_TXDW0_LSG);
txd->txdw1 = htole32(SM(R92S_TXDW1_QSEL, R92S_TXDW1_QSEL_H2C));
/* Setup command header. */
cmd = (struct r92s_fw_cmd_hdr *)&txd[1];
cmd->len = htole16(cmdsz);
cmd->code = code;
cmd->seq = sc->cmd_seq;
sc->cmd_seq = (sc->cmd_seq + 1) & 0x7f;
/* Copy command payload. */
memcpy(&cmd[1], buf, len);
DPRINTFN(2, ("Tx cmd code=%d len=%d\n", code, cmdsz));
pipe = sc->pipe[sc->qid2idx[RSU_QID_H2C]];
usbd_setup_xfer(data->xfer, pipe, NULL, data->buf, xferlen,
USBD_SHORT_XFER_OK | USBD_NO_COPY | USBD_SYNCHRONOUS,
RSU_CMD_TIMEOUT, NULL);
return (usbd_transfer(data->xfer));
}
コマンドの種類
- ソースコード内で使っているコマンド
# define R92S_CMD_JOIN_BSS 14
# define R92S_CMD_DISCONNECT 15
# define R92S_CMD_SET_OPMODE 17
# define R92S_CMD_SITE_SURVEY 18
# define R92S_CMD_SET_AUTH 19
# define R92S_CMD_SET_KEY 20
# define R92S_CMD_SET_PWR_MODE 36
# define R92S_CMD_SET_MAC_ADDRESS 58
APに接続
- R92S_CMD_SET_OPMODEコマンドを送信
- R92S_CMD_SET_AUTHコマンドを送信 (WPA or Open)
- 引数niから接続先APの情報をbssに用意
- 付加するIEの情報をfrmに用意。
- R92S_CMD_JOIN_BSSコマンド
int
rsu_join_bss(struct rsu_softc *sc, struct ieee80211_node *ni)
{
struct ieee80211com *ic = &sc->sc_ic;
struct ndis_wlan_bssid_ex *bss;
struct ndis_802_11_fixed_ies *fixed;
struct r92s_fw_cmd_auth auth;
uint8_t buf[sizeof(*bss) + 128], *frm;
uint8_t opmode;
int error;
/* Let the FW decide the opmode based on the capinfo field. */
opmode = NDIS802_11AUTOUNKNOWN;
DPRINTF(("setting operating mode to %d\n", opmode));
error = rsu_fw_cmd(sc, R92S_CMD_SET_OPMODE, &opmode, sizeof(opmode));
if (error != 0)
return (error);
memset(&auth, 0, sizeof(auth));
if (ic->ic_flags & IEEE80211_F_RSNON) {
auth.mode = R92S_AUTHMODE_WPA;
auth.dot1x = ieee80211_is_8021x_akm(ni->ni_rsnakms);
} else
auth.mode = R92S_AUTHMODE_OPEN;
DPRINTF(("setting auth mode to %d\n", auth.mode));
error = rsu_fw_cmd(sc, R92S_CMD_SET_AUTH, &auth, sizeof(auth));
if (error != 0)
return (error);
memset(buf, 0, sizeof(buf));
bss = (struct ndis_wlan_bssid_ex *)buf;
IEEE80211_ADDR_COPY(bss->macaddr, ni->ni_bssid);
bss->ssid.ssidlen = htole32(ni->ni_esslen);
memcpy(bss->ssid.ssid, ni->ni_essid, ni->ni_esslen);
if (ic->ic_flags & (IEEE80211_F_WEPON | IEEE80211_F_RSNON))
bss->privacy = htole32(1);
bss->rssi = htole32(ni->ni_rssi);
if (ic->ic_curmode == IEEE80211_MODE_11B)
bss->networktype = htole32(NDIS802_11DS);
else
bss->networktype = htole32(NDIS802_11OFDM24);
bss->config.len = htole32(sizeof(bss->config));
bss->config.bintval = htole32(ni->ni_intval);
bss->config.dsconfig = htole32(ieee80211_chan2ieee(ic, ni->ni_chan));
bss->inframode = htole32(NDIS802_11INFRASTRUCTURE);
memcpy(bss->supprates, ni->ni_rates.rs_rates,
ni->ni_rates.rs_nrates);
/* Write the fixed fields of the beacon frame. */
fixed = (struct ndis_802_11_fixed_ies *)&bss[1];
memcpy(&fixed->tstamp, ni->ni_tstamp, 8);
fixed->bintval = htole16(ni->ni_intval);
fixed->capabilities = htole16(ni->ni_capinfo);
/* Write IEs to be included in the association request. */
frm = (uint8_t *)&fixed[1];
if ((ic->ic_flags & IEEE80211_F_RSNON) &&
(ni->ni_rsnprotos & IEEE80211_PROTO_RSN))
frm = ieee80211_add_rsn(frm, ic, ni);
if (ni->ni_flags & IEEE80211_NODE_QOS)
frm = ieee80211_add_qos_capability(frm, ic);
if (ni->ni_flags & IEEE80211_NODE_QOS)
frm = ieee80211_add_qos_capability(frm, ic);
if (ni->ni_flags & IEEE80211_NODE_HT)
frm = ieee80211_add_htcaps(frm, ic);
if ((ic->ic_flags & IEEE80211_F_RSNON) &&
(ni->ni_rsnprotos & IEEE80211_PROTO_WPA))
frm = ieee80211_add_wpa(frm, ic, ni);
bss->ieslen = htole32(frm - (uint8_t *)fixed);
bss->len = htole32(((frm - buf) + 3) & ~3);
DPRINTF(("sending join bss command to %s chan %d\n",
ether_sprintf(bss->macaddr), letoh32(bss->config.dsconfig)));
return (rsu_fw_cmd(sc, R92S_CMD_JOIN_BSS, buf, sizeof(buf)));
}
APから切断
- R92S_CMD_DISCONNECTコマンドを送信
int
rsu_disconnect(struct rsu_softc *sc)
{
uint32_t zero = 0; /* :-) */
/* Disassociate from our current BSS. */
DPRINTF(("sending disconnect command\n"));
return (rsu_fw_cmd(sc, R92S_CMD_DISCONNECT, &zero, sizeof(zero)));
}
データ送信
- WPA有効なら、ieee80211_get_txkey()の鍵を使ってieee80211_encrypt()で暗号化する。
- endpointを選択。QoSを使わないなら RSU_QID_BEに対応するEndpoint 0を選択。
- Tx descriptorを用意
- 送信データを用意
- usbd_setup_xfer() usbd_transfer()で送信。
int
rsu_tx(struct rsu_softc *sc, struct mbuf *m, struct ieee80211_node *ni)
{
struct ieee80211com *ic = &sc->sc_ic;
struct ieee80211_frame *wh;
struct ieee80211_key *k = NULL;
struct rsu_tx_data *data;
struct r92s_tx_desc *txd;
struct usbd_pipe *pipe;
uint16_t qos;
uint8_t type, qid, tid = 0;
int hasqos, xferlen, error;
wh = mtod(m, struct ieee80211_frame *);
type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
k = ieee80211_get_txkey(ic, wh, ni);
if ((m = ieee80211_encrypt(ic, m, k)) == NULL)
return (ENOBUFS);
wh = mtod(m, struct ieee80211_frame *);
}
if ((hasqos = ieee80211_has_qos(wh))) {
qos = ieee80211_get_qos(wh);
tid = qos & IEEE80211_QOS_TID;
qid = rsu_ac2qid[ieee80211_up_to_ac(ic, tid)];
} else
qid = RSU_QID_BE;
/* Get the USB pipe to use for this queue id. */
pipe = sc->pipe[sc->qid2idx[qid]];
/* Grab a Tx buffer from our free list. */
data = TAILQ_FIRST(&sc->tx_free_list);
TAILQ_REMOVE(&sc->tx_free_list, data, next);
/* Fill Tx descriptor. */
txd = (struct r92s_tx_desc *)data->buf;
memset(txd, 0, sizeof(*txd));
txd->txdw0 |= htole32(
SM(R92S_TXDW0_PKTLEN, m->m_pkthdr.len) |
SM(R92S_TXDW0_OFFSET, sizeof(*txd)) |
R92S_TXDW0_OWN | R92S_TXDW0_FSG | R92S_TXDW0_LSG);
txd->txdw1 |= htole32(
SM(R92S_TXDW1_MACID, R92S_MACID_BSS) |
SM(R92S_TXDW1_QSEL, R92S_TXDW1_QSEL_BE));
if (!hasqos)
txd->txdw1 |= htole32(R92S_TXDW1_NONQOS);
# ifdef notyet
if (k != NULL) {
switch (k->k_cipher) {
case IEEE80211_CIPHER_WEP40:
case IEEE80211_CIPHER_WEP104:
cipher = R92S_TXDW1_CIPHER_WEP;
break;
case IEEE80211_CIPHER_TKIP:
cipher = R92S_TXDW1_CIPHER_TKIP;
break;
case IEEE80211_CIPHER_CCMP:
cipher = R92S_TXDW1_CIPHER_AES;
break;
default:
cipher = R92S_TXDW1_CIPHER_NONE;
}
txd->txdw1 |= htole32(
SM(R92S_TXDW1_CIPHER, cipher) |
SM(R92S_TXDW1_KEYIDX, k->k_id));
}
# endif
txd->txdw2 |= htole32(R92S_TXDW2_BK);
if (IEEE80211_IS_MULTICAST(wh->i_addr1))
txd->txdw2 |= htole32(R92S_TXDW2_BMCAST);
/*
* Firmware will use and increment the sequence number for the
* specified TID.
*/
txd->txdw3 |= htole32(SM(R92S_TXDW3_SEQ, tid));
# if NBPFILTER > 0
if (__predict_false(sc->sc_drvbpf != NULL)) {
struct rsu_tx_radiotap_header *tap = &sc->sc_txtap;
struct mbuf mb;
tap->wt_flags = 0;
tap->wt_chan_freq = htole16(ic->ic_bss->ni_chan->ic_freq);
tap->wt_chan_flags = htole16(ic->ic_bss->ni_chan->ic_flags);
mb.m_data = (caddr_t)tap;
mb.m_len = sc->sc_txtap_len;
mb.m_next = m;
mb.m_nextpkt = NULL;
mb.m_type = 0;
mb.m_flags = 0;
bpf_mtap(sc->sc_drvbpf, &mb, BPF_DIRECTION_OUT);
}
# endif
xferlen = sizeof(*txd) + m->m_pkthdr.len;
m_copydata(m, 0, m->m_pkthdr.len, (caddr_t)&txd[1]);
m_freem(m);
data->pipe = pipe;
usbd_setup_xfer(data->xfer, pipe, data, data->buf, xferlen,
USBD_FORCE_SHORT_XFER | USBD_NO_COPY, RSU_TX_TIMEOUT,
rsu_txeof);
error = usbd_transfer(data->xfer);
if (__predict_false(error != USBD_IN_PROGRESS && error != 0)) {
/* Put this Tx buffer back to our free list. */
TAILQ_INSERT_TAIL(&sc->tx_free_list, data, next);
return (error);
}
ieee80211_release_node(ic, ni);
return (0);
}