/* $Id: hysdn_boot.c,v 1.4.6.4 2001/09/23 22:24:54 kai Exp $1*2* Linux driver for HYSDN cards3* specific routines for booting and pof handling4*5* Author Werner Cornelius ([email protected]) for Hypercope GmbH6* Copyright 1999 by Werner Cornelius ([email protected])7*8* This software may be used and distributed according to the terms9* of the GNU General Public License, incorporated herein by reference.10*11*/1213#include <linux/vmalloc.h>14#include <linux/slab.h>15#include <asm/uaccess.h>1617#include "hysdn_defs.h"18#include "hysdn_pof.h"1920/********************************/21/* defines for pof read handler */22/********************************/23#define POF_READ_FILE_HEAD 024#define POF_READ_TAG_HEAD 125#define POF_READ_TAG_DATA 22627/************************************************************/28/* definition of boot specific data area. This data is only */29/* needed during boot and so allocated dynamically. */30/************************************************************/31struct boot_data {32unsigned short Cryptor; /* for use with Decrypt function */33unsigned short Nrecs; /* records remaining in file */34unsigned char pof_state;/* actual state of read handler */35unsigned char is_crypted;/* card data is crypted */36int BufSize; /* actual number of bytes bufferd */37int last_error; /* last occurred error */38unsigned short pof_recid;/* actual pof recid */39unsigned long pof_reclen;/* total length of pof record data */40unsigned long pof_recoffset;/* actual offset inside pof record */41union {42unsigned char BootBuf[BOOT_BUF_SIZE];/* buffer as byte count */43tPofRecHdr PofRecHdr; /* header for actual record/chunk */44tPofFileHdr PofFileHdr; /* header from POF file */45tPofTimeStamp PofTime; /* time information */46} buf;47};4849/*****************************************************/50/* start decryption of successive POF file chuncks. */51/* */52/* to be called at start of POF file reading, */53/* before starting any decryption on any POF record. */54/*****************************************************/55static void56StartDecryption(struct boot_data *boot)57{58boot->Cryptor = CRYPT_STARTTERM;59} /* StartDecryption */606162/***************************************************************/63/* decrypt complete BootBuf */64/* NOTE: decryption must be applied to all or none boot tags - */65/* to HI and LO boot loader and (all) seq tags, because */66/* global Cryptor is started for whole POF. */67/***************************************************************/68static void69DecryptBuf(struct boot_data *boot, int cnt)70{71unsigned char *bufp = boot->buf.BootBuf;7273while (cnt--) {74boot->Cryptor = (boot->Cryptor >> 1) ^ ((boot->Cryptor & 1U) ? CRYPT_FEEDTERM : 0);75*bufp++ ^= (unsigned char)boot->Cryptor;76}77} /* DecryptBuf */7879/********************************************************************************/80/* pof_handle_data executes the required actions dependent on the active record */81/* id. If successful 0 is returned, a negative value shows an error. */82/********************************************************************************/83static int84pof_handle_data(hysdn_card * card, int datlen)85{86struct boot_data *boot = card->boot; /* pointer to boot specific data */87long l;88unsigned char *imgp;89int img_len;9091/* handle the different record types */92switch (boot->pof_recid) {9394case TAG_TIMESTMP:95if (card->debug_flags & LOG_POF_RECORD)96hysdn_addlog(card, "POF created %s", boot->buf.PofTime.DateTimeText);97break;9899case TAG_CBOOTDTA:100DecryptBuf(boot, datlen); /* we need to encrypt the buffer */101case TAG_BOOTDTA:102if (card->debug_flags & LOG_POF_RECORD)103hysdn_addlog(card, "POF got %s len=%d offs=0x%lx",104(boot->pof_recid == TAG_CBOOTDTA) ? "CBOOTDATA" : "BOOTDTA",105datlen, boot->pof_recoffset);106107if (boot->pof_reclen != POF_BOOT_LOADER_TOTAL_SIZE) {108boot->last_error = EPOF_BAD_IMG_SIZE; /* invalid length */109return (boot->last_error);110}111imgp = boot->buf.BootBuf; /* start of buffer */112img_len = datlen; /* maximum length to transfer */113114l = POF_BOOT_LOADER_OFF_IN_PAGE -115(boot->pof_recoffset & (POF_BOOT_LOADER_PAGE_SIZE - 1));116if (l > 0) {117/* buffer needs to be truncated */118imgp += l; /* advance pointer */119img_len -= l; /* adjust len */120}121/* at this point no special handling for data wrapping over buffer */122/* is necessary, because the boot image always will be adjusted to */123/* match a page boundary inside the buffer. */124/* The buffer for the boot image on the card is filled in 2 cycles */125/* first the 1024 hi-words are put in the buffer, then the low 1024 */126/* word are handled in the same way with different offset. */127128if (img_len > 0) {129/* data available for copy */130if ((boot->last_error =131card->writebootimg(card, imgp,132(boot->pof_recoffset > POF_BOOT_LOADER_PAGE_SIZE) ? 2 : 0)) < 0)133return (boot->last_error);134}135break; /* end of case boot image hi/lo */136137case TAG_CABSDATA:138DecryptBuf(boot, datlen); /* we need to encrypt the buffer */139case TAG_ABSDATA:140if (card->debug_flags & LOG_POF_RECORD)141hysdn_addlog(card, "POF got %s len=%d offs=0x%lx",142(boot->pof_recid == TAG_CABSDATA) ? "CABSDATA" : "ABSDATA",143datlen, boot->pof_recoffset);144145if ((boot->last_error = card->writebootseq(card, boot->buf.BootBuf, datlen)) < 0)146return (boot->last_error); /* error writing data */147148if (boot->pof_recoffset + datlen >= boot->pof_reclen)149return (card->waitpofready(card)); /* data completely spooled, wait for ready */150151break; /* end of case boot seq data */152153default:154if (card->debug_flags & LOG_POF_RECORD)155hysdn_addlog(card, "POF got data(id=0x%lx) len=%d offs=0x%lx", boot->pof_recid,156datlen, boot->pof_recoffset);157158break; /* simply skip record */159} /* switch boot->pof_recid */160161return (0);162} /* pof_handle_data */163164165/******************************************************************************/166/* pof_write_buffer is called when the buffer has been filled with the needed */167/* number of data bytes. The number delivered is additionally supplied for */168/* verification. The functions handles the data and returns the needed number */169/* of bytes for the next action. If the returned value is 0 or less an error */170/* occurred and booting must be aborted. */171/******************************************************************************/172int173pof_write_buffer(hysdn_card * card, int datlen)174{175struct boot_data *boot = card->boot; /* pointer to boot specific data */176177if (!boot)178return (-EFAULT); /* invalid call */179if (boot->last_error < 0)180return (boot->last_error); /* repeated error */181182if (card->debug_flags & LOG_POF_WRITE)183hysdn_addlog(card, "POF write: got %d bytes ", datlen);184185switch (boot->pof_state) {186case POF_READ_FILE_HEAD:187if (card->debug_flags & LOG_POF_WRITE)188hysdn_addlog(card, "POF write: checking file header");189190if (datlen != sizeof(tPofFileHdr)) {191boot->last_error = -EPOF_INTERNAL;192break;193}194if (boot->buf.PofFileHdr.Magic != TAGFILEMAGIC) {195boot->last_error = -EPOF_BAD_MAGIC;196break;197}198/* Setup the new state and vars */199boot->Nrecs = (unsigned short)(boot->buf.PofFileHdr.N_PofRecs); /* limited to 65535 */200boot->pof_state = POF_READ_TAG_HEAD; /* now start with single tags */201boot->last_error = sizeof(tPofRecHdr); /* new length */202break;203204case POF_READ_TAG_HEAD:205if (card->debug_flags & LOG_POF_WRITE)206hysdn_addlog(card, "POF write: checking tag header");207208if (datlen != sizeof(tPofRecHdr)) {209boot->last_error = -EPOF_INTERNAL;210break;211}212boot->pof_recid = boot->buf.PofRecHdr.PofRecId; /* actual pof recid */213boot->pof_reclen = boot->buf.PofRecHdr.PofRecDataLen; /* total length */214boot->pof_recoffset = 0; /* no starting offset */215216if (card->debug_flags & LOG_POF_RECORD)217hysdn_addlog(card, "POF: got record id=0x%lx length=%ld ",218boot->pof_recid, boot->pof_reclen);219220boot->pof_state = POF_READ_TAG_DATA; /* now start with tag data */221if (boot->pof_reclen < BOOT_BUF_SIZE)222boot->last_error = boot->pof_reclen; /* limit size */223else224boot->last_error = BOOT_BUF_SIZE; /* maximum */225226if (!boot->last_error) { /* no data inside record */227boot->pof_state = POF_READ_TAG_HEAD; /* now start with single tags */228boot->last_error = sizeof(tPofRecHdr); /* new length */229}230break;231232case POF_READ_TAG_DATA:233if (card->debug_flags & LOG_POF_WRITE)234hysdn_addlog(card, "POF write: getting tag data");235236if (datlen != boot->last_error) {237boot->last_error = -EPOF_INTERNAL;238break;239}240if ((boot->last_error = pof_handle_data(card, datlen)) < 0)241return (boot->last_error); /* an error occurred */242boot->pof_recoffset += datlen;243if (boot->pof_recoffset >= boot->pof_reclen) {244boot->pof_state = POF_READ_TAG_HEAD; /* now start with single tags */245boot->last_error = sizeof(tPofRecHdr); /* new length */246} else {247if (boot->pof_reclen - boot->pof_recoffset < BOOT_BUF_SIZE)248boot->last_error = boot->pof_reclen - boot->pof_recoffset; /* limit size */249else250boot->last_error = BOOT_BUF_SIZE; /* maximum */251}252break;253254default:255boot->last_error = -EPOF_INTERNAL; /* unknown state */256break;257} /* switch (boot->pof_state) */258259return (boot->last_error);260} /* pof_write_buffer */261262263/*******************************************************************************/264/* pof_write_open is called when an open for boot on the cardlog device occurs. */265/* The function returns the needed number of bytes for the next operation. If */266/* the returned number is less or equal 0 an error specified by this code */267/* occurred. Additionally the pointer to the buffer data area is set on success */268/*******************************************************************************/269int270pof_write_open(hysdn_card * card, unsigned char **bufp)271{272struct boot_data *boot; /* pointer to boot specific data */273274if (card->boot) {275if (card->debug_flags & LOG_POF_OPEN)276hysdn_addlog(card, "POF open: already opened for boot");277return (-ERR_ALREADY_BOOT); /* boot already active */278}279/* error no mem available */280if (!(boot = kzalloc(sizeof(struct boot_data), GFP_KERNEL))) {281if (card->debug_flags & LOG_MEM_ERR)282hysdn_addlog(card, "POF open: unable to allocate mem");283return (-EFAULT);284}285card->boot = boot;286card->state = CARD_STATE_BOOTING;287288card->stopcard(card); /* first stop the card */289if (card->testram(card)) {290if (card->debug_flags & LOG_POF_OPEN)291hysdn_addlog(card, "POF open: DPRAM test failure");292boot->last_error = -ERR_BOARD_DPRAM;293card->state = CARD_STATE_BOOTERR; /* show boot error */294return (boot->last_error);295}296boot->BufSize = 0; /* Buffer is empty */297boot->pof_state = POF_READ_FILE_HEAD; /* read file header */298StartDecryption(boot); /* if POF File should be encrypted */299300if (card->debug_flags & LOG_POF_OPEN)301hysdn_addlog(card, "POF open: success");302303*bufp = boot->buf.BootBuf; /* point to buffer */304return (sizeof(tPofFileHdr));305} /* pof_write_open */306307/********************************************************************************/308/* pof_write_close is called when an close of boot on the cardlog device occurs. */309/* The return value must be 0 if everything has happened as desired. */310/********************************************************************************/311int312pof_write_close(hysdn_card * card)313{314struct boot_data *boot = card->boot; /* pointer to boot specific data */315316if (!boot)317return (-EFAULT); /* invalid call */318319card->boot = NULL; /* no boot active */320kfree(boot);321322if (card->state == CARD_STATE_RUN)323card->set_errlog_state(card, 1); /* activate error log */324325if (card->debug_flags & LOG_POF_OPEN)326hysdn_addlog(card, "POF close: success");327328return (0);329} /* pof_write_close */330331/*********************************************************************************/332/* EvalSysrTokData checks additional records delivered with the Sysready Message */333/* when POF has been booted. A return value of 0 is used if no error occurred. */334/*********************************************************************************/335int336EvalSysrTokData(hysdn_card *card, unsigned char *cp, int len)337{338u_char *p;339u_char crc;340341if (card->debug_flags & LOG_POF_RECORD)342hysdn_addlog(card, "SysReady Token data length %d", len);343344if (len < 2) {345hysdn_addlog(card, "SysReady Token Data to short");346return (1);347}348for (p = cp, crc = 0; p < (cp + len - 2); p++)349if ((crc & 0x80))350crc = (((u_char) (crc << 1)) + 1) + *p;351else352crc = ((u_char) (crc << 1)) + *p;353crc = ~crc;354if (crc != *(cp + len - 1)) {355hysdn_addlog(card, "SysReady Token Data invalid CRC");356return (1);357}358len--; /* don't check CRC byte */359while (len > 0) {360361if (*cp == SYSR_TOK_END)362return (0); /* End of Token stream */363364if (len < (*(cp + 1) + 2)) {365hysdn_addlog(card, "token 0x%x invalid length %d", *cp, *(cp + 1));366return (1);367}368switch (*cp) {369case SYSR_TOK_B_CHAN: /* 1 */370if (*(cp + 1) != 1)371return (1); /* length invalid */372card->bchans = *(cp + 2);373break;374375case SYSR_TOK_FAX_CHAN: /* 2 */376if (*(cp + 1) != 1)377return (1); /* length invalid */378card->faxchans = *(cp + 2);379break;380381case SYSR_TOK_MAC_ADDR: /* 3 */382if (*(cp + 1) != 6)383return (1); /* length invalid */384memcpy(card->mac_addr, cp + 2, 6);385break;386387default:388hysdn_addlog(card, "unknown token 0x%02x length %d", *cp, *(cp + 1));389break;390}391len -= (*(cp + 1) + 2); /* adjust len */392cp += (*(cp + 1) + 2); /* and pointer */393}394395hysdn_addlog(card, "no end token found");396return (1);397} /* EvalSysrTokData */398399400