2005-04-17 02:20:36 +04:00
/* $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 {
2006-03-25 14:07:04 +03:00
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 */
2005-04-17 02:20:36 +04:00
int BufSize ; /* actual number of bytes bufferd */
int last_error ; /* last occurred error */
2006-03-25 14:07:04 +03:00
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 */
2005-04-17 02:20:36 +04:00
union {
2006-03-25 14:07:04 +03:00
unsigned char BootBuf [ BOOT_BUF_SIZE ] ; /* buffer as byte count */
2005-04-17 02:20:36 +04:00
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. */
/*****************************************************/
2005-06-29 07:44:56 +04:00
static void
2005-04-17 02:20:36 +04:00
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. */
/***************************************************************/
2005-06-29 07:44:56 +04:00
static void
2005-04-17 02:20:36 +04:00
DecryptBuf ( struct boot_data * boot , int cnt )
{
2006-03-25 14:07:04 +03:00
unsigned char * bufp = boot - > buf . BootBuf ;
2005-04-17 02:20:36 +04:00
while ( cnt - - ) {
boot - > Cryptor = ( boot - > Cryptor > > 1 ) ^ ( ( boot - > Cryptor & 1U ) ? CRYPT_FEEDTERM : 0 ) ;
2006-03-25 14:07:04 +03:00
* bufp + + ^ = ( unsigned char ) boot - > Cryptor ;
2005-04-17 02:20:36 +04:00
}
} /* 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
2012-02-20 07:52:38 +04:00
pof_handle_data ( hysdn_card * card , int datlen )
2005-04-17 02:20:36 +04:00
{
struct boot_data * boot = card - > boot ; /* pointer to boot specific data */
long l ;
2006-03-25 14:07:04 +03:00
unsigned char * imgp ;
2005-04-17 02:20:36 +04:00
int img_len ;
/* handle the different record types */
switch ( boot - > pof_recid ) {
2012-02-20 07:52:38 +04:00
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 */
2005-04-17 02:20:36 +04:00
2012-02-20 07:52:38 +04:00
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 ) ;
2005-04-17 02:20:36 +04:00
2012-02-20 07:52:38 +04:00
if ( ( boot - > last_error = card - > writebootseq ( card , boot - > buf . BootBuf , datlen ) ) < 0 )
return ( boot - > last_error ) ; /* error writing data */
2005-04-17 02:20:36 +04:00
2012-02-20 07:52:38 +04:00
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 ) ;
2005-04-17 02:20:36 +04:00
2012-02-20 07:52:38 +04:00
break ; /* simply skip record */
2005-04-17 02:20:36 +04:00
} /* 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
2012-02-20 07:52:38 +04:00
pof_write_buffer ( hysdn_card * card , int datlen )
2005-04-17 02:20:36 +04:00
{
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 ) {
2012-02-20 07:52:38 +04:00
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 ;
2005-04-17 02:20:36 +04:00
break ;
2012-02-20 07:52:38 +04:00
}
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 */
2005-04-17 02:20:36 +04:00
2012-02-20 07:52:38 +04:00
if ( card - > debug_flags & LOG_POF_RECORD )
hysdn_addlog ( card , " POF: got record id=0x%lx length=%ld " ,
boot - > pof_recid , boot - > pof_reclen ) ;
2005-04-17 02:20:36 +04:00
2012-02-20 07:52:38 +04:00
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 */
2005-04-17 02:20:36 +04:00
2012-02-20 07:52:38 +04:00
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 ;
2005-04-17 02:20:36 +04:00
2012-02-20 07:52:38 +04:00
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 ;
2005-04-17 02:20:36 +04:00
break ;
2012-02-20 07:52:38 +04:00
}
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 ;
2005-04-17 02:20:36 +04:00
} /* 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
2012-02-20 07:52:38 +04:00
pof_write_open ( hysdn_card * card , unsigned char * * bufp )
2005-04-17 02:20:36 +04:00
{
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 */
2006-12-08 13:39:35 +03:00
if ( ! ( boot = kzalloc ( sizeof ( struct boot_data ) , GFP_KERNEL ) ) ) {
2005-04-17 02:20:36 +04:00
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
2012-02-20 07:52:38 +04:00
pof_write_close ( hysdn_card * card )
2005-04-17 02:20:36 +04:00
{
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
2006-03-25 14:07:04 +03:00
EvalSysrTokData ( hysdn_card * card , unsigned char * cp , int len )
2005-04-17 02:20:36 +04:00
{
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 ) {
2012-02-20 07:52:38 +04:00
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 ;
2005-04-17 02:20:36 +04:00
}
len - = ( * ( cp + 1 ) + 2 ) ; /* adjust len */
cp + = ( * ( cp + 1 ) + 2 ) ; /* and pointer */
}
hysdn_addlog ( card , " no end token found " ) ;
return ( 1 ) ;
} /* EvalSysrTokData */