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