Understanding the Raspberry Pi – FreeBSD gpio device

Recently I found myself wanting to understand how I2C for FreeBSD ARM, on the Pi works.  The first step is to understand the GPIO implementation on Pi, since on a Pi, I2C is implemented on the GPIO.

So, here's the basic summary.

Firstly, the FreeBSD source browser is here.

In the directory /sys/ is the kernel code for all platforms, and in /sys/sys/ is the core kernel code.  In /sys/sys/gpio.h, you'll find the header which declares the GPIO API for FreeBSD, which must be supported by all FreeBSD GPIO devices on all platforms.

Here's the important parts:

/* GPIO pin states */
#define GPIO_PIN_LOW		0x00	/* low level (logical 0) */
#define GPIO_PIN_HIGH		0x01	/* high level (logical 1) */

/* Max name length of a pin */
#define GPIOMAXNAME		64

/* GPIO pin configuration flags */
#define GPIO_PIN_INPUT		0x0001	/* input direction */
#define GPIO_PIN_OUTPUT		0x0002	/* output direction */
#define GPIO_PIN_OPENDRAIN	0x0004	/* open-drain output */
#define GPIO_PIN_PUSHPULL	0x0008	/* push-pull output */
#define GPIO_PIN_TRISTATE	0x0010	/* output disabled */
#define GPIO_PIN_PULLUP		0x0020	/* internal pull-up enabled */
#define GPIO_PIN_PULLDOWN	0x0040	/* internal pull-down enabled */
#define GPIO_PIN_INVIN		0x0080	/* invert input */
#define GPIO_PIN_INVOUT		0x0100	/* invert output */
#define GPIO_PIN_PULSATE	0x0200	/* pulsate in hardware */

struct gpio_pin {
	uint32_t gp_pin;			/* pin number */
	char gp_name[GPIOMAXNAME];		/* human-readable name */
	uint32_t gp_caps;			/* capabilities */
	uint32_t gp_flags;			/* current flags */
};

/* GPIO pin request (read/write/toggle) */
struct gpio_req {
	uint32_t gp_pin;			/* pin number */
	uint32_t gp_value;			/* value */
};

/*
 * ioctls
 */
#define GPIOMAXPIN		_IOR('G', 0, int)
#define	GPIOGETCONFIG		_IOWR('G', 1, struct gpio_pin)
#define	GPIOSETCONFIG		_IOW('G', 2, struct gpio_pin)
#define	GPIOGET			_IOWR('G', 3, struct gpio_req)
#define	GPIOSET			_IOW('G', 4, struct gpio_req)
#define	GPIOTOGGLE		_IOWR('G', 5, struct gpio_req)

So, this defines what a GPIO device looks like to FreeBSD and the ioctls it supports, on all platforms.

For the ARM kernel, the source code is in /sys/arm/, and for Raspberry Pi, which based on a Broadcom BCM 2835 chip the kernel sources are at /sys/arm/broadcom/bcm2835/.

In /sys/arm/broadcom/bcm2835/bcm2835_gpio.c, you'll find the implementation which exposes the GPIO device, for a Raspberry Pi.  You should notice that the header "gpio_if.h" is included.  This is a generated file, and the source is in /sys/dev/gpio_if.m.   The important parts of this are here:

INTERFACE gpio;

#
# Get total number of pins
#
METHOD int pin_max {
	device_t dev;
	int *npins;
};

#
# Set value of pin specifed by pin_num 
#
METHOD int pin_set {
	device_t dev;
	uint32_t pin_num;
	uint32_t pin_value;
};

#
# Get value of pin specifed by pin_num 
#
METHOD int pin_get {
	device_t dev;
	uint32_t pin_num;
	uint32_t *pin_value;
};

#
# Toggle value of pin specifed by pin_num 
#
METHOD int pin_toggle {
	device_t dev;
	uint32_t pin_num;
};

#
# Get pin capabilities
#
METHOD int pin_getcaps {
	device_t dev;
	uint32_t pin_num;
	uint32_t *caps;
};

#
# Get pin flags
#
METHOD int pin_getflags {
	device_t dev;
	uint32_t pin_num;
	uint32_t *flags;
};

#
# Get pin name
#
METHOD int pin_getname {
	device_t dev;
	uint32_t pin_num;
	char *name;
};

#
# Set current configuration and capabilities
#
METHOD int pin_setflags {
	device_t dev;
	uint32_t pin_num;
	uint32_t flags;
};

An important piece of code to notice in "bcm2835_gpio.c" is the device driver method "probe":

static int
bcm_gpio_probe(device_t dev)
{
	if (!ofw_bus_is_compatible(dev, "broadcom,bcm2835-gpio"))
		return (ENXIO);

	device_set_desc(dev, "BCM2708/2835 GPIO controller");
	return (BUS_PROBE_DEFAULT);
}

This is the method FreeBSD calls to ask the driver if a specific piece of hardware is appropriate for that driver.  The ARM GPIO driver looks for "broadcom,bcm2835-gpio", which is the exact name defined in the Raspberry Pi FDT.

Another method which  if the "attach" method, which initializes the driver and adds it to the bus.  This line gets a "bcm_gpio_softc" struct from the device_t passed by the kernel.  The method "device_get_softc" is forward declared in /sys/sys/bus.h, and implemented in /sys/kern/subr_bus.c

struct bcm_gpio_softc *sc = device_get_softc(dev);

This method initializes a mutex for the driver:

mtx_init(&sc->sc_mtx, "bcm gpio", "gpio", MTX_DEF);

These macros

#define	BCM_GPIO_LOCK(_sc)	mtx_lock(&_sc->sc_mtx)
#define	BCM_GPIO_UNLOCK(_sc)	mtx_unlock(&_sc->sc_mtx)
#define	BCM_GPIO_LOCK_ASSERT(_sc)	mtx_assert(&_sc->sc_mtx, MA_OWNED)

Are used to serialize access in certain methods in the driver.

These lines allocate memory and IRQs for the driver, as specified in the FTD:

rid = 0;
	sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
	    RF_ACTIVE);
	if (!sc->sc_mem_res) {
		device_printf(dev, "cannot allocate memory window\n");
		return (ENXIO);
	}

	sc->sc_bst = rman_get_bustag(sc->sc_mem_res);
	sc->sc_bsh = rman_get_bushandle(sc->sc_mem_res);

	rid = 0;

 

sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
	    RF_ACTIVE);
	if (!sc->sc_irq_res) {
		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);
		device_printf(dev, "cannot allocate interrupt\n");
		return (ENXIO);
	}

These lines query the Open Firmware and verify the that device is indeed a GPIO controller, according to the FTD:

/* Find our node. */
gpio = ofw_bus_get_node(sc->sc_dev);
if (!OF_hasprop(gpio, "gpio-controller"))
	/* Node is not a GPIO controller. */
	goto fail;

These lines set up the initial state of the GPIO controller pins:

	if (bcm_gpio_get_reserved_pins(sc) == -1)
		goto fail;

	/* Initialize the software controlled pins. */
	for (i = 0, j = 0; j < BCM_GPIO_PINS - 1; j++) {
		if (bcm_gpio_pin_is_ro(sc, j))
			continue;
		snprintf(sc->sc_gpio_pins[i].gp_name, GPIOMAXNAME,
		    "pin %d", j);
		func = bcm_gpio_get_function(sc, j);
		sc->sc_gpio_pins[i].gp_pin = j;
		sc->sc_gpio_pins[i].gp_caps = BCM_GPIO_DEFAULT_CAPS;
		sc->sc_gpio_pins[i].gp_flags = bcm_gpio_func_flag(func);
		i++;
	}
	sc->sc_gpio_npins = i;

	bcm_gpio_sysctl_init(sc);

Finally, these lines add two devices "gpioc" and "gpiocbus", and then attach the dev to the main bus

device_add_child(dev, "gpioc", device_get_unit(dev));
device_add_child(dev, "gpiobus", device_get_unit(dev));
return (bus_generic_attach(dev));

The function "device_add_child" is implemented in /sys/kern/subr_bus.c.  The devices "gpio" and "gpioc" are generic devices, which work for all architectures, and they are in:

/sys/dev/gpio/gpioc.c and /sys/dev/gpio/gpiobus.c.  The reason the kernel is able to find them by their device class names "gpio" and "gpiobus" is because they are registered with the kernel via these lines of code in gpioc.c and gpiobus.c.

driver_t gpioc_driver = {
	"gpioc",
	gpioc_methods,
	sizeof(struct gpioc_softc)
};

devclass_t	gpioc_devclass;

DRIVER_MODULE(gpioc, gpio, gpioc_driver, gpioc_devclass, 0, 0);
MODULE_VERSION(gpioc, 1);
driver_t gpiobus_driver = {
	"gpiobus",
	gpiobus_methods,
	sizeof(struct gpiobus_softc)
};

devclass_t	gpiobus_devclass;

DRIVER_MODULE(gpiobus, gpio, gpiobus_driver, gpiobus_devclass, 0, 0);
MODULE_VERSION(gpiobus, 1);

The gpio driver which will finally appear on the Pi as "/dev/gpio0", includes the header system header "/sys/gpio.h" and implements all its methods via that API, which ultimately comes back to /sys/arm/broadcom/bcm2835/bcm2835_gpio.c.

 

Spelunking the FreeBSD ARM FDT

The FDT is the "Flattened Device Tree", a simple device tree used in embedded FreeBSD, for example on a Pi.  Every architecture has a DTS file, which contains a human-readable list of hardware for that architecture.   For example, the Pi FTD is here.  You should notice that there are declarations for:

  • cpu
  • axi (the system bus)
  • time & watchdog
  • gpio: GPIO
  • dma: DMA controller
  • vc_mbox
  • uart; serial controller
  • sdhci; SD card controller
  • memory
  • display
  • leds; the GPIO leds
  • power regulators

In other words, all the hardware in the Pi.  If you look at this datasheet from Broadcom, you should see that the hardware declared in the FDT file is the hardware on the Pi's SOC chip, the Broadcom BCM2835.

In my case, I'm specifically interested in RS-232 using the GPIO pins 14 and 15.  The FDT file contains this:

/* UART0 */
pins_uart0_a: uart0_a {
	broadcom,pins = <14>, <15>;
	broadcom,function = "ALT0";
};

Which specifies that uart0 binds GPIO pins 14 and 15.   If you look in the datasheet, on page 101, you'll see that GPIO pins 14 and 15, in ALT0 mode are serial TX and RX respectively.

If you have FreeBSD up on a Pi, and you have the /dev/openfirm device compiled in to your kernel, you can find uart0 using /usr/sbin/ofwdump.

The output "ofwdump -a" on my Pi shows:

root@raspberry-pi:/etc # ofwdump -a
Node 0x48: 
  Node 0xf8: system
  Node 0x150: cpus
    Node 0x15c: cpu@0
  Node 0x18c: axi
    Node 0x1f8: interrupt-controller
    Node 0x29c: timer
    Node 0x344: armtimer
    Node 0x3bc: watchdog0
    Node 0x41c: gpio
      Node 0x538: bsc0_a
      Node 0x570: bsc0_b
      Node 0x5a8: bsc0_c
      Node 0x5e0: bsc1_a
      Node 0x618: bsc1_b
      Node 0x650: gpclk0_a
      Node 0x688: gpclk0_b
      Node 0x6c0: gpclk0_c
      Node 0x6f8: gpclk0_d
      Node 0x730: gpclk1_a
      Node 0x768: gpclk1_b
      Node 0x7a0: gpclk1_c
      Node 0x7d8: gpclk1_d
      Node 0x810: gpclk2_a
      Node 0x848: gpclk2_b
      Node 0x880: spi0_a
      Node 0x8c4: spi0_b
      Node 0x908: pwm0_a
      Node 0x93c: pwm0_b
      Node 0x970: pwm0_c
      Node 0x9a4: pwm1_a
      Node 0x9d8: pwm1_b
      Node 0xa0c: pwm1_c
      Node 0xa40: pwm1_d
      Node 0xa74: uart0_a
      Node 0xaac: uart0_b
      Node 0xae4: uart0_c
      Node 0xb1c: uart0_fc_a
      Node 0xb58: uart0_fc_b
      Node 0xb94: uart0_fc_c
      Node 0xbd0: pcm_a
      Node 0xc10: pcm_b
      Node 0xc50: sm_addr_a
      Node 0xc9c: sm_addr_b
      Node 0xce8: sm_ctl_a
      Node 0xd24: sm_ctl_b
      Node 0xd60: sm_data_8bit_a
      Node 0xdb8: sm_data_8bit_b
      Node 0xe10: sm_data_16bit
      Node 0xe68: sm_data_18bit
      Node 0xea8: bscsl
      Node 0xee0: spisl
      Node 0xf20: spi1
      Node 0xf68: uart1_a
      Node 0xfa0: uart1_b
      Node 0xfd8: uart1_c
      Node 0x1010: uart1_fc_a
      Node 0x104c: uart1_fc_b
      Node 0x1088: uart1_fc_c
      Node 0x10c4: spi2
      Node 0x110c: arm_jtag_trst
      Node 0x1148: arm_jtag_a
      Node 0x1190: arm_jtag_b
      Node 0x11d8: reserved
    Node 0x1238: dma
    Node 0x12f8: mbox
    Node 0x1384: sdhci
    Node 0x1414: uart0
    Node 0x14c8: vchiq
    Node 0x1530: usb
      Node 0x15e0: hub
        Node 0x1638: ethernet
  Node 0x16a0: memory
  Node 0x16d8: display
  Node 0x176c: leds
    Node 0x1790: ok
  Node 0x17f4: regulator
    Node 0x18ec: regulator@0
    Node 0x1998: regulator@3
  Node 0x1a48: aliases
  Node 0x1a70: chosen

The list of devices on my Pi matches the list in the FTD (here).

I can also use devinfo to find the devices that are actuallly found in my system.  Here's the output, with the USB stuff removed:

root@raspberry-pi:/etc # devinfo -r
nexus0
  fdtbus0
    simplebus0
      intc0
          I/O memory:
              0x2000b200-0x2000b3ff
      systimer0
          Interrupt request lines:
              8
              9
              10
              11
          I/O memory:
              0x20003000-0x20003fff
      bcmwd0
          I/O memory:
              0x2010001c-0x20100027
      gpio0
          Interrupt request lines:
              57
          I/O memory:
              0x20200000-0x202000af
        gpioc0
        gpiobus0
      bcm_dma0
          Interrupt request lines:
              24
              25
              26
              27
              28
              29
              30
              31
              32
              33
              34
              35
          I/O memory:
              0x20007000-0x20007fff
      mbox0
          Interrupt request lines:
              1
          I/O memory:
              0x2000b880-0x2000b8bf
      sdhci_bcm0
          Interrupt request lines:
              70
          I/O memory:
              0x20300000-0x203000ff
        mmc0
          mmcsd0
      uart0
          Interrupt request lines:
              65
          I/O memory:
              0x20201000-0x20201fff
      dwcotg0
          Interrupt request lines:
              17
          I/O memory:
              0x20980000-0x2099ffff
        usbus0
          uhub0
            uhub1
              smsc0
                miibus0
                  ukphy0
              ukbd0
    fb0
    simplebus1

If I check the dmesg output I see that I do indeed have a uart0 that FreeBSD found on boot:

root@raspberry-pi:/dev # dmesg | grep uart
uart0: <PrimeCell UART (PL011)> mem 0x20201000-0x20201fff irq 65 on simplebus0

Verifying with ofwdump:

root@raspberry-pi:/etc # ofwdump -p uart0
Node 0x1414: uart0
  compatible:
    62 72 6f 61 64 63 6f 6d 2c 62 63 6d 32 38 33 35 2d 75 61 72 
    74 00 62 72 6f 61 64 63 6f 6d 2c 62 63 6d 32 37 30 38 2d 75 
    61 72 74 00 61 72 6d 2c 70 6c 30 31 31 00 61 72 6d 2c 70 72 
    69 6d 65 63 65 6c 6c 00 
  reg:
    00 20 10 00 00 00 10 00 
  interrupts:
    00 00 00 41 
  interrupt-parent:
    00 00 00 01 
  clock-frequency:
    00 2d c6 c0 
  reg-shift:
    00 00 00 02

You can see that the data ofwdump returns for uart0 matches with the FTD and with uart0 that FreeBSD reported.  The IRQ that ofwdump returns is 0x41, which is 65 in base-10; exactly what's expected.

If you look at the bottom of the FTD file, you will see that uart0 is bound to stdin and stdout:

chosen {
		bootargs = "";			/* Set by VideoCore */
		stdin = "uart0";
		stdout = "uart0";
	};

There is a description of the "/chosen" node in the FDT here and here from devicetree.org.

For more information, there is this excellent presentation, this FreeBSD Wiki page, and this white paper.   There is a wiki here, with quite a lot of good information on Flattened Device Tree.

Looking at the /dev/ tree I see:

crw------- 1 root wheel 0x1f Apr 27 13:42 ttyu0
crw------- 1 root wheel 0x20 Apr 27 21:42 ttyu0.init
crw------- 1 root wheel 0x21 Apr 27 21:42 ttyu0.lock
crw-rw---- 1 uucp dialer 0x22 Apr 27 21:42 cuau0
crw-rw---- 1 uucp dialer 0x23 Apr 27 21:42 cuau0.init
crw-rw---- 1 uucp dialer 0x24 Apr 27 21:42 cuau0.lock

I these devices are the sio device, bound to uart0.

/dev/ttyu0 was declared in the /etc/ttys.  The build script here was used to create my Pi install, and the specific line that declares this tty was;

ttyu0 "/usr/libexec/getty 3wire.115200" dialup on secure

If you're interested in what "3wire.115200" actually means, it's declared in /etc/gettytab.  The details of setting up tty devices in FreeBSD are here, in the FreeBSD Handbook.

 

restcache

I recently found myself in a situation where mission-critical software was suffering from performance problems due to relying on a remote API which was both slow (as slow as 11sec / transaction), and unreliable.   In this case, it turned out that there were multiple applications accessing this API, and every individual application was affected.  There were some details of the remote API that were notable:

  • It is accessible via RESTful URLs
  • It returns cacheable results
  • It is generally used in a read-only mode

So, a reasonable solution appeared to be to write a caching proxy.  The result is restcache.

Details of the solution are very simple.  I implemented a Java Servlet which proxies GET and POST requests to a foreign server.  GET requests which do not have query parameters are intercepted and cached using Apache JCS.  JCS is a very mature piece of software which can implement multi-layer caches, and includes features such as disk based caches, relational caches and even in-memory caches.

For restcache, it was likely that there would be more that one foreign API which I wanted to proxy-cache, so I implemented cache pools. A cache pool is simply a dedicated JCS region which caches responses from a specific HTTP URL.

Finally, real-world caches, on real-world sites, need to be maintained, monitored and occasionally cleared by system admins.  restcache exposes a great deal of data using JMX, and supports clearing pools via JMX.  Any reasonable system administration tool which can communicate with servers over JMX could monitor restcache, or an admin could simply use JConsole.

Configuration of caches in restcache is simple:

  • Configure the JCS regions, like this.   The example which matches the XML below is here.
  • Configure the restcache pools in XML.  The schema is here.

Here's an example pool declaration which caches RSS from CBC.  The JCS region it uses is called "cbc", and is configured in cache.ccf.

<rcpool>
 <name>cbc</name>
 <region>cbc</region>
 <target>rss.cbc.ca</target>
</rcpool>

So, any cacheable GET request to "rss.cbc.ca" will be stored in the JCS region "cbc".  So this, for example, would be cached.

Interestingly, JCS supports lateral caching, so if you really need that, it's available.

Finally, there is an additional potential application; reducing the costs incurred by accessing per-transaction APIs.   Some APIs charge a fee every time the API is accessed.  If those APIs are accessed from a public-facing site, there quickly becomes an issue of cost control.  restcache could be inserted between the costly API and the public facing site with the intention of returning cached results rather than accessing the API for every page render.

Simple pf for Raspberry Pi

One of the best things about the Pi is that it's small, and portable.  It's very tempting to use it on wireless networks, unattended.  Sadly, in the real world, any wireless network can be hacked, and any device on wireless should be considered vulnerable.  So, pf is an easy way to harden your Pi.

The first thing you'll need is a FreeBSD Pi install, which has the pf device in the kernel.  You can get that here, or just build yourself a kernel with pf.

Then, you will need some simple rules.  I used these rules in the file "/etc/pf.conf"

# options
set block-policy return
set optimization conservative

# normalization
scrub in all
scrub out all

# default, deny everything
block in log all
pass out quick

#icmp
pass out inet proto icmp from any to any keep state
pass in quick inet proto icmp from any to any keep state

#ssh
pass out inet proto tcp from any to any port 22 keep state
pass in quick inet proto tcp from any to any port 22 keep state

#localhost
pass in quick on lo0 all
pass out quick on lo0 all

Simply; I block everything other than ssh, icmp (ping).

In order to load the rules:

pfctl -f /etc/pf.conf

and to show the current status of the packet filter:

pfctl -s all

You'll want FreeBSD to load your pf rules when it starts, so add the below to "/etc/rc.conf"

pf_enable="YES"

Identifying unknown hardware a Pi running FreeBSD

I've been building FreeBSD-CURRENT images for my Pi, and I've noticed I have this:

ugen0.5: <vendor 0x7392> at usbus0

So, it turns out I have one of these plugged in, and it seems likely that the unknown USB device is ugen0.5.  So firstly, there is the question of "ugen".  You can read about it here.  Essentially when FreeBSD finds a USB device it doesn't have a specific driver for, it assigns it the ugen driver.   In my case, the driver is attached to device 0, endpoint 5.

To get more info, I can use usbconfig.

root@raspberry-pi:/home/tom # usbconfig -d ugen0.5 dump_info
ugen0.5: <product 0x7811 vendor 0x7392> at usbus0, cfg=0 md=HOST spd=HIGH (480Mbps) pwr=ON (500mA)

The product code noted in by usbconfig, is 0x7811, which matches with the Edimax 7811.

So to find out what "vendor 0x7392" is, I used http://usb-ids.gowdy.us/index.html. If you search in this file, you'll find this:

7392  Edimax Technology Co., Ltd
	7711  EW-7711UTn nLite Wireless Adapter [Ralink RT2870]
	7717  EW-7717UN 802.11n Wireless Adapter [Ralink RT2870]
	7718  EW-7718UN 802.11n Wireless Adapter [Ralink RT2870]
	7722  EW-7722UTn 802.11n Wireless Adapter [Ralink RT307x]
	7811  EW-7811Un 802.11n Wireless Adapter [Realtek RTL8188CUS]

So, my device "ugen0.5" is the Edimax EW-7811Un.  It uses a Realtek  RTL8188CUS chipset.   Some Edimax devices are supported by the rum driver, but that driver appears to support the Ralink chipset, rather than Realtek.  If FreeBSD did support this chipset, it might be more likely to be supported by the urtw driver.  The man page doesn't mention the 8188CUS Realtek chipset, however.

In fact, you can look at the source for the WLAN USB drivers in FreeBSD here and you will find the source for the urtw driver here.  It supports the RTL 8187B.

If you take a look in /sys/dev/usb/usbdevs, you will see a list of the USB devices FreeBSD knows about.  There are some Edimax devices, and the 7811 is listed:

/* Edimax products */
1556 product EDIMAX EW7318USG 0x7318 USB Wireless dongle
1557 product EDIMAX RT2870_1 0x7711 RT2870
1558 product EDIMAX EW7717 0x7717 EW-7717
1559 product EDIMAX EW7718 0x7718 EW-7718
1560 product EDIMAX EW7811UN 0x7811 EW-7811Un

So, it appears that FreeBSD knows that it's a ED7811UN, but RTL8188 chipset is not supported.

Creating a custom FreeBSD build for Raspberry Pi

The default FreeBSD kernel for Raspberry Pi can be found here in the FreeBSD source browser.  It's everything you need to boot FreeBSD on a Pi, and includes GPIO support.  However, the Pi is a nice small computer, so it's likely that you'll want it to include options such as Wifi, and pf.  In my case, I wanted IPSEC and support for USB Serial via uftdi.

In order to add drivers to the kernel, it's useful to have a list of the drivers that can be added.  That exists here.  The man pages, are a useful place to look up what a specific driver does, what hardware it supports, etc.

I added this stuff.

# WLAN required for wireless
device		wlan
device		wlan_wep
device		wlan_ccmp
device		wlan_tkip

# USB wireless support
device		ehci
device		uhci
device		ohci
device		wlan_amrr
device 		firmware

device		urtw
device		zyd
device		ural
device		upgt
device		uath
device		run
device		rum

# USB ethernet support
device		smcphy
device		mii
device		smsc

# USB serial 
# device uftdi

#proc filesystem
options 	PROCFS			#Process filesystem (requires PSEUDOFS)
options 	PSEUDOFS		#Pseudo-filesystem framework

#pf
device		pf

# IPsec interface.
device		enc

I also renamed my kernel configuration

ident		RPI-B-WIFI

The kernel options I added give me:

  • WLAN support
  • All the USB Wifi drivers
  • pf, the packet filter
  • the /proc filesystem
  • the enc device, for IPSEC
  • uftdi

I copied this kernel configuration (name RPI-B-WIFI) to the right spot on my local source tree

cp RPI-B-WIFI src/FreeBSD/head/sys/arm/conf/

Then I used the freebsd-arm-tools to create a 4GB disk image, with this kernel, a small swap, and the full ports tree:

sh build-arm-image.sh -s4 -u -p -mtom@khubla.com -w512 -kRPI-B-WIFI

A simple FreeBSD GPIO Example

I've been making FreeBSD-Current builds for my Raspberry Pi.  Naturally I wanted to try out the GPIO support to find out just how simple it is to use.   Here's an example, and the full code is here.

int main (int argc, char *argv[])
	{
	char *device = "/dev/gpioc0";
	int gpiofd = open(device, O_RDWR);
	if (-1!=gpiofd)
		{
		// get total pins
		int maxpin=0;
		if (ioctl(gpiofd, GPIOMAXPIN, &maxpin) < 0) 
			{
			perror("ioctl(GPIOMAXPIN)");
			exit(1);
			}
			else
			{
			printf("GPIO has %d pins\n",maxpin);
			// walk the pins
			printf("pin# description capabilties flags\n");
			for (int i = 0; i <= maxpin; i++)
				{
				struct gpio_pin pinStatus;
				pinStatus.gp_pin=i;
				int r = ioctl(gpiofd, GPIOGETCONFIG, &pinStatus);
				if ( r>= 0)
					{
					printf("%i: %s  0x%x  0x%x\n",i, pinStatus.gp_name, pinStatus.gp_caps, pinStatus.gp_flags);
					}
					else 
					{
					printf("failed to get status of pin %d with status %d\n",i,r);
					}				
				} 
			}
		} 
		else 
		{
		printf("failed to open device with error%d\n",gpiofd);
		}	
	}

freebsd-arm-tools

I've had the opportunity to make some updates to the freebsd-arm-tools.   If you're interested in trying FreeBSD-current on a Raspberry Pi, I've posted a build at http://files.khubla.com/freebsd-raspberry-pi/ which has full Wifi and pf support.  If you're interested, the kernel configuration is here.

Installing the FreeBSD on the Pi is simple.  Firstly, get an image, such as this one

http://files.khubla.com/freebsd-raspberry-pi/FreeBSD-HEAD-r249996-ARMv6-RPI-B-WIFI-3GB.img.tgz

Extract the Image

tar zxvf FreeBSD-HEAD-r249907-ARMv6-RPI-B-WIFI-4GB.img.tgz

Unmount the SD card

sudo diskutil unmount /dev/disk1s1

Burn the image to the SD card

sudo dd if=FreeBSD-HEAD-r249907-ARMv6-RPI-B-WIFI-4GB.img of=/dev/rdisk1 bs=1m

Unmount the SD again

sudo diskutil unmount /dev/disk1s1

Then, boot the Pi from the SD card.   ssh to root is disabled, however you can log in as username "pi" with password "raspberry", and su to root.