Annotation of wikisrc/Converting_drivers_to_the_new_wifi_stack.mdwn, revision 1.2

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

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