记录下首次使用反射特性。
还有两个比较坑的事情。一个是使用 unix.Syscall
,交叉编译到ARM平台时,指定 GOARCH=arm
,如果使用的是arm64平台,unix.Syscall
调用会失败,报错:inappropriate ioctl for device 。32位平台和64位平台在内核中使用ioctl不一样,如果内核中只实现了 unlocked_ioctl ,就会报上面的错误。因此需要指定 GOARCH=arm64
。第二个,也是指定了64位平台,那么golang下的int和uint将变成64bit,即8字节,因此如果要使用4字节的长度,最好直接用int32和uint32。
下面是使用反射的代码:
go
package main
import (
"flag"
"fmt"
"os"
"reflect"
"golang.org/x/sys/unix"
)
type Args struct {
calRead bool
calWrite bool
calFile string
dev string
}
var gArgs Args
type hudCal struct {
args *Args
devf *os.File
}
type IOC_C uint32
type IOC_T int32
const (
IOC_W = 1
IOC_R = 2
IOC_RW = 3
)
/**
* Notice!!! In arm64, int is int64!
*/
func parseArgs() {
flag.BoolVar(&gArgs.calRead, "r", false, "Read calibration data")
flag.BoolVar(&gArgs.calWrite, "w", false, "Write calibration data")
flag.StringVar(&gArgs.calFile, "f", "", "Calibration file")
flag.StringVar(&gArgs.dev, "d", "/dev/panel-03", "Panel device control node")
flag.Parse()
}
func IOC_CMD(rw IOC_T, magic byte, cmd byte, size any) IOC_C {
tp := reflect.TypeOf(size)
iocCmd := ((uint32(rw) & 0x3) << 30) | (uint32(magic) << 8) | uint32(cmd) | uint32((tp.Size()&0x3FFF)<<16)
fmt.Printf("ioctl cmd: %x(%b)\n", iocCmd, iocCmd)
return IOC_C(iocCmd)
}
func ioctl(f *os.File, cmd IOC_C, value any) error {
v := reflect.ValueOf(value)
if v.Kind() != reflect.Ptr || !v.Elem().CanSet() {
return fmt.Errorf("ioctl error: value isn't a pointer type or can't set")
}
_, _, errno := unix.Syscall(unix.SYS_IOCTL, f.Fd(), uintptr(cmd), uintptr(v.Pointer()))
if errno != 0 {
return fmt.Errorf("ioctl error: %v", errno)
}
return nil
}
func ioctlText(h *hudCal) {
var link int32
cmd := IOC_CMD(IOC_R, 'f', 50, link)
err := ioctl(h.devf, cmd, &link)
if err != nil {
fmt.Println("ioctl error:", err)
os.Exit(-1)
}
fmt.Println("link status: ", link)
}
func (h *hudCal) Open() error {
var err error
h.devf, err = os.OpenFile(gArgs.dev, os.O_RDWR, 0666)
return err
}
func (h *hudCal) Close() {
h.devf.Close()
}
func main() {
fmt.Println("hello world")
var hud hudCal
var err error
parseArgs()
hud.args = &gArgs
err = hud.Open()
if err != nil {
fmt.Println("hud open error: ", err)
}
defer hud.Close()
ioctlText(&hud)
}