/*****************************************************************************
 * Copyright (c) 2013 IBM Corporation
 * All rights reserved.
 * This program and the accompanying materials
 * are made available under the terms of the BSD License
 * which accompanies this distribution, and is available at
 * http://www.opensource.org/licenses/bsd-license.php
 *
 * Contributors:
 *     IBM Corporation - initial implementation
 *****************************************************************************/

#ifndef __USB_CORE_H
#define __USB_CORE_H

#include <stdio.h>
#include <stdbool.h>
#include "helpers.h"
#include "usb.h"
#include "tools.h"

enum usb_hcd_type {
	USB_OHCI = 1,
	USB_EHCI = 2,
	USB_XHCI = 3,
};

struct usb_hcd_dev;

struct usb_hcd_dev {
	void *base;
	long type;
	long num;
	struct usb_hcd_ops *ops;
	void *priv; /* hcd owned structure */
	long nextaddr; /* address for devices */
};

struct usb_pipe;

/*******************************************/
/* Standard Endpoint Descriptor            */
/*******************************************/
/* bmAttributes */
#define USB_EP_TYPE_MASK          0x03
#define USB_EP_TYPE_CONTROL       0
#define USB_EP_TYPE_ISOC          1
#define USB_EP_TYPE_BULK          2
#define USB_EP_TYPE_INTR          3

struct usb_ep_descr {
	uint8_t		bLength;		/* size of descriptor */
	uint8_t		bDescriptorType;	/* Type = 5 */
	uint8_t		bEndpointAddress;
	uint8_t		bmAttributes;
	uint16_t	wMaxPacketSize;
	uint8_t		bInterval;
} __attribute__((packed));

#define	DEV_HID_KEYB            0x030101	/* class=HIB,	protocol=Keyboard */
#define	DEV_HID_MOUSE           0x030102	/* class=HIB,	protocol=Mouse */
#define	DEV_HUB                 0x090000	/* class=HUB, subclass, protocol */
#define	DEV_MASS_RBC            0x080150	/* MassStorage, RBC, Bulk */
#define	DEV_CDROM_ATAPI         0x080250	/* MassStorage, SFF-8020i , Bulk */
#define	DEV_MASS_FLOPPY         0x080450	/* MassStorage, UFI, Bulk */
#define	DEV_MASS_ATAPI          0x080550	/* MassStorage, SFF-8070i , Bulk */
#define	DEV_MASS_SCSI           0x080650	/* MassStorage, SCSI, Bulk */

enum USB_SPEED_TYPE {
	USB_LOW_SPEED = 0,
	USB_FULL_SPEED = 1,
	USB_HIGH_SPEED = 2,
	USB_SUPER_SPEED = 3,
};

/* Max number of endpoints supported in a device */
#define USB_DEV_EP_MAX 4
#define USB_TIMEOUT    5000 /* 5 sec usb timeout */

struct usb_dev {
	struct usb_dev     *next;
	struct usb_hcd_dev *hcidev;
	struct usb_pipe    *intr;
	struct usb_pipe    *control;
	struct usb_pipe    *bulk_in;
	struct usb_pipe    *bulk_out;
	struct usb_ep_descr ep[USB_DEV_EP_MAX];
	void *priv;
	uint32_t ep_cnt;
	uint32_t class;
	uint32_t speed;
	uint32_t addr;
	uint32_t mps0;
	uint32_t port;
	uint16_t intf_num;
};

#define DEVICE_KEYBOARD    1
#define DEVICE_MOUSE       2
#define DEVICE_DISK        3
#define DEVICE_HUB         4

/* Structure in sync with FORTH code */
struct slof_usb_dev {
	void     *udev;
	uint32_t port;
	uint32_t addr;
	uint32_t hcitype;
	uint32_t num;
	uint32_t devtype;
} __attribute__((packed));

enum USB_PIPE_DIR {
	USB_PIPE_OUT = 0,
	USB_PIPE_IN,
};

struct usb_pipe {
	struct usb_dev *dev;
	struct usb_pipe *next;
	uint32_t type;
	uint32_t speed;
	uint32_t dir;
	uint16_t epno;
	uint16_t mps;
} __attribute__((packed));

#define	REQ_GET_STATUS		     0	/* see Table 9-4 */
#define	REQ_CLEAR_FEATURE	     1
#define	REQ_GET_STATE		     2	/* HUB specific */
#define	REQ_SET_FEATURE		     3
#define	REQ_SET_ADDRESS		     5
#define	REQ_GET_DESCRIPTOR	     6
#define	REQ_SET_DESCRIPTOR	     7
#define	REQ_GET_CONFIGURATION	     8
#define	REQ_SET_CONFIGURATION	     9
#define	REQ_GET_INTERFACE	     10
#define	REQ_SET_INTERFACE	     11
#define	REQ_SYNCH_FRAME              12

#define FEATURE_DEVICE_REMOTE_WAKEUP 1
#define FEATURE_ENDPOINT_HALT        0

#define REQT_REC_DEVICE              0
#define REQT_REC_INTERFACE           1
#define REQT_REC_EP                  2
#define REQT_REC_OTHER               3
#define REQT_TYPE_STANDARD           (0 << 5)
#define REQT_TYPE_CLASS              (1 << 5)
#define REQT_TYPE_VENDOR             (2 << 5)
#define REQT_TYPE_RSRVD              (3 << 5)
#define REQT_DIR_OUT                 (0 << 7) /* host -> device */
#define REQT_DIR_IN                  (1 << 7) /* device -> host */

#define	DESCR_TYPE_DEVICE		1	/* see Table 9-5 */
#define	DESCR_TYPE_CONFIGURATION	2
#define	DESCR_TYPE_STRING		3
#define	DESCR_TYPE_INTERFACE		4
#define	DESCR_TYPE_ENDPOINT		5
#define	DESCR_TYPE_HUB			0x29	/* Class Descriptor HUB */
#define DESCR_TYPE_HID			0x21	/* Class Descriptor HID */
#define DESCR_TYPE_REPORT		0x22	/* Class Descriptor HID */
#define DESCR_TYPE_PHYSICAL		0x23	/* Class Descriptor HID */

struct usb_dev_req {
	uint8_t		bmRequestType;		/* direction, recipient */
	uint8_t		bRequest;		/* see spec: Table 9-3 */
	uint16_t	wValue;
	uint16_t	wIndex;
	uint16_t	wLength;		/* number of bytes to transfer */
} __attribute__((packed));

/* Standard Device Descriptor (18 Bytes)   */
/*******************************************/
struct usb_dev_descr {
	uint8_t		bLength;
	uint8_t		bDescriptorType;
	uint16_t	bcdUSB;
	uint8_t		bDeviceClass;
	uint8_t		bDeviceSubClass;
	uint8_t		bDeviceProtocol;
	uint8_t		bMaxPacketSize0;
	uint16_t	idVendor;
	uint16_t	idProduct;
	uint16_t	bcdDevice;
	uint8_t		iManufacturer;
	uint8_t		iProduct;
	uint8_t		iSerialNumber;
	uint8_t		bNumConfigurations;
} __attribute__((packed));

/*******************************************/
/* Standard Configuration Descriptor       */
/*******************************************/
struct usb_dev_config_descr {
	uint8_t		bLength;		/* size of descriptor */
	uint8_t		bDescriptorType;	/* Type = 2 */
	uint16_t	wTotalLength;		/* total returned data */
	uint8_t		bNumInterfaces;		/* interfaces supported by this config */
	uint8_t		bConfigurationValue;	/* Configuration-ID for SetConfiguration */
	uint8_t		iConfiguration;		/* index of string descriptor */
	uint8_t		bmAttributes;		/* configuration characteristics */
	uint8_t		bMaxPower;		/* in 2mA units */
} __attribute__((packed));

/*******************************************/
/* Standard Interface Descriptor */
/*******************************************/
struct usb_dev_intf_descr {
	uint8_t		bLength;		/* size of descriptor */
	uint8_t		bDescriptorType;	/* Type = 4 */
	uint8_t		bInterfaceNumber;
	uint8_t		bAlternateSetting;
	uint8_t		bNumEndpoints;
	uint8_t		bInterfaceClass;
	uint8_t		bInterfaceSubClass;
	uint8_t		bInterfaceProtocol;	/* protocol code */
	uint8_t		iInterface;		/* index to string descriptor */
} __attribute__((packed));

/*******************************************/
/* HUB-Class Descriptor                    */
/*******************************************/
struct usb_dev_hub_descr {
	uint8_t		bLength;		/* size of complete descriptor */
	uint8_t		bDescriptorType;	/* type = 0x29 for HUB */
	uint8_t		bNbrPorts;		/* number of downstream ports */
	uint8_t		wHubCharacteristics;	/* mode bits	7..0 */
	uint8_t		reserved;		/* mode bits 15..8 */
	uint8_t		bPwrOn2PwrGood;		/* in 2ms units */
	uint8_t		bHubContrCurrent;	/* current requirement in mA */
	uint8_t		DeviceTable;	        /* length depends on number of ports */
} __attribute__((packed));

/*******************************************/
/* HID-Class Descriptor                    */
/*******************************************/
struct usb_dev_hid_descr {
	uint8_t		bLength;		/* size of this descriptor */
	uint8_t		bDescriptorType;	/* type = 0x21 for HID     */
	uint16_t	bcdHID;			/* Sample: 0x0102 for 2.01  */
	uint8_t		bCountryCode;		/* Hardware target country */
	uint8_t		bNumDescriptors;	/* Number of HID class descr. */
	uint8_t		bReportType;		/* Report Descriptor Type */
	uint16_t	wReportLength;		/* Total Length of Report Descr. */
} __attribute__((packed));

struct usb_hcd_ops {
	const char *name;
	void (*init)(struct usb_hcd_dev *);
	void (*exit)(struct usb_hcd_dev *);
	void (*detect)(void);
	void (*disconnect)(void);
	int  (*send_ctrl)(struct usb_pipe *pipe, struct usb_dev_req *req, void *data);
	struct usb_pipe* (*get_pipe)(struct usb_dev *dev, struct usb_ep_descr *ep,
				char *buf, size_t len);
	int  (*transfer_bulk)(struct usb_pipe *pipe, void *td, void *td_phys, void *data, int size);
	void (*put_pipe)(struct usb_pipe *);
	int (*poll_intr)(struct usb_pipe *, uint8_t *);
	struct usb_hcd_ops *next;
	unsigned int usb_type;
};

#define usb_get_intf_class(x) ((x & 0x00FF0000) >> 16)

extern void usb_hcd_register(struct usb_hcd_ops *ops);
extern struct usb_pipe *usb_get_pipe(struct usb_dev *dev, struct usb_ep_descr *ep,
				char *buf, size_t len);
extern void usb_put_pipe(struct usb_pipe *pipe);
extern int usb_poll_intr(struct usb_pipe *pipe, uint8_t *buf);
extern int usb_send_ctrl(struct usb_pipe *pipe, struct usb_dev_req *req, void *data);
extern struct usb_dev *usb_devpool_get(void);
extern void usb_devpool_put(struct usb_dev *);
extern int usb_setup_new_device(struct usb_dev *dev, unsigned int port);
extern void usb_slof_populate_new_device(struct usb_dev *dev);
extern int usb_dev_populate_pipe(struct usb_dev *dev, struct usb_ep_descr *ep,
				void *buf, size_t len);
extern int usb_hid_kbd_init(struct usb_dev *dev);
extern int usb_hid_kbd_exit(struct usb_dev *dev);
extern int usb_msc_reset(struct usb_dev *dev);
extern void usb_msc_resetrecovery(struct usb_dev *dev);
#endif
