/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
/*
* Copyright ( C ) 2024 - 2025 Intel Corporation
*/
#include <net/gso.h>
#include <linux/ieee80211.h>
#include <net/ip.h>
#include "iwl-drv.h"
#include "iwl-utils.h"
#ifdef CONFIG_INET
int iwl_tx_tso_segment(struct sk_buff *skb, unsigned int num_subframes,
netdev_features_t netdev_flags,
struct sk_buff_head *mpdus_skbs)
{
struct sk_buff *tmp, *next;
struct ieee80211_hdr *hdr = (void *)skb->data;
char cb[sizeof (skb->cb)];
u16 i = 0 ;
unsigned int tcp_payload_len;
unsigned int mss = skb_shinfo(skb)->gso_size;
bool ipv4 = (skb->protocol == htons(ETH_P_IP));
bool qos = ieee80211_is_data_qos(hdr->frame_control);
u16 ip_base_id = ipv4 ? ntohs(ip_hdr(skb)->id) : 0 ;
skb_shinfo(skb)->gso_size = num_subframes * mss;
memcpy(cb, skb->cb, sizeof (cb));
next = skb_gso_segment(skb, netdev_flags);
skb_shinfo(skb)->gso_size = mss;
skb_shinfo(skb)->gso_type = ipv4 ? SKB_GSO_TCPV4 : SKB_GSO_TCPV6;
if (IS_ERR(next) && PTR_ERR(next) == -ENOMEM)
return -ENOMEM;
if (WARN_ONCE(IS_ERR(next),
"skb_gso_segment error: %d\n" , (int )PTR_ERR(next)))
return PTR_ERR(next);
if (next)
consume_skb(skb);
skb_list_walk_safe(next, tmp, next) {
memcpy(tmp->cb, cb, sizeof (tmp->cb));
/*
* Compute the length of all the data added for the A - MSDU .
* This will be used to compute the length to write in the TX
* command . We have : SNAP + IP + TCP for n - 1 subframes and
* ETH header for n subframes .
*/
tcp_payload_len = skb_tail_pointer(tmp) -
skb_transport_header(tmp) -
tcp_hdrlen(tmp) + tmp->data_len;
if (ipv4)
ip_hdr(tmp)->id = htons(ip_base_id + i * num_subframes);
if (tcp_payload_len > mss) {
skb_shinfo(tmp)->gso_size = mss;
skb_shinfo(tmp)->gso_type = ipv4 ? SKB_GSO_TCPV4 :
SKB_GSO_TCPV6;
} else {
if (qos) {
u8 *qc;
if (ipv4)
ip_send_check(ip_hdr(tmp));
qc = ieee80211_get_qos_ctl((void *)tmp->data);
*qc &= ~IEEE80211_QOS_CTL_A_MSDU_PRESENT;
}
skb_shinfo(tmp)->gso_size = 0 ;
}
skb_mark_not_on_list(tmp);
__skb_queue_tail(mpdus_skbs, tmp);
i++;
}
return 0 ;
}
IWL_EXPORT_SYMBOL(iwl_tx_tso_segment);
#endif /* CONFIG_INET */
static u32 iwl_div_by_db(u32 value, u8 db)
{
/*
* 2 ^ 32 * 10 * * ( i / 10 ) for i = [ 1 , 10 ] , skipping 0 and simply stopping
* at 10 dB and looping instead of using a much larger table .
*
* Using 64 bit math is overkill , but means the helper does not require
* a limit on the input range .
*/
static const u32 db_to_val[] = {
0 xcb59185e, 0 xa1866ba8, 0 x804dce7a, 0 x65ea59fe, 0 x50f44d89,
0 x404de61f, 0 x331426af, 0 x2892c18b, 0 x203a7e5b, 0 x1999999a,
};
while (value && db > 0 ) {
u8 change = min_t(u8, db, ARRAY_SIZE(db_to_val));
value = (((u64)value) * db_to_val[change - 1 ]) >> 32 ;
db -= change;
}
return value;
}
s8 iwl_average_neg_dbm(const u8 *neg_dbm_values, u8 len)
{
int average_magnitude;
u32 average_factor;
int sum_magnitude = -128 ;
u32 sum_factor = 0 ;
int i, count = 0 ;
/*
* To properly average the decibel values ( signal values given in dBm )
* we need to do the math in linear space . Doing a linear average of
* dB ( dBm ) values is a bit annoying though due to the large range of
* at least - 10 to - 110 dBm that will not fit into a 32 bit integer .
*
* A 64 bit integer should be sufficient , but then we still have the
* problem that there are no directly usable utility functions
* available .
*
* So , lets not deal with that and instead do much of the calculation
* with a 16 . 16 fixed point integer along with a base in dBm . 16 . 16 bit
* gives us plenty of head - room for adding up a few values and even
* doing some math on it . And the tail should be accurate enough too
* ( 1 / 2 ^ 16 is somewhere around - 48 dB , so effectively zero ) .
*
* i . e . the real value of sum is :
* sum = sum_factor / 2 ^ 16 * 10 ^ ( sum_magnitude / 10 ) mW
*
* However , that does mean we need to be able to bring two values to
* a common base , so we need a helper for that .
*
* Note that this function takes an input with unsigned negative dBm
* values but returns a signed dBm ( i . e . a negative value ) .
*/
for (i = 0 ; i < len; i++) {
int val_magnitude;
u32 val_factor;
/* Assume invalid */
if (neg_dbm_values[i] == 0 xff)
continue ;
val_factor = 0 x10000;
val_magnitude = -neg_dbm_values[i];
if (val_magnitude <= sum_magnitude) {
u8 div_db = sum_magnitude - val_magnitude;
val_factor = iwl_div_by_db(val_factor, div_db);
val_magnitude = sum_magnitude;
} else {
u8 div_db = val_magnitude - sum_magnitude;
sum_factor = iwl_div_by_db(sum_factor, div_db);
sum_magnitude = val_magnitude;
}
sum_factor += val_factor;
count++;
}
/* No valid noise measurement, return a very high noise level */
if (count == 0 )
return 0 ;
average_magnitude = sum_magnitude;
average_factor = sum_factor / count;
/*
* average_factor will be a number smaller than 1 . 0 ( 0 x10000 ) at this
* point . What we need to do now is to adjust average_magnitude so that
* average_factor is between - 0 . 5 dB and 0 . 5 dB .
*
* Just do - 1 dB steps and find the point where
* - 0 . 5 dB * - i dB = 0 x10000 * 10 ^ ( - 0 . 5 / 10 ) / i dB
* = div_by_db ( 0 xe429 , i )
* is smaller than average_factor .
*/
for (i = 0 ; average_factor < iwl_div_by_db(0 xe429, i); i++) {
/* nothing */
}
return clamp(average_magnitude - i, -128 , 0 );
}
IWL_EXPORT_SYMBOL(iwl_average_neg_dbm);
Messung V0.5 in Prozent C=95 H=88 G=91
¤ Dauer der Verarbeitung: 0.9 Sekunden
(vorverarbeitet am 2026-10-04)
¤
*© Formatika GbR, Deutschland