├── eepromutils ├── voicehat.eep ├── Makefile ├── README.txt ├── eeptypes.h ├── eeprom_settings.txt ├── eepflash.sh ├── eepdump.c └── eepmake.c ├── Rpi-custom-voice-hat-wiring.fzz ├── Rpi-custom-voice-hat-wiring.png ├── README.md ├── audio-config └── scripts │ ├── asound.conf │ ├── custom-voice-hat.sh │ └── install-i2s.sh └── Instructions.txt /eepromutils/voicehat.eep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shivasiddharth/custom-voice-hat/master/eepromutils/voicehat.eep -------------------------------------------------------------------------------- /Rpi-custom-voice-hat-wiring.fzz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shivasiddharth/custom-voice-hat/master/Rpi-custom-voice-hat-wiring.fzz -------------------------------------------------------------------------------- /Rpi-custom-voice-hat-wiring.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shivasiddharth/custom-voice-hat/master/Rpi-custom-voice-hat-wiring.png -------------------------------------------------------------------------------- /eepromutils/Makefile: -------------------------------------------------------------------------------- 1 | # Current tools are eepmake and eepdump 2 | 3 | CC ?= gcc 4 | 5 | all: eepmake eepdump 6 | 7 | eepmake: eeptypes.h eepmake.c 8 | $(CC) eepmake.c -o eepmake -Wno-format 9 | 10 | eepdump: eeptypes.h eepdump.c 11 | $(CC) eepdump.c -o eepdump -Wno-format 12 | 13 | clean: 14 | rm -f eepmake eepdump 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # custom-voice-hat 2 | Create a custom Voice HAT, much like Google's VOICE HAT 3 | 4 | Use the below video for reference. 5 | 6 | Detailed Youtube Video 9 | -------------------------------------------------------------------------------- /eepromutils/README.txt: -------------------------------------------------------------------------------- 1 | Utilities to create, flash and dump HAT EEPROM images. 2 | 3 | Edit eeprom_setting.txt for your particular board and run through 4 | eepmake tool, then use eepflash tool to write to attached HAT ID EEPROM 5 | 6 | Tools available: 7 | 8 | eepmake: Parses EEPROM text file and creates binary .eep file 9 | 10 | eepdump: Dumps a binary .eep file as human readable text (for debug) 11 | 12 | eepflash: Write or read .eep binary image to/from HAT EEPROM 13 | -------------------------------------------------------------------------------- /audio-config/scripts/asound.conf: -------------------------------------------------------------------------------- 1 | options snd_rpi_googlemihat_soundcard index=0 2 | 3 | pcm.softvol { 4 | type softvol 5 | slave.pcm dmix 6 | control { 7 | name Master 8 | card 0 9 | } 10 | } 11 | 12 | pcm.micboost { 13 | type plug 14 | slave.pcm dsnoop 15 | 16 | } 17 | 18 | pcm.!default { 19 | type asym 20 | playback.pcm "plug:softvol" 21 | capture.pcm "plug:micboost" 22 | } 23 | 24 | ctl.!default { 25 | type hw 26 | card 0 27 | } 28 | -------------------------------------------------------------------------------- /audio-config/scripts/custom-voice-hat.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Configure Raspberry Pi audio for Custom Voice HAT. 4 | 5 | set -o errexit 6 | 7 | if [[ $EUID -ne 0 ]]; then 8 | echo "This script must be run as root (use sudo)" 1>&2 9 | exit 1 10 | fi 11 | 12 | cd "$(dirname "${BASH_SOURCE[0]}")/.." 13 | 14 | asoundrc=/home/pi/.asoundrc 15 | global_asoundrc=/etc/asound.conf 16 | 17 | for rcfile in "$asoundrc" "$global_asoundrc"; do 18 | if [[ -f "$rcfile" ]] ; then 19 | echo "Renaming $rcfile to $rcfile.bak..." 20 | sudo mv "$rcfile" "$rcfile.bak" 21 | fi 22 | done 23 | 24 | sudo cp scripts/asound.conf "$global_asoundrc" 25 | echo "Installing CUSTOM Voice HAT config at $global_asoundrc" 26 | -------------------------------------------------------------------------------- /audio-config/scripts/install-i2s.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Copyright 2017 Google Inc. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | set -o errexit 18 | 19 | if [[ $EUID -ne 0 ]]; then 20 | echo "This script must be run as root (use sudo)" 1>&2 21 | exit 1 22 | fi 23 | 24 | set -e 25 | 26 | sed -i \ 27 | -e "s/^dtparam=audio=on/#\0/" \ 28 | -e "s/^#\(dtparam=i2s=on\)/\1/" \ 29 | /boot/config.txt 30 | grep -q "dtoverlay=i2s-mmap" /boot/config.txt || \ 31 | echo "dtoverlay=i2s-mmap" >> /boot/config.txt 32 | grep -q "dtoverlay=googlevoicehat-soundcard" /boot/config.txt || \ 33 | echo "dtoverlay=googlevoicehat-soundcard" >> /boot/config.txt 34 | grep -q "dtparam=i2s=on" /boot/config.txt || \ 35 | echo "dtparam=i2s=on" >> /boot/config.txt 36 | 37 | -------------------------------------------------------------------------------- /Instructions.txt: -------------------------------------------------------------------------------- 1 | # Parts List 2 | 1. Adafruit I2S MEMS Microphone Breakout - SPH0645LM4H - 2Nos - https://www.adafruit.com/product/3421 3 | 2. Adafruit I2S 3W Class D Amplifier Breakout - MAX98357A - 1No - https://www.adafruit.com/product/3006 4 | 3. Adafruit Perma-Proto HAT for Pi Mini Kit - With EEPROM - 1No - https://www.adafruit.com/product/2314 5 | 6 | # Following steps to be completed on the Pi. 7 | 8 | #Clone the project onto the pi 9 | git clone https://github.com/shivasiddharth/custom-voice-hat 10 | 11 | 1. Turn on i2s support in /boot/config.txt with: 12 | sudo nano /boot/config.txt 13 | 14 | ****Uncomment the following**** 15 | #dtparam=i2s=on 16 | #dtparam=i2c_arm=on 17 | #dtparam=spi=on 18 | #dtparam=audio=on 19 | 20 | ****Add the following new entry**** 21 | dtparam=i2c_vc=on 22 | 23 | Save Exit and Reboot. 24 | 25 | 2. Check for the Hats folder: 26 | cd /proc/device-tree/ 27 | 28 | 3. Open a new terminal and change directory to eepromutils: 29 | cd /home/pi/custom-voice-hat/eepromutils 30 | 31 | 4. Make the EEPROM flasher executable and Flash the EEPROM with the eeprom file: 32 | sudo chmod +x ./eepflash.sh 33 | sudo ./eepflash.sh -w -f=voicehat.eep -t=24c32 34 | 35 | 5. Update raspberry pi kernels: 36 | sudo apt-get update 37 | sudo apt-get install raspberrypi-kernel 38 | 39 | 6. Reboot 40 | sudo reboot 41 | 42 | 7. Check if your HAT is recognized,: 43 | cd /proc/device-tree/ 44 | 45 | ***YOU WILL HAVE A HAT FOLDER NOW WITH VOICE HAT DETAILS*** 46 | 47 | # Shutdown and Wire the Parts as indicated in the attached wiring diagram 48 | ***CAUTION- Amplifier board should be powered using 5V and the Mics using 3.3v*** 49 | 50 | # Start your Pi and move into the audio configuration scripts: 51 | cd /home/pi/custom-voice-hat/audio-config/scripts/ 52 | 53 | #Make the scripts executable: 54 | sudo chmod +x ./custom-voice-hat.sh 55 | sudo chmod +x ./install-i2s.sh 56 | 57 | #Run the Executable scripts 58 | sudo ./custom-voice-hat.sh 59 | sudo ./install-i2s.sh 60 | 61 | ****RUN THE custom-voice-hat.sh script untill you get .bak notification**** 62 | ****REBOOT and Enjoy your HAT**** 63 | -------------------------------------------------------------------------------- /eepromutils/eeptypes.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | /* Atom types */ 4 | #define ATOM_INVALID_TYPE 0x0000 5 | #define ATOM_VENDOR_TYPE 0x0001 6 | #define ATOM_GPIO_TYPE 0x0002 7 | #define ATOM_DT_TYPE 0x0003 8 | #define ATOM_CUSTOM_TYPE 0x0004 9 | #define ATOM_HINVALID_TYPE 0xffff 10 | 11 | #define ATOM_VENDOR_NUM 0x0000 12 | #define ATOM_GPIO_NUM 0x0001 13 | #define ATOM_DT_NUM 0x0002 14 | 15 | //minimal sizes of data structures 16 | #define HEADER_SIZE 12 17 | #define ATOM_SIZE 10 18 | #define VENDOR_SIZE 22 19 | #define GPIO_SIZE 30 20 | #define CRC_SIZE 2 21 | 22 | #define GPIO_MIN 2 23 | #define GPIO_COUNT 28 24 | 25 | #define FORMAT_VERSION 0x01 26 | 27 | #define CRC16 0x8005 28 | 29 | /* EEPROM header structure */ 30 | struct header_t { 31 | uint32_t signature; 32 | unsigned char ver; 33 | unsigned char res; 34 | uint16_t numatoms; 35 | uint32_t eeplen; 36 | }; 37 | 38 | /* Atom structure */ 39 | struct atom_t { 40 | uint16_t type; 41 | uint16_t count; 42 | uint32_t dlen; 43 | char* data; 44 | uint16_t crc16; 45 | }; 46 | 47 | /* Vendor info atom data */ 48 | struct vendor_info_d { 49 | uint32_t serial_1; //least significant 50 | uint32_t serial_2; 51 | uint32_t serial_3; 52 | uint32_t serial_4; //most significant 53 | uint16_t pid; 54 | uint16_t pver; 55 | unsigned char vslen; 56 | unsigned char pslen; 57 | char* vstr; 58 | char* pstr; 59 | }; 60 | 61 | /* GPIO map atom data */ 62 | struct gpio_map_d { 63 | unsigned char flags; 64 | unsigned char power; 65 | unsigned char pins[GPIO_COUNT]; 66 | }; 67 | 68 | 69 | uint16_t getcrc(char* data, unsigned int size) { 70 | 71 | uint16_t out = 0; 72 | int bits_read = 0, bit_flag; 73 | 74 | /* Sanity check: */ 75 | if((data == NULL) || size==0) 76 | return 0; 77 | 78 | while(size > 0) 79 | { 80 | bit_flag = out >> 15; 81 | 82 | /* Get next bit: */ 83 | out <<= 1; 84 | out |= (*data >> bits_read) & 1; // item a) work from the least significant bits 85 | 86 | /* Increment bit counter: */ 87 | bits_read++; 88 | if(bits_read > 7) 89 | { 90 | bits_read = 0; 91 | data++; 92 | size--; 93 | } 94 | 95 | /* Cycle check: */ 96 | if(bit_flag) 97 | out ^= CRC16; 98 | 99 | } 100 | 101 | // item b) "push out" the last 16 bits 102 | int i; 103 | for (i = 0; i < 16; ++i) { 104 | bit_flag = out >> 15; 105 | out <<= 1; 106 | if(bit_flag) 107 | out ^= CRC16; 108 | } 109 | 110 | // item c) reverse the bits 111 | uint16_t crc = 0; 112 | i = 0x8000; 113 | int j = 0x0001; 114 | for (; i != 0; i >>=1, j <<= 1) { 115 | if (i & out) crc |= j; 116 | } 117 | 118 | return crc; 119 | } 120 | -------------------------------------------------------------------------------- /eepromutils/eeprom_settings.txt: -------------------------------------------------------------------------------- 1 | ######################################################################## 2 | # EEPROM settings text file 3 | # 4 | # Edit this file for your particular board and run through eepmake tool, 5 | # then use eepflash tool to write to attached HAT ID EEPROM 6 | # 7 | # Tools available: 8 | # eepmake Parses EEPROM text file and creates binary .eep file 9 | # eepdump Dumps a binary .eep file as human readable text (for debug) 10 | # eepflash Write or read .eep binary image to/from HAT EEPROM 11 | # 12 | ######################################################################## 13 | 14 | ######################################################################## 15 | # Vendor info 16 | 17 | # 128 bit UUID. If left at zero eepmake tool will auto-generate 18 | # RFC 4122 compliant UUID 19 | product_uuid 00000000-0000-0000-0000-000000000000 20 | 21 | # 16 bit product id 22 | product_id 0x0000 23 | 24 | # 16 bit product version 25 | product_ver 0x0000 26 | 27 | # ASCII vendor string (max 255 characters) 28 | vendor "ACME Technology Company" 29 | 30 | # ASCII product string (max 255 characters) 31 | product "Special Sensor Board" 32 | 33 | 34 | ######################################################################## 35 | # GPIO bank settings, set to nonzero to change from the default. 36 | # NOTE these setting can only be set per BANK, uncommenting any of 37 | # these will force the bank to use the custom setting. 38 | 39 | # drive strength, 0=default, 1-8=2,4,6,8,10,12,14,16mA, 9-15=reserved 40 | gpio_drive 0 41 | 42 | # 0=default, 1=slew rate limiting, 2=no slew limiting, 3=reserved 43 | gpio_slew 0 44 | 45 | # 0=default, 1=hysteresis disabled, 2=hysteresis enabled, 3=reserved 46 | gpio_hysteresis 0 47 | 48 | # If board back-powers Pi via 5V GPIO header pins: 49 | # 0 = board does not back-power 50 | # 1 = board back-powers and can supply the Pi with a minimum of 1.3A 51 | # 2 = board back-powers and can supply the Pi with a minimum of 2A 52 | # 3 = reserved 53 | # If back_power=2 then USB high current mode will be automatically 54 | # enabled on the Pi 55 | back_power 0 56 | 57 | ######################################################################## 58 | # GPIO pins, uncomment for GPIOs used on board 59 | # Options for FUNCTION: INPUT, OUTPUT, ALT0-ALT5 60 | # Options for PULL: DEFAULT, UP, DOWN, NONE 61 | # NB GPIO0 and GPIO1 are reserved for ID EEPROM so cannot be set 62 | 63 | # GPIO FUNCTION PULL 64 | # ---- -------- ---- 65 | #setgpio 2 INPUT DEFAULT 66 | #setgpio 3 INPUT DEFAULT 67 | #setgpio 4 INPUT DEFAULT 68 | #setgpio 5 INPUT DEFAULT 69 | #setgpio 6 INPUT DEFAULT 70 | #setgpio 7 INPUT DEFAULT 71 | #setgpio 8 INPUT DEFAULT 72 | #setgpio 9 INPUT DEFAULT 73 | #setgpio 10 INPUT DEFAULT 74 | #setgpio 11 INPUT DEFAULT 75 | #setgpio 12 INPUT DEFAULT 76 | #setgpio 13 INPUT DEFAULT 77 | #setgpio 14 INPUT DEFAULT 78 | #setgpio 15 INPUT DEFAULT 79 | #setgpio 16 INPUT DEFAULT 80 | #setgpio 17 INPUT DEFAULT 81 | #setgpio 18 INPUT DEFAULT 82 | #setgpio 19 INPUT DEFAULT 83 | #setgpio 20 INPUT DEFAULT 84 | #setgpio 21 INPUT DEFAULT 85 | #setgpio 22 INPUT DEFAULT 86 | #setgpio 23 INPUT DEFAULT 87 | #setgpio 24 INPUT DEFAULT 88 | #setgpio 25 INPUT DEFAULT 89 | #setgpio 26 INPUT DEFAULT 90 | #setgpio 27 INPUT DEFAULT 91 | -------------------------------------------------------------------------------- /eepromutils/eepflash.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | MODE="NOT_SET" 4 | FILE="NOT_SET" 5 | TYPE="NOT_SET" 6 | 7 | usage() 8 | { 9 | echo "eepflash: Writes or reads .eep binary image to/from HAT EEPROM on a Raspberry Pi" 10 | echo "" 11 | echo "./eepflash.sh" 12 | echo " -h --help: display this help message" 13 | echo " -r --read: read .eep from the eeprom" 14 | echo " -w --write: write .eep to the eeprom" 15 | echo " -f=file_name --file=file_name: binary .eep file to read to/from" 16 | echo " -t=eeprom_type --type=eeprom_type: eeprom type to use" 17 | echo " We support the following eeprom types:" 18 | echo " -24c32" 19 | echo " -24c64" 20 | echo " -24c128" 21 | echo " -24c256" 22 | echo " -24c512" 23 | echo " -24c1024" 24 | echo "" 25 | } 26 | 27 | if [ "$(id -u)" != "0" ]; then 28 | echo "This script must be run as root" 1>&2 29 | exit 1 30 | fi 31 | 32 | while [ "$1" != "" ]; do 33 | PARAM=`echo $1 | awk -F= '{print $1}'` 34 | VALUE=`echo $1 | awk -F= '{print $2}'` 35 | case $PARAM in 36 | -h | --help) 37 | usage 38 | exit 39 | ;; 40 | -r | --read) 41 | MODE="read" 42 | ;; 43 | -w | --write) 44 | MODE="write" 45 | ;; 46 | -t | --type) 47 | if [ "$VALUE" = "24c32" ] || [ "$VALUE" = "24c64" ] || [ "$VALUE" = "24c128" ] || 48 | [ "$VALUE" = "24c256" ] || [ "$VALUE" = "24c512" ] || [ "$VALUE" = "24c1024" ]; then 49 | TYPE=$VALUE 50 | else 51 | echo "ERROR: Unrecognised eeprom type. Try -h for help" 52 | exit 1 53 | fi 54 | ;; 55 | -f | --file) 56 | FILE=$VALUE 57 | ;; 58 | *) 59 | echo "ERROR: unknown parameter \"$PARAM\"" 60 | usage 61 | exit 1 62 | ;; 63 | esac 64 | shift 65 | done 66 | 67 | if [ "$MODE" = "NOT_SET" ]; then 68 | echo "You need to set mode (read or write). Try -h for help." 69 | exit 1 70 | elif [ "$FILE" = "NOT_SET" ]; then 71 | echo "You need to set binary .eep file to read to/from. Try -h for help." 72 | exit 1 73 | elif [ "$TYPE" = "NOT_SET" ]; then 74 | echo "You need to set eeprom type. Try -h for help." 75 | exit 1 76 | fi 77 | 78 | echo "This will attempt to talk to an eeprom at i2c address 0x50. Make sure there is an eeprom at this address." 79 | echo "This script comes with ABSOLUTELY no warranty. Continue only if you know what you are doing." 80 | 81 | while true; do 82 | read -p "Do you wish to continue? (yes/no): " yn 83 | case $yn in 84 | yes | Yes ) break;; 85 | no | No ) exit;; 86 | * ) echo "Please type yes or no.";; 87 | esac 88 | done 89 | 90 | modprobe i2c_dev 91 | if [ -e "/dev/i2c-0" ]; then 92 | BUS=0 93 | elif [ -e "/dev/i2c-3" ]; then 94 | BUS=3 95 | else 96 | dtoverlay i2c-gpio i2c_gpio_sda=0 i2c_gpio_scl=1 97 | rc=$? 98 | if [ $rc != 0 ]; then 99 | echo "Loading of i2c-gpio dtoverlay failed. Do an rpi-update (and maybe apt-get update; apt-get upgrade)." 100 | exit $rc 101 | fi 102 | if [ -e "/dev/i2c-3" ]; then 103 | BUS=3 104 | else 105 | echo "Expected I2C bus (i2c-3) not found." 106 | fi 107 | fi 108 | modprobe at24 109 | rc=$? 110 | if [ $rc != 0 ]; then 111 | echo "Modprobe of at24 failed. Do an rpi-update." 112 | exit $rc 113 | fi 114 | 115 | SYS=/sys/class/i2c-adapter/i2c-$BUS 116 | 117 | if [ ! -d "$SYS/$BUS-0050" ]; then 118 | echo "$TYPE 0x50" > $SYS/new_device 119 | fi 120 | 121 | 122 | if [ "$MODE" = "write" ] 123 | then 124 | echo "Writing..." 125 | dd if=$FILE of=$SYS/$BUS-0050/eeprom 126 | rc=$? 127 | elif [ "$MODE" = "read" ] 128 | then 129 | echo "Reading..." 130 | dd if=$SYS/$BUS-0050/eeprom of=$FILE 131 | rc=$? 132 | fi 133 | 134 | if [ $rc != 0 ]; then 135 | echo "Error doing I/O operation." 136 | exit $rc 137 | else 138 | echo "Done." 139 | fi 140 | -------------------------------------------------------------------------------- /eepromutils/eepdump.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "eeptypes.h" 8 | 9 | struct header_t header; 10 | struct atom_t atom; 11 | struct vendor_info_d vinf; 12 | struct gpio_map_d gpiomap; 13 | unsigned char* data; 14 | 15 | int read_bin(char *in, char *outf) { 16 | 17 | uint16_t crc; 18 | FILE *fp, *out; 19 | int i,j; 20 | 21 | fp=fopen(in, "r"); 22 | if (!fp) { 23 | printf("Error reading file %s\n", in); 24 | return -1; 25 | } 26 | 27 | out=fopen(outf, "w"); 28 | if (!out) { 29 | printf("Error writing file %s\n", outf); 30 | return -1; 31 | } 32 | 33 | if (!fread(&header, sizeof(header), 1, fp)) goto err; 34 | 35 | fprintf(out, "# ---------- Dump generated by eepdump handling format version 0x%02x ----------\n#\n", FORMAT_VERSION); 36 | 37 | if (FORMAT_VERSION!=header.ver) fprintf(out, "# WARNING: format version mismatch!!!\n"); 38 | 39 | fprintf(out, "# --Header--\n# signature=0x%08x\n# version=0x%02x\n# reserved=%u\n# numatoms=%u\n# eeplen=%u\n# ----------\n\n\n", header.signature, header.ver, header.res, header.numatoms, header.eeplen); 40 | 41 | 42 | for (i = 0; i>16, vinf.serial_3 & 0xffff, vinf.serial_2>>16, vinf.serial_2 & 0xffff, vinf.serial_1); 69 | fprintf(out, "product_id 0x%04x\n", vinf.pid); 70 | fprintf(out, "product_ver 0x%04x\n", vinf.pver); 71 | 72 | vinf.vstr = (char *) malloc(vinf.vslen+1); 73 | vinf.pstr = (char *) malloc(vinf.pslen+1); 74 | 75 | if (!fread(vinf.vstr, vinf.vslen, 1, fp)) goto err; 76 | if (!fread(vinf.pstr, vinf.pslen, 1, fp)) goto err; 77 | //close strings 78 | vinf.vstr[vinf.vslen] = 0; 79 | vinf.pstr[vinf.pslen] = 0; 80 | 81 | fprintf(out, "vendor \"%s\" # length=%u\n", vinf.vstr, vinf.vslen); 82 | fprintf(out, "product \"%s\" # length=%u\n", vinf.pstr, vinf.pslen); 83 | 84 | if (!fread(&crc, CRC_SIZE, 1, fp)) goto err; 85 | 86 | } else if (atom.type==ATOM_GPIO_TYPE) { 87 | //decode GPIO map 88 | if (!fread(&gpiomap, GPIO_SIZE, 1, fp)) goto err; 89 | 90 | fprintf(out, "# GPIO map info\n"); 91 | fprintf(out, "gpio_drive %d\n", gpiomap.flags & 15); //1111 92 | fprintf(out, "gpio_slew %d\n", (gpiomap.flags & 48)>>4); //110000 93 | fprintf(out, "gpio_hysteresis %d\n", (gpiomap.flags & 192)>>6); //11000000 94 | fprintf(out, "back_power %d\n", gpiomap.power); 95 | fprintf(out, "# GPIO FUNCTION PULL\n# ---- -------- ----\n"); 96 | 97 | for (j = 0; j<28; j++) { 98 | if (gpiomap.pins[j] & (1<<7)) { 99 | //board uses this pin 100 | 101 | char *pull_str = "INVALID"; 102 | switch ((gpiomap.pins[j] & 96)>>5) { //1100000 103 | case 0: pull_str = "DEFAULT"; 104 | break; 105 | case 1: pull_str = "UP"; 106 | break; 107 | case 2: pull_str = "DOWN"; 108 | break; 109 | case 3: pull_str = "NONE"; 110 | break; 111 | } 112 | 113 | char *func_str = "INVALID"; 114 | switch ((gpiomap.pins[j] & 7)) { //111 115 | case 0: func_str = "INPUT"; 116 | break; 117 | case 1: func_str = "OUTPUT"; 118 | break; 119 | case 4: func_str = "ALT0"; 120 | break; 121 | case 5: func_str = "ALT1"; 122 | break; 123 | case 6: func_str = "ALT2"; 124 | break; 125 | case 7: func_str = "ALT3"; 126 | break; 127 | case 3: func_str = "ALT4"; 128 | break; 129 | case 2: func_str = "ALT5"; 130 | break; 131 | } 132 | 133 | fprintf(out, "setgpio %d %s %s\n", j, func_str, pull_str); 134 | } 135 | } 136 | 137 | if (!fread(&crc, CRC_SIZE, 1, fp)) goto err; 138 | 139 | } else if (atom.type==ATOM_DT_TYPE) { 140 | //decode DT blob 141 | 142 | fprintf(out, "dt_blob"); 143 | data = (char *) malloc(atom.dlen-CRC_SIZE); 144 | if (!fread(data, atom.dlen-CRC_SIZE, 1, fp)) goto err; 145 | 146 | for (j = 0; j 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "eeptypes.h" 14 | 15 | #define HEADER_SIGN 0x69502d52 //"R-Pi" in ASCII reversed for endianness 16 | 17 | //todo: larger initial mallocs 18 | 19 | struct header_t header; 20 | struct atom_t *custom_atom, vinf_atom, gpio_atom, dt_atom; 21 | struct vendor_info_d* vinf; 22 | struct gpio_map_d* gpiomap; 23 | 24 | bool product_serial_set, product_id_set, product_ver_set, vendor_set, product_set, 25 | gpio_drive_set, gpio_slew_set, gpio_hysteresis_set, gpio_power_set; 26 | 27 | bool data_receive, has_dt, receive_dt; 28 | 29 | char **data; 30 | char *current_atom; //rearranged to write out 31 | unsigned int data_len, custom_ct, total_size, data_cap, custom_cap; 32 | 33 | 34 | int write_binary(char* out) { 35 | FILE *fp; 36 | int i, offset; 37 | short crc; 38 | 39 | fp=fopen(out, "wb"); 40 | if (!fp) { 41 | printf("Error writing file %s\n", out); 42 | return -1; 43 | } 44 | 45 | fwrite(&header, sizeof(header), 1, fp); 46 | 47 | 48 | current_atom = (char *) malloc(vinf_atom.dlen+ATOM_SIZE-CRC_SIZE); 49 | offset = 0; 50 | //vendor information atom first part 51 | memcpy(current_atom, &vinf_atom, ATOM_SIZE-CRC_SIZE); 52 | offset += ATOM_SIZE-2; 53 | //data first part 54 | memcpy(current_atom+offset, vinf_atom.data, VENDOR_SIZE); 55 | offset += VENDOR_SIZE; 56 | //data strings 57 | memcpy(current_atom+offset, vinf->vstr, vinf->vslen); 58 | offset += vinf->vslen; 59 | memcpy(current_atom+offset, vinf->pstr, vinf->pslen); 60 | offset += vinf->pslen; 61 | //vinf last part 62 | crc = getcrc(current_atom, offset); 63 | memcpy(current_atom+offset, &crc, CRC_SIZE); 64 | offset += CRC_SIZE; 65 | 66 | fwrite(current_atom, offset, 1, fp); 67 | free(current_atom); 68 | 69 | current_atom = (char *) malloc(gpio_atom.dlen+ATOM_SIZE-CRC_SIZE); 70 | offset = 0; 71 | //GPIO map first part 72 | memcpy(current_atom, &gpio_atom, ATOM_SIZE-CRC_SIZE); 73 | offset += ATOM_SIZE-CRC_SIZE; 74 | //GPIO data 75 | memcpy(current_atom+offset, gpiomap, GPIO_SIZE); 76 | offset += GPIO_SIZE; 77 | //GPIO map last part 78 | crc = getcrc(current_atom, offset); 79 | memcpy(current_atom+offset, &crc, CRC_SIZE); 80 | offset += CRC_SIZE; 81 | 82 | fwrite(current_atom, offset, 1, fp); 83 | free(current_atom); 84 | 85 | if (has_dt) { 86 | printf("Writing out DT...\n"); 87 | current_atom = (char *) malloc(dt_atom.dlen+ATOM_SIZE-CRC_SIZE); 88 | offset = 0; 89 | 90 | memcpy(current_atom, &dt_atom, ATOM_SIZE-CRC_SIZE); 91 | offset += ATOM_SIZE-CRC_SIZE; 92 | 93 | memcpy(current_atom+offset, dt_atom.data, dt_atom.dlen-CRC_SIZE); 94 | offset += dt_atom.dlen-CRC_SIZE; 95 | 96 | crc = getcrc(current_atom, offset); 97 | memcpy(current_atom+offset, &crc, CRC_SIZE); 98 | offset += CRC_SIZE; 99 | 100 | fwrite(current_atom, offset, 1, fp); 101 | free(current_atom); 102 | } 103 | 104 | for (i = 0; iserial_4, 203 | &high1, &vinf->serial_3, &high2, &vinf->serial_2, &vinf->serial_1); 204 | 205 | vinf->serial_3 |= high1<<16; 206 | vinf->serial_2 |= high2<<16; 207 | 208 | if ((vinf->serial_4==0) && (vinf->serial_3==0) && (vinf->serial_2==0) && (vinf->serial_1==0)) { 209 | //read 128 random bits from /dev/urandom 210 | int random_file = open("/dev/urandom", O_RDONLY); 211 | ssize_t result = read(random_file, &vinf->serial_1, 16); 212 | close(random_file); 213 | if (result <= 0) printf("Unable to read from /dev/urandom to set up UUID"); 214 | else { 215 | //put in the version 216 | vinf->serial_3 = (vinf->serial_3 & 0xffff0fff) | 0x00004000; 217 | 218 | //put in the variant 219 | vinf->serial_2 = (vinf->serial_2 & 0x3fffffff) | 0x80000000; 220 | 221 | printf("UUID=%08x-%04x-%04x-%04x-%04x%08x\n", vinf->serial_4, vinf->serial_3>>16, vinf->serial_3 & 0xffff, vinf->serial_2>>16, vinf->serial_2 & 0xffff, vinf->serial_1); 222 | } 223 | 224 | } 225 | 226 | } else if (strcmp(cmd, "product_id")==0) { 227 | product_id_set = true; //required field 228 | sscanf(c, "%100s %hx", cmd, &vinf->pid); 229 | 230 | } else if (strcmp(cmd, "product_ver")==0) { 231 | product_ver_set = true; //required field 232 | sscanf(c, "%100s %hx", cmd, &vinf->pver); 233 | 234 | } else if (strcmp(cmd, "vendor")==0) { 235 | vendor_set = true; //required field 236 | 237 | vinf->vstr = (char*) malloc (256); 238 | sscanf(c, "%100s \"%255[^\"]\"", cmd, vinf->vstr); 239 | 240 | total_size-=vinf->vslen; 241 | vinf_atom.dlen-=vinf->vslen; 242 | 243 | vinf->vslen = strlen(vinf->vstr); 244 | 245 | total_size+=vinf->vslen; 246 | vinf_atom.dlen+=vinf->vslen; 247 | 248 | } else if (strcmp(cmd, "product")==0) { 249 | product_set = true; //required field 250 | 251 | vinf->pstr = (char*) malloc (256); 252 | sscanf(c, "%100s \"%255[^\"]\"", cmd, vinf->pstr); 253 | 254 | total_size-=vinf->pslen; 255 | vinf_atom.dlen-=vinf->pslen; 256 | 257 | vinf->pslen = strlen(vinf->pstr); 258 | 259 | total_size+=vinf->pslen; 260 | vinf_atom.dlen+=vinf->pslen; 261 | } 262 | 263 | /* GPIO map related part */ 264 | else if (strcmp(cmd, "gpio_drive")==0) { 265 | gpio_drive_set = true; //required field 266 | 267 | sscanf(c, "%100s %1x", cmd, &val); 268 | if (val>8 || val<0) printf("Warning: gpio_drive property in invalid region, using default value instead\n"); 269 | else gpiomap->flags |= val; 270 | 271 | 272 | } else if (strcmp(cmd, "gpio_slew")==0) { 273 | gpio_slew_set = true; //required field 274 | 275 | sscanf(c, "%100s %1x", cmd, &val); 276 | 277 | if (val>2 || val<0) printf("Warning: gpio_slew property in invalid region, using default value instead\n"); 278 | else gpiomap->flags |= val<<4; 279 | 280 | } else if (strcmp(cmd, "gpio_hysteresis")==0) { 281 | gpio_hysteresis_set = true; //required field 282 | 283 | sscanf(c, "%100s %1x", cmd, &val); 284 | 285 | if (val>2 || val<0) printf("Warning: gpio_hysteresis property in invalid region, using default value instead\n"); 286 | else gpiomap->flags |= val<<6; 287 | 288 | } else if (strcmp(cmd, "back_power")==0) { 289 | gpio_power_set = true; //required field 290 | 291 | sscanf(c, "%100s %1x", cmd, &val); 292 | 293 | if (val>2 || val<0) printf("Warning: back_power property in invalid region, using default value instead\n"); 294 | else gpiomap->power = val; 295 | 296 | } else if (strcmp(cmd, "setgpio")==0) { 297 | fn = (char*) malloc (101); 298 | pull = (char*) malloc (101); 299 | 300 | sscanf(c, "%100s %d %100s %100s", cmd, &val, fn, pull); 301 | 302 | if (val=GPIO_COUNT) printf("Error: GPIO number out of bounds\n"); 303 | else { 304 | valid = true; 305 | pin = 0; 306 | 307 | if (strcmp(fn, "INPUT")==0) { 308 | //no action 309 | } else if (strcmp(fn, "OUTPUT")==0) { 310 | pin |= 1; 311 | } else if (strcmp(fn, "ALT0")==0) { 312 | pin |= 4; 313 | } else if (strcmp(fn, "ALT1")==0) { 314 | pin |= 5; 315 | } else if (strcmp(fn, "ALT2")==0) { 316 | pin |= 6; 317 | } else if (strcmp(fn, "ALT3")==0) { 318 | pin |= 7; 319 | } else if (strcmp(fn, "ALT4")==0) { 320 | pin |= 3; 321 | } else if (strcmp(fn, "ALT5")==0) { 322 | pin |= 2; 323 | } else { 324 | printf("Error at setgpio: function type not recognised\n"); 325 | valid=false; 326 | } 327 | 328 | if (strcmp(pull, "DEFAULT")==0) { 329 | //no action 330 | } else if (strcmp(pull, "UP")==0) { 331 | pin |= 1<<5; 332 | } else if (strcmp(pull, "DOWN")==0) { 333 | pin |= 2<<5; 334 | } else if (strcmp(pull, "NONE")==0) { 335 | pin |= 3<<5; 336 | } else { 337 | printf("Error at setgpio: pull type not recognised\n"); 338 | valid=false; 339 | } 340 | 341 | pin |= 1<<7; //board uses this pin 342 | 343 | if (valid) gpiomap->pins[val] = pin; 344 | } 345 | } 346 | 347 | /* DT atom related part */ 348 | else if (strcmp(cmd, "dt_blob")==0) { 349 | finish_data(); 350 | 351 | has_dt = true; 352 | c+=strlen("dt_blob"); 353 | 354 | receive_dt=true; 355 | data_receive=true; 356 | 357 | data_len = 0; 358 | data_cap = 4; 359 | data = &dt_atom.data; 360 | *data = (char *) malloc(data_cap); 361 | 362 | parse_data(c); 363 | continue_data = true; 364 | 365 | } 366 | 367 | /* Custom data related part */ 368 | else if (strcmp(cmd, "custom_data")==0) { 369 | finish_data(); 370 | 371 | c+=strlen("custom_data"); 372 | 373 | if (custom_cap == custom_ct) { 374 | custom_cap *= 2; 375 | custom_atom = (struct atom_t*) realloc(custom_atom, custom_cap * sizeof(struct atom_t)); 376 | } 377 | 378 | receive_dt=false; 379 | data_receive=true; 380 | 381 | data_len = 0; 382 | data_cap = 4; 383 | data = &custom_atom[custom_ct].data; 384 | *data = (char *) malloc(data_cap); 385 | 386 | parse_data(c); 387 | continue_data = true; 388 | 389 | } else if (strcmp(cmd, "end") ==0) { 390 | //close last data atom 391 | continue_data=false; 392 | } 393 | /* Incoming data */ 394 | else if (data_receive) { 395 | parse_data(c); 396 | continue_data = true; 397 | } 398 | 399 | 400 | if (!continue_data) finish_data(); 401 | 402 | } 403 | 404 | int read_text(char* in) { 405 | FILE * fp; 406 | char * line = NULL; 407 | char * c = NULL; 408 | size_t len = 0; 409 | ssize_t read; 410 | char *comment = NULL; 411 | int atomct = 2; 412 | int linect = 0; 413 | char * command = (char*) malloc (101); 414 | int i; 415 | 416 | has_dt = false; 417 | 418 | printf("Opening file %s for read\n", in); 419 | 420 | fp = fopen(in, "r"); 421 | if (fp == NULL) { 422 | printf("Error opening input file\n"); 423 | return -1; 424 | } 425 | 426 | //allocating memory and setting up required atoms 427 | custom_cap = 1; 428 | custom_atom = (struct atom_t*) malloc(sizeof(struct atom_t) * custom_cap); 429 | 430 | total_size=ATOM_SIZE*2+HEADER_SIZE+VENDOR_SIZE+GPIO_SIZE; 431 | 432 | vinf_atom.type = ATOM_VENDOR_TYPE; 433 | vinf_atom.count = ATOM_VENDOR_NUM; 434 | vinf = (struct vendor_info_d *) malloc(sizeof(struct vendor_info_d)); 435 | vinf_atom.data = (char *)vinf; 436 | vinf_atom.dlen = VENDOR_SIZE + CRC_SIZE; 437 | 438 | gpio_atom.type = ATOM_GPIO_TYPE; 439 | gpio_atom.count = ATOM_GPIO_NUM; 440 | gpiomap = (struct gpio_map_d *) malloc(sizeof(struct gpio_map_d)); 441 | gpio_atom.data = (char *)gpiomap; 442 | gpio_atom.dlen = GPIO_SIZE + CRC_SIZE; 443 | 444 | while ((read = getline(&line, &len, fp)) != -1) { 445 | linect++; 446 | c = line; 447 | 448 | for (i=0; i3) { 577 | if (strcmp(argv[3], "-c")==0) { 578 | custom_o=4; 579 | } else { 580 | //DT file specified 581 | if (dt_atom.dlen) total_size-=(ATOM_SIZE +dt_atom.dlen - CRC_SIZE); 582 | ret = read_dt(argv[3]); 583 | if (ret) { 584 | printf("Error reading DT file, aborting\n"); 585 | return 0; 586 | } 587 | } 588 | } 589 | 590 | if (argc>4 && strcmp(argv[4], "-c")==0) custom_o = 5; 591 | 592 | if (custom_o) 593 | for (i = custom_o; i