mirror of
https://github.com/linux-msm/laptops-kernel.git
synced 2026-08-13 14:19:53 -07:00
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: Srinivasan Raju <srini.raju@purelifi.com> Signed-off-by: Kalle Valo <kvalo@kernel.org> Link: https://lore.kernel.org/r/20220224182042.132466-3-srini.raju@purelifi.com
This commit is contained in:
committed by
Kalle Valo
parent
2fb822f82a
commit
68d57a07bf
@@ -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/
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
# 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
|
||||
@@ -0,0 +1,2 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
obj-$(CONFIG_PLFXLC) := plfxlc/
|
||||
@@ -0,0 +1,14 @@
|
||||
# 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.
|
||||
@@ -0,0 +1,3 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
obj-$(CONFIG_PLFXLC) := plfxlc.o
|
||||
plfxlc-objs += chip.o firmware.o usb.o mac.o
|
||||
@@ -0,0 +1,99 @@
|
||||
// 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;
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/* 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 */
|
||||
@@ -0,0 +1,276 @@
|
||||
// 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;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
/* 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];
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,184 @@
|
||||
/* 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 */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,198 @@
|
||||
/* 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 */
|
||||
Reference in New Issue
Block a user