/*=====================================================
 * CopyRight (C) 2007 Qualcomm Inc. All Rights Reserved.
 *
 *
 * This file is part of Express Card USB Driver
 *
 * $Id:
 *====================================================
 */
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/usb.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/firmware.h>
#include "ft1000_usb.h"

#include <linux/kthread.h>

MODULE_DESCRIPTION("FT1000 EXPRESS CARD DRIVER");
MODULE_LICENSE("Dual MPL/GPL");
MODULE_SUPPORTED_DEVICE("QFT FT1000 Express Cards");

void *pFileStart;
size_t FileLength;

#define VENDOR_ID 0x1291	/* Qualcomm vendor id */
#define PRODUCT_ID 0x11		/* fake product id */

/* table of devices that work with this driver */
static struct usb_device_id id_table[] = {
	{USB_DEVICE(VENDOR_ID, PRODUCT_ID)},
	{},
};

MODULE_DEVICE_TABLE(usb, id_table);

static bool gPollingfailed = FALSE;
int ft1000_poll_thread(void *arg)
{
	int ret = STATUS_SUCCESS;

	while (!kthread_should_stop()) {
		msleep(10);
		if (!gPollingfailed) {
			ret = ft1000_poll(arg);
			if (ret != STATUS_SUCCESS) {
				DEBUG("ft1000_poll_thread: polling failed\n");
				gPollingfailed = TRUE;
			}
		}
	}
	return STATUS_SUCCESS;
}

static int ft1000_probe(struct usb_interface *interface,
			const struct usb_device_id *id)
{
	struct usb_host_interface *iface_desc;
	struct usb_endpoint_descriptor *endpoint;
	struct usb_device *dev;
	unsigned numaltsetting;
	int i, ret = 0, size;

	struct ft1000_device *ft1000dev;
	struct ft1000_info *pft1000info = NULL;
	const struct firmware *dsp_fw;

	ft1000dev = kmalloc(sizeof(struct ft1000_device), GFP_KERNEL);

	if (!ft1000dev) {
		printk(KERN_ERR "out of memory allocating device structure\n");
		return 0;
	}

	memset(ft1000dev, 0, sizeof(*ft1000dev));

	dev = interface_to_usbdev(interface);
	DEBUG("ft1000_probe: usb device descriptor info:\n");
	DEBUG("ft1000_probe: number of configuration is %d\n",
	      dev->descriptor.bNumConfigurations);

	ft1000dev->dev = dev;
	ft1000dev->status = 0;
	ft1000dev->net = NULL;
	ft1000dev->tx_urb = usb_alloc_urb(0, GFP_ATOMIC);
	ft1000dev->rx_urb = usb_alloc_urb(0, GFP_ATOMIC);

	DEBUG("ft1000_probe is called\n");
	numaltsetting = interface->num_altsetting;
	DEBUG("ft1000_probe: number of alt settings is :%d\n", numaltsetting);
	iface_desc = interface->cur_altsetting;
	DEBUG("ft1000_probe: number of endpoints is %d\n",
	      iface_desc->desc.bNumEndpoints);
	DEBUG("ft1000_probe: descriptor type is %d\n",
	      iface_desc->desc.bDescriptorType);
	DEBUG("ft1000_probe: interface number is %d\n",
	      iface_desc->desc.bInterfaceNumber);
	DEBUG("ft1000_probe: alternatesetting is %d\n",
	      iface_desc->desc.bAlternateSetting);
	DEBUG("ft1000_probe: interface class is %d\n",
	      iface_desc->desc.bInterfaceClass);
	DEBUG("ft1000_probe: control endpoint info:\n");
	DEBUG("ft1000_probe: descriptor0 type -- %d\n",
	      iface_desc->endpoint[0].desc.bmAttributes);
	DEBUG("ft1000_probe: descriptor1 type -- %d\n",
	      iface_desc->endpoint[1].desc.bmAttributes);
	DEBUG("ft1000_probe: descriptor2 type -- %d\n",
	      iface_desc->endpoint[2].desc.bmAttributes);

	for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) {
		endpoint =
		    (struct usb_endpoint_descriptor *)&iface_desc->
		    endpoint[i].desc;
		DEBUG("endpoint %d\n", i);
		DEBUG("bEndpointAddress=%x, bmAttributes=%x\n",
		      endpoint->bEndpointAddress, endpoint->bmAttributes);
		if ((endpoint->bEndpointAddress & USB_DIR_IN)
		    && ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
			USB_ENDPOINT_XFER_BULK)) {
			ft1000dev->bulk_in_endpointAddr =
			    endpoint->bEndpointAddress;
			DEBUG("ft1000_probe: in: %d\n",
			      endpoint->bEndpointAddress);
		}

		if (!(endpoint->bEndpointAddress & USB_DIR_IN)
		    && ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
			USB_ENDPOINT_XFER_BULK)) {
			ft1000dev->bulk_out_endpointAddr =
			    endpoint->bEndpointAddress;
			DEBUG("ft1000_probe: out: %d\n",
			      endpoint->bEndpointAddress);
		}
	}

	DEBUG("bulk_in=%d, bulk_out=%d\n", ft1000dev->bulk_in_endpointAddr,
	      ft1000dev->bulk_out_endpointAddr);

	ret = request_firmware(&dsp_fw, "ft3000.img", &dev->dev);
	if (ret < 0) {
		printk(KERN_ERR "Error request_firmware().\n");
		goto err_fw;
	}

	size = max_t(uint, dsp_fw->size, 4096);
	pFileStart = kmalloc(size, GFP_KERNEL);

	if (!pFileStart) {
		release_firmware(dsp_fw);
		ret = -ENOMEM;
		goto err_fw;
	}

	memcpy(pFileStart, dsp_fw->data, dsp_fw->size);
	FileLength = dsp_fw->size;
	release_firmware(dsp_fw);

	DEBUG("ft1000_probe: start downloading dsp image...\n");

	ret = init_ft1000_netdev(ft1000dev);
	if (ret)
		goto err_load;

	pft1000info = netdev_priv(ft1000dev->net);

	DEBUG("In probe: pft1000info=%p\n", pft1000info);
	ret = dsp_reload(ft1000dev);
	if (ret) {
		printk(KERN_ERR "Problem with DSP image loading\n");
		goto err_load;
	}

	gPollingfailed = FALSE;
	pft1000info->pPollThread =
	    kthread_run(ft1000_poll_thread, ft1000dev, "ft1000_poll");

	if (IS_ERR(pft1000info->pPollThread)) {
		ret = PTR_ERR(pft1000info->pPollThread);
		goto err_load;
	}

	msleep(500);

	while (!pft1000info->CardReady) {
		if (gPollingfailed) {
			ret = -EIO;
			goto err_thread;
		}
		msleep(100);
		DEBUG("ft1000_probe::Waiting for Card Ready\n");
	}

	DEBUG("ft1000_probe::Card Ready!!!! Registering network device\n");

	ret = reg_ft1000_netdev(ft1000dev, interface);
	if (ret)
		goto err_thread;

	ret = ft1000_init_proc(ft1000dev->net);
	if (ret)
		goto err_proc;

	pft1000info->NetDevRegDone = 1;

	return 0;

err_proc:
	unregister_netdev(ft1000dev->net);
	free_netdev(ft1000dev->net);
err_thread:
	kthread_stop(pft1000info->pPollThread);
err_load:
	kfree(pFileStart);
err_fw:
	kfree(ft1000dev);
	return ret;
}

static void ft1000_disconnect(struct usb_interface *interface)
{
	struct ft1000_info *pft1000info;

	DEBUG("ft1000_disconnect is called\n");

	pft1000info = (struct ft1000_info *) usb_get_intfdata(interface);
	DEBUG("In disconnect pft1000info=%p\n", pft1000info);

	if (pft1000info) {
		ft1000_cleanup_proc(pft1000info);
		if (pft1000info->pPollThread)
			kthread_stop(pft1000info->pPollThread);

		DEBUG("ft1000_disconnect: threads are terminated\n");

		if (pft1000info->pFt1000Dev->net) {
			DEBUG("ft1000_disconnect: destroy char driver\n");
			ft1000_destroy_dev(pft1000info->pFt1000Dev->net);
			unregister_netdev(pft1000info->pFt1000Dev->net);
			DEBUG
			    ("ft1000_disconnect: network device unregisterd\n");
			free_netdev(pft1000info->pFt1000Dev->net);

		}

		usb_free_urb(pft1000info->pFt1000Dev->rx_urb);
		usb_free_urb(pft1000info->pFt1000Dev->tx_urb);

		DEBUG("ft1000_disconnect: urb freed\n");

		kfree(pft1000info->pFt1000Dev);
	}
	kfree(pFileStart);

	return;
}

static struct usb_driver ft1000_usb_driver = {
	.name = "ft1000usb",
	.probe = ft1000_probe,
	.disconnect = ft1000_disconnect,
	.id_table = id_table,
};

static int __init usb_ft1000_init(void)
{
	int ret = 0;

	DEBUG("Initialize and register the driver\n");

	ret = usb_register(&ft1000_usb_driver);
	if (ret)
		err("usb_register failed. Error number %d", ret);

	return ret;
}

static void __exit usb_ft1000_exit(void)
{
	DEBUG("Deregister the driver\n");
	usb_deregister(&ft1000_usb_driver);
}

module_init(usb_ft1000_init);
module_exit(usb_ft1000_exit);
