Path: blob/main/sys/contrib/dev/iwlwifi/iwl-utils.c
105687 views
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */1/*2* Copyright (C) 2024-2025 Intel Corporation3*/4#include <net/gso.h>5#include <linux/ieee80211.h>6#include <net/ip.h>78#include "iwl-drv.h"9#include "iwl-utils.h"1011#ifdef CONFIG_INET12int iwl_tx_tso_segment(struct sk_buff *skb, unsigned int num_subframes,13netdev_features_t netdev_flags,14struct sk_buff_head *mpdus_skbs)15{16struct sk_buff *tmp, *next;17struct ieee80211_hdr *hdr = (void *)skb->data;18char cb[sizeof(skb->cb)];19u16 i = 0;20unsigned int tcp_payload_len;21unsigned int mss = skb_shinfo(skb)->gso_size;22bool ipv4 = (skb->protocol == htons(ETH_P_IP));23bool qos = ieee80211_is_data_qos(hdr->frame_control);24u16 ip_base_id = ipv4 ? ntohs(ip_hdr(skb)->id) : 0;2526skb_shinfo(skb)->gso_size = num_subframes * mss;27memcpy(cb, skb->cb, sizeof(cb));2829next = skb_gso_segment(skb, netdev_flags);30skb_shinfo(skb)->gso_size = mss;31skb_shinfo(skb)->gso_type = ipv4 ? SKB_GSO_TCPV4 : SKB_GSO_TCPV6;3233if (IS_ERR(next) && PTR_ERR(next) == -ENOMEM)34return -ENOMEM;3536if (WARN_ONCE(IS_ERR(next),37"skb_gso_segment error: %d\n", (int)PTR_ERR(next)))38return PTR_ERR(next);3940if (next)41consume_skb(skb);4243skb_list_walk_safe(next, tmp, next) {44memcpy(tmp->cb, cb, sizeof(tmp->cb));45/*46* Compute the length of all the data added for the A-MSDU.47* This will be used to compute the length to write in the TX48* command. We have: SNAP + IP + TCP for n -1 subframes and49* ETH header for n subframes.50*/51tcp_payload_len = skb_tail_pointer(tmp) -52skb_transport_header(tmp) -53tcp_hdrlen(tmp) + tmp->data_len;5455if (ipv4)56ip_hdr(tmp)->id = htons(ip_base_id + i * num_subframes);5758if (tcp_payload_len > mss) {59skb_shinfo(tmp)->gso_size = mss;60skb_shinfo(tmp)->gso_type = ipv4 ? SKB_GSO_TCPV4 :61SKB_GSO_TCPV6;62} else {63if (qos) {64u8 *qc;6566if (ipv4)67ip_send_check(ip_hdr(tmp));6869qc = ieee80211_get_qos_ctl((void *)tmp->data);70*qc &= ~IEEE80211_QOS_CTL_A_MSDU_PRESENT;71}72skb_shinfo(tmp)->gso_size = 0;73}7475skb_mark_not_on_list(tmp);76__skb_queue_tail(mpdus_skbs, tmp);77i++;78}7980return 0;81}82IWL_EXPORT_SYMBOL(iwl_tx_tso_segment);83#endif /* CONFIG_INET */8485static u32 iwl_div_by_db(u32 value, u8 db)86{87/*88* 2^32 * 10**(i / 10) for i = [1, 10], skipping 0 and simply stopping89* at 10 dB and looping instead of using a much larger table.90*91* Using 64 bit math is overkill, but means the helper does not require92* a limit on the input range.93*/94static const u32 db_to_val[] = {950xcb59185e, 0xa1866ba8, 0x804dce7a, 0x65ea59fe, 0x50f44d89,960x404de61f, 0x331426af, 0x2892c18b, 0x203a7e5b, 0x1999999a,97};9899while (value && db > 0) {100u8 change = min_t(u8, db, ARRAY_SIZE(db_to_val));101102value = (((u64)value) * db_to_val[change - 1]) >> 32;103104db -= change;105}106107return value;108}109110s8 iwl_average_neg_dbm(const u8 *neg_dbm_values, u8 len)111{112int average_magnitude;113u32 average_factor;114int sum_magnitude = -128;115u32 sum_factor = 0;116int i, count = 0;117118/*119* To properly average the decibel values (signal values given in dBm)120* we need to do the math in linear space. Doing a linear average of121* dB (dBm) values is a bit annoying though due to the large range of122* at least -10 to -110 dBm that will not fit into a 32 bit integer.123*124* A 64 bit integer should be sufficient, but then we still have the125* problem that there are no directly usable utility functions126* available.127*128* So, lets not deal with that and instead do much of the calculation129* with a 16.16 fixed point integer along with a base in dBm. 16.16 bit130* gives us plenty of head-room for adding up a few values and even131* doing some math on it. And the tail should be accurate enough too132* (1/2^16 is somewhere around -48 dB, so effectively zero).133*134* i.e. the real value of sum is:135* sum = sum_factor / 2^16 * 10^(sum_magnitude / 10) mW136*137* However, that does mean we need to be able to bring two values to138* a common base, so we need a helper for that.139*140* Note that this function takes an input with unsigned negative dBm141* values but returns a signed dBm (i.e. a negative value).142*/143144for (i = 0; i < len; i++) {145int val_magnitude;146u32 val_factor;147148/* Assume invalid */149if (neg_dbm_values[i] == 0xff)150continue;151152val_factor = 0x10000;153val_magnitude = -neg_dbm_values[i];154155if (val_magnitude <= sum_magnitude) {156u8 div_db = sum_magnitude - val_magnitude;157158val_factor = iwl_div_by_db(val_factor, div_db);159val_magnitude = sum_magnitude;160} else {161u8 div_db = val_magnitude - sum_magnitude;162163sum_factor = iwl_div_by_db(sum_factor, div_db);164sum_magnitude = val_magnitude;165}166167sum_factor += val_factor;168count++;169}170171/* No valid noise measurement, return a very high noise level */172if (count == 0)173return 0;174175average_magnitude = sum_magnitude;176average_factor = sum_factor / count;177178/*179* average_factor will be a number smaller than 1.0 (0x10000) at this180* point. What we need to do now is to adjust average_magnitude so that181* average_factor is between -0.5 dB and 0.5 dB.182*183* Just do -1 dB steps and find the point where184* -0.5 dB * -i dB = 0x10000 * 10^(-0.5/10) / i dB185* = div_by_db(0xe429, i)186* is smaller than average_factor.187*/188for (i = 0; average_factor < iwl_div_by_db(0xe429, i); i++) {189/* nothing */190}191192return clamp(average_magnitude - i, -128, 0);193}194IWL_EXPORT_SYMBOL(iwl_average_neg_dbm);195196197