On Wed, 2018-10-03 at 19:20 +0800, [email protected] wrote:
> 
> +static int rtw_ops_config(struct ieee80211_hw *hw, u32 changed)
> +{
> +     struct rtw_dev *rtwdev = hw->priv;
> +     int ret = 0;
> +
> +     mutex_lock(&rtwdev->mutex);
> +
> +     if (changed & IEEE80211_CONF_CHANGE_IDLE) {
> +             if (hw->conf.flags & IEEE80211_CONF_IDLE) {
> +                     rtw_enter_ips(rtwdev);
> +             } else {
> +                     ret = rtw_leave_ips(rtwdev);
> +                     if (ret) {
> +                             rtw_err(rtwdev, "failed to leave idle state\n");
> +                             goto out;
> +                     }
> +             }
> +     }
> +
> +     if (changed & IEEE80211_CONF_CHANGE_CHANNEL)
> +             rtw_set_channel(rtwdev);

You really should consider supporting channel contexts - it's the far
more modern API and likely gives you more control even if you support
only a single channel.

> +static struct rtw_vif_port rtw_vif_port[] = {
> +     [0] = {
> +             .mac_addr       = {.addr = 0x0610},
> +             .bssid          = {.addr = 0x0618},
> +             .net_type       = {.addr = 0x0100, .mask = 0x30000},
> +             .aid            = {.addr = 0x06a8, .mask = 0x7ff},
> +     },

err, what's all this?

Anyway, you really cannot make this static - again, multiple devices
might get plugged in.

> +     list_add_rcu(&rtwvif->list, &rtwdev->vif_list);

I don't see a reason for you to maintain your own list, you can always
iterate mac80211's list if you really need to?

> +     switch (vif->type) {
> +     case NL80211_IFTYPE_AP:
> +     case NL80211_IFTYPE_MESH_POINT:
> +             net_type = RTW_NET_AP_MODE;
> +             break;
> +     case NL80211_IFTYPE_ADHOC:
> +             net_type = RTW_NET_AD_HOC;
> +             break;
> +     default:
> +             net_type = RTW_NET_NO_LINK;

you might to add STATION and then fail in the default case?

> +static void rtw_ops_remove_interface(struct ieee80211_hw *hw,
> +                                  struct ieee80211_vif *vif)
> +{
> +     struct rtw_dev *rtwdev = hw->priv;
> +     struct rtw_vif *rtwvif = (struct rtw_vif *)vif->drv_priv;
> +     u32 config = 0;
> +
> +     rtw_info(rtwdev, "stop vif %pM on port %d", vif->addr, rtwvif->port);
> +
> +     mutex_lock(&rtwdev->mutex);
> +
> +     eth_zero_addr(rtwvif->mac_addr);
> +     config |= PORT_SET_MAC_ADDR;
> +     rtwvif->net_type = RTW_NET_NO_LINK;
> +     config |= PORT_SET_NET_TYPE;
> +     rtw_vif_port_config(rtwdev, rtwvif, config);
> +
> +     list_del_rcu(&rtwvif->list);
> +     synchronize_rcu();

That synchronize_rcu() is *really* expensive, you should probably use
mac80211's list iteration to avoid it.

> +static void rtw_ops_configure_filter(struct ieee80211_hw *hw,
> +                                  unsigned int changed_flags,
> +                                  unsigned int *new_flags,
> +                                  u64 multicast)
> +{
> +     struct rtw_dev *rtwdev = hw->priv;
> +
> +     *new_flags &= (FIF_ALLMULTI | FIF_OTHER_BSS | FIF_FCSFAIL |
> +                    FIF_BCN_PRBRESP_PROMISC);

nit: not much need for those parentheses

> +static u8 rtw_acquire_macid(struct rtw_dev *rtwdev)
> +{
> +     u8 i;
> +
> +     for (i = 0; i < RTW_MAX_MAC_ID_NUM; i++) {
> +             if (!rtwdev->macid_used[i]) {
> +                     rtwdev->macid_used[i] = true;
> +                     return i;
> +             }
> +     }
> +
> +     return i;
> +}
> +
> +static void rtw_release_macid(struct rtw_dev *rtwdev, u8 mac_id)
> +{
> +     rtwdev->macid_used[mac_id] = false;
> +}

This would be way simpler (and use much less memory) with a bitmap and
find_first_zero_bit().

> +static int rtw_ops_sta_add(struct ieee80211_hw *hw,
> +                        struct ieee80211_vif *vif,
> +                        struct ieee80211_sta *sta)

You might want to use sta_state() instead of sta_add(), it's likely the
better API.

> +     si->sta = sta;
> +     si->vif = vif;
> +     si->init_ra_lv = 1;
> +     ewma_rssi_init(&si->avg_rssi);

What's this for that mac80211 doesn't do already?

> +     rtw_update_sta_info(rtwdev, si);
> +     rtw_fw_media_status_report(rtwdev, si->mac_id, true);
> +
> +     list_add_tail_rcu(&si->list, &rtwvif->sta_list);

Again, you shouldn't need to keep your own list in the driver, mac80211
does all that bookkeeping for you.

> +static int rtw_ops_sta_remove(struct ieee80211_hw *hw,
> +                           struct ieee80211_vif *vif,
> +                           struct ieee80211_sta *sta)
> +{
> +     struct rtw_dev *rtwdev = hw->priv;
> +     struct rtw_sta_info *si = (struct rtw_sta_info *)sta->drv_priv;
> +
> +     mutex_lock(&rtwdev->mutex);
> +
> +     rtw_release_macid(rtwdev, si->mac_id);
> +     rtw_fw_media_status_report(rtwdev, si->mac_id, false);
> +
> +     list_del_rcu(&si->list);
> +     synchronize_rcu();

This synchronize_rcu() will hurt your roaming performance.

> +     switch (key->cipher) {
> +     case WLAN_CIPHER_SUITE_WEP40:
> +             hw_key_type = RTW_CAM_WEP40;
> +             break;
> +     case WLAN_CIPHER_SUITE_WEP104:
> +             hw_key_type = RTW_CAM_WEP104;
> +             break;
> +     case WLAN_CIPHER_SUITE_TKIP:
> +             hw_key_type = RTW_CAM_TKIP;
> +             key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
> +             break;
> +     case WLAN_CIPHER_SUITE_CCMP:
> +             hw_key_type = RTW_CAM_AES;
> +             key->flags |= IEEE80211_KEY_FLAG_SW_MGMT_TX;
> +             break;
> +     default:
> +             return -ENOTSUPP;
> +     }

This will provoke error messages to be printed for e.g. CMAC keys, or do
you really not support protected management frames? If you were to pick
"-EOPNOTSUPP" then no errors would be printed.

> +     mutex_lock(&rtwdev->mutex);
> +
> +     if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) {
> +             hw_key_idx = rtw_sec_get_free_cam(sec);
> +     } else {
> +             /* multiple interfaces? */
> +             hw_key_idx = key->keyidx;
> +     }

Indeed, good question :-)


> +};
> +
> +static struct ieee80211_rate rtw_ratetable_2g[] = {
> +     {.bitrate = 10, .hw_value = 0x00,},
> +     {.bitrate = 20, .hw_value = 0x01,},
> +     {.bitrate = 55, .hw_value = 0x02,},
> +     {.bitrate = 110, .hw_value = 0x03,},
> +     {.bitrate = 60, .hw_value = 0x04,},
> +     {.bitrate = 90, .hw_value = 0x05,},
> +     {.bitrate = 120, .hw_value = 0x06,},
> +     {.bitrate = 180, .hw_value = 0x07,},
> +     {.bitrate = 240, .hw_value = 0x08,},
> +     {.bitrate = 360, .hw_value = 0x09,},
> +     {.bitrate = 480, .hw_value = 0x0a,},
> +     {.bitrate = 540, .hw_value = 0x0b,},
> +};
> +
> +static struct ieee80211_rate rtw_ratetable_5g[] = {
> +     {.bitrate = 60, .hw_value = 0x04,},
> +     {.bitrate = 90, .hw_value = 0x05,},
> +     {.bitrate = 120, .hw_value = 0x06,},
> +     {.bitrate = 180, .hw_value = 0x07,},
> +     {.bitrate = 240, .hw_value = 0x08,},
> +     {.bitrate = 360, .hw_value = 0x09,},
> +     {.bitrate = 480, .hw_value = 0x0a,},
> +     {.bitrate = 540, .hw_value = 0x0b,},
> +};

The 5G one is the same as the 2G one without the first 4 entries, so you
could do rtw_ratetable_2g+4 to avoid duplicating the data.

> +static struct ieee80211_supported_band rtw_band_2ghz = {
> +     .band = NL80211_BAND_2GHZ,
> +
> +     .channels = rtw_channeltable_2g,
> +     .n_channels = ARRAY_SIZE(rtw_channeltable_2g),
> +
> +     .bitrates = rtw_ratetable_2g,
> +     .n_bitrates = ARRAY_SIZE(rtw_ratetable_2g),
> +
> +     .ht_cap = {0},
> +     .vht_cap = {0},
> +};

I see no reason to init the ht/vht cap?

> +static struct ieee80211_supported_band rtw_band_5ghz = {
> +     .band = NL80211_BAND_5GHZ,
> +
> +     .channels = rtw_channeltable_5g,
> +     .n_channels = ARRAY_SIZE(rtw_channeltable_5g),
> +
> +     .bitrates = rtw_ratetable_5g,
> +     .n_bitrates = ARRAY_SIZE(rtw_ratetable_5g),
> +
> +     .ht_cap = {0},
> +     .vht_cap = {0},
> +};

dito

> +static void rtw_watch_dog_work(struct work_struct *work)
> +{
> +     struct rtw_dev *rtwdev = container_of(work, struct rtw_dev,
> +                                           watch_dog_work.work);
> +     struct rtw_vif *rtwvif;
> +
> +     if (!rtw_flag_check(rtwdev, RTW_FLAG_RUNNING))
> +             return;
> +
> +     ieee80211_queue_delayed_work(rtwdev->hw, &rtwdev->watch_dog_work,
> +                                  RTW_WATCH_DOG_DELAY_TIME);

You're aware of the power cost of waking up every 2 seconds? That's a
really bad idea, in general, at the very least you should use a more
power efficient scheduling here to combine with other wakeups
(round_jiffies_relative, or so).

> +     /* check if we can enter lps */
> +     rtw_lps_enter_check(rtwdev);
> +
> +     /* reset tx/rx statictics */
> +     rtwdev->stats.tx_unicast = 0;
> +     rtwdev->stats.rx_unicast = 0;
> +     rtwdev->stats.tx_cnt = 0;
> +     rtwdev->stats.rx_cnt = 0;
> +     rcu_read_lock();
> +     list_for_each_entry_rcu(rtwvif, &rtwdev->vif_list, list) {
> +             rtwvif->stats.tx_unicast = 0;
> +             rtwvif->stats.rx_unicast = 0;
> +             rtwvif->stats.tx_cnt = 0;
> +             rtwvif->stats.rx_cnt = 0;
> +     }
> +     rcu_read_unlock();

???

why should statistics be reset evyer 2 seconds?

> +
> +     switch (bw_cap) {
> +     case EFUSE_HW_CAP_IGNORE:
> +     case EFUSE_HW_CAP_SUPP_BW80:
> +             bw |= BIT(RTW_CHANNEL_WIDTH_80);
> +     /* fall through */
> +     case EFUSE_HW_CAP_SUPP_BW40:
> +             bw |= BIT(RTW_CHANNEL_WIDTH_40);
> +     /* fall through */

I'd probably indent the comments by one more tab (to be where the
"break" would be), but that's really a style nit.

> +     case WIRELESS_OFDM | WIRELESS_HT:

Btw ... you have all this HT stuff and 40/80 MHz but no HT/VHT
capabilities?

> +static void rtw_init_ht_cap(struct rtw_dev *rtwdev,
> +                         struct ieee80211_sta_ht_cap *ht_cap)
> +{

Oh... ok.

> +static void rtw_set_supported_band(struct ieee80211_hw *hw,
> +                                struct rtw_chip_info *chip)
> +{
> +     struct rtw_dev *rtwdev = hw->priv;
> +     struct ieee80211_supported_band *sband;
> +
> +     if (chip->band & RTW_BAND_2G) {
> +             sband = kmalloc(sizeof(*sband), GFP_KERNEL);
> +             memcpy(sband, &rtw_band_2ghz, sizeof(rtw_band_2ghz));

error check, kmemdup, make rtw_band_2ghz const.

> +     if (chip->band & RTW_BAND_5G) {
> +             sband = kmalloc(sizeof(*sband), GFP_KERNEL);
> +             memcpy(sband, &rtw_band_5ghz, sizeof(rtw_band_5ghz));

dito

> +     if (chip->band & RTW_BAND_2G)
> +             kfree(hw->wiphy->bands[NL80211_BAND_2GHZ]);
> +     if (chip->band & RTW_BAND_5G)
> +             kfree(hw->wiphy->bands[NL80211_BAND_5GHZ]);

Don't really need the if in both cases, kfree(NULL) is fine.

> +static int rtw_load_firmware(struct rtw_dev *rtwdev, const char *fw_name)
> +{
> +     struct rtw_fw_state *fw = &rtwdev->fw;
> +     const struct firmware *firmware;
> +     int ret;
> +
> +     ret = request_firmware(&firmware, fw_name, rtwdev->dev);

You should use request_firmware_nowait(), otherwise you can stall the
boot if your driver is built-in (or lives in initramfs?).

> +EXPORT_SYMBOL(rtw_core_init);

You could also remove the exports if you put the pci.c into the same
module. Dunno, maybe it's some sort of future-proofing, but if you're
going to have one module with *everything* except for ~1.2k LOC PCI, it
seems hardly worth it (especially since it's only useful if you load
both anyway)

> +     ieee80211_hw_set(hw, MFP_CAPABLE);

so you do have MFP - I guess you should test it and check for spurious
hardware crypto messages

> +#define LE_BITS_CLEARED_TO_4BYTE(addr, offset, len)                          
> \
> +     (le32_to_cpu(*(__le32 *)(addr)) & (~GENMASK(offset + len - 1, offset)))
> +#define LE_BITS_TO_4BYTE(addr, offset, len)                                  
> \
> +     ((le32_to_cpu(*((__le32 *)(addr))) >> (offset)) & GENMASK(len - 1, 0))
> +#define SET_BITS_TO_LE_4BYTE(addr, offset, len, val)                         
> \
> +     do {                                                                    
> \
> +             *((__le32 *)(addr)) =                                           
> \
> +             cpu_to_le32(                                                    
> \
> +             LE_BITS_CLEARED_TO_4BYTE(addr, offset, len) |                   
> \
> +             ((((u32)val) & GENMASK(len - 1, 0)) << (offset))                
> \
> +             );                                                              
> \
> +     } while (0)

Seems like that likely has alignment issues again.

> +struct rtw_2g_1s_pwr_idx_diff {
> +#ifdef __LITTLE_ENDIAN
> +     s8 ofdm:4;
> +     s8 bw20:4;
> +#else
> +     s8 bw20:4;
> +     s8 ofdm:4;
> +#endif

You have this a lot, but IMHO it's generally not a good idea to try to
use bitfields when you actually need accurate bit layout for hardware.

Take a look at include/linux/bitfield.h for an alternative.

> +struct rtw_cam_entry {
> +     bool used;
> +     bool valid;
> +     bool group;
> +     u8 addr[ETH_ALEN];
> +     u8 hw_key_type;
> +     struct ieee80211_key_conf *key;
> +};

I'd also argue you should split hardware/firmware API things (like much
of this file) from driver-implementation things (like this and more
below) - it makes the driver easier to maintain since one can then leave
the hardware/firmware things pretty much alone for the most part. Or, if
that changes, just has to look there. The separation is good.

> +struct rtw_sec_desc {
> +     /* search strategy */
> +     bool default_key_search;

Incidental nit: that seems a bit strange, that's not a "strategy enum"
or so?

> +     /* protected by rcu */
> +     struct list_head sta_list;

RCU doesn't protect a list by itself - you need to say "protected by xyz
mutex, readers can use RCU" or so.

> +#include "hci.h"

Uh, I think it's more customary to put includes at the top of the file,
and if you can't that's probably a sign you haven't split things up
well.

> +static inline struct rtw_sta_info *get_hdr_sta(struct rtw_dev *rtwdev,
> +                                            struct ieee80211_vif *vif,
> +                                            struct ieee80211_hdr *hdr)
> +{
> +     struct rtw_vif *rtwvif;
> +     struct rtw_sta_info *si;
> +     struct rtw_sta_info *target = NULL;
> +
> +     rcu_read_lock();
> +     if (vif) {
> +             rtwvif = (struct rtw_vif *)vif->drv_priv;
> +             list_for_each_entry(si, &rtwvif->sta_list, list) {
> +                     if (ether_addr_equal(si->sta->addr, hdr->addr2)) {
> +                             target = si;
> +                             break;
> +                     }
> +             }
> +     } else {
> +             list_for_each_entry_rcu(rtwvif, &rtwdev->vif_list, list) {
> +                     list_for_each_entry(si, &rtwvif->sta_list, list) {
> +                             if (ether_addr_equal(si->sta->addr, 
> hdr->addr2)) {
> +                                     target = si;
> +                                     break;
> +                             }
> +                     }
> +             }
> +     }
> +     rcu_read_unlock();
> +
> +     return target;
> +}

Seems a bit large for an inline?

johannes

Reply via email to