Compiling BeagleBone Kernel and Making a Kernel Module.

In my process of learning Linux and the BeagleBone, I have needed to compile the CircuitCo standard released kernel and to make kernel modules. This post will give a brief overview of how I have accomplished this and hopefully help out others that seem to be struggling in the same area.

First I would like to thank several long time friends and past co-workers for their help and patience in helping me get over some of the hurdles I have encountered. Thanks goes to Matt Porter (http://engineersofthecorn.blogspot.com) and Mark Greer of Animal Creek Technologies (mgreer@animalcreek.com) for their continued friendship and assistance in git and Linux.

Note: All of this blog entry was performed as root. If you are not root then there is a good chance you will have to proceed the commands with “sudo” to not get errors.

Step #1 – Getting a Linux Development Environment

If you have a Linux system set up already, skip to step #2.

Instead of going through what is needed in a Linux development environment, I’ll just point you to what I was pointed to when I first started. The link below is a virtual machine based on the VMware architecture that has everything you need to get started except the cross environment tools.

http://www.trendsigma.net/vmware/lubuntu1110t.zip

This image can be used on Windows (by VMware Player – http://www.trendsigma.net/vmware/vmwareplayer.html ) or on a MAC (by Parallels – http://www.parallels.com ). If you want to see details on this,  see http://www.trendsigma.net/vmware/lubuntu1110t.html.

Step #2 – Getting Cross Development Tools

Once you get the Linux up and going, login as root (note: on kernel above acct and password are:  “root” and “password”) and get a terminal window up. In the terminal window issue the following command:

# apt-get install build-essential gcc-arm-linux-gnueabi git lzop u-boot-tools

This command will download and install:

Step #3 – Find Out What Kernel Branch is Used on a CircuitCo Release

In this section we will determine what makes up the current CircuitCo kernel .To do this, we will need to load up the kernel and boot it. The BeagleBone CircuitCo standard releases are located here: http://circuitco.com/support/index.php?title=BeagleBone. At the time of this writing, the latest version (Revision A6 Image) is located here. All of the examples are based on this Linux 3.2.18 based A6 image.

Follow the instructions on how to make an SD card and boot it on your BeagleBone. From a terminal window do the following command…

# opkg list_installed | grep kernel-image-3.2.18
kernel-image-3.2.18 - 3.2.18-r12j+gitr720e07b4c1f687b61b147b31c698cb6816d72f01

We are interested in the long string after the kernel image. This is the branch that the kernel was made from. Copy it down or copy/paste it somewhere for reference in the next step.

Step #4 – Git the Linux Kernel

Note:  There is a lot of commands below that are for the git revision control system. I am not going to get into detail on what these commands do or how git works. If you need help or background, see this page for a list of the Top 10 Git Tutorials for Beginners.

In this step you will get (or clone) the Linux kernel repository used by CircuitCo and set up the branch that makes up the current kernel from CircuitCo.

The BeagleBone Linux kernel that is used is located in Koen Kooi’s Linux github repository. In your Linux development system start a terminal widow and execute the commands below. Be warned, the first command will take a while to complete depending on your network connection and system performance. Also, the output from the commands are omitted due to the length of the output.

# git clone git://github.com/koenkooi/linux.git
# cd linux
# git checkout remotes/origin/linux-ti33x-psp-3.2.18-r12j+gitr720e07b4c1f687b61b147b31c698cb6816d72f01 -b beaglebone-a6

The first command will clone the repository on your system. This will make a snapshot of what has been checked in at this moment. This includes code that has been changed since the release we are trying to duplicate (A6) was produced.

The next two commands (cd and git checkout), set up a branch that contains the version of the source that was used to produce the A6 kernel. This is where the long string that was found in the previous step is used.

Check to make sure you are on the proper branch by doing the following and see that the asterisk “*” is located on the branch we made above (beaglebone-a6).
# git branch
 * beaglebone-a6
   master
#

And finally we need to get the firmware that the BeagleBone kernel needs. The kernel will need this binary PM firmware blob to build.

# wget -O firmware/am335x-pm-firmware.bin http://arago-project.org/git/projects/?p=am33x-cm3.git;a=blob_plain;f=bin/am335x-pm-firmware.bin;hb=HEAD

Step #5 – Build The Kernel…

And now that we have the proper tools, the kernel source and firmware blob we can start it building. In these steps we will make a directory to put the kernel we are building, copy the configuration file that was used and finally start the build.

# mkdir a6
# cp defconfig a6/.config
# make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- O=./a6 uImage modules

Note: Again I did not put in the output from the commands. The make command will take a fair amount of time depending on your system performance and output pages and pages.  Also note, the directory name ( “a6″ ) can be changed to anything you want it to be. But you will have to change the Makefile in the later kernel module build steps.

When the make command is completed, you have duplicated the CircuitCo A6 kernel and kernel modules. Now this has made all of the necessary directories, source and headers that are needed to easily build a kernel module. And if you wanted to, you could even replace the kernel and modules on the BeagleBone SD card and reboot the kernel. I’ll make another post later on how to do that as well as how to change the kernel configuration away form the default.

Step #6 – Making a New Kernel Module

In this section I will finally make a kernel module and get it on the BeagleBone. First steps will be to make a directory for building the kernel module, putting in some source (I have example source) and compiling the module.

The kernel module that I am going to build is my version of the “hello world” kernel module. It is a version of the hello_world.c program made into a kernel module and is a fairly common first kernel module. Do a little googling and you will see several versions of it on the internet. In my version (like others), a kernel message will be made when the module is added and when it is removed.

hello.c source here… ( kernel module )
#include <linux/module.h>       /* Needed by all modules */
#include <linux/kernel.h>       /* Needed for KERN_INFO */
#include <linux/init.h>         /* Needed for the macros */

static int __init hello_start(void)
{
    printk(KERN_INFO "Loading hello.ko module...\n");
    printk(KERN_INFO "Hello world\n");
    return 0;
}

static void __exit hello_end(void)
{
    printk(KERN_INFO "Goodbye: hello.ko exiting...\n");
}

module_init(hello_start);
module_exit(hello_end);

MODULE_DESCRIPTION("Example kernel module based on many sources on the internet.");
MODULE_AUTHOR("Ken Keller <kkeller@cabinprograms.com>");
MODULE_LICENSE("GPL");
Makefile source here… ( kernel module Makefile )
obj-m = hello.o
KDIR := /root/linux/a6
all:
    make -C $(KDIR) M=$(shell pwd) modules
clean:
    make -C $(KDIR) M=$(shell pwd) clean

Note: In the Makefile above, you will need to change the KDIR to point to the directory path where you compiled the kernel.

Here are the simple commands to make the kernel module. When these are done, you will have a hello.ko file that can be put in the kernel on the BeagleBone.

# cd
# mkdir kernel_mod
# cd kernel_mod
<get hello.c and Makefile source above and put the files in here>
# make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi-

At this point, you need to copy the hello.ko kernel module over to the BeagleBone. I personally use the “scp” command to move files around. There are many different ways to do this so choose your favorite way. (I see need for another blog post on this subject…)

Once the kernel module is moved over, login to the BeagleBone and “cd” to where you moved the module. Below are the commands to install the module (insmod), check to see if the module was installed (lsmod) and remove (rmmod) the module into the kernel.

# insmod hello.ko
# lsmod
Module                  Size  Used by
hello                    738  0 
ip_tables               7829  0 
x_tables               14368  1 ip_tables
spidev                  3920  0 
g_mass_storage         24002  0 
rfcomm                 24289  0 
ircomm_tty             14015  0 
ircomm                  8515  1 ircomm_tty
irda                   85575  2 ircomm_tty,ircomm
hidp                   10079  0 
bluetooth             109011  4 rfcomm,hidp
rfkill                 14663  2 bluetooth
ipv6                  208881  16 
# dmesg | tail
[ 8307.639412] gpio_request: gpio-38 (sysfs) status -16
[ 8307.639424] export_store: status -16
[ 8307.639534] gpio_request: gpio-73 (sysfs) status -16
[ 8307.639546] export_store: status -16
[ 8307.639647] gpio_request: gpio-70 (sysfs) status -16
[ 8307.639659] export_store: status -16
[ 8307.639756] gpio_request: gpio-71 (sysfs) status -16
[ 8307.639767] export_store: status -16
[13479.961098] Loading hello.ko module...
[13479.961112] Hello world
# rmmod hello
# dmesg | tail
[ 8307.639424] export_store: status -16
[ 8307.639534] gpio_request: gpio-73 (sysfs) status -16
[ 8307.639546] export_store: status -16
[ 8307.639647] gpio_request: gpio-70 (sysfs) status -16
[ 8307.639659] export_store: status -16
[ 8307.639756] gpio_request: gpio-71 (sysfs) status -16
[ 8307.639767] export_store: status -16
[13479.961098] Loading hello.ko module...
[13479.961112] Hello world
[13511.073550] Goodbye: hello.ko exiting
#

Final comment…  This is a really long set of instructions and not much was said about what can go wrong. Believe me, there is a lot that go wrong. Good luck and hope it helps!

Note: All of this blog entry was performed as root. If you are not root then there is a good chance you will have to proceed the commands with “sudo” to not get errors.

Adventures in BeagleBone Cape EEPROMs

Recently I had to figure out how to put data into a BeagleBone Cape EEPROM. It was not a hard thing to do but I figured I would document it because I could not find this information anywhere…

In this post, I will show two methods to prepare the data for the EEPROM. One method will be using bonescript files that are provided in the CircuitCo factory images. The other method will be using a small utility I made to prompt for the data.

Before you start to develop data for the EEPROM, you should become familiar with the data stored in the EEPROM. This information is in the BeagleBone SRM. The current version of this document is located here:

http://beagleboard.org/static/beaglebone/latest/Docs/Hardware/BONE_SRM.pdf

The Cape EEPROM information is located in section “8.0  Cape Board Support“. Here is a quick summary of the data for the current version of the EEPROM (“A0″).

Name

Offset

Size (bytes)

Contents

Header

0

4

0xAA, 0x55, 0x33, 0xEE

EEPROM Format Revision

4

2

Revision number of the overall format of this
EEPROM in ASCII =A0

Board Name

6

32

Name of board in ASCII

Version

38

4

Hardware version code for board in ASCII

Manufacturer

42

16

ASCII name of the manufacturer

Part Number

58

16

ASCII Characters for the part number

Number of Pins

74

2

Number of pins used by the daughter board

Serial Number

76

12

Serial number of the board. This is a 12 character
string which is: WWYY4P13nnnn
where: WW = 2 digit week of the year of production
YY = 2 digit year of production
nnnn = incrementing board number

Pin Usage

88

148

Two bytes for each configurable 74 pins on the
expansion connectors

Bit 15: Pin is used or not; 0=Unused by
Cape 1=Used by Cape

Bit 14-13: Pin Direction; 1 0=Output 01=Input
11=BDIR
Bits 12-7: Reserved
Bit 6: Slew Rate; 0=Fast 1=Slow

Bit 5: Rx Enable; 0=Disabled 1=Enabled
Bit 4: Pull Up/Dn Select; 0=Pulldown 1=PullUp
Bit 3: Pull Up/DN enabled; 0=Enabled 1=Disabled

Bits 2-0: Mux Mode Selection; Mode 0-7

VDD_3V3EXP Current

236

2

Maximum current in milliamps

VDD_5V Current

238

2

Maximum current in milliamps

SYS_5V Current

240

2

Maximum current in milliamps

DC Supplied

242

2

Indicates whether or not the board is supplying
voltage on the VDD_5V rail and the current rating 000=No 1-0xFFFF is the
current supplied

Available

244

32543

Available space for other non-volatile codes/data

NOTE: The offset of the Part Number field is incorrect in the current version of the BeagleBone SRM (Rev A5.0.1). I corrected it here.

All of the examples and work was developed on the CircuitCo factory BeagleBone image for A6. Everything was tested and ran on both a BeagleBone revision A5 and A3. The image can be downloaded here:

http://circuitco.com/support/index.php?title=BeagleBone#Revision_A6_Image

Bonescript

This section will give instructions on how to make the EEPROM data file using bonescript.

The first thing to do is to make sure you are using the latest bonescript. +Boris has been making many great changes recently. To update your bonescript…

# cd /var/lib/cloud9/
# git pull

I will use some of the bonescript to make the data file that has all of the EEPROM data in it. To help contain everything in one spot, I usually copy the appropriate bonescript file to a local directory.

# cd
# mkdir eeprom
# cd eeprom
# cp /var/lib/cloud9/bonescript/eeprom.js .
# cp /var/lib/cloud9/bonescript/bone.js .

The eeprom.js file contains routines that parse and make the data in the EEPROMs. The bone.js file contains the fundamental BeagleBone hardware definitions. If you run eeprom.js (using node) with a “-h” option it will give you a small explanation of how to use it from the command line…

# node ./eeprom.js -h
Print usage:
  node bonescript/eeprom.js -h

Read eeproms and write the output to a JSON-compatible file:
  node bonescript/eeprom.js [-r [type:source.eeprom ...] destination.json] 
    type               : the word "bone" or "cape"
    source.eeprom      : source eeprom file

Read JSON eeproms file and write the output to eeprom(s):
  node bonescript/eeprom.js -w source.json [[source-eeprom] destination.eeprom]
    source.json        : source JSON file containing one or more eeprom structures
    destination.eeprom : where to write the output,
                         must either match eeprom structure name or
                         provide a source-eeprom parameter
    source-eeprom      : which eeprom structure to use as source

The command line functions all deal with EEPROM data in a JSON file. These functions either read the EEPROM data from the Cape (or BeagleBone itself) or prepares data to be written to the EEPROM. (Know what a JSON file is? See http://en.wikipedia.org/wiki/JSON). These routines also make EEPROM data files for the BeagleBone itself. I will not cover this in this entry. Example of a JSON file for a Cape EEPROM…

# cat data1.json                       
{
 "Display-Cape.eeprom": {
    "type": "cape",
    "header": "aa5533ee",
    "formatRev": "A0",
    "boardName": "Nokia 5110 Display Cape",
    "version": "01",
    "manufacturer": "CabinProgramming",
    "partNumber": "000100-001",
    "numPins": 2,
    "serialNumber": "00000000001",
    "currentVDD_3V3EXP": 40,
    "currentVDD_5V": 0,
    "currentSYS_5V": 0,
    "DCSupplied": 0,
    "mux": {
      "P8_46": {
        "used": "used",
        "direction": "out",
        "pullup": "pulldown",
        "rx": "disabled",
        "mode": 7,
        "function": "gpio2_7"
      },
      "P8_45": {
        "used": "used",
        "direction": "out",
        "pullup": "pullup",
        "slew": "fast",
        "mode": 7,
        "function": "gpio2_6"
      }
    }
  }
}

As you can see, the data is presented in a readable manner and is not that hard to understand. Here is a list of key words and the values that they can have…

  • type: “cape” or “bone”
  • header: “aa5533ee” (Defined in this version of the EEPROM spec)
  • formatRev: “A0″ (Defined in this version of the EEPROM spec)
  • boardName: Your Cape name in ASCII and in quotes
  • version: Your version number in ASCII and in quotes
  • manufacturer: Manufacturer name in ASCII and in quotes
  • partNumber: Your part number in ASCII and in quotes
  • numPins: Number of I/O pins used in your design. Needs to match the number of mux: data entries.
  • serialNumber: Your serial number in ASCII and in quotes. Note: See the suggested format in the SRM and the table above.
  • currentVDD_3V3EXP: Maximum current drawn in milliamps (Range 0 to 250)
  • currentVDD_5V: Maximum current drawn in milliamps (Range 0 to 250)
  • currentSYS_5V: Maximum current drawn in milliamps (Range 0 to 1000)
  • DCSupplied: Amount of current in supplied milliamps on the VDD_5V rail by the Cape. (Range 0 to 65535)
  • used: “used” or “available”
  • direction: “in” or “out” or “bidir”
  • pullup: “disabled” or “pullup” or pulldown”
  • rx: “enabled” or “disabled”
  • slew: “slow” or “fast”
  • mode: Mode mux value (Range 0 to 7)

A good example of a complex multi Cape EEPROM JSON file is in:  /var/lib/cloud9/bonescript/test-eeproms.json on the BeagleBone.

To turn the JSON file into an EEPROM data file you issue the following command (this example has the JSON file named “data1.json” ):

# node ./eeprom.js -w data1.json

This command writes a file to the current directory with the name specified in the JSON file. In my example above, it would be: “Display-Cape.eeprom”.

If you want to check the EEPROM file, use the hex dump command…

# hexdump -C Display-Cape.eeprom 
00000000  aa 55 33 ee 41 30 4e 6f  6b 69 61 20 35 31 31 30  |.U3.A0Nokia 5110|
00000010  20 44 69 73 70 6c 61 79  20 43 61 70 65 00 00 00  | Display Cape...|
00000020  00 00 00 00 00 00 30 31  00 00 43 61 62 69 6e 50  |......01..CabinP|
00000030  72 6f 67 72 61 6d 6d 69  6e 67 30 30 30 31 30 30  |rogramming000100|
00000040  2d 30 30 31 00 00 00 00  00 00 00 02 30 30 30 30  |-001........0000|
00000050  30 30 30 30 30 30 31 00  00 00 00 00 00 00 00 00  |0000001.........|
00000060  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
000000b0  00 00 c0 17 c0 07 00 00  00 00 00 00 00 00 00 00  |................|
000000c0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
000000e0  00 00 00 00 00 00 00 00  00 00 00 00 00 28 00 00  |.............(..|
000000f0  00 00 00 00                                       |....|
000000f4

See the “How to Program the EEPROM” section of this post on how to write this file to the EEPROM on the cape…

Utility Program

As an alternate method to make the EEPROM data, I wrote a simple utility to walk you through inputting the data needed. This is a very simple program and is not very fancy but gets the job done. It uses gets() so be prepared for a warning when you compile the utility.  I do not plan on going through all of the design details of the program but will show you where to get the source, how to compile it and use it.

Source is located here… mkeeprom.c

To compile and use the program do this…

# cd
# mkdir utility
# cd utility
<get file source and put the file in here>
# make mkeeprom
cc     mkeeprom.c   -o mkeeprom
/tmp/cciVqrCD.o: In function `main':
mkeeprom.c:(.text+0xe8): warning: the `gets' function is dangerous and should not be used.
# ./mkeeprom 

---EEPROM MAKER---

This is a program to make the EEPROM data file for a BeagleBone Cape.

This program produces an output file named: data.eeprom
The data file follows EEPROM Format Revision 'A0'
This data file can be put in the BeagleBone EEPROM by this command on a BeagleBone:
   > cat data.eeprom >/sys/bus/i2c/drivers/at24/3-005x/eeprom
         Where:  5x is 54, 55, 56, 57 depending on Cape addressing.
         NOTE:  See blog.azkeller.com for more details.

+++ No warranties or support is implied - sorry for CYA +++

Enter Name of Board in ASCII (max 32): Nokia 5110 Display Cape
Enter HW Version of Board in ASCII (max 4): 01
Enter Name of Manufacturer in ASCII (max 16): CabinProgramming
Enter Part Number in ASCII (max 16): 000100-001
Enter Serial Number in ASCII (max 16): 00000000001
Enter MAX Current (mA) on VDD_3V3EXP Used by Cape (Range 0 to 250mA): 40
Enter MAX Current (mA) on VDD_5V Used by Cape (Range 0 to 1000mA): 0
Enter MAX Current (mA) on SYS_5V Used by Cape (Range 0 to 250mA): 0
Enter Current (mA) Supplied on VDD_5V by Cape (Range 0 to 65535mA): 0

Enter Number of Pins Used by Cape (Range 0 to 74): 2

Get data for pin 1
    PIN # 1 - Enter Connector number (8 or 9): 8
    PIN # 1 - Enter pin number (1 through 46): 46
    PIN # 1 P8_46 - Usage? 1=pin used, 0=unused: 1
    PIN # 1 P8_46 - Type? 1=input, 2=output, 3=bidirectional: 2
    PIN # 1 P8_46 - Slew? 0=fast, 1=slow: 0
    PIN # 1 P8_46 - RX Enabled? 0=disabled, 1=enabled: 0
    PIN # 1 P8_46 - Pullup or Pulldown? 0=pulldown, 1=pullup: 0
    PIN # 1 P8_46 - Pull up-down Enabled? 0=enabled, 1=disabled: 0
    PIN # 1 P8_46 - Pin Mux Mode? (0 through 7): 7

Get data for pin 2
    PIN # 2 - Enter Connector number (8 or 9): 8
    PIN # 2 - Enter pin number (1 through 46): 45
    PIN # 2 P8_45 - Usage? 1=pin used, 0=unused: 1
    PIN # 2 P8_45 - Type? 1=input, 2=output, 3=bidirectional: 2
    PIN # 2 P8_45 - Slew? 0=fast, 1=slow: 0
    PIN # 2 P8_45 - RX Enabled? 0=disabled, 1=enabled: 0
    PIN # 2 P8_45 - Pullup or Pulldown? 0=pulldown, 1=pullup: 1
    PIN # 2 P8_45 - Pull up-down Enabled? 0=enabled, 1=disabled: 0
    PIN # 2 P8_45 - Pin Mux Mode? (0 through 7): 7

Creating output file... ./data.eeprom

Now you have an EEPROM data file…

How to Program the EEPROM

The following section describes how to get the EEPROM data file into your Cape EEPROM. The pin numbering used is the same as on the schematic in the BeagleBone SRM section “8.1  EEPROM“.

BeagleBone Capes should have an address selection on each board. This may be in the form of switches, jumpers or resistors soldered in place. The BeagleBone Cape I am using to do my prototype work on is the FlyingBone Prototype Cape (https://github.com/piranha32/FlyingBone). This uses a set of jumpers to perform the addressing. The addressing becomes important because you need it to find out where to copy the data. Here is a table showing the addressing scheme…

Pin Grounded

Location of EEPROM

NONE

/sys/bus/i2c/drivers/at24/3-0057/eeprom

A0

/sys/bus/i2c/drivers/at24/3-0056/eeprom

A1

/sys/bus/i2c/drivers/at24/3-0055/eeprom

A0 and A1

/sys/bus/i2c/drivers/at24/3-0054/eeprom

Now that you have the location of the EEPROM driver, it is simply a matter of copying the data to the EEPROM using “cat”.

# cat data.eeprom >/sys/bus/i2c/drivers/at24/3-0057/eeprom

And now you are done…

A Little More on EEPROMs

If you want to check what is in the EEPROMs, you can run the eeprom.js and get a listing of all of the  EEPROMs including the BeagleBone’s EEPROM. Note in my example below, that I have one Cape installed with A0 and A1 grounded (/sys/bus/i2c/drivers/at24/3-0054/). All of the other EEPROM locations show “Error: ETIMEDOUT, connection timed out”.

# node ./eeprom.js
Reading EEPROM at /sys/bus/i2c/drivers/at24/1-0050/eeprom
Reading EEPROM at /sys/bus/i2c/drivers/at24/1-0051/eeprom
Unable to open EEPROM at /sys/bus/i2c/drivers/at24/1-0051/eeprom: Error: ETIMEDOUT, connection timed out
Reading EEPROM at /sys/bus/i2c/drivers/at24/3-0054/eeprom
Reading EEPROM at /sys/bus/i2c/drivers/at24/3-0055/eeprom
Unable to open EEPROM at /sys/bus/i2c/drivers/at24/3-0055/eeprom: Error: ETIMEDOUT, connection timed out
Reading EEPROM at /sys/bus/i2c/drivers/at24/3-0056/eeprom
Unable to open EEPROM at /sys/bus/i2c/drivers/at24/3-0056/eeprom: Error: ETIMEDOUT, connection timed out
Reading EEPROM at /sys/bus/i2c/drivers/at24/3-0057/eeprom
Unable to open EEPROM at /sys/bus/i2c/drivers/at24/3-0057/eeprom: Error: ETIMEDOUT, connection timed out
Reading EEPROM at test-bone.eeprom
Unable to open EEPROM at test-bone.eeprom: Error: ENOENT, no such file or directory 'test-bone.eeprom'
Reading EEPROM at test-cape.eeprom
Unable to open EEPROM at test-cape.eeprom: Error: ENOENT, no such file or directory 'test-cape.eeprom'
{
  "/sys/bus/i2c/drivers/at24/1-0050/eeprom": {
    "header": "aa5533ee",
    "boardName": "A335BONE",
    "version": "00A5",
    "serialNumber": "0812BB000028",
    "configOption": "0000000000000000000000000000000000000000000000000000000000000000",
    "type": "bone"
  },
  "/sys/bus/i2c/drivers/at24/3-0054/eeprom": {
    "header": "aa5533ee",
    "formatRev": "A0",
    "boardName": "Nokia 5110 Display Cape",
    "version": "01",
    "manufacturer": "Cabin Programs",
    "partNumber": "CB0100-001",
    "numPins": 9,
    "serialNumber": "00000000001",
    "currentVDD_3V3EXP": 40,
    "currentVDD_5V": 0,
    "currentSYS_5V": 0,
    "DCSupplied": 0,
    "mux": {
      "P8_40": {
        "used": "used",
        "direction": "out",
        "slew": "fast",
        "rx": "disabled",
        "pullup": "pulldown",
        "mode": 7,
        "function": "gpio2_13"
      },
      "P8_41": {
        "used": "used",
        "direction": "out",
        "slew": "fast",
        "rx": "disabled",
        "pullup": "pulldown",
        "mode": 7,
        "function": "gpio2_10"
      },
      "P8_42": {
        "used": "used",
        "direction": "out",
        "slew": "fast",
        "rx": "disabled",
        "pullup": "pulldown",
        "mode": 7,
        "function": "gpio2_11"
      },
      "P8_43": {
        "used": "used",
        "direction": "out",
        "slew": "fast",
        "rx": "disabled",
        "pullup": "pulldown",
        "mode": 7,
        "function": "gpio2_8"
      },
      "P8_44": {
        "used": "used",
        "direction": "out",
        "slew": "fast",
        "rx": "disabled",
        "pullup": "pulldown",
        "mode": 7,
        "function": "gpio2_9"
      },
      "P8_45": {
        "used": "used",
        "direction": "out",
        "slew": "fast",
        "rx": "disabled",
        "pullup": "pulldown",
        "mode": 7,
        "function": "gpio2_6"
      },
      "P8_46": {
        "used": "used",
        "direction": "out",
        "slew": "fast",
        "rx": "disabled",
        "pullup": "pulldown",
        "mode": 7,
        "function": "gpio2_7"
      },
      "P9_30": {
        "used": "used",
        "direction": "out",
        "slew": "fast",
        "rx": "disabled",
        "pullup": "pulldown",
        "mode": 3,
        "function": "spi1_d1"
      },
      "P9_31": {
        "used": "used",
        "direction": "out",
        "slew": "fast",
        "rx": "disabled",
        "pullup": "pulldown",
        "mode": 3,
        "function": "spi1_sclk"
      }
    },
    "type": "cape"
  }
}