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