Logo Search packages:      
Sourcecode: linux version File versions  Download package

hysdn_boot.c

/* $Id: hysdn_boot.c,v 1.4.6.4 2001/09/23 22:24:54 kai Exp $
 *
 * Linux driver for HYSDN cards
 * specific routines for booting and pof handling
 *
 * Author    Werner Cornelius (werner@titro.de) for Hypercope GmbH
 * Copyright 1999 by Werner Cornelius (werner@titro.de)
 *
 * This software may be used and distributed according to the terms
 * of the GNU General Public License, incorporated herein by reference.
 *
 */

#include <linux/vmalloc.h>
#include <linux/slab.h>
#include <asm/uaccess.h>

#include "hysdn_defs.h"
#include "hysdn_pof.h"

/********************************/
/* defines for pof read handler */
/********************************/
#define POF_READ_FILE_HEAD  0
#define POF_READ_TAG_HEAD   1
#define POF_READ_TAG_DATA   2

/************************************************************/
/* definition of boot specific data area. This data is only */
/* needed during boot and so allocated dynamically.         */
/************************************************************/
struct boot_data {
      unsigned short Cryptor; /* for use with Decrypt function */
      unsigned short Nrecs;   /* records remaining in file */
      unsigned char pof_state;/* actual state of read handler */
      unsigned char is_crypted;/* card data is crypted */
      int BufSize;            /* actual number of bytes bufferd */
      int last_error;         /* last occurred error */
      unsigned short pof_recid;/* actual pof recid */
      unsigned long pof_reclen;/* total length of pof record data */
      unsigned long pof_recoffset;/* actual offset inside pof record */
      union {
            unsigned char BootBuf[BOOT_BUF_SIZE];/* buffer as byte count */
            tPofRecHdr PofRecHdr;   /* header for actual record/chunk */
            tPofFileHdr PofFileHdr;       /* header from POF file */
            tPofTimeStamp PofTime;  /* time information */
      } buf;
};

/*****************************************************/
/*  start decryption of successive POF file chuncks.  */
/*                                                   */
/*  to be called at start of POF file reading,       */
/*  before starting any decryption on any POF record. */
/*****************************************************/
static void
StartDecryption(struct boot_data *boot)
{
      boot->Cryptor = CRYPT_STARTTERM;
}                       /* StartDecryption */


/***************************************************************/
/* decrypt complete BootBuf                                    */
/* NOTE: decryption must be applied to all or none boot tags - */
/*       to HI and LO boot loader and (all) seq tags, because  */
/*       global Cryptor is started for whole POF.              */
/***************************************************************/
static void
DecryptBuf(struct boot_data *boot, int cnt)
{
      unsigned char *bufp = boot->buf.BootBuf;

      while (cnt--) {
            boot->Cryptor = (boot->Cryptor >> 1) ^ ((boot->Cryptor & 1U) ? CRYPT_FEEDTERM : 0);
            *bufp++ ^= (unsigned char)boot->Cryptor;
      }
}                       /* DecryptBuf */

/********************************************************************************/
/* pof_handle_data executes the required actions dependent on the active record */
/* id. If successful 0 is returned, a negative value shows an error.           */
/********************************************************************************/
static int
pof_handle_data(hysdn_card * card, int datlen)
{
      struct boot_data *boot = card->boot;      /* pointer to boot specific data */
      long l;
      unsigned char *imgp;
      int img_len;

      /* handle the different record types */
      switch (boot->pof_recid) {

            case TAG_TIMESTMP:
                  if (card->debug_flags & LOG_POF_RECORD)
                        hysdn_addlog(card, "POF created %s", boot->buf.PofTime.DateTimeText);
                  break;

            case TAG_CBOOTDTA:
                  DecryptBuf(boot, datlen);     /* we need to encrypt the buffer */
            case TAG_BOOTDTA:
                  if (card->debug_flags & LOG_POF_RECORD)
                        hysdn_addlog(card, "POF got %s len=%d offs=0x%lx",
                                   (boot->pof_recid == TAG_CBOOTDTA) ? "CBOOTDATA" : "BOOTDTA",
                                   datlen, boot->pof_recoffset);

                  if (boot->pof_reclen != POF_BOOT_LOADER_TOTAL_SIZE) {
                        boot->last_error = EPOF_BAD_IMG_SIZE;     /* invalid length */
                        return (boot->last_error);
                  }
                  imgp = boot->buf.BootBuf;     /* start of buffer */
                  img_len = datlen; /* maximum length to transfer */

                  l = POF_BOOT_LOADER_OFF_IN_PAGE -
                      (boot->pof_recoffset & (POF_BOOT_LOADER_PAGE_SIZE - 1));
                  if (l > 0) {
                        /* buffer needs to be truncated */
                        imgp += l;  /* advance pointer */
                        img_len -= l;     /* adjust len */
                  }
                  /* at this point no special handling for data wrapping over buffer */
                  /* is necessary, because the boot image always will be adjusted to */
                  /* match a page boundary inside the buffer.                        */
                  /* The buffer for the boot image on the card is filled in 2 cycles */
                  /* first the 1024 hi-words are put in the buffer, then the low 1024 */
                  /* word are handled in the same way with different offset.         */

                  if (img_len > 0) {
                        /* data available for copy */
                        if ((boot->last_error =
                             card->writebootimg(card, imgp,
                                          (boot->pof_recoffset > POF_BOOT_LOADER_PAGE_SIZE) ? 2 : 0)) < 0)
                              return (boot->last_error);
                  }
                  break;      /* end of case boot image hi/lo */

            case TAG_CABSDATA:
                  DecryptBuf(boot, datlen);     /* we need to encrypt the buffer */
            case TAG_ABSDATA:
                  if (card->debug_flags & LOG_POF_RECORD)
                        hysdn_addlog(card, "POF got %s len=%d offs=0x%lx",
                                   (boot->pof_recid == TAG_CABSDATA) ? "CABSDATA" : "ABSDATA",
                                   datlen, boot->pof_recoffset);

                  if ((boot->last_error = card->writebootseq(card, boot->buf.BootBuf, datlen) < 0))
                        return (boot->last_error);    /* error writing data */

                  if (boot->pof_recoffset + datlen >= boot->pof_reclen)
                        return (card->waitpofready(card));  /* data completely spooled, wait for ready */

                  break;      /* end of case boot seq data */

            default:
                  if (card->debug_flags & LOG_POF_RECORD)
                        hysdn_addlog(card, "POF got data(id=0x%lx) len=%d offs=0x%lx", boot->pof_recid,
                                   datlen, boot->pof_recoffset);

                  break;      /* simply skip record */
      }                 /* switch boot->pof_recid */

      return (0);
}                       /* pof_handle_data */


/******************************************************************************/
/* pof_write_buffer is called when the buffer has been filled with the needed */
/* number of data bytes. The number delivered is additionally supplied for    */
/* verification. The functions handles the data and returns the needed number */
/* of bytes for the next action. If the returned value is 0 or less an error  */
/* occurred and booting must be aborted.                                       */
/******************************************************************************/
int
pof_write_buffer(hysdn_card * card, int datlen)
{
      struct boot_data *boot = card->boot;      /* pointer to boot specific data */

      if (!boot)
            return (-EFAULT); /* invalid call */
      if (boot->last_error < 0)
            return (boot->last_error);    /* repeated error */

      if (card->debug_flags & LOG_POF_WRITE)
            hysdn_addlog(card, "POF write: got %d bytes ", datlen);

      switch (boot->pof_state) {
            case POF_READ_FILE_HEAD:
                  if (card->debug_flags & LOG_POF_WRITE)
                        hysdn_addlog(card, "POF write: checking file header");

                  if (datlen != sizeof(tPofFileHdr)) {
                        boot->last_error = -EPOF_INTERNAL;
                        break;
                  }
                  if (boot->buf.PofFileHdr.Magic != TAGFILEMAGIC) {
                        boot->last_error = -EPOF_BAD_MAGIC;
                        break;
                  }
                  /* Setup the new state and vars */
                  boot->Nrecs = (unsigned short)(boot->buf.PofFileHdr.N_PofRecs);   /* limited to 65535 */
                  boot->pof_state = POF_READ_TAG_HEAD;      /* now start with single tags */
                  boot->last_error = sizeof(tPofRecHdr);    /* new length */
                  break;

            case POF_READ_TAG_HEAD:
                  if (card->debug_flags & LOG_POF_WRITE)
                        hysdn_addlog(card, "POF write: checking tag header");

                  if (datlen != sizeof(tPofRecHdr)) {
                        boot->last_error = -EPOF_INTERNAL;
                        break;
                  }
                  boot->pof_recid = boot->buf.PofRecHdr.PofRecId;       /* actual pof recid */
                  boot->pof_reclen = boot->buf.PofRecHdr.PofRecDataLen; /* total length */
                  boot->pof_recoffset = 0;      /* no starting offset */

                  if (card->debug_flags & LOG_POF_RECORD)
                        hysdn_addlog(card, "POF: got record id=0x%lx length=%ld ",
                              boot->pof_recid, boot->pof_reclen);

                  boot->pof_state = POF_READ_TAG_DATA;      /* now start with tag data */
                  if (boot->pof_reclen < BOOT_BUF_SIZE)
                        boot->last_error = boot->pof_reclen;      /* limit size */
                  else
                        boot->last_error = BOOT_BUF_SIZE;   /* maximum */

                  if (!boot->last_error) {      /* no data inside record */
                        boot->pof_state = POF_READ_TAG_HEAD;      /* now start with single tags */
                        boot->last_error = sizeof(tPofRecHdr);    /* new length */
                  }
                  break;

            case POF_READ_TAG_DATA:
                  if (card->debug_flags & LOG_POF_WRITE)
                        hysdn_addlog(card, "POF write: getting tag data");

                  if (datlen != boot->last_error) {
                        boot->last_error = -EPOF_INTERNAL;
                        break;
                  }
                  if ((boot->last_error = pof_handle_data(card, datlen)) < 0)
                        return (boot->last_error);    /* an error occurred */
                  boot->pof_recoffset += datlen;
                  if (boot->pof_recoffset >= boot->pof_reclen) {
                        boot->pof_state = POF_READ_TAG_HEAD;      /* now start with single tags */
                        boot->last_error = sizeof(tPofRecHdr);    /* new length */
                  } else {
                        if (boot->pof_reclen - boot->pof_recoffset < BOOT_BUF_SIZE)
                              boot->last_error = boot->pof_reclen - boot->pof_recoffset;  /* limit size */
                        else
                              boot->last_error = BOOT_BUF_SIZE;   /* maximum */
                  }
                  break;

            default:
                  boot->last_error = -EPOF_INTERNAL;  /* unknown state */
                  break;
      }                 /* switch (boot->pof_state) */

      return (boot->last_error);
}                       /* pof_write_buffer */


/*******************************************************************************/
/* pof_write_open is called when an open for boot on the cardlog device occurs. */
/* The function returns the needed number of bytes for the next operation. If  */
/* the returned number is less or equal 0 an error specified by this code      */
/* occurred. Additionally the pointer to the buffer data area is set on success */
/*******************************************************************************/
int
pof_write_open(hysdn_card * card, unsigned char **bufp)
{
      struct boot_data *boot; /* pointer to boot specific data */

      if (card->boot) {
            if (card->debug_flags & LOG_POF_OPEN)
                  hysdn_addlog(card, "POF open: already opened for boot");
            return (-ERR_ALREADY_BOOT);   /* boot already active */
      }
      /* error no mem available */
      if (!(boot = kzalloc(sizeof(struct boot_data), GFP_KERNEL))) {
            if (card->debug_flags & LOG_MEM_ERR)
                  hysdn_addlog(card, "POF open: unable to allocate mem");
            return (-EFAULT);
      }
      card->boot = boot;
      card->state = CARD_STATE_BOOTING;

      card->stopcard(card);   /* first stop the card */
      if (card->testram(card)) {
            if (card->debug_flags & LOG_POF_OPEN)
                  hysdn_addlog(card, "POF open: DPRAM test failure");
            boot->last_error = -ERR_BOARD_DPRAM;
            card->state = CARD_STATE_BOOTERR;   /* show boot error */
            return (boot->last_error);
      }
      boot->BufSize = 0;      /* Buffer is empty */
      boot->pof_state = POF_READ_FILE_HEAD;     /* read file header */
      StartDecryption(boot);  /* if POF File should be encrypted */

      if (card->debug_flags & LOG_POF_OPEN)
            hysdn_addlog(card, "POF open: success");

      *bufp = boot->buf.BootBuf;    /* point to buffer */
      return (sizeof(tPofFileHdr));
}                       /* pof_write_open */

/********************************************************************************/
/* pof_write_close is called when an close of boot on the cardlog device occurs. */
/* The return value must be 0 if everything has happened as desired.            */
/********************************************************************************/
int
pof_write_close(hysdn_card * card)
{
      struct boot_data *boot = card->boot;      /* pointer to boot specific data */

      if (!boot)
            return (-EFAULT); /* invalid call */

      card->boot = NULL;      /* no boot active */
      kfree(boot);

      if (card->state == CARD_STATE_RUN)
            card->set_errlog_state(card, 1);    /* activate error log */

      if (card->debug_flags & LOG_POF_OPEN)
            hysdn_addlog(card, "POF close: success");

      return (0);
}                       /* pof_write_close */

/*********************************************************************************/
/* EvalSysrTokData checks additional records delivered with the Sysready Message */
/* when POF has been booted. A return value of 0 is used if no error occurred.    */
/*********************************************************************************/
int
EvalSysrTokData(hysdn_card *card, unsigned char *cp, int len)
{
      u_char *p;
      u_char crc;

      if (card->debug_flags & LOG_POF_RECORD)
            hysdn_addlog(card, "SysReady Token data length %d", len);

      if (len < 2) {
            hysdn_addlog(card, "SysReady Token Data to short");
            return (1);
      }
      for (p = cp, crc = 0; p < (cp + len - 2); p++)
            if ((crc & 0x80))
                  crc = (((u_char) (crc << 1)) + 1) + *p;
            else
                  crc = ((u_char) (crc << 1)) + *p;
      crc = ~crc;
      if (crc != *(cp + len - 1)) {
            hysdn_addlog(card, "SysReady Token Data invalid CRC");
            return (1);
      }
      len--;                  /* don't check CRC byte */
      while (len > 0) {

            if (*cp == SYSR_TOK_END)
                  return (0); /* End of Token stream */

            if (len < (*(cp + 1) + 2)) {
                  hysdn_addlog(card, "token 0x%x invalid length %d", *cp, *(cp + 1));
                  return (1);
            }
            switch (*cp) {
                  case SYSR_TOK_B_CHAN:   /* 1 */
                        if (*(cp + 1) != 1)
                              return (1); /* length invalid */
                        card->bchans = *(cp + 2);
                        break;

                  case SYSR_TOK_FAX_CHAN: /* 2 */
                        if (*(cp + 1) != 1)
                              return (1); /* length invalid */
                        card->faxchans = *(cp + 2);
                        break;

                  case SYSR_TOK_MAC_ADDR: /* 3 */
                        if (*(cp + 1) != 6)
                              return (1); /* length invalid */
                        memcpy(card->mac_addr, cp + 2, 6);
                        break;

                  default:
                        hysdn_addlog(card, "unknown token 0x%02x length %d", *cp, *(cp + 1));
                        break;
            }
            len -= (*(cp + 1) + 2);       /* adjust len */
            cp += (*(cp + 1) + 2);  /* and pointer */
      }

      hysdn_addlog(card, "no end token found");
      return (1);
}                       /* EvalSysrTokData */

Generated by  Doxygen 1.6.0   Back to index