Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/contrib/dev/iwlwifi/iwl-utils.c
105687 views
1
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
2
/*
3
* Copyright (C) 2024-2025 Intel Corporation
4
*/
5
#include <net/gso.h>
6
#include <linux/ieee80211.h>
7
#include <net/ip.h>
8
9
#include "iwl-drv.h"
10
#include "iwl-utils.h"
11
12
#ifdef CONFIG_INET
13
int iwl_tx_tso_segment(struct sk_buff *skb, unsigned int num_subframes,
14
netdev_features_t netdev_flags,
15
struct sk_buff_head *mpdus_skbs)
16
{
17
struct sk_buff *tmp, *next;
18
struct ieee80211_hdr *hdr = (void *)skb->data;
19
char cb[sizeof(skb->cb)];
20
u16 i = 0;
21
unsigned int tcp_payload_len;
22
unsigned int mss = skb_shinfo(skb)->gso_size;
23
bool ipv4 = (skb->protocol == htons(ETH_P_IP));
24
bool qos = ieee80211_is_data_qos(hdr->frame_control);
25
u16 ip_base_id = ipv4 ? ntohs(ip_hdr(skb)->id) : 0;
26
27
skb_shinfo(skb)->gso_size = num_subframes * mss;
28
memcpy(cb, skb->cb, sizeof(cb));
29
30
next = skb_gso_segment(skb, netdev_flags);
31
skb_shinfo(skb)->gso_size = mss;
32
skb_shinfo(skb)->gso_type = ipv4 ? SKB_GSO_TCPV4 : SKB_GSO_TCPV6;
33
34
if (IS_ERR(next) && PTR_ERR(next) == -ENOMEM)
35
return -ENOMEM;
36
37
if (WARN_ONCE(IS_ERR(next),
38
"skb_gso_segment error: %d\n", (int)PTR_ERR(next)))
39
return PTR_ERR(next);
40
41
if (next)
42
consume_skb(skb);
43
44
skb_list_walk_safe(next, tmp, next) {
45
memcpy(tmp->cb, cb, sizeof(tmp->cb));
46
/*
47
* Compute the length of all the data added for the A-MSDU.
48
* This will be used to compute the length to write in the TX
49
* command. We have: SNAP + IP + TCP for n -1 subframes and
50
* ETH header for n subframes.
51
*/
52
tcp_payload_len = skb_tail_pointer(tmp) -
53
skb_transport_header(tmp) -
54
tcp_hdrlen(tmp) + tmp->data_len;
55
56
if (ipv4)
57
ip_hdr(tmp)->id = htons(ip_base_id + i * num_subframes);
58
59
if (tcp_payload_len > mss) {
60
skb_shinfo(tmp)->gso_size = mss;
61
skb_shinfo(tmp)->gso_type = ipv4 ? SKB_GSO_TCPV4 :
62
SKB_GSO_TCPV6;
63
} else {
64
if (qos) {
65
u8 *qc;
66
67
if (ipv4)
68
ip_send_check(ip_hdr(tmp));
69
70
qc = ieee80211_get_qos_ctl((void *)tmp->data);
71
*qc &= ~IEEE80211_QOS_CTL_A_MSDU_PRESENT;
72
}
73
skb_shinfo(tmp)->gso_size = 0;
74
}
75
76
skb_mark_not_on_list(tmp);
77
__skb_queue_tail(mpdus_skbs, tmp);
78
i++;
79
}
80
81
return 0;
82
}
83
IWL_EXPORT_SYMBOL(iwl_tx_tso_segment);
84
#endif /* CONFIG_INET */
85
86
static u32 iwl_div_by_db(u32 value, u8 db)
87
{
88
/*
89
* 2^32 * 10**(i / 10) for i = [1, 10], skipping 0 and simply stopping
90
* at 10 dB and looping instead of using a much larger table.
91
*
92
* Using 64 bit math is overkill, but means the helper does not require
93
* a limit on the input range.
94
*/
95
static const u32 db_to_val[] = {
96
0xcb59185e, 0xa1866ba8, 0x804dce7a, 0x65ea59fe, 0x50f44d89,
97
0x404de61f, 0x331426af, 0x2892c18b, 0x203a7e5b, 0x1999999a,
98
};
99
100
while (value && db > 0) {
101
u8 change = min_t(u8, db, ARRAY_SIZE(db_to_val));
102
103
value = (((u64)value) * db_to_val[change - 1]) >> 32;
104
105
db -= change;
106
}
107
108
return value;
109
}
110
111
s8 iwl_average_neg_dbm(const u8 *neg_dbm_values, u8 len)
112
{
113
int average_magnitude;
114
u32 average_factor;
115
int sum_magnitude = -128;
116
u32 sum_factor = 0;
117
int i, count = 0;
118
119
/*
120
* To properly average the decibel values (signal values given in dBm)
121
* we need to do the math in linear space. Doing a linear average of
122
* dB (dBm) values is a bit annoying though due to the large range of
123
* at least -10 to -110 dBm that will not fit into a 32 bit integer.
124
*
125
* A 64 bit integer should be sufficient, but then we still have the
126
* problem that there are no directly usable utility functions
127
* available.
128
*
129
* So, lets not deal with that and instead do much of the calculation
130
* with a 16.16 fixed point integer along with a base in dBm. 16.16 bit
131
* gives us plenty of head-room for adding up a few values and even
132
* doing some math on it. And the tail should be accurate enough too
133
* (1/2^16 is somewhere around -48 dB, so effectively zero).
134
*
135
* i.e. the real value of sum is:
136
* sum = sum_factor / 2^16 * 10^(sum_magnitude / 10) mW
137
*
138
* However, that does mean we need to be able to bring two values to
139
* a common base, so we need a helper for that.
140
*
141
* Note that this function takes an input with unsigned negative dBm
142
* values but returns a signed dBm (i.e. a negative value).
143
*/
144
145
for (i = 0; i < len; i++) {
146
int val_magnitude;
147
u32 val_factor;
148
149
/* Assume invalid */
150
if (neg_dbm_values[i] == 0xff)
151
continue;
152
153
val_factor = 0x10000;
154
val_magnitude = -neg_dbm_values[i];
155
156
if (val_magnitude <= sum_magnitude) {
157
u8 div_db = sum_magnitude - val_magnitude;
158
159
val_factor = iwl_div_by_db(val_factor, div_db);
160
val_magnitude = sum_magnitude;
161
} else {
162
u8 div_db = val_magnitude - sum_magnitude;
163
164
sum_factor = iwl_div_by_db(sum_factor, div_db);
165
sum_magnitude = val_magnitude;
166
}
167
168
sum_factor += val_factor;
169
count++;
170
}
171
172
/* No valid noise measurement, return a very high noise level */
173
if (count == 0)
174
return 0;
175
176
average_magnitude = sum_magnitude;
177
average_factor = sum_factor / count;
178
179
/*
180
* average_factor will be a number smaller than 1.0 (0x10000) at this
181
* point. What we need to do now is to adjust average_magnitude so that
182
* average_factor is between -0.5 dB and 0.5 dB.
183
*
184
* Just do -1 dB steps and find the point where
185
* -0.5 dB * -i dB = 0x10000 * 10^(-0.5/10) / i dB
186
* = div_by_db(0xe429, i)
187
* is smaller than average_factor.
188
*/
189
for (i = 0; average_factor < iwl_div_by_db(0xe429, i); i++) {
190
/* nothing */
191
}
192
193
return clamp(average_magnitude - i, -128, 0);
194
}
195
IWL_EXPORT_SYMBOL(iwl_average_neg_dbm);
196
197