blob: 770f4e727522ee5e7f75e8a2af64f0821adfac86 [file] [log] [blame]
/*
* Copyright (c) 2016-2018 The Linux Foundation. All rights reserved.
*
* Permission to use, copy, modify, and/or 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 "pld_usb.h"
#include "pld_internal.h"
#include <linux/atomic.h>
#include <linux/usb.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/err.h>
#include <linux/list.h>
#ifdef CONFIG_PLD_USB_CNSS
#include <net/cnss2.h>
#endif
#define VENDOR_ATHR 0x0CF3
static struct usb_device_id pld_usb_id_table[] = {
{USB_DEVICE_AND_INTERFACE_INFO(VENDOR_ATHR, 0x9378, 0xFF, 0xFF, 0xFF)},
{USB_DEVICE_AND_INTERFACE_INFO(VENDOR_ATHR, 0x9379, 0xFF, 0xFF, 0xFF)},
{} /* Terminating entry */
};
atomic_t pld_usb_reg_done;
/**
* pld_usb_probe() - pld_usb_probe
* @interface: pointer to usb_interface structure
* @id: pointer to usb_device_id obtained from the enumerated device
* Return: int 0 on success and errno on failure.
*/
static int pld_usb_probe(struct usb_interface *interface,
const struct usb_device_id *id)
{
struct usb_device *pdev = interface_to_usbdev(interface);
struct pld_context *pld_context;
int ret = 0;
pld_context = pld_get_global_context();
if (!pld_context) {
ret = -ENODEV;
goto out;
}
ret = pld_add_dev(pld_context, &pdev->dev, PLD_BUS_TYPE_USB);
if (ret)
goto out;
ret = pld_context->ops->probe(&pdev->dev,
PLD_BUS_TYPE_USB, interface, (void *)id);
if (ret != 0) {
pr_err("%s, probe returned %d", __func__, ret);
atomic_set(&pld_usb_reg_done, false);
} else {
atomic_set(&pld_usb_reg_done, true);
}
out:
return ret;
}
/**
* pld_usb_remove() - Remove function for USB device
* @interface: pointer to usb_interface for the usb device being removed
*
* Return: void
*/
static void pld_usb_remove(struct usb_interface *interface)
{
struct usb_device *pdev = interface_to_usbdev(interface);
struct pld_context *pld_context;
pld_context = pld_get_global_context();
if (!pld_context)
return;
if (atomic_read(&pld_usb_reg_done) != true) {
pr_info("%s: already de-registered!\n", __func__);
return;
}
pld_context->ops->remove(&pdev->dev, PLD_BUS_TYPE_USB);
pld_del_dev(pld_context, &pdev->dev);
atomic_set(&pld_usb_reg_done, false);
pr_info("%s: done!\n", __func__);
}
/**
* pld_usb_suspend() - Suspend callback function for power management
* @interface: pointer to usb_interface for the usb device
* @state: power state
*
* This function is to suspend the PCIE device when power management is
* enabled.
*
* Return: void
*/
static int pld_usb_suspend(struct usb_interface *interface,
pm_message_t state)
{
struct usb_device *pdev = interface_to_usbdev(interface);
struct pld_context *pld_context;
pld_context = pld_get_global_context();
return pld_context->ops->suspend(&pdev->dev, PLD_BUS_TYPE_USB, state);
}
/**
* pld_usb_resume() - Resume callback function for power management
* @interface: pointer to usb_interface for the usb device
*
* This function is to resume the USB device when power management is
* enabled.
*
* Return: void
*/
static int pld_usb_resume(struct usb_interface *interface)
{
struct pld_context *pld_context;
struct usb_device *pdev = interface_to_usbdev(interface);
pld_context = pld_get_global_context();
return pld_context->ops->resume(&pdev->dev, PLD_BUS_TYPE_USB);
}
/**
* pld_usb_reset_resume() - pld_usb_reset_resume
* @interface: pointer to usb_interface for the usb device
*
* Return: void
*/
static int pld_usb_reset_resume(struct usb_interface *interface)
{
struct pld_context *pld_context;
struct usb_device *pdev = interface_to_usbdev(interface);
pld_context = pld_get_global_context();
return pld_context->ops->reset_resume(&pdev->dev, PLD_BUS_TYPE_USB);
}
#ifdef CONFIG_PLD_USB_CNSS
struct cnss_usb_wlan_driver pld_usb_ops = {
.name = "pld_usb_cnss",
.id_table = pld_usb_id_table,
.probe = pld_usb_probe,
.disconnect = pld_usb_remove,
#ifdef CONFIG_PM
.suspend = pld_usb_suspend,
.resume = pld_usb_resume,
.reset_resume = pld_usb_reset_resume,
#endif
/**
* pld_usb_register_driver() - registration routine for wlan usb drive
*
* Return: int negative error code on failure and 0 on success
*/
int pld_usb_register_driver(void)
{
pr_info("%s usb_register\n", __func__);
return cnss_wlan_register_driver(&pld_usb_ops);
}
/**
* pld_usb_unregister_driver() - de-registration routine for wlan usb driver
*
* Return: void
*/
void pld_usb_unregister_driver(void)
{
cnss_wlan_register_driver(&pld_usb_ops);
pr_info("%s usb_deregister done!\n", __func__);
}
#else /* CONFIG_PLD_USB_CNSS */
struct usb_driver pld_usb_ops = {
.name = "pld_usb",
.id_table = pld_usb_id_table,
.probe = pld_usb_probe,
.disconnect = pld_usb_remove,
#ifdef CONFIG_PM
.suspend = pld_usb_suspend,
.resume = pld_usb_resume,
.reset_resume = pld_usb_reset_resume,
#endif
.supports_autosuspend = true,
};
/**
* pld_usb_register_driver() - registration routine for wlan usb driver
*
* Return: int negative error code on failure and 0 on success
*/
int pld_usb_register_driver(void)
{
int status;
usb_register(&pld_usb_ops);
if (atomic_read(&pld_usb_reg_done) == true)
status = 0;
else
status = -1;
pr_info("%s usb_register %s, status %d\n", __func__,
(status == 0) ? "done" : "failed", status);
return status;
}
/**
* pld_usb_unregister_driver() - de-registration routine for wlan usb driver
*
* Return: void
*/
void pld_usb_unregister_driver(void)
{
struct pld_context *pld_context;
pld_context = pld_get_global_context();
if (atomic_read(&pld_usb_reg_done) == false)
return;
pld_context->ops->remove(NULL, PLD_BUS_TYPE_USB);
atomic_set(&pld_usb_reg_done, false);
usb_deregister(&pld_usb_ops);
pr_info("%s usb_deregister done!\n", __func__);
}
#endif /* CONFIG_PLD_USB_CNSS */