2005-04-16 15:20:36 -07:00
/******************************************************************************
*
* Name : skvpd . c
* Project : GEnesis , PCI Gigabit Ethernet Adapter
* Version : $ Revision : 1.37 $
* Date : $ Date : 2003 / 01 / 13 10 : 42 : 45 $
* Purpose : Shared software to read and write VPD data
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/******************************************************************************
*
* ( C ) Copyright 1998 - 2003 SysKonnect GmbH .
*
* This program 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 .
*
* The information in this file is provided " AS IS " without warranty .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*
2006-06-30 18:25:18 +02:00
Please refer skvpd . txt for information how to include this module
2005-04-16 15:20:36 -07:00
*/
static const char SysKonnectFileId [ ] =
" @(#)$Id: skvpd.c,v 1.37 2003/01/13 10:42:45 rschmidt Exp $ (C) SK " ;
# include "h/skdrv1st.h"
# include "h/sktypes.h"
# include "h/skdebug.h"
# include "h/skdrv2nd.h"
/*
* Static functions
*/
# ifndef SK_KR_PROTO
static SK_VPD_PARA * vpd_find_para (
SK_AC * pAC ,
const char * key ,
SK_VPD_PARA * p ) ;
# else /* SK_KR_PROTO */
static SK_VPD_PARA * vpd_find_para ( ) ;
# endif /* SK_KR_PROTO */
/*
* waits for a completion of a VPD transfer
* The VPD transfer must complete within SK_TICKS_PER_SEC / 16
*
* returns 0 : success , transfer completes
* error exit ( 9 ) with a error message
*/
static int VpdWait (
SK_AC * pAC , /* Adapters context */
SK_IOC IoC , /* IO Context */
int event ) /* event to wait for (VPD_READ / VPD_write) completion*/
{
SK_U64 start_time ;
SK_U16 state ;
SK_DBG_MSG ( pAC , SK_DBGMOD_VPD , SK_DBGCAT_CTRL ,
( " VPD wait for %s \n " , event ? " Write " : " Read " ) ) ;
start_time = SkOsGetTime ( pAC ) ;
do {
if ( SkOsGetTime ( pAC ) - start_time > SK_TICKS_PER_SEC ) {
/* Bug fix AF: Thu Mar 28 2002
* Do not call : VPD_STOP ( pAC , IoC ) ;
* A pending VPD read cycle can not be aborted by writing
* VPD_WRITE to the PCI_VPD_ADR_REG ( VPD address register ) .
* Although the write threshold in the OUR - register protects
* VPD read only space from being overwritten this does not
* protect a VPD read from being ` converted ` into a VPD write
* operation ( on the fly ) . As a consequence the VPD_STOP would
* delete VPD read only data . In case of any problems with the
* I2C bus we exit the loop here . The I2C read operation can
* not be aborted except by a reset ( - > LR ) .
*/
SK_DBG_MSG ( pAC , SK_DBGMOD_VPD , SK_DBGCAT_FATAL | SK_DBGCAT_ERR ,
( " ERROR:VPD wait timeout \n " ) ) ;
return ( 1 ) ;
}
VPD_IN16 ( pAC , IoC , PCI_VPD_ADR_REG , & state ) ;
SK_DBG_MSG ( pAC , SK_DBGMOD_VPD , SK_DBGCAT_CTRL ,
( " state = %x, event %x \n " , state , event ) ) ;
} while ( ( int ) ( state & PCI_VPD_FLAG ) = = event ) ;
return ( 0 ) ;
}
# ifdef SKDIAG
/*
* Read the dword at address ' addr ' from the VPD EEPROM .
*
* Needed Time : MIN 1 , 3 ms MAX 2 , 6 ms
*
* Note : The DWord is returned in the endianess of the machine the routine
* is running on .
*
* Returns the data read .
*/
SK_U32 VpdReadDWord (
SK_AC * pAC , /* Adapters context */
SK_IOC IoC , /* IO Context */
int addr ) /* VPD address */
{
SK_U32 Rtv ;
/* start VPD read */
SK_DBG_MSG ( pAC , SK_DBGMOD_VPD , SK_DBGCAT_CTRL ,
( " VPD read dword at 0x%x \n " , addr ) ) ;
addr & = ~ VPD_WRITE ; /* ensure the R/W bit is set to read */
VPD_OUT16 ( pAC , IoC , PCI_VPD_ADR_REG , ( SK_U16 ) addr ) ;
/* ignore return code here */
( void ) VpdWait ( pAC , IoC , VPD_READ ) ;
/* Don't swap here, it's a data stream of bytes */
Rtv = 0 ;
VPD_IN32 ( pAC , IoC , PCI_VPD_DAT_REG , & Rtv ) ;
SK_DBG_MSG ( pAC , SK_DBGMOD_VPD , SK_DBGCAT_CTRL ,
( " VPD read dword data = 0x%x \n " , Rtv ) ) ;
return ( Rtv ) ;
}
# endif /* SKDIAG */
/*
* Read one Stream of ' len ' bytes of VPD data , starting at ' addr ' from
* or to the I2C EEPROM .
*
* Returns number of bytes read / written .
*/
static int VpdWriteStream (
SK_AC * pAC , /* Adapters context */
SK_IOC IoC , /* IO Context */
char * buf , /* data buffer */
int Addr , /* VPD start address */
int Len ) /* number of bytes to read / to write */
{
int i ;
int j ;
SK_U16 AdrReg ;
int Rtv ;
SK_U8 * pComp ; /* Compare pointer */
SK_U8 Data ; /* Input Data for Compare */
/* Init Compare Pointer */
pComp = ( SK_U8 * ) buf ;
for ( i = 0 ; i < Len ; i + + , buf + + ) {
if ( ( i % sizeof ( SK_U32 ) ) = = 0 ) {
/*
* At the begin of each cycle read the Data Reg
* So it is initialized even if only a few bytes
* are written .
*/
AdrReg = ( SK_U16 ) Addr ;
AdrReg & = ~ VPD_WRITE ; /* READ operation */
VPD_OUT16 ( pAC , IoC , PCI_VPD_ADR_REG , AdrReg ) ;
/* Wait for termination */
Rtv = VpdWait ( pAC , IoC , VPD_READ ) ;
if ( Rtv ! = 0 ) {
return ( i ) ;
}
}
/* Write current Byte */
VPD_OUT8 ( pAC , IoC , PCI_VPD_DAT_REG + ( i % sizeof ( SK_U32 ) ) ,
* ( SK_U8 * ) buf ) ;
if ( ( ( i % sizeof ( SK_U32 ) ) = = 3 ) | | ( i = = ( Len - 1 ) ) ) {
/* New Address needs to be written to VPD_ADDR reg */
AdrReg = ( SK_U16 ) Addr ;
Addr + = sizeof ( SK_U32 ) ;
AdrReg | = VPD_WRITE ; /* WRITE operation */
VPD_OUT16 ( pAC , IoC , PCI_VPD_ADR_REG , AdrReg ) ;
/* Wait for termination */
Rtv = VpdWait ( pAC , IoC , VPD_WRITE ) ;
if ( Rtv ! = 0 ) {
SK_DBG_MSG ( pAC , SK_DBGMOD_VPD , SK_DBGCAT_ERR ,
( " Write Timed Out \n " ) ) ;
return ( i - ( i % sizeof ( SK_U32 ) ) ) ;
}
/*
* Now re - read to verify
*/
AdrReg & = ~ VPD_WRITE ; /* READ operation */
VPD_OUT16 ( pAC , IoC , PCI_VPD_ADR_REG , AdrReg ) ;
/* Wait for termination */
Rtv = VpdWait ( pAC , IoC , VPD_READ ) ;
if ( Rtv ! = 0 ) {
SK_DBG_MSG ( pAC , SK_DBGMOD_VPD , SK_DBGCAT_ERR ,
( " Verify Timed Out \n " ) ) ;
return ( i - ( i % sizeof ( SK_U32 ) ) ) ;
}
for ( j = 0 ; j < = ( int ) ( i % sizeof ( SK_U32 ) ) ; j + + , pComp + + ) {
VPD_IN8 ( pAC , IoC , PCI_VPD_DAT_REG + j , & Data ) ;
if ( Data ! = * pComp ) {
/* Verify Error */
SK_DBG_MSG ( pAC , SK_DBGMOD_VPD , SK_DBGCAT_ERR ,
( " WriteStream Verify Error \n " ) ) ;
return ( i - ( i % sizeof ( SK_U32 ) ) + j ) ;
}
}
}
}
return ( Len ) ;
}
/*
* Read one Stream of ' len ' bytes of VPD data , starting at ' addr ' from
* or to the I2C EEPROM .
*
* Returns number of bytes read / written .
*/
static int VpdReadStream (
SK_AC * pAC , /* Adapters context */
SK_IOC IoC , /* IO Context */
char * buf , /* data buffer */
int Addr , /* VPD start address */
int Len ) /* number of bytes to read / to write */
{
int i ;
SK_U16 AdrReg ;
int Rtv ;
for ( i = 0 ; i < Len ; i + + , buf + + ) {
if ( ( i % sizeof ( SK_U32 ) ) = = 0 ) {
/* New Address needs to be written to VPD_ADDR reg */
AdrReg = ( SK_U16 ) Addr ;
Addr + = sizeof ( SK_U32 ) ;
AdrReg & = ~ VPD_WRITE ; /* READ operation */
VPD_OUT16 ( pAC , IoC , PCI_VPD_ADR_REG , AdrReg ) ;
/* Wait for termination */
Rtv = VpdWait ( pAC , IoC , VPD_READ ) ;
if ( Rtv ! = 0 ) {
return ( i ) ;
}
}
VPD_IN8 ( pAC , IoC , PCI_VPD_DAT_REG + ( i % sizeof ( SK_U32 ) ) ,
( SK_U8 * ) buf ) ;
}
return ( Len ) ;
}
/*
* Read ore writes ' len ' bytes of VPD data , starting at ' addr ' from
* or to the I2C EEPROM .
*
* Returns number of bytes read / written .
*/
static int VpdTransferBlock (
SK_AC * pAC , /* Adapters context */
SK_IOC IoC , /* IO Context */
char * buf , /* data buffer */
int addr , /* VPD start address */
int len , /* number of bytes to read / to write */
int dir ) /* transfer direction may be VPD_READ or VPD_WRITE */
{
int Rtv ; /* Return value */
int vpd_rom_size ;
SK_DBG_MSG ( pAC , SK_DBGMOD_VPD , SK_DBGCAT_CTRL ,
( " VPD %s block, addr = 0x%x, len = %d \n " ,
dir ? " write " : " read " , addr , len ) ) ;
if ( len = = 0 )
return ( 0 ) ;
vpd_rom_size = pAC - > vpd . rom_size ;
if ( addr > vpd_rom_size - 4 ) {
SK_DBG_MSG ( pAC , SK_DBGMOD_VPD , SK_DBGCAT_ERR | SK_DBGCAT_FATAL ,
( " Address error: 0x%x, exp. < 0x%x \n " ,
addr , vpd_rom_size - 4 ) ) ;
return ( 0 ) ;
}
if ( addr + len > vpd_rom_size ) {
len = vpd_rom_size - addr ;
SK_DBG_MSG ( pAC , SK_DBGMOD_VPD , SK_DBGCAT_ERR ,
( " Warning: len was cut to %d \n " , len ) ) ;
}
if ( dir = = VPD_READ ) {
Rtv = VpdReadStream ( pAC , IoC , buf , addr , len ) ;
}
else {
Rtv = VpdWriteStream ( pAC , IoC , buf , addr , len ) ;
}
return ( Rtv ) ;
}
# ifdef SKDIAG
/*
* Read ' len ' bytes of VPD data , starting at ' addr ' .
*
* Returns number of bytes read .
*/
int VpdReadBlock (
SK_AC * pAC , /* pAC pointer */
SK_IOC IoC , /* IO Context */
char * buf , /* buffer were the data should be stored */
int addr , /* start reading at the VPD address */
int len ) /* number of bytes to read */
{
return ( VpdTransferBlock ( pAC , IoC , buf , addr , len , VPD_READ ) ) ;
}
/*
* Write ' len ' bytes of * but to the VPD EEPROM , starting at ' addr ' .
*
* Returns number of bytes writes .
*/
int VpdWriteBlock (
SK_AC * pAC , /* pAC pointer */
SK_IOC IoC , /* IO Context */
char * buf , /* buffer, holds the data to write */
int addr , /* start writing at the VPD address */
int len ) /* number of bytes to write */
{
return ( VpdTransferBlock ( pAC , IoC , buf , addr , len , VPD_WRITE ) ) ;
}
# endif /* SKDIAG */
/*
* ( re ) initialize the VPD buffer
*
* Reads the VPD data from the EEPROM into the VPD buffer .
* Get the remaining read only and read / write space .
*
* return 0 : success
* 1 : fatal VPD error
*/
static int VpdInit (
SK_AC * pAC , /* Adapters context */
SK_IOC IoC ) /* IO Context */
{
SK_VPD_PARA * r , rp ; /* RW or RV */
int i ;
unsigned char x ;
int vpd_size ;
SK_U16 dev_id ;
SK_U32 our_reg2 ;
SK_DBG_MSG ( pAC , SK_DBGMOD_VPD , SK_DBGCAT_INIT , ( " VpdInit .. " ) ) ;
VPD_IN16 ( pAC , IoC , PCI_DEVICE_ID , & dev_id ) ;
VPD_IN32 ( pAC , IoC , PCI_OUR_REG_2 , & our_reg2 ) ;
pAC - > vpd . rom_size = 256 < < ( ( our_reg2 & PCI_VPD_ROM_SZ ) > > 14 ) ;
/*
* this function might get used before the hardware is initialized
* therefore we cannot always trust in GIChipId
*/
if ( ( ( pAC - > vpd . v . vpd_status & VPD_VALID ) = = 0 & &
dev_id ! = VPD_DEV_ID_GENESIS ) | |
( ( pAC - > vpd . v . vpd_status & VPD_VALID ) ! = 0 & &
! pAC - > GIni . GIGenesis ) ) {
/* for Yukon the VPD size is always 256 */
vpd_size = VPD_SIZE_YUKON ;
}
else {
/* Genesis uses the maximum ROM size up to 512 for VPD */
if ( pAC - > vpd . rom_size > VPD_SIZE_GENESIS ) {
vpd_size = VPD_SIZE_GENESIS ;
}
else {
vpd_size = pAC - > vpd . rom_size ;
}
}
/* read the VPD data into the VPD buffer */
if ( VpdTransferBlock ( pAC , IoC , pAC - > vpd . vpd_buf , 0 , vpd_size , VPD_READ )
! = vpd_size ) {
SK_DBG_MSG ( pAC , SK_DBGMOD_VPD , SK_DBGCAT_ERR ,
( " Block Read Error \n " ) ) ;
return ( 1 ) ;
}
pAC - > vpd . vpd_size = vpd_size ;
/* Asus K8V Se Deluxe bugfix. Correct VPD content */
/* MBo April 2004 */
if ( ( ( unsigned char ) pAC - > vpd . vpd_buf [ 0x3f ] = = 0x38 ) & &
( ( unsigned char ) pAC - > vpd . vpd_buf [ 0x40 ] = = 0x3c ) & &
( ( unsigned char ) pAC - > vpd . vpd_buf [ 0x41 ] = = 0x45 ) ) {
printk ( " sk98lin: Asus mainboard with buggy VPD? "
" Correcting data. \n " ) ;
pAC - > vpd . vpd_buf [ 0x40 ] = 0x38 ;
}
/* find the end tag of the RO area */
if ( ! ( r = vpd_find_para ( pAC , VPD_RV , & rp ) ) ) {
SK_DBG_MSG ( pAC , SK_DBGMOD_VPD , SK_DBGCAT_ERR | SK_DBGCAT_FATAL ,
( " Encoding Error: RV Tag not found \n " ) ) ;
return ( 1 ) ;
}
if ( r - > p_val + r - > p_len > pAC - > vpd . vpd_buf + vpd_size / 2 ) {
SK_DBG_MSG ( pAC , SK_DBGMOD_VPD , SK_DBGCAT_ERR | SK_DBGCAT_FATAL ,
( " Encoding Error: Invalid VPD struct size \n " ) ) ;
return ( 1 ) ;
}
pAC - > vpd . v . vpd_free_ro = r - > p_len - 1 ;
/* test the checksum */
for ( i = 0 , x = 0 ; ( unsigned ) i < = ( unsigned ) vpd_size / 2 - r - > p_len ; i + + ) {
x + = pAC - > vpd . vpd_buf [ i ] ;
}
if ( x ! = 0 ) {
/* checksum error */
SK_DBG_MSG ( pAC , SK_DBGMOD_VPD , SK_DBGCAT_ERR | SK_DBGCAT_FATAL ,
( " VPD Checksum Error \n " ) ) ;
return ( 1 ) ;
}
/* find and check the end tag of the RW area */
if ( ! ( r = vpd_find_para ( pAC , VPD_RW , & rp ) ) ) {
SK_DBG_MSG ( pAC , SK_DBGMOD_VPD , SK_DBGCAT_ERR | SK_DBGCAT_FATAL ,
( " Encoding Error: RV Tag not found \n " ) ) ;
return ( 1 ) ;
}
if ( r - > p_val < pAC - > vpd . vpd_buf + vpd_size / 2 ) {
SK_DBG_MSG ( pAC , SK_DBGMOD_VPD , SK_DBGCAT_ERR | SK_DBGCAT_FATAL ,
( " Encoding Error: Invalid VPD struct size \n " ) ) ;
return ( 1 ) ;
}
pAC - > vpd . v . vpd_free_rw = r - > p_len ;
/* everything seems to be ok */
if ( pAC - > GIni . GIChipId ! = 0 ) {
pAC - > vpd . v . vpd_status | = VPD_VALID ;
}
SK_DBG_MSG ( pAC , SK_DBGMOD_VPD , SK_DBGCAT_INIT ,
( " done. Free RO = %d, Free RW = %d \n " ,
pAC - > vpd . v . vpd_free_ro , pAC - > vpd . v . vpd_free_rw ) ) ;
return ( 0 ) ;
}
/*
* find the Keyword ' key ' in the VPD buffer and fills the
* parameter struct ' p ' with it ' s values
*
* returns * p success
* 0 : parameter was not found or VPD encoding error
*/
static SK_VPD_PARA * vpd_find_para (
SK_AC * pAC , /* common data base */
const char * key , /* keyword to find (e.g. "MN") */
SK_VPD_PARA * p ) /* parameter description struct */
{
char * v ; /* points to VPD buffer */
int max ; /* Maximum Number of Iterations */
v = pAC - > vpd . vpd_buf ;
max = 128 ;
SK_DBG_MSG ( pAC , SK_DBGMOD_VPD , SK_DBGCAT_CTRL ,
( " VPD find para %s .. " , key ) ) ;
/* check mandatory resource type ID string (Product Name) */
if ( * v ! = ( char ) RES_ID ) {
SK_DBG_MSG ( pAC , SK_DBGMOD_VPD , SK_DBGCAT_ERR | SK_DBGCAT_FATAL ,
( " Error: 0x%x missing \n " , RES_ID ) ) ;
return NULL ;
}
if ( strcmp ( key , VPD_NAME ) = = 0 ) {
p - > p_len = VPD_GET_RES_LEN ( v ) ;
p - > p_val = VPD_GET_VAL ( v ) ;
SK_DBG_MSG ( pAC , SK_DBGMOD_VPD , SK_DBGCAT_CTRL ,
( " found, len = %d \n " , p - > p_len ) ) ;
return ( p ) ;
}
v + = 3 + VPD_GET_RES_LEN ( v ) + 3 ;
for ( ; ; ) {
if ( SK_MEMCMP ( key , v , 2 ) = = 0 ) {
p - > p_len = VPD_GET_VPD_LEN ( v ) ;
p - > p_val = VPD_GET_VAL ( v ) ;
SK_DBG_MSG ( pAC , SK_DBGMOD_VPD , SK_DBGCAT_CTRL ,
( " found, len = %d \n " , p - > p_len ) ) ;
return ( p ) ;
}
/* exit when reaching the "RW" Tag or the maximum of itera. */
max - - ;
if ( SK_MEMCMP ( VPD_RW , v , 2 ) = = 0 | | max = = 0 ) {
break ;
}
if ( SK_MEMCMP ( VPD_RV , v , 2 ) = = 0 ) {
v + = 3 + VPD_GET_VPD_LEN ( v ) + 3 ; /* skip VPD-W */
}
else {
v + = 3 + VPD_GET_VPD_LEN ( v ) ;
}
SK_DBG_MSG ( pAC , SK_DBGMOD_VPD , SK_DBGCAT_CTRL ,
( " scanning '%c%c' len = %d \n " , v [ 0 ] , v [ 1 ] , v [ 2 ] ) ) ;
}
# ifdef DEBUG
SK_DBG_MSG ( pAC , SK_DBGMOD_VPD , SK_DBGCAT_CTRL , ( " not found \n " ) ) ;
if ( max = = 0 ) {
SK_DBG_MSG ( pAC , SK_DBGMOD_VPD , SK_DBGCAT_ERR | SK_DBGCAT_FATAL ,
( " Key/Len Encoding error \n " ) ) ;
}
# endif /* DEBUG */
return NULL ;
}
/*
* Move ' n ' bytes . Begin with the last byte if ' n ' is > 0 ,
* Start with the last byte if n is < 0.
*
* returns nothing
*/
static void vpd_move_para (
char * start , /* start of memory block */
char * end , /* end of memory block to move */
int n ) /* number of bytes the memory block has to be moved */
{
char * p ;
int i ; /* number of byte copied */
if ( n = = 0 )
return ;
i = ( int ) ( end - start + 1 ) ;
if ( n < 0 ) {
p = start + n ;
while ( i ! = 0 ) {
* p + + = * start + + ;
i - - ;
}
}
else {
p = end + n ;
while ( i ! = 0 ) {
* p - - = * end - - ;
i - - ;
}
}
}
/*
* setup the VPD keyword ' key ' at ' ip ' .
*
* returns nothing
*/
static void vpd_insert_key (
const char * key , /* keyword to insert */
const char * buf , /* buffer with the keyword value */
int len , /* length of the value string */
char * ip ) /* inseration point */
{
SK_VPD_KEY * p ;
p = ( SK_VPD_KEY * ) ip ;
p - > p_key [ 0 ] = key [ 0 ] ;
p - > p_key [ 1 ] = key [ 1 ] ;
p - > p_len = ( unsigned char ) len ;
SK_MEMCPY ( & p - > p_val , buf , len ) ;
}
/*
* Setup the VPD end tag " RV " / " RW " .
* Also correct the remaining space variables vpd_free_ro / vpd_free_rw .
*
* returns 0 : success
* 1 : encoding error
*/
static int vpd_mod_endtag (
SK_AC * pAC , /* common data base */
char * etp ) /* end pointer input position */
{
SK_VPD_KEY * p ;
unsigned char x ;
int i ;
int vpd_size ;
SK_DBG_MSG ( pAC , SK_DBGMOD_VPD , SK_DBGCAT_CTRL ,
( " VPD modify endtag at 0x%x = '%c%c' \n " , etp , etp [ 0 ] , etp [ 1 ] ) ) ;
vpd_size = pAC - > vpd . vpd_size ;
p = ( SK_VPD_KEY * ) etp ;
if ( p - > p_key [ 0 ] ! = ' R ' | | ( p - > p_key [ 1 ] ! = ' V ' & & p - > p_key [ 1 ] ! = ' W ' ) ) {
/* something wrong here, encoding error */
SK_DBG_MSG ( pAC , SK_DBGMOD_VPD , SK_DBGCAT_ERR | SK_DBGCAT_FATAL ,
( " Encoding Error: invalid end tag \n " ) ) ;
return ( 1 ) ;
}
if ( etp > pAC - > vpd . vpd_buf + vpd_size / 2 ) {
/* create "RW" tag */
p - > p_len = ( unsigned char ) ( pAC - > vpd . vpd_buf + vpd_size - etp - 3 - 1 ) ;
pAC - > vpd . v . vpd_free_rw = ( int ) p - > p_len ;
i = pAC - > vpd . v . vpd_free_rw ;
etp + = 3 ;
}
else {
/* create "RV" tag */
p - > p_len = ( unsigned char ) ( pAC - > vpd . vpd_buf + vpd_size / 2 - etp - 3 ) ;
pAC - > vpd . v . vpd_free_ro = ( int ) p - > p_len - 1 ;
/* setup checksum */
for ( i = 0 , x = 0 ; i < vpd_size / 2 - p - > p_len ; i + + ) {
x + = pAC - > vpd . vpd_buf [ i ] ;
}
p - > p_val = ( char ) 0 - x ;
i = pAC - > vpd . v . vpd_free_ro ;
etp + = 4 ;
}
while ( i ) {
* etp + + = 0x00 ;
i - - ;
}
return ( 0 ) ;
}
/*
* Insert a VPD keyword into the VPD buffer .
*
* The keyword ' key ' is inserted at the position ' ip ' in the
* VPD buffer .
* The keywords behind the input position will
* be moved . The VPD end tag " RV " or " RW " is generated again .
*
* returns 0 : success
* 2 : value string was cut
* 4 : VPD full , keyword was not written
* 6 : fatal VPD error
*
*/
2006-01-09 18:34:08 -08:00
static int VpdSetupPara (
2005-04-16 15:20:36 -07:00
SK_AC * pAC , /* common data base */
const char * key , /* keyword to insert */
const char * buf , /* buffer with the keyword value */
int len , /* length of the keyword value */
int type , /* VPD_RO_KEY or VPD_RW_KEY */
int op ) /* operation to do: ADD_KEY or OWR_KEY */
{
SK_VPD_PARA vp ;
char * etp ; /* end tag position */
int free ; /* remaining space in selected area */
char * ip ; /* input position inside the VPD buffer */
int rtv ; /* return code */
int head ; /* additional haeder bytes to move */
int found ; /* additinoal bytes if the keyword was found */
int vpd_size ;
SK_DBG_MSG ( pAC , SK_DBGMOD_VPD , SK_DBGCAT_CTRL ,
( " VPD setup para key = %s, val = %s \n " , key , buf ) ) ;
vpd_size = pAC - > vpd . vpd_size ;
rtv = 0 ;
ip = NULL ;
if ( type = = VPD_RW_KEY ) {
/* end tag is "RW" */
free = pAC - > vpd . v . vpd_free_rw ;
etp = pAC - > vpd . vpd_buf + ( vpd_size - free - 1 - 3 ) ;
}
else {
/* end tag is "RV" */
free = pAC - > vpd . v . vpd_free_ro ;
etp = pAC - > vpd . vpd_buf + ( vpd_size / 2 - free - 4 ) ;
}
SK_DBG_MSG ( pAC , SK_DBGMOD_VPD , SK_DBGCAT_CTRL ,
( " Free RO = %d, Free RW = %d \n " ,
pAC - > vpd . v . vpd_free_ro , pAC - > vpd . v . vpd_free_rw ) ) ;
head = 0 ;
found = 0 ;
if ( op = = OWR_KEY ) {
if ( vpd_find_para ( pAC , key , & vp ) ) {
found = 3 ;
ip = vp . p_val - 3 ;
free + = vp . p_len + 3 ;
SK_DBG_MSG ( pAC , SK_DBGMOD_VPD , SK_DBGCAT_CTRL ,
( " Overwrite Key \n " ) ) ;
}
else {
op = ADD_KEY ;
SK_DBG_MSG ( pAC , SK_DBGMOD_VPD , SK_DBGCAT_CTRL ,
( " Add Key \n " ) ) ;
}
}
if ( op = = ADD_KEY ) {
ip = etp ;
vp . p_len = 0 ;
head = 3 ;
}
if ( len + 3 > free ) {
if ( free < 7 ) {
SK_DBG_MSG ( pAC , SK_DBGMOD_VPD , SK_DBGCAT_ERR ,
( " VPD Buffer Overflow, keyword not written \n " ) ) ;
return ( 4 ) ;
}
/* cut it again */
len = free - 3 ;
rtv = 2 ;
SK_DBG_MSG ( pAC , SK_DBGMOD_VPD , SK_DBGCAT_ERR ,
( " VPD Buffer Full, Keyword was cut \n " ) ) ;
}
vpd_move_para ( ip + vp . p_len + found , etp + 2 , len - vp . p_len + head ) ;
vpd_insert_key ( key , buf , len , ip ) ;
if ( vpd_mod_endtag ( pAC , etp + len - vp . p_len + head ) ) {
pAC - > vpd . v . vpd_status & = ~ VPD_VALID ;
SK_DBG_MSG ( pAC , SK_DBGMOD_VPD , SK_DBGCAT_ERR ,
( " VPD Encoding Error \n " ) ) ;
return ( 6 ) ;
}
return ( rtv ) ;
}
/*
* Read the contents of the VPD EEPROM and copy it to the
* VPD buffer if not already done .
*
* return : A pointer to the vpd_status structure . The structure contains
* this fields .
*/
SK_VPD_STATUS * VpdStat (
SK_AC * pAC , /* Adapters context */
SK_IOC IoC ) /* IO Context */
{
if ( ( pAC - > vpd . v . vpd_status & VPD_VALID ) = = 0 ) {
( void ) VpdInit ( pAC , IoC ) ;
}
return ( & pAC - > vpd . v ) ;
}
/*
* Read the contents of the VPD EEPROM and copy it to the VPD
* buffer if not already done .
* Scan the VPD buffer for VPD keywords and create the VPD
* keyword list by copying the keywords to ' buf ' , all after
* each other and terminated with a ' \0 ' .
*
* Exceptions : o The Resource Type ID String ( product name ) is called " Name "
* o The VPD end tags ' RV ' and ' RW ' are not listed
*
* The number of copied keywords is counted in ' elements ' .
*
* returns 0 : success
* 2 : buffer overfull , one or more keywords are missing
* 6 : fatal VPD error
*
* example values after returning :
*
* buf = " Name \0 PN \0 EC \0 MN \0 SN \0 CP \0 VF \0 VL \0 YA \0 "
* * len = 30
* * elements = 9
*/
int VpdKeys (
SK_AC * pAC , /* common data base */
SK_IOC IoC , /* IO Context */
char * buf , /* buffer where to copy the keywords */
int * len , /* buffer length */
int * elements ) /* number of keywords returned */
{
char * v ;
int n ;
SK_DBG_MSG ( pAC , SK_DBGMOD_VPD , SK_DBGCAT_RX , ( " list VPD keys .. " ) ) ;
* elements = 0 ;
if ( ( pAC - > vpd . v . vpd_status & VPD_VALID ) = = 0 ) {
if ( VpdInit ( pAC , IoC ) ! = 0 ) {
* len = 0 ;
SK_DBG_MSG ( pAC , SK_DBGMOD_VPD , SK_DBGCAT_ERR ,
( " VPD Init Error, terminated \n " ) ) ;
return ( 6 ) ;
}
}
if ( ( signed ) strlen ( VPD_NAME ) + 1 < = * len ) {
v = pAC - > vpd . vpd_buf ;
strcpy ( buf , VPD_NAME ) ;
n = strlen ( VPD_NAME ) + 1 ;
buf + = n ;
* elements = 1 ;
SK_DBG_MSG ( pAC , SK_DBGMOD_VPD , SK_DBGCAT_RX ,
( " '%c%c' " , v [ 0 ] , v [ 1 ] ) ) ;
}
else {
* len = 0 ;
SK_DBG_MSG ( pAC , SK_DBGMOD_VPD , SK_DBGCAT_ERR ,
( " buffer overflow \n " ) ) ;
return ( 2 ) ;
}
v + = 3 + VPD_GET_RES_LEN ( v ) + 3 ;
for ( ; ; ) {
/* exit when reaching the "RW" Tag */
if ( SK_MEMCMP ( VPD_RW , v , 2 ) = = 0 ) {
break ;
}
if ( SK_MEMCMP ( VPD_RV , v , 2 ) = = 0 ) {
v + = 3 + VPD_GET_VPD_LEN ( v ) + 3 ; /* skip VPD-W */
continue ;
}
if ( n + 3 < = * len ) {
SK_MEMCPY ( buf , v , 2 ) ;
buf + = 2 ;
* buf + + = ' \0 ' ;
n + = 3 ;
v + = 3 + VPD_GET_VPD_LEN ( v ) ;
* elements + = 1 ;
SK_DBG_MSG ( pAC , SK_DBGMOD_VPD , SK_DBGCAT_RX ,
( " '%c%c' " , v [ 0 ] , v [ 1 ] ) ) ;
}
else {
* len = n ;
SK_DBG_MSG ( pAC , SK_DBGMOD_VPD , SK_DBGCAT_ERR ,
( " buffer overflow \n " ) ) ;
return ( 2 ) ;
}
}
SK_DBG_MSG ( pAC , SK_DBGMOD_VPD , SK_DBGCAT_RX , ( " \n " ) ) ;
* len = n ;
return ( 0 ) ;
}
/*
* Read the contents of the VPD EEPROM and copy it to the
* VPD buffer if not already done . Search for the VPD keyword
* ' key ' and copy its value to ' buf ' . Add a terminating ' \0 ' .
* If the value does not fit into the buffer cut it after
* ' len ' - 1 bytes .
*
* returns 0 : success
* 1 : keyword not found
* 2 : value string was cut
* 3 : VPD transfer timeout
* 6 : fatal VPD error
*/
int VpdRead (
SK_AC * pAC , /* common data base */
SK_IOC IoC , /* IO Context */
const char * key , /* keyword to read (e.g. "MN") */
char * buf , /* buffer where to copy the keyword value */
int * len ) /* buffer length */
{
SK_VPD_PARA * p , vp ;
SK_DBG_MSG ( pAC , SK_DBGMOD_VPD , SK_DBGCAT_RX , ( " VPD read %s .. " , key ) ) ;
if ( ( pAC - > vpd . v . vpd_status & VPD_VALID ) = = 0 ) {
if ( VpdInit ( pAC , IoC ) ! = 0 ) {
* len = 0 ;
SK_DBG_MSG ( pAC , SK_DBGMOD_VPD , SK_DBGCAT_ERR ,
( " VPD init error \n " ) ) ;
return ( 6 ) ;
}
}
if ( ( p = vpd_find_para ( pAC , key , & vp ) ) ! = NULL ) {
if ( p - > p_len > ( * ( unsigned * ) len ) - 1 ) {
p - > p_len = * len - 1 ;
}
SK_MEMCPY ( buf , p - > p_val , p - > p_len ) ;
buf [ p - > p_len ] = ' \0 ' ;
* len = p - > p_len ;
SK_DBG_MSG ( pAC , SK_DBGMOD_VPD , SK_DBGCAT_RX ,
( " %c%c%c%c.., len = %d \n " ,
buf [ 0 ] , buf [ 1 ] , buf [ 2 ] , buf [ 3 ] , * len ) ) ;
}
else {
* len = 0 ;
SK_DBG_MSG ( pAC , SK_DBGMOD_VPD , SK_DBGCAT_ERR , ( " not found \n " ) ) ;
return ( 1 ) ;
}
return ( 0 ) ;
}
/*
* Check whether a given key may be written
*
* returns
* SK_TRUE Yes it may be written
* SK_FALSE No it may be written
*/
SK_BOOL VpdMayWrite (
char * key ) /* keyword to write (allowed values "Yx", "Vx") */
{
if ( ( * key ! = ' Y ' & & * key ! = ' V ' ) | |
key [ 1 ] < ' 0 ' | | key [ 1 ] > ' Z ' | |
( key [ 1 ] > ' 9 ' & & key [ 1 ] < ' A ' ) | | strlen ( key ) ! = 2 ) {
return ( SK_FALSE ) ;
}
return ( SK_TRUE ) ;
}
/*
* Read the contents of the VPD EEPROM and copy it to the VPD
* buffer if not already done . Insert / overwrite the keyword ' key '
* in the VPD buffer . Cut the keyword value if it does not fit
* into the VPD read / write area .
*
* returns 0 : success
* 2 : value string was cut
* 3 : VPD transfer timeout
* 4 : VPD full , keyword was not written
* 5 : keyword cannot be written
* 6 : fatal VPD error
*/
int VpdWrite (
SK_AC * pAC , /* common data base */
SK_IOC IoC , /* IO Context */
const char * key , /* keyword to write (allowed values "Yx", "Vx") */
const char * buf ) /* buffer where the keyword value can be read from */
{
int len ; /* length of the keyword to write */
int rtv ; /* return code */
int rtv2 ;
SK_DBG_MSG ( pAC , SK_DBGMOD_VPD , SK_DBGCAT_TX ,
( " VPD write %s = %s \n " , key , buf ) ) ;
if ( ( * key ! = ' Y ' & & * key ! = ' V ' ) | |
key [ 1 ] < ' 0 ' | | key [ 1 ] > ' Z ' | |
( key [ 1 ] > ' 9 ' & & key [ 1 ] < ' A ' ) | | strlen ( key ) ! = 2 ) {
SK_DBG_MSG ( pAC , SK_DBGMOD_VPD , SK_DBGCAT_ERR ,
( " illegal key tag, keyword not written \n " ) ) ;
return ( 5 ) ;
}
if ( ( pAC - > vpd . v . vpd_status & VPD_VALID ) = = 0 ) {
if ( VpdInit ( pAC , IoC ) ! = 0 ) {
SK_DBG_MSG ( pAC , SK_DBGMOD_VPD , SK_DBGCAT_ERR ,
( " VPD init error \n " ) ) ;
return ( 6 ) ;
}
}
rtv = 0 ;
len = strlen ( buf ) ;
if ( len > VPD_MAX_LEN ) {
/* cut it */
len = VPD_MAX_LEN ;
rtv = 2 ;
SK_DBG_MSG ( pAC , SK_DBGMOD_VPD , SK_DBGCAT_ERR ,
( " keyword too long, cut after %d bytes \n " , VPD_MAX_LEN ) ) ;
}
if ( ( rtv2 = VpdSetupPara ( pAC , key , buf , len , VPD_RW_KEY , OWR_KEY ) ) ! = 0 ) {
SK_DBG_MSG ( pAC , SK_DBGMOD_VPD , SK_DBGCAT_ERR ,
( " VPD write error \n " ) ) ;
return ( rtv2 ) ;
}
return ( rtv ) ;
}
/*
* Read the contents of the VPD EEPROM and copy it to the
* VPD buffer if not already done . Remove the VPD keyword
* ' key ' from the VPD buffer .
* Only the keywords in the read / write area can be deleted .
* Keywords in the read only area cannot be deleted .
*
* returns 0 : success , keyword was removed
* 1 : keyword not found
* 5 : keyword cannot be deleted
* 6 : fatal VPD error
*/
int VpdDelete (
SK_AC * pAC , /* common data base */
SK_IOC IoC , /* IO Context */
char * key ) /* keyword to read (e.g. "MN") */
{
SK_VPD_PARA * p , vp ;
char * etp ;
int vpd_size ;
vpd_size = pAC - > vpd . vpd_size ;
SK_DBG_MSG ( pAC , SK_DBGMOD_VPD , SK_DBGCAT_TX , ( " VPD delete key %s \n " , key ) ) ;
if ( ( pAC - > vpd . v . vpd_status & VPD_VALID ) = = 0 ) {
if ( VpdInit ( pAC , IoC ) ! = 0 ) {
SK_DBG_MSG ( pAC , SK_DBGMOD_VPD , SK_DBGCAT_ERR ,
( " VPD init error \n " ) ) ;
return ( 6 ) ;
}
}
if ( ( p = vpd_find_para ( pAC , key , & vp ) ) ! = NULL ) {
if ( p - > p_val < pAC - > vpd . vpd_buf + vpd_size / 2 ) {
/* try to delete read only keyword */
SK_DBG_MSG ( pAC , SK_DBGMOD_VPD , SK_DBGCAT_ERR ,
( " cannot delete RO keyword \n " ) ) ;
return ( 5 ) ;
}
etp = pAC - > vpd . vpd_buf + ( vpd_size - pAC - > vpd . v . vpd_free_rw - 1 - 3 ) ;
vpd_move_para ( vp . p_val + vp . p_len , etp + 2 ,
- ( ( int ) ( vp . p_len + 3 ) ) ) ;
if ( vpd_mod_endtag ( pAC , etp - vp . p_len - 3 ) ) {
pAC - > vpd . v . vpd_status & = ~ VPD_VALID ;
SK_DBG_MSG ( pAC , SK_DBGMOD_VPD , SK_DBGCAT_ERR ,
( " VPD encoding error \n " ) ) ;
return ( 6 ) ;
}
}
else {
SK_DBG_MSG ( pAC , SK_DBGMOD_VPD , SK_DBGCAT_ERR ,
( " keyword not found \n " ) ) ;
return ( 1 ) ;
}
return ( 0 ) ;
}
/*
* If the VPD buffer contains valid data write the VPD
* read / write area back to the VPD EEPROM .
*
* returns 0 : success
* 3 : VPD transfer timeout
*/
int VpdUpdate (
SK_AC * pAC , /* Adapters context */
SK_IOC IoC ) /* IO Context */
{
int vpd_size ;
vpd_size = pAC - > vpd . vpd_size ;
SK_DBG_MSG ( pAC , SK_DBGMOD_VPD , SK_DBGCAT_TX , ( " VPD update .. " ) ) ;
if ( ( pAC - > vpd . v . vpd_status & VPD_VALID ) ! = 0 ) {
if ( VpdTransferBlock ( pAC , IoC , pAC - > vpd . vpd_buf + vpd_size / 2 ,
vpd_size / 2 , vpd_size / 2 , VPD_WRITE ) ! = vpd_size / 2 ) {
SK_DBG_MSG ( pAC , SK_DBGMOD_VPD , SK_DBGCAT_ERR ,
( " transfer timed out \n " ) ) ;
return ( 3 ) ;
}
}
SK_DBG_MSG ( pAC , SK_DBGMOD_VPD , SK_DBGCAT_TX , ( " done \n " ) ) ;
return ( 0 ) ;
}