search
LoginSignup
2

More than 5 years have passed since last update.

posted at

Golangでioctlのシステムコールを使う

組み込みLinuxでGolangを使っていると、ioctlのシステムコールを使用したい場面が出てきます。ioctlは汎用的で使用する場面によって異なるストラクチャを使用します。
Golangでカーネルの定義するストラクチャに値をセットする場合、以下の2つの方法があります。

  1. cgoを使ってカーネルのヘッダをインクルードする。
  2. Golangでメモリレイアウトが等価になるようなstructを定義して使用する。

今回は2の方法を試してみました。

i2cのioctlで使用されていたstruct

/* This is the structure as used in the I2C_RDWR ioctl call */
struct i2c_rdwr_ioctl_data {
    struct i2c_msg __user *msgs;    /* pointers to i2c_msgs */
    __u32 nmsgs;            /* number of i2c_msgs */
};
struct i2c_msg {
    __u16 addr; /* slave address            */
    __u16 flags;
#define I2C_M_TEN       0x0010  /* this is a ten bit chip address */
#define I2C_M_RD        0x0001  /* read data, from slave to master */
#define I2C_M_STOP      0x8000  /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_NOSTART       0x4000  /* if I2C_FUNC_NOSTART */
#define I2C_M_REV_DIR_ADDR  0x2000  /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_IGNORE_NAK    0x1000  /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_NO_RD_ACK     0x0800  /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_RECV_LEN      0x0400  /* length will be first received byte */
#define I2C_M_16BIT_REG     0x0002  /* indicate reg bit-width is 16bit */
#define I2C_M_16BIT_DATA    0x0008  /* indicate data bit-width is 16bit */
    __u16 len;      /* msg length               */
    __u8 *buf;      /* pointer to msg data          */
};

この2つのstructとメモリレイアウトが等価になるようなGoのstructを定義して使用しました。

Golangのサンプルプログラム

const (
    _I2C_RDWR                = 0x0707
    _I2C_RDRW_IOCTL_MAX_MSGS = 42
    _I2C_M_RD                = 0x0001
)

type i2c_msg struct {
    addr      uint16
    flags     uint16
    len       uint16
    __padding uint16
    buf       uintptr
}

type i2c_rdwr_ioctl_data struct {
    msgs  uintptr
    nmsgs uint32
}

func transfer(f *os.File, msgs *i2c_msg, n int) (err error) {
    data := i2c_rdwr_ioctl_data{
        msgs:  uintptr(unsafe.Pointer(msgs)),
        nmsgs: uint32(n),
    }
    err = nil
    _, _, errno := syscall.Syscall(
        syscall.SYS_IOCTL,
        uintptr(f.Fd()),
        uintptr(_I2C_RDWR),
        uintptr(unsafe.Pointer(&data)),
    )
    if (errno != 0) {
        err = errno
    }
    return
}

これの全ソースコードはgistに貼りました。ここ

余談

今回使用したSoCのi2cドライバが特殊なようで、一般のi2c用のダンプコマンド等が使用で来ませんでした。よって世の中にあるいくつかのgolangのi2c用ライブラリも使えませんでした。
SoCのSDKに付属するi2cツールで使っているシステムコールをstraceで観察し、それと同様の動きをするものをgolangで書きました。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
2