/******************************************************************************
|
*
|
* Copyright(c) 2007 - 2019 Realtek Corporation.
|
*
|
* This program is free software; you can redistribute it and/or modify it
|
* under the terms of version 2 of the GNU General Public License as
|
* published by the Free Software Foundation.
|
*
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
* more details.
|
*
|
*****************************************************************************/
|
#define _HCI_INTF_C_
|
|
#include <drv_types.h>
|
|
#include <platform_ops.h>
|
#include <rtw_trx_usb.h>
|
|
#ifndef CONFIG_USB_HCI
|
#error "CONFIG_USB_HCI shall be on!\n"
|
#endif
|
|
#ifdef CONFIG_80211N_HT
|
extern int rtw_ht_enable;
|
extern int rtw_bw_mode;
|
extern int rtw_ampdu_enable;/* for enable tx_ampdu */
|
#endif
|
|
#ifdef CONFIG_GLOBAL_UI_PID
|
int ui_pid[3] = {0, 0, 0};
|
#endif
|
|
|
static int rtw_dev_suspend(struct usb_interface *intf, pm_message_t message);
|
static int rtw_dev_resume(struct usb_interface *intf);
|
|
|
static int rtw_dev_probe(struct usb_interface *pusb_intf, const struct usb_device_id *pdid);
|
static void rtw_dev_remove(struct usb_interface *pusb_intf);
|
|
static void rtw_dev_shutdown(struct device *dev)
|
{
|
struct usb_interface *usb_intf = container_of(dev, struct usb_interface, dev);
|
struct dvobj_priv *dvobj = NULL;
|
_adapter *adapter = NULL;
|
|
RTW_INFO("%s\n", __func__);
|
|
if (usb_intf) {
|
dvobj = usb_get_intfdata(usb_intf);
|
if (dvobj) {
|
adapter = dvobj_get_primary_adapter(dvobj);
|
if (adapter) {
|
if (!dev_is_surprise_removed(dvobj)) {
|
#ifdef CONFIG_WOWLAN
|
struct pwrctrl_priv *pwrctl = adapter_to_pwrctl(adapter);
|
|
#ifdef CONFIG_GPIO_WAKEUP
|
/*default wake up pin change to BT*/
|
RTW_INFO("%s:default wake up pin change to BT\n", __FUNCTION__);
|
/* ToDo: clear pin mux code is not ready
|
rtw_hal_switch_gpio_wl_ctrl(adapter, WAKEUP_GPIO_IDX, _FALSE); */
|
#endif /* CONFIG_GPIO_WAKEUP */
|
|
if (pwrctl->wowlan_mode == _TRUE)
|
RTW_PRINT("%s wowlan_mode ==_TRUE do not run rtw_hw_stop()\n", __FUNCTION__);
|
else
|
#endif
|
{
|
if (rtw_hw_is_init_completed(dvobj))
|
rtw_hw_stop(dvobj);
|
dev_set_surprise_removed(dvobj);
|
}
|
}
|
}
|
ATOMIC_SET(&dvobj->continual_io_error, MAX_CONTINUAL_IO_ERR + 1);
|
}
|
}
|
}
|
|
#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 23))
|
/* Some useful macros to use to create struct usb_device_id */
|
#define USB_DEVICE_ID_MATCH_VENDOR 0x0001
|
#define USB_DEVICE_ID_MATCH_PRODUCT 0x0002
|
#define USB_DEVICE_ID_MATCH_DEV_LO 0x0004
|
#define USB_DEVICE_ID_MATCH_DEV_HI 0x0008
|
#define USB_DEVICE_ID_MATCH_DEV_CLASS 0x0010
|
#define USB_DEVICE_ID_MATCH_DEV_SUBCLASS 0x0020
|
#define USB_DEVICE_ID_MATCH_DEV_PROTOCOL 0x0040
|
#define USB_DEVICE_ID_MATCH_INT_CLASS 0x0080
|
#define USB_DEVICE_ID_MATCH_INT_SUBCLASS 0x0100
|
#define USB_DEVICE_ID_MATCH_INT_PROTOCOL 0x0200
|
#define USB_DEVICE_ID_MATCH_INT_NUMBER 0x0400
|
|
|
#define USB_DEVICE_ID_MATCH_INT_INFO \
|
(USB_DEVICE_ID_MATCH_INT_CLASS | \
|
USB_DEVICE_ID_MATCH_INT_SUBCLASS | \
|
USB_DEVICE_ID_MATCH_INT_PROTOCOL)
|
|
|
#define USB_DEVICE_AND_INTERFACE_INFO(vend, prod, cl, sc, pr) \
|
.match_flags = USB_DEVICE_ID_MATCH_INT_INFO \
|
| USB_DEVICE_ID_MATCH_DEVICE, \
|
.idVendor = (vend), \
|
.idProduct = (prod), \
|
.bInterfaceClass = (cl), \
|
.bInterfaceSubClass = (sc), \
|
.bInterfaceProtocol = (pr)
|
|
/**
|
* USB_VENDOR_AND_INTERFACE_INFO - describe a specific usb vendor with a class of usb interfaces
|
* @vend: the 16 bit USB Vendor ID
|
* @cl: bInterfaceClass value
|
* @sc: bInterfaceSubClass value
|
* @pr: bInterfaceProtocol value
|
*
|
* This macro is used to create a struct usb_device_id that matches a
|
* specific vendor with a specific class of interfaces.
|
*
|
* This is especially useful when explicitly matching devices that have
|
* vendor specific bDeviceClass values, but standards-compliant interfaces.
|
*/
|
#define USB_VENDOR_AND_INTERFACE_INFO(vend, cl, sc, pr) \
|
.match_flags = USB_DEVICE_ID_MATCH_INT_INFO \
|
| USB_DEVICE_ID_MATCH_VENDOR, \
|
.idVendor = (vend), \
|
.bInterfaceClass = (cl), \
|
.bInterfaceSubClass = (sc), \
|
.bInterfaceProtocol = (pr)
|
|
/* ----------------------------------------------------------------------- */
|
#endif
|
|
|
#define USB_VENDER_ID_REALTEK 0x0BDA
|
|
|
/* DID_USB_v916_20130116 */
|
static struct usb_device_id rtw_usb_id_tbl[] = {
|
#ifdef CONFIG_RTL8852A
|
/*=== Realtek demoboard ===*/
|
{USB_DEVICE_AND_INTERFACE_INFO(USB_VENDER_ID_REALTEK, 0x8832, 0xff, 0xff, 0xff), .driver_info = RTL8852A},
|
{USB_DEVICE_AND_INTERFACE_INFO(USB_VENDER_ID_REALTEK, 0x885A, 0xff, 0xff, 0xff), .driver_info = RTL8852A},
|
{USB_DEVICE_AND_INTERFACE_INFO(USB_VENDER_ID_REALTEK, 0x885C, 0xff, 0xff, 0xff), .driver_info = RTL8852A},
|
#endif /* CONFIG_RTL8852A */
|
|
#ifdef CONFIG_RTL8852B
|
/*=== Realtek demoboard ===*/
|
{USB_DEVICE_AND_INTERFACE_INFO(USB_VENDER_ID_REALTEK, 0xB832, 0xff, 0xff, 0xff), .driver_info = RTL8852B},
|
{USB_DEVICE_AND_INTERFACE_INFO(USB_VENDER_ID_REALTEK, 0xB83A, 0xff, 0xff, 0xff), .driver_info = RTL8852B},
|
{USB_DEVICE_AND_INTERFACE_INFO(USB_VENDER_ID_REALTEK, 0xB852, 0xff, 0xff, 0xff), .driver_info = RTL8852B},
|
{USB_DEVICE_AND_INTERFACE_INFO(USB_VENDER_ID_REALTEK, 0xB85A, 0xff, 0xff, 0xff), .driver_info = RTL8852B},
|
{USB_DEVICE_AND_INTERFACE_INFO(USB_VENDER_ID_REALTEK, 0xA85B, 0xff, 0xff, 0xff), .driver_info = RTL8852B},
|
#endif /* CONFIG_RTL8852B */
|
|
{} /* Terminating entry */
|
};
|
|
MODULE_DEVICE_TABLE(usb, rtw_usb_id_tbl);
|
|
int const rtw_usb_id_len = sizeof(rtw_usb_id_tbl) / sizeof(struct usb_device_id);
|
|
static struct specific_device_id specific_device_id_tbl[] = {
|
{.idVendor = USB_VENDER_ID_REALTEK, .idProduct = 0x8177, .flags = SPEC_DEV_ID_DISABLE_HT}, /* 8188cu 1*1 dongole, (b/g mode only) */
|
{.idVendor = USB_VENDER_ID_REALTEK, .idProduct = 0x817E, .flags = SPEC_DEV_ID_DISABLE_HT}, /* 8188CE-VAU USB minCard (b/g mode only) */
|
{.idVendor = 0x0b05, .idProduct = 0x1791, .flags = SPEC_DEV_ID_DISABLE_HT},
|
{.idVendor = 0x13D3, .idProduct = 0x3311, .flags = SPEC_DEV_ID_DISABLE_HT},
|
{.idVendor = 0x13D3, .idProduct = 0x3359, .flags = SPEC_DEV_ID_DISABLE_HT}, /* Russian customer -Azwave (8188CE-VAU g mode) */
|
#ifdef RTK_DMP_PLATFORM
|
{.idVendor = USB_VENDER_ID_REALTEK, .idProduct = 0x8111, .flags = SPEC_DEV_ID_ASSIGN_IFNAME}, /* Realtek 5G dongle for WiFi Display */
|
{.idVendor = 0x2019, .idProduct = 0xAB2D, .flags = SPEC_DEV_ID_ASSIGN_IFNAME}, /* PCI-Abocom 5G dongle for WiFi Display */
|
#endif /* RTK_DMP_PLATFORM */
|
{}
|
};
|
|
struct rtw_usb_drv {
|
struct usb_driver usbdrv;
|
int drv_registered;
|
};
|
|
struct rtw_usb_drv usb_drv = {
|
.usbdrv.name = (char *)DRV_NAME,
|
.usbdrv.probe = rtw_dev_probe,
|
.usbdrv.disconnect = rtw_dev_remove,
|
.usbdrv.id_table = rtw_usb_id_tbl,
|
.usbdrv.suspend = rtw_dev_suspend,
|
.usbdrv.resume = rtw_dev_resume,
|
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 22))
|
.usbdrv.reset_resume = rtw_dev_resume,
|
#endif
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19))
|
.usbdrv.drvwrap.driver.shutdown = rtw_dev_shutdown,
|
#else
|
.usbdrv.driver.shutdown = rtw_dev_shutdown,
|
#endif
|
};
|
|
static inline int RT_usb_endpoint_dir_in(const struct usb_endpoint_descriptor *epd)
|
{
|
return (epd->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN;
|
}
|
|
static inline int RT_usb_endpoint_dir_out(const struct usb_endpoint_descriptor *epd)
|
{
|
return (epd->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT;
|
}
|
|
static inline int RT_usb_endpoint_xfer_int(const struct usb_endpoint_descriptor *epd)
|
{
|
return (epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT;
|
}
|
|
static inline int RT_usb_endpoint_xfer_bulk(const struct usb_endpoint_descriptor *epd)
|
{
|
return (epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK;
|
}
|
|
static inline int RT_usb_endpoint_is_bulk_in(const struct usb_endpoint_descriptor *epd)
|
{
|
return RT_usb_endpoint_xfer_bulk(epd) && RT_usb_endpoint_dir_in(epd);
|
}
|
|
static inline int RT_usb_endpoint_is_bulk_out(const struct usb_endpoint_descriptor *epd)
|
{
|
return RT_usb_endpoint_xfer_bulk(epd) && RT_usb_endpoint_dir_out(epd);
|
}
|
|
static inline int RT_usb_endpoint_is_int_in(const struct usb_endpoint_descriptor *epd)
|
{
|
return RT_usb_endpoint_xfer_int(epd) && RT_usb_endpoint_dir_in(epd);
|
}
|
|
static inline int RT_usb_endpoint_num(const struct usb_endpoint_descriptor *epd)
|
{
|
return epd->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
|
}
|
|
static u8 rtw_init_intf_priv(struct dvobj_priv *dvobj)
|
{
|
u8 rst = _SUCCESS;
|
PUSB_DATA pusb_data = dvobj_to_usb(dvobj);
|
|
#ifdef CONFIG_USB_VENDOR_REQ_MUTEX
|
_rtw_mutex_init(&pusb_data->usb_vendor_req_mutex);
|
#endif
|
|
|
#ifdef CONFIG_USB_VENDOR_REQ_BUFFER_PREALLOC
|
pusb_data->usb_alloc_vendor_req_buf = rtw_zmalloc(MAX_USB_IO_CTL_SIZE);
|
if (pusb_data->usb_alloc_vendor_req_buf == NULL) {
|
RTW_INFO("alloc usb_vendor_req_buf failed... /n");
|
rst = _FAIL;
|
goto exit;
|
}
|
pusb_data->usb_vendor_req_buf =
|
(u8 *)N_BYTE_ALIGMENT((SIZE_PTR)(pusb_data->usb_alloc_vendor_req_buf), ALIGNMENT_UNIT);
|
exit:
|
#endif
|
|
return rst;
|
|
}
|
|
static u8 rtw_deinit_intf_priv(struct dvobj_priv *dvobj)
|
{
|
u8 rst = _SUCCESS;
|
PUSB_DATA pusb_data = dvobj_to_usb(dvobj);
|
|
#ifdef CONFIG_USB_VENDOR_REQ_BUFFER_PREALLOC
|
if (pusb_data->usb_vendor_req_buf)
|
rtw_mfree(pusb_data->usb_alloc_vendor_req_buf, MAX_USB_IO_CTL_SIZE);
|
#endif
|
|
#ifdef CONFIG_USB_VENDOR_REQ_MUTEX
|
_rtw_mutex_free(&pusb_data->usb_vendor_req_mutex);
|
#endif
|
|
return rst;
|
}
|
|
static unsigned int rtw_endpoint_max_bpi(struct usb_device *dev,
|
struct usb_host_endpoint *ep)
|
{
|
u16 psize;
|
u16 mult = 1;
|
int max_size_1 = 0, max_size_2 = 0;
|
|
switch (dev->speed) {
|
case USB_SPEED_SUPER:
|
#if ((LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 20) && LINUX_VERSION_CODE < KERNEL_VERSION(4, 5, 0)) ||\
|
LINUX_VERSION_CODE >= KERNEL_VERSION(4, 6, 0))
|
case USB_SPEED_SUPER_PLUS:
|
max_size_1 = le16_to_cpu(ep->ss_ep_comp.wBytesPerInterval);
|
max_size_2 = usb_endpoint_maxp(&ep->desc);
|
#endif
|
break;
|
case USB_SPEED_HIGH:
|
psize = usb_endpoint_maxp(&ep->desc);
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 71))
|
mult = usb_endpoint_maxp_mult(&ep->desc);
|
max_size_1 = psize * mult;
|
#endif
|
max_size_2 = usb_endpoint_maxp(&ep->desc);
|
break;
|
case USB_SPEED_WIRELESS:
|
max_size_1 = max_size_2 = usb_endpoint_maxp(&ep->desc);
|
break;
|
default:
|
max_size_1 = max_size_2 = usb_endpoint_maxp(&ep->desc);
|
break;
|
|
}
|
RTW_INFO("USB EP MAX_PKT_SZ:%d-%d\n",max_size_1, max_size_2);
|
return max_size_1;
|
}
|
|
static struct dvobj_priv *usb_dvobj_init(struct usb_interface *usb_intf,
|
const struct usb_device_id *pdid)
|
{
|
int i;
|
int status = _FAIL;
|
struct dvobj_priv *dvobj;
|
struct usb_device_descriptor *pdev_desc;
|
struct usb_host_config *phost_conf;
|
struct usb_config_descriptor *pconf_desc;
|
struct usb_host_interface *phost_iface;
|
struct usb_interface_descriptor *piface_desc;
|
struct usb_host_endpoint *phost_endp;
|
struct usb_endpoint_descriptor *pendp_desc;
|
struct usb_device *pusbd;
|
PUSB_DATA pusb_data;
|
|
dvobj = devobj_init();
|
if (dvobj == NULL)
|
goto exit;
|
|
pusb_data = dvobj_to_usb(dvobj);
|
|
pusb_data->pusbintf = usb_intf;
|
pusbd = pusb_data->pusbdev = interface_to_usbdev(usb_intf);
|
usb_set_intfdata(usb_intf, dvobj);
|
|
pusb_data->RtNumInPipes = 0;
|
pusb_data->RtNumOutPipes = 0;
|
|
pdev_desc = &pusbd->descriptor;
|
|
#if 0
|
RTW_INFO("\n8712_usb_device_descriptor:\n");
|
RTW_INFO("bLength=%x\n", pdev_desc->bLength);
|
RTW_INFO("bDescriptorType=%x\n", pdev_desc->bDescriptorType);
|
RTW_INFO("bcdUSB=%x\n", pdev_desc->bcdUSB);
|
RTW_INFO("bDeviceClass=%x\n", pdev_desc->bDeviceClass);
|
RTW_INFO("bDeviceSubClass=%x\n", pdev_desc->bDeviceSubClass);
|
RTW_INFO("bDeviceProtocol=%x\n", pdev_desc->bDeviceProtocol);
|
RTW_INFO("bMaxPacketSize0=%x\n", pdev_desc->bMaxPacketSize0);
|
RTW_INFO("idVendor=%x\n", pdev_desc->idVendor);
|
RTW_INFO("idProduct=%x\n", pdev_desc->idProduct);
|
RTW_INFO("bcdDevice=%x\n", pdev_desc->bcdDevice);
|
RTW_INFO("iManufacturer=%x\n", pdev_desc->iManufacturer);
|
RTW_INFO("iProduct=%x\n", pdev_desc->iProduct);
|
RTW_INFO("iSerialNumber=%x\n", pdev_desc->iSerialNumber);
|
RTW_INFO("bNumConfigurations=%x\n", pdev_desc->bNumConfigurations);
|
#endif
|
|
phost_conf = pusbd->actconfig;
|
pconf_desc = &phost_conf->desc;
|
|
#if 0
|
RTW_INFO("\n[USB] configuration_descriptor:\n");
|
RTW_INFO("bNumInterfaces=%x\n", pconf_desc->bNumInterfaces);
|
RTW_INFO("bLength=%x\n", pconf_desc->bLength);
|
RTW_INFO("bDescriptorType=%x\n", pconf_desc->bDescriptorType);
|
RTW_INFO("wTotalLength=%x\n", pconf_desc->wTotalLength);
|
RTW_INFO("bConfigurationValue=%x\n", pconf_desc->bConfigurationValue);
|
RTW_INFO("iConfiguration=%x\n", pconf_desc->iConfiguration);
|
RTW_INFO("bmAttributes=%x\n", pconf_desc->bmAttributes);
|
RTW_INFO("bMaxPower=%x\n", pconf_desc->bMaxPower);
|
#endif
|
|
phost_iface = &usb_intf->altsetting[0];
|
piface_desc = &phost_iface->desc;
|
#if 0
|
RTW_INFO("\n[USB] usb_interface_descriptor:\n");
|
RTW_INFO("bInterfaceNumber=%x\n", piface_desc->bInterfaceNumber);
|
RTW_INFO("bAlternateSetting=%x\n", piface_desc->bAlternateSetting);
|
RTW_INFO("bLength=%x\n", piface_desc->bLength);
|
RTW_INFO("bDescriptorType=%x\n", piface_desc->bDescriptorType);
|
RTW_INFO("bNumEndpoints=%x\n", piface_desc->bNumEndpoints);
|
RTW_INFO("bInterfaceClass=%x\n", piface_desc->bInterfaceClass);
|
RTW_INFO("bInterfaceSubClass=%x\n", piface_desc->bInterfaceSubClass);
|
RTW_INFO("bInterfaceProtocol=%x\n", piface_desc->bInterfaceProtocol);
|
RTW_INFO("iInterface=%x\n", piface_desc->iInterface);
|
#endif
|
|
pusb_data->nr_endpoint = piface_desc->bNumEndpoints;
|
if (pusb_data->nr_endpoint > MAX_ENDPOINT_NUM) {
|
RTW_ERR("USB EP_Number : %d > RT DEF-MAX_EP_NUM :%d\n",
|
pusb_data->nr_endpoint, MAX_ENDPOINT_NUM);
|
rtw_warn_on(1);
|
}
|
|
/* RTW_INFO("\ndump usb_endpoint_descriptor:\n"); */
|
|
for (i = 0; i < pusb_data->nr_endpoint; i++) {
|
phost_endp = phost_iface->endpoint + i;
|
if (phost_endp) {
|
pendp_desc = &phost_endp->desc;
|
|
RTW_INFO("\nusb_endpoint_descriptor(%d):\n", i);
|
RTW_INFO("bLength=%x\n", pendp_desc->bLength);
|
RTW_INFO("bDescriptorType=%x\n", pendp_desc->bDescriptorType);
|
RTW_INFO("bEndpointAddress=%x\n", pendp_desc->bEndpointAddress);
|
/* RTW_INFO("bmAttributes=%x\n",pendp_desc->bmAttributes); */
|
RTW_INFO("wMaxPacketSize=%d\n", le16_to_cpu(pendp_desc->wMaxPacketSize));
|
RTW_INFO("bInterval=%x\n", pendp_desc->bInterval);
|
/* RTW_INFO("bRefresh=%x\n",pendp_desc->bRefresh); */
|
/* RTW_INFO("bSynchAddress=%x\n",pendp_desc->bSynchAddress); */
|
|
if (RT_usb_endpoint_is_bulk_in(pendp_desc)) {
|
RTW_INFO("RT_usb_endpoint_is_bulk_in = %x\n", RT_usb_endpoint_num(pendp_desc));
|
pusb_data->RtInPipe[pusb_data->RtNumInPipes] = RT_usb_endpoint_num(pendp_desc);
|
pusb_data->inpipe_type[pusb_data->RtNumInPipes] = REALTEK_USB_BULK_IN_EP_IDX;
|
pusb_data->RtNumInPipes++;
|
RTW_INFO("USB#%d bulkin size:%d", pusb_data->RtNumOutPipes,
|
rtw_endpoint_max_bpi(pusbd, phost_endp));
|
} else if (RT_usb_endpoint_is_int_in(pendp_desc)) {
|
RTW_INFO("RT_usb_endpoint_is_int_in = %x, Interval = %x\n", RT_usb_endpoint_num(pendp_desc), pendp_desc->bInterval);
|
pusb_data->RtInPipe[pusb_data->RtNumInPipes] = RT_usb_endpoint_num(pendp_desc);
|
pusb_data->inpipe_type[pusb_data->RtNumInPipes] = REALTEK_USB_IN_INT_EP_IDX;
|
pusb_data->RtNumInPipes++;
|
} else if (RT_usb_endpoint_is_bulk_out(pendp_desc)) {
|
RTW_INFO("RT_usb_endpoint_is_bulk_out = %x\n", RT_usb_endpoint_num(pendp_desc));
|
pusb_data->RtOutPipe[pusb_data->RtNumOutPipes] = RT_usb_endpoint_num(pendp_desc);
|
RTW_INFO("USB#%d bulkout size:%d", pusb_data->RtNumOutPipes,
|
rtw_endpoint_max_bpi(pusbd, phost_endp));
|
pusb_data->RtNumOutPipes++;
|
}
|
/*pusb_data->ep_num[i] = RT_usb_endpoint_num(pendp_desc);*/
|
}
|
}
|
|
RTW_INFO("nr_endpoint=%d, in_num=%d, out_num=%d\n\n",
|
pusb_data->nr_endpoint, pusb_data->RtNumInPipes, pusb_data->RtNumOutPipes);
|
|
switch (pusbd->speed) {
|
case USB_SPEED_LOW:
|
case USB_SPEED_FULL:
|
RTW_INFO("USB_SPEED_FULL\n");
|
pusb_data->usb_speed = RTW_USB_SPEED_FULL;/*U2- 1.1 - 1.5MBs*/
|
pusb_data->usb_bulkout_size = USB_FULL_SPEED_BULK_SIZE;
|
break;
|
case USB_SPEED_HIGH:
|
RTW_INFO("USB_SPEED_HIGH\n");
|
pusb_data->usb_speed = RTW_USB_SPEED_HIGH;/*U2- 2.1 - 60MBs*/
|
pusb_data->usb_bulkout_size = USB_HIGH_SPEED_BULK_SIZE;
|
break;
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31))
|
case USB_SPEED_SUPER:
|
RTW_INFO("USB_SPEED_SUPER\n");
|
pusb_data->usb_speed = RTW_USB_SPEED_SUPER;/*U3- 3.0 - 640MBs*/
|
pusb_data->usb_bulkout_size = USB_SUPER_SPEED_BULK_SIZE;
|
break;
|
#if ((LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 20) && LINUX_VERSION_CODE < KERNEL_VERSION(4, 5, 0)) ||\
|
LINUX_VERSION_CODE >= KERNEL_VERSION(4, 6, 0))
|
case USB_SPEED_SUPER_PLUS:
|
RTW_INFO("USB_SPEED_SUPER_PLUS\n");
|
pusb_data->usb_speed = RTW_USB_SPEED_SUPER_10G;/*U3- 3.1 - 1280MBs*/
|
pusb_data->usb_bulkout_size = USB_SUPER_SPEED_BULK_SIZE;
|
break;
|
#endif
|
#endif
|
default:
|
RTW_INFO("USB_SPEED_UNKNOWN(%d)\n", pusbd->speed);
|
pusb_data->usb_speed = RTW_USB_SPEED_UNKNOWN;
|
break;
|
}
|
|
if (pusb_data->usb_speed == RTW_USB_SPEED_UNKNOWN) {
|
RTW_INFO("UNKNOWN USB SPEED MODE, ERROR !!!\n");
|
goto free_dvobj;
|
}
|
|
if (rtw_init_intf_priv(dvobj) == _FAIL) {
|
goto free_dvobj;
|
}
|
|
/*step 1-1., get chip_id via driver_info*/
|
dvobj->interface_type = RTW_HCI_USB;
|
dvobj->ic_id = pdid->driver_info;
|
dvobj->intf_ops = &usb_ops;
|
|
/* .3 misc */
|
rtw_reset_continual_io_error(dvobj);
|
|
usb_get_dev(pusbd);
|
|
status = _SUCCESS;
|
|
free_dvobj:
|
if (status != _SUCCESS && dvobj) {
|
usb_set_intfdata(usb_intf, NULL);
|
devobj_deinit(dvobj);
|
dvobj = NULL;
|
}
|
exit:
|
return dvobj;
|
}
|
|
static void usb_dvobj_deinit(struct usb_interface *usb_intf)
|
{
|
struct dvobj_priv *dvobj = usb_get_intfdata(usb_intf);
|
|
usb_set_intfdata(usb_intf, NULL);
|
if (dvobj) {
|
rtw_deinit_intf_priv(dvobj);
|
devobj_deinit(dvobj);
|
}
|
|
/* RTW_INFO("%s %d\n", __func__, ATOMIC_READ(&usb_intf->dev.kobj.kref.refcount)); */
|
usb_put_dev(interface_to_usbdev(usb_intf));
|
|
}
|
|
static int usb_reprobe_switch_usb_mode(_adapter *adapter)
|
{
|
struct registry_priv *registry_par = &adapter->registrypriv;
|
struct dvobj_priv *dvobj = adapter_to_dvobj(adapter);
|
PUSB_DATA pusb_data = dvobj_to_usb(dvobj);
|
u8 ret = _FALSE;
|
|
/* registry not allow driver to switch usb mode */
|
if (registry_par->switch_usb_mode == 0)
|
goto exit;
|
else if (registry_par->switch_usb_mode == 1 && pusb_data->usb_speed < RTW_USB_SPEED_SUPER)
|
ret = rtw_phl_cmd_force_usb_switch(dvobj->phl, RTW_USB_SPEED_SUPER, HW_BAND_0, PHL_CMD_DIRECTLY, 0);
|
else if (registry_par->switch_usb_mode == 2 && pusb_data->usb_speed >= RTW_USB_SPEED_SUPER)
|
ret = rtw_phl_cmd_force_usb_switch(dvobj->phl, RTW_USB_SPEED_HIGH, HW_BAND_0, PHL_CMD_DIRECTLY, 0);
|
exit:
|
return ret;
|
}
|
|
|
static void process_spec_devid(const struct usb_device_id *pdid)
|
{
|
u16 vid, pid;
|
u32 flags;
|
int i;
|
int num = sizeof(specific_device_id_tbl) / sizeof(struct specific_device_id);
|
|
for (i = 0; i < num; i++) {
|
vid = specific_device_id_tbl[i].idVendor;
|
pid = specific_device_id_tbl[i].idProduct;
|
flags = specific_device_id_tbl[i].flags;
|
|
#ifdef CONFIG_80211N_HT
|
if ((pdid->idVendor == vid) && (pdid->idProduct == pid) && (flags & SPEC_DEV_ID_DISABLE_HT)) {
|
/*GEORGIA_TODO_FIXIT_MULTI_IC*/
|
rtw_ht_enable = 0;
|
rtw_bw_mode = 0;
|
rtw_ampdu_enable = 0;
|
}
|
#endif
|
|
#ifdef RTK_DMP_PLATFORM
|
/* Change the ifname to wlan10 when PC side WFD dongle plugin on DMP platform. */
|
/* It is used to distinguish between normal and PC-side wifi dongle/module. */
|
if ((pdid->idVendor == vid) && (pdid->idProduct == pid) && (flags & SPEC_DEV_ID_ASSIGN_IFNAME)) {
|
extern char *ifname;
|
strncpy(ifname, "wlan10", 6);
|
/* RTW_INFO("%s()-%d: ifname=%s, vid=%04X, pid=%04X\n", __FUNCTION__, __LINE__, ifname, vid, pid); */
|
}
|
#endif /* RTK_DMP_PLATFORM */
|
|
}
|
}
|
|
static int rtw_dev_suspend(struct usb_interface *pusb_intf, pm_message_t message)
|
{
|
struct dvobj_priv *dvobj;
|
struct pwrctrl_priv *pwrpriv;
|
struct debug_priv *pdbgpriv;
|
_adapter *padapter;
|
int ret = 0;
|
|
|
dvobj = usb_get_intfdata(pusb_intf);
|
pwrpriv = dvobj_to_pwrctl(dvobj);
|
pdbgpriv = &dvobj->drv_dbg;
|
padapter = dvobj_get_primary_adapter(dvobj);
|
|
if (pwrpriv->bInSuspend == _TRUE) {
|
RTW_INFO("%s bInSuspend = %d\n", __FUNCTION__, pwrpriv->bInSuspend);
|
pdbgpriv->dbg_suspend_error_cnt++;
|
goto exit;
|
}
|
|
ret = rtw_suspend_common(padapter);
|
|
exit:
|
return ret;
|
}
|
|
static int rtw_resume_process(_adapter *padapter)
|
{
|
int ret;
|
struct pwrctrl_priv *pwrpriv = adapter_to_pwrctl(padapter);
|
struct dvobj_priv *pdvobj = padapter->dvobj;
|
struct debug_priv *pdbgpriv = &pdvobj->drv_dbg;
|
|
|
if (pwrpriv->bInSuspend == _FALSE) {
|
pdbgpriv->dbg_resume_error_cnt++;
|
RTW_INFO("%s bInSuspend = %d\n", __FUNCTION__, pwrpriv->bInSuspend);
|
return -1;
|
}
|
|
/*
|
* Due to usb wow suspend flow will cancel read/write port via intf_stop and
|
* bReadPortCancel and bWritePortCancel are set _TRUE in intf_stop.
|
* But they will not be clear in intf_start during wow resume flow.
|
* It should move to os_intf in the feature.
|
*/
|
RTW_ENABLE_FUNC(pdvobj, DF_RX_BIT);
|
RTW_ENABLE_FUNC(pdvobj, DF_TX_BIT);
|
|
ret = rtw_resume_common(padapter);
|
|
return ret;
|
}
|
|
static int rtw_dev_resume(struct usb_interface *pusb_intf)
|
{
|
struct dvobj_priv *dvobj;
|
struct pwrctrl_priv *pwrpriv;
|
struct debug_priv *pdbgpriv;
|
_adapter *padapter;
|
struct mlme_ext_priv *pmlmeext;
|
int ret = 0;
|
|
|
dvobj = usb_get_intfdata(pusb_intf);
|
pwrpriv = dvobj_to_pwrctl(dvobj);
|
pdbgpriv = &dvobj->drv_dbg;
|
padapter = dvobj_get_primary_adapter(dvobj);
|
pmlmeext = &padapter->mlmeextpriv;
|
|
RTW_INFO("==> %s (%s:%d)\n", __FUNCTION__, current->comm, current->pid);
|
pdbgpriv->dbg_resume_cnt++;
|
|
|
if (pwrpriv->wowlan_mode || pwrpriv->wowlan_ap_mode) {
|
rtw_resume_lock_suspend();
|
ret = rtw_resume_process(padapter);
|
rtw_resume_unlock_suspend();
|
} else {
|
#ifdef CONFIG_RESUME_IN_WORKQUEUE
|
rtw_resume_in_workqueue(pwrpriv);
|
#else
|
if (rtw_is_earlysuspend_registered(pwrpriv)) {
|
/* jeff: bypass resume here, do in late_resume */
|
rtw_set_do_late_resume(pwrpriv, _TRUE);
|
} else {
|
rtw_resume_lock_suspend();
|
ret = rtw_resume_process(padapter);
|
rtw_resume_unlock_suspend();
|
}
|
#endif
|
}
|
|
pmlmeext->last_scan_time = rtw_get_current_time();
|
RTW_INFO("<======== %s return %d\n", __FUNCTION__, ret);
|
|
return ret;
|
}
|
|
|
/*
|
* drv_init() - a device potentially for us
|
*
|
* notes: drv_init() is called when the bus driver has located a card for us to support.
|
* We accept the new device by returning 0.
|
*/
|
_adapter *rtw_usb_primary_adapter_init(struct dvobj_priv *dvobj,
|
struct usb_interface *pusb_intf)
|
{
|
_adapter *padapter = NULL;
|
int status = _FAIL;
|
u8 hw_mac_addr[ETH_ALEN] = {0};
|
|
padapter = (_adapter *)rtw_zvmalloc(sizeof(*padapter));
|
if (padapter == NULL)
|
goto exit;
|
|
padapter->dvobj = dvobj;
|
|
/*registry_priv*/
|
if (rtw_load_registry(padapter) != _SUCCESS)
|
goto free_adapter;
|
|
dvobj->padapters[dvobj->iface_nums++] = padapter;
|
padapter->iface_id = IFACE_ID0;
|
|
/* set adapter_type/iface type for primary padapter */
|
padapter->isprimary = _TRUE;
|
padapter->adapter_type = PRIMARY_ADAPTER;
|
|
/* step 5. */
|
if (rtw_init_drv_sw(padapter) == _FAIL) {
|
goto free_adapter;
|
}
|
|
#ifdef CONFIG_PM
|
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 18))
|
if (dvobj_to_pwrctl(dvobj)->bSupportRemoteWakeup) {
|
dvobj_to_usb(dvobj)->pusbdev->do_remote_wakeup = 1;
|
pusb_intf->needs_remote_wakeup = 1;
|
device_init_wakeup(&pusb_intf->dev, 1);
|
RTW_INFO("pwrctrlpriv.bSupportRemoteWakeup~~~~~~\n");
|
RTW_INFO("pwrctrlpriv.bSupportRemoteWakeup~~~[%d]~~~\n",
|
device_may_wakeup(&pusb_intf->dev));
|
}
|
#endif
|
#endif
|
/* 2012-07-11 Move here to prevent the 8723AS-VAU BT auto suspend influence */
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 33))
|
if (usb_autopm_get_interface(pusb_intf) < 0)
|
RTW_INFO("can't get autopm:\n");
|
#endif
|
#ifdef CONFIG_BTC
|
dvobj_to_pwrctl(dvobj)->autopm_cnt = 1;
|
#endif
|
|
/* get mac addr */
|
rtw_hw_get_mac_addr(dvobj, hw_mac_addr);
|
/* set mac addr */
|
rtw_macaddr_cfg(adapter_mac_addr(padapter), hw_mac_addr);
|
|
RTW_INFO("bDriverStopped:%s, bSurpriseRemoved:%s, netif_up:%d, hw_init_completed:%d\n"
|
, dev_is_drv_stopped(dvobj) ? "True" : "False"
|
, dev_is_surprise_removed(dvobj) ? "True" : "False"
|
, padapter->netif_up
|
, rtw_hw_get_init_completed(dvobj)
|
);
|
|
status = _SUCCESS;
|
|
free_adapter:
|
if (status != _SUCCESS && padapter) {
|
rtw_vmfree((u8 *)padapter, sizeof(*padapter));
|
padapter = NULL;
|
}
|
exit:
|
return padapter;
|
}
|
|
static void rtw_usb_primary_adapter_deinit(_adapter *padapter)
|
{
|
RTW_INFO(FUNC_ADPT_FMT"\n", FUNC_ADPT_ARG(padapter));
|
|
#ifdef CONFIG_BTC
|
if (1 == adapter_to_pwrctl(padapter)->autopm_cnt) {
|
struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
|
struct dvobj_priv *dvobj = adapter_to_dvobj(padapter);
|
PUSB_DATA usb_data = dvobj_to_usb(dvobj);
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 33))
|
usb_autopm_put_interface(usb_data->pusbintf);
|
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20))
|
usb_autopm_enable(usb_data->pusbintf);
|
#else
|
usb_autosuspend_device(usb_data->pusbdev, 1);
|
#endif
|
adapter_to_pwrctl(padapter)->autopm_cnt--;
|
}
|
#endif
|
|
rtw_free_drv_sw(padapter);
|
|
/* TODO: use rtw_os_ndevs_deinit instead at the first stage of driver's dev deinit function */
|
rtw_os_ndev_free(padapter);
|
|
rtw_vmfree((u8 *)padapter, sizeof(_adapter));
|
|
}
|
|
static void rtw_usb_drop_all_phl_rx_pkt(struct dvobj_priv *dvobj)
|
{
|
u16 rx_pkt_num = 0;
|
struct rtw_recv_pkt *rx_req = NULL;
|
|
rx_pkt_num = rtw_phl_query_new_rx_num(GET_PHL_INFO(dvobj));
|
|
if (rx_pkt_num) {
|
RTW_INFO("%s, rx_pkt not empty !!(%d)\n", __func__, rx_pkt_num);
|
|
while (rx_pkt_num--) {
|
rx_req = rtw_phl_query_rx_pkt(GET_PHL_INFO(dvobj));
|
if (rx_req) {
|
struct sk_buff *skb = rx_req->os_priv;
|
|
rtw_phl_return_rxbuf(GET_PHL_INFO(dvobj), (u8 *)rx_req);
|
rtw_skb_free(skb);
|
}
|
}
|
}
|
}
|
|
static int rtw_dev_probe(struct usb_interface *pusb_intf, const struct usb_device_id *pdid)
|
{
|
_adapter *padapter = NULL;
|
struct dvobj_priv *dvobj;
|
|
RTW_INFO("+%s\n", __func__);
|
|
/* step 0. */
|
process_spec_devid(pdid);
|
|
/* Initialize dvobj_priv */
|
dvobj = usb_dvobj_init(pusb_intf, pdid);
|
if (dvobj == NULL) {
|
RTW_ERR("usb_dvobj_init Failed!\n");
|
goto exit;
|
}
|
|
if (devobj_trx_resource_init(dvobj) == _FAIL)
|
goto free_dvobj;
|
|
/*init hw - register and get chip-info */
|
if (rtw_hw_init(dvobj) == _FAIL) {
|
RTW_ERR("rtw_hw_init Failed!\n");
|
goto free_trx_reso;
|
}
|
|
padapter = rtw_usb_primary_adapter_init(dvobj, pusb_intf);
|
if (padapter == NULL) {
|
RTW_ERR("rtw_usb_primary_adapter_init Failed!\n");
|
goto free_hw;
|
}
|
|
if (usb_reprobe_switch_usb_mode(padapter) == _TRUE) {
|
RTW_ERR("usb_reprobe_switch_usb_mode Failed!\n");
|
goto free_if_prim;
|
}
|
|
#ifdef CONFIG_CONCURRENT_MODE
|
if (rtw_drv_add_vir_ifaces(dvobj) == _FAIL)
|
goto free_if_vir;
|
#endif
|
|
/*init data of dvobj from registary and ic spec*/
|
if (devobj_data_init(dvobj) == _FAIL) {
|
RTW_ERR("devobj_data_init Failed!\n");
|
goto free_devobj_data;
|
}
|
|
#ifdef CONFIG_GLOBAL_UI_PID
|
if (ui_pid[1] != 0) {
|
RTW_INFO("ui_pid[1]:%d\n", ui_pid[1]);
|
rtw_signal_process(ui_pid[1], SIGUSR2);
|
}
|
#endif
|
|
/* dev_alloc_name && register_netdev */
|
if (rtw_os_ndevs_init(dvobj) != _SUCCESS) {
|
RTW_ERR("rtw_os_ndevs_init Failed!\n");
|
goto free_devobj_data;
|
}
|
|
#ifdef CONFIG_HOSTAPD_MLME
|
hostapd_mode_init(padapter);
|
#endif
|
RTW_INFO("-%s success\n", __func__);
|
return 0; /*_SUCCESS*/
|
|
free_devobj_data:
|
devobj_data_deinit(dvobj);
|
|
#ifdef CONFIG_CONCURRENT_MODE
|
free_if_vir:
|
rtw_drv_stop_vir_ifaces(dvobj);
|
rtw_drv_free_vir_ifaces(dvobj);
|
#endif
|
|
free_if_prim:
|
if (padapter)
|
rtw_usb_primary_adapter_deinit(padapter);
|
free_hw:
|
rtw_hw_deinit(dvobj);
|
|
free_trx_reso:
|
devobj_trx_resource_deinit(dvobj);
|
|
free_dvobj:
|
usb_dvobj_deinit(pusb_intf);
|
exit:
|
return -ENODEV;
|
}
|
|
/*
|
* dev_remove() - our device is being removed
|
*/
|
/* rmmod module & unplug(SurpriseRemoved) will call r871xu_dev_remove() => how to recognize both */
|
static void rtw_dev_remove(struct usb_interface *pusb_intf)
|
{
|
struct dvobj_priv *dvobj = usb_get_intfdata(pusb_intf);
|
#if defined(CONFIG_HAS_EARLYSUSPEND) || defined(CONFIG_ANDROID_POWER)
|
struct pwrctrl_priv *pwrctl = dvobj_to_pwrctl(dvobj);
|
#endif
|
_adapter *padapter = dvobj_get_primary_adapter(dvobj);
|
|
RTW_INFO("+%s\n", __func__);
|
|
dvobj->processing_dev_remove = _TRUE;
|
|
/* TODO: use rtw_os_ndevs_deinit instead at the first stage of driver's dev deinit function */
|
rtw_os_ndevs_unregister(dvobj);
|
|
if (usb_drv.drv_registered == _TRUE) {
|
/* RTW_INFO("r871xu_dev_remove():padapter->bSurpriseRemoved == _TRUE\n"); */
|
dev_set_surprise_removed(dvobj);
|
}
|
|
|
#if defined(CONFIG_HAS_EARLYSUSPEND) || defined(CONFIG_ANDROID_POWER)
|
rtw_unregister_early_suspend(pwrctl);
|
#endif
|
#if 0 /*GEORGIA_TODO_FIXIT*/
|
if (GET_PHL_COM(dvobj)->fw_ready == _TRUE) {
|
rtw_pm_set_ips(padapter, IPS_NONE);
|
rtw_pm_set_lps(padapter, PM_PS_MODE_ACTIVE);
|
|
LeaveAllPowerSaveMode(padapter);
|
}
|
#endif
|
dev_set_drv_stopped(adapter_to_dvobj(padapter)); /*for stop thread*/
|
#if 0 /*#ifdef CONFIG_CORE_CMD_THREAD*/
|
rtw_stop_cmd_thread(padapter);
|
#endif
|
#ifdef CONFIG_CONCURRENT_MODE
|
rtw_drv_stop_vir_ifaces(dvobj);
|
#endif /* CONFIG_CONCURRENT_MODE */
|
|
rtw_drv_stop_prim_iface(padapter);
|
|
if (rtw_hw_is_init_completed(dvobj))
|
rtw_hw_stop(dvobj);
|
dev_set_surprise_removed(dvobj);
|
|
rtw_usb_drop_all_phl_rx_pkt(dvobj);
|
|
rtw_usb_primary_adapter_deinit(padapter);
|
|
#ifdef CONFIG_CONCURRENT_MODE
|
rtw_drv_free_vir_ifaces(dvobj);
|
#endif /* CONFIG_CONCURRENT_MODE */
|
|
rtw_hw_deinit(dvobj);
|
devobj_data_deinit(dvobj);
|
devobj_trx_resource_deinit(dvobj);
|
usb_dvobj_deinit(pusb_intf);
|
|
RTW_INFO("-%s done\n", __func__);
|
return;
|
|
}
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24))
|
extern int console_suspend_enabled;
|
#endif
|
|
static int __init rtw_drv_entry(void)
|
{
|
int ret = 0;
|
|
RTW_PRINT("module init start\n");
|
dump_drv_version(RTW_DBGDUMP);
|
#ifdef BTCOEXVERSION
|
RTW_PRINT(DRV_NAME" BT-Coex version = %s\n", BTCOEXVERSION);
|
#endif /* BTCOEXVERSION */
|
|
ret = platform_wifi_power_on();
|
if (ret != 0) {
|
RTW_INFO("%s: power on failed!!(%d)\n", __FUNCTION__, ret);
|
ret = -1;
|
goto exit;
|
}
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24))
|
/* console_suspend_enabled=0; */
|
#endif
|
|
usb_drv.drv_registered = _TRUE;
|
rtw_suspend_lock_init();
|
rtw_drv_proc_init();
|
rtw_nlrtw_init();
|
rtw_ndev_notifier_register();
|
rtw_inetaddr_notifier_register();
|
|
ret = usb_register(&usb_drv.usbdrv);
|
|
if (ret != 0) {
|
usb_drv.drv_registered = _FALSE;
|
rtw_suspend_lock_uninit();
|
rtw_drv_proc_deinit();
|
rtw_nlrtw_deinit();
|
rtw_ndev_notifier_unregister();
|
rtw_inetaddr_notifier_unregister();
|
goto exit;
|
}
|
|
exit:
|
RTW_PRINT("module init ret=%d\n", ret);
|
return ret;
|
}
|
|
static void __exit rtw_drv_halt(void)
|
{
|
RTW_PRINT("module exit start\n");
|
|
usb_drv.drv_registered = _FALSE;
|
|
usb_deregister(&usb_drv.usbdrv);
|
|
platform_wifi_power_off();
|
|
rtw_suspend_lock_uninit();
|
rtw_drv_proc_deinit();
|
rtw_nlrtw_deinit();
|
rtw_ndev_notifier_unregister();
|
rtw_inetaddr_notifier_unregister();
|
|
RTW_PRINT("module exit success\n");
|
|
rtw_mstat_dump(RTW_DBGDUMP);
|
}
|
|
module_init(rtw_drv_entry);
|
module_exit(rtw_drv_halt);
|