Advice, serial-term and U-Boot

At work I make heavy use of the GNU Emacs mode serial-term to interface with various target boards. The boards all run U-Boot with a 5 second autoboot timeout. I often want to power-cycle a group of boards, then stop them all at the U-Boot prompt before they autoboot. It’s a race to C-x b to all the different buffers in time. I used to frequently miss some buffers; the boards would start booting, and I would have to wait to issue a software reset. It became annoying enough that I decided to create an autostop extension.

Luckily, serial-term is written in Emacs Lisp, so I can extend it to do exactly what I want. In this case I want a global “U-Boot autoboot-stop” mode. Here’s what I came up with:

First, a variable to to keep track of whether the mode is enabled or not.

;; Detect U-Boot autoboot prompt and stop it.
(defvar u-boot-stop-autoboot-mode nil
  "Non-nil if term-mode should prevent U-Boot from autobooting.
Use the function u-boot-toggle-stop-autoboot-mode to toggle.")

Then the advice itself. It jacks into the term-emulate-terminal process filter to detect the U-Boot autoboot prompt, then sends a newline in response.

(defadvice term-emulate-terminal
  (before u-boot-maybe-stop-autoboot activate disable)
  (with-current-buffer (process-buffer proc)
    (when (string-match "Hit any key to stop autoboot:" str)
      (message "U-Boot autoboot stopped in buffer %s"
               (buffer-name (current-buffer)))
      (term-send-raw-string "\n"))))

And finally a function to toggle the advice on and off.

(defun u-boot-toggle-stop-autoboot-mode ()
  "Toggle whether or not term-mode should interrupt U-Boot autoboot."
  (if u-boot-stop-autoboot-mode
         'term-emulate-terminal 'before 'u-boot-maybe-stop-autoboot)
        (ad-update 'term-emulate-terminal)
        (setq u-boot-stop-autoboot-mode nil)
        (message "U-Boot autoboot will not be interrupted"))
       'term-emulate-terminal 'before 'u-boot-maybe-stop-autoboot)
      (ad-activate 'term-emulate-terminal)
      (setq u-boot-stop-autoboot-mode t)
      (message "U-Boot autoboot will be interrupted"))))

Now I can enable the mode globally with

M-x u-boot-toggle-stop-autoboot-mode

and be sure that all the boards will be interrupted in time.

It’s worth noting that developing this feature was relatively straightforward. I never left my Emacs session; I developed the code right in ~/.emacs.d/init.el, tested it quickly in a terminal buffer, and confirmed its operation in my existing serial-term buffers. Most of the development time was spent reading the Info documentation for Advice, to figure out how to enable and disable a specific piece of advice.