Skip to content

Commit 875e5f7

Browse files
committed
hm2_rpspi/hm2_spix: detect kernel SPI driver, fail at load
Replace the eshellf-driven modprobe/rmmod dance with a passive check: on load, look for the conflicting kernel SPI master driver (spi_bcm2835 on RPi3/4, dw_spi_mmio on RPi5) and refuse to start if present, with a message pointing at raspi-config or the modprobe.d blacklist recipe in the man page. Avoids needing root to call /sbin/rmmod and removes the last user of rtapi_spawn_as_root from these drivers. The shared kernel_module_loaded() helper queries /sys/module/<name>, which covers both loadable and built-in modules; /proc/modules alone would miss kernels with SPI compiled in. Lives in a new kmod_check.c linked into hm2_rpspi and hm2_spix, replacing eshellf.c which has no remaining callers (hm2_eth no longer needs it either after the direct iptables posix_spawn rework). Update hm2_rpspi(9) and hm2_spix(9) NOTES to describe the per-platform disable recipe instead of claiming auto-unload.
1 parent 937427c commit 875e5f7

9 files changed

Lines changed: 113 additions & 117 deletions

File tree

docs/src/man/man9/hm2_rpspi.9.adoc

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -70,10 +70,14 @@ Mesa's SPI based Anything I/O boards (with the HostMot2 firmware) to the
7070
LinuxCNC HAL. This driver is not based on the linux spidev driver, but
7171
on a dedicated BCM2835-SPI driver.
7272

73-
It is *strongly* recommended that you unload/disable the kernel's spidev
74-
driver by disabling it using *raspi-config*. Please note that having
75-
both kernel and user-space SPI drivers installed can result in
76-
unexpected interactions and system instabilities.
73+
The kernel's *spi_bcm2835* driver conflicts with this user-space driver
74+
and must be disabled before *hm2_rpspi* will load. Use *raspi-config*
75+
(Interface Options -> SPI -> Disable) and reboot. If the kernel module
76+
is still present at load time the driver will refuse to start with
77+
"Kernel SPI driver spi_bcm2835 is loaded and conflicts" rather than
78+
fight the kernel for the bus. Having both kernel and user-space SPI
79+
drivers installed otherwise leads to unexpected interactions and
80+
system instabilities.
7781

7882
The supported boards are: 7I90HD.
7983

docs/src/man/man9/hm2_spix.9.adoc

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -208,13 +208,27 @@ setting would be to set one step below the maximum speeds.
208208

209209
== NOTES
210210

211-
If you know your setup and do not require the spix_spidev driver, then
212-
it is *strongly* recommended that you unload/disable the kernel's SPI
213-
drivers *dw_spi* and *dw_spi_mmio* for the RPi5 or *spi_bmc2835* for the
214-
RPi3 and RPi4. The hm2_spix hardware drivers attempt to unload the
215-
kernel driver at startup if detected and restore it at exit if initially
216-
loaded. However, there are no guarantees about the effectiveness of the
217-
module unload/load actions.
211+
If you do not require the spix_spidev driver you must disable the
212+
kernel's SPI driver before *hm2_spix* will load. The conflicting module
213+
is *spi_bcm2835* on the RPi3 / RPi4 and *dw_spi_mmio* (with its
214+
dependency *dw_spi*) on the RPi5. The driver detects the kernel module
215+
at startup and refuses to load with "Kernel SPI driver ... is loaded
216+
and conflicts" rather than fight the kernel for the bus.
217+
218+
To disable on RPi3 / RPi4, run *raspi-config* and pick
219+
Interface Options -> SPI -> Disable, then reboot.
220+
221+
To disable on RPi5, blacklist both kernel modules. Create
222+
*/etc/modprobe.d/blacklist-linuxcnc.conf* containing:
223+
224+
----
225+
blacklist dw_spi_mmio
226+
blacklist dw_spi
227+
----
228+
229+
Then reboot. If either module is built-in to the kernel rather than
230+
loadable, use a kernel command-line override
231+
(*modprobe.blacklist=dw_spi_mmio,dw_spi* in `/boot/firmware/cmdline.txt`).
218232

219233
*Warning*: having both kernel and user-space SPI drivers installed can
220234
result in unexpected interactions and system instabilities.

src/Makefile

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1047,7 +1047,6 @@ hm2_pci-objs := \
10471047
$(MATHSTUB)
10481048
hm2_eth-objs := \
10491049
hal/drivers/mesa-hostmot2/hm2_eth.o \
1050-
hal/drivers/mesa-hostmot2/eshellf.o \
10511050
$(MATHSTUB)
10521051
hm2_spi-objs := \
10531052
hal/drivers/mesa-hostmot2/hm2_spi.o \
@@ -1056,15 +1055,15 @@ hm2_spi-objs := \
10561055
hm2_rpspi-objs := \
10571056
hal/drivers/mesa-hostmot2/hm2_rpspi.o \
10581057
hal/drivers/mesa-hostmot2/llio_info.o \
1059-
hal/drivers/mesa-hostmot2/eshellf.o \
1058+
hal/drivers/mesa-hostmot2/kmod_check.o \
10601059
$(MATHSTUB)
10611060
hm2_spix-objs := \
10621061
hal/drivers/mesa-hostmot2/hm2_spix.o \
10631062
hal/drivers/mesa-hostmot2/spix_rpi5.o \
10641063
hal/drivers/mesa-hostmot2/spix_rpi3.o \
10651064
hal/drivers/mesa-hostmot2/spix_spidev.o \
10661065
hal/drivers/mesa-hostmot2/llio_info.o \
1067-
hal/drivers/mesa-hostmot2/eshellf.o \
1066+
hal/drivers/mesa-hostmot2/kmod_check.o \
10681067
$(MATHSTUB)
10691068
hm2_modbus-objs := \
10701069
hal/drivers/mesa-hostmot2/hm2_modbus.o \

src/hal/drivers/mesa-hostmot2/eshellf.c

Lines changed: 0 additions & 63 deletions
This file was deleted.

src/hal/drivers/mesa-hostmot2/hm2_rpspi.c

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@
4141
#include "hostmot2-lowlevel.h"
4242
#include "hostmot2.h"
4343
#include "spi_common_rpspi.h"
44-
#include "eshellf.h"
4544
#include "llio_info.h"
45+
#include "kmod_check.h"
4646

4747
#define HM2_LLIO_NAME "hm2_rpspi"
4848

@@ -1272,15 +1272,20 @@ static void hm2_rpspi_cleanup(void)
12721272
peripheral_restore();
12731273
munmap(peripheralmem, peripheralsize);
12741274
}
1275-
eshellf(HM2_LLIO_NAME, "/sbin/modprobe spi-bcm2835");
12761275
}
12771276

12781277
/*************************************************/
12791278
int rtapi_app_main()
12801279
{
12811280
int ret;
12821281

1283-
eshellf(HM2_LLIO_NAME, "/sbin/rmmod spi_bcm2835");
1282+
if(kernel_module_loaded("spi_bcm2835")) {
1283+
LL_ERR("Kernel SPI driver spi_bcm2835 is loaded and conflicts with "
1284+
HM2_LLIO_NAME ". Disable it with 'sudo raspi-config' "
1285+
"(Interface Options -> SPI -> Disable) and reboot. "
1286+
"See hm2_rpspi(9) for details.\n");
1287+
return -EBUSY;
1288+
}
12841289

12851290
if((comp_id = ret = hal_init("hm2_rpspi")) < 0)
12861291
goto fail;
Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
/*
2-
* This is a component for hostmot2 board drivers
3-
* Copyright (c) 2013,2014,2020,2024 Michael Geszkiewicz <micges@wp.pl>,
4-
* Jeff Epler <jepler@unpythonic.net>
5-
* B.Stultiens <lcnc@vagrearg.org>
2+
* Shared helper: check whether a kernel module is loaded.
3+
* Copyright (c) 2026 Luca Toniolo
64
*
75
* This program is free software; you can redistribute it and/or modify it
86
* under the terms of the GNU General Public License as published by the Free
@@ -17,12 +15,15 @@
1715
* You should have received a copy of the GNU General Public License along with
1816
* this program; if not, see <https://www.gnu.org/licenses/>.
1917
*/
20-
#ifndef HAL_HM2_ESHELLF_H
21-
#define HAL_HM2_ESHELLF_H
2218

23-
int shell(char *command);
24-
int eshellf(const char *errpfx, const char *fmt, ...);
19+
#include <stdio.h>
20+
#include <unistd.h>
2521

26-
#endif
27-
/* vim: ts=4
28-
*/
22+
#include "kmod_check.h"
23+
24+
int kernel_module_loaded(const char *name)
25+
{
26+
char path[256];
27+
snprintf(path, sizeof(path), "/sys/module/%s", name);
28+
return access(path, F_OK) == 0;
29+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Shared helper: check whether a kernel module is loaded.
3+
* Copyright (c) 2026 Luca Toniolo
4+
*
5+
* This program is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License as published by the Free
7+
* Software Foundation; either version 2 of the License, or (at your option)
8+
* any later version.
9+
*
10+
* This program is distributed in the hope that it will be useful, but WITHOUT
11+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13+
* more details.
14+
*
15+
* You should have received a copy of the GNU General Public License along with
16+
* this program; if not, see <https://www.gnu.org/licenses/>.
17+
*/
18+
#ifndef HAL_HM2_KMOD_CHECK_H
19+
#define HAL_HM2_KMOD_CHECK_H
20+
21+
/*
22+
* Check whether a kernel module is loaded (loadable or built-in).
23+
* Returns non-zero when present, zero when absent.
24+
*
25+
* Implemented via /sys/module/<name>, which the kernel populates for
26+
* both loadable modules (initstate "live") and built-in ones (no
27+
* initstate file). This catches the built-in case that /proc/modules
28+
* misses; on a custom kernel where the conflicting driver is compiled
29+
* in rather than modular, the check still fires.
30+
*
31+
* NOTE: pass the canonical underscore form of the module name. The
32+
* kernel exposes modules in /sys/module/ with underscores only, so
33+
* "spi-bcm2835" will not match "spi_bcm2835" on disk. modprobe/lsmod
34+
* canonicalize for you; this helper does not.
35+
*/
36+
int kernel_module_loaded(const char *name);
37+
38+
#endif
39+
/* vim: ts=4
40+
*/

src/hal/drivers/mesa-hostmot2/spix_rpi3.c

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,10 @@
3131

3232
#include "hostmot2-lowlevel.h"
3333

34-
#include "eshellf.h"
3534
#include "spix.h"
3635
#include "dtcboards.h"
3736
#include "spi_common_rpspi.h"
37+
#include "kmod_check.h"
3838

3939
//#define RPSPI_DEBUG_PIN 23 // Define for pin-debugging
4040

@@ -120,7 +120,6 @@ spix_driver_t spix_driver_rpi3 = {
120120
.close = rpi3_close,
121121
};
122122

123-
static int has_spi_module; // Set to non-zero when the kernel module spi_bcm2835 is loaded
124123
static int driver_enabled; // Set to non-zero when rpi3_setup() is successfully called
125124
static int port_probe_mask; // Which ports are requested
126125

@@ -724,7 +723,7 @@ static int rpi3_detect(const char *dtcs[])
724723

725724
/*
726725
* Setup the driver.
727-
* - remove kernel spidev driver modules if detected
726+
* - check for conflicting kernel SPI module
728727
* - map the I/O memory
729728
* - setup the GPIO pins and SPI peripheral(s)
730729
*/
@@ -742,12 +741,15 @@ static int rpi3_setup(int probemask)
742741

743742
port_probe_mask = probemask; // For peripheral_setup() and peripheral_restore()
744743

745-
// Now we know what platform we are running, remove kernel SPI module if
746-
// detected
747-
if((has_spi_module = (0 == shell("/usr/bin/grep -qw ^spi_bcm2835 /proc/modules")))) {
748-
if(shell("/sbin/rmmod spi_bcm2835"))
749-
LL_ERR("Unable to remove kernel SPI module spi_bcm2835. "
750-
"Your system may become unstable using LinuxCNC with the " HM2_LLIO_NAME " driver.\n");
744+
// The kernel SPI driver conflicts with direct peripheral access. Fail at
745+
// load if it is present so the user can disable it deliberately rather
746+
// than running a half-working system.
747+
if(kernel_module_loaded("spi_bcm2835")) {
748+
LL_ERR("Kernel SPI driver spi_bcm2835 is loaded and conflicts with "
749+
HM2_LLIO_NAME ". Disable it with 'sudo raspi-config' "
750+
"(Interface Options -> SPI -> Disable) and reboot. "
751+
"See hm2_spix(9) NOTES for details.\n");
752+
return -EBUSY;
751753
}
752754

753755
spiclk_base = read_spiclkbase();
@@ -805,10 +807,6 @@ static int rpi3_cleanup(void)
805807
munmap(peripheralmem, peripheralsize);
806808
}
807809

808-
// Restore kernel SPI module if it was detected before
809-
if(has_spi_module)
810-
shell("/sbin/modprobe spi_bcm2835");
811-
812810
driver_enabled = 0;
813811
return 0;
814812
}

src/hal/drivers/mesa-hostmot2/spix_rpi5.c

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,10 @@
2727

2828
#include "hostmot2-lowlevel.h"
2929

30-
#include "eshellf.h"
3130
#include "spix.h"
3231
#include "dtcboards.h"
3332
#include "rp1dev.h"
33+
#include "kmod_check.h"
3434

3535
//#define RPSPI_DEBUG_PIN 23 // Define for pin-debugging
3636

@@ -123,7 +123,6 @@ typedef struct __spisave_t {
123123

124124
static spisave_t spi0save; // Settings before our setup
125125
static spisave_t spi1save;
126-
static int has_spi_module; // Set to non-zero when the kernel modules dw_spi and dw_spi_mmio are loaded
127126
static int driver_enabled; // Set to non-zero when rpi5_setup() is successfully called
128127
static int port_probe_mask; // Which ports are requested
129128

@@ -526,7 +525,7 @@ static int rpi5_detect(const char *dtcs[])
526525

527526
/*
528527
* Setup the driver.
529-
* - remove kernel spidev driver modules if detected
528+
* - check for conflicting kernel SPI module
530529
* - map the I/O memory
531530
* - setup the GPIO pins and SPI peripheral(s)
532531
*/
@@ -541,12 +540,15 @@ static int rpi5_setup(int probemask)
541540

542541
port_probe_mask = probemask; // For peripheral_setup() and peripheral_restore()
543542

544-
// Now we know what platform we are running, remove kernel SPI module if
545-
// detected
546-
if((has_spi_module = (0 == shell("/usr/bin/grep -qw ^dw_spi_mmio /proc/modules")))) {
547-
if(shell("/sbin/rmmod dw_spi_mmio dw_spi"))
548-
LL_ERR("Unable to remove kernel SPI modules dw_spi_mmio and dw_spi. "
549-
"Your system may become unstable using LinuxCNC with the " HM2_LLIO_NAME " driver.\n");
543+
// The kernel SPI driver conflicts with direct peripheral access. Fail at
544+
// load if it is present so the user can disable it deliberately rather
545+
// than running a half-working system.
546+
if(kernel_module_loaded("dw_spi_mmio")) {
547+
LL_ERR("Kernel SPI driver dw_spi_mmio is loaded and conflicts with "
548+
HM2_LLIO_NAME ". Blacklist dw_spi_mmio and dw_spi via "
549+
"/etc/modprobe.d/blacklist-linuxcnc.conf and reboot. "
550+
"See hm2_spix(9) NOTES for the exact recipe.\n");
551+
return -EBUSY;
550552
}
551553

552554
// The IO address for the RPi5 is at a fixed address. No need to do fancy
@@ -590,10 +592,6 @@ static int rpi5_cleanup(void)
590592
munmap(peripheralmem, peripheralsize);
591593
}
592594

593-
// Restore kernel SPI module if it was detected before
594-
if(has_spi_module)
595-
shell("/sbin/modprobe dw_spi_mmio");
596-
597595
driver_enabled = 0;
598596
return 0;
599597
}

0 commit comments

Comments
 (0)