1#!/bin/sh 2#- 3# SPDX-License-Identifier: BSD-2-Clause 4# 5# Copyright (c) 2024-2025 The FreeBSD Foundation 6# 7# This software was developed by Björn Zeeb 8# under sponsorship from the FreeBSD Foundation. 9# 10# This is neither efficient nor elegant but we need it few times 11# a year and it does the job. 12# 13# 14# USAGE: please check out the correct tag/hash for ports in the 15# linux-firmware.git repository you point this script to. 16# 17# USAGE: please make sure to pre-load if_iwlwifi.ko so that we 18# have access to the sysctl. You do not need to have a supported 19# card in the system. 20# In case that is not possible you can save the results to a file 21# and provide that locally. It will be renamed at the end of the 22# run. 23# 24 25set -e 26 27# sysctl -n compat.linuxkpi.iwlwifi_pci_ids_name > iwlwifi_pci_ids_name.txt 28PCI_IDS_FILE=iwlwifi_pci_ids_name.txt 29D_PCI_IDS_FILE=`pwd`/${PCI_IDS_FILE} 30 31################################################################################ 32# 33# Check pre-reqs 34# 35if [ $# -ne 1 ]; then 36 printf "USAGE: %s /path/to/linux-firmware.git\n" $0 >&2 37 exit 1 38fi 39 40if [ ! -d cfg/ -o ! -e cfg/bz.c ]; then 41 printf "ERROR: run from iwlwifi driver directory; no cfg/bz.c here\n" >&2 42 exit 1 43fi 44 45LFWDIR=${1} 46if test ! -d ${LFWDIR} -o ! -e ${LFWDIR}/WHENCE; then 47 printf "ERROR: cannot find linux-firmware.git at '%s'\n" ${LFWDIR} >&2 48 exit 1 49fi 50 51if test -r ${D_PCI_IDS_FILE}; then 52 printf "NOTICE: using proovided ${D_PCI_IDS_FILE}\n" >&2 53else 54 55 kldstat -n if_iwlwifi.ko > /dev/null 2>&1 56 rc=$? 57 case ${rc} in 58 0) ;; 59 *) printf "ERROR: please pre-load if_iwlwifi.ko (you do not need a device)\n" >&2 60 exit 1 61 ;; 62 esac 63 64 sysctl -N compat.linuxkpi.iwlwifi_pci_ids_name > /dev/null 2>&1 65 rc=$? 66 case ${rc} in 67 0) sysctl -n compat.linuxkpi.iwlwifi_pci_ids_name > ${D_PCI_IDS_FILE} 68 ;; 69 *) printf "ERROR: cannot get compat.linuxkpi.iwlwifi_pci_ids_name\n" >&2 70 exit 1 71 ;; 72 esac 73fi 74 75# We need to be in the config directory for simplicity. 76cd cfg 77 78################################################################################ 79 80# Get a list of all device/firmware flavors as seen/supported by the driver. 81flavors=$(awk -F\\t '{ 82 if (/^$/) { next; } 83 if ($5 == "undefined") { next; } 84 print tolower($5); 85}' ${D_PCI_IDS_FILE} | sort -V | uniq) 86 87################################################################################ 88# 89# Helper functions. 90# 91 92# 93# This uses a hack (cpp) to expand some macros for us and parses out the result 94# which is the firmware name with the maximum FW version supported for that 95# firmware. 96# We then go and check that said firmware actually exists in linux-firmware.git. 97# We try to find a lower version number if the "MAX" version given from the cpp 98# output does not (yet) publicly exist. 99# .pnvm files are now properly listed as MODULE_FIRMWARE so no more magic needed 100# for them. 101# Given the filename matches a "flavor" at this point, we then group all the 102# available firmware files from this flavor together and print it as a ports 103# Makefile variable. 104# 105# We also print some other meta-data that callers will filter out depending on 106# their needs to generate other lists and mappings. 107# 108 109# For each get a list of firmware names we know. 110list_fw() 111{ 112 for f in ${flavors}; do 113 #echo "==> ${f}" 114 #awk -F \\t -v flav=${f} '{ 115 # if ($5 != flav) { next; } 116 # # No firmwre; skip. 117 # if ($3 ~ /^$/) { next; } 118 # if ($3 == "(null)") { next; }; 119 # print $3; 120 #}' ${D_PCI_IDS_FILE} | sort | uniq 121 122 # For now the flavor names and the file names are 1:1 which makes this 123 # a lot easier (given some sysctl/file entries are not able to list 124 # their firmware but we know their "flavor". 125 l=$(cpp ${f}.c 2>&1 | awk ' 126 /^MODULE_FIRMWARE\(/ { 127 gsub(/"/, ""); 128 gsub("__stringify\\(", ""); 129 gsub("\\);$", ""); 130 gsub("\\)", ""); 131 gsub("^MODULE_FIRMWARE\\(", ""); 132 gsub(" ", ""); 133 printf "%s\n", $0; 134 }' | sort -V | uniq) 135 #echo "${l}" 136 137 lx="" 138 for fx in ${l}; do 139 if test -e ${LFWDIR}/${fx}; then 140 lx="${lx} ${fx}" 141 142 # Check for matching .pnvm file. 143 # They are now properly listed in MODULE_FIRMWARE() as well so no more magic. 144 #px=$(echo ${fx} | awk '{ gsub("-[[:digit:]]*.ucode", ".pnvm"); print; }') 145 #if test -e ${LFWDIR}/${px}; then 146 # lx="${lx} ${px}" 147 #fi 148 else 149 case "${fx}" in 150 *.pnvm) 151 printf "NOTICE: pnvm file not found for '%s'\n" ${fx} >&2 152 ;; 153 *.ucode) 154 # Try lowering the version number. 155 bn=$(echo ${fx} | awk '{ gsub("-[[:digit:]]*.ucode", ""); print; }') 156 vn=$(echo ${fx} | awk '{ gsub(".ucode$", ""); gsub("^.*-", ""); print; }') 157 #echo "BN ${bn} VN ${vn}" 158 # Single digits are not zero-padded so just ${i} will be fine. 159 for i in `jot ${vn} ${vn} 1`; do 160 xn="${bn}-${i}.ucode" 161 if test -e ${LFWDIR}/${xn}; then 162 lx="${lx} ${xn}" 163 break 164 fi 165 done 166 ;; 167 *) 168 printf "NOTICE: file for unknown firmware type not found for '%s'\n" ${fx} >&2 169 ;; 170 esac 171 fi 172 done 173 174 # Get a count so we can automatically add \\ apart from the last line. 175 fn=$(echo "${lx}" | wc -w | awk '{ print $1 }') 176 177 #echo "==> ${f} :: ${fn} :: ${lx}" 178 179 if test ${fn} -gt 0; then 180 181 # Ports FLAVOR names are [a-z0-9_]. If needed add more mangling magic here. 182 flav=`echo ${f} | awk '{ printf "%s", tolower($0); }'` 183 184 echo "FWS ${flav}" 185 echo "DISTFILES_${flav}= \\" 186 for fz in ${lx}; do echo "${fz}"; done | \ 187 awk -v fn=$fn -v fwg=${flav} '{ 188 if (FNR == fn) { x="" } else { x=" \\" }; 189 printf "\t%s${DISTURL_SUFFIX}%s\n", $0, x; 190 fwn=$0; 191 gsub("-[[:digit:]]*\.ucode$", "", fwn); 192 printf "FWGET %s %s\n", fwg, fwn; 193 }' 194 fi 195 196 done 197} 198 199################################################################################ 200# 201# Generate the PORTS file template. 202# 203 204fwsl=$(list_fw | grep ^FWS | awk '{ print $2 }') 205# Get a count so we can automatically add \\ apart from the last line. 206fn=$(echo "${fwsl}" | wc -w | awk '{ print $1 }') 207 208if test ${fn} -gt 0; then 209 210 portsfile=$(mktemp -p /tmp iwlwifi-fwport.XXXXXX) 211 212 :> ${portsfile} 213 ( 214 echo "FWSUBS= \\" 215 for sz in ${fwsl}; do echo "${sz}"; done | \ 216 awk -v fn=$fn '{ if (FNR == fn) { x="" } else { x=" \\" }; printf "\t%s%s\n", $0, x; }' 217 218 echo 219 echo "# Do not prefix with empty \${FWSUBDIR}/!" 220 list_fw | grep -v ^FWS | grep -v ^FWGET 221 222 echo 223 echo "DISTFILES_\${FWDRV}= \\" 224 for sz in ${fwsl}; do echo "${sz}"; done | \ 225 awk -v fn=$fn '{ if (FNR == fn) { x="" } else { x=" \\" }; printf "\t${DISTFILES_%s}%s\n", $0, x; }' 226 echo "DISTFILES_\${FWDRV}_lic=" 227 ) >> ${portsfile} 228 229 printf "INFO: wifi-firmware-iwlwifi-kmod template at %s\n" ${portsfile} >&2 230fi 231 232################################################################################ 233# 234# Generate a temporary firmware -> flavor mapping table for fwget generation. 235# 236 237mapfile=$(mktemp -p /tmp iwlwifi-mapfile.XXXXXX) 238:> ${mapfile} 239 240fwgl=$(list_fw | grep FWGET) 241# Get a count so we can automatically add \\ apart from the last line. 242fn=$(echo "${fwgl}" | wc -w | awk '{ print $1 }') 243if test ${fn} -gt 0; then 244 245 ( 246 list_fw | grep FWGET | grep -v '.pnvm' | \ 247 while read x flav fw; do 248 printf "%s\t%s\n" ${fw} ${flav} 249 done | \ 250 sort -n | uniq 251 ) >> ${mapfile} 252fi 253 254################################################################################ 255# 256# Try to generate the PCI ID -> port flavor mapping 257# 258# We get PCI ID, description, firmware base from the sysctl and can work our 259# way back from fw name base to flavor via the mapping table file. 260# 261 262fwgetfile=$(mktemp -p /tmp iwlwifi-fwget.XXXXXX) 263:> ${fwgetfile} 264 265awk 'BEGIN { FS="\t"; } 266{ 267 # Skip empty lines. 268 if (/^$/) { next; } 269 # Skip "undefined" flavors as we have no idea what chipset. 270 if ($5 == "undefined") { next; } 271 272 # No firmware name; do not skip! 273 # All we need is the flavor, which we now always have. 274 #if ($3 == "(null)") { next; }; 275 276 FLAV=tolower($5); 277 278 split($1, i, "/"); 279 gsub("\t.*$", "", i[4]); 280 281 # Not an Intel Vednor ID; skip. 282 if (i[1] != "0x8086") { next; }; 283 284 # No defined device ID; skip. 285 if (i[2] == "0xffff") { next; }; 286 287 # Adjust wildcards or a ill-printed 0. 288 if (i[3] == "0xffffffff") { i[3] = "*"; }; 289 if (i[4] == "000000") { i[4] = "0x0000"; }; 290 if (i[4] == "0xffffffff") { i[4] = "*"; }; 291 if (i[4] == "0xffff") { i[4] = "*"; }; 292 293 printf "%s\t%s/%s/%s\n", FLAV, i[2], i[3], i[4]; 294}' ${D_PCI_IDS_FILE} | \ 295sort -V | uniq | \ 296while read flav match; do 297 298 #flav=$(awk -v fw=$fw '{ if ($1 == fw) { print $2; } }' ${mapfile}) 299 #echo "${fw} :: ${match} :: ${flav}" 300 301 if test "${flav}" != ""; then 302 printf "${flav}\t${match}\t${flav}\n" 303 else 304 #echo "NO FLAV ${fw} ${match}" >&2 305 fi 306 307done | \ 308awk 'BEGIN { FS="\t"; FWN=""; } 309{ 310 FW=$1; 311 if (FWN != FW) { printf "\n\t# %s\n", FW; FWN=FW; }; 312 313 printf "\t%s) addpkg \"wifi-firmware-iwlwifi-kmod-%s\"; return 1 ;;\n", $2, $3; 314} END { 315 printf "\n"; 316}' >> ${fwgetfile} 317 318printf "INFO: fwget pci_network_intel template at %s\n" ${fwgetfile} >&2 319 320################################################################################ 321# 322# Try to build the iwlwififw.4 bits too. 323# 324 325dl=$(grep -v ^$ ${D_PCI_IDS_FILE} | uniq | \ 326awk ' 327{ 328 # Sourt out duplicate lines. 329 if (dup[$0]++) { next; } 330 331 split($0, a, "\t"); 332 ids=a[1]; 333 name=a[2]; 334 fw=a[3]; 335 flavor=a[5]; 336 337 #my ($v, $d, $sv, $sd) = split("/", $ids); 338 split(ids, i, "/"); 339 gsub("^0xffff+", "any", i[1]); 340 gsub("^0xffff+", "any", i[2]); 341 gsub("^0xffff+", "any", i[3]); 342 gsub("^0xffff+", "any", i[4]); 343 344 if (name == "") { name="(unknown)"; } 345 if (fw == "") { fw="(unknown)"; } 346 if (flavor == "") { flavor="iwlwifi"; } 347 if (flavor == "undefined") { flavor="iwlwifi"; } 348 349 # iwlwififw.4 350 printf ".It \"\"\n.It %s\n.It %s Ta %s Ta %s Ta %s Ta %s Ta %s\n", name, i[1], i[2], i[3], i[4], flavor, fw; 351 352 # wiki 353 # XXX TODO possibly quote some in `` to avoid automatic linking? 354 # || PCI IDs || Chipset Name || Firmware prefix || Comment || 355 printf "WIKI || %s / %s / %s / %s || %s || %s || ||\n", i[1], i[2], i[3], i[4], name, fw; 356 if ((FNR % 25) == 0) { printf "WIKI \n"; } 357}') 358 359manfwfile=$(mktemp -p /tmp iwlwifi-iwlwififw4.XXXXXX) 360:> ${manfwfile} 361echo "${dl}" | grep -v ^WIKI >> ${manfwfile} 362printf "INFO: share/man/man4/iwlwififw.4 template at %s\n" ${manfwfile} >&2 363 364wikifile=$(mktemp -p /tmp iwlwifi-wiki.XXXXXX) 365:> ${wikifile} 366echo "${dl}" | awk '/^WIKI / { gsub("^WIKI ", ""); print; }' >> ${wikifile} 367printf "INFO: WIKI template at %s\n" ${wikifile} >&2 368 369 370################################################################################ 371# 372# Cleanup 373# 374rm ${mapfile} 375mv -f ${D_PCI_IDS_FILE} ${D_PCI_IDS_FILE}.old 376 377# end 378