// SPDX-License-Identifier: GPL-2.0
/* Copyright(c) 2009-2012 Realtek Corporation.*/
#include "wifi.h"
#include "core.h"
#include "usb.h"
#include "base.h"
#include "ps.h"
#include "rtl8192c/fw_common.h"
#include <linux/export.h>
#include <linux/module.h>
MODULE_AUTHOR("lizhaoming <chaoming_li@realsil.com.cn>");
MODULE_AUTHOR("Realtek WlanFAE <wlanfae@realtek.com>");
MODULE_AUTHOR("Larry Finger <Larry.FInger@lwfinger.net>");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("USB basic driver for rtlwifi");
#define REALTEK_USB_VENQT_READ 0xC0
#define REALTEK_USB_VENQT_WRITE 0x40
#define REALTEK_USB_VENQT_CMD_REQ 0x05
#define REALTEK_USB_VENQT_CMD_IDX 0x00
#define MAX_USBCTRL_VENDORREQ_TIMES 10
static void _rtl_usb_cleanup_tx(struct ieee80211_hw *hw);
static void _usbctrl_vendorreq_sync(struct usb_device *udev, u8 reqtype,
u16 value, void *pdata, u16 len)
{
unsigned int pipe;
int status;
int vendorreq_times = 0;
static int count;
if (reqtype == REALTEK_USB_VENQT_READ)
pipe = usb_rcvctrlpipe(udev, 0); /* read_in */
else
pipe = usb_sndctrlpipe(udev, 0); /* write_out */
do {
status = usb_control_msg(udev, pipe, REALTEK_USB_VENQT_CMD_REQ,
reqtype, value, REALTEK_USB_VENQT_CMD_IDX,
pdata, len, 1000);
if (status < 0) {
/* firmware download is checksumed, don't retry */
if ((value >= FW_8192C_START_ADDRESS &&
value <= FW_8192C_END_ADDRESS))
break;
} else {
break;
}
} while (++vendorreq_times < MAX_USBCTRL_VENDORREQ_TIMES);
if (status < 0 && count++ < 4)
dev_err(&udev->dev, "reg 0x%x, usbctrl_vendorreq TimeOut! status:0x%x value=0x%x reqtype=0x%x\n",
value, status, *(u32 *)pdata, reqtype);
}
static u32 _usb_read_sync(struct rtl_priv *rtlpriv, u32 addr, u16 len)
{
struct device *dev = rtlpriv->io.dev;
struct usb_device *udev = to_usb_device(dev);
u16 wvalue;
__le32 *data;
unsigned long flags;
spin_lock_irqsave(&rtlpriv->locks.usb_lock, flags);
if (++rtlpriv->usb_data_index >= RTL_USB_MAX_RX_COUNT)
rtlpriv->usb_data_index = 0;
data = &rtlpriv->usb_data[rtlpriv->usb_data_index];
spin_unlock_irqrestore(&rtlpriv->locks.usb_lock, flags);
wvalue = (u16)addr;
_usbctrl_vendorreq_sync(udev, REALTEK_USB_VENQT_READ, wvalue, data, len);
return le32_to_cpu(*data);
}
static void _usb_write_sync(struct rtl_priv *rtlpriv, u32 addr, u32 val, u16 len)
{
struct device *dev = rtlpriv->io.dev;
struct usb_device *udev = to_usb_device(dev);
unsigned long flags;
__le32 *data;
u16 wvalue;
spin_lock_irqsave(&rtlpriv->locks.usb_lock, flags);
if (++rtlpriv->usb_data_index >= RTL_USB_MAX_RX_COUNT)
rtlpriv->usb_data_index = 0;
data = &rtlpriv->usb_data[rtlpriv->usb_data_index];
spin_unlock_irqrestore(&rtlpriv->locks.usb_lock, flags);
wvalue = (u16)(addr & 0x0000ffff);
*data = cpu_to_le32(val);
_usbctrl_vendorreq_sync(udev, REALTEK_USB_VENQT_WRITE, wvalue, data, len);
}
static u8 _usb_read8_sync(struct rtl_priv *rtlpriv, u32 addr)
{
return (u8)_usb_read_sync(rtlpriv, addr, 1);
}
static u16 _usb_read16_sync(struct rtl_priv *rtlpriv, u32 addr)
{
return (u16)_usb_read_sync(rtlpriv, addr, 2);
}
static u32 _usb_read32_sync(struct rtl_priv *rtlpriv, u32 addr)
{
return _usb_read_sync(rtlpriv, addr, 4);
}
static void _usb_write8_sync(struct rtl_priv *rtlpriv, u32 addr, u8 val)
{
_usb_write_sync(rtlpriv, addr, val, 1);
}
static void _usb_write16_sync(struct rtl_priv *rtlpriv, u32 addr, u16 val)
{
_usb_write_sync(rtlpriv, addr, val, 2);
}
static void _usb_write32_sync(struct rtl_priv *rtlpriv, u32 addr, u32 val)
{
_usb_write_sync(rtlpriv, addr, val, 4);
}
static void _usb_write_chunk_sync(struct rtl_priv *rtlpriv, u32 addr,
u32 length, u8 *data)
{
struct usb_device *udev = to_usb_device(rtlpriv->io.dev);
_usbctrl_vendorreq_sync(udev, REALTEK_USB_VENQT_WRITE