#!/bin/sh #- # SPDX-License-Identifier: BSD-2-Clause # # Copyright (c) 2024 The FreeBSD Foundation # # This software was developed by Björn Zeeb # under sponsorship from the FreeBSD Foundation. # # This is neither efficient nor elegant but we need it few times # a year and it does the job. # # # USAGE: please check out the correct tag/hash for ports in the # linux-firmware.git repository you point this script to. # # USAGE: please make sure to pre-load if_iwlwifi.ko so that we # have access to the sysctl. You do not need to have a supported # card in the system. # In case that is not possible you can save the results to a file # and provide that locally. It will be renamed at the end of the # run. # set -e # sysctl -n compat.linuxkpi.iwlwifi_pci_ids_name > iwlwifi_pci_ids_name.txt PCI_IDS_FILE=iwlwifi_pci_ids_name.txt D_PCI_IDS_FILE=`pwd`/${PCI_IDS_FILE} ################################################################################ # # Check pre-reqs # if [ $# -ne 1 ]; then printf "USAGE: %s /path/to/linux-firmware.git\n" $0 >&2 exit 1 fi if [ ! -d cfg/ -o ! -e cfg/bz.c ]; then printf "ERROR: run from iwlwifi driver directory; no cfg/bz.c here\n" >&2 exit 1 fi LFWDIR=${1} if test ! -d ${LFWDIR} -o ! -e ${LFWDIR}/WHENCE; then printf "ERROR: cannot find linux-firmware.git at '%s'\n" ${LFWDIR} >&2 exit 1 fi kldstat -n if_iwlwifi.ko > /dev/null 2>&1 rc=$? case ${rc} in 0) ;; *) printf "ERROR: please pre-load if_iwlwifi.ko (you do not need a device)\n" >&2 exit 1 ;; esac if test -r ${D_PCI_IDS_FILE}; then printf "NOTICE: using proovided ${D_PCI_IDS_FILE}\n" >&2 else sysctl -N compat.linuxkpi.iwlwifi_pci_ids_name > /dev/null 2>&1 rc=$? case ${rc} in 0) sysctl -n compat.linuxkpi.iwlwifi_pci_ids_name > ${D_PCI_IDS_FILE} ;; *) printf "ERROR: cannot get compat.linuxkpi.iwlwifi_pci_ids_name\n" >&2 exit 1 ;; esac fi # We need to be in the config directory for simplicity. cd cfg ################################################################################ # Get a list of all device/firmware flavors as seen/supported by the driver. flavors=$(awk -F\\t '{ if (/^$/) { next; } if ($5 == "undefined") { next; } print tolower($5); }' ${D_PCI_IDS_FILE} | sort -V | uniq) ################################################################################ # # Helper functions. # # # This uses a hack (cpp) to expand some macros for us and parses out the result # which is the firmware name with the maximum FW version supported for that # firmware. # We then go and check that said firmware actually exists in linux-firmware.git. # We try to find a lower version number if the "MAX" version given from the cpp # output does not (yet) publicly exist. # .pnvm files are now properly listed as MODULE_FIRMWARE so no more magic needed # for them. # Given the filename matches a "flavor" at this point, we then group all the # available firmware files from this flavor together and print it as a ports # Makefile variable. # # We also print some other meta-data that callers will filter out depending on # their needs to generate other lists and mappings. # # For each get a list of firmware names we know. list_fw() { for f in ${flavors}; do #echo "==> ${f}" #awk -F \\t -v flav=${f} '{ # if ($5 != flav) { next; } # # No firmwre; skip. # if ($3 ~ /^$/) { next; } # if ($3 == "(null)") { next; }; # print $3; #}' ${D_PCI_IDS_FILE} | sort | uniq # For now the flavor names and the file names are 1:1 which makes this # a lot easier (given some sysctl/file entries are not able to list # their firmware but we know their "flavor". l=$(cpp ${f}.c 2>&1 | awk ' /^MODULE_FIRMWARE\(/ { gsub(/"/, ""); gsub("__stringify\\(", ""); gsub("\\);$", ""); gsub("\\)", ""); gsub("^MODULE_FIRMWARE\\(", ""); gsub(" ", ""); printf "%s\n", $0; }' | sort -V | uniq) #echo "${l}" lx="" for fx in ${l}; do if test -e ${LFWDIR}/${fx}; then lx="${lx} ${fx}" # Check for matching .pnvm file. # They are now properly listed in MODULE_FIRMWARE() as well so no more magic. #px=$(echo ${fx} | awk '{ gsub("-[[:digit:]]*.ucode", ".pnvm"); print; }') #if test -e ${LFWDIR}/${px}; then # lx="${lx} ${px}" #fi else case "${fx}" in *.pnvm) printf "NOTICE: pnvm file not found for '%s'\n" ${fx} >&2 ;; *.ucode) # Try lowering the version number. bn=$(echo ${fx} | awk '{ gsub("-[[:digit:]]*.ucode", ""); print; }') vn=$(echo ${fx} | awk '{ gsub(".ucode$", ""); gsub("^.*-", ""); print; }') #echo "BN ${bn} VN ${vn}" # Single digits are not zero-padded so just ${i} will be fine. for i in `jot ${vn} ${vn} 1`; do xn="${bn}-${i}.ucode" if test -e ${LFWDIR}/${xn}; then lx="${lx} ${xn}" break fi done ;; *) printf "NOTICE: file for unknown firmware type not found for '%s'\n" ${fx} >&2 ;; esac fi done # Get a count so we can automatically add \\ apart from the last line. fn=$(echo "${lx}" | wc -w | awk '{ print $1 }') #echo "==> ${f} :: ${fn} :: ${lx}" if test ${fn} -gt 0; then # Ports FLAVOR names are [a-z0-9_]. If needed add more mangling magic here. flav=`echo ${f} | awk '{ printf "%s", tolower($0); }'` echo "FWS ${flav}" echo "DISTFILES_${flav}= \\" for fz in ${lx}; do echo "${fz}"; done | \ awk -v fn=$fn -v fwg=${flav} '{ if (FNR == fn) { x="" } else { x=" \\" }; printf "\t%s${DISTURL_SUFFIX}%s\n", $0, x; fwn=$0; gsub("-[[:digit:]]*\.ucode$", "", fwn); printf "FWGET %s %s\n", fwg, fwn; }' fi done } ################################################################################ # # Generate the PORTS file template. # fwsl=$(list_fw | grep ^FWS | awk '{ print $2 }') # Get a count so we can automatically add \\ apart from the last line. fn=$(echo "${fwsl}" | wc -w | awk '{ print $1 }') if test ${fn} -gt 0; then portsfile=$(mktemp -p /tmp iwlwifi-fwport.XXXXXX) :> ${portsfile} ( echo "FWSUBS= \\" for sz in ${fwsl}; do echo "${sz}"; done | \ awk -v fn=$fn '{ if (FNR == fn) { x="" } else { x=" \\" }; printf "\t%s%s\n", $0, x; }' echo echo "# Do not prefix with empty \${FWSUBDIR}/!" list_fw | grep -v ^FWS | grep -v ^FWGET echo echo "DISTFILES_\${FWDRV}= \\" for sz in ${fwsl}; do echo "${sz}"; done | \ awk -v fn=$fn '{ if (FNR == fn) { x="" } else { x=" \\" }; printf "\t${DISTFILES_%s}%s\n", $0, x; }' echo "DISTFILES_\${FWDRV}_lic=" ) >> ${portsfile} printf "INFO: wifi-firmware-iwlwifi-kmod template at %s\n" ${portsfile} >&2 fi ################################################################################ # # Generate a temporary firmware -> flavor mapping table for fwget generation. # mapfile=$(mktemp -p /tmp iwlwifi-mapfile.XXXXXX) :> ${mapfile} fwgl=$(list_fw | grep FWGET) # Get a count so we can automatically add \\ apart from the last line. fn=$(echo "${fwgl}" | wc -w | awk '{ print $1 }') if test ${fn} -gt 0; then ( list_fw | grep FWGET | grep -v '.pnvm' | \ while read x flav fw; do printf "%s\t%s\n" ${fw} ${flav} done | \ sort -n | uniq ) >> ${mapfile} fi ################################################################################ # # Try to generate the PCI ID -> port flavor mapping # # We get PCI ID, description, firmware base from the sysctl and can work our # way back from fw name base to flavor via the mapping table file. # fwgetfile=$(mktemp -p /tmp iwlwifi-fwget.XXXXXX) :> ${fwgetfile} awk 'BEGIN { FS="\t"; } { # Skip empty lines. if (/^$/) { next; } # Skip "undefined" flavors as we have no idea what chipset. if ($5 == "undefined") { next; } # No firmware name; do not skip! # All we need is the flavor, which we now always have. #if ($3 == "(null)") { next; }; FLAV=tolower($5); split($1, i, "/"); gsub("\t.*$", "", i[4]); # Not an Intel Vednor ID; skip. if (i[1] != "0x8086") { next; }; # No defined device ID; skip. if (i[2] == "0xffff") { next; }; # Adjust wildcards or a ill-printed 0. if (i[3] == "0xffffffff") { i[3] = "*"; }; if (i[4] == "000000") { i[4] = "0x0000"; }; if (i[4] == "0xffffffff") { i[4] = "*"; }; if (i[4] == "0xffff") { i[4] = "*"; }; printf "%s\t%s/%s/%s\n", FLAV, i[2], i[3], i[4]; }' ${D_PCI_IDS_FILE} | \ sort -V | uniq | \ while read flav match; do #flav=$(awk -v fw=$fw '{ if ($1 == fw) { print $2; } }' ${mapfile}) #echo "${fw} :: ${match} :: ${flav}" if test "${flav}" != ""; then printf "${flav}\t${match}\t${flav}\n" else #echo "NO FLAV ${fw} ${match}" >&2 fi done | \ awk 'BEGIN { FS="\t"; FWN=""; } { FW=$1; if (FWN != FW) { printf "\n\t# %s\n", FW; FWN=FW; }; printf "\t%s) addpkg \"wifi-firmware-iwlwifi-kmod-%s\"; return 1 ;;\n", $2, $3; } END { printf "\n"; }' >> ${fwgetfile} printf "INFO: fwget pci_network_intel template at %s\n" ${fwgetfile} >&2 ################################################################################ # # Try to build the iwlwififw.4 bits too. # dl=$(grep -v ^$ ${D_PCI_IDS_FILE} | uniq | \ awk ' { # Sourt out duplicate lines. if (dup[$0]++) { next; } # my ($ids, $name, $fw) = split /\t/; split($0, a, "\t"); ids=a[1]; name=a[2]; fw=a[3]; #my ($v, $d, $sv, $sd) = split("/", $ids); split(ids, i, "/"); gsub("^0xffff+", "any", i[1]); gsub("^0xffff+", "any", i[2]); gsub("^0xffff+", "any", i[3]); gsub("^0xffff+", "any", i[4]); if (name == "") { name="(unknown)"; } if (fw == "") { fw="(unknown)"; } # iwlwififw.4 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; # wiki # XXX TODO possibly quote some in `` to avoid automatic linking? # || PCI IDs || Chipset Name || Firmware prefix || Comment || printf "WIKI || %s / %s / %s / %s || %s || %s || ||\n", i[1], i[2], i[3], i[4], name, fw; if ((FNR % 25) == 0) { printf "WIKI \n"; } }') manfwfile=$(mktemp -p /tmp iwlwifi-iwlwififw4.XXXXXX) :> ${manfwfile} echo "${dl}" | grep -v ^WIKI >> ${manfwfile} printf "INFO: share/man/man4/iwlwififw.4 template at %s\n" ${manfwfile} >&2 wikifile=$(mktemp -p /tmp iwlwifi-wiki.XXXXXX) :> ${wikifile} echo "${dl}" | awk '/^WIKI / { gsub("^WIKI ", ""); print; }' >> ${wikifile} printf "INFO: WIKI template at %s\n" ${wikifile} >&2 ################################################################################ # # Cleanup # rm ${mapfile} mv -f ${D_PCI_IDS_FILE} ${D_PCI_IDS_FILE}.old # end