3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

OpenBSDのif_urtwn.cを読む

Last updated at Posted at 2016-07-18

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処理はドライバより上側で処理する構成でした。

ソースコード

対応チップ

  • 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);
}
3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?