OpenBSDのif_urtwn.c を読んでみました。
if_urtwn.cはRealtekのWi-Fiチップのデバイスドライバです。
前回 「OpenBSDのif_rsu.cを読む」でif_rsu.cをちょっと読んでみたので、if_urwtn.cも読んでみました。
if_urtwn.c はsoftmacなドライバなので、AP接続などのMAC処理はドライバより上側で処理する構成でした。
ソースコード
- http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/sys/dev/usb/if_urtwn.c?rev=1.65&content-type=text/x-cvsweb-markup
- http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/sys/dev/ic/rtwn.c?rev=1.9&content-type=text/x-cvsweb-markup
- rtwn.cはホストインタフェースに依存しない部分のコードです。PCIE, CardBus、USBで共通なコードです。
- rsu.cに比べて、ソースコードの行数が倍くらいになっています。 でも、Linuxドライバよりは全然コードが少ないので、楽に読めます。
対応チップ
- RTL8188CTV
- RTL8188CU
- RTL8188CUS
- RTL8188ETV
- RTL8188EU
- RTL8192CU
エンドポイント
送信エンドポイント
- データ送信用にエンドポイントを使う。
- 11eのACに応じてendpointを選択する。
- 送信エンドポイントの総数ntxはチップによって異なる。
/* Map 802.11 access categories to USB pipes. */
sc->ac2idx[EDCA_AC_BK] =
sc->ac2idx[EDCA_AC_BE] = (ntx == 3) ? 2 : ((ntx == 2) ? 1 : 0);
sc->ac2idx[EDCA_AC_VI] = (ntx == 3) ? 1 : 0;
sc->ac2idx[EDCA_AC_VO] = 0; /* Always use highest prio. */
受信エンドポイント
- 不明
コマンド
- コマンドはコントロール転送を使う。
コマンド送信
- コマンドを書き込むアドレスはsc->fwcurで管理
- コマンド送信可能かどうかを確認。
- コマンドを用意
- rtwn_write_2()とrtwn_write_4()で計6バイトを書き込み
- sc->fwcurをアップデート
int
rtwn_fw_cmd(struct rtwn_softc *sc, uint8_t id, const void *buf, int len)
{
struct r92c_fw_cmd cmd;
int ntries;
/* Wait for current FW box to be empty. */
for (ntries = 0; ntries < 100; ntries++) {
if (!(rtwn_read_1(sc, R92C_HMETFR) & (1 << sc->fwcur)))
break;
DELAY(1);
}
if (ntries == 100) {
printf("%s: could not send firmware command %d\n",
sc->sc_pdev->dv_xname, id);
return (ETIMEDOUT);
}
memset(&cmd, 0, sizeof(cmd));
cmd.id = id;
if (len > 3)
cmd.id |= R92C_CMD_FLAG_EXT;
KASSERT(len <= sizeof(cmd.msg));
memcpy(cmd.msg, buf, len);
/* Write the first word last since that will trigger the FW. */
rtwn_write_2(sc, R92C_HMEBOX_EXT(sc->fwcur), *((uint8_t *)&cmd + 4));
rtwn_write_4(sc, R92C_HMEBOX(sc->fwcur), *((uint8_t *)&cmd + 0));
sc->fwcur = (sc->fwcur + 1) % R92C_H2C_NBOX;
if (sc->chip & RTWN_CHIP_PCI) {
/* Give firmware some time for processing. */
DELAY(2000);
}
return (0);
}
APに接続
- このドライバでは、APに接続する処理は実装されていない。
- ということは、APに接続する処理はカーネルのieee80211レイヤでソフト的に実装されているようです。
- ieee80211_proto.c にAuthフレーム、Association Reqフレーム、Association Respフレームの処理があるので、そちらで行っているようです。
データ送信
- WPAなら ieee80211_encrypt()で暗号化する
- qidからendpointを選択
- TXバッファから取得
- TXデスクリプタを用意
- マルチキャストなら、マルチキャスト送信用の設定をする。(APのみ)
- TXデスクリプタにシーケンス番号を付加
- 送信データをバッファにコピー
- usbd_setup_xfer()とusbd_transfer()でUSBに転送する。
int
urtwn_tx(void *cookie, struct mbuf *m, struct ieee80211_node *ni)
{
struct urtwn_softc *sc = cookie;
struct ieee80211com *ic = &sc->sc_sc.sc_ic;
struct ieee80211_frame *wh;
struct ieee80211_key *k = NULL;
struct urtwn_tx_data *data;
struct r92c_tx_desc_usb *txd;
struct usbd_pipe *pipe;
uint16_t qos, sum;
uint8_t raid, type, tid, qid;
int i, 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 = ieee80211_up_to_ac(ic, tid);
} else if (type != IEEE80211_FC0_TYPE_DATA) {
/* Use AC VO for management frames. */
qid = EDCA_AC_VO;
} else
qid = EDCA_AC_BE;
/* Get the USB pipe to use for this AC. */
pipe = sc->tx_pipe[sc->ac2idx[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 r92c_tx_desc_usb *)data->buf;
memset(txd, 0, sizeof(*txd));
txd->txdw0 |= htole32(
SM(R92C_TXDW0_PKTLEN, m->m_pkthdr.len) |
SM(R92C_TXDW0_OFFSET, sizeof(*txd)) |
R92C_TXDW0_OWN | R92C_TXDW0_FSG | R92C_TXDW0_LSG);
if (IEEE80211_IS_MULTICAST(wh->i_addr1))
txd->txdw0 |= htole32(R92C_TXDW0_BMCAST);
# ifdef notyet
if (k != NULL) {
switch (k->k_cipher) {
case IEEE80211_CIPHER_WEP40:
case IEEE80211_CIPHER_WEP104:
case IEEE80211_CIPHER_TKIP:
cipher = R92C_TXDW1_CIPHER_RC4;
break;
case IEEE80211_CIPHER_CCMP:
cipher = R92C_TXDW1_CIPHER_AES;
break;
default:
cipher = R92C_TXDW1_CIPHER_NONE;
}
txd->txdw1 |= htole32(SM(R92C_TXDW1_CIPHER, cipher));
}
# endif
if (!IEEE80211_IS_MULTICAST(wh->i_addr1) &&
type == IEEE80211_FC0_TYPE_DATA) {
if (ic->ic_curmode == IEEE80211_MODE_11B ||
(sc->sc_sc.sc_flags & RTWN_FLAG_FORCE_RAID_11B))
raid = R92C_RAID_11B;
else
raid = R92C_RAID_11BG;
if (sc->sc_sc.chip & RTWN_CHIP_88E) {
txd->txdw1 |= htole32(
SM(R88E_TXDW1_MACID, R92C_MACID_BSS) |
SM(R92C_TXDW1_QSEL, R92C_TXDW1_QSEL_BE) |
SM(R92C_TXDW1_RAID, raid));
txd->txdw2 |= htole32(R88E_TXDW2_AGGBK);
} else {
txd->txdw1 |= htole32(
SM(R92C_TXDW1_MACID, R92C_MACID_BSS) |
SM(R92C_TXDW1_QSEL, R92C_TXDW1_QSEL_BE) |
SM(R92C_TXDW1_RAID, raid) | R92C_TXDW1_AGGBK);
}
if (ic->ic_flags & IEEE80211_F_USEPROT) {
if (ic->ic_protmode == IEEE80211_PROT_CTSONLY) {
txd->txdw4 |= htole32(R92C_TXDW4_CTS2SELF |
R92C_TXDW4_HWRTSEN);
} else if (ic->ic_protmode == IEEE80211_PROT_RTSCTS) {
txd->txdw4 |= htole32(R92C_TXDW4_RTSEN |
R92C_TXDW4_HWRTSEN);
}
}
/* Send RTS at OFDM24. */
txd->txdw4 |= htole32(SM(R92C_TXDW4_RTSRATE, 8));
txd->txdw5 |= htole32(0x0001ff00);
/* Send data at OFDM54. */
txd->txdw5 |= htole32(SM(R92C_TXDW5_DATARATE, 11));
} else {
txd->txdw1 |= htole32(
SM(R92C_TXDW1_MACID, 0) |
SM(R92C_TXDW1_QSEL, R92C_TXDW1_QSEL_MGNT) |
SM(R92C_TXDW1_RAID, R92C_RAID_11B));
/* Force CCK1. */
txd->txdw4 |= htole32(R92C_TXDW4_DRVRATE);
txd->txdw5 |= htole32(SM(R92C_TXDW5_DATARATE, 0));
}
/* Set sequence number (already little endian). */
txd->txdseq |= *(uint16_t *)wh->i_seq;
if (!hasqos) {
/* Use HW sequence numbering for non-QoS frames. */
txd->txdw4 |= htole32(R92C_TXDW4_HWSEQ);
txd->txdseq |= htole16(0x8000); /* WTF? */
} else
txd->txdw4 |= htole32(R92C_TXDW4_QOS);
/* Compute Tx descriptor checksum. */
sum = 0;
for (i = 0; i < sizeof(*txd) / 2; i++)
sum ^= ((uint16_t *)txd)[i];
txd->txdsum = sum; /* NB: already little endian. */
# if NBPFILTER > 0
if (__predict_false(sc->sc_drvbpf != NULL)) {
struct urtwn_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, URTWN_TX_TIMEOUT,
urtwn_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);
}