mirror of
https://github.com/Mathewnd/Astral.git
synced 2026-10-27 12:48:22 +00:00
u80211_drv: port over rtl8188eu driver
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
#include <u80211_drv/u80211_drv.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <kernel/alloc.h>
|
||||
#include <kernel/init.h>
|
||||
#include <kernel/interrupt.h>
|
||||
#include <kernel/usb.h>
|
||||
#include <kernel/work.h>
|
||||
#include <logging.h>
|
||||
|
||||
typedef struct {
|
||||
usb_device_t *device;
|
||||
usb_interface_t *interface;
|
||||
work_t work;
|
||||
} u80211_drv_usb_device_t;
|
||||
|
||||
static work_queue_t *u80211_drv_attach_queue;
|
||||
|
||||
static void u80211_drv_usb_attach_worker(void *context, size_t pending) {
|
||||
(void)pending;
|
||||
u80211_drv_usb_device_t *device = context;
|
||||
|
||||
int status = u80211_drv_attach(device->device, device->interface);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
printf("u80211_drv: attachment failed with status %d\n", status);
|
||||
}
|
||||
|
||||
static int u80211_drv_usb_probe(usb_device_t *device, usb_interface_t *interface) {
|
||||
int status = u80211_drv_probe(device, interface);
|
||||
if (status == U80211_DRV_STATUS_SUCCESS)
|
||||
return USB_DRIVER_SCORE_EXACT_MATCH;
|
||||
|
||||
if (status != U80211_DRV_STATUS_NO_MATCH)
|
||||
printf("u80211_drv: probe failed with status %d\n", status);
|
||||
|
||||
return USB_DRIVER_SCORE_NONE;
|
||||
}
|
||||
|
||||
static int u80211_drv_usb_attach(usb_device_t *device, usb_interface_t *interface) {
|
||||
u80211_drv_usb_device_t *usb_device = alloc(sizeof(*usb_device));
|
||||
if (usb_device == NULL)
|
||||
return ENOMEM;
|
||||
|
||||
usb_device->device = device;
|
||||
usb_device->interface = interface;
|
||||
WORK_INIT(&usb_device->work, u80211_drv_usb_attach_worker, usb_device);
|
||||
interface->driver_data = usb_device;
|
||||
work_enqueue(u80211_drv_attach_queue, &usb_device->work);
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_USB_CLASS_DRIVER(u80211_drv,
|
||||
.name = "u80211 USB NIC",
|
||||
.probe = u80211_drv_usb_probe,
|
||||
.attach = u80211_drv_usb_attach
|
||||
)
|
||||
|
||||
static void u80211_drv_usb_init(void) {
|
||||
u80211_drv_attach_queue = work_queue_create("u80211_drv", 1, IPL_USB);
|
||||
__assert(u80211_drv_attach_queue);
|
||||
}
|
||||
|
||||
INIT_ROUTINE_DEFINE(u80211_drv_usb, INIT_ROUTINE_FLAGS_NONE, u80211_drv_usb_init, work_queue);
|
||||
@@ -0,0 +1,37 @@
|
||||
#ifndef U80211_DRV_80211_H
|
||||
#define U80211_DRV_80211_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <u80211_drv/util.h>
|
||||
|
||||
#define U80211_DRV_80211_FRAME_TYPE_MANAGEMENT 0
|
||||
#define U80211_DRV_80211_FRAME_TYPE_DATA 2
|
||||
|
||||
#define U80211_DRV_80211_FRAME_DATA_QOS_BIT 0x8
|
||||
#define U80211_DRV_80211_HEADER_MINIMUM_SIZE 24
|
||||
#define U80211_DRV_80211_MAX_MPDU_SIZE 2352
|
||||
|
||||
static inline uint16_t u80211_drv_80211_frame_control(const void *frame) {
|
||||
return u80211_drv_deserialize_le16(frame);
|
||||
}
|
||||
|
||||
static inline uint8_t u80211_drv_80211_frame_type(uint16_t frame_control) {
|
||||
return (frame_control >> 2) & 0x3;
|
||||
}
|
||||
|
||||
static inline uint8_t u80211_drv_80211_frame_subtype(uint16_t frame_control) {
|
||||
return (frame_control >> 4) & 0xf;
|
||||
}
|
||||
|
||||
static inline const uint8_t *u80211_drv_80211_receiver_address(const void *frame) {
|
||||
const uint8_t *frame_bytes = frame;
|
||||
return frame_bytes + 4;
|
||||
}
|
||||
|
||||
static inline uint16_t u80211_drv_80211_sequence_number(const void *frame) {
|
||||
const uint8_t *frame_bytes = frame;
|
||||
return u80211_drv_deserialize_le16(frame_bytes + 22) >> 4;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,8 @@
|
||||
#ifndef U80211_DRV_DRV_INIT_H
|
||||
#define U80211_DRV_DRV_INIT_H
|
||||
|
||||
#include <u80211_drv/kernel_interface.h>
|
||||
|
||||
int u80211_drv_rtl8188eu_init(u80211_drv_device_handle_t device, u80211_drv_interface_handle_t interface);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,171 @@
|
||||
#ifndef U80211_DRV_KERNEL_INTERFACE_H
|
||||
#define U80211_DRV_KERNEL_INTERFACE_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
// opaque handles supplied by the kernel integration
|
||||
// the driver only stores and passes these handles back to the integration
|
||||
typedef void *u80211_drv_device_handle_t;
|
||||
typedef void *u80211_drv_interface_handle_t;
|
||||
typedef void *u80211_drv_transfer_handle_t;
|
||||
typedef void *u80211_drv_network_device_handle_t;
|
||||
|
||||
typedef void (*u80211_drv_kernel_transfer_callback_t)(void *context, int status, size_t transferred_size);
|
||||
|
||||
// usb device descriptor fields used to match a driver
|
||||
typedef struct {
|
||||
uint16_t vendor_id;
|
||||
uint16_t product_id;
|
||||
} u80211_drv_device_descriptor_t;
|
||||
|
||||
// usb interface descriptor fields used to match a driver and discover its endpoints
|
||||
typedef struct {
|
||||
uint8_t number;
|
||||
uint8_t class_code;
|
||||
uint8_t subclass;
|
||||
uint8_t protocol;
|
||||
uint8_t endpoint_count;
|
||||
} u80211_drv_interface_descriptor_t;
|
||||
|
||||
// usb endpoint descriptor fields required by a driver
|
||||
typedef struct {
|
||||
uint8_t address;
|
||||
uint8_t attributes;
|
||||
uint16_t maximum_packet_size;
|
||||
uint8_t interval;
|
||||
} u80211_drv_endpoint_descriptor_t;
|
||||
|
||||
#define U80211_DRV_DEVICE_MAC_ADDRESS_LEN 6
|
||||
#define U80211_DRV_DEVICE_RATE_BITMAP_LEN 16
|
||||
|
||||
// radio properties published once initialization has completed
|
||||
typedef struct {
|
||||
uint8_t mac_address[U80211_DRV_DEVICE_MAC_ADDRESS_LEN];
|
||||
// bit N represents a supported rate of N * 500 kbit/s
|
||||
uint8_t rate_bitmap[U80211_DRV_DEVICE_RATE_BITMAP_LEN];
|
||||
} u80211_drv_device_metadata_t;
|
||||
|
||||
// cipher values used by key installation and transmission
|
||||
#define U80211_DRV_CIPHER_NONE -1
|
||||
#define U80211_DRV_CIPHER_CCMP 0
|
||||
#define U80211_DRV_CIPHER_TKIP 1
|
||||
#define U80211_DRV_CIPHER_WEP40 2
|
||||
#define U80211_DRV_CIPHER_WEP104 3
|
||||
|
||||
#define U80211_DRV_KEY_PAIRWISE (1u << 0)
|
||||
#define U80211_DRV_KEY_GROUP (1u << 1)
|
||||
#define U80211_DRV_KEY_RX (1u << 2)
|
||||
#define U80211_DRV_KEY_TX (1u << 3)
|
||||
|
||||
// key data passed to set_key
|
||||
typedef struct {
|
||||
int cipher;
|
||||
uint8_t index;
|
||||
uint8_t peer[U80211_DRV_DEVICE_MAC_ADDRESS_LEN];
|
||||
const uint8_t *key;
|
||||
size_t key_len;
|
||||
uint32_t flags;
|
||||
} u80211_drv_key_t;
|
||||
|
||||
// per-packet encryption selection
|
||||
// key is the installed key index or -1 when no key is selected
|
||||
// cipher is one of U80211_DRV_CIPHER_*
|
||||
typedef struct {
|
||||
int key;
|
||||
int cipher;
|
||||
} u80211_drv_transmit_options_t;
|
||||
|
||||
// operations implemented by a hardware driver and published through u80211_drv_device_ready()
|
||||
// unless otherwise stated operations return a U80211_DRV_STATUS_* value
|
||||
typedef struct {
|
||||
// allocates a buffer of 'size' bytes
|
||||
// on success '*buffer' points at the start of the writable 802.11 frame region
|
||||
int (*allocate_tx_buffer)(size_t size, void **buffer);
|
||||
void (*free_tx_buffer)(void *buffer);
|
||||
// transmits the bytes and consumes buffer including on failure
|
||||
int (*transmit)(void *device, void *buffer, size_t size, size_t current_offset, const u80211_drv_transmit_options_t *options);
|
||||
// sets the rf channel number the device will rx/tx on
|
||||
int (*set_channel)(void *device, uint8_t channel);
|
||||
// adds an encryption key
|
||||
int (*set_key)(void *device, const u80211_drv_key_t *key);
|
||||
// deletes an encryption ke
|
||||
int (*del_key)(void *device, uint8_t index);
|
||||
} u80211_drv_device_ops_t;
|
||||
|
||||
// values used to control usb transfers
|
||||
#define U80211_DRV_KERNEL_XFER_OUT 0x00
|
||||
#define U80211_DRV_KERNEL_XFER_IN 0x80
|
||||
#define U80211_DRV_KERNEL_XFER_DIRECTION_MASK 0x80
|
||||
|
||||
#define U80211_DRV_KERNEL_ENDPOINT_TRANSFER_TYPE_MASK 0x03
|
||||
#define U80211_DRV_KERNEL_ENDPOINT_TRANSFER_TYPE_BULK 0x02
|
||||
|
||||
#define U80211_DRV_KERNEL_XFER_REQUEST_TYPE_STANDARD 0x00
|
||||
#define U80211_DRV_KERNEL_XFER_REQUEST_TYPE_CLASS 0x20
|
||||
#define U80211_DRV_KERNEL_XFER_REQUEST_TYPE_VENDOR 0x40
|
||||
#define U80211_DRV_KERNEL_XFER_REQUEST_TYPE_RESERVED 0x60
|
||||
#define U80211_DRV_KERNEL_XFER_REQUEST_TYPE_MASK 0x60
|
||||
|
||||
#define U80211_DRV_KERNEL_XFER_RECIPIENT_DEVICE 0x00
|
||||
#define U80211_DRV_KERNEL_XFER_RECIPIENT_INTERFACE 0x01
|
||||
#define U80211_DRV_KERNEL_XFER_RECIPIENT_ENDPOINT 0x02
|
||||
#define U80211_DRV_KERNEL_XFER_RECIPIENT_OTHER 0x03
|
||||
#define U80211_DRV_KERNEL_XFER_RECIPIENT_MASK 0x1f
|
||||
|
||||
#define U80211_DRV_KERNEL_PRINT_LEVEL_INFO 0
|
||||
#define U80211_DRV_KERNEL_PRINT_LEVEL_WARN 1
|
||||
#define U80211_DRV_KERNEL_PRINT_LEVEL_ERROR 2
|
||||
|
||||
// allocates 'size' bytes of memory
|
||||
// return NULL on allocation failure
|
||||
void *u80211_drv_kernel_allocate(size_t size);
|
||||
|
||||
// frees the kernel memory referenced by 'memory'
|
||||
// 'memory' will never be NULL
|
||||
void u80211_drv_kernel_free(void *memory);
|
||||
|
||||
// requests for firmware to be loaded
|
||||
// invokes callback once firmware is available
|
||||
// the callback must not be invoked on failure
|
||||
// 'firmware_data' only needs to be valid for the duration of the callback
|
||||
typedef void (*u80211_drv_kernel_firmware_callback_t)(void *context, const void *firmware_data, size_t firmware_size);
|
||||
int u80211_drv_kernel_get_firmware(const char *name, u80211_drv_kernel_firmware_callback_t callback, void *context);
|
||||
|
||||
// these functions return usb descriptors into the specified buffers
|
||||
int u80211_drv_kernel_get_device_descriptor(u80211_drv_device_handle_t device, u80211_drv_device_descriptor_t *descriptor);
|
||||
int u80211_drv_kernel_get_interface_descriptor(u80211_drv_interface_handle_t interface, u80211_drv_interface_descriptor_t *descriptor);
|
||||
int u80211_drv_kernel_get_endpoints(u80211_drv_interface_handle_t interface, u80211_drv_endpoint_descriptor_t *endpoints, size_t endpoint_count);
|
||||
|
||||
// synchronous usb control transfers with a timeout in milliseconds
|
||||
// 'flags' combines U80211_DRV_KERNEL_XFER_* values into the usb control request type
|
||||
int u80211_drv_kernel_submit_control_xfer_and_wait(u80211_drv_device_handle_t device, uint8_t flags, uint8_t request, uint16_t value, uint16_t index, void *buf, uint16_t buffer_size, size_t *transferred_size, unsigned int timeout);
|
||||
|
||||
// synchronous usb bulk transfers with a timeout in milliseconds
|
||||
int u80211_drv_kernel_submit_bulk_xfer_and_wait(u80211_drv_device_handle_t device, uint8_t endpoint_address, void *buf, size_t buffer_size, size_t *transferred_size, unsigned int timeout);
|
||||
|
||||
// allocates a reusable asynchronous bulk transfer
|
||||
// 'buffer' and 'context' must stay valid across submissions and callbacks
|
||||
// callback receives a U80211_DRV_STATUS_* value and zero transferred_size on failure
|
||||
// a completed transfer may be submitted again from its callback
|
||||
int u80211_drv_kernel_allocate_bulk_xfer(u80211_drv_device_handle_t device, uint8_t endpoint_address, void *buffer, size_t buffer_size, u80211_drv_kernel_transfer_callback_t callback, void *context, u80211_drv_transfer_handle_t *transfer);
|
||||
|
||||
// starts an asynchronous transfer
|
||||
// submitting one that is already active must return U80211_DRV_STATUS_INVALID_ARGUMENT
|
||||
int u80211_drv_kernel_submit_xfer(u80211_drv_transfer_handle_t transfer);
|
||||
|
||||
// busy-waits for at least 'microseconds' before returning
|
||||
void u80211_drv_kernel_stall_us(unsigned int microseconds);
|
||||
|
||||
// writes one complete message at the requested U80211_DRV_KERNEL_PRINT_LEVEL_*
|
||||
void u80211_drv_kernel_print(int level, const char *msg);
|
||||
|
||||
// publishes an initialized radio to the kernel integration
|
||||
// 'network_device' receives the handle used for received packets
|
||||
int u80211_drv_device_ready(void *device, const u80211_drv_device_metadata_t *metadata, const u80211_drv_device_ops_t *ops, u80211_drv_network_device_handle_t *network_device);
|
||||
|
||||
// delivers one 802.11 packet
|
||||
// might be called from an interrupt context
|
||||
void u80211_drv_packet_received(u80211_drv_network_device_handle_t device, void *packet, size_t packet_size);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,389 @@
|
||||
#ifndef U80211_DRV_RTL8188EU_H
|
||||
#define U80211_DRV_RTL8188EU_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <u80211_drv/kernel_interface.h>
|
||||
|
||||
#define U80211_DRV_RTL8188EU_REG_SYS_ISO_CTRL 0x0000
|
||||
#define U80211_DRV_RTL8188EU_REG_SYS_ISO_CTRL_PWC_EV12V (1u << 15)
|
||||
|
||||
#define U80211_DRV_RTL8188EU_REG_SYS_FUNC 0x0002
|
||||
#define U80211_DRV_RTL8188EU_REG_SYS_FUNC_BBRSTB (1u << 0)
|
||||
#define U80211_DRV_RTL8188EU_REG_SYS_FUNC_BB_GLB_RSTN (1u << 1)
|
||||
#define U80211_DRV_RTL8188EU_REG_SYS_FUNC_USBA (1u << 2)
|
||||
#define U80211_DRV_RTL8188EU_REG_SYS_FUNC_USBD (1u << 4)
|
||||
#define U80211_DRV_RTL8188EU_REG_SYS_FUNC_CPUEN (1u << 10)
|
||||
#define U80211_DRV_RTL8188EU_REG_SYS_FUNC_ELDR (1u << 12)
|
||||
#define U80211_DRV_RTL8188EU_REG_SYS_FUNC_DIO_RF (1u << 13)
|
||||
|
||||
#define U80211_DRV_RTL8188EU_REG_APS_FSMCO 0x0004
|
||||
#define U80211_DRV_RTL8188EU_REG_APS_FSMCO_MAC_ENABLE (1u << 8)
|
||||
#define U80211_DRV_RTL8188EU_REG_APS_FSMCO_HW_SUSPEND (1u << 11)
|
||||
#define U80211_DRV_RTL8188EU_REG_APS_FSMCO_PCIE (1u << 12)
|
||||
#define U80211_DRV_RTL8188EU_REG_APS_FSMCO_HW_POWERDOWN (1u << 15)
|
||||
#define U80211_DRV_RTL8188EU_REG_APS_FSMCO_POWER_READY (1u << 17)
|
||||
|
||||
#define U80211_DRV_RTL8188EU_REG_SYS_CLKR 0x0008
|
||||
#define U80211_DRV_RTL8188EU_REG_SYS_CLKR_ANA8M (1u << 1)
|
||||
#define U80211_DRV_RTL8188EU_REG_SYS_CLKR_LOADER_ENABLE (1u << 5)
|
||||
|
||||
#define U80211_DRV_RTL8188EU_REG_9346CR 0x000a
|
||||
#define U80211_DRV_RTL8188EU_REG_9346CR_EEPROM_BOOT (1u << 4)
|
||||
#define U80211_DRV_RTL8188EU_REG_9346CR_EEPROM_ENABLE (1u << 5)
|
||||
|
||||
#define U80211_DRV_RTL8188EU_REG_RSV_CTRL 0x001c
|
||||
#define U80211_DRV_RTL8188EU_REG_RSV_CTRL_WLOCK_00 (1u << 1)
|
||||
#define U80211_DRV_RTL8188EU_REG_RSV_CTRL_MCU_RST (1u << 11)
|
||||
|
||||
#define U80211_DRV_RTL8188EU_REG_RF_CTRL 0x001f
|
||||
#define U80211_DRV_RTL8188EU_REG_RF_CTRL_ENABLE (1u << 0)
|
||||
#define U80211_DRV_RTL8188EU_REG_RF_CTRL_RSTB (1u << 1)
|
||||
#define U80211_DRV_RTL8188EU_REG_RF_CTRL_SDMRSTB (1u << 2)
|
||||
|
||||
#define U80211_DRV_RTL8188EU_REG_LPLDO_CTRL 0x0023
|
||||
#define U80211_DRV_RTL8188EU_REG_LPLDO_CTRL_SLEEP (1u << 4)
|
||||
|
||||
#define U80211_DRV_RTL8188EU_REG_AFE_XTAL_CTRL 0x0024
|
||||
#define U80211_DRV_RTL8188EU_REG_AFE_XTAL_CTRL_SCHMITT_TRIGGER (1u << 23)
|
||||
#define U80211_DRV_RTL8188EU_REG_AFE_XTAL_CTRL_ADDRESS_MASK 0x007ff800
|
||||
#define U80211_DRV_RTL8188EU_REG_AFE_XTAL_CTRL_ADDRESS_SHIFT 11
|
||||
|
||||
#define U80211_DRV_RTL8188EU_REG_EFUSE_CTRL 0x0030
|
||||
#define U80211_DRV_RTL8188EU_REG_EFUSE_CTRL_READ_READY (1u << 31)
|
||||
|
||||
#define U80211_DRV_RTL8188EU_REG_LEDCFG2 0x004e
|
||||
#define U80211_DRV_RTL8188EU_REG_LEDCFG2_HW_LED_CONTROL (1u << 1)
|
||||
#define U80211_DRV_RTL8188EU_REG_LEDCFG2_SW_LED_DISABLE (1u << 3)
|
||||
#define U80211_DRV_RTL8188EU_REG_LEDCFG2_HW_LED_ENABLE (1u << 5)
|
||||
|
||||
#define U80211_DRV_RTL8188EU_REG_EFUSE_ACCESS 0x00cf
|
||||
#define U80211_DRV_RTL8188EU_EFUSE_ACCESS_ENABLE 0x69
|
||||
#define U80211_DRV_RTL8188EU_EFUSE_ACCESS_DISABLE 0x00
|
||||
|
||||
#define U80211_DRV_RTL8188EU_REG_MCUFWDL 0x0080
|
||||
#define U80211_DRV_RTL8188EU_REG_MCUFWDL_ENABLE (1u << 0)
|
||||
#define U80211_DRV_RTL8188EU_REG_MCUFWDL_READY (1u << 1)
|
||||
#define U80211_DRV_RTL8188EU_REG_MCUFWDL_CHECKSUM_REPORT (1u << 2)
|
||||
#define U80211_DRV_RTL8188EU_REG_MCUFWDL_WINTINI_READY (1u << 6)
|
||||
#define U80211_DRV_RTL8188EU_REG_MCUFWDL_RAM_DOWNLOAD_SELECT (1u << 7)
|
||||
#define U80211_DRV_RTL8188EU_REG_MCUFWDL_PAGE_MASK (7u << 16)
|
||||
#define U80211_DRV_RTL8188EU_REG_MCUFWDL_ROM_DOWNLOAD_LENGTH (1u << 19)
|
||||
|
||||
#define U80211_DRV_RTL8188EU_FIRMWARE_START_ADDRESS 0x1000
|
||||
#define U80211_DRV_RTL8188EU_FIRMWARE_PAGE_SIZE 4096
|
||||
#define U80211_DRV_RTL8188EU_FIRMWARE_HEADER_SIZE 32
|
||||
|
||||
#define U80211_DRV_RTL8188EU_EFUSE_PHYSICAL_LEN 512
|
||||
#define U80211_DRV_RTL8188EU_EFUSE_MAP_LEN 512
|
||||
#define U80211_DRV_RTL8188EU_EFUSE_WORDS_PER_SECTION 4
|
||||
#define U80211_DRV_RTL8188EU_EFUSE_RTL_ID 0x8129
|
||||
#define U80211_DRV_RTL8188EU_MAC_ADDRESS_LEN 6
|
||||
#define U80211_DRV_RTL8188EU_CCK_TX_POWER_BASE_INDEX_COUNT 6
|
||||
#define U80211_DRV_RTL8188EU_HT40_1S_TX_POWER_BASE_INDEX_COUNT 5
|
||||
|
||||
#define U80211_DRV_RTL8188EU_REG_SYS_CFG 0xf0
|
||||
#define U80211_DRV_RTL8188EU_REG_SYS_CFG_TRP_VAUX_EN (1u << 23)
|
||||
#define U80211_DRV_RTL8188EU_REG_SYS_CFG_VER(value) (((value) >> 12) & 0xfu)
|
||||
|
||||
#define U80211_DRV_RTL8188EU_REG_CR 0x0100
|
||||
#define U80211_DRV_RTL8188EU_REG_CR_HCI_TXDMA_ENABLE (1u << 0)
|
||||
#define U80211_DRV_RTL8188EU_REG_CR_HCI_RXDMA_ENABLE (1u << 1)
|
||||
#define U80211_DRV_RTL8188EU_REG_CR_TXDMA_ENABLE (1u << 2)
|
||||
#define U80211_DRV_RTL8188EU_REG_CR_RXDMA_ENABLE (1u << 3)
|
||||
#define U80211_DRV_RTL8188EU_REG_CR_PROTOCOL_ENABLE (1u << 4)
|
||||
#define U80211_DRV_RTL8188EU_REG_CR_SCHEDULE_ENABLE (1u << 5)
|
||||
#define U80211_DRV_RTL8188EU_REG_CR_MAC_TX_ENABLE (1u << 6)
|
||||
#define U80211_DRV_RTL8188EU_REG_CR_MAC_RX_ENABLE (1u << 7)
|
||||
#define U80211_DRV_RTL8188EU_REG_CR_SECURITY_ENABLE (1u << 9)
|
||||
#define U80211_DRV_RTL8188EU_REG_CR_CALTIMER_ENABLE (1u << 10)
|
||||
|
||||
#define U80211_DRV_RTL8188EU_REG_MSR 0x0102
|
||||
#define U80211_DRV_RTL8188EU_REG_MSR_NETWORK_TYPE_MASK 0x03
|
||||
|
||||
#define U80211_DRV_RTL8188EU_REG_PBP 0x0104
|
||||
#define U80211_DRV_RTL8188EU_REG_PBP_128_BYTES 0x11
|
||||
|
||||
#define U80211_DRV_RTL8188EU_REG_TRXDMA_CTRL 0x010c
|
||||
#define U80211_DRV_RTL8188EU_REG_TRXDMA_CTRL_VO_SHIFT 4
|
||||
#define U80211_DRV_RTL8188EU_REG_TRXDMA_CTRL_VI_SHIFT 6
|
||||
#define U80211_DRV_RTL8188EU_REG_TRXDMA_CTRL_BE_SHIFT 8
|
||||
#define U80211_DRV_RTL8188EU_REG_TRXDMA_CTRL_BK_SHIFT 10
|
||||
#define U80211_DRV_RTL8188EU_REG_TRXDMA_CTRL_MG_SHIFT 12
|
||||
#define U80211_DRV_RTL8188EU_REG_TRXDMA_CTRL_HI_SHIFT 14
|
||||
#define U80211_DRV_RTL8188EU_REG_TRXDMA_CTRL_LOW_CONTROL_MASK 0x0007
|
||||
#define U80211_DRV_RTL8188EU_REG_TRXDMA_CTRL_RXDMA_AGG_ENABLE (1u << 2)
|
||||
|
||||
#define U80211_DRV_RTL8188EU_REG_TRXFF_BNDY 0x0114
|
||||
#define U80211_DRV_RTL8188EU_RX_FIFO_BOUNDARY 0x25ff
|
||||
|
||||
#define U80211_DRV_RTL8188EU_REG_LLT_INIT 0x01e0
|
||||
#define U80211_DRV_RTL8188EU_REG_LLT_INIT_OP_WRITE (1u << 30)
|
||||
#define U80211_DRV_RTL8188EU_REG_LLT_INIT_OP_MASK (3u << 30)
|
||||
#define U80211_DRV_RTL8188EU_LLT_LAST_ENTRY 0xaf
|
||||
#define U80211_DRV_RTL8188EU_LLT_END 0xff
|
||||
|
||||
#define U80211_DRV_RTL8188EU_REG_RQPN 0x0200
|
||||
#define U80211_DRV_RTL8188EU_REG_RQPN_HI_SHIFT 0
|
||||
#define U80211_DRV_RTL8188EU_REG_RQPN_LO_SHIFT 8
|
||||
#define U80211_DRV_RTL8188EU_REG_RQPN_PUB_SHIFT 16
|
||||
#define U80211_DRV_RTL8188EU_REG_RQPN_LOAD (1u << 31)
|
||||
|
||||
#define U80211_DRV_RTL8188EU_REG_RQPN_NPQ 0x0214
|
||||
#define U80211_DRV_RTL8188EU_REG_RQPN_NPQ_SHIFT 0
|
||||
|
||||
#define U80211_DRV_RTL8188EU_REG_TDECTRL 0x0208
|
||||
|
||||
#define U80211_DRV_RTL8188EU_REG_FWHW_TXQ_CTRL 0x0420
|
||||
#define U80211_DRV_RTL8188EU_REG_FWHW_TXQ_CTRL_AMPDU_RTY_NEW (1u << 7)
|
||||
#define U80211_DRV_RTL8188EU_REG_HWSEQ_CTRL 0x0423
|
||||
|
||||
#define U80211_DRV_RTL8188EU_REG_SPEC_SIFS 0x0428
|
||||
#define U80211_DRV_RTL8188EU_REG_RL 0x042a
|
||||
|
||||
#define U80211_DRV_RTL8188EU_REG_RRSR 0x0440
|
||||
#define U80211_DRV_RTL8188EU_REG_RRSR_RATE_MASK 0x000fffff
|
||||
#define U80211_DRV_RTL8188EU_REG_RRSR_RATE_CCK_ONLY_1M 0x000ffff1
|
||||
|
||||
#define U80211_DRV_RTL8188EU_REG_TXPKTBUF_BCNQ_BDNY 0x0424
|
||||
#define U80211_DRV_RTL8188EU_REG_TXPKTBUF_MGQ_BDNY 0x0425
|
||||
#define U80211_DRV_RTL8188EU_REG_TXPKTBUF_WMAC_LBK_BF_HD 0x045d
|
||||
|
||||
#define U80211_DRV_RTL8188EU_REG_MAX_AGGR_NUM 0x04ca
|
||||
#define U80211_DRV_RTL8188EU_REG_MAX_AGGR_NUM_8188EU 0x07
|
||||
|
||||
#define U80211_DRV_RTL8188EU_REG_EDCA_VO 0x0500
|
||||
#define U80211_DRV_RTL8188EU_REG_EDCA_VI 0x0504
|
||||
#define U80211_DRV_RTL8188EU_REG_EDCA_BE 0x0508
|
||||
#define U80211_DRV_RTL8188EU_REG_EDCA_BK 0x050c
|
||||
|
||||
#define U80211_DRV_RTL8188EU_REG_BCNTCFG 0x0510
|
||||
#define U80211_DRV_RTL8188EU_REG_SIFS_CCK 0x0514
|
||||
#define U80211_DRV_RTL8188EU_REG_SIFS_OFDM 0x0516
|
||||
|
||||
#define U80211_DRV_RTL8188EU_REG_TBTT_PROHIBIT 0x0540
|
||||
#define U80211_DRV_RTL8188EU_REG_BCN_CTRL 0x0550
|
||||
#define U80211_DRV_RTL8188EU_REG_DRVERLYINT 0x0558
|
||||
#define U80211_DRV_RTL8188EU_REG_BCNDMATIM 0x0559
|
||||
|
||||
#define U80211_DRV_RTL8188EU_REG_BWOPMODE 0x0603
|
||||
#define U80211_DRV_RTL8188EU_REG_BWOPMODE_20MHZ (1u << 2)
|
||||
|
||||
#define U80211_DRV_RTL8188EU_REG_RCR 0x0608
|
||||
#define U80211_DRV_RTL8188EU_REG_RCR_AAP (1u << 0)
|
||||
#define U80211_DRV_RTL8188EU_REG_RCR_APM (1u << 1)
|
||||
#define U80211_DRV_RTL8188EU_REG_RCR_AM (1u << 2)
|
||||
#define U80211_DRV_RTL8188EU_REG_RCR_AB (1u << 3)
|
||||
#define U80211_DRV_RTL8188EU_REG_RCR_AMF (1u << 13)
|
||||
#define U80211_DRV_RTL8188EU_REG_RCR_HTC_LOC_CTRL (1u << 14)
|
||||
#define U80211_DRV_RTL8188EU_REG_RCR_APP_PHYSTS (1u << 28)
|
||||
#define U80211_DRV_RTL8188EU_REG_RCR_APP_ICV (1u << 29)
|
||||
#define U80211_DRV_RTL8188EU_REG_RCR_APP_MIC (1u << 30)
|
||||
|
||||
#define U80211_DRV_RTL8188EU_REG_RX_DRVINFO_SZ 0x060f
|
||||
#define U80211_DRV_RTL8188EU_RX_DRVINFO_SZ 4
|
||||
|
||||
#define U80211_DRV_RTL8188EU_REG_MACID 0x0610
|
||||
#define U80211_DRV_RTL8188EU_REG_MAR 0x0620
|
||||
|
||||
#define U80211_DRV_RTL8188EU_REG_MAC_SPEC_SIFS 0x063a
|
||||
#define U80211_DRV_RTL8188EU_REG_RESP_SIFS_CCK 0x063c
|
||||
#define U80211_DRV_RTL8188EU_REG_RESP_SIFS_OFDM 0x063e
|
||||
#define U80211_DRV_RTL8188EU_REG_ACKTO 0x0640
|
||||
|
||||
#define U80211_DRV_RTL8188EU_REG_CAMCMD 0x0670
|
||||
#define U80211_DRV_RTL8188EU_REG_CAMWRITE 0x0674
|
||||
#define U80211_DRV_RTL8188EU_REG_CAMCMD_WRITE (1u << 16)
|
||||
#define U80211_DRV_RTL8188EU_REG_CAMCMD_CLR (1u << 30)
|
||||
#define U80211_DRV_RTL8188EU_REG_CAMCMD_POLLING (1u << 31)
|
||||
|
||||
#define U80211_DRV_RTL8188EU_CAM_ENTRY_WORD_COUNT 8
|
||||
#define U80211_DRV_RTL8188EU_CAM_CTL0_KEY_ID_MASK 0x03
|
||||
#define U80211_DRV_RTL8188EU_CAM_CTL0_ALGORITHM_SHIFT 2
|
||||
#define U80211_DRV_RTL8188EU_CAM_CTL0_GROUP (1u << 6)
|
||||
#define U80211_DRV_RTL8188EU_CAM_CTL0_VALID (1u << 15)
|
||||
#define U80211_DRV_RTL8188EU_CAM_ALGORITHM_WEP40 1
|
||||
#define U80211_DRV_RTL8188EU_CAM_ALGORITHM_TKIP 2
|
||||
#define U80211_DRV_RTL8188EU_CAM_ALGORITHM_CCMP 4
|
||||
#define U80211_DRV_RTL8188EU_CAM_ALGORITHM_WEP104 5
|
||||
|
||||
#define U80211_DRV_RTL8188EU_REG_SECCFG 0x0680
|
||||
#define U80211_DRV_RTL8188EU_REG_SECCFG_TXUCKEY_DEF (1u << 0)
|
||||
#define U80211_DRV_RTL8188EU_REG_SECCFG_RXUCKEY_DEF (1u << 1)
|
||||
#define U80211_DRV_RTL8188EU_REG_SECCFG_TXENC_ENABLE (1u << 2)
|
||||
#define U80211_DRV_RTL8188EU_REG_SECCFG_RXENC_ENABLE (1u << 3)
|
||||
#define U80211_DRV_RTL8188EU_REG_SECCFG_TXBCKEY_DEF (1u << 6)
|
||||
#define U80211_DRV_RTL8188EU_REG_SECCFG_RXBCKEY_DEF (1u << 7)
|
||||
|
||||
#define U80211_DRV_RTL8188EU_REG_RXFLTMAP0 0x06a0
|
||||
#define U80211_DRV_RTL8188EU_REG_RXFLTMAP1 0x06a2
|
||||
#define U80211_DRV_RTL8188EU_REG_RXFLTMAP2 0x06a4
|
||||
|
||||
#define U80211_DRV_RTL8188EU_REG_FPGA0_RFMOD 0x0800
|
||||
#define U80211_DRV_RTL8188EU_REG_FPGA_RFMOD_40MHZ (1u << 0)
|
||||
#define U80211_DRV_RTL8188EU_REG_FPGA0_RFMOD_CCK_ENABLE (1u << 24)
|
||||
#define U80211_DRV_RTL8188EU_REG_FPGA0_RFMOD_OFDM_ENABLE (1u << 25)
|
||||
|
||||
#define U80211_DRV_RTL8188EU_REG_HSSI_PARAM1_A 0x0820
|
||||
#define U80211_DRV_RTL8188EU_REG_HSSI_PARAM1_PI (1u << 8)
|
||||
|
||||
#define U80211_DRV_RTL8188EU_REG_HSSI_PARAM2_A 0x0824
|
||||
#define U80211_DRV_RTL8188EU_REG_HSSI_PARAM2_CCK_HIGH_POWER (1u << 9)
|
||||
#define U80211_DRV_RTL8188EU_REG_HSSI_PARAM2_ADDRESS_LENGTH (1u << 10)
|
||||
#define U80211_DRV_RTL8188EU_REG_HSSI_PARAM2_DATA_LENGTH (1u << 11)
|
||||
#define U80211_DRV_RTL8188EU_REG_HSSI_PARAM2_READ_ADDRESS_MASK 0x7f800000
|
||||
#define U80211_DRV_RTL8188EU_REG_HSSI_PARAM2_READ_ADDRESS_SHIFT 23
|
||||
#define U80211_DRV_RTL8188EU_REG_HSSI_PARAM2_READ_EDGE (1u << 31)
|
||||
|
||||
#define U80211_DRV_RTL8188EU_REG_LSSI_PARAM_A 0x0840
|
||||
#define U80211_DRV_RTL8188EU_REG_LSSI_PARAM_ADDRESS_SHIFT 20
|
||||
#define U80211_DRV_RTL8188EU_REG_LSSI_PARAM_DATA_MASK 0x000fffff
|
||||
|
||||
#define U80211_DRV_RTL8188EU_REG_RF_IFACE_OE_A 0x0860
|
||||
#define U80211_DRV_RTL8188EU_REG_RF_IFACE_OE_RF_ENV (1u << 4)
|
||||
#define U80211_DRV_RTL8188EU_REG_RF_IFACE_OE_RF_ENV_ENABLE (1u << 20)
|
||||
|
||||
#define U80211_DRV_RTL8188EU_REG_TXAGC_B_CCK11_A_CCK2_11 0x086c
|
||||
|
||||
#define U80211_DRV_RTL8188EU_REG_RF_IFACE_SW_A 0x0870
|
||||
#define U80211_DRV_RTL8188EU_REG_RF_IFACE_SW_RF_ENV (1u << 4)
|
||||
|
||||
#define U80211_DRV_RTL8188EU_REG_LSSI_READBACK_A 0x08a0
|
||||
#define U80211_DRV_RTL8188EU_REG_HSPI_READBACK_A 0x08b8
|
||||
#define U80211_DRV_RTL8188EU_RF_READBACK_MASK 0x000fffff
|
||||
#define U80211_DRV_RTL8188EU_RF_CHNLBW 0x18
|
||||
#define U80211_DRV_RTL8188EU_RF_CHNLBW_CHANNEL_BW_MASK 0x00000fff
|
||||
#define U80211_DRV_RTL8188EU_RF_CHNLBW_BW20 0x00000c00
|
||||
|
||||
#define U80211_DRV_RTL8188EU_REG_FPGA1_RFMOD 0x0900
|
||||
|
||||
#define U80211_DRV_RTL8188EU_REG_OFDM0_AGCCORE1 0x0c50
|
||||
#define U80211_DRV_RTL8188EU_REG_OFDM0_AGCCORE1_LATCH 0x69553422
|
||||
#define U80211_DRV_RTL8188EU_REG_OFDM0_AGCCORE1_FINAL 0x69553420
|
||||
|
||||
#define U80211_DRV_RTL8188EU_REG_OFDM0_AGCRSSITABLE 0x0c78
|
||||
|
||||
#define U80211_DRV_RTL8188EU_REG_TXAGC_A_RATE18_06 0x0e00
|
||||
#define U80211_DRV_RTL8188EU_REG_TXAGC_A_RATE54_24 0x0e04
|
||||
#define U80211_DRV_RTL8188EU_REG_TXAGC_A_CCK1 0x0e08
|
||||
#define U80211_DRV_RTL8188EU_REG_TXAGC_A_MCS03 0x0e10
|
||||
#define U80211_DRV_RTL8188EU_REG_TXAGC_A_MCS07 0x0e14
|
||||
#define U80211_DRV_RTL8188EU_REG_TXAGC_A_CCK1_MASK 0x0000ff00
|
||||
|
||||
#define U80211_DRV_RTL8188EU_REG_USB_SPECIAL_OPTION 0xfe55
|
||||
#define U80211_DRV_RTL8188EU_REG_USB_SPECIAL_OPTION_AGG_ENABLE (1u << 3)
|
||||
|
||||
#define U80211_DRV_RTL8188EU_TX_TOTAL_PAGE_NUM 0xa9
|
||||
#define U80211_DRV_RTL8188EU_TX_PAGE_NUM_HI_PQ 0x29
|
||||
#define U80211_DRV_RTL8188EU_TX_PAGE_NUM_LO_PQ 0x1c
|
||||
#define U80211_DRV_RTL8188EU_TX_PAGE_NUM_NORM_PQ 0x1c
|
||||
|
||||
#define U80211_DRV_RTL8188EU_TRXDMA_QUEUE_LOW 1
|
||||
#define U80211_DRV_RTL8188EU_TRXDMA_QUEUE_NORMAL 2
|
||||
#define U80211_DRV_RTL8188EU_TRXDMA_QUEUE_HIGH 3
|
||||
|
||||
#define U80211_DRV_RTL8188EU_RX_SLOT_COUNT 4
|
||||
|
||||
typedef enum {
|
||||
U80211_DRV_RTL8188EU_ACCESS_CATEGORY_VO,
|
||||
U80211_DRV_RTL8188EU_ACCESS_CATEGORY_VI,
|
||||
U80211_DRV_RTL8188EU_ACCESS_CATEGORY_BE,
|
||||
U80211_DRV_RTL8188EU_ACCESS_CATEGORY_BK,
|
||||
} u80211_drv_rtl8188eu_access_category_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t aifsn;
|
||||
uint8_t ecwmin;
|
||||
uint8_t ecwmax;
|
||||
uint16_t txop;
|
||||
uint8_t slot_time;
|
||||
} u80211_drv_rtl8188eu_edca_params_t;
|
||||
|
||||
typedef struct {
|
||||
uint16_t rtl_id;
|
||||
uint8_t mac_address[U80211_DRV_RTL8188EU_MAC_ADDRESS_LEN];
|
||||
uint8_t cck_tx_power_base_indexes[U80211_DRV_RTL8188EU_CCK_TX_POWER_BASE_INDEX_COUNT];
|
||||
uint8_t ht40_1s_tx_power_base_indexes[U80211_DRV_RTL8188EU_HT40_1S_TX_POWER_BASE_INDEX_COUNT];
|
||||
uint8_t ht20_ofdm_tx_power_diff;
|
||||
uint8_t xtal_k;
|
||||
} u80211_drv_rtl8188eu_efuse_t;
|
||||
|
||||
typedef struct u80211_drv_rtl8188eu u80211_drv_rtl8188eu_t;
|
||||
|
||||
typedef struct {
|
||||
u80211_drv_rtl8188eu_t *rtl8188eu;
|
||||
void *buffer;
|
||||
u80211_drv_transfer_handle_t transfer;
|
||||
} u80211_drv_rtl8188eu_rx_slot_t;
|
||||
|
||||
struct u80211_drv_rtl8188eu {
|
||||
u80211_drv_device_handle_t device;
|
||||
u80211_drv_interface_handle_t interface;
|
||||
u80211_drv_network_device_handle_t network_device;
|
||||
uint8_t rx_endpoint;
|
||||
uint8_t bulk_out_endpoint_count;
|
||||
uint8_t tx_endpoint_high;
|
||||
uint8_t tx_endpoint_normal;
|
||||
uint8_t tx_endpoint_low;
|
||||
bool cck_high_power;
|
||||
uint32_t rf_chnlbw;
|
||||
u80211_drv_rtl8188eu_efuse_t efuse;
|
||||
u80211_drv_rtl8188eu_rx_slot_t rx_slots[U80211_DRV_RTL8188EU_RX_SLOT_COUNT];
|
||||
};
|
||||
|
||||
int u80211_drv_rtl8188eu_efuse_prepare(u80211_drv_device_handle_t device);
|
||||
int u80211_drv_rtl8188eu_efuse_finish(u80211_drv_device_handle_t device);
|
||||
int u80211_drv_rtl8188eu_read_efuse(u80211_drv_device_handle_t device, uint8_t efuse_map[U80211_DRV_RTL8188EU_EFUSE_MAP_LEN]);
|
||||
int u80211_drv_rtl8188eu_parse_efuse(const uint8_t efuse_map[U80211_DRV_RTL8188EU_EFUSE_MAP_LEN], u80211_drv_rtl8188eu_efuse_t *result);
|
||||
|
||||
int u80211_drv_rtl8188eu_power_active(u80211_drv_device_handle_t device);
|
||||
|
||||
int u80211_drv_rtl8188eu_mac_enable_infrastructure(u80211_drv_device_handle_t device);
|
||||
int u80211_drv_rtl8188eu_mac_enable_tx_rx(u80211_drv_device_handle_t device);
|
||||
int u80211_drv_rtl8188eu_mac_disable_rx_aggregation(u80211_drv_device_handle_t device);
|
||||
int u80211_drv_rtl8188eu_mac_configure_tx_queues(u80211_drv_device_handle_t device, uint8_t bulk_out_endpoint_count);
|
||||
int u80211_drv_rtl8188eu_mac_configure_rx_fifo_boundary(u80211_drv_device_handle_t device);
|
||||
int u80211_drv_rtl8188eu_mac_configure_packet_buffer(u80211_drv_device_handle_t device);
|
||||
int u80211_drv_rtl8188eu_mac_initialize_llt(u80211_drv_device_handle_t device);
|
||||
int u80211_drv_rtl8188eu_mac_configure_wmac(u80211_drv_rtl8188eu_t *rtl8188eu);
|
||||
int u80211_drv_rtl8188eu_mac_set_edca(u80211_drv_device_handle_t device, u80211_drv_rtl8188eu_access_category_t access_category, u80211_drv_rtl8188eu_edca_params_t params);
|
||||
int u80211_drv_rtl8188eu_mac_configure_timing(u80211_drv_device_handle_t device);
|
||||
int u80211_drv_rtl8188eu_mac_configure_hardware_offloads(u80211_drv_device_handle_t device);
|
||||
int u80211_drv_rtl8188eu_mac_load_table(u80211_drv_device_handle_t device);
|
||||
|
||||
int u80211_drv_rtl8188eu_bb_enable(u80211_drv_device_handle_t device);
|
||||
int u80211_drv_rtl8188eu_bb_enable_datapaths(u80211_drv_device_handle_t device);
|
||||
int u80211_drv_rtl8188eu_bb_load_table(u80211_drv_device_handle_t device);
|
||||
int u80211_drv_rtl8188eu_bb_load_agc_table(u80211_drv_device_handle_t device);
|
||||
int u80211_drv_rtl8188eu_bb_apply_efuse_calibration(u80211_drv_rtl8188eu_t *rtl8188eu);
|
||||
|
||||
int u80211_drv_rtl8188eu_rf_write(u80211_drv_device_handle_t device, uint8_t rf_reg, uint32_t value);
|
||||
int u80211_drv_rtl8188eu_rf_read(u80211_drv_device_handle_t device, uint8_t rf_reg, uint32_t *value);
|
||||
int u80211_drv_rtl8188eu_rf_load_table(u80211_drv_device_handle_t device);
|
||||
|
||||
int u80211_drv_rtl8188eu_set_tx_power(u80211_drv_rtl8188eu_t *rtl8188eu, uint8_t channel);
|
||||
int u80211_drv_rtl8188eu_set_channel(u80211_drv_rtl8188eu_t *rtl8188eu, uint8_t channel);
|
||||
int u80211_drv_rtl8188eu_set_key(u80211_drv_rtl8188eu_t *rtl8188eu, const u80211_drv_key_t *key);
|
||||
int u80211_drv_rtl8188eu_del_key(u80211_drv_rtl8188eu_t *rtl8188eu, uint8_t index);
|
||||
int u80211_drv_rtl8188eu_tx_buffer_allocate(size_t size, void **buffer);
|
||||
void u80211_drv_rtl8188eu_tx_buffer_free(void *buffer);
|
||||
int u80211_drv_rtl8188eu_transmit(u80211_drv_rtl8188eu_t *rtl8188eu, void *buffer, size_t size, size_t current_offset, const u80211_drv_transmit_options_t *options);
|
||||
int u80211_drv_rtl8188eu_rx_start(u80211_drv_rtl8188eu_t *rtl8188eu);
|
||||
|
||||
int u80211_drv_rtl8188eu_calibrate(u80211_drv_device_handle_t device);
|
||||
|
||||
int u80211_drv_rtl8188eu_firmware_prepare(u80211_drv_device_handle_t device, const void *firmware_data, size_t firmware_size, const uint8_t **firmware_payload, size_t *firmware_payload_size);
|
||||
int u80211_drv_rtl8188eu_firmware_upload(u80211_drv_device_handle_t device, const uint8_t *firmware_payload, size_t firmware_payload_size);
|
||||
int u80211_drv_rtl8188eu_firmware_start(u80211_drv_device_handle_t device);
|
||||
|
||||
int u80211_drv_rtl8188eu_reg_read8(u80211_drv_device_handle_t device, uint16_t reg, uint8_t *value);
|
||||
int u80211_drv_rtl8188eu_reg_read16(u80211_drv_device_handle_t device, uint16_t reg, uint16_t *value);
|
||||
int u80211_drv_rtl8188eu_reg_read32(u80211_drv_device_handle_t device, uint16_t reg, uint32_t *value);
|
||||
int u80211_drv_rtl8188eu_reg_write8(u80211_drv_device_handle_t device, uint16_t reg, uint8_t value);
|
||||
int u80211_drv_rtl8188eu_reg_write16(u80211_drv_device_handle_t device, uint16_t reg, uint16_t value);
|
||||
int u80211_drv_rtl8188eu_reg_write32(u80211_drv_device_handle_t device, uint16_t reg, uint32_t value);
|
||||
int u80211_drv_rtl8188eu_reg_write_region(u80211_drv_device_handle_t device, uint16_t reg, const void *data, uint16_t size);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,14 @@
|
||||
#ifndef U80211_DRV_STATUS_H
|
||||
#define U80211_DRV_STATUS_H
|
||||
|
||||
#define U80211_DRV_STATUS_SUCCESS 0
|
||||
#define U80211_DRV_STATUS_NO_MATCH 1
|
||||
#define U80211_DRV_STATUS_UNKNOWN_ERROR 2
|
||||
#define U80211_DRV_STATUS_TIMEOUT 3
|
||||
#define U80211_DRV_STATUS_NOT_SUPPORTED 4
|
||||
#define U80211_DRV_STATUS_FAULTY_HARDWARE 5
|
||||
#define U80211_DRV_STATUS_OUT_OF_MEMORY 6
|
||||
#define U80211_DRV_STATUS_INVALID_ARGUMENT 7
|
||||
#define U80211_DRV_STATUS_MALFORMED_PACKET 8
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,9 @@
|
||||
#ifndef U80211_DRV_STRING_H
|
||||
#define U80211_DRV_STRING_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
void *u80211_drv_memcpy(void *destination, const void *source, size_t size);
|
||||
void *u80211_drv_memset(void *destination, int value, size_t size);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,25 @@
|
||||
#ifndef U80211_DRV_U80211_DRV_H
|
||||
#define U80211_DRV_U80211_DRV_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <u80211_drv/kernel_interface.h>
|
||||
#include <u80211_drv/status.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// probes a interface to check if there is a matching driver for a specific usb device
|
||||
// return U80211_DRV_STATUS_SUCCESS when there is a match
|
||||
int u80211_drv_probe(u80211_drv_device_handle_t device, u80211_drv_interface_handle_t interface);
|
||||
|
||||
// attaches an interface to a driver
|
||||
// returns U80211_DRV_STATUS_SUCCESS when driver successfully attaches the interface
|
||||
int u80211_drv_attach(u80211_drv_device_handle_t device, u80211_drv_interface_handle_t interface);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,54 @@
|
||||
#ifndef U80211_DRV_UTIL_H
|
||||
#define U80211_DRV_UTIL_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <u80211_drv/string.h>
|
||||
|
||||
static inline uint16_t u80211_drv_host_to_le16(uint16_t value) {
|
||||
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
return value;
|
||||
#else
|
||||
return __builtin_bswap16(value);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline uint32_t u80211_drv_host_to_le32(uint32_t value) {
|
||||
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
return value;
|
||||
#else
|
||||
return __builtin_bswap32(value);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline uint16_t u80211_drv_le_to_host16(uint16_t value) {
|
||||
return u80211_drv_host_to_le16(value);
|
||||
}
|
||||
|
||||
static inline uint32_t u80211_drv_le_to_host32(uint32_t value) {
|
||||
return u80211_drv_host_to_le32(value);
|
||||
}
|
||||
|
||||
static inline uint16_t u80211_drv_deserialize_le16(const void *source) {
|
||||
uint16_t value;
|
||||
u80211_drv_memcpy(&value, source, sizeof(value));
|
||||
return u80211_drv_le_to_host16(value);
|
||||
}
|
||||
|
||||
static inline uint32_t u80211_drv_deserialize_le32(const void *source) {
|
||||
uint32_t value;
|
||||
u80211_drv_memcpy(&value, source, sizeof(value));
|
||||
return u80211_drv_le_to_host32(value);
|
||||
}
|
||||
|
||||
static inline void u80211_drv_serialize_le16(uint8_t *destination, uint16_t value) {
|
||||
uint16_t little_endian_value = u80211_drv_host_to_le16(value);
|
||||
u80211_drv_memcpy(destination, &little_endian_value, sizeof(little_endian_value));
|
||||
}
|
||||
|
||||
static inline void u80211_drv_serialize_le32(uint8_t *destination, uint32_t value) {
|
||||
uint32_t little_endian_value = u80211_drv_host_to_le32(value);
|
||||
u80211_drv_memcpy(destination, &little_endian_value, sizeof(little_endian_value));
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,262 @@
|
||||
#include <u80211_drv/kernel_interface.h>
|
||||
#include <u80211_drv/status.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <arch/mmu.h>
|
||||
#include <kernel/alloc.h>
|
||||
#include <kernel/firmware.h>
|
||||
#include <kernel/page.h>
|
||||
#include <kernel/timekeeper.h>
|
||||
#include <kernel/usb.h>
|
||||
#include <logging.h>
|
||||
#include <semaphore.h>
|
||||
#include <string.h>
|
||||
|
||||
void *u80211_drv_kernel_allocate(size_t size) {
|
||||
return alloc(size);
|
||||
}
|
||||
|
||||
void u80211_drv_kernel_free(void *memory) {
|
||||
free(memory);
|
||||
}
|
||||
|
||||
int u80211_drv_kernel_get_firmware(const char *name, u80211_drv_kernel_firmware_callback_t callback, void *context) {
|
||||
int error = firmware_load(name, (firmware_callback_t)callback, context);
|
||||
if (error == 0)
|
||||
return U80211_DRV_STATUS_SUCCESS;
|
||||
|
||||
if (error == ENOMEM)
|
||||
return U80211_DRV_STATUS_OUT_OF_MEMORY;
|
||||
|
||||
return U80211_DRV_STATUS_UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
void u80211_drv_kernel_stall_us(unsigned int microseconds) {
|
||||
timekeeper_wait_us(microseconds);
|
||||
}
|
||||
|
||||
void u80211_drv_kernel_print(int level, const char *msg) {
|
||||
switch (level) {
|
||||
case U80211_DRV_KERNEL_PRINT_LEVEL_INFO:
|
||||
printf("u80211_drv: [INFO] %s\n", msg);
|
||||
break;
|
||||
case U80211_DRV_KERNEL_PRINT_LEVEL_WARN:
|
||||
printf("u80211_drv: [WARN] %s\n", msg);
|
||||
break;
|
||||
case U80211_DRV_KERNEL_PRINT_LEVEL_ERROR:
|
||||
printf("u80211_drv: [ERROR] %s\n", msg);
|
||||
break;
|
||||
default:
|
||||
printf("u80211_drv: [UNKNOWN] %s\n", msg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int u80211_drv_kernel_get_device_descriptor(u80211_drv_device_handle_t opaque_device, u80211_drv_device_descriptor_t *descriptor) {
|
||||
usb_device_t *device = opaque_device;
|
||||
descriptor->vendor_id = device->desc.idVendor;
|
||||
descriptor->product_id = device->desc.idProduct;
|
||||
return U80211_DRV_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
int u80211_drv_kernel_get_interface_descriptor(u80211_drv_interface_handle_t opaque_interface, u80211_drv_interface_descriptor_t *descriptor) {
|
||||
usb_interface_t *interface = opaque_interface;
|
||||
descriptor->number = interface->desc->bInterfaceNumber;
|
||||
descriptor->class_code = interface->desc->bInterfaceClass;
|
||||
descriptor->subclass = interface->desc->bInterfaceSubClass;
|
||||
descriptor->protocol = interface->desc->bInterfaceProtocol;
|
||||
descriptor->endpoint_count = interface->desc->bNumEndpoints;
|
||||
return U80211_DRV_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
int u80211_drv_kernel_get_endpoints(u80211_drv_interface_handle_t opaque_interface, u80211_drv_endpoint_descriptor_t *endpoints, size_t endpoint_count) {
|
||||
usb_interface_t *interface = opaque_interface;
|
||||
|
||||
for (size_t i = 0; i < endpoint_count; ++i) {
|
||||
usb_endpoint_desc_t *endpoint = interface->endpoints[i].desc;
|
||||
endpoints[i].address = endpoint->bEndpointAddress;
|
||||
endpoints[i].attributes = endpoint->bmAttributes;
|
||||
endpoints[i].maximum_packet_size = endpoint->wMaxPacketSize;
|
||||
endpoints[i].interval = endpoint->bInterval;
|
||||
}
|
||||
|
||||
return U80211_DRV_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
usb_device_t *device;
|
||||
usb_xfer_t xfer;
|
||||
iovec_t iovec;
|
||||
iovec_iterator_t iterator;
|
||||
u80211_drv_kernel_transfer_callback_t callback;
|
||||
void *context;
|
||||
bool active;
|
||||
} u80211_drv_transfer_t;
|
||||
|
||||
static int u80211_drv_transfer_status(usb_status_t status) {
|
||||
if (status == USB_STATUS_SUCCESS || status == USB_STATUS_SHORT_PACKET)
|
||||
return U80211_DRV_STATUS_SUCCESS;
|
||||
|
||||
return U80211_DRV_STATUS_UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
static int u80211_drv_transfer_error(int error) {
|
||||
if (error == ENOMEM)
|
||||
return U80211_DRV_STATUS_OUT_OF_MEMORY;
|
||||
|
||||
if (error == EINVAL)
|
||||
return U80211_DRV_STATUS_INVALID_ARGUMENT;
|
||||
|
||||
return U80211_DRV_STATUS_UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
static void u80211_drv_transfer_complete(usb_device_t *, void *context, usb_status_t status, size_t transferred_size) {
|
||||
u80211_drv_transfer_t *transfer = context;
|
||||
int driver_status = u80211_drv_transfer_status(status);
|
||||
if (driver_status != U80211_DRV_STATUS_SUCCESS)
|
||||
transferred_size = 0;
|
||||
|
||||
__atomic_store_n(&transfer->active, false, __ATOMIC_RELEASE);
|
||||
transfer->callback(transfer->context, driver_status, transferred_size);
|
||||
}
|
||||
|
||||
int u80211_drv_kernel_allocate_bulk_xfer(u80211_drv_device_handle_t opaque_device, uint8_t endpoint_address, void *buffer, size_t buffer_size, u80211_drv_kernel_transfer_callback_t callback, void *context, u80211_drv_transfer_handle_t *transfer_out) {
|
||||
usb_device_t *device = opaque_device;
|
||||
usb_endpoint_t *endpoint = usb_find_endpoint(device, endpoint_address);
|
||||
if (endpoint == NULL || (endpoint->desc->bmAttributes & USB_ENDPOINT_ATTRIB_TYPE_MASK) != USB_ENDPOINT_ATTRIB_TYPE_BULK)
|
||||
return U80211_DRV_STATUS_INVALID_ARGUMENT;
|
||||
|
||||
u80211_drv_transfer_t *transfer = alloc(sizeof(u80211_drv_transfer_t));
|
||||
if (transfer == NULL)
|
||||
return U80211_DRV_STATUS_OUT_OF_MEMORY;
|
||||
|
||||
transfer->device = device;
|
||||
transfer->iovec.addr = buffer;
|
||||
transfer->iovec.len = buffer_size;
|
||||
transfer->callback = callback;
|
||||
transfer->context = context;
|
||||
transfer->xfer.ep = endpoint;
|
||||
transfer->xfer.flags = (endpoint_address & USB_ENDPOINT_ADDRESS_DIR_IN ? USB_XFER_FLAG_TO_HOST : USB_XFER_FLAG_TO_DEVICE) | USB_XFER_FLAG_IOVEC;
|
||||
transfer->xfer.type = USB_XFER_TYPE_BULK;
|
||||
transfer->xfer.iov = &transfer->iterator;
|
||||
transfer->xfer.completion = u80211_drv_transfer_complete;
|
||||
transfer->xfer.completion_ctx = transfer;
|
||||
*transfer_out = transfer;
|
||||
return U80211_DRV_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
int u80211_drv_kernel_submit_xfer(u80211_drv_transfer_handle_t opaque_transfer) {
|
||||
u80211_drv_transfer_t *transfer = opaque_transfer;
|
||||
|
||||
bool expected = false;
|
||||
if (!__atomic_compare_exchange_n(&transfer->active, &expected, true, false, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED))
|
||||
return U80211_DRV_STATUS_INVALID_ARGUMENT;
|
||||
|
||||
iovec_iterator_init(&transfer->iterator, &transfer->iovec, 1);
|
||||
int error = usb_submit_xfer(transfer->device, &transfer->xfer);
|
||||
if (error == 0)
|
||||
return U80211_DRV_STATUS_SUCCESS;
|
||||
|
||||
__atomic_store_n(&transfer->active, false, __ATOMIC_RELEASE);
|
||||
return u80211_drv_transfer_error(error);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
semaphore_t semaphore;
|
||||
usb_status_t status;
|
||||
size_t transferred_size;
|
||||
} u80211_drv_sync_transfer_t;
|
||||
|
||||
static void u80211_drv_sync_transfer_complete(usb_device_t *, void *context, usb_status_t status, size_t transferred_size) {
|
||||
u80211_drv_sync_transfer_t *transfer = context;
|
||||
transfer->status = status;
|
||||
transfer->transferred_size = transferred_size;
|
||||
semaphore_signal(&transfer->semaphore);
|
||||
}
|
||||
|
||||
static int u80211_drv_wait_for_transfer(u80211_drv_sync_transfer_t *transfer, int error, size_t *transferred_size) {
|
||||
if (error != 0)
|
||||
return u80211_drv_transfer_error(error);
|
||||
|
||||
semaphore_wait(&transfer->semaphore, false);
|
||||
int status = u80211_drv_transfer_status(transfer->status);
|
||||
*transferred_size = status == U80211_DRV_STATUS_SUCCESS ? transfer->transferred_size : 0;
|
||||
return status;
|
||||
}
|
||||
|
||||
int u80211_drv_kernel_submit_control_xfer_and_wait(u80211_drv_device_handle_t opaque_device, uint8_t flags, uint8_t request, uint16_t value, uint16_t index, void *buf, uint16_t buffer_size, size_t *transferred_size, unsigned int timeout) {
|
||||
(void)timeout;
|
||||
// TODO: implement timeouts once transfer cancellation is available
|
||||
// TODO: once scatter-gather control transfers are implemented, drop the bounce page stuff
|
||||
void *bounce_page = NULL;
|
||||
void *transfer_buffer = NULL;
|
||||
if (buffer_size != 0) {
|
||||
bounce_page = mm_alloc_page(MEMORY_SECTION_DEFAULT);
|
||||
if (bounce_page == NULL)
|
||||
return U80211_DRV_STATUS_OUT_OF_MEMORY;
|
||||
|
||||
transfer_buffer = MAKE_HHDM(bounce_page);
|
||||
if ((flags & U80211_DRV_KERNEL_XFER_DIRECTION_MASK) == U80211_DRV_KERNEL_XFER_OUT)
|
||||
memcpy(transfer_buffer, buf, buffer_size);
|
||||
}
|
||||
|
||||
usb_setup_t setup = {
|
||||
.bmRequestType = flags,
|
||||
.bRequest = request,
|
||||
.wValue = value,
|
||||
.wIndex = index,
|
||||
.wLength = buffer_size,
|
||||
};
|
||||
u80211_drv_sync_transfer_t transfer;
|
||||
SEMAPHORE_INIT(&transfer.semaphore, 0);
|
||||
|
||||
int status = u80211_drv_wait_for_transfer(
|
||||
&transfer,
|
||||
usb_control_xfer(opaque_device, &setup, transfer_buffer, u80211_drv_sync_transfer_complete, &transfer),
|
||||
transferred_size
|
||||
);
|
||||
|
||||
if (status == U80211_DRV_STATUS_SUCCESS && *transferred_size != 0 && (flags & U80211_DRV_KERNEL_XFER_DIRECTION_MASK) == U80211_DRV_KERNEL_XFER_IN)
|
||||
memcpy(buf, transfer_buffer, *transferred_size);
|
||||
|
||||
if (bounce_page != NULL)
|
||||
mm_release_page(bounce_page);
|
||||
return status;
|
||||
}
|
||||
|
||||
int u80211_drv_kernel_submit_bulk_xfer_and_wait(u80211_drv_device_handle_t opaque_device, uint8_t endpoint_address, void *buf, size_t buffer_size, size_t *transferred_size, unsigned int timeout) {
|
||||
(void)timeout;
|
||||
// TODO: Implement timeouts once USB transfer cancellation is available.
|
||||
usb_device_t *device = opaque_device;
|
||||
usb_endpoint_t *endpoint = usb_find_endpoint(device, endpoint_address);
|
||||
if (endpoint == NULL || (endpoint->desc->bmAttributes & USB_ENDPOINT_ATTRIB_TYPE_MASK) != USB_ENDPOINT_ATTRIB_TYPE_BULK)
|
||||
return U80211_DRV_STATUS_INVALID_ARGUMENT;
|
||||
|
||||
iovec_t iovec = {
|
||||
.addr = buf,
|
||||
.len = buffer_size,
|
||||
};
|
||||
iovec_iterator_t iterator;
|
||||
iovec_iterator_init(&iterator, &iovec, 1);
|
||||
u80211_drv_sync_transfer_t transfer;
|
||||
SEMAPHORE_INIT(&transfer.semaphore, 0);
|
||||
usb_xfer_t xfer = {
|
||||
.ep = endpoint,
|
||||
.flags = (endpoint_address & USB_ENDPOINT_ADDRESS_DIR_IN ? USB_XFER_FLAG_TO_HOST : USB_XFER_FLAG_TO_DEVICE) | USB_XFER_FLAG_IOVEC,
|
||||
.type = USB_XFER_TYPE_BULK,
|
||||
.iov = &iterator,
|
||||
.completion = u80211_drv_sync_transfer_complete,
|
||||
.completion_ctx = &transfer,
|
||||
};
|
||||
|
||||
return u80211_drv_wait_for_transfer(&transfer, usb_submit_xfer(device, &xfer), transferred_size);
|
||||
}
|
||||
|
||||
int u80211_drv_device_ready(void *device, const u80211_drv_device_metadata_t *metadata, const u80211_drv_device_ops_t *ops, u80211_drv_network_device_handle_t *network_device) {
|
||||
printf("device ready\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
void u80211_drv_packet_received(u80211_drv_network_device_handle_t device, void *packet, size_t packet_size) {
|
||||
printf("got packet!\n");
|
||||
}
|
||||
@@ -0,0 +1,252 @@
|
||||
/* $OpenBSD: r92creg.h,v 1.31 2024/09/01 03:14:48 jsg Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2010 Damien Bergamini <damien.bergamini@free.fr>
|
||||
* Copyright (c) 2015 Stefan Sperling <stsp@openbsd.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <u80211_drv/rtl8188eu.h>
|
||||
#include <u80211_drv/status.h>
|
||||
|
||||
#define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0]))
|
||||
#define RTL8188EU_AGC_TABLE_ENTRY_COUNT 128
|
||||
|
||||
// Imported from OpenBSD sys/dev/ic/r92creg.h.
|
||||
static const uint16_t rtl8188eu_bb_regs[] = {
|
||||
0x800, 0x804, 0x808, 0x80c, 0x810, 0x814, 0x818, 0x81c, 0x820,
|
||||
0x824, 0x828, 0x82c, 0x830, 0x834, 0x838, 0x83c, 0x840, 0x844,
|
||||
0x848, 0x84c, 0x850, 0x854, 0x858, 0x85c, 0x860, 0x864, 0x868,
|
||||
0x86c, 0x870, 0x874, 0x878, 0x87c, 0x880, 0x884, 0x888, 0x88c,
|
||||
0x890, 0x894, 0x898, 0x89c, 0x900, 0x904, 0x908, 0x90c, 0x910,
|
||||
0x914, 0xa00, 0xa04, 0xa08, 0xa0c, 0xa10, 0xa14, 0xa18, 0xa1c,
|
||||
0xa20, 0xa24, 0xa28, 0xa2c, 0xa70, 0xa74, 0xa78, 0xa7c, 0xa80,
|
||||
0xb2c, 0xc00, 0xc04, 0xc08, 0xc0c, 0xc10, 0xc14, 0xc18, 0xc1c,
|
||||
0xc20, 0xc24, 0xc28, 0xc2c, 0xc30, 0xc34, 0xc38, 0xc3c, 0xc40,
|
||||
0xc44, 0xc48, 0xc4c, 0xc50, 0xc54, 0xc58, 0xc5c, 0xc60, 0xc64,
|
||||
0xc68, 0xc6c, 0xc70, 0xc74, 0xc78, 0xc7c, 0xc80, 0xc84, 0xc88,
|
||||
0xc8c, 0xc90, 0xc94, 0xc98, 0xc9c, 0xca0, 0xca4, 0xca8, 0xcac,
|
||||
0xcb0, 0xcb4, 0xcb8, 0xcbc, 0xcc0, 0xcc4, 0xcc8, 0xccc, 0xcd0,
|
||||
0xcd4, 0xcd8, 0xcdc, 0xce0, 0xce4, 0xce8, 0xcec, 0xd00, 0xd04,
|
||||
0xd08, 0xd0c, 0xd10, 0xd14, 0xd18, 0xd2c, 0xd30, 0xd34, 0xd38,
|
||||
0xd3c, 0xd40, 0xd44, 0xd48, 0xd4c, 0xd50, 0xd54, 0xd58, 0xd5c,
|
||||
0xd60, 0xd64, 0xd68, 0xd6c, 0xd70, 0xd74, 0xd78, 0xe00, 0xe04,
|
||||
0xe08, 0xe10, 0xe14, 0xe18, 0xe1c, 0xe28, 0xe30, 0xe34, 0xe38,
|
||||
0xe3c, 0xe40, 0xe44, 0xe48, 0xe4c, 0xe50, 0xe54, 0xe58, 0xe5c,
|
||||
0xe60, 0xe68, 0xe6c, 0xe70, 0xe74, 0xe78, 0xe7c, 0xe80, 0xe84,
|
||||
0xe88, 0xe8c, 0xed0, 0xed4, 0xed8, 0xedc, 0xee0, 0xee8, 0xeec,
|
||||
0xf14, 0xf4c, 0xf00
|
||||
};
|
||||
|
||||
static const uint32_t rtl8188eu_bb_values[] = {
|
||||
0x80040000, 0x00000003, 0x0000fc00, 0x0000000a, 0x10001331,
|
||||
0x020c3d10, 0x02200385, 0x00000000, 0x01000100, 0x00390204,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00010000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x569a11a9, 0x01000014, 0x66f60110,
|
||||
0x061f0649, 0x00000000, 0x27272700, 0x07000760, 0x25004000,
|
||||
0x00000808, 0x00000000, 0xb0000c1c, 0x00000001, 0x00000000,
|
||||
0xccc000c0, 0x00000800, 0xfffffffe, 0x40302010, 0x00706050,
|
||||
0x00000000, 0x00000023, 0x00000000, 0x81121111, 0x00000002,
|
||||
0x00000201, 0x00d047c8, 0x80ff000c, 0x8c838300, 0x2e7f120f,
|
||||
0x9500bb78, 0x1114d028, 0x00881117, 0x89140f00, 0x1a1b0000,
|
||||
0x090e1317, 0x00000204, 0x00d30000, 0x101fbf00, 0x00000007,
|
||||
0x00000900, 0x225b0606, 0x218075b1, 0x80000000, 0x48071d40,
|
||||
0x03a05611, 0x000000e4, 0x6c6c6c6c, 0x08800000, 0x40000100,
|
||||
0x08800000, 0x40000100, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x69e9ac47, 0x469652af, 0x49795994, 0x0a97971c,
|
||||
0x1f7c403f, 0x000100b7, 0xec020107, 0x007f037f, 0x69553420,
|
||||
0x43bc0094, 0x00013169, 0x00250492, 0x00000000, 0x7112848b,
|
||||
0x47c00bff, 0x00000036, 0x2c7f000d, 0x020610db, 0x0000001f,
|
||||
0x00b91612, 0x390000e4, 0x20f60000, 0x40000100, 0x20200000,
|
||||
0x00091521, 0x00000000, 0x00121820, 0x00007f7f, 0x00000000,
|
||||
0x000300a0, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x28000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x64b22427, 0x00766932,
|
||||
0x00222222, 0x00000000, 0x37644302, 0x2f97d40c, 0x00000740,
|
||||
0x00020401, 0x0000907f, 0x20010201, 0xa0633333, 0x3333bc43,
|
||||
0x7a8f5b6f, 0xcc979975, 0x00000000, 0x80608000, 0x00000000,
|
||||
0x00127353, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x6437140a, 0x00000000, 0x00000282, 0x30032064, 0x4653de68,
|
||||
0x04518a3c, 0x00002101, 0x2a201c16, 0x1812362e, 0x322c2220,
|
||||
0x000e3c24, 0x2d2d2d2d, 0x2d2d2d2d, 0x0390272d, 0x2d2d2d2d,
|
||||
0x2d2d2d2d, 0x2d2d2d2d, 0x2d2d2d2d, 0x00000000, 0x1000dc1f,
|
||||
0x10008c1f, 0x02140102, 0x681604c2, 0x01007c00, 0x01004800,
|
||||
0xfb000000, 0x000028d1, 0x1000dc1f, 0x10008c1f, 0x02140102,
|
||||
0x28160d05, 0x00000008, 0x001b25a4, 0x00c00014, 0x00c00014,
|
||||
0x01000014, 0x01000014, 0x01000014, 0x01000014, 0x00c00014,
|
||||
0x01000014, 0x00c00014, 0x00c00014, 0x00c00014, 0x00c00014,
|
||||
0x00000014, 0x00000014, 0x21555448, 0x01c00014, 0x00000003,
|
||||
0x00000000, 0x00000300
|
||||
};
|
||||
|
||||
static const uint32_t rtl8188eu_agc_values[] = {
|
||||
0xfb000001, 0xfb010001, 0xfb020001, 0xfb030001, 0xfb040001,
|
||||
0xfb050001, 0xfa060001, 0xf9070001, 0xf8080001, 0xf7090001,
|
||||
0xf60a0001, 0xf50b0001, 0xf40c0001, 0xf30d0001, 0xf20e0001,
|
||||
0xf10f0001, 0xf0100001, 0xef110001, 0xee120001, 0xed130001,
|
||||
0xec140001, 0xeb150001, 0xea160001, 0xe9170001, 0xe8180001,
|
||||
0xe7190001, 0xe61a0001, 0xe51b0001, 0xe41c0001, 0xe31d0001,
|
||||
0xe21e0001, 0xe11f0001, 0x8a200001, 0x89210001, 0x88220001,
|
||||
0x87230001, 0x86240001, 0x85250001, 0x84260001, 0x83270001,
|
||||
0x82280001, 0x6b290001, 0x6a2a0001, 0x692b0001, 0x682c0001,
|
||||
0x672d0001, 0x662e0001, 0x652f0001, 0x64300001, 0x63310001,
|
||||
0x62320001, 0x61330001, 0x46340001, 0x45350001, 0x44360001,
|
||||
0x43370001, 0x42380001, 0x41390001, 0x403a0001, 0x403b0001,
|
||||
0x403c0001, 0x403d0001, 0x403e0001, 0x403f0001, 0xfb400001,
|
||||
0xfb410001, 0xfb420001, 0xfb430001, 0xfb440001, 0xfb450001,
|
||||
0xfb460001, 0xfb470001, 0xfb480001, 0xfa490001, 0xf94a0001,
|
||||
0xf84b0001, 0xf74c0001, 0xf64d0001, 0xf54e0001, 0xf44f0001,
|
||||
0xf3500001, 0xf2510001, 0xf1520001, 0xf0530001, 0xef540001,
|
||||
0xee550001, 0xed560001, 0xec570001, 0xeb580001, 0xea590001,
|
||||
0xe95a0001, 0xe85b0001, 0xe75c0001, 0xe65d0001, 0xe55e0001,
|
||||
0xe45f0001, 0xe3600001, 0xe2610001, 0xc3620001, 0xc2630001,
|
||||
0xc1640001, 0x8b650001, 0x8a660001, 0x89670001, 0x88680001,
|
||||
0x87690001, 0x866a0001, 0x856b0001, 0x846c0001, 0x676d0001,
|
||||
0x666e0001, 0x656f0001, 0x64700001, 0x63710001, 0x62720001,
|
||||
0x61730001, 0x60740001, 0x46750001, 0x45760001, 0x44770001,
|
||||
0x43780001, 0x42790001, 0x417a0001, 0x407b0001, 0x407c0001,
|
||||
0x407d0001, 0x407e0001, 0x407f0001
|
||||
};
|
||||
|
||||
int u80211_drv_rtl8188eu_bb_enable(u80211_drv_device_handle_t device) {
|
||||
uint16_t value;
|
||||
int status = u80211_drv_rtl8188eu_reg_read16(device, U80211_DRV_RTL8188EU_REG_SYS_FUNC, &value);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
value |= U80211_DRV_RTL8188EU_REG_SYS_FUNC_BBRSTB |
|
||||
U80211_DRV_RTL8188EU_REG_SYS_FUNC_BB_GLB_RSTN |
|
||||
U80211_DRV_RTL8188EU_REG_SYS_FUNC_DIO_RF;
|
||||
status = u80211_drv_rtl8188eu_reg_write16(device, U80211_DRV_RTL8188EU_REG_SYS_FUNC, value);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
status = u80211_drv_rtl8188eu_reg_write8(
|
||||
device,
|
||||
U80211_DRV_RTL8188EU_REG_RF_CTRL,
|
||||
U80211_DRV_RTL8188EU_REG_RF_CTRL_ENABLE |
|
||||
U80211_DRV_RTL8188EU_REG_RF_CTRL_RSTB |
|
||||
U80211_DRV_RTL8188EU_REG_RF_CTRL_SDMRSTB
|
||||
);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
return u80211_drv_rtl8188eu_reg_write8(
|
||||
device,
|
||||
U80211_DRV_RTL8188EU_REG_SYS_FUNC,
|
||||
U80211_DRV_RTL8188EU_REG_SYS_FUNC_USBA |
|
||||
U80211_DRV_RTL8188EU_REG_SYS_FUNC_USBD |
|
||||
U80211_DRV_RTL8188EU_REG_SYS_FUNC_BB_GLB_RSTN |
|
||||
U80211_DRV_RTL8188EU_REG_SYS_FUNC_BBRSTB
|
||||
);
|
||||
}
|
||||
|
||||
int u80211_drv_rtl8188eu_bb_enable_datapaths(u80211_drv_device_handle_t device) {
|
||||
// enable cck datapath
|
||||
uint32_t rfmod;
|
||||
int status = u80211_drv_rtl8188eu_reg_read32(device, U80211_DRV_RTL8188EU_REG_FPGA0_RFMOD, &rfmod);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
rfmod |= U80211_DRV_RTL8188EU_REG_FPGA0_RFMOD_CCK_ENABLE;
|
||||
status = u80211_drv_rtl8188eu_reg_write32(device, U80211_DRV_RTL8188EU_REG_FPGA0_RFMOD, rfmod);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
// enable ofdm data path
|
||||
status = u80211_drv_rtl8188eu_reg_read32(device, U80211_DRV_RTL8188EU_REG_FPGA0_RFMOD, &rfmod);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
rfmod |= U80211_DRV_RTL8188EU_REG_FPGA0_RFMOD_OFDM_ENABLE;
|
||||
status = u80211_drv_rtl8188eu_reg_write32(device, U80211_DRV_RTL8188EU_REG_FPGA0_RFMOD, rfmod);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
// sanity check
|
||||
status = u80211_drv_rtl8188eu_reg_read32(device, U80211_DRV_RTL8188EU_REG_FPGA0_RFMOD, &rfmod);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
uint32_t enable_mask = U80211_DRV_RTL8188EU_REG_FPGA0_RFMOD_CCK_ENABLE | U80211_DRV_RTL8188EU_REG_FPGA0_RFMOD_OFDM_ENABLE;
|
||||
if ((rfmod & enable_mask) != enable_mask)
|
||||
return U80211_DRV_STATUS_FAULTY_HARDWARE;
|
||||
|
||||
return U80211_DRV_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
int u80211_drv_rtl8188eu_bb_load_table(u80211_drv_device_handle_t device) {
|
||||
for (size_t i = 0; i < ARRAY_SIZE(rtl8188eu_bb_regs); ++i) {
|
||||
int status = u80211_drv_rtl8188eu_reg_write32(device, rtl8188eu_bb_regs[i], rtl8188eu_bb_values[i]);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS) {
|
||||
u80211_drv_kernel_print(U80211_DRV_KERNEL_PRINT_LEVEL_ERROR, "rtl8188eu: BB table write failed");
|
||||
return status;
|
||||
}
|
||||
|
||||
u80211_drv_kernel_stall_us(1);
|
||||
}
|
||||
|
||||
return U80211_DRV_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
int u80211_drv_rtl8188eu_bb_load_agc_table(u80211_drv_device_handle_t device) {
|
||||
for (size_t i = 0; i < ARRAY_SIZE(rtl8188eu_agc_values); ++i) {
|
||||
int status = u80211_drv_rtl8188eu_reg_write32(device, U80211_DRV_RTL8188EU_REG_OFDM0_AGCRSSITABLE, rtl8188eu_agc_values[i]);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS) {
|
||||
u80211_drv_kernel_print(U80211_DRV_KERNEL_PRINT_LEVEL_ERROR, "rtl8188eu: AGC table write failed");
|
||||
return status;
|
||||
}
|
||||
|
||||
u80211_drv_kernel_stall_us(1);
|
||||
}
|
||||
|
||||
int status = u80211_drv_rtl8188eu_reg_write32(device, U80211_DRV_RTL8188EU_REG_OFDM0_AGCCORE1, U80211_DRV_RTL8188EU_REG_OFDM0_AGCCORE1_LATCH);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
u80211_drv_kernel_stall_us(1);
|
||||
|
||||
status = u80211_drv_rtl8188eu_reg_write32(device, U80211_DRV_RTL8188EU_REG_OFDM0_AGCCORE1, U80211_DRV_RTL8188EU_REG_OFDM0_AGCCORE1_FINAL);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
u80211_drv_kernel_stall_us(1);
|
||||
return U80211_DRV_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
int u80211_drv_rtl8188eu_bb_apply_efuse_calibration(u80211_drv_rtl8188eu_t *rtl8188eu) {
|
||||
uint32_t value;
|
||||
int status = u80211_drv_rtl8188eu_reg_read32(rtl8188eu->device, U80211_DRV_RTL8188EU_REG_AFE_XTAL_CTRL, &value);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
uint32_t crystal_cap = rtl8188eu->efuse.xtal_k;
|
||||
uint32_t crystal_field = crystal_cap | (crystal_cap << 6);
|
||||
value &= ~U80211_DRV_RTL8188EU_REG_AFE_XTAL_CTRL_ADDRESS_MASK;
|
||||
value |= (crystal_field << U80211_DRV_RTL8188EU_REG_AFE_XTAL_CTRL_ADDRESS_SHIFT) & U80211_DRV_RTL8188EU_REG_AFE_XTAL_CTRL_ADDRESS_MASK;
|
||||
status = u80211_drv_rtl8188eu_reg_write32(rtl8188eu->device, U80211_DRV_RTL8188EU_REG_AFE_XTAL_CTRL, value);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
status = u80211_drv_rtl8188eu_reg_read32(rtl8188eu->device, U80211_DRV_RTL8188EU_REG_HSSI_PARAM2_A, &value);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
rtl8188eu->cck_high_power = (value & U80211_DRV_RTL8188EU_REG_HSSI_PARAM2_CCK_HIGH_POWER) != 0;
|
||||
return U80211_DRV_STATUS_SUCCESS;
|
||||
}
|
||||
@@ -0,0 +1,663 @@
|
||||
/*-
|
||||
* Copyright (c) 2010 Damien Bergamini <damien.bergamini@free.fr>
|
||||
* Copyright (c) 2015 Stefan Sperling <stsp@openbsd.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <u80211_drv/rtl8188eu.h>
|
||||
#include <u80211_drv/status.h>
|
||||
|
||||
#define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0]))
|
||||
|
||||
#define RTL8188EU_IQK_RUN_COUNT 3
|
||||
#define RTL8188EU_IQK_TOLERANCE 5
|
||||
#define RTL8188EU_IQK_DELAY_US 1000
|
||||
#define RTL8188EU_LC_MAX_POLLS 100
|
||||
#define RTL8188EU_LC_POLL_DELAY_US 100
|
||||
|
||||
#define RTL8188EU_REG_GPIO_MUXCFG 0x0040
|
||||
#define RTL8188EU_REG_GPIO_MUXCFG_ENBT (1u << 5)
|
||||
|
||||
#define RTL8188EU_REG_TXPAUSE 0x0522
|
||||
#define RTL8188EU_REG_TXPAUSE_IQK_QUEUES 0x3f
|
||||
#define RTL8188EU_REG_BCN_CTRL1 0x0551
|
||||
#define RTL8188EU_REG_BCN_CTRL_ENABLE (1u << 3)
|
||||
|
||||
#define RTL8188EU_REG_CCK0_AFESETTING 0x0a04
|
||||
#define RTL8188EU_REG_OFDM1_LSTF 0x0d00
|
||||
#define RTL8188EU_REG_CONFIG_ANT_A 0x0b68
|
||||
#define RTL8188EU_REG_OFDM0_TRXPATHENA 0x0c04
|
||||
#define RTL8188EU_REG_OFDM0_TRMUXPAR 0x0c08
|
||||
#define RTL8188EU_REG_OFDM0_RXIQIMBALANCE_A 0x0c14
|
||||
#define RTL8188EU_REG_OFDM0_ECCATHRESHOLD 0x0c4c
|
||||
#define RTL8188EU_REG_OFDM0_TXIQIMBALANCE_A 0x0c80
|
||||
#define RTL8188EU_REG_OFDM0_TXAFE_A 0x0c94
|
||||
#define RTL8188EU_REG_OFDM0_RXIQEXTANTA 0x0ca0
|
||||
|
||||
#define RTL8188EU_REG_FPGA0_IQK 0x0e28
|
||||
#define RTL8188EU_REG_TX_IQK_TONE_A 0x0e30
|
||||
#define RTL8188EU_REG_RX_IQK_TONE_A 0x0e34
|
||||
#define RTL8188EU_REG_TX_IQK_PI_A 0x0e38
|
||||
#define RTL8188EU_REG_RX_IQK_PI_A 0x0e3c
|
||||
#define RTL8188EU_REG_TX_IQK 0x0e40
|
||||
#define RTL8188EU_REG_RX_IQK 0x0e44
|
||||
#define RTL8188EU_REG_IQK_AGC_PTS 0x0e48
|
||||
#define RTL8188EU_REG_IQK_AGC_RSP 0x0e4c
|
||||
#define RTL8188EU_REG_RX_IQK_TONE_B 0x0e54
|
||||
#define RTL8188EU_REG_TX_POWER_BEFORE_IQK_A 0x0e94
|
||||
#define RTL8188EU_REG_TX_POWER_AFTER_IQK_A 0x0e9c
|
||||
#define RTL8188EU_REG_RX_POWER_BEFORE_IQK_A 0x0ea4
|
||||
#define RTL8188EU_REG_RX_POWER_AFTER_IQK_A 0x0eac
|
||||
|
||||
#define RTL8188EU_IQK_TX_FAILURE (1u << 28)
|
||||
#define RTL8188EU_IQK_RX_FAILURE (1u << 27)
|
||||
#define RTL8188EU_IQK_RESULT_MASK 0x03ff
|
||||
#define RTL8188EU_IQK_TX0_FAILURE 0x0142
|
||||
#define RTL8188EU_IQK_TX1_FAILURE 0x0042
|
||||
#define RTL8188EU_IQK_RX0_FAILURE 0x0132
|
||||
#define RTL8188EU_IQK_RX1_FAILURE 0x0036
|
||||
|
||||
#define RTL8188EU_RF_AC 0x00
|
||||
#define RTL8188EU_RF_AC_MODE_MASK 0x70000
|
||||
#define RTL8188EU_RF_AC_MODE_STANDBY 0x10000
|
||||
#define RTL8188EU_RF_CHNLBW_LCSTART 0x08000
|
||||
#define RTL8188EU_CONTINUOUS_TX_MASK 0x70
|
||||
|
||||
static const uint16_t rtl8188eu_iqk_adda_regs[] = {
|
||||
0x085c,
|
||||
0x0e6c, 0x0e70, 0x0e74,
|
||||
0x0e78, 0x0e7c, 0x0e80, 0x0e84,
|
||||
0x0e88, 0x0e8c,
|
||||
0x0ed0, 0x0ed4, 0x0ed8, 0x0edc,
|
||||
0x0ee0, 0x0eec,
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
uint32_t adda[ARRAY_SIZE(rtl8188eu_iqk_adda_regs)];
|
||||
uint8_t txpause;
|
||||
uint8_t bcn_ctrl;
|
||||
uint8_t bcn_ctrl1;
|
||||
uint32_t gpio_mux;
|
||||
uint32_t trxpathena;
|
||||
uint32_t trmuxpar;
|
||||
uint32_t rfifacesw0;
|
||||
uint32_t rfifacesw1;
|
||||
uint32_t rfifaceoe0;
|
||||
uint32_t rfifaceoe1;
|
||||
uint32_t ant_a;
|
||||
uint32_t cck_afe;
|
||||
uint8_t agc_gain;
|
||||
} rtl8188eu_iqk_saved_t;
|
||||
|
||||
typedef struct {
|
||||
uint16_t tx[2];
|
||||
uint16_t rx[2];
|
||||
bool tx_valid;
|
||||
bool rx_valid;
|
||||
} rtl8188eu_iqk_result_t;
|
||||
|
||||
static int rtl8188eu_iqk_save(u80211_drv_device_handle_t device, rtl8188eu_iqk_saved_t *saved) {
|
||||
// IQK reconfigures a lot of baseband stuff, so we need to save these.
|
||||
for (size_t i = 0; i < ARRAY_SIZE(rtl8188eu_iqk_adda_regs); ++i) {
|
||||
int status = u80211_drv_rtl8188eu_reg_read32(device, rtl8188eu_iqk_adda_regs[i], &saved->adda[i]);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
}
|
||||
|
||||
int status = u80211_drv_rtl8188eu_reg_read8(device, RTL8188EU_REG_TXPAUSE, &saved->txpause);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
status = u80211_drv_rtl8188eu_reg_read8(device, U80211_DRV_RTL8188EU_REG_BCN_CTRL, &saved->bcn_ctrl);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
status = u80211_drv_rtl8188eu_reg_read8(device, RTL8188EU_REG_BCN_CTRL1, &saved->bcn_ctrl1);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
status = u80211_drv_rtl8188eu_reg_read32(device, RTL8188EU_REG_GPIO_MUXCFG, &saved->gpio_mux);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
status = u80211_drv_rtl8188eu_reg_read32(device, RTL8188EU_REG_OFDM0_TRXPATHENA, &saved->trxpathena);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
status = u80211_drv_rtl8188eu_reg_read32(device, RTL8188EU_REG_OFDM0_TRMUXPAR, &saved->trmuxpar);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
status = u80211_drv_rtl8188eu_reg_read32(device, U80211_DRV_RTL8188EU_REG_RF_IFACE_SW_A, &saved->rfifacesw0);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
status = u80211_drv_rtl8188eu_reg_read32(device, U80211_DRV_RTL8188EU_REG_RF_IFACE_SW_A + 4, &saved->rfifacesw1);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
status = u80211_drv_rtl8188eu_reg_read32(device, U80211_DRV_RTL8188EU_REG_RF_IFACE_OE_A, &saved->rfifaceoe0);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
status = u80211_drv_rtl8188eu_reg_read32(device, U80211_DRV_RTL8188EU_REG_RF_IFACE_OE_A + 4, &saved->rfifaceoe1);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
status = u80211_drv_rtl8188eu_reg_read32(device, RTL8188EU_REG_CONFIG_ANT_A, &saved->ant_a);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
status = u80211_drv_rtl8188eu_reg_read32(device, RTL8188EU_REG_CCK0_AFESETTING, &saved->cck_afe);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
uint32_t agc;
|
||||
status = u80211_drv_rtl8188eu_reg_read32(device, U80211_DRV_RTL8188EU_REG_OFDM0_AGCCORE1, &agc);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
saved->agc_gain = agc & UINT8_MAX;
|
||||
return U80211_DRV_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static int rtl8188eu_iqk_prepare(u80211_drv_device_handle_t device, const rtl8188eu_iqk_saved_t *saved) {
|
||||
int status = u80211_drv_rtl8188eu_reg_write32(device, rtl8188eu_iqk_adda_regs[0], 0x0b1b25a0);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
for (size_t i = 1; i < ARRAY_SIZE(rtl8188eu_iqk_adda_regs); ++i) {
|
||||
status = u80211_drv_rtl8188eu_reg_write32(device, rtl8188eu_iqk_adda_regs[i], 0x0bdb25a0);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
}
|
||||
|
||||
uint32_t hssi_param1;
|
||||
status = u80211_drv_rtl8188eu_reg_read32(device, U80211_DRV_RTL8188EU_REG_HSSI_PARAM1_A, &hssi_param1);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
if ((hssi_param1 & U80211_DRV_RTL8188EU_REG_HSSI_PARAM1_PI) == 0) {
|
||||
status = u80211_drv_rtl8188eu_reg_write32(device, U80211_DRV_RTL8188EU_REG_HSSI_PARAM1_A, hssi_param1 | U80211_DRV_RTL8188EU_REG_HSSI_PARAM1_PI);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
}
|
||||
|
||||
status = u80211_drv_rtl8188eu_reg_write32(device, RTL8188EU_REG_OFDM0_TRXPATHENA, 0x03a05600);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
status = u80211_drv_rtl8188eu_reg_write32(device, RTL8188EU_REG_OFDM0_TRMUXPAR, 0x000800e4);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
status = u80211_drv_rtl8188eu_reg_write32(device, U80211_DRV_RTL8188EU_REG_RF_IFACE_SW_A + 4, 0x22204000);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
status = u80211_drv_rtl8188eu_reg_write8(device, RTL8188EU_REG_TXPAUSE, RTL8188EU_REG_TXPAUSE_IQK_QUEUES);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
status = u80211_drv_rtl8188eu_reg_write8(device, U80211_DRV_RTL8188EU_REG_BCN_CTRL, saved->bcn_ctrl & ~RTL8188EU_REG_BCN_CTRL_ENABLE);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
status = u80211_drv_rtl8188eu_reg_write8(device, RTL8188EU_REG_BCN_CTRL1, saved->bcn_ctrl1 & ~RTL8188EU_REG_BCN_CTRL_ENABLE);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
status = u80211_drv_rtl8188eu_reg_write8(device, RTL8188EU_REG_GPIO_MUXCFG, (uint8_t)saved->gpio_mux & ~RTL8188EU_REG_GPIO_MUXCFG_ENBT);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
status = u80211_drv_rtl8188eu_reg_write32(device, RTL8188EU_REG_CONFIG_ANT_A, 0x00080000);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
status = u80211_drv_rtl8188eu_reg_write32(device, RTL8188EU_REG_FPGA0_IQK, 0x80800000);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
status = u80211_drv_rtl8188eu_reg_write32(device, RTL8188EU_REG_TX_IQK, 0x01007c00);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
return u80211_drv_rtl8188eu_reg_write32(device, RTL8188EU_REG_RX_IQK, 0x01004800);
|
||||
}
|
||||
|
||||
static int rtl8188eu_iqk_measure_once(u80211_drv_device_handle_t device, rtl8188eu_iqk_result_t *result) {
|
||||
result->tx_valid = false;
|
||||
result->rx_valid = false;
|
||||
|
||||
int status = u80211_drv_rtl8188eu_reg_write32(device, RTL8188EU_REG_TX_IQK_TONE_A, 0x10008c1f);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
status = u80211_drv_rtl8188eu_reg_write32(device, RTL8188EU_REG_RX_IQK_TONE_B, 0x10008c1f);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
status = u80211_drv_rtl8188eu_reg_write32(device, RTL8188EU_REG_TX_IQK_PI_A, 0x82140102);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
status = u80211_drv_rtl8188eu_reg_write32(device, RTL8188EU_REG_RX_IQK_PI_A, 0x28160502);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
status = u80211_drv_rtl8188eu_reg_write32(device, RTL8188EU_REG_IQK_AGC_RSP, 0x00462911);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
status = u80211_drv_rtl8188eu_reg_write32(device, RTL8188EU_REG_IQK_AGC_PTS, 0xf9000000);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
status = u80211_drv_rtl8188eu_reg_write32(device, RTL8188EU_REG_IQK_AGC_PTS, 0xf8000000);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
u80211_drv_kernel_stall_us(RTL8188EU_IQK_DELAY_US);
|
||||
|
||||
uint32_t iqk_status;
|
||||
status = u80211_drv_rtl8188eu_reg_read32(device, RTL8188EU_REG_RX_POWER_AFTER_IQK_A, &iqk_status);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
if ((iqk_status & RTL8188EU_IQK_TX_FAILURE) != 0)
|
||||
return U80211_DRV_STATUS_SUCCESS;
|
||||
|
||||
uint32_t value;
|
||||
status = u80211_drv_rtl8188eu_reg_read32(device, RTL8188EU_REG_TX_POWER_BEFORE_IQK_A, &value);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
result->tx[0] = (value >> 16) & RTL8188EU_IQK_RESULT_MASK;
|
||||
status = u80211_drv_rtl8188eu_reg_read32(device, RTL8188EU_REG_TX_POWER_AFTER_IQK_A, &value);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
result->tx[1] = (value >> 16) & RTL8188EU_IQK_RESULT_MASK;
|
||||
if (result->tx[0] == RTL8188EU_IQK_TX0_FAILURE || result->tx[1] == RTL8188EU_IQK_TX1_FAILURE)
|
||||
return U80211_DRV_STATUS_SUCCESS;
|
||||
|
||||
result->tx_valid = true;
|
||||
|
||||
if ((iqk_status & RTL8188EU_IQK_RX_FAILURE) != 0)
|
||||
return U80211_DRV_STATUS_SUCCESS;
|
||||
|
||||
status = u80211_drv_rtl8188eu_reg_read32(device, RTL8188EU_REG_RX_POWER_BEFORE_IQK_A, &value);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
result->rx[0] = (value >> 16) & RTL8188EU_IQK_RESULT_MASK;
|
||||
result->rx[1] = (iqk_status >> 16) & RTL8188EU_IQK_RESULT_MASK;
|
||||
if (result->rx[0] == RTL8188EU_IQK_RX0_FAILURE || result->rx[1] == RTL8188EU_IQK_RX1_FAILURE)
|
||||
return U80211_DRV_STATUS_SUCCESS;
|
||||
|
||||
result->rx_valid = true;
|
||||
return U80211_DRV_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static void rtl8188eu_iqk_record_error(int *result, int status) {
|
||||
if (*result == U80211_DRV_STATUS_SUCCESS && status != U80211_DRV_STATUS_SUCCESS)
|
||||
*result = status;
|
||||
}
|
||||
|
||||
static int rtl8188eu_iqk_leave_mode(u80211_drv_device_handle_t device) {
|
||||
int result = U80211_DRV_STATUS_SUCCESS;
|
||||
int status = u80211_drv_rtl8188eu_reg_write32(device, RTL8188EU_REG_FPGA0_IQK, 0);
|
||||
rtl8188eu_iqk_record_error(&result, status);
|
||||
status = u80211_drv_rtl8188eu_reg_write32(device, U80211_DRV_RTL8188EU_REG_LSSI_PARAM_A, 0x00032ed3);
|
||||
rtl8188eu_iqk_record_error(&result, status);
|
||||
return result;
|
||||
}
|
||||
|
||||
static int rtl8188eu_iqk_restore(u80211_drv_device_handle_t device, const rtl8188eu_iqk_saved_t *saved) {
|
||||
int result = rtl8188eu_iqk_leave_mode(device);
|
||||
int status;
|
||||
|
||||
for (size_t i = 0; i < ARRAY_SIZE(rtl8188eu_iqk_adda_regs); ++i) {
|
||||
status = u80211_drv_rtl8188eu_reg_write32(device, rtl8188eu_iqk_adda_regs[i], saved->adda[i]);
|
||||
rtl8188eu_iqk_record_error(&result, status);
|
||||
}
|
||||
status = u80211_drv_rtl8188eu_reg_write8(device, RTL8188EU_REG_TXPAUSE, saved->txpause);
|
||||
rtl8188eu_iqk_record_error(&result, status);
|
||||
status = u80211_drv_rtl8188eu_reg_write8(device, U80211_DRV_RTL8188EU_REG_BCN_CTRL, saved->bcn_ctrl);
|
||||
rtl8188eu_iqk_record_error(&result, status);
|
||||
status = u80211_drv_rtl8188eu_reg_write8(device, RTL8188EU_REG_BCN_CTRL1, saved->bcn_ctrl1);
|
||||
rtl8188eu_iqk_record_error(&result, status);
|
||||
status = u80211_drv_rtl8188eu_reg_write32(device, RTL8188EU_REG_GPIO_MUXCFG, saved->gpio_mux);
|
||||
rtl8188eu_iqk_record_error(&result, status);
|
||||
status = u80211_drv_rtl8188eu_reg_write32(device, RTL8188EU_REG_OFDM0_TRXPATHENA, saved->trxpathena);
|
||||
rtl8188eu_iqk_record_error(&result, status);
|
||||
status = u80211_drv_rtl8188eu_reg_write32(device, U80211_DRV_RTL8188EU_REG_RF_IFACE_SW_A, saved->rfifacesw0);
|
||||
rtl8188eu_iqk_record_error(&result, status);
|
||||
status = u80211_drv_rtl8188eu_reg_write32(device, U80211_DRV_RTL8188EU_REG_RF_IFACE_SW_A + 4, saved->rfifacesw1);
|
||||
rtl8188eu_iqk_record_error(&result, status);
|
||||
status = u80211_drv_rtl8188eu_reg_write32(device, U80211_DRV_RTL8188EU_REG_RF_IFACE_OE_A, saved->rfifaceoe0);
|
||||
rtl8188eu_iqk_record_error(&result, status);
|
||||
status = u80211_drv_rtl8188eu_reg_write32(device, U80211_DRV_RTL8188EU_REG_RF_IFACE_OE_A + 4, saved->rfifaceoe1);
|
||||
rtl8188eu_iqk_record_error(&result, status);
|
||||
status = u80211_drv_rtl8188eu_reg_write32(device, RTL8188EU_REG_OFDM0_TRMUXPAR, saved->trmuxpar);
|
||||
rtl8188eu_iqk_record_error(&result, status);
|
||||
status = u80211_drv_rtl8188eu_reg_write32(device, RTL8188EU_REG_CONFIG_ANT_A, saved->ant_a);
|
||||
rtl8188eu_iqk_record_error(&result, status);
|
||||
status = u80211_drv_rtl8188eu_reg_write32(device, RTL8188EU_REG_CCK0_AFESETTING, saved->cck_afe);
|
||||
rtl8188eu_iqk_record_error(&result, status);
|
||||
|
||||
uint32_t agc;
|
||||
status = u80211_drv_rtl8188eu_reg_read32(device, U80211_DRV_RTL8188EU_REG_OFDM0_AGCCORE1, &agc);
|
||||
rtl8188eu_iqk_record_error(&result, status);
|
||||
if (status == U80211_DRV_STATUS_SUCCESS) {
|
||||
agc &= ~UINT32_C(0xff);
|
||||
status = u80211_drv_rtl8188eu_reg_write32(device, U80211_DRV_RTL8188EU_REG_OFDM0_AGCCORE1, agc | 0x50);
|
||||
rtl8188eu_iqk_record_error(&result, status);
|
||||
status = u80211_drv_rtl8188eu_reg_write32(device, U80211_DRV_RTL8188EU_REG_OFDM0_AGCCORE1, agc | saved->agc_gain);
|
||||
rtl8188eu_iqk_record_error(&result, status);
|
||||
}
|
||||
status = u80211_drv_rtl8188eu_reg_write32(device, RTL8188EU_REG_TX_IQK_TONE_A, 0x01008c00);
|
||||
rtl8188eu_iqk_record_error(&result, status);
|
||||
status = u80211_drv_rtl8188eu_reg_write32(device, RTL8188EU_REG_RX_IQK_TONE_A, 0x01008c00);
|
||||
rtl8188eu_iqk_record_error(&result, status);
|
||||
return result;
|
||||
}
|
||||
|
||||
static unsigned int rtl8188eu_iqk_difference(uint16_t a, uint16_t b) {
|
||||
return a > b ? a - b : b - a;
|
||||
}
|
||||
|
||||
static bool rtl8188eu_iqk_results_close(const rtl8188eu_iqk_result_t *a, const rtl8188eu_iqk_result_t *b) {
|
||||
if (!a->tx_valid || !a->rx_valid || !b->tx_valid || !b->rx_valid)
|
||||
return false;
|
||||
|
||||
return rtl8188eu_iqk_difference(a->tx[0], b->tx[0]) <= RTL8188EU_IQK_TOLERANCE &&
|
||||
rtl8188eu_iqk_difference(a->tx[1], b->tx[1]) <= RTL8188EU_IQK_TOLERANCE &&
|
||||
rtl8188eu_iqk_difference(a->rx[0], b->rx[0]) <= RTL8188EU_IQK_TOLERANCE &&
|
||||
rtl8188eu_iqk_difference(a->rx[1], b->rx[1]) <= RTL8188EU_IQK_TOLERANCE;
|
||||
}
|
||||
|
||||
static int32_t rtl8188eu_iqk_sign_extend_10(uint16_t value) {
|
||||
return (value & 0x0200) != 0 ? (int32_t)value - 0x0400 : value;
|
||||
}
|
||||
|
||||
static int32_t rtl8188eu_iqk_arithmetic_shift(int32_t value, unsigned int shift) {
|
||||
if (value >= 0)
|
||||
return value >> shift;
|
||||
|
||||
return -(((-value) + ((1u << shift) - 1)) >> shift);
|
||||
}
|
||||
|
||||
static int rtl8188eu_iqk_write_results(u80211_drv_device_handle_t device, const rtl8188eu_iqk_result_t *result) {
|
||||
uint32_t reg;
|
||||
int status = u80211_drv_rtl8188eu_reg_read32(device, RTL8188EU_REG_OFDM0_TXIQIMBALANCE_A, ®);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
uint32_t factor = (reg >> 22) & RTL8188EU_IQK_RESULT_MASK;
|
||||
int32_t x_product = rtl8188eu_iqk_sign_extend_10(result->tx[0]) * (int32_t)factor;
|
||||
int32_t tx_a = rtl8188eu_iqk_arithmetic_shift(x_product, 8);
|
||||
|
||||
reg = (reg & ~UINT32_C(0x000003ff)) | ((uint32_t)tx_a & 0x000003ff);
|
||||
status = u80211_drv_rtl8188eu_reg_write32(device, RTL8188EU_REG_OFDM0_TXIQIMBALANCE_A, reg);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
status = u80211_drv_rtl8188eu_reg_read32(device, RTL8188EU_REG_OFDM0_ECCATHRESHOLD, ®);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
if (((uint32_t)rtl8188eu_iqk_arithmetic_shift(x_product, 7) & 1) != 0)
|
||||
reg |= UINT32_C(0x80000000);
|
||||
else
|
||||
reg &= ~UINT32_C(0x80000000);
|
||||
|
||||
status = u80211_drv_rtl8188eu_reg_write32(device, RTL8188EU_REG_OFDM0_ECCATHRESHOLD, reg);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
int32_t y_product = rtl8188eu_iqk_sign_extend_10(result->tx[1]) * (int32_t)factor;
|
||||
int32_t tx_c = rtl8188eu_iqk_arithmetic_shift(y_product, 8);
|
||||
|
||||
status = u80211_drv_rtl8188eu_reg_read32(device, RTL8188EU_REG_OFDM0_TXAFE_A, ®);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
reg = (reg & ~UINT32_C(0xf0000000)) | (((uint32_t)tx_c & 0x000003c0) << 22);
|
||||
status = u80211_drv_rtl8188eu_reg_write32(device, RTL8188EU_REG_OFDM0_TXAFE_A, reg);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
status = u80211_drv_rtl8188eu_reg_read32(device, RTL8188EU_REG_OFDM0_TXIQIMBALANCE_A, ®);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
reg = (reg & ~UINT32_C(0x003f0000)) | (((uint32_t)tx_c & 0x0000003f) << 16);
|
||||
status = u80211_drv_rtl8188eu_reg_write32(device, RTL8188EU_REG_OFDM0_TXIQIMBALANCE_A, reg);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
status = u80211_drv_rtl8188eu_reg_read32(device, RTL8188EU_REG_OFDM0_ECCATHRESHOLD, ®);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
if (((uint32_t)rtl8188eu_iqk_arithmetic_shift(y_product, 7) & 1) != 0)
|
||||
reg |= UINT32_C(0x20000000);
|
||||
else
|
||||
reg &= ~UINT32_C(0x20000000);
|
||||
|
||||
status = u80211_drv_rtl8188eu_reg_write32(device, RTL8188EU_REG_OFDM0_ECCATHRESHOLD, reg);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
status = u80211_drv_rtl8188eu_reg_read32(device, RTL8188EU_REG_OFDM0_RXIQIMBALANCE_A, ®);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
reg = (reg & ~UINT32_C(0x000003ff)) | (result->rx[0] & 0x03ff);
|
||||
reg = (reg & ~UINT32_C(0x0000fc00)) | ((uint32_t)(result->rx[1] & 0x003f) << 10);
|
||||
status = u80211_drv_rtl8188eu_reg_write32(device, RTL8188EU_REG_OFDM0_RXIQIMBALANCE_A, reg);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
status = u80211_drv_rtl8188eu_reg_read32(device, RTL8188EU_REG_OFDM0_RXIQEXTANTA, ®);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
reg = (reg & ~UINT32_C(0xf0000000)) | ((uint32_t)(result->rx[1] & 0x03c0) << 22);
|
||||
return u80211_drv_rtl8188eu_reg_write32(device, RTL8188EU_REG_OFDM0_RXIQEXTANTA, reg);
|
||||
}
|
||||
|
||||
static int rtl8188eu_iq_calibrate(u80211_drv_device_handle_t device) {
|
||||
rtl8188eu_iqk_saved_t saved;
|
||||
int status = rtl8188eu_iqk_save(device, &saved);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
rtl8188eu_iqk_result_t results[RTL8188EU_IQK_RUN_COUNT];
|
||||
bool hardware_modified = false;
|
||||
bool stable = false;
|
||||
unsigned int accepted_run = 0;
|
||||
|
||||
for (unsigned int run = 0; run < RTL8188EU_IQK_RUN_COUNT; ++run) {
|
||||
hardware_modified = true;
|
||||
status = rtl8188eu_iqk_prepare(device, &saved);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
break;
|
||||
|
||||
rtl8188eu_iqk_result_t discarded;
|
||||
status = rtl8188eu_iqk_measure_once(device, &discarded);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
break;
|
||||
status = rtl8188eu_iqk_measure_once(device, &results[run]);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
break;
|
||||
status = rtl8188eu_iqk_leave_mode(device);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
break;
|
||||
|
||||
if (run == 0)
|
||||
continue;
|
||||
|
||||
int restore_status = rtl8188eu_iqk_restore(device, &saved);
|
||||
hardware_modified = false;
|
||||
if (restore_status != U80211_DRV_STATUS_SUCCESS) {
|
||||
status = restore_status;
|
||||
break;
|
||||
}
|
||||
|
||||
if (rtl8188eu_iqk_results_close(&results[run - 1], &results[run])) {
|
||||
stable = true;
|
||||
accepted_run = run;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (hardware_modified) {
|
||||
int restore_status = rtl8188eu_iqk_restore(device, &saved);
|
||||
if (status == U80211_DRV_STATUS_SUCCESS)
|
||||
status = restore_status;
|
||||
}
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
if (!stable) {
|
||||
u80211_drv_kernel_print(U80211_DRV_KERNEL_PRINT_LEVEL_WARN, "rtl8188eu: IQ calibration did not stabilize");
|
||||
return U80211_DRV_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
return rtl8188eu_iqk_write_results(device, &results[accepted_run]);
|
||||
}
|
||||
|
||||
static int rtl8188eu_lc_calibrate(u80211_drv_device_handle_t device) {
|
||||
uint8_t txmode;
|
||||
int status = u80211_drv_rtl8188eu_reg_read8(device, RTL8188EU_REG_OFDM1_LSTF + 3, &txmode);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
int result = U80211_DRV_STATUS_SUCCESS;
|
||||
bool continuous_tx = (txmode & RTL8188EU_CONTINUOUS_TX_MASK) != 0;
|
||||
bool txmode_modified = false;
|
||||
bool txpause_modified = false;
|
||||
bool rf_ac_modified = false;
|
||||
uint8_t txpause = 0;
|
||||
uint32_t rf_ac = 0;
|
||||
|
||||
if (continuous_tx) {
|
||||
// disable continuous tx
|
||||
txmode_modified = true;
|
||||
status = u80211_drv_rtl8188eu_reg_write8(device, RTL8188EU_REG_OFDM1_LSTF + 3, txmode & ~RTL8188EU_CONTINUOUS_TX_MASK);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS) {
|
||||
result = status;
|
||||
goto restore;
|
||||
}
|
||||
|
||||
status = u80211_drv_rtl8188eu_rf_read(device, RTL8188EU_RF_AC, &rf_ac);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS) {
|
||||
result = status;
|
||||
goto restore;
|
||||
}
|
||||
rf_ac_modified = true;
|
||||
status = u80211_drv_rtl8188eu_rf_write(device, RTL8188EU_RF_AC, (rf_ac & ~RTL8188EU_RF_AC_MODE_MASK) | RTL8188EU_RF_AC_MODE_STANDBY);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS) {
|
||||
result = status;
|
||||
goto restore;
|
||||
}
|
||||
} else {
|
||||
// pause tx
|
||||
status = u80211_drv_rtl8188eu_reg_read8(device, RTL8188EU_REG_TXPAUSE, &txpause);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
txpause_modified = true;
|
||||
status = u80211_drv_rtl8188eu_reg_write8(device, RTL8188EU_REG_TXPAUSE, UINT8_MAX);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS) {
|
||||
result = status;
|
||||
goto restore;
|
||||
}
|
||||
}
|
||||
|
||||
// calibrate lc
|
||||
uint32_t chnlbw;
|
||||
status = u80211_drv_rtl8188eu_rf_read(device, U80211_DRV_RTL8188EU_RF_CHNLBW, &chnlbw);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS) {
|
||||
result = status;
|
||||
goto restore;
|
||||
}
|
||||
status = u80211_drv_rtl8188eu_rf_write(
|
||||
device,
|
||||
U80211_DRV_RTL8188EU_RF_CHNLBW,
|
||||
chnlbw | RTL8188EU_RF_CHNLBW_LCSTART
|
||||
);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS) {
|
||||
result = status;
|
||||
goto restore;
|
||||
}
|
||||
|
||||
unsigned int poll;
|
||||
for (poll = 0; poll < RTL8188EU_LC_MAX_POLLS; ++poll) {
|
||||
status = u80211_drv_rtl8188eu_rf_read(device, U80211_DRV_RTL8188EU_RF_CHNLBW, &chnlbw);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS) {
|
||||
result = status;
|
||||
goto restore;
|
||||
}
|
||||
if ((chnlbw & RTL8188EU_RF_CHNLBW_LCSTART) == 0)
|
||||
break;
|
||||
|
||||
u80211_drv_kernel_stall_us(RTL8188EU_LC_POLL_DELAY_US);
|
||||
}
|
||||
if (poll == RTL8188EU_LC_MAX_POLLS)
|
||||
result = U80211_DRV_STATUS_TIMEOUT;
|
||||
|
||||
restore:
|
||||
if (rf_ac_modified) {
|
||||
status = u80211_drv_rtl8188eu_rf_write(device, RTL8188EU_RF_AC, rf_ac);
|
||||
rtl8188eu_iqk_record_error(&result, status);
|
||||
}
|
||||
if (txmode_modified) {
|
||||
status = u80211_drv_rtl8188eu_reg_write8(device, RTL8188EU_REG_OFDM1_LSTF + 3, txmode);
|
||||
rtl8188eu_iqk_record_error(&result, status);
|
||||
}
|
||||
if (txpause_modified) {
|
||||
status = u80211_drv_rtl8188eu_reg_write8(device, RTL8188EU_REG_TXPAUSE, txpause);
|
||||
rtl8188eu_iqk_record_error(&result, status);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static int rtl8188eu_calibration_gpio_cleanup(u80211_drv_device_handle_t device) {
|
||||
uint8_t gpio;
|
||||
int status = u80211_drv_rtl8188eu_reg_read8(device, RTL8188EU_REG_GPIO_MUXCFG, &gpio);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
return u80211_drv_rtl8188eu_reg_write8(device, RTL8188EU_REG_GPIO_MUXCFG, gpio & ~RTL8188EU_REG_GPIO_MUXCFG_ENBT);
|
||||
}
|
||||
|
||||
int u80211_drv_rtl8188eu_calibrate(u80211_drv_device_handle_t device) {
|
||||
int status = rtl8188eu_iq_calibrate(device);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
status = rtl8188eu_lc_calibrate(device);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
return rtl8188eu_calibration_gpio_cleanup(device);
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#include <u80211_drv/rtl8188eu.h>
|
||||
#include <u80211_drv/status.h>
|
||||
|
||||
// TODO: serialize channel setting (either here or in u80211)
|
||||
int u80211_drv_rtl8188eu_set_channel(u80211_drv_rtl8188eu_t *rtl8188eu, uint8_t channel) {
|
||||
if (channel < 1 || channel > 11)
|
||||
return U80211_DRV_STATUS_INVALID_ARGUMENT;
|
||||
|
||||
// set the transmission power for this channel
|
||||
int status = u80211_drv_rtl8188eu_set_tx_power(rtl8188eu, channel);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
// tell wmac to use 20 mhz-sized channels
|
||||
uint8_t bwopmode;
|
||||
status = u80211_drv_rtl8188eu_reg_read8(rtl8188eu->device, U80211_DRV_RTL8188EU_REG_BWOPMODE, &bwopmode);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
bwopmode |= U80211_DRV_RTL8188EU_REG_BWOPMODE_20MHZ;
|
||||
status = u80211_drv_rtl8188eu_reg_write8(rtl8188eu->device, U80211_DRV_RTL8188EU_REG_BWOPMODE, bwopmode);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
// tell baseband blocks we are using 20 mhz channels
|
||||
uint32_t rfmod;
|
||||
status = u80211_drv_rtl8188eu_reg_read32(rtl8188eu->device, U80211_DRV_RTL8188EU_REG_FPGA0_RFMOD, &rfmod);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
rfmod &= ~U80211_DRV_RTL8188EU_REG_FPGA_RFMOD_40MHZ;
|
||||
status = u80211_drv_rtl8188eu_reg_write32(rtl8188eu->device, U80211_DRV_RTL8188EU_REG_FPGA0_RFMOD, rfmod);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
status = u80211_drv_rtl8188eu_reg_read32(rtl8188eu->device, U80211_DRV_RTL8188EU_REG_FPGA1_RFMOD, &rfmod);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
rfmod &= ~U80211_DRV_RTL8188EU_REG_FPGA_RFMOD_40MHZ;
|
||||
status = u80211_drv_rtl8188eu_reg_write32(rtl8188eu->device, U80211_DRV_RTL8188EU_REG_FPGA1_RFMOD, rfmod);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
// program channel and 20mhz bandwidth
|
||||
uint32_t chnlbw = rtl8188eu->rf_chnlbw & ~U80211_DRV_RTL8188EU_RF_CHNLBW_CHANNEL_BW_MASK;
|
||||
chnlbw |= U80211_DRV_RTL8188EU_RF_CHNLBW_BW20 | channel;
|
||||
return u80211_drv_rtl8188eu_rf_write(rtl8188eu->device, U80211_DRV_RTL8188EU_RF_CHNLBW, chnlbw);
|
||||
}
|
||||
@@ -0,0 +1,202 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#include <u80211_drv/kernel_interface.h>
|
||||
#include <u80211_drv/rtl8188eu.h>
|
||||
#include <u80211_drv/status.h>
|
||||
#include <u80211_drv/string.h>
|
||||
|
||||
#define EFUSE_ADDRESS_HIGH_MASK 0x03
|
||||
#define EFUSE_CONTROL_READ_READY (1u << 7)
|
||||
#define EFUSE_MAX_POLLS 500
|
||||
#define EFUSE_READ_SETTLE_US 50
|
||||
#define EFUSE_SECTION_LEN (U80211_DRV_RTL8188EU_EFUSE_WORDS_PER_SECTION * 2)
|
||||
#define EFUSE_SECTION_COUNT (U80211_DRV_RTL8188EU_EFUSE_MAP_LEN / EFUSE_SECTION_LEN)
|
||||
#define EFUSE_RTL_ID_OFFSET 0x000
|
||||
#define EFUSE_CCK_TX_POWER_BASE_INDEX_OFFSET 0x010
|
||||
#define EFUSE_HT40_1S_TX_POWER_BASE_INDEX_OFFSET 0x016
|
||||
#define EFUSE_HT20_OFDM_TX_POWER_DIFF_OFFSET 0x01b
|
||||
#define EFUSE_XTAL_K_OFFSET 0x0b9
|
||||
#define EFUSE_MAC_ADDRESS_OFFSET 0x0d7
|
||||
|
||||
int u80211_drv_rtl8188eu_efuse_prepare(u80211_drv_device_handle_t device) {
|
||||
uint16_t value;
|
||||
int status;
|
||||
|
||||
status = u80211_drv_rtl8188eu_reg_write8(device, U80211_DRV_RTL8188EU_REG_EFUSE_ACCESS, U80211_DRV_RTL8188EU_EFUSE_ACCESS_ENABLE);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
status = u80211_drv_rtl8188eu_reg_read16(device, U80211_DRV_RTL8188EU_REG_SYS_ISO_CTRL, &value);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
if ((value & U80211_DRV_RTL8188EU_REG_SYS_ISO_CTRL_PWC_EV12V) == 0) {
|
||||
value |= U80211_DRV_RTL8188EU_REG_SYS_ISO_CTRL_PWC_EV12V;
|
||||
status = u80211_drv_rtl8188eu_reg_write16(device, U80211_DRV_RTL8188EU_REG_SYS_ISO_CTRL, value);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
}
|
||||
|
||||
status = u80211_drv_rtl8188eu_reg_read16(device, U80211_DRV_RTL8188EU_REG_SYS_FUNC, &value);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
if ((value & U80211_DRV_RTL8188EU_REG_SYS_FUNC_ELDR) == 0) {
|
||||
value |= U80211_DRV_RTL8188EU_REG_SYS_FUNC_ELDR;
|
||||
status = u80211_drv_rtl8188eu_reg_write16(device, U80211_DRV_RTL8188EU_REG_SYS_FUNC, value);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
}
|
||||
|
||||
status = u80211_drv_rtl8188eu_reg_read16(device, U80211_DRV_RTL8188EU_REG_SYS_CLKR, &value);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
uint16_t required_clocks = U80211_DRV_RTL8188EU_REG_SYS_CLKR_LOADER_ENABLE | U80211_DRV_RTL8188EU_REG_SYS_CLKR_ANA8M;
|
||||
if ((value & required_clocks) != required_clocks) {
|
||||
value |= required_clocks;
|
||||
status = u80211_drv_rtl8188eu_reg_write16(device, U80211_DRV_RTL8188EU_REG_SYS_CLKR, value);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
}
|
||||
|
||||
return U80211_DRV_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
int u80211_drv_rtl8188eu_efuse_finish(u80211_drv_device_handle_t device) {
|
||||
return u80211_drv_rtl8188eu_reg_write8(device, U80211_DRV_RTL8188EU_REG_EFUSE_ACCESS, U80211_DRV_RTL8188EU_EFUSE_ACCESS_DISABLE);
|
||||
}
|
||||
|
||||
static int efuse_read_physical8(u80211_drv_device_handle_t device, uint16_t address, uint8_t *result) {
|
||||
if (address >= U80211_DRV_RTL8188EU_EFUSE_PHYSICAL_LEN)
|
||||
return U80211_DRV_STATUS_FAULTY_HARDWARE;
|
||||
|
||||
int status = u80211_drv_rtl8188eu_reg_write8(device, U80211_DRV_RTL8188EU_REG_EFUSE_CTRL + 1, address & 0xff);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
uint8_t value8;
|
||||
status = u80211_drv_rtl8188eu_reg_read8(device, U80211_DRV_RTL8188EU_REG_EFUSE_CTRL + 2, &value8);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
value8 &= ~EFUSE_ADDRESS_HIGH_MASK;
|
||||
value8 |= (address >> 8) & EFUSE_ADDRESS_HIGH_MASK;
|
||||
status = u80211_drv_rtl8188eu_reg_write8(device, U80211_DRV_RTL8188EU_REG_EFUSE_CTRL + 2, value8);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
status = u80211_drv_rtl8188eu_reg_read8(device, U80211_DRV_RTL8188EU_REG_EFUSE_CTRL + 3, &value8);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
value8 &= ~EFUSE_CONTROL_READ_READY;
|
||||
status = u80211_drv_rtl8188eu_reg_write8(device, U80211_DRV_RTL8188EU_REG_EFUSE_CTRL + 3, value8);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
uint32_t value32;
|
||||
unsigned int poll;
|
||||
for (poll = 0; poll < EFUSE_MAX_POLLS; ++poll) {
|
||||
status = u80211_drv_rtl8188eu_reg_read32(device, U80211_DRV_RTL8188EU_REG_EFUSE_CTRL, &value32);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
if ((value32 & U80211_DRV_RTL8188EU_REG_EFUSE_CTRL_READ_READY) != 0)
|
||||
break;
|
||||
}
|
||||
|
||||
if (poll == EFUSE_MAX_POLLS)
|
||||
return U80211_DRV_STATUS_TIMEOUT;
|
||||
|
||||
u80211_drv_kernel_stall_us(EFUSE_READ_SETTLE_US);
|
||||
|
||||
status = u80211_drv_rtl8188eu_reg_read32(device, U80211_DRV_RTL8188EU_REG_EFUSE_CTRL, &value32);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
*result = value32 & 0xff;
|
||||
return U80211_DRV_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
int u80211_drv_rtl8188eu_read_efuse(u80211_drv_device_handle_t device, uint8_t efuse_map[U80211_DRV_RTL8188EU_EFUSE_MAP_LEN]) {
|
||||
u80211_drv_memset(efuse_map, 0xff, U80211_DRV_RTL8188EU_EFUSE_MAP_LEN);
|
||||
|
||||
uint16_t physical_address = 0;
|
||||
while (physical_address < U80211_DRV_RTL8188EU_EFUSE_PHYSICAL_LEN) {
|
||||
uint8_t header;
|
||||
int status = efuse_read_physical8(device, physical_address++, &header);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
if (header == 0xff)
|
||||
return U80211_DRV_STATUS_SUCCESS;
|
||||
|
||||
unsigned int section;
|
||||
uint8_t word_mask;
|
||||
if ((header & 0x1f) == 0x0f) {
|
||||
section = (header & 0xe0) >> 5;
|
||||
|
||||
if (physical_address >= U80211_DRV_RTL8188EU_EFUSE_PHYSICAL_LEN)
|
||||
return U80211_DRV_STATUS_FAULTY_HARDWARE;
|
||||
|
||||
uint8_t extended_header;
|
||||
status = efuse_read_physical8(device, physical_address++, &extended_header);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
if ((extended_header & 0x0f) == 0x0f)
|
||||
continue;
|
||||
|
||||
section |= (extended_header & 0xf0) >> 1;
|
||||
word_mask = extended_header & 0x0f;
|
||||
} else {
|
||||
section = (header >> 4) & 0x0f;
|
||||
word_mask = header & 0x0f;
|
||||
}
|
||||
|
||||
if (section >= EFUSE_SECTION_COUNT)
|
||||
return U80211_DRV_STATUS_FAULTY_HARDWARE;
|
||||
|
||||
uint16_t logical_address = section * EFUSE_SECTION_LEN;
|
||||
for (unsigned int word = 0; word < U80211_DRV_RTL8188EU_EFUSE_WORDS_PER_SECTION; ++word) {
|
||||
if ((word_mask & (1u << word)) != 0) {
|
||||
logical_address += 2;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (logical_address + 1 >= U80211_DRV_RTL8188EU_EFUSE_MAP_LEN || physical_address + 1 >= U80211_DRV_RTL8188EU_EFUSE_PHYSICAL_LEN)
|
||||
return U80211_DRV_STATUS_FAULTY_HARDWARE;
|
||||
|
||||
status = efuse_read_physical8(device, physical_address++, &efuse_map[logical_address++]);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
status = efuse_read_physical8(device, physical_address++, &efuse_map[logical_address++]);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
return U80211_DRV_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
int u80211_drv_rtl8188eu_parse_efuse(const uint8_t efuse_map[U80211_DRV_RTL8188EU_EFUSE_MAP_LEN], u80211_drv_rtl8188eu_efuse_t *result) {
|
||||
uint16_t rtl_id = (uint16_t)efuse_map[EFUSE_RTL_ID_OFFSET] | ((uint16_t)efuse_map[EFUSE_RTL_ID_OFFSET + 1] << 8);
|
||||
if (rtl_id != U80211_DRV_RTL8188EU_EFUSE_RTL_ID)
|
||||
return U80211_DRV_STATUS_FAULTY_HARDWARE;
|
||||
|
||||
result->rtl_id = rtl_id;
|
||||
for (unsigned int i = 0; i < U80211_DRV_RTL8188EU_MAC_ADDRESS_LEN; ++i)
|
||||
result->mac_address[i] = efuse_map[EFUSE_MAC_ADDRESS_OFFSET + i];
|
||||
|
||||
for (unsigned int i = 0; i < U80211_DRV_RTL8188EU_CCK_TX_POWER_BASE_INDEX_COUNT; ++i)
|
||||
result->cck_tx_power_base_indexes[i] = efuse_map[EFUSE_CCK_TX_POWER_BASE_INDEX_OFFSET + i];
|
||||
|
||||
for (unsigned int i = 0; i < U80211_DRV_RTL8188EU_HT40_1S_TX_POWER_BASE_INDEX_COUNT; ++i)
|
||||
result->ht40_1s_tx_power_base_indexes[i] = efuse_map[EFUSE_HT40_1S_TX_POWER_BASE_INDEX_OFFSET + i];
|
||||
result->ht20_ofdm_tx_power_diff = efuse_map[EFUSE_HT20_OFDM_TX_POWER_DIFF_OFFSET];
|
||||
|
||||
result->xtal_k = efuse_map[EFUSE_XTAL_K_OFFSET] & 0x3f;
|
||||
return U80211_DRV_STATUS_SUCCESS;
|
||||
}
|
||||
@@ -0,0 +1,265 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <u80211_drv/rtl8188eu.h>
|
||||
#include <u80211_drv/status.h>
|
||||
|
||||
#define RTL8188EU_FIRMWARE_SIGNATURE 0x88e
|
||||
#define RTL8188EU_FIRMWARE_PREPARE_DELAY_US 50
|
||||
#define RTL8188EU_FIRMWARE_WRITE_SIZE 196
|
||||
#define RTL8188EU_FIRMWARE_PAGE_COUNT 8
|
||||
#define RTL8188EU_FIRMWARE_MAX_POLLS 1000
|
||||
#define RTL8188EU_FIRMWARE_CHECKSUM_POLL_DELAY_US 5
|
||||
#define RTL8188EU_FIRMWARE_READY_POLL_DELAY_US 10
|
||||
|
||||
static int firmware_reset(u80211_drv_device_handle_t device) {
|
||||
// hold mcu wrapper
|
||||
uint8_t value8;
|
||||
int status = u80211_drv_rtl8188eu_reg_read8(device, U80211_DRV_RTL8188EU_REG_RSV_CTRL, &value8);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
value8 &= ~U80211_DRV_RTL8188EU_REG_RSV_CTRL_WLOCK_00;
|
||||
status = u80211_drv_rtl8188eu_reg_write8(device, U80211_DRV_RTL8188EU_REG_RSV_CTRL, value8);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
// assert wrapper reset
|
||||
uint16_t value16;
|
||||
status = u80211_drv_rtl8188eu_reg_read16(device, U80211_DRV_RTL8188EU_REG_RSV_CTRL, &value16);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
value16 &= ~U80211_DRV_RTL8188EU_REG_RSV_CTRL_MCU_RST;
|
||||
status = u80211_drv_rtl8188eu_reg_write16(device, U80211_DRV_RTL8188EU_REG_RSV_CTRL, value16);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
// assert cpu reset
|
||||
status = u80211_drv_rtl8188eu_reg_read16(device, U80211_DRV_RTL8188EU_REG_SYS_FUNC, &value16);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
value16 &= ~U80211_DRV_RTL8188EU_REG_SYS_FUNC_CPUEN;
|
||||
status = u80211_drv_rtl8188eu_reg_write16(device, U80211_DRV_RTL8188EU_REG_SYS_FUNC, value16);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
// hold mcu wrapper
|
||||
status = u80211_drv_rtl8188eu_reg_read8(device, U80211_DRV_RTL8188EU_REG_RSV_CTRL, &value8);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
value8 &= ~U80211_DRV_RTL8188EU_REG_RSV_CTRL_WLOCK_00;
|
||||
status = u80211_drv_rtl8188eu_reg_write8(device, U80211_DRV_RTL8188EU_REG_RSV_CTRL, value8);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
// deassert mcu wrapper reset
|
||||
status = u80211_drv_rtl8188eu_reg_read16(device, U80211_DRV_RTL8188EU_REG_RSV_CTRL, &value16);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
value16 |= U80211_DRV_RTL8188EU_REG_RSV_CTRL_MCU_RST;
|
||||
status = u80211_drv_rtl8188eu_reg_write16(device, U80211_DRV_RTL8188EU_REG_RSV_CTRL, value16);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
// deassert cpu reset
|
||||
status = u80211_drv_rtl8188eu_reg_read16(device, U80211_DRV_RTL8188EU_REG_SYS_FUNC, &value16);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
value16 |= U80211_DRV_RTL8188EU_REG_SYS_FUNC_CPUEN;
|
||||
return u80211_drv_rtl8188eu_reg_write16(device, U80211_DRV_RTL8188EU_REG_SYS_FUNC, value16);
|
||||
}
|
||||
|
||||
int u80211_drv_rtl8188eu_firmware_prepare(u80211_drv_device_handle_t device, const void *firmware_data, size_t firmware_size, const uint8_t **firmware_payload, size_t *firmware_payload_size) {
|
||||
// verify if the firmware is ok
|
||||
const uint8_t *payload = firmware_data;
|
||||
size_t payload_size = firmware_size;
|
||||
uint16_t signature = (uint16_t)payload[0] | ((uint16_t)payload[1] << 8);
|
||||
if ((signature >> 4) == RTL8188EU_FIRMWARE_SIGNATURE) {
|
||||
payload += U80211_DRV_RTL8188EU_FIRMWARE_HEADER_SIZE;
|
||||
payload_size -= U80211_DRV_RTL8188EU_FIRMWARE_HEADER_SIZE;
|
||||
}
|
||||
|
||||
// check if there is stale ram in the mcu
|
||||
uint8_t value8;
|
||||
int status = u80211_drv_rtl8188eu_reg_read8(device, U80211_DRV_RTL8188EU_REG_MCUFWDL, &value8);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
if ((value8 & U80211_DRV_RTL8188EU_REG_MCUFWDL_RAM_DOWNLOAD_SELECT) != 0) {
|
||||
// clear stale ram and reset firmware
|
||||
status = u80211_drv_rtl8188eu_reg_write8(device, U80211_DRV_RTL8188EU_REG_MCUFWDL, 0);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
status = firmware_reset(device);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
}
|
||||
|
||||
// enable firmware download
|
||||
status = u80211_drv_rtl8188eu_reg_read8(device, U80211_DRV_RTL8188EU_REG_MCUFWDL, &value8);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
value8 |= U80211_DRV_RTL8188EU_REG_MCUFWDL_ENABLE;
|
||||
status = u80211_drv_rtl8188eu_reg_write8(device, U80211_DRV_RTL8188EU_REG_MCUFWDL, value8);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
uint32_t value32;
|
||||
status = u80211_drv_rtl8188eu_reg_read32(device, U80211_DRV_RTL8188EU_REG_MCUFWDL, &value32);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
value32 &= ~U80211_DRV_RTL8188EU_REG_MCUFWDL_ROM_DOWNLOAD_LENGTH;
|
||||
status = u80211_drv_rtl8188eu_reg_write32(device, U80211_DRV_RTL8188EU_REG_MCUFWDL, value32);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
// reset firmware checksum
|
||||
status = u80211_drv_rtl8188eu_reg_read8(device, U80211_DRV_RTL8188EU_REG_MCUFWDL, &value8);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
value8 |= U80211_DRV_RTL8188EU_REG_MCUFWDL_CHECKSUM_REPORT;
|
||||
status = u80211_drv_rtl8188eu_reg_write8(device, U80211_DRV_RTL8188EU_REG_MCUFWDL, value8);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
u80211_drv_kernel_stall_us(RTL8188EU_FIRMWARE_PREPARE_DELAY_US);
|
||||
*firmware_payload = payload;
|
||||
*firmware_payload_size = payload_size;
|
||||
return U80211_DRV_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static int firmware_write_page(u80211_drv_device_handle_t device, unsigned int page, const uint8_t *data, size_t size) {
|
||||
// set the page selector
|
||||
uint32_t value;
|
||||
int status = u80211_drv_rtl8188eu_reg_read32(device, U80211_DRV_RTL8188EU_REG_MCUFWDL, &value);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
value &= ~U80211_DRV_RTL8188EU_REG_MCUFWDL_PAGE_MASK;
|
||||
value |= (uint32_t)page << 16;
|
||||
status = u80211_drv_rtl8188eu_reg_write32(device, U80211_DRV_RTL8188EU_REG_MCUFWDL, value);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
// write firmware in 196-byte chunks.
|
||||
// this is actually needed because some chips apparently have issues
|
||||
// with different-sized writes: https://github.com/a5a5aa555oo/rtl8xxxu/issues/2
|
||||
uint16_t address = U80211_DRV_RTL8188EU_FIRMWARE_START_ADDRESS;
|
||||
while (size >= RTL8188EU_FIRMWARE_WRITE_SIZE) {
|
||||
status = u80211_drv_rtl8188eu_reg_write_region(device, address, data, RTL8188EU_FIRMWARE_WRITE_SIZE);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
address += RTL8188EU_FIRMWARE_WRITE_SIZE;
|
||||
data += RTL8188EU_FIRMWARE_WRITE_SIZE;
|
||||
size -= RTL8188EU_FIRMWARE_WRITE_SIZE;
|
||||
}
|
||||
|
||||
if (size != 0)
|
||||
return u80211_drv_rtl8188eu_reg_write_region(device, address, data, (uint16_t)size);
|
||||
|
||||
return U80211_DRV_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
int u80211_drv_rtl8188eu_firmware_upload(u80211_drv_device_handle_t device, const uint8_t *firmware_payload, size_t firmware_payload_size) {
|
||||
if (firmware_payload_size > U80211_DRV_RTL8188EU_FIRMWARE_PAGE_SIZE * RTL8188EU_FIRMWARE_PAGE_COUNT)
|
||||
return U80211_DRV_STATUS_INVALID_ARGUMENT;
|
||||
|
||||
unsigned int page = 0;
|
||||
while (firmware_payload_size != 0) {
|
||||
size_t page_size = firmware_payload_size;
|
||||
if (page_size > U80211_DRV_RTL8188EU_FIRMWARE_PAGE_SIZE)
|
||||
page_size = U80211_DRV_RTL8188EU_FIRMWARE_PAGE_SIZE;
|
||||
|
||||
int status = firmware_write_page(device, page, firmware_payload, page_size);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
firmware_payload += page_size;
|
||||
firmware_payload_size -= page_size;
|
||||
++page;
|
||||
}
|
||||
|
||||
return U80211_DRV_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
int u80211_drv_rtl8188eu_firmware_start(u80211_drv_device_handle_t device) {
|
||||
u80211_drv_kernel_print(U80211_DRV_KERNEL_PRINT_LEVEL_INFO, "rtl8188eu: waiting for firmware checksum");
|
||||
|
||||
uint32_t value32;
|
||||
unsigned int poll;
|
||||
int status;
|
||||
for (poll = 0; poll < RTL8188EU_FIRMWARE_MAX_POLLS; ++poll) {
|
||||
status = u80211_drv_rtl8188eu_reg_read32(device, U80211_DRV_RTL8188EU_REG_MCUFWDL, &value32);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
if ((value32 & U80211_DRV_RTL8188EU_REG_MCUFWDL_CHECKSUM_REPORT) != 0)
|
||||
break;
|
||||
|
||||
u80211_drv_kernel_stall_us(RTL8188EU_FIRMWARE_CHECKSUM_POLL_DELAY_US);
|
||||
}
|
||||
|
||||
if (poll == RTL8188EU_FIRMWARE_MAX_POLLS) {
|
||||
u80211_drv_kernel_print(U80211_DRV_KERNEL_PRINT_LEVEL_ERROR, "rtl8188eu: firmware checksum timed out");
|
||||
return U80211_DRV_STATUS_TIMEOUT;
|
||||
}
|
||||
|
||||
u80211_drv_kernel_print(U80211_DRV_KERNEL_PRINT_LEVEL_INFO, "rtl8188eu: firmware checksum ready");
|
||||
|
||||
// leave firmware download mode
|
||||
uint8_t value8;
|
||||
status = u80211_drv_rtl8188eu_reg_read8(device, U80211_DRV_RTL8188EU_REG_MCUFWDL, &value8);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
value8 &= ~U80211_DRV_RTL8188EU_REG_MCUFWDL_ENABLE;
|
||||
status = u80211_drv_rtl8188eu_reg_write8(device, U80211_DRV_RTL8188EU_REG_MCUFWDL, value8);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
status = u80211_drv_rtl8188eu_reg_write8(device, U80211_DRV_RTL8188EU_REG_MCUFWDL + 1, 0);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
// mark the downloaded image ready and discard stale firmware state
|
||||
status = u80211_drv_rtl8188eu_reg_read32(device, U80211_DRV_RTL8188EU_REG_MCUFWDL, &value32);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
value32 &= ~U80211_DRV_RTL8188EU_REG_MCUFWDL_WINTINI_READY;
|
||||
value32 |= U80211_DRV_RTL8188EU_REG_MCUFWDL_READY;
|
||||
status = u80211_drv_rtl8188eu_reg_write32(device, U80211_DRV_RTL8188EU_REG_MCUFWDL, value32);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
status = firmware_reset(device);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
u80211_drv_kernel_print(U80211_DRV_KERNEL_PRINT_LEVEL_INFO, "rtl8188eu: waiting for firmware ready");
|
||||
for (poll = 0; poll < RTL8188EU_FIRMWARE_MAX_POLLS; ++poll) {
|
||||
status = u80211_drv_rtl8188eu_reg_read32(device, U80211_DRV_RTL8188EU_REG_MCUFWDL, &value32);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
if ((value32 & U80211_DRV_RTL8188EU_REG_MCUFWDL_WINTINI_READY) != 0) {
|
||||
u80211_drv_kernel_print(U80211_DRV_KERNEL_PRINT_LEVEL_INFO, "rtl8188eu: firmware ready");
|
||||
return U80211_DRV_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
u80211_drv_kernel_stall_us(RTL8188EU_FIRMWARE_READY_POLL_DELAY_US);
|
||||
}
|
||||
|
||||
u80211_drv_kernel_print(U80211_DRV_KERNEL_PRINT_LEVEL_ERROR, "rtl8188eu: firmware ready timed out");
|
||||
return U80211_DRV_STATUS_TIMEOUT;
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <u80211_drv/kernel_interface.h>
|
||||
#include <u80211_drv/rtl8188eu.h>
|
||||
#include <u80211_drv/status.h>
|
||||
|
||||
#define RTL8188EU_CAM_WRITE_DELAY_US 100
|
||||
#define RTL8188EU_CAM_KEY_WORD_FIRST 2
|
||||
#define RTL8188EU_CAM_KEY_WORD_LAST 5
|
||||
|
||||
static int rtl8188eu_cam_write_word(u80211_drv_device_handle_t device, uint8_t entry, uint8_t word, uint32_t value) {
|
||||
uint32_t address = (uint32_t)entry * U80211_DRV_RTL8188EU_CAM_ENTRY_WORD_COUNT + word;
|
||||
int status = u80211_drv_rtl8188eu_reg_write32(device, U80211_DRV_RTL8188EU_REG_CAMWRITE, value);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
status = u80211_drv_rtl8188eu_reg_write32(device,
|
||||
U80211_DRV_RTL8188EU_REG_CAMCMD,
|
||||
U80211_DRV_RTL8188EU_REG_CAMCMD_POLLING | U80211_DRV_RTL8188EU_REG_CAMCMD_WRITE | address
|
||||
);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
u80211_drv_kernel_stall_us(RTL8188EU_CAM_WRITE_DELAY_US);
|
||||
return U80211_DRV_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static int rtl8188eu_key_parameters(const u80211_drv_key_t *key, uint32_t *algorithm) {
|
||||
switch (key->cipher) {
|
||||
case U80211_DRV_CIPHER_CCMP:
|
||||
if (key->key_len != 16)
|
||||
return U80211_DRV_STATUS_INVALID_ARGUMENT;
|
||||
|
||||
*algorithm = U80211_DRV_RTL8188EU_CAM_ALGORITHM_CCMP;
|
||||
return U80211_DRV_STATUS_SUCCESS;
|
||||
case U80211_DRV_CIPHER_TKIP:
|
||||
if (key->key_len != 32)
|
||||
return U80211_DRV_STATUS_INVALID_ARGUMENT;
|
||||
|
||||
*algorithm = U80211_DRV_RTL8188EU_CAM_ALGORITHM_TKIP;
|
||||
return U80211_DRV_STATUS_SUCCESS;
|
||||
case U80211_DRV_CIPHER_WEP40:
|
||||
if (key->key_len != 5)
|
||||
return U80211_DRV_STATUS_INVALID_ARGUMENT;
|
||||
|
||||
*algorithm = U80211_DRV_RTL8188EU_CAM_ALGORITHM_WEP40;
|
||||
return U80211_DRV_STATUS_SUCCESS;
|
||||
case U80211_DRV_CIPHER_WEP104:
|
||||
if (key->key_len != 13)
|
||||
return U80211_DRV_STATUS_INVALID_ARGUMENT;
|
||||
|
||||
*algorithm = U80211_DRV_RTL8188EU_CAM_ALGORITHM_WEP104;
|
||||
return U80211_DRV_STATUS_SUCCESS;
|
||||
default:
|
||||
return U80211_DRV_STATUS_NOT_SUPPORTED;
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t rtl8188eu_key_word(const u80211_drv_key_t *key, size_t offset) {
|
||||
uint32_t value = 0;
|
||||
for (size_t i = 0; i < sizeof(value) && offset + i < key->key_len && offset + i < 16; ++i)
|
||||
value |= (uint32_t)key->key[offset + i] << (i * 8);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
#define KNOWN_FLAGS (U80211_DRV_KEY_PAIRWISE | U80211_DRV_KEY_GROUP | U80211_DRV_KEY_RX | U80211_DRV_KEY_TX)
|
||||
|
||||
int u80211_drv_rtl8188eu_set_key(u80211_drv_rtl8188eu_t *rtl8188eu, const u80211_drv_key_t *key) {
|
||||
if (key->index > U80211_DRV_RTL8188EU_CAM_CTL0_KEY_ID_MASK)
|
||||
return U80211_DRV_STATUS_INVALID_ARGUMENT;
|
||||
|
||||
bool pairwise = (key->flags & U80211_DRV_KEY_PAIRWISE) != 0;
|
||||
bool group = (key->flags & U80211_DRV_KEY_GROUP) != 0;
|
||||
if (pairwise == group || (key->flags & (U80211_DRV_KEY_RX | U80211_DRV_KEY_TX)) == 0 || (key->flags & ~KNOWN_FLAGS) != 0)
|
||||
return U80211_DRV_STATUS_INVALID_ARGUMENT;
|
||||
|
||||
uint32_t algorithm;
|
||||
int status = rtl8188eu_key_parameters(key, &algorithm);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
// invalidate an existing entry before changing any of its contents
|
||||
status = rtl8188eu_cam_write_word(rtl8188eu->device, key->index, 0, 0);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
// clear the high words
|
||||
status = rtl8188eu_cam_write_word(rtl8188eu->device, key->index, 6, 0);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
status = rtl8188eu_cam_write_word(rtl8188eu->device, key->index, 7, 0);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
// copy the key
|
||||
for (uint8_t word = RTL8188EU_CAM_KEY_WORD_FIRST; word <= RTL8188EU_CAM_KEY_WORD_LAST; ++word) {
|
||||
status = rtl8188eu_cam_write_word(rtl8188eu->device, key->index, word, rtl8188eu_key_word(key, (word - 2) * 4));
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
}
|
||||
|
||||
// write the mac address high bytes
|
||||
uint32_t mac_high = key->peer[2] | ((uint32_t)key->peer[3] << 8) | ((uint32_t)key->peer[4] << 16) | ((uint32_t)key->peer[5] << 24);
|
||||
status = rtl8188eu_cam_write_word(rtl8188eu->device, key->index, 1, mac_high);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
// write key id, cipher id, low mac bytes and valid bit
|
||||
uint32_t control = (key->index & U80211_DRV_RTL8188EU_CAM_CTL0_KEY_ID_MASK) |
|
||||
(algorithm << U80211_DRV_RTL8188EU_CAM_CTL0_ALGORITHM_SHIFT) |
|
||||
U80211_DRV_RTL8188EU_CAM_CTL0_VALID |
|
||||
((uint32_t)key->peer[0] << 16) |
|
||||
((uint32_t)key->peer[1] << 24);
|
||||
if (group)
|
||||
control |= U80211_DRV_RTL8188EU_CAM_CTL0_GROUP;
|
||||
|
||||
return rtl8188eu_cam_write_word(rtl8188eu->device, key->index, 0, control);
|
||||
}
|
||||
|
||||
int u80211_drv_rtl8188eu_del_key(u80211_drv_rtl8188eu_t *rtl8188eu, uint8_t index) {
|
||||
if (index > U80211_DRV_RTL8188EU_CAM_CTL0_KEY_ID_MASK)
|
||||
return U80211_DRV_STATUS_INVALID_ARGUMENT;
|
||||
|
||||
return rtl8188eu_cam_write_word(rtl8188eu->device, index, 0, 0);
|
||||
}
|
||||
@@ -0,0 +1,384 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#include <u80211_drv/rtl8188eu.h>
|
||||
#include <u80211_drv/status.h>
|
||||
|
||||
#define RTL8188EU_LLT_MAX_POLLS 20
|
||||
#define RTL8188EU_LLT_POLL_DELAY_US 5
|
||||
|
||||
int u80211_drv_rtl8188eu_mac_enable_infrastructure(u80211_drv_device_handle_t device) {
|
||||
// explicitly leave the RX and TX engines disabled.
|
||||
uint16_t cr = U80211_DRV_RTL8188EU_REG_CR_HCI_TXDMA_ENABLE |
|
||||
U80211_DRV_RTL8188EU_REG_CR_HCI_RXDMA_ENABLE |
|
||||
U80211_DRV_RTL8188EU_REG_CR_TXDMA_ENABLE |
|
||||
U80211_DRV_RTL8188EU_REG_CR_RXDMA_ENABLE |
|
||||
U80211_DRV_RTL8188EU_REG_CR_PROTOCOL_ENABLE |
|
||||
U80211_DRV_RTL8188EU_REG_CR_SCHEDULE_ENABLE |
|
||||
U80211_DRV_RTL8188EU_REG_CR_SECURITY_ENABLE |
|
||||
U80211_DRV_RTL8188EU_REG_CR_CALTIMER_ENABLE;
|
||||
|
||||
return u80211_drv_rtl8188eu_reg_write16(device, U80211_DRV_RTL8188EU_REG_CR, cr);
|
||||
}
|
||||
|
||||
int u80211_drv_rtl8188eu_mac_enable_tx_rx(u80211_drv_device_handle_t device) {
|
||||
const uint16_t enable_mask = U80211_DRV_RTL8188EU_REG_CR_MAC_TX_ENABLE | U80211_DRV_RTL8188EU_REG_CR_MAC_RX_ENABLE;
|
||||
uint16_t cr;
|
||||
int status = u80211_drv_rtl8188eu_reg_read16(device, U80211_DRV_RTL8188EU_REG_CR, &cr);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
cr |= enable_mask;
|
||||
status = u80211_drv_rtl8188eu_reg_write16(device, U80211_DRV_RTL8188EU_REG_CR, cr);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
status = u80211_drv_rtl8188eu_reg_read16(device, U80211_DRV_RTL8188EU_REG_CR, &cr);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
if ((cr & enable_mask) != enable_mask)
|
||||
return U80211_DRV_STATUS_FAULTY_HARDWARE;
|
||||
|
||||
return U80211_DRV_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
int u80211_drv_rtl8188eu_mac_disable_rx_aggregation(u80211_drv_device_handle_t device) {
|
||||
uint8_t usb_special;
|
||||
int status = u80211_drv_rtl8188eu_reg_read8(device, U80211_DRV_RTL8188EU_REG_USB_SPECIAL_OPTION, &usb_special);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
usb_special &= ~U80211_DRV_RTL8188EU_REG_USB_SPECIAL_OPTION_AGG_ENABLE;
|
||||
status = u80211_drv_rtl8188eu_reg_write8(device, U80211_DRV_RTL8188EU_REG_USB_SPECIAL_OPTION, usb_special);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
uint8_t trxdma_ctrl;
|
||||
status = u80211_drv_rtl8188eu_reg_read8(device, U80211_DRV_RTL8188EU_REG_TRXDMA_CTRL, &trxdma_ctrl);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
trxdma_ctrl &= ~U80211_DRV_RTL8188EU_REG_TRXDMA_CTRL_RXDMA_AGG_ENABLE;
|
||||
status = u80211_drv_rtl8188eu_reg_write8(device, U80211_DRV_RTL8188EU_REG_TRXDMA_CTRL, trxdma_ctrl);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
status = u80211_drv_rtl8188eu_reg_read8(device, U80211_DRV_RTL8188EU_REG_USB_SPECIAL_OPTION, &usb_special);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
status = u80211_drv_rtl8188eu_reg_read8(device, U80211_DRV_RTL8188EU_REG_TRXDMA_CTRL, &trxdma_ctrl);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
if ((usb_special & U80211_DRV_RTL8188EU_REG_USB_SPECIAL_OPTION_AGG_ENABLE) != 0 || (trxdma_ctrl & U80211_DRV_RTL8188EU_REG_TRXDMA_CTRL_RXDMA_AGG_ENABLE) != 0)
|
||||
return U80211_DRV_STATUS_FAULTY_HARDWARE;
|
||||
|
||||
return U80211_DRV_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static uint16_t tx_queue_mapping(uint8_t bulk_out_endpoint_count) {
|
||||
// map wifi traffic classes onto hardware queues
|
||||
uint16_t vi_queue = bulk_out_endpoint_count >= 2 ? U80211_DRV_RTL8188EU_TRXDMA_QUEUE_NORMAL : U80211_DRV_RTL8188EU_TRXDMA_QUEUE_HIGH;
|
||||
uint16_t be_bk_queue;
|
||||
|
||||
if (bulk_out_endpoint_count >= 3)
|
||||
be_bk_queue = U80211_DRV_RTL8188EU_TRXDMA_QUEUE_LOW;
|
||||
else if (bulk_out_endpoint_count == 2)
|
||||
be_bk_queue = U80211_DRV_RTL8188EU_TRXDMA_QUEUE_NORMAL;
|
||||
else
|
||||
be_bk_queue = U80211_DRV_RTL8188EU_TRXDMA_QUEUE_HIGH;
|
||||
|
||||
return (U80211_DRV_RTL8188EU_TRXDMA_QUEUE_HIGH << U80211_DRV_RTL8188EU_REG_TRXDMA_CTRL_VO_SHIFT) |
|
||||
(vi_queue << U80211_DRV_RTL8188EU_REG_TRXDMA_CTRL_VI_SHIFT) |
|
||||
(be_bk_queue << U80211_DRV_RTL8188EU_REG_TRXDMA_CTRL_BE_SHIFT) |
|
||||
(be_bk_queue << U80211_DRV_RTL8188EU_REG_TRXDMA_CTRL_BK_SHIFT) |
|
||||
(U80211_DRV_RTL8188EU_TRXDMA_QUEUE_HIGH << U80211_DRV_RTL8188EU_REG_TRXDMA_CTRL_MG_SHIFT) |
|
||||
(U80211_DRV_RTL8188EU_TRXDMA_QUEUE_HIGH << U80211_DRV_RTL8188EU_REG_TRXDMA_CTRL_HI_SHIFT);
|
||||
}
|
||||
|
||||
int u80211_drv_rtl8188eu_mac_configure_tx_queues(u80211_drv_device_handle_t device, uint8_t bulk_out_endpoint_count) {
|
||||
// configure packet buffer pages
|
||||
uint32_t high_pages = U80211_DRV_RTL8188EU_TX_PAGE_NUM_HI_PQ;
|
||||
uint32_t normal_pages = bulk_out_endpoint_count >= 2 ? U80211_DRV_RTL8188EU_TX_PAGE_NUM_NORM_PQ : 0;
|
||||
uint32_t low_pages = bulk_out_endpoint_count >= 3 ? U80211_DRV_RTL8188EU_TX_PAGE_NUM_LO_PQ : 0;
|
||||
uint32_t public_pages = U80211_DRV_RTL8188EU_TX_TOTAL_PAGE_NUM - high_pages - normal_pages - low_pages - 1;
|
||||
|
||||
int status = u80211_drv_rtl8188eu_reg_write32(device, U80211_DRV_RTL8188EU_REG_RQPN_NPQ, normal_pages << U80211_DRV_RTL8188EU_REG_RQPN_NPQ_SHIFT);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
uint32_t rqpn = U80211_DRV_RTL8188EU_REG_RQPN_LOAD |
|
||||
(high_pages << U80211_DRV_RTL8188EU_REG_RQPN_HI_SHIFT) |
|
||||
(low_pages << U80211_DRV_RTL8188EU_REG_RQPN_LO_SHIFT) |
|
||||
(public_pages << U80211_DRV_RTL8188EU_REG_RQPN_PUB_SHIFT);
|
||||
status = u80211_drv_rtl8188eu_reg_write32(device, U80211_DRV_RTL8188EU_REG_RQPN, rqpn);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
uint16_t trxdma_ctrl;
|
||||
status = u80211_drv_rtl8188eu_reg_read16(device, U80211_DRV_RTL8188EU_REG_TRXDMA_CTRL, &trxdma_ctrl);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
trxdma_ctrl = (trxdma_ctrl & U80211_DRV_RTL8188EU_REG_TRXDMA_CTRL_LOW_CONTROL_MASK) | tx_queue_mapping(bulk_out_endpoint_count);
|
||||
return u80211_drv_rtl8188eu_reg_write16(device, U80211_DRV_RTL8188EU_REG_TRXDMA_CTRL, trxdma_ctrl);
|
||||
}
|
||||
|
||||
int u80211_drv_rtl8188eu_mac_configure_rx_fifo_boundary(u80211_drv_device_handle_t device) {
|
||||
uint16_t reg = U80211_DRV_RTL8188EU_REG_TRXFF_BNDY + 2;
|
||||
int status = u80211_drv_rtl8188eu_reg_write16(device, reg, U80211_DRV_RTL8188EU_RX_FIFO_BOUNDARY);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
uint16_t boundary;
|
||||
status = u80211_drv_rtl8188eu_reg_read16(device, reg, &boundary);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
if (boundary != U80211_DRV_RTL8188EU_RX_FIFO_BOUNDARY)
|
||||
return U80211_DRV_STATUS_FAULTY_HARDWARE;
|
||||
|
||||
return U80211_DRV_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
int u80211_drv_rtl8188eu_mac_configure_packet_buffer(u80211_drv_device_handle_t device) {
|
||||
uint8_t boundary = U80211_DRV_RTL8188EU_TX_TOTAL_PAGE_NUM + 1;
|
||||
const uint16_t boundary_regs[] = {
|
||||
U80211_DRV_RTL8188EU_REG_TXPKTBUF_BCNQ_BDNY,
|
||||
U80211_DRV_RTL8188EU_REG_TXPKTBUF_MGQ_BDNY,
|
||||
U80211_DRV_RTL8188EU_REG_TXPKTBUF_WMAC_LBK_BF_HD,
|
||||
U80211_DRV_RTL8188EU_REG_TRXFF_BNDY,
|
||||
U80211_DRV_RTL8188EU_REG_TDECTRL + 1,
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < sizeof(boundary_regs) / sizeof(boundary_regs[0]); ++i) {
|
||||
int status = u80211_drv_rtl8188eu_reg_write8(device, boundary_regs[i], boundary);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
}
|
||||
|
||||
return u80211_drv_rtl8188eu_reg_write8(device, U80211_DRV_RTL8188EU_REG_PBP, U80211_DRV_RTL8188EU_REG_PBP_128_BYTES);
|
||||
}
|
||||
|
||||
static int rtl8188eu_llt_write(u80211_drv_device_handle_t device, uint8_t address, uint8_t data) {
|
||||
// write the LLT entry
|
||||
uint32_t command = U80211_DRV_RTL8188EU_REG_LLT_INIT_OP_WRITE | ((uint32_t)address << 8) | data;
|
||||
int status = u80211_drv_rtl8188eu_reg_write32(device, U80211_DRV_RTL8188EU_REG_LLT_INIT, command);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
// wait for the write operation to complete
|
||||
for (unsigned int poll = 0; poll < RTL8188EU_LLT_MAX_POLLS; ++poll) {
|
||||
uint32_t value;
|
||||
status = u80211_drv_rtl8188eu_reg_read32(device, U80211_DRV_RTL8188EU_REG_LLT_INIT, &value);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
if ((value & U80211_DRV_RTL8188EU_REG_LLT_INIT_OP_MASK) == 0)
|
||||
return U80211_DRV_STATUS_SUCCESS;
|
||||
|
||||
u80211_drv_kernel_stall_us(RTL8188EU_LLT_POLL_DELAY_US);
|
||||
}
|
||||
|
||||
return U80211_DRV_STATUS_TIMEOUT;
|
||||
}
|
||||
|
||||
int u80211_drv_rtl8188eu_mac_initialize_llt(u80211_drv_device_handle_t device) {
|
||||
// write the linear tx page list
|
||||
for (unsigned int entry = 0; entry < U80211_DRV_RTL8188EU_TX_TOTAL_PAGE_NUM; ++entry) {
|
||||
int status = rtl8188eu_llt_write(device, (uint8_t)entry, (uint8_t)(entry + 1));
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
}
|
||||
|
||||
int status = rtl8188eu_llt_write(device, U80211_DRV_RTL8188EU_TX_TOTAL_PAGE_NUM, U80211_DRV_RTL8188EU_LLT_END);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
// write the cyclic pages
|
||||
uint8_t first_remaining_page = U80211_DRV_RTL8188EU_TX_TOTAL_PAGE_NUM + 1;
|
||||
for (unsigned int entry = first_remaining_page; entry < U80211_DRV_RTL8188EU_LLT_LAST_ENTRY; ++entry) {
|
||||
status = rtl8188eu_llt_write(device, (uint8_t)entry, (uint8_t)(entry + 1));
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
}
|
||||
|
||||
return rtl8188eu_llt_write(device, U80211_DRV_RTL8188EU_LLT_LAST_ENTRY, first_remaining_page);
|
||||
}
|
||||
|
||||
int u80211_drv_rtl8188eu_mac_configure_wmac(u80211_drv_rtl8188eu_t *rtl8188eu) {
|
||||
// 32-byte phy data aftter every rx descriptor
|
||||
u80211_drv_device_handle_t device = rtl8188eu->device;
|
||||
int status = u80211_drv_rtl8188eu_reg_write8(device, U80211_DRV_RTL8188EU_REG_RX_DRVINFO_SZ, U80211_DRV_RTL8188EU_RX_DRVINFO_SZ);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
// program the efuse mac into the wmac
|
||||
for (unsigned int i = 0; i < U80211_DRV_RTL8188EU_MAC_ADDRESS_LEN; ++i) {
|
||||
status = u80211_drv_rtl8188eu_reg_write8(device, U80211_DRV_RTL8188EU_REG_MACID + i, rtl8188eu->efuse.mac_address[i]);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
}
|
||||
|
||||
// set to 'no link' mode
|
||||
uint8_t msr;
|
||||
status = u80211_drv_rtl8188eu_reg_read8(device, U80211_DRV_RTL8188EU_REG_MSR, &msr);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
msr &= ~U80211_DRV_RTL8188EU_REG_MSR_NETWORK_TYPE_MASK;
|
||||
status = u80211_drv_rtl8188eu_reg_write8(device, U80211_DRV_RTL8188EU_REG_MSR, msr);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
// configure the receive filter
|
||||
// (any frames, append phy receive status, report icv and mic information)
|
||||
uint32_t rcr = U80211_DRV_RTL8188EU_REG_RCR_AAP |
|
||||
U80211_DRV_RTL8188EU_REG_RCR_APM |
|
||||
U80211_DRV_RTL8188EU_REG_RCR_AM |
|
||||
U80211_DRV_RTL8188EU_REG_RCR_AB |
|
||||
U80211_DRV_RTL8188EU_REG_RCR_AMF |
|
||||
U80211_DRV_RTL8188EU_REG_RCR_HTC_LOC_CTRL |
|
||||
U80211_DRV_RTL8188EU_REG_RCR_APP_PHYSTS |
|
||||
U80211_DRV_RTL8188EU_REG_RCR_APP_ICV |
|
||||
U80211_DRV_RTL8188EU_REG_RCR_APP_MIC;
|
||||
status = u80211_drv_rtl8188eu_reg_write32(device, U80211_DRV_RTL8188EU_REG_RCR, rcr);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
// accept all multicasts
|
||||
status = u80211_drv_rtl8188eu_reg_write32(device, U80211_DRV_RTL8188EU_REG_MAR, UINT32_MAX);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
status = u80211_drv_rtl8188eu_reg_write32(device, U80211_DRV_RTL8188EU_REG_MAR + 4, UINT32_MAX);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
// accept management and data, reject control
|
||||
status = u80211_drv_rtl8188eu_reg_write16(device, U80211_DRV_RTL8188EU_REG_RXFLTMAP0, UINT16_MAX);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
status = u80211_drv_rtl8188eu_reg_write16(device, U80211_DRV_RTL8188EU_REG_RXFLTMAP1, 0);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
return u80211_drv_rtl8188eu_reg_write16(device, U80211_DRV_RTL8188EU_REG_RXFLTMAP2, UINT16_MAX);
|
||||
}
|
||||
|
||||
int u80211_drv_rtl8188eu_mac_set_edca(u80211_drv_device_handle_t device, u80211_drv_rtl8188eu_access_category_t access_category, u80211_drv_rtl8188eu_edca_params_t params) {
|
||||
uint16_t reg;
|
||||
switch (access_category) {
|
||||
case U80211_DRV_RTL8188EU_ACCESS_CATEGORY_VO:
|
||||
reg = U80211_DRV_RTL8188EU_REG_EDCA_VO;
|
||||
break;
|
||||
case U80211_DRV_RTL8188EU_ACCESS_CATEGORY_VI:
|
||||
reg = U80211_DRV_RTL8188EU_REG_EDCA_VI;
|
||||
break;
|
||||
case U80211_DRV_RTL8188EU_ACCESS_CATEGORY_BE:
|
||||
reg = U80211_DRV_RTL8188EU_REG_EDCA_BE;
|
||||
break;
|
||||
case U80211_DRV_RTL8188EU_ACCESS_CATEGORY_BK:
|
||||
reg = U80211_DRV_RTL8188EU_REG_EDCA_BK;
|
||||
break;
|
||||
default:
|
||||
return U80211_DRV_STATUS_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
unsigned int aifs = params.aifsn * params.slot_time + 10;
|
||||
if (params.ecwmin > 0x0f || params.ecwmax > 0x0f || aifs > UINT8_MAX)
|
||||
return U80211_DRV_STATUS_INVALID_ARGUMENT;
|
||||
|
||||
uint32_t value = ((uint32_t)params.txop << 16) | ((uint32_t)params.ecwmax << 12) | ((uint32_t)params.ecwmin << 8) | aifs;
|
||||
return u80211_drv_rtl8188eu_reg_write32(device, reg, value);
|
||||
}
|
||||
|
||||
int u80211_drv_rtl8188eu_mac_configure_timing(u80211_drv_device_handle_t device) {
|
||||
// set fallback response rate to 1mbps using cck
|
||||
uint32_t rrsr;
|
||||
int status = u80211_drv_rtl8188eu_reg_read32(device, U80211_DRV_RTL8188EU_REG_RRSR, &rrsr);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
rrsr = (rrsr & ~U80211_DRV_RTL8188EU_REG_RRSR_RATE_MASK) | U80211_DRV_RTL8188EU_REG_RRSR_RATE_CCK_ONLY_1M;
|
||||
status = u80211_drv_rtl8188eu_reg_write32(device, U80211_DRV_RTL8188EU_REG_RRSR, rrsr);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
// set retry limits to 48 retries
|
||||
status = u80211_drv_rtl8188eu_reg_write16(device, U80211_DRV_RTL8188EU_REG_RL, 0x3030);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
// configure SIFS
|
||||
const uint16_t sifs_regs[] = {
|
||||
U80211_DRV_RTL8188EU_REG_SPEC_SIFS,
|
||||
U80211_DRV_RTL8188EU_REG_SIFS_CCK,
|
||||
U80211_DRV_RTL8188EU_REG_SIFS_OFDM,
|
||||
U80211_DRV_RTL8188EU_REG_MAC_SPEC_SIFS,
|
||||
U80211_DRV_RTL8188EU_REG_RESP_SIFS_CCK,
|
||||
U80211_DRV_RTL8188EU_REG_RESP_SIFS_OFDM,
|
||||
};
|
||||
for (size_t i = 0; i < sizeof(sifs_regs) / sizeof(sifs_regs[0]); ++i) {
|
||||
status = u80211_drv_rtl8188eu_reg_write16(device, sifs_regs[i], 0x100a);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
}
|
||||
|
||||
// use AMPDU new retry mechanism
|
||||
uint8_t txq_ctrl;
|
||||
status = u80211_drv_rtl8188eu_reg_read8(device, U80211_DRV_RTL8188EU_REG_FWHW_TXQ_CTRL, &txq_ctrl);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
txq_ctrl |= U80211_DRV_RTL8188EU_REG_FWHW_TXQ_CTRL_AMPDU_RTY_NEW;
|
||||
status = u80211_drv_rtl8188eu_reg_write8(device, U80211_DRV_RTL8188EU_REG_FWHW_TXQ_CTRL, txq_ctrl);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
// set ack timeout to 64 microseconds
|
||||
status = u80211_drv_rtl8188eu_reg_write8(device, U80211_DRV_RTL8188EU_REG_ACKTO, 0x40);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
// initialize beacon packet machinery
|
||||
status = u80211_drv_rtl8188eu_reg_write16(device, U80211_DRV_RTL8188EU_REG_BCN_CTRL, 0x1010);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
status = u80211_drv_rtl8188eu_reg_write16(device, U80211_DRV_RTL8188EU_REG_TBTT_PROHIBIT, 0x6404);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
status = u80211_drv_rtl8188eu_reg_write8(device, U80211_DRV_RTL8188EU_REG_DRVERLYINT, 0x05);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
status = u80211_drv_rtl8188eu_reg_write8(device, U80211_DRV_RTL8188EU_REG_BCNDMATIM, 0x02);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
return u80211_drv_rtl8188eu_reg_write16(device, U80211_DRV_RTL8188EU_REG_BCNTCFG, 0x660f);
|
||||
}
|
||||
|
||||
int u80211_drv_rtl8188eu_mac_configure_hardware_offloads(u80211_drv_device_handle_t device) {
|
||||
// configure encryption offload
|
||||
uint32_t cam_command = U80211_DRV_RTL8188EU_REG_CAMCMD_CLR | U80211_DRV_RTL8188EU_REG_CAMCMD_POLLING;
|
||||
int status = u80211_drv_rtl8188eu_reg_write32(device, U80211_DRV_RTL8188EU_REG_CAMCMD, cam_command);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
// station-mode WPA keys are selected from the CAM by peer address or the
|
||||
// cipher-header key ID. the default-key bits are for legacy key selection
|
||||
// and can make encrypted data use a different CAM entry than the handshake.
|
||||
uint8_t security_config = U80211_DRV_RTL8188EU_REG_SECCFG_TXENC_ENABLE |
|
||||
U80211_DRV_RTL8188EU_REG_SECCFG_RXENC_ENABLE;
|
||||
status = u80211_drv_rtl8188eu_reg_write8(device, U80211_DRV_RTL8188EU_REG_SECCFG, security_config);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
// configure sequence handling offload
|
||||
return u80211_drv_rtl8188eu_reg_write8(device, U80211_DRV_RTL8188EU_REG_HWSEQ_CTRL, UINT8_MAX);
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/* $OpenBSD: r92creg.h,v 1.31 2024/09/01 03:14:48 jsg Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2010 Damien Bergamini <damien.bergamini@free.fr>
|
||||
* Copyright (c) 2015 Stefan Sperling <stsp@openbsd.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <u80211_drv/rtl8188eu.h>
|
||||
#include <u80211_drv/status.h>
|
||||
|
||||
#define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0]))
|
||||
|
||||
typedef struct {
|
||||
uint16_t reg;
|
||||
uint8_t value;
|
||||
} rtl8188eu_reg8_t;
|
||||
|
||||
// Imported from OpenBSD sys/dev/ic/r92creg.h.
|
||||
static const rtl8188eu_reg8_t rtl8188eu_mac_table[] = {
|
||||
{ 0x026, 0x41 }, { 0x027, 0x35 }, { 0x040, 0x00 }, { 0x428, 0x0a },
|
||||
{ 0x429, 0x10 }, { 0x430, 0x00 }, { 0x431, 0x01 }, { 0x432, 0x02 },
|
||||
{ 0x433, 0x04 }, { 0x434, 0x05 }, { 0x435, 0x06 }, { 0x436, 0x07 },
|
||||
{ 0x437, 0x08 }, { 0x438, 0x00 }, { 0x439, 0x00 }, { 0x43a, 0x01 },
|
||||
{ 0x43b, 0x02 }, { 0x43c, 0x04 }, { 0x43d, 0x05 }, { 0x43e, 0x06 },
|
||||
{ 0x43f, 0x07 }, { 0x440, 0x5d }, { 0x441, 0x01 }, { 0x442, 0x00 },
|
||||
{ 0x444, 0x15 }, { 0x445, 0xf0 }, { 0x446, 0x0f }, { 0x447, 0x00 },
|
||||
{ 0x458, 0x41 }, { 0x459, 0xa8 }, { 0x45a, 0x72 }, { 0x45b, 0xb9 },
|
||||
{ 0x460, 0x66 }, { 0x461, 0x66 }, { 0x480, 0x08 }, { 0x4c8, 0xff },
|
||||
{ 0x4c9, 0x08 }, { 0x4cc, 0xff }, { 0x4cd, 0xff }, { 0x4ce, 0x01 },
|
||||
{ 0x4d3, 0x01 }, { 0x500, 0x26 }, { 0x501, 0xa2 }, { 0x502, 0x2f },
|
||||
{ 0x503, 0x00 }, { 0x504, 0x28 }, { 0x505, 0xa3 }, { 0x506, 0x5e },
|
||||
{ 0x507, 0x00 }, { 0x508, 0x2b }, { 0x509, 0xa4 }, { 0x50a, 0x5e },
|
||||
{ 0x50b, 0x00 }, { 0x50c, 0x4f }, { 0x50d, 0xa4 }, { 0x50e, 0x00 },
|
||||
{ 0x50f, 0x00 }, { 0x512, 0x1c }, { 0x514, 0x0a }, { 0x516, 0x0a },
|
||||
{ 0x525, 0x4f }, { 0x550, 0x10 }, { 0x551, 0x10 }, { 0x559, 0x02 },
|
||||
{ 0x55d, 0xff }, { 0x605, 0x30 }, { 0x608, 0x0e }, { 0x609, 0x2a },
|
||||
{ 0x620, 0xff }, { 0x621, 0xff }, { 0x622, 0xff }, { 0x623, 0xff },
|
||||
{ 0x624, 0xff }, { 0x625, 0xff }, { 0x626, 0xff }, { 0x627, 0xff },
|
||||
{ 0x652, 0x20 }, { 0x63c, 0x0a }, { 0x63d, 0x0a }, { 0x63e, 0x0e },
|
||||
{ 0x63f, 0x0e }, { 0x640, 0x40 }, { 0x66e, 0x05 }, { 0x700, 0x21 },
|
||||
{ 0x701, 0x43 }, { 0x702, 0x65 }, { 0x703, 0x87 }, { 0x708, 0x21 },
|
||||
{ 0x709, 0x43 }, { 0x70a, 0x65 }, { 0x70b, 0x87 }
|
||||
};
|
||||
|
||||
int u80211_drv_rtl8188eu_mac_load_table(u80211_drv_device_handle_t device) {
|
||||
for (size_t i = 0; i < ARRAY_SIZE(rtl8188eu_mac_table); ++i) {
|
||||
int status = u80211_drv_rtl8188eu_reg_write8(device, rtl8188eu_mac_table[i].reg, rtl8188eu_mac_table[i].value);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS) {
|
||||
u80211_drv_kernel_print(U80211_DRV_KERNEL_PRINT_LEVEL_ERROR, "rtl8188eu: MAC table write failed");
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
return u80211_drv_rtl8188eu_reg_write8(device, U80211_DRV_RTL8188EU_REG_MAX_AGGR_NUM, U80211_DRV_RTL8188EU_REG_MAX_AGGR_NUM_8188EU);
|
||||
}
|
||||
@@ -0,0 +1,360 @@
|
||||
#include <u80211_drv/drv_init.h>
|
||||
#include <u80211_drv/rtl8188eu.h>
|
||||
#include <u80211_drv/status.h>
|
||||
|
||||
static int u80211_drv_rtl8188eu_led_enable_activity(u80211_drv_device_handle_t device) {
|
||||
uint8_t ledcfg;
|
||||
int status = u80211_drv_rtl8188eu_reg_read8(device, U80211_DRV_RTL8188EU_REG_LEDCFG2, &ledcfg);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
ledcfg &= ~U80211_DRV_RTL8188EU_REG_LEDCFG2_SW_LED_DISABLE;
|
||||
ledcfg |= U80211_DRV_RTL8188EU_REG_LEDCFG2_HW_LED_CONTROL | U80211_DRV_RTL8188EU_REG_LEDCFG2_HW_LED_ENABLE;
|
||||
return u80211_drv_rtl8188eu_reg_write8(device, U80211_DRV_RTL8188EU_REG_LEDCFG2, ledcfg);
|
||||
}
|
||||
|
||||
static int transmit(void *device, void *buffer, size_t size, size_t current_offset, const u80211_drv_transmit_options_t *options) {
|
||||
return u80211_drv_rtl8188eu_transmit(device, buffer, size, current_offset, options);
|
||||
}
|
||||
|
||||
static int set_channel(void *device, uint8_t channel) {
|
||||
return u80211_drv_rtl8188eu_set_channel(device, channel);
|
||||
}
|
||||
|
||||
static int set_key(void *device, const u80211_drv_key_t *key) {
|
||||
return u80211_drv_rtl8188eu_set_key(device, key);
|
||||
}
|
||||
|
||||
static int del_key(void *device, uint8_t index) {
|
||||
return u80211_drv_rtl8188eu_del_key(device, index);
|
||||
}
|
||||
|
||||
static const u80211_drv_device_ops_t device_ops = {
|
||||
.allocate_tx_buffer = u80211_drv_rtl8188eu_tx_buffer_allocate,
|
||||
.free_tx_buffer = u80211_drv_rtl8188eu_tx_buffer_free,
|
||||
.transmit = transmit,
|
||||
.set_channel = set_channel,
|
||||
.set_key = set_key,
|
||||
.del_key = del_key,
|
||||
};
|
||||
|
||||
static int discover_bulk_endpoints(u80211_drv_rtl8188eu_t *rtl8188eu) {
|
||||
u80211_drv_interface_descriptor_t interface_descriptor;
|
||||
int status = u80211_drv_kernel_get_interface_descriptor(rtl8188eu->interface, &interface_descriptor);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
if (interface_descriptor.endpoint_count == 0)
|
||||
return U80211_DRV_STATUS_NOT_SUPPORTED;
|
||||
|
||||
u80211_drv_endpoint_descriptor_t *endpoints = u80211_drv_kernel_allocate(interface_descriptor.endpoint_count * sizeof(*endpoints));
|
||||
if (endpoints == NULL)
|
||||
return U80211_DRV_STATUS_OUT_OF_MEMORY;
|
||||
|
||||
status = u80211_drv_kernel_get_endpoints(rtl8188eu->interface, endpoints, interface_descriptor.endpoint_count);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS) {
|
||||
u80211_drv_kernel_free(endpoints);
|
||||
return status;
|
||||
}
|
||||
|
||||
rtl8188eu->bulk_out_endpoint_count = 0;
|
||||
rtl8188eu->rx_endpoint = 0;
|
||||
rtl8188eu->tx_endpoint_high = 0;
|
||||
rtl8188eu->tx_endpoint_normal = 0;
|
||||
rtl8188eu->tx_endpoint_low = 0;
|
||||
|
||||
for (uint8_t i = 0; i < interface_descriptor.endpoint_count; ++i) {
|
||||
if ((endpoints[i].attributes & U80211_DRV_KERNEL_ENDPOINT_TRANSFER_TYPE_MASK) != U80211_DRV_KERNEL_ENDPOINT_TRANSFER_TYPE_BULK)
|
||||
continue;
|
||||
if ((endpoints[i].address & U80211_DRV_KERNEL_XFER_DIRECTION_MASK) == U80211_DRV_KERNEL_XFER_IN) {
|
||||
if (rtl8188eu->rx_endpoint == 0)
|
||||
rtl8188eu->rx_endpoint = endpoints[i].address;
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (rtl8188eu->bulk_out_endpoint_count) {
|
||||
case 0:
|
||||
rtl8188eu->tx_endpoint_high = endpoints[i].address;
|
||||
break;
|
||||
case 1:
|
||||
rtl8188eu->tx_endpoint_normal = endpoints[i].address;
|
||||
break;
|
||||
case 2:
|
||||
rtl8188eu->tx_endpoint_low = endpoints[i].address;
|
||||
break;
|
||||
}
|
||||
|
||||
++rtl8188eu->bulk_out_endpoint_count;
|
||||
}
|
||||
|
||||
u80211_drv_kernel_free(endpoints);
|
||||
|
||||
if (rtl8188eu->rx_endpoint == 0 || rtl8188eu->bulk_out_endpoint_count == 0)
|
||||
return U80211_DRV_STATUS_NOT_SUPPORTED;
|
||||
|
||||
return U80211_DRV_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static void firmware_loaded(void *context, const void *firmware_data, size_t firmware_size) {
|
||||
u80211_drv_rtl8188eu_t *rtl8188eu = context;
|
||||
// the firmware is now in memory, we can continue initializing the chip.
|
||||
|
||||
u80211_drv_kernel_print(U80211_DRV_KERNEL_PRINT_LEVEL_INFO, "rtl8188eu: found rtl8188eufw.bin");
|
||||
int status = u80211_drv_rtl8188eu_power_active(rtl8188eu->device);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
goto error;
|
||||
|
||||
status = u80211_drv_rtl8188eu_mac_enable_infrastructure(rtl8188eu->device);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
goto error;
|
||||
|
||||
status = u80211_drv_rtl8188eu_mac_configure_tx_queues(rtl8188eu->device, rtl8188eu->bulk_out_endpoint_count);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
goto error;
|
||||
|
||||
status = u80211_drv_rtl8188eu_mac_configure_rx_fifo_boundary(rtl8188eu->device);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
goto error;
|
||||
|
||||
u80211_drv_kernel_print(U80211_DRV_KERNEL_PRINT_LEVEL_INFO, "rtl8188eu: uploading firmware");
|
||||
|
||||
// upload the firmware into the MCU
|
||||
const uint8_t *firmware_payload;
|
||||
size_t firmware_payload_size;
|
||||
status = u80211_drv_rtl8188eu_firmware_prepare(
|
||||
rtl8188eu->device,
|
||||
firmware_data,
|
||||
firmware_size,
|
||||
&firmware_payload,
|
||||
&firmware_payload_size
|
||||
);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
goto error;
|
||||
|
||||
status = u80211_drv_rtl8188eu_firmware_upload(rtl8188eu->device, firmware_payload, firmware_payload_size);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
goto error;
|
||||
|
||||
status = u80211_drv_rtl8188eu_firmware_start(rtl8188eu->device);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
goto error;
|
||||
|
||||
u80211_drv_kernel_print(U80211_DRV_KERNEL_PRINT_LEVEL_INFO, "rtl8188eu: firmware initialized");
|
||||
|
||||
status = u80211_drv_rtl8188eu_mac_load_table(rtl8188eu->device);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
goto error;
|
||||
|
||||
u80211_drv_kernel_print(U80211_DRV_KERNEL_PRINT_LEVEL_INFO, "rtl8188eu: MAC table initialized");
|
||||
|
||||
status = u80211_drv_rtl8188eu_bb_enable(rtl8188eu->device);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
goto error;
|
||||
|
||||
status = u80211_drv_rtl8188eu_bb_load_table(rtl8188eu->device);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
goto error;
|
||||
|
||||
u80211_drv_kernel_print(U80211_DRV_KERNEL_PRINT_LEVEL_INFO, "rtl8188eu: BB table initialized");
|
||||
|
||||
status = u80211_drv_rtl8188eu_bb_load_agc_table(rtl8188eu->device);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
goto error;
|
||||
|
||||
u80211_drv_kernel_print(U80211_DRV_KERNEL_PRINT_LEVEL_INFO, "rtl8188eu: AGC table initialized");
|
||||
|
||||
status = u80211_drv_rtl8188eu_bb_apply_efuse_calibration(rtl8188eu);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
goto error;
|
||||
|
||||
u80211_drv_kernel_print(U80211_DRV_KERNEL_PRINT_LEVEL_INFO, "rtl8188eu: EFUSE crystal calibration applied");
|
||||
|
||||
status = u80211_drv_rtl8188eu_rf_load_table(rtl8188eu->device);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
goto error;
|
||||
|
||||
u80211_drv_kernel_print(U80211_DRV_KERNEL_PRINT_LEVEL_INFO, "rtl8188eu: RF table initialized");
|
||||
|
||||
status = u80211_drv_rtl8188eu_rf_read(rtl8188eu->device, U80211_DRV_RTL8188EU_RF_CHNLBW, &rtl8188eu->rf_chnlbw);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
goto error;
|
||||
|
||||
u80211_drv_kernel_print(U80211_DRV_KERNEL_PRINT_LEVEL_INFO, "rtl8188eu: RF channel state cached");
|
||||
|
||||
status = u80211_drv_rtl8188eu_mac_configure_packet_buffer(rtl8188eu->device);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
goto error;
|
||||
|
||||
u80211_drv_kernel_print(U80211_DRV_KERNEL_PRINT_LEVEL_INFO, "rtl8188eu: packet buffer configured");
|
||||
|
||||
status = u80211_drv_rtl8188eu_mac_initialize_llt(rtl8188eu->device);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
goto error;
|
||||
|
||||
u80211_drv_kernel_print(U80211_DRV_KERNEL_PRINT_LEVEL_INFO, "rtl8188eu: LLT initialized");
|
||||
|
||||
status = u80211_drv_rtl8188eu_mac_enable_tx_rx(rtl8188eu->device);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
goto error;
|
||||
|
||||
u80211_drv_kernel_print(U80211_DRV_KERNEL_PRINT_LEVEL_INFO, "rtl8188eu: MAC TX and RX enabled");
|
||||
|
||||
status = u80211_drv_rtl8188eu_mac_disable_rx_aggregation(rtl8188eu->device);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
goto error;
|
||||
|
||||
u80211_drv_kernel_print(U80211_DRV_KERNEL_PRINT_LEVEL_INFO, "rtl8188eu: RX aggregation disabled");
|
||||
|
||||
status = u80211_drv_rtl8188eu_mac_configure_wmac(rtl8188eu);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
goto error;
|
||||
|
||||
u80211_drv_kernel_print(U80211_DRV_KERNEL_PRINT_LEVEL_INFO, "rtl8188eu: WMAC configured");
|
||||
|
||||
status = u80211_drv_rtl8188eu_mac_configure_timing(rtl8188eu->device);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
goto error;
|
||||
|
||||
u80211_drv_kernel_print(U80211_DRV_KERNEL_PRINT_LEVEL_INFO, "rtl8188eu: timing configured");
|
||||
|
||||
status = u80211_drv_rtl8188eu_bb_enable_datapaths(rtl8188eu->device);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
goto error;
|
||||
|
||||
u80211_drv_kernel_print(U80211_DRV_KERNEL_PRINT_LEVEL_INFO, "rtl8188eu: BB datapaths enabled");
|
||||
|
||||
status = u80211_drv_rtl8188eu_mac_configure_hardware_offloads(rtl8188eu->device);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
goto error;
|
||||
|
||||
u80211_drv_kernel_print(U80211_DRV_KERNEL_PRINT_LEVEL_INFO, "rtl8188eu: hardware offloads configured");
|
||||
|
||||
status = u80211_drv_rtl8188eu_calibrate(rtl8188eu->device);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
goto error;
|
||||
|
||||
u80211_drv_kernel_print(U80211_DRV_KERNEL_PRINT_LEVEL_INFO, "rtl8188eu: RF calibration complete");
|
||||
|
||||
status = u80211_drv_rtl8188eu_set_channel(rtl8188eu, 1);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
goto error;
|
||||
|
||||
u80211_drv_kernel_print(U80211_DRV_KERNEL_PRINT_LEVEL_INFO, "rtl8188eu: channel 1 configured");
|
||||
|
||||
status = u80211_drv_rtl8188eu_led_enable_activity(rtl8188eu->device);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
goto error;
|
||||
|
||||
u80211_drv_kernel_print(U80211_DRV_KERNEL_PRINT_LEVEL_INFO, "rtl8188eu: activity LED enabled");
|
||||
|
||||
status = u80211_drv_rtl8188eu_rx_start(rtl8188eu);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
goto error;
|
||||
|
||||
u80211_drv_kernel_print(U80211_DRV_KERNEL_PRINT_LEVEL_INFO, "rtl8188eu: RX started");
|
||||
|
||||
u80211_drv_device_metadata_t metadata = {
|
||||
.rate_bitmap = {
|
||||
0x14, // 1 and 2 Mbps
|
||||
0x18, // 5.5 and 6 Mbps
|
||||
0x44, // 9 and 11 Mbps
|
||||
0x01, // 12 Mbps
|
||||
0x10, // 18 Mbps
|
||||
0x00,
|
||||
0x01, // 24 Mbps
|
||||
0x00,
|
||||
0x00,
|
||||
0x01, // 36 Mbps
|
||||
0x00,
|
||||
0x00,
|
||||
0x01, // 48 Mbps
|
||||
0x10, // 54 Mbps
|
||||
0x00,
|
||||
0x00,
|
||||
},
|
||||
};
|
||||
for (size_t i = 0; i < U80211_DRV_DEVICE_MAC_ADDRESS_LEN; ++i)
|
||||
metadata.mac_address[i] = rtl8188eu->efuse.mac_address[i];
|
||||
|
||||
status = u80211_drv_device_ready(rtl8188eu, &metadata, &device_ops, &rtl8188eu->network_device);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
goto error;
|
||||
|
||||
return;
|
||||
error:
|
||||
u80211_drv_kernel_print(U80211_DRV_KERNEL_PRINT_LEVEL_ERROR, "rtl8188eu: post firmware initialization failed");
|
||||
}
|
||||
|
||||
int u80211_drv_rtl8188eu_init(u80211_drv_device_handle_t device, u80211_drv_interface_handle_t interface) {
|
||||
uint32_t sys_cfg;
|
||||
int status;
|
||||
|
||||
// some cuts are not supported by this driver, check if we are attaching one of them
|
||||
status = u80211_drv_rtl8188eu_reg_read32(device, U80211_DRV_RTL8188EU_REG_SYS_CFG, &sys_cfg);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
if ((sys_cfg & U80211_DRV_RTL8188EU_REG_SYS_CFG_TRP_VAUX_EN) != 0 || U80211_DRV_RTL8188EU_REG_SYS_CFG_VER(sys_cfg) == 8)
|
||||
return U80211_DRV_STATUS_NOT_SUPPORTED;
|
||||
|
||||
u80211_drv_rtl8188eu_t *rtl8188eu = u80211_drv_kernel_allocate(sizeof(*rtl8188eu));
|
||||
if (rtl8188eu == NULL)
|
||||
return U80211_DRV_STATUS_OUT_OF_MEMORY;
|
||||
|
||||
rtl8188eu->device = device;
|
||||
rtl8188eu->interface = interface;
|
||||
__atomic_store_n(&rtl8188eu->network_device, NULL, __ATOMIC_RELAXED);
|
||||
|
||||
// this is necessary to set up the TX queues and submit RX transfers later
|
||||
status = discover_bulk_endpoints(rtl8188eu);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS) {
|
||||
u80211_drv_kernel_free(rtl8188eu);
|
||||
return status;
|
||||
}
|
||||
|
||||
// read efuses to get information like the MAC address
|
||||
uint8_t *efuse_map = u80211_drv_kernel_allocate(U80211_DRV_RTL8188EU_EFUSE_MAP_LEN);
|
||||
if (efuse_map == NULL) {
|
||||
u80211_drv_kernel_free(rtl8188eu);
|
||||
return U80211_DRV_STATUS_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
status = u80211_drv_rtl8188eu_efuse_prepare(device);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS) {
|
||||
u80211_drv_kernel_free(efuse_map);
|
||||
u80211_drv_kernel_free(rtl8188eu);
|
||||
return status;
|
||||
}
|
||||
|
||||
status = u80211_drv_rtl8188eu_read_efuse(device, efuse_map);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS) {
|
||||
u80211_drv_kernel_free(efuse_map);
|
||||
u80211_drv_kernel_free(rtl8188eu);
|
||||
return status;
|
||||
}
|
||||
|
||||
status = u80211_drv_rtl8188eu_efuse_finish(device);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS) {
|
||||
u80211_drv_kernel_free(efuse_map);
|
||||
u80211_drv_kernel_free(rtl8188eu);
|
||||
return status;
|
||||
}
|
||||
|
||||
status = u80211_drv_rtl8188eu_parse_efuse(efuse_map, &rtl8188eu->efuse);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS) {
|
||||
u80211_drv_kernel_free(efuse_map);
|
||||
u80211_drv_kernel_free(rtl8188eu);
|
||||
return status;
|
||||
}
|
||||
|
||||
u80211_drv_kernel_free(efuse_map);
|
||||
|
||||
// wait until firmware gets loaded from disk by the kernel
|
||||
// TODO: have a way of cancelling this wait for detach
|
||||
u80211_drv_kernel_print(U80211_DRV_KERNEL_PRINT_LEVEL_INFO, "rtl8188eu: waiting for rtl8188eufw.bin");
|
||||
status = u80211_drv_kernel_get_firmware("rtl8188eufw.bin", firmware_loaded, rtl8188eu);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
u80211_drv_kernel_free(rtl8188eu);
|
||||
|
||||
return status;
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#include <u80211_drv/kernel_interface.h>
|
||||
#include <u80211_drv/rtl8188eu.h>
|
||||
#include <u80211_drv/status.h>
|
||||
|
||||
#define POWER_MAX_POLLS 500
|
||||
#define POWER_POLL_DELAY_US 10
|
||||
|
||||
int u80211_drv_rtl8188eu_power_active(u80211_drv_device_handle_t device) {
|
||||
uint16_t value16;
|
||||
int status = u80211_drv_rtl8188eu_reg_read16(device, U80211_DRV_RTL8188EU_REG_APS_FSMCO, &value16);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
// disable wlan suspend and some pcie state machine stuff?
|
||||
value16 &= ~(U80211_DRV_RTL8188EU_REG_APS_FSMCO_HW_SUSPEND | U80211_DRV_RTL8188EU_REG_APS_FSMCO_PCIE);
|
||||
status = u80211_drv_rtl8188eu_reg_write16(device, U80211_DRV_RTL8188EU_REG_APS_FSMCO, value16);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
u80211_drv_kernel_print(U80211_DRV_KERNEL_PRINT_LEVEL_INFO, "rtl8188eu: waiting for power ready");
|
||||
|
||||
uint32_t value32;
|
||||
unsigned int poll;
|
||||
for (poll = 0; poll < POWER_MAX_POLLS; ++poll) {
|
||||
status = u80211_drv_rtl8188eu_reg_read32(device, U80211_DRV_RTL8188EU_REG_APS_FSMCO, &value32);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
if ((value32 & U80211_DRV_RTL8188EU_REG_APS_FSMCO_POWER_READY) != 0)
|
||||
break;
|
||||
|
||||
u80211_drv_kernel_stall_us(POWER_POLL_DELAY_US);
|
||||
}
|
||||
|
||||
if (poll == POWER_MAX_POLLS)
|
||||
return U80211_DRV_STATUS_TIMEOUT;
|
||||
|
||||
u80211_drv_kernel_print(U80211_DRV_KERNEL_PRINT_LEVEL_INFO, "rtl8188eu: power ready");
|
||||
|
||||
// hold baseband off for now
|
||||
uint8_t value8;
|
||||
status = u80211_drv_rtl8188eu_reg_read8(device, U80211_DRV_RTL8188EU_REG_SYS_FUNC, &value8);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
value8 &= ~(U80211_DRV_RTL8188EU_REG_SYS_FUNC_BBRSTB | U80211_DRV_RTL8188EU_REG_SYS_FUNC_BB_GLB_RSTN);
|
||||
status = u80211_drv_rtl8188eu_reg_write8(device, U80211_DRV_RTL8188EU_REG_SYS_FUNC, value8);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
// configure analog frontend crystal
|
||||
status = u80211_drv_rtl8188eu_reg_read32(device, U80211_DRV_RTL8188EU_REG_AFE_XTAL_CTRL, &value32);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
value32 |= U80211_DRV_RTL8188EU_REG_AFE_XTAL_CTRL_SCHMITT_TRIGGER;
|
||||
status = u80211_drv_rtl8188eu_reg_write32(device, U80211_DRV_RTL8188EU_REG_AFE_XTAL_CTRL, value32);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
// power on the device proper (disable hardware power down bit)
|
||||
status = u80211_drv_rtl8188eu_reg_read16(device, U80211_DRV_RTL8188EU_REG_APS_FSMCO, &value16);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
value16 &= ~U80211_DRV_RTL8188EU_REG_APS_FSMCO_HW_POWERDOWN;
|
||||
status = u80211_drv_rtl8188eu_reg_write16(device, U80211_DRV_RTL8188EU_REG_APS_FSMCO, value16);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
// disable wlan suspend and pcie state machine stuff (again)
|
||||
status = u80211_drv_rtl8188eu_reg_read16(device, U80211_DRV_RTL8188EU_REG_APS_FSMCO, &value16);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
value16 &= ~(U80211_DRV_RTL8188EU_REG_APS_FSMCO_HW_SUSPEND | U80211_DRV_RTL8188EU_REG_APS_FSMCO_PCIE);
|
||||
status = u80211_drv_rtl8188eu_reg_write16(device, U80211_DRV_RTL8188EU_REG_APS_FSMCO, value16);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
// enable MAC
|
||||
status = u80211_drv_rtl8188eu_reg_read32(device, U80211_DRV_RTL8188EU_REG_APS_FSMCO, &value32);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
value32 |= U80211_DRV_RTL8188EU_REG_APS_FSMCO_MAC_ENABLE;
|
||||
status = u80211_drv_rtl8188eu_reg_write32(device, U80211_DRV_RTL8188EU_REG_APS_FSMCO, value32);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
u80211_drv_kernel_print(U80211_DRV_KERNEL_PRINT_LEVEL_INFO, "rtl8188eu: waiting for MAC ready");
|
||||
for (poll = 0; poll < POWER_MAX_POLLS; ++poll) {
|
||||
status = u80211_drv_rtl8188eu_reg_read32(device, U80211_DRV_RTL8188EU_REG_APS_FSMCO, &value32);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
if ((value32 & U80211_DRV_RTL8188EU_REG_APS_FSMCO_MAC_ENABLE) == 0)
|
||||
break;
|
||||
|
||||
u80211_drv_kernel_stall_us(POWER_POLL_DELAY_US);
|
||||
}
|
||||
|
||||
if (poll == POWER_MAX_POLLS)
|
||||
return U80211_DRV_STATUS_TIMEOUT;
|
||||
|
||||
u80211_drv_kernel_print(U80211_DRV_KERNEL_PRINT_LEVEL_INFO, "rtl8188eu: MAC ready");
|
||||
|
||||
// enable low power low-dropout
|
||||
status = u80211_drv_rtl8188eu_reg_read8(device, U80211_DRV_RTL8188EU_REG_LPLDO_CTRL, &value8);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
value8 &= ~U80211_DRV_RTL8188EU_REG_LPLDO_CTRL_SLEEP;
|
||||
return u80211_drv_rtl8188eu_reg_write8(device, U80211_DRV_RTL8188EU_REG_LPLDO_CTRL, value8);
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <u80211_drv/kernel_interface.h>
|
||||
#include <u80211_drv/rtl8188eu.h>
|
||||
#include <u80211_drv/status.h>
|
||||
#include <u80211_drv/util.h>
|
||||
|
||||
enum {
|
||||
REALTEK_USB_READ = 0xc0,
|
||||
REALTEK_USB_WRITE = 0x40,
|
||||
REALTEK_USB_CMD_REQ = 0x05,
|
||||
REALTEK_USB_CMD_IDX = 0x00,
|
||||
REALTEK_USB_TIMEOUT_MS = 500,
|
||||
};
|
||||
|
||||
static int reg_xfer(u80211_drv_device_handle_t device, uint8_t flags, uint16_t reg, void *data, uint16_t size) {
|
||||
size_t transferred = 0;
|
||||
int status = u80211_drv_kernel_submit_control_xfer_and_wait(
|
||||
device,
|
||||
flags,
|
||||
REALTEK_USB_CMD_REQ,
|
||||
reg,
|
||||
REALTEK_USB_CMD_IDX,
|
||||
data,
|
||||
size,
|
||||
&transferred,
|
||||
REALTEK_USB_TIMEOUT_MS
|
||||
);
|
||||
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
if (transferred != size)
|
||||
return U80211_DRV_STATUS_UNKNOWN_ERROR;
|
||||
|
||||
return U80211_DRV_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static int reg_read(u80211_drv_device_handle_t device, uint16_t reg, void *value, uint16_t size) {
|
||||
return reg_xfer(device, REALTEK_USB_READ, reg, value, size);
|
||||
}
|
||||
|
||||
int u80211_drv_rtl8188eu_reg_read8(u80211_drv_device_handle_t device, uint16_t reg, uint8_t *value) {
|
||||
return reg_read(device, reg, value, sizeof(*value));
|
||||
}
|
||||
|
||||
int u80211_drv_rtl8188eu_reg_read16(u80211_drv_device_handle_t device, uint16_t reg, uint16_t *value) {
|
||||
uint16_t little_endian_value;
|
||||
int status = reg_read(device, reg, &little_endian_value, sizeof(little_endian_value));
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
*value = u80211_drv_le_to_host16(little_endian_value);
|
||||
return U80211_DRV_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
int u80211_drv_rtl8188eu_reg_read32(u80211_drv_device_handle_t device, uint16_t reg, uint32_t *value) {
|
||||
uint32_t little_endian_value;
|
||||
int status = reg_read(device, reg, &little_endian_value, sizeof(little_endian_value));
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
*value = u80211_drv_le_to_host32(little_endian_value);
|
||||
return U80211_DRV_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
int u80211_drv_rtl8188eu_reg_write8(u80211_drv_device_handle_t device, uint16_t reg, uint8_t value) {
|
||||
return reg_xfer(device, REALTEK_USB_WRITE, reg, &value, sizeof(value));
|
||||
}
|
||||
|
||||
int u80211_drv_rtl8188eu_reg_write16(u80211_drv_device_handle_t device, uint16_t reg, uint16_t value) {
|
||||
uint16_t little_endian_value = u80211_drv_host_to_le16(value);
|
||||
return reg_xfer(device, REALTEK_USB_WRITE, reg, &little_endian_value, sizeof(little_endian_value));
|
||||
}
|
||||
|
||||
int u80211_drv_rtl8188eu_reg_write32(u80211_drv_device_handle_t device, uint16_t reg, uint32_t value) {
|
||||
uint32_t little_endian_value = u80211_drv_host_to_le32(value);
|
||||
return reg_xfer(device, REALTEK_USB_WRITE, reg, &little_endian_value, sizeof(little_endian_value));
|
||||
}
|
||||
|
||||
int u80211_drv_rtl8188eu_reg_write_region(u80211_drv_device_handle_t device, uint16_t reg, const void *data, uint16_t size) {
|
||||
return reg_xfer(device, REALTEK_USB_WRITE, reg, (void *)data, size);
|
||||
}
|
||||
@@ -0,0 +1,224 @@
|
||||
/* $OpenBSD: r92creg.h,v 1.31 2024/09/01 03:14:48 jsg Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2010 Damien Bergamini <damien.bergamini@free.fr>
|
||||
* Copyright (c) 2015 Stefan Sperling <stsp@openbsd.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <u80211_drv/rtl8188eu.h>
|
||||
#include <u80211_drv/status.h>
|
||||
|
||||
#define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0]))
|
||||
#define RTL8188EU_RF_TABLE_ENTRY_COUNT 95
|
||||
#define RTL8188EU_RF_READ_DELAY_US 1000
|
||||
|
||||
// Imported from OpenBSD sys/dev/ic/r92creg.h.
|
||||
static const uint16_t rtl8188eu_rf_regs[] = {
|
||||
0x00, 0x08, 0x18, 0x19, 0x1e, 0x1f, 0x2f, 0x3f, 0x42, 0x57, 0x58,
|
||||
0x67, 0x83, 0xb0, 0xb1, 0xb2, 0xb4, 0xb6, 0xb7, 0xb8, 0xb9, 0xba,
|
||||
0xbb, 0xbf, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca,
|
||||
0xdf, 0xef, 0x51, 0x52, 0x53, 0x56, 0x35, 0x35, 0x35, 0x36, 0x36,
|
||||
0x36, 0x36, 0xb6, 0x18, 0x5a, 0x19, 0x34, 0x34, 0x34, 0x34, 0x34,
|
||||
0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x00, 0x84, 0x86, 0x87, 0x8e,
|
||||
0x8f, 0xef, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b,
|
||||
0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0xef, 0x00, 0x18, 0xfe,
|
||||
0xfe, 0x1f, 0xfe, 0xfe, 0x1e, 0x1f, 0x00
|
||||
};
|
||||
|
||||
static const uint32_t rtl8188eu_rf_values[] = {
|
||||
0x30000, 0x84000, 0x00407, 0x00012, 0x80009, 0x00880, 0x1a060,
|
||||
0x00000, 0x060c0, 0xd0000, 0xbe180, 0x01552, 0x00000, 0xff8fc,
|
||||
0x54400, 0xccc19, 0x43003, 0x4953e, 0x1c718, 0x060ff, 0x80001,
|
||||
0x40000, 0x00400, 0xc0000, 0x02400, 0x00009, 0x40c91, 0x99999,
|
||||
0x000a3, 0x88820, 0x76c06, 0x00000, 0x80000, 0x00180, 0x001a0,
|
||||
0x6b27d, 0x7e49d, 0x00073, 0x51ff3, 0x00086, 0x00186, 0x00286,
|
||||
0x01c25, 0x09c25, 0x11c25, 0x19c25, 0x48538, 0x00c07, 0x4bd00,
|
||||
0x739d0, 0x0adf3, 0x09df0, 0x08ded, 0x07dea, 0x06de7, 0x054ee,
|
||||
0x044eb, 0x034e8, 0x0246b, 0x01468, 0x0006d, 0x30159, 0x68200,
|
||||
0x000ce, 0x48a00, 0x65540, 0x88000, 0x020a0, 0xf02b0, 0xef7b0,
|
||||
0xd4fb0, 0xcf060, 0xb0090, 0xa0080, 0x90080, 0x8f780, 0x722b0,
|
||||
0x6f7b0, 0x54fb0, 0x4f060, 0x30090, 0x20080, 0x10080, 0x0f780,
|
||||
0x000a0, 0x10159, 0x0f407, 0x00000, 0x00000, 0x80003, 0x00000,
|
||||
0x00000, 0x00001, 0x80000, 0x33e60
|
||||
};
|
||||
|
||||
int u80211_drv_rtl8188eu_rf_write(u80211_drv_device_handle_t device, uint8_t rf_reg, uint32_t value) {
|
||||
uint32_t command = ((uint32_t)rf_reg << U80211_DRV_RTL8188EU_REG_LSSI_PARAM_ADDRESS_SHIFT) | (value & U80211_DRV_RTL8188EU_REG_LSSI_PARAM_DATA_MASK);
|
||||
int status = u80211_drv_rtl8188eu_reg_write32(device, U80211_DRV_RTL8188EU_REG_LSSI_PARAM_A, command);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
u80211_drv_kernel_stall_us(1);
|
||||
return U80211_DRV_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
int u80211_drv_rtl8188eu_rf_read(u80211_drv_device_handle_t device, uint8_t rf_reg, uint32_t *value) {
|
||||
// drop read edge
|
||||
uint32_t hssi_param2;
|
||||
int status = u80211_drv_rtl8188eu_reg_read32(device, U80211_DRV_RTL8188EU_REG_HSSI_PARAM2_A, &hssi_param2);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
status = u80211_drv_rtl8188eu_reg_write32(device, U80211_DRV_RTL8188EU_REG_HSSI_PARAM2_A, hssi_param2 & ~U80211_DRV_RTL8188EU_REG_HSSI_PARAM2_READ_EDGE);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
u80211_drv_kernel_stall_us(RTL8188EU_RF_READ_DELAY_US);
|
||||
|
||||
// program rf register address and raise read edge
|
||||
uint32_t command = hssi_param2 & ~U80211_DRV_RTL8188EU_REG_HSSI_PARAM2_READ_ADDRESS_MASK;
|
||||
command |= ((uint32_t)rf_reg << U80211_DRV_RTL8188EU_REG_HSSI_PARAM2_READ_ADDRESS_SHIFT) & U80211_DRV_RTL8188EU_REG_HSSI_PARAM2_READ_ADDRESS_MASK;
|
||||
command |= U80211_DRV_RTL8188EU_REG_HSSI_PARAM2_READ_EDGE;
|
||||
status = u80211_drv_rtl8188eu_reg_write32(device, U80211_DRV_RTL8188EU_REG_HSSI_PARAM2_A, command);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
u80211_drv_kernel_stall_us(RTL8188EU_RF_READ_DELAY_US);
|
||||
|
||||
// read back
|
||||
uint32_t hssi_param1;
|
||||
status = u80211_drv_rtl8188eu_reg_read32(device, U80211_DRV_RTL8188EU_REG_HSSI_PARAM1_A, &hssi_param1);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
uint16_t readback_reg = (hssi_param1 & U80211_DRV_RTL8188EU_REG_HSSI_PARAM1_PI) != 0 ? U80211_DRV_RTL8188EU_REG_HSPI_READBACK_A : U80211_DRV_RTL8188EU_REG_LSSI_READBACK_A;
|
||||
uint32_t readback;
|
||||
status = u80211_drv_rtl8188eu_reg_read32(device, readback_reg, &readback);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
*value = readback & U80211_DRV_RTL8188EU_RF_READBACK_MASK;
|
||||
return U80211_DRV_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static int rtl8188eu_rf_prepare(u80211_drv_device_handle_t device) {
|
||||
uint32_t value;
|
||||
int status = u80211_drv_rtl8188eu_reg_read32(device, U80211_DRV_RTL8188EU_REG_RF_IFACE_OE_A, &value);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
value |= U80211_DRV_RTL8188EU_REG_RF_IFACE_OE_RF_ENV_ENABLE;
|
||||
status = u80211_drv_rtl8188eu_reg_write32(device, U80211_DRV_RTL8188EU_REG_RF_IFACE_OE_A, value);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
u80211_drv_kernel_stall_us(50);
|
||||
|
||||
status = u80211_drv_rtl8188eu_reg_read32(device, U80211_DRV_RTL8188EU_REG_RF_IFACE_OE_A, &value);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
value |= U80211_DRV_RTL8188EU_REG_RF_IFACE_OE_RF_ENV;
|
||||
status = u80211_drv_rtl8188eu_reg_write32(device, U80211_DRV_RTL8188EU_REG_RF_IFACE_OE_A, value);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
u80211_drv_kernel_stall_us(50);
|
||||
|
||||
status = u80211_drv_rtl8188eu_reg_read32(device, U80211_DRV_RTL8188EU_REG_HSSI_PARAM2_A, &value);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
value &= ~U80211_DRV_RTL8188EU_REG_HSSI_PARAM2_ADDRESS_LENGTH;
|
||||
status = u80211_drv_rtl8188eu_reg_write32(device, U80211_DRV_RTL8188EU_REG_HSSI_PARAM2_A, value);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
u80211_drv_kernel_stall_us(50);
|
||||
|
||||
status = u80211_drv_rtl8188eu_reg_read32(device, U80211_DRV_RTL8188EU_REG_HSSI_PARAM2_A, &value);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
value &= ~U80211_DRV_RTL8188EU_REG_HSSI_PARAM2_DATA_LENGTH;
|
||||
status = u80211_drv_rtl8188eu_reg_write32(device, U80211_DRV_RTL8188EU_REG_HSSI_PARAM2_A, value);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
u80211_drv_kernel_stall_us(50);
|
||||
|
||||
return U80211_DRV_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static int rtl8188eu_rf_apply_table(u80211_drv_device_handle_t device) {
|
||||
for (size_t i = 0; i < ARRAY_SIZE(rtl8188eu_rf_regs); ++i) {
|
||||
uint16_t reg = rtl8188eu_rf_regs[i];
|
||||
uint32_t delay;
|
||||
|
||||
switch (reg) {
|
||||
case 0xfe:
|
||||
case 0xffe:
|
||||
delay = 50000;
|
||||
break;
|
||||
case 0xfd:
|
||||
delay = 5000;
|
||||
break;
|
||||
case 0xfc:
|
||||
delay = 1000;
|
||||
break;
|
||||
case 0xfb:
|
||||
delay = 50;
|
||||
break;
|
||||
case 0xfa:
|
||||
delay = 5;
|
||||
break;
|
||||
case 0xf9:
|
||||
delay = 1;
|
||||
break;
|
||||
default: {
|
||||
int status = u80211_drv_rtl8188eu_rf_write(device, (uint8_t)reg, rtl8188eu_rf_values[i]);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS) {
|
||||
u80211_drv_kernel_print(U80211_DRV_KERNEL_PRINT_LEVEL_ERROR, "rtl8188eu: RF table write failed");
|
||||
return status;
|
||||
}
|
||||
|
||||
u80211_drv_kernel_stall_us(5);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
u80211_drv_kernel_stall_us(delay);
|
||||
}
|
||||
|
||||
return U80211_DRV_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static int rtl8188eu_rf_restore_env(u80211_drv_device_handle_t device, uint32_t saved_rf_env) {
|
||||
uint32_t value;
|
||||
int status = u80211_drv_rtl8188eu_reg_read32(device, U80211_DRV_RTL8188EU_REG_RF_IFACE_SW_A, &value);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
value &= ~U80211_DRV_RTL8188EU_REG_RF_IFACE_SW_RF_ENV;
|
||||
value |= saved_rf_env;
|
||||
return u80211_drv_rtl8188eu_reg_write32(device, U80211_DRV_RTL8188EU_REG_RF_IFACE_SW_A, value);
|
||||
}
|
||||
|
||||
int u80211_drv_rtl8188eu_rf_load_table(u80211_drv_device_handle_t device) {
|
||||
uint32_t value;
|
||||
int status = u80211_drv_rtl8188eu_reg_read32(device, U80211_DRV_RTL8188EU_REG_RF_IFACE_SW_A, &value);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
uint32_t saved_rf_env = value & U80211_DRV_RTL8188EU_REG_RF_IFACE_SW_RF_ENV;
|
||||
status = rtl8188eu_rf_prepare(device);
|
||||
if (status == U80211_DRV_STATUS_SUCCESS)
|
||||
status = rtl8188eu_rf_apply_table(device);
|
||||
|
||||
int restore_status = rtl8188eu_rf_restore_env(device, saved_rf_env);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
return restore_status;
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <u80211_drv/80211.h>
|
||||
#include <u80211_drv/kernel_interface.h>
|
||||
#include <u80211_drv/rtl8188eu.h>
|
||||
#include <u80211_drv/status.h>
|
||||
#include <u80211_drv/util.h>
|
||||
|
||||
#define RTL8188EU_RX_DESCRIPTOR_SIZE 24
|
||||
#define RTL8188EU_RX_BUFFER_SIZE (16 * 1024)
|
||||
|
||||
#define RTL8188EU_RX_DESCRIPTOR_PACKET_LENGTH_MASK 0x00003fff
|
||||
#define RTL8188EU_RX_DESCRIPTOR_CRC_ERROR (1u << 14)
|
||||
#define RTL8188EU_RX_DESCRIPTOR_ICV_ERROR (1u << 15)
|
||||
#define RTL8188EU_RX_DESCRIPTOR_DRVINFO_SIZE_MASK 0x000f0000
|
||||
#define RTL8188EU_RX_DESCRIPTOR_DRVINFO_SIZE_SHIFT 16
|
||||
#define RTL8188EU_RX_DESCRIPTOR_SECURITY_MASK 0x00700000
|
||||
#define RTL8188EU_RX_DESCRIPTOR_SECURITY_SHIFT 20
|
||||
#define RTL8188EU_RX_DESCRIPTOR_SECURITY_NONE 0
|
||||
#define RTL8188EU_RX_DESCRIPTOR_SECURITY_TKIP 2
|
||||
#define RTL8188EU_RX_DESCRIPTOR_SECURITY_AES 4
|
||||
#define RTL8188EU_RX_DESCRIPTOR_SHIFT_MASK 0x03000000
|
||||
#define RTL8188EU_RX_DESCRIPTOR_SHIFT_SHIFT 24
|
||||
#define RTL8188EU_RX_DESCRIPTOR_SOFTWARE_DECRYPTED (1u << 27)
|
||||
#define RTL8188EU_RX_DESCRIPTOR_REPORT_SELECT_MASK 0x0000c000
|
||||
|
||||
#define RTL8188EU_RX_DESCRIPTOR_PACKET_COUNT_MASK 0x00ff0000
|
||||
#define RTL8188EU_RX_DESCRIPTOR_PACKET_COUNT_SHIFT 16
|
||||
#define RTL8188EU_RX_PACKET_ALIGNMENT 128
|
||||
|
||||
static size_t align_rx_packet(size_t size) {
|
||||
return (size + RTL8188EU_RX_PACKET_ALIGNMENT - 1) & ~(RTL8188EU_RX_PACKET_ALIGNMENT - 1);
|
||||
}
|
||||
|
||||
static void process_rx_buffer(u80211_drv_rtl8188eu_t *rtl8188eu, void *buffer, size_t size) {
|
||||
uint8_t *bytes = buffer;
|
||||
size_t offset = 0;
|
||||
size_t packet_count = 0;
|
||||
|
||||
while (size - offset >= RTL8188EU_RX_DESCRIPTOR_SIZE) {
|
||||
uint8_t *descriptor = bytes + offset;
|
||||
uint32_t descriptor0 = u80211_drv_deserialize_le32(descriptor);
|
||||
uint32_t descriptor2 = u80211_drv_deserialize_le32(descriptor + 8);
|
||||
uint32_t descriptor3 = u80211_drv_deserialize_le32(descriptor + 12);
|
||||
// there might be many packets in one transfer.
|
||||
if (packet_count == 0) {
|
||||
packet_count = (descriptor2 & RTL8188EU_RX_DESCRIPTOR_PACKET_COUNT_MASK) >> RTL8188EU_RX_DESCRIPTOR_PACKET_COUNT_SHIFT;
|
||||
if (packet_count == 0)
|
||||
packet_count = 1;
|
||||
}
|
||||
|
||||
// TODO: extract channel?
|
||||
// TODO: extract rate?
|
||||
// TODO: get dbm info from phy data?
|
||||
bool is_rx_record = (descriptor3 & RTL8188EU_RX_DESCRIPTOR_REPORT_SELECT_MASK) == 0;
|
||||
bool crc_icv_error = descriptor0 & (RTL8188EU_RX_DESCRIPTOR_CRC_ERROR | RTL8188EU_RX_DESCRIPTOR_ICV_ERROR);
|
||||
uint32_t security = (descriptor0 & RTL8188EU_RX_DESCRIPTOR_SECURITY_MASK) >> RTL8188EU_RX_DESCRIPTOR_SECURITY_SHIFT;
|
||||
bool software_decryption_required = (descriptor0 & RTL8188EU_RX_DESCRIPTOR_SOFTWARE_DECRYPTED) != 0;
|
||||
size_t packet_size = descriptor0 & RTL8188EU_RX_DESCRIPTOR_PACKET_LENGTH_MASK;
|
||||
size_t drvinfo_size = ((descriptor0 & RTL8188EU_RX_DESCRIPTOR_DRVINFO_SIZE_MASK) >> RTL8188EU_RX_DESCRIPTOR_DRVINFO_SIZE_SHIFT) * 8;
|
||||
size_t descriptor_shift = (descriptor0 & RTL8188EU_RX_DESCRIPTOR_SHIFT_MASK) >> RTL8188EU_RX_DESCRIPTOR_SHIFT_SHIFT;
|
||||
size_t packet_offset = RTL8188EU_RX_DESCRIPTOR_SIZE + drvinfo_size + descriptor_shift;
|
||||
size_t remaining = size - offset;
|
||||
|
||||
if (packet_offset > remaining || packet_size > remaining - packet_offset)
|
||||
break;
|
||||
|
||||
if (packet_size != 0 && packet_size <= U80211_DRV_80211_MAX_MPDU_SIZE && !crc_icv_error && is_rx_record) {
|
||||
uint8_t *packet = descriptor + packet_offset;
|
||||
// u80211 consumes the cipher header and trailer after hardware has decrypted the payload.
|
||||
if (security != RTL8188EU_RX_DESCRIPTOR_SECURITY_NONE &&
|
||||
((security != RTL8188EU_RX_DESCRIPTOR_SECURITY_TKIP && security != RTL8188EU_RX_DESCRIPTOR_SECURITY_AES) ||
|
||||
software_decryption_required))
|
||||
goto next_packet;
|
||||
|
||||
u80211_drv_network_device_handle_t network_device = __atomic_load_n(&rtl8188eu->network_device, __ATOMIC_ACQUIRE);
|
||||
if (network_device != NULL)
|
||||
u80211_drv_packet_received(network_device, packet, packet_size);
|
||||
}
|
||||
|
||||
next_packet:
|
||||
--packet_count;
|
||||
if (packet_count == 0)
|
||||
break;
|
||||
|
||||
size_t unit_size = align_rx_packet(packet_offset + packet_size);
|
||||
if (unit_size > remaining)
|
||||
break;
|
||||
offset += unit_size;
|
||||
}
|
||||
}
|
||||
|
||||
static void rx_complete(void *context, int status, size_t transferred_size) {
|
||||
u80211_drv_rtl8188eu_rx_slot_t *slot = context;
|
||||
u80211_drv_rtl8188eu_t *rtl8188eu = slot->rtl8188eu;
|
||||
|
||||
if (status == U80211_DRV_STATUS_SUCCESS)
|
||||
process_rx_buffer(rtl8188eu, slot->buffer, transferred_size);
|
||||
|
||||
status = u80211_drv_kernel_submit_xfer(slot->transfer);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
u80211_drv_kernel_print(U80211_DRV_KERNEL_PRINT_LEVEL_WARN, "rtl8188eu: failed to resubmit RX transfer");
|
||||
}
|
||||
|
||||
int u80211_drv_rtl8188eu_rx_start(u80211_drv_rtl8188eu_t *rtl8188eu) {
|
||||
for (size_t i = 0; i < U80211_DRV_RTL8188EU_RX_SLOT_COUNT; ++i) {
|
||||
u80211_drv_rtl8188eu_rx_slot_t *slot = &rtl8188eu->rx_slots[i];
|
||||
slot->rtl8188eu = rtl8188eu;
|
||||
slot->buffer = NULL;
|
||||
slot->transfer = NULL;
|
||||
}
|
||||
|
||||
int status = U80211_DRV_STATUS_SUCCESS;
|
||||
for (size_t i = 0; i < U80211_DRV_RTL8188EU_RX_SLOT_COUNT; ++i) {
|
||||
u80211_drv_rtl8188eu_rx_slot_t *slot = &rtl8188eu->rx_slots[i];
|
||||
slot->buffer = u80211_drv_kernel_allocate(RTL8188EU_RX_BUFFER_SIZE);
|
||||
if (slot->buffer == NULL) {
|
||||
status = U80211_DRV_STATUS_OUT_OF_MEMORY;
|
||||
break;
|
||||
}
|
||||
|
||||
status = u80211_drv_kernel_allocate_bulk_xfer(
|
||||
rtl8188eu->device,
|
||||
rtl8188eu->rx_endpoint,
|
||||
slot->buffer,
|
||||
RTL8188EU_RX_BUFFER_SIZE,
|
||||
rx_complete,
|
||||
slot,
|
||||
&slot->transfer
|
||||
);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
break;
|
||||
|
||||
status = u80211_drv_kernel_submit_xfer(slot->transfer);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
break;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <u80211_drv/80211.h>
|
||||
#include <u80211_drv/kernel_interface.h>
|
||||
#include <u80211_drv/rtl8188eu.h>
|
||||
#include <u80211_drv/status.h>
|
||||
#include <u80211_drv/string.h>
|
||||
#include <u80211_drv/util.h>
|
||||
|
||||
#define RTL8188EU_TX_DESCRIPTOR_SIZE 32
|
||||
#define RTL8188EU_TX_TIMEOUT_MS 1000
|
||||
|
||||
#define RTL8188EU_TX_QUEUE_BEST_EFFORT 0x00
|
||||
#define RTL8188EU_TX_QUEUE_MANAGEMENT 0x12
|
||||
#define RTL8188EU_TX_RAID_11BG 4
|
||||
#define RTL8188EU_TX_RAID_11B 6
|
||||
#define RTL8188EU_TX_RAID_SHIFT 16
|
||||
#define RTL8188EU_TX_DESCRIPTOR_MACID_MASK 0x1f
|
||||
#define RTL8188EU_TX_DESCRIPTOR_ENABLE_DESCRIPTOR_ID (1u << 21)
|
||||
#define RTL8188EU_TX_DESCRIPTOR_SECURITY_TKIP 0x00400000
|
||||
#define RTL8188EU_TX_DESCRIPTOR_SECURITY_AES 0x00c00000
|
||||
|
||||
#define RTL8188EU_TX_DESCRIPTOR_OWN (1u << 7)
|
||||
#define RTL8188EU_TX_DESCRIPTOR_FIRST_SEGMENT (1u << 3)
|
||||
#define RTL8188EU_TX_DESCRIPTOR_LAST_SEGMENT (1u << 2)
|
||||
#define RTL8188EU_TX_DESCRIPTOR_BROADCAST_MULTICAST (1u << 0)
|
||||
#define RTL8188EU_TX_DESCRIPTOR_AGGREGATION_BREAK (1u << 16)
|
||||
#define RTL8188EU_TX_DESCRIPTOR_ANTENNA_A (1u << 24)
|
||||
#define RTL8188EU_TX_DESCRIPTOR_ANTENNA_B (1u << 25)
|
||||
#define RTL8188EU_TX_DESCRIPTOR_ANTENNA_C (1u << 29)
|
||||
#define RTL8188EU_TX_DESCRIPTOR_USE_DRIVER_RATE (1u << 8)
|
||||
#define RTL8188EU_TX_DESCRIPTOR_DATA_FALLBACK_LIMITS 0x0001ff00
|
||||
#define RTL8188EU_TX_DESCRIPTOR_RETRY_LIMIT_ENABLE (1u << 17)
|
||||
#define RTL8188EU_TX_DESCRIPTOR_RETRY_LIMIT_SHIFT 18
|
||||
#define RTL8188EU_TX_MANAGEMENT_RETRY_LIMIT 6
|
||||
|
||||
static uint8_t data_endpoint(const u80211_drv_rtl8188eu_t *rtl8188eu) {
|
||||
if (rtl8188eu->bulk_out_endpoint_count >= 3)
|
||||
return rtl8188eu->tx_endpoint_low;
|
||||
|
||||
if (rtl8188eu->bulk_out_endpoint_count == 2)
|
||||
return rtl8188eu->tx_endpoint_normal;
|
||||
|
||||
return rtl8188eu->tx_endpoint_high;
|
||||
}
|
||||
|
||||
static void calculate_checksum(uint8_t *descriptor) {
|
||||
uint16_t checksum = 0;
|
||||
for (size_t i = 0; i < RTL8188EU_TX_DESCRIPTOR_SIZE; i += sizeof(uint16_t))
|
||||
checksum ^= u80211_drv_deserialize_le16(descriptor + i);
|
||||
|
||||
u80211_drv_serialize_le16(descriptor + 28, checksum);
|
||||
}
|
||||
|
||||
static int build_descriptor(const uint8_t *frame, size_t frame_size, const u80211_drv_transmit_options_t *options, uint8_t *descriptor, uint8_t *queue_out) {
|
||||
int key_index = options == NULL ? -1 : options->key;
|
||||
int cipher = options == NULL ? U80211_DRV_CIPHER_NONE : options->cipher;
|
||||
|
||||
if (frame_size < U80211_DRV_80211_HEADER_MINIMUM_SIZE)
|
||||
return U80211_DRV_STATUS_MALFORMED_PACKET;
|
||||
|
||||
if (key_index < -1 || key_index > U80211_DRV_RTL8188EU_CAM_CTL0_KEY_ID_MASK)
|
||||
return U80211_DRV_STATUS_INVALID_ARGUMENT;
|
||||
|
||||
if ((key_index < 0) != (cipher == U80211_DRV_CIPHER_NONE))
|
||||
return U80211_DRV_STATUS_INVALID_ARGUMENT;
|
||||
|
||||
uint16_t frame_control = u80211_drv_80211_frame_control(frame);
|
||||
uint8_t frame_type = u80211_drv_80211_frame_type(frame_control);
|
||||
uint8_t frame_subtype = u80211_drv_80211_frame_subtype(frame_control);
|
||||
uint8_t queue;
|
||||
|
||||
if (frame_type == U80211_DRV_80211_FRAME_TYPE_MANAGEMENT)
|
||||
queue = RTL8188EU_TX_QUEUE_MANAGEMENT;
|
||||
else if (frame_type == U80211_DRV_80211_FRAME_TYPE_DATA && (frame_subtype & U80211_DRV_80211_FRAME_DATA_QOS_BIT) == 0)
|
||||
queue = RTL8188EU_TX_QUEUE_BEST_EFFORT;
|
||||
else
|
||||
return U80211_DRV_STATUS_NOT_SUPPORTED;
|
||||
|
||||
u80211_drv_memset(descriptor, 0, RTL8188EU_TX_DESCRIPTOR_SIZE);
|
||||
u80211_drv_serialize_le16(descriptor, (uint16_t)frame_size); // packet size
|
||||
descriptor[2] = RTL8188EU_TX_DESCRIPTOR_SIZE; // packet start offset
|
||||
// once HT is implemented, that stuff would go here.
|
||||
descriptor[3] = RTL8188EU_TX_DESCRIPTOR_OWN | RTL8188EU_TX_DESCRIPTOR_FIRST_SEGMENT | RTL8188EU_TX_DESCRIPTOR_LAST_SEGMENT;
|
||||
|
||||
const uint8_t *receiver = u80211_drv_80211_receiver_address(frame);
|
||||
bool group_addressed = (receiver[0] & 1u) != 0;
|
||||
if (group_addressed)
|
||||
descriptor[3] |= RTL8188EU_TX_DESCRIPTOR_BROADCAST_MULTICAST;
|
||||
|
||||
// use the mixed 11b/g table for unicast data and the basic 11b table for management and group-addressed traffic.
|
||||
uint8_t raid = frame_type == U80211_DRV_80211_FRAME_TYPE_DATA && !group_addressed ? RTL8188EU_TX_RAID_11BG : RTL8188EU_TX_RAID_11B;
|
||||
uint32_t descriptor1 = ((uint32_t)queue << 8) | ((uint32_t)raid << RTL8188EU_TX_RAID_SHIFT);
|
||||
if (key_index >= 0) {
|
||||
// use hardware encryption. set the selected cipher bit
|
||||
if (frame_type != U80211_DRV_80211_FRAME_TYPE_DATA)
|
||||
return U80211_DRV_STATUS_INVALID_ARGUMENT;
|
||||
|
||||
if (cipher == U80211_DRV_CIPHER_CCMP)
|
||||
descriptor1 |= RTL8188EU_TX_DESCRIPTOR_SECURITY_AES;
|
||||
else if (cipher == U80211_DRV_CIPHER_TKIP)
|
||||
descriptor1 |= RTL8188EU_TX_DESCRIPTOR_SECURITY_TKIP;
|
||||
else
|
||||
return U80211_DRV_STATUS_NOT_SUPPORTED;
|
||||
|
||||
if (group_addressed) {
|
||||
descriptor1 |= RTL8188EU_TX_DESCRIPTOR_ENABLE_DESCRIPTOR_ID;
|
||||
descriptor1 |= (uint32_t)key_index & RTL8188EU_TX_DESCRIPTOR_MACID_MASK;
|
||||
}
|
||||
}
|
||||
u80211_drv_serialize_le32(descriptor + 4, descriptor1);
|
||||
|
||||
// do not combine packets and use a known working setting for antennas (0b111, last bit is set in dword 7)
|
||||
u80211_drv_serialize_le32(descriptor + 8, RTL8188EU_TX_DESCRIPTOR_AGGREGATION_BREAK | RTL8188EU_TX_DESCRIPTOR_ANTENNA_A | RTL8188EU_TX_DESCRIPTOR_ANTENNA_B);
|
||||
|
||||
uint16_t sequence_number = u80211_drv_80211_sequence_number(frame);
|
||||
u80211_drv_serialize_le32(descriptor + 12, (uint32_t)sequence_number << 16);
|
||||
|
||||
// this controls a lot of things about how packets are transmitted.
|
||||
// at the moment, we only care about telling it to use our selected rate.
|
||||
// this is a list of things of interest that would be handled here:
|
||||
// qos, hw sequence control, short preamble, different bandwidth stuff
|
||||
u80211_drv_serialize_le32(descriptor + 16, RTL8188EU_TX_DESCRIPTOR_USE_DRIVER_RATE);
|
||||
|
||||
if (frame_type == U80211_DRV_80211_FRAME_TYPE_MANAGEMENT) {
|
||||
// six hardware retries @ 1mbps for management packets
|
||||
u80211_drv_serialize_le32(descriptor + 20, RTL8188EU_TX_DESCRIPTOR_RETRY_LIMIT_ENABLE | (RTL8188EU_TX_MANAGEMENT_RETRY_LIMIT << RTL8188EU_TX_DESCRIPTOR_RETRY_LIMIT_SHIFT));
|
||||
} else {
|
||||
// TODO: encode our selected rate here as well once selection is implemented
|
||||
// enable rate fallback for data packets (31 data retries + 15 request-to-send retries)
|
||||
u80211_drv_serialize_le32(descriptor + 20, RTL8188EU_TX_DESCRIPTOR_DATA_FALLBACK_LIMITS);
|
||||
}
|
||||
|
||||
// dw6 would contain things related to usb aggregation. that is completely disabled in this driver, so it is skipped over.
|
||||
|
||||
// dw7 has a checksum and the last bit of the antenna selector
|
||||
u80211_drv_serialize_le32(descriptor + 28, RTL8188EU_TX_DESCRIPTOR_ANTENNA_C);
|
||||
calculate_checksum(descriptor);
|
||||
*queue_out = queue;
|
||||
return U80211_DRV_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
int u80211_drv_rtl8188eu_tx_buffer_allocate(size_t size, void **buffer) {
|
||||
if (size > U80211_DRV_80211_MAX_MPDU_SIZE)
|
||||
return U80211_DRV_STATUS_INVALID_ARGUMENT;
|
||||
|
||||
size_t allocation_size = size + RTL8188EU_TX_DESCRIPTOR_SIZE;
|
||||
void *data = u80211_drv_kernel_allocate(allocation_size);
|
||||
if (data == NULL)
|
||||
return U80211_DRV_STATUS_OUT_OF_MEMORY;
|
||||
|
||||
*buffer = (uint8_t *)data + RTL8188EU_TX_DESCRIPTOR_SIZE;
|
||||
return U80211_DRV_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
void u80211_drv_rtl8188eu_tx_buffer_free(void *buffer) {
|
||||
u80211_drv_kernel_free((uint8_t *)buffer - RTL8188EU_TX_DESCRIPTOR_SIZE);
|
||||
}
|
||||
|
||||
int u80211_drv_rtl8188eu_transmit(u80211_drv_rtl8188eu_t *rtl8188eu, void *buffer, size_t size, size_t current_offset, const u80211_drv_transmit_options_t *options) {
|
||||
uint8_t *frame = (uint8_t *)buffer + current_offset;
|
||||
size_t frame_size = size - current_offset;
|
||||
uint8_t *descriptor = frame - RTL8188EU_TX_DESCRIPTOR_SIZE;
|
||||
|
||||
uint8_t queue;
|
||||
int status = build_descriptor(frame, frame_size, options, descriptor, &queue);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
goto cleanup;
|
||||
|
||||
uint8_t endpoint = queue == RTL8188EU_TX_QUEUE_MANAGEMENT ? rtl8188eu->tx_endpoint_high : data_endpoint(rtl8188eu);
|
||||
size_t transfer_size = RTL8188EU_TX_DESCRIPTOR_SIZE + frame_size;
|
||||
size_t transferred_size = 0;
|
||||
status = u80211_drv_kernel_submit_bulk_xfer_and_wait(
|
||||
rtl8188eu->device,
|
||||
endpoint,
|
||||
descriptor,
|
||||
transfer_size,
|
||||
&transferred_size,
|
||||
RTL8188EU_TX_TIMEOUT_MS
|
||||
);
|
||||
|
||||
if (status == U80211_DRV_STATUS_SUCCESS && transferred_size != transfer_size)
|
||||
status = U80211_DRV_STATUS_UNKNOWN_ERROR;
|
||||
|
||||
cleanup:
|
||||
u80211_drv_rtl8188eu_tx_buffer_free(buffer);
|
||||
return status;
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
/*-
|
||||
* Copyright (c) 2010 Damien Bergamini <damien.bergamini@free.fr>
|
||||
* Copyright (c) 2015 Stefan Sperling <stsp@openbsd.org>
|
||||
* Copyright (c) 2016 Nathanial Sloss <nathanialsloss@yahoo.com.au>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <u80211_drv/rtl8188eu.h>
|
||||
#include <u80211_drv/status.h>
|
||||
|
||||
#define RTL8188EU_MAX_TX_POWER 0x3f
|
||||
|
||||
typedef struct {
|
||||
uint8_t cck1;
|
||||
uint8_t cck2;
|
||||
uint8_t cck55;
|
||||
uint8_t cck11;
|
||||
uint8_t ofdm;
|
||||
uint8_t mcs;
|
||||
} rtl8188eu_tx_power_t;
|
||||
|
||||
static unsigned int rtl8188eu_channel_group(uint8_t channel) {
|
||||
if (channel <= 2)
|
||||
return 0;
|
||||
|
||||
if (channel <= 5)
|
||||
return 1;
|
||||
|
||||
if (channel <= 8)
|
||||
return 2;
|
||||
|
||||
if (channel <= 11)
|
||||
return 3;
|
||||
|
||||
if (channel <= 13)
|
||||
return 4;
|
||||
|
||||
return 5;
|
||||
}
|
||||
|
||||
static int rtl8188eu_sign_extend_4(uint8_t value) {
|
||||
value &= 0x0f;
|
||||
return (value & 0x08) != 0 ? (int)value - 16 : value;
|
||||
}
|
||||
|
||||
static uint8_t rtl8188eu_clamp_tx_power(int value) {
|
||||
if (value < 0)
|
||||
return 0;
|
||||
|
||||
if (value > RTL8188EU_MAX_TX_POWER)
|
||||
return RTL8188EU_MAX_TX_POWER;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static rtl8188eu_tx_power_t rtl8188eu_compute_tx_power(const u80211_drv_rtl8188eu_efuse_t *efuse, uint8_t channel) {
|
||||
unsigned int group = rtl8188eu_channel_group(channel);
|
||||
int cck = efuse->cck_tx_power_base_indexes[group];
|
||||
unsigned int ht_group = group == 5 ? 4 : group;
|
||||
int ht = efuse->ht40_1s_tx_power_base_indexes[ht_group];
|
||||
int ofdm = ht + rtl8188eu_sign_extend_4(efuse->ht20_ofdm_tx_power_diff);
|
||||
int mcs = ht + rtl8188eu_sign_extend_4(efuse->ht20_ofdm_tx_power_diff >> 4);
|
||||
|
||||
return (rtl8188eu_tx_power_t) {
|
||||
.cck1 = rtl8188eu_clamp_tx_power(cck),
|
||||
.cck2 = rtl8188eu_clamp_tx_power(cck - 9),
|
||||
.cck55 = rtl8188eu_clamp_tx_power(cck),
|
||||
.cck11 = rtl8188eu_clamp_tx_power(cck),
|
||||
.ofdm = rtl8188eu_clamp_tx_power(ofdm),
|
||||
.mcs = rtl8188eu_clamp_tx_power(mcs),
|
||||
};
|
||||
}
|
||||
|
||||
static uint32_t rtl8188eu_repeat_power(uint8_t power) {
|
||||
return (uint32_t)power * UINT32_C(0x01010101);
|
||||
}
|
||||
|
||||
int u80211_drv_rtl8188eu_set_tx_power(u80211_drv_rtl8188eu_t *rtl8188eu, uint8_t channel) {
|
||||
if (channel < 1 || channel > 14)
|
||||
return U80211_DRV_STATUS_INVALID_ARGUMENT;
|
||||
|
||||
rtl8188eu_tx_power_t power = rtl8188eu_compute_tx_power(&rtl8188eu->efuse, channel);
|
||||
uint32_t value;
|
||||
int status = u80211_drv_rtl8188eu_reg_read32(rtl8188eu->device, U80211_DRV_RTL8188EU_REG_TXAGC_A_CCK1, &value);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
value &= ~U80211_DRV_RTL8188EU_REG_TXAGC_A_CCK1_MASK;
|
||||
value |= (uint32_t)power.cck1 << 8;
|
||||
status = u80211_drv_rtl8188eu_reg_write32(rtl8188eu->device, U80211_DRV_RTL8188EU_REG_TXAGC_A_CCK1, value);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
status = u80211_drv_rtl8188eu_reg_read32(rtl8188eu->device, U80211_DRV_RTL8188EU_REG_TXAGC_B_CCK11_A_CCK2_11, &value);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
value &= UINT32_C(0x000000ff);
|
||||
value |= (uint32_t)power.cck2 << 8;
|
||||
value |= (uint32_t)power.cck55 << 16;
|
||||
value |= (uint32_t)power.cck11 << 24;
|
||||
status = u80211_drv_rtl8188eu_reg_write32(rtl8188eu->device, U80211_DRV_RTL8188EU_REG_TXAGC_B_CCK11_A_CCK2_11, value);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
uint32_t ofdm = rtl8188eu_repeat_power(power.ofdm);
|
||||
status = u80211_drv_rtl8188eu_reg_write32(rtl8188eu->device, U80211_DRV_RTL8188EU_REG_TXAGC_A_RATE18_06, ofdm);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
status = u80211_drv_rtl8188eu_reg_write32(rtl8188eu->device, U80211_DRV_RTL8188EU_REG_TXAGC_A_RATE54_24, ofdm);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
uint32_t mcs = rtl8188eu_repeat_power(power.mcs);
|
||||
status = u80211_drv_rtl8188eu_reg_write32(rtl8188eu->device, U80211_DRV_RTL8188EU_REG_TXAGC_A_MCS03, mcs);
|
||||
if (status != U80211_DRV_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
return u80211_drv_rtl8188eu_reg_write32(rtl8188eu->device, U80211_DRV_RTL8188EU_REG_TXAGC_A_MCS07, mcs);
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <u80211_drv/string.h>
|
||||
|
||||
void *u80211_drv_memcpy(void *destination, const void *source, size_t size) {
|
||||
uint8_t *destination_bytes = destination;
|
||||
const uint8_t *source_bytes = source;
|
||||
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
destination_bytes[i] = source_bytes[i];
|
||||
|
||||
return destination;
|
||||
}
|
||||
|
||||
void *u80211_drv_memset(void *destination, int value, size_t size) {
|
||||
uint8_t *destination_bytes = destination;
|
||||
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
destination_bytes[i] = (uint8_t)value;
|
||||
|
||||
return destination;
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
#include <u80211_drv/u80211_drv.h>
|
||||
#include <u80211_drv/status.h>
|
||||
#include <u80211_drv/kernel_interface.h>
|
||||
#include <u80211_drv/drv_init.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef int (*u80211_drv_initialize_fn_t)(u80211_drv_device_handle_t device, u80211_drv_interface_handle_t interface);
|
||||
|
||||
typedef struct {
|
||||
uint32_t id;
|
||||
uint32_t interface;
|
||||
u80211_drv_initialize_fn_t fn;
|
||||
const char *name;
|
||||
} u80211_drv_match_table_entry_t;
|
||||
|
||||
static const u80211_drv_match_table_entry_t match_table[] = {
|
||||
{0x20013310, 0xffffff, u80211_drv_rtl8188eu_init, "DWA-123 (Revision D1, rtl8188eu driver)"},
|
||||
};
|
||||
|
||||
#define MATCH_TABLE_ENTRIES (sizeof(match_table) / sizeof(*match_table))
|
||||
|
||||
static const u80211_drv_match_table_entry_t *get_table_entry(const u80211_drv_device_descriptor_t *device_descriptor,
|
||||
const u80211_drv_interface_descriptor_t *interface_descriptor) {
|
||||
uint32_t id = ((uint32_t)device_descriptor->vendor_id << 16) | device_descriptor->product_id;
|
||||
uint32_t interface = ((uint32_t)interface_descriptor->class_code << 16) | ((uint32_t)interface_descriptor->subclass << 8) | interface_descriptor->protocol;
|
||||
|
||||
for (size_t i = 0; i < MATCH_TABLE_ENTRIES; ++i) {
|
||||
if (match_table[i].id == id && match_table[i].interface == interface)
|
||||
return &match_table[i];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int u80211_drv_probe(u80211_drv_device_handle_t device, u80211_drv_interface_handle_t interface) {
|
||||
u80211_drv_device_descriptor_t device_descriptor;
|
||||
u80211_drv_interface_descriptor_t interface_descriptor;
|
||||
if (u80211_drv_kernel_get_device_descriptor(device, &device_descriptor) != U80211_DRV_STATUS_SUCCESS ||
|
||||
u80211_drv_kernel_get_interface_descriptor(interface, &interface_descriptor) != U80211_DRV_STATUS_SUCCESS)
|
||||
return U80211_DRV_STATUS_UNKNOWN_ERROR;
|
||||
|
||||
const u80211_drv_match_table_entry_t *entry = get_table_entry(&device_descriptor, &interface_descriptor);
|
||||
|
||||
return entry ? U80211_DRV_STATUS_SUCCESS : U80211_DRV_STATUS_NO_MATCH;
|
||||
}
|
||||
|
||||
int u80211_drv_attach(u80211_drv_device_handle_t device, u80211_drv_interface_handle_t interface) {
|
||||
u80211_drv_device_descriptor_t device_descriptor;
|
||||
u80211_drv_interface_descriptor_t interface_descriptor;
|
||||
if (u80211_drv_kernel_get_device_descriptor(device, &device_descriptor) != U80211_DRV_STATUS_SUCCESS ||
|
||||
u80211_drv_kernel_get_interface_descriptor(interface, &interface_descriptor) != U80211_DRV_STATUS_SUCCESS)
|
||||
return U80211_DRV_STATUS_UNKNOWN_ERROR;
|
||||
|
||||
const u80211_drv_match_table_entry_t *entry = get_table_entry(&device_descriptor, &interface_descriptor);
|
||||
|
||||
if (entry == NULL)
|
||||
return U80211_DRV_STATUS_NO_MATCH;
|
||||
|
||||
u80211_drv_kernel_print(U80211_DRV_KERNEL_PRINT_LEVEL_INFO, "Matched device:");
|
||||
u80211_drv_kernel_print(U80211_DRV_KERNEL_PRINT_LEVEL_INFO, entry->name);
|
||||
|
||||
return entry->fn(device, interface);
|
||||
}
|
||||
@@ -798,4 +798,4 @@ void xhci_init(void) {
|
||||
}
|
||||
}
|
||||
|
||||
INIT_ROUTINE_DEFINE(xhci, INIT_ROUTINE_FLAGS_NONE, xhci_init, acpi);
|
||||
INIT_ROUTINE_DEFINE(xhci, INIT_ROUTINE_FLAGS_NONE, xhci_init, acpi, u80211_drv_usb);
|
||||
|
||||
Reference in New Issue
Block a user