Annotation of wikisrc/Converting_drivers_to_the_new_wifi_stack.mdwn, revision 1.3

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

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