Index: linux-2.6.9/drivers/crypto/fcrypt.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.9/drivers/crypto/fcrypt.c	2004-10-28 22:45:31.813559775 +0200
@@ -0,0 +1,729 @@
+/* 
+ * Support for CE-InfoSys FastCrypt hardware crypto engine.
+ *
+ * Linux developers:
+ * 	Michal Ludvig <michal@logix.cz>
+ * 	              http://www.logix.cz/michal
+ *
+ * Details about communication with the card learned from fcrypt-2.00
+ * driver written by Benjamin Reichardt <reichard@math.uni-goettingen.de>
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2 as published by the Free
+ * Software Foundation, or the BSD-like license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ *  - Redistributions of source code must retain the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer.
+ *
+ *  - Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials
+ *    provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+
+#include <linux/crypto.h>
+
+#include <linux/delay.h>
+#include <linux/wait.h>
+
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+
+#include "fcrypt.h"
+#include "fcrypt_refdat.h"
+
+#define	FCRYPT_MODULE_NAME "fcrypt"
+#define	PFX FCRYPT_MODULE_NAME ": "
+
+MODULE_AUTHOR("Michal Ludvig <michal@logix.cz>");
+MODULE_DESCRIPTION("CE-Infosys FastCrypt PCI crypto accelerator driver");
+MODULE_LICENSE("Dual BSD/GPL");
+
+#ifdef CONFIG_SUSE_KERNEL
+MODULE_INFO(supported, "yes");
+#endif
+
+static struct pci_device_id fcrypt_pci_tbl[] =
+{
+	{0x10e8, 0x80ad, PCI_ANY_ID, PCI_ANY_ID, 0, 0, },
+	{ }
+};
+MODULE_DEVICE_TABLE(pci, fcrypt_pci_tbl);
+
+// FIXME: This must be a list or something alike!!!
+struct fcrypt_dev *fdev_global;
+
+#define fcrypt_get_device() (fdev_global)
+#define fcrypt_put_device(a)
+
+/* ====== Functions for accessing indirect ports ====== */
+
+static inline void
+fcrypt_aport_iwrite_v(struct fcrypt_dev *fdev,
+		      unsigned long iregister,
+		      int count, ...)
+{
+	va_list ap;
+	
+	outl(A_PORT_APTR_OFFSET, fdev->adrctr);
+	outl(iregister, fdev->aport);
+	outl(A_PORT_AIAR_OFFSET, fdev->adrctr);
+	
+	va_start(ap, count);
+	while(count--)
+		outl(va_arg(ap, unsigned long), fdev->aport);
+	va_end(ap);
+}
+
+static inline void
+fcrypt_aport_iwrite(struct fcrypt_dev *fdev,
+		    unsigned int iregister,
+		    unsigned long value)
+{
+	outl(A_PORT_APTR_OFFSET, fdev->adrctr);
+	outl(iregister, fdev->aport);
+	outl(A_PORT_AIAR_OFFSET, fdev->adrctr);
+	outl(value, fdev->aport);
+}
+
+static inline void 
+fcrypt_cport_iwrite_v(struct fcrypt_dev *fdev,
+		      unsigned long iregister,
+		      int count, ...)
+{
+	va_list ap;
+	
+	outl(C_PORT_CPTR_OFFSET, fdev->adrctr);
+	outl(iregister, fdev->cport);
+	outl(C_PORT_CIAR_OFFSET, fdev->adrctr);
+	
+	va_start(ap, count);
+	while(count--)
+		outl(va_arg(ap, unsigned long), fdev->cport);
+	va_end(ap);
+}
+
+static inline void
+fcrypt_cport_iwrite(struct fcrypt_dev *fdev,
+		    unsigned int iregister,
+		    unsigned long value)
+{
+	outl(C_PORT_CPTR_OFFSET, fdev->adrctr);
+	outl(iregister, fdev->cport);
+	outl(C_PORT_CIAR_OFFSET, fdev->adrctr);
+	outl(value, fdev->cport);
+}
+
+/* ====== Functions for loading keys and IVs into the card ====== */
+
+static void
+fcrypt_load_one_key(struct fcrypt_dev *fdev, const char *key, int slot)
+{
+	int i;
+	
+	BUG_ON(slot & ~0x0F);
+
+	fcrypt_cport_iwrite(fdev, C_PORT_CMD1_IOFFSET, slot << 4);
+	fcrypt_cport_iwrite(fdev, C_PORT_CMD2_IOFFSET, 0);
+
+	fcrypt_cport_iwrite_v(fdev, C_PORT_CDP_IOFFSET, 0);
+	
+	for (i = 0; i < 8; i++)
+		outl(key[i], fdev->cport);
+
+	outl(C_PORT_CSTAT_OFFSET, fdev->adrctr);
+}
+
+static void
+fcrypt_load_key(struct fcrypt_dev *fdev, const char *key,
+		int key_slots[3], int mode)
+{
+	if (mode & TRIPLE_CIPHER) {
+		if ((mode & ENCDEC_MASK) == ENCODE) {
+			fcrypt_load_one_key(fdev, key +  0, key_slots[0]);
+			fcrypt_load_one_key(fdev, key +  8, key_slots[1]);
+			fcrypt_load_one_key(fdev, key + 16, key_slots[2]);
+		} else {
+			/* 3DES decryption needs the key in reversed order. */
+			fcrypt_load_one_key(fdev, key + 16, key_slots[0]);
+			fcrypt_load_one_key(fdev, key +  8, key_slots[1]);
+			fcrypt_load_one_key(fdev, key +  0, key_slots[2]);
+		}
+	} else
+		fcrypt_load_one_key(fdev, key, key_slots[0]);
+}
+
+static void
+fcrypt_load_iv(struct fcrypt_dev *fdev, const char *iv)
+{
+	int i;
+	
+	fcrypt_aport_iwrite_v(fdev, A_PORT_IV_IOFFSET, 0);
+	
+	for (i = 0; i < 8; i++)
+		outl(iv[i], fdev->aport);
+
+	fcrypt_aport_iwrite(fdev, A_PORT_CMD_IOFFSET, A_PORT_StopStoreIV1_CMD);
+}
+
+/* ====== Helper function ====== */
+
+static void 
+dump_mem(const char *msg, const char *ibuf, size_t len)
+{
+	char obuf[100];
+	int i = 0;
+
+	memset(obuf, 0, sizeof(obuf));
+	for (i=0; i<len; i++) {
+		sprintf(&obuf[(i%8)*2], "%02x", ibuf[i] & 0xFF);
+		if ((i % 8 == 7) || (i==len - 1))
+			printk(KERN_INFO PFX "dump: %s %s\n", msg, obuf);
+	}
+}
+
+/* ====== Encryption/decryption functions ====== */
+
+static void
+fcrypt_crypt_dma(struct fcrypt_dev *fdev, int len, int key_slot[3], int mode)
+{
+	/* Set board status */
+	fdev->status = BOARD_BUSY;
+
+	/* Reset PCI FIFO */
+	outl(PCI_RESET_CMD, (fdev->opreg + MCSR_REG_OFFSET));
+
+	/* Reset A-Port */
+	fcrypt_aport_iwrite(fdev, A_PORT_CMD_IOFFSET, A_PORT_Reset_CMD);
+
+	/* Enable B-Port */
+	fcrypt_aport_iwrite(fdev, A_PORT_CFG_IOFFSET, CFG_ENABLE_B_RW);
+
+	/* Enable DMA */
+	fcrypt_aport_iwrite(fdev, A_PORT_DMA_IOFFSET, DMA_ENABLE);
+
+	/* Set used key slots */
+	fcrypt_aport_iwrite(fdev, A_PORT_KPTR_IOFFSET, key_slot[0]);
+	if (mode & TRIPLE_CIPHER) {
+		fcrypt_aport_iwrite(fdev, A_PORT_KPTR_IOFFSET + 1, key_slot[1]);
+		fcrypt_aport_iwrite(fdev, A_PORT_KPTR_IOFFSET + 2, key_slot[2]);
+	}
+
+	/* Set chaining mode */
+	fcrypt_aport_iwrite(fdev, A_PORT_MOD_IOFFSET, mode);
+
+	/* Initiate BusMaster transfer */
+	/* Input buffer */
+	outl(fdev->ipage_phys, fdev->opreg + MRAR_REG_OFFSET);
+	outl(len, fdev->opreg + MRTC_REG_OFFSET);
+	
+	/* Output buffer */
+	outl(fdev->opage_phys, fdev->opreg + MWAR_REG_OFFSET);
+	outl(len, fdev->opreg + MWTC_REG_OFFSET);
+
+	/* Enable IRQ and BusMaster transfers in both directions */
+	outl(INT_ON_WRITE_COMPLETE, fdev->opreg + INTR_CTRL_REG_OFFSET);
+	outl(PCI_BUSMASTER_WRITE_ENABLE + PCI_BUSMASTER_READ_ENABLE,
+	     fdev->opreg + MCSR_REG_OFFSET);
+
+	/* Start encryption with IV#1 */
+	fcrypt_aport_iwrite(fdev, A_PORT_CMD_IOFFSET, A_PORT_Fetch1Start_CMD);
+
+	/* Enable B-Port transfer */
+	outl(AdrCtl_EBT_BIT + A_PORT_ADATA_OFFSET, fdev->adrctr);
+}
+
+static int
+fcrypt_run_sync(struct fcrypt_dev *fdev, const uint8_t *iv,
+		int len, int key_slots[3], int mode)
+{
+	int rc = 0;
+	wait_queue_head_t waitq;
+	unsigned long timeout;
+
+	if (iv)
+		fcrypt_load_iv(fdev, iv);
+
+	if (!in_atomic()) {
+		fdev->pwaitq = &waitq;
+		init_waitqueue_head(fdev->pwaitq);
+	} else
+		fdev->pwaitq = NULL;
+	
+	fcrypt_crypt_dma(fdev, len, key_slots, mode);
+
+	if (!in_atomic())
+		wait_event_interruptible_timeout(*fdev->pwaitq, 
+						 (fdev->status != BOARD_BUSY),
+						 2 * HZ);
+	else {
+		timeout = jiffies + 2 * HZ;
+		while((fdev->status == BOARD_BUSY) && jiffies < timeout)
+			;
+		if (fdev->status == BOARD_BUSY)
+			printk(KERN_CRIT PFX "%s(): Timeout!!!\n", __func__);
+	}
+
+	if (unlikely(fdev->status != BOARD_CRYPT_OK))
+		printk(KERN_CRIT PFX "Reported bad status! (status=%d)\n", fdev->status);
+
+	fdev->status = BOARD_FREE;
+
+	return rc;
+}
+
+static int
+fcrypt_test(struct fcrypt_dev *fdev)
+{
+	int key_slots[3] = { 1, 2, 3 };
+	int i;
+
+	for (i = 0; i < num_fcrypt_test_vectors; i++) {
+		memcpy(fdev->ipage, fcrypt_test_vectors[i].ref_input,
+		       fcrypt_test_vectors[i].bytes);
+
+		fcrypt_load_key(fdev, fcrypt_test_vectors[i].key,
+				key_slots, fcrypt_test_vectors[i].mode);
+
+		fcrypt_run_sync(fdev, fcrypt_test_vectors[i].iv,
+				fcrypt_test_vectors[i].bytes,
+				key_slots,
+				fcrypt_test_vectors[i].mode);
+
+		if (memcmp(fdev->opage, fcrypt_test_vectors[i].ref_output,
+			   fcrypt_test_vectors[i].bytes) == 0) {
+			printk(KERN_INFO PFX "%s succeeded\n", fcrypt_test_vectors[i].label);
+		} else {
+			printk(KERN_ERR PFX "%s FAILED\n", fcrypt_test_vectors[i].label);
+			dump_mem("Input:    ", fdev->ipage, fcrypt_test_vectors[i].bytes);
+			dump_mem("Expected: ", fcrypt_test_vectors[i].ref_output,
+				 fcrypt_test_vectors[i].bytes);
+			dump_mem("Got:      ", fdev->opage, fcrypt_test_vectors[i].bytes);
+		}
+	}
+
+	return 0;
+}
+
+static int
+fcrypt_3des_set_key(void *ctx_arg, const uint8_t *in_key,
+		    unsigned int key_len, uint32_t *flags)
+{
+	struct fcrypt_3des_ctx *ctx = ctx_arg;
+
+	if (key_len != DES3_KEY_SIZE) {
+		*flags |= CRYPTO_TFM_RES_BAD_KEY_LEN;
+		return -EINVAL;
+	}
+
+	memcpy(ctx->key, in_key, key_len);
+
+	return 0;
+}
+
+static void
+fcrypt_3des_singleblock(void *ctx_arg, uint8_t *dst, const uint8_t *src, int encdec)
+{
+	struct fcrypt_dev *fdev;
+	int key_slots[3] = { 1, 2, 3 };
+	struct fcrypt_3des_ctx *ctx = ctx_arg;
+
+	fdev = fcrypt_get_device();
+	fcrypt_load_key(fdev, ctx->key, key_slots, TRIPLE_CIPHER | encdec);
+
+	memcpy(fdev->ipage, src, DES3_BLOCK_SIZE);
+	fcrypt_run_sync(fdev, NULL, DES3_BLOCK_SIZE, key_slots, ECB_MODE | TRIPLE_CIPHER | encdec);
+	memcpy(dst, fdev->opage, DES3_BLOCK_SIZE);
+
+	fcrypt_put_device(fdev);
+}
+
+static void
+fcrypt_3des_encrypt(void *ctx_arg, uint8_t *dst, const uint8_t *src)
+{
+	fcrypt_3des_singleblock(ctx_arg, dst, src, ENCODE);
+}
+
+static void
+fcrypt_3des_decrypt(void *ctx_arg, uint8_t *dst, const uint8_t *src)
+{
+	fcrypt_3des_singleblock(ctx_arg, dst, src, DECODE);
+}
+
+static void
+fcrypt_3des_multiblock(void *ctx_arg, uint8_t *dst, const uint8_t *src,
+		       const uint8_t *iv, size_t nbytes, int encdec,
+		       int mode)
+{
+	struct fcrypt_dev *fdev = fcrypt_get_device();
+	int key_slots[3] = { 1, 2, 3 };
+	struct fcrypt_3des_ctx *ctx = ctx_arg;
+	size_t chunk, index = 0, todo = nbytes;
+
+	fcrypt_load_key(fdev, ctx->key, key_slots, TRIPLE_CIPHER | encdec);
+
+	while (todo) {
+		chunk = todo > PAGE_SIZE ? PAGE_SIZE : todo;
+		
+		memcpy(fdev->ipage, src + index, chunk);
+		fcrypt_run_sync(fdev, iv, chunk, key_slots, TRIPLE_CIPHER | mode | encdec);
+		memcpy(dst + index, fdev->opage, chunk);
+
+		index += chunk;
+		todo -= chunk;
+		iv = NULL;	/* Actual IV is already stored on the chip */
+	}
+	fcrypt_put_device(fdev);
+}
+
+static void
+fcrypt_3des_ecb(void *ctx_arg, uint8_t *dst, const uint8_t *src,
+		const uint8_t *iv, size_t nbytes, int encdec,
+		int inplace)
+{
+	fcrypt_3des_multiblock(ctx_arg, dst, src, iv, nbytes,
+			       encdec == CRYPTO_DIR_ENCRYPT ? ENCODE : DECODE,
+			       ECB_MODE);
+}
+
+static void
+fcrypt_3des_cbc(void *ctx_arg, uint8_t *dst, const uint8_t *src,
+		const uint8_t *iv, size_t nbytes, int encdec,
+		int inplace)
+{
+	fcrypt_3des_multiblock(ctx_arg, dst, src, iv, nbytes,
+			       encdec == CRYPTO_DIR_ENCRYPT ? ENCODE : DECODE,
+			       CBC_MODE);
+}
+
+static void
+fcrypt_3des_cfb(void *ctx_arg, uint8_t *dst, const uint8_t *src,
+		const uint8_t *iv, size_t nbytes, int encdec,
+		int inplace)
+{
+	fcrypt_3des_multiblock(ctx_arg, dst, src, iv, nbytes,
+			       encdec == CRYPTO_DIR_ENCRYPT ? ENCODE : DECODE,
+			       CFB_MODE);
+}
+
+static void
+fcrypt_3des_ofb(void *ctx_arg, uint8_t *dst, const uint8_t *src,
+		const uint8_t *iv, size_t nbytes, int encdec,
+		int inplace)
+{
+	fcrypt_3des_multiblock(ctx_arg, dst, src, iv, nbytes,
+			       encdec == CRYPTO_DIR_ENCRYPT ? ENCODE : DECODE,
+			       OFB_MODE);
+}
+
+static irqreturn_t
+fcrypt_interrupt(int irq, void *dev_instance, struct pt_regs *rgs)
+{
+	struct fcrypt_dev *fdev = dev_instance;
+	unsigned long status;
+
+	status = inl(fdev->opreg + INTR_CTRL_REG_OFFSET);
+
+	/* Request from our card? */
+	if ((status & INT_ASSERTED) == 0)
+		return IRQ_NONE;
+
+	if ((status & (WRITE_TRANSFER_COMPLETE | READ_TRANSFER_COMPLETE))) {
+		/* Disable BusMaster */
+		outl(0x00000000, fdev->opreg + MCSR_REG_OFFSET);
+
+		/* Disable IRQ */
+		outl(0x003f0000, fdev->opreg + INTR_CTRL_REG_OFFSET);
+
+		/* Reset EBT */
+		outl(0, fdev->adrctr);
+
+		/* Stop CryptoEngine and store IV to slot #1 */
+		fcrypt_aport_iwrite(fdev, A_PORT_CMD_IOFFSET, A_PORT_StopStoreIV1_CMD);
+
+		/* Set board status */
+		fdev->status = BOARD_CRYPT_OK;
+	} else {
+		/* Set board status */
+		fdev->status = BOARD_WRONG_INT;
+		
+		printk(KERN_CRIT PFX "received undefined IRQ\n");
+	}
+
+	if (fdev->pwaitq)
+		wake_up(fdev->pwaitq);
+
+	return IRQ_HANDLED;
+}
+
+/* ====== Card initialization routines ====== */
+
+static int
+fcrypt_init_hardware(struct fcrypt_dev *fdev)
+{
+	int i;
+	unsigned char xbox_read;
+
+	/* Reset the PCI chip */
+	outl(PCI_RESET_CMD, (unsigned long) (fdev->opreg + MCSR_REG_OFFSET));
+
+	/* Reset the SuperCrypt chip */
+	outl(PCI_FULL_RESET_CMD, (unsigned int)(fdev->opreg + MCSR_REG_OFFSET));
+
+	/* Clear MSCR */
+	outl(0, (unsigned int)(fdev->opreg + MCSR_REG_OFFSET));
+
+	/* Reset C-Port */
+	fcrypt_cport_iwrite(fdev, C_PORT_CMD1_IOFFSET, C_PORT_Reset_CMD1);
+
+	/* Clear C-Port */
+	fcrypt_cport_iwrite(fdev, C_PORT_CMD1_IOFFSET, 0);
+
+	/* Load X-Box */
+	fcrypt_cport_iwrite(fdev, C_PORT_CMD2_IOFFSET, C_PORT_XBoxLoad_CMD2);
+	
+	fcrypt_cport_iwrite_v(fdev, C_PORT_CDP_IOFFSET, 0);
+
+	for (i = 0; i < X_BOX_SIZE; i++)
+		outl(gXBox_Data[i], fdev->cport);
+
+	for (i = 0; i < X_BOX_SIZE; i++) {
+		xbox_read = inb(fdev->cport);
+		if (xbox_read != gXBox_Data[i]) {
+			printk(KERN_CRIT PFX "X-Box verification failed (%x/%x@%d)!\n",
+			       xbox_read & 0xff, gXBox_Data[i] & 0xff, i);
+			return -EACCES;
+		}
+	}
+	printk(KERN_INFO PFX "X-Box loaded and verified.\n");
+
+	/* Reset C-Port */
+	fcrypt_cport_iwrite(fdev, C_PORT_CMD1_IOFFSET, C_PORT_Reset_CMD1);
+
+	/* Enable all key slots */
+	fcrypt_cport_iwrite_v(fdev, C_PORT_KYE_IOFFSET, 2, 0x000000ff, 0x000000ff);
+
+	/* Enable A-Port */
+	fcrypt_cport_iwrite(fdev, C_PORT_CFG_IOFFSET,
+			    CFG_ENABLE_A_PORT + CFG_ENABLE_A_PORT_IV);
+
+	printk(KERN_INFO PFX "Hardware initialized\n");
+
+	return 0;
+}
+
+static int __devinit
+fcrypt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+	int		rc;
+	struct fcrypt_dev *fdev;
+
+	// FIXME: Single-board hack.
+	if (fdev_global) {
+		printk(KERN_ERR PFX "Device already configured. Aborting.\n");
+		return -EBUSY;
+	}
+
+	rc = pci_enable_device(pdev);
+	if (rc) {
+		printk(KERN_ERR PFX "Cannot enable PCI card. Aborting.\n");
+		return rc;
+	}
+
+	rc = pci_set_dma_mask(pdev, 0xffffffff);
+	if (rc) {
+		printk(KERN_ERR PFX "DMA not supported. Aborting.\n");
+		goto err_out_disable;
+	}
+
+	rc = pci_request_regions(pdev, FCRYPT_MODULE_NAME);
+	if (rc) {
+		printk(KERN_ERR PFX "Cannot register resources. Aborting.\n");
+		goto err_out_disable;
+	}
+
+	pci_set_master(pdev);
+
+	fdev = kmalloc(sizeof(struct fcrypt_dev), GFP_KERNEL);
+	if (!fdev) {
+		printk(KERN_ERR PFX "Memory allocation failed. Aborting.\n");
+		rc = -ENOMEM;
+		goto err_out_relreg;
+	}
+	fdev_global = fdev;
+
+	memset(fdev, 0, sizeof(struct fcrypt_dev));
+
+	fdev->pdev   = pdev;
+	fdev->opreg  = pci_resource_start(pdev, 0);
+	fdev->adrctr = pci_resource_start(pdev, 1);
+	fdev->aport  = pci_resource_start(pdev, 2);
+	fdev->cport  = pci_resource_start(pdev, 3);
+
+	fdev->status = BOARD_FREE;
+
+	fdev->ipage  = (void*)get_zeroed_page(GFP_DMA);
+        fdev->opage  = (void*)get_zeroed_page(GFP_DMA);
+	if (!fdev->ipage || !fdev->opage) {
+		printk(KERN_ERR PFX "Unable to get free page.\n");
+		rc = -ENOMEM;
+		goto err_out_freemem;
+	}
+	fdev->ipage_phys = virt_to_bus(fdev->ipage);
+	fdev->opage_phys = virt_to_bus(fdev->opage);
+
+	pci_set_drvdata(pdev, fdev);
+
+	rc = request_irq(pdev->irq, fcrypt_interrupt,
+			 SA_INTERRUPT | SA_SHIRQ, "fcrypt", fdev);
+	if (rc) {
+		printk(KERN_ERR PFX "Cannot register for IRQ %d\n", pdev->irq);
+		goto err_out_freemem;
+	}
+
+	rc = fcrypt_init_hardware(fdev);
+	if (rc) {
+		printk(KERN_ERR PFX "Hardware initialization failed. Aborting.\n");
+		goto err_out_freeirq;
+	}
+
+	fcrypt_test(fdev);
+
+	printk(KERN_INFO PFX "driver configured for 0x%lx/0x%lx/0x%lx/0x%lx, IRQ=%d\n",
+	       fdev->opreg, fdev->adrctr, fdev->aport, fdev->cport, fdev->pdev->irq);
+
+	return 0;
+
+err_out_freeirq:
+	free_irq(pdev->irq, fdev);
+
+err_out_freemem:
+	if (fdev) {
+		if (fdev->ipage) 
+			free_page((unsigned long)(fdev->ipage));
+
+		if (fdev->opage)
+			free_page((unsigned long)(fdev->opage));
+
+		kfree(fdev);
+		fdev_global = NULL;
+	}
+
+err_out_relreg:
+	pci_release_regions(pdev);
+
+err_out_disable:
+	pci_disable_device(pdev);
+	pci_set_drvdata(pdev, NULL);
+
+	return rc;
+}
+
+static void __devexit
+fcrypt_remove_one(struct pci_dev *pdev)
+{
+	struct fcrypt_dev *fdev = pci_get_drvdata(pdev);
+
+	free_irq(pdev->irq, fdev);
+
+	pci_release_regions(pdev);
+	pci_set_drvdata(pdev, NULL);
+	pci_disable_device(pdev);
+
+	// FIXME: Single board hack
+	if (fdev_global == fdev)
+		fdev_global = NULL;
+
+	if (fdev->ipage) 
+		free_page((unsigned long)(fdev->ipage));
+
+	if (fdev->opage)
+		free_page((unsigned long)(fdev->opage));
+
+	if (fdev)
+		kfree(fdev);
+
+	printk(KERN_INFO PFX "driver unloaded.\n");
+}
+
+/* ====== Module services ====== */
+
+static struct pci_driver fcrypt_driver = {
+	.name		= FCRYPT_MODULE_NAME,
+	.id_table	= fcrypt_pci_tbl,
+	.probe		= fcrypt_init_one,
+	.remove		= __devexit_p(fcrypt_remove_one),
+};
+
+static struct crypto_alg fcrypt_3des_alg = {
+	.cra_name		= "des3_ede",
+	.cra_preference		= CRYPTO_PREF_HARDWARE,
+	.cra_flags		= CRYPTO_ALG_TYPE_CIPHER,
+	.cra_blocksize		= DES3_BLOCK_SIZE,
+	.cra_ctxsize		= sizeof(struct fcrypt_3des_ctx),
+	.cra_module		= THIS_MODULE,
+	.cra_list		= LIST_HEAD_INIT(fcrypt_3des_alg.cra_list),
+	.cra_u			= {
+		.cipher = {
+			.cia_min_keysize	= DES3_KEY_SIZE,
+			.cia_max_keysize	= DES3_KEY_SIZE,
+			.cia_setkey	   	= fcrypt_3des_set_key,
+			.cia_encrypt	 	= fcrypt_3des_encrypt,
+			.cia_decrypt	  	= fcrypt_3des_decrypt,
+
+			.cia_max_nbytes		= (size_t)-1,
+			.cia_ecb		= fcrypt_3des_ecb,
+			.cia_cbc		= fcrypt_3des_cbc,
+			.cia_cfb		= fcrypt_3des_cfb,
+			.cia_ofb		= fcrypt_3des_ofb,
+		}
+	}
+};
+
+static int __init
+fcrypt_init(void)
+{
+	int rc;
+
+	rc = pci_module_init(&fcrypt_driver);
+	if (rc)
+		return rc;
+
+	rc = crypto_register_alg(&fcrypt_3des_alg);
+	if (rc) {
+		pci_unregister_driver(&fcrypt_driver);
+		return rc;
+	}
+
+	return 0;
+}
+
+static void __exit
+fcrypt_exit(void)
+{
+	crypto_unregister_alg(&fcrypt_3des_alg);
+	pci_unregister_driver(&fcrypt_driver);
+}
+
+module_init(fcrypt_init);
+module_exit(fcrypt_exit);
Index: linux-2.6.9/drivers/crypto/Kconfig
===================================================================
--- linux-2.6.9.orig/drivers/crypto/Kconfig	2004-10-23 01:20:07.000000000 +0200
+++ linux-2.6.9/drivers/crypto/Kconfig	2004-10-28 22:49:50.614597224 +0200
@@ -20,4 +20,16 @@ config CRYPTO_DEV_PADLOCK_AES
 	help
 	  Use VIA PadLock for AES algorithm.
 
+config CRYPTO_DEV_FCRYPT
+	tristate "Support for CE-InfoSys FastCrypt PCI card"
+	depends on CRYPTO && PCI
+	help
+	  This driver adds support for CE-InfoSys FastCrypt 
+	  PCI card driven by SuperCrypt CE99C003B processor. 
+
+	  FastCrypt can offload DES and 3DES operations 
+	  out of the CPU.
+
+	  If you are unsure, say N.
+
 endmenu
Index: linux-2.6.9/drivers/crypto/Makefile
===================================================================
--- linux-2.6.9.orig/drivers/crypto/Makefile	2004-10-23 01:20:07.000000000 +0200
+++ linux-2.6.9/drivers/crypto/Makefile	2004-10-28 22:50:19.463917491 +0200
@@ -5,3 +5,4 @@ padlock-objs-$(CONFIG_CRYPTO_DEV_PADLOCK
 
 padlock-objs := padlock-generic.o $(padlock-objs-y)
 
+obj-$(CONFIG_CRYPTO_DEV_FCRYPT) += fcrypt.o
Index: linux-2.6.9/drivers/crypto/fcrypt.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.9/drivers/crypto/fcrypt.h	2004-10-28 22:45:31.813559775 +0200
@@ -0,0 +1,249 @@
+/* 
+ * Support for CE-InfoSys FastCrypt hardware crypto engine.
+ *
+ * Linux developers:
+ * 	Michal Ludvig <michal@logix.cz>
+ * 	              http://www.logix.cz/michal
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2 as published by the Free
+ * Software Foundation, or the BSD-like license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ *  - Redistributions of source code must retain the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer.
+ *
+ *  - Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials
+ *    provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ */
+
+#ifndef _FCRYPT_H
+#define _FCRYPT_H
+
+#define DES_BLOCK_SIZE	8
+#define	DES_KEY_SIZE	8
+
+#define	DES3_BLOCK_SIZE	DES_BLOCK_SIZE
+#define	DES3_KEY_SIZE	(3 * DES_KEY_SIZE)
+
+struct fcrypt_3des_ctx {
+	uint8_t iv[DES_BLOCK_SIZE];
+	uint8_t key[DES3_KEY_SIZE];
+};
+
+/* Values for status in fcrypt_dev */
+#define BOARD_FREE 0
+#define BOARD_BUSY 1
+#define BOARD_CRYPT_OK 2
+#define BOARD_WRONG_INT 3
+
+struct fcrypt_dev {
+	struct pci_dev *pdev;
+	long opreg,
+	     adrctr,
+	     aport,
+	     cport;
+	void *ipage, *opage;
+	unsigned long ipage_phys, opage_phys;
+	wait_queue_head_t *pwaitq;
+	volatile unsigned char status;
+};
+
+/* Symbolic constants for accessing various FastCrypt functions. */
+
+//
+// PCI-Chip Address-offsets (from PortBase0)
+//
+#define FIFO_REG_OFFSET         0x20       // FIFO Register (B-Port)
+#define MWAR_REG_OFFSET         0x24       // Master Write Address Register
+#define MWTC_REG_OFFSET         0x28       // Master Write Transfer Count Reg.
+#define MRAR_REG_OFFSET         0x2C       // Master Read Adr. Reg.
+#define MRTC_REG_OFFSET         0x30       // Master Read Transfer Count Reg.
+#define MCSR_REG_OFFSET         0x3C       // BusMaster Control/Status Register
+#define INTR_CTRL_REG_OFFSET    0x38       // Interrupt Control/Status Register
+//
+// PCI-Chip Commands (for MCSR-Reg)
+//
+#define PCI_TO_ADDON_RESET      0x02000000  // Reset PCI to Addon Flags
+#define ADDON_TO_PCI_RESET      0x04000000  // Reset Addon to PCI Flags
+#define PCI_RESET_CMD           0x0E000000  // Reset FIFO/Mailbox Flags
+#define PCI_FULL_RESET_CMD      0x0F000000  // Reset FIFO/Mailbox & SuperCrypt
+#define PCI_BUSMASTER_WRITE_ENABLE 0x00000400 //Enable Busmaster write transfer
+#define PCI_BUSMASTER_READ_ENABLE  0x00004000 //Enable Busmaster read transfer
+//
+// PCI-Chip FIFO-Status Bitmasks (for MCSR-Reg)
+//
+#define FIFO_WRITE_FULL         0x00000001  // PCI to FIFO->AddOn full
+#define FIFO_WRITE_HALF         0x00000002  // PCI to FIFO->AddOn half empty
+#define FIFO_WRITE_EMPTY        0x00000004  // PCI to FIFO->AddOn empty
+#define FIFO_READ_FULL          0x00000008  // AddOn->PCI FIFO full
+#define FIFO_READ_HALF          0x00000010  // AddOn->PCI FIFO half full
+#define FIFO_READ_EMPTY         0x00000020  // AddOn->PCI FIFO empty
+//
+// PCI-Chip Interrupt selection (INTCSR-Reg)
+//
+#define INT_ON_WRITE_COMPLETE   0x00004000 //initiate an Int. on write complete
+#define INT_ON_READ_COMPLETE    0x00008000 //initiate an Int. on read complete
+//
+// PCI-Chip Actual Interrupt Bitmasks
+//
+#define INT_ASSERTED            0x00800000  // Interrupt-Condition
+#define INT_TARGET_ABORT        0x00200000  // Target-Abort while Busmaster-DMA
+#define INT_MASTER_ABORT        0x00100000  // Master-Abort while Busmaster-DMA
+#define READ_TRANSFER_COMPLETE  0x00080000  /* completition of PCI-Master
+                                             * Operation */
+#define WRITE_TRANSFER_COMPLETE 0x00040000  /* completition of PCI-Master
+                                             * Operation */
+//
+// AdrCtrl-Reg. Bitmasks
+//
+#define AdrCtl_EBT_BIT          0x80000000  // Enable B-Port-Transfer
+#define AdrCtl_CIE_BIT          0x08000000  // Crypt Input-Buffer empty
+#define AdrCtl_COF_BIT          0x10000000  // Crypt Output-Buffer full
+
+//
+// A-Port Address-register (from PortBase2)
+//
+#define A_PORT_CXA_AXC_OFFSET   0x02        // Data Exchange to/from C-Port
+#define A_PORT_ASTAT_OFFSET     0x03        // Status Register of A-Port (read)
+#define A_PORT_APTR_OFFSET      0x03        // Addresspointer of A-Port (write)
+#define A_PORT_AIAR_OFFSET      0x04        // Indirect addressed register
+#define A_PORT_ADATA_OFFSET     0x08        // Crypt-data in/out    
+//
+// A_Port indirect-registers
+//
+#define A_PORT_CFG_IOFFSET      0x00  // Port Configuration Register (r/part.w)
+#define A_PORT_AIE_IOFFSET      0x01  // Interrupt-Enable A-Port (write)
+#define A_PORT_AIS_IOFFSET      0x01  // Interrupt-Source A-Port (read)
+#define A_PORT_DMA_IOFFSET      0x02  // DMA-Mode (r/w)
+#define A_PORT_IV_IOFFSET       0x03  // Initial-Vector (write)
+#define A_PORT_CRL_IOFFSET      0x03  // Chip Revision Level (read)
+#define A_PORT_DBC_IOFFSET      0x0B  // Data Block Counter (r/w)
+#define A_PORT_KYE_IOFFSET      0x0E  // Key Enable Register (read)
+#define A_PORT_KPTR_IOFFSET     0x10  // Key Pointer Register (r/w)
+#define A_PORT_MOD_IOFFSET      0x13  // Cipher Mode Register (r/w)
+#define A_PORT_CMD_IOFFSET      0x14  // A-Port Command (write)
+#define A_PORT_MID_IOFFSET      0x15  // Multitasking ID (r/w)
+#define A_PORT_CID_IOFFSET      0x19  // C-Port ID (read)
+#define A_PORT_XEN_IOFFSET      0x1D  // Extended enable Register (read)
+//
+// A-Port Command-Codes
+//
+#define A_PORT_StartCipher_CMD  0x01  // starting the Cipher engine
+#define A_PORT_StopCipher_CMD   0x00  // stop and reset buffers
+#define A_PORT_Reset_CMD        0x08  // cleasr all registers except the IV
+#define A_PORT_StopStoreIV1_CMD 0x04  // stops and stores act. IV as IV#1
+#define A_PORT_Fetch1Start_CMD  0x05  // loads IV#1 and starts cipher
+#define A_PORT_StopStoreIV2_CMD 0x06  // stops and stores act. IV as IV#2
+#define A_PORT_Fetch2Start_CMD  0x07  // loads IV#2 and starts cipher
+//
+// DMA-Bitmasks
+//
+#define DMA_CH1_READ            0x00  // write through ch. 2
+#define DMA_CH1_WRITE           0x01  // read through ch. 2
+#define DMA_ENABLE_CH1          0x02  // enable DMA-Channel 1
+#define DMA_ENABLE_CH2          0x08  // enable DMA-Channel 2
+#define DMA_BURST_CH1           0x04  // enable Burst-mode on Ch.1
+#define DMA_BURST_CH2           0x10  // enable Burst-mode on Ch.2
+#define DMA_ENABLE          DMA_CH1_WRITE + DMA_ENABLE_CH1 + DMA_ENABLE_CH2 \
+                            + DMA_BURST_CH1 + DMA_BURST_CH2
+//
+// Interrupt-Enable-Bitmasks
+//
+#define AIE_KEY_PROTECT  0x01  // Interrupt on setting a pointer to invalid key
+#define AIE_INPUT_EMPTY  0x02  // Int. while empty input buffer
+#define AIE_OUTPUT_FULL  0x04  // Int. while output buffer full
+#define AIE_DBC_ZERO     0x08  // DataBlockCounter is zero
+#define AIE_CXA_FULL     0x10  // Message from C-Port to A-Port is pending
+//
+// Cipher-modes
+//
+#define ENCODE                  0x00  // Encrypt-Mode
+#define DECODE                  0x01  // Decrypt-Mode
+#define	ENCDEC_MASK		0x01
+
+#define TRIPLE_CIPHER           0x02  // Triple-Cipher Mode
+#define SINGLE_KEY_LENGTH       0x00  // Single Key (56 bit)
+#define EXTENDED_KEY_LENGTH     0x04  // Double key (112 bit)
+#define ECB_MODE                0x00
+#define CBC_MODE                0x10
+#define OFB_MODE                0x20
+#define CFB_MODE                0x30
+#define KSG_MODE                0x40
+#define MAC_MODE                0x50
+#define CRYPT_MODE_MASK         0xf0
+
+//
+// ASTAT-Bitmasks
+//
+#define ASTAT_CIPHER_ACTIVE     0x01  // Cipher engine busy or idle ?
+#define ASTAT_OUTPUT_EMPTY      0x04  // A-Port Output empty
+#define ASTAT_INPUT_FULL        0x02  // A-Port Input full
+
+//
+// C-Port Address-register (from PortBase3)
+//
+#define C_PORT_AXC_CXA_OFFSET   0x00  // no offset for ExchangeReg. to A-Port
+#define C_PORT_CSTAT_OFFSET     0x01  // Status Register of C-Port (read)
+#define C_PORT_CPTR_OFFSET      0x01  // Addresspointer of C-Port (write)
+#define C_PORT_CIAR_OFFSET      0x02  // indirect C-Port Register
+#define C_PORT_CIB_COB_OFFSET   0x04  // Crypt-data in/out
+//
+// C-Port indirect-registers
+//
+#define C_PORT_CFG_IOFFSET      0x00  /* Port Configuration Register
+                                       * (same as A-Port) (r/w) */
+#define C_PORT_CIE_IOFFSET      0x01  // Interrupt-Enable C-Port (write)
+#define C_PORT_CIS_IOFFSET      0x01  // Interrupt-Source C-Port (read)
+#define C_PORT_KYE_IOFFSET      0x04  // 2 byte(!) Key-enable Register (write)
+#define C_PORT_CMD1_IOFFSET     0x05  // Command-Reg. 1 (key and Alg. loading)
+#define C_PORT_CMD2_IOFFSET     0x06  // Command-Reg. 2 ( -"- )
+#define C_PORT_CDP_IOFFSET      0x07  // C-Port Data-Path (for X-Box & Keys)
+#define C_PORT_XEN_IOFFSET      0x08  /* ExtendedEnableReg.
+                                       * (access-rights for A-Port) */
+//
+// C-Port Command-words for Command-Reg. 1
+//
+#define C_PORT_Reset_CMD1       0x08  // C-Port reset
+//
+// C-Port Command-words for Command-Reg. 2
+//
+#define C_PORT_XBoxLoad_CMD2    0x08  // prepare for X-Box load
+#define C_PORT_ExtKey_CMD2      0x04  // use Extended Key (112 Bit)
+//
+// CSTAT-Bitmasks
+//
+#define CSTAT_CIPHER_ACTIVE     0x01  // Cipher is active
+#define CSTAT_KEY_WRITE_PROTECT 0x02  // Key write-protection error
+#define CSTAT_AXC_FULL          0x04  // there's a message from A-Port waiting
+#define CSTAT_CXA_NOT_EMPTY     0x08  // there's a message to A-Port pending
+#define CSTAT_MASTER_RESET      0x10  // Master-Reset occured
+#define CSTAT_KEY_PARITY_ERROR  0x20  // Parity error during key upload
+//
+// CFG-Bitmasks (valid for A-Port, too)
+//
+#define CFG_ENABLE_A_PORT       0x80  // enable A-Port
+#define CFG_ENABLE_A_PORT_IV    0x40  // enable IV-load for A-Port
+#define CFG_ENABLE_B_READ       0x01  // enable B-Port Read Access
+#define CFG_ENABLE_B_WRITE      0x02  // enable B-Port Write Access
+#define CFG_ENABLE_B_RW         0x03  // enable B-Port r/w Access
+#define CFG_ENABLE_B_DBM        0x06  // enable DBM with B-Port
+
+#endif /* _FCRYPT_H */
