9

For my application, I need to cut off the USB power at the earliest possible stage. Right now it starts early in init.d but I would like it, if possible, to start before the init.d stuff starts. I read somewhere that Linux normally calls /sbin/init after it boots the kernel. I looked at that file and I only calls this binary "/lib/systemd/systemd" Is this the case for Raspi as well? Can I put stuff right before or after that line?

I did:

journalctl -u disableusb.service
No journal files were found.

And yes, I did a sudo systemctl enable disableusb before. And it said it created a link to it.

pi@raspberrypi:~$ systemd-analyze
Startup finished in 3.312s (kernel) + 14.995s (userspace) = 18.308s

multi-user.target @14.786s
└─getty.target @14.767s
  └─[email protected] @14.733s
    └─systemd-user-sessions.service @13.720s +251ms
      └─network.target @13.142s
        └─dhcpcd.service @11.656s +1.249s
          └─basic.target @11.571s
            └─timers.target @11.552s
              └─systemd-tmpfiles-clean.timer @11.551s
                └─sysinit.target @11.522s
                  └─networking.service @7.911s +3.569s
                    └─local-fs.target @7.757s
                      └─boot.mount @7.503s +162ms
                        └─[email protected] @7.195s +248ms
                          └─dev-mmcblk0p6.device @7.166s
3
  • Which operating system are you using? Raspbian Stretch?
    – Aurora0001
    Commented Jan 19, 2018 at 14:07
  • raspbian-jessie
    – Radu
    Commented Jan 19, 2018 at 14:13
  • 2
    Most linux distros use systemd for the init implementation now. /etc/init.d is only around for backward compatibility (and possible cross compatibility with other *nixes). The best opportunity for fine tuning is, as Aurora0001 indicates, using systemd facilities.
    – goldilocks
    Commented Jan 19, 2018 at 14:29

4 Answers 4

19

If you're using systemd (which you are if you have Jessie or Stretch), you'll want to create a systemd unit that will run your script as early as possible. In your /lib/systemd/system (note: not /lib/systemd/systemd, unlike some other distributions — see the official Pi docs) directory, create a new file called disableusb.service, containing the following:

[Unit]
Description=Disable USB power
Before=basic.target
After=local-fs.target sysinit.target
DefaultDependencies=no

[Service]
Type=oneshot
ExecStart=/path/to/script

[Install]
WantedBy=basic.target

Then, run systemctl enable disableusb, and the script should run on reboot.


Systemd works by running 'units' in order of their dependencies. A chart showing which order units are booted is available on the systemd website, reproduced here for reference:

local-fs-pre.target
         |
         v
(various mounts and   (various swap   (various cryptsetup
 fsck services...)     devices...)        devices...)       (various low-level   (various low-level
         |                  |                  |             services: udevd,     API VFS mounts:
         v                  v                  v             tmpfiles, random     mqueue, configfs,
  local-fs.target      swap.target     cryptsetup.target    seed, sysctl, ...)      debugfs, ...)
         |                  |                  |                    |                    |
         \__________________|_________________ | ___________________|____________________/
                                              \|/
                                               v
                                        sysinit.target
                                               |
          ____________________________________/|\________________________________________
         /                  |                  |                    |                    \
         |                  |                  |                    |                    |
         v                  v                  |                    v                    v
     (various           (various               |                (various          rescue.service
    timers...)          paths...)              |               sockets...)               |
         |                  |                  |                    |                    v
         v                  v                  |                    v              rescue.target
   timers.target      paths.target             |             sockets.target
         |                  |                  |                    |
         v                  \_________________ | ___________________/
                                              \|/
                                               v
                                         basic.target
                                               |
          ____________________________________/|                                 emergency.service
         /                  |                  |                                         |
         |                  |                  |                                         v
         v                  v                  v                                 emergency.target
     display-        (various system    (various system
 manager.service         services           services)
         |             required for            |
         |            graphical UIs)           v
         |                  |           multi-user.target
         |                  |                  |
         \_________________ | _________________/
                           \|/
                            v
                  graphical.target

The earliest you could realistically run your service is after sysinit.target, when the low-level services are initialized, I suspect. The systemd configuration above will execute the script path declared by ExecStart= after sysinit.target is complete, which should be relatively early in the boot sequence.

13
  • 1
    Thank you very much. I will experiment with this, and if it works well, I will try to move it even earlier than that, until it doesn't work or it hangs on boot :)
    – Radu
    Commented Jan 19, 2018 at 14:42
  • Btw, my script is actually just a line of code, "echo 0 | tee /sys/devices/platform/soc/20980000.usb/buspower >/dev/null" Can I just put that in ExecStart, or does it have to be a path to an actual script?
    – Radu
    Commented Jan 19, 2018 at 14:53
  • 1
    Just replace your old disableusb.service with the new one – I've edited my post with the correct version now, so just copy that and put it in disableusb.service, then re-enable it.
    – Aurora0001
    Commented Jan 19, 2018 at 15:47
  • 1
    Worked really well, ty. Now that I know the magic behind it, I will try to experiment more, I will try to put it before the network target, since I'd like to shut down the networking at all (at least the wifi part of it).
    – Radu
    Commented Jan 19, 2018 at 15:54
  • 2
    That's a cracking looking chart that. Commented Jan 19, 2018 at 17:52
3

The earliest possible place is in the initrd (init ramdisk). For this you should look into creating an initramfs-tools hook in /etc/initramfs-tools/hooks or similar. You probably best look for some examples how other programs do it.

The initrd-code will be executed right after the kernel is loaded and sets up the most important things like loading different kernel modules needed for booting, mounting cryptoroot, etc. Something like you need to do would be possible there and prevent any programs from accessing usb things before your script ran.

4
  • Thanks. I took a look in /etc/initramfs-tools/ and it's pretty much empty. No hooks, no scripts no nothing, except for a few mostly empty files. Also, I think I need the file system to be there, because there will be a file that decides if I want the usb to start or not.
    – Radu
    Commented Jan 19, 2018 at 16:28
  • Have a look at the files in /usr/share/initramfs-tools, there are the file supplied by the system. For an example how to extend it you can install dropbear-initramfs (ssh server in the initrd to remotely unlock a cryptoroot).
    – allo
    Commented Jan 21, 2018 at 9:40
  • How to set and read the flag if you want usb to be enabled is up to you. Probably you could hook into some "rootfs mounted" step in the initrd, but I guess for the details you need to experiment yourself.
    – allo
    Commented Jan 21, 2018 at 9:41
  • Thanks, that looks interesting, I guess I could use an external MCU to pull some GPIO so I know when to disable the usb and when not to.
    – Radu
    Commented Jan 21, 2018 at 12:17
0

Possibly this answer is outside "earliest in the boot process". But if you really need USB disabled at the earliest possible time, you will need to modify the USB drivers in the kernel so that they initialize the USB hardware as "off". Ideally you could control this with a writeable module parameter. Then you could set the desired value as a kernel command line option and change it later by writing to /sys/module/parameters/. Sorry I don't have a patch. ps: Awesome systemd dependency chart from Aurora.

0

Maybe passing usbcore.nousb option to the kernel command line (at the end of /boot/cmdline.txt) would be a better way, not initializing it at all instead of deactivating it early?

Though deactivating USB would obviously be a viable option only for headless machines (more exactly keyboardless and mouseless), and communicating only with methods not dependant on USB:

  • serial console on TXD and RXD pins
  • autonomous robot driving things with I²C / SPI / GPIO
  • ethernet on Pi4 (IIRC Pi3 and earlier uses an on-board USB-ethernet chip)
  • don't know about integrated WiFi or Bluetooth, if/on which versions they depend on USB or not. If not then it could be fully used as desktop with a bluetooth KB+mouse.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.