buflib 8.86 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386
/* Copyright 1998 Acorn Computers Ltd
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*
*    C.BUFLIB - Buffer management routines
*
*    28-02-93 INH Original
*    18-12-95	  Split from Network.C
*/

/* Standard includes */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

/* RISCOS includes */

#include "kernel.h"
#include "os.h"
#include "swis.h"


/* Our includes */

#include "stdtypes.h"
#include "mbuf.h"
#include "stats.h"
#include "buflib.h"

/* -------------- */

#define SAVE_INT_STATE   \
              int intstate = _kernel_irqs_disabled(); _kernel_irqs_off();

#define RESTORE_INT_STATE \
              if ( !intstate ) _kernel_irqs_on();

/* MBuffer handling routines ******************************************** */

static struct mbctl Net_Mbctl;  /* MBuf control struct */
static bool Mbufs_Attached;     /* True if we've got an active mbuf
       	    			   manager session */

/* ------------------------------------ */

void FreeBuf( BUFCHAIN pB )
{
  Net_Mbctl.free ( &Net_Mbctl, pB  );
}

/* ------------------------------------ */

/* It is documented that NULL is a safe value to pass here */

void FreeChain ( BUFCHAIN pB )
{
  Net_Mbctl.freem ( &Net_Mbctl, pB );
}

/* ----------------------------------- */

/* AddChain can be used to allocate a chain of mbufs, or to add
   some data to the start of an existing chain. A block of data
   is defined by the pData and datalen parameters: if pRest is
   NULL, a chain of buffers is allocated to contain the data.
   Otherwise the data is added to the start of the chain
   defined by pRest, allocating more buffers if necessary. The
   resultant chain is returned as a result. NULL result means
   we're out of buffers!
*/

BUFCHAIN AddChain ( BUFCHAIN pBin, void *pData, int datalen )
{
  struct mbuf *pBnew;
  int n;

  /* If we already have a chain there, try to copy as much
     data at the start as possible
  */

  if ( pBin != NULL )
  {
    n = pBin->m_off - pBin->m_inioff; /* Amount of space at start */
    /* Most common case - append to start of existing chain */
    if ( n >= datalen )
    {
      pBin->m_off -= datalen;
      pBin->m_len += datalen;
      memcpy ( mtod(pBin, char*), pData, datalen );
      return pBin;
    }
    else if ( n > 0 )
    {
      pBin->m_off -= n;
      pBin->m_len += n;
      /* Won't all fit in this buffer - copy some anyway */
      memcpy ( mtod(pBin, char*), (BYTE *)pData+datalen-n, n );
      datalen -= n;
    }
  }

  /* We need to alloc some new mbufs to hold data */

  /* Try to get a single mbuf & put the data at the end */

  pBnew = Net_Mbctl.alloc_s(&Net_Mbctl, datalen, NULL);
  if ( pBnew != NULL )
  {
    pBnew->m_len = datalen;
    pBnew->m_off = pBnew->m_inioff + pBnew->m_inilen - datalen;
    memcpy ( mtod(pBnew, char *), (BYTE *)pData, datalen );
    pBnew->m_next = pBin;
    return pBnew;
  }

  /* Give up trying to allocate efficiently at this point.
     Just make a chain and tack it on.
   */

  pBnew = Net_Mbctl.alloc(&Net_Mbctl, datalen, pData);
  if ( pBnew == NULL )  /* Bother! */
  {
    FreeChain ( pBin );
    return NULL;
  }

  return Net_Mbctl.cat(&Net_Mbctl, pBnew, pBin);
}

/* ---------------------------------- */

/* AddChainIndirect() behaves as above, but the data is not copied and
    an indirect mbuf is tacked to the start of the mbuf chain.
*/

BUFCHAIN AddChainIndirect ( BUFCHAIN pRest, void *pData,
                                           int datalen )
{
  BUFCHAIN pBnew;

  pBnew = Net_Mbctl.alloc_u(&Net_Mbctl, datalen, pData);
  /* We rely on alloc_u setting m_inioff and m_inilen to the
     same as m_off and m_len (as documented), to stop further
     AddChains from trampling on memory. Also rely on
     it being a single allocation (as documented).
  */

  if ( pBnew == NULL ) /* Bother! */
  {
    FreeChain(pRest);
    return NULL;
  }

  pBnew->m_next = pRest;
  return pBnew;
}

/* ---------------------------------- */

BUFCHAIN AllocBlankChain ( int datalen )
{
  return Net_Mbctl.alloc(&Net_Mbctl, datalen, NULL);
}

/* ---------------------------------- */

/* This is used e.g. to generate a scatter list of all the
   blocks in a buffer chain. It is called initially with
   pGBP->pChain set to the buffer chain to be traced.
   After each call, pGBP->pBlock and pGBP->BlockLen will
   point to a block of memory from the buffer chain. It
   will return non-zero for so long as pGBP->BlockLen is non
   zero.
*/

int GetBlockPointer ( struct GBP_in_out *pGBP )
{
  do
  {
    if ( pGBP->pChain == NULL )
    {
      pGBP->BlockLen = 0;
      return 0;
    }
    pGBP->pBlock = mtod(pGBP->pChain, BYTE *);
    pGBP->BlockLen = pGBP->pChain->m_len;
    pGBP->pChain = pGBP->pChain->m_next;
  }
    while ( pGBP->BlockLen == 0 );

  return 1;
}

/* ---------------------------------- */

/* ConcatChain adds two chains together */

BUFCHAIN ConcatChain ( BUFCHAIN first, BUFCHAIN last )
{
  return Net_Mbctl.cat (&Net_Mbctl, first, last );
}

/* ---------------------------------- */

/* GetData reads data off the start of an mbuf chain to a given
   memory area. pDst and datalen define the memory area
   concerned. pDst may be NULL to simply strip 'datalen' bytes
   of data off the front.

   Datalen must be > 0 for successful operation.
*/

BUFCHAIN GetData ( BUFCHAIN pB, void *pDst, int datalen )
{
  BUFCHAIN pB2;
  int len_tst;

  /* Check data is available: this will, in practise,
     rarely need to look at more than one mbuf */

  pB2 = pB;
  len_tst = datalen;
  while ( pB2 != NULL )
  {
    len_tst -= pB2->m_len;
    if ( len_tst <= 0 )      /* We're there, dude */
      return Net_Mbctl.trim (&Net_Mbctl, pB, datalen, pDst );

    pB2 = pB2->m_next;
  }

  /* Out of data! */
  FreeChain(pB);
  return NULL;
}

/* ------------------------------- */

int ChainLen ( BUFCHAIN pB )
{
  return Net_Mbctl.count_bytes(&Net_Mbctl, pB);
}

/* ------------------------------- */

int GetAllData ( BUFCHAIN pB, BYTE *pDst )
{
  int n = Net_Mbctl.count_bytes(&Net_Mbctl, pB);
  Net_Mbctl.trim (&Net_Mbctl, pB, n, pDst );
  return n;
}


/* Queue handling routines *********************************** */

/* Queues use the m_list field in an mbuf to link mbuf chains
   together in a list. Each queue has a Last pointer (the last item
   to be added) and a Head pointer (the next item to be removed).
*/

void QueueFlush ( BUFQUEUE *pQ )
{
  BUFCHAIN pB;

  while ( pB = QueueGet(pQ), pB != NULL )
    FreeChain (pB);
}

/* -------------------- */

void QueueAdd ( BUFQUEUE *pQ, BUFCHAIN pB )
{
  SAVE_INT_STATE;

  if ( pQ == NULL || pB == NULL )
  {
    STAT(STA_SERIOUS_BARF);
  }
  else
  {
    pB->m_list = NULL;

    if ( pQ->Last == NULL )
    {
      pQ->Head=pQ->Last=pB;
    }
    else
    {
      pQ->Last->m_list = pB;
      pQ->Last = pB;
    }
  }

  RESTORE_INT_STATE;
}

/* ------------------------- */

BUFCHAIN  QueueGet ( BUFQUEUE *pQ )
{
  BUFCHAIN pB;
  SAVE_INT_STATE;

  if ( pQ == NULL || pQ->Head == NULL )
  {
    pB = NULL;
  }
  else
  {
    pB = pQ->Head;
    pQ->Head = pB->m_list;

    if ( pQ->Head == NULL )
      pQ->Last = NULL;
  }

  RESTORE_INT_STATE;
  return pB;
}

/* ------------------------- */

BUFCHAIN  QueuePeek ( BUFQUEUE *pQ )
{
  if ( pQ == NULL )
    return NULL;
  return pQ->Head;
}


/* Initialise routine ------------------------------- */

err_t Buf_Init ( void )
{
  _kernel_swi_regs R;

  Mbufs_Attached = false;

  Net_Mbctl.mbcsize = sizeof(struct mbctl);
  Net_Mbctl.mbcvers = MBUF_MANAGER_VERSION;
  Net_Mbctl.flags   = 0;     /* Normal entry points */
  Net_Mbctl.advminubs = 112; /* Advisory Minimum mbuf size; this
                             is the DCI-2 value, and in fact allows
                             most SMB packets to live in one mbuf */
  Net_Mbctl.advmaxubs = 1280; /* Advisory max mbuf size; in fact we
                             never really use the full 1500 bytes;
                             block writes tend to do it in 1K chunks */
  Net_Mbctl.mincontig = 0;    /* We don't use the 'make contiguous'
                             routine at present; no need to specify a
                             value here */
  Net_Mbctl.spare1 = 0;

  R.r[0] = (int) &Net_Mbctl;
  if ( _kernel_swi ( Mbuf_OpenSession, &R, &R ) != NULL )
    return EMBUFMODULE;

  Mbufs_Attached = true;
  return OK;
}

/* --------------------------- */

void Buf_Shutdown(void)
{
  _kernel_swi_regs R;
  if ( Mbufs_Attached )
  {
    R.r[0] = (int) &Net_Mbctl;
    _kernel_swi ( Mbuf_CloseSession, &R, &R );
  }
  Mbufs_Attached = false;
}