sirroom
驱动大牛
驱动大牛
  • 注册日期2001-07-30
  • 最后登录2018-05-29
  • 粉丝0
  • 关注0
  • 积分6分
  • 威望11点
  • 贡献值1点
  • 好评度0点
  • 原创分0分
  • 专家分0分
阅读:1911回复:6

就freeswan的硬件加密的第二桶水

楼主#
更多 发布于:2002-06-21 13:13
hifn7811中 Cryptolib.c
 

/************************************************
 Cryptolib.c
 Cryptographic front end.
*************************************************/

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/if_crypto.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/random.h>
#include <linux/init.h>

/* S/W crypto includes */
#include <openssl/rc4.h>
#include <linux/des.h>
#include <linux/md5.h>
#include <linux/sha1.h>

#define ENGINE_MAX 5

#ifndef TRUE
#define TRUE (1==1)
#define FALSE !TRUE
#endif

static
struct crypto_engine_def *engine_db[ENGINE_MAX];

struct crypto_request *crypto_alloc_request(int flags)
{
struct crypto_request *req;
req = kmalloc(sizeof(struct crypto_request),flags);
if(req == NULL)
return NULL;
memset(req,0,sizeof(struct crypto_request));
return req;
}

struct crypto_engine_def *crypto_open_engine(crypto_cmd_t cap,unsigned int attrib)
{
struct crypto_engine_def *eng;
int t;

for(t=0;t<ENGINE_MAX;t++)
{
eng = engine_db[t];
if(eng == NULL)
continue;
if((cap & eng->capability) == cap
&& (attrib & eng->attributes) == attrib)
{
if(eng->open != NULL)
eng->open();
return eng;
}
}
return NULL;
}

void crypto_close_engine(struct crypto_engine_def *eng)
{
if(eng != NULL && eng->close != NULL)
eng->close();
}

#define BASIC_CMDS (CRYPT_RC4|CRYPT_DES|CRYPT_3DES|CRYPT_MD5|CRYPT_SHA|CRYPT_RANDOM)
int crypto_execute(struct crypto_request *req)
{
struct crypto_engine_def *eng;
int t,ret;

if(req != NULL)
{
if(req->engine == NULL)
{
for(t=0;t<ENGINE_MAX;t++)
{
eng = engine_db[t];
if(eng == NULL)
continue;
/* Engines are sorted in performance order, take first one that matches */
if((req->op.cmd & eng->capability) == (req->op.cmd & BASIC_CMDS))
break;
}
}
else
eng = req->engine;

if(eng != NULL && eng->request_handler != NULL)
{
ret = eng->request_handler(req);
if(ret == CRYPT_READY && req->callback != NULL)
req->callback(req);

return ret;
}
}
return CRYPT_ERROR;
}

int crypto_query(crypto_cmd_t cap,unsigned int attrib)
{
struct crypto_engine_def *eng;
int t;

for(t=0;t<ENGINE_MAX;t++)
{
eng = engine_db[t];
if(eng == NULL)
continue;
if((cap & eng->capability) == cap
&& (attrib & eng->attributes) == attrib)
return TRUE;
}
return FALSE;
}



int crypto_register_engine(struct crypto_engine_def *eng)
{
int t,r;
struct crypto_engine_def **slot = NULL;

if(eng == NULL || eng->name == NULL)
return -EINVAL;

eng->capability &= BASIC_CMDS;
for(t=0;t<ENGINE_MAX;t++)
{
/* Put S/W last, H/W first */
if(eng->attributes & IS_HARDWARE)
r = (ENGINE_MAX-1) - t;
else
r = t;
if(engine_db[r] == NULL)
{
slot = &engine_db[r];
}
else
{
if(strcmp(eng->name,engine_db[r]->name) == 0)
return -EEXIST;
}
}
if(slot == NULL)
return -EMFILE;

*slot = eng;
printk(KERN_INFO \"Crypto engine %s registered\\n\",eng->name);
return 0;
}

int crypto_unregister_engine(char *name)
{
int t;

for(t=0;t<ENGINE_MAX;t++)
{
if(engine_db[t] != NULL && strcmp(engine_db[t]->name,name) == 0)
{
printk(KERN_INFO \"Crypto engine %s unregistered\\n\",engine_db[t]->name);
engine_db[t] = NULL;
return 0;
}
}
return -ENOENT;
}

/***************************************************
* S/W crypto engine
****************************************************/
static inline
int do_hmac(struct crypto_request *req,struct crypto_operation *op)
{
unsigned char digest[20];
int hash_len,i;
MD5_CTX md5_ctx;
SHA1_CTX sha1_ctx;
unsigned char k_ipad[65];    /* inner padding -
                                      * key XORd with ipad
                                      */
unsigned char k_opad[65];    /* outer padding -
                                      * key XORd with opad
                                      */
      /* start out by storing key in pads */
memset(k_ipad, 0, sizeof k_ipad);
memset(k_opad, 0, sizeof k_opad);
memcpy(k_ipad, op->u.hash.hmac_key,op->u.hash.hmac_key_len);
memcpy(k_opad, op->u.hash.hmac_key,op->u.hash.hmac_key_len);

        /* XOR key with ipad and opad values */
for (i=0; i<64; i++) {
k_ipad ^= 0x36;
k_opad ^= 0x5c;
}
if(op->cmd & CRYPT_MD5)
{
        /*
         * perform inner MD5
         */
hash_len = 16;
MD5Init(&md5_ctx);                   /* init md5_ctx for 1st
                                              * pass */
MD5Update(&md5_ctx, k_ipad, 64);      /* start with inner pad */
MD5Update(&md5_ctx, &req->outbuf[op->offset], op->len); /* then text of datagram */
MD5Final(digest, &md5_ctx);          /* finish up 1st pass */
        /*
         * perform outer MD5
         */
MD5Init(&md5_ctx);                   /* init md5_ctx for 2nd
                                              * pass */
MD5Update(&md5_ctx, k_opad, 64);     /* start with outer pad */
MD5Update(&md5_ctx, digest, 16);     /* then results of 1st
                                              * hash */
MD5Final(digest, &md5_ctx);          /* finish up 2nd pass */
}
else if(op->cmd & CRYPT_SHA)
{
hash_len = 20;
SHA1Init(&sha1_ctx);                   /* init sha1_ctx for 1st
                                              * pass */
SHA1Update(&sha1_ctx, k_ipad, 64);      /* start with inner pad */
SHA1Update(&sha1_ctx, &req->outbuf[op->offset], op->len); /* then text of datagram */
SHA1Final(digest, &sha1_ctx);          /* finish up 1st pass */
        /*
         * perform outer
         */
SHA1Init(&sha1_ctx);                   /* init sha1_ctx for 2nd
                                              * pass */
SHA1Update(&sha1_ctx, k_opad, 64);     /* start with outer pad */
SHA1Update(&sha1_ctx, digest, 20);     /* then results of 1st
                                              * hash */
SHA1Final(digest, &sha1_ctx);          /* finish up 2nd pass */
}
if(op->cmd & CRYPT_96)
{
hash_len = 96/8; /* Truncate digest */
}

if(op->cmd & CRYPT_APPEND)
{
if(req->outlen < (req->inlen + hash_len))
return CRYPT_ERROR;
memcpy(req->outbuf + req->inlen, digest, hash_len);
req->outlen = req->inlen + hash_len;
}
if(req->outlen < hash_len)
return CRYPT_ERROR;
memcpy(op->u.hash.result, digest, hash_len);
op->u.hash.reslen= hash_len;
return CRYPT_READY;
}

static inline
int do_regular_hash(struct crypto_request *req,struct crypto_operation *op)
{
unsigned char digest[20];
int hash_len;
MD5_CTX md5_ctx;
SHA1_CTX sha1_ctx;

if(op->cmd & CRYPT_MD5)
{
hash_len = 16;
MD5Init(&md5_ctx);                  
MD5Update(&md5_ctx, &req->outbuf[op->offset], op->len);
MD5Final(digest, &md5_ctx);          
}
else if(op->cmd & CRYPT_SHA)
{
hash_len = 20;
SHA1Init(&sha1_ctx);                  
SHA1Update(&sha1_ctx, &req->outbuf[op->offset], op->len); /* then text of datagram */
SHA1Final(digest, &sha1_ctx);          /* finish up 1st pass */
}
if(op->cmd & CRYPT_96)
{
hash_len = 96/8; /* Truncate digest */
}

if(op->cmd & CRYPT_APPEND)
{
if(req->outlen < (req->inlen + hash_len))
return CRYPT_ERROR;
memcpy(req->outbuf + req->inlen, digest, hash_len);
req->outlen = req->inlen + hash_len;
}

memcpy(op->u.hash.result, digest, hash_len);
op->u.hash.reslen= hash_len;
return CRYPT_READY;
}

static
int sw_request_handler(struct crypto_request *req)
{
unsigned char *in, *out;
int t,ret,maxout,reslen=0;
struct crypto_operation *op;

maxout = req->outlen;
if(req->outlen  < req->inlen)
return CRYPT_ERROR;

if(req->inbuf != NULL && req->inbuf != req->outbuf)
memcpy(req->outbuf,req->inbuf,req->inlen);

for(op = &req->op;op != NULL;op=op->next)
{
in = &req->outbuf[op->offset];
out = &req->outbuf[op->offset];

if(op->cmd & CRYPT_RANDOM)
{
get_random_bytes(req->outbuf,req->outlen);
reslen = req->outlen;
break; /* Should be the only real operation... */
}

if(op->cmd & CRYPT_RC4)
{
RC4_KEY key;

if(maxout < req->inlen || req->op.u.rc4.key == NULL)
return CRYPT_ERROR;

RC4_set_key(&key,op->u.rc4.key->len,op->u.rc4.key->raw);
RC4(&key,op->len,in,out);
req->outlen = req->inlen;
}

if(op->cmd & CRYPT_DES)
{
/* To do */
return CRYPT_ERROR;
}
if(op->cmd & CRYPT_3DES)
{
if(maxout < req->inlen)
return CRYPT_ERROR;

if(op->cmd & CRYPT_CBC)
{
des_key_schedule sched[3];
des_cblock ivec;

memcpy(&ivec, op->u.des.ivec, 8);

for(t=0;t<3;t++)
des_set_key(&(op->u.des.key[t]),sched[t]);
des_ede3_cbc_encrypt((des_cblock*)in,(des_cblock*)out,op->len,sched[0],sched[1],sched[2]
,&ivec,(op->cmd & CRYPT_DECODE)?0:1);
req->outlen = req->inlen;
}
if(op->cmd & CRYPT_ECB)
{
/* Todo */
return CRYPT_ERROR;
}
}
if(op->cmd & (CRYPT_MD5|CRYPT_SHA))
{
if(op->cmd & CRYPT_HMAC)
{
ret = do_hmac(req,op);
if(ret != CRYPT_READY)
return ret;
}
else
{
ret = do_regular_hash(req,op);
if(ret != CRYPT_READY)
return ret;
}
}
reslen = req->outlen > reslen? req->outlen: reslen;
req->outlen = maxout;
}

req->outlen = reslen;
req->flags |= CRYPT_SOFTWARE;
return CRYPT_READY;
}

/****** RANDOM NUMBERS HANDLING HERE ********/
static struct wait_queue *crypto_random_read_wait;

struct random_priv
{
ssize_t nbytes,count;
char *buf;
};

static
void crypto_random_callback(struct crypto_request *req)
{
struct random_priv *rp = (struct random_priv *) req->priv;

rp->count += req->outlen;
rp->buf += req->outlen;
rp->nbytes -= req->outlen;
crypto_free_request(req);
wake_up_interruptible(&crypto_random_read_wait);
}

static ssize_t
crypto_random_read(struct file * file, char * buf, size_t nbytes, loff_t *ppos)
{
struct wait_queue       wait = { current, NULL };
ssize_t                 n, retval = 0;
struct crypto_request *req;
struct random_priv rpriv;

if (nbytes == 0)
return 0;

rpriv.nbytes = nbytes;
rpriv.count = 0;
rpriv.buf = buf;

add_wait_queue(&crypto_random_read_wait, &wait);
current->state = TASK_INTERRUPTIBLE;

n = rpriv.nbytes;
if (file->f_flags & O_NONBLOCK) {
return -EAGAIN;
}
if (signal_pending(current)) {
return -ERESTARTSYS;
}
/* Go and get random data */
req = crypto_alloc_request(GFP_KERNEL);
req->op.cmd = CRYPT_RANDOM;
req->op.offset = 0;
req->op.len = n;
req->inbuf = NULL;
req->inlen = 0;
req->outlen = n;
req->outbuf = rpriv.buf;
req->flags = 0;
req->callback = crypto_random_callback;
req->priv = (void*) &rpriv;
crypto_execute(req);

schedule();

current->state = TASK_RUNNING;
remove_wait_queue(&crypto_random_read_wait, &wait);


        /*
         * If we gave the user some bytes, update the access time.
         */
if (rpriv.count != 0) {
UPDATE_ATIME(file->f_dentry->d_inode);
}

return (rpriv.count ? rpriv.count : retval);
}

static struct file_operations crypto_random_fops = {
#if LINUX_VERSION_CODE >= 0x20300 /* Someone knows when these made their debut? */
owner: THIS_MODULE,
#endif
read: crypto_random_read /* read */
};

static
void __init crypto_random_init(void)
{
if (register_chrdev(121, \"crypto_random\", &crypto_random_fops))
{
printk(KERN_NOTICE \"Can\'t allocate major number 121 for random.\\n\");
}
}


static
struct crypto_engine_def sw_def =
{
name : \"S/W\",
capability: (CRYPT_RC4|CRYPT_DES|CRYPT_3DES|CRYPT_MD5|CRYPT_SHA),
attributes: IS_SOFTWARE | IS_SYNC,
request_handler: sw_request_handler
};

int  __init crypto_init(void)
{
crypto_register_engine(&sw_def);
crypto_random_init();
return 0;
}

#ifdef MODULE
mod_exit_t crypto_cleanup (void)
{
crypto_unregister_engine(sw.def.name);
}

module_exit(crypto_cleanup);
#endif

//#if LINUX_VERSION_CODE > 0x20300
module_init(crypto_init);
//#endif
111
sirroom
驱动大牛
驱动大牛
  • 注册日期2001-07-30
  • 最后登录2018-05-29
  • 粉丝0
  • 关注0
  • 积分6分
  • 威望11点
  • 贡献值1点
  • 好评度0点
  • 原创分0分
  • 专家分0分
沙发#
发布于:2002-06-21 13:14
hifn7811.c 驱动程序

/****************************************************************************/

/*
 * hifn7811.c   -- Driver for Hi/fn 7811 encryption engine.
 *
 *    (C) Copyright 2001, Colubris Networks, Inc (sources.colubris.com)
 *   (C) Copyright 2000, Lineo, Inc (www.lineo.com)
 * (C) Copyright 2000, Greg Ungerer (gerg@lineo.com)
 *      
 * History:
 * - Major modifications by Martin Gadbois (Colubris Networks Inc.) Almost no original code remains from
 * initial release from Lineo.
 *
 *      
 * This program is a part of the Linux kernel, and may be freely
 * copied under the terms of the GNU General Public License (GPL),
 * version 2, or at your option any later version.
 */

/****************************************************************************/

#include <linux/config.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
#include <linux/string.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/smp_lock.h>
#include <linux/slab.h>
#include <linux/tqueue.h>
#include <linux/proc_fs.h>
#include <linux/timer.h>
#include <linux/if_crypto.h>
#include <linux/pci.h>

#include <asm/io.h>
#include <asm/system.h>
#include <asm/delay.h>
#include <asm/irq.h>
#include <asm/bitops.h>
#include <asm/processor.h>
#include <asm/cache.h>

#include \"hifn7811.h\"

/*********i386*************************/
#define flush_dcache_range(a,b)
#define invalidate_dcache_range(a,b)
/***********************************/


/* Borrow a BH for our usage... */
#define HIFN_BH RISCOM8_BH /* Won\'t be needing this... Must be < NET_BH */

#define hifn7811_memcpyto(dst,src,len) memcpy((void *)dst,src,len)
#define hifn7811_memcpyfrom hifn7811_memcpyto

#define MGTEST
#define PRINT_USAGE

#define SMAX 1024
#undef TRACING

#ifdef DEBUG
#define SIG_ON(req)   if(req->flags & CRYPT_DATA)  { cn1000_BSCR_biton(BCSR0_LED_POWEROFF); }
#define SIG_OFF(req)  if(req->flags & CRYPT_DATA)  { cn1000_BSCR_bitoff(BCSR0_LED_POWEROFF); }
#else
#define SIG_ON(req)  
#define SIG_OFF(req)  
#endif
#define SIG_MARK(req)  SIG_OFF(req) SIG_ON(req)

static int disable = 0; /* Set to 1 while loading module to start task */
static int always = 1; /* Set to 1 to force HW only, to 2 to force SW only */
MODULE_PARM(disable, \"i\");
MODULE_PARM(always, \"i\"); /* Set to 1 to force H/W */

static DECLARE_WAIT_QUEUE_HEAD(hifn_wait);

/* Local prototype */
static
void hifn7811_interrupt(int, void *, struct pt_regs *);
static
void hifn7811_bh(void);
static
int process_hw_crypto(void);
static
int hw_crypto_request_fnc(struct crypto_request *req);

#define HIFN_INC_PTR(h,f,type) h->f##type++; if(h->f##type->cmd & HIFN7811_DESC_JUMP) h->f##type = &h->desc->f[0]
#define P(code) code; mb() /* Protect code (similar to writel() but I\'m lazy) */

#define FUNC_IN(h) /* printk(\"Entering %s GS=%x\\n\",__FUNCTION__,read_pci(h->grp1->global_status)) */
#define FUNC_OUT(h) /* printk(\"Leaving %s GS=%x\\n\",__FUNCTION__,read_pci(h->grp1->global_status)) */

struct HIFN_ALLOC
{
unsigned int alloc_map;
unsigned int group[32];
unsigned char *mem;
} hifn_mem;
#define HIFN_BLOCK_SIZE (PAGE_SIZE/(sizeof(unsigned int /* hifn_mem.alloc_map */)*8))

struct xreq
{
struct crypto_request *req;
struct hifn7811cmd *cmdp;
char *resp;
struct timeval in_time;
char hash_len;
struct xreq *next,*prev;
} dummy[3],*to_crypt,*in_hifn,*to_crypt_high;

struct HIFN_DESCRIPTORS
{
struct hifn7811desc cmd[HIFN7811_DESCR_CNT+1];
struct hifn7811desc src[HIFN7811_DESCR_CNT+1];
struct hifn7811desc res[HIFN7811_DESCR_CNT+1];
struct hifn7811desc dst[HIFN7811_DESCR_CNT+1];
};

char hifn_in_use = 0;
struct HIFN_GLOBAL
{
/* Statistics */
volatile unsigned int ints;
unsigned int bh,req,inq,cb,sw,hw,retry; /* Various stats */
unsigned int deepness,max,process_time;
unsigned char dev_dst_index,dev_res_index;
unsigned char normal_in_hifn; /* Number of low-priority in in_hifn queue */
struct timeval usage_time,usage_start,start;


/* Duplicate, for debugging only */
char *hifn_in_use;
struct xreq *to_crypt,*in_hifn;

/* PCI info structs */
struct pci_dev *pci_dev;
volatile struct hifn7811grp0 *grp0;
volatile struct hifn7811grp1 *grp1;
unsigned char myirq;
dma_addr_t pci_ring_base; /* Contains the PCI address of *desc */

/* Descriptors */
volatile struct HIFN_DESCRIPTORS *desc;
volatile struct hifn7811desc *cmdPtr,*srcPtr,*resPtr,*dstPtr;
volatile struct hifn7811desc *resDone,*dstDone;
unsigned char slotin,slotout;

struct timer_list timer;

} hifn;

#if defined(TRACING) && defined(CONFIG_8xx)
#define BIT_ON(bit) set_tracing(get_tracing() | (1<<bit))
#define BIT_OFF(bit) set_tracing(get_tracing() & ~(1<<bit))
#define BIT_MARK(bit) do { char _c = get_tracing(); set_tracing(_c ^ (1<<bit)); set_tracing(_c ^ (1<<bit)); } while(0)

#include <asm/8xx_immap.h>
void tracing_setup(void)
{
volatile immap_t *imp;

imp = (immap_t *)IMAP_ADDR;

imp->im_cpm.cp_pbpar &= ~0xc0; /* Set PB24-25 to I/O */
imp->im_cpm.cp_pbdir |=  0xc0; /* Set PB24-25 to Output */
imp->im_cpm.cp_pbodr &= ~0xc0; /* Set PB24-25 to General output */
}
char get_tracing(void)
{
volatile immap_t *imp;
imp = (immap_t *)IMAP_ADDR;
return (imp->im_cpm.cp_pbdat & 0xc0) >> 6;
}
char set_tracing(char m)
{
volatile immap_t *imp;
char c;
imp = (immap_t *)IMAP_ADDR;
c = m & 0x3;
imp->im_cpm.cp_pbdat = (imp->im_cpm.cp_pbdat & ~0xc0) | (c << 6);
return c;
}
#else
#define BIT_ON(bit)
#define BIT_OFF(bit)
#define BIT_MARK(bit)
#endif

#ifdef PRINT_USAGE
/* Calc arg1 - arg2 */
static inline
struct timeval timeval_diff(struct timeval *arg1, struct timeval *arg2)
{
struct timeval diff;
int carry_sec=0,carry_usec=0;

if(arg2->tv_usec > arg1->tv_usec)
{
carry_sec = -1;
carry_usec = 1000000;
}
diff.tv_sec = arg1->tv_sec + carry_sec - arg2->tv_sec;
diff.tv_usec = arg1->tv_usec + carry_usec - arg2->tv_usec;
return diff;
}

/* Calc arg1 + arg2 */
static inline
struct timeval timeval_add(struct timeval *arg1, struct timeval *arg2)
{
struct timeval add;

add.tv_sec = arg1->tv_sec + arg2->tv_sec;
add.tv_usec = arg1->tv_usec + arg2->tv_usec;
add.tv_sec += add.tv_usec/1000000;
add.tv_usec %= 1000000;
return add;
}

void start_usage(struct HIFN_GLOBAL *h)
{
do_gettimeofday(&h->usage_start);
}

void stop_usage(struct HIFN_GLOBAL *h)
{
struct timeval now,usage;

if(h->usage_start.tv_sec == 0)
return;

do_gettimeofday(&now);
usage = timeval_diff(&now,&h->usage_start);
h->usage_time = timeval_add(&h->usage_time,&usage);

memset(&h->usage_start,0,sizeof(h->usage_start));

}
#else
#define start_usage(h) do { } while(0)
#define stop_usage(h) do { } while(0)
#endif /* PRINT_USAGE */

/* Q prototype */
typedef struct xreq qhead;

void put_queue(qhead *q,struct xreq *item);
struct xreq *get_queue(qhead *q);
inline void re_queue(qhead *q,struct xreq *item);

void put_queue(qhead *q,struct xreq *i)
{
unsigned long flags;

save_flags(flags);
cli();

i->next = q->next;
i->prev = q;
q->next->prev = i;
q->next = i;

restore_flags(flags);
return;
}

struct xreq *get_queue(qhead *q)
{
struct xreq *i;
unsigned long flags;

if(q->prev == q)
{
return NULL;
}

save_flags(flags);
cli();
i = q->prev;
q->prev = i->prev;
i->prev->next = i->next;
restore_flags(flags);

i->next = i->prev = NULL;
return i;
}

inline
void re_queue(qhead *q,struct xreq *item)
{
put_queue(q->prev,item);
}

inline
struct xreq *peek_queue(qhead *q)
{
if(q->prev == q)
return NULL;
return q->prev;
}


/*
Simple alloc functions, to allocate internal HiFn memory.
*/
inline
void *hifn_alloc(int size)
{
int t;
unsigned long pattern;
char blocks;

// printk(\"Alloc %u size\\n\",size);
size = (size + (HIFN_BLOCK_SIZE-1)) & ~(HIFN_BLOCK_SIZE-1); /*Round up */
blocks = size/HIFN_BLOCK_SIZE;
pattern = (1<<blocks) -1;

for(t=0;t<(32-blocks);t++)
{
if((hifn_mem.alloc_map & pattern) == 0)
{
/* Found section with all pattern bits 0 */
hifn_mem.alloc_map |= pattern;
hifn_mem.group[t] = pattern;
//printk(\"map: 0x%x group %u = 0x%x\\n\",hifn_mem.alloc_map,t,pattern);
return (void*) &(hifn_mem.mem[t*HIFN_BLOCK_SIZE]);
}
pattern <<= 1;
}
printk(\"Alloc failed map: 0x%x asking for %u blocks\\n\",hifn_mem.alloc_map,blocks);
return NULL;
}

inline
void hifn_free(void *ptr)
{
int index;

if(ptr == NULL)
return;

index = ((unsigned int) ptr - (unsigned int)hifn_mem.mem)/HIFN_BLOCK_SIZE;
if(index >= 32 || index < 0)
{
printk(KERN_DEBUG \"Free: Bad %x [%x]\\n\",ptr,index);
return;
}
if((hifn_mem.alloc_map & hifn_mem.group[index]) != hifn_mem.group[index])
{
printk(KERN_DEBUG \"Free: Not alloc\'d %x Free-> %x\\n\",hifn_mem.alloc_map,hifn_mem.group[index]);
}

hifn_mem.alloc_map &= ~hifn_mem.group[index];
// printk(\"Free Index %u, pattern 0x%x, map=%x\\n\",index,hifn_mem.group[index],hifn_mem.alloc_map);
hifn_mem.group[index] = 0;
return;

}
#if 1
#define read_pci(a) readl(&(a))
#define write_pci(a,v) do { volatile unsigned int dummy; \\
/* printk(\"Writing %x at %x\\n\",v,a); */ \\
writel(v,a); dummy=readl(a); } while(0)
#else
static inline
u32 read_pci(u32 a)
{
#if 0
u32 r = (((a)>>24) | (((a)>>8)&0xff00) | (((a)<<8)&0xff0000) | ((a)<<24));
rmb();
return r;
#endif
rmb();
return a;
}

static inline
void write_pci(u32 *addr,u32 a)
{
*addr = read_pci(a);
printk(\"Writing %x at %x\\n\",a,addr);
wmb();
}
#endif

static kmem_cache_t     *xreq_cache = NULL;

static inline
struct xreq *hifn_alloc_xreq(void)
{
return kmem_cache_alloc(xreq_cache,GFP_ATOMIC);
}

static inline
void hifn_free_xreq(struct xreq *xreq)
{
kmem_cache_free(xreq_cache,xreq);
}

#ifdef MGTEST
static
void hifn7811_RC4_set_key(RC4_KEY_HW *key, int len, const unsigned char *data)
{
                /* Use key->data as a char array, key->x as the length and key->y
                as a flag to see if the key was changed. */
unsigned char *rc4key;
int t;

memcpy(key->raw,data,len);
key->len = len;

#if 0
rc4key = (unsigned char *)key->data;
for(t=0;t<256;t+=len)
memcpy(rc4key+t,data,(256-t)<len?256-t:len);

rc4key[256] = rc4key[257] = rc4key[258] = rc4key[259] = 0;
key->y = 1;
key->x = 260;
#endif
}
#endif

#define HIFN7811_TEST

#ifdef HIFN7811_TEST
static int bench = 0; /* Set to 1 while loading module to start task */
MODULE_PARM(bench, \"i\");

static int debug = 0;
MODULE_PARM(debug, \"i\");
#define DEBUG(n, args...)                                             \\
        do {                                                          \\
                if (debug>(n))                                     \\
                        printk(KERN_DEBUG \"hifn7811: \" args);      \\
        } while(0)
#else
#define DEBUG(n, args...)
#endif

static void hifn7811_dec_use_count(void)
{
MOD_DEC_USE_COUNT;
}

static void hifn7811_inc_use_count(void)
{
MOD_INC_USE_COUNT;
}


/****************************************************************************/

/*
 *      Hifn 7811 hardware support.
 */
int hifn7811_present;

/*
 * Macro to calculate a relative pointer based on Hifn memory address.
 */
#define HIFN7811_PTR(x) (virt_to_bus(x))
#define HIFN7811_DSOFF(x) (((x) % 8) * sizeof(struct hifn7811desc))


/*
 * If testing or debugging agaisnt a real libdes then use these
 * redefines to rename functions so both can co-exist.
 */

#if defined(HIFN7811_TEST) || defined(MGTEST)
#define lib_des_set_key des_set_key
#define lib_des_cbc_encrypt des_cbc_encrypt
#define lib_des_ede3_cbc_encrypt des_ede3_cbc_encrypt
#else
//#define hifn7811_des_set_key des_set_key
//#define hifn7811_des_cbc_encrypt des_cbc_encrypt
//#define hifn7811_des_ede3_cbc_encrypt des_ede3_cbc_encrypt
#endif

//#define hifn7811_des_ede3_cbc_encrypt des_ede3_cbc_encrypt
//#define hifn7811_des_set_key des_set_key


/****************************************************************************/
/*
 * Fast 32bit based memory set for Hifn. It is actually better
 * (and smaller) to inline this.
 */

void __inline__ *hifn7811_memset (void *s, int c, int len)
{
register volatile unsigned long *sp;

sp = (volatile unsigned long *) s;
len >>= 2;

for (; len; len--)
*sp++ = c;
return (s);
}

/****************************************************************************/

/*
 * An efficent key copy function. We know keys are 8 bytes,
 * so I will just do 2 int copies (better then memcpy for this).
 * This optimizes very nicely...
 */

void __inline__ hifn7811_keycpy (void *dst, void *src)
{
unsigned int *dp = (unsigned int *) dst;
unsigned int *sp = (unsigned int *) src;

dp[0] = sp[0];
dp[1] = sp[1];
}

/****************************************************************************/

/*
 * As per standard DES library functions.
 */

int hifn7811_des_set_key (des_cblock * key, des_key_schedule schedule)
{

DEBUG(2, \"des_set_key(key=%x,schedule=%x) -> key=%08x%08x\\n\",
(int) key, (int) schedule, ((unsigned int *) key)[0], ((unsigned int *) key)[1]);

    /* FIXME: need to check for non-odd parity of key, return -1 */
    /* FIXME: need to check for illegal week key, return -2 */

memcpy (schedule, key, sizeof (*key));
return (0);
}


/****************************************************************************/

/*
 * As per standard DES library functions.
 */

void hifn7811_des_ede3_cbc_encrypt (des_cblock * input, des_cblock * output,
long length, des_key_schedule ks1,
des_key_schedule ks2, des_key_schedule ks3,
des_cblock * ivec, int enc)
{
struct crypto_request *req;

DEBUG(2, \"des_ede3_cbc_encrypt(input=%x,output=%x,length=%d,\"
\"ks1=%x,ks2=%x,ks3=%x,ivec=%x,encrypt=%d)\\n\",
(int) input, (int) output, (int) length,
(int) ks1, (int) ks2, (int) ks3, (int) ivec, enc);

req = crypto_alloc_request(GFP_ATOMIC);
req->op.cmd = (enc ? CRYPT_ENCODE:CRYPT_DECODE) | CRYPT_3DES | CRYPT_CBC;
req->inbuf = (unsigned char *)input;
req->inlen = length;
req->outlen = length;
req->outbuf = (unsigned char *)output;
req->flags = CRYPT_SYNC;
hifn7811_keycpy (&req->op.u.des.key[0], ks1);
hifn7811_keycpy (&req->op.u.des.key[1], ks2);
hifn7811_keycpy (&req->op.u.des.key[2], ks3);
req->op.u.des.ivec = ivec;
req->op.offset = 0;
req->op.len = length;
hw_crypto_request_fnc(req);
crypto_free_request(req);
}


/**************************************************
 Generic H/W crypto request
**************************************************/
int hifn7811_start_xreq (struct xreq *xreq)
{
struct hifn7811cmd *hifncmd;
struct hifn7811comp *hifncomp;
struct hifn7811enc *hifnenc;
struct hifn7811mac *hifnmac;
unsigned char *mp;
int cmdlen, reslen;
unsigned long cmd,cmdenc,cmdcomp,cmdmac;
void *key;
int keysize;
des_cblock * ivec;
struct crypto_request *req;
struct crypto_operation *op,*op_mac=NULL,*op_crypt=NULL;
struct HIFN_GLOBAL *h = &hifn;

h->req++;

FUNC_IN(h);

BIT_ON(0);

xreq->cmdp = NULL;

req = xreq->req;
// Point D
// Point D-B: 115us
// Point D-C: 2.2us

cmd = 0;
cmdenc = 0;
cmdcomp = 0;
cmdmac = 0;
key = NULL;
keysize = 0;
ivec = NULL;
for(op = &req->op; op != NULL; op=op->next)
{
cmd |= ((op->cmd & CRYPT_DECODE) ? HIFN7811_CMD_DECODE:HIFN7811_CMD_ENCODE);
if(op->cmd & (CRYPT_RC4 | CRYPT_3DES))
{
cmd |= HIFN7811_CMD_ENCRYPT;
op_crypt = op;

if(op->cmd & CRYPT_RC4)
{
cmdenc = HIFN7811_ENC_RC4;
key = op->u.rc4.key->raw;
keysize = op->u.rc4.key->len;
if(key != NULL && keysize == 0)
{
// printk(KERN_DEBUG \"Hifn: Key not ready\\n\");
goto no_ressource;
}
}
else
{
cmdenc = HIFN7811_ENC_3DES;
if(op_crypt->cmd & CRYPT_ECB)
cmdenc |= HIFN7811_ENC_ECB;
else if(op_crypt->cmd & CRYPT_CBC)
{
cmdenc |= HIFN7811_ENC_CBC;
ivec = op_crypt->u.des.ivec;
}
key = op_crypt->u.des.key;
keysize = sizeof(op_crypt->u.des.key[0])*3;
}
}
else if(op->cmd & (CRYPT_MD5 | CRYPT_SHA))
{
op_mac = op;
cmd |= HIFN7811_CMD_MAC;
}
}
req->flags |= CRYPT_HARDWARE;
if(!(req->flags & CRYPT_HIGH_PRIORITY))
h->normal_in_hifn++;

reslen = sizeof(*hifncmd) + sizeof (*hifncomp) + sizeof (*hifnenc) + sizeof(*hifnmac) + sizeof(struct hifn7811res);
if (cmd & HIFN7811_CMD_COMP)
reslen += sizeof (struct hifn7811compres);
if (cmd & HIFN7811_CMD_ENCRYPT)
reslen += sizeof (struct hifn7811encres);
if(key)
{
if(cmd & HIFN7811_CMD_ENCRYPT)
{
if(key)
reslen += (cmdenc & HIFN7811_ENC_RC4)?260:24;
if(ivec)
reslen += 8;
}
}
xreq->hash_len = 0;
if((cmd & HIFN7811_CMD_MAC))
{
reslen += sizeof(struct hifn7811macres);
if(op_mac->cmd & CRYPT_96)
{
xreq->hash_len = 96/8;
}
else
{
if(op_mac->cmd & CRYPT_MD5)
{
xreq->hash_len = 16;
}
if(op_mac->cmd & CRYPT_SHA)
{
xreq->hash_len = 20;
}
}
reslen += xreq->hash_len;
if(op_mac->cmd & CRYPT_HMAC)
reslen += 64;
}

reslen += 4; /* For rounding */
if((mp = hifn_alloc(reslen)) == NULL)
goto no_ressource;
xreq->cmdp = (struct hifn7811cmd *)mp;

/* Base command structure. */
hifncmd = (struct hifn7811cmd *)mp;
hifncmd->cmd = cmd;
hifncmd->scount = HIFN7811_SWAPB (req->inlen);
hifncmd->dcount = HIFN7811_SWAPB (req->outlen);
mp += sizeof (*hifncmd);
memset(mp,0,16-sizeof(*hifncmd)); /* Pad with 0s if necessary */

    /* Compression command structure. */
if (cmd & HIFN7811_CMD_COMP)
{
hifncomp = (struct hifn7811comp *) mp;
hifncomp->cmd = cmdcomp;
hifncomp->hdrcnt = HIFN7811_SWAPB (0);
hifncomp->srccnt = HIFN7811_SWAPB (req->inlen);
hifncomp->reserved = 0;
mp += sizeof (*hifncomp);
}

/* Padding would go here... */

    /* Mac command structure. */
if (cmd & HIFN7811_CMD_MAC)
{
unsigned short cmdmac;

cmdmac =  HIFN7811_MAC_AEBD | HIFN7811_MAC_RESULT | HIFN7811_MAC_CNTMODE
| ((op_mac->cmd & CRYPT_MD5)? HIFN7811_MAC_MD5:0)
| ((op_mac->cmd & CRYPT_SHA)? HIFN7811_MAC_SHA:0)
| ((op_mac->cmd & CRYPT_HMAC)? (HIFN7811_MAC_HMAC|HIFN7811_MAC_NEWKEY):HIFN7811_MAC_HASH)
| ((op_mac->cmd & CRYPT_96)? HIFN7811_MAC_TRUNC:0)
| ((op_mac->cmd & CRYPT_APPEND)? HIFN7811_MAC_INSERT:0);

hifnmac = (struct hifn7811mac *) mp;
hifnmac->cmd = cmdmac;
hifnmac->hdrcnt = HIFN7811_SWAPB (op_mac->offset);
hifnmac->srccnt = HIFN7811_SWAPB (op_mac->len);
hifnmac->reserved = 0;
mp += sizeof (*hifnmac);

}
    /* Encryption command structure. */
if (cmd & HIFN7811_CMD_ENCRYPT)
{
hifnenc = (struct hifn7811enc *)mp;
hifnenc->cmd = cmdenc | HIFN7811_ENC_CLEAR
| ((key != NULL)?HIFN7811_ENC_NEWKEY:0)
| ((ivec != NULL)?HIFN7811_ENC_NEWIV:0);
hifnenc->hdrcnt = HIFN7811_SWAPB (op_crypt->offset);
hifnenc->srccnt = HIFN7811_SWAPB (op_crypt->len);
hifnenc->reserved = 0;
mp += sizeof (*hifnenc);
}

if (op_mac != NULL && (op_mac->cmd & CRYPT_HMAC))
{
memset(mp,0,64);
hifn7811_memcpyto (mp, op_mac->u.hash.hmac_key, op_mac->u.hash.hmac_key_len);
mp += 64;
}

if (key)
{
int t;

if(cmdenc & HIFN7811_ENC_RC4)
{
for(t=0;t<256;t+=keysize)
hifn7811_memcpyto(mp + t,key,(256-t)<keysize?256-t:keysize);

mp[256] = mp[257] = mp[258] = mp[259] = 0;
mp += 260;
}
else
{
hifn7811_memcpyto(mp,key,keysize);
mp += keysize;
}

}
if (ivec)
{  
hifn7811_memcpyto (mp, ivec, 8);
mp += 8;
}

// #define PRINT_DESCR(p) printk(#p \" cmd: %x ptr: %x\\n\",h->p##Ptr->cmd,h->p##Ptr->ptr)
#define PRINT_DESCR(p) do { ; } while(0)

//#define HENDIAN HIFN7811_DESC_BE
#define HENDIAN 0


/* Setup Command descriptor */
cmdlen = (unsigned int) mp - (unsigned int) hifncmd; /* Command length including keys and ivec. */
if(cmdlen < 16) cmdlen = 16; /* Minimum cmd length */
h->cmdPtr->cmd = (cmdlen & HIFN7811_DESC_LENMASK) | HIFN7811_DESC_LAST | HENDIAN;
h->cmdPtr->ptr = HIFN7811_PTR (xreq->cmdp);
PRINT_DESCR(cmd);


    /* Setup memory space for the results structure. */
reslen = (reslen - cmdlen + 3)&(~0x3);
mp = (unsigned char *)(((unsigned long)mp+3) & (~0x3));
h->resPtr->cmd = (reslen & HIFN7811_DESC_LENMASK) | HIFN7811_DESC_LAST | HENDIAN;
h->resPtr->ptr = HIFN7811_PTR (mp);
PRINT_DESCR(res);
xreq->resp = (char*)mp;



/* Setup source descriptor */
h->srcPtr->cmd = (req->inlen & HIFN7811_DESC_LENMASK) | HIFN7811_DESC_LAST | HENDIAN;
h->srcPtr->ptr = (unsigned long) pci_map_single(h->pci_dev,req->inbuf,req->inlen,PCI_DMA_TODEVICE);
PRINT_DESCR(src);

    /* Setup destination descriptors */
h->dstPtr->cmd = ((req->outlen+3) & HIFN7811_DESC_LENMASK) | HIFN7811_DESC_LAST | HENDIAN;
h->dstPtr->ptr = (unsigned long) pci_map_single(h->pci_dev,req->outbuf,req->outlen,PCI_DMA_FROMDEVICE);
PRINT_DESCR(dst);

if(h->dstPtr->ptr & 0x3) printk(\"Dst buffer not aligned!\\n\");
if(req->outlen & 0x3) printk(\"Dst len not aligned!\\n\");

put_queue(in_hifn,xreq);

// Point E
// Point E-D, ping -s 1: 27us
SIG_MARK(req);
/* Set valid bit */
h->slotin++;
#ifdef MG
if((h->dstPtr->cmd & HIFN7811_DESC_VALID)
|| (h->dstPtr->cmd & HIFN7811_DESC_VALID)) {
printk(\"Already Valid! In: %u Out: %u\\n\",h->slotin, h->slotout);
}
#endif

#define SET_VALID(f) h->f##Ptr->cmd |= HIFN7811_DESC_VALID
SET_VALID(dst);
SET_VALID(res);
SET_VALID(src);
SET_VALID(cmd);
#undef SET_VALID

HIFN_INC_PTR(h,src,Ptr);
HIFN_INC_PTR(h,cmd,Ptr);
HIFN_INC_PTR(h,res,Ptr);
HIFN_INC_PTR(h,dst,Ptr);

BIT_OFF(0);
BIT_ON(1);
FUNC_OUT(h);
return CRYPT_OK;


no_ressource:
hifn_free(xreq->cmdp);
hifn.retry++;
printk(\"No Ressources\");
FUNC_OUT(h);
return CRYPT_NO_RESSOURCE;
}

static void hifn_dump_register(void);

static
void hifn7811_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
struct HIFN_GLOBAL *h = &hifn;
u32 reg;

if(hifn_in_use == 0)
printk(KERN_ERR \"HIFN Int: Hifh Not in use\\n\");
reg = read_pci(h->grp1->se_status_ctrl);
write_pci(&h->grp1->se_status_ctrl,reg);

if(reg & (STATUS_BITS(RING_RES,CMD_IS_PCI_ABORT)
| STATUS_BITS(RING_CMD,CMD_IS_PCI_ABORT)
| STATUS_BITS(RING_SRC,CMD_IS_PCI_ABORT)
| STATUS_BITS(RING_DST,CMD_IS_PCI_ABORT)
))
{
printk(\"PCI abort reg=%x, Status\\n\",reg);
hifn_dump_register();
return;
}

if(!(reg & STATUS_BITS(RING_RES,CMD_IS_DONE)))
{
struct timeval tm;
do_gettimeofday(&tm);
printk(\"Hifn Int: unexpected interrupt: %x\\n\",reg);
write_pci(&h->grp1->pci_int_reg,0);
printk(\"INT at  %u:%u\\n\",tm.tv_sec,tm.tv_usec);
}

mark_bh(HIFN_BH);
h->ints++;
}

static
void hifn7811_bh(void)
{
struct hifn7811res *res;
int size;
struct xreq *xreq;
struct crypto_request *req;
struct crypto_operation *op;
struct HIFN_GLOBAL *h  = &hifn;
unsigned char *c;
struct timeval now;


BIT_OFF(1);
FUNC_IN(h);
h->bh++;

while(h->slotout != h->slotin
&& !(h->dstDone->cmd & HIFN7811_DESC_VALID)
&& !(h->resDone->cmd & HIFN7811_DESC_VALID))
{
xreq = get_queue(in_hifn);
if(xreq == NULL)
{
printk(KERN_ERR \"HiFnBH: queue empty\\n\");
hifn_in_use = 0;
return;
}

req = xreq->req;
SIG_MARK(req);
h->hw++;
// Point F
// Point E-F: 62us, ping -s 1
SIG_MARK(req);

    /* Get results structure... */
res = (struct hifn7811res *) xreq->resp;
if(res->cmd & HIFN7811_RES_DSTOVERRUN)
{
printk(KERN_ERR \"Hifn: dest Overrun\\n\");
}

res->srccnt = HIFN7811_SWAPB (res->srccnt);
if(res->srccnt != 0)
{
printk(KERN_DEBUG \"HIfn: Src not 0,=%x\\n\",res->srccnt);
}

res->dstcnt = HIFN7811_SWAPB (res->dstcnt);

size = (int) (req->outlen - res->dstcnt);
if (size < 0)
size = 0;
if (size > req->outlen)
size = req->outlen;

/* Clear PCI buffers */
pci_unmap_single(h->pci_dev,h->srcPtr->ptr,req->inlen,PCI_DMA_TODEVICE);
pci_unmap_single(h->pci_dev,h->dstPtr->ptr,req->outlen,PCI_DMA_FROMDEVICE);


/* Get other results */
c = (unsigned char *)xreq->resp;
c += sizeof(struct hifn7811res);
if(xreq->cmdp->cmd & HIFN7811_CMD_COMP)
{
/* Skip compression  */
c+= sizeof(struct hifn7811compres);
}
if(xreq->cmdp->cmd & HIFN7811_CMD_MAC)
{
struct hifn7811macres *mres;

mres = (struct hifn7811macres *)c;
/*
if(mres->cmd & HIFN7811_MACRES_NZERO)
printk(KERN_DEBUG \"HIfn: MAC: src NOT 0\\n\");
*/
c += sizeof(struct hifn7811macres);
for(op = &req->op;op != NULL; op=op->next)
{
if(op->cmd & (CRYPT_MD5|CRYPT_SHA))
{
if(xreq->hash_len != 0)
memcpy(op->u.hash.result, c, xreq->hash_len);
op->u.hash.reslen = xreq->hash_len;
c += xreq->hash_len;
break;
}
}
}

if((xreq->cmdp->cmd & HIFN7811_CMD_ENCRYPT))
{

struct hifn7811encres *eres;

eres = (struct hifn7811macres *)c;
if(eres->cmd & HIFN7811_ENCRES_NZERO)
printk(KERN_DEBUG \"HIfn: Crypt: src NOT 0\\n\");
}

req->outlen = size;
hifn_free(xreq->cmdp);

h->slotout++;
HIFN_INC_PTR(h,dst,Done);
HIFN_INC_PTR(h,res,Done);

hifn_in_use--;
process_hw_crypto(); /* Start a new encryption right away */

do_gettimeofday(&now);
now.tv_usec += (now.tv_sec - xreq->in_time.tv_sec)*1000000;
h->process_time += (now.tv_usec - xreq->in_time.tv_usec);

hifn_free_xreq(xreq);

h->cb++;

// Point G
// Point G-F: 9us, ping -s 1
SIG_OFF(req);
if(!(req->flags & CRYPT_HIGH_PRIORITY))
h->normal_in_hifn--;



if(req->callback != NULL)
{
req->callback(req);
}
else
crypto_free_request(req);

if((h->dstDone->cmd & HIFN7811_DESC_VALID)^ (h->resDone->cmd & HIFN7811_DESC_VALID))
{
mark_bh(HIFN_BH);
}
}
process_hw_crypto();
if(hifn_in_use == 0)
{
stop_usage(h);
}
FUNC_OUT(h);
}

static
int process_hw_crypto(void)
{
struct xreq *xreq,*head;
unsigned long flags;
int err;
struct HIFN_GLOBAL *h = &hifn;

do
{
xreq = head = NULL;
if(peek_queue(to_crypt_high) != NULL)
{
head = to_crypt_high;
}
else if(h->normal_in_hifn < 4)
{
head = to_crypt;
}

// head = to_crypt;

if(head != NULL)
xreq = get_queue(head);

save_flags(flags);
cli();
// Point C
// Point C-B: 120us

if(xreq != NULL)
{
if(hifn_in_use < HIFN7811_DESCR_CNT)
{
hifn_in_use++;

restore_flags(flags);
err = hifn7811_start_xreq(xreq);
switch(err)
{
case CRYPT_OK:
break;
default: /* Error? */
re_queue(head,xreq);
xreq = NULL; /* Will exit */
hifn_in_use--;
break;
}
}
else
{
re_queue(head,xreq);
break;
}
}
restore_flags(flags);
}
while(xreq != NULL);

return CRYPT_OK;
}

static
int hw_crypto_request_fnc(struct crypto_request *req)
{
struct xreq *xreq;
volatile int busy;
struct HIFN_GLOBAL *h;

h = &hifn; /* For debugger */
/* Verify request (debug?)*/
if(req->inlen <0 || req->inlen > 2000
|| req->outlen <0 || req->outlen > 2000)
{
printk(KERN_WARNING \"hw_crypto: Bad Request len In:$%x Out:$%x\\n\",req->inlen,req->outlen);
return CRYPT_ERROR;
}

if(req->flags & CRYPT_SYNC)
{
return CRYPT_ERROR;
}
// Point A
SIG_ON(req);
xreq = hifn_alloc_xreq();
if(xreq == NULL)
return CRYPT_ERROR;

xreq->req = req;
xreq->cmdp = NULL;
do_gettimeofday(&xreq->in_time);


/* Queue request */
if(req->flags & CRYPT_HIGH_PRIORITY)
put_queue(to_crypt_high,xreq);
else
put_queue(to_crypt,xreq);

h->inq++;

if(h->max < (h->inq - h->cb)) h->max = (h->inq - h->cb);

h->deepness += (h->inq - h->cb);

if(hifn_in_use == 0)
{
start_usage(h);
}

// Point B
// Point A-B: 8us

/* start H/W if possible */
process_hw_crypto();
return CRYPT_OK;
}


#define DES_ENCRYPT 1
#define DES_DECRYPT 0

#if LINUX_VERSION_CODE < 0x20300

#ifdef MODULE
#define hifn7811_init init_module
#define hifn7811_cleanup cleanup_module
#endif

#define mod_init_t int  __init
#define mod_exit_t void  

#else
#define mod_init_t static int __init
#define mod_exit_t static void __exit
#endif

#ifdef MGTEST
static void print_result(struct crypto_request *req)
{
int t;
struct crypto_operation *op;

req->inlen = req->inlen < 10 ? req->inlen: 10;
req->outlen = req->outlen < 30 ? req->outlen: 30;

printk(\"HiFn Result in %u jiffies\\n\",jiffies-(long)req->priv);
printk(\"HIFN In: \");
for(t=0;t<req->inlen;t++)
printk(\"%x \",(unsigned char)req->inbuf[t]);
printk(\"\\n\");
printk(\"HIFN Out: \");
for(t=0;t<req->outlen;t++)
printk(\"%x \",(unsigned char)req->outbuf[t]);
printk(\"\\n\");

kfree(req->inbuf);
kfree(req->outbuf);
for(op=&req->op; op != NULL; op = op->next)
{
switch(op->cmd)
{
case CRYPT_ENCODE | CRYPT_RC4:
kfree(op->u.rc4.key);
break;
case CRYPT_ENCODE | CRYPT_3DES:
if(op->u.des.ivec)
kfree(op->u.des.ivec);
break;
default:
if(op->cmd & (CRYPT_MD5 | CRYPT_SHA))
{
printk(\"HIFN MAC: \");
for(t=0;t<op->u.hash.reslen;t++)
printk(\"%x \",(unsigned char)op->u.hash.result[t]);
printk(\"\\n\");
}
break;
}
if(op != &req->op)
kfree(op);
}
crypto_free_request(req);
}


#undef SYNC
static void irc4(char *key,char *data,int len)
{
struct crypto_request *req;

req = crypto_alloc_request(GFP_KERNEL);
req->op.cmd = CRYPT_ENCODE | CRYPT_RC4;
req->op.offset = 0;
req->op.len = len;
req->inbuf = kmalloc(len,GFP_KERNEL);
memcpy(req->inbuf,data,len);
req->inlen = len;
req->outlen = len;
req->outbuf = kmalloc(len,GFP_KERNEL);
memset(req->outbuf,0,len);
#ifdef SYNC
req->flags = CRYPT_SYNC;
#else
req->flags = 0;
#endif
req->op.u.rc4.key = kmalloc(sizeof(*(req->op.u.rc4.key)),GFP_KERNEL);
hifn7811_RC4_set_key(req->op.u.rc4.key, strlen(key),key);
#ifndef SYNC
req->callback = print_result;
req->priv = (void*) jiffies;
#endif
crypto_execute(req);
#ifdef SYNC
print_result(req);
#endif
}

//#define DES3_TEST CRYPT_ECB
#define DES3_TEST CRYPT_CBC
static void ides(char key[3][8],char ivec[8],char *data,int len)
{
struct crypto_request *req;
struct crypto_operation *op;
int t;

req = crypto_alloc_request(GFP_KERNEL);
req->op.cmd = CRYPT_ENCODE | CRYPT_3DES | DES3_TEST;
req->op.offset = 0;
req->op.len = len;
req->inbuf = kmalloc(len,GFP_KERNEL);
memcpy(req->inbuf,data,len);
req->inlen = len;
req->outlen = len+20;
req->outbuf = kmalloc(len+20,GFP_KERNEL);
memset(req->outbuf,0,len+20);
#ifdef SYNC
req->flags = CRYPT_SYNC;
#else
req->flags = 0;
#endif
for(t=0;t<3;t++)
{
hifn7811_keycpy(&req->op.u.des.key[t],key[t]);
}

#if DES3_TEST == CRYPT_CBC
req->op.u.des.ivec = kmalloc(sizeof(*(req->op.u.des.ivec)),GFP_KERNEL);
hifn7811_keycpy(req->op.u.des.ivec,ivec);
#else
req->op.u.des.ivec = NULL;
#endif
req->op.offset = 0;
#ifdef MG
/* Append MD5 hash to output */
req->op.next = kmalloc(sizeof(*op),GFP_KERNEL);
op = req->op.next;
op->offset = 0;
op->len = len;
op->cmd = CRYPT_MD5  /* | CRYPT_APPEND */;
op->next = NULL;
#endif

#ifndef SYNC
req->callback = print_result;
req->priv = (void*) jiffies;
#endif
crypto_execute(req);
#ifdef SYNC
print_result(req);
#endif
}
static void imac(char *data,int len)
{
struct crypto_request *req;
struct crypto_operation *op;
int t;

req = crypto_alloc_request(GFP_KERNEL);
req->op.cmd = CRYPT_ENCODE | CRYPT_MD5;
req->op.offset = 0;
req->op.len = len;
req->inbuf = kmalloc(len,GFP_KERNEL);
memcpy(req->inbuf,data,len);
req->inlen = len;
req->outlen = len+20;
req->outbuf = kmalloc(len+20,GFP_KERNEL);
memset(req->outbuf,0,len+20);
#ifdef SYNC
req->flags = CRYPT_SYNC;
#else
req->flags = 0;
#endif
req->op.offset = 0;
#ifndef SYNC
req->callback = print_result;
req->priv = (void*) jiffies;
#endif
crypto_execute(req);
#ifdef SYNC
print_result(req);
#endif
}


static
void test_interrupt(void)
{
#ifdef MG
struct crypto_request *req;
char *inbuf,*outbuf,*key;
int t;

put_queue(to_crypt,(void*)1);
put_queue(to_crypt,(void*)2);
put_queue(to_crypt,(void*)3);
printk(\"Q: %x\\n\",(unsigned int)get_queue(to_crypt));
printk(\"Q: %x\\n\",(unsigned int)get_queue(to_crypt));
printk(\"Q: %x\\n\",(unsigned int)get_queue(to_crypt));
printk(\"Q: %x\\n\",(unsigned int)get_queue(to_crypt));

put_queue(to_crypt,(void*)0xa);
put_queue(to_crypt,(void*)0xb);
put_queue(to_crypt,(void*)0xc);
printk(\"Q: %x\\n\",(unsigned int)get_queue(to_crypt));
printk(\"Q: %x\\n\",(unsigned int)get_queue(to_crypt));
printk(\"Q: %x\\n\",(unsigned int)get_queue(to_crypt));
printk(\"Q: %x\\n\",(unsigned int)get_queue(to_crypt));
printk(\"Q: %x\\n\",(unsigned int)get_queue(to_crypt));
#endif

#define DATA(a) a,sizeof(a)-1
//irc4(\"\\x01\\x23\\x45\\x67\\x89\\xab\\xcd\\xef\",DATA(\"\\x01\\x23\\x45\\x67\\x89\\xab\\xcd\\xef\"));
irc4(\"\\x01\\xDD\\x45\\xDD\\x89\\xab\\xcd\\xef\",DATA(\"\\xda\\xda\\x00\\x00\\xda\\xda\\x00\\x00\"));
//irc4(\"\\xef\\x01\\x23\\x45\",DATA(\"\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"));
// imac(DATA(\"\\x01\\x23\\x45\\x67\\x89\\xab\\xcd\\xef\"));
do {
char keys[3][8] = {
\"\\x01\\x23\\x45\\x67\\x89\\xab\\xcd\\xef\",
\"\\x23\\x45\\x67\\x89\\xab\\xcd\\xef\\x01\",
\"\\x45\\x67\\x89\\xab\\xcd\\xef\\x01\\x23\"
};
ides(keys,\"\\x12\\x34\\x56\\x78\\x90\\xab\\xcd\\xef\",DATA(\"\\x4e\\x6f\\x77\\x20\\x69\\x73\\x20\\x74\"));
}
while(0);
imac(DATA(\"\\x01\\x23\\x45\\x67\\x89\\xab\\xcd\\xef\"));
#ifdef MG
inbuf = kmalloc(1024,GFP_KERNEL);
outbuf = kmalloc(1024,GFP_KERNEL);
key = kmalloc(280,GFP_KERNEL);
for(t=0;t<5;t++)
{
req = kmalloc(sizeof(struct crypto_request),GFP_KERNEL);
req->op.cmd = CRYPT_ENCODE | CRYPT_RC4;
req->inbuf = inbuf;
req->inlen = 1024;
req->outlen = 1024;
req->outbuf = outbuf;
req->flags = 0;
req->op.u.rc4.key = key;
req->callback = NULL;
hw_crypto_request(req);
}
kfree(inbuf);
kfree(outbuf);
kfree(key);
#endif
}
#endif // MGTEST

static struct proc_dir_entry *proc_hifn;


static
void hifn_dump_register(void)
{

int t;

printk(\"SE ctrl: %x\\n\",read_pci(hifn.grp0->se_control));
printk( \"SE CTRL:%x MIPS_CFG:%x GPDMA src:%x\\n\",read_pci(hifn.grp1->se_status_ctrl),read_pci(hifn.grp1->mips_config),read_pci(hifn.grp1->GPDMA1_src));
#define ARG(c) hifn.desc->c[t].cmd,hifn.desc->c[t].ptr
for(t=0;t<9;t++)
printk(\"%u: Cmd:%08x/%08x Src:%08x/%08x Dst:%08x/%08x  Res:%08x/%08x  \\n\",t,ARG(cmd),ARG(src),ARG(dst),ARG(res));
#undef ARG

}



static int hifn_read_proc ( char *page, char **start, off_t off,int count
,int *eof, void *data_unused
)
{
int len = 0;
off_t   begin = 0;
int t;

len = sprintf(page + len, \"InQ Req Ints BH CB Retry H/W S/W In_Use\\n\");
len += sprintf(page + len, \"%u %u %u %u %u %u %u %u %u\\n\",hifn.inq,hifn.req,hifn.ints,hifn.bh,hifn.cb,hifn.retry,hifn.hw,hifn.sw,hifn_in_use);
len += sprintf(page + len, \"Avg deepness %u/%u, Max: %u  Ptime:%uus\\n\",hifn.deepness,hifn.inq,hifn.max,hifn.cb?hifn.process_time/hifn.cb:0);

len += sprintf(page + len, \"SE ctrl: %x\\n\",read_pci(hifn.grp0->se_control));
len += sprintf(page + len, \"SE CTRL:%x MIPS_CFG:%x GPDMA src:%x\\n\",read_pci(hifn.grp1->se_status_ctrl),read_pci(hifn.grp1->mips_config),read_pci(hifn.grp1->GPDMA1_src));
len += sprintf(page + len, \"GlobalSt:%x seDMA:%x Grp0Stat:%x\\n\",read_pci(hifn.grp1->global_status),read_pci(hifn.grp1->se_dma_config),read_pci(hifn.grp0->se_status));
len += sprintf(page + len, \"SlotIn:%x Out:%x AllocMap:%x Addr:%x\\n\",hifn.slotin,hifn.slotout,hifn_mem.alloc_map,hifn_mem.mem);

for(t=0;t<9;t++)
len += sprintf(page + len, \"%u: Cmd:%08x Src:%08x Dst:%08x Res:%08x \\n\",t,hifn.desc->cmd[t].cmd,hifn.desc->src[t].cmd,hifn.desc->dst[t].cmd,hifn.desc->res[t].cmd);

*eof = 1;
/*
if (off >= len+begin)
return 0;
*start = page + (begin-off);
return ((count < begin+len-off) ? count : begin+len-off);
*/
if(len > PAGE_SIZE)
printk(\"proc: len > 4096\\n\");
return len;
}

static
int hifn_write_proc(struct file *file, const char *buffer,unsigned long count, void *data)
{
int t;

switch(*buffer)
{
case \'A\':
always = buffer[1]-\'0\';
break;
case \'D\':
disable = buffer[1]-\'0\';
hifn7811_present = disable;
break;
case \'T\':
#ifdef MGTEST
t=1;
if(buffer[1] > \'0\' && buffer[1] <= \'9\')
t = buffer[1]-\'0\';

while(t-->0)
test_interrupt();

if(0)
{
int t;
struct timeval tm;
write_pci(&hifn.grp1->pci_ier,1);
do_gettimeofday(&tm);
write_pci(&hifn.grp1->pci_int_reg,1);

for(t=0;t<0x100000;t++);
printk(\"INT done, started %u:%u\\n\",tm.tv_sec,tm.tv_usec);
}
#endif
break;
}
return count;
}



//#ifdef __BIG_ENDIAN
//#define HIFN7811_ENDIAN HIFN7811_DESC_BE
//#else
#define HIFN7811_ENDIAN 0
//#endif
void hifn7811_setup(struct HIFN_GLOBAL *h)
{
struct hifn7811grp0 *grp0 = h->grp0;
struct hifn7811grp1 *grp1 = h->grp1;
int t;

/****** Setup *******/
write_pci(&grp0->se_isr,0xffffffff);
write_pci(&grp0->se_config, SE_CONFIG_DEFAULT);
write_pci(&grp0->se_fifo_config, SE_FIFO_CONFIG_DEFAULT);
write_pci(&grp0->se_ier, 0); /* Disable all interrupts */

/* Setup rings */
h->desc = pci_alloc_consistent(h->pci_dev,PAGE_SIZE,&h->pci_ring_base);
#define INIT_DESC(type) h->desc->type[t].cmd = HIFN7811_ENDIAN | HIFN7811_DESC_LAST; \\
h->desc->type[t].ptr = 0
for(t=0;t<HIFN7811_DESCR_CNT;t++)
{
INIT_DESC(cmd);
INIT_DESC(src);
INIT_DESC(res);
INIT_DESC(dst);
}
#undef INIT_DESC

#define DO_JUMP(f)  h->desc->f[t].cmd = HIFN7811_ENDIAN | HIFN7811_DESC_NOVALID \\
| HIFN7811_DESC_VALID | HIFN7811_DESC_JUMP; \\
h->desc->f[t].ptr = virt_to_bus(&h->desc->f[0]); h->f##Ptr = &h->desc->f[0]
DO_JUMP(cmd);
DO_JUMP(src);
DO_JUMP(dst);
DO_JUMP(res);
#undef DO_JUMP
#define SET_RING_REG(f) write_pci(&grp1->se_##f.reg,virt_to_bus(&h->desc->f[0]))
SET_RING_REG(dst);
SET_RING_REG(src);
SET_RING_REG(res);
SET_RING_REG(cmd);
#undef SET_RING_REG
h->dstDone = h->dstPtr;
h->resDone = h->resPtr;

/* Setup memory for command and result structure */
hifn_mem.mem = (unsigned char *)h->desc;
/* Block 0/1/2 are reseved for descriptors */
hifn_mem.alloc_map = (1<<((sizeof(struct HIFN_DESCRIPTORS)+HIFN_BLOCK_SIZE-1) & ~(HIFN_BLOCK_SIZE-1))/HIFN_BLOCK_SIZE)-1;
printk(\"Sizeof HIFN_BLOCK_SIZE %u, %x->%x, map=%x\\n\",HIFN_BLOCK_SIZE,hifn_mem.mem,hifn_mem.mem+PAGE_SIZE,hifn_mem.alloc_map);


/* Setup DMA config */
#ifdef __BIG_ENDIAN
write_pci(&grp1->se_dma_config,  SE_INBOUND_BE | SE_OUTBOUND_BE | SE_POLL_FREQ(1) | SE_POLL_SCALE(1));
#else
write_pci(&grp1->se_dma_config,  SE_POLL_FREQ(3) | SE_POLL_SCALE(3));
#endif

/* No RNG needed... */

/* No MIPS stuff needed. */

/* No GPRAM, no GPDMA */

/*********** Enable *************/
/* Enable  DMA, Interrupts */

write_pci(&grp1->se_status_ctrl, STATUS_BITS(RING_DST,CMD_ENABLE)
| STATUS_BITS(RING_RES,CMD_ENABLE)
| STATUS_BITS(RING_CMD,CMD_ENABLE)
| STATUS_BITS(RING_SRC,CMD_ENABLE));

write_pci(&grp1->se_ier, STATUS_BITS(RING_RES,CMD_IS_DONE)
| STATUS_BITS(RING_CMD,CMD_IS_PCI_ABORT)
| STATUS_BITS(RING_SRC,CMD_IS_PCI_ABORT)
| STATUS_BITS(RING_DST,CMD_IS_PCI_ABORT)
| STATUS_BITS(RING_RES,CMD_IS_PCI_ABORT)
);
}

#ifdef PRINT_USAGE
void hifn_sample(unsigned long arg)
{
struct HIFN_GLOBAL *h = (struct HIFN_GLOBAL *)arg;
struct timeval now;
unsigned int ttl,usage;
int was_running;

do_gettimeofday(&now);

if(h->usage_start.tv_sec != 0)
{
stop_usage(h);
was_running = 1;
}
else
was_running = 0;

usage = (h->usage_time.tv_sec)*1000000 + h->usage_time.tv_usec;
ttl = (now.tv_sec - h->start.tv_sec)*1000000 + (now.tv_usec - h->start.tv_usec);
/*
printk(\"Hifn: Usage time: %u:%uus Total time %uus\\n\",
(h->usage_time.tv_sec), h->usage_time.tv_usec,
(now.tv_sec - h->start.tv_sec)*1000000 + (now.tv_usec - h->start.tv_usec));
*/
printk(\"Hifn Occ: %3u.%u%%\\n\",100*usage/ttl,(1000*usage/ttl) %10);
h->start = now;
memset(&h->usage_time,0,sizeof(h->usage_time));
if(was_running)
start_usage(h);

mod_timer(&h->timer,jiffies+HZ);
}
#endif

static
struct crypto_engine_def hifn7811_def =
{
name : \"HiFn7811\",
capability: (CRYPT_RC4|CRYPT_DES|CRYPT_3DES|CRYPT_MD5|CRYPT_SHA),
attributes: IS_HARDWARE | IS_ASYNC,
request_handler: hw_crypto_request_fnc,
open: hifn7811_inc_use_count,
close: hifn7811_dec_use_count
};


mod_init_t hifn7811_init(void)
{
int i;
struct HIFN_GLOBAL *h = &hifn; /* To debug modules...*/

#if 0
hifn7811_diags ();
#endif

#ifdef TRACING
tracing_setup();

set_tracing(0);
set_tracing(0);
set_tracing(1);
set_tracing(1);
set_tracing(2);
set_tracing(2);
set_tracing(3);
set_tracing(3);
set_tracing(0);
set_tracing(0);
#endif

printk (\"HIFN7811: cryptographic accelerator \");
if (!disable)
{
h->pci_dev = pci_find_device(HIFN_PCI_VENDOR_ID,HIFN_PCI_DEVICE_ID,NULL);
if(h->pci_dev != NULL)
{
int err;

printk(\"Found!\\n\");
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
pci_enable_device(h->pci_dev);
#endif
h->grp0 = h->pci_dev->resource[0].start;
h->grp1 = h->pci_dev->resource[1].start;
printk(KERN_DEBUG \"GRP0:%x GRP1:%x\\n\",h->grp0,h->grp1);
pci_set_master(h->pci_dev);
h->grp0 = ioremap_nocache((unsigned long)h->grp0,1024*4);
h->grp1 = ioremap_nocache((unsigned long)h->grp1,1024*4);
/*
h->grp0 = ioremap((unsigned long)h->grp0,1024*4);
h->grp1 = ioremap((unsigned long)h->grp1,1024*4);
*/
printk(KERN_DEBUG \"Remap GRP0:%x GRP1:%x\\n\",h->grp0,h->grp1);
hifn7811_present = 1;
printk (\"PRESENT, Rev=%x\\n\",readl(&h->grp1->revision_number));
}
else
{
printk (\"NOT present\\n\");
}
}
else {
printk(\"DISABLED\\n\");
}

printk (\"HIFN7811: Copyright (C) 2000, Lineo (www.lineo.com)\\n\");
printk (\"HIFN7811: Copyright (C) 2001, Colubris Networks\\n\");
#ifdef MG
{
      unsigned long vaddr = (unsigned long) &h->grp0->se_control;
      pgd_t *pgd;
      pmd_t *pmd;
      pte_t *pte, old_pte;

           pgd = swapper_pg_dir + __pgd_offset(vaddr);
           pmd = pmd_offset(pgd, vaddr);
           pte = pte_offset(pmd, vaddr);
  printk(\"Value of PTE for se-control: %x\\n\",pte_val(*pte));
          old_pte = *pte;
}
#endif  
if (hifn7811_present)
{
/* Reset the hifn chip */
printk(\"Resetting board...\\n\");

// write_pci(&h->grp1->GPDMA1_src,0x12345678);
// printk(\"Wrote 0x12345678 == 0x%x\\n\",read_pci(h->grp1->GPDMA1_src));

// printk(\"Reset Failed! Wrote:%x Read:%x\\n\",SE_CONTROL_RESET|SE_CONTROL_MUST_BIT,read_pci(h->grp0->se_control));
// write_pci(&h->grp0->se_control,SE_CONTROL_RESET|SE_CONTROL_MUST_BIT);
// writel(SE_CONTROL_RESET|SE_CONTROL_MUST_BIT,&h->grp0->se_control);
write_pci(&h->grp0->se_control,0xffffffff);
write_pci(&h->grp1->se_dma_config,SE_MSTRRESET);
// h->grp0->se_control = SE_CONTROL_RESET|SE_CONTROL_MUST_BIT;
// barrier();
// printk(\"A1\\n\");
for (i = 0; (i < 100); i++)
{
udelay(10000);
if ( (read_pci(h->grp0->se_control)&SE_CONTROL_RESET) == 0)
break;
}
// printk(\"Does reset work? is 0x12345678 == 0x%x\\n\",read_pci(h->grp1->GPDMA1_src));
if(i == 100)
{
printk(\"Reset Failed! Wrote:%x Read:%x\\n\",SE_CONTROL_RESET|SE_CONTROL_MUST_BIT,read_pci(h->grp0->se_control));
printk(\"Is endian ok? does 0x4040 == 0x%x\\n\",read_pci(h->grp0->se_fifo_status));
printk(\"Is endian ok? does 0x40000000 == 0x%x\\n\",read_pci(h->grp1->mips_pci1_addr));
/*
iounmap(h->grp0);
iounmap(h->grp1);
return -1;
*/
}
else
printk(\"Reset ok: %u\\n\",i);


if ((proc_hifn = create_proc_entry( \"hifn\", 0, 0 )))
{
proc_hifn->read_proc = hifn_read_proc;
proc_hifn->write_proc = hifn_write_proc;

}

/* Reset all events */
hifn7811_setup(h);

xreq_cache = kmem_cache_create(\"hifn_xreq\",sizeof(struct xreq),
0, SLAB_HWCACHE_ALIGN, NULL, NULL);

/* Install interrupt request */
pci_read_config_byte(h->pci_dev,PCI_INTERRUPT_LINE,&(h->myirq));
printk(KERN_DEBUG \" Using IRQ %d\\n\",h->myirq);
request_irq(h->myirq,hifn7811_interrupt,0,\"hifn7811\",NULL);
// request_8xxirq(h->myirq,hifn7811_interrupt,0,\"hifn7811\",NULL);

/* Setup BH */

init_bh(HIFN_BH,hifn7811_bh);

to_crypt=&dummy[0];
to_crypt->next = to_crypt->prev = to_crypt;
in_hifn=&dummy[1];
in_hifn->next = in_hifn->prev = in_hifn;
to_crypt_high=&dummy[2];
to_crypt_high->next = to_crypt_high->prev = to_crypt_high;

hifn.to_crypt = to_crypt;
hifn.in_hifn = in_hifn;
hifn.hifn_in_use = &hifn_in_use;
#ifdef MGTEST
printk(\"SW only\\n\");
test_interrupt();
#endif
crypto_register_engine(&hifn7811_def);
init_timer(&h->timer);
h->timer.expires=jiffies+HZ;
h->timer.data = h;
h->timer.function = hifn_sample;
add_timer(&h->timer);
}

return 0;
}


#ifdef MODULE
mod_exit_t hifn7811_cleanup (void)
{
int i;
struct HIFN_GLOBAL *h = &hifn; /* To debug modules...*/

DEBUG(0, \"Unloading module hifn7811\\n\");
write_pci(&h->grp0->se_control,SE_CONTROL_RESET|SE_CONTROL_MUST_BIT);
for (i = 0; (i < 0x100000); i++)
{
if ( (h->grp0->se_control&SE_CONTROL_RESET) == 0)
break;
}
del_timer(&h->timer);
iounmap(h->grp0);
iounmap(h->grp1);
if(h->pci_dev != NULL && h->desc != NULL)
pci_free_consistent(h->pci_dev,PAGE_SIZE,(void*)h->desc,h->pci_ring_base);
free_irq(hifn.myirq,NULL);
remove_proc_entry(\"hifn\",NULL);
if(xreq_cache != NULL)
kmem_cache_destroy(xreq_cache);
xreq_cache = NULL;
crypto_unregister_engine(hifn7811_def.name);
}
#endif

#if LINUX_VERSION_CODE > 0x20300
module_init(hifn7811_init);
module_exit(hifn7811_cleanup);
#endif
111
sirroom
驱动大牛
驱动大牛
  • 注册日期2001-07-30
  • 最后登录2018-05-29
  • 粉丝0
  • 关注0
  • 积分6分
  • 威望11点
  • 贡献值1点
  • 好评度0点
  • 原创分0分
  • 专家分0分
板凳#
发布于:2002-06-21 13:20
为了关税,把头文件也加上,只是,偶不是用这卡
/****************************************************************************/

/*
 * hifn7811.h   -- Definitions for Hi/fn 7811 encryption engine.
 *
 *      (C) Copyright 2000, Lineo, Inc.  (www.lineo.com)
 * (C) Copyright 2000, Greg Ungerer (gerg@lineo.com)
 *
 * This program is a part of the Linux kernel, and may be freely
 * copied under the terms of the GNU General Public License (GPL),
 * version 2, or at your option any later version.
 */

/****************************************************************************/
#ifndef _HIFN7811_H_
#define _HIFN7811_H_
/****************************************************************************/

#define HIFN_PCI_VENDOR_ID 0x13a3 /* HIFN */
#define HIFN_PCI_DEVICE_ID 0x7 /* 7811 */

/*
 * Address offsets in 7811 memory mapping.
 */
#define HIFN7811_REGBASE 0 /* Register Base Address */

#define HIFN7811_MEMBASE 0x2000 /* Memory Base Address */
#define HIFN7811_CMDD 0x2000 /* Command Descriptors */
#define HIFN7811_SRCD 0x2040 /* Source Decsriptors */
#define HIFN7811_RESD 0x2080 /* Result Descriptors */
#define HIFN7811_DSTD 0x20c0 /* Destination Descriptors */
#define HIFN7811_PKTDATA 0x2100 /* Packet data */
#define HIFN7811_MEMMAX 0x4000 /* Maximum address offset */

#define HIFN7811_MEMSIZE 0x2000 /* Size of internal memory */

/*
 * Register offsets within register table.
 */
#define HIFN7811_GC 0x0004 /* General Configuration */
#define HIFN7811_ID 0x0008 /* Chip ID */

#define HIFN7811_HCI 0x0080 /* Host Command Index */
#define HIFN7811_HSI 0x0084 /* Host Source Index */
#define HIFN7811_HRI 0x0088 /* Host Result Index */
#define HIFN7811_HDI 0x008c /* Host Destination Index */
#define HIFN7811_PKTS 0x0090 /* Packet Status */
#define HIFN7811_PKTIE 0x0094 /* Packet Interrupt Enable */
#define HIFN7811_PKTC 0x0098 /* Packet Configuration */

#define HIFN7811_PBA 0x0100 /* Public Base Address */
#define HIFN7811_POL 0x0104 /* Public Operand Length */
#define HIFN7811_PO 0x0108 /* Public Operand */
#define HIFN7811_PS 0x010c /* Public Status */
#define HIFN7811_PIE 0x0110 /* Public Interrupt Enable */
#define HIFN7811_RNGC 0x0114 /* RNG Configuration */
#define HIFN7811_RNGD 0x0118 /* RNG Data */

#define HIFN7811_PKTS_DLAST(x) (((x) >> 24) & 0x1)
#define HIFN7811_PKTS_DINDEX(x) (((x) >> 20) & 0xf)
#define HIFN7811_PKTS_RINDEX(x) (((x) >> 14) & 0xf)
#define HIFN7811_PKTS_SINDEX(x) (((x) >> 8) & 0xf)
#define HIFN7811_PKTS_CINDEX(x) (((x) >> 2) & 0xf)

/*
 * Defines to support the Security Engine Control register.
 */


/*
 * Defines to support Chip ID register.
 */
#define HIFN7811_ID_PRODREV 0x00000800 /* Product/Revision ID 7811 */


/*
 * Define Random Number Generator Configuration flags.
 */
#define HIFN7811_RNGC_ENABLE 0x00000001 /* Enable RNG */


#define HIFN7811_DESCR_CNT (8)
//#define LE32(a) (((a)>>24) | (((a)>>8)&0xff00) | (((a)<<8)&0xff0000) | ((a)<<24))
#define LE32(a) a

/***************************************
   GROUP 0 Registers definition
***************************************/
enum grp0_se_control {
SE_CONTROL_MUST_BIT = LE32(0x2),
SE_CONTROL_RESET = LE32(0x1)
};

/* This works for ISR and IER */
enum grp0_se_interrupts {
SE_INVCMD = LE32(0x8000),
SE_DATAERR = LE32(0x4000),
SE_SRC_FIFO_READY = LE32(0x2000),
SE_DST_FIFO_READY = LE32(0x1000),
SE_DST_OVERRUN = LE32(0x0200),
SE_SRC_CMD = LE32(0x0080),
SE_SRC_CONTEXT = LE32(0x0040),
SE_SRC_DATA = LE32(0x0020),
SE_DST_DATA = LE32(0x0010),
SE_DST_RESULT = LE32(0x0004)
};

enum grp0_se_config {
SE_CONFIG_DEFAULT = LE32(0x340),
SE_CONFIG_COMP_CFG=LE32(0x344),
SE_CONFIG_ENCRYPT_CFG=LE32(0x342)
};

#define SE_FIFO_CONFIG_DEFAULT LE32(0x00000400)

/* Group 0 PCI register set */
struct hifn7811grp0 {
u32 se_data;
u32   se_control;
u32 se_isr; /* Interrupt Status Reg */

u32 se_config;
u32 se_ier; /* Interrupt Enable Reg */
u32 se_status;
u32 se_fifo_status;
u32 se_fifo_config;
};


/*****************************************
 GROUP 1 Register definition
******************************************/

/* Group 1 PCI registers */
#define SE_RING_MASK LE32(0xfffffffc)
enum grp1_se_status_ctrl {
/* See STATUS_BITS macro as well */
SE_ILLEGAL_WRITE= LE32(0x200),
SE_ILLEGAL_READ= LE32(0x100),
SE_ENGINE_IRQ= LE32(0x001)

/* Rings */
#define RING_DST 3
#define RING_SRC 1
#define RING_CMD 0
#define RING_RES 2

/* cmd */
#define CMD_ENABLE 0x80
#define CMD_DISABLE 0x40
#define CMD_IS_PCI_ABORT 0x20
#define CMD_IS_LAST 0x08
#define CMD_IS_DONE 0x10
#define CMD_IS_WAITING 0x04
#define CMD_IS_OVER 0x02 /* For DST and RES ring only */
#define STATUS_BITS(ring,cmd) LE32(((cmd) << ((ring)*8))  ) /* USE ME!! */
};

/* DMA configuration */
enum grp1_dma_config {
SE_INBOUND_BE= LE32(0x20000003),
SE_OUTBOUND_BE= LE32(0x10000003),
SE_DST_ADDR_MIPS= LE32(0x08000003),
SE_RES_ADDR_MIPS= LE32(0x04000003),
SE_SRC_ADDR_MIPS= LE32(0x02000003),
SE_CMD_ADDR_MIPS= LE32(0x01000003),
#define SE_POLL_FREQ(freq) LE32(((((unsigned char)freq) << 16) | 0x3))
SE_GPRAM_BE= LE32(0x00008003),
/* ProtMem left as exercise */
#define SE_POLL_SCALE(sc) LE32(((((sc) & 0x7) << 8) | 0x3))
SE_SW_LAST= LE32(0x00000013),
SE_DMARESET= LE32(0x00000001),
SE_MSTRRESET= LE32(0x00000002)
};

/* Random Number Generator */
#define RNG_ENABLE LE32(0x1)
#define RNG_DISABLE LE32(0x0)

#define RNG_PRESCALE(a) LE32((((unsigned char)(a)) << 8))

#define RNG_READY LE32(0x4000)
#define RNG_UNDERFLOW LE32(0x1000)

/* No MIPS register . Today\'s friday. (and not required...) */
#define MIPS_CFG_SE_PCI_INTS LE32(0x4000)

/* No GPRAM registers. Idem. */

enum grp1_global_status {
GL_SE_DST_DONE= LE32(0x80000000),
GL_SE_DST_WAITING= LE32(0x40000000),
GL_SE_RES_DONE= LE32(0x20000000),
GL_SE_RES_WAITING= LE32(0x10000000),
GL_SE_SRC_DONE= LE32(0x08000000),
GL_SE_SRC_WAITING= LE32(0x04000000),
GL_SE_CMD_DONE= LE32(0x02000000),
GL_SE_CMD_WAITING= LE32(0x01000000),
/* DO the GPDMA stuff..............later (ie: never) */
GL_ABORT_OVERFLOW= LE32(0x00000080),
GL_MIPS_RW_ERROR= LE32(0x00000040),
GL_PIPELINE_INT= LE32(0x00000020),
GL_PCI_INTERRUPT= LE32(0x00000010),
GL_MIPS_INTERRUPT= LE32(0x00000008),
GL_SE_INTERRUPT= LE32(0x00000004),
GL_GPDMA3_4_INT= LE32(0x00000002),
GL_GPDMA1_2_INT= LE32(0X00000001)
};

struct hifn7811_ring {
u32 dummy[3];
u32 reg;
};

#define se_cmd_addr se_cmd.reg
#define se_src_addr se_src.reg
#define se_res_addr se_res.reg
#define se_dst_addr se_dst.reg

struct hifn7811grp1 {
struct hifn7811_ring se_cmd;
struct hifn7811_ring se_src;
struct hifn7811_ring se_res;
struct hifn7811_ring se_dst;
u32 se_status_ctrl;
u32 se_ier; /* Interrupt Enable Reg */
u32 se_dma_config;
u32 pci_addr_or_mask;
u32 pci_int_reg;
u32 pci_ier;
u32 mips_int_reg;
u32 mips_imr; /* Interrupt Mask register */
u32 rng_enable;
u32 rng_config;
u32 rng_data;
u32 rng_status;
u32 mips_SDRAM1_addr;
u32 mips_SDRAM2_addr;
u32 mips_Group1_addr;
u32 mips_Group0_addr;
u32 mips_pci1_addr;
u32 mips_pci2_addr;
u32 mips_pci1_tran;
u32 mips_pci2_tran;
u32 mips_config;
u32 mips_reset;
u32 revision_number;
u32 EEPROM_data;
u32 GPDMA1_src;
u32 GPDMA2_src;
u32 GPDMA1_dst;
u32 GPDMA2_dst;
u32 GPDMA1_2_arbitration;
u32 GPDMA1_2_config;
u32 GPDMA1_2_status;
u32 GPDMA1_2_ier;
u32 pci_BAR0_shadow;
u32 pci_BAR1_shadow;
u32 pci_BAR2_shadow;
u32 SDRAM_config;
u32 GPDMA3_src;
u32 GPDMA4_src;
u32 GPDMA3_dst;
u32 GPDMA4_dst;
u32 GPDMA3_4_arbitration;
u32 GPDMA3_4_config;
u32 GPDMA3_4_status;
u32 GPDMA3_4_ier;
u32 global_status;
u32 test_set;
};





/*
 * Descriptor defines.
 */
struct hifn7811desc {
unsigned long cmd;
unsigned long ptr;
};

#define HIFN7811_DESC_LAST 0x20000000 /* Last fragment */
#define HIFN7811_DESC_MASKDONE 0x02000000 /* Mask done status */
#define HIFN7811_DESC_VALID 0x80000000 /* Valid bit */
#define HIFN7811_DESC_JUMP 0x40000000
#define HIFN7811_DESC_NOVALID 0x01000000
#define HIFN7811_DESC_ADD32 0x00800000
#define HIFN7811_DESC_MIPSADDR 0x00400000
#define HIFN7811_DESC_BE 0x00200000 /* Big Endian? */
#define HIFN7811_DESC_LENMASK 0x0000ffff /* Mask for buffer length */
/* Set by chip, results */
#define HIFN7811_DESC_OVERFLOW 0x08000000
#define HIFN7811_DESC_DST_OVERFLOW 0x04000000


/*
 * Base Command Structure.
 */
struct hifn7811cmd {
unsigned long cmd;
unsigned short scount;
unsigned short dcount;
};

#define HIFN7811_CMD_SES(x) ((((x) & 0xff) << 8) | (((x) >> 8) & 0xf))

#define HIFN7811_CMD_COMP 0x00010000 /* Enable compression */
#define HIFN7811_CMD_PAD 0x00020000 /* Enable padding */
#define HIFN7811_CMD_MAC 0x00040000 /* Enable mac processing */
#define HIFN7811_CMD_ENCRYPT 0x00080000 /* Enable encryption */

#define HIFN7811_CMD_ENCODE 0x00000000 /* Encode basic command */
#define HIFN7811_CMD_DECODE 0x00200000 /* Decode basic command */
#define HIFN7811_CMD_READRAM 0x00400000 /* Read RAM basic command */
#define HIFN7811_CMD_WRITERAM 0x00600000 /* Write RAM basic command */

#define HIFN7811_SWAPB(x) ((((x) & 0xff) << 8) | (((x) >> 8) & 0xff))


/*
 * Compress Command Structure.
 */
struct hifn7811comp {
unsigned short cmd;
unsigned short hdrcnt;
unsigned short srccnt;
unsigned short reserved;
};

#define HIFN7811_COMP_LZS 0x0001 /* LZS compression */
#define HIFN7811_COMP_MPPC 0x0101 /* MPPC compression */
#define HIFN7811_COMP_STRIP0 0x0400 /* LZS strip 0 mode */
#define HIFN7811_COMP_RESTART 0x0400 /* MPPC restart mode */
#define HIFN7811_COMP_UPDTHIST 0x0800 /* Update decompress history */
#define HIFN7811_COMP_CLEARHIST 0x1000 /* Clear compress history */


/*
 * Pad Command Structure.
 */
struct hifn7811pad {
unsigned short cmd;
unsigned short srccnt;
};

#define HIFN7811_PAD_MODE0 0x00000000 /* Mode 0 algorithm */
#define HIFN7811_PAD_MODE1 0x01000000 /* Mode 1 algorithm */
#define HIFN7811_PAD_MODE2 0x02000000 /* Mode 2 algorithm */
#define HIFN7811_PAD_MODE3 0x03000000 /* Mode 3 algorithm */

#define HIFN7811_PAD_CNTMODEM 0x00200000 /* Decrement count mode */


/*
 * MAC Command Structure.
 */
struct hifn7811mac {
unsigned short cmd;
unsigned short hdrcnt;
unsigned short srccnt;
unsigned short reserved;
};

#define HIFN7811_MAC_COMP2PAD 0x0000 /* Compress->MAC->Padding */
#define HIFN7811_MAC_PAD2ENC 0x0001 /* Padding->MAC->Encryption */
#define HIFN7811_MAC_AEBD 0x0002 /* After encode,before decode */
#define HIFN7811_MAC_NEWKEY 0x0008 /* New key */
#define HIFN7811_MAC_CNTMODE 0x0020 /* Continue counting */
#define HIFN7811_MAC_SHA 0x0000 /* SHA mode */
#define HIFN7811_MAC_MD5 0x0100 /* MD5 mode */
#define HIFN7811_MAC_HMAC 0x0000 /* HMAC mode */
#define HIFN7811_MAC_SSL 0x0400 /* SSL MAC mode */
#define HIFN7811_MAC_HASH 0x0800 /* HASH only */
#define HIFN7811_MAC_TRUNC 0x1000 /* Truncate to 12 bytes */
#define HIFN7811_MAC_RESULT 0x2000 /* Generate result */
#define HIFN7811_MAC_INSERT 0x4000 /* Insert MAC in data */


/*
 * Encryption Command Structure.
 */
struct hifn7811enc {
unsigned short cmd;
unsigned short hdrcnt;
unsigned short srccnt;
unsigned short reserved;
};

#define HIFN7811_ENC_NEWKEY 0x0008 /* New key */
#define HIFN7811_ENC_NEWIV 0x0010 /* New initialization vector */
#define HIFN7811_ENC_CNTMODE 0x0020 /* Continue counting */
#define HIFN7811_ENC_DES 0x0000 /* DES encryption */
#define HIFN7811_ENC_3DES 0x0100 /* Triple DES encryption */
#define HIFN7811_ENC_RC4 0x0200 /* RC4 encryption */
#define HIFN7811_ENC_ECB 0x0000 /* ECB DES mode */
#define HIFN7811_ENC_CBC 0x0800 /* CBC DES mode */
#define HIFN7811_ENC_CFB64 0x1000 /* CFB-64 DES mode */
#define HIFN7811_ENC_OFB 0x1800 /* CFB DES mode */
#define HIFN7811_ENC_CLEAR 0x4000 /* Clear context */


/*
 * Base Result Structure.
 */
struct hifn7811res {
unsigned long cmd;
unsigned short srccnt;
unsigned short dstcnt;
};

#define HIFN7811_RES_SES HIFN7811_CMD_SES
#define HIFN7811_RES_DSTOVERRUN 0x0002000 /* Destination overrun */


/*
 * Compression Result Structure.
 */
struct hifn7811compres {
unsigned short cmd;
unsigned short crc;
};

#define HIFN7811_COMPRES_NZERO 0x0100 /* Source count non-zero */
#define HIFN7811_COMPRES_LZSEND 0x0200 /* LZS reached end marker */
#define HIFN7811_COMPRES_RSTART 0x0400 /* MPPC restart */
#define HIFN7811_COMPRES_LCBMSK 0x00ff /* LCB check byte mask */


/*
 * MAC Result Structure.
 */
struct hifn7811macres {
unsigned long cmd;
/* unsigned char hash[20]; */
};

#define HIFN7811_MACRES_NZERO 0x01000000 /* Source count non-zero */
#define HIFN7811_MACRES_MISCMP 0x02000000 /* Mis-compare result */


/*
 * Encryption Result Structure.
 */
struct hifn7811encres {
unsigned long cmd;
};

#define HIFN7811_ENCRES_NZERO 0x01000000 /* Source count non-zero */



/****************************************************************************/
#endif /* _HIFN7811_H_ */


头文件2#ifndef __IF_CRYPTO_H
#define __IF_CRYPTO_H
#include <openssl/rc4.h>
#include <linux/des_locl.h>
#include <linux/malloc.h>

typedef struct RC4_KEY_HW {
unsigned char raw[16]; /* Raw key */
int len; /* Key len */
} RC4_KEY_HW;

typedef unsigned short crypto_cmd_t;
struct crypto_engine_def;

struct crypto_request
{
char *inbuf; /* Input buffer */
char *outbuf; /* Output buffer */
int inlen; /* Length of input buffer */
int outlen; /* On input, max size. On Output, real size of output */
unsigned int flags;
struct crypto_engine_def *engine; /* Engine to use. If NULL, use best one */
struct crypto_operation
{
crypto_cmd_t cmd; /* Operation to perform */
int offset; /* Offset, from inbuf, to start operation */
int len; /* Length of operation */
union
{
struct CRYPTO_RC4
{
struct RC4_KEY_HW *key;
} rc4;
struct CRYPTO_DES
{
des_cblock key[3]; /* For DES, key[0] only is used */
des_cblock *ivec; /* NULL if no ivec */
} des;
struct CRYPTO_HASH
{
unsigned char hmac_key[64];
unsigned char hmac_key_len;
unsigned char result[20]; /* On output, contains the hash result */
unsigned char reslen; /* Contains the result len */
} hash;
} u;
struct crypto_operation *next; /* If non-NULL, pointer to next operation to perform on buffer
* Users are responsable for memory allocated for *next
*/
} op;
void (*callback)(struct crypto_request *); /* function to call when done request. This struct * is passed as argument. */
void *priv; /* Private to requester */
};

/* Commands */
#define CRYPT_NO_OP 0x00
#define CRYPT_ENCODE 0x00
#define CRYPT_DECODE 0x80
#define CRYPT_RC4 0x01
#define CRYPT_DES 0x02 /* Should not be used... insecure (key space too small) */
#define CRYPT_3DES 0x04
# define CRYPT_ECB 0x08 /* Meaningful only for [3]DES */
# define CRYPT_CBC 0x10 /* Meaningful only for [3]DES */
#define CRYPT_MD5 0x20
#define CRYPT_SHA 0x40
# define CRYPT_HMAC 0x08 /* Meaningful only for MD5|SHA */
# define CRYPT_96 0x10 /* Meaningful only for MD5|SHA */
# define CRYPT_APPEND 0x100 /* Meaningful only for MD5|SHA, append hash to output */
#define CRYPT_RANDOM 0x200 /* Get some random bytes in outbuf */

/* Flags */
#define CRYPT_SYNC 0x1 /* Must wait for completion */
#define CRYPT_HIGH_PRIORITY 0x2 /* Process in priority (insert at end of queue) */
#define CRYPT_DATA 0x4 /* Use to filter data from other stuff */
#define CRYPT_SOFTWARE 0x8 /* Force software, or in output, was done in Software automagically */
#define CRYPT_HARDWARE 0x10 /* Force hardware, or in output, was done in Hardware */

/* Return codes */
#define CRYPT_READY 1
#define CRYPT_OK 0
#define CRYPT_ERROR -1
#define CRYPT_NO_RESSOURCE -2

enum crypto_engine_attributes
{
IS_SOFTWARE =0x01,
IS_HARDWARE =0x02,
IS_ASYNC =0x04, /* Can return CRYPT_OK and calls back when done */
IS_SYNC =0x08 /* Can be instructed to wait until completion (BUSY LOOPING?!) */
};


/* Prototypes */

struct crypto_request *crypto_alloc_request(int flags);
int crypto_execute(struct crypto_request *req);
int crypto_query(crypto_cmd_t cap,unsigned int attrib); /* Returns TRUE when there is an engine that matches */
/* Optionnaly, open the engine to use and put it in req->engine */
struct crypto_engine_def *crypto_open_engine(crypto_cmd_t cap,unsigned int attrib);
void crypto_close_engine(struct crypto_engine_def *eng);

static inline void crypto_free_request(struct crypto_request *r)
{
kfree(r);
}

/**************************************************
*
* For crypto providers
*
***************************************************/

struct crypto_engine_def
{
char *name;
crypto_cmd_t capability;
unsigned int attributes;
int (*request_handler)(struct crypto_request *req);
void (*open)(void);
void (*close)(void);
};


int crypto_register_engine(struct crypto_engine_def *eng);
int crypto_unregister_engine(char *name);


#endif


111
moqingsong
论坛版主
论坛版主
  • 注册日期2002-04-07
  • 最后登录2011-02-03
  • 粉丝0
  • 关注0
  • 积分74分
  • 威望71点
  • 贡献值0点
  • 好评度10点
  • 原创分0分
  • 专家分0分
地板#
发布于:2002-06-21 13:42
你这桶水浓度不小嘛,我得慢慢看看。。。
关于卡工作原理方面的资料有连接什么的吗?或者给我邮一个?moqingsong@sohu.com
按第一贴的“给分”键,给分。
sirroom
驱动大牛
驱动大牛
  • 注册日期2001-07-30
  • 最后登录2018-05-29
  • 粉丝0
  • 关注0
  • 积分6分
  • 威望11点
  • 贡献值1点
  • 好评度0点
  • 原创分0分
  • 专家分0分
地下室#
发布于:2002-06-25 09:50
kao,相关资源早在第一桶水里就给出来了.这个只是把东东拿出来了而已.看来还得自已来...
111
moqingsong
论坛版主
论坛版主
  • 注册日期2002-04-07
  • 最后登录2011-02-03
  • 粉丝0
  • 关注0
  • 积分74分
  • 威望71点
  • 贡献值0点
  • 好评度10点
  • 原创分0分
  • 专家分0分
5楼#
发布于:2002-06-25 10:12
惭愧,没好好看。。。。。
按第一贴的“给分”键,给分。
moqingsong
论坛版主
论坛版主
  • 注册日期2002-04-07
  • 最后登录2011-02-03
  • 粉丝0
  • 关注0
  • 积分74分
  • 威望71点
  • 贡献值0点
  • 好评度10点
  • 原创分0分
  • 专家分0分
6楼#
发布于:2002-06-25 11:07
.cim文件用什么打开看?
哦,我好笨呀。。。
按第一贴的“给分”键,给分。
游客

返回顶部