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