From 9de0b2c23ef2dfc4e71fd0334487c08a7f43d9d8 Mon Sep 17 00:00:00 2001 From: medusalix Date: Sun, 6 Feb 2022 11:36:24 +0100 Subject: [PATCH] Add wireless dongle driver --- Kbuild | 3 +- README.md | 37 +- dkms.conf | 12 +- install.sh | 11 + install/firmware.sh | 35 ++ install/modprobe.conf | 1 + transport/dongle.c | 984 +++++++++++++++++++++++++++++++++++ transport/mt76.c | 1142 +++++++++++++++++++++++++++++++++++++++++ transport/mt76.h | 65 +++ transport/mt76_defs.h | 1052 +++++++++++++++++++++++++++++++++++++ uninstall.sh | 1 + 11 files changed, 3334 insertions(+), 9 deletions(-) create mode 100644 install/firmware.sh create mode 100644 transport/dongle.c create mode 100644 transport/mt76.c create mode 100644 transport/mt76.h create mode 100644 transport/mt76_defs.h diff --git a/Kbuild b/Kbuild index 03133b5..70d56f7 100644 --- a/Kbuild +++ b/Kbuild @@ -1,7 +1,8 @@ xone-wired-y := transport/wired.o +xone-dongle-y := transport/dongle.o transport/mt76.o xone-gip-bus-y := bus/bus.o bus/protocol.o xone-gip-common-y := driver/common.o xone-gip-gamepad-y := driver/gamepad.o xone-gip-headset-y := driver/headset.o xone-gip-chatpad-y := driver/chatpad.o -obj-m := xone-wired.o xone-gip-bus.o xone-gip-common.o xone-gip-gamepad.o xone-gip-headset.o xone-gip-chatpad.o +obj-m := xone-wired.o xone-dongle.o xone-gip-bus.o xone-gip-common.o xone-gip-gamepad.o xone-gip-headset.o xone-gip-chatpad.o diff --git a/README.md b/README.md index 5e6c62a..6ebe9f5 100644 --- a/README.md +++ b/README.md @@ -20,8 +20,8 @@ Always update your Xbox devices to the latest firmware version! - [x] Battery reporting (`UPower` integration) - [x] LED control (using `/sys/class/leds`) - [x] Audio capture/playback (through `ALSA`) -- [x] Power management (suspend/resume and remote wakeup) -- [ ] Wireless connectivity (via dongle) +- [x] Power management (suspend/resume and remote/wireless wakeup) +- [x] Wired and wireless connectivity (via dongle) ## Supported devices @@ -38,6 +38,8 @@ Always update your Xbox devices to the latest firmware version! - [x] Xbox One Chatpad - [x] Xbox Adaptive Controller +⚠️ Standalone wireless headsets are currently not supported! + ## Releases [![Packaging status](https://repology.org/badge/vertical-allrepos/xone.svg)](https://repology.org/project/xone/versions) @@ -51,6 +53,8 @@ Any issues regarding the packaging should be reported to the respective maintain - Linux (kernel 4.15+ and headers) - DKMS +- curl (for firmware download) +- cabextract (for firmware extraction) ### Guide @@ -71,7 +75,15 @@ sudo ./install.sh --release **NOTE:** Please omit the `--release` flag when asked for your debug logs. -4. Plug in your Xbox devices. +4. Download the firmware for the wireless dongle: + +``` +sudo xone-get-firmware.sh +``` + +**NOTE:** The `--skip-disclaimer` flag might be useful for scripting purposes. + +5. Plug in your Xbox devices. ### Updating @@ -95,12 +107,31 @@ echo 5 | sudo tee /sys/class/leds/gip*/brightness Replace the wildcard (`gip*`) if you want to control the LED of a specific device. The modes and the maximum brightness can vary from device to device. +### Pairing mode + +The pairing mode of the dongle can be queried via `sysfs`: + +``` +cat /sys/bus/usb/drivers/xone-dongle/*/pairing +``` + +You can enable (`1`) or disable (`0`) the pairing using the following command: + +``` +echo 1 | sudo tee /sys/bus/usb/drivers/xone-dongle/*/pairing +``` + ## Troubleshooting Uninstall the release version and install a debug build of `xone` (see installation guide). Run `sudo dmesg` to gather logs and check for any error messages related to `xone`. If `xone` is not being loaded automatically you might have to reboot your system. +### Error messages + +- `Direct firmware load for xow_dongle.bin failed with error -2` + - Download the firmware for the wireless dongle (see installation guide). + ### Input issues You can use `evtest` to check if your input devices are working correctly. diff --git a/dkms.conf b/dkms.conf index 6998c30..bab2560 100644 --- a/dkms.conf +++ b/dkms.conf @@ -1,15 +1,17 @@ PACKAGE_NAME="xone" PACKAGE_VERSION="#VERSION#" BUILT_MODULE_NAME[0]="xone-wired" -BUILT_MODULE_NAME[1]="xone-gip-bus" -BUILT_MODULE_NAME[2]="xone-gip-common" -BUILT_MODULE_NAME[3]="xone-gip-gamepad" -BUILT_MODULE_NAME[4]="xone-gip-headset" -BUILT_MODULE_NAME[5]="xone-gip-chatpad" +BUILT_MODULE_NAME[1]="xone-dongle" +BUILT_MODULE_NAME[2]="xone-gip-bus" +BUILT_MODULE_NAME[3]="xone-gip-common" +BUILT_MODULE_NAME[4]="xone-gip-gamepad" +BUILT_MODULE_NAME[5]="xone-gip-headset" +BUILT_MODULE_NAME[6]="xone-gip-chatpad" DEST_MODULE_LOCATION[0]="/kernel/drivers/input/joystick" DEST_MODULE_LOCATION[1]="/kernel/drivers/input/joystick" DEST_MODULE_LOCATION[2]="/kernel/drivers/input/joystick" DEST_MODULE_LOCATION[3]="/kernel/drivers/input/joystick" DEST_MODULE_LOCATION[4]="/kernel/drivers/input/joystick" DEST_MODULE_LOCATION[5]="/kernel/drivers/input/joystick" +DEST_MODULE_LOCATION[6]="/kernel/drivers/input/joystick" AUTOINSTALL="yes" diff --git a/install.sh b/install.sh index c783ae9..e004aef 100755 --- a/install.sh +++ b/install.sh @@ -17,6 +17,11 @@ if [ -n "$(dkms status xone)" ]; then exit 1 fi +if [ -f /usr/local/bin/xow ]; then + echo 'Please uninstall xow!' >&2 + exit 1 +fi + version=$(git describe --tags 2> /dev/null || echo unknown) source="/usr/src/xone-$version" log="/var/lib/dkms/xone/$version/build/make.log" @@ -32,11 +37,17 @@ fi if dkms install -m xone -v "$version"; then # The blacklist should be placed in /usr/local/lib/modprobe.d for kmod 29+ install -D -m 644 install/modprobe.conf /etc/modprobe.d/xone-blacklist.conf + install -D -m 755 install/firmware.sh /usr/local/bin/xone-get-firmware.sh # Avoid conflicts between xpad and xone if lsmod | grep -q '^xpad'; then modprobe -r xpad fi + + # Avoid conflicts between mt76x2u and xone + if lsmod | grep -q '^mt76x2u'; then + modprobe -r mt76x2u + fi else if [ -r "$log" ]; then cat "$log" >&2 diff --git a/install/firmware.sh b/install/firmware.sh new file mode 100644 index 0000000..66adf52 --- /dev/null +++ b/install/firmware.sh @@ -0,0 +1,35 @@ +#!/usr/bin/env sh + +set -eu + +if [ "$(id -u)" -ne 0 ]; then + echo 'This script must be run as root!' >&2 + exit 1 +fi + +if ! [ -x "$(command -v curl)" ]; then + echo 'This script requires curl!' >&2 + exit 1 +fi + +if ! [ -x "$(command -v cabextract)" ]; then + echo 'This script requires cabextract!' >&2 + exit 1 +fi + +if [ "${1:-}" != --skip-disclaimer ]; then + echo "The firmware for the wireless dongle is subject to Microsoft's Terms of Use:" + echo 'https://www.microsoft.com/en-us/legal/terms-of-use' + echo + echo 'Press enter to continue!' + read -r _ +fi + +driver_url='http://download.windowsupdate.com/c/msdownload/update/driver/drvs/2017/07/1cd6a87c-623f-4407-a52d-c31be49e925c_e19f60808bdcbfbd3c3df6be3e71ffc52e43261e.cab' +firmware_hash='48084d9fa53b9bb04358f3bb127b7495dc8f7bb0b3ca1437bd24ef2b6eabdf66' + +curl -L -o driver.cab "$driver_url" +cabextract -F FW_ACC_00U.bin driver.cab +echo "$firmware_hash" FW_ACC_00U.bin | sha256sum -c +mv FW_ACC_00U.bin /lib/firmware/xow_dongle.bin +rm driver.cab diff --git a/install/modprobe.conf b/install/modprobe.conf index 76f498f..21855c8 100644 --- a/install/modprobe.conf +++ b/install/modprobe.conf @@ -1 +1,2 @@ blacklist xpad +blacklist mt76x2u diff --git a/transport/dongle.c b/transport/dongle.c new file mode 100644 index 0000000..fd44656 --- /dev/null +++ b/transport/dongle.c @@ -0,0 +1,984 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2021 Severin von Wnuck + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "mt76.h" +#include "../bus/bus.h" + +#define XONE_DONGLE_NUM_IN_URBS 12 +#define XONE_DONGLE_NUM_OUT_URBS 12 + +#define XONE_DONGLE_LEN_CMD_PKT 0x0654 +#define XONE_DONGLE_LEN_WLAN_PKT 0x8400 + +#define XONE_DONGLE_MAX_CLIENTS 16 + +#define XONE_DONGLE_PWR_OFF_TIMEOUT msecs_to_jiffies(5000) + +enum xone_dongle_queue { + XONE_DONGLE_QUEUE_DATA = 0x00, + XONE_DONGLE_QUEUE_AUDIO = 0x02, +}; + +struct xone_dongle_skb_cb { + struct xone_dongle *dongle; + struct urb *urb; +}; + +struct xone_dongle_client { + struct xone_dongle *dongle; + u8 wcid; + u8 address[ETH_ALEN]; + + struct gip_adapter *adapter; +}; + +struct xone_dongle_event { + enum xone_dongle_event_type { + XONE_DONGLE_EVT_ADD_CLIENT, + XONE_DONGLE_EVT_REMOVE_CLIENT, + XONE_DONGLE_EVT_PAIR_CLIENT, + XONE_DONGLE_EVT_TOGGLE_PAIRING, + } type; + + struct xone_dongle *dongle; + u8 address[ETH_ALEN]; + u8 wcid; + + struct work_struct work; +}; + +struct xone_dongle { + struct xone_mt76 mt; + + struct usb_anchor urbs_in_idle; + struct usb_anchor urbs_in_busy; + struct usb_anchor urbs_out_idle; + struct usb_anchor urbs_out_busy; + + /* serializes pairing changes */ + struct mutex pairing_lock; + bool pairing; + + /* serializes access to clients array */ + spinlock_t clients_lock; + struct xone_dongle_client *clients[XONE_DONGLE_MAX_CLIENTS]; + atomic_t client_count; + wait_queue_head_t disconnect_wait; + + struct workqueue_struct *event_wq; +}; + +static void xone_dongle_prep_packet(struct xone_dongle_client *client, + struct sk_buff *skb, + enum xone_dongle_queue queue) +{ + struct ieee80211_qos_hdr hdr = {}; + struct mt76_txwi txwi = {}; + u8 data[] = { + 0x00, 0x00, queue, client->wcid - 1, 0x00, 0x00, 0x00, 0x00, + }; + + /* frame is sent from AP (DS) */ + /* duration is the time required to transmit (in μs) */ + hdr.frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA | + IEEE80211_STYPE_QOS_DATA | + IEEE80211_FCTL_FROMDS); + hdr.duration_id = cpu_to_le16(144); + memcpy(hdr.addr1, client->address, ETH_ALEN); + memcpy(hdr.addr2, client->dongle->mt.address, ETH_ALEN); + memcpy(hdr.addr3, client->dongle->mt.address, ETH_ALEN); + + /* wait for acknowledgment */ + txwi.flags = cpu_to_le16(FIELD_PREP(MT_TXWI_FLAGS_MPDU_DENSITY, + IEEE80211_HT_MPDU_DENSITY_4)); + txwi.rate = cpu_to_le16(FIELD_PREP(MT_RXWI_RATE_PHY, MT_PHY_TYPE_OFDM)); + txwi.ack_ctl = MT_TXWI_ACK_CTL_REQ; + txwi.len_ctl = cpu_to_le16(sizeof(hdr) + skb->len); + + memset(skb_push(skb, 2), 0, 2); + memcpy(skb_push(skb, sizeof(hdr)), &hdr, sizeof(hdr)); + memcpy(skb_push(skb, sizeof(txwi)), &txwi, sizeof(txwi)); + memcpy(skb_push(skb, sizeof(data)), data, sizeof(data)); + + xone_mt76_prep_command(skb, 0); +} + +static int xone_dongle_get_buffer(struct gip_adapter *adap, + struct gip_adapter_buffer *buf) +{ + struct xone_dongle_client *client = dev_get_drvdata(&adap->dev); + struct xone_dongle_skb_cb *cb; + struct urb *urb; + struct sk_buff *skb; + + urb = usb_get_from_anchor(&client->dongle->urbs_out_idle); + if (!urb) + return -ENOSPC; + + skb = xone_mt76_alloc_message(XONE_DONGLE_LEN_CMD_PKT, GFP_ATOMIC); + if (!skb) + return -ENOMEM; + + /* command header + WCID data + TXWI + QoS header + padding */ + /* see xone_dongle_prep_packet and xone_mt76_prep_message */ + skb_reserve(skb, MT_CMD_HDR_LEN + 8 + sizeof(struct mt76_txwi) + + sizeof(struct ieee80211_qos_hdr) + 2 + MT_CMD_HDR_LEN); + + cb = (struct xone_dongle_skb_cb *)skb->cb; + cb->dongle = client->dongle; + cb->urb = urb; + + buf->context = skb; + buf->data = skb->data; + buf->length = skb->len; + + return 0; +} + +static int xone_dongle_submit_buffer(struct gip_adapter *adap, + struct gip_adapter_buffer *buf) +{ + struct xone_dongle_client *client = dev_get_drvdata(&adap->dev); + struct xone_dongle_skb_cb *cb; + struct sk_buff *skb = buf->context; + int err; + + skb_put(skb, buf->length); + + if (buf->type == GIP_BUF_DATA) + xone_dongle_prep_packet(client, skb, XONE_DONGLE_QUEUE_DATA); + else if (buf->type == GIP_BUF_AUDIO) + xone_dongle_prep_packet(client, skb, XONE_DONGLE_QUEUE_AUDIO); + else + return -EINVAL; + + cb = (struct xone_dongle_skb_cb *)skb->cb; + cb->urb->context = skb; + cb->urb->transfer_buffer = skb->data; + cb->urb->transfer_buffer_length = skb->len; + usb_anchor_urb(cb->urb, &client->dongle->urbs_out_busy); + + err = usb_submit_urb(cb->urb, GFP_ATOMIC); + if (err) { + usb_unanchor_urb(cb->urb); + usb_anchor_urb(cb->urb, &client->dongle->urbs_out_idle); + dev_kfree_skb_any(skb); + } + + usb_free_urb(cb->urb); + + return err; +} + +static struct gip_adapter_ops xone_dongle_adapter_ops = { + .get_buffer = xone_dongle_get_buffer, + .submit_buffer = xone_dongle_submit_buffer, +}; + +static int xone_dongle_toggle_pairing(struct xone_dongle *dongle, bool enable) +{ + int err = 0; + + mutex_lock(&dongle->pairing_lock); + + /* pairing is already enabled */ + if (dongle->pairing && enable) + goto err_unlock; + + err = xone_mt76_set_pairing(&dongle->mt, enable); + if (err) + goto err_unlock; + + err = xone_mt76_set_led_mode(&dongle->mt, enable ? XONE_MT_LED_BLINK : + XONE_MT_LED_OFF); + if (err) + goto err_unlock; + + dev_dbg(dongle->mt.dev, "%s: enabled=%d\n", __func__, enable); + dongle->pairing = enable; + +err_unlock: + mutex_unlock(&dongle->pairing_lock); + + return err; +} + +static ssize_t xone_dongle_pairing_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct usb_interface *intf = to_usb_interface(dev); + struct xone_dongle *dongle = usb_get_intfdata(intf); + + return sprintf(buf, "%d\n", dongle->pairing); +} + +static ssize_t xone_dongle_pairing_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct usb_interface *intf = to_usb_interface(dev); + struct xone_dongle *dongle = usb_get_intfdata(intf); + bool enable; + int err; + + err = kstrtobool(buf, &enable); + if (err) + return err; + + err = xone_dongle_toggle_pairing(dongle, enable); + if (err) + return err; + + return count; +} + +static struct device_attribute xone_dongle_attr_pairing = + __ATTR(pairing, 0644, + xone_dongle_pairing_show, + xone_dongle_pairing_store); + +static struct attribute *xone_dongle_attrs[] = { + &xone_dongle_attr_pairing.attr, + NULL, +}; +ATTRIBUTE_GROUPS(xone_dongle); + +static struct xone_dongle_client * +xone_dongle_create_client(struct xone_dongle *dongle, u8 *addr) +{ + struct xone_dongle_client *client; + int i, err; + + /* find free WCID */ + for (i = 0; i < XONE_DONGLE_MAX_CLIENTS; i++) + if (!dongle->clients[i]) + break; + + if (i == XONE_DONGLE_MAX_CLIENTS) + return ERR_PTR(-ENOSPC); + + client = kzalloc(sizeof(*client), GFP_KERNEL); + if (!client) + return ERR_PTR(-ENOMEM); + + client->dongle = dongle; + client->wcid = i + 1; + memcpy(client->address, addr, ETH_ALEN); + + client->adapter = gip_create_adapter(dongle->mt.dev, + &xone_dongle_adapter_ops, 1); + if (IS_ERR(client->adapter)) { + err = PTR_ERR(client->adapter); + kfree(client); + return ERR_PTR(err); + } + + dev_set_drvdata(&client->adapter->dev, client); + + return client; +} + +static int xone_dongle_add_client(struct xone_dongle *dongle, u8 *addr) +{ + struct xone_dongle_client *client; + int err; + unsigned long flags; + + client = xone_dongle_create_client(dongle, addr); + if (IS_ERR(client)) + return PTR_ERR(client); + + err = xone_mt76_associate_client(&dongle->mt, client->wcid, addr); + if (err) + goto err_free_client; + + err = xone_mt76_set_led_mode(&dongle->mt, XONE_MT_LED_ON); + if (err) + goto err_free_client; + + dev_dbg(dongle->mt.dev, "%s: wcid=%d, address=%pM\n", + __func__, client->wcid, addr); + + spin_lock_irqsave(&dongle->clients_lock, flags); + dongle->clients[client->wcid - 1] = client; + spin_unlock_irqrestore(&dongle->clients_lock, flags); + + atomic_inc(&dongle->client_count); + + return 0; + +err_free_client: + gip_destroy_adapter(client->adapter); + kfree(client); + + return err; +} + +static int xone_dongle_remove_client(struct xone_dongle *dongle, u8 wcid) +{ + struct xone_dongle_client *client; + int err; + unsigned long flags; + + client = dongle->clients[wcid - 1]; + if (!client) + return 0; + + dev_dbg(dongle->mt.dev, "%s: wcid=%d, address=%pM\n", + __func__, wcid, client->address); + + spin_lock_irqsave(&dongle->clients_lock, flags); + dongle->clients[wcid - 1] = NULL; + spin_unlock_irqrestore(&dongle->clients_lock, flags); + + gip_destroy_adapter(client->adapter); + kfree(client); + + err = xone_mt76_remove_client(&dongle->mt, wcid); + if (err) + dev_err(dongle->mt.dev, "%s: remove failed: %d\n", + __func__, err); + + /* turn off LED if all clients have disconnected */ + if (atomic_read(&dongle->client_count) == 1) + err = xone_mt76_set_led_mode(&dongle->mt, XONE_MT_LED_OFF); + + atomic_dec(&dongle->client_count); + wake_up(&dongle->disconnect_wait); + + return err; +} + +static int xone_dongle_pair_client(struct xone_dongle *dongle, u8 *addr) +{ + int err; + + dev_dbg(dongle->mt.dev, "%s: address=%pM\n", __func__, addr); + + err = xone_mt76_pair_client(&dongle->mt, addr); + if (err) + return err; + + return xone_dongle_toggle_pairing(dongle, false); +} + +static void xone_dongle_handle_event(struct work_struct *work) +{ + struct xone_dongle_event *evt = container_of(work, typeof(*evt), work); + int err; + + switch (evt->type) { + case XONE_DONGLE_EVT_ADD_CLIENT: + err = xone_dongle_add_client(evt->dongle, evt->address); + break; + case XONE_DONGLE_EVT_REMOVE_CLIENT: + err = xone_dongle_remove_client(evt->dongle, evt->wcid); + break; + case XONE_DONGLE_EVT_PAIR_CLIENT: + err = xone_dongle_pair_client(evt->dongle, evt->address); + break; + case XONE_DONGLE_EVT_TOGGLE_PAIRING: + err = xone_dongle_toggle_pairing(evt->dongle, true); + break; + } + + if (err) + dev_err(evt->dongle->mt.dev, "%s: handle event failed: %d\n", + __func__, err); + + kfree(evt); +} + +static struct xone_dongle_event * +xone_dongle_alloc_event(struct xone_dongle *dongle, + enum xone_dongle_event_type type) +{ + struct xone_dongle_event *evt; + + evt = kzalloc(sizeof(*evt), GFP_ATOMIC); + if (!evt) + return NULL; + + evt->type = type; + evt->dongle = dongle; + INIT_WORK(&evt->work, xone_dongle_handle_event); + + return evt; +} + +static int xone_dongle_handle_qos_data(struct xone_dongle *dongle, + struct sk_buff *skb, u8 wcid) +{ + struct xone_dongle_client *client; + int err = 0; + unsigned long flags; + + if (!wcid || wcid > XONE_DONGLE_MAX_CLIENTS) + return 0; + + spin_lock_irqsave(&dongle->clients_lock, flags); + + client = dongle->clients[wcid - 1]; + if (client) + err = gip_process_buffer(client->adapter, skb->data, skb->len); + + spin_unlock_irqrestore(&dongle->clients_lock, flags); + + return err; +} + +static int xone_dongle_handle_association(struct xone_dongle *dongle, u8 *addr) +{ + struct xone_dongle_event *evt; + + evt = xone_dongle_alloc_event(dongle, XONE_DONGLE_EVT_ADD_CLIENT); + if (!evt) + return -ENOMEM; + + memcpy(evt->address, addr, ETH_ALEN); + + queue_work(dongle->event_wq, &evt->work); + + return 0; +} + +static int xone_dongle_handle_disassociation(struct xone_dongle *dongle, + u8 wcid) +{ + struct xone_dongle_event *evt; + + if (!wcid || wcid > XONE_DONGLE_MAX_CLIENTS) + return 0; + + evt = xone_dongle_alloc_event(dongle, XONE_DONGLE_EVT_REMOVE_CLIENT); + if (!evt) + return -ENOMEM; + + evt->wcid = wcid; + + queue_work(dongle->event_wq, &evt->work); + + return 0; +} + +static int xone_dongle_handle_reserved(struct xone_dongle *dongle, + struct sk_buff *skb, u8 *addr) +{ + struct xone_dongle_event *evt; + + if (skb->len < 2) + return -EINVAL; + + if (skb->data[1] != 0x01) + return 0; + + evt = xone_dongle_alloc_event(dongle, XONE_DONGLE_EVT_PAIR_CLIENT); + if (!evt) + return -ENOMEM; + + memcpy(evt->address, addr, ETH_ALEN); + + queue_work(dongle->event_wq, &evt->work); + + return 0; +} + +static int xone_dongle_handle_button(struct xone_dongle *dongle) +{ + struct xone_dongle_event *evt; + + evt = xone_dongle_alloc_event(dongle, XONE_DONGLE_EVT_TOGGLE_PAIRING); + if (!evt) + return -ENOMEM; + + queue_work(dongle->event_wq, &evt->work); + + return 0; +} + +static int xone_dongle_handle_loss(struct xone_dongle *dongle, + struct sk_buff *skb) +{ + u8 wcid; + + if (skb->len < sizeof(wcid)) + return -EINVAL; + + wcid = skb->data[0]; + if (!wcid || wcid > XONE_DONGLE_MAX_CLIENTS) + return 0; + + dev_dbg(dongle->mt.dev, "%s: wcid=%d\n", __func__, wcid); + + return xone_dongle_handle_disassociation(dongle, wcid); +} + +static int xone_dongle_process_frame(struct xone_dongle *dongle, + struct sk_buff *skb, + unsigned int hdr_len, u8 wcid) +{ + struct ieee80211_hdr_3addr *hdr = + (struct ieee80211_hdr_3addr *)skb->data; + u16 type; + + if (skb->len < hdr_len) + return -EINVAL; + + /* ignore other frames */ + if (hdr_len < sizeof(*hdr)) + return 0; + + skb_pull(skb, hdr_len); + type = le16_to_cpu(hdr->frame_control); + + switch (type & (IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) { + case IEEE80211_FTYPE_DATA | IEEE80211_STYPE_QOS_DATA: + return xone_dongle_handle_qos_data(dongle, skb, wcid); + case IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ASSOC_REQ: + return xone_dongle_handle_association(dongle, hdr->addr2); + case IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_DISASSOC: + return xone_dongle_handle_disassociation(dongle, wcid); + case IEEE80211_FTYPE_MGMT | XONE_MT_WLAN_RESERVED: + return xone_dongle_handle_reserved(dongle, skb, hdr->addr2); + } + + return 0; +} + +static int xone_dongle_process_wlan(struct xone_dongle *dongle, + struct sk_buff *skb) +{ + struct mt76_rxwi *rxwi = (struct mt76_rxwi *)skb->data; + unsigned int hdr_len; + u32 ctl; + + if (skb->len < sizeof(*rxwi)) + return -EINVAL; + + skb_pull(skb, sizeof(*rxwi)); + hdr_len = ieee80211_get_hdrlen_from_skb(skb); + + /* 2 bytes of padding after 802.11 header */ + if (rxwi->rxinfo & cpu_to_le32(MT_RXINFO_L2PAD)) { + if (skb->len < hdr_len + 2) + return -EINVAL; + + memmove(skb->data + 2, skb->data, hdr_len); + skb_pull(skb, 2); + } + + ctl = le32_to_cpu(rxwi->ctl); + skb_trim(skb, FIELD_GET(MT_RXWI_CTL_MPDU_LEN, ctl)); + + return xone_dongle_process_frame(dongle, skb, hdr_len, + FIELD_GET(MT_RXWI_CTL_WCID, ctl)); +} + +static int xone_dongle_process_message(struct xone_dongle *dongle, + struct sk_buff *skb) +{ + enum mt76_dma_msg_port port; + u32 info; + + /* command header + trailer */ + if (skb->len < MT_CMD_HDR_LEN * 2) + return -EINVAL; + + info = get_unaligned_le32(skb->data); + port = FIELD_GET(MT_RX_FCE_INFO_D_PORT, info); + + /* ignore command reponses */ + if (FIELD_GET(MT_RX_FCE_INFO_CMD_SEQ, info) == 0x01) + return 0; + + /* remove header + trailer */ + skb_pull(skb, MT_CMD_HDR_LEN); + skb_trim(skb, skb->len - MT_CMD_HDR_LEN); + + if (port == MT_WLAN_PORT) + return xone_dongle_process_wlan(dongle, skb); + + if (port != MT_CPU_RX_PORT) + return 0; + + switch (FIELD_GET(MT_RX_FCE_INFO_EVT_TYPE, info)) { + case XONE_MT_EVT_BUTTON: + return xone_dongle_handle_button(dongle); + case XONE_MT_EVT_PACKET_RX: + return xone_dongle_process_wlan(dongle, skb); + case XONE_MT_EVT_CLIENT_LOST: + return xone_dongle_handle_loss(dongle, skb); + } + + return 0; +} + +static int xone_dongle_process_buffer(struct xone_dongle *dongle, + void *data, int len) +{ + struct sk_buff *skb; + int err; + + if (!len) + return 0; + + skb = dev_alloc_skb(len); + if (!skb) + return -ENOMEM; + + skb_put_data(skb, data, len); + + err = xone_dongle_process_message(dongle, skb); + if (err) { + dev_err(dongle->mt.dev, "%s: process failed: %d\n", + __func__, err); + print_hex_dump_debug("xone-dongle packet: ", DUMP_PREFIX_NONE, + 16, 1, data, len, false); + } + + dev_kfree_skb(skb); + + return err; +} + +static void xone_dongle_complete_in(struct urb *urb) +{ + struct xone_dongle *dongle = urb->context; + int err; + + switch (urb->status) { + case 0: + break; + case -ENOENT: + case -ECONNRESET: + case -ESHUTDOWN: + usb_anchor_urb(urb, &dongle->urbs_in_idle); + return; + default: + goto resubmit; + } + + err = xone_dongle_process_buffer(dongle, urb->transfer_buffer, + urb->actual_length); + if (err) + dev_err(dongle->mt.dev, "%s: process failed: %d\n", + __func__, err); + +resubmit: + /* can fail during USB device removal */ + err = usb_submit_urb(urb, GFP_ATOMIC); + if (err) { + dev_dbg(dongle->mt.dev, "%s: submit failed: %d\n", + __func__, err); + usb_anchor_urb(urb, &dongle->urbs_in_idle); + } else { + usb_anchor_urb(urb, &dongle->urbs_in_busy); + } +} + +static void xone_dongle_complete_out(struct urb *urb) +{ + struct sk_buff *skb = urb->context; + struct xone_dongle_skb_cb *cb = (struct xone_dongle_skb_cb *)skb->cb; + + usb_anchor_urb(urb, &cb->dongle->urbs_out_idle); + dev_consume_skb_any(skb); +} + +static int xone_dongle_init_urbs_in(struct xone_dongle *dongle, + int ep, int buf_len) +{ + struct xone_mt76 *mt = &dongle->mt; + struct urb *urb; + void *buf; + int i, err; + + for (i = 0; i < XONE_DONGLE_NUM_IN_URBS; i++) { + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) + return -ENOMEM; + + usb_anchor_urb(urb, &dongle->urbs_in_busy); + usb_free_urb(urb); + + buf = usb_alloc_coherent(mt->udev, buf_len, + GFP_KERNEL, &urb->transfer_dma); + if (!buf) + return -ENOMEM; + + usb_fill_bulk_urb(urb, mt->udev, + usb_rcvbulkpipe(mt->udev, ep), buf, buf_len, + xone_dongle_complete_in, dongle); + urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + err = usb_submit_urb(urb, GFP_KERNEL); + if (err) + return err; + } + + return 0; +} + +static int xone_dongle_init_urbs_out(struct xone_dongle *dongle) +{ + struct xone_mt76 *mt = &dongle->mt; + struct urb *urb; + int i; + + for (i = 0; i < XONE_DONGLE_NUM_OUT_URBS; i++) { + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) + return -ENOMEM; + + usb_fill_bulk_urb(urb, mt->udev, + usb_sndbulkpipe(mt->udev, XONE_MT_EP_OUT), + NULL, 0, xone_dongle_complete_out, NULL); + usb_anchor_urb(urb, &dongle->urbs_out_idle); + usb_free_urb(urb); + } + + return 0; +} + +static int xone_dongle_init(struct xone_dongle *dongle) +{ + struct xone_mt76 *mt = &dongle->mt; + int err; + + init_usb_anchor(&dongle->urbs_out_idle); + init_usb_anchor(&dongle->urbs_out_busy); + init_usb_anchor(&dongle->urbs_in_idle); + init_usb_anchor(&dongle->urbs_in_busy); + + err = xone_dongle_init_urbs_out(dongle); + if (err) + return err; + + err = xone_dongle_init_urbs_in(dongle, XONE_MT_EP_IN_CMD, + XONE_DONGLE_LEN_CMD_PKT); + if (err) + return err; + + err = xone_dongle_init_urbs_in(dongle, XONE_MT_EP_IN_WLAN, + XONE_DONGLE_LEN_WLAN_PKT); + if (err) + return err; + + err = xone_mt76_load_firmware(mt, "xow_dongle.bin"); + if (err) { + dev_err(mt->dev, "%s: load firmware failed: %d\n", + __func__, err); + return err; + } + + err = xone_mt76_init_radio(mt); + if (err) + dev_err(mt->dev, "%s: init radio failed: %d\n", __func__, err); + + return err; +} + +static int xone_dongle_power_off_clients(struct xone_dongle *dongle) +{ + struct xone_dongle_client *client; + int i; + int err = 0; + unsigned long flags; + + spin_lock_irqsave(&dongle->clients_lock, flags); + + for (i = 0; i < XONE_DONGLE_MAX_CLIENTS; i++) { + client = dongle->clients[i]; + if (!client) + continue; + + err = gip_power_off_adapter(client->adapter); + if (err) + break; + } + + spin_unlock_irqrestore(&dongle->clients_lock, flags); + + if (err) + return err; + + /* can time out if new client connects */ + if (!wait_event_timeout(dongle->disconnect_wait, + !atomic_read(&dongle->client_count), + XONE_DONGLE_PWR_OFF_TIMEOUT)) + return -ETIMEDOUT; + + return 0; +} + +static void xone_dongle_destroy(struct xone_dongle *dongle) +{ + struct xone_dongle_client *client; + struct urb *urb; + int i; + + usb_kill_anchored_urbs(&dongle->urbs_in_busy); + destroy_workqueue(dongle->event_wq); + + for (i = 0; i < XONE_DONGLE_MAX_CLIENTS; i++) { + client = dongle->clients[i]; + if (!client) + continue; + + gip_destroy_adapter(client->adapter); + kfree(client); + dongle->clients[i] = NULL; + } + + usb_kill_anchored_urbs(&dongle->urbs_out_busy); + + while ((urb = usb_get_from_anchor(&dongle->urbs_out_idle))) + usb_free_urb(urb); + + while ((urb = usb_get_from_anchor(&dongle->urbs_in_idle))) { + usb_free_coherent(urb->dev, urb->transfer_buffer_length, + urb->transfer_buffer, urb->transfer_dma); + usb_free_urb(urb); + } + + mutex_destroy(&dongle->pairing_lock); +} + +static int xone_dongle_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct xone_dongle *dongle; + int err; + + dongle = devm_kzalloc(&intf->dev, sizeof(*dongle), GFP_KERNEL); + if (!dongle) + return -ENOMEM; + + dongle->mt.dev = &intf->dev; + dongle->mt.udev = interface_to_usbdev(intf); + + usb_reset_device(dongle->mt.udev); + + /* enable USB remote wakeup feature */ + device_wakeup_enable(&dongle->mt.udev->dev); + + dongle->event_wq = alloc_ordered_workqueue("xone_dongle", 0); + if (!dongle->event_wq) + return -ENOMEM; + + mutex_init(&dongle->pairing_lock); + spin_lock_init(&dongle->clients_lock); + init_waitqueue_head(&dongle->disconnect_wait); + + err = xone_dongle_init(dongle); + if (err) { + xone_dongle_destroy(dongle); + return err; + } + + usb_set_intfdata(intf, dongle); + + return 0; +} + +static void xone_dongle_disconnect(struct usb_interface *intf) +{ + struct xone_dongle *dongle = usb_get_intfdata(intf); + int err; + + /* can fail during USB device removal */ + err = xone_dongle_power_off_clients(dongle); + if (err) + dev_dbg(dongle->mt.dev, "%s: power off failed: %d\n", + __func__, err); + + xone_dongle_destroy(dongle); + usb_set_intfdata(intf, NULL); +} + +static int xone_dongle_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct xone_dongle *dongle = usb_get_intfdata(intf); + int err; + + err = xone_dongle_power_off_clients(dongle); + if (err) + dev_err(dongle->mt.dev, "%s: power off failed: %d\n", + __func__, err); + + usb_kill_anchored_urbs(&dongle->urbs_in_busy); + usb_kill_anchored_urbs(&dongle->urbs_out_busy); + flush_workqueue(dongle->event_wq); + + return xone_mt76_suspend_radio(&dongle->mt); +} + +static int xone_dongle_resume(struct usb_interface *intf) +{ + struct xone_dongle *dongle = usb_get_intfdata(intf); + struct urb *urb; + int err; + + while ((urb = usb_get_from_anchor(&dongle->urbs_in_idle))) { + usb_anchor_urb(urb, &dongle->urbs_in_busy); + usb_free_urb(urb); + + err = usb_submit_urb(urb, GFP_KERNEL); + if (err) + return err; + } + + return xone_mt76_resume_radio(&dongle->mt); +} + +static void xone_dongle_shutdown(struct device *dev) +{ + struct usb_interface *intf = to_usb_interface(dev); + struct xone_dongle *dongle = usb_get_intfdata(intf); + int err; + + err = xone_dongle_power_off_clients(dongle); + if (err) + dev_err(dongle->mt.dev, "%s: power off failed: %d\n", + __func__, err); +} + +static const struct usb_device_id xone_dongle_id_table[] = { + { USB_DEVICE(0x045e, 0x02e6) }, /* old dongle */ + { USB_DEVICE(0x045e, 0x02fe) }, /* new dongle */ + { USB_DEVICE(0x045e, 0x02f9) }, /* built-in dongle (ASUS, Lenovo) */ + { USB_DEVICE(0x045e, 0x091e) }, /* built-in dongle (Surface Book 2) */ + { }, +}; + +static struct usb_driver xone_dongle_driver = { + .name = "xone-dongle", + .probe = xone_dongle_probe, + .disconnect = xone_dongle_disconnect, + .suspend = xone_dongle_suspend, + .resume = xone_dongle_resume, + .id_table = xone_dongle_id_table, + .dev_groups = xone_dongle_groups, + .drvwrap.driver.shutdown = xone_dongle_shutdown, + .soft_unbind = 1, + .disable_hub_initiated_lpm = 1, +}; + +module_usb_driver(xone_dongle_driver); + +MODULE_DEVICE_TABLE(usb, xone_dongle_id_table); +MODULE_AUTHOR("Severin von Wnuck "); +MODULE_DESCRIPTION("xone dongle driver"); +MODULE_VERSION("#VERSION#"); +MODULE_LICENSE("GPL"); diff --git a/transport/mt76.c b/transport/mt76.c new file mode 100644 index 0000000..bc3d4e9 --- /dev/null +++ b/transport/mt76.c @@ -0,0 +1,1142 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2021 Severin von Wnuck + */ + +#include +#include +#include +#include +#include +#include + +#include "mt76.h" + +/* bulk transfer timeout in ms */ +#define XONE_MT_USB_TIMEOUT 1000 + +#define XONE_MT_POLL_RETRIES 50 + +#define XONE_MT_RF_PATCH 0x0130 +#define XONE_MT_FW_LOAD_IVB 0x12 +#define XONE_MT_FW_ILM_OFFSET 0x080000 +#define XONE_MT_FW_DLM_OFFSET 0x110800 +#define XONE_MT_FW_CHUNK_SIZE 0x3800 + +/* wireless channel bands */ +#define XONE_MT_CH_2G_LOW 0x01 +#define XONE_MT_CH_2G_MID 0x02 +#define XONE_MT_CH_2G_HIGH 0x03 +#define XONE_MT_CH_5G_LOW 0x01 +#define XONE_MT_CH_5G_HIGH 0x02 + +/* commands specific to the dongle's firmware */ +enum xone_mt76_ms_command { + XONE_MT_SET_MAC_ADDRESS = 0x00, + XONE_MT_ADD_CLIENT = 0x01, + XONE_MT_REMOVE_CLIENT = 0x02, + XONE_MT_SET_IDLE_TIME = 0x05, + XONE_MT_SET_CHAN_CANDIDATES = 0x07, +}; + +enum xone_mt76_wow_feature { + XONE_MT_WOW_ENABLE = 0x01, + XONE_MT_WOW_TRAFFIC = 0x03, +}; + +enum xone_mt76_wow_traffic { + XONE_MT_WOW_TO_FIRMWARE = 0x00, + XONE_MT_WOW_TO_HOST = 0x01, +}; + +struct xone_mt76_msg_load_cr { + u8 mode; + u8 temperature; + u8 channel; + u8 padding; +} __packed; + +struct xone_mt76_msg_switch_channel { + u8 channel; + u8 padding1[3]; + __le16 tx_rx_setting; + u8 padding2[10]; + u8 bandwidth; + u8 tx_power; + u8 scan; + u8 unknown; +} __packed; + +static u32 xone_mt76_read_register(struct xone_mt76 *mt, u32 addr) +{ + u8 req = MT_VEND_MULTI_READ; + int ret; + + if (addr & MT_VEND_TYPE_CFG) { + req = MT_VEND_READ_CFG; + addr &= ~MT_VEND_TYPE_CFG; + } + + ret = usb_control_msg(mt->udev, usb_rcvctrlpipe(mt->udev, 0), req, + USB_DIR_IN | USB_TYPE_VENDOR, addr >> 16, addr, + &mt->control_data, sizeof(mt->control_data), + XONE_MT_USB_TIMEOUT); + if (ret != sizeof(mt->control_data)) + ret = -EREMOTEIO; + + if (ret < 0) { + dev_err(mt->dev, "%s: control message failed: %d\n", + __func__, ret); + return 0; + } + + return le32_to_cpu(mt->control_data); +} + +static void xone_mt76_write_register(struct xone_mt76 *mt, u32 addr, u32 val) +{ + u8 req = MT_VEND_MULTI_WRITE; + int ret; + + if (addr & MT_VEND_TYPE_CFG) { + req = MT_VEND_WRITE_CFG; + addr &= ~MT_VEND_TYPE_CFG; + } + + mt->control_data = cpu_to_le32(val); + + ret = usb_control_msg(mt->udev, usb_sndctrlpipe(mt->udev, 0), req, + USB_DIR_OUT | USB_TYPE_VENDOR, addr >> 16, addr, + &mt->control_data, sizeof(mt->control_data), + XONE_MT_USB_TIMEOUT); + if (ret != sizeof(mt->control_data)) + ret = -EREMOTEIO; + + if (ret < 0) + dev_err(mt->dev, "%s: control message failed: %d\n", + __func__, ret); +} + +static int xone_mt76_load_ivb(struct xone_mt76 *mt) +{ + /* load interrupt vector block */ + return usb_control_msg(mt->udev, usb_sndctrlpipe(mt->udev, 0), + MT_VEND_DEV_MODE, USB_DIR_OUT | USB_TYPE_VENDOR, + XONE_MT_FW_LOAD_IVB, 0, NULL, 0, + XONE_MT_USB_TIMEOUT); +} + +static bool xone_mt76_poll(struct xone_mt76 *mt, u32 offset, u32 mask, u32 val) +{ + int i; + u32 reg; + + for (i = 0; i < XONE_MT_POLL_RETRIES; i++) { + reg = xone_mt76_read_register(mt, offset); + if ((reg & mask) == val) + return true; + + usleep_range(10000, 20000); + } + + return false; +} + +static int xone_mt76_read_efuse(struct xone_mt76 *mt, u16 addr, + void *data, int len) +{ + u32 ctrl, offset, val; + int i, remaining; + + ctrl = xone_mt76_read_register(mt, MT_EFUSE_CTRL); + ctrl &= ~(MT_EFUSE_CTRL_AIN | MT_EFUSE_CTRL_MODE); + ctrl |= MT_EFUSE_CTRL_KICK; + ctrl |= FIELD_PREP(MT_EFUSE_CTRL_AIN, addr & ~0x0f); + ctrl |= FIELD_PREP(MT_EFUSE_CTRL_MODE, MT_EE_READ); + xone_mt76_write_register(mt, MT_EFUSE_CTRL, ctrl); + + if (!xone_mt76_poll(mt, MT_EFUSE_CTRL, MT_EFUSE_CTRL_KICK, 0)) + return -ETIMEDOUT; + + for (i = 0; i < len; i += sizeof(u32)) { + /* block data offset (multiple of 32 bits) */ + offset = (addr & GENMASK(3, 2)) + i; + val = xone_mt76_read_register(mt, MT_EFUSE_DATA_BASE + offset); + remaining = min_t(int, len - i, sizeof(u32)); + + memcpy(data + i, &val, remaining); + } + + return 0; +} + +struct sk_buff *xone_mt76_alloc_message(int len, gfp_t gfp) +{ + struct sk_buff *skb; + + /* up to 4 bytes of padding */ + skb = alloc_skb(MT_CMD_HDR_LEN + len + sizeof(u32) + MT_CMD_HDR_LEN, + gfp); + if (!skb) + return NULL; + + skb_reserve(skb, MT_CMD_HDR_LEN); + + return skb; +} + +void xone_mt76_prep_message(struct sk_buff *skb, u32 info) +{ + int len, pad; + + /* padding and trailer */ + len = round_up(skb->len, sizeof(u32)); + pad = len - skb->len + MT_CMD_HDR_LEN; + + put_unaligned_le32(info | FIELD_PREP(MT_MCU_MSG_LEN, len), + skb_push(skb, MT_CMD_HDR_LEN)); + memset(skb_put(skb, pad), 0, pad); +} + +void xone_mt76_prep_command(struct sk_buff *skb, enum mt76_mcu_cmd cmd) +{ + xone_mt76_prep_message(skb, MT_MCU_MSG_TYPE_CMD | + FIELD_PREP(MT_MCU_MSG_PORT, MT_CPU_TX_PORT) | + FIELD_PREP(MT_MCU_MSG_CMD_TYPE, cmd)); +} + +static int xone_mt76_send_command(struct xone_mt76 *mt, struct sk_buff *skb, + enum mt76_mcu_cmd cmd) +{ + int err; + + xone_mt76_prep_command(skb, cmd); + + err = usb_bulk_msg(mt->udev, usb_sndbulkpipe(mt->udev, XONE_MT_EP_OUT), + skb->data, skb->len, NULL, XONE_MT_USB_TIMEOUT); + consume_skb(skb); + + return err; +} + +static int xone_mt76_send_wlan(struct xone_mt76 *mt, struct sk_buff *skb) +{ + struct mt76_txwi txwi = {}; + int err; + + /* wait for acknowledgment */ + /* ignore wireless client identifier (WCID) */ + txwi.flags = cpu_to_le16(FIELD_PREP(MT_TXWI_FLAGS_MPDU_DENSITY, + IEEE80211_HT_MPDU_DENSITY_4)); + txwi.rate = cpu_to_le16(FIELD_PREP(MT_RXWI_RATE_PHY, MT_PHY_TYPE_OFDM)); + txwi.ack_ctl = MT_TXWI_ACK_CTL_REQ; + txwi.wcid = 0xff; + txwi.len_ctl = cpu_to_le16(skb->len); + + memcpy(skb_push(skb, sizeof(txwi)), &txwi, sizeof(txwi)); + + /* enhanced distributed channel access (EDCA) */ + /* wireless information valid (WIV) */ + xone_mt76_prep_message(skb, + FIELD_PREP(MT_TXD_INFO_DPORT, MT_WLAN_PORT) | + FIELD_PREP(MT_TXD_INFO_QSEL, MT_QSEL_EDCA) | + MT_TXD_INFO_WIV | + MT_TXD_INFO_80211); + + err = usb_bulk_msg(mt->udev, usb_sndbulkpipe(mt->udev, XONE_MT_EP_OUT), + skb->data, skb->len, NULL, XONE_MT_USB_TIMEOUT); + consume_skb(skb); + + return err; +} + +static int xone_mt76_select_function(struct xone_mt76 *mt, + enum mt76_mcu_function func, u32 val) +{ + struct sk_buff *skb; + + skb = xone_mt76_alloc_message(sizeof(u32) * 2, GFP_KERNEL); + if (!skb) + return -ENOMEM; + + put_unaligned_le32(func, skb_put(skb, sizeof(u32))); + put_unaligned_le32(val, skb_put(skb, sizeof(u32))); + + return xone_mt76_send_command(mt, skb, MT_CMD_FUN_SET_OP); +} + +static int xone_mt76_load_cr(struct xone_mt76 *mt, enum mt76_mcu_cr_mode mode) +{ + struct sk_buff *skb; + struct xone_mt76_msg_load_cr msg = {}; + + skb = xone_mt76_alloc_message(sizeof(msg), GFP_KERNEL); + if (!skb) + return -ENOMEM; + + msg.mode = mode; + skb_put_data(skb, &msg, sizeof(msg)); + + return xone_mt76_send_command(mt, skb, MT_CMD_LOAD_CR); +} + +static int xone_mt76_send_ms_command(struct xone_mt76 *mt, + enum xone_mt76_ms_command cmd, + void *data, int len) +{ + struct sk_buff *skb; + + skb = xone_mt76_alloc_message(sizeof(u32) + len, GFP_KERNEL); + if (!skb) + return -ENOMEM; + + put_unaligned_le32(cmd, skb_put(skb, sizeof(u32))); + skb_put_data(skb, data, len); + + /* send command to Microsoft's proprietary firmware */ + return xone_mt76_send_command(mt, skb, MT_CMD_INIT_GAIN_OP); +} + +static int xone_mt76_write_burst(struct xone_mt76 *mt, u32 idx, + void *data, int len) +{ + struct sk_buff *skb; + + skb = xone_mt76_alloc_message(sizeof(idx) + len, GFP_KERNEL); + if (!skb) + return -ENOMEM; + + /* register offset in memory */ + put_unaligned_le32(idx + MT_MCU_MEMMAP_WLAN, skb_put(skb, sizeof(idx))); + skb_put_data(skb, data, len); + + return xone_mt76_send_command(mt, skb, MT_CMD_BURST_WRITE); +} + +int xone_mt76_set_led_mode(struct xone_mt76 *mt, enum xone_mt76_led_mode mode) +{ + struct sk_buff *skb; + + skb = xone_mt76_alloc_message(sizeof(u32), GFP_KERNEL); + if (!skb) + return -ENOMEM; + + put_unaligned_le32(mode, skb_put(skb, sizeof(u32))); + + return xone_mt76_send_command(mt, skb, MT_CMD_LED_MODE_OP); +} + +static int xone_mt76_set_power_mode(struct xone_mt76 *mt, + enum mt76_mcu_power_mode mode) +{ + struct sk_buff *skb; + + skb = xone_mt76_alloc_message(sizeof(u32), GFP_KERNEL); + if (!skb) + return -ENOMEM; + + put_unaligned_le32(mode, skb_put(skb, sizeof(u32))); + + return xone_mt76_send_command(mt, skb, MT_CMD_POWER_SAVING_OP); +} + +static int xone_mt76_set_wow_enable(struct xone_mt76 *mt, bool enable) +{ + struct sk_buff *skb; + + skb = xone_mt76_alloc_message(sizeof(u32) + sizeof(u8) * 2, GFP_KERNEL); + if (!skb) + return -ENOMEM; + + put_unaligned_le32(XONE_MT_WOW_ENABLE, skb_put(skb, sizeof(u32))); + skb_put_u8(skb, enable); + skb_put_u8(skb, mt->channel->index); + + return xone_mt76_send_command(mt, skb, MT_CMD_WOW_FEATURE); +} + +static int xone_mt76_set_wow_traffic(struct xone_mt76 *mt, + enum xone_mt76_wow_traffic traffic) +{ + struct sk_buff *skb; + + skb = xone_mt76_alloc_message(sizeof(u32) + sizeof(u8), GFP_KERNEL); + if (!skb) + return -ENOMEM; + + put_unaligned_le32(XONE_MT_WOW_TRAFFIC, skb_put(skb, sizeof(u32))); + skb_put_u8(skb, traffic); + + return xone_mt76_send_command(mt, skb, MT_CMD_WOW_FEATURE); +} + +static int xone_mt76_switch_channel(struct xone_mt76 *mt, + struct xone_mt76_channel *chan) +{ + struct sk_buff *skb; + struct xone_mt76_msg_switch_channel msg = {}; + + skb = xone_mt76_alloc_message(sizeof(msg), GFP_KERNEL); + if (!skb) + return -ENOMEM; + + /* select TX and RX stream 1 */ + /* enable or disable scanning (unknown purpose) */ + msg.channel = chan->index; + msg.tx_rx_setting = cpu_to_le16(0x0101); + msg.bandwidth = chan->bandwidth; + msg.tx_power = chan->power; + msg.scan = chan->scan; + skb_put_data(skb, &msg, sizeof(msg)); + + return xone_mt76_send_command(mt, skb, MT_CMD_SWITCH_CHANNEL_OP); +} + +static int xone_mt76_calibrate(struct xone_mt76 *mt, + enum mt76_mcu_calibration calib, u32 val) +{ + struct sk_buff *skb; + + skb = xone_mt76_alloc_message(sizeof(u32) * 2, GFP_KERNEL); + if (!skb) + return -ENOMEM; + + put_unaligned_le32(calib, skb_put(skb, sizeof(u32))); + put_unaligned_le32(val, skb_put(skb, sizeof(u32))); + + return xone_mt76_send_command(mt, skb, MT_CMD_CALIBRATION_OP); +} + +static int xone_mt76_send_firmware_part(struct xone_mt76 *mt, u32 offset, + const u8 *data, u32 len) +{ + struct sk_buff *skb; + u32 pos, chunk_len, complete; + int err; + + for (pos = 0; pos < len; pos += XONE_MT_FW_CHUNK_SIZE) { + chunk_len = min_t(u32, len - pos, XONE_MT_FW_CHUNK_SIZE); + + skb = xone_mt76_alloc_message(chunk_len, GFP_KERNEL); + if (!skb) + return -ENOMEM; + + skb_put_data(skb, data + pos, chunk_len); + chunk_len = roundup(chunk_len, sizeof(u32)); + + xone_mt76_write_register(mt, MT_FCE_DMA_ADDR | MT_VEND_TYPE_CFG, + offset + pos); + xone_mt76_write_register(mt, MT_FCE_DMA_LEN | MT_VEND_TYPE_CFG, + chunk_len << 16); + + err = xone_mt76_send_command(mt, skb, 0); + if (err) + return err; + + complete = 0xc0000000 | (chunk_len << 16); + if (!xone_mt76_poll(mt, MT_FCE_DMA_LEN | MT_VEND_TYPE_CFG, + 0xffffffff, complete)) + return -ETIMEDOUT; + } + + return 0; +} + +static int xone_mt76_send_firmware(struct xone_mt76 *mt, + const struct firmware *fw) +{ + const struct mt76_fw_header *hdr; + u32 ilm_len, dlm_len; + int err; + + if (fw->size < sizeof(*hdr)) + return -EINVAL; + + hdr = (const struct mt76_fw_header *)fw->data; + ilm_len = le32_to_cpu(hdr->ilm_len); + dlm_len = le32_to_cpu(hdr->dlm_len); + + if (fw->size != sizeof(*hdr) + ilm_len + dlm_len) + return -EINVAL; + + dev_dbg(mt->dev, "%s: build=%.16s\n", __func__, hdr->build_time); + + /* configure DMA, enable FCE and packet DMA */ + xone_mt76_write_register(mt, MT_USB_U3DMA_CFG | MT_VEND_TYPE_CFG, + MT_USB_DMA_CFG_TX_BULK_EN | + MT_USB_DMA_CFG_RX_BULK_EN); + xone_mt76_write_register(mt, MT_FCE_PSE_CTRL, 0x01); + xone_mt76_write_register(mt, MT_TX_CPU_FROM_FCE_BASE_PTR, 0x00400230); + xone_mt76_write_register(mt, MT_TX_CPU_FROM_FCE_MAX_COUNT, 0x01); + xone_mt76_write_register(mt, MT_TX_CPU_FROM_FCE_CPU_DESC_IDX, 0x01); + xone_mt76_write_register(mt, MT_FCE_PDMA_GLOBAL_CONF, 0x44); + xone_mt76_write_register(mt, MT_FCE_SKIP_FS, 0x03); + + /* send instruction local memory */ + err = xone_mt76_send_firmware_part(mt, XONE_MT_FW_ILM_OFFSET, + fw->data + sizeof(*hdr), ilm_len); + if (err) + return err; + + /* send data local memory */ + return xone_mt76_send_firmware_part(mt, XONE_MT_FW_DLM_OFFSET, + fw->data + sizeof(*hdr) + ilm_len, + dlm_len); +} + +static int xone_mt76_reset_firmware(struct xone_mt76 *mt) +{ + u32 val; + int err; + + /* apply power-on RF patch */ + val = xone_mt76_read_register(mt, XONE_MT_RF_PATCH | MT_VEND_TYPE_CFG); + xone_mt76_write_register(mt, XONE_MT_RF_PATCH | MT_VEND_TYPE_CFG, + val & ~BIT(19)); + + err = xone_mt76_load_ivb(mt); + if (err) + return err; + + /* wait for reset */ + if (!xone_mt76_poll(mt, MT_FCE_DMA_ADDR | MT_VEND_TYPE_CFG, + 0x80000000, 0x80000000)) + return -ETIMEDOUT; + + return 0; +} + +int xone_mt76_load_firmware(struct xone_mt76 *mt, const char *name) +{ + const struct firmware *fw; + int err; + + if (xone_mt76_read_register(mt, MT_FCE_DMA_ADDR | MT_VEND_TYPE_CFG)) { + dev_dbg(mt->dev, "%s: resetting firmware...\n", __func__); + return xone_mt76_reset_firmware(mt); + } + + err = request_firmware(&fw, name, mt->dev); + if (err) { + if (err == -ENOENT) + dev_err(mt->dev, "%s: firmware not found\n", __func__); + + return err; + } + + err = xone_mt76_send_firmware(mt, fw); + if (err) + goto err_free_firmware; + + xone_mt76_write_register(mt, MT_FCE_DMA_ADDR | MT_VEND_TYPE_CFG, 0); + + err = xone_mt76_load_ivb(mt); + if (err) + goto err_free_firmware; + + if (!xone_mt76_poll(mt, MT_FCE_DMA_ADDR | MT_VEND_TYPE_CFG, 0x01, 0x01)) + err = -ETIMEDOUT; + +err_free_firmware: + release_firmware(fw); + + return err; +} + +static const struct xone_mt76_channel +xone_mt76_channels[XONE_MT_NUM_CHANNELS] = { + { 0x01, XONE_MT_CH_2G_LOW, MT_PHY_BW_20, 0, true }, + { 0x06, XONE_MT_CH_2G_MID, MT_PHY_BW_20, 0, true }, + { 0x0b, XONE_MT_CH_2G_HIGH, MT_PHY_BW_20, 0, true }, + { 0x24, XONE_MT_CH_5G_LOW, MT_PHY_BW_40, MT_CH_5G_UNII_1, true }, + { 0x28, XONE_MT_CH_5G_LOW, MT_PHY_BW_40, MT_CH_5G_UNII_1, false }, + { 0x2c, XONE_MT_CH_5G_HIGH, MT_PHY_BW_40, MT_CH_5G_UNII_1, true }, + { 0x30, XONE_MT_CH_5G_HIGH, MT_PHY_BW_40, MT_CH_5G_UNII_1, false }, + { 0x95, XONE_MT_CH_5G_LOW, MT_PHY_BW_80, MT_CH_5G_UNII_3, true }, + { 0x99, XONE_MT_CH_5G_LOW, MT_PHY_BW_80, MT_CH_5G_UNII_3, false }, + { 0x9d, XONE_MT_CH_5G_HIGH, MT_PHY_BW_80, MT_CH_5G_UNII_3, true }, + { 0xa1, XONE_MT_CH_5G_HIGH, MT_PHY_BW_80, MT_CH_5G_UNII_3, false }, + { 0xa5, XONE_MT_CH_5G_HIGH, MT_PHY_BW_80, MT_CH_5G_UNII_3, false }, +}; + +static int xone_mt76_set_channel_candidates(struct xone_mt76 *mt) +{ + struct sk_buff *skb; + u8 best_chan = mt->channel->index; + u8 chan; + int i, err; + + skb = alloc_skb(sizeof(u32) * 2 + sizeof(u32) * XONE_MT_NUM_CHANNELS, + GFP_KERNEL); + if (!skb) + return -ENOMEM; + + put_unaligned_le32(1, skb_put(skb, sizeof(u32))); + put_unaligned_le32(best_chan, skb_put(skb, sizeof(u32))); + put_unaligned_le32(XONE_MT_NUM_CHANNELS - 1, skb_put(skb, sizeof(u32))); + + for (i = 0; i < XONE_MT_NUM_CHANNELS; i++) { + chan = mt->channels[i].index; + if (chan != best_chan) + put_unaligned_le32(chan, skb_put(skb, sizeof(u32))); + } + + err = xone_mt76_send_ms_command(mt, XONE_MT_SET_CHAN_CANDIDATES, + skb->data, skb->len); + consume_skb(skb); + + return err; +} + +static int xone_mt76_get_channel_power(struct xone_mt76 *mt, + struct xone_mt76_channel *chan) +{ + u16 addr; + u8 idx, target, offset; + u8 entry[8]; + int err; + + if (chan->bandwidth == MT_PHY_BW_20) { + addr = MT_EE_TX_POWER_0_START_2G; + idx = 4; + } else { + /* each group has its own power table */ + addr = MT_EE_TX_POWER_0_START_5G + + chan->group * MT_TX_POWER_GROUP_SIZE_5G; + idx = 5; + } + + err = xone_mt76_read_efuse(mt, addr, entry, sizeof(entry)); + if (err) { + dev_err(mt->dev, "%s: read EFUSE failed: %d\n", __func__, err); + return err; + } + + target = entry[idx]; + offset = entry[idx + chan->band]; + + /* increase or decrease power by offset (in 0.5 dB steps) */ + if (offset & BIT(7)) + chan->power = (offset & BIT(6)) ? + target + (offset & GENMASK(5, 0)) : + target - (offset & GENMASK(5, 0)); + else + chan->power = target; + + return 0; +} + +static int xone_mt76_evaluate_channels(struct xone_mt76 *mt) +{ + struct xone_mt76_channel *chan; + int i, err; + + memcpy(mt->channels, xone_mt76_channels, sizeof(xone_mt76_channels)); + + for (i = 0; i < XONE_MT_NUM_CHANNELS; i++) { + chan = &mt->channels[i]; + + /* original driver increases power for channels 0x24 to 0x30 */ + err = xone_mt76_get_channel_power(mt, chan); + if (err) + return err; + + err = xone_mt76_switch_channel(mt, chan); + if (err) + return err; + + dev_dbg(mt->dev, "%s: channel=%u, power=%u\n", __func__, + chan->index, chan->power); + } + + /* the last channel might not be the best one */ + mt->channel = chan; + + return 0; +} + +static int xone_mt76_init_channels(struct xone_mt76 *mt) +{ + int err; + + /* enable promiscuous mode */ + xone_mt76_write_register(mt, MT_RX_FILTR_CFG, 0x014f13); + + err = xone_mt76_evaluate_channels(mt); + if (err) + return err; + + /* disable promiscuous mode */ + xone_mt76_write_register(mt, MT_RX_FILTR_CFG, 0x017f17); + + dev_dbg(mt->dev, "%s: channel=%u\n", __func__, mt->channel->index); + + mt->channel->scan = true; + + err = xone_mt76_switch_channel(mt, mt->channel); + if (err) + return err; + + err = xone_mt76_set_power_mode(mt, MT_RADIO_OFF); + if (err) + return err; + + msleep(50); + + err = xone_mt76_set_power_mode(mt, MT_RADIO_ON); + if (err) + return err; + + mt->channel->scan = false; + + err = xone_mt76_switch_channel(mt, mt->channel); + if (err) + return err; + + return xone_mt76_set_channel_candidates(mt); +} + +static int xone_mt76_set_idle_time(struct xone_mt76 *mt) +{ + __le32 time = cpu_to_le32(64); + + /* prevent wireless clients from disconnecting when idle */ + return xone_mt76_send_ms_command(mt, XONE_MT_SET_IDLE_TIME, + &time, sizeof(time)); +} + +static int xone_mt76_init_address(struct xone_mt76 *mt) +{ + int err; + + err = xone_mt76_read_efuse(mt, MT_EE_MAC_ADDR, + mt->address, sizeof(mt->address)); + if (err) + return err; + + dev_dbg(mt->dev, "%s: address=%pM\n", __func__, mt->address); + + /* some addresses start with 6c:5d:3a */ + /* clients only connect to 62:45:bx:xx:xx:xx */ + if (mt->address[0] != 0x62) { + mt->address[0] = 0x62; + mt->address[1] = 0x45; + mt->address[2] = 0xbd; + } + + err = xone_mt76_write_burst(mt, MT_MAC_ADDR_DW0, + mt->address, sizeof(mt->address)); + if (err) + return err; + + err = xone_mt76_write_burst(mt, MT_MAC_BSSID_DW0, + mt->address, sizeof(mt->address)); + if (err) + return err; + + return xone_mt76_send_ms_command(mt, XONE_MT_SET_MAC_ADDRESS, + mt->address, sizeof(mt->address)); +} + +static int xone_mt76_calibrate_crystal(struct xone_mt76 *mt) +{ + u8 trim[4]; + u16 val; + s8 offset; + u32 ctrl; + int err; + + err = xone_mt76_read_efuse(mt, MT_EE_XTAL_TRIM_2, trim, sizeof(trim)); + if (err) + return err; + + val = (trim[3] << 8) | trim[2]; + offset = val & GENMASK(6, 0); + if ((val & 0xff) == 0xff) + offset = 0; + else if (val & BIT(7)) + offset = -offset; + + val >>= 8; + if (!val || val == 0xff) { + err = xone_mt76_read_efuse(mt, MT_EE_XTAL_TRIM_1, trim, + sizeof(trim)); + if (err) + return err; + + val = (trim[3] << 8) | trim[2]; + val &= 0xff; + if (!val || val == 0xff) + val = 0x14; + } + + val = (val & GENMASK(6, 0)) + offset; + ctrl = xone_mt76_read_register(mt, MT_XO_CTRL5 | MT_VEND_TYPE_CFG); + xone_mt76_write_register(mt, MT_XO_CTRL5 | MT_VEND_TYPE_CFG, + (ctrl & ~MT_XO_CTRL5_C2_VAL) | (val << 8)); + xone_mt76_write_register(mt, MT_XO_CTRL6 | MT_VEND_TYPE_CFG, + MT_XO_CTRL6_C2_CTRL); + xone_mt76_write_register(mt, MT_CMB_CTRL, 0x0091a7ff); + + return 0; +} + +static int xone_mt76_calibrate_radio(struct xone_mt76 *mt) +{ + int err; + + /* configure automatic gain control (AGC) */ + xone_mt76_write_register(mt, MT_BBP(AGC, 8), 0x18365efa); + xone_mt76_write_register(mt, MT_BBP(AGC, 9), 0x18365efa); + + /* reset required for reliable WLAN associations */ + xone_mt76_write_register(mt, MT_MAC_SYS_CTRL, 0); + xone_mt76_write_register(mt, MT_RF_BYPASS_0, 0); + xone_mt76_write_register(mt, MT_RF_SETTING_0, 0); + + err = xone_mt76_calibrate(mt, MT_MCU_CAL_TEMP_SENSOR, 0); + if (err) + return err; + + err = xone_mt76_calibrate(mt, MT_MCU_CAL_RXDCOC, 1); + if (err) + return err; + + err = xone_mt76_calibrate(mt, MT_MCU_CAL_RC, 0); + if (err) + return err; + + xone_mt76_write_register(mt, MT_MAC_SYS_CTRL, + MT_MAC_SYS_CTRL_ENABLE_RX | + MT_MAC_SYS_CTRL_ENABLE_TX); + + return 0; +} + +static void xone_mt76_init_registers(struct xone_mt76 *mt) +{ + xone_mt76_write_register(mt, MT_MAC_SYS_CTRL, + MT_MAC_SYS_CTRL_RESET_BBP | + MT_MAC_SYS_CTRL_RESET_CSR); + xone_mt76_write_register(mt, MT_USB_DMA_CFG, 0); + xone_mt76_write_register(mt, MT_MAC_SYS_CTRL, 0); + xone_mt76_write_register(mt, MT_PWR_PIN_CFG, 0); + xone_mt76_write_register(mt, MT_LDO_CTRL_1, 0x6b006464); + xone_mt76_write_register(mt, MT_WPDMA_GLO_CFG, 0x70); + xone_mt76_write_register(mt, MT_WMM_AIFSN, 0x2273); + xone_mt76_write_register(mt, MT_WMM_CWMIN, 0x2344); + xone_mt76_write_register(mt, MT_WMM_CWMAX, 0x34aa); + xone_mt76_write_register(mt, MT_FCE_DMA_ADDR, 0x041200); + xone_mt76_write_register(mt, MT_TSO_CTRL, 0); + xone_mt76_write_register(mt, MT_PBF_SYS_CTRL, 0x080c00); + xone_mt76_write_register(mt, MT_PBF_TX_MAX_PCNT, 0x1fbf1f1f); + xone_mt76_write_register(mt, MT_FCE_PSE_CTRL, 0x01); + xone_mt76_write_register(mt, MT_MAC_SYS_CTRL, + MT_MAC_SYS_CTRL_ENABLE_RX | + MT_MAC_SYS_CTRL_ENABLE_TX); + xone_mt76_write_register(mt, MT_AUTO_RSP_CFG, 0x13); + xone_mt76_write_register(mt, MT_MAX_LEN_CFG, 0x3e3fff); + xone_mt76_write_register(mt, MT_AMPDU_MAX_LEN_20M1S, 0xfffc9855); + xone_mt76_write_register(mt, MT_AMPDU_MAX_LEN_20M2S, 0xff); + xone_mt76_write_register(mt, MT_BKOFF_SLOT_CFG, 0x0109); + xone_mt76_write_register(mt, MT_PWR_PIN_CFG, 0); + xone_mt76_write_register(mt, MT_EDCA_CFG_AC(0), 0x064320); + xone_mt76_write_register(mt, MT_EDCA_CFG_AC(1), 0x0a4700); + xone_mt76_write_register(mt, MT_EDCA_CFG_AC(2), 0x043238); + xone_mt76_write_register(mt, MT_EDCA_CFG_AC(3), 0x03212f); + xone_mt76_write_register(mt, MT_TX_PIN_CFG, 0x150f0f); + xone_mt76_write_register(mt, MT_TX_SW_CFG0, 0x101001); + xone_mt76_write_register(mt, MT_TX_SW_CFG1, 0x010000); + xone_mt76_write_register(mt, MT_TXOP_CTRL_CFG, 0x10583f); + xone_mt76_write_register(mt, MT_TX_TIMEOUT_CFG, 0x0a0f90); + xone_mt76_write_register(mt, MT_TX_RETRY_CFG, 0x47d01f0f); + xone_mt76_write_register(mt, MT_CCK_PROT_CFG, 0x03f40003); + xone_mt76_write_register(mt, MT_OFDM_PROT_CFG, 0x03f40003); + xone_mt76_write_register(mt, MT_MM20_PROT_CFG, 0x01742004); + xone_mt76_write_register(mt, MT_GF20_PROT_CFG, 0x01742004); + xone_mt76_write_register(mt, MT_GF40_PROT_CFG, 0x03f42084); + xone_mt76_write_register(mt, MT_EXP_ACK_TIME, 0x2c00dc); + xone_mt76_write_register(mt, MT_TX_ALC_CFG_2, 0x22160a00); + xone_mt76_write_register(mt, MT_TX_ALC_CFG_3, 0x22160a76); + xone_mt76_write_register(mt, MT_TX_ALC_CFG_0, 0x3f3f1818); + xone_mt76_write_register(mt, MT_TX_ALC_CFG_4, 0x0606); + xone_mt76_write_register(mt, MT_PIFS_TX_CFG, 0x060fff); + xone_mt76_write_register(mt, MT_RX_FILTR_CFG, 0x017f17); + xone_mt76_write_register(mt, MT_LEGACY_BASIC_RATE, 0x017f); + xone_mt76_write_register(mt, MT_HT_BASIC_RATE, 0x8003); + xone_mt76_write_register(mt, MT_PN_PAD_MODE, 0x02); + xone_mt76_write_register(mt, MT_TXOP_HLDR_ET, 0x02); + xone_mt76_write_register(mt, MT_TX_PROT_CFG6, 0xe3f42004); + xone_mt76_write_register(mt, MT_TX_PROT_CFG7, 0xe3f42084); + xone_mt76_write_register(mt, MT_TX_PROT_CFG8, 0xe3f42104); + xone_mt76_write_register(mt, MT_DACCLK_EN_DLY_CFG, 0); + xone_mt76_write_register(mt, MT_RF_PA_MODE_ADJ0, 0xee000000); + xone_mt76_write_register(mt, MT_RF_PA_MODE_ADJ1, 0xee000000); + xone_mt76_write_register(mt, MT_TX0_RF_GAIN_CORR, 0x0f3c3c3c); + xone_mt76_write_register(mt, MT_TX1_RF_GAIN_CORR, 0x0f3c3c3c); + xone_mt76_write_register(mt, MT_PBF_CFG, 0x1efebcf5); + xone_mt76_write_register(mt, MT_PAUSE_ENABLE_CONTROL1, 0x0a); + xone_mt76_write_register(mt, MT_RF_BYPASS_0, 0x7f000000); + xone_mt76_write_register(mt, MT_RF_SETTING_0, 0x1a800000); + xone_mt76_write_register(mt, MT_XIFS_TIME_CFG, 0x33a40e0a); + xone_mt76_write_register(mt, MT_FCE_L2_STUFF, 0x03ff0223); + xone_mt76_write_register(mt, MT_TX_RTS_CFG, 0); + xone_mt76_write_register(mt, MT_BEACON_TIME_CFG, 0x0640); + xone_mt76_write_register(mt, MT_EXT_CCA_CFG, 0xf0e4); + xone_mt76_write_register(mt, MT_CH_TIME_CFG, 0x015f); +} + +static u16 xone_mt76_get_chip_id(struct xone_mt76 *mt) +{ + u8 id[4]; + + if (xone_mt76_read_efuse(mt, MT_EE_CHIP_ID, &id, sizeof(id))) + return 0; + + return (id[1] << 8) | id[2]; +} + +int xone_mt76_init_radio(struct xone_mt76 *mt) +{ + int err; + + dev_dbg(mt->dev, "%s: id=0x%04x\n", __func__, + xone_mt76_get_chip_id(mt)); + + err = xone_mt76_select_function(mt, MT_Q_SELECT, 1); + if (err) + return err; + + err = xone_mt76_set_power_mode(mt, MT_RADIO_ON); + if (err) + return err; + + err = xone_mt76_load_cr(mt, MT_RF_BBP_CR); + if (err) + return err; + + xone_mt76_init_registers(mt); + + err = xone_mt76_calibrate_crystal(mt); + if (err) + return err; + + err = xone_mt76_init_address(mt); + if (err) + return err; + + err = xone_mt76_set_idle_time(mt); + if (err) + return err; + + err = xone_mt76_calibrate_radio(mt); + if (err) + return err; + + err = xone_mt76_init_channels(mt); + if (err) + return err; + + /* mandatory delay after channel change */ + msleep(1000); + + return xone_mt76_set_pairing(mt, false); +} + +int xone_mt76_suspend_radio(struct xone_mt76 *mt) +{ + int err; + + xone_mt76_write_register(mt, MT_MAC_SYS_CTRL, 0); + + /* enable wake-on-wireless */ + err = xone_mt76_set_wow_enable(mt, true); + if (err) + return err; + + return xone_mt76_set_wow_traffic(mt, XONE_MT_WOW_TO_HOST); +} + +int xone_mt76_resume_radio(struct xone_mt76 *mt) +{ + int err; + + err = xone_mt76_set_wow_traffic(mt, XONE_MT_WOW_TO_FIRMWARE); + if (err) + return err; + + /* disable wake-on-wireless */ + err = xone_mt76_set_wow_enable(mt, false); + if (err) + return err; + + err = xone_mt76_switch_channel(mt, mt->channel); + if (err) + return err; + + err = xone_mt76_set_pairing(mt, false); + if (err) + return err; + + xone_mt76_write_register(mt, MT_MAC_SYS_CTRL, + MT_MAC_SYS_CTRL_ENABLE_RX | + MT_MAC_SYS_CTRL_ENABLE_TX); + + return 0; +} + +static int xone_mt76_write_beacon(struct xone_mt76 *mt, bool pair) +{ + struct sk_buff *skb; + struct mt76_txwi txwi = {}; + struct ieee80211_mgmt mgmt = {}; + u8 data[] = { + /* information element with Microsoft's OUI (00:50:f2) */ + /* probably includes the selected channel pair */ + 0x00, 0x00, 0xdd, 0x10, 0x00, 0x50, 0xf2, 0x11, + 0x01, 0x10, pair, 0xa5, 0x30, 0x99, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + }; + int mgmt_len = sizeof(struct ieee80211_hdr_3addr) + + sizeof(mgmt.u.beacon); + int err; + + skb = alloc_skb(sizeof(txwi) + mgmt_len + sizeof(data), GFP_KERNEL); + if (!skb) + return -ENOMEM; + + /* generate beacon timestamp */ + /* use hardware sequence control */ + txwi.flags = cpu_to_le16(MT_TXWI_FLAGS_TS); + txwi.rate = cpu_to_le16(FIELD_PREP(MT_RXWI_RATE_PHY, MT_PHY_TYPE_OFDM)); + txwi.ack_ctl = MT_TXWI_ACK_CTL_NSEQ; + txwi.len_ctl = cpu_to_le16(mgmt_len + sizeof(data)); + + mgmt.frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | + IEEE80211_STYPE_BEACON); + eth_broadcast_addr(mgmt.da); + memcpy(mgmt.sa, mt->address, ETH_ALEN); + memcpy(mgmt.bssid, mt->address, ETH_ALEN); + + /* default beacon interval (100 ms) */ + /* original capability info */ + mgmt.u.beacon.beacon_int = cpu_to_le16(100); + mgmt.u.beacon.capab_info = cpu_to_le16(0xc631); + + skb_put_data(skb, &txwi, sizeof(txwi)); + skb_put_data(skb, &mgmt, mgmt_len); + skb_put_data(skb, data, sizeof(data)); + + err = xone_mt76_write_burst(mt, MT_BEACON_BASE, skb->data, skb->len); + consume_skb(skb); + + return err; +} + +int xone_mt76_set_pairing(struct xone_mt76 *mt, bool enable) +{ + int err; + + err = xone_mt76_write_beacon(mt, enable); + if (err) + return err; + + /* enable timing synchronization function (TSF) timer */ + /* enable target beacon transmission time (TBTT) timer */ + /* set TSF timer to AP mode */ + /* activate beacon transmission */ + xone_mt76_write_register(mt, MT_BEACON_TIME_CFG, + MT_BEACON_TIME_CFG_BEACON_TX | + MT_BEACON_TIME_CFG_TBTT_EN | + MT_BEACON_TIME_CFG_SYNC_MODE | + MT_BEACON_TIME_CFG_TIMER_EN | + FIELD_PREP(MT_BEACON_TIME_CFG_INTVAL, 0x0640)); + + return 0; +} + +int xone_mt76_pair_client(struct xone_mt76 *mt, u8 *addr) +{ + struct sk_buff *skb; + struct ieee80211_hdr_3addr hdr = {}; + u8 data[] = { + 0x70, 0x02, 0x00, 0x45, 0x55, 0x01, 0x0f, 0x8f, + 0xff, 0x87, 0x1f, + }; + + skb = xone_mt76_alloc_message(sizeof(struct mt76_txwi) + sizeof(hdr) + + sizeof(data), GFP_KERNEL); + if (!skb) + return -ENOMEM; + + hdr.frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | + XONE_MT_WLAN_RESERVED); + memcpy(hdr.addr1, addr, ETH_ALEN); + memcpy(hdr.addr2, mt->address, ETH_ALEN); + memcpy(hdr.addr3, mt->address, ETH_ALEN); + + skb_reserve(skb, sizeof(struct mt76_txwi)); + skb_put_data(skb, &hdr, sizeof(hdr)); + skb_put_data(skb, data, sizeof(data)); + + return xone_mt76_send_wlan(mt, skb); +} + +int xone_mt76_associate_client(struct xone_mt76 *mt, u8 wcid, u8 *addr) +{ + struct sk_buff *skb; + struct ieee80211_mgmt mgmt = {}; + u8 data[] = { wcid - 1, 0x00, 0x00, 0x00, 0x40, 0x1f, 0x00, 0x00 }; + int mgmt_len = sizeof(struct ieee80211_hdr_3addr) + + sizeof(mgmt.u.assoc_resp); + int err; + + skb = xone_mt76_alloc_message(sizeof(struct mt76_txwi) + mgmt_len, + GFP_KERNEL); + if (!skb) + return -ENOMEM; + + mgmt.frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | + IEEE80211_STYPE_ASSOC_RESP); + memcpy(mgmt.da, addr, ETH_ALEN); + memcpy(mgmt.sa, mt->address, ETH_ALEN); + memcpy(mgmt.bssid, mt->address, ETH_ALEN); + + /* original status code and association ID */ + mgmt.u.assoc_resp.status_code = cpu_to_le16(0x0110); + mgmt.u.assoc_resp.aid = cpu_to_le16(0x0f00); + + skb_reserve(skb, sizeof(struct mt76_txwi)); + skb_put_data(skb, &mgmt, mgmt_len); + memset(skb_put(skb, 8), 0, 8); + + err = xone_mt76_write_burst(mt, MT_WCID_ADDR(wcid), addr, ETH_ALEN); + if (err) + goto err_free_skb; + + err = xone_mt76_send_ms_command(mt, XONE_MT_ADD_CLIENT, + data, sizeof(data)); + if (err) + goto err_free_skb; + + return xone_mt76_send_wlan(mt, skb); + +err_free_skb: + kfree_skb(skb); + + return err; +} + +int xone_mt76_remove_client(struct xone_mt76 *mt, u8 wcid) +{ + u8 data[] = { wcid - 1, 0x00, 0x00, 0x00 }; + u8 addr[ETH_ALEN] = {}; + int err; + + err = xone_mt76_send_ms_command(mt, XONE_MT_REMOVE_CLIENT, + data, sizeof(data)); + if (err) + return err; + + return xone_mt76_write_burst(mt, MT_WCID_ADDR(wcid), addr, ETH_ALEN); +} diff --git a/transport/mt76.h b/transport/mt76.h new file mode 100644 index 0000000..71474ad --- /dev/null +++ b/transport/mt76.h @@ -0,0 +1,65 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2021 Severin von Wnuck + */ + +#pragma once + +#include "mt76_defs.h" + +#define XONE_MT_EP_IN_CMD 0x05 +#define XONE_MT_EP_IN_WLAN 0x04 +#define XONE_MT_EP_OUT 0x04 + +#define XONE_MT_NUM_CHANNELS 12 + +/* 802.11 frame subtype: reserved */ +#define XONE_MT_WLAN_RESERVED 0x70 + +enum xone_mt76_led_mode { + XONE_MT_LED_BLINK = 0x00, + XONE_MT_LED_ON = 0x01, + XONE_MT_LED_OFF = 0x02, +}; + +enum xone_mt76_event { + XONE_MT_EVT_BUTTON = 0x04, + XONE_MT_EVT_CHANNELS = 0x0a, + XONE_MT_EVT_PACKET_RX = 0x0c, + XONE_MT_EVT_COREDUMP = 0x0d, + XONE_MT_EVT_CLIENT_LOST = 0x0e, +}; + +struct xone_mt76_channel { + u8 index; + u8 band; + enum mt76_phy_bandwidth bandwidth; + enum mt76_cal_channel_group group; + bool scan; + u8 power; +}; + +struct xone_mt76 { + struct device *dev; + struct usb_device *udev; + + __le32 control_data; + u8 address[ETH_ALEN]; + + struct xone_mt76_channel channels[XONE_MT_NUM_CHANNELS]; + struct xone_mt76_channel *channel; +}; + +struct sk_buff *xone_mt76_alloc_message(int len, gfp_t gfp); +void xone_mt76_prep_command(struct sk_buff *skb, enum mt76_mcu_cmd cmd); + +int xone_mt76_set_led_mode(struct xone_mt76 *mt, enum xone_mt76_led_mode mode); +int xone_mt76_load_firmware(struct xone_mt76 *mt, const char *name); +int xone_mt76_init_radio(struct xone_mt76 *mt); +int xone_mt76_suspend_radio(struct xone_mt76 *mt); +int xone_mt76_resume_radio(struct xone_mt76 *mt); +int xone_mt76_set_pairing(struct xone_mt76 *mt, bool enable); + +int xone_mt76_pair_client(struct xone_mt76 *mt, u8 *addr); +int xone_mt76_associate_client(struct xone_mt76 *mt, u8 wcid, u8 *addr); +int xone_mt76_remove_client(struct xone_mt76 *mt, u8 wcid); diff --git a/transport/mt76_defs.h b/transport/mt76_defs.h new file mode 100644 index 0000000..d190cee --- /dev/null +++ b/transport/mt76_defs.h @@ -0,0 +1,1052 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Based on code from the open source mt76 driver with minor modifications. + * + * Copyright (C) 2021 Severin von Wnuck + * + * Special thanks to the authors of the mt76 driver: + * + * Copyright (C) Felix Fietkau + * Copyright (C) Lorenzo Bianconi + * Copyright (C) Stanislaw Gruszka + */ + +#pragma once + +#include + +#define MT_ASIC_VERSION 0x0000 + +#define MT_CMB_CTRL 0x0020 +#define MT_CMB_CTRL_XTAL_RDY BIT(22) +#define MT_CMB_CTRL_PLL_LD BIT(23) + +#define MT_EFUSE_CTRL 0x0024 +#define MT_EFUSE_CTRL_AOUT GENMASK(5, 0) +#define MT_EFUSE_CTRL_MODE GENMASK(7, 6) +#define MT_EFUSE_CTRL_LDO_OFF_TIME GENMASK(13, 8) +#define MT_EFUSE_CTRL_LDO_ON_TIME GENMASK(15, 14) +#define MT_EFUSE_CTRL_AIN GENMASK(25, 16) +#define MT_EFUSE_CTRL_KICK BIT(30) +#define MT_EFUSE_CTRL_SEL BIT(31) + +#define MT_EFUSE_DATA_BASE 0x0028 +#define MT_EFUSE_DATA(n) (MT_EFUSE_DATA_BASE + ((n) << 2)) + +#define MT_COEXCFG0 0x0040 +#define MT_COEXCFG0_COEX_EN BIT(0) + +#define MT_WLAN_FUN_CTRL 0x0080 +#define MT_WLAN_FUN_CTRL_WLAN_EN BIT(0) +#define MT_WLAN_FUN_CTRL_WLAN_CLK_EN BIT(1) +#define MT_WLAN_FUN_CTRL_WLAN_RESET_RF BIT(2) + +#define MT_COEXCFG3 0x004c + +#define MT_LDO_CTRL_0 0x006c +#define MT_LDO_CTRL_1 0x0070 + +#define MT_WLAN_FUN_CTRL_CSR_F20M_CKEN BIT(3) + +#define MT_WLAN_FUN_CTRL_PCIE_CLK_REQ BIT(4) +#define MT_WLAN_FUN_CTRL_FRC_WL_ANT_SEL BIT(5) +#define MT_WLAN_FUN_CTRL_INV_ANT_SEL BIT(6) +#define MT_WLAN_FUN_CTRL_WAKE_HOST BIT(7) + +#define MT_WLAN_FUN_CTRL_THERM_RST BIT(8) +#define MT_WLAN_FUN_CTRL_THERM_CKEN BIT(9) + +#define MT_XO_CTRL0 0x0100 +#define MT_XO_CTRL1 0x0104 +#define MT_XO_CTRL2 0x0108 +#define MT_XO_CTRL3 0x010c +#define MT_XO_CTRL4 0x0110 + +#define MT_XO_CTRL5 0x0114 +#define MT_XO_CTRL5_C2_VAL GENMASK(14, 8) + +#define MT_XO_CTRL6 0x0118 +#define MT_XO_CTRL6_C2_CTRL GENMASK(14, 8) + +#define MT_XO_CTRL7 0x011c + +#define MT_IOCFG_6 0x0124 + +#define MT_USB_U3DMA_CFG 0x9018 +#define MT_USB_DMA_CFG_RX_BULK_AGG_TOUT GENMASK(7, 0) +#define MT_USB_DMA_CFG_RX_BULK_AGG_LMT GENMASK(15, 8) +#define MT_USB_DMA_CFG_UDMA_TX_WL_DROP BIT(16) +#define MT_USB_DMA_CFG_WAKE_UP_EN BIT(17) +#define MT_USB_DMA_CFG_RX_DROP_OR_PAD BIT(18) +#define MT_USB_DMA_CFG_TX_CLR BIT(19) +#define MT_USB_DMA_CFG_TXOP_HALT BIT(20) +#define MT_USB_DMA_CFG_RX_BULK_AGG_EN BIT(21) +#define MT_USB_DMA_CFG_RX_BULK_EN BIT(22) +#define MT_USB_DMA_CFG_TX_BULK_EN BIT(23) +#define MT_USB_DMA_CFG_EP_OUT_VALID GENMASK(29, 24) +#define MT_USB_DMA_CFG_RX_BUSY BIT(30) +#define MT_USB_DMA_CFG_TX_BUSY BIT(31) + +#define MT_WLAN_MTC_CTRL 0x010148 +#define MT_WLAN_MTC_CTRL_MTCMOS_PWR_UP BIT(0) +#define MT_WLAN_MTC_CTRL_PWR_ACK BIT(12) +#define MT_WLAN_MTC_CTRL_PWR_ACK_S BIT(13) +#define MT_WLAN_MTC_CTRL_BBP_MEM_PD GENMASK(19, 16) +#define MT_WLAN_MTC_CTRL_PBF_MEM_PD BIT(20) +#define MT_WLAN_MTC_CTRL_FCE_MEM_PD BIT(21) +#define MT_WLAN_MTC_CTRL_TSO_MEM_PD BIT(22) +#define MT_WLAN_MTC_CTRL_BBP_MEM_RB BIT(24) +#define MT_WLAN_MTC_CTRL_PBF_MEM_RB BIT(25) +#define MT_WLAN_MTC_CTRL_FCE_MEM_RB BIT(26) +#define MT_WLAN_MTC_CTRL_TSO_MEM_RB BIT(27) +#define MT_WLAN_MTC_CTRL_STATE_UP BIT(28) + +#define MT_INT_SOURCE_CSR 0x0200 +#define MT_INT_MASK_CSR 0x0204 + +#define MT_INT_RX_DONE(n) BIT(n) +#define MT_INT_RX_DONE_ALL GENMASK(1, 0) +#define MT_INT_TX_DONE_ALL GENMASK(13, 4) +#define MT_INT_TX_DONE(n) BIT((n) + 4) +#define MT_INT_RX_COHERENT BIT(16) +#define MT_INT_TX_COHERENT BIT(17) +#define MT_INT_ANY_COHERENT BIT(18) +#define MT_INT_MCU_CMD BIT(19) +#define MT_INT_TBTT BIT(20) +#define MT_INT_PRE_TBTT BIT(21) +#define MT_INT_TX_STAT BIT(22) +#define MT_INT_AUTO_WAKEUP BIT(23) +#define MT_INT_GPTIMER BIT(24) +#define MT_INT_RXDELAYINT BIT(26) +#define MT_INT_TXDELAYINT BIT(27) + +#define MT_WPDMA_GLO_CFG 0x0208 +#define MT_WPDMA_GLO_CFG_TX_DMA_EN BIT(0) +#define MT_WPDMA_GLO_CFG_TX_DMA_BUSY BIT(1) +#define MT_WPDMA_GLO_CFG_RX_DMA_EN BIT(2) +#define MT_WPDMA_GLO_CFG_RX_DMA_BUSY BIT(3) +#define MT_WPDMA_GLO_CFG_DMA_BURST_SIZE GENMASK(5, 4) +#define MT_WPDMA_GLO_CFG_TX_WRITEBACK_DONE BIT(6) +#define MT_WPDMA_GLO_CFG_BIG_ENDIAN BIT(7) +#define MT_WPDMA_GLO_CFG_HDR_SEG_LEN GENMASK(15, 8) +#define MT_WPDMA_GLO_CFG_CLK_GATE_DIS BIT(30) +#define MT_WPDMA_GLO_CFG_RX_2B_OFFSET BIT(31) + +#define MT_WPDMA_RST_IDX 0x020c + +#define MT_WPDMA_DELAY_INT_CFG 0x0210 + +#define MT_WMM_AIFSN 0x0214 +#define MT_WMM_AIFSN_MASK GENMASK(3, 0) +#define MT_WMM_AIFSN_SHIFT(n) ((n) * 4) + +#define MT_WMM_CWMIN 0x0218 +#define MT_WMM_CWMIN_MASK GENMASK(3, 0) +#define MT_WMM_CWMIN_SHIFT(n) ((n) * 4) + +#define MT_WMM_CWMAX 0x021c +#define MT_WMM_CWMAX_MASK GENMASK(3, 0) +#define MT_WMM_CWMAX_SHIFT(n) ((n) * 4) + +#define MT_WMM_TXOP_BASE 0x0220 +#define MT_WMM_TXOP(n) (MT_WMM_TXOP_BASE + (((n) / 2) << 2)) +#define MT_WMM_TXOP_SHIFT(n) (((n) & 1) * 16) +#define MT_WMM_TXOP_MASK GENMASK(15, 0) + +#define MT_FCE_DMA_ADDR 0x0230 +#define MT_FCE_DMA_LEN 0x0234 +#define MT_USB_DMA_CFG 0x0238 + +#define MT_TSO_CTRL 0x0250 +#define MT_HEADER_TRANS_CTRL_REG 0x0260 + +#define MT_US_CYC_CFG 0x02a4 +#define MT_US_CYC_CNT GENMASK(7, 0) + +#define MT_TX_RING_BASE 0x0300 +#define MT_RX_RING_BASE 0x03c0 + +#define MT_PBF_SYS_CTRL 0x0400 +#define MT_PBF_SYS_CTRL_MCU_RESET BIT(0) +#define MT_PBF_SYS_CTRL_DMA_RESET BIT(1) +#define MT_PBF_SYS_CTRL_MAC_RESET BIT(2) +#define MT_PBF_SYS_CTRL_PBF_RESET BIT(3) +#define MT_PBF_SYS_CTRL_ASY_RESET BIT(4) + +#define MT_PBF_CFG 0x0404 +#define MT_PBF_CFG_TX0Q_EN BIT(0) +#define MT_PBF_CFG_TX1Q_EN BIT(1) +#define MT_PBF_CFG_TX2Q_EN BIT(2) +#define MT_PBF_CFG_TX3Q_EN BIT(3) +#define MT_PBF_CFG_RX0Q_EN BIT(4) +#define MT_PBF_CFG_RX_DROP_EN BIT(8) + +#define MT_PBF_TX_MAX_PCNT 0x0408 +#define MT_PBF_RX_MAX_PCNT 0x040c + +#define MT_BCN_OFFSET_BASE 0x041c +#define MT_BCN_OFFSET(n) (MT_BCN_OFFSET_BASE + ((n) << 2)) + +#define MT_RXQ_STA 0x0430 +#define MT_TXQ_STA 0x0434 +#define MT_RF_CSR_CFG 0x0500 +#define MT_RF_CSR_CFG_DATA GENMASK(7, 0) +#define MT_RF_CSR_CFG_REG_ID GENMASK(14, 8) +#define MT_RF_CSR_CFG_REG_BANK GENMASK(17, 15) +#define MT_RF_CSR_CFG_WR BIT(30) +#define MT_RF_CSR_CFG_KICK BIT(31) + +#define MT_RF_BYPASS_0 0x0504 +#define MT_RF_BYPASS_1 0x0508 +#define MT_RF_SETTING_0 0x050c + +#define MT_RF_MISC 0x0518 +#define MT_RF_DATA_WRITE 0x0524 + +#define MT_RF_CTRL 0x0528 +#define MT_RF_CTRL_ADDR GENMASK(11, 0) +#define MT_RF_CTRL_WRITE BIT(12) +#define MT_RF_CTRL_BUSY BIT(13) +#define MT_RF_CTRL_IDX BIT(16) + +#define MT_RF_DATA_READ 0x052c + +#define MT_COM_REG0 0x0730 +#define MT_COM_REG1 0x0734 +#define MT_COM_REG2 0x0738 +#define MT_COM_REG3 0x073c + +#define MT_LED_CTRL 0x0770 +#define MT_LED_CTRL_REPLAY(n) BIT(0 + (8 * (n))) +#define MT_LED_CTRL_POLARITY(n) BIT(1 + (8 * (n))) +#define MT_LED_CTRL_TX_BLINK_MODE(n) BIT(2 + (8 * (n))) +#define MT_LED_CTRL_KICK(n) BIT(7 + (8 * (n))) + +#define MT_LED_TX_BLINK_0 0x0774 +#define MT_LED_TX_BLINK_1 0x0778 + +#define MT_LED_S0_BASE 0x077c +#define MT_LED_S0(n) (MT_LED_S0_BASE + 8 * (n)) +#define MT_LED_S1_BASE 0x0780 +#define MT_LED_S1(n) (MT_LED_S1_BASE + 8 * (n)) +#define MT_LED_STATUS_OFF GENMASK(31, 24) +#define MT_LED_STATUS_ON GENMASK(23, 16) +#define MT_LED_STATUS_DURATION GENMASK(15, 8) + +#define MT_FCE_PSE_CTRL 0x0800 +#define MT_FCE_PARAMETERS 0x0804 +#define MT_FCE_CSO 0x0808 + +#define MT_FCE_L2_STUFF 0x080c +#define MT_FCE_L2_STUFF_HT_L2_EN BIT(0) +#define MT_FCE_L2_STUFF_QOS_L2_EN BIT(1) +#define MT_FCE_L2_STUFF_RX_STUFF_EN BIT(2) +#define MT_FCE_L2_STUFF_TX_STUFF_EN BIT(3) +#define MT_FCE_L2_STUFF_WR_MPDU_LEN_EN BIT(4) +#define MT_FCE_L2_STUFF_MVINV_BSWAP BIT(5) +#define MT_FCE_L2_STUFF_TS_CMD_QSEL_EN GENMASK(15, 8) +#define MT_FCE_L2_STUFF_TS_LEN_EN GENMASK(23, 16) +#define MT_FCE_L2_STUFF_OTHER_PORT GENMASK(25, 24) + +#define MT_FCE_WLAN_FLOW_CONTROL1 0x0824 + +#define MT_TX_CPU_FROM_FCE_BASE_PTR 0x09a0 +#define MT_TX_CPU_FROM_FCE_MAX_COUNT 0x09a4 +#define MT_TX_CPU_FROM_FCE_CPU_DESC_IDX 0x09a8 +#define MT_FCE_PDMA_GLOBAL_CONF 0x09c4 +#define MT_FCE_SKIP_FS 0x0a6c + +#define MT_PAUSE_ENABLE_CONTROL1 0x0a38 + +#define MT_MAC_CSR0 0x1000 + +#define MT_MAC_SYS_CTRL 0x1004 +#define MT_MAC_SYS_CTRL_RESET_CSR BIT(0) +#define MT_MAC_SYS_CTRL_RESET_BBP BIT(1) +#define MT_MAC_SYS_CTRL_ENABLE_TX BIT(2) +#define MT_MAC_SYS_CTRL_ENABLE_RX BIT(3) + +#define MT_MAC_ADDR_DW0 0x1008 +#define MT_MAC_ADDR_DW1 0x100c +#define MT_MAC_ADDR_DW1_U2ME_MASK GENMASK(23, 16) + +#define MT_MAC_BSSID_DW0 0x1010 +#define MT_MAC_BSSID_DW1 0x1014 +#define MT_MAC_BSSID_DW1_ADDR GENMASK(15, 0) +#define MT_MAC_BSSID_DW1_MBSS_MODE GENMASK(17, 16) +#define MT_MAC_BSSID_DW1_MBEACON_N GENMASK(20, 18) +#define MT_MAC_BSSID_DW1_MBSS_LOCAL_BIT BIT(21) +#define MT_MAC_BSSID_DW1_MBSS_MODE_B2 BIT(22) +#define MT_MAC_BSSID_DW1_MBEACON_N_B3 BIT(23) +#define MT_MAC_BSSID_DW1_MBSS_IDX_BYTE GENMASK(26, 24) + +#define MT_MAX_LEN_CFG 0x1018 +#define MT_MAX_LEN_CFG_AMPDU GENMASK(13, 12) + +#define MT_LED_CFG 0x102c + +#define MT_AMPDU_MAX_LEN_20M1S 0x1030 +#define MT_AMPDU_MAX_LEN_20M2S 0x1034 +#define MT_AMPDU_MAX_LEN_40M1S 0x1038 +#define MT_AMPDU_MAX_LEN_40M2S 0x103c +#define MT_AMPDU_MAX_LEN 0x1040 + +#define MT_WCID_DROP_BASE 0x106c +#define MT_WCID_DROP(n) (MT_WCID_DROP_BASE + ((n) >> 5) * 4) +#define MT_WCID_DROP_MASK(n) BIT((n) % 32) + +#define MT_BCN_BYPASS_MASK 0x108c + +#define MT_MAC_APC_BSSID_BASE 0x1090 +#define MT_MAC_APC_BSSID_L(n) (MT_MAC_APC_BSSID_BASE + ((n) * 8)) +#define MT_MAC_APC_BSSID_H(n) (MT_MAC_APC_BSSID_BASE + ((n) * 8 + 4)) +#define MT_MAC_APC_BSSID_H_ADDR GENMASK(15, 0) +#define MT_MAC_APC_BSSID0_H_EN BIT(16) + +#define MT_XIFS_TIME_CFG 0x1100 +#define MT_XIFS_TIME_CFG_CCK_SIFS GENMASK(7, 0) +#define MT_XIFS_TIME_CFG_OFDM_SIFS GENMASK(15, 8) +#define MT_XIFS_TIME_CFG_OFDM_XIFS GENMASK(19, 16) +#define MT_XIFS_TIME_CFG_EIFS GENMASK(28, 20) +#define MT_XIFS_TIME_CFG_BB_RXEND_EN BIT(29) + +#define MT_BKOFF_SLOT_CFG 0x1104 +#define MT_BKOFF_SLOT_CFG_SLOTTIME GENMASK(7, 0) +#define MT_BKOFF_SLOT_CFG_CC_DELAY GENMASK(11, 8) + +#define MT_CH_TIME_CFG 0x110c +#define MT_CH_TIME_CFG_TIMER_EN BIT(0) +#define MT_CH_TIME_CFG_TX_AS_BUSY BIT(1) +#define MT_CH_TIME_CFG_RX_AS_BUSY BIT(2) +#define MT_CH_TIME_CFG_NAV_AS_BUSY BIT(3) +#define MT_CH_TIME_CFG_EIFS_AS_BUSY BIT(4) +#define MT_CH_TIME_CFG_MDRDY_CNT_EN BIT(5) +#define MT_CH_CCA_RC_EN BIT(6) +#define MT_CH_TIME_CFG_CH_TIMER_CLR GENMASK(9, 8) +#define MT_CH_TIME_CFG_MDRDY_CLR GENMASK(11, 10) + +#define MT_PBF_LIFE_TIMER 0x1110 + +#define MT_BEACON_TIME_CFG 0x1114 +#define MT_BEACON_TIME_CFG_INTVAL GENMASK(15, 0) +#define MT_BEACON_TIME_CFG_TIMER_EN BIT(16) +#define MT_BEACON_TIME_CFG_SYNC_MODE GENMASK(18, 17) +#define MT_BEACON_TIME_CFG_TBTT_EN BIT(19) +#define MT_BEACON_TIME_CFG_BEACON_TX BIT(20) +#define MT_BEACON_TIME_CFG_TSF_COMP GENMASK(31, 24) + +#define MT_TBTT_SYNC_CFG 0x1118 +#define MT_TSF_TIMER_DW0 0x111c +#define MT_TSF_TIMER_DW1 0x1120 +#define MT_TBTT_TIMER 0x1124 +#define MT_TBTT_TIMER_VAL GENMASK(16, 0) + +#define MT_INT_TIMER_CFG 0x1128 +#define MT_INT_TIMER_CFG_PRE_TBTT GENMASK(15, 0) +#define MT_INT_TIMER_CFG_GP_TIMER GENMASK(31, 16) + +#define MT_INT_TIMER_EN 0x112c +#define MT_INT_TIMER_EN_PRE_TBTT_EN BIT(0) +#define MT_INT_TIMER_EN_GP_TIMER_EN BIT(1) + +#define MT_CH_IDLE 0x1130 +#define MT_CH_BUSY 0x1134 +#define MT_EXT_CH_BUSY 0x1138 +#define MT_ED_CCA_TIMER 0x1140 + +#define MT_MAC_STATUS 0x1200 +#define MT_MAC_STATUS_TX BIT(0) +#define MT_MAC_STATUS_RX BIT(1) + +#define MT_PWR_PIN_CFG 0x1204 +#define MT_AUX_CLK_CFG 0x120c + +#define MT_BB_PA_MODE_CFG0 0x1214 +#define MT_BB_PA_MODE_CFG1 0x1218 +#define MT_RF_PA_MODE_CFG0 0x121c +#define MT_RF_PA_MODE_CFG1 0x1220 + +#define MT_RF_PA_MODE_ADJ0 0x1228 +#define MT_RF_PA_MODE_ADJ1 0x122c + +#define MT_DACCLK_EN_DLY_CFG 0x1264 + +#define MT_EDCA_CFG_BASE 0x1300 +#define MT_EDCA_CFG_AC(n) (MT_EDCA_CFG_BASE + ((n) << 2)) +#define MT_EDCA_CFG_TXOP GENMASK(7, 0) +#define MT_EDCA_CFG_AIFSN GENMASK(11, 8) +#define MT_EDCA_CFG_CWMIN GENMASK(15, 12) +#define MT_EDCA_CFG_CWMAX GENMASK(19, 16) + +#define MT_TX_PWR_CFG_0 0x1314 +#define MT_TX_PWR_CFG_1 0x1318 +#define MT_TX_PWR_CFG_2 0x131c +#define MT_TX_PWR_CFG_3 0x1320 +#define MT_TX_PWR_CFG_4 0x1324 +#define MT_TX_PIN_CFG 0x1328 +#define MT_TX_PIN_CFG_TXANT GENMASK(3, 0) +#define MT_TX_PIN_CFG_RXANT GENMASK(11, 8) +#define MT_TX_PIN_RFTR_EN BIT(16) +#define MT_TX_PIN_TRSW_EN BIT(18) + +#define MT_TX_BAND_CFG 0x132c +#define MT_TX_BAND_CFG_UPPER_40M BIT(0) +#define MT_TX_BAND_CFG_5G BIT(1) +#define MT_TX_BAND_CFG_2G BIT(2) + +#define MT_HT_FBK_TO_LEGACY 0x1384 +#define MT_TX_MPDU_ADJ_INT 0x1388 + +#define MT_TX_PWR_CFG_7 0x13d4 +#define MT_TX_PWR_CFG_8 0x13d8 +#define MT_TX_PWR_CFG_9 0x13dc + +#define MT_TX_SW_CFG0 0x1330 +#define MT_TX_SW_CFG1 0x1334 +#define MT_TX_SW_CFG2 0x1338 + +#define MT_TXOP_CTRL_CFG 0x1340 +#define MT_TXOP_TRUN_EN GENMASK(5, 0) +#define MT_TXOP_EXT_CCA_DLY GENMASK(15, 8) +#define MT_TXOP_ED_CCA_EN BIT(20) + +#define MT_TX_RTS_CFG 0x1344 +#define MT_TX_RTS_CFG_RETRY_LIMIT GENMASK(7, 0) +#define MT_TX_RTS_CFG_THRESH GENMASK(23, 8) +#define MT_TX_RTS_FALLBACK BIT(24) + +#define MT_TX_TIMEOUT_CFG 0x1348 +#define MT_TX_TIMEOUT_CFG_ACKTO GENMASK(15, 8) + +#define MT_TX_RETRY_CFG 0x134c +#define MT_TX_LINK_CFG 0x1350 +#define MT_TX_CFACK_EN BIT(12) +#define MT_VHT_HT_FBK_CFG0 0x1354 +#define MT_VHT_HT_FBK_CFG1 0x1358 +#define MT_LG_FBK_CFG0 0x135c +#define MT_LG_FBK_CFG1 0x1360 + +#define MT_PROT_CFG_RATE GENMASK(15, 0) +#define MT_PROT_CFG_CTRL GENMASK(17, 16) +#define MT_PROT_CFG_NAV GENMASK(19, 18) +#define MT_PROT_CFG_TXOP_ALLOW GENMASK(25, 20) +#define MT_PROT_CFG_RTS_THRESH BIT(26) + +#define MT_CCK_PROT_CFG 0x1364 +#define MT_OFDM_PROT_CFG 0x1368 +#define MT_MM20_PROT_CFG 0x136c +#define MT_MM40_PROT_CFG 0x1370 +#define MT_GF20_PROT_CFG 0x1374 +#define MT_GF40_PROT_CFG 0x1378 + +#define MT_PROT_RATE GENMASK(15, 0) +#define MT_PROT_CTRL_RTS_CTS BIT(16) +#define MT_PROT_CTRL_CTS2SELF BIT(17) +#define MT_PROT_NAV_SHORT BIT(18) +#define MT_PROT_NAV_LONG BIT(19) +#define MT_PROT_TXOP_ALLOW_CCK BIT(20) +#define MT_PROT_TXOP_ALLOW_OFDM BIT(21) +#define MT_PROT_TXOP_ALLOW_MM20 BIT(22) +#define MT_PROT_TXOP_ALLOW_MM40 BIT(23) +#define MT_PROT_TXOP_ALLOW_GF20 BIT(24) +#define MT_PROT_TXOP_ALLOW_GF40 BIT(25) +#define MT_PROT_RTS_THR_EN BIT(26) +#define MT_PROT_RATE_CCK_11 0x0003 +#define MT_PROT_RATE_OFDM_6 0x2000 +#define MT_PROT_RATE_OFDM_24 0x2004 +#define MT_PROT_RATE_DUP_OFDM_24 0x2084 +#define MT_PROT_RATE_SGI_OFDM_24 0x2104 +#define MT_PROT_TXOP_ALLOW_ALL GENMASK(25, 20) +#define MT_PROT_TXOP_ALLOW_BW20 (MT_PROT_TXOP_ALLOW_ALL & \ + ~MT_PROT_TXOP_ALLOW_MM40 & \ + ~MT_PROT_TXOP_ALLOW_GF40) + +#define MT_EXP_ACK_TIME 0x1380 + +#define MT_TX_PWR_CFG_0_EXT 0x1390 +#define MT_TX_PWR_CFG_1_EXT 0x1394 + +#define MT_TX_FBK_LIMIT 0x1398 +#define MT_TX_FBK_LIMIT_MPDU_FBK GENMASK(7, 0) +#define MT_TX_FBK_LIMIT_AMPDU_FBK GENMASK(15, 8) +#define MT_TX_FBK_LIMIT_MPDU_UP_CLEAR BIT(16) +#define MT_TX_FBK_LIMIT_AMPDU_UP_CLEAR BIT(17) +#define MT_TX_FBK_LIMIT_RATE_LUT BIT(18) + +#define MT_TX0_RF_GAIN_CORR 0x13a0 +#define MT_TX1_RF_GAIN_CORR 0x13a4 +#define MT_TX0_RF_GAIN_ATTEN 0x13a8 + +#define MT_TX_ALC_CFG_0 0x13b0 +#define MT_TX_ALC_CFG_0_CH_INIT_0 GENMASK(5, 0) +#define MT_TX_ALC_CFG_0_CH_INIT_1 GENMASK(13, 8) +#define MT_TX_ALC_CFG_0_LIMIT_0 GENMASK(21, 16) +#define MT_TX_ALC_CFG_0_LIMIT_1 GENMASK(29, 24) + +#define MT_TX_ALC_CFG_1 0x13b4 +#define MT_TX_ALC_CFG_1_TEMP_COMP GENMASK(5, 0) + +#define MT_TX_ALC_CFG_2 0x13a8 +#define MT_TX_ALC_CFG_2_TEMP_COMP GENMASK(5, 0) + +#define MT_TX_ALC_CFG_3 0x13ac +#define MT_TX_ALC_CFG_4 0x13c0 +#define MT_TX_ALC_CFG_4_LOWGAIN_CH_EN BIT(31) + +#define MT_TX_ALC_VGA3 0x13c8 + +#define MT_TX_PROT_CFG6 0x13e0 +#define MT_TX_PROT_CFG7 0x13e4 +#define MT_TX_PROT_CFG8 0x13e8 + +#define MT_PIFS_TX_CFG 0x13ec + +#define MT_RX_FILTR_CFG 0x1400 + +#define MT_RX_FILTR_CFG_CRC_ERR BIT(0) +#define MT_RX_FILTR_CFG_PHY_ERR BIT(1) +#define MT_RX_FILTR_CFG_PROMISC BIT(2) +#define MT_RX_FILTR_CFG_OTHER_BSS BIT(3) +#define MT_RX_FILTR_CFG_VER_ERR BIT(4) +#define MT_RX_FILTR_CFG_MCAST BIT(5) +#define MT_RX_FILTR_CFG_BCAST BIT(6) +#define MT_RX_FILTR_CFG_DUP BIT(7) +#define MT_RX_FILTR_CFG_CFACK BIT(8) +#define MT_RX_FILTR_CFG_CFEND BIT(9) +#define MT_RX_FILTR_CFG_ACK BIT(10) +#define MT_RX_FILTR_CFG_CTS BIT(11) +#define MT_RX_FILTR_CFG_RTS BIT(12) +#define MT_RX_FILTR_CFG_PSPOLL BIT(13) +#define MT_RX_FILTR_CFG_BA BIT(14) +#define MT_RX_FILTR_CFG_BAR BIT(15) +#define MT_RX_FILTR_CFG_CTRL_RSV BIT(16) + +#define MT_AUTO_RSP_CFG 0x1404 +#define MT_AUTO_RSP_EN BIT(0) +#define MT_AUTO_RSP_PREAMB_SHORT BIT(4) +#define MT_LEGACY_BASIC_RATE 0x1408 +#define MT_HT_BASIC_RATE 0x140c + +#define MT_HT_CTRL_CFG 0x1410 +#define MT_RX_PARSER_CFG 0x1418 +#define MT_RX_PARSER_RX_SET_NAV_ALL BIT(0) + +#define MT_EXT_CCA_CFG 0x141c +#define MT_EXT_CCA_CFG_CCA0 GENMASK(1, 0) +#define MT_EXT_CCA_CFG_CCA1 GENMASK(3, 2) +#define MT_EXT_CCA_CFG_CCA2 GENMASK(5, 4) +#define MT_EXT_CCA_CFG_CCA3 GENMASK(7, 6) +#define MT_EXT_CCA_CFG_CCA_MASK GENMASK(11, 8) +#define MT_EXT_CCA_CFG_ED_CCA_MASK GENMASK(15, 12) + +#define MT_TX_SW_CFG3 0x1478 + +#define MT_PN_PAD_MODE 0x150c + +#define MT_TXOP_HLDR_ET 0x1608 +#define MT_TXOP_HLDR_TX40M_BLK_EN BIT(1) + +#define MT_PROT_AUTO_TX_CFG 0x1648 +#define MT_PROT_AUTO_TX_CFG_PROT_PADJ GENMASK(11, 8) +#define MT_PROT_AUTO_TX_CFG_AUTO_PADJ GENMASK(27, 24) + +#define MT_RX_STAT_0 0x1700 +#define MT_RX_STAT_0_CRC_ERRORS GENMASK(15, 0) +#define MT_RX_STAT_0_PHY_ERRORS GENMASK(31, 16) + +#define MT_RX_STAT_1 0x1704 +#define MT_RX_STAT_1_CCA_ERRORS GENMASK(15, 0) +#define MT_RX_STAT_1_PLCP_ERRORS GENMASK(31, 16) + +#define MT_RX_STAT_2 0x1708 +#define MT_RX_STAT_2_DUP_ERRORS GENMASK(15, 0) +#define MT_RX_STAT_2_OVERFLOW_ERRORS GENMASK(31, 16) + +#define MT_TX_STA_0 0x170c +#define MT_TX_STA_1 0x1710 +#define MT_TX_STA_2 0x1714 + +#define MT_TX_STAT_FIFO 0x1718 +#define MT_TX_STAT_FIFO_VALID BIT(0) +#define MT_TX_STAT_FIFO_SUCCESS BIT(5) +#define MT_TX_STAT_FIFO_AGGR BIT(6) +#define MT_TX_STAT_FIFO_ACKREQ BIT(7) +#define MT_TX_STAT_FIFO_WCID GENMASK(15, 8) +#define MT_TX_STAT_FIFO_RATE GENMASK(31, 16) + +#define MT_TX_AGG_STAT 0x171c + +#define MT_TX_AGG_CNT_BASE0 0x1720 +#define MT_MPDU_DENSITY_CNT 0x1740 +#define MT_TX_AGG_CNT_BASE1 0x174c + +#define MT_TX_STAT_FIFO_EXT 0x1798 +#define MT_TX_STAT_FIFO_EXT_RETRY GENMASK(7, 0) +#define MT_TX_STAT_FIFO_EXT_PKTID GENMASK(15, 8) + +#define MT_WCID_TX_RATE_BASE 0x1c00 +#define MT_WCID_TX_RATE(i) (MT_WCID_TX_RATE_BASE + ((i) << 3)) + +#define MT_BBP_CORE_BASE 0x2000 +#define MT_BBP_IBI_BASE 0x2100 +#define MT_BBP_AGC_BASE 0x2300 +#define MT_BBP_TXC_BASE 0x2400 +#define MT_BBP_RXC_BASE 0x2500 +#define MT_BBP_TXO_BASE 0x2600 +#define MT_BBP_TXBE_BASE 0x2700 +#define MT_BBP_RXFE_BASE 0x2800 +#define MT_BBP_RXO_BASE 0x2900 +#define MT_BBP_DFS_BASE 0x2a00 +#define MT_BBP_TR_BASE 0x2b00 +#define MT_BBP_CAL_BASE 0x2c00 +#define MT_BBP_DSC_BASE 0x2e00 +#define MT_BBP_PFMU_BASE 0x2f00 + +#define MT_BBP(type, n) (MT_BBP_##type##_BASE + ((n) << 2)) + +#define MT_BBP_CORE_R1_BW GENMASK(4, 3) + +#define MT_BBP_AGC_R0_CTRL_CHAN GENMASK(9, 8) +#define MT_BBP_AGC_R0_BW GENMASK(14, 12) + +/* AGC, R4/R5 */ +#define MT_BBP_AGC_LNA_HIGH_GAIN GENMASK(21, 16) +#define MT_BBP_AGC_LNA_MID_GAIN GENMASK(13, 8) +#define MT_BBP_AGC_LNA_LOW_GAIN GENMASK(5, 0) + +/* AGC, R6/R7 */ +#define MT_BBP_AGC_LNA_ULOW_GAIN GENMASK(5, 0) + +/* AGC, R8/R9 */ +#define MT_BBP_AGC_LNA_GAIN_MODE GENMASK(7, 6) +#define MT_BBP_AGC_GAIN GENMASK(14, 8) + +#define MT_BBP_AGC20_RSSI0 GENMASK(7, 0) +#define MT_BBP_AGC20_RSSI1 GENMASK(15, 8) + +#define MT_BBP_TXBE_R0_CTRL_CHAN GENMASK(1, 0) + +#define MT_WCID_ADDR_BASE 0x1800 +#define MT_WCID_ADDR(n) (MT_WCID_ADDR_BASE + (n) * 8) + +#define MT_SRAM_BASE 0x4000 + +#define MT_WCID_KEY_BASE 0x8000 +#define MT_WCID_KEY(n) (MT_WCID_KEY_BASE + (n) * 32) + +#define MT_WCID_IV_BASE 0xa000 +#define MT_WCID_IV(n) (MT_WCID_IV_BASE + (n) * 8) + +#define MT_WCID_ATTR_BASE 0xa800 +#define MT_WCID_ATTR(n) (MT_WCID_ATTR_BASE + (n) * 4) + +#define MT_WCID_ATTR_PAIRWISE BIT(0) +#define MT_WCID_ATTR_PKEY_MODE GENMASK(3, 1) +#define MT_WCID_ATTR_BSS_IDX GENMASK(6, 4) +#define MT_WCID_ATTR_RXWI_UDF GENMASK(9, 7) +#define MT_WCID_ATTR_PKEY_MODE_EXT BIT(10) +#define MT_WCID_ATTR_BSS_IDX_EXT BIT(11) +#define MT_WCID_ATTR_WAPI_MCBC BIT(15) +#define MT_WCID_ATTR_WAPI_KEYID GENMASK(31, 24) + +#define MT_SKEY_BASE_0 0xac00 +#define MT_SKEY_BASE_1 0xb400 +#define MT_SKEY_0(bss, idx) (MT_SKEY_BASE_0 + (4 * (bss) + (idx)) * 32) +#define MT_SKEY_1(bss, idx) (MT_SKEY_BASE_1 + (4 * ((bss) & 7) + (idx)) * 32) + +#define MT_SKEY_MODE_BASE_0 0xb000 +#define MT_SKEY_MODE_BASE_1 0xb3f0 +#define MT_SKEY_MODE_0(bss) (MT_SKEY_MODE_BASE_0 + (((bss) / 2) << 2)) +#define MT_SKEY_MODE_1(bss) (MT_SKEY_MODE_BASE_1 + ((((bss) & 7) / 2) << 2)) +#define MT_SKEY_MODE_MASK GENMASK(3, 0) +#define MT_SKEY_MODE_SHIFT(bss, idx) (4 * ((idx) + 4 * ((bss) & 1))) + +#define MT_BEACON_BASE 0xc000 + +#define MT_TEMP_SENSOR 0x01d000 +#define MT_TEMP_SENSOR_VAL GENMASK(6, 0) + +#define MT_MCU_RESET_CTL 0x070c +#define MT_MCU_INT_LEVEL 0x0718 +#define MT_MCU_COM_REG0 0x0730 +#define MT_MCU_COM_REG1 0x0734 +#define MT_MCU_COM_REG2 0x0738 +#define MT_MCU_COM_REG3 0x073c + +#define MT_MCU_MEMMAP_WLAN 0x410000 + +#define MT_TXD_INFO_LEN GENMASK(15, 0) +#define MT_TXD_INFO_NEXT_VLD BIT(16) +#define MT_TXD_INFO_TX_BURST BIT(17) +#define MT_TXD_INFO_80211 BIT(19) +#define MT_TXD_INFO_TSO BIT(20) +#define MT_TXD_INFO_CSO BIT(21) +#define MT_TXD_INFO_WIV BIT(24) +#define MT_TXD_INFO_QSEL GENMASK(26, 25) +#define MT_TXD_INFO_DPORT GENMASK(29, 27) +#define MT_TXD_INFO_TYPE GENMASK(31, 30) + +#define MT_RX_FCE_INFO_LEN GENMASK(13, 0) +#define MT_RX_FCE_INFO_SELF_GEN BIT(15) +#define MT_RX_FCE_INFO_CMD_SEQ GENMASK(19, 16) +#define MT_RX_FCE_INFO_EVT_TYPE GENMASK(23, 20) +#define MT_RX_FCE_INFO_PCIE_INTR BIT(24) +#define MT_RX_FCE_INFO_QSEL GENMASK(26, 25) +#define MT_RX_FCE_INFO_D_PORT GENMASK(29, 27) +#define MT_RX_FCE_INFO_TYPE GENMASK(31, 30) + +#define MT_MCU_MSG_LEN GENMASK(15, 0) +#define MT_MCU_MSG_CMD_SEQ GENMASK(19, 16) +#define MT_MCU_MSG_CMD_TYPE GENMASK(26, 20) +#define MT_MCU_MSG_PORT GENMASK(29, 27) +#define MT_MCU_MSG_TYPE GENMASK(31, 30) +#define MT_MCU_MSG_TYPE_CMD BIT(30) + +#define MT_FCE_DMA_ADDR 0x0230 +#define MT_FCE_DMA_LEN 0x0234 + +#define MT_TX_CPU_FROM_FCE_CPU_DESC_IDX 0x09a8 + +#define MT_PKTID_RATE GENMASK(4, 0) +#define MT_PKTID_AC GENMASK(6, 5) + +#define MT_RXINFO_BA BIT(0) +#define MT_RXINFO_DATA BIT(1) +#define MT_RXINFO_NULL BIT(2) +#define MT_RXINFO_FRAG BIT(3) +#define MT_RXINFO_UNICAST BIT(4) +#define MT_RXINFO_MULTICAST BIT(5) +#define MT_RXINFO_BROADCAST BIT(6) +#define MT_RXINFO_MYBSS BIT(7) +#define MT_RXINFO_CRCERR BIT(8) +#define MT_RXINFO_ICVERR BIT(9) +#define MT_RXINFO_MICERR BIT(10) +#define MT_RXINFO_AMSDU BIT(11) +#define MT_RXINFO_HTC BIT(12) +#define MT_RXINFO_RSSI BIT(13) +#define MT_RXINFO_L2PAD BIT(14) +#define MT_RXINFO_AMPDU BIT(15) +#define MT_RXINFO_DECRYPT BIT(16) +#define MT_RXINFO_BSSIDX3 BIT(17) +#define MT_RXINFO_WAPI_KEY BIT(18) +#define MT_RXINFO_PN_LEN GENMASK(21, 19) +#define MT_RXINFO_SW_FTYPE0 BIT(22) +#define MT_RXINFO_SW_FTYPE1 BIT(23) +#define MT_RXINFO_PROBE_RESP BIT(24) +#define MT_RXINFO_BEACON BIT(25) +#define MT_RXINFO_DISASSOC BIT(26) +#define MT_RXINFO_DEAUTH BIT(27) +#define MT_RXINFO_ACTION BIT(28) +#define MT_RXINFO_TCP_SUM_ERR BIT(30) +#define MT_RXINFO_IP_SUM_ERR BIT(31) + +#define MT_RXWI_CTL_WCID GENMASK(7, 0) +#define MT_RXWI_CTL_KEY_IDX GENMASK(9, 8) +#define MT_RXWI_CTL_BSS_IDX GENMASK(12, 10) +#define MT_RXWI_CTL_UDF GENMASK(15, 13) +#define MT_RXWI_CTL_MPDU_LEN GENMASK(29, 16) +#define MT_RXWI_CTL_EOF BIT(31) + +#define MT_RXWI_TID GENMASK(3, 0) +#define MT_RXWI_SN GENMASK(15, 4) + +#define MT_RXWI_RATE_INDEX GENMASK(5, 0) +#define MT_RXWI_RATE_LDPC BIT(6) +#define MT_RXWI_RATE_BW GENMASK(8, 7) +#define MT_RXWI_RATE_SGI BIT(9) +#define MT_RXWI_RATE_STBC BIT(10) +#define MT_RXWI_RATE_LDPC_EXSYM BIT(11) +#define MT_RXWI_RATE_PHY GENMASK(15, 13) + +#define MT_RATE_INDEX_VHT_IDX GENMASK(3, 0) +#define MT_RATE_INDEX_VHT_NSS GENMASK(5, 4) + +#define MT_TX_PWR_ADJ GENMASK(3, 0) + +#define MT_TXWI_FLAGS_FRAG BIT(0) +#define MT_TXWI_FLAGS_MMPS BIT(1) +#define MT_TXWI_FLAGS_CFACK BIT(2) +#define MT_TXWI_FLAGS_TS BIT(3) +#define MT_TXWI_FLAGS_AMPDU BIT(4) +#define MT_TXWI_FLAGS_MPDU_DENSITY GENMASK(7, 5) +#define MT_TXWI_FLAGS_TXOP GENMASK(9, 8) +#define MT_TXWI_FLAGS_NDPS BIT(10) +#define MT_TXWI_FLAGS_RTSBWSIG BIT(11) +#define MT_TXWI_FLAGS_NDP_BW GENMASK(13, 12) +#define MT_TXWI_FLAGS_SOUND BIT(14) +#define MT_TXWI_FLAGS_TX_RATE_LUT BIT(15) + +#define MT_TXWI_ACK_CTL_REQ BIT(0) +#define MT_TXWI_ACK_CTL_NSEQ BIT(1) +#define MT_TXWI_ACK_CTL_BA_WINDOW GENMASK(7, 2) + +#define MT_EE_ANTENNA_DUAL BIT(15) + +#define MT_EE_NIC_CONF_0_RX_PATH GENMASK(3, 0) +#define MT_EE_NIC_CONF_0_TX_PATH GENMASK(7, 4) +#define MT_EE_NIC_CONF_0_PA_TYPE GENMASK(9, 8) +#define MT_EE_NIC_CONF_0_PA_INT_2G BIT(8) +#define MT_EE_NIC_CONF_0_PA_INT_5G BIT(9) +#define MT_EE_NIC_CONF_0_PA_IO_CURRENT BIT(10) +#define MT_EE_NIC_CONF_0_BOARD_TYPE GENMASK(13, 12) + +#define MT_EE_NIC_CONF_1_HW_RF_CTRL BIT(0) +#define MT_EE_NIC_CONF_1_TEMP_TX_ALC BIT(1) +#define MT_EE_NIC_CONF_1_LNA_EXT_2G BIT(2) +#define MT_EE_NIC_CONF_1_LNA_EXT_5G BIT(3) +#define MT_EE_NIC_CONF_1_TX_ALC_EN BIT(13) + +#define MT_EE_NIC_CONF_2_ANT_OPT BIT(3) +#define MT_EE_NIC_CONF_2_ANT_DIV BIT(4) +#define MT_EE_NIC_CONF_2_XTAL_OPTION GENMASK(10, 9) + +#define MT_VEND_TYPE_CFG BIT(30) + +#define MT_CMD_HDR_LEN 4 + +enum mt76_vendor_req { + MT_VEND_DEV_MODE = 0x01, + MT_VEND_WRITE = 0x02, + MT_VEND_POWER_ON = 0x04, + MT_VEND_MULTI_WRITE = 0x06, + MT_VEND_MULTI_READ = 0x07, + MT_VEND_READ_EEPROM = 0x09, + MT_VEND_WRITE_FCE = 0x42, + MT_VEND_WRITE_CFG = 0x46, + MT_VEND_READ_CFG = 0x47, + MT_VEND_READ_EXT = 0x63, + MT_VEND_WRITE_EXT = 0x66, + MT_VEND_FEATURE_SET = 0x91, +}; + +enum mt76_dma_msg_port { + MT_WLAN_PORT, + MT_CPU_RX_PORT, + MT_CPU_TX_PORT, + MT_HOST_PORT, + MT_VIRTUAL_CPU_RX_PORT, + MT_VIRTUAL_CPU_TX_PORT, + MT_DISCARD, +}; + +enum mt76_mcu_cmd { + MT_CMD_FUN_SET_OP = 1, + MT_CMD_LOAD_CR = 2, + MT_CMD_INIT_GAIN_OP = 3, + MT_CMD_DYNC_VGA_OP = 6, + MT_CMD_TDLS_CH_SW = 7, + MT_CMD_BURST_WRITE = 8, + MT_CMD_READ_MODIFY_WRITE = 9, + MT_CMD_RANDOM_READ = 10, + MT_CMD_BURST_READ = 11, + MT_CMD_RANDOM_WRITE = 12, + MT_CMD_LED_MODE_OP = 16, + MT_CMD_POWER_SAVING_OP = 20, + MT_CMD_WOW_CONFIG = 21, + MT_CMD_WOW_QUERY = 22, + MT_CMD_WOW_FEATURE = 24, + MT_CMD_CARRIER_DETECT_OP = 28, + MT_CMD_RADOR_DETECT_OP = 29, + MT_CMD_SWITCH_CHANNEL_OP = 30, + MT_CMD_CALIBRATION_OP = 31, + MT_CMD_BEACON_OP = 32, + MT_CMD_ANTENNA_OP = 33, +}; + +enum mt76_mcu_function { + MT_Q_SELECT = 1, + MT_BW_SETTING = 2, + MT_USB2_SW_DISCONNECT = 2, + MT_USB3_SW_DISCONNECT = 3, + MT_LOG_FW_DEBUG_MSG = 4, + MT_GET_FW_VERSION = 5, +}; + +enum mt76_mcu_cr_mode { + MT_RF_CR, + MT_BBP_CR, + MT_RF_BBP_CR, + MT_HL_TEMP_CR_UPDATE, +}; + +enum mt76_mcu_power_mode { + MT_RADIO_OFF = 0x30, + MT_RADIO_ON = 0x31, + MT_RADIO_OFF_AUTO_WAKEUP = 0x32, + MT_RADIO_OFF_ADVANCE = 0x33, + MT_RADIO_ON_ADVANCE = 0x34, +}; + +enum mt76_mcu_calibration { + MT_MCU_CAL_R = 1, + MT_MCU_CAL_TEMP_SENSOR, + MT_MCU_CAL_RXDCOC, + MT_MCU_CAL_RC, + MT_MCU_CAL_SX_LOGEN, + MT_MCU_CAL_LC, + MT_MCU_CAL_TX_LOFT, + MT_MCU_CAL_TXIQ, + MT_MCU_CAL_TSSI, + MT_MCU_CAL_TSSI_COMP, + MT_MCU_CAL_DPD, + MT_MCU_CAL_RXIQC_FI, + MT_MCU_CAL_RXIQC_FD, + MT_MCU_CAL_PWRON, + MT_MCU_CAL_TX_SHAPING, +}; + +enum mt76_eeprom_mode { + MT_EE_READ, + MT_EE_PHYSICAL_READ, +}; + +enum mt76_eeprom_field { + MT_EE_CHIP_ID = 0x0000, + MT_EE_VERSION = 0x0002, + MT_EE_MAC_ADDR = 0x0004, + MT_EE_PCI_ID = 0x000a, + MT_EE_ANTENNA = 0x0022, + MT_EE_CFG1_INIT = 0x0024, + MT_EE_NIC_CONF_0 = 0x0034, + MT_EE_NIC_CONF_1 = 0x0036, + MT_EE_COUNTRY_REGION_5GHZ = 0x0038, + MT_EE_COUNTRY_REGION_2GHZ = 0x0039, + MT_EE_FREQ_OFFSET = 0x003a, + MT_EE_NIC_CONF_2 = 0x0042, + + MT_EE_XTAL_TRIM_1 = 0x003a, + MT_EE_XTAL_TRIM_2 = 0x009e, + + MT_EE_LNA_GAIN = 0x0044, + MT_EE_RSSI_OFFSET_2G_0 = 0x0046, + MT_EE_RSSI_OFFSET_2G_1 = 0x0048, + MT_EE_LNA_GAIN_5GHZ_1 = 0x0049, + MT_EE_RSSI_OFFSET_5G_0 = 0x004a, + MT_EE_RSSI_OFFSET_5G_1 = 0x004c, + MT_EE_LNA_GAIN_5GHZ_2 = 0x004d, + + MT_EE_TX_POWER_DELTA_BW40 = 0x0050, + MT_EE_TX_POWER_DELTA_BW80 = 0x0052, + + MT_EE_TX_POWER_EXT_PA_5G = 0x0054, + + MT_EE_TX_POWER_0_START_2G = 0x0056, + MT_EE_TX_POWER_1_START_2G = 0x005c, + +#define MT_TX_POWER_GROUP_SIZE_5G 5 +#define MT_TX_POWER_GROUPS_5G 6 + MT_EE_TX_POWER_0_START_5G = 0x0062, + MT_EE_TSSI_SLOPE_2G = 0x006e, + + MT_EE_TX_POWER_0_GRP3_TX_POWER_DELTA = 0x0074, + MT_EE_TX_POWER_0_GRP4_TSSI_SLOPE = 0x0076, + + MT_EE_TX_POWER_1_START_5G = 0x0080, + + MT_EE_TX_POWER_CCK = 0x00a0, + MT_EE_TX_POWER_OFDM_2G_6M = 0x00a2, + MT_EE_TX_POWER_OFDM_2G_24M = 0x00a4, + MT_EE_TX_POWER_OFDM_5G_6M = 0x00b2, + MT_EE_TX_POWER_OFDM_5G_24M = 0x00b4, + MT_EE_TX_POWER_HT_MCS0 = 0x00a6, + MT_EE_TX_POWER_HT_MCS4 = 0x00a8, + MT_EE_TX_POWER_HT_MCS8 = 0x00aa, + MT_EE_TX_POWER_HT_MCS12 = 0x00ac, + MT_EE_TX_POWER_VHT_MCS0 = 0x00ba, + MT_EE_TX_POWER_VHT_MCS4 = 0x00bc, + MT_EE_TX_POWER_VHT_MCS8 = 0x00be, + + MT_EE_2G_TARGET_POWER = 0x00d0, + MT_EE_TEMP_OFFSET = 0x00d1, + MT_EE_5G_TARGET_POWER = 0x00d2, + MT_EE_TSSI_BOUND1 = 0x00d4, + MT_EE_TSSI_BOUND2 = 0x00d6, + MT_EE_TSSI_BOUND3 = 0x00d8, + MT_EE_TSSI_BOUND4 = 0x00da, + MT_EE_FREQ_OFFSET_COMPENSATION = 0x00db, + MT_EE_TSSI_BOUND5 = 0x00dc, + MT_EE_TX_POWER_BYRATE_BASE = 0x00de, + + MT_EE_TSSI_SLOPE_5G = 0x00f0, + MT_EE_RF_TEMP_COMP_SLOPE_5G = 0x00f2, + MT_EE_RF_TEMP_COMP_SLOPE_2G = 0x00f4, + + MT_EE_RF_2G_TSSI_OFF_TXPOWER = 0x00f6, + MT_EE_RF_2G_RX_HIGH_GAIN = 0x00f8, + MT_EE_RF_5G_GRP0_1_RX_HIGH_GAIN = 0x00fa, + MT_EE_RF_5G_GRP2_3_RX_HIGH_GAIN = 0x00fc, + MT_EE_RF_5G_GRP4_5_RX_HIGH_GAIN = 0x00fe, + + MT_EE_BT_RCAL_RESULT = 0x0138, + MT_EE_BT_VCDL_CALIBRATION = 0x013c, + MT_EE_BT_PMUCFG = 0x013e, + + MT_EE_USAGE_MAP_START = 0x01e0, + MT_EE_USAGE_MAP_END = 0x01fc, +}; + +enum mt76_phy_type { + MT_PHY_TYPE_CCK, + MT_PHY_TYPE_OFDM, + MT_PHY_TYPE_HT, + MT_PHY_TYPE_HT_GF, + MT_PHY_TYPE_VHT, + MT_PHY_TYPE_HE_SU = 8, + MT_PHY_TYPE_HE_EXT_SU, + MT_PHY_TYPE_HE_TB, + MT_PHY_TYPE_HE_MU, +}; + +enum mt76_phy_bandwidth { + MT_PHY_BW_20, + MT_PHY_BW_40, + MT_PHY_BW_80, +}; + +enum mt76_cal_channel_group { + MT_CH_5G_JAPAN, + MT_CH_5G_UNII_1, + MT_CH_5G_UNII_2, + MT_CH_5G_UNII_2E_1, + MT_CH_5G_UNII_2E_2, + MT_CH_5G_UNII_3, +}; + +enum mt76_qsel { + MT_QSEL_MGMT, + MT_QSEL_HCCA, + MT_QSEL_EDCA, + MT_QSEL_EDCA_2, +}; + +struct mt76_fw_header { + __le32 ilm_len; + __le32 dlm_len; + __le16 build_ver; + __le16 fw_ver; + u8 pad[4]; + char build_time[16]; +} __packed; + +struct mt76_rxwi { + __le32 rxinfo; + __le32 ctl; + __le16 tid_sn; + __le16 rate; + u8 rssi[4]; + __le32 bbp_rxinfo[4]; +} __packed; + +struct mt76_txwi { + __le16 flags; + __le16 rate; + u8 ack_ctl; + u8 wcid; + __le16 len_ctl; + __le32 iv; + __le32 eiv; + u8 aid; + u8 txstream; + u8 ctl2; + u8 pktid; +} __packed; diff --git a/uninstall.sh b/uninstall.sh index b560e20..1d15456 100755 --- a/uninstall.sh +++ b/uninstall.sh @@ -21,6 +21,7 @@ if [ -n "$version" ]; then dkms remove -m xone -v "$version" --all rm -r "/usr/src/xone-$version" rm -f /etc/modprobe.d/xone-blacklist.conf + rm -f /usr/local/bin/xone-get-firmware.sh else echo 'Driver is not installed!' >&2 fi