/*
 * usbgem.h: General USB to Ethernet MAC driver framework
 * @(#)usbgem.h	1.4 12/02/09
 * (C) Copyright 2003-2009 Masayuki Murayama KHF04453@nifty.ne.jp
 */

#ifndef __USBGEM_H__
#define	__USBGEM_H__

#pragma	ident	"@(#)usbgem.h	1.4 12/02/09"

#include <sys/mac.h>
#ifndef MAC_VERSION
#include <sys/mac_provider.h>
#endif
#include <sys/mac_ether.h>

/*
 * Useful macros and typedefs
 */
#define	USBGEM_NAME_LEN	32

#define	USBGEM_TX_TIMEOUT		(drv_usectohz(3*1000000))
#define	USBGEM_TX_TIMEOUT_INTERVAL	(drv_usectohz(1*1000000))
#define	USBGEM_LINK_WATCH_INTERVAL	(drv_usectohz(1*1000000))

/* general return code */
#define	USBGEM_SUCCESS	0
#define	USBGEM_FAILURE	1

/* return code of usbgem_tx_done */
#define	INTR_RESTART_TX	0x80000000U

struct usbgem_stats {
	uint32_t	intr;

	uint32_t	crc;
	uint32_t	errrcv;
	uint32_t	overflow;
	uint32_t	frame;
	uint32_t	missed;
	uint32_t	runt;
	uint32_t	frame_too_long;
	uint32_t	norcvbuf;
	uint32_t	sqe;

	uint32_t	collisions;
	uint32_t	first_coll;
	uint32_t	multi_coll;
	uint32_t	excoll;
	uint32_t	xmit_internal_err;
	uint32_t	nocarrier;
	uint32_t	defer;
	uint32_t	errxmt;
	uint32_t	underflow;
	uint32_t	xmtlatecoll;
	uint32_t	noxmtbuf;
	uint32_t	jabber;


	uint64_t	rbytes;
	uint64_t	obytes;
	uint64_t	rpackets;
	uint64_t	opackets;
	uint32_t	rbcast;
	uint32_t	obcast;
	uint32_t	rmcast;
	uint32_t	omcast;
	uint32_t	rcv_internal_err;
};

struct mcast_addr {
	struct ether_addr	addr;
	uint32_t		hash;
};

#define	USBGEM_MAXMC	64
#define	USBGEM_MCALLOC	(sizeof (struct mcast_addr) * USBGEM_MAXMC)

#define	SLOT(dp, n)	((n) % (dp)->ugc.usbgc_tx_list_max)

/*
 * mac soft state
 */
struct usbgem_dev {
	dev_info_t	*dip;
	mac_handle_t	mh;
	char		name[USBGEM_NAME_LEN];

	/* pointer to usb private data */
	usb_client_dev_data_t	*reg_data;

	/* usb handles */
	usb_pipe_handle_t	default_pipe;
	usb_pipe_handle_t	bulkin_pipe;
	usb_pipe_handle_t	bulkout_pipe;
	usb_pipe_handle_t	intr_pipe;

	/* usb endpoints */
	usb_ep_descr_t		*ep_default;
	usb_ep_descr_t		*ep_bulkin;
	usb_ep_descr_t		*ep_bulkout;
	usb_ep_descr_t		*ep_intr;

	/* usb policies */
	usb_pipe_policy_t	policy_default;
	usb_pipe_policy_t	policy_bulkin;
	usb_pipe_policy_t	policy_bulkout;
	usb_pipe_policy_t	policy_interrupt;

	/* MAC address information */
	struct ether_addr	cur_addr;
	struct ether_addr	dev_addr;

	/* RX state and resource management */
	kmutex_t		rxlock;
	int			rx_busy_cnt;
	boolean_t		rx_active;
	kcondvar_t		rx_drain_cv;

	/* RX buffer management */
	int			rx_buf_len;

	/* TX state and resource management */
	kmutex_t		txlock;
	int			tx_busy_cnt;
	usb_bulk_req_t		*tx_free_list;
	kcondvar_t		tx_drain_cv;
	clock_t			tx_start_time;
	int			bulkout_timeout;	/* in second */
	int			tx_max_packets;
	int			tx_seq_num;
	int			tx_intr_pended;

	/* NIC state from OS view */
	int			nic_state;
#define	NIC_STATE_UNKNOWN	0
#define	NIC_STATE_STOPPED	1
#define	NIC_STATE_INITIALIZED	2
#define	NIC_STATE_ONLINE	3

	/* MAC state from hardware view */
	int			mac_state;
#define	MAC_STATE_DISCONNECTED	0	/* it includes suspended state too */
#define	MAC_STATE_STOPPED	1	/* powered up / buf not initialized */
#define	MAC_STATE_INITIALIZED	2	/* initialized */
#define	MAC_STATE_ONLINE	3	/* working correctly  */
#define	MAC_STATE_ERROR		4	/* need to restart nic */

	clock_t			fatal_error;

	/* robustness: timer and watchdog */
	uint_t			tx_watcher_stop;
	kt_did_t		tx_watcher_did;
	kcondvar_t		tx_watcher_cv;
	kmutex_t		tx_watcher_lock;
	clock_t			tx_watcher_timeout;
	clock_t			tx_watcher_interval;

	/* MII mamagement */
	boolean_t		anadv_autoneg:1;
	boolean_t		anadv_1000fdx:1;
	boolean_t		anadv_1000hdx:1;
	boolean_t		anadv_100t4:1;
	boolean_t		anadv_100fdx:1;
	boolean_t		anadv_100hdx:1;
	boolean_t		anadv_10fdx:1;
	boolean_t		anadv_10hdx:1;
	boolean_t		anadv_1000t_ms:2;
	boolean_t		anadv_pause:1;
	boolean_t		anadv_asmpause:1;
	boolean_t		mii_advert_ro:1;

	boolean_t		full_duplex:1;
	int			speed:3;
#define		USBGEM_SPD_10	0
#define		USBGEM_SPD_100	1
#define		USBGEM_SPD_1000	2
#define		USBGEM_SPD_NUM	3
	unsigned int		flow_control:2;
#define		FLOW_CONTROL_NONE	0
#define		FLOW_CONTROL_SYMMETRIC	1
#define		FLOW_CONTROL_TX_PAUSE	2
#define		FLOW_CONTROL_RX_PAUSE	3

	boolean_t		mii_supress_msg:1;

	uint32_t		mii_phy_id;
	uint16_t		mii_status;
	uint16_t		mii_advert;
	uint16_t		mii_lpable;
	uint16_t		mii_exp;
	uint16_t		mii_ctl1000;
	uint16_t		mii_stat1000;
	uint16_t		mii_xstatus;
	int8_t			mii_phy_addr;	/* must be signed */

	uint16_t		mii_status_ro;
	uint16_t		mii_xstatus_ro;

	int			mii_state;
#define		MII_STATE_UNKNOWN		0
#define		MII_STATE_RESETTING		1
#define		MII_STATE_AUTONEGOTIATING	2
#define		MII_STATE_AN_DONE		3
#define		MII_STATE_MEDIA_SETUP		4
#define		MII_STATE_LINKUP		5
#define		MII_STATE_LINKDOWN		6

	clock_t			mii_last_check;	/* in tick */
	clock_t			mii_timer;	/* in tick */
#define		MII_RESET_TIMEOUT	drv_usectohz(1000*1000)
#define		MII_AN_TIMEOUT		drv_usectohz(5000*1000)
#define		MII_LINKDOWN_TIMEOUT	drv_usectohz(10000*1000)

	clock_t			mii_interval;	/* in tick */
	clock_t			linkup_delay;	/* in tick */

	uint_t			link_watcher_stop;
	kt_did_t		link_watcher_did;
	kcondvar_t		link_watcher_wait_cv;
	kmutex_t		link_watcher_lock;

	krwlock_t		dev_state_lock;	/* mac_state and nic_state */
	ksema_t			hal_op_lock;	/* serialize hw operations */
	ksema_t			drv_op_lock;	/* hotplug op lock */

	/* multcast list */
	ksema_t			rxfilter_lock;
	int			mc_count;
	int			mc_count_req;
	struct mcast_addr	*mc_list;
	int			rxmode;
#define		RXMODE_PROMISC		0x01
#define		RXMODE_ALLMULTI_REQ	0x02
#define		RXMODE_MULTI_OVF	0x04
#define		RXMODE_ENABLE		0x08
#define		RXMODE_ALLMULTI		(RXMODE_ALLMULTI_REQ | RXMODE_MULTI_OVF)
#define		RXMODE_BITS	\
			"\020"	\
			"\004ENABLE"	\
			"\003MULTI_OVF"	\
			"\002ALLMULTI_REQ"	\
			"\001PROMISC"

	/* statistcs */
	struct usbgem_stats		stats;

	/* pointer to local structure */
	void			*private;
	int			priv_size;

	/* configuration */
	struct usbgem_conf {
		/* name */
		char		usbgc_name[USBGEM_NAME_LEN];
		int		usbgc_ppa;

		/* specification on usb */
		int	usbgc_ifnum;	/* interface number */
		int	usbgc_alt;	/* alternate */

		/* specification on tx engine */
		int		usbgc_tx_list_max;

		/* specification on rx engine */
		int		usbgc_rx_header_len;
		int		usbgc_rx_list_max;

		/* time out parameters */
		clock_t		usbgc_tx_timeout;
		clock_t		usbgc_tx_timeout_interval;

		/* flow control */
		int		usbgc_flow_control;

		/* MII timeout parameters */
		clock_t	usbgc_mii_linkdown_timeout;
		clock_t	usbgc_mii_link_watch_interval;
		clock_t	usbgc_mii_reset_timeout;

		clock_t	usbgc_mii_an_watch_interval;
		clock_t	usbgc_mii_an_timeout;
		clock_t	usbgc_mii_an_wait;
		clock_t	usbgc_mii_an_delay;

		/* MII configuration */
		int	usbgc_mii_addr_min;
		int	usbgc_mii_linkdown_action;
		int	usbgc_mii_linkdown_timeout_action;
#define		MII_ACTION_NONE		0
#define		MII_ACTION_RESET	1
#define		MII_ACTION_RSA		2
		boolean_t	usbgc_mii_dont_reset:1;
		boolean_t	usbgc_mii_an_oneshot:1;
		boolean_t	usbgc_mii_hw_link_detection:1;
		boolean_t	usbgc_mii_stop_mac_on_linkdown:1;
		uint16_t	usbgc_mii_an_cmd;

		/* I/O methods */

		/* mac operation */
		int	(*usbgc_attach_chip)(struct usbgem_dev *dp);
		int	(*usbgc_reset_chip)(struct usbgem_dev *dp);
		int	(*usbgc_init_chip)(struct usbgem_dev *dp);
		int	(*usbgc_start_chip)(struct usbgem_dev *dp);
		int	(*usbgc_stop_chip)(struct usbgem_dev *dp);
		uint32_t (*usbgc_multicast_hash)(struct usbgem_dev *dp,
		    const uint8_t *);
		int	(*usbgc_set_rx_filter)(struct usbgem_dev *dp);
		int	(*usbgc_set_media)(struct usbgem_dev *dp);
		int	(*usbgc_get_stats)(struct usbgem_dev *dp);
		void	(*usbgc_interrupt)(struct usbgem_dev *dp, mblk_t *mp);

		/* packet manipulation */
		mblk_t	*(*usbgc_tx_make_packet)(struct usbgem_dev *dp,
		    mblk_t *mp);
		mblk_t	*(*usbgc_rx_make_packet)(struct usbgem_dev *dp,
		    mblk_t *mp);
		/* mii operations */
		int	(*usbgc_mii_probe)(struct usbgem_dev *dp);
		int	(*usbgc_mii_init)(struct usbgem_dev *dp);
		int	(*usbgc_mii_config)(struct usbgem_dev *dp, int *errp);
		uint16_t (*usbgc_mii_read)(struct usbgem_dev *dp, uint_t reg,
		    int *errp);
		void	(*usbgc_mii_write)(struct usbgem_dev *dp, uint_t reg,
		    uint16_t val, int *errp);

		/* jumbo frame */
		int	usbgc_max_mtu;
		int	usbgc_default_mtu;
		int	usbgc_min_mtu;
	} ugc;

	int	misc_flag;
#define	USBGEM_VLAN	0x0001
	timeout_id_t	intr_watcher_id;

	/* buffer size */
	uint_t	mtu;

	/* performance tuning parameters */
	uint_t	txthr;		/* tx fifo threshoold */
	uint_t	txmaxdma;	/* tx max dma burst size */
	uint_t	rxthr;		/* rx fifo threshoold */
	uint_t	rxmaxdma;	/* tx max dma burst size */

	/* kstat stuff */
	kstat_t	*ksp;

	/* ndd stuff */
	caddr_t	nd_data_p;
	caddr_t	nd_arg_p;

#ifdef USBGEM_DEBUG_LEVEL
	int	tx_cnt;
#endif
};

/*
 * Exported functions
 */
int usbgem_ctrl_out(struct usbgem_dev *dp,
    uint8_t reqt, uint8_t req, uint16_t val, uint16_t ix, uint16_t len,
    void *bp, int size);

int usbgem_ctrl_in(struct usbgem_dev *dp,
    uint8_t reqt, uint8_t req, uint16_t val, uint16_t ix, uint16_t len,
    void *bp, int size);

int usbgem_ctrl_out_val(struct usbgem_dev *dp,
    uint8_t reqt, uint8_t req, uint16_t val, uint16_t ix, uint16_t len,
    uint32_t v);

int usbgem_ctrl_in_val(struct usbgem_dev *dp,
    uint8_t reqt, uint8_t req, uint16_t val, uint16_t ix, uint16_t len,
    void *valp);

void usbgem_generate_macaddr(struct usbgem_dev *, uint8_t *);
boolean_t usbgem_get_mac_addr_conf(struct usbgem_dev *);
int usbgem_mii_probe_default(struct usbgem_dev *);
int usbgem_mii_init_default(struct usbgem_dev *);
int usbgem_mii_config_default(struct usbgem_dev *, int *errp);
void usbgem_mii_update_link(struct usbgem_dev *);
void usbgem_restart_tx(struct usbgem_dev *);
boolean_t usbgem_tx_done(struct usbgem_dev *, int);
void usbgem_receive(struct usbgem_dev *);
struct usbgem_dev *usbgem_do_attach(dev_info_t *,
    struct usbgem_conf *, void *, int);
int usbgem_do_detach(dev_info_t *);

uint32_t usbgem_ether_crc_le(const uint8_t *addr);
uint32_t usbgem_ether_crc_be(const uint8_t *addr);

int usbgem_resume(dev_info_t *);
int usbgem_suspend(dev_info_t *);
int usbgem_quiesce(dev_info_t *);

#define	USBGEM_STREAM_OPS(dev_ops, attach, detach) \
    DDI_DEFINE_STREAM_OPS(dev_ops, nulldev, nulldev, attach, detach, \
    nodev, NULL, D_MP, NULL, usbgem_quiesce)

int usbgem_mod_init(struct dev_ops *, char *);
void usbgem_mod_fini(struct dev_ops *);

#define	USBGEM_GET_DEV(dip) \
	((struct usbgem_dev *)(ddi_get_driver_private(dip)))

#endif /* __USBGEM_H__ */