Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

OpenBSDのif_urtwn.cを読む

More than 3 years have passed since last update.

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);
}
eggman
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away