Commit 68d57a07 authored by Srinivasan Raju's avatar Srinivasan Raju Committed by Kalle Valo

wireless: add plfxlc driver for pureLiFi X, XL, XC devices

This is a driver for pureLiFi X, XL, XC devices which use light to transmit
data, so they are not compatible with normal Wi-Fi devices. The driver uses
separate NL80211_BAND_LC band to distinguish from Wi-Fi.  The driver is based
on 802.11 softMAC Architecture and uses native 802.11 for configuration and
management. Station and Ad-Hoc modes are supported.

The driver is compiled and tested in ARM, x86 architectures and compiled in
powerpc architecture. This driver implementation has been based on the zd1211rw
driver.
Signed-off-by: default avatarSrinivasan Raju <srini.raju@purelifi.com>
Signed-off-by: default avatarKalle Valo <kvalo@kernel.org>
Link: https://lore.kernel.org/r/20220224182042.132466-3-srini.raju@purelifi.com
parent 2fb822f8
......@@ -15970,6 +15970,12 @@ T: git git://linuxtv.org/media_tree.git
F: Documentation/admin-guide/media/pulse8-cec.rst
F: drivers/media/cec/usb/pulse8/
PURELIFI PLFXLC DRIVER
M: Srinivasan Raju <srini.raju@purelifi.com>
L: linux-wireless@vger.kernel.org
S: Supported
F: drivers/net/wireless/purelifi/plfxlc/
PVRUSB2 VIDEO4LINUX DRIVER
M: Mike Isely <isely@pobox.com>
L: pvrusb2@isely.net (subscribers-only)
......
......@@ -28,6 +28,7 @@ source "drivers/net/wireless/intersil/Kconfig"
source "drivers/net/wireless/marvell/Kconfig"
source "drivers/net/wireless/mediatek/Kconfig"
source "drivers/net/wireless/microchip/Kconfig"
source "drivers/net/wireless/purelifi/Kconfig"
source "drivers/net/wireless/ralink/Kconfig"
source "drivers/net/wireless/realtek/Kconfig"
source "drivers/net/wireless/rsi/Kconfig"
......
......@@ -13,6 +13,7 @@ obj-$(CONFIG_WLAN_VENDOR_INTERSIL) += intersil/
obj-$(CONFIG_WLAN_VENDOR_MARVELL) += marvell/
obj-$(CONFIG_WLAN_VENDOR_MEDIATEK) += mediatek/
obj-$(CONFIG_WLAN_VENDOR_MICROCHIP) += microchip/
obj-$(CONFIG_WLAN_VENDOR_PURELIFI) += purelifi/
obj-$(CONFIG_WLAN_VENDOR_RALINK) += ralink/
obj-$(CONFIG_WLAN_VENDOR_REALTEK) += realtek/
obj-$(CONFIG_WLAN_VENDOR_RSI) += rsi/
......
# SPDX-License-Identifier: GPL-2.0-only
config WLAN_VENDOR_PURELIFI
bool "pureLiFi devices"
default y
help
If you have a pureLiFi device, say Y.
Note that the answer to this question doesn't directly affect the
kernel: saying N will just cause the configurator to skip all the
questions about these cards. If you say Y, you will be asked for
your specific card in the following questions.
if WLAN_VENDOR_PURELIFI
source "drivers/net/wireless/purelifi/plfxlc/Kconfig"
endif # WLAN_VENDOR_PURELIFI
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_PLFXLC) := plfxlc/
# SPDX-License-Identifier: GPL-2.0-only
config PLFXLC
tristate "pureLiFi X, XL, XC device support"
depends on CFG80211 && MAC80211 && USB
help
This option adds support for pureLiFi LiFi wireless USB
adapters. The pureLiFi X, XL, XC USB devices are based on
802.11 OFDM PHY but uses light as the transmission medium.
The driver supports common 802.11 encryption/authentication
methods including Open, WPA, WPA2-Personal and
WPA2-Enterprise (802.1X).
To compile this driver as a module, choose m here. The module will
be called plfxlc.
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_PLFXLC) := plfxlc.o
plfxlc-objs += chip.o firmware.o usb.o mac.o
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2021 pureLiFi
*/
#include <linux/kernel.h>
#include <linux/errno.h>
#include "chip.h"
#include "mac.h"
#include "usb.h"
void plfxlc_chip_init(struct plfxlc_chip *chip,
struct ieee80211_hw *hw,
struct usb_interface *intf)
{
memset(chip, 0, sizeof(*chip));
mutex_init(&chip->mutex);
plfxlc_usb_init(&chip->usb, hw, intf);
}
void plfxlc_chip_release(struct plfxlc_chip *chip)
{
plfxlc_usb_release(&chip->usb);
mutex_destroy(&chip->mutex);
}
int plfxlc_set_beacon_interval(struct plfxlc_chip *chip, u16 interval,
u8 dtim_period, int type)
{
if (!interval ||
(chip->beacon_set &&
le16_to_cpu(chip->beacon_interval) == interval))
return 0;
chip->beacon_interval = cpu_to_le16(interval);
chip->beacon_set = true;
return plfxlc_usb_wreq(chip->usb.ez_usb,
&chip->beacon_interval,
sizeof(chip->beacon_interval),
USB_REQ_BEACON_INTERVAL_WR);
}
int plfxlc_chip_init_hw(struct plfxlc_chip *chip)
{
unsigned char *addr = plfxlc_mac_get_perm_addr(plfxlc_chip_to_mac(chip));
struct usb_device *udev = interface_to_usbdev(chip->usb.intf);
pr_info("plfxlc chip %04x:%04x v%02x %pM %s\n",
le16_to_cpu(udev->descriptor.idVendor),
le16_to_cpu(udev->descriptor.idProduct),
le16_to_cpu(udev->descriptor.bcdDevice),
addr,
plfxlc_speed(udev->speed));
return plfxlc_set_beacon_interval(chip, 100, 0, 0);
}
int plfxlc_chip_switch_radio(struct plfxlc_chip *chip, u16 value)
{
int r;
__le16 radio_on = cpu_to_le16(value);
r = plfxlc_usb_wreq(chip->usb.ez_usb, &radio_on,
sizeof(value), USB_REQ_POWER_WR);
if (r)
dev_err(plfxlc_chip_dev(chip), "POWER_WR failed (%d)\n", r);
return r;
}
int plfxlc_chip_enable_rxtx(struct plfxlc_chip *chip)
{
plfxlc_usb_enable_tx(&chip->usb);
return plfxlc_usb_enable_rx(&chip->usb);
}
void plfxlc_chip_disable_rxtx(struct plfxlc_chip *chip)
{
u8 value = 0;
plfxlc_usb_wreq(chip->usb.ez_usb,
&value, sizeof(value), USB_REQ_RXTX_WR);
plfxlc_usb_disable_rx(&chip->usb);
plfxlc_usb_disable_tx(&chip->usb);
}
int plfxlc_chip_set_rate(struct plfxlc_chip *chip, u8 rate)
{
int r;
if (!chip)
return -EINVAL;
r = plfxlc_usb_wreq(chip->usb.ez_usb,
&rate, sizeof(rate), USB_REQ_RATE_WR);
if (r)
dev_err(plfxlc_chip_dev(chip), "RATE_WR failed (%d)\n", r);
return r;
}
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2021 pureLiFi
*/
#ifndef PLFXLC_CHIP_H
#define PLFXLC_CHIP_H
#include <net/mac80211.h>
#include "usb.h"
enum unit_type {
STA = 0,
AP = 1,
};
enum {
PLFXLC_RADIO_OFF = 0,
PLFXLC_RADIO_ON = 1,
};
struct plfxlc_chip {
struct plfxlc_usb usb;
struct mutex mutex; /* lock to protect chip data */
enum unit_type unit_type;
u16 link_led;
u8 beacon_set;
u16 beacon_interval;
};
struct plfxlc_mc_hash {
u32 low;
u32 high;
};
#define plfxlc_chip_dev(chip) (&(chip)->usb.intf->dev)
void plfxlc_chip_init(struct plfxlc_chip *chip,
struct ieee80211_hw *hw,
struct usb_interface *intf);
void plfxlc_chip_release(struct plfxlc_chip *chip);
void plfxlc_chip_disable_rxtx(struct plfxlc_chip *chip);
int plfxlc_chip_init_hw(struct plfxlc_chip *chip);
int plfxlc_chip_enable_rxtx(struct plfxlc_chip *chip);
int plfxlc_chip_set_rate(struct plfxlc_chip *chip, u8 rate);
int plfxlc_set_beacon_interval(struct plfxlc_chip *chip, u16 interval,
u8 dtim_period, int type);
int plfxlc_chip_switch_radio(struct plfxlc_chip *chip, u16 value);
static inline struct plfxlc_chip *plfxlc_usb_to_chip(struct plfxlc_usb
*usb)
{
return container_of(usb, struct plfxlc_chip, usb);
}
static inline void plfxlc_mc_add_all(struct plfxlc_mc_hash *hash)
{
hash->low = 0xffffffff;
hash->high = 0xffffffff;
}
#endif /* PLFXLC_CHIP_H */
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2021 pureLiFi
*/
#include <linux/firmware.h>
#include <linux/bitrev.h>
#include "mac.h"
#include "usb.h"
static int send_vendor_request(struct usb_device *udev, int request,
unsigned char *buffer, int buffer_size)
{
return usb_control_msg(udev,
usb_rcvctrlpipe(udev, 0),
request, 0xC0, 0, 0,
buffer, buffer_size, PLF_USB_TIMEOUT);
}
static int send_vendor_command(struct usb_device *udev, int request,
unsigned char *buffer, int buffer_size)
{
return usb_control_msg(udev,
usb_sndctrlpipe(udev, 0),
request, USB_TYPE_VENDOR /*0x40*/, 0, 0,
buffer, buffer_size, PLF_USB_TIMEOUT);
}
int plfxlc_download_fpga(struct usb_interface *intf)
{
struct usb_device *udev = interface_to_usbdev(intf);
unsigned char *fpga_dmabuff = NULL;
const struct firmware *fw = NULL;
int blk_tran_len = PLF_BULK_TLEN;
unsigned char *fw_data;
const char *fw_name;
int r, actual_length;
int fw_data_i = 0;
if ((le16_to_cpu(udev->descriptor.idVendor) ==
PURELIFI_X_VENDOR_ID_0) &&
(le16_to_cpu(udev->descriptor.idProduct) ==
PURELIFI_X_PRODUCT_ID_0)) {
fw_name = "plfxlc/lifi-x.bin";
dev_dbg(&intf->dev, "bin file for X selected\n");
} else if ((le16_to_cpu(udev->descriptor.idVendor)) ==
PURELIFI_XC_VENDOR_ID_0 &&
(le16_to_cpu(udev->descriptor.idProduct) ==
PURELIFI_XC_PRODUCT_ID_0)) {
fw_name = "plfxlc/lifi-xc.bin";
dev_dbg(&intf->dev, "bin file for XC selected\n");
} else {
r = -EINVAL;
goto error;
}
r = request_firmware(&fw, fw_name, &intf->dev);
if (r) {
dev_err(&intf->dev, "request_firmware failed (%d)\n", r);
goto error;
}
fpga_dmabuff = kmalloc(PLF_FPGA_STATUS_LEN, GFP_KERNEL);
if (!fpga_dmabuff) {
r = -ENOMEM;
goto error_free_fw;
}
send_vendor_request(udev, PLF_VNDR_FPGA_SET_REQ,
fpga_dmabuff, PLF_FPGA_STATUS_LEN);
send_vendor_command(udev, PLF_VNDR_FPGA_SET_CMD, NULL, 0);
if (fpga_dmabuff[0] != PLF_FPGA_MG) {
dev_err(&intf->dev, "fpga_dmabuff[0] is wrong\n");
r = -EINVAL;
goto error_free_fw;
}
for (fw_data_i = 0; fw_data_i < fw->size;) {
int tbuf_idx;
if ((fw->size - fw_data_i) < blk_tran_len)
blk_tran_len = fw->size - fw_data_i;
fw_data = kmemdup(&fw->data[fw_data_i], blk_tran_len,
GFP_KERNEL);
if (!fw_data) {
r = -ENOMEM;
goto error_free_fw;
}
for (tbuf_idx = 0; tbuf_idx < blk_tran_len; tbuf_idx++) {
/* u8 bit reverse */
fw_data[tbuf_idx] = bitrev8(fw_data[tbuf_idx]);
}
r = usb_bulk_msg(udev,
usb_sndbulkpipe(interface_to_usbdev(intf),
fpga_dmabuff[0] & 0xff),
fw_data,
blk_tran_len,
&actual_length,
2 * PLF_USB_TIMEOUT);
if (r)
dev_err(&intf->dev, "Bulk msg failed (%d)\n", r);
kfree(fw_data);
fw_data_i += blk_tran_len;
}
kfree(fpga_dmabuff);
fpga_dmabuff = kmalloc(PLF_FPGA_STATE_LEN, GFP_KERNEL);
if (!fpga_dmabuff) {
r = -ENOMEM;
goto error_free_fw;
}
memset(fpga_dmabuff, 0xff, PLF_FPGA_STATE_LEN);
send_vendor_request(udev, PLF_VNDR_FPGA_STATE_REQ, fpga_dmabuff,
PLF_FPGA_STATE_LEN);
dev_dbg(&intf->dev, "%*ph\n", 8, fpga_dmabuff);
if (fpga_dmabuff[0] != 0) {
r = -EINVAL;
goto error_free_fw;
}
send_vendor_command(udev, PLF_VNDR_FPGA_STATE_CMD, NULL, 0);
msleep(PLF_MSLEEP_TIME);
error_free_fw:
kfree(fpga_dmabuff);
release_firmware(fw);
error:
return r;
}
int plfxlc_download_xl_firmware(struct usb_interface *intf)
{
struct usb_device *udev = interface_to_usbdev(intf);
const struct firmware *fwp = NULL;
struct plfxlc_firmware_file file = {0};
const char *fw_pack;
int s, r;
u8 *buf;
u32 i;
r = send_vendor_command(udev, PLF_VNDR_XL_FW_CMD, NULL, 0);
msleep(PLF_MSLEEP_TIME);
if (r) {
dev_err(&intf->dev, "vendor command failed (%d)\n", r);
return -EINVAL;
}
/* Code for single pack file download */
fw_pack = "plfxlc/lifi-xl.bin";
r = request_firmware(&fwp, fw_pack, &intf->dev);
if (r) {
dev_err(&intf->dev, "Request_firmware failed (%d)\n", r);
return -EINVAL;
}
file.total_files = get_unaligned_le32(&fwp->data[0]);
file.total_size = get_unaligned_le32(&fwp->size);
dev_dbg(&intf->dev, "XL Firmware (%d, %d)\n",
file.total_files, file.total_size);
buf = kzalloc(PLF_XL_BUF_LEN, GFP_KERNEL);
if (!buf) {
release_firmware(fwp);
return -ENOMEM;
}
if (file.total_files > 10) {
dev_err(&intf->dev, "Too many files (%d)\n", file.total_files);
release_firmware(fwp);
kfree(buf);
return -EINVAL;
}
/* Download firmware files in multiple steps */
for (s = 0; s < file.total_files; s++) {
buf[0] = s;
r = send_vendor_command(udev, PLF_VNDR_XL_FILE_CMD, buf,
PLF_XL_BUF_LEN);
if (s < file.total_files - 1)
file.size = get_unaligned_le32(&fwp->data[4 + ((s + 1) * 4)])
- get_unaligned_le32(&fwp->data[4 + (s) * 4]);
else
file.size = file.total_size -
get_unaligned_le32(&fwp->data[4 + (s) * 4]);
if (file.size > file.total_size || file.size > 60000) {
dev_err(&intf->dev, "File size is too large (%d)\n", file.size);
break;
}
file.start_addr = get_unaligned_le32(&fwp->data[4 + (s * 4)]);
if (file.size % PLF_XL_BUF_LEN && s < 2)
file.size += PLF_XL_BUF_LEN - file.size % PLF_XL_BUF_LEN;
file.control_packets = file.size / PLF_XL_BUF_LEN;
for (i = 0; i < file.control_packets; i++) {
memcpy(buf,
&fwp->data[file.start_addr + (i * PLF_XL_BUF_LEN)],
PLF_XL_BUF_LEN);
r = send_vendor_command(udev, PLF_VNDR_XL_DATA_CMD, buf,
PLF_XL_BUF_LEN);
}
dev_dbg(&intf->dev, "fw-dw step=%d,r=%d size=%d\n", s, r,
file.size);
}
release_firmware(fwp);
kfree(buf);
/* Code for single pack file download ends fw download finish */
r = send_vendor_command(udev, PLF_VNDR_XL_EX_CMD, NULL, 0);
dev_dbg(&intf->dev, "Download fpga (4) (%d)\n", r);
return 0;
}
int plfxlc_upload_mac_and_serial(struct usb_interface *intf,
unsigned char *hw_address,
unsigned char *serial_number)
{
struct usb_device *udev = interface_to_usbdev(intf);
unsigned long long firmware_version;
unsigned char *dma_buffer = NULL;
dma_buffer = kmalloc(PLF_SERIAL_LEN, GFP_KERNEL);
if (!dma_buffer)
return -ENOMEM;
BUILD_BUG_ON(ETH_ALEN > PLF_SERIAL_LEN);
BUILD_BUG_ON(PLF_FW_VER_LEN > PLF_SERIAL_LEN);
send_vendor_request(udev, PLF_MAC_VENDOR_REQUEST, dma_buffer,
ETH_ALEN);
memcpy(hw_address, dma_buffer, ETH_ALEN);
send_vendor_request(udev, PLF_SERIAL_NUMBER_VENDOR_REQUEST,
dma_buffer, PLF_SERIAL_LEN);
send_vendor_request(udev, PLF_SERIAL_NUMBER_VENDOR_REQUEST,
dma_buffer, PLF_SERIAL_LEN);
memcpy(serial_number, dma_buffer, PLF_SERIAL_LEN);
memset(dma_buffer, 0x00, PLF_SERIAL_LEN);
send_vendor_request(udev, PLF_FIRMWARE_VERSION_VENDOR_REQUEST,
(unsigned char *)dma_buffer, PLF_FW_VER_LEN);
memcpy(&firmware_version, dma_buffer, PLF_FW_VER_LEN);
dev_info(&intf->dev, "Firmware Version: %llu\n", firmware_version);
kfree(dma_buffer);
dev_dbg(&intf->dev, "Mac: %pM\n", hw_address);
return 0;
}
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2021 pureLiFi
*/
#define PURELIFI_BYTE_NUM_ALIGNMENT 4
#define ETH_ALEN 6
#define AP_USER_LIMIT 8
#define PLF_VNDR_FPGA_STATE_REQ 0x30
#define PLF_VNDR_FPGA_SET_REQ 0x33
#define PLF_VNDR_FPGA_SET_CMD 0x34
#define PLF_VNDR_FPGA_STATE_CMD 0x35
#define PLF_VNDR_XL_FW_CMD 0x80
#define PLF_VNDR_XL_DATA_CMD 0x81
#define PLF_VNDR_XL_FILE_CMD 0x82
#define PLF_VNDR_XL_EX_CMD 0x83
#define PLF_MAC_VENDOR_REQUEST 0x36
#define PLF_SERIAL_NUMBER_VENDOR_REQUEST 0x37
#define PLF_FIRMWARE_VERSION_VENDOR_REQUEST 0x39
#define PLF_SERIAL_LEN 14
#define PLF_FW_VER_LEN 8
struct rx_status {
__be16 rssi;
u8 rate_idx;
u8 pad;
__be64 crc_error_count;
} __packed;
enum plf_usb_req_enum {
USB_REQ_TEST_WR = 0,
USB_REQ_MAC_WR = 1,
USB_REQ_POWER_WR = 2,
USB_REQ_RXTX_WR = 3,
USB_REQ_BEACON_WR = 4,
USB_REQ_BEACON_INTERVAL_WR = 5,
USB_REQ_RTS_CTS_RATE_WR = 6,
USB_REQ_HASH_WR = 7,
USB_REQ_DATA_TX = 8,
USB_REQ_RATE_WR = 9,
USB_REQ_SET_FREQ = 15
};
struct plf_usb_req {
__be32 id; /* should be plf_usb_req_enum */
__be32 len;
u8 buf[512];
};
This diff is collapsed.
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2021 pureLiFi
*/
#ifndef PLFXLC_MAC_H
#define PLFXLC_MAC_H
#include <linux/kernel.h>
#include <net/mac80211.h>
#include "chip.h"
#define PURELIFI_CCK 0x00
#define PURELIFI_OFDM 0x10
#define PURELIFI_CCK_PREA_SHORT 0x20
#define PURELIFI_OFDM_PLCP_RATE_6M 0xb
#define PURELIFI_OFDM_PLCP_RATE_9M 0xf
#define PURELIFI_OFDM_PLCP_RATE_12M 0xa
#define PURELIFI_OFDM_PLCP_RATE_18M 0xe
#define PURELIFI_OFDM_PLCP_RATE_24M 0x9
#define PURELIFI_OFDM_PLCP_RATE_36M 0xd
#define PURELIFI_OFDM_PLCP_RATE_48M 0x8
#define PURELIFI_OFDM_PLCP_RATE_54M 0xc
#define PURELIFI_CCK_RATE_1M (PURELIFI_CCK | 0x00)
#define PURELIFI_CCK_RATE_2M (PURELIFI_CCK | 0x01)
#define PURELIFI_CCK_RATE_5_5M (PURELIFI_CCK | 0x02)
#define PURELIFI_CCK_RATE_11M (PURELIFI_CCK | 0x03)
#define PURELIFI_OFDM_RATE_6M (PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_6M)
#define PURELIFI_OFDM_RATE_9M (PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_9M)
#define PURELIFI_OFDM_RATE_12M (PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_12M)
#define PURELIFI_OFDM_RATE_18M (PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_18M)
#define PURELIFI_OFDM_RATE_24M (PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_24M)
#define PURELIFI_OFDM_RATE_36M (PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_36M)
#define PURELIFI_OFDM_RATE_48M (PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_48M)
#define PURELIFI_OFDM_RATE_54M (PURELIFI_OFDM | PURELIFI_OFDM_PLCP_RATE_54M)
#define PURELIFI_RX_ERROR 0x80
#define PURELIFI_RX_CRC32_ERROR 0x10
#define PLF_REGDOMAIN_FCC 0x10
#define PLF_REGDOMAIN_IC 0x20
#define PLF_REGDOMAIN_ETSI 0x30
#define PLF_REGDOMAIN_SPAIN 0x31
#define PLF_REGDOMAIN_FRANCE 0x32
#define PLF_REGDOMAIN_JAPAN_2 0x40
#define PLF_REGDOMAIN_JAPAN 0x41
#define PLF_REGDOMAIN_JAPAN_3 0x49
#define PLF_RX_ERROR 0x80
#define PLF_RX_CRC32_ERROR 0x10
enum {
MODULATION_RATE_BPSK_1_2 = 0,
MODULATION_RATE_BPSK_3_4,
MODULATION_RATE_QPSK_1_2,
MODULATION_RATE_QPSK_3_4,
MODULATION_RATE_QAM16_1_2,
MODULATION_RATE_QAM16_3_4,
MODULATION_RATE_QAM64_1_2,
MODULATION_RATE_QAM64_3_4,
MODULATION_RATE_AUTO,
MODULATION_RATE_NUM
};
#define plfxlc_mac_dev(mac) plfxlc_chip_dev(&(mac)->chip)
#define PURELIFI_MAC_STATS_BUFFER_SIZE 16
#define PURELIFI_MAC_MAX_ACK_WAITERS 50
struct plfxlc_ctrlset {
/* id should be plf_usb_req_enum */
__be32 id;
__be32 len;
u8 modulation;
u8 control;
u8 service;
u8 pad;
__le16 packet_length;
__le16 current_length;
__le16 next_frame_length;
__le16 tx_length;
__be32 payload_len_nw;
} __packed;
/* overlay */
struct plfxlc_header {
struct plfxlc_ctrlset plf_ctrl;
u32 frametype;
u8 *dmac;
} __packed;
struct tx_status {
u8 type;
u8 id;
u8 rate;
u8 pad;
u8 mac[ETH_ALEN];
u8 retry;
u8 failure;
} __packed;
struct beacon {
struct delayed_work watchdog_work;
struct sk_buff *cur_beacon;
unsigned long last_update;
u16 interval;
u8 period;
};
enum plfxlc_device_flags {
PURELIFI_DEVICE_RUNNING,
};
struct plfxlc_mac {
struct ieee80211_hw *hw;
struct ieee80211_vif *vif;
struct beacon beacon;
struct work_struct set_rts_cts_work;
struct work_struct process_intr;
struct plfxlc_mc_hash multicast_hash;
struct sk_buff_head ack_wait_queue;
struct ieee80211_channel channels[14];
struct ieee80211_rate rates[12];
struct ieee80211_supported_band band;
struct plfxlc_chip chip;
spinlock_t lock; /* lock for mac data */
u8 intr_buffer[USB_MAX_EP_INT_BUFFER];
char serial_number[PURELIFI_SERIAL_LEN];
unsigned char hw_address[ETH_ALEN];
u8 default_regdomain;
unsigned long flags;
bool pass_failed_fcs;
bool pass_ctrl;
bool ack_pending;
int ack_signal;
int associated;
u8 regdomain;
u8 channel;
int type;
u64 crc_errors;
u64 rssi;
};
static inline struct plfxlc_mac *
plfxlc_hw_mac(struct ieee80211_hw *hw)
{
return hw->priv;
}
static inline struct plfxlc_mac *
plfxlc_chip_to_mac(struct plfxlc_chip *chip)
{
return container_of(chip, struct plfxlc_mac, chip);
}
static inline struct plfxlc_mac *
plfxlc_usb_to_mac(struct plfxlc_usb *usb)
{
return plfxlc_chip_to_mac(plfxlc_usb_to_chip(usb));
}
static inline u8 *plfxlc_mac_get_perm_addr(struct plfxlc_mac *mac)
{
return mac->hw->wiphy->perm_addr;
}
struct ieee80211_hw *plfxlc_mac_alloc_hw(struct usb_interface *intf);
void plfxlc_mac_release(struct plfxlc_mac *mac);
int plfxlc_mac_preinit_hw(struct ieee80211_hw *hw, const u8 *hw_address);
int plfxlc_mac_init_hw(struct ieee80211_hw *hw);
int plfxlc_mac_rx(struct ieee80211_hw *hw, const u8 *buffer,
unsigned int length);
void plfxlc_mac_tx_failed(struct urb *urb);
void plfxlc_mac_tx_to_dev(struct sk_buff *skb, int error);
int plfxlc_op_start(struct ieee80211_hw *hw);
void plfxlc_op_stop(struct ieee80211_hw *hw);
int plfxlc_restore_settings(struct plfxlc_mac *mac);
#endif /* PLFXLC_MAC_H */
This diff is collapsed.
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2021 pureLiFi
*/
#ifndef PLFXLC_USB_H
#define PLFXLC_USB_H
#include <linux/completion.h>
#include <linux/netdevice.h>
#include <linux/spinlock.h>
#include <linux/skbuff.h>
#include <linux/usb.h>
#include "intf.h"
#define USB_BULK_MSG_TIMEOUT_MS 2000
#define PURELIFI_X_VENDOR_ID_0 0x16C1
#define PURELIFI_X_PRODUCT_ID_0 0x1CDE
#define PURELIFI_XC_VENDOR_ID_0 0x2EF5
#define PURELIFI_XC_PRODUCT_ID_0 0x0008
#define PURELIFI_XL_VENDOR_ID_0 0x2EF5
#define PURELIFI_XL_PRODUCT_ID_0 0x000A /* Station */
#define PLF_FPGA_STATUS_LEN 2
#define PLF_FPGA_STATE_LEN 9
#define PLF_BULK_TLEN 16384
#define PLF_FPGA_MG 6 /* Magic check */
#define PLF_XL_BUF_LEN 64
#define PLF_MSG_STATUS_OFFSET 7
#define PLF_USB_TIMEOUT 1000
#define PLF_MSLEEP_TIME 200
#define PURELIFI_URB_RETRY_MAX 5
#define plfxlc_usb_dev(usb) (&(usb)->intf->dev)
/* Tx retry backoff timer (in milliseconds) */
#define TX_RETRY_BACKOFF_MS 10
#define STA_QUEUE_CLEANUP_MS 5000
/* Tx retry backoff timer (in jiffies) */
#define TX_RETRY_BACKOFF_JIFF ((TX_RETRY_BACKOFF_MS * HZ) / 1000)
#define STA_QUEUE_CLEANUP_JIFF ((STA_QUEUE_CLEANUP_MS * HZ) / 1000)
/* Ensures that MAX_TRANSFER_SIZE is even. */
#define MAX_TRANSFER_SIZE (USB_MAX_TRANSFER_SIZE & ~1)
#define plfxlc_urb_dev(urb) (&(urb)->dev->dev)
#define STATION_FIFO_ALMOST_FULL_MESSAGE 0
#define STATION_FIFO_ALMOST_FULL_NOT_MESSAGE 1
#define STATION_CONNECT_MESSAGE 2
#define STATION_DISCONNECT_MESSAGE 3
int plfxlc_usb_wreq(struct usb_interface *ez_usb, void *buffer, int buffer_len,
enum plf_usb_req_enum usb_req_id);
void plfxlc_tx_urb_complete(struct urb *urb);
enum {
USB_MAX_RX_SIZE = 4800,
USB_MAX_EP_INT_BUFFER = 64,
};
struct plfxlc_usb_interrupt {
spinlock_t lock; /* spin lock for usb interrupt buffer */
struct urb *urb;
void *buffer;
int interval;
};
#define RX_URBS_COUNT 5
struct plfxlc_usb_rx {
spinlock_t lock; /* spin lock for rx urb */
struct mutex setup_mutex; /* mutex lockt for rx urb */
u8 fragment[2 * USB_MAX_RX_SIZE];
unsigned int fragment_length;
unsigned int usb_packet_size;
struct urb **urbs;
int urbs_count;
};
struct plf_station {
/* 7...3 | 2 | 1 | 0 |
* Reserved | Heartbeat | FIFO full | Connected |
*/
unsigned char flag;
unsigned char mac[ETH_ALEN];
struct sk_buff_head data_list;
};
struct plfxlc_firmware_file {
u32 total_files;
u32 total_size;
u32 size;
u32 start_addr;
u32 control_packets;
} __packed;
#define STATION_CONNECTED_FLAG 0x1
#define STATION_FIFO_FULL_FLAG 0x2
#define STATION_HEARTBEAT_FLAG 0x4
#define STATION_ACTIVE_FLAG 0xFD
#define PURELIFI_SERIAL_LEN 256
#define STA_BROADCAST_INDEX (AP_USER_LIMIT)
#define MAX_STA_NUM (AP_USER_LIMIT + 1)
struct plfxlc_usb_tx {
unsigned long enabled;
spinlock_t lock; /* spinlock for USB tx */
u8 mac_fifo_full;
struct sk_buff_head submitted_skbs;
struct usb_anchor submitted;
int submitted_urbs;
bool stopped;
struct timer_list tx_retry_timer;
struct plf_station station[MAX_STA_NUM];
};
/* Contains the usb parts. The structure doesn't require a lock because intf
* will not be changed after initialization.
*/
struct plfxlc_usb {
struct timer_list sta_queue_cleanup;
struct plfxlc_usb_rx rx;
struct plfxlc_usb_tx tx;
struct usb_interface *intf;
struct usb_interface *ez_usb;
u8 req_buf[64]; /* plfxlc_usb_iowrite16v needs 62 bytes */
u8 sidx; /* store last served */
bool rx_usb_enabled;
bool initialized;
bool was_running;
bool link_up;
};
enum endpoints {
EP_DATA_IN = 2,
EP_DATA_OUT = 8,
};
enum devicetype {
DEVICE_LIFI_X = 0,
DEVICE_LIFI_XC = 1,
DEVICE_LIFI_XL = 1,
};
enum {
PLF_BIT_ENABLED = 1,
PLF_BIT_MAX = 2,
};
int plfxlc_usb_wreq_async(struct plfxlc_usb *usb, const u8 *buffer,
int buffer_len, enum plf_usb_req_enum usb_req_id,
usb_complete_t complete_fn, void *context);
static inline struct usb_device *
plfxlc_usb_to_usbdev(struct plfxlc_usb *usb)
{
return interface_to_usbdev(usb->intf);
}
static inline struct ieee80211_hw *
plfxlc_intf_to_hw(struct usb_interface *intf)
{
return usb_get_intfdata(intf);
}
static inline struct ieee80211_hw *
plfxlc_usb_to_hw(struct plfxlc_usb *usb)
{
return plfxlc_intf_to_hw(usb->intf);
}
void plfxlc_usb_init(struct plfxlc_usb *usb, struct ieee80211_hw *hw,
struct usb_interface *intf);
void plfxlc_send_packet_from_data_queue(struct plfxlc_usb *usb);
void plfxlc_usb_release(struct plfxlc_usb *usb);
void plfxlc_usb_disable_rx(struct plfxlc_usb *usb);
void plfxlc_usb_enable_tx(struct plfxlc_usb *usb);
void plfxlc_usb_disable_tx(struct plfxlc_usb *usb);
int plfxlc_usb_tx(struct plfxlc_usb *usb, struct sk_buff *skb);
int plfxlc_usb_enable_rx(struct plfxlc_usb *usb);
int plfxlc_usb_init_hw(struct plfxlc_usb *usb);
const char *plfxlc_speed(enum usb_device_speed speed);
/* Firmware declarations */
int plfxlc_download_xl_firmware(struct usb_interface *intf);
int plfxlc_download_fpga(struct usb_interface *intf);
int plfxlc_upload_mac_and_serial(struct usb_interface *intf,
unsigned char *hw_address,
unsigned char *serial_number);
#endif /* PLFXLC_USB_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment