2012-07-19 09:42:40 -05:00
/*
* Driver for IBM Power 842 compression accelerator
*
* 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 .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 , USA .
*
* Copyright ( C ) IBM Corporation , 2012
*
* Authors : Robert Jennings < rcj @ linux . vnet . ibm . com >
* Seth Jennings < sjenning @ linux . vnet . ibm . com >
*/
2012-08-03 12:23:15 +10:00
# include <linux/kernel.h>
2012-07-19 09:42:40 -05:00
# include <linux/module.h>
# include <linux/nx842.h>
2012-08-03 12:23:15 +10:00
# include <linux/of.h>
# include <linux/slab.h>
# include <asm/page.h>
# include <asm/vio.h>
2012-07-19 09:42:40 -05:00
# include "nx_csbcpb.h" /* struct nx_csbcpb */
# define MODULE_NAME "nx-compress"
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " Robert Jennings <rcj@linux.vnet.ibm.com> " ) ;
MODULE_DESCRIPTION ( " 842 H/W Compression driver for IBM Power processors " ) ;
# define SHIFT_4K 12
# define SHIFT_64K 16
# define SIZE_4K (1UL << SHIFT_4K)
# define SIZE_64K (1UL << SHIFT_64K)
/* IO buffer must be 128 byte aligned */
# define IO_BUFFER_ALIGN 128
struct nx842_header {
int blocks_nr ; /* number of compressed blocks */
int offset ; /* offset of the first block (from beginning of header) */
int sizes [ 0 ] ; /* size of compressed blocks */
} ;
static inline int nx842_header_size ( const struct nx842_header * hdr )
{
return sizeof ( struct nx842_header ) +
hdr - > blocks_nr * sizeof ( hdr - > sizes [ 0 ] ) ;
}
/* Macros for fields within nx_csbcpb */
/* Check the valid bit within the csbcpb valid field */
# define NX842_CSBCBP_VALID_CHK(x) (x & BIT_MASK(7))
/* CE macros operate on the completion_extension field bits in the csbcpb.
* CE0 0 = full completion , 1 = partial completion
* CE1 0 = CE0 indicates completion , 1 = termination ( output may be modified )
* CE2 0 = processed_bytes is source bytes , 1 = processed_bytes is target bytes */
# define NX842_CSBCPB_CE0(x) (x & BIT_MASK(7))
# define NX842_CSBCPB_CE1(x) (x & BIT_MASK(6))
# define NX842_CSBCPB_CE2(x) (x & BIT_MASK(5))
/* The NX unit accepts data only on 4K page boundaries */
# define NX842_HW_PAGE_SHIFT SHIFT_4K
# define NX842_HW_PAGE_SIZE (ASM_CONST(1) << NX842_HW_PAGE_SHIFT)
# define NX842_HW_PAGE_MASK (~(NX842_HW_PAGE_SIZE-1))
enum nx842_status {
UNAVAILABLE ,
AVAILABLE
} ;
struct ibm_nx842_counters {
atomic64_t comp_complete ;
atomic64_t comp_failed ;
atomic64_t decomp_complete ;
atomic64_t decomp_failed ;
atomic64_t swdecomp ;
atomic64_t comp_times [ 32 ] ;
atomic64_t decomp_times [ 32 ] ;
} ;
static struct nx842_devdata {
struct vio_dev * vdev ;
struct device * dev ;
struct ibm_nx842_counters * counters ;
unsigned int max_sg_len ;
unsigned int max_sync_size ;
unsigned int max_sync_sg ;
enum nx842_status status ;
} __rcu * devdata ;
static DEFINE_SPINLOCK ( devdata_mutex ) ;
# define NX842_COUNTER_INC(_x) \
static inline void nx842_inc_ # # _x ( \
const struct nx842_devdata * dev ) { \
if ( dev ) \
atomic64_inc ( & dev - > counters - > _x ) ; \
}
NX842_COUNTER_INC ( comp_complete ) ;
NX842_COUNTER_INC ( comp_failed ) ;
NX842_COUNTER_INC ( decomp_complete ) ;
NX842_COUNTER_INC ( decomp_failed ) ;
NX842_COUNTER_INC ( swdecomp ) ;
# define NX842_HIST_SLOTS 16
static void ibm_nx842_incr_hist ( atomic64_t * times , unsigned int time )
{
int bucket = fls ( time ) ;
if ( bucket )
bucket = min ( ( NX842_HIST_SLOTS - 1 ) , bucket - 1 ) ;
atomic64_inc ( & times [ bucket ] ) ;
}
/* NX unit operation flags */
# define NX842_OP_COMPRESS 0x0
# define NX842_OP_CRC 0x1
# define NX842_OP_DECOMPRESS 0x2
# define NX842_OP_COMPRESS_CRC (NX842_OP_COMPRESS | NX842_OP_CRC)
# define NX842_OP_DECOMPRESS_CRC (NX842_OP_DECOMPRESS | NX842_OP_CRC)
# define NX842_OP_ASYNC (1<<23)
# define NX842_OP_NOTIFY (1<<22)
# define NX842_OP_NOTIFY_INT(x) ((x & 0xff)<<8)
static unsigned long nx842_get_desired_dma ( struct vio_dev * viodev )
{
/* No use of DMA mappings within the driver. */
return 0 ;
}
struct nx842_slentry {
2012-08-03 12:23:15 +10:00
unsigned long ptr ; /* Real address (use __pa()) */
2012-07-19 09:42:40 -05:00
unsigned long len ;
} ;
/* pHyp scatterlist entry */
struct nx842_scatterlist {
int entry_nr ; /* number of slentries */
struct nx842_slentry * entries ; /* ptr to array of slentries */
} ;
/* Does not include sizeof(entry_nr) in the size */
static inline unsigned long nx842_get_scatterlist_size (
struct nx842_scatterlist * sl )
{
return sl - > entry_nr * sizeof ( struct nx842_slentry ) ;
}
2014-01-29 10:34:56 -06:00
static inline unsigned long nx842_get_pa ( void * addr )
{
if ( is_vmalloc_addr ( addr ) )
return page_to_phys ( vmalloc_to_page ( addr ) )
+ offset_in_page ( addr ) ;
else
return __pa ( addr ) ;
}
2012-07-19 09:42:40 -05:00
static int nx842_build_scatterlist ( unsigned long buf , int len ,
struct nx842_scatterlist * sl )
{
unsigned long nextpage ;
struct nx842_slentry * entry ;
sl - > entry_nr = 0 ;
entry = sl - > entries ;
while ( len ) {
2014-01-29 10:34:56 -06:00
entry - > ptr = nx842_get_pa ( ( void * ) buf ) ;
2012-07-19 09:42:40 -05:00
nextpage = ALIGN ( buf + 1 , NX842_HW_PAGE_SIZE ) ;
if ( nextpage < buf + len ) {
/* we aren't at the end yet */
if ( IS_ALIGNED ( buf , NX842_HW_PAGE_SIZE ) )
/* we are in the middle (or beginning) */
entry - > len = NX842_HW_PAGE_SIZE ;
else
/* we are at the beginning */
entry - > len = nextpage - buf ;
} else {
/* at the end */
entry - > len = len ;
}
len - = entry - > len ;
buf + = entry - > len ;
sl - > entry_nr + + ;
entry + + ;
}
return 0 ;
}
/*
* Working memory for software decompression
*/
struct sw842_fifo {
union {
char f8 [ 256 ] [ 8 ] ;
char f4 [ 512 ] [ 4 ] ;
} ;
char f2 [ 256 ] [ 2 ] ;
unsigned char f84_full ;
unsigned char f2_full ;
unsigned char f8_count ;
unsigned char f2_count ;
unsigned int f4_count ;
} ;
/*
* Working memory for crypto API
*/
struct nx842_workmem {
char bounce [ PAGE_SIZE ] ; /* bounce buffer for decompression input */
union {
/* hardware working memory */
struct {
/* scatterlist */
char slin [ SIZE_4K ] ;
char slout [ SIZE_4K ] ;
/* coprocessor status/parameter block */
struct nx_csbcpb csbcpb ;
} ;
/* software working memory */
struct sw842_fifo swfifo ; /* software decompression fifo */
} ;
} ;
int nx842_get_workmem_size ( void )
{
return sizeof ( struct nx842_workmem ) + NX842_HW_PAGE_SIZE ;
}
EXPORT_SYMBOL_GPL ( nx842_get_workmem_size ) ;
int nx842_get_workmem_size_aligned ( void )
{
return sizeof ( struct nx842_workmem ) ;
}
EXPORT_SYMBOL_GPL ( nx842_get_workmem_size_aligned ) ;
static int nx842_validate_result ( struct device * dev ,
struct cop_status_block * csb )
{
/* The csb must be valid after returning from vio_h_cop_sync */
if ( ! NX842_CSBCBP_VALID_CHK ( csb - > valid ) ) {
dev_err ( dev , " %s: cspcbp not valid upon completion. \n " ,
__func__ ) ;
dev_dbg ( dev , " valid:0x%02x cs:0x%02x cc:0x%02x ce:0x%02x \n " ,
csb - > valid ,
csb - > crb_seq_number ,
csb - > completion_code ,
csb - > completion_extension ) ;
dev_dbg ( dev , " processed_bytes:%d address:0x%016lx \n " ,
csb - > processed_byte_count ,
( unsigned long ) csb - > address ) ;
return - EIO ;
}
/* Check return values from the hardware in the CSB */
switch ( csb - > completion_code ) {
case 0 : /* Completed without error */
break ;
case 64 : /* Target bytes > Source bytes during compression */
case 13 : /* Output buffer too small */
dev_dbg ( dev , " %s: Compression output larger than input \n " ,
__func__ ) ;
return - ENOSPC ;
case 66 : /* Input data contains an illegal template field */
case 67 : /* Template indicates data past the end of the input stream */
dev_dbg ( dev , " %s: Bad data for decompression (code:%d) \n " ,
__func__ , csb - > completion_code ) ;
return - EINVAL ;
default :
dev_dbg ( dev , " %s: Unspecified error (code:%d) \n " ,
__func__ , csb - > completion_code ) ;
return - EIO ;
}
/* Hardware sanity check */
if ( ! NX842_CSBCPB_CE2 ( csb - > completion_extension ) ) {
dev_err ( dev , " %s: No error returned by hardware, but "
" data returned is unusable, contact support. \n "
" (Additional info: csbcbp->processed bytes "
" does not specify processed bytes for the "
" target buffer.) \n " , __func__ ) ;
return - EIO ;
}
return 0 ;
}
/**
* nx842_compress - Compress data using the 842 algorithm
*
* Compression provide by the NX842 coprocessor on IBM Power systems .
* The input buffer is compressed and the result is stored in the
* provided output buffer .
*
* Upon return from this function @ outlen contains the length of the
* compressed data . If there is an error then @ outlen will be 0 and an
* error will be specified by the return code from this function .
*
* @ in : Pointer to input buffer , must be page aligned
* @ inlen : Length of input buffer , must be PAGE_SIZE
* @ out : Pointer to output buffer
* @ outlen : Length of output buffer
* @ wrkmem : ptr to buffer for working memory , size determined by
* nx842_get_workmem_size ( )
*
* Returns :
* 0 Success , output of length @ outlen stored in the buffer at @ out
* - ENOMEM Unable to allocate internal buffers
* - ENOSPC Output buffer is to small
* - EMSGSIZE XXX Difficult to describe this limitation
* - EIO Internal error
* - ENODEV Hardware unavailable
*/
int nx842_compress ( const unsigned char * in , unsigned int inlen ,
unsigned char * out , unsigned int * outlen , void * wmem )
{
struct nx842_header * hdr ;
struct nx842_devdata * local_devdata ;
struct device * dev = NULL ;
struct nx842_workmem * workmem ;
struct nx842_scatterlist slin , slout ;
struct nx_csbcpb * csbcpb ;
int ret = 0 , max_sync_size , i , bytesleft , size , hdrsize ;
unsigned long inbuf , outbuf , padding ;
struct vio_pfo_op op = {
. done = NULL ,
. handle = 0 ,
. timeout = 0 ,
} ;
unsigned long start_time = get_tb ( ) ;
/*
* Make sure input buffer is 64 k page aligned . This is assumed since
* this driver is designed for page compression only ( for now ) . This
* is very nice since we can now use direct DDE ( s ) for the input and
* the alignment is guaranteed .
*/
inbuf = ( unsigned long ) in ;
if ( ! IS_ALIGNED ( inbuf , PAGE_SIZE ) | | inlen ! = PAGE_SIZE )
return - EINVAL ;
rcu_read_lock ( ) ;
local_devdata = rcu_dereference ( devdata ) ;
if ( ! local_devdata | | ! local_devdata - > dev ) {
rcu_read_unlock ( ) ;
return - ENODEV ;
}
max_sync_size = local_devdata - > max_sync_size ;
dev = local_devdata - > dev ;
/* Create the header */
hdr = ( struct nx842_header * ) out ;
hdr - > blocks_nr = PAGE_SIZE / max_sync_size ;
hdrsize = nx842_header_size ( hdr ) ;
outbuf = ( unsigned long ) out + hdrsize ;
bytesleft = * outlen - hdrsize ;
/* Init scatterlist */
workmem = ( struct nx842_workmem * ) ALIGN ( ( unsigned long ) wmem ,
NX842_HW_PAGE_SIZE ) ;
slin . entries = ( struct nx842_slentry * ) workmem - > slin ;
slout . entries = ( struct nx842_slentry * ) workmem - > slout ;
/* Init operation */
op . flags = NX842_OP_COMPRESS ;
csbcpb = & workmem - > csbcpb ;
memset ( csbcpb , 0 , sizeof ( * csbcpb ) ) ;
2014-01-29 10:34:56 -06:00
op . csbcpb = nx842_get_pa ( csbcpb ) ;
op . out = nx842_get_pa ( slout . entries ) ;
2012-07-19 09:42:40 -05:00
for ( i = 0 ; i < hdr - > blocks_nr ; i + + ) {
/*
* Aligning the output blocks to 128 bytes does waste space ,
* but it prevents the need for bounce buffers and memory
* copies . It also simplifies the code a lot . In the worst
* case ( 64 k page , 4 k max_sync_size ) , you lose up to
* ( 128 * 16 ) / 64 k = ~ 3 % the compression factor . For 64 k
* max_sync_size , the loss would be at most 128 / 64 k = ~ 0.2 % .
*/
padding = ALIGN ( outbuf , IO_BUFFER_ALIGN ) - outbuf ;
outbuf + = padding ;
bytesleft - = padding ;
if ( i = = 0 )
/* save offset into first block in header */
hdr - > offset = padding + hdrsize ;
if ( bytesleft < = 0 ) {
ret = - ENOSPC ;
goto unlock ;
}
/*
* NOTE : If the default max_sync_size is changed from 4 k
* to 64 k , remove the " likely " case below , since a
* scatterlist will always be needed .
*/
if ( likely ( max_sync_size = = NX842_HW_PAGE_SIZE ) ) {
/* Create direct DDE */
2014-01-29 10:34:56 -06:00
op . in = nx842_get_pa ( ( void * ) inbuf ) ;
2012-07-19 09:42:40 -05:00
op . inlen = max_sync_size ;
} else {
/* Create indirect DDE (scatterlist) */
nx842_build_scatterlist ( inbuf , max_sync_size , & slin ) ;
2014-01-29 10:34:56 -06:00
op . in = nx842_get_pa ( slin . entries ) ;
2012-07-19 09:42:40 -05:00
op . inlen = - nx842_get_scatterlist_size ( & slin ) ;
}
/*
* If max_sync_size ! = NX842_HW_PAGE_SIZE , an indirect
* DDE is required for the outbuf .
* If max_sync_size = = NX842_HW_PAGE_SIZE , outbuf must
* also be page aligned ( 1 in 128 / 4 k = 32 chance ) in order
* to use a direct DDE .
* This is unlikely , just use an indirect DDE always .
*/
nx842_build_scatterlist ( outbuf ,
min ( bytesleft , max_sync_size ) , & slout ) ;
/* op.out set before loop */
op . outlen = - nx842_get_scatterlist_size ( & slout ) ;
/* Send request to pHyp */
ret = vio_h_cop_sync ( local_devdata - > vdev , & op ) ;
/* Check for pHyp error */
if ( ret ) {
dev_dbg ( dev , " %s: vio_h_cop_sync error (ret=%d, hret=%ld) \n " ,
__func__ , ret , op . hcall_err ) ;
ret = - EIO ;
goto unlock ;
}
/* Check for hardware error */
ret = nx842_validate_result ( dev , & csbcpb - > csb ) ;
if ( ret & & ret ! = - ENOSPC )
goto unlock ;
/* Handle incompressible data */
if ( unlikely ( ret = = - ENOSPC ) ) {
if ( bytesleft < max_sync_size ) {
/*
* Not enough space left in the output buffer
* to store uncompressed block
*/
goto unlock ;
} else {
/* Store incompressible block */
memcpy ( ( void * ) outbuf , ( void * ) inbuf ,
max_sync_size ) ;
hdr - > sizes [ i ] = - max_sync_size ;
outbuf + = max_sync_size ;
bytesleft - = max_sync_size ;
/* Reset ret, incompressible data handled */
ret = 0 ;
}
} else {
/* Normal case, compression was successful */
size = csbcpb - > csb . processed_byte_count ;
dev_dbg ( dev , " %s: processed_bytes=%d \n " ,
__func__ , size ) ;
hdr - > sizes [ i ] = size ;
outbuf + = size ;
bytesleft - = size ;
}
inbuf + = max_sync_size ;
}
* outlen = ( unsigned int ) ( outbuf - ( unsigned long ) out ) ;
unlock :
if ( ret )
nx842_inc_comp_failed ( local_devdata ) ;
else {
nx842_inc_comp_complete ( local_devdata ) ;
ibm_nx842_incr_hist ( local_devdata - > counters - > comp_times ,
( get_tb ( ) - start_time ) / tb_ticks_per_usec ) ;
}
rcu_read_unlock ( ) ;
return ret ;
}
EXPORT_SYMBOL_GPL ( nx842_compress ) ;
static int sw842_decompress ( const unsigned char * , int , unsigned char * , int * ,
const void * ) ;
/**
* nx842_decompress - Decompress data using the 842 algorithm
*
* Decompression provide by the NX842 coprocessor on IBM Power systems .
* The input buffer is decompressed and the result is stored in the
* provided output buffer . The size allocated to the output buffer is
* provided by the caller of this function in @ outlen . Upon return from
* this function @ outlen contains the length of the decompressed data .
* If there is an error then @ outlen will be 0 and an error will be
* specified by the return code from this function .
*
* @ in : Pointer to input buffer , will use bounce buffer if not 128 byte
* aligned
* @ inlen : Length of input buffer
* @ out : Pointer to output buffer , must be page aligned
* @ outlen : Length of output buffer , must be PAGE_SIZE
* @ wrkmem : ptr to buffer for working memory , size determined by
* nx842_get_workmem_size ( )
*
* Returns :
* 0 Success , output of length @ outlen stored in the buffer at @ out
* - ENODEV Hardware decompression device is unavailable
* - ENOMEM Unable to allocate internal buffers
* - ENOSPC Output buffer is to small
* - EINVAL Bad input data encountered when attempting decompress
* - EIO Internal error
*/
int nx842_decompress ( const unsigned char * in , unsigned int inlen ,
unsigned char * out , unsigned int * outlen , void * wmem )
{
struct nx842_header * hdr ;
struct nx842_devdata * local_devdata ;
struct device * dev = NULL ;
struct nx842_workmem * workmem ;
struct nx842_scatterlist slin , slout ;
struct nx_csbcpb * csbcpb ;
int ret = 0 , i , size , max_sync_size ;
unsigned long inbuf , outbuf ;
struct vio_pfo_op op = {
. done = NULL ,
. handle = 0 ,
. timeout = 0 ,
} ;
unsigned long start_time = get_tb ( ) ;
/* Ensure page alignment and size */
outbuf = ( unsigned long ) out ;
if ( ! IS_ALIGNED ( outbuf , PAGE_SIZE ) | | * outlen ! = PAGE_SIZE )
return - EINVAL ;
rcu_read_lock ( ) ;
local_devdata = rcu_dereference ( devdata ) ;
if ( local_devdata )
dev = local_devdata - > dev ;
/* Get header */
hdr = ( struct nx842_header * ) in ;
workmem = ( struct nx842_workmem * ) ALIGN ( ( unsigned long ) wmem ,
NX842_HW_PAGE_SIZE ) ;
inbuf = ( unsigned long ) in + hdr - > offset ;
if ( likely ( ! IS_ALIGNED ( inbuf , IO_BUFFER_ALIGN ) ) ) {
/* Copy block(s) into bounce buffer for alignment */
memcpy ( workmem - > bounce , in + hdr - > offset , inlen - hdr - > offset ) ;
inbuf = ( unsigned long ) workmem - > bounce ;
}
/* Init scatterlist */
slin . entries = ( struct nx842_slentry * ) workmem - > slin ;
slout . entries = ( struct nx842_slentry * ) workmem - > slout ;
/* Init operation */
op . flags = NX842_OP_DECOMPRESS ;
csbcpb = & workmem - > csbcpb ;
memset ( csbcpb , 0 , sizeof ( * csbcpb ) ) ;
2014-01-29 10:34:56 -06:00
op . csbcpb = nx842_get_pa ( csbcpb ) ;
2012-07-19 09:42:40 -05:00
/*
* max_sync_size may have changed since compression ,
* so we can ' t read it from the device info . We need
* to derive it from hdr - > blocks_nr .
*/
max_sync_size = PAGE_SIZE / hdr - > blocks_nr ;
for ( i = 0 ; i < hdr - > blocks_nr ; i + + ) {
/* Skip padding */
inbuf = ALIGN ( inbuf , IO_BUFFER_ALIGN ) ;
if ( hdr - > sizes [ i ] < 0 ) {
/* Negative sizes indicate uncompressed data blocks */
size = abs ( hdr - > sizes [ i ] ) ;
memcpy ( ( void * ) outbuf , ( void * ) inbuf , size ) ;
outbuf + = size ;
inbuf + = size ;
continue ;
}
if ( ! dev )
goto sw ;
/*
* The better the compression , the more likely the " likely "
* case becomes .
*/
if ( likely ( ( inbuf & NX842_HW_PAGE_MASK ) = =
( ( inbuf + hdr - > sizes [ i ] - 1 ) & NX842_HW_PAGE_MASK ) ) ) {
/* Create direct DDE */
2014-01-29 10:34:56 -06:00
op . in = nx842_get_pa ( ( void * ) inbuf ) ;
2012-07-19 09:42:40 -05:00
op . inlen = hdr - > sizes [ i ] ;
} else {
/* Create indirect DDE (scatterlist) */
nx842_build_scatterlist ( inbuf , hdr - > sizes [ i ] , & slin ) ;
2014-01-29 10:34:56 -06:00
op . in = nx842_get_pa ( slin . entries ) ;
2012-07-19 09:42:40 -05:00
op . inlen = - nx842_get_scatterlist_size ( & slin ) ;
}
/*
* NOTE : If the default max_sync_size is changed from 4 k
* to 64 k , remove the " likely " case below , since a
* scatterlist will always be needed .
*/
if ( likely ( max_sync_size = = NX842_HW_PAGE_SIZE ) ) {
/* Create direct DDE */
2014-01-29 10:34:56 -06:00
op . out = nx842_get_pa ( ( void * ) outbuf ) ;
2012-07-19 09:42:40 -05:00
op . outlen = max_sync_size ;
} else {
/* Create indirect DDE (scatterlist) */
nx842_build_scatterlist ( outbuf , max_sync_size , & slout ) ;
2014-01-29 10:34:56 -06:00
op . out = nx842_get_pa ( slout . entries ) ;
2012-07-19 09:42:40 -05:00
op . outlen = - nx842_get_scatterlist_size ( & slout ) ;
}
/* Send request to pHyp */
ret = vio_h_cop_sync ( local_devdata - > vdev , & op ) ;
/* Check for pHyp error */
if ( ret ) {
dev_dbg ( dev , " %s: vio_h_cop_sync error (ret=%d, hret=%ld) \n " ,
__func__ , ret , op . hcall_err ) ;
dev = NULL ;
goto sw ;
}
/* Check for hardware error */
ret = nx842_validate_result ( dev , & csbcpb - > csb ) ;
if ( ret ) {
dev = NULL ;
goto sw ;
}
/* HW decompression success */
inbuf + = hdr - > sizes [ i ] ;
outbuf + = csbcpb - > csb . processed_byte_count ;
continue ;
sw :
/* software decompression */
size = max_sync_size ;
ret = sw842_decompress (
( unsigned char * ) inbuf , hdr - > sizes [ i ] ,
( unsigned char * ) outbuf , & size , wmem ) ;
if ( ret )
pr_debug ( " %s: sw842_decompress failed with %d \n " ,
__func__ , ret ) ;
if ( ret ) {
if ( ret ! = - ENOSPC & & ret ! = - EINVAL & &
ret ! = - EMSGSIZE )
ret = - EIO ;
goto unlock ;
}
/* SW decompression success */
inbuf + = hdr - > sizes [ i ] ;
outbuf + = size ;
}
* outlen = ( unsigned int ) ( outbuf - ( unsigned long ) out ) ;
unlock :
if ( ret )
/* decompress fail */
nx842_inc_decomp_failed ( local_devdata ) ;
else {
if ( ! dev )
/* software decompress */
nx842_inc_swdecomp ( local_devdata ) ;
nx842_inc_decomp_complete ( local_devdata ) ;
ibm_nx842_incr_hist ( local_devdata - > counters - > decomp_times ,
( get_tb ( ) - start_time ) / tb_ticks_per_usec ) ;
}
rcu_read_unlock ( ) ;
return ret ;
}
EXPORT_SYMBOL_GPL ( nx842_decompress ) ;
/**
* nx842_OF_set_defaults - - Set default ( disabled ) values for devdata
*
* @ devdata - struct nx842_devdata to update
*
* Returns :
* 0 on success
* - ENOENT if @ devdata ptr is NULL
*/
static int nx842_OF_set_defaults ( struct nx842_devdata * devdata )
{
if ( devdata ) {
devdata - > max_sync_size = 0 ;
devdata - > max_sync_sg = 0 ;
devdata - > max_sg_len = 0 ;
devdata - > status = UNAVAILABLE ;
return 0 ;
} else
return - ENOENT ;
}
/**
* nx842_OF_upd_status - - Update the device info from OF status prop
*
* The status property indicates if the accelerator is enabled . If the
* device is in the OF tree it indicates that the hardware is present .
* The status field indicates if the device is enabled when the status
* is ' okay ' . Otherwise the device driver will be disabled .
*
* @ devdata - struct nx842_devdata to update
* @ prop - struct property point containing the maxsyncop for the update
*
* Returns :
* 0 - Device is available
* - EINVAL - Device is not available
*/
static int nx842_OF_upd_status ( struct nx842_devdata * devdata ,
struct property * prop ) {
int ret = 0 ;
const char * status = ( const char * ) prop - > value ;
if ( ! strncmp ( status , " okay " , ( size_t ) prop - > length ) ) {
devdata - > status = AVAILABLE ;
} else {
dev_info ( devdata - > dev , " %s: status '%s' is not 'okay' \n " ,
__func__ , status ) ;
devdata - > status = UNAVAILABLE ;
}
return ret ;
}
/**
* nx842_OF_upd_maxsglen - - Update the device info from OF maxsglen prop
*
* Definition of the ' ibm , max - sg - len ' OF property :
* This field indicates the maximum byte length of a scatter list
* for the platform facility . It is a single cell encoded as with encode - int .
*
* Example :
* # od - x ibm , max - sg - len
* 0000000 0000 0ff 0
*
* In this example , the maximum byte length of a scatter list is
* 0x0ff0 ( 4 , 080 ) .
*
* @ devdata - struct nx842_devdata to update
* @ prop - struct property point containing the maxsyncop for the update
*
* Returns :
* 0 on success
* - EINVAL on failure
*/
static int nx842_OF_upd_maxsglen ( struct nx842_devdata * devdata ,
struct property * prop ) {
int ret = 0 ;
const int * maxsglen = prop - > value ;
if ( prop - > length ! = sizeof ( * maxsglen ) ) {
dev_err ( devdata - > dev , " %s: unexpected format for ibm,max-sg-len property \n " , __func__ ) ;
dev_dbg ( devdata - > dev , " %s: ibm,max-sg-len is %d bytes long, expected %lu bytes \n " , __func__ ,
prop - > length , sizeof ( * maxsglen ) ) ;
ret = - EINVAL ;
} else {
devdata - > max_sg_len = ( unsigned int ) min ( * maxsglen ,
( int ) NX842_HW_PAGE_SIZE ) ;
}
return ret ;
}
/**
* nx842_OF_upd_maxsyncop - - Update the device info from OF maxsyncop prop
*
* Definition of the ' ibm , max - sync - cop ' OF property :
* Two series of cells . The first series of cells represents the maximums
* that can be synchronously compressed . The second series of cells
* represents the maximums that can be synchronously decompressed .
* 1. The first cell in each series contains the count of the number of
* data length , scatter list elements pairs that follow – each being
* of the form
* a . One cell data byte length
* b . One cell total number of scatter list elements
*
* Example :
* # od - x ibm , max - sync - cop
* 0000000 0000 0001 0000 1000 0000 01f e 0000 0001
* 0000020 0000 1000 0000 01f e
*
* In this example , compression supports 0x1000 ( 4 , 096 ) data byte length
* and 0x1fe ( 510 ) total scatter list elements . Decompression supports
* 0x1000 ( 4 , 096 ) data byte length and 0x1f3 ( 510 ) total scatter list
* elements .
*
* @ devdata - struct nx842_devdata to update
* @ prop - struct property point containing the maxsyncop for the update
*
* Returns :
* 0 on success
* - EINVAL on failure
*/
static int nx842_OF_upd_maxsyncop ( struct nx842_devdata * devdata ,
struct property * prop ) {
int ret = 0 ;
const struct maxsynccop_t {
int comp_elements ;
int comp_data_limit ;
int comp_sg_limit ;
int decomp_elements ;
int decomp_data_limit ;
int decomp_sg_limit ;
} * maxsynccop ;
if ( prop - > length ! = sizeof ( * maxsynccop ) ) {
dev_err ( devdata - > dev , " %s: unexpected format for ibm,max-sync-cop property \n " , __func__ ) ;
dev_dbg ( devdata - > dev , " %s: ibm,max-sync-cop is %d bytes long, expected %lu bytes \n " , __func__ , prop - > length ,
sizeof ( * maxsynccop ) ) ;
ret = - EINVAL ;
goto out ;
}
maxsynccop = ( const struct maxsynccop_t * ) prop - > value ;
/* Use one limit rather than separate limits for compression and
* decompression . Set a maximum for this so as not to exceed the
* size that the header can support and round the value down to
* the hardware page size ( 4 K ) */
devdata - > max_sync_size =
( unsigned int ) min ( maxsynccop - > comp_data_limit ,
maxsynccop - > decomp_data_limit ) ;
devdata - > max_sync_size = min_t ( unsigned int , devdata - > max_sync_size ,
SIZE_64K ) ;
if ( devdata - > max_sync_size < SIZE_4K ) {
dev_err ( devdata - > dev , " %s: hardware max data size (%u) is "
" less than the driver minimum, unable to use "
" the hardware device \n " ,
__func__ , devdata - > max_sync_size ) ;
ret = - EINVAL ;
goto out ;
}
devdata - > max_sync_sg = ( unsigned int ) min ( maxsynccop - > comp_sg_limit ,
maxsynccop - > decomp_sg_limit ) ;
if ( devdata - > max_sync_sg < 1 ) {
dev_err ( devdata - > dev , " %s: hardware max sg size (%u) is "
" less than the driver minimum, unable to use "
" the hardware device \n " ,
__func__ , devdata - > max_sync_sg ) ;
ret = - EINVAL ;
goto out ;
}
out :
return ret ;
}
/**
*
* nx842_OF_upd - - Handle OF properties updates for the device .
*
* Set all properties from the OF tree . Optionally , a new property
* can be provided by the @ new_prop pointer to overwrite an existing value .
* The device will remain disabled until all values are valid , this function
* will return an error for updates unless all values are valid .
*
* @ new_prop : If not NULL , this property is being updated . If NULL , update
* all properties from the current values in the OF tree .
*
* Returns :
* 0 - Success
* - ENOMEM - Could not allocate memory for new devdata structure
* - EINVAL - property value not found , new_prop is not a recognized
* property for the device or property value is not valid .
* - ENODEV - Device is not available
*/
static int nx842_OF_upd ( struct property * new_prop )
{
struct nx842_devdata * old_devdata = NULL ;
struct nx842_devdata * new_devdata = NULL ;
struct device_node * of_node = NULL ;
struct property * status = NULL ;
struct property * maxsglen = NULL ;
struct property * maxsyncop = NULL ;
int ret = 0 ;
unsigned long flags ;
spin_lock_irqsave ( & devdata_mutex , flags ) ;
old_devdata = rcu_dereference_check ( devdata ,
lockdep_is_held ( & devdata_mutex ) ) ;
if ( old_devdata )
of_node = old_devdata - > dev - > of_node ;
if ( ! old_devdata | | ! of_node ) {
pr_err ( " %s: device is not available \n " , __func__ ) ;
spin_unlock_irqrestore ( & devdata_mutex , flags ) ;
return - ENODEV ;
}
new_devdata = kzalloc ( sizeof ( * new_devdata ) , GFP_NOFS ) ;
if ( ! new_devdata ) {
dev_err ( old_devdata - > dev , " %s: Could not allocate memory for device data \n " , __func__ ) ;
ret = - ENOMEM ;
goto error_out ;
}
memcpy ( new_devdata , old_devdata , sizeof ( * old_devdata ) ) ;
new_devdata - > counters = old_devdata - > counters ;
/* Set ptrs for existing properties */
status = of_find_property ( of_node , " status " , NULL ) ;
maxsglen = of_find_property ( of_node , " ibm,max-sg-len " , NULL ) ;
maxsyncop = of_find_property ( of_node , " ibm,max-sync-cop " , NULL ) ;
if ( ! status | | ! maxsglen | | ! maxsyncop ) {
dev_err ( old_devdata - > dev , " %s: Could not locate device properties \n " , __func__ ) ;
ret = - EINVAL ;
goto error_out ;
}
2014-07-16 12:48:23 -06:00
/*
* If this is a property update , there are only certain properties that
* we care about . Bail if it isn ' t in the below list
*/
if ( new_prop & & ( strncmp ( new_prop - > name , " status " , new_prop - > length ) | |
strncmp ( new_prop - > name , " ibm,max-sg-len " , new_prop - > length ) | |
strncmp ( new_prop - > name , " ibm,max-sync-cop " , new_prop - > length ) ) )
goto out ;
2012-07-19 09:42:40 -05:00
/* Perform property updates */
ret = nx842_OF_upd_status ( new_devdata , status ) ;
if ( ret )
goto error_out ;
ret = nx842_OF_upd_maxsglen ( new_devdata , maxsglen ) ;
if ( ret )
goto error_out ;
ret = nx842_OF_upd_maxsyncop ( new_devdata , maxsyncop ) ;
if ( ret )
goto error_out ;
out :
dev_info ( old_devdata - > dev , " %s: max_sync_size new:%u old:%u \n " ,
__func__ , new_devdata - > max_sync_size ,
old_devdata - > max_sync_size ) ;
dev_info ( old_devdata - > dev , " %s: max_sync_sg new:%u old:%u \n " ,
__func__ , new_devdata - > max_sync_sg ,
old_devdata - > max_sync_sg ) ;
dev_info ( old_devdata - > dev , " %s: max_sg_len new:%u old:%u \n " ,
__func__ , new_devdata - > max_sg_len ,
old_devdata - > max_sg_len ) ;
rcu_assign_pointer ( devdata , new_devdata ) ;
spin_unlock_irqrestore ( & devdata_mutex , flags ) ;
synchronize_rcu ( ) ;
dev_set_drvdata ( new_devdata - > dev , new_devdata ) ;
kfree ( old_devdata ) ;
return 0 ;
error_out :
if ( new_devdata ) {
dev_info ( old_devdata - > dev , " %s: device disabled \n " , __func__ ) ;
nx842_OF_set_defaults ( new_devdata ) ;
rcu_assign_pointer ( devdata , new_devdata ) ;
spin_unlock_irqrestore ( & devdata_mutex , flags ) ;
synchronize_rcu ( ) ;
dev_set_drvdata ( new_devdata - > dev , new_devdata ) ;
kfree ( old_devdata ) ;
} else {
dev_err ( old_devdata - > dev , " %s: could not update driver from hardware \n " , __func__ ) ;
spin_unlock_irqrestore ( & devdata_mutex , flags ) ;
}
if ( ! ret )
ret = - EINVAL ;
return ret ;
}
/**
* nx842_OF_notifier - Process updates to OF properties for the device
*
* @ np : notifier block
* @ action : notifier action
* @ update : struct pSeries_reconfig_prop_update pointer if action is
* PSERIES_UPDATE_PROPERTY
*
* Returns :
* NOTIFY_OK on success
* NOTIFY_BAD encoded with error number on failure , use
* notifier_to_errno ( ) to decode this value
*/
2012-10-02 16:57:57 +00:00
static int nx842_OF_notifier ( struct notifier_block * np , unsigned long action ,
void * update )
2012-07-19 09:42:40 -05:00
{
2012-10-02 16:57:57 +00:00
struct of_prop_reconfig * upd = update ;
2012-07-19 09:42:40 -05:00
struct nx842_devdata * local_devdata ;
struct device_node * node = NULL ;
rcu_read_lock ( ) ;
local_devdata = rcu_dereference ( devdata ) ;
if ( local_devdata )
node = local_devdata - > dev - > of_node ;
if ( local_devdata & &
2012-10-02 16:57:57 +00:00
action = = OF_RECONFIG_UPDATE_PROPERTY & &
! strcmp ( upd - > dn - > name , node - > name ) ) {
2012-07-19 09:42:40 -05:00
rcu_read_unlock ( ) ;
2012-10-02 16:57:57 +00:00
nx842_OF_upd ( upd - > prop ) ;
2012-07-19 09:42:40 -05:00
} else
rcu_read_unlock ( ) ;
return NOTIFY_OK ;
}
static struct notifier_block nx842_of_nb = {
. notifier_call = nx842_OF_notifier ,
} ;
# define nx842_counter_read(_name) \
static ssize_t nx842_ # # _name # # _show ( struct device * dev , \
struct device_attribute * attr , \
char * buf ) { \
struct nx842_devdata * local_devdata ; \
int p = 0 ; \
rcu_read_lock ( ) ; \
local_devdata = rcu_dereference ( devdata ) ; \
if ( local_devdata ) \
p = snprintf ( buf , PAGE_SIZE , " %ld \n " , \
atomic64_read ( & local_devdata - > counters - > _name ) ) ; \
rcu_read_unlock ( ) ; \
return p ; \
}
# define NX842DEV_COUNTER_ATTR_RO(_name) \
nx842_counter_read ( _name ) ; \
static struct device_attribute dev_attr_ # # _name = __ATTR ( _name , \
0444 , \
nx842_ # # _name # # _show , \
NULL ) ;
NX842DEV_COUNTER_ATTR_RO ( comp_complete ) ;
NX842DEV_COUNTER_ATTR_RO ( comp_failed ) ;
NX842DEV_COUNTER_ATTR_RO ( decomp_complete ) ;
NX842DEV_COUNTER_ATTR_RO ( decomp_failed ) ;
NX842DEV_COUNTER_ATTR_RO ( swdecomp ) ;
static ssize_t nx842_timehist_show ( struct device * ,
struct device_attribute * , char * ) ;
static struct device_attribute dev_attr_comp_times = __ATTR ( comp_times , 0444 ,
nx842_timehist_show , NULL ) ;
static struct device_attribute dev_attr_decomp_times = __ATTR ( decomp_times ,
0444 , nx842_timehist_show , NULL ) ;
static ssize_t nx842_timehist_show ( struct device * dev ,
struct device_attribute * attr , char * buf ) {
char * p = buf ;
struct nx842_devdata * local_devdata ;
atomic64_t * times ;
int bytes_remain = PAGE_SIZE ;
int bytes ;
int i ;
rcu_read_lock ( ) ;
local_devdata = rcu_dereference ( devdata ) ;
if ( ! local_devdata ) {
rcu_read_unlock ( ) ;
return 0 ;
}
if ( attr = = & dev_attr_comp_times )
times = local_devdata - > counters - > comp_times ;
else if ( attr = = & dev_attr_decomp_times )
times = local_devdata - > counters - > decomp_times ;
else {
rcu_read_unlock ( ) ;
return 0 ;
}
for ( i = 0 ; i < ( NX842_HIST_SLOTS - 2 ) ; i + + ) {
bytes = snprintf ( p , bytes_remain , " %u-%uus: \t %ld \n " ,
i ? ( 2 < < ( i - 1 ) ) : 0 , ( 2 < < i ) - 1 ,
atomic64_read ( & times [ i ] ) ) ;
bytes_remain - = bytes ;
p + = bytes ;
}
/* The last bucket holds everything over
* 2 < < ( NX842_HIST_SLOTS - 2 ) us */
bytes = snprintf ( p , bytes_remain , " %uus - : \t %ld \n " ,
2 < < ( NX842_HIST_SLOTS - 2 ) ,
atomic64_read ( & times [ ( NX842_HIST_SLOTS - 1 ) ] ) ) ;
p + = bytes ;
rcu_read_unlock ( ) ;
return p - buf ;
}
static struct attribute * nx842_sysfs_entries [ ] = {
& dev_attr_comp_complete . attr ,
& dev_attr_comp_failed . attr ,
& dev_attr_decomp_complete . attr ,
& dev_attr_decomp_failed . attr ,
& dev_attr_swdecomp . attr ,
& dev_attr_comp_times . attr ,
& dev_attr_decomp_times . attr ,
NULL ,
} ;
static struct attribute_group nx842_attribute_group = {
. name = NULL , /* put in device directory */
. attrs = nx842_sysfs_entries ,
} ;
static int __init nx842_probe ( struct vio_dev * viodev ,
const struct vio_device_id * id )
{
struct nx842_devdata * old_devdata , * new_devdata = NULL ;
unsigned long flags ;
int ret = 0 ;
spin_lock_irqsave ( & devdata_mutex , flags ) ;
old_devdata = rcu_dereference_check ( devdata ,
lockdep_is_held ( & devdata_mutex ) ) ;
if ( old_devdata & & old_devdata - > vdev ! = NULL ) {
dev_err ( & viodev - > dev , " %s: Attempt to register more than one instance of the hardware \n " , __func__ ) ;
ret = - 1 ;
goto error_unlock ;
}
dev_set_drvdata ( & viodev - > dev , NULL ) ;
new_devdata = kzalloc ( sizeof ( * new_devdata ) , GFP_NOFS ) ;
if ( ! new_devdata ) {
dev_err ( & viodev - > dev , " %s: Could not allocate memory for device data \n " , __func__ ) ;
ret = - ENOMEM ;
goto error_unlock ;
}
new_devdata - > counters = kzalloc ( sizeof ( * new_devdata - > counters ) ,
GFP_NOFS ) ;
if ( ! new_devdata - > counters ) {
dev_err ( & viodev - > dev , " %s: Could not allocate memory for performance counters \n " , __func__ ) ;
ret = - ENOMEM ;
goto error_unlock ;
}
new_devdata - > vdev = viodev ;
new_devdata - > dev = & viodev - > dev ;
nx842_OF_set_defaults ( new_devdata ) ;
rcu_assign_pointer ( devdata , new_devdata ) ;
spin_unlock_irqrestore ( & devdata_mutex , flags ) ;
synchronize_rcu ( ) ;
kfree ( old_devdata ) ;
2012-10-02 16:57:57 +00:00
of_reconfig_notifier_register ( & nx842_of_nb ) ;
2012-07-19 09:42:40 -05:00
ret = nx842_OF_upd ( NULL ) ;
if ( ret & & ret ! = - ENODEV ) {
dev_err ( & viodev - > dev , " could not parse device tree. %d \n " , ret ) ;
ret = - 1 ;
goto error ;
}
rcu_read_lock ( ) ;
2014-05-28 14:02:24 +02:00
dev_set_drvdata ( & viodev - > dev , rcu_dereference ( devdata ) ) ;
2012-07-19 09:42:40 -05:00
rcu_read_unlock ( ) ;
if ( sysfs_create_group ( & viodev - > dev . kobj , & nx842_attribute_group ) ) {
dev_err ( & viodev - > dev , " could not create sysfs device attributes \n " ) ;
ret = - 1 ;
goto error ;
}
return 0 ;
error_unlock :
spin_unlock_irqrestore ( & devdata_mutex , flags ) ;
if ( new_devdata )
kfree ( new_devdata - > counters ) ;
kfree ( new_devdata ) ;
error :
return ret ;
}
static int __exit nx842_remove ( struct vio_dev * viodev )
{
struct nx842_devdata * old_devdata ;
unsigned long flags ;
pr_info ( " Removing IBM Power 842 compression device \n " ) ;
sysfs_remove_group ( & viodev - > dev . kobj , & nx842_attribute_group ) ;
spin_lock_irqsave ( & devdata_mutex , flags ) ;
old_devdata = rcu_dereference_check ( devdata ,
lockdep_is_held ( & devdata_mutex ) ) ;
2012-10-02 16:57:57 +00:00
of_reconfig_notifier_unregister ( & nx842_of_nb ) ;
2014-03-24 01:02:42 +05:30
RCU_INIT_POINTER ( devdata , NULL ) ;
2012-07-19 09:42:40 -05:00
spin_unlock_irqrestore ( & devdata_mutex , flags ) ;
synchronize_rcu ( ) ;
dev_set_drvdata ( & viodev - > dev , NULL ) ;
if ( old_devdata )
kfree ( old_devdata - > counters ) ;
kfree ( old_devdata ) ;
return 0 ;
}
static struct vio_device_id nx842_driver_ids [ ] = {
{ " ibm,compression-v1 " , " ibm,compression " } ,
{ " " , " " } ,
} ;
static struct vio_driver nx842_driver = {
. name = MODULE_NAME ,
. probe = nx842_probe ,
. remove = nx842_remove ,
. get_desired_dma = nx842_get_desired_dma ,
. id_table = nx842_driver_ids ,
} ;
static int __init nx842_init ( void )
{
struct nx842_devdata * new_devdata ;
pr_info ( " Registering IBM Power 842 compression driver \n " ) ;
RCU_INIT_POINTER ( devdata , NULL ) ;
new_devdata = kzalloc ( sizeof ( * new_devdata ) , GFP_KERNEL ) ;
if ( ! new_devdata ) {
pr_err ( " Could not allocate memory for device data \n " ) ;
return - ENOMEM ;
}
new_devdata - > status = UNAVAILABLE ;
RCU_INIT_POINTER ( devdata , new_devdata ) ;
return vio_register_driver ( & nx842_driver ) ;
}
module_init ( nx842_init ) ;
static void __exit nx842_exit ( void )
{
struct nx842_devdata * old_devdata ;
unsigned long flags ;
pr_info ( " Exiting IBM Power 842 compression driver \n " ) ;
spin_lock_irqsave ( & devdata_mutex , flags ) ;
old_devdata = rcu_dereference_check ( devdata ,
lockdep_is_held ( & devdata_mutex ) ) ;
2014-03-24 01:02:42 +05:30
RCU_INIT_POINTER ( devdata , NULL ) ;
2012-07-19 09:42:40 -05:00
spin_unlock_irqrestore ( & devdata_mutex , flags ) ;
synchronize_rcu ( ) ;
if ( old_devdata )
dev_set_drvdata ( old_devdata - > dev , NULL ) ;
kfree ( old_devdata ) ;
vio_unregister_driver ( & nx842_driver ) ;
}
module_exit ( nx842_exit ) ;
/*********************************
* 842 software decompressor
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
typedef int ( * sw842_template_op ) ( const char * * , int * , unsigned char * * ,
struct sw842_fifo * ) ;
static int sw842_data8 ( const char * * , int * , unsigned char * * ,
struct sw842_fifo * ) ;
static int sw842_data4 ( const char * * , int * , unsigned char * * ,
struct sw842_fifo * ) ;
static int sw842_data2 ( const char * * , int * , unsigned char * * ,
struct sw842_fifo * ) ;
static int sw842_ptr8 ( const char * * , int * , unsigned char * * ,
struct sw842_fifo * ) ;
static int sw842_ptr4 ( const char * * , int * , unsigned char * * ,
struct sw842_fifo * ) ;
static int sw842_ptr2 ( const char * * , int * , unsigned char * * ,
struct sw842_fifo * ) ;
/* special templates */
# define SW842_TMPL_REPEAT 0x1B
# define SW842_TMPL_ZEROS 0x1C
# define SW842_TMPL_EOF 0x1E
static sw842_template_op sw842_tmpl_ops [ 26 ] [ 4 ] = {
{ sw842_data8 , NULL } , /* 0 (00000) */
{ sw842_data4 , sw842_data2 , sw842_ptr2 , NULL } ,
{ sw842_data4 , sw842_ptr2 , sw842_data2 , NULL } ,
{ sw842_data4 , sw842_ptr2 , sw842_ptr2 , NULL } ,
{ sw842_data4 , sw842_ptr4 , NULL } ,
{ sw842_data2 , sw842_ptr2 , sw842_data4 , NULL } ,
{ sw842_data2 , sw842_ptr2 , sw842_data2 , sw842_ptr2 } ,
{ sw842_data2 , sw842_ptr2 , sw842_ptr2 , sw842_data2 } ,
{ sw842_data2 , sw842_ptr2 , sw842_ptr2 , sw842_ptr2 , } ,
{ sw842_data2 , sw842_ptr2 , sw842_ptr4 , NULL } ,
{ sw842_ptr2 , sw842_data2 , sw842_data4 , NULL } , /* 10 (01010) */
{ sw842_ptr2 , sw842_data4 , sw842_ptr2 , NULL } ,
{ sw842_ptr2 , sw842_data2 , sw842_ptr2 , sw842_data2 } ,
{ sw842_ptr2 , sw842_data2 , sw842_ptr2 , sw842_ptr2 } ,
{ sw842_ptr2 , sw842_data2 , sw842_ptr4 , NULL } ,
{ sw842_ptr2 , sw842_ptr2 , sw842_data4 , NULL } ,
{ sw842_ptr2 , sw842_ptr2 , sw842_data2 , sw842_ptr2 } ,
{ sw842_ptr2 , sw842_ptr2 , sw842_ptr2 , sw842_data2 } ,
{ sw842_ptr2 , sw842_ptr2 , sw842_ptr2 , sw842_ptr2 } ,
{ sw842_ptr2 , sw842_ptr2 , sw842_ptr4 , NULL } ,
{ sw842_ptr4 , sw842_data4 , NULL } , /* 20 (10100) */
{ sw842_ptr4 , sw842_data2 , sw842_ptr2 , NULL } ,
{ sw842_ptr4 , sw842_ptr2 , sw842_data2 , NULL } ,
{ sw842_ptr4 , sw842_ptr2 , sw842_ptr2 , NULL } ,
{ sw842_ptr4 , sw842_ptr4 , NULL } ,
{ sw842_ptr8 , NULL }
} ;
/* Software decompress helpers */
static uint8_t sw842_get_byte ( const char * buf , int bit )
{
uint8_t tmpl ;
uint16_t tmp ;
tmp = htons ( * ( uint16_t * ) ( buf ) ) ;
tmp = ( uint16_t ) ( tmp < < bit ) ;
tmp = ntohs ( tmp ) ;
memcpy ( & tmpl , & tmp , 1 ) ;
return tmpl ;
}
static uint8_t sw842_get_template ( const char * * buf , int * bit )
{
uint8_t byte ;
byte = sw842_get_byte ( * buf , * bit ) ;
byte = byte > > 3 ;
byte & = 0x1F ;
* buf + = ( * bit + 5 ) / 8 ;
* bit = ( * bit + 5 ) % 8 ;
return byte ;
}
/* repeat_count happens to be 5-bit too (like the template) */
static uint8_t sw842_get_repeat_count ( const char * * buf , int * bit )
{
uint8_t byte ;
byte = sw842_get_byte ( * buf , * bit ) ;
byte = byte > > 2 ;
byte & = 0x3F ;
* buf + = ( * bit + 6 ) / 8 ;
* bit = ( * bit + 6 ) % 8 ;
return byte ;
}
static uint8_t sw842_get_ptr2 ( const char * * buf , int * bit )
{
uint8_t ptr ;
ptr = sw842_get_byte ( * buf , * bit ) ;
( * buf ) + + ;
return ptr ;
}
static uint16_t sw842_get_ptr4 ( const char * * buf , int * bit ,
struct sw842_fifo * fifo )
{
uint16_t ptr ;
ptr = htons ( * ( uint16_t * ) ( * buf ) ) ;
ptr = ( uint16_t ) ( ptr < < * bit ) ;
ptr = ptr > > 7 ;
ptr & = 0x01FF ;
* buf + = ( * bit + 9 ) / 8 ;
* bit = ( * bit + 9 ) % 8 ;
return ptr ;
}
static uint8_t sw842_get_ptr8 ( const char * * buf , int * bit ,
struct sw842_fifo * fifo )
{
return sw842_get_ptr2 ( buf , bit ) ;
}
/* Software decompress template ops */
static int sw842_data8 ( const char * * inbuf , int * inbit ,
unsigned char * * outbuf , struct sw842_fifo * fifo )
{
int ret ;
ret = sw842_data4 ( inbuf , inbit , outbuf , fifo ) ;
if ( ret )
return ret ;
ret = sw842_data4 ( inbuf , inbit , outbuf , fifo ) ;
return ret ;
}
static int sw842_data4 ( const char * * inbuf , int * inbit ,
unsigned char * * outbuf , struct sw842_fifo * fifo )
{
int ret ;
ret = sw842_data2 ( inbuf , inbit , outbuf , fifo ) ;
if ( ret )
return ret ;
ret = sw842_data2 ( inbuf , inbit , outbuf , fifo ) ;
return ret ;
}
static int sw842_data2 ( const char * * inbuf , int * inbit ,
unsigned char * * outbuf , struct sw842_fifo * fifo )
{
* * outbuf = sw842_get_byte ( * inbuf , * inbit ) ;
( * inbuf ) + + ;
( * outbuf ) + + ;
* * outbuf = sw842_get_byte ( * inbuf , * inbit ) ;
( * inbuf ) + + ;
( * outbuf ) + + ;
return 0 ;
}
static int sw842_ptr8 ( const char * * inbuf , int * inbit ,
unsigned char * * outbuf , struct sw842_fifo * fifo )
{
uint8_t ptr ;
ptr = sw842_get_ptr8 ( inbuf , inbit , fifo ) ;
if ( ! fifo - > f84_full & & ( ptr > = fifo - > f8_count ) )
return 1 ;
memcpy ( * outbuf , fifo - > f8 [ ptr ] , 8 ) ;
* outbuf + = 8 ;
return 0 ;
}
static int sw842_ptr4 ( const char * * inbuf , int * inbit ,
unsigned char * * outbuf , struct sw842_fifo * fifo )
{
uint16_t ptr ;
ptr = sw842_get_ptr4 ( inbuf , inbit , fifo ) ;
if ( ! fifo - > f84_full & & ( ptr > = fifo - > f4_count ) )
return 1 ;
memcpy ( * outbuf , fifo - > f4 [ ptr ] , 4 ) ;
* outbuf + = 4 ;
return 0 ;
}
static int sw842_ptr2 ( const char * * inbuf , int * inbit ,
unsigned char * * outbuf , struct sw842_fifo * fifo )
{
uint8_t ptr ;
ptr = sw842_get_ptr2 ( inbuf , inbit ) ;
if ( ! fifo - > f2_full & & ( ptr > = fifo - > f2_count ) )
return 1 ;
memcpy ( * outbuf , fifo - > f2 [ ptr ] , 2 ) ;
* outbuf + = 2 ;
return 0 ;
}
static void sw842_copy_to_fifo ( const char * buf , struct sw842_fifo * fifo )
{
unsigned char initial_f2count = fifo - > f2_count ;
memcpy ( fifo - > f8 [ fifo - > f8_count ] , buf , 8 ) ;
fifo - > f4_count + = 2 ;
fifo - > f8_count + = 1 ;
if ( ! fifo - > f84_full & & fifo - > f4_count > = 512 ) {
fifo - > f84_full = 1 ;
fifo - > f4_count / = 512 ;
}
memcpy ( fifo - > f2 [ fifo - > f2_count + + ] , buf , 2 ) ;
memcpy ( fifo - > f2 [ fifo - > f2_count + + ] , buf + 2 , 2 ) ;
memcpy ( fifo - > f2 [ fifo - > f2_count + + ] , buf + 4 , 2 ) ;
memcpy ( fifo - > f2 [ fifo - > f2_count + + ] , buf + 6 , 2 ) ;
if ( fifo - > f2_count < initial_f2count )
fifo - > f2_full = 1 ;
}
static int sw842_decompress ( const unsigned char * src , int srclen ,
unsigned char * dst , int * destlen ,
const void * wrkmem )
{
uint8_t tmpl ;
const char * inbuf ;
int inbit = 0 ;
unsigned char * outbuf , * outbuf_end , * origbuf , * prevbuf ;
const char * inbuf_end ;
sw842_template_op op ;
int opindex ;
int i , repeat_count ;
struct sw842_fifo * fifo ;
int ret = 0 ;
fifo = & ( ( struct nx842_workmem * ) ( wrkmem ) ) - > swfifo ;
memset ( fifo , 0 , sizeof ( * fifo ) ) ;
origbuf = NULL ;
inbuf = src ;
inbuf_end = src + srclen ;
outbuf = dst ;
outbuf_end = dst + * destlen ;
while ( ( tmpl = sw842_get_template ( & inbuf , & inbit ) ) ! = SW842_TMPL_EOF ) {
if ( inbuf > = inbuf_end ) {
ret = - EINVAL ;
goto out ;
}
opindex = 0 ;
prevbuf = origbuf ;
origbuf = outbuf ;
switch ( tmpl ) {
case SW842_TMPL_REPEAT :
if ( prevbuf = = NULL ) {
ret = - EINVAL ;
goto out ;
}
repeat_count = sw842_get_repeat_count ( & inbuf ,
& inbit ) + 1 ;
/* Did the repeat count advance past the end of input */
if ( inbuf > inbuf_end ) {
ret = - EINVAL ;
goto out ;
}
for ( i = 0 ; i < repeat_count ; i + + ) {
/* Would this overflow the output buffer */
if ( ( outbuf + 8 ) > outbuf_end ) {
ret = - ENOSPC ;
goto out ;
}
memcpy ( outbuf , prevbuf , 8 ) ;
sw842_copy_to_fifo ( outbuf , fifo ) ;
outbuf + = 8 ;
}
break ;
case SW842_TMPL_ZEROS :
/* Would this overflow the output buffer */
if ( ( outbuf + 8 ) > outbuf_end ) {
ret = - ENOSPC ;
goto out ;
}
memset ( outbuf , 0 , 8 ) ;
sw842_copy_to_fifo ( outbuf , fifo ) ;
outbuf + = 8 ;
break ;
default :
if ( tmpl > 25 ) {
ret = - EINVAL ;
goto out ;
}
/* Does this go past the end of the input buffer */
if ( ( inbuf + 2 ) > inbuf_end ) {
ret = - EINVAL ;
goto out ;
}
/* Would this overflow the output buffer */
if ( ( outbuf + 8 ) > outbuf_end ) {
ret = - ENOSPC ;
goto out ;
}
while ( opindex < 4 & &
( op = sw842_tmpl_ops [ tmpl ] [ opindex + + ] )
! = NULL ) {
ret = ( * op ) ( & inbuf , & inbit , & outbuf , fifo ) ;
if ( ret ) {
ret = - EINVAL ;
goto out ;
}
sw842_copy_to_fifo ( origbuf , fifo ) ;
}
}
}
out :
if ( ! ret )
* destlen = ( unsigned int ) ( outbuf - dst ) ;
else
* destlen = 0 ;
return ret ;
}