Skip to content

Commit

Permalink
Add mmapping of received Videocore memory, as well as mapping of all …
Browse files Browse the repository at this point in the history
…registers. This all builds and doesn't throw errors, but doesn't actually _do_ anything yet.
  • Loading branch information
Jon-Bright committed Jan 3, 2021
1 parent 0a0ed37 commit 0707e5d
Show file tree
Hide file tree
Showing 6 changed files with 284 additions and 17 deletions.
5 changes: 5 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
module github.com/Jon-Bright/ledctl

go 1.11

require (
github.com/edsrzf/mmap-go v1.0.0
golang.org/x/sys v0.0.0-20201231184435-2d18734c6014 // indirect
)
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
github.com/edsrzf/mmap-go v1.0.0 h1:CEBF7HpRnUCSJgGUb5h1Gm7e3VkmVDrR8lvWVLtrOFw=
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
golang.org/x/sys v0.0.0-20201231184435-2d18734c6014 h1:joucsQqXmyBVxViHCPFjG3hx8JzIFSaym3l3MM/Jsdg=
golang.org/x/sys v0.0.0-20201231184435-2d18734c6014/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
74 changes: 58 additions & 16 deletions pixarray/ws281x.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,44 +2,80 @@ package pixarray

import (
"fmt"
mmap "github.com/edsrzf/mmap-go"
"os"
)

type WS281x struct {
numPixels int
numColors int
g int
r int
b int
w int
mbox *os.File
mboxSize uint32
rp *RasPiHW
numPixels int
numColors int
g int
r int
b int
w int
mbox *os.File
mboxSize uint32
rp *RasPiHW
pixHandle uintptr
pixBusAddr uintptr
pixBuf mmap.MMap
pixBufOffs uintptr
dmaCb *dmaCallback
dmaBuf mmap.MMap
dma *dmaT
pwmBuf mmap.MMap
pwm *pwmT
gpioBuf mmap.MMap
gpio *gpioT
cmClkBuf mmap.MMap
cmClk *cmClkT
}

func NewWS281x(numPixels int, numColors int, order int, freq uint) (LEDStrip, error) {
func NewWS281x(numPixels int, numColors int, order int, freq uint, dma int) (LEDStrip, error) {
rp, err := detectRPiHW()
if err != nil {
return nil, fmt.Errorf("couldn't detect RPi hardware: %v", err)
}
offsets := offsets[order]
wa := WS281x{numPixels, numColors, offsets[0], offsets[1], offsets[2], offsets[3], nil, 0, rp}
wa := WS281x{
numPixels: numPixels,
numColors: numColors,
g: offsets[0],
r: offsets[1],
b: offsets[2],
w: offsets[3],
rp: rp,
}
wa.mbox, err = wa.mboxOpen()
if err != nil {
return nil, fmt.Errorf("couldn't open mbox: %v", err)
}
wa.calcMboxSize(freq)
handle, err := wa.allocMem()
wa.pixHandle, err = wa.allocMem()
if err != nil {
return nil, fmt.Errorf("couldn't allocMem: %v", err)
}
fmt.Printf("got handle %08X\n", handle)
busAddr, err := wa.lockMem(handle)
fmt.Printf("got handle %08X\n", wa.pixHandle)
wa.pixBusAddr, err = wa.lockMem(wa.pixHandle)
if err != nil {
wa.freeMem(handle) // Ignore error
wa.freeMem(wa.pixHandle) // Ignore error
return nil, fmt.Errorf("couldn't lockMem: %v", err)
}
fmt.Printf("got busAddr %08X\n", busAddr)
fmt.Printf("got busAddr %08X\n", wa.pixBusAddr)
wa.pixBuf, wa.pixBufOffs, err = wa.mapMem(wa.busToPhys(wa.pixBusAddr), int(wa.mboxSize))
if err != nil {
wa.unlockMem(wa.pixHandle) // Ignore error
wa.freeMem(wa.pixHandle) // Ignore error
return nil, fmt.Errorf("couldn't map pixBuf: %v", err)
}
fmt.Printf("got offset %d\n", wa.pixBufOffs)
wa.initDmaCb()
err = wa.mapDmaRegisters(dma)
if err != nil {
wa.unlockMem(wa.pixHandle) // Ignore error
wa.freeMem(wa.pixHandle) // Ignore error
return nil, fmt.Errorf("couldn't init registers: %v", err)
}

return &wa, nil
}
Expand All @@ -49,6 +85,12 @@ func (ws *WS281x) GetPixel(i int) Pixel {
}

func (ws *WS281x) SetPixel(i int, p Pixel) {
ws.pixBuf[int(ws.pixBufOffs)+i*ws.numColors+ws.r] = byte(p.R)
ws.pixBuf[int(ws.pixBufOffs)+i*ws.numColors+ws.g] = byte(p.G)
ws.pixBuf[int(ws.pixBufOffs)+i*ws.numColors+ws.b] = byte(p.B)
if ws.numColors == 4 {
ws.pixBuf[int(ws.pixBufOffs)+i*ws.numColors+ws.w] = byte(p.W)
}
}

func (wa *WS281x) Write() error {
Expand Down
150 changes: 150 additions & 0 deletions pixarray/ws281x_dma.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
package pixarray

import (
"fmt"
"unsafe"
)

const (
PWM_OFFSET = uintptr(0x0020c000)
GPIO_OFFSET = uintptr(0x00200000)
CM_PWM_OFFSET = uintptr(0x001010a0)
)

var dmaOffsets = map[int]uintptr{
0: 0x00007000,
1: 0x00007100,
2: 0x00007200,
3: 0x00007300,
4: 0x00007400,
5: 0x00007500,
6: 0x00007600,
7: 0x00007700,
8: 0x00007800,
9: 0x00007900,
10: 0x00007a00,
11: 0x00007b00,
12: 0x00007c00,
13: 0x00007d00,
14: 0x00007e00,
15: 0x00e05000,
}

type dmaT struct {
cs uint32
conblkAd uint32
ti uint32
sourceAd uint32
destAd uint32
txLen uint32
stride uint32
nextConBk uint32
debug uint32
}

type pwmT struct {
ctl uint32
sta uint32
dmac uint32
resvd_0x0c uint32
rng1 uint32
dat1 uint32
fif1 uint32
resvd_0x1c uint32
rng2 uint32
dat2 uint32
}

type gpioT struct {
fsel0 uint32 // GPIO Function Select
fsel1 uint32
fsel2 uint32
fsel3 uint32
fsel4 uint32
fsel5 uint32
resvd_0x18 uint32
set0 uint32 // GPIO Pin Output Set
set1 uint32
resvc_0x24 uint32
clr0 uint32 // GPIO Pin Output Clear
clr1 uint32
resvd_0x30 uint32
lev0 uint32 // GPIO Pin Level
lev1 uint32
resvd_0x3c uint32
eds0 uint32 // GPIO Pin Event Detect Status
eds1 uint32
resvd_0x48 uint32
ren0 uint32 // GPIO Pin Rising Edge Detect Enable
ren1 uint32
resvd_0x54 uint32
fen0 uint32 // GPIO Pin Falling Edge Detect Enable
fen1 uint32
resvd_0x60 uint32
hen0 uint32 // GPIO Pin High Detect Enable
hen1 uint32
resvd_0x6c uint32
len0 uint32 // GPIO Pin Low Detect Enable
len1 uint32
resvd_0x78 uint32
aren0 uint32 // GPIO Pin Async Rising Edge Detect
aren1 uint32
resvd_0x84 uint32
afen0 uint32 // GPIO Pin Async Falling Edge Detect
afen1 uint32
resvd_0x90 uint32
pud uint32 // GPIO Pin Pull up/down Enable
pudclk0 uint32 // GPIO Pin Pull up/down Enable Clock
pudclk1 uint32
resvd_0xa0 uint32
resvd_0xa4 uint32
resvd_0xa8 uint32
resvd_0xac uint32
test uint32
}

type cmClkT struct {
ctl uint32
div uint32
}

func (ws *WS281x) mapDmaRegisters(dma int) error {
offset, ok := dmaOffsets[dma]
if !ok {
return fmt.Errorf("no offset found for DMA %d", dma)
}
offset += ws.rp.periphBase
var (
err error
bufOffs uintptr
)
ws.dmaBuf, bufOffs, err = ws.mapMem(offset, int(unsafe.Sizeof(dmaT{})))
if err != nil {
return fmt.Errorf("couldn't map dmaT at %08X: %v", offset, err)
}
fmt.Printf("Got dmaBuf[%d], offset %d\n", len(ws.dmaBuf), bufOffs)
ws.dma = (*dmaT)(unsafe.Pointer(&ws.dmaBuf[bufOffs]))

ws.pwmBuf, bufOffs, err = ws.mapMem(PWM_OFFSET+ws.rp.periphBase, int(unsafe.Sizeof(pwmT{})))
if err != nil {
return fmt.Errorf("couldn't map pwmT at %08X: %v", PWM_OFFSET+ws.rp.periphBase, err)
}
fmt.Printf("Got pwmBuf[%d], offset %d\n", len(ws.pwmBuf), bufOffs)
ws.pwm = (*pwmT)(unsafe.Pointer(&ws.pwmBuf[bufOffs]))

ws.gpioBuf, bufOffs, err = ws.mapMem(GPIO_OFFSET+ws.rp.periphBase, int(unsafe.Sizeof(gpioT{})))
if err != nil {
return fmt.Errorf("couldn't map gpioT at %08X: %v", GPIO_OFFSET+ws.rp.periphBase, err)
}
fmt.Printf("Got gpioBuf[%d], offset %d\n", len(ws.gpioBuf), bufOffs)
ws.gpio = (*gpioT)(unsafe.Pointer(&ws.gpioBuf[bufOffs]))

ws.cmClkBuf, bufOffs, err = ws.mapMem(CM_PWM_OFFSET+ws.rp.periphBase, int(unsafe.Sizeof(cmClkT{})))
if err != nil {
return fmt.Errorf("couldn't map cmClkT at %08X: %v", CM_PWM_OFFSET+ws.rp.periphBase, err)
}
fmt.Printf("Got cmClkBuf[%d], offset %d\n", len(ws.cmClkBuf), bufOffs)
ws.cmClk = (*cmClkT)(unsafe.Pointer(&ws.cmClkBuf[bufOffs]))

return nil
}
65 changes: 65 additions & 0 deletions pixarray/ws281x_mbox.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package pixarray

import (
"fmt"
mmap "github.com/edsrzf/mmap-go"
"os"
"path"
"syscall"
Expand All @@ -10,6 +11,7 @@ import (

const (
VIDEOCORE_MAJOR_NUM = 100
MEM_FILE = "/dev/mem"
VCIO_FILE = "/dev/vcio"
MBOX_DEV = 100 << 20 // Assumes devices have 12-bit major, 20-bit minor numbers
MBOX_MODE = 0600
Expand All @@ -29,6 +31,32 @@ type dmaCallback struct {
resvd2 uint32
}

func (ws *WS281x) initDmaCb() {
ws.dmaCb = (*dmaCallback)(unsafe.Pointer(&ws.pixBuf[ws.pixBufOffs]))
}

func (ws *WS281x) busToPhys(busAddr uintptr) uintptr {
return busAddr &^ 0xC0000000
}

func (ws *WS281x) mapMem(physAddr uintptr, size int) (mmap.MMap, uintptr, error) {
pagemask := ^uintptr(PAGE_SIZE - 1)
f, err := os.OpenFile(MEM_FILE, os.O_RDWR|os.O_SYNC, os.ModePerm)
if err != nil {
return nil, 0, fmt.Errorf("couldn't open %s: %v", MEM_FILE, err)
}
mapAddr := physAddr & pagemask
size += int(physAddr - mapAddr)
fmt.Printf("MapRegion(f, %d, RDWR, 0, %08X), physAddr %08X, mask %08X\n", size, int64(mapAddr), physAddr, pagemask)
mm, err := mmap.MapRegion(f, size, mmap.RDWR, 0, int64(mapAddr))
if err != nil {
return nil, 0, fmt.Errorf("couldn't map region (%v, %v): %v", physAddr, size, err)
}
f.Close() // Ignore error

return mm, physAddr & (PAGE_SIZE - 1), nil
}

func (ws *WS281x) mboxOpenTemp() (*os.File, error) {
tf := path.Join(os.TempDir(), fmt.Sprintf("mailbox-%d", os.Getpid()))
err := os.Remove(tf)
Expand Down Expand Up @@ -245,3 +273,40 @@ func (ws *WS281x) lockMem(handle uintptr) (uintptr, error) {
}
return uintptr(p[5]), nil // 5 is the same place as handle above - first part of the tag value
}

func (ws *WS281x) unlockMem(handle uintptr) error {
i := uint32(0)
p := make([]uint32, 32)
p[i] = 0 // size
i++
p[i] = 0x00000000 // process request
i++

p[i] = 0x3000e // tag ID for "unlock memory"
i++
p[i] = 4 // size of the tag value to follow
i++
p[i] = 0 // afaict, mailbox.c has this wrong, we just need bit 31 clear, rest is reserved
i++

// tag value
p[i] = uint32(handle) // handle of the block we want to unlock
i++

p[i] = 0 // no more tags
i++

p[0] = i * 4 // actual size of the tag

err := ws.mboxProperty(p)
if err != nil {
return fmt.Errorf("mboxProperty failed: %v", err)
}
if p[4]&0x80000000 == 0 {
return fmt.Errorf("response tag unset: %v", p[4])
}
if p[5] != 0 {
return fmt.Errorf("status non-zero: %v", p[5])
}
return nil
}
3 changes: 2 additions & 1 deletion serve.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
var lpd8806Dev = flag.String("dev", "/dev/spidev0.0", "The SPI device on which LPD8806 LEDs are connected")
var lpd8806SpiSpeed = flag.Uint("spispeed", 1000000, "The speed to send data via SPI to LPD8806s, in Hz")
var ws281xFreq = flag.Uint("ws281xfreq", 800000, "The frequency to send data to WS2801x devices, in Hz")
var ws281xDma = flag.Int("ws281xdma", 10, "The DMA channel to use for sending data to WS281x devices")
var ledChip = flag.String("ledchip", "ws281x", "The type of LED strip to drive: one of ws281x, lpd8806")
var port = flag.Int("port", 24601, "The port that the server should listen to")
var pixels = flag.Int("pixels", 5*32, "The number of pixels to be controlled")
Expand Down Expand Up @@ -289,7 +290,7 @@ func main() {
log.Fatalf("Failed creating LPD8806: %v", err)
}
case "ws281x":
leds, err = pixarray.NewWS281x(*pixels, 3, order, *ws281xFreq)
leds, err = pixarray.NewWS281x(*pixels, 3, order, *ws281xFreq, *ws281xDma)
if err != nil {
log.Fatalf("Failed creating WS281x: %v", err)
}
Expand Down

0 comments on commit 0707e5d

Please sign in to comment.