I THINK ∴ I'M DANGEROUS

Project: Tin

Decided I wanted a programmable pocket computer. Something that fits in a tin and runs off a battery. Something I can use to cause all sortsa mischief.

Use Cases

  • Work as an Onity Skeleton Key
  • GPS tracker (?)
  • Universal Remote Control (IR led can be drive directly via gpio)
  • rtl-sdr for use as radio receiver and transmitter (via this)
    • Being able to receive and transmit means possibly scanning for and then spoofing signals.
  • Speaker (plugs into headphone jack so it can be removed and headphones used)
  • rfID and NFC cracker (?)
  • communicate with car computers (via ELM327) to perform various tasks (clear trouble codes, possibly disable engine)
  • Voice Changer (via bluetooth mic or usb mic + audio out and sox)
  • Ethernet interception device (plug in between ethernet and device to sniff traffic)
  • Wifi AP scanner

Features

  • Battery powered
  • Works as a mobile bus pirate / bus pirate integration
  • USB and bluetooth peripheral support
  • rtl-sdr integration (?)
  • serial character display with multi-button interface
  • Compass

Hardware

Parts

Modules

Obviously, the R-pi is the main component. Along with it is the custom built add-on board that connects directly to the R-pi's main header. On the custom add-on board is:

New Plan:

MSP430 Galore!

An MSP430 will be used as to essentially implement ACPI. A lithium ion battery with support (charging) circuitry will power everything (at 3.3V). An MSP connected to the R-Pi via SPI will actuate either a mosfet or a solid-state relay to switch power on and off to the R-Pi. The R-Pi can tell the MSP to power off or a button connected to one of the MSP's GPIO pins can actuate it. The Pi can schedule a wake up time.

Assuming the MSP is not too busy, it will also act as an RTC, communication also via SPI. The Pi can send a command to write the time to the pi and send a command to read the time. Time is stored as a 32-bit unsigned int (uint32_t). The MSP's normal operation is to go into low power mode LPM3 or LPM4. The ACLK, wired to a 32khz crystal keeps track of the time. Every second the MSP wakes, increments it's time, then goes back to sleep.

The MSP also takes input from the various other buttons (probably gameboy style, d-pad and buttons) and sends them to the Pi via SPI. This frees up the Pi's pins so it can talk to peripherals directly.

At Boot, the MSP also displays a splash screen on the nokia display (so as to acknowledge boot/power on, since the pi takes several seconds to boot).

USB Components

The vast majority of the interesting peripheral will be connected via USB. Internal components will be connected via a USB hub.

The hub and the devices plugged into it will all be stripped of their housings and possibly coated in hot-glue or resin to prevent shorts. For testing purposes, electrical tape may be used.

For power management, manual control of USB suspends will be utilized: https://www.kernel.org/doc/Documentation/usb/power-management.txt

This will not be as efficient as actually cutting power to the device, but it's a good compromise between the complexity of trying to switch power on and off for each port and the flexibility of being able to do it in software.

Devices will have their modules unloaded (as this will definitively mark the device as idle per Linux USB PM) and the USB port will set to aggressively keep the device suspend. When a device is needed (i.e. an rtl-sdr for when gqrx is launched) the launch script loads up the driver and forces the port to resume from suspend.

RTL-SDR

An SDR is too cool, too useful, and too cheap not to just build into the case. There are small form factor rtl-sdr based SDRs that

Sound Card

I definitely want and need a usb sound card. This is to provide a mic-input as well as a second audio out. I'll either make the on board audio or the usb audio connect to a small speaker that can be driven via line-level audio (if this is too quiet, I may fashion a small class A or B audio amp circuit).

The sound card will allow the FEK to do audio munging via SOX (can act as voice changer) as well as take analog audio input and transmit it. I may do 2 usb sound cards. one for a permanently affixed speaker and mic and one for auxiliary inputs.

The real problems arise when I figure out how I want to handle audio routing. I despise pulseaudio, but it may be the most functional and best supported option. I would really prefer not to have to run it (and thus dbus). If only KLANG were more than vaporware.

Wifi

It'd sure be great if the upstream driver for rtl8192cu chipsets worked. As it is, the Pi Linux Kernel does not support the upstream driver for some unfathomable reason. The driver it does ship with is from realtek and only works for managed mode. Ad-hoc and master (via hostapd) do not work.

Software

Running arch linux with some modifications.

Kernel

Apparently, the r-pi cannot run a vanilla kernel. I learned this the hard way (after 10+ hours of compiling). The raspberry-pi branch of the linux kernel has some broken dmaengine.c / bcm2708-dmaengine.c. I'm no kernel hacker, but doing my best to resolve the issues.

Update: There were too many issues to resolve myself, but switching to a newer 3.18 branch seems to have resolve that issue. I hit yet another issue which appears to be due to unzip not decompressing the rpi zip file correctly. Use of p7zip seems to have alleviated this issue.

Update2: Well I had to wait for more progress to be made. I can now load 3.18.y and even load ssd1307fb, but apparently I have to manually add information into the Device Tree. Also I have to learn what the hell the “Device Tree” is.

Display

I'm using an OLED display ran using an SSD1306 driver chip. There is a linux driver to utilize this screen as a framebuffer device: http://lxr.free-electrons.com/source/drivers/video/fbdev/ssd1307fb.c

I'm using a nokia display with a PCD8544 controller.

pikeyd

Also running a custom fork of pikeyd to generate keyboard events based on gpio input. The 5 direction switch will be mapped to up, down, left, right. The center click will be left disconnected. the A and B buttons will be mapped to enter and backspace.

The fork is largely a clean up of the code, with little functional changes. Addtional changes may be made as necessary.

fastinit

The fastinit script displaces systemd. It boots much faster and uses less RAM. And is entirely more sensible for an embedded system than the monster that is systemd.

Because archlinux is designed to work with systemd-udevd (which uses netlink for communication), the kernels are compiled without the old style /proc/sys/kernel/hotplug or slightly newer /sys/kernel/uevent_helper. So I adapted some sample code from the kernel to make a wrapper for busybox's mdev so hotplug events reported via netlink can be passed to mdev.

This may be unnecessary since I'm also using devtmpfs.

Steps

  1. Install fastinit script to /sbin/fastinit
    • Be sure to make it executable: chmod +x /sbin/fastinit
  2. Install busybox
    • pacman -S busybox
  3. Symlink to busybox as mdev
    • ln -s /usr/bin/busybox /sbin/mdev
  4. Edit /boot/cmdline.txt, add this to the end:
    • init=/sbin/fastinit

You can switch back to a normal systemd init/boot by removing init=/sbin/fastinit from the cmdline.txt file.

fastinit
#!/bin/bash
 
export PATH=/bin:/sbin                                                                                                                                  
 
service ()
{
        while true; do
                $@
                sleep 1
        done &
}
 
#mount system pseudo filesystems
mount -t proc proc /proc
mount -t sysfs sys /sys
mount -t devtmpfs dev /dev
mkdir /dev/pts
mount -t devpts devpts /dev/pts
 
#launch the netlink to mdev bridge service
service /usr/sbin/nl2mdev
 
#load any modules specified in /etc/modprobe.d/*
if [ $(ls /etc/modprobe.d/ | wc --lines) -gt 0 ]; then
        for module in /etc/modprobe.d/*; do
                modprobe $(< $module )
        done
fi
 
#alternate module config info
if [ $(ls /etc/modules-load.d/*.conf | wc --lines) -gt 0 ]; then
        cat /etc/modules-load.d/*.conf | xargs -i{} modprobe {}
fi
 
#rescan for device changes
mdev -s
 
#mount anything specified in /etc/fstab
mount -a
 
#set hostname
hostname $(< /etc/hostname )
 
#initialize serial port
stty -F /dev/ttyAMA0 sane
 
 
#network setup and network dependent services
(
  ifconfig lo up 127.0.0.1
  ifconfig eth0 up
  ifconfig eth0:0 up 192.168.10.1
 
  #launch network-dependent services here
  service /usr/sbin/sshd -D
 
) &
 
 
#other services
service /sbin/agetty --noclear tty1 linux
service /sbin/agetty ttyAMA0 115200 vt100
service /usr/bin/crond -n
 
wait
 
#done booting!

nl2mdev

I could just recompile the kernel to support the older style hotplug manager, but I'm trying to keep changes down to a minimum to lessen the amount of trouble upgrading will be down the line. This little program (most of it from the example netlink code from the kernel documentation) let's me use an unmodified mdev from busybox and an unmodified archlinux kernel.

In reviewing the code, I realized in my laziness (trying to avoid having to free() memory), I might have introduced a race condition. Oops. Will work on fixing this…

nl2mdev.c
/* nl2mdev - netlink wrapper for busybox's mdev
 * gcc -std=c99 -pedantic -Wall -O2 nl2mdev.c -o nl2mdev */
 
#define _XOPEN_SOURCE 500
 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
 
#include <sys/poll.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
 
#include <linux/types.h>
#include <linux/netlink.h>
 
 
void die(char *s)
{
	write(2,s,strlen(s));
	exit(1);
}
 
int main(int argc, char *argv[])
{
	struct sockaddr_nl nls;
	struct pollfd pfd;
	char buf[512];
 
	signal(SIGCHLD, SIG_IGN);
 
	// Open hotplug event netlink socket
 
	memset(&nls,0,sizeof(struct sockaddr_nl));
	nls.nl_family = AF_NETLINK;
	nls.nl_pid = getpid();
	nls.nl_groups = -1;
 
	pfd.events = POLLIN;
	pfd.fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
	if (pfd.fd==-1)
		die("Not root\n");
 
	// Listen to netlink socket
 
	if (bind(pfd.fd, (void *)&nls, sizeof(struct sockaddr_nl)))
		die("Bind failed\n");
	while (-1!=poll(&pfd, 1, -1)) {
		int i, len = recv(pfd.fd, buf, sizeof(buf), MSG_DONTWAIT);
		if (len == -1) die("recv\n");
 
 
 
		if (!fork())
		{
			unsigned int count = 0;
			char **env = calloc(9, sizeof(char *));
			i = 0;
			while (i<len) {
				env[count++] = (char *) strdup(buf+i);
				i += strlen(buf+i)+1;
			}
			env[count] = (char *) NULL;
 
			execle("/usr/bin/mdev", "/usr/bin/mdev", (char *) NULL, env);
 
			exit(0);
		}
	}
	die("poll\n");
 
	// Dear gcc: shut up.
	return 0;
}

References