LoginSignup
2
1

More than 5 years have passed since last update.

OpenBSDのif_rsu.cを読む

Last updated at Posted at 2016-07-16

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);
}
2
1
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
2
1