Annotation of wikisrc/Converting_drivers_to_the_new_wifi_stack.mdwn, revision 1.11

1.1       wiki        1: # Intro
                      2: 
                      3:  This guide is meant to help you convert an old style NetBSD driver
                      4: for a wifi device to the newer wifi stack based on FreeBSD's current
                      5: version.
                      6: 
                      7:  This is work in progress, as we are trying to create helper functions
                      8: and libraries where usefull, and also the target API is a slightly
                      9: moving target.
                     10: 
                     11: # Overview
                     12: 
                     13:  The main difference between old style and new style drivers is the
                     14: VAP concept. VAP means Virtual Access Point, and it is used to split
                     15: the configuration properties of a wlan (as visible on air) from the
                     16: radio hardware. In many cases multiple wlans can be created using a
                     17: single radio hardware, which is naturally modelled as multiple VAPs
                     18: being created (by pure software) using the same physical radio device.
                     19: 
                     20:  At the user level the radio hardware is hidden quite well - it only
1.6       wiki       21: shows up in `dmesg` or `sysctl net.wlan.devices`.
1.1       wiki       22: There is no network interface for the raw hardware, so ifconfig will
                     23: not show them.
                     24: 
                     25:  To create a VAP for a radio device (and that way a network interface)
                     26: use a command like:
                     27: 
                     28:     ifconfig wlan0 create wlandev run0
                     29: 
                     30:  This split between common radio hardware and per-VAP network properties
                     31: reflects on the driver and causes most of the changes needed.
                     32: 
1.6       wiki       33:  After creation of the `wlan0` VAP, it will also show up under
1.1       wiki       34: 
1.4       wiki       35:     sysctl net.wlan.wlan0
1.1       wiki       36: 
1.4       wiki       37:  and in ifconfig output.
1.1       wiki       38: 
1.3       wiki       39:   More complete instructions for testing are here: [[Testing new wifi]].
                     40:   There also is an older paper describing the initial design in FreeBSD:
                     41:   [FreeBSD wireless BSDCan 2005](https://www.bsdcan.org/2005/papers/FreeBSDWirelessNetwokringSupport.pdf)
                     42: 
1.1       wiki       43: # Step by Step at the driver level
                     44: 
1.6       wiki       45:  * The data is split into the generic `struct ieee80211com`
                     46:    describing the radio hardware (often used as `ic` pointer),
1.1       wiki       47:    which is part of the drivers softc. The individual VAPs are
                     48:    in a TAILQ in the ic structure, and often are a driver defined
1.6       wiki       49:    structure 'derived' from `struct ieee80211vap` (with some
1.1       wiki       50:    additional driver specific data). The VAP structure has the
1.6       wiki       51:    network interface `struct ifnet` as `iv_ifp`.
                     52:    The `if_softc` pointer in the ifnet structure points to the VAP, the VAP structure
                     53:    has a pointer to the ic `iv_ic`, the ic structure has a `ic_softc` pointer to the softc.
1.1       wiki       54: 
                     55:  * MAC addresses are now stored in ic_macaddr instead of ic_myaddr
                     56:    (in struct ieee80211com). It is typically read from the device
                     57:    and cloned into each VAP. Typical code:
                     58: 
                     59:     IEEE80211_ADDR_COPY(ic->ic_macaddr, rom->macaddr);
                     60: 
1.6       wiki       61:  * In the attach function, no `struct ifnet ifp` will be needed,
1.1       wiki       62:    so get rid of code like:
                     63: 
                     64:      struct ifnet *ifp = GET_IFP(sc);
                     65: 
                     66:    and all initialization of ifp members. Move them to the vap_create
                     67:    function (see below) if needed instead, but typically none
                     68:    should be needed (as the generic code initializes the VAP's
                     69:    struct ifnet).
                     70: 
                     71:    Set a direct backlink to the softc, like:
                     72: 
                     73:     ic->ic_softc = sc;
                     74: 
                     75:    Make sure the ic_caps get initialized correctly to the capabilities
                     76:    of the radio, especially you want to add IEEE80211_C_STA for
                     77:    standard AP (station) mode.
                     78: 
                     79:    There is no statemachine in the common radio part, it all moves into
                     80:    per-VAP state, so also remove initialization for it, like:
                     81: 
                     82:     ic->ic_state = IEEE80211_S_INIT;
                     83: 
                     84:    You may want a replacement for IFF_OACATIVE logic for the main device,
                     85:    as there is no interface so no flags to (ab)use for that.
                     86: 
                     87:    Note that the way channels are set up has changed.
                     88:    If your driver had a driver_init_channels() function, you can
1.6       wiki       89:    use it mostly verbatim. Rename it to `driver_get_radiocaps` and
1.1       wiki       90:    make it look like:
                     91: 
1.2       wiki       92:     ```
1.1       wiki       93:     static void
                     94:     urtwn_get_radiocaps(struct ieee80211com *ic,
                     95:         int maxchans, int *nchans, struct ieee80211_channel chans[])
                     96:     {
                     97:         uint8_t bands[IEEE80211_MODE_BYTES];
                     98:         
                     99:         memset(bands, 0, sizeof(bands));
                    100:         setbit(bands, IEEE80211_MODE_11B);
                    101:         setbit(bands, IEEE80211_MODE_11G);
                    102:         setbit(bands, IEEE80211_MODE_11NG);
1.10      wiki      103:         // XXX see description below if your chip can use other ranges (like 5ghz)
1.1       wiki      104:         ieee80211_add_channels_default_2ghz(chans, maxchans, nchans, bands, 0);
                    105:     }
1.2       wiki      106:     ```
1.1       wiki      107: 
1.6       wiki      108:    and also assign this function to `ic->ic_getradiocaps` in
1.1       wiki      109:    the attach function.
                    110: 
1.6       wiki      111:    An initial setup must happen before calling `ieee80211_ifattach(ic)`.
                    112:    You can just call the `driver_get_radiocaps()` function during
1.1       wiki      113:    attach like:
                    114: 
1.2       wiki      115:     ```
                    116:     urtwn_get_radiocaps(ic, IEEE80211_CHAN_MAX, &ic->ic_nchans,   
                    117:          ic->ic_channels);
                    118:     ieee80211_ifattach(ic);
                    119:     [..]
                    120:     ic->ic_getradiocaps = urtwn_get_radiocaps;
                    121:     ```
1.1       wiki      122: 
1.10      wiki      123:    If the hardware can use different frequency bands you may have to add them
                    124:    band by band instead of using `ieee80211_add_channels_default_2ghz`, also if
                    125:    the firmware knows about regulatory domains it may be required to select
                    126:    subsets of channels.
                    127: 
                    128:    An example of a multi-frequency driver doing this is iwm(4).
                    129: 
1.11    ! wiki      130:    It uses a helper function to add single channels individually
        !           131:    via ``ieee80211_add_channel``.
        !           132:    To add the channels for a band, it calls
1.10      wiki      133:    ``iwm_add_channel_band(ic, chans, maxchans, nchans, start, num, bands)``
                    134:    where ``chans``, ``maxchans`` and ``nchans`` are the arguments passed to
                    135:    this callback, chans collecting the resulting channels, nchans pointing
                    136:    to the number of channels already collected, ``bands`` is the bitset of
                    137:    bands to add (multiple IEEE80211_MODE_* bits). The offset ``start``
                    138:    is the index of the first channel to add to the output (first call will
                    139:    always be with 0 as ``start``), and ``num`` the number of channels
1.11    ! wiki      140:    you expect ``iwm_add_channel_band`` to add. The ``iwm_add_channel_band``
        !           141:    helper function iterates over the channels, gets flags for the channels
        !           142:    from firmware and calls ``ieee80211_add_channel``.
1.10      wiki      143: 
1.1       wiki      144:  * If your driver used to override state machine functions (typical
1.6       wiki      145:    `newstate` to controll LEDs when associating or similar),
1.1       wiki      146:    remove all traces for this from your softc and override a
                    147:    custom VAP type instead (i.e. derive a struct from
                    148:    struct ieee80211vap and use your own struct in your vap_create
                    149:    and vap_delete method).
                    150: 
                    151:    Remove the (likely to exist) old sc_newstate function from
                    152:    your drivers softc (previously used to store the frameworks
                    153:    newstate function, which now is per VAP and moved into your
                    154:    derived per-VAP structure.
                    155: 
1.6       wiki      156:  * Many functions will get a `struct ieee80211vap *vap` passed instead
1.1       wiki      157:    of a softc. A typical change to adapt for that looks like:
                    158: 
1.2       wiki      159:     ```
1.1       wiki      160:     -       struct ieee80211com *ic = &sc->sc_ic;
                    161:     -       struct ieee80211_node *ni = ic->ic_bss;
                    162:     +       struct ieee80211com *ic = vap->iv_ic;
                    163:     +       struct rtwn_softc *sc = ic->ic_softc;
                    164:     +       struct ieee80211_node *ni = vap->iv_bss;
1.2       wiki      165:     ```
1.1       wiki      166: 
                    167:  * The hardware driver will need a global new outgoing packet queue
                    168:    (as the per-interface queues are attached to VAPs, not the single
                    169:    common transmit hardware). Add the new ifque like to your softc
                    170:    like this:
                    171: 
                    172:     struct ifqueue sc_sendq;
                    173: 
                    174:    While there, check that there is no "struct ethercom sc_ec" in your
                    175:    softc - it is not needed.
                    176: 
                    177:    This queue needs initialization in the attach function, like:
                    178: 
1.2       wiki      179:     ```
1.1       wiki      180:     sc->sc_sendq.ifq_maxlen = ifqmaxlen;
                    181:     IFQ_LOCK_INIT(&sc->sc_sendq);
1.2       wiki      182:     ```
1.1       wiki      183: 
                    184:    This device private send queue (writer: any of the current VAPs
                    185:    of this interface, consumer: the devices transmit interrupt handler)
                    186:    abuses the mbuf rcvif pointer to store the node pointer
1.6       wiki      187:    `struct ieee80211_node *` of the target node for this packet.
1.1       wiki      188:    When dequeing a packet before handing it over to the output function,
                    189:    the node is extracted and the rcvif pointer set to NULL.
                    190:    Yes - this is a hack.
                    191: 
                    192:    Also the attach function needs to set the name of the interface
                    193:    in the common radio device structure:
                    194: 
                    195:     ic->ic_name = device_xname(self);
                    196: 
                    197:    Many drivers query the hardware/firmware for the number of available
1.6       wiki      198:    input / output queues and store these in e.g. `sc->ntxchains`
                    199:    and `sc->nrxchains` or similar variables. This information
1.1       wiki      200:    needs to be propagated to the common radio structure:
                    201: 
1.2       wiki      202:     ```
1.1       wiki      203:     ic->ic_txstream = sc->ntxchains;
                    204:     ic->ic_rxstream = sc->nrxchains;
1.2       wiki      205:     ```
1.1       wiki      206: 
                    207:    Same for interface flags:
                    208: 
                    209:     ic->ic_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
                    210: 
1.6       wiki      211:    After calling `ieee80211_ifattach(ic);` some methods in ic
1.1       wiki      212:    need to be overwritten. The functions needed are explained
                    213:    below, here is a typical setup:
                    214: 
1.2       wiki      215:     ```
1.1       wiki      216:     /* override default methods */
                    217:     ic->ic_newassoc = urtwn_newassoc;
                    218:     ic->ic_wme.wme_update = urtwn_wme_update;
                    219:     ic->ic_vap_create = urtwn_vap_create;
                    220:     ic->ic_vap_delete = urtwn_vap_delete;
                    221:     ic->ic_parent = urtwn_parent;
                    222:     ic->ic_scan_start = urtwn_scan_start;
                    223:     ic->ic_scan_end = urtwn_scan_end;
                    224:     ic->ic_set_channel = urtwn_set_channel;
                    225:     ic->ic_transmit = urtwn_transmit;
                    226:     ic->ic_raw_xmit = urtwn_raw_xmit;
1.2       wiki      227:     ```
1.1       wiki      228: 
                    229:  * detach does not deal with any interfaces any more, remove all traces
1.6       wiki      230:    of `struct ifnet *ifp`.
1.1       wiki      231: 
                    232:  * Add a new function for VAP creation and move all interface specific
                    233:    setup there. It looks like:
                    234: 
1.2       wiki      235:     ```
1.1       wiki      236:     static struct ieee80211vap *
                    237:     urtwn_vap_create(struct ieee80211com *ic,  const char name[IFNAMSIZ],
                    238:         int  unit, enum ieee80211_opmode opmode, int flags,
                    239:         const uint8_t bssid[IEEE80211_ADDR_LEN],
                    240:         const uint8_t macaddr[IEEE80211_ADDR_LEN])
                    241:     {
                    242:            struct urtwn_softc *sc = ic->ic_softc;
                    243:            struct ifnet *ifp;
                    244:            struct ieee80211vap *vap;
                    245:     ...
                    246:            return vap;
                    247:     }
1.2       wiki      248:     ```
1.1       wiki      249: 
1.6       wiki      250:    It allocates a new VAP with `kmem_zalloc` initializes it
                    251:    by calling `ieee80211_vap_setup` for it and then ovverriding whatever
                    252:    the driver needs, passes it to `ieee80211_vap_attach` and
1.1       wiki      253:    attaches bpf to the interface.
                    254: 
                    255:    If your interrupt handler is called in hard interrupt context
                    256:    (that probably means: for all devices that are not on USB)
                    257:    you need to initialize the per CPU interface queue:
                    258: 
1.2       wiki      259:     ```
1.1       wiki      260:     /* Use common softint-based if_input */
                    261:     ifp->if_percpuq = if_percpuq_create(ifp);
1.2       wiki      262:     ```
1.1       wiki      263: 
                    264:    If your interrupt handler always is called in softint context,
                    265:    do not do this. If if_percpuq is NULL the wifi framework will
                    266:    directly handle incoming packets, otherwise it will take a
                    267:    round trip via a per cpu softint handler.
                    268: 
                    269:  * Add a new function for VAP destruction and move interface specific
                    270:    parts that you deleted in the detach() function here. A very
                    271:    minimalistic example looks like:
                    272: 
1.2       wiki      273:     ```
1.1       wiki      274:     static void
                    275:     urtwn_vap_delete(struct ieee80211vap *vap)
                    276:     {
                    277:         struct ifnet *ifp = vap->iv_ifp;
1.5       wiki      278:         struct urtwn_softc *sc __unused = vap->iv_ic->ic_softc;
1.1       wiki      279: 
                    280:         bpf_detach(ifp);
                    281:         ieee80211_vap_detach(vap);
                    282:         kmem_free(vap, sizeof(struct ieee80211vap));
                    283:     }
1.2       wiki      284:     ```
1.1       wiki      285: 
                    286:  * Rate Adaption happens per VAP, so the ra_init call changes like this:
                    287: 
1.2       wiki      288:     ```
1.1       wiki      289:     -static int urtwn_ra_init(struct urtwn_softc *);
                    290:     +static int urtwn_ra_init(struct ieee80211vap );
1.2       wiki      291:     ```
1.1       wiki      292: 
                    293:    See above for recipes how to acces the needed structs in there.
                    294: 
                    295:  * State is per VAP, so newstate changes the first parameter:
                    296: 
1.2       wiki      297:     ```
1.1       wiki      298:     -static int urtwn_newstate(struct ieee80211com *, enum ieee80211_state, int);
                    299:     +static int urtwn_newstate(struct ieee80211vap *, enum ieee80211_state, int);
1.2       wiki      300:     ```
1.1       wiki      301: 
                    302:    If there was a newstate_cb function previously, it is probably not needed
                    303:    any more.
                    304: 
                    305:  * Reset is per interface, so per VAP:
                    306: 
1.2       wiki      307:     ```
1.1       wiki      308:     -static int urtwn_reset(struct ifnet *);
                    309:     +static int urtwn_reset(struct ieee80211vap *, u_long cmd);
1.2       wiki      310:     ```
1.1       wiki      311: 
                    312:    A driver is not required to provide this method, if not filled in a
                    313:    default method will be used that always causes a full reset of the
                    314:    device.
                    315: 
                    316:  * The set channel function is generic now, so gets passed a
1.6       wiki      317:    `struct ieee80211com *ic` instead of a softc, and sets
1.1       wiki      318:    the radio hardware to the current channel in that structure,
1.6       wiki      319:    `ic->ic_curchan`.
1.1       wiki      320: 
1.6       wiki      321:  * The start function does not need a `struct ifnet *ifp` any more,
1.9       wiki      322:    but can use a softc or `struct ieee80211com *ic` instead.
1.1       wiki      323: 
                    324:  * Typicall generic "media change" and "status" functions will
1.6       wiki      325:    be used: `ieee80211_media_change` and `ieee80211_media_status`.
1.1       wiki      326:    Old drivers often implement their own function - remove that (unless
                    327:    there is something actually chip specific in them).
                    328: 
                    329:    If there is, override media_change and/or media_status, and make
                    330:    sure to call the generic variant for all the real work. Also
1.6       wiki      331:    make sure to pass your override function to `ieee80211_vap_attach`.
1.1       wiki      332:    run(4) is an example of this.
                    333: 
                    334:    The overriden media_{change,status} functions are the only one
1.6       wiki      335:    in a wlan driver that should get a `struct ifnet *` argument
1.1       wiki      336:    passed. Checking for this is a good measurement to know you are
                    337:    done with conversion (besides the kernel build failing, of course).
                    338: 
                    339:  * Your driver needs a new transmit function, used to enqueue a
                    340:    mbuf to the hardware send queue (and start transmission if needed).
                    341:    A very simple example:
                    342: 
1.2       wiki      343:     ```
1.1       wiki      344:     static int
                    345:     urtwn_transmit(struct ieee80211com *ic, struct mbuf *m)
                    346:     {  
                    347:         struct urtwn_softc *sc = ic->ic_softc;
                    348:         int s;
                    349:         
                    350:         s = splnet();
                    351:         IF_ENQUEUE(&sc->sc_sendq, m);
                    352:         splx(s);
                    353: 
                    354:         if (!sc->sc_oactive)
                    355:                 urtwn_start(sc);
                    356: 
                    357:         return 0;
                    358:     }
1.2       wiki      359:     ```
1.1       wiki      360: 
                    361:    A pointer to this function is assigned to the ic->ic_transmit member
                    362:    in the attach function.
                    363: 
                    364:  * Your driver needs a raw_xmit function which is close to what
                    365:    the old driver called *_tx(), it looks like:
                    366: 
1.2       wiki      367:     ```
1.1       wiki      368:     static int
                    369:     urtwn_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
                    370:         const struct ieee80211_bpf_params *bpfp)
1.2       wiki      371:     ```
1.1       wiki      372: 
                    373:    and the "missing" arguments are derived like:
                    374: 
1.2       wiki      375:     ```
1.1       wiki      376:     struct ieee80211vap *vap = ni->ni_vap;
                    377:     struct ieee80211com *ic = ni->ni_ic;
                    378:     struct urtwn_softc *sc = ic->ic_softc;
1.2       wiki      379:     ```
1.1       wiki      380: 
1.6       wiki      381:    The check for encrypting packets often was testing the `ieee80211_frame'
                    382:    wh->i_fc[1] & IEEE80211_FC1_WE` before encapsulating the `mbuf`
                    383:    via `ieee80211_crypto_encap()`. The check now is for
                    384:    `wh->i_fc[1] & IEEE80211_FC1_PROTECTED`, since WEP is a thing of the
                    385:    past and there are other protocols. Also the `ieee80211_crypto_encap`
1.1       wiki      386:    function lost its first argument (the radio common ic pointer).
                    387: 
                    388:      XXXXX
                    389: 
1.6       wiki      390:    This function frees the `mbuf` and calls `ieee80211_free_node(ni)`.
1.1       wiki      391: 
                    392:  * If the driver_activate() function only passes deactivation requests
                    393:    on to if_deactivate() you can replace it by a shim that gets
                    394:    your softc and calls ieee80211_activate(), like:
                    395: 
1.2       wiki      396:     ```
1.1       wiki      397:     static int
                    398:     urtwn_activate(device_t self, enum devact act)
                    399:     {
                    400:         struct urtwn_softc *sc = device_private(self);
                    401:         
                    402:         return ieee80211_activate(&sc->sc_ic, act);
                    403:     }       
1.2       wiki      404:     ```
1.1       wiki      405: 
                    406:  * When the hardware received a full frame, move it into a mbuf
                    407:    and call (with proper frame bound checks)
                    408: 
1.2       wiki      409:     ```
1.1       wiki      410:     struct ieee80211_node *ni;
                    411: 
                    412:     // XXX unclear - same on NetBSD?
                    413:     if (ieee80211_radiotap_active(ic))
                    414:         mactime = rx->mactime;
                    415:     // or:
                    416:     if (ieee80211_radiotap_active(ic)) 
                    417:         struct urtw_rx_radiotap_header *tap = &sc->sc_rxtap;
                    418: 
                    419:         tap->wr_tsf = mactime;
                    420:         tap->wr_flags = 0;
                    421:         tap->wr_dbm_antsignal = (int8_t)rssi;
                    422:     }
                    423: 
                    424:     ni = ieee80211_find_rxnode(ic,
                    425:         mtod(m, struct ieee80211_frame_min *));
                    426:     if (ni != NULL) {
                    427:         ieee80211_input(ni, m, rssi, nf);
                    428:        ieee80211_free_node(ni);
                    429:     } else {
                    430:         ieee80211_input_all(ic, m, rssi, nf);
                    431:     }
1.2       wiki      432:     ```
1.1       wiki      433: 
                    434:    If a node is found, the data is passed on to that VAP, otherwise
                    435:    it is a general management packet.
                    436: 
                    437:    Old code often has tests for too short or too long packets and
                    438:    increments error counters in the struct ifnet of the interface
                    439:    that used to belong to the wlan driver. Since there is no such
                    440:    single interface anymore, errors are accounted later (at the VAP
                    441:    level) if a VAP/node is identified for the packet. Otherwise
                    442:    just increment the global ic->ic_ierrors counter for errors with
                    443:    incoming packets that can not be associated with a concrete
                    444:    network (or similar ic->ic_oerrors for output errors).
                    445:  
                    446:  * Watchdog functions
                    447:    Since there is no 1:1 ifnet and its global watchdog mechanism, all
                    448:    driver watchdog functions are converted to a callout.
                    449:    A typical implementation looks like:
                    450: 
1.2       wiki      451:     ```
1.1       wiki      452:     static void     
                    453:     urtwn_watchdog(void *arg)
                    454:     {
                    455:         struct urtwn_softc *sc = arg;
                    456:        struct ieee80211com *ic = &sc->sc_ic;
                    457: 
                    458:         if (sc->tx_timer > 0) {
                    459:                 if (--sc->tx_timer == 0) {
                    460:                         aprint_error_dev(sc->sc_dev, "device timeout\n");
                    461:                         ieee80211_stat_add(&ic->ic_oerrors, 1);
                    462:                         ieee80211_restart_all(ic);
                    463:                         return;
                    464:                 }
                    465:                 callout_schedule(&sc->sc_watchdog_to, hz);
                    466:         }
                    467:     }
1.2       wiki      468:     ```
1.1       wiki      469: 
1.6       wiki      470:    There is no `ieee80211_watchdog()` function any more.
1.1       wiki      471: 
                    472: 
                    473:  * ioctl functions
                    474:    Typicaly there are no ioctl functions in a wlan driver. The ioctls
                    475:    are handled in generic code and the ic_parent function is called to
                    476:    update the device state accordingly.  XXXX - is this true? implemented?
                    477:    Or one of ic_scan_start/ic_scan_end/ic_set_channel
                    478: 
                    479:  * device _parent function
                    480:    You need to add a new function to update the device state when one of
                    481:    the active VAPs requires something special, like switch to promiscous
                    482:    mode. The function looks like:
                    483: 
1.2       wiki      484:     ```
1.1       wiki      485:     static void
                    486:     rtwn_parent(struct ieee80211com *ic)
                    487:     {
                    488:         struct rtwn_softc *sc = ic->ic_softc;
1.2       wiki      489:         bool startall = false;
1.1       wiki      490: 
                    491:         mutex_enter(&sc->sc_tx_mtx);
                    492:         if (ic->ic_nrunning > 0 && !sc->sc_running)
1.2       wiki      493:             startall = true;
                    494:         else if (ic->ic_nrunning == 0 && sc->sc_running)
                    495:             rtwn_stop(sc);
1.1       wiki      496:         mutex_exit(&sc->sc_tx_mtx);
                    497: 
                    498:         if (startall)
1.2       wiki      499:             ieee80211_start_all(ic);
1.1       wiki      500:     }
1.2       wiki      501:     ```

CVSweb for NetBSD wikisrc <wikimaster@NetBSD.org> software: FreeBSD-CVSweb