feat: port MT76 register and EFUSE access
Add the mt76::chip class on top of the USB transport: 32-bit register read/write via vendor requests, register polling, and EFUSE reads with the kick/control sequence from transport/mt76.c. Expose chip_id() and mac_address() (with the 62:45:bd fallback). Verified on hardware: chip ID 0x7612 (MT7612) and MAC address read from EFUSE. Co-Authored-By: qwen3.8-27b@q2_k_xl: ported register and EFUSE layer
This commit is contained in:
+114
-5
@@ -1,16 +1,125 @@
|
||||
// MT76 chip protocol (MediaTek MT76xx radio in the dongle)
|
||||
// TODO(phase 3): port from medusalix/xone transport/mt76.c.
|
||||
// Port of medusalix/xone transport/mt76.c (register + EFUSE layer).
|
||||
|
||||
#include "mt76/mt76.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include "common/log.hpp"
|
||||
#include "common/types.hpp"
|
||||
#include "mt76/mt76_defs.hpp"
|
||||
#include "usb/usb_transport.hpp"
|
||||
|
||||
namespace xone::mt76 {
|
||||
|
||||
auto load_firmware(std::uint16_t pid, char const *firmware_path) -> bool
|
||||
// Poll retry count (port of XONE_MT_POLL_RETRIES).
|
||||
constexpr int poll_retries = 50;
|
||||
|
||||
chip::chip(usb::transport &transport) : transport_(transport) {}
|
||||
|
||||
auto chip::read_register(std::uint32_t addr) -> std::uint32_t
|
||||
{
|
||||
(void)pid;
|
||||
(void)firmware_path;
|
||||
// Not implemented yet: no USB transport in place.
|
||||
auto req = usb::vendor_request::multi_read;
|
||||
if (addr & mt_vend_type_cfg) {
|
||||
req = usb::vendor_request::read_cfg;
|
||||
addr &= ~mt_vend_type_cfg;
|
||||
}
|
||||
|
||||
std::uint8_t buf[4] = {};
|
||||
int ret = transport_.send_vendor_request(req, true,
|
||||
static_cast<std::uint16_t>(addr >> 16),
|
||||
static_cast<std::uint16_t>(addr & 0xFFFF),
|
||||
buf, sizeof(buf));
|
||||
if (ret != static_cast<int>(sizeof(buf))) {
|
||||
xone::log_msg(log_level::error, "mt76: register read 0x%04x failed (%d)", addr, ret);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return xone::load_le32(buf);
|
||||
}
|
||||
|
||||
auto chip::write_register(std::uint32_t addr, std::uint32_t val) -> void
|
||||
{
|
||||
auto req = usb::vendor_request::multi_write;
|
||||
if (addr & mt_vend_type_cfg) {
|
||||
req = usb::vendor_request::write_cfg;
|
||||
addr &= ~mt_vend_type_cfg;
|
||||
}
|
||||
|
||||
std::uint8_t buf[4];
|
||||
xone::store_le32(buf, val);
|
||||
int ret = transport_.send_vendor_request(req, false,
|
||||
static_cast<std::uint16_t>(addr >> 16),
|
||||
static_cast<std::uint16_t>(addr & 0xFFFF),
|
||||
buf, sizeof(buf));
|
||||
if (ret != static_cast<int>(sizeof(buf)))
|
||||
xone::log_msg(log_level::error, "mt76: register write 0x%04x failed (%d)", addr, ret);
|
||||
}
|
||||
|
||||
auto chip::poll(std::uint32_t offset, std::uint32_t mask, std::uint32_t val) -> bool
|
||||
{
|
||||
for (int i = 0; i < poll_retries; i++) {
|
||||
auto reg = read_register(offset);
|
||||
if ((reg & mask) == val)
|
||||
return true;
|
||||
usleep(10000); // upstream: usleep_range(10000, 20000)
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
auto chip::read_efuse(std::uint16_t addr, void *data, std::size_t len) -> int
|
||||
{
|
||||
auto ctrl = read_register(mt_efuse_ctrl);
|
||||
ctrl &= ~(mt_efuse_ctrl_ain | mt_efuse_ctrl_mode);
|
||||
ctrl |= mt_efuse_ctrl_kick;
|
||||
ctrl |= field_prep(mt_efuse_ctrl_ain, static_cast<std::uint32_t>(addr) & ~0x0Fu);
|
||||
// efuse_read (mode 0) leaves the MODE bits cleared.
|
||||
write_register(mt_efuse_ctrl, ctrl);
|
||||
|
||||
if (!poll(mt_efuse_ctrl, mt_efuse_ctrl_kick, 0))
|
||||
return -ETIMEDOUT;
|
||||
|
||||
for (std::size_t i = 0; i < len; i += sizeof(std::uint32_t)) {
|
||||
// Block data offset (multiple of 32 bits)
|
||||
auto offset = static_cast<std::uint16_t>((addr & genmask(3, 2)) + i);
|
||||
std::uint8_t buf[4];
|
||||
xone::store_le32(buf, read_register(mt_efuse_data_base + offset));
|
||||
std::memcpy(static_cast<char *>(data) + i, buf,
|
||||
std::min(len - i, sizeof(std::uint32_t)));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto chip::chip_id() -> std::uint16_t
|
||||
{
|
||||
std::uint8_t id[4] = {};
|
||||
if (read_efuse(mt_ee_chip_id, id, sizeof(id)) != 0)
|
||||
return 0;
|
||||
|
||||
return static_cast<std::uint16_t>((id[1] << 8) | id[2]);
|
||||
}
|
||||
|
||||
auto chip::mac_address() -> std::array<std::uint8_t, 6>
|
||||
{
|
||||
auto address = std::array<std::uint8_t, 6>{};
|
||||
if (read_efuse(mt_ee_mac_addr, address.data(), address.size()) != 0)
|
||||
return address;
|
||||
|
||||
// Some addresses start with 6c:5d:3a.
|
||||
// Clients only connect to 62:45:bd:xx:xx:xx.
|
||||
if (address[0] != 0x62) {
|
||||
address[0] = 0x62;
|
||||
address[1] = 0x45;
|
||||
address[2] = 0xbd;
|
||||
}
|
||||
|
||||
return address;
|
||||
}
|
||||
|
||||
} // namespace xone::mt76
|
||||
|
||||
Reference in New Issue
Block a user