I do not yet have an Atheros USB Wi-Fi dongle, but I should soon. In
spite of that, a while ago I began trying to convert athn(4) for USB
– first I tried without usbwifi (on a branch in the git
repository, and here). I also tried
with usbwifi, changing sc_ic to a pointer (probably not the best way
to go about it; that is on the branch "usbwifi"
and here). Of course neither are at all
tested at this point; if the former doesn't work, it would probably
be easier to fix. Both compiled and PCI athn still works on the
latter.
Yesterday added calls to ratectl to athn. Now I realized that
ni_txrate has changed in meaning (from being an index
to rs_rates to the rate itself) and fixed ath(4) and athn(4)
accordingly.
Here is some code to help confirm get_radiocaps behaves
the same on the old & new wifi stack. It may not be perfect but
there is a slim chance someone could take advantage of it or improve
it somehow.
static void
chan_flags2string(char *str, uint32_t flags)
{
const char *flag_names[] = {
"PRIV0", "PRIV1", "PRIV2", "PRIV3", "TURBO", "CCK", "OFDM",
"2GHZ", "5GHZ", "PASSIVE", "DYN", "GFSK", "GSM", "STURBO",
"HALF", "QUARTER", "HT20", "HT40U", "HT40D", "DFS",
"4MSXMIT", "NOADHOC", "NOHOSTAP", "11D", "VHT20", "VHT40U",
"VHT40D", "VHT80", "VHT80_80", "VHT160"
};
int pow = nitems(flag_names), i = 0;
memset(str, 0, 200);
do {
pow--;
if (flags & (1 << pow)) {
if (i != 0) str[i++] = ',';
strcat(str, flag_names[pow]);
i += strlen(flag_names[pow]);
}
} while (pow > 0);
str[i] = 0;
}
static void
display_channels(struct ieee80211com *ic)
{
#define NEWSTACK
char flagstr[200];
printf("Channels by IEEE number:\n");
#ifdef NEWSTACK
struct {uint16_t freq; uint32_t flags;} byieee[256] = {0};
for (int i = 0; i < ic->ic_nchans; i++) {
int n = ic->ic_channels[i].ic_ieee;
byieee[n].freq = ic->ic_channels[i].ic_freq;
byieee[n].flags |= ic->ic_channels[i].ic_flags;
}
for (int i = 0; i < 256; i++) {
if (byieee[i].flags != 0) {
chan_flags2string(flagstr, byieee[i].flags);
printf("%d\t%d\t%x\t%s\n", i,
byieee[i].freq,
byieee[i].flags, flagstr);
}
}
#else
for (int i = 0; i < IEEE80211_CHAN_MAX; i++) {
if (ic->ic_channels[i].ic_freq != 0) {
chan_flags2string(flagstr, ic->ic_channels[i].ic_flags);
printf("%d\t%d\t%x\t%s\n", i,
ic->ic_channels[i].ic_freq,
ic->ic_channels[i].ic_flags, flagstr);
}
}
#endif
printf("Channels per mode:\n");
#ifdef NEWSTACK
for (int i = 0; i < ic->ic_nchans; i++) {
chan_flags2string(flagstr, ic->ic_channels[i].ic_flags);
printf("%d\t%d\t%x\t%s\n",
ic->ic_channels[i].ic_ieee,
ic->ic_channels[i].ic_freq,
ic->ic_channels[i].ic_flags, flagstr);
}
#else
#define ADDPERMODE(_i, _freq, _flags) \
do { \
permode[nchans].ieee = _i; \
permode[nchans].freq = _freq; \
permode[nchans].flags = _flag; \
nchans++; \
} while (0);
struct {
uint8_t ieee;
uint32_t flags;
uint16_t freq;
} *permode;
int nchans = 0;
permode = kmem_zalloc(1024*sizeof(*permode), KM_SLEEP);
for (int i = 0; i < IEEE80211_CHAN_MAX; i++) {
uint16_t freq = ic->ic_channels[i].ic_freq;
uint32_t flags = ic->ic_channels[i].ic_flags;
if (IEEE80211_IS_CHAN_A(&ic->ic_channels[i])) {
ADDPERMODE(i, freq, IEEE80211_CHAN_A);
if (flags & IEEE80211_CHAN_TURBO)
ADDPERMODE(i, freq,
IEEE80211_CHAN_A | IEEE80211_CHAN_TURBO);
}
if (IEEE80211_IS_CHAN_B(&ic->ic_channels[i])) {
ADDPERMODE(i, freq, IEEE80211_CHAN_B);
}
if (IEEE80211_IS_CHAN_G(&ic->ic_channels[i])
|| IEEE80211_IS_CHAN_G(&ic->ic_channels[i])) {
ADDPERMODE(i, freq, IEEE80211_CHAN_G);
if (flags & IEEE80211_CHAN_TURBO)
ADDPERMODE(i, freq,
IEEE80211_CHAN_G | IEEE80211_CHAN_TURBO);
}
}
for (int i = 0; i < nchans; i++) {
chan_flags2string(flagstr, permode[i].flags);
printf("%d\t%d\t%x\t%s\n",
permode[i].ieee,
permode[i].freq,
permode[i].flags, flagstr);
}
kmem_free(permode, 1024*sizeof(*permode));
#undef ADDPERMODE
#endif
#undef NEWSTACK
}
Got athrate-amrr & athrate-onoe working (before I only had
athrate-sample). This was easy enough because everything
rate-adaption-related is in a single driver-specific module (though
ultimately it will probably be redundant because of net80211).
This commit
could be relevant to athrate-sample (then again net80211 may make it
obsolete).
Martin (my mentor) recently let me know of an existing patch for
athn(4) written by James Brown and suggested I could use parts or
merge with my effort. Since it's written by someone more
knowledgeable I'll take the latter path. Some of the changes are
very similar; others are just more sensible, or reflect stuff of
which I was ignorant. So I changed it enough to compile and work as
before, merging it with a bit of my previous work. From here I can
look into rate adaption, etc.
Also made a few changes to ath(4) – ideally hostapd(8)
would work (if that's possible at this point), but not yet.
I've made a few gradual changes to athn(4).
ieee80211_tx_complete is now called, and presumably
athn_get_radiocaps behaves now as did
athn_get_chanlist before. It works on 2.4GHz and 5GHz.
Also fixed (I believe) [io]errors on both drivers.
athn(4) appears to be in a working state after some more changes. The
changes will need to be cleaned up, and I haven't yet figured out what
to do with rate adaption.
Made some minor changes to ath(4). Since 27/06 I've worked a bit on
converting athn(4) (on a separate git branch) – so far it
compiles and boots but creating a VAP causes a panic.
Turns out I hadn't actually fixed that. It happened whenever
ath_start was invoked by the ath_tx_proc
family (without IEEE80211_TX_LOCK) and then called
ieee80211_encap. Removing the call to
ieee80211_encap would fix this as the call was
hoisted up into net80211; the only reason it worked previously
was because I mixed up ic_caps with
ic_cryptocaps.
All of that is fixed now, except that if the code sets
ic_cryptocaps then wifi no longer works; so for now it's
0. No crashes yet since.
I couldn't reproduce the crash I thought was caused by wpa_supplicant
mentioned in the last post. However, it did tend to crash when
streaming video for more than a few minutes. M_CLEARCTX
seemed to fix this, and it hasn't crashed since (ignoring mdnsd).
Reenabling ath_chan_change fixed the "bad rate" messages
reported by athrate-sample. But reenabling ath_watchdog
results in occasional "device timeouts."
After making the code regarding getting channels behave (mostly)
correctly, adding IEEE80211_C_WPA to
ic_caps, and restoring/adjusting
athrate-sample.c, wpa_supplicant connects to my network
and Wi-Fi mostly works(!) ignoring some kernel messages about "bad
rates." Doubtless some things still need to be corrected (and other
things need to be cleaned up, fixed, and made less hacky). mdnsd
(Bonjour) causes an assertion to fail during some ioctl.
wpa_supplicant also sometimes seems to crash the system based on the
exact sequence of commands used to set Wi-Fi up, so I'll also need to
perform some debugging.
The code is now on
GitHub.
Re-enabling code, adjusting as necessary and incorporating some bits
from FreeBSD brought the driver to the point (around last Friday [per
my report on tech-net]) where one can create a VAP and scan for
networks. Since then adding ath_recv_mgmt back seems to
have somehow made scanning faster (…or maybe it didn't really).
I have not yet been able to join a network with wpa_supplicant, and
there's still a lot that I might need to try to better understand.
Successfully initialized wpa_supplicant
ioctl[SIOCS80211, op=26, val=3, arg_len=0]: Operation not supported
wpa_driver_bsd_scan: failed to set wpa: Operation not supported
wlan0: CTRL-EVENT-SCAN-FAILED ret=-1 retry=1
<etc.>
Trying to build athn(4) on the wifi topic results in a lot of errors
in multiple arnXXXX.c files. Trying to build ath(4) on the other hand
seemed to result in fewer errors. So it might be better to convert
ath first.
I substituted ic_macaddr for ic_myaddr;
made newstate take a VAP; replaced
ic->ic_ifp->if_softc with ic->ic_softc;
changed some other references; haphazardly disabled swathes of
(important) code (a lot of important functions); and added a
few random stopgaps. Eventually it compiled without error;
ieee80211_ifattach panicked but taking the contents of
rtwn_get_radiocaps served as a stopgap. Thus it was
able to boot, and ath0 was shown by sysctl.
Creating a VAP with ifconfig is (unsurprisingly) still far away (it
reported "STA mode not supported").
As noted by
Martin's guide,
the main difference in new style drivers is the VAP concept. To that
end FreeBSD ath(4) defines struct ath_vap, deriving from
struct ieee80211vap. So whereas before it would look
something like this:
Now for any driver, the representation will look more or less like
this:
By comparing with FreeBSD ath(4) & the new rtwn driver:
Some new functions exist e.g. ath_vap_create,
ath_vap_delete, ath_parent.
Methods previously existing on the struct ifnet are
gone, e.g. if_start (replaced by
ic_transmit) if_watchdog,
if_ioctl and if_init.
Some methods previously existing on struct ieee80211com
are shifted to the VAP e.g. iv_recv_mgmt,
iv_newstate.
sc_ec is gone (or rather never existed in FreeBSD).
This only seems to affect the handling of multicast addresses.
ath(4) and athn(4) support somewhat different sets of hardware. The
most obvious difference in implementation is that ath(4) uses the
Atheros HAL. This was initially a blob but
open-sourced in 2009.
According to the
FreeBSD wiki,
The Atheros HAL is an abstraction layer which attempts to hide much of the card specific configuration from the rest of the atheros wireless driver.
The HAL takes care of the MAC, baseband and radio. It exports a set of routines which allow the atheros wireless driver to do certain tasks (set channel, configure TX queue, queue TX packet, queue RX buffer, setup interrupts, do calibration, set/get TX power, configure beacon parameters, enter power saving/sleep mode, etc.)
It doesn't seem that it'll require any changes (except ah_osdep.c).
So, for example, athn.c has routines that directly change particular
registers (using AR_WRITE i.e. sc_ops.write
which might resolve to athn_pci_write), the lowest-level
routines being implemented in if_athn_pci.c & if_athn_cardbus.c,
whereas ath.c uses the higher-level routines already provided by the HAL.
ath_softc and athn_softc are the most
important structs in both, which, presumably, will have a few mutexes
on them when finished (as FreeBSD). ath(4) seems to be the more
complicated of the two drivers.
This far I have ensured that the wifi branch builds and runs on the
target netbook. All the unconverted drivers need to be disabled.
Ethernet works. Presently it identifies as NetBSD 9.99.100.
To examine:
dev/pci/if_athn_pci.c and dev/pci/if_ath_pci.c and (perhaps
especially) dev/ic/athn.c and dev/ic/ath.c
other converted drivers e.g. rtwn(4)
FreeBSD
ath(4)
– converted and seemingly a lot more code now
One of the most obvious differences in the converted drivers is
the locking (mutex_init, mutex_enter,
mutex_exit).