GPIO LED control

Sorry to be ignorant, but programmatically, where would that enable command be put to automatically enable it? And how do the others in init.d get started?

Actually, I put ā€œservice hostap-leds enableā€ in /etc/rc.local and that seemed to start hostap-leds. I see the logread messages I put in there for the file and start of each function. However, when the client connected or disconnected, the clientconnected.sh did not get run. I have log messages in that, too, per previous note. I see those when I conn/disc if I run the ā€œhostapd_cli_s1g -i wlan0 -a /lib/functions/clientconnected.shā€ in the console (shell), but not from the service enable in rc.local.
Note the ā€œhostap-ledsā€ items in the log below. Lines that say ā€œhostap-leds launchedā€ is at the top of the ā€œhostap-ledsā€ file.

Thu Dec 12 14:47:43 2024 user.notice -p: daemon.crit hostap-leds launched...
Thu Dec 12 14:47:44 2024 daemon.notice netifd: lan (3013): udhcpc: broadcasting discover
Thu Dec 12 14:47:44 2024 daemon.notice netifd: lan (3013): udhcpc: broadcasting select for 192.168.1.2, server 192.168.1.1
Thu Dec 12 14:47:44 2024 daemon.notice netifd: lan (3013): udhcpc: lease of 192.168.1.2 obtained from 192.168.1.1, lease time 86400
Thu Dec 12 14:47:44 2024 daemon.crit hostap-leds: hostap-leds start_service()...
Thu Dec 12 14:47:44 2024 daemon.crit hostap-leds: hostap-leds start_cli()...
Thu Dec 12 14:47:44 2024 daemon.crit hostap-leds: hostap-leds start_cli()...
Thu Dec 12 14:47:44 2024 daemon.crit hostap-leds: hostap-leds service_triggers()...
Thu Dec 12 14:47:44 2024 daemon.notice hostapd: Set new config for phy phy0:
Thu Dec 12 14:47:45 2024 daemon.notice netifd: Interface 'lan' is now up
Thu Dec 12 14:47:45 2024 daemon.notice netifd: Wireless device 'radio0' is now up
Thu Dec 12 14:47:45 2024 user.notice -p: daemon.crit hostap-leds launched...
Thu Dec 12 14:47:46 2024 kern.info kernel: [   65.452021] Resetting Morse Chip
Thu Dec 12 14:47:46 2024 kern.info kernel: [   65.478498] Done
Thu Dec 12 14:47:46 2024 user.notice root: Prplmesh is disabled
Thu Dec 12 14:47:46 2024 kern.info kernel: [   65.628760] morse_sdio mmc0:0001:1: sdio removed func 1 vendor 0x325b device 0x306
Thu Dec 12 14:47:46 2024 kern.info kernel: [   65.644719] mmc0: card 0001 removed
Thu Dec 12 14:47:46 2024 kern.info kernel: [   65.713854] mmc0: new high speed SDIO card at address 0001
Thu Dec 12 14:47:46 2024 kern.debug kernel: [   65.725085] mmcblk mmc0:0001: no of_node; not parsing pinctrl DT
Thu Dec 12 14:47:46 2024 user.notice HOTPLUG: 05-getapipaddr ****** 'ifup' - device:'br0' interface:'lan' ******

I really should have tested the script before sharing it! Thereā€™s a few lines in the start_cli function which are definitely going to cause premature exit as they are looking for a configuration option you likely have not enabled. These lines are artefacts of me modifying a previous script - I must have just missed them.

Please remove

        config_get_bool enabled "$cfg" 'dcs' 0
        [ $enabled -ne 1 ] && return

from the start_cli() function in the procd script. Reboot or service hostap-leds restart once the changes are saved.

Bingo!! :wink: That worked, Arien. Thanks so much! And I left ā€œservice hostap-leds enableā€ in /etc/rc.local. I suspect you donā€™t need to put ā€œservice hostap-leds startā€ right after that, since it worked with just the enable. Correct?

I still need to finish addressing the issue of a client finding AP ip addr I raised in another thread. Iā€™ll reply to that one specifically as needed, not here.

1 Like

Arien - question for you on this. I have the hostap-leds in /etc/init.d as discussed above. Works great on the AP. However, the client doesnā€™t necessarily like having this. When I disable halow radio, it takes about 70 sec for the system to finally getting around to when it shows the line in the log ā€œdaemon.notice netifd: Interface ā€˜ahwlanā€™ is disabledā€. Without hostap-leds in the image when running as client, it is fairly quick and that is done in 11 sec after disabling halow radio in luci, which from what Iā€™ve seen before is typical.
Question is: what should I use to not have hostap-leds do anything if itā€™s running on a client?

Somewhere else in my code, after the device is setup as ap or client through the wizard, Iā€™ve checked wireless.default_radio1.mode. If itā€™s ā€˜staā€™, itā€™s client; if ā€˜apā€™, itā€™s ap and Iā€™ve acted accordingly in that other code. And if you havenā€™t run the wizard, that param is not even there. In this case, if Iā€™m looking to use hostap-leds do what it does, that parameter must exist and be an ā€˜apā€™.

What would you recommend as the best way to check this so it only does the hostap-leds if the device is ap?

Recall that post above about /lib/functions/clientconnected.sh with the /etc/init.d/hostap-leds hook for the timeout when a client goes away ungracefully? It works great, as i know when that happens. However, in the disconnected path of that sh, I was hoping it would allow me to change the led at that time. But, before clientconnected.sh exits, I call the routine (set_ap_halow_led) that checks if ANY clients are attached to know how to set the led. In this condition, since itā€™s still in the disconnected path before exiting, this client is still in the assoclist so the led doesnā€™t change until the cron I have setup to check every minute triggers again. Then, itā€™s then that timer fires to do ā€œiwinfo wlan0 assoclistā€, that it shows ā€œNo station connectedā€. I suspect itā€™s the timing of when hostap-leds hits that clientconnected.sh that it does that before the client is actually out of the assoclist. However, if I do the graceful disconnect by disabling the halow radio in network/wireless in luci, the same routine (set_ap_halow_led) works fine. If this was windows, which Iā€™m so much more familiar with, I could do a pump loop to process events which could allow processing things when youā€™re in a thread. Does this make sense? Iā€™m wondering how to deal with this in this openwrt linux? Ideas?

Question is: what should I use to not have hostap-leds do anything if itā€™s running on a client?

In order to get the Linux interface name from the UCI configuration, the script has to wait_for_ifname() which expects an ap to exist. Unfortunately, there is asynchronicity here and it can take some time between when this script is started after a wireless configuration change, to when a Linux interface is actually brought up. This script is a little naive, and just performs a busy loop with a 60 second timeout before it will fail. A slightly better design may have been to push the timeout into the procd command itself to prevent procd from blocking, however we would then lose the netdev parameter which would mean the service wonā€™t restart on any changes to the interface.

So continuing with the naive design, you definitely want a check in start_cli() to ensure there is an AP configured on the device before running this script. Hereā€™s an updated procd script which again, I havenā€™t tested. Note that Iā€™ve changed start_service to iterate over the configured list of wifi-iface instead of wifi-device as it does simplify this check a bit. We can also better filter for the ifname.

#!/bin/sh /etc/rc.common

. /lib/netifd/netifd-wireless.sh

START=99
STOP=10

USE_PROCD=1
PROG=/sbin/hostapd_cli_s1g
ACTION_SCRIPT=/lib/functions/clientconnected.sh
SERVICE=hostap-leds

wait_for_ifname() {
        local radio=$1
        local iface=$2
        local delay=${3-5}
        local timeout=60

        local start_time="$(date -u +%s)"
        local end_time=
        local elapsed=

        while true; do
                wifi status "$radio" | jsonfilter -e '@.*.interfaces[@.section="$iface"].ifname'
                [ -n "$ifname" ] && return 0;

                end_time="$(date -u +%s)"
                elapsed="$(($end_time-$start_time))"
                if [ "$elapsed" -gt "$timeout" ]; then
                        return 1
                fi

                sleep $delay
        done
}

start_cli() {
        local cfg="$1"    

        config_get mode "$cfg" mode
        if [ "$mode" != "ap" ]; then
                logger -t $SERVICE -p daemon.debug "wireless.${cfg} not an ap interface"
                return 1
        fi

        config_get radio "$cfg" device
        config_get type "$radio" 'type'
        if [ "$type" != "morse" ]; then
                logger -t $SERVICE -p daemon.crit "wireless.${radio} not a morse device!"
                return 1
        fi

        radiojs=$(wifi status $radio 2>/dev/null)
        if [ $? -ne 0 ]; then
                logger -t $SERVICE -p daemon.crit "Selected radio $radio does not exist!"
                return 1
        fi

        json_load "$radiojs"
        json_select $radio
        json_get_vars disabled
        set_default disabled 1
        if [ "$disabled" -ne 0 ]; then
                logger -t $SERVICE -p daemon.crit "$radio disabled"
                return 1
        fi

        ifname=
        networks=
        if ! wait_for_ifname $radio $cfg; then
                logger -t $SERVICE -p daemon.crit "Couldn't get ifname for $cfg on $radio"
                return 1
        fi

        procd_open_instance $radio.$ifname
        procd_set_param command $PROG -i "$ifname" -a $ACTION_SCRIPT
        procd_set_param file $ACTION_SCRIPT
        procd_set_param netdev "$ifname"
        procd_set_param stdout 1
        procd_set_param stderr 1
        procd_set_param respawn
        procd_close_instance
}

start_service() {
        config_load wireless
        config_foreach start_cli wifi-iface
}

service_triggers() {
        procd_add_reload_trigger wireless
        procd_add_reload_interface_trigger $networks
}

before clientconnected.sh exits, I call the routine (set_ap_halow_led) that checks if ANY clients are attached to know how to set the led. In this condition, since itā€™s still in the disconnected path before exiting, this client is still in the assoclist so the led doesnā€™t change until the cron I have setup to check every minute triggers again. Then, itā€™s then that timer fires to do ā€œiwinfo wlan0 assoclistā€, that it shows ā€œNo station connectedā€.

Interesting that the client is still in the list during the AP_STA_DISCONNECTED event. Can you run hostapd_cli all_sta and iwinfo wlan0 assoclist during this event so we can examine if there is a difference? Itā€™s possible that during the non-graceful disconnect hostap has registered the station leaving, but it hasnā€™t been propagated to underlying mac80211 layer yet and so iwinfo is still seeing the stations through netlink.

Hi, Arien. Question for you here.

When I factory reset then setup the client then ap using default ssid, I set the ap to be a bridge. Obviously it should be connected to a dhcp router which would assign the ap and client their ip addresses. This works perfectly fine when I switch the cable on the ap from the laptop to the router as soon as I click to leave the ap wizard. Then, the process of it getting a new i/f due to the router runs through that script you last gave me and my blue led on the ap turns on immediately since it goes through that clientconnected.sh script when the client halow connects.

However, if I donā€™t connect the router after the wizard, it doesnā€™t run through the hostap-leds script to setup the trigger to clientconnected.sh. I have checks in that script below to only do start_cli(), start_service() and service_triggers() if it is an ap. Before I connect the router, those routines show: ā€œthis is not an apā€. Thus, this script doesnā€™t activate to turn the ap blue led on immediately when thereā€™s a client halow connect.

This is a non-issue when this is setup as ap router, but I want to cover the base that if this is setup as ap bridge and either a router doesnā€™t exist or something happens to the router (i.e. say facility power loss that knocks out the router but our devices still work), that ap blue led will still work as we desire. That means another way to activate this script so it knows this is an ap after the wizard is complete, but not rely on the router connection.

Iā€™m trying to figure out a good spot to put this to accomplish this. What would you suggest?

top of script. Note what's added to rc.local:
#this is used to replace the command "hostapd_cli_s1g -i wlan0 -a /lib/functions/clientconnected.sh"
#this is enabled and started in /etc/rc.local, which does stuff after boot done
#the commands there are: "service hostap-leds enable" and "service hostap-leds start"

logger -t $SERVICE -p daemon.crit "hostap-leds launched..."

:
:

start_cli() {
logger -t $SERVICE -p daemon.crit "hostap-leds start_cli()..."
                #Only do if radio is 'ap'. Since this is run before wizard after image loaded,
                #when device set as ap after wizard, reboot to run this as an ap unless we
                #know where to put something after wizard to make this service restart
                result=$(uci show wireless.default_radio1.mode | grep "'ap'")
                if [ ! -z "$result" ]; then
                                isap=1
                fi
                if [ $isap -eq 1 ]; then
                                :
                                Do this routine as provided
                                :
                else
                                logger -t $SERVICE -p daemon.crit "hostap-leds start_cli()...THIS IS NOT AN AP!!!"
                fi
}

start_service() {
logger -t $SERVICE -p daemon.crit "hostap-leds start_service()..."

                #Only do if radio is 'ap'. Since this is run before wizard after image loaded,
                #when device set as ap after wizard, reboot to run this as an ap unless we
                #know where to put something after wizard to make this service restart
                result=$(uci show wireless.default_radio1.mode | grep "'ap'")
                if [ ! -z "$result" ]; then
                                isap=1
                fi
                if [ $isap -eq 1 ]; then
                                config_load wireless
                                config_foreach start_cli wifi-device
                else
                                logger -t $SERVICE -p daemon.crit "hostap-leds start_service()...THIS IS NOT AN AP!!!"
                fi
}

service_triggers() {
logger -t $SERVICE -p daemon.crit "hostap-leds service_triggers()..."

                #Only do if radio is 'ap'. Since this is run before wizard after image loaded,
                #when device set as ap after wizard, reboot to run this as an ap unless we
                #know where to put something after wizard to make this service restart
                result=$(uci show wireless.default_radio1.mode | grep "'ap'")
                if [ ! -z "$result" ]; then
                                isap=1
                fi
                if [ $isap -eq 1 ]; then
                        procd_add_reload_trigger wireless
               procd_add_reload_interface_trigger $networks
                else
                                logger -t $SERVICE -p daemon.crit "hostap-leds service_triggers()...THIS IS NOT AN AP!!!"
fi
}

Arien, I actually put ā€œservice hostapd-leds restartā€ in /etc/hotplug.d/ieee80211/10-wifi-detect" and it seemed to work. That file is:

#!/bin/sh

echo "10-wifi-detect action: ${ACTION} - device:'$DEVICENAME' interface:'$INTERFACE'" > /dev/console

logger -t HOTPLUG -s "****** 10-wifi-detect '$ACTION' - device:'$DEVICENAME' interface:'$INTERFACE' ******"

[ "${ACTION}" = "add" ] && {
	#default from original file
	/sbin/wifi config

	basename "$(readlink -f "/sys/${DEVPATH}/device/driver")" | grep '^morse_' || return;

	phy=${DEVPATH##*/}
	[ -n $phy ] || exit 0

	echo "${phy}assoc" > /sys/class/leds/blue:sys/trigger
	echo "${phy}assoc" > /sys/class/leds/red:sys/trigger

	logger -t HOTPLUG -s "${phy}assoc"

	echo "New assoc: ${phy}assoc" > /dev/console

	service hostap-leds restart
}

[ "${ACTION}" = "remove" ] && {
	#echo "clear blue, red led trigger to none" > /dev/console
	echo "none" > /sys/class/leds/blue:sys/trigger
	echo "none" > /sys/class/leds/red:sys/trigger
}

It appears youā€™ve found a fairly valid solution of giving the service a kick when the interface comes up.

Iā€™ll take a look at spinning it all up myself and see if I have any alternative suggestions. The procd service itself should work when the device is in bridge mode.

What may be going wrong is the configuration set before entering bridge mode is such that the service hasnā€™t correctly registered the service triggers.
Was it also failing before you added the if statements to the service_triggers() function? I would encourage leaving the service_triggers present in all modes, and just handle the ap check early in the start_service(), that way procd can be left responsible for kicking the service.