/*
  Copyright 2004 Feyd
  Copyright 2004, 2005 Jean-Baptiste Note

  This file is part of prism54usb.

  prism54usb is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.

  prism54usb 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.

  You should have received a copy of the GNU General Public License
  along with prism54usb; if not, write to the Free Software Foundation,
  Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

*/

#ifndef _HAS_ISLUSB_DEV_H
#define _HAS_ISLUSB_DEV_H

#include <linux/usb.h>
#include <linux/netdevice.h>	/* net_device */

#define DRV_NAME "islusb"

#define P54U_IMAGE_FILE		"isl3890usb"
#define P54U_IMAGE_FILE_X2	"isl3887usb"
#define IMAGE_FILE_3887		"isl3887usb_bare"

/* Should be reduced as
   max(0x400, mru + rx_headers) */
#define P54U_MAX_FRAME_SIZE	4096
#define P54U_QUEUE_LEN          16
#define P54U_FW_BLOCK		512

/* pipes 3 and 4 are not used by the driver */
#define P54U_PIPE_NUMBER 9

enum p54u_pipe_addr {
	P54U_PIPE_DATA = 0x01,
	P54U_PIPE_MGMT = 0x02,
	P54U_PIPE_3 = 0x03,
	P54U_PIPE_4 = 0x04,
	P54U_PIPE_BRG = 0x0d,
	P54U_PIPE_DEV = 0x0e,
	P54U_PIPE_INT = 0x0f,
};

enum p54u_pipe_index {
	P54U_TX_DATA,
	P54U_TX_MGMT,
	P54U_TX_BRG,
	P54U_TX_DEV,
	P54U_RX_DATA,
	P54U_RX_MGMT,
	P54U_RX_3,
	P54U_RX_4,
	P54U_RX_BRG,
	P54U_RX_DEV,
	P54U_RX_INT,
	P54U_PIPES,
};

enum p54u_state {
	P54U_BOOT,
	P54U_RUN,
	P54U_SHUTDOWN,
	P54U_STOPPED,
};

enum p54u_versions {
	P54U_DEVICE_VER1,
	P54U_DEVICE_VER2,
};

/* net2280.h ? */
struct net2280_reg_write {
	u16                     port;
	u32                     addr;
	u32                     val;
} __attribute__ ((packed));

struct net2280_reg_read {
	u16                     port;
	u32                     addr;
} __attribute__ ((packed));

struct p54u_mgmt_tx {
	u32                     magic1;
	u32                     magic2;
	u32                     magic3;
	u16                     pos;
	u16                     len;
} __attribute__ ((packed));

struct p54u_pipe_desc {
	u8                      addr;	/* the address of the endpoint */
	u8                      type;	/* the transfer type of the endpoint */
	size_t                  p_size;	/* the size of the packet */
	void                    (*callback) (struct urb * urb,
					     struct pt_regs * regs);
};

/* RX QUEUES DESCRIPTION

DATA ORGANIZATION:
   for rx queue we maintain 3 ever-increasing indexes to be interpreted
   modulo the size of the queue, which serves as a ring buffer : f,c,p

   - f points to the next frame to be processed by the softmac
     layer. It's modified in driver bottom half => needs locking ?

   - c points to the next submitted frame pending usb callback (meaning
     the data it contains will have been written by the device). It's
     modified during the urb callback, in interrupt context.

   - p points to the next free frame. It's modified during frame
     resubmission, after processing the rx'd frames in the bottom half
     => needs locking ?

Initially, a frame is free in the [p,f[ range; its receive buffer is
alloc'd. It is then submitted to the usb layer an goes to the [c,p[
range of submitted but not filled in frames.  Upon urb callback, it
falls into the [f,c[ interval : this frame has been received but its
contents have not yet been processed by the softmac layer. This allows
the callback to return quickly, as it does minimal work. Later, in a
bottom half, the frame will be processed (then its buffer is freed), its
buffer alloc'd, and it will return into the [p,f[ range of free
frames. And resubmitted immediately.

DATA CONTENTS: an element in a rx queue is composed of an urb and an skb
whose data pointer is the receive buffer of the urb.

BUFFER ALLOCATION:

    - the urb are recycled, and allocated once and for all.

    - the softmac layer is in charge of freeing the transfer buffers.
the rx queue mechanism is in charge of allocating the transfer buffers
(on frame free). We could refine this allocation mechanism by allowing
the softmac not to free the transfert buffer.
*/

/* associated functions

   XXX
*/

/* TX QUEUES DESCRIPTION

DATA ORGANIZATION
   for tx queue we maintain 3 ever-increasing indexes to be interpreted
   modulo the size of the queue, which serves as a ring buffer : f,c,p

   - f points to the next frame to be processed by the softmac
     layer.

   - c points to the next submitted frame pending usb callback (meaning
     the data it contains will have been received and acknowledged by
     the device). It's modified during the urb callback, in interrupt
     context.

   - p points to the next free frame. It's modified during frame
     submission, after processing, in the bottom half => needs locking ?

Basically f is not really needed here, because the softmac layer does
not process the tx'd frames. We can have only two indexes, f being
always equal to c, for instance ; or always equal to p.

DATA CONTENTS: an element in a rx queue is composed of two urbs, one
with a fixed size, either 32 bits (version 1) or 16 bytes (version 2),
for the announcement message, and a data urb proper, whose transfert
buffer needs to be provided by the upper layer (softmac).

BUFFER ALLOCATION:

   - the urbs are recycled. The announcement urbs's buffer are recycled
     too.

   - the tx skb are freed by us upon callback completion.

*/

/* associated functions :
   XXX
*/

typedef struct {
	struct urb             *descr_urb;	/* urbs used to send the announced message */
	struct urb             *data_urb;	/* actual data to send */
	struct sk_buff         *skb;	/* data buffer */
} usb_command_t;

struct p54u;

struct p54u_pipe {
	/* these three should go into a struct */
	usb_command_t          *ringbuf;
	int                     len;	/* the length of the ringbufer */

	/* queue management */
	unsigned int            f;
	unsigned int            c;
	unsigned int            p;

	/* bottom half to be run on data rx/tx */
	spinlock_t              lock;
	struct tasklet_struct   task;

	/* usb characteristics :replace with endp descrptor ? */
	u8                      addr;	/* the address of the endpoint */
	u8                      type;	/* the transfer type of the endpoint */
	size_t                  buffer_size;	/* the size of the buffer to allocate */
	int                     interval;
	int                     endp;

	/* our container p54u instance */
	struct p54u            *p54u;
};

/* Structure to hold all of our device specific stuff */
struct p54u {
	struct usb_device      *usbdev;	/* save off the usb device pointer */
	struct usb_interface   *interface;	/* save off the usb device pointer */

	int                     state;
	/* version-specific handling */
	int                     device_version;

	/* Bulk data pipes */
	struct p54u_pipe        data_tx;
	struct p54u_pipe        data_rx;
	struct p54u_pipe        mgmt_tx;
	struct p54u_pipe        mgmt_rx;

	/* net2280-specific variables */
	struct semaphore        reg_sem;
	struct work_struct      int_bh;
	struct urb             *int_urb;

	/* 3887-specific URBs for uart-over-usb */
	struct urb             *dr;
	struct urb             *cts;
};

extern int              load_fw;

#define P54U_OF_ISLSM(x) ((struct p54u *)(x)->priv)
#define P54U_OF_NETDEV(x) ((struct p54u *) islsm_priv(x))

/* device hardware queue management */
void                    p54u_queue_destroy(struct p54u_pipe *queue);
int                     p54u_queue_init(struct p54u *device, struct usb_endpoint_descriptor
					*desc, struct p54u_pipe *queue);
int                     rx_queue_refill(struct p54u *p54u,
					struct p54u_pipe *queue);

/* usb transport functions */
int                     p54u_bulk_msg(struct p54u *device,
				      unsigned int ep, void *data, int len);

/* tx callbacks for usb devices */
int                     tx_queue_submit(struct p54u_pipe *queue, struct sk_buff *skb);

#endif /* _HAS_ISLUSB_DEV_H */
