How to boot the Samsung Galaxy S7 modem with plain Linux kernel interfaces only

Part of a series: #squeak-phone


I’ve been running PostmarketOS on my Samsung Galaxy S7 recently. As far as I can tell, none of the available modem/telephony stacks supports the Galaxy S7 modem.

I was expecting to be able to just open /dev/ttyACM0 or similar and speak AT commands to it, like I used to be able to on Openmoko.

However, Samsung phones have a proprietary, undocumented1, unstandardized binary interface to their modems. Operating a modem is the same across members of the Samsung family, but each different handset seems to have a different procedure for booting the thing.

So I decided to reverse engineer (a fancy name for “running strace on cbd and rild and reading a lot of kernel source code”) the protocol.

The result is a couple of quick-and-dirty python scripts which, together, boot the modem and print out the messages it sends us. It’d be straightforward to extend it to, for example, send SMS, or to manage incoming and outgoing calls.

How Android (LineageOS) does it

LineageOS uses Samsung’s proprietary cbd and rild programs, extracted as binary blobs from the stock firmware.

The rild program implements the Android Radio Interface Layer (RIL), talking directly to the modem over /dev/umts_ipc0.

The cbd program performs the modem boot and reset sequences, and is started after rild is running by Android’s init. It talks to the modem over /dev/umts_boot0.

The cbd program relies on the availability of the Android RADIO partition, which contains the modem firmware, as well as on the existence of a file nv_data.bin stored on the phone’s EFS partition.

My S7, running LineageOS, started its cbd with the following command-line:

/sbin/cbd -d -tss310 -bm -mm -P platform/155a0000.ufs/by-name/RADIO

Here’s the “help” output of cbd, that we can use to decode this:

CP Boot Daemon
Usage: ./cbd [OPTION]
 -h Usage
 -d Daemon mode
 -t modem Type [cmc22x, xmm626x, etc.]
 -b Boot link [d, h, s, u, p, c, m, l]
 	 d(DPRAM) h(HSIC) s(SPI) u(UART) p(PLD) c(C2C) m(shared Memory) l(LLI)
 -m Main link [d, h, s, p, c, m, l]
 	 d(DPRAM) h(HSIC) s(SPI) p(PLD) c(C2C) m(shared Memory) l(LLI)
 -o Options [u, t, r]
 	 u(Upload test) t(Tegra EHCI) r(run with root)
 -p Partition# of CP binary

The command line, then, tells us a few interesting things:

  • the modem is an “SS310”.
  • it uses shared memory (corresponding to drivers/misc/modem_v1/link_device_shmem.c in the kernel source code) to communicate
  • the modem firmware partition name is RADIO
  • there’s an undocumented flag, -P (cf. the documented -p) which lets you supply a partition path fragment rather than a partition number

That last is crucial for running the same binary on PostmarketOS, which has a different layout of the files in /dev.

How libsamsung-ipc does it

The open-source libsamsung-ipc from the Replicant project handles the boot and communication processes for a number of (older?) Samsung handsets, but not the Galaxy S7. Once a modem is booted, libsamsung-ipc passes higher-level protocol messages back and forth between the modem and libsamsung-ril, which layers Android telephony support atop the device-independent abstraction that libsamsung-ipc provides.

Generally, modems are booted by a combination of ioctl calls and uploads of firmware blobs, with specific start addresses and blob layouts varying per modem type.

At first, I was concerned that I wouldn’t have enough information to figure out the necessary constants for the S7 modem. However, luckily just strace combined with dmesg output and kernel source code was enough to get it working.

Booting the modem - qnd_cbd.py

The overall sequence is similar to, but not quite the same as, other Samsung models already supported by libsamsung-ipc.

Running strace on Samsung’s cbd yields the following steps.

Extract the firmware blobs. The firmware partition has a table-of-contents that has a known structure. From here, we can read out the chunks of data we will need to upload to the modem.2

Acquire a wake-lock. I don’t understand the Android wake-lock system, but during modem boot, cbd acquires the ss310 wakelock.

Open /dev/umts_boot0. This character device is the focus of most of the subsequent activity. I’ll call the resulting file descriptor boot0_fd below.

Issue a modem reset ioctl. Send IOCTL_MODEM_RESET (0x6f21) to boot0_fd.

Issue a “security request”. Whatever that is! Send IOCTL_SECURITY_REQ (0x6f53) with mode=2, size_boot=0 and size_main=0 (like this). According to cbd’s diagnostics3, this is asking for “insecure” mode; the same ioctl will be used later to enter a “secure” mode.

One interesting thing about this particular call to IOCTL_SECURITY_REQ is that it answers error status 11 if you run it as root. The Samsung cbd does a prctl(PR_SET_KEEPCAPS, 1)/setuid(1001) just before IOCTL_SECURITY_REQ, which appears to give a happier result of 0 from the ioctl. However, if you ask cbd to stay as root by supplying the command-line flag -or to it, then it too gets error status 11 from the ioctl. Fortunately, the error code seems to be ignorable and the modem seems to boot successfully despite it! This makes me think that perhaps running IOCTL_SECURITY_REQ at this point, in this way, is optional. (UPDATE. Here’s what I think is going on. Setting mode=2 apparently asks for “insecure” mode, which allows uploading of firmware chunks. If we omit the mode=2 call to IOCTL_SECURITY_REQ, the phone reboots if it has previously successfully booted the modem. Later, mode=0 requests “secure” mode, which is what causes the phone to reboot unless mode=2 is selected. So ultimately IOCTL_SECURITY_REQ with mode=2 is mandatory, because otherwise you’ll end up crashing hard each time you restart the modem daemon.)

Upload three binary blob chunks. In order, send the BOOT and MAIN blobs from the firmware table-of-contents, followed by the contents of the nv_data.bin file on the EFS partition.

Sending a blob is a slightly involved process. Repeat the following steps until you run out of blob to upload:

  1. Read the next chunk into memory. The stock cbd confines itself to chunks of 62k (yes, 62k, not 64k) or smaller. The kernel doesn’t appear to care, but why mess with success?

  2. Prepare a struct modem_firmware descriptor, pointing to the chunk.

    1. The binary field should be the address in RAM of the start of the chunk you just read.

    2. The size field is the total size of the blob being uploaded.

    3. The m_offset field is an offset into the chunk of RAM reserved on the kernel side for uploaded blobs. The BOOT blob goes in at offset 0, so its load_addr field from the firmware partition’s table-of-contents, which for me was 0x40000000, corresponds to m_offset 0.

      As another example, for the first 62k chunk of the MAIN blob, which has load_addr 0x40010000, m_offset should be 0x10000, since MAIN’s load_addr is 0x10000 greater than BOOT’s load_addr. For the second 62k chunk, m_offset should be 0x1f800, and so on.

    4. The b_offset field isn’t currently used by the kernel, but I suspect that cbd fills it in anyway, so I do too: I set it to the offset within the firmware partition of the beginning of the chunk being uploaded.

    5. The mode field is interpreted by the kernel simply on a zero/nonzero basis, despite some hints elsewhere that valid values are 0, 1 and 2. Set mode=0 for all the uploaded chunks, since this is what cbd does.

    6. Finally, the len field is the length of the chunk to be uploaded.

  3. Issue an ioctl IOCTL_MODEM_XMIT_BOOT to boot0_fd, with argument a pointer to the modem_firmware descriptor you just filled in.

Note well that the chunks are to be read out of the firmware partition, following the appropriate TOC entries, for the BOOT and MAIN blobs, but chunks are to be read from nv_data.bin on the EFS partition, and not anywhere in the RADIO partition, for the NV blob.

Issue a second “security request”. This time, send IOCTL_SECURITY_REQ (0x6f53) with mode=0, size_boot set to the size of the BOOT blob from the TOC, and size_main set to the size of the MAIN blob. For me, those values were 9572 and 40027244, respectively. According to cbd’s diagnostics, this is asking for “secure” mode.

Tell the modem to power on. This interacts with power management code on the kernel side somehow. Issue ioctl IOCTL_MODEM_ON (0x6f19) to boot0_fd.

Tell the modem to start its boot sequence. Issue IOCTL_MODEM_BOOT_ON (0x6f22) to boot0_fd.

Tell the kernel to forward the firmware blobs to the modem. Issue IOCTL_MODEM_DL_START (0x6f28) to boot0_fd.

At this point, cbd engages in a little dance with the newly-booted modem, apparently to verify that it is running as expected. I don’t know the sources of these magic numbers, I just faithfully reproduce them:

  1. Write 0D 90 00 00 to boot0_fd.
  2. Read back four bytes. Expect them to be 0D A0 00 00.
  3. Write 00 9F 00 00 to boot0_fd.
  4. Read back four bytes. Expect them to be 00 AF 00 00.

Tell the modem the boot sequence is complete. Issue IOCTL_MODEM_BOOT_OFF (0x6f23) to boot0_fd.

Close boot0_fd, and release the wake-lock. At this point the modem is booted! Congratulations!

Monitoring the modem - qnd_cbd.py

After finishing the boot procedure, cbd goes into a loop apparently waiting for administrative messages from the modem. It does this by opening /dev/umts_boot0 again and reading from it. My script does the same. I don’t know what cbd does with the results: I’ve yet to see any information come out of the modem this way.

Interacting with the modem - qnd_ril.py

To interact with the modem, open /dev/umts_ipc0 and /dev/umts_rfs0. IPC stands for the usual Inter-Process Communication, but RFS apparently (?) stands for Remote File System.

Most modem interaction happens over the IPC channel. I don’t really know what the RFS channel is for yet.

Each packet sent to or from /dev/umts_ipc0 is formatted as a struct sipc_fmt_hdr that includes its own length, making parsing easy. Simply read and write a series of sipc_fmt_hdrs (with appropriate body bytes tacked on after each) from and to /dev/umts_ipc0.

Decoding them is another matter entirely! The libsamsung-ril library does this well. However, a little bit more information can be gleaned by matching bytes sent and received by rild to the diagnostic outputs it produces. Here’s a lightly-reformatted snippet of straced output of Samsung’s rild:

[pid  3969] 13:25:51.716591
  read(18</dev/umts_ipc0>,
       "\x0b\x00\xd3\x00\x02\x02\x03\x00\x01\x00\x01",
       264192) = 11 <0.000448>

[pid  3969] 13:25:51.718454
  writev(3<socket:[2555]>,
         [
           {"\x01\x81\x0f?jW_\x12\xc0\xc7*", 11},
           {"\x06", 1},
           {"RILD\x00", 5},
           {"[G] RX: (M)CALL_CMD (S)CALL_INCOMING (T)NOTI l:b m:d3 a:00 [ 00 01 00 01 ]\x00", 75}
         ],
         4) = 92 <0.000528>

Analysing the first seven bytes (the sipc_fmt_hdr) send to fd 18, we see len=11, msg_seq=0xd3, ack_seq=0, main_cmd=2, sub_cmd=2, cmd_type=3.

From the log message it prints, we can deduce that CALL_CMD=2, CALL_INCOMING=2, and NOTI=3. This lines up well with the definitions in libsamsung-ril, and it turns out that by observing the modem in operation you can learn a few more definitions not included in libsamsung-ril.

Next steps

Perhaps a good next step would be to translate this knowledge into support for the Galaxy S7 in libsamsung-ipc; for now, I don’t need that for myself, but it’s probably the Right Thing To Do. I’ll see if they’re interested in taking such a contribution.

  1. A handful of open-source projects, notably libsamsung-ipc and libsamsung-ril, plus the relevant kernel source code, are the only real documentation available! 

  2. Here’s the table of contents from my modem’s firmware partition:

    00000000: 544f 4300 0000 0000 0000 0000 0000 0000  TOC.............
              ----------------------------- ---------
                 name                         offset
    00000010: 0000 0000 0002 0000 0000 0000 0100 0000  ................
              --------- --------- --------- ---------
               loadadr   size      crc        count/entryid
    
    00000020: 424f 4f54 0000 0000 0000 0000 0002 0000  BOOT............
    00000030: 0000 0040 6425 0000 0888 f1a2 0000 0000  ...@d%..........
    
    00000040: 4d41 494e 0000 0000 0000 0000 8027 0000  MAIN.........'..
    00000050: 0000 0140 6cc4 6202 51b1 c353 0200 0000  ...@l.b.Q..S....
    
    00000060: 4e56 0000 0000 0000 0000 0000 0000 0000  NV..............
    00000070: 0000 ee47 0000 1000 0000 0000 0300 0000  ...G............
    
    00000080: 4f46 4653 4554 0000 0000 0000 00aa 0700  OFFSET..........
    00000090: 0000 0000 0056 0800 0000 0000 0400 0000  .....V..........
    

  3. Actually the information about “insecure” and “secure” IOCTL_SECURITY_REQ comes from a dmesg trace uploaded by someone anonymous to a pastebin I cannot find again. My own cbd doesn’t seem to produce these diagnostics. Sorry. 

Squeak-on-a-cellphone update: touchscreen working!

Part of a series: #squeak-phone


Here’s a quick demo video of the status of my quixotic project to get Squeak running as a kind of standalone userland on a modern cellphone:

(The sound is bad! I recorded it on my other cellphone…)

Currently, I develop by coding in Squeak on my Linux desktop, using my graphics tablet as a kind of proxy for a touchscreen. I use the FFI and AIO/OSProcess support in Squeak to read events from /dev/input/event.... For event sources that present absolute axes, I create instances of HandMorph in the World and animate them according to the incoming events.

Every now and then, to test on the real hardware, I use rsync to copy the changes and image files up to the cellphone, and then log in to the phone over ssh to restart the Cog VM.

At the moment, the image has enough smarts to figure out how to read the touchscreen and offer basic touchscreen click support. This lets me do simple things like open, move and close windows, and lets me save and/or quit the image.

Next steps are to make it harder to misclick – perhaps by increasing the size of some of the touch targets – and to think about coding up a simple onscreen keyboard.

Alternatively, on a parallel path, I’ve been reverse-engineering (really nothing more sophisticated than strace of cbd and rild) the Samsung protocols for booting and operating the cellular modem. The code is short and simple. Perhaps instead of an onscreen keyboard I’ll code up a quick dialer Morph and get Squeak making phone calls.

Squeak-on-a-cellphone update: better fonts!

Part of a series: #squeak-phone


I’ve made some minor image changes to adjust cached glyphs in TrueType fonts in Squeak when the DPI changes. Here are the results:

Squeak, under the illusion that the screen is 96 DPI Squeak, under the illusion that the screen is 96 DPI

Squeak, correctly configured for 535 DPI (!!) Squeak, correctly configured for 535 DPI (!!)

On the left, a stock, fresh-from-squeak.org unconfigured image, that wrongly believes the screen to be 96 DPI.

On the right, my dev image as I left it on my desktop PC, simply scped up to the phone and run, set to the correct 535 DPI resolution for the phone. Much better!

Squeak Smalltalk on a PostmarketOS cellphone

Part of a series: #squeak-phone


Back in 2007, when Openmoko was first a thing, I wrote an Erlang-based userland that got to the point of being able to take and make calls and receive and send SMS. The project stalled: the Openmoko GTA01 was too slow, its power-management too primitive, and Erlang’s GUI facilities too rudimentary to make further work worthwhile.

Modern cellphone hardware is much more capable. Is it time to have another run at the idea of a mobile personal computer?

Erlang OpenMoko userland (2008) Erlang OpenMoko userland (2008)

PostmarketOS on my cellphone PostmarketOS on my cellphone

PostmarketOS Weston demo PostmarketOS Weston demo

PostmarketOS is awesome

Last week, I installed PostmarketOS on my previous cellphone, a Samsung Galaxy S7 (using PostmarketOS’s samsung-herolte configuration).

PostmarketOS turns out to be a beautifully engineered system that’s easy to understand and modify. The basics of kernel and Alpine Linux userland installed cleanly and easily on the phone, and it’s running well as a development platform. I’m looking forward to getting into PostmarketOS more deeply.

htop running on my cellphone htop running on my cellphone. Six cores!

Running htop on the phone shows what an amazing little machine it is! So much power. Loads of cores, lots of RAM. Plenty of space to explore alternative visions of mobile personal computing.

However, the built-in demos, such as the Weston demo (shown above at right), currently leave quite a bit to be desired. Perhaps some of the other user interface options included with PostmarketOS could get me closer to a day-to-day usable cellphone - but I’m interested in running my own software! Let’s get hacking.

Running my own programs

PostmarketOS is a plain, clean Alpine Linux distribution. You can SSH into it initially via USB networking. From there, you can configure wifi using nmcli, set up SSH keys, and then access it directly using SSH over wifi.

lflow: Framebuffer demo lflow: Framebuffer demo

Building software is just as simple:

apk add alpine-sdk

To experiment with drawing to the framebuffer and reading touchscreen input via /dev/input, I compiled and ran an old quick and dirty framebuffer hack I wrote years ago. The results (shown at left) were encouraging: the program effortlessly animates tens of thousands of points at 30 frames per second, responding to touch inputs. Display is via brute-force pixel output to the mmap‘d frame buffer. It doesn’t even use a full core.

PostmarketOS turns a phone into a fully capable Linux machine, with total control over the attached hardware, and with everything accessible to the developer in the usual places using the usual tools.

But Unix tools are inappropriate for a mobile personal computing platform. We’ll need something else.

A Smalltalk phone

Squeak Smalltalk on PostmarketOS Squeak Smalltalk 6.0-alpha on PostmarketOS

Smalltalk could make an ideal basis for a mobile personal computing platform.

I’ve enjoyed using, developing with, and contributing to the Squeak Smalltalk implementation since the mid ’00s.

So I compiled the Cog Smalltalk VM on the phone itself, making use of the 64-bit ARM support code that landed extremely recently.

And lo and behold, it runs! Shown to the right is a bleeding-edge, fully up-to-date Squeak 6.0-alpha image running on the phone itself. (Click here or on the image to embiggen.)

From here, I can experiment with new ideas using the full power of a modern Smalltalk environment.

What next?

My previous Openmoko experiments foundered, in part, on the GUI aspect of the system; GTK+ via Erlang was fine for quick prototyping but wasn’t really up to the task for a day-to-day usable machine.

I recall getting Squeak running on my GTA01, in order to see if it could provide a viable UI. However, I remember being stymied by the mismatch between the expectations of the Smalltalk environment and the realities of the phone.

Squeak wants a mouse and keyboard. It assumes a monitor-sized display, in everything from widget and font sizes to window management. To work well on a phone, it needs a touchscreen-based, high-DPI UI in addition to its existing toolset.

Smalltalk, in both its language aspect and its system design aspect, also suffers from some weaknesses in areas where Erlang shines.

However, in the years since the GTA01:

So I think using Erlang/Syndicate-style Actors to structure a Smalltalk-based phone userland, perhaps with cgroups-based sub-virtual-machines and images, could work well.

My initial experiments have concentrated on

  • fixing the tiny fonts (the DPI-change support code in the image needs work, and the support in the VM seems to be absent (?)),

  • reading from the touchscreen (probably like this),

  • thinking about how to structure Actor supervision hierarchies and Dataspaces for a mobile phone (probably borrowing some design elements from my earlier Openmoko Erlang-based userland), and

  • thinking about how to layer a touchscreen (panel-based?) GUI atop Squeak’s Morphic UI.

I’ll write more on this blog under the tag #squeak-phone as things develop.

Time Division Multiplexing; or, How I am Learning to Stop Worrying and Love the Blog

I am doing some fascinating and rewarding contract work that makes direct use of some of the skills I developed and knowledge I acquired during my PhD studies. It’s bloody wonderful and I’m very lucky.

I’m even luckier that it’s not currently a full-time gig. This means I have, in principle, plenty of time to pursue my own ideas. Pandemic and family life permitting, of course.

While there’s a lot of joy in building things just for myself, it’s also a lot of fun to share the things I make with others. So I’ve decided I’ll aim to write more here about what I’m doing.

Cross-compiling Rust for Alpine Linux on Raspberry Pi

I’ve just figured out (with the assistance of pages like japaric’s rust-cross guide) how to cross-build a small Rust project for FRμITOS, which is an Alpine Linux based distribution (using musl libc) for Raspberry Pi, using a Debian x86_64 host.

It was ultimately very nicely straightforward.

  1. Install the Debian binutils-arm-linux-gnueabihf package.
  2. Use rustup to install support for the armv7-unknown-linux-musleabihf target.
  3. Set the CARGO_TARGET_ARMV7_UNKNOWN_LINUX_MUSLEABIHF_LINKER environment variable appropriately.
  4. Run cargo build --target=armv7-unknown-linux-musleabihf.

That’s it!

In shell script form:

sudo apt install binutils-arm-linux-gnueabihf
rustup target add armv7-unknown-linux-musleabihf
CARGO_TARGET_ARMV7_UNKNOWN_LINUX_MUSLEABIHF_LINKER=arm-linux-gnueabihf-ld \
  cargo build --target=armv7-unknown-linux-musleabihf

#lang something: An alternative syntax for Racket

Recent discussions (e.g. 1, 2) about potentially revising Racket syntax for Racket2 have reminded me I never properly announced #lang something, an experiment from back in 2016.

The main idea is S-expressions, but with usually-implicit parentheses and support for prefix/infix/postfix operators. Indentation for grouping is explicitly represented in the S-expression returned from the reader.

  • (+) keeps a semi-structured input format: reader yields ordinary syntax
  • (+) macros Just Work
  • (+) you can do “if … then … else …”: an example
  • (+) you can do “… where …”: an example
  • (-) uses indentation (though it doesn’t have to; see for example this module)
  • (-) the function syntax isn’t function(arg, ...)

(More links at the bottom of this post.)

In addition to the reader, #lang something provides a small selection of special forms that take advantage of the new syntax, and #lang something/shell adds Unix-shell-like behaviour and a few associated utilities.

This program:

#lang something
for { x: 1 .. 10 }
  def y: x + 1
  printf "x ~a y ~a\n" x y

… reads as this S-expression:

(module something-module something/base
  (#%rewrite-body
   (for (block (x (block (1 .. 10))))
        (block (def y (block (x + 1)))
               (printf "x ~a y ~a\n" x y)))))

The #%rewrite-body macro, together with its companion #%rewrite-infix, consults an operator table, extendable via the def-operator macro, to rewrite infix syntax into standard prefix S-expressions using a Pratt parser.

The block syntax has many different interpretations. It has a macro binding that turns it into a Racket match-lambda*, and it is used as literal syntax as input to other macro definitions.

For example, here’s one possible implementation of that for syntax:

#lang something

provide
  for

require
  for-syntax something/base
  prefix-in base_ racket/base

def-syntax for stx
  syntax-case stx (block)
    _ (block (v (block exp)) ...) (block body ...)
      (syntax (base_for ((v exp) ...) body ...))

def-operator .. 10 nonassoc in-range

Notice how the block S-expressions are rewritten into a normal S-expression compatible with the underlying for from racket/base.

Generally, all of these forms are equivalent

x y z          x y z:          x y z { a; b }
  a              a
  b              b

and they are read as

(x y z (block a b))

and are then made available to the normal macro-expansion process (which involves a new infix-rewriting semi-phase).

Colons are optional to indicate a following suite at the end of an indentation-sensitive line. Indentation-sensitivity is disabled inside parentheses. If inside a parenthesised expression, indentation-sensitivity can be reenabled with a colon at the end of a line:

a b (c d:
      e
      f)

= (a b (c d (block e f)))

a b (c d
      e
      f)

= (a b (c d e f))

Conversely, long lines may be split up and logically continued over subsequent physical lines with a trailing \:

a b c \
  d \
  e

= (a b c d e)

Semicolons may also appear in vertically-laid-out suites; these two are equivalent:

x y z
  a
  b; c
  d

x y z { a; b; c; d }

Suites may begin on the same line as their colon. Any indented subsequent lines become children of the portion after the colon, rather than the portion before.

This example:

x y z: a b
  c d
  e

reads as

(x y z (block (a b (block (c d) e))))

Square brackets are syntactic sugar for a #%seq macro:

[a; b; c; d e f]    →        (#%seq a b c (d e f))

[                   →        (#%seq a (b (block c)) (d e f))
  a
  b
    c
  d e f
]

Forms starting with block in expression context expand into match-lambda* like this:

{
  pat1a pat1b
    exp1a
    exp1b
  pat2a
    exp2
}

 (match-lambda*
    [(list pat1a pat1b) exp1a exp1b]
    [(list pat2a) exp2])

The map* function exported from something/base differs from map in racket/base in that it takes its arguments in the opposite order, permitting maps to be written

map* [1; 2; 3; 4]
  item:
    item + 1

map* [1; 2; 3; 4]
  item: item + 1

map* [1; 2; 3; 4]: item: item + 1

map* [1; 2; 3; 4] { item: item + 1 }

A nice consequence of all of the above is that curried functions have an interesting appearance:

def curried x:: y:: z:
  [x; y; z]

require rackunit
check-equal? (((curried 1) 2) 3) [1; 2; 3]

A few more links:

Another small example “shell script”, which I used while working on the module today:

#lang something/shell

def in-hashlang? re :: source-filename
  zero? (cat source-filename | grep -Eq (format "^#lang.*(~a).*" re))

(find "." -iname "*.rkt"
  | port->lines
  |> filter (in-hashlang? "something")
  |> ag -F "parameterize")

How I keep notes

A few years back, I decided to try to put a little bit of structure on how I kept records of such things as

  • ideas and thoughts I have, related to my work
  • procedures I performed in setting up machines and software
  • phone calls I’d had for arranging real-life things
  • important identifiers and numbers and so on
  • meeting notes
  • to-do lists

I’ve ended up with a loose collection of journal-like documents, each with a different feel.

  • A master org-mode document, which is always open in a buffer in my Emacs session.

    It contains

    • a plain-text time-stamped journal of thoughts, ideas, workings-through of proofs and formalisms, book and paper reviews, talk notes, meeting notes, phone call notes, etc.
    • detailed step-by-step records of how I’ve installed and configured various pieces of software for specific tasks; “how-tos”, essentially, for when I have to do the same kind of thing again
    • detailed step-by-step records of how I’ve set up various servers
    • to-do lists

    I use org-mode sections, with one top-level heading called Journal containing the bulk of the entries.

    To-do items each get a top-level heading of their own and an org-mode TODO tag. Completed to-do items are demoted to second-level and moved into a “done items” top-level heading.

    When I’m doing paid work, I use org-mode’s timesheet-management commands org-clock-in and org-clock-out under a Timesheet top-level heading; a couple of simple scripts help me prepare summaries for invoicing.

    Here’s a sample of just a few headings - each entry in the real document also has a bunch of text contained within it.

    * Journal
      :PROPERTIES:
      :VISIBILITY: children
      :END:
    ** (2011-05-08 14:31:13 tonyg) Vertical interpretation vs Horizontal interpretation :STUDY:
    ** (2011-05-19 00:00:00 tonyg) Inter-network routing: should be *tunnelling* not *chaining*
    ** (2011-05-19 11:49:40 tonyg) Memory Pool System - API for virtual machine interface? :STUDY:
    ** (2011-05-19 18:30:50 tonyg) Scripting languages integrate with system languages. :STUDY:
    ** (2011-05-23 00:00:00 tonyg) Origins of Credit-based Flow Control and Acks, functional this time
    ** (2011-05-24 10:13:33 tonyg) Message buffer size should be determined by arrival-time jitter  :IDEA:
    ** (2011-05-24 10:14:08 tonyg) Fine-grain scheduling in a distributed system using PLLs :IDEA:
    * TODO Build an imperative workalike os.rkt that uses real threads  :PROJECT:
    * TODO Upload ~/src/racket-kademlia
    * TODO racket-rc4: RSA, DSA, DH, ECDH, ECDSA, etc
    * TODO Thank-you notes for xmas gifts!
    * Old, done to-do items
    ** DONE Configure Flashbake and git-syncing for Uni
    ** DONE Write presentation
    ** DONE View mini-DVD and write up evaluation
    

    Some of the journal entries have gone on to become blog posts here.

    The document lives in a git repository, and a git commit is executed by cron every five minutes. I have checkouts of the repository on the two or three machines I use most frequently. I make extensive use of a variant of Emacs’ time-stamp feature:

    (defun stamp ()
      (interactive)
      (insert (time-stamp-string)))
    

    … which inserts text like 2019-05-19 13:15:43 tonyg when I run M-x stamp.

  • A second such document, very similar, that also includes slightly more in the way of private or personal information, that I don’t have checkouts of on as many machines.

  • A paper journal, which I use when I need the different style of thinking it affords. The freedom to draw sketches and rough lines connecting thoughts is useful from time to time. I use a nice fountain pen that a friend gave me, even though it smudges horribly because I’m left-handed.

    A journal entry

  • A Google doc that is a project-specific journal for one of my main projects, Syndicate. It’s a Google doc because I share it with various collaborators, and because I occasionally want to put pictures in the file. Otherwise it’s similar in feel to the main org-mode document I use: a record of thoughts, ideas and workings-through.

In every case, the documents function as append-only logs of thoughts and workings-through. I draw on them when digesting and summarising in later write-ups and in writing actual software. They’re mostly useful as an audit log, for finding out what I was thinking or what I did in the past, or for trying to reconstruct an argument.

The electronic documents are searchable, of course. It’s inconvenient that they are in different places, and I sometimes don’t know which of the small number of documents has the item I am looking for, but it’s not too unmanageable.

The paper documents are a different story. I intend eventually to photograph each page of my paper journal as a kind of backup, though I haven’t yet started doing so. Indexing that archive will require a bit of work too.

Actors for Smalltalk

About two years ago I wrote an Erlang-style Actors implementation for Squeak Smalltalk, based on subclassing Process, using Smalltalk’s Message class to represent inter-actor messages, and using Promise for RPC. Roughly a year ago, I finally dusted it off, documented it, and released it.

It draws on my experience of Erlang programming in a few ways: it has links and monitors for between-actor failure signalling; it has library actors representing sockets; it has a simple tracing facility. There’s crude and no doubt heavily problematic support for basic Morphic interaction.

Installation instructions, comprehensive documentation and tutorials can be found at https://tonyg.github.io/squeak-actors/.

It’s by no means as ambitious as other Smalltalk Actor systems: it only deals with single-image in-image messaging between actors, and doesn’t have the E-style ability to refer to objects within a vat. Instead it follows Erlang in having references denote actors (i.e. vats, roughly), rather than anything more fine-grained.

Next steps could be:

  • a Workspace that was actor aware, i.e. each Workspace an actor.
  • better Supervisors.
  • tools for visualizing the current constellation of actors, perhaps based on Ned Konz’s Connectors?
  • an ActorEventTrace subclass that is able to draw message interaction diagrams as a Morph.
  • a screencast of building an IRC client maybe?

To give it a try:

  1. Download and run a recent version of Squeak. For example, I just downloaded https://files.squeak.org/trunk/Squeak5.3alpha-18431-32bit/Squeak5.3alpha-18431-32bit-201810190412-Linux.zip.

  2. Update your image. Click the Squeak icon in the top left of the window, and choose “Update Squeak”, or execute the following in a workspace:

    MCMcmUpdater updateFromServer
    
  3. Install the Actors project into your Squeak:

    (Installer squeaksource project: 'Actors') install: 'ConfigurationOfActors'
    
  4. Follow the documentation, which includes tutorials of various levels of complexity and a detailed user manual,

It could in principle work in Pharo, as well. I did try to port it to Pharo, but found two main obstacles. First, Pharo doesn’t have an integrated Promise implementation; the Actors project makes heavy use of promises. Second, I couldn’t get Pharo’s sockets to behave as reliably as Squeak’s. I don’t remember details, but I’d be very pleased if a Pharo expert were to have a try at porting the code across.

The project has recently been discussed on HN; please feel free to get in touch if you have any questions, comments or feedback.

Why learn Smalltalk?

Smalltalk is three things in one. It is a language; it embodies a language design idea; and it is an operating system.

Learning Smalltalk gives you three things:

  1. An understanding of Smalltalk, the language. This is least essential. It’s also the kind of thing you can pick up in a single 30-minute session.1 The language is tiny and simple.

  2. An understanding of the design idea, uniformly object-oriented programming. This is crucial and will connect with other styles of programming including Actor-based and pure-functional. This is something you will never get from Java, which is not a uniformly object-oriented language.

  3. An understanding of a completely different and (these days) unusual way of designing an operating system. An object-oriented operating system, to boot. The IDE, the debugger, and the other tooling all integrates to give a level of manageability in many ways far superior to e.g. Unix or Windows.

After a while, you may find yourself discovering subtle things about the interplay between the three aspects of Smalltalk that you can then apply in your own designs. For example, Smalltalk is a “dynamic” language, but the way classes and methods are arranged is very rigid, giving a lot of static structure to the system organisation. This static structure is what unlocks the powerful tooling, and is what simplifies the programmer’s mental model to make the system understandable. Comparable OO and almost-OO languages, such as Python, have a more “dynamic” system organisation, making it harder to write tools for automatically manipulating Python systems and making it harder for programmers to understand how Python code, data, state, and processes come together to form a running program image.