/*
  Copyright 2006 Jean-Baptiste Note

  This file is part of islsm.

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

  islsm 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 islsm; if not, write to the Free Software Foundation, Inc.,
  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

*/

/*
  Foreword : how come we need this ? The firmware in intersil's chipsets
  does not do memory management by itself.  When you send a frame to the
  device: 
  
  - for a control frame, it treats it directly in-interrupt; 

  - for a data frame, it puts a header in front of it, of length less than
  ISLSM_80211_FW_HEADROOM, and links it into doubly-linked queues
  depending on it's type. 
  
  But in all cases, *we* get to tell the firmware where to put the
  frames in its own memory space, and we must take great care not to
  completely screw up the firmware state by doing overwrites on active
  control data.

  Usually, for a standard data frame, the firmware will send you an ack
  packets saying "OK, the frame that you sent at this address has been
  dequeued, you can reclaim the associated memory".

  However, for some reasons, sometimes this ack does not goes through
  (probably when the frame is not acked).

  So the driver has the possibility to send a command saying "take the
  frame at this address, which I sent you earlier, unlink it from the
  queue it's in, and tell me when you're done" -- this actually allows
  you to explicitly reclaim the buffer (see islsm_empty_queue).

  You can do this when you're pretty sure the frame has exceeded it's
  expected ACK time (all of this is somewhat speculative, he) ; allowing
  you to reclaim the associated memory for other frames.

  HOWEVER, some special frames, such as the beacon, or the probe
  request, for instance, do not follow this pattern. They occupy special
  slots (obviously, you don't want to retransmit the beacon frame
  everytime you want it out), and they are automatically freed with
  special control packets (tx/rx filters, or frequency change). I don't
  know if it's legit to reclaim them with the above command.

  Thus you'll find here a: Small Block Allocator, currently dirty.

  The plan is to use the control block of tx frames to chain the skbs of
  the frames which are present (or on the verge of being present, once
  submitted) in device memory. The normal chaining of skbs is reserved
  for future use in the usb driver on the usbnet model.

*/

#ifndef _HAS_ISLSM_ALLOC_H
#define _HAS_ISLSM_ALLOC_H

#include <linux/skbuff.h>
#include <linux/list.h>
#include <linux/types.h>

#define ISLSM_BLOCK_SIZE (1<<8)
/* oversized for some,
   but should be okay for all */
/* latest firmwares (lm87) seem to need 0x60 */
#define ISLSM_80211_FW_HEADROOM 0x70
/* firmware does very strange things at the end of the frames. See for
   instance @0x1984 in 2.4.3.4 -- leave a safe room */
#define ISLSM_80211_FW_TAILROOM 0x100

/* various kind of frames */

/* control frames which do not need a response can be freed as soon as
   they are submitted -- they are treated in-order by the firmware */
#define      ISLSM_ALLOC_FREE_WHEN_SUBMITTED 1
/* control frames which do need a response should not be freed so soon
   -- one should wait for them to be transmitted back with their result,
   as the data therein could be corrupted by a concurrent TX */
#define      ISLSM_ALLOC_FREE_ON_RESPONSE    2
/* standard data frames should be freed when receiving a packet in
   islsm_input_txack -- actually i'm not really sure, maybe the frame is
   only a status report, which *may* indicate that the slot has been
   freed. But this seems to be usually the case. */
#define      ISLSM_ALLOC_FREE_ON_ACK         4

/* control block of the tx skbs, allocator-specific part */
struct islsm_alloc_cb {
	u32                     lmac_addr;
	unsigned                flags;
	unsigned                len;
	/* access is serialized islsm->alloc->slock */
	struct list_head        chain;
};


typedef struct mem_descr {
	uint32_t                start_addr;
	uint32_t                end_addr;
	spinlock_t              slock;
	struct list_head        indevice_skbs;
} mem_descr_t;

int                     islsm_alloc_create(mem_descr_t *, uint32_t start,
					   uint32_t end);
void                    islsm_alloc_destroy(mem_descr_t *);

/* functions exported to other modules */
int                     islsm_alloc(mem_descr_t *mem, struct sk_buff *skb);
void                    islsm_free(mem_descr_t *, uint32_t addr);
ssize_t                 islsm_alloc_dump(mem_descr_t *mem, char *buf);

#define LMAC_ADDR_OF_SKB(skb) ( ((struct islsm_alloc_cb *)skb->cb)->lmac_addr )
#define LMAC_ALLOC_FLAGS(skb) ( ((struct islsm_alloc_cb *)skb->cb)->flags )

/* This is an intermediate step which relies on the old
   allocator to do the job. Will completely redo under the hood
   ultimately */
static inline void 
islsm_alloc_set_flags(struct sk_buff *skb, unsigned flags) {
	struct islsm_alloc_cb *alloc = (void *)skb->cb;
	alloc->flags = flags;
}

#endif				/* _HAS_ISLSM_ALLOC_H */
