geschätzte Lesezeit: 35 min

Table of Contents

1 Intro

The D-Link DNS-320L is a simple 2-bay SATA NAS device which can run multiple versions of Linux. This post provides information on how to run the newest (at the point of writing) mainline kernel on it without patching as well as how to build a simple initramfs using buildroot. This is a bit different than other projects building Linux which usually focus on using debian packages.

2 Hardware

There exist at least four versions of the hardware, I own two of them and the ones that I own can be distinguished by using the following features:

Version P/N on bottom label HW version on bottom label U-Boot banner Other distinguishing marks Notes
DNS-320L A1         Referenced here
DNS-320L A2         Referenced here
DNS-320L A3 ENS320LM…..A3E H/W Ver.: A3 U-Boot 1.1.4 (Aug 22 2012 - 17:06:54) Marvell version: 3.6.0.DNS-320L.01 Text "TWO BAY NETWORK ATTACHED STORAGE" on top plastic cover Only 100 Mbit link working?
DNS-320L A4 ENS320LM…..A4E H/W Ver.: A4 U-Boot 1.1.4 (Apr 17 2014 - 17:31:34) Marvell version: 3.6.0.DNS-320L.01    

In the A3 version I have only the 100 Mbit Ethernet link is working contrary to the NAS being advertised as supporting 1000 Mbps Ethernet. This is broken both in the stock firmware and in the kernel that I have built so I suspect it's some hardware problem. Is this a bug on this board revision or is my particular NAS damaged I do not know.

There are differences between the A3 and A4 versions in the type of memory chips and used Ethernet transformer. Other than that I have not noticed any differences.

The system is based on a Marvell Kirkwood 1GHz SOC, other chips include an Ethernet controller, flash, RAM as well as an MCU which is a systems management controller similar to what you would have in a laptop or PC. The NAS has it's page on both deviwiki and new wikidevi where detailed information about the chips on the board can be found. The main board looks like this:

thumb-board1.jpg

Number Label Chip type on A3 board Chip type on A4 board Description
1 M1 Fan: AVC DS04010B12U   The fan connector
2 JP1     The serial console
3 U2 Hynix H27U1G8F2BTR   NAND flash
4 U3 Marvell 88E1318R   Ethernet chip
5       Testpads (likely JTAG)
6 U7 Marvell 88F6702   The CPU
7 U10,U11 Nanya NT5TU128M8GE-AC Hynix H5PS1G83KFR RAM chips
8 U14 Weltrend WT69P3   The system management MCU
9 JP2     WT69P3 programming bus?
10 T1 MNC G2436CG MNC G2497CG Ethernet transformer (datasheet)
         

In order to access the unpopulated pads on the board I soldered two pin connectors, because the JP2 pitch was smaller than the standard 2.54mm pinheads I had I used some wire to route it to where it can be accessed:

thumb-connectors.jpg

The board contains four LEDs with the following purpose:

Label Color Description
D6 Blue Power-on and booting indicator LED
D7 Red "USB" LED
D8 Red SATA1 port activity LED
D9 Red SATA2 port activity LED

2.0.1 Weltrend WT69P3

The WT69P3 is a systems management MCU which controls the power to the board, provides an RTC for time keeping and controls the fan. The MCU is connected via a RS232 link with the main CPU and in the original firmare a daemon in the system communicates with it. The communications protocol has been reverse-engineered and there exist at least two independent open-source implementations of the manamgement daemon - https://www.aboehler.at/hg/dns320l-daemon and https://github.com/martignlo/DNS-320L.

2.0.2 JP2 connector

This connector seems to terminate an I2C bus. Both lines have pullups to +3.3 V and the Weltrend chip's ISP protocol is I2C according to information from the manufacturer. Unfortunately the programmer software published uses a custom WLINK-I2C adapter with a WT6563 microcontroller. Unfortunately I was not able to find any place selling this programmer online. The assumption was that this is the ISP interface for the Weltrend WT69P3 MCU.

When I connected a Saleae logic analyzer to the bus I noticed something interesting however. When the system boots it tries to initiate a write to address 0x40.

[WT69P3-boot.png]

In order to investigate this I used an Arduino board and the builtin Wire library to make an I2C slave respond to the address where the MCU is writing. The code for the sketch used is below:

#include <Wire.h>
void setup()
{  Wire.begin(0x20);  Wire.onReceive(receiveCmd);  Wire.onRequest(sendResponse);  Serial.begin(9600);
}
void loop()
{  delay(100);
}
void receiveCmd(int howMany)
{  while(Wire.available()>0) // loop through all but the last  {    char c = Wire.read(); // receive byte as a character    Serial.print("CMD: ");    Serial.println(c, HEX);         // print the character  }
}
void sendResponse() {  Wire.write(0xAA);
}

The wiring is documented in the Wire library reference page. Now things have started to look more interesting:

[WT69P3-i2c-write.png]

When we decode the traffic we see that the MCU is first writing byte 0x00 to address 0x40 and then reading a byte from address 0x41:

Time [s], Analyzer Name, Decoded Protocol Result
4.226217250000000,I2C,Setup Write to [0x40] + ACK
4.228212250000000,I2C,0x00 + ACK
4.233196500000000,I2C,Setup Read to [0x41] + ACK
4.235190500000000,I2C,0xAA + NAK

As I was expecting the second read to be some kind of status register I tried to send all 256 different responses to see what would happen. Some bytes sent back have triggered interesting behaviour:

Response byte Boot message LEDs
0x01 Module 0 is TDM  
0x02 Module 0 is AUDIO D7 and D9 LEDs light up
0x03 Module 0 is RGMII  
0x04 Module 0 is GMII  
0x05 Module 0 is TS  
0x06 Module 0 is MII  
0x07 Module 0 is TDM  
0x09 Error!, MV88F6282 doesn't support LCD module when booting from NAND!  

Later I noticed an "iprobe" command in the U-Boot help. It detected 3 addresses on the bus:

Marvell>> iprobe
Valid chip addresses: 13 20 64

This bus layout seems to be confirmed when we scan the bus with buspirate:

I2C>(1)
Searching I2C address space. Found devices at:
0x02(0x01 W) 0x26(0x13 W) 0x27(0x13 R) 0x40(0x20 W) 0x41(0x20 R) 0xC9(0x64 R) 

What is interesting apart from the 0x13, 0x20 (our fake Arduino slave) and 0x64 we also get write address 0x02. Interestingly, when the device is not booting (when LED6 is not blinking) the 0x64 is not present:

I2C>(1)
Searching I2C address space. Found devices at:
0x02(0x01 W) 0x26(0x13 W) 0x27(0x13 R) 0x40(0x20 W) 0x41(0x20 R) 

The "Boot message" documents additional messages that are printed by U-Boot when we respond to a read from 0x20 with a particular byte, for example for response 0x04:

 ** MARVELL BOARD: DB-88F6702A-BP LE 
U-Boot 1.1.4 (Aug 22 2012 - 17:06:54) Marvell version: 3.6.0.DNS-320L.01
U-Boot code: 00600000 -> 0067FFF0  BSS: -> 006CFB00
Soc: 88F6702 A1 CPU running @ 1000Mhz L2 running @ 500Mhz
SysClock = 400Mhz , TClock = 166Mhz 
DRAM (DDR2) CAS Latency = 5 tRP = 5 tRAS = 18 tRCD=6
DRAM CS[0] base 0x00000000   size 256MB DRAM Total size 256MB  16bit width
Addresses 8M - 0M are saved for the U-Boot usage.
Mem malloc Initialization (8M - 7M): Done
NAND:128 MB
Flash:  0 kB
CPU : Marvell Feroceon (Rev 1)
Streaming disabled Write allocate disabled
Module 0 is GMII
USB 0: host mode

It looks like the 0x20 address is some kind of peripheral type detection mechanism and the u-boot code is shared between different devices. I have found references to the "Boot message" strings from the table in various places on the internet, for example here, here and here always in connection to some hardware using the Kirkwood chipset.

3 Bootloader

The board uses the U-Boot bootloader like other Kirkwood-based devices. The boot process can be escaped in the usual way for U-Boot - by pressing SPACE and then 1. When we do this we are greeted with the U-Boot prompt and can poke around various commands. Below you can see exploration results of the information seen by the bootloader:

Marvell>> printenv
bootargs=root=/dev/ram console=ttyS0,115200 :::DB88FXX81:egiga0:none
baudrate=115200
loads_echo=0
ipaddr=2.66.66.201
serverip=2.66.66.32
rootpath=/srv/ubuntu
netmask=255.255.255.0
run_diag=yes
console=console=ttyS0,115200 mtdparts=nand_mtd:0xc0000@0(uboot)ro,0x7f00000@0x100000(root)
MALLOC_len=1
ethprime=egiga0
bootargs_root=root=/dev/nfs rw
bootargs_end=:::DB88FXX81:eth0:none
image_name=uImage
standalone=fsload 0x2000000 $(image_name);setenv bootargs $(console) root=/dev/mtdblock0 rw ip=$(ipaddr):$(serverip)$(bootargs_end) $(mvPhoneConfig); bootm 0x2000000;
ethaddr=00:50:43:00:02:02
ethmtu=1500
mvPhoneConfig=mv_phone_config=dev[0]:fxs,dev[1]:fxo
mvNetConfig=mv_net_config=(00:11:88:0f:62:81,0:1:2:3),mtu=1500
usb0Mode=host
yuk_ethaddr=00:00:00:EE:51:81
nandEcc=1bit
netretry=no
rcvrip=169.254.100.100
loadaddr=0x02000000
autoload=no
image_multi=yes
ethact=egiga0
bootcmd=nand read.e 0xa00000 0x100000 0x300000;nand read.e 0xf00000 0x600000 0x300000;bootm 0xa00000 0xf00000
stdin=serial
stdout=serial
stderr=serial
mainlineLinux=no
enaMonExt=no
enaCpuStream=no
enaWrAllo=no
pexMode=RC
disL2Cache=no
setL2CacheWT=yes
disL2Prefetch=yes
enaICPref=yes
enaDCPref=yes
sata_dma_mode=yes
netbsd_en=no
vxworks_en=no
bootdelay=1
disaMvPnp=no
enaAutoRecovery=yes
pcieTune=no
Marvell>> version
U-Boot 1.1.4 (Aug 22 2012 - 17:06:54) Marvell version: 3.6.0.DNS-320L.01
Marvell>> usb start
(Re)start USB...
USB:   scanning bus for devices... 2 USB Device(s) found
Waiting for storage device(s) to settle before scanning...
1 Storage Device(s) found
Marvell>> usb tree
Device Tree:  1  Hub (480MBit/s, 0mA)  |  Marvell EHCI   |  +-2  Mass Storage (480MBit/s, 200mA)       Kingston DT 100 G2 XXXXXXXXXXXXXXXXXXXX     Marvell>> usb storage  Device 0: Vendor: Kingston Prod.: DT 100 G2        Rev: PMAP            Type: Removable Hard Disk            Capacity: 3736.9 MB = 3.6 GB (7653312 x 512)
Marvell>> Temp
Unknown command 'Temp' - try 'help'
Marvell>> temp
Tj temp is 0
Marvell>> sp
Bus: 0 Device: 0 Func: 0 Vendor ID: 11ab Device ID: 6702
-------------------------------------------------------------------
Class: Memory controller
PCI_BAR0 (Mem-64bit) base: 0f1000000    size: 1048576 bytes
PCI_BAR1 (Mem-64bit) base: 000000000            size: 268435456 bytes
Marvell>> sg
PHY 0 :
---------
Auto negotiation: Enabled
Speed: 100 Mbps
Duplex: Half
Link: up
PHY 1 :
---------
Auto negotiation: Enabled
Speed: Uknown
Duplex: Full
Link: up
Marvell>> nand info
Device 0: NAND 128MB 3,3V 8-bit, sector size 128 KB
Marvell>> map
CPU Interface
-------------
SDRAM_CS0 ....base 00000000, size 256MB SDRAM_CS1 ....disable
SDRAM_CS2 ....disable
SDRAM_CS3 ....disable
PEX0_MEM ....base 90000000, size 128MB PEX0_IO ....base f0000000, size  16MB PEX1_MEM ....no such
PEX1_IO ....no such
INTER_REGS ....base f1000000, size   1MB NFLASH_CS ....base f9000000, size   8MB SPI_CS ....base f8000000, size  16MB BOOT_ROM_CS ....no such
DEV_BOOTCS ....no such
CRYPT_ENG ....base fb000000, size  64KB 
AHB To MBUS Bridge:
-------------------
win0 - PEX0_MEM base 90000000, ....size 128MB win1 - disable
win2 - PEX0_IO base f0000000, ....size  16MB win3 - disable
win4 - NFLASH_CS base f9000000, ....size   8MB win5 - SPI_CS base f8000000, ....size  16MB win6 - disable
win7 - CRYPT_ENG base fb000000, ....size  64KB win8 - INTER_REGS base f1000000, ....size   1MB 
PEX0:
-----
Pex Bars 
Internal Regs Bar0.... base f1000000, size   1MB DRAM Bar1............. base 00000000, size 256MB Devices Bar2.......... disable
Pex Decode Windows
win0 - SDRAM_CS0 base 00000000, ....size 256MB win1 - disable
win2 - disable
win3 - disable
win4 - disable
win5 - disable
default win - target unknown Expansion ROM - NFLASH_CS 
USB:
----
Device 0:
win0 - SDRAM_CS0 base 00000000, size 256MB win1 - PEX0_MEM base 90000000, size 128MB win2 - disable
win3 - disable
ETH 0:
----
win0 - SDRAM_CS0 base 00000000, ....size 256MB win1 - NFLASH_CS base f9000000, ....size   8MB win2 - SPI_CS base f8000000, ....size  16MB win3 - PEX0_IO base f0000000, ....size  16MB win4 - disable
win5 - disable
XOR 0:
----
win0 - NFLASH_CS base f9000000, size   8MB win1 - PEX0_MEM base 90000000, size 128MB win2 - SDRAM_CS0 base 0, size 256MB win3 - SPI_CS base f8000000, size  16MB win4 - CRYPT_ENG base fb000000, size  64KB win5 - disable
win6 - disable
win7 - disable
XOR 1:
----
win0 - NFLASH_CS base f9000000, size   8MB win1 - PEX0_MEM base 90000000, size 128MB win2 - SDRAM_CS0 base 0, size 256MB win3 - SPI_CS base f8000000, size  16MB win4 - CRYPT_ENG base fb000000, size  64KB win5 - disable
win6 - disable
win7 - disable
SATA 0:
----
win0 - SDRAM_CS0 base 00000000, ....size 256MB win1 - SDRAM_CS1 base 10000000, ....size 256MB win2 - SDRAM_CS2 base 20000000, ....size 256MB win3 - SDRAM_CS3 base 30000000, ....size 256MB 
SATA 1:
----
win0 - SDRAM_CS0 base 00000000, ....size 256MB win1 - SDRAM_CS1 base 10000000, ....size 256MB win2 - SDRAM_CS2 base 20000000, ....size 256MB win3 - SDRAM_CS3 base 30000000, ....size 256MB 
AUDIO:
----
win0 - SDRAM_CS0 base 00000000, ....size 256MB win1 - SDRAM_CS1 base 10000000, ....size 256MB Marvell>> dclk
TCLK 166Mhz, SYSCLK 400Mhz (UART baudrate 115200)
Marvell>> ide reset
Reset IDE: Marvell Serial ATA Adapter
Integrated Sata device found
[0 0 0]: Enable DMA mode (6)  Device 0 @ 0 0:
Model: HITACHI HUA722010ALA330                  Firm: JP4ONA01 Ser#: XXXXXX            Type: Hard Disk            Supports 48-bit addressing            Capacity: 953869.7 MB = 931.5 GB (1953525168 x 512)
[0 1 0]: Enable DMA mode (6)  Device 1 @ 0 1:
Model: ST31000524AS                             Firm: JC4A     Ser#:             XXXXXXXXXX            Type: Hard Disk            Supports 48-bit addressing            Capacity: 953869.7 MB = 931.5 GB (1953525168 x 512)

Some of these variables are documented, some seem to be Marvell or Kirkwood specific cruft. The bootloader version is ancient, according to github, version 1.1.4 was released in 2005.

4 Original firmware

The original firmware resides on the NAND flash and an example of the entire boot console log is provided below:

 ** MARVELL BOARD: DB-88F6702A-BP LE 
U-Boot 1.1.4 (Aug 22 2012 - 17:06:54) Marvell version: 3.6.0.DNS-320L.01
U-Boot code: 00600000 -> 0067FFF0  BSS: -> 006CFB00
Soc: 88F6702 A1 CPU running @ 1000Mhz L2 running @ 500Mhz
SysClock = 400Mhz , TClock = 166Mhz 
DRAM (DDR2) CAS Latency = 5 tRP = 5 tRAS = 18 tRCD=6
DRAM CS[0] base 0x00000000   size 256MB DRAM Total size 256MB  16bit width
Addresses 8M - 0M are saved for the U-Boot usage.
Mem malloc Initialization (8M - 7M): Done
NAND:128 MB
Flash:  0 kB
CPU : Marvell Feroceon (Rev 1)
Streaming disabled Write allocate disabled
USB 0: host mode
PEX 0: interface detected no Link.
Net:   egiga0 [PRIME]
Hit any key to stop autoboot:  0 
NAND read: device 0 offset 0x100000, size 0x300000
load addr ....  =a00000
 3145728 bytes read: OK
NAND read: device 0 offset 0x600000, size 0x300000
load addr ....  =f00000
 3145728 bytes read: OK
## Booting image at 00a00000 ...   Image Name:   Linux-2.6.31.8   Created:      2012-08-22   8:55:08 UTC   Image Type:   ARM Linux Kernel Image (uncompressed)   Data Size:    2630552 Bytes =  2.5 MB   Load Address: 00008000   Entry Point:  00008000   Verifying Checksum ... OK
OK
## Loading Ramdisk Image at 00f00000 ...   Image Name:   Ramdisk   Created:      2014-12-26   3:00:57 UTC   Image Type:   ARM Linux RAMDisk Image (gzip compressed)   Data Size:    1743894 Bytes =  1.7 MB   Load Address: 00e00000   Entry Point:  00e00000   Verifying Checksum ... OK
Starting kernel ...
Uncompressing Linux......................................................................................................................................................................... done, booting the kernel.
Linux version 2.6.31.8 (jack@swtest6) (gcc version 4.3.2 (sdk3.3-ct-ng-1.4.1) ) #1 Wed Aug 22 16:55:05 CST 2012
CPU: Feroceon 88FR131 [56251311] revision 1 (ARMv5TE), cr=00053977
CPU: VIVT data cache, VIVT instruction cache
Machine: Feroceon-KW
Using UBoot passing parameters structure
Memory policy: ECC disabled, Data cache writeback
Built 1 zonelists in Zone order, mobility grouping off.  Total pages: 65024
Kernel command line: root=/dev/ram console=ttyS0,115200 :::DB88FXX81:egiga0:none
PID hash table entries: 1024 (order: 10, 4096 bytes)
Dentry cache hash table entries: 32768 (order: 5, 131072 bytes)
Inode-cache hash table entries: 16384 (order: 4, 65536 bytes)
Memory: 256MB = 256MB total
Memory: 246272KB available (4960K code, 334K data, 136K init, 0K highmem)
Hierarchical RCU implementation.
NR_IRQS:128
Console: colour dummy device 80x30
Calibrating delay loop... 999.42 BogoMIPS (lpj=4997120)
Mount-cache hash table entries: 512
CPU: Testing write buffer coherency: ok
NET: Registered protocol family 16
Feroceon L2: Enabling L2
Feroceon L2: Cache support initialised.
CPU Interface
-------------
SDRAM_CS0 ....base 00000000, size 256MB SDRAM_CS1 ....disable
SDRAM_CS2 ....disable
SDRAM_CS3 ....disable
PEX0_MEM ....base e0000000, size 128MB PEX0_IO ....base f2000000, size   1MB PEX1_MEM ....no such
PEX1_IO ....no such
INTER_REGS ....base f1000000, size   1MB NFLASH_CS ....base fa000000, size   2MB SPI_CS ....base f4000000, size  16MB BOOT_ROM_CS ....no such
DEV_BOOTCS ....no such
CRYPT_ENG ....base f0000000, size   2MB   Marvell Development Board (LSP Version KW_LSP_5.1.3_patch29)-- DB-88F6702A-BP  Soc: 88F6702 A1 LE
 Detected Tclk 166666667 and SysClk 400000000 MV Buttons Device Load
Marvell USB EHCI Host controller #0: c8040740
PEX0 interface detected no Link.
PCI: bus0: Fast back to back transfers enabled
mvPexLocalBusNumSet: ERR. Invalid PEX interface 1
bio: create slab  at 0
SCSI subsystem initialized
usbcore: registered new interface driver usbfs
usbcore: registered new interface driver hub
usbcore: registered new device driver usb
NET: Registered protocol family 2
IP route cache hash table entries: 2048 (order: 1, 8192 bytes)
TCP established hash table entries: 8192 (order: 4, 65536 bytes)
TCP bind hash table entries: 8192 (order: 3, 32768 bytes)
TCP: Hash tables configured (established 8192 bind 8192)
TCP reno registered
NET: Registered protocol family 1
Trying to unpack rootfs image as initramfs...
rootfs image is not initramfs (no cpio magic); looks like an initrd
Freeing initrd memory: 1700K
RTC has been updated!!!
rtc mv_rtc: rtc core: registered kw-rtc as rtc0
RTC registered
cpufreq: Init kirkwood cpufreq driver
XOR registered 4 channels
XOR 2nd invalidate WA enabled
cesadev_init(c000ed5c)
mvCesaInit: sessions=640, queue=64, pSram=f0000000
MV Buttons Driver Load
VFS: Disk quotas dquot_6.5.2
Dquot-cache hash table entries: 1024 (order 0, 4096 bytes)
squashfs: version 4.0 (2009/01/31) Phillip Lougher
Installing knfsd (copyright (C) 1996 okir@monad.swb.de).
NTFS driver 2.1.29 [Flags: R/O].
JFFS2 version 2.2. (NAND) © 2001-2006 Red Hat, Inc.
fuse init (API version 7.12)
msgmni has been set to 484
alg: No test for cipher_null (cipher_null-generic)
alg: No test for ecb(cipher_null) (ecb-cipher_null)
alg: No test for digest_null (digest_null-generic)
alg: No test for compress_null (compress_null-generic)
alg: No test for lzma (lzma-generic)
alg: No test for stdrng (krng)
alg: No test for hmac(digest_null) (hmac(digest_null-generic))
Block layer SCSI generic (bsg) driver version 0.4 loaded (major 253)
io scheduler noop registered
io scheduler anticipatory registered (default)
Initializing ths8200_init
Initializing dove_adi9889_init
Serial: 8250/16550 driver, 4 ports, IRQ sharing disabled
serial8250.0: ttyS0 at MMIO 0xf1012000 (irq = 33) is a 16550A
console [ttyS0] enabled
serial8250.1: ttyS1 at MMIO 0xf1012100 (irq = 34) is a 16550A
brd: module loaded
loop: module loaded
Integrated Sata device found
IRQ 21/mvSata: IRQF_DISABLED is not guaranteed on shared IRQs
scsi0 : Marvell SCSI to SATA adapter
scsi1 : Marvell SCSI to SATA adapter
Loading Marvell Ethernet Driver:  o Cached descriptors in DRAM  o DRAM SW cache-coherency  o 2 Giga ports supported  o Single RX Queue support - ETH_DEF_RXQ=0  o Single TX Queue support - ETH_DEF_TXQ=0  o TCP segmentation offload (TSO) supported  o Large Receive offload (LRO) supported  o Receive checksum offload supported  o Transmit checksum offload supported  o Network Fast Processing (Routing) supported - (Disabled)  o Driver ERROR statistics enabled  o Proc tool API enabled  o SKB Reuse supported - (Disabled)  o SKB Recycle supported - (Disabled)  o Rx descripors: q0=128  o Tx descripors: q0=532  o Loading network interface(s):     o register under mv88fx_eth platform     o egiga0, ifindex = 2, GbE port = 0
Warning: Giga 1 is Powered Off
mvFpRuleDb (c0edc000): 2048 entries, 8192 bytes
Counter=0, opIdx=6, overhead=16
Counter=1, opIdx=2, overhead=0
Counter=2, opIdx=1, overhead=18
Counter=3, opIdx=2, overhead=0
NAND device: Manufacturer ID: 0xad, Chip ID: 0xf1 (Hynix NAND 128MiB 3,3V 8-bit)
Scanning device for bad blocks
Using static partition definition
Creating 7 MTD partitions on "nand_mtd":
0x000000000000-0x000000100000 : "u-boot"
0x000000100000-0x000000600000 : "uImage"
0x000000600000-0x000000b00000 : "ramdisk"
0x000000b00000-0x000006f00000 : "image"
0x000006f00000-0x000007900000 : "rescue firmware"
0x000007900000-0x000007e00000 : "config"
0x000007e00000-0x000008000000 : "my-dlink"
ehci_hcd: USB 2.0 'Enhanced' Host Controller (EHCI) Driver
ehci_marvell ehci_marvell.70059: Marvell Orion EHCI
ehci_marvell ehci_marvell.70059: new USB bus registered, assigned bus number 1
ehci_marvell ehci_marvell.70059: irq 19, io base 0xf1050100
ehci_marvell ehci_marvell.70059: USB 2.0 started, EHCI 1.00
usb usb1: configuration #1 chosen from 1 choice
hub 1-0:1.0: USB hub found
hub 1-0:1.0: 1 port detected
ohci_hcd: USB 1.1 'Open' Host Controller (OHCI) Driver
uhci_hcd: USB Universal Host Controller Interface driver
Initializing USB Mass Storage driver...
usbcore: registered new interface driver usb-storage
USB Mass Storage support registered.
mice: PS/2 mouse device common for all mice
i2c /dev entries driver
md: linear personality registered for level -1
md: raid0 personality registered for level 0
md: raid1 personality registered for level 1
device-mapper: ioctl: 4.15.0-ioctl (2009-04-01) initialised: dm-devel@redhat.com
usbcore: registered new interface driver usbhid
usbhid: v2.6:USB HID core driver
TCP cubic registered
NET: Registered protocol family 17
RPC: Registered udp transport module.
RPC: Registered tcp transport module.
rtc mv_rtc: setting system clock to 2000-01-01 00:00:00 UTC (946684800)
md: Waiting for all devices to be available before autodetect
md: If you don't use raid, use raid=noautodetect
md: Autodetecting RAID arrays.
md: Scanned 0 and added 0 devices.
md: autorun ...
md: ... autorun DONE.
RAMDISK: gzip image found at block 0
VFS: Mounted root (ext2 filesystem) on device 1:0.
Freeing init memory: 136K
init started: BusyBox v1.20.2 (2013-11-27 15:54:37 CST)
starting pid 536, tty '': '/etc/rc.sh' Mounting /etc/fstab
umount: can't umount /proc: Invalid argument
umount: can't umount /usr/local/modules: Invalid argument
sh: can't open '/usr/sbin/pre_usb.sh'
first good block is 0
image len = 39137280 , image checksum = beaa9c0
umount: can't umount /usr/local/tmp: Invalid argument
dump image checksum=beaa9c0
mount cmd:busybox mount -t squashfs -o loop /usr/local/tmp/image.cfs /usr/local/modules
ln: /lib/./libnss_dns-2.8.so: File exists
ln: /lib/./libnss_dns.so.2: File exists
ln: /usr/sbin/./system_init: File exists
hardware init
GbE port 0: TxEnable WA - Enabled, deep=1, tx_en_bk=1
mtd check v1.02.08062012
config mtd type is JFFS2
/usr/local/config free size is 4767744 copy config files
cp: can't stat '/usr/local/config/user.log.old': No such file or directory
usbcore: registered new interface driver usblp set loopback interface
old firmware ver:20141226
new firmware ver:20141226
first good block is 0
mac1 = 70:62:B8:2A:52:73
lan0:ifconfig egiga0 hw ether 70:62:B8:2A:52:73
egiga0: mac address changed
egiga0: started
Support Mydlink
NET: Registered protocol family 10
lo: Disabled Privacy Extensions
ADDRCONF(NETDEV_UP): egiga0: link is not ready
IPv6 over IPv4 tunneling driver
sit0: Disabled Privacy Extensions
ip6tnl0: Disabled Privacy Extensions
IPv4 over IPv4 tunneling driver
tunl0: Disabled Privacy Extensions
net.ipv6.conf.default.accept_dad = 2
net.ipv6.conf.egiga0.accept_dad = 2
net.ipv6.conf.default.dad_transmits = 1
net.ipv6.conf.egiga0.dad_transmits = 1
net.ipv6.conf.default.forwarding = 0
net.ipv6.conf.default.accept_redirects = 1 execute rc.init.sh
awk: /var/run/udhcpc0.pid: No such file or directory
udhcpc (v1.20.2) started
Sending discover...
Sending discover...
Sending discover...
init egiga0
No lease, forking to background
killall: crond: no process killed set Time Zone ****
Fri Dec 31 22:00:51 GMT 1999 get Time from rtc and set it into system ****
rtc: RTC time = 2020/3/2 Mon 10:16:15
Mon Mar  2 10:16:15 GMT 2020 Do not adjust RTC time ***
Module IPC SERVER Version:(1.00.20090706)
ads=0
created mail daemon thread 0
ifconfig: egiga1: error fetching interface information: Device not found
Starting system message bus
Command: wget -T5 -t3 -q http://cfaj.freeshell.org/ipaddr.cgi -O /tmp/exip.0
Command: wget -T5 -t3 -q http://icanhazip.com/ -O /tmp/exip.1
Command: wget -T5 -t3 -q http://ifconfig.me/ip -O /tmp/exip.2
Command: wget -T5 -t3 -q http://whatismyip.org/ -O /tmp/exip.3
Command: wget -T5 -t3 -q http://ifconfig.me/ip -O /tmp/exip.4
Command: wget -T5 -t3 -q http://checkip.dyndns.com:8245/ -O /tmp/exip.5
config egiga0 169.254.173.176
RTNETLINK answers: File exists
call load_module network
Up_Send_Ctl : Can not find specified command "SysIP1"
Command line is not complete. Try option "help"
zcip: script /usr/share/udhcpc/zcip.script config failed, exitcode=1
Stop NFS Deamon....
Stop NFS mountd....
Unload Driver....
Stop Portmap.
No NFS information .
Stop NFS Server OVER.
Start config the NFS needed file....
Config Over.
No NFS information .
Start portmap....
Load Driver .
Start NFS Deamon .
svc: failed to register lockdv1 RPC service (errno 97).
NFSD: Using /var/lib/nfs/v4recovery as the NFSv4 state recovury0di�e{u�_k�owSf:�unicne*�o*gind recovery directory /var/lib/nfs/v4recovery
NFSD: starting 90-second grace period
Start NFS Server OVER .
cp: can't stat '/usr/local/config/Mydlink_Status.xml': No such file or directory
mcu version 1.02
system daemon v1.03.20130707
chk_io v1.03.20130502
mfg_start version 1.00(2014-12-26)
Mon Mar  2 10:16:42 2020
usb_dir []
cp: can't stat '/usr/local/config/mydlink_time.xml': No such file or directory
2020-03-02 10:16:39: (../../src/log.c.166) server started killall: chk_hotplug: no process killed
sh: you need to specify whom to kill
killall: dcp: no process killed
2020-03-02 10:16:42: (../../src/log.c.166) server started kinmyno��signalc: no process killed
usb_dir [/]
filename_mfg mfg_DNS_320L
check //mfg_DNS_320L file
opeo*��/mfw_Vn[�3::m�vime>gainme�jkillall: upnpc-ddns: no process killed
killall: tsa: no process killed
opt.local stop�ok.
o|w/oo{un���izu6ok��Jkillall: smbd: no process killed
Please press Enter to activate this console. LED_POWER_ON

5 Building mainline Linux kernel

I managed to build the mainline kernel 5.5.7 using a slightly modified device tree from github using Ubuntu 18.04 LTS:

➜  linux-5.5.7 lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 18.04.4 LTS
Release:        18.04
Codename:       bionic

The GCC version used was 7.5.0:

➜  linux-5.5.7 arm-linux-gnueabi-gcc -v
Using built-in specs.
COLLECT_GCC=arm-linux-gnueabi-gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc-cross/arm-linux-gnueabi/7/lto-wrapper
Target: arm-linux-gnueabi
Configured with: ../src/configure -v --with-pkgversion='Ubuntu/Linaro 7.5.0-3ubuntu1~18.04' --with-bugurl=file:///usr/share/doc/gcc-7/README.Bugs --enable-languages=c,ada,c++,go,d,fortran,objc,obj-c++ --prefix=/usr --with-gcc-major-version-only --program-suffix=-7 --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-libitm --disable-libquadmath --disable-libquadmath-support --enable-plugin --with-system-zlib --with-target-system-zlib --enable-multiarch --enable-multilib --disable-sjlj-exceptions --with-arch=armv5t --with-float=soft --disable-werror --enable-multilib --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=arm-linux-gnueabi --program-prefix=arm-linux-gnueabi- --includedir=/usr/arm-linux-gnueabi/include
Thread model: posix
gcc version 7.5.0 (Ubuntu/Linaro 7.5.0-3ubuntu1~18.04) 

In order to install this compiler you should run the command:

➜  linux-5.5.7 sudo apt install -y gcc-arm-linux-gnueabi

Now we can setup an alias which will be useful for building all other software. The alias should use the prefix for the compiler we just installed:

➜  linux-5.5.7 alias cross-make='make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi-'

First we need to create a default config for the mvebuv5 target. Why mvebu? mv means Marvell, EBU is Engineering Business Unit and v5 is the ARM architecture version. Therefore:

➜  linux-5.5.7 cross-make mvebu_v5_defconfig  HOSTCC  scripts/basic/fixdep  HOSTCC  scripts/kconfig/conf.o  HOSTCC  scripts/kconfig/confdata.o  HOSTCC  scripts/kconfig/expr.o  LEX     scripts/kconfig/lexer.lex.c  YACC    scripts/kconfig/parser.tab.[ch]  HOSTCC  scripts/kconfig/lexer.lex.o  HOSTCC  scripts/kconfig/parser.tab.o  HOSTCC  scripts/kconfig/preprocess.o  HOSTCC  scripts/kconfig/symbol.o  HOSTCC  scripts/kconfig/util.o  HOSTLD  scripts/kconfig/conf
#
# configuration written to .config
#

Now we need to copy in the Device Tree (.dts) file from the github repo to our Linux source directory:

➜ ~ git clone https://github.com/scus1/dns320l
Cloning into 'dns320l'...
remote: Enumerating objects: 327, done.
remote: Total 327 (delta 0), reused 0 (delta 0), pack-reused 327
Receiving objects: 100% (327/327), 82.17 KiB | 203.00 KiB/s, done.
Resolving deltas: 100% (154/154), done.
➜ ~ cp dns320l/kernel/dts/kirkwood-dns320l.dts linux-5.5.7/arch/arm/boot/dts 

At this point we can build the U-boot kernel image (uImage) and the DTB which is a compiled version of the Device Tree:

➜  linux-5.5.7 cross-make -j16 LOADADDR=0x8000 uImage kirkwood-dns320l.dtb  [...]  CALL    scripts/atomic/check-atomics.sh  CALL    scripts/checksyscalls.sh  CHK     include/generated/compile.h  Kernel: arch/arm/boot/Image is ready  Kernel: arch/arm/boot/zImage is ready  UIMAGE  arch/arm/boot/uImage
Image Name:   Linux-5.5.7
Created:      Sat Apr  4 21:49:09 2020
Image Type:   ARM Linux Kernel Image (uncompressed)
Data Size:    4550496 Bytes = 4443.84 KiB = 4.34 MiB
Load Address: 00008000
Entry Point:  00008000  Kernel: arch/arm/boot/uImage is ready
➜  linux-5.5.7 

The kernel image and DTB files need to be joined together for the kernel to notice the DTB blob. Then a new uImage needs to built from the joined file:

➜  linux-5.5.7 cat arch/arm/boot/zImage arch/arm/boot/dts/kirkwood-dns320l.dtb > arch/arm/boot/zImage-dtb ➜  linux-5.5.7 bash ./scripts/mkuboot.sh -A arm -O linux -C none  -T kernel -a 0x8000 -e 0x8000 -n 'Linux-5.5.7' -d arch/arm/boot/zImage-dtb arch/arm/boot/uImage
Image Name:   Linux-5.5.7
Created:      Sat Apr  4 21:51:29 2020
Image Type:   ARM Linux Kernel Image (uncompressed)
Data Size:    4561674 Bytes = 4454.76 KiB = 4.35 MiB
Load Address: 00008000
Entry Point:  00008000
➜  linux-5.5.7 

Now the kernel can be booted with PXE for example:

## Booting image at 00a00000 ...   Image Name:   Linux-5.5.7   Created:      2020-04-04  19:51:29 UTC   Image Type:   ARM Linux Kernel Image (uncompressed)   Data Size:    4561674 Bytes =  4.4 MB   Load Address: 00008000   Entry Point:  00008000   Verifying Checksum ... OK
OK
Starting kernel ...
Booting Linux on physical CPU 0x0
Linux version 5.5.7 (enki@newton) (gcc version 7.5.0 (Ubuntu/Linaro 7.5.0-3ubuntu1~18.04)) #1 PREEMPT Sat Apr 4 21:47:13 CEST 2020
CPU: Feroceon 88FR131 [56251311] revision 1 (ARMv5TE), cr=0005397f
CPU: VIVT data cache, VIVT instruction cache
OF: fdt: Machine model: D-Link DNS-320L
Memory policy: Data cache writeback
Built 1 zonelists, mobility grouping on.  Total pages: 65024
Kernel command line: root=/dev/ram console=ttyS0,115200 :::DB88FXX81:egiga0:none
Dentry cache hash table entries: 32768 (order: 5, 131072 bytes, linear)
Inode-cache hash table entries: 16384 (order: 4, 65536 bytes, linear)
mem auto-init: stack:off, heap alloc:off, heap free:off
Memory: 250112K/262144K available (6669K kernel code, 286K rwdata, 1676K rodata, 200K init, 657K bss, 12032K reserved, 0K cma-reserved, 0K highmem)
SLUB: HWalign=32, Order=0-3, MinObjects=0, CPUs=1, Nodes=1
rcu: Preemptible hierarchical RCU implementation.
        Tasks RCU enabled.
rcu: RCU calculated value of scheduler-enlistment delay is 10 jiffies.
NR_IRQS: 16, nr_irqs: 16, preallocated irqs: 16
random: get_random_bytes called from start_kernel+0x278/0x410 with crng_init=0
clocksource: orion_clocksource: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 11467562657 ns
sched_clock: 32 bits at 166MHz, resolution 6ns, wraps every 12884901885ns
Switching to timer-based delay loop, resolution 6ns
Console: colour dummy device 80x30
Calibrating delay loop (skipped), value calculated using timer frequency.. 333.33 BogoMIPS (lpj=1666666)
pid_max: default: 32768 minimum: 301
Mount-cache hash table entries: 1024 (order: 0, 4096 bytes, linear)
Mountpoint-cache hash table entries: 1024 (order: 0, 4096 bytes, linear)
CPU: Testing write buffer coherency: ok
Setting up static identity map for 0x81e0 - 0x8238
mvebu-soc-id: MVEBU SoC ID=0x6702, Rev=0x3
rcu: Hierarchical SRCU implementation.
devtmpfs: initialized
clocksource: jiffies: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 19112604462750000 ns
futex hash table entries: 256 (order: -1, 3072 bytes, linear)
pinctrl core: initialized pinctrl subsystem
thermal_sys: Registered thermal governor 'step_wise'
NET: Registered protocol family 16
DMA: preallocated 256 KiB pool for atomic coherent allocations
cpuidle: using governor menu
Feroceon L2: Enabling L2
Feroceon L2: Cache support initialised.
vgaarb: loaded
SCSI subsystem initialized
usbcore: registered new interface driver usbfs
usbcore: registered new interface driver hub
usbcore: registered new device driver usb
Advanced Linux Sound Architecture Driver Initialized.
clocksource: Switched to clocksource orion_clocksource
NET: Registered protocol family 2
tcp_listen_portaddr_hash hash table entries: 512 (order: 0, 4096 bytes, linear)
TCP established hash table entries: 2048 (order: 1, 8192 bytes, linear)
TCP bind hash table entries: 2048 (order: 1, 8192 bytes, linear)
TCP: Hash tables configured (established 2048 bind 2048)
UDP hash table entries: 256 (order: 0, 4096 bytes, linear)
UDP-Lite hash table entries: 256 (order: 0, 4096 bytes, linear)
NET: Registered protocol family 1
RPC: Registered named UNIX socket transport module.
RPC: Registered udp transport module.
RPC: Registered tcp transport module.
RPC: Registered tcp NFSv4.1 backchannel transport module.
PCI: CLS 0 bytes, default 32
Initialise system trusted keyrings
workingset: timestamp_bits=30 max_order=16 bucket_order=0
jffs2: version 2.2. (NAND) © 2001-2006 Red Hat, Inc.
Key type asymmetric registered
Asymmetric key parser 'x509' registered
io scheduler mq-deadline registered
io scheduler kyber registered
kirkwood-pinctrl f1010000.pin-controller: registered pinctrl driver
mvebu-gpio f1010140.gpio: IRQ index 3 not found
mv_xor f1060800.xor: Marvell shared XOR driver
mv_xor f1060800.xor: Marvell XOR (Registers Mode): ( xor cpy intr )
mv_xor f1060900.xor: Marvell shared XOR driver
mv_xor f1060900.xor: Marvell XOR (Registers Mode): ( xor cpy intr )
Serial: 8250/16550 driver, 2 ports, IRQ sharing disabled
printk: console [ttyS0] disabled
f1012000.serial: ttyS0 at MMIO 0xf1012000 (irq = 25, base_baud = 10416666) is a 16550A
printk: console [ttyS0] enabled
f1012100.serial: ttyS1 at MMIO 0xf1012100 (irq = 26, base_baud = 10416666) is a 16550A
loop: module loaded
sata_mv f1080000.sata: slots 32 ports 2
scsi host0: sata_mv
scsi host1: sata_mv
ata1: SATA max UDMA/133 irq 33
ata2: SATA max UDMA/133 irq 33
nand: device found, Manufacturer ID: 0xad, Chip ID: 0xf1
nand: Hynix H27U1G8F2BTR-BC
nand: 128 MiB, SLC, erase size: 128 KiB, page size: 2048, OOB size: 64
Scanning device for bad blocks
7 fixed-partitions partitions found on MTD device orion_nand
Creating 7 MTD partitions on "orion_nand":
0x000000000000-0x000000100000 : "u-boot"
0x000000100000-0x000000600000 : "uImage"
0x000000600000-0x000000b00000 : "ramdisk"
0x000000b00000-0x000006f00000 : "image"
0x000006f00000-0x000007900000 : "mini firmware"
0x000007900000-0x000007e00000 : "config"
0x000007e00000-0x000008000000 : "my-dlink"
libphy: Fixed MDIO Bus: probed
libphy: orion_mdio_bus: probed
mv643xx_eth: MV-643xx 10/100/1000 ethernet driver version 1.4
mv643xx_eth_port mv643xx_eth_port.0 eth0: port 0 with MAC address 00:50:43:00:02:02
libertas_sdio: Libertas SDIO driver
libertas_sdio: Copyright Pierre Ossman
ehci_hcd: USB 2.0 'Enhanced' Host Controller (EHCI) Driver
ehci-pci: EHCI PCI platform driver
ehci-orion: EHCI orion driver
orion-ehci f1050000.ehci: EHCI Host Controller
orion-ehci f1050000.ehci: new USB bus registered, assigned bus number 1
orion-ehci f1050000.ehci: irq 30, io mem 0xf1050000
orion-ehci f1050000.ehci: USB 2.0 started, EHCI 1.00
hub 1-0:1.0: USB hub found
hub 1-0:1.0: 1 port detected
usbcore: registered new interface driver usb-storage
usbcore: registered new interface driver ums-datafab
usbcore: registered new interface driver ums-freecom
usbcore: registered new interface driver ums-jumpshot
usbcore: registered new interface driver ums-sddr09
usbcore: registered new interface driver ums-sddr55
ata1: SATA link down (SStatus 0 SControl F300)
ata2: SATA link down (SStatus 0 SControl F300)
rtc-mv f1010300.rtc: internal RTC not ticking
i2c /dev entries driver
watchdog: f1020300.watchdog-timer: driver supplied timeout (4294967295) out of range
watchdog: f1020300.watchdog-timer: falling back to default timeout (25)
orion_wdt: Initial timeout 25 sec
marvell-cesa f1030000.crypto: CESA device successfully registered
usbcore: registered new interface driver usbhid
usbhid: USB HID core driver
oprofile: no performance counters
oprofile: using timer interrupt.
NET: Registered protocol family 17
lib80211: common routines for IEEE802.11 drivers
Loading compiled-in X.509 certificates
input: gpio-keys as /devices/platform/gpio-keys/input/input0
hctosys: unable to open rtc device (rtc0)
cfg80211: Loading compiled-in X.509 certificates for regulatory database
cfg80211: Loaded X.509 cert 'sforshee: 00b28ddf47aef9cea7'
platform regulatory.0: Direct firmware load for regulatory.db failed with error -2
cfg80211: failed to load regulatory.db
ALSA device list:  No soundcards found.
VFS: Cannot open root device "ram" or unknown-block(1,0): error -6
[...]

Additional kernel options can now be added depending on your needs. I have provided the config I was using here.

6 Building a rootfs

For the rootfs I wanted to use buildroot as I've found it's build system to be the easiest to understand. First we download buildroot-2020.02:

➜  ~ tar xvf buildroot-2020.02.tar.bz2
[...]
buildroot-2020.02/docs/manual/manual.html
buildroot-2020.02/.br2-external.in.openssl
buildroot-2020.02/.br2-external.in.toolchains
➜  ~

Then we need to copy in the packages for the DNS320L mcu communications daemon:

(default) ➜  buildroot-2020.02 cp -r buildroot-dns320l/package/* package
(default) ➜  buildroot-2020.02 patch -p1 < buildroot-dns320l/add-to-config.patch patching file package/Config.in
(default) ➜  buildroot-2020.02 

We can now build whatever options we want, the config I used can be downloaded here. The target architecture should be "ARM little-endian" and the target architecture variant is arm926t. After building buildroot

(default) ➜  buildroot-2020.02 make -j16
[...]
ln -snf /home/enki/Pobrane/buildroot-2020.02/output/host/arm-buildroot-linux-gnueabi/sysroot /home/enki/Pobrane/buildroot-2020.02/output/staging
(default) ➜  buildroot-2020.02 ls -l output/images/rootfs.cpio.uboot -rw-r--r-- 1 enki enki 20115884 kwi  5 19:08 output/images/rootfs.cpio.uboot

The initramfs image is inside the output/images/ directory and can be directly booted with the kernel built before.

7 Bootloader setup

For my own firmware I made the choice to not remove the original firmware from NAND just in case. I opted to boot my FW from an external USB stick plugged into the back USB port as I won't use it anyway. In order to configure the NAS for USB booting you need to perform the following steps:

First, interrupt the autoboot sequence by pressing SPACE and 1. After that perform the u-boot shell commands to save the existing boot configuration:

USB 0: host mode
PEX 0: interface detected no Link.
Net:   egiga0 [PRIME]
Hit any key to stop autoboot:  0 Marvell>> Marvell>> setenv bootcmd_stock $(bootcmd)
Marvell>> printenv bootcmd_stock
bootcmd_stock=nand read.e 0xa00000 0x100000 0x300000;nand read.e 0xf00000 0x600000 0x300000;bootm 0xa00000 0xf00000

As you can see the original boot configuration has been saved to the 'bootcmdstock' variable and can be restored later if required. Now create a new variable with commands used to launch the kernel and initramfs from the USB stick and set 'bootcmd' to run them:

Marvell>> setenv bootcmd_usb usb start\;fatload usb 0 0xa00000 /slots/A/kernel\;fatload usb 0 0xf00000 /slots/A/initramfs\;bootm 0xa00000 0xf00000
Marvell>> printenv bootcmd_usb
bootcmd_usb=usb start;fatload usb 0 0xa00000 /slots/A/kernel;fatload usb 0 0xf00000 /slots/A/initramfs;bootm 0xa00000 0xf00000
Marvell>> Marvell>> setenv bootcmd run bootcmd_usb
Marvell>> printenv bootcmd
bootcmd=run bootcmd_usb
Marvell>> 

The last setenv command sets the boootcmd to run the contents of the bootcmdusb variable which contains commands to load the kernel and initramfs from the usb stick and execute the kernel entry point. Now if you would want to restore the NAS to boot the original firmware you just need to setenv bootcmd to contain the 'run bootcmdstock' string. The last step is to save the u-boot configuration:

Marvell>> saveenv
Saving Environment to NAND...
Erasing Nand...Writing to Nand... done
Marvell>> 

8 References

This is a list of references I used when working on the NAS. A lot has been done already by others:

geschätzte Lesezeit: 12 min

For most application developers, the database is an altar of demon gods best left unapproached. But it need not be that way!

Other things being equal, the level of comfort a developer has with the underlying database defines their seniority level. Little database and little coding experience = junior developer; little database and good coding experience = mid-level developer; good database and good coding experience = senior developer.

It&#8217;s a harsh reality that even devs with 6-8 years under their belt struggle to explain the intricacies of the query optimizer and prefer to look heavenwards when asked about database tuning.

Why?

Surprisingly, the reason isn&#8217;t laziness (although in some part it is).

The point is that databases are a force of their own to contend with. Even traditionally, when there were only the relational types of databases to deal with, mastering them was a miracle and career path in its own; these days, we have so many types of databases that it&#8217;s just impossible to expect a single, mortal soul to master everything.

That said, there&#8217;s a good chance that you&#8217;re still happy with relational databases or are part of a team that has a product running on a relational database satisfactorily for a long, long time. And in nine cases out of ten, you&#8217;re on MySQL (or MariaDB). For these cases, diving just a little deeper under the hood yields massive benefits in boosting application performance and is every bit worth learning.

Curious? Let&#8217;s dive in!

Not curious? Well, dive in anyway, because your career depends on it! 😛

Optimize the MySQL query cache

Almost all optimization in the field of computers comes down to caching. On one end, the CPU maintains several levels of cache to speed up its computations, and on the other, web apps make aggressive use of caching solutions like Redis to server precomputed results to users rather than hitting the database every time.

But hey, even the poor MySQL database has its own query cache! That is, every time you query something, and the data is still stale, MySQL will serve up these cached results rather than running the query again, making the app ridiculously faster.

You can check whether you have query cache available (note, available, not enabled) in your database by running this query in the database console:

MariaDB [(none)]&gt; SHOW VARIABLES LIKE &#039;have_query_cache&#039;; +------------------+-------+ | Variable_name | Value | +------------------+-------+ | have_query_cache | YES | +------------------+-------+ 

So, you can see that I&#8217;m running MariaDB and that I have query caching available to be turned on. It&#8217;s extremely unlikely that you&#8217;d have it turned off if you&#8217;re using a standard MySQL install.

Now let&#8217;s see if I have the query cache actually turned on:

MariaDB [(none)]&gt; SHOW VARIABLES LIKE &#039;query_cache_type&#039;; +------------------+-------+ | Variable_name | Value | +------------------+-------+ | query_cache_type | ON | +------------------+-------+

Yes, I do. But in case you don&#8217;t, you can turn it on by saying:

MariaDB [(none)]&gt; SET GLOBAL query_cache_type = ON;

Interestingly, this variable also accepts a third value that denotes &#8220;on-demand,&#8221; meaning MySQL will cache only those queries we tell it to, but we won&#8217;t go into that here.

With this, you have query caching on and have taken the first step towards a more robust MySQL setup! I say the first step because while turning it on is a major improvement, we do need to tune query caching to suit our setup. So let&#8217;s learn to do that.

The other variable of interest here is query_cache_size, whose function is self-explanatory:

MariaDB [(none)]&gt; SHOW VARIABLES LIKE &#039;query_cache_size&#039;; +------------------+----------+ | Variable_name | Value | +------------------+----------+ | query_cache_size | 16777216 | +------------------+----------+

So, I have a query cache of around 16 MB size. Note that even if query caching is turned on, but this size is zero, caching is effectively off. That&#8217;s why checking only one variable isn&#8217;t enough. Now, you should set a query cache size, but how much should it be? First off, please note that the query caching feature will itself need 4 KB to store its metadata, so whatever you select should be above that.

Let&#8217;s say you set the query cache size to be 500 KB:

MariaDB [(none)]&gt; SET GLOBAL query_cache_size = 500000;

Is doing this much enough? Well, no, because how the query engine will actually end up performing depends on a couple more things:

  • First off, the query_cache_size variable must be large enough to hold your the result of your queries. If it&#8217;s too small, nothing will be cached.
  • Secondly, if query_cache_size is set to too high a number, there will be two types of problems: 1) The engine will have to do extra work storing and locating query results in this massive memory area. 2) If most of the queries result in much smaller sizes, the cache will get fragmented, and the benefits of using a cache will get lost.

How do you know the cache is getting fragmented? Check the total number of blocks in the cache like this:

MariaDB [(none)]&gt; show status like &#039;Qcache_total_blocks&#039;; +---------------------+-------+ | Variable_name | Value | +---------------------+-------+ | Qcache_total_blocks | 33 | +---------------------+-------+

If the number is very high, the cache is fragmented and needs to be flushed.

So, to avoid these problems, make sure that the size of query_cache_size is chosen wisely. If you feel frustrated that I didn&#8217;t leave you with a concrete number here, I&#8217;m afraid that&#8217;s the way things are once you move past development and step into engineering. You must look into the app you&#8217;re running and see what the query sizes for the important query results are and then set this number. And even then you might end up making a mistake. 🙂

Threading, thread pools, waiting, and timeouts

This is probably the most interesting part of how MySQL works and getting it right means making your app several times faster!

Threading

MySQL is a multi-threaded server. That means, every time there&#8217;s a new connection to the MySQL server, it opens a new thread with the connection data and passes off a handle to it to the client (just in case you&#8217;re wondering what a thread is, see this). The client then sends all queries over this thread and receives results. This leads us to ask a natural question: how many threads can MySQL spin up? The answer lies in the next section.

Thread pool

No program in a computer system can open as many threads as it wants. The reason is twofold: 1) Threads cost memory (RAM), and the operating system just won&#8217;t allow you to go berserk and eat up all of it. 2) Managing, say, a million threads is a massive task on its own, and if the MySQL server could create that many threads, it would die trying to deal with the overhead.

To avoid these problems, MySQL comes with a thread pool &#8212; a fixed number of threads that are part of a pool at the start. New connection requests cause MySQL to pick up one of these threads and return the connection data, and if all the threads are used up, new connections are naturally refused. Let&#8217;s see how large the thread pool is:

ariaDB [(none)]&gt; show variables like &#039;thread_pool_size&#039;; +------------------+-------+ | Variable_name | Value | +------------------+-------+ | thread_pool_size | 4 | +------------------+-------+

So, my machine allows a maximum of four connections at the same time. It&#8217;s interesting to note that the number 4 comes from the fact that I have a four-core processor, which means my computer can run only 4 parallel tasks at a time (I&#8217;m talking of truly parallel tasks here, not concurrent ones). Ideally, this is the limit that should drive the value of thread_pool_size, but on beefier machines increasing it does benefit to a point. If you want not to make all new connections wait and are okay to take some performance hit (again, this is an area that you can judge best based on your app&#8217;s performance under load), bumping it up to 8 might be a good idea.

However, setting it beyond 16 is a terrible idea unless you have a 32-core machine, as performance degrades significantly. The rabbit hole of thread pools in MySQL goes deep, but if you&#8217;re interested, here&#8217;s a more detailed discussion.

Waiting and timeouts

Once a thread has been created and attached to a client, it&#8217;d be a waste of resources if the client sent no queries for the next few seconds (or minutes). As a result, MySQL terminates a connection after a period of inactivity. This is controlled by the wait_timeout variable:

MariaDB [(none)]&gt; show variables like &#039;wait%&#039;; +---------------+-------+ | Variable_name | Value | +---------------+-------+ | wait_timeout | 28800 | +---------------+-------+

The resulting value is in seconds. So yes, by default MySQL is set to wait 8+ hours before it severs the cord! This may be good if you have long-running queries and actually want to wait for them (but even then, eight hours is absurd!) but terrible in most cases. When a query is run, this value is set to 0 (meaning forever), but generally, this should be set to a very low value (5 seconds, for example, or maybe even less) to free up the connection for other processes.

Tuning temporary tables

Let&#8217;s begin with what are temporary tables in MySQL.

Suppose we have a MySQL that structurally looks like this: TABLE A UNION (TABLE B INNER JOIN C). That is, we&#8217;re interested in joining tables B and C, and then performing a union of the result with table A. Now, MySQL would first proceed to join the tables B and C, but before it can perform a union, it needs to store this data somewhere. This is where temporary tables come in &#8212; MySQL uses them to store data at intermediate stages in complex queries temporarily, and once the query is over, this temporary table gets discarded.

Now the question is: why should we bother with all this?

Simply because the temporary table, just a query result, is data that is being used by MySQL in computation, and the speed of its access (among other limitations) will determine how fast the query gets executed. For instance, storing the temporary table in RAM will be several times faster than storing it on the disk.

There are two variables that control this behavior:

MariaDB [(none)]&gt; show variables like &#039;MariaDB [(none)]&gt; show variables like &#039;tmp_table_size&#039;;   +----------------+----------+ | Variable_name  | Value    | +----------------+----------+ | tmp_table_size | 16777216 | +----------------+----------+ &#039;; +---------------------+----------+ | Variable_name | Value | +---------------------+----------+ | max_heap_table_size | 16777216 | +---------------------+----------+ MariaDB [(none)]&gt; show variables like &#039;tmp_table_size&#039;; +----------------+----------+ | Variable_name | Value | +----------------+----------+ | tmp_table_size | 16777216 | +----------------+----------+

The first one, max_heap_table_size, tells us how much RAM can be used up by a MySQL table (&#8220;heap&#8221; here refers here to the data structure used in RAM allocation and management &#8212; read more here), while the second one, tmp_table_size, shows what the maximum size of the temporary table is. In my case, both are set to 16 MB, though the point I&#8217;m trying to make it that increasing only tmp_table_size will not work as overall, MySQL would still be limited by max_table_heap_size.

Now comes the point: if the temporary tables being created are larger than the limit allowed by these variables, MySQL would be forced to write them to the hard disk, resulting in extremely poor performance. Our job now is simple: do our best to guess the most accurate data size for temporary tables and tweak these variables to that limit. However, I&#8217;d like to caution against absurdity: setting this limit to 16 GB (assuming you have this much RAM) when most of your temporary tables are less than 24 MB in size is foolishness &#8212; you&#8217;re simply wasting RAM that could&#8217;ve been used by other queries or parts of the system (cache, for example).

Conclusion

It&#8217;s not possible to cover all the system variables in one article, or even all the important ones in one article when the MySQL documentation itself spans several thousand words. While we covered some universal variables here, I&#8217;d encourage you to look into the system variables for the engine you&#8217;re using (InnoDB or MyISAM).

My most desirable outcome for writing this article is for you to take away three things:

  1. MySQL is a typical piece of software that works within limits set by the operating system. It&#8217;s not some mysterious program that does God-knows-what and is impossible to tame. Also, thankfully, it&#8217;s not that difficult to understand how it&#8217;s set up and gets controlled by its system variables.
  2.  There is no single setting that will make your MySQL install go zoom. You have no choice but to look within your running systems (remember, optimization comes after the app is in production, not before), make the best guesses and measurements, and live with the reality that it&#8217;s never going to be perfect.
  3. Tuning the variables isn&#8217;t the only way to optimize MySQL &#8212; efficient writing queries is another big deal, but it&#8217;s something I&#8217;ll address in another article. But the point is, even if you&#8217;ve done a godlike analysis and tuned these parameters to the best, it&#8217;s still possible for you to bring everything to a screeching halt.

What&#8217;s your favorite system variable for tuning? 🙂

ref: https://www.reddit.com/r/discgolf/comments/kt4kj8/upshot_exercise_i_made_to_suck_less_more_details/

Layout

I have 10 cones set-up on a grid system. The rows are 100, 150, 200, and 250 feet away from the throwing area. The rows that are 100 and 200 feet away have cones set 50 and 100 feet out from the center.

Exercise

The image really explains it all. The goal is to practice your upshot/teebox accuracy.

• Straight throws are not meant to flex as the goal is to develop a dead straight, extremely accurate straight shot. • When throwing to cones 6 – 9 I the general goal is to try to have the disc pass between cones 2 and 3 then hyzer/anhyzer to the cones. • I use a number randomizer to decide what order I throw in.

Edit: Check out the comment by u/lampshade4ever for another good practice method.

Items Needed

100 ft Measuring tape I use https://www.amazon.com/gp/product/B008AGWPMW/ref=ox_sc_act_title_2?smid=ATVPDKIKX0DER&psc=1

Cones I use https://www.amazon.com/gp/product/B07BJ2VXLQ/ref=ox_sc_act_title_1?smid=A1QCFBCSX82WDP&psc=1

Other Notes

Basically, I’m pretty bad at disc golf and am just playing with putters and mids for January and February. I try to do field work at least once a week and came up with this exercise to focus on my upshot game and accuracy.

The goal for me is to get as close to each cone as possible and to figure out which discs/how I’m going to throw each shot. In addition, this helps show what tendencies you have when you are throwing similar shots over and over

For example, my common weaknesses are:

• Accuracy on the backhand anyhzer shot going to cones 4 and 9 • Having a tendency to undershoot and land southeast of cones 5 and 6. • Accuracy going to cone 10

I hope you potentially get some use out of this!


— Direct link

geschätzte Lesezeit: 13 min

Results from the 3DiscGolf Survey (Part I)

By Josh Woods, PhD, and Dee Leekha ~

Cover art
Garbage on disc golf courses, long grass on fairways, uneven or small tee pads, erosion, and no bathrooms top the list of concerns among experienced course developers.

Disc golf has changed a lot over the last twenty years.

Two decades ago, there were 4,776 active members of the PDGA in the United States. Today, there are more than 28,861. Americans played in 329 PDGA sanctioned events in 1998. They will play in no fewer than 2,368 in the coming year, per the PDGA.

In 1998, Scott Stokely broke the World Record for distance with a jaw-dropping throw that carried 693 feet. In 2017, Simon Lizotte parked a 726-foot hole during tournament play, and the World Record is now held by David Wiggins Jr. with a distance that exceeds Stokely’s by nearly 400 feet.

Twenty or so years ago, one of the best disc golfers in the world, Eagle McMahon, did not exist.

As notable as new world records and unborn Eagles may be, perhaps the most amazing change in disc golf land can be found on the courses. In 1998, U.S. disc golfers had only 851 places to play, according to PDGA stats. Twenty-four states had fewer than ten courses to choose from. Now we have nearly 6,000 courses in the U.S., and only one state in the nation has fewer than ten.

This tremendous growth has prompted many observers to rank disc golf among the fastest growing sports in America. And yet, while the number of disc golf courses is clearly on the rise, less is known about the quality of this growth.

With so many changes afoot, we decided to take a closer look at the country’s disc golf courses by surveying the attitudes of experienced course developers.

About the Respondents

On January 13, 2018, we invited the members of a Facebook group called 3DiscGolf to fill out an online questionnaire. 3DISCgolf is a forum for anyone who supports the goal of creating safer, smarter and more sustainable disc golf.

Within two days, 156 of the group’s 354 members answered our call. The great majority of them have designed, co-designed or helped construct at least two disc golf courses. Roughly 22 percent of the respondents have worked on ten or more courses.

Members of the 3DiscGolf group are also avid players. Most of them play at least four different courses a month. Over a lifetime, the typical respondent has played more than 100 courses. They also typically play at least once or more per week, and most describe their skill levels as advanced or professional. A clear majority are (or have been) members of a local disc golf club and the PDGA.

The respondents are predominately male, white, over the age of 35, and educated with a bachelor’s degree or higher. The 3DiscGolf sample has at least one representative from 39 states, with large shares hailing from Michigan, New York and Texas. (For more details, see the Appendix).

Course Maintenance and Facilities

Based on a content analysis of 30 randomly selected reviews from Disc Golf Course Review (DGCR), we identified thirteen aspects of courses related to maintenance and facilities that are commonly regarded as “cons” by reviewers. We transformed these thirteen comments into questions and included them in the survey.

We asked respondents, “Please base your answers on your general attitude toward the U.S. disc golf courses that you have played or observed. How big of a problem are the following issues related to maintenance and facilities?” Respondents rated each aspect on a four-point scale: “A big problem,” “Somewhat of a problem,” “A small problem,” or “Not a problem,” with “Don’t know” as a fifth option.

To quickly summarize the findings, we combined two categories and constructed a list of course aspects that were most likely to be considered “a big problem” or “somewhat of a problem” (see Figure 1).

Figure 1

On issues of course maintenance and facilities, the respondents pointed to the lack of a course map on site, no bathrooms, and garbage on courses as key problems. Fairways in need of mowing or trimming, as well as soil erosion and compaction were also considered notable concerns on the courses they played.

Some of the issues found on DGCR were not deemed significant problems by our participants. Surprisingly, for instance, only 11 percent of respondents named broken or missing baskets as a “big problem” or “somewhat of a problem.” The respondents were also less concerned about off-leash dogs, and various types of insects and wildlife. (For more details, see the Appendix).

Course Design and Construction

We also identified twelve course design and construction aspects that often appear in the “cons” list of reviewers on DGCR. We measured our respondents’ attitudes toward them using the same scale, and constructed a list of the five biggest design problems, as seen in Figures 2.

Figure 2

Three of the twelve course design and construction issues were related to tee pads (slippery, uneven, too short), and each one made the top-five list of problems. The difficulty of navigating between holes, as well as boring or unimaginative courses also topped the list of course design issues.

Two problem tee pads
Examples of a lumpy tee pad (left) and a narrow, raised tee pad (right) that present ankle injury potential. Photos by Houck Design.

The takeaways

One purpose of this study is to provide information to course developers that may help them build safer and more enjoyable disc golf courses. To our best knowledge, this is the first (publicly available) survey of its kind. As illustrated in the Appendix, this report offers a unique ranking of twenty-five problems by some of the world’s most experienced course developers. Remedying these problems at new and existing courses would likely encourage more people to play and keep playing disc golf.

A second goal of this project is to encourage people of all backgrounds and experiences to come together and share their views. So, what do you think? What are the most important problems facing disc golf courses in the United States? Please join the discussion on the 3DiscGolf Facebook group, and by offering your comments below.

What’s Next?

In Part II of our 3DiscGolf survey results, we’ll tackle the issue of safety on disc golf courses. What are the most common safety issues, and which ones are perceived by course designers as big concerns? Is safety on disc golf courses really a problem?

~~~

Josh Woods

Josh Woods, editor at Parked, is an associate professor of sociology at West Virginia University. His current research focuses on the growth and group dynamics of non-normative sports.

Dee

Dee Leekha, author of Business Consciousness®, is the co-founder of Circular Productions, LLC and Houck Design, a disc golf course design company. As an entrepreneur, her business operations focus on uplifting communities and lives through the sport and game of disc golf.

~~~

Parked is a non-profit, educational blog. We don’t want to sell you something. No ads. No products. No private sponsors. We just want to include you in the conversation. Please show your support and follow us by entering your email address below. You’ll receive two or three free Parked articles per month. Like Parked on Facebook and Twitter to receive notifications.

~~~

Parked is underwritten in part by a grant from the Professional Disc Golf Association.

PDGA Logo Long Version

~~~

APPENDIX

 How many disc golf courses have you designed, co-designed and/or helped construct?

Answer % Count
None 8.33% 13
1 14.74% 23
2-5 42.95% 67
6-9 12.18% 19
10 or more 21.79% 34
Total 100% 156

About how many disc golf courses have you played?

Answer % Count
None 0.64% 1
1-25 9.62% 15
26-50 21.15% 33
51-100 21.79% 34
100-200 26.92% 42
More than 200 19.87% 31
Total 100% 156

About how often, on average, do you play disc golf, not including during very cold or hot seasons.

Answer % Count
Every day 2.56% 4
4 to 6 times per week 17.95% 28
1 to 3 times per week 55.13% 86
2 or 3 times per month 17.31% 27
Once per month 2.56% 4
5 to 11 times per year 1.92% 3
Less than 5 times per year 1.28% 2
Never 1.28% 2
Total 100% 156

Within a month time span, how many different courses do you typically play?

Answer % Count
None 2.56% 4
1 6.41% 10
2 16.67% 26
3 21.79% 34
4 16.03% 25
5 14.10% 22
6 5.77% 9
More than 6 16.67% 26
Total 100% 156

For these questions, please base your answers on your general attitude toward the U.S. disc golf courses that you have played or observed. How big of a problem are the following issues related to maintenance and facilities?

Question A big problem Somewhat of a problem A small problem Not a problem Don’t know Total
Garbage on courses. 22.00% 33 30.00% 45 43.33% 65 4.67% 7 0.00% 0 150
Fairways that need mowing, trimming or clearing. 8.67% 13 40.67% 61 42.00% 63 8.67% 13 0.00% 0 150
Broken or missing baskets. 2.67% 4 11.33% 17 48.00% 72 38.00% 57 0.00% 0 150
Missing, weathered or damaged tee signs. 21.33% 32 42.00% 63 30.67% 46 6.00% 9 0.00% 0 150
No course maps available at the site. 24.00% 36 41.33% 62 24.67% 37 10.00% 15 0.00% 0 150
Courses labeled as “18 hole courses” that only have 9 baskets and two tee pad locations. 2.68% 4 5.37% 8 22.82% 34 60.40% 90 8.72% 13 149
Off-leash dogs on courses. 6.08% 9 13.51% 20 36.49% 54 43.24% 64 0.68% 1 148
Insects, ticks, snakes or other wildlife on courses. 4.00% 6 28.00% 42 40.00% 60 26.00% 39 2.00% 3 150
Standing water or mud on courses. 4.67% 7 31.33% 47 50.67% 76 12.67% 19 0.67% 1 150
Soil erosion or compaction. 18.00% 27 31.33% 47 34.67% 52 14.67% 22 1.33% 2 150
No garbage cans on courses. 15.33% 23 32.00% 48 37.33% 56 14.67% 22 0.67% 1 150
No public bathrooms near courses. 21.33% 32 40.00% 60 28.67% 43 10.00% 15 0.00% 0 150
Incomplete removal of cut tree limbs, stumps or dead trees on or near fairways. 8.11% 12 27.03% 40 45.95% 68 18.24% 27 0.68% 1 148

For these questions, please base your answers on your general attitude toward the U.S. disc golf courses that you have played or observed. How big of a problem are the following issues related to course design and construction?

Question A big problem Somewhat of a problem A small problem Not a problem Don’t know Total
Tee pads that are too short or narrow. 17.33% 26 36.00% 54 37.33% 56 9.33% 14 0.00% 0 150
Tee pads that are slippery. 19.33% 29 42.00% 63 28.67% 43 9.33% 14 0.67% 1 150
Tee pads that are uneven, lumpy or rocky. 16.67% 25 38.00% 57 35.33% 53 9.33% 14 0.67% 1 150
Players have a hard time finding hole 1. 9.33% 14 30.67% 46 41.33% 62 18.00% 27 0.67% 1 150
Players have a hard time navigating between holes on the course. 18.00% 27 40.00% 60 33.33% 50 8.00% 12 0.67% 1 150
Players must take long walks between holes. 6.00% 9 14.67% 22 48.00% 72 30.67% 46 0.67% 1 150
Players must take a long walk after the finishing hole. 2.67% 4 14.00% 21 51.33% 77 31.33% 47 0.67% 1 150
Courses that are boring, unimaginative, too easy, or repetitive. 17.33% 26 32.00% 48 33.33% 50 16.67% 25 0.67% 1 150
Fairways that are too narrow. 4.67% 7 14.67% 22 40.00% 60 40.00% 60 0.67% 1 150
Fairways that are too open. 6.00% 9 20.00% 30 39.33% 59 34.00% 51 0.67% 1 150
Courses that favor right-handed, backhand-dominant players. 5.33% 8 20.67% 31 38.00% 57 34.67% 52 1.33% 2 150
No elevation changes. 5.33% 8 20.67% 31 36.00% 54 37.33% 56 0.67% 1 150

Finally, we would like to ask you a few background questions. First, are you now or have you ever been a member of the Professional Disc Golf Association?

Answer % Count
Yes 93.88% 138
No 6.12% 9
Total 100% 147

How old are you?

Answer % Count
18-24 years old 0.00% 0
25-34 years old 13.61% 20
35-44 years old 35.37% 52
45-54 years old 29.25% 43
55-64 years old 19.05% 28
65-74 years old 2.72% 4
75 years or older 0.00% 0
Total 100% 147

What is your sex?

Answer % Count
Male 96.58% 141
Female 3.42% 5
Total 100% 146

 

Please specify your ethnicity (or Race).

Answer % Count
White 88.51% 131
Hispanic or Latino 2.70% 4
Black or African American 0.68% 1
Native American or American Indian 1.35% 2
Asian / Pacific Islander 1.35% 2
Other 5.41% 8
Total 100% 148

What is the highest degree or level of school you have completed? If currently enrolled, highest degree received.

Answer % Count
No schooling completed 0.68% 1
Nursery school to 8th grade 0.00% 0
Some high school, no diploma 0.00% 0
High school graduate, diploma or the equivalent (for example: GED) 5.41% 8
Some college credit, no degree 25.68% 38
Trade/technical/vocational training 6.08% 9
Associate degree 8.11% 12
Bachelor’s degree 33.78% 50
Master’s degree 14.19% 21
Professional degree 3.38% 5
Doctorate degree 2.70% 4
Total 100% 148

What is your current state of residence?

Answer % Count
Alabama 2.70% 4
Alaska 0.00% 0
Arizona 1.35% 2
Arkansas 0.00% 0
California 2.70% 4
Colorado 2.03% 3
Connecticut 1.35% 2
Delaware 0.68% 1
Florida 2.03% 3
Georgia 3.38% 5
Hawaii 0.00% 0
Idaho 0.68% 1
Illinois 1.35% 2
Indiana 0.00% 0
Iowa 0.68% 1
Kansas 0.68% 1
Kentucky 0.68% 1
Louisiana 2.70% 4
Maine 0.00% 0
Maryland 1.35% 2
Massachusetts 1.35% 2
Michigan 14.19% 21
Minnesota 5.41% 8
Mississippi 0.00% 0
Missouri 0.68% 1
Montana 0.68% 1
Nebraska 0.00% 0
Nevada 2.03% 3
New Hampshire 0.68% 1
New Jersey 0.68% 1
New Mexico 0.00% 0
New York 10.81% 16
North Carolina 4.05% 6
North Dakota 0.00% 0
Ohio 1.35% 2
Oklahoma 1.35% 2
Oregon 1.35% 2
Pennsylvania 2.70% 4
Rhode Island 0.00% 0
South Carolina 0.68% 1
South Dakota 0.68% 1
Tennessee 2.03% 3
Texas 13.51% 20
Utah 0.68% 1
Vermont 0.00% 0
Virginia 2.03% 3
Washington 2.03% 3
West Virginia 0.68% 1
Wisconsin 2.70% 4
Wyoming 0.00% 0
I do not reside in the United States 3.38% 5
Total 100% 148

Are you a member of a disc golf club?

Answer % Count
Yes 89.12% 131
No 10.88% 16
Total 100% 147

How would you rate your skill level as a disc golfer?

Answer % Count
Professional 33.11% 49
Advanced 35.81% 53
Intermediate 22.30% 33
Recreational 7.43% 11
Beginner 1.35% 2
Total 100% 148

~~~

Cover art photo sources: Icy pad, photo by Full Metal Basket Editor James McDonald; “Dirty discer,” pinterest.com; extreme erosion, martindiscgolfclub.wordpress.com; grassy basket, timescall.com; short pad, dgcoursereview.com.