Golang基于反射的ioctl实现

记录下首次使用反射特性。

还有两个比较坑的事情。一个是使用 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)
}
相关推荐
傻啦嘿哟13 小时前
一篇文章讲清楚Python的变量作用域
开发语言·python
devilnumber13 小时前
Java 二分查找(二分算法)详解 + 实战运用 + 核心坑点
java·开发语言·算法
仍然.13 小时前
SpringBoot快速上手
java·spring boot·后端
ch.ju14 小时前
Java程序设计(第3版)第四章——重载和覆盖的区别
java·开发语言
AI科技星14 小时前
第四卷:橡皮泥江湖(拓扑学)
c语言·开发语言·网络·量子计算·agi·拓扑学
浮尘笔记14 小时前
Go实现大文件异步流式采集引擎
开发语言·后端·golang
霸道流氓气质14 小时前
Spring Boot 大数据量 Excel 导入导出功能实现指南
spring boot·后端·excel
yugi98783814 小时前
基于C#实现数字识别率的OCR方案
开发语言·c#·ocr
星越华夏14 小时前
python中四种获取文件后缀名的方法
开发语言·python
小刘|14 小时前
Spring AI 结构化输出 + 大模型参数全解(含千问调优)
java·后端·spring