unix: add EthtoolDrvinfo and IoctlGetEthtoolDrvinfo on linux

Add type EthtoolDrvinfo and the type-safe IoctlGetEthtoolDrvinfo ioctl
wrapper func to retrieve it.

Change-Id: Ia514df89175939d9b0c2c4ddace621882e86b21a
Reviewed-on: https://go-review.googlesource.com/c/sys/+/308989
Trust: Tobias Klauser <tobias.klauser@gmail.com>
Reviewed-by: Matt Layher <mdlayher@gmail.com>
Run-TryBot: Tobias Klauser <tobias.klauser@gmail.com>
TryBot-Result: Go Bot <gobot@golang.org>
diff --git a/unix/linux/types.go b/unix/linux/types.go
index 7efe890..17fbb72 100644
--- a/unix/linux/types.go
+++ b/unix/linux/types.go
@@ -3645,6 +3645,8 @@
 	ETHTOOL_A_TUNNEL_INFO_MAX                 = C.ETHTOOL_A_TUNNEL_INFO_MAX
 )
 
+type EthtoolDrvinfo C.struct_ethtool_drvinfo
+
 type (
 	HIDRawReportDescriptor C.struct_hidraw_report_descriptor
 	HIDRawDevInfo          C.struct_hidraw_devinfo
diff --git a/unix/mkpost.go b/unix/mkpost.go
index c6b8951..f1a92dd 100644
--- a/unix/mkpost.go
+++ b/unix/mkpost.go
@@ -95,6 +95,15 @@
 		b = bytes.Replace(b, s, newNames, 1)
 	}
 
+	// Convert []int8 to []byte in EthtoolDrvinfo
+	convertEthtoolDrvinfoNames := regexp.MustCompile(`(Driver|Version|Fw_version|Bus_info|Erom_version|Reserved2)(\s+)\[(\d+)\]u?int8`)
+	ethtoolDrvinfoTypes := regexp.MustCompile(`type EthtoolDrvinfo struct {[^}]*}`)
+	ethtoolDrvinfoStructs := ethtoolDrvinfoTypes.FindAll(b, -1)
+	for _, s := range ethtoolDrvinfoStructs {
+		newNames := convertEthtoolDrvinfoNames.ReplaceAll(s, []byte("$1$2[$3]byte"))
+		b = bytes.Replace(b, s, newNames, 1)
+	}
+
 	// Convert []int8 to []byte in ctl_info ioctl interface
 	convertCtlInfoName := regexp.MustCompile(`(Name)(\s+)\[(\d+)\]int8`)
 	ctlInfoType := regexp.MustCompile(`type CtlInfo struct {[^}]*}`)
diff --git a/unix/syscall_linux.go b/unix/syscall_linux.go
index fe37eaf..c61c963 100644
--- a/unix/syscall_linux.go
+++ b/unix/syscall_linux.go
@@ -106,6 +106,31 @@
 	return &value, err
 }
 
+type ifreqEthtool struct {
+	name [IFNAMSIZ]byte
+	data unsafe.Pointer
+}
+
+// IoctlGetEthtoolDrvinfo fetches ethtool driver information for the network
+// device specified by ifname.
+func IoctlGetEthtoolDrvinfo(fd int, ifname string) (*EthtoolDrvinfo, error) {
+	// Leave room for terminating NULL byte.
+	if len(ifname) >= IFNAMSIZ {
+		return nil, EINVAL
+	}
+
+	value := EthtoolDrvinfo{
+		Cmd: ETHTOOL_GDRVINFO,
+	}
+	ifreq := ifreqEthtool{
+		data: unsafe.Pointer(&value),
+	}
+	copy(ifreq.name[:], ifname)
+	err := ioctl(fd, SIOCETHTOOL, uintptr(unsafe.Pointer(&ifreq)))
+	runtime.KeepAlive(ifreq)
+	return &value, err
+}
+
 // IoctlGetWatchdogInfo fetches information about a watchdog device from the
 // Linux watchdog API. For more information, see:
 // https://www.kernel.org/doc/html/latest/watchdog/watchdog-api.html.
diff --git a/unix/ztypes_linux.go b/unix/ztypes_linux.go
index c769e73..3bfc6f7 100644
--- a/unix/ztypes_linux.go
+++ b/unix/ztypes_linux.go
@@ -3698,6 +3698,21 @@
 	ETHTOOL_A_TUNNEL_INFO_MAX                 = 0x2
 )
 
+type EthtoolDrvinfo struct {
+	Cmd          uint32
+	Driver       [32]byte
+	Version      [32]byte
+	Fw_version   [32]byte
+	Bus_info     [32]byte
+	Erom_version [32]byte
+	Reserved2    [12]byte
+	N_priv_flags uint32
+	N_stats      uint32
+	Testinfo_len uint32
+	Eedump_len   uint32
+	Regdump_len  uint32
+}
+
 type (
 	HIDRawReportDescriptor struct {
 		Size  uint32